pax_global_header00006660000000000000000000000064130017076570014520gustar00rootroot0000000000000052 comment=d250e1cc6e73b8a40a938a823a6532795a088aab opensips-2.2.2/000077500000000000000000000000001300170765700133635ustar00rootroot00000000000000opensips-2.2.2/.gitignore000066400000000000000000000004741300170765700153600ustar00rootroot00000000000000*.o *.d *.so *.sw? tags cscope* # / /cfg.tab.c /cfg.tab.h /lex.yy.c /opensips /debian /curses.out /.svnrevision /.gitrevision # /doc/ /doc/database # /menuconfig/ /menuconfig/configure /Makefile.conf # /utils/opensipsunix/ /utils/opensipsunix/opensipsunix # miscellaneous files core core.* out log *.log *.html opensips-2.2.2/.travis.yml000066400000000000000000000016421300170765700154770ustar00rootroot00000000000000language: c compiler: - gcc - clang sudo: false addons: apt: packages: - flex - bison - libsqlite3-dev - libsctp-dev - libradiusclient-ng-dev - libhiredis-dev - unixodbc-dev - libconfuse-dev - libmysqlclient-dev - libexpat1-dev - libxml2-dev - libpq-dev - zlib1g-dev - libperl-dev - libsnmp-dev - libdb-dev - libldap2-dev - libcurl4-gnutls-dev - libgeoip-dev - libpcre3-dev - libmemcached-dev - libmicrohttpd-dev - librabbitmq-dev - liblua5.1-0-dev - libncurses5-dev - libjson0-dev script: FASTER=1 make exclude_modules="db_oracle osp sngtc cachedb_cassandra cachedb_couchbase cachedb_mongodb" all opensips-2.2.2/AUTHORS000066400000000000000000000114171300170765700144370ustar00rootroot00000000000000# # The format of this file was inspired by the Linux kernel CREDITS file. # # Authors and contributors are listed alphabetically (first name). # # The fields are: name (N), email (E), web-address (W), username (U), # PGP key ID and fingerprint (P) and description (D). ACTIVE PEOPLE ====================== N: Alexandr Dubovikov U: adubovikov E: voip@start4.info D: Modules Contributor to OpenSIPS N: Bogdan Andrei Iancu U: bogdan-iancu E: bogdan@opensips.org W: http://www.opensips.org D: Core developer of SER / OpenSER / OpenSIPS N: Di-Shi Sun U: di-shi E: di-shi@transnexus.com D: Modules Contributor to OpenSIPS N: Ionel Cerghit U: ionel-cerghit E: ionel.cerghit@gmail.com D: Contributor to OpenSIPS N: Ionut-Razvan Ionita U: ionutrazvanionita E: ionutionita@opensips.org D: Contributor to OpenSIPS N: Liviu Chircu U: liviuchircu E: liviu@opensips.org D: Core Developer of OpenSIPS N: Maxim Sobolev U: sobomax E: sobomax@sippysoft.com D: Contributor to OpenSIPS N: Norman Brandinger U: goestelecom E: norm@goes.com D: Contributor to OpenSIPS N: Ovidiu Sas U: osas E: osas@voipembedded.com D: Contributor to OpenSIPS N: Saúl Ibarra Corretgé U: saghul E: saghul@gmail.com D: Contributor to OpenSIPS N: Razvan Crainea U: razvancrainea E: razvancrainea@opensips.org D: Core Developer of OpenSIPS N: Robert Vladut Patrascu U: rvlad-patrascu E: rvlad.patrascu@gmail.com D: Contributor to OpenSIPS N: Vladut Paiu U: vladut-paiu E: vladpaiu@opensips.org D: Core Developer of OpenSIPS INACTIVE PEOPLE ========================== N: Anca Vamanu U: anca_vamanu E: anca@opensips.org D: Main developer of OpenSIPS N: Andrei Dragus U: andreidragus E: andreidragus@yahoo.com D: Contributor to OpenSIPS N: Andrei Pisau U: apisau E: andrei.pisau@opensips.org D: Contributor to OpenSIPS N: Andreas Granig U: agranig E: andreas.granig@inode.info D: Contributor to OpenSER N: Andrei Pelinescu - Onciul E: pelinescu-onciul@fokus.fraunhofer.de P: 2CF56A2D D96D 8B80 BA31 9B93 6208 5D68 3910 FA00 2CF5 6A2D D: Core developer of SER N: Arnaud Chong U: achamo D: Modules Contributor to OpenSIPS N: Bastian Friedrich U: bastian E: bastian.friedrich@collax.com D: Contributor to OpenSER N: Daniel-Constantin Mierla U: miconda E: daniel@opensips.org W: http://www.opensips.org D: Core developer of SER / OpenSER N: Dan Pascu U: dan_pascu E: dan@ag-projects.com D: Main developer of OpenSIPS N: Denis Bilenko U: denik E: denis@ag-projects.com D: Contributor to OpenSIPS N: Dmitry Isakbayev U: isakdim E: isakdim@gmail.com D: Contributor to OpenSIPS N: Elias Baixas U: eliasbaixas E: elias.baixas@voztele.com D: Contributor to OpenSIPS N: Gabriel Vasile D: Contributor to SER N: Greg Fausak D: Contributor to SER N: Henning Westerholt U: henningw E: henning.westerholt@1und1.de D: Contributor to OpenSER N: Iñaki Baz Castillo U: ibc_sf E: ibc@aliax.net D: Contributor to OpenSIPS N: Irina-Maria Stanescu U: ironmissy E: ironmissy@gmail.com D: Contributor to OpenSIPS N: Iulia Bublea U: iulia_bublea E: iulia@opensips.org D: Contributor to OpenSIPS N: Jan Janak E: jan@iptel.org W: http://iptel.org/~janakj P: F8190A31 FCC0 3F4A 1ACB 84C2 505C 573E CC03 B08E F819 0A31 D: Core developer of SER N: Jamey Hicks D: Contributor to SER N: Jeffrey Magder U: jmagder E: jmagder@somanetworks.com D: Contributor to OpenSIPS N: Jesus Rodrigues U: jerocu E: jesusr@voztele.com W: www.voztele.com D: Contributor to OpenSIPS N: Jiri Kuthan D: Main developer of SER N: Juha Heinanen U: juhe E: jh@tutpro.com W: http://lohi.eng.song.fi D: Contributor to OpenSER N: Klaus Darilion U: klaus_darilion E: klaus.mailinglists@pernau.at W: http://www.enum.at/ D: Contributor to OpenSER N: Kobi Eshun U: ekobi E: kobi@sightspeed.com D: Contributer to OpenSIPS N: Lavinia Andrei Spirea U: lavinia_andreea E: lavinia@opensips.org D: Contributor to OpenSER N: Luci Stanescu U: luci_stanescu E: luci@ag-projects.com D: Contributer to OpenSIPS N: Marco Lorrai E: marco.lorrai@abbeynet.it D: Contributor to OpenSER N: Miklos Tirpak D: Contributor to SER N: Nils Ohlmeier E: nils@iptel.org W: http://www.ohlmeier.org P: BAD6CA40 B5E7 4FB0 800F 0A2C E3E9 4073 C7C3 F275 BAD6 CA40 D: Contributor to SER N: Peter Nixon U: peternixon E: peter@peternixon.net D: Packager for OpenSIPS N: Ramona-Elena Modroiu U: ramona E: ramona@opensips.org W: http://www.opensips.org D: Main developer of SER / OpenSER N: Raphael Coeffic E: raphael.coeffic@gmx.de D: Minor contributor to SER N: Razvan Pistolea U: rpistolea E: razvy000@yahoo.com D: Packager for OpenSIPS N: Ron Winacott U: karwin E: ronw@somanetworks.com D: Contributor to OpenSIPS N: Sergio Gutierrez U: saguti E: saguti@gmail.com D: Contributor to OpenSIPS N: Ricardo Baratto D: Minor contributor to SER N: Stelios Sidiroglou-Douskos D: Minor contributor to SER opensips-2.2.2/COPYING000066400000000000000000000414111300170765700144170ustar00rootroot00000000000000------------------------------------------------------------------------- IMPORTANT NOTES 1) The GPL applies to this copy of OpenSIPS (Open SIP Server) software. 2) opensips software allows programmers to plug-in external modules to the core part. Note that GPL mandates all plug-ins developed for the opensips software released under GPL license to be GPL-ed as well. (see http://www.gnu.org/copyleft/gpl-faq.html#GPLAndPlugins for a detailed explanation) 3) OpenSIPS software is derived from SER (SIP Express Router) project developed by FhG FOKUS. 4) Note that the GPL bellow is copyrighted by the Free Software Foundation, but the opensips software is mainly copyrighted by FhG FOKUS (each source file has posted the copyright holder). 5) In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. ------------------------------------------------------------------------- GNU Licence FAQ This FAQ provides answers to most frequently asked questions. To fully understand implications of the GNU license, read it. - you can run OpenSIPS for any purpose - you can redistribute it as long as you include source code and license conditions with the distribution - you cannot release programs derived from OpenSIPS without releasing their source code ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS opensips-2.2.2/CREDITS000066400000000000000000000030341300170765700144030ustar00rootroot00000000000000#=============# # OpenSIPS # #=============# Current OpenSIPS team in alphabetical order as of 31 March 2016: Core Team: Bogdan Andrei Iancu Ionut-Razvan Ionita Liviu Chircu Razvan Crainea Vladut-Stefan Paiu Module Maintainers: Alexandr Dubovikov Anca Vamanu Arnaud Chong Dan Pascu Di-Shi Sun Iñaki Baz Castillo Ionel Cerghit Marc Leurent Ovidiu Sas Saúl Ibarra Corretgé Dynamic Packet Robert Vladut Patrascu Robison Tesini Evandro Villaron ============== Release 2.2.0 ================= Alan Erringer Antonis Psaras Aron Podrigal Colin Martin DMOsipov (GitHub) Damien Sanders Dan Bogos David Sanders Di-Shi Sun Don Steul Dragomir Haralambiev Dusan Klinec Eric Tamme Eric Werkhoven Eseanu Marius Cristian Evandro Villaron Federico Edorna Giuseppe Cardone Grygoriy Dobrovolskyy Hamid Elaosta Hieu Ta James Criscuolo Jarrod Baumann Jeff Pyle Jock McKechnie Jonathan Hunter Jordan Williams Joseph Frazier Julian Santer Julián Moreno Patiño Maksym Sobolyev Matthew Williams Mickael Marrache Mikko Lehto Minh Phan Mohammad Qandeel Muhammad Shahzad Shafi Nabeel Shikder Nathaniel L. Keeling III Nick Altmann Oliver Severin Mulelid-Tynes Pete Kelly Peter Baines Rik Broers Rob Gagnon Robison Gonçalves Tesini Robison Tesini Rudy Pedraza Ryan Bullock Santhosh Kumar Sasmita Panda Satish Patel Sebastian Sastre Sergey Khripchenko Stas Kobzar Steve Frécinaux Søren Andersen Trevor Francis Walter Doekes AVFedorov (GitHub) arovetto (GitHub) cepehutu (gitHub) ferrored (GitHub) gergelypeli (GitHub) mishehu (GitHub) narunask (GitHub) telephone-man (GitHub) opensips-2.2.2/ChangeLog000066400000000000000000011374461300170765700151550ustar00rootroot00000000000000=========================== Release 2.2.2 ============================== 2016-10-19 Razvan Crainea * [9e406b2] : Increase version to 2.2.2 2016-10-19 Razvan Crainea * [f79d3b4] : userblacklist: sync between reload and read This commit fixes possible crash due to concurrency between the data read while checking the blacklist and reload. Reported by Michele Pinassi Close #884 (cherry picked from commit edad7265c71d2259003e7500872e51c1a461ad9f) 2016-10-19 Liviu Chircu * [ab2a67b] : fraud_detection: Fix docs for "show_fraud_stats" (cherry picked from commit d25633505ceaf053bf96d78583f520e4f446d817) 2016-10-19 Liviu Chircu * [243aacb] : fraud_detection: Fix "concurrent_calls" statistic Ensure the "concurrent_calls" stat is properly decremented for un-established dialogs as well (due to negative INVITE replies). Thanks to @Freenex for the bug report Closes #949 (cherry picked from commit 703ce9098f0f91ee3ffa6ae0254d77903037b32a) 2016-10-19 Liviu Chircu * [bb9fd7c] : fraud_detection: Fix shm memory leak (cherry picked from commit 4a0d22d95b34eed82c87b6b2f836b6d4789e4100) 2016-10-19 Razvan Crainea * [14f72c6] : dns_cache: fix examples Credits go to Pete Kelly for reporting it. (cherry picked from commit 7cdd3ee99d6c46dcde238c8571f21c46e85e82b3) 2016-10-19 ionutrazvanionita * [1c7e19d] : Fix broken ldap async engine Previous ldap async engine implementation was broken. Now the module stores a pool of connections for each session which will be used for executing async queries and one separate connection that will be used for synchronous queries, in case the maximum number of open connections allowed(max_async_connections module parameter) is reached. The sync connection will also be used for synchronous queries from other functions. The connection pool is stored per process, so each worker process will have its own pool and synchronous connection. For large numbers of asynchronous ldap calls per second, having a higher value for max_async_connections will result in better performance. (cherry picked from commit 7d7dd9effd82275b0727d99d2d11ea00e016cad2) 2016-10-19 Razvan Crainea * [1c5911e] : Do not load a protocol module if there is no listener (cherry picked from commit 3593690d5a7da57a00280743a6effbb443be0c33) 2016-10-18 ionutrazvanionita * [757389d] : [sst] fix using unallocated memory bus * sst was freeing shm memory after freeing it in dialog callbacs; * sst was using memory without checking if it was allocated; (cherry picked from commit 33dc084dd553a20cc691b3ec38e17993ac5ae5b6) 2016-10-13 Bogdan-Andrei Iancu * [9ed154b] : Fix CANCELLing of transactions not forwarded. Due async operations, a transaction may be created and it may exist for a long time before any branch is created for it (via a t_relay). The current fix takes care and does proper handling on incoming CANCELs matching the transaction while it has no branch (no t_relay() on it). Old behavior - the INVITE transaction was never getting a final reply New brhavior - the INVITE transaction gets an internally generated 487. (cherry picked from commit fb0d90ec75601304eed98168bb988ebdbf10c260) 2016-10-12 Bogdan-Andrei Iancu * [944fdc1] : Fix consistency and use dialog_id. Dialog_id is printed by dlg_list (along the h_label and h_id) as a simpler alternative to identify the dialog. Also, the dlg_end_dlg accepts both h_label and h_id or a dialog_id. This is needed as the DB exposes only the dialog_id, and the dialogs from DB cannot be correlated anymore with the MI commands. See #963 Closes #963. 2016-10-12 Liviu Chircu * [bea4f4a] : Fix a possible segmentation fault on startup Do not generate a corefile on "listen = udp:eth0:bad_port" errors (cherry picked from commit 2352d57e35c15dab34c046566e5a89cbc5d068aa) 2016-10-12 Liviu Chircu * [de6dba0] : Fix "use_children" to work with interface names (cherry picked from commit c7da1af1642697269eb74865018d017f1a6ee664) 2016-10-12 Bogdan-Andrei Iancu * [b7ff6b3] : Fix documentation errors 2016-10-12 Bogdan-Andrei Iancu * [34bbe68] : Fix regression on is_registered() returning the contact ATTRibutes. The function, upon success, must also return the attributes of the first found contact for the searched AOR. Reported by Rik Broers ( @hydrosine ). Closes #960 2016-10-11 Liviu Chircu * [e31aee8] : Improve parsing for "hep_udp" and "hep_tcp" literals (cherry picked from commit add97f8726a83aa0c408cd7701ce28f9d1f7ff62) 2016-10-11 Liviu Chircu * [f94b24d] : proto_sctp: Fix startup bug caused by a typo (cherry picked from commit e8d9a71a44fc4d9a83ee2f8690904993330f05ef) 2016-10-07 Liviu Chircu * [3fde889] : rest_client: Update documentation (cherry picked from commit efe5dd76e15b9ef06b7cf113c2e3fb7ec1e19d75) 2016-10-07 Liviu Chircu * [7cdab71] : rest_client: Add "connect_poll_interval" module parameter Allows complete control over how quickly we want to detect libcurl's completed TCP handshakes, so the transfers can be started. A lower "connect_poll_interval" will speed up the HTTP transfer, but will also increase CPU usage. (cherry picked from commit f5abfb3dee649a14ae39ad74d7f04454f815748f) Conflicts: modules/rest_client/rest_methods.c Includes fix-commit 3ba7be6692c6987f. 2016-10-07 Bogdan-Andrei Iancu * [db433bc] : Downgrade logs from NOTICE to DBG - part 2 (cherry picked from commit dd518ccdcdc02503ccdbead26f5fc44f2c405093) 2016-10-07 Bogdan-Andrei Iancu * [7b6d0d7] : Downgrade logs from NOTICE to DBG 2016-10-06 Daniel Fussia * [8570c34] : Changed from VAL_STR to VAL_STRING in case DB_STRING 2016-10-05 Bogdan Andrei IANCU * [dfdb2d8] : Merge pull request #962 from tavyc/exec-flush exec: Flush the pipe after writing the message (cherry picked from commit bcf3118ec6b280d90f65a45747abd81c2fcfe44b) 2016-10-05 ionutrazvanionita * [9a7aac3] : [sipcapture] memset contact uri structure to 0 (cherry picked from commit ac66dc14bb736bb5afd70d923db7e7fa065462ff) 2016-10-04 rvlad-patrascu * [70e1d6b] : Use signed type for computing timer drift (cherry picked from commit f8c11e17532db852901b7cc4a040b613d91ed5e3) 2016-10-03 Liviu Chircu * [a9bdf7b] : usrloc User-Agent filtering: Fix memory corruption The "ua_re_check" macro added by commit e5cb9805bc is broken in several ways: * unsafe read operation on shared memory * unsafe write operation on shared memory * incorrectly handled error case, without restoring the backup byte This patch corrects the above issues by extending the user_agent buffer. (cherry picked from commit 58a944cddbfbe7aab47a3e296d8aba5d36454a1e) Includes fix commits: - 03398fbf - e16abb4b 2016-09-29 Bogdan-Andrei Iancu * [c8ee7d5] : Fix computing the pinging partition in timer job. Avoid using static variables in timer jobs as they may be run in different processes, leading to unexpected results. Many thanks to Andrew ( @kertor on GITHUB) for spotting and reporting this. Fixes #959 (cherry picked from commit 82fd3ae683b7fc108d47760f73cbdd80f2dd70af) 2016-09-29 Bogdan-Andrei Iancu * [f10e57f] : Fix documentation for calculate_ha1 parameter Reported by Aqs Younas on the mailing list. 2016-09-28 ionutrazvanionita * [f22b884] : [proto_hep] fix bug introduced in 7190a2d Commit 7190a2d was freeing the extra chunks twice when the message was processed as SIP. Now the original hep context free function is not registered as a context free function, but called manually each time all the hep processing is done. This way we avoid the memory leaks that appeared when the message was not a SIP message, and the chunks were not freed. (cherry picked from commit 63b2a52c2c2d453ccf889cc5e74991a2c81aaccd) 2016-09-28 ionutrazvanionita * [4198511] : [proto_hep] fix extra chunks memory leak Extra chunks (the ones that are not defined in the internal hep structure) were not freed, causing a memory leak for each hep packet that had such chunks. (cherry picked from commit 7190a2d89f54998a88a2b0b208bba50c9bb4027d) 2016-09-28 ionutrazvanionita * [4e1ef8b] : [proto_hep] don't drop packets with port 0 This packets might be different than sip, internally generated that might not necessarily be related to a connection. (cherry picked from commit 7d2ab2bce0c5955b45cdbc88867e81c5e0d93908) 2016-09-28 ionutrazvanionita * [3f26dc2] : [proto_hep] convert to host byte order before allocating custom chunk (cherry picked from commit 89f5ec299f2d9bcaa1a0f5933ab6f59638ee1aa6) 2016-09-26 Bogdan-Andrei Iancu * [9432241] : Extra logging to troubleshooting presence event handling (cherry picked from commit d536c3d41b3738842e531448c53a82791dd5e9b7) 2016-09-23 ionutrazvanionita * [9a38079] : [acc] fixed missed calls callback registration If the missed calls callback was not registered the first time do_accounting was called then it wouldn't have been registered thereafter. A new internal flag was defined to know wheteher or not the callback has been registered before the current do_accounting call. (cherry picked from commit 0592ab6db2f00b586d5f74fadd9f7f65cc00a7cf) Conflicts: modules/acc/acc_logic.c modules/acc/acc_logic.h 2016-09-20 Bogdan-Andrei Iancu * [c26401a] : Fix socket selection for outbound UDP. Do not use the bind_address of the process when comes to send an UDP package (bind_address may be different from process to process, so the selection may give different results, depending on the processes sending the UDP). If you have a sip_msg, try using received interface of the msg (if matches), otherwise use the first UDP interface. (cherry picked from commit 7596eb99fceb68bd95653b76a388f8bc42fee5c6) 2016-09-19 Bogdan-Andrei Iancu * [e921f57] : Remove testing warning message 2016-09-19 Bogdan-Andrei Iancu * [8aebdcb] : Merge previous cherry-pick from trunk 2016-09-19 Bogdan-Andrei Iancu * [73710a7] : Fix async resume in the timer dedicated timer. The timer dedicated timer may end up running script routes (like failure route), so it may trigger some async operations. So, the dedicated timer process must be async enabled. (cherry picked from commit 91e87f18d48b9e66cd052e825ae1f2f3c061955f) 2016-09-19 Bogdan-Andrei Iancu * [e77626e] : Fix starting TCP processes. Do not start TCP main if tcp is disabled - this was introduced with the previous commit, when we split the starting of TCP workers and TCP main (see c2e2a0f141d11a972705f1555d0e2a23ec3f01ed). (cherry picked from commit 542a9c98844bffec96d7cc790c37ef842b43ee66) 2016-09-19 Bogdan-Andrei Iancu * [3767f33] : Fix the TCP forking sequence. Keep the TCP-main the last forked process, to be sure it inherits all the communication sockets from all other OpenSIPS processes. Credits go to Razvan for reporting. Alternative fix for 9fd451fd684ed3c9133bcc0e896de8d28ad45208 (cherry picked from commit c2e2a0f141d11a972705f1555d0e2a23ec3f01ed) 2016-09-19 Bogdan-Andrei Iancu * [7080dda] : Fix building the FROM header in XMPP2SIP. Reported by Igor Pavlov. (cherry picked from commit bf9acc3a15f376f17a8d415a5d48789e19ce3dee) 2016-09-19 Razvan Crainea * [8d4ae49] : usrloc: fix race between update for different registers In case there are two REGISTER messages with incremental CSEQ are replicated in a different order, skip the processing, since this has already been resolved by the master instance. Thanks to Deniz Beskök for reporing this Closes #956 (cherry picked from commit 26f6c1bacb33ddf0bcbd54ccffc65bee8754e01d) 2016-09-14 Bogdan-Andrei Iancu * [a60f28d] : Fix potential starvation for the timer tasks. Even if the timer tasks have priority in the asyns reactor (for being handled), if all the worker processes are already busy in handling some SIP tasks which takes too long, may lead into a starvation of the timer tasks. For the moment we address this by creating a extra worker processes (similar to the SIP workers) which is dedicated to executing the timer tasks. Still, based on their availability and load, the SIP workers are able to consume timer tasks. In the next steps, this issue will be better addressed by implementing the concepts of "preferred jobs" and "capacity reservation" for the processes in the pool. Credits for helping with the investigation and testing go to Ramachandran, Agalya (cherry picked from commit c40f2b057209b8078520abc95e5d1bf5e5a28db6) 2016-09-13 Di-Shi Sun * [68ace2e] : Updated to support SUBSCRIBE/NOTIFY CNAM and OSP Toolkit 4.12.0. 1. Added SUBSCRIBE/NOTIFY CNAM support. 2. Formatted sample configuration file. 3. Updated for OSP Toolkit 4.12.0 2016-09-07 ionutrazvanionita * [a55ac46] : [db_virtual] check if backend exports async function (cherry picked from commit 4537a21ed3a4cbb1a3925e8d5a420d4400366a8d) 2016-08-30 Razvan Crainea * [7261cf0] : packaging/debian: move all current files in a common directory (cherry picked from commit 03b4a7eae52f21b0676d0ac1a337e6a996c2de42) 2016-08-30 Razvan Crainea * [57e94a2] : packaging/debian: use newer version for libmicrohttpd (cherry picked from commit 84d38d617e605cf56e89e098e4b911de2ee2c874) 2016-08-30 Razvan Crainea * [dc732f2] : fix expresion - expression typo (cherry picked from commit c2d0b38d53dac8c8975f2d40a3268b14829e53b2) 2016-08-30 Razvan Crainea * [098e448] : debian packaging: remove non-existent dependency replace dependency of db4.6-util to db-util (>= 4.6.19) for new Debian versions reported by Julián Moreno Patiño (cherry picked from commit 6d7cf35658c22c8c65ddc3b6c54e9320c2198d5a) 2016-08-29 Răzvan Crainea * [1ecb911] : Merge pull request #944 from Deni90/master Added support to fast_lock for mips and mips64el (cherry picked from commit 65d86fd0c362f3081cc0341760c6ebd1b900abaa) 2016-08-29 ionutrazvanionita * [fb761ed] : [acc] Remove accidentally added file 2016-08-29 ionutrazvanionita * [12c5864] : fix bag pkg pointer used for storing accounting flags This closes #906 (Many thanks to @gcuppers for support) 2016-08-26 Razvan Crainea * [313b171] : ws(s): make sure we cleanup the request in the con Reported by Tito Cumpen (cherry picked from commit 39e40f93fe5bc76c58c5b64f8b32b327b975f62e) 2016-08-25 Liviu Chircu * [456fc2c] : fraud_detection: Fix bad script retcode for check_fraud() Thanks to Freenex on IRC for fixing and testing (cherry picked from commit daf63bb229b810c21fee013c5ed01a00652fb850) 2016-08-19 Bogdan-Andrei Iancu * [d30f39a] : Fix is_contact_registered() to accept less params. The last 3 params are optional, so we can invoke the function without them. 2016-08-19 Bogdan-Andrei Iancu * [65d981b] : Fix using is_registered() function with first param only. Reported by neo_land1 on the #opensips IRC channel. 2016-08-17 ionutrazvanionita * [373394b] : [acc] fix missed call flag check macro (cherry picked from commit 5b9006874ab0afd02d70a5317446220002c47449) Conflicts: modules/acc/acc_logic.c 2016-08-17 Bogdan-Andrei Iancu * [2f4d016] : Do not delete any internal AVP if "C" flag is used. When calling do_routing() with "C" flag (only check the prefix, without actually doing any routing), do not remove any AVP (in the beginning) as we do not add any (after prefix matching) -> no adding, so no deleting. This allows you to safely call do_routing("C") while looping throuhg the GWs of another do_routing(). (cherry picked from commit 8fcc77cb87ba76e831466fb0a3da90eb556671d8) 2016-08-09 Liviu Chircu * [0a60671] : drouting: Fix docs Thanks to Richard Robson for reporting (cherry picked from commit 058991d7806c047e42d78ece45577f5dc2929634) 2016-08-05 Bogdan-Andrei Iancu * [e7319c1] : Fix bug in evaluating the port spec in lb_count_call 2016-08-02 Bogdan Andrei IANCU * [2b79d96] : Merge pull request #937 from was4444/1.11 fix: pthread_mutex should be process-shared (cherry picked from commit 97cd5c481bfd9dca4fff941cc86a497d35474704) 2016-08-02 Bogdan-Andrei Iancu * [2e60179] : Removes docs for old MI function "profile_bin_status" was removed when dialog module was migrated to clusterer Credits to Sebastien Couture (@sysreq0) for reporting Closes #939. 2016-07-22 Bogdan-Andrei Iancu * [3fab95a] : Remove unnecessary test. "res" cannot be null at that point (cherry picked from commit 0b60681f0cc08ffd13fbac3dc5cbde17904ad177) 2016-07-22 Ozzyboshi * [9568db8] : Fixed memory leak on modules/drouting/drouting.c This leak occures in function get_group_id() when do_routing is called without parameters in opensips.cfg. In this case the dr_default_grp variable is not equal -1 and 'return dr_default_grp' is executed without freeing the res variable previously allocated for query execution. (cherry picked from commit 65739943151f08aee18a7bc2125c7ac90ee84d00) 2016-07-20 Razvan Crainea * [f090238] : Update ChangeLog for 2.2.1 =========================== Release 2.2.1 ============================== 2016-07-20 Razvan Crainea * [e626dd1] : update version to 2.2.1 2016-07-18 Razvan Crainea * [e99b2cc] : permissions: allow any supported protocol Before this commit, the protocol check was hardcoded in the module. This commit allows to add in the permissions table any transport protocol supported. Thanks go to Kirill Galinurov for reporting this! (cherry picked from commit 0eecff942dc3700e008a9941ac406a565325ef89) 2016-07-15 Liviu Chircu * [97438ac] : rest_client: Fix incorrect trim operation (cherry picked from commit d8bf10da34152612e7835a64cedbb7593b8fc57b) 2016-07-15 Bogdan-Andrei Iancu * [9f79778] : Fix building Via Branch in stateless mode Instead of copying the branch value from a previous statefull Via header (which may lead to overflow as OpenSIPS has a maximum value for the built branch), we generate a new branch value based on the previous Via branch - in this way, we have full controll over the length of the resulting Branch and we also inherit the uniqueness of the branch value (from the prev Via hdr). Reported by Gupta, Rahul and Elliott, Ray (cherry picked from commit 482e643469b351d12418ff54c96beee7b27dca94) 2016-07-15 Bogdan-Andrei Iancu * [09ddd52] : Fix advertised port and address per branch. Before the advertised_port/address are preserved only in the UAS part of the transaction - that means it is only one value and it is from the setting done in Request Route; whatever later setting done in Failure Route or Branch Route are not preserved at transaction level (even if they are used on the spot, for sending out the INVITE). So, when we have to build a local request (ACK or CANCEL), we do not remember which specific address/port were advertised for that particular UAC (branch). Fixes #917. (cherry picked from commit eb850a22f0af83bf75e75dfadbe1320e8b92784b) 2016-07-12 Razvan Crainea * [b40035e] : add debian packaging for proto_wss, proto_tls and tls_mgm Reported by Rik Broers Close #931 2016-07-12 Bogdan-Andrei Iancu * [59011d5] : Fix populating blacklists. When creating the blacklists, use the port and protocol of the destination too, otherwise the rules may conflict or be too wide to be used (like 2 destinations with same IP but different ports). (cherry picked from commit 7fa9cee5037af26fd1c63b70710f2bc03e6a31f4) 2016-07-12 Bogdan-Andrei Iancu * [67f4e25] : Fix populating blacklists. When creating the blacklists, use the port and protocol of the destination too, otherwise the rules may conflict or be too wide to be used (like 2 destinations with same IP but different ports). (cherry picked from commit 97f309c34c7333270a3f0f997868bdb9b94107f1) 2016-07-11 Bogdan-Andrei Iancu * [10c2016] : Fix populating blacklists. When creating the blacklists, use the port and protocol of the destination too, otherwise the rules may conflict or be too wide to be used (like 2 destinations with same IP but different ports). (cherry picked from commit 0495faa7d96f92da13102480ca43cd1befd37ba0) 2016-07-11 ionutrazvanionita * [04f54fc] : [acc][bugfix] check if database loaded before using its handler (cherry picked from commit 8dd10d85b408947576223e75a67228ed7c7abd91) 2016-07-08 Razvan Crainea * [24274b5] : dialog: advance to next cell in case of error (cherry picked from commit 70db2cfcdb0c74e6437db2aa9663fd0a5000ecdd) 2016-07-07 Bogdan-Andrei Iancu * [eb02211] : Fix error message when a route is not defined. Print the name of the route too - the ID is irrelevant for the script writter. (cherry picked from commit 409481818148d7951e1520c9dd4eb715c2bec7c6) 2016-07-05 Bogdan-Andrei Iancu * [a85f454] : Fixed compiling on arm6 with fastlocks Partial revert of 058e16 as we need some extra asm code for fast locking on arm6. See #923 Closes #912 (cherry picked from commit a69c32877b99fbd6eef736eff58c9b6ed8228166) 2016-07-05 Bogdan-Andrei Iancu * [7c3afa2] : Fix script varibles for providing port and proto (as part of SIP URI). If the SIP URI does not have an explicit port or proto, determine the default port/proto in a SIP wise manner (rather than returning 5060 / UDP) (Ex: sip:example.com;transport=tls has default port 5061 and not 5060 ; or sips:example.com has default proto TLS and not UDP). Affected variables are $dp, $rp, $op and $dP, $rP, $oP (cherry picked from commit 6cc850e34dcd022f1ed354bd50d9a1809518fa1f) 2016-07-04 ionutrazvanionita * [2ea5754] : [db_sqlite] remove unnecessary file introduced in commit ccda718 (cherry picked from commit 450265ff4032e4d316d21fa6890946bed2866f87) 2016-07-04 ionutrazvanionita * [ccda718] : [db_sqlite] fix memory management bugs * core free function was used to free starting with commit 980f15ec which was not freeing the memory correctly(result rows were allocated all at once, but in the current version they were freed once at a time); * check if memory is allocated to prevent core dumps; (cherry picked from commit bb506d2563c2670094537e70cc35b85d4ce4cd80) 2016-07-04 ionutrazvanionita * [5cbe3fa] : fix core database engine bug introduced with commit 980f15ec (cherry picked from commit db37c5c411b529acd0cfeee4dfbc7925db6c2901) 2016-07-04 Liviu Chircu * [4da1a06] : acc: Fix incorrect Request-URI handling If the config script does not include any Request-URI altering logic (e.g. no lookup() or $ru manipulation), the acc module would incorrectly fill in a "msg->new_uri" field which should NOT be freed into the SIP request structure, leading to the memory pool being corrupted upon transaction release. Credits to Guillaume Lacroix for reporting (cherry picked from commit 02cb9f048d0a5e1f988aa9ccae1691216ed16ee5) 2016-07-04 Razvan Crainea * [57586d7] : permissions: fix get_source_group() return In case a group is not found, return -1 instead of true 2016-06-30 ionutrazvanionita * [4482329] : [sqlite] fix warning (cherry picked from commit 0b4cbaa41c7f9beb9f2f2c72807781b148b9877a) 2016-06-30 ionutrazvanionita * [db06d61] : [db_sqlite] add explanation about implementation (cherry picked from commit 3ab8237c481f1977d59613b356d03980e6dc2271) 2016-06-30 Jarrod Baumann * [db80f6a] : [db_sqlite] use DB_STRING type when the column value type is DB_STRING (cherry picked from commit a2c799451f3466ccd5239ca3cba408153d8ff35a) Conflicts: modules/db_sqlite/row.c 2016-06-30 Ionut Ionita * [308ff0d] : Merge pull request #919 from Danfx/2.2 Fix memory leak: after sqlite prepare was deleted stmt compiled, free… 2016-06-29 Daniel Fussia * [eea7194] : Fix memory leak: after sqlite prepare was deleted stmt compiled, freed error message after sqlite exec, allocated rows was set to free and it also db_free_row was repaired. 2016-06-29 Liviu Chircu * [d8c4b2a] : module statistics: Properly update the "real_used" memory stat (cherry picked from commit bc3404302d72f484206bfd3a620690f5c2bf6133) 2016-06-27 ionutrazvanionita * [d835721] : [siptrace][bugfix] fix bad macro in siptrace(trace local ip logic) This commit closes #916 Thanks to @Tinet-AaronAn for reporting the issue (cherry picked from commit 2a4f686d4997b814eeaa425dc23fe822cb8ef91a) 2016-06-25 Liviu Chircu * [0c709f9] : Fix compiler warning Using gcc 4.8.4, we would get: net/trans.c:43:2: warning: missing initializer for field ‘name’ of ‘struct proto_info’ [-Wmissing-field-initializers] { }, /* PROTO_NONE */ ^ (cherry picked from commit 376579fa53abb6fc16de76a3070d082c9d958165) 2016-06-24 Bogdan-Andrei Iancu * [8f54897] : Fix dialplan module when using db_text backend. The db_text makes no difference between NULL or "empty string" values in DB -> both are internally translated as NULL . The dialplan module, in a very abusive way, forces "not null" (in DB schema and in the code for data validation) even for columns that are optional (like subst_exp, repl_exp, timerec and attrs). Besides being bogus (if a column is not to be used, you have to set it to empty string rather than let it NULL), it makes impossible the usage of db_text with dialplan. This fix allows (DB and code) the mentioned DB columns to be also NULL. The change is backward compatible, it should not break any existing usage of the dialplan module. 2016-06-24 Razvan Crainea * [d0183af] : Allow proto modules to populate its name (cherry picked from commit 00d052c18bd663e24769b7198a24aefbcd1963dc) 2016-06-24 Razvan Crainea * [cb8f5d7] : proto: always populate default values for protocols name and port This allows you to print the protocol you received, and its associated port (cherry picked from commit 0ae5ba280618e076a7ba658d15db32b877b55442) 2016-06-23 Bogdan-Andrei Iancu * [055172e] : Merge branch '2.2' of github.com:OpenSIPS/opensips into 2.2 2016-06-23 Bogdan-Andrei Iancu * [2fac489] : Fix wrong number of columns when preloading via a DB backend without Fetch support. 2016-06-23 Razvan Crainea * [bb8bd33] : init: use the specified CFGFILE in debian init In case the CFGFILE changes in the init script, opensips will still start with the compiled file instead of the one configured (cherry picked from commit 030efef84d240646ed8e99616b8a6eb64f7d17d2) 2016-06-17 ionutrazvanionita * [c1aa55e] : [sqlite][bugfix] free column names when freeing the result (cherry picked from commit cf380773cec9f91ad08e343c03261154dabc36a5) 2016-06-17 Razvan Crainea * [37c46b8] : Update packaging to 2.2.0 2016-06-17 Bogdan-Andrei Iancu * [968a8a8] : Fix proper testing of context content If a STR is register to the context, a valid STR * will be always returned , so we need to test the content, not the pointer. 2016-06-17 Razvan Crainea * [c6dddd0] : rtpproxy: no need to update the len for IPv6 (cherry picked from commit 5cfd916ca41ed60c9997f95d05694c9083b743b1) 2016-06-17 davesidwell * [fd3841d] : Update rtpproxy.c When moving stored parameters from Branch AVP to Dialog variable, make sure that the integer value "setid" is handled correctly so that it remains valid for the remainder of the dialog. (cherry picked from commit e267e9eb83e2345ca716b897975c2fbfbb4e75ce) 2016-06-17 davesidwell * [503f127] : Update rtpproxy.c When building I/O vector for command to rtpproxy in "force_rtp_proxy_body", after an IPv6 address is detected and the "6" option is added, increase the length component in the I/O vector so it is passed correctly. (cherry picked from commit 4e73871adba3f48b67a9ebbf20a2d9c60b823be8) 2016-06-16 Andrey Vorobiev * [20aea65] : Add support for SIP URIs without user part in 'avpops' module. This module provides a set of avp_db_xxx functions which take pseudo var and flag as first argument. Flag can be one of username, domain, uri or uuid. Previosly avp_db_xxx functions used to return an error if flag has been set to username, domain or uri and specifed pseudo var did not contain username and host after parsing as SIP URI. This behaviour seems to be strange because if flag is being set to domain/username and sip URI contains domain/username we can still load/store/delete this variable. (cherry picked from commit 459f2e90ed277a0371a3325b27038213c5083e8c) 2016-06-16 Bogdan-Andrei Iancu * [04945d2] : Fixed failure to enable NAT pinging. Commit 8f0c677 introduces a bug in testing is usrloc domains are available, and if not, to disable the NAT pinging in nathelper. The test is wrong as it is done in mod_init, while registrar module registerd the usrloc domain via fixup function, after the mod_init section. Reported by Nick Altmann (cherry picked from commit f150fb75611a6e61eabc134f266064bd23f1c850) 2016-06-16 Bogdan-Andrei Iancu * [e690461] : Fix memory corruption on $stat() vars The $stat() var specs are kept in pkg, while the name (if stat not found) is kept in memeory. This means all the copies (form all procs) of the specs will point to a single shm block. It is not safet to free this block as you have no idea how many copies of the spec (from other processes) still refer the name. Closes #902 (cherry picked from commit 4baead765179f6bfbc098b3e59791d7bbc4a8d42) 2016-06-15 ionutrazvanionita * [a0cf37a] : [acc][bugfix] enable acc flags refcount only after dlg callbacks called (cherry picked from commit a9a8fba51da95d3244ab46e22b3ed6cd8d0bea9e) 2016-06-14 Bogdan-Andrei Iancu * [4357fe8] : Fix docs on missing module dependency. If replicate_contacts_to parameter is turned on, usrloc depends on the clusterer module. Credits go to Sammy on mailing list. (cherry picked from commit 735958d87fe73a823a83847bf0d3e822c60aa515) 2016-06-09 Bogdan-Andrei Iancu * [9512363] : Fixed forcing FAST LOCKS for arm6. Use FAST locking support for arm6 too. For Linux, use POSIX as default locking support. (cherry picked from commit 058e16f2a76df9be4e48fa6866b096502981d530) 2016-06-09 Bogdan-Andrei Iancu * [d090114] : Fixed migration of Call Center tables Reported by John Quick. 2016-06-08 Bogdan-Andrei Iancu * [28c1946] : Fix evaluating the SIP port from a SIP URI. Use the new function get_uri_port() to evaluate the port and proto, in a SIP wise manner, form a SIP URI. If port/proto are not explicitly set in the URI, consider the default port for the used transpor proto. If protocol misses, we assume the default protos according to the URI schema. NOTE: it is completly bogus (and dangerous) to assue 5061 if SIPS schema is used as: 1) SIPS can use used with WSS, which actually has 443 port 2) TLS can pe required via transport param in a SIP schema URI too (cherry picked from commit 4c54bcd8bf0426f899b82505e493c97ef5378c79) 2016-06-08 Bogdan-Andrei Iancu * [96cd9c5] : Consider WSS also a SIPS protocol Reported by Varghese Paul ( @varghesepaul ) Closing #899. (cherry picked from commit 39e0debef3174fab9fd2445e57a265ff5394eb1c) 2016-06-07 ionutrazvanionita * [b9171c5] : [mi_xmlrpc_ng] fix fault incompatibilities with xmlrpc standard (cherry picked from commit d57c2fd7b20cf85a897d36d23e47df440fb3321c) 2016-06-07 Vlad Paiu * [8ef8d79] : Removed compilation warning implicit declaration of function â"sched_yield" [-Wimplicit-function-declaration] 2016-06-03 Bogdan-Andrei Iancu * [0e1cea7] : Removed wrong "NOT NULL" constraint over the contact DB fields The code actually handles the case of NULL callee / caller contacts, so DB must allow NULL values. 2016-06-03 Bogdan-Andrei Iancu * [001c2a8] : Removed unused "error" jump label (cherry picked from commit 5819b0fa7033bf2a6daef824bc21dd4ba497ae1b) 2016-06-03 Bogdan-Andrei Iancu * [eb11687] : Fix error handling in DB writes. If inserting/updating a dlg record in DB fails, do not exit with error, but continue with the rest of the dialogs. Credits for finding and reporting this go to Rik Broers @ Motto (cherry picked from commit 0ca6cf035471fc010366e4d29ced5d6fd63199ab) 2016-06-03 Bogdan-Andrei Iancu * [8f0c677] : Proper handle the case of no usrloc domain registered. Prevent get_next_udomain() crashing if there are no usrloc domains registered (reported by Qasim Akhan on users mailing list. Do not enable the pinging support in nathelper if usrloc has no registered domains. 2016-06-01 Liviu Chircu * [29995e4] : load_balancer: Fix a possible crash Could have led to a segfault when OpenSIPS would run oom Reported by Qasim Khan (cherry picked from commit 4081fa349eeb1d3836186bfb0a56c41e7081c330) 2016-06-01 Bogdan-Andrei Iancu * [0269b44] : Fix documentation on former b2bl_key_avp module param (cherry picked from commit ca21c6d2871e01d093c69371ddcc204c5af0355a) 2016-05-30 Razvan Crainea * [086cd47] : sync changelog for latest 2.2.0 =========================== Release 2.2.0 ============================== 2016-05-30 Bogdan-Andrei Iancu * [f595cf4] : Fixed interface matching in loose_route() If port is missing in Route URI, do not assume default 5060, but consider the schema and protocol (they may require a different default port). Ex: sip:10.0.0.5;transport=tls was previously handled as sip:10.0.0.5:5060;transport=tls, which is wrong as the default TLS port is 5061 Reported on mailing list by Ravitez Dondeti. (cherry picked from commit 3593a5f5b2e012154b1c9a00e1645c67c5ece91c) 2016-05-30 Bogdan-Andrei Iancu * [ff237c8] : If POSIX sems are used, be sure to link against pthread lib (cherry picked from commit f7de6406056cbae95ba9f7521d822d280afbe6a2) 2016-05-30 Bogdan-Andrei Iancu * [77701c4] : Fixed proper cleanup on failure of tcpconn_new() The chances for going for error cases are very low, still let's do it in the proper way (cherry picked from commit 57c163b1b7680d3cc01227dbca7676e532aa7539) 2016-05-30 Bogdan-Andrei Iancu * [5cc7999] : Disable QM and DBG for memory manager. 2016-05-27 Razvan Crainea * [a26b0a3] : Update ChangeLog with latest changes 2016-05-27 Razvan Crainea * [5e87139] : promote 2.2.0-rc2 to stable 2016-05-27 ionutrazvanionita * [e432851] : [acc] fix double free issue for multiple dlg callbacks The last byte in acc flag mask now holds a ref counter in the last byte. The counter is increased each time a dialog callback is invoked, and decreased for each free function corresponding to a callback. When the ref counter reaches 0, flags can be freed. The atomicity of the ref counter is based on the fact that dialog callbacks and their free functions are called sequentially, one after the other. ACC_DIALOG_CONTEXT and ACC_CDR_REGISTERED flags where moved in the 7th byte of the flags. (cherry picked from commit 7834aa5d86187c5fda416fb2763510a015da351a) 2016-05-27 Vlad Paiu * [7a846e9] : Link the dialog cell into internally generated requests from the dialog module Fixes #839 (cherry picked from commit 66937a2834d5680031f81c881719b43018fa06ad) 2016-05-27 Razvan Crainea * [e8beb7b] : add license to rw_locking.h header (cherry picked from commit e3a57ac953259a0254a07198aabc88eaac9da9c2) 2016-05-27 Bogdan-Andrei Iancu * [cad565d] : Fixed missing MI reply on pua_publish. Properly send an MI reply for the async pua_publish with Expires 0. Many thanks to Damien Sandrs for invetigating and reporting. Closing #657 (cherry picked from commit 60d01efe811ac7eeaa063032a309b1895c59e92f) 2016-05-26 Liviu Chircu * [e98d96a] : Improve error reporting for unknown script vars Fixes #574 (cherry picked from commit 97ace293dd4c7eb2fe8cd8fbee693455a5737861) 2016-05-26 Razvan Crainea * [f210f84] : clusterer: proper translation to bigint for dbtext This creates the proper type for bigint in the clusterer table for dbtext Thanks go to Trevor Francis from 46Labs for reporting this Close #699 (cherry picked from commit 5bae37316277f8fa31659c5e334e47f0ca3b78ab) 2016-05-26 Razvan Crainea * [1bfd454] : dbtext: convert DBG to ERR for better debugging (cherry picked from commit 0f08b26e40b31bb8af325ba62cfa32676c62ae86) 2016-05-26 Liviu Chircu * [270b757] : dialplan: Improve documentation Added paragraphs regarding rule types along with explanations on they way priorities and rule tie-breaking work. (cherry picked from commit 46894f819d1d7d6092129b334e33956e89e5d4ea) 2016-05-26 Razvan Crainea * [6d74ed1] : Update makefile to support gcc 6.1 2016-05-26 Liviu Chircu * [4dd88e8] : dialplan: Fix a rule tie-breaking bug When an input string would match both a "string" rule and a "regex" rule, the priority based tie-breaking was incorrect (now favouring the bigger priority instead - opposite to default module behavior). Thanks to @paolodepa for suggesting this fix Fixes issue #696 (cherry picked from commit cbdd79ab3f6d772c6ef1fb7afc44990e9cad294e) 2016-05-26 Razvan Crainea * [fe5a869] : acc: properly detect parsing errors (cherry picked from commit d6a44c0e77216f2b22aefdcffebd808374a9be85) 2016-05-25 Liviu Chircu * [eda5580] : acc: Remove deprecated modparam dependency (cherry picked from commit d983d2875ef5dbbbffcdc62855f49b0cf3994459) 2016-05-25 Razvan Crainea * [109a89f] : call_center: obey lock_set interface 2016-05-25 Bogdan-Andrei Iancu * [ffad254] : Fix warning in SYSTEM V locking functions (cherry picked from commit 53b3f402a4d9fb0ff1ba30288d810d41d6b55b1b) 2016-05-25 Bogdan-Andrei Iancu * [08fe6f5] : Fix bogus set locking functions. It is used for SYSTEM V locking only, looks like a copy'n'paste error. (cherry picked from commit 4bcdf4e6aee07ef965f6ae573b289f3fd082fdde) 2016-05-25 Bogdan-Andrei Iancu * [952f652] : Proper support for USE_POSIX_SEM option in Makefile (cherry picked from commit 5e40aaf9c3ed8ab4381d847f6a1f6a01a8113bf7) 2016-05-25 Bogdan-Andrei Iancu * [9bb5e71] : Fix bogus destroy of a RW lock. (cherry picked from commit 83f394579a88a6a9185342cf5f92401ef5c18e5a) 2016-05-25 Bogdan-Andrei Iancu * [a3887b5] : Obey USE_SYSV_SEM and USE_PTHREAD_MUTEX. If USE_SYSV_SEM or USE_PTHREAD_MUTEX are defined via Makefile.conf, do you force autodetection (based on arch type) of the locking support. (cherry picked from commit 39592dc9f25ecee982156e365d160642bdde48e8) 2016-05-25 Bogdan-Andrei Iancu * [e06048f] : Fixed typo (cherry picked from commit 1503ca6f2311cd635f4732d180d670eae04ac466) 2016-05-25 Liviu Chircu * [8239ff1] : mi_xmlrpc_ng: Remove an incorrect free operation Completes commit d047e109 (cherry picked from commit 99f039e37cd79621b2dcc2b5ec2decc9e7c39670) 2016-05-25 Liviu Chircu * [bde14cd] : mi_xmlrpc_ng: Fix NULL pointer dereference Any MI tree with a topmost Array structure would have caused a crash (cherry picked from commit 555fb8a3cc91ffc48ae6629a47936391cfe6b2bc) 2016-05-25 Liviu Chircu * [638e5d5] : mi_xmlrpc_ng: Fix a shared memory leak (cherry picked from commit d047e1096e2f6624f776d9a031f92be3cc9a0a0d) 2016-05-24 Liviu Chircu * [bc5f651] : TCP alias lookup: Add matching by transport Further improving commit 13970bb. (cherry picked from commit ea699f0116a28cd03a0d1ff50da3fc6045ac3da5) 2016-05-24 Liviu Chircu * [1a93613] : TCP connection lookup: Add matching by transport The fact that all currently supported TCP-oriented protocols have different standard ports is not an excuse for not including the transport into the match condition (i.e. dest IP address + dest port + dest transport). This may very well prevent issues with messages being sent out over the wrong TCP connection in a number of scenarios where we employ TCP connection reusage over both plain TCP and TLS with a Proxy that specifies the same port in the topmost Via header when it establishes each of the two connections. (cherry picked from commit 13970bb65253f1b35156466c6f514a4c520d8893) 2016-05-24 Liviu Chircu * [bc996b4] : Revert "Accept TCP aliases by default" This reverts commit d62bc967b0d6784d2baced88b895da57f3f4ab9a. Firstly, TCP connection reusage (RFC 5923) should only be employed between TLS endpoints - only TLS allows the endpoints to authenticate each other during connection setup, preventing unauthorized connection hijacking. Secondly, aside from any security considerations, RFC 5923 must not be employed fanatically (Proxy-Proxy, Edge-UA, UA-Proxy) on any TCP connection (especially disregarding the ";alias" Via parameter!), but rather only between adjacent SIP entities who can both initiate a connection towards each other, and also support the RFC. In the real world, we're basically only talking about the Proxy-Proxy usage case, as any SIP UA scenario will most likely involve some form of NAT and a Proxy that cannot open connections backwards, towards the UA. In these cases, TCP connection reusage is _completely_ out of the question, as a single UA might end up receiving all calls of all users behind their public IP. Thanks to Jonas Borjesson for reporting the problem in the first place. (cherry picked from commit 715339fdd25bc74797e6f978d164eb0c4d5669ce) 2016-05-23 ionutrazvanionita * [4beba6d] : [mmgeoip] allow chosing memory caching type Before GEOIP_MMAP_CACHE was used causing a segmentation fault when database file was changed. Now users can choose between reading the database directly from file, cahing it into memory with the possibility to change the database file at runtime. (cherry picked from commit ecf5032aaafbe5a93bbfc3562c4b3358548839e7) 2016-05-23 Bogdan-Andrei Iancu * [71b5274] : Fix potential 100 Trying duplicates. Added the "auto_100trying" module parameter to allow the script writter to take control (from script level) over when the 100 Trying reply is to be sent back. By default, the reply is automatically generated when the INVITE transaction is created. Fixes #833 (cherry picked from commit b023c93a8c476e70b1550da4f46c91d6e2679442) 2016-05-23 Bogdan-Andrei Iancu * [c5bb0d0] : Added extra log message on openssl 1.0.1e-fips bug. If CRYPTO_set_mem_functions() fails, make a note on the openssl 1.0.1e-fips bug, so users may have a hint on the actual issue. See more #834. Closes #834. 2016-05-20 Razvan Crainea * [0668ec7] : Upgrade to release candidate 2 2016-05-20 Liviu Chircu * [dc77cfd] : Fix invalid memory access with async MySQL queries Due to the way the libmysqlclient query result strings were managed, they would be freed by the library before OpenSIPS would copy them into the output AVPs. This patch fixes the issue by adding a new DB API function which handles the cleanup of async query results. Reported by Ovidiu Sas Addresses issue #873 (cherry picked from commit 177c60a0e13471a87d80f21504894b883038ffa4) 2016-05-20 Razvan Crainea * [a080811] : th: fix dlg flag check (cherry picked from commit 1b5802d2e4876e0d63eeeec5a6edcb7a2321e9d0) 2016-05-20 Razvan Crainea * [1dd0afb] : re-register topo hiding callbacks after restart Thanks to Pete Kelly for reporting this (cherry picked from commit 4c84bd84ad67e71f174b3297ff3432ca21bafab4) 2016-05-20 Liviu Chircu * [0e22144] : clusterer: Fix bad doc example Reported by "alias_neo" on IRC (cherry picked from commit fd0ddaa8587c38dda74a5f0e853a283724281252) 2016-05-20 Liviu Chircu * [3d91ab1] : Fix a string concatenation operator bug This patch fixes an issue with string concatenation where any NULL operand would cause all operands to its left to be ignored when computing the result of the expression. Reported by Ovidiu Sas Closes #870 (cherry picked from commit a420752f436a1f952c255df8d5dbe9ea59b0d653) 2016-05-20 Vlad Paiu * [0fa379c] : Process sequential messages received in CONFIRMED_NA state Due to UDP race conditions, sequential messages may be received before the ACK confirming the initial INVITE ( eg. Re-INVITE sent immediately as the call is established in order to force a specific codec ) - also push these requests through the whole dialog processing in order to avoid any future issues ( eg. CSEQ updating which, if left unprocessed, will lead to generating out of order sequentials by OpenSIPS ) (cherry picked from commit bfaf02242a98c464dfbdcd3c3ebf3fbb555bcc47) 2016-05-19 Bogdan-Andrei Iancu * [21ad02f] : Fix bogus timer ref counter during expire update. Many thanks to Trevor Francis for reporting and helping with troubleshooting and testing. (cherry picked from commit 047b1b3e32a9af30dd44f2487460975ed7b3a815) 2016-05-19 ionutrazvanionita * [dea3a12] : [dispatcher] multiple fixes * use pkg memory instead of shm for fixup variables * fix int list building - avoid building a circular list 2016-05-19 Bogdan-Andrei Iancu * [a41b6c4] : Fixed preserving 3xx Contacts in TH without dialog Reported by Pete Kelly. Closes #656. (cherry picked from commit 6184b96c94d0fcc298dbe5bce1cd0ca9fb0445d3) 2016-05-18 ionutrazvanionita * [8ae32d8] : [mi_xmlrpc] return code rework for errors Mi_xmlrpc_ng now returns only 200 OK as HTTP code. We have two labels for inspecting mi level faults, "code" for the error code and "message" for the error message. Check http://www.tutorialspoint.com/xml-rpc/xml_rpc_fault.htm for more details. (cherry picked from commit 2bb871d0e77a739aac310235a1d042f3241d02e0) 2016-05-18 ionutrazvanionita * [24d63b6] : [mi_json] minor fixes in mi_json (cherry picked from commit c091d068768d76fe34ea67ebb010b30082d55ca5) 2016-05-18 Liviu Chircu * [cf7b16f] : usrloc: Fix some uninitialized memory issues Reported by Minh Phan Closes #878 (cherry picked from commit 38f525d14ab03df94cf5194cd60543636d1d73b3) 2016-05-18 ionutrazvanionita * [4a086b6] : [mi_json] return jsonrpc compliat error message (cherry picked from commit 26a66f6aa2fb957a16ed000cc10360bf5f648e18) 2016-05-18 ionutrazvanionita * [841343b] : [mi_json] remove double inverted commas for internal error messages (cherry picked from commit b5a51a964ee1658f41ad435190b14f82edfedffd) 2016-05-18 ionutrazvanionita * [07dbc08] : [mi_json] return code rework for errors Mi_json now returns only 200 OK as HTTP code. We have two records for inspecting mi level errors, "code" for the error code and "reason" for the error message. (cherry picked from commit 644ea2938f4a8b3f3a25aff1529374f9dfc68ae0) 2016-05-18 Razvan Crainea * [6a22f40] : nathelper: ping WSS clients when using natping_tcp See ticket #875 (cherry picked from commit 54a306ae68c6ccb30caff0eb470a40517a687827) 2016-05-18 Bogdan-Andrei Iancu * [515484f] : Fixed bogus memory operations. Fixed bogus free (of uninit pointer) if parse_contact() fails Fixed shm leak on errors after print_rr_body Fixed shm leak on success if record_route is present. Reported by John Nash. (cherry picked from commit 4b0fca533cd7be4a45c1381c78f2b37aaba6152b) 2016-05-18 ionutrazvanionita * [f8a3a1d] : [sipcapture] fix possible data type loss (cherry picked from commit 1c811ceb987b678e81af6b7669b219adf15250a1) 2016-05-18 ionutrazvanionita * [aeed4cb] : [sipcapture] fix bad time calculation (cherry picked from commit d54dc2d0234825a92497fea1c3df08eec4afac83) 2016-05-11 ionutrazvanionita * [eff04e0] : [dispatcher] fix double allocation issue (cherry picked from commit d6891363e3b3b4047a1948eb03fb6b6bab285885) 2016-05-09 Razvan Crainea * [3ca4bfb] : Update ChangeLog for 2.2.0-rc1 2016-05-09 Razvan Crainea * [7e7a2df] : Update name to opensips-2.2.0-rc1 2016-05-09 ionutrazvanionita * [3c75721] : [nathelper] fix bad logic regarding the timer list (cherry picked from commit a86d2c4edcf9c62778a2a9d2438d57fac869bc10) 2016-05-09 ionutrazvanionita * [2330afa] : [sipcapture] fix sip_capture table schema (cherry picked from commit 8fd97163058c798f45d381b124cc465261e9e1e5) 2016-05-09 ionutrazvanionita * [22bdc37] : [siptrace][bugfix] bad structre used when printing HEP trace_id via mi (cherry picked from commit 8c6d9bacb580960f1a0a642a5be5a5ffeb5f958a) 2016-05-08 Liviu Chircu * [cd4101a] : event_virtual doc: Fix grammar/typos (cherry picked from commit 3fd96863cb611d1eefad3b175535944545253f19) 2016-05-08 Liviu Chircu * [664ec6b] : event_flatstore doc: Fix some typos (cherry picked from commit 3cedcc4af994aa8ce132547c325c13cbc8d2cd07) 2016-05-06 ionutrazvanionita * [f5753f9] : [siptrace] fix hash calculus for extended hep trace_id (cherry picked from commit 5e261d7e9f1ea632806370c16cb0f733cee4edfc) 2016-05-06 ionutrazvanionita * [91c3247] : [sipcapture] allow any hep version for hep_net variable (cherry picked from commit bcbded5653d5703b53ed4bc961a8a66d724d3e32) 2016-05-06 ionutrazvanionita * [d09775b] : [sipcapture] fix new hep proto names string (cherry picked from commit 3c15d2d89ec1ab8ad01a2e29a844eb3084128abf) 2016-05-06 ionutrazvanionita * [c939605] : [sipcapture][bugfix] fix bad string hex value parsing (cherry picked from commit 5d6b927d41988e3170ab37889fb26c8026dce29d) 2016-05-05 ionutrazvanionita * [454a557] : [siptrace][bugfix] fix trace_id parsing (cherry picked from commit 2878741afc71620c5910f689c07db38b5003a48c) 2016-05-05 ionutrazvanionita * [ed3f224] : [sipcapture][bugfix] invalid static vector size calculation (cherry picked from commit b8a82b159d220389513808c8fb8f651fc1498cda) 2016-05-05 Liviu Chircu * [5589ffe] : dialog: Remove a chunk of dead code (cherry picked from commit fd330577d66de599430694334e8605ae8b46b9d9) 2016-05-05 ionutrazvanionita * [55f45c4] : [sipcapture][bugfix] invalid memory access (cherry picked from commit 8056a984ce914d6585aef6054d727d82f72da3c6) 2016-05-04 renicok * [f12cf9b] : Updated opensipsctl.base to also search /sbin The md5 utility is located at /sbin/md5 on FreeBSD 8.3. An additional common location was added to allow the script to accommodate this. (cherry picked from commit ca87f55ad071257aa3b891347aadb55a7729d25b) 2016-05-04 ionutrazvanionita * [ef5f3b5] : [sipcapture] fix bad conditions (cherry picked from commit 4caa2ac37177f2ad59c8f2de23fcc3b12c11fe69) 2016-05-04 ionutrazvanionita * [c24abca] : [sipcapture] be able to set proto_type in report capture function (cherry picked from commit e3466c4adecf11fa6d7da206359847c3a1c4b0f9) 2016-05-04 Razvan Crainea * [2d4d2ff] : ratelimit: initialize the length of the algorithm (cherry picked from commit 0eebf1058574c622ba3532fb520908da1c1d94a5) 2016-05-04 Razvan Crainea * [ebaad38] : Makefile.conf: add deps for tls_mgm and proto_wss This prevents 'make menuconf' from deleting these modules from the generated configuration file (cherry picked from commit 616b2ba189771a499c76d645627869270555511d) 2016-05-03 Răzvan Crainea * [83a23be] : debian: add missing dependency Ovewrite lintian warning 2016-05-03 Razvan Crainea * [1e99bb0] : dialog and usrloc: broadcast replication data all time This prevents clusterer module from marking a node as not reachable when there is no traffic at all. Fixes #849 (cherry picked from commit 7f7f8711ccfd0aabb71b124fbb7f4c3e8afd6e37) 2016-05-03 Razvan Crainea * [d02ae4f] : regex: fix spell-check in package (cherry picked from commit 647ea214f1892f8901f562bc45e0e1e33b699aba) 2016-05-03 ionutrazvanionita * [30d8048] : [acc][bugfix] fix memory leak * fix leak caused by missed calls - receiving a negative reply caused flags not being freed 2016-05-03 Razvan Crainea * [a1af71c] : proto_bin: remove debug INFO message (cherry picked from commit d388e804a320aa0f267e47cbcbaf473d519fab31) 2016-05-03 ionutrazvanionita * [4389c26] : [siptrace] fix bad cherry-pick conflict solving 2016-05-03 ionutrazvanionita * [090525c] : [sipcapture] updated docs for HEPVERSION 2016-05-03 ionutrazvanionita * [e547ec6] : [fix functional bug][sipcapture] new HEPVERSION script variable (cherry picked from commit 136fcb65edd4ca00cb15f088de3e58f960385518) 2016-05-03 ionutrazvanionita * [a9a9d7f] : [proto_hep] correctly name function (cherry picked from commit 853b0bbb759d3c58f00b0749e90e4016dd500891) 2016-05-03 ionutrazvanionita * [608881b] : [hep] updated docs for previous commit (cherry picked from commit f9c6cdd2acf93062c252f9fc64faa6327e696718) 2016-05-03 ionutrazvanionita * [b2ff3cc] : [fix functional bug][hep] multiple versions/protocols can be used in the same time Since commit 0873c14d1b81887823242a955217dcc854eb97c9 multiple network protocols can be used from one module. Proto_hep was changed to be able to use both tcp and udp in the same time. Moreover, there is no hep_version needed since we can receive HEPv1,v2 and v3 on any UDP interface. Since HEPv1 and v2 are not compatible with TCP, only HEPv3 will be support. For HEPv3, Sipcapture module can now relay packets from TCP to UDP or the reverse way. Siptrace module was also improved. Trace id's can now be defined with hep version and transport protocol, in order to be able to do multiple types of tracing with different HEP protocols and/or network transports. (cherry picked from commit 45a3aef8e151a03966a7739c2f880a3fdb0d6811) Conflicts: modules/siptrace/siptrace.c 2016-05-03 Razvan Crainea * [5e8f0ac] : [fix functional bug]allow a module to register multiple protocols (cherry picked from commit 0873c14d1b81887823242a955217dcc854eb97c9) 2016-04-29 ionutrazvanionita * [e82a5b3] : [drouting] bug fix and memory leak fix * free gateways list for each carrier at reload * use map_find instead of map_get to search an element in the tree (cherry picked from commit f9f3b83ef621850c2c128b12b9bf6ab2e78e5ee1) 2016-04-28 ionutrazvanionita * [19421eb] : [siptrace][bugfix] use the correct sockaddr structure for storing (cherry picked from commit 556f394212bb07c6f7641f3b1df954d52ff730a5) 2016-04-28 ionutrazvanionita * [ba0fcc2] : [proto_hep][bugfix] use the correct port when unpacking (cherry picked from commit 3dda4131b458bc6f3145196f7661671571dc948e) 2016-04-28 ionutrazvanionita * [c2e8c17] : Revert "[hep][bugfix] correctly store ports/addresses" This reverts commit 7b1359adfcd55f12fd3739c04e30e1c12b91a71b. (cherry picked from commit ab2292945b19f00faa063c457a0efce384b8f245) 2016-04-28 ionutrazvanionita * [4cce28a] : [hep][bugfix] correctly store ports/addresses (cherry picked from commit 51e6f102daac7d0de9a7ca4efea74d5c559746fe) 2016-04-28 Bogdan-Andrei Iancu * [70a3018] : Enable debug_mode settings only if debug_mode gets set to enable. (cherry picked from commit e7c14bc0c85c888040aec7f0949b085c5cee38e7) 2016-04-27 Bogdan-Andrei Iancu * [5c8869b] : Avoid space in MI node name 2016-04-27 Bogdan-Andrei Iancu * [555c0e7] : Fixed wrong MI tree in clusterer_list (related to previous commit) (cherry picked from commit fa4e7f0fa10c6e987b1b69bc5302f0d12a3f8714) 2016-04-27 Bogdan-Andrei Iancu * [aa856a7] : Fixed arrays in clusterer_list MI command (cherry picked from commit 31943a4e2c4ae84babc181f4e5a31e33f2d6b63a) 2016-04-26 ionutrazvanionita * [0e81a70] : [tls_mgm] properly handle SSL_ERROR_SYSCALL on SSL_shutdown (cherry picked from commit bdcee39ebf04f8a74b926dc6e6c311487d3ef601) 2016-04-26 ionutrazvanionita * [dbdc46c] : [siptrace][bugfix] avoid double free on cancelled transaction (cherry picked from commit 3c6b6930d615adb978dd2779b6cd4054c3bc49df) 2016-04-25 ionutrazvanionita * [a9b0011] : [sipcapture] fix incompatibilities with HOMERv5 db schema (cherry picked from commit f75687847a142f18b44c49a0b274457d7039f165) 2016-04-25 ionutrazvanionita * [85b126a] : [sipcapture] remove time-consuming unnecessary chunk of code (cherry picked from commit cc7a2ea114444b82645e484a1cf728c4464c17dd) 2016-04-25 ionutrazvanionita * [d49afa6] : [proto_hep][bugfix] fix bad sip payload size calculation (cherry picked from commit eec5045d44ec1fc29aba9a6d5d024e38ace10ae1) 2016-04-25 ionutrazvanionita * [e6258da] : [acc][bugfix] correctly store do_accounting() flags Flags are now moved from processing context into transaction context when the transaction is created, this way having visibility for the whole transaction. (cherry picked from commit 41bcd2559b29a23e96636c6c2dfc5628f69ae5b7) 2016-04-25 Bogdan-Andrei Iancu * [507db77] : Fixed MI nodes in 'sip_trace' command. Formatting chars must not be included in the MI tree (cherry picked from commit 7811e2d6b6013fe21c744531299f490671462a05) 2016-04-24 Bogdan-Andrei Iancu * [0a1fc05] : Fixed default FIFO file name (cherry picked from commit ffdbb970bfa60dcd1ed7d88fc94c4a07f9356ad9) 2016-04-24 Bogdan-Andrei Iancu * [b7ac4fc] : exclude proto_wss, use QM_MALLOC 2016-04-24 Bogdan-Andrei Iancu * [7aef53c] : tls_list() may be called even without DB support Avoid locking if DB support was not enabled. (cherry picked from commit e6944bb064750611585d0fa516551b944aa44cdc) 2016-04-22 ionutrazvanionita * [eee4fae] : [proto_hep][bugfix] check memory allocation (cherry picked from commit f5ad753d56a95d68bc937e0721895d6ee4aefd43) 2016-04-21 ionutrazvanionita * [21ca280] : [siptrace][bugfix] sequential requests - set context for sl callbacks (cherry picked from commit 943d9015f8f1e3467acafee67693f157beab9755) 2016-04-21 ionutrazvanionita * [6ce202a] : [context][bugfix] each context has its own destroy functions vector Credits to Razvan Crainea (cherry picked from commit 53263b2a65dc771cb660078f4b20171a48f04248) 2016-04-21 ionutrazvanionita * [576a730] : [tm]fix context functions header (cherry picked from commit 59e5a4165f88c2a4a4dbb5e159786c441f6fa502) 2016-04-20 Liviu Chircu * [4cd99ec] : MI library: Add comments regarding a known issue Currently, MI arrays cannot contain objects with non-identical "name" attributes. This patch also quickfixes this issue in mi_list_all_profiles() by dropping the array idea altogether, in favour of a top-most object. (cherry picked from commit 6c105015c3a0a02c6f65822f5ce52e12bd0ae108) 2016-04-20 Bogdan-Andrei Iancu * [134ad0d] : Fixed bogus pkg_free on pointer in data segment Reported by @dzhou121 Closes #854 (cherry picked from commit b9aa87360eecf1e77baea661b855eec4f5798f2f) 2016-04-20 Bogdan-Andrei Iancu * [afc4c40] : Fix typo in do_accounting() naming is cfg templates (cherry picked from commit 7f8656e026435bf4ac6eff19b964b81d7a37d28e) 2016-04-18 ionutrazvanionita * [b65c6c2] : [sipcapture][HOMERv5 incompatibility fix] updated docs for report_capture and new table name format (cherry picked from commit 5d725a42d0262807dd9a2c555640a980e50381ce) 2016-04-18 ionutrazvanionita * [acd0a9c] : [sipcapture][HOMERv5 incompatibility fix]generic capturing function Added new function called report_capture which allows capturing hep packets in a generic format. It will be very effective for the third version of the hep protocol for capturing packets other than SIP. Also added a new table called rtcp_capture for storing everything capture with report capture. (cherry picked from commit c5700cce4a37e484869941502e495733b8138b98) 2016-04-18 ionutrazvanionita * [3053c7e] : [sipcapture][HOMERv5 incompatibility fix] HOMERv5 table rotation aware logic * HOMVERv5 allows rotating tables by having a prefix which is the fixed table name and a suffix which is a string containig timestamps * sip_capture now accepts table name as a parameter to sip_capture() function * both global table name and sip_capture table_name can have a prefix(string) and a suffix(strftime format string); * fixed bugs in async query building (cherry picked from commit 074463f17f2e57fb15c120f306ce895438ad1865) 2016-04-14 ionutrazvanionita * [e894f7a] : [acc][bugfix] register tm free function only once (cherry picked from commit 92d20416568c23f58f378fd14b272d2b16d7518e) 2016-04-14 Liviu Chircu * [19dca29] : Fix a PKG leak in the SIP message processing stack Commit 7464a86ec introduced a processing context leak during the handling of basically any SIP request in all protocol stacks except for HEP. Reported by Trevor Francis (cherry picked from commit 731719a2b10283439e2f6a0ba15bb8c6c6ef189c) 2016-04-14 ionutrazvanionita * [c5c4df0] : [acc] fix double free issue Since callbacks were registered only at first, and aftewards only flags were changed there was no way to know if tm is to free the shmed flags from acc or dialog. For this a new flag was introduced to indicate whether the flags shall be removed either by dialog or tm. (cherry picked from commit 6852bd427376cea0cc53cbc5f87476f3377cbd04) 2016-04-12 Liviu Chircu * [9888984] : Suppress "no free TCP receiver" logging noise This was added long before async TCP support, and it is now only useful to developers. (cherry picked from commit 142e7e0121040aceb2f9c31c4d4a0e8d8b573cc6) 2016-04-11 Ovidiu Sas * [7d371ab] : pi_http: return "200 OK" instead of "0 Unknown" (cherry picked from commit 73f8117f2f5f01560b6698ad845cce0b485df203) 2016-04-07 Kristian Høgh * [423f277] : cachedb_redis: Prevent crash when re-connect keeps failing (cherry picked from commit b501df74a231a987babe6eeadb3c676d8823619c) 2016-04-05 rvlad-patrascu * [032bb69] : Fix missing GPL headers for mem files (cherry picked from commit 8ffd592e316a4436e2144dfc08f1b20571e6622e) 2016-04-05 Bogdan-Andrei Iancu * [be22446] : Fix typo in README example (cherry picked from commit 983ad955e21f57878fcefd61f8a734e89de5eab9) 2016-04-05 ionutrazvanionita * [836d3b5] : [drouting] docs updated for partition id variable (cherry picked from commit 2013276a1b1e4a29766f22b5f483eb4ace6a7235) 2016-04-05 ionutrazvanionita * [5689d43] : [drouting] check if partition pvar is writable (cherry picked from commit cf0945c16eff03efd5ec2b120d283ebc7f4b8616) 2016-04-05 ionutrazvanionita * [7745b11] : [drouting] pvar for matched partition if wildcard operator used fix missing functionality - return to script the matching partition (cherry picked from commit 7551c668972e63970c9a65f7dc5bbd1959100d79) 2016-04-05 Bogdan-Andrei Iancu * [4be2662] : Fixed checking return code for pre raw callbacks If the TH pre raw callbacks fails, it may return a NULL buffer, leading to a later crash when we want to parse the SIP message. 2016-04-04 Jarrod Baumann * [cf1e11d] : [dialog] repl_prof_add never increments the return buffer size for threshold comparison (cherry picked from commit 52028bdd2c729904c7e337d7aa0b99cce4195f0c) 2016-04-01 Bogdan-Andrei Iancu * [bcf7d40] : Fix missing logs if debug_mode after log_stderror (cherry picked from commit 29182c9baa98a8e156a1c0dd4ceb4992005c7950) 2016-03-31 Liviu Chircu * [2c84108] : Various doc improvements (cherry picked from commit d9326522ae9c3dc39b8163d07495dc013217ab80) Conflicts: scripts/opensipsdbctl scripts/opensipsdbctl.base 2016-03-31 Liviu Chircu * [7ac6bfc] : Update opensipsdbctl for 2.2 2016-03-31 Razvan Crainea * [11877a6] : Update ChangeLog 2016-03-31 Liviu Chircu * [c4b48fb] : Improve the AUTHORS file (cherry picked from commit 32d0e1a4fa500e7693828b3f98392d0502b8b0da) 2016-05-09 ionutrazvanionita * [3c75721] : [nathelper] fix bad logic regarding the timer list (cherry picked from commit a86d2c4edcf9c62778a2a9d2438d57fac869bc10) 2016-05-09 ionutrazvanionita * [2330afa] : [sipcapture] fix sip_capture table schema (cherry picked from commit 8fd97163058c798f45d381b124cc465261e9e1e5) 2016-05-09 ionutrazvanionita * [22bdc37] : [siptrace][bugfix] bad structre used when printing HEP trace_id via mi (cherry picked from commit 8c6d9bacb580960f1a0a642a5be5a5ffeb5f958a) 2016-05-08 Liviu Chircu * [cd4101a] : event_virtual doc: Fix grammar/typos (cherry picked from commit 3fd96863cb611d1eefad3b175535944545253f19) 2016-05-08 Liviu Chircu * [664ec6b] : event_flatstore doc: Fix some typos (cherry picked from commit 3cedcc4af994aa8ce132547c325c13cbc8d2cd07) 2016-05-06 ionutrazvanionita * [f5753f9] : [siptrace] fix hash calculus for extended hep trace_id (cherry picked from commit 5e261d7e9f1ea632806370c16cb0f733cee4edfc) 2016-05-06 ionutrazvanionita * [91c3247] : [sipcapture] allow any hep version for hep_net variable (cherry picked from commit bcbded5653d5703b53ed4bc961a8a66d724d3e32) 2016-05-06 ionutrazvanionita * [d09775b] : [sipcapture] fix new hep proto names string (cherry picked from commit 3c15d2d89ec1ab8ad01a2e29a844eb3084128abf) 2016-05-06 ionutrazvanionita * [c939605] : [sipcapture][bugfix] fix bad string hex value parsing (cherry picked from commit 5d6b927d41988e3170ab37889fb26c8026dce29d) 2016-05-05 ionutrazvanionita * [454a557] : [siptrace][bugfix] fix trace_id parsing (cherry picked from commit 2878741afc71620c5910f689c07db38b5003a48c) 2016-05-05 ionutrazvanionita * [ed3f224] : [sipcapture][bugfix] invalid static vector size calculation (cherry picked from commit b8a82b159d220389513808c8fb8f651fc1498cda) 2016-05-05 Liviu Chircu * [5589ffe] : dialog: Remove a chunk of dead code (cherry picked from commit fd330577d66de599430694334e8605ae8b46b9d9) 2016-05-05 ionutrazvanionita * [55f45c4] : [sipcapture][bugfix] invalid memory access (cherry picked from commit 8056a984ce914d6585aef6054d727d82f72da3c6) 2016-05-04 renicok * [f12cf9b] : Updated opensipsctl.base to also search /sbin The md5 utility is located at /sbin/md5 on FreeBSD 8.3. An additional common location was added to allow the script to accommodate this. (cherry picked from commit ca87f55ad071257aa3b891347aadb55a7729d25b) 2016-05-04 ionutrazvanionita * [ef5f3b5] : [sipcapture] fix bad conditions (cherry picked from commit 4caa2ac37177f2ad59c8f2de23fcc3b12c11fe69) 2016-05-04 ionutrazvanionita * [c24abca] : [sipcapture] be able to set proto_type in report capture function (cherry picked from commit e3466c4adecf11fa6d7da206359847c3a1c4b0f9) 2016-05-04 Razvan Crainea * [2d4d2ff] : ratelimit: initialize the length of the algorithm (cherry picked from commit 0eebf1058574c622ba3532fb520908da1c1d94a5) 2016-05-04 Razvan Crainea * [ebaad38] : Makefile.conf: add deps for tls_mgm and proto_wss This prevents 'make menuconf' from deleting these modules from the generated configuration file (cherry picked from commit 616b2ba189771a499c76d645627869270555511d) 2016-05-03 Răzvan Crainea * [83a23be] : debian: add missing dependency Ovewrite lintian warning 2016-05-03 Razvan Crainea * [1e99bb0] : dialog and usrloc: broadcast replication data all time This prevents clusterer module from marking a node as not reachable when there is no traffic at all. Fixes #849 (cherry picked from commit 7f7f8711ccfd0aabb71b124fbb7f4c3e8afd6e37) 2016-05-03 Razvan Crainea * [d02ae4f] : regex: fix spell-check in package (cherry picked from commit 647ea214f1892f8901f562bc45e0e1e33b699aba) 2016-05-03 ionutrazvanionita * [30d8048] : [acc][bugfix] fix memory leak * fix leak caused by missed calls - receiving a negative reply caused flags not being freed 2016-05-03 Razvan Crainea * [a1af71c] : proto_bin: remove debug INFO message (cherry picked from commit d388e804a320aa0f267e47cbcbaf473d519fab31) 2016-05-03 ionutrazvanionita * [4389c26] : [siptrace] fix bad cherry-pick conflict solving 2016-05-03 ionutrazvanionita * [090525c] : [sipcapture] updated docs for HEPVERSION 2016-05-03 ionutrazvanionita * [e547ec6] : [fix functional bug][sipcapture] new HEPVERSION script variable (cherry picked from commit 136fcb65edd4ca00cb15f088de3e58f960385518) 2016-05-03 ionutrazvanionita * [a9a9d7f] : [proto_hep] correctly name function (cherry picked from commit 853b0bbb759d3c58f00b0749e90e4016dd500891) 2016-05-03 ionutrazvanionita * [608881b] : [hep] updated docs for previous commit (cherry picked from commit f9c6cdd2acf93062c252f9fc64faa6327e696718) 2016-05-03 ionutrazvanionita * [b2ff3cc] : [fix functional bug][hep] multiple versions/protocols can be used in the same time Since commit 0873c14d1b81887823242a955217dcc854eb97c9 multiple network protocols can be used from one module. Proto_hep was changed to be able to use both tcp and udp in the same time. Moreover, there is no hep_version needed since we can receive HEPv1,v2 and v3 on any UDP interface. Since HEPv1 and v2 are not compatible with TCP, only HEPv3 will be support. For HEPv3, Sipcapture module can now relay packets from TCP to UDP or the reverse way. Siptrace module was also improved. Trace id's can now be defined with hep version and transport protocol, in order to be able to do multiple types of tracing with different HEP protocols and/or network transports. (cherry picked from commit 45a3aef8e151a03966a7739c2f880a3fdb0d6811) Conflicts: modules/siptrace/siptrace.c 2016-05-03 Razvan Crainea * [5e8f0ac] : [fix functional bug]allow a module to register multiple protocols (cherry picked from commit 0873c14d1b81887823242a955217dcc854eb97c9) 2016-04-29 ionutrazvanionita * [e82a5b3] : [drouting] bug fix and memory leak fix * free gateways list for each carrier at reload * use map_find instead of map_get to search an element in the tree (cherry picked from commit f9f3b83ef621850c2c128b12b9bf6ab2e78e5ee1) 2016-04-28 ionutrazvanionita * [19421eb] : [siptrace][bugfix] use the correct sockaddr structure for storing (cherry picked from commit 556f394212bb07c6f7641f3b1df954d52ff730a5) 2016-04-28 ionutrazvanionita * [ba0fcc2] : [proto_hep][bugfix] use the correct port when unpacking (cherry picked from commit 3dda4131b458bc6f3145196f7661671571dc948e) 2016-04-28 ionutrazvanionita * [c2e8c17] : Revert "[hep][bugfix] correctly store ports/addresses" This reverts commit 7b1359adfcd55f12fd3739c04e30e1c12b91a71b. (cherry picked from commit ab2292945b19f00faa063c457a0efce384b8f245) 2016-04-28 ionutrazvanionita * [4cce28a] : [hep][bugfix] correctly store ports/addresses (cherry picked from commit 51e6f102daac7d0de9a7ca4efea74d5c559746fe) 2016-04-28 Bogdan-Andrei Iancu * [70a3018] : Enable debug_mode settings only if debug_mode gets set to enable. (cherry picked from commit e7c14bc0c85c888040aec7f0949b085c5cee38e7) 2016-04-27 Bogdan-Andrei Iancu * [5c8869b] : Avoid space in MI node name 2016-04-27 Bogdan-Andrei Iancu * [555c0e7] : Fixed wrong MI tree in clusterer_list (related to previous commit) (cherry picked from commit fa4e7f0fa10c6e987b1b69bc5302f0d12a3f8714) 2016-04-27 Bogdan-Andrei Iancu * [aa856a7] : Fixed arrays in clusterer_list MI command (cherry picked from commit 31943a4e2c4ae84babc181f4e5a31e33f2d6b63a) 2016-04-26 ionutrazvanionita * [0e81a70] : [tls_mgm] properly handle SSL_ERROR_SYSCALL on SSL_shutdown (cherry picked from commit bdcee39ebf04f8a74b926dc6e6c311487d3ef601) 2016-04-26 ionutrazvanionita * [dbdc46c] : [siptrace][bugfix] avoid double free on cancelled transaction (cherry picked from commit 3c6b6930d615adb978dd2779b6cd4054c3bc49df) 2016-04-25 ionutrazvanionita * [a9b0011] : [sipcapture] fix incompatibilities with HOMERv5 db schema (cherry picked from commit f75687847a142f18b44c49a0b274457d7039f165) 2016-04-25 ionutrazvanionita * [85b126a] : [sipcapture] remove time-consuming unnecessary chunk of code (cherry picked from commit cc7a2ea114444b82645e484a1cf728c4464c17dd) 2016-04-25 ionutrazvanionita * [d49afa6] : [proto_hep][bugfix] fix bad sip payload size calculation (cherry picked from commit eec5045d44ec1fc29aba9a6d5d024e38ace10ae1) 2016-04-25 ionutrazvanionita * [e6258da] : [acc][bugfix] correctly store do_accounting() flags Flags are now moved from processing context into transaction context when the transaction is created, this way having visibility for the whole transaction. (cherry picked from commit 41bcd2559b29a23e96636c6c2dfc5628f69ae5b7) 2016-04-25 Bogdan-Andrei Iancu * [507db77] : Fixed MI nodes in 'sip_trace' command. Formatting chars must not be included in the MI tree (cherry picked from commit 7811e2d6b6013fe21c744531299f490671462a05) 2016-04-24 Bogdan-Andrei Iancu * [0a1fc05] : Fixed default FIFO file name (cherry picked from commit ffdbb970bfa60dcd1ed7d88fc94c4a07f9356ad9) 2016-04-24 Bogdan-Andrei Iancu * [b7ac4fc] : exclude proto_wss, use QM_MALLOC 2016-04-24 Bogdan-Andrei Iancu * [7aef53c] : tls_list() may be called even without DB support Avoid locking if DB support was not enabled. (cherry picked from commit e6944bb064750611585d0fa516551b944aa44cdc) 2016-04-22 ionutrazvanionita * [eee4fae] : [proto_hep][bugfix] check memory allocation (cherry picked from commit f5ad753d56a95d68bc937e0721895d6ee4aefd43) 2016-04-21 ionutrazvanionita * [21ca280] : [siptrace][bugfix] sequential requests - set context for sl callbacks (cherry picked from commit 943d9015f8f1e3467acafee67693f157beab9755) 2016-04-21 ionutrazvanionita * [6ce202a] : [context][bugfix] each context has its own destroy functions vector Credits to Razvan Crainea (cherry picked from commit 53263b2a65dc771cb660078f4b20171a48f04248) 2016-04-21 ionutrazvanionita * [576a730] : [tm]fix context functions header (cherry picked from commit 59e5a4165f88c2a4a4dbb5e159786c441f6fa502) 2016-04-20 Liviu Chircu * [4cd99ec] : MI library: Add comments regarding a known issue Currently, MI arrays cannot contain objects with non-identical "name" attributes. This patch also quickfixes this issue in mi_list_all_profiles() by dropping the array idea altogether, in favour of a top-most object. (cherry picked from commit 6c105015c3a0a02c6f65822f5ce52e12bd0ae108) 2016-04-20 Bogdan-Andrei Iancu * [134ad0d] : Fixed bogus pkg_free on pointer in data segment Reported by @dzhou121 Closes #854 (cherry picked from commit b9aa87360eecf1e77baea661b855eec4f5798f2f) 2016-04-20 Bogdan-Andrei Iancu * [afc4c40] : Fix typo in do_accounting() naming is cfg templates (cherry picked from commit 7f8656e026435bf4ac6eff19b964b81d7a37d28e) 2016-04-18 ionutrazvanionita * [b65c6c2] : [sipcapture][HOMERv5 incompatibility fix] updated docs for report_capture and new table name format (cherry picked from commit 5d725a42d0262807dd9a2c555640a980e50381ce) 2016-04-18 ionutrazvanionita * [acd0a9c] : [sipcapture][HOMERv5 incompatibility fix]generic capturing function Added new function called report_capture which allows capturing hep packets in a generic format. It will be very effective for the third version of the hep protocol for capturing packets other than SIP. Also added a new table called rtcp_capture for storing everything capture with report capture. (cherry picked from commit c5700cce4a37e484869941502e495733b8138b98) 2016-04-18 ionutrazvanionita * [3053c7e] : [sipcapture][HOMERv5 incompatibility fix] HOMERv5 table rotation aware logic * HOMVERv5 allows rotating tables by having a prefix which is the fixed table name and a suffix which is a string containig timestamps * sip_capture now accepts table name as a parameter to sip_capture() function * both global table name and sip_capture table_name can have a prefix(string) and a suffix(strftime format string); * fixed bugs in async query building (cherry picked from commit 074463f17f2e57fb15c120f306ce895438ad1865) 2016-04-14 ionutrazvanionita * [e894f7a] : [acc][bugfix] register tm free function only once (cherry picked from commit 92d20416568c23f58f378fd14b272d2b16d7518e) 2016-04-14 Liviu Chircu * [19dca29] : Fix a PKG leak in the SIP message processing stack Commit 7464a86ec introduced a processing context leak during the handling of basically any SIP request in all protocol stacks except for HEP. Reported by Trevor Francis (cherry picked from commit 731719a2b10283439e2f6a0ba15bb8c6c6ef189c) 2016-04-14 ionutrazvanionita * [c5c4df0] : [acc] fix double free issue Since callbacks were registered only at first, and aftewards only flags were changed there was no way to know if tm is to free the shmed flags from acc or dialog. For this a new flag was introduced to indicate whether the flags shall be removed either by dialog or tm. (cherry picked from commit 6852bd427376cea0cc53cbc5f87476f3377cbd04) 2016-04-12 Liviu Chircu * [9888984] : Suppress "no free TCP receiver" logging noise This was added long before async TCP support, and it is now only useful to developers. (cherry picked from commit 142e7e0121040aceb2f9c31c4d4a0e8d8b573cc6) 2016-04-11 Ovidiu Sas * [7d371ab] : pi_http: return "200 OK" instead of "0 Unknown" (cherry picked from commit 73f8117f2f5f01560b6698ad845cce0b485df203) 2016-04-07 Kristian Høgh * [423f277] : cachedb_redis: Prevent crash when re-connect keeps failing (cherry picked from commit b501df74a231a987babe6eeadb3c676d8823619c) 2016-04-05 rvlad-patrascu * [032bb69] : Fix missing GPL headers for mem files (cherry picked from commit 8ffd592e316a4436e2144dfc08f1b20571e6622e) 2016-04-05 Bogdan-Andrei Iancu * [be22446] : Fix typo in README example (cherry picked from commit 983ad955e21f57878fcefd61f8a734e89de5eab9) 2016-04-05 ionutrazvanionita * [836d3b5] : [drouting] docs updated for partition id variable (cherry picked from commit 2013276a1b1e4a29766f22b5f483eb4ace6a7235) 2016-04-05 ionutrazvanionita * [5689d43] : [drouting] check if partition pvar is writable (cherry picked from commit cf0945c16eff03efd5ec2b120d283ebc7f4b8616) 2016-04-05 ionutrazvanionita * [7745b11] : [drouting] pvar for matched partition if wildcard operator used fix missing functionality - return to script the matching partition (cherry picked from commit 7551c668972e63970c9a65f7dc5bbd1959100d79) 2016-04-05 Bogdan-Andrei Iancu * [4be2662] : Fixed checking return code for pre raw callbacks If the TH pre raw callbacks fails, it may return a NULL buffer, leading to a later crash when we want to parse the SIP message. 2016-04-04 Jarrod Baumann * [cf1e11d] : [dialog] repl_prof_add never increments the return buffer size for threshold comparison (cherry picked from commit 52028bdd2c729904c7e337d7aa0b99cce4195f0c) 2016-04-01 Bogdan-Andrei Iancu * [bcf7d40] : Fix missing logs if debug_mode after log_stderror (cherry picked from commit 29182c9baa98a8e156a1c0dd4ceb4992005c7950) 2016-03-31 Liviu Chircu * [2c84108] : Various doc improvements (cherry picked from commit d9326522ae9c3dc39b8163d07495dc013217ab80) Conflicts: scripts/opensipsdbctl scripts/opensipsdbctl.base 2016-03-31 Liviu Chircu * [7ac6bfc] : Update opensipsdbctl for 2.2 2016-03-31 Razvan Crainea * [11877a6] : Update ChangeLog 2016-03-31 Liviu Chircu * [c4b48fb] : Improve the AUTHORS file (cherry picked from commit 32d0e1a4fa500e7693828b3f98392d0502b8b0da) 2016-05-09 Razvan Crainea * [7e7a2df] : Update name to opensips-2.2.0-rc1 2016-05-09 ionutrazvanionita * [3c75721] : [nathelper] fix bad logic regarding the timer list (cherry picked from commit a86d2c4edcf9c62778a2a9d2438d57fac869bc10) 2016-05-09 ionutrazvanionita * [2330afa] : [sipcapture] fix sip_capture table schema (cherry picked from commit 8fd97163058c798f45d381b124cc465261e9e1e5) 2016-05-09 ionutrazvanionita * [22bdc37] : [siptrace][bugfix] bad structre used when printing HEP trace_id via mi (cherry picked from commit 8c6d9bacb580960f1a0a642a5be5a5ffeb5f958a) 2016-05-08 Liviu Chircu * [cd4101a] : event_virtual doc: Fix grammar/typos (cherry picked from commit 3fd96863cb611d1eefad3b175535944545253f19) 2016-05-08 Liviu Chircu * [664ec6b] : event_flatstore doc: Fix some typos (cherry picked from commit 3cedcc4af994aa8ce132547c325c13cbc8d2cd07) 2016-05-06 ionutrazvanionita * [f5753f9] : [siptrace] fix hash calculus for extended hep trace_id (cherry picked from commit 5e261d7e9f1ea632806370c16cb0f733cee4edfc) 2016-05-06 ionutrazvanionita * [91c3247] : [sipcapture] allow any hep version for hep_net variable (cherry picked from commit bcbded5653d5703b53ed4bc961a8a66d724d3e32) 2016-05-06 ionutrazvanionita * [d09775b] : [sipcapture] fix new hep proto names string (cherry picked from commit 3c15d2d89ec1ab8ad01a2e29a844eb3084128abf) 2016-05-06 ionutrazvanionita * [c939605] : [sipcapture][bugfix] fix bad string hex value parsing (cherry picked from commit 5d6b927d41988e3170ab37889fb26c8026dce29d) 2016-05-05 ionutrazvanionita * [454a557] : [siptrace][bugfix] fix trace_id parsing (cherry picked from commit 2878741afc71620c5910f689c07db38b5003a48c) 2016-05-05 ionutrazvanionita * [ed3f224] : [sipcapture][bugfix] invalid static vector size calculation (cherry picked from commit b8a82b159d220389513808c8fb8f651fc1498cda) 2016-05-05 Liviu Chircu * [5589ffe] : dialog: Remove a chunk of dead code (cherry picked from commit fd330577d66de599430694334e8605ae8b46b9d9) 2016-05-05 ionutrazvanionita * [55f45c4] : [sipcapture][bugfix] invalid memory access (cherry picked from commit 8056a984ce914d6585aef6054d727d82f72da3c6) 2016-05-04 renicok * [f12cf9b] : Updated opensipsctl.base to also search /sbin The md5 utility is located at /sbin/md5 on FreeBSD 8.3. An additional common location was added to allow the script to accommodate this. (cherry picked from commit ca87f55ad071257aa3b891347aadb55a7729d25b) 2016-05-04 ionutrazvanionita * [ef5f3b5] : [sipcapture] fix bad conditions (cherry picked from commit 4caa2ac37177f2ad59c8f2de23fcc3b12c11fe69) 2016-05-04 ionutrazvanionita * [c24abca] : [sipcapture] be able to set proto_type in report capture function (cherry picked from commit e3466c4adecf11fa6d7da206359847c3a1c4b0f9) 2016-05-04 Razvan Crainea * [2d4d2ff] : ratelimit: initialize the length of the algorithm (cherry picked from commit 0eebf1058574c622ba3532fb520908da1c1d94a5) 2016-05-04 Razvan Crainea * [ebaad38] : Makefile.conf: add deps for tls_mgm and proto_wss This prevents 'make menuconf' from deleting these modules from the generated configuration file (cherry picked from commit 616b2ba189771a499c76d645627869270555511d) 2016-05-03 Răzvan Crainea * [83a23be] : debian: add missing dependency Ovewrite lintian warning 2016-05-03 Razvan Crainea * [1e99bb0] : dialog and usrloc: broadcast replication data all time This prevents clusterer module from marking a node as not reachable when there is no traffic at all. Fixes #849 (cherry picked from commit 7f7f8711ccfd0aabb71b124fbb7f4c3e8afd6e37) 2016-05-03 Razvan Crainea * [d02ae4f] : regex: fix spell-check in package (cherry picked from commit 647ea214f1892f8901f562bc45e0e1e33b699aba) 2016-05-03 ionutrazvanionita * [30d8048] : [acc][bugfix] fix memory leak * fix leak caused by missed calls - receiving a negative reply caused flags not being freed 2016-05-03 Razvan Crainea * [a1af71c] : proto_bin: remove debug INFO message (cherry picked from commit d388e804a320aa0f267e47cbcbaf473d519fab31) 2016-05-03 ionutrazvanionita * [4389c26] : [siptrace] fix bad cherry-pick conflict solving 2016-05-03 ionutrazvanionita * [090525c] : [sipcapture] updated docs for HEPVERSION 2016-05-03 ionutrazvanionita * [e547ec6] : [fix functional bug][sipcapture] new HEPVERSION script variable (cherry picked from commit 136fcb65edd4ca00cb15f088de3e58f960385518) 2016-05-03 ionutrazvanionita * [a9a9d7f] : [proto_hep] correctly name function (cherry picked from commit 853b0bbb759d3c58f00b0749e90e4016dd500891) 2016-05-03 ionutrazvanionita * [608881b] : [hep] updated docs for previous commit (cherry picked from commit f9c6cdd2acf93062c252f9fc64faa6327e696718) 2016-05-03 ionutrazvanionita * [b2ff3cc] : [fix functional bug][hep] multiple versions/protocols can be used in the same time Since commit 0873c14d1b81887823242a955217dcc854eb97c9 multiple network protocols can be used from one module. Proto_hep was changed to be able to use both tcp and udp in the same time. Moreover, there is no hep_version needed since we can receive HEPv1,v2 and v3 on any UDP interface. Since HEPv1 and v2 are not compatible with TCP, only HEPv3 will be support. For HEPv3, Sipcapture module can now relay packets from TCP to UDP or the reverse way. Siptrace module was also improved. Trace id's can now be defined with hep version and transport protocol, in order to be able to do multiple types of tracing with different HEP protocols and/or network transports. (cherry picked from commit 45a3aef8e151a03966a7739c2f880a3fdb0d6811) Conflicts: modules/siptrace/siptrace.c 2016-05-03 Razvan Crainea * [5e8f0ac] : [fix functional bug]allow a module to register multiple protocols (cherry picked from commit 0873c14d1b81887823242a955217dcc854eb97c9) 2016-04-29 ionutrazvanionita * [e82a5b3] : [drouting] bug fix and memory leak fix * free gateways list for each carrier at reload * use map_find instead of map_get to search an element in the tree (cherry picked from commit f9f3b83ef621850c2c128b12b9bf6ab2e78e5ee1) 2016-04-28 ionutrazvanionita * [19421eb] : [siptrace][bugfix] use the correct sockaddr structure for storing (cherry picked from commit 556f394212bb07c6f7641f3b1df954d52ff730a5) 2016-04-28 ionutrazvanionita * [ba0fcc2] : [proto_hep][bugfix] use the correct port when unpacking (cherry picked from commit 3dda4131b458bc6f3145196f7661671571dc948e) 2016-04-28 ionutrazvanionita * [c2e8c17] : Revert "[hep][bugfix] correctly store ports/addresses" This reverts commit 7b1359adfcd55f12fd3739c04e30e1c12b91a71b. (cherry picked from commit ab2292945b19f00faa063c457a0efce384b8f245) 2016-04-28 ionutrazvanionita * [4cce28a] : [hep][bugfix] correctly store ports/addresses (cherry picked from commit 51e6f102daac7d0de9a7ca4efea74d5c559746fe) 2016-04-28 Bogdan-Andrei Iancu * [70a3018] : Enable debug_mode settings only if debug_mode gets set to enable. (cherry picked from commit e7c14bc0c85c888040aec7f0949b085c5cee38e7) 2016-04-27 Bogdan-Andrei Iancu * [5c8869b] : Avoid space in MI node name 2016-04-27 Bogdan-Andrei Iancu * [555c0e7] : Fixed wrong MI tree in clusterer_list (related to previous commit) (cherry picked from commit fa4e7f0fa10c6e987b1b69bc5302f0d12a3f8714) 2016-04-27 Bogdan-Andrei Iancu * [aa856a7] : Fixed arrays in clusterer_list MI command (cherry picked from commit 31943a4e2c4ae84babc181f4e5a31e33f2d6b63a) 2016-04-26 ionutrazvanionita * [0e81a70] : [tls_mgm] properly handle SSL_ERROR_SYSCALL on SSL_shutdown (cherry picked from commit bdcee39ebf04f8a74b926dc6e6c311487d3ef601) 2016-04-26 ionutrazvanionita * [dbdc46c] : [siptrace][bugfix] avoid double free on cancelled transaction (cherry picked from commit 3c6b6930d615adb978dd2779b6cd4054c3bc49df) 2016-04-25 ionutrazvanionita * [a9b0011] : [sipcapture] fix incompatibilities with HOMERv5 db schema (cherry picked from commit f75687847a142f18b44c49a0b274457d7039f165) 2016-04-25 ionutrazvanionita * [85b126a] : [sipcapture] remove time-consuming unnecessary chunk of code (cherry picked from commit cc7a2ea114444b82645e484a1cf728c4464c17dd) 2016-04-25 ionutrazvanionita * [d49afa6] : [proto_hep][bugfix] fix bad sip payload size calculation (cherry picked from commit eec5045d44ec1fc29aba9a6d5d024e38ace10ae1) 2016-04-25 ionutrazvanionita * [e6258da] : [acc][bugfix] correctly store do_accounting() flags Flags are now moved from processing context into transaction context when the transaction is created, this way having visibility for the whole transaction. (cherry picked from commit 41bcd2559b29a23e96636c6c2dfc5628f69ae5b7) 2016-04-25 Bogdan-Andrei Iancu * [507db77] : Fixed MI nodes in 'sip_trace' command. Formatting chars must not be included in the MI tree (cherry picked from commit 7811e2d6b6013fe21c744531299f490671462a05) 2016-04-24 Bogdan-Andrei Iancu * [0a1fc05] : Fixed default FIFO file name (cherry picked from commit ffdbb970bfa60dcd1ed7d88fc94c4a07f9356ad9) 2016-04-24 Bogdan-Andrei Iancu * [b7ac4fc] : exclude proto_wss, use QM_MALLOC 2016-04-24 Bogdan-Andrei Iancu * [7aef53c] : tls_list() may be called even without DB support Avoid locking if DB support was not enabled. (cherry picked from commit e6944bb064750611585d0fa516551b944aa44cdc) 2016-04-22 ionutrazvanionita * [eee4fae] : [proto_hep][bugfix] check memory allocation (cherry picked from commit f5ad753d56a95d68bc937e0721895d6ee4aefd43) 2016-04-21 ionutrazvanionita * [21ca280] : [siptrace][bugfix] sequential requests - set context for sl callbacks (cherry picked from commit 943d9015f8f1e3467acafee67693f157beab9755) 2016-04-21 ionutrazvanionita * [6ce202a] : [context][bugfix] each context has its own destroy functions vector Credits to Razvan Crainea (cherry picked from commit 53263b2a65dc771cb660078f4b20171a48f04248) 2016-04-21 ionutrazvanionita * [576a730] : [tm]fix context functions header (cherry picked from commit 59e5a4165f88c2a4a4dbb5e159786c441f6fa502) 2016-04-20 Liviu Chircu * [4cd99ec] : MI library: Add comments regarding a known issue Currently, MI arrays cannot contain objects with non-identical "name" attributes. This patch also quickfixes this issue in mi_list_all_profiles() by dropping the array idea altogether, in favour of a top-most object. (cherry picked from commit 6c105015c3a0a02c6f65822f5ce52e12bd0ae108) 2016-04-20 Bogdan-Andrei Iancu * [134ad0d] : Fixed bogus pkg_free on pointer in data segment Reported by @dzhou121 Closes #854 (cherry picked from commit b9aa87360eecf1e77baea661b855eec4f5798f2f) 2016-04-20 Bogdan-Andrei Iancu * [afc4c40] : Fix typo in do_accounting() naming is cfg templates (cherry picked from commit 7f8656e026435bf4ac6eff19b964b81d7a37d28e) 2016-04-18 ionutrazvanionita * [b65c6c2] : [sipcapture][HOMERv5 incompatibility fix] updated docs for report_capture and new table name format (cherry picked from commit 5d725a42d0262807dd9a2c555640a980e50381ce) 2016-04-18 ionutrazvanionita * [acd0a9c] : [sipcapture][HOMERv5 incompatibility fix]generic capturing function Added new function called report_capture which allows capturing hep packets in a generic format. It will be very effective for the third version of the hep protocol for capturing packets other than SIP. Also added a new table called rtcp_capture for storing everything capture with report capture. (cherry picked from commit c5700cce4a37e484869941502e495733b8138b98) 2016-04-18 ionutrazvanionita * [3053c7e] : [sipcapture][HOMERv5 incompatibility fix] HOMERv5 table rotation aware logic * HOMVERv5 allows rotating tables by having a prefix which is the fixed table name and a suffix which is a string containig timestamps * sip_capture now accepts table name as a parameter to sip_capture() function * both global table name and sip_capture table_name can have a prefix(string) and a suffix(strftime format string); * fixed bugs in async query building (cherry picked from commit 074463f17f2e57fb15c120f306ce895438ad1865) 2016-04-14 ionutrazvanionita * [e894f7a] : [acc][bugfix] register tm free function only once (cherry picked from commit 92d20416568c23f58f378fd14b272d2b16d7518e) 2016-04-14 Liviu Chircu * [19dca29] : Fix a PKG leak in the SIP message processing stack Commit 7464a86ec introduced a processing context leak during the handling of basically any SIP request in all protocol stacks except for HEP. Reported by Trevor Francis (cherry picked from commit 731719a2b10283439e2f6a0ba15bb8c6c6ef189c) 2016-04-14 ionutrazvanionita * [c5c4df0] : [acc] fix double free issue Since callbacks were registered only at first, and aftewards only flags were changed there was no way to know if tm is to free the shmed flags from acc or dialog. For this a new flag was introduced to indicate whether the flags shall be removed either by dialog or tm. (cherry picked from commit 6852bd427376cea0cc53cbc5f87476f3377cbd04) 2016-04-12 Liviu Chircu * [9888984] : Suppress "no free TCP receiver" logging noise This was added long before async TCP support, and it is now only useful to developers. (cherry picked from commit 142e7e0121040aceb2f9c31c4d4a0e8d8b573cc6) 2016-04-11 Ovidiu Sas * [7d371ab] : pi_http: return "200 OK" instead of "0 Unknown" (cherry picked from commit 73f8117f2f5f01560b6698ad845cce0b485df203) 2016-04-07 Kristian Høgh * [423f277] : cachedb_redis: Prevent crash when re-connect keeps failing (cherry picked from commit b501df74a231a987babe6eeadb3c676d8823619c) 2016-04-05 rvlad-patrascu * [032bb69] : Fix missing GPL headers for mem files (cherry picked from commit 8ffd592e316a4436e2144dfc08f1b20571e6622e) 2016-04-05 Bogdan-Andrei Iancu * [be22446] : Fix typo in README example (cherry picked from commit 983ad955e21f57878fcefd61f8a734e89de5eab9) 2016-04-05 ionutrazvanionita * [836d3b5] : [drouting] docs updated for partition id variable (cherry picked from commit 2013276a1b1e4a29766f22b5f483eb4ace6a7235) 2016-04-05 ionutrazvanionita * [5689d43] : [drouting] check if partition pvar is writable (cherry picked from commit cf0945c16eff03efd5ec2b120d283ebc7f4b8616) 2016-04-05 ionutrazvanionita * [7745b11] : [drouting] pvar for matched partition if wildcard operator used fix missing functionality - return to script the matching partition (cherry picked from commit 7551c668972e63970c9a65f7dc5bbd1959100d79) 2016-04-05 Bogdan-Andrei Iancu * [4be2662] : Fixed checking return code for pre raw callbacks If the TH pre raw callbacks fails, it may return a NULL buffer, leading to a later crash when we want to parse the SIP message. 2016-04-04 Jarrod Baumann * [cf1e11d] : [dialog] repl_prof_add never increments the return buffer size for threshold comparison (cherry picked from commit 52028bdd2c729904c7e337d7aa0b99cce4195f0c) 2016-04-01 Bogdan-Andrei Iancu * [bcf7d40] : Fix missing logs if debug_mode after log_stderror (cherry picked from commit 29182c9baa98a8e156a1c0dd4ceb4992005c7950) 2016-03-31 Liviu Chircu * [2c84108] : Various doc improvements (cherry picked from commit d9326522ae9c3dc39b8163d07495dc013217ab80) Conflicts: scripts/opensipsdbctl scripts/opensipsdbctl.base 2016-03-31 Liviu Chircu * [7ac6bfc] : Update opensipsdbctl for 2.2 2016-03-31 Razvan Crainea * [11877a6] : Update ChangeLog 2016-03-31 Liviu Chircu * [c4b48fb] : Improve the AUTHORS file (cherry picked from commit 32d0e1a4fa500e7693828b3f98392d0502b8b0da) 2016-03-31 Liviu Chircu * [c4b48fb] : Improve the AUTHORS file (cherry picked from commit 32d0e1a4fa500e7693828b3f98392d0502b8b0da) 2016-03-31 Razvan Crainea * [133c17b] : Update version to beta 2016-03-31 Razvan Crainea * [a91fce6] : For beta release, enable malloc debugging 2016-03-31 Razvan Crainea * [092fb36] : Makefile: always put the minor in name Even if it is redundant 2016-03-31 Razvan Crainea * [cf676f2] : Update CREDITS and AUTHORS 2016-03-31 Răzvan Crainea * [5a8dc76] : Merge pull request #831 from james-criscuolo/master presence module: move strlen check to avoid pkg_malloc(0) 2016-03-31 Bogdan-Andrei Iancu * [6018192] : Fix extended Call-ID in B2B The old CallID format did not ensure unicity across time, so we include now time info into the callid. Closes #783 . 2016-03-31 Bogdan-Andrei Iancu * [6459085] : README files regenerated 2016-03-31 Bogdan-Andrei Iancu * [0500b6e] : Fix bogus log (cherry picked from commit 87c93b8c91815107eacb7069653689d6e0f38d19) 2016-03-31 Bogdan-Andrei Iancu * [d1e9743] : Revert commit d6cbbff Do not automatically set db_url to the db_default_url as we have no idea if the default partition is gone be used. By forcing it we create some extra unwanted dependencies (like setting the version for the defaul table, even if not used). Even if we break the backward compatibility, makes more sens to explicitly set the db_url if you use the default partition 2016-03-31 Bogdan-Andrei Iancu * [086305e] : Revert commit 1e1ea14 Do not automatically set db_url to the db_default_url as we have no idea if the default partition is gone be used. By forcing it we create some extra unwanted dependencies (like setting the version for the defaul table, even if not used). Even if we break the backward compatibility, makes more sens to explicitly set the db_url if you use the default partition 2016-03-31 Bogdan-Andrei Iancu * [107a04c] : Fixed ilegal chars in XML 2016-03-31 Bogdan-Andrei Iancu * [eca4434] : Migrate cfg scripts to new acc interface. 2016-03-30 Bogdan-Andrei Iancu * [d5e2e28] : MYSQL DB migration updated. SIP TRACE table changed, CALL CENTER tables added. 2016-03-30 Razvan Crainea * [39cc2df] : Update ws & tls modules documentation 2016-03-30 Razvan Crainea * [3ca7813] : python: fix documentation links 2016-03-30 Bogdan-Andrei Iancu * [05930d1] : Fixed bogus free of soring algorithms in DR. 2016-03-30 Liviu Chircu * [8d04721] : TLS: Fix misleading error logs on accept() failures OpenSIPS would always log SSL_accept() failures as being "rejected by client". This is entirely not the case, as the errors may as very well be server-side, due to cipher mismatches, failed peer verifications, etc. Reported by Nabeel Shikder on the "users" mailing list 2016-03-29 Razvan Crainea * [12df046] : clusterer: update html README layout 2016-03-29 Razvan Crainea * [984ddb2] : clusterer: update README with a usage example 2016-03-29 ionutrazvanionita * [cf62502] : [sipcapture] update docs for new function 2016-03-29 ionutrazvanionita * [7d30be0] : [sipcapture hep] function to continue with the script after the hep route 2016-03-29 Liviu Chircu * [2380bbd] : drouting api: Fix a double free bug Crashes / memory corruptions could ensue after running "fraud_reload" MI commands or during OpenSIPS shutdown, if fraud_detection had some data to work with. 2016-03-28 Liviu Chircu * [2e061b9] : Merge pull request #842 from rrb3942/relaxed_s_int2 Make the s.int transformation more permissive 2016-03-28 Ryan Bullock * [6e71969] : Preserve sign on partially converted strings 2016-03-28 Ryan Bullock * [9b37e74] : Make the s.int transformation more permissive 2016-03-28 ionutrazvanionita * [cbf3a85] : [acc] fix bugs in acc * init all accounting methods data in mod_init as it was before; cdr accounting initial data is loaded even if users don't use CDRs but this shouldn't affect anything * fix aaa bug - api was not loaded because it was badly included in diameter define section * fix event interface extra parameters - parsing extra parameters for event interface was wrongly removed 2016-03-28 Bogdan-Andrei Iancu * [ef6ba0e] : Fixed function name in export. 2016-03-28 Bogdan-Andrei Iancu * [4db733a] : Fix function name in docs 2016-03-25 ionutrazvanionita * [eed88e2] : [acc]fix event registration issue 2016-03-25 Liviu Chircu * [d148ca2] : sql_cacher: Add module dependencies It should be loaded somewhere after all SQL and NoSQL drivers 2016-03-25 Bogdan Andrei IANCU * [b65bbe2] : Merge pull request #837 from ionutrazvanionita/hep-route [sipcapture] special route for hep messages 2016-03-25 Bogdan Andrei IANCU * [ad361f5] : Merge pull request #841 from ionutrazvanionita/acc-do-acc drop_accounting function for acc 2016-03-25 ionutrazvanionita * [fe5e818] : [acc]fix docs for drop_accounting() 2016-03-25 ionutrazvanionita * [4802253] : [acc]change cancel_accounting() name to drop_accounting() 2016-03-25 ionutrazvanionita * [b452f8a] : [acc]updated docs for cancel_accounting() 2016-03-25 ionutrazvanionita * [0548384] : [acc]cancel_accounting function for acc This function allows unsetting parameters set by do accounting function. It has the same parameters excepting the table name. 2016-03-25 Liviu Chircu * [29aa72f] : clusterer: Various coding style improvements * clean loops * shorter boolean test operators * avoid redundant global initializations * improve variable naming * fix bad log messages 2016-03-25 Bogdan-Andrei Iancu * [aaaeea8] : Updated the ACC documentation 2016-03-25 Bogdan Andrei IANCU * [6992f2d] : Merge pull request #840 from ionutrazvanionita/acc-do-acc [acc]flags used for accounting now replaced by do_accounting() 2016-03-25 ionutrazvanionita * [31ef12b] : [sipcapture]fix dummy request building 2016-03-24 Liviu Chircu * [c2a0767] : F_MALLOC: Allow free(NULL) when debugging memory 2016-03-24 Liviu Chircu * [2787308] : Improve robustness of core URI copying functions Namely set_ruri(), set_dst_uri(), set_path_vector(). This patch corrects the behaviour of the above functions when they are fed strange/corrupt strings, such as {NULL, 5} or {0x7fb..., 0}. There are still plenty of pieces of OpenSIPS code which assume such strings should be correctly processed, e.g. next_branches(), which may lead to a whole world of problems ranging from segfaults, double free operations and dangling pointers all the way up to intermixed shm/pkg memory pointers. The best way of addressing this issue is to simply accept such input and zeroize (and free) the concerned sip_msg attributes. Many thanks to Ovidiu Sas for in-depth error reporting and assistance in fixing this issue 2016-03-24 Liviu Chircu * [ad27b4b] : Improve logging when using "memlog" * blocks are more readable now (pkg vs. shm) * malloc vs. free vs. realloc operations are more distiguishable 2016-03-24 ionutrazvanionita * [5700163] : [sipcapture] Allow module starting without db url if db not used 2016-03-24 ionutrazvanionita * [43f6d74] : [sipcapture] updated docs for hep_route parameter 2016-03-24 ionutrazvanionita * [02e2634] : [sipcapture] special route for hep messages hep_store_no_script parameter now changed into hep_route. Using this parameter one can either do sip_capture() without going through the script or define a route where messages can be forwarded using hep_relay() function and modified if hep version 3 is used, withot parsing the sip message, or go through the main request route as usual where the message will be parsed. For the second version, the hep route, sip_capture() won't be available since the message it's not parsed. This functionality stands only for routing/modifying the message. 2016-03-24 ionutrazvanionita * [9a9f092] : [acc] updated docs for do_accounting() 2016-03-24 ionutrazvanionita * [da17513] : [acc]flags used for accounting now replaced by do_accounting() Introducing new do_accounting() function. It has 3 arguments, first one is used to specify the type of accounting separated by '|'(db, log, aaa, evi, diam), second one to specify the flags used for accounting also separated by '|' (cdr, missed, failed - failed_transaction_flag) and the third parameter represents the name of the table used for accounting. 2016-03-24 Ovidiu Sas * [195a7e6] : Makefile.defs: disable optimizations while debugging (DBG_MALLOC is set) 2016-03-24 Ovidiu Sas * [5722f36] : Makefile.defs: new CC_OPTIMIZE_FLAG to define the compiler optimization level 2016-03-23 Bogdan-Andrei Iancu * [2503bc4] : Proper handling of isolated EPOLLHUP events. capture and handle EPOLLHUP or EPOLLERR events even if they are not in conjunction with EPOLLIN or EPOLLOUT. Many thanks to @santhosh-kumar for reporting and helping with troubleshooting. Fix for #829 2016-03-23 Bogdan-Andrei Iancu * [ea1eb62] : Preserve 3xx contacts via TH Do not change the contacts in 3xx replies to initial INVITE, as this breaks SIP redirect. Reported by @petekelly Issue #656 2016-03-23 ionutrazvanionita * [b7bd2d7] : [siptrace]docfix - default trace_on param value 2016-03-23 ionutrazvanionita * [06cec36] : [nathelper] fix bad condintion for branch checking 2016-03-22 Bogdan-Andrei Iancu * [43ff3fe] : Fixed some overlapping defines 2016-03-22 Bogdan-Andrei Iancu * [ec7b4e5] : Fixed 491 Pending scenario in B2B. Generate 491 Pending reply to the correct transaction (the new incoming one) and drop the requests (do not forward it). Credits for debugging, reporting and fixing go to @DMOsipov . Closes #679 2016-03-22 Bogdan-Andrei Iancu * [d8a302b] : Extend DR api to allow external sorting functions. Another module may register functions for sorting the GW list based on custom algorithms. This commit contains just the API, nothing more. 2016-03-21 Bogdan-Andrei Iancu * [3a136fe] : Prevent async in local route. The way local route is implemented (triggered and terminated in TM module) does not allow async resumes, so, for the moment, we simply force all the async triggers to be executed in a sync way in local route. (cherry picked from commit 940996fbbd9e7a131b4de4c1983df2788528250e) 2016-03-21 Bogdan-Andrei Iancu * [d20b3d0] : Accept XMLRPC requests without params node. If there are no parameters, the params XML node is not required. Repored by Nick Altmann Closes #826 2016-03-20 Bogdan-Andrei Iancu * [66d5cff] : Fixed port param for lb_is_destination(). It must accept integer values also, not only variables. Reported by @telephone-man on GITHUB Closes #808. (cherry picked from commit 143328f718e8c1e753e16d187415eb0ba80b7ef7) 2016-03-19 Bogdan-Andrei Iancu * [d837ed8] : Fixed usage of Prep Statement on failed CDRs. The number of inserted columns is variable (n+m or n+m+2), so we need different statements for each. Reported by @apsaras (on GITHUB) Closes #821 (cherry picked from commit e49e2e848bbd7243a31d7ca99d4bdc9360466cf9) 2016-03-18 Bogdan Andrei IANCU * [d2b5c52] : Merge pull request #832 from ionutrazvanionita/dr-wildcard Wildcard operator for partitions in some drouting functions 2016-03-18 Bogdan-Andrei Iancu * [992b49b] : Fix extracting TO hdr. Do not add '@' if username is not present. Reported by @DMOsipov on GITHUB. Closes #818 (cherry picked from commit e74f6655ee735feb7e5f22298405172fb74e86c1) 2016-03-18 ionutrazvanionita * [66399b7] : [drouting]updated docs 2016-03-18 ionutrazvanionita * [af25ef8] : [drouting]wildcard operator for is_from_gw partitions 2016-03-18 ionutrazvanionita * [57d7bfb] : [drouting]wildcard operator for do_routing 2016-03-18 James Criscuolo * [ae7a8ee] : presence module: move strlen check to avoid pkg_malloc(0) 2016-03-17 Bogdan-Andrei Iancu * [6c12ba9] : Reset to 0 the whole branch if failed. If we do not reset, on trying to add the next branch (on the same position), we will end up with dangling pointers. (cherry picked from commit d5c3bd1eed5f40f7425562f8bb65fe2f5c9fc678) 2016-03-17 Ovidiu Sas * [04d8a84] : scripts/opensipsctl: complete support for optional database port 2016-03-16 Razvan Crainea * [2600e00] : topology_hiding: prefent crash at shutdown 2016-03-16 Vlad Paiu * [2a61e97] : Fixed $TH_callee_callid to reflect '=' being an invalid character in callids Closes #800 (cherry picked from commit dc229a1ac6312cba9db75e49bc09b931a549375f) 2016-03-15 Bogdan-Andrei Iancu * [2d0cebc] : Fix crash when using $si in non SIP routes. If using $si in event, timer or startup routes, return a NULL value. Use a dummy AF_INET from src and dst IPs. Prevent crashing while evaluating $si by checking return code of ip_addr2a() Closes #817 Outdates #828 2016-03-15 Bogdan-Andrei Iancu * [7511e84] : Fixed async jump for end2end ACKs. As end2end ACKs do not create a sip transaction, force sync for all async operations (as we cannot suspend and resume). Closes #819 (cherry picked from commit b2bc43e76494e07d6cb1ce1bec2fc27e72fa0a47) 2016-03-14 Răzvan Crainea * [4a692e7] : Merge pull request #827 from jarrodb/rabbitmq event_rabbitmq order of _rmq_send attributes 2016-03-14 ionutrazvanionita * [63d8542] : [drouting] improve loading/reloading speed Drouting gateways and carriers are now being held internally as an avl map(implemented in core) instead of a linked list. This brings dramatic improvement to the time in which the module is loaded. 2016-03-14 Jarrod Baumann * [b8779e5] : event_rabbitmq order of _rmq_send attributes 2016-03-11 Bogdan-Andrei Iancu * [4601e56] : Fixed memory leak on checking disposition hdr. Free the parsed disposition body all the time (on success and failure) as it is not attached to any structure, so it will leak if not explicitly be freed. Credits for reporting this go to Jarrod Baumann. Based on an original fix from Jarrod Baumann Closes PR #825 2016-03-09 Ovidiu Sas * [764968f] : modules/usrloc: since domain field can be null, make null to be default 2016-03-09 Bogdan Andrei IANCU * [e6bb7ac] : Merge pull request #823 from jarrodb/db_postgres Add JSONOID to pg_type.h for supporting JSON return types as DB_STRING 2016-03-08 Jarrod Baumann * [8ef3f38] : Add JSONOID to pg_type.h for supporting JSON return types 2016-03-08 ionutrazvanionita * [b68c9b9] : [nathelper]updated fix_nated_contact() documentation Document that changes related to the contact made by fix_nated_contact() are lost in the resume route called after an async function is used. 2016-03-08 Liviu Chircu * [e403a2b] : dialog: Lower log level of state change errors Out-of-order SIP requests are a frequent occurence in high-traffic environments. The dialog module should not consider these events as being critical, but rather as warnings, so they can be easily filtered out from the logs. 2016-03-08 ionutrazvanionita * [fceff26] : [sipcapture]updated docs for hep_relay() 2016-03-08 ionutrazvanionita * [4ec54e8] : [sipcapture]new hep_relay() function This function allows turning OpenSIPS into a full HEP compatible proxy. Now you can add/change/remove hep headers and after all this you can forward your message anywhere you want. The routing is done same as in the tm module such that one can use multiple HEP destinations and balance between them. 2016-03-08 ionutrazvanionita * [e8679a0] : [proto_hep]fix buffer bug in hepv1/2 2016-03-08 Bogdan-Andrei Iancu * [370270c] : removed files uploaded by mistake 2016-03-08 Bogdan-Andrei Iancu * [d2307e5] : Update all script with log_level and debug_mode 2016-03-08 Bogdan-Andrei Iancu * [3b37b82] : Fixed leak due wrong return code handling. Based on a patch provided by Jarrod Baumann (@jarrodb) Closes #822 2016-03-08 Bogdan Andrei IANCU * [f906d93] : Merge pull request #814 from sippy/master_rtpp_ip6_fix Fix an issue with IPv6 in the rtpproxy module 2016-03-08 Bogdan Andrei IANCU * [3f4dc80] : Merge pull request #770 from ionutrazvanionita/sipcapture [sipcapture] full support for hep in sipcapture/proto_hep 2016-03-07 Liviu Chircu * [c234282] : Completely remove script flags Namely, setsflag(), resetsflag(), issflagset() With the advent of async operations, the delimitation between current SIP message context and script context is even more blurry. Fixing the script flags to work with async would involve storing them into message context. But that would also make them persistent through failure route. In turn, this breaks the concept of scripts flags, since they should only live through a single script execution (entry->exit). To sum up, we dropped script flags because async silently broke them, and fixing them would actually break them even more! 2016-03-05 Ovidiu Sas * [f4b803f] : modules/b2b_logic: fix b2b_logic lable for dbtext - allow scenario, e1_sid and e2_sid fields to be NULL 2016-03-05 Ovidiu Sas * [11a805b] : modules/b2b_entities: fix crash when restoring b2b entities from dbtext 2016-03-05 Ovidiu Sas * [7e0175b] : modules/usrloc: fix location table for dbtext - allow domain field to be NULL 2016-03-04 Liviu Chircu * [60d4d4b] : tm: Refactor some hardcoded static string lengths This also fixes a couple of chopped MI error strings in "t_uac_dlg" 2016-03-04 Razvan Crainea * [a607aa8] : tcp: prevent invalid mem access Reported by Saúl Ibarra Corretgé 2016-03-03 Liviu Chircu * [6d1704c] : Allow setting branch flags when no branches are yet appended Commit 5e3f06800e was too restrictive, since flags for "branch 0" (a special branch, stored in current struct sip_msg) may be set regardless of the number of append_branch() operations done previously! Reported by Dragomir Haralambiev 2016-03-03 ionutrazvanionita * [3ae1811] : [proto_hep]set predefined context memory to 0 before using it 2016-03-03 ionutrazvanionita * [419c9dc] : [sipcapture]updated docs 2016-03-03 ionutrazvanionita * [67c7473] : [sipcapture] hep_chunks setter/getter/delete funcs; hep_net variable * hep version 3 chunks have now three functions with which they can be manipulated: hep_set(add/modify a chunk), hep_get(get vendor id and value of a chunk) and hep_del(remove a chunk); * hep_net is a pseudovariable which gives layer 3 and 4 information about hep communication(where does the packet comes from and on which interfaces it was received); * sip capture now holds an extra parameter - protocol type which gives information about the protocol of the encapsulated packet; 2016-03-03 ionutrazvanionita * [1714db5] : [proto_hep]custom chunks, predefined context, bug fixes * proto_hep can now read custom chunks; * use 0x0000 vendor id for generic chunks instead of OpenSIPS vendor id; * what was being done in sipcapture(modify receive_info structure with data from the hep message) is now being done in proto_hep; * proto_hep populates a predefined context in which he holds its own data, passing this data to receive_msg() function; * don't use hardcoded generic chunk values; * fixed bug in hep udp(v1/2) 2016-03-03 ionutrazvanionita * [7464a86] : have a predefined context before calling receive_msg 2016-03-03 ionutrazvanionita * [09d6d47] : [proto_hep]avoid double byte order conversion for payload chunk 2016-03-03 Ionut Ionita * [202b0e1] : Merge pull request #809 from ionutrazvanionita/postgres_bigint [dbschema]8 byte autoincrement is now fixed to BIGSERIAL PRIMARY KEY 2016-03-02 Răzvan Crainea * [252a758] : Merge pull request #815 from sippy/master_freebsd_fix Fix build error on FreeBSD 2016-03-01 Maksym Sobolyev * [60342f5] : Fix build error on FreeBSD by including proper headers for the struct in_addr. 2016-03-01 Ovidiu Sas * [9e8654f] : modules/presence: print error log only for errors 2016-02-29 Maksym Sobolyev * [ef01e85] : Fix an issue with IPv6 which may result in SDP containing mismatching protocols, e.g. "c=IN IP4 0:0:0:0:0:0:0:1" 2016-02-29 Razvan Crainea * [6c99004] : rename s_addr to avoid overlapping with Solaris structs Discovered by Nathaniel L. Keeling 2016-02-29 Julián Moreno Patiño * [e5f0b55] : Deprecate 'use UNIVERSAL' 2016-02-29 Liviu Chircu * [84be3ac] : mathops doc: fix typos 2016-02-29 Liviu Chircu * [f3a1d2d] : Merge pull request #812 from rrb3942/master Switch math_eval to use tinyexpr and remove conversion to RPN 2016-02-27 Ovidiu Sas * [103e2b7] : db/schema: emergency_service_provider - fix copy/paste error generating duplicate fiels name 2016-02-27 Ryan Bullock * [ba5da58] : Switch math_eval to use tinyexpr and remove conversion to RPN 2016-02-27 Ovidiu Sas * [103f5f5] : modules/presence: fix active_watchers table for dbtext - allow reason field to be NULL 2016-02-26 Liviu Chircu * [0962af1] : rest_client: Improve logging of TCP connect 2016-02-26 ionutrazvanionita * [6e591a6] : [epoll]LM_DBG on EPOLLHUP event 2016-02-26 Razvan Crainea * [fe785bf] : loop: declare fd_map for devpoll Reported by Nathaniel L. Keeling III 2016-02-26 Di-Shi Sun * [0e3c707] : Merge branch 'master' of https://github.com/OpenSIPS/opensips.git 2016-02-26 Di-Shi Sun * [b0145d4] : Fixed typos. 2016-02-26 Di-Shi Sun * [96b8c70] : Updated to support STIR. 2016-02-25 ionutrazvanionita * [ef5ae01] : [dbschema]8 byte autoincrement is now fixed to BIGSERIAL PRIMARY KEY 2016-02-25 Ionut Ionita * [2b8fd2d] : Merge pull request #781 from ionutrazvanionita/dlg_profiles [dialog] flag for replicating profiles 2016-02-25 ionutrazvanionita * [6a4f732] : [dialog][dlg profiles patch]fix coding style issues 2016-02-25 Razvan Crainea * [a547af5] : other spellchecks 2016-02-25 Julián Moreno Patiño * [a71f129] : update spelling checks (cherry picked from commit e6b73956c817629b66eef801a8ffe06d5e2d457f) 2016-02-25 Julián Moreno Patiño * [a3de5fc] : support reproducible builds (cherry picked from commit d04e3b7fa96142b6def8430890c6c51fa5e1ef6a) 2016-02-25 Julián Moreno Patiño * [25a53ef] : update changelog and add berkeley deb (cherry picked from commit 04b2eba31b6f7806a1f10a492aa393920a1b4d1f) 2016-02-25 Julián Moreno Patiño * [a336e99] : fix parallel building 2016-02-24 Liviu Chircu * [5718e24] : destination sets: Add yet another NULL check Reported by Ovidiu Sas Fixes #806 2016-02-24 Liviu Chircu * [4b1ed7d] : Merge pull request #786 from rvlad-patrascu/time_keeper_bug Fix the lagging behind of time_keeper process 2016-02-24 Liviu Chircu * [fb8b1f2] : SIP forking logic: Add extra NULL checks This prevents a crash when no branches are pushed yet, but a branch management function is called (e.g. t_replicate() or a write to $(branch(uri)[0]) is done) Issue reported by Trevor Francis 2016-02-24 Liviu Chircu * [5e3f068] : SIP forking logic: Add extra NULL checks This prevents a crash when no branches are pushed yet, but a branch management function is called (e.g. t_replicate() or a write to $(branch(uri)[0]) is done) Issue reported by Trevor Francis 2016-02-24 Julián Moreno Patiño * [2a999c7] : Order target dependencies to avoid FTBFS in parallel builds. (cherry picked from commit 9fc407d01a1743fa8332d9436004994d3e4dbfb3) 2016-02-24 Liviu Chircu * [f09cd45] : blacklists code: Improve coding style * add space between: * statement and paranthesis * operands and operators * proper use of "NULL" vs. "0" * proper way to perform NULL-checking: * for function results * for all pointers 2016-02-24 Liviu Chircu * [2e7588b] : Blacklisting logic: Fix async-related issues Similar issue to the one behind commit a7b287f5f. Static memory holders are no longer compatible with async operations and need to be moved into dynamic mem (pkg in our case) so that data can be properly restored before resuming the script. 2016-02-23 Răzvan Crainea * [85618dc] : Merge pull request #631 from staskobzar/master Fixing bug in debian init script. 2016-02-23 Răzvan Crainea * [2253071] : Merge pull request #728 from ionutrazvanionita/rlimit Slot Based Taildrop algorithm for ratelimit 2016-02-23 Razvan Crainea * [cec0c72] : build: change variables for other OSes 2016-02-23 Razvan Crainea * [b6324b3] : debian: remove date-time 2016-02-23 Liviu Chircu * [a7b287f] : SIP forking engine: Fix async-related issues Previously (on OpenSIPS...), doing _any_ async operation at script level after a destination set had been built (by lookup(), append_branch(), etc) would have lead to unpredictable results. This patch addresses the problem by moving the destination set storage into the current processing context (from static mem to pkg mem). Impact on memory usage at worst (12 branches for each request) is an extra 30 MB of PKG memory per every 1000 cps. 2016-02-23 Razvan Crainea * [ff0a71a] : fix warnings on i386 2016-02-23 Razvan Crainea * [91ff393] : debian: update prefix names 2016-02-23 Liviu Chircu * [d0418af] : Improve logging for out of memory errors OpenSIPS now kindly displays _what kind_ of memory it actually ran out of! 2016-02-23 Razvan Crainea * [4d5d2ce] : Makefile: dash cannot use variables that contain - 2016-02-23 Razvan Crainea * [ea4b145] : debian: add glibc for xmlrpc 2016-02-23 Razvan Crainea * [f7aeca8] : debian: lua no longer depends on libmemcache 2016-02-23 Razvan Crainea * [e8368f4] : lua: update memcached readme 2016-02-22 Razvan Crainea * [1ba0327] : lua: port obsolete memcache to memcached 2016-02-22 Razvan Crainea * [6ec4106] : complete rename set_proc_log_level 2016-02-22 Razvan Crainea * [439e5ee] : allow libjson0-dev in travis they are using older packages 2016-02-22 Razvan Crainea * [2abd579] : obsolete libjson0-dev 2016-02-22 rvlad-patrascu * [29dd83b] : Fix the lagging behind of time_keeper process Solution involves occasionally synchronizing with the system time in the time_keeper and also time drift compensation in the timer process. Fixes #745 2016-02-19 Liviu Chircu * [9cf7bec] : Merge pull request #715 from ionutrazvanionita/db_virtual_async ASYNC_CHANGE_FD status + db engine async state parameter + db_virtual async 2016-02-18 Bogdan-Andrei Iancu * [bd94e6b] : Docs updated on t_newtran() 2016-02-18 Bogdan-Andrei Iancu * [0db7789] : t_newtran() automatically sends a 100 Tryting for INVITE. TO have a consistent behavior between: t_newtran(); t_relay(); and t_relay(); (where in the first case a 100 Trying will not be auto generated), we do not sent the 100 Trying on r_relay (when transaction is built) but on t_newtan(). Reported by MAxim Sobolev. 2016-02-18 ionutrazvanionita * [737ced7] : async support for db_virtual 2016-02-18 ionutrazvanionita * [9abcf92] : hold db state between async call and resume function Databases can now keep their state between the async call and the resume parameter using a newly added (void*) parameter. It is the job of the upper layer(the module calling the async function) to store this database state inside the (void*) parameter they are using to hold their state (see avpops and sipcapture async handling). Also this patch handles the new ASYNC_CHANGE_FD state in avpops and sipcapture. 2016-02-18 ionutrazvanionita * [ff09d49] : add ASYNC_CHANGE_FD status in reactor Allows changing the file descriptor in the resume function. Such functionality is useful for failover purposes, like having multiple destinations, you find that one is not working only in the resume function and you want to try another one. Now you can change the file descriptor, remove the old one and add the new descriptor and then wait for it to be triggered. 2016-02-17 Ovidiu Sas * [8d6bba5] : modules/pua: fix crash at startup when no tuple_id 2016-02-16 Ovidiu Sas * [386eba0] : modules/b2b_sca: set default table to b2b_sca 2016-02-16 Ovidiu Sas * [5dab066] : modules/b2b_logic: fix crash when using "top hiding" scenario 2016-02-16 Ionut Ionita * [fbdf076] : Merge pull request #790 from liviuchircu/master Improve qvalue parsing 2016-02-16 Bogdan Andrei IANCU * [1ed467b] : Merge pull request #793 from jarrodb/dispatcher add ds_probing_list parameter to specify which sets to probe 2016-02-16 Jarrod Baumann * [2140eee] : documentation for additional ds_probing_list parameter 2016-02-15 Bogdan-Andrei Iancu * [2b7d89b] : Fixed bogus init of missed cols types. Closes #795 . Reported by @ankogan on GITHUB 2016-02-15 Razvan Crainea * [c24b587] : arm: unforce thumb mode 2016-02-15 Jarrod Baumann * [a49551c] : add ds_probing_list parameter to specify which sets to probe 2016-02-12 Liviu Chircu * [ed60363] : Improve qvalue parsing * accept qvalues with no decimal digits (e.g. "1." and "0.") (this removes E_Q_DEC_MISSING from error.h) * improve bad qvalue error reporting 2016-02-12 ionutrazvanionita * [0a2cc3e] : [freeradius-client async patch][bugfix]on resume fetch index from async context 2016-02-12 Liviu Chircu * [037e259] : Update ViM syntax file 2016-02-11 Vlad Paiu * [82218fa] : Don't push throug RTPProxy streams with port 0 in SDP (cherry picked from commit 5dec43a750c7af714dfec9ac9ef43cc0c33e2280) 2016-02-11 ionutrazvanionita * [6a40cd6] : [acc][bugfix]update module's log_level not global in modparam section 2016-02-11 Bogdan-Andrei Iancu * [7367cee] : Fix proper callig in local cancels with TH. Extend the coverage of the preocessing context and TM context over the cancel_branch() function (in the timeout handler) so the TH callbacks can reach back the dialog and do the TH related changes. Reported by Julian Santer on mailing list. (cherry picked from commit 8133656de9503a122a72c0f80d11eff975bc43f1) 2016-02-11 Razvan Crainea * [421e3e3] : proper compile on gcc 4.8 2016-02-09 Bogdan-Andrei Iancu * [583ab03] : Fix compile error. As now log_level is a global variable, avoid using it as function param or local var. Reported by Nick Altmann 2016-02-08 Bogdan-Andrei Iancu * [f174629] : set_debug() function replaced with $log_level The new script variables allows read and write operations. On write, it does exactly as the old set_debug() function (on writting the NULL value -> reset the log level) 2016-02-08 Bogdan-Andrei Iancu * [46a26bf] : Updated to new the prototypes for logging functions 2016-02-08 Bogdan-Andrei Iancu * [61f1b42] : "debug" global parameter renamed to "log_level" 2016-02-08 Bogdan-Andrei Iancu * [8803a87] : Renamed local log_level variables/parameters. 2016-02-08 Bogdan-Andrei Iancu * [b9b0083] : Added "debug_mode" option as replacement for "fork" While enabling "debug_mode", opensips will automataically force: - staying in foreground (do not detach from console) - set logging level to 4 (debug) - set logging to standard error - enable core dumping - set UDP worker processes to 2 - set TCP worker processes to 2 2016-02-05 Bogdan-Andrei Iancu * [7eced16] : Removed the "dont_fork" feature 2016-02-05 Bogdan-Andrei Iancu * [ce89e29] : Fix IPv6 insertion in fix_nated_contact() Add the [ ] around IPv6 in the newly built contact URI. Reported by Jonathan Hunter on the mailing list 2016-02-02 ionutrazvanionita * [81cf203] : [dialog] updated the docs for the new flag feature 2016-02-02 ionutrazvanionita * [4468b4a] : [dialog] fix bug - don't set repl to null after allocating it 2016-02-02 ionutrazvanionita * [23c02f3] : [dialog] flag for replicating profiles added a new flag which allows replicating over the bin interface only the profiles which have it set 2016-02-02 Bogdan Andrei IANCU * [8e10339] : Merge pull request #752 from ionutrazvanionita/siptrace [siptrace] all types of tracing are done using sip_trace function 2016-02-02 ionutrazvanionita * [76ba3b5] : [siptrace] all types of tracing are done using sip_trace function * added trace_id parameter with which one is able to define database urls and trace uris (HEP and SIP); the trace id is given as an argument to sip_trace() function; * multiple trace_id s can be defined under the same name which will alow users to do multiple types of tracing using only one function call; * sip_trace() now accepts flags to specify what to trace: message, transaction or dialog; in stateless mode only message trace does a valid type of trace; * traced_user_avp now is as an argument to sip_trace() function; it's name now changed to trace_attrs and also the column in the database and table version were modified;setting it won't duplicate entries in the database; it will do only one insert setting the trace_attrs column accordingly; * most of the parameters in the modparam section(db_url, table, duplicate_uri, duplicate_with_hep) are now part of a trace_id called 'default'; removed the old ones completely; all that remains now is the trace_on parameter which can be set/unset with mi (read the docs for more info), trace_local_ip and trace id; * the module can catch internally generated stateless replies using the sl module; * if transaction module not loaded but transaction mode selected for sip_trace() function, the module will work in transaction aware mode, meaning that will catch incoming and outgoing requests and also will catch internally generated replies using the sl module; 2016-02-02 Bogdan-Andrei Iancu * [031304a] : Fix freeing processing context after a second async jump. If thw processing ctx was stored back into transaction (due a new async call), do not free the current ctx (as it became NULL). Reported by Minh PHAN (@qmphan). Closes #780 . 2016-02-01 Bogdan-Andrei Iancu * [6dd88ab] : Fixed autodetection of the reactor size (open files) If we auto-detect the size of the reactor(via rulimit), be sure we have enough pkg memory (and do the necessary correction). If the open files limit is given, warn if the reactor will use too much memory. Thanks to MAxim Sobolev for reporting it via mailing list and to @cepehutu Closes #778 2016-02-01 Bogdan-Andrei Iancu * [0921088] : Fix missing update on SL callbacks. related to the prev commit (8c56303) 2016-02-01 Bogdan-Andrei Iancu * [8c56303] : SL callbacks moved into core and merged with fwd callbacks. 2016-02-01 Bogdan-Andrei Iancu * [91df869] : Fix calculation of timer set on 32b A bogus bitwise operation broke the calculation of the timer set on 32bits arch. Reported by @mshary (Muhammad Shahzad Shafi) Closes #732. (cherry picked from commit 5b7b5f78cb675d1d4126866307dc0caa22067d09) 2016-01-28 Liviu Chircu * [a4e3ff8] : Fix declarations/definitions for all clusterer api variables Variable definitions (as opposed to declarations) in C header files are a bad practice and must be avoided. Reported by Dragomir Haralambiev 2016-01-28 Liviu Chircu * [4c60af8] : $DLG_timeout pseudo-var: Fix crash on write operations Unless dialog replication is enabled, issue would occur. Credits to Dragomir Haralambiev for reporting and helping troubleshoot this 2016-01-28 Bogdan-Andrei Iancu * [2efdbf8] : Fix the sizing of the Async Reactors. Adjust the size of the reactor accroding the limit of opens files. Closing #765. 2016-01-26 Bogdan-Andrei Iancu * [b03fce9] : Fixed wrong warning on memory leak. Set the auth_hdr buffer to NULL after moving the buffer into the lump. Reported by Colin Martin on mailing list (cherry picked from commit bbaf8213ae9856d2345aa66d8aa002ec5f1f3fa5) 2016-01-26 ionutrazvanionita * [be43fcd] : [event_xmlrpc] accept paths when subscribing to event Before, by default, in the first line of the HTTP POST that was sent to the server the path was always '/RPC2'. Now you can specify the path after the port, just like in urls 'xmlrpc:ip:port/path/to/rpc:method'. 2016-01-26 Bogdan-Andrei Iancu * [3a6f4b9] : Fix codec handling functions when faulty streams are defined. Bogus test on the return code of stream_process() (using bitwise OR for -1, 0 and 1 values :O ), leads to a generic failure to find any codec in any stream if there is an invalid stream in the SDP. For example, if a video stream is defined but with no codecs, stream_process() will return -1 for it (as it is a bogus stream). While iterating through all the stream (including the valid audio stream), the -1 ret code will discard any 1 future ret code due the bogus bitwise OR. (cherry picked from commit ef82ca2368462c41f7115202e90f73d8ab16fd1f) Conflicts: modules/sipmsgops/codecs.c (cherry picked from commit 41c8902327aa42af9718980acf9260d3621f2a50) 2016-01-25 Bogdan-Andrei Iancu * [eaef57e] : README files regenerated 2016-01-25 Bogdan-Andrei Iancu * [0042b94] : Fix bugs in modules-readm target. 2016-01-25 Bogdan Andrei IANCU * [a61a88d] : Merge pull request #632 from shripchenko/alias_db alias_db module changed to allow alias_db_find() to be called from startup route 2016-01-25 Bogdan-Andrei Iancu * [e210f45] : Fix STANDARD and EXTRA list of tables. Permissions (in postgres) were not granted for the missing tables. Reported by Nathaniel L. Keeling III on mailing list (cherry picked from commit aebf41f79274679bfc9a984a9d0c6871ac139462) Conflicts: scripts/opensipsdbctl.base 2016-01-25 Vlad Paiu * [e303afa] : Fix double free in case of errors when running MI command (cherry picked from commit ce6e8444afeb887f30d38cf90b0176ae9960fb72) 2016-01-25 ionutrazvanionita * [48bc2d4] : [siptrace][Bugfix]Use hep proto socket_info struct when trying to send 2016-01-22 ionutrazvanionita * [dfa7787] : [nathelper]before trying to remove check if remove_on_timeout flag set 2016-01-21 Razvan Crainea * [9f68260] : daemonize: fix unnecessary close command 2016-01-21 Razvan Crainea * [8a17be2] : fixed some missed compile warnings 2016-01-21 Razvan Crainea * [a0caa91] : fix more bugs identified by coverity 2016-01-21 Razvan Crainea * [3c37af3] : ws: verify headers when in client mode 2016-01-21 Bogdan-Andrei Iancu * [b11baad] : Missing module "emergency" in extra list. This was braking the creation of extra tables (at least in postgres is did). Reported by Nathaniel L. Keeling III on mailing. (cherry picked from commit d9af15b70ed50b143e0ba5b27ff4a4b554ece28a) Conflicts: scripts/opensipsdbctl.base 2016-01-21 Liviu Chircu * [1fedeee] : Coverity PR: minor refactorings (cherry picked from commit 975024142e724b315203dd14c58056a77f3d3959) Conflicts: modules/ldap/iniparser.c modules/usrloc/dlist.c 2016-01-20 Bogdan Andrei IANCU * [02b4c25] : Merge pull request #769 from nikbyte/master Completely cpl-c rename 2016-01-20 Nick Altmann * [67d3e8b] : Completely cpl-c rename 2016-01-20 Bogdan Andrei IANCU * [ecfe3f7] : Merge pull request #768 from nikbyte/master Debian repository: fix cpl-c module building 2016-01-20 Nick Altmann * [00f2228] : Debian repository: fix cpl-c module building 2016-01-20 Nick Altmann * [13daa5c] : RPM spec: prepare for opensips 2.2 2016-01-20 Bogdan-Andrei Iancu * [a76789c] : Renamed cpl-c to cpl_c for consistency reasons 2016-01-20 Bogdan-Andrei Iancu * [cc75a4c] : Added structure for wss docs. (work in progress) 2016-01-19 Bogdan Andrei IANCU * [8d23d9b] : Merge pull request #664 from wdoekes/wjd-allow_unset_dlg_in_req_route dialog: Document that unset_dlg_profile can be used from request_route. 2016-01-19 Razvan Crainea * [384a4ad] : integrate with clang and fix warnings 2016-01-19 Razvan Crainea * [3560844] : evi: subs can never be NULL 2016-01-19 Razvan Crainea * [b33f1ac] : call va_end() on all paths 2016-01-19 Bogdan-Andrei Iancu * [4fc06a9] : Fix restoring leg route set. Reported by Søren Andersen. (cherry picked from commit b602a7fb06a3658b71adedd69cf94eae814e93e5) 2016-01-18 Razvan Crainea * [70e5a65] : uri: fix e164_check overflow checking Reported by @ferrored on github Closes #762 2016-01-18 Liviu Chircu * [ccce2e6] : Coverity PR: Fix bad conflict merge (cherry picked from commit 83f459781eb662c351f6edaaa088443a34ca2136) 2016-01-18 Dusan Klinec * [4ff4715] : fixing coverity found defects - memory corruption, null dereference (cherry picked from commit fd57609f5411e622dca593b729e1826269243c5b) (cherry picked from commit 56a72304b9ec154d70dd41a63f29fa33faecaef5) 2016-01-18 Dusan Klinec * [79f19c7] : fixing coverity found defects - null dereference & broken logic (cherry picked from commit 81aeba42ca04df7b3fc42ab2498717cc31227967) (cherry picked from commit 6b4a3271da6a62f5bead2a958672de45def57ad6) 2016-01-18 Dusan Klinec * [8c9b818] : fixing coverity found defects - null dereference (cherry picked from commit 906bb9172b4ce0ab0e7bf4eca7da58211aa8ac5b) Conflicts: cachedb/cachedb_id.c (cherry picked from commit 37a7466c8b7c3e8ec979444c37c9f738bd0e43d8) 2016-01-18 Dusan Klinec * [49c1d65] : fixing coverity found defects - null dereference (cherry picked from commit b578dda307f24e56f456e5b02c0c7591ed6746a4) (cherry picked from commit 19e518835dd0f88868613c5fe6fbf8fe19180252) 2016-01-18 Dusan Klinec * [0a8e277] : fixing coverity found defects - copying into fixed size buffer without check, db_http (cherry picked from commit d471234733de299c88f1036ba17a2406b8ae7a89) (cherry picked from commit 20004e4a87d4e84c216782dc06484a970183894a) 2016-01-18 Dusan Klinec * [3cf56b1] : fixing coverity found defects - copying into fixed size buffer (cherry picked from commit 92a7361eda2c6cd6e9ad637dab187cf87fefa987) (cherry picked from commit c9aaae3ac63970e3f394f66e048a1c82b99774c7) 2016-01-18 Dusan Klinec * [26de471] : fixing coverity found defects - null dereference, invalid e164 number check (cherry picked from commit b8e2318ba58e56484294913e21a74a7cd79adf40) (cherry picked from commit c48773cdb2ae9f8b6ab44b38df1d2c37127537ba) 2016-01-18 Dusan Klinec * [5e31e35] : fixing coverity found defects - null dereference, break missing (cherry picked from commit c004967f7c272e5b9043f5f3e330827f0682a009) Conflicts: db/db.c (cherry picked from commit ed699bfde4759434937349e198a694a4bab3f9ff) Conflicts: modules/usrloc/dlist.c 2016-01-18 Dusan Klinec * [790495e] : fixing coverity found defects - logical fix in ul callback check type, null dereference (cherry picked from commit aeedb7d94439b774d35009899b04d2c6060437d0) Conflicts: modules/dispatcher/dispatcher.c (cherry picked from commit a152bdf3c0a3877827e1a02f7e05d611a0366a7f) 2016-01-18 Dusan Klinec * [347fea8] : fixing coverity found defects - processing initialized variables, control flow, invalid expressions (cherry picked from commit b22844e0a1d4d54d0f8f836837f8447154799011) Conflicts: modules/drouting/drouting.c (cherry picked from commit 50737ff2de91e55971512bf327042fe3f51ca619) 2016-01-18 Dusan Klinec * [853702b] : fixing coverity found defects - resource leakage (cherry picked from commit e09f09ae89aff31393b04e40b4f82632fd4f50ff) (cherry picked from commit 21083e84ede0551ae937bb4cea7c6ddab44d86f1) 2016-01-18 Dusan Klinec * [1f7d58e] : fixing coverity found defects - invalid memory access / memory corruptions (cherry picked from commit c537cb8151e9436d88a9410e02ef11a093008ff0) Conflicts: modules/dialog/dlg_tophiding.c (cherry picked from commit 57a7bf02a54f91020e5163be82b0ac3cf09e0531) 2016-01-17 Nick Altmann * [6a5d421] : RPM spec: prepare for new build system - remove obsolete triggers - change tarball name and url 2016-01-15 Ovidiu Sas * [be371da] : uac_registrant: update timer handling - closes #758 - uac_registrant wrong hash sequence 2016-01-15 ionutrazvanionita * [9bd4c77] : [avpops]fix acmds instead of cmds 2016-01-15 ionutrazvanionita * [939b343] : [avpops]check if asyn funcs used in separate loop(on acmds struct) 2016-01-14 ionutrazvanionita * [785e4b8] : [proto_hep]don't htons one byte type variable 2016-01-14 Ovidiu Sas * [666cf83] : tm: remove unused variables 2016-01-14 Bogdan-Andrei Iancu * [f14804b] : Import some changes from PR #661 2016-01-14 ionutrazvanionita * [16cea6a] : [sipcapture]completely remove mod_init mi_cmds registration 2016-01-14 Razvan Crainea * [47bbc07] : bin: report the dimension of the buffer This allows the user to control how much info to send 2016-01-14 ionutrazvanionita * [e36453e] : [sipcapture]mi_cmds were registered twice 2016-01-13 ionutrazvanionita * [055f4b1] : [snmpstats]more hints for setting up in the docs 2016-01-13 Liviu Chircu * [eef55c9] : opensipsdbctl: Properly create "clusterer" and "tls_mgm" tables 2016-01-13 Bogdan-Andrei Iancu * [7d090c3] : Allow retransmissions and wait for reply/timeout on canceled branches. Do not do anything about branches with no received reply; better continue the retransmission hoping to get something back; if still not, we will generate the 408 Timeout based on FR timer; this helps with better coping with missed/lated provisional replies in the context of cancelling the transaction Many thanks to Maxim Sobolev for bringing this under discussion. More to be read: http://lists.opensips.org/pipermail/devel/2015-November/018623.html http://lists.opensips.org/pipermail/devel/2015-December/018810.html (cherry picked from commit 0dfb46df96d594c9b592d64913a44ab1cccd80e3) 2016-01-13 Bogdan-Andrei Iancu * [ea6e1fa] : Fix race condition between retransmission and wait timer in TM. Overlapping of retransmission timer and wait timer may lead to inconsistent timer list -> various crashes around TM timer lists. Related to #749, #732, #723. Many thanks for Trevor Francis ( @46labs ) for helping with the troubleshooting. IMPORTANT NOTE: this is a temporary fix as it simply avoid the collision between the two timer routines - normally the routines themselves should be safe to run in parallel (more digging is required here). 2016-01-13 Bogdan-Andrei Iancu * [aa5d483] : Fix pinging partitioning from nat_traversal. Do not use the static counter as the timer routine gets executed in various processes now - better use a shm counter. See all the details in #751 Based on the PR #751 Closing / outdating #751 Credits for finding the fix and comming up with a solutoin go to @aerringer. (cherry picked from commit 4395575d7f251e14fdc7b0dd92b7f5b329515b4e) 2016-01-13 Razvan Crainea * [76034ad] : redis: add timeout for connect and query This prevents OpenSIPS from blocking waiting for a connection to be established. Fixes #753 reported by Nick Altmann 2016-01-12 Razvan Crainea * [29190f3] : ws: release TCP connection if not static This should fix a memory leak (reported in #743 by Eric Tamme) 2016-01-12 Razvan Crainea * [ffb228d] : ratelimit: supress limit warning when it changes 2016-01-12 Liviu Chircu * [acb9f72] : Merge pull request #665 from ionutrazvanionita/master fix calls per minute for fraud_detection module 2016-01-12 ionutrazvanionita * [dd25c63] : fix calls per minute for fraud_detection module Now the calls per minute parameter is build as consisting of an initial time(t0) and a 60 seconds window(as was before). Each window slot holds the calls received in that second(as before). The difference is that now these parameters are updated based on three conditions: * we receive the call between [t0; t0 + WINDOW_SIZE(60s)] - we only update the number of calls for that slot(t0 + current time) and the calls per minute parameter * we receive the call in the interval of [t0+WINDOW_SIZE; t0+2*WINDOW_SIZE] - we do the following: move t0 in current call time - WINDOW_SIZE, invalidate all the calls we knew of from t0 to current t0 (current call time - WINDOW_SIZE) * we receive a call after 2 * WINDOW_SIZE(60 seconds) + t0 - this invalidates all the calls we knew of since the window is not does not contains calls newer than the last 60 seconds; 2016-01-11 ionutrazvanionita * [b32a14e] : [proto_hep]correctly calculate length when sending 2016-01-11 Bogdan Andrei IANCU * [48023d7] : Merge pull request #748 from ionel-cerghit/ds_list_explicit dispatcher: add ds_list full mi command, update documentation 2016-01-11 Cerghit Ionel * [829bd47] : dispatcher: reworked ds_print_mi_list flags parameter 2016-01-08 Cerghit Ionel * [f471785] : dispatcher: add ds_list full mi command, update documentation optional parameter 'full' added for ds_list full, which will display the weight, priority and description of a destination role of weight and priority briefly explained in documentation, FAQ section 2016-01-08 Liviu Chircu * [2e5160e] : cachedb_mongodb: Fix crash when losing DB connection Tested with mongo-c-driver v0.6. It turns out that the driver's mongo_cmd_get_last_error() does not always return an iterable bson structure. This patch adds a safety check, so that iteration is skipped. Reported by Sasmita Panda Closes issue #747 2016-01-08 Bogdan Andrei IANCU * [2e59ba9] : Merge pull request #746 from lemenkov/remove_has_sdp rtpproxy: patch: has_sdp() does not exist 2016-01-08 Liviu Chircu * [1cf1997] : dispatcher doc: Improve "partition" param section 2016-01-08 Liviu Chircu * [e0c0a54] : Fix several print formatting bugs %*.s is a format specifier for string width %.*s is a format specifier for string length 99% of the time, we want the latter one and _not_ the former. 2016-01-08 Liviu Chircu * [d334a16] : dispatcher params: Add support for any whitespace char Improves script coding style. This is working now: modparam("dispatcher", "partition", "ds_sip_inbound_gws : \ db_url = mysql://user:pass@172.16.4.5/opensips; \ table_name = dispatcher; \ attrs_avp = $avp(ds_attr_i)") 2016-01-06 Mikko Lehto * [4af7963] : rtpproxy: patch: has_sdp() does not exist 2016-01-06 Bogdan-Andrei Iancu * [078646e] : Just identation fixes 2016-01-06 Bogdan-Andrei Iancu * [6205cff] : Improve WSS parsing. Avoid doing one step back (and double parsing) when deciding between WS and WSS. 2016-01-06 Bogdan-Andrei Iancu * [f0aed8e] : Merge branch 'jarrodb-parse_wss' 2016-01-06 Jarrod Baumann * [8dd8c0b] : add parse_uri support for transport=wss 2016-01-05 Razvan Crainea * [968a4e0] : proto_wss: first version 2016-01-05 Razvan Crainea * [b3db77b] : nathelper: support wss in received 2016-01-05 Razvan Crainea * [5a38f69] : rearange tls functions 2016-01-05 Razvan Crainea * [e97fea1] : tls: more rework 2016-01-05 Razvan Crainea * [f2687dc] : ws: make resource configurable 2016-01-05 Razvan Crainea * [0665943] : fix wss socket print 2016-01-05 ionutrazvanionita * [7461c83] : Slot Based Taildrop algorithm for ratelimit In this new algorithm a window is held per every pipe, which is defined by two parameters: window_size(seconds) and slot_period(microseconds). The number of slots is window_size*1000/slot_period. The window is updated with every call received and it's implemented as a circular vector, which modifies it's start based on the elements that were dropped. How is the window updated: * if no message comes in 2*window_time from when the window start was set , it means we can't use the values from any of the slots and we set the value from any of the slots to 0; the window start time is updated; * if the message comes in t=[start+window_time; start+2*window_time) it means that we can keep the values in slots in the interval [t-window_time;start+window_time) and drop the ones in [window_time; t-window_time); the window start time is updated; * it the message come in t=[start; start+window_time) we don't do any update do the window, only to the value of the slot corresponding to time t; The algorithm also shares this value through the bin interface, allowing to have the values from all the replicas. Also, before sending the value through the interface, the replica checks the window in order to send only the values in valid slots. Two different functions were made because of efficiency reasons. When replicating we only do some very short loops or maybe nothing. 2016-01-05 Liviu Chircu * [1befb58] : Merge pull request #731 from ionel-cerghit/sql_cacher_simplified sql_cacher: remove pkg memory temporary list 2016-01-05 Liviu Chircu * [8d697a6] : Makefile.conf.template: Improve mem allocator tooltips Also disable VQ_MALLOC for upcoming versions, since it's unmaintained and broken. 2016-01-05 Liviu Chircu * [a6de582] : menuconfig: Allow entries to be commented out By using double comment, "##", we can prevent a DEFS line from being displayed in menuconfig, while keeping it in the template file. 2016-01-04 Ovidiu Sas * [6f183e7] : b2b_logic: remove unused variable 2016-01-04 Liviu Chircu * [a51291f] : Merge branch 'rvlad-patrascu-allocator_rework' 2016-01-04 rvlad-patrascu * [826aff0] : fix memory debug hash function collisions caused by integer downcasting when adding line number 2016-01-02 Ovidiu Sas * [ab96254] : dispatcher: documentation updates for ds_reload - partition is accepted as an optional parameter 2015-12-29 Razvan Crainea * [446dd58] : function search: properly search through switch st 2015-12-28 Razvan Crainea * [b49d374] : httpd: properly treat select response 2015-12-23 ionutrazvanionita * [56c8714] : avpops: start without db_url if no func is using DB 2015-12-23 rvlad-patrascu * [dc01b2a] : q_malloc: don't abort on free(NULL) when debug is enabled 2015-12-23 rvlad-patrascu * [ec603ae] : small changes to debug output for q_malloc, f_malloc and hp_malloc * f_malloc: print debug info to memlog instead of debug * display free memory amount for out of memory errors 2015-12-23 ionutrazvanionita * [1e93226] : strip_body(): check if content-type exists 2015-12-22 Bogdan Andrei IANCU * [638b255] : Merge pull request #733 from ionutrazvanionita/cseq-fix fix validate_dialog cseq checking 2015-12-22 Bogdan Andrei IANCU * [0deaad3] : Merge pull request #735 from OpenSIPS/revert-661-lb_fixup_fix Revert "load balancer: fixup functions fixes" 2015-12-22 Bogdan Andrei IANCU * [3edf1f3] : Revert "load balancer: fixup functions fixes" 2015-12-22 Bogdan Andrei IANCU * [9dbfdfa] : Merge pull request #661 from shripchenko/lb_fixup_fix load balancer: fixup functions fixes 2015-12-22 rvlad-patrascu * [d4fd23a] : use the compile flag DBG_MALLOC to debug any memory allocator As a result, q_malloc is selected with the QM_MALLOC flag instead of DBG_QM_MALLOC. 2015-12-22 Bogdan Andrei IANCU * [420b663] : Merge pull request #717 from ionel-cerghit/shmem_rework Shmem rework 2015-12-22 ionutrazvanionita * [49efaa9] : comparse_uris: unescape all characters in user field 2015-12-22 ionutrazvanionita * [ef2232c] : fix validate_dialog cseq checking If prev_cseq from the dlg_leg is not set, check the r_cseq 2015-12-21 ionutrazvanionita * [4c1d619] : Usrloc: modify invalid contacts behaviour * stop inserting a contact even though received is OK (call parse_uri() for each contact) * at startup, if a bogus contact is found continue processing, letting the user know that he has to remove that contact from the database 2015-12-21 Razvan Crainea * [bee77b2] : b2b_logic: allow the initial setup to have a dummy SDP 2015-12-21 Vlad Paiu * [6110e92] : ReINVITE Pinging Improvements : - ensure compatibility with NAT scenarios and Topology Hiding, by extract Contact on the outgoing side - extract SDP on the outgoing side, in order to ensure compatibility with RTPProxy and various other SDP changes Work still in progress : - Properly handle late negociation between endpoints - Ensure SDP persistency ( DB and BIN replication ) 2015-12-20 rvlad-patrascu * [5a07ed1] : add memory status dump to f_malloc and hp_malloc * the memory dump contains a summary of allocated fragments * also correctly update the used memory statistics 2015-12-18 Cerghit Ionel * [33998ab] : sql_caher: remove pkg memory temporary list the caching entries are parsed and stored directly in shared memory fix delimiters default initialisation, now done before script parsing 2015-12-18 ionutrazvanionita * [d605032] : Sipcapture: store in the database without passing through script With this new feature it is possible to store the HEP message inside the database without going through the script,thus allowing you to use the script for processing valid messages, since the ones that are send to this node aren't intended for it. There have also been defined a return code from the hep callbacks(HEP_SCRIPT_SKIP) which allows the type of behaviour explained earlier. If at least one HEP callback sets this return code then passing through the script shall be skipped. 2015-12-18 rvlad-patrascu * [3d183e7] : q_malloc: change memory dump output to show summary of allocated fragments 2015-12-18 rvlad-patrascu * [ec7fb15] : menuconfig: add posibility to toggle options in Compile Flags menu In order to specify which options should be exclusive with each other, the DEFS lines in Makefile.conf should be preceded by a "#DEFS_GROUP_START" line and followed by a "#DEFS_GROUP_END" line. 2015-12-16 Ovidiu Sas * [78cc475] : uac_registrant: fix index into the reg_hash table - thanks to Razvan Crainea for spotting this out 2015-12-16 Ionut Ionita * [2117a62] : Merge pull request #727 from ionutrazvanionita/kill_bug Kill bug 2015-12-16 ionutrazvanionita * [cf53d0c] : wait for all processes to initialize before sending SIGTERM 2015-12-15 ionutrazvanionita * [d13db49] : gzip support for hepV3 2015-12-15 ionutrazvanionita * [3cb44df] : compression module:export gzip compress/decompress functions through an api 2015-12-14 Ovidiu Sas * [a4294ff] : Merge pull request #701 from nikbyte/master uac_registrant: avoid bulk re-registrations on large servers 2015-12-14 Nick Altmann * [e86bbfa] : uac_registrant: remove redundant loop 2015-12-14 Nick Altmann * [b69e56f] : Merge branch 'master' of github.com:OpenSIPS/opensips 2015-12-11 Cerghit Ionel * [d182f9d] : blacklists: removed preinit_blacklists function the vector of blacklists is now created when the first list is created:w 2015-12-09 Razvan Crainea * [20df836] : b2b_logic: set late SDP negociation flag For the entity that initiates a call, late SDP negociation is used, because the SDP of the first entity is not known. This patch sets the late_sdp flag for that entity, to flag the b2b logic to send the ACK with SDP. This bug was reported on the mailing list by Sebastian Sastre. 2015-12-08 Bogdan-Andrei Iancu * [bd050fd] : Fix DB key when updating the destination state. Beside SIP URI, use the SET ID too when performing FB updates to set the destination state. (a SIP URI may be present in multiple SET IDs) Closes #711 , reported by @mishehu 2015-12-08 ionutrazvanionita * [93061b1] : avoid using unallocated jiffies 2015-12-08 ionutrazvanionita * [e9f6e97] : fix proto selections issues * proto was set to PROTO_UDP in siptrace;now set it depending on the protocol used * proto_hep function pack_hep will receive the proto id in netinet form(IPPROTO_*) * siptrace transforms from netinet id (IPPROTO_*) to opensips-like id (PROTO_*) 2015-12-08 ionutrazvanionita * [6dd210a] : Merge branch 'ar45-fix_sqlite_delete_update_avp_query' 2015-12-08 ionutrazvanionita * [8be032b] : Merge branch 'fix_sqlite_delete_update_avp_query' of https://github.com/ar45/opensips into ar45-fix_sqlite_delete_update_avp_query Conflicts: modules/db_sqlite/dbase.c 2015-12-08 ionutrazvanionita * [9c43924] : correctly update markers when multiple messages read 2015-12-07 Răzvan Crainea * [4990532] : Merge pull request #718 from sippy/master Silently disable GLIBC-specific features if we are not on Linux (unbrear modules on FreeBSD) 2015-12-07 ionutrazvanionita * [1efdcc0] : case sensitive compare in sqlite raw query 2015-12-07 ionutrazvanionita * [222d511] : add str_strncasecmp function 2015-12-05 Maksym Sobolyev * [46df8f8] : Silently disable GLIBC-specific features if we are not on Linux. This just breaks modules build on the FreeBSD currently. 2015-12-04 Bogdan Andrei IANCU * [ab5cd61] : Merge pull request #681 from shiningstarj/dialoginfo-memory-leak Presence_dialoginfo: Fix memory leaks in notify_body.c 2015-12-04 Bogdan-Andrei Iancu * [e125726] : Return value for missing AVPS. Maintain database result integrity by supporting null column values. Closes #693 and #677 (cherry picked from commit 88bd36bd8b200bfde902f2bb28c2be8ea0fb18f7) 2015-12-04 Cerghit Ionel * [baddec0] : cfgutils: $shv pseudo-variables initized directly in shared memory since shared memory is now initialized before script parsing, the list containing the variables can be created directly in shared memery in order to simplify code 2015-12-04 Razvan Crainea * [b29d7aa] : ratelimit: always publish the previous counter Although this seems a "hack", without having a stable counter, pipes are never stable, and therefore the real limit can never be estimated. 2015-12-04 Razvan Crainea * [22187c0] : b2b_logic: parameters cannot be lower than 1 2015-12-04 ionutrazvanionita * [6a2954a] : sqlite raw query: search for select only as first word in query 2015-12-04 ionutrazvanionita * [4304777] : only exit, no error when no result given 2015-12-03 Bogdan Andrei IANCU * [671fa5c] : Merge pull request #667 from ionel-cerghit/kill_dialogs modules/dialog: add mi command to terminate all dialogs from a profile 2015-12-03 Bogdan Andrei IANCU * [aa91970] : Merge pull request #706 from ionel-cerghit/shmem_rework move init_shm before the script parsing 2015-12-03 ionutrazvanionita * [7913288] : properly handle epoll events 2015-12-03 Liviu Chircu * [938a88a] : Merge pull request #676 from rvlad-patrascu/sql_cacher SQL Cacher 2015-12-03 rvlad-patrascu * [e095586] : sql_cacher: update documentation with usage example 2015-12-03 Nick Altmann * [ec77280] : RPM spec: move ulimit to init-script 2015-12-03 Nick Altmann * [2b57ec6] : RPM spec: change folder name to more sensible 2015-12-03 Nick Altmann * [dce2c5b] : RPM spec: increase max files limit and add dependencies 2015-12-03 Razvan Crainea * [41168cd] : remove rest package 2015-12-03 rvlad-patrascu * [db74655] : sql_cacher: further improvements -if table is empty don't return error when loading entire table -improve error reporting for cache_table parameter -cache entire table if no columns are specified -don't allocate new array of columns for sql query function call -fix memory leaks in insert_in_cachedb() -add columns_delimiter module parameter -update README 2015-12-02 Cerghit Ionel * [3e56950] : blacklists.c: move package memory list initialisation in shared memory 2015-11-27 Bogdan-Andrei Iancu * [0dca32b] : Reduce the amount of "useless" DBG's 2015-11-27 ionutrazvanionita * [672cb13] : fix async: context shall be destroyed only at the last async call 2015-11-27 Bogdan-Andrei Iancu * [d6d3dd5] : Remove annoying LM_INFO 2015-11-27 Bogdan-Andrei Iancu * [52d5ef5] : Fix proper reporting for the statistics types. This fixes the charting of SHMEM related stats in Control Panel 2015-11-25 Bogdan-Andrei Iancu * [eccbf8d] : Fix bug in populating the PIDs 2015-11-25 Cerghit Ionel * [4089d44] : move init_shm before the script parsing shared memory is now initialized before script parsing removed uid and git script variables (can now be set only from commad line) removed pending statistics list from statistics.c as dynamic statistics can now be allocated directly in shared memory 2015-11-24 ionutrazvanionita * [47b5dc9] : fix dangling db conns bug * database are now started in child_init * avoid opening db conns in mod_init and leaving them in the db pool because when fork() shall be done all the processes will use the same db structure therefore the same tcp connection 2015-11-24 ionutrazvanionita * [4e93c5b] : fix Makefile.defs; optimization flags back to O9; set to O0 by commit 82c16b0 2015-11-24 ionutrazvanionita * [82c16b0] : code refactoring in dialplan module * fix memory leaks (partition parameters in pkg were not freed when structure was moved to shm) * avoid to do unnecessary allocations; use static strings received as modparam both in pkg and shm partition structure * improved code design * fix bug in add_rule2hash - when void string match expression received("") return error since it will segfault in core's hash function 2015-11-24 Nick Altmann * [e10e647] : uac_registrant: avoid bulk re-registrations on large servers with active using of reg_reload 2015-11-23 Ovidiu Sas * [889f353] : dialplan: check if memory is properly allocated when updateing partition records 2015-11-23 Ovidiu Sas * [bd35916] : dialplan: allocate the right amount of memory when creating a partition 2015-11-23 Nick Altmann * [0631965] : RPM spec: remove excess rest client package 2015-11-23 Ovidiu Sas * [1cf5dd8] : dialplan: memset to 0 newly alocated memory for dp partition - suggested by Liviu Chircu 2015-11-23 Ovidiu Sas * [fb3923d] : dialplan: after inserting a new partition, set the pointer to the next record to NULL 2015-11-23 Ovidiu Sas * [f0a75d9] : dialplan: enhance debug probe 2015-11-20 Răzvan Crainea * [af5db9e] : Merge pull request #700 from ionel-cerghit/logging_pvars add $cfg_file, $cfg_line pvars 2015-11-20 Cerghit Ionel * [1acea06] : add $cfg_file, $cfg_line pvars this pseudovariables store the current script line and file being executed, useful for logging purposes 2015-11-19 Razvan Crainea * [18f5da0] : acc:evi: if the request failed send a missed event In case the acc_evi_request() function was used on a failed request (code < 300), send a missed event rather than the normal one. Reported by Trevor Francis from 46Labs 2015-11-18 Razvan Crainea * [1a41bb1] : Makefile: use flex -B flag for versions lower than 2.5.6 Reported by Dragomir Haralambiev 2015-11-17 Razvan Crainea * [8268bf8] : Makefile: fix isatty() error + NICER output 2015-11-17 Razvan Crainea * [2debeb8] : Makefile remove white spaces 2015-11-17 Razvan Crainea * [19edf61] : fix warnings reported by Ovidiu Sas 2015-11-17 Razvan Crainea * [7754003] : clusterer: remove exhaustive warnings Also re-arrange the code to be properly used 2015-11-17 Razvan Crainea * [d583f0e] : clusterer: use proper states for machines 2015-11-17 Razvan Crainea * [6816bad] : clusterer: convert WARN to DEBUG Reported by Trevor Francis from 46Labs 2015-11-13 ionutrazvanionita * [c4ebfc8] : check if startup_done var is allocated before trying to set it 2015-11-13 ionutrazvanionita * [4fdb55b] : fix str2int and str2sint input params check * if a void string was given to the function ("") 0 value was returned which was not correct * input values should be checked for NULL 2015-11-13 ionutrazvanionita * [8a51273] : db in string or int pvar for avp_db_query() 2015-11-13 Razvan Crainea * [39a7231] : clusterer: add version in db schema Thanks go to Trevor Francis from 46Labs for reporting this Closes #697 2015-11-12 Liviu Chircu * [6baba32] : dialplan: improve startup error reporting Reported by Jeff Pyle 2015-11-12 rvlad-patrascu * [f897e89] : sql_cacher: small improvements -improved coding style -more verbose error messages -trim whitespaces for cache entry specification -fix memory leak: free temporary cache entries list used for parsing 2015-11-12 Razvan Crainea * [c70f765] : carrierroute: fix uninitialized warning 2015-11-12 Razvan Crainea * [54657ba] : emergency: fix write signature warning 2015-11-11 Razvan Crainea * [5519d76] : proto_hep: fixed fixed-aliasing warning 2015-11-11 Razvan Crainea * [e9d2cf4] : tls: re-arrange code to be usable 2015-11-11 Razvan Crainea * [e761f7e] : remove hep T8 redefinition 2015-11-11 ionutrazvanionita * [d666051] : adapt siptrace module to new proto_hep module 2015-11-11 ionutrazvanionita * [331e48d] : adapt sipcapture module to new proto_hep module 2015-11-11 ionutrazvanionita * [acabbbd] : introducing proto_hep module; supports both hepv1/2 & hepv3 2015-11-06 Razvan Crainea * [5286558] : dialog: increase ref when setting dialog 2015-11-05 Razvan Crainea * [d69d085] : tm: free async context when resuming This commit fixes context_alloc memory leak 2015-11-04 Aron Podrigal * [1882e6b] : Fixed sqlite avp_db_query for 'UPDATE and DELETE statements 2015-11-03 Razvan Crainea * [3f3b6fd] : async: store cancelled and e2eack transactions in async ctx This fix prevents transactions from being unrefferenced when dangling transaction pointers remain stored in static variables. 2015-11-03 Razvan Crainea * [f9eac69] : topo_hiding: replace LM_INFO with LM_DBG for debugging logs 2015-11-03 Bogdan-Andrei Iancu * [b13214d] : Fixed Post Script callbacks triggering in Async. If the async engine in TM detects a SIP retransmission, break script and run the Post Script callbacks, othewise the transaction will stay ref'ed for ever. Credits for spotting this problem go to Razvan Crainea. (cherry picked from commit 6735c276ba9c66107759fd883da6d7f6301a50f5) 2015-11-02 ionutrazvanionita * [5f3981c] : fix mem leak in permissions 2015-10-30 Razvan Crainea * [3c1ffb1] : tls_mgm: CRL support is only available starting with openssl 1.0.0 2015-10-29 Bogdan-Andrei Iancu * [d0f95a8] : Replace tlsops with tls_mgm 2015-10-29 Razvan Crainea * [c18c8ea] : properly handle async functions when reactor cannot fullfil the request Credits go to 46labs for debugging this 2015-10-29 Razvan Crainea * [28bcadf] : run destroy context when the message is sent Credits go to 46Labs for discovering and providing test scenarios 2015-10-28 Liviu Chircu * [48b88bd] : Fix compiler warnings with NO_DEBUG, NO_LOG Also add a handy macro for future usage 2015-10-28 Razvan Crainea * [a5b195e] : mem: fix c11 inline problems 2015-10-27 Liviu Chircu * [cb5eebe] : TCP aliases: Fix more race conditions (issue #589) _tcpconn_find() returns a pointer which should never be used outside of TCP lock. This patch completes commit 22f4c1a2e05 by fixing two additional issues of the same type in tcpconn_add_alias() 2015-10-27 Liviu Chircu * [3e1a1a7] : Fix potential memory corruption on TCP write errors This patch fixes a well-hidden TCP connection referencing bug which would only reveal itself upon running into failed TCP send() operations due to various reasons (e.g. full send buffer) in high-traffic environments. Credits to Hieu Ta and Don Steul from Jibe Mobile for detailed reporting and providing an adequate replication environment 2015-10-26 Razvan Crainea * [a76ab7a] : context: add destroy function for registered ctx vars (cherry picked from commit 59708d51e1796c0c7e8d7b975af964fc8c9a54e2) 2015-10-26 Razvan Crainea * [1785ab3] : fix context reusage if async is made This affects timers and failure routes that are supposed to reuse a static context (cherry picked from commit d75a0a01a87b8260dc220e1de98c2be20b7755f0) 2015-10-22 shiningstarj * [3f98620] : Remove unnecessary fix One issue was with a different branch, this now resolves the only actual leak 2015-10-22 shiningstarj * [182959a] : Fix memory leaks in notify_body.c 2015-10-22 Vlad Paiu * [27b0b21] : Fixed ACKs CSEQ number when doing in-dialog pinging The ACK CSEQ number does not depend on the number of pings or other in-dialog requests since the INVITE transaction, it must be the same as the INVITE Closes issue #680 (cherry picked from commit 26a0a62621f6d1e4ea9fee90684a143fbfdc8494) 2015-10-21 ionutrazvanionita * [bd11680] : wb_timer on DB_ONLY:don't update NULL stats;alloc contact id buffer 2015-10-20 Liviu Chircu * [35451d5] : Merge pull request #678 from was4444/master fix: some sh(busybox) do not support syntax of $(< file) 2015-10-20 wxf * [ffb2554] : fix: some sh(busybox) do not support syntax of `$(< file)' 2015-10-16 ionutrazvanionita * [0a826a9] : update registrar: now certain AOR, contact can be searched; easier to understand the api three new functions added: * is_registered - whether or not an AOR is inside the usrloc table * is_contact_registered - whether or not a contact and/or a callid is inside the location table * is_ip_registered - whether or not an ip from an avp/pvar is registered or not two old functions are now deprecated: * is_other_contact - replaced with is_ip_registered + all pvar support * registered - replaced with is_contact_registered 2015-10-16 ionutrazvanionita * [3d91beb] : perl_exec(): accept variable as parameter 2015-10-15 Cerghit Ionel * [3e1dc7d] : profile_end_dlgs: doesn't return error message if profile is empty 2015-10-14 rvlad-patrascu * [fbccbfd] : add Makefile 2015-10-14 Vlad Paiu * [c0f25f7] : Added Re-INVITE in-dialog pinging support Controlled via the new "R" and "r" flags available to create_dialog() as well as the new reinvite_ping_interval module param Work still in progress : - Properly handle late negociation between endpoints - Ensure SDP persistency ( DB and BIN replication ) - Ensure compatibility with topology hiding ( currently the Contact header will be bogus when doing TH ) - Whitelist or blacklist logic ( terminate call for 481 and 408 timeout, or terminate call for anything else other than 200 and 491 ) - Extensive testing needed for race conditions specified in rfc 5407 2015-10-09 ionutrazvanionita * [db77978] : fix acc: when doing no-cdr accounting, do not use CDR-generated info 2015-10-09 rvlad-patrascu * [238d108] : sql_cacher: add documentation 2015-10-09 ionutrazvanionita * [453ad33] : set event only on acc_evi_request 2015-10-09 ionutrazvanionita * [c0f1507] : fix acc_*_request: determine the event based on flags * cdr flag and acc - cdr logging (E_ACC_CDR for events) * only acc flag - normal logging (E_ACC_EVENT for events) * missed calls flag - missed calls logging (E_ACC_MISSED event for events) The order is as stated so if you set both missed calls and acc flag, only acc logging shall be done 2015-10-08 ionutrazvanionita * [10ada03] : fix dispatcher compiler warning 2015-10-08 rvlad-patrascu * [2646f0a] : add MI reload function 2015-10-07 Cerghit Ionel * [b219273] : modules/dialog: fixed possible memory leak from profile_end_dlgs 2015-10-07 Cerghit Ionel * [ef1b675] : modules/dialog: add mi command to terminate all dialogs from a profile added the the profile_end_dlgs command 2015-10-06 Razvan Crainea * [ec25c07] : ratelimit: avoid deadlock if counter is not updated 2015-10-06 Vlad Paiu * [7ecb192] : Added millisecond precision of the call duration to the CDR accounting 2015-10-06 Di-Shi Sun * [c7e1eab] : Updated to support CNAM diversion use case. 2015-10-05 ionutrazvanionita * [db25690] : fix compression module bugs * proper error messages on compress * correct buffer size aproximation on gzip compress(10% + 12) * fix bug on decompression 2015-10-02 Răzvan Crainea * [05c4d39] : Merge pull request #659 from ionel-cerghit/extra_mem_statistics add configurable statistics about shared memory usage 2015-10-02 Walter Doekes * [7849b7f] : dialog: Document that unset_dlg_profile can be used from request_route. This has been possible since 23f2322d in 1.8 and higher. 2015-10-02 ionutrazvanionita * [165d833] : fix failover default value when M max results not specified in ds_select_dst/domain 2015-10-01 Sergey Khripchenko * [35be9c9] : properly handle wrong params count in fixup functions 2015-10-01 Sergey KHripchenko * [0a347bd] : Merge pull request #2 from OpenSIPS/master Merge upstream OpenSIPS/master to fork 2015-10-01 Cerghit Ionel * [d7ecb73] : add configurable statistics about shared memory usage groups of modules can be defined in the .cfg file that would lead to statistics being generated with the number of fragments, used memory and real-used memory by those modules. The global fragments statistics now show the nuber of fragments alocated and in use.(for q_malloc, f_malloc) Please enter the commit message for your changes. Lines starting Please enter the commit message for your changes. Lines starting 2015-09-30 Bogdan Andrei IANCU * [98dccc5] : Merge pull request #654 from eseanucristian/tls-rework Tls rework 2015-09-30 Bogdan Andrei IANCU * [5a07ba0] : Merge pull request #658 from eseanucristian/clusterer-schema clusterer: fix readme 2015-09-30 Eseanu Marius Cristian * [c5b3fe3] : tls_mgm: remove unnecessary api functions 2015-09-30 Eseanu Marius Cristian * [47243b3] : clusterer: fix readme 2015-09-30 Eseanu Marius Cristian * [ff5ab68] : tls: fix locking issues 2015-09-30 Eseanu Marius Cristian * [b419b56] : config.h: TLS_PKEY_FILE and TLS_CERT_FILE have different values 2015-09-30 Eseanu Marius Cristian * [571fd70] : tls_mgm: add database schema 2015-09-30 ionutrazvanionita * [db84363] : on DB error: avoid double free;release only in destroy function 2015-09-30 Eseanu Marius Cristian * [a19b772] : tls_mgm: remove unnecessary DBG messages 2015-09-30 Eseanu Marius Cristian * [1d5dacc] : tls_mgm: can not change domain attributes from the script when the db mode is enabled 2015-09-30 ionutrazvanionita * [b4ffc0d] : fix possible non '\0' terminated table name bug 2015-09-29 ionutrazvanionita * [6ed6cd7] : memset all connection memory to 0 to avoid unwanted values 2015-09-29 Eseanu Marius Cristian * [2012468] : tls_mgm: update readme 2015-09-28 Ionut Ionita * [4488d95] : Merge pull request #653 from wdoekes/wjd-exec_admindoc_safety exec: Clarify what not to do with exec(). 2015-09-27 Walter Doekes * [1051075] : exec: Clarify what not to do with exec(). See: https://github.com/OpenSIPS/opensips/pull/375#discussion-diff-19524753 2015-09-25 rvlad-patrascu * [0f8540e] : add reload version int in cachedb for full caching reload 2015-09-25 Eseanu Marius Cristian * [23f767f] : tls_mgm: updated readme 2015-09-25 Eseanu Marius Cristian * [6826357] : add db support for tls 2015-09-24 Bogdan Andrei IANCU * [1e1ea14] : Merge pull request #648 from dsanders11/patch-3 Dispatcher module should honor db_default_url 2015-09-24 Bogdan Andrei IANCU * [d6cbbff] : Merge pull request #649 from dsanders11/patch-4 Dialplan module should honor db_default_url 2015-09-24 Bogdan-Andrei Iancu * [cdc5803] : Check if at least one DB URL is provided in avpops Reported by David Sanders (@dsanders11). Closes #644 . (cherry picked from commit 34fab0d51ffb473b25871eddd3acef88664c4ec4) 2015-09-24 David Sanders * [566d4db] : Dialplan module should honor db_default_url 2015-09-24 David Sanders * [684846d] : Dispatcher module should honor db_default_url 2015-09-23 Bogdan-Andrei Iancu * [16019d3] : Fix usage of t_reply_with_body() with reply lumps. Force pushing all reply lumps (headers added to the reply) when using t_reply_with_body() - the function takes the msg from the transaction, not the real msg; so we need to be sure that the msg in transaction was fully cloned. Reported by Giuseppe Cardone on mailing list. (cherry picked from commit 783a18a8df4b8e9dcfd147ec0c63caceeeaa7bb9) 2015-09-22 rvlad-patrascu * [f59d4bb] : improve on demand loading 2015-09-22 ionutrazvanionita * [8a745d9] : uri module: remove parameter with given key script function 2015-09-22 ionutrazvanionita * [fa854c4] : no need to set proto since this is determined in parse_proto function 2015-09-22 Bogdan Andrei IANCU * [9874ff1] : Merge pull request #627 from eseanucristian/master Clusterer readme 2015-09-21 Bogdan-Andrei Iancu * [a6a896f] : Fix auth via B2B module. Remove the bogus hooks placed by TM in the outbound SIP buffer (as the buffer may change in local route) and rebuild the B2B invite (with credentials) from the initial sent invite. Reported by @DMOsipov and @xxReaper on GITHUB. Closing #577 and #628 . 2015-09-21 Eseanu Marius Cristian * [70c4701] : usrloc: updated readme 2015-09-21 Eseanu Marius Cristian * [23d0a4e] : ratelimit: updated readme 2015-09-21 Eseanu Marius Cristian * [09de554] : dialog: updated readme 2015-09-18 ionutrazvanionita * [6a565b8] : also check input parameter for exec_async 2015-09-18 ionutrazvanionita * [8f5aab6] : correctly check input parameter in exec_sync; close pipe with pclose to avoid leaving unfreed structs in kernel; correctly handle errors in exec_syc 2015-09-17 rvlad-patrascu * [604b67a] : add per process db handlers list 2015-09-16 rvlad-patrascu * [5012365] : sql_cacher: immplement on demand caching 2015-09-16 Razvan Crainea * [72926c2] : Merge branch 'tls-rework' 2015-09-16 Răzvan Crainea * [67143b1] : Merge pull request #636 from mgwilliams/debian_init add missing space in debian/opensips.init 2015-09-16 Matthew Williams * [fd11c7d] : add missing space in debian/opensips.init 2015-09-15 Razvan Crainea * [878f79e] : tls-modules: update readme 2015-09-15 Razvan Crainea * [5ff322d] : Makefile-readme: allow different xsl-stylesheets versions 2015-09-14 Razvan Crainea * [bbc2c63] : Merge branch 'saghul-xcap-doc-size' 2015-09-14 Razvan Crainea * [70a587f] : dbschema: re-generate SQL scripts 2015-09-14 Razvan Crainea * [00489e0] : ssl cert: remove passphrase 2015-09-14 Saúl Ibarra Corretgé * [d419f05] : xcap: make the 'doc' column a LONGBLOB on MySQL A normal-sized XCAP document with external references easily exceedes the default column size, leading to all sorts of problems. 2015-09-14 Razvan Crainea * [a9486e5] : tls: remove unnecessary TLS init 2015-09-14 Eseanu Marius Cristian * [d71e8f2] : tls_mgm: update dependency list; modify mod_init 2015-09-14 Razvan Crainea * [aa7429e] : regex: release the memory for unused pattern strings Thanks go to Federico Edorna for reporting and giving the solution for this memory leak. 2015-09-11 Razvan Crainea * [0faac6a] : Revert "Merge pull request #629 from eseanucristian/tls_rework" This reverts commit 4a807466714b7228f074e46e0bc5e5669f02d4ee, reversing changes made to 579a82b7977e2ee8115048a255dfbdfd0aeb2676. 2015-09-11 Razvan Crainea * [5da100b] : emergency: fix compile warning 2015-09-11 Eseanu Marius Cristian * [b9b4aab] : tls_mgm: added readme 2015-09-11 Eseanu Marius Cristian * [ace2d5f] : proto_tls: readme updated 2015-09-11 Eseanu Marius Cristian * [b2b25a1] : tls_mgm: fixed exports; improved find_server_domain api function 2015-09-11 Eseanu Marius Cristian * [35343cd] : proto_tls: added tls_mgm dependency; simplified tls_conn_init 2015-09-11 root * [3a80eb4] : emergency -- last test cases 2015-09-11 Razvan Crainea * [1d411bb] : dialog: return 0 profile size if no value in cache Reported by Damien Sanders Closes #562 2015-09-11 Razvan Crainea * [6bf0cff] : packaging: fix to properly build on jessie 2015-09-11 Vlad Paiu * [2f6abab] : Fixed increasing the CSEQ for in-dialog generated message when uac_auth is used in combination with the dialog module (cherry picked from commit d9d254a2fe93700748dd4cc5268409647a209e7f) 2015-09-11 Eseanu Marius Cristian * [7a32301] : proto_tls: new module tls_mgm 2015-09-11 Ovidiu Sas * [fbc2f78] : nathelper: fix some -Wformat compiler warnings 2015-09-11 ionutrazvanionita * [0038231] : fix possible leak cause by sqlite3_closev2; easier to understand error message on sqlite3_column_metadata 2015-09-11 Bogdan-Andrei Iancu * [752dfc2] : Fix callid for "ul_add" MI command. As now the callid is updatable, we need to provide a callid string. Reported by Stas Kobzar (cherry picked from commit 02e886f34ba0a6a147ef4fda0035fb1179bbf835) 2015-09-11 Di-Shi Sun * [9fad08f] : 1. Updated to support reporting media addresses, proxy local addresses and provider post dial delay. 2. Updated to support CNAM. 2015-09-11 Liviu Chircu * [1586af2] : Fix `make menuconfig` compile warnings 2015-09-11 Liviu Chircu * [440b44f] : DBG_LOCK compile flag: Add menuconfig help line 2015-09-11 ionutrazvanionita * [633f6b6] : added hex2int 64 bit versions 2015-09-11 ionutrazvanionita * [d6d5b83] : fix unique label in nathelper 2015-09-11 ionutrazvanionita * [b8cfd17] : stateful ping for nathelper 2015-09-11 Saúl Ibarra Corretgé * [b0f65d1] : build: fix setting LIBDIR correctly 2015-09-11 Saúl Ibarra Corretgé * [c8ecd8c] : build: fix setting HOST_ARCH on Solaris 2015-09-11 Bogdan Andrei IANCU * [0bbb099] : Merge pull request #588 from jalung/patch-4 Update nathelper.c (cherry picked from commit 079de59850d9b9e199c5ff5285ba11600a8d2faf) 2015-09-11 Razvan Crainea * [2b5aeb0] : perl: Makefile NICER implementation 2015-09-11 Cerghit Ionel * [6af5a11] : lock_ops.h: fixed typo for SYSV semaphores 2015-09-11 Razvan Crainea * [62d4ace] : copyright: remove duplicate license 2015-09-11 Razvan Crainea * [9df20a3] : travis: run in containers 2015-09-11 Razvan Crainea * [7235629] : copyright: remove flex generated files 2015-09-11 Bogdan-Andrei Iancu * [cb18eb6] : Re-try the writing on EAGAIN. As the UDP sockets were moved to non blocking, sendto() may generate EAGAIN if the prev write operation was not yet completed by the stack. 2015-09-11 Cerghit Ionel * [c1c277a] : lock_ops.h: added support for debugging locks with the flag DBG_LOCK set(from menuconfig for example), with each lock will be stored information regarding the place from where the lock was aquired (file, function, line) 2015-09-11 Steve Frécinaux * [9144672] : Debian: add package opensips-rest-module This package contains the rest_client.so module. 2015-09-11 Steve Frécinaux * [ee9dfb5] : Debian: add systemd unit file. This unit file supports the configuration check before restart and retains the P_MEMORY and S_MEMORY variable usage. Since both the systemd configuration and the init script take care of creating /var/run/opensips, the opensips.dirs file has been removed. 2015-09-11 Steve Frécinaux * [185c15d] : Debian: update dependencies to build on Jessie. Unfortunately, the lua module doesn't build anymore because it depends on libmemcache, which is not present in Jessie. The other memcached-related modules depend on libmemcached, which is fine. See https://github.com/OpenSIPS/opensips/issues/580 2015-09-11 Steve Frécinaux * [eeeadd3] : Debian: do not make opensips-module-mysql depend on mysql-client This makes it impossible to use opensips with mariadb. 2015-09-11 Steve Frécinaux * [c86c386] : Debian: make the init script use LSB functions. This is more consistent with the other init scripts, and most importantly fixes the behaviour of the init script when it comes to systemd-enabled systems. 2015-09-11 Ryan Bullock * [85c42c2] : Remove use of deprecated synchronous mode in couchbase 2015-09-10 Sergey Khripchenko * [39e34d4] : * allow alias_db_find() to be called from startup route (f.e. to pre-potulate dbtext cache for a table) * allow alias_db_find(,input,) parameter to be strng or PV or mix of them instead of just PV 2015-09-10 Sergey KHripchenko * [505fcb2] : Merge pull request #1 from OpenSIPS/master Update fork to origin 2015-09-10 ionutrazvanionita * [bf1d089] : sqlite: store int values as bigint, but set their type to int 2015-09-10 ionutrazvanionita * [97491fc] : fix nathelper flag check 2015-09-10 ionutrazvanionita * [ab0e7c4] : fix sqlite bug: autoincrement check skipped on raw queries 2015-09-09 ionutrazvanionita * [3df19e5] : Revert "retrieve table name from select queries on raw_query in order to use sqlite library functions later" This reverts commit 779996d9b37ad11f01420961ac708c6daa4d2828. 2015-09-09 ionutrazvanionita * [a5e4a71] : Revert "sqlite raw query: fix table name parsing" This reverts commit 46c2f42a5f28dd8c8d29c2a73d352b51e04e0938. 2015-09-09 ionutrazvanionita * [46c2f42] : sqlite raw query: fix table name parsing 2015-09-09 ionutrazvanionita * [779996d] : retrieve table name from select queries on raw_query in order to use sqlite library functions later 2015-09-09 Razvan Crainea * [3d7c6c2] : emergency: fix compile warning 2015-09-09 Stas Kobzar * [ec8ff54] : Fixing bug in debian init script. 2015-09-08 Răzvan Crainea * [4a80746] : Merge pull request #629 from eseanucristian/tls_rework Tls rework 2015-09-08 Eseanu Marius Cristian * [b2ac11f] : tls_mgm: added readme 2015-09-08 Eseanu Marius Cristian * [2f59c7a] : proto_tls: readme updated 2015-09-08 Eseanu Marius Cristian * [5919f27] : tls_mgm: fixed exports; improved find_server_domain api function 2015-09-08 Eseanu Marius Cristian * [0579d43] : proto_tls: added tls_mgm dependency; simplified tls_conn_init 2015-09-08 root * [579a82b] : emergency -- last test cases 2015-09-07 rvlad-patrascu * [4cc712a] : add function to get value from cachedb; full caching completed 2015-09-04 Razvan Crainea * [7781342] : dialog: return 0 profile size if no value in cache Reported by Damien Sanders Closes #562 2015-09-04 vladpaiu * [1cb5a2e] : Merge pull request #560 from rrb3942/master Remove use of deprecated synchronous mode in couchbase 2015-09-04 Razvan Crainea * [230d97a] : packaging: fix to properly build on jessie 2015-09-04 Razvan Crainea * [65bfc1a] : Merge branch 'BeIP-debian-jessie' 2015-09-04 Razvan Crainea * [cda3986] : Merge branch 'debian-jessie' of https://github.com/BeIP/opensips into BeIP-debian-jessie 2015-09-04 Vlad Paiu * [086fe7c] : Fixed increasing the CSEQ for in-dialog generated message when uac_auth is used in combination with the dialog module (cherry picked from commit d9d254a2fe93700748dd4cc5268409647a209e7f) 2015-09-04 Eseanu Marius Cristian * [40f5841] : proto_tls: new module tls_mgm 2015-09-03 Razvan Crainea * [f36a8a1] : add secure websocket support in parser 2015-09-03 Razvan Crainea * [a889f1e] : proto_ws: refactor code Split the connection specific logic from the WS implementation 2015-09-02 Ovidiu Sas * [28a7b2b] : nathelper: fix some -Wformat compiler warnings 2015-09-02 ionutrazvanionita * [eac7b82] : fix possible leak cause by sqlite3_closev2; easier to understand error message on sqlite3_column_metadata 2015-09-02 Razvan Crainea * [a2e73ba] : fix WS SRV scheme according to the draft 2015-09-02 rvlad-patrascu * [6c22820] : remove duplicate code by using macro in parse_cache_entries function 2015-09-02 rvlad-patrascu * [e706558] : sql_cacher: add sql_cached_value pseudo-variable; parse pvar name and optimize for next pvar get 2015-09-02 Razvan Crainea * [77f77e3] : proto_tls: split tls init in different header 2015-09-01 Bogdan-Andrei Iancu * [3191ed2] : Fix callid for "ul_add" MI command. As now the callid is updatable, we need to provide a callid string. Reported by Stas Kobzar (cherry picked from commit 02e886f34ba0a6a147ef4fda0035fb1179bbf835) 2015-09-01 Di-Shi Sun * [0fc5816] : 1. Updated to support reporting media addresses, proxy local addresses and provider post dial delay. 2. Updated to support CNAM. 2015-08-31 Liviu Chircu * [b9fe3c2] : Fix `make menuconfig` compile warnings 2015-08-31 Liviu Chircu * [4bf31be] : DBG_LOCK compile flag: Add menuconfig help line 2015-08-31 ionutrazvanionita * [d6129b6] : added hex2int 64 bit versions 2015-08-31 ionutrazvanionita * [56d17be] : fix unique label in nathelper 2015-08-31 ionutrazvanionita * [a1992cd] : stateful ping for nathelper 2015-08-31 Liviu Chircu * [89d06e8] : Merge pull request #621 from ionel-cerghit/locking-debug lock_ops.h: added support for debugging locks 2015-08-29 Bogdan Andrei IANCU * [b4d60f7] : Merge pull request #626 from saghul/fix-libdir Fix setting LIBDIR 2015-08-28 Saúl Ibarra Corretgé * [3d485cd] : build: fix setting LIBDIR correctly 2015-08-28 Saúl Ibarra Corretgé * [63bccb4] : build: fix setting HOST_ARCH on Solaris 2015-08-28 Bogdan Andrei IANCU * [f0fb16f] : Merge pull request #588 from jalung/patch-4 Update nathelper.c (cherry picked from commit 079de59850d9b9e199c5ff5285ba11600a8d2faf) 2015-08-28 Razvan Crainea * [7ab18e2] : perl: Makefile NICER implementation 2015-08-28 Cerghit Ionel * [0d939a2] : lock_ops.h: fixed typo for SYSV semaphores 2015-08-28 Razvan Crainea * [7d6cc39] : copyright: remove duplicate license 2015-08-28 Razvan Crainea * [e11409d] : travis: run in containers 2015-08-27 Razvan Crainea * [b8fbf7f] : copyright: remove flex generated files 2015-08-27 Bogdan-Andrei Iancu * [d3aaf44] : Re-try the writing on EAGAIN. As the UDP sockets were moved to non blocking, sendto() may generate EAGAIN if the prev write operation was not yet completed by the stack. 2015-08-27 Cerghit Ionel * [a5e23d8] : lock_ops.h: added support for debugging locks with the flag DBG_LOCK set(from menuconfig for example), with each lock will be stored information regarding the place from where the lock was aquired (file, function, line) 2015-08-27 Razvan Crainea * [5e19a94] : travis: rename liblua5.1-dev to liblua5.1-0-dev 2015-08-27 Razvan Crainea * [1fac483] : travis: switch to legacy until dependencies are added upstream 2015-08-27 Bogdan-Andrei Iancu * [3cb77f9] : Fix warning of printing uint64_t variables. 2015-08-27 Bogdan Andrei IANCU * [491e38c] : Merge pull request #620 from eseanucristian/clusterer-schema Clusterer dependencies 2015-08-27 Eseanu Marius Cristian * [a9a8312] : generated sql scripts for clusterer module 2015-08-27 Eseanu Marius Cristian * [98c0533] : added default database schema for clusterer module 2015-08-27 Eseanu Marius Cristian * [f614272] : ratelimit: added clusterer dependencies 2015-08-27 Eseanu Marius Cristian * [73f5fd3] : dialog: added clusterer dependencies 2015-08-27 Eseanu Marius Cristian * [1fcb875] : usrloc: added clusterer dependencies 2015-08-27 Razvan Crainea * [489c300] : travis: use FASTER compile time 2015-08-27 Razvan Crainea * [a047da9] : Revert "travis: install dependencies in a restricted way" This reverts commit f5850238d4f1e7d09c9e803480284aa94827ed8f. 2015-08-27 Razvan Crainea * [f585023] : travis: install dependencies in a restricted way 2015-08-27 Razvan Crainea * [4a240df] : travis: use container-based infrastructure 2015-08-26 Eseanu Marius Cristian * [95d30de] : clusterer: changed default name for a column 2015-08-26 Bogdan-Andrei Iancu * [db65681] : Fix migration of location table to BIGINT index. DBTEXT accepts autoincrement for BIGINT columns too. Closes #617 Reported by @arovetto 2015-08-25 Eseanu Marius Cristian * [d12e877] : clusterer: added dependencies 2015-08-25 Liviu Chircu * [82c3fd6] : -c and -C binary args: Properly validate module dependencies Along with script syntax validation, the "-c" and "-C" binary arguments should also ensure all necessary modules are loaded. Reported by Maxim Sobolev Fixes #616 2015-08-25 Razvan Crainea * [aad2e1c] : rtpproxy: dialog is not a mandatory dependency 2015-08-25 Bogdan-Andrei Iancu * [134dd72] : Merge branch 'eseanucristian-clusterer' 2015-08-25 Bogdan-Andrei Iancu * [e0493bf] : Merge branch 'clusterer' of https://github.com/eseanucristian/opensips into eseanucristian-clusterer Conflicts: modules/ratelimit/ratelimit_helper.c 2015-08-25 Răzvan Crainea * [6b265e8] : Merge pull request #613 from chiforbogdan/tls-crl-and-serial Tls crl and serial 2015-08-25 Bogdan Andrei IANCU * [21a07ba] : Merge pull request #615 from sippy/master Fix build on FreeBSD. 2015-08-25 Răzvan Crainea * [c0a0f12] : Merge pull request #614 from sippy/2.1 Fix build on FreeBSD (2.1 branch) (cherry picked from commit 76a0a56347cfc77775e37fe9944de1fd8c37798f) 2015-08-24 Maksym Sobolyev * [e348489] : Fix build on FreeBSD. 2015-08-24 Bogdan Chifor * [d532f41] : Extract serial number from certificate subject (TLS module) Serial number field from the certificate subject was extracted and exposed in a script variable. Even though this certificate DN field in quite unusual it could be used for a granular client authorization after the TLS process. The following script variables were added: tls_peer_subject_serial and tls_my_subject_serial. The variable tls_peer_subject_serial contains the client certificate subject serial number and tls_my_subject_serial contains the server certificate subject serial number. In the following example is presented a certificate CN field which has a serial number: CN=opensips_user/serialNumber=129/emailAddress=opensips_user@opensips.com 2015-08-24 Bogdan Chifor * [5503634] : Add CRL (Certificate Revocation List) verification for TLS CRL verification was added in the proto tls module. By adding this feature revoked client certificates can be detected during the TLS process, thus permitting a robust security mechanism. The following parameters where added to the proto tls module in the configuration script: crl_dir and crl_check_all. The crl_dir parameter specifies the directory which contains the CRL files (multiple CRL files can be added). The crl_check_all parameter must be 0 or 1 and specifies whether all the certificates from the chain are verified against a CRL or not. By default, CRL verification is enabled only for client certificates (or when crl_check_all is 0). If crl_check_all is 1 then the issuer (chain) certificates are also verified against the given CRL files. 2015-08-24 Bogdan-Andrei Iancu * [019494b] : Removed useless 'mi_xmlrpc_ng' prefix in param names 2015-08-24 Bogdan Andrei IANCU * [e7d27b8] : Merge pull request #611 from ionel-cerghit/mi_xmlrpc_ng_format mi_xmlrpc_ng: updated documentation 2015-08-24 Cerghit Ionel * [021b18a] : mi_xmlrpc_ng: updated documentation the documentation now offers information about the module parameter used to switch between the output formating 2015-08-24 Bogdan Andrei IANCU * [33ab7b2] : Merge pull request #610 from shripchenko/lb_fl_random A new flag for LB module lb_start() function to pick a rand()'om destinations with equal load 2015-08-24 Bogdan Andrei IANCU * [30d5f9c] : Merge pull request #609 from shripchenko/dbtext_dbt_reload New MI command dbt_reload - causes db_text module to reload cached tables from disk. 2015-08-24 Bogdan Andrei IANCU * [9ee1c23] : Merge pull request #608 from shripchenko/dbtext_bugs two bugs fixed in db_text module 2015-08-24 Sergey Khripchenko * [e904af7] : + documentation of lb_start() 's' flag 2015-08-24 Sergey Khripchenko * [3d00c09] : A new flag for LB module lb_start() function to pick a rand()'om destinations with equal load, if multiple destinations selected. This could help to offload an excessive load from first destination and distribute load in situations when calls duration is near to zero (like failed calls) and counts in resources are almost always zero and does not reflect an actual calls flow. Already discussed in PR: OpenSIPS/opensips#345 2015-08-24 Sergey Khripchenko * [8cfe55f] : New MI command dbt_reload - causes db_text module to reload cached tables from disk. Depending on parameters it could be a whole cache or a specified database or a single table. If any table cannot be reloaded from disk - the old version preserved and error reported. 2015-08-24 Eseanu Marius Cristian * [69cc93e] : dialog: readme - added necessary parameters to use clusterer api 2015-08-24 Eseanu Marius Cristian * [33603b1] : clusterer: changed data structure 2015-08-24 Eseanu Marius Cristian * [745d362] : clusterer: readme 2015-08-24 Eseanu Marius Cristian * [c52c4e9] : ratelimit: fixed segmentation fault when server_timeout is received 2015-08-24 Eseanu Marius Cristian * [3db517b] : dialog: added auth_check and timeout parameters 2015-08-24 Eseanu Marius Cristian * [e01751d] : ratelimit: added auth_check and timeout parameters 2015-08-24 Eseanu Marius Cristian * [2dd5b4c] : usrloc: improved clusterer api usage for replication 2015-08-24 Eseanu Marius Cristian * [778fe7d] : ratelimit: improved clusterer api usagefor replication 2015-08-24 Eseanu Marius Cristian * [156096b] : dialog: improved clusterer api usage for replication 2015-08-24 Eseanu Marius Cristian * [f088632] : usrloc: clusterer_api is used for replication 2015-08-24 Eseanu Marius Cristian * [6c496d4] : ratelimit: clusterer api usage for replication 2015-08-24 Eseanu Marius Cristian * [22c720d] : dialog:added clusterer api usage for replication 2015-08-24 Razvan Crainea * [d2c08e9] : fix gcc 5.2 warnings 2015-08-24 Sergey Khripchenko * [de9224b] : two bugs fixed in db_text module + incorrect use of strncasecmp() + lost pkg_malloc 2015-08-22 Razvan Crainea * [d13c844] : add packaging/ copyrights (cherry picked from commit cbfbed00dd9be7f0287decfdbc9f1a3b2fed0337) 2015-08-21 Razvan Crainea * [9073e5b] : debian: Makefile skip tests (cherry picked from commit 446e44c3aff6f8e82d29620ef33caadb202a37d9) Conflicts: packaging/debian/changelog 2015-08-21 Razvan Crainea * [2d64c2c] : update copyright files (cherry picked from commit eda0ae2088d64a69028d67c6907cb39f0b998049) 2015-08-20 Razvan Crainea * [4bb9e46] : debian update build dependency 2015-08-20 Razvan Crainea * [641338b] : debian/copyright: remove gentoo files They are not included in the tar anyway (cherry picked from commit 871d4ebc857ba747d7c9aca8b165d92bc35d6e45) 2015-08-20 Razvan Crainea * [3bf5519] : remove invalid Makefile line (cherry picked from commit 4e1aa91b826ccdf7725fe00c70124f0066692d79) 2015-08-20 Răzvan Crainea * [3099f80] : debian: move to new dh_ packaging system (cherry picked from commit f45a92998cc9a787b9936753a4965f18edc915b1) 2015-08-20 Răzvan Crainea * [6dbf6e1] : copyright: fix license names (cherry picked from commit b595b829c3f202cad0d42af032c30368dde86bdb) 2015-08-20 Răzvan Crainea * [7c261a9] : copyright: sync with Julien's work http://anonscm.debian.org/viewvc/pkg-voip/opensips/trunk/debian/copyright?view=markup (cherry picked from commit ba9c9328e56bb5cc62251b77e7d934250e0cf2a1) 2015-08-20 Răzvan Crainea * [8690c30] : licence: add BSD and MIT licences 2015-08-20 Răzvan Crainea * [4dbfb6b] : licence: update FSF address 2015-08-20 Răzvan Crainea * [cf81803] : radius: move custom install inside module Thanks go to Nick Altman for reporting this 2015-08-19 Razvan Crainea * [70a34b9] : tm: fix bavp leaks in case of error Thanks go to Mickael Marrache for spotting and debugging 2015-08-19 Razvan Crainea * [dcd3ec4] : fix previous commit 2015-08-19 Razvan Crainea * [14b3bb6] : Makefile.defs: print target info only if changed 2015-08-19 Răzvan Crainea * [47c5ced] : debian: change from native to quilt (cherry picked from commit 3eaa77fa8d2a382ce37f01750213306206e1c605) 2015-08-19 Răzvan Crainea * [e6d60ad] : remove useless route.c include (cherry picked from commit 857857cd708aa2a7a278cd682debffa123ee4bc3) Conflicts: modules/emergency/emergency_methods.c 2015-08-19 Răzvan Crainea * [92baf28] : fix EXPRESSION typo (cherry picked from commit 6f07dab0bea530c6da593ab317aed46620deaea6) 2015-08-19 Răzvan Crainea * [f6981da] : debian/rules: add build-{arch,indep} rules (cherry picked from commit f53761262947638f5c80cdcd25f75faab679522f) 2015-08-19 Răzvan Crainea * [cddc950] : update github watch file (cherry picked from commit ac3ce34482dd7eb4402c5f08794160966cae6d7a) 2015-08-19 Răzvan Crainea * [e14455a] : fix http package description (cherry picked from commit d094f09c74e15581d2d05f62ee168701bd1455b0) 2015-08-19 Răzvan Crainea * [ef28834] : fix some spellchecks detected by lintian (cherry picked from commit 39bccea3282f94d2a34c6e448b118b99b0085799) 2015-08-19 Răzvan Crainea * [800035b] : install-modules: each module install its own custom files (cherry picked from commit 4afbfefe1fa11d8a01e980fa9baf164a80e16b3c) Conflicts: Makefile 2015-08-19 Răzvan Crainea * [f7025f3] : man: fix hyphens (cherry picked from commit 2ac0d082aafa6612d79a3479d132553910f380f0) 2015-08-19 Răzvan Crainea * [3478180] : init: add LSB functions support (cherry picked from commit 7524b2bd47dcbac10b54f3333ad5af8be07a57b0) 2015-08-19 Răzvan Crainea * [aa5ebb3] : debian: add python dependency for opensips (cherry picked from commit 2d1a5d954214fd3698c593fa7c1de6c967eec2a8) 2015-08-19 Răzvan Crainea * [76c37f0] : Makefile: do not install docs on debian installs (cherry picked from commit 064d562fad49631be82c97850046a8e3689036ae) 2015-08-19 Răzvan Crainea * [cb7bc74] : Fix man pages * add osipsconfig man page * fix lists info for opensipsconsole and opensipsdbctl (cherry picked from commit 979ee74cb537e71b2c73730386ff215903e30231) 2015-08-19 Răzvan Crainea * [6e23554] : fix debian packaging (cherry picked from commit d4a5a6a5bf353d74d60f286b06e8bc4ffe83b227) Conflicts: packaging/debian/changelog packaging/debian/control 2015-08-19 Razvan Crainea * [e2be56d] : ratelimit: rl_list show the last counter 2015-08-19 Răzvan Crainea * [87a1958] : Merge pull request #605 from saghul/makefile-target-master makefile: detect target architecture using the compiler 2015-08-19 Saúl Ibarra Corretgé * [1161779] : makefile: detect target architecture using the compiler Fixes producing a 32bit build in a 64bit system. Original patch by Andrei Pelinescu-Onciul: https://github.com/kamailio/kamailio/commit/c46e79709216c4975abc83869fbce9fa696eac74 2015-08-18 Bogdan-Andrei Iancu * [725573b] : Fix handling of ASYNC MI commands Similar to report #552. (cherry picked from commit 2b3a3ea9588854be5d7b64d4c04b48f6907a91ba) Conflicts: modules/mi_http/mi_http.c (cherry picked from commit 98435049c104151afa01864a8453957d9463ee51) 2015-08-18 rvlad-patrascu * [e7faa3e] : sql_cacher: Full caching improvements -use fetch_result for queries if sql db supports -fix cachedb encoding for null sql db values -caching id added in cachedb key 2015-08-18 Bogdan-Andrei Iancu * [f01fe37] : Fix restoring/freeing the path_vec in failure route. The path_vec (array holding the PATH string) was not properly pushed into the faked requests before calling the failure route. This was generating a mixture of shm/pkg memory when the faked request had to be freed. This crash was trigger only if PATH support was used and requests were looked up with PATH header. Reported by @AVFedorov and @petekelly. Closes #484 Many thanks to @AVFedorov for his details information and to @petekelly for support in investigating and testing. (cherry picked from commit cc033520ea5ab59fafd6ca7d52cc88fdad8574ef) 2015-08-18 Bogdan-Andrei Iancu * [69e5671] : Fix handling of ASYNC MI commands Part of fixing #552. Reported by @mqandeel . Closes #552 (cherry picked from commit c08b03310b9d8001b346ff3ba5a45996b1a5dc45) Conflicts: modules/mi_xmlrpc_ng/http_fnc.h modules/mi_xmlrpc_ng/mi_xmlrpc_http.c (cherry picked from commit c8c8410d8a97288af40edbabed7b5360dec968fb) 2015-08-18 ionutrazvanionita * [78da6d3] : fix usrloc clabel mask bug 2015-08-18 ionutrazvanionita * [b9cb15f] : fix usrloc contact label mask; in get_ucontact_from_id check if contact not found 2015-08-18 Bogdan-Andrei Iancu * [2ff5bdf] : Fix handling of ASYNC MI commands Part of fixing #552. Reported by Damien Sandras. (cherry picked from commit 5244b54afe63bba02fe45f82b736c327672b221a) Conflicts: modules/mi_json/mi_json.c (cherry picked from commit b1ee3a579a292faa47b6bfb1e5051982882f0447) 2015-08-18 Bogdan-Andrei Iancu * [24def0d] : Fix reporting if t_request() failed. Capture and report futher if the t_request() failed to send out the PUBLISH request. (cherry picked from commit 4791a506912d67f90dfe66e7292500046d59ef4a) 2015-08-17 Eseanu Marius Cristian * [efb0c5b] : clusterer: added register and send_to API functions 2015-08-17 Eseanu Marius Cristian * [6b61980] : clusterer:Added clusterer api 2015-08-17 Eseanu Marius Cristian * [0d104b7] : clusterer: added persistent mode 2015-08-17 Eseanu Marius Cristian * [e4bf577] : clusterer: improved load_info function 2015-08-17 Eseanu Marius Cristian * [ac90425] : clusterer: Added initial infrastructure 2015-08-17 Eseanu Marius Cristian * [10e2e7a] : bin_interface: changed bin_register_cb function 2015-08-17 Eseanu Marius Cristian * [a439b28] : Removed async forcing code 2015-08-17 Eseanu Marius Cristian * [eab11c2] : Set async to be disabled by default 2015-08-17 Eseanu Marius Cristian * [b8f9c55] : Fixed crash when failing to find a send socket Bogus free was done in case of error to find a socket Also, do not call raw callbacks for the BIN protocol 2015-08-13 rvlad-patrascu * [230fa58] : sql_cacher: full caching in mod_init 2015-08-12 ionutrazvanionita * [ac7c05c] : fix usrloc doc typo 2015-08-12 ionutrazvanionita * [cce2a5b] : fixed bug: set contact id val nul to 0 when inserting to db 2015-08-12 Liviu Chircu * [a85c14c] : Merge pull request #598 from jarrodb/593 Do not append colon if uri password is NULL 2015-08-12 Jarrod Baumann * [76dc734] : Do not append colon if uri password is NULL 2015-08-11 ionutrazvanionita * [40f56ca] : usrloc contact label now uses 14 bits instead of 16 2015-08-11 Bogdan Andrei IANCU * [18aa625] : Merge pull request #596 from rvlad-patrascu/master Add synchronous operation for raise functions for event_xmlrpc and event_rabbitmq 2015-08-11 Bogdan Andrei IANCU * [caaaf76] : Merge pull request #597 from ionel-cerghit/mi_xmlrpc_ng_format Mi_xmlrpc_ng response formatting improved 2015-08-11 Cerghit Ionel * [f2a1e3d] : Add module parameter to chose response formatting add mi_xmlrpc_ng_format_version parameter to let the user choose between the two ways of formatting output, a value of 1 means the old formating and a value of 2 means the new formatting which is the default. 2015-08-11 rvlad-patrascu * [7f515f9] : event_rabbitmq: update documentation for sync_mode module parameter 2015-08-11 rvlad-patrascu * [7c40030] : event_xmlrpc: update documentation for sync_mode module parameter 2015-08-10 Bogdan-Andrei Iancu * [0e1fabb] : Fix use_domain, escaping, cases in remove() Use extract_aor() in script function remove() ; extract_aor() takes into account the use_domain and cases settings and is also able to deal with escaped chars. Closes #557 . 2015-08-10 Bogdan-Andrei Iancu * [4ab975e] : Proper test on active destinatins for failover. Test both INACTIVE and PROBING flags before considering a destination suitable for failover. Reported by @gergelypeli Closes #564 . 2015-08-10 Bogdan-Andrei Iancu * [461cd33] : Simple text aligment to fit 80 chars wide. 2015-08-10 Bogdan-Andrei Iancu * [c8d102e] : Simple text aligment to fit 80 chars wide. 2015-08-10 Bogdan-Andrei Iancu * [4b3944f] : Fix race condition - the log must be under lock As information from the alias strucuture is printed, this must be done under lock protection. Closes #589 by @dbeskoek. (cherry picked from commit 22f4c1a2e05b5f3d8d3c7ba06ec30c21d75bc4de) 2015-08-10 Bogdan Andrei IANCU * [83cb342] : Merge pull request #595 from ionel-cerghit/add_bin_version Add version field in the header of bin protocol 2015-08-10 Cerghit Ionel * [a0dae0a] : mi_xmlrpc_ng: fixed formatting and flushing problems in module 2015-08-10 Cerghit Ionel * [87bd7a9] : Added version field in the header of bin protocol, to avoid compatibility problems between different versions of opensips 2015-08-07 ionutrazvanionita * [750d36a] : Revert "stateful ping for nathelper" This reverts commit aecbf986eee2f931a41057a9c400e18c77bbbaed. 2015-08-07 ionutrazvanionita * [35426b2] : fix bug: db key index when inserting 2015-08-07 ionutrazvanionita * [aecbf98] : stateful ping for nathelper 2015-08-07 Bogdan-Andrei Iancu * [6112b8b] : MyISAM no longer default table engine. InnoDB is now the default, and this value can now be changed via MYSQL_ENGINE env variable in opensipsctlrc. Closes #399 2015-08-07 Bogdan-Andrei Iancu * [67c2c91] : Use SIP pinging when NAT traversal is on 2015-08-06 Bogdan-Andrei Iancu * [164dbe5] : Fix async sleep() in sync mode. If the timer FD cannot be placed in reactor, the resume will work (in sync way), but without any actual sleep. We fix this by remembering the time when the sleep should finish and checking it again in resume function. 2015-08-06 Bogdan-Andrei Iancu * [0ff15f6] : Remove double define. param_export_t is already defined by sr_module_deps.h which is included from here. 2015-08-06 Răzvan Crainea * [2d15060] : fix debian packaging 2015-08-06 Bogdan-Andrei Iancu * [ad6a923] : Fix usage of timerfd on old systems. timerfd functions were introduced starting with glib 2.8, so disable them if older glib versions are detected. 2015-08-06 Bogdan-Andrei Iancu * [5af7010] : Fix int64_t printing. 2015-08-06 ionutrazvanionita * [7ef4eaa] : Merge branch 'usrlocCID' 2015-08-06 ionutrazvanionita * [7d20a36] : fix contact id in DB_ONLY mode issue 2015-08-06 Bogdan-Andrei Iancu * [21fb5cd] : Just fixing indentation. 2015-08-06 root * [570d7d7] : emergency -- code review 2015-08-05 Razvan Crainea * [3dfc83c] : add rest_client specs for debian, fedora and rpms Closes #587 2015-07-30 ionutrazvanionita * [5c33319] : delete multiple contacts in one db call for WRITE_THROUGH and WRITE_BACK 2015-07-30 Steve Frécinaux * [d20175e] : Debian: add package opensips-rest-module This package contains the rest_client.so module. 2015-07-29 Steve Frécinaux * [4b6996e] : Debian: add systemd unit file. This unit file supports the configuration check before restart and retains the P_MEMORY and S_MEMORY variable usage. Since both the systemd configuration and the init script take care of creating /var/run/opensips, the opensips.dirs file has been removed. 2015-07-29 Steve Frécinaux * [981c5e6] : Debian: update dependencies to build on Jessie. Unfortunately, the lua module doesn't build anymore because it depends on libmemcache, which is not present in Jessie. The other memcached-related modules depend on libmemcached, which is fine. See https://github.com/OpenSIPS/opensips/issues/580 2015-07-29 Steve Frécinaux * [459d61d] : Debian: do not make opensips-module-mysql depend on mysql-client This makes it impossible to use opensips with mariadb. 2015-07-29 Steve Frécinaux * [80143e1] : Debian: make the init script use LSB functions. This is more consistent with the other init scripts, and most importantly fixes the behaviour of the init script when it comes to systemd-enabled systems. 2015-07-29 ionutrazvanionita * [a859a45] : updated usrloc documentation 2015-07-29 ionutrazvanionita * [ece2774] : get_all/domain_ucontacts: posibility to fetch contact id 2015-07-29 ionutrazvanionita * [91828e3] : possibility to delete a contact only with the id 2015-07-28 ionutrazvanionita * [b46429e] : fix db_insert_ucontact: no value for contactid when AUTOINCREMENT wanted 2015-07-28 ionutrazvanionita * [603a660] : for PRIMARY KEY AUTOINCREMENT in sqlite: value is stored as BIGINT, type is INT, allowing in code usage to see the key both int and bigint(default int) 2015-07-28 Liviu Chircu * [b25164e] : Add new s.trim / s.trimr / s.triml transformations Closes #579 2015-07-28 ionutrazvanionita * [bbccb7a] : updated location version 2015-07-27 ionutrazvanionita * [9404a76] : update and delete based on contact_id 2015-07-27 ionutrazvanionita * [4d7c67f] : on DB_ONLY insert contact id value is null(autoincrement is used) 2015-07-27 ionutrazvanionita * [f1578b1] : modified id column to contact_id bigint primary key autoincrement in al dbs; regenerated scripts 2015-07-24 ionutrazvanionita * [04c9add] : introduced contactid as composed of aorhash|recrod label|contact label 2015-07-23 ionutrazvanionita * [3293358] : registrar: lookup for aors in branches and expand them to contacts 2015-07-22 Bogdan-Andrei Iancu * [ef0dc6f] : Fix broken condition in handling ASYNC_CONTINUE. (cherry picked from commit eaf3ca55148cffad9859fa00f6e7ccc4ce4ec938) 2015-07-22 rvlad-patrascu * [1bc8baf] : event_rabbitmq: added synchronous operation for raise function 2015-07-22 rvlad-patrascu * [5a3d230] : event_xmlrpc: added synchronous operation for raise function 2015-07-20 Răzvan Crainea * [c959f49] : Merge pull request #575 from ar45/patch-1 Update README - change db_mysql to db_postgres 2015-07-17 Aron Podrigal * [37f5449] : Update db_postgres_admin.xml changed db_mysql to db_postgres 2015-07-17 Aron Podrigal * [7ef27a9] : Update README 2015-07-17 Bogdan-Andrei Iancu * [0886073] : Fixed memeory exhausting during concurrent reloads. Add a new module parameter no_concurrent_reload to prevent multiple reloads in the same time. If you have a large routing set (millions of rules/prefixes), you should consider disabling concurrent reload as they will exhaust the shared memory (by reloading into memory, in the same time, multiple instances of routing data). (cherry picked from commit 4480d115ee8f15c077a86c41086537758105ba8a) 2015-07-16 Razvan Crainea * [97ea216] : event_route: fix uninit _bogus_ warning 2015-07-16 Razvan Crainea * [9eb48f0] : ememrgency: fix curl cast warning 2015-07-16 Razvan Crainea * [621bd5a] : httpd: fix cb cast warning 2015-07-16 Răzvan Crainea * [dc0edf5] : lua: fix compile warning 2015-07-16 Răzvan Crainea * [668533e] : mi_xmlrpc_ng: fix include warning 2015-07-16 Răzvan Crainea * [544db1e] : xcap_client: fix timer include 2015-07-16 Liviu Chircu * [2649a3a] : for-each statement: Add support for $json iterators Example way of usage: for ($json(foo) in $(avp(bar)[*])) xlog("JSON fields: $json(foo/baz) $json(foo/qux)\n"); 2015-07-16 Liviu Chircu * [3607208] : regex: Fix implicit declaration of 'isspace' 2015-07-16 Liviu Chircu * [b391ea3] : db_virtual: Do not crash if no DB sets are defined 2015-07-16 Razvan Crainea * [e6fb1c1] : emergency: fix warnings 2015-07-16 Razvan Crainea * [9da7c53] : gcc 5.1 warnings fix re-arranged code to comply with gnu11 2015-07-16 Ovidiu Sas * [084ed77] : avpops: fix undefined symbol: is_script_async_func_used 2015-07-15 Razvan Crainea * [b9d0ad2] : add lua proper dependencies 2015-07-15 Razvan Crainea * [a02d31b] : travis: add all dependencies 2015-07-15 Razvan Crainea * [bfd6895] : travis: add extra dependecies 2015-07-15 Razvan Crainea * [08e0277] : travis: skip cachedb_{cassandra,couchbase} 2015-07-15 Razvan Crainea * [96e680a] : travis: update dependecies 2015-07-15 Razvan Crainea * [6181434] : add markup readme 2015-07-15 Razvan Crainea * [f09522f] : travis: check all modules 2015-07-15 Vlad Paiu * [babfdab] : Fixed ctl & ctlrc changes introduced by proto_bin commit 2015-07-15 Razvan Crainea * [2b89a69] : travis: remove clang compiler 2015-07-15 rvlad-patrascu * [75acc35] : event_virtual: Added documentation 2015-07-15 rvlad-patrascu * [0bbd1c7] : event_virtual: Added comments, minor changes to socket printing 2015-07-15 rvlad-patrascu * [c22ad08] : event_virtual: Added free function; fixed printing of the virtual socket 2015-07-15 rvlad-patrascu * [f2ea585] : event_virtual: Added raise function 2015-07-15 rvlad-patrascu * [9f54c5f] : event_virtual: created new module: event_virtual, added parse and match functions 2015-07-15 Răzvan Crainea * [ae39a9c] : Merge pull request #573 from ionel-cerghit/master add new proto_bin module 2015-07-15 Razvan Crainea * [e6dbf9e] : add travis file 2015-07-15 Cerghit Ionel * [bfe6bc6] : added new proto_bin moudle 2015-07-09 Liviu Chircu * [5a832d9] : db_virtual: Do not crash if no DB URLs are defined 2015-07-09 Liviu Chircu * [5415531] : config parser: Exit if no more memory available Otherwise, some code paths would generate segmentation faults 2015-07-09 Liviu Chircu * [1e675ac] : avpops: Proper handling for async with non-async drivers Fallback to normal mode in case of no async capabilities. If this behaviour is employed, a startup warning will be issued. 2015-07-09 Liviu Chircu * [aad94da] : db_mysql: Improve error handling in async mode i.e. Logging and error handling in case of bad async queries 2015-07-09 Liviu Chircu * [87acbbc] : core API: Add detection for async script function usage 2015-07-09 root * [0962723] : emergency -- change configuration of the module for a new table 2015-07-09 root * [3cbebd4] : emergency -- change configuration of the module for a new table 2015-07-07 Răzvan Crainea * [1b15064] : Merge pull request #569 from vladpaiu/master New features for OpenSIPS 2.2 2015-07-07 Razvan Crainea * [35ae885] : fix inline functions to comply with gnu11 The new gcc 5.1 version uses by default gnu11 standard, which behaves a bit differently with inline functions (more details here: https://gcc.gnu.org/gcc-5/porting_to.html) Closes #555 2015-07-07 Bogdan-Andrei Iancu * [912b0d1] : More verbous logging. Log the error reported by stat() when it fails. (cherry picked from commit 8fefc911e643142b4206fa574896171dbdb1eb82) 2015-07-07 ionutrazvanionita * [f077ee7] : fix 32 bit warning 2015-07-06 ionutrazvanionita * [d9eec58] : Also build sqlite files on all Makefile rule 2015-07-06 Vlad Paiu * [43a2619] : Fixed typo in encoding the callid 2015-07-06 Eseanu Marius Cristian * [0eb9a8c] : db_postgres: Added module parameter max_db_queries 2015-07-06 Eseanu Marius Cristian * [140cc8c] : db_mysql: Added module parameters max_db_queries and max_db_retries 2015-07-03 Cerghit Ionel * [524bd1c] : dispatcher: first_hit_counter 2015-07-03 rvlad-patrascu * [c1dd6d2] : added global parameter for config file: default logging level for xlog 2015-07-03 Eseanu Marius Cristian * [19ac2ea] : digiplan.c: Fixed memory leak 2015-07-03 Eseanu Marius Cristian * [322c2cb] : dialplan.c: Fixed memory leak 2015-07-03 ionutrazvanionita * [c53b81d] : fix db/schmea Makefile bug V2 2015-07-03 ionutrazvanionita * [5a018a0] : fixed db/schema Makefile bug; regenerated files that were affected by this bug 2015-07-03 Eseanu Marius Cristian * [d5691f6] : dialplan.c: Fixed mi_show_partition to not show the password 2015-07-03 ionutrazvanionita * [f55095e] : added record and contact labels; currently no usage 2015-07-03 ionutrazvanionita * [dd89571] : exposed get_domain_ucontacts; get_all_ucontacts now calls get_domain_ucontacts 2015-07-03 Cerghit Ionel * [a814271] : event_flatstore: added documentation 2015-07-02 Vlad Paiu * [a0ec25a] : Fixed generated callid = is not a valid character in a callid, so replaced it with a hyphen 2015-07-02 Liviu Chircu * [649a35e] : Merge pull request #565 from jarrodb/b64_xor Add b64encode, b64decode and xor string transformations 2015-07-02 Jarrod Baumann * [6eb4431] : adds better error reporting and fixes typos 2015-07-02 rvlad-patrascu * [b8b48a3] : event_flatstore: fixed coding style 2015-07-02 Cerghit Ionel * [223f0c7] : event_flatstore: small fix iov buffer incrementing 2015-07-02 ionutrazvanionita * [6036215] : proper error message when ldap tls fails to start 2015-07-02 Cerghit Ionel * [121afc8] : event_flatstore: fixed delete and parse for expire 2015-07-02 Cerghit Ionel * [60f2c6b] : event_flatstore:fixed raise event 2015-07-01 Jarrod Baumann * [9d35613] : Added b64encode, b64decode, and xor string transformations 2015-07-01 Cerghit Ionel * [dfd548a] : Merge branch 'master' of https://github.com/vladpaiu/opensips Conflicts: modules/event_flatstore/event_flatstore.c 2015-07-01 Cerghit Ionel * [875f727] : event_flatstore: fixed rotation funcion 2015-07-01 rvlad-patrascu * [d650a69] : event_flatstore: added file permission parameter, fixed delimiter parameter 2015-07-01 Cerghit Ionel * [4e76482] : event_flatstore: added debugging messages 2015-07-01 Eseanu Marius Cristian * [ce287e2] : Merge branch 'master' of https://github.com/vladpaiu/opensips 2015-07-01 Eseanu Marius Cristian * [787eaff] : event_flatstore: added debug info for rotating and delete functions 2015-07-01 Cerghit Ionel * [63a7aa3] : Merge branch 'master' of https://github.com/vladpaiu/opensips 2015-07-01 Cerghit Ionel * [fbd5020] : event_flatstore: added debuging prints 2015-07-01 rvlad-patrascu * [b5481fc] : event_flatstore: added debug prints for flat_raise 2015-07-01 Cerghit Ionel * [034fe68] : event_flatstore: check if path is valid (parse) 2015-07-01 Cerghit Ionel * [182f421] : event_flatstore:modified raise and parse functions 2015-06-30 Bogdan-Andrei Iancu * [d6cd8b0] : Expose unset_dlg_profile() to Request Route. Related to issue #554. (cherry picked from commit 23f2322db56540be4d0b36a099a02e380aeafe19) 2015-06-30 Cerghit Ionel * [5ef4a84] : event_flatstore: fixed mod_init 2015-06-30 Eseanu Marius Cristian * [c90fc34] : event_flatstore: added destroy and param function 2015-06-30 Cerghit Ionel * [6f2934d] : event_flatstore: merged all functions 2015-06-30 Cerghit Ionel * [a6ee51e] : event_flatstore: solved conflicts 2015-06-30 Cerghit Ionel * [2a815a2] : event_flatstore: added parse function 2015-06-30 rvlad-patrascu * [807df79] : event_flatstore: fixed errors 2015-06-30 Eseanu Marius Cristian * [9cdbc24] : Added print, match, rotate and partial raise functions 2015-06-30 Eseanu Marius Cristian * [56eba63] : Added print, match, rotate and partial raise functions 2015-06-30 rvlad-patrascu * [11f61cf] : event_flatstore: added flat_free and verify_delete functions 2015-06-29 Cerghit Ionel * [9642050] : event_flatstore: defined used structures and mod_init 2015-06-26 Cerghit Ionel * [cb13543] : event_flatstore: infrastructure 2015-06-26 Cerghit Ionel * [996fadf] : event_route: fix route name when no sync/async 2015-06-26 Bogdan-Andrei Iancu * [d171b7a] : Fix TH for BYEs used as CANCEL Trigger the dialog callbacks (used by TH) for BYEs which do not terminate the call (BYEs in early state can be used as CANCELs). Reported by Antonis Psaras. Closes #555 (cherry picked from commit b0f52d559e615e8dfecd1e7630b15a9c7584af4f) 2015-06-26 Razvan Crainea * [31f46c2] : always create tar with TLS files 2015-06-25 Razvan Crainea * [ea2a041] : acc: fix column type mismatch for db missed calls This bug was making OpenSIPS crash when a missed call was inserted in DB Thanks go to Peter Baines for reporting it (Issue #559) 2015-06-25 Razvan Crainea * [307388d] : acc: fix multi_leg_bye info behavior This bug was making the ACC module to print less information in case there were no AVPs populated for the BYE multi_leg_info 2015-06-24 Bogdan-Andrei Iancu * [cc06843] : Fix $ct and $ct.fields variables. Force parsing of all Contact headers. Reported by Nick Altmann. 2015-06-22 Ryan Bullock * [80233f8] : Remove use of deprecated synchronous mode in couchbase 2015-06-18 Ovidiu Sas * [237b771] : event_rabbitmq: reconnect to rabbit-mq server on "Socket error" - closes #535 2015-06-15 Razvan Crainea * [1a89f57] : dialog: fix profile_get_values to get the proper counter 2015-06-15 Bogdan-Andrei Iancu * [cfb7415] : Fix compile without RADIUS ASYNC support. Credits go to Rudy Pedraza. 2015-06-15 Bogdan-Andrei Iancu * [1bd25ca] : Fix name of coumns in emergency tables 2015-06-15 ionutrazvanionita * [c98caf5] : async support for ldap module 2015-06-12 Razvan Crainea * [2e8b265] : dialog: add missing header file 2015-06-12 Razvan Crainea * [307c593] : dialog: add bin profiles replication 2015-06-11 Ovidiu Sas * [8450630] : mi_xmlrpc_ng: unescape xml strings - related to bug #537 2015-06-11 Ovidiu Sas * [f6386ca] : core: adding unescape_xml() function helper to strcommon.[ch] 2015-06-11 Nick Altmann * [611b9bb] : RPM spec: enabled restart feature (in case of crash) in systemd service 2015-06-11 Nick Altmann * [92ffe5b] : RPM spec: returned missed m4 feature, changed version number 2015-06-11 Bogdan-Andrei Iancu * [2421034] : Fix documentation on loose_route(). loose_route() returns FALSE only if there is no Route header or if preloaded local Route only. Reported by @gergelypeli on github Closes #550 2015-06-11 Bogdan-Andrei Iancu * [800117b] : Revert "Fix parsing for Diversion header." This reverts commit 36384927a5d2fd1d900ac36adc2ee15b2acad0c9. 2015-06-11 Bogdan-Andrei Iancu * [44f740b] : Fix fixing hdr names shorter than 3 chars. The fixup function fails to identify header names shorter than 3 (like To or short formats). This affected script functions like is_present_hf() or remove_hf(). Reported by Nick Altmann (cherry picked from commit 1f7bae0915ac93ca231ede55c5540560e5489a7b) 2015-06-10 Razvan Crainea * [37cd34d] : ratelimit: add rl_count(name) variable 2015-06-10 ionutrazvanionita * [769cae8] : async support for radius module 2015-06-09 Bogdan Andrei IANCU * [1bc3a58] : Merge pull request #545 from dynamicpacket/master fixes for Debian packaging 2015-06-09 User * [b0fed16] : * Check for freeradius libs in Makefile * Check for json lib include paths in Makefile * Update debian/changelog to reflect 2.1.0 stable * Update debian/control with new dependency names and standards * Update debian/rules to remove deprecated dpatch 2015-06-09 root * [65444d9] : emergency module - change from list to hash 2015-06-08 ionutrazvanionita * [42ec45a] : fix partition list bug 2015-06-08 ionutrazvanionita * [c2fd6c8] : fixed permission table_name bug 2015-06-08 Bogdan-Andrei Iancu * [84e78f2] : Add emergency tables to TABLES_EXTRA 2015-06-08 Bogdan-Andrei Iancu * [95328b5] : DB schema regenerated (to add the emergency tables) 2015-06-08 Bogdan-Andrei Iancu * [6cc12f1] : Add DB schema for the emergency module. Credits go to Robison Gonçalves Tesini. Closes #543 2015-06-05 Razvan Crainea * [1e998bb] : ratelimit: print the actual counter for the PIPE 2015-06-05 Razvan Crainea * [b3b5888] : ratelimit: do not multiply the expiration with the interval 2015-06-05 Bogdan-Andrei Iancu * [0708685] : Add folded_string token. This token is now used by xlog, so you can have a multi-chunk (even multi line folded) string as parameter, like: xlog("Bob" "hates Alice" " very much\n"); Based on the idea from #538, but done at grammar level. 2015-06-04 Bogdan-Andrei Iancu * [c146594] : Fix init of UDP listener in nofork mode. Init the UDP listener before forking the MI procs, otherwise they will not be aware of the network sockets. Closes #541 Self reported. 2015-06-04 Razvan Crainea * [fd8036c] : ratelimit: update the last time used for the pipe 2015-06-04 Di-Shi Sun * [ab60f69] : Updated for OSP Toolkit 4.5.0. 2015-06-03 Bogdan-Andrei Iancu * [3638492] : Fix parsing for Diversion header. As Diversion header may be a multi-body header, do not use the parse_to() function as it does not accept multiple bodies. Use parse_rr() function. Reported by Nick Altmann. 2015-06-03 Vlad Paiu * [eea8476] : Removed bogus ERR message 2015-06-03 Razvan Crainea * [842588f] : ratelimit: add bin support Added bin interface replication support. This provides distributed pipes features 2015-06-03 Ionut Ionita * [4d3ab1f] : Merge pull request #528 from jarrodb/db_sqlite_datetime update sqlite convert DB_DATETIME types to proper time values 2015-06-03 Bogdan Andrei IANCU * [e45cc1f] : Merge pull request #534 from seanchann/1.11 fix rpm build error on centos (cherry picked from commit 715914848153678c473dc3d4211b8b534bf66fa1) 2015-06-02 Razvan Crainea * [05bc754] : event_xmlrpc: only retry send/recv a couple of times if there are errors Credits go to Joseph Frazier (onsip) for spotting this 2015-06-02 Razvan Crainea * [008b423] : event_rabbitmq: EINTR is not an error and should not be considered for retry 2015-06-02 Eric Tamme * [ac419bc] : do not always retry write to pipe if we get blocking errors as this causes the proxy to lock up and consume cpu 2015-06-02 Liviu Chircu * [0db7e0e] : s.fill transformation: Prioritize string pvars over integer ones Reason for this is that a pvar which holds a string is a more particular occurrence than an int one, since integers can be easily stringified. This fixes some issues with pvars which have multiple internal representations (e.g. $rm is "INVITE" and 1 at the same time) Reported by Eric Tamme 2015-06-02 Liviu Chircu * [954e771] : s.width transformation: Fix a rare crash Prevent double free in case a negative number is given 2015-06-02 Liviu Chircu * [5ff8ff5] : Merge pull request #530 from jarrodb/transformations add width string transformation 2015-06-02 Vlad Paiu * [5bf5479] : Fixed typo 2015-06-02 Jarrod Baumann * [1bc334b] : Add support for hiredis type REDIS_REPLY_NIL in redis_raw_query 2015-06-02 Liviu Chircu * [ee4374f] : s.fill transformations: Allow whitespace in quoted params This patch actually fixes several issues: * allows the "s.fill" functions to be used for alignment purposes * bugfix: possible "s.fill" usage which caused infinite loop * new: trim.h string trimming macros * some code refactoring Fixes #531 2015-06-02 Liviu Chircu * [21de1c7] : Merge pull request #533 from jarrodb/pvar_529 verify pv_export_t parse_name function is set before calling 2015-06-01 Ovidiu Sas * [1288d26] : event_rabbitmq: add specif error probe for AMQP_STATUS_SOCKET_ERROR - triggered after rabbitmq server restart 2015-06-01 Ovidiu Sas * [b516510] : event_rabbitmq: enhance AMQP connection error probe 2015-05-31 Jarrod Baumann * [a9d3150] : cleanup code and add comments 2015-05-30 Jarrod Baumann * [3c47edd] : verify pv_export_t parse_name function is set before calling 2015-05-30 Liviu Chircu * [bcf8524] : vim syntax: Add full support for OpenSIPS variables * coloured context, indexes and transformations * matching coloured variable parantheses * everything is also shown within strings 2015-05-30 Jarrod Baumann * [f70fdb9] : add string transformation e.g. 2015-05-29 Jarrod Baumann * [65e3a6c] : update sqlite convert DB_DATETIME types to proper time values 2015-05-29 Liviu Chircu * [94e3593] : script parser: fix bad startup error message 2015-05-29 Liviu Chircu * [57938c0] : vim syntax: proper matching for route statements * conflicts with route() calls are now solved * add syslog facility validation 2015-05-29 ionutrazvanionita * [84b4e7e] : partition support for permission module 2015-05-29 Bogdan-Andrei Iancu * [1713bb2] : Fix debug log. Do not print un-initialized variables. Reported by miha- on IRC. (cherry picked from commit 59d0f336083f262c36cac522117addf989c87f99) 2015-05-28 Liviu Chircu * [69f6510] : binary interface: skip() functions now return skipped bytes 2015-05-28 Liviu Chircu * [23f6a26] : binary interface: Add retcode for too many pop() operations 2015-05-28 Liviu Chircu * [1a396bf] : binary interface: push() functions now return added bytes 2015-05-27 Liviu Chircu * [84554f5] : bin_interface: also provide source IP address to all callbacks 2015-05-27 Bogdan-Andrei Iancu * [3f806a3] : Add aysnc sleep() and usleep() functions. The sleep() and usleep() script functions can now be used with the async statement - the waiting is done non-blocking, but with suspend and resume. 2015-05-27 Liviu Chircu * [91d1b4f] : avpops: Fix possible runtime crash The patch corrects a bug in commit 685da74670, since mod_init() is actually run _before_ module function fixups. Detection for raw SQL function calls is now done (properly) at fixup time, rather than at module initialization. Reported by Antonis Psaras Closes #523 2015-05-26 Liviu Chircu * [724d16d] : residential cfg: Set db_url for "uri" module when doing auth 2015-05-26 Vlad Paiu * [74a52d0] : Set the upsert flag when incrementing / decrementing counters This way the counters don't need to be pre-populated in the DB ( eg. when using them for dialog profiles ) 2015-05-26 Bogdan-Andrei Iancu * [607fb3e] : Fix "dialplan addrule" command. The match_len column was replaced with match_flags Closes #513 2015-05-22 Bogdan-Andrei Iancu * [032c425] : Fix NULL value as route param. Closes #516, open by @liviuchircu. 2015-05-22 Bogdan-Andrei Iancu * [6f5dde4] : Fix handling of gw_priprefix_avp param. Closes #518, reported by @apsaras. (cherry picked from commit 3870a57702595a4816e2520c708bb4f337a17546) 2015-05-21 root * [b667632] : emergency -- change callcell identity 2015-05-21 root * [8221c64] : Merge branch 'master' of github.com:OpenSIPS/opensips 2015-05-21 root * [1c56870] : emergency -- change callcell identity 2015-05-20 Bogdan Andrei IANCU * [899bbdd] : Merge pull request #517 from staskobzar/1.11 Make sure httpd can recognize Content-Type headers with parameters. (cherry picked from commit a8971c2fcb0207a34005a2ee586de97ecf759638) Conflicts: modules/httpd/httpd_proc.c 2015-05-20 Liviu Chircu * [564acbe] : route params: increment nesting level on no-param routes Completes commit f3ed4db99 2015-05-20 ionutrazvanionita * [6164d1c] : Closed connection in child init after data load; Connected to db from mi process in mi_child_init 2015-05-20 Vlad Paiu * [a57d67f] : Expose API for setting connection attributes in WS module Fix so that tcp_persistent_flag in registrar also works for WS connections 2015-05-20 ionutrazvanionita * [fa14431] : move db data initialization from mi_child_init to child init 2015-05-19 Ovidiu Sas * [67948ce] : dialplan: documentation for MI command: dp_show_partiton 2015-05-19 Ovidiu Sas * [3710087] : dialplan: doc updates - compact partition example 2015-05-19 Bogdan-Andrei Iancu * [1c1d732] : Fix $proto to read from loaded protocols 2015-05-18 ionutrazvanionita * [7846e63] : fixed partition parsing 2015-05-18 vladpaiu * [31fb1bc] : Merge pull request #507 from jarrodb/cachedb_cassandra set local variable memory to 0 to prevent core dump 2015-05-18 Bogdan-Andrei Iancu * [9597bc9] : Fix restoring the route type after async jump. A more accurate fix on preserving the route type after an async jump - see commit 950966. 2015-05-16 Liviu Chircu * [be87372] : fix doc typo Reported by Nick Altmann 2015-05-16 Liviu Chircu * [9509665] : async: set route type before running resume routes 2015-05-16 Liviu Chircu * [6353c5a] : vim syntax: update with all script parameters 2015-05-16 root * [961752e] : Merge branch 'master' of github.com:OpenSIPS/opensips 2015-05-16 root * [70b662c] : emergency -- Notifier Implementation 2015-05-15 Bogdan-Andrei Iancu * [e461b65] : Fix - DLG_STATE_CHANGED event contains the DID too Reported by Dan Bogos. This is a bug, as without the Dialog ID, the DLG_STATE_CHANGED event is useless. 2015-05-15 Ovidiu Sas * [b3a0b3e] : dialplan: dp_show_partition shows db_url for each partition 2015-05-15 Razvan Crainea * [a5d16d2] : proto_tls: fix README tcp typo 2015-05-15 Razvan Crainea * [a63d6fe] : dialog: do not raise event under lock This allows nested dialog operations and prevents opensips from dialog deadlock 2015-05-15 Liviu Chircu * [df2c030] : bin_interface: use blocking I/O The 2.1 code uses non-blocking UDP socket init primitives. This specifically made the binary interface workers do a lot of busy-waiting Reported by Rik Broers Fixes #502 2015-05-14 Ovidiu Sas * [37ce047] : dialplan: new 'dp_show_partition' mi command 2015-05-12 Jarrod Baumann * [d84df19] : set local variable memory to 0 to prevent core dump 2015-05-11 Liviu Chircu * [4d6baa5] : io_wait.h: always include pt.h pt.h is required even without HAVE_SIGIO_RT Reported by @narunask Fixes #505 2015-05-11 Liviu Chircu * [999364e] : build system: properly parse "uname -r" output Also handle "MAJOR.MINOR-SUB" types of strings 2015-05-11 Bogdan-Andrei Iancu * [885097d] : Fix bogus jump to "error" label. if unhash_fd_map() is not done, do not try to do fix_fd_array() . (cherry picked from commit 9c066e61673ed35225a4b5a78483c3e6722cc413) 2015-05-11 Liviu Chircu * [c358997] : Add vim syntax highlighting script Also add INSTALL instructions 2015-05-08 Liviu Chircu * [fd8fc60] : async DB core: fix noisy logs 2015-05-07 Liviu Chircu * [2ef34a5] : rest_client: properly free ctype buffer 2015-05-07 Liviu Chircu * [910990c] : rest_client: update doc & README 2015-05-07 Liviu Chircu * [a614e21] : rest_client: properly handle async calls in local deployments Proper libcurl usage is to poll for handle status updates after each perform operation. This patch also fixes a few cleanup issues with the global list of pending HTTP headers Thanks to Dan Bogos for reporting and helping with troubleshooting 2015-05-07 Vlad Paiu * [147ee7f] : While doing retransmissions, set the current processing transaction - in case anybody uses it (cherry picked from commit ef3c2539323580c713d86b9c554700dff058d7e0) 2015-05-07 Bogdan-Andrei Iancu * [271c7be] : Fix crash on building CANCEL requests. Avoid the race condition between building a local cancel and handling a negative reply (for the same branch, in different processes). The build_local mya use the temporary uac->reply and end up with a dnagling pointer. Anyhow, in CANCEL situation, you never have a stored reply in transaction (storing is done only during parallel forking for nagative branches - which are not to be cancelled anyhow). (cherry picked from commit 156fba8d5eeedaa3bbb5cd89a525e1591a288123) 2015-05-06 Liviu Chircu * [4072c47] : revert previous commit (4b5b16f9a) Only 1.11 and 1.10 branches are affected by this bug 2015-05-06 Liviu Chircu * [4b5b16f] : async TCP: fix memory leak Also free the array of chunk pointers when freeing up TCP conns 2015-05-06 Bogdan-Andrei Iancu * [409ca2a] : README file updated from XML 2015-05-06 Bogdan Andrei IANCU * [4d3e2cb] : Merge pull request #499 from jarrodb/rest_client_docs Added documentation for rest_client:rest_append_hf command 2015-05-05 Jarrod Baumann * [9764a88] : Added documentation for rest_client:rest_append_hf command 2015-05-05 Liviu Chircu * [2e9b36c] : sipmsgops: fix possible crash with debug=4 Introduced in commit 445d90bc7 Reported by Hieu Ta 2015-05-05 Liviu Chircu * [21344d1] : core fd management: suppress DEL errors for closed fds (EBADF) Reasons: * library-specific corner-case (e.g. libcurl with async ops) * network core removes the fds anyway 2015-05-05 Liviu Chircu * [82eded8] : core fd management: properly remove fds on DEL errors Also improve debug printing 2015-05-05 Liviu Chircu * [aa88b5f] : Merge pull request #497 from jarrodb/rest_append_hf addition of rest_append_hf command 2015-05-05 Răzvan Crainea * [7ba692d] : Merge pull request #488 from jarrodb/scripts Removed default DBPORT from opensipsdbctl.base 2015-05-05 Bogdan Andrei IANCU * [a91de4c] : Merge pull request #498 from olivermt/master Make rtpengine retry sending command on UDP fragmentation 2015-05-05 Oliver Severin Mulelid-Tynes * [61c5e71] : Make rtpengine retry sending command on UDP fragmentation 2015-05-05 Liviu Chircu * [3e82c48] : fraud_detection: fix "daysoftheweek" parsing bug Parsing was incorrect for sequences such as "Fri-Mon", "Thu-Wed" etc. 2015-05-05 Liviu Chircu * [2d5dcad] : fraud_detection: properly release stats lock on error 2015-05-05 Jarrod Baumann * [6a8c14f] : moved curl_slist to global process variable to support addition of rest_append_hf command 2015-05-04 Bogdan-Andrei Iancu * [447301c] : Fix order of rows during query; respect file order When buidling the response to a select query, respect the order of rows as given by the file; before the selected rows were given in the reverted order. Reported by @jockmckechnie on github. Closes bug #479 . (cherry picked from commit 54f6c22303f0d258c9e5f24520bba342aeb11ac3) 2015-05-04 Vlad Paiu * [c4eafa7] : Fixed json results for negative integers (cherry picked from commit 3bf8b49ec8ee3d29409c75e54581141792ee54e9) 2015-05-04 Liviu Chircu * [4d651f4] : rest_client: caseless matching when searching for Content-Type Also properly handle missing "Content-Type" 2015-05-04 Razvan Crainea * [89eb9dc] : acc: when doing no-cdr accounting, do not use CDR-generated info Related to ticket #495 2015-05-01 vladpaiu * [ee2a7a2] : Merge pull request #491 from jarrodb/event_rabbitmq Updated log macro used for reporting event_rabbitmq heartbeat modparam 2015-04-30 Jarrod Baumann * [25fc10c] : Updated log macro used for reporting event_rabbitmq heartbeat modparam 2015-04-30 Jarrod Baumann * [901c3f7] : Removed mandatory statement from opensipsctlrc for PostgreSQL 2015-04-30 Jarrod Baumann * [c06b45d] : Removed default DBPORT from opensipsdbctl.base so it will be determined by the client 2015-04-30 Răzvan Crainea * [6340435] : Merge pull request #486 from jarrodb/scripts Add DBPORT parameter with comment to opensipsctlrc 2015-04-30 Jarrod Baumann * [c168307] : Add DBPORT parameter with comment to opensipsctlrc to alleviate confusion when using PostgreSQL 2015-04-29 Razvan Crainea * [bf3ae34] : socket_info: return socket list only for known protocols 2015-04-27 Bogdan-Andrei Iancu * [d154fe2] : Docs updated and refurbished. 2015-04-27 Bogdan-Andrei Iancu * [ed82ac8] : Fix: do not try to kill procs which were not created. 2015-04-25 Bogdan Andrei IANCU * [dd7b8c8] : Merge pull request #474 from jarrodb/ipv6 resolve inet6 records for hosts and prefer them if dns_try_ipv6 is set 2015-04-25 Bogdan-Andrei Iancu * [4588f84] : Fix name of variable in passwd test 2015-04-25 Bogdan Andrei IANCU * [c7eb0d6] : Merge pull request #477 from satishdotpatel/patch-2 Fix reading password from prompt for PGSQL 2015-04-24 Liviu Chircu * [a51be51] : Merge pull request #481 from jarrodb/rest Populate the rcode pval in rest get/post methods before returning error 2015-04-24 Jarrod Baumann * [9945b9d] : Populate the rcode pval in rest get/post methods before returning error 2015-04-23 Ionut Ionita * [cd38248] : Merge pull request #478 from jarrodb/sqlite Resolves the core dump of issue #473 2015-04-23 Jarrod Baumann * [0f1576d] : Resolves the core dump of issue #473 2015-04-22 satishdotpatel * [e0ab1ce] : Fix reading password from prompt for PGSQ root@dopensips:/etc/opensips# opensipsdbctl create INFO: creating database opensips ... Password for user postgres: Password for user postgres: Password for user postgres: NOTICE: CREATE TABLE / UNIQUE will create implicit index "version_t_name_idx" for table "version" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "acc_id_seq" for serial column "acc.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "acc_pkey" for table "acc" NOTICE: CREATE TABLE will create implicit sequence "missed_calls_id_seq" for serial column "missed_calls.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "missed_calls_pkey" for table "missed_calls" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "domain_id_seq" for serial column "domain.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "domain_pkey" for table "domain" NOTICE: CREATE TABLE / UNIQUE will create implicit index "domain_domain_idx" for table "domain" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "grp_id_seq" for serial column "grp.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "grp_pkey" for table "grp" NOTICE: CREATE TABLE / UNIQUE will create implicit index "grp_account_group_idx" for table "grp" NOTICE: CREATE TABLE will create implicit sequence "re_grp_id_seq" for serial column "re_grp.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "re_grp_pkey" for table "re_grp" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "address_id_seq" for serial column "address.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "address_pkey" for table "address" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "aliases_id_seq" for serial column "aliases.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "aliases_pkey" for table "aliases" NOTICE: CREATE TABLE / UNIQUE will create implicit index "aliases_alias_idx" for table "aliases" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "location_id_seq" for serial column "location.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "location_pkey" for table "location" NOTICE: CREATE TABLE / UNIQUE will create implicit index "location_account_contact_idx" for table "location" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "silo_id_seq" for serial column "silo.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "silo_pkey" for table "silo" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "dbaliases_id_seq" for serial column "dbaliases.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dbaliases_pkey" for table "dbaliases" NOTICE: CREATE TABLE / UNIQUE will create implicit index "dbaliases_alias_idx" for table "dbaliases" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "uri_id_seq" for serial column "uri.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "uri_pkey" for table "uri" NOTICE: CREATE TABLE / UNIQUE will create implicit index "uri_account_idx" for table "uri" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "rtpproxy_sockets_id_seq" for serial column "rtpproxy_sockets.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "rtpproxy_sockets_pkey" for table "rtpproxy_sockets" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "speed_dial_id_seq" for serial column "speed_dial.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "speed_dial_pkey" for table "speed_dial" NOTICE: CREATE TABLE / UNIQUE will create implicit index "speed_dial_speed_dial_idx" for table "speed_dial" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "usr_preferences_id_seq" for serial column "usr_preferences.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "usr_preferences_pkey" for table "usr_preferences" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "subscriber_id_seq" for serial column "subscriber.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "subscriber_pkey" for table "subscriber" NOTICE: CREATE TABLE / UNIQUE will create implicit index "subscriber_account_idx" for table "subscriber" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "pdt_id_seq" for serial column "pdt.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pdt_pkey" for table "pdt" NOTICE: CREATE TABLE / UNIQUE will create implicit index "pdt_sdomain_prefix_idx" for table "pdt" Password for user postgres: NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dialog_pkey" for table "dialog" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "dispatcher_id_seq" for serial column "dispatcher.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dispatcher_pkey" for table "dispatcher" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "dialplan_id_seq" for serial column "dialplan.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dialplan_pkey" for table "dialplan" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "dr_gateways_id_seq" for serial column "dr_gateways.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dr_gateways_pkey" for table "dr_gateways" NOTICE: CREATE TABLE / UNIQUE will create implicit index "dr_gateways_dr_gw_idx" for table "dr_gateways" NOTICE: CREATE TABLE will create implicit sequence "dr_rules_ruleid_seq" for serial column "dr_rules.ruleid" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dr_rules_pkey" for table "dr_rules" NOTICE: CREATE TABLE will create implicit sequence "dr_carriers_id_seq" for serial column "dr_carriers.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dr_carriers_pkey" for table "dr_carriers" NOTICE: CREATE TABLE / UNIQUE will create implicit index "dr_carriers_dr_carrier_idx" for table "dr_carriers" NOTICE: CREATE TABLE will create implicit sequence "dr_groups_id_seq" for serial column "dr_groups.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dr_groups_pkey" for table "dr_groups" Password for user postgres: NOTICE: CREATE TABLE will create implicit sequence "load_balancer_id_seq" for serial column "load_balancer.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "load_balancer_pkey" for table "load_balancer" Password for user postgres: Password for user postgres: Password for user postgres: Password for user postgres: Password for user postgres: 2015-04-22 Jarrod Baumann * [101b3e4] : Reverted to original logic based on comments from @bogdan-iancu and @etamme but preferring v6 records if dns_try_ipv6 is enabled 2015-04-22 ionutrazvanionita * [52d3ec6] : fix escaping issue; two ways of binding values(using sqlite3_bind* interface or sqlite3_snprintf); removed unecessary code 2015-04-22 ionutrazvanionita * [91fc6cc] : added CON_SET_CURR_PS macro 2015-04-22 ionutrazvanionita * [0b6402c] : fixed url '\0' issue 2015-04-22 Liviu Chircu * [17d639b] : F_MALLOC: suppress warnings for free(NULL) 2015-04-21 Vlad Paiu * [138813f] : Fixed json lib dependency 2015-04-21 Razvan Crainea * [cb9f46b] : rtpproxy: store the notification buffer in the main stack frame Thanks go to Hamid Elaosta for reporting and helping me debug this bug 2015-04-21 Liviu Chircu * [0e80ba3] : proto_tcp: fix "microsec" -> "millisec" doc typo 2015-04-20 ionutrazvanionita * [b7db080] : don't log errors in case connection closed by the other peer; don't call SSL_shutdown when connection is bad/eof 2015-04-20 Liviu Chircu * [1f154c0] : db_mysql: properly set TCP-related timeouts According to official documentation, mysql_options() must be used _after_ mysql_init() 2015-04-20 Vlad Paiu * [57f331f] : Do not leak the dialog in case there's an error on creation, after linking 2015-04-20 Bogdan Andrei IANCU * [ead1033] : Merge pull request #442 from rgagnon24/redirect_fix Fix get_redirects(max, reason) 2015-04-20 Razvan Crainea * [c26a4a3] : rtpproxy: properly parse rtpproxy timeout notifications 2015-04-17 ionutrazvanionita * [a32b92a] : Merge branch 'jarrodb-sqlite' 2015-04-17 ionutrazvanionita * [a97d357] : modified and tested sqlite opensipsdbctl create, drop, reinit backup, restore, copy 2015-04-16 Jarrod Baumann * [40308f9] : Reset default /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin references back to original script (had copied from an already installed file) 2015-04-16 Jarrod Baumann * [0606435] : Fixed Makefile script sqlite references that move the database related files into the library path 2015-04-16 Jarrod Baumann * [5b557d0] : updated osipsconsolerc to match the opensipsctlrc header for the DBENGINE definition 2015-04-16 Jarrod Baumann * [bfde3a3] : Updated control scripts to include considerations for the new db_sqlite 2015-04-16 ionutrazvanionita * [24341a0] : fix extension loading; fix bigint separate case 2015-04-16 ionutrazvanionita * [7366daa] : redesigned sqlite module 2015-04-16 Razvan Crainea * [0a38032] : do not add Makefile.conf to the tarball Since every tarball install is independent, Makefile.conf should not be inherited 2015-04-15 Liviu Chircu * [00cdfc6] : hp_malloc.h: properly include sys/time.h Prevents compilation issues with some modules Credits to Hamid Elaosta for reporting 2015-04-15 Liviu Chircu * [008fba1] : fix compilation issues with no -DSTATISTICS 2015-04-15 Vlad Paiu * [3583989] : Set T to T_UNDEFINED (cherry picked from commit 6c0625e1f7218265f944d8ee69f7d8fb332b40de) 2015-04-15 Vlad Paiu * [eeff37f] : Set the current transaction to NULL when adding FD to reactor for async jump 2015-04-15 Bogdan-Andrei Iancu * [1ebf813] : Removed useless var setting. 2015-04-15 Bogdan-Andrei Iancu * [4884d6c] : Fix race condition on terminating child procs. There is a race condition between a child getting initalized (and properly setting all its vars like is_main, pid, etc) and main process trying to terminate that child proc (due a failed startup). Use the pt[].pid as a marker to know when the child is init (to be filled only by child). Main proc (when delivering signals) will do a busy waiting if a proc haven't set its pid (still doing init). Closes #463 2015-04-14 Bogdan-Andrei Iancu * [dbf2aec] : Fix dont_fork mode. Closes #445. 2015-04-14 Razvan Crainea * [b8c0740] : rr: do not force send socket on preloaded routes 2015-04-14 Vlad Paiu * [521bfc0] : Maintain rport and received in VIA header of replies when doing topology hiding 2015-04-14 Razvan Crainea * [3ac458f] : acc: always populate the created AVP 2015-04-14 Răzvan Crainea * [432c9cc] : Merge pull request #462 from jarrodb/dlg_hash Verify the dialog mi parameter is not null before passing to dlg_hash 2015-04-13 Jarrod Baumann * [c745068] : Verify the dialog mi parameter is not null before passing to dlg_hash 2015-04-10 ionutrazvanionita * [8d8ea6f] : fix: extension list free after first connect; when reconnect was done extensions were already freed; now free is done when conn is closed 2015-04-10 Razvan Crainea * [44fa562] : proto_ws: send error message before closing the connection Spotted by Rik Broers 2015-04-10 Razvan Crainea * [e252740] : proto_ws: store mask in request when msg is not complete Reported by Rik Broers 2015-04-09 ionutrazvanionita * [baa342d] : added dbschema scripts and tables 2015-04-09 Bogdan Andrei IANCU * [0fed048] : Merge pull request #456 from jarrodb/pike_rm Added pike mi command for unblocking IP addresses 2015-04-09 Razvan Crainea * [7e1bded] : tcp: drop CRLF TCP messages 2015-04-09 ionutrazvanionita * [7bedddd] : fixed buffer overflow check when query too big; fixed null value return; query string/blob value is now copied into opensips pkg memory 2015-04-09 Jarrod Baumann * [3a37fa4] : updated logic to reset the block flags and counters instead of actually removing the node from the tree 2015-04-09 Răzvan Crainea * [9dd26b8] : proto_ws: update doc to reflect WS client implementation 2015-04-09 Jarrod Baumann * [996c989] : Added simple section to the pike_admin.xml docs detailing the command 2015-04-09 Jarrod Baumann * [d506309] : Added pike mi command for unblocking IP addresses 2015-04-08 ionutrazvanionita * [8627946] : cc_calls: modified b2buaid to unique and put index on int; id is noi int primary key autoincrement 2015-04-08 ionutrazvanionita * [b2e2a62] : username comparation not necessary anymore on db_id 2015-04-08 ionutrazvanionita * [82648c2] : fixed memset bug when alloc; fixed select query detection in db_raw query; fixed null query parameter in raw_query; fixed lock error in wrapper_single_sqlite_stmt_prepare 2015-04-07 Vlad Paiu * [4cb4272] : Silence t_newtrani log error in case transaction was already created (cherry picked from commit 9e9818f86ca61cc22bf1273fd5eefda7a62e4ffc) 2015-04-07 Vlad Paiu * [210a096] : Do not crash when faked req has no lumps added to it 2015-04-06 Ovidiu Sas * [0a404a7] : nathelper: fix documentation typo 2015-04-05 Liviu Chircu * [445d90b] : sipmsgops: fix buffer management issue is_present_hf() and remove_hf() would sometimes misbehave when used with pseudo-variables (commit 6e88f653702). Reported by Eric Werkhoven Also fix a couple of noisy log messages. 2015-04-03 Ovidiu Sas * [1119c66] : Makefile.conf.template: add db_sqlite to the list of excluded modules 2015-04-03 ionutrazvanionita * [0a2be49] : introducing sqlite module 2015-04-03 ionutrazvanionita * [2682925] : added str_strstr function 2015-04-03 ionutrazvanionita * [2f192ca] : hold original url in id structure 2015-04-02 Bogdan-Andrei Iancu * [4a12950] : Better err logging (cherry picked from commit a9ea0a142fc0e18f36e17e5129f522407edcab80) 2015-04-02 Bogdan-Andrei Iancu * [ac8c2e6] : Fix changing proto via force_send_socket(). If the RURI does not request any protocol, use the proto of the forced socket (if any). Related to #420 2015-04-01 Liviu Chircu * [5a346da] : force_send_socket(): update error message on bad parameter The transport protocol is now mandatory Reported by @hydrosine Closes #447 2015-04-01 Liviu Chircu * [bc160a7] : flag fixups: fix a sign conversion bug Reported by @hydrosine Fixes #448 2015-04-01 Vlad Paiu * [d62bc96] : Accept TCP aliases by default Do not rely on the previous hop signaling alias via param 2015-03-31 Liviu Chircu * [54b3291] : Merge pull request #444 from etamme/master fixed flex sub version check for isatty definition 2015-03-31 Ovidiu Sas * [b52b2b9] : parser: fix memory leak - add Supported header to the list of headers that performs memory allocation 2015-03-30 Eric Tamme * [9393492] : fixed flex sub version check to include unistd.h for anything subversion 36 or higher 2015-03-30 Eric Tamme * [4015747] : fixed flex sub version check for isatty definition - isatty was removed in .36 2015-03-30 Bogdan-Andrei Iancu * [3da2b0d] : Fix documentation typo. Credits go to Antonis Psaras. 2015-03-30 Razvan Crainea * [4c5e226] : proto_ws: proper clen print 2015-03-30 Razvan Crainea * [5883393] : Add client support for websocket 2015-03-30 Razvan Crainea * [12467f2] : net expose tcp destroy 2015-03-30 Razvan Crainea * [6572930] : split tcp_create function in two this allows you to first create the connection, do stuff with it and only then release it 2015-03-27 Razvan Crainea * [9a4ae5c] : proto_ws: prevent buffer overflow when masking 2015-03-27 rgagnon24 * [53a1700] : Maintain "n/a" string if none supplied for backward compatibility 2015-03-27 rgagnon24 * [832295c] : Name prototype variables the same as function usage 2015-03-27 rgagnon24 * [f842752] : Remove whitespace change 2015-03-27 rgagnon24 * [a25753a] : Fix function prototypes and casts 2015-03-27 rgagnon24 * [ddbe64b] : Add editor, fix tags 2015-03-27 rgagnon24 * [2567b63] : Fix get_redirects(max, reason) so that reason is properly transferred to the acc module function 2015-03-25 ionutrazvanionita * [d29cd9d] : no warnings from emergency; modified list deletion logic; removed trailing whitespaces 2015-03-25 Razvan Crainea * [adc4455] : rename modules_obsolete to obsolete_modules this makes tree browsing easier using autocompletion 2015-03-24 Razvan Crainea * [e5b793e] : replace libmysqlclient*-dev with metapackage libmysqlclient-dev Credits go to Leo for reporting this 2015-03-24 Razvan Crainea * [a305152] : tcp & tls: preserve fd after connect succeeds Credits go to Peter Kelly for reporting this and helping with the testing 2015-03-24 Vlad Paiu * [558a4f1] : Fixed TLS CRLF pingpong regression Properly handle CRLFCRLF pings over TLS 2015-03-23 Vlad Paiu * [bfbb5bb] : Fixed TLS connection closing Update the SSL library with the TCP main socket before running SSL_Shutdown Thanks to Lirakis for the help in debugging this (cherry picked from commit 9ee50938482621e92074fd69cd5d0a078c47596b) 2015-03-23 ionutrazvanionita * [e5b7abc] : removed unecessary rootCA files 2015-03-23 Răzvan Crainea * [1dd83cf] : python: add documentation 2015-03-23 Liviu Chircu * [1fdb716] : Merge pull request #437 from rgagnon24/master RTPEngine.c cleanup 2015-03-23 Rob Gagnon * [715f62e] : Fix functions to match their documented API Change function prototypes to use gparam_p instead of char * 2015-03-23 Razvan Crainea * [593f269] : python: properly parse the script name 2015-03-23 Bogdan-Andrei Iancu * [8d953eb] : Have tcp_async by default ON. (cherry picked from commit 525bd70bef7de94a18bebaa61f6b18125598ebbd) 2015-03-23 Vlad Paiu * [712b75e] : opensipsdbctl : Fixed creating Postgres database (cherry picked from commit 3eb5f2bf9eb13451f621ced244c7d65ee40ae18c) Conflicts: scripts/opensipsdbctl.base 2015-03-23 Liviu Chircu * [eed1c0a] : fix typo in previous commit 2015-03-23 Liviu Chircu * [49ed85a] : opensipsdbctl: allow running from root dir for _all_ SQL backends (completes commit 31c472c) 2015-03-23 Razvan Crainea * [5092a46] : python: add more verbose error reporting Even though the errors are only reported at stderr, this is still useful for debugging. In the future we should find a way to log the errors at the logging facility 2015-03-21 root * [5a167bc] : subscriber function for emergency module 2015-03-20 Vlad Paiu * [8cc812d] : Added compression and emergency to excluded modules list They depend on zlib and respectively libcurl for compiling 2015-03-19 Bogdan-Andrei Iancu * [1337a5f] : Fix compile err/warn on redhat. Reported by Nick Altmann. Credits go to Liviu Chircu. (cherry picked from commit a1ea06b97a9212378d21456afc750ad8ed0b9d86) 2015-03-19 Vlad Paiu * [eb9a063] : Fixed msilo README - added mention of the extra route types the function can be called in Credits to Nick Altmann 2015-03-19 Razvan Crainea * [ab93408] : obsolete pdt module 2015-03-18 Bogdan Andrei IANCU * [ad6ef2c] : Merge pull request #435 from etamme/master fix rtpstat for rtpengine - thanks to jarrod for tracking down the fix 2015-03-18 Eric Tamme * [d10e679] : fix rtpstat for rtpengine - thanks to jarrod for tracking down the fix 2015-03-18 Vlad Paiu * [f276691] : Fix PKG mem leak 2015-03-18 Razvan Crainea * [dd81c75] : increase devel version 2015-03-18 Razvan Crainea * [936e93b] : update version name 2015-03-18 Razvan Crainea * [d1c151a] : update ChangeLog for 2.1 2015-03-18 Ovidiu Sas * [f1652d2] : mi_http: update copyright 2015-03-18 Ovidiu Sas * [b7e2137] : pi_http: fix compiler warning 2015-03-18 Vlad Paiu * [75c8154] : Fix route_to_carrier and route_to_gw Duplicate the RURI before pushing GWs for usage ( first gw will overwrite the actual URI ) 2015-03-18 Liviu Chircu * [3c6873c] : update acc SQL scripts opensips-2.2.2/INSTALL000066400000000000000000000512611300170765700144210ustar00rootroot00000000000000$Id$ =========================================== OpenSIPS Installation Notes http://www.opensips.org/ =========================================== This memo gives you hints how to set up OpenSIPS quickly. To understand how OpenSIPS works and how to configure it properly, read admin's guide available from OpenSIPS website. We also urge you to read latest ISSUES (available from OpenSIPS website too) and check for potential problems in this release. Users of previous releases are encouraged to read NEWS to learn how to move to this new OpenSIPS version. TOC 1. Supported Architectures and Requirements 2. Howto Build opensips From Source Distribution 3. Quick-Start Installation Guide A) Getting Help B) Disclaimers C) Quick Start D) opensips with Persistent Data Storage E) osipsconsole installation F) menuconfig installation 4. Troubleshooting 1. Supported Architectures and Requirements ------------------------------------------- Supported architectures: Linux/i386, Linux/armv4l, FreeBSD/i386, OpenBSD/i386 Solaris/sparc64, NetBSD/sparc64 (for other architectures the Makefiles might need to be edited) There are various configuration options defined in the Makefile. Requirements: - gcc / suncc / icc : gcc >= 2.9x; 4.[012] recommended (it will work with older version but it might require some options tweaking for best performance) - bison or yacc (Berkley yacc) - flex - GNU make (on Linux this is the standard "make", on FreeBSD and Solaris is called "gmake") version >= 3.79. - sed and tr (used in the makefiles) - GNU tar ("gtar" on Solaris) and gzip if you want "make tar" to work - GNU install or BSD install (on Solaris "ginstall") if you want "make install", "make bin", "make sunpkg" to work - openssl if you want to compile the TLS support - libsctp if you want to compile the SCTP support - libmysqlclient & libz (zlib) -libs and devel headers- if you want mysql DB support (the db_mysql module) - libpq / postgresql -libs and devel headers- if you want postgres DB support (the db_postgres module) - unixodbc -libs and devel headers- if you want unixodbc DB support (the db_unixodbc module) - libexpat if you want the jabber gateway support (the jabber module) or the XMPP gateway support - libxml2 if you want to use the cpl_c (Call Processing Language) or the presence modules (presence and pua*) - libradius-ng -libs and devel headers- if you want to use functionalities with radius support - authentication, accounting, group support, etc - unixodbc - libs and devel headers - if you want UNIXODBC support as DB underlayer - libxmlrpc-c3 - libs and devel headers - if you want to have XML-RPC support for the Management interface (MI) - libperl - libs and devel headers - if you want PERL connector to support perl scripting from you config file (perl module) - libsnmp9 - libs and devel headers - if you want SNMP client functionality (SNMP AgentX subagent) for opensips - libldap libs and devel headers v2.1 or greater - if you want LDAP support - libconfuse and devel headers - if you want to compile the carrierroute module - libncurses5-dev and m4 - if you want to use the menuconfig graphical user interface for configuring OpenSIPS compilation & cfg file options OS Notes: - FreeBSD/OpenBSD/NetBSD: make sure gmake, bison or yacc & flex are installed - Solaris: as above; you can use Solaris's yacc instead of bison. You might need also gtar and ginstall. 2. Howto Build opensips From Source Distribution ------------------------------------------- (NOTE: if make doesn't work try gmake instead) - compile with default options (TLS support is enabled by "TLS=1"; SCTP support is enabled by "SCTP=1"): make #builds only opensips core, equivalent to make opensips make modules or make all #builds everything -compile debug mode version make mode=debug all -compile only the textops module make modules=modules/textops modules -compile all the "default" modules except textops and db_mysql make skip_modules="textops db_mysql" modules -compile all default modules and include uri_radius (not compiled by default): make include_modules="uri_radius" modules -compile all the modules from the modules subdirectory (even the one excluded by default): make exclude_modules="" modules -compile all the modules from the modules subdirectory excluding exec: make exclude_modules=exec modules or make exclude_modules="" skip_modules=exec modules -generate README file for textops module make modules=modules/textops modules-readme -compile with gcc-3.2 instead of gcc make CC=gcc-3.2 all or CC=gcc-3.2 make all Make targets: Clean: make clean (clean the modules too) make proper (clean also the dependencies) make distclean (the same as proper) make mantainer-clean (clean everything, including auto generated files, tags, *.dbg a.s.o) Compile: make proper make (or gmake on non-Linux systems) make modules or make modules exclude_modules="exec" etc. Make tags: make TAGS Create a tar.gz with the sources (in ../): make tar Create a tar.gz with the binary distribution (in ../): make bin Create a gzipped solaris package (in ../): make sunpkg Create debian packages (in ../): make deb or dpkg-buildpackage Install: make prefix=/usr/local install Note: If you use prefix parameter in make install then you also need to use this parameter in previous make commands, i.e. make, make modules, or make all. If you fail to do this then OpenSIPS will look for the default configuration file in a wrong directory, because the directory of the default configuration file is hard coded into opensips during compile time. When you use a different prefix parameter when installing then the directory hard coded in opensips and the directory in which the file will be installed by make install will not match. (You can specify exact location of the configuration file using -f parameter of opensips). For example, if you do the following: make all make prefix=/ install Then the installation will put the default configuration file into /etc/opensips/opensips.cfg (because prefix is /), but opensips will look for the file in /usr/local/etc/opensips/opensips.cfg (because there was no prefix parameter in make all and /usr/local is the default value of prefix). Workaround is trivial, use the same parameters in all make commands: make prefix=/ all make prefix=/ install That applies to other make parameters as well (for example parameters "modules" or "excluded_modules"). Start graphical user interface: make menuconfig 3. Quick-Start Installation Guide ---------------------------------------------- A) Getting Help This guide gives you instructions on how to quickly set up OpenSIPS on your box. In case the default configuration does not fly, check documentation at opensips site http://www.opensips.org/ to learn how to configure OpenSIPS for your site. If the documentation does not resolve your problem you may try contacting our user forum by E-mail at users@opensips.org -- that is the mailing list of opensips community. To participate in the mailing list, subscribe at the following web address: http://www.opensips.org/cgi-bin/mailman/listinfo/users B) Disclaimers Note well the default "quick-start" configuration is very simple in order to be easily installable. It provides minimum features. Particularly, authentication is by default disabled, which means anyone can register using any name with the server. (This is on purpose to avoid installation dependencies on MySQL which is needed for storing user credentials.) C) Quick Start The following step-by step guide gives you instructions how to install the sql-free distribution of opensips. If you need persistence and authentication, then you have to install additional MySql support -- proceed to section D) after you are finished with C). 1) Download an RPM or debian package from our site http://opensips.org/pub/opensips/latest/packages/ If you don't use an rpm or debian based distribution, see if corresponding packages are available or try our tar.gz'ed binaries. If you use Gentoo Linux you do not have to download a package. For debian, packages are available via the Debian official repositories for testing and unstable. For stable, use the project's repository at: deb http://www.opensips.org/debian stable main 2) install the package RPM: rpm -i debian: dpkg -i or if APT repository is used: apt-get install gentoo: emerge opensips (or if use only stable packets: ACCEPT_KEYWORDS="~x86" emerge opensips) tar.gz: cd /; tar zxvf _os_arch.tar.gz (it will install in /usr/local/, and the configuration file in /usr/local/etc/opensips/opensips.cfg) Solaris: gunzip .gz ; pkgadd -d *BSD: pkg_add package_name Note that the OpenSIPS package is in the FreeBSD package tree included, but is not present in the OpenBSD and NetBSD repository. You'll probably have more luck trying to build directly from the source with the tar.gz, as the package files for this systems are somewhat out of date at the moment. 3) start the server RPM + gentoo: /etc/init.d/opensips start debian: opensips is started automatically after the install (in case something fails you can start it with /etc/init.d/opensips start) tar.gz: the tar.gz does not include an init.d script, you'll have to create one of your own or adapt one from the source distribution (debian/init.d, rpm/opensips.init.*, gentoo/opensips.init) You can start opensips directly with /usr/local/sbin/opensips. Solaris: see tar.gz. 4) optionally, watch server's health using the opensipsctl utility - to do so, first set the environment variable SIP_DOMAIN to your domain name, e.g., in Bourne shell, call export SIP_DOMAIN="myserver.foobar.com" - if you are using other than 'localhost' mysql server for maintaining subscriber database, change the variable 'SQL_HOST' to the proper host name in the opensipsctl script - run the opensipsctl utility /usr/sbin/opensipsctl moni or /usr/local/sbin/opensipsserctl moni (if you installed from a tar.gz or solaris package) - you can create a resource file for opensipsctl, name it .opensipsctlrc and place it in your home directory. You can set there the values for opensipsctl variables (e.g., SIP_DOMAIN, SQL_HOST, SQL_USER, SQL_DB ...) 5) Register with the server using your favourite SIP User Agent. For example, users of Windows Messenger need to set in Tools->Options->Accounts the following values: Sign-in Name: @ Advanced->Configure Settings (on) Advanced->Server: Connect Using: UDP D) opensips with Persistent Data Storage The default configuration is very simple and features many simplifications. In particular, it does not authenticate users and loses User Location database on reboot. To provide persistence, keep user credentials and remember users' locations across reboots, opensips can be configured to use MySQL. Before you proceed, you need to make sure MySQL is installed on your box. 1) Download the package containing mysql support for opensips from: http://www.opensips.org/pub/opensips/ (rpm and deb provided, most of the binary tar.gz distributions and the solaris package include it; if it is not present you'll have to rebuild from the source). For gentoo please include 'mysql' to your USE variable in /etc/make.conf or give it as variable to the emerge command. 2) install the package RPM based: rpm -i DEB based: dpkg -i or if APT repository is used apt-get install Gentoo Linux: emerge opensips (if do not want to put 'mysql' into your USE variable you can type: USE="mysql" emerge opensips) 3) create SQL tables You must specify your database type in the /etc/opensipsctlrc file, e.g. MySQL. See section 7 for an explanation of further possible parameters. - if you have a previously installed OpenSIPS on your system, use /usr/sbin/opensipsdbctl migrate to convert your OpenSIPS database into new structures NOTE: "migrate" is available only for mysql DBs - otherwise, if this is your very first installation, use /usr/sbin/opensipsdbctl create to create OpenSIPS database structures (you will be prompted for password of MySQL "root" user) 4) configure opensips to use SQL uncomment all lines in configuration file opensips.cfg which are related to authentication: - loadmodule "/usr/lib/opensips/modules/db_mysql.so" - loadmodule "/usr/lib/opensips/modules/auth.so" - loadmodule "/usr/lib/opensips/modules/auth_db.so" - modparam("usrloc", "db_mode", 2) - modparam("auth", "calculate_ha1", yes) - modparam("auth_db", "password_column", "password") - if (!www_authorize("sip.org", "subscriber")) { www_challenge("sip.org", "0"); break; }; 5) be sure to replace realm, the first parameter in www_* actions, with name of your server; some broken UAC implementations don't authenticate otherwise; the authentication command in your configuration script should look then like this: if (!www_authorize("myserver.foobar.com", "subscriber")) { www_challenge("myserver.foobar.com", "0"); break; } 6) restart the server /etc/init.d/opensips restart 7) you can now start managing the server using the opensipsctl utility; you need to first set the environment variable SIP_DOMAIN to your local SIP realm, e.g., export SIP_DOMAIN="myserver.foobar.com" or you can configure via the resource file for opensipsctlrc. The default file is installed in the etc/ directory of your installation (along with the OpenSIPS config file). For per user configuration, create .opensipsctlrc in your home directory. You can set there the values for opensipsctl variables like: SIP_DOMAIN - your SIP domain DBENGINE - database type: MYSQL, PGSQL or DBTEXT by default none is loaded DBHOST - database host DBNAME - database name DBRWUSER - database read/write user DBROUSER - database read only user DBROPW - password for database read only user DBROOTUSER - database super user ALIASES_TYPE - type of aliases used: DB - database aliases UL - usrloc aliases default none CTLENGINE - control engine: FIFO or UNIXSOCK OSIPS_FIFO - path to FIFO file VERBOSE - verbose - debug purposes - default '0' a) watch the server status using 'opensipsctl moni' b) try to login with your SIP client as user 'admin' with password 'opensipsrw' c) try adding new users using 'opensipsctl add ' 8) default values (database url, users and passwords) are: - db_default_url="mysql://opensips:opensipsrw@localhost/opensips" - r/w user: opensips ; passwd: opensipsrw - r/o user: opensipsro ; passwd: opensipsro VERY IMPORTANT NOTE: for security reasons, do change the values of passwords after installation E) osipsconsole installation 1) Perl requirements For the osipsconsole tool the following perl modules are needed: DBI DBD::mysql DBD::Pg DBD::Oracle BerkeleyDB Frontier::RPC2 2) Debian/Ubuntu Installation In order for the script to work, there are listed bellow the modules needed and their debian package correspondent, a brief description of each package and whether it is mandatory to install it or not. ( To be mentioned the fact that these packages have been tested in Debian and Ubuntu distros. ) Perl module | Debian Package | Description | Use --------------------------------------------------------------------------- perl |perl |Practical Extraction | mandatory | |and Report Language | DBI |libdbi-perl |Perl database interface| mandatory DBD::mysql |libdbd-mysql-perl |MySQL driver for the |*optional | |Perl5 DBI | DBD::Pg |libdbd-pg-perl |PostgreSQL database |*optional | |driver for DBI module | **DBD::Oracle | ------ |Oracle database driver |*optional | |for the DBI module | Frontier::RPC2 |libfrontier-rpc-perl |encode/decode RPC2 | mandatory | |format XML | Term::ReadLine::Gnu|libterm-readline-gnu-per|Perl extension for the | mandatory | |GNU Readline/History Lib| BerkeleyDB |libberkeleydb-perl |Perl extension for | mandatory | |Berkeley database | | |version 2, 3 or 4 | Install these packages using apt-get: apt-get install perl libdbi-perl libdbd-mysql-perl libdbd-pg-perl libfrontier-rpc-perl libterm-readline-gnu-perl libberkeleydb-perl NOTE: * packages are at the user's choice. At least one of them is mandatory, depending on the DB type used by OpenSIPS. The other ones have no need of being installed. ** For the DBD::Oracle module there is no debian package. This will be installed as follows: - Download the .tar.gz archive from: http://search.cpan.org/~pythian/DBD-Oracle-1.22/Oracle.pm - Decompress and unpack it - cd DBD-Oracle-1.22 - perl Makefile.PL - make - make test - make install F) menuconfig installation The Interface allows the user to do the following : 1. Configure OpenSIPS compilation related options such as : - Compilation Flags. For example, the user can now easily compile in TCP support from within the GUI, or enable memory allocation debugging, etc. Each compilation flag functionality is explained in short in the GUI. - Module Compilation Selection. The user can now easily select to compile modules that have external dependencies, and that are not compiled in by default. For example, the user can choose to also enable the MySQL support by enabling the db_mysql module. The interface will also notify the user about the dependencies that must be installed based on the modules that the user has selected. - Installation Prefix. The user can use the GUI to configure the OpenSIPS installation path to be used 2. Install OpenSIPS and Cleanup OpenSIPS sources - Upon configuring OpenSIPS related options from above, the user can choose to install OpenSIPS directly from the GUI. 3. Generate OpenSIPS config files - The tool can also generate OpenSIPS configuration files based on the user's preferences. So far, we have defined three main classes of OpenSIPS configuration files : - Residential - Trunking - Load-balancer For each type of configuration file, the user can choose to enable/disable certain options. For example, for the Residential script, the user can choose to enable presence support, to handle NAT, and many more. After the user has properly configured it's desired OpenSIPS script in the GUI, it will have the option to generate and obtain the final OpenSIPS cfg. If you have installed OpenSIPS from packages ( debs, rpms, etc ) and not from sources, you will still be able to use the graphical interface for generating configuration files, by running osipsconfig 4. Troubleshooting ------------------ Q: SIP requests are replied by opensips with "483 Too Many Hops" or "513 Message Too Large" A: In both cases, the reason is probably an error in request routing script which caused an infinite loop. You can easily verify whether this happens by watching SIP traffic on loopback interface. A typical reason for misrouting is a failure to match local domain correctly. If a server fails to recognize a request for itself, it will try to forward it to current URI in believe it would forward them to a foreign domain. Alas, it forwards the request to itself again. This continues to happen until value of max_forwards header field reaches zero or the request grows too big. Solutions is easy: make sure that domain matching is correctly configured. A quick way to achieve that is to introduce a config option to opensips.cfg: alias=domainname, where domainname shall be replaced with name of domain, which you wish to server and which appears in request-URIs. opensips-2.2.2/Makefile000066400000000000000000000542571300170765700150400ustar00rootroot00000000000000# # OpenSIPS makefile # # WARNING: requires gmake (GNU Make) # Arch supported: Linux, FreeBSD, SunOS (tested on Solaris 8), OpenBSD (3.2), # NetBSD (1.6). # # History: # -------- # created by andrei # 2003-02-24 make install no longer overwrites opensips.cfg - patch provided # by Maxim Sobolev and # Tomas Bjoerklund # 2003-03-11 PREFIX & LOCALBASE must also be exported (andrei) # 2003-04-07 hacked to work with solaris install (andrei) # 2003-04-17 exclude modules overwritable from env. or cmd. line, # added include_modules and skip_modules (andrei) # 2003-05-30 added extra_defs & EXTRA_DEFS # Makefile.defs force-included to allow recursive make # calls -- see comment (andrei) # 2003-06-02 make tar changes -- unpacks in $NAME-$RELEASE (andrei) # 2003-06-03 make install-cfg will properly replace the module path # in the cfg (re: /usr/.*lib/opensips/modules) # opensips.cfg.default is installed only if there is a previous # cfg. -- fixes packages containing opensips.cfg.default (andrei) # 2003-08-29 install-modules-doc split from install-doc, added # install-modules-all, removed README.cfg (andrei) # added skip_cfg_install (andrei) # 2004-09-02 install-man will automatically "fix" the path of the files # referred in the man pages # 2007-09-28 added db_berkeley (wiquan) # #FREERADIUS=1 # freeradius libs check (must be done in toplevel makefile) ifneq ("$(wildcard /usr/include/freeradius-client.h)","") FREERADIUS=1 else #FREERADIUS=0 endif #SQLITE_BIND=1 NICER?=1 auto_gen=lex.yy.c cfg.tab.c #lexx, yacc etc # whether or not to install opensips.cfg or just opensips.cfg.default # (opensips.cfg will never be overwritten by make install, this is useful # when creating packages) skip_cfg_install?= #extra modules to exclude skip_modules?= # whether or not to overwrite TLS certificates tls_overwrite_certs?= makefile_defs=0 DEFS:= DEBUG_PARSER?= # json libs check ifneq ("$(wildcard /usr/include/json-c/json.h)","") DEFS += -I/usr/include/json-c else DEFS += -I/usr/include/json endif # create the template only if the file is not yet created ifeq (,$(wildcard Makefile.conf)) $(shell cp Makefile.conf.template Makefile.conf) endif include Makefile.conf include Makefile.sources include Makefile.defs # always exclude the SVN dir override exclude_modules+= .svn $(skip_modules) #always include this modules #include_modules?= # first 2 lines are excluded because of the experimental or incomplete # status of the modules # the rest is excluded because it depends on external libraries # static_modules= static_modules_path=$(addprefix modules/, $(static_modules)) extra_sources=$(wildcard $(addsuffix /*.c, $(static_modules_path))) extra_objs=$(extra_sources:.c=.o) static_defs=$(foreach mod, $(static_modules), \ -DSTATIC_$(shell echo $(mod) | tr [:lower:] [:upper:]) ) override extra_defs+=$(static_defs) $(EXTRA_DEFS) export extra_defs # If modules is supplied, only do those. If not, use all modules when # building documentation. ifeq ($(modules),) doc_modules=$(all_modules) else doc_modules=$(modules) endif # Take subset of all modules, excluding the exclude_modules and the # static_modules. modules=$(filter-out $(addprefix modules/, \ $(exclude_modules) $(static_modules)), \ $(wildcard modules/*)) # Let modules consist of modules and include_modules (but remove # duplicates). modules:=$(filter-out $(modules), $(addprefix modules/, $(include_modules) )) \ $(modules) ifneq ($(module),) modules:=$(addprefix modules/, $(module)) endif modules_names=$(patsubst modules/%, %.so, $(modules)) modules_basenames=$(patsubst modules/%, %, $(modules)) modules_full_path=$(join $(modules), $(addprefix /, $(modules_names))) ALLDEP=Makefile Makefile.sources Makefile.defs Makefile.rules Makefile.conf install_docs := README-MODULES AUTHORS NEWS README ifneq ($(skip-install-doc),yes) install_docs += INSTALL endif #include general defs (like CC, CFLAGS a.s.o) # hack to force makefile.defs re-inclusion (needed when make calls itself with # other options -- e.g. make bin) #DEFS:= #include Makefile.defs NAME=$(MAIN_NAME) #export relevant variables to the sub-makes export DEFS PROFILE CC LD MKDEP MKTAGS CFLAGS LDFLAGS MOD_CFLAGS MOD_LDFLAGS export LIBS RADIUS_LIB export LEX LEX_FLAGS YACC YACC_FLAGS export PREFIX LOCALBASE SYSBASE # export relevant variables for recursive calls of this makefile # (e.g. make deb) #export LIBS #export TAR export NAME RELEASE OS ARCH export cfg_prefix cfg_dir bin_prefix bin_dir modules_prefix modules_dir export doc_prefix doc_dir man_prefix man_dir ut_prefix ut_dir lib_dir export cfg_target modules_target data_dir data_prefix data_target export INSTALL INSTALL_CFG INSTALL_BIN INSTALL_MODULES INSTALL_DOC INSTALL_MAN export INSTALL_TOUCH # extra excludes for tar tar_extra_args+= # include the common rules include Makefile.rules #extra targets $(NAME): $(extra_objs) # static_modules lex.yy.c: cfg.lex cfg.tab.h $(ALLDEP) ifeq (,$(FASTER)) @echo "Generating lexer" endif $(Q)$(LEX) $(LEX_FLAGS) $< cfg.tab.c cfg.tab.h: cfg.y $(ALLDEP) ifeq (,$(FASTER)) @echo "Generating parser" endif $(Q)$(YACC) $(YACC_FLAGS) $< .PHONY: all all: $(NAME) modules utils .PHONY: app app: $(NAME) .PHONY: _modules _modules: $(modules) .PHONY: $(modules) $(modules): @$(MAKE) --no-print-directory -C $@ && \ echo "Building $(notdir $@) module succeeded" || (\ status=$$?; \ echo "ERROR: Building $(notdir $@) module failed!"; \ exit $$status; \ ) .PHONY: modules modules: ifeq (,$(FASTER)) @set -e; \ for r in $(modules) "" ; do \ if [ -n "$$r" ]; then \ if [ -d "$$r" ]; then \ echo "" ; \ echo "" ; \ $(MAKE) -j -C $$r ; \ fi ; \ fi ; \ done else @$(MAKE) _modules || ( \ status=$$?; \ if echo $(MAKEFLAGS) | grep -q -- --jobserver; then \ printf '\nBuilding one or more modules failed!\n'; \ printf 'Please re-run make without -j / --jobs to find out which.\n\n'; \ fi; \ exit $$status \ ) endif .PHONY: tool-docbook2pdf tool-docbook2pdf: @if [ -z "$(DBXML2PDF)" ]; then \ echo "error: docbook2pdf not found"; exit 1; \ fi .PHONY: tool-lynx tool-lynx: @if [ -z "$(DBHTML2TXT)" ]; then \ echo "error: lynx not found"; exit 1; \ fi .PHONY: tool-xsltproc tool-xsltproc: @if [ -z "$(DBXML2HTML)" ]; then \ echo "error: xsltproc not found"; exit 1; \ fi @if [ -z "$(DBHTMLXSL)" ]; then \ echo "error: docbook.xsl not found (docbook-xsl)"; exit 1; \ fi .PHONY: modules-readme modules-readme: tool-lynx tool-xsltproc @set -e; \ for mod in $(doc_modules); do \ r=`basename $$mod`;\ echo "Reading directory $$mod for module $$r";\ if [ ! -d "$$mod/doc" ]; then \ continue; \ fi; \ cd "$$mod/doc"; \ if [ -f "$$r".xml ]; then \ echo "docbook xml to html: $$r.xml"; \ $(DBXML2HTML) -o $$r.html $(DBXML2HTMLPARAMS) $(DBHTMLXSL) \ $$r.xml; \ echo "docbook html to txt: $$r.html"; \ $(DBHTML2TXT) $(DBHTML2TXTPARAMS) $$r.html >$$r.txt; \ echo "docbook txt to readme: $$r.txt"; \ rm $$r.html; \ mv $$r.txt ../README; \ echo ""; \ fi; \ cd ../../..; \ done .PHONY: modules-docbook-txt modules-docbook-txt: tool-lynx tool-xsltproc @set -e; \ for mod in $(doc_modules); do \ r=`basename $$mod`;\ echo "Reading directory $$mod for module $$r";\ if [ ! -d "$$mod/doc" ]; then \ continue; \ fi; \ cd "$$mod/doc"; \ if [ -f "$$r".xml ]; then \ echo ""; \ echo "docbook xml to html: $$r.xml"; \ $(DBXML2HTML) -o $$r.html $(DBXML2HTMLPARAMS) $(DBHTMLXSL) \ $$r.xml; \ echo "docbook html to txt: $$r.html"; \ $(DBHTML2TXT) $(DBHTML2TXTPARAMS) $$r.html >$$r.txt; \ rm $$r.html; \ echo ""; \ fi; \ cd ../../..; \ done .PHONY: modules-docbook-html modules-docbook-html: tool-xsltproc @set -e; \ for mod in $(doc_modules); do \ r=`basename $$mod`;\ echo "Reading directory $$mod for module $$r";\ if [ ! -d "$$mod/doc" ]; then \ continue; \ fi; \ cd "$$mod/doc"; \ if [ -f "$$r".xml ]; then \ echo ""; \ echo "docbook xml to html: $$r.xml"; \ $(DBXML2HTML) -o $$r.html $(DBXML2HTMLPARAMS) $(DBHTMLXSL) \ $$r.xml; \ echo ""; \ fi; \ cd ../../..; \ done .PHONY: modules-docbook-pdf modules-docbook-pdf: tool-docbook2pdf @set -e; \ for mod in $(doc_modules); do \ r=`basename $$mod`;\ echo "Reading directory $$mod for module $$r";\ if [ ! -d "$$mod/doc" ]; then \ continue; \ fi; \ cd "$$mod/doc"; \ if [ -f "$$r".xml ]; then \ echo ""; \ echo "docbook xml to pdf: $$r.xml"; \ $(DBXML2PDF) "$$r".xml; \ fi; \ cd ../../..; \ done .PHONY: modules-docbook modules-docbook: modules-docbook-txt modules-docbook-html modules-docbook-pdf .PHONY: dbschema-docbook-txt dbschema-docbook-txt: dbschema @set -e; \ for r in $(wildcard doc/database/*.sgml) "" ; do \ if [ -f "$$r" ]; then \ echo "" ; \ echo "docbook2txt $$r" ; \ docbook2txt -o "doc/database/" "$$r" ; \ fi ; \ done .PHONY: dbschema-docbook-html dbschema-docbook-html: dbschema @set -e; \ for r in $(wildcard doc/database/*.sgml) "" ; do \ if [ -f "$$r" ]; then \ echo "" ; \ echo "docbook2html $$r" ; \ docbook2html --nochunks -o "doc/database/" "$$r" ; \ fi ; \ done .PHONY: dbschema-docbook-pdf dbschema-docbook-pdf: dbschema @set -e; \ for r in $(wildcard doc/database/*.sgml) "" ; do \ if [ -f "$$r" ]; then \ echo "" ; \ echo "docbook2pdf $$r" ; \ docbook2pdf -o "doc/database/" "$$r" ; \ fi ; \ done .PHONY: dbschema-docbook dbschema-docbook: dbschema-docbook-txt dbschema-docbook-html dbschema-docbook-pdf $(extra_objs): -@echo "Extra objs: $(extra_objs)" @set -e; \ for r in $(static_modules_path) "" ; do \ if [ -n "$$r" ]; then \ echo "" ; \ echo "Making static module $r" ; \ $(MAKE) -C $$r static ; \ fi ; \ done dbg: $(NAME) gdb -command debug.gdb .PHONY: tar .PHONY: dist dist: tar tar: $(NEWREVISION) $(TAR) -C .. \ --exclude=$(notdir $(CURDIR))/tmp* \ --exclude=$(notdir $(CURDIR))/debian* \ --exclude=.svn* \ --exclude=.git \ --exclude=.gitignore \ --exclude=Makefile.conf \ --exclude=*.[do] \ --exclude=*.so \ --exclude=*.il \ --exclude=$(notdir $(CURDIR))/$(NAME) \ --exclude=*.gz \ --exclude=*.bz2 \ --exclude=*.tar \ --exclude=*.patch \ --exclude=.\#* \ --exclude=*.swp \ --exclude=*~ \ ${tar_extra_args} \ -cf - $(notdir $(CURDIR)) | \ (mkdir -p tmp/_tar1; mkdir -p tmp/_tar2 ; \ cd tmp/_tar1; $(TAR) -xf - ) && \ mv tmp/_tar1/$(notdir $(CURDIR)) \ tmp/_tar2/"$(NAME)-$(RELEASE)" && \ (cd tmp/_tar2 && $(TAR) \ -zcf ../../"$(NAME)-$(RELEASE)_src".tar.gz \ "$(NAME)-$(RELEASE)" ) ; \ rm -rf tmp/_tar1; rm -rf tmp/_tar2 # binary dist. tar.gz .PHONY: bin bin: mkdir -p tmp/$(NAME)/usr/local $(MAKE) install basedir=tmp/$(NAME) prefix=/usr/local $(TAR) -C tmp/$(NAME)/ -zcf ../$(NAME)-$(RELEASE)_$(OS)_$(ARCH).tar.gz . rm -rf tmp/$(NAME) .PHONY: deb-orig-tar deb-orig-tar: tar mv "$(NAME)-$(RELEASE)_src".tar.gz ../$(NAME)_$(RELEASE).orig.tar.gz .PHONY: deb-% deb-%: rm -rf debian # dpkg-source cannot use links for debian source cp -r packaging/debian/$(@:deb-%=%) debian dpkg-buildpackage \ -I.git -I.gitignore \ -I*.swp -I*~ \ -i\\.git\|debian\|^\\.\\w+\\.swp\|lex\\.yy\\.c\|cfg\\.tab\\.\(c\|h\)\|\\w+\\.patch \ -rfakeroot -tc $(DEBBUILD_EXTRA_OPTIONS) rm -rf debian .PHONY: deb deb: deb-common .PHONY: sunpkg sunpkg: mkdir -p tmp/$(NAME) mkdir -p tmp/$(NAME)_sun_pkg $(MAKE) install basedir=tmp/$(NAME) prefix=/usr/local (cd packaging/solaris; \ pkgmk -r ../../tmp/$(NAME)/usr/local -o -d ../../tmp/$(NAME)_sun_pkg/ -v "$(RELEASE)" ;\ cd ../..) cat /dev/null > ../$(NAME)-$(RELEASE)-$(OS)-$(ARCH)-local pkgtrans -s tmp/$(NAME)_sun_pkg/ ../$(NAME)-$(RELEASE)-$(OS)-$(ARCH)-local \ OpenSIPS gzip -9 ../$(NAME)-$(RELEASE)-$(OS)-$(ARCH)-local rm -rf tmp/$(NAME) rm -rf tmp/$(NAME)_sun_pkg .PHONY: install-app install-modules-all install # Install app only, excluding console, modules and module docs install-app: app mk-install-dirs install-cfg install-bin \ install-app-doc install-man # Install all module stuff (except modules-docbook?) install-modules-all: install-modules install-modules-doc # Install everything (except modules-docbook?) install: install-app install-console install-modules-all opensipsmc: $(cfg_prefix)/$(cfg_dir) $(data_prefix)/$(data_dir) $(MAKE) -C menuconfig proper $(MAKE) -C menuconfig \ MENUCONFIG_CFG_PATH=$(data_target)/menuconfig_templates/ \ MENUCONFIG_GEN_PATH=$(cfg_target) MENUCONFIG_HAVE_SOURCES=0 mkdir -p $(data_prefix)/$(data_dir)/menuconfig_templates/ $(INSTALL_TOUCH) menuconfig/configs/* $(data_prefix)/$(data_dir)/menuconfig_templates/ $(INSTALL_CFG) menuconfig/configs/* $(data_prefix)/$(data_dir)/menuconfig_templates/ sed -i -e "s#/usr/.*lib/$(NAME)/modules/#$(modules_target)#" \ $(data_prefix)/$(data_dir)/menuconfig_templates/* .PHONY: dbschema dbschema: -@echo "Build database schemas" $(MAKE) -C db/schema -@echo "Done" mk-install-dirs: $(cfg_prefix)/$(cfg_dir) $(bin_prefix)/$(bin_dir) \ $(modules_prefix)/$(modules_dir) $(doc_prefix)/$(doc_dir) \ $(man_prefix)/$(man_dir)/man8 $(man_prefix)/$(man_dir)/man5 \ $(data_prefix)/$(data_dir) # note: on solaris 8 sed: ? or \(...\)* (a.s.o) do not work install-cfg: $(cfg_prefix)/$(cfg_dir) sed -e "s#/usr/.*lib/$(NAME)/modules/#$(modules_target)#g" \ < etc/$(NAME).cfg > $(cfg_prefix)/$(cfg_dir)$(NAME).cfg.sample0 sed -e "s#/usr/.*etc/$(NAME)/tls/#$(cfg_target)tls/#g" \ < $(cfg_prefix)/$(cfg_dir)$(NAME).cfg.sample0 \ > $(cfg_prefix)/$(cfg_dir)$(NAME).cfg.sample rm -fr $(cfg_prefix)/$(cfg_dir)$(NAME).cfg.sample0 chmod 600 $(cfg_prefix)/$(cfg_dir)$(NAME).cfg.sample chmod 700 $(cfg_prefix)/$(cfg_dir) if [ -z "${skip_cfg_install}" -a \ ! -f $(cfg_prefix)/$(cfg_dir)$(NAME).cfg ]; then \ mv -f $(cfg_prefix)/$(cfg_dir)$(NAME).cfg.sample \ $(cfg_prefix)/$(cfg_dir)$(NAME).cfg; \ fi # opensipsctl config $(INSTALL_TOUCH) $(cfg_prefix)/$(cfg_dir)/opensipsctlrc.sample $(INSTALL_CFG) scripts/opensipsctlrc \ $(cfg_prefix)/$(cfg_dir)/opensipsctlrc.sample if [ ! -f $(cfg_prefix)/$(cfg_dir)/opensipsctlrc ]; then \ mv -f $(cfg_prefix)/$(cfg_dir)/opensipsctlrc.sample \ $(cfg_prefix)/$(cfg_dir)/opensipsctlrc; \ fi # osipsconsole config $(INSTALL_TOUCH) $(cfg_prefix)/$(cfg_dir)/osipsconsolerc.sample $(INSTALL_CFG) scripts/osipsconsolerc \ $(cfg_prefix)/$(cfg_dir)/osipsconsolerc.sample if [ ! -f $(cfg_prefix)/$(cfg_dir)/osipsconsolerc ]; then \ mv -f $(cfg_prefix)/$(cfg_dir)/osipsconsolerc.sample \ $(cfg_prefix)/$(cfg_dir)/osipsconsolerc; \ fi install-console: $(bin_prefix)/$(bin_dir) # install osipsconsole cat scripts/osipsconsole | \ sed -e "s#PATH_BIN[ \t]*=[ \t]*\"\./\"#PATH_BIN = \"$(bin-target)\"#g" | \ sed -e "s#PATH_CTLRC[ \t]*=[ \t]*\"\./scripts/\"#PATH_CTLRC = \"$(cfg_target)\"#g" | \ sed -e "s#PATH_LIBS[ \t]*=[ \t]*\"\./scripts/\"#PATH_LIBS = \"$(lib-target)/opensipsctl/\"#g" | \ sed -e "s#PATH_SHARE[ \t]*=[ \t]*\"\./scripts/\"#PATH_SHARE = \"$(data_target)\"#g" | \ sed -e "s#PATH_ETC[ \t]*=[ \t]*\"\./etc/\"#PATH_ETC = \"$(cfg_target)\"#g" \ > /tmp/osipsconsole $(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/osipsconsole $(INSTALL_BIN) /tmp/osipsconsole $(bin_prefix)/$(bin_dir) rm -fr /tmp/osipsconsole install-bin: $(bin_prefix)/$(bin_dir) opensipsmc utils # install opensips binary $(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/$(NAME) $(INSTALL_BIN) $(NAME) $(bin_prefix)/$(bin_dir) # install opensips menuconfig $(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/osipsconfig $(INSTALL_BIN) menuconfig/configure $(bin_prefix)/$(bin_dir)/osipsconfig # install opensipsctl (and family) tool cat scripts/opensipsctl | \ sed -e "s#/usr/local/sbin#$(bin-target)#g" | \ sed -e "s#/usr/local/lib/opensips#$(lib-target)#g" | \ sed -e "s#/usr/local/etc/opensips#$(cfg_target)#g" >/tmp/opensipsctl $(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/opensipsctl $(INSTALL_BIN) /tmp/opensipsctl $(bin_prefix)/$(bin_dir) rm -fr /tmp/opensipsctl sed -e "s#/usr/local/sbin#$(bin-target)#g" \ < scripts/opensipsctl.base > /tmp/opensipsctl.base mkdir -p $(modules_prefix)/$(lib_dir)/opensipsctl $(INSTALL_TOUCH) \ $(modules_prefix)/$(lib_dir)/opensipsctl $(INSTALL_CFG) /tmp/opensipsctl.base \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.base rm -fr /tmp/opensipsctl.base sed -e "s#/usr/local#$(bin-target)#g" \ < scripts/opensipsctl.ctlbase > /tmp/opensipsctl.ctlbase $(INSTALL_CFG) /tmp/opensipsctl.ctlbase \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.ctlbase rm -fr /tmp/opensipsctl.ctlbase sed -e "s#/usr/local#$(bin-target)#g" \ < scripts/opensipsctl.fifo > /tmp/opensipsctl.fifo $(INSTALL_CFG) /tmp/opensipsctl.fifo \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.fifo rm -fr /tmp/opensipsctl.fifo sed -e "s#/usr/local#$(bin-target)#g" \ < scripts/opensipsctl.unixsock > /tmp/opensipsctl.unixsock $(INSTALL_CFG) /tmp/opensipsctl.unixsock \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.unixsock rm -fr /tmp/opensipsctl.unixsock sed -e "s#/usr/local#$(bin-target)#g" \ < scripts/opensipsctl.sqlbase > /tmp/opensipsctl.sqlbase $(INSTALL_CFG) /tmp/opensipsctl.sqlbase \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.sqlbase rm -fr /tmp/opensipsctl.sqlbase # install db setup base script sed -e "s#/usr/local/sbin#$(bin-target)#g" \ -e "s#/usr/local/etc/opensips#$(cfg_target)#g" \ -e "s#/usr/local/share/opensips#$(data_target)#g" \ < scripts/opensipsdbctl.base > /tmp/opensipsdbctl.base $(INSTALL_CFG) /tmp/opensipsdbctl.base \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsdbctl.base rm -fr /tmp/opensipsdbctl.base cat scripts/opensipsdbctl | \ sed -e "s#/usr/local/sbin#$(bin-target)#g" | \ sed -e "s#/usr/local/lib/opensips#$(lib-target)#g" | \ sed -e "s#/usr/local/etc/opensips#$(cfg_target)#g" >/tmp/opensipsdbctl $(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/opensipsdbctl $(INSTALL_BIN) /tmp/opensipsdbctl $(bin_prefix)/$(bin_dir) rm -fr /tmp/opensipsdbctl $(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/$(NAME)unix $(INSTALL_BIN) utils/$(NAME)unix/$(NAME)unix $(bin_prefix)/$(bin_dir) .PHONY: utils utils: cd utils/$(NAME)unix; $(MAKE) all if [ "$(BERKELEYDBON)" = "yes" ]; then \ cd utils/db_berkeley; $(MAKE) all ; \ fi ; if [ "$(ORACLEON)" = "yes" ]; then \ cd utils/db_oracle; $(MAKE) all ; \ fi ; install-modules: modules $(modules_prefix)/$(modules_dir) @for r in $(modules_full_path) "" ; do \ if [ -n "$$r" ]; then \ if [ -f "$$r" ]; then \ $(INSTALL_TOUCH) \ $(modules_prefix)/$(modules_dir)/`basename "$$r"` ; \ $(INSTALL_MODULES) "$$r" $(modules_prefix)/$(modules_dir) ; \ $(MAKE) -C `dirname "$$r"` install_module_custom ; \ else \ echo "ERROR: module $$r not compiled" ; \ fi ;\ fi ; \ done .PHONY: install-doc install-app-doc install-modules-doc install-doc: install-app-doc install-modules-doc install-app-doc: $(doc_prefix)/$(doc_dir) -@for d in $(install_docs) ""; do \ if [ -n "$$d" ]; then \ $(INSTALL_TOUCH) $(doc_prefix)/$(doc_dir)/"$$d" ; \ $(INSTALL_DOC) "$$d" $(doc_prefix)/$(doc_dir) ; \ fi ; \ done install-modules-doc: $(doc_prefix)/$(doc_dir) -@for r in $(modules_basenames) "" ; do \ if [ -n "$$r" ]; then \ if [ -f modules/"$$r"/README ]; then \ $(INSTALL_TOUCH) $(doc_prefix)/$(doc_dir)/README."$$r" ; \ $(INSTALL_DOC) modules/"$$r"/README \ $(doc_prefix)/$(doc_dir)/README."$$r" ; \ fi ; \ fi ; \ done install-man: $(man_prefix)/$(man_dir)/man8 $(man_prefix)/$(man_dir)/man5 sed -e "s#/etc/$(NAME)/$(NAME)\.cfg#$(cfg_target)$(NAME).cfg#g" \ -e "s#/usr/sbin/#$(bin-target)#g" \ -e "s#/usr/lib/$(NAME)/modules/#$(modules_target)#g" \ -e "s#/usr/share/doc/$(NAME)/#$(doc-target)#g" \ < $(NAME).8 > $(man_prefix)/$(man_dir)/man8/$(NAME).8 chmod 644 $(man_prefix)/$(man_dir)/man8/$(NAME).8 sed -e "s#/etc/$(NAME)/$(NAME)\.cfg#$(cfg_target)$(NAME).cfg#g" \ -e "s#/usr/sbin/#$(bin-target)#g" \ -e "s#/usr/lib/$(NAME)/modules/#$(modules_target)#g" \ -e "s#/usr/share/doc/$(NAME)/#$(doc-target)#g" \ < $(NAME).cfg.5 > $(man_prefix)/$(man_dir)/man5/$(NAME).cfg.5 chmod 644 $(man_prefix)/$(man_dir)/man5/$(NAME).cfg.5 sed -e "s#/etc/$(NAME)/$(NAME)\.cfg#$(cfg_target)$(NAME).cfg#g" \ -e "s#/usr/sbin/#$(bin-target)#g" \ -e "s#/usr/lib/$(NAME)/modules/#$(modules_target)#g" \ -e "s#/usr/share/doc/$(NAME)/#$(doc-target)#g" \ < scripts/opensipsctl.8 > $(man_prefix)/$(man_dir)/man8/opensipsctl.8 chmod 644 $(man_prefix)/$(man_dir)/man8/opensipsctl.8 sed -e "s#/etc/$(NAME)/$(NAME)\.cfg#$(cfg_target)$(NAME).cfg#g" \ -e "s#/usr/sbin/#$(bin-target)#g" \ -e "s#/usr/lib/$(NAME)/modules/#$(modules_target)#g" \ -e "s#/usr/share/doc/$(NAME)/#$(doc-target)#g" \ < utils/opensipsunix/opensipsunix.8 > \ $(man_prefix)/$(man_dir)/man8/opensipsunix.8 chmod 644 $(man_prefix)/$(man_dir)/man8/opensipsunix.8 install-modules-docbook: $(doc_prefix)/$(doc_dir) -@for r in $(modules_basenames) "" ; do \ if [ -n "$$r" ]; then \ if [ -d modules/"$$r"/doc ]; then \ if [ -f modules/"$$r"/doc/"$$r".txt ]; then \ $(INSTALL_TOUCH) $(doc_prefix)/$(doc_dir)/"$$r".txt ; \ $(INSTALL_DOC) modules/"$$r"/doc/"$$r".txt \ $(doc_prefix)/$(doc_dir)/"$$r".txt ; \ fi ; \ if [ -f modules/"$$r"/doc/"$$r".html ]; then \ $(INSTALL_TOUCH) $(doc_prefix)/$(doc_dir)/"$$r".html ; \ $(INSTALL_DOC) modules/"$$r"/doc/"$$r".html \ $(doc_prefix)/$(doc_dir)/"$$r".html ; \ fi ; \ if [ -f modules/"$$r"/doc/"$$r".pdf ]; then \ $(INSTALL_TOUCH) $(doc_prefix)/$(doc_dir)/"$$r".pdf ; \ $(INSTALL_DOC) modules/"$$r"/doc/"$$r".pdf \ $(doc_prefix)/$(doc_dir)/"$$r".pdf ; \ fi ; \ fi ; \ fi ; \ done .PHONY: test test: -@echo "Start tests" $(MAKE) -C test/ -@echo "Tests finished" doxygen: -@echo "Create Doxygen documentation" # disable call graphes, because of the DOT dependencies (cat doc/doxygen/opensips-doxygen; \ echo "HAVE_DOT=no" ;\ echo "PROJECT_NUMBER=$(NAME)-$(RELEASE)" )| doxygen - -@echo "Doxygen documentation created" comp_menuconfig: $(MAKE) -C menuconfig menuconfig: comp_menuconfig ./menuconfig/configure --local opensips-2.2.2/Makefile.conf.template000066400000000000000000000221621300170765700175640ustar00rootroot00000000000000#aaa_radius= Radius implementation for the AAA API from the core | Radius client development library, tipically radiusclient-ng 0.5.0 or higher #b2b_logic= Logic engine of B2BUA, responsible of actually implementing the B2BUA services | xml parsing development library, typically libxml2-dev #cachedb_cassandra= Implementation of a cache system designed to work with Cassandra servers | thrift 0.6.1 #cachedb_couchbase= Implementation of a cache system designed to work with CouchBase servers | libcouchbase >= 2.0 #cachedb_memcached= Implementation of a cache system designed to work with a memcached server. | Memcached client library, tipically libmemcached #cachedb_mongodb= Implementation of a cache system designed to work with a MongoDB server. | libjson and the mongo-c-driver #cachedb_redis= Implementation of a cache system designed to work with Redis servers | Redis client library, hiredis #carrierroute= Provides routing, balancing and blacklisting capabilities. | libconfuse, a configuration file parser library #compression= Implements SIP message compression/decompression and base64 encoding | zlib dev library, tipically zlib1g-dev #cpl_c= Implements a CPL (Call Processing Language) interpreter | library for parsing XML files, tipically libxml2 and libxml2-devel #db_berkeley= Integrates the Berkeley DB into OpenSIPS | Berkeley embedded database #db_http= Provides access to a database that is implemented as a HTTP server. | CURL library - libcurl #db_mysql= Provides MySQL connectivity for OpenSIPS | development libraries of mysql-client , tipically libmysqlclient-dev #db_oracle= Provides Oracle connectivity for OpenSIPS. | Development library of OCI, tipically instantclient-sdk-10.2.0.3 #db_perlvdb= Provides a virtualization framework for OpenSIPS's database access. | Perl library development files, tipically libperl-dev #db_postgres= Provides Postgres connectivity for OpenSIPS | PostgreSQL library and development library - tipically libpq5 and libpq-dev #db_sqlite= Provides SQLite connectivity for OpenSIPS | SQLite library and development library - tipically libsqlite3 and libsqlite3-dev #db_unixodbc= Allows to use the unixodbc package with OpenSIPS | ODBC library and ODBC development library #dialplan= Implements generic string translations based on matching and replacement rules | PCRE development library, tipically libpcre-dev #emergency= Provides emergency call treatment for OpenSIPS | CURL dev library - tipically libcurl4-openssl-dev #event_rabbitmq= Provides the implementation of a RabbitMQ client for the Event Interface | RabbitMQ development library, librabbitmq-dev #h350= Enables access to SIP account data stored in an LDAP [RFC4510] directory containing H.350 commObjects | OpenLDAP library & development files, tipically libldap and libldap-dev #regex= Offers matching operations against regular expressions using the powerful PCRE library. | Development library for PCRE, tipically libpcre-dev #identity= Adds support for SIP Identity (see RFC 4474). | SSL library, tipically libssl #jabber= Integrates XODE XML parser for parsing Jabber messages | Expat library. #json= Introduces a new type of variable that provides both serialization and de-serialization from JSON format. | JSON library, libjson #ldap= Implements an LDAP search interface for OpenSIPS | OpenLDAP library & development files, tipically libldap and libldap-dev #lua= Easily implement your own OpenSIPS extensions in Lua | liblua5.1-0-dev, libmemcache-dev and libmysqlclient-dev #httpd= Provides an HTTP transport layer implementation for OpenSIPS. | libmicrohttpd #mi_xmlrpc_ng= New version of the xmlrpc server that handles xmlrpc requests and generates xmlrpc responses. | parsing/building XML library, tipically libxml #mmgeoip= Lightweight wrapper for the MaxMind GeoIP API | libGeoIP #osp= Enables OpenSIPS to support secure, multi-lateral peering using the OSP standard | OSP development kit, tipically osptoolkit #perl= Easily implement your own OpenSIPS extensions in Perl | Perl library development files, tipically libperl-dev #pi_http= Provides a simple web database provisioning interface | XML parsing & building library, tipically libxml-dev #proto_sctp= Provides support for SCTP listeners in OpenSIPS | SCTP development library, tipically libsctp-dev #proto_tls= Provides support for TLS listeners in OpenSIPS | SSL development library, tipically libssl-dev #proto_wss= Provides support for Secure WebSocket listeners in OpenSIPS | SSL development library, tipically libssl-dev #presence= Handles PUBLISH and SUBSCRIBE messages and generates NOTIFY messages in a general, event independent way | XML parsing & Building library, tipically libxml-dev #presence_dialoginfo= Enables the handling of "Event: dialog" (as defined in RFC 4235) | XML parsing & building library, tipically libxml-dev #presence_mwi= Does specific handling for notify-subscribe message-summary (message waiting indication) events as specified in RFC 3842 | XML parsing & building library, tipically libxml-dev #presence_xml= Does specific handling for notify-subscribe events using xml bodies. | XML parsing & building library, tipically libxml-dev #pua= Offers the functionality of a presence user agent client, sending Subscribe and Publish messages. | XML parsing & building library, tipically libxml-dev #pua_bla= Enables Bridged Line Appearances support according | XML parsing & building library, tipically libxml-dev #pua_dialoginfo= Retrieves dialog state information from the dialog module and PUBLISHes the dialog-information using the pua module. | XML parsing & building library,tipically libxml-dev #pua_mi= Offers the possibility to publish presence information and subscribe to presence information via MI transports. | XML parsing & building library,tipically libxml-dev #pua_usrloc= Connector between usrloc and pua modules. | XML parsing & building library,tipically libxml-dev #pua_xmpp= Gateway for presence between SIP and XMPP. | XML parsing & building library,tipically libxml-dev #python= Easily implement your own OpenSIPS extensions in Python | Shared Python runtime library, libpython #rest_client= Simple HTTP client | CURL library - libcurl #rls= Resource List Server implementation following the specification in RFC 4662 and RFC 4826 | parsing/building XML library, tipically libxml-dev #sngtc= Voice Transcoding using the D-series Sangoma transcoding cards | libsngtc_node #snmpstats= Provides an SNMP management interface to OpenSIPS | NetSNMP v5.3 #tls_mgm= Provides a TLS interface to manage certificates for OpenSIPS | SSL development library, tipically libssl-dev #xcap= XCAP utility functions for OpenSIPS. | libxml-dev #xcap_client= XCAP client for OpenSIPS.It fetches XCAP elements, either documents or part of them, by sending HTTP GET requests | libxml-dev and libcurl-dev #xmpp= Gateway between OpenSIPS and a jabber server. It enables the exchange of IMs between SIP clients and XMPP(jabber) clients. | parsing/building XML files, tipically libexpat1-devel exclude_modules?= aaa_radius b2b_logic cachedb_cassandra cachedb_couchbase cachedb_memcached cachedb_mongodb cachedb_redis carrierroute compression cpl_c db_berkeley db_http db_mysql db_oracle db_perlvdb db_postgres db_sqlite db_unixodbc dialplan emergency event_rabbitmq h350 regex identity jabber json ldap lua httpd mi_xmlrpc_ng mmgeoip osp perl pi_http presence presence_dialoginfo presence_mwi presence_xml proto_sctp proto_tls proto_wss pua pua_bla pua_dialoginfo pua_mi pua_usrloc pua_xmpp python rest_client rls sngtc snmpstats tls_mgm xcap xcap_client xmpp include_modules?= #DEFS_GROUP_START DEFS+= -DPKG_MALLOC #Uses a faster malloc #DEFS+= -DUSE_SHM_MEM #All PKG allocations are mapped to SHM #DEFS_GROUP_END DEFS+= -DSHM_MMAP #Use mmap instead of SYSV shared memory DEFS+= -DUSE_MCAST #Compile in support for IP Multicast DEFS+= -DDISABLE_NAGLE #Disabled the TCP NAgle Algorithm ( lower delay ) DEFS+= -DSTATISTICS #Enables the statistics manager DEFS+= -DHAVE_RESOLV_RES #Support for changing some of the resolver parameters #DEFS_GROUP_START ##DEFS+= -DVQ_MALLOC #Very quick & wasteful mem allocator (currently disabled) #DEFS+= -DQM_MALLOC #Quality assurance memory allocator with runtime safety checks DEFS+= -DF_MALLOC #Fast memory allocator with minimal runtime overhead #DEFS+= -DHP_MALLOC #High performance allocator with fine-grained locking #DEFS_GROUP_END #DEFS+= -DDBG_MALLOC #Enables debugging for memory allocators #DEFS+= -DF_MALLOC_OPTIMIZATIONS #Remove all safety checks in F_MALLOC #DEFS+= -DNO_DEBUG #Turns off all debug messages #DEFS+= -DNO_LOG #Completely turns off all the logging #DEFS+= -DFAST_LOCK #Uses fast architecture specific locking #DEFS+= -DUSE_FUTEX #Uses linux futexs with fast architecture specific locking #DEFS+= -DUSE_SYSV_SEM #Uses SYSV sems for locking ( slower & limited number of locks #DEFS+= -DUSE_PTHREAD_MUTEX #Uses pthread mutexes for locking #DEFS+= -DUSE_POSIX_SEM #Uses POSIX sems for locking #DEFS+= -DBUSY_WAIT #Uses busy waiting on the lock #DEFS+= -DDBG_LOCK #Attach debug info to all lock structures #DEFS+= -DNOSMP #Do not use SMP sompliant locking. Faster but won't work on SMP machines #DEFS+= -DEXTRA_DEBUG #Compiles in some extra debugging code #DEFS+= -DORACLE_USRLOC #Uses Oracle compatible queries for USRLOC PREFIX=/usr/local/ opensips-2.2.2/Makefile.defs000066400000000000000000001250351300170765700157510ustar00rootroot00000000000000# # makefile defs (CC, LD,a.s.o) # # Environment variables: # PREFIX, LOCALBASE, SYSBASE, BASEDIR # INSTALL, TAR , CC, LEX, YACC, # CPU, CC_EXTRA_OPTS, LD_EXTRA_OPTS # exclude_modules, skip_modules, include_modules # extra_defs # # History: # -------- # created by andrei # 2003-02-24 added LOCALBASE, fixed doc_dir for freebsd - patch provided # by Maxim Sobolev # 2003-02-25 added -DDISABLE_NAGLE (andrei) # 2003-03-02 added -DDIGEST_DOMAIN (janakj) # 2003-03-10 added -xcode=pic32 for module compilation w/ sun cc # (too many symbols for pic13) (andrei) # 2003-04-16 added CC_EXTRA_OPTS, s/march/mcpu, added CPU (cpu to optimize # for, used only with gcc-3.x) (andrei) # 2003-05-23 check if this makefile was already included (andrei) # removed -DDIGEST_DOMAIN (andrei) # 2003-05-30 added extra_defs (andrei) # 2003-06-06 moved compiler detection before DEFS (andrei) # 2003-06-10 removed -m32 for gcc 3.x/sparc64 -- it will use # arch. default: -m32 on solaris, -m64 on *bsd (andrei) # 2003-09-25 added -pthread into LIBS when compiling on FreeBSD/alpha # and other FreeBSD arches for which no fast locking assembly # code exists (sobomax) # 2003-11-08 mips1 support introduced (andrei) # 2003-11-24 openbsd 3.4 (elf) fixes (andrei) # 2004-07-27 darwin (mac os x) port (andrei) # 2004-09-12 mips2 & cobalt support introduced (andrei) # 2004-09-28 x86_64 support introduced (andrei) # 2004-12-14 gcc-3.4 special case added (andrei) # 2004-12-15 HAVE_ALLOCA_H added (andrei) # 2004-12-19 amd64 transformed in x86_64 (andrei) # 2005-04-27 alpha support added (andrei) # 2005-06-01 use $(LOCALBASE) instead of /usr/{local,pkg} (andrei) # 2006-10-19 gcc-3.4 and gcc-3.0 cases merged as they proved to be identical; # CC_CLASS replaces CC_SHORTVER while CC_SHORTVER is keept without # any wildcards (bogdan) # 2007-02-12 use $(SYSBASE) instead of /usr - easy to switch to another # root for cross compiling (bogdan) # 2007-02-12 CPU replaced with CPU_TYPE to avoid overlapping with system # env. variables on some OS. (bogdan) # 2007-11-29 Add support for GNU/kFreeBSD. Substitute / for _ in $(OS). # 2009-01-15 Detection of CPU Type in Solaris to apply specific optimization # flags (saguti) # check if already included/exported ifeq ($(makefile_defs), 1) else makefile_defs=1 export makefile_defs # main binary name MAIN_NAME=opensips #version number VERSION_MAJOR = 2 VERSION_MINOR = 2 VERSION_SUBMINOR = 2 VERSION_BUILD = ifneq (,$(VERSION_BUILD)) RELEASE=$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_SUBMINOR)-$(VERSION_BUILD) else RELEASE=$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_SUBMINOR) endif ifneq ($(OPENSIPS_RELEASE),) # allow override thru environment RELEASE=$(OPENSIPS_RELEASE) endif OS = $(shell uname -s | sed -e s/SunOS/solaris/ | tr "[A-Z/]" "[a-z_]") ifeq ($(OS),solaris) GETARCH=uname -p # CHIP Detection CHIP = $(shell /usr/sbin/modinfo | grep SUNW | head | cut -d, -f2 | \ awk '{print $1}' | tr "[A-Z]" "[a-z]" | sed 's/ ()//g' ) else GETARCH=uname -m endif HOST_ARCH := $(shell $(GETARCH) |sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -e s/armv4l/arm/ -e "s/Power Macintosh/ppc/" \ -e s/sun4v/sparc64/ -e "s/cobalt/mips2/" \ -e s/amd64/x86_64/ ) # fix sparc -> sparc64 ifeq ($(HOST_ARCH),sparc) ifeq ($(shell uname -m),sun4u) HOST_ARCH := sparc64 endif ifeq ($(shell uname -m),sun4v) HOST_ARCH := sparc64 endif endif # additional check for architecture on solaris ifeq ($(OS),solaris) ifeq ($(shell isainfo -k),amd64) HOST_ARCH := x86_64 endif endif # Check of SMP systems ifeq ($(OS),solaris) CPU=$(shell /usr/sbin/psrinfo | grep -i online | wc -l) ifeq ($(CPU),1) ISSMP ?= no else ISSMP ?= yes endif else SMP_STR = $(shell uname -v | grep -i "SMP") ifeq (,$(SMP_STR)) ISSMP ?= no else ISSMP ?= yes endif endif #set some vars from the environment (and not make builtins) CC := $(shell echo "$${CC}") LEX := $(shell echo "$${LEX}") YACC := $(shell echo "$${YACC}") # find compiler name & version ifeq ($(CC),) CC=gcc endif LD= $(CC) CC_LONGVER=$(shell if $(CC) -v 2>/dev/null; then \ $(CC) -v 2>&1 ;\ else \ $(CC) -V 2>&1 ; \ fi ) #find-out the compiler's name # clang contains path to gcc ifneq (,$(findstring clang, $(CC_LONGVER))) CC_NAME=clang CC_VER=$(word 1,$(CC)) $(shell $(CC) - -dumpversion) MKDEP=$(CC) -MM CC_SHORTVER=$(shell echo "$(CC_VER)" | cut -d" " -f 2| \ sed -e 's/[^0-9]*-\(.*\)/\1/'| \ sed -e 's/\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/' ) CC_CLASS=$(shell echo "$(CC_SHORTVER)" | \ sed -e 's/2\.9.*/2.9x/' -e 's/3\.[0-9]/3.x/' \ -e 's/[4-9]\.[0-9]/4.x/') else ifneq (,$(findstring gcc, $(CC_LONGVER))) CC_NAME=gcc CC_VER=$(word 1,$(CC)) $(shell $(CC) - -dumpversion|head -n 1|cut -d" " -f 3|\ sed -e 's/^.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/' \ -e 's/^[^0-9].*\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/') # sun sed is a little brain damaged => this complicated expression MKDEP=$(CC) -MM CC_SHORTVER=$(shell echo "$(CC_VER)" | cut -d" " -f 2| \ sed -e 's/[^0-9]*-\(.*\)/\1/'| \ sed -e 's/\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/' ) #transform gcc version into 2.9x, 3.x or 4.x #XXX: for now treat 4+.x the same as 4.x CC_CLASS=$(shell echo "$(CC_SHORTVER)" | \ sed -e 's/2\.9.*/2.9x/' -e 's/3\.[0-9]/3.x/' \ -e 's/[4-9]\.[0-9]/4.x/') endif ifneq (, $(findstring Sun, $(CC_LONGVER))) CC_NAME=suncc CC_SHORTVER=$(shell echo "$(CC_LONGVER)"|head -n 1| \ sed -e 's/.*\([0-9]\.[0-9]\).*/\1/g' ) CC_VER=$(CC) $(CC_SHORTVER) MKDEP=$(CC) -xM1 endif ifneq (, $(findstring Intel(R) C++ Compiler, $(CC_LONGVER))) # very nice: gcc compatible CC_NAME=icc CC_FULLVER=$(shell echo "$(CC_LONGVER)"|head -n 1| \ sed -e 's/.*Version \([0-9]\.[0-9]\.[0-9]*\).*/\1/g' ) CC_SHORTVER=$(shell echo "$(CC_FULLVER)" | cut -d. -f1,2 ) CC_VER=$(CC) $(CC_FULLVER) MKDEP=$(CC) -MM endif ifeq (,$(CC_NAME)) #not found CC_NAME=$(CC) CC_SHORTVER=unknown CC_VER=unknown MKDEP=gcc -MM $(warning Unknown compiler $(CC)\; supported compilers: \ gcc, sun cc, intel icc ) endif export CC_NAME # ARCH detection # predefined compiler macros for different architectures # (see http://predef.sourceforge.net/prearch.html for a more complete list) i386_macros= i386 __i386__ __i486__ __i586__ __i686__ \ __i386 _M_IX86 __X86__ _X86_ x86_64_macros= __amd64__ __amd64 __x86_64__ __x86_64 _M_X64 sparc_macros= __sparc__ __sparc __sparcv8 sparc64_macros= __sparcv9 __sparc_v9__ arm_macros= __arm__ __thumb__ arm6_macros= __ARM_ARCH_6__ ppc_macros= __powerpc __powerpc__ __POWERPC__ __ppc__ _ARCH_PPC ppc64_macros= __ppc64__ _ARCH_PPC64 mips_macros= __mips__ __mips _MIPS_ARCH_MIPS1 mips2_macros= _MIPS_ISA_MIPS2 _MIPS_ISA_MIPS3 _MIPS_ISA_MIPS4 \ _MIPS_ARCH_MIPS2 _MIPS_ARCH_MIPS3 _MIPS_ARCH_MIPS4 mips32_macros= _MIPS_ISA_MIPS32 _MIPS_ARCH_MIPS32R2 _MIPS_ARCH_MIPS32 mips64_macros= _MIPS_ISA_MIPS64 _MIPS_ARCH_MIPS64 alpha_macros= __alpha__ __alpha _M_ALPHA_ ifeq ($(CC_NAME),gcc) #if gcc use gcc arch predef_macros:=$(shell $(CC) -dM -E -x c $(CC_EXTRA_OPTS) $(extra_defs) \ $(CFLAGS) /dev/null) ifneq ($(strip $(filter $(i386_macros), $(predef_macros))),) CC_ARCH=i386 else ifneq ($(strip $(filter $(x86_64_macros), $(predef_macros))),) CC_ARCH=x86_64 else ifneq ($(strip $(filter $(sparc_macros), $(predef_macros))),) ifneq ($(strip $(filter $(sparc64_macros), $(predef_macros))),) CC_ARCH=sparc64 else # sparc64_macros CC_ARCH=sparc endif # sparc64_macros else ifneq ($(strip $(filter $(arm_macros), $(predef_macros))),) ifneq ($(strip $(filter $(arm6_macros), $(predef_macros))),) CC_ARCH=arm6 else # arm6_macros CC_ARCH=arm endif # arm6_macros else ifneq ($(strip $(filter $(ppc64_macros), $(predef_macros))),) CC_ARCH=ppc64 else ifneq ($(strip $(filter $(ppc_macros), $(predef_macros))),) CC_ARCH=ppc else ifneq ($(strip $(filter $(mips_macros), $(predef_macros))),) ifneq ($(strip $(filter $(mips64_macros), $(predef_macros))),) CC_ARCH=mips64 else ifneq ($(strip $(filter $(mips32_macros), $(predef_macros))),) CC_ARCH=mips32 else ifneq ($(strip $(filter $(mips2_macros), $(predef_macros))),) CC_ARCH=mips2 else # mips2_macros CC_ARCH=mips endif # mips64_macros else ifneq ($(strip $(filter $(alpha_macros), $(predef_macros))),) CC_ARCH=alpha else $(warn "Unknown target compiler architecture") endif # predefined macros tests (x86_macros, ...) endif # gcc ifdef CC_ARCH ifneq ($(CC_ARCH),$(HOST_ARCH)) $(info Target architecture <$(CC_ARCH)>, host architecture <$(HOST_ARCH)>) endif ARCH:=$(CC_ARCH) else ARCH:=$(HOST_ARCH) endif OSREL = $(shell uname -r) # numerical version (good for comparisons: A.B.C => A*1000000+B*1000+C) OSREL_N= $(shell echo $(OSREL) | sed -e 's/^[^0-9]*//' \ -e 's/^\([0-9][0-9]*\(\.[0-9][0-9]*\)*\).*$$/\1/g' | \ (IFS=. read A B C D; [ -z "$$B" ] && B=0; [ -z "$$C" ] && C=0; R=0; \ [ -n "$$A" ] && R=`expr $$R \* 1000 + $$A` && \ [ -n "$$B" ] && R=`expr $$R \* 1000 + $$B` && \ [ -n "$$C" ] && R=`expr $$R \* 1000 + $$C`; echo $$R ) ) # no Debug (-g) ? DEBUGSYM=-g ifdef NODEBUGSYM DEBUGSYM:= endif # nicer compilation? Q= NICER ?= ifeq ($(NICER),1) export Q=@ endif # extra CC command line options (e.g -march=athlon-mp) CC_EXTRA_OPTS ?= # cross compiling? CROSS_COMPILE ?= # system base SYSBASE ?= /usr # dirs cfg_dir = etc/$(MAIN_NAME)/ bin_dir = sbin/ ARCH_B = $(shell echo $(ARCH) | sed -e 's/.*64.*/64b/') ifeq ($(ARCH_B),64b) LIBDIR ?= lib64 else LIBDIR ?= lib # assume 32b - it is not really used further ARCH_B=32b endif lib_dir = $(LIBDIR)/$(MAIN_NAME) modules_dir = $(LIBDIR)/$(MAIN_NAME)/modules/ ifeq ($(OS), linux) doc_dir = share/doc/$(MAIN_NAME)/ man_dir = share/man/ data_dir = share/$(MAIN_NAME)/ LOCALBASE ?= $(SYSBASE)/local else ifeq ($(OS), freebsd) doc_dir = share/doc/$(MAIN_NAME)/ man_dir = man/ data_dir = share/$(MAIN_NAME)/ LOCALBASE ?= $(SYSBASE)/local else ifeq ($(OS), openbsd) doc_dir = share/doc/$(MAIN_NAME)/ man_dir = man/ data_dir = share/$(MAIN_NAME)/ LOCALBASE ?= $(SYSBASE)/local else ifeq ($(OS), netbsd) doc_dir = share/doc/$(MAIN_NAME) man_dir = man/ data_dir = share/$(MAIN_NAME)/ LOCALBASE ?= $(SYSBASE)/pkg else ifeq ($(OS), darwin) doc_dir = share/doc/$(MAIN_NAME)/ man_dir = man/ data_dir = share/$(MAIN_NAME)/ LOCALBASE ?= $(SYSBASE)/local else ifeq ($(OS), gnu_kfreebsd) doc_dir = share/doc/$(MAIN_NAME)/ man_dir = share/man/ data_dir = share/$(MAIN_NAME)/ LOCALBASE = ?= $(SYSBASE)/local else ifeq ($(OS), solaris) doc_dir = doc/$(MAIN_NAME)/ man_dir = man/ data_dir = $(MAIN_NAME)/ LOCALBASE = $(SYSBASE) else doc_dir = doc/$(MAIN_NAME)/ man_dir = man/ data_dir = $(MAIN_NAME)/ LOCALBASE ?= $(SYSBASE)/local endif endif endif endif endif endif endif # install location DESTDIR ?= $(LOCALBASE) PREFIX ?= $(DESTDIR) prefix = $(PREFIX) # install path is $(basedir) $(prefix) # example: # creating a bin. archive in /tmp, which unpacks in /usr/local # basedir=/tmp # prefix=/usr/local BASEDIR ?= basedir = $(BASEDIR) # install prefixes for various stuff cfg_prefix = $(basedir)$(prefix) bin_prefix = $(basedir)$(prefix) modules_prefix = $(basedir)$(prefix) doc_prefix = $(basedir)$(prefix) man_prefix = $(basedir)$(prefix) ut_prefix = $(basedir)$(prefix) data_prefix = $(basedir)$(prefix) # target dirs for various stuff cfg_target = $(prefix)/$(cfg_dir) bin-target = $(prefix)/$(bin_dir) lib-target = $(prefix)/$(lib_dir) modules_target = $(prefix)/$(modules_dir) doc-target = $(prefix)/$(doc_dir) data_target = $(prefix)/$(data_dir) ifeq ($(OS), solaris) #use GNU versions INSTALL ?= ginstall TAR ?= gtar else INSTALL ?= install TAR ?= tar endif INSTALL_TOUCH = touch # used to create the file first (good to # make solaris install work) INSTALL_CFG = $(INSTALL) -m 644 INSTALL_BIN = $(INSTALL) -m 755 INSTALL_MODULES = $(INSTALL) -m 755 INSTALL_DOC = $(INSTALL) -m 644 INSTALL_MAN = $(INSTALL) -m 644 ifeq ($(VERSIONTYPE),) VERSIONTYPE = $(shell if [ -d ".svn" ]; then \ echo "svn"; \ elif [ -d ".git" ]; then \ echo "git"; \ fi ) endif ifneq ($(VERSIONTYPE),) ifeq ($(GETVERSION),) ifeq ($(VERSIONTYPE),svn) GETVERSION = $(shell which svnversion) GETVERSIONOPTS = -n -c . endif ifeq ($(VERSIONTYPE),git) GETVERSION = $(shell which git) GETVERSIONOPTS = rev-parse --short HEAD endif endif ifeq ($(OLDREVISION),) OLDREVISION = $(shell if [ -f ".$(VERSIONTYPE)revision" ] ; then \ cat .$(VERSIONTYPE)revision ;\ fi ) endif ifeq ($(THISREVISION),) THISREVISION = $(shell if [ -f main.c -a -f Makefile.defs ] ; then \ if [ -x "$(GETVERSION)" ] ; then \ $(GETVERSION) $(GETVERSIONOPTS) ;\ fi ;\ fi ) endif else # git is the default versioning method VERSIONTYPE = $(shell [ -f ".gitrevision" ] && echo "git") ifneq ($(VERSIONTYPE),) THISREVISION = $(shell cat .gitrevision) endif OLDREVISION = "$(THISREVISION)" endif ifneq ($(THISREVISION),) NEWREVISION = $(shell if [ "$(THISREVISION)" != "$(OLDREVISION)" ] ; then \ echo "dosetrev" ; \ fi ) else THISREVISION = unknown endif ifeq ($(DBXML2HTML),) DBXML2HTML = $(shell which xsltproc) endif ifneq ($(DBXML2HTML),) ifeq ($(DBHTMLCSS),) DBHTMLCSS = ../../../doc/module-docbook.css endif #DBHTMLXSL = /usr/share/xml/docbook/stylesheet/nwalsh/html/docbook.xsl # On CentOS, this is the right path: #DBHTMLXSL = /usr/share/sgml/docbook/xsl-stylesheets/xhtml/docbook.xsl DBHTMLXSL=$(shell \ if [ -e /usr/share/xml/docbook/stylesheet/nwalsh/html/docbook.xsl ]; then \ echo "/usr/share/xml/docbook/stylesheet/nwalsh/html/docbook.xsl"; \ elif [ -e /usr/share/sgml/docbook/xsl-stylesheets/xhtml/docbook.xsl ]; then \ echo "/usr/share/sgml/docbook/xsl-stylesheets/xhtml/docbook.xsl"; \ elif [ -e /usr/share/xml/docbook/xsl-stylesheets*/xhtml/docbook.xsl ]; then \ ls -1 /usr/share/xml/docbook/xsl-stylesheets*/xhtml/docbook.xsl; \ fi) DBXML2HTMLPARAMS = --stringparam section.autolabel 1 DBXML2HTMLPARAMS += --stringparam section.label.includes.component.label 1 DBXML2HTMLPARAMS += --stringparam generate.toc "book toc,title,figure,table,example" DBXML2HTMLPARAMS += --stringparam html.stylesheet $(DBHTMLCSS) endif ifeq ($(DBHTML2TXT),) DBHTML2TXT = $(shell which lynx) endif ifneq ($(DBHTML2TXT),) DBHTML2TXTPARAMS = -force_html -dump -nolist -width=72 endif ifeq ($(DBXML2PDF),) DBXML2PDF = $(shell which docbook2pdf) endif MKTAGS=ctags -R . # compile-time options # # -DNO_DEBUG # turns off some of the debug messages (DBG(...)). # -DNO_LOG # completely turns of all the logging (and DBG(...)) # -DEXTRA_DEBUG # compiles in some extra debugging code # -DSHM_MMAP # use mmap instead of SYSV shared memory # -DPKG_MALLOC # uses a faster malloc (exclusive w/ USE_SHM_MEM) # -DUSE_SHM_MEM # all pkg_malloc are mapped to shm_malloc (most mallocs use # a common shared mem. segment); don't define PKG_MALLOC # if you want this! # -DDBG_MALLOC # inserts debug code, will cause pkg_malloc and shm_malloc # to keep and display lot of debuging information: file name, # function, line number of malloc/free call for each block, # extra error checking (trying to free the same pointer # twice, trying to free a pointer alloc'ed with a different # malloc etc.) # -DVQ_MALLOC # additional option of PKG_MALLOC which utilizes faster then the # QM version # (this is not true anymore, q_malloc performs approx. the same) # -DQM_MALLOC # memory allocator recommended for debugging # -DF_MALLOC # an even faster malloc, not recommended for debugging # -DFAST_LOCK # uses fast architecture specific locking (see the arch. specific section) # -DUSE_FUTEX # uses linux futexs with fast architecture specific locking (FAST_LOCK) # -DUSE_SYSV_SEM # uses SYSV sems for locking (this is slower and supports only a # limited number of locks) # -DUSE_PTHREAD_MUTEX # uses pthread mutexes, faster than SYSV or posix semaphores, but it do # not work on all systems inter-processes (e.g. linux) # -DUSE_POSIX_SEM # uses posix semaphores for locking (faster than SYSV) # -DBUSY_WAIT # uses busy waiting on the lock (FAST_LOCK) # -DADAPTIVE_WAIT # try busy waiting for a while and if the lock is still held go to # force reschedule (FAST_LOCK, USE_FUTEX) # -DADAPTIVE_WAIT_LOOPS=number # number of loops we busy wait, after "number" loops have elapsed we # force a reschedule (FAST_LOCK, USE_FUTEX) # -DNOSMP # don't use smp compliant locking (faster but won't work on SMP machines) # (not yet enabled) (FAST_LOCK, USE_FUTEX) # -DDISABLE_NAGLE # disable the tcp Nagle algorithm (lower delay) # -DHAVE_RESOLV_RES # support for changing some of the resolver parameters present # (_res structure in ) # -DSTATISTICS # enables statistics manager - support for collecting statistics # from core and all modules; all info may be fetch via FIFO/UNIX_SOCK # -DORACLE_USRLOC # use oracle compatible SQL in the the get_all_ucontacts function in usrloc, # needed for example from the nathelper module DEFS+= $(extra_defs) \ -DNAME='"$(MAIN_NAME)"' -DVERSION='"$(RELEASE)"' -DARCH='"$(ARCH)"' \ -DOS='"$(OS)"' -DCOMPILER='"$(CC_VER)"' -D__CPU_$(ARCH) -D__OS_$(OS) \ -D__SMP_$(ISSMP) -DCFG_DIR='"$(cfg_target)"'\ #PROFILE= -pg #set this if you want profiling # WARNING: do not add mode=debug or mode=release anymore in the Makefile, # use make mode=debug all instead. Anyway now by default opensips # is compiled w/ debugging symbols in all cases (-g). --andrei ifneq ($(VERSIONTYPE),) DEFS+= -DVERSIONTYPE='"$(VERSIONTYPE)"' -DTHISREVISION='"$(THISREVISION)"' endif ifneq ($(FREERADIUS),) DEFS+= -DUSE_FREERADIUS RADIUS_LIB= -lfreeradius-client else RADIUS_LIB= -lradiusclient-ng endif ifneq ($(SQLITE_BIND),) DEFS+= -DSQLITE_BIND endif ifeq ($(mode),) mode = release endif ifeq ($(mode),debug) DEFS+= -DEXTRA_DEBUG endif # platform dependent settings # find ld & as name (gnu or solaris) ifeq ($(OS), solaris) ifeq ($(CC_NAME), gcc) LDGCC=$(shell $(CC) -v 2>&1 | grep with-ld| \ sed -e 's/.*--with-ld=\([^ ][^ ]*\).*/\1/' ) ASGCC=$(shell $(CC) -v 2>&1 | grep with-as| \ sed -e 's/.*--with-as=\([^ ][^ ]*\).*/\1/' ) LDPATH=$(shell if [ -z "$(LDGCC)" ] ; then echo "ld" ;\ else \ if $(LDGCC) -V 2>/dev/null 1>/dev/null; then \ echo $(LDGCC); \ else echo "ld" ; \ fi\ fi) ASPATH=$(shell if [ -z "$(ASGCC)" ] ; then echo "as" ;\ else \ if $(ASGCC) -V 2>/dev/null 1>/dev/null; then \ echo $(ASGCC); \ else echo "as" ; \ fi\ fi) LDTYPE=$(shell if $(LDPATH) -V 1>/dev/null 2>/dev/null; then \ if $(LDPATH) -V 2>&1|grep GNU >/dev/null; \ then echo gnu; \ else \ if $(LDPATH) -V 2>&1|grep Solaris >/dev/null;\ then echo solaris; \ else \ echo unknown ; \ fi \ fi \ fi) ASTYPE=$(shell if $(ASPATH) -V 1>/dev/null 2>/dev/null &1 /dev/null; \ then echo gnu; \ else \ if $(ASPATH) -V 2>&1 /dev/null;\ then echo solaris; \ else \ echo unknown ; \ fi \ fi \ fi) #$(warning "using ld=$(LDPATH)/$(LDTYPE), as=$(ASPATH)/$(ASTYPE)") endif endif # arh. specific definitions ifeq ($(ARCH), i386) use_fast_lock=yes endif ifeq ($(ARCH), x86_64) use_fast_lock=yes endif ifeq ($(ARCH), sparc64) ifeq ($(CC_NAME), gcc) use_fast_lock=yes endif endif ifeq ($(ARCH), sparc) # smp no supported on sparc32 DEFS+= -DNOSMP use_fast_lock=yes endif ifeq ($(ARCH), arm) use_fast_lock=yes endif # use need some asm code for locking before enabling this #ifeq ($(ARCH), arm6) # use_fast_lock=yes #endif ifeq ($(ARCH), ppc) use_fast_lock=yes endif ifeq ($(ARCH), ppc64) use_fast_lock=yes endif ifeq ($(ARCH), mips) # mips1 arch. (e.g. R3000) - no hardware locking support use_fast_lock=no endif ifeq ($(ARCH), mips2) # mips2 arch and newer (mips3=R4000, mips4=R5000 a.s.o) use_fast_lock=yes endif ifeq ($(ARCH), mips32) use_fast_lock=yes endif ifeq ($(ARCH), mips64) use_fast_lock=yes endif ifeq ($(ARCH), alpha) use_fast_lock=yes endif # decide on the locking type ifeq (,$(findstring USE_PTHREAD_MUTEX, $(DEFS))) ifeq (,$(findstring USE_POSIX_SEM, $(DEFS))) ifeq (,$(findstring USE_SYSV_SEM, $(DEFS))) ifeq ($(use_fast_lock), yes) DEFS+= -DFAST_LOCK -DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=1024 found_lock_method=yes endif else ## USE_SYSV_SEM was manually forced from .conf found_lock_method=yes endif else ## USE_POSIX_SEM was manually forced from .conf found_lock_method=yes endif else ## USE_PTHREAD_MUTEX was manually forced from .conf found_lock_method=yes endif CFLAGS=$(shell echo "$${CFLAGS}") LDFLAGS+= ifneq (,$(findstring DBG_MALLOC, $(DEFS))) CC_OPTIMIZE_FLAG?=-O0 endif ifeq ($(CC_NAME), gcc) CC_OPTIMIZE_FLAG?=-O9 endif ifeq ($(CC_NAME), icc) CC_OPTIMIZE_FLAG?=-O3 endif ifeq ($(CC_NAME), clang) CC_OPTIMIZE_FLAG?=-O3 endif ifeq (,$(CFLAGS)) #common stuff CFLAGS+=$(DEBUGSYM) # setting more CFLAGS ifeq ($(mode), release) #if i386 ifeq ($(ARCH), i386) # These CPU optimization assumes that you run a Intel processor. # If you use a AMD CPU, you could ajust the settings here for better # performance. For gcc 4.x use CPU_TYPE ?= athlon-xp # if gcc ifeq ($(CC_NAME), gcc) #common stuff CFLAGS+=-m32 $(CC_OPTIMIZE_FLAG) -funroll-loops -Wcast-align $(PROFILE) \ -Wall LDFLAGS+=-m32 #if gcc 4.0+ ifeq ($(CC_CLASS), 4.x) ARCH_VER=$(shell $(GETARCH)) ifeq ($(ARCH_VER), i386) CPU_TYPE ?= i686 else CPU_TYPE ?= prescott endif CFLAGS+=-minline-all-stringops \ -falign-loops \ -ftree-vectorize \ -mtune=$(CPU_TYPE) \ -Wold-style-definition -Wmissing-field-initializers -Wredundant-decls else #if gcc 3.0+ ifeq ($(CC_CLASS), 3.x) CPU_TYPE ?= pentium4 CFLAGS+=-minline-all-stringops \ -falign-loops ifeq (X,$(shell echo "$(CC_SHORTVER)" | sed -e 's/3\.[0-3]/X/')) CFLAGS+=-mcpu=$(CPU_TYPE) else CFLAGS+=-mtune=$(CPU_TYPE) endif else ifeq ($(CC_CLASS), 2.9x) #older gcc version (2.9[1-5]) $(warning Old gcc detected ($(CC_SHORTVER)), use gcc >= 3.1 \ for better results) CFLAGS+=-m486 \ -malign-loops=4 else #really old version $(warning You are using an old and unsupported gcc \ version ($(CC_SHORTVER)), compile at your own risk!) endif # CC_CLASS, 2.9x endif # CC_CLASS, 3.x endif # CC_CLASS, 4.x else # CC_NAME, gcc ifeq ($(CC_NAME), icc) CFLAGS+=$(CC_OPTIMIZE_FLAG) -ipo -ipo_obj -unroll $(PROFILE) \ -tpp6 -xK #-openmp #optimize for PIII # -prefetch doesn't seem to work #( ty to inline acroos files, unroll loops,prefetch, # optimize for PIII, use PIII instructions & vect., # mutlithread loops) else #CC_NAME, icc ifeq ($(CC_NAME), suncc) CFLAGS+=-xO5 -fast -native -xCC \ -xc99=no_lib # C99 support # -Dinline="" # add this if cc < 5.3 (define inline as null) else ifeq ($(CC_NAME), clang) CFLAGS+=$(CC_OPTIMIZE_FLAG) -funroll-loops else #other compilers $(error Unsupported compiler ($(CC):$(CC_NAME)), try gcc) endif #CC_NAME, clang endif #CC_NAME, suncc endif #CC_NAME, icc endif #CC_NAME, gcc endif #ARCH, i386 #x86_64 ifeq ($(ARCH), x86_64) # These CPU optimization assumes that you run a Intel 64 processor. # If you use a AMD 64 CPU, you could ajust the settings here for better # performance. For gcc 4.x use CPU_TYPE ?= athlon64 # Perhaps it makes sense to use '-m64' here? # if gcc ifeq ($(CC_NAME), gcc) #common stuff CFLAGS+=$(CC_OPTIMIZE_FLAG) -funroll-loops -Wcast-align $(PROFILE) \ -Wall #if gcc 4.0+ ifeq ($(CC_CLASS), 4.x) CPU_TYPE ?= nocona CFLAGS+=-minline-all-stringops \ -falign-loops \ -ftree-vectorize \ -mtune=$(CPU_TYPE) \ -Wold-style-definition -Wmissing-field-initializers -Wredundant-decls else #if gcc 3.0+ ifeq ($(CC_CLASS), 3.x) CPU_TYPE ?= nocona CFLAGS+=-minline-all-stringops \ -falign-loops \ -mtune=$(CPU_TYPE) else ifeq ($(CC_CLASS), 2.9x) #older gcc version (2.9[1-5]) $(warning Old gcc detected ($(CC_SHORTVER)), use gcc >= 3.1 \ for better results) CFLAGS+=-m486 \ -malign-loops=4 else #really old version $(warning You are using an old and unsupported gcc \ version ($(CC_SHORTVER)), compile at your own risk!) endif # CC_CLASS, 2.9x endif # CC_CLASS, 3.x endif # CC_CLASS, 4.x else # CC_NAME, gcc ifeq ($(CC_NAME), icc) CFLAGS+=$(CC_OPTIMIZE_FLAG) -ipo -ipo_obj -unroll $(PROFILE) \ -tpp6 -xK #-openmp #optimize for PIII # -prefetch doesn't seem to work #( ty to inline acroos files, unroll loops,prefetch, # optimize for PIII, use PIII instructions & vect., # mutlithread loops) else ifeq ($(CC_NAME), suncc) CFLAGS+=-xO5 -fast -native -xCC \ -xc99=no_lib # C99 support # -Dinline="" # add this if cc < 5.3 (define inline as null) else ifeq ($(CC_NAME), clang) CFLAGS+=$(CC_OPTIMIZE_FLAG) -funroll-loops else #other compilers $(error Unsupported compiler ($(CC):$(CC_NAME)), try gcc) endif #CC_NAME, clang endif #CC_NAME, icc endif #CC_NAME, gcc endif #CC_NAME, suncc endif #ARCH, x86_64 #if sparc ifeq ($(ARCH), sparc64) #if gcc ifeq ($(CC_NAME), gcc) #common stuff CFLAGS+=$(CC_OPTIMIZE_FLAG) -funroll-loops $(PROFILE) \ -Wall\ #-Wcast-align \ #-Wmissing-prototypes #if gcc 4.x ifeq ($(CC_CLASS), 4.x) # Processors of Family UltraSparc II ifeq ($(CHIP), ultrasparc-ii) CFLAGS+= -ftree-vectorize -mcpu=ultrasparc -mtune=ultrasparc -mvis endif # Processors of Family UltraSparc III (III, III, IIIi, IIICu) ifeq ($(CHIP), ultrasparc-iiii) CFLAGS+= -ftree-vectorize -mcpu=ultrasparc3 -mtune=ultrasparc3 -mvis endif ifeq ($(CHIP), ultrasparc-iii) CFLAGS+= -ftree-vectorize -mcpu=ultrasparc3 -mtune=ultrasparc3 -mvis endif ifeq ($(CHIP), ultrasparc-iiicu) CFLAGS+= -ftree-vectorize -mcpu=ultrasparc3 -mtune=ultrasparc3 -mvis endif # Processors of Family UltraSparc T1 ifeq ($(CHIP), ultrasparc-t1) # niagara switch is not supported on GCC < 4.2 # Sets to default ultrasparc ifeq ($(CC_SHORTVER), 4.2) CFLAGS+= -ftree-vectorize -mcpu=niagara -mtune=niagara -mvis else ifeq ($(CC_SHORTVER), 4.3) CFLAGS+= -ftree-vectorize -mcpu=niagara -mtune=niagara -mvis else CFLAGS+= -ftree-vectorize -mcpu=ultrasparc -mtune=ultrasparc -mvis endif endif endif # If processor detection fails, defaults to UltraSPARC without VIS ifeq (,$(CHIP)) #CPU_TYPE=ultrasparc CFLAGS += -mcpu=ultrasparc -mtune=ultrasparc endif # use -m64 to force 64 bit (but add it also to LDFLAGS), # -m32 for 32 bit (default on solaris), # nothing for arch. default # -mcpu=v9 or ultrasparc? # -mtune implied by -mcpu #-mno-epilogue #try to inline function exit code #-mflat # omit save/restore #-,faster-structs #faster non Sparc ABI structure copy ops else #if gcc 3.0+ ifeq ($(CC_CLASS), 3.x) # Processors of Family UltraSparc II ifeq ($(CHIP), ultrasparc-ii) #CPU_TYPE=ultrasparc CFLAGS+= -mcpu=ultrasparc -mtune=ultrasparc -mvis endif # Processors of Family UltraSparc III ifeq ($(CHIP), ultrasparc-iiii) CFLAGS+= -mcpu=ultrasparc3 -mtune=ultrasparc3 -mvis endif ifeq ($(CHIP), ultrasparc-iiii) CFLAGS+= -mcpu=ultrasparc3 -mtune=ultrasparc3 -mvis endif ifeq ($(CHIP), ultrasparc-iiicu) CFLAGS+= -mcpu=ultrasparc3 -mtune=ultrasparc3 -mvis endif # Processors of Family UltraSparc T1 ifeq ($(CHIP), ultrasparc-t1) # niagara switch is not supported on GCC < 4.2 # Sets to default ultrasparc CFLAGS+= -mcpu=ultrasparc -mtune=ultrasparc -mvis endif # If processor detection fails, defaults to UltraSPARC without VIS ifeq (,$(CHIP)) #CPU_TYPE=ultrasparc CFLAGS += -mcpu=ultrasparc -mtune=ultrasparc endif # use -m64 to force 64 bit (but add it also to LDFLAGS), # -m32 for 32 bit (default on solaris), # nothing for arch. default # -mcpu=v9 or ultrasparc? # -mtune implied by -mcpu #-mno-epilogue #try to inline function exit code #-mflat # omit save/restore #-,faster-structs #faster non Sparc ABI structure copy ops else # CC_CLASS, 3.0 ifeq ($(CC_CLASS), 2.9x) #older gcc version (2.9[1-5]) $(warning Old gcc detected ($(CC_SHORTVER)), use gcc >= 3.1 \ for better results) ifneq ($(OS), netbsd) # on netbsd/sparc64, gcc 2.95.3 does not compile # opensips with -mv8 CFLAGS+= -mv8 endif ifeq ($(ASTYPE), solaris) CFLAGS+= -Wa,-xarch=v8plus endif else #CC_CLASS, 2.9x #really old version $(warning You are using an old and unsupported gcc \ version ($(CC_SHORTVER)), compile at your own risk!) CFLAGS+= -mv8 ifeq ($(ASTYPE), solaris) CFLAGS+= -Wa,-xarch=v8plus endif endif #CC_CLASS, 2.9x endif #CC_CLASS, 3.x endif #CC_CLASS, 4.x else #CC_NAME, gcc ifeq ($(CC_NAME), suncc) CFLAGS+=-xO5 -fast -native -xarch=v8plusa -xCC \ -xc99=no_lib # C99 support # -Dinline="" # add this if cc < 5.3 (define inline as null) else #other compilers $(error Unsupported compiler ($(CC):$(CC_NAME)), try gcc) endif #CC_NAME, suncc endif #CC_NAME, gcc endif #ARCH, sparc64 #if ipaq/netwinder ifeq ($(ARCH), arm) # if gcc ifeq ($(CC_NAME), gcc) #common stuff CFLAGS+=$(CC_OPTIMIZE_FLAG) -funroll-loops -Wcast-align $(PROFILE) \ -Wall -marm #if gcc 4.x+ ifeq ($(CC_CLASS), 4.x) CFLAGS+=-mcpu=strongarm1100 -minline-all-stringops \ -ftree-vectorize else #if gcc 3.0+ ifeq ($(CC_CLASS), 3.x) CFLAGS+= -mcpu=strongarm1100 else ifeq ($(CC_CLASS), 2.9x) #older gcc version (2.9[1-5]) $(warning Old gcc detected ($(CC_SHORTVER)), use gcc 3.0.x \ for better results) CFLAGS+= else #really old version $(warning You are using an old and unsupported gcc \ version ($(CC_SHORTVER)), compile at your own risk!) endif # CC_CLASS, 2.9x endif # CC_CLASS, 3.x endif # CC_CLASS, 4.x else # CC_NAME, gcc #other compilers $(error Unsupported compiler ($(CC):$(CC_NAME)), try gcc) endif #CC_NAME, gcc endif #ARCH, arm #if mips (R3000) ifeq ($(ARCH), mips) # if gcc ifeq ($(CC_NAME), gcc) #common stuff CFLAGS+=$(CC_OPTIMIZE_FLAG) -funroll-loops -Wcast-align $(PROFILE) \ -Wall #if gcc 4.0+ ifeq ($(CC_CLASS), 4.x) CFLAGS+=-mcpu=r3000 -minline-all-stringops \ -ftree-vectorize else #if gcc 3.0+ ifeq ($(CC_CLASS), 3.x) CFLAGS+= -mcpu=r3000 #-mcpu=athlon else ifeq ($(CC_CLASS), 2.9x) #older gcc version (2.9[1-5]) $(warning Old gcc detected ($(CC_SHORTVER)), use gcc 3.0.x \ for better results) CFLAGS+=-mcpu=r3000 else #really old version $(warning You are using an old and unsupported gcc \ version ($(CC_SHORTVER)), compile at your own risk!) endif # CC_CLASS, 2.9x endif # CC_CLASS, 3.x endif # CC_CLASS, 4.x else # CC_NAME, gcc #other compilers $(error Unsupported compiler ($(CC):$(CC_NAME)), try gcc) endif #CC_NAME, gcc endif #ARCH, mips #if >=mips2 (R4000, R5000, R6000 ....) ifeq ($(ARCH), mips2) # if gcc ifeq ($(CC_NAME), gcc) #common stuff CFLAGS+=$(CC_OPTIMIZE_FLAG) -mips2 -funroll-loops $(PROFILE) \ -Wall #if gcc 4.0+ ifeq ($(CC_CLASS), 4.x) CFLAGS+=-minline-all-stringops -ftree-vectorize else #if gcc 3.0+ ifeq ($(CC_CLASS), 3.x) CFLAGS+= else ifeq ($(CC_CLASS), 2.9x) #older gcc version (2.9[1-5]) $(warning Old gcc detected ($(CC_SHORTVER)), use gcc 3.0.x \ for better results) CFLAGS+= else #really old version $(warning You are using an old and unsupported gcc \ version ($(CC_SHORTVER)), compile at your own risk!) endif # CC_CLASS, 2.9x endif # CC_CLASS, 3.x endif # CC_CLASS, 4.x else # CC_NAME, gcc #other compilers $(error Unsupported compiler ($(CC):$(CC_NAME)), try gcc) endif #CC_NAME, gcc endif #ARCH, mips2 #if alpha ifeq ($(ARCH), alpha) # if gcc ifeq ($(CC_NAME), gcc) #common stuff CFLAGS+=$(CC_OPTIMIZE_FLAG) -funroll-loops $(PROFILE) -Wall #if gcc 4.0+ ifeq ($(CC_CLASS), 4.x) CFLAGS+=-minline-all-stringops else #if gcc 3.0+ ifeq ($(CC_CLASS), 3.x) CFLAGS+= else ifeq ($(CC_CLASS), 2.9x) #older gcc version (2.9[1-5]) $(warning Old gcc detected ($(CC_SHORTVER)), use gcc 3.0.x \ for better results) CFLAGS+= else #really old version $(warning You are using an old and unsupported gcc \ version ($(CC_SHORTVER)), compile at your own risk!) endif # CC_CLASS, 2.9x endif # CC_CLASS, 3.x endif # CC_CLASS, 4.x else # CC_NAME, gcc #other compilers $(error Unsupported compiler ($(CC):$(CC_NAME)), try gcc) endif #CC_NAME, gcc endif #ARCH, alpha #if ppc ifeq ($(ARCH), ppc) # if gcc ifeq ($(CC_NAME), gcc) #common stuff CFLAGS+=$(CC_OPTIMIZE_FLAG) -funroll-loops $(PROFILE) -Wall #if gcc 4.0+ ifeq ($(CC_CLASS), 4.x) CPU_TYPE ?= powerpc CFLAGS+=-ftree-vectorize \ -mtune=$(CPU_TYPE) -maltivec else #if gcc 3.0+ ifeq ($(CC_CLASS), 3.x) CFLAGS+= else ifeq ($(CC_CLASS), 2.9x) #older gcc version (2.9[1-5]) $(warning Old gcc detected ($(CC_SHORTVER)), use gcc 3.0.x \ for better results) CFLAGS+= else #really old version $(warning You are using an old and unsupported gcc \ version ($(CC_SHORTVER)), compile at your own risk!) endif # CC_CLASS, 2.9x endif # CC_CLASS, 3.x endif # CC_CLASS, 4.x else # CC_NAME, gcc #other compilers $(error Unsupported compiler ($(CC):$(CC_NAME)), try gcc) endif #CC_NAME, gcc endif #ARCH, ppc #if ppc64 ifeq ($(ARCH), ppc64) # if gcc ifeq ($(CC_NAME), gcc) #common stuff CFLAGS+=$(CC_OPTIMIZE_FLAG) -funroll-loops $(PROFILE) -Wall ifeq ($(CC_CLASS), 4.x) CPU_TYPE ?= powerpc64 CFLAGS+=-ftree-vectorize \ -mtune=$(CPU_TYPE) -maltivec else #if gcc 3.0+ ifeq ($(CC_CLASS), 3.x) CFLAGS+= else ifeq ($(CC_CLASS), 2.9x) #older gcc version (2.9[1-5]) $(warning Old gcc detected ($(CC_SHORTVER)), use gcc 3.0.x \ for better results) CFLAGS+= else #really old version $(warning You are using an old and unsupported gcc \ version ($(CC_SHORTVER)), compile at your own risk!) endif # CC_CLASS, 2.9x endif # CC_CLASS, 3.x endif # CC_CLASS, 4.x else # CC_NAME, gcc #other compilers $(error Unsupported compiler ($(CC):$(CC_NAME)), try gcc) endif #CC_NAME, gcc endif #ARCH, ppc endif # CFLAGS not set CFLAGS+= $(CC_EXTRA_OPTS) LD_EXTRA_OPTS ?= # setting LDFLAGS ifeq ($(CC_NAME), gcc) ifeq ($(LDTYPE), solaris) # solaris ld LDFLAGS+=-O2 $(PROFILE) MOD_LDFLAGS=-G $(LDFLAGS) else #gcc and maybe others, => gnu ld LDFLAGS+=-Wl,-O2 -Wl,-E $(PROFILE) MOD_LDFLAGS=-shared $(LDFLAGS) endif endif ifeq ($(CC_NAME), clang) LDFLAGS+=-Wl,-O2 -Wl,-E $(PROFILE) MOD_LDFLAGS=-shared -fPIC -DPIC $(LDFLAGS) endif ifeq ($(CC_NAME), icc) #gcc and maybe others LDFLAGS+=-Wl,-O2 -Wl,-E $(PROFILE) MOD_LDFLAGS=-shared $(LDFLAGS) endif ifeq ($(CC_NAME), suncc) LDFLAGS+=-xO5 $(PROFILE) MOD_LDFLAGS=-G $(LDFLAGS) endif # we need -fPIC -DPIC only for shared objects, we don't need them for # the executable file, because it's always loaded at a fixed address # -andrei else #mode,release ifeq ($(CC_NAME), gcc) CFLAGS +=-Wcast-align $(PROFILE) ifeq ($(ARCH), sparc64) CFLAGS+= -mcpu=ultrasparc endif ifeq ($(LDTYPE), solaris) #solaris ld LDFLAGS+=$(PROFILE) MOD_LDFLAGS=-G $(LDFLAGS) else #gnu or other ld type LDFLAGS+=-Wl,-E $(PROFILE) MOD_LDFLAGS=-shared $(LDFLAGS) endif endif ifeq ($(CC_NAME), icc) CFLAGS+= $(PROFILE) LDFLAGS+=-Wl,-E $(PROFILE) MOD_LDFLAGS=-shared $(LDFLAGS) endif ifeq ($(CC_NAME), suncc) CFLAGS+=$(PROFILE) LDFLAGS+=$(PROFILE) MOD_LDFLAGS=-G $(LDFLAGS) endif endif #mode=release #*FLAGS used for compiling the modules ifeq ($(CC_NAME), gcc) MOD_CFLAGS=-fPIC -DPIC $(CFLAGS) endif ifeq ($(CC_NAME), clang) MOD_CFLAGS=-fPIC -DPIC $(CFLAGS) endif ifeq ($(CC_NAME), icc) MOD_CFLAGS=-Kpic $(CFLAGS) endif ifeq ($(CC_NAME), suncc) #MOD_CFLAGS=-xcode=pic32 $(CFLAGS) MOD_CFLAGS=-Kpic $(CFLAGS) endif DEFAULT_YACC=bison # on solaris add -lxnet (e.g. LIBS= -lxnet) LIBS= -ldl -lresolv #os specific stuff ifeq ($(OS), linux) DEFS+=-DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN -DHAVE_SCHED_YIELD \ -DHAVE_MSG_NOSIGNAL -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_ALLOCA_H \ -DHAVE_TIMEGM ifneq ($(found_lock_method), yes) DEFS+= -DUSE_POSIX_SEM # try posix sems found_lock_method=yes endif ifneq (,$(findstring USE_PTHREAD_MUTEX, $(DEFS))) LIBS += -pthread endif # check for >= 2.5.44 ifeq ($(shell [ $(OSREL_N) -ge 2005044 ] && echo has_epoll), has_epoll) ifeq ($(NO_EPOLL),) DEFS+=-DHAVE_EPOLL # linux + gcc >= 3.0 + -malign-double + epoll => problems CFLAGS:=$(filter-out -malign-double, $(CFLAGS)) endif endif # check for >= 2.2.0 ifeq ($(shell [ $(OSREL_N) -ge 2002000 ] && echo has_sigio), has_sigio) ifeq ($(NO_SIGIO),) DEFS+=-DHAVE_SIGIO_RT endif endif ifeq ($(NO_SELECT),) DEFS+=-DHAVE_SELECT endif endif ifeq ($(OS), gnu_kfreebsd) DEFS+=-DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN -DHAVE_SCHED_YIELD \ -DHAVE_MSG_NOSIGNAL -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_ALLOCA_H \ -DHAVE_TIMEGM ifneq ($(found_lock_method), yes) DEFS+= -DUSE_SYSV_SEM # try posix sems found_lock_method=yes endif # check for ver >= 4.1 ifeq ($(shell [ $(OSREL_N) -gt 4001 ] && echo has_kqueue), has_kqueue) ifeq ($(NO_KQUEUE),) DEFS+=-DHAVE_KQUEUE endif endif ifeq ($(NO_SELECT),) DEFS+=-DHAVE_SELECT endif endif ifeq ($(OS), solaris) DEFS+= -DHAVE_GETIPNODEBYNAME -DHAVE_SYS_SOCKIO_H -DHAVE_SCHED_YIELD \ -DHAVE_ALLOCA_H -DUSE_SIGACTION -D_POSIX_PTHREAD_SEMANTICS ifneq ($(found_lock_method), yes) DEFS+= -DUSE_PTHREAD_MUTEX # try pthread sems found_lock_method=yes endif # check for ver >= 5.7 ifeq ($(shell [ $(OSREL_N) -gt 5007 ] && echo has_devpoll), has_devpoll) ifeq ($(NO_DEVPOLL),) DEFS+=-DHAVE_DEVPOLL endif endif ifeq ($(NO_SELECT),) DEFS+=-DHAVE_SELECT endif ifeq ($(mode), release) #use these only if you're using gcc with Solaris ld #LDFLAGS=-O2 $(PROFILE) #MOD_LDFLAGS=-O2 -G else #LDFLAGS=$(PROFILE) #MOD_LDFLAGS=-G endif DEFAULT_YACC=yacc ifeq ($(CC_NAME), suncc) LIBS= -fast -ldl -lresolv endif OLD_SOLARIS= $(shell echo "$(OSREL)" | \ sed -e 's/^5\.[0-6][^0-9]*$$/yes/' ) LIBS+= -L$(LOCALBASE)/lib -lxnet -lnsl ifeq ($(OLD_SOLARIS), yes) LIBS+=-lposix4 else LIBS+=-lrt endif # -lrt needed for sched_yield endif ifeq ($(OS), freebsd) DEFS+=-DHAVE_SOCKADDR_SA_LEN -DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN \ -DHAVE_SCHED_YIELD -DHAVE_MSGHDR_MSG_CONTROL \ -DHAVE_CONNECT_ECONNRESET_BUG -DHAVE_TIMEGM \ -DHAVE_NETINET_IN_SYSTM ifneq ($(found_lock_method), yes) DEFS+= -DUSE_PTHREAD_MUTEX # try pthread sems found_lock_method=yes LIBS= -pthread #dlopen is in libc else LIBS= #dlopen is in libc endif # check for ver >= 4.1 ifeq ($(shell [ $(OSREL_N) -gt 4001 ] && echo has_kqueue), has_kqueue) ifeq ($(NO_KQUEUE),) DEFS+=-DHAVE_KQUEUE endif endif ifeq ($(NO_SELECT),) DEFS+=-DHAVE_SELECT endif DEFAULT_YACC=yacc endif ifeq ($(OS), openbsd) DEFS+=-DHAVE_SOCKADDR_SA_LEN -DHAVE_GETHOSTBYNAME2 \ -DHAVE_UNION_SEMUN -DHAVE_MSGHDR_MSG_CONTROL \ -DHAVE_CONNECT_ECONNRESET_BUG -DHAVE_TIMEGM ifneq ($(found_lock_method), yes) DEFS+= -DUSE_PTHREAD_MUTEX # try pthread sems found_lock_method=yes endif # check for ver >=2 9 ifeq ($(shell [ $(OSREL_N) -ge 2009 ] && echo has_kqueue), has_kqueue) ifeq ($(NO_KQUEUE),) DEFS+=-DHAVE_KQUEUE endif endif ifeq ($(NO_SELECT),) DEFS+=-DHAVE_SELECT endif # (symbols on openbsd are prefixed by "_") DEFAULT_YACC=yacc # no sched_yield on openbsd unless linking with c_r (not recommended) LIBS= OPENBSD_IS_AOUT= $(shell echo "$(OSREL)" | \ sed -e 's/^3\.[0-3][^0-9]*$$/yes/' |sed -e 's/^[0-2]\..*/yes/') # exception: on sparc openbsd 3.2 is elf and not aout ifeq ($(OSREL), 3.2) ifeq ($(ARCH), sparc) OPENBSD_IS_AOUT:=no endif ifeq ($(ARCH), sparc64) OPENBSD_IS_AOUT:=no endif endif ifeq ($(OPENBSD_IS_AOUT), yes) DEFS+=-DDLSYM_PREFIX='"_"' LDFLAGS= # openbsd ld doesn't like -O2 or -E endif endif # if openbsd ifeq ($(OS), netbsd) DEFS+=-DHAVE_SOCKADDR_SA_LEN -DHAVE_GETHOSTBYNAME2 \ -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_CONNECT_ECONNRESET_BUG -DHAVE_TIMEGM ifneq ($(found_lock_method), yes) DEFS+= -DUSE_SYSV_SEM # try pthread sems found_lock_method=yes endif # check for ver >= 2.0.0 ifeq ($(shell [ $(OSREL_N) -ge 2000000 ] && echo has_kqueue), has_kqueue) ifeq ($(NO_KQUEUE),) DEFS+=-DHAVE_KQUEUE # netbsd + kqueue and -malign-double don't work CFLAGS:=$(filter-out -malign-double, $(CFLAGS)) endif endif ifeq ($(NO_SELECT),) DEFS+=-DHAVE_SELECT endif DEFAULT_YACC=yacc LIBS= endif # OS X support, same as freebsd ifeq ($(OS), darwin) DEFS+=-DHAVE_SOCKADDR_SA_LEN -DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN \ -DHAVE_SCHED_YIELD -DHAVE_MSGHDR_MSG_CONTROL \ -DUSE_ANON_MMAP \ -DNDEBUG -DHAVE_CONNECT_ECONNRESET_BUG -DHAVE_TIMEGM # -DNDEBUG used to turn off assert (assert wants to call # eprintf which doesn't seem to be defined in any shared lib ifneq ($(found_lock_method), yes) DEFS+= -DUSE_PTHREAD_MUTEX # try pthread sems found_lock_method=yes LIBS= -pthread -lresolv #dlopen is in libc else LIBS= -lresolv #dlopen is in libc endif ifeq ($(NO_KQUEUE),) DEFS+=-DHAVE_KQUEUE endif ifeq ($(NO_SELECT),) DEFS+=-DHAVE_SELECT endif LDFLAGS= # darwin doesn't like -O2 or -E MOD_LDFLAGS= -bundle -bundle_loader ../../$(MAIN_NAME) DEFAULT_YACC=yacc endif ifeq ($(LEX),) LEX=flex endif ifeq ($(YACC),) YACC=$(DEFAULT_YACC) endif YACC_FLAGS ?= -d -b cfg ifeq ($(LEX_FLAGS),) LEX_VER=$(shell $(LEX) --version | \ tr -d "[a-zA-Z ]" | \ tr "\." " " | \ awk '{printf "%02d%02d%02d", $$1, $$2, $$3}') # versions lower than 2.5.6 don't have never-interactive ifeq ($(shell [ $(LEX_VER) -lt 020506 ] && echo old), old) LEX_FLAGS = -B else LEX_FLAGS = --never-interactive endif endif ifdef DEBUG_PARSER CFLAGS+=-DYYDEBUG -DDEBUG_PARSER YACC_FLAGS+=-t endif LDFLAGS+= $(LD_EXTRA_OPTS) ifneq (,$(findstring CYGWIN, $(OS))) #cygwin is the same as common ifneq ($(found_lock_method), yes) DEFS+= -DUSE_SYSV_SEM # try sys v sems found_lock_method=yes endif endif #if POSIX sems are to be used, be sure to link with ptread ifneq (,$(findstring USE_POSIX_SEM, $(DEFS))) ifeq (,$(findstring pthread, $(LIBS))) LIBS+= -pthread endif endif #add libsctp if needed ifneq ($(SCTP),) LIBS+= -lsctp endif ifneq ($(found_lock_method), yes) $(warning No locking method found so far, trying SYS V sems) DEFS+= -DUSE_SYSV_SEM # try sys v sems found_lock_method=yes endif endif # ifeq ($(makefile_defs, 1) opensips-2.2.2/Makefile.modules000066400000000000000000000017101300170765700164710ustar00rootroot00000000000000# # module Makefile #(to be included from each module) # MOD_NAME=$(NAME:.so=) ALLDEP=Makefile ../../Makefile.sources ../../Makefile.rules \ ../../Makefile.modules ../../Makefile.conf #override modules value, a module cannot have submodules override modules= override static_modules= override static_modules_path= ifeq ($(MAKELEVEL), 0) # make called directly in the module dir! $(warning "you should run make from the main opensips directory") ifneq ($(makefile_defs), 1) $(error "the local makefile does not include Makefile.defs!") endif ifeq (,$(wildcard ../../Makefile.conf)) $(shell cp ../../Makefile.conf.template ../../Makefile.conf) endif include ../../Makefile.conf else # called by the main Makefile ALLDEP+=../../Makefile ../../Makefile.defs endif include ../../Makefile.sources ifeq (,$(filter $(MOD_NAME), $(static_modules))) CFLAGS:=$(MOD_CFLAGS) LDFLAGS:=$(MOD_LDFLAGS) endif CFLAGS+=-DMOD_NAME='$(MOD_NAME)' include ../../Makefile.rules opensips-2.2.2/Makefile.rules000066400000000000000000000121361300170765700161570ustar00rootroot00000000000000# # # common Makefile rules, included by main Makefile & the modules # # # Uses: NAME, ALLDEP, CC, CFLAGS, DEFS, LIBS, MKDEP, auto_gen, depends, objs, # extra_objs, static_modules, static_modules_path # (all this must be defined previously!, see Makefile.defs & Makefile) # #directory lists for clean rules all_modules=$(shell [ -d modules -a -d utils ] && ls modules) all_utils=$(shell [ -d modules -a -d utils ] && ls utils) builtin_modules=$(shell [ -d net ] && ls net | grep ^proto_ ) all_nostatic_modules=$(filter-out $(addprefix modules/, $(static_modules)), \ $(wildcard modules/*)) all_modules:=$(addprefix modules/, $(all_modules)) all_modules:=$(addprefix net/, $(builtin_modules)) $(all_modules) all_utils:=$(addprefix utils/, $(all_utils)) #implicit rules %.o: %.c $(ALLDEP) ifeq (,$(FASTER)) @echo "Compiling $<" endif $(Q)$(CC) $(CFLAGS) $(DEFS) -c $< -o $@ %.d: %.c $(ALLDEP) @set -e; $(MKDEP) $(DEFS) $< \ | sed 's#\(\($*D)\)\?$(*F)\)\.o[ :]*#$*.o $@ : #g' > $@; \ [ -s $@ ] || rm -f $@ # normal rules $(NAME): $(objs) $(extra_objs) $(ALLDEP) ifeq (,$(FASTER)) @echo "Linking $(NAME)" endif $(Q)$(LD) $(LDFLAGS) $(objs) $(extra_objs) $(LIBS) -o $(NAME) main.o: main.c $(ALLDEP) $(NEWREVISION) ifeq (,$(FASTER)) @echo "Compiling $<" endif $(Q)$(CC) $(CFLAGS) $(DEFS) -c $< -o $@ .PHONY: generate-mem-stats generate-mem-stats: -@rm -f mem/mem_stats.c @echo "/* This file is auto-generated by running 'make generate-mem-stats' */" >> mem/mem_stats.c @echo "/* You should NOT edit this file manually */" >> mem/mem_stats.c @echo "unsigned long core_mem_stat; /* core statistic */" >> mem/mem_stats.c @find modules/ -name "[Mm]akefile" -exec grep NAME= {} \; \ | awk 'BEGIN { FS = "[=\. ]" } {print "unsigned long " $$(NF-1) "_mem_stat;"}' >> mem/mem_stats.c -@rm -f mem/mem_stats.h @echo "/* This file is auto-generated by running 'make generate-mem-stats' */" >> mem/mem_stats.h @echo "/* You should NOT edit this file manually */" >> mem/mem_stats.h @ echo "#ifndef __MEM_STAT_H_\n#define __MEM_STAT_H_" >> mem/mem_stats.h @echo "extern unsigned long core_mem_stat; /* core statistic */" >> mem/mem_stats.h @find modules/ -name "[Mm]akefile" -exec grep NAME= {} \; \ | awk 'BEGIN { FS = "[=\. ]" } {print "extern unsigned long " $$(NF-1) "_mem_stat;"}' >> mem/mem_stats.h @echo "#endif" >> mem/mem_stats.h .PHONY: all all: $(NAME) modules .PHONY: dep dep: $(depends) .PHONY: static static: $(objs) Makefile.conf: Makefile.conf.template @if ! [ -e "$@" ]; then \ echo Creating new $@; \ cp $< $@; \ fi $(cfg_prefix)/$(cfg_dir): mkdir -p $(cfg_prefix)/$(cfg_dir) $(bin_prefix)/$(bin_dir): mkdir -p $(bin_prefix)/$(bin_dir) $(modules_prefix)/$(modules_dir): mkdir -p $(modules_prefix)/$(modules_dir) $(doc_prefix)/$(doc_dir): mkdir -p $(doc_prefix)/$(doc_dir) $(man_prefix)/$(man_dir)/man8: mkdir -p $(man_prefix)/$(man_dir)/man8 $(man_prefix)/$(man_dir)/man5: mkdir -p $(man_prefix)/$(man_dir)/man5 $(data_prefix)/$(data_dir): mkdir -p $(data_prefix)/$(data_dir) .PHONY: dosetrev dosetrev: @echo "New $(VERSIONTYPE) revision: $(THISREVISION)" @if [ -f "main.o" ] ; then \ rm main.o ; \ fi @echo "$(THISREVISION)" >.$(VERSIONTYPE)revision .PHONY: docbook-clean docbook-clean: -@for r in $(modules) $(static_modules_path) "" ; do \ if [ -d "$$r" ]; then \ if [ -d "$$r"/doc ]; then \ rm -f "$$r"/doc/*.txt ; \ rm -f "$$r"/doc/*.html ; \ rm -f "$$r"/doc/*.pdf ; \ fi ; \ fi ; \ done .PHONY: dbschema-docbook-clean dbschema-docbook-clean: -@if [ -d doc/database ] ; then \ rm -f doc/database/*.txt ; \ rm -f doc/database/*.html ; \ rm -f doc/database/*.pdf ; \ $(MAKE) -C db/schema docbook_clean; \ fi .PHONY: clean clean: docbook-clean dbschema-docbook-clean -@rm -f $(objs) $(NAME) $(objs:.o=.il) 2>/dev/null -@for r in $(all_modules) "" ; do \ if [ -d "$$r" -a -f "$$r/Makefile" ]; then \ $(MAKE) -C $$r clean ; \ fi ; \ done -@for r in $(all_utils) "" ; do \ if [ -d "$$r" -a -f "$$r/Makefile" ]; then \ $(MAKE) -C $$r clean ; \ fi ; \ done .PHONY: proper .PHONY: distclean .PHONY: realclean proper realclean distclean: clean -@rm -f $(depends) $(auto_gen) 2>/dev/null -@rm -f cfg.tab.h 2>/dev/null -@for r in $(all_nostatic_modules) "" ; do \ if [ -d "$$r" -a -f "$$r/Makefile" ]; then \ $(MAKE) -C $$r proper ; \ fi ; \ done -@if [ -d menuconfig ]; then $(MAKE) -C menuconfig proper; fi -@if [ -d utils/opensipsunix ]; then $(MAKE) -C utils/opensipsunix proper; fi -@if [ -d utils/db_berkeley ]; then $(MAKE) -C utils/db_berkeley proper; fi -@if [ -d utils/db_oracle ]; then $(MAKE) -C utils/db_oracle proper; fi .PHONY: mantainer-clean mantainer-clean: distclean -rm -f TAGS tags *.dbg .*.swp -@for r in $(modules) "" ; do \ if [ -d "$$r" ]; then \ $(MAKE) -C $$r mantainer-clean; \ fi ; \ done -@if [ -d utils/opensipsunix ]; then $(MAKE) -C utils/opensipsunix mantainer-clean; fi .PHONY: install_module_custom .PHONY: TAGS TAGS: $(MKTAGS) ifeq (,$(MAKECMDGOALS)) -include $(depends) endif ifneq (,$(filter-out clean proper distclean realclean mantainer-clean TAGS \ tar modules, $(MAKECMDGOALS))) -include $(depends) endif opensips-2.2.2/Makefile.sources000066400000000000000000000011571300170765700165110ustar00rootroot00000000000000# # common Makefile variables # (source files, objs, etc) # # expects: auto_gen defined # # defines: sources, objs, depends # sources=$(filter-out $(auto_gen) $(exclude_files), $(wildcard *.c) \ $(wildcard mem/*.c) $(wildcard aaa/*.c) \ $(wildcard parser/*.c) \ $(wildcard parser/digest/*.c) \ $(wildcard parser/sdp/*.c) \ $(wildcard parser/contact/*.c) $(wildcard db/*.c) \ $(wildcard mi/*.c) $(wildcard evi/*.c) $(wildcard cachedb/*.c) \ $(wildcard net/*.c) $(wildcard net/proto*/*.c)) $(auto_gen) objs=$(sources:.c=.o) extra_objs= depends=$(sources:.c=.d) modules= static_modules= static_modules_path= opensips-2.2.2/NEWS000066400000000000000000000566611300170765700141000ustar00rootroot00000000000000What is new in 1.6.0 2.1 Core * new 'statistic classes for network and pkg_mem information: NET class , to provide information about the network sockets and PKMEM class, to provide information about the private memory of each process - see http://lists.opensips.org/pipermail/users/2009-April/004868.html * new mem_dump core parameter for a better control on logging memory info. Former "mem_log" was split in mem_log (only for debug log during mallog/free/realloc ops) and mem_dump (for dumping the mem status at runtime/shutdown). This will help in debugging memory leaks - you can request only the mem dumping without actually getting all the debugs during mem ops. For backward compatibility, setting mem_log will automatically set to the same value the mem_dump. To get distinct setting, do: mem_log = 6; # do not report mem logs (report them as debugs) mem_dump = 0; # report mem dumps (report on ERR level) * new '''db_version_table" core parameter to allow configuration of custom names for "version" table. There are cases when a single DB contains tables from 2 versions of OpenSIPS, so distinct version tables are needed. db_version_table = "version_1_6"; * extendes the idea of the advertised_address and advertised_port parameters so that it is possible to bind such an address to each listen= statement. Syntax-wise, it works like this: listen=[proto:]host[:port][ AS host[:port]] See Patch #2706135 (see https://sourceforge.net/tracker/?func=detail&aid=2706135&group_id=232389&atid=1086412) 2.2 Script Routes * The routes can have string names also, not only numerical IDs as it was until now. This way the routing flow will be more meaningful and easier to understand and follow. The backward compatibility was kept - and you can also use number IDs as route names. * new startup_route type of script route - called only once when the server is started and before the listener processes are created. You can configure here some initialization actions to ease up the future processing. For example, you could take some values from database and store them in cache or in shared memory pseudovariables to have faster access to those values when needed. * new timer_route type of script route - called on a timer basis at a given interval. This can be used to configure some periodical actions that do some data refresh actions for example. The time interval is given after the name of the route separated by comma and it represents a value in seconds. 2.3 Pseudo variables * $an - the nonce from Authorization or Proxy-Authorization header * $ar - the authentication response from Authorization or Proxy-Authorization header * $branch - replaces the $br variable (http://www.opensips.org/Resources/DocsCoreVar#toc13) * $(branch()[n]) - this variable provides read/write access to all fields/attributes of an already existing branch (priorly created with append_branch()). The variable accepts also index $(branch(uri)[1]) for accessing a specific branch (multiple branches can be defined at a moment). The index starts from 0 (first branch). If the index is negative, it is considered the n-th branch from the end ( index -1 means the last branch).To get all branches, use the * index - $(branch(uri)[*]). - see http://www.opensips.org/Resources/DocsCoreVar#toc14. The fields of the branch are: o uri - the RURI of the branch (string value) o duri - destination URI of the branch (outbound proxy of the branch) (string value) o q - q value of the branch (int value) o path - the PATH string for this branch (string value) o flags - the branch flags of this branch (int value) o socket - the local socket to be used for relaying this branch (string value) * $TS - startup time stamp * delete the value at a certain index; syntax: $pvar[index]= NULL; * overwrite the value at a certain index; syntax: $pvar[index] = new_val * new PV $time(format) to print time is dynamic formatted time string. Format: see "man strftime" ; Ex: $time(B %Y) -> Thursday September 2009 * added context meaning to pseudovariables - a new token added in front of the pv type name that specifies the context where the pv should be taken from; for now there are 2 pv contexts: reply and request, registered by the tm module o $(ru) from a reply route will get the Request-URI from the request o $(hdr(Contact)) context can be used from failure route to access information from the reply 2.4 Transformations * added a new nameaddr transformation: param; it extract the value of a certain parameter o example: avp(from) = $(hdr(From){nameaddr.param, tag}); 2.5 AAA API and enhancement of RADIUS support(NEW) AAA API The AAA API represents a set of generic callbacks and structures needed for AAA operations. The purpose of the API is to move all the AAA specific (Radius) implementations in a single module. The AAA API will hide the implementations details from the modules that want to use AAA support, making it easier to use. Currently, only one module implements the AAA API - aaa_radius, offering Radius support. Because both standardized AAA protocols, Radius and Diameter, use lists of attribute-value pairs, the API is designed based on attribute-value pairs, as well. One of the advantages of using the API is that any module that wishes to do AAA operations does not depend on a certain protocol implementation, therefore it is not linked with a certain AAA library. Hence, any module that was previously using Radius, for example, is not linked with the radiusclient library any more, which is obviously an advantage, especially when the module uses more than one API (DB, AAA, etc). The AAA protocol to be used by a module is specified from the script using a so called "aaa_url" that encodes the protocol used and other useful information depending on the protocol. The way the generic AAA API is used makes it very similar to the generic database API. The ability to make custom Radius requests directly from the script This feature is very useful because of its flexibility. Basically, any type of Radius queries can be yielded directly from the script, and also, Radius replies can be inspected for certain attributes. Radius implementation for the generic AAA API The handling of Radius AVP "SIP-AVP" (used for fetching variables from the Radius server) was integrated in this module. All SIP-AVPs are automatically and transparently handled by the module, so that this functionality is by default available for all the modules that use Radius. Module changes Due to the fact that the modules that were previously using Radius were ported to the generic AAA API, the following changes have been made: * auth_radius modules became auth_aaa module * group_radius module was merged with group module * uri_radius and uri_db were merged with uri module 2.6 B2BUA support (NEW) A signaling B2BUA implementation has been added in OpenSIPS. It can keep the state of the dialogs, create new dialogs and control dialogs to offer some PBX specific services. This feature is implemented in two modules: b2b_entities and b2b_logic. The services are defined in XML documents that are parsed and applied by the b2b_logic module. It uses the basic dialog management functions offered by the b2b_entities module to command the actions needed to implement the service. Find more on http://www.opensips.org/Main/News0036. 2.7 MEMCACHED module (NEW) A new module that provides a new caching method using memcached servers was added. It provides a way to access memcached servers using the existing memcache API. Advantages over the existing "localcache" module: * memory costs are no longer on the server * many servers may be used so the memory is virtually unlimited * the cache is persistent so a restart of the server will not affect the cache * memcached is an open-source project so it can be used to exchange data with various other applications * servers may be grouped together (e.g. for security purposes : some can be inside a private network, some can be in a public one). Find more on http://lists.opensips.org/pipermail/users/2009-July/007024.html or http://www.opensips.org/html/docs/modules/devel/memcached.html 2.8 DB_HTTP module (NEW) This module provides access to a database that is implemented as a HTTP server. It may be used in special cases where traversing firewalls is a problem, or where data encryption is required. In order to use this module you must have a server that can communicate via HTTP or HTTPS with this module that follows exactly the format decribed in the specifications section. The module can provide SSL, authentication, and all the functionalities of an opensips db as long as the server supports them ( except result_fetch). There is a slight difference between the url of db_http and the urls of the other db modules. The url doesn't have to contain the database name. Instead, everything that is after the address is considered to be a path to the db resource, it may be missing. Find more on http://www.opensips.org/html/docs/modules/devel/db_http.html 2.9 DB_VIRTUAL module (NEW) The DB_VIRTUAL module allows you to create virtual DB connection on top of real connections. A virtual connection may use multiple real connection (a set), acting as a mixer layer - based on different algorithms, the DB operation pushed on the virtual DB will be pushed further to one or more real DB connections. The DB_VIRTUAL provides failover mode (you have a master real connection and when this is down, the queries will be pushed via the next real connection from the set), parallel mode (the received query is pushed via all real connections) and round-robin mode ( the received queries are spread across the set of real connections). The purpose of this module is to provide failover and load-balancing at DB connection level in a totally transparent way for the modules that needs DB support. Read more on http://www.opensips.org/Main/News0035. 2.10 JSON module (NEW) This module introduces a new type of variable that provides both serialization and de-serialization from JSON format. The Json format it is very useful as you can encode an unlimited number (and types) of info elements into a single string. For example a gateway description may contain several infos on the GW (if SST is accepted, RPID/PAI, etc), also a user profile (with all its date) can be condensed into a single string, so that DB ops are reduced to a maximum. The variable provides ways to access objects and arrays to add,replace or delete values from the script. The correct approach is to consider a json object as a hashtable ( you can put (key;value) pairs, and you can delete and get values by key) and a json array as an array ( you can append, delete and replace values). Since the JSON format can have objects inside other objects you can have multiple nested hashtables or arrays and you can access these using paths. See more on http://www.opensips.org/html/docs/modules/devel/json.html 2.11 STUN server module (NEW) There is a new STUN server(rfc 3489) module integrated in OpenSIPS. Why an integrated STUN server module? * A STUN server uses 2 ips and 2 ports to create 4 sockets on which to listen or respond. * This integrated STUN module uses the SIPS sockets created by the OpenSIPS core: o one (or two) of the sockets will overlap with the SIP signaling socket o the rest will be created by this module for STUN only purposes. In the case of a bad behaving NATs (such as symmetric NAT) only this integrated STUN server will increase the likelihood of STUN to solve the NAT traversal for a wider range of NAT implementations. This translates into less need of media relaying (for NAT traversal purposes). Note: * It is important to set the primary_ip:primary_port to match the OpenSIPS signalling ip and ports. * If OpenSIPS binds one more interface this module will use that too; so make sure you set the correct alternate_ip:primary_port. * STUN requires 2 routable ip addresses; if in shortage create a virtual interface (i don't recommend because it destroys the purpose for some usages). Example: Opensips listening for signaling traffic: 89.89.89.12:5060 for stun module we configure : primary_ip = 89.89.89.12; primary_port = 5060 - overlapping with signaling socket primary_ip = 89.89.89.12; alternate_port = 3478 - dedicated stun socket alternate_ip = 34.34.34.12; primary_port = 5060 - dedicated stun socket alternate_ip = 34.34.34.12; alternate_port = 3478 - dedicated stun socket Documentation can be found here: http://www.opensips.org/html/docs/modules/devel/stun.html 2.12 ALIAS_DB module * new function alias_db_find("table","input_var","output_var") - similar to alias_db_lookup(), but instead of using fixed input (RURI) and output (RURI) is able to get the input SIP URI from a pseudo-variable and place the result back also in a pseudo-variable. The function is useful as the alias lookup does not affect the request it self (no RURI changes), can be used in a reply context (as it does not work with RURI only) and can be used for others URI than the RURI ( To URI, From URI, custom URI). The function returns TRUE any alias mapping was found and returned. ->See http://www.opensips.org/html/docs/modules/1.6.x/alias_db.html#id228302 * both alias_db_lookup() and alias_db_find() may take an optional param, a list of flags (char based flags): o 'd' - do not use domain part in the lookup (by default domain is used) o 'r' - do reverse lookup - the input is the ID URI and the result is alias URI (normally the input is the alias URI and the result is the ID URI) * use_domain module parameter was removed as replaced by the "d" per-lookup option. 2.13 AVPOPS module * New function avp_insert(_pvar_name_, value, index) - inserts a value to an avp set at a certain position 2.14 AVP_RADIUS module important - Removed as its functionality is now part of the AAA_RADIUS module - see the AAA API and module changes. 2.15 AUTH_RADIUS module important - Renamed as AUTH_AAA module - see the AAA API and module changes. 2.16 BENACHMARK module * allow granularity == 0 (disables automatic logging) * add bm_poll_results MI command (poll current results and restart local counters) * reject granularity < 0 2.17 CALL_CONTROL module * new parameters init, start, stop added to support customized call_control messages directly from the script * example: modparam("call_control", "init", "call-id=$ci to=$tu from=$fu authruri=$du another_field = $avp(extra)") * the message sent if one of these parameter is set will be the string resulted after expanding the pvars 2.18 CFGUTILS module * New function set_count(_pvar_name_) - returns the number of values of the pseudovariable * New function set_select_weight(_pvar_name_) - selects an element from the set formed by the values of the pseudovariable; the selection algorithm is the genetic algorithm roulette-wheel selection; it return the index of the element 2.19 DIALOG module * added support for early dialogs in order to properly support PRACK requests * dialog script flags, dialog profiles and dialog variables have DB persistence - all these data will be kept over an OpenSIPS restart. * new function validate_dialog() for verifying the in-dialog requests against the date stored in dialog internal structure (cseq, rr set, contact) - this is used to discover bogus or malicious sequential requests. * new PV $DLG_dir to return (as string) the direction of an in-dialog request. 2.20 DISPATCHER module * ds_is_from_list() replaced with a more generic function ds_is_in_list("ip","port"). The new function takes as parameters the IP and PORT to test against the dispatcher list, instead of using only the source IP and PORT (as ds_is_from_list()). ds_is_from_list() == ds_is_in_list("$si","$sp") The new function can be used also if the destination is part of a dispatching set : ds_is_in_list("$rd","$rp") 2.21 DROUTING module * goes_to_gw() takes a second optional parameter (flags) to control the strip, the prefix and attr when GW matches 2.22 GROUP module * is_user_in() renamed as db_is_user_in() * get_user_group() renamed as db_get_user_group() 2.23 GROUP_RADIUS module important - Removed as merged into GROUP module - see the AAA API and module changes. 2.24 LOAD_BALANCER module * more algorithm for computing the load are available - relative or absolute values. See: http://www.opensips.org/html/docs/modules/1.6.x/load_balancer.html#id227286 * function load_balance() accepts the group parameter as pseudo-variable also * support for destination probing (pinging) and enabling / disabling state. The module is now capable to monitor the status of the destinations by doing SIP probing (sending SIP requests like OPTIONS) and to automatically enables / disable destination based on the probing result. See: http://lists.opensips.org/pipermail/users/2009-July/006818.html 2.25 MI_DATAGRAM * the UDP communication is symmetric - the UDP reply packages are sent back from the RX socket - this helps when firewalls, nat , etc are in the middle; also from security point of view is a plus to know all the time the ports involved in the communication. 2.26 NAT_TRAVERSAL * new UAC test "8" - tests if client has contacted OpenSIPS from an address that is different from the one in the Contact field. Only IP is compared by this test. 2.27 NATHELPER module * MI command "nh_enable_ping", if triggered with no parameter, will return the internal status of natping (if enabled or disabled) * new UAC test "32" - address in Contact is compared against source IP address of signaling 2.28 PERMISSIONS module * this module was reorganized to use only one database table (so that no duplicate information is used) : trusted database table is no longer used and there is a new schema for the address database table * the following script parameters are no longer used: peer_tag_avp, trusted_table, source_col * ip_addr_col parameter was renamed as ip_col * tag_col parameter was renamed as info_col * from_col parameter was renamed as pattern_col * the following functions were introduced: check_address(), check_source_address(), get_source_group() to replace allow_address(), allow_source_address(), allow_trusted() * the new functions perform the same, but they are more flexible and use only one database table 2.29 PIKE module New extensions on the pike module for extending the flood detection : (1) from checking requests only, to checking all SIP traffic and (2) from checking the valid SIP packages to checking all received data (even if junk). New way of using the module -> automatic - the module will install internal hooks to catch all incoming requests and replies (even if not well formed from SIP point of view) - more or less the module will monitor all incoming packages (from the network) on the SIP sockets. Each time the source IP of a package needs to be analyse (to see if trusted or not), the module will run a script route - see "check_route" module parameter -, where, based on custom logic, you can decide if that IP needs to be monitored for flooding or not. As action, when flood is detected, the module will automatically drop the packages. 2.30 REGISTRAR module * former global parameters like method_filtering, max_contacts, append_branches, use_path, path_mode and path_use_received are now per AOR / function parameters ; also * lookup() function takes char flags (see http://www.opensips.org/html/docs/modules/devel/registrar.html#id271073): o if parallel forking should be done or not for this lookup (if branches should be appended or not) o if method filtering should be performed or not for this lookup * save() function takes char flags (see http://www.opensips.org/html/docs/modules/devel/registrar.html#id228571): o if contacts should not be saved in DB (cache update only) o if no reply should be sent out o the maximum number of contacts allowed for AOR o path support control (what path mode should be used) - (former global params) o path received (former global param) * registered() function takes a third optional parameter - a callid ; in this case, the function will check if a contact with that callid is already registered or not. * sock_flag was moved as flag of the save() function 2.31 TEXTOPS module * new functions were added for SDP codec manipulation (see http://lists.opensips.org/pipermail/users/2009-July/007196.html): o codec_exists(name[,clock]); //test if a codec exists o codec_delete(name[,clock]); //delete a codec o codec_move_up(name[,clock]); //move a codec to the front of the list o codec_move_down(name[,clock]);//move a codec to the back of the list o codec_exists_re ( regexp ); //test if a codec exists based on a POSIX regular expression. o codec_delete_re( regexp ); //delete a codec based on a POSIX regular expression. o codec_move_up_re( regexp ); //move a codec to the front of the list, based on a POSIX regular expression. o codec_move_down_re( regexp );//move a codec to the back of the list, based on a POSIX regular expression. 2.32 TM module * RFC 3326 support (Call Completed Elsewhere) added: A proxy forks an INVITE request and one of the branches returns a 200 (OK). The forking proxy includes this status code in a Reason header field in the CANCEL request that it sends to the rest of the branches. * t_reply() function does not requires any more a prior create Transaction (as in 1.5). If no transaction is found, it will be automatically created. * t_cancel_branch() take flags ("a" - cancel all branches; "o" - cancel all other branches except current; "" - current branch); t_cancel_call() is obsoleted and removed (same as t_cancel_branch("a")). * per branch onreply route - when calling "t_on_reply()" from branch route, the reply route will be set only for the current branch (and not for the whole transaction) - that's it, it will be called only for relies belonging to that particular branch. Of course, from branch route, you can set different reply routes for each branch. * - removed the dependency to Retry-After in 503 in order to do DNS based failure. This was debated on the sip-implementers mailing list and as the RFC3261 is a bit ambigous in that area, we prefer to have the common sense taking over. 2.33 UAC module * new functions uac_replace_to() and uac_restore_to() to perform TO header changing in a safe manner - similar to uac_replace_from() and uac_restore_from() 2.34 URI_DB module important - This module does not exists anymore as its was merged into URI module (see AAA API and module changes). All functions from this module are now in URI module and prefixed db_ string. * New function get_auth_id() - Checks given uri-string username against URI table (if use_uri_table is set) or subscriber table database backend required). Returns true if the user exists in the database, and sets the given variables to the authentication id and realm corresponding to the given uri. 2.35 URI_RADIUS module important - This module does not exists anymore as its was merged into URI module (see AAA API and module changes). All functions from this module are now in URI module and prefixed radius_ prefix from the name of the functions was replaced with aaa_. opensips-2.2.2/README000066400000000000000000000045121300170765700142450ustar00rootroot00000000000000$Id$ ****************************************************************************** * * * Welcome to OpenSIPS Project * * * ****************************************************************************** About ----- OpenSIPS is a GPL licensed SIP server implementation. It started as a fork of Fokus Fraunhofer SIP Express Router (SER) project. OpenSIPS wants to be a more open project, not only from license point of view, but more open as project management, especially for external contributions. OpenSIPS wants to overcome the development latency of current SER project, to ensure a shorter path into a release for new added features. OpenSIPS is a project maintained by OpenSIPS Solutions http://www.opensips-solutions.com/ by a team including core and main developers of SER project. Info ---- For information regarding the OpenSIPS installation, please see the INSTALL file. For current developers/contributors of this project, see the CREDITS and AUTHORS file. For complete license information, please see the COPYING file. For a quick overview of OpenSIPS modules, please see the README-MODULES file. Docs ---- Documentation about each module can be found in the README file in each module directory. For online documentation, please see http://www.opensips.org/Resources/Documentation For additional documentation, tutorials and examples please see also http://www.opensips.org/Resources/DocsTutorials Questions --------- For any question related to the OpenSIPS usage, please use the users@lists.opensips.org public mailing list. For questions regarding the development of OpenSIPS - like contributions, bug reports, etc - please use the devel@lists.opensips.org public mailing list. For questions regarding businesses around OpenSIPS - like products, consultancy, trainings, etc - please use the business@lists.opensips.org public mailing list. Also there is a generic news mailing list where you can learn about what is new or important for the OpenSIPS project, about alerts and updates regarding relaces and about events around the project. news@lists.opensips.org opensips-2.2.2/README-MODULES000066400000000000000000000333441300170765700153600ustar00rootroot00000000000000# $Id$ The content here is quite obsolete. Please refer to http://www.opensips.org/Resources/DocsModules16 for a actual list. 'Use' tells how deployable a module is. Regular indicates we deploy and use this feature in our infrastructure. Experimental tells we are still gaining operation experience. 'Maturity' is label as stable if a module has been deployed for longer time, alpha if it is still being developed and beta if it is under test. 'Depends on' field tells which modules the module needs. List of currently available opensips modules: Name: aaa_radius Use: regular Maturity: beta Depends on: Purpose: RADIUS implementation for AAA API Name: acc Use: regular Maturity: stable Depends on: tm, rr, mysql|postgres|unixodbc|dbtext|flatstore Purpose: Transaction accounting Name: alias_db Use: regular Maturity: stable Depends on: mysql|postgres|unixodbc|dbtext Purpose: Alias with DB support (no caching). Name: auth Use: regular Maturity: stable Depends on: sl Purpose: Common functions for digest authentication Name: auth_aaa Use: regular Maturity: stable Depends on: auth Purpose: AAA support for digest authentication Name: auth_db Use: regular Maturity: stable Depends on: mysql auth Purpose: Database support for digest authentication Name: auth_diameter Use: experimental Maturity: alpha Depends on: auth Purpose: Diameter support for digest authentication Name: avpops Use: regular Maturity: stable Depends on: Purpose: avp operations, pseudo-variable support Name: b2b_entities Use: regular Maturity: beta Depends on: Purpose: B2BUA framework Name: b2b_logic Use: regular Maturity: beta Depends on: b2b_entities Purpose: B2BUA controller via XML based scenarios Name: benchmark Use: experimental Maturity: beta Depends on: Purpose: Support for script benchmarking Name: call_control Use: experimental Maturity: alpha Depends on: dialog Purpose: Prepaid like functionality Name: carrierroute Use: experimental Maturity: beta Depends on: db-module Purpose: LCR Name: cfgutils Use: experimental Maturity: beta Depends on: Purpose: Script functions for randomization and others. Name: closeddial Use: experimental Maturity: alpha Depends on: db-module Purpose: Group dialing (PBX like). Name: cpl_c Use: regular Maturity: stable Depends on: tm, sl, usrloc Purpose: Call Processing Language interpreter Name: db_berkeley Use: regular Maturity: beta Depends on: Purpose: Interface to Berkeley database. Name: db_flatstore Use: regular Maturity: beta Depends on: Purpose: Very fast accounting in flat text files Name: db_http Use: experimental Maturity: alpha Depends on: Purpose: DB interaction via HTTP Name: db_mysql Use: regular Maturity: stable Depends on: Purpose: MySQL database support Name: db_oracle Use: regular Maturity: beta Depends on: Purpose: Interface to Oracle database. Name: db_postgres Use: regular Maturity: stable Depends on: Purpose: Postgress DB support Name: db_text Use: regular Maturity: stable Depends on: Purpose: Database emulation in plaintext files Name: db_unixodbc Use: regular Maturity: beta Depends on: Purpose: Unixodbc DB support Name: db_virtual Use: regular Maturity: beta Depends on: Purpose: Virtual DB URLs based on existing real URL connections Name: dialog Use: regular Maturity: stable Depends on: tm, rr, db-module Purpose: Dialog support, call tracking Name: dialplan Use: regular Maturity: stable Depends on: db-module Purpose: Dialplan implementation based on translation rules Name: dispatcher Use: regular Maturity: stable Depends on: Purpose: Implements a dispatcher for incoming requests using hashes Name: diversion Use: experimental Maturity: beta Depends on: - Purpose: Support for the Diversion extensions (draft-levy-sip-diversion-08) Name: domain Use: regular Maturity: stable Depends on: db-module Purpose: support for maintenance of multiple domains and related functions Name: domainpolicy Use: experimental Maturity: alpha Depends on: db-module Purpose: Federation membership based on DNS records. Name: drouting Use: regular Maturity: stable Depends on: db-module Purpose: Scalable and performant LCR. Name: enum Use: regular Maturity: stable Depends on: Purpose: Enum lookup support Name: exec Use: regular Maturity: beta Depends on: - Purpose: Execution of external URI processing logic Name: gflags Use: regular Maturity: stable Depends on: - Purpose: global flags that can be changed by fifo commands Name: group Use: regular Maturity: stable Depends on: database Purpose: Group membership checking using database and AAA Name: h350 Use: regular Maturity: stable Depends on: - Purpose: Access to data stored in an LDAP dir containing H.350 commObjects Name: identity Use: experimental Maturity: alpha Depends on: - Purpose: Support for SIP Identity (see RFC 4474) Name: imc Use: experimental Maturity: beta Depends on: database Purpose: Instance Messaging Chat support Name: jabber Use: experimental Maturity: beta Depends on: database tm Purpose: SIMPLE 2 Jabber gateway Name: json Use: experimental Maturity: beta Depends on: Purpose: JSON encoder / decoder Name: ldap Use: regular Maturity: stable Depends on: - Purpose: LDAP search interfac (search ops) Name: load_blancer Use: experimental Maturity: beta Depends on: dialog, database Purpose: Load Balancer routing engine Name: localcache Use: experimental Maturity: stable Depends on: - Purpose: Implementation of Cache interface (local storage in hash table) Name: mangler Use: experimental Maturity: alpha Depends on: - Purpose: Helper with SDP mangling Name: maxfwd Use: regular Maturity: stable Depends on: - Purpose: Max-Forwards check Name: mediaproxy Use: regular Maturity: stable Depends on: mediaproxy media relay Purpose: nat traversal Name: memcached Use: experimental Maturity: beta Depends on: - Purpose: Implementation of Cache interface via memcached servers Name: mi_datagram Use: regular Maturity: stable Depends on: - Purpose: Datagram (UDP and unixsock) communication for Mangement Interface. Name: mi_fifo Use: regular Maturity: stable Depends on: - Purpose: FIFO file communication for Mangement Interface. Name: mi_xmlrpc Use: regular Maturity: stable Depends on: - Purpose: XMLRPC communication for Mangement Interface. Name: mmgeoip Use: experimental Maturity: alpha Depends on: - Purpose: IP address-to-location lookup capability. Name: msilo Use: regular Maturity: stable Depends on: database tm Purpose: Message store Name: nathelper Use: regular Maturity: stable Depends on: rtpproxy Purpose: nat traversal Name: nat_traversal Use: regular Maturity: beta Depends on: dialog, tm, sl Purpose: nat traversal (signalling only) Name: options Use: experimental Maturity: stable Depends on: - Purpose: Answers server options requests Name: osp Use: experimental Maturity: alpha Depends on: textops, OSP Toolkit Purpose: Secure, multi-lateral peering using the OSP standard Name: path Use: experimental Maturity: beta Depends on: rr Purpose: PATH support for complex routing Name: peering Use: experimental Maturity: alpha Depends on: - Purpose: Tusted peer relationship management Name: perl Use: experimental Maturity: beta Depends on: - Purpose: Perl script execution Name: perlvdb Use: experimental Maturity: beta Depends on: - Purpose: Perl vistual DB Name: permissions Use: experimental Maturity: stable Depends on: database Purpose: hosts.allow-like ACLs Name: pike Use: experimental Maturity: beta Depends on: - Purpose: Excessive load detection Name: presence Use: regular Maturity: stable Depends on: database, signaling Purpose: Handles PUBLISH and SUBSCRIBE messages (SIMPLE engine) Name: presence_callinfo Use: experimental Maturity: alpha Depends on: presence Purpose: Event CALLINFO implementation Name: presence_dialoginfo Use: experimental Maturity: alpha Depends on: dialog, presence Purpose: Event DIALOG implementation Name: presence_mwi Use: experimental Maturity: alpha Depends on: presence Purpose: Event MWI implementation Name: presence_xcapdiff Use: experimental Maturity: alpha Depends on: presence Purpose: Event XCAPDIFF implementation Name: presence_xml Use: regular Maturity: stable Depends on: presence,signaling,xcap_client,database Purpose: Handling for notify-subscribe events using xml bodies Name: pua Use: regular Maturity: stable Depends on: tm Purpose: Presence user agent client, sending Subscribe and Publish messages Name: pua_bla Use: experimental Maturity: alpha Depends on: pua Purpose: Presence user agent client for BLA events Name: pua_dialoginfo Use: experimental Maturity: alpha Depends on: pua, dialog Purpose: PUA Publisher for Event DIALOG Name: pua_mi Use: regular Maturity: stable Depends on: pua Purpose: PUA Publisher via Management Interface Name: pua_usrloc Use: regular Maturity: stable Depends on: pua, usrloc Purpose: PUA Publisher based on user location (registrations) Name: pua_xmpp Use: regular Maturity: stable Depends on: presence, pua, xmpp Purpose: SIP 2 XMPP transaltion for presence Name: python Use: experimental Maturity: alpha Depends on: dialog Purpose: Track of per dialog SDP session(s) Name: qos Use: experimental Maturity: alpha Depends on: dialog Purpose: Track of per dialog SDP session(s) Name: ratelimit Use: experimental Maturity: beta Depends on: - Purpose: Rate limiting for SIP requests. Name: regex Use: experimental Maturity: alpha Depends on: Purpose: PERL like (PCRE) regexp support. Name: registrar Use: regular Maturity: stable Depends on: usrloc, siganling Purpose: SIP Registrar Name: rls Use: experimental Maturity: beta Depends on: database, siganling, tm, presence, pua Purpose: Resource List Server implementation (RFC 4826) Name: rr Use: regular Maturity: stable Depends on: - Purpose: Routing and Record-Routing Name: rtpproxy Use: regular Maturity: stable Depends on: - Purpose: NAT Traversal using RTPProxy Name: seas Use: regular Maturity: stable Depends on: - Purpose: Connector to weSIP (SIP Servlet Java server) Name: signaling Use: regular Maturity: stable Depends on: Purpose: Signalling wrapper (stateless and stateful) Name: siptrace Use: experimental Maturity: stable Depends on: tm, sl, dialog Purpose: SIP traffic logging Name: sl Use: regular Maturity: stable Depends on: tm, sl Purpose: Stateless replies Name: sms Use: regular Maturity: stable Depends on: tm Purpose: SMS gateway Name: snmpstats Use: regular Maturity: stable Depends on: Purpose: SNMP interface for OpenSIPS Name: speeddial Use: regular Maturity: stable Depends on: database Purpose: On-server speed dial facilities Name: sst Use: regular Maturity: stable Depends on: dialog Purpose: SIP Session Timer implementation Name: statistics Use: regular Maturity: stable Depends on: - Purpose: Routing script statistics Name: stun Use: regular Maturity: alpha Depends on: - Purpose: STUN server Name: textops Use: regular Maturity: stable Depends on: - Purpose: Message Textual Operations Name: tlsops Use: experimental Maturity: alpha Depends on: - Purpose: Helper TLS information and control Name: tm Use: regular Maturity: stable Depends on: - Purpose: Transaction Management Name: uac Use: regular Maturity: stable Depends on: tm, rr Purpose: User Agent Client functionalities Name: uac_auth Use: regular Maturity: stable Depends on: - Purpose: User Agent Client authentification functionalities Name: uac_redirect Use: regular Maturity: stable Depends on: tm, acc Purpose: Redirect reply processing on server Name: uac_registrant Use: regular Maturity: alpha Depends on: uac_auth Purpose: Registers OpenSIPS to a remote SIP registrar Name: uri Use: regular Maturity: stable Depends on: database Purpose: Various URI checks, including DB and AAA Name: userblacklist Use: experimental Maturity: beta Depends on: database Purpose: Blacklists on a per user basis Name: usrloc Use: regular Maturity: stable Depends on: database Purpose: User location support Name: xcap_client Use: regular Maturity: beta Depends on: - Purpose: XCAP client for fetching XCAP elements/docs via HTTP GET. Name: xmpp Use: experimental Maturity: beta Depends on: tm Purpose: Gateway between OpenSIPS and a jabber server (SIP2XMPP) opensips-2.2.2/README.md000066400000000000000000000042161300170765700146450ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/OpenSIPS/opensips.svg?branch=master)](https://travis-ci.org/OpenSIPS/opensips) # Welcome to OpenSIPS Project ## About OpenSIPS is a GPL licensed SIP server implementation. It started as a fork of Fokus Fraunhofer SIP Express Router (SER) project. OpenSIPS wants to be a more open project, not only from license point of view, but more open as project management, especially for external contributions. OpenSIPS wants to overcome the development latency of current SER project, to ensure a shorter path into a release for new added features. OpenSIPS is a project maintained by OpenSIPS Solutions by a team including core and main developers of SER project. ## Info For information regarding the OpenSIPS installation, please see the [INSTALL](INSTALL) file. For current developers/contributors of this project, see the [CREDITS](CREDITS) and [AUTHORS](AUTHORS) file. For complete license information, please see the [COPYING](COPYING) file. For a quick overview of OpenSIPS modules, please see the [README-MODULES](README-MODULES) file. ## Docs Documentation about each module can be found in the [README]() file in each module directory. For online documentation, please see For additional documentation, tutorials and examples please see also ## Questions For any question related to the OpenSIPS usage, please use the public mailing list. For questions regarding the development of OpenSIPS - like contributions, bug reports, etc - please use the public mailing list. For questions regarding businesses around OpenSIPS - like products,· consultancy, trainings, etc - please use the public mailing list. Also there is a generic news mailing list where you can learn about what is· new or important for the OpenSIPS project, about alerts and updates regarding relaces and about events around the project. opensips-2.2.2/aaa/000077500000000000000000000000001300170765700141055ustar00rootroot00000000000000opensips-2.2.2/aaa/aaa.c000066400000000000000000000047631300170765700150050ustar00rootroot00000000000000/* * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * -------- * 2009-07-10 First version (Irina Stanescu) */ #include #include "aaa.h" #include "../ut.h" int aaa_parse_url(str* aaa_url, aaa_prot_config* aaa_config) { char* p; int len; if (!aaa_url || !aaa_config) { LM_ERR("null arguments\n"); return -1; } p = q_memchr(aaa_url->s, ':', aaa_url->len); if (!p) { LM_ERR("invalid aaa url\n"); return -1; } len = p - aaa_url->s; aaa_config->prot_name = (str*) pkg_malloc (sizeof(str)); if (!aaa_config->prot_name) { LM_ERR("no pkg memory left\n"); return -1; } aaa_config->prot_name->s = (char*) pkg_malloc (len * sizeof(char)); if (!aaa_config->prot_name->s) { LM_ERR("no pkg memory left\n"); return -1; } aaa_config->prot_name->len = len; aaa_config->rest = p + 1; strncpy(aaa_config->prot_name->s, aaa_url->s, len); return 0; } int aaa_prot_bind(str* aaa_url, aaa_prot* prot) { aaa_prot_config pc; char *module_name; aaa_bind_api_f bind_f; if (!aaa_url || !prot) { LM_ERR("null argument\n"); return -1; } if (aaa_parse_url(aaa_url, &pc)) { LM_ERR("parse url error\n"); return -1; } module_name = (char*) pkg_malloc(pc.prot_name->len + 4 + 1); if (!module_name) { LM_ERR("no pkg memory left\n"); return -1; } sprintf(module_name, "aaa_%.*s", pc.prot_name->len,pc.prot_name->s); bind_f = (aaa_bind_api_f) find_mod_export(module_name, "aaa_bind_api", 0, 0); if (bind_f) { LM_DBG("using aaa bind api for %s\n", module_name); if (bind_f(prot)) { pkg_free(module_name); return -1; } } else { LM_ERR("<%s> has no bind api function\n", module_name); pkg_free(module_name); return -1; } pkg_free(module_name); return 0; } opensips-2.2.2/aaa/aaa.h000066400000000000000000000172361300170765700150110ustar00rootroot00000000000000/* * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA * * History * -------- * 2009-07-20 First version (Irina Stanescu) */ /* * Generic AAA Interface * * This is a generic interface for modules that need to use AAA protocols. * The interface is independent of the underlying AAA protocol that implements * it and it should be used by all modules that use AAA features. * For other information, check out the documentation. */ #ifndef _AAA_H_ #define _AAA_H_ #include #include "../dprint.h" #include "../mem/mem.h" #include "../str.h" #include "../sr_module.h" #include "aaa_avp.h" #define AAA_DICT_FIND_VAL 1 #define AAA_DICT_FIND_ATTR 2 #define AAA_DICT_FIND_VEND 3 #define AAA_AUTH 4 #define AAA_ACCT 5 #define AAA_RECV 6 #define AAA_GET_FROM_START 7 #define AAA_GET_FROM_CURRENT 8 /* Generic structure for an AVP */ typedef struct _aaa_map { char *name; int value; int type; } aaa_map; /* Generic structure for a message sent or received by the AAA protocol. avpair - the list of AVPs contained by the message last_found - a pointer in the list of AVPs used to store the last element found by a search, this is needed by the find function in case a AAA_GET_FROM_CURRENT type of search is wanted type - the type of message (AAA_AUTH or AAA_ACCT) */ typedef struct _aaa_message { void* avpair; void* last_found; int type; } aaa_message; /* Generic AAA connection This is a type definition for a generic AAA connection. The implementation for a connection variable is protocol dependent. */ typedef void aaa_conn; /* Creates a generic AAA message This function creates a structure for a message. The function takes two parameters: - the address of a AAA connection variable - a flag representing the type of message (for authentication or accounting) The return value is a pointer to the AAA message structure. */ typedef aaa_message* (create_message_f)(aaa_conn*, int); /* Destroys an AAA message This function destroys the AVP list contained by the message, and then releases the memory allocated for the message. The return value is an error code. */ typedef int (destroy_message_f)(aaa_conn*, aaa_message*); /* Sends an AAA message This function sends a message on a specified connection. The function takes three parameters: - a pointer to the connection variable - the address of a message to be sent - pointer to the address of a message to be received (may be NULL) The return value is an error code. */ typedef int (send_request_f)(aaa_conn*, aaa_message*, aaa_message**); /* Search in dictionary This function searches a certain value for a name in the dictionary of AVPs loaded at protcol intialization. The result is returned in the value field of the aaa_map structure. The third parameter represents the type of search wanted: for a value, for an attribute or for a vendor dictionary entry. The return value is an error code. */ typedef int (find_f)(aaa_conn*, aaa_map*, int); /* Add AVP to a message This function adds a AVP to a specified AAA message. The first two parameters are the connection handle and the message. The last three parameters have the following meaning: - a pointer to the value to be added - the value length - the vendorpec Depending on the implementation, some of these values may be empty. The return value is an error code. */ typedef int (avp_add_f)(aaa_conn*, aaa_message*, aaa_map*, void*, int, int); /* Get AVP from a message This function gets a AVP from a specified AAA message. The first two parameters are the connection handle and the message. The last three parameters have the following meaning: - a pointer to the location where the value should be placed - a pointer to the location where the value length should be placed - a flag specifying the type of search in the AVPs list (from the start or from the current position) The return value is an error code. */ typedef int (avp_get_f)(aaa_conn*, aaa_message*, aaa_map*, void**, int*, int); /* Initialize AAA protocol implementation This function initializes the protocol and returns a pointer to the connection variable that represents it. The return value is a pointer to a connection variable. */ typedef aaa_conn* (init_prot_f)(str*); /* AAA API module callbacks This structure is a collection of callbacks provided by the modules that implement this generic AAA interface. A variable of this type will be filled when a bind call is made, and therefore it cannot be used before aaa_prot_bind. */ typedef struct _aaa_prot { init_prot_f* init_prot; /*initializes a protocol implementation*/ create_message_f* create_aaa_message; /*creates a request*/ destroy_message_f* destroy_aaa_message; /*destroys a message*/ send_request_f* send_aaa_request; /*sends a request*/ find_f* dictionary_find; /*searches a name in a dictionary*/ avp_add_f* avp_add; /*adds a AVP to a message*/ avp_get_f* avp_get; /*gets the value of a AVP in a message*/ } aaa_prot; /* Bind AAA module functions This is the function called by a module that wishes to use an implementation for an AAA protocol. The first parameter is the protocol URL. The second parameter represents the address where the structure for the protocol callback functions should be stored. The return value is an error code. */ int aaa_prot_bind(str*, aaa_prot*); /* Type definition for a bind function. */ typedef int (*aaa_bind_api_f)(aaa_prot*); /* Protocol configuration structure The configuration structure for an AAA protocol. It contains - the protocol name extracted from the URL - a pointer to the location of what is left of the URL string This information can be used by the module that implements the interface as it pleases. */ typedef struct _aaa_prot_config { str *prot_name; void *rest; } aaa_prot_config; /* AAA URL parser This function parses a string representing the URL given through the configuration file and returns a configuration structure for the protocol implementation. An example for a URL for anAAA Radius Module is: "radius:/etc/radiusclient-ng/radiusclient.conf" */ int aaa_parse_url(str*, aaa_prot_config*); /* Dictionary initialization macro This macro initializes an array of AVPs with the corresponding information from the protocol dictionary. */ #define INIT_AV(ap, rh, at, nr_at, vl, nr_vl, fn, e1, e2) \ { \ int i; \ for (i = 0; i < nr_at; i++) { \ if (at[i].name == NULL) \ continue; \ if (ap.dictionary_find(rh, &at[i], AAA_DICT_FIND_ATTR) < 0) { \ LM_ERR("%s: can't get code for the " \ "%s attribute\n", fn, at[i].name); \ return e1; \ } \ } \ for (i = 0; i < nr_vl; i++) { \ if (vl[i].name == NULL) \ continue; \ if (ap.dictionary_find(rh, &vl[i], AAA_DICT_FIND_VAL) < 0) { \ LM_ERR("%s: can't get code for the " \ "%s attribute value\n", fn, vl[i].name);\ return e2; \ } \ } \ } #endif opensips-2.2.2/aaa/aaa_avp.h000066400000000000000000000044021300170765700156460ustar00rootroot00000000000000/* * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * WARNING: Don't forget to update the dictionary if you update this file !!! * */ #ifndef _AAA_AVP_H #define _AAA_AVP_H #define A_USER_NAME 0 #define A_SERVICE_TYPE 1 #define A_CALLED_STATION_ID 2 #define A_CALLING_STATION_ID 3 #define A_ACCT_STATUS_TYPE 4 #define A_ACCT_SESSION_ID 5 #define A_SIP_METHOD 6 #define A_SIP_RESPONSE_CODE 7 #define A_SIP_CSEQ 8 #define A_SIP_TO_TAG 9 #define A_SIP_FROM_TAG 10 #define A_DIGEST_RESPONSE 11 #define A_DIGEST_ATTRIBUTES 12 #define A_SIP_URI_USER 13 #define A_SIP_URI_HOST 14 #define A_DIGEST_REALM 15 #define A_DIGEST_NONCE 16 #define A_DIGEST_METHOD 17 #define A_DIGEST_URI 18 #define A_DIGEST_QOP 19 #define A_DIGEST_ALGORITHM 20 #define A_DIGEST_BODY_DIGEST 21 #define A_DIGEST_CNONCE 22 #define A_DIGEST_NONCE_COUNT 23 #define A_DIGEST_USER_NAME 24 #define A_SIP_GROUP 25 #define A_CISCO_AVPAIR 26 #define A_SIP_AVP 27 #define A_TIME_STAMP 28 #define A_SIP_CALL_ID 29 #define A_SIP_REQUEST_HASH 30 #define A_MAX 31 #define V_STATUS_START 0 #define V_STATUS_STOP 1 #define V_STATUS_FAILED 2 #define V_CALL_CHECK 3 #define V_SIP_SESSION 4 #define V_GROUP_CHECK 5 #define V_SIP_CALLER_AVPS 6 #define V_SIP_CALLEE_AVPS 7 #define V_SIP_VERIFY_DESTINATION 8 #define V_SIP_VERIFY_SOURCE 9 #define V_MAX 10 #endif opensips-2.2.2/aaa/doc/000077500000000000000000000000001300170765700146525ustar00rootroot00000000000000opensips-2.2.2/aaa/doc/aaa-api.txt000066400000000000000000000231561300170765700167130ustar00rootroot00000000000000# $Id: $ # Generic AAA Protocol Interface ------------------------------- Overview This is a generic interface for modules that want to use features provided by AAA protocols. The interface is independent of the underlying AAA protocol that implements it and it should be used by all modules that use AAA. The API architecture is based on the assumption that every AAA protocol uses messages based on AVPs, since both Radius and Diameter, two of the current AAA implementations, do that. 1 Data types There are several new data types used by the API. All of them are defined in the header file aaa.h, a client must include the header file to be able to use them. 1.1 Type aaa_map 1.1.1 Description This type represents a generic structure for an AVP. 1.1.2 Definition typedef struct _aaa_map { char *name; int value; } aaa_map; 1.1.3 Macros There are no macros for this type. 1.2 Type aaa_message 1.2.1 Description This type is a generic structure for a message sent or received by the AAA protocol. The fields of the structure have the following meaning: avpair : the list of AVPs contained by the message last_found : a pointer in the list of AVPs used to store the last element found by a search, this is needed by the find function in case a AAA_GET_FROM_CURRENT type of search is wanted type : the type of message (AAA_AUTH or AAA_ACCT) 1.2.2 Definition typedef struct _aaa_message { void* avpair; void* last_found; int type; } aaa_message; 1.2.3 Macros For this type there are three macro's defined representing the allowed types of messages: AAA_AUTH : the message will be for authentication AAA_ACCT : the message will be for accounting AAA_RECV : the message will contain received information 1.3 Type aaa_conn 1.3.1 Description This is a type definition for a generic AAA connection. The implementation for a connection variable is protocol dependent. 1.3.2 Definition typedef void aaa_conn; 1.3.3 Macros There are no macros for this type. 1.4 Type aaa_prot 1.4.1 Description This structure is a collection of callbacks provided by the modules that implement this generic AAA interface. A variable of this type will be filled when a bind call is made, and therefore it cannot be used before aaa_prot_bind. 1.4.2 Definition typedef struct _aaa_prot { init_prot_f* init_prot; /*initializes a protocol implementation*/ create_request_f* create_aaa_request; /*creates a request*/ destroy_message_f* destroy_aaa_message; /*destroys a message*/ send_request_f* send_aaa_request; /*sends a request*/ find_f* dictionary_find; /*searches a name in a dictionary*/ avp_add_f* avp_add; /*adds a AVP to a message*/ avp_get_f* avp_get /*gets the value of a AVP in a message*/ } aaa_prot; 1.4.3 Macros There are no macros for this type. 1.5 Type aaa_prot_config 1.5.1 Description Thsi type represents the configuration structure for an AAA protocol. The structure fields have the following meaning: prot_name : the protocol name extracted from the URL rest : a pointer to the location of what is left of the URL string The "rest" field can be used by the module that implements the AAA interface as it pleases. 1.5.2 Definition typedef struct _aaa_prot_config { str *prot_name; void *rest; } aaa_prot_config; 1.6 Type aaa_bind_api_f 1.6.1 Description 1.6.2 Definition typedef int (*aaa_bind_api_f)(aaa_prot*); 2 Functions 2.1 Callback prot.create_aaa_request 2.1.1 Description This function creates a structure for a message. The message can be a request, or a reply. 2.1.2 Prototype typedef aaa_message* (create_request_f)(aaa_conn* ac, int f); 2.1.3 Parameters The function takes two parameters: ac: pointer to an AAA connection variable f: a flag representing the type of message (for authentication or accounting) 2.1.4 Return Value The return value is a pointer to the AAA message structure, or NULL if error occurred. 2.2 Callback prot.destroy_aaa_message 2.2.1 Description This function destroys the AVP list contained by the message, and then releases the memory allocated for the message. The message can be a request or reply. 2.2.2 Prototype typedef int (destroy_message_f)(aaa_conn* ac, aaa_message* mess); 2.2.3 Parameters The function takes two parameters: ac : pointer to an AAA connection variable mess : pointer to an AAA message variable 2.2.4 Return Value The return value is an error code: 0 for succes, -1 for error. 2.3 Callback prot.send_aaa_request 2.3.1 Description This function sends a message on a specified connection. 2.3.2 Prototype typedef int (send_request_f)(aaa_conn* ac, aaa_message* send, aaa_message* recv); 2.3.3 Parameters The function takes three parameters: ac : a pointer to an AAA connection variable send : the address of a message to be sent recv : the address of a message to be received (may be NULL) 2.3.4 Return Value The return value is an error code: 0 for succes, -1 for error. 2.4 Callback prot.dictionary_find 2.4.1 Description This function searches a certain value for a name in the dictionary of AVPs loaded at protcol intialization. The result is returned in the value field of the aaa_map structure. 2.4.2 Prototype typedef int (find_f)(aaa_conn* ac, aaa_map* map, int flag); 2.4.3 Parameters The function takes 3 parameters: ac : a pointer to an AAA connection variable map : a pointer to the aaa_map structure wich contains the name searched flag : a flag representing the type of search wanted: for a value, for an attribute or for a vendor dictionary entry. The values allowed for the flag are the given by the following definitions: AAA_DICT_FIND_VAL : a value is searched AAA_DICT_FIND_ATTR : an attribute is searched AAA_DICT_FIND_VEND : a vendor entry is searched 2.4.4 Return Value The return value is an error code: 0 if the name was found, 1 if the name was not found, -1 for error. 2.5 Callback prot.avp_add 2.5.1 Description This function adds a AVP to a specified AAA message. 2.5.2 Prototype typedef int (avp_add_f)(aaa_conn* ac, aaa_message* mess, aaa_map* map, void* value, int length, int vendor); 2.5.3 Parameters ac : a pointer to an AAA connection variable mess : a pointer to the message where the AVP will be added map : a pointer to a map structure wich contains the name of the AVP to be added value : a pointer to the value to be added length : the length of the value added vendor : the vendorpec Depending on the implementation, some of these values may be empty. 2.5.4 Return Value The return value is an error code: 0 for succes, -1 for error. 2.6 Callback prot.avp_get 2.6.1 Description This function gets a AVP from a specified AAA message. 2.6.2 Prototype typedef int (avp_get_f)(aaa_conn* ac, aaa_message* mess, aaa_map* map, void** value, int* length, int flag); 2.6.3 Parameters ac : a pointer to an AAA connection variable mess : a pointer to the message where the AVP will be added map : a pointer to a map structure wich contains the name searched value : a pointer to the location where the value should be placed length : a pointer to the location where the value length should be placed flag : a flag specifying the type of search in the AVPs list (from the start or from the current position) There are two possible values for the flag: AAA_GET_FROM_START : the search should start from the beginning of the AVPs list AAA_GET_FROM_CURRENT : the search should start from the current search position 2.6.4 Return Value The return value is an error code: 0 for succes, -1 for error. 2.7 Callback prot.init_prot 2.7.1 Description This function initializes the protocol and returns a pointer to the connection variable that represents it. 2.7.2 Prototype typedef aaa_conn* (init_prot_f)(str* url); 2.7.3 Parameters The function receives only one parameter: url : a string that contains protocol dependent information needed to identify the module that implements the interface and other useful information 2.7.4 Return Value The return value is a pointer to a connection variable, or NULL if error occurred. 2.8 Function aaa_prot_bind 2.8.1 Description This is the function called by a module that wishes to use animplementation for an AAA protocol. 2.8.2 Prototype int aaa_prot_bind(str* url, aaa_prot* ap); 2.8.3 Parameters url : the protocol URL. ap : where the structure for the protocol callback functions should be stored. 2.8.4 Return Value The return value is an error code: 0 for succes, -1 for error. 2.9 Function aaa_parse_url 2.9.1 Description This function parses a string representing the URL given through the configuration file and returns a configuration structure for the protocol implementation. An example for a URL for an AAA Radius Module is: "radius:/etc/radiusclient-ng/radiusclient.conf" 2.9.2 Prototype int aaa_parse_url(str* url, aaa_prot_config* protcfg); 2.9.3 Parameters url : the string for the url protcfg : pointer to the configuration structure resulted 2.9.4 Return Value The return value is an error code: 0 for succes, -1 for error. 3 Other useful information 3.1 Useful Macros 3.1.1 INIT_AV Macro 3.1.1.1 Description This macro initializes two arrays of AVPs with the corresponding information from the protocol dictionary. The first array needs to be filled with the corresponding information for some given attributes, and the second array, for some given values. 3.1.1.2 Prototype #define INIT_AV(ap, rh, at, nr_at, vl, nr_vl, fn, e1, e2) 3.1.1.2 Parameters ap : the aaa protocol rh : the aaa connection handle at : array of attributes nr_at : number of attributes vl : array of values nr_vl : number of values fn : the name of the function that calls this macro e1 : error code 1 e2 : error code 2 opensips-2.2.2/action.c000066400000000000000000001554221300170765700150150ustar00rootroot00000000000000/* * Copyright (C) 2010-2014 OpenSIPS Solutions * Copyright (C) 2005-2006 Voice Sistem S.R.L. * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-29 removed scratchpad (jiri) * 2003-03-19 fixed set* len calculation bug & simplified a little the code * (should be a little faster now) (andrei) * replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-01 Added support for loose routing in forward (janakj) * 2003-04-12 FORCE_RPORT_T added (andrei) * 2003-04-22 strip_tail added (jiri) * 2003-10-02 added SET_ADV_ADDR_T & SET_ADV_PORT_T (andrei) * 2003-10-29 added FORCE_TCP_ALIAS_T (andrei) * 2004-11-30 added FORCE_SEND_SOCKET_T (andrei) * 2005-11-29 added serialize_branches and next_branches (bogdan) * 2006-03-02 MODULE_T action points to a cmd_export_t struct instead to * a function address - more info is accessible (bogdan) * 2006-05-22 forward(_udp,_tcp,_tls) and send(_tcp) merged in forward() and * send() (bogdan) * 2006-12-22 functions for script and branch flags added (bogdan) */ /*! * \file * \brief OpenSIPS Generic functions */ #include "action.h" #include "config.h" #include "error.h" #include "dprint.h" #include "proxy.h" #include "forward.h" #include "route.h" #include "parser/msg_parser.h" #include "parser/parse_uri.h" #include "ut.h" #include "sr_module.h" #include "mem/mem.h" #include "globals.h" #include "dset.h" #include "flags.h" #include "errinfo.h" #include "serialize.h" #include "blacklists.h" #include "cachedb/cachedb.h" #include "msg_translator.h" #include "mod_fix.h" /* needed by tcpconn_add_alias() */ #include "net/tcp_conn_defs.h" #include "script_var.h" #include "xlog.h" #include "evi/evi_modules.h" #include #include #include #include #include #include #include #ifdef DEBUG_DMALLOC #include #endif int action_flags = 0; int return_code = 0; int max_while_loops = 100; /* script tracing options */ int use_script_trace = 0; int script_trace_log_level = L_ALERT; char *script_trace_info = NULL; pv_elem_t script_trace_elem; static int rec_lev=0; extern err_info_t _oser_err_info; action_time longest_action[LONGEST_ACTION_SIZE]; int min_action_time=0; action_elem_p route_params[MAX_REC_LEV]; int route_params_number[MAX_REC_LEV]; int route_rec_level = -1; int curr_action_line; char *curr_action_file; static int for_each_handler(struct sip_msg *msg, struct action *a); /* run actions from a route */ /* returns: 0, or 1 on success, <0 on error */ /* (0 if drop or break encountered, 1 if not ) */ static inline int run_actions(struct action* a, struct sip_msg* msg) { int ret; rec_lev++; if (rec_lev>ROUTE_MAX_REC_LEV){ LM_ERR("too many recursive routing table lookups (%d) giving up!\n", rec_lev); ret=E_UNSPEC; goto error; } if (a==0){ LM_WARN("null action list (rec_level=%d)\n", rec_lev); ret=1; goto error; } ret=run_action_list(a, msg); /* if 'return', reset the flag */ if(action_flags&ACT_FL_RETURN) action_flags &= ~ACT_FL_RETURN; rec_lev--; return ret; error: rec_lev--; return ret; } /* run the error route with correct handling - simpler wrapper to allow the usage from other parts of the code */ void run_error_route(struct sip_msg* msg, int force_reset) { int old_route; LM_DBG("triggering\n"); swap_route_type(old_route, ERROR_ROUTE); run_actions(error_rlist.a, msg); /* reset error info */ init_err_info(); set_route_type(old_route); } /* run a list of actions */ int run_action_list(struct action* a, struct sip_msg* msg) { int ret=E_UNSPEC; struct action* t; for (t=a; t!=0; t=t->next){ ret=do_action(t, msg); /* if action returns 0, then stop processing the script */ if(ret==0) action_flags |= ACT_FL_EXIT; /* check for errors */ if (_oser_err_info.eclass!=0 && error_rlist.a!=NULL && (route_type&(ERROR_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE))==0 ) run_error_route(msg,0); /* continue or not ? */ if( action_flags&(ACT_FL_RETURN|ACT_FL_EXIT) ) break; } return ret; } int run_top_route(struct action* a, struct sip_msg* msg) { int bk_action_flags; int bk_rec_lev; int ret; bk_action_flags = action_flags; bk_rec_lev = rec_lev; action_flags = 0; rec_lev = 0; init_err_info(); run_actions(a, msg); ret = action_flags; action_flags = bk_action_flags; rec_lev = bk_rec_lev; /* reset script tracing */ use_script_trace = 0; return ret; } /* execute assignment operation */ int do_assign(struct sip_msg* msg, struct action* a) { int ret; pv_value_t val; pv_spec_p dspec; ret = -1; dspec = (pv_spec_p)a->elem[0].u.data; if(!pv_is_w(dspec)) { LM_ERR("read only PV in left expression\n"); goto error; } memset(&val, 0, sizeof(pv_value_t)); if(a->elem[1].type != NULLV_ST) { ret = eval_expr((struct expr*)a->elem[1].u.data, msg, &val); if(ret < 0 || !(val.flags & (PV_VAL_STR | PV_VAL_INT | PV_VAL_NULL))) { LM_WARN("no value in right expression at %s:%d\n", a->file, a->line); goto error2; } } switch ((unsigned char)a->type){ case EQ_T: case COLONEQ_T: case PLUSEQ_T: case MINUSEQ_T: case DIVEQ_T: case MULTEQ_T: case MODULOEQ_T: case BANDEQ_T: case BOREQ_T: case BXOREQ_T: script_trace("assign", (unsigned char)a->type == EQ_T ? "equal" : (unsigned char)a->type == COLONEQ_T ? "colon-eq" : (unsigned char)a->type == PLUSEQ_T ? "plus-eq" : (unsigned char)a->type == MINUSEQ_T ? "minus-eq" : (unsigned char)a->type == DIVEQ_T ? "div-eq" : (unsigned char)a->type == MULTEQ_T ? "mult-eq" : (unsigned char)a->type == MODULOEQ_T? "modulo-eq" : (unsigned char)a->type == BANDEQ_T ? "b-and-eq" : (unsigned char)a->type == BOREQ_T ? "b-or-eq":"b-xor-eq", msg, a->file, a->line); if(a->elem[1].type == NULLV_ST || (val.flags & PV_VAL_NULL)) { if(pv_set_value(msg, dspec, (int)a->type, 0)<0) { LM_ERR("setting PV failed\n"); goto error; } } else { if(pv_set_value(msg, dspec, (int)a->type, &val)<0) { LM_ERR("setting PV failed\n"); goto error; } } ret = 1; break; default: LM_ALERT("BUG -> unknown op type %d\n", a->type); goto error; } pv_value_destroy(&val); return ret; error: LM_ERR("error at %s:%d\n", a->file, a->line); error2: pv_value_destroy(&val); return -1; } static int do_action_set_adv_address(struct sip_msg *msg, struct action *a) { str adv_addr; int ret = 1; /* continue processing */ if (a->elem[0].type != STR_ST) { report_programming_bug("set_advertised_address type %d", a->elem[0].type); ret = E_BUG; goto out; } if (pv_printf_s(msg, (pv_elem_t *)a->elem[0].u.data, &adv_addr) != 0 || adv_addr.len <= 0) { LM_WARN("cannot get string for value (%s:%d)\n",a->file,a->line); ret = E_BUG; goto out; } LM_DBG("setting adv address = [%.*s]\n", adv_addr.len, adv_addr.s); /* duplicate the advertised address into private memory */ if (adv_addr.len > msg->set_global_address.len) { msg->set_global_address.s = pkg_realloc(msg->set_global_address.s, adv_addr.len); if (!msg->set_global_address.s) { LM_ERR("out of pkg mem\n"); ret = E_OUT_OF_MEM; goto out; } } memcpy(msg->set_global_address.s, adv_addr.s, adv_addr.len); msg->set_global_address.len = adv_addr.len; out: return ret; } static int do_action_set_adv_port(struct sip_msg *msg, struct action *a) { str adv_port; int ret = 1; if (a->elem[0].type != STR_ST) { report_programming_bug("set_advertised_port type %d", a->elem[0].type); ret = E_BUG; goto out; } if (pv_printf_s(msg, (pv_elem_t *)a->elem[0].u.data, &adv_port) != 0 || adv_port.len <= 0) { LM_WARN("cannot get string for value (%s:%d)\n", a->file,a->line); ret = E_BUG; goto out; } LM_DBG("setting adv port '%.*s'\n", adv_port.len, adv_port.s); /* duplicate the advertised port into private memory */ if (adv_port.len > msg->set_global_port.len) { msg->set_global_port.s = pkg_realloc(msg->set_global_port.s, adv_port.len); if (!msg->set_global_port.s) { LM_ERR("out of pkg mem\n"); ret = E_OUT_OF_MEM; goto out; } } memcpy(msg->set_global_port.s, adv_port.s, adv_port.len); msg->set_global_port.len = adv_port.len; out: return ret; } #define should_skip_updating(action_type) \ (action_type == IF_T || action_type == ROUTE_T || \ action_type == WHILE_T || action_type == FOR_EACH_T) #define update_longest_action(a) do { \ if (execmsgthreshold && !should_skip_updating((unsigned char)(a)->type)) { \ end_time = get_time_diff(&start); \ if (end_time > min_action_time) { \ for (i=0;i end of list(e.g DROP), > 0 to continue processing next actions and <0 on error */ int do_action(struct action* a, struct sip_msg* msg) { int ret; int v; int sec,usec; union sockaddr_union* to; struct proxy_l* p; char* tmp; char *new_uri, *end, *crt; int len,i; int user = 0; int expires = 0; str vals[5]; str result; struct sip_uri uri, next_hop; struct sip_uri *u; unsigned short port; int cmatch; struct action *aitem; struct action *adefault; pv_spec_t *spec; pv_elem_p model; pv_value_t val; pv_elem_t *pve; str name_s; struct timeval start; int end_time; int aux_counter; /* reset the value of error to E_UNSPEC so avoid unknowledgable functions to return with error (status<0) and not setting it leaving there previous error; cache the previous value though for functions which want to process it */ prev_ser_error=ser_error; ser_error=E_UNSPEC; start_expire_timer(start,execmsgthreshold); curr_action_line = a->line; curr_action_file = a->file; ret=E_BUG; switch ((unsigned char)a->type){ case ASSERT_T: if (enable_asserts) { /* if null expr => ignore if? */ if ((a->elem[0].type==EXPR_ST)&&a->elem[0].u.data){ v=eval_expr((struct expr*)a->elem[0].u.data, msg, 0); ret=1; /*default is continue */ if (v<=0) { ret=0; LM_CRIT("ASSERTION FAILED - %s\n", a->elem[1].u.string); if (abort_on_assert) { abort(); } else { set_err_info(OSER_EC_ASSERT, OSER_EL_CRITIC, "assertion failed"); set_err_reply(500, "server error"); run_error_route(msg,0); } } } } break; case DROP_T: script_trace("core", "drop", msg, a->file, a->line) ; action_flags |= ACT_FL_DROP|ACT_FL_EXIT; break; case EXIT_T: script_trace("core", "exit", msg, a->file, a->line) ; ret=0; action_flags |= ACT_FL_EXIT; break; case RETURN_T: script_trace("core", "return", msg, a->file, a->line) ; if (a->elem[0].type == SCRIPTVAR_ST) { spec = (pv_spec_t*)a->elem[0].u.data; if(pv_get_spec_value(msg, spec, &val)!=0 || (val.flags&PV_VAL_NULL)) { ret=-1; } else { if(!(val.flags&PV_VAL_INT)) ret = 1; else ret = val.ri; } pv_value_destroy(&val); } else { ret=a->elem[0].u.number; } action_flags |= ACT_FL_RETURN; break; case FORWARD_T: script_trace("core", "forward", msg, a->file, a->line) ; if (a->elem[0].type==NOSUBTYPE){ /* parse uri and build a proxy */ if (msg->dst_uri.len) { ret = parse_uri(msg->dst_uri.s, msg->dst_uri.len, &next_hop); u = &next_hop; } else { ret = parse_sip_msg_uri(msg); u = &msg->parsed_uri; } if (ret<0) { LM_ERR("forward: bad_uri dropping packet\n"); break; } /* create a temporary proxy*/ p=mk_proxy(u->maddr_val.len?&u->maddr_val:&u->host, u->port_no, u->proto, (u->type==SIPS_URI_T)?1:0 ); if (p==0){ LM_ERR("bad host name in uri, dropping packet\n"); ret=E_BAD_ADDRESS; goto error_fwd_uri; } ret=forward_request(msg, p); free_proxy(p); /* frees only p content, not p itself */ pkg_free(p); if (ret==0) ret=1; }else if (a->elem[0].type==PROXY_ST) { if (0==(p=clone_proxy((struct proxy_l*)a->elem[0].u.data))) { LM_ERR("failed to clone proxy, dropping packet\n"); ret=E_OUT_OF_MEM; goto error_fwd_uri; } ret=forward_request(msg, p); free_proxy(p); /* frees only p content, not p itself */ pkg_free(p); if (ret==0) ret=1; }else{ LM_ALERT("BUG in forward() types %d, %d\n", a->elem[0].type, a->elem[1].type); ret=E_BUG; } break; case SEND_T: script_trace("core", "send", msg, a->file, a->line) ; if (a->elem[0].type!= PROXY_ST){ LM_ALERT("BUG in send() type %d\n", a->elem[0].type); ret=E_BUG; break; } if (a->elem[1].u.data) { if (a->elem[1].type != SCRIPTVAR_ELEM_ST){ LM_ALERT("BUG in send() header type %d\n",a->elem[1].type); ret=E_BUG; break; } else { pve = (pv_elem_t *)a->elem[1].u.data; } } else { pve = NULL; } to=(union sockaddr_union*) pkg_malloc(sizeof(union sockaddr_union)); if (to==0){ LM_ERR("memory allocation failure\n"); ret=E_OUT_OF_MEM; break; } if (0==(p=clone_proxy((struct proxy_l*)a->elem[0].u.data))) { LM_ERR("failed to clone proxy, dropping packet\n"); ret=E_OUT_OF_MEM; break; } ret=hostent2su(to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT ); if (ret==0){ if (pve) { if ( pv_printf_s(msg, pve, &name_s)!=0 || name_s.len == 0 || name_s.s == NULL) { LM_WARN("cannot get string for value (%s:%d)\n", a->file,a->line); ret=E_UNSPEC; break; } /* build new msg */ tmp = pkg_malloc(msg->len + name_s.len); if (!tmp) { LM_ERR("memory allocation failure\n"); ret = E_OUT_OF_MEM; break; } LM_DBG("searching for first line %d\n", msg->first_line.len); /* search first line of previous msg */ /* copy headers */ len = msg->first_line.len; memcpy(tmp, msg->buf, len); memcpy(tmp + len, name_s.s, name_s.len); memcpy(tmp + len + name_s.len, msg->buf + len, msg->len - len); ret = msg_send(0/*send_sock*/, p->proto, to, 0/*id*/, tmp, msg->len + name_s.len, msg); pkg_free(tmp); } else { ret = msg_send(0/*send_sock*/, p->proto, to, 0/*id*/, msg->buf, msg->len, msg); } if (ret!=0 && p->host.h_addr_list[p->addr_idx+1]) p->addr_idx++; } free_proxy(p); /* frees only p content, not p itself */ pkg_free(p); pkg_free(to); if (ret==0) ret=1; break; case LOG_T: script_trace("core", "log", msg, a->file, a->line) ; if ((a->elem[0].type!=NUMBER_ST)|(a->elem[1].type!=STRING_ST)){ LM_ALERT("BUG in log() types %d, %d\n", a->elem[0].type, a->elem[1].type); ret=E_BUG; break; } LM_GEN1(a->elem[0].u.number, "%s", a->elem[1].u.string); ret=1; break; case APPEND_BRANCH_T: script_trace("core", "append_branch", msg, a->file, a->line) ; if ((a->elem[0].type!=STR_ST)) { LM_ALERT("BUG in append_branch %d\n", a->elem[0].type ); ret=E_BUG; break; } if (a->elem[0].u.s.s==NULL) { ret = append_branch(msg, 0, &msg->dst_uri, &msg->path_vec, get_ruri_q(msg), getb0flags(msg), msg->force_send_socket); /* reset all branch info */ msg->force_send_socket = 0; setb0flags(msg,0); set_ruri_q(msg,Q_UNSPECIFIED); if(msg->dst_uri.s!=0) pkg_free(msg->dst_uri.s); msg->dst_uri.s = 0; msg->dst_uri.len = 0; if(msg->path_vec.s!=0) pkg_free(msg->path_vec.s); msg->path_vec.s = 0; msg->path_vec.len = 0; } else { ret = append_branch(msg, &a->elem[0].u.s, &msg->dst_uri, &msg->path_vec, a->elem[1].u.number, getb0flags(msg), msg->force_send_socket); } break; case REMOVE_BRANCH_T: script_trace("core", "remove_branch", msg, a->file, a->line) ; if (a->elem[0].type == SCRIPTVAR_ST) { spec = (pv_spec_t*)a->elem[0].u.data; if( pv_get_spec_value(msg, spec, &val)!=0 || (val.flags&PV_VAL_NULL) || !(val.flags&PV_VAL_INT) ) { ret=-1; break; } i = val.ri; } else { i=a->elem[0].u.number; } ret = (remove_branch((unsigned int)i)==0)?1:-1; break; case LEN_GT_T: script_trace("core", "len_gt", msg, a->file, a->line) ; if (a->elem[0].type!=NUMBER_ST) { LM_ALERT("BUG in len_gt type %d\n", a->elem[0].type ); ret=E_BUG; break; } ret = (msg->len >= (unsigned int)a->elem[0].u.number) ? 1 : -1; break; case SETFLAG_T: script_trace("core", "setflag", msg, a->file, a->line) ; ret = setflag( msg, a->elem[0].u.number ); break; case RESETFLAG_T: script_trace("core", "resetflag", msg, a->file, a->line) ; ret = resetflag( msg, a->elem[0].u.number ); break; case ISFLAGSET_T: script_trace("core", "isflagset", msg, a->file, a->line) ; ret = isflagset( msg, a->elem[0].u.number ); break; case SETBFLAG_T: script_trace("core", "setbflag", msg, a->file, a->line) ; ret = setbflag( msg, a->elem[0].u.number, a->elem[1].u.number ); break; case RESETBFLAG_T: script_trace("core", "resetbflag", msg, a->file, a->line) ; ret = resetbflag( msg, a->elem[0].u.number, a->elem[1].u.number ); break; case ISBFLAGSET_T: script_trace("core", "isbflagset", msg, a->file, a->line) ; ret = isbflagset( msg, a->elem[0].u.number, a->elem[1].u.number ); break; case ERROR_T: script_trace("core", "error", msg, a->file, a->line) ; if ((a->elem[0].type!=STRING_ST)|(a->elem[1].type!=STRING_ST)){ LM_ALERT("BUG in error() types %d, %d\n", a->elem[0].type, a->elem[1].type); ret=E_BUG; break; } LM_ERR("error(\"%s\", \"%s\") not implemented yet\n", a->elem[0].u.string, a->elem[1].u.string); ret=1; break; case ROUTE_T: script_trace("route", rlist[a->elem[0].u.number].name, msg, a->file, a->line) ; if (a->elem[0].type!=NUMBER_ST){ LM_ALERT("BUG in route() type %d\n", a->elem[0].type); ret=E_BUG; break; } if ((a->elem[0].u.number>RT_NO)||(a->elem[0].u.number<0)){ LM_ALERT("BUG - invalid routing table number in" "route(%lu)\n", a->elem[0].u.number); ret=E_CFG; break; } /* check if the route has parameters */ if (a->elem[1].type != 0) { if (a->elem[1].type != NUMBER_ST || a->elem[2].type != SCRIPTVAR_ELEM_ST) { LM_ALERT("BUG in route() type %d/%d\n", a->elem[1].type, a->elem[2].type); ret=E_BUG; break; } route_rec_level++; route_params[route_rec_level] = (action_elem_t *)a->elem[2].u.data; route_params_number[route_rec_level] = a->elem[1].u.number; return_code=run_actions(rlist[a->elem[0].u.number].a, msg); route_rec_level--; } else { route_rec_level++; route_params[route_rec_level] = NULL; route_params_number[route_rec_level] = 0; return_code=run_actions(rlist[a->elem[0].u.number].a, msg); route_rec_level--; } ret=return_code; break; case REVERT_URI_T: script_trace("core", "revert_uri", msg, a->file, a->line) ; if (msg->new_uri.s) { pkg_free(msg->new_uri.s); msg->new_uri.len=0; msg->new_uri.s=0; msg->parsed_uri_ok=0; /* invalidate current parsed uri*/ }; ret=1; break; case SET_HOST_T: case SET_HOSTPORT_T: case SET_USER_T: case SET_USERPASS_T: case SET_PORT_T: case SET_URI_T: case PREFIX_T: case STRIP_T: case STRIP_TAIL_T: script_trace("core", (unsigned char)a->type == SET_HOST_T ? "set_host" : (unsigned char)a->type == SET_HOSTPORT_T ? "set_hostport" : (unsigned char)a->type == SET_USER_T ? "set_user" : (unsigned char)a->type == SET_USERPASS_T ? "set_userpass" : (unsigned char)a->type == SET_PORT_T ? "set_port" : (unsigned char)a->type == SET_URI_T ? "set_uri" : (unsigned char)a->type == PREFIX_T ? "prefix" : (unsigned char)a->type == STRIP_T ? "strip" : "strip_tail", msg, a->file, a->line); user=0; if (a->type==STRIP_T || a->type==STRIP_TAIL_T) { if (a->elem[0].type!=NUMBER_ST) { LM_ALERT("BUG in set*() type %d\n", a->elem[0].type); break; } } else if (a->elem[0].type!=STR_ST){ LM_ALERT("BUG in set*() type %d\n", a->elem[0].type); ret=E_BUG; break; } if (a->type==SET_URI_T) { if (set_ruri( msg, &a->elem[0].u.s) ) { LM_ERR("failed to set new RURI\n"); ret=E_OUT_OF_MEM; break; } ret=1; break; } if (msg->new_uri.s) { tmp=msg->new_uri.s; len=msg->new_uri.len; }else{ tmp=msg->first_line.u.request.uri.s; len=msg->first_line.u.request.uri.len; } if (parse_uri(tmp, len, &uri)<0){ LM_ERR("bad uri <%.*s>, dropping packet\n", len, tmp); ret=E_UNSPEC; break; } new_uri=pkg_malloc(MAX_URI_SIZE); if (new_uri==0){ LM_ERR("memory allocation failure\n"); ret=E_OUT_OF_MEM; break; } end=new_uri+MAX_URI_SIZE; crt=new_uri; /* begin copying */ len = (uri.user.len?uri.user.s:uri.host.s) - tmp; if (crt+len>end) goto error_uri; memcpy(crt,tmp,len);crt+=len; if (a->type==PREFIX_T) { if (crt+a->elem[0].u.s.len>end) goto error_uri; memcpy( crt, a->elem[0].u.s.s, a->elem[0].u.s.len); crt+=a->elem[0].u.s.len; /* whatever we had before, with prefix we have username now */ user=1; } if ((a->type==SET_USER_T)||(a->type==SET_USERPASS_T)) { tmp=a->elem[0].u.s.s; len=a->elem[0].u.s.len; } else if (a->type==STRIP_T) { if (a->elem[0].u.number>uri.user.len) { LM_WARN("too long strip asked; " " deleting username: %lu of <%.*s>\n", a->elem[0].u.number, uri.user.len, uri.user.s); len=0; } else if (a->elem[0].u.number==uri.user.len) { len=0; } else { tmp=uri.user.s + a->elem[0].u.number; len=uri.user.len - a->elem[0].u.number; } } else if (a->type==STRIP_TAIL_T) { if (a->elem[0].u.number>uri.user.len) { LM_WARN("too long strip_tail asked;" " deleting username: %lu of <%.*s>\n", a->elem[0].u.number, uri.user.len, uri.user.s); len=0; } else if (a->elem[0].u.number==uri.user.len) { len=0; } else { tmp=uri.user.s; len=uri.user.len - a->elem[0].u.number; } } else { tmp=uri.user.s; len=uri.user.len; } if (len){ if(crt+len>end) goto error_uri; memcpy(crt,tmp,len);crt+=len; user=1; /* we have an user field so mark it */ } if (a->type==SET_USERPASS_T) tmp=0; else tmp=uri.passwd.s; /* passwd */ if (tmp){ len=uri.passwd.len; if(crt+len+1>end) goto error_uri; *crt=':'; crt++; memcpy(crt,tmp,len);crt+=len; } /* host */ if (user || tmp){ /* add @ */ if(crt+1>end) goto error_uri; *crt='@'; crt++; } if ((a->type==SET_HOST_T) ||(a->type==SET_HOSTPORT_T)) { tmp=a->elem[0].u.s.s; len=a->elem[0].u.s.len; } else { tmp=uri.host.s; len = uri.host.len; } if (tmp){ if(crt+len>end) goto error_uri; memcpy(crt,tmp,len);crt+=len; } /* port */ if (a->type==SET_HOSTPORT_T) tmp=0; else if (a->type==SET_PORT_T) { tmp=a->elem[0].u.s.s; len=a->elem[0].u.s.len; } else { tmp=uri.port.s; len = uri.port.len; } if (tmp && len>0){ if(crt+len+1>end) goto error_uri; *crt=':'; crt++; memcpy(crt,tmp,len);crt+=len; } /* params */ tmp=uri.params.s; if (tmp){ /* include in param string the starting ';' */ len=uri.params.len+1; tmp--; if(crt+len+1>end) goto error_uri; /* if a maddr param is present, strip it out */ if (uri.maddr.len && (a->type==SET_HOSTPORT_T || a->type==SET_HOST_T)) { memcpy(crt,tmp,uri.maddr.s-tmp-1); crt+=uri.maddr.s-tmp-1; memcpy(crt,uri.maddr_val.s+uri.maddr_val.len, tmp+len-uri.maddr_val.s-uri.maddr_val.len); crt+=tmp+len-uri.maddr_val.s-uri.maddr_val.len; } else { memcpy(crt,tmp,len);crt+=len; } } /* headers */ tmp=uri.headers.s; if (tmp){ len=uri.headers.len; if(crt+len+1>end) goto error_uri; *crt='?'; crt++; memcpy(crt,tmp,len);crt+=len; } *crt=0; /* null terminate the thing */ /* copy it to the msg */ if (msg->new_uri.s) pkg_free(msg->new_uri.s); msg->new_uri.s=new_uri; msg->new_uri.len=crt-new_uri; msg->parsed_uri_ok=0; ret=1; break; case SET_DSTURI_T: script_trace("core", "set_dsturi", msg, a->file, a->line) ; if (a->elem[0].type!=STR_ST){ LM_ALERT("BUG in setdsturi() type %d\n", a->elem[0].type); ret=E_BUG; break; } if(set_dst_uri(msg, &a->elem[0].u.s)!=0) ret = -1; else ret = 1; break; case SET_DSTHOST_T: case SET_DSTPORT_T: script_trace("core", (unsigned char) a->type == SET_DSTHOST_T ? "set_dsturi" : "set_dstport", msg, a->file, a->line); if (a->elem[0].type!=STR_ST){ LM_ALERT("BUG in domain setting type %d\n", a->elem[0].type); ret=E_BUG; break; } tmp = msg->dst_uri.s; len = msg->dst_uri.len; if (tmp == NULL || len == 0) { LM_ERR("failure - null uri\n"); ret = E_UNSPEC; break; } if (a->type == SET_DSTHOST_T && (a->elem[0].u.s.s == NULL || a->elem[0].u.s.len == 0)) { LM_ERR("cannot set a null uri domain\n"); break; } if (parse_uri(tmp, len, &uri)<0) { LM_ERR("bad uri <%.*s>, dropping packet\n", len, tmp); break; } new_uri=pkg_malloc(MAX_URI_SIZE); if (new_uri == NULL) { LM_ERR("memory allocation failure\n"); ret=E_OUT_OF_MEM; break; } end=new_uri+MAX_URI_SIZE; crt=new_uri; len = (uri.user.len?uri.user.s:uri.host.s) - tmp; if (crt+len>end) goto error_uri; memcpy(crt,tmp,len); crt += len; /* user */ tmp = uri.user.s; len = uri.user.len; if (tmp) { if (crt+len>end) goto error_uri; memcpy(crt,tmp,len); crt += len; user = 1; } /* passwd */ tmp = uri.passwd.s; len = uri.passwd.len; if (tmp) { if (crt+len+1>end) goto error_uri; *crt++=':'; memcpy(crt, tmp, len); crt += len; } /* host */ if (a->type==SET_DSTHOST_T) { tmp = a->elem[0].u.s.s; len = a->elem[0].u.s.len; } else { tmp = uri.host.s; len = uri.host.len; } if (tmp) { if (user) { if (crt+1>end) goto error_uri; *crt++='@'; } if (crt+len+1>end) goto error_uri; memcpy(crt, tmp, len); crt += len; } /* port */ if (a->type==SET_DSTPORT_T) { tmp = a->elem[0].u.s.s; len = a->elem[0].u.s.len; } else { tmp = uri.port.s; len = uri.port.len; } if (tmp) { if (crt+len+1>end) goto error_uri; *crt++=':'; memcpy(crt, tmp, len); crt += len; } /* params */ tmp=uri.params.s; if (tmp){ len=uri.params.len; if(crt+len+1>end) goto error_uri; *crt++=';'; memcpy(crt,tmp,len); crt += len; } /* headers */ tmp=uri.headers.s; if (tmp){ len=uri.headers.len; if(crt+len+1>end) goto error_uri; *crt++='?'; memcpy(crt,tmp,len); crt += len; } *crt=0; /* null terminate the thing */ /* copy it to the msg */ pkg_free(msg->dst_uri.s); msg->dst_uri.s=new_uri; msg->dst_uri.len=crt-new_uri; ret = 1; break; case RESET_DSTURI_T: script_trace("core", "reset_dsturi", msg, a->file, a->line) ; if(msg->dst_uri.s!=0) pkg_free(msg->dst_uri.s); msg->dst_uri.s = 0; msg->dst_uri.len = 0; ret = 1; break; case ISDSTURISET_T: script_trace("core", "isdsturiset", msg, a->file, a->line) ; if(msg->dst_uri.s==0 || msg->dst_uri.len<=0) ret = -1; else ret = 1; break; case IF_T: script_trace("core", "if", msg, a->file, a->line) ; /* if null expr => ignore if? */ if ((a->elem[0].type==EXPR_ST)&&a->elem[0].u.data){ v=eval_expr((struct expr*)a->elem[0].u.data, msg, 0); /* set return code to expr value */ if (v<0 || (action_flags&ACT_FL_RETURN) || (action_flags&ACT_FL_EXIT) ){ if (v==EXPR_DROP || (action_flags&ACT_FL_RETURN) || (action_flags&ACT_FL_EXIT) ){ /* hack to quit on DROP*/ ret=0; return_code = 0; break; }else{ LM_WARN("error in expression at %s:%d\n", a->file, a->line); } } ret=1; /*default is continue */ if (v>0) { if ((a->elem[1].type==ACTIONS_ST)&&a->elem[1].u.data){ ret=run_action_list( (struct action*)a->elem[1].u.data,msg ); return_code = ret; } else return_code = v; }else{ if ((a->elem[2].type==ACTIONS_ST)&&a->elem[2].u.data){ ret=run_action_list( (struct action*)a->elem[2].u.data,msg); return_code = ret; } else return_code = v; } } break; case WHILE_T: script_trace("core", "while", msg, a->file, a->line) ; /* if null expr => ignore if? */ if ((a->elem[0].type==EXPR_ST)&&a->elem[0].u.data){ len = 0; while(1) { if(len++ >= max_while_loops) { LM_INFO("max while loops are encountered\n"); break; } v=eval_expr((struct expr*)a->elem[0].u.data, msg, 0); /* set return code to expr value */ if (v<0 || (action_flags&ACT_FL_RETURN) || (action_flags&ACT_FL_EXIT) ){ if (v==EXPR_DROP || (action_flags&ACT_FL_RETURN) || (action_flags&ACT_FL_EXIT) ){ ret=0; return_code = 0; break; }else{ LM_WARN("error in expression at %s:%d\n", a->file, a->line); } } ret=1; /*default is continue */ if (v>0) { if ((a->elem[1].type==ACTIONS_ST) &&a->elem[1].u.data){ ret=run_action_list( (struct action*)a->elem[1].u.data,msg ); /* check if return was done */ if ((action_flags&ACT_FL_RETURN) || (action_flags&ACT_FL_EXIT) ){ break; } return_code = ret; } else { /* we should not get here */ return_code = v; break; } } else { /* condition was false */ return_code = v; break; } } } break; case FOR_EACH_T: script_trace("core", "for-each", msg, a->file, a->line) ; ret = for_each_handler(msg, a); break; case CACHE_STORE_T: script_trace("core", "cache_store", msg, a->file, a->line) ; if ((a->elem[0].type!=STR_ST)) { LM_ALERT("BUG in cache_store() - first argument not of" " type string [%d]\n", a->elem[0].type ); ret=E_BUG; break; } if ((a->elem[1].type!=STR_ST)) { LM_ALERT("BUG in cache_store() - second argument not of " "type string [%d]\n", a->elem[1].type ); ret=E_BUG; break; } if ((a->elem[2].type!=STR_ST)) { LM_ALERT("BUG in cache_store() - third argument not of type" " string%d\n", a->elem[2].type ); ret=E_BUG; break; } str val_s; /* parse the name argument */ pve = (pv_elem_t *)a->elem[1].u.data; if ( pv_printf_s(msg, pve, &name_s)!=0 || name_s.len == 0 || name_s.s == NULL) { LM_WARN("cannot get string for value\n"); ret=E_BUG; break; } /* parse the value argument */ pve = (pv_elem_t *)a->elem[2].u.data; if ( pv_printf_s(msg, pve, &val_s)!=0 || val_s.len == 0 || val_s.s == NULL) { LM_WARN("cannot get string for value\n"); ret=E_BUG; break; } /* get the expires value */ if ( a->elem[3].type == SCRIPTVAR_ST ) { spec = (pv_spec_t*)a->elem[3].u.data; memset(&val, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, spec, &val) < 0) { LM_DBG("Failed to get scriptvar value while executing cache_store\n"); ret=E_BUG; break; } if (!(val.flags&PV_VAL_INT)) { LM_ERR("Wrong value for cache_store expires, not an integer [%.*s]\n", val.rs.len, val.rs.s); } expires = val.ri; } else if ( a->elem[3].type == NUMBER_ST ) { expires = (int)a->elem[3].u.number; } ret = cachedb_store( &a->elem[0].u.s, &name_s, &val_s,expires); break; case CACHE_REMOVE_T: script_trace("core", "cache_remove", msg, a->file, a->line) ; if ((a->elem[0].type!=STR_ST)) { LM_ALERT("BUG in cache_remove() %d\n", a->elem[0].type ); ret=E_BUG; break; } if ((a->elem[1].type!=STR_ST)) { LM_ALERT("BUG in cache_remove() %d\n", a->elem[1].type ); ret=E_BUG; break; } /* parse the name argument */ pve = (pv_elem_t *)a->elem[1].u.data; if ( pv_printf_s(msg, pve, &name_s)!=0 || name_s.len == 0 || name_s.s == NULL) { LM_WARN("cannot get string for value\n"); ret=E_BUG; break; } ret = cachedb_remove( &a->elem[0].u.s, &name_s); break; case CACHE_FETCH_T: script_trace("core", "cache_fetch", msg, a->file, a->line) ; if ((a->elem[0].type!=STR_ST)) { LM_ALERT("BUG in cache_fetch() %d\n", a->elem[0].type ); ret=E_BUG; break; } if ((a->elem[1].type!=STR_ST)) { LM_ALERT("BUG in cache_fetch() %d\n", a->elem[1].type ); ret=E_BUG; break; } if (a->elem[2].type!=SCRIPTVAR_ST){ LM_ALERT("BUG in cache_fetch() type %d\n", a->elem[2].type); ret=E_BUG; break; } str aux = {0, 0}; /* parse the name argument */ pve = (pv_elem_t *)a->elem[1].u.data; if ( pv_printf_s(msg, pve, &name_s)!=0 || name_s.len == 0 || name_s.s == NULL) { LM_WARN("cannot get string for value\n"); ret=E_BUG; break; } ret = cachedb_fetch( &a->elem[0].u.s, &name_s, &aux); if(ret > 0) { val.rs = aux; val.flags = PV_VAL_STR; spec = (pv_spec_t*)a->elem[2].u.data; if (pv_set_value(msg, spec, 0, &val) < 0) { LM_ERR("cannot set the variable value\n"); pkg_free(aux.s); return -1; } pkg_free(aux.s); } break; case CACHE_COUNTER_FETCH_T: script_trace("core", "cache_counter_fetch", msg, a->file, a->line) ; if ((a->elem[0].type!=STR_ST)) { LM_ALERT("BUG in cache_fetch() %d\n", a->elem[0].type ); ret=E_BUG; break; } if ((a->elem[1].type!=STR_ST)) { LM_ALERT("BUG in cache_fetch() %d\n", a->elem[1].type ); ret=E_BUG; break; } if (a->elem[2].type!=SCRIPTVAR_ST){ LM_ALERT("BUG in cache_fetch() type %d\n", a->elem[2].type); ret=E_BUG; break; } /* parse the name argument */ pve = (pv_elem_t *)a->elem[1].u.data; if ( pv_printf_s(msg, pve, &name_s)!=0 || name_s.len == 0 || name_s.s == NULL) { LM_WARN("cannot get string for value\n"); ret=E_BUG; break; } ret = cachedb_counter_fetch( &a->elem[0].u.s, &name_s, &aux_counter); if(ret > 0) { val.ri = aux_counter; val.flags = PV_TYPE_INT|PV_VAL_INT; spec = (pv_spec_t*)a->elem[2].u.data; if (pv_set_value(msg, spec, 0, &val) < 0) { LM_ERR("cannot set the variable value\n"); pkg_free(aux.s); return -1; } } break; case CACHE_ADD_T: script_trace("core", "cache_add", msg, a->file, a->line) ; if ((a->elem[0].type!=STR_ST)) { LM_ALERT("BUG in cache_add() - first argument not of" " type string [%d]\n", a->elem[0].type ); ret=E_BUG; break; } if ((a->elem[1].type!=STR_ST)) { LM_ALERT("BUG in cache_add() - second argument not of " "type string [%d]\n", a->elem[1].type ); ret=E_BUG; break; } /* parse the name argument */ pve = (pv_elem_t *)a->elem[1].u.data; if ( pv_printf_s(msg, pve, &name_s)!=0 || name_s.len == 0 || name_s.s == NULL) { LM_WARN("cannot get string for value\n"); ret=E_BUG; break; } int increment=0; /* get the increment value */ if ( a->elem[2].type == SCRIPTVAR_ST ) { spec = (pv_spec_t*)a->elem[2].u.data; memset(&val, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, spec, &val) < 0) { LM_DBG("Failed to get scriptvar value while executing cache_add\n"); ret=E_BUG; break; } if (!(val.flags&PV_VAL_INT)) { LM_ERR("Wrong value for cache_add, not an integer [%.*s]\n", val.rs.len, val.rs.s); } increment = val.ri; } else if ( a->elem[2].type == NUMBER_ST ) { increment = (int)a->elem[2].u.number; } expires = (int)a->elem[3].u.number; ret = cachedb_add(&a->elem[0].u.s, &name_s, increment, expires, &aux_counter); /* Return the new value */ if (ret > 0 && a->elem[4].u.data != NULL) { val.ri = aux_counter; val.flags = PV_TYPE_INT|PV_VAL_INT; spec = (pv_spec_t*)a->elem[4].u.data; if (pv_set_value(msg, spec, 0, &val) < 0) { LM_ERR("cannot set the variable value\n"); return -1; } } break; case CACHE_SUB_T: script_trace("core", "cache_sub", msg, a->file, a->line) ; if ((a->elem[0].type!=STR_ST)) { LM_ALERT("BUG in cache_sub() - first argument not of" " type string [%d]\n", a->elem[0].type ); ret=E_BUG; break; } if ((a->elem[1].type!=STR_ST)) { LM_ALERT("BUG in cache_sub() - second argument not of " "type string [%d]\n", a->elem[1].type ); ret=E_BUG; break; } /* parse the name argument */ pve = (pv_elem_t *)a->elem[1].u.data; if ( pv_printf_s(msg, pve, &name_s)!=0 || name_s.len == 0 || name_s.s == NULL) { LM_WARN("cannot get string for value\n"); ret=E_BUG; break; } int decrement=0; /* get the increment value */ if ( a->elem[2].type == SCRIPTVAR_ST ) { spec = (pv_spec_t*)a->elem[2].u.data; memset(&val, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, spec, &val) < 0) { LM_DBG("Failed to get scriptvar value while executing cache_sub\n"); ret=E_BUG; break; } if (!(val.flags&PV_VAL_INT)) { LM_ERR("Wrong value for cache_sub, not an integer [%.*s]\n", val.rs.len, val.rs.s); } decrement = val.ri; } else if ( a->elem[2].type == NUMBER_ST ) { decrement = (int)a->elem[2].u.number; } expires = (int)a->elem[3].u.number; ret = cachedb_sub(&a->elem[0].u.s, &name_s, decrement,expires,&aux_counter); /* Return the new value */ if (ret > 0 && a->elem[4].u.data != NULL) { val.ri = aux_counter; val.flags = PV_TYPE_INT|PV_VAL_INT; spec = (pv_spec_t*)a->elem[4].u.data; if (pv_set_value(msg, spec, 0, &val) < 0) { LM_ERR("cannot set the variable value\n"); return -1; } } break; case CACHE_RAW_QUERY_T: if ((a->elem[0].type!=STR_ST)) { LM_ALERT("BUG in cache_fetch() %d\n", a->elem[0].type ); ret=E_BUG; break; } if ((a->elem[1].type!=STR_ST)) { LM_ALERT("BUG in cache_fetch() %d\n", a->elem[1].type ); ret=E_BUG; break; } if (a->elem[2].u.data != NULL && a->elem[2].type!=STR_ST){ LM_ALERT("BUG in cache_raw_query() type %d\n", a->elem[2].type); ret=E_BUG; break; } /* parse the name argument */ pve = (pv_elem_t *)a->elem[1].u.data; if ( pv_printf_s(msg, pve, &name_s)!=0 || name_s.len == 0 || name_s.s == NULL) { LM_WARN("cannot get string for value\n"); ret=E_BUG; break; } cdb_raw_entry **cdb_reply; int val_number=0,i,j; int key_number=0; pvname_list_t *cdb_res,*it; int_str avp_val; int_str avp_name; unsigned short avp_type; if (a->elem[2].u.data) { cdb_res = (pvname_list_t*)a->elem[2].u.data; for (it=cdb_res;it;it=it->next) val_number++; LM_DBG("The query expects %d results back\n",val_number); ret = cachedb_raw_query( &a->elem[0].u.s, &name_s, &cdb_reply,val_number,&key_number); if (ret >= 0 && val_number > 0) { for (i=key_number-1; i>=0;i--) { it=cdb_res; for (j=0;j < val_number;j++) { avp_type = 0; if (pv_get_avp_name(msg,&it->sname.pvp,&avp_name.n, &avp_type) != 0) { LM_ERR("cannot get avp name [%d/%d]\n",i,j); goto next_avp; } switch (cdb_reply[i][j].type) { case CDB_INT: avp_val.n = cdb_reply[i][j].val.n; break; case CDB_STR: avp_type |= AVP_VAL_STR; avp_val.s = cdb_reply[i][j].val.s; break; case CDB_NULL: avp_type |= AVP_VAL_NULL; avp_val.s = cdb_reply[i][j].val.s; break; default: LM_WARN("Unknown type %d\n",cdb_reply[i][j].type); goto next_avp; } if (add_avp(avp_type,avp_name.n,avp_val) != 0) { LM_ERR("Unable to add AVP\n"); free_raw_fetch(cdb_reply,val_number,key_number); return -1; } next_avp: if (it) { it = it->next; if (it==NULL) break; } } } free_raw_fetch(cdb_reply,val_number,key_number); } } else ret = cachedb_raw_query( &a->elem[0].u.s, &name_s, NULL,0,NULL); break; case XDBG_T: script_trace("core", "xdbg", msg, a->file, a->line) ; if (a->elem[0].type == SCRIPTVAR_ELEM_ST) { if (xdbg(msg, a->elem[0].u.data, val.rs.s) < 0) { LM_ALERT("Cannot print message\n"); break; } } else { LM_ALERT("BUG in xdbg() type %d\n", a->elem[0].type); ret=E_BUG; } break; case XLOG_T: script_trace("core", "xlog", msg, a->file, a->line) ; if (a->elem[1].u.data != NULL) { if (a->elem[1].type != SCRIPTVAR_ELEM_ST) { LM_ALERT("BUG in xlog() type %d\n", a->elem[1].type); ret=E_BUG; break; } if (a->elem[0].type != STR_ST) { LM_ALERT("BUG in xlog() type %d\n", a->elem[0].type); ret=E_BUG; break; } if (xlog_2(msg,a->elem[0].u.data, a->elem[1].u.data) < 0) { LM_ALERT("Cannot print xlog debug message\n"); break; } } else { if (a->elem[0].type != SCRIPTVAR_ELEM_ST) { LM_ALERT("BUG in xlog() type %d\n", a->elem[0].type); ret=E_BUG; break; } if (xlog_1(msg,a->elem[0].u.data, val.rs.s) < 0) { LM_ALERT("Cannot print xlog debug message\n"); break; } } break; case RAISE_EVENT_T: script_trace("core", "raise_event", msg, a->file, a->line) ; if (a->elem[0].type != NUMBER_ST) { LM_ERR("invalid event id\n"); ret=E_BUG; break; } if (a->elem[2].u.data) { /* three parameters specified */ ret = evi_raise_script_event(msg, (event_id_t)a->elem[0].u.number, a->elem[1].u.data, a->elem[2].u.data); } else { /* two parameters specified */ ret = evi_raise_script_event(msg, (event_id_t)a->elem[0].u.number, NULL, a->elem[1].u.data); } if (ret <= 0) { LM_ERR("cannot raise event\n"); ret=E_UNSPEC; break; } break; case SUBSCRIBE_EVENT_T: script_trace("core", "subscribe_event", msg, a->file, a->line) ; if (a->elem[0].type != STR_ST || a->elem[1].type != STR_ST) { LM_ERR("BUG in subscribe arguments\n"); ret=E_BUG; break; } if (a->elem[2].u.data) { if (a->elem[2].type != NUMBER_ST) { LM_ERR("BUG in subscribe expiration time\n"); ret=E_BUG; break; } else { i = a->elem[2].u.number; } } else { i = 0; } name_s.s = a->elem[0].u.data; name_s.len = strlen(name_s.s); /* result should be the socket */ result.s = a->elem[1].u.data; result.len = strlen(result.s); ret = evi_event_subscribe(name_s, result, i, 0); break; case CONSTRUCT_URI_T: script_trace("core", "construct_uri", msg, a->file, a->line) ; for (i=0;i<5;i++) { pve = (pv_elem_t *)a->elem[i].u.data; if (pve->spec.getf) { if ( pv_printf_s(msg, pve, &vals[i])!=0 || vals[i].len == 0 || vals[i].s == NULL) { LM_WARN("cannot get string for value\n"); ret=E_BUG; return -1; } } else vals[i] = pve->text; } result.s = construct_uri(&vals[0],&vals[1],&vals[2],&vals[3],&vals[4], &result.len); if (result.s) { int_str res; int avp_name; unsigned short avp_type; spec = (pv_spec_t*)a->elem[5].u.data; if (pv_get_avp_name( msg, &(spec->pvp), &avp_name, &avp_type)!=0){ LM_CRIT("BUG in getting AVP name\n"); return -1; } res.s = result; if (add_avp(AVP_VAL_STR|avp_type, avp_name, res)<0){ LM_ERR("cannot add AVP\n"); return -1; } } break; case GET_TIMESTAMP_T: script_trace("core", "get_timestamp", msg, a->file, a->line) ; if (get_timestamp(&sec,&usec) == 0) { int avp_name; int_str res; unsigned short avp_type; spec = (pv_spec_t*)a->elem[0].u.data; if (pv_get_avp_name(msg, &(spec->pvp), &avp_name, &avp_type) != 0) { LM_CRIT("BUG in getting AVP name\n"); return -1; } res.n = sec; if (add_avp(avp_type, avp_name, res) < 0) { LM_ERR("cannot add AVP\n"); return -1; } spec = (pv_spec_t*)a->elem[1].u.data; if (pv_get_avp_name(msg, &(spec->pvp), &avp_name, &avp_type) != 0) { LM_CRIT("BUG in getting AVP name\n"); return -1; } res.n = usec; if (add_avp(avp_type, avp_name, res) < 0) { LM_ERR("cannot add AVP\n"); return -1; } } else { LM_ERR("failed to get time\n"); return -1; } break; case SWITCH_T: script_trace("core", "switch", msg, a->file, a->line) ; if (a->elem[0].type!=SCRIPTVAR_ST){ LM_ALERT("BUG in switch() type %d\n", a->elem[0].type); ret=E_BUG; break; } spec = (pv_spec_t*)a->elem[0].u.data; if(pv_get_spec_value(msg, spec, &val)!=0) { LM_ALERT("BUG - no value in switch()\n"); ret=E_BUG; break; } /* get the value of pvar */ if(a->elem[1].type!=ACTIONS_ST) { LM_ALERT("BUG in switch() actions\n"); ret=E_BUG; break; } return_code=1; adefault = NULL; aitem = (struct action*)a->elem[1].u.data; cmatch=0; while(aitem) { if((unsigned char)aitem->type==DEFAULT_T) adefault=aitem; if(cmatch==0) { if(aitem->elem[0].type==STR_ST) { if(val.flags&PV_VAL_STR && val.rs.len==aitem->elem[0].u.s.len && strncasecmp(val.rs.s, aitem->elem[0].u.s.s, val.rs.len)==0) cmatch = 1; } else { /* number */ if(val.flags&PV_VAL_INT && val.ri==aitem->elem[0].u.number) cmatch = 1; } } if(cmatch==1) { if(aitem->elem[1].u.data) { return_code=run_action_list( (struct action*)aitem->elem[1].u.data, msg); if ((action_flags&ACT_FL_RETURN) || (action_flags&ACT_FL_EXIT)) break; } if(aitem->elem[2].u.number==1) break; } aitem = aitem->next; } if((cmatch==0) && (adefault!=NULL)) { LM_DBG("switch: running default statement\n"); if(adefault->elem[0].u.data) return_code=run_action_list( (struct action*)adefault->elem[0].u.data, msg); } ret=return_code; break; case MODULE_T: script_trace("module", ((cmd_export_t*)(a->elem[0].u.data))->name, msg, a->file, a->line) ; if ( (a->elem[0].type==CMD_ST) && a->elem[0].u.data ) { ret=((cmd_export_t*)(a->elem[0].u.data))->function(msg, (char*)a->elem[1].u.data, (char*)a->elem[2].u.data, (char*)a->elem[3].u.data, (char*)a->elem[4].u.data, (char*)a->elem[5].u.data, (char*)a->elem[6].u.data); }else{ LM_ALERT("BUG in module call\n"); } break; case ASYNC_T: /* first param - an ACTIONS_ST containing an ACMD_ST * second param - a NUMBER_ST pointing to resume route */ aitem = (struct action *)(a->elem[0].u.data); if (async_start_f==NULL || a->elem[0].type!=ACTIONS_ST || a->elem[1].type!=NUMBER_ST || aitem->type!=AMODULE_T) { LM_ALERT("BUG in async expression\n"); } else { script_trace("async", ((acmd_export_t*)(aitem->elem[0].u.data))->name, msg, a->file, a->line) ; ret = async_start_f( msg, aitem, a->elem[1].u.number); if (ret>=0) action_flags |= ACT_FL_TBCONT; } ret = 0; break; case FORCE_RPORT_T: script_trace("core", "force_rport", msg, a->file, a->line) ; msg->msg_flags|=FL_FORCE_RPORT; ret=1; /* continue processing */ break; case FORCE_LOCAL_RPORT_T: script_trace("core", "force_local_rport", msg, a->file, a->line) ; msg->msg_flags|=FL_FORCE_LOCAL_RPORT; ret=1; /* continue processing */ break; case SET_ADV_ADDR_T: script_trace("core", "set_adv_addr", msg, a->file, a->line); ret = do_action_set_adv_address(msg, a); break; case SET_ADV_PORT_T: script_trace("core", "set_adv_port", msg, a->file, a->line); ret = do_action_set_adv_port(msg, a); break; case FORCE_TCP_ALIAS_T: script_trace("core", "force_tcp_alias", msg, a->file, a->line) ; if (is_tcp_based_proto(msg->rcv.proto)) { if (a->elem[0].type==NOSUBTYPE) port=msg->via1->port; else if (a->elem[0].type==NUMBER_ST) port=(int)a->elem[0].u.number; else{ LM_ALERT("BUG in force_tcp_alias" " port type %d\n", a->elem[0].type); ret=E_BUG; break; } if (tcpconn_add_alias(msg->rcv.proto_reserved1, port, msg->rcv.proto)!=0){ LM_WARN("tcp alias failed\n"); ret=E_UNSPEC; break; } } ret=1; /* continue processing */ break; case FORCE_SEND_SOCKET_T: script_trace("core", "force_send_socket", msg, a->file, a->line) ; if (a->elem[0].type!=SOCKETINFO_ST){ LM_ALERT("BUG in force_send_socket argument" " type: %d\n", a->elem[0].type); ret=E_BUG; break; } msg->force_send_socket=(struct socket_info*)a->elem[0].u.data; ret=1; /* continue processing */ break; case SERIALIZE_BRANCHES_T: script_trace("core", "serialize_branches", msg, a->file, a->line) ; if (a->elem[0].type!=NUMBER_ST){ LM_ALERT("BUG in serialize_branches argument" " type: %d\n", a->elem[0].type); ret=E_BUG; break; } if (serialize_branches(msg,(int)a->elem[0].u.number)!=0) { LM_ERR("serialize_branches failed\n"); ret=E_UNSPEC; break; } ret=1; /* continue processing */ break; case NEXT_BRANCHES_T: script_trace("core", "next_branches", msg, a->file, a->line) ; if ((ret=next_branches(msg))<0) { LM_ERR("next_branches failed\n"); ret=E_UNSPEC; break; } /* continue processing */ break; case EQ_T: case COLONEQ_T: case PLUSEQ_T: case MINUSEQ_T: case DIVEQ_T: case MULTEQ_T: case MODULOEQ_T: case BANDEQ_T: case BOREQ_T: case BXOREQ_T: ret = do_assign(msg, a); break; case USE_BLACKLIST_T: script_trace("core", "use_blacklist", msg, a->file, a->line) ; mark_for_search((struct bl_head*)a->elem[0].u.data, 1); break; case UNUSE_BLACKLIST_T: script_trace("core", "unuse_blacklist", msg, a->file, a->line); mark_for_search((struct bl_head*)a->elem[0].u.data, 0); break; case PV_PRINTF_T: script_trace("core", "pv_printf", msg, a->file, a->line); ret = -1; spec = (pv_spec_p)a->elem[0].u.data; if(!pv_is_w(spec)) { LM_ERR("read only PV in first parameter of pv_printf\n"); goto error; } model = (pv_elem_p)a->elem[1].u.data; memset(&val, 0, sizeof(pv_value_t)); if(pv_printf_s(msg, model, &val.rs)!=0) { LM_ERR("cannot eval second parameter\n"); goto error; } val.flags = PV_VAL_STR; if(pv_set_value(msg, spec, EQ_T, &val)<0) { LM_ERR("setting PV failed\n"); goto error; } ret = 1; break; case SCRIPT_TRACE_T: script_trace("core", "script_trace", msg, a->file, a->line); if (a->elem[0].type==NOSUBTYPE) { use_script_trace = 0; } else { use_script_trace = 1; if (a->elem[0].type != NUMBER_ST || a->elem[1].type != SCRIPTVAR_ELEM_ST) { LM_ERR("BUG in use_script_trace() arguments\n"); ret=E_BUG; break; } if (a->elem[2].type!=NOSUBTYPE) { script_trace_info = (char *)a->elem[2].u.data; } else { script_trace_info = NULL; } script_trace_log_level = (int)a->elem[0].u.number; script_trace_elem = *(pv_elem_p)a->elem[1].u.data; } break; default: LM_ALERT("BUG - unknown type %d\n", a->type); goto error; } if((unsigned char)a->type!=IF_T && (unsigned char)a->type!=ROUTE_T) return_code = ret; /*skip:*/ update_longest_action(a); return ret; error: LM_ERR("error in %s:%d\n", a->file, a->line); update_longest_action(a); return ret; error_uri: LM_ERR("set*: uri too long\n"); if (new_uri) pkg_free(new_uri); update_longest_action(a); return E_UNSPEC; error_fwd_uri: update_longest_action(a); return ret; } static int for_each_handler(struct sip_msg *msg, struct action *a) { pv_spec_p iter, spec; pv_param_t pvp; pv_value_t val; int ret = 1; int op = 0; if (a->elem[2].type == ACTIONS_ST && a->elem[2].u.data) { iter = a->elem[0].u.data; spec = a->elem[1].u.data; /* * simple is always better. * just don't allow fancy for-each statements */ if (spec->pvp.pvi.type != PV_IDX_ALL) { LM_ERR("for-each must be used on a \"[*]\" index! skipping!\n"); return E_SCRIPT; } memset(&pvp, 0, sizeof pvp); pvp.pvi.type = PV_IDX_INT; pvp.pvn = spec->pvp.pvn; /* * for $json iterators, better to assume script writer * wants data to be interpreted, rather than not * (i.e. ":=" script operator, and not simply "=") */ if (iter->type == PVT_JSON) op = COLONEQ_T; for (;;) { if (spec->getf(msg, &pvp, &val) != 0) { LM_ERR("failed to get spec value\n"); return E_BUG; } if (val.flags & PV_VAL_NULL) break; if (iter->setf(msg, &iter->pvp, op, &val) != 0) { LM_ERR("failed to set scriptvar value\n"); return E_BUG; } ret = run_action_list( (struct action *)a->elem[2].u.data, msg); /* check for "return" statements or "0" retcodes */ if (action_flags & (ACT_FL_RETURN | ACT_FL_EXIT)) return ret; pvp.pvi.u.ival++; } } return ret; } /** * prints the current point of execution in the OpenSIPS script * * @class - optional, string to be printed meaning the class of action (if any) * @action - mandatory, string with the name of action * @msg - mandatory, sip message * @line - line in script */ void __script_trace(char *class, char *action, struct sip_msg *msg, char *file, int line) { gparam_t param; str val; param.type = GPARAM_TYPE_PVE; param.v.pve = &script_trace_elem; val.s = NULL; val.len = 0; if (fixup_get_svalue(msg, ¶m, &val) != 0) { LM_ERR("Failed to get pv elem value!\n"); return; } /* Also print extra info */ if (script_trace_info) { LM_GEN1(script_trace_log_level, "[Script Trace][%s:%d][%s][%s %s]"\ " -> (%.*s)\n", file, line, script_trace_info, class?class:"", action, val.len, val.s); } else { LM_GEN1(script_trace_log_level, "[Script Trace][%s:%d][%s %s]"\ " -> (%.*s)\n", file, line, class?class:"", action, val.len, val.s); } } opensips-2.2.2/action.h000066400000000000000000000036171300170765700150200ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief OpenSIPS generic functions */ #ifndef action_h #define action_h #include "parser/msg_parser.h" #include "route_struct.h" #define ACT_FL_EXIT (1<<0) #define ACT_FL_RETURN (1<<1) #define ACT_FL_DROP (2<<2) #define ACT_FL_TBCONT (2<<3) extern int action_flags; extern int use_script_trace; extern action_elem_p route_params[MAX_REC_LEV]; extern int route_params_number[MAX_REC_LEV]; extern int route_rec_level; #define LONGEST_ACTION_SIZE 5 typedef struct { struct action* a; int a_time; } action_time; extern action_time longest_action[LONGEST_ACTION_SIZE]; extern int min_action_time; int do_action(struct action* a, struct sip_msg* msg); int run_top_route(struct action* a, struct sip_msg* msg); int run_action_list(struct action* a, struct sip_msg* msg); void run_error_route(struct sip_msg* msg, int force_reset); #define script_trace(class, action, msg, file, line) \ do { \ if (use_script_trace) \ __script_trace(class, action, msg, file, line); \ } while (0) void __script_trace(char *class, char *action, struct sip_msg *msg, char *file, int line); #endif opensips-2.2.2/async.c000066400000000000000000000024221300170765700146440ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2014-10-15 created (bogdan) */ #include "dprint.h" #include "async.h" int async_status = ASYNC_NO_IO; async_start_function *async_start_f = NULL; async_resume_function *async_resume_f = NULL; int register_async_handlers(async_start_function *f1, async_resume_function *f2) { if (async_start_f) { LM_ERR("aync handler already registered, it cannot be override\n"); return -1; } async_start_f = f1; async_resume_f = f2; return 0; } opensips-2.2.2/async.h000066400000000000000000000043551300170765700146600ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2014-10-15 created (bogdan) */ #ifndef _ASYNC_H_ #define _ASYNC_H_ #include "route_struct.h" #include "parser/msg_parser.h" /* The possible values of the status of async operatations (as reported by * module functions, at start and resume) * NOTE: all values in this enum must be negative */ enum async_ret_code { ASYNC_NO_IO = -6, ASYNC_SYNC, ASYNC_CONTINUE, ASYNC_CHANGE_FD, ASYNC_DONE_CLOSE_FD, ASYNC_DONE, }; extern int async_status; /* function to handle script function in async mode. Input: the sip message, the function/action (MODULE_T) and the ID of the resume route (where to continue after the I/O is done). Output: 0 if the async call was successfully done and script execution must be terminated. -1 some error happened and the async call did not happened. */ /* internal used functions to start (from script) and * to continue (from reactor) async I/O ops */ typedef int (async_start_function) (struct sip_msg *msg, struct action* a , int resume_route); typedef int (async_resume_function) (int *fd, void *param); extern async_start_function *async_start_f; extern async_resume_function *async_resume_f; int register_async_handlers(async_start_function *f1, async_resume_function *f2); /* async related functions to be used by the * functions exported by modules */ typedef int (async_resume_module) (int fd, struct sip_msg *msg, void *param); #endif opensips-2.2.2/atomic.h000066400000000000000000000107751300170765700150220ustar00rootroot00000000000000/* * Copyright (C) 2006 kernel.org * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /*! * \file * \brief Assembler routines for atomic operations */ #ifndef _ATOMIC_OPS_H_ #define _ATOMIC_OPS_H_ /************************* i386 & x86_64 ARCH ****************************/ #if defined(__CPU_i386) || defined(__CPU_x86_64) #if defined(__SMP_yes) #define LOCK "lock ; " #else #define LOCK "" #endif #endif #if defined(__CPU_i386) /*! \brief * atomic_set - set atomic variable * @v: pointer of type atomic_t * @i: required value * * Atomically sets the value of @v to @i. */ #define atomic_set(v,i) (((v)->counter) = (i)) /*! \brief * Make sure gcc doesn't try to be clever and move things around * on us. We need to use _exactly_ the address the user gave us, * not some alias that contains the same information. */ typedef struct { volatile unsigned int counter; } atomic_t; /*! \brief * atomic_add - add integer to atomic variable * @i: integer value to add * @v: pointer of type atomic_t * * Atomically adds @i to @v. */ static __inline__ void atomic_add(int i, atomic_t *v) { __asm__ __volatile__( LOCK "addl %1,%0" :"=m" (v->counter) :"ir" (i), "m" (v->counter)); } /*! \brief * atomic_sub - subtract the atomic variable * @i: integer value to subtract * @v: pointer of type atomic_t * * Atomically subtracts @i from @v. */ static __inline__ void atomic_sub(int i, atomic_t *v) { __asm__ __volatile__( LOCK "subl %1,%0" :"=m" (v->counter) :"ir" (i), "m" (v->counter)); } /*! \brief * atomic_inc - increment atomic variable * @v: pointer of type atomic_t * * Atomically increments @v by 1. */ static __inline__ void atomic_inc(atomic_t *v) { __asm__ __volatile__( LOCK "incl %0" :"=m" (v->counter) :"m" (v->counter)); } /*! \brief * atomic_dec - decrement atomic variable * @v: pointer of type atomic_t * * Atomically decrements @v by 1. */ static __inline__ void atomic_dec(atomic_t *v) { __asm__ __volatile__( LOCK "decl %0" :"=m" (v->counter) :"m" (v->counter)); } #undef NO_ATOMIC_OPS #elif defined(__CPU_x86_64) /* __CPU_i386 */ /*! \brief * Make sure gcc doesn't try to be clever and move things around * on us. We need to use _exactly_ the address the user gave us, * not some alias that contains the same information. */ typedef struct { volatile unsigned long counter; } atomic_t; /*! \brief * atomic_set - set atomic variable * @v: pointer of type atomic_t * @i: required value * * Atomically sets the value of @v to @i. */ #define atomic_set(v,i) (((v)->counter) = (i)) /*! \brief * atomic_add - add integer to atomic variable * @i: integer value to add * @v: pointer of type atomic_t * * Atomically adds @i to @v. */ static __inline__ void atomic_add(unsigned long i, atomic_t *v) { __asm__ __volatile__( LOCK "addq %1,%0" :"=m" (v->counter) :"er" (i), "m" (v->counter)); } /*! \brief * atomic_sub - subtract the atomic variable * @i: integer value to subtract * @v: pointer of type atomic_t * * Atomically subtracts @i from @v. */ static __inline__ void atomic_sub(unsigned long i, atomic_t *v) { __asm__ __volatile__( LOCK "subq %1,%0" :"=m" (v->counter) :"er" (i), "m" (v->counter)); } /*! \brief * atomic_inc - increment atomic variable * @v: pointer of type atomic_t * * Atomically increments @v by 1. */ static __inline__ void atomic_inc(atomic_t *v) { __asm__ __volatile__( LOCK "incq %0" :"=m" (v->counter) :"m" (v->counter)); } /*! \brief * atomic_dec - decrement atomic variable * @v: pointer of type atomic_t * * Atomically decrements @v by 1. */ static __inline__ void atomic_dec(atomic_t *v) { __asm__ __volatile__( LOCK "decq %0" :"=m" (v->counter) :"m" (v->counter)); } #undef NO_ATOMIC_OPS /************************* other ARCH ****************************/ #else #define NO_ATOMIC_OPS #endif #endif opensips-2.2.2/bdb_recover.8000066400000000000000000000057311300170765700157360ustar00rootroot00000000000000.\" $Id: osipsconsole.8 5891 2009-11-22 12:53:09Z alerios $ .TH bdb_recover 8 22.11.2009 opensips-berkeley-module "OpenSIPS" .\" Process with .\" groff -man -Tascii bdb_recover.8 .\" .SH NAME bdb_recover \- utility for recovering OpenSIPS db_berkeley files .SH SYNOPSIS .B bdb_recover [ .BI parameters ] .SH DESCRIPTION .B bdb_recover is an utility to recover data from db_berkeley files created by .B OpenSIPS SIP server The db_berkeley module uses the Concurrent Data Store (CDS) architecture. As such, no transaction or journaling is provided by the DB natively. The application bdb_recover is specifically written to recover data from journal files that OpenSIPS creates. The bdb_recover application requires an additional text file that contains the table schema. The schema is loaded with the '\-s' option and is required for all operations. Provide the path to the db_berkeley plain-text schema files. By default, these install to '/usr/local/share/opensips/db_berkeley/opensips/'. The '\-h' home option is the DB_PATH path. Unlike the Berkeley utilities, this application does not look for the DB_PATH environment variable, so you have to specify it. If not specified, it will assume the current working directory. The last argument is the operation. There are fundamentally only two operations - create and recover. .SH FILES .PD 0 .I /usr/local/share/opensips/db_berkeley/opensips/ .SH USAGE .TP 12 .B bdb_recover \-s schemadir [\-h home] [\-c tablename] This will create a brand new DB file with metadata. .TP .B bdb_recover \-s schemadir [\-h home] [\-C all] This will create all the core tables, each with metadata. .TP .B bdb_recover \-s schemadir [\-h home] [\-r journal-file] This will rebuild a DB and populate it with operation from journal-file. The table name is embedded in the journal-file name by convention. .TP .B bdb_recover \-s schemadir [\-h home] [\-R lastN] This will iterate over all core tables enumerated. If journal files exist in 'home', a new DB file will be created and populated with the data found in the last N files. The files are 'replayed' in chronological order (oldest to newest). This allows the administrator to rebuild the db with a subset of all possible operations if needed. For example, you may only be interested in the last hours data in table location. .SH NOTES .PP A corrupted DB file must be moved out of the way before bdb_recover is executed. .SH AUTHORS see .B /usr/share/doc/opensips/AUTHORS .PP This manual page was written by Alejandro Rios P. , based on db_berkeley module's README by Will Quan Copyright (C) 2007 Cisco Systems, for the Debian project (and may be used by others). .SH SEE ALSO .BR opensips(8) .PP Full documentation on opensips db_berkeley module is available at /usr/share/doc/opensips-berkeley-module/README.db_berkeley and .I http://www.opensips.org/. .PP Mailing lists: .nf users@opensips.org - opensips user community .nf devel@opensips.org - opensips development, new features and unstable version opensips-2.2.2/bin_interface.c000066400000000000000000000172051300170765700163240ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-04-10: Created (Liviu) */ #include "bin_interface.h" #include "config.h" #include "daemonize.h" #include "pt.h" #include "net/net_udp.h" struct socket_info *bin; static char *send_buffer; static char *cpos; static char *rcv_buf; static char *rcv_end; static struct packet_cb_list *reg_modules; /** * bin_init - begins the construction of a new binary packet (header part): * * +-------------------+-----------------------------------------------------------------+ * | 12-byte HEADER | BODY max 65535 bytes | * +-------------------+-----------------------------------------------------------------+ * | PK_MARKER |PGK LEN| Version | LEN | MOD_NAME | CMD | LEN | FIELD | LEN | FIELD |....| * +-------------------+-----------------------------------------------------------------+ * * @param: { LEN, MOD_NAME } + CMD */ short get_bin_pkg_version(void) { return *(short *)(rcv_buf + BIN_PACKET_MARKER_SIZE + PKG_LEN_FIELD_SIZE); } void set_len(char *send_buffer, char *cpos){ unsigned short len = cpos - send_buffer, *px; px = (unsigned short *) (send_buffer + BIN_PACKET_MARKER_SIZE); *px = len; } int bin_init(str *mod_name, int cmd_type, short version) { if (!send_buffer) { send_buffer = pkg_malloc(BUF_SIZE); if (!send_buffer) { LM_ERR("No more pkg memory!\n"); return -1; } } /* binary packet header: marker + pkg_len */ memcpy(send_buffer, BIN_PACKET_MARKER, BIN_PACKET_MARKER_SIZE); cpos = send_buffer + BIN_PACKET_MARKER_SIZE + PKG_LEN_FIELD_SIZE; /* bin version */ memcpy(cpos, &version, sizeof(version)); cpos += VERSION_FIELD_SIZE; /* module name */ memcpy(cpos, &mod_name->len, LEN_FIELD_SIZE); cpos += LEN_FIELD_SIZE; memcpy(cpos, mod_name->s, mod_name->len); cpos += mod_name->len; memcpy(cpos, &cmd_type, sizeof(cmd_type)); cpos += sizeof(cmd_type); set_len(send_buffer, cpos); return 0; } /* * copies the given string at the 'cpos' position in the buffer * allows null strings (NULL content or NULL param) * * @return: * > 0: success, the size of the buffer * < 0: internal buffer limit reached */ int bin_push_str(const str *info) { if (!cpos || (cpos - send_buffer + LEN_FIELD_SIZE + (info ? info->len : 0)) > BUF_SIZE) return -1; if (!info || info->len == 0 || !info->s) { memset(cpos, 0, LEN_FIELD_SIZE); cpos += LEN_FIELD_SIZE; return (int)LEN_FIELD_SIZE; } memcpy(cpos, &info->len, LEN_FIELD_SIZE); cpos += LEN_FIELD_SIZE; memcpy(cpos, info->s, info->len); cpos += info->len; set_len(send_buffer, cpos); return (int)(cpos - send_buffer); } /* * adds a new integer value at the 'cpos' position in the buffer * * @return: * > 0: success, the size of the buffer * < 0: internal buffer limit reached */ int bin_push_int(int info) { if (!cpos || (cpos + sizeof(info) - send_buffer) > BUF_SIZE) return -1; memcpy(cpos, &info, sizeof(info)); cpos += sizeof(info); set_len(send_buffer, cpos); return (int)(cpos - send_buffer); } int bin_get_buffer(str *buffer) { if (!buffer) return -1; buffer->s = send_buffer; buffer->len = bin_send_size; return 1; } /* * skips @count integers from the current position in the received binary packet * * @return: * >= 0: success, number of skipped bytes * < 0: error, buffer limit reached */ int bin_skip_int(int count) { int i; char *in = cpos; for (i = 0; i < count; i++) { if (cpos + LEN_FIELD_SIZE > rcv_end) { LM_ERR("Receive binary packet buffer overflow"); return -1; } cpos += LEN_FIELD_SIZE; } return (int)(cpos - in); } /* * skips @count strings from the current position in a received binary packet * * @return: * >= 0: success, number of skipped bytes * < 0: error, buffer limit reached */ int bin_skip_str(int count) { int i, len; char *in = cpos; for (i = 0; i < count; i++) { if (cpos + LEN_FIELD_SIZE > rcv_end) goto error; memcpy(&len, cpos, LEN_FIELD_SIZE); cpos += LEN_FIELD_SIZE; if (cpos + len > rcv_end) goto error; cpos += len; } return (int)(cpos - in); error: LM_ERR("Receive binary packet buffer overflow"); return -1; } /* * pops an str from the current position in the buffer * @info: pointer to store the result * * @return: * 0 (success): info retrieved * 1 (success): nothing returned, all data has been consumed! * < 0: error * * Note: The pointer returned in @info str is only valid for the duration of * the callback. Don't forget to copy the info into a safe buffer! */ int bin_pop_str(str *info) { if (cpos == rcv_end) return 1; if (cpos + LEN_FIELD_SIZE > rcv_end) goto error; memcpy(&info->len, cpos, LEN_FIELD_SIZE); cpos += LEN_FIELD_SIZE; if (cpos + info->len > rcv_end) goto error; if (info->len == 0) info->s = NULL; else info->s = cpos; cpos += info->len; LM_DBG("Popped: '%.*s' [%d]\n", info->len, info->s, info->len); return 0; error: LM_ERR("Receive binary packet buffer overflow"); return -1; } /* * pops an integer value from the current position in the buffer * @info: pointer to store the result * * @return: * 0 (success): info retrieved * 1 (success): nothing returned, all data has been consumed! * < 0: error */ int bin_pop_int(void *info) { if (cpos == rcv_end) return 1; if (cpos + sizeof(int) > rcv_end) { LM_ERR("Receive binary packet buffer overflow"); return -1; } memcpy(info, cpos, sizeof(int)); cpos += sizeof(int); return 0; } /** * bin_register_cb - registers a module handler for specific packets * @mod_name: used to classify the incoming packets * @cb: the handler function, called once for each matched packet * * @return: 0 on success */ int bin_register_cb(char *mod_name, void (*cb)(int, struct receive_info *, void * atr), void *att) { struct packet_cb_list *new_mod; new_mod = pkg_malloc(sizeof(*new_mod)); if (!new_mod) { LM_ERR("No more pkg mem!\n"); return -1; } memset(new_mod, 0, sizeof(*new_mod)); new_mod->cbf = cb; new_mod->module.len = strlen(mod_name); new_mod->module.s = mod_name; new_mod->att = att; new_mod->next = reg_modules; reg_modules = new_mod; return 0; } /* * main binary packet UDP receiver loop */ void call_callbacks(char* buffer, struct receive_info *rcv){ str name; struct packet_cb_list *p; rcv_buf = buffer; get_name(rcv_buf, name); rcv_end = rcv_buf + *(unsigned short*)(buffer + BIN_PACKET_MARKER_SIZE); cpos = name.s + name.len + CMD_FIELD_SIZE; /* packet will be now processed by a specific module */ for (p = reg_modules; p; p = p->next) { if (p->module.len == name.len && memcmp(name.s, p->module.s, name.len) == 0) { LM_DBG("binary Packet CMD: %d. Module: %.*s\n", bin_rcv_type, name.len, name.s); p->cbf(bin_rcv_type, rcv,p->att); break; } } } /* * called in the OpenSIPS initialization phase by the main process. * forks the binary packet UDP receivers. * * @return: 0 on success */ opensips-2.2.2/bin_interface.h000066400000000000000000000111221300170765700163210ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-04-10: Created (Liviu) */ #ifndef __BINARY_INTERFACE__ #define __BINARY_INTERFACE__ #include "ip_addr.h" #include "crc.h" #define BIN_PACKET_MARKER "P4CK" #define BIN_PACKET_MARKER_SIZE 4 #define PKG_LEN_FIELD_SIZE 4 #define VERSION_FIELD_SIZE 4 #define LEN_FIELD_SIZE sizeof(int) #define CMD_FIELD_SIZE sizeof(int) #define HEADER_SIZE (BIN_PACKET_MARKER_SIZE + PKG_LEN_FIELD_SIZE + VERSION_FIELD_SIZE) #define MIN_BIN_PACKET_SIZE \ (HEADER_SIZE + LEN_FIELD_SIZE + CMD_FIELD_SIZE + 2) /* e.g. >tm<.so */ #define bin_send_type \ (*(int *)(send_buffer + HEADER_SIZE + LEN_FIELD_SIZE + \ *(int *)(send_buffer + HEADER_SIZE))) #define bin_rcv_type \ (*(int *)(rcv_buf + HEADER_SIZE + LEN_FIELD_SIZE + \ *(int *)(rcv_buf + HEADER_SIZE))) #define bin_send_size ((int)(cpos - send_buffer)) #define bin_rcv_size ((int)(rcv_end - rcv_buf)) #define is_valid_bin_packet(_p) \ (memcmp(_p, BIN_PACKET_MARKER, BIN_PACKET_MARKER_SIZE) == 0) #define get_name(_p, name) \ do { \ name.len = *(int *)(_p + HEADER_SIZE); \ name.s = _p + HEADER_SIZE + LEN_FIELD_SIZE; \ } while (0) struct packet_cb_list { str module; /* registered module */ void (*cbf)(int packet_type, /* module callback */ struct receive_info *ri, void *att); void *att; struct packet_cb_list *next; }; /** returns the version of the bin protocol from the received message */ short get_bin_pkg_version(void); /** calls all the registered functions @buffer: buffer containing a complete bin message @rcv: information about the sender of the message */ void call_callbacks(char* buffer, struct receive_info *rcv); /* * registers a callback function to be triggered on a received * binary packet marked with the @mod_name module name */ int bin_register_cb(char *mod_name, void (*)(int packet_type, struct receive_info *ri, void * atr), void *att); /** * first function called when building a binary packet * * @mod_name: module specific string * @cmd_type: module specific identifier for this new packet * * @return: 0 on success */ int bin_init(str *mod_name, int packet_type, short version); /* * adds a new string value to the packet being currently built * @info: may also be NULL * * @return: * > 0: success, the size of the buffer * < 0: internal buffer limit reached */ int bin_push_str(const str *info); /* * adds a new integer value to the packet being currently built * * @return: * > 0: success, the size of the buffer * < 0: internal buffer limit reached */ int bin_push_int(int info); /* TODO - comment, lol */ int bin_get_buffer(str *buffer); /* * pops a str structure from a received binary packet * @info: pointer to store the result * * @return: * 0 (success): info retrieved * 1 (success): nothing returned, all data has been consumed! * < 0: error * * Note: The pointer returned in @info is only valid for the duration of * the callback. Don't forget to copy the data into a safe buffer! * * Note2: Information is retrieved in the same order it was stored */ int bin_pop_str(str *info); /* * pops an integer from a received binary packet * @info: pointer to store the result * * @return: * 0 (success): info retrieved * 1 (success): nothing returned, all data has been consumed! * < 0: error * * Note: Information is retrieved in the same order it was stored */ int bin_pop_int(void *info); /* * skips @count integers from a received binary packet * * @return: * >= 0: success, number of skipped bytes * < 0: error, buffer limit reached */ int bin_skip_int(int count); /* * skips @count strings from a received binary packet * * @return: * >= 0: success, number of skipped bytes * < 0: error, buffer limit reached */ int bin_skip_str(int count); #endif /* __BINARY_INTERFACE__ */ opensips-2.2.2/blacklists.c000066400000000000000000000373551300170765700156770ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /*! * \file * \brief OpenSIPS Blacklist functions */ #include #include #include #include #include #include #include "mem/mem.h" #include "mem/shm_mem.h" #include "mi/mi.h" #include "dprint.h" #include "blacklists.h" #include "context.h" #include "timer.h" #include "ut.h" static struct bl_head *blst_heads; static unsigned int bl_default_marker; static unsigned int max_heads = 8 * sizeof(int); static unsigned int used_heads; static int bl_ctx_idx = -1; static void delete_expired_routine(unsigned int ticks, void *param); static struct mi_root *mi_print_blacklists(struct mi_root *cmd, void *param); static mi_export_t mi_bl_cmds[] = { { "list_blacklists", "lists all the defined (static or learned) blacklists", mi_print_blacklists, MI_NO_INPUT_FLAG , 0, 0 }, { 0, 0, 0, 0, 0, 0} }; int init_black_lists(void) { bl_ctx_idx = context_register_int(CONTEXT_GLOBAL, NULL); if (bl_ctx_idx < 0) return -1; /* register timer routine */ if (register_timer("blcore-expire", delete_expired_routine, 0, 1, TIMER_FLAG_SKIP_ON_DELAY) < 0) { LM_ERR("failed to register timer\n"); return -1; } /* register MI commands */ if (register_mi_mod("blacklists", mi_bl_cmds) < 0) { LM_ERR("unable to register MI cmds\n"); return -1; } return 0; } /* * get_bl_marker() and store_bl_marker(): * easy manipulation of the blacklist bitmask stored in global context */ static int get_bl_marker(unsigned int *marker) { if (!current_processing_ctx) return -1; if (marker) *marker = (unsigned int)context_get_int( CONTEXT_GLOBAL, current_processing_ctx, bl_ctx_idx); return 0; } #define store_bl_marker(value) \ (context_put_int( \ CONTEXT_GLOBAL, current_processing_ctx, bl_ctx_idx, value)) struct bl_head *create_bl_head(int owner, int flags, struct bl_rule *head, struct bl_rule *tail, str *name) { unsigned int i; if (!blst_heads) { blst_heads = shm_malloc(max_heads * sizeof *blst_heads); if (!blst_heads) { LM_ERR("no more shared memory!\n"); return NULL; } memset(blst_heads, 0, max_heads * sizeof *blst_heads); } i = used_heads; if (i == max_heads) { LM_ERR("too many lists\n"); return NULL; } if (get_bl_head_by_name(name)) { LM_CRIT("duplicated name!\n"); return NULL; } if (flags & BL_READONLY_LIST && flags & BL_DO_EXPIRE) { LM_CRIT("RO lists cannot accept EXPIRES!\n"); return NULL; } /* copy list name */ blst_heads[i].name.s = shm_malloc(name->len + 1); if (!blst_heads[i].name.s) { LM_ERR("no more shm memory!\n"); return NULL; } memcpy(blst_heads[i].name.s, name->s, name->len); blst_heads[i].name.s[name->len] = '\0'; blst_heads[i].name.len = name->len; /* build lock? */ if (!(flags & BL_READONLY_LIST)) { if (!(blst_heads[i].lock = lock_alloc())) { LM_ERR("failed to create lock!\n"); shm_free(blst_heads[i].name.s); return NULL; } if (!lock_init(blst_heads[i].lock)) { LM_ERR("failed to init lock!\n"); shm_free(blst_heads[i].name.s); lock_dealloc(blst_heads[i].lock); return NULL; } } used_heads++; blst_heads[i].owner = owner; blst_heads[i].flags = flags; blst_heads[i].first = head; blst_heads[i].last = tail; if (flags & BL_BY_DEFAULT) bl_default_marker |= (1 << i); return blst_heads + i; } void destroy_black_lists(void) { unsigned int i; struct bl_rule *p, *q; for (i = 0; i < used_heads; i++) { if (blst_heads[i].lock) { lock_destroy(blst_heads[i].lock); lock_dealloc(blst_heads[i].lock); } for (p = blst_heads[i].first; p; ) { q = p; p = p->next; shm_free(q); } if (blst_heads[i].name.s) shm_free(blst_heads[i].name.s); blst_heads[i].first = blst_heads[i].last = NULL; } if (blst_heads) shm_free(blst_heads); } static inline void delete_expired(struct bl_head *elem, unsigned int ticks) { struct bl_rule *p, *q; p = q = 0; /* get list for write */ lock_get(elem->lock); while (elem->count_write){ lock_release(elem->lock); sleep_us(5); lock_get(elem->lock); } elem->count_write = 1; while (elem->count_read){ lock_release(elem->lock); sleep_us(5); lock_get(elem->lock); } lock_release(elem->lock); if (!elem->first) goto done; for (q = 0, p = elem->first; p; q = p, p = p->next) if (p->expire_end > ticks) break; if (!q) goto done; /* nothing to remove */ if (!p) { /* remove everything */ q = elem->first; elem->first = elem->last = NULL; } else { /* remove up to p */ q->next = NULL; q = elem->first; elem->first = p; } done: elem->count_write = 0; for (; q; ) { p = q; q = q->next; shm_free(p); } } static void delete_expired_routine(unsigned int ticks, void *param) { unsigned int i; for (i = 0 ; i < used_heads ; i++) if (blst_heads[i].flags&BL_DO_EXPIRE && blst_heads[i].first) delete_expired(blst_heads + i, ticks); } static inline int ip_class_compare(struct net *net1, struct net *net2) { unsigned int r; if (net1->ip.af == net2->ip.af){ /* ipv4 & ipv6 addresses are all multiples of 4 */ for(r=0; rip.len/4; r++) if ((net1->ip.u.addr32[r]&net1->mask.u.addr32[r])!= (net2->ip.u.addr32[r]&net2->mask.u.addr32[r])) return 0; return 1; } return -1; } /*! \brief adds a new rule to a list of rules */ int add_rule_to_list(struct bl_rule **first, struct bl_rule **last, struct net *ip_net, str *body, unsigned short port, unsigned short proto, int flags) { struct bl_rule *p; struct bl_rule *q; if (!first || !last || !ip_net){ LM_ERR("wrong input parameter format\n"); return -1; } if (body && body->len==0) body = 0; /* is it a duplicate? */ for (q = *first; q; q = q->next) { if ( (flags==q->flags) && (port==q->port) && (proto==q->proto) && (ip_class_compare(ip_net, &q->ip_net)==1) && ((body==NULL && q->body.s==NULL) || (body && q->body.s && (body->len==q->body.len) && !strncmp(body->s,q->body.s,body->len)) ) ) { return 1; } } /* alloc memory */ p = shm_malloc(sizeof *p + (body?(body->len + 1):0)); if (!p) { LM_ERR("no more shm memory!\n"); return -1; } /* fill in the structure */ p->flags = flags; p->ip_net = *ip_net; p->proto = proto; p->port = port; if (body) { p->body.s = (char *)(p + 1); memcpy(p->body.s, body->s, body->len); p->body.s[body->len] = '\0'; p->body.len = body->len; } else { p->body.s = NULL; p->body.len = 0; } p->next = NULL; p->expire_end = 0; /* link the structure */ if (!*first) { *first = *last = p; } else { (*last)->next = p; *last = p; } return 0; } static inline void rm_dups(struct bl_head *head, struct bl_rule **first, struct bl_rule **last) { struct bl_rule *p, *q; struct bl_rule *r; for( p=0,q=*first ; q ; ) { for( r=head->first; r ; r = r->next) { if ( (r->flags==q->flags) && (r->port==q->port) && (r->proto==q->proto) && (ip_class_compare(&r->ip_net, &q->ip_net)==1) && ((!r->body.s && !q->body.s) || ((r->body.len==q->body.len) && r->body.s!=NULL && q->body.s!=NULL && !strncmp(r->body.s,q->body.s,q->body.len)) ) ) { break; } } if (r) { /* q duplicates r -> free q */ if (q->next==NULL) *last=p; if (p) { p->next = q->next; shm_free(q); q = p->next; } else { *first = q->next; shm_free(q); q = *first; } } else { p=q; q=q->next; } } } static inline int reload_permanent_list(struct bl_rule *first, struct bl_rule *last, struct bl_head *head) { struct bl_rule *p, *q; /* get list for write */ lock_get( head->lock); while(head->count_write){ lock_release( head->lock ); sleep_us(5); lock_get( head->lock ); } head->count_write = 1; while(head->count_read){ lock_release( head->lock ); sleep_us(5); lock_get( head->lock ); } lock_release( head->lock ); for(p = head->first ; p ; ){ q = p; p = p->next; shm_free(q); } head->first = first; head->last = last; head->count_write = 0; return 0; } /* should NOT add ANY DUPLICATES */ int add_list_to_head(struct bl_head *head, struct bl_rule *first, struct bl_rule *last, int truncate, int expire_limit) { struct bl_rule *p; unsigned int expire_end = 0; if (!head || !first || !last) return -1; /* may I add to this list? */ if (head->flags & BL_READONLY_LIST) { LM_CRIT("list is readonly!!!\n"); return -1; } LM_DBG("adding to bl %.*s %p,%p\n", head->name.len, head->name.s, first, last); /* for expiring lists, sets the timeout */ if (head->flags & BL_DO_EXPIRE) { if (expire_limit==0) { LM_CRIT("expire is zero!!!\n"); return -1; } expire_end = get_ticks() + expire_limit; for (p = first; p; p = p->next) p->expire_end = expire_end; } /* truncate? -> just do reload */ if (truncate) return reload_permanent_list( first, last, head); /* get list for write */ lock_get(head->lock); while (head->count_write){ lock_release(head->lock); sleep_us(5); lock_get(head->lock); } head->count_write = 1; while (head->count_read){ lock_release(head->lock); sleep_us(5); lock_get(head->lock); } lock_release(head->lock); rm_dups(head, &first, &last); if (!first) goto done; if (!head->first) { head->last = last; head->first = first; } else if (!(head->flags & BL_DO_EXPIRE)) { head->last->next = first; head->last = last; } else if (head->first->expire_end >= expire_end) { last->next = head->first; head->first = first; } else if (head->last->expire_end <= expire_end) { head->last->next = first; head->last = last; } else { for (p = head->first; ; p = p->next) if (p->next->expire_end >= expire_end) break; last->next = p->next; p->next = first; } done: head->count_write = 0; return 0; } struct bl_head *get_bl_head_by_name(str *name) { unsigned int i; for (i = 0; i < used_heads; i++) if ((name->len == blst_heads[i].name.len) && !strncmp(name->s, blst_heads[i].name.s, name->len)) return blst_heads + i; return NULL; } int mark_for_search(struct bl_head *list, unsigned int set) { unsigned int n; unsigned int bl_marker; if (get_bl_marker(&bl_marker) != 0) return 1; /* is it an "all" operation? */ if (!list) { store_bl_marker(set ? (unsigned int)-1 : 0); return 0; } n = list - blst_heads; if (list < blst_heads || n >= used_heads) return 1; if (set) store_bl_marker(bl_marker | (1 << n)); else store_bl_marker(bl_marker & ~(1 << n)); return 0; } /* * If possible, reset the bitmask stored in the current global context */ void reset_bl_markers(void) { if (get_bl_marker(NULL) == 0) store_bl_marker(bl_default_marker); } static inline int check_against_rule_list(struct ip_addr *ip, str *text, unsigned short port, unsigned short proto, int i) { struct bl_rule *p; int t_val; int ret = 0; LM_DBG("using list %.*s \n", blst_heads[i].name.len, blst_heads[i].name.s); if( !blst_heads[i].flags&BL_READONLY_LIST ) { /* get list for read */ lock_get( blst_heads[i].lock ); while(blst_heads[i].count_write) { lock_release( blst_heads[i].lock ); sleep_us(5); lock_get( blst_heads[i].lock ); } blst_heads[i].count_read++; lock_release(blst_heads[i].lock); } for(p = blst_heads[i].first ; p ; p = p->next) { t_val = (p->port==0 || p->port==port) && (p->proto==PROTO_NONE || p->proto==proto) && (matchnet(ip, &(p->ip_net)) == 1) && (p->body.s==NULL || !fnmatch(p->body.s, text->s, 0)); if(!!(p->flags & BLR_APPLY_CONTRARY) ^ !!(t_val)){ ret = 1; LM_DBG("matched list %.*s \n", blst_heads[i].name.len,blst_heads[i].name.s); break; } } if( !blst_heads[i].flags&BL_READONLY_LIST ) { lock_get( blst_heads[i].lock ); blst_heads[i].count_read--; lock_release(blst_heads[i].lock); } return ret; } int check_against_blacklist(struct ip_addr *ip, str *text, unsigned short port, unsigned short proto) { unsigned int i; unsigned int bl_marker; /* no context -> no blacklists at all -> successful check */ if (get_bl_marker(&bl_marker) != 0) return 0; for (i = 0; i < used_heads; i++) if (bl_marker & (1 << i) && check_against_rule_list(ip, text, port, proto, i)) return 1; return 0; } static struct mi_root* mi_print_blacklists(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct mi_node *rpl; struct mi_node *node; struct mi_node *node1; struct mi_node *node2; struct mi_attr *attr; unsigned int i; struct bl_rule *blr; char *p; int len; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return 0; rpl = &rpl_tree->node; for ( i=0 ; inext) { /* add a rule node */ node1 = add_mi_node_child( node, 0, "Rule", 4, 0, 0 ); if (node1==0) goto error; /* add attributes to the rule node */ p= int2str((unsigned long)blr->flags, &len); attr = add_mi_attr( node1, MI_DUP_VALUE, "flags", 5, p, len); if (attr==0) goto error; /* add to rule node */ p = ip_addr2a(&blr->ip_net.ip); len = p?strlen(p):0; node2 = add_mi_node_child( node1, MI_DUP_VALUE, "IP", 2, p, len); if (node2==0) goto error; p = ip_addr2a(&blr->ip_net.mask); len = p?strlen(p):0; node2 = add_mi_node_child( node1, MI_DUP_VALUE, "Mask", 4, p, len); if (node2==0) goto error; p= int2str((unsigned long)blr->proto, &len); node2 = add_mi_node_child( node1, MI_DUP_VALUE, "Proto", 5, p,len); if (node2==0) goto error; p= int2str((unsigned long)blr->port, &len); node2 = add_mi_node_child( node1, MI_DUP_VALUE, "Port", 4, p,len); if (node2==0) goto error; if (blr->body.s) { node2 = add_mi_node_child( node1, MI_DUP_VALUE, "Match", 5, blr->body.s, blr->body.len); if (node2==0) goto error; } if (blst_heads[i].flags&BL_DO_EXPIRE) { p= int2str((unsigned long)blr->expire_end, &len); node2 = add_mi_node_child( node1, MI_DUP_VALUE, "Expire", 6, p, len); if (node2==0) goto error; } } if( !blst_heads[i].flags&BL_READONLY_LIST ) { lock_get( blst_heads[i].lock ); blst_heads[i].count_read--; lock_release(blst_heads[i].lock); } } return rpl_tree; error: if( !blst_heads[i].flags&BL_READONLY_LIST ) { lock_get( blst_heads[i].lock ); blst_heads[i].count_read--; lock_release(blst_heads[i].lock); } free_mi_tree(rpl_tree); return 0; } opensips-2.2.2/blacklists.h000066400000000000000000000050611300170765700156710ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /*! * \file * \brief OpenSIPS Blacklist functions */ #ifndef _BLACKLISTS_H_ #define _BLACKLISTS_H_ #include "ip_addr.h" #include "str.h" #include "locking.h" #define BL_READONLY_LIST (1<<0) #define BL_DO_EXPIRE (1<<1) #define BL_BY_DEFAULT (1<<2) #define BLR_APPLY_CONTRARY (1<<0) struct bl_rule{ int flags; struct net ip_net; unsigned short port; unsigned short proto; str body; struct bl_rule *next; unsigned int expire_end; }; struct bl_head{ str name; int owner; /*!< the id of the module that owns the set of rules */ int flags; gen_lock_t *lock; int count_write; int count_read; /* ... more fields, maybe ... */ struct bl_rule *first; struct bl_rule *last; }; #define BL_CORE_ID 13 int init_black_lists(); void destroy_black_lists(); struct bl_head *create_bl_head(int owner, int flags, struct bl_rule *head, struct bl_rule *tail, str *name); int add_rule_to_list(struct bl_rule **first, struct bl_rule **last, struct net *ip_net, str *body, unsigned short port, unsigned short proto, int flags); int add_list_to_head(struct bl_head *elem, struct bl_rule *first, struct bl_rule *last, int truncate, int expire_limit); struct bl_head *get_bl_head_by_name(str *name); int mark_for_search(struct bl_head *list, unsigned int set); void reset_bl_markers(); int check_against_blacklist(struct ip_addr *ip, str *text, unsigned short port, unsigned short proto); static inline int check_blacklists( unsigned short proto, union sockaddr_union *to, char *body_s, int body_len) { str body; struct ip_addr ip; unsigned short port; body.s = body_s; body.len = body_len; su2ip_addr(&ip, to); port = su_getport(to); return check_against_blacklist(&ip, &body, port, proto); } #endif /* _BLACKLST_H */ opensips-2.2.2/cachedb/000077500000000000000000000000001300170765700147345ustar00rootroot00000000000000opensips-2.2.2/cachedb/cachedb.c000066400000000000000000000413021300170765700164510ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #include "cachedb.h" #include "cachedb_cap.h" #include "../dprint.h" #include "../sr_module.h" #include "../mem/mem.h" #include "../mem/meminfo.h" #include "../str.h" #include #include struct cachedb_engine_t { cachedb_engine cde; struct cachedb_engine_t* next; }; int cachedb_store_url(struct cachedb_url **list,char *val) { struct cachedb_url *new,*it; int len; len = strlen(val); new = pkg_malloc(sizeof(struct cachedb_url) + len); if (new == NULL) { LM_ERR("no more pkg\n"); return -1; } memset(new,0,sizeof(struct cachedb_url) + len); new->url.len = len; new->url.s = (char *)(new + 1); memcpy(new->url.s,val,len); if (*list == NULL) *list = new; else { for (it=*list;it->next;it=it->next); it->next = new; } return 0; } void cachedb_free_url(struct cachedb_url *list) { struct cachedb_url *it=list,*aux; while (it){ aux = it->next; pkg_free(it); it=aux; } } static struct cachedb_engine_t* cachedb_list = NULL; static inline cachedb_engine* lookup_cachedb(str *name) { struct cachedb_engine_t* cde_node; cde_node = cachedb_list; while(cde_node) { if (name->len == cde_node->cde.name.len && strncmp(name->s, cde_node->cde.name.s, name->len) == 0) return &cde_node->cde; cde_node = cde_node->next; } return 0; } int cachedb_bind_mod(str *url,cachedb_funcs *funcs) { char *mod_name,*grp_name; int len; str cachedb_name; cachedb_engine *cde; if (url == NULL || url->s == NULL || funcs == NULL) { LM_ERR("NULL parameter provided\n"); return -1; } memset(funcs,0,sizeof(cachedb_funcs)); mod_name = memchr(url->s,':',url->len); if (mod_name == NULL) { LM_ERR("cannot extract cachedb type\n"); return -1; } len = mod_name - url->s; cachedb_name.len = len; cachedb_name.s = url->s; /* no point in giving here the grp_name, but for the sake of uniform * cachedb_urls in modules and for script, take in into account * the presence of grp here too, and skip it */ grp_name=memchr(cachedb_name.s,':',cachedb_name.len); if (grp_name) cachedb_name.len = grp_name - cachedb_name.s; cde = lookup_cachedb(&cachedb_name); if (cde == NULL) { LM_ERR("failed to bind to [%.*s] module. Is it loaded ?\n", cachedb_name.len,cachedb_name.s); return -1; } LM_DBG("Binded to mod %.*s\n",cachedb_name.len,cachedb_name.s); *funcs = cde->cdb_func; return 0; } int register_cachedb(cachedb_engine* cde_entry) { struct cachedb_engine_t* cde_node; if(cde_entry == NULL) { LM_ERR("null argument\n"); return -1; } if (lookup_cachedb( &cde_entry->name)) { LM_ERR("cachedb system <%.*s> already registered\n", cde_entry->name.len, cde_entry->name.s); return -1; } cde_node = (struct cachedb_engine_t*)pkg_malloc( sizeof(struct cachedb_engine_t) + cde_entry->name.len); if (cde_node== NULL) { LM_ERR("no more pkg memory\n"); return -1; } cde_node->cde.name.s = (char*)cde_node + sizeof(struct cachedb_engine_t); memcpy(cde_node->cde.name.s, cde_entry->name.s, cde_entry->name.len); cde_node->cde.name.len = cde_entry->name.len; cde_node->cde.cdb_func = cde_entry->cdb_func; if (check_cachedb_api(&cde_node->cde) < 0) { LM_ERR("failed to meet api needs\n"); pkg_free(cde_node); return -1; } cde_node->cde.default_connection = NULL; cde_node->cde.connections = NULL; cde_node->next = cachedb_list; cachedb_list = cde_node; LM_DBG("registered cachedb system [%.*s]\n", cde_node->cde.name.len, cde_node->cde.name.s); return 0; } int cachedb_insert_connection(cachedb_engine *cde,cachedb_con *conn) { cachedb_con_list *new,*it; str grp; grp.s = ((cachedb_pool_con *)conn->data)->id->group_name; if (grp.s) grp.len = strlen(grp.s); if (grp.s == NULL || grp.len == 0) { LM_DBG("inserting default script connection\n"); cde->default_connection = conn; return 0; } LM_DBG("inserting grp connection [%.*s]\n",grp.len,grp.s); new = pkg_malloc(sizeof(cachedb_con_list)); if (new == NULL) { LM_ERR("no more pkg\n"); return -1; } memset(new,0,sizeof(cachedb_con_list)); new->connection = conn; new->grp = grp; if (cde->connections == NULL) { cde->connections = new; } else { for (it=cde->connections;it->next;it=it->next); it->next = new; } return 0; } int cachedb_put_connection(str *cachedb_name,cachedb_con *con) { cachedb_engine *cde; cde = lookup_cachedb(cachedb_name); if(cde == NULL) { LM_ERR("Wrong argument <%.*s> - no cachedb system with" " this name registered\n", cachedb_name->len,cachedb_name->s); return -1; } LM_DBG("in cachedb_put_connection %p\n",con); if (cachedb_insert_connection(cde,con) < 0) { LM_ERR("failed to insert new connection\n"); return -1; } return 0; } cachedb_con *cachedb_get_connection(cachedb_engine *cde,str *group_name) { cachedb_con_list *ret; if (cde == NULL) { LM_ERR("no such cachedb engine\n"); return 0; } if (group_name == NULL || group_name->s == NULL || group_name->len == 0) return cde->default_connection; else { for (ret=cde->connections;ret;ret=ret->next) { if (ret->grp.len == group_name->len && memcmp(ret->grp.s,group_name->s,group_name->len) == 0) return ret->connection; } return NULL; } } void cachedb_end_connections(str *cachedb_name) { cachedb_engine *cde; cachedb_con_list *it; cde = lookup_cachedb(cachedb_name); if(cde == NULL) { LM_ERR("Wrong argument <%.*s> - no cachedb system with" " this name registered\n", cachedb_name->len,cachedb_name->s); return; } if (cde->default_connection) cde->cdb_func.destroy(cde->default_connection); for (it=cde->connections;it;it=it->next) cde->cdb_func.destroy(it->connection); } int cachedb_remove(str* cachedb_name, str* attr) { cachedb_engine* cde; str cde_engine,grp_name; char *p; cachedb_con *con; int ret; if(cachedb_name == NULL || attr == NULL) { LM_ERR("null arguments\n"); return -1; } p = memchr(cachedb_name->s,':',cachedb_name->len); if (p == NULL) { cde_engine = *cachedb_name; grp_name.s = NULL; grp_name.len = 0; LM_DBG("from script [%.*s] - no grp\n",cde_engine.len,cde_engine.s); } else { cde_engine.s = cachedb_name->s; cde_engine.len = p - cde_engine.s; grp_name.s = p+1; grp_name.len = cachedb_name->len - cde_engine.len -1; LM_DBG("from script [%.*s] - with grp [%.*s]\n",cde_engine.len, cde_engine.s,grp_name.len,grp_name.s); } cde = lookup_cachedb(&cde_engine); if(cde == NULL) { LM_ERR("Wrong argument <%.*s> - no cachedb system with" " this name registered\n", cde_engine.len,cde_engine.s); return -1; } con = cachedb_get_connection(cde,&grp_name); if (con == NULL) { LM_ERR("failed to get connection for grp name [%.*s]\n", grp_name.len,grp_name.s); return -1; } ret = cde->cdb_func.remove(con,attr); if (ret == 0) ret++; return ret; } int cachedb_store(str* cachedb_name, str* attr, str* val,int expires) { cachedb_engine* cde; str cde_engine,grp_name; char *p; cachedb_con *con; int ret; if(cachedb_name == NULL || attr == NULL || val == NULL) { LM_ERR("null arguments\n"); return -1; } p = memchr(cachedb_name->s,':',cachedb_name->len); if (p == NULL) { cde_engine = *cachedb_name; grp_name.s = NULL; grp_name.len = 0; LM_DBG("from script [%.*s] - no grp\n",cde_engine.len,cde_engine.s); } else { cde_engine.s = cachedb_name->s; cde_engine.len = p - cde_engine.s; grp_name.s = p+1; grp_name.len = cachedb_name->len - cde_engine.len -1; LM_DBG("from script [%.*s] - with grp [%.*s]\n",cde_engine.len, cde_engine.s,grp_name.len,grp_name.s); } cde = lookup_cachedb(&cde_engine); if(cde == NULL) { LM_ERR("Wrong argument <%.*s> - no cachedb system with" " this name registered\n", cde_engine.len,cde_engine.s); return -1; } con = cachedb_get_connection(cde,&grp_name); if (con == NULL) { LM_ERR("failed to get connection for grp name [%.*s]\n", grp_name.len,grp_name.s); return -1; } ret = cde->cdb_func.set(con,attr,val,expires); if (ret ==0) ret++; return ret; } int cachedb_fetch(str* cachedb_name, str* attr, str* val) { cachedb_engine* cde; str cde_engine,grp_name; char *p; cachedb_con *con; int ret; if(cachedb_name == NULL || attr == NULL || val == NULL) { LM_ERR("null arguments\n"); return -1; } p = memchr(cachedb_name->s,':',cachedb_name->len); if (p == NULL) { cde_engine = *cachedb_name; grp_name.s = NULL; grp_name.len = 0; LM_DBG("from script [%.*s] - no grp\n",cde_engine.len,cde_engine.s); } else { cde_engine.s = cachedb_name->s; cde_engine.len = p - cde_engine.s; grp_name.s = p+1; grp_name.len = cachedb_name->len - cde_engine.len -1; LM_DBG("from script [%.*s] - with grp [%.*s]\n",cde_engine.len, cde_engine.s,grp_name.len,grp_name.s); } cde = lookup_cachedb(&cde_engine); if(cde == NULL) { LM_ERR("Wrong argument <%.*s> - no cachedb system with" " this name registered\n", cde_engine.len,cde_engine.s); return -1; } con = cachedb_get_connection(cde,&grp_name); if (con == NULL) { LM_ERR("failed to get connection for grp name [%.*s]\n", grp_name.len,grp_name.s); return -1; } ret = cde->cdb_func.get(con,attr,val); if (ret == 0) ret++; return ret; } int cachedb_counter_fetch(str* cachedb_name, str* attr, int* val) { cachedb_engine* cde; str cde_engine,grp_name; char *p; cachedb_con *con; int ret; if(cachedb_name == NULL || attr == NULL || val == NULL) { LM_ERR("null arguments\n"); return -1; } p = memchr(cachedb_name->s,':',cachedb_name->len); if (p == NULL) { cde_engine = *cachedb_name; grp_name.s = NULL; grp_name.len = 0; LM_DBG("from script [%.*s] - no grp\n",cde_engine.len,cde_engine.s); } else { cde_engine.s = cachedb_name->s; cde_engine.len = p - cde_engine.s; grp_name.s = p+1; grp_name.len = cachedb_name->len - cde_engine.len -1; LM_DBG("from script [%.*s] - with grp [%.*s]\n",cde_engine.len, cde_engine.s,grp_name.len,grp_name.s); } cde = lookup_cachedb(&cde_engine); if(cde == NULL) { LM_ERR("Wrong argument <%.*s> - no cachedb system with" " this name registered\n", cde_engine.len,cde_engine.s); return -1; } con = cachedb_get_connection(cde,&grp_name); if (con == NULL) { LM_ERR("failed to get connection for grp name [%.*s]\n", grp_name.len,grp_name.s); return -1; } ret = cde->cdb_func.get_counter(con,attr,val); if (ret == 0) ret++; return ret; } int cachedb_add(str* cachedb_name, str* attr, int val,int expires,int *new_val) { cachedb_engine* cde; str cde_engine,grp_name; char *p; cachedb_con *con; int ret; if(cachedb_name == NULL || attr == NULL) { LM_ERR("null arguments\n"); return -1; } p = memchr(cachedb_name->s,':',cachedb_name->len); if (p == NULL) { cde_engine = *cachedb_name; grp_name.s = NULL; grp_name.len = 0; LM_DBG("from script [%.*s] - no grp\n",cde_engine.len,cde_engine.s); } else { cde_engine.s = cachedb_name->s; cde_engine.len = p - cde_engine.s; grp_name.s = p+1; grp_name.len = cachedb_name->len - cde_engine.len -1; LM_DBG("from script [%.*s] - with grp [%.*s]\n",cde_engine.len, cde_engine.s,grp_name.len,grp_name.s); } cde = lookup_cachedb(&cde_engine); if(cde == NULL) { LM_ERR("Wrong argument <%.*s> - no cachedb system with" " this name registered\n", cde_engine.len,cde_engine.s); return -1; } if (!CACHEDB_CAPABILITY(&cde->cdb_func,CACHEDB_CAP_ADD)) { LM_ERR("Engine %.*s does not support add ops\n", cde_engine.len,cde_engine.s); return -1; } con = cachedb_get_connection(cde,&grp_name); if (con == NULL) { LM_ERR("failed to get connection for grp name [%.*s]\n", grp_name.len,grp_name.s); return -1; } ret = cde->cdb_func.add(con,attr,val,expires,new_val); if (ret == 0) ret++; return ret; } int cachedb_sub(str* cachedb_name, str* attr, int val,int expires,int *new_val) { cachedb_engine* cde; str cde_engine,grp_name; char *p; cachedb_con *con; int ret; if(cachedb_name == NULL || attr == NULL) { LM_ERR("null arguments\n"); return -1; } p = memchr(cachedb_name->s,':',cachedb_name->len); if (p == NULL) { cde_engine = *cachedb_name; grp_name.s = NULL; grp_name.len = 0; LM_DBG("from script [%.*s] - no grp\n",cde_engine.len,cde_engine.s); } else { cde_engine.s = cachedb_name->s; cde_engine.len = p - cde_engine.s; grp_name.s = p+1; grp_name.len = cachedb_name->len - cde_engine.len -1; LM_DBG("from script [%.*s] - with grp [%.*s]\n",cde_engine.len, cde_engine.s,grp_name.len,grp_name.s); } cde = lookup_cachedb(&cde_engine); if(cde == NULL) { LM_ERR("Wrong argument <%.*s> - no cachedb system with" " this name registered\n", cde_engine.len,cde_engine.s); return -1; } if (!CACHEDB_CAPABILITY(&cde->cdb_func,CACHEDB_CAP_SUB)) { LM_ERR("Engine %.*s does not support sub ops\n", cde_engine.len,cde_engine.s); return -1; } con = cachedb_get_connection(cde,&grp_name); if (con == NULL) { LM_ERR("failed to get connection for grp name [%.*s]\n", grp_name.len,grp_name.s); return -1; } ret = cde->cdb_func.sub(con,attr,val,expires,new_val); if (ret == 0) ret++; return ret; } cachedb_con* cachedb_do_init(str *url,void* (*new_connection)(struct cachedb_id *)) { struct cachedb_id* id; cachedb_con* res; void *con; if (url == NULL || url->s == NULL || new_connection == NULL) { LM_ERR("NULL parameter provided\n"); return 0; } res = pkg_malloc(sizeof(cachedb_con) + url->len); if (res == NULL) { LM_ERR("no more pkg mem\n"); return 0; } id = NULL; memset(res,0,sizeof(cachedb_con) + url->len); res->url.s = (char *)res + sizeof(cachedb_con); res->url.len = url->len; memcpy(res->url.s,url->s,url->len); id = new_cachedb_id(url); if (!id) { LM_ERR("cannot parse url [%.*s]\n",url->len,url->s); pkg_free(res); return 0; } con = cachedb_pool_get(id); if (con == NULL) { LM_DBG("opening new connection\n"); con = new_connection(id); if (con == NULL) { LM_ERR("failed to open connection\n"); goto err; } cachedb_pool_insert((cachedb_pool_con *)con); } else LM_DBG("connection already in pool\n"); res->data = con; return res; err: if (res) pkg_free(res); if (id) free_cachedb_id(id); return 0; } void cachedb_do_close(cachedb_con *con, void (*free_connection)(cachedb_pool_con *)) { cachedb_pool_con *tmp; if (con == NULL) { LM_ERR("NULL parameter provided\n"); return; } tmp = (cachedb_pool_con*)con->data; if (cachedb_pool_remove(tmp) == 1) { free_connection(tmp); } pkg_free(con); } int cachedb_raw_query(str* cachedb_name, str* attr, cdb_raw_entry*** reply,int expected_kv_no,int *rpl_no) { cachedb_engine* cde; str cde_engine,grp_name; char *p; cachedb_con *con; int ret; if(cachedb_name == NULL || attr == NULL) { LM_ERR("null arguments\n"); return -1; } p = memchr(cachedb_name->s,':',cachedb_name->len); if (p == NULL) { cde_engine = *cachedb_name; grp_name.s = NULL; grp_name.len = 0; LM_DBG("from script [%.*s] - no grp\n",cde_engine.len,cde_engine.s); } else { cde_engine.s = cachedb_name->s; cde_engine.len = p - cde_engine.s; grp_name.s = p+1; grp_name.len = cachedb_name->len - cde_engine.len -1; LM_DBG("from script [%.*s] - with grp [%.*s]\n",cde_engine.len, cde_engine.s,grp_name.len,grp_name.s); } cde = lookup_cachedb(&cde_engine); if(cde == NULL) { LM_ERR("Wrong argument <%.*s> - no cachedb system with" " this name registered\n", cde_engine.len,cde_engine.s); return -1; } if (!CACHEDB_CAPABILITY(&cde->cdb_func,CACHEDB_CAP_RAW)) { LM_ERR("The backend does not support raw queries\n"); return -1; } con = cachedb_get_connection(cde,&grp_name); if (con == NULL) { LM_ERR("failed to get connection for grp name [%.*s]\n", grp_name.len,grp_name.s); return -1; } ret = cde->cdb_func.raw_query(con,attr,reply,expected_kv_no,rpl_no); if (ret == 0) ret++; return ret; } void free_raw_fetch(cdb_raw_entry **reply, int no_val, int no_key) { int i,j; for (i=0;i typedef enum { CACHEDB_CAP_GET = 1<<0, CACHEDB_CAP_SET = 1<<1, CACHEDB_CAP_REMOVE = 1<<2, CACHEDB_CAP_ADD = 1<<3, CACHEDB_CAP_SUB = 1<<4, CACHEDB_CAP_BINARY_VALUE = 1<<5, CACHEDB_CAP_RAW = 1<<6 } cachedb_cap; #define CACHEDB_CAPABILITY(cdbf,cpv) (((cdbf)->capability & (cpv)) == (cpv)) static inline int check_cachedb_api(cachedb_engine *cde) { if (cde == NULL) return -1; if (cde->cdb_func.init == 0) { LM_ERR("module %.*s does not export init func\n", cde->name.len,cde->name.s); return -1; } if (cde->cdb_func.destroy == 0) { LM_ERR("module %.*s doesn't export destroy func\n", cde->name.len,cde->name.s); return -1; } if (cde->cdb_func.get) cde->cdb_func.capability |= CACHEDB_CAP_GET; if (cde->cdb_func.set) cde->cdb_func.capability |= CACHEDB_CAP_SET; if (cde->cdb_func.remove) cde->cdb_func.capability |= CACHEDB_CAP_REMOVE; if (cde->cdb_func.add) cde->cdb_func.capability |= CACHEDB_CAP_ADD; if (cde->cdb_func.sub) cde->cdb_func.capability |= CACHEDB_CAP_SUB; if (cde->cdb_func.raw_query) cde->cdb_func.capability |= CACHEDB_CAP_RAW; return 0; } #endif opensips-2.2.2/cachedb/cachedb_con.h000066400000000000000000000022041300170765700173130ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #ifndef CACHEDB_CON_H #define CACHEDB_CON_H #include "../str.h" typedef struct { str url; void *data; } cachedb_con; typedef struct cachedbcon_list { str grp; cachedb_con *connection; struct cachedbcon_list *next; } cachedb_con_list; #endif /* CACHEDB_CON_H */ opensips-2.2.2/cachedb/cachedb_id.c000066400000000000000000000216501300170765700171310ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #include "cachedb_id.h" #include "../dprint.h" #include "../mem/mem.h" #include "../ut.h" #include #include /** * Duplicate a string * \param dst destination * \param begin start of the string * \param end end of the string */ static int dupl_string(char** dst, const char* begin, const char* end) { if (*dst) pkg_free(*dst); *dst = pkg_malloc(end - begin + 1); if ((*dst) == NULL) { return -1; } memcpy(*dst, begin, end - begin); (*dst)[end - begin] = '\0'; return 0; } /** * Parse a database URL of form * scheme://[username[:password]@]hostname[:port]/database * * \param id filled id struct * \param url parsed URL * \return 0 if parsing was successful and -1 otherwise */ static int parse_cachedb_url(struct cachedb_id* id, const str* url) { #define SHORTEST_DB_URL "s://" #define SHORTEST_DB_URL_LEN (sizeof(SHORTEST_DB_URL) - 1) enum state { ST_SCHEME, /* Scheme part */ ST_SLASH1, /* First slash */ ST_SLASH1_GRP, /* Group Name or first / */ ST_SLASH2, /* Second slash */ ST_USER_HOST, /* Username or hostname */ ST_PASS_PORT, /* Password or port part */ ST_HOST, /* Hostname part */ ST_PORT, /* Port part */ ST_DB /* Database part */ }; enum state st; unsigned int len, i; char* begin; char* prev_token,*start_host=NULL,*start_prev=NULL,*ptr; prev_token = 0; if (!id || !url || !url->s) { goto err; } len = url->len; if (len < SHORTEST_DB_URL_LEN) { goto err; } LM_DBG("parsing [%.*s]\n",url->len,url->s); /* Initialize all attributes to 0 */ memset(id, 0, sizeof(struct cachedb_id)); st = ST_SCHEME; begin = url->s; if (dupl_string(&id->initial_url,url->s,url->s+url->len) < 0) goto err; for(i = 0; i < len; i++) { switch(st) { case ST_SCHEME: switch(url->s[i]) { case ':': st = ST_SLASH1_GRP; if (dupl_string(&id->scheme, begin, url->s + i) < 0) goto err; begin = url->s+i+1; break; } break; case ST_SLASH1_GRP: switch(url->s[i]) { case ':': st = ST_SLASH1; if (dupl_string(&id->group_name,begin,url->s+i) < 0) goto err; break; case '/': st = ST_SLASH2; break; } break; case ST_SLASH1: switch(url->s[i]) { case '/': st = ST_SLASH2; break; default: goto err; } break; case ST_SLASH2: switch(url->s[i]) { case '/': st = ST_USER_HOST; begin = url->s + i + 1; break; default: goto err; } break; case ST_USER_HOST: switch(url->s[i]) { case '@': st = ST_HOST; if (dupl_string(&id->username, begin, url->s + i) < 0) goto err; begin = url->s + i + 1; break; case ':': st = ST_PASS_PORT; if (dupl_string(&prev_token, begin, url->s + i) < 0) goto err; start_prev = begin; begin = url->s + i + 1; break; case '/': if (dupl_string(&id->host, begin, url->s + i) < 0) goto err; if (url->s+i+1 == url->s + len) { st = ST_DB; break; } if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err; return 0; } break; case ST_PASS_PORT: switch(url->s[i]) { case '@': st = ST_HOST; id->username = prev_token; if (dupl_string(&id->password, begin, url->s + i) < 0) goto err; begin = url->s + i + 1; break; case '/': id->host = prev_token; id->port = str2s(begin, url->s + i - begin, 0); if (url->s+i+1 == url->s + len) { st = ST_DB; break; } if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err; return 0; case ',': st=ST_HOST; start_host=start_prev; id->flags |= CACHEDB_ID_MULTIPLE_HOSTS; break; } break; case ST_HOST: switch(url->s[i]) { case ':': LM_DBG("in host - :\n"); if (id->flags & CACHEDB_ID_MULTIPLE_HOSTS) { LM_DBG("multiple hosts, skipping\n"); break; } st = ST_PORT; if (dupl_string(&id->host, begin, url->s + i) < 0) goto err; start_host = begin; begin = url->s + i + 1; break; case '/': if (id->flags & CACHEDB_ID_MULTIPLE_HOSTS) ptr = start_host; else ptr = begin; if (dupl_string(&id->host, ptr, url->s + i) < 0) goto err; if (url->s+i+1 == url->s + len) { st = ST_DB; break; } if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err; return 0; } break; case ST_PORT: switch(url->s[i]) { case '/': id->port = str2s(begin, url->s + i - begin, 0); if (url->s+i+1 == url->s + len) { st = ST_DB; break; } if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err; return 0; case ',': st = ST_HOST; pkg_free(id->host); id->host=NULL; begin = start_host; id->flags |= CACHEDB_ID_MULTIPLE_HOSTS; break; } case ST_DB: break; } } if (st == ST_USER_HOST && begin == url->s+url->len) { /* Not considered an error - to cope with modules that * offer cacheDB functionality backed up by OpenSIPS mem */ id->flags |= CACHEDB_ID_NO_URL; LM_DBG("Just scheme, no actual url\n"); return 0; } if (st != ST_DB) goto err; return 0; err: if (id && id->initial_url) pkg_free(id->initial_url); if (id && id->scheme) pkg_free(id->scheme); if (id && id->username) pkg_free(id->username); if (id && id->password) pkg_free(id->password); if (id && id->host) pkg_free(id->host); if (id && id->database) pkg_free(id->database); if (prev_token) pkg_free(prev_token); return -1; } /** * Create a new connection identifier * \param url database URL * \return connection identifier, or zero on error */ struct cachedb_id* new_cachedb_id(const str* url) { struct cachedb_id* ptr; if (!url || !url->s) { LM_ERR("invalid parameter\n"); return 0; } ptr = pkg_malloc(sizeof(struct cachedb_id)); if (!ptr) { LM_ERR("no private memory left\n"); goto err; } memset(ptr, 0, sizeof(struct cachedb_id)); if (parse_cachedb_url(ptr, url) < 0) { LM_ERR("error while parsing database URL: '%.*s' \n", url->len, url->s); goto err; } return ptr; err: if (ptr) pkg_free(ptr); return 0; } /** * Compare two connection identifiers * \param id1 first identifier * \param id2 second identifier * \return one if both are equal, zero otherwise */ int cmp_cachedb_id(struct cachedb_id* id1, struct cachedb_id* id2) { if (!id1 || !id2) return 0; /* connections with different flags never match */ if (id1->flags != id2->flags) return 0; /* different scehemes - never match */ if (strcmp(id1->scheme,id2->scheme)) return 0; if (id1->flags == CACHEDB_ID_NO_URL) { /* no url - always match, based just on scheme */ return 1; } /* different group names - never match */ if ((id1->group_name == NULL && id2->group_name != NULL) || (id1->group_name != NULL && id2->group_name == NULL)) return 0; if (id1->group_name && strcmp(id1->group_name,id2->group_name)) return 0; /* different usernames - never match */ if ((id1->username == NULL && id2->username != NULL) || (id1->username != NULL && id2->username == NULL)) return 0; if (id1->username && strcmp(id1->username,id2->username)) return 0; /* different passwords - never match */ if ((id1->password == NULL && id2->password != NULL) || (id1->password != NULL && id2->password == NULL)) return 0; if (id1->password && strcmp(id1->password,id2->password)) return 0; if (strcmp(id1->host,id2->host)) return 0; if ((id1->database == NULL && id2->database != NULL) || (id1->database != NULL && id2->database == NULL)) return 0; if (id1->database && strcmp(id1->database,id2->database)) return 0; if (id1->flags != CACHEDB_ID_MULTIPLE_HOSTS) { /* also check port as it is not included in host member */ if (id1->port != id2->port) return 0; } return 1; } /** * Free a connection identifier * \param id identifier */ void free_cachedb_id(struct cachedb_id* id) { if (!id) return; if (id->initial_url) pkg_free(id->initial_url); if (id->scheme) pkg_free(id->scheme); if (id->group_name) pkg_free(id->group_name); if (id->username) pkg_free(id->username); if (id->password) pkg_free(id->password); if (id->host) pkg_free(id->host); if (id->database) pkg_free(id->database); pkg_free(id); } opensips-2.2.2/cachedb/cachedb_id.h000066400000000000000000000041531300170765700171350ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #ifndef _CACHEDB_ID_H #define _CACHEDB_ID_H #include "../str.h" /** Structure representing a database ID */ struct cachedb_id { char* scheme; /**< URL scheme */ char* group_name; /**< Name of the URL group */ char* username; /**< Username, case sensitive */ char* password; /**< Password, case sensitive */ char* host; /**< Host or IP, case insensitive */ unsigned short port; /**< Port number */ char* database; /**< Database, case sensitive */ char* initial_url; /**< Initial full URL */ int flags; /**< Flags for signaling various events */ }; #define CACHEDB_ID_NO_URL (1<<0) #define CACHEDB_ID_MULTIPLE_HOSTS (1<<1) /** * Create a new connection identifier * \param url database URL * \return new allocated db_id structure, NULL on failure */ struct cachedb_id* new_cachedb_id(const str* url); /** * Compare two connection identifiers * \param id1 first identifier * \param id2 second identifier * \return 1 if both identifier are equal, 0 if there not equal */ int cmp_cachedb_id(struct cachedb_id* id1, struct cachedb_id* id2); /** * Free a connection identifier * \param id the identifier that should released */ void free_cachedb_id(struct cachedb_id* id); #endif /* _CACHEDB_ID_H */ opensips-2.2.2/cachedb/cachedb_pool.c000066400000000000000000000045041300170765700175050ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #include "../dprint.h" #include "../mem/mem.h" #include "cachedb_pool.h" #include static cachedb_pool_con *cachedb_pool = NULL; cachedb_pool_con* cachedb_pool_get(struct cachedb_id *id) { cachedb_pool_con *it; for (it=cachedb_pool;it;it=it->next) if (cmp_cachedb_id(id,it->id)) { it->ref++; return it; } return 0; } cachedb_pool_con** filter_pool_by_scheme(str *scheme,int* lst_size) { cachedb_pool_con *it; cachedb_pool_con **lst=NULL; int size = 0; int alloc_size = 0; for (it=cachedb_pool;it;it=it->next) { if (memcmp(scheme->s,it->id->scheme,scheme->len) == 0) { if (alloc_size - size == 0) { alloc_size=(alloc_size==0)?2:2*alloc_size; lst = pkg_realloc(lst,alloc_size * sizeof(cachedb_pool_con*)); if (lst == NULL) { LM_ERR("No more pkg \n"); *lst_size = 0; return NULL; } } lst[size]=it; size++; } } *lst_size = size; return lst; } void cachedb_pool_insert(cachedb_pool_con *con) { if (!con) return; con->next = cachedb_pool; cachedb_pool = con; } int cachedb_pool_remove(cachedb_pool_con *con) { cachedb_pool_con *it; if (!con) return -2; if (con->ref > 1) { con->ref--; return 0; } if (cachedb_pool == con) { cachedb_pool = cachedb_pool->next; } else { it = cachedb_pool; while (it) { if (it->next == con) break; it = it->next; } if (!it) { LM_ERR("BUG - conn not found in pool\n"); return -1; } it->next = con->next; } return 1; } opensips-2.2.2/cachedb/cachedb_pool.h000066400000000000000000000025241300170765700175120ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #ifndef _CACHEDB_POOL_H #define _CACHEDB_POOL_H #include "../str.h" #include "cachedb_id.h" typedef struct cachedb_pool_con_t{ struct cachedb_id *id; unsigned int ref; struct cachedb_pool_con_t *next; } cachedb_pool_con; cachedb_pool_con* cachedb_pool_get(struct cachedb_id* id); cachedb_pool_con** filter_pool_by_scheme(str *scheme,int *size); void cachedb_pool_insert(cachedb_pool_con *con); int cachedb_pool_remove(cachedb_pool_con *con); #endif /* _CACHEDB_POOL_H */ opensips-2.2.2/cachedb/example/000077500000000000000000000000001300170765700163675ustar00rootroot00000000000000opensips-2.2.2/cachedb/example/Makefile000066400000000000000000000003761300170765700200350ustar00rootroot00000000000000# $Id: Makefile 6253 2009-10-12 19:00:53Z bogdan_iancu $ # # Presence Agent # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=example_cachedb.so include ../../Makefile.modules opensips-2.2.2/cachedb/example/README000066400000000000000000000002201300170765700172410ustar00rootroot00000000000000This is very simple example module that shows how to use Cache/DB interface. If you want to compile this module, move it to modules directory. opensips-2.2.2/cachedb/example/example_cachedb.c000066400000000000000000000120251300170765700216170ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-10-xx created (vlad-paiu) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../pt.h" /* needed for cachedb functionality */ #include "../../cachedb/cachedb.h" static int mod_init(void); static int child_init(int rank); static void destroy(void); static int process_msg(struct sip_msg *msg); /* URL provided as modparam * Examples of URLs : ** local:// ** memcached://192.168.2.134:9999,192.168.2.135:10234/ ** redis://root:foobared@localhost:6381/ */ str cachedb_url; /* Functions that will allow operations with a Cache/DB back-end */ cachedb_funcs cdbf; /* Actual connection to the Cache/DB back-end */ cachedb_con *con; static cmd_export_t cmds[]= { {"process_msg", (cmd_function)process_msg, 0, 0, 0, REQUEST_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[]={ { "cachedb_url", STR_PARAM, &cachedb_url.s}, {0,0,0} }; /** module exports */ struct module_exports exports= { "example_cachedb", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function)destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { LM_NOTICE("initializing module example_cachedb ...\n"); str key=str_init("opensips_online"); str value=str_init("1"); cachedb_url.len = cachedb_url.s ? strlen(cachedb_url.s) : 0; LM_DBG("cachedb_url=%s\n", ZSW(cachedb_url.s)); if(cachedb_url.s== NULL) { LM_ERR("URL not set!\n"); return -1; } LM_DBG("binding to specific module, based on URL\n"); if (cachedb_bind_mod(&cachedb_url,&cdbf) < 0) { LM_ERR("failed to bind to mod\n"); return -1; } LM_DBG("initializing connection to back-end \n"); con = cdbf.init(&cachedb_url); if (con == NULL) { LM_ERR("failed to connect to back-end\n"); return -1; } LM_DBG("Setting key opensips_online in back-end\n"); if (cdbf.set(con,&key,&value,0) < 0) { LM_ERR("failed to set key\n"); return -1; } LM_DBG("Destroying connection to back-end\n"); cdbf.destroy(con); LM_INFO("successfully loaded cachedb_example module\n"); return 0; } static int child_init(int rank) { /* create a connection for each child */ LM_DBG("initializing connection to back-end for child %d \n",rank); con = cdbf.init(&cachedb_url); if (con == NULL) { LM_ERR("failed to connect to back-end\n"); return -1; } return 0; } /* * destroy function */ static void destroy(void) { str key_op=str_init("opensips_online"); str value_op=str_init("0"); str key_inv=str_init("inv_bye"); str val_inv; LM_NOTICE("Destroy module cachedb_example...\n"); if (cdbf.get(con,&key_inv,&val_inv) < 0) LM_ERR("failed to get key\n"); LM_DBG("At OpenSIPS shutdown, counter = %.*s\n",val_inv.len,val_inv.s); LM_DBG("Setting key opensips_online in back-end\n"); if (cdbf.set(con,&key_op,&value_op,0) < 0) LM_ERR("failed to set key\n"); LM_DBG("Closing connection to back-end\n"); cdbf.destroy(con); } static int process_msg(struct sip_msg *msg) { str key=str_init("inv_bye"); int ret,result; LM_DBG("Inside process_msg\n"); /* based on different message contents, decide to do some Cache/DB ops ** Each time we receive an Invite we will increment a counter ** Each time we receive a BYE we will decrement a counter */ if (msg->first_line.u.request.method_value==METHOD_INVITE) { ret = cdbf.add(con,&key,1,0,&result); if (ret<0) { LM_ERR("failed to add to key\n"); return -1; } } else if (msg->first_line.u.request.method_value==METHOD_BYE) { ret = cdbf.sub(con,&key,1,0,&result); if (ret<0) { LM_ERR("failed to add to key\n"); return -1; } } LM_DBG("Exiting process_msg. Counter = %d\n",result); return 1; } opensips-2.2.2/ccopts.sh000066400000000000000000000044251300170765700152170ustar00rootroot00000000000000#!/bin/sh #$Id$ # # returns the CFLAGS for the given compiler (maximum optimizations) # ARCH=`uname -m |sed -e s/i.86/i386/ -e s/sun4u/sparc64/ ` # gcc 3.x optimize for: x86CPU=athlon WARN_ARCH="WARNING: Not tested on architecture $ARCH, using default flags" if [ $# -lt 1 ] then echo "ERROR: you must specify the compiler name" 1>&2 exit 1 fi if [ "$1" = "-h" ] then echo "Usage: " echo " $0 compiler_name" exit 1 fi if CCVER=`./ccver.sh $1` then NAME=`echo "$CCVER"|cut -d" " -f 1` VER=`echo "$CCVER"|cut -d" " -f 2` MAJOR_VER=`echo "$CCVER"|cut -d" " -f 3` else echo "ERROR executing ./ccver.sh" 2>&1 exit 1 fi echo "name=$NAME, ver=$VER, mver=$MAJOR_VER" case $NAME in gcc) #common stuff CFLAGS="-O9 -funroll-loops -Winline -Wall" case $MAJOR_VER in 3) case $ARCH in i386) CFLAGS="$CFLAGS -minline-all-stringops -malign-double" CFLAGS="$CFLAGS -falign-loops -march=$x86CPU" ;; sparc64) CFLAGS="$CFLAGS -mcpu=ultrasparc -mtune=ultrasparc" CFLAGS="$CFLAGS -m32" #other interesting options: # -mcpu=v9 or ultrasparc? # -mtune implied by -mcpu #-mno-epilogue #try to inline function exit code #-mflat # omit save/restore #-faster-structs #faster non Sparc ABI structure copy ;; armv4l) CFLAGS="$CFLAGS -mcpu=strongarm1100" ;; *) echo "$WARN_ARCH" 1>&2 ;; esac ;; 2|*) case $ARCH in i386) CFLAGS="$CFLAGS -m486 -malign-loops=4" ;; sparc64) CFLAGS="$CFLAGS -mv8 -Wa,-xarch=v8plus" ;; armv4l) ;; *) echo "$WARN_ARCH" 1>&2 ;; esac ;; esac ;; icc) CFLAGS="-O3 -ipo -ipo_obj -unroll" case $ARCH in i386) CFLAGS="$CFLAGS -tpp6 -xK" #-openmp #optimize for PIII # -prefetch doesn't seem to work #( ty to inline acroos files, unroll loops,prefetch, # optimize for PIII, use PIII instructions & vect., # mutlithread loops) ;; *) echo "$WARN_ARCH" 1>&2 ;; esac ;; suncc) CFLAGS="-xO5 -fast -native -xCC -xc99" case $ARCH in sparc64) CFLAGS="$CFLAGS -xarch=v8plusa" ;; *) echo "$WARN_ARCH" 1>&2 ;; esac ;; *) echo "WARNING: unknown compiler $NAME, trying _very_ generic flags" 1>&2 CFLAGS="-O2" esac echo "CFLAGS=$CFLAGS" opensips-2.2.2/ccver.sh000066400000000000000000000025671300170765700150330ustar00rootroot00000000000000#!/bin/sh #$Id$ # # finds CC version and prints it in the following format: # compiler_name version major_version # if [ $# -lt 1 ] then echo "Error: you must specify the compiler name" 1>&2 exit 1 fi if [ "$1" = "-h" ] then echo "Usage: " echo " $0 compiler_name" exit 1 fi CC=$1 if which $CC >/dev/null then (test ! -x `which $CC`) && echo "Error: $CC not executable" 1>&2 && exit 1 else echo "Error: $CC not found or not executable" 1>&2 exit 1 fi if $CC -v 2>/dev/null 1>/dev/null then FULLVER=`$CC -v 2>&1` else FULLVER=`$CC -V 2>&1` fi if [ -n "$FULLVER" ] then # check if gcc if echo "$FULLVER"|grep gcc >/dev/null then NAME=gcc VER=`$CC --version|head -n 1| \ sed -e 's/^[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/'\ -e 's/^[^.0-9]*\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/'` elif echo "$FULLVER"|grep Sun >/dev/null then NAME=suncc VER=`echo "$FULLVER"|head -n 1| \ sed -e 's/.*\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/'` elif echo "$FULLVER"|grep "Intel(R) C++ Compiler" >/dev/null then NAME=icc VER=`echo "$FULLVER"|head -n 1| \ sed -e 's/.*Version \([0-9]\.[0-9]\.[0-9]*\).*/\1/' ` fi # find major ver if [ -n "$VER" -a -z "$MAJOR_VER" ] then MAJOR_VER=`echo "$VER" |cut -d. -f1` fi fi #unknown if [ -z "$NAME" ] then NAME="unknown" VER="unknown" MAJOR_VER="unknown" fi echo "$NAME $VER $MAJOR_VER" opensips-2.2.2/cfg.lex000066400000000000000000001110201300170765700146270ustar00rootroot00000000000000/* * scanner for cfg files * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2005-2009 Voice Sistem S.R.L. * Copyright (C) 2006 enum.at * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-01-29 src_port added (jiri) * 2003-01-23 mhomed added (jiri) * 2003-03-19 replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-01 added dst_port, proto (tcp, udp, tls), af(inet, inet6) (andrei) * 2003-04-05 s/reply_route/failure_route, onreply_route introduced (jiri) * 2003-04-12 added force_rport, chdir and wdir (andrei) * 2003-04-22 strip_tail added (jiri) * 2003-07-03 tls* (disable, certificate, private_key, ca_list, verify, * require_certificate added (andrei) * 2003-07-06 more tls config. vars added: tls_method, tls_port_no (andrei) * 2003-10-02 added {,set_}advertised_{address,port} (andrei) * 2003-10-07 added hex and octal numbers support (andrei) * 2003-10-10 replaced len_gt w/ msg:len (andrei) * 2003-10-13 added fifo_dir (andrei) * 2003-10-28 added tcp_accept_aliases (andrei) * 2003-11-29 added {tcp_send, tcp_connect, tls_*}_timeout (andrei) * 2004-03-30 added DISABLE_CORE and OPEN_FD_LIMIT (andrei) * 2004-04-28 added sock_mode (replaces fifo_mode), sock_user & * sock_group (andrei) * 2004-05-03 applied multicast support patch from janakj * added MCAST_TTL (andrei) * 2005-07-08 added tcp_connection_lifetime, tcp_poll_method, * tcp_max_connections (andrei) * 2004-10-08 more escapes: \", \xHH, \nnn and minor optimizations (andrei) * 2004-10-19 added FROM_URI and TO_URI (andrei) * 2004-11-30 added force_send_socket * 2005-11-22 added tos configurability (thanks to Andreas Granig) * 2005-11-29 added serialize_branches and next_branches (bogdan) * 2006-12-22 functions for script and branch flags added (bogdan) * 2007-01-11 auto_aliases option added (bogdan) * 2007-01-25 disable_dns_failover option added (bogdan) */ %{ #include "cfg.tab.h" #include "dprint.h" #include "globals.h" #include "mem/mem.h" #include #include #include "ip_addr.h" /* states */ #define INITIAL_S 0 #define COMMENT_S 1 #define COMMENT_LN_S 2 #define STRING_S 3 #define SCRIPTVAR_S 4 #define STR_BUF_ALLOC_UNIT 128 struct str_buf{ char* s; char* crt; int left; }; static int comment_nest=0; static int state=0; static struct str_buf s_buf; int line=1; int np=0; int svar_tlen=0; int column=1; int startcolumn=1; int startline=1; char *finame = 0; static char* addchar(struct str_buf *, char); static char* addstr(struct str_buf *, char*, int); static void count(); #define MAX_INCLUDE_DEPTH 10 #define MAX_INCLUDE_FNAME 128 static struct oss_yy_state { YY_BUFFER_STATE state; int line; int column; int startcolumn; int startline; char *finame; } include_stack[MAX_INCLUDE_DEPTH]; static int include_stack_ptr = 0; static int oss_push_yy_state(char *fin, int mode); static int oss_pop_yy_state(void); static struct oss_yy_fname { char *fname; struct oss_yy_fname *next; } *oss_yy_fname_list = 0; /* hack to solve the duplicate declaration of 'isatty' function */ #if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION < 36 #define YY_NO_UNISTD_H #else #include #endif /* hack to skip the declaration of lex unused function 'input' */ #define YY_NO_INPUT %} /* start conditions */ %x STRING1 STRING2 COMMENT COMMENT_LN SCRIPTVARS %x INCLF IMPTF /* action keywords */ FORWARD forward ASSERT "assert" DROP "drop" EXIT "exit" RETURN "return" SEND send SEND_TCP send_tcp LOG log ERROR error ROUTE route ROUTE_FAILURE failure_route ROUTE_ONREPLY onreply_route ROUTE_BRANCH branch_route ROUTE_ERROR error_route ROUTE_LOCAL local_route ROUTE_STARTUP startup_route ROUTE_TIMER timer_route ROUTE_EVENT event_route FORCE_RPORT "force_rport"|"add_rport" FORCE_LOCAL_RPORT "force_local_rport"|"add_local_rport" FORCE_TCP_ALIAS "force_tcp_alias"|"add_tcp_alias" SETFLAG setflag RESETFLAG resetflag ISFLAGSET isflagset SETBFLAG "setbflag"|"setbranchflag" RESETBFLAG "resetbflag"|"resetbranchflag" ISBFLAGSET "isbflagset"|"isbranchflagset" SET_HOST "rewritehost"|"sethost"|"seth" SET_HOSTPORT "rewritehostport"|"sethostport"|"sethp" SET_USER "rewriteuser"|"setuser"|"setu" SET_USERPASS "rewriteuserpass"|"setuserpass"|"setup" SET_PORT "rewriteport"|"setport"|"setp" SET_URI "rewriteuri"|"seturi" REVERT_URI "revert_uri" SET_DSTURI "setdsturi"|"setduri" RESET_DSTURI "resetdsturi"|"resetduri" ISDSTURISET "isdsturiset"|"isduriset" PREFIX "prefix" STRIP "strip" STRIP_TAIL "strip_tail" APPEND_BRANCH "append_branch" REMOVE_BRANCH "remove_branch" PV_PRINTF "pv_printf"|"avp_printf" IF "if" ELSE "else" SWITCH "switch" CASE "case" DEFAULT "default" SBREAK "break"|"esac" WHILE "while" FOR "for" IN "in" SET_ADV_ADDRESS "set_advertised_address" SET_ADV_PORT "set_advertised_port" FORCE_SEND_SOCKET "force_send_socket" SERIALIZE_BRANCHES "serialize_branches" NEXT_BRANCHES "next_branches" USE_BLACKLIST "use_blacklist" UNUSE_BLACKLIST "unuse_blacklist" CACHE_STORE "cache_store" CACHE_FETCH "cache_fetch" CACHE_COUNTER_FETCH "cache_counter_fetch" CACHE_REMOVE "cache_remove" CACHE_ADD "cache_add" CACHE_SUB "cache_sub" CACHE_RAW_QUERY "cache_raw_query" XDBG "xdbg" XLOG_BUF_SIZE "xlog_buf_size" XLOG_FORCE_COLOR "xlog_force_color" XLOG_DEFAULT_LEVEL "xlog_default_level" XLOG "xlog" RAISE_EVENT "raise_event" SUBSCRIBE_EVENT "subscribe_event" CONSTRUCT_URI "construct_uri" GET_TIMESTAMP "get_timestamp" SCRIPT_TRACE "script_trace" SYNC_TOKEN "sync" ASYNC_TOKEN "async" /*ACTION LVALUES*/ URIHOST "uri:host" URIPORT "uri:port" MAX_LEN "max_len" /* condition keywords */ METHOD method /* hack -- the second element in first line is referable as either uri or status; it only would makes sense to call it "uri" from route{} and status from onreply_route{} */ URI "uri"|"status" FROM_URI "from_uri" TO_URI "to_uri" SRCIP src_ip SRCPORT src_port DSTIP dst_ip DSTPORT dst_port PROTO proto AF af MYSELF myself MSGLEN "msg:len" /* operators */ EQUAL = EQUAL_T == GT > LT < GTE >= LTE <= DIFF != MATCH =~ NOTMATCH !~ BAND "&" BOR "|" BXOR "^" BNOT "~" BLSHIFT "<<" BRSHIFT ">>" NOT !|"not" AND "and"|"&&" OR "or"|"||" PLUS "+" MINUS "-" MULT "*" MODULO "%" COLONEQ ":=" PLUSEQ "+=" MINUSEQ "-=" SLASHEQ "/=" MULTEQ "*=" MODULOEQ "%=" BANDEQ "&=" BOREQ "|=" BXOREQ "^=" ASSIGNOP {EQUAL}|{COLONEQ}|{PLUSEQ}|{MINUSEQ}|{SLASHEQ}|{MULTEQ}|{MODULOEQ}|{BANDEQ}|{BOREQ}|{BXOREQ} BITOP {BAND}|{BOR}|{BXOR}|{BNOT}|{BLSHIFT}|{BRSHIFT} ARITHOP {PLUS}|{MINUS}|{SLASH}|{MULT}|{MODULO} LOGOP {EQUAL_T}|{GT}|{LT}|{GTE}|{LTE}|{DIFF}|{MATCH}|{NOTMATCH}|{NOT}|{AND}|{OR} /* variables */ SCRIPTVAR_START "$" /* config vars. */ DEBUG_MODE debug_mode FORK fork CHILDREN children CHROOT "chroot" WDIR "workdir"|"wdir" DISABLE_CORE "disable_core_dump" OPEN_FD_LIMIT "open_files_limit" ENABLE_ASSERTS enable_asserts ABORT_ON_ASSERT abort_on_assert DEBUG debug LOGLEVEL log_level LOGSTDERROR log_stderror LOGFACILITY log_facility LOGNAME log_name LISTEN listen MEMGROUP mem-group ALIAS alias AUTO_ALIASES auto_aliases DNS dns REV_DNS rev_dns DNS_TRY_IPV6 dns_try_ipv6 DNS_TRY_NAPTR dns_try_naptr DNS_RETR_TIME dns_retr_time DNS_RETR_NO dns_retr_no DNS_SERVERS_NO dns_servers_no DNS_USE_SEARCH dns_use_search_list MAXBUFFER maxbuffer CHECK_VIA check_via SHM_HASH_SPLIT_PERCENTAGE "shm_hash_split_percentage" SHM_SECONDARY_HASH_SIZE "shm_secondary_hash_size" MEM_WARMING_ENABLED "mem_warming"|"mem_warming_enabled" MEM_WARMING_PATTERN_FILE "mem_warming_pattern_file" MEM_WARMING_PERCENTAGE "mem_warming_percentage" MEMLOG "memlog"|"mem_log" MEMDUMP "memdump"|"mem_dump" EXECMSGTHRESHOLD "execmsgthreshold"|"exec_msg_threshold" EXECDNSTHRESHOLD "execdnsthreshold"|"exec_dns_threshold" TCPTHRESHOLD "tcpthreshold"|"tcp_threshold" EVENT_SHM_THRESHOLD "event_shm_threshold" EVENT_PKG_THRESHOLD "event_pkg_threshold" QUERYBUFFERSIZE query_buffer_size QUERYFLUSHTIME query_flush_time SIP_WARNING sip_warning SERVER_SIGNATURE server_signature SERVER_HEADER server_header USER_AGENT_HEADER user_agent_header MHOMED mhomed POLL_METHOD "poll_method" TCP_CHILDREN "tcp_children" TCP_ACCEPT_ALIASES "tcp_accept_aliases" TCP_CONNECT_TIMEOUT "tcp_connect_timeout" TCP_CON_LIFETIME "tcp_connection_lifetime" TCP_LISTEN_BACKLOG "tcp_listen_backlog" TCP_MAX_CONNECTIONS "tcp_max_connections" TCP_NO_NEW_CONN_BFLAG "tcp_no_new_conn_bflag" TCP_KEEPALIVE "tcp_keepalive" TCP_KEEPCOUNT "tcp_keepcount" TCP_KEEPIDLE "tcp_keepidle" TCP_KEEPINTERVAL "tcp_keepinterval" TCP_MAX_MSG_TIME "tcp_max_msg_time" ADVERTISED_ADDRESS "advertised_address" ADVERTISED_PORT "advertised_port" MCAST_LOOPBACK "mcast_loopback" MCAST_TTL "mcast_ttl" TOS "tos" DISABLE_DNS_FAILOVER "disable_dns_failover" DISABLE_DNS_BLACKLIST "disable_dns_blacklist" DST_BLACKLIST "dst_blacklist" MAX_WHILE_LOOPS "max_while_loops" DISABLE_STATELESS_FWD "disable_stateless_fwd" DB_VERSION_TABLE "db_version_table" DB_DEFAULT_URL "db_default_url" DB_MAX_ASYNC_CONNECTIONS "db_max_async_connections" DISABLE_503_TRANSLATION "disable_503_translation" MPATH mpath LOADMODULE loadmodule MODPARAM modparam /* values */ YES "yes"|"true"|"on"|"enable" NO "no"|"false"|"off"|"disable" INET "inet"|"INET" INET6 "inet6"|"INET6" NULLV "null"|"NULL" LETTER [a-zA-Z] DIGIT [0-9] ALPHANUM {LETTER}|{DIGIT}|[_] NUMBER 0|([1-9]{DIGIT}*) /*NUMBER 0|(([-+])?[1-9]{DIGIT}*)*/ ID {LETTER}{ALPHANUM}* HEX [0-9a-fA-F] HEXNUMBER 0x{HEX}+ OCTNUMBER 0[0-7]+ HEX4 {HEX}{1,4} IPV6ADDR ({HEX4}":"){7}{HEX4}|({HEX4}":"){1,7}(":"{HEX4}){1,7}|":"(":"{HEX4}){1,7}|({HEX4}":"){1,7}":"|"::" QUOTES \" TICK \' SLASH "/" AS {EAT_ABLE}("as"|"AS"){EAT_ABLE} USE_CHILDREN {EAT_ABLE}("use_children"|"USE_CHILDREN"){EAT_ABLE} SEMICOLON ; RPAREN \) LPAREN \( LBRACE \{ RBRACE \} LBRACK \[ RBRACK \] COMMA "," COLON ":" DOT \. CR \n ANY "any" COM_LINE # COM_START "/\*" COM_END "\*/" EAT_ABLE [\ \t\b\r] WHITESPACE [ \t\r\n] /* include files */ INCLUDEFILE "include_file" IMPORTFILE "import_file" %% {EAT_ABLE} { count(); } {FORWARD} {count(); yylval.strval=yytext; return FORWARD; } {ASSERT} {count(); yylval.strval=yytext; return ASSERT; } {DROP} { count(); yylval.strval=yytext; return DROP; } {EXIT} { count(); yylval.strval=yytext; return EXIT; } {RETURN} { count(); yylval.strval=yytext; return RETURN; } {SEND} { count(); yylval.strval=yytext; return SEND; } {LOG} { count(); yylval.strval=yytext; return LOG_TOK; } {ERROR} { count(); yylval.strval=yytext; return ERROR; } {SETFLAG} { count(); yylval.strval=yytext; return SETFLAG; } {RESETFLAG} { count(); yylval.strval=yytext; return RESETFLAG; } {ISFLAGSET} { count(); yylval.strval=yytext; return ISFLAGSET; } {SETBFLAG} { count(); yylval.strval=yytext; return SETBFLAG; } {RESETBFLAG} { count(); yylval.strval=yytext; return RESETBFLAG; } {ISBFLAGSET} { count(); yylval.strval=yytext; return ISBFLAGSET; } {MSGLEN} { count(); yylval.strval=yytext; return MSGLEN; } {ROUTE} { count(); yylval.strval=yytext; return ROUTE; } {ROUTE_ONREPLY} { count(); yylval.strval=yytext; return ROUTE_ONREPLY; } {ROUTE_FAILURE} { count(); yylval.strval=yytext; return ROUTE_FAILURE; } {ROUTE_BRANCH} { count(); yylval.strval=yytext; return ROUTE_BRANCH; } {ROUTE_ERROR} { count(); yylval.strval=yytext; return ROUTE_ERROR; } {ROUTE_LOCAL} { count(); yylval.strval=yytext; return ROUTE_LOCAL; } {ROUTE_STARTUP} { count(); yylval.strval=yytext; return ROUTE_STARTUP; } {ROUTE_TIMER} { count(); yylval.strval=yytext; return ROUTE_TIMER; } {ROUTE_EVENT} { count(); yylval.strval=yytext; return ROUTE_EVENT; } {SET_HOST} { count(); yylval.strval=yytext; return SET_HOST; } {SET_HOSTPORT} { count(); yylval.strval=yytext; return SET_HOSTPORT; } {SET_USER} { count(); yylval.strval=yytext; return SET_USER; } {SET_USERPASS} { count(); yylval.strval=yytext; return SET_USERPASS; } {SET_PORT} { count(); yylval.strval=yytext; return SET_PORT; } {SET_URI} { count(); yylval.strval=yytext; return SET_URI; } {REVERT_URI} { count(); yylval.strval=yytext; return REVERT_URI; } {SET_DSTURI} { count(); yylval.strval=yytext; return SET_DSTURI; } {RESET_DSTURI} { count(); yylval.strval=yytext; return RESET_DSTURI; } {ISDSTURISET} { count(); yylval.strval=yytext; return ISDSTURISET; } {PREFIX} { count(); yylval.strval=yytext; return PREFIX; } {STRIP} { count(); yylval.strval=yytext; return STRIP; } {STRIP_TAIL} { count(); yylval.strval=yytext; return STRIP_TAIL; } {APPEND_BRANCH} { count(); yylval.strval=yytext; return APPEND_BRANCH; } {REMOVE_BRANCH} { count(); yylval.strval=yytext; return REMOVE_BRANCH; } {PV_PRINTF} { count(); yylval.strval=yytext; return PV_PRINTF; } {FORCE_RPORT} { count(); yylval.strval=yytext; return FORCE_RPORT; } {FORCE_LOCAL_RPORT} { count(); yylval.strval=yytext; return FORCE_LOCAL_RPORT; } {FORCE_TCP_ALIAS} { count(); yylval.strval=yytext; return FORCE_TCP_ALIAS; } {IF} { count(); yylval.strval=yytext; return IF; } {ELSE} { count(); yylval.strval=yytext; return ELSE; } {SWITCH} { count(); yylval.strval=yytext; return SWITCH; } {CASE} { count(); yylval.strval=yytext; return CASE; } {DEFAULT} { count(); yylval.strval=yytext; return DEFAULT; } {SBREAK} { count(); yylval.strval=yytext; return SBREAK; } {WHILE} { count(); yylval.strval=yytext; return WHILE; } {FOR} { count(); yylval.strval=yytext; return FOR; } {IN} { count(); yylval.strval=yytext; return IN; } {INCLUDEFILE} { count(); BEGIN(INCLF); } {IMPORTFILE} { count(); BEGIN(IMPTF); } {SET_ADV_ADDRESS} { count(); yylval.strval=yytext; return SET_ADV_ADDRESS; } {SET_ADV_PORT} { count(); yylval.strval=yytext; return SET_ADV_PORT; } {FORCE_SEND_SOCKET} { count(); yylval.strval=yytext; return FORCE_SEND_SOCKET; } {SERIALIZE_BRANCHES} { count(); yylval.strval=yytext; return SERIALIZE_BRANCHES; } {NEXT_BRANCHES} { count(); yylval.strval=yytext; return NEXT_BRANCHES; } {USE_BLACKLIST} { count(); yylval.strval=yytext; return USE_BLACKLIST; } {UNUSE_BLACKLIST} { count(); yylval.strval=yytext; return UNUSE_BLACKLIST; } {CACHE_STORE} { count(); yylval.strval=yytext; return CACHE_STORE; } {CACHE_FETCH} { count(); yylval.strval=yytext; return CACHE_FETCH; } {CACHE_COUNTER_FETCH} { count(); yylval.strval=yytext; return CACHE_COUNTER_FETCH; } {CACHE_REMOVE} { count(); yylval.strval=yytext; return CACHE_REMOVE; } {CACHE_ADD} { count(); yylval.strval=yytext; return CACHE_ADD; } {CACHE_SUB} { count(); yylval.strval=yytext; return CACHE_SUB; } {CACHE_RAW_QUERY} { count(); yylval.strval=yytext; return CACHE_RAW_QUERY; } {XDBG} { count(); yylval.strval=yytext; return XDBG; } {XLOG} { count(); yylval.strval=yytext; return XLOG; } {XLOG_BUF_SIZE} { count(); yylval.strval=yytext; return XLOG_BUF_SIZE; } {XLOG_FORCE_COLOR} { count(); yylval.strval=yytext; return XLOG_FORCE_COLOR;} {XLOG_DEFAULT_LEVEL} { count(); yylval.strval=yytext; return XLOG_DEFAULT_LEVEL;} {RAISE_EVENT} { count(); yylval.strval=yytext; return RAISE_EVENT;} {SUBSCRIBE_EVENT} { count(); yylval.strval=yytext; return SUBSCRIBE_EVENT;} {CONSTRUCT_URI} { count(); yylval.strval=yytext; return CONSTRUCT_URI;} {GET_TIMESTAMP} { count(); yylval.strval=yytext; return GET_TIMESTAMP;} {SCRIPT_TRACE} { count(); yylval.strval=yytext; return SCRIPT_TRACE;} {SYNC_TOKEN} { count(); yylval.strval=yytext; return SYNC_TOKEN;} {ASYNC_TOKEN} { count(); yylval.strval=yytext; return ASYNC_TOKEN;} {MAX_LEN} { count(); yylval.strval=yytext; return MAX_LEN; } {METHOD} { count(); yylval.strval=yytext; return METHOD; } {URI} { count(); yylval.strval=yytext; return URI; } {FROM_URI} { count(); yylval.strval=yytext; return FROM_URI; } {TO_URI} { count(); yylval.strval=yytext; return TO_URI; } {SRCIP} { count(); yylval.strval=yytext; return SRCIP; } {SRCPORT} { count(); yylval.strval=yytext; return SRCPORT; } {DSTIP} { count(); yylval.strval=yytext; return DSTIP; } {DSTPORT} { count(); yylval.strval=yytext; return DSTPORT; } {PROTO} { count(); yylval.strval=yytext; return PROTO; } {AF} { count(); yylval.strval=yytext; return AF; } {MYSELF} { count(); yylval.strval=yytext; return MYSELF; } {FORK} { count(); yylval.strval=yytext; return FORK; /*obsolete*/ } {DEBUG_MODE} { count(); yylval.strval=yytext; return DEBUG_MODE; } {CHILDREN} { count(); yylval.strval=yytext; return CHILDREN; } {CHROOT} { count(); yylval.strval=yytext; return CHROOT; } {WDIR} { count(); yylval.strval=yytext; return WDIR; } {DISABLE_CORE} { count(); yylval.strval=yytext; return DISABLE_CORE; } {OPEN_FD_LIMIT} { count(); yylval.strval=yytext; return OPEN_FD_LIMIT; } {ENABLE_ASSERTS} { count(); yylval.strval=yytext; return ENABLE_ASSERTS; } {ABORT_ON_ASSERT} { count(); yylval.strval=yytext; return ABORT_ON_ASSERT; } {DEBUG} { count(); yylval.strval=yytext; return DEBUG; /*obsolete*/ } {LOGLEVEL} { count(); yylval.strval=yytext; return LOGLEVEL; } {LOGSTDERROR} { yylval.strval=yytext; return LOGSTDERROR; } {LOGFACILITY} { yylval.strval=yytext; return LOGFACILITY; } {LOGNAME} { yylval.strval=yytext; return LOGNAME; } {LISTEN} { count(); yylval.strval=yytext; return LISTEN; } {MEMGROUP} { count(); yylval.strval=yytext; return MEMGROUP; } {ALIAS} { count(); yylval.strval=yytext; return ALIAS; } {AUTO_ALIASES} { count(); yylval.strval=yytext; return AUTO_ALIASES; } {DNS} { count(); yylval.strval=yytext; return DNS; } {REV_DNS} { count(); yylval.strval=yytext; return REV_DNS; } {DNS_TRY_IPV6} { count(); yylval.strval=yytext; return DNS_TRY_IPV6; } {DNS_TRY_NAPTR} { count(); yylval.strval=yytext; return DNS_TRY_NAPTR; } {DNS_RETR_TIME} { count(); yylval.strval=yytext; return DNS_RETR_TIME; } {DNS_RETR_NO} { count(); yylval.strval=yytext; return DNS_RETR_NO; } {DNS_SERVERS_NO} { count(); yylval.strval=yytext; return DNS_SERVERS_NO; } {DNS_USE_SEARCH} { count(); yylval.strval=yytext; return DNS_USE_SEARCH; } {MAX_WHILE_LOOPS} { count(); yylval.strval=yytext; return MAX_WHILE_LOOPS; } {MAXBUFFER} { count(); yylval.strval=yytext; return MAXBUFFER; } {CHECK_VIA} { count(); yylval.strval=yytext; return CHECK_VIA; } {SHM_HASH_SPLIT_PERCENTAGE} { count(); yylval.strval=yytext; return SHM_HASH_SPLIT_PERCENTAGE; } {SHM_SECONDARY_HASH_SIZE} { count(); yylval.strval=yytext; return SHM_SECONDARY_HASH_SIZE; } {MEM_WARMING_ENABLED} { count(); yylval.strval=yytext; return MEM_WARMING_ENABLED; } {MEM_WARMING_PATTERN_FILE} { count(); yylval.strval=yytext; return MEM_WARMING_PATTERN_FILE; } {MEM_WARMING_PERCENTAGE} { count(); yylval.strval=yytext; return MEM_WARMING_PERCENTAGE; } {MEMLOG} { count(); yylval.strval=yytext; return MEMLOG; } {MEMDUMP} { count(); yylval.strval=yytext; return MEMDUMP; } {EXECMSGTHRESHOLD} { count(); yylval.strval=yytext; return EXECMSGTHRESHOLD; } {EXECDNSTHRESHOLD} { count(); yylval.strval=yytext; return EXECDNSTHRESHOLD; } {TCPTHRESHOLD} { count(); yylval.strval=yytext; return TCPTHRESHOLD; } {EVENT_SHM_THRESHOLD} { count(); yylval.strval=yytext; return EVENT_SHM_THRESHOLD; } {EVENT_PKG_THRESHOLD} { count(); yylval.strval=yytext; return EVENT_PKG_THRESHOLD; } {QUERYBUFFERSIZE} { count(); yylval.strval=yytext; return QUERYBUFFERSIZE; } {QUERYFLUSHTIME} { count(); yylval.strval=yytext; return QUERYFLUSHTIME; } {SIP_WARNING} { count(); yylval.strval=yytext; return SIP_WARNING; } {MHOMED} { count(); yylval.strval=yytext; return MHOMED; } {TCP_NO_NEW_CONN_BFLAG} { count(); yylval.strval=yytext; return TCP_NO_NEW_CONN_BFLAG; } {TCP_CHILDREN} { count(); yylval.strval=yytext; return TCP_CHILDREN; } {TCP_ACCEPT_ALIASES} { count(); yylval.strval=yytext; return TCP_ACCEPT_ALIASES; } {TCP_CONNECT_TIMEOUT} { count(); yylval.strval=yytext; return TCP_CONNECT_TIMEOUT; } {TCP_CON_LIFETIME} { count(); yylval.strval=yytext; return TCP_CON_LIFETIME; } {TCP_LISTEN_BACKLOG} { count(); yylval.strval=yytext; return TCP_LISTEN_BACKLOG; } {POLL_METHOD} { count(); yylval.strval=yytext; return POLL_METHOD; } {TCP_MAX_CONNECTIONS} { count(); yylval.strval=yytext; return TCP_MAX_CONNECTIONS; } {TCP_KEEPALIVE} { count(); yylval.strval=yytext; return TCP_KEEPALIVE; } {TCP_KEEPCOUNT} { count(); yylval.strval=yytext; return TCP_KEEPCOUNT; } {TCP_KEEPIDLE} { count(); yylval.strval=yytext; return TCP_KEEPIDLE; } {TCP_KEEPINTERVAL} { count(); yylval.strval=yytext; return TCP_KEEPINTERVAL; } {TCP_MAX_MSG_TIME} { count(); yylval.strval=yytext; return TCP_MAX_MSG_TIME; } {SERVER_SIGNATURE} { count(); yylval.strval=yytext; return SERVER_SIGNATURE; } {SERVER_HEADER} { count(); yylval.strval=yytext; return SERVER_HEADER; } {USER_AGENT_HEADER} { count(); yylval.strval=yytext; return USER_AGENT_HEADER; } {ADVERTISED_ADDRESS} { count(); yylval.strval=yytext; return ADVERTISED_ADDRESS; } {ADVERTISED_PORT} { count(); yylval.strval=yytext; return ADVERTISED_PORT; } {MCAST_LOOPBACK} { count(); yylval.strval=yytext; return MCAST_LOOPBACK; } {MCAST_TTL} { count(); yylval.strval=yytext; return MCAST_TTL; } {TOS} { count(); yylval.strval=yytext; return TOS; } {DISABLE_DNS_FAILOVER} { count(); yylval.strval=yytext; return DISABLE_DNS_FAILOVER; } {DISABLE_DNS_BLACKLIST} { count(); yylval.strval=yytext; return DISABLE_DNS_BLACKLIST; } {DST_BLACKLIST} { count(); yylval.strval=yytext; return DST_BLACKLIST; } {DISABLE_STATELESS_FWD} { count(); yylval.strval=yytext; return DISABLE_STATELESS_FWD; } {DB_VERSION_TABLE} { count(); yylval.strval=yytext; return DB_VERSION_TABLE; } {DB_DEFAULT_URL} { count(); yylval.strval=yytext; return DB_DEFAULT_URL; } {DB_MAX_ASYNC_CONNECTIONS} { count(); yylval.strval=yytext; return DB_MAX_ASYNC_CONNECTIONS; } {DISABLE_503_TRANSLATION} { count(); yylval.strval=yytext; return DISABLE_503_TRANSLATION; } {MPATH} { count(); yylval.strval=yytext; return MPATH; } {LOADMODULE} { count(); yylval.strval=yytext; return LOADMODULE; } {MODPARAM} { count(); yylval.strval=yytext; return MODPARAM; } {EQUAL} { count(); return EQUAL; } {EQUAL_T} { count(); return EQUAL_T; } {GT} { count(); return GT; } {LT} { count(); return LT; } {GTE} { count(); return GTE; } {LTE} { count(); return LTE; } {DIFF} { count(); return DIFF; } {MATCH} { count(); return MATCH; } {NOTMATCH} { count(); return NOTMATCH; } {NOT} { count(); return NOT; } {AND} { count(); return AND; } {OR} { count(); return OR; } {PLUS} { count(); return PLUS; } {MINUS} { count(); return MINUS; } {BAND} { count(); return BAND; } {BOR} { count(); return BOR; } {BXOR} { count(); return BXOR; } {BNOT} { count(); return BNOT; } {BLSHIFT} { count(); return BLSHIFT; } {BRSHIFT} { count(); return BRSHIFT; } {MULT} { count(); return MULT; } {MODULO} { count(); return MODULO; } {COLONEQ} { count(); return COLONEQ; } {PLUSEQ} { count(); return PLUSEQ; } {MINUSEQ} { count(); return MINUSEQ; } {SLASHEQ} { count(); return SLASHEQ; } {MULTEQ} { count(); return MULTEQ; } {MODULOEQ} { count(); return MODULOEQ; } {BANDEQ} { count(); return BANDEQ; } {BOREQ} { count(); return BOREQ; } {BXOREQ} { count(); return BXOREQ; } {IPV6ADDR} { count(); yylval.strval=yytext; return IPV6ADDR; } {NUMBER} { count(); yylval.intval=atoi(yytext);return NUMBER; } {HEXNUMBER} { count(); yylval.intval=(int)strtol(yytext, 0, 16); return NUMBER; } {OCTNUMBER} { count(); yylval.intval=(int)strtol(yytext, 0, 8); return NUMBER; } {YES} { count(); yylval.intval=1; return NUMBER; } {NO} { count(); yylval.intval=0; return NUMBER; } {NULLV} { count(); yylval.intval=0; return NULLV; } {INET} { count(); yylval.intval=AF_INET; return NUMBER; } {INET6} { count(); yylval.intval=AF_INET6; return NUMBER; } {COMMA} { count(); return COMMA; } {SEMICOLON} { count(); return SEMICOLON; } {USE_CHILDREN} { count(); return USE_CHILDREN; } {COLON} { count(); return COLON; } {RPAREN} { count(); return RPAREN; } {LPAREN} { count(); return LPAREN; } {LBRACE} { count(); return LBRACE; } {RBRACE} { count(); return RBRACE; } {LBRACK} { count(); return LBRACK; } {RBRACK} { count(); return RBRACK; } {AS} { count(); return AS; } {DOT} { count(); return DOT; } \\{CR} {count(); } /* eat the escaped CR */ {CR} { count();/* return CR;*/ } {ANY} { count(); return ANY; } {SLASH} { count(); return SLASH; } {SCRIPTVAR_START} { count(); np=0; state=SCRIPTVAR_S; svar_tlen = yyleng; yymore(); BEGIN(SCRIPTVARS); } {LPAREN} { count(); np++; yymore(); svar_tlen = yyleng; } {RPAREN} { count(); if(np==0 || np==1) { if(np==0) { addstr(&s_buf, yytext, yyleng-1); unput(yytext[yyleng-1]); yyleng--; } else { addstr(&s_buf, yytext, yyleng); np--; } state=INITIAL_S; BEGIN(INITIAL); yylval.strval=s_buf.s; memset(&s_buf, 0, sizeof(s_buf)); return SCRIPTVAR; } else { np--; yymore(); svar_tlen = yyleng; } } {WHITESPACE} { count(); if(np==0) { addstr(&s_buf, yytext, yyleng-1); unput(yytext[yyleng-1]); yyleng--; state=INITIAL_S; BEGIN(INITIAL); yylval.strval=s_buf.s; memset(&s_buf, 0, sizeof(s_buf)); return SCRIPTVAR; } else { yymore(); svar_tlen = yyleng; } } {SEMICOLON}|{COMMA}|{ASSIGNOP}|{ARITHOP}|{BITOP}|{LOGOP} { count(); if(np==0) { addstr(&s_buf, yytext, svar_tlen); while(yyleng>svar_tlen) { unput(yytext[yyleng-1]); yyleng--; } state=INITIAL_S; BEGIN(INITIAL); yylval.strval=s_buf.s; memset(&s_buf, 0, sizeof(s_buf)); return SCRIPTVAR; } else { yymore(); svar_tlen = yyleng; } } . { yymore(); svar_tlen = yyleng; } {QUOTES} { count(); state=STRING_S; BEGIN(STRING1); } {TICK} { count(); state=STRING_S; BEGIN(STRING2); } {QUOTES} { count(); state=INITIAL_S; BEGIN(INITIAL); yytext[yyleng-1]=0; yyleng--; addstr(&s_buf, yytext, yyleng); yylval.strval=s_buf.s; memset(&s_buf, 0, sizeof(s_buf)); return STRING; } {TICK} { count(); state=INITIAL_S; BEGIN(INITIAL); yytext[yyleng-1]=0; yyleng--; addstr(&s_buf, yytext, yyleng); yylval.strval=s_buf.s; memset(&s_buf, 0, sizeof(s_buf)); return STRING; } .|{EAT_ABLE}|{CR} { yymore(); } \\n { count(); addchar(&s_buf, '\n'); } \\r { count(); addchar(&s_buf, '\r'); } \\a { count(); addchar(&s_buf, '\a'); } \\t { count(); addchar(&s_buf, '\t'); } \\{QUOTES} { count(); addchar(&s_buf, '"'); } \\\\ { count(); addchar(&s_buf, '\\'); } \\x{HEX}{1,2} { count(); addchar(&s_buf, (char)strtol(yytext+2, 0, 16)); } /* don't allow \[0-7]{1}, it will eat the backreferences from subst_uri if allowed (although everybody should use '' in subt_uri) */ \\[0-7]{2,3} { count(); addchar(&s_buf, (char)strtol(yytext+1, 0, 8)); } \\{CR} { count(); } /* eat escaped CRs */ {CR} { count();addchar(&s_buf, *yytext); } .|{EAT_ABLE}|{CR} { addchar(&s_buf, *yytext); } {COM_START} { count(); comment_nest++; state=COMMENT_S; BEGIN(COMMENT); } {COM_END} { count(); comment_nest--; if (comment_nest==0){ state=INITIAL_S; BEGIN(INITIAL); } } .|{EAT_ABLE}|{CR} { count(); }; {COM_LINE}.*{CR} { count(); } {ID} { count(); addstr(&s_buf, yytext, yyleng); yylval.strval=s_buf.s; memset(&s_buf, 0, sizeof(s_buf)); return ID; } [ \t]* /* eat the whitespace */ [^ \t\n]+ { /* get the include file name */ memset(&s_buf, 0, sizeof(s_buf)); addstr(&s_buf, yytext, yyleng); if(oss_push_yy_state(s_buf.s, 0)<0) { LM_CRIT("error at %s line %d\n", (finame)?finame:"cfg", line); exit(-1); } memset(&s_buf, 0, sizeof(s_buf)); BEGIN(INITIAL); } [ \t]* /* eat the whitespace */ [^ \t\n]+ { /* get the import file name */ memset(&s_buf, 0, sizeof(s_buf)); addstr(&s_buf, yytext, yyleng); if(oss_push_yy_state(s_buf.s, 1)<0) { LM_CRIT("error at %s line %d\n", (finame)?finame:"cfg", line); exit(-1); } memset(&s_buf, 0, sizeof(s_buf)); BEGIN(INITIAL); } <> { switch(state){ case STRING_S: LM_CRIT("cfg. parser: unexpected EOF in" " unclosed string\n"); if (s_buf.s){ pkg_free(s_buf.s); memset(&s_buf, 0, sizeof(s_buf)); } break; case COMMENT_S: LM_CRIT("cfg. parser: unexpected EOF:" " %d comments open\n", comment_nest); break; case COMMENT_LN_S: LM_CRIT("unexpected EOF:" "comment line open\n"); break; } if(oss_pop_yy_state()<0) return 0; } %% static char* addchar(struct str_buf* dst, char c) { return addstr(dst, &c, 1); } static char* addstr(struct str_buf* dst_b, char* src, int len) { char *tmp; unsigned size; unsigned used; if (dst_b->left<(len+1)){ used=(unsigned)(dst_b->crt-dst_b->s); size=used+len+1; /* round up to next multiple */ size+= STR_BUF_ALLOC_UNIT-size%STR_BUF_ALLOC_UNIT; tmp=pkg_malloc(size); if (tmp==0) goto error; if (dst_b->s){ memcpy(tmp, dst_b->s, used); pkg_free(dst_b->s); } dst_b->s=tmp; dst_b->crt=dst_b->s+used; dst_b->left=size-used; } memcpy(dst_b->crt, src, len); dst_b->crt+=len; *(dst_b->crt)=0; dst_b->left-=len; return dst_b->s; error: LM_CRIT("lex:addstr: memory allocation error\n"); return 0; } static void count(void) { int i; startcolumn=column; for (i=0; i= MAX_INCLUDE_DEPTH ) { LM_CRIT("too many includes\n"); return -1; } l = strlen(fin); if(l>=MAX_INCLUDE_FNAME) { LM_CRIT("included file name too long: %s\n", fin); return -1; } if(fin[0]!='"' || fin[l-1]!='"') { LM_CRIT("included file name must be between quotes: %s\n", fin); return -1; } j = 0; for(i=1; ifname, newf)==0) { if(newf!=fbuf) pkg_free(newf); newf = fbuf; break; } fn = fn->next; } if(fn==0) { fn = (struct oss_yy_fname*)pkg_malloc(sizeof(struct oss_yy_fname)); if(fn==0) { if(newf!=fbuf) pkg_free(newf); LM_CRIT("no more pkg\n"); return -1; } if(newf==fbuf) { fn->fname = (char*)pkg_malloc(strlen(fbuf)+1); if(fn->fname==0) { pkg_free(fn); LM_CRIT("no more pkg!\n"); return -1; } strcpy(fn->fname, fbuf); } else { fn->fname = newf; } fn->next = oss_yy_fname_list; oss_yy_fname_list = fn; } finame = fn->fname; yy_switch_to_buffer( yy_create_buffer(yyin, YY_BUF_SIZE ) ); return 0; } static int oss_pop_yy_state(void) { include_stack_ptr--; if (include_stack_ptr<0 ) return -1; yy_delete_buffer( YY_CURRENT_BUFFER ); yy_switch_to_buffer(include_stack[include_stack_ptr].state); line=include_stack[include_stack_ptr].line; column=include_stack[include_stack_ptr].column; startline=include_stack[include_stack_ptr].startline; startcolumn=include_stack[include_stack_ptr].startcolumn; finame = include_stack[include_stack_ptr].finame; return 0; } opensips-2.2.2/cfg.y000066400000000000000000002424641300170765700143300ustar00rootroot00000000000000/* * cfg grammar * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2005-2009 Voice Sistem S.R.L. * Copyright (C) 2006 enum.at * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * History: * --------- * 2003-01-29 src_port added (jiri) * 2003-01-23 mhomed added (jiri) * 2003-03-19 replaced all mallocs/frees with pkg_malloc/pkg_free (andrei) * 2003-03-19 Added support for route type in find_export (janakj) * 2003-03-20 Regex support in modparam (janakj) * 2003-04-01 added dst_port, proto , af (andrei) * 2003-04-05 s/reply_route/failure_route, onreply_route introduced (jiri) * 2003-04-12 added force_rport, chroot and wdir (andrei) * 2003-04-15 added tcp_children, disable_tcp (andrei) * 2003-04-22 strip_tail added (jiri) * 2003-07-03 tls* (disable, certificate, private_key, ca_list, verify, * require_certificate added (andrei) * 2003-07-06 more tls config. vars added: tls_method, tls_port_no (andrei) * 2003-10-02 added {,set_}advertised_{address,port} (andrei) * 2003-10-10 added <,>,<=,>=, != operators support * added msg:len (andrei) * 2003-10-11 if(){} doesn't require a ';' after it anymore (andrei) * 2003-10-13 added FIFO_DIR & proto:host:port listen/alias support (andrei) * 2003-10-24 converted to the new socket_info lists (andrei) * 2003-10-28 added tcp_accept_aliases (andrei) * 2003-11-20 added {tcp_connect, tcp_send, tls_*}_timeout (andrei) * 2004-03-30 added DISABLE_CORE and OPEN_FD_LIMIT (andrei) * 2004-04-29 added SOCK_MODE, SOCK_USER & SOCK_GROUP (andrei) * 2004-05-03 applied multicast support patch (MCAST_LOOPBACK) from janakj added MCAST_TTL (andrei) * 2004-07-05 src_ip & dst_ip will detect ip addresses between quotes * (andrei) * 2004-10-19 added FROM_URI, TO_URI (andrei) * 2004-11-30 added force_send_socket (andrei) * 2005-07-08 added TCP_CON_LIFETIME, TCP_POLL_METHOD, TCP_MAX_CONNECTIONS * (andrei) * 2005-07-26 default onreply route added (andrei) * 2005-11-22 added tos configurability (thanks to Andreas Granig) * 2005-11-29 added serialize_branches and next_branches (bogdan) * 2006-03-02 MODULE_T action points to a cmd_export_t struct instead to * a function address - more info is accessible (bogdan) * 2006-03-02 store the cfg line into the action struct to be able to * give more hints if fixups fail (bogdan) * 2006-05-22 forward(_udp,_tcp,_tls) and send(_tcp) merged in forward() and * send() (bogdan) * 2006-12-22 functions for script and branch flags added (bogdan) * 2007-01-11 auto_aliases option added (bogdan) * 2007-01-25 disable_dns_failover option added (bogdan) * 2012-01-19 added TCP keepalive support * 2012-12-06 added event_route (razvanc) * 2013-05-23 added NAPTR lookup option (dsandras) * 2013-09-25 added TLS_CA_DIR option (chris_secusmart) * 2013-10-06 added TLS_DH_PARAM option (mehmet_secusmart) * 2013-10-30 added TLS_EC_CURVE option (yrjoe_secusmart) */ %{ #include #include #include #include #include #include #include #include #include #include #include "route_struct.h" #include "globals.h" #include "route.h" #include "dprint.h" #include "sr_module.h" #include "modparam.h" #include "ip_addr.h" #include "resolve.h" #include "socket_info.h" #include "name_alias.h" #include "ut.h" #include "dset.h" #include "pvar.h" #include "blacklists.h" #include "xlog.h" #include "db/db_insertq.h" #include "bin_interface.h" #include "net/trans.h" #include "config.h" #ifdef SHM_EXTRA_STATS #include "mem/module_info.h" #endif #ifdef DEBUG_DMALLOC #include #endif /* hack to avoid alloca usage in the generated C file (needed for compiler with no built in alloca, like icc*/ #undef _ALLOCA_H extern int yylex(); static void yyerror(char* s); static void yyerrorf(char* fmt, ...); static char* tmp; static int i_tmp, rc; static void* cmd_tmp; static struct socket_id* lst_tmp; static int rt; /* Type of route block for find_export */ static str s_tmp; static str tstr; static struct ip_addr* ip_tmp; static pv_spec_t *spec; static pv_elem_t *pvmodel; static struct bl_rule *bl_head = 0; static struct bl_rule *bl_tail = 0; action_elem_t elems[MAX_ACTION_ELEMS]; static action_elem_t route_elems[MAX_ACTION_ELEMS]; action_elem_t *a_tmp; static inline void warn(char* s); static struct socket_id* mk_listen_id(char*, enum sip_protos, int); static struct socket_id* set_listen_id_adv(struct socket_id *, char *, int); static struct multi_str *new_string(char *s); extern int line; extern int column; extern int startcolumn; extern char *finame; #ifndef SHM_EXTRA_STATS struct multi_str{ char *s; struct multi_str* next; }; #else static struct multi_str *tmp_mod; #endif #define get_cfg_file_name \ ((finame) ? finame : cfg_file ? cfg_file : "default") #define mk_action_(_res, _type, _no, _elems) \ do { \ _res = mk_action(_type, _no, _elems, line, get_cfg_file_name); \ } while(0) #define mk_action0(_res, _type, _p1_type, _p2_type, _p1, _p2) \ do { \ _res = mk_action(_type, 0, 0, line, get_cfg_file_name); \ } while(0) #define mk_action1(_res, _type, _p1_type, _p1) \ do { \ elems[0].type = _p1_type; \ elems[0].u.data = _p1; \ _res = mk_action(_type, 1, elems, line, get_cfg_file_name); \ } while(0) #define mk_action2(_res, _type, _p1_type, _p2_type, _p1, _p2) \ do { \ elems[0].type = _p1_type; \ elems[0].u.data = _p1; \ elems[1].type = _p2_type; \ elems[1].u.data = _p2; \ _res = mk_action(_type, 2, elems, line, get_cfg_file_name); \ } while(0) #define mk_action3(_res, _type, _p1_type, _p2_type, _p3_type, _p1, _p2, _p3) \ do { \ elems[0].type = _p1_type; \ elems[0].u.data = _p1; \ elems[1].type = _p2_type; \ elems[1].u.data = _p2; \ elems[2].type = _p3_type; \ elems[2].u.data = _p3; \ _res = mk_action(_type, 3, elems, line, get_cfg_file_name); \ } while(0) %} %union { long intval; unsigned long uval; char* strval; struct expr* expr; struct action* action; struct net* ipnet; struct ip_addr* ipaddr; struct socket_id* sockid; struct _pv_spec *specval; struct multi_str* multistr; } /* terminals */ /* keywords */ %token FORWARD %token SEND %token DROP %token ASSERT %token EXIT %token RETURN %token LOG_TOK %token ERROR %token ROUTE %token ROUTE_FAILURE %token ROUTE_ONREPLY %token ROUTE_BRANCH %token ROUTE_ERROR %token ROUTE_LOCAL %token ROUTE_STARTUP %token ROUTE_TIMER %token ROUTE_EVENT %token SET_HOST %token SET_HOSTPORT %token PREFIX %token STRIP %token STRIP_TAIL %token APPEND_BRANCH %token REMOVE_BRANCH %token PV_PRINTF %token SET_USER %token SET_USERPASS %token SET_PORT %token SET_URI %token REVERT_URI %token SET_DSTURI %token RESET_DSTURI %token ISDSTURISET %token FORCE_RPORT %token FORCE_LOCAL_RPORT %token FORCE_TCP_ALIAS %token IF %token ELSE %token SWITCH %token CASE %token DEFAULT %token SBREAK %token WHILE %token FOR %token IN %token SET_ADV_ADDRESS %token SET_ADV_PORT %token FORCE_SEND_SOCKET %token SERIALIZE_BRANCHES %token NEXT_BRANCHES %token USE_BLACKLIST %token UNUSE_BLACKLIST %token MAX_LEN %token SETFLAG %token RESETFLAG %token ISFLAGSET %token SETBFLAG %token RESETBFLAG %token ISBFLAGSET %token METHOD %token URI %token FROM_URI %token TO_URI %token SRCIP %token SRCPORT %token DSTIP %token DSTPORT %token PROTO %token AF %token MYSELF %token MSGLEN %token NULLV %token CACHE_STORE %token CACHE_FETCH %token CACHE_COUNTER_FETCH %token CACHE_REMOVE %token CACHE_ADD %token CACHE_SUB %token CACHE_RAW_QUERY %token XDBG %token XLOG %token XLOG_BUF_SIZE %token XLOG_FORCE_COLOR %token XLOG_DEFAULT_LEVEL %token RAISE_EVENT %token SUBSCRIBE_EVENT %token CONSTRUCT_URI %token GET_TIMESTAMP %token SCRIPT_TRACE /* config vars. */ %token FORK %token DEBUG_MODE %token DEBUG %token ENABLE_ASSERTS %token ABORT_ON_ASSERT %token LOGLEVEL %token LOGSTDERROR %token LOGFACILITY %token LOGNAME %token AVP_ALIASES %token LISTEN %token MEMGROUP %token ALIAS %token AUTO_ALIASES %token DNS %token REV_DNS %token DNS_TRY_IPV6 %token DNS_TRY_NAPTR %token DNS_RETR_TIME %token DNS_RETR_NO %token DNS_SERVERS_NO %token DNS_USE_SEARCH %token MAX_WHILE_LOOPS %token CHILDREN %token CHECK_VIA %token SHM_HASH_SPLIT_PERCENTAGE %token SHM_SECONDARY_HASH_SIZE %token MEM_WARMING_ENABLED %token MEM_WARMING_PATTERN_FILE %token MEM_WARMING_PERCENTAGE %token MEMLOG %token MEMDUMP %token EXECMSGTHRESHOLD %token EXECDNSTHRESHOLD %token TCPTHRESHOLD %token EVENT_SHM_THRESHOLD %token EVENT_PKG_THRESHOLD %token QUERYBUFFERSIZE %token QUERYFLUSHTIME %token SIP_WARNING %token SOCK_MODE %token SOCK_USER %token SOCK_GROUP %token UNIX_SOCK %token UNIX_SOCK_CHILDREN %token UNIX_TX_TIMEOUT %token SERVER_SIGNATURE %token SERVER_HEADER %token USER_AGENT_HEADER %token LOADMODULE %token MPATH %token MODPARAM %token MAXBUFFER %token CHROOT %token WDIR %token MHOMED %token POLL_METHOD %token TCP_ACCEPT_ALIASES %token TCP_CHILDREN %token TCP_CONNECT_TIMEOUT %token TCP_CON_LIFETIME %token TCP_LISTEN_BACKLOG %token TCP_MAX_CONNECTIONS %token TCP_NO_NEW_CONN_BFLAG %token TCP_KEEPALIVE %token TCP_KEEPCOUNT %token TCP_KEEPIDLE %token TCP_KEEPINTERVAL %token TCP_MAX_MSG_TIME %token ADVERTISED_ADDRESS %token ADVERTISED_PORT %token DISABLE_CORE %token OPEN_FD_LIMIT %token MCAST_LOOPBACK %token MCAST_TTL %token TOS %token DISABLE_DNS_FAILOVER %token DISABLE_DNS_BLACKLIST %token DST_BLACKLIST %token DISABLE_STATELESS_FWD %token DB_VERSION_TABLE %token DB_DEFAULT_URL %token DB_MAX_ASYNC_CONNECTIONS %token DISABLE_503_TRANSLATION %token SYNC_TOKEN %token ASYNC_TOKEN /* operators */ %nonassoc EQUAL %nonassoc EQUAL_T %nonassoc GT %nonassoc LT %nonassoc GTE %nonassoc LTE %nonassoc DIFF %nonassoc MATCH %nonassoc NOTMATCH %nonassoc COLONEQ %nonassoc PLUSEQ %nonassoc MINUSEQ %nonassoc SLASHEQ %nonassoc MULTEQ %nonassoc MODULOEQ %nonassoc BANDEQ %nonassoc BOREQ %nonassoc BXOREQ %left OR AND %left BOR BAND BXOR BLSHIFT BRSHIFT %left PLUS MINUS SLASH MULT MODULO %right NOT BNOT /* values */ %token NUMBER %token ZERO %token ID %token STRING %token SCRIPTVAR %token IPV6ADDR /* other */ %token COMMA %token SEMICOLON %token RPAREN %token LPAREN %token LBRACE %token RBRACE %token LBRACK %token RBRACK %token SLASH %token AS %token USE_CHILDREN %token DOT %token CR %token COLON %token ANY %token SCRIPTVARERR /*non-terminals */ %type exp exp_elem exp_cond assignexp /*, condition*/ %type action actions cmd if_cmd stm exp_stm assign_cmd while_cmd foreach_cmd async_func %type switch_cmd switch_stm case_stms case_stm default_stm %type module_func_param %type ipv4 ipv6 ipv6addr ip %type ipnet %type script_var %type host %type listen_id %type listen_def %type id_lst %type alias_def %type phostport %type proto port any_proto %type host_sep %type uri_type %type equalop compop matchop strop intop %type assignop %type snumber %type route_name %type route_param %type folded_string %type multi_string /* * since "if_cmd" is inherently ambiguous, * skip 1 harmless shift/reduce conflict when compiling our grammar */ %expect 1 %% cfg: statements ; statements: statements statement {} | statement {} | statements error { yyerror(""); YYABORT;} ; statement: assign_stm | module_stm | {rt=REQUEST_ROUTE;} route_stm | {rt=FAILURE_ROUTE;} failure_route_stm | {rt=ONREPLY_ROUTE;} onreply_route_stm | {rt=BRANCH_ROUTE;} branch_route_stm | {rt=ERROR_ROUTE;} error_route_stm | {rt=LOCAL_ROUTE;} local_route_stm | {rt=STARTUP_ROUTE;} startup_route_stm | {rt=TIMER_ROUTE;} timer_route_stm | {rt=EVENT_ROUTE;} event_route_stm | CR /* null statement*/ ; listen_id: ip { tmp=ip_addr2a($1); if(tmp==0){ LM_CRIT("cfg. parser: bad ip address.\n"); $$=0; }else{ $$=pkg_malloc(strlen(tmp)+1); if ($$==0){ LM_CRIT("cfg. parser: out of memory.\n"); YYABORT; }else{ strncpy($$, tmp, strlen(tmp)+1); } } } | STRING { $$=pkg_malloc(strlen($1)+1); if ($$==0){ LM_CRIT("cfg. parser: out of memory.\n"); YYABORT; }else{ strncpy($$, $1, strlen($1)+1); } } | host { if ($1==0) { $$ = 0; } else { $$=pkg_malloc(strlen($1)+1); if ($$==0){ LM_CRIT("cfg. parser: out of memory.\n"); YYABORT; }else{ strncpy($$, $1, strlen($1)+1); } } } ; proto: ID { if (parse_proto((unsigned char *)$1, strlen($1), &i_tmp) < 0) { yyerrorf("cannot handle protocol <%s>\n", $1); YYABORT; } $$ = i_tmp; } ; port: NUMBER { $$=$1; } | ANY { $$=0; } ; snumber: NUMBER { $$=$1; } | PLUS NUMBER { $$=$2; } | MINUS NUMBER { $$=-$2; } ; phostport: proto COLON listen_id { $$=mk_listen_id($3, $1, 0); } | proto COLON listen_id COLON port { $$=mk_listen_id($3, $1, $5);} | proto COLON listen_id COLON error { $$=0; yyerror("port number expected"); YYABORT; } | NUMBER error { $$=0; yyerror("protocol expected"); YYABORT; } ; alias_def: listen_id { $$=mk_listen_id($1, PROTO_NONE, 0); } | ANY COLON listen_id { $$=mk_listen_id($3, PROTO_NONE, 0); } | ANY COLON listen_id COLON port { $$=mk_listen_id($3, PROTO_NONE, $5); } | ANY COLON listen_id COLON error { $$=0; yyerror(" port number expected"); } | phostport ; id_lst: alias_def { $$=$1 ; } | alias_def id_lst { $$=$1; $$->next=$2; } ; listen_def: phostport { $$=$1; } | phostport USE_CHILDREN NUMBER { $$=$1; $$->children=$3; } | phostport AS listen_id { $$=$1; set_listen_id_adv((struct socket_id *)$1, $3, 5060); } | phostport AS listen_id USE_CHILDREN NUMBER { $$=$1; set_listen_id_adv((struct socket_id *)$1, $3, 5060); $1->children=$5; } | phostport AS listen_id COLON port{ $$=$1; set_listen_id_adv((struct socket_id *)$1, $3, $5); } | phostport AS listen_id COLON port USE_CHILDREN NUMBER { $$=$1; set_listen_id_adv((struct socket_id *)$1, $3, $5); $1->children=$7; } ; any_proto: ANY { $$=PROTO_NONE; } | proto { $$=$1; } multi_string: STRING { $$=new_string($1); } | STRING multi_string { $$=new_string($1); $$->next=$2; } ; blst_elem: LPAREN any_proto COMMA ipnet COMMA port COMMA STRING RPAREN { s_tmp.s=$8; s_tmp.len=strlen($8); if (add_rule_to_list(&bl_head,&bl_tail,$4,&s_tmp,$6,$2,0)) { yyerror("failed to add backlist element\n");YYABORT; } } | NOT LPAREN any_proto COMMA ipnet COMMA port COMMA STRING RPAREN { s_tmp.s=$9; s_tmp.len=strlen($9); if (add_rule_to_list(&bl_head,&bl_tail,$5,&s_tmp, $7,$3,BLR_APPLY_CONTRARY)) { yyerror("failed to add backlist element\n");YYABORT; } } ; blst_elem_list: blst_elem_list COMMA blst_elem {} | blst_elem {} | blst_elem_list error { yyerror("bad black list element");} ; assign_stm: DEBUG EQUAL snumber { yyerror("\'debug\' is deprecated, use \'log_level\' instead\n");} | FORK EQUAL NUMBER {yyerror("fork is deprecated, use debug_mode\n");} | LOGLEVEL EQUAL snumber { /* in debug mode, force logging to DEBUG level*/ *log_level = debug_mode?L_DBG:$3; } | ENABLE_ASSERTS EQUAL NUMBER { enable_asserts=$3; } | ENABLE_ASSERTS EQUAL error { yyerror("boolean value expected"); } | ABORT_ON_ASSERT EQUAL NUMBER { abort_on_assert=$3; } | ABORT_ON_ASSERT EQUAL error { yyerror("boolean value expected"); } | DEBUG_MODE EQUAL NUMBER { debug_mode=$3; if (debug_mode) { *log_level = L_DBG;log_stderr=1;} } | DEBUG_MODE EQUAL error { yyerror("boolean value expected for debug_mode"); } | LOGSTDERROR EQUAL NUMBER /* in config-check or debug mode we force logging * to standard error */ { if (!config_check && !debug_mode) log_stderr=$3; } | LOGSTDERROR EQUAL error { yyerror("boolean value expected"); } | LOGFACILITY EQUAL ID { if ( (i_tmp=str2facility($3))==-1) yyerror("bad facility (see syslog(3) man page)"); if (!config_check) log_facility=i_tmp; } | LOGFACILITY EQUAL error { yyerror("ID expected"); } | LOGNAME EQUAL STRING { log_name=$3; } | LOGNAME EQUAL error { yyerror("string value expected"); } | DNS EQUAL NUMBER { received_dns|= ($3)?DO_DNS:0; } | DNS EQUAL error { yyerror("boolean value expected"); } | REV_DNS EQUAL NUMBER { received_dns|= ($3)?DO_REV_DNS:0; } | REV_DNS EQUAL error { yyerror("boolean value expected"); } | DNS_TRY_IPV6 EQUAL NUMBER { dns_try_ipv6=$3; } | DNS_TRY_IPV6 error { yyerror("boolean value expected"); } | DNS_TRY_NAPTR EQUAL NUMBER { dns_try_naptr=$3; } | DNS_TRY_NAPTR error { yyerror("boolean value expected"); } | DNS_RETR_TIME EQUAL NUMBER { dns_retr_time=$3; } | DNS_RETR_TIME error { yyerror("number expected"); } | DNS_RETR_NO EQUAL NUMBER { dns_retr_no=$3; } | DNS_RETR_NO error { yyerror("number expected"); } | DNS_SERVERS_NO EQUAL NUMBER { dns_servers_no=$3; } | DNS_SERVERS_NO error { yyerror("number expected"); } | DNS_USE_SEARCH EQUAL NUMBER { dns_search_list=$3; } | DNS_USE_SEARCH error { yyerror("boolean value expected"); } | MAX_WHILE_LOOPS EQUAL NUMBER { max_while_loops=$3; } | MAX_WHILE_LOOPS EQUAL error { yyerror("number expected"); } | MAXBUFFER EQUAL NUMBER { maxbuffer=$3; } | MAXBUFFER EQUAL error { yyerror("number expected"); } | CHILDREN EQUAL NUMBER { children_no=$3; } | CHILDREN EQUAL error { yyerror("number expected"); } | CHECK_VIA EQUAL NUMBER { check_via=$3; } | CHECK_VIA EQUAL error { yyerror("boolean value expected"); } | SHM_HASH_SPLIT_PERCENTAGE EQUAL NUMBER { #ifdef HP_MALLOC shm_hash_split_percentage=$3; #else yyerror("Cannot set parameter; Please recompile with support " "for HP_MALLOC"); #endif } | SHM_HASH_SPLIT_PERCENTAGE EQUAL error { yyerror("number expected"); } | SHM_SECONDARY_HASH_SIZE EQUAL NUMBER { #ifdef HP_MALLOC shm_secondary_hash_size=$3; #else yyerror("Cannot set parameter; Please recompile with support" " for HP_MALLOC"); #endif } | SHM_SECONDARY_HASH_SIZE EQUAL error { yyerror("number expected"); } | MEM_WARMING_ENABLED EQUAL NUMBER { #ifdef HP_MALLOC mem_warming_enabled = $3; #else yyerror("Cannot set parameter; Please recompile with support" " for HP_MALLOC"); #endif } | MEM_WARMING_ENABLED EQUAL error { yyerror("number expected"); } | MEM_WARMING_PATTERN_FILE EQUAL STRING { #ifdef HP_MALLOC mem_warming_pattern_file = $3; #else yyerror("Cannot set parameter; Please recompile with " "support for HP_MALLOC"); #endif } | MEM_WARMING_PATTERN_FILE EQUAL error { yyerror("string expected"); } | MEM_WARMING_PERCENTAGE EQUAL NUMBER { #ifdef HP_MALLOC mem_warming_percentage = $3; #else yyerror("Cannot set parameter; Please recompile with " "support for HP_MALLOC"); #endif } | MEM_WARMING_PERCENTAGE EQUAL error { yyerror("number expected"); } | MEMLOG EQUAL NUMBER { memlog=$3; memdump=$3; } | MEMLOG EQUAL error { yyerror("int value expected"); } | MEMDUMP EQUAL NUMBER { memdump=$3; } | MEMDUMP EQUAL error { yyerror("int value expected"); } | EXECMSGTHRESHOLD EQUAL NUMBER { execmsgthreshold=$3; } | EXECMSGTHRESHOLD EQUAL error { yyerror("int value expected"); } | EXECDNSTHRESHOLD EQUAL NUMBER { execdnsthreshold=$3; } | EXECDNSTHRESHOLD EQUAL error { yyerror("int value expected"); } | TCPTHRESHOLD EQUAL NUMBER { tcpthreshold=$3; } | TCPTHRESHOLD EQUAL error { yyerror("int value expected"); } | EVENT_SHM_THRESHOLD EQUAL NUMBER { #ifdef STATISTICS if ($3 < 0 || $3 > 100) yyerror("SHM threshold has to be a percentage between" " 0 and 100"); event_shm_threshold=$3; #else yyerror("statistics support not compiled in"); #endif /* STATISTICS */ } | EVENT_SHM_THRESHOLD EQUAL error { yyerror("int value expected"); } | EVENT_PKG_THRESHOLD EQUAL NUMBER { #ifdef STATISTICS if ($3 < 0 || $3 > 100) yyerror("PKG threshold has to be a percentage between " "0 and 100"); event_pkg_threshold=$3; #else yyerror("statistics support not compiled in"); #endif } | EVENT_PKG_THRESHOLD EQUAL error { yyerror("int value expected"); } | QUERYBUFFERSIZE EQUAL NUMBER { query_buffer_size=$3; } | QUERYBUFFERSIZE EQUAL error { yyerror("int value expected"); } | QUERYFLUSHTIME EQUAL NUMBER { query_flush_time=$3; } | QUERYFLUSHTIME EQUAL error { yyerror("int value expected"); } | SIP_WARNING EQUAL NUMBER { sip_warning=$3; } | SIP_WARNING EQUAL error { yyerror("boolean value expected"); } | CHROOT EQUAL STRING { chroot_dir=$3; } | CHROOT EQUAL ID { chroot_dir=$3; } | CHROOT EQUAL error { yyerror("string value expected"); } | WDIR EQUAL STRING { working_dir=$3; } | WDIR EQUAL ID { working_dir=$3; } | WDIR EQUAL error { yyerror("string value expected"); } | MHOMED EQUAL NUMBER { mhomed=$3; } | MHOMED EQUAL error { yyerror("boolean value expected"); } | POLL_METHOD EQUAL ID { io_poll_method=get_poll_type($3); if (io_poll_method==POLL_NONE){ LM_CRIT("bad poll method name:" " %s\n, try one of %s.\n", $3, poll_support); yyerror("bad poll_method " "value"); } } | POLL_METHOD EQUAL STRING { io_poll_method=get_poll_type($3); if (io_poll_method==POLL_NONE){ LM_CRIT("bad poll method name:" " %s\n, try one of %s.\n", $3, poll_support); yyerror("bad poll_method " "value"); } } | POLL_METHOD EQUAL error { yyerror("poll method name expected"); } | TCP_ACCEPT_ALIASES EQUAL NUMBER { tcp_accept_aliases=$3; } | TCP_ACCEPT_ALIASES EQUAL error { yyerror("boolean value expected"); } | TCP_CHILDREN EQUAL NUMBER { tcp_children_no=$3; } | TCP_CHILDREN EQUAL error { yyerror("number expected"); } | TCP_CONNECT_TIMEOUT EQUAL NUMBER { tcp_connect_timeout=$3; } | TCP_CONNECT_TIMEOUT EQUAL error { yyerror("number expected"); } | TCP_CON_LIFETIME EQUAL NUMBER { tcp_con_lifetime=$3; } | TCP_CON_LIFETIME EQUAL error { yyerror("number expected"); } | TCP_LISTEN_BACKLOG EQUAL NUMBER { tcp_listen_backlog=$3; } | TCP_LISTEN_BACKLOG EQUAL error { yyerror("number expected"); } | TCP_MAX_CONNECTIONS EQUAL NUMBER { tcp_max_connections=$3; } | TCP_MAX_CONNECTIONS EQUAL error { yyerror("number expected"); } | TCP_NO_NEW_CONN_BFLAG EQUAL NUMBER { tmp = NULL; fix_flag_name(tmp, $3); tcp_no_new_conn_bflag = get_flag_id_by_name(FLAG_TYPE_BRANCH, tmp); if (!flag_in_range( (flag_t)tcp_no_new_conn_bflag ) ) yyerror("invalid TCP no_new_conn Branch Flag"); flag_idx2mask( &tcp_no_new_conn_bflag ); } | TCP_NO_NEW_CONN_BFLAG EQUAL ID { tcp_no_new_conn_bflag = get_flag_id_by_name(FLAG_TYPE_BRANCH, $3); if (!flag_in_range( (flag_t)tcp_no_new_conn_bflag ) ) yyerror("invalid TCP no_new_conn Branch Flag"); flag_idx2mask( &tcp_no_new_conn_bflag ); } | TCP_NO_NEW_CONN_BFLAG EQUAL error { yyerror("number value expected"); } | TCP_KEEPALIVE EQUAL NUMBER { tcp_keepalive=$3; } | TCP_KEEPALIVE EQUAL error { yyerror("boolean value expected"); } | TCP_MAX_MSG_TIME EQUAL NUMBER { tcp_max_msg_time=$3; } | TCP_MAX_MSG_TIME EQUAL error { yyerror("boolean value expected"); } | TCP_KEEPCOUNT EQUAL NUMBER { #ifndef HAVE_TCP_KEEPCNT warn("cannot be enabled TCP_KEEPCOUNT (no OS support)"); #else tcp_keepcount=$3; #endif } | TCP_KEEPCOUNT EQUAL error { yyerror("int value expected"); } | TCP_KEEPIDLE EQUAL NUMBER { #ifndef HAVE_TCP_KEEPIDLE warn("cannot be enabled TCP_KEEPIDLE (no OS support)"); #else tcp_keepidle=$3; #endif } | TCP_KEEPIDLE EQUAL error { yyerror("int value expected"); } | TCP_KEEPINTERVAL EQUAL NUMBER { #ifndef HAVE_TCP_KEEPINTVL warn("cannot be enabled TCP_KEEPINTERVAL (no OS support)"); #else tcp_keepinterval=$3; #endif } | TCP_KEEPINTERVAL EQUAL error { yyerror("int value expected"); } | SERVER_SIGNATURE EQUAL NUMBER { server_signature=$3; } | SERVER_SIGNATURE EQUAL error { yyerror("boolean value expected"); } | SERVER_HEADER EQUAL STRING { server_header.s=$3; server_header.len=strlen($3); } | SERVER_HEADER EQUAL error { yyerror("string value expected"); } | USER_AGENT_HEADER EQUAL STRING { user_agent_header.s=$3; user_agent_header.len=strlen($3); } | USER_AGENT_HEADER EQUAL error { yyerror("string value expected"); } | XLOG_BUF_SIZE EQUAL NUMBER { xlog_buf_size = $3; } | XLOG_FORCE_COLOR EQUAL NUMBER { xlog_force_color = $3; } | XLOG_DEFAULT_LEVEL EQUAL NUMBER { xlog_default_level = $3; } | XLOG_BUF_SIZE EQUAL error { yyerror("number expected"); } | XLOG_FORCE_COLOR EQUAL error { yyerror("boolean value expected"); } | XLOG_DEFAULT_LEVEL EQUAL error { yyerror("number expected"); } | LISTEN EQUAL listen_def { if (add_listener($3, 0)!=0){ LM_CRIT("cfg. parser: failed" " to add listen address\n"); break; } } | LISTEN EQUAL error { yyerror("ip address or hostname " "expected (use quotes if the hostname includes" " config keywords)"); } | MEMGROUP EQUAL STRING COLON multi_string { /* convert STIRNG ($3) to an ID */ /* update the memstats type for each module */ #ifndef SHM_EXTRA_STATS LM_CRIT("SHM_EXTRA_STATS not defined"); YYABORT; #else #ifdef SHM_SHOW_DEFAULT_GROUP if(strcmp($3, "default") == 0){ LM_CRIT("default group name is not allowed"); YYABORT; } #endif for(tmp_mod = mod_names; tmp_mod; tmp_mod=tmp_mod->next){ if(strcmp($3, tmp_mod->s) == 0){ LM_CRIT("The same mem-group name is used twice: [%s] [%s]\n", $3, tmp_mod->s); YYABORT; } } tmp_mod = pkg_malloc(sizeof(struct multi_str)); if(!tmp_mod){ LM_CRIT("out of pkg memory"); YYABORT; } tmp_mod->s = $3; tmp_mod->next = mod_names; mod_names = tmp_mod; for (tmp_mod = $5; tmp_mod; tmp_mod = tmp_mod->next){ if(set_mem_idx(tmp_mod->s, mem_free_idx)){ YYABORT; } } mem_free_idx++; #endif } | MEMGROUP EQUAL STRING COLON error { yyerror("invalid or no module specified"); } | ALIAS EQUAL id_lst { for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) add_alias(lst_tmp->name, strlen(lst_tmp->name), lst_tmp->port, lst_tmp->proto); } | ALIAS EQUAL error { yyerror("hostname expected (use quotes" " if the hostname includes config keywords)"); } | AUTO_ALIASES EQUAL NUMBER { auto_aliases=$3; } | AUTO_ALIASES EQUAL error { yyerror("number expected"); } | ADVERTISED_ADDRESS EQUAL listen_id { if ($3) { default_global_address.s=$3; default_global_address.len=strlen($3); } } | ADVERTISED_ADDRESS EQUAL error {yyerror("ip address or hostname " "expected"); } | ADVERTISED_PORT EQUAL NUMBER { tmp = int2str($3, &i_tmp); if (i_tmp > default_global_port.len) default_global_port.s = pkg_realloc(default_global_port.s, i_tmp); if (!default_global_port.s) { LM_CRIT("cfg. parser: out of memory.\n"); YYABORT; } else { default_global_port.len = i_tmp; memcpy(default_global_port.s, tmp, default_global_port.len); } } |ADVERTISED_PORT EQUAL error {yyerror("ip address or hostname " "expected"); } | DISABLE_CORE EQUAL NUMBER { disable_core_dump=$3; } | DISABLE_CORE EQUAL error { yyerror("boolean value expected"); } | OPEN_FD_LIMIT EQUAL NUMBER { open_files_limit=$3; } | OPEN_FD_LIMIT EQUAL error { yyerror("number expected"); } | MCAST_LOOPBACK EQUAL NUMBER { #ifdef USE_MCAST mcast_loopback=$3; #else warn("no multicast support compiled in"); #endif } | MCAST_LOOPBACK EQUAL error { yyerror("boolean value expected"); } | MCAST_TTL EQUAL NUMBER { #ifdef USE_MCAST mcast_ttl=$3; #else warn("no multicast support compiled in"); #endif } | MCAST_TTL EQUAL error { yyerror("number expected as tos"); } | TOS EQUAL NUMBER { tos = $3; if (tos<=0) yyerror("invalid tos value"); } | TOS EQUAL ID { if (strcasecmp($3,"IPTOS_LOWDELAY")) { tos=IPTOS_LOWDELAY; } else if (strcasecmp($3,"IPTOS_THROUGHPUT")) { tos=IPTOS_THROUGHPUT; } else if (strcasecmp($3,"IPTOS_RELIABILITY")) { tos=IPTOS_RELIABILITY; #if defined(IPTOS_MINCOST) } else if (strcasecmp($3,"IPTOS_MINCOST")) { tos=IPTOS_MINCOST; #endif #if defined(IPTOS_LOWCOST) } else if (strcasecmp($3,"IPTOS_LOWCOST")) { tos=IPTOS_LOWCOST; #endif } else { yyerror("invalid tos value - allowed: " "IPTOS_LOWDELAY,IPTOS_THROUGHPUT," "IPTOS_RELIABILITY" #if defined(IPTOS_LOWCOST) ",IPTOS_LOWCOST" #endif #if defined(IPTOS_MINCOST) ",IPTOS_MINCOST" #endif "\n"); } } | TOS EQUAL error { yyerror("number expected"); } | MPATH EQUAL STRING { mpath=$3; strcpy(mpath_buf, $3); mpath_len=strlen($3); if(mpath_buf[mpath_len-1]!='/') { mpath_buf[mpath_len]='/'; mpath_len++; mpath_buf[mpath_len]='\0'; } } | MPATH EQUAL error { yyerror("string value expected"); } | DISABLE_DNS_FAILOVER EQUAL NUMBER { disable_dns_failover=$3; } | DISABLE_DNS_FAILOVER error { yyerror("boolean value expected"); } | DISABLE_DNS_BLACKLIST EQUAL NUMBER { disable_dns_blacklist=$3; } | DISABLE_DNS_BLACKLIST error { yyerror("boolean value expected"); } | DST_BLACKLIST EQUAL ID COLON LBRACE blst_elem_list RBRACE { s_tmp.s = $3; s_tmp.len = strlen($3); if (create_bl_head( BL_CORE_ID, BL_READONLY_LIST, bl_head, bl_tail, &s_tmp)==0) { yyerror("failed to create blacklist\n"); YYABORT; } bl_head = bl_tail = NULL; } | DISABLE_STATELESS_FWD EQUAL NUMBER { sl_fwd_disabled=$3; } | DB_VERSION_TABLE EQUAL STRING { db_version_table=$3; } | DB_VERSION_TABLE EQUAL error { yyerror("string value expected"); } | DB_DEFAULT_URL EQUAL STRING { db_default_url=$3; } | DB_DEFAULT_URL EQUAL error { yyerror("string value expected"); } | DB_MAX_ASYNC_CONNECTIONS EQUAL NUMBER { db_max_async_connections=$3; } | DB_MAX_ASYNC_CONNECTIONS EQUAL error { yyerror("integer value expected"); } | DISABLE_503_TRANSLATION EQUAL NUMBER { disable_503_translation=$3; } | DISABLE_503_TRANSLATION EQUAL error { yyerror("integer value expected"); } | error EQUAL { yyerror("unknown config variable"); } ; module_stm: LOADMODULE STRING { if (load_module($2) < 0) yyerrorf("failed to load module %s\n", $2); } | LOADMODULE error { yyerror("string expected"); } | MODPARAM LPAREN STRING COMMA STRING COMMA STRING RPAREN { if (set_mod_param_regex($3, $5, STR_PARAM, $7) != 0) { yyerrorf("Parameter <%s> not found in module <%s> - " "can't set", $5, $3); } } | MODPARAM LPAREN STRING COMMA STRING COMMA snumber RPAREN { if (set_mod_param_regex($3, $5, INT_PARAM, (void*)$7) != 0) { yyerrorf("Parameter <%s> not found in module <%s> - " "can't set", $5, $3); } } | MODPARAM error { yyerror("Invalid arguments"); } ; ip: ipv4 { $$=$1; } |ipv6 { $$=$1; } ; ipv4: NUMBER DOT NUMBER DOT NUMBER DOT NUMBER { $$=pkg_malloc( sizeof(struct ip_addr)); if ($$==0){ LM_CRIT("cfg. parser: " "out of memory\n"); YYABORT; }else{ memset($$, 0, sizeof(struct ip_addr)); $$->af=AF_INET; $$->len=4; if (($1>255) || ($1<0) || ($3>255) || ($3<0) || ($5>255) || ($5<0) || ($7>255) || ($7<0)){ yyerror("invalid ipv4" "address"); $$->u.addr32[0]=0; /* $$=0; */ }else{ $$->u.addr[0]=$1; $$->u.addr[1]=$3; $$->u.addr[2]=$5; $$->u.addr[3]=$7; /* $$=htonl( ($1<<24)| ($3<<16)| ($5<<8)|$7 ); */ } } } ; ipv6addr: IPV6ADDR { $$=pkg_malloc(sizeof(struct ip_addr)); if ($$==0){ LM_CRIT("ERROR: cfg. parser: out of memory.\n"); YYABORT; }else{ memset($$, 0, sizeof(struct ip_addr)); $$->af=AF_INET6; $$->len=16; if (inet_pton(AF_INET6, $1, $$->u.addr)<=0){ yyerror("bad ipv6 address"); } } } ; ipv6: ipv6addr { $$=$1; } | LBRACK ipv6addr RBRACK {$$=$2; } ; folded_string: STRING STRING { $$ = pkg_malloc( strlen($1) + strlen($2) + 1); if ($$==0){ yyerror("cfg. parser: out of memory"); YYABORT; } else { strcpy($$,$1); strcat($$,$2); pkg_free($1); pkg_free($2); } } | folded_string STRING { $$ = pkg_malloc( strlen($1) + strlen($2) + 1); if ($$==0){ LM_CRIT("ERROR: cfg. parser: out of memory.\n"); YYABORT; } else { strcpy($$,$1); strcat($$,$2); pkg_free($1); pkg_free($2); } } route_name: ID { $$ = $1; } | NUMBER { tmp=int2str($1, &i_tmp); if (($$=pkg_malloc(i_tmp+1))==0) { yyerror("cfg. parser: out of memory.\n"); YYABORT; } memcpy( $$, tmp, i_tmp); $$[i_tmp] = 0; } |STRING { $$ = $1; } ; route_stm: ROUTE LBRACE actions RBRACE { if (rlist[DEFAULT_RT].a!=0) { yyerror("overwriting default " "request routing table"); YYABORT; } push($3, &rlist[DEFAULT_RT].a); } | ROUTE LBRACK route_name RBRACK LBRACE actions RBRACE { if ( strtol($3,&tmp,10)==0 && *tmp==0) { /* route[0] detected */ if (rlist[DEFAULT_RT].a!=0) { yyerror("overwriting(2) default " "request routing table"); YYABORT; } push($6, &rlist[DEFAULT_RT].a); } else { i_tmp = get_script_route_idx($3,rlist,RT_NO,1); if (i_tmp==-1) YYABORT; push($6, &rlist[i_tmp].a); } } | ROUTE error { yyerror("invalid route statement"); } ; failure_route_stm: ROUTE_FAILURE LBRACK route_name RBRACK LBRACE actions RBRACE { i_tmp = get_script_route_idx($3,failure_rlist, FAILURE_RT_NO,1); if (i_tmp==-1) YYABORT; push($6, &failure_rlist[i_tmp].a); } | ROUTE_FAILURE error { yyerror("invalid failure_route statement"); } ; onreply_route_stm: ROUTE_ONREPLY LBRACE actions RBRACE { if (onreply_rlist[DEFAULT_RT].a!=0) { yyerror("overwriting default " "onreply routing table"); YYABORT; } push($3, &onreply_rlist[DEFAULT_RT].a); } | ROUTE_ONREPLY LBRACK route_name RBRACK LBRACE actions RBRACE { i_tmp = get_script_route_idx($3,onreply_rlist, ONREPLY_RT_NO,1); if (i_tmp==-1) YYABORT; push($6, &onreply_rlist[i_tmp].a); } | ROUTE_ONREPLY error { yyerror("invalid onreply_route statement"); } ; branch_route_stm: ROUTE_BRANCH LBRACK route_name RBRACK LBRACE actions RBRACE { i_tmp = get_script_route_idx($3,branch_rlist, BRANCH_RT_NO,1); if (i_tmp==-1) YYABORT; push($6, &branch_rlist[i_tmp].a); } | ROUTE_BRANCH error { yyerror("invalid branch_route statement"); } ; error_route_stm: ROUTE_ERROR LBRACE actions RBRACE { if (error_rlist.a!=0) { yyerror("overwriting default " "error routing table"); YYABORT; } push($3, &error_rlist.a); } | ROUTE_ERROR error { yyerror("invalid error_route statement"); } ; local_route_stm: ROUTE_LOCAL LBRACE actions RBRACE { if (local_rlist.a!=0) { yyerror("re-definition of local " "route detected"); YYABORT; } push($3, &local_rlist.a); } | ROUTE_LOCAL error { yyerror("invalid local_route statement"); } ; startup_route_stm: ROUTE_STARTUP LBRACE actions RBRACE { if (startup_rlist.a!=0) { yyerror("re-definition of startup " "route detected"); YYABORT; } push($3, &startup_rlist.a); } | ROUTE_STARTUP error { yyerror("invalid startup_route statement"); } ; timer_route_stm: ROUTE_TIMER LBRACK route_name COMMA NUMBER RBRACK LBRACE actions RBRACE { i_tmp = 0; while (timer_rlist[i_tmp].a!=0 && i_tmp < TIMER_RT_NO) { i_tmp++; } if(i_tmp == TIMER_RT_NO) { yyerror("Too many timer routes defined\n"); YYABORT; } timer_rlist[i_tmp].interval = $5; push($8, &timer_rlist[i_tmp].a); } | ROUTE_TIMER error { yyerror("invalid timer_route statement"); } ; event_route_stm: ROUTE_EVENT LBRACK route_name RBRACK LBRACE actions RBRACE { i_tmp = 1; while (event_rlist[i_tmp].a !=0 && i_tmp < EVENT_RT_NO) { if (strcmp($3, event_rlist[i_tmp].name) == 0) { LM_ERR("Script route <%s> redefined\n", $3); YYABORT; } i_tmp++; } if (i_tmp == EVENT_RT_NO) { yyerror("Too many event routes defined\n"); YYABORT; } event_rlist[i_tmp].name = $3; event_rlist[i_tmp].mode = EV_ROUTE_SYNC; push($6, &event_rlist[i_tmp].a); } | ROUTE_EVENT LBRACK route_name COMMA SYNC_TOKEN RBRACK LBRACE actions RBRACE { i_tmp = 1; while (event_rlist[i_tmp].a !=0 && i_tmp < EVENT_RT_NO) { if (strcmp($3, event_rlist[i_tmp].name) == 0) { LM_ERR("Script route <%s> redefined\n", $3); YYABORT; } i_tmp++; } if (i_tmp == EVENT_RT_NO) { yyerror("Too many event routes defined\n"); YYABORT; } event_rlist[i_tmp].name = $3; event_rlist[i_tmp].mode = EV_ROUTE_SYNC; push($8, &event_rlist[i_tmp].a); } | ROUTE_EVENT LBRACK route_name COMMA ASYNC_TOKEN RBRACK LBRACE actions RBRACE { i_tmp = 1; while (event_rlist[i_tmp].a !=0 && i_tmp < EVENT_RT_NO) { if (strcmp($3, event_rlist[i_tmp].name) == 0) { LM_ERR("Script route <%s> redefined\n", $3); YYABORT; } i_tmp++; } if (i_tmp == EVENT_RT_NO) { yyerror("Too many event routes defined\n"); YYABORT; } event_rlist[i_tmp].name = $3; event_rlist[i_tmp].mode = EV_ROUTE_ASYNC; push($8, &event_rlist[i_tmp].a); } | ROUTE_EVENT error { yyerror("invalid event_route statement"); } ; exp: exp AND exp { $$=mk_exp(AND_OP, $1, $3); } | exp OR exp { $$=mk_exp(OR_OP, $1, $3); } | NOT exp { $$=mk_exp(NOT_OP, $2, 0); } | LPAREN exp RPAREN { $$=mk_exp(EVAL_OP, $2, 0); } | LBRACK assignexp RBRACK { $$=$2; } | exp_elem { $$=$1; } ; equalop: EQUAL_T {$$=EQUAL_OP; } | DIFF {$$=DIFF_OP; } ; compop: GT {$$=GT_OP; } | LT {$$=LT_OP; } | GTE {$$=GTE_OP; } | LTE {$$=LTE_OP; } ; matchop: MATCH {$$=MATCH_OP; } | NOTMATCH {$$=NOTMATCH_OP; } ; intop: equalop {$$=$1; } | compop {$$=$1; } ; strop: equalop {$$=$1; } | compop {$$=$1; } | matchop {$$=$1; } ; uri_type: URI {$$=URI_O;} | FROM_URI {$$=FROM_URI_O;} | TO_URI {$$=TO_URI_O;} ; script_var: SCRIPTVAR { spec = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); if (spec==NULL){ yyerror("no more pkg memory\n"); YYABORT; } memset(spec, 0, sizeof(pv_spec_t)); tstr.s = $1; tstr.len = strlen(tstr.s); if(pv_parse_spec(&tstr, spec)==NULL) { yyerror("unknown script variable"); } $$ = spec; } | SCRIPTVARERR { $$=0; yyerror("invalid script variable name"); } ; exp_elem: exp_cond {$$=$1; } | exp_stm {$$=mk_elem( NO_OP, ACTION_O, 0, ACTIONS_ST, $1 ); } | snumber {$$=mk_elem( NO_OP, NUMBER_O, 0, NUMBER_ST, (void*)$1 ); } | script_var { $$=mk_elem(NO_OP, SCRIPTVAR_O,0,SCRIPTVAR_ST,(void*)$1); } | uri_type strop host {$$ = mk_elem($2, $1, 0, STR_ST, $3); } | DSTIP equalop ipnet { $$=mk_elem($2, DSTIP_O, 0, NET_ST, $3); } | DSTIP strop host { $$=mk_elem($2, DSTIP_O, 0, STR_ST, $3); } | SRCIP equalop ipnet { $$=mk_elem($2, SRCIP_O, 0, NET_ST, $3); } | SRCIP strop host { $$=mk_elem($2, SRCIP_O, 0, STR_ST, $3); } ; exp_cond: METHOD strop STRING {$$= mk_elem($2, METHOD_O, 0, STR_ST, $3); } | METHOD strop ID {$$ = mk_elem($2, METHOD_O, 0, STR_ST, $3); } | METHOD strop error { $$=0; yyerror("string expected"); } | METHOD error { $$=0; yyerror("invalid operator," "== , !=, or =~ expected"); } | script_var strop script_var { $$=mk_elem( $2, SCRIPTVAR_O,(void*)$1,SCRIPTVAR_ST,(void*)$3); } | script_var strop STRING { $$=mk_elem( $2, SCRIPTVAR_O,(void*)$1,STR_ST,$3); } | script_var strop ID { $$=mk_elem( $2, SCRIPTVAR_O,(void*)$1,STR_ST,$3); } | script_var intop snumber { $$=mk_elem( $2, SCRIPTVAR_O,(void*)$1,NUMBER_ST,(void *)$3); } | script_var equalop MYSELF { $$=mk_elem( $2, SCRIPTVAR_O,(void*)$1, MYSELF_ST, 0); } | script_var equalop NULLV { $$=mk_elem( $2, SCRIPTVAR_O,(void*)$1, NULLV_ST, 0); } | uri_type strop STRING {$$ = mk_elem($2, $1, 0, STR_ST, $3); } | uri_type equalop MYSELF { $$=mk_elem($2, $1, 0, MYSELF_ST, 0); } | uri_type strop error { $$=0; yyerror("string or MYSELF expected"); } | uri_type error { $$=0; yyerror("invalid operator," " == , != or =~ expected"); } | SRCPORT intop NUMBER { $$=mk_elem($2, SRCPORT_O, 0, NUMBER_ST, (void *) $3 ); } | SRCPORT intop error { $$=0; yyerror("number expected"); } | SRCPORT error { $$=0; yyerror("==, !=, <,>, >= or <= expected"); } | DSTPORT intop NUMBER { $$=mk_elem($2, DSTPORT_O, 0, NUMBER_ST, (void *) $3 ); } | DSTPORT intop error { $$=0; yyerror("number expected"); } | DSTPORT error { $$=0; yyerror("==, !=, <,>, >= or <= expected"); } | PROTO intop proto { $$=mk_elem($2, PROTO_O, 0, NUMBER_ST, (void *) $3 ); } | PROTO intop error { $$=0; yyerror("protocol expected (udp, tcp or tls)"); } | PROTO error { $$=0; yyerror("equal/!= operator expected"); } | AF intop NUMBER { $$=mk_elem($2, AF_O, 0, NUMBER_ST, (void *) $3 ); } | AF intop error { $$=0; yyerror("number expected"); } | AF error { $$=0; yyerror("equal/!= operator expected"); } | MSGLEN intop NUMBER { $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) $3 ); } | MSGLEN intop MAX_LEN { $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) BUF_SIZE); } | MSGLEN intop error { $$=0; yyerror("number expected"); } | MSGLEN error { $$=0; yyerror("equal/!= operator expected"); } | SRCIP strop STRING { s_tmp.s=$3; s_tmp.len=strlen($3); ip_tmp=str2ip(&s_tmp); if (ip_tmp==0) ip_tmp=str2ip6(&s_tmp); if (ip_tmp){ $$=mk_elem($2, SRCIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) ); }else{ $$=mk_elem($2, SRCIP_O, 0, STR_ST, $3); } } | SRCIP equalop MYSELF { $$=mk_elem($2, SRCIP_O, 0, MYSELF_ST, 0); } | SRCIP strop error { $$=0; yyerror( "ip address or hostname" "expected" ); } | SRCIP error { $$=0; yyerror("invalid operator, ==, != or =~ expected");} | DSTIP strop STRING { s_tmp.s=$3; s_tmp.len=strlen($3); ip_tmp=str2ip(&s_tmp); if (ip_tmp==0) ip_tmp=str2ip6(&s_tmp); if (ip_tmp){ $$=mk_elem($2, DSTIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) ); }else{ $$=mk_elem($2, DSTIP_O, 0, STR_ST, $3); } } | DSTIP equalop MYSELF { $$=mk_elem($2, DSTIP_O, 0, MYSELF_ST, 0); } | DSTIP strop error { $$=0; yyerror( "ip address or hostname" "expected" ); } | DSTIP error { $$=0; yyerror("invalid operator, ==, != or =~ expected");} | MYSELF equalop uri_type { $$=mk_elem($2, $3, 0, MYSELF_ST, 0); } | MYSELF equalop SRCIP { $$=mk_elem($2, SRCIP_O, 0, MYSELF_ST, 0); } | MYSELF equalop DSTIP { $$=mk_elem($2, DSTIP_O, 0, MYSELF_ST, 0); } | MYSELF equalop error { $$=0; yyerror(" URI, SRCIP or DSTIP expected"); } | MYSELF error { $$=0; yyerror ("invalid operator, == or != expected"); } ; ipnet: ip SLASH ip { $$=mk_net($1, $3); } | ip SLASH NUMBER { if (($3<0) || ($3>(long)$1->len*8)){ yyerror("invalid bit number in netmask"); $$=0; }else{ $$=mk_net_bitlen($1, $3); /* $$=mk_net($1, htonl( ($3)?~( (1<<(32-$3))-1 ):0 ) ); */ } } | ip { $$=mk_net_bitlen($1, $1->len*8); } | ip SLASH error { $$=0; yyerror("netmask (eg:255.0.0.0 or 8) expected"); } ; host_sep: DOT {$$=".";} | MINUS {$$="-"; } ; host: ID { $$=$1; } | host host_sep ID { $$=(char*)pkg_malloc(strlen($1)+1+strlen($3)+1); if ($$==0){ LM_CRIT("cfg. parser: memory allocation" " failure while parsing host\n"); YYABORT; }else{ memcpy($$, $1, strlen($1)); $$[strlen($1)]=*$2; memcpy($$+strlen($1)+1, $3, strlen($3)); $$[strlen($1)+1+strlen($3)]=0; } pkg_free($1); pkg_free($3); } | host DOT error { $$=0; pkg_free($1); yyerror("invalid hostname (use quotes if hostname has config keywords)"); } ; assignop: EQUAL { $$ = EQ_T; } | COLONEQ { $$ = COLONEQ_T; } | PLUSEQ { $$ = PLUSEQ_T; } | MINUSEQ { $$ = MINUSEQ_T;} | SLASHEQ { $$ = DIVEQ_T; } | MULTEQ { $$ = MULTEQ_T; } | MODULOEQ { $$ = MODULOEQ_T; } | BANDEQ { $$ = BANDEQ_T; } | BOREQ { $$ = BOREQ_T; } | BXOREQ { $$ = BXOREQ_T; } ; assignexp : snumber { $$ = mk_elem(VALUE_OP, NUMBERV_O, (void*)$1, 0, 0); } | STRING { $$ = mk_elem(VALUE_OP, STRINGV_O, $1, 0, 0); } | ID { $$ = mk_elem(VALUE_OP, STRINGV_O, $1, 0, 0); } | script_var { $$ = mk_elem(VALUE_OP, SCRIPTVAR_O, $1, 0, 0); } | exp_cond { $$= $1; } | cmd { $$=mk_elem( NO_OP, ACTION_O, 0, ACTIONS_ST, $1 ); } | assignexp PLUS assignexp { $$ = mk_elem(PLUS_OP, EXPR_O, $1, EXPR_ST, $3); } | assignexp MINUS assignexp { $$ = mk_elem(MINUS_OP, EXPR_O, $1, EXPR_ST, $3); } | assignexp MULT assignexp { $$ = mk_elem(MULT_OP, EXPR_O, $1, EXPR_ST, $3); } | assignexp SLASH assignexp { $$ = mk_elem(DIV_OP, EXPR_O, $1, EXPR_ST, $3); } | assignexp MODULO assignexp { $$ = mk_elem(MODULO_OP, EXPR_O, $1, EXPR_ST, $3); } | assignexp BAND assignexp { $$ = mk_elem(BAND_OP, EXPR_O, $1, EXPR_ST, $3); } | assignexp BOR assignexp { $$ = mk_elem(BOR_OP, EXPR_O, $1, EXPR_ST, $3); } | assignexp BXOR assignexp { $$ = mk_elem(BXOR_OP, EXPR_O, $1, EXPR_ST, $3); } | assignexp BLSHIFT assignexp { $$ = mk_elem(BLSHIFT_OP, EXPR_O, $1, EXPR_ST, $3); } | assignexp BRSHIFT assignexp { $$ = mk_elem(BRSHIFT_OP, EXPR_O, $1, EXPR_ST, $3); } | BNOT assignexp { $$ = mk_elem(BNOT_OP, EXPR_O, $2, 0, 0); } | LPAREN assignexp RPAREN { $$ = $2; } ; assign_cmd: script_var assignop assignexp { if(!pv_is_w($1)) yyerror("invalid left operand in assignment"); if($1->trans!=0) yyerror("transformations not accepted in left side " "of assignment"); mk_action2( $$, $2, SCRIPTVAR_ST, EXPR_ST, $1, $3); } | script_var EQUAL NULLV { if(!pv_is_w($1)) yyerror("invalid left operand in assignment"); if($1->trans!=0) yyerror("transformations not accepted in left side " "of assignment"); mk_action2( $$, EQ_T, SCRIPTVAR_ST, NULLV_ST, $1, 0); } | script_var COLONEQ NULLV { if(!pv_is_w($1)) yyerror("invalid left operand in assignment"); /* not all can get NULL with := */ switch($1->type) { case PVT_AVP: break; default: yyerror("invalid left operand in NULL assignment"); } if($1->trans!=0) yyerror("transformations not accepted in left side " "of assignment"); mk_action2( $$, COLONEQ_T, SCRIPTVAR_ST, NULLV_ST, $1, 0); } ; exp_stm: cmd { $$=$1; } | if_cmd { $$=$1; } | assign_cmd { $$=$1; } | LBRACE actions RBRACE { $$=$2; } | LBRACE RBRACE { $$=0; } ; stm: action { $$=$1; } | LBRACE actions RBRACE { $$=$2; } | LBRACE RBRACE { $$=0; } ; actions: actions action {$$=append_action($1, $2); } | action {$$=$1;} | actions error { $$=0; yyerror("bad command!)"); } ; action: cmd SEMICOLON {$$=$1;} | if_cmd {$$=$1;} | while_cmd { $$=$1;} | foreach_cmd { $$=$1;} | switch_cmd {$$=$1;} | assign_cmd SEMICOLON {$$=$1;} | SEMICOLON /* null action */ {$$=0;} | cmd error { $$=0; yyerror("bad command: missing ';'?"); } ; if_cmd: IF exp stm { mk_action3( $$, IF_T, EXPR_ST, ACTIONS_ST, NOSUBTYPE, $2, $3, 0); } | IF exp stm ELSE stm { mk_action3( $$, IF_T, EXPR_ST, ACTIONS_ST, ACTIONS_ST, $2, $3, $5); } ; while_cmd: WHILE exp stm { mk_action2( $$, WHILE_T, EXPR_ST, ACTIONS_ST, $2, $3); } ; foreach_cmd: FOR LPAREN script_var IN script_var RPAREN stm { if ($3->type != PVT_SCRIPTVAR && $3->type != PVT_AVP && $3->type != PVT_JSON) { yyerror("\nfor-each statement: only \"var\", \"avp\" " "and \"json\" iterators are supported!"); } mk_action3( $$, FOR_EACH_T, SCRIPTVAR_ST, SCRIPTVAR_ST, ACTIONS_ST, $3, $5, $7); } ; switch_cmd: SWITCH LPAREN script_var RPAREN LBRACE switch_stm RBRACE { mk_action2( $$, SWITCH_T, SCRIPTVAR_ST, ACTIONS_ST, $3, $6); } ; switch_stm: case_stms default_stm { $$=append_action($1, $2); } | case_stms { $$=$1; } ; case_stms: case_stms case_stm {$$=append_action($1, $2); } | case_stm {$$=$1;} ; case_stm: CASE snumber COLON actions SBREAK SEMICOLON { mk_action3( $$, CASE_T, NUMBER_ST, ACTIONS_ST, NUMBER_ST, (void*)$2, $4, (void*)1); } | CASE snumber COLON SBREAK SEMICOLON { mk_action3( $$, CASE_T, NUMBER_ST, ACTIONS_ST, NUMBER_ST, (void*)$2, 0, (void*)1); } | CASE snumber COLON actions { mk_action3( $$, CASE_T, NUMBER_ST, ACTIONS_ST, NUMBER_ST, (void*)$2, $4, (void*)0); } | CASE snumber COLON { mk_action3( $$, CASE_T, NUMBER_ST, ACTIONS_ST, NUMBER_ST, (void*)$2, 0, (void*)0); } | CASE STRING COLON actions SBREAK SEMICOLON { mk_action3( $$, CASE_T, STR_ST, ACTIONS_ST, NUMBER_ST, (void*)$2, $4, (void*)1); } | CASE STRING COLON SBREAK SEMICOLON { mk_action3( $$, CASE_T, STR_ST, ACTIONS_ST, NUMBER_ST, (void*)$2, 0, (void*)1); } | CASE STRING COLON actions { mk_action3( $$, CASE_T, STR_ST, ACTIONS_ST, NUMBER_ST, (void*)$2, $4, (void*)0); } | CASE STRING COLON { mk_action3( $$, CASE_T, STR_ST, ACTIONS_ST, NUMBER_ST, (void*)$2, 0, (void*)0); } ; default_stm: DEFAULT COLON actions { mk_action2( $$, DEFAULT_T, ACTIONS_ST, 0, $3, 0); } | DEFAULT COLON { mk_action2( $$, DEFAULT_T, ACTIONS_ST, 0, 0, 0); } ; module_func_param: STRING { elems[1].type = STRING_ST; elems[1].u.data = $1; $$=1; } | module_func_param COMMA STRING { if ($1+1>=MAX_ACTION_ELEMS) { yyerror("too many arguments " "in function\n"); $$=0; } elems[$1+1].type = STRING_ST; elems[$1+1].u.data = $3; $$=$1+1; } | COMMA { elems[1].type = NULLV_ST; elems[1].u.data = NULL; elems[2].type = NULLV_ST; elems[2].u.data = NULL; $$=2; } | COMMA STRING { elems[1].type = NULLV_ST; elems[1].u.data = NULL; elems[2].type = STRING_ST; elems[2].u.data = $2; $$=2; } | module_func_param COMMA { if ($1+1>=MAX_ACTION_ELEMS) { yyerror("too many arguments " "in function\n"); $$=0; } elems[$1+1].type = NULLV_ST; elems[$1+1].u.data = NULL; $$=$1+1; } | NUMBER { $$=0; yyerror("numbers used as parameters -" " they should be quoted"); } | COMMA NUMBER { $$=0; yyerror("numbers used as parameters -" " they should be quoted"); } | module_func_param COMMA NUMBER { $$=0; yyerror("numbers used as parameters -" " they should be quoted"); } ; route_param: STRING { route_elems[0].type = STRING_ST; route_elems[0].u.data = $1; $$=1; } | NUMBER { route_elems[0].type = NUMBER_ST; route_elems[0].u.data = (void*)(long)$1; $$=1; } | NULLV { route_elems[0].type = NULLV_ST; route_elems[0].u.data = 0; $$=1; } | script_var { route_elems[0].type = SCRIPTVAR_ST; route_elems[0].u.data = $1; $$=1; } | route_param COMMA STRING { if ($1>=MAX_ACTION_ELEMS) { yyerror("too many arguments in function\n"); $$=-1; } else { route_elems[$1].type = STRING_ST; route_elems[$1].u.data = $3; $$=$1+1; } } | route_param COMMA NUMBER { if ($1>=MAX_ACTION_ELEMS) { yyerror("too many arguments in function\n"); $$=-1; } else { route_elems[$1].type = NUMBER_ST; route_elems[$1].u.data = (void*)(long)$3; $$=$1+1; } } | route_param COMMA script_var { if ($1+1>=MAX_ACTION_ELEMS) { yyerror("too many arguments in function\n"); $$=-1; } else { route_elems[$1].type = SCRIPTVAR_ST; route_elems[$1].u.data = $3; $$=$1+1; } } ; async_func: ID LPAREN RPAREN { cmd_tmp=(void*)find_acmd_export_t($1, 0); if (cmd_tmp==0){ yyerrorf("unknown async command <%s>, " "missing loadmodule?", $1); $$=0; }else{ elems[0].type = ACMD_ST; elems[0].u.data = cmd_tmp; mk_action_($$, AMODULE_T, 1, elems); } } | ID LPAREN module_func_param RPAREN { cmd_tmp=(void*)find_acmd_export_t($1, $3); if (cmd_tmp==0){ yyerrorf("unknown async command <%s>, " "missing loadmodule?", $1); $$=0; }else{ elems[0].type = ACMD_ST; elems[0].u.data = cmd_tmp; mk_action_($$, AMODULE_T, $3+1, elems); } } | ID LPAREN error RPAREN { $$=0; yyerrorf("bad arguments for command <%s>", $1); } | ID error { $$=0; yyerrorf("bare word <%s> found, command calls need '()'", $1); } ; cmd: FORWARD LPAREN STRING RPAREN { mk_action2( $$, FORWARD_T, STRING_ST, 0, $3, 0); } | FORWARD LPAREN RPAREN { mk_action2( $$, FORWARD_T, 0, 0, 0, 0); } | FORWARD error { $$=0; yyerror("missing '(' or ')' ?"); } | FORWARD LPAREN error RPAREN { $$=0; yyerror("bad forward " "argument"); } | SEND LPAREN STRING RPAREN { mk_action2( $$, SEND_T, STRING_ST, 0, $3, 0); } | SEND LPAREN STRING COMMA STRING RPAREN { mk_action2( $$, SEND_T, STRING_ST, STRING_ST, $3, $5); } | SEND error { $$=0; yyerror("missing '(' or ')' ?"); } | SEND LPAREN error RPAREN { $$=0; yyerror("bad send" "argument"); } | ASSERT LPAREN exp COMMA STRING RPAREN { mk_action2( $$, ASSERT_T, EXPR_ST, STRING_ST, $3, $5); } | DROP LPAREN RPAREN {mk_action2( $$, DROP_T,0, 0, 0, 0); } | DROP {mk_action2( $$, DROP_T,0, 0, 0, 0); } | EXIT LPAREN RPAREN {mk_action2( $$, EXIT_T,0, 0, 0, 0); } | EXIT {mk_action2( $$, EXIT_T,0, 0, 0, 0); } | RETURN LPAREN snumber RPAREN {mk_action2( $$, RETURN_T, NUMBER_ST, 0, (void*)$3, 0); } | RETURN LPAREN script_var RPAREN {mk_action2( $$, RETURN_T, SCRIPTVAR_ST, 0, (void*)$3, 0); } | RETURN LPAREN RPAREN {mk_action2( $$, RETURN_T, NUMBER_ST, 0, (void*)1, 0); } | RETURN {mk_action2( $$, RETURN_T, NUMBER_ST, 0, (void*)1, 0); } | LOG_TOK LPAREN STRING RPAREN {mk_action2( $$, LOG_T, NUMBER_ST, STRING_ST,(void*)4,$3); } | LOG_TOK LPAREN snumber COMMA STRING RPAREN {mk_action2( $$, LOG_T, NUMBER_ST, STRING_ST, (void*)$3, $5); } | LOG_TOK error { $$=0; yyerror("missing '(' or ')' ?"); } | LOG_TOK LPAREN error RPAREN { $$=0; yyerror("bad log" "argument"); } | SETFLAG LPAREN NUMBER RPAREN { mk_action2($$, SETFLAG_T, NUMBER_ST, 0, (void *)$3, 0 ); } | SETFLAG LPAREN ID RPAREN {mk_action2($$, SETFLAG_T, STR_ST, 0, (void *)$3, 0 ); } | SETFLAG error { $$=0; yyerror("missing '(' or ')'?"); } | RESETFLAG LPAREN NUMBER RPAREN {mk_action2( $$, RESETFLAG_T, NUMBER_ST, 0, (void *)$3, 0 ); } | RESETFLAG LPAREN ID RPAREN {mk_action2( $$, RESETFLAG_T, STR_ST, 0, (void *)$3, 0 ); } | RESETFLAG error { $$=0; yyerror("missing '(' or ')'?"); } | ISFLAGSET LPAREN NUMBER RPAREN {mk_action2( $$, ISFLAGSET_T, NUMBER_ST, 0, (void *)$3, 0 ); } | ISFLAGSET LPAREN ID RPAREN {mk_action2( $$, ISFLAGSET_T, STR_ST, 0, (void *)$3, 0 ); } | ISFLAGSET error { $$=0; yyerror("missing '(' or ')'?"); } | SETBFLAG LPAREN NUMBER COMMA NUMBER RPAREN {mk_action2( $$, SETBFLAG_T, NUMBER_ST, NUMBER_ST, (void *)$3, (void *)$5 ); } | SETBFLAG LPAREN NUMBER COMMA ID RPAREN {mk_action2( $$, SETBFLAG_T, NUMBER_ST, STR_ST, (void *)$3, (void *)$5 ); } | SETBFLAG LPAREN NUMBER RPAREN {mk_action2( $$, SETBFLAG_T, NUMBER_ST, NUMBER_ST, 0, (void *)$3 ); } | SETBFLAG LPAREN ID RPAREN {mk_action2( $$, SETBFLAG_T, NUMBER_ST, STR_ST, 0, (void *)$3 ); } | SETBFLAG error { $$=0; yyerror("missing '(' or ')'?"); } | RESETBFLAG LPAREN NUMBER COMMA NUMBER RPAREN {mk_action2( $$, RESETBFLAG_T, NUMBER_ST, NUMBER_ST, (void *)$3, (void *)$5 ); } | RESETBFLAG LPAREN NUMBER COMMA ID RPAREN {mk_action2( $$, RESETBFLAG_T, NUMBER_ST, STR_ST, (void *)$3, (void *)$5 ); } | RESETBFLAG LPAREN NUMBER RPAREN {mk_action2( $$, RESETBFLAG_T, NUMBER_ST, NUMBER_ST, 0, (void *)$3 ); } | RESETBFLAG LPAREN ID RPAREN {mk_action2( $$, RESETBFLAG_T, NUMBER_ST, STR_ST, 0, (void *)$3 ); } | RESETBFLAG error { $$=0; yyerror("missing '(' or ')'?"); } | ISBFLAGSET LPAREN NUMBER COMMA NUMBER RPAREN {mk_action2( $$, ISBFLAGSET_T, NUMBER_ST, NUMBER_ST, (void *)$3, (void *)$5 ); } | ISBFLAGSET LPAREN NUMBER COMMA ID RPAREN {mk_action2( $$, ISBFLAGSET_T, NUMBER_ST, STR_ST, (void *)$3, (void *)$5 ); } | ISBFLAGSET LPAREN NUMBER RPAREN {mk_action2( $$, ISBFLAGSET_T, NUMBER_ST, NUMBER_ST, 0, (void *)$3 ); } | ISBFLAGSET LPAREN ID RPAREN {mk_action2( $$, ISBFLAGSET_T, NUMBER_ST, STR_ST, 0, (void *)$3 ); } | ISBFLAGSET error { $$=0; yyerror("missing '(' or ')'?"); } | ERROR LPAREN STRING COMMA STRING RPAREN {mk_action2( $$, ERROR_T, STRING_ST, STRING_ST, $3, $5); } | ERROR error { $$=0; yyerror("missing '(' or ')' ?"); } | ERROR LPAREN error RPAREN { $$=0; yyerror("bad error" "argument"); } | ROUTE LPAREN route_name RPAREN { i_tmp = get_script_route_idx( $3, rlist, RT_NO, 0); if (i_tmp==-1) yyerror("too many script routes"); mk_action2( $$, ROUTE_T, NUMBER_ST, 0, (void*)(long)i_tmp, 0); } | ROUTE LPAREN route_name COMMA route_param RPAREN { i_tmp = get_script_route_idx( $3, rlist, RT_NO, 0); if (i_tmp==-1) yyerror("too many script routes"); if ($5 <= 0) yyerror("too many route parameters"); /* duplicate the list */ a_tmp = pkg_malloc($5 * sizeof(action_elem_t)); if (!a_tmp) { yyerror("no more pkg memory"); YYABORT; } memcpy(a_tmp, route_elems, $5*sizeof(action_elem_t)); mk_action3( $$, ROUTE_T, NUMBER_ST, /* route idx */ NUMBER_ST, /* number of params */ SCRIPTVAR_ELEM_ST, /* parameters */ (void*)(long)i_tmp, (void*)(long)$5, (void*)a_tmp); } | ROUTE error { $$=0; yyerror("missing '(' or ')' ?"); } | ROUTE LPAREN error RPAREN { $$=0; yyerror("bad route" "argument"); } | SET_HOST LPAREN STRING RPAREN { mk_action2( $$, SET_HOST_T, STR_ST, 0, $3, 0); } | SET_HOST error { $$=0; yyerror("missing '(' or ')' ?"); } | SET_HOST LPAREN error RPAREN { $$=0; yyerror("bad argument, " "string expected"); } | PREFIX LPAREN STRING RPAREN { mk_action2( $$, PREFIX_T, STR_ST, 0, $3, 0); } | PREFIX error { $$=0; yyerror("missing '(' or ')' ?"); } | PREFIX LPAREN error RPAREN { $$=0; yyerror("bad argument, " "string expected"); } | STRIP_TAIL LPAREN NUMBER RPAREN { mk_action2( $$, STRIP_TAIL_T, NUMBER_ST, 0, (void *) $3, 0); } | STRIP_TAIL error { $$=0; yyerror("missing '(' or ')' ?"); } | STRIP_TAIL LPAREN error RPAREN { $$=0; yyerror("bad argument, " "number expected"); } | STRIP LPAREN NUMBER RPAREN { mk_action2( $$, STRIP_T, NUMBER_ST, 0, (void *) $3, 0); } | STRIP error { $$=0; yyerror("missing '(' or ')' ?"); } | STRIP LPAREN error RPAREN { $$=0; yyerror("bad argument, " "number expected"); } | APPEND_BRANCH LPAREN STRING COMMA STRING RPAREN { { qvalue_t q; rc = str2q(&q, $5, strlen($5)); if (rc < 0) yyerrorf("bad qvalue (%.*s): %s", strlen($5), $5, qverr2str(rc)); mk_action2( $$, APPEND_BRANCH_T, STR_ST, NUMBER_ST, $3, (void *)(long)q); } } | APPEND_BRANCH LPAREN STRING RPAREN { mk_action2( $$, APPEND_BRANCH_T, STR_ST, NUMBER_ST, $3, (void *)Q_UNSPECIFIED) ; } | APPEND_BRANCH LPAREN RPAREN { mk_action2( $$, APPEND_BRANCH_T, STR_ST, NUMBER_ST, 0, (void *)Q_UNSPECIFIED) ; } | APPEND_BRANCH { mk_action2( $$, APPEND_BRANCH_T, STR_ST, NUMBER_ST, 0, (void *)Q_UNSPECIFIED ) ; } | REMOVE_BRANCH LPAREN NUMBER RPAREN { mk_action1($$, REMOVE_BRANCH_T, NUMBER_ST, (void*)$3);} | REMOVE_BRANCH LPAREN script_var RPAREN { mk_action1( $$, REMOVE_BRANCH_T, SCRIPTVAR_ST, $3);} | PV_PRINTF LPAREN STRING COMMA STRING RPAREN { spec = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); memset(spec, 0, sizeof(pv_spec_t)); tstr.s = $3; tstr.len = strlen(tstr.s); if(pv_parse_spec(&tstr, spec)==NULL) { yyerror("unknown script variable in first parameter"); } if(!pv_is_w(spec)) yyerror("read-only script variable in first parameter"); pvmodel = 0; tstr.s = $5; tstr.len = strlen(tstr.s); if(pv_parse_format(&tstr, &pvmodel)<0) { yyerror("error in second parameter"); } mk_action2( $$, PV_PRINTF_T, SCRIPTVAR_ST, SCRIPTVAR_ELEM_ST, spec, pvmodel) ; } | PV_PRINTF LPAREN script_var COMMA STRING RPAREN { if(!pv_is_w($3)) yyerror("read-only script variable in first parameter"); pvmodel = 0; tstr.s = $5; tstr.len = strlen(tstr.s); if(pv_parse_format(&tstr, &pvmodel)<0) { yyerror("error in second parameter"); } mk_action2( $$, PV_PRINTF_T, SCRIPTVAR_ST, SCRIPTVAR_ELEM_ST, $3, pvmodel) ; } | SET_HOSTPORT LPAREN STRING RPAREN { mk_action2( $$, SET_HOSTPORT_T, STR_ST, 0, $3, 0); } | SET_HOSTPORT error { $$=0; yyerror("missing '(' or ')' ?"); } | SET_HOSTPORT LPAREN error RPAREN { $$=0; yyerror("bad argument," " string expected"); } | SET_PORT LPAREN STRING RPAREN { mk_action2( $$, SET_PORT_T, STR_ST, 0, $3, 0); } | SET_PORT error { $$=0; yyerror("missing '(' or ')' ?"); } | SET_PORT LPAREN error RPAREN { $$=0; yyerror("bad argument, " "string expected"); } | SET_USER LPAREN STRING RPAREN { mk_action2( $$, SET_USER_T, STR_ST, 0, $3, 0); } | SET_USER error { $$=0; yyerror("missing '(' or ')' ?"); } | SET_USER LPAREN error RPAREN { $$=0; yyerror("bad argument, " "string expected"); } | SET_USERPASS LPAREN STRING RPAREN { mk_action2( $$, SET_USERPASS_T, STR_ST, 0, $3, 0); } | SET_USERPASS error { $$=0; yyerror("missing '(' or ')' ?"); } | SET_USERPASS LPAREN error RPAREN { $$=0; yyerror("bad argument, " "string expected"); } | SET_URI LPAREN STRING RPAREN { mk_action2( $$, SET_URI_T, STR_ST, 0, $3, 0); } | SET_URI error { $$=0; yyerror("missing '(' or ')' ?"); } | SET_URI LPAREN error RPAREN { $$=0; yyerror("bad argument, " "string expected"); } | REVERT_URI LPAREN RPAREN { mk_action2( $$, REVERT_URI_T, 0,0,0,0); } | REVERT_URI { mk_action2( $$, REVERT_URI_T, 0,0,0,0); } | SET_DSTURI LPAREN STRING RPAREN { mk_action2( $$, SET_DSTURI_T, STR_ST, 0, $3, 0); } | SET_DSTURI error { $$=0; yyerror("missing '(' or ')' ?"); } | SET_DSTURI LPAREN error RPAREN { $$=0; yyerror("bad argument, " "string expected"); } | RESET_DSTURI LPAREN RPAREN { mk_action2( $$, RESET_DSTURI_T, 0,0,0,0); } | RESET_DSTURI { mk_action2( $$, RESET_DSTURI_T, 0,0,0,0); } | ISDSTURISET LPAREN RPAREN { mk_action2( $$, ISDSTURISET_T, 0,0,0,0);} | ISDSTURISET { mk_action2( $$, ISDSTURISET_T, 0,0,0,0); } | FORCE_RPORT LPAREN RPAREN { mk_action2( $$, FORCE_RPORT_T, 0, 0, 0, 0); } | FORCE_RPORT { mk_action2( $$, FORCE_RPORT_T,0, 0, 0, 0); } | FORCE_LOCAL_RPORT LPAREN RPAREN { mk_action2( $$, FORCE_LOCAL_RPORT_T,0, 0, 0, 0); } | FORCE_LOCAL_RPORT { mk_action2( $$, FORCE_LOCAL_RPORT_T,0, 0, 0, 0); } | FORCE_TCP_ALIAS LPAREN NUMBER RPAREN { mk_action2( $$, FORCE_TCP_ALIAS_T,NUMBER_ST, 0, (void*)$3, 0); } | FORCE_TCP_ALIAS LPAREN RPAREN { mk_action2( $$, FORCE_TCP_ALIAS_T,0, 0, 0, 0); } | FORCE_TCP_ALIAS { mk_action2( $$, FORCE_TCP_ALIAS_T,0, 0, 0, 0); } | FORCE_TCP_ALIAS LPAREN error RPAREN {$$=0; yyerror("bad argument, number expected"); } | SET_ADV_ADDRESS LPAREN listen_id RPAREN { mk_action2( $$, SET_ADV_ADDR_T, STR_ST, 0, $3, 0); } | SET_ADV_ADDRESS LPAREN error RPAREN { $$=0; yyerror("bad argument, " "string expected"); } | SET_ADV_ADDRESS error {$$=0; yyerror("missing '(' or ')' ?"); } | SET_ADV_PORT LPAREN NUMBER RPAREN { tstr.s = int2str($3, &tstr.len); if (!(tmp = pkg_malloc(tstr.len + 1))) { LM_CRIT("out of pkg memory\n"); $$ = 0; YYABORT; } else { memcpy(tmp, tstr.s, tstr.len); tmp[tstr.len] = '\0'; mk_action2($$, SET_ADV_PORT_T, STR_ST, 0, tmp, 0); } } | SET_ADV_PORT LPAREN STRING RPAREN { mk_action2($$, SET_ADV_PORT_T, STR_ST, NOSUBTYPE, $3, NULL); } | SET_ADV_PORT LPAREN error RPAREN { $$=0; yyerror("bad argument " "(string or integer expected)"); } | SET_ADV_PORT error {$$=0; yyerror("missing '(' or ')' ?"); } | FORCE_SEND_SOCKET LPAREN phostport RPAREN { mk_action2( $$, FORCE_SEND_SOCKET_T, SOCKID_ST, 0, $3, 0); } | FORCE_SEND_SOCKET LPAREN error RPAREN { $$=0; yyerror("bad argument," " proto:host[:port] expected"); } | FORCE_SEND_SOCKET error {$$=0; yyerror("missing '(' or ')' ?"); } | SERIALIZE_BRANCHES LPAREN NUMBER RPAREN { mk_action2( $$, SERIALIZE_BRANCHES_T, NUMBER_ST, 0, (void*)(long)$3, 0); } | SERIALIZE_BRANCHES LPAREN error RPAREN {$$=0; yyerror("bad argument," " number expected"); } | SERIALIZE_BRANCHES error {$$=0; yyerror("missing '(' or ')' ?"); } | NEXT_BRANCHES LPAREN RPAREN { mk_action2( $$, NEXT_BRANCHES_T, 0, 0, 0, 0); } | NEXT_BRANCHES LPAREN error RPAREN {$$=0; yyerror("no argument is" " expected"); } | NEXT_BRANCHES error {$$=0; yyerror("missing '(' or ')' ?"); } | USE_BLACKLIST LPAREN STRING RPAREN { mk_action2( $$, USE_BLACKLIST_T, STRING_ST, 0, $3, 0); } | USE_BLACKLIST LPAREN error RPAREN {$$=0; yyerror("bad argument," " string expected"); } | USE_BLACKLIST error {$$=0; yyerror("missing '(' or ')' ?"); } | UNUSE_BLACKLIST LPAREN STRING RPAREN { mk_action2( $$, UNUSE_BLACKLIST_T, STRING_ST, 0, $3, 0); } | UNUSE_BLACKLIST LPAREN error RPAREN {$$=0; yyerror("bad argument," " string expected"); } | UNUSE_BLACKLIST error {$$=0; yyerror("missing '(' or ')' ?"); } | CACHE_STORE LPAREN STRING COMMA STRING COMMA STRING RPAREN { mk_action3( $$, CACHE_STORE_T, STR_ST, STR_ST, STR_ST, $3, $5, $7); } | CACHE_STORE LPAREN STRING COMMA STRING COMMA STRING COMMA NUMBER RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = STR_ST; elems[2].u.data = $7; elems[3].type = NUMBER_ST; elems[3].u.number = $9; mk_action_($$, CACHE_STORE_T, 4, elems); } | CACHE_STORE LPAREN STRING COMMA STRING COMMA STRING COMMA script_var RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = STR_ST; elems[2].u.data = $7; elems[3].type = SCRIPTVAR_ST; elems[3].u.data = $9; mk_action_($$, CACHE_STORE_T, 4, elems); } | CACHE_REMOVE LPAREN STRING COMMA STRING RPAREN { mk_action2( $$, CACHE_REMOVE_T, STR_ST, STR_ST, $3, $5); } | CACHE_FETCH LPAREN STRING COMMA STRING COMMA script_var RPAREN { mk_action3( $$, CACHE_FETCH_T, STR_ST, STR_ST, SCRIPTVAR_ST, $3, $5, $7); } | CACHE_COUNTER_FETCH LPAREN STRING COMMA STRING COMMA script_var RPAREN { mk_action3( $$, CACHE_COUNTER_FETCH_T, STR_ST, STR_ST, SCRIPTVAR_ST, $3, $5, $7); } | CACHE_ADD LPAREN STRING COMMA STRING COMMA NUMBER COMMA NUMBER RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = NUMBER_ST; elems[2].u.number = $7; elems[3].type = NUMBER_ST; elems[3].u.number = $9; mk_action_($$, CACHE_ADD_T, 4, elems); } | CACHE_ADD LPAREN STRING COMMA STRING COMMA script_var COMMA NUMBER RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = SCRIPTVAR_ST; elems[2].u.data = $7; elems[3].type = NUMBER_ST; elems[3].u.number = $9; mk_action_($$, CACHE_ADD_T, 4, elems); } | CACHE_ADD LPAREN STRING COMMA STRING COMMA NUMBER COMMA NUMBER COMMA script_var RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = NUMBER_ST; elems[2].u.number = $7; elems[3].type = NUMBER_ST; elems[3].u.number = $9; elems[4].type = SCRIPTVAR_ST; elems[4].u.data = $11; mk_action_($$, CACHE_ADD_T, 5, elems); } | CACHE_ADD LPAREN STRING COMMA STRING COMMA script_var COMMA NUMBER COMMA script_var RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = SCRIPTVAR_ST; elems[2].u.data = $7; elems[3].type = NUMBER_ST; elems[3].u.number = $9; elems[4].type = SCRIPTVAR_ST; elems[4].u.data = $11; mk_action_($$, CACHE_ADD_T, 5, elems); } | CACHE_SUB LPAREN STRING COMMA STRING COMMA NUMBER COMMA NUMBER RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = NUMBER_ST; elems[2].u.number = $7; elems[3].type = NUMBER_ST; elems[3].u.number = $9; mk_action_($$, CACHE_SUB_T, 4, elems); } | CACHE_SUB LPAREN STRING COMMA STRING COMMA script_var COMMA NUMBER RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = SCRIPTVAR_ST; elems[2].u.data = $7; elems[3].type = NUMBER_ST; elems[3].u.number = $9; mk_action_($$, CACHE_SUB_T, 4, elems); } | CACHE_SUB LPAREN STRING COMMA STRING COMMA NUMBER COMMA NUMBER COMMA script_var RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = NUMBER_ST; elems[2].u.number = $7; elems[3].type = NUMBER_ST; elems[3].u.number = $9; elems[4].type = SCRIPTVAR_ST; elems[4].u.data = $11; mk_action_($$, CACHE_SUB_T, 5, elems); } | CACHE_SUB LPAREN STRING COMMA STRING COMMA script_var COMMA NUMBER COMMA script_var RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = SCRIPTVAR_ST; elems[2].u.data = $7; elems[3].type = NUMBER_ST; elems[3].u.number = $9; elems[4].type = SCRIPTVAR_ST; elems[4].u.data = $11; mk_action_($$, CACHE_SUB_T, 5, elems); } | CACHE_RAW_QUERY LPAREN STRING COMMA STRING COMMA STRING RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = STR_ST; elems[2].u.data = $7; mk_action_($$, CACHE_RAW_QUERY_T, 3, elems); } | CACHE_RAW_QUERY LPAREN STRING COMMA STRING RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; mk_action_($$, CACHE_RAW_QUERY_T, 2, elems); } | ID LPAREN RPAREN { cmd_tmp=(void*)find_cmd_export_t($1, 0, rt); if (cmd_tmp==0){ if (find_cmd_export_t($1, 0, 0)) { yyerror("Command cannot be " "used in the block\n"); } else { yyerrorf("unknown command <%s>, " "missing loadmodule?", $1); } $$=0; }else{ elems[0].type = CMD_ST; elems[0].u.data = cmd_tmp; mk_action_($$, MODULE_T, 1, elems); } } | ID LPAREN module_func_param RPAREN { cmd_tmp=(void*)find_cmd_export_t($1,$3,rt); if (cmd_tmp==0){ if (find_cmd_export_t($1, $3, 0)) { yyerror("Command cannot be " "used in the block\n"); } else { yyerrorf("unknown command <%s>, " "missing loadmodule?", $1); } $$=0; }else{ elems[0].type = CMD_ST; elems[0].u.data = cmd_tmp; mk_action_($$, MODULE_T, $3+1, elems); } } | ID LPAREN error RPAREN { $$=0; yyerrorf("bad arguments for " "command <%s>", $1); } | ID error { $$=0; yyerrorf("bare word <%s> found, command calls need '()'", $1); } | XDBG LPAREN STRING RPAREN { mk_action1($$, XDBG_T, STR_ST, $3); } | XDBG LPAREN folded_string RPAREN { mk_action1($$, XDBG_T, STR_ST, $3); } | XLOG LPAREN STRING RPAREN { mk_action1($$, XLOG_T, STR_ST, $3); } | XLOG LPAREN folded_string RPAREN { mk_action1($$, XLOG_T, STR_ST, $3); } | XLOG LPAREN STRING COMMA STRING RPAREN { mk_action2($$, XLOG_T, STR_ST, STR_ST, $3, $5); } | XLOG LPAREN STRING COMMA folded_string RPAREN { mk_action2($$, XLOG_T, STR_ST, STR_ST, $3, $5); } | RAISE_EVENT LPAREN STRING RPAREN { mk_action1($$, RAISE_EVENT_T, STR_ST, $3); } | RAISE_EVENT LPAREN STRING COMMA script_var RPAREN { mk_action2($$, RAISE_EVENT_T, STR_ST, SCRIPTVAR_ST, $3, $5); } | RAISE_EVENT LPAREN STRING COMMA script_var COMMA script_var RPAREN { mk_action3($$, RAISE_EVENT_T, STR_ST, SCRIPTVAR_ST, SCRIPTVAR_ST, $3, $5, $7); } | SUBSCRIBE_EVENT LPAREN STRING COMMA STRING RPAREN { mk_action2($$, SUBSCRIBE_EVENT_T, STR_ST, STR_ST, $3, $5); } | SUBSCRIBE_EVENT LPAREN STRING COMMA STRING COMMA NUMBER RPAREN { mk_action3($$, SUBSCRIBE_EVENT_T, STR_ST, STR_ST, NUMBER_ST, $3, $5, (void*)(long)$7); } | CONSTRUCT_URI LPAREN STRING COMMA STRING COMMA STRING COMMA STRING COMMA STRING COMMA script_var RPAREN { elems[0].type = STR_ST; elems[0].u.data = $3; elems[1].type = STR_ST; elems[1].u.data = $5; elems[2].type = STR_ST; elems[2].u.data = $7; elems[3].type = STR_ST; elems[3].u.data = $9; elems[4].type = STR_ST; elems[4].u.data = $11; elems[5].type = SCRIPTVAR_ST; elems[5].u.data = $13; mk_action_($$, CONSTRUCT_URI_T,6,elems); } | GET_TIMESTAMP LPAREN script_var COMMA script_var RPAREN { elems[0].type = SCRIPTVAR_ST; elems[0].u.data = $3; elems[1].type = SCRIPTVAR_ST; elems[1].u.data = $5; mk_action_($$, GET_TIMESTAMP_T,2,elems); } | SCRIPT_TRACE LPAREN RPAREN { mk_action2($$, SCRIPT_TRACE_T, 0, 0, 0, 0); } | SCRIPT_TRACE LPAREN NUMBER COMMA STRING RPAREN { pvmodel = 0; tstr.s = $5; tstr.len = strlen(tstr.s); if(pv_parse_format(&tstr, &pvmodel)<0) yyerror("error in second parameter"); mk_action2($$, SCRIPT_TRACE_T, NUMBER_ST, SCRIPTVAR_ELEM_ST, (void *)$3, pvmodel); } | SCRIPT_TRACE LPAREN NUMBER COMMA STRING COMMA STRING RPAREN { pvmodel = 0; tstr.s = $5; tstr.len = strlen(tstr.s); if(pv_parse_format(&tstr, &pvmodel)<0) yyerror("error in second parameter"); mk_action3($$, SCRIPT_TRACE_T, NUMBER_ST, SCRIPTVAR_ELEM_ST, STR_ST, (void *)$3, pvmodel, $7); } | ASYNC_TOKEN LPAREN async_func COMMA route_name RPAREN { i_tmp = get_script_route_idx( $5, rlist, RT_NO, 0); if (i_tmp==-1) yyerror("too many script routes"); mk_action2($$, ASYNC_T, ACTIONS_ST, NUMBER_ST, $3, (void*)(long)i_tmp); } ; %% static inline void warn(char* s) { LM_WARN("warning in config file %s, line %d, column %d-%d: %s\n", get_cfg_file_name, line, startcolumn, column, s); } static void yyerror(char* s) { LM_CRIT("parse error in config file %s, line %d, column %d-%d: %s\n", get_cfg_file_name, line, startcolumn, column, s); cfg_errors++; } #define ERROR_MAXLEN 1024 static void yyerrorf(char *fmt, ...) { char *tmp = pkg_malloc(ERROR_MAXLEN); va_list ap; va_start(ap, fmt); vsnprintf(tmp, ERROR_MAXLEN, fmt, ap); yyerror(tmp); pkg_free(tmp); va_end(ap); } static struct socket_id* mk_listen_id(char* host, enum sip_protos proto, int port) { struct socket_id* l; l=pkg_malloc(sizeof(struct socket_id)); if (l==0){ LM_CRIT("cfg. parser: out of memory.\n"); }else{ l->name = host; l->adv_name = NULL; l->adv_port = 0; l->proto = proto; l->port = port; l->children = 0; l->next = NULL; } return l; } static struct multi_str *new_string(char *s) { struct multi_str *ms = pkg_malloc(sizeof(struct multi_str)); if (!ms) { LM_CRIT("cfg. parser: out of memory.\n"); }else{ ms->s = s; ms->next = NULL; } return ms; } static struct socket_id* set_listen_id_adv(struct socket_id* sock, char *adv_name, int adv_port) { sock->adv_name=adv_name; sock->adv_port=adv_port; return sock; } opensips-2.2.2/config.h000066400000000000000000000144231300170765700150050ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-05 DEFAULT_*_URL introduced (jiri) * 2003-07-04 fixed SRV lookup prefix for TLS/sips (andrei) * 2007-02-16 Added an OPENSER_OID define to localize OpenSER's IANA assigned * OID under the enterprise branch (jmagder) * 2013-09-17 TLS_DH_PARAMS_FILE added (mehmet) */ /*! * \file * \brief Main Configuration settings */ #ifndef config_h #define config_h #define SIP_PORT 5060 /*!< default sip port if none specified */ #define SIPS_PORT 5061 /*! default sip port for tls if none specified */ #define CFG_FILE CFG_DIR "opensips.cfg" #define MEM_WARMING_DEFAULT_PATTERN_FILE CFG_DIR "mem_warming_pattern" #define MEM_WARMING_DEFAULT_PERCENTAGE 75 #define TLS_PKEY_FILE CFG_DIR "tls/ckey.pem" #define TLS_CERT_FILE CFG_DIR "tls/cert.pem" #define TLS_CA_FILE 0 /*!< no CA list file by default */ #define TLS_CA_DIRECTORY "/etc/pki/CA/" #define TLS_DH_PARAMS_FILE 0 /*!< no DH params file by default */ #define MAX_LISTEN 16 /*!< maximum number of addresses on which we will listen */ #define CHILD_NO 8 /*!< default number of child processes started */ #define RT_NO 100 /*!< routing tables number */ #define FAILURE_RT_NO RT_NO /*!< on_failure routing tables number */ #define ONREPLY_RT_NO RT_NO /*!< on_reply routing tables number */ #define BRANCH_RT_NO RT_NO /*!< T-branch routing tables number */ #define TIMER_RT_NO RT_NO /*!< Timer routing tables number */ #define EVENT_RT_NO RT_NO /*!< Event routing tables number */ #define DEFAULT_RT 0 /*!< default routing table */ #define MAX_REC_LEV 100 /*!< maximum number of recursive calls */ #define ROUTE_MAX_REC_LEV 100 /*!< maximum number of recursive calls for route()*/ #define MAX_URI_SIZE 1024 /*!< used when rewriting URIs */ #define MAX_PATH_SIZE 255 /*!< maximum length of Path header */ #define MY_VIA "Via: SIP/2.0/UDP " #define MY_VIA_LEN (sizeof(MY_VIA) - 1) #define CONTENT_LENGTH "Content-Length: " #define CONTENT_LENGTH_LEN (sizeof(CONTENT_LENGTH)-1) #define USER_AGENT "User-Agent: OpenSIPS (" VERSION " (" ARCH "/" OS"))" /*!< Default User-Agent header */ #define USER_AGENT_LEN (sizeof(USER_AGENT)-1) #define SERVER_HDR "Server: OpenSIPS (" VERSION " (" ARCH "/" OS"))" /*!< Default Server: header */ #define SERVER_HDR_LEN (sizeof(SERVER_HDR)-1) #define MAX_WARNING_LEN 256 #define MY_BRANCH ";branch=" #define MY_BRANCH_LEN (sizeof(MY_BRANCH) - 1) #define MAX_PORT_LEN 7 /*!< ':' + max 5 letters + \\0 */ #define CRLF "\r\n" #define CRLF_LEN (sizeof(CRLF) - 1) #define RECEIVED ";received=" #define RECEIVED_LEN (sizeof(RECEIVED) - 1) #define TRANSPORT_PARAM ";transport=" #define TRANSPORT_PARAM_LEN (sizeof(TRANSPORT_PARAM) - 1) #define TOTAG_TOKEN ";tag=" #define TOTAG_TOKEN_LEN (sizeof(TOTAG_TOKEN)-1) #define RPORT ";rport=" #define RPORT_LEN (sizeof(RPORT) - 1) #define ID_PARAM ";i=" #define ID_PARAM_LEN (sizeof(ID_PARAM) - 1) #define SRV_UDP_PREFIX "_sip._udp." #define SRV_UDP_PREFIX_LEN (sizeof(SRV_UDP_PREFIX) - 1) #define SRV_TCP_PREFIX "_sip._tcp." #define SRV_TCP_PREFIX_LEN (sizeof(SRV_TCP_PREFIX) - 1) #define SRV_SCTP_PREFIX "_sip._sctp." #define SRV_SCTP_PREFIX_LEN (sizeof(SRV_SCTP_PREFIX) - 1) #define SRV_TLS_PREFIX "_sips._tcp." #define SRV_TLS_PREFIX_LEN (sizeof(SRV_TLS_PREFIX) - 1) #define SRV_WS_PREFIX "_ws._tcp." #define SRV_WS_PREFIX_LEN (sizeof(SRV_WS_PREFIX) - 1) #define SRV_WSS_PREFIX "_wss._tcp." #define SRV_WSS_PREFIX_LEN (sizeof(SRV_WSS_PREFIX) - 1) #define SRV_MAX_PREFIX_LEN SRV_TLS_PREFIX_LEN #ifdef HP_MALLOC #define PKG_MEM_SIZE 16 /*!< Used only if PKG_MALLOC is defined*/ #else #define PKG_MEM_SIZE 2 /*!< Used only if PKG_MALLOC is defined*/ #endif #define SHM_MEM_SIZE 32 /*!< Used if SH_MEM is defined*/ #define SHM_MAX_SECONDARY_HASH_SIZE 32 #define DEFAULT_SHM_HASH_SPLIT_PERCENTAGE 1 /*!< Used if SH_MEM is defined*/ #define DEFAULT_SHM_SECONDARY_HASH_SIZE 8 #define TIMER_TICK 1 /*!< one second */ #define UTIMER_TICK 100*1000 /*!< 100 milliseconds*/ /*!< dimensioning buckets in q_malloc size of the size2bucket table; everything beyond that asks for a variable-size kilo-bucket */ #define MAX_FIXED_BLOCK 3072 #define BLOCK_STEP 512 /*!< distance of kilo-buckets */ #define MAX_BUCKET 15 /*!< maximum number of possible buckets */ /*! \brief receive buffer size \note preferably set low to avoid terror of excessively huge messages; they are useless anyway */ #define BUF_SIZE 65535 /*!< forwarding -- Via buffer dimensioning */ #define MAX_VIA_LINE_SIZE 240 #define MAX_RECEIVED_SIZE 57 #define MAX_RPORT_SIZE 13 #define MAX_BRANCHES 12 /*!< maximum number of branches per transaction */ #define MCOOKIE "z9hG4bK" /*!< magic cookie for transaction matching as defined in RFC3261 */ #define MCOOKIE_LEN (sizeof(MCOOKIE)-1) /*! \brief Maximum length of values appended to Via-branch parameter */ #define MAX_BRANCH_PARAM_LEN (MCOOKIE_LEN+8 /*! /* Pointer to the current processing context */ context_p current_processing_ctx = NULL; unsigned int context_sizes[CONTEXT_COUNT]; enum osips_context_val { CONTEXT_INT_TYPE, CONTEXT_STR_TYPE, CONTEXT_PTR_TYPE, CONTEXT_COUNT_TYPE }; static unsigned int type_sizes[CONTEXT_COUNT][CONTEXT_COUNT_TYPE]; static unsigned int type_offsets[CONTEXT_COUNT][CONTEXT_COUNT_TYPE]; /* vector of destroy functions */ static context_destroy_f *context_destroy_array[CONTEXT_COUNT]; static void register_context_destroy(context_destroy_f f, enum osips_context ctx, enum osips_context_val t) { static int count = 0; /* contains all counters */ context_destroy_f *tmp; int pos = 0; int i; /* * group all functions based on their types: * first the int functions, then the str and pointers the last */ switch (t) { case CONTEXT_PTR_TYPE: pos += type_sizes[ctx][CONTEXT_PTR_TYPE]; case CONTEXT_STR_TYPE: pos += type_sizes[ctx][CONTEXT_STR_TYPE]; case CONTEXT_INT_TYPE: pos += type_sizes[ctx][CONTEXT_INT_TYPE]; break; default: LM_ERR("should not get here with ctx %d\n", t); return; } /* TODO: check whether this should be in pkg or shm? */ tmp = pkg_realloc(context_destroy_array[ctx], (count + 1) * sizeof(context_destroy_f)); if (!tmp) { LM_ERR("cannot add any more destroy functions\n"); return; } context_destroy_array[ctx] = tmp; /* move everything to the right to make room for pos */ for (i = count; i > pos; i--) context_destroy_array[ctx][i] = context_destroy_array[ctx][i - 1]; context_destroy_array[ctx][pos] = f; count++; } void context_destroy(enum osips_context ctxtype, context_p ctx) { int f = 0; int n; int i; str *s; void *p; /* int ctx */ for (n = 0; n < type_sizes[ctxtype][CONTEXT_INT_TYPE]; n++, f++) if (context_destroy_array[ctxtype][f]) { i = context_get_int(ctxtype, ctx, n); if (i)/* XXX: should we call for 0 values? */ context_destroy_array[ctxtype][f](&i); } /* str ctx */ for (n = 0; n < type_sizes[ctxtype][CONTEXT_STR_TYPE]; n++, f++) if (context_destroy_array[ctxtype][f]) { s = context_get_str(ctxtype, ctx, n); if (s)/* XXX: how do we determine if s is empty? */ context_destroy_array[ctxtype][f](s); } /* ptr ctx */ for (n = 0; n < type_sizes[ctxtype][CONTEXT_PTR_TYPE]; n++, f++) { if (context_destroy_array[ctxtype][f]) { p = context_get_ptr(ctxtype, ctx, n); if (p) context_destroy_array[ctxtype][f](p); } } } context_p context_alloc(enum osips_context type) { context_p ctx; ctx = pkg_malloc(context_size(type)); if (!ctx) { LM_ERR("no more pkg mem\n"); return NULL; } return ctx; } int context_register_int(enum osips_context type, context_destroy_f f) { context_sizes[type] += sizeof(int); type_offsets[type][CONTEXT_STR_TYPE] += sizeof(int); type_offsets[type][CONTEXT_PTR_TYPE] += sizeof(int); register_context_destroy(f, type, CONTEXT_INT_TYPE); return type_sizes[type][CONTEXT_INT_TYPE]++; } int context_register_str(enum osips_context type, context_destroy_f f) { context_sizes[type] += sizeof(str); type_offsets[type][CONTEXT_PTR_TYPE] += sizeof(str); register_context_destroy(f, type, CONTEXT_STR_TYPE); return type_sizes[type][CONTEXT_STR_TYPE]++; } int context_register_ptr(enum osips_context type, context_destroy_f f) { context_sizes[type] += sizeof(void *); register_context_destroy(f, type, CONTEXT_PTR_TYPE); return type_sizes[type][CONTEXT_PTR_TYPE]++; } void context_put_int(enum osips_context type, context_p ctx, int pos, int data) { #ifdef DBG_MALLOC if (pos < 0 || pos >= type_sizes[type][CONTEXT_INT_TYPE]) { LM_CRIT("Bad pos: %d (%d)\n", pos, type_sizes[type][CONTEXT_INT_TYPE]); abort(); } #endif ((int *)ctx)[pos] = data; } void context_put_str(enum osips_context type, context_p ctx, int pos, str *data) { #ifdef DBG_MALLOC if (pos < 0 || pos >= type_sizes[type][CONTEXT_STR_TYPE]) { LM_CRIT("Bad pos: %d (%d)\n", pos, type_sizes[type][CONTEXT_STR_TYPE]); abort(); } #endif ((str *)((char *)ctx + type_offsets[type][CONTEXT_STR_TYPE]))[pos] = *data; } void context_put_ptr(enum osips_context type, context_p ctx, int pos, void *data) { #ifdef DBG_MALLOC if (pos < 0 || pos >= type_sizes[type][CONTEXT_PTR_TYPE]) { LM_CRIT("Bad pos: %d (%d)\n", pos, type_sizes[type][CONTEXT_PTR_TYPE]); abort(); } #endif ((void **)((char *)ctx + type_offsets[type][CONTEXT_PTR_TYPE]))[pos] = data; } int context_get_int(enum osips_context type, context_p ctx, int pos) { #ifdef DBG_MALLOC if (pos < 0 || pos >= type_sizes[type][CONTEXT_INT_TYPE]) { LM_CRIT("Bad pos: %d (%d)\n", pos, type_sizes[type][CONTEXT_INT_TYPE]); abort(); } #endif return ((int *)ctx)[pos]; } str *context_get_str(enum osips_context type, context_p ctx, int pos) { #ifdef DBG_MALLOC if (pos < 0 || pos >= type_sizes[type][CONTEXT_STR_TYPE]) { LM_CRIT("Bad pos: %d (%d)\n", pos, type_sizes[type][CONTEXT_STR_TYPE]); abort(); } #endif return &((str *)((char *)ctx + type_offsets[type][CONTEXT_STR_TYPE]))[pos]; } void *context_get_ptr(enum osips_context type, context_p ctx, int pos) { #ifdef DBG_MALLOC if (pos < 0 || pos >= type_sizes[type][CONTEXT_PTR_TYPE]) { LM_CRIT("Bad pos: %d (%d)\n", pos, type_sizes[type][CONTEXT_PTR_TYPE]); abort(); } #endif return ((void **)((char *)ctx + type_offsets[type][CONTEXT_PTR_TYPE]))[pos]; } opensips-2.2.2/context.h000066400000000000000000000061031300170765700152200ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-10-30 initial version (liviu) */ /* * This header exposes the basic operations with an OpenSIPS context. * * A "context" is: * - a data storage buffer * - typically allocated next to the intended structure * e.g. | struct cell | CONTEXT_BUFFER | * * !! All data registrations must be done in the pre-forking phases !! * (see the register functions below) */ #ifndef __CONTEXT_H #define __CONTEXT_H #include typedef void * context_p; enum osips_context { CONTEXT_GLOBAL, CONTEXT_TRAN, CONTEXT_COUNT, }; #define context_of(entity_p) ((context_p)((entity_p) + 1)) #define context_size(enum_ctx) (context_sizes[enum_ctx]) extern context_p current_processing_ctx; extern unsigned int context_sizes[]; /* * allocate a new GLOBAL context in pkg mem * * Note: this will not change the "current_processing_ctx" */ context_p context_alloc(enum osips_context ctx); #define context_free(context_p) pkg_free(context_p) /* * destroys a context by calling each callback registered */ void context_destroy(enum osips_context type, context_p ctx); /* * - register a different function for each field you add in the context * - each function will be called exactly once, every time a context is freed * * Note: for int and (str *) types, you must perform the appropriate casting */ typedef void (*context_destroy_f)(void *); /* * - the register functions should be called before any forks are made * (mod_init(), function fixups) * * - they reserve and return a position in the context buffer of the given type */ int context_register_int(enum osips_context type, context_destroy_f f); int context_register_str(enum osips_context type, context_destroy_f f); int context_register_ptr(enum osips_context type, context_destroy_f f); void context_put_int(enum osips_context type, context_p ctx, int pos, int data); void context_put_str(enum osips_context type, context_p ctx, int pos, str *data); void context_put_ptr(enum osips_context type, context_p ctx, int pos, void *data); int context_get_int(enum osips_context type, context_p ctx, int pos); str *context_get_str(enum osips_context type, context_p ctx, int pos); void *context_get_ptr(enum osips_context type, context_p ctx, int pos); #endif /* __CONTEXT_H */ opensips-2.2.2/core_stats.c000066400000000000000000000152071300170765700157020ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-01-23 first version (bogdan) * 2006-11-28 Added statistics for the number of bad URI's, methods, and * proxy requests (Jeffrey Magder - SOMA Networks) * 2009-04-23 NET and PKG statistics added (bogdan) */ /*! * \file * \brief OpenSIPS Core statistics */ #include #include "statistics.h" #include "globals.h" #include "pt.h" #include "timer.h" #include #include #include "socket_info.h" #ifdef STATISTICS /*************************** SIP statistics *********************************/ stat_var* rcv_reqs; stat_var* rcv_rpls; stat_var* fwd_reqs; stat_var* fwd_rpls; stat_var* drp_reqs; stat_var* drp_rpls; stat_var* err_reqs; stat_var* err_rpls; stat_var* bad_URIs; stat_var* unsupported_methods; stat_var* bad_msg_hdr; stat_export_t core_stats[] = { {"rcv_requests" , 0, &rcv_reqs }, {"rcv_replies" , 0, &rcv_rpls }, {"fwd_requests" , 0, &fwd_reqs }, {"fwd_replies" , 0, &fwd_rpls }, {"drop_requests" , 0, &drp_reqs }, {"drop_replies" , 0, &drp_rpls }, {"err_requests" , 0, &err_reqs }, {"err_replies" , 0, &err_rpls }, {"bad_URIs_rcvd", 0, &bad_URIs }, {"unsupported_methods", 0, &unsupported_methods }, {"bad_msg_hdr", 0, &bad_msg_hdr }, {"timestamp", STAT_IS_FUNC, (stat_var**)get_ticks }, {0,0,0} }; /*************************** NET statistics *********************************/ static unsigned long net_get_wb_udp(unsigned short foo) { return get_total_bytes_waiting(PROTO_UDP); } static unsigned long net_get_wb_tcp(unsigned short foo) { return get_total_bytes_waiting(PROTO_TCP); } static unsigned long net_get_wb_tls(unsigned short foo) { return get_total_bytes_waiting(PROTO_TLS); } stat_export_t net_stats[] = { {"waiting_udp" , STAT_IS_FUNC, (stat_var**)net_get_wb_udp }, {"waiting_tcp" , STAT_IS_FUNC, (stat_var**)net_get_wb_tcp }, {"waiting_tls" , STAT_IS_FUNC, (stat_var**)net_get_wb_tls }, {0,0,0} }; /*************************** PKG statistics *********************************/ #ifdef PKG_MALLOC static pkg_status_holder *pkg_status = NULL; static time_t *marker_t = NULL; static int no_pkg_status = 0; pkg_status_holder * get_pkg_status_holder(int proc_id) { return (pkg_status && proc_idmarker_t[proc_id]+1) { if (pt[proc_id].pid) kill(pt[proc_id].pid, SIGUSR2); marker_t[proc_id] = t; usleep(20); } } static unsigned long get_pkg_total_size( void* proc_id) { signal_pkg_status((unsigned long)proc_id); return pkg_status[(unsigned long)proc_id][PKG_TOTAL_SIZE_IDX]; } static unsigned long get_pkg_used_size( void* proc_id) { signal_pkg_status((unsigned long)proc_id); return pkg_status[(unsigned long)proc_id][PKG_USED_SIZE_IDX]; } static unsigned long get_pkg_real_used_size( void* proc_id) { signal_pkg_status((unsigned long)proc_id); return pkg_status[(unsigned long)proc_id][PKG_REAL_USED_SIZE_IDX]; } static unsigned long get_pkg_max_used_size( void* proc_id) { signal_pkg_status((unsigned long)proc_id); return pkg_status[(unsigned long)proc_id][PKG_MAX_USED_SIZE_IDX]; } static unsigned long get_pkg_free_size( void* proc_id) { signal_pkg_status((unsigned long)proc_id); return pkg_status[(unsigned long)proc_id][PKG_FREE_SIZE_IDX]; } static unsigned long get_pkg_fragments( void*proc_id) { signal_pkg_status((unsigned long)proc_id); return pkg_status[(unsigned long)proc_id][PKG_FRAGMENTS_SIZE_IDX]; } int init_pkg_stats(int no_procs) { unsigned short n; str n_str; char *name; LM_DBG("setting stats for %d processes\n",no_procs); pkg_status = shm_malloc(no_procs*sizeof(pkg_status_holder)); marker_t = shm_malloc(no_procs*sizeof(time_t)); if (pkg_status==NULL || marker_t==NULL) { LM_ERR("no more pkg mem for stats\n"); return -1; } memset( pkg_status, 0, no_procs*sizeof(pkg_status_holder)); memset( marker_t, 0, no_procs*sizeof(time_t)); no_pkg_status = no_procs; /* build the stats and register them */ for( n=0 ; n #include "str.h" #include "ut.h" #include "crc.h" #define UPDC32(ch, crc) (crc_32_tab[((crc) ^ (ch)) & 0xff] ^ ((crc) >> 8)) #define UPDCIT(ch, crc) (ccitt_tab[((crc) ^ (ch)) & 0xff] ^ ((crc) >> 8)) #define UPDC16(ch, crc) (crc_16_tab[((crc) ^ (ch)) & 0xff] ^ ((crc) >> 8)) /* First, the polynomial itself and its table of feedback terms. The */ /* polynomial is */ /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ /* Note that we take it "backwards" and put the highest-order term in */ /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ /* the MSB being 1. */ /* added CCITT (X^16+X^12+X^5+X^0) and CRC-16 (X^16+X^15+X^2+X^0) */ /* polynomials (Heinz Repp, Feb 4 1997) */ /* Note that the usual hardware shift register implementation, which */ /* is what we're using (we're merely optimizing it by doing eight-bit */ /* chunks at a time) shifts bits into the lowest-order term. In our */ /* implementation, that means shifting towards the right. Why do we */ /* do it this way? Because the calculated CRC must be transmitted in */ /* order from highest-order term to lowest-order term. UARTs transmit */ /* characters in order from LSB to MSB. By storing the CRC this way, */ /* we hand it to the UART in the order low-byte to high-byte; the UART */ /* sends each low-bit to hight-bit; and the result is transmission bit */ /* by bit from highest- to lowest-order term without requiring any bit */ /* shuffling on our part. Reception works similarly. */ /* The feedback terms table consists of 256, 32-bit entries. Notes: */ /* */ /* 1. The table can be generated at runtime if desired; code to do so */ /* is shown later. It might not be obvious, but the feedback */ /* terms simply represent the results of eight shift/xor opera- */ /* tions for all combinations of data and CRC register values. */ /* */ /* 2. The CRC accumulation logic is the same for all CRC polynomials, */ /* be they sixteen or thirty-two bits wide. You simply choose the */ /* appropriate table. Alternatively, because the table can be */ /* generated at runtime, you can start by generating the table for */ /* the polynomial in question and use exactly the same "updcrc", */ /* if your application needn't simultaneously handle two CRC */ /* polynomials. (Note, however, that XMODEM is strange.) */ /* */ /* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ /* of course, 32-bit entries work OK if the high 16 bits are zero. */ /* */ /* 4. The values must be right-shifted by eight bits by the "updcrc" */ /* logic; the shift must be unsigned (bring in zeroes). On some */ /* hardware you could probably optimize the shift in assembler by */ /* using byte-swap instructions. */ unsigned long int crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; unsigned short int ccitt_tab[] = { /* CRC polynomial 0x8408 */ 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78, }; unsigned short int crc_16_tab[] = { /* CRC polynomial 0xA001 */ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, }; unsigned short crcitt_string( char *s, int len ) { register unsigned short ccitt; ccitt = 0xFFFF; while( len ) { ccitt = UPDCIT(*s, ccitt); s++; len--; } return ~ccitt; } void crcitt_string_array( char *dst, str src[], int size ) { register int i; register unsigned short ccitt; register char *c; register int len; int str_len; ccitt = 0xFFFF; str_len=CRC16_LEN; for (i=0; is; for (len = source_string->len / 4; len--; data += 4) { hash = crc_32_tab[((unsigned char)hash) ^ data[0]] ^ (hash >> 8); hash = crc_32_tab[((unsigned char)hash) ^ data[1]] ^ (hash >> 8); hash = crc_32_tab[((unsigned char)hash) ^ data[2]] ^ (hash >> 8); hash = crc_32_tab[((unsigned char)hash) ^ data[3]] ^ (hash >> 8); } for (len = source_string->len % 4; len--; data++) { hash = crc_32_tab[((unsigned char)hash) ^ *data] ^ (hash >> 8); } *hash_ret = hash; } /* End of crc.c */ opensips-2.2.2/crc.h000066400000000000000000000006021300170765700143010ustar00rootroot00000000000000#ifndef _CRC_H_ #define _CRC_H_ #include "str.h" #define CRC16_LEN 4 extern unsigned long int crc_32_tab[]; extern unsigned short int ccitt_tab[]; extern unsigned short int crc_16_tab[]; unsigned short crcitt_string( char *s, int len ); void crcitt_string_array( char *dst, str src[], int size ); void crc32_uint(str *source_string, unsigned int *hash_ret); #endif /* _CRC_H_ */ opensips-2.2.2/daemonize.c000066400000000000000000000336331300170765700155120ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-02-20 removed from ser main.c into its own file (andrei) * 2004-03-04 moved setuid/setgid in do_suid() (andrei) * 2004-03-25 added increase_open_fds & set_core_dump (andrei) * 2004-05-03 applied pgid patch from janakj */ /*! * \file * \brief Setup the OpenSIPS daemon prozess */ #include #define _XOPEN_SOURCE /* needed on linux for the getpgid prototype, but openbsd 3.2 won't include common types (uint a.s.o) if defined before including sys/types.h */ #define _XOPEN_SOURCE_EXTENDED /* same as above */ #define __USE_XOPEN_EXTENDED /* same as above, overrides features.h */ #define __EXTENSIONS__ /* needed on solaris: if XOPEN_SOURCE is defined struct timeval definition from won't be included => workarround define _EXTENSIONS_ */ #include #include #include #include #include #include #include #include /* setrlimit */ #include #ifdef __OS_linux #include #endif #include "mem/shm_mem.h" #include "daemonize.h" #include "sr_module.h" #include "globals.h" #include "dprint.h" #include "pt.h" static int status_pipe[2]; static int *init_timer_no; /* creates the status pipe which will be used for * proper status code returning * * must be called before any forking */ int create_status_pipe(void) { int rc; status_pipe[0] = -1; status_pipe[1] = -1; retry: rc = pipe(status_pipe); if (rc < 0 && errno == EINTR) goto retry; LM_DBG("pipe created ? rc = %d, errno = %s\n",rc,strerror(errno)); /* also create SHM var which the attendent will use * to notify us about the overall number of timers * that need init * * at this point we do not know how many timers we will need */ init_timer_no = shm_malloc(sizeof(int)); if (!init_timer_no) { LM_ERR("no more shm\n"); return -1; } *init_timer_no = 0; return rc; } void inc_init_timer(void) { LM_DBG("incrementing init timer no\n"); (*init_timer_no)++; } /* attempts to send the val * status code to the waiting end */ int send_status_code(char val) { int rc; retry: rc = write(status_pipe[1], &val, 1); if (rc < 0 && errno == EINTR) goto retry; LM_DBG("send %d ? rc = %d , errno=%s\n",val,rc,strerror(errno)); if (rc == 1) return 0; return -1; } /* blockingly waits on the pipe * until a child sends a status code */ int wait_status_code(char *code) { int rc; /* close writing end */ if (status_pipe[1] != -1) { close(status_pipe[1]); status_pipe[1] = -1; } if (status_pipe[0] == -1) { LM_DBG("invalid read pipe\n"); goto error; } retry: rc = read(status_pipe[0], code, 1); if (rc < 0 && errno == EINTR) goto retry; LM_DBG("read code %d ? rc = %d, errno=%s\n",*code,rc,strerror(errno)); if (rc == 1) return 0; error: *code = -1; return -1; } int wait_for_all_children(void) { int procs_no,i,ret; char rc; procs_no = count_init_children(PROC_FLAG_INITCHILD); for (i=0;i wait for status codes from children*/ clean_write_pipeend(); LM_DBG("waiting for status code from children\n"); rc = wait_for_all_children(); LM_INFO("pre-daemon process exiting with %d\n",rc); exit(rc); } /* cleanup read end - nobody should * need to read from status pipe from this point on */ clean_read_pipeend(); /* become session leader to drop the ctrl. terminal */ if (setsid()<0){ LM_WARN("setsid failed: %s\n",strerror(errno)); }else{ *own_pgid=1;/* we have our own process group */ } /* fork again to drop group leadership */ if ((pid=fork())<0){ LM_CRIT("Cannot fork: %s\n", strerror(errno)); goto error; }else if (pid!=0){ /*parent process => exit */ exit(0); } } #ifdef __OS_linux /* setsid may disables core dumping on linux, reenable it */ if ( !disable_core_dump && prctl(PR_SET_DUMPABLE, 1)) { LM_ERR("Cannot enable core dumping after setuid\n"); } #endif /* added by noh: create a pid file for the main process */ if (pid_file!=0){ if ((pid_stream=fopen(pid_file, "r"))!=NULL){ pid_items=fscanf(pid_stream, "%d", &p); fclose(pid_stream); if (p==-1 || pid_items <= 0){ LM_WARN("pid file %s exists, but doesn't contain a valid" " pid number, replacing...\n", pid_file); } else if (kill((pid_t)p, 0)==0 || errno==EPERM){ LM_CRIT("running process found in the pid file %s\n", pid_file); goto error; }else{ LM_WARN("pid file contains old pid, replacing pid\n"); } } pid=getpid(); if ((pid_stream=fopen(pid_file, "w"))==NULL){ LM_ERR("unable to create pid file %s: %s\n", pid_file, strerror(errno)); goto error; }else{ r = fprintf(pid_stream, "%i\n", (int)pid); fclose(pid_stream); if (r<=0) { LM_ERR("unable to write pid to file %s: %s\n", pid_file, strerror(errno)); goto error; } } } if (pgid_file!=0){ if ((pid_stream=fopen(pgid_file, "r"))!=NULL){ pid_items=fscanf(pid_stream, "%d", &p); fclose(pid_stream); if (p==-1 || pid_items <= 0){ LM_WARN("pgid file %s exists, but doesn't contain a valid" " pgid number, replacing...\n", pgid_file); } } if (own_pgid){ pid=getpgid(0); if ((pid_stream=fopen(pgid_file, "w"))==NULL){ LM_ERR("unable to create pgid file %s: %s\n", pgid_file, strerror(errno)); goto error; }else{ r = fprintf(pid_stream, "%i\n", (int)pid); fclose(pid_stream); if (r<=0) { LM_ERR("unable to write pgid to file %s: %s\n", pid_file, strerror(errno)); goto error; } } }else{ LM_WARN("we don't have our own process so we won't save" " our pgid\n"); unlink(pgid_file); /* just to be sure nobody will miss-use the old value*/ } } /* try to replace stdin, stdout & stderr with /dev/null */ if (freopen("/dev/null", "r", stdin)==0){ LM_WARN("unable to replace stdin with /dev/null: %s\n", strerror(errno)); /* continue, leave it open */ }; if (freopen("/dev/null", "w", stdout)==0){ LM_WARN("unable to replace stdout with /dev/null: %s\n", strerror(errno)); /* continue, leave it open */ }; /* close stderr only if not to be used */ if ( (!log_stderr) && (freopen("/dev/null", "w", stderr)==0)){ LM_WARN("unable to replace stderr with /dev/null: %s\n", strerror(errno)); /* continue, leave it open */ }; /* close any open file descriptors */ closelog(); /* 32 is the maximum number of inherited open file descriptors */ for (r=3; r < 32; r++){ /* future children must still inherit * and write to this pipe end */ if (r != status_pipe[1]) close(r); } if (!log_stderr) openlog(name, LOG_PID|LOG_CONS, log_facility); /* LOG_CONS, LOG_PERRROR ? */ return 0; error: return -1; } /*! * \brief set daemon user and group id * \param uid user id * \param gid group id * \return return 0 on success, -1 on error */ int do_suid(const int uid, const int gid) { if (pid_file) { /* pid file should be already created by deamonize function -> change the owner and group also */ if (chown( pid_file , uid?uid:-1, gid?gid:-1)!=0) { LM_ERR("failed to change owner of pid file %s: %s(%d)\n", pid_file, strerror(errno), errno); goto error; } } if (pgid_file) { /* pgid file should be already created by deamonize function -> change the owner and group also */ if (chown( pgid_file , uid?uid:-1, gid?gid:-1)!=0) { LM_ERR("failed to change owner of pid file %s: %s(%d)\n", pgid_file, strerror(errno), errno); goto error; } } if (gid){ if(setgid(gid)<0){ LM_CRIT("cannot change gid to %d: %s\n", gid, strerror(errno)); goto error; } } if(uid){ if(setuid(uid)<0){ LM_CRIT("cannot change uid to %d: %s\n", uid, strerror(errno)); goto error; } } #ifdef __OS_linux /* setuid disables core dumping on linux, reenable it */ if ( !disable_core_dump && prctl(PR_SET_DUMPABLE, 1)) { LM_ERR("Cannot enable core dumping after setuid\n"); } #endif return 0; error: return -1; } /*! * \brief try to increase the open file limit to the value given by the global * option "open_files_limit" ; the value is updated back in case of a * partial increase of the limit * \return return 0 on success, -1 on error */ int set_open_fds_limit(void) { struct rlimit lim, orig; if (getrlimit(RLIMIT_NOFILE, &lim)<0){ LM_CRIT("cannot get the maximum number of file descriptors: %s\n", strerror(errno)); goto error; } orig=lim; LM_DBG("current open file limits: %lu/%lu\n", (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max); if ((lim.rlim_cur==RLIM_INFINITY) || (open_files_limit<=lim.rlim_cur)) /* nothing to do (we do no reduce the limit) */ goto done; if ((lim.rlim_max==RLIM_INFINITY) || (open_files_limit<=lim.rlim_max)) { lim.rlim_cur=open_files_limit; /* increase soft limit to target */ } else { /* more than the hard limit */ LM_INFO("trying to increase the open file limit" " past the hard limit (%ld -> %d)\n", (unsigned long)lim.rlim_max, open_files_limit); lim.rlim_max=open_files_limit; lim.rlim_cur=open_files_limit; } LM_DBG("increasing open file limits to: %lu/%lu\n", (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max); if (setrlimit(RLIMIT_NOFILE, &lim)<0){ LM_CRIT("cannot increase the open file limit to" " %lu/%lu: %s\n", (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max, strerror(errno)); if (orig.rlim_max>orig.rlim_cur){ /* try to increase to previous maximum, better than not increasing * at all */ lim.rlim_max=orig.rlim_max; lim.rlim_cur=orig.rlim_max; if (setrlimit(RLIMIT_NOFILE, &lim)==0){ LM_CRIT("maximum number of file descriptors increased to" " %u\n",(unsigned)orig.rlim_max); open_files_limit = orig.rlim_max; goto done; } } goto error; } done: LM_DBG("open files limit set to %d\n",open_files_limit); return 0; error: return -1; } /*! * \brief enable or disable core dumps * \param enable set to 1 to enable, to 0 to disable * \param size core dump size * \return return 0 on success, -1 on error */ int set_core_dump(int enable, unsigned int size) { struct rlimit lim, newlim; if (enable){ if (getrlimit(RLIMIT_CORE, &lim)<0){ LM_CRIT("cannot get the maximum core size: %s\n", strerror(errno)); goto error; } if (lim.rlim_cureoh comparisons (andrei) * 2003-10-28 added extra checks (paranoia) for {anchor,del}_lump (andrei) * 2005-08-22 added init_lump_flags -initial flags- for all built lumps * (bogdan) * 2005-08-23 del_nonshm_lump() -> del_flaged_lumps(LUMPFLAG_SHMEM) (bogdan) */ /*! * \file data_lump.c * \brief OpenSIPS Lump (internal message manipulation) functions */ #include "data_lump.h" #include "dprint.h" #include "mem/mem.h" #include "globals.h" #include "error.h" #include #include #ifdef DEBUG_DMALLOC #include #endif /*! \note WARNING: all lump add/insert operations expect a pkg_malloc'ed char* * pointer the will be DEALLOCATED when the sip_msg is destroyed! */ enum lump_dir { LD_NEXT, LD_BEFORE, LD_AFTER }; int init_lump_flags = 0; /*! \brief adds a header to the end * \return returns pointer if success, 0 on error * * WARNING: currently broken! * - lumps_len() needs to properly handle LUMP_ADD along the main chain of * lumps before we can use this */ struct lump* append_new_lump(struct lump** list, char* new_hdr, unsigned int len, enum _hdr_types_t type) { struct lump** t; struct lump* tmp; for (t=list;*t;t=&((*t)->next)); tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->type=type; tmp->flags=init_lump_flags; tmp->op=LUMP_ADD; tmp->u.value=new_hdr; tmp->len=len; *t=tmp; return tmp; } /*! \brief inserts a header to the beginning * \return returns pointer if success, 0 on error * * WARNING: currently broken! * - lumps_len() needs to properly handle LUMP_ADD along the main chain of * lumps before we can use this */ struct lump* insert_new_lump(struct lump** list, char* new_hdr, unsigned int len, enum _hdr_types_t type) { struct lump* tmp; tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->next=*list; tmp->type=type; tmp->flags=init_lump_flags; tmp->op=LUMP_ADD; tmp->u.value=new_hdr; tmp->len=len; *list=tmp; return tmp; } /*! \brief inserts a header/data lump immediately after hdr * \return returns pointer on success, 0 on error */ struct lump* insert_new_lump_after( struct lump* after, char* new_hdr, unsigned int len, enum _hdr_types_t type) { struct lump* tmp; tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ ser_error=E_OUT_OF_MEM; LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->after=after->after; tmp->type=type; tmp->flags=init_lump_flags; tmp->op=LUMP_ADD; tmp->u.value=new_hdr; tmp->len=len; after->after=tmp; return tmp; } /*! \brief inserts a header/data lump immediately before "before" * \return returns pointer on success, 0 on error */ struct lump* insert_new_lump_before( struct lump* before, char* new_hdr, unsigned int len, enum _hdr_types_t type) { struct lump* tmp; tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ ser_error=E_OUT_OF_MEM; LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->before=before->before; tmp->type=type; tmp->flags=init_lump_flags; tmp->op=LUMP_ADD; tmp->u.value=new_hdr; tmp->len=len; before->before=tmp; return tmp; } /*! \brief inserts a subst lump immediately after hdr * \return returns pointer on success, 0 on error */ struct lump* insert_subst_lump_after( struct lump* after,enum lump_subst subst, enum _hdr_types_t type) { struct lump* tmp; tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ ser_error=E_OUT_OF_MEM; LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->after=after->after; tmp->type=type; tmp->flags=init_lump_flags; tmp->op=LUMP_ADD_SUBST; tmp->u.subst=subst; tmp->len=0; after->after=tmp; return tmp; } /*! \brief inserts a subst lump immediately before "before" * \return returns pointer on success, 0 on error */ struct lump* insert_subst_lump_before( struct lump* before, enum lump_subst subst, enum _hdr_types_t type) { struct lump* tmp; tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ ser_error=E_OUT_OF_MEM; LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->before=before->before; tmp->type=type; tmp->flags=init_lump_flags; tmp->op=LUMP_ADD_SUBST; tmp->u.subst=subst; tmp->len=0; before->before=tmp; return tmp; } /*! \brief inserts a cond lump immediately after hdr * \return returns pointer on success, 0 on error */ struct lump* insert_cond_lump_after( struct lump* after,enum lump_conditions c, enum _hdr_types_t type) { struct lump* tmp; tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ ser_error=E_OUT_OF_MEM; LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->after=after->after; tmp->type=type; tmp->flags=init_lump_flags; tmp->op=LUMP_ADD_OPT; tmp->u.cond=c; tmp->len=0; after->after=tmp; return tmp; } /*! \brief inserts a conditional lump immediately before "before" * \return returns pointer on success, 0 on error */ struct lump* insert_cond_lump_before( struct lump* before, enum lump_conditions c, enum _hdr_types_t type) { struct lump* tmp; tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ ser_error=E_OUT_OF_MEM; LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->before=before->before; tmp->type=type; tmp->flags=init_lump_flags; tmp->op=LUMP_ADD_OPT; tmp->u.cond=c; tmp->len=0; before->before=tmp; return tmp; } /*! \brief inserts a skip lump immediately after hdr * \return returns pointer on success, 0 on error */ struct lump* insert_skip_lump_after( struct lump* after) { struct lump* tmp; tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ ser_error=E_OUT_OF_MEM; LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->after=after->after; tmp->flags=init_lump_flags; tmp->op=LUMP_SKIP; after->after=tmp; return tmp; } /*! \brief inserts a skip lump immediately before "before" * \return returns pointer on success, 0 on error */ struct lump* insert_skip_lump_before( struct lump* before ) { struct lump* tmp; tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ ser_error=E_OUT_OF_MEM; LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->before=before->before; tmp->flags=init_lump_flags; tmp->op=LUMP_SKIP; before->before=tmp; return tmp; } /*! \brief removes an already existing header/data lump */ /* WARNING: this function adds the lump either to the msg->add_rm or * msg->body_lumps list, depending on the offset being greater than msg->eoh, * so msg->eoh must be parsed (parse with HDR_EOH) if you think your lump * might affect the body!! */ struct lump* del_lump(struct sip_msg* msg, unsigned int offset, unsigned int len, enum _hdr_types_t type) { struct lump* tmp; struct lump* prev, *t; struct lump** list; /* extra checks */ if (offset>msg->len){ LM_CRIT("offset exceeds message size (%d > %d)" " aborting...\n", offset, msg->len); abort(); } if (offset+len>msg->len){ LM_CRIT("offset + len exceeds message" " size (%d + %d > %d)\n", offset, len, msg->len); abort(); } if (len==0){ LM_WARN("called with 0 len (offset =%d)\n", offset); } tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->op=LUMP_DEL; tmp->type=type; tmp->flags=init_lump_flags; tmp->u.offset=offset; tmp->len=len; prev=0; /* check to see whether this might be a body lump */ if ((msg->eoh) && (offset>(unsigned long)(msg->eoh-msg->buf))) list=&msg->body_lumps; else list=&msg->add_rm; for (t=*list;t; prev=t, t=t->next){ /* insert it sorted after offset */ if (((t->op==LUMP_DEL)||(t->op==LUMP_NOP))&&(t->u.offset>offset)) break; } tmp->next=t; if (prev) prev->next=tmp; else *list=tmp; return tmp; } /*! \brief add an anchor * WARNING: this function adds the lump either to the msg->add_rm or * msg->body_lumps list, depending on the offset being greater than msg->eoh, * so msg->eoh must be parsed (parse with HDR_EOH) if you think your lump * might affect the body!! */ struct lump* anchor_lump(struct sip_msg* msg, unsigned int offset, enum _hdr_types_t type) { struct lump* tmp; struct lump* prev, *t; struct lump** list; /* extra checks */ if (offset>msg->len){ LM_CRIT("offset exceeds message size (%d > %d)" " aborting...\n", offset, msg->len); abort(); } tmp=pkg_malloc(sizeof(struct lump)); if (tmp==0){ ser_error=E_OUT_OF_MEM; LM_ERR("out of pkg memory\n"); return 0; } memset(tmp,0,sizeof(struct lump)); tmp->op=LUMP_NOP; tmp->type=type; tmp->flags=init_lump_flags; tmp->u.offset=offset; prev=0; /* check to see whether this might be a body lump */ if ((msg->eoh) && (offset> (unsigned long)(msg->eoh-msg->buf))) list=&msg->body_lumps; else list=&msg->add_rm; for (t=*list;t; prev=t, t=t->next){ /* insert it sorted after offset */ if (((t->op==LUMP_DEL)||(t->op==LUMP_NOP))&&(t->u.offset>offset)) break; } tmp->next=t; if (prev) prev->next=tmp; else *list=tmp; return tmp; } void free_lump(struct lump* lmp) { if (lmp && (lmp->op==LUMP_ADD)){ if (lmp->u.value){ if (lmp->flags &(LUMPFLAG_SHMEM)){ LM_CRIT("called on a not free-able lump:" "%p flags=%x\n", lmp, lmp->flags); abort(); }else{ pkg_free(lmp->u.value); lmp->u.value=0; lmp->len=0; } } } } void free_lump_list(struct lump* l) { struct lump* t, *r, *foo,*crt; t=l; while(t){ crt=t; t=t->next; r=crt->before; while(r){ foo=r; r=r->before; free_lump(foo); pkg_free(foo); } r=crt->after; while(r){ foo=r; r=r->after; free_lump(foo); pkg_free(foo); } /*clean current elem*/ free_lump(crt); pkg_free(crt); } } /*! \brief* duplicate a lump list into pkg memory */ static struct lump *dup_lump_list_r( struct lump *l, enum lump_dir dir, int *error) { int deep_error; struct lump *new_lump; deep_error=0; /* optimist: assume success in recursion */ /* if at list end, terminate recursion successfully */ if (!l) { *error=0; return 0; } /* otherwise duplicate current element */ new_lump=pkg_malloc(sizeof(struct lump)); if (!new_lump) { *error=1; return 0; } memcpy(new_lump, l, sizeof(struct lump)); new_lump->flags=init_lump_flags; new_lump->next=new_lump->before=new_lump->after=0; if (new_lump->op==LUMP_ADD) { new_lump->u.value = pkg_malloc(l->len); if (!new_lump->u.value) { *error=1; return 0; } memcpy(new_lump->u.value,l->u.value,l->len); } switch(dir) { case LD_NEXT: new_lump->before=dup_lump_list_r(l->before, LD_BEFORE, &deep_error); if (deep_error) goto deeperror; new_lump->after=dup_lump_list_r(l->after, LD_AFTER, &deep_error); if (deep_error) goto deeperror; new_lump->next=dup_lump_list_r(l->next, LD_NEXT, &deep_error); break; case LD_BEFORE: new_lump->before=dup_lump_list_r(l->before, LD_BEFORE, &deep_error); break; case LD_AFTER: new_lump->after=dup_lump_list_r(l->after, LD_AFTER, &deep_error); break; default: LM_CRIT("unknown dir: %d\n", dir ); deep_error=1; } if (deep_error) goto deeperror; *error=0; return new_lump; deeperror: LM_ERR("out of pkg mem\n"); free_lump(new_lump); *error=1; return 0; } /*! \brief full pkg copy of a lump list * * \return if either original list empty or error occur returns, 0 * is returned, pointer to the copy otherwise */ struct lump* dup_lump_list( struct lump *l ) { int deep_error; deep_error=0; return dup_lump_list_r(l, LD_NEXT, &deep_error); } /*! \brief Delete flagged lumps */ void del_flaged_lumps( struct lump** lump_list, enum lump_flag flags ) { struct lump *r, *foo, *crt, **prev, *prev_r; prev = lump_list; crt = *lump_list; while (crt) { if ( crt->flags&flags ) { /* unlink it */ foo = crt; crt = crt->next; foo->next = 0; /* update the 'next' link of the previous lump */ *prev = crt; /* entire before/after list must be removed */ free_lump_list( foo ); } else { /* check on before and prev list for flaged lumps */ r = crt->after; prev_r = crt; while(r){ foo=r; r=r->after; if ( foo->flags&flags ) { prev_r->after = r; free_lump(foo); pkg_free(foo); } else { prev_r = foo; } } /* before */ r = crt->before; prev_r = crt; while(r){ foo=r; r=r->before; if ( foo->flags&flags ) { prev_r->before = r; free_lump(foo); pkg_free(foo); } else { prev_r = foo; } } /* go to next lump */ prev = &(crt->next); crt = crt->next; } } } /*! \brief Delete not flagged lumps */ void del_notflaged_lumps( struct lump** lump_list, enum lump_flag not_flags ) { struct lump *r, *foo, *crt, **prev, *prev_r; prev = lump_list; crt = *lump_list; while (crt) { if ( (~crt->flags)¬_flags ) { /* unlink it */ foo = crt; crt = crt->next; foo->next = 0; /* update the 'next' link of the previous lump */ *prev = crt; /* entire before/after list must be removed */ free_lump_list( foo ); } else { /* check on after and before list for not_flaged lumps */ r = crt->after; prev_r = crt; while(r){ foo=r; r=r->after; if ( (~foo->flags)¬_flags ) { prev_r->after = r; free_lump(foo); pkg_free(foo); } else { prev_r = foo; } } /* before */ r = crt->before; prev_r = crt; while(r){ foo=r; r=r->before; if ( (~foo->flags)¬_flags ) { prev_r->before = r; free_lump(foo); pkg_free(foo); } else { prev_r = foo; } } /* go to next lump */ prev = &(crt->next); crt = crt->next; } } } opensips-2.2.2/data_lump.h000066400000000000000000000077361300170765700155170ustar00rootroot00000000000000/* * adding/removing headers or any other data chunk from a message * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-29 s/int/enum ... more convenient for gdb (jiri) * 2003-03-31 added subst lumps -- they expand in ip addr, port a.s.o (andrei) * 2003-04-01 added opt (condition) lumps (andrei) * 2003-04-02 added more subst lumps: SUBST_{SND,RCV}_ALL * => ip:port;transport=proto (andrei) * */ /*! * \file data_lump.h * \brief adding/removing headers or any other data chunk from a message */ #ifndef data_lump_h #define data_lump_h #include "lump_struct.h" #include "parser/msg_parser.h" #include "parser/hf.h" extern int init_lump_flags; #define set_init_lump_flags(_flags) \ do{\ init_lump_flags = _flags;\ }while(0) #define reset_init_lump_flags() \ do{\ init_lump_flags = 0;\ }while(0) /*! \brief adds a header to the end * WARNING: currently broken! * - lumps_len() needs to properly handle LUMP_ADD along the main chain of * lumps before we can use this */ struct lump* append_new_lump(struct lump** list, char* new_hdr, unsigned int len, enum _hdr_types_t type); /*! \brief inserts a header to the beginning * WARNING: currently broken! * - lumps_len() needs to properly handle LUMP_ADD along the main chain of * lumps before we can use this */ struct lump* insert_new_lump(struct lump** list, char* new_hdr, unsigned int len, enum _hdr_types_t type); /*! \brief inserts a header to the beginning - after */ struct lump* insert_new_lump_after(struct lump* after, char* new_hdr, unsigned int len, enum _hdr_types_t type); /*! \brief inserts a header to the beginning - before */ struct lump* insert_new_lump_before(struct lump* before, char* new_hdr, unsigned int len,enum _hdr_types_t type); /*! \brief substitutions (replace with ip address, port etc) - after */ struct lump* insert_subst_lump_after(struct lump* after, enum lump_subst subst, enum _hdr_types_t type); /*! \brief substitutions (replace with ip address, port etc) - before */ struct lump* insert_subst_lump_before(struct lump* before, enum lump_subst subst, enum _hdr_types_t type); /*! \brief conditional lumps - insert*/ struct lump* insert_cond_lump_after(struct lump* after, enum lump_conditions c, enum _hdr_types_t type); /*! \brief conditional lumps - before */ struct lump* insert_cond_lump_before(struct lump* after,enum lump_conditions c, enum _hdr_types_t type); /*! \brief skip lumps - after*/ struct lump* insert_skip_lump_after( struct lump* after); /*! \brief skip lumps - before*/ struct lump* insert_skip_lump_before( struct lump* before); /*! \brief removes an already existing header */ struct lump* del_lump(struct sip_msg* msg, unsigned int offset, unsigned int len, enum _hdr_types_t type); /*! \brief set an anchor */ struct lump* anchor_lump(struct sip_msg* msg, unsigned int offset, enum _hdr_types_t type); /*! \brief duplicates a lump list in pkg-mem */ struct lump* dup_lump_list( struct lump *l ); /*! \brief remove all flagged lumps from the list */ void del_flaged_lumps( struct lump** lump_list, enum lump_flag flags ); /*! \brief remove all unflagged lumps from the list */ void del_notflaged_lumps( struct lump** lump_list, enum lump_flag not_flags); #endif opensips-2.2.2/data_lump_rpl.c000066400000000000000000000066311300170765700163600ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2002-02-14 : created by bogdan * 2003-09-11 : lump_rpl type added - LUMP_RPL_BODY & LUMP_RPL_HDR (bogdan) * 2003-11-11 : build_lump_rpl merged into add_lump_rpl; types -> flags ; * flags LUMP_RPL_NODUP and LUMP_RPL_NOFREE added (bogdan) */ /*! * \file * \brief OpenSIPS Generic lump functions for replies */ #include #include "dprint.h" #include "mem/mem.h" #include "data_lump_rpl.h" struct lump_rpl* add_lump_rpl(struct sip_msg *msg, char *s, int len, int flags) { struct lump_rpl *lump = 0; struct lump_rpl *foo; /* some checking */ if ( (flags&(LUMP_RPL_HDR|LUMP_RPL_BODY))==(LUMP_RPL_HDR|LUMP_RPL_BODY) || (flags&(LUMP_RPL_HDR|LUMP_RPL_BODY))==0 || (flags&LUMP_RPL_SHMEM) ) { LM_ERR("bad flags combination (%d)!\n",flags); goto error; } if (len<=0 || s==0) { LM_ERR("I won't add an empty lump!\n"); goto error; } /* build the lump */ lump = (struct lump_rpl*) pkg_malloc ( sizeof(struct lump_rpl) + ((flags&LUMP_RPL_NODUP)?0:len) ); if (!lump) { LM_ERR("no free pkg memory !\n"); goto error; } if (flags&LUMP_RPL_NODUP) { lump->text.s = s; } else { lump->text.s = ((char*)lump)+sizeof(struct lump_rpl); memcpy( lump->text.s, s, len); } lump->text.len = len; lump->flags = flags; lump->next = 0; /* add the lump to the msg */ if (!msg->reply_lump) { msg->reply_lump = lump; }else{ if (!(flags&LUMP_RPL_BODY)) for(foo=msg->reply_lump;foo->next;foo=foo->next); else for(foo=msg->reply_lump; ;foo=foo->next) { if (foo->flags&LUMP_RPL_BODY) { LM_ERR("LUMP_RPL_BODY already added!\n"); pkg_free(lump); goto error; } if (foo->next==0) break; } foo->next = lump; } return lump; error: return 0; } void free_lump_rpl(struct lump_rpl* lump) { if (lump) { if (!((lump->flags)&LUMP_RPL_NOFREE) && ((lump->flags)&LUMP_RPL_NODUP) && lump->text.s) pkg_free(lump->text.s); pkg_free(lump); } } void unlink_lump_rpl(struct sip_msg * msg, struct lump_rpl* lump) { struct lump_rpl *foo,*prev; /* look for the lump to be unlink */ foo = msg->reply_lump; prev = 0; while( foo && foo!=lump ) { prev = foo; foo = foo->next; } /* if the lump was found into the list -> unlink it */ if (foo) { if (prev) prev->next = foo->next; else msg->reply_lump = foo->next; } } void del_nonshm_lump_rpl(struct lump_rpl** list) { struct lump_rpl* it, *tmp; struct lump_rpl** pred; it = *list; pred = list; while(it) { if (!(it->flags & LUMP_RPL_SHMEM)) { tmp = it; *pred = it->next; it = it->next; free_lump_rpl(tmp); continue; } pred = &it->next; it = it->next; } } opensips-2.2.2/data_lump_rpl.h000066400000000000000000000032641300170765700163640ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2002-02-14 : created by bogdan * 2003-09-11 : lump_rpl type added - LUMP_RPL_BODY & LUMP_RPL_HDR (bogdan) * 2003-11-11 : build_lump_rpl merged into add_lump_rpl; type removed; * flags LUMP_RPL_BODY, LUMP_RPL_NODUP and LUMP_RPL_NOFREE * added (bogdan) */ /*! * \file * \brief Data lump reply handling */ #ifndef data_lump_rpl_h #define data_lump_rpl_h #include "parser/msg_parser.h" #define LUMP_RPL_HDR (1<<1) #define LUMP_RPL_BODY (1<<2) #define LUMP_RPL_NODUP (1<<3) #define LUMP_RPL_NOFREE (1<<4) #define LUMP_RPL_SHMEM (1<<5) struct lump_rpl { str text; int flags; struct lump_rpl* next; }; struct lump_rpl* add_lump_rpl(struct sip_msg *, char *, int , int ); void free_lump_rpl(struct lump_rpl* ); void unlink_lump_rpl(struct sip_msg *, struct lump_rpl* ); void del_nonshm_lump_rpl( struct lump_rpl ** ); #endif opensips-2.2.2/db/000077500000000000000000000000001300170765700137505ustar00rootroot00000000000000opensips-2.2.2/db/README000066400000000000000000000005771300170765700146410ustar00rootroot00000000000000$Id$ This directory contains generic database support interface. The interface should be used by all modules willing to communicate with a database. OpenSIPS can then switch from one database to another simply by loading a different database support module. Directory example contains a very simple example how to use the interface. Directory doc contains the API documentation. opensips-2.2.2/db/db.c000066400000000000000000000255441300170765700145130ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * History: * -------- * 2004-06-06 bind_dbmod takes dbf as parameter (andrei) * 2006-10-10 Added support for retrieving the last inserted ID (Carsten Bock, BASIS AudioNet GmbH) */ /** * \file db/db.c * \brief Generic Database Interface * * This is a generic database interface for modules that need to utilize a * database. The interface should be used by all modules that access database. * The interface will be independent of the underlying database server. * Notes: * If possible, use the predefined macros if you need to access any structure * attributes. * For additional description, see the comments in the sources of mysql module. * * If you want to see more complicated examples of how the API could be used, * take a look at the sources of the usrloc or auth modules. */ #include "../dprint.h" #include "../sr_module.h" #include "../mem/mem.h" #include "../mem/meminfo.h" #include "../ut.h" #include "db_cap.h" #include "db_id.h" #include "db_pool.h" #include "db.h" char *db_version_table = VERSION_TABLE; char *db_default_url = NULL; int db_max_async_connections = 10; /** maximal length of a SQL URL */ static unsigned int MAX_URL_LENGTH = 255; #define COLUMN_OVERHEAD 256 int estimate_available_rows(int payload_size, int column_count) { struct mem_info info; memset(&info, 0, sizeof (struct mem_info)); #ifdef pkg_info pkg_info(&info); return (int) (info.free / (payload_size + column_count * COLUMN_OVERHEAD)); #else return 0; #endif } int db_check_api(db_func_t* dbf, char *mname) { if(dbf==NULL) return -1; /* All modules must export db_use_table */ if (dbf->use_table == 0) { LM_ERR("module %s does not export db_use_table function\n", mname); goto error; } /* All modules must export db_init */ if (dbf->init == 0) { LM_ERR("module %s does not export db_init function\n", mname); goto error; } /* All modules must export db_close */ if (dbf->close == 0) { LM_ERR("module %s does not export db_close function\n", mname); goto error; } if (dbf->query) { dbf->cap |= DB_CAP_QUERY; } if (dbf->fetch_result) { dbf->cap |= DB_CAP_FETCH; } if (dbf->raw_query) { dbf->cap |= DB_CAP_RAW_QUERY; } /* Free result must be exported if DB_CAP_QUERY or * DB_CAP_RAW_QUERY is set */ if ((dbf->cap & (DB_CAP_QUERY|DB_CAP_RAW_QUERY)) && (dbf->free_result==0)) { LM_ERR("module %s supports queries but does not export free_result\n", mname); goto error; } if (dbf->insert) { dbf->cap |= DB_CAP_INSERT; } if (dbf->delete) { dbf->cap |= DB_CAP_DELETE; } if (dbf->update) { dbf->cap |= DB_CAP_UPDATE; } if (dbf->replace) { dbf->cap |= DB_CAP_REPLACE; } if (dbf->last_inserted_id) { dbf->cap |= DB_CAP_LAST_INSERTED_ID; } if (dbf->insert_update) { dbf->cap |= DB_CAP_INSERT_UPDATE; } if (dbf->async_raw_query || dbf->async_resume || dbf->async_free_result) { if (!dbf->async_raw_query || !dbf->async_resume || !dbf->async_free_result) { LM_BUG("NULL async raw_query | resume | free_result in %s", mname); return -1; } dbf->cap |= DB_CAP_ASYNC_RAW_QUERY; } return 0; error: return -1; } /* fills mydbf with the corresponding db module callbacks * returns 0 on success, -1 on error * on error mydbf will contain only 0s */ int db_bind_mod(const str* mod, db_func_t* mydbf) { char *name, *tmp, *p; int len; db_func_t dbf; db_bind_api_f dbind; if (!mod || !mod->s) { LM_CRIT("null database module name\n"); return -1; } if (mydbf==0) { LM_CRIT("null dbf parameter\n"); return -1; } if (mod->len > MAX_URL_LENGTH) { LM_ERR("SQL URL too long\n"); return 0; } // add the prefix name = pkg_malloc(mod->len + 4); if (!name) { LM_ERR("no private memory left\n"); return -1; } memcpy(name, "db_", 3); memcpy(name+3, mod->s, mod->len); name[mod->len+3] = 0; /* for safety we initialize mydbf with 0 (this will cause * a segfault immediately if someone tries to call a function * from it without checking the return code from bind_dbmod */ memset((void*)mydbf, 0, sizeof(db_func_t)); p = strchr(name, ':'); if (p) { len = p - name; tmp = (char*)pkg_malloc(len + 4); if (!tmp) { LM_ERR("no private memory left\n"); pkg_free(name); return -1; } memcpy(tmp, name, len); tmp[len] = '\0'; pkg_free(name); } else { tmp = name; } dbind = (db_bind_api_f)find_mod_export(tmp, "db_bind_api", 0, 0); if(dbind != NULL) { LM_DBG("using db bind api for %s\n", tmp); if(dbind(mod, &dbf)<0) { LM_ERR("db_bind_api returned error for module %s\n", tmp); goto error; } } else { memset(&dbf, 0, sizeof(db_func_t)); LM_DBG("using export interface to bind %s\n", tmp); dbf.use_table = (db_use_table_f)find_mod_export(tmp, "db_use_table", 2, 0); dbf.init = (db_init_f)find_mod_export(tmp, "db_init", 1, 0); dbf.close = (db_close_f)find_mod_export(tmp, "db_close", 2, 0); dbf.query = (db_query_f)find_mod_export(tmp, "db_query", 2, 0); dbf.fetch_result = (db_fetch_result_f)find_mod_export(tmp, "db_fetch_result", 2, 0); dbf.raw_query = (db_raw_query_f)find_mod_export(tmp, "db_raw_query", 2, 0); dbf.free_result = (db_free_result_f)find_mod_export(tmp, "db_free_result", 2, 0); dbf.insert = (db_insert_f)find_mod_export(tmp, "db_insert", 2, 0); dbf.delete = (db_delete_f)find_mod_export(tmp, "db_delete", 2, 0); dbf.update = (db_update_f)find_mod_export(tmp, "db_update", 2, 0); dbf.replace = (db_replace_f)find_mod_export(tmp, "db_replace", 2, 0); dbf.last_inserted_id= (db_last_inserted_id_f)find_mod_export(tmp, "db_last_inserted_id", 1, 0); dbf.insert_update = (db_insert_update_f)find_mod_export(tmp, "db_insert_update", 2, 0); } if(db_check_api(&dbf, tmp)!=0) goto error; *mydbf=dbf; /* copy */ pkg_free(tmp); return 0; error: pkg_free(tmp); return -1; } /* * Initialize database module * No function should be called before this */ db_con_t* db_do_init(const str* url, void* (*new_connection)()) { struct db_id *id = NULL; struct pool_con *con = NULL; db_con_t *res = NULL; int con_size = 0; if (!url || !url->s || !new_connection) { LM_ERR("invalid parameter value\n"); return 0; } con_size = sizeof(db_con_t) + sizeof(void *) + url->len; if (url->len > MAX_URL_LENGTH) { LM_ERR("SQL URL too long\n"); return 0; } /* this is the root memory for this database connection. */ res = (db_con_t*)pkg_malloc(con_size); if (!res) { LM_ERR("no private memory left\n"); return 0; } memset(res, 0, con_size); /* fill in the URL info */ res->url.s = (char *)res + sizeof(db_con_t) + sizeof(void *); res->url.len = url->len; memcpy(res->url.s,url->s,url->len); id = new_db_id(url); if (!id) { LM_ERR("cannot parse URL '%.*s'\n", url->len, url->s); goto err; } /* Find the connection in the pool */ con = pool_get(id); if (!con) { LM_DBG("connection %p not found in pool\n", id); /* Not in the pool yet */ con = (struct pool_con *)new_connection(id); if (!con) { LM_ERR("could not add connection to the pool\n"); goto err; } pool_insert(con); LM_DBG("connection %p inserted in pool as %p\n", id,con); } else { LM_DBG("connection %p found in pool as %p\n", id,con); } if (!con->transfers) { con->transfers = pkg_malloc(db_max_async_connections * sizeof *con->transfers); if (!con->transfers) { LM_ERR("no more pkg\n"); goto err; } } res->tail = (unsigned long)con; return res; err: if (id) free_db_id(id); if (res) pkg_free(res); return 0; } /* * Shut down database module * No function should be called after this */ void db_do_close(db_con_t* _h, void (*free_connection)()) { struct pool_con* con; if (!_h) { LM_ERR("invalid parameter value\n"); return; } con = (struct pool_con*)_h->tail; if (pool_remove(con) == 1) { free_connection(con); } pkg_free(_h); } /* * Get version of a table * If there is no row for the given table, return version 0 */ int db_table_version(const db_func_t* dbf, db_con_t* connection, const str* table) { db_key_t key[1], col[1]; db_val_t val[1]; db_res_t* res = NULL; db_val_t* ver = 0; str version; int ret; if (!dbf||!connection || !table || !table->s) { LM_CRIT("invalid parameter value\n"); return -1; } version.s = db_version_table; version.len = strlen(version.s); if (dbf->use_table(connection, &version) < 0) { LM_ERR("error while changing table\n"); return -1; } str tmp1 = str_init(TABLENAME_COLUMN); key[0] = &tmp1; VAL_TYPE(val) = DB_STR; VAL_NULL(val) = 0; VAL_STR(val) = *table; str tmp2 = str_init(VERSION_COLUMN); col[0] = &tmp2; if (dbf->query(connection, key, 0, val, col, 1, 1, 0, &res) < 0) { LM_ERR("error in db_query\n"); return -1; } if (RES_ROW_N(res) == 0) { LM_DBG("no row for table %.*s found\n", table->len, ZSW(table->s)); return 0; } if (RES_ROW_N(res) != 1) { LM_ERR("invalid number of rows received:" " %d, %.*s\n", RES_ROW_N(res), table->len, ZSW(table->s)); dbf->free_result(connection, res); return -1; } ver = ROW_VALUES(RES_ROWS(res)); if ( VAL_TYPE(ver)!=DB_INT || VAL_NULL(ver) ) { LM_ERR("invalid type (%d) or nul (%d) version " "columns for %.*s\n", VAL_TYPE(ver), VAL_NULL(ver), table->len, ZSW(table->s)); dbf->free_result(connection, res); return -1; } ret = VAL_INT(ver); dbf->free_result(connection, res); return ret; } /* * Check the table version * 0 means ok, -1 means an error occurred */ int db_check_table_version(db_func_t* dbf, db_con_t* dbh, const str* table, const unsigned int version) { int ver; /* if DB does not support QUERY, return TRUE */ if (!DB_CAPABILITY(*dbf, DB_CAP_QUERY)) return 0; ver = db_table_version(dbf, dbh, table); if (ver < 0) { LM_ERR("querying version for table %.*s\n", table->len, table->s); return -1; } else if (ver != version) { LM_ERR("invalid version %d for table %.*s found, expected %d\n", ver, table->len, table->s, version); return -1; } return 0; } /* * Store name of table that will be used by * subsequent database functions */ int db_use_table(db_con_t* _h, const str* _t) { if (!_h || !_t || !_t->s) { LM_ERR("invalid parameter value %p, %p\n", _h, _t); return -1; } CON_TABLE(_h) = _t; return 0; } opensips-2.2.2/db/db.h000066400000000000000000000451611300170765700145150ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db.h * \brief Generic Database Interface * * This is a generic database interface for modules that need to utilize a * database. The interface should be used by all modules that access database. * The interface will be independent of the underlying database server. * Notes: * If possible, use the predefined macros if you need to access any structure * attributes. * For additional description, see the comments in the sources of mysql module. * * If you want to see more complicated examples of how the API could be used, * take a look at the sources of the usrloc or auth modules. */ #ifndef DB_H #define DB_H #include "db_key.h" #include "db_op.h" #include "db_val.h" #include "db_con.h" #include "db_res.h" #include "db_cap.h" #include "db_con.h" #include "db_row.h" #include "db_ps.h" #include "../globals.h" /** * \brief Specify table name that will be used for subsequent operations. * * The function db_use_table takes a table name and stores it db_con_t structure. * All subsequent operations (insert, delete, update, query) are performed on * that table. * \param _h database connection handle * \param _t table name * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_use_table_f)(db_con_t* _h, const str * _t); /** * \brief Initialize database connection and obtain the connection handle. * * This function initialize the database API and open a new database * connection. This function must be called after bind_dbmod but before any * other database API function is called. * * The function takes one parameter, the parameter must contain the database * connection URL. The URL is of the form * mysql://username:password\@host:port/database where: * * username: Username to use when logging into database (optional). * password: password if it was set (optional) * host: Hosname or IP address of the host where database server lives (mandatory) * port: Port number of the server if the port differs from default value (optional) * database: If the database server supports multiple databases, you must specify the * name of the database (optional). * \see bind_dbmod * \param _sqlurl database connection URL * \return returns a pointer to the db_con_t representing the connection if it was * successful, otherwise 0 is returned */ typedef db_con_t* (*db_init_f) (const str* _sqlurl); /** * \brief Close a database connection and free all memory used. * * The function closes previously open connection and frees all previously * allocated memory. The function db_close must be the very last function called. * \param _h db_con_t structure representing the database connection */ typedef void (*db_close_f) (db_con_t* _h); /** * \brief Query table for specified rows. * * This function implements the SELECT SQL directive. * If _k and _v parameters are NULL and _n is zero, you will get the whole table. * * if _c is NULL and _nc is zero, you will get all table columns in the result. * _r will point to a dynamically allocated structure, it is neccessary to call * db_free_result function once you are finished with the result. * * If _op is 0, equal (=) will be used for all key-value pairs comparisons. * * Strings in the result are not duplicated, they will be discarded if you call * db_free_result, make a copy yourself if you need to keep it after db_free_result. * * You must call db_free_result before you can call db_query again! * \see db_free_result * * \param _h database connection handle * \param _k array of column names that will be compared and their values must match * \param _op array of operators to be used with key-value pairs * \param _v array of values, columns specified in _k parameter must match these values * \param _c array of column names that you are interested in * \param _n number of key-value pairs to match in _k and _v parameters * \param _nc number of columns in _c parameter * \param _o order by statement for query * \param _r address of variable where pointer to the result will be stored * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_query_f) (const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r); /** * \brief Fetch a number of rows from a result. * * The function fetches a number of rows from a database result. If the number * of wanted rows is zero, the function returns anything with a result of zero. * \param _h structure representing database connection * \param _r structure for the result * \param _n the number of rows that should be fetched * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_fetch_result_f) (const db_con_t* _h, db_res_t** _r, const int _n); /** * \brief Raw SQL query. * * This function can be used to do database specific queries. Please * use this function only if needed, as this creates portability issues * for the different databases. Also keep in mind that you need to * escape all external data sources that you use. You could use the * escape_common and unescape_common functions in the core for this task. * \see escape_common * \see unescape_common * \param _h structure representing database connection * \param _s the SQL query * \param _r structure for the result * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_raw_query_f) (const db_con_t* _h, const str* _s, db_res_t** _r); /** * \brief Free a result allocated by db_query. * * This function frees all memory allocated previously in db_query. Its * neccessary to call this function on a db_res_t structure if you don't need the * structure anymore. You must call this function before you call db_query again! * \param _h database connection handle * \param _r pointer to db_res_t structure to destroy * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_free_result_f) (db_con_t* _h, db_res_t* _r); /** * \brief Insert a row into the specified table. * * This function implements INSERT SQL directive, you can insert one or more * rows in a table using this function. * \param _h database connection handle * \param _k array of keys (column names) * \param _v array of values for keys specified in _k parameter * \param _n number of keys-value pairs int _k and _v parameters * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_insert_f) (const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /** * \brief Delete a row from the specified table. * * This function implements DELETE SQL directive, it is possible to delete one or * more rows from a table. * If _k is NULL and _v is NULL and _n is zero, all rows are deleted, the * resulting table will be empty. * If _o is NULL, the equal operator "=" will be used for the comparison. * * \param _h database connection handle * \param _k array of keys (column names) that will be matched * \param _o array of operators to be used with key-value pairs * \param _v array of values that the row must match to be deleted * \param _n number of keys-value parameters in _k and _v parameters * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_delete_f) (const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n); /** * \brief Update some rows in the specified table. * * The function implements UPDATE SQL directive. It is possible to modify one * or more rows in a table using this function. * \param _h database connection handle * \param _k array of keys (column names) that will be matched * \param _o array of operators to be used with key-value pairs * \param _v array of values that the row must match to be modified * \param _uk array of keys (column names) that will be modified * \param _uv new values for keys specified in _k parameter * \param _n number of key-value pairs in _k and _v parameters * \param _un number of key-value pairs in _uk and _uv parameters * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_update_f) (const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un); /** * \brief Insert a row and replace if one already exists. * * The function implements the REPLACE SQL directive. It is possible to insert * a row and replace if one already exists. The old row will be deleted before * the insertion of the new data. * \param _h structure representing database connection * \param _k key names * \param _v values of the keys * \param _n number of key=value pairs * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_replace_f) (const db_con_t* handle, const db_key_t* keys, const db_val_t* vals, const int n); /** * \brief Retrieve the last inserted ID in a table. * * The function returns the value generated for an AUTO_INCREMENT column by the * previous INSERT or UPDATE statement. Use this function after you have * performed an INSERT statement into a table that contains an AUTO_INCREMENT * field. * \param _h structure representing database connection * \return returns the ID as integer or returns 0 if the previous statement * does not use an AUTO_INCREMENT value. */ typedef int (*db_last_inserted_id_f) (const db_con_t* _h); /** * \brief Insert a row into specified table, update on duplicate key. * * The function implements the INSERT ON DUPLICATE KEY UPDATE SQL directive. * It is possible to insert a row and update if one already exists. * The old row will not deleted before the insertion of the new data. * \param _h structure representing database connection * \param _k key names * \param _v values of the keys * \param _n number of key=value pairs * \return returns 0 if everything is OK, otherwise returns value < 0 */ typedef int (*db_insert_update_f) (const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /** * \brief Asynchronous raw SQL query on a separate DB connection. * Returns immediately. * * If all currently open connections are in use, it will attempt to open a new * one, up to "db_max_async_connections". If maximum is reached, the query is * done synchronously! * * \param _h structure representing the database handle * \param _s the SQL query string * \param _priv data that shall be populated by the engine * !!! must be preserved by the upper layer while the query is run * \return * success: Unix FD for polling * failure: negative error code */ typedef int (*db_async_raw_query_f) (db_con_t *_h, const str *_q, void **_priv); /* * \brief Reads data from the given fd's SQL connection. Populates the query * result parameter when it resumes fetching data for the last time. * * The results ("_r" output parameter) are ready to be used only when data is * fully read (i.e. iff async_status == ASYNC_DONE). * * After the results are processed by the calling module, they must be freed * using the "db_async_free_result_f" function. * * \param _h structure representing the database handle * \param fd read file descriptor obtained in starting phase * \param _r structure for the result * \param _priv data that shall be populated by the engine * !!! the same data pointer passed to the "query" function call * \return: * -> 0 on success, negative on failure * -> also populates the global "async_status": ASYNC_CONTINUE / ASYNC_DONE */ typedef int (*db_async_resume_f) (db_con_t *_h, int fd, db_res_t **_r, void *_priv); /* * \brief Performs the necessary cleanup of asynchronous query results and * their associated internal structures * * This function must be called once for every "async_resume" call, after the * query has been completed (i.e. "async_resume" resulted in ASYNC_DONE) and * its results have been processed by the calling module. * * \param _h structure representing the database handle * \param _r structure for the result * \param _priv data that shall be populated by the engine * !!! the same data pointer passed to the "query" and "resume" calls * \return: * -> 0 on success, negative on failure */ typedef int (*db_async_free_result_f) (db_con_t *_h, db_res_t *_r, void *_priv); /** * \brief Database module callbacks * * This structure holds function pointer to all database functions. Before this * structure can be used it must be initialized with bind_dbmod. * \see bind_dbmod */ typedef struct db_func { unsigned int cap; /* Capability vector of the database transport */ db_use_table_f use_table; /* Specify table name */ db_init_f init; /* Initialize database connection */ db_close_f close; /* Close database connection */ db_query_f query; /* query a table */ db_fetch_result_f fetch_result; /* fetch result */ db_raw_query_f raw_query; /* Raw query - SQL */ db_free_result_f free_result; /* Free a query result */ db_insert_f insert; /* Insert into table */ db_delete_f delete; /* Delete from table */ db_update_f update; /* Update table */ db_replace_f replace; /* Replace row in a table */ db_last_inserted_id_f last_inserted_id; /* Retrieve the last inserted ID in a table */ db_insert_update_f insert_update; /* Insert into table, update on duplicate key */ db_async_raw_query_f async_raw_query; /* Starts an asynchronous raw query */ db_async_resume_f async_resume; /* Called on progress or completed query */ db_async_free_result_f async_free_result; /* Clean up after an async query */ } db_func_t; /** * \brief Bind database module functions * * This function is special, it's only purpose is to call find_export function in * the core and find the addresses of all other database related functions. The * db_func_t callback given as parameter is updated with the found addresses. * * This function must be called before any other database API call! * * The database URL is of the form "mysql://username:password@host:port/database" or * "mysql" (database module name). * In the case of a database connection URL, this function looks only at the first * token (the database protocol). In the example above that would be "mysql": * \see db_func_t * \param mod database connection URL or a database module name * \param dbf database module callbacks * \return returns 0 if everything is OK, otherwise returns value < 0 */ int db_bind_mod(const str* mod, db_func_t* dbf); /** * \brief Helper for db_init function. * * This helper method do the actual work for the database specific db_init * functions. * \param url database connection URL * \param (*new_connection)() Pointer to the db specific connection creation method * \return returns a pointer to the db_con_t representing the connection if it was successful, otherwise 0 is returned. */ db_con_t* db_do_init(const str* url, void* (*new_connection)()); /** * \brief Helper for db_close function. * * This helper method does some work for the closing of a database * connection. No function should be called after this * \param _h database connection handle * \param (*free_connection) Pointer to the db specifc free_connection method */ void db_do_close(db_con_t* _h, void (*free_connection)()); /** * \brief Get the version of a table. * * Returns the version number of a given table from the version table. * Instead of this function you could also use db_check_table_version * \param dbf database module callbacks * \param con database connection handle * \param table checked table * \return the version number if present, 0 if no version data available, < 0 on error */ int db_table_version(const db_func_t* dbf, db_con_t* con, const str* table); /** * \brief Check the table version * * Small helper function to check the table version. * \param dbf database module callbacks * \param dbh database connection handle * \param table checked table * \param \version checked version * \return 0 means ok, -1 means an error occurred */ int db_check_table_version(db_func_t* dbf, db_con_t* dbh, const str* table, const unsigned int version); /** * \brief Stores the name of a table. * * Stores the name of the table that will be used by subsequent database * functions calls in a db_con_t structure. * \param _h database connection handle * \param _t stored name * \return 0 if everything is ok, otherwise returns value < 0 */ int db_use_table(db_con_t* _h, const str* _t); /** * \brief Bind the DB API exported by a module. * * The function links the functions implemented by the module to the members * of db_func_t structure * \param dbb db_func_t structure representing the variable where to bind * \return 0 if everything is ok, otherwise returns -1 */ typedef int (*db_bind_api_f)(const str* mod, db_func_t *dbb); /** * Method that returns an estimate of how many rows may be allocated in pkg. * You must use a smaller size than is available to take into account * memory fragmentation. * input: * payload_size: the total size of data that will be stored in a row * column_count: the column count, used for aproximating the overhead * return > 0 : estimate of how many rows may be allocated * = 0 : allocator does not support statistics. * < 0 : allocator internal error when counting. -> you should ignore it */ int estimate_available_rows( int payload_size, int column_count); #define init_db_url(_db_url , _can_be_null) \ do{\ if (_db_url.s==NULL) {\ if (db_default_url==NULL) { \ if (!_can_be_null) {\ LM_ERR("DB URL is not defined!\n"); \ return -1; \ } \ } else { \ _db_url.s = db_default_url; \ _db_url.len = strlen(_db_url.s); \ } \ } else {\ _db_url.len = strlen(_db_url.s); \ } \ }while(0) #endif /* DB_H */ opensips-2.2.2/db/db_async.c000066400000000000000000000067001300170765700157010ustar00rootroot00000000000000/* * MySQL async connection array management * * Copyright (C) 2015 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2015-01-XX initial version (liviu) */ #include #include "db_async.h" #include "db_pool.h" #include "../dprint.h" #include "../error.h" /* * aux variable which holds the default connection (used in blocking mode) * while async database operations are done (separate connections & queries) */ static struct pool_con *sync_con; struct pool_con *db_init_async(db_con_t *_h, get_con_fd_f get_fd, int **fd_ref, void *(*new_connection)(const struct db_id *)) { struct pool_con *con = (struct pool_con *)_h->tail; void *new; if (con->no_transfers == db_max_async_connections) return NULL; /* no idle connections for async queries? open a new one! */ if (!con->async_pool) { new = new_connection(con->id); if (!new) { LM_ERR("failed to open new DB connection on " "%s://XXXX:XXXX@%s:%d/%s\n", con->id->scheme, con->id->host, con->id->port, con->id->database); return NULL; } } else { new = con->async_pool; con->async_pool = con->async_pool->next; } *fd_ref = &con->transfers[con->no_transfers].fd; con->transfers[con->no_transfers].fd = get_fd(new); con->transfers[con->no_transfers].con = new; LM_DBG(">> %d/%d transfers: (%d - %p)\n", con->no_transfers + 1, db_max_async_connections, con->transfers[con->no_transfers].fd, con->transfers[con->no_transfers].con); con->no_transfers++; /* switch to the new async con */ db_switch_to_async(_h, new); return new; } void db_switch_to_async(db_con_t *_h, struct pool_con *async_con) { sync_con = (struct pool_con *)_h->tail; _h->tail = (unsigned long)async_con; } void db_switch_to_sync(db_con_t *_h) { if (!sync_con) { LM_BUG("sync_con == NULL"); abort(); } /* switch to sync con */ _h->tail = (unsigned long)sync_con; } void db_store_async_con(db_con_t *_h, struct pool_con *con) { int i; struct pool_con *tail = (struct pool_con *)_h->tail; con->next = tail->async_pool; tail->async_pool = con; LM_DBG(">> restore conn %p\n", con); for (i = 0; i < tail->no_transfers; i++) { if (tail->transfers[i].con == con) { tail->no_transfers--; for (; i < tail->no_transfers; i++) tail->transfers[i] = tail->transfers[i + 1]; return; } } LM_BUG("DB con %p not found", con); abort(); } struct pool_con *db_match_async_con(int fd, db_con_t *_h) { int i, max; struct db_transfer *transfers; LM_DBG(">> match fd %d\n", fd); transfers = ((struct pool_con *)_h->tail)->transfers; max = ((struct pool_con *)_h->tail)->no_transfers; for (i = 0; i < max; i++) if (fd == transfers[i].fd) return (struct pool_con *)_h->tail; return NULL; } opensips-2.2.2/db/db_async.h000066400000000000000000000057321300170765700157120ustar00rootroot00000000000000/* * MySQL async connection array management * * Copyright (C) 2015 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2015-01-XX initial version (liviu) */ /** * Basic primitives which enable async DB support on top of existing code * while keeping the existing logic working exactly as before */ #ifndef DB_ASYNC_H #define DB_ASYNC_H #include "db_con.h" typedef int (*get_con_fd_f) (void *con); /** * Sets up the DB handle for an asynchronous query. A new connection is * opened if necessary, otherwise one is grabbed from the idle pool. * * params: * _h - DB handle * get_fd - function which returns a read file descriptor (used for polling) * from the backend's connection structure * fd_ref - if the connection fetched from the pool disconnected * (requiring a reconnect operation), or if you happen to know its fd * has changed, make sure you also update the reference passed on here * new_connection - backend-specific function to allocate and set up a new con */ struct pool_con *db_init_async(db_con_t *_h, get_con_fd_f get_fd, int **fd_ref, void *(*new_connection)(const struct db_id *)); /** * Replaces the currently in use connection of the DB handle with "async_con" * * Must not be called twice in a row */ void db_switch_to_async(db_con_t *_h, struct pool_con *async_con); /** * Restores the DB handle in its normal state (i.e. ready for blocking queries) * after a previous call to "db_switch_to_async" * * MUST be called after initiating async operations and/or if: * * a previous db_switch_to_async() was done * * a previous db_match_async_con() was done */ void db_switch_to_sync(db_con_t *_h); /** * Places the given connection back into the async idle pool. * * MUST be called after db_switch_to_sync(). * * MUST be called if: * * errors occurred while starting up a new async transfer * * a transfer is fully completed. */ void db_store_async_con(db_con_t *_h, struct pool_con *con); /** * Attempts to match the given fd to one of the ongoing async DB transfers. * Returns the DB connection of the given fd or NULL if not found */ struct pool_con *db_match_async_con(int fd, db_con_t *_h); #endif /* DB_ASYNC_H */ opensips-2.2.2/db/db_cap.h000066400000000000000000000050041300170765700153300ustar00rootroot00000000000000/* * Copyright (C) 2001-2004 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db_cap.h * \brief Data structures that represents capabilities in the database. * * This file defines data structures that represents certain database * capabilities. It also provides some macros for convenient access to this * values. */ #ifndef DB_CAP_H #define DB_CAP_H /** * Represents the capabilities that a database driver supports. */ typedef enum db_cap { DB_CAP_QUERY = 1 << 0, /**< driver can perform queries */ DB_CAP_RAW_QUERY = 1 << 1, /**< driver can perform raw queries */ DB_CAP_ASYNC_RAW_QUERY = 1 << 2, DB_CAP_INSERT = 1 << 3, /**< driver can insert data */ DB_CAP_DELETE = 1 << 4, /**< driver can delete data */ DB_CAP_UPDATE = 1 << 5, /**< driver can update data */ DB_CAP_REPLACE = 1 << 6, /**< driver can replace data (also known as INSERT OR UPDATE) */ DB_CAP_FETCH = 1 << 7, /**< driver supports fetch result queries */ DB_CAP_LAST_INSERTED_ID = 1 << 8, /**< driver can return the ID of the last insert operation */ DB_CAP_INSERT_UPDATE = 1 << 9, /**< driver can insert data into database and update on duplicate */ DB_CAP_MULTIPLE_INSERT = 1 << 10, /**< driver can insert multiple rows at once */ } db_cap_t; /** * All database capabilities except raw_query, replace, insert_update and * last_inserted_id which should be checked separately when needed */ #define DB_CAP_ALL (DB_CAP_QUERY | DB_CAP_INSERT | DB_CAP_DELETE | DB_CAP_UPDATE) /** * Returns true if all the capabilities in cpv are supported by module * represented by dbf, false otherwise */ #define DB_CAPABILITY(dbf, cpv) (((dbf).cap & (cpv)) == (cpv)) #endif /* DB_CAP_H */ opensips-2.2.2/db/db_con.h000066400000000000000000000040161300170765700153460ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_con.h * \brief Type that represents a database connection */ #ifndef DB_CON_H #define DB_CON_H #include "../str.h" #include "db_ps.h" #include "db_id.h" /** * This structure represents a database connection, pointer to this structure * are used as a connection handle from modules uses the db API. */ typedef struct { const str* table; /**< Default table that should be used */ db_ps_t* curr_ps; /**< Prepared statement to be used for next query */ struct query_list *ins_list; /**< Insert list to be used for the next insert */ unsigned long tail; /**< Hook to implementation-specific database state */ str url; /**< URL that this connection is bound on */ int flags; } db_con_t; /** Return the table of the connection handle */ #define CON_TABLE(cn) ((cn)->table) /** Return the tail of the connection handle */ #define CON_TAIL(cn) ((cn)->tail) #define CON_INSTANT_FLUSH (1<<0) #define CON_OR_OPERATOR (1<<1) #define CON_USE_OR_OP(con) \ do { \ *((int *)&(con)->flags) |= CON_OR_OPERATOR; \ } while (0) #define CON_OR_RESET(con) \ do { \ *((int *)&(con)->flags) &= ~CON_OR_OPERATOR; \ } while (0) #endif /* DB_CON_H */ opensips-2.2.2/db/db_id.c000066400000000000000000000147731300170765700151710ustar00rootroot00000000000000/* * Copyright (C) 2001-2005 iptel.org * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_id.c * \brief Functions for parsing a database URL and work with db identifier. */ #include "db_id.h" #include "../dprint.h" #include "../mem/mem.h" #include "../ut.h" #include #include /** * Duplicate a string * \param dst destination * \param begin start of the string * \param end end of the string */ static int dupl_string(char** dst, const char* begin, const char* end) { if (*dst) pkg_free(*dst); *dst = pkg_malloc(end - begin + 1); if ((*dst) == NULL) { return -1; } memcpy(*dst, begin, end - begin); (*dst)[end - begin] = '\0'; return 0; } /** * Parse a database URL of form * scheme://[username[:password]@]hostname[:port]/database * * \param id filled id struct * \param url parsed URL * \return 0 if parsing was successful and -1 otherwise */ static int parse_db_url(struct db_id* id, const str* url) { #define SHORTEST_DB_URL "s://a/b" #define SHORTEST_DB_URL_LEN (sizeof(SHORTEST_DB_URL) - 1) enum state { ST_SCHEME, /* Scheme part */ ST_SLASH1, /* First slash */ ST_SLASH2, /* Second slash */ ST_USER_HOST, /* Username or hostname */ ST_PASS_PORT, /* Password or port part */ ST_HOST, /* Hostname part */ ST_PORT, /* Port part */ ST_DB /* Database part */ }; enum state st; unsigned int len, i; const char* begin; char* prev_token; if (!id || !url || !url->s) { return -1; } len = url->len; if (len < SHORTEST_DB_URL_LEN) { return -1; } /* Initialize all attributes to 0 */ memset(id, 0, sizeof(struct db_id)); st = ST_SCHEME; begin = url->s; prev_token = 0; for(i = 0; i < len; i++) { switch(st) { case ST_SCHEME: switch(url->s[i]) { case ':': st = ST_SLASH1; if (dupl_string(&id->scheme, begin, url->s + i) < 0) goto err; break; } break; case ST_SLASH1: switch(url->s[i]) { case '/': st = ST_SLASH2; break; default: goto err; } break; case ST_SLASH2: switch(url->s[i]) { case '/': st = ST_USER_HOST; begin = url->s + i + 1; break; default: goto err; } break; case ST_USER_HOST: switch(url->s[i]) { case '@': st = ST_HOST; if (dupl_string(&id->username, begin, url->s + i) < 0) goto err; begin = url->s + i + 1; break; case ':': st = ST_PASS_PORT; if (dupl_string(&prev_token, begin, url->s + i) < 0) goto err; begin = url->s + i + 1; break; case '/': if (dupl_string(&id->host, begin, url->s + i) < 0) goto err; if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err; return 0; } break; case ST_PASS_PORT: switch(url->s[i]) { case '@': st = ST_HOST; id->username = prev_token; if (dupl_string(&id->password, begin, url->s + i) < 0) goto err; begin = url->s + i + 1; break; case '/': id->host = prev_token; id->port = str2s(begin, url->s + i - begin, 0); if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err; return 0; } break; case ST_HOST: switch(url->s[i]) { case ':': st = ST_PORT; if (dupl_string(&id->host, begin, url->s + i) < 0) goto err; begin = url->s + i + 1; break; case '/': if (dupl_string(&id->host, begin, url->s + i) < 0) goto err; if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err; return 0; } break; case ST_PORT: switch(url->s[i]) { case '/': id->port = str2s(begin, url->s + i - begin, 0); if (dupl_string(&id->database, url->s + i + 1, url->s + len) < 0) goto err; return 0; } break; case ST_DB: break; } } if (st != ST_DB) goto err; return 0; err: if (id->scheme) pkg_free(id->scheme); if (id->username) pkg_free(id->username); if (id->password) pkg_free(id->password); if (id->host) pkg_free(id->host); if (id->database) pkg_free(id->database); if (prev_token) pkg_free(prev_token); return -1; } /** * Create a new connection identifier * \param url database URL * \return connection identifier, or zero on error */ struct db_id* new_db_id(const str* url) { struct db_id* ptr; if (!url || !url->s) { LM_ERR("invalid parameter\n"); return 0; } ptr = (struct db_id*)pkg_malloc(sizeof(struct db_id)); if (!ptr) { LM_ERR("no private memory left\n"); goto err; } memset(ptr, 0, sizeof(struct db_id)); if (parse_db_url(ptr, url) < 0) { LM_ERR("error while parsing database URL: '%.*s' \n", url->len, url->s); goto err; } /* store the original url */ ptr->url.s = url->s; ptr->url.len = url->len; return ptr; err: if (ptr) pkg_free(ptr); return 0; } /** * Compare two connection identifiers * \param id1 first identifier * \param id2 second identifier * \return one if both are equal, zero otherwise */ unsigned char cmp_db_id(const struct db_id* id1, const struct db_id* id2) { if (!id1 || !id2) return 0; if (id1->port != id2->port) return 0; if (strcmp(id1->scheme, id2->scheme)) return 0; if (id1->username != 0 && id2->username != 0) { if (strcmp(id1->username, id2->username)) return 0; } else { if (id1->username!=0 || id2->username!=0) return 0; } if (id1->password!=0 && id2->password!=0) { if(strcmp(id1->password, id2->password)) return 0; } else { if (id1->password!=0 || id2->password!=0) return 0; } if (strcasecmp(id1->host, id2->host)) return 0; if (strcmp(id1->database, id2->database)) return 0; return 1; } /** * Free a connection identifier * \param id identifier */ void free_db_id(struct db_id* id) { if (!id) return; if (id->scheme) pkg_free(id->scheme); if (id->username) pkg_free(id->username); if (id->password) pkg_free(id->password); if (id->host) pkg_free(id->host); if (id->database) pkg_free(id->database); pkg_free(id); } opensips-2.2.2/db/db_id.h000066400000000000000000000037321300170765700151670ustar00rootroot00000000000000/* * Copyright (C) 2001-2005 iptel.org * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_id.h * \brief Functions for parsing a database URL and works with db identifier. */ #ifndef _DB_ID_H #define _DB_ID_H #include "../str.h" /** Structure representing a database ID */ struct db_id { char* scheme; /**< URL scheme */ char* username; /**< Username, case sensitive */ char* password; /**< Password, case sensitive */ char* host; /**< Host or IP, case insensitive */ unsigned short port; /**< Port number */ char* database; /**< Database, case sensitive */ str url; /**< Pointer to the original url> */ }; /** * Create a new connection identifier * \param url database URL * \return new allocated db_id structure, NULL on failure */ struct db_id* new_db_id(const str* url); /** * Compare two connection identifiers * \param id1 first identifier * \param id2 second identifier * \return 1 if both identifier are equal, 0 if there not equal */ unsigned char cmp_db_id(const struct db_id* id1, const struct db_id* id2); /** * Free a connection identifier * \param id the identifier that should released */ void free_db_id(struct db_id* id); #endif /* _DB_ID_H */ opensips-2.2.2/db/db_insertq.c000066400000000000000000000352141300170765700162530ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-06-07 created (vlad) */ #include "db_insertq.h" #include "db_cap.h" #include "../timer.h" #include "../pt.h" int query_buffer_size = 0; int query_flush_time = 0; query_list_t **query_list = NULL; query_list_t **last_query = NULL; gen_lock_t *ql_lock; /* inits all the global variables needed for the insert query lists */ int init_query_list(void) { query_list = shm_malloc(sizeof(query_list_t *)); if (!query_list) { LM_ERR("no more shm\n"); return -1; } last_query = shm_malloc(sizeof(query_list_t *)); if (!last_query) { LM_ERR("no more shm\n"); shm_free(query_list); return -1; } *query_list = NULL; *last_query = NULL; ql_lock = lock_alloc(); if (ql_lock == 0) { LM_ERR("failed to alloc lock\n"); goto error0; } if (lock_init(ql_lock) == 0) { LM_ERR("failed to init lock\n"); goto error1; } LM_DBG("Initialised query list. Insert queue size = %d\n",query_buffer_size); return 0; error1: lock_dealloc(ql_lock); error0: shm_free(query_list); return -1; } /* Initializes needed structures and registeres timer * * Important : To be called before forking so all processes * inherit same queue */ int init_ql_support(void) { if (query_buffer_size > 1) { if (init_query_list() != 0 || register_timer("querydb-flush", ql_timer_routine,NULL, query_flush_time>0?query_flush_time:DEF_FLUSH_TIME, TIMER_FLAG_DELAY_ON_DELAY) < 0 ) { LM_ERR("failed initializing ins list support\n"); return -1; } } return 0; } void flush_query_list(void) { query_list_t *it; static db_ps_t my_ps = NULL; int i; /* no locks, only attendent is left at this point */ for (it=*query_list;it;it=it->next) { if (it->no_rows > 0) { memset(&it->dbf,0,sizeof(db_func_t)); if (db_bind_mod(&it->url,&it->dbf) < 0) { LM_ERR("failed to bind to db at shutdown\n"); lock_release(it->lock); continue; } it->conn[process_no] = it->dbf.init(&it->url); if (it->conn[process_no] == 0) { LM_ERR("unable to connect to DB at shutdown\n"); lock_release(it->lock); continue; } it->dbf.use_table(it->conn[process_no],&it->table); //Reset prepared statement between query lists/connections my_ps = NULL; CON_PS_REFERENCE(it->conn[process_no]) = &my_ps; /* and let's insert the rows */ for (i=0;ino_rows;i++) { if (it->dbf.insert(it->conn[process_no],it->cols,it->rows[i], it->col_no) < 0) LM_ERR("failed to insert into DB\n"); shm_free(it->rows[i]); } /* no longer need this connection */ if (it->conn[process_no] && it->dbf.close) it->dbf.close(it->conn[process_no]); } } } /* free all resources used by insert buffering */ void destroy_query_list(void) { query_list_t *it; for (it=*query_list;it;it=it->next) { lock_destroy(it->lock); lock_dealloc(it->lock); shm_free(it); } lock_destroy(ql_lock); lock_dealloc(ql_lock); } /* to be called only at shutdown * * flushes all remaining rows to DB * and frees memory */ void handle_ql_shutdown(void) { if (query_buffer_size > 1 && query_list && *query_list) { flush_query_list(); destroy_query_list(); } } /* adds a new type of query to the list * assumes ql_lock is acquired*/ void ql_add_unsafe(query_list_t *entry) { if (*query_list == NULL) { *query_list = entry; *last_query = entry; } else { (*last_query)->next=entry; entry->prev = *last_query; *last_query = entry; } } int ql_detach_rows_unsafe(query_list_t *entry,db_val_t ***ins_rows) { static db_val_t **detached_rows = NULL; int no_rows; if (detached_rows == NULL) { /* one time allocate buffer to pkg */ detached_rows = pkg_malloc(query_buffer_size*sizeof(db_val_t *)); if (detached_rows == NULL) { LM_ERR("no more pkg mem\n"); lock_release(entry->lock); return -1; } } if (entry->no_rows == 0) return 0; memcpy(detached_rows,entry->rows,query_buffer_size * sizeof(db_val_t *)); memset(entry->rows,0,query_buffer_size * sizeof(db_val_t *)); no_rows = entry->no_rows; LM_DBG("detached %d rows\n",no_rows); entry->no_rows = 0; entry->oldest_query = 0; *ins_rows = detached_rows; return no_rows; } /* safely adds a new row to the insert list * also checks if the queue is full and returns all the rows that need to * be flushed to DB to the caller * * returns the number of rows detached * * Important : it is the caller's job to shm_free the rows * after flushing to DB * */ int ql_row_add(query_list_t *entry,const db_val_t *row,db_val_t ***ins_rows) { int val_size,i,len,no_rows = 0; char *pos; db_val_t *shm_row; val_size = entry->col_no * sizeof(db_val_t); for (i=0;icol_no;i++) { if (VAL_TYPE(row+i) == DB_STR && VAL_NULL(row+i) == 0) { val_size += VAL_STR(row+i).len; continue; } if (VAL_TYPE(row+i) == DB_STRING && VAL_NULL(row+i) == 0) { val_size += strlen(VAL_STRING(row+i))+1; continue; } if (VAL_TYPE(row+i) == DB_BLOB && VAL_NULL(row+i) == 0) val_size += VAL_BLOB(row+i).len; } shm_row = shm_malloc(val_size); if (shm_row == NULL) { LM_ERR("no more shm\n"); return -1; } LM_DBG("adding row to table [%.*s] & entry %p\n",entry->table.len,entry->table.s,entry); /* save row info to shm */ pos = (char *)(shm_row + entry->col_no); memcpy(shm_row,row,entry->col_no * sizeof(db_val_t)); for (i=0;icol_no;i++) { if (VAL_TYPE(row+i) == DB_STR && VAL_NULL(row+i) == 0) { len = VAL_STR(row+i).len; VAL_STR(shm_row+i).len = len; VAL_STR(shm_row+i).s = pos; memcpy(VAL_STR(shm_row+i).s,VAL_STR(row+i).s,len); pos += len; continue; } if (VAL_TYPE(row+i) == DB_STRING && VAL_NULL(row+i) == 0) { len = strlen(VAL_STRING(row+i)) + 1; VAL_STRING(shm_row+i) = pos; memcpy((void *)VAL_STRING(shm_row+i),VAL_STRING(row+i),len); pos += len; continue; } if (VAL_TYPE(row+i) == DB_BLOB && VAL_NULL(row+i) == 0) { len = VAL_BLOB(row+i).len; VAL_BLOB(shm_row+i).len = len; VAL_BLOB(shm_row+i).s = pos; memcpy(VAL_BLOB(shm_row+i).s,VAL_BLOB(row+i).s,len); pos += len; } } LM_DBG("before locking query entry\n"); lock_get(entry->lock); /* store oldest query for timer to know */ if (entry->no_rows == 0) entry->oldest_query = time(0); entry->rows[entry->no_rows++] = shm_row; LM_DBG("query for table [%.*s] has %d rows\n",entry->table.len,entry->table.s,entry->no_rows); /* is it time to flush to DB ? */ if (entry->no_rows == query_buffer_size) { if ((no_rows = ql_detach_rows_unsafe(entry,ins_rows)) < 0) { LM_ERR("failed to detach rows for insertion\n"); lock_release(entry->lock); return -1; } } lock_release(entry->lock); return no_rows; } /* initializez a new query entry */ query_list_t *ql_init(db_con_t *con,db_key_t *cols,int col_no) { int key_size,row_q_size,size,i; char *pos; query_list_t *entry; key_size = col_no * sizeof(db_key_t) + col_no * sizeof(str); for (i=0;ilen; row_q_size = sizeof(db_val_t *) * query_buffer_size; size = sizeof(query_list_t) + counted_processes * sizeof(db_con_t *) + con->table->len + key_size + row_q_size + con->url.len; entry = shm_malloc(size); if (entry == NULL) { LM_ERR("no more shm\n"); return NULL; } memset(entry,0,size); LM_DBG("alloced %p for %d bytes\n",entry,size); entry->lock = lock_alloc(); if (entry->lock == 0) { LM_ERR("failed to alloc lock\n"); shm_free(entry); return NULL; } if (lock_init(entry->lock) == 0) { LM_ERR("failed to init lock\n"); lock_dealloc(entry->lock); shm_free(entry); return NULL; } /* deal with the table name */ entry->table.s = (char *)entry+sizeof(query_list_t); entry->table.len = con->table->len; memcpy(entry->table.s,con->table->s,con->table->len); /* deal with the columns */ entry->cols = (db_key_t *)((char *)entry+sizeof(query_list_t)+ con->table->len); entry->col_no = col_no; pos = (char *)(entry->cols + col_no) + col_no * sizeof(str); for (i=0;icols[i] = (str *)((char *)(entry->cols + col_no) + i * sizeof(str)); entry->cols[i]->len = cols[i]->len; entry->cols[i]->s = pos; memcpy(pos,cols[i]->s,cols[i]->len); pos += cols[i]->len; } /* deal with the rows */ entry->rows = (db_val_t **)((char *)entry + sizeof(query_list_t) + con->table->len + key_size); /* save url for later use by timer */ entry->url.s = (char *)entry + sizeof(query_list_t) + con->table->len + key_size + row_q_size; entry->url.len = con->url.len; memcpy(entry->url.s,con->url.s,con->url.len); /* build array of connections per process */ entry->conn = (db_con_t**)((char *)entry + sizeof(query_list_t) + con->table->len + key_size + row_q_size + con->url.len); LM_DBG("initialized query list for table [%.*s]\n",entry->table.len,entry->table.s); return entry; } /* attempts to find a query list described by the given parameters * if found, returns the entry * else, return NULL * assumes ql_lock is acquired */ query_list_t *find_query_list_unsafe(const str *table,db_key_t *cols,int col_no) { query_list_t *it,*entry=NULL; int i; LM_DBG("attempting to find q\n"); for (it=*query_list;it;it=it->next) { LM_DBG("iterating through %p\n",it); /* match number of columns */ if (it->col_no != col_no) { LM_DBG("different col no it = %d , %d\n",it->col_no,col_no); continue; } /* match table name */ if (it->table.len != table->len || memcmp(it->table.s,table->s,table->len) != 0) { LM_DBG("different tables - [%.*s] - [%.*s] \n",it->table.len,it->table.s, table->len,table->s); continue; } /* match columns */ for (i=0;icols[i]->len != cols[i]->len || memcmp(it->cols[i]->s,cols[i]->s,cols[i]->len) != 0) { LM_DBG("failed matching column %d - [%.*s] - [%.*s]\n",i,it->cols[i]->len, it->cols[i]->s,cols[i]->len,cols[i]->s); goto next_query; } } /* got here, we have found our match */ entry = it; LM_DBG("successful match on %p\n",entry); break; next_query: ; } LM_DBG("returning %p\n",entry); return entry; } /* set's the query_list that will be used for inserts * on the provided db connection * * also takes care of initialisation of this is the first process * attempting to execute this type of query */ int con_set_inslist(db_func_t *dbf,db_con_t *con,query_list_t **list, db_key_t *cols,int col_no) { query_list_t *entry; /* if buffering not enabled, ignore */ if (query_buffer_size <= 1) return 0; /* if buffering is enabled, but user is using a module * that does not support multiple inserts, * also ignore */ if (!DB_CAPABILITY(*dbf,DB_CAP_MULTIPLE_INSERT)) return 0; if (list == NULL) return 0; /* first time we are being called from this process */ if (*list == NULL) { LM_DBG("first inslist call. searching for query list \n"); lock_get(ql_lock); entry = find_query_list_unsafe(con->table,cols,col_no); if (entry == NULL) { LM_DBG("couldn't find entry for this query\n"); /* first query of this type is done from this process, * it's my job to initialize the query list * and save for later use */ entry = ql_init(con,cols,col_no); if (entry == NULL) { LM_ERR("failed to initialize ins queue\n"); lock_release(ql_lock); return -1; } ql_add_unsafe(entry); con->ins_list = entry; *list = entry; } else { LM_DBG("query list already exists - attaching\n"); /* another process has done a query of this type, * just attach to the con and save for later use */ con->ins_list = entry; *list = entry; } lock_release(ql_lock); return 0; } else { /* we've previously found our query list */ LM_DBG("process already found it's query list\n"); con->ins_list = *list; } LM_DBG("successfully returned from con_set_inslist\n"); return 0; } /* clean shm memory used by the rows */ void cleanup_rows(db_val_t **rows) { int i; if (rows != NULL) for (i=0;inext) { lock_get(it->lock); /* are there any old queries in queue ? */ if (it->oldest_query && (now - it->oldest_query > query_flush_time)) { LM_DBG("insert timer kicking in for query %p [%d]\n",it, it->no_rows); if (it->dbf.init == NULL) { /* first time timer kicked in for this query */ if (db_bind_mod(&it->url,&it->dbf) < 0) { LM_ERR("timer failed to bind to db\n"); lock_release(it->lock); continue; } } if (it->conn[process_no] == NULL) { if (!it->dbf.init) { LM_ERR("DB engine does not have init function\n"); lock_release(it->lock); continue; } it->conn[process_no] = it->dbf.init(&it->url); if (it->conn[process_no] == 0) { LM_ERR("unable to connect to DB\n"); lock_release(it->lock); continue; } LM_DBG("timer has init conn for query %p\n",it); } it->dbf.use_table(it->conn[process_no],&it->table); /* simulate the finding of the right query list */ it->conn[process_no]->ins_list = it; /* tell the core that this is the insert timer handler */ CON_FLUSH_UNSAFE(it->conn[process_no]); /* no actual new row to provide, flush existing ones */ if (it->dbf.insert(it->conn[process_no],it->cols,(db_val_t *)-1, it->col_no) < 0) LM_ERR("failed to insert rows to DB\n"); } else lock_release(it->lock); } } int ql_flush_rows(db_func_t *dbf,db_con_t *conn,query_list_t *entry) { if (query_buffer_size <= 1 || !entry) return 0; /* simulate the finding of the right query list */ conn->ins_list = entry; /* tell the core that we need to flush right away */ CON_FLUSH_SAFE(conn); /* no actual new row to provide, flush existing ones */ if (dbf->insert(conn,entry->cols,(db_val_t *)-1,entry->col_no) < 0) { LM_ERR("failed to flush rows to DB\n"); return -1; } return 0; } opensips-2.2.2/db/db_insertq.h000066400000000000000000000061671300170765700162650ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-06-07 created (vlad) */ #ifndef _DB_INSERTQ_H #define _DB_INSERTQ_H #include "db_ut.h" #include "db_query.h" #include "../locking.h" extern int query_buffer_size; /* number of insert queries that will be held in memory once this number of same type of queries pile up to this number, they will be flushed to DB */ extern int query_flush_time; /* if the query contains inserts older that query_flush_time seconds, the timer will kick in and flush to DB, to maintain "real time" sync with DB */ #define CON_HAS_INSLIST(cn) ((cn)->ins_list) #define DEF_FLUSH_TIME 10 /* seconds */ typedef struct query_list { str url; /* url for the connection - needed by timer */ db_func_t dbf; /* func handlers that will be used by timer */ db_con_t **conn; /* connection that will be used by timer */ str table; /* table that query is targetting */ db_key_t *cols; /* columns for the insert */ int col_no; /* number of columns */ db_val_t **rows; /* rows queued to be inserted */ gen_lock_t* lock; /* lock for adding rows */ int no_rows; /* number of rows in queue */ time_t oldest_query; /* timestamp of oldest query in queue */ struct query_list *next; struct query_list *prev; } query_list_t; extern query_list_t **query_list; extern gen_lock_t *ql_lock; int init_ql_support(void); int ql_row_add(query_list_t *entry,const db_val_t *row,db_val_t ***ins_rows); int ql_detach_rows_unsafe(query_list_t *entry,db_val_t ***ins_rows); int con_set_inslist(db_func_t *dbf,db_con_t *con, query_list_t **list,db_key_t *cols,int col_no); void ql_timer_routine(unsigned int ticks,void *param); int ql_flush_rows(db_func_t *dbf, db_con_t *conn,query_list_t *entry); #define CON_RESET_INSLIST(con) \ do { \ *((query_list_t **)&con->ins_list) = NULL; \ } while (0) #define IS_INSTANT_FLUSH(con) ((con)->flags & CON_INSTANT_FLUSH) #define CON_FLUSH_UNSAFE(con) \ do { \ (con)->flags |= CON_INSTANT_FLUSH; \ } while (0) #define CON_FLUSH_SAFE(con) \ do { \ lock_get((con)->ins_list->lock); \ (con)->flags |= CON_INSTANT_FLUSH; \ } while (0) #define CON_FLUSH_RESET(con,entry) \ do { \ *((int *)&(con)->flags) &= ~CON_INSTANT_FLUSH; \ lock_release((entry)->lock); \ } while (0) void cleanup_rows(db_val_t **rows); void handle_ql_shutdown(void); #endif opensips-2.2.2/db/db_key.h000066400000000000000000000022151300170765700153560ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_key.h * \brief Type that represents a database key. */ #ifndef DB_KEY_H #define DB_KEY_H #include "../ut.h" /** * This type represents a database key (column). * Every time you need to specify a key value, this type should be used. */ typedef str* db_key_t; #endif /* DB_KEY_H */ opensips-2.2.2/db/db_op.h000066400000000000000000000025541300170765700152120ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_op.h * \brief Type that represents a expression operator. */ #ifndef DB_OP_H #define DB_OP_H /** operator less than */ #define OP_LT "<" /** operator greater than */ #define OP_GT ">" /** operator equal */ #define OP_EQ "=" /** operator less than equal */ #define OP_LEQ "<=" /** operator greater than equal */ #define OP_GEQ ">=" /** operator negation */ #define OP_NEQ "!=" /** * This type represents an expression operator uses for SQL queries. */ typedef const char* db_op_t; #endif /* DB_OP_H */ opensips-2.2.2/db/db_pool.c000066400000000000000000000051531300170765700155360ustar00rootroot00000000000000/* * Copyright (C) 2001-2005 iptel.org * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_pool.c * \brief Functions for managing a pool of database connections. */ #include "../dprint.h" #include "db_pool.h" /* The head of the pool */ static struct pool_con* db_pool = 0; /* * Search the pool for a connection with * the identifier equal to id, NULL is returned * when no connection is found */ struct pool_con* pool_get(const struct db_id* id) { struct pool_con* ptr; if (!id) { LM_ERR("invalid parameter value\n"); return 0; } ptr = db_pool; while (ptr) { if (cmp_db_id(id, ptr->id)) { ptr->ref++; return ptr; } ptr = ptr->next; } return 0; } /* * Insert a new connection into the pool */ void pool_insert(struct pool_con* con) { if (!con) return; con->next = db_pool; db_pool = con; } /* * Release connection from the pool, the function * would return 1 when if the connection is not * referenced anymore and thus can be closed and * deleted by the backend. The function returns * 0 if the connection should still be kept open * because some other module is still using it. * The function returns -1 if the connection is * not in the pool. */ int pool_remove(struct pool_con* con) { struct pool_con* ptr; if (!con) return -2; if (con->ref > 1) { /* There are still other users, just * decrease the reference count and return */ LM_DBG("connection still kept in the pool\n"); con->ref--; return 0; } LM_DBG("removing connection from the pool\n"); if (db_pool == con) { db_pool = db_pool->next; } else { ptr = db_pool; while(ptr) { if (ptr->next == con) break; ptr = ptr->next; } if (!ptr) { LM_ERR("weird, connection not found in the pool\n"); return -1; } else { /* Remove the connection from the pool */ ptr->next = con->next; } } return 1; } opensips-2.2.2/db/db_pool.h000066400000000000000000000052371300170765700155460ustar00rootroot00000000000000/* * Copyright (C) 2001-2005 iptel.org * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_pool.h * \brief Functions for managing a pool of database connections. */ #ifndef _DB_POOL_H #define _DB_POOL_H #include "../globals.h" #include "db_id.h" #include "db_con.h" struct db_transfer { int fd; struct pool_con *con; }; /** * This is a stub that contains all attributes * that pool members must have, it is not really * used, real connection structures are created * by database backends. All such structures ( * created by the backends) must have these * attributes. */ struct pool_con { struct db_id* id; /**< Connection identifier */ unsigned int ref; /**< Reference count */ struct pool_con *async_pool; /**< Subpool of identical database handles */ int no_transfers; /**< Number of async queries to this backend */ struct db_transfer *transfers; /**< Array of ongoing async operations */ struct pool_con *next; /**< Next element in the pool (different db_id) */ }; /** * Search the pool for a connection with the identifier equal to * the id. * \param id searched id * \return the connection if it could be found, NULL otherwise */ struct pool_con* pool_get(const struct db_id* id); /** * Insert a new connection into the pool. * \param con the inserted connection */ void pool_insert(struct pool_con* con); /** * Release a connection from the pool, the function * would return 1 when if the connection is not * referenced anymore and thus can be closed and * deleted by the backend. The function returns * 0 if the connection should still be kept open * because some other module is still using it. * The function returns -1 if the connection is * not in the pool. * \param con connection that should be removed * \return 1 if the connection can be freed, 0 if it can't be freed, -1 if not found */ int pool_remove(struct pool_con* con); #endif /* _POOL_H */ opensips-2.2.2/db/db_ps.h000066400000000000000000000026741300170765700152210ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _DB_PS_H #define _DB_PS_H typedef void * db_ps_t; /** Is any prepared statement provided for the next query? */ #define CON_HAS_PS(cn) ((cn)->curr_ps) /** Does the connection has attached an uninitialized prepared statemen? */ #define CON_HAS_UNINIT_PS(cn) (*((cn)->curr_ps)==NULL) /** Pointer to the current used prepared statment */ #define CON_CURR_PS(cn) (*(cn)->curr_ps) /** Pointer to the address of the current used prepared statment */ #define CON_PS_REFERENCE(cn) ((cn)->curr_ps) #define CON_RESET_CURR_PS(cn) *((void***)&cn->curr_ps)=NULL #define CON_SET_CURR_PS(cn, ptr) *((void***)&cn->curr_ps)=ptr #endif opensips-2.2.2/db/db_query.c000066400000000000000000000266011300170765700157330ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * \file db/db_query.c * \brief Query helper for database drivers * * This helper methods for database queries are used from the database * SQL driver to do the actual work. Each function uses some functions from * the actual driver with function pointers to the concrete, specific * implementation. */ #include #include "../dprint.h" #include "../locking.h" #include "db_ut.h" #include "db_query.h" #include "db_insertq.h" static str sql_str; static char sql_buf[SQL_BUF_LEN]; int db_do_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r, int (*val2str) (const db_con_t*, const db_val_t*, char*, int* _len), int (*submit_query)(const db_con_t*, const str*), int (*store_result)(const db_con_t* _h, db_res_t** _r)) { int off, ret; if (!_h || !val2str || !submit_query || (_r && !store_result)) { LM_ERR("invalid parameter value\n"); goto err_exit; } if (!_c) { ret = snprintf(sql_buf, SQL_BUF_LEN, "select * from %.*s ", CON_TABLE(_h)->len, CON_TABLE(_h)->s); if (ret < 0 || ret >= SQL_BUF_LEN) goto error; off = ret; } else { ret = snprintf(sql_buf, SQL_BUF_LEN, "select "); if (ret < 0 || ret >= SQL_BUF_LEN) goto error; off = ret; ret = db_print_columns(sql_buf + off, SQL_BUF_LEN - off, _c, _nc); if (ret < 0) goto err_exit; off += ret; ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, "from %.*s ", CON_TABLE(_h)->len, CON_TABLE(_h)->s); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; } if (_n) { ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, "where "); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; ret = db_print_where(_h, sql_buf + off, SQL_BUF_LEN - off, _k, _op, _v, _n, val2str); if (ret < 0) goto err_exit; off += ret; } if (_o) { ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, " order by %.*s", _o->len, _o->s); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; } /* * Null-terminate the string for the postgres driver. Its query function * don't support a length parameter, so they need this for the correct * function of strlen. This zero is not included in the 'str' length. * We need to check the length here, otherwise we could overwrite the buffer * boundaries if off is equal to SQL_BUF_LEN. */ if (off + 1 >= SQL_BUF_LEN) goto error; sql_buf[off + 1] = '\0'; sql_str.s = sql_buf; sql_str.len = off; if (submit_query(_h, &sql_str) < 0) { LM_ERR("error while submitting query - [%.*s]\n",sql_str.len,sql_str.s); goto err_exit; } if(_r) { int tmp = store_result(_h, _r); if (tmp < 0) { LM_ERR("error while storing result for query [%.*s]\n",sql_str.len,sql_str.s); CON_OR_RESET(_h); return tmp; } } CON_OR_RESET(_h); return 0; error: LM_ERR("error while preparing query\n"); err_exit: if (_r) *_r = NULL; if (_h) CON_OR_RESET(_h); return -1; } int db_do_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r, int (*submit_query)(const db_con_t* _h, const str* _c), int (*store_result)(const db_con_t* _h, db_res_t** _r)) { if (!_h || !_s || !submit_query || !store_result) { LM_ERR("invalid parameter value\n"); return -1; } if (submit_query(_h, _s) < 0) { LM_ERR("error while submitting query\n"); return -2; } if(_r) { int tmp = store_result(_h, _r); if (tmp < 0) { LM_ERR("error while storing result"); return tmp; } } return 0; } int db_do_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c)) { int off, ret,i,no_rows=0; db_val_t **buffered_rows = NULL; if (!_h || !_k || !_v || !_n || !val2str || !submit_query) { LM_ERR("invalid parameter value\n"); return -1; } /* insert buffering is enabled ? */ if (CON_HAS_INSLIST(_h) && !CON_HAS_PS(_h)) { LM_DBG("inlist %p\n",CON_HAS_INSLIST(_h)); if (IS_INSTANT_FLUSH(_h)) { LM_DBG("timer wishing to flush \n"); /* if caller signals it's flush time ( timer, etc ), * detach rows in queue * the caller is holding the lock at this point */ no_rows = ql_detach_rows_unsafe(_h->ins_list,&buffered_rows); CON_FLUSH_RESET(_h,_h->ins_list); if (no_rows == -1) { LM_ERR("failed to detach rows for insertion\n"); goto error; } if (no_rows > 0) goto build_query; else { /* caller wanted to make sure that everything if flushed * but queue is empty */ return 0; } } /* if connection has prepared statement, leave the row insertion to the proper module func, as the submit_query func provided is a dummy one*/ if ( (no_rows = ql_row_add(_h->ins_list,_v,&buffered_rows)) < 0) { LM_ERR("failed to insert row to buffered list \n"); goto error; } LM_DBG("no rows = %d\n",no_rows); if (no_rows == 0) { /* wait for queries to pile up */ return 0; } } build_query: ret = snprintf(sql_buf, SQL_BUF_LEN, "insert into %.*s (", CON_TABLE(_h)->len, CON_TABLE(_h)->s); if (ret < 0 || ret >= SQL_BUF_LEN) goto error; off = ret; ret = db_print_columns(sql_buf + off, SQL_BUF_LEN - off, _k, _n); if (ret < 0) goto error; off += ret; if (CON_HAS_INSLIST(_h)) { if (buffered_rows != NULL || CON_HAS_PS(_h)) { /* if we have to insert now, build the query * * if a prep stmt is provided, * build a prep stmt with query_buffer_size elements */ if (CON_HAS_PS(_h)) no_rows = query_buffer_size; ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values"); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; for (i=0;i SQL_BUF_LEN) goto error0; sql_buf[off] = '\0'; sql_str.s = sql_buf; sql_str.len = off; goto submit; } else { /* wait for queries to pile up */ return 0; } } else { ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values ("); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error0; off += ret; ret = db_print_values(_h, sql_buf + off, SQL_BUF_LEN - off, _v, _n, val2str); if (ret < 0) goto error0; off += ret; if (off + 2 > SQL_BUF_LEN) goto error0; } sql_buf[off++] = ')'; sql_buf[off] = '\0'; sql_str.s = sql_buf; sql_str.len = off; submit: if (submit_query(_h, &sql_str) < 0) { LM_ERR("error while submitting query\n"); return -2; } return 0; error: cleanup_rows(buffered_rows); error0: LM_ERR("error while preparing insert operation\n"); return -1; } int db_do_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c)) { int off, ret; if (!_h || !val2str || !submit_query) { LM_ERR("invalid parameter value\n"); goto err_exit; } ret = snprintf(sql_buf, SQL_BUF_LEN, "delete from %.*s", CON_TABLE(_h)->len, CON_TABLE(_h)->s); if (ret < 0 || ret >= SQL_BUF_LEN) goto error; off = ret; if (_n) { ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, " where "); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; ret = db_print_where(_h, sql_buf + off, SQL_BUF_LEN - off, _k, _o, _v, _n, val2str); if (ret < 0) goto err_exit; off += ret; } if (off + 1 > SQL_BUF_LEN) goto error; sql_buf[off] = '\0'; sql_str.s = sql_buf; sql_str.len = off; if (submit_query(_h, &sql_str) < 0) { LM_ERR("error while submitting query\n"); CON_OR_RESET(_h); return -2; } CON_OR_RESET(_h); return 0; error: LM_ERR("error while preparing delete operation\n"); err_exit: if (_h) CON_OR_RESET(_h); return -1; } int db_do_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c)) { int off, ret; if (!_h || !_uk || !_uv || !_un || !val2str || !submit_query) { LM_ERR("invalid parameter value\n"); goto err_exit; } ret = snprintf(sql_buf, SQL_BUF_LEN, "update %.*s set ", CON_TABLE(_h)->len, CON_TABLE(_h)->s); if (ret < 0 || ret >= SQL_BUF_LEN) goto error; off = ret; ret = db_print_set(_h, sql_buf + off, SQL_BUF_LEN - off, _uk, _uv, _un, val2str); if (ret < 0) goto err_exit; off += ret; if (_n) { ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, " where "); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; ret = db_print_where(_h, sql_buf + off, SQL_BUF_LEN - off, _k, _o, _v, _n, val2str); if (ret < 0) goto err_exit; off += ret; } if (off + 1 > SQL_BUF_LEN) goto error; sql_buf[off] = '\0'; sql_str.s = sql_buf; sql_str.len = off; if (submit_query(_h, &sql_str) < 0) { LM_ERR("error while submitting query\n"); CON_OR_RESET(_h); return -2; } CON_OR_RESET(_h); return 0; error: LM_ERR("error while preparing update operation\n"); err_exit: if(_h) CON_OR_RESET(_h); return -1; } int db_do_replace(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c)) { int off, ret; if (!_h || !_k || !_v || !val2str|| !submit_query) { LM_ERR("invalid parameter value\n"); return -1; } ret = snprintf(sql_buf, SQL_BUF_LEN, "replace %.*s (", CON_TABLE(_h)->len, CON_TABLE(_h)->s); if (ret < 0 || ret >= SQL_BUF_LEN) goto error; off = ret; ret = db_print_columns(sql_buf + off, SQL_BUF_LEN - off, _k, _n); if (ret < 0) return -1; off += ret; ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values ("); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; ret = db_print_values(_h, sql_buf + off, SQL_BUF_LEN - off, _v, _n, val2str); if (ret < 0) return -1; off += ret; if (off + 2 > SQL_BUF_LEN) goto error; sql_buf[off++] = ')'; sql_buf[off] = '\0'; sql_str.s = sql_buf; sql_str.len = off; if (submit_query(_h, &sql_str) < 0) { LM_ERR("error while submitting query\n"); return -2; } return 0; error: LM_ERR("error while preparing replace operation\n"); return -1; } opensips-2.2.2/db/db_query.h000066400000000000000000000174441300170765700157450ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * \file db/db_query.h * \brief Query helper for database drivers * * This helper methods for database queries are used from the database * SQL driver to do the actual work. Each function uses some functions from * the actual driver with function pointers to the concrete, specific * implementation. */ #ifndef DB_QUERY_H #define DB_QUERY_H #include "db_key.h" #include "db_op.h" #include "db_val.h" #include "db_con.h" #include "db_row.h" #include "db_res.h" #include "db_cap.h" /** * \brief Helper function for db queries * * This method evaluates the actual arguments for the database query and * setups the string that is used for the query in the db module. * Then its submit the query and stores the result if necessary. It uses for * its work the implementation in the concrete database module. * * \param _h structure representing database connection * \param _k key names, if not present the whole table will be returned * \param _op operators * \param _v values of the keys that must match * \param _c column names that should be returned * \param _n number of key/value pairs that are compared, if zero then no comparison is done * \param _nc number of colums that should be returned * \param _o order by the specificied column, optional * \param _r the result that is returned * \param (*val2str) function pointer to the db specific val conversion function * \param (*submit_query) function pointer to the db specific query submit function * \param (*store_result) function pointer to the db specific store result function * \return zero on success, negative on errors */ int db_do_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c), int (*store_result)(const db_con_t* _h, db_res_t** _r)); /** * \brief Helper function for raw db queries * * This method evaluates the actual arguments for the database raw query * and setups the string that is used for the query in the db module. * Then its submit the query and stores the result if necessary. * It uses for its work the implementation in the concrete database module. * * \param _h structure representing database connection * \param _s char holding the raw query * \param _r the result that is returned * \param (*submit_query) function pointer to the db specific query submit function * \param (*store_result) function pointer to the db specific store result function * \return zero on success, negative on errors */ int db_do_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r, int (*submit_query)(const db_con_t* _h, const str* _c), int (*store_result)(const db_con_t* _h, db_res_t** _r)); /** * \brief Helper function for db insert operations * * This method evaluates the actual arguments for the database operation * and setups the string that is used for the insert operation in the db * module. Then its submit the query for the operation. It uses for its work * the implementation in the concrete database module. * * \param _h structure representing database connection * \param _k key names * \param _v values of the keys * \param _n number of key/value pairs * \param (*val2str) function pointer to the db specific val conversion function * \param (*submit_query) function pointer to the db specific query submit function * \return zero on success, negative on errors */ int db_do_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c)); /** * \brief Helper function for db delete operations * * This method evaluates the actual arguments for the database operation * and setups the string that is used for the delete operation in the db * module. Then its submit the query for the operation. It uses for its work * the implementation in the concrete database module. * * \param _h structure representing database connection * \param _k key names * \param _o operators * \param _v values of the keys that must match * \param _n number of key/value pairs that are compared, if zero then the whole table is deleted * \param (*val2str) function pointer to the db specific val conversion function * \param (*submit_query) function pointer to the db specific query submit function * \return zero on success, negative on errors */ int db_do_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c)); /** * \brief Helper function for db update operations * * This method evaluates the actual arguments for the database operation * and setups the string that is used for the update operation in the db * module. Then its submit the query for the operation. It uses for its work * the implementation in the concrete database module. * * \param _h structure representing database connection * \param _k key names, if not present the whole table will be returned * \param _o operators * \param _v values of the keys that must match * \param _uk: updated columns * \param _uv: updated values of the columns * \param _n number of key/value pairs that are compared, if zero then no comparison is done * \param _un: number of columns that should be updated * \param (*val2str) function pointer to the db specific val conversion function * \param (*submit_query) function pointer to the db specific query submit function * \return zero on success, negative on errors */ int db_do_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c)); /** * \brief Helper function for db delete operations * * This helper method evaluates the actual arguments for the database operation * and setups the string that is used for the replace operation in the db * module. Then its submit the query for the operation. It uses for its work the * implementation in the concrete database module. * * \param _h structure representing database connection * \param _k key names, if not present the whole table will be returned * \param _v values of the keys that must match * \param _n number of key/value pairs that are compared, if zero then no comparison is done * \param (*val2str) function pointer to the db specific val conversion function * \param (*submit_query) function pointer to the db specific query submit function * \return zero on success, negative on errors */ int db_do_replace(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*), int (*submit_query)(const db_con_t* _h, const str* _c)); #endif opensips-2.2.2/db/db_res.c000066400000000000000000000075501300170765700153610ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_res.c * \brief Functions to manage result structures * * Provides some convenience macros and some memory management * functions for result structures. */ #include "db_res.h" #include "db_row.h" #include "../dprint.h" #include "../mem/mem.h" #include /* * Release memory used by rows */ int db_free_rows(db_res_t* _r) { int i; if (!_r) { LM_ERR("invalid parameter value\n"); return -1; } LM_DBG("freeing %d rows\n", RES_ROW_N(_r)); if (RES_ROWS(_r)) { for(i = 0; i < RES_ROW_N(_r); i++) db_free_row(&(RES_ROWS(_r)[i])); LM_DBG("freeing rows at %p\n", RES_ROWS(_r)); pkg_free(RES_ROWS(_r)); RES_ROWS(_r) = NULL; } RES_ROW_N(_r) = 0; return 0; } /* * Release memory used by columns */ int db_free_columns(db_res_t* _r) { if (!_r) { LM_ERR("invalid parameter value\n"); return -1; } /* free names and types */ if (RES_NAMES(_r)) { LM_DBG("freeing result columns at %p\n", RES_NAMES(_r)); RES_TYPES(_r) = NULL; pkg_free(RES_NAMES(_r)); RES_NAMES(_r) = NULL; } return 0; } /* * Create a new result structure and initialize it */ db_res_t* db_new_result(void) { db_res_t* r = NULL; r = (db_res_t*)pkg_malloc(sizeof(db_res_t)); if (!r) { LM_ERR("no private memory left\n"); return 0; } LM_DBG("allocate %d bytes for result set at %p\n", (int)sizeof(db_res_t), r); memset(r, 0, sizeof(db_res_t)); return r; } /* * Release memory used by a result structure */ int db_free_result(db_res_t* _r) { if (!_r) { LM_ERR("invalid parameter\n"); return -1; } db_free_columns(_r); db_free_rows(_r); LM_DBG("freeing result set at %p\n", _r); pkg_free(_r); _r = NULL; return 0; } /* * Allocate storage for column names and type in existing * result structure. */ int db_allocate_columns(db_res_t* _r, const unsigned int cols) { unsigned int i; RES_NAMES(_r) = (db_key_t*)pkg_malloc ( cols * (sizeof(db_key_t)+sizeof(db_type_t)+sizeof(str)) ); if (!RES_NAMES(_r)) { LM_ERR("no private memory left\n"); return -1; } LM_DBG("allocate %d bytes for result columns at %p\n", (int)(cols * (sizeof(db_key_t)+sizeof(db_type_t)+sizeof(str))), RES_NAMES(_r)); for ( i=0 ; icol.names) /** Return the column types */ #define RES_TYPES(re) ((re)->col.types) /** Return the number of columns */ #define RES_COL_N(re) ((re)->col.n) /** Return the result rows */ #define RES_ROWS(re) ((re)->rows) /** Return the number of current result rows */ #define RES_ROW_N(re) ((re)->n) /** Return the last row of the result */ #define RES_LAST_ROW(re) ((re)->last_row) /** Return the number of total result rows */ #define RES_NUM_ROWS(re) ((re)->res_rows) /** * Release memory used by rows in a result structure. * \param _r the result that should be released * \return zero on success, negative on errors */ int db_free_rows(db_res_t* _r); /** * Release memory used by columns. This methods assumes that the string values * holding the column names are in memory allocated from the database driver, * and thus must be not freed here. * \param _r the result that should be released * \return zero on success, negative on errors */ int db_free_columns(db_res_t* _r); /** * Create a new result structure and initialize it. * \return a pointer to the new result on success, NULL on errors */ db_res_t* db_new_result(void); /** * Release memory used by a result structure. * \return zero on success, negative on errors */ int db_free_result(db_res_t* _r); /** * Allocate storage for column names and type in existing result structure. * If no more memory is available for the allocation of the types then the * already allocated memory for the names is freed. * \param _r filled result set * \param cols number of columns * \return zero on success, negative on errors */ int db_allocate_columns(db_res_t* _r, const unsigned int cols); int db_allocate_rows(db_res_t* _res, const unsigned int rows); #endif /* DB_RES_H */ opensips-2.2.2/db/db_row.c000066400000000000000000000060151300170765700153720ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_row.c * \brief Type that represents a row in a database. * * This file holds a type that represents a row in a database, some convenience * macros and a function for memory managements. */ #include "db_row.h" #include #include "../dprint.h" #include "../mem/mem.h" /* * Release memory used by row */ int db_free_row(db_row_t* _r) { int col; db_val_t* _val; if (!_r) { LM_ERR("invalid parameter value\n"); return -1; } LM_DBG("freeing row values at %p\n", ROW_VALUES(_r)); /* * Loop thru each columm, then check to determine if the storage pointed to * by db_val_t structure must be freed. This is required for all data types * which use a pointer to a buffer like DB_STRING, DB_STR and DB_BLOB and * the database module copied them during the assignment. * If this is not done, a memory leak will happen. * Don't try to free the static dummy string (as indicated from the NULL * value), as this is not valid. */ for (col = 0; col < ROW_N(_r); col++) { _val = &(ROW_VALUES(_r)[col]); switch (VAL_TYPE(_val)) { case DB_STRING: if ( (!VAL_NULL(_val)) && VAL_FREE(_val)) { LM_DBG("free VAL_STRING[%d] '%s' at %p\n", col, (char *)VAL_STRING(_val), (char *)VAL_STRING(_val)); pkg_free((char *)VAL_STRING(_val)); VAL_STRING(_val) = NULL; } break; case DB_STR: if ( (!VAL_NULL(_val)) && VAL_FREE(_val)) { LM_DBG("free VAL_STR[%d] '%.*s' at %p\n", col, VAL_STR(_val).len, VAL_STR(_val).s, VAL_STR(_val).s); pkg_free(VAL_STR(_val).s); VAL_STR(_val).s = NULL; } break; case DB_BLOB: if ( (!VAL_NULL(_val)) && VAL_FREE(_val)) { LM_DBG("free VAL_BLOB[%d] at %p\n", col, VAL_BLOB(_val).s); pkg_free(VAL_BLOB(_val).s); VAL_BLOB(_val).s = NULL; } break; default: break; } } /* HINT: this is based on db_allocate_rwos_function which allocates the * whole ROW_VALUES array in the same time with the holder, RES_ROWS * this way when RES_ROWS will be freed, all the ROW_VALUES will be freed * if using other function than db_allocate_rows for allocating take * great care when using this function */ ROW_VALUES(_r) = NULL; return 0; } opensips-2.2.2/db/db_row.h000066400000000000000000000041131300170765700153740ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_row.h * \brief Type that represents a row in a database. * * This file holds a type that represents a row in a database, some convenience * macros and a function for memory managements. */ #ifndef DB_ROW_H #define DB_ROW_H #include "db_val.h" /** * Structure holding the result of a query table function. * It represents one row in a database table. In other words, the row is an * array of db_val_t variables, where each db_val_t variable represents exactly * one cell in the table. */ typedef struct db_row { db_val_t* values; /**< Columns in the row */ int n; /**< Number of columns in the row */ } db_row_t; /** Return the columns in the row */ #define ROW_VALUES(rw) ((rw)->values) /** Return the number of colums */ #define ROW_N(rw) ((rw)->n) /** * Release memory used by a row. This method only frees values that are inside * the row if the free flag of the specific value is set. Otherwise this * storage must be released when the database specific result free function is * called. Only string based values are freed if wanted, null values are skipped. * \param _r row that should be released * \return zero on success, negative on error */ int db_free_row(db_row_t* _r); #endif /* DB_ROW_H */ opensips-2.2.2/db/db_ut.c000066400000000000000000000201771300170765700152200ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_ut.c * \brief Utility functions for database drivers. * * This utility methods are used from the database SQL driver to convert * values and print SQL queries from the internal API representation. */ #include "db_ut.h" #include "../mem/mem.h" #include "../dprint.h" #include #include #include #include #include inline int db_str2int(const char* _s, int* _v) { long tmp; char* p = NULL; if (!_s || !_v) { LM_ERR("Invalid parameter value\n"); return -1; } tmp = strtol(_s, &p, 10); if (((tmp == LONG_MAX || tmp == LONG_MIN) && errno == ERANGE) || (tmp < INT_MIN) || (tmp > INT_MAX)) { LM_ERR("Value out of range\n"); return -1; } if (p && *p != '\0') { LM_ERR("Unexpected characters: [%s]\n", p); return -2; } *_v = (int)tmp; return 0; } inline int db_str2bigint(const char* _s, long long* _v) { long long tmp; char* p = NULL; if (!_s || !_v) { LM_ERR("Invalid parameter value\n"); return -1; } tmp = strtoll(_s, &p, 10); if ((tmp == LLONG_MIN || tmp == LLONG_MAX) && errno == ERANGE) { LM_ERR("Value out of range\n"); return -1; } if (p && *p != '\0') { LM_ERR("Unexpected characters: [%s]\n", p); return -2; } *_v = tmp; return 0; } /* * Convert a string to double */ inline int db_str2double(const char* _s, double* _v) { if ((!_s) || (!_v)) { LM_ERR("Invalid parameter value\n"); return -1; } *_v = atof(_s); return 0; } /* * Convert an integer to string */ inline int db_int2str(int _v, char* _s, int* _l) { int ret; if ((!_s) || (!_l) || (!*_l)) { LM_ERR("Invalid parameter value\n"); return -1; } ret = snprintf(_s, *_l, "%-d", _v); if (ret < 0 || ret >= *_l) { LM_ERR("Error in snprintf\n"); return -1; } *_l = ret; return 0; } /* * Convert an integer to string */ inline int db_bigint2str(long long _v, char* _s, int* _l) { int ret; if ((!_s) || (!_l) || (!*_l)) { LM_ERR("Invalid parameter value\n"); return -1; } ret = snprintf(_s, *_l, "%-lld", _v); if (ret < 0 || ret >= *_l) { LM_ERR("Error in snprintf\n"); return -1; } *_l = ret; return 0; } /* * Convert a double to string */ inline int db_double2str(double _v, char* _s, int* _l) { int ret; if ((!_s) || (!_l) || (!*_l)) { LM_ERR("Invalid parameter value\n"); return -1; } ret = snprintf(_s, *_l, "%-10.2f", _v); if (ret < 0 || ret >= *_l) { LM_ERR("Error in snprintf\n"); return -1; } *_l = ret; return 0; } /* * Convert a string to time_t */ inline int db_str2time(const char* _s, time_t* _v) { struct tm time; if ((!_s) || (!_v)) { LM_ERR("Invalid parameter value\n"); return -1; } /* Convert database time representation to time_t structure It is necessary to zero tm structure first */ memset(&time, '\0', sizeof(struct tm)); if (strptime(_s, "%Y-%m-%d %H:%M:%S", &time) == NULL) { LM_ERR("Error during time conversion\n"); return -1; } /* Daylight saving information got lost in the database * so let mktime to guess it. This eliminates the bug when * contacts reloaded from the database have different time * of expiration by one hour when daylight saving is used */ time.tm_isdst = -1; *_v = mktime(&time); return 0; } inline int db_time2str_nq(time_t _v, char* _s, int* _l) { struct tm* t; int l; if ((!_s) || (!_l) || (*_l < 2)) { LM_ERR("Invalid parameter value\n"); return -1; } /* Convert time_t structure to format accepted by the database */ t = localtime(&_v); l = strftime(_s, *_l -1, "%Y-%m-%d %H:%M:%S", t); if (l == 0) { LM_ERR("Error during time conversion\n"); /* the value of _s is now unspecified */ _s = NULL; _l = 0; return -1; } *_l = l; return 0; } inline int db_time2str(time_t _v, char* _s, int* _l) { if ((!_s) || (!_l) || (*_l < 2)) { LM_ERR("Invalid parameter value\n"); return -1; } *_s++ = '\''; if (db_time2str_nq(_v, _s, _l)!=0) return -1; *(_s + *_l) = '\''; *_l += 2; return 0; } /* * Print list of columns separated by comma */ inline int db_print_columns(char* _b, const int _l, const db_key_t* _c, const int _n) { int i, ret, len = 0; if ((!_c) || (!_n) || (!_b) || (!_l)) { LM_ERR("Invalid parameter value\n"); return -1; } for(i = 0; i < _n; i++) { if (i == (_n - 1)) { ret = snprintf(_b + len, _l - len, "%.*s ", _c[i]->len, _c[i]->s); if (ret < 0 || ret >= (_l - len)) goto error; len += ret; } else { ret = snprintf(_b + len, _l - len, "%.*s,", _c[i]->len, _c[i]->s); if (ret < 0 || ret >= (_l - len)) goto error; len += ret; } } return len; error: LM_ERR("Error in snprintf\n"); return -1; } /* * Print values of SQL statement */ int db_print_values(const db_con_t* _c, char* _b, const int _l, const db_val_t* _v, const int _n, int (*val2str)(const db_con_t*, const db_val_t*, char*, int*)) { int i, l, len = 0; if (!_c || !_b || !_l || !_v || !_n) { LM_ERR("Invalid parameter value\n"); return -1; } for(i = 0; i < _n; i++) { if (CON_HAS_PS(_c)) { *(_b+len++) = '?'; } else { l = _l - len; if ( (*val2str)(_c, _v + i, _b + len, &l) < 0) { LM_ERR("Error while converting value to string\n"); return -1; } len += l; } if (i != (_n - 1)) { *(_b + len) = ','; len++; } } return len; } /* * Print where clause of SQL statement */ int db_print_where(const db_con_t* _c, char* _b, const int _l, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*)) { int i, l, ret, len = 0; if (!_c || !_b || !_l || !_k || !_v || !_n) { LM_ERR("Invalid parameter value\n"); return -1; } for(i = 0; i < _n; i++) { if (_o) { ret = snprintf(_b + len, _l - len, "%.*s%s", _k[i]->len, _k[i]->s, _o[i]); if (ret < 0 || ret >= (_l - len)) goto error; len += ret; } else { ret = snprintf(_b + len, _l - len, "%.*s=", _k[i]->len, _k[i]->s); if (ret < 0 || ret >= (_l - len)) goto error; len += ret; } if (CON_HAS_PS(_c)) { *(_b+len++) = '?'; } else { l = _l - len; if ( (*val2str)(_c, &(_v[i]), _b + len, &l) < 0) { LM_ERR("Error while converting value to string\n"); return -1; } len += l; } if (i != (_n - 1)) { if (_c->flags & CON_OR_OPERATOR) ret = snprintf(_b + len, _l - len, " OR "); else ret = snprintf(_b + len, _l - len, " AND "); if (ret < 0 || ret >= (_l - len)) goto error; len += ret; } } return len; error: LM_ERR("Error in snprintf\n"); return -1; } /* * Print set clause of update SQL statement */ int db_print_set(const db_con_t* _c, char* _b, const int _l, const db_key_t* _k, const db_val_t* _v, const int _n, int (*val2str)(const db_con_t*, const db_val_t*,char*, int*)) { int i, l, ret, len = 0; if (!_c || !_b || !_l || !_k || !_v || !_n) { LM_ERR("Invalid parameter value\n"); return -1; } for(i = 0; i < _n; i++) { ret = snprintf(_b + len, _l - len, "%.*s=", _k[i]->len, _k[i]->s); if (ret < 0 || ret >= (_l - len)) goto error; len += ret; if (CON_HAS_PS(_c)) { *(_b+len++) = '?'; } else { l = _l - len; if ( (*val2str)(_c, &(_v[i]), _b + len, &l) < 0) { LM_ERR("Error while converting value to string\n"); return -1; } len += l; } if (i != (_n - 1)) { if ((_l - len) >= 1) { *(_b + len++) = ','; } } } return len; error: LM_ERR("Error in snprintf\n"); return -1; } opensips-2.2.2/db/db_ut.h000066400000000000000000000143201300170765700152160ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_ut.h * \brief Utility functions for database drivers. * * This utility methods are used from the database SQL driver to convert * values and print SQL queries from the internal API representation. */ #ifndef DB_UT_H #define DB_UT_H /** * maximal SQL buffer length for database drivers */ #define SQL_BUF_LEN 65536 /** * make strptime available * use 600 for 'Single UNIX Specification, Version 3' */ #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 /* glibc2 on linux, bsd */ #define _XOPEN_SOURCE_EXTENDED 1 /* solaris */ #endif /** * _XOPEN_SOURCE creates conflict in swab definition in Solaris */ #ifdef __OS_solaris #undef _XOPEN_SOURCE #endif /** * Compatabilty for older compilers where we get stuck with the #defines for the first * include of features.h * Ensures 'syscall' is exported which is required by some locking. * Should not cause any conflicts? */ #ifndef _BSD_SOURCE #define _BSD_SOURCE /** * _BSD_SOURCE has been replaced with the _DEFAULT_SOURCE macro */ #define _DEFAULT_SOURCE 1 #endif #include #undef _XOPEN_SOURCE #undef _XOPEN_SOURCE_EXTENDED #include "db_key.h" #include "db.h" /** * Converts a char into an integer value. * * \param _s source value * \param _v target value * \return zero on sucess, negative on conversion errors */ int db_str2int(const char* _s, int* _v); /** * Converts a char into an int64 value. * * \param _s source value * \param _v target value * \return zero on sucess, negative on conversion errors */ int db_str2bigint(const char* _s, long long* _v); /** * Converts a char into a double value. * * \param _s source value * \param _v target value * \return zero on sucess, negative on conversion errors */ int db_str2double(const char* _s, double* _v); /** * Converts a integer value in a char pointer. * * \param _v source value * \param _s target value * \param _l available length and target length * \return zero on sucess, negative on conversion errors */ int db_int2str(int _v, char* _s, int* _l); /** * Converts an int64 value in a char pointer. * * \param _v source value * \param _s target value * \param _l available length and target length * \return zero on sucess, negative on conversion errors */ int db_bigint2str(long long _v, char* _s, int* _l); /** * Converts a double value into a char pointer. * * \param _v source value * \param _s target value * \param _l available length and target length * \return zero on sucess, negative on conversion errors */ int db_double2str(double _v, char* _s, int* _l); /** * Convert a time_t value to string. * * \param _v source value * \param _s target value * \param _l available length and target length * \return zero on sucess, negative on conversion errors * \todo This functions add quotes to the time value. This * should be done in the val2str function, as some databases * like db_berkeley don't need or like this at all. */ int db_time2str(time_t _v, char* _s, int* _l); /** * Converts a char into a time_t value. * * \param _s source value * \param _v target value * \return zero on sucess, negative on conversion errors */ int db_str2time(const char* _s, time_t* _v); int db_time2str_nq(time_t _v, char* _s, int* _l); /** * Print columns for a SQL statement, separated by commas. * * \param _b target char * \param _l length of the target * \param _c keys that should be printed * \param _n number of keys * \return the length of the printed result on success, negative on errors */ int db_print_columns(char* _b, const int _l, const db_key_t* _c, const int _n); /** * Print values for a SQL statement. * * \param _c structure representing database connection * \param _b target char * \param _l length of the target * \param _v values that should be printed * \param _n number of values * \param (*val2str) function pointer to a db specific conversion function * \return the length of the printed result on success, negative on errors */ int db_print_values(const db_con_t* _c, char* _b, const int _l, const db_val_t* _v, const int _n, int (*val2str)(const db_con_t*, const db_val_t*, char*, int*)); /** * Print where clause for a SQL statement. * * \param _c structure representing database connection * \param _b target char * \param _l length of the target * \param _k keys that should be printed * \param _o optional operators * \param _v values that should be printed * \param _n number of key/value pairs * \param (*val2str) function pointer to a db specific conversion function * \return the length of the printed result on success, negative on errors */ int db_print_where(const db_con_t* _c, char* _b, const int _l, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*)); /** * Print set clause for a SQL statement. * * \param _c structure representing database connection * \param _b target char * \param _l length of the target * \param _k keys that should be printed * \param _v vals that should be printed * \param _n number of key/value pairs * \param (*val2str) function pointer to a db specific conversion function * \return the length of the printed result on success, negative on errors */ int db_print_set(const db_con_t* _c, char* _b, const int _l, const db_key_t* _k, const db_val_t* _v, const int _n, int (*val2str) (const db_con_t*, const db_val_t*, char*, int*)); #endif opensips-2.2.2/db/db_val.h000066400000000000000000000150311300170765700153500ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file db/db_val.h * \brief Data structures that represents values in the database. * * This file defines data structures that represents values in the database. * Several datatypes are recognized and converted by the database API. * Available types: DB_INT, DB_DOUBLE, DB_STRING, DB_STR, DB_DATETIME, DB_BLOB and DB_BITMAP * It also provides some macros for convenient access to this values. */ #ifndef DB_VAL_H #define DB_VAL_H #include #include #include "../str.h" /** * Each cell in a database table can be of a different type. To distinguish * among these types, the db_type_t enumeration is used. Every value of the * enumeration represents one datatype that is recognized by the database * API. */ typedef enum { DB_INT, /**< represents an 32 bit integer number */ DB_BIGINT, /**< represents an 64 bit integer number */ DB_DOUBLE, /**< represents a floating point number */ DB_STRING, /**< represents a zero terminated const char* */ DB_STR, /**< represents a string of 'str' type */ DB_DATETIME, /**< represents date and time */ DB_BLOB, /**< represents a large binary object */ DB_BITMAP /**< an one-dimensional array of 32 flags */ } db_type_t; /** * This structure represents a value in the database. Several datatypes are * recognized and converted by the database API. These datatypes are automaticaly * recognized, converted from internal database representation and stored in the * variable of corresponding type. * * Module that want to use this values needs to copy them to another memory * location, because after the call to free_result there are not more available. * * If the structure holds a pointer to a string value that needs to be freed * because the module allocated new memory for it then the free flag must * be set to a non-zero value. A free flag of zero means that the string * data must be freed internally by the database driver. */ typedef struct { db_type_t type; /**< Type of the value */ int nul; /**< Means that the column in database has no value */ int free; /**< Means that the value should be freed */ /** Column value structure that holds the actual data in a union. */ union { int int_val; /**< integer value */ long long bigint_val; /**< big integer value */ double double_val; /**< double value */ time_t time_val; /**< unix time_t value */ const char* string_val; /**< zero terminated string */ str str_val; /**< str type string value */ str blob_val; /**< binary object data */ unsigned int bitmap_val; /**< Bitmap data type */ } val; } db_val_t; /** * Useful macros for accessing attributes of db_val structure. * All macros expect a reference to a db_val_t variable as parameter. */ /** * Use this macro if you need to set/get the type of the value. */ #define VAL_TYPE(dv) ((dv)->type) /** * Use this macro if you need to set/get the null flag. A non-zero flag means that * the corresponding cell in the database contains no data (a NULL value in MySQL * terminology). */ #define VAL_NULL(dv) ((dv)->nul) /** * Use this macro if you need to set/ get the free flag. A non-zero flag means that * the corresponding cell in the database contains data that must be freed from the * DB API. */ #define VAL_FREE(dv) ((dv)->free) /** * Use this macro if you need to access the integer value in the db_val_t structure. */ #define VAL_INT(dv) ((dv)->val.int_val) /** * Use this macro if you need to access the big integer value in the db_val_t structure. */ #define VAL_BIGINT(dv) ((dv)->val.bigint_val) /** * Use this macro if you need to access the double value in the db_val_t structure. */ #define VAL_DOUBLE(dv) ((dv)->val.double_val) /** * Use this macro if you need to access the time_t value in the db_val_t structure. */ #define VAL_TIME(dv) ((dv)->val.time_val) /** * Use this macro if you need to access the string value in the db_val_t structure. */ #define VAL_STRING(dv) ((dv)->val.string_val) /** * Use this macro if you need to access the str structure in the db_val_t structure. */ #define VAL_STR(dv) ((dv)->val.str_val) /** * Use this macro if you need to access the blob value in the db_val_t structure. */ #define VAL_BLOB(dv) ((dv)->val.blob_val) /** * Use this macro if you need to access the bitmap value in the db_val_t structure. */ #define VAL_BITMAP(dv) ((dv)->val.bitmap_val) #define get_str_from_dbval( _col_name, _val, _not_null, _not_empty, _str, _error_label) \ do{\ if ((_val)->nul) { \ if (_not_null) { \ LM_ERR("value in column %s cannot be null\n", _col_name); \ goto _error_label;\ } else { \ _str.s = NULL; _str.len = 0; \ } \ } \ if ((_val)->type==DB_STRING) { \ if ( VAL_STRING(_val)==NULL || *(VAL_STRING(_val))==0 ) { \ if (_not_empty) { \ LM_ERR("value in column %s cannot be empty\n", _col_name); \ goto _error_label;\ } else { \ _str.s = (char*)VAL_STRING(_val) ; _str.len = 0; \ } \ } else { \ _str.s = (char*)VAL_STRING(_val) ; _str.len = strlen(_str.s); \ } \ } else if ((_val)->type==DB_STR) { \ if ( VAL_STR(_val).s==NULL || VAL_STR(_val).len==0 ) { \ if (_not_empty) { \ LM_ERR("value in column %s cannot be empty\n", _col_name); \ goto _error_label;\ } else { \ _str = VAL_STR(_val) ;\ } \ } else { \ _str = VAL_STR(_val); \ } \ } else {\ LM_ERR("column %s does not have a string type (found %d)\n",\ _col_name,(_val)->type); \ goto _error_label;\ } \ }while(0) #endif /* DB_VAL_H */ opensips-2.2.2/db/doc/000077500000000000000000000000001300170765700145155ustar00rootroot00000000000000opensips-2.2.2/db/doc/db-api.txt000066400000000000000000000450541300170765700164220ustar00rootroot00000000000000# $Id$ # Generic Database Interface -------------------------- This is a generic database interface for modules that need to utilize a database. The interface should be used by all modules that access database. The interface will be independent of the underlying database server. Notes: If possible, use predefined macros if you need to access any structure attributes. For additional description, see comments in sources of mysql module. If you want to see more complicated examples of how the API could be used, see sources of dbexample, usrloc or auth modules. 1 Data types There are several new data types. All of them are defined in header file db.h, a client must include the header file to be able to use them. 1.1 Type db_con_t 1.1.1 Description This type represents a database connection, all database functions (described below) use a variable of this type as one argument. In other words, variable of db_con_t type serves as a handle for a particular database connection. 1.1.2 Definition typedef struct db_con { char* table; /* Default table to use */ void* con; /* Database connection */ void* res; /* Result of previous operation */ void* row; /* Internal, not for public use */ int connected; /* 1 if connection is established */ } db_con_t; 1.1.3 Macros There are no macros for db_con_t type. 1.2 Type db_key_t 1.2.1 Description This type represents a database key. Every time you need to specify a key value, this type should be used. In fact, this type is identical to const char*. 1.2.2 Definition typedef const char* db_key_t; 1.2.3 Macros There are no macros (It is not needed). 1.3 Type db_type_t 1.3.1 Description Each cell in a database table can be of a different type. To distinguish among these types, the db_type_t enumeration is used. Every value of the enumeration represents one datatype that is recognized by the database API. This enumeration is used in conjunction with db_type_t. For more information, see the next section. 1.3.2 Definition typedef enum { DB_INT, /* Integer number */ DB_DOUBLE, /* Decimal number */ DB_STRING, /* String */ DB_STR, /* str structure */ DB_DATETIME /* Date and time */ DB_BLOB /* Binary large object */ DB_BITMAP /* Bitmap, one-dimensional array of flags */ } db_type_t; 1.3.3 Macros There are no macros. 1.4 Type db_val_t 1.4.1 Description This structure represents a value in the database. Several datatypes are recognized and converted by the database API: DB_INT - Value in the database represents an integer number DB_DOUBLE - Value in the database represents a decimal number DB_STRING - Value in the database represents a string DB_STR - Value in the database represents a string DB_DATETIME - Value in the database represents date and time DB_BLOB - Value in the database represents binary large object DB_BITMAP - Value in the database represents an array of flags These datatypes are automaticaly recognized, converted from internal database representation and stored in the variable of corresponding type. 1.4.2 Definition typedef struct db_val { db_type_t type; /* Type of the value */ int nul; /* NULL flag */ union { int int_val; /* Integer value */ double double_val; /* Double value */ time_t time_val; /* Unix time_t value */ const char* string_val; /* Zero terminated string */ str str_val; /* str structure */ str blob_val; /* Structure describing blob */ unsigned int bitmap_val; /* Array of flags */ } val; } db_val_t; 1.4.3 Macros Note: All macros expect reference to db_val_t variable as the parameter. 1.4.3.1 VAL_TYPE(value) Macro Use this macro if you need to set/get the type of the value Example: VAL_TYPE(val) = DB_INT; if (VAL_TYPE(val) == DB_FLOAT) ... 1.4.3.2 VAL_NULL(value) Macro Use this macro if you need to set/get the null flag. Non-zero flag means that the corresponding cell in the database contained no data (NULL value in MySQL terminology). Example: if (VAL_NULL(val) == 1) { printf("The cell is NULL"); } 1.4.3.3 VAL_INT(value) Macro Use this macro if you need to access integer value in the db_val_t structure. Example: if (VAL_TYPE(val) == DB_INT) { printf("%d", VAL_INT(val)); } 1.4.3.4 VAL_DOUBLE(value) Macro Use this macro if you need to access double value in the db_val_t structure. Example: if (VAL_TYPE(val) == DB_DOUBLE) { printf("%f", VAL_DOUBLE(val)); } 1.4.3.5 VAL_TIME(value) Macro Use this macro if you need to access time_t value in the db_val_t structure. Example: time_t tim; if (VAL_TYPE(val) == DB_DATETIME) { tim = VAL_TIME(val); } 1.4.3.6 VAL_STRING(value) Macro Use this macro if you need to access string value in the db_val_t structure. Example: if (VAL_TYPE(val) == DB_STRING) { printf("%s", VAL_STRING(val)); } 1.4.3.7 VAL_STR(value) Macro Use this macro if you need to access str structure in the db_val_t structure. Example: if (VAL_TYPE(val) == DB_STR) { printf("%.*s", VAL_STR(val).len, VAL_STR(val).s); } 1.4.3.8 VAL_BLOB(value) Macro Use this macro if you need to access blob value in the db_val_t structure. Example: if (VAL_TYPE(val) == DB_BLOB) { printf("%.*s", VAL_BLOB(val).len, VAL_BLOB(val).s); } 1.4.3.9 VAL_BITMAP(value) Macro Use this macro if you need to access bitmap value in the db_val_t structure. Example: if (VAL_TYPE(val) == DB_BITMAP) { printf("%d", VAL_BITMAP(val)); } 1.5 Type db_row_t 1.5.1 Description This type represents one row in a database table. In other words, the row is an array of db_val_t variables, where each db_val_t variable represents exactly one cell in the table. 1.5.2 Definition typedef struct db_row { db_val_t* values; /* Array of values in the row */ int n; /* Number of values in the row */ } db_val_t; 1.5.3 Macros 1.5.3.1 ROW_VALUES(row) Macro Use this macro to get pointer to the array of db_val_t structures. Example: db_val_t* v = ROW_VALUES(row); if (VAL_TYPE(v) == DB_INT) .... 1.5.3.2 ROW_N(row) Macro Use this macro to get number of cells in the row. Example: db_val_t* val = ROW_VALUES(row); for(i = 0; i < ROW_N(row); i++) { switch(VAL_TYPE(val + i)) { case DB_INT: ...; break; case DB_DOUBLE: ...; break; ... } } 1.6 Type db_res_t 1.6.1 Description This type represents a result returned by db_query function (see below). The result can consist of zero or more rows (see db_row_t description). Note: A variable of type db_res_t returned by db_query function uses dynamicaly allocated memory, don't forget to call db_free_result if you don't need the variable anymore. You will encounter memory leaks if you fail to do this ! In addition to zero or more rows, each db_res_t object contains also an array of db_key_t objects. The objects represent keys (names of columns). 1.6.2 Definition typedef struct db_res { struct { db_key_t* keys; /* Array of column names */ db_type_t* types; /* Array of column types */ int n; /* Number of columns */ } col; struct db_row* rows; /* Array of rows */ int n; /* Number of rows */ } db_res_t; 1.6.3 Macros 1.6.3.1 RES_NAMES(res) Macro Use this macro if you want to obtain pointer to the array of cell names. Example: db_key_t* column_names = ROW_NAMES(row); 1.6.3.2 RES_COL_N(res) Macro Use this macro if you want to get the number of columns in the result. Example: int ncol = RES_COL_N(res) for(i = 0; i < ncol; i++) { /* do something with the column */ } 1.6.3.3 RES_ROWS(res) Macro Use this macro if you need to obtain pointer to array of rows. Example: db_row_t* rows = RES_ROWS(res); 1.6.3.4 RES_ROW_N(res) Macro Use this macro if you need to obtain the number of rows in the result Example: int n = RES_ROW_N(res); 1.7 Type db_op_t 1.7.1 Description This type represents an expression operator. In fact, this type is identical to const char*. 1.7.2 Definition typedef const char* db_op_t; 1.7.3 Macros There are no macros (It is not needed). 2 Functions There are several functions that implement the database API logic. All function names start with db_ prefix, except bind_dbmod. bind_dbmod function is implemented in db.c file, all other functions are implemented in a standalone database module. You will need to compile and link db.c in your module to be able to use the bind_dbmod function. Detailed function description follows. 2.1 Function bind_dbmod 2.1.1 Description This function is special, it's only purpose is to call find_export function in the ser core and find addresses of all other functions (starting with db_ prefix). This function MUST be called __FIRST__ ! 2.1.2 Prototype int bind_dbmod(char* db_url, db_func_t* dbf); 2.1.3 Parameters The function takes two parameters, the first parameter must contain a database connection URL or a database module name. The db_url is of the form "mysql://username:password@host:port/database" or "mysql" (database module name). In the case of a database connection URL, this function looks only at the first token (the database protocol). In the example above that would be "mysql": The second parameter will be filled by this function with the corresponding database module callbacks (see the db_func_t structure definition in db.h and the callbacks definitions below). 2.1.4 Return Value The function returns 0 if it was able to find the addresses of all the corresponding module database functions and a value < 0 otherwise. 2.2 Callback dbf.init 2.2.1 Description Use this function to initialize the database API and open a new database connection. This function must be called after bind_dbmod but before any other function is called. 2.2.2 Prototype db_con_t* (*db_init_f)(const char* _sql_url); 2.2.3 Parameters The function takes one parameter, the parameter must contain database connection URL. The URL is of the form mysql://username:password@host:port/database where: username: Username to use when logging into database (optional). password: password if it was set (optional) host: Hosname or IP address of the host where database server lives (mandatory) port: Port number of the server if the port differs from default value (optional) database: If the database server supports multiple databases, you must specify name of the database (optional). 2.2.4 Return Value The function returns pointer to db_con_t* representing the connection if it was successful, otherwise 0 is returned. 2.3 Callback dbf.close 2.3.1 Description The function closes previously open connection and frees all previously allocated memory. The function db_close must be the very last function called. 2.3.2 Prototype void (*db_close_f)(db_con_t* _h); 2.3.3 Parameters The function takes one parameter, this parameter is a pointer to db_con_t structure representing database connection that should be closed. 2.3.4 Return Value Function doesn't return anything. 2.4 Callback dbf.query 2.4.1 Description This function implements SELECT SQL directive. 2.4.2 Prototype int (*db_query_f)(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r); 2.4.3 Parameters The function takes 7 parameters: _h: Database connection handle _k: Array of column names that will be compared and their values must match _op: Array of operators to be used with key-value pairs _v: Array of values, columns specified in _k parameter must match these values _c: Array of column names that you are interested in _n: Number of key-value pairs to match in _k and _v parameters _nc: Number of columns in _c parameter _o: Order by _r: Address of variable where pointer to the result will be stored If _k and _v parameters are NULL and _n is zero, you will get the whole table. if _c is NULL and _nc is zero, you will get all table columns in the result _r will point to a dynamically allocated structure, it is neccessary to call db_free_result function once you are finished with the result. If _op is 0, equal (=) will be used for all key-value pairs. Strings in the result are not duplicated, they will be discarded if you call db_free_result, make a copy yourself if you need to keep it after db_free_result. You must call db_free_result _BEFORE_ you can call db_query again ! 2.4.4 Return Value The function returns 0 if everything is OK, otherwise value < 0 is returned. 2.5 Callback dbf.free_result 2.5.1 Description This function frees all memory allocated previously in db_query, it is neccessary to call this function on a db_res_t structure if you don't need the structure anymore. You must call this function _BEFORE_ you call db_query again ! 2.5.2 Prototype int (*db_free_result_f)(db_con_t* _h, db_res_t* _r); 2.5.3 Parameters The function takes 2 parameters: _h: Database connection handle _r: Pointer to db_res_t structure to destroy 2.5.4 Return Value The function returns 0 if everything is OK, otherwise the function returns value < 0. 2.6 Callback dbf.insert 2.6.1 Description This function implements INSERT SQL directive, you can insert one or more rows in a table using this function. 2.6.2 Prototype int (*db_insert_f)(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n); 2.6.3 Parameters The function takes 4 parameters: _h: Database connection handle _k: Array of keys (column names) _v: Array of values for keys specified in _k parameter _n: Number of keys-value pairs int _k and _v parameters 2.6.4 Return Value The function returns 0 if everything is OK, otherwise the function returns value < 0. 2.7 Callback dbf.delete 2.7.1 Description This function implements DELETE SQL directive, it is possible to delete one or more rows from a table. 2.7.2 Prototype int (*db_delete_f)(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n); 2.7.3 Parameters The function takes 4 parameters: _h: Database connection handle _k: Array of keys (column names) that will be matched _o: Array of operators to be used with key-value pairs _v: Array of values that the row must match to be deleted _n: Number of keys-value parameters in _k and _v parameters If _k is NULL and _v is NULL and _n is zero, all rows are deleted (table will be empty). If _o is NULL, equal operator (=) will be used everywhere. 2.7.4 Return Value The function returns 0 if everything is OK, otherwise the function returns value < 0. 2.8 Callback dbf.update 2.8.1 Description The function implements UPDATE SQL directive. It is possible to modify one or more rows in a table using this function. 2.8.2 Prototype int (*db_update_f)(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un); 2.8.3 Parameters The function takes 7 parameters: _h: Database connection handle _k: Array of keys (column names) that will be matched _o: Array of operators to be used with key-value pairs _v: Array of values that the row must match to be modified _uk: Array of keys (column names) that will be modified _uv: New values for keys specified in _k parameter _n: Number of key-value pairs in _k and _v parameters _un: Number of key-value pairs in _uk and _uv parameters 2.8.4 Return Value The function returns 0 if everything is OK, otherwise the function returns value < 0. 2.9 Callback dbf.use_table 2.9.1 Description The function db_use_table takes a table name and stores it db_con_t structure. All subsequent operations (insert, delete, update, query) are performed on that table. 2.9.2 Prototype int (*db_use_table_f)(db_con_t* _h, const char* _t); 2.9.3 Parameters The function takes 2 parameters: _h: Database connection handle _t: Table name 2.9.4 Return Value The function returns 0 if everything is OK, otherwise the function returns value < 0. 2.10 Callback dbf.replace 2.10.1 Description The function implements the REPLACE SQL directive. It is possible to insert a row and replace if one already exists. The old row will be deleted before the insertion of the new data. 2.10.2 Prototype int (*db_replace_f) (db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n); 2.10.3 Parameters The function takes 4 parameters: _h: structure representing database connection _k: key names _v: values of the keys _n: number of key=value pairs 2.10.4 Return Value The function returns 0 if everything is OK, otherwise the function returns value < 0. 2.11 Callback dbf.last_inserted_id 2.11.1 Description The function returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE statement. Use this function after you have performed an INSERT statement into a table that contains an AUTO_INCREMENT field. 2.11.2 Prototype int (*db_last_inserted_id) (db_con_t* handle); 2.11.3 Parameters The function takes one parameter: handle: structure representing database connection 2.11.4 Return Value The function returns the ID as integer or returns 0 if the previous statement does not use an AUTO_INCREMENT value. 2.12 Callback dbf.fetch_result 2.12.1 Description The function fetches a number of rows from a database result. If the number of wanted rows is zero, the function return anything with a result of zero. 2.12.2 Prototype int (*db_fetch_result_f) (db_con_t* _h, db_res_t** _r, int _n) 2.12.3 Parameters The function takes three parameters: _h: structure representing database connection _r: structure for the result _n: the number of rows that should be fetched 2.12.4 Return Value The function returns 0 if everything is ok, otherwise the function returns a value < 0. 2.13 Callback dbf.insert_update 2.13.1 Description The function implements the INSERT ON DUPLICATE KEY UPDATE SQL directive. It is possible to insert a row and update if one already exists. The old row will not deleted before the insertion of the new data. 2.13.2 Prototype int db_insert_update(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n) 2.13.3 Parameters The function takes four parameters: _h: structure representing database connection _k: key names _v: values of the keys _n: number of key=value pairs 2.13.4 Return Value The function returns 0 if everything is ok, otherwise the function returns a value < 0. opensips-2.2.2/db/example/000077500000000000000000000000001300170765700154035ustar00rootroot00000000000000opensips-2.2.2/db/example/Makefile000066400000000000000000000003011300170765700170350ustar00rootroot00000000000000# $Id$ # # database example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile auto_gen= NAME=dbexample.so LIBS= include ../../Makefile.modules opensips-2.2.2/db/example/README000066400000000000000000000002251300170765700162620ustar00rootroot00000000000000$Id$ This is very simple example module that shows how to use database interface. If you want to compile this module, move it to modules directory.opensips-2.2.2/db/example/dbexample.c000066400000000000000000000162141300170765700175140ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../sr_module.h" #include #include "../../db/db.h" #define DB_URL "mysql://root@localhost/ser" #define DB_TABLE "location" #define TRUE 1 #define FALSE 0 /* * Database module client example */ static struct module_exports dbex_exports= { "DBExample", (char*[]) { }, (cmd_function[]) { }, (int[]) { }, (fixup_function[]) { }, 0, /* number of functions*/ NULL, /* Module parameter names */ NULL, /* Module parameter types */ NULL, /* Module parameter variable pointers */ 0, /* Number of module parameters */ 0, /* response function*/ 0 /* destroy function */ }; static int print_res(db_res_t* _r) { int i, j; for(i = 0; i < RES_COL_N(_r); i++) { printf("%s ", RES_NAMES(_r)[i]); } printf("\n"); for(i = 0; i < RES_ROW_N(_r); i++) { for(j = 0; j < RES_COL_N(_r); j++) { if (RES_ROWS(_r)[i].values[j].nul == TRUE) { printf("NULL "); continue; } switch(RES_ROWS(_r)[i].values[j].type) { case DB_INT: printf("%d ", RES_ROWS(_r)[i].values[j].val.int_val); break; case DB_DOUBLE: printf("%f ", RES_ROWS(_r)[i].values[j].val.double_val); break; case DB_DATETIME: printf("%s ", ctime(&(RES_ROWS(_r)[i].values[j].val.time_val))); break; case DB_STRING: printf("%s ", RES_ROWS(_r)[i].values[j].val.string_val); break; case DB_STR: printf("%.*s ", RES_ROWS(_r)[i].values[j].val.str_val.len, RES_ROWS(_r)[i].values[j].val.str_val.s); break; case DB_BLOB: printf("%.*s ", RES_ROWS(_r)[i].values[j].val.blob_val.len, RES_ROWS(_r)[i].values[j].val.blob_val.s); break; case DB_BITMAP: printf("%d ", RES_ROWS(_r)[i].values[j].val.bitmap_val); break; } } printf("\n"); } return TRUE; } struct module_exports* mod_register() { /* * Column names of table location */ db_key_t keys1[] = {"username", "contact", "q", "expire", "opaque", "flags" }; db_key_t keys2[] = {"username", "q"}; db_key_t keys3[] = {"username", "contact"}; db_key_t keys4[] = {"contact", "q"}; db_val_t vals1[] = { { DB_STRING , 0, { .string_val = "foo@bar.com" } }, { DB_STR , 0, { .str_val = { "real@foo.bar.com", 18 } } }, { DB_DOUBLE , 0, { .double_val = 1.2 } }, { DB_DATETIME, 0, { .time_val = 439826493 } }, { DB_BLOB , 0, { .blob_val = { "hdslgkhas\0glksf", 17 } } }, { DB_BITMAP , 0, { .bitmap_val = FLAG_NAT | FLAG_INVITE } } }; db_val_t vals2[] = { { DB_STRING , 0, { .string_val = "foo2@bar2.com" } }, { DB_STR , 0, { .str_val = { "real2@foo.bar2.com", 18 } } }, { DB_DOUBLE , 0, { .double_val = 1.3 } }, { DB_DATETIME, 0, { .time_val = 12345 } }, { DB_BLOB , 0, { .blob_val = { "\0a\0balkdfj", 10 } } }, { DB_BITMAP , 0, { .bitmap_val = FLAG_NAT, FLAG_NOT_INVITE } } }; db_val_t vals3[] = { { DB_STRING , 0, { .string_val = "foo3@bar3.com" } }, { DB_STR , 0, { .str_val = { "real3@foo.bar3.com", 18 } } }, { DB_DOUBLE , 0, { .double_val = 1.5 } }, { DB_DATETIME, 0, { .time_val = 123456 } }, { DB_BLOB , 0, { .blob_val = { "halgkasdg\'", 10 } } }, { DB_BITMAP , 0, { .blob_val = FLAG_NAT } } }; db_val_t vals4[] = { { DB_STRING, 0, { .string_val = "foo2@bar2.com" } }, { DB_DOUBLE, 0, { .double_val = 1.30 } } }; db_val_t vals5[] = { { DB_STRING, 0, { .string_val = "foo3@bar3.com" } }, { DB_STRING, 0, { .string_val = "real3@foo.bar3.com" } } }; db_val_t vals6[] = { { DB_STRING, 0, { .string_val = "different@address.com" } }, { DB_DOUBLE, 0, { .double_val = 2.5 } } }; db_con_t* h; db_res_t* res = NULL; fprintf(stderr, "DBExample - registering...\n"); /* The first call must be bind_dbmod * This call will search for functions * exported by a database module */ if (bind_dbmod()) { fprintf(stderr, "Error while binding database module, did you forget to load a database module ?\n"); return &dbex_exports; } /* * Create a database connection * DB_URL is database URL of form * mysql://user:password@host:port/database * The function returns handle, that * represents a database connection */ h = db_init(DB_URL); if (!h) { fprintf(stderr, "Error while initializing database connection\n"); return &dbex_exports; } /* * Specify a table name, that will * be used for manipulations */ if (db_use_table(h, DB_TABLE) < 0) { fprintf(stderr, "Error while calling db_use_table\n"); return &dbex_exports; } /* If you do not specify any keys and values to be * matched, all rows will be deleted */ if (db_delete(h, NULL, NULL, NULL, 0) < 0) { fprintf(stderr, "Error while flushing table\n"); return &dbex_exports; } if (db_insert(h, keys1, vals1, 6) < 0) { fprintf(stderr, "Error while inserting line 1\n"); return &dbex_exports; } if (db_insert(h, keys1, vals2, 6) < 0) { fprintf(stderr, "Error while inserting line 2\n"); return &dbex_exports; } if (db_insert(h, keys1, vals3, 6) < 0) { fprintf(stderr, "Error while inserting line 3\n"); return &dbex_exports; } /* * Let's delete middle line with * user = foo2@bar2.com and q = 1.3 */ if (db_delete(h, keys2, NULL, vals4, 2) < 0) { fprintf(stderr, "Error while deleting line\n"); return &dbex_exports; } /* * Modify last line */ if (db_update(h, keys3, NULL, vals5, keys4, vals6, 2, 2) < 0) { fprintf(stderr, "Error while modifying table\n"); return &dbex_exports; } /* * Last but not least, dump the result of db_query */ if (db_query(h, NULL, NULL, NULL, NULL, 0, 0, NULL, &res) < 0) { fprintf(stderr, "Error while querying table\n"); return &dbex_exports; } print_res(res); /* * Free the result because we don't need it * anymore */ if (db_free_result(h, res) < 0) { fprintf(stderr, "Error while freeing result of query\n"); return &dbex_exports; } /* * Close existing database connection * and free previously allocated * memory */ db_close(h); return &dbex_exports; } opensips-2.2.2/db/schema/000077500000000000000000000000001300170765700152105ustar00rootroot00000000000000opensips-2.2.2/db/schema/Makefile000066400000000000000000000156101300170765700166530ustar00rootroot00000000000000# # $Id$ # # OpenSIPS database descriptions for modules TABLES := $(patsubst opensips-%.xml,%,$(wildcard opensips-*.xml)) ROOT=../.. STYLESHEETS=$(ROOT)/doc/dbschema/xsl # Stylesheet used to generate db_table nodes for pi_framework XML schema PI_FRAMEWORK_TABLE_XSL = $(STYLESHEETS)/pi_framework_table.xsl # Stylesheet used to generate mod nodes for pi_framework XML schema PI_FRAMEWORK_MOD_XSL = $(STYLESHEETS)/pi_framework_mod.xsl # Stylesheet used to generate MySQL database schema MYSQL_XSL = $(STYLESHEETS)/mysql.xsl # Stylesheet used to generate Postgres database schema POSTGRES_XSL = $(STYLESHEETS)/postgres.xsl # Stylesheet used to generate dbtext database schema DBTEXT_XSL = $(STYLESHEETS)/dbtext.xsl # Stylesheet used to generate berkeley database schema DB_BERKELEY_XSL = $(STYLESHEETS)/db_berkeley.xsl # Stylesheet used to generate oracle database schema ORACLE_XSL = $(STYLESHEETS)/oracle.xsl #Stylesheet used to generate sqlite database schema SQLITE_XSL = $(STYLESHEETS)/sqlite.xsl # Stylesheet used to generate docbook documentation DOCBOOK_XSL = $(STYLESHEETS)/docbook.xsl # Enable/disable DTD validation VALIDATE = 0 # Enable/disable verbose output (and DTD validation) VERBOSE = 0 # XML Catalog used to resolve entities CATALOG = $(ROOT)/doc/dbschema/catalog.xml XSLTPROC = /usr/bin/xsltproc XSLTPROC_FLAGS = --xinclude ifeq ($(VALIDATE), 0) override XSLTPROC := $(XSLTPROC) --novalid endif ifeq ($(VERBOSE), 1) override XSLTPROC := $(XSLTPROC) --verbose endif all: mysql postgres dbtext db_berkeley docbook oracle pi_framework sqlite .PHONY: pi_framework pi_framework_clean pi_framework: for FILE in $(TABLES); do \ XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \ --stringparam dir "$(ROOT)/scripts/pi_http" \ --stringparam prefix "$$FILE-" \ $(PI_FRAMEWORK_TABLE_XSL) opensips-"$$FILE".xml ; \ done for FILE in $(TABLES); do \ XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \ --stringparam dir "$(ROOT)/scripts/pi_http" \ --stringparam prefix "$$FILE-" \ $(PI_FRAMEWORK_MOD_XSL) opensips-"$$FILE".xml ; \ done cat $(ROOT)/scripts/pi_http/pi_framework-00 \ $(ROOT)/scripts/pi_http/*-table \ $(ROOT)/scripts/pi_http/pi_framework-01 \ $(ROOT)/scripts/pi_http/*-mod \ $(ROOT)/scripts/pi_http/pi_framework-02 > \ $(ROOT)/scripts/pi_http/pi_framework.xml pi_framework_clean: -@rm -f $(ROOT)/scripts/pi_http/*-table -@rm -f $(ROOT)/scripts/pi_http/*-mod .PHONY: mysql mysql_clean mysql: for FILE in $(TABLES); do \ XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \ --stringparam dir "$(ROOT)/scripts/mysql" \ --stringparam prefix "$$FILE-" \ --stringparam db "mysql" \ $(MYSQL_XSL) opensips-"$$FILE".xml ; \ done mysql_clean: -@rm -f $(ROOT)/scripts/mysql/* .PHONY: sqlite sqlite_clean sqlite: for FILE in $(TABLES); do \ XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \ --stringparam dir "$(ROOT)/scripts/sqlite" \ --stringparam prefix "$$FILE-" \ --stringparam db "sqlite" \ $(SQLITE_XSL) opensips-"$$FILE".xml ; \ done sqlite_clean: -@rm -f $(ROOT)/scripts/sqlite/* .PHONY: postgres postgres_clean postgres: for FILE in $(TABLES); do \ XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \ --stringparam dir "$(ROOT)/scripts/postgres" \ --stringparam prefix "$$FILE-" \ --stringparam db "postgres" \ $(POSTGRES_XSL) opensips-"$$FILE".xml ; \ done postgres_clean: -@rm -f $(ROOT)/scripts/postgres/* .PHONY: oracle oracle_clean oracle: for FILE in $(TABLES); do \ XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \ --stringparam dir "$(ROOT)/scripts/oracle" \ --stringparam prefix "$$FILE-" \ --stringparam db "oracle" \ $(ORACLE_XSL) opensips-"$$FILE".xml ; \ done oracle_clean: -@rm -f $(ROOT)/scripts/oracle/* .PHONY: dbtext dbtext_clean dbtext: for FILE in $(TABLES); do \ XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \ --stringparam dir "$(ROOT)/scripts/dbtext/opensips" \ --stringparam prefix "" \ --stringparam db "dbtext" \ $(DBTEXT_XSL) opensips-"$$FILE".xml ; \ done set -e; \ TMPFILE=`mktemp -t opensips-test.XXXXXXXXXX`; \ for FILE in $(sort $(wildcard $(ROOT)/scripts/dbtext/opensips/*)) ; do \ if [ -f "$$FILE" ]; then \ if [ "$$FILE" != "$(ROOT)/scripts/dbtext/opensips/version" -a \ `wc -l "$$FILE" | cut -f 1 -d ' '` -gt 1 ]; then \ tail -1 "$$FILE" >> "$(ROOT)/scripts/dbtext/opensips/version" ; \ head -n 1 "$$FILE" > "$$TMPFILE" ; \ cp "$$TMPFILE" "$$FILE" ; \ fi ; \ fi ; \ done ; \ rm -f "$$TMPFILE" dbtext_clean: -@rm -f $(ROOT)/scripts/dbtext/opensips/* .PHONY: db_berkeley db_berkeley_clean db_berkeley: for FILE in $(TABLES); do \ XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \ --stringparam dir "$(ROOT)/scripts/db_berkeley/opensips" \ --stringparam prefix "" \ --stringparam db "db_berkeley" \ $(DB_BERKELEY_XSL) opensips-"$$FILE".xml ; \ done set -e; \ TMPFILE=`mktemp -t opensips-test.XXXXXXXXXX`; \ for FILE in $(sort $(wildcard $(ROOT)/scripts/db_berkeley/opensips/*)) ; do \ if [ -f "$$FILE" ]; then \ if [ "$$FILE" != "$(ROOT)/scripts/db_berkeley/opensips/version" ]; then \ tail -2 "$$FILE" >> "$(ROOT)/scripts/db_berkeley/opensips/version" ; \ head -n 10 "$$FILE" > "$$TMPFILE" ; \ cp "$$TMPFILE" "$$FILE" ; \ fi ; \ fi ; \ done ; \ rm -f "$$TMPFILE" db_berkeley_clean: -@rm -f $(ROOT)/scripts/db_berkeley/opensips/* .PHONY: docbook docbook_clean docbook: for FILE in $(TABLES); do \ XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \ --stringparam dir "$(ROOT)/doc/database" \ --stringparam prefix "$$FILE-" \ $(DOCBOOK_XSL) opensips-"$$FILE".xml ; \ done ; \ # link all documents to one file, to get nicer output echo " "$(ROOT)/doc/database/tables.sgml" # create entities, as xi:include is not available in sgml docbook # substitute '-' for '_', docbook smgl don't like this for FILE in $(wildcard $(ROOT)/doc/database/*.xml); do \ echo " " >> "$(ROOT)/doc/database/tables.sgml" ; \ done ; \ #Include general documentation entities echo " %docentities;" >> "$(ROOT)/doc/database/tables.sgml" ; \ echo "]>" >> "$(ROOT)/doc/database/tables.sgml" # add bookinfo cat "$(ROOT)/doc/dbschema/bookinfo.xml" >> "$(ROOT)/doc/database/tables.sgml" # actually include the entities for FILE in $(wildcard $(ROOT)/doc/database/*.xml); do \ echo " &`basename "$$FILE" | sed -e 's#_#-#g'`" >> "$(ROOT)/doc/database/tables.sgml" ; \ done ; \ echo "" >> "$(ROOT)/doc/database/tables.sgml" docbook_clean: -@rm -f $(ROOT)/doc/database/*.xml -@rm -f $(ROOT)/doc/database/tables.sgml .PHONY: clean clean: mysql_clean postgres_clean oracle_clean dbtext_clean db_berkeley_clean docbook_clean pi_framework_clean opensips-2.2.2/db/schema/acc.xml000066400000000000000000000067231300170765700164700ustar00rootroot00000000000000 %entities; ]> acc7&MYSQL_TABLE_TYPE; This table is used by the ACC module to report on transactions - accounted calls. More information is available at: &OPENSIPS_MOD_DOC;acc.html id unsigned int &table_id_len; int,auto unique ID method string &method_len; A method is the primary function that a request is meant to invoke on a server. from_tag string &user_len; The tag parameter serves as a general mechanism to identify a dialog, which is the combination of the Call-ID along with two tags, one from participant in the dialog. to_tag string &user_len; The tag parameter serves as a general mechanism to identify a dialog, which is the combination of the Call-ID along with two tags, one from participant in the dialog. callid string &domain_len; Call-ID header field uniquely identifies a particular invitation or all registrations of a particular client. sip_code string &sip_code_len; SIP reply code sip_reason string &sip_reason_len; SIP reply reason time datetime Date and time when this record was written. duration unsigned int &expires_len; 0 Call duration (from 200OK INVITE to BYE request) in seconds - this field is populated only if CDR support is enabled in ACC module (see cdr_flag parameter) ms_duration unsigned int &expires_len; 0 Call duration (from 200OK INVITE to BYE request) in milliseconds - this field is populated only if CDR support is enabled in ACC module (see cdr_flag parameter) setuptime unsigned int &expires_len; 0 Call initialization duration - (from INVITE request to 200 OK INVITE) - this filed is populated only if CDR support is enabled in ACC module (see cdr_flag parameter) created datetime The call creation date and time. callid_idx
opensips-2.2.2/db/schema/address.xml000066400000000000000000000044501300170765700173620ustar00rootroot00000000000000 %entities; ]> address5&MYSQL_TABLE_TYPE; This table is used by the permissions module. More information is available at: &OPENSIPS_MOD_DOC;permissions.html id unsigned int &table_id_len; int,auto unique ID grp unsigned short 5 0 Group ip string &ip_add_len; IP address mask char 32 Network mask port unsigned short 5 0 Port proto string 4 any Transport protocol is either "any" or equal to transport protocol of request. Possible values that can be stored are "any", "udp", "tcp", "tls", and "sctp". pattern string &id_len; Regular expression context_info string 32 Extra context information
opensips-2.2.2/db/schema/aliases.xml000066400000000000000000000127571300170765700173670ustar00rootroot00000000000000 %entities; ]> aliases1009&MYSQL_TABLE_TYPE; This table for the registrar module is similar to the "location" table, (the aliases index makes lookup of missed calls much faster). Thus, the function lookup("alias") can be used to map aliases to AORs. More information is available at: &OPENSIPS_MOD_DOC;registrar.html. Most people uses the dbaliases table with the alias_db module now. id unsigned int &table_id_len; int,auto unique ID &USERCOL; string &user_len; Alias Username / Phone Number domain string &domain_len; &DEFAULT_DOMAIN; Domain name contact string &hf_len; Contact header field value provides a URI whoses meaning depends on the type of request or response it is in. received string &uri_len; Received IP:PORT in the format SIP:IP:PORT path string &hf_len; Path Header(s) per RFC 3327 expires datetime &DEFAULT_ALIASES_EXPIRES; to_date('&DEFAULT_ALIASES_EXPIRES;','yyyy-mm-dd hh24:mi:ss') Date and time when this entry expires. q float 10,2 &DEFAULT_Q; Value used for preferential routing. callid string &hf_len; &DEFAULT_CALLID; Call-ID header field uniquely identifies a particular invitation or all registrations of a particular client. cseq int &cseq_len; &DEFAULT_CSEQ; CSeq header field contains a single decimal sequence number and the request method. last_modified datetime &DEFAULT_DATETIME; to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss') Date and time when this entry was last modified. flags int &flag_len; 0 Flags cflags string 255 CFlags user_agent string &hf_len; User-Agent header field contains information about the UAC originating the request. socket string &domain_len; Socket used to connect to OpenSIPS. For example: UDP:IP:PORT methods int 11 Flags that indicate the SIP Methods this contact will accept. sip_instance string 255 SIP Instance for this particular contact attr string 255 Optional information specific to each registration alias_idx
opensips-2.2.2/db/schema/b2b_entities.xml000066400000000000000000000130341300170765700203040ustar00rootroot00000000000000 %entities; ]> b2b_entities1&MYSQL_TABLE_TYPE; Table for the b2b_entities module. More information can be found at: &OPENSIPS_MOD_DOC;b2b_entities.html id unsigned int &table_id_len; int,auto Unique ID type int 2 Entity type: 0-server, 1-client state int 2 Dialog state ruri string &uri_len; RURI(stored only for server entities to correctly match CANCEL) from_uri string &uri_len; From URI to_uri string &uri_len; To URI from_dname string &user_len; From display name to_dname string &user_len; To display name tag0 string &domain_len; TO tag tag1 string &domain_len; From tag callid string &domain_len; Call ID cseq0 int &cseq_len; Cseq0 cseq1 int &cseq_len; Cseq1 contact0 string &uri_len; Contact0 contact1 string &uri_len; Contact1 route0 text Record route 0 route1 text Record route 1 sockinfo_srv string &domain_len; Socket Info param string &uri_len; Logic parameter lm int &cseq_len; Last method lrc int &cseq_len; Last reply code lic int &cseq_len; Last invite cseq leg_cseq int &cseq_len; Leg cseq leg_route text Leg route leg_tag string &domain_len; Leg tag leg_contact string &uri_len; Leg contact leg_sockinfo string &uri_len; Leg sockinfo b2b_entities_idx b2b_entities_param
opensips-2.2.2/db/schema/b2b_logic.xml000066400000000000000000000124651300170765700175640ustar00rootroot00000000000000 %entities; ]> b2b_logic3&MYSQL_TABLE_TYPE; Table for the b2b_logic module. More information can be found at: &OPENSIPS_MOD_DOC;b2b_logic.html id unsigned int &table_id_len; int,auto Unique ID si_key string &user_len; Scenario instantiation key scenario string &user_len; Scenario id sstate int 2 Scenario State next_sstate int 2 Next Scenario State sparam0 string &user_len; Scenario id sparam1 string &user_len; Scenario id sparam2 string &user_len; Scenario id sparam3 string &user_len; Scenario id sparam4 string &user_len; Scenario id sdp text &user_len; Scenario id lifetime int 10 0 Lifetime e1_type int 2 E1 type e1_sid string &user_len; E1 Scenario ID e1_from string &uri_len; E1 From URI e1_to string &uri_len; E1 To URI e1_key string &user_len; E1 Key e2_type int 2 E2 type e2_sid string &user_len; E2 Scenario ID e2_from string &uri_len; E2 From URI e2_to string &uri_len; E2 To URI e2_key string &user_len; E2 Key e3_type int 2 E3 type e3_sid string &user_len; E3 Scenario ID e3_from string &uri_len; E3 From URI e3_to string &uri_len; E3 To URI e3_key string &user_len; E3 Key b2b_logic_idx
opensips-2.2.2/db/schema/b2b_sca.xml000066400000000000000000000257721300170765700172420ustar00rootroot00000000000000 %entities; ]> b2b_sca1&MYSQL_TABLE_TYPE; Persistent sca information for the b2b_sca module. More information can be found at: &OPENSIPS_MOD_DOC;b2b_sca.html id unsigned int &table_id_len; int,auto unique ID shared_line string &user_len; The shared line. watchers string 255 The URI list of watchers app1_shared_entity unsigned int 1 The entity to keep. app1_call_state unsigned int 1 The state of the appearance index. app1_call_info_uri string &uri_len; The URI of the Call-Info header app1_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app1_b2bl_key string &user_len; The b2b_logic key. app2_shared_entity unsigned int 1 The entity to keep. app2_call_state unsigned int 1 The state of the appearance index. app2_call_info_uri string &uri_len; The URI of the Call-Info header app2_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app2_b2bl_key string &user_len; The b2b_logic key. app3_shared_entity unsigned int 1 The entity to keep. app3_call_state unsigned int 1 The state of the appearance index. app3_call_info_uri string &uri_len; The URI of the Call-Info header app3_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app3_b2bl_key string &user_len; The b2b_logic key. app4_shared_entity unsigned int 1 The entity to keep. app4_call_state unsigned int 1 The state of the appearance index. app4_call_info_uri string &uri_len; The URI of the Call-Info header app4_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app4_b2bl_key string &user_len; The b2b_logic key. app5_shared_entity unsigned int 1 The entity to keep. app5_call_state unsigned int 1 The state of the appearance index. app5_call_info_uri string &uri_len; The URI of the Call-Info header app5_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app5_b2bl_key string &user_len; The b2b_logic key. app6_shared_entity unsigned int 1 The entity to keep. app6_call_state unsigned int 1 The state of the appearance index. app6_call_info_uri string &uri_len; The URI of the Call-Info header app6_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app6_b2bl_key string &user_len; The b2b_logic key. app7_shared_entity unsigned int 1 The entity to keep. app7_call_state unsigned int 1 The state of the appearance index. app7_call_info_uri string &uri_len; The URI of the Call-Info header app7_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app7_b2bl_key string &user_len; The b2b_logic key. app8_shared_entity unsigned int 1 The entity to keep. app8_call_state unsigned int 1 The state of the appearance index. app8_call_info_uri string &uri_len; The URI of the Call-Info header app8_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app8_b2bl_key string &user_len; The b2b_logic key. app9_shared_entity unsigned int 1 The entity to keep. app9_call_state unsigned int 1 The state of the appearance index. app9_call_info_uri string &uri_len; The URI of the Call-Info header app9_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app9_b2bl_key string &user_len; The b2b_logic key. app10_shared_entity unsigned int 1 The entity to keep. app10_call_state unsigned int 1 The state of the appearance index. app10_call_info_uri string &uri_len; The URI of the Call-Info header app10_call_info_appearance_uri string &uri_len; The URI of the appearance in Call-Info header app10_b2bl_key string &user_len; The b2b_logic key. sca_idx
opensips-2.2.2/db/schema/cachedb_sql.xml000066400000000000000000000021551300170765700201650ustar00rootroot00000000000000 %entities; ]> cachedb2&MYSQL_TABLE_TYPE; DB implementation of the CacheDB interface: &OPENSIPS_MOD_DOC;cachedb_sql.html keyname string 255 The Key value text 512 The value counter int 10 0 The value of the counter expires unsigned int 10 0 The unix timestamp when the key will expires
opensips-2.2.2/db/schema/carrierfailureroute.xml000066400000000000000000000060521300170765700220130ustar00rootroot00000000000000 %entities; ]> carrierfailureroute2&MYSQL_TABLE_TYPE; This table is used by the carrierroute module to provide failure routing capabilities. More information is available at: &OPENSIPS_MOD_DOC;carrierroute.html id unsigned int &table_id_len; int,auto unique ID carrier unsigned int &table_id_len; 0 This column contains the carrier id. domain string &user_len; This column contains the route domain. Additional domains could be used for example as fallback. scan_prefix string &user_len; This column contains the scan prefix, which define the matching portion of a phone number. host_name string &uri_len; This column contains the routing destination used for rule matching. reply_code string 3 This column contains the reply code used for rule matching. flags unsigned int &flag_len; 0 This column contains the flags used for rule matching. mask unsigned int &flag_len; 0 This column contains the mask that is applied to the message flags before rule matching. next_domain string &user_len; This column contains the route domain that should be used for the next routing attempt. description string &description_len; A comment for the route entry, useful for larger routing tables.
opensips-2.2.2/db/schema/carrierroute.xml000066400000000000000000000071621300170765700204460ustar00rootroot00000000000000 %entities; ]> carrierroute3&MYSQL_TABLE_TYPE; This table is used by the carrierroute module to provides routing, balancing and blacklisting capabilities. More information is available at: &OPENSIPS_MOD_DOC;carrierroute.html id unsigned int &table_id_len; int,auto unique ID carrier unsigned int &table_id_len; 0 This column contains the carrier id. domain string &user_len; This column contains the route domain. Additional domains could be used for example as fallback. scan_prefix string &user_len; This column contains the scan prefix, which define the matching portion of a phone number. flags unsigned int &flag_len; 0 This column contains the flags used for rule matching. mask unsigned int &flag_len; 0 This column contains the mask that is applied to the message flags before rule matching. prob float 0 Name of column containing the probability. The probability value is used to distribute the traffic between several gateways. strip unsigned int &flag_len; 0 Name of the column containing the number of digits to be stripped of the userpart of an URI before prepending rewrite_prefix. rewrite_host string &uri_len; Name of column containing rewrite prefixes. Here you can define a rewrite prefix for the localpart of the SIP URI. rewrite_prefix string &user_len; Rewrite prefix for the localpart of the SIP URI. rewrite_suffix string &user_len; Rewrite suffix for the localpart of the SIP URI. description string &description_len; A comment for the route entry, useful for larger routing tables.
opensips-2.2.2/db/schema/cc_agents.xml000066400000000000000000000046331300170765700176660ustar00rootroot00000000000000 %entities; ]> cc_agents1&MYSQL_TABLE_TYPE; This table is used by the Call Center module to store the definition of the agents serving the flows/queues. More information can be found at: &OPENSIPS_MOD_DOC;call_center.html. id unsigned int &table_id_len; int,auto Agent unique ID in DB agentid string 128 The unique ID of the agent in the Call Center module - to be used to identify the agent in the module and from outside the module; It is an alphanumerical string. location string 128 SIP URI point to the agent location; All calls for this agents will be sent to this SIP address. logstate unsigned int 10 0 The login state of the agent; 0 - not logged in; 1 - logged in ; Agent will start receiving calls only if logged in. skills string 255 Comma separated list of skills offered by the agent; these skills must match the skills used in the queues/flows definition; In order to receive calls from a flow, the agent must have the skill required by that flow. last_call_end int 11 0 The timestamp of the last call of an agent. If different than 0, the agent will only receive calls after wrapup seconds pass after this timestamp. unique_agentid
opensips-2.2.2/db/schema/cc_calls.xml000066400000000000000000000061111300170765700174740ustar00rootroot00000000000000 %entities; ]> cc_calls&MYSQL_TABLE_TYPE; This table is used by the Call Center module to store ongoing calls for restart persitancy. It consists only of runtime data and should not be manually provisioned. More information can be found at: &OPENSIPS_MOD_DOC;call_center.html. id unsigned int &table_id_len; int,auto unique ID of the call. state int 11 The state of the call. ig_cback int 11 Indicates if the call should be ignored. no_rej int 11 Indicates whether the call can be rejected or not. setup_time int 11 Stores the call setup time. eta int 11 The estimated wait time for a call until it is answered by an agent. last_start int 11 Stores the timestamp when the last call has started. recv_time int 11 Stores the timestamp when the call was received by the call center. caller_dn string 128 Caller Display Name. caller_un string 128 Caller User Name. b2buaid string 128 The B2B id internally used by the B2B module to identify the call. flow string 128 The flow/queue this call belongs to. agent string 128 The agent that handles the call. unique_id b2buaid_idx
opensips-2.2.2/db/schema/cc_cdrs.xml000066400000000000000000000063551300170765700173430ustar00rootroot00000000000000 %entities; ]> cc_cdrs1&MYSQL_TABLE_TYPE; This table is used by the Call Center module to store the Call Data Records (CDRs) for all the handled calls. More information can be found at: &OPENSIPS_MOD_DOC;call_center.html. id unsigned int &table_id_len; int,auto CDR unique ID in DB caller string 64 The SIP URI identifing the caller. received_timestamp datetime When the call was received. wait_time unsigned int 11 0 Time (in seconds) spent by the call in queue (onhold). pickup_time unsigned int 11 0 Time (in seconds) spent by the call in ringing to the agent. talk_time unsigned int 11 0 The duration (in seconds) of the call. flow_id string 128 The ID of the flow the call was received on. agent_id string 128 The ID of the agent who picked this call (if any). call_type int 11 -1 Type of call: -2 - call rejected by agent; -1 - call dropped because of internal error; 0 - call handled by agent; 1 - call dropped while in queue; rejected unsigned int 11 0 How many times the call was rejected by agents (agent not answering his phone). fstats unsigned int 11 0 Bitmask of the following binary flags: 0 - it is inbound call; 1 - call was distributed to agents; 2 - call was answered; 3 - call was abandoned. cid unsigned int 11 0 Sequence number of the call.
opensips-2.2.2/db/schema/cc_flows.xml000066400000000000000000000052301300170765700175310ustar00rootroot00000000000000 %entities; ]> cc_flows1&MYSQL_TABLE_TYPE; This table is used by the Call Center module to store the definition of the call queues / flows. More information can be found at: &OPENSIPS_MOD_DOC;call_center.html. id unsigned int &table_id_len; int,auto Flow unique ID in DB flowid string 64 The unique ID of the flow in the Call Center module - to be used to identify the flow/queue in the module and from outside the module; It is an alphanumerical string. priority unsigned int 11 256 The priority of the flow (in relation to the other flows); 0 is maximum priority and calls for this flow will be processed first all the time. skill string 64 The skill required from an agent in order to receive calls from this flow/queue. prependcid string 32 Aphanumerical prefix to be added to the caller displayname when sending calls from this flow to agents (so agent - serving muliple flows - can see what was the flow the call was received on. message_welcome string 128 SIP URI point to a media server; this is used for playing the welcome message for this flow. message_queue string 128 SIP URI point to a media server; this is used for playing the onhold message for this flow. IMPORTANT - this message must cycle and media server must never hung up on it. unique_flowid
opensips-2.2.2/db/schema/closeddial.xml000066400000000000000000000052271300170765700200430ustar00rootroot00000000000000 %entities; ]> closeddial1&MYSQL_TABLE_TYPE; This table is used by the closeddial module to provide closed dial functionality for groups of usernames; This is a functionality similar to a Centrex. More information about the closeddial module can be found at: &OPENSIPS_MOD_DOC;closeddial.html id unsigned int &table_id_len; int,auto unique ID &USERCOL; string &user_len; Username / phone number domain string &domain_len; Domain name cd_username string &user_len; Closed dial username cd_domain string &domain_len; Closed dial domain group_id string &domain_len; Attribute use to group usernames new_uri string &uri_len; New URI cd_idx1 cd_idx2 cd_idx3 cd_idx4
opensips-2.2.2/db/schema/clusterer.xml000066400000000000000000000060031300170765700177410ustar00rootroot00000000000000 %entities; ]> clusterer1&MYSQL_TABLE_TYPE; This table is used for defining clusters of OpenSIPS instances. id int &table_id_len; int,auto unique ID cluster_id int &table_id_len; unique identifier for a cluster machine_id int &table_id_len; unique identifier for a machine (a node in a cluster) url string 64 network location of the machine, like protocol:ip:port state int 1 1 state of the machine 0 - down/ 1 - up / 2 - temporary disable last_attempt unsigned long 64 long 0 last time(UNIX timestamp) when a failed attempt has occurred failed_attempts int &table_id_len; 3 maximum number of failed attempts no_tries int &table_id_len; 0 number of failed tries before auto-disabling duration int &table_id_len; 30 tempory disabled state duration(seconds) description string 64 opaque text not used by the module clusterer_idx
opensips-2.2.2/db/schema/cpl.xml000066400000000000000000000027231300170765700165140ustar00rootroot00000000000000 %entities; ]> cpl2&MYSQL_TABLE_TYPE; Table for the call processing language "cpl" module. More information is available at: &OPENSIPS_MOD_DOC;cpl_c.html id unsigned int &table_id_len; int,auto Unique ID username string &user_len; domain string 64 cpl_xml text cpl_bin text account_idx
opensips-2.2.2/db/schema/dbaliases.xml000066400000000000000000000040611300170765700176620ustar00rootroot00000000000000 %entities; ]> dbaliases2&MYSQL_TABLE_TYPE; This table us used by the alias_db module as an alternative for user aliases via userloc. More information about the alias_db module can be found at: &OPENSIPS_MOD_DOC;alias_db.html id unsigned int &table_id_len; int,auto unique ID alias_username string &user_len; Alias username / phone number alias_domain string &domain_len; Alias domain name &USERCOL; string &user_len; Username / phone number domain string &domain_len; Domain name alias_idx target_idx
opensips-2.2.2/db/schema/dialog.xml000066400000000000000000000124231300170765700171730ustar00rootroot00000000000000 %entities; ]> dialog10&MYSQL_TABLE_TYPE; Persistent dialog information for the dialog module. More information can be found at: &OPENSIPS_MOD_DOC;dialog.html dlg_id unsigned long &table_id_len; long h_entry | h_id callid string &hf_len; Call-ID of the dialog from_uri string &uri_len; The URI of the FROM header (as per INVITE) from_tag string &user_len; The tag parameter serves as a general mechanism to identify a dialog, which is the combination of the Call-ID along with two tags, one from participant in the dialog. to_uri string &uri_len; The URI of the TO header (as per INVITE) to_tag string &user_len; The tag parameter serves as a general mechanism to identify a dialog, which is the combination of the Call-ID along with two tags, one from participant in the dialog. mangled_from_uri string &user_len; The mangled from URI, in case uac_replace_from was called for this dialog. mangled_to_uri string &user_len; The mangled to URI, in case uac_replace_to was called for this dialog caller_cseq string &cseq_len; Last Cseq number on the caller side. callee_cseq string &cseq_len; Last Cseq number on the callee side. caller_ping_cseq unsigned int &cseq_len; Last Cseq number of pings generated on caller side. callee_ping_cseq unsigned int &cseq_len; Last Cseq number of pings generated on callee side. caller_route_set text 512 Route set on the caller side. callee_route_set text 512 Route set on on the caller side. caller_contact string &uri_len; Caller's contact uri. callee_contact string &uri_len; Callee's contact uri. caller_sock string 64 Local socket used to communicate with caller callee_sock string 64 Local socket used to communicate with callee state unsigned int 10 The state of the dialog. start_time unsigned int 10 The timestamp (unix time) when the dialog was confirmed. timeout unsigned int 10 The timestamp (unix time) when the dialog will expire. vars binary 4096 Variables attached to this dialog. profiles text 512 Profiles this dialog belongs to. script_flags unsigned int 10 0 Script flags for the dialog. module_flags unsigned int 10 0 Module flags for the dialog. flags unsigned int 10 0 Internal flags used by the module.
opensips-2.2.2/db/schema/dialplan.xml000066400000000000000000000055771300170765700175340ustar00rootroot00000000000000 %entities; ]> dialplan5&MYSQL_TABLE_TYPE; This table is used by the dialplan module for the translation rules. More information is available at: &OPENSIPS_MOD_DOC;dialplan.html id unsigned int &table_id_len; int,auto unique ID dpid int 11 Dialplan ID. pr int 11 Priority of rule. match_op int 11 Matching operator for rule (0-equal, 1-regexp). match_exp string 64 Matching expression (regexp or string). match_flags int 11 Matching flags (0-case sensitive, 1-case insensitive). subst_exp string 64 Substitution expression. repl_exp string 32 Replacement expression (sed like). timerec string 255 Time recurrence used to match this rule. disabled int 11 0 Specifies if the command can be used, or is disabled. attrs string 32 General attributes string to be returned in case of rule matching.
opensips-2.2.2/db/schema/dispatcher.xml000066400000000000000000000054641300170765700200710ustar00rootroot00000000000000 %entities; ]> dispatcher7&MYSQL_TABLE_TYPE; This table is used by the dispatcher module. It contains the sets of destinations used for load balancing and dispatching. More information about the dispatcher module can be found at: &OPENSIPS_MOD_DOC;dispatcher.html id unsigned int &table_id_len; int,auto unique ID setid int Destination set id 0 destination string 192 Destination SIP address socket string 128 Local Socket to be used when sending requests (traffic and probes) to the destination - must be an listener configured in opensips. state int The state of the destination (0 enabled, 1 disabled , 2 probing) 0 weight int The weight of the destination 1 priority int The priority of each destination (only useful with algorithm 8) 0 attrs string 128 Attribute string - custom, opaque string that will be pushed into script when this destination will be selected description string 64 Description for this destination
opensips-2.2.2/db/schema/domain.xml000066400000000000000000000033621300170765700172050ustar00rootroot00000000000000 %entities; ]> domain3&MYSQL_TABLE_TYPE; This table is used by the domain module to determine if a host part of a URI is "local" or not. More information about the domain module can be found at: &OPENSIPS_MOD_DOC;domain.html id unsigned int &table_id_len; int,auto unique ID domain string &domain_len; Domain name attrs string 255 Domain Attributes last_modified datetime &DEFAULT_DATETIME; to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss') Date and time when this record was last modified. domain_idx
opensips-2.2.2/db/schema/domainpolicy.xml000066400000000000000000000047141300170765700204270ustar00rootroot00000000000000 %entities; ]> domainpolicy3&MYSQL_TABLE_TYPE; Table for the domainpolicy module. More information at &OPENSIPS_MOD_DOC;domainpolicy.html. id unsigned int &table_id_len; int,auto unique ID rule string &rule_len; Domain policy rule name which is equal to the URI as published in the domain policy NAPTRs. type string &rule_len; Domain policy rule type. In the case of federation names, this is "fed". For standard referrals according to draft-lendl-speermint-technical-policy-00, this is "std". For direct domain lookups, this is "dom". Default value is "type". att string &rule_len; It contains the AVP's name. If the rule stored in this row triggers, than dp_can_connect() will add an AVP with that name. val string &avp_val_len; It contains the values for AVPs created by dp_can_connect(). Default value is "val" description string &rule_len; Comment about the rule rav_idx rule_idx
opensips-2.2.2/db/schema/dr_carriers.xml000066400000000000000000000041241300170765700202320ustar00rootroot00000000000000 %entities; ]> dr_carriers2&MYSQL_TABLE_TYPE; This table is used by the Dynamic Routing module to define carriers (a carrier is defined by a list of gateways and an ordering rule). More information can be found at: &OPENSIPS_MOD_DOC;drouting.html. id unsigned int &table_id_len; int,auto Table key, not used by module carrierid string 64 Unique ID of the carrier gwlist string 255 Reference to the GWs/destinations from the list. flags unsigned int &flag_len; 0 Flags (for different purposes) of the carriers state unsigned int &flag_len; 0 The state of the carrier (on / off). attrs string 255 Attributes string for the carrier description string 128 Text description of the GW list dr_carrier_idx
opensips-2.2.2/db/schema/dr_gateways.xml000066400000000000000000000062601300170765700202470ustar00rootroot00000000000000 %entities; ]> dr_gateways6&MYSQL_TABLE_TYPE; This table is used by the Dynamic Routing module to store information about the destinations/gateways where to route calls. More information can be found at: &OPENSIPS_MOD_DOC;drouting.html. id unsigned int &table_id_len; int,auto Table primary key, not used by module gwid string 64 GW unique ID - used to link the GW from the routing rules type unsigned int 11 0 Type/class of the GW (user defined) address string 128 GW/destination address as name/IP[:port] strip unsigned int 11 0 Number of digits to be striped out for the beginning of the username when using this GW/destination pri_prefix string 16 String to prefix the username of RURI when using this GW/destination attrs string 255 Generic string describing GW attributes - this string is to be interpreted from the script probe_mode unsigned int 11 0 0- No probing; 1-Probe on disable only ; 2-Always probe; state unsigned int 11 0 State of the gateway: 0 - enabled; 1 - permanent disabled; 2 - temporary disabled (probing) socket string 128 Local Socket to be used when sending requests (traffic and probes) to the destination - must be an listener configured in opensips. description string 128 Text description of the GW/destination dr_gw_idx
opensips-2.2.2/db/schema/dr_groups.xml000066400000000000000000000030731300170765700177410ustar00rootroot00000000000000 %entities; ]> dr_groups2&MYSQL_TABLE_TYPE; This table is used by the Dynamic Routing module to store information about the routing groups (users mapped over groups). More information can be found at: &OPENSIPS_MOD_DOC;drouting.html. id unsigned int &table_id_len; int,auto Unique ID username string 64 Username part of user domain string 128 Domain part of user groupid unsigned int 11 0 The ID of the routing group the user belongs to. description string 128 Text description of the group/user
opensips-2.2.2/db/schema/dr_partitions.xml000066400000000000000000000066341300170765700206240ustar00rootroot00000000000000 %entities; ]> dr_partitions1&MYSQL_TABLE_TYPE; This table is used by the Dynamic Routing module to store information about the partitions used in routing (url to database, table names and AVP names for each partition). More information can be found at: &OPENSIPS_MOD_DOC;drouting.html. id unsigned int &table_id_len; int,auto Partition unique ID partition_name string 255 The name of the partition. db_url string 255 The url to the database containing the tables: dr_rules, dr_groups, dr_carriers and dr_gateways drd_table string 255 The name of the dr_gateways table in the given database (for the given partition). drr_table string 255 The name of the dr_rules table in the given database (for the given partition). drg_table string 255 The name of the dr_groups table in the given database (for the given partition). drc_table string 255 The name of the dr_carriers table in the given database (for the given partition). ruri_avp string 255 The name of ruri_avp AVP. gw_id_avp string 255 The name of gw_id_avp AVP gw_priprefix_avp string 255 The name of gw_priprefix_avp AVP. gw_sock_avp string 255 The name of gw_sock_avp AVP. rule_id_avp string 255 The name of rule_id_avp AVP. rule_prefix_avp string 255 The name of rule_prefix_avp AVP. carrier_id_avp string 255 The name of carrier_id_avp AVP.
opensips-2.2.2/db/schema/dr_rules.xml000066400000000000000000000047241300170765700175600ustar00rootroot00000000000000 %entities; ]> dr_rules3&MYSQL_TABLE_TYPE; This table is used by the Dynamic Routing module to store information about the routing rules. More information can be found at: &OPENSIPS_MOD_DOC;drouting.html. ruleid unsigned int &table_id_len; int,auto Rule unique ID groupid string 255 The ID(s) of the routing group(s) this rule is to be used for - comma separeted list of numerical Ids prefix string 64 Numerical prefix to match this rule timerec string 255 Time recurrence used for matching this rule. priority int 11 0 Priority of this rule (among rules with same prefix and timerec). routeid string 255 Route block (from cfg script) to be called when rule matches. gwlist string 255 Reference to the GWs/destinations to be used when rule matches. attrs string 255 Generic string describing RULE attributes - this string is to be interpreted from the script description string 128 Text description of the rule
opensips-2.2.2/db/schema/emergency_report.xml000066400000000000000000000065711300170765700213140ustar00rootroot00000000000000 %entities; ]> emergency_report1&MYSQL_TABLE_TYPE; This table is used by the Emergency module to save information associated with a emergency call, for trouble shooting purposes. More information can be found at: &OPENSIPS_MOD_DOC;emergency.html. id unsigned int &table_id_len; int,auto Unique ID callid string 25 header that uniquely identifies the call. selectiveRoutingID string 11 The Common Language Location Indicator(CLLI) code associated with the Selective Router to which the emergency call is to be directed routingESN unsigned int 5 0 The Emergency Services Number associated with a particular ESZ that respresents a unique combination of Police, Fire and EMS emergency responders. npa unsigned int 3 0 The primary Numbering Plan Area (NPA) associated with the outgoing route to the Selective Router that is appropriate for caller's location. esgwri string 50 Routing information used to direct the call to the ESGW. lro string 20 last routing option destination for the call. VPC_organizationName string 50 company name or other label of the VPC that provided the routing information. VPC_hostname string 50 identifies the fully qualified domain name or IP address of the VPC that provided routing information. VPC_timestamp string 30 Date Time Stamp indicating UTC date and time that the message was sent from VPC. result string 4 Code indicating the reason for success or failure to determine an ERT/ESGWRI and ESQK. disposition string 10 Describe how routing of call was done(e.g.,by ESGWRI or bye LRO)
opensips-2.2.2/db/schema/emergency_routing.xml000066400000000000000000000036201300170765700214600ustar00rootroot00000000000000 %entities; ]> emergency_routing1&MYSQL_TABLE_TYPE; This table is used by the Emergency module to translate ERT informations in ESGWRI. More information can be found at: &OPENSIPS_MOD_DOC;emergency.html. id unsigned int &table_id_len; int,auto Unique ID selectiveRoutingID string 11 The Common Language Location Indicator(CLLI) code associated with the Selective Router to which the emergency call is to be directed routingESN unsigned int 5 0 The Emergency Services Number associated with a particular ESZ that respresents a unique combination of Police, Fire and EMS emergency responders. npa unsigned int 3 0 The primary Numbering Plan Area (NPA) associated with the outgoing route to the Selective Router that is appropriate for caller's location. esgwri string 50 Routing information used to direct the call to the ESGW.
opensips-2.2.2/db/schema/emergency_service_provider.xml000066400000000000000000000066341300170765700233530ustar00rootroot00000000000000 %entities; ]> emergency_service_provider1&MYSQL_TABLE_TYPE; This table is used by the Emergency module to store information of the organizations involved in the routing of the emergency call, this information is necessary to send the request to the VPC, according to the NENA standard. This table isn't necessary if opensips role not send request to VPC, such as the opensips acting as call server in the scenarios II and III. More information can be found at: &OPENSIPS_MOD_DOC;emergency.html. id unsigned int &table_id_len; int,auto Unique ID organizationName string 50 provider company name's. This parameter is optional field in the NENA v2 interface (call server - VPC) hostId string 30 provider hostname's. This parameter is  mandatory if attribution is 0(source) or 2(VSP), otherwise it is optional. nenaId string 50 the NENA administered company identifier (NENA Company ID) of provider. This parameter is optional field in the NENA v2 interface (call server - VPC). contact string 20 telephone number by which the provider operator can be reached 24 hours a day, 7 days a week. This parameter is  mandatory if attribution is 0(source) or 2(VSP), otherwise it is optional. certUri string 50 provides a means of directly obtaining the VESA(Valid Emergency Services Authority) issued certificate for the provider. This parameter is optional field in the NENA v2 interface (call server - VPC). nodeIP string 20 IP address of the node that is being registered. This parameter is  mandatory. attribution unsigned int 2 It is a field of type int designating the function of the organization involved in the composition of architecture NENA being registered in this table. This parameter is  mandatory.  The values that this field can take are: 0 - the organization is a Source. Source is node directly requesting emergency call routing from the VPC. 1 - the organization is a VPC. VPC is the routing information provider to emengency call 2- the organization is a VSP. VSP is the caller's voice service provider
opensips-2.2.2/db/schema/entities.xml000066400000000000000000000017251300170765700175630ustar00rootroot00000000000000 opensips-2.2.2/db/schema/fraud_detection.xml000066400000000000000000000072761300170765700211050ustar00rootroot00000000000000 %entities; ]> fraud_detection1&MYSQL_TABLE_TYPE; This table is used by the Fraud Detection module to store information about fraud-profiles. More information can be found at: &OPENSIPS_MOD_DOC;fraud_detection.html. ruleid unsigned int &table_id_len; int,auto Rule unique ID profileid unsigned int The ID of the profile the current rule is part of prefix string 64 Numerical prefix to match this rule start_hour string 5 Start of the interval in which the rule should be matched. end_hour string 5 End of the interval in which the rule should be matched. daysoftheweek string 64 List/interval of days in which the rule is available. cpm_warning unsigned int 5 Warning threshold for calls per minute. cpm_critical unsigned int 5 Crtical threshold for calls per minute. call_duration_warning unsigned int 5 Warning threshold for calls per minute. call_duration_critical unsigned int 5 Crtical threshold for call duration. total_calls_warning unsigned int 5 Warning threshold for total calls. total_calls_critical unsigned int 5 Crtical threshold for total calls. concurrent_calls_warning unsigned int 5 Warning threshold for concurrent calls. concurrent_calls_critical unsigned int 5 Crtical threshold for concurrent calls. sequential_calls_warning unsigned int 5 Warning threshold for sequential calls. sequential_calls_critical unsigned int 5 Crtical threshold for sequential calls.
opensips-2.2.2/db/schema/globalblacklist.xml000066400000000000000000000032631300170765700210670ustar00rootroot00000000000000 %entities; ]> globalblacklist2&MYSQL_TABLE_TYPE; This table is used by the userblacklist module for the global blacklists. More information is available at: &OPENSIPS_MOD_DOC;userblacklist.html id unsigned int &table_id_len; int,auto unique ID prefix string &user_len; The prefix that is matched for the blacklist. whitelist char 1 0 Specify if this a blacklist (0) or a whitelist (1) entry. description string &description_len; A comment for the entry. globalblacklist_idx
opensips-2.2.2/db/schema/grp.xml000066400000000000000000000040641300170765700165260ustar00rootroot00000000000000 %entities; ]> grp3&MYSQL_TABLE_TYPE; This table us used by the group module as a means of group membership checking. Used primarily for Access Control Lists (ACL's). More information about the group module can be found at: &OPENSIPS_MOD_DOC;group.html id unsigned int &table_id_len; int,auto unique ID &USERCOL; string &user_len; Username / phone number domain string &domain_len; Domain name grp string &id_len; Group name last_modified datetime &DEFAULT_DATETIME; to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss') Date and time when this record was last modified. account_group_idx
opensips-2.2.2/db/schema/imc_members.xml000066400000000000000000000032441300170765700202170ustar00rootroot00000000000000 %entities; ]> imc_members2&MYSQL_TABLE_TYPE; Member table for the IMC module. More information at &OPENSIPS_MOD_DOC;imc.html. id unsigned int &table_id_len; int,auto unique ID username string &domain_len; Username domain string &domain_len; Domain room string &realm_len; flag int &flag_len; Flags account_room_idx
opensips-2.2.2/db/schema/imc_rooms.xml000066400000000000000000000027631300170765700177310ustar00rootroot00000000000000 %entities; ]> imc_rooms2&MYSQL_TABLE_TYPE; Room table for the IMC module. More information at &OPENSIPS_MOD_DOC;imc.html. id unsigned int &table_id_len; int,auto unique ID name string &domain_len; Name of the room domain string &domain_len; Domain of the room flag int &flag_len; Flags name_domain_idx
opensips-2.2.2/db/schema/load_balancer.xml000066400000000000000000000037021300170765700205020ustar00rootroot00000000000000 %entities; ]> load_balancer2&MYSQL_TABLE_TYPE; This table is used by the Load-Balancer module to store information about the destinations the balance the calls between. More information can be found at: &OPENSIPS_MOD_DOC;load_balancer.html. id unsigned int &table_id_len; int,auto unique ID of the destination group_id unsigned int 11 0 The group the destination belongs to dst_uri string 128 Destination address as a SIP URI resources string 255 String with the definition of the resource provided by the destination and the capacity of each resource probe_mode unsigned int 11 0 Probing mode (0-none, 1-if disabled, 2-all the time) description string 128 Text description of the destination dsturi_idx
opensips-2.2.2/db/schema/location.xml000066400000000000000000000124651300170765700175520ustar00rootroot00000000000000 %entities; ]> location1011&MYSQL_TABLE_TYPE; Persistent user location information for the usrloc module. More information can be found at: &OPENSIPS_MOD_DOC;usrloc.html contact_id unsigned long &table_id_len; long,auto unique ID &USERCOL; string &user_len; Username / phone number domain string &domain_len; Domain name contact string &hf_len; Contact header field value provides a URI whoses meaning depends on the type of request or response it is in. received string &uri_len; Received IP:PORT in the format SIP:IP:PORT path string &hf_len; Path Header(s) per RFC 3327 expires datetime &DEFAULT_ALIASES_EXPIRES; to_date('&DEFAULT_ALIASES_EXPIRES;','yyyy-mm-dd hh24:mi:ss') Date and time when this entry expires. q float 10,2 &DEFAULT_Q; Value used for preferential routing. callid string &hf_len; &DEFAULT_CALLID; Call-ID header field uniquely identifies a particular invitation or all registrations of a particular client. cseq int &cseq_len; &DEFAULT_CSEQ; CSeq header field contains a single decimal sequence number and the request method. last_modified datetime &DEFAULT_DATETIME; to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss') Date and time when this entry was last modified. flags int &flag_len; 0 Flags cflags string 255 CFlags user_agent string &hf_len; User-Agent header field contains information about the UAC originating the request. socket string &domain_len; Socket used to connect to OpenSIPS. For example: UDP:IP:PORT methods int 11 Flags that indicate the SIP Methods this contact will accept. sip_instance string 255 SIP Instance for this particular contact attr string 255 Optional information specific to each registration account_contact_idx
opensips-2.2.2/db/schema/missed_calls.xml000066400000000000000000000063201300170765700203750ustar00rootroot00000000000000 %entities; ]> missed_calls5&MYSQL_TABLE_TYPE; This table is used by the ACC module for keeping track of missed calls. This table is similar to the 'acc' table. More information is available at: &OPENSIPS_MOD_DOC;acc.html id unsigned int &table_id_len; int,auto unique ID method string &method_len; A method is the primary function that a request is meant to invoke on a server. from_tag string &user_len; The tag parameter serves as a general mechanism to identify a dialog, which is the combination of the Call-ID along with two tags, one from participant in the dialog. to_tag string &user_len; The tag parameter serves as a general mechanism to identify a dialog, which is the combination of the Call-ID along with two tags, one from participant in the dialog. callid string &domain_len; Call-ID header field uniquely identifies a particular invitation or all registrations of a particular client. sip_code string &sip_code_len; SIP reply code sip_reason string &sip_reason_len; SIP reply reason time datetime Date and time when this record was written. setuptime unsigned int &expires_len; 0 Call initialization duration - (from INVITE request to reply) - this filed is populated only if CDR support is enabled in ACC module (see cdr_flag parameter) created datetime The call creation date and time. callid_idx
opensips-2.2.2/db/schema/opensips-acc.xml000066400000000000000000000006241300170765700203200ustar00rootroot00000000000000 %entities; ]> Accounting opensips-2.2.2/db/schema/opensips-alias_db.xml000066400000000000000000000005611300170765700213300ustar00rootroot00000000000000 %entities; ]> alias db opensips-2.2.2/db/schema/opensips-auth_db.xml000066400000000000000000000005641300170765700212030ustar00rootroot00000000000000 %entities; ]> Subscriber opensips-2.2.2/db/schema/opensips-avpops.xml000066400000000000000000000005751300170765700211070ustar00rootroot00000000000000 %entities; ]> AVP Operations opensips-2.2.2/db/schema/opensips-b2b.xml000066400000000000000000000006311300170765700202350ustar00rootroot00000000000000 %entities; ]> B2BUA opensips-2.2.2/db/schema/opensips-b2b_sca.xml000066400000000000000000000005621300170765700210660ustar00rootroot00000000000000 %entities; ]> SCA support opensips-2.2.2/db/schema/opensips-cachedb_sql.xml000066400000000000000000000005661300170765700220270ustar00rootroot00000000000000 %entities; ]> CacheDB_SQL opensips-2.2.2/db/schema/opensips-call_center.xml000066400000000000000000000007221300170765700220440ustar00rootroot00000000000000 %entities; ]> Call Center opensips-2.2.2/db/schema/opensips-carrierroute.xml000066400000000000000000000007161300170765700223020ustar00rootroot00000000000000 %entities; ]> carrierroute opensips-2.2.2/db/schema/opensips-closeddial.xml000066400000000000000000000005611300170765700216750ustar00rootroot00000000000000 %entities; ]> Accounting opensips-2.2.2/db/schema/opensips-clusterer.xml000066400000000000000000000005721300170765700216040ustar00rootroot00000000000000 %entities; ]> Clusterer support opensips-2.2.2/db/schema/opensips-cpl.xml000066400000000000000000000005731300170765700203530ustar00rootroot00000000000000 %entities; ]> Call-processing language opensips-2.2.2/db/schema/opensips-dialog.xml000066400000000000000000000005641300170765700210340ustar00rootroot00000000000000 %entities; ]> Dialog support opensips-2.2.2/db/schema/opensips-dialplan.xml000066400000000000000000000006031300170765700213530ustar00rootroot00000000000000 %entities; ]> Matching and translation rules opensips-2.2.2/db/schema/opensips-dispatcher.xml000066400000000000000000000005641300170765700217230ustar00rootroot00000000000000 %entities; ]> Dispatcher opensips-2.2.2/db/schema/opensips-domain.xml000066400000000000000000000005541300170765700210430ustar00rootroot00000000000000 %entities; ]> Domain opensips-2.2.2/db/schema/opensips-domainpolicy.xml000066400000000000000000000005701300170765700222610ustar00rootroot00000000000000 %entities; ]> Domainpolicy opensips-2.2.2/db/schema/opensips-drouting.xml000066400000000000000000000010051300170765700214170ustar00rootroot00000000000000 %entities; ]> Dynamic Routing opensips-2.2.2/db/schema/opensips-emergency.xml000066400000000000000000000007201300170765700215450ustar00rootroot00000000000000 %entities; ]> Emergency opensips-2.2.2/db/schema/opensips-extensions.xml000066400000000000000000000012761300170765700217750ustar00rootroot00000000000000 %entities; ]> Extensions opensips-2.2.2/db/schema/opensips-fraud_detection.xml000066400000000000000000000005641300170765700227340ustar00rootroot00000000000000 %entities; ]> Fraud Detection opensips-2.2.2/db/schema/opensips-group.xml000066400000000000000000000006251300170765700207270ustar00rootroot00000000000000 %entities; ]> Group checking opensips-2.2.2/db/schema/opensips-imc.xml000066400000000000000000000006541300170765700203450ustar00rootroot00000000000000 %entities; ]> Instant Message Conference opensips-2.2.2/db/schema/opensips-load_balancer.xml000066400000000000000000000005601300170765700223370ustar00rootroot00000000000000 %entities; ]> Load Balancer opensips-2.2.2/db/schema/opensips-msilo.xml000066400000000000000000000005631300170765700207170ustar00rootroot00000000000000 %entities; ]> Message Storage opensips-2.2.2/db/schema/opensips-permissions.xml000066400000000000000000000005621300170765700221460ustar00rootroot00000000000000 %entities; ]> Permissions opensips-2.2.2/db/schema/opensips-presence.xml000066400000000000000000000010301300170765700213660ustar00rootroot00000000000000 %entities; ]> Presence opensips-2.2.2/db/schema/opensips-registrant.xml000066400000000000000000000005741300170765700217600ustar00rootroot00000000000000 %entities; ]> Registrant support opensips-2.2.2/db/schema/opensips-registrar.xml000066400000000000000000000005601300170765700215730ustar00rootroot00000000000000 %entities; ]> Registrar opensips-2.2.2/db/schema/opensips-rls.xml000066400000000000000000000006441300170765700203740ustar00rootroot00000000000000 %entities; ]> RLS --> opensips-2.2.2/db/schema/opensips-rtpproxy.xml000066400000000000000000000005701300170765700215010ustar00rootroot00000000000000 %entities; ]> RTPProxy opensips-2.2.2/db/schema/opensips-siptrace.xml000066400000000000000000000005611300170765700214040ustar00rootroot00000000000000 %entities; ]> SIPtrace opensips-2.2.2/db/schema/opensips-speeddial.xml000066400000000000000000000005641300170765700215270ustar00rootroot00000000000000 %entities; ]> Speed dial opensips-2.2.2/db/schema/opensips-standard.xml000066400000000000000000000006761300170765700214010ustar00rootroot00000000000000 %entities; ]> Version opensips-2.2.2/db/schema/opensips-tls_mgm.xml000066400000000000000000000005661300170765700212410ustar00rootroot00000000000000 %entities; ]> TLS_MGM support opensips-2.2.2/db/schema/opensips-uri_db.xml000066400000000000000000000005611300170765700210360ustar00rootroot00000000000000 %entities; ]> SIP URI checks opensips-2.2.2/db/schema/opensips-userblacklist.xml000066400000000000000000000006611300170765700224420ustar00rootroot00000000000000 %entities; ]> User and global blacklists opensips-2.2.2/db/schema/opensips-usrloc.xml000066400000000000000000000005651300170765700211050ustar00rootroot00000000000000 %entities; ]> User location opensips-2.2.2/db/schema/pr_active_watchers.xml000066400000000000000000000107151300170765700216120ustar00rootroot00000000000000 %entities; ]> active_watchers11&MYSQL_TABLE_TYPE; Table for the presence module. More information can be found at: &OPENSIPS_MOD_DOC;presence.html id unsigned int &table_id_len; int,auto Unique ID presentity_uri string &uri_len; Presence URI watcher_username string &user_len; From User watcher_domain string &domain_len; From Domain to_user string &user_len; To User to_domain string &domain_len; To Domain event string &user_len; presence Event description event_id string &user_len; Event ID to_tag string &domain_len; TO tag from_tag string &domain_len; From tag callid string &domain_len; Call ID local_cseq int &cseq_len; Local cseq remote_cseq int &cseq_len; Remote cseq contact string &uri_len; Contact record_route text Record route expires int &expires_len; Expires status int 11 2 Status reason string &user_len; Reason version int 11 0 Version socket_info string &domain_len; Socket info local_contact string &uri_len; Local contact active_watchers_idx
opensips-2.2.2/db/schema/pr_presentity.xml000066400000000000000000000047171300170765700206520ustar00rootroot00000000000000 %entities; ]> presentity5&MYSQL_TABLE_TYPE; Table for the presence module. More information can be found at: &OPENSIPS_MOD_DOC;presence.html id unsigned int &table_id_len; int,auto Unique ID username string &user_len; User name domain string &domain_len; Domain event string &user_len; Event etag string &user_len; User name expires int &expires_len; Expires received_time int &expires_len; Reveived time body binary extra_hdrs binary sender string &uri_len; Sender contact presentity_idx
opensips-2.2.2/db/schema/pr_pua.xml000066400000000000000000000105331300170765700172220ustar00rootroot00000000000000 %entities; ]> pua8&MYSQL_TABLE_TYPE; Table for the presence related pua module. More information can be found at: &OPENSIPS_MOD_DOC;pua.html id unsigned int &table_id_len; int,auto Unique ID pres_uri string &uri_len; URI pres_id string 255 ID event int 11 Event expires int &expires_len; Expires desired_expires int &expires_len; Desired Expires flag int &flag_len; Flags etag string &domain_len; Etag tuple_id string &domain_len; Tuple ID watcher_uri string &uri_len; Watcher URI to_uri string &uri_len; URI call_id string &domain_len; Call ID to_tag string &domain_len; To tag from_tag string &domain_len; From tag cseq int &cseq_len; record_route text Record route contact string &uri_len; Contact remote_contact string &uri_len; Remote contact version int 11 extra_headers text Extra Headers del1_idx del2_idx update_idx
opensips-2.2.2/db/schema/pr_watchers.xml000066400000000000000000000044541300170765700202620ustar00rootroot00000000000000 %entities; ]> watchers4&MYSQL_TABLE_TYPE; Table for the presence module. More information can be found at: &OPENSIPS_MOD_DOC;presence.html id unsigned int &table_id_len; int,auto Unique ID presentity_uri string &uri_len; Presentity Uri watcher_username string &user_len; Watcher User watcher_domain string &domain_len; Watcher Domain event string &user_len; presence Event description status int 11 Status reason string &user_len; Reason inserted_time int 11 watcher_idx
opensips-2.2.2/db/schema/pr_xcap.xml000066400000000000000000000050041300170765700173650ustar00rootroot00000000000000 %entities; ]> xcap4&MYSQL_TABLE_TYPE; Table for the presence module. More information can be found at: &OPENSIPS_MOD_DOC;presence.html id unsigned int &table_id_len; int,auto Unique ID username string &user_len; User name domain string &domain_len; Domain doc binary doc LONGBLOB doc_type int 11 Document type etag string &user_len; Document Etag source int 11 Entity inserting the record doc_uri string &uri_len; Document uri port int 11 XCAP server port account_doc_type_idx source_idx
opensips-2.2.2/db/schema/re_grp.xml000066400000000000000000000025701300170765700172140ustar00rootroot00000000000000 %entities; ]> re_grp2&MYSQL_TABLE_TYPE; This table is used by the group module to check membership based on regular expressions. More information about the group module can be found at: &OPENSIPS_MOD_DOC;group.html id unsigned int &table_id_len; int,auto unique ID reg_exp string 128 Regular expression group_id int 11 0 Group ID group_idx
opensips-2.2.2/db/schema/registrant.xml000066400000000000000000000052761300170765700201260ustar00rootroot00000000000000 %entities; ]> registrant1&MYSQL_TABLE_TYPE; Registrant information for the uac_registrant module. More information can be found at: &OPENSIPS_MOD_DOC;uac_registrant.html id unsigned int &table_id_len; int,auto unique ID registrar string &uri_len; URI pointing to the remote registrar. proxy string &uri_len; URI pointing to the outbond proxy. aor string &uri_len; URI defining the address of record. third_party_registrant string &uri_len; URI defining the third party registrant. username string &user_len; Username for authentication. password string &user_len; Password for authentication. binding_URI string &uri_len; Contact URI in REGISTER. binding_params string &user_len; Contact params in REGISTER. expiry unsigned int 1 Expiration time. forced_socket string &user_len; socket for sending the REGISTER. aor_idx
opensips-2.2.2/db/schema/rls_presentity.xml000066400000000000000000000045551300170765700210310ustar00rootroot00000000000000 %entities; ]> rls_presentity1&MYSQL_TABLE_TYPE; Table for the RLS module. id unsigned int &table_id_len; int,auto Unique ID rlsubs_did string 255 Resource list subscribe dialog id resource_uri string &uri_len; List Uri content_type string 255 Content type presence_state binary expires int &expires_len; Expires updated int &expires_len; Update flag auth_state int &expires_len; Watcher authorization state reason string &user_len; reason for watcher authorization state rls_presentity_idx updated_idx
opensips-2.2.2/db/schema/rls_watchers.xml000066400000000000000000000106611300170765700204360ustar00rootroot00000000000000 %entities; ]> rls_watchers2&MYSQL_TABLE_TYPE; Table for RLS module used for storing resource lists subscribe information. id unsigned int &table_id_len; int,auto Unique ID presentity_uri string &uri_len; Presence URI to_user string &user_len; To user to_domain string &domain_len; To domain watcher_username string &user_len; From user watcher_domain string &domain_len; From domain event string &user_len; presence Event description event_id string &user_len; Event ID to_tag string &domain_len; To tag from_tag string &domain_len; From tag callid string &domain_len; Call ID local_cseq int &cseq_len; Local cseq remote_cseq int &cseq_len; Remote cseq contact string &domain_len; Contact record_route text Record route expires int &expires_len; Expires status int 11 2 Status reason string &user_len; Reason version int 11 0 Version socket_info string &domain_len; Socket info local_contact string &uri_len; Local contact rls_watcher_idx
opensips-2.2.2/db/schema/route_tree.xml000066400000000000000000000022151300170765700201070ustar00rootroot00000000000000 %entities; ]> route_tree2&MYSQL_TABLE_TYPE; This table is used by the carrierroute module to provides routing, balancing and blacklisting capabilities. More information is available at: &OPENSIPS_MOD_DOC;carrierroute.html id unsigned int &table_id_len; int,auto unique ID carrier string &user_len; This column contains the carrier name.
opensips-2.2.2/db/schema/rtpproxy_sockets.xml000066400000000000000000000023601300170765700213750ustar00rootroot00000000000000 %entities; ]> rtpproxy_sockets0&MYSQL_TABLE_TYPE; This table is used by the NAT Helper module to store definitions of socket(s) used to connect to (a set) RTPProxy. More information can be found at: &OPENSIPS_MOD_DOC;nathelper.html. id unsigned int &table_id_len; int,auto Unique ID rtpproxy_sock text A list of sockets use to connect to a set of RTPProxy. Example: "udp:localhost:12221 udp:localhost:12222". set_id unsigned int &table_id_len; The ID of the RTPProxy set.
opensips-2.2.2/db/schema/silo.xml000066400000000000000000000053321300170765700167030ustar00rootroot00000000000000 %entities; ]> silo6&MYSQL_TABLE_TYPE; This table us used by the msilo module to provide offline message storage More information about the msilo module can be found at: &OPENSIPS_MOD_DOC;msilo.html id unsigned int &table_id_len; int,auto unique ID src_addr string &uri_len; Source address - From URI dst_addr string &uri_len; Destination address - To URI &USERCOL; string &user_len; SIP domain of target user domain string &domain_len; Username / phone number of target user inc_time int 0 Incoming time exp_time int 0 Expiration time snd_time int 0 Reminder send time ctype string 255 Content type body binary Body of the message account_idx
opensips-2.2.2/db/schema/sip_trace.xml000066400000000000000000000076501300170765700177130ustar00rootroot00000000000000 %entities; ]> sip_trace5&MYSQL_TABLE_TYPE; This table is used to store incoming/outgoing SIP messages in database. More informations can be found in the siptrace module documentation at: &OPENSIPS_MOD_DOC;siptrace.html. id unsigned int &table_id_len; int,auto unique ID time_stamp datetime &DEFAULT_DATETIME; to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss') Recording date callid string 255 call ID from SIP message trace_attrs string &uri_len; SIP URI of the user being traced msg text Full SIP message method string 32 SIP method name status string &uri_len; SIP reply status from_proto string 5 Source protocol from_ip string &ip_add_len; Source IP address from_port unsigned int 5 Source port to_proto string 5 Destination protocol to_ip string &ip_add_len; Destination IP address to_port unsigned int 5 Destination port fromtag string &id_len; From tag direction string 4 Destination IP address trace_attrs_idx date_idx fromip_idx callid_idx
opensips-2.2.2/db/schema/speed_dial.xml000066400000000000000000000052651300170765700200330ustar00rootroot00000000000000 %entities; ]> speed_dial3&MYSQL_TABLE_TYPE; This table is used by the speeddial module to provide on-server speed dial facilities. More information about the speeddial module can be found at: &OPENSIPS_MOD_DOC;speeddial.html id unsigned int &table_id_len; int,auto unique ID &USERCOL; string &user_len; Username / phone number domain string &domain_len; Domain name sd_username string &user_len; Speed dial username sd_domain string &domain_len; Speed dial domain new_uri string &uri_len; New URI fname string &user_len; First name lname string &user_len; Last name description string &user_len; Description speed_dial_idx
opensips-2.2.2/db/schema/subscriber.xml000066400000000000000000000052331300170765700201000ustar00rootroot00000000000000 %entities; ]> subscriber7&MYSQL_TABLE_TYPE; This table is used to provide authentication information. More information about the auth_db module can be found at: &OPENSIPS_MOD_DOC;auth_db.html id unsigned int &table_id_len; int,auto Unique ID &USERCOL; string &user_len; Username / phone number domain string &domain_len; Domain name password string 25 Password email_address string &user_len; Email address ha1 string &domain_len; md5(username:realm:password) ha1b string &domain_len; md5(username@domain:realm:password) rpid string &domain_len; The SIP Remote-Party-ID header identifies the calling party and includes user, party, screen and privacy headers that specify how a call is presented and screened. account_idx username_idx
opensips-2.2.2/db/schema/template.xml000066400000000000000000000042601300170765700175470ustar00rootroot00000000000000 %entities; ]> table_name2&MYSQL_TABLE_TYPE; table documentation id int &table_id_len; int,auto unique ID foo string 100 column documentation test123 string 25 bar blub
opensips-2.2.2/db/schema/tls_mgm.xml000066400000000000000000000075441300170765700174060ustar00rootroot00000000000000 %entities; ]> tls_mgm1&MYSQL_TABLE_TYPE; This table is used for defining domains. id string &id_len; unique ID address string &domain_len; network location, like: "ip:port" or "name" type int 1 int specifies the type of a domain : client domain(0) or server domain (1) method string &method_len; SSL method used by a certain domain verify_cert int 1 int verify certificate: 0 - no, 1 - yes require_cert int 1 int require certificate: 0 - no, 1 - yes certificate string 255 string certificate associated with a certain domain private_key string 255 int private_key crl_check_all int 1 int check all crl: 0 -no, 1 - yes crl_dir string 255 crl directory ca_list string 255 CA list ca_dir string 255 ca directory cipher_list string 255 the list of algorithms used for authentication and encryption allowed dh_params string 255 specifies the Diffie-Hellmann parameters ec_curve string 255 specifies an elliptic curve which should be used for ciphers which demand an elliptic curve
opensips-2.2.2/db/schema/uri.xml000066400000000000000000000037671300170765700165460ustar00rootroot00000000000000 %entities; ]> uri2&MYSQL_TABLE_TYPE; This table is used by uri_db module to implement various SIP URI checks. More information about the uri_db module can be found at: &OPENSIPS_MOD_DOC;uri_db.html id unsigned int &table_id_len; int,auto &USERCOL; string &user_len; Username / phone number domain string &domain_len; Domain name uri_user string &user_len; Username / phone number last_modified datetime &DEFAULT_DATETIME; to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss') Date and time when this record was last modified. account_idx
opensips-2.2.2/db/schema/userblacklist.xml000066400000000000000000000037401300170765700206050ustar00rootroot00000000000000 %entities; ]> userblacklist2&MYSQL_TABLE_TYPE; This table is used by the userblacklist module for the user specific blacklists. More information is available at: &OPENSIPS_MOD_DOC;userblacklist.html id unsigned int &table_id_len; int,auto unique ID username string &user_len; The user that is used for the blacklist lookup. domain string &domain_len; The domain that is used for the blacklist lookup. prefix string &user_len; The prefix that is matched for the blacklist. whitelist char 1 0 Specify if this a blacklist (0) or a whitelist (1) entry. userblacklist_idx
opensips-2.2.2/db/schema/usr_preferences.xml000066400000000000000000000054251300170765700211320ustar00rootroot00000000000000 %entities; ]> usr_preferences3&MYSQL_TABLE_TYPE; This table us used by the avpops module to implement Attribute Value Pairs (AVP's). More information about the avpops module can be found at: &OPENSIPS_MOD_DOC;avpops.html id unsigned int &table_id_len; int,auto Unique ID uuid string &user_len; Unique user ID &USERCOL; string &uri_len; 0 Username / phone number domain string &domain_len; Domain name attribute string &avp_name_len; AVP attribute type int &flag_len; 0 AVP type value string &avp_val_len; AVP value last_modified datetime &DEFAULT_DATETIME; to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss') Date and time when this record was last modified. ua_idx uda_idx value_idx
opensips-2.2.2/db/schema/version.xml000066400000000000000000000014131300170765700174160ustar00rootroot00000000000000 %entities; ]> version&MYSQL_TABLE_TYPE; table_name string 32 table_version unsigned int 0 t_name_idx
opensips-2.2.2/doc/000077500000000000000000000000001300170765700141305ustar00rootroot00000000000000opensips-2.2.2/doc/authors.xml000066400000000000000000000042761300170765700163500ustar00rootroot00000000000000 Anca Vamanu, <anca at opensips dot org> Ancuta Onofrei, <ancuta at voice-system dot ro> Andreas Granig, <andreas dot granig at inode dot info> Elena-Ramona Modroiu, <ramona at rosdeve dot ro> Bastian Friedrich, <bastian dot friedrich at collax dot com> Bogdan-Andrei Iancu, <bogdan at opensips dot org> Christian Schlatter, <cs at unc dot edu> Dan Pascu, <dan at ag-projects dot com> Di-Shi Sun, <di-shi at transnexus dot com> Elias Baixas, <elias dot baixas at voztele dot com> Daniel-Constantin Mierla, <miconda at gmail dot com> Henning Westerholt, <henning dot westerholt at 1und1 dot de> Dmitry Isakbayev, <isakdim at gmail dot com> Jesus Rodriguez, <jesusr at voztele dot com> Juha Heinanen, <jh at tutpro dot com> Julien Blache, <jblache at debian dot org> Klaus Darilion, <klaus dot mailinglists at pernau dot at> Jan Ondrej, <ondrejj at salstar dot sk> Will Quan, <wiquan at employees dot org> Ovidiu Sas, <osas at voipembedded dot com> Razvan Crainea, <razvan at opensips dot org> Vlad Paiu, <vladpaiu at opensips dot org> Maxim Sobolev, <sobomax at sippysoft dot com> opensips-2.2.2/doc/dbschema/000077500000000000000000000000001300170765700156765ustar00rootroot00000000000000opensips-2.2.2/doc/dbschema/bookinfo.xml000066400000000000000000000010711300170765700202250ustar00rootroot00000000000000 OpenSIPS database tables &osips; &osips; Development Team &osipshomelink;
&osipsdevmail;
2007-2014 OpenSIPS development Team $Revision: 8814 $ $Date$
opensips-2.2.2/doc/dbschema/catalog.xml000066400000000000000000000065701300170765700200420ustar00rootroot00000000000000 opensips-2.2.2/doc/dbschema/dtd/000077500000000000000000000000001300170765700164515ustar00rootroot00000000000000opensips-2.2.2/doc/dbschema/dtd/dbschema.dtd000066400000000000000000000032431300170765700207160ustar00rootroot00000000000000 opensips-2.2.2/doc/dbschema/xsl/000077500000000000000000000000001300170765700165045ustar00rootroot00000000000000opensips-2.2.2/doc/dbschema/xsl/common.xsl000066400000000000000000000154341300170765700205330ustar00rootroot00000000000000 unsigned ERROR: Table: , column: - unsupported column type: . 0 1 1 1 0 ERROR: Column with id ' ' does not exist. ERROR: Column with id ' ' does not exist. opensips-2.2.2/doc/dbschema/xsl/db_berkeley.xsl000066400000000000000000000154111300170765700215050ustar00rootroot00000000000000 METADATA_COLUMNS METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NULL NULL ' ' NIL | | | ( int datetime double str ) opensips-2.2.2/doc/dbschema/xsl/dbschema2docbook.xsl000066400000000000000000000220771300170765700224350ustar00rootroot00000000000000 Table "<xsl:value-of select="name"/>" name type size default null key extra attributes description not specified NULL ' ' default yes no primary autoincrement
Table "<xsl:value-of select="name"/>" indexes name type links description unique primary default ,
opensips-2.2.2/doc/dbschema/xsl/dbtext.xsl000066400000000000000000000075631300170765700205410ustar00rootroot00000000000000 : ( int double string blob ,null ) opensips-2.2.2/doc/dbschema/xsl/docbook.xsl000066400000000000000000000035651300170765700206650ustar00rootroot00000000000000 opensips-2.2.2/doc/dbschema/xsl/mysql.xsl000066400000000000000000000101671300170765700204060ustar00rootroot00000000000000 ) ENGINE= ; TINYINT SMALLINT INT BIGINT DATETIME DOUBLE FLOAT CHAR BLOB TEXT UNSIGNED AUTO_INCREMENT PRIMARY KEY opensips-2.2.2/doc/dbschema/xsl/oracle.xsl000066400000000000000000000153241300170765700205060ustar00rootroot00000000000000 ) ; CREATE OR REPLACE TRIGGER before insert on FOR EACH ROW BEGIN auto_id(:NEW.id); END / BEGIN map2users(' '); END; / NUMBER(5) NUMBER(5) NUMBER(10) NUMBER(11) BIGINT DATE NUMBER NUMBER VARCHAR2 BLOB CLOB PRIMARY KEY DEFAULT NULL DEFAULT NULL ' ' NOT NULL , opensips-2.2.2/doc/dbschema/xsl/pi_framework_mod.xsl000066400000000000000000000137071300170765700225700ustar00rootroot00000000000000 <!-- provisionning --> <mod><mod_name> </mod_name> <cmd><cmd_name>show</cmd_name> <db_table_id> </db_table_id> <cmd_type>DB_QUERY</cmd_type> <query_cols> <col><field> </field><link_cmd>update</link_cmd></col> <col><field> </field></col> </query_cols> </cmd> <cmd><cmd_name>add</cmd_name> <db_table_id> </db_table_id> <cmd_type>DB_INSERT</cmd_type> <query_cols> <col><field> </field></col> </query_cols> </cmd> <cmd><cmd_name>update</cmd_name> <db_table_id> </db_table_id> <cmd_type>DB_UPDATE</cmd_type> <clause_cols> <col><field> </field><operator>=</operator></col> </clause_cols> <query_cols> <col><field> </field></col> </query_cols> </cmd> <cmd><cmd_name>delete</cmd_name> <db_table_id> </db_table_id> <cmd_type>DB_DELETE</cmd_type> <clause_cols> <col><field> </field><operator>=</operator></col> </clause_cols> </cmd> </mod> opensips-2.2.2/doc/dbschema/xsl/pi_framework_table.xsl000066400000000000000000000070531300170765700230750ustar00rootroot00000000000000 <!-- Declaration of table--> <db_table id=" "> <table_name> </table_name> <db_url_id>mysql</db_url_id> </db_table> <column><field> </field><type> </type></column> DB_INT DB_INT DB_INT DB_BIGINT DB_DATETIME DB_DOUBLE DB_DOUBLE DB_STR DB_BLOB DB_BLOB opensips-2.2.2/doc/dbschema/xsl/postgres.xsl000066400000000000000000000122531300170765700211050ustar00rootroot00000000000000 ) Type= ; BIGSERIAL PRIMARY KEY BIGSERIAL SMALLINT SMALLINT INTEGER BIGINT TIMESTAMP DOUBLE PRECISION REAL VARCHAR BYTEA TEXT WITHOUT TIME ZONE SERIAL PRIMARY KEY ALTER SEQUENCE _ _seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/doc/dbschema/xsl/sql.xsl000066400000000000000000000156141300170765700200420ustar00rootroot00000000000000 CREATE TABLE ( INSERT INTO version (table_name, table_version) values (' ',' '); , CONSTRAINT UNIQUE ( ) , PRIMARY KEY ( ) , CREATE UNIQUE INDEX ON ( ); DEFAULT NULL DEFAULT NULL ' ' NOT NULL , ( ) , opensips-2.2.2/doc/dbschema/xsl/sqlite.xsl000066400000000000000000000110631300170765700205360ustar00rootroot00000000000000 ) ; INTEGER PRIMARY KEY AUTOINCREMENT TINYINT SMALLINT INTEGER BIGINT DATETIME DOUBLE FLOAT CHAR BLOB TEXT PRIMARY KEY AUTOINCREMENT opensips-2.2.2/doc/doxygen/000077500000000000000000000000001300170765700156055ustar00rootroot00000000000000opensips-2.2.2/doc/doxygen/opensips-doxygen000066400000000000000000001602721300170765700210530ustar00rootroot00000000000000# Doxyfile 1.5.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file that # follow. The default is UTF-8 which is also the encoding used for all text before # the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into # libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of # possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "OpenSIPS - The Open SIP Server " # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, # Italian, Japanese, Japanese-en (Japanese with English messages), Korean, # Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, # Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 3 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be extracted # and appear in the documentation as a namespace called 'anonymous_namespace{file}', # where file will be replaced with the base name of the file that contains the anonymous # namespace. By default anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = YES # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 5 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ./ \ db \ mem \ mi \ modules/acc \ modules/alias_db \ modules/auth \ modules/auth_db \ modules/auth_diameter \ modules/auth_radius \ modules/avpops \ modules/avp_radius \ modules/benchmark \ modules/cfgutils \ modules/cpl_c \ modules/carrierroute \ modules/db_berkeley \ modules/db_text \ modules/dialog \ modules/dispatcher \ modules/diversion \ modules/domain \ modules/domainpolicy \ modules/enum \ modules/exec \ modules/db_flatstore \ modules/gflags \ modules/group \ modules/group_radius \ modules/h350 \ modules/imc \ modules/jabber \ modules/ldap \ modules/mangler \ modules/maxfwd \ modules/mediaproxy \ modules/mi_fifo \ modules/mi_xmlrpc \ modules/mi_datagram \ modules/msilo \ modules/db_mysql \ modules/nathelper \ modules/options \ modules/osp \ modules/path \ modules/perl \ modules/perlvdb \ modules/permissions \ modules/pike \ modules/db_postgres \ modules/presence \ modules/pua \ modules/pua_mi \ modules/pua_usrloc \ modules/registrar \ modules/rr \ modules/seas \ modules/siptrace \ modules/sl \ modules/sms \ modules/snmpstats \ modules/speeddial \ modules/sst \ modules/statistics \ modules/textops \ modules/tlsops \ modules/tm \ modules/uac \ modules/uac_redirect \ modules/db_unixodbc \ modules/uri \ modules/uri_db \ modules/uri_radius \ modules/usrloc \ modules/xlog \ modules/xmpp \ parser \ parser/contact \ parser/digest \ tls \ utils # This tag can be used to specify the character encoding of the source files that # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default # input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. # See http://www.gnu.org/software/libiconv for the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py FILE_PATTERNS = *.c \ *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the output. # The symbol name can be a fully qualified name, a word, or if the wildcard * is used, # a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = ./ \ doc \ etc \ scripts \ examples # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = images # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH # then you must also enable this option. If you don't then doxygen will produce # a warning and turn it on anyway SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = __GNUC__ # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to # produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to # specify the directory where the mscgen tool resides. If left empty the tool is assumed to # be found in the default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a caller dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the number # of direct children of the root node in a graph is already larger than # MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO opensips-2.2.2/doc/entities.xml000066400000000000000000000055571300170765700165120ustar00rootroot00000000000000 www.opensips-solutions.com"> &osipshome;"> &osipsbugs;"> &osipshelp;"> &osipsusers;"> &osipsdev;"> &osipsdbdocs;"> users@lists.opensips.org
"> users@lists.opensips.org
"> devel@lists.opensips.org"> SIP"> SDP"> RTP"> PSTN"> HTTP"> R-URI"> URI"> URL"> UAC"> UAS"> UA"> NAT"> IP"> UDP"> IETF"> IM"> SMTP"> GNU"> GPL"> RFC3261"> RFC822"> RFC2543"> opensips-2.2.2/doc/module-docbook.css000066400000000000000000000101701300170765700175440ustar00rootroot00000000000000BODY { padding: 20px; margin: 5px 50px 5px 50px; background: #ffffff; color: #000000; width: 700px; border: solid 2px #888888; font-family: Helvetica,Arial; } P { font-family: Helvetica,Arial; font-size: 12; text-align: justify; } P.C2 { COLOR: #ffffff ; BACKGROUND-color: #a0a0d0; BORDER: solid 1px #606090; PADDING: 1px } A { color: #041fc5; text-decoration: none; } A:hover { color: #990000; text-decoration: underline; } DIV.ABSTRACT { border: solid 2px; padding-left: 10pt; padding-right: 10pt; } PRE.SCREEN { font-family:monospace; white-space: pre; background-color: #fefeee; border:solid; color: #000000; border-color: #99CCCC; border-left: solid #99CCCC 1px; border-right: solid #99CCCC 1px; border-top: solid #99CCCC 1px; border-bottom: solid #99CCCC 1px; padding-left: 15pt; } PRE.PROGRAMLISTING { font-family:monospace; white-space: pre; background-color: #fefeee; border:solid; color: #000000; border-color: #99CCCC; border-left: solid #99CCCC 1px; border-right: solid #99CCCC 1px; border-top: solid #99CCCC 1px; border-bottom: solid #99CCCC 1px; padding-left: 15pt; } H1 { color: #000000; border: solid 2px #a0a0a0; background-color: #DCDCDC; font-variant: small-caps; font-size: 16; padding-left: 5px; } .TITLE a { color: #000000; text-decoration: none; } .TITLE a:active { color: #000000; text-decoration: none; } .TITLE a:visited { color: #000000; text-decoration: none; } H2 { COLOR: #000000 ; font-style: italic; border: solid 1px #b0b0b0; background-color: #ECECEC; padding-left: 5px; font-family: Helvetica,Arial; font-weight: bold; font-size: 14; } H2 a { color: #000000; text-decoration: none; } H2 a:active { color: #000000; text-decoration: none; } H2 a:visited { color: #000000; text-decoration: none; } H2 a:hover { color: #000000; text-decoration: none; } H3.SECTION { COLOR: #000000 ; font-style: italic; border: solid 1px #c0c0c0; background-color: #F2F2F2; padding-left: 5px; font-family: Helvetica,Arial; font-weight: bold; font-size: 13; } H3 a { color: #000000; text-decoration: none; } H3 a:active { color: #000000; text-decoration: none; } H3 a:visited { color: #000000; text-decoration: none; } H3 a:hover { color: #000000; text-decoration: none; } H3.AUTHOR { font-family: Helvetica,Arial; font-weight: bold; font-size: 13; } H3.EDITOR { font-family: Helvetica,Arial; font-weight: bold; font-style: italic; font-size: 11; } H4 { font-family: Helvetica,Arial; font-weight: bold; font-size: 12; } TABLE.IMPORTANT { font-style:italic; border: solid 2px #ff0000; width: 70%; margin-left: 15%; } TABLE.CAUTION { font-style:italic; border: ridge 2px #ffff00; width: 70%; margin-left: 15%; } TABLE.NOTE { font-style:italic; border: solid 1px #000000; width: 70%; margin-left: 15%; } TABLE.TIP { font-style:italic; border: solid 1px #000000; width: 70%; margin-left: 15%; } TABLE.WARNING { font-style:italic; font-weight: bold; border: ridge 4px #ff0000; width: 70%; margin-left: 15%; } DIV.VARIABLELIST { font-family: sans-serif; font-style: normal; font-weight: normal; padding-left: 20px; } .VARLISTENTRY { font-weight: bold; margin-top: 10px; COLOR: #ffffff ; BACKGROUND-color: #a0a0d0; BORDER: solid 1px #606090; PADDING: 1px } DIV.NAVFOOTER { color: #000000; background-color: #EFEFF8; padding: 5px; margin-top: 10px; width: 100%; border: thin solid #a0a0d0; } DIV.NUKEFOOTER { color: #000000; background-color: #B0E0E6; padding: 5px; margin-top: 10px; width: 100%; border: thin solid #a0a0d0; } DIV.NAVHEADER { color: #000000; background-color: #EFEFF8; padding: 5px; margin-bottom: 10px; width: 100%; border: thin solid #a0a0d0; } DIV.SECT1,DIV.SECT2,DIV.SECT3 { margin-left: 20px; } DIV.EXAMPLE,DIV.TOC { border: thin dotted #70AAE5; padding-left: 10px; padding-right: 10px; color: #000000; background-color: #EFF8F8; } DIV.TOC { margin-left: 20px; margin-right: 20px; } opensips-2.2.2/doc/module_faq.xml000066400000000000000000000000001300170765700167540ustar00rootroot00000000000000opensips-2.2.2/dprint.c000066400000000000000000000052261300170765700150340ustar00rootroot00000000000000/* * debug print * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief OpenSIPS Debug console print functions */ #include "dprint.h" #include "globals.h" #include "pt.h" #include #include #include static int log_level_holder = L_NOTICE; /* current logging level for this process */ int *log_level = &log_level_holder; /* used when resetting the logging level of this process */ static int *default_log_level; static char* str_fac[]={"LOG_AUTH","LOG_CRON","LOG_DAEMON", "LOG_KERN","LOG_LOCAL0","LOG_LOCAL1", "LOG_LOCAL2","LOG_LOCAL3","LOG_LOCAL4","LOG_LOCAL5", "LOG_LOCAL6","LOG_LOCAL7","LOG_LPR","LOG_MAIL", "LOG_NEWS","LOG_USER","LOG_UUCP", #ifndef __OS_solaris "LOG_AUTHPRIV","LOG_FTP","LOG_SYSLOG", #endif 0}; static int int_fac[]={LOG_AUTH , LOG_CRON , LOG_DAEMON , LOG_KERN , LOG_LOCAL0 , LOG_LOCAL1 , LOG_LOCAL2 , LOG_LOCAL3 , LOG_LOCAL4 , LOG_LOCAL5 , LOG_LOCAL6 , LOG_LOCAL7 , LOG_LPR , LOG_MAIL , LOG_NEWS , LOG_USER , LOG_UUCP #ifndef __OS_solaris ,LOG_AUTHPRIV,LOG_FTP,LOG_SYSLOG #endif }; char ctime_buf[256]; int str2facility(char *s) { int i; for( i=0; str_fac[i] ; i++) { if (!strcasecmp(s,str_fac[i])) return int_fac[i]; } return -1; } int dp_my_pid(void) { return my_pid(); } void dprint(char * format, ...) { va_list ap; //fprintf(stderr, "%2d(%d) ", process_no, my_pid()); va_start(ap, format); vfprintf(stderr,format,ap); fflush(stderr); va_end(ap); } int init_log_level(void) { log_level = &pt[process_no].log_level; *log_level = log_level_holder; default_log_level = &pt[process_no].default_log_level; *default_log_level = log_level_holder; return 0; } /* call before pt is freed */ void cleanup_log_level(void) { static int my_log_level; my_log_level = *log_level; log_level = &my_log_level; } void reset_proc_log_level(void) { *log_level = *default_log_level; } opensips-2.2.2/dprint.h000066400000000000000000000263351300170765700150450ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief OpenSIPS Debug console print functions * \see syslog.h */ /*! \page DebugLogFunction Description of the logging functions: * * A) macros to log on a predefine log level and with standard prefix * for with additional info: [time] * No dynamic FMT is accepted (due macro processing). * LM_ALERT( fmt, ....) * LM_CRIT( fmt, ....) * LM_ERR( fmt, ...) * LM_WARN( fmt, ...) * LM_NOTICE( fmt, ...) * LM_INFO( fmt, ...) * LM_DBG( fmt, ...) * B) macros for generic logging ; no additional information is added; * Works with dynamic FMT. * LM_GEN1( log_level, fmt, ....) * LM_GEN2( log_facility, log_level, fmt, ...) */ #ifndef dprint_h #define dprint_h #include #include #include "pt.h" #define L_ALERT -3 /*!< Alert level */ #define L_CRIT -2 /*!< Critical level */ #define L_ERR -1 /*!< Error level */ #define L_WARN 1 /*!< Warning level */ #define L_NOTICE 2 /*!< Notice level */ #define L_INFO 3 /*!< Info level */ #define L_DBG 4 /*!< Debug level */ #ifdef __SUNPRO_C #define DP_PREFIX #else #define DP_PREFIX "%s [%d] " #endif #define DP_ALERT_TEXT "ALERT:" #define DP_CRIT_TEXT "CRITICAL:" #define DP_ERR_TEXT "ERROR:" #define DP_WARN_TEXT "WARNING:" #define DP_NOTICE_TEXT "NOTICE:" #define DP_INFO_TEXT "INFO:" #define DP_DBG_TEXT "DBG:" #define DP_ALERT_PREFIX DP_PREFIX DP_ALERT_TEXT #define DP_CRIT_PREFIX DP_PREFIX DP_CRIT_TEXT #define DP_ERR_PREFIX DP_PREFIX DP_ERR_TEXT #define DP_WARN_PREFIX DP_PREFIX DP_WARN_TEXT #define DP_NOTICE_PREFIX DP_PREFIX DP_NOTICE_TEXT #define DP_INFO_PREFIX DP_PREFIX DP_INFO_TEXT #define DP_DBG_PREFIX DP_PREFIX DP_DBG_TEXT #define DPRINT_LEV L_ERR #ifndef MOD_NAME #define MOD_NAME core #endif #ifndef NO_DEBUG #undef NO_LOG #endif /* vars:*/ extern int *log_level; extern int log_stderr; extern int log_facility; extern char* log_name; extern char ctime_buf[]; /* * must be called after init_multi_proc_support() * must be called once for each OpenSIPS process */ int init_log_level(void); /* must be called once, before the "pt" process table is freed */ void cleanup_log_level(void); int dp_my_pid(void); void dprint (char* format, ...); int str2facility(char *s); /* * set the (default) log level of a given process * * Note: the index param is not validated! */ static inline void __set_proc_log_level(int proc_idx, int level) { pt[proc_idx].log_level = level; } static inline void __set_proc_default_log_level(int proc_idx, int level) { pt[proc_idx].default_log_level = level; } /* set the current and default log levels for all OpenSIPS processes */ static inline void set_global_log_level(int level) { int i; for (i = 0; i < counted_processes; i++) { __set_proc_default_log_level(i, level); __set_proc_log_level(i, level); } } /* set the log level of the current process */ static inline void set_proc_log_level(int level) { __set_proc_log_level(process_no, level); } /* changes the logging level to the default value for the current process */ void reset_proc_log_level(void); static inline char* dp_time(void) { time_t ltime; time(<ime); ctime_r( <ime, ctime_buf); ctime_buf[19] = 0; /* remove year*/ return ctime_buf+4; /* remove name of day*/ } #define is_printable(_level) (((int)(*log_level)) >= ((int)(_level))) #if defined __GNUC__ #define __DP_FUNC __FUNCTION__ #elif defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L #define __DP_FUNC __func__ #else #define __DP_FUNC ((__const char *) 0) #endif #ifdef NO_LOG #ifdef __SUNPRO_C #define LM_GEN2(facility, lev, ...) #define LM_GEN1(lev, ...) #define LM_ALERT( ...) #define LM_CRIT( ...) #define LM_ERR( ...) #define LM_WARN( ...) #define LM_NOTICE( ...) #define LM_INFO( ...) #define LM_DBG( ...) #else #define LM_GEN2(facility, lev, fmt, args...) #define LM_GEN1(lev, fmt, args...) #define LM_ALERT(fmt, args...) #define LM_CRIT(fmt, args...) #define LM_ERR(fmt, args...) #define LM_WARN(fmt, args...) #define LM_NOTICE(fmt, args...) #define LM_INFO(fmt, args...) #define LM_DBG(fmt, args...) #endif #else /* NO_LOG */ #ifdef __SUNPRO_C #define LOG_PREFIX_UTIL2(_n) #_n #define LOG_PREFIX_UTIL(_n) LOG_PREFIX_UTIL2(_n) #define LOG_PREFIX LOG_PREFIX_UTIL(MOD_NAME) ": " #define MY_DPRINT( ...) \ dprint( LOG_PREFIX __VA_ARGS__ ) \ #define MY_SYSLOG( _log_level, ...) \ syslog( (_log_level)|log_facility, \ LOG_PREFIX __VA_ARGS__);\ #define LM_GEN1(_lev, ...) \ LM_GEN2( log_facility, _lev, __VA_ARGS__) #define LM_GEN2( _facility, _lev, ...) \ do { \ if (is_printable(_lev)){ \ if (log_stderr) dprint (__VA_ARGS__); \ else { \ switch(_lev){ \ case L_CRIT: \ syslog(LOG_CRIT|_facility, __VA_ARGS__); \ break; \ case L_ALERT: \ syslog(LOG_ALERT|_facility, __VA_ARGS__); \ break; \ case L_ERR: \ syslog(LOG_ERR|_facility, __VA_ARGS__); \ break; \ case L_WARN: \ syslog(LOG_WARNING|_facility, __VA_ARGS__);\ break; \ case L_NOTICE: \ syslog(LOG_NOTICE|_facility, __VA_ARGS__); \ break; \ case L_INFO: \ syslog(LOG_INFO|_facility, __VA_ARGS__); \ break; \ case L_DBG: \ syslog(LOG_DEBUG|_facility, __VA_ARGS__); \ break; \ } \ } \ } \ }while(0) #define LM_ALERT( ...) \ do { \ if (is_printable(L_ALERT)){ \ if (log_stderr)\ MY_DPRINT( DP_ALERT_PREFIX __VA_ARGS__);\ else \ MY_SYSLOG( LOG_ALERT, DP_ALERT_TEXT __VA_ARGS__);\ } \ }while(0) #define LM_CRIT( ...) \ do { \ if (is_printable(L_CRIT)){ \ if (log_stderr)\ MY_DPRINT( DP_CRIT_PREFIX __VA_ARGS__);\ else \ MY_SYSLOG( LOG_CRIT, DP_CRIT_TEXT __VA_ARGS__);\ } \ }while(0) #define LM_ERR( ...) \ do { \ if (is_printable(L_ERR)){ \ if (log_stderr)\ MY_DPRINT( DP_ERR_PREFIX __VA_ARGS__);\ else \ MY_SYSLOG( LOG_ERR, DP_ERR_TEXT __VA_ARGS__);\ } \ }while(0) #define LM_WARN( ...) \ do { \ if (is_printable(L_WARN)){ \ if (log_stderr)\ MY_DPRINT( DP_WARN_PREFIX __VA_ARGS__);\ else \ MY_SYSLOG( LOG_WARNING, DP_WARN_TEXT __VA_ARGS__);\ } \ }while(0) #define LM_NOTICE( ...) \ do { \ if (is_printable(L_NOTICE)){ \ if (log_stderr)\ MY_DPRINT( DP_NOTICE_PREFIX __VA_ARGS__);\ else \ MY_SYSLOG( LOG_NOTICE, DP_NOTICE_TEXT __VA_ARGS__);\ } \ }while(0) #define LM_INFO( ...) \ do { \ if (is_printable(L_INFO)){ \ if (log_stderr)\ MY_DPRINT( DP_INFO_PREFIX __VA_ARGS__);\ else \ MY_SYSLOG( LOG_INFO, DP_INFO_TEXT __VA_ARGS__);\ } \ }while(0) #ifdef NO_DEBUG #define LM_DBG( ...) #else #define LM_DBG( ...) \ do { \ if (is_printable(L_DBG)){ \ if (log_stderr)\ MY_DPRINT( DP_DBG_PREFIX __VA_ARGS__);\ else \ MY_SYSLOG( LOG_DEBUG, DP_DBG_TEXT __VA_ARGS__);\ } \ }while(0) #endif /*NO_DEBUG*/ #else /*SUN_PRO_C*/ #define LOG_PREFIX_UTIL2(_n) #_n #define LOG_PREFIX_UTIL(_n) LOG_PREFIX_UTIL2(_n) #define LOG_PREFIX LOG_PREFIX_UTIL(MOD_NAME) ":%s: " #define MY_DPRINT( _prefix, _fmt, args...) \ dprint( _prefix LOG_PREFIX _fmt, dp_time(), \ dp_my_pid(), __DP_FUNC, ## args) \ #define MY_SYSLOG( _log_level, _prefix, _fmt, args...) \ syslog( (_log_level)|log_facility, \ _prefix LOG_PREFIX _fmt, __DP_FUNC, ##args);\ #define LM_GEN1(_lev, args...) \ LM_GEN2( log_facility, _lev, ##args) #define LM_GEN2( _facility, _lev, fmt, args...) \ do { \ if (is_printable(_lev)){ \ if (log_stderr) dprint ( fmt, ## args); \ else { \ switch(_lev){ \ case L_CRIT: \ syslog(LOG_CRIT|_facility, fmt, ##args); \ break; \ case L_ALERT: \ syslog(LOG_ALERT|_facility, fmt, ##args); \ break; \ case L_ERR: \ syslog(LOG_ERR|_facility, fmt, ##args); \ break; \ case L_WARN: \ syslog(LOG_WARNING|_facility, fmt, ##args);\ break; \ case L_NOTICE: \ syslog(LOG_NOTICE|_facility, fmt, ##args); \ break; \ case L_INFO: \ syslog(LOG_INFO|_facility, fmt, ##args); \ break; \ case L_DBG: \ syslog(LOG_DEBUG|_facility, fmt, ##args); \ break; \ } \ } \ } \ }while(0) #define LM_ALERT( fmt, args...) \ do { \ if (is_printable(L_ALERT)){ \ if (log_stderr)\ MY_DPRINT( DP_ALERT_PREFIX, fmt, ##args);\ else \ MY_SYSLOG( LOG_ALERT, DP_ALERT_TEXT, fmt, ##args);\ } \ }while(0) #define LM_CRIT( fmt, args...) \ do { \ if (is_printable(L_CRIT)){ \ if (log_stderr)\ MY_DPRINT( DP_CRIT_PREFIX, fmt, ##args);\ else \ MY_SYSLOG( LOG_CRIT, DP_CRIT_TEXT, fmt, ##args);\ } \ }while(0) #define LM_ERR( fmt, args...) \ do { \ if (is_printable(L_ERR)){ \ if (log_stderr)\ MY_DPRINT( DP_ERR_PREFIX, fmt, ##args);\ else \ MY_SYSLOG( LOG_ERR, DP_ERR_TEXT, fmt, ##args);\ } \ }while(0) #define LM_WARN( fmt, args...) \ do { \ if (is_printable(L_WARN)){ \ if (log_stderr)\ MY_DPRINT( DP_WARN_PREFIX, fmt, ##args);\ else \ MY_SYSLOG( LOG_WARNING, DP_WARN_TEXT, fmt, ##args);\ } \ }while(0) #define LM_NOTICE( fmt, args...) \ do { \ if (is_printable(L_NOTICE)){ \ if (log_stderr)\ MY_DPRINT( DP_NOTICE_PREFIX, fmt, ##args);\ else \ MY_SYSLOG( LOG_NOTICE, DP_NOTICE_TEXT, fmt, ##args);\ } \ }while(0) #define LM_INFO( fmt, args...) \ do { \ if (is_printable(L_INFO)){ \ if (log_stderr)\ MY_DPRINT( DP_INFO_PREFIX, fmt, ##args);\ else \ MY_SYSLOG( LOG_INFO, DP_INFO_TEXT, fmt, ##args);\ } \ }while(0) #ifdef NO_DEBUG #define LM_DBG( fmt, args...) #else #define LM_DBG( fmt, args...) \ do { \ if (is_printable(L_DBG)){ \ if (log_stderr)\ MY_DPRINT( DP_DBG_PREFIX, fmt, ##args);\ else \ MY_SYSLOG( LOG_DEBUG, DP_DBG_TEXT, fmt, ##args);\ } \ }while(0) #endif /*NO_DEBUG*/ #endif /*SUN_PRO_C*/ #endif #define report_programming_bug(format, args...) \ LM_CRIT("\n>>> " format"\nIt seems you have hit a programming bug.\n" \ "Please help us make OpenSIPS better by reporting it at " \ "https://github.com/OpenSIPS/opensips/issues\n\n", ##args); #define LM_BUG report_programming_bug #endif /* ifndef dprint_h */ opensips-2.2.2/dset.c000066400000000000000000000321641300170765700144740ustar00rootroot00000000000000/* * Copyright (C) 2001-2004 FhG FOKUS * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Destination set handling functions */ #include #include "dprint.h" #include "config.h" #include "parser/parser_f.h" #include "parser/msg_parser.h" #include "ut.h" #include "hash_func.h" #include "error.h" #include "context.h" #include "dset.h" #include "mem/mem.h" #include "ip_addr.h" #define CONTACT "Contact: " #define CONTACT_LEN (sizeof(CONTACT) - 1) #define CONTACT_DELIM ", " #define CONTACT_DELIM_LEN (sizeof(CONTACT_DELIM) - 1) #define Q_PARAM ";q=" #define Q_PARAM_LEN (sizeof(Q_PARAM) - 1) #define DSET_INCREMENT 4 struct branch { char uri[MAX_URI_SIZE]; unsigned int len; /* Real destination of the request */ char dst_uri[MAX_URI_SIZE]; unsigned int dst_uri_len; /* Path vector of the request */ char path[MAX_PATH_SIZE]; unsigned int path_len; int q; /* Preference of the contact among contact within the array */ struct socket_info* force_send_socket; unsigned int flags; }; struct dset_ctx { int enabled; /*! how many of them we currently have */ int nr_branches; /*! * Where we store URIs of additional transaction branches * (-1 because of the default branch, #0) */ struct branch *branches; }; static int dset_ctx_idx = -1; #define get_dset_ctx() \ (!current_processing_ctx ? NULL : (struct dset_ctx *) \ context_get_ptr(CONTEXT_GLOBAL, current_processing_ctx, dset_ctx_idx)) int get_nr_branches(void) { struct dset_ctx *dsct = get_dset_ctx(); return !dsct ? 0 : dsct->nr_branches; } #define store_dset_ctx(value) \ (context_put_ptr( \ CONTEXT_GLOBAL, current_processing_ctx, dset_ctx_idx, value)) /*! Frees a destination set which used to be stored in the global context */ static void dset_destroy(void *dsct) { pkg_free(((struct dset_ctx *)dsct)->branches); pkg_free(dsct); } int init_dset(void) { dset_ctx_idx = context_register_ptr(CONTEXT_GLOBAL, dset_destroy); if (dset_ctx_idx < 0) return -1; return 0; } static inline unsigned int* get_ptr_bflags(struct sip_msg *msg, unsigned int b_idx) { struct dset_ctx *dsct = get_dset_ctx(); if (!dsct && b_idx != 0) return NULL; if (b_idx == 0) { return &getb0flags(msg); } else { if (b_idx - 1 < dsct->nr_branches) { return &dsct->branches[b_idx - 1].flags; } else { return 0; } } } int setbflag(struct sip_msg *msg, unsigned int b_idx, unsigned int mask) { unsigned int *flags; flags = get_ptr_bflags( msg, b_idx ); #ifdef EXTRA_DEBUG LM_DBG("bflags for %p : (%u, %u)\n", msg, mask, *flags); #endif if (flags==0) return -1; (*flags) |= mask; return 1; } /*! \brief * Tests the per branch flags */ int isbflagset(struct sip_msg *msg, unsigned int b_idx, unsigned int mask) { unsigned int *flags; flags = get_ptr_bflags( msg, b_idx ); #ifdef EXTRA_DEBUG LM_DBG("bflags for %p : (%u, %u)\n", msg, mask, *flags); #endif if (flags==0) return -1; return ( (*flags) & mask) ? 1 : -1; } /*! \brief * Resets the per branch flags */ int resetbflag(struct sip_msg *msg, unsigned int b_idx, unsigned int mask) { unsigned int *flags; flags = get_ptr_bflags( msg, b_idx ); #ifdef EXTRA_DEBUG LM_DBG("bflags for %p : (%u, %u)\n", msg, mask, *flags); #endif if (flags==0) return -1; (*flags) &= ~mask; return 1; } /*! \brief Disable/Enables parallel branch usage (read and write) */ void set_dset_state(unsigned char enable) { struct dset_ctx *dsct = get_dset_ctx(); static unsigned int bk_nr_branches; if (!dsct) return; if (enable) { /* enable dset usage */ if (dsct->enabled) return; /* already enabled */ /* enable read */ dsct->nr_branches = bk_nr_branches; bk_nr_branches = 0; /* enable write */ dsct->enabled = 1; } else { /* disable dset usage */ if (!dsct->enabled) return; /* already disabled */ /* disable read */ bk_nr_branches = dsct->nr_branches; dsct->nr_branches = 0; /* disabel write */ dsct->enabled = 0; } } /*! \brief Find the next brand from the destination set * \return Return the next branch from the dset * array, 0 is returned if there are no * more branches */ char* get_branch(unsigned int idx, int* len, qvalue_t* q, str* dst_uri, str* path, unsigned int *flags, struct socket_info** force_socket) { struct dset_ctx *dsct = get_dset_ctx(); struct branch *branches; if (dsct && idx < dsct->nr_branches) { branches = dsct->branches; *len = branches[idx].len; *q = branches[idx].q; if (dst_uri) { dst_uri->len = branches[idx].dst_uri_len; dst_uri->s = (dst_uri->len)?branches[idx].dst_uri : NULL; } if (path) { path->len = branches[idx].path_len; path->s = (path->len)?branches[idx].path : NULL; } if (force_socket) *force_socket = branches[idx].force_send_socket; if (flags) *flags = branches[idx].flags; return branches[idx].uri; } else { *len = 0; *q = Q_UNSPECIFIED; if (dst_uri) { dst_uri->s = NULL; dst_uri->len = 0; } if (force_socket) *force_socket = NULL; if (flags) *flags = 0; return NULL; } } /*! \brief * Empty the dset array */ void clear_branches(void) { struct dset_ctx *dsct = get_dset_ctx(); if (dsct) dsct->nr_branches = 0; } /* ! \brief * Add a new branch to current transaction */ int append_branch(struct sip_msg* msg, str* uri, str* dst_uri, str* path, qvalue_t q, unsigned int flags, struct socket_info* force_socket) { str luri; int nr_branches; struct branch *branches; struct dset_ctx *dsct = get_dset_ctx(); if (dsct && !dsct->enabled) return -1; if (!dsct) { dsct = pkg_malloc(sizeof *dsct); if (!dsct) { LM_ERR("no more pkg mem!\n"); return E_OUT_OF_MEM; } memset(dsct, 0, sizeof *dsct); dsct->enabled = 1; store_dset_ctx(dsct); } nr_branches = dsct->nr_branches; /* if we have already set up the maximum number * of branches, don't try new ones */ if (nr_branches == MAX_BRANCHES - 1) { LM_ERR("max nr of branches exceeded\n"); ser_error = E_TOO_MANY_BRANCHES; return -1; } if (nr_branches % DSET_INCREMENT == 0) { dsct->branches = pkg_realloc(dsct->branches, (nr_branches + DSET_INCREMENT) * sizeof *dsct->branches); if (!dsct->branches) { LM_ERR("no more pkg mem!\n"); pkg_free(dsct); return E_OUT_OF_MEM; } } /* if not parameterized, take current uri */ if (uri==0 || uri->len==0 || uri->s==0) { if (msg->new_uri.s) luri = msg->new_uri; else luri = msg->first_line.u.request.uri; } else { luri = *uri; } if (luri.len > MAX_URI_SIZE - 1) { LM_ERR("too long uri: %.*s\n", luri.len, luri.s); return -1; } branches = dsct->branches; /* copy the dst_uri */ if (dst_uri && dst_uri->len && dst_uri->s) { if (dst_uri->len > MAX_URI_SIZE - 1) { LM_ERR("too long dst_uri: %.*s\n", dst_uri->len, dst_uri->s); return -1; } memcpy(branches[nr_branches].dst_uri, dst_uri->s, dst_uri->len); branches[nr_branches].dst_uri[dst_uri->len] = '\0'; branches[nr_branches].dst_uri_len = dst_uri->len; } else { branches[nr_branches].dst_uri[0] = '\0'; branches[nr_branches].dst_uri_len = 0; } /* copy the path string */ if (path && path->len && path->s) { if (path->len > MAX_PATH_SIZE - 1) { LM_ERR("too long path: %.*s\n", path->len, path->s); return -1; } memcpy(branches[nr_branches].path, path->s, path->len); branches[nr_branches].path[path->len] = 0; branches[nr_branches].path_len = path->len; } else { branches[nr_branches].path[0] = '\0'; branches[nr_branches].path_len = 0; } /* copy the ruri */ memcpy(branches[nr_branches].uri, luri.s, luri.len); branches[nr_branches].uri[luri.len] = '\0'; branches[nr_branches].len = luri.len; branches[nr_branches].q = q; branches[nr_branches].force_send_socket = force_socket; branches[nr_branches].flags = flags; dsct->nr_branches++; return 1; } /* ! \brief * Updates one or more fields of an already appended branch */ int update_branch(unsigned int idx, str** uri, str** dst_uri, str** path, qvalue_t* q, unsigned int* flags, struct socket_info** force_socket) { struct dset_ctx *dsct = get_dset_ctx(); struct branch *branches; if (!dsct || !dsct->enabled || idx >= dsct->nr_branches) return -1; branches = dsct->branches; /* uri ? */ if (uri) { /* set uri */ if (*uri==NULL || (*uri)->len>MAX_URI_SIZE-1) { LM_ERR("empty or too long uri\n"); return -1; } memcpy(branches[idx].uri, (*uri)->s, (*uri)->len); branches[idx].uri[(*uri)->len] = '\0'; branches[idx].len = (*uri)->len; } /* duri ? */ if (dst_uri) { if (*dst_uri && (*dst_uri)->len && (*dst_uri)->s) { if ((*dst_uri)->len > MAX_URI_SIZE - 1) { LM_ERR("too long dst_uri: %.*s\n", (*dst_uri)->len, (*dst_uri)->s); return -1; } memcpy(branches[idx].dst_uri, (*dst_uri)->s, (*dst_uri)->len); branches[idx].dst_uri[(*dst_uri)->len] = '\0'; branches[idx].dst_uri_len = (*dst_uri)->len; } else { branches[idx].dst_uri[0] = '\0'; branches[idx].dst_uri_len = 0; } } /* path ? */ if (path) { if (*path && (*path)->len && (*path)->s) { if ((*path)->len > MAX_PATH_SIZE - 1) { LM_ERR("too long path: %.*s\n", (*path)->len, (*path)->s); return -1; } memcpy(branches[idx].path, (*path)->s, (*path)->len); branches[idx].path[(*path)->len] = '\0'; branches[idx].path_len = (*path)->len; } else { branches[idx].path[0] = '\0'; branches[idx].path_len = 0; } } /* Q value ? */ if (q) branches[idx].q = *q; /* flags ? */ if (flags) branches[idx].flags = *flags; /* socket ? */ if (force_socket) branches[idx].force_send_socket = *force_socket; return 0; } int remove_branch(unsigned int idx) { struct dset_ctx *dsct = get_dset_ctx(); if (!dsct || !dsct->enabled || idx >= dsct->nr_branches) return -1; /* not last branch? */ if (idx + 1 != dsct->nr_branches) memmove(dsct->branches + idx, dsct->branches + idx + 1, (dsct->nr_branches - idx - 1) * sizeof *dsct->branches); dsct->nr_branches--; return 0; } /*! \brief * Create a Contact header field from the dset * array */ char* print_dset(struct sip_msg* msg, int* len) { int cnt, i, idx; unsigned int qlen; qvalue_t q; str uri; char* p, *qbuf; static char *dset = NULL; static unsigned int dset_len = 0; if (msg->new_uri.s) { cnt = 1; *len = msg->new_uri.len+2 /*for <>*/; if (get_ruri_q(msg) != Q_UNSPECIFIED) { *len += Q_PARAM_LEN + len_q(get_ruri_q(msg)); } } else { cnt = 0; *len = 0; } for( idx=0 ; (uri.s=get_branch(idx,&uri.len,&q,0,0,0,0))!=0 ; idx++ ) { cnt++; *len += uri.len+2 /*for <>*/ ; if (q != Q_UNSPECIFIED) { *len += Q_PARAM_LEN + len_q(q); } } if (cnt == 0) return 0; *len += CONTACT_LEN + CRLF_LEN + (cnt - 1) * CONTACT_DELIM_LEN; /* does the current buffer fit the new dset ? */ if (*len + 1 > dset_len) { /* need to resize */ dset = pkg_realloc(dset, *len + 1); if (!dset) { dset_len = 0; LM_ERR("failed to allocate redirect buffer for %d bytes\n", *len + 1); return NULL; } dset_len = *len + 1; } memcpy(dset, CONTACT, CONTACT_LEN); p = dset + CONTACT_LEN; if (msg->new_uri.s) { *p++ = '<'; memcpy(p, msg->new_uri.s, msg->new_uri.len); p += msg->new_uri.len; *p++ = '>'; if (get_ruri_q(msg) != Q_UNSPECIFIED) { memcpy(p, Q_PARAM, Q_PARAM_LEN); p += Q_PARAM_LEN; qbuf = q2str(get_ruri_q(msg), &qlen); memcpy(p, qbuf, qlen); p += qlen; } i = 1; } else { i = 0; } for( idx=0 ; (uri.s=get_branch(idx,&uri.len,&q,0,0,0,0))!=0 ; idx++ ) { if (i) { memcpy(p, CONTACT_DELIM, CONTACT_DELIM_LEN); p += CONTACT_DELIM_LEN; } *p++ = '<'; memcpy(p, uri.s, uri.len); p += uri.len; *p++ = '>'; if (q != Q_UNSPECIFIED) { memcpy(p, Q_PARAM, Q_PARAM_LEN); p += Q_PARAM_LEN; qbuf = q2str(q, &qlen); memcpy(p, qbuf, qlen); p += qlen; } i++; } memcpy(p, CRLF " ", CRLF_LEN + 1); return dset; } /*! \brief moves the uri to destination for all branches and * all uris are set to given uri */ int branch_uri2dset( str *new_uri ) { struct dset_ctx *dsct = get_dset_ctx(); struct branch *branches; unsigned int b; /* no branches have been added yet */ if (!dsct) return 0; branches = dsct->branches; if (new_uri->len+1 > MAX_URI_SIZE) { LM_ERR("new uri too long (%d)\n",new_uri->len); return -1; } for (b = 0; b < dsct->nr_branches; b++) { /* move uri to dst */ memcpy(branches[b].dst_uri, branches[b].uri, branches[b].len + 1); branches[b].dst_uri_len = branches[b].len; /* set new uri */ memcpy(branches[b].uri, new_uri->s, new_uri->len); branches[b].len = new_uri->len; branches[b].uri[new_uri->len] = '\0'; } return 0; } opensips-2.2.2/dset.h000066400000000000000000000047751300170765700145100ustar00rootroot00000000000000/* * Copyright (C) 2001-2004 FhG FOKUS * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Destination set handling functions */ #ifndef _DSET_H #define _DSET_H #include "ip_addr.h" #include "qvalue.h" struct sip_msg; int get_nr_branches(void); /*! \brief * To be called in the startup phase of OpenSIPS */ int init_dset(void); /*! \brief * Add a new branch to current transaction */ int append_branch(struct sip_msg* msg, str* uri, str* dst_uri, str* path, qvalue_t q, unsigned int flags, struct socket_info* force_socket); /* ! \brief * Updates an already created branches */ int update_branch(unsigned int idx, str** uri, str** dst_uri, str** path, qvalue_t* q, unsigned int* flags, struct socket_info** force_socket); /*! \brief * Get the next branch in the current transaction */ char* get_branch( unsigned int idx, int* len, qvalue_t* q, str* dst_uri, str* path, unsigned int *flags, struct socket_info** force_socket); /*! \brief * Removes a given branch in the current transaction */ int remove_branch( unsigned int idx); /*! \brief * Disable/Enables parallel branch usage (read and write) */ void set_dset_state(unsigned char enable); /*! \brief * Empty the array of branches */ void clear_branches(void); /*! \brief * Create a Contact header field from the * list of current branches */ char* print_dset(struct sip_msg* msg, int* len); int branch_uri2dset( str *new_uri ); /*! \brief * Set the per branch flag */ int setbflag(struct sip_msg *msg, unsigned int b_idx, unsigned int mask); /*! \brief * Test the per branch flag */ int isbflagset(struct sip_msg *msg, unsigned int b_idx, unsigned int mask); /*! \brief * Reset the per branch flag */ int resetbflag(struct sip_msg *msg, unsigned int b_idx, unsigned int mask); #endif /* _DSET_H */ opensips-2.2.2/errinfo.c000066400000000000000000000033361300170765700152000ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file errinfo.c * \brief OpenSIPS Error info functions */ #include #include #include "dprint.h" #include "errinfo.h" /*! global error info */ err_info_t _oser_err_info; /*! \brief Get global error state */ err_info_t* get_err_info(void) { return &_oser_err_info; } /*! \brief Initialize global error state */ void init_err_info(void) { memset(&_oser_err_info, 0, sizeof(err_info_t)); } /*! \brief Set suggested error info message */ void set_err_info(int ec, int el, char *info) { LM_DBG("ec: %d, el: %d, ei: '%s'\n", ec, el, (info)?info:""); _oser_err_info.eclass = ec; _oser_err_info.level = el; if(info) { _oser_err_info.info.s = info; _oser_err_info.info.len = strlen(info); } } /*! \brief Set suggested error reply */ void set_err_reply(int rc, char *rr) { _oser_err_info.rcode = rc; if(rr) { _oser_err_info.rreason.s = rr; _oser_err_info.rreason.len = strlen(rr); } } opensips-2.2.2/errinfo.h000066400000000000000000000033271300170765700152050ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file errinfo.h * \brief Error handling */ #ifndef _errinfo_h_ #define _errinfo_h_ #include "str.h" /*! \name ErrorClasses error clases */ #define OSER_EC_PARSER 1 /*!< parse error */ #define OSER_EC_PMEMORY 2 /*!< private memory error */ #define OSER_EC_SMEMORY 3 /*!< share memory error */ #define OSER_EC_ASSERT 4 /*!< assertion error */ #define OSER_EL_CRITIC 1 #define OSER_EL_HIGH 2 #define OSER_EL_MEDIUM 3 /*!< severity level normal - used by parsing errors */ #define OSER_EL_NORMAL 4 #define OSER_EL_LOW 5 typedef struct err_info_ { int eclass; /*!< error class */ int level; /*!< severity level (lower is higher) */ str info; /*!< error details */ int rcode; /*!< recommended reply code */ str rreason; /*!< recommended reply reason phrase */ } err_info_t; void init_err_info(); void set_err_info(int ec, int el, char *info); void set_err_reply(int rc, char *rr); err_info_t* get_err_info(); #endif opensips-2.2.2/error.c000066400000000000000000000151731300170765700146670ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2003-04-04 phrase length corrected not to include trailer 0 (jiri) * 2006-12-18 error phrases updates (norman) */ /*! * \file error.c * \brief OpenSIPS Error handling functions * * \note For a list of error codes in SIP, please check * http://www.iana.org/assignments/sip-parameters */ #include #include "error.h" #include "str.h" #include "parser/msg_parser.h" #include "mem/mem.h" /*! current function's error; */ int ser_error=-1; /*! previous error */ int prev_ser_error=-1; int err2reason_phrase( int ser_error, /*!< current internal ser error */ int *sip_error, /*!< the sip error code to which ser error will be turned */ char *phrase, /*!< resulting error text */ int etl, /*!< error text buffer length */ char *signature ) /*!< extra text to be appended */ { char *error_txt; switch( ser_error ) { case E_IP_BLOCKED: error_txt="Filtered destination"; *sip_error=-ser_error; break; case E_SEND: error_txt="Send failed"; *sip_error=-ser_error; break; case E_BAD_ADDRESS: error_txt="Unresolvable destination"; *sip_error=-ser_error; break; case E_BAD_REQ: error_txt="Bad Request"; *sip_error=-ser_error; break; case E_BAD_URI: error_txt="Bad URI"; *sip_error=-ser_error; break; case E_BAD_TUPEL: error_txt="Transaction tuple incomplete"; *sip_error=-E_BAD_REQ; break; case E_BAD_TO: error_txt="Bad To"; *sip_error=-E_BAD_REQ; break; case E_EXEC: error_txt="Error in external logic"; *sip_error=-E_BAD_SERVER; break; case E_TOO_MANY_BRANCHES: error_txt="Forking capacity exceeded"; *sip_error=-E_BAD_SERVER; break; case E_Q_INV_CHAR: error_txt="Invalid character in q parameter"; *sip_error=-E_BAD_REQ; break; case E_Q_EMPTY: error_txt="Empty q parameter"; *sip_error=-E_BAD_REQ; break;; case E_Q_TOO_BIG: error_txt="q parameter too big"; *sip_error=-E_BAD_REQ; break; case E_NO_DESTINATION: error_txt="No destination available"; *sip_error=-E_BAD_SERVER; break; case E_OUT_OF_MEM: /* dont disclose lack of mem in release mode */ #ifdef DEBUG error_txt="Excuse me I ran out of memory"; *sip_error=-E_BAD_SERVER; break; #endif default: error_txt="Server error occurred"; *sip_error=-E_BAD_SERVER; break; } return snprintf( phrase, etl, "%s (%d/%s)", error_txt, -ser_error, signature ); } char *error_text( int code ) { switch(code) { case 100: return "Trying"; case 180: return "Ringing"; case 181: return "Call is Being Forwarded"; case 182: return "Queued"; case 183: return "Session Progress"; case 200: return "OK"; case 202: return "Accepted"; case 300: return "Multiple Choices"; case 301: return "Moved Permanently"; case 302: return "Moved Temporarily"; case 305: return "Use Proxy"; case 380: return "Alternative Service"; case 400: return "Bad Request"; case 401: return "Unauthorized"; case 402: return "Payment Required"; case 403: return "Forbidden"; case 404: return "Not Found"; case 405: return "Method not Allowed"; case 406: return "Not Acceptable"; case 407: return "Proxy Authentication Required"; case 408: return "Request Timeout"; case 409: return "Conflict"; case 410: return "Gone"; case 411: return "Length Required"; case 412: return "Conditional Request Failed"; case 413: return "Request Entity Too Large"; case 414: return "Request-URI Too Long"; case 415: return "Unsupported Media Type"; case 416: return "Unsupported URI Scheme"; case 417: return "Unknown Resource-Priority"; case 420: return "Bad Extension"; case 421: return "Extension Required"; case 422: return "Session Interval Too Small"; case 423: return "Interval Too Brief"; case 428: return "Use Identity Header"; case 429: return "Provide Referrer Identity"; case 436: return "Bad Identity-Info"; case 437: return "Unsupported Certificate"; case 438: return "Invalid Identity Header"; case 480: return "Temporarily Unavailable"; case 481: return "Call/Transaction Does not Exist"; case 482: return "Loop Detected"; case 483: return "Too Many Hops"; case 484: return "Address Incomplete"; case 485: return "Ambiguous"; case 486: return "Busy Here"; case 487: return "Request Terminated"; case 488: return "Not Acceptable Here"; case 489: return "Bad Event"; case 491: return "Request Pending"; case 493: return "Undecipherable"; case 494: return "Security Agreement Required"; case 500: return "Server Internal Error"; case 501: return "Not Implemented"; case 502: return "Bad Gateway"; case 503: return "Service Unavailable"; case 504: return "Server Time-out"; case 505: return "Version Not Supported"; case 513: return "Message Too Large"; case 580: return "Precondition Failure"; case 600: return "Busy Everywhere"; case 603: return "Decline"; case 604: return "Does not Exist Anywhere"; case 606: return "Not Acceptable"; } if (code>=600) return "Global Failure"; else if (code>=500) return "Server Failure"; else if (code>=400) return "Request Failure"; else if (code>=300) return "Redirection"; else if (code>=200) return "Successful"; else if (code>=100) return "Provisional"; else return "Unspecified"; } void get_reply_status( str *status, struct sip_msg *reply, int code ) { str phrase; status->s=0; if (reply==0) { LM_CRIT("called with 0 msg\n"); return; } if (reply==FAKED_REPLY) { phrase.s=error_text(code); phrase.len=strlen(phrase.s); } else { phrase=reply->first_line.u.reply.reason; } status->len=phrase.len+3/*code*/+1/*space*/; status->s=pkg_malloc(status->len+1/*ZT */); if (!status->s) { LM_ERR("no pkg mem\n"); return; } status->s[3]=' '; status->s[2]='0'+code % 10; code=code/10; status->s[1]='0'+code% 10; code=code/10; status->s[0]='0'+code % 10; memcpy(&status->s[4], phrase.s, phrase.len); status->s[status->len]=0; } opensips-2.2.2/error.h000066400000000000000000000055601300170765700146730ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Error definitions */ #ifndef error_h #define error_h #define E_UNSPEC -1 #define E_OUT_OF_MEM -2 #define E_BAD_RE -3 /* #define E_BAD_ADDRESS -4 */ #define E_BUG -5 #define E_CFG -6 #define E_NO_SOCKET -7 #define E_BAD_VIA -8 /*!< unresolvable topmost Via */ #define E_BAD_TUPEL -9 /*!< incomplete transaction tuple */ #define E_SCRIPT -10 /*!< script programming error */ #define E_EXEC -11 /*!< error in execution of external tools*/ #define E_TOO_MANY_BRANCHES -12 /*!< too many branches demanded */ #define E_BAD_TO -13 #define E_INVALID_PARAMS -14 /*!< invalid params */ #define E_Q_INV_CHAR -15 /*!< Invalid character in q */ #define E_Q_EMPTY -16 /*!< Empty q */ #define E_Q_TOO_BIG -17 /*!< q too big (> 1) */ #define E_NO_DESTINATION -18 /*!< No available destination */ /* opensips specific error codes */ #define E_IP_BLOCKED -473 /*!< destination filtered */ #define E_BAD_PROTO -474 /*!< bad protocol, like */ #define E_BAD_URI -475 /*!< unparseable URI */ #define E_BAD_ADDRESS -476 /*!< unresolvable next-hop address */ #define E_SEND -477 /*!< generic send error */ #define E_BAD_REQ -400 /*!< generic malformed request */ #define E_BAD_SERVER -500 /*!< error in server */ /* * portable macro which prevents "unused variable" compiler warnings * when defining certain flags, e.g. NO_LOG, NO_DEBUG */ #define UNUSED(x) (void)(x) #define MAX_REASON_LEN 128 #include "str.h" /*! \brief processing status of the last command */ extern int ser_error; extern int prev_ser_error; struct sip_msg; /*! \brief ser error -> SIP error */ int err2reason_phrase( int ser_error, int *sip_error, char *phrase, int etl, char *signature ); /*! \brief SIP error core -> SIP text */ char *error_text( int code ); /*! \brief return pkg_malloc-ed reply status in status->s */ void get_reply_status( str *status, struct sip_msg *reply, int code ); #endif opensips-2.2.2/etc/000077500000000000000000000000001300170765700141365ustar00rootroot00000000000000opensips-2.2.2/etc/dictionary.opensips000066400000000000000000000032121300170765700200630ustar00rootroot00000000000000# # $Id$ # # SIP RADIUS attributes # # Proprietary indicates an attribute that hasn't # been standardized # # # NOTE: All standard (IANA registered) attributes are # defined in the default dictionary of the # radiusclient-ng library. # #### Attributes ### ATTRIBUTE Sip-Uri-User 208 string # Proprietary, auth_radius ATTRIBUTE Sip-Group 211 string # Proprietary, group_radius ATTRIBUTE Sip-Rpid 213 string # Proprietary, auth_radius ATTRIBUTE SIP-AVP 225 string # Proprietary, avp_radius ATTRIBUTE Sip-Call-Duration 227 integer ATTRIBUTE Sip-Call-Setuptime 228 integer ATTRIBUTE Sip-Call-Created 229 integer ATTRIBUTE Sip-Call-MSDuration 230 integer ### Service-Type Values ### VALUE Service-Type Group-Check 12 # Proprietary, group_radius VALUE Service-Type SIP-Caller-AVPs 30 # Proprietary, avp_radius VALUE Service-Type SIP-Callee-AVPs 31 # Proprietary, avp_radius ### Sip-Method Values ### VALUE Sip-Method Undefined 0 VALUE Sip-Method Invite 1 VALUE Sip-Method Cancel 2 VALUE Sip-Method Ack 4 VALUE Sip-Method Bye 8 VALUE Sip-Method Info 16 VALUE Sip-Method Options 32 VALUE Sip-Method Update 64 VALUE Sip-Method Register 128 VALUE Sip-Method Message 256 VALUE Sip-Method Subscribe 512 VALUE Sip-Method Notify 1024 VALUE Sip-Method Prack 2048 VALUE Sip-Method Refer 4096 VALUE Sip-Method Other 8192 opensips-2.2.2/etc/opensips.cfg000066400000000000000000000132051300170765700164600ustar00rootroot00000000000000# # $Id$ # # OpenSIPS residential configuration script # by OpenSIPS Solutions # # This script was generated via "make menuconfig", from # the "Residential" scenario. # You can enable / disable more features / functionalities by # re-generating the scenario with different options.# # # Please refer to the Core CookBook at: # http://www.opensips.org/Resources/DocsCookbooks # for a explanation of possible statements, functions and parameters. # ####### Global Parameters ######### log_level=3 log_stderror=no log_facility=LOG_LOCAL0 children=4 /* uncomment the following line to enable debugging */ #debug_mode=yes /* uncomment the next line to enable the auto temporary blacklisting of not available destinations (default disabled) */ #disable_dns_blacklist=no /* uncomment the next line to enable IPv6 lookup after IPv4 dns lookup failures (default disabled) */ #dns_try_ipv6=yes /* comment the next line to enable the auto discovery of local aliases based on revers DNS on IPs */ auto_aliases=no listen=udp:127.0.0.1:5060 # CUSTOMIZE ME ####### Modules Section ######## #set module path mpath="/usr/local/lib/opensips/modules/" #### SIGNALING module loadmodule "signaling.so" #### StateLess module loadmodule "sl.so" #### Transaction Module loadmodule "tm.so" modparam("tm", "fr_timeout", 5) modparam("tm", "fr_inv_timeout", 30) modparam("tm", "restart_fr_on_each_reply", 0) modparam("tm", "onreply_avp_mode", 1) #### Record Route Module loadmodule "rr.so" /* do not append from tag to the RR (no need for this script) */ modparam("rr", "append_fromtag", 0) #### MAX ForWarD module loadmodule "maxfwd.so" #### SIP MSG OPerationS module loadmodule "sipmsgops.so" #### FIFO Management Interface loadmodule "mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("mi_fifo", "fifo_mode", 0666) #### URI module loadmodule "uri.so" modparam("uri", "use_uri_table", 0) #### USeR LOCation module loadmodule "usrloc.so" modparam("usrloc", "nat_bflag", "NAT") modparam("usrloc", "db_mode", 0) #### REGISTRAR module loadmodule "registrar.so" /* uncomment the next line not to allow more than 10 contacts per AOR */ #modparam("registrar", "max_contacts", 10) #### ACCounting module loadmodule "acc.so" /* what special events should be accounted ? */ modparam("acc", "early_media", 0) modparam("acc", "report_cancels", 0) /* by default we do not adjust the direct of the sequential requests. if you enable this parameter, be sure the enable "append_fromtag" in "rr" module */ modparam("acc", "detect_direction", 0) #### UDP protocol loadmodule "proto_udp.so" ####### Routing Logic ######## # main request routing logic route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (has_totag()) { # sequential requests within a dialog should # take the path determined by record-routing if (loose_route()) { if (is_method("BYE")) { # do accunting, even if the transaction fails do_accounting("log","failed"); } else if (is_method("INVITE")) { # even if in most of the cases is useless, do RR for # re-INVITEs alos, as some buggy clients do change route set # during the dialog. record_route(); } # route it out to whatever destination was set by loose_route() # in $du (destination URI). route(relay); } else { if ( is_method("ACK") ) { if ( t_check_trans() ) { # non loose-route, but stateful ACK; must be an ACK after # a 487 or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction -> # ignore and discard exit; } } sl_send_reply("404","Not here"); } exit; } # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); if ( !(is_method("REGISTER") ) ) { if (from_uri==myself) { } else { # if caller is not local, then called number must be local if (!uri==myself) { send_reply("403","Rely forbidden"); exit; } } } # preloaded route checking if (loose_route()) { xlog("L_ERR", "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]"); if (!is_method("ACK")) sl_send_reply("403","Preload Route denied"); exit; } # record routing if (!is_method("REGISTER|MESSAGE")) record_route(); # account only INVITEs if (is_method("INVITE")) { do_accounting("log"); } if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(relay); } # requests for my domain if (is_method("PUBLISH|SUBSCRIBE")) { sl_send_reply("503", "Service Unavailable"); exit; } if (is_method("REGISTER")) { if (!save("location")) sl_reply_error(); exit; } if ($rU==NULL) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } # do lookup with method filtering if (!lookup("location","m")) { t_newtran(); t_reply("404", "Not Found"); exit; } # when routing via usrloc, log the missed calls also do_accounting("log","missed"); route(relay); } route[relay] { # for INVITEs enable some additional helper routes if (is_method("INVITE")) { t_on_branch("per_branch_ops"); t_on_reply("handle_nat"); t_on_failure("missed_call"); } if (!t_relay()) { send_reply("500","Internal Error"); }; exit; } branch_route[per_branch_ops] { xlog("new branch at $ru\n"); } onreply_route[handle_nat] { xlog("incoming reply\n"); } failure_route[missed_call] { if (t_was_cancelled()) { exit; } # uncomment the following lines if you want to block client # redirect based on 3xx replies. ##if (t_check_status("3[0-9][0-9]")) { ##t_reply("404","Not found"); ## exit; ##} } opensips-2.2.2/etc/tls/000077500000000000000000000000001300170765700147405ustar00rootroot00000000000000opensips-2.2.2/etc/tls/README000066400000000000000000000020301300170765700156130ustar00rootroot00000000000000This directory contains an already generated TLS certificate that can be used in your OpenSIPS configuration. It is a generic certificate with the main purpose of serving both as an example and a testing sample. IMPORTANT: it is not a trustable certificate - the CA is also an example. All TLS configuration files may be found in the "user" directory. If you want to generate your own certificate, a CA you may sign your request with can be found in the "rootCA" directory. Use the "opensipsctl tls userCERT" command to create a new certificate; the rootCA passphrase is "opensips". What is the purpose of the default CA and certificate? Firstly, to make an out-of-the box TLS configuration for users not so familiar with SSL/TLS. Secondly, to give access to the same CA root to a large community in order to encourage testings and interconnections via TLS with a minimum of troubles. If you have any questions, please address them to: team@opensips.org (if you want to keep your question private) users@opensips.org (public mailing list) opensips-2.2.2/etc/tls/ca.conf000066400000000000000000000040011300170765700161650ustar00rootroot00000000000000# # Default configuration to use when one # is not provided on the command line. # [ ca ] default_ca = local_ca # # Default location of directories and # files needed to generate certificates. # [ local_ca ] dir = ./rootCA certificate = $dir/cacert.pem database = $dir/index.txt new_certs_dir = $dir/certs private_key = $dir/private/cakey.pem serial = $dir/serial # # Default expiration and encryption # policies for certificates. # default_crl_days = 365 default_days = 1825 default_md = sha1 policy = local_ca_policy x509_extensions = local_ca_extensions # # Default policy to use when generating # server certificates. The following # fields must be defined in the server # certificate. # [ local_ca_policy ] commonName = supplied stateOrProvinceName = supplied countryName = supplied emailAddress = supplied organizationName = supplied organizationalUnitName = supplied # # x509 extensions to use when generating # server certificates. # [ local_ca_extensions ] #subjectAltName = DNS:altname.somewhere.com basicConstraints = CA:false nsCertType = server # # The default policy to use when # generating the root certificate. # [ req ] default_bits = 2048 default_keyfile = ./private/cakey.pem default_md = sha1 prompt = no distinguished_name = root_ca_distinguished_name x509_extensions = root_ca_extensions # # Root Certificate Authority distin- # guished name. Changes these fields to # your local environment. # [ root_ca_distinguished_name ] commonName = Your_NAME # please update stateOrProvinceName = Your_STATE # please update countryName = CO # please update emailAddress = YOUR_EMAIL # please update organizationName = YOUR_ORG_NAME # please update [ root_ca_extensions ] basicConstraints = CA:true subjectAltName = email:copy issuerAltName = issuer:copy opensips-2.2.2/etc/tls/request.conf000066400000000000000000000021471300170765700173030ustar00rootroot00000000000000# # Default configuration to use when one # is not provided on the command line. # [ ca ] default_ca = CA_request # # Default location of directories and # files needed to generate certificates. # [ CA_request ] dir = ./rootCA database = $dir/index.txt new_certs_dir = $dir/certs certificate = $dir/cacert.pem serial = $dir/serial private_key = $dir/private/cakey.pem # # Default expiration and encryption # policies for certificates. # default_days = 365 default_crl_days = 1825 default_md = sha1 policy = req_policy # # Information to be moved from # request to the certificate # nameopt = ca_default certopt = ca_default copy_extensions = copy x509_extensions = cert_extensions # # The default policy to use when # generating the certificate. # [ req_policy ] countryName = supplied stateOrProvinceName = optional organizationName = supplied organizationalUnitName = optional commonName = supplied emailAddress = supplied [ cert_extensions ] basicConstraints = CA:false opensips-2.2.2/etc/tls/rootCA/000077500000000000000000000000001300170765700161275ustar00rootroot00000000000000opensips-2.2.2/etc/tls/rootCA/cacert.pem000066400000000000000000000024721300170765700201000ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIJAOCJVoQZAHIyMA0GCSqGSIb3DQEBBQUAMHAxETAPBgNV BAMTCE9wZW5TSVBTMRUwEwYDVQQIEwxvcGVuc2lwcy5vcmcxCzAJBgNVBAYTAklQ MSAwHgYJKoZIhvcNAQkBFhF0ZWFtQG9wZW5zaXBzLm9yZzEVMBMGA1UEChMMb3Bl bnNpcHMub3JnMB4XDTE1MDMxNzEzMDcyMloXDTE2MDMxNjEzMDcyMlowcDERMA8G A1UEAxMIT3BlblNJUFMxFTATBgNVBAgTDG9wZW5zaXBzLm9yZzELMAkGA1UEBhMC SVAxIDAeBgkqhkiG9w0BCQEWEXRlYW1Ab3BlbnNpcHMub3JnMRUwEwYDVQQKEwxv cGVuc2lwcy5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDi45z5 dor6c5I0B+8EBrpwS9/pq4EvCMzotiJ7NRdvjXmI3cq5kEgsHVJEfAR3Sgto0hnc ijoAMCPeZGV3Kqs67JHOE30fOeRrcM2r4QIIg4bmdhGkWzW1l7OY1HAj73rcDi+D U8tzXz6QF+P9yWkatAU1vfvAgXJDCfjwGOW3RWW1lejLBt/pl1HqIsoSTO5aB1db 8Z/Txxgy87zJVYye3MWFhczGlPayZE7fkKmeup9n0seVY9Vw2RoTvYqIjUKGqQbC pQ+Me5peY92Q2RUDNaKwvq2bm/0DrNLDlYLyyBRpG97eWfMnGjTejXsTyRUGsxrW FY26T4r9PTqaOqPzAgMBAAGjTDBKMAwGA1UdEwQFMAMBAf8wHAYDVR0RBBUwE4ER dGVhbUBvcGVuc2lwcy5vcmcwHAYDVR0SBBUwE4ERdGVhbUBvcGVuc2lwcy5vcmcw DQYJKoZIhvcNAQEFBQADggEBABgMT5/7TmOea/am7BLLiDZZZWgrnjPoVd3BKPRc YW9Vw9z23iyI2HSLfekDdwGw58pA38CcsCI+mUSTcX7y9iL2MHayXHQ1T0vWS82U /XqaDapnOSzHP19jsCvc/yBBbrPsxRKjFEO3KjgBzTNqWjblAQmJHfuvxVgxHVnU xU1UfUeLlL32p5S1tTlsFTRknyKtqShYEytFmtHFV09cqSQDeUyTk+mQ10RV5UgA Iy+fQRNe4MYAOktp6aa++Y3rsEB9/Wmmpmev7WOWZZvD0MTwQ/mtj+hJby6mrwCs TL+InrlO6h6/qbf/9SYtN49/9QUuCZVVC25HnE2kie+85Jk= -----END CERTIFICATE----- opensips-2.2.2/etc/tls/rootCA/certs/000077500000000000000000000000001300170765700172475ustar00rootroot00000000000000opensips-2.2.2/etc/tls/rootCA/certs/01.pem000066400000000000000000000057171300170765700202040ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: CN=OpenSIPS, ST=opensips.org, C=IP/emailAddress=team@opensips.org, O=opensips.org Validity Not Before: Mar 17 13:07:36 2015 GMT Not After : Mar 16 13:07:36 2016 GMT Subject: C=IP, ST=opensips.org, O=OpenSIPS project, OU=OpenSIPS TLS tester, CN=OpenSIPS/emailAddress=team@opensips.org Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (512 bit) Modulus: 00:d4:84:7c:1c:b9:bd:36:4a:95:e2:cc:36:32:3a: 49:54:31:a9:57:6f:b1:a0:f4:2a:59:79:f0:76:ce: d8:2b:b0:80:50:72:94:1d:0a:c5:a9:b2:13:ee:a6: f3:f4:9e:0d:36:67:cb:e9:dc:30:45:66:5f:cb:9a: 3e:a2:82:27:03 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Signature Algorithm: sha1WithRSAEncryption d1:d6:29:eb:6b:e0:8f:92:7d:1c:de:a1:16:c0:9b:cf:bc:ad: 19:73:39:8c:e1:07:99:85:26:08:e2:7d:a3:ac:e3:a1:e0:23: 27:14:ee:37:44:2e:a3:27:2a:16:c6:1f:ff:34:22:4d:47:13: 7d:10:f4:df:0b:06:b7:99:9f:ae:dc:16:e7:b6:0e:6e:36:54: 82:19:a1:c9:92:4f:5c:1d:65:d6:7b:08:31:95:d7:9f:5e:2a: 8b:26:b7:dd:42:ba:10:c4:97:ba:8f:47:c3:99:3f:34:2d:bf: 06:eb:bf:10:94:50:72:de:80:fa:fb:4e:03:8b:8d:00:e9:5b: 8c:6f:25:95:7d:68:4d:ba:11:55:86:6d:4b:9e:1a:29:17:27: cf:a2:25:eb:4c:21:37:2f:7c:e9:c6:bc:fc:c4:7c:61:30:06: 78:33:ee:d0:a7:d8:49:7c:4e:f9:a8:be:32:8f:08:86:3e:b0: 6a:3d:a0:4e:a1:01:21:7b:68:50:98:a9:18:a5:10:19:bf:99: bd:c5:ee:0e:6e:f3:06:e4:1c:01:39:4d:c5:8d:35:82:c5:4f: cc:4b:c3:f2:82:15:7d:b0:59:f7:cd:85:24:b4:77:1a:78:97: 08:6f:8a:f4:91:96:cb:5d:68:01:60:82:97:06:a1:fb:39:1b: 3b:a5:7b:4d -----BEGIN CERTIFICATE----- MIICwzCCAaugAwIBAgIBATANBgkqhkiG9w0BAQUFADBwMREwDwYDVQQDEwhPcGVu U0lQUzEVMBMGA1UECBMMb3BlbnNpcHMub3JnMQswCQYDVQQGEwJJUDEgMB4GCSqG SIb3DQEJARYRdGVhbUBvcGVuc2lwcy5vcmcxFTATBgNVBAoTDG9wZW5zaXBzLm9y ZzAeFw0xNTAzMTcxMzA3MzZaFw0xNjAzMTYxMzA3MzZaMIGSMQswCQYDVQQGEwJJ UDEVMBMGA1UECBMMb3BlbnNpcHMub3JnMRkwFwYDVQQKExBPcGVuU0lQUyBwcm9q ZWN0MRwwGgYDVQQLExNPcGVuU0lQUyBUTFMgdGVzdGVyMREwDwYDVQQDEwhPcGVu U0lQUzEgMB4GCSqGSIb3DQEJARYRdGVhbUBvcGVuc2lwcy5vcmcwXDANBgkqhkiG 9w0BAQEFAANLADBIAkEA1IR8HLm9NkqV4sw2MjpJVDGpV2+xoPQqWXnwds7YK7CA UHKUHQrFqbIT7qbz9J4NNmfL6dwwRWZfy5o+ooInAwIDAQABow0wCzAJBgNVHRME AjAAMA0GCSqGSIb3DQEBBQUAA4IBAQDR1inra+CPkn0c3qEWwJvPvK0ZczmM4QeZ hSYI4n2jrOOh4CMnFO43RC6jJyoWxh//NCJNRxN9EPTfCwa3mZ+u3Bbntg5uNlSC GaHJkk9cHWXWewgxldefXiqLJrfdQroQxJe6j0fDmT80Lb8G678QlFBy3oD6+04D i40A6VuMbyWVfWhNuhFVhm1LnhopFyfPoiXrTCE3L3zpxrz8xHxhMAZ4M+7Qp9hJ fE75qL4yjwiGPrBqPaBOoQEhe2hQmKkYpRAZv5m9xe4ObvMG5BwBOU3FjTWCxU/M S8PyghV9sFn3zYUktHcaeJcIb4r0kZbLXWgBYIKXBqH7ORs7pXtN -----END CERTIFICATE----- opensips-2.2.2/etc/tls/rootCA/index.txt000066400000000000000000000002071300170765700177760ustar00rootroot00000000000000V 160316130736Z 01 unknown /C=IP/ST=opensips.org/O=OpenSIPS project/OU=OpenSIPS TLS tester/CN=OpenSIPS/emailAddress=team@opensips.org opensips-2.2.2/etc/tls/rootCA/private/000077500000000000000000000000001300170765700176015ustar00rootroot00000000000000opensips-2.2.2/etc/tls/rootCA/private/cakey.pem000066400000000000000000000032171300170765700214030ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA4uOc+XaK+nOSNAfvBAa6cEvf6auBLwjM6LYiezUXb415iN3K uZBILB1SRHwEd0oLaNIZ3Io6ADAj3mRldyqrOuyRzhN9Hznka3DNq+ECCIOG5nYR pFs1tZezmNRwI+963A4vg1PLc18+kBfj/clpGrQFNb37wIFyQwn48Bjlt0VltZXo ywbf6ZdR6iLKEkzuWgdXW/Gf08cYMvO8yVWMntzFhYXMxpT2smRO35CpnrqfZ9LH lWPVcNkaE72KiI1ChqkGwqUPjHuaXmPdkNkVAzWisL6tm5v9A6zSw5WC8sgUaRve 3lnzJxo03o17E8kVBrMa1hWNuk+K/T06mjqj8wIDAQABAoIBAQDFTmsUBaKWaiqa VMvmFHPNdC4kZqDQ5jZLqRgTTPg0ajNmcnn5JR+ePWFtbOv1UM7bONqEjzAhObv3 ihcyqkh91Xx2WTxWC2I4jMBO2D2MadTGVabB5w+t+16TaSqcFYYiF7P54MTsb8dY R25dJOnh7usY8T4dCnTzF5kEMqKR3IPRY5MNXoX4ittuBgLXMfY7MTtNMn+dSW6A b7D9NSESLeltggpC/smOVyQ6rIwQMdHP9ArRTzz1oHwoDGm5WbePAwABlGDE7pbu I44RXZ1xCSDWw0hEouOvZOcWN+89+WRsLrE6aQ4gH5mgWvu/UhfKhvjJIKaxbGOt bD4u2isBAoGBAP+ZDNHaCH89Rq4yEccdgdN/AvUwGmP6QltaYOj2vyleplZGtXn0 kF4a6VF6nm8Re0ZbqDmw4YiY5DSc/7Kr0veg96VqjB+fnr7FLfJ9ua+Ls8gniK4z 39hukeUFmUi5Yk04Mg0vs0Mmko6uKhmV0yqH8UDRir0gtMNhHM8LAlWVAoGBAOM+ //E0nbKlDWxpSm58Y9bzkCQVNjAiyPYCjbfhE+FHC3nnFMfNO+EfBgvU/NXOFV3t PGbFfOrvZcuyNhLE/sfEg3W8DGvb1R1NV0hpUFhSGv/ooqtINLPzynoyj/eTDV4X rE9p01ol+/ZlVeMG4dUX2Gdik8yjSw2jh5KtuiFnAoGBAPUJWGXyngTFzPYBIFQA sM7+y+odhJ9mc6k6uv98VoWlrNe3kBFr6d+d8yJe6HbYjpNEGcG9FmqrENRz0xsK aQVndyNJQy6qCzketc7sw1BNITz40asYJjhKwGmMdYhn2ZroDevgV/4/g29v/wJq RXhXvW0Dtftc2I8akdDX4BQFAoGAP9l9tFoiTQWPJfzXiv+sVcMNpvXGyfOcH3NM OnQlT9DUaqYA8rq5rmE3I6/ucmx7GG8F36JxnNky8u7ZvqBXLe4LOdNichPMZ3VJ pvDgQFChgXHt8s5Q8Jb5NyhpxxgsU1m3coC7f8LYbry+SxGt9l/C0BmkiYUAG47t Est5VtUCgYEA7VRmzXQ7/PaJ8jxou2ODAJ/NLURgv3j5XkVvYUqEXMkViqQAhaTr RdxvM2G+YYt8MYT3qCNwjX/uT5ePw0PAh6ceaztbHrp5rUXzhc9EmT1e275KsykQ sp4s5TjNf5j5n97DW0oj1btmq85gb3KEROqMoKVNeDtoMnIQVsFZpAA= -----END RSA PRIVATE KEY----- opensips-2.2.2/etc/tls/rootCA/serial000066400000000000000000000000031300170765700173220ustar00rootroot0000000000000002 opensips-2.2.2/etc/tls/user.conf000066400000000000000000000011171300170765700165650ustar00rootroot00000000000000# # LocalServer.conf # [ req ] prompt = no distinguished_name = server_distinguished_name [ server_distinguished_name ] commonName = somename.somewhere.com # please update stateOrProvinceName = Some State # please update countryName = XY # please update emailAddress = root@somename.somewhere.com # please update organizationName = My Large Organization Name # please update organizationalUnitName = My Subunit of Large Organization # please update opensips-2.2.2/etc/tls/user/000077500000000000000000000000001300170765700157165ustar00rootroot00000000000000opensips-2.2.2/etc/tls/user/user-calist.pem000066400000000000000000000024721300170765700206610ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIJAOCJVoQZAHIyMA0GCSqGSIb3DQEBBQUAMHAxETAPBgNV BAMTCE9wZW5TSVBTMRUwEwYDVQQIEwxvcGVuc2lwcy5vcmcxCzAJBgNVBAYTAklQ MSAwHgYJKoZIhvcNAQkBFhF0ZWFtQG9wZW5zaXBzLm9yZzEVMBMGA1UEChMMb3Bl bnNpcHMub3JnMB4XDTE1MDMxNzEzMDcyMloXDTE2MDMxNjEzMDcyMlowcDERMA8G A1UEAxMIT3BlblNJUFMxFTATBgNVBAgTDG9wZW5zaXBzLm9yZzELMAkGA1UEBhMC SVAxIDAeBgkqhkiG9w0BCQEWEXRlYW1Ab3BlbnNpcHMub3JnMRUwEwYDVQQKEwxv cGVuc2lwcy5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDi45z5 dor6c5I0B+8EBrpwS9/pq4EvCMzotiJ7NRdvjXmI3cq5kEgsHVJEfAR3Sgto0hnc ijoAMCPeZGV3Kqs67JHOE30fOeRrcM2r4QIIg4bmdhGkWzW1l7OY1HAj73rcDi+D U8tzXz6QF+P9yWkatAU1vfvAgXJDCfjwGOW3RWW1lejLBt/pl1HqIsoSTO5aB1db 8Z/Txxgy87zJVYye3MWFhczGlPayZE7fkKmeup9n0seVY9Vw2RoTvYqIjUKGqQbC pQ+Me5peY92Q2RUDNaKwvq2bm/0DrNLDlYLyyBRpG97eWfMnGjTejXsTyRUGsxrW FY26T4r9PTqaOqPzAgMBAAGjTDBKMAwGA1UdEwQFMAMBAf8wHAYDVR0RBBUwE4ER dGVhbUBvcGVuc2lwcy5vcmcwHAYDVR0SBBUwE4ERdGVhbUBvcGVuc2lwcy5vcmcw DQYJKoZIhvcNAQEFBQADggEBABgMT5/7TmOea/am7BLLiDZZZWgrnjPoVd3BKPRc YW9Vw9z23iyI2HSLfekDdwGw58pA38CcsCI+mUSTcX7y9iL2MHayXHQ1T0vWS82U /XqaDapnOSzHP19jsCvc/yBBbrPsxRKjFEO3KjgBzTNqWjblAQmJHfuvxVgxHVnU xU1UfUeLlL32p5S1tTlsFTRknyKtqShYEytFmtHFV09cqSQDeUyTk+mQ10RV5UgA Iy+fQRNe4MYAOktp6aa++Y3rsEB9/Wmmpmev7WOWZZvD0MTwQ/mtj+hJby6mrwCs TL+InrlO6h6/qbf/9SYtN49/9QUuCZVVC25HnE2kie+85Jk= -----END CERTIFICATE----- opensips-2.2.2/etc/tls/user/user-cert.pem000066400000000000000000000057171300170765700203440ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: CN=OpenSIPS, ST=opensips.org, C=IP/emailAddress=team@opensips.org, O=opensips.org Validity Not Before: Mar 17 13:07:36 2015 GMT Not After : Mar 16 13:07:36 2016 GMT Subject: C=IP, ST=opensips.org, O=OpenSIPS project, OU=OpenSIPS TLS tester, CN=OpenSIPS/emailAddress=team@opensips.org Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (512 bit) Modulus: 00:d4:84:7c:1c:b9:bd:36:4a:95:e2:cc:36:32:3a: 49:54:31:a9:57:6f:b1:a0:f4:2a:59:79:f0:76:ce: d8:2b:b0:80:50:72:94:1d:0a:c5:a9:b2:13:ee:a6: f3:f4:9e:0d:36:67:cb:e9:dc:30:45:66:5f:cb:9a: 3e:a2:82:27:03 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Signature Algorithm: sha1WithRSAEncryption d1:d6:29:eb:6b:e0:8f:92:7d:1c:de:a1:16:c0:9b:cf:bc:ad: 19:73:39:8c:e1:07:99:85:26:08:e2:7d:a3:ac:e3:a1:e0:23: 27:14:ee:37:44:2e:a3:27:2a:16:c6:1f:ff:34:22:4d:47:13: 7d:10:f4:df:0b:06:b7:99:9f:ae:dc:16:e7:b6:0e:6e:36:54: 82:19:a1:c9:92:4f:5c:1d:65:d6:7b:08:31:95:d7:9f:5e:2a: 8b:26:b7:dd:42:ba:10:c4:97:ba:8f:47:c3:99:3f:34:2d:bf: 06:eb:bf:10:94:50:72:de:80:fa:fb:4e:03:8b:8d:00:e9:5b: 8c:6f:25:95:7d:68:4d:ba:11:55:86:6d:4b:9e:1a:29:17:27: cf:a2:25:eb:4c:21:37:2f:7c:e9:c6:bc:fc:c4:7c:61:30:06: 78:33:ee:d0:a7:d8:49:7c:4e:f9:a8:be:32:8f:08:86:3e:b0: 6a:3d:a0:4e:a1:01:21:7b:68:50:98:a9:18:a5:10:19:bf:99: bd:c5:ee:0e:6e:f3:06:e4:1c:01:39:4d:c5:8d:35:82:c5:4f: cc:4b:c3:f2:82:15:7d:b0:59:f7:cd:85:24:b4:77:1a:78:97: 08:6f:8a:f4:91:96:cb:5d:68:01:60:82:97:06:a1:fb:39:1b: 3b:a5:7b:4d -----BEGIN CERTIFICATE----- MIICwzCCAaugAwIBAgIBATANBgkqhkiG9w0BAQUFADBwMREwDwYDVQQDEwhPcGVu U0lQUzEVMBMGA1UECBMMb3BlbnNpcHMub3JnMQswCQYDVQQGEwJJUDEgMB4GCSqG SIb3DQEJARYRdGVhbUBvcGVuc2lwcy5vcmcxFTATBgNVBAoTDG9wZW5zaXBzLm9y ZzAeFw0xNTAzMTcxMzA3MzZaFw0xNjAzMTYxMzA3MzZaMIGSMQswCQYDVQQGEwJJ UDEVMBMGA1UECBMMb3BlbnNpcHMub3JnMRkwFwYDVQQKExBPcGVuU0lQUyBwcm9q ZWN0MRwwGgYDVQQLExNPcGVuU0lQUyBUTFMgdGVzdGVyMREwDwYDVQQDEwhPcGVu U0lQUzEgMB4GCSqGSIb3DQEJARYRdGVhbUBvcGVuc2lwcy5vcmcwXDANBgkqhkiG 9w0BAQEFAANLADBIAkEA1IR8HLm9NkqV4sw2MjpJVDGpV2+xoPQqWXnwds7YK7CA UHKUHQrFqbIT7qbz9J4NNmfL6dwwRWZfy5o+ooInAwIDAQABow0wCzAJBgNVHRME AjAAMA0GCSqGSIb3DQEBBQUAA4IBAQDR1inra+CPkn0c3qEWwJvPvK0ZczmM4QeZ hSYI4n2jrOOh4CMnFO43RC6jJyoWxh//NCJNRxN9EPTfCwa3mZ+u3Bbntg5uNlSC GaHJkk9cHWXWewgxldefXiqLJrfdQroQxJe6j0fDmT80Lb8G678QlFBy3oD6+04D i40A6VuMbyWVfWhNuhFVhm1LnhopFyfPoiXrTCE3L3zpxrz8xHxhMAZ4M+7Qp9hJ fE75qL4yjwiGPrBqPaBOoQEhe2hQmKkYpRAZv5m9xe4ObvMG5BwBOU3FjTWCxU/M S8PyghV9sFn3zYUktHcaeJcIb4r0kZbLXWgBYIKXBqH7ORs7pXtN -----END CERTIFICATE----- opensips-2.2.2/etc/tls/user/user-cert_req.pem000066400000000000000000000010221300170765700211740ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBTTCB+AIBADCBkjERMA8GA1UEAxMIT3BlblNJUFMxFTATBgNVBAgTDG9wZW5z aXBzLm9yZzELMAkGA1UEBhMCSVAxIDAeBgkqhkiG9w0BCQEWEXRlYW1Ab3BlbnNp cHMub3JnMRkwFwYDVQQKExBPcGVuU0lQUyBwcm9qZWN0MRwwGgYDVQQLExNPcGVu U0lQUyBUTFMgdGVzdGVyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANSEfBy5vTZK leLMNjI6SVQxqVdvsaD0Kll58HbO2CuwgFBylB0KxamyE+6m8/SeDTZny+ncMEVm X8uaPqKCJwMCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA0EADtLxHAJMLCuO+cQOVCSH UE9Pe24nqpZcNkLaVrmNAVZ4VfDwWaRhWgwO4aUR1Y/Mqc5E6hwib4/WVnMxT8PZ QQ== -----END CERTIFICATE REQUEST----- opensips-2.2.2/etc/tls/user/user-privkey.pem000066400000000000000000000010161300170765700210640ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA1IR8HLm9NkqV4sw2 MjpJVDGpV2+xoPQqWXnwds7YK7CAUHKUHQrFqbIT7qbz9J4NNmfL6dwwRWZfy5o+ ooInAwIDAQABAkBaulDuRpGz+vWwhLRijyW9LxJ3xLSV3Yey+W8imb4KMCFDuIT2 WAS04nH0zI9DGxGlC2wcs6ZBO7K5XrANsJmJAiEA/h4nRA0Xjj0c0Y3V1SIlhY23 FlJsgUJ90+1YiyC9gQUCIQDWF3NtkkdiYgqKtrEBG19s5R1JogP5Oc6gJwn1DVKm ZwIhAOgYAEL+NiJ6nKSkh8YHTv/hBHTFyUZxxVzXrYQ83fhFAiEAv/t9jedbzhSe 4W8cYSK7urnmPo5jw80bxi/b1PzifccCIQDiz44ICrjUfNZU3/Ct96unxEWps7+D IEcF+TC+YhL0kA== -----END PRIVATE KEY----- opensips-2.2.2/evi/000077500000000000000000000000001300170765700141465ustar00rootroot00000000000000opensips-2.2.2/evi/event_interface.c000066400000000000000000000410331300170765700174540ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #include "event_interface.h" #include "evi_modules.h" #include "../mem/shm_mem.h" #include "../mi/mi.h" #include "../pvar.h" #include "../timer.h" #include "../ut.h" int events_no = 0; int max_alloc_events = 10; static int events_rec_level = MAX_REC_LEV; /* holds all exported events */ evi_event_t *events = NULL; event_id_t evi_publish_event(str event_name) { int idx; if (event_name.len > MAX_EVENT_NAME) { LM_ERR("event name too long [%d>%d]\n", event_name.len, MAX_EVENT_NAME); return EVI_ERROR; } idx = evi_get_id(&event_name); if (idx != EVI_ERROR) { LM_WARN("Event \"%.*s\" was previously published\n", event_name.len, event_name.s); return idx; } /* check if the event was already registered */ if (!events) { /* first event */ events = shm_malloc(max_alloc_events * sizeof(evi_event_t)); if (!events) { LM_ERR("no more shm memory to hold %d events\n", max_alloc_events); return EVI_ERROR; } } else if (events_no == max_alloc_events) { max_alloc_events *= 2; events = shm_realloc(events, max_alloc_events * sizeof(evi_event_t)); if (!events) { LM_ERR("no more shm memory to hold %d events\n", max_alloc_events); return EVI_ERROR; } } events[events_no].lock = lock_alloc(); if (!events[events_no].lock) { LM_ERR("Failed to allocate subscribers lock\n"); return EVI_ERROR; } events[events_no].lock = lock_init(events[events_no].lock); if (!events[events_no].lock) { LM_ERR("Failed to create subscribers lock\n"); return EVI_ERROR; } events[events_no].id = events_no; events[events_no].name.s = event_name.s; events[events_no].name.len = event_name.len; events[events_no].subscribers = NULL; LM_INFO("Registered event <%.*s(%d)>\n", event_name.len, event_name.s, events_no); return events_no++; } int evi_raise_event(event_id_t id, evi_params_t* params) { int status; struct sip_msg* req= NULL; struct usr_avp *event_avps = 0; struct usr_avp **bak_avps = 0; /* * because these might be nested, a different message has * to be generated each time */ req = (struct sip_msg*)pkg_malloc(sizeof(struct sip_msg)); if(req == NULL) { LM_ERR("No more memory\n"); return -1; } memset(req, 0, sizeof(struct sip_msg)); req->first_line.type = SIP_REQUEST; req->first_line.u.request.method.s= "DUMMY"; req->first_line.u.request.method.len= 5; req->first_line.u.request.uri.s= "sip:user@domain.com"; req->first_line.u.request.uri.len= 19; req->rcv.src_ip.af = AF_INET; req->rcv.dst_ip.af = AF_INET; bak_avps = set_avp_list(&event_avps); status = evi_raise_event_msg(req, id, params); /* clean whatever extra structures were added by script functions */ free_sip_msg(req); pkg_free(req); /* remove all avps added */ destroy_avp_list(&event_avps); set_avp_list(bak_avps); return status; } int evi_raise_event_msg(struct sip_msg *msg, event_id_t id, evi_params_t* params) { evi_subs_p subs, prev; long now; int flags, pflags = 0; int ret = 0; if (id < 0 || id >= events_no) { LM_ERR("invalid event %d\n", id); return -1; } if (events_rec_level == 0) { LM_ERR("Too many nested events %d\n", MAX_REC_LEV); return -1; } events_rec_level--; if (params) pflags = params->flags; lock_get(events[id].lock); now = time(0); subs = events[id].subscribers; prev = NULL; while (subs) { if (!subs->reply_sock) { LM_ERR("unknown destination\n"); continue; } /* check expire */ if (!(subs->reply_sock->flags & EVI_PENDING) && subs->reply_sock->flags & EVI_EXPIRE && subs->reply_sock->subscription_time + subs->reply_sock->expire < now) { if (subs->trans_mod && subs->trans_mod->free) subs->trans_mod->free(subs->reply_sock); else shm_free(subs->reply_sock); if (!prev) { events[id].subscribers = subs->next; shm_free(subs); subs = events[id].subscribers; } else { prev->next = subs->next; shm_free(subs); subs = prev->next; } continue; } if (!subs->trans_mod) { LM_ERR("unknown transfer protocol\n"); goto next; } LM_DBG("found subscriber %.*s\n", subs->reply_sock->address.len, subs->reply_sock->address.s); if (!subs->trans_mod->raise) { LM_ERR("\"%.*s\" protocol cannot raise events\n", subs->trans_mod->proto.len, subs->trans_mod->proto.s); goto next; } /* we use this var to make sure nested calls don't reset the flag */ flags = subs->reply_sock->flags; subs->reply_sock->flags |= EVI_PENDING; /* make sure nested events don't deadlock */ lock_release(events[id].lock); ret += (subs->trans_mod->raise)(msg, &events[id].name, subs->reply_sock, params); lock_get(events[id].lock); subs->reply_sock->flags = flags; next: prev = subs; subs = subs->next; } lock_release(events[id].lock); /* done sending events - free parameters */ if (params) { /* make sure no one is messing with our flags */ params->flags = pflags; if (params->flags & EVI_FREE_LIST) evi_free_params(params); } events_rec_level++; return ret; } int evi_probe_event(event_id_t id) { if (id < 0 || id >= events_no) { LM_ERR("invalid event %d\n", id); return -1; } /* check for subscribers */ if (!events[id].subscribers) return 0; /* returns the number of transport module loaded */ return get_trans_mod_no(); } /* returns the id of an event */ event_id_t evi_get_id(str *name) { int i; for (i = 0; i < events_no; i++) if (events[i].name.len == name->len && !memcmp(events[i].name.s, name->s, name->len)) return i; return EVI_ERROR; } /* returns an event id */ evi_event_p evi_get_event(str *name) { event_id_t id = evi_get_id(name); return id == EVI_ERROR ? NULL : &events[id]; } /* * Subscribes an event * Returns: * 1 - success * 0 - internal error * -1 - param error */ int evi_event_subscribe(str event_name, str sock_str, unsigned expire, unsigned unsubscribe) { evi_subs_t *subscriber = NULL; evi_event_p event; evi_export_t *trans_mod = NULL; evi_reply_sock *sock; event = evi_get_event(&event_name); if (!event) { LM_ERR("invalid event name <%.*s>\n", event_name.len, event_name.s); goto bad_param; } /* transport module name */ trans_mod = get_trans_mod(&sock_str); if (!trans_mod) { LM_ERR("couldn't find a protocol to support %.*s\n", sock_str.len, sock_str.s); goto bad_param; } sock_str.s += trans_mod->proto.len + 1; sock_str.len -= (trans_mod->proto.len + 1); /* parse reply socket */ sock = trans_mod->parse(sock_str); if (!sock) goto bad_param; /* reset unrequired flags */ if (!expire && !unsubscribe) sock->flags &= ~EVI_EXPIRE; /* tries to match other socket */ if (trans_mod->match) { lock_get(event->lock); for (subscriber = event->subscribers; subscriber; subscriber = subscriber->next) { if (subscriber->trans_mod != trans_mod) continue; if (trans_mod->match(sock, subscriber->reply_sock)) { /* update subscription time */ subscriber->reply_sock->subscription_time = time(0); /* update expire if required */ if (EVI_EXPIRE & sock->flags) { subscriber->reply_sock->expire = expire; subscriber->reply_sock->flags = sock->flags; } if (trans_mod->free) trans_mod->free(sock); else shm_free(sock); break; } } lock_release(event->lock); } /* if no socket matches - create a new one */ if (!subscriber) { subscriber = shm_malloc(sizeof(evi_subs_t)); if (!subscriber) { LM_ERR("no more shm memory\n"); if (trans_mod && sock) { /* if the module has it's own free function */ if (trans_mod->free) trans_mod->free(sock); else shm_free(sock); } return 0; } sock->subscription_time = time(0); subscriber->trans_mod = trans_mod; subscriber->reply_sock = sock; if (EVI_EXPIRE & sock->flags) subscriber->reply_sock->expire = expire; subscriber->reply_sock->flags |= trans_mod->flags; /* guard subscribers list */ lock_get(event->lock); subscriber->next = event->subscribers; event->subscribers = subscriber; lock_release(event->lock); LM_DBG("added new subscriber for event %d\n", event->id); } return 1; bad_param: return -1; } int evi_raise_script_event(struct sip_msg *msg, event_id_t id, void * _a, void * _v) { pv_spec_p vals = (pv_spec_p)_v; pv_spec_p attrs = (pv_spec_p)_a; struct usr_avp *v_avp = NULL; struct usr_avp *a_avp = NULL; int err = evi_probe_event(id); int_str val, attr; str *at; evi_params_p params = NULL; if (err < 0) return err; else if (!err) return 1; if (!vals) goto raise; if (!(params = evi_get_params())) { LM_ERR("cannot create parameters list\n"); goto raise; } /* handle parameters */ while ((v_avp = search_first_avp(vals->pvp.pvn.u.isname.type, vals->pvp.pvn.u.isname.name.n, &val, v_avp))) { at = NULL; /* check attribute */ if (attrs) { err = -1; a_avp = search_first_avp(attrs->pvp.pvn.u.isname.type, attrs->pvp.pvn.u.isname.name.n, &attr, a_avp); if (!a_avp) { LM_ERR("missing attribute\n"); goto error; } if (!(a_avp->flags & AVP_VAL_STR)) { LM_ERR("invalid attribute name - must be string\n"); goto error; } at = &attr.s; } if (v_avp->flags & AVP_VAL_STR) err = evi_param_add_str(params, at, &val.s); else err = evi_param_add_int(params, at, &val.n); if (err) { LM_ERR("error while adding parameter\n"); goto error; } } /* check if there were too many attribute names */ if (attrs && a_avp && search_first_avp(attrs->pvp.pvn.u.isname.type, attrs->pvp.pvn.u.isname.name.n, &attr, a_avp)) { /* only signal error - continue */ LM_ERR("too many attribute names\n"); } raise: err = evi_raise_event_msg(msg, id, params); return err ? err : 1; error: evi_free_params(params); return -1; } struct mi_root * mi_event_subscribe(struct mi_root *root, void *param ) { struct mi_node *node; int ret; unsigned int expire = 0; str event_name, transport_sock; /* event name */ node = root->node.kids; if (!node || !node->value.len || !node->value.s) { LM_ERR("no parameters received\n"); goto missing_param; } event_name = node->value; /* socket */ node = node->next; if (!node || !node->value.len || !node->value.s) { LM_ERR("no transport type\n"); goto missing_param; } transport_sock = node->value; /* check expire */ node = node->next; if (node) { /* expiration period is set */ if (str2int(&node->value, &expire) < 0) { LM_ERR("invalid expire value %.*s", node->value.len, node->value.s); goto bad_param; } } else expire = DEFAULT_EXPIRE; ret = evi_event_subscribe(event_name, transport_sock, expire, 1); if (ret < 0) goto bad_param; return ret ? init_mi_tree(200, MI_SSTR(MI_OK)) : 0; missing_param: return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); bad_param: return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM)); } /* used to list all the registered events */ struct mi_root * mi_events_list(struct mi_root *cmd_tree, void *param) { struct mi_root *rpl_tree; struct mi_node *node=NULL, *rpl=NULL; unsigned i; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; for (i = 0; i < events_no; i++) { node = add_mi_node_child(rpl, 0, "Event", 5, events[i].name.s, events[i].name.len); if(node == NULL) goto error; if (!addf_mi_attr(node, 0, "id", 2, "%d", events[i].id)) goto error; if ((i + 1) % 50 == 0) { flush_mi_tree(rpl_tree); } } return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } static int evi_print_subscriber(struct mi_node *rpl, evi_subs_p subs) { evi_reply_sock *sock; struct mi_node *node; str socket; if (!subs || !subs->trans_mod || !subs->trans_mod->print) { LM_ERR("subscriber does not have a print method exported\n"); return -1; } node = add_mi_node_child(rpl, 0, "Subscriber", 10, 0, 0); if(node == NULL) return -1; sock = subs->reply_sock; if (!sock) { LM_DBG("no socket specified\n"); if (!add_mi_attr(node, 0, "protocol", 8, subs->trans_mod->proto.s, subs->trans_mod->proto.len)) return -1; return 0; } socket = subs->trans_mod->print(sock); LM_DBG("print subscriber socket <%.*s> %d\n", socket.len, socket.s, socket.len); if (!addf_mi_attr(node, MI_DUP_VALUE, "socket", 6, "%.*s:%.*s", subs->trans_mod->proto.len, subs->trans_mod->proto.s, socket.len, socket.s)) return -1; if (sock->flags & EVI_EXPIRE) { if (!addf_mi_attr(node, 0, "expire", 6, "%d", sock->expire)) return -1; } else { if (!add_mi_attr(node, 0, "expire", 6, "never", 5)) return -1; } /* XXX - does subscription time make sense? */ return 0; } struct evi_mi_param { struct mi_node * node; struct mi_root * root; int nr; }; static int evi_print_event(struct evi_mi_param *param, evi_event_t *ev, evi_subs_p subs) { struct mi_node *node=NULL; struct mi_node *rpl = param->node; /* add event only if there are subscribers */ if (!subs && !ev->subscribers) return 0; node = add_mi_node_child(rpl, MI_IS_ARRAY, "Event", 5, ev->name.s, ev->name.len); if(node == NULL) goto error; if (!addf_mi_attr(node, 0, "id", 2, "%d", ev->id)) goto error; if (subs) { if (evi_print_subscriber(node, subs) < 0) { LM_ERR("cannot print subscriber info\n"); goto error; } } else { for (subs = ev->subscribers; subs; subs = subs->next) { if (evi_print_subscriber(node, subs) < 0) { LM_ERR("cannot print subscriber info\n"); goto error; } if (++param->nr % 50 == 0) flush_mi_tree(param->root); } } return 0; error: return -1; } static evi_subs_p evi_get_subscriber(evi_event_p event, str sock_str) { evi_export_t * trans_mod; evi_subs_p subscriber = NULL; evi_reply_sock * sock; /* transport module name */ trans_mod = get_trans_mod(&sock_str); if (!trans_mod) { LM_DBG("couldn't find a protocol to support %.*s\n", sock_str.len, sock_str.s); return NULL; } sock_str.s += trans_mod->proto.len + 1; sock_str.len -= (trans_mod->proto.len + 1); /* parse reply socket */ sock = trans_mod->parse(sock_str); if (!sock) return NULL; /* tries to match other socket */ if (trans_mod->match) { lock_get(event->lock); for (subscriber = event->subscribers; subscriber; subscriber = subscriber->next) { if (subscriber->trans_mod != trans_mod) continue; if (trans_mod->match(sock, subscriber->reply_sock)) { if (trans_mod->free) trans_mod->free(sock); else shm_free(sock); break; } } lock_release(event->lock); } return subscriber; } /* used to list all subscribers */ struct mi_root * mi_subscribers_list(struct mi_root *cmd_tree, void *param) { struct mi_root *rpl_tree, *err=NULL; struct mi_node *node=NULL, *rpl=NULL; struct evi_mi_param prm; evi_subs_p subs = NULL; evi_event_p event; unsigned i; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; memset(&prm, 0, sizeof(struct evi_mi_param)); rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; node = cmd_tree->node.kids; prm.node = rpl; prm.root = rpl_tree; /* dump all info */ if (!node) { for (i = 0; i < events_no; i++) { if (evi_print_event(&prm, &events[i], NULL) < 0) { LM_ERR("cannot print event %.*s info\n", events[i].name.len, events[i].name.s); goto error; } } return rpl_tree; } /* get the event name */ event = evi_get_event(&node->value); if (!event) { err = init_mi_tree(404, MI_SSTR("Event not published")); goto error; } node = node->next; /* if a subscriber was specified */ if (node) { if (node->next) { err = init_mi_tree(400, MI_SSTR(MI_MISSING_PARM)); goto error; } /* search for subscriber */ subs = evi_get_subscriber(event, node->value); if (!subs) { err = init_mi_tree(404, MI_SSTR("Subscriber does not exist")); goto error; } } if (evi_print_event(&prm, event, subs) < 0) { LM_ERR("cannot print event %.*s info\n", event->name.len, event->name.s); goto error; } return rpl_tree; error: free_mi_tree(rpl_tree); return err; } opensips-2.2.2/evi/event_interface.h000066400000000000000000000040321300170765700174570ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef _EVENT_INTERFACE_H_ #define _EVENT_INTERFACE_H_ #include "evi_transport.h" #include "evi.h" #include "../locking.h" #include "../str.h" #define TRANSPORT_SEP ':' #define DEFAULT_EXPIRE 3600 typedef struct evi_subscriber { evi_export_t* trans_mod; /* transport module */ evi_reply_sock* reply_sock; /* reply socket */ struct evi_subscriber *next; /* next subscriber */ } evi_subs_t, *evi_subs_p; typedef struct evi_event { event_id_t id; /* event id */ str name; /* event name */ gen_lock_t *lock; /* lock for list */ evi_subs_p subscribers; /* subscribers list for this event */ } evi_event_t, *evi_event_p; /* function used to subscribe for an event */ struct mi_root * mi_event_subscribe(struct mi_root *cmd_tree, void *param); /* used to list all the registered events */ struct mi_root * mi_events_list(struct mi_root *cmd_tree, void *param); /* used to listt all subscribers */ struct mi_root * mi_subscribers_list(struct mi_root *cmd_tree, void *param); /* returns the transport export */ evi_export_t* get_trans_mod(str* tran); /* returns the transport modules number */ int get_trans_mod_no(void); #endif /* _EVENT_INTERFACE_H_ */ opensips-2.2.2/evi/evi.h000066400000000000000000000017531300170765700151100ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef _EVI_H_ #define _EVI_H_ #define EVI_ERROR -1 #define MAX_EVENT_NAME 64 typedef int event_id_t; #endif opensips-2.2.2/evi/evi_core.c000066400000000000000000000025111300170765700161040ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #include "evi_modules.h" #include "evi_core.h" #include "../ut.h" /* static events exported by the core */ static str evi_core_table[] = { CORE_EVENT_STR(THRESHOLD), #ifdef STATISTICS CORE_EVENT_STR(SHM_THRESHOLD), #endif CORE_EVENT_STR(PKG_THRESHOLD), }; int evi_register_core(void) { int i, size = sizeof(evi_core_table) / sizeof(str); for (i = 0; i < size; i++) { if (EVI_ERROR == evi_publish_event(evi_core_table[i])) return -1; } return 0; } opensips-2.2.2/evi/evi_core.h000066400000000000000000000024051300170765700161130ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef EVI_CORE_H #define EVI_CORE_H /* events id declared - these must be always incremented by one */ #define EVI_THRESHOLD_ID 0 #define EVI_SHM_THRESHOLD_ID 1 #define EVI_PKG_THRESHOLD_ID 2 #define EVI_CORE_PREFIX "E_CORE_" #define CORE_EVENT_STR(_event) \ { EVI_CORE_PREFIX # _event, sizeof(EVI_CORE_PREFIX # _event)-1 } extern int evi_register_core(void); #endif /* EVI_CORE_H */ opensips-2.2.2/evi/evi_modules.h000066400000000000000000000054051300170765700166360ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef _EVI_MODULES_H_ #define _EVI_MODULES_H_ #include "../str.h" #include "event_interface.h" struct sip_msg; /* functions used by modules */ /* * Used to register a new event * Parameters: * + Event name * * Returns: * - event id if successfull or EVI_ERROR on error */ event_id_t evi_publish_event(str event_name); /* * Used to raise an event * Parameters: * + event id * + parameters * * Returns: * - 0 on success or negative on error */ int evi_raise_event(event_id_t id, evi_params_t* params); /* * Used to raise an event that uses the message attached * Parameters: * + message * + event id * + parameters * * Returns: * - 0 on success or negative on error */ int evi_raise_event_msg(struct sip_msg *msg, event_id_t id, evi_params_t* params); /* * Used to raise an event from script * Parameters: * + message * + event id * + params attributes * + params values * * Returns: * - 0 on success or negative on error */ int evi_raise_script_event(struct sip_msg *msg, event_id_t id, void * attrs, void * vals); /* * Used to subscribe for an event * Parameter: * + event name * + socket name * + expire * + unsubscription * * Returns: * - -1 on parameters error * - 0 on internal error * - 1 on success */ int evi_event_subscribe(str , str , unsigned , unsigned ); /* * Used to check if there are subscribers * Parameters: * + event id * * Returns: * - 0 if there is no subscriber for this event * - positive if there are subscribers listening */ int evi_probe_event(event_id_t id); /* * Used to return the event id of an event * Parameters: * + event name * * Returns: * - event_id or error */ event_id_t evi_get_id(str *name); /* * Used to return an event with a specific name * Parameters: * + event name * * Returns: * - event_id or error */ evi_event_p evi_get_event(str *name); #endif /* _EVI_MODULES_H_ */ opensips-2.2.2/evi/evi_params.c000066400000000000000000000062301300170765700164410ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #include "evi_params.h" #include "../mem/mem.h" #include /* creates an element and links it to the parameters list * but without populating the parameter value */ evi_param_p evi_param_create(evi_params_p list, str *name) { evi_param_p new_p; if (!list) { LM_ERR("invalid param list\n"); return 0; } new_p = pkg_malloc(sizeof(evi_param_t)); if (!new_p) { LM_ERR("no more pkg mem for new parameter\n"); return 0; } memset(new_p, 0, sizeof(evi_param_t)); if (name) { new_p->name.s = name->s; new_p->name.len = name->len; } new_p->next = NULL; if (list->last) { list->last->next = new_p; list->last = new_p; } else { list->last = list->first = new_p; } return new_p; } int evi_param_set(evi_param_p el, void *param, int flags) { if (!el) { LM_ERR("no parameter specified\n"); return 1; } if (!(EVI_INT_VAL & flags) && !(EVI_STR_VAL & flags)) { LM_ERR("params should be int or str [%x]\n", flags); return -1; } LM_DBG("adding %s param\n", EVI_INT_VAL & flags ? "int" : "string"); el->flags = flags; if (flags & EVI_INT_VAL) el->val.n = *((int*)param); else memcpy(&el->val, param, sizeof(str)); return 0; } /* adds a new parameter to the list */ int evi_param_add(evi_params_p list, str *name, void *param, int flags) { evi_param_p new_p; if (!(EVI_INT_VAL & flags) && !(EVI_STR_VAL & flags)) { LM_ERR("params should be int or str [%x]\n", flags); return -1; } new_p = evi_param_create(list, name); if (!new_p) { LM_ERR("cannot create parameter\n"); return -1; } if (evi_param_set(new_p, param, flags) < 0) { LM_ERR("cannot set the parameter value\n"); return -1; } return 0; } /* allocs a new structure and initializes it with 0 */ evi_params_p evi_get_params(void) { evi_params_p new_list = pkg_malloc(sizeof(evi_params_t)); if (!new_list) { LM_ERR("no more pkg memory for the list\n"); return NULL; } memset(new_list, 0, sizeof(evi_params_t)); /* used to remember to free it */ new_list->flags = EVI_FREE_LIST; return new_list; } /* frees a parameters list */ void evi_free_params(evi_params_p list) { evi_param_p node, nxt; if (!list) return; for (node = list->first; node; node = nxt) { nxt = node->next; pkg_free(node); } list->first = list->last = NULL; /* list should be freed */ pkg_free(list); } opensips-2.2.2/evi/evi_params.h000066400000000000000000000045751300170765700164600ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef _EVI_PARAMS_H_ #define _EVI_PARAMS_H_ #include "../str.h" #define EVI_INT_VAL 0x01 /* val is int */ #define EVI_STR_VAL 0x02 /* val is str */ #define EVI_FREE_LIST (1<<31) /* should free params list */ typedef struct evi_param_ { int flags; union { int n; str s; } val; str name; struct evi_param_ *next; } evi_param_t, *evi_param_p; /* * Remember to initilize this structure with 0 * or use the functions below to alloc and free it */ typedef struct evi_params_ { int flags; evi_param_p first; evi_param_p last; } evi_params_t, *evi_params_p; /* used to build parameters list */ evi_params_p evi_get_params(void); /* frees all parameters */ void evi_free_params(evi_params_p); /* generic parameter add */ int evi_param_add(evi_params_p list, str *name, void *param, int flags); /* adds an integer to the list */ #define evi_param_add_int(p_list, p_name, p_int) \ evi_param_add(p_list, p_name, p_int, EVI_INT_VAL) /* adds a string to the list */ #define evi_param_add_str(p_list, p_name, p_str) \ evi_param_add(p_list, p_name, p_str, EVI_STR_VAL) /* creates a new parameter */ evi_param_p evi_param_create(evi_params_p list, str *name); /* sets the value of a parameter */ int evi_param_set(evi_param_p element, void *param, int flags); /* sets an integer value to a parameter */ #define evi_param_set_int(p_el, p_int) \ evi_param_set(p_el, p_int, EVI_INT_VAL) /* sets a string value to a parameter */ #define evi_param_set_str(p_el, p_str) \ evi_param_set(p_el, p_str, EVI_STR_VAL) #endif opensips-2.2.2/evi/evi_transport.c000066400000000000000000000062171300170765700172170ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #include "event_interface.h" #include "evi_transport.h" #include "../mem/shm_mem.h" /* list with the transport modules */ static evi_trans_t *evi_trans_mods = NULL; static int evi_trans_mods_size = 0; /* functions used by the transport modules */ int register_event_mod(evi_export_t *ev) { evi_trans_t *trans_mod; if (!ev || !ev->proto.len || !ev->proto.s) { LM_ERR("no export or name specified\n"); goto error; } if (!ev->raise) { LM_ERR("raise function should be specified for protocol %.*s\n", ev->proto.len, ev->proto.s); goto error; } if (!ev->parse) { LM_ERR("parse function should be specified for protocol %.*s\n", ev->proto.len, ev->proto.s); goto error; } if (!ev->print) { LM_ERR("print function should be specified for protocol %.*s\n", ev->proto.len, ev->proto.s); goto error; } if (ev->flags) { if (ev->flags & EVI_FREE_LIST) { LM_ERR("module cannot have the id %x\n", ev->flags); goto error; } /* check to see if there are two modules with the same id (or protocol) */ for (trans_mod = evi_trans_mods; trans_mod; trans_mod = trans_mod->next){ if (trans_mod->module->flags & ev->flags) { LM_ERR("duplicate flag %x\n", ev->flags); goto error; } if (ev->proto.len == trans_mod->module->proto.len && !memcmp(ev->proto.s,trans_mod->module->proto.s,ev->proto.len)){ LM_ERR("duplicate transport module protocol <%.*s>\n", ev->proto.len, ev->proto.s); goto error; } } } trans_mod = shm_malloc(sizeof(evi_trans_t)); if (!trans_mod) { LM_ERR("no more shm memory\n"); goto error; } trans_mod->module = ev; trans_mod->next = evi_trans_mods; evi_trans_mods = trans_mod; evi_trans_mods_size++; return 0; error: return EVI_ERROR; } /* checks if there are any modules loaded */ int get_trans_mod_no(void) { return evi_trans_mods_size; } /* Returns the transport export */ evi_export_t* get_trans_mod(str* tran) { str t; char *p; evi_trans_t *ev = evi_trans_mods; if (!tran || !tran->len || !tran->s) return NULL; t.s = tran->s; p = memchr(tran->s, TRANSPORT_SEP, tran->len); if (!p) t.len = tran->len; else t.len = p - tran->s; while (ev) { if (ev->module->proto.len == t.len && !memcmp(ev->module->proto.s, t.s, t.len)) return ev->module; ev = ev->next; } return NULL; } opensips-2.2.2/evi/evi_transport.h000066400000000000000000000054401300170765700172210ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef _EVI_TRANSPORT_H_ #define _EVI_TRANSPORT_H_ #include #include #include #include "../mi/mi.h" #include "../str.h" #include "../ip_addr.h" #include "../parser/msg_parser.h" #include "evi_params.h" #define EVI_ADDRESS (1 << 1) #define EVI_PORT (1 << 2) #define EVI_SOCKET (1 << 3) #define EVI_PARAMS (1 << 4) #define EVI_EXPIRE (1 << 8) // indicates that the socket may expire #define EVI_PENDING (1 << 9) // indicates that the socket is in use /* sockets */ typedef union { union sockaddr_union udp_addr; struct sockaddr_un unix_addr; } sockaddr_reply; /* reply socket */ typedef struct ev_reply_sock_ { unsigned int flags; unsigned short port; str address; unsigned int expire; long subscription_time; sockaddr_reply src_addr; void *params; } evi_reply_sock; /* event raise function */ typedef int (raise_f)(struct sip_msg *msg, str *ev_name, evi_reply_sock *sock, evi_params_t * params); /* socket parse function */ typedef evi_reply_sock* (parse_f)(str); /* tries to match two sockets */ typedef int (match_f)(evi_reply_sock *sock1, evi_reply_sock *sock2); /* free a socket */ typedef void (free_f)(evi_reply_sock *sock); /* prints a given socket */ typedef str (print_f)(evi_reply_sock *sock); typedef struct evi_export_ { str proto; /* protocol name */ raise_f *raise; /* raise function */ parse_f *parse; /* parse function */ match_f *match; /* sockets match function */ free_f *free; /* free a socket */ print_f *print; /* prints a socket */ unsigned int flags; } evi_export_t; /* transport list */ typedef struct evi_trans_ { evi_export_t *module; struct evi_trans_ *next; } evi_trans_t; /* functions used by the transport modules */ /* * Used to register a transport module * Parameters: * + export functions * * Returns: * - 0 if successfull or negative on error */ int register_event_mod(evi_export_t *ev); #endif /* _EVI_TRANSPORT_H_ */ opensips-2.2.2/examples/000077500000000000000000000000001300170765700152015ustar00rootroot00000000000000opensips-2.2.2/examples/acc-mysql.cfg000066400000000000000000000143521300170765700175600ustar00rootroot00000000000000# # Sample config for MySQL accouting with OpenSIPS # # - db_mysql module must be compiled and installed # # - new columns have to be added since by default only few are recorded # - here are full SQL statements to create acc and missed_calls tables # # CREATE TABLE `acc` ( # `id` int(10) unsigned NOT NULL auto_increment, # `method` varchar(16) NOT NULL default '', # `from_tag` varchar(64) NOT NULL default '', # `to_tag` varchar(64) NOT NULL default '', # `callid` varchar(128) NOT NULL default '', # `sip_code` char(3) NOT NULL default '', # `sip_reason` varchar(32) NOT NULL default '', # `time` datetime NOT NULL default '0000-00-00 00:00:00', # `src_ip` varchar(64) NOT NULL default '', # `dst_user` varchar(64) NOT NULL default '', # `dst_domain` varchar(128) NOT NULL default '', # `src_user` varchar(64) NOT NULL default '', # `src_domain` varchar(128) NOT NULL default '', # INDEX acc_callid (`callid`), # PRIMARY KEY (`id`) # ); # # CREATE TABLE `missed_calls` ( # `id` int(10) unsigned NOT NULL auto_increment, # `method` varchar(16) NOT NULL default '', # `from_tag` varchar(64) NOT NULL default '', # `to_tag` varchar(64) NOT NULL default '', # `callid` varchar(128) NOT NULL default '', # `sip_code` char(3) NOT NULL default '', # `sip_reason` varchar(32) NOT NULL default '', # `time` datetime NOT NULL default '0000-00-00 00:00:00', # `src_ip` varchar(64) NOT NULL default '', # `dst_user` varchar(64) NOT NULL default '', # `dst_domain` varchar(128) NOT NULL default '', # `src_user` varchar(64) NOT NULL default '', # `src_domain` varchar(128) NOT NULL default '', # INDEX acc_callid (`callid`), # PRIMARY KEY (`id`) # ); # # # ----------- global configuration parameters ------------------------ log_level=3 # debug level (cmd line: -dddddddddd) log_stderror=no # (cmd line: -E) /* Uncomment these lines to enter debugging mode */ #debug_mode=yes check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 # # uncomment the following lines for TLS support #disable_tls = 0 #listen = tls:your_IP:5061 #tls_verify_server = 1 #tls_verify_client = 1 #tls_require_client_certificate = 0 #tls_method = TLSv1 #tls_certificate = "/usr/local/etc/opensips/tls/user/user-cert.pem" #tls_private_key = "/usr/local/etc/opensips/tls/user/user-privkey.pem" #tls_ca_list = "/usr/local/etc/opensips/tls/user/user-calist.pem" # ------------------ module loading ---------------------------------- # set module path mpath="/usr/local/lib/opensips/modules/" # Uncomment this if you want to use SQL database # - MySQL loaded for accounting as well loadmodule "db_mysql.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "acc.so" loadmodule "mi_fifo.so" # Uncomment this if you want digest authentication # db_mysql.so must be loaded ! #loadmodule "auth.so" #loadmodule "auth_db.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- usrloc params -- #modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # #modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # #modparam("auth_db", "password_column", "password") # -- acc params -- modparam("acc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # flag to record to db modparam("acc", "db_flag", 1) modparam("acc", "db_missed_flag", 2) # flag to log to syslog modparam("acc", "log_flag", 1) modparam("acc", "log_missed_flag", 2) # use extra accounting to record caller and callee username/domain # - take them from From URI and R-URI modparam("acc", "log_extra", "src_user=$fU;src_domain=$fd;dst_user=$rU;dst_domain=$rd") modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;dst_user=$rU;dst_domain=$rd") # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); if(is_method("BYE")) { # account BYE for STOP record setflag(1); } route(1); }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!is_method("REGISTER")) record_route(); # account all calls if(is_method("INVITE")) { # set accounting on for INVITE (success or missed call) setflag(1); setflag(2); } if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); # if you have some interdomain connections via TLS #if(uri=~"@tls_domain1.net") { # t_relay("tls:domain1.net"); # exit; #} else if(uri=~"@tls_domain2.net") { # t_relay("tls:domain2.net"); # exit; #} route(1); }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (is_method("REGISTER")) { # Uncomment this if you want to use digest authentication #if (!www_authorize("opensips.org", "subscriber")) { # www_challenge("opensips.org", "0"); # exit; #}; save("location"); exit; }; if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; append_hf("P-hint: usrloc applied\r\n"); }; route(1); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; exit; } opensips-2.2.2/examples/acc.cfg000066400000000000000000000031341300170765700164110ustar00rootroot00000000000000# # $Id$ # # example: accounting calls to nummerical destinations # # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/opensips/modules/" loadmodule "tm.so" loadmodule "acc.so" loadmodule "sl.so" loadmodule "maxfwd.so" loadmodule "rr.so" # ----------------- setting module-specific parameters --------------- # -- acc params -- # set the reporting log level modparam("acc", "log_level", 1) # number of flag, which will be used for accounting; if a message is # labeled with this flag, its completion status will be reported modparam("acc", "log_flag", 1 ) # ------------------------- request routing logic ------------------- # main routing logic route{ /* ********* ROUTINE CHECKS ********************************** */ # filter too old messages if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # Process record-routing if (loose_route()) { # label BYEs for accounting if (is_method("BYE")) setflag(1); t_relay(); exit; }; # labeled all transaction for accounting setflag(1); # record-route INVITES to make sure BYEs will visit our server too if (is_method("INVITE")) record_route(); # forward the request statefuly now; (we need *stateful* forwarding, # because the stateful mode correlates requests with replies and # drops retranmissions; otherwise, we would have to report on # every single message received) if (!t_relay()) { sl_reply_error(); exit; }; } opensips-2.2.2/examples/ctd.sh000066400000000000000000000124711300170765700163140ustar00rootroot00000000000000#!/bin/sh # # $Id$ # # Usage: ctd.sh $FROM $TARGET # # click-to-dial example using REFER #---------------------------------- # # About: # ------ # this script initiates a call from SIP user $FROM to SIP # user $TARGET; it works as follows: a dummy user invites # $FROM to a dummy "call on hold"; as soon as it is set up, the # dummy user transfers $FROM to $TARGET (REFER transaction) # and terminates the dummy session established previously # (BYE transaction). Note: the "dummy call" is used to # make $FROM accept $REFER -- most of SIP phones do not # accept REFER if no call has not been established yet. # # Requirements: # ------------- # - SER with FIFO server turned on and TM module loaded # # Limitations: # ------------ # it only works with UAs supporting REFER; it has been tested # with Cisco 7960, Mitel 5055, Grandstream and Pingtel; Windows # Messenger does not support REFER. Never tested on solaris. # Some cisco 7960 images don't work (in particular, POS30202 # doesn't, POS3-03-8-21 does) # # History: # -------- # 2003-03-01 bug_fix: route set reversed # 2003-02-27 dialog support completed (jiri) # 2003-04-28 dialog info precomputed in SER (jiri) # 2007-04-06 updated for OpenSIPS 1.2.0+ (daniel) #-------------------------------- # config: who with whom # address of the final destination to which we want to transfer # initial CSeq and CallId if [ -z "$2" ]; then TARGET="sip:23@192.168.2.16" echo "destination unspecified -- taking default value $TARGET" else TARGET="$2" fi # address of user wishing to initiate conversation if [ -z "$1" ] ; then URI="sip:44@192.168.2.16" echo "caller unspecified -- taking default value $URI" else URI="$1" fi #--------------------------------- # fixed config data FIFO="/tmp/opensips_fifo" # address of controller FROM="" CSEQ="1" CALLIDNR=`date '+%s'`$$ CALLID="${CALLIDNR}.fifouacctd" name="ctd_fifo_$$" fifo_reply="/tmp/$name" dlg="/tmp/$CALLID.dlg" FIXED_DLG="From: $FROM;tag=$CALLIDNR\r\nCall-ID: $CALLID\r\nContact: \r\n" #---------------------------------- # generate parts of FIFO-request essential to forming # subsequent in-dialog reuqests # # limitations: parsing broken if <> in display names or # line-folding used filter_fl() { awk -F ' ' ' BEGIN { IGNORECASE=1; line=0; eoh=0;ret=1 } END { exit ret; } {line++; } # line 2: status code line==2 && /^2[0-9][0-9] / { ret=0;next; } line==2 && /^[3-6][0-9][0-9] / { print; print $0 > "/dev/stderr"; next; } line==2 { print "reply error"; print; next; } # skip body /^$/ { eoh=1 } eoh==1 { next } # uri and outbound uri at line 2,3: copy and paste line==3 { print $0; next; } line==4 { print $0; print "."; printf("\""); next; } # line 5: Route; empty if ".", copy and paste otherwise line==5 && /^\.$/ { next; } # if non-empty, copy and paste it line==5 { printf("%s\n", $0); next; } # filter out to header field for use in next requests /^(To|t):/ { printf("%s\n", $0); next; } # anything else will be ignored {next} ' # end of awk script } # end of filter_fl #--------------------------- # main # set up exit cleaner trap "rm -f $dlg $fifo_reply; exit 1" 0 # set up FIFO communication if [ ! -w $FIFO ] ; then # can I write to FIFO server? echo "Error opening ser's FIFO $FIFO" exit 1 fi mkfifo $fifo_reply # create a reply FIFO if [ $? -ne 0 ] ; then echo "error opening reply fifo $fifo_reply" exit 1 fi chmod a+w $fifo_reply # start reader now so that it is ready for replies # immediately after a request is out cat < $fifo_reply | filter_fl > $dlg & fifo_job="$!" # initiate dummy INVITE with pre-3261 "on-hold" # (note the dots -- they mean in order of appearance: # outbound uri, end of headers, end of body; eventualy # the FIFO request must be terminated with an empty line) #cat < $FIFO <\r\nCSeq: $CSEQ INVITE\r\nContent-Type: application/sdp\r\n"` " "`printf "v=0\r\no=click-to-dial 0 0 IN IP4 0.0.0.0\r\ns=session\r\nc=IN IP4 0.0.0.0\r\nb=CT:1000\r\nt=0 0\r\nm=audio 9 RTP/AVP 8 0\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\n"` " EOF #exit # wait for reply wait $fifo_job # returns completion status of filter_fl if [ "$?" -ne "0" ] ; then echo "invitation failed" exit 1 fi echo "invitation succeeded" # proceed to REFER now if [ \! -r $dlg ] ; then echo "dialog broken" exit 1 fi CSEQ=`expr $CSEQ + 1` # start reader now so that it is ready for replies # immediately after a request is out cat < $fifo_reply | filter_fl > /dev/null & fifo_job="$!" # dump the REFER request to FIFO server cat > $FIFO < /dev/null & fifo_job="$!" cat > $FIFO < dumping messages with MSILO\n"); # MSILO - dumping user's offline messages if (m_dump()) { log("MSILO: offline messages dumped - if they were\n"); } else { log("MSILO: no offline messages dumped\n"); }; exit; }; # backup r-uri for m_dump() in case of delivery failure $avp(11) = $ru; # domestic SIP destinations are handled using our USRLOC DB if(!lookup("location")) { if (! t_newtran()) { sl_reply_error(); exit; }; # we do not care about anything else but MESSAGEs if (!is_method("MESSAGE")) { if (!t_reply("404", "Not found")) { sl_reply_error(); }; exit; }; log("MESSAGE received -> storing using MSILO\n"); # MSILO - storing as offline message if (m_store("$ru")) { log("MSILO: offline message stored\n"); if (!t_reply("202", "Accepted")) { sl_reply_error(); }; }else{ log("MSILO: offline message NOT stored\n"); if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; exit; }; # if the downstream UA does not support MESSAGE requests # go to failure_route[1] t_on_failure("1"); t_relay(); exit; }; # forward anything else t_relay(); } failure_route[1] { # forwarding failed -- check if the request was a MESSAGE if (!is_method("MESSAGE")) exit; log(1,"MSILO: the downstream UA does not support MESSAGE requests ...\n"); # we have changed the R-URI with the contact address -- ignore it now if (m_store("$avp(11)")) { log("MSILO: offline message stored\n"); t_reply("202", "Accepted"); }else{ log("MSILO: offline message NOT stored\n"); t_reply("503", "Service Unavailable"); }; } opensips-2.2.2/examples/nathelper.cfg000066400000000000000000000142631300170765700176520ustar00rootroot00000000000000# # simple quick-start config script including nathelper support # This default script includes nathelper support. To make it work # you will also have to install Maxim's RTP proxy. The proxy is enforced # if one of the parties is behind a NAT. # # If you have an endpoing in the public internet which is known to # support symmetric RTP (Cisco PSTN gateway or voicemail, for example), # then you don't have to force RTP proxy. If you don't want to enforce # RTP proxy for some destinations than simply use t_relay() instead of # route(1) # # Sections marked with !! Nathelper contain modifications for nathelper # # NOTE !! This config is EXPERIMENTAL ! # # ----------- global configuration parameters ------------------------ log_level=3 # logging level (cmd line: -dddddddddd) log_stderror=no # (cmd line: -E) /* Uncomment these lines to enter debugging mode */ #debug_mode=yes check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/opensips/modules/" # Uncomment this if you want to use SQL database #loadmodule "db_mysql.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "signaling.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "mi_fifo.so" # Uncomment this if you want digest authentication # db_mysql.so must be loaded ! #loadmodule "auth.so" #loadmodule "auth_db.so" # !! Nathelper loadmodule "nathelper.so" loadmodule "rtpproxy.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- usrloc params -- modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line #modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module #modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) #modparam("auth_db", "password_column", "password") # !! Nathelper modparam("usrloc","nat_bflag",6) modparam("nathelper","sipping_bflag",8) modparam("nathelper", "ping_nated_only", 1) # Ping only clients behind NAT # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # !! Nathelper # Special handling for NATed clients; first, NAT test is # executed: it looks for via!=received and RFC1918 addresses # in Contact (may fail if line-folding is used); also, # the received test should, if completed, should check all # vias for rpesence of received if (nat_uac_test("3")) { # Allow RR-ed requests, as these may indicate that # a NAT-enabled proxy takes care of it; unless it is # a REGISTER if (is_method("REGISTER") || !is_present_hf("Record-Route")) { log("LOG:Someone trying to register from private IP, rewriting\n"); # This will work only for user agents that support symmetric # communication. We tested quite many of them and majority is # smart enough to be symmetric. In some phones it takes a # configuration option. With Cisco 7960, it is called # NAT_Enable=Yes, with kphone it is called "symmetric media" and # "symmetric signalling". # Rewrite contact with source IP of signalling fix_nated_contact(); if ( is_method("INVITE") ) { fix_nated_sdp("1"); # Add direction=active to SDP }; force_rport(); # Add rport parameter to topmost Via setbflag(6); # Mark as NATed # if you want sip nat pinging # setbflag(8); }; }; # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); exit; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!is_method("REGISTER")) record_route(); if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); route(1); exit; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (is_method("REGISTER")) { # Uncomment this if you want to use digest authentication #if (!www_authorize("siphub.org", "subscriber")) { # www_challenge("siphub.org", "0"); # return; #}; save("location"); exit; }; lookup("aliases"); if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); exit; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; }; append_hf("P-hint: usrloc applied\r\n"); route(1); } route[1] { # !! Nathelper if (uri=~"[@:](192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)" && !search("^Route:")){ sl_send_reply("479", "We don't forward to private IP addresses"); exit; }; # if client or server know to be behind a NAT, enable relay if (isbflagset(6)) { rtpproxy_offer(); }; # NAT processing of replies; apply to all transactions (for example, # re-INVITEs from public to private UA are hard to identify as # NATed at the moment of request processing); look at replies t_on_reply("1"); # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } # !! Nathelper onreply_route[1] { # NATed transaction ? if (isbflagset(6) && status =~ "(183)|2[0-9][0-9]") { fix_nated_contact(); rtpproxy_answer(); # otherwise, is it a transaction behind a NAT and we did not # know at time of request processing ? (RFC1918 contacts) } else if (nat_uac_test("1")) { fix_nated_contact(); }; } opensips-2.2.2/examples/pi_framework.xml000066400000000000000000000275731300170765700204260ustar00rootroot00000000000000 mysql://opensips:opensipsrw@localhost/opensips dispatcher mysql id DB_INT setid DB_INT destination DB_STR URI_IPV4HOST socket DB_STR P_IPV4_PORT flags DB_INT weight DB_INT attrs DB_STR description DB_STR dialplan mysql id DB_INT dpid DB_INT pr DB_INT match_op DB_INT match_exp DB_STR match_flags DB_INT subst_exp DB_STR repl_exp DB_STR disabled DB_INT attrs DB_STR dispatcher show_destinations_with_small_setid dispatcher DB_QUERY setid< id setid destination description show_all dispatcher DB_QUERY id setid destination socket flags weight attrs description update_setid dispatcher DB_UPDATE id= setid update_destination dispatcher DB_UPDATE id= destination update_attr dispatcher DB_UPDATE id= attrs update_description dispatcher DB_UPDATE id= description update_socket dispatcher DB_UPDATE id= socket add_gw dispatcher DB_INSERT setid destination socket attrs description add_server_with_setid_100 dispatcher DB_INSERT setid 100 destination socket attrs description delete_by_id dispatcher DB_DELETE id= dialplan show_all dialplan DB_QUERY id dpid pr match_op match_exp match_flags subst_exp repl_exp disabled attrs id show_dpid dialplan DB_QUERY dpid= id dpid pr match_op match_exp match_flags subst_exp repl_exp disabled attrs id show_exact_matching dialplan DB_QUERY match_op= 0 id dpid pr match_op match_exp match_flags subst_exp repl_exp disabled attrs show_regex_matching dialplan DB_QUERY match_op= 1 id dpid pr match_op match_exp match_flags subst_exp repl_exp disabled attrs add dialplan DB_INSERT dpid pr match_op 0 1 match_exp match_flags 0 1 disabled 0 1 attrs delete dialplan DB_DELETE id= update_attr dialplan DB_UPDATE id= attrs update_repl_exp dialplan DB_UPDATE id= repl_exp enable_rule dialplan DB_UPDATE id= disabled 0 disable_rule dialplan DB_UPDATE id= disabled 1 replace dialplan DB_REPLACE dpid pr match_op match_exp match_flags subst_exp repl_exp disabled attrs opensips-2.2.2/examples/pstn.cfg000066400000000000000000000100631300170765700166460ustar00rootroot00000000000000# # $Id$ # # example: ser configured as PSTN gateway guard; PSTN gateway is located # at 192.168.0.10 # # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/opensips/modules/" loadmodule "sl.so" loadmodule "tm.so" loadmodule "acc.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "db_mysql.so" loadmodule "auth.so" loadmodule "auth_db.so" loadmodule "group.so" loadmodule "uri.so" # ----------------- setting module-specific parameters --------------- modparam("auth_db", "db_url","mysql://opensips:opensipsrw@localhost/opensips") modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") # -- acc params -- modparam("acc", "log_level", 1) # that is the flag for which we will account -- don't forget to # set the same one :-) modparam("acc", "log_flag", 1 ) # ------------------------- request routing logic ------------------- # main routing logic route{ /* ********* ROUTINE CHECKS ********************************** */ # filter too old messages if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; /* ********* RR ********************************** */ /* grant Route routing if route headers present */ if (loose_route()) { t_relay(); exit; }; /* record-route INVITEs -- all subsequent requests must visit us */ if (method=="INVITE") { record_route(); }; # now check if it really is a PSTN destination which should be handled # by our gateway; if not, and the request is an invitation, drop it -- # we cannot terminate it in PSTN; relay non-INVITE requests -- it may # be for example BYEs sent by gateway to call originator if (!uri=~"sip:\+?[0-9]+@.*") { if (method=="INVITE") { sl_send_reply("403", "Call cannot be served here"); } else { forward(); }; exit; }; # account completed transactions via syslog setflag(1); # free call destinations ... no authentication needed if ( is_user_in("Request-URI", "free-pstn") /* free destinations */ || uri=~"sip:[79][0-9][0-9][0-9]@.*" /* local PBX */ || uri=~"sip:98[0-9][0-9][0-9][0-9]") { log("free call"); } else if (src_ip==192.168.0.10) { # our gateway doesn't support digest authentication; # verify that a request is coming from it by source # address log("gateway-originated request"); } else { # in all other cases, we need to check the request against # access control lists; first of all, verify request # originator's identity if (!proxy_authorize( "gateway" /* realm */, "subscriber" /* table name */)) { proxy_challenge( "gateway" /* realm */, "0" /* no qop */ ); exit; }; # authorize only for INVITEs -- RR/Contact may result in weird # things showing up in d-uri that would break our logic; our # major concern is INVITE which causes PSTN costs if (method=="INVITE") { # does the authenticated user have a permission for local # calls (destinations beginning with a single zero)? # (i.e., is he in the "local" group?) if (uri=~"sip:0[1-9][0-9]+@.*") { if (!is_user_in("credentials", "local")) { sl_send_reply("403", "No permission for local calls"); exit; }; # the same for long-distance (destinations begin with two zeros") } else if (uri=~"sip:00[1-9][0-9]+@.*") { if (!is_user_in("credentials", "ld")) { sl_send_reply("403", " no permission for LD "); exit; }; # the same for international calls (three zeros) } else if (uri=~"sip:000[1-9][0-9]+@.*") { if (!is_user_in("credentials", "int")) { sl_send_reply("403", "International permissions needed"); exit; }; # everything else (e.g., interplanetary calls) is denied } else { sl_send_reply("403", "Forbidden"); exit; }; }; # INVITE to authorized PSTN }; # authorized PSTN # if you have passed through all the checks, let your call go to GW! rewritehostport("192.168.0.10:5060"); # forward the request now if (!t_relay()) { sl_reply_error(); exit; }; } opensips-2.2.2/examples/redirect.cfg000066400000000000000000000013201300170765700174570ustar00rootroot00000000000000# # $Id$ # # this example shows use of ser as stateless redirect server # # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/opensips/modules/" loadmodule "sl.so" # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { log("REGISTER"); sl_send_reply("200", "ok"); return; }; # rewrite current URI, which is always part of destination ser rewriteuri("sip:parallel@siphub.net:9"); # append one more URI to the destination ser append_branch("sip:redirect@siphub.net:9"); # redirect now sl_send_reply("300", "Redirect"); } opensips-2.2.2/examples/replicate.cfg000066400000000000000000000034121300170765700176320ustar00rootroot00000000000000# # demo script showing how to set-up usrloc replication # # ----------- global configuration parameters ------------------------ log_level=3 # logging level (cmd line: -dddddddddd) log_stderror=yes # (cmd line: -E) # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/opensips/modules/" loadmodule "db_mysql.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "auth.so" loadmodule "auth_db.so" # ----------------- setting module-specific parameters --------------- # digest generation secret; use the same in backup server; # also, make sure that the backup server has sync'ed time modparam("auth", "secret", "alsdkhglaksdhfkloiwr") # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwars==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { # verify credentials if (!www_authorize("foo.bar", "subscriber")) { www_challenge("foo.bar", "0"); exit; }; # if ok, update contacts and ... save("location"); # ... if this REGISTER is not a replica from our # peer server, replicate to the peer server if (!src_ip==backup.foo.bar) { t_replicate("sip:backup.foo.bar:5060"); }; exit; }; # do whatever else appropriate for your domain log("non-REGISTER\n"); }; } opensips-2.2.2/examples/serial_183.cfg000066400000000000000000000024571300170765700175440ustar00rootroot00000000000000# # this example shows how to use forking on failure # log_level=3 log_stderror=1 listen=192.168.2.16 # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/opensips/modules/" # Uncomment this if you want to use SQL database loadmodule "tm.so" loadmodule "sl.so" loadmodule "maxfwd.so" # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # skip register for testing purposes if (is_methos("REGISTER")) { sl_send_reply("200", "ok"); exit; }; if (is_method("INVITE")) { seturi("sip:xxx@192.168.2.16:5064"); # if transaction broken, try other an alternative route t_on_failure("1"); # if a provisional came, stop alternating t_on_reply("1"); }; t_relay(); } failure_route[1] { log(1, "trying at alternate destination\n"); seturi("sip:yyy@192.168.2.16:5064"); t_relay(); } onreply_route[1] { log(1, "reply came in\n"); if (status=~"18[0-9]") { log(1, "provisional -- resetting negative failure\n"); t_on_failure("0"); }; } opensips-2.2.2/examples/web_im/000077500000000000000000000000001300170765700164435ustar00rootroot00000000000000opensips-2.2.2/examples/web_im/README000066400000000000000000000004131300170765700173210ustar00rootroot00000000000000# # $Id$ # This examle illustrate how to use ser's FIFO interface to initate sending an instant message from a webpage. To enable this example, you need - web server with PHP support - install the example webpages on the server - have running ser with enabled fifo opensips-2.2.2/examples/web_im/click_to_dial.html000066400000000000000000000011271300170765700221120ustar00rootroot00000000000000 Click-To-Dial

Click-To-Dial (using REFER)

Unfortunately, this example does not work. The reason is use of REFER for third-party call-control has not been standardized due to resistance of proponents of B2BUA use (which is somewhat bloated and not always operational).
Caller's SIP Address
Callee's SIP Address
Click to dial
opensips-2.2.2/examples/web_im/click_to_dial.php000066400000000000000000000025331300170765700217370ustar00rootroot00000000000000 Click-To-Dial

Click-To-Dial

"; /* open fifo now */ $fifo_handle=fopen( $fifo, "w" ); if (!$fifo_handle) { exit ("Sorry -- cannot open fifo: ".$fifo); } /* construct FIFO command */ $fifo_cmd=":t_uac_dlg:".$myfilename."\n". "REFER\n". $caller."\n". $outbound_proxy."\n". ".\n". "\"From: ".$web_contact."\r\n". "To: ".$callee."\r\n". "p-version: ".$signature."\r\n". "Contact: ".$web_contact."\r\n". "Referred-By: ".$web_contact."\r\n". "Refer-To: ".$callee."\r\n". "\"\n\n"; /* create fifo for replies */ system("mkfifo -m 666 ".$mypath ); /* write fifo command */ if (fwrite( $fifo_handle, $fifo_cmd)==-1) { unlink($mypath); fclose($fifo_handle); exit("Sorry -- fifo writing error"); } fclose($fifo_handle); /* read output now */ if (readfile( $mypath )==-1) { unlink($mypath); exit("Sorry -- fifo reading error"); } unlink($mypath); echo "

Thank you for using click-to-dial

"; ?> opensips-2.2.2/examples/web_im/send_im.html000066400000000000000000000006031300170765700207460ustar00rootroot00000000000000 Send IM

Send IM

SIP Address
Example: john@siphub.net
Message
Click to send
opensips-2.2.2/examples/web_im/send_im.php000066400000000000000000000026021300170765700205720ustar00rootroot00000000000000 Send IM Status

Send IM Status

"; /* open fifo now */ $fifo_handle=fopen( $fifo, "w" ); if (!$fifo_handle) { exit ("Sorry -- cannot open fifo: ".$fifo); } /* construct FIFO command */ $fifo_cmd=":t_uac_dlg:".$myfilename."\n". "MESSAGE\n". $sip_address."\n". $outbound_proxy."\n". ".\n". "\"From: ".$web_contact."\r\n". "To: ".$sip_address."\r\n". "p-version: ".$signature."\r\n". "Contact: ".$web_contact."\r\n". "Content-Type: text/plain; charset=UTF-8\r\n". "\"\n". "\"".$instant_message."\"". "\n\n"; /* create fifo for replies */ system("mkfifo -m 666 ".$mypath ); /* write fifo command */ if (fwrite( $fifo_handle, $fifo_cmd)==-1) { unlink($mypath); fclose($fifo_handle); exit("Sorry -- fifo writing error"); } fclose($fifo_handle); /* read output now */ if (readfile( $mypath )==-1) { unlink($mypath); exit("Sorry -- fifo reading error"); } unlink($mypath); echo "

Thank you for using IM

"; ?> opensips-2.2.2/fastlock.h000066400000000000000000000157251300170765700153540ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2002-02-05 created by andrei * 2003-01-16 added PPC locking code contributed by Dinos Dorkofikis * * 2004-09-12 added MIPS locking for ISA>=2 (>r3000) (andrei) * 2004-12-16 for now use the same locking code for sparc32 as for sparc64 * (it will work only if NOSMP is defined) (andrei) * * 2005-04-27 added alpha locking code (andrei) * 2005-05-25 PPC locking code enabled for PPC64; added a lwsync to * the tsl part and replaced the sync with a lwsync for the * unlock part (andrei) */ /*! * \file * \brief Assembler routines for fast architecture dependend locking. * * Contains the assembler routines for the fast architecture dependend * locking primitives used by the server. This routines are needed e.g. * to protect shared data structures that are accessed from muliple processes. * \todo replace this with the assembler routines provided by the linux kernel */ #ifndef fastlock_h #define fastlock_h #ifdef HAVE_SCHED_YIELD #include #else #include /** Fake sched_yield if no unistd.h include is available */ #define sched_yield() sleep(0) #endif /*! The actual lock */ #ifndef DBG_LOCK typedef volatile int fl_lock_t; #else typedef struct fl_lock_t_{ volatile int lock; char* file; char* func; unsigned long line; } fl_lock_t; #endif /*! Initialize a lock, zero is unlocked. */ #ifndef DBG_LOCK #define init_lock( l ) (l)=0 #else #define init_lock( l ) (l).lock = 0 #endif /*! \brief * Test and set a lock. Used by the get_lock function. * \param lock the lock that should be set * \return 1 if the lock is held by someone else, 0 otherwise * \see get_lock */ #ifndef DBG_LOCK inline static int tsl(fl_lock_t* lock) #else inline static int tsl(volatile int* lock) #endif { int val; #if defined(__CPU_i386) || defined(__CPU_x86_64) #ifdef NOSMP val=0; asm volatile( " btsl $0, %1 \n\t" " adcl $0, %0 \n\t" : "=q" (val), "=m" (*lock) : "0"(val) : "memory", "cc" /* "cc" */ ); #else val=1; asm volatile( " xchg %1, %0" : "=q" (val), "=m" (*lock) : "0" (val) : "memory" ); #endif /*NOSMP*/ #elif defined(__CPU_sparc64) || defined(__CPU_sparc) asm volatile( "ldstub [%1], %0 \n\t" #ifndef NOSMP "membar #StoreStore | #StoreLoad \n\t" #endif : "=r"(val) : "r"(lock):"memory" ); #elif defined __CPU_arm asm volatile( "# here \n\t" "swpb %0, %1, [%2] \n\t" : "=&r" (val) : "r"(1), "r" (lock) : "memory" ); #elif defined(__CPU_ppc) || defined(__CPU_ppc64) asm volatile( "1: lwarx %0, 0, %2\n\t" " cmpwi %0, 0\n\t" " bne 0f\n\t" " stwcx. %1, 0, %2\n\t" " bne- 1b\n\t" " lwsync\n\t" /* lwsync or isync, lwsync is faster and should work, see [ IBM Programming environments Manual, D.4.1.1] */ "0:\n\t" : "=r" (val) : "r"(1), "b" (lock) : "memory", "cc" ); #elif defined(__CPU_mips2) || defined(__CPU_mips32) || defined(__CPU_mips64) long tmp; tmp=1; /* just to kill a gcc 2.95 warning */ asm volatile( ".set noreorder\n\t" "1: ll %1, %2 \n\t" " li %0, 1 \n\t" " sc %0, %2 \n\t" " beqz %0, 1b \n\t" " nop \n\t" ".set reorder\n\t" : "=&r" (tmp), "=&r" (val), "=m" (*lock) : "0" (tmp), "2" (*lock) : "cc" ); #elif defined __CPU_alpha long tmp; tmp=0; /* lock low bit set to 1 when the lock is hold and to 0 otherwise */ asm volatile( "1: ldl %0, %1 \n\t" " blbs %0, 2f \n\t" /* optimization if locked */ " ldl_l %0, %1 \n\t" " blbs %0, 2f \n\t" " lda %2, 1 \n\t" /* or: or $31, 1, %2 ??? */ " stl_c %2, %1 \n\t" " beq %2, 1b \n\t" " mb \n\t" "2: \n\t" :"=&r" (val), "=m"(*lock), "=r"(tmp) :"1"(*lock) /* warning on gcc 3.4: replace it with m or remove it and use +m in the input line ? */ : "memory" ); #else #error "unknown architecture" #endif return val; } /*! \brief * Set a lock. * \param lock the lock that should be set * \see tsl */ #ifndef DBG_LOCK inline static void get_lock(fl_lock_t* lock) { #else inline static void get_lock(fl_lock_t* lock_struct, const char* file, const char* func, unsigned int line) { volatile int *lock = &lock_struct->lock; #endif #ifdef ADAPTIVE_WAIT int i=ADAPTIVE_WAIT_LOOPS; #endif while(tsl(lock)){ #ifdef BUSY_WAIT #elif defined ADAPTIVE_WAIT if (i>0) i--; else sched_yield(); #else sched_yield(); #endif } #ifdef DBG_LOCK lock_struct->file = (char*)file; lock_struct->func = (char*)func; lock_struct->line = line; #endif } /*! \brief * Release a lock * \param lock the lock that should be released */ #ifndef DBG_LOCK inline static void release_lock(fl_lock_t* lock) { #else inline static void release_lock(fl_lock_t* lock_struct) { volatile int *lock = &lock_struct->lock; lock_struct->file = 0; lock_struct->func = 0; lock_struct->line = 0; #endif #if defined(__CPU_i386) || defined(__CPU_x86_64) /* char val; val=0; */ asm volatile( " movb $0, (%0)" : /*no output*/ : "r"(lock): "memory" /*" xchg %b0, %1" : "=q" (val), "=m" (*lock) : "0" (val) : "memory"*/ ); #elif defined(__CPU_sparc64) || defined(__CPU_sparc) asm volatile( #ifndef NOSMP "membar #LoadStore | #StoreStore \n\t" /*is this really needed?*/ #endif "stb %%g0, [%0] \n\t" : /*no output*/ : "r" (lock) : "memory" ); #elif defined __CPU_arm asm volatile( " str %0, [%1] \n\r" : /*no outputs*/ : "r"(0), "r"(lock) : "memory" ); #elif defined(__CPU_ppc) || defined(__CPU_ppc64) asm volatile( /* "sync\n\t" lwsync is faster and will work * here too * [IBM Programming Environments Manual, D.4.2.2] */ "lwsync\n\t" "stw %0, 0(%1)\n\t" : /* no output */ : "r"(0), "b" (lock) : "memory" ); *lock = 0; #elif defined(__CPU_mips2) || defined(__CPU_mips32) || defined(__CPU_mips64) asm volatile( ".set noreorder \n\t" " sync \n\t" " sw $0, %0 \n\t" ".set reorder \n\t" : /*no output*/ : "m" (*lock) : "memory" ); #elif defined __CPU_alpha asm volatile( " mb \n\t" " stl $31, %0 \n\t" : "=m"(*lock) :/* no input*/ : "memory" /* because of the mb */ ); #else #error "unknown architecture" #endif } #endif opensips-2.2.2/flags.c000066400000000000000000000117771300170765700146400ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2006-12-22 added script flags (bogdan) */ /*! * \file * \brief OpenSIPS configuration flag functions. */ #include "sr_module.h" #include "dprint.h" #include "parser/msg_parser.h" #include "mem/mem.h" #include "ut.h" #include "flags.h" /* several lists of maximum MAX_FLAG flags */ struct flag_entry *flag_lists[FLAG_LIST_COUNT]; /* buffer used to offer string representations of flag bitmasks */ static char print_buffer[PRINT_BUFFER_SIZE]; /*********************** msg flags ****************************/ int setflag(struct sip_msg* msg, flag_t flag) { #ifdef EXTRA_DEBUG LM_DBG("mflags for %p : (%u, %u)\n", msg, flag, msg->flags); #endif msg->flags |= 1 << flag; return 1; } int resetflag(struct sip_msg* msg, flag_t flag) { #ifdef EXTRA_DEBUG LM_DBG("mflags for %p : (%u, %u)\n", msg, flag, msg->flags); #endif msg->flags &= ~ (1 << flag); return 1; } int isflagset(struct sip_msg* msg, flag_t flag) { #ifdef EXTRA_DEBUG LM_DBG("mflags for %p : (%u, %u)\n", msg, flag, msg->flags); #endif return (msg->flags & (1< MAX_FLAG ) { LM_ERR("message flag (%d) must be in range %d..%d\n", flag, 1, MAX_FLAG ); return 0; } return 1; } int flag_idx2mask(int *flag) { if (*flag<0) { *flag = 0; } else if (*flag>(int)MAX_FLAG) { LM_ERR("flag %d out of range\n",*flag); return -1; } else { *flag = 1<<(*flag); } return 0; } str bitmask_to_flag_list(enum flag_type type, int bitmask) { struct flag_entry *entry; str ret; #ifdef EXTRA_DEBUG LM_DBG("bitmask -> %u\n", bitmask); #endif ret.s = print_buffer; ret.len = 0; for (entry = flag_lists[type]; entry; entry = entry->next) { if (bitmask & (1 << entry->bit)) { memcpy(ret.s + ret.len, entry->name.s, entry->name.len); ret.len += entry->name.len; ret.s[ret.len++] = FLAG_DELIM; } } if (ret.len > 0) ret.len--; return ret; } int flag_list_to_bitmask(str *flags, enum flag_type type, char delim) { char *p, *lim; char *crt_flag; str name; struct flag_entry *e; int ret = 0; if (flags->len < 0) return 0; #ifdef EXTRA_DEBUG LM_DBG("flag_list -> '%.*s'\n", flags->len, flags->s); #endif lim = flags->s + flags->len; crt_flag = flags->s; for (p = flags->s; p <= lim; p++) { if (p == lim || *p == delim) { name.s = crt_flag; name.len = p - crt_flag; for (e = flag_lists[type]; e; e = e->next) { if (e->name.len == p - crt_flag && str_strcmp(&e->name, &name) == 0) { ret |= 1 << e->bit; break; } } crt_flag = p + 1; } } return ret; } /** * The function MUST be called only in the pre-forking phases of OpenSIPS * (mod_init() or in function fixups) */ int get_flag_id_by_name(int flag_type, char *flag_name) { struct flag_entry *it, **flag_list; str fn; if (!flag_name) { LM_DBG("Flag name is null!\n"); return -1; } fn.s = flag_name; fn.len = strlen(flag_name); if (fn.len == 0) { LM_WARN("found empty string flag modparam! possible scripting error?\n"); return -1; } if (flag_type < 0 || flag_type >= FLAG_LIST_COUNT) { LM_ERR("Invalid flag list: %d\n", flag_type); return -2; } flag_list = flag_lists + flag_type; if (*flag_list && (*flag_list)->bit == MAX_FLAG) { LM_CRIT("Maximum number of message flags reached! (32 flags)\n"); return E_CFG; } /* Check if flag has been already defined */ for (it = *flag_list; it; it = it->next) { if (str_strcmp(&it->name, &fn) == 0) { return it->bit; } } if (!(it = pkg_malloc(sizeof(*it) + fn.len))) { LM_CRIT("Out of memory!\n"); return E_OUT_OF_MEM; } it->name.s = (char *)(it + 1); it->name.len = fn.len; memcpy(it->name.s, fn.s, fn.len); it->bit = (*flag_list ? (*flag_list)->bit + 1 : 0); it->next = *flag_list; *flag_list = it; LM_DBG("New flag: [ %.*s : %d ][%d]\n", fn.len, fn.s, it->bit, flag_type); return it->bit; } unsigned int fixup_flag(int flag_type, str *flag_name) { int ret; ret = get_flag_id_by_name(flag_type, flag_name->s); if (ret < 0) { LM_CRIT("Failed to get a flag id!\n"); return NAMED_FLAG_ERROR; } if (flag_type != FLAG_TYPE_MSG) return 1 << ret; return ret; } opensips-2.2.2/flags.h000066400000000000000000000045031300170765700146320ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Flag management */ #ifndef _FLAGS_H #define _FLAGS_H #include typedef unsigned int flag_t; #define MAX_FLAG ((unsigned int)( sizeof(flag_t) * CHAR_BIT - 1 )) #define PRINT_BUFFER_SIZE 2048 #define NAMED_FLAG_ERROR 33 #define FLAG_DELIM ' ' #define fix_flag_name(_s, _flag) \ do { \ if (!_s && (int)(_flag) > 0) { \ LM_WARN("Integer flags are now deprecated! " \ "Use unique quoted strings!\n"); \ _s = int2str(_flag, NULL); \ } \ } while (0) enum flag_type { FLAG_TYPE_MSG=0, FLAG_TYPE_BRANCH, FLAG_LIST_COUNT, }; struct sip_msg; struct flag_entry { str name; int bit; /* 0 .. 31 */ struct flag_entry *next; }; int flag_in_range( flag_t flag ); int setflag( struct sip_msg* msg, flag_t flag ); int resetflag( struct sip_msg* msg, flag_t flag ); int isflagset( struct sip_msg* msg, flag_t flag ); int flag_idx2mask(int *flag); /** * returns a string representation of the named flags set in the bitmask * * Note: prints data in a static buffer, flags are delimited by FLAG_DELIM */ str bitmask_to_flag_list(enum flag_type type, int bitmask); /** * parses a list of named flags and returns the corresponding bitmask * * Note: flags which are not used at script level and are not instantiated with * get_flag_id_by_name will be ignored */ int flag_list_to_bitmask(str *flags, enum flag_type type, char delim); unsigned int fixup_flag(int flag_type, str *flag_name); int get_flag_id_by_name(int flag_type, char *flag_name); #endif opensips-2.2.2/forward.c000066400000000000000000000363511300170765700152030ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2005-2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2001-??-?? created by andrei * ????-??-?? lots of changes by a lot of people * 2003-01-23 support for determination of outbound interface added : * get_out_socket (jiri) * 2003-01-24 reply to rport support added, contributed by * Maxim Sobolev and modified by andrei * 2003-02-11 removed calls to upd_send & tcp_send & replaced them with * calls to msg_send (andrei) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-02 fixed get_send_socket for tcp fwd to udp (andrei) * 2003-04-03 added su_setport (andrei) * 2003-04-04 update_sock_struct_from_via now differentiates between * local replies & "normal" replies (andrei) * 2003-04-12 update_sock_struct_from via uses also FL_FORCE_RPORT for * local replies (andrei) * 2003-08-21 check_self properly handles ipv6 addresses & refs (andrei) * 2003-10-21 check_self updated to handle proto (andrei) * 2003-10-24 converted to the new socket_info lists (andrei) * 2004-10-10 modified check_self to use grep_sock_info (andrei) * 2004-11-08 added force_send_socket support in get_send_socket (andrei) * 2006-09-06 added new algorithm for building VIA branch parameter for * stateless requests - it complies to RFC3261 requirement to be * unique through time and space (bogdan) */ /*! * \file * \brief OpenSIPS Stateless forward support */ #include #include #include #include #include #include #include #include #include #include "forward.h" #include "parser/msg_parser.h" #include "parser/parse_from.h" #include "dprint.h" #include "ut.h" #include "dset.h" #include "mem/mem.h" #include "msg_translator.h" #include "sr_module.h" #include "ip_addr.h" #include "resolve.h" #include "net/trans.h" #include "name_alias.h" #include "socket_info.h" #include "core_stats.h" #include "blacklists.h" #include "msg_callbacks.h" #include "md5utils.h" /*! \brief return a socket_info_pointer to the sending socket * \note As opposed to * get_send_socket(), which returns process's default socket, get_out_socket * attempts to determine the outbound interface which will be used; * it creates a temporary connected socket to determine it; it will * be very likely noticeably slower, but it can deal better with * multihomed hosts */ struct socket_info* get_out_socket(union sockaddr_union* to, int proto) { int temp_sock; socklen_t len; union sockaddr_union from; struct socket_info* si; struct ip_addr ip; if (proto!=PROTO_UDP) { LM_CRIT("can only be called for UDP\n"); return 0; } temp_sock=socket(to->s.sa_family, SOCK_DGRAM, 0 ); if (temp_sock==-1) { LM_ERR("socket() failed: %s\n", strerror(errno)); return 0; } if (connect(temp_sock, &to->s, sockaddru_len(*to))==-1) { LM_ERR("connect failed: %s\n", strerror(errno)); goto error; } len=sizeof(from); if (getsockname(temp_sock, &from.s, &len)==-1) { LM_ERR("getsockname failed: %s\n", strerror(errno)); goto error; } su2ip_addr(&ip, &from); si=find_si(&ip, 0, proto); if (si==0) goto error; close(temp_sock); LM_DBG("socket determined: %p\n", si ); return si; error: LM_ERR("no socket found\n"); close(temp_sock); return 0; } /*! \brief returns a socket_info pointer to the sending socket or 0 on error * \param msg SIP message (can be null) * \param to destination socket_union pointer * \param proto protocol * * \note if msg!=null and msg->force_send_socket, the force_send_socket will be used */ struct socket_info* get_send_socket(struct sip_msg *msg, union sockaddr_union* to, int proto) { struct socket_info* send_sock; /* check if send interface is not forced */ if (msg && msg->force_send_socket){ if (msg->force_send_socket->proto!=proto){ LM_DBG("force_send_socket of different proto (%d)!\n", proto); msg->force_send_socket=find_si(&(msg->force_send_socket->address), msg->force_send_socket->port_no, proto); } if (msg->force_send_socket && (msg->force_send_socket->socket!=-1)) return msg->force_send_socket; else{ if (msg->force_send_socket && msg->force_send_socket->socket==-1) LM_WARN("not listening on the requested socket, no fork mode?\n"); else LM_WARN("protocol/port mismatch\n"); } }; if (mhomed && proto==PROTO_UDP){ send_sock=get_out_socket(to, proto); if ((send_sock==0) || (send_sock->socket!=-1)) return send_sock; /* found or error*/ else if (send_sock->socket==-1){ LM_WARN("not listening on the requested socket, no fork mode?\n"); /* continue: try to use some socket */ } } send_sock=0; /* check if we need to change the socket (different address families - * eg: ipv4 -> ipv6 or ipv6 -> ipv4) */ switch(proto){ case PROTO_UDP: if (msg && msg->rcv.bind_address->address.af==to->s.sa_family && msg->rcv.bind_address->proto==PROTO_UDP) { send_sock = msg->rcv.bind_address; break; } /* default logic for all protos */ default: /* we don't really now the sending address (we can find it out, * but we'll need also to see if we listen on it, and if yes on * which port -> too complicated*/ send_sock = (to->s.sa_family==AF_INET) ? protos[proto].sendipv4 : protos[proto].sendipv6; } return send_sock; } /*! \brief checks if the proto: host:port is one of the address we listen on * * if port==0, the port number is ignored * if proto==0 (PROTO_NONE) the protocol is ignored * returns 1 if true, 0 if false, -1 on error * WARNING: uses str2ip6 so it will overwrite any previous * unsaved result of this function (static buffer) */ int check_self(str* host, unsigned short port, unsigned short proto) { if (grep_sock_info(host, port, proto)) goto found; /* try to look into the aliases*/ if (grep_aliases(host->s, host->len, port, proto)==0){ LM_DBG("host != me\n"); return 0; } found: return 1; } static inline int set_sl_branch(struct sip_msg* msg) { struct hdr_field *h_via; struct via_body *b_via; str *branch; int via_parsed; char b_md5[MD5_LEN]; via_parsed = 0; branch = 0; /* first VIA header must be parsed */ for( h_via=msg->h_via1 ; h_via ; h_via=h_via->sibling ) { b_via = (struct via_body*)h_via->parsed; for( ; b_via ; b_via=b_via->next ) { /* check if there is any valid branch param */ if (b_via->branch==0 || b_via->branch->value.s==0 || b_via->branch->value.len==0 ) continue; branch = &b_via->branch->value; /* check if the branch param has the magic cookie */ if (branch->len <= (int)MCOOKIE_LEN || memcmp( branch->s, MCOOKIE, MCOOKIE_LEN)!=0 ) continue; /* found a statefull branch -> use it */ goto found; } if (!via_parsed) { if ( parse_headers(msg,HDR_EOH_F,0)<0 ) { LM_ERR("failed to parse all hdrs\n"); return -1; } via_parsed = 1; } } /* no statefull branch :(.. -> use the branch from the last via */ found: if (branch==NULL) { /* no branch found :(.. -> try to use the From TAG param as * a value to seed the MD5 - the From TAG is per call, so it gives * a bit of uniqueness; if this is empty, as a last resort, use the * FROM URI (it cannot mis) */ if ( parse_from_header(msg)!=0 ) { LM_ERR("failed to extract FROM header\n"); return -1; } if ( get_from(msg)->tag_value.len ) branch = &get_from(msg)->tag_value; else branch = &get_from(msg)->uri; } /* make an MD5 over the found branch, to ensure a controlable * length of the resulting branch */ MD5StringArray ( b_md5, branch, 1 ); /* and make a hash over transaction-related values */ if ( parse_headers(msg, HDR_CALLID_F|HDR_CSEQ_F,0)==-1 || msg->callid==NULL || msg->cseq==NULL ) { LM_ERR("failed to extract CALLID or CSEQ hdr from SIP msg\n"); return -1; } /* build the new branch */ if (branch_builder( core_hash( &msg->callid->body, &get_cseq(msg)->number, 1<<16 ), 0 /*labled - not used here */, b_md5, 0 /*branch - not used here */, msg->add_to_branch_s, &msg->add_to_branch_len )==0 ) { LM_ERR("branch_builder failed to construct the branch\n"); return -1; } return 0; } int forward_request( struct sip_msg* msg, struct proxy_l * p) { union sockaddr_union to; str buf; struct socket_info* send_sock; struct socket_info* last_sock; buf.s=NULL; /* calculate branch for outbound request - if the branch buffer is already * set (maybe by an upper level as TM), used it; otherwise computes * the branch for stateless fwd. . According to the latest discussions * on the topic, you should reuse the latest statefull branch * --bogdan */ if ( msg->add_to_branch_len==0 ) { if (set_sl_branch(msg)!=0) { LM_ERR("unable to compute and add stateless VIA branch\n"); goto error; } } msg_callback_process(msg, REQ_PRE_FORWARD, (void *)p); hostent2su( &to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT); last_sock = 0; if (getb0flags(msg) & tcp_no_new_conn_bflag) tcp_no_new_conn = 1; do { send_sock=get_send_socket( msg, &to, p->proto); if (send_sock==0){ LM_ERR("cannot forward to af %d, proto %d no corresponding" "listening socket\n", to.s.sa_family, p->proto); ser_error=E_NO_SOCKET; continue; } if ( last_sock!=send_sock ) { if (buf.s) pkg_free(buf.s); buf.s = build_req_buf_from_sip_req( msg, (unsigned int*)&buf.len, send_sock, p->proto, 0 /*flags*/); if (!buf.s){ LM_ERR("building req buf failed\n"); tcp_no_new_conn = 0; goto error; } last_sock = send_sock; } if (check_blacklists( p->proto, &to, buf.s, buf.len)) { LM_DBG("blocked by blacklists\n"); ser_error=E_IP_BLOCKED; continue; } /* send it! */ LM_DBG("sending:\n%.*s.\n", buf.len, buf.s); LM_DBG("orig. len=%d, new_len=%d, proto=%d\n", msg->len, buf.len, p->proto ); if (msg_send(send_sock, p->proto, &to, 0, buf.s, buf.len, msg)<0){ ser_error=E_SEND; continue; } slcb_run_req_out( msg, &buf, &to, send_sock, p->proto); ser_error = 0; break; }while( get_next_su( p, &to, (ser_error==E_IP_BLOCKED)?0:1)==0 ); tcp_no_new_conn = 0; if (ser_error) { update_stat( drp_reqs, 1); goto error; } /* sent requests stats */ update_stat( fwd_reqs, 1); pkg_free(buf.s); /* received_buf & line_buf will be freed in receive_msg by free_lump_list*/ return 0; error: if (buf.s) pkg_free(buf.s); return -1; } int update_sock_struct_from_via( union sockaddr_union* to, struct sip_msg* msg, struct via_body* via ) { struct hostent* he; str* name; int err; unsigned short port; port=0; if(via==msg->via1){ /* _local_ reply, we ignore any rport or received value * (but we will send back to the original port if rport is * present) */ if ((msg->msg_flags&FL_FORCE_RPORT)||(via->rport)) port=msg->rcv.src_port; else port=via->port; if(via->maddr) name= &(via->maddr->value); else name=&(via->host); /* received=ip in 1st via is ignored (it's not added by us so it's bad) */ }else{ /* "normal" reply, we use rport's & received value if present */ if (via->rport && via->rport->value.s){ LM_DBG("using 'rport'\n"); port=str2s(via->rport->value.s, via->rport->value.len, &err); if (err){ LM_NOTICE("bad rport value(%.*s)\n", via->rport->value.len,via->rport->value.s); port=0; } } if (via->maddr){ name= &(via->maddr->value); if (port==0) port=via->port?via->port:SIP_PORT; } else if (via->received){ LM_DBG("using 'received'\n"); name=&(via->received->value); /* making sure that we won't do SRV lookup on "received" */ if (port==0) port=via->port?via->port:SIP_PORT; }else{ LM_DBG("using via host\n"); name=&(via->host); if (port==0) port=via->port; } } LM_DBG("trying SRV lookup\n"); he=sip_resolvehost(name, &port, &via->proto, 0, 0); if (he==0){ LM_NOTICE("resolve_host(%.*s) failure\n", name->len, name->s); return -1; } hostent2su( to, he, 0, port); return 1; } /*! \brief removes first via & sends msg to the second */ int forward_reply(struct sip_msg* msg) { char* new_buf; union sockaddr_union* to; unsigned int new_len; struct sr_module *mod; int proto; int id; /* used only by tcp*/ struct socket_info *send_sock; char* s; int len; to=0; id=0; new_buf=0; /*check if first via host = us */ if (check_via){ if (check_self(&msg->via1->host, msg->via1->port?msg->via1->port:SIP_PORT, msg->via1->proto)!=1){ LM_ERR("host in first via!=me : %.*s:%d\n", msg->via1->host.len, msg->via1->host.s, msg->via1->port); /* send error msg back? */ goto error; } } /* quick hack, slower for multiple modules*/ for (mod=modules;mod;mod=mod->next){ if ((mod->exports) && (mod->exports->response_f)){ LM_DBG("found module %s, passing reply to it\n", mod->exports->name); if (mod->exports->response_f(msg)==0) goto skip; } } /* if stateless fwd was disabled, we cannot have stateless replies here*/ if (sl_fwd_disabled) goto skip; /* we have to forward the reply stateless, so we need second via -bogdan*/ if (parse_headers( msg, HDR_VIA2_F, 0 )==-1 || (msg->via2==0) || (msg->via2->error!=PARSE_OK)) { /* no second via => error */ LM_ERR("no 2nd via found in reply\n"); goto error; } to=(union sockaddr_union*)pkg_malloc(sizeof(union sockaddr_union)); if (to==0){ LM_ERR("out of pkg memory\n"); goto error; } proto=msg->via2->proto; if (update_sock_struct_from_via( to, msg, msg->via2 )==-1) goto error; if (is_tcp_based_proto(proto)){ /* find id in i param if it exists */ if (msg->via1->i&&msg->via1->i->value.s){ s=msg->via1->i->value.s; len=msg->via1->i->value.len; id=reverse_hex2int(s, len); } } send_sock = get_send_socket(msg, to, proto); new_buf = build_res_buf_from_sip_res( msg, &new_len, send_sock,0); if (!new_buf){ LM_ERR("failed to build rpl from req failed\n"); goto error; } if (msg_send(send_sock, proto, to, id, new_buf, new_len, msg)<0) { update_stat( drp_rpls, 1); goto error0; } update_stat( fwd_rpls, 1); /* * If no port is specified in the second via, then this * message output a wrong port number - zero. Despite that * the correct port is choosen in update_sock_struct_from_via, * as its visible with su_getport(to); . */ LM_DBG("reply forwarded to %.*s:%d\n", msg->via2->host.len, msg->via2->host.s, (unsigned short) msg->via2->port); pkg_free(new_buf); pkg_free(to); skip: return 0; error: update_stat( err_rpls, 1); error0: if (new_buf) pkg_free(new_buf); if (to) pkg_free(to); return -1; } opensips-2.2.2/forward.h000066400000000000000000000101141300170765700151750ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2001-??-?? created by andrei * ????-??-?? lots of changes by a lot of people * 2003-02-11 added inline msg_send (andrei) * 2003-04-07 changed all ports to host byte order (andrei) * 2003-04-12 FORCE_RPORT_T added (andrei) * 2003-04-15 added tcp_disable support (andrei) */ /*! * \file * \brief OpenSIPS Stateless forward support */ #ifndef forward_h #define forward_h #include "globals.h" #include "mem/mem.h" #include "parser/msg_parser.h" #include "route.h" #include "proxy.h" #include "ip_addr.h" #include "script_cb.h" #include "sl_cb.h" #include "net/trans.h" struct socket_info* get_send_socket(struct sip_msg* msg, union sockaddr_union* su, int proto); struct socket_info* get_out_socket(union sockaddr_union* to, int proto); int check_self(str* host, unsigned short port, unsigned short proto); int forward_request( struct sip_msg* msg, struct proxy_l* p); int update_sock_struct_from_via( union sockaddr_union* to, struct sip_msg* msg, struct via_body* via ); /*! \brief use src_ip, port=src_port if rport, via port if via port, 5060 otherwise */ #define update_sock_struct_from_ip( to, msg ) \ init_su((to), &(msg)->rcv.src_ip, \ ((!msg->via1)||((msg)->via1->rport)||((msg)->msg_flags&FL_FORCE_RPORT))? \ (msg)->rcv.src_port: \ ((msg)->via1->port)?(msg)->via1->port: SIP_PORT ) int forward_reply( struct sip_msg* msg); /*! \brief * * \param send_sock = 0 if already known (e.g. for udp in some cases), * non-0 otherwise * \param proto =TCP|UDP * \param to = sockaddr-like description of the destination * \param id - only used on tcp, it will force sending on connection "id" * if id!=0 and the connection exists, else it will send to "to" * (useful for sending replies on the same connection as the request * that generated them; use 0 if you don't want this) * \param buf - the buffer containing the message to be sent * \param len - the length of the message to be sent * \return 0 if ok, -1 on error */ static inline int msg_send( struct socket_info* send_sock, int proto, union sockaddr_union* to, int id, char* buf, int len, struct sip_msg* msg) { str out_buff; if (proto<=PROTO_NONE || proto>=PROTO_OTHER) { LM_BUG("bogus proto %d received!\n",proto); return -1; } if (protos[proto].id==PROTO_NONE) { LM_BUG("using proto %d which is not init!\n",proto); return -1; } out_buff.len = len; out_buff.s = buf; /* determin the send socket */ if (send_sock==0) send_sock=get_send_socket(0, to, proto); if (send_sock==0){ LM_ERR("no sending socket found for proto %d\n", proto); goto error; } /* the raw processing callbacks are free to change whatever inside * the buffer further use out_buff.s and at the end try to free out_buff.s * if changed by callbacks */ if (proto != PROTO_BIN) run_post_raw_processing_cb(POST_RAW_PROCESSING,&out_buff, msg); /* update the length for further processing */ len = out_buff.len; if (protos[proto].tran.send(send_sock, out_buff.s, out_buff.len, to, id)<0){ LM_ERR("send() for proto %d failed\n",proto); goto error; } /* potentially allocated by the out raw processing */ if (out_buff.s != buf) pkg_free(out_buff.s); return 0; error: if (out_buff.s != buf) pkg_free(out_buff.s); return -1; } #endif opensips-2.2.2/futex_lock.h000066400000000000000000000162521300170765700157050ustar00rootroot00000000000000/* * Copyright (C) 2012-2013 Ryan Bullock * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2012-09-04 created by Ryan Bullock */ /*! * \file * \brief Support for Linux futex locks * * Implementation based off http://people.redhat.com/drepper/futex.pdf * Modified to add support for an adapative spinlock before sleeping on the lock * * Contains the assembler routines for the fast architecture dependend * locking primitives used by the server. This routines are needed e.g. * to protect shared data structures that are accessed from muliple processes. * \todo replace this with the assembler routines provided by the linux kernel * * Uses GCC atomic builtin for atomic cmpxchg operation */ #ifndef futex_lock_h #define futex_lock_h #include #include #include /*! The actual lock */ #ifndef DBG_LOCK typedef volatile int fx_lock_t; #else typedef struct fx_lock_t_{ volatile int lock; char* file; char* func; unsigned long line; } fx_lock_t; #endif /* * Possible Lock values: * 0 - Not locked * 1 - Locked, but no other processes sleeping on lock * 2 - Locked and other processes sleeping on lock (requires a call to futex() with FUTEX_WAKUP on unlock) */ /*! Initialize a lock, zero is unlocked. */ #ifndef DBG_LOCK #define init_lock( l ) (l)=0 #else #define init_lock( l ) (l).lock = 0 #endif /* * Wait on a futex * param lock - futex to wait on * param val - value to check against lock */ #define futex_wait(lock, val) syscall(SYS_futex, lock, FUTEX_WAIT, val, 0, 0, 0) /* * Wake up waiters * param lock - futex to wake up * param val - number of processes to wakeup */ #define futex_wake(lock, val) syscall(SYS_futex, lock, FUTEX_WAKE, val, 0, 0 ,0) /* * Atomic cmpxchg operation * Conditionally sets lock to newval if current value of lock is oldval * param lock is lock to check/set * param oldval is the value that must be in lock for newval to be set * param newval is the new value to assign to lock if lock contains oldval * returns value of lock before the operation */ #define atomic_cmpxchg(lock, oldval, newval) __sync_val_compare_and_swap(lock, oldval, newval) /* * Atomic xchg operation * Adapted tsl() from fastlock.h * Used as fall back from gcc atomic builtin for unsupported targets * Atomically writes value into lock, returning the previously value of lock * param lock is lock to set * param val is the value to write to the lock * returns previous value of lock */ #ifndef DBG_LOCK inline static int _atomic_xchg(fx_lock_t* lock, int val) #else inline static int _atomic_xchg(volatile int *lock, int val) #endif { #if defined(__CPU_i386) || defined(__CPU_x86_64) #ifdef NOSMP asm volatile( " btsl $0, %1 \n\t" " adcl $0, %0 \n\t" : "=q" (val), "=m" (*lock) : "0"(val) : "memory", "cc" /* "cc" */ ); #else asm volatile( " xchg %1, %0" : "=q" (val), "=m" (*lock) : "0" (val) : "memory" ); #endif /*NOSMP*/ #elif defined(__CPU_sparc64) || defined(__CPU_sparc) asm volatile( "ldstub [%1], %0 \n\t" #ifndef NOSMP "membar #StoreStore | #StoreLoad \n\t" #endif : "=r"(val) : "r"(lock):"memory" ); #elif defined __CPU_arm asm volatile( "# here \n\t" "swpb %0, %1, [%2] \n\t" : "=&r" (val) : "r"(1), "r" (lock) : "memory" ); #elif defined(__CPU_ppc) || defined(__CPU_ppc64) asm volatile( "1: lwarx %0, 0, %2\n\t" " cmpwi %0, 0\n\t" " bne 0f\n\t" " stwcx. %1, 0, %2\n\t" " bne- 1b\n\t" " lwsync\n\t" /* lwsync or isync, lwsync is faster and should work, see [ IBM Programming environments Manual, D.4.1.1] */ "0:\n\t" : "=r" (val) : "r"(1), "b" (lock) : "memory", "cc" ); #elif defined(__CPU_mips2) || defined(__CPU_mips32) || defined(__CPU_mips64) long tmp; tmp=1; /* just to kill a gcc 2.95 warning */ asm volatile( ".set noreorder\n\t" "1: ll %1, %2 \n\t" " li %0, 1 \n\t" " sc %0, %2 \n\t" " beqz %0, 1b \n\t" " nop \n\t" ".set reorder\n\t" : "=&r" (tmp), "=&r" (val), "=m" (*lock) : "0" (tmp), "2" (*lock) : "cc" ); #elif defined __CPU_alpha long tmp; tmp=0; /* lock low bit set to 1 when the lock is hold and to 0 otherwise */ asm volatile( "1: ldl %0, %1 \n\t" " blbs %0, 2f \n\t" /* optimization if locked */ " ldl_l %0, %1 \n\t" " blbs %0, 2f \n\t" " lda %2, 1 \n\t" /* or: or $31, 1, %2 ??? */ " stl_c %2, %1 \n\t" " beq %2, 1b \n\t" " mb \n\t" "2: \n\t" :"=&r" (val), "=m"(*lock), "=r"(tmp) :"1"(*lock) /* warning on gcc 3.4: replace it with m or remove it and use +m in the input line ? */ : "memory" ); #else #error "unknown architecture" #endif return val; } /* * Atomic xchg * Use gcc atomic builtin on supported platforms, fail back to assembly * might want to also check for compiler support */ #if !defined(NOSMP) && (defined(__CPU_i386) || defined(__CPU_x86_64)) #define atomic_xchg(lock, val) __sync_lock_test_and_set(lock, val) #else #define atomic_xchg(lock, val) _atomic_xchg(lock, val) #endif /*! \brief * Get a lock. * \param lock the lock that should be gotten */ #ifndef DBG_LOCK inline static void get_lock(fx_lock_t* lock) { #else inline static void get_lock(fx_lock_t* lock_struct, const char* file, const char* func, unsigned int line) { volatile int *lock = &lock_struct->lock; #endif int c; #ifdef ADAPTIVE_WAIT register int i = ADAPTIVE_WAIT_LOOPS; #endif //Getting lock failed if ((c = atomic_cmpxchg(lock, 0, 1)) != 0) { //Ensure a wakeup gets scheduled if (c != 2) { #ifdef ADAPTIVE_WAIT //No sleepers on the lock, try spinning for a bit first while(i > 0) { if ((c = atomic_cmpxchg(lock, 0, 1)) == 0) { return; } i--; } #endif //Going to need a wakeup c = atomic_xchg(lock, 2); } //Wait for wakeup while (c != 0) { futex_wait(lock, 2); c = atomic_xchg(lock, 2); } } #ifdef DBG_LOCK lock_struct->file = (char*)file; lock_struct->func = (char*)func; lock_struct->line = line; #endif } /*! \brief * Release a lock * \param lock the lock that should be released */ #ifndef DBG_LOCK inline static void release_lock(fx_lock_t* lock) { #else inline static void release_lock(fx_lock_t* lock_struct) { volatile int *lock = &lock_struct->lock; #endif int c; #ifdef DBG_LOCK lock_struct->file = NULL; lock_struct->func = NULL; lock_struct->line = 0; #endif c = atomic_xchg(lock, 0); //Only do wakekup if others are waiting on the lock (value of 2) if (c != 1) { futex_wake(lock, 1); } } #endif opensips-2.2.2/globals.h000066400000000000000000000075061300170765700151670ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Global variables */ #ifndef globals_h #define globals_h #include "ip_addr.h" #include "str.h" #include "poll_types.h" #define DO_DNS 1 #define DO_REV_DNS 2 extern char * cfg_file; extern int config_check; extern char *stat_file; extern char* pid_file; extern char* pgid_file; extern struct socket_info* bind_address; /*!< pointer to the crt. proc. listening address */ extern int auto_aliases; extern unsigned int maxbuffer; extern int children_no; extern enum poll_types io_poll_method; /* TCP network layer related parameters */ extern int tcp_children_no; extern int tcp_disable; extern int tcp_accept_aliases; extern int tcp_connect_timeout; extern int tcp_con_lifetime; /*!< connection lifetime */ extern int tcp_listen_backlog; extern int tcp_max_fd_no; extern int tcp_max_connections; extern int tcp_keepalive; extern int tcp_keepcount; extern int tcp_keepidle; extern int tcp_keepinterval; extern int tcp_max_msg_time; extern int tcp_no_new_conn; extern int tcp_no_new_conn_bflag; extern int no_daemon_mode; extern int debug_mode; extern int check_via; extern int received_dns; extern int sip_warning; extern int server_signature; extern str server_header; extern str user_agent_header; extern char* user; extern char* group; extern char* sock_user; extern char* sock_group; extern int sock_uid; extern int sock_gid; extern int sock_mode; extern char* chroot_dir; extern char* working_dir; #ifdef USE_MCAST extern int mcast_loopback; extern int mcast_ttl; #endif /* USE_MCAST */ extern int tos; extern int disable_dns_failover; extern int disable_dns_blacklist; extern int cfg_errors; extern unsigned long shm_mem_size; extern unsigned int shm_hash_split_percentage; extern unsigned int shm_hash_split_factor; extern unsigned int shm_secondary_hash_size; extern unsigned long pkg_mem_size; extern int reply_to_via; extern int is_main; extern int memlog; /*!< debugging level for printing memory debugs */ extern int memdump; /*!< debugging level for dumping memory status */ extern int execmsgthreshold; /*!< Maximum number of microseconds a SIP msg processing can last before triggering Warning log */ extern int execdnsthreshold; extern int tcpthreshold; extern int mhomed; /*!< looking up outbound interface ? */ extern int my_argc; /*!< command-line arguments */ extern char **my_argv; extern str default_global_address; /*!< pre-set addresses */ extern str default_global_port; /*!< pre-ser ports */ extern int disable_core_dump; /*!< core dump limits */ extern int open_files_limit; /*!< file limits */ extern int dns_retr_time; /*!< DNS resolver: Retry time */ extern int dns_retr_no; /*!< DNS resolver : Retry # */ extern int dns_servers_no; /*!< DNS resolver: Server no */ extern int dns_search_list; /*!< DNS resolver: Search list */ extern int max_while_loops; extern int sl_fwd_disabled; extern time_t startup_time; extern char *db_version_table; extern char *db_default_url; extern int db_max_async_connections; extern int disable_503_translation; extern int enable_asserts; extern int abort_on_assert; #endif opensips-2.2.2/hash_func.h000066400000000000000000000051051300170765700154730ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: *--------- * * 2006-01-20 - new_hash1() added; support for configurable hash size * added (bogdan) * 2006-03-13 - new_hash1() and new_hash2() merged into core_hash(); * added core_case_hash() for case insensitive hashes; * all TM dependet stuff moved to TM config file (bogdan) */ /*! * \file * \brief Hash functions */ #ifndef _HASH_FUNC_H_ #define _HASH_FUNC_H_ #include "str.h" #define ch_h_inc h+=v^(v>>3) #define ch_icase(_c) (((_c)>='A'&&(_c)<='Z')?((_c)|0x20):(_c)) static inline unsigned int core_hash(const str *s1, const str *s2, const unsigned int size) { char *p, *end; register unsigned v; register unsigned h; h=0; end=s1->s+s1->len; for ( p=s1->s ; p<=(end-4) ; p+=4 ){ v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; ch_h_inc; } v=0; for (; ps+s2->len; for (p=s2->s; p<=(end-4); p+=4){ v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; ch_h_inc; } v=0; for (; p>11))+((h>>13)+(h>>23)); return size?((h)&(size-1)):h; } static inline unsigned int core_case_hash( str *s1, str *s2, unsigned int size) { char *p, *end; register unsigned v; register unsigned h; h=0; end=s1->s+s1->len; for ( p=s1->s ; p<=(end-4) ; p+=4 ){ v=(ch_icase(*p)<<24)+(ch_icase(p[1])<<16)+(ch_icase(p[2])<<8) + ch_icase(p[3]); ch_h_inc; } v=0; for (; ps+s2->len; for (p=s2->s; p<=(end-4); p+=4){ v=(ch_icase(*p)<<24)+(ch_icase(p[1])<<16)+(ch_icase(p[2])<<8) + ch_icase(p[3]); ch_h_inc; } v=0; for (; p>11))+((h>>13)+(h>>23)); return size?((h)&(size-1)):h; } #endif opensips-2.2.2/help_msg.h000066400000000000000000000100251300170765700153300ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of OpenSIPS, a free SIP server. * * OpenSIPS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * OpenSIPS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! *\file * \brief The help message that is shown on the console with 'opensips -h' */ /*! \mainpage Developer documentation * * \section intro_sec Introduction * This is the OpenSIPS developer documentation. At the moment most information * here is autogenerated from the sources. Additional content will be added * over time, any help is of course welcome. * * This page tries to give some starting points for developers that want to * understand the server structure and create their own extensions or modules. * * \section db_sec Database interface * The server uses a own database interface to hide the differences of the * supported db engines. Every module that implementst this API can use all * database drivers, as long as they implement the needed capabilities. * * The file db.h defines the database API. * * \section start_sec Server start * The implementation of the server start process can be found in the * main.c file. * * \section datastruct_sec Important data structures * hdr_field - represents a SIP header \n * \ref _str "str" - contains text variables \n * * \todo incomplete * */ #ifndef HELP_MSG_H #define HELP_MSG_H #include "config.h" static char help_msg[]= "\ Usage: " NAME " -l address [-l address ...] [options]\n\ Options:\n\ -f file Configuration file (default " CFG_FILE ")\n\ -c Check configuration file for errors\n\ -C Similar to '-c' but in addition checks the flags of exported\n\ functions from included route blocks\n\ -l address Listen on the specified address/interface (multiple -l\n\ mean listening on more addresses). The address format is\n\ [proto:]addr[:port], where proto=udp|tcp and \n\ addr= host|ip_address|interface_name. E.g: -l locahost, \n\ -l udp:127.0.0.1:5080, -l eth0:5062 The default behavior\n\ is to listen on all the interfaces.\n\ -n processes Number of child processes to fork per interface\n\ (default: 8)\n\ -r Use dns to check if is necessary to add a \"received=\"\n\ field to a via\n\ -R Same as `-r` but use reverse dns;\n\ (to use both use `-rR`)\n\ -v Turn on \"via:\" host checking when forwarding replies\n\ -d Debugging mode (multiple -d increase the level)\n\ -D Do not fork into daemon mode\n\ -F Daemon mode, but leave main process foreground\n\ -E Log to stderr\n\ -N processes Number of tcp child processes (default: equal to `-n`)\n\ -W method poll method\n\ -V Version number\n\ -h This help message\n\ -b nr Maximum receive buffer size which will not be exceeded by\n\ auto-probing procedure even if OS allows\n\ -m nr Size of shared memory allocated in Megabytes\n\ -M nr Size of pkg memory allocated in Megabytes\n\ -w dir Change the working directory to \"dir\" (default \"/\")\n\ -t dir Chroot to \"dir\"\n\ -u uid Change uid \n\ -g gid Change gid \n\ -P file Create a pid file\n\ -G file Create a pgid file\n" ; #endif opensips-2.2.2/io_wait.c000066400000000000000000000403131300170765700151630ustar00rootroot00000000000000/* * Copyright (C) 2005 iptelorg GmbH * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-06-15 created by andrei * 2005-06-26 added kqueue (andrei) * 2005-07-04 added /dev/poll (andrei) */ /*! * \file * \brief OpenSIPS TCP IO wait common functions */ #ifdef HAVE_EPOLL #include /* close() */ #endif #ifdef HAVE_DEVPOLL #include /* open */ #include #include #include /* close, ioctl */ #endif #include /* uname() */ #include /* strtol() */ #include "io_wait.h" #include "mem/mem.h" #ifndef local_malloc #define local_malloc pkg_malloc #endif #ifndef local_free #define local_free pkg_free #endif char* poll_support="poll" #ifdef HAVE_EPOLL ", epoll_lt, epoll_et" #endif #ifdef HAVE_SIGIO_RT ", sigio_rt" #endif #ifdef HAVE_SELECT ", select" #endif #ifdef HAVE_KQUEUE ", kqueue" #endif #ifdef HAVE_DEVPOLL ", /dev/poll" #endif ; /*! supported poll methods */ char* poll_method_str[POLL_END]={ "none", "poll", "epoll_lt", "epoll_et", "sigio_rt", "select", "kqueue", "/dev/poll" }; #ifdef HAVE_SIGIO_RT static int _sigio_init=0; static int _sigio_crt_rtsig; static sigset_t _sigio_rtsig_used; #endif #ifdef HAVE_SIGIO_RT /*! * \brief sigio specific init * \param h IO handle * \param rsig real time signal * \return returns -1 on error, 0 on success */ static int init_sigio(io_wait_h* h, int rsig) { int r; int n; int signo; int start_sig; sigset_t oldset; if (!_sigio_init){ _sigio_init=1; _sigio_crt_rtsig=SIGRTMIN; sigemptyset(&_sigio_rtsig_used); } h->signo=0; if (rsig==0){ start_sig=_sigio_crt_rtsig; n=SIGRTMAX-SIGRTMIN; }else{ if ((rsig < SIGRTMIN) || (rsig >SIGRTMAX)){ LM_CRIT("real time signal %d out of" " range [%d, %d]\n", rsig, SIGRTMIN, SIGRTMAX); goto error; } start_sig=rsig; n=0; } sigemptyset(&h->sset); sigemptyset(&oldset); retry1: /* get current block mask */ if (sigprocmask(SIG_BLOCK, &h->sset, &oldset )==-1){ if (errno==EINTR) goto retry1; LM_ERR("1st sigprocmask failed: %s [%d]\n", strerror(errno), errno); /* try to continue */ } for (r=start_sig; r<=(n+start_sig); r++){ signo=(r>SIGRTMAX)?r-SIGRTMAX+SIGRTMIN:r; if (! sigismember(&_sigio_rtsig_used, signo) && ! sigismember(&oldset, signo)){ sigaddset(&_sigio_rtsig_used, signo); h->signo=signo; _sigio_crt_rtsig=(signosigno==0){ LM_CRIT("init_sigio: %s\n", rsig?"could not assign requested real-time signal": "out of real-time signals"); goto error; } LM_DBG("trying signal %d... \n", h->signo); if (sigaddset(&h->sset, h->signo)==-1){ LM_ERR("sigaddset failed for %d: %s [%d]\n", h->signo, strerror(errno), errno); goto error; } if (sigaddset(&h->sset, SIGIO)==-1){ LM_ERR("sigaddset failed for %d: %s [%d]\n", SIGIO, strerror(errno), errno); goto error; } retry: if (sigprocmask(SIG_BLOCK, &h->sset, 0)==-1){ if (errno==EINTR) goto retry; LM_ERR("sigprocmask failed: %s [%d]\n", strerror(errno), errno); goto error; } return 0; error: h->signo=0; sigemptyset(&h->sset); return -1; } /*! * \brief sigio specific destroy * \param h IO handle */ static void destroy_sigio(io_wait_h* h) { if (h->signo){ sigprocmask(SIG_UNBLOCK, &h->sset, 0); sigemptyset(&h->sset); sigdelset(&_sigio_rtsig_used, h->signo); h->signo=0; } } #endif #ifdef HAVE_EPOLL /*! * \brief epoll specific init * \param h IO handle * \return -1 on error, 0 on success */ static int init_epoll(io_wait_h* h) { again: h->epfd=epoll_create(h->max_fd_no); if (h->epfd==-1){ if (errno==EINTR) goto again; LM_ERR("epoll_create: %s [%d]\n", strerror(errno), errno); return -1; } return 0; } /*! * \brief epoll specific destroy * \param h IO handle */ static void destroy_epoll(io_wait_h* h) { if (h->epfd!=-1){ close(h->epfd); h->epfd=-1; } } #endif #ifdef HAVE_KQUEUE /*! * \brief kqueue specific init * \param h IO handle * \return -1 on error, 0 on success */ static int init_kqueue(io_wait_h* h) { again: h->kq_fd=kqueue(); if (h->kq_fd==-1){ if (errno==EINTR) goto again; LM_ERR("kqueue: %s [%d]\n", strerror(errno), errno); return -1; } return 0; } /*! * \brief kqueue specific destroy * \param h IO handle */ static void destroy_kqueue(io_wait_h* h) { if (h->kq_fd!=-1){ close(h->kq_fd); h->kq_fd=-1; } } #endif #ifdef HAVE_DEVPOLL /*! * \brief /dev/poll specific init * \param h IO handle * \return -1 on error, 0 on success */ static int init_devpoll(io_wait_h* h) { again: h->dpoll_fd=open("/dev/poll", O_RDWR); if (h->dpoll_fd==-1){ if (errno==EINTR) goto again; LM_ERR("open: %s [%d]\n", strerror(errno), errno); return -1; } return 0; } /*! * \brief dev/poll specific destroy * \param h IO handle */ static void destroy_devpoll(io_wait_h* h) { if (h->dpoll_fd!=-1){ close(h->dpoll_fd); h->dpoll_fd=-1; } } #endif #ifdef HAVE_SELECT /*! * \brief select specific init * \param h IO handle * \return zero * \todo make this method void, and remove the check in io_wait.c */ static int init_select(io_wait_h* h) { FD_ZERO(&h->master_set); return 0; } #endif /*! * \brief return system version * Return system version (major.minor.minor2) as (major<<16)|(minor)<<8|(minor2) * (if some of them are missing, they are set to 0) * if the parameters are not null they are set to the coresp. part * \param major major version * \param minor minor version * \param minor2 minor2 version * \return (major<<16)|(minor)<<8|(minor2) */ static unsigned int get_sys_version(int* major, int* minor, int* minor2) { struct utsname un; int m1; int m2; int m3; char* p; memset (&un, 0, sizeof(un)); m1=m2=m3=0; /* get sys version */ uname(&un); m1=strtol(un.release, &p, 10); if (*p=='.'){ p++; m2=strtol(p, &p, 10); if (*p=='.'){ p++; m3=strtol(p, &p, 10); } } if (major) *major=m1; if (minor) *minor=m2; if (minor2) *minor2=m3; return ((m1<<16)|(m2<<8)|(m3)); } /*! * \brief Check preferred OS poll method * \param poll_method supported IO poll methods * \return 0 on success, and an error message on error */ char* check_poll_method(enum poll_types poll_method) { char* ret; unsigned int os_ver; ret=0; os_ver=get_sys_version(0,0,0); (void)os_ver; switch(poll_method){ case POLL_NONE: break; case POLL_POLL: /* always supported */ break; case POLL_SELECT: /* should be always supported */ #ifndef HAVE_SELECT ret="select not supported, try re-compiling with -DHAVE_SELECT"; #endif break; case POLL_EPOLL_LT: case POLL_EPOLL_ET: #ifndef HAVE_EPOLL ret="epoll not supported, try re-compiling with -DHAVE_EPOLL"; #else /* only on 2.6 + */ if (os_ver<0x020542) /* if ver < 2.5.66 */ ret="epoll not supported on kernels < 2.6"; #endif break; case POLL_SIGIO_RT: #ifndef HAVE_SIGIO_RT ret="sigio_rt not supported, try re-compiling with" " -DHAVE_SIGIO_RT"; #else /* only on 2.2 + ?? */ if (os_ver<0x020200) /* if ver < 2.2.0 */ ret="epoll not supported on kernels < 2.2 (?)"; #endif break; case POLL_KQUEUE: #ifndef HAVE_KQUEUE ret="kqueue not supported, try re-compiling with -DHAVE_KQUEUE"; #else /* only in FreeBSD 4.1, NETBSD 2.0, OpenBSD 2.9, Darwin */ #ifdef __OS_freebsd if (os_ver<0x0401) /* if ver < 4.1 */ ret="kqueue not supported on FreeBSD < 4.1"; #elif defined (__OS_netbsd) if (os_ver<0x020000) /* if ver < 2.0 */ ret="kqueue not supported on NetBSD < 2.0"; #elif defined (__OS_openbsd) if (os_ver<0x0209) /* if ver < 2.9 ? */ ret="kqueue not supported on OpenBSD < 2.9 (?)"; #endif /* assume that the rest support kqueue ifdef HAVE_KQUEUE */ #endif break; case POLL_DEVPOLL: #ifndef HAVE_DEVPOLL ret="/dev/poll not supported, try re-compiling with" " -DHAVE_DEVPOLL"; #else /* only in Solaris >= 7.0 (?) */ #ifdef __OS_solaris if (os_ver<0x0507) /* ver < 5.7 */ ret="/dev/poll not supported on Solaris < 7.0 (SunOS 5.7)"; #endif #endif break; default: ret="unknown not supported method"; } return ret; } /*! * \brief Choose a IO poll method * \return the choosen poll method */ enum poll_types choose_poll_method(void) { enum poll_types poll_method; unsigned int os_ver; os_ver=get_sys_version(0,0,0); (void)os_ver; poll_method=0; #ifdef HAVE_EPOLL if (os_ver>=0x020542) /* if ver >= 2.5.66 */ poll_method=POLL_EPOLL_LT; /* or POLL_EPOLL_ET */ #endif #ifdef HAVE_KQUEUE if (poll_method==0) /* only in FreeBSD 4.1, NETBSD 2.0, OpenBSD 2.9, Darwin */ #ifdef __OS_freebsd if (os_ver>=0x0401) /* if ver >= 4.1 */ #elif defined (__OS_netbsd) if (os_ver>=0x020000) /* if ver >= 2.0 */ #elif defined (__OS_openbsd) if (os_ver>=0x0209) /* if ver >= 2.9 (?) */ #endif /* assume that the rest support kqueue ifdef HAVE_KQUEUE */ poll_method=POLL_KQUEUE; #endif #ifdef HAVE_DEVPOLL #ifdef __OS_solaris if (poll_method==0) /* only in Solaris >= 7.0 (?) */ if (os_ver>=0x0507) /* if ver >=SunOS 5.7 */ poll_method=POLL_DEVPOLL; #endif #endif #ifdef HAVE_SIGIO_RT if (poll_method==0) if (os_ver>=0x020200) /* if ver >= 2.2.0 */ poll_method=POLL_SIGIO_RT; #endif if (poll_method==0) poll_method=POLL_POLL; return poll_method; } /*! * \brief output the IO poll method name * \param poll_method used poll method */ char* poll_method_name(enum poll_types poll_method) { if ( poll_methodPOLL_NONE; r--) if ((strlen(poll_method_str[r])==l) && (strncasecmp(poll_method_str[r], s, l)==0)) break; return r; } /*! * \brief initializes the static vars/arrays * \param h - pointer to the io_wait_h that will be initialized * \param max_fd - maximum allowed fd number * \param poll_method - poll method (0 for automatic best fit) */ int init_io_wait(io_wait_h* h, char *name, int max_fd, enum poll_types poll_method, int max_prio) { char * poll_err; memset(h, 0, sizeof(*h)); h->name = name; h->max_prio = max_prio; h->max_fd_no=max_fd; #ifdef HAVE_EPOLL h->epfd=-1; #endif #ifdef HAVE_KQUEUE h->kq_fd=-1; #endif #ifdef HAVE_DEVPOLL h->dpoll_fd=-1; #endif poll_err=check_poll_method(poll_method); /* set an appropiate poll method */ if (poll_err || (poll_method==0)){ poll_method=choose_poll_method(); if (poll_err){ LM_ERR("%s, using %s instead\n", poll_err, poll_method_str[poll_method]); }else{ LM_INFO("using %s as the io watch method" " (auto detected)\n", poll_method_str[poll_method]); } } h->poll_method=poll_method; /* common stuff, everybody has fd_hash */ h->fd_hash=local_malloc(sizeof(*(h->fd_hash))*h->max_fd_no); if (h->fd_hash==0){ LM_CRIT("could not alloc fd hashtable (%ld bytes)\n", (long)sizeof(*(h->fd_hash))*h->max_fd_no ); goto error; } memset((void*)h->fd_hash, 0, sizeof(*(h->fd_hash))*h->max_fd_no); /* init the fd array as needed for priority ordering */ h->fd_array=local_malloc(sizeof(*(h->fd_array))*h->max_fd_no); if (h->fd_array==0){ LM_CRIT("could not alloc fd array (%ld bytes)\n", (long)sizeof(*(h->fd_hash))*h->max_fd_no); goto error; } memset((void*)h->fd_array, 0, sizeof(*(h->fd_array))*h->max_fd_no); /* array with indexes in fd_array where the priority changes */ h->prio_idx=local_malloc(sizeof(*(h->prio_idx))*h->max_prio); if (h->prio_idx==0){ LM_CRIT("could not alloc fd array (%ld bytes)\n", (long)sizeof(*(h->prio_idx))*h->max_prio); goto error; } memset((void*)h->prio_idx, 0, sizeof(*(h->prio_idx))*h->max_prio); switch(poll_method){ case POLL_POLL: break; #ifdef HAVE_SELECT case POLL_SELECT: if ((poll_method==POLL_SELECT) && (init_select(h)<0)){ LM_CRIT("select init failed\n"); goto error; } break; #endif #ifdef HAVE_DEVPOLL case POLL_DEVPOLL: if ((poll_method==POLL_DEVPOLL) && (init_devpoll(h)<0)){ LM_CRIT("/dev/poll init failed\n"); goto error; } h->dp_changes=local_malloc(sizeof(*(h->dp_changes))*h->max_fd_no); if (h->dp_changes==0){ LM_CRIT("could not alloc db changes array (%ld bytes)\n", (long)sizeof(*(h->dp_changes))*h->max_fd_no); goto error; } memset((void*)h->dp_changes, 0, sizeof(*(h->dp_changes))*h->max_fd_no); break; #endif #ifdef HAVE_SIGIO_RT case POLL_SIGIO_RT: if ((poll_method==POLL_SIGIO_RT) && (init_sigio(h, 0)<0)){ LM_CRIT("sigio init failed\n"); goto error; } break; #endif #ifdef HAVE_EPOLL case POLL_EPOLL_LT: case POLL_EPOLL_ET: h->ep_array=local_malloc(sizeof(*(h->ep_array))*h->max_fd_no); if (h->ep_array==0){ LM_CRIT("could not alloc epoll array\n"); goto error; } memset((void*)h->ep_array, 0, sizeof(*(h->ep_array))*h->max_fd_no); if (init_epoll(h)<0){ LM_CRIT("epoll init failed\n"); goto error; } break; #endif #ifdef HAVE_KQUEUE case POLL_KQUEUE: h->kq_array=local_malloc(sizeof(*(h->kq_array))*h->max_fd_no); if (h->kq_array==0){ LM_CRIT("could not alloc kqueue event array\n"); goto error; } h->kq_changes_size=KQ_CHANGES_ARRAY_SIZE; h->kq_changes=local_malloc(sizeof(*(h->kq_changes))* h->kq_changes_size); if (h->kq_changes==0){ LM_CRIT("could not alloc kqueue changes array\n"); goto error; } h->kq_nchanges=0; memset((void*)h->kq_array, 0, sizeof(*(h->kq_array))*h->max_fd_no); memset((void*)h->kq_changes, 0, sizeof(*(h->kq_changes))* h->kq_changes_size); if (init_kqueue(h)<0){ LM_CRIT("kqueue init failed\n"); goto error; } break; #endif default: LM_CRIT("unknown/unsupported poll method %s (%d)\n", poll_method_str[poll_method], poll_method); goto error; } return 0; error: return -1; } /*! * \brief destroys everything init_io_wait allocated * \param h IO handle */ void destroy_io_wait(io_wait_h* h) { switch(h->poll_method){ #ifdef HAVE_EPOLL case POLL_EPOLL_LT: case POLL_EPOLL_ET: destroy_epoll(h); if (h->ep_array){ local_free(h->ep_array); h->ep_array=0; } break; #endif #ifdef HAVE_KQUEUE case POLL_KQUEUE: destroy_kqueue(h); if (h->kq_array){ local_free(h->kq_array); h->kq_array=0; } if (h->kq_changes){ local_free(h->kq_changes); h->kq_changes=0; } break; #endif #ifdef HAVE_SIGIO_RT case POLL_SIGIO_RT: destroy_sigio(h); break; #endif #ifdef HAVE_DEVPOLL case POLL_DEVPOLL: destroy_devpoll(h); if (h->dp_changes){ local_free(h->dp_changes); h->dp_changes=0; } break; #endif default: /*do nothing*/ ; } if (h->fd_array){ local_free(h->fd_array); h->fd_array=0; } if (h->fd_hash){ local_free(h->fd_hash); h->fd_hash=0; } if (h->prio_idx){ local_free(h->prio_idx); h->prio_idx=0; } } void fix_poll_method( enum poll_types *poll_method ) { char* poll_err; /* fix config variables */ /* they can have only positive values due the config parser so we can * ignore most of them */ poll_err=check_poll_method(*poll_method); /* set an appropiate poll method */ if (poll_err || (*poll_method==0)){ *poll_method=choose_poll_method(); if (poll_err){ LM_ERR("%s, using %s instead\n", poll_err, poll_method_name(*poll_method)); }else{ LM_INFO("using %s as the IO watch method" " (auto detected)\n", poll_method_name(*poll_method)); } }else{ LM_INFO("using %s as the IO watch method (config)\n", poll_method_name(*poll_method)); } return; } opensips-2.2.2/io_wait.h000066400000000000000000000504771300170765700152040ustar00rootroot00000000000000/* * Copyright (C) 2014-2015 OpenSIPS Solutions * Copyright (C) 2005 iptelorg GmbH * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-06-13 created by andrei * 2005-06-26 added kqueue (andrei) * 2005-07-01 added /dev/poll (andrei) * 2014-08-25 looping functions moved to io_wait_loop.h (bogdan) */ /*! * \file * \brief tcp io wait common stuff used by tcp_main.c & tcp_read.c * - \ref TCPiowait */ /*! \page TCPiowait TCP io wait common stuff used by tcp_main.c & tcp_read.c * All the functions are inline because of speed reasons and because they are * used only from 2 places. * You also have to define: * - int handle_io(struct fd_map* fm, int idx) (see below) * (this could be trivially replaced by a callback pointer entry attached * to the io_wait handler if more flexibility rather then performance * is needed) * - fd_type - define to some enum of you choice and define also * FD_TYPE_DEFINED (if you don't do it fd_type will be defined * to int). 0 has a special not set/not init. meaning * (a lot of sanity checks and the sigio_rt code are based on * this assumption) * - local_malloc (defaults to pkg_malloc) * - local_free (defaults to pkg_free) * */ #ifndef _io_wait_h #define _io_wait_h #include #include #ifdef HAVE_SIGIO_RT #define __USE_GNU /* or else F_SETSIG won't be included */ #define _GNU_SOURCE /* define this as well */ #include /* recv */ #include /* recv */ #include /* sigprocmask, sigwait a.s.o */ #endif #ifdef HAVE_EPOLL #include #endif #ifdef HAVE_KQUEUE #include /* needed on freebsd */ #include #include #endif #ifdef HAVE_DEVPOLL #include #endif #ifdef HAVE_SELECT /* needed on openbsd for select*/ #include #include #include /* needed according to POSIX for select*/ #include #endif #include #include #include "dprint.h" #include "poll_types.h" /* poll_types*/ #include "pt.h" /* mypid() */ #include "error.h" #ifndef FD_TYPE_DEFINED typedef int fd_type; #define FD_TYPE_DEFINED #endif /*! \brief maps a fd to some other structure; used in almost all cases * except epoll and maybe kqueue or /dev/poll */ struct fd_map { int fd; /* fd no */ fd_type type; /* "data" type */ void* data; /* pointer to the corresponding structure */ int flags; /* so far used to indicate whether we should * read, write or both ; last 4 are reserved for * internal usage */ }; #ifdef HAVE_KQUEUE #ifndef KQ_CHANGES_ARRAY_SIZE #define KQ_CHANGES_ARRAY_SIZE 128 #ifdef __OS_netbsd #define KEV_UDATA_CAST (intptr_t) #else #define KEV_UDATA_CAST #endif #endif #endif #define IO_FD_CLOSING 16 /*! \brief handler structure */ struct io_wait_handler{ char *name; int max_prio; #ifdef HAVE_EPOLL struct epoll_event* ep_array; int epfd; /* epoll ctrl fd */ #endif #ifdef HAVE_SIGIO_RT sigset_t sset; /* signal mask for sigio & sigrtmin */ int signo; /* real time signal used */ #endif #ifdef HAVE_KQUEUE struct kevent* kq_array; /* used for the eventlist*/ struct kevent* kq_changes; /* used for the changelist */ size_t kq_nchanges; size_t kq_changes_size; /* size of the changes array */ int kq_fd; #endif #ifdef HAVE_DEVPOLL int dpoll_fd; struct pollfd* dp_changes; #endif #ifdef HAVE_SELECT fd_set master_set; int max_fd_select; /* maximum select used fd */ #endif /* common stuff for POLL, SIGIO_RT and SELECT * since poll support is always compiled => this will always be compiled */ int *prio_idx; /* size of max_prio - idxs in fd_array where prio changes*/ struct fd_map* fd_hash; struct pollfd* fd_array; int fd_no; /* current index used in fd_array */ int max_fd_no; /* maximum fd no, is also the size of fd_array, fd_hash and ep_array*/ enum poll_types poll_method; int flags; }; typedef struct io_wait_handler io_wait_h; /*! \brief get the corresponding fd_map structure pointer */ #define get_fd_map(h, fd) (&(h)->fd_hash[(fd)]) /*! \brief remove a fd_map structure from the hash; * the pointer must be returned by get_fd_map or hash_fd_map */ #define unhash_fd_map(pfm,c_flags,sock_flags,erase) \ do{ \ if ((c_flags & IO_FD_CLOSING) || pfm->flags == sock_flags) { \ (pfm)->type=0 /*F_NONE */; \ (pfm)->fd=-1; \ (pfm)->flags = 0; \ erase = 1; \ } else { \ (pfm)->flags &= ~sock_flags; \ erase = 0; \ } \ }while(0) /*! \brief add a fd_map structure to the fd hash */ static inline struct fd_map* hash_fd_map( io_wait_h* h, int fd, fd_type type, void* data, int flags, int *already) { if (h->fd_hash[fd].fd <= 0) { *already = 0; } else { *already = 1; } h->fd_hash[fd].fd=fd; h->fd_hash[fd].type=type; h->fd_hash[fd].data=data; h->fd_hash[fd].flags|=flags; return &h->fd_hash[fd]; } #ifdef HAVE_KQUEUE /* * kqueue specific function: register a change * (adds a change to the kevent change array, and if full flushes it first) * returns: -1 on error, 0 on success */ static inline int kq_ev_change(io_wait_h* h, int fd, int filter, int flag, void* data) { int n; struct timespec tspec; if (h->kq_nchanges>=h->kq_changes_size){ /* changes array full ! */ LM_WARN("[%s] kqueue changes array full trying to flush...\n", h->name); tspec.tv_sec=0; tspec.tv_nsec=0; again: n=kevent(h->kq_fd, h->kq_changes, h->kq_nchanges, 0, 0, &tspec); if (n==-1){ if (errno==EINTR) goto again; LM_ERR("[%s] kevent flush changes failed: %s [%d]\n", h->name, strerror(errno), errno); return -1; } h->kq_nchanges=0; /* changes array is empty */ } EV_SET(&h->kq_changes[h->kq_nchanges], fd, filter, flag, 0, 0, KEV_UDATA_CAST data); h->kq_nchanges++; return 0; } #endif #define IO_WATCH_READ (1<<0) #define IO_WATCH_WRITE (1<<1) #define IO_WATCH_ERROR (1<<2) /* reserved, do not attempt to use */ #define IO_WATCH_PRV_TRIG_READ (1<<30) #define IO_WATCH_PRV_TRIG_WRITE (1<<31) #define fd_array_print \ do { \ int k;\ LM_DBG("[%s] size=%d, fd array is",h->name,h->fd_no);\ for(k=0;kfd_no;k++) LM_GEN1(L_DBG," %d flags = %d",h->fd_array[k].fd,h->fd_hash[h->fd_array[k].fd].flags);\ LM_GEN1(L_DBG,"\n"); \ LM_DBG("[%s] size=%d, prio array is",h->name,h->max_prio);\ for(k=0;kmax_prio;k++) LM_GEN1(L_DBG," %d",h->prio_idx[k]);\ LM_GEN1(L_DBG,"\n"); \ }while(0) /*! \brief generic io_watch_add function * \return 0 on success, -1 on error * * this version should be faster than pointers to poll_method specific * functions (it avoids functions calls, the overhead being only an extra * switch()) */ inline static int io_watch_add( io_wait_h* h, int fd, fd_type type, void* data, int prio, int flags) { /* helper macros */ #define fd_array_setup \ do{ \ n = h->prio_idx[prio]; \ if (nfd_no)\ memmove( &h->fd_array[n+1], &h->fd_array[n],\ (h->fd_no-n)*sizeof(*(h->fd_array)) ); \ h->fd_array[n].fd=fd; \ h->fd_array[n].events=0; \ if (flags & IO_WATCH_READ) \ h->fd_array[n].events|=POLLIN; /* useless for select */ \ if (flags & IO_WATCH_WRITE) \ h->fd_array[n].events|=POLLOUT; /* useless for select */ \ h->fd_array[n].revents=0; /* useless for select */ \ for( n=prio ; nmax_prio ; n++) \ h->prio_idx[n]++; \ h->fd_no++; \ }while(0) #define set_fd_flags(f) \ do{ \ ctl_flags=fcntl(fd, F_GETFL); \ if (ctl_flags==-1){ \ LM_ERR("[%s] fcntl: GETFL failed:" \ " %s [%d]\n", h->name, strerror(errno), errno); \ goto error; \ } \ if (fcntl(fd, F_SETFL, ctl_flags|(f))==-1){ \ LM_ERR("[%s] fcntl: SETFL" \ " failed: %s [%d]\n", h->name, strerror(errno), errno);\ goto error; \ } \ }while(0) struct fd_map* e; int already; #ifdef HAVE_EPOLL struct epoll_event ep_event; #endif #ifdef HAVE_DEVPOLL struct pollfd pfd; #endif int ctl_flags; int n; //FIXME #if 0 //defined(HAVE_SIGIO_RT) || defined (HAVE_EPOLL) FIXME int n; int idx; int check_io; struct pollfd pf; check_io=0; /* set to 1 if we need to check for pre-existing queued io/data on the fd */ idx=-1; #endif e=0; if (fd==-1){ LM_CRIT("fd is -1!\n"); goto error; } /* check if not too big */ if (h->fd_no >= h->max_fd_no || fd >= h->max_fd_no) { LM_CRIT("[%s] maximum fd number exceeded: %d, %d/%d\n", h->name, fd, h->fd_no, h->max_fd_no); goto error; } if (prio > h->max_prio) { LM_BUG("[%s] priority %d requested (max is %d)\n", h->name, prio, h->max_prio); goto error; } #if defined (HAVE_EPOLL) LM_DBG("[%s] io_watch_add op (%d on %d) (%p, %d, %d, %p,%d), fd_no=%d/%d\n", h->name,fd,h->epfd, h,fd,type,data,flags,h->fd_no,h->max_fd_no); #else LM_DBG("[%s] io_watch_add op (%d) (%p, %d, %d, %p,%d), fd_no=%d/%d\n", h->name,fd, h,fd,type,data,flags,h->fd_no,h->max_fd_no); #endif //fd_array_print; /* hash sanity check */ e=get_fd_map(h, fd); if (e->flags & flags){ if (e->data != data) { LM_BUG("[%s] BUG trying to overwrite entry %d" " in the hash(%d, %d, %p,%d) with (%d, %d, %p,%d)\n", h->name,fd, e->fd, e->type, e->data,e->flags, fd, type, data,flags); goto error; } LM_DBG("[%s] Socket %d is already being listened on for flags %d\n", h->name,fd,flags); return 0; } if ((e=hash_fd_map(h, fd, type, data,flags,&already))==0){ LM_ERR("[%s] failed to hash the fd %d\n",h->name, fd); goto error; } switch(h->poll_method){ /* faster then pointer to functions */ case POLL_POLL: set_fd_flags(O_NONBLOCK); break; #ifdef HAVE_SELECT case POLL_SELECT: FD_SET(fd, &h->master_set); if (h->max_fd_selectmax_fd_select=fd; break; #endif #ifdef HAVE_SIGIO_RT case POLL_SIGIO_RT: /* re-set O_ASYNC might be needed, if not done from * io_watch_del (or if somebody wants to add a fd which has * already O_ASYNC/F_SETSIG set on a dupplicate) */ /* set async & signal */ if (fcntl(fd, F_SETOWN, my_pid())==-1){ LM_ERR("[%s] fcntl: SETOWN" " failed: %s [%d]\n",h->name, strerror(errno), errno); goto error; } if (fcntl(fd, F_SETSIG, h->signo)==-1){ LM_ERR("[%s] fcntl: SETSIG" " failed: %s [%d]\n",h->name, strerror(errno), errno); goto error; } /* set both non-blocking and async */ set_fd_flags(O_ASYNC| O_NONBLOCK); #ifdef EXTRA_DEBUG LM_DBG("[%s] sigio_rt on f %d, signal %d to pid %d\n", h->name,fd, h->signo, my_pid()); #endif /* empty socket receive buffer, if buffer is already full * no more space to put packets * => no more signals are ever generated * also when moving fds, the freshly moved fd might have * already some bytes queued, we want to get them now * and not later -- andrei */ //idx=h->fd_no; FIXME //check_io=1; break; #endif #ifdef HAVE_EPOLL case POLL_EPOLL_LT: ep_event.data.ptr=e; ep_event.events=0; if (e->flags & IO_WATCH_READ) ep_event.events|=EPOLLIN; if (e->flags & IO_WATCH_WRITE) ep_event.events|=EPOLLOUT; if (!already) { again1: n=epoll_ctl(h->epfd, EPOLL_CTL_ADD, fd, &ep_event); if (n==-1){ if (errno==EAGAIN) goto again1; LM_ERR("[%s] epoll_ctl ADD failed: %s [%d]\n", h->name,strerror(errno), errno); goto error; } } else { again11: n=epoll_ctl(h->epfd, EPOLL_CTL_MOD, fd, &ep_event); if (n==-1){ if (errno==EAGAIN) goto again11; LM_ERR("[%s] epoll_ctl MOD failed: %s [%d]\n", h->name,strerror(errno), errno); goto error; } } break; case POLL_EPOLL_ET: set_fd_flags(O_NONBLOCK); ep_event.events=EPOLLET; ep_event.data.ptr=e; if (e->flags & IO_WATCH_READ) ep_event.events|=EPOLLIN; if (e->flags & IO_WATCH_WRITE) ep_event.events|=EPOLLOUT; again2: if (!already) { n=epoll_ctl(h->epfd, EPOLL_CTL_ADD, fd, &ep_event); if (n==-1){ if (errno==EAGAIN) goto again2; LM_ERR("[%s] epoll_ctl failed: %s [%d]\n", h->name,strerror(errno), errno); goto error; } //check_io=1; FIXME } else { again22: n=epoll_ctl(h->epfd, EPOLL_CTL_MOD, fd, &ep_event); if (n==-1){ if (errno==EAGAIN) goto again22; LM_ERR("[%s] epoll_ctl failed: %s [%d]\n", h->name,strerror(errno), errno); goto error; } } //idx=-1; FIXME break; #endif #ifdef HAVE_KQUEUE case POLL_KQUEUE: if (kq_ev_change(h, fd, EVFILT_READ, EV_ADD, e)==-1) goto error; break; #endif #ifdef HAVE_DEVPOLL case POLL_DEVPOLL: pfd.fd=fd; pfd.events=POLLIN; pfd.revents=0; again_devpoll: if (write(h->dpoll_fd, &pfd, sizeof(pfd))==-1){ if (errno==EAGAIN) goto again_devpoll; LM_ERR("[%s] /dev/poll write failed:" "%s [%d]\n",h->name, strerror(errno), errno); goto error; } break; #endif default: LM_CRIT("[%s] no support for poll method " " %s (%d)\n",h->name, poll_method_str[h->poll_method], h->poll_method); goto error; } if (!already) { fd_array_setup; } #if 0 //defined(HAVE_SIGIO_RT) || defined (HAVE_EPOLL) FIXME !!! if (check_io){ /* handle possible pre-existing events */ pf.fd=fd; pf.events=POLLIN; check_io_again: while( ((n=poll(&pf, 1, 0))>0) && (handle_io(e, idx,IO_WATCH_READ)>0)); if (n==-1){ if (errno==EINTR) goto check_io_again; LM_ERR("check_io poll: %s [%d]\n", strerror(errno), errno); } } #endif //fd_array_print; return 0; error: if (e) unhash_fd_map(e,0,flags,already); return -1; #undef fd_array_setup #undef set_fd_flags } /*! * \brief * \param h handler * \param fd file descriptor * \param idx index in the fd_array if known, -1 if not * (if index==-1 fd_array will be searched for the * corresponding fd* entry -- slower but unavoidable in * some cases). index is not used (no fd_array) for epoll, * /dev/poll and kqueue * \param flags optimization flags, e.g. IO_FD_CLOSING, the fd was or will * shortly be closed, in some cases we can avoid extra * remove operations (e.g.: epoll, kqueue, sigio) * \return 0 if ok, -1 on error */ inline static int io_watch_del(io_wait_h* h, int fd, int idx, int flags,int sock_flags) { #define fix_fd_array \ do{\ if (idx==-1){ \ /* fix idx if -1 and needed */ \ for (idx=0; (idxfd_no) && \ (h->fd_array[idx].fd!=fd); idx++); \ } \ if (idxfd_no){ \ if (erase) { \ memmove(&h->fd_array[idx], &h->fd_array[idx+1], \ (h->fd_no-(idx+1))*sizeof(*(h->fd_array))); \ for( i=0 ; imax_prio && h->prio_idx[i]<=idx ; i++ ); \ for( ; imax_prio ; i++ ) h->prio_idx[i]-- ; \ h->fd_no--; \ } else { \ h->fd_array[idx].events = 0; \ if (e->flags & IO_WATCH_READ) \ h->fd_array[idx].events|=POLLIN; /* useless for select */ \ if (flags & IO_WATCH_WRITE) \ h->fd_array[idx].events|=POLLOUT; /* useless for select */ \ h->fd_array[idx].revents = 0; \ } \ } \ }while(0) struct fd_map* e; #ifdef HAVE_EPOLL int n; struct epoll_event ep_event; #endif #ifdef HAVE_DEVPOLL struct pollfd pfd; #endif #ifdef HAVE_SIGIO_RT int fd_flags; #endif int erase = 0; int i; if ((fd<0) || (fd>=h->max_fd_no)){ LM_CRIT("[%s] invalid fd %d, not in [0, %d)\n", h->name, fd, h->fd_no); goto error0; } LM_DBG("[%s] io_watch_del op on index %d %d (%p, %d, %d, 0x%x,0x%x) " "fd_no=%d called\n", h->name,idx,fd, h, fd, idx, flags, sock_flags,h->fd_no); //fd_array_print; e=get_fd_map(h, fd); /* more sanity checks */ if (e==0){ LM_CRIT("[%s] no corresponding hash entry for %d\n",h->name, fd); goto error0; } if (e->type==0 /*F_NONE*/){ LM_ERR("[%s] trying to delete already erased" " entry %d in the hash(%d, %d, %p) )\n", h->name,fd, e->fd, e->type, e->data); goto error0; } if ((e->flags & sock_flags) == 0) { LM_ERR("BUG - [%s] trying to del fd %d with flags %d %d\n", h->name, fd, e->flags,sock_flags); goto error0; } unhash_fd_map(e,flags,sock_flags,erase); switch(h->poll_method){ case POLL_POLL: break; #ifdef HAVE_SELECT case POLL_SELECT: FD_CLR(fd, &h->master_set); if (h->max_fd_select && (h->max_fd_select==fd)) /* we don't know the prev. max, so we just decrement it */ h->max_fd_select--; break; #endif #ifdef HAVE_SIGIO_RT case POLL_SIGIO_RT: /* the O_ASYNC flag must be reset all the time, the fd * can be changed only if O_ASYNC is reset (if not and * the fd is a duplicate, you will get signals from the dup. fd * and not from the original, even if the dup. fd was closed * and the signals re-set on the original) -- andrei */ /*if (!(flags & IO_FD_CLOSING)){*/ /* reset ASYNC */ fd_flags=fcntl(fd, F_GETFL); if (fd_flags==-1){ LM_ERR("[%s] fcntl: GETFL failed:" " %s [%d]\n",h->name, strerror(errno), errno); goto error; } if (fcntl(fd, F_SETFL, fd_flags&(~O_ASYNC))==-1){ LM_ERR("[%s] fcntl: SETFL" " failed: %s [%d]\n",h->name, strerror(errno), errno); goto error; } break; #endif #ifdef HAVE_EPOLL case POLL_EPOLL_LT: case POLL_EPOLL_ET: /* epoll doesn't seem to automatically remove sockets, * if the socket is a dupplicate/moved and the original * is still open. The fd is removed from the epoll set * only when the original (and all the copies?) is/are * closed. This is probably a bug in epoll. --andrei */ #ifdef EPOLL_NO_CLOSE_BUG if (!(flags & IO_FD_CLOSING)){ #endif if (erase) { n=epoll_ctl(h->epfd, EPOLL_CTL_DEL, fd, &ep_event); /* * in some cases (fds managed by external libraries), * the fd may have already been closed */ if (n==-1 && errno != EBADF) { LM_ERR("[%s] removing fd from epoll (%d from %d) " "list failed: %s [%d]\n",h->name, fd, h->epfd, strerror(errno), errno); goto error; } } else { ep_event.data.ptr=e; ep_event.events=0; if (e->flags & IO_WATCH_READ) ep_event.events|=EPOLLIN; if (e->flags & IO_WATCH_WRITE) ep_event.events|=EPOLLOUT; n=epoll_ctl(h->epfd, EPOLL_CTL_MOD, fd, &ep_event); if (n==-1){ LM_ERR("[%s] epoll_ctl failed: %s [%d]\n", h->name,strerror(errno), errno); goto error; } } #ifdef EPOLL_NO_CLOSE_BUG } #endif break; #endif #ifdef HAVE_KQUEUE case POLL_KQUEUE: if (!(flags & IO_FD_CLOSING)){ if (kq_ev_change(h, fd, EVFILT_READ, EV_DELETE, 0)==-1) goto error; } break; #endif #ifdef HAVE_DEVPOLL case POLL_DEVPOLL: /* for /dev/poll the closed fds _must_ be removed (they are not removed automatically on close()) */ pfd.fd=fd; pfd.events=POLLREMOVE; pfd.revents=0; again_devpoll: if (write(h->dpoll_fd, &pfd, sizeof(pfd))==-1){ if (errno==EINTR) goto again_devpoll; LM_ERR("[%s] removing fd from /dev/poll failed: " "%s [%d]\n",h->name, strerror(errno), errno); goto error; } break; #endif default: LM_CRIT("[%s] no support for poll method %s (%d)\n", h->name,poll_method_str[h->poll_method], h->poll_method); goto error; } fix_fd_array; //fd_array_print; return 0; error: /* * although the DEL operation failed, both * "fd_hash" and "fd_array" must remain consistent */ fix_fd_array; error0: return -1; #undef fix_fd_array } /* init */ /*! \brief initializes the static vars/arrays * \param h pointer to the io_wait_h that will be initialized * \param max_fd maximum allowed fd number * \param poll_method poll method (0 for automatic best fit) */ int init_io_wait(io_wait_h* h, char *name, int max_fd, enum poll_types poll_method, int max_prio); /*! \brief destroys everything init_io_wait allocated */ void destroy_io_wait(io_wait_h* h); #endif opensips-2.2.2/io_wait_loop.h000066400000000000000000000310721300170765700162230ustar00rootroot00000000000000/* * Copyright (C) 2014-2015 OpenSIPS Solutions * Copyright (C) 2005 iptelorg GmbH * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-08-25 split from io_wait.h (bogdan) */ /*! * \file * \brief io wait looping and triggering functions */ #ifndef _io_wait_loop_h #define _io_wait_loop_h #include "io_wait.h" #ifdef HANDLE_IO_INLINE /*!\brief generic handle io routine * this must be defined in the including file * (faster then registering a callback pointer) * * \param fm pointer to a fd hash entry * \param idx index in the fd_array (or -1 if not known) * \return return: -1 on error * 0 on EAGAIN or when by some other way it is known that no more * io events are queued on the fd (the receive buffer is empty). * Usefull to detect when there are no more io events queued for * sigio_rt, epoll_et, kqueue. * >0 on successfull read from the fd (when there might be more io * queued -- the receive buffer might still be non-empty) */ inline static int handle_io(struct fd_map* fm, int idx,int event_type); #else static int handle_io(struct fd_map* fm, int idx,int event_type) { return 0; } #endif /*! \brief io_wait_loop_x style function * wait for io using poll() * \param h io_wait handle * \param t timeout in s * \param repeat if !=0 handle_io will be called until it returns <=0 * \return number of IO events handled on success (can be 0), -1 on error */ inline static int io_wait_loop_poll(io_wait_h* h, int t, int repeat) { int n, r; int ret; again: ret=n=poll(h->fd_array, h->fd_no, t*1000); if (n==-1){ if (errno==EINTR) goto again; /* signal, ignore it */ else{ LM_ERR("[%s] poll: %s [%d]\n",h->name, strerror(errno), errno); goto error; } } for (r=h->fd_no-1; (r>=0) && n; r--){ if (h->fd_array[r].revents & POLLOUT) { n--; /* sanity checks */ if ((h->fd_array[r].fd >= h->max_fd_no)|| (h->fd_array[r].fd < 0)){ LM_CRIT("[%s] bad fd %d (no in the 0 - %d range)\n", h->name, h->fd_array[r].fd, h->max_fd_no); /* try to continue anyway */ h->fd_array[r].events=0; /* clear the events */ continue; } handle_io(get_fd_map(h, h->fd_array[r].fd),r,IO_WATCH_WRITE); } else if (h->fd_array[r].revents & (POLLIN|POLLERR|POLLHUP)){ n--; /* sanity checks */ if ((h->fd_array[r].fd >= h->max_fd_no)|| (h->fd_array[r].fd < 0)){ LM_CRIT("[%s] bad fd %d (no in the 0 - %d range)\n", h->name,h->fd_array[r].fd, h->max_fd_no); /* try to continue anyway */ h->fd_array[r].events=0; /* clear the events */ continue; } while((handle_io(get_fd_map(h, h->fd_array[r].fd), r,IO_WATCH_READ) > 0) && repeat); } } error: return ret; } #ifdef HAVE_SELECT /*! \brief wait for io using select */ inline static int io_wait_loop_select(io_wait_h* h, int t, int repeat) { fd_set sel_set; int n, ret; struct timeval timeout; int r; again: sel_set=h->master_set; timeout.tv_sec=t; timeout.tv_usec=0; ret=n=select(h->max_fd_select+1, &sel_set, 0, 0, &timeout); if (n<0){ if (errno==EINTR) goto again; /* just a signal */ LM_ERR("[%s] select: %s [%d]\n",h->name, strerror(errno), errno); n=0; /* continue */ } /* use poll fd array */ for(r=h->fd_no-1; (r>=0) && n; r--){ if (FD_ISSET(h->fd_array[r].fd, &sel_set)){ while((handle_io(get_fd_map(h, h->fd_array[r].fd), r,IO_WATCH_READ)>0) && repeat); n--; } }; return ret; } #endif #ifdef HAVE_EPOLL inline static int io_wait_loop_epoll(io_wait_h* h, int t, int repeat) { int ret, n, r; struct fd_map *e; again: ret=n=epoll_wait(h->epfd, h->ep_array, h->fd_no, t*1000); if (n==-1){ if (errno==EINTR) goto again; /* signal, ignore it */ else{ LM_ERR("[%s] epoll_wait(%d, %p, %d, %d): %s [%d]\n", h->name,h->epfd, h->ep_array, h->fd_no, t*1000, strerror(errno), errno); goto error; } } for (r=0; rname, ((struct fd_map*)h->ep_array[r].data.ptr)->fd, h->ep_array[r].events, ((struct fd_map*)h->ep_array[r].data.ptr)->flags); #endif /* anything containing EPOLLIN (like HUP or ERR) goes as a READ */ if (h->ep_array[r].events & EPOLLIN) { if (h->ep_array[r].events&EPOLLHUP) { LM_DBG("[%s] EPOLLHUP on IN ->" "connection closed by the remote peer!\n",h->name); } ((struct fd_map*)h->ep_array[r].data.ptr)->flags |= IO_WATCH_PRV_TRIG_READ; /* anything containing EPOLLOUT (like HUP or ERR) goes as a WRITE*/ } else if (h->ep_array[r].events & EPOLLOUT){ if (h->ep_array[r].events&EPOLLHUP) { LM_DBG("[%s] EPOLLHUP on OUT ->" "connection closed by the remote peer!\n",h->name); } ((struct fd_map*)h->ep_array[r].data.ptr)->flags |= IO_WATCH_PRV_TRIG_WRITE; /* ERR or HUP without IN or OUT triggering ?? */ } else if (h->ep_array[r].events & (EPOLLERR|EPOLLHUP) ) { LM_DBG("[%s] non-op event %x, using flags %x\n",h->name, h->ep_array[r].events, ((struct fd_map*)h->ep_array[r].data.ptr)->flags); /* as the epoll did not provide any info on IN/OUT * we look back the IO flags we set */ if ( ((struct fd_map*)h->ep_array[r].data.ptr)->flags & IO_WATCH_WRITE ) ((struct fd_map*)h->ep_array[r].data.ptr)->flags |= IO_WATCH_PRV_TRIG_WRITE; else ((struct fd_map*)h->ep_array[r].data.ptr)->flags |= IO_WATCH_PRV_TRIG_READ; } else { LM_ERR("[%s] unexpected event %x on %d/%d, data=%p\n", h->name,h->ep_array[r].events, r+1, n, h->ep_array[r].data.ptr); } } /* now do the actual running of IO handlers */ for(r=h->fd_no-1; (r>=0) && n ; r--) { e = get_fd_map(h, h->fd_array[r].fd); if ( e->flags & IO_WATCH_PRV_TRIG_READ ) { e->flags &= ~IO_WATCH_PRV_TRIG_READ; while((handle_io( e, r, IO_WATCH_READ)>0) && repeat); n--; } else if ( e->flags & IO_WATCH_PRV_TRIG_WRITE ){ e->flags &= ~IO_WATCH_PRV_TRIG_WRITE; handle_io( e, r, IO_WATCH_WRITE); n--; } } error: return ret; } #endif #ifdef HAVE_KQUEUE inline static int io_wait_loop_kqueue(io_wait_h* h, int t, int repeat) { int ret, n, r; struct timespec tspec; struct fd_map *e; tspec.tv_sec=t; tspec.tv_nsec=0; again: ret=n=kevent(h->kq_fd, h->kq_changes, h->kq_nchanges, h->kq_array, h->fd_no, &tspec); if (n==-1){ if (errno==EINTR) goto again; /* signal, ignore it */ else{ LM_ERR("[%s] kevent: %s [%d]\n", h->name, strerror(errno), errno); goto error; } } h->kq_nchanges=0; /* reset changes array */ for (r=0; rname, r, n, h->kq_array[r].ident, (long)h->kq_array[r].udata, h->kq_array[r].flags); #endif if (h->kq_array[r].flags & EV_ERROR){ /* error in changes: we ignore it, it can be caused by trying to remove an already closed fd: race between adding smething to the changes array, close() and applying the changes */ LM_INFO("[%s] kevent error on fd %u: %s [%ld]\n", h->name, (unsigned int)h->kq_array[r].ident, strerror(h->kq_array[r].data), (long)h->kq_array[r].data); }else /* READ/EOF */ ((struct fd_map*)h->kq_array[r].udata)->flags |= IO_WATCH_PRV_TRIG_READ; } /* now do the actual running of IO handlers */ for(r=h->fd_no-1; (r>=0) && n ; r--) { e = get_fd_map(h, h->fd_array[r].fd); if ( e->flags & IO_WATCH_PRV_TRIG_READ ) { e->flags &= ~IO_WATCH_PRV_TRIG_READ; while((handle_io( e, r, IO_WATCH_READ)>0) && repeat); n--; } } error: return ret; } #endif #ifdef HAVE_SIGIO_RT /*! \brief sigio rt version has no repeat (it doesn't make sense)*/ inline static int io_wait_loop_sigio_rt(io_wait_h* h, int t) { int n; int ret; struct timespec ts; siginfo_t siginfo; int sigio_band; int sigio_fd; struct fd_map* fm; ret=1; /* 1 event per call normally */ ts.tv_sec=t; ts.tv_nsec=0; if (!sigismember(&h->sset, h->signo) || !sigismember(&h->sset, SIGIO)){ LM_CRIT("[%s] the signal mask is not properly set!\n",h->name); goto error; } again: n=sigtimedwait(&h->sset, &siginfo, &ts); if (n==-1){ if (errno==EINTR) goto again; /* some other signal, ignore it */ else if (errno==EAGAIN){ /* timeout */ ret=0; goto end; }else{ LM_ERR("[%s] sigtimed_wait %s [%d]\n",h->name, strerror(errno), errno); goto error; } } if (n!=SIGIO){ #ifdef SIGINFO64_WORKARROUND /* on linux siginfo.si_band is defined as long in userspace * and as int kernel => on 64 bits things will break! * (si_band will include si_fd, and si_fd will contain * garbage) * see /usr/src/linux/include/asm-generic/siginfo.h and * /usr/include/bits/siginfo.h * -- andrei */ if (sizeof(siginfo.si_band)>sizeof(int)){ sigio_band=*((int*)&siginfo.si_band); sigio_fd=*(((int*)&siginfo.si_band)+1); }else #endif { sigio_band=siginfo.si_band; sigio_fd=siginfo.si_fd; } if (siginfo.si_code==SI_SIGIO){ /* old style, we don't know the event (linux 2.2.?) */ LM_WARN("[%s] old style sigio interface\n",h->name); fm=get_fd_map(h, sigio_fd); /* we can have queued signals generated by fds not watched * any more, or by fds in transition, to a child => ignore them*/ if (fm->type) handle_io(fm, -1,IO_WATCH_READ); }else{ #ifdef EXTRA_DEBUG LM_DBG("[%s] siginfo: signal=%d (%d)," " si_code=%d, si_band=0x%x," " si_fd=%d\n", h->name,siginfo.si_signo, n, siginfo.si_code, (unsigned)sigio_band, sigio_fd); #endif /* on some errors (e.g. when receving TCP RST), sigio_band will * be set to 0x08 (undocumented, no corresp. POLL_xx), so better * catch all events --andrei */ if (sigio_band/*&(POLL_IN|POLL_ERR|POLL_HUP)*/){ fm=get_fd_map(h, sigio_fd); /* we can have queued signals generated by fds not watched * any more, or by fds in transition, to a child * => ignore them */ if (fm->type) handle_io(fm, -1,IO_WATCH_READ); else LM_ERR("[%s] ignoring event" " %x on fd %d (fm->fd=%d, fm->data=%p)\n", h->name,sigio_band, sigio_fd, fm->fd, fm->data); }else{ LM_ERR("[%s] unexpected event on fd %d: %x\n",h->name, sigio_fd, sigio_band); } } }else{ /* signal queue overflow * TODO: increase signal queue size: 2.4x /proc/.., 2.6x -rlimits */ LM_WARN("[%s] signal queue overflowed- falling back to poll\n",h->name); /* clear real-time signal queue * both SIG_IGN and SIG_DFL are needed , it doesn't work * only with SIG_DFL */ if (signal(h->signo, SIG_IGN)==SIG_ERR){ LM_CRIT("[%s] couldn't reset signal to IGN\n",h->name); } if (signal(h->signo, SIG_DFL)==SIG_ERR){ LM_CRIT("[%s] couldn't reset signal to DFL\n",h->name); } /* falling back to normal poll */ ret=io_wait_loop_poll(h, -1, 1); } end: return ret; error: return -1; } #endif #ifdef HAVE_DEVPOLL inline static int io_wait_loop_devpoll(io_wait_h* h, int t, int repeat) { int n, r; int ret; struct dvpoll dpoll; struct fd_map *e; dpoll.dp_timeout=t*1000; dpoll.dp_nfds=h->fd_no; dpoll.dp_fds=h->dp_changes; again: ret=n=ioctl(h->dpoll_fd, DP_POLL, &dpoll); if (n==-1){ if (errno==EINTR) goto again; /* signal, ignore it */ else{ LM_ERR("[%s] ioctl: %s [%d]\n",h->name, strerror(errno), errno); goto error; } } for (r=0; r< n; r++){ if (h->dp_changes[r].revents & (POLLNVAL|POLLERR)){ LM_ERR("[%s] pollinval returned for fd %d, revents=%x\n", h->name,h->fd_array[r].fd, h->fd_array[r].revents); } /* POLLIN|POLLHUP just go through */ (get_fd_map(h, h->dp_changes[r].fd))->flags |= IO_WATCH_PRV_TRIG_READ; } /* now do the actual running of IO handlers */ for(r=h->fd_no-1; (r>=0) && n ; r--) { e = get_fd_map(h, h->fd_array[r].fd); if ( e->flags & IO_WATCH_PRV_TRIG_READ ) { e->flags &= ~IO_WATCH_PRV_TRIG_READ; while((handle_io( e, r, IO_WATCH_READ)>0) && repeat); n--; } } error: return ret; } #endif #endif opensips-2.2.2/ip_addr.c000066400000000000000000000106351300170765700151360ustar00rootroot00000000000000/* * ip address & address family related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free * 2004-10-01 mk_net fixes bad network addresses now (andrei) */ /*! * \file * \brief OpenSIPS IP address & address family related functions */ #include #include #include "ip_addr.h" #include "dprint.h" #include "mem/mem.h" char _ip_addr_A_buff[IP_ADDR_MAX_STR_SIZE]; struct net* mk_net(struct ip_addr* ip, struct ip_addr* mask) { struct net* n; int warning; unsigned int r; warning=0; if ((ip->af != mask->af) || (ip->len != mask->len)){ LM_CRIT("trying to use a different mask family" " (eg. ipv4/ipv6mask or ipv6/ipv4mask)\n"); goto error; } n=(struct net*)pkg_malloc(sizeof(struct net)); if (n==0){ LM_CRIT("memory allocation failure\n"); goto error; } n->ip=*ip; n->mask=*mask; for (r=0; rip.len/4; r++) { /*ipv4 & ipv6 addresses are multiple of 4*/ n->ip.u.addr32[r] &= n->mask.u.addr32[r]; if (n->ip.u.addr32[r]!=ip->u.addr32[r]) warning=1; }; if (warning){ LM_WARN("invalid network address/netmask " "combination fixed...\n"); print_ip("original network address:", ip, "/"); print_ip("", mask, "\n"); print_ip("fixed network address:", &(n->ip), "/"); print_ip("", &(n->mask), "\n"); }; return n; error: return 0; } struct net* mk_net_bitlen(struct ip_addr* ip, unsigned int bitlen) { struct ip_addr mask; unsigned int r; if (bitlen>ip->len*8){ LM_CRIT("bad bitlen number %d\n", bitlen); goto error; } memset(&mask,0, sizeof(mask)); for (r=0;raf; mask.len=ip->len; return mk_net(ip, &mask); error: return 0; } void print_ip(char* p, struct ip_addr* ip, char *s) { switch(ip->af){ case AF_INET: LM_DBG("%s%d.%d.%d.%d%s", (p)?p:"", ip->u.addr[0], ip->u.addr[1], ip->u.addr[2], ip->u.addr[3], (s)?s:"" ); break; case AF_INET6: LM_DBG("%s%x:%x:%x:%x:%x:%x:%x:%x%s", (p)?p:"", htons(ip->u.addr16[0]), htons(ip->u.addr16[1]), htons(ip->u.addr16[2]), htons(ip->u.addr16[3]), htons(ip->u.addr16[4]), htons(ip->u.addr16[5]), htons(ip->u.addr16[6]), htons(ip->u.addr16[7]), (s)?s:"" ); break; default: LM_DBG("warning unknown address family %d\n", ip->af); } } void stdout_print_ip(struct ip_addr* ip) { switch(ip->af){ case AF_INET: printf("%d.%d.%d.%d", ip->u.addr[0], ip->u.addr[1], ip->u.addr[2], ip->u.addr[3]); break; case AF_INET6: printf("%x:%x:%x:%x:%x:%x:%x:%x", htons(ip->u.addr16[0]), htons(ip->u.addr16[1]), htons(ip->u.addr16[2]), htons(ip->u.addr16[3]), htons(ip->u.addr16[4]), htons(ip->u.addr16[5]), htons(ip->u.addr16[6]), htons(ip->u.addr16[7]) ); break; default: LM_DBG("warning unknown address family %d\n", ip->af); } } void print_net(struct net* net) { if (net==0){ LM_WARN("null pointer\n"); return; } print_ip("", &net->ip, "/"); print_ip("", &net->mask, ""); } #ifdef USE_MCAST /* Returns 1 if the given address is a multicast address */ int is_mcast(struct ip_addr* ip) { if (!ip){ LM_ERR("invalid parameter value\n"); return -1; } if (ip->af==AF_INET){ return IN_MULTICAST(htonl(ip->u.addr32[0])); } else if (ip->af==AF_INET6){ return IN6_IS_ADDR_MULTICAST((struct in6_addr *)ip->u.addr); } else { LM_ERR("unsupported protocol family\n"); return -1; } } #endif /* USE_MCAST */ opensips-2.2.2/ip_addr.h000066400000000000000000000334351300170765700151460ustar00rootroot00000000000000/* * ip address family related structures * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-13 added struct dest_info (andrei) * 2003-04-06 all ports are stored/passed in host byte order now (andrei) */ /*! * \file * \brief ip address family related structures */ #ifndef ip_addr_h #define ip_addr_h #include #include #include #include #include #include #include "str.h" #include "dprint.h" #define MAX_RECV_BUFFER_SIZE 256*1024 #define MAX_SEND_BUFFER_SIZE 512*1024 #define BUFFER_INCREMENT 2048 enum sip_protos { PROTO_NONE = 0, PROTO_FIRST = 1, PROTO_UDP = 1, \ PROTO_TCP, PROTO_TLS, PROTO_SCTP, PROTO_WS, PROTO_WSS, PROTO_BIN, PROTO_HEP_UDP, PROTO_HEP_TCP, PROTO_OTHER }; #define PROTO_LAST PROTO_OTHER struct ip_addr{ unsigned int af; /*!< address family: AF_INET6 or AF_INET */ unsigned int len; /*!< address len, 16 or 4 */ /*! \brief 64 bits aligned address */ union { unsigned long addrl[16/sizeof(long)]; /*!< long format*/ unsigned int addr32[4]; unsigned short addr16[8]; unsigned char addr[16]; }u; }; struct net{ struct ip_addr ip; struct ip_addr mask; }; union sockaddr_union{ struct sockaddr s; struct sockaddr_in sin; struct sockaddr_in6 sin6; }; enum si_flags { SI_NONE=0, SI_IS_IP=1, SI_IS_LO=2, SI_IS_MCAST=4 }; struct socket_info { int socket; str name; /*!< name - eg.: foo.bar or 10.0.0.1 */ struct ip_addr address; /*!< ip address */ str address_str; /*!< ip address converted to string -- optimization*/ unsigned short port_no; /*!< port number */ str port_no_str; /*!< port number converted to string -- optimization*/ enum si_flags flags; /*!< SI_IS_IP | SI_IS_LO | SI_IS_MCAST */ union sockaddr_union su; int proto; /*!< tcp or udp*/ str sock_str; str adv_sock_str; str adv_name_str; /* Advertised name of this interface */ str adv_port_str; /* Advertised port of this interface */ struct ip_addr adv_address; /* Advertised address in ip_addr form (for find_si) */ unsigned short adv_port; /* optimization for grep_sock_info() */ unsigned short children; struct socket_info* next; struct socket_info* prev; }; struct receive_info { struct ip_addr src_ip; struct ip_addr dst_ip; unsigned short src_port; /*!< host byte order */ unsigned short dst_port; /*!< host byte order */ int proto; int proto_reserved1; /*!< tcp stores the connection id here */ int proto_reserved2; union sockaddr_union src_su; /*!< useful for replies*/ struct socket_info* bind_address; /*!< sock_info structure on which the msg was received*/ /* no need for dst_su yet */ }; struct dest_info { int proto; int proto_reserved1; /*!< tcp stores the connection id here */ union sockaddr_union to; struct socket_info* send_sock; }; struct socket_id { char* name; char* adv_name; int adv_port; int proto; int port; int children; struct socket_id* next; }; /* len of the sockaddr */ #ifdef HAVE_SOCKADDR_SA_LEN #define sockaddru_len(su) ((su).s.sa_len) #else #define sockaddru_len(su) \ (((su).s.sa_family==AF_INET6)?sizeof(struct sockaddr_in6):\ sizeof(struct sockaddr_in)) #endif /* HAVE_SOCKADDR_SA_LEN*/ /*! \brief inits an ip_addr with the addr. info from a hostent structure * ip = struct ip_addr* * he= struct hostent* */ #define hostent2ip_addr(ip, he, addr_no) \ do{ \ (ip)->af=(he)->h_addrtype; \ (ip)->len=(he)->h_length; \ memcpy((ip)->u.addr, (he)->h_addr_list[(addr_no)], (ip)->len); \ }while(0) #define get_su_info(_su, _ip_char, _port_no) \ do { \ struct ip_addr __ip; \ sockaddr2ip_addr( &__ip, (struct sockaddr*)_su ); \ _ip_char = ip_addr2a(&__ip); \ _port_no = su_getport( (union sockaddr_union*)_su); \ } while(0) /* gets the protocol family corresponding to a specific address family * ( PF_INET - AF_INET, PF_INET6 - AF_INET6, af for others) */ #define AF2PF(af) (((af)==AF_INET)?PF_INET:((af)==AF_INET6)?PF_INET6:(af)) struct net* mk_net(struct ip_addr* ip, struct ip_addr* mask); struct net* mk_net_bitlen(struct ip_addr* ip, unsigned int bitlen); void print_ip(char* prefix, struct ip_addr* ip, char* suffix); void stdout_print_ip(struct ip_addr* ip); void print_net(struct net* net); #ifdef USE_MCAST /*! \brief Returns 1 if the given address is a multicast address */ int is_mcast(struct ip_addr* ip); #endif /* USE_MCAST */ /*! \brief returns 1 if ip & net.mask == net.ip ; 0 otherwise & -1 on error [ diff. address families ]) */ inline static int matchnet(struct ip_addr* ip, struct net* net) { unsigned int r; /* int ret; ret=-1; */ if (ip->af == net->ip.af){ for(r=0; rlen/4; r++){ /* ipv4 & ipv6 addresses are all multiple of 4*/ if ((ip->u.addr32[r]&net->mask.u.addr32[r])!= net->ip.u.addr32[r]){ return 0; } } return 1; }; return -1; } /*! \brief inits an ip_addr pointer from a sockaddr structure*/ static inline void sockaddr2ip_addr(struct ip_addr* ip, struct sockaddr* sa) { switch(sa->sa_family){ case AF_INET: ip->af=AF_INET; ip->len=4; memcpy(ip->u.addr, &((struct sockaddr_in*)sa)->sin_addr, 4); break; case AF_INET6: ip->af=AF_INET6; ip->len=16; memcpy(ip->u.addr, &((struct sockaddr_in6*)sa)->sin6_addr, 16); break; default: LM_CRIT("unknown address family %d\n", sa->sa_family); /* clear the structure to prevent uninitialized warnings */ ip->af=sa->sa_family; } } /*! \brief compare 2 ip_addrs (both args are pointers)*/ #define ip_addr_cmp(ip1, ip2) \ (((ip1)->af==(ip2)->af)&& \ (memcmp((ip1)->u.addr, (ip2)->u.addr, (ip1)->len)==0)) /*! \brief compare 2 sockaddr_unions */ static inline int su_cmp(union sockaddr_union* s1, union sockaddr_union* s2) { if (s1->s.sa_family!=s2->s.sa_family) return 0; switch(s1->s.sa_family){ case AF_INET: return (s1->sin.sin_port==s2->sin.sin_port)&& (memcmp(&s1->sin.sin_addr, &s2->sin.sin_addr, 4)==0); case AF_INET6: return (s1->sin6.sin6_port==s2->sin6.sin6_port)&& (memcmp(&s1->sin6.sin6_addr, &s2->sin6.sin6_addr, 16)==0); default: LM_CRIT("unknown address family %d\n", s1->s.sa_family); return 0; } } /*! \brief gets the port number (host byte order) */ static inline unsigned short su_getport(union sockaddr_union* su) { if(su==0) return 0; switch(su->s.sa_family){ case AF_INET: return ntohs(su->sin.sin_port); case AF_INET6: return ntohs(su->sin6.sin6_port); default: LM_CRIT("unknown address family %d\n", su->s.sa_family); return 0; } } /*! \brief sets the port number (host byte order) */ static inline void su_setport(union sockaddr_union* su, unsigned short port) { switch(su->s.sa_family){ case AF_INET: su->sin.sin_port=htons(port); break; case AF_INET6: su->sin6.sin6_port=htons(port); break; default: LM_CRIT("unknown address family %d\n", su->s.sa_family); } } /*! \brief inits an ip_addr pointer from a sockaddr_union ip address */ static inline void su2ip_addr(struct ip_addr* ip, union sockaddr_union* su) { switch(su->s.sa_family){ case AF_INET: ip->af=AF_INET; ip->len=4; memcpy(ip->u.addr, &su->sin.sin_addr, 4); break; case AF_INET6: ip->af=AF_INET6; ip->len=16; memcpy(ip->u.addr, &su->sin6.sin6_addr, 16); break; default: LM_CRIT("Unknown address family %d\n", su->s.sa_family); ip->af=0; ip->len=0; } } /*! \brief ip_addr2su -> the same as \ref init_su() */ #define ip_addr2su init_su /*! \brief inits a struct sockaddr_union from a struct ip_addr and a port no * \return 0 if ok, -1 on error (unknown address family) * \note the port number is in host byte order */ static inline int init_su( union sockaddr_union* su, struct ip_addr* ip, unsigned short port ) { memset(su, 0, sizeof(union sockaddr_union));/*needed on freebsd*/ su->s.sa_family=ip->af; switch(ip->af){ case AF_INET6: memcpy(&su->sin6.sin6_addr, ip->u.addr, ip->len); #ifdef HAVE_SOCKADDR_SA_LEN su->sin6.sin6_len=sizeof(struct sockaddr_in6); #endif su->sin6.sin6_port=htons(port); break; case AF_INET: memcpy(&su->sin.sin_addr, ip->u.addr, ip->len); #ifdef HAVE_SOCKADDR_SA_LEN su->sin.sin_len=sizeof(struct sockaddr_in); #endif su->sin.sin_port=htons(port); break; default: LM_CRIT("unknown address family %d\n", ip->af); return -1; } return 0; } /*! \brief inits a struct sockaddr_union from a struct hostent, an address index in * the hostent structure and a port no. (host byte order) * WARNING: no index overflow checks! * \return 0 if ok, -1 on error (unknown address family) */ static inline int hostent2su( union sockaddr_union* su, struct hostent* he, unsigned int idx, unsigned short port ) { memset(su, 0, sizeof(union sockaddr_union)); /*needed on freebsd*/ su->s.sa_family=he->h_addrtype; switch(he->h_addrtype){ case AF_INET6: memcpy(&su->sin6.sin6_addr, he->h_addr_list[idx], he->h_length); #ifdef HAVE_SOCKADDR_SA_LEN su->sin6.sin6_len=sizeof(struct sockaddr_in6); #endif su->sin6.sin6_port=htons(port); break; case AF_INET: memcpy(&su->sin.sin_addr, he->h_addr_list[idx], he->h_length); #ifdef HAVE_SOCKADDR_SA_LEN su->sin.sin_len=sizeof(struct sockaddr_in); #endif su->sin.sin_port=htons(port); break; default: LM_CRIT("unknown address family %d\n", he->h_addrtype); return -1; } return 0; } /*! \brief maximum size of a str returned by ip_addr2a (including \\0') */ #define IP_ADDR_MAX_STR_SIZE 40 /* 1234:5678:9012:3456:7890:1234:5678:9012\0 */ /*! \brief fast ip_addr -> string converter; * it uses an internal buffer */ extern char _ip_addr_A_buff[IP_ADDR_MAX_STR_SIZE]; static inline char* ip_addr2a(struct ip_addr* ip) { int offset; register unsigned char a,b,c; register unsigned char d; register unsigned short hex4; int r; #define HEXDIG(x) (((x)>=10)?(x)-10+'A':(x)+'0') offset=0; switch(ip->af){ case AF_INET6: for(r=0;r<7;r++){ hex4=ntohs(ip->u.addr16[r]); a=hex4>>12; b=(hex4>>8)&0xf; c=(hex4>>4)&0xf; d=hex4&0xf; if (a){ _ip_addr_A_buff[offset]=HEXDIG(a); _ip_addr_A_buff[offset+1]=HEXDIG(b); _ip_addr_A_buff[offset+2]=HEXDIG(c); _ip_addr_A_buff[offset+3]=HEXDIG(d); _ip_addr_A_buff[offset+4]=':'; offset+=5; }else if(b){ _ip_addr_A_buff[offset]=HEXDIG(b); _ip_addr_A_buff[offset+1]=HEXDIG(c); _ip_addr_A_buff[offset+2]=HEXDIG(d); _ip_addr_A_buff[offset+3]=':'; offset+=4; }else if(c){ _ip_addr_A_buff[offset]=HEXDIG(c); _ip_addr_A_buff[offset+1]=HEXDIG(d); _ip_addr_A_buff[offset+2]=':'; offset+=3; }else{ _ip_addr_A_buff[offset]=HEXDIG(d); _ip_addr_A_buff[offset+1]=':'; offset+=2; } } /* last int16*/ hex4=ntohs(ip->u.addr16[r]); a=hex4>>12; b=(hex4>>8)&0xf; c=(hex4>>4)&0xf; d=hex4&0xf; if (a){ _ip_addr_A_buff[offset]=HEXDIG(a); _ip_addr_A_buff[offset+1]=HEXDIG(b); _ip_addr_A_buff[offset+2]=HEXDIG(c); _ip_addr_A_buff[offset+3]=HEXDIG(d); _ip_addr_A_buff[offset+4]=0; }else if(b){ _ip_addr_A_buff[offset]=HEXDIG(b); _ip_addr_A_buff[offset+1]=HEXDIG(c); _ip_addr_A_buff[offset+2]=HEXDIG(d); _ip_addr_A_buff[offset+3]=0; }else if(c){ _ip_addr_A_buff[offset]=HEXDIG(c); _ip_addr_A_buff[offset+1]=HEXDIG(d); _ip_addr_A_buff[offset+2]=0; }else{ _ip_addr_A_buff[offset]=HEXDIG(d); _ip_addr_A_buff[offset+1]=0; } break; case AF_INET: for(r=0;r<3;r++){ a=ip->u.addr[r]/100; c=ip->u.addr[r]%10; b=ip->u.addr[r]%100/10; if (a){ _ip_addr_A_buff[offset]=a+'0'; _ip_addr_A_buff[offset+1]=b+'0'; _ip_addr_A_buff[offset+2]=c+'0'; _ip_addr_A_buff[offset+3]='.'; offset+=4; }else if (b){ _ip_addr_A_buff[offset]=b+'0'; _ip_addr_A_buff[offset+1]=c+'0'; _ip_addr_A_buff[offset+2]='.'; offset+=3; }else{ _ip_addr_A_buff[offset]=c+'0'; _ip_addr_A_buff[offset+1]='.'; offset+=2; } } /* last number */ a=ip->u.addr[r]/100; c=ip->u.addr[r]%10; b=ip->u.addr[r]%100/10; if (a){ _ip_addr_A_buff[offset]=a+'0'; _ip_addr_A_buff[offset+1]=b+'0'; _ip_addr_A_buff[offset+2]=c+'0'; _ip_addr_A_buff[offset+3]=0; }else if (b){ _ip_addr_A_buff[offset]=b+'0'; _ip_addr_A_buff[offset+1]=c+'0'; _ip_addr_A_buff[offset+2]=0; }else{ _ip_addr_A_buff[offset]=c+'0'; _ip_addr_A_buff[offset+1]=0; } break; default: LM_CRIT("unknown address family %d\n", ip->af); return 0; } return _ip_addr_A_buff; } /*! \brief converts an ip_addr structure to a hostent * \return pointer to internal statical structure */ static inline struct hostent* ip_addr2he(str* name, struct ip_addr* ip) { static struct hostent he; static char hostname[256]; static char* p_aliases[1]; static char* p_addr[2]; static char address[16]; int len; p_aliases[0]=0; /* no aliases*/ p_addr[1]=0; /* only one address*/ p_addr[0]=address; len = (name->len < 255) ? name->len : 255; strncpy(hostname, name->s, len); hostname[len] = 0; if (ip->len>16) return 0; memcpy(address, ip->u.addr, ip->len); he.h_addrtype=ip->af; he.h_length=ip->len; he.h_addr_list=p_addr; he.h_aliases=p_aliases; he.h_name=hostname; return &he; } #endif opensips-2.2.2/lock_alloc.h000066400000000000000000000055111300170765700156400ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-06 created by andrei (contains parts of the original locking.h) * 2003-03-17 fixed cast warning in shm_free (forced to void*) (andrei) * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) */ /*! * \file * \brief OpenSIPS locking library * \note WARNING: don't include this directly, use instead locking.h. */ /*! * \page OpenSIPS locking library * Implements simple locks and lock sets. * * simple locks: * - gen_lock_t* lock_alloc(); - allocates a lock in shared mem. * - void lock_dealloc(gen_lock_t* lock); - deallocates the lock's shared m. * * lock sets: [implemented only for FL & SYSV so far] * - gen_lock_set_t* lock_set_alloc(no) - allocs a lock set in shm. * - void lock_set_dealloc(gen_lock_set_t* s); - deallocs the lock set shm. * * \see locking.h */ #ifndef _lock_alloc_h #define _lock_alloc_h /*shm_{malloc, free}*/ #include "mem/mem.h" #include "mem/shm_mem.h" #if defined(FAST_LOCK) || defined(USE_PTHREAD_MUTEX) || defined(USE_POSIX_SEM) /* simple locks*/ #define lock_alloc() shm_malloc(sizeof(gen_lock_t)) #define lock_dealloc(lock) shm_free((void*)lock) /* lock sets */ inline static gen_lock_set_t* lock_set_alloc(int n) { gen_lock_set_t* ls; ls=(gen_lock_set_t*)shm_malloc(sizeof(gen_lock_set_t)+n*sizeof(gen_lock_t)); if (ls==0){ LM_CRIT("no more shm memory\n"); }else{ ls->locks=(gen_lock_t*)((char*)ls+sizeof(gen_lock_set_t)); ls->size=n; } return ls; } #define lock_set_dealloc(lock_set) shm_free((void*)lock_set) #elif defined USE_SYSV_SEM /*simple locks*/ #define lock_alloc() shm_malloc(sizeof(gen_lock_t)) #define lock_dealloc(lock) shm_free((void*)lock) /* lock sets */ inline static gen_lock_set_t* lock_set_alloc(int n) { gen_lock_set_t* ls; ls=(gen_lock_set_t*)shm_malloc(sizeof(gen_lock_set_t)); if (ls){ ls->size=n; ls->semid=-1; }; return ls; } #define lock_set_dealloc(lock_set) shm_free((void*)lock_set) #else #error "no locking method selected" #endif #endif opensips-2.2.2/lock_ops.h000066400000000000000000000205261300170765700153520ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * History: * -------- * 2002-12-16 created by andrei * 2003-02-20 s/gen_lock_t/gen_lock_t/ to avoid a type conflict * on solaris (andrei) * 2003-03-05 lock set support added for FAST_LOCK & SYSV (andrei) * 2003-03-06 removed *_alloc,*_dealloc & moved them to lock_alloc.h * renamed locking.h to lock_ops.h (all this to solve * the locking.h<->shm_mem.h interdependency) (andrei) * 2003-03-10 lock set support added also for PTHREAD_MUTEX & POSIX_SEM * (andrei) * 2003-03-17 possible signal interruptions treated for sysv (andrei) * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) */ /*! * \file * \brief OpenSIPS locking operations * * Implements simple locks and lock sets. * * simple locks: * - gen_lock_t* lock_init(gen_lock_t* lock); - inits the lock * - void lock_destroy(gen_lock_t* lock); - removes the lock (e.g sysv rmid) * - void lock_get(gen_lock_t* lock); - lock (mutex down) * - void lock_release(gen_lock_t* lock); - unlock (mutex up) * * lock sets: [implemented only for FL & SYSV so far] * - gen_lock_set_t* lock_set_init(gen_lock_set_t* set); - inits the lock set * - void lock_set_destroy(gen_lock_set_t* s); - removes the lock set * - void lock_set_get(gen_lock_set_t* s, int i); - locks sem i from the set * - void lock_set_release(gen_lock_set_t* s, int i)- unlocks sem i from the set * * WARNING: * - lock_set_init may fail for large number of sems (e.g. sysv). * - signals are not treated! (some locks are "awakened" by the signals) * * \note Warning: do not include this file directly, use instead locking.h * (unless you don't need to alloc/dealloc locks). * \todo investigate futex support on linux */ #ifndef _lock_ops_h #define _lock_ops_h #ifdef FAST_LOCK #ifdef USE_FUTEX #include "futex_lock.h" typedef fx_lock_t gen_lock_t; #elif defined FAST_LOCK #include "fastlock.h" typedef fl_lock_t gen_lock_t; #endif #define lock_destroy(lock) /* do nothing */ inline static gen_lock_t* lock_init(gen_lock_t* lock) { init_lock(*lock); return lock; } #define lock_release(lock) release_lock(lock) #ifndef DBG_LOCK #define lock_get(lock) get_lock(lock) #else #define lock_get(lock) get_lock(lock, __FILE__, __FUNCTION__, __LINE__) #endif #elif defined USE_PTHREAD_MUTEX #include typedef pthread_mutex_t gen_lock_t; #define lock_destroy(lock) /* do nothing */ inline static gen_lock_t* lock_init(gen_lock_t* lock) { pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr) != 0) return 0; if (pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) != 0) { pthread_mutexattr_destroy(&attr); return 0; } if (pthread_mutex_init(lock, &attr) != 0) { pthread_mutexattr_destroy(&attr); return 0; } pthread_mutexattr_destroy(&attr); return lock; } #define lock_get(lock) pthread_mutex_lock(lock) #define lock_release(lock) pthread_mutex_unlock(lock) #elif defined USE_POSIX_SEM #include typedef sem_t gen_lock_t; #define lock_destroy(lock) /* do nothing */ inline static gen_lock_t* lock_init(gen_lock_t* lock) { if (sem_init(lock, 1, 1)<0) return 0; return lock; } #define lock_get(lock) sem_wait(lock) #define lock_release(lock) sem_post(lock) #elif defined USE_SYSV_SEM #include #include #include #include #include #include #include "dprint.h" extern int uid; /* from main.c */ #if ((defined(HAVE_UNION_SEMUN) || defined(__GNU_LIBRARY__) )&& !defined(_SEM_SEMUN_UNDEFINED)) /* union semun is defined by including sem.h */ #else /* according to X/OPEN we have to define it ourselves */ union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; #endif typedef int gen_lock_t; inline static gen_lock_t* lock_init(gen_lock_t* lock) { union semun su; int euid; euid=geteuid(); if (uid && uid!=euid) seteuid(uid); /* set euid to the cfg. requested one */ *lock=semget(IPC_PRIVATE, 1, 0700); if (uid && uid!=euid) seteuid(euid); /* restore it */ if (*lock==-1) return 0; su.val=1; if (semctl(*lock, 0, SETVAL, su)==-1){ /* init error*/ return 0; } return lock; } //TODO if is init inline static void lock_destroy(gen_lock_t* lock) { union semun su; su.val = 0; semctl(*lock, 0, IPC_RMID, su); } inline static void lock_get(gen_lock_t* lock) { struct sembuf sop; sop.sem_num=0; sop.sem_op=-1; /* down */ sop.sem_flg=0; tryagain: if (semop(*lock, &sop, 1)==-1){ if (errno==EINTR){ LM_DBG("signal received while waiting for on a mutex\n"); goto tryagain; }else{ LM_CRIT("%s (%d)\n", strerror(errno), errno); } } } inline static void lock_release(gen_lock_t* lock) { struct sembuf sop; sop.sem_num=0; sop.sem_op=1; /* up */ sop.sem_flg=0; tryagain: if (semop(*lock, &sop, 1)==-1){ if (errno==EINTR){ /* very improbable*/ LM_DBG("signal received while releasing a mutex\n"); goto tryagain; }else{ LM_CRIT("%s (%d)\n", strerror(errno), errno); } } } #else #error "no locking method selected" #endif /* lock sets */ #if defined(FAST_LOCK) || defined(USE_PTHREAD_MUTEX) || defined(USE_POSIX_SEM) #define GEN_LOCK_T_PREFERED struct gen_lock_set_t_ { long size; gen_lock_t* locks; }; /* must be aligned (32 bits or 64 depending on the arch)*/ typedef struct gen_lock_set_t_ gen_lock_set_t; #define lock_set_destroy(lock_set) /* do nothing */ inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s) { int r; for (r=0; rsize; r++) if (lock_init(&s->locks[r])==0) return 0; return s; } /* WARNING: no boundary checks!*/ #define lock_set_get(set, i) lock_get(&set->locks[i]) #define lock_set_release(set, i) lock_release(&set->locks[i]) #elif defined(USE_SYSV_SEM) #undef GEN_LOCK_T_PREFERED struct gen_lock_set_t_ { int size; int semid; }; typedef struct gen_lock_set_t_ gen_lock_set_t; inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s) { union semun su; int r; int euid; euid=geteuid(); if (uid && uid!=euid) seteuid(uid); /* set euid to the cfg. requested one */ s->semid=semget(IPC_PRIVATE, s->size, 0700); if (uid && uid!=euid) seteuid(euid); /* restore euid */ if (s->semid==-1){ LM_CRIT("semget (..., %d, 0700) failed: %s\n", s->size, strerror(errno)); return 0; } su.val=1; for (r=0; rsize; r++){ if (semctl(s->semid, r, SETVAL, su)==-1){ LM_CRIT("semctl failed on sem %d: %s\n", r, strerror(errno)); su.val = 0; semctl(s->semid, 0, IPC_RMID, su); return 0; } } return s; } inline static void lock_set_destroy(gen_lock_set_t* s) { union semun su; su.val = 0; semctl(s->semid, 0, IPC_RMID, su); } inline static void lock_set_get(gen_lock_set_t* s, int n) { struct sembuf sop; sop.sem_num=n; sop.sem_op=-1; /* down */ sop.sem_flg=0; tryagain: if (semop(s->semid, &sop, 1)==-1){ if (errno==EINTR){ LM_DBG("signal received while waiting on a mutex\n"); goto tryagain; }else{ LM_CRIT("%s (%d)\n", strerror(errno), errno); } } } inline static void lock_set_release(gen_lock_set_t* s, int n) { struct sembuf sop; sop.sem_num=n; sop.sem_op=1; /* up */ sop.sem_flg=0; tryagain: if (semop(s->semid, &sop, 1)==-1){ if (errno==EINTR){ /* very improbable */ LM_DBG("signal received while releasing mutex\n"); goto tryagain; }else{ LM_CRIT("%s (%d)\n", strerror(errno), errno); } } } #else #error "no lock set method selected" #endif #endif opensips-2.2.2/locking.h000066400000000000000000000051411300170765700151630ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2002-12-16 created by andrei * 2003-02-20 s/gen_lock_t/gen_lock_t/ to avoid a type conflict * on solaris (andrei) * 2003-03-05 lock set support added for FAST_LOCK & SYSV (andrei) * 2003-03-06 split in two: lock_ops.h & lock_alloc.h, to avoid * shm_mem.h<->locking.h interdependency (andrei) * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) */ /*! * \file * \brief OpenSIPS locking library * * Implements (in lock_ops.h & lock_alloc.h): * * simple locks: * - type: gen_lock_t * - gen_lock_t* lock_alloc(); - allocates a lock in shared mem. * - gen_lock_t* lock_init(gen_lock_t* lock); - inits the lock * - void lock_destroy(gen_lock_t* lock); - removes the lock (e.g sysv rmid) * - void lock_dealloc(gen_lock_t* lock); - deallocates the lock's shared m. * - void lock_get(gen_lock_t* lock); - lock (mutex down) * - void lock_release(gen_lock_t* lock); - unlock (mutex up) * * lock sets: * - type: gen_lock_set_t * - gen_lock_set_t* lock_set_alloc(no) - allocs a lock set in shm. * - gen_lock_set_t* lock_set_init(gen_lock_set_t* set); - inits the lock set * - void lock_set_destroy(gen_lock_set_t* s); - removes the lock set * - void lock_set_dealloc(gen_lock_set_t* s); - deallocs the lock set shm. * - void lock_set_get(gen_lock_set_t* s, int i); - locks sem i from the set * - void lock_set_release(gen_lock_set_t* s, int i)- unlocks sem i from the set * * WARNING: * - lock_set_init may fail for large number of sems (e.g. sysv). * - signals are not treated! (some locks are "awakened" by the signals) */ #ifndef _locking_h #define _locking_h /* the order is important */ #include "lock_ops.h" #include "lock_alloc.h" #endif opensips-2.2.2/lump_struct.h000066400000000000000000000103721300170765700161200ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file lump_struct.h * \brief Data structures for adding or removing data chunks from messages. */ #ifndef lump_struct_h #define lump_struct_h #include "./parser/hf.h" /*! \brief * Operations on lumps. */ enum lump_op { LUMP_NOP=0, LUMP_DEL, LUMP_ADD, LUMP_ADD_SUBST, LUMP_ADD_OPT , LUMP_SKIP }; /*! \brief * Substitutions for lumps. * Where: * SND = sending, e.g the src ip of the outgoing message * RCV = received e.g the dst ip of the original incoming msg, * or the ip of the socket on which the msg was received. * For SUBST_{RCV,SND}_ALL, :port is added only if port!=5060 * and transport=proto only if proto!=udp is specified. */ enum lump_subst{ SUBST_NOP=0, /*!< do nothing */ SUBST_RCV_IP, SUBST_SND_IP, /*!< add ip address */ SUBST_RCV_PORT, SUBST_SND_PORT, /*!< add port no */ SUBST_RCV_PROTO, SUBST_SND_PROTO,/*!< add protocol(udp,tcp,tls)*/ SUBST_RCV_ALL, SUBST_SND_ALL /*!< ip:port;transport=proto */ }; /*! \brief * Conditions for lumps. * Where: * REALM= ip_addr:port:proto * af = address family (ipv4 or ipv6) * proto = protocol (tcp, udp, tls) */ enum lump_conditions { COND_FALSE, /*!< always false */ COND_TRUE, /*!< always true */ COND_IF_DIFF_REALMS,/*!< true if RCV realm != SND realm */ COND_IF_DIFF_AF, /*!< true if RCV af != SND af */ COND_IF_DIFF_PROTO, /*!< true if RCV proto != SND proto */ COND_IF_DIFF_PORT, /*!< true if RCV port != SND port */ COND_IF_DIFF_IP, /*!< true if RCV ip != SND ip */ }; /*! \brief * Flags for lumps, mainly used from the tm module. */ enum lump_flag { LUMPFLAG_NONE=0, LUMPFLAG_SHMEM=2 , LUMPFLAG_BRANCH=4, LUMPFLAG_COND_TRUE=8, LUMPFLAG_CODEC=16}; /*! \brief * Main lump structure. */ struct lump{ enum _hdr_types_t type; /*!< HDR_VIA_T, HDR_OTHER_T (0), ... */ enum lump_op op; /*!< DEL, ADD, NOP, UNSPEC(=0) */ union{ unsigned int offset; /*!< used for DEL, MODIFY */ enum lump_subst subst; /*!< what to subst: ip addr, port, proto*/ enum lump_conditions cond; /*!< condition for LUMP_ADD_OPT */ char * value; /*!< used for ADD */ }u; unsigned int len; /*!< length of this header field */ struct lump* before; /*!< list of headers to be inserted in front of the current one */ struct lump* after; /*!< list of headers to be inserted immediately after the current one */ struct lump* next; enum lump_flag flags; /*!< additional hints for use from TM's shmem */ }; /* * hdrs must be kept sorted after their offset (DEL, NOP, UNSPEC) * and/or their position (ADD). E.g.: * - to delete header Z insert it in to the list according to its offset * and with op=DELETE * - if you want to add a new header X after a header Y, insert Y in the list * with op NOP and after it X (op ADD). * - if you want X before Y, insert X in Y's before list. * - if you want X to be the first header just put it first in hdr_lst. * -if you want to replace Y with X, insert Y with op=DELETE and then X with * op=ADD. * before and after must contain only ADD ops! * * Difference between "after" & "next" when Adding: * "after" forces the new header immediately after the current one while * "next" means another header can be inserted between them. * */ /*! \brief frees the content of a lump struct */ void free_lump(struct lump* l); /*! \brief frees an entire lump list, recursively */ void free_lump_list(struct lump* lump_list); #endif opensips-2.2.2/main.c000066400000000000000000001002401300170765700144500ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2005-2006 Voice Sistem S.R.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2002-01-29 argc/argv globalized via my_{argc|argv} (jiri) * 2003-01-23 mhomed added (jiri) * 2003-03-19 replaced all malloc/frees w/ pkg_malloc/pkg_free (andrei) * 2003-03-29 pkg cleaners for fifo and script callbacks introduced (jiri) * 2003-03-31 removed snmp part (obsolete & no place in core) (andrei) * 2003-04-06 child_init called in all processes (janakj) * 2003-04-08 init_mallocs split into init_{pkg,shm}_mallocs and * init_shm_mallocs called after cmd. line parsing (andrei) * 2003-04-15 added tcp_disable support (andrei) * 2003-05-09 closelog() before openlog to force opening a new fd * (needed on solaris) (andrei) * 2003-06-11 moved all signal handlers init. in install_sigs and moved it * after daemonize (so that we won't catch anymore our own * SIGCHLD generated when becoming session leader) (andrei) * changed is_main default value to 1 (andrei) * 2003-06-28 kill_all_children is now used instead of kill(0, sig) * see comment above it for explanations. (andrei) * 2003-06-29 replaced port_no_str snprintf w/ int2str (andrei) * 2003-10-10 added switch for config check (-c) (andrei) * 2003-10-24 converted to the new socket_info lists (andrei) * 2004-03-30 core dump is enabled by default * added support for increasing the open files limit (andrei) * 2004-04-28 sock_{user,group,uid,gid,mode} added * user2uid() & user2gid() added (andrei) * 2004-09-11 added timeout on children shutdown and final cleanup * (if it takes more than 60s => something is definitely wrong * => kill all or abort) (andrei) * force a shm_unlock before cleaning-up, in case we have a * crashed childvwhich still holds the lock (andrei) * 2004-12-02 removed -p, extended -l to support [proto:]address[:port], * added parse_phostport, parse_proto (andrei) * 2005-06-16 always record the pid in pt[process_no].pid twice: once in the * parent & once in the child to avoid a short window when one * of them might use it "unset" (andrei) * 2005-12-22 added tos configurability (thanks to Andreas Granig) * 2006-04-26 2-stage TLS init: before and after config file parsing (klaus) */ /*! * \file main.c * \brief Command line parsing, initializiation and server startup. * * Contains methods for parsing the command line, the initialization of * the execution environment (signals, config file parsing) and forking * the TCP, UDP, timer and fifo children. */ #include "reactor_defs.h" /*keep this first*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKIO_H #include #endif #include "help_msg.h" #include "config.h" #include "dprint.h" #include "daemonize.h" #include "route.h" #include "bin_interface.h" #include "globals.h" #include "mem/mem.h" #include "mem/shm_mem.h" #include "sr_module.h" #include "timer.h" #include "parser/msg_parser.h" #include "ip_addr.h" #include "resolve.h" #include "parser/parse_hname2.h" #include "parser/digest/digest_parser.h" #include "name_alias.h" #include "hash_func.h" #include "pt.h" #include "script_cb.h" #include "dset.h" #include "blacklists.h" #include "pt.h" #include "ut.h" #include "serialize.h" #include "statistics.h" #include "core_stats.h" #include "pvar.h" #include "poll_types.h" #include "net/net_tcp.h" #include "net/net_udp.h" #include "version.h" #include "mi/mi_core.h" #include "db/db_insertq.h" #include "net/trans.h" static char* version=OPENSIPS_FULL_VERSION; static char* flags=OPENSIPS_COMPILE_FLAGS; #ifdef VERSION_NODATE char compiled[] = "" ; #else #ifdef VERSION_DATE const char compiled[] = VERSION_DATE ; #else const char compiled[] = __TIME__ " " __DATE__ ; #endif #endif /** * Print compile-time constants */ void print_ct_constants(void) { #ifdef ADAPTIVE_WAIT printf("ADAPTIVE_WAIT_LOOPS=%d, ", ADAPTIVE_WAIT_LOOPS); #endif printf("MAX_RECV_BUFFER_SIZE %d, MAX_LISTEN %d," " MAX_URI_SIZE %d, BUF_SIZE %d\n", MAX_RECV_BUFFER_SIZE, MAX_LISTEN, MAX_URI_SIZE, BUF_SIZE ); printf("poll method support: %s.\n", poll_support); #ifdef VERSIONTYPE printf("%s revision: %s\n", VERSIONTYPE, THISREVISION); #endif } /* global vars */ int own_pgid = 0; /* whether or not we have our own pgid (and it's ok to use kill(0, sig) */ char* cfg_file = 0; unsigned int maxbuffer = MAX_RECV_BUFFER_SIZE; /* maximum buffer size we do not want to exceed during the auto-probing procedure; may be re-configured */ int children_no = CHILD_NO; /* number of children processing requests */ enum poll_types io_poll_method=0; /*!< by default choose the best method */ int sig_flag = 0; /* last signal received */ /* activate debug mode */ int debug_mode = 0; /* do not become daemon, stay attached to the console */ int no_daemon_mode = 0; /* assertion statements in script. disabled by default */ int enable_asserts = 0; /* abort process on failed assertion. disabled by default */ int abort_on_assert = 0; /* start by logging to stderr */ int log_stderr = 1; /* log facility (see syslog(3)) */ int log_facility = LOG_DAEMON; /* the id to be printed in syslog */ char *log_name = 0; int config_check = 0; /* check if reply first via host==us */ int check_via = 0; /* debugging level for memory stats */ int memlog = L_DBG + 10; int memdump = L_DBG + 10; /* debugging in case msg processing takes. too long disabled by default */ int execmsgthreshold = 0; /* debugging in case dns takes too long. disabled by default */ int execdnsthreshold = 0; /* debugging in case tcp stuff take too long. disabled by default */ int tcpthreshold = 0; /* should replies include extensive warnings? by default yes, good for trouble-shooting */ int sip_warning = 0; /* should localy-generated messages include server's signature? */ int server_signature=1; /* Server header to be used when proxy generates request as UAS. Default is to use SERVER_HDR CRLF (assigned later). */ str server_header = {SERVER_HDR,sizeof(SERVER_HDR)-1}; /* User-Agent header to be used when proxy generates request as UAC. Default is to use USER_AGENT CRLF (assigned later). */ str user_agent_header = {USER_AGENT,sizeof(USER_AGENT)-1}; /* should opensips try to locate outbound interface on multihomed * host? by default not -- too expensive */ int mhomed=0; /* use dns and/or rdns or to see if we need to add a ;received=x.x.x.x to via: */ int received_dns = 0; char* working_dir = 0; char* chroot_dir = 0; char* user=0; char* group=0; int uid = 0; int gid = 0; /* more config stuff */ int disable_core_dump=0; /* by default enabled */ int open_files_limit=-1; /* don't touch it by default */ #ifdef USE_MCAST int mcast_loopback = 0; int mcast_ttl = -1; /* if -1, don't touch it, use the default (usually 1) */ #endif /* USE_MCAST */ int tos = IPTOS_LOWDELAY; struct socket_info* bind_address=0; /* pointer to the crt. proc. listening address*/ /* if aliases should be automatically discovered and added * during fixing listening sockets */ int auto_aliases=1; /* if the stateless forwarding support in core should be * disabled or not */ int sl_fwd_disabled=-1; /* process number - 0 is the main process */ int process_no = 0; /* cfg parsing */ int cfg_errors=0; /* start-up time */ time_t startup_time = 0; /* shared memory (in MB) */ unsigned long shm_mem_size=SHM_MEM_SIZE * 1024 * 1024; unsigned int shm_hash_split_percentage = DEFAULT_SHM_HASH_SPLIT_PERCENTAGE; unsigned int shm_secondary_hash_size = DEFAULT_SHM_SECONDARY_HASH_SIZE; /* packaged memory (in MB) */ unsigned long pkg_mem_size=PKG_MEM_SIZE * 1024 * 1024; /* export command-line to anywhere else */ int my_argc; char **my_argv; extern FILE* yyin; extern int yyparse(); #ifdef DEBUG_PARSER extern int yydebug; #endif int is_main = 1; /* flag = is this the "main" process? */ char* pid_file = 0; /* filename as asked by user */ char* pgid_file = 0; /** * Clean up on exit. This should be called before exiting. * \param show_status set to one to display the mem status */ void cleanup(int show_status) { LM_INFO("cleanup\n"); /*clean-up*/ /* hack: force-unlock the shared memory lock in case some process crashed and let it locked; this will allow an almost gracious shutdown */ if (mem_lock) #ifdef HP_MALLOC { int i; for (i = 0; i < HP_HASH_SIZE; i++) shm_unlock(i); } #else shm_unlock(); #endif handle_ql_shutdown(); destroy_modules(); udp_destroy(); tcp_destroy(); destroy_timer(); destroy_stats_collector(); destroy_script_cb(); pv_free_extra_list(); destroy_argv_list(); destroy_black_lists(); #ifdef PKG_MALLOC if (show_status){ LM_GEN1(memdump, "Memory status (pkg):\n"); pkg_status(); } #endif cleanup_log_level(); if (pt) shm_free(pt); pt=0; if (show_status){ LM_GEN1(memdump, "Memory status (shm):\n"); shm_status(); } /* zero all shmem alloc vars that we still use */ shm_mem_destroy(); if (pid_file) unlink(pid_file); if (pgid_file) unlink(pgid_file); } /** * Tries to send a signal to all our processes * If daemonized is ok to send the signal to all the process group, * however if not daemonized we might end up sending the signal also * to the shell which launched us => most signals will kill it if * it's not in interactive mode and we don't want this. The non-daemonized * case can occur when an error is encountered before daemonize is called * (e.g. when parsing the config file) or when opensips is started in * "dont-fork" mode. * \param signum signal for killing the children */ static void kill_all_children(int signum) { int r; if (own_pgid) { /* check that all processes initialized properly */ for (r=1; r we should kill everything and exit * \param signo signal for killing the children */ static void sig_alarm_kill(int signo) { kill_all_children(SIGKILL); /* this will kill the whole group including "this" process; for debugging replace with SIGABRT (but warning: it might generate lots of cores) */ } /** * Timeout handler during wait for children exit. * like sig_alarm_kill, but the timeout has occurred when cleaning up, * try to leave a core for future diagnostics * \param signo signal for killing the children * \see sig_alarm_kill */ static void sig_alarm_abort(int signo) { /* LOG is not signal safe, but who cares, we are abort-ing anyway :-) */ LM_CRIT("BUG - shutdown timeout triggered, dying..."); abort(); } /** * Signal handler for the server. */ void handle_sigs(void) { pid_t chld; int chld_status,overall_status=0; int i; int do_exit; const unsigned int shutdown_time = 60; /* one minute close timeout */ switch(sig_flag){ case 0: break; /* do nothing*/ case SIGPIPE: /* SIGPIPE might be rarely received on use of exec module; simply ignore it */ LM_WARN("SIGPIPE received and ignored\n"); break; case SIGINT: case SIGTERM: /* we end the program in all these cases */ if (sig_flag==SIGINT) LM_DBG("INT received, program terminates\n"); else LM_DBG("SIGTERM received, program terminates\n"); /* first of all, kill the children also */ kill_all_children(SIGTERM); if (signal(SIGALRM, sig_alarm_kill) == SIG_ERR ) { LM_ERR("could not install SIGALARM handler\n"); /* continue, the process will die anyway if no * alarm is installed which is exactly what we want */ } alarm(shutdown_time); while(wait(0) > 0); /* Wait for all the children to terminate */ signal(SIGALRM, sig_alarm_abort); cleanup(1); /* cleanup & show status*/ alarm(0); signal(SIGALRM, SIG_IGN); dprint("Thank you for flying " NAME "\n"); exit(0); break; case SIGUSR1: #ifdef PKG_MALLOC LM_GEN1(memdump, "Memory status (pkg):\n"); pkg_status(); #endif LM_GEN1(memdump, "Memory status (shm):\n"); shm_status(); break; case SIGUSR2: #ifdef PKG_MALLOC set_pkg_stats( get_pkg_status_holder(process_no) ); #endif break; case SIGCHLD: do_exit = 0; while ((chld=waitpid( -1, &chld_status, WNOHANG ))>0) { /* is it a process we know about? */ for( i=0 ; i 0); /* wait for all the children to terminate*/ signal(SIGALRM, sig_alarm_abort); cleanup(1); /* cleanup & show status*/ alarm(0); signal(SIGALRM, SIG_IGN); LM_DBG("terminating due to SIGCHLD\n"); exit(overall_status ? -1 : 0); break; case SIGHUP: /* ignoring it*/ LM_DBG("SIGHUP received, ignoring it\n"); break; default: LM_CRIT("unhandled signal %d\n", sig_flag); } sig_flag=0; } /** * Exit regulary on a specific signal. * This is good for profiling which only works if exited regularly * and not by default signal handlers * \param signo The signal that should be handled */ static void sig_usr(int signo) { int status; pid_t pid; UNUSED(pid); if (is_main){ if (sig_flag==0) sig_flag=signo; else /* previous sig. not processed yet, ignoring? */ return; ; }else{ /* process the important signals */ switch(signo){ case SIGPIPE: LM_INFO("signal %d received\n", signo); break; case SIGINT: case SIGTERM: LM_INFO("signal %d received\n", signo); /* print memory stats for non-main too */ #ifdef PKG_MALLOC LM_GEN1(memdump, "Memory status (pkg):\n"); pkg_status(); #endif exit(0); break; case SIGUSR1: /* statistics -> show only pkg mem */ #ifdef PKG_MALLOC LM_GEN1(memdump, "Memory status (pkg):\n"); pkg_status(); #endif break; case SIGUSR2: #ifdef PKG_MALLOC set_pkg_stats( get_pkg_status_holder(process_no) ); #endif break; case SIGHUP: /* ignored*/ break; case SIGCHLD: pid = waitpid(-1, &status, WNOHANG); LM_DBG("SIGCHLD received from %ld (status=%d), ignoring\n", (long)pid,status); } } } /** * Install the signal handlers. * \return 0 on success, -1 on error */ int install_sigs(void) { /* added by jku: add exit handler */ if (signal(SIGINT, sig_usr) == SIG_ERR ) { LM_ERR("no SIGINT signal handler can be installed\n"); goto error; } /* if we debug and write to a pipe, we want to exit nicely too */ if (signal(SIGPIPE, sig_usr) == SIG_ERR ) { LM_ERR("no SIGINT signal handler can be installed\n"); goto error; } if (signal(SIGUSR1, sig_usr) == SIG_ERR ) { LM_ERR("no SIGUSR1 signal handler can be installed\n"); goto error; } if (signal(SIGCHLD , sig_usr) == SIG_ERR ) { LM_ERR("no SIGCHLD signal handler can be installed\n"); goto error; } if (signal(SIGTERM , sig_usr) == SIG_ERR ) { LM_ERR("no SIGTERM signal handler can be installed\n"); goto error; } if (signal(SIGHUP , sig_usr) == SIG_ERR ) { LM_ERR("no SIGHUP signal handler can be installed\n"); goto error; } if (signal(SIGUSR2 , sig_usr) == SIG_ERR ) { LM_ERR("no SIGUSR2 signal handler can be installed\n"); goto error; } return 0; error: return -1; } /** * Main loop, forks the children, bind to addresses, * handle signals. * \return don't return on sucess, -1 on error */ static int main_loop(void) { static int chd_rank; int* startup_done = NULL; chd_rank=0; if (start_module_procs()!=0) { LM_ERR("failed to fork module processes\n"); goto error; } if(startup_rlist.a) {/* if a startup route was defined */ startup_done = (int*)shm_malloc(sizeof(int)); if(startup_done == NULL) { LM_ERR("No more shared memory\n"); goto error; } *startup_done = 0; } /* fork for the timer process*/ if (start_timer_processes()!=0) { LM_CRIT("cannot start timer process(es)\n"); goto error; } /* fork all processes required by UDP network layer */ if (udp_start_processes( &chd_rank, startup_done)<0) { LM_CRIT("cannot start TCP processes\n"); goto error; } /* fork all processes required by TCP network layer */ if (tcp_start_processes( &chd_rank, startup_done)<0) { LM_CRIT("cannot start TCP processes\n"); goto error; } /* fork for the extra timer processes */ if (start_timer_extra_processes( &chd_rank )!=0) { LM_CRIT("cannot start timer extra process(es)\n"); goto error; } /* fork the TCP listening process */ if (tcp_start_listener()<0) { LM_CRIT("cannot start TCP listener process\n"); goto error; } /* this is the main process -> it shouldn't send anything */ bind_address=0; if (startup_done) { if (*startup_done==0) LM_CRIT("BUG: startup route defined, but not run :( \n"); shm_free(startup_done); } /* main process left */ is_main=1; set_proc_attrs("attendant"); if (init_child(PROC_MAIN) < 0) { LM_ERR("error in init_child for PROC_MAIN\n"); report_failure_status(); goto error; } report_conditional_status( (!no_daemon_mode), 0); for(;;){ handle_sigs(); pause(); } /*return 0; */ error: is_main=1; /* if we are here, we are the "main process", any forked children should exit with exit(-1) and not ever use return */ report_failure_status(); return -1; } /** * Main routine, start of the program execution. * \param argc the number of arguments * \param argv pointer to the arguments array * \return don't return on sucess, -1 on error * \see main_loop */ int main(int argc, char** argv) { /* configure by default logging to syslog */ int cfg_log_stderr = 1; FILE* cfg_stream; int c,r; char *tmp; int tmp_len; int port; int proto; int protos_no; char *options; int ret; unsigned int seed; int rfd; /*init*/ ret=-1; my_argc=argc; my_argv=argv; /* process pkg mem size from command line */ opterr=0; options="f:cCm:M:b:l:n:N:rRvdDFETSVhw:t:u:g:P:G:W:o:"; while((c=getopt(argc,argv,options))!=-1){ switch(c){ case 'M': pkg_mem_size=strtol(optarg, &tmp, 10) * 1024 * 1024; if (tmp &&(*tmp)){ LM_ERR("bad pkgmem size number: -m %s\n", optarg); goto error00; }; break; } } /*init pkg mallocs (before parsing cfg but after parsing cmd line !)*/ if (init_pkg_mallocs()==-1) goto error00; init_route_lists(); /* process command line (get port no, cfg. file path etc) */ /* first reset getopt */ optind = 1; while((c=getopt(argc,argv,options))!=-1){ switch(c){ case 'f': cfg_file=optarg; break; case 'C': config_check |= 2; case 'c': if (config_check==3) break; config_check |= 1; cfg_log_stderr=1; /* force stderr logging */ break; case 'm': shm_mem_size=strtol(optarg, &tmp, 10) * 1024 * 1024; if (tmp &&(*tmp)){ LM_ERR("bad shmem size number: -m %s\n", optarg); goto error00; }; break; case 'M': /* ignoring it, parsed previously */ break; case 'b': maxbuffer=strtol(optarg, &tmp, 10); if (tmp &&(*tmp)){ LM_ERR("bad max buffer size number: -b %s\n", optarg); goto error00; } break; case 'l': if (parse_phostport(optarg, strlen(optarg), &tmp, &tmp_len, &port, &proto)<0){ LM_ERR("bad -l address specifier: %s\n", optarg); goto error00; } tmp[tmp_len]=0; /* null terminate the host */ /* add a new addr. to our address list */ if (add_cmd_listener(tmp, port, proto)!=0){ LM_ERR("failed to add new listen address\n"); goto error00; } break; case 'n': children_no=strtol(optarg, &tmp, 10); if ((tmp==0) ||(*tmp)){ LM_ERR("bad process number: -n %s\n", optarg); goto error00; } break; case 'v': check_via=1; break; case 'r': received_dns|=DO_DNS; break; case 'R': received_dns|=DO_REV_DNS; break; case 'd': *log_level = debug_mode ? L_DBG : (*log_level)+1; break; case 'D': debug_mode=1; *log_level = L_DBG; break; case 'F': no_daemon_mode=1; break; case 'E': cfg_log_stderr=1; break; case 'N': tcp_children_no=strtol(optarg, &tmp, 10); if ((tmp==0) ||(*tmp)){ LM_ERR("bad process number: -N %s\n", optarg); goto error00; } break; case 'W': io_poll_method=get_poll_type(optarg); if (io_poll_method==POLL_NONE){ LM_ERR("bad poll method name: -W %s\ntry " "one of %s.\n", optarg, poll_support); goto error00; } break; case 'V': printf("version: %s\n", version); printf("flags: %s\n", flags ); print_ct_constants(); printf("%s compiled on %s with %s\n", __FILE__, compiled, COMPILER ); exit(0); break; case 'h': printf("version: %s\n", version); printf("%s",help_msg); exit(0); break; case 'w': working_dir=optarg; break; case 't': chroot_dir=optarg; break; case 'u': user=optarg; break; case 'g': group=optarg; break; case 'P': pid_file=optarg; break; case 'G': pgid_file=optarg; break; case 'o': if (add_arg_var(optarg) < 0) LM_ERR("cannot add option %s\n", optarg); break; case '?': if (isprint(optopt)) LM_ERR("Unknown option `-%c`.\n", optopt); else LM_ERR("Unknown option character `\\x%x`.\n", optopt); goto error00; case ':': LM_ERR("Option `-%c` requires an argument.\n", optopt); goto error00; default: abort(); } } log_stderr = cfg_log_stderr; /* fill missing arguments with the default values*/ if (cfg_file==0) cfg_file=CFG_FILE; /* load config file or die */ cfg_stream=fopen (cfg_file, "r"); if (cfg_stream==0){ LM_ERR("loading config file(%s): %s\n", cfg_file, strerror(errno)); goto error00; } /* seed the prng, try to use /dev/urandom if possible */ /* no debugging information is logged, because the standard log level prior the config file parsing is L_NOTICE */ seed=0; if ((rfd=open("/dev/urandom", O_RDONLY))!=-1){ try_again: if (read(rfd, (void*)&seed, sizeof(seed))==-1){ if (errno==EINTR) goto try_again; /* interrupted by signal */ LM_WARN("could not read from /dev/urandom (%d)\n", errno); } LM_DBG("initialize the pseudo random generator from " "/dev/urandom\n"); LM_DBG("read %u from /dev/urandom\n", seed); close(rfd); }else{ LM_WARN("could not open /dev/urandom (%d)\n", errno); LM_WARN("using a unsafe seed for the pseudo random number generator"); } seed+=getpid()+time(0); LM_DBG("seeding PRNG with %u\n", seed); srand(seed); LM_DBG("test random number %u\n", rand()); /*register builtin modules*/ register_builtin_modules(); /* init avps */ if (init_global_avps() != 0) { LM_ERR("error while initializing avps\n"); goto error; } /* used for parser debugging */ #ifdef DEBUG_PARSER yydebug = 1; #endif /* get uid/gid */ if (user){ if (user2uid(&uid, &gid, user)<0){ LM_ERR("bad user name/uid number: -u %s\n", user); goto error00; } } if (group){ if (group2gid(&gid, group)<0){ LM_ERR("bad group name/gid number: -u %s\n", group); goto error00; } } /*init shm mallocs * this must be here * -to allow setting shm mem size from the command line * -it must be also before init_timer and init_tcp * -it must be after we know uid (so that in the SYSV sems case, * the sems will have the correct euid) * --andrei */ if (init_shm_mallocs()==-1) goto error; if (init_stats_collector() < 0) { LM_ERR("failed to initialize statistics\n"); goto error; } /* parse the config file, prior to this only default values e.g. for debugging settings will be used */ yyin=cfg_stream; if ((yyparse()!=0)||(cfg_errors)){ LM_ERR("bad config file (%d errors)\n", cfg_errors); goto error00; } if (config_check>1 && check_rls()!=0) { LM_ERR("bad function call in config file\n"); return ret; } if (solve_module_dependencies(modules) != 0) { LM_ERR("failed to solve module dependencies\n"); return -1; } /* init the resolver, before fixing the config */ resolv_init(); fix_poll_method( &io_poll_method ); /* fix temporary listeners added in the cmd line */ if (fix_cmd_listeners() < 0) { LM_ERR("cannot add temproray listeners\n"); return ret; } /* load transport protocols */ protos_no = trans_load(); if (protos_no < 0) { LM_ERR("cannot load transport protocols\n"); goto error; } else if (protos_no == 0) { LM_ERR("no transport protocol loaded\n"); goto error; } else LM_DBG("Loaded %d transport protocols\n", protos_no); /* fix parameters */ if (working_dir==0) working_dir="/"; if (fix_all_socket_lists()!=0){ LM_ERR("failed to initialize list addresses\n"); goto error00; } /* print all the listen addresses */ printf("Listening on \n"); print_all_socket_lists(); printf("Aliases: \n"); /*print_aliases();*/ print_aliases(); printf("\n"); if (config_check){ LM_NOTICE("config file ok, exiting...\n"); return 0; } time(&startup_time); /* Init statistics */ init_shm_statistics(); /*init UDP networking layer*/ if (udp_init()<0){ LM_CRIT("could not initialize tcp\n"); goto error; } /*init TCP networking layer*/ if (tcp_init()<0){ LM_CRIT("could not initialize tcp\n"); goto error; } if (create_status_pipe() < 0) { LM_ERR("failed to create status pipe\n"); goto error; } if (debug_mode) { LM_NOTICE("DEBUG MODE activated\n"); if (no_daemon_mode==0) { LM_NOTICE("disabling daemon mode (found enabled)\n"); no_daemon_mode = 1; } if (log_stderr==0) { LM_NOTICE("enabling logging to standard error (found disabled)\n"); log_stderr = 1; } if (*log_level0) { if(set_open_fds_limit()<0){ LM_ERR("ERROR: error could not increase file limits\n"); goto error; } } /* print OpenSIPS version to log for history tracking */ LM_NOTICE("version: %s\n", version); /* print some data about the configuration */ LM_INFO("using %ld Mb shared memory\n", ((shm_mem_size/1024)/1024)); LM_INFO("using %ld Mb private memory per process\n", ((pkg_mem_size/1024)/1024)); /* init async reactor */ if (init_reactor_size()<0) { LM_CRIT("failed to init internal reactor, exiting...\n"); goto error; } /* init timer */ if (init_timer()<0){ LM_CRIT("could not initialize timer, exiting...\n"); goto error; } /* init serial forking engine */ if (init_serialization()!=0) { LM_ERR("failed to initialize serialization\n"); goto error; } /* Init MI */ if (init_mi_core()<0) { LM_ERR("failed to initialize MI core\n"); goto error; } /* Register core events */ if (evi_register_core() != 0) { LM_ERR("failed register core events\n"); goto error; } /* init black list engine */ if (init_black_lists()!=0) { LM_CRIT("failed to init blacklists\n"); goto error; } /* init resolver's blacklist */ if (resolv_blacklist_init()!=0) { LM_CRIT("failed to create DNS blacklist\n"); goto error; } if (init_dset() != 0) { LM_ERR("failed to initialize SIP forking logic!\n"); goto error; } /* init modules */ if (init_modules() != 0) { LM_ERR("error while initializing modules\n"); goto error; } /* register route timers */ if(register_route_timers() < 0) { LM_ERR("Failed to register timer\n"); goto error; } /* check pv context list */ if(pv_contextlist_check() != 0) { LM_ERR("used pv context that was not defined\n"); goto error; } /* init query list now in shm * so all processes that will be forked from now on * will have access to it * * if it fails, give it a try and carry on */ if (init_ql_support() != 0) { LM_ERR("failed to initialise buffering query list\n"); query_buffer_size = 0; *query_list = NULL; } /* init multi processes support */ if (init_multi_proc_support()!=0) { LM_ERR("failed to init multi-proc support\n"); goto error; } #ifdef PKG_MALLOC /* init stats support for pkg mem */ if (init_pkg_stats(counted_processes)!=0) { LM_ERR("failed to init stats for pkg\n"); goto error; } #endif /* init avps */ if (init_extra_avps() != 0) { LM_ERR("error while initializing avps\n"); goto error; } /* fix routing lists */ if ( (r=fix_rls())!=0){ LM_ERR("failed to fix configuration with err code %d\n", r); goto error; } if (init_log_level() != 0) { LM_ERR("failed to init logging levels\n"); goto error; } if (trans_init_all_listeners()<0) { LM_ERR("failed to init all SIP listeners, aborting\n"); goto error; } /* all processes should have access to all the sockets (for sending) * so we open all first*/ if (do_suid(uid, gid)==-1) goto error; ret = main_loop(); error: /*kill everything*/ kill_all_children(SIGTERM); /*clean-up*/ cleanup(0); error00: LM_NOTICE("Exiting....\n"); return ret; } opensips-2.2.2/map.c000066400000000000000000000342121300170765700143060ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-09-16 initial version (andreidragus) * */ #include #include #include #include #include "str.h" #include "map.h" #include "mem/mem.h" #include "mem/shm_mem.h" #define avl_malloc(dest,size,flags) do \ { \ if(flags & AVLMAP_SHARED) \ (dest) = shm_malloc(size); \ else \ (dest) = pkg_malloc(size); \ } while(0) #define avl_free(dest,flags) do \ { \ if(flags & AVLMAP_SHARED) \ shm_free(dest); \ else \ pkg_free(dest); \ } while(0) #define min(a,b) ((a)<(b))?(a):(b) static int str_cmp(str s1, str s2) { int ret; ret = strncmp( s1.s, s2.s, min( s1.len, s2.len) ); if( ret == 0) ret = s1.len - s2.len; return ret; } /* Creates and returns a new table with comparison function |compare| using parameter |param| and memory allocator |allocator|. Returns |NULL| if memory allocation failed. */ map_t map_create(int flags) { map_t tree; avl_malloc(tree, sizeof *tree, flags); if (tree == NULL) return NULL; tree->avl_root = NULL; tree->flags = flags; tree->avl_count = 0; return tree; } /* Search |tree| for an item matching |item|, and return it if found. Otherwise return |NULL|. */ void ** map_find( map_t tree, str key) { struct avl_node *p; for (p = tree->avl_root; p != NULL;) { int cmp = str_cmp(key, p->key); if (cmp < 0) p = p->avl_link[0]; else if (cmp > 0) p = p->avl_link[1]; else /* |cmp == 0| */ return & (p->val); } return NULL; } /* Inserts |item| into |tree| and returns a pointer to |item|'s address. If a duplicate item is found in the tree, returns a pointer to the duplicate without inserting |item|. Returns |NULL| in case of memory allocation failure. */ void ** map_get( map_t tree, str key) { struct avl_node *y; /* Top node to update balance factor, and parent. */ struct avl_node *p, *q; /* Iterator, and parent. */ struct avl_node *n; /* Newly inserted node. */ struct avl_node *w; /* New root of rebalanced subtree. */ int dir; /* Direction to descend. */ str key_copy; y = tree->avl_root; dir = 0; for (q = NULL, p = tree->avl_root; p != NULL; q = p, p = p->avl_link[dir]) { int cmp = str_cmp(key, p->key); if (cmp == 0) return &p->val; dir = cmp > 0; if (p->avl_balance != 0) y = p; } avl_malloc( n, sizeof *n, tree->flags ); if (n == NULL) return NULL; tree->avl_count++; n->avl_link[0] = n->avl_link[1] = NULL; n->avl_parent = q; if( !( tree->flags & AVLMAP_NO_DUPLICATE ) ) { avl_malloc(key_copy.s, key.len, tree->flags ); memcpy(key_copy.s,key.s,key.len); key_copy.len = key.len; n->key = key_copy; } else n->key = key; n->val = NULL; if (q != NULL) q->avl_link[dir] = n; else tree->avl_root = n; n->avl_balance = 0; if (tree->avl_root == n) return &n->val; for (p = n; p != y; p = q) { q = p->avl_parent; dir = q->avl_link[0] != p; if (dir == 0) q->avl_balance--; else q->avl_balance++; } if (y->avl_balance == -2) { struct avl_node *x = y->avl_link[0]; if (x->avl_balance == -1) { w = x; y->avl_link[0] = x->avl_link[1]; x->avl_link[1] = y; x->avl_balance = y->avl_balance = 0; x->avl_parent = y->avl_parent; y->avl_parent = x; if (y->avl_link[0] != NULL) y->avl_link[0]->avl_parent = y; } else { assert(x->avl_balance == +1); w = x->avl_link[1]; x->avl_link[1] = w->avl_link[0]; w->avl_link[0] = x; y->avl_link[0] = w->avl_link[1]; w->avl_link[1] = y; if (w->avl_balance == -1) x->avl_balance = 0, y->avl_balance = +1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == +1| */ x->avl_balance = -1, y->avl_balance = 0; w->avl_balance = 0; w->avl_parent = y->avl_parent; x->avl_parent = y->avl_parent = w; if (x->avl_link[1] != NULL) x->avl_link[1]->avl_parent = x; if (y->avl_link[0] != NULL) y->avl_link[0]->avl_parent = y; } } else if (y->avl_balance == +2) { struct avl_node *x = y->avl_link[1]; if (x->avl_balance == +1) { w = x; y->avl_link[1] = x->avl_link[0]; x->avl_link[0] = y; x->avl_balance = y->avl_balance = 0; x->avl_parent = y->avl_parent; y->avl_parent = x; if (y->avl_link[1] != NULL) y->avl_link[1]->avl_parent = y; } else { assert(x->avl_balance == -1); w = x->avl_link[0]; x->avl_link[0] = w->avl_link[1]; w->avl_link[1] = x; y->avl_link[1] = w->avl_link[0]; w->avl_link[0] = y; if (w->avl_balance == +1) x->avl_balance = 0, y->avl_balance = -1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == -1| */ x->avl_balance = +1, y->avl_balance = 0; w->avl_balance = 0; w->avl_parent = y->avl_parent; x->avl_parent = y->avl_parent = w; if (x->avl_link[0] != NULL) x->avl_link[0]->avl_parent = x; if (y->avl_link[1] != NULL) y->avl_link[1]->avl_parent = y; } } else return &n->val; if (w->avl_parent != NULL) w->avl_parent->avl_link[y != w->avl_parent->avl_link[0]] = w; else tree->avl_root = w; return &n->val; } /* Inserts |item| into |table|. Returns |NULL| if |item| was successfully inserted or if a memory allocation error occurred. Otherwise, returns the duplicate item. */ void * map_put( map_t table, str key, void *item) { void **p = map_get(table, key); void * ret; if( p == NULL ) return p; ret = *p; *p = item; return ret == item ? NULL : ret; } void * delete_node(map_t tree, struct avl_node * p) { struct avl_node *q; /* Parent of |p|. */ int dir; /* Side of |q| on which |p| is linked. */ void * val; val = p->val; q = p->avl_parent; if (q == NULL) { q = (struct avl_node *) & tree->avl_root; dir = 0; } else { if( p == q->avl_link[0] ) dir = 0; else dir = 1; } if (p->avl_link[1] == NULL) { q->avl_link[dir] = p->avl_link[0]; if (q->avl_link[dir] != NULL) q->avl_link[dir]->avl_parent = p->avl_parent; } else { struct avl_node *r = p->avl_link[1]; if (r->avl_link[0] == NULL) { r->avl_link[0] = p->avl_link[0]; q->avl_link[dir] = r; r->avl_parent = p->avl_parent; if (r->avl_link[0] != NULL) r->avl_link[0]->avl_parent = r; r->avl_balance = p->avl_balance; q = r; dir = 1; } else { struct avl_node *s = r->avl_link[0]; while (s->avl_link[0] != NULL) s = s->avl_link[0]; r = s->avl_parent; r->avl_link[0] = s->avl_link[1]; s->avl_link[0] = p->avl_link[0]; s->avl_link[1] = p->avl_link[1]; q->avl_link[dir] = s; if (s->avl_link[0] != NULL) s->avl_link[0]->avl_parent = s; s->avl_link[1]->avl_parent = s; s->avl_parent = p->avl_parent; if (r->avl_link[0] != NULL) r->avl_link[0]->avl_parent = r; s->avl_balance = p->avl_balance; q = r; dir = 0; } } if(!( tree->flags & AVLMAP_NO_DUPLICATE ) ) avl_free(p->key.s,tree->flags); avl_free(p,tree->flags); while (q != (struct avl_node *) & tree->avl_root) { struct avl_node *y = q; if (y->avl_parent != NULL) q = y->avl_parent; else q = (struct avl_node *) & tree->avl_root; if (dir == 0) { dir = q->avl_link[0] != y; y->avl_balance++; if (y->avl_balance == +1) break; else if (y->avl_balance == +2) { struct avl_node *x = y->avl_link[1]; if (x->avl_balance == -1) { struct avl_node *w; w = x->avl_link[0]; x->avl_link[0] = w->avl_link[1]; w->avl_link[1] = x; y->avl_link[1] = w->avl_link[0]; w->avl_link[0] = y; if (w->avl_balance == +1) x->avl_balance = 0, y->avl_balance = -1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == -1| */ x->avl_balance = +1, y->avl_balance = 0; w->avl_balance = 0; w->avl_parent = y->avl_parent; x->avl_parent = y->avl_parent = w; if (x->avl_link[0] != NULL) x->avl_link[0]->avl_parent = x; if (y->avl_link[1] != NULL) y->avl_link[1]->avl_parent = y; q->avl_link[dir] = w; } else { y->avl_link[1] = x->avl_link[0]; x->avl_link[0] = y; x->avl_parent = y->avl_parent; y->avl_parent = x; if (y->avl_link[1] != NULL) y->avl_link[1]->avl_parent = y; q->avl_link[dir] = x; if (x->avl_balance == 0) { x->avl_balance = -1; y->avl_balance = +1; break; } else { x->avl_balance = y->avl_balance = 0; y = x; } } } } else { dir = q->avl_link[0] != y; y->avl_balance--; if (y->avl_balance == -1) break; else if (y->avl_balance == -2) { struct avl_node *x = y->avl_link[0]; if (x->avl_balance == +1) { struct avl_node *w; w = x->avl_link[1]; x->avl_link[1] = w->avl_link[0]; w->avl_link[0] = x; y->avl_link[0] = w->avl_link[1]; w->avl_link[1] = y; if (w->avl_balance == -1) x->avl_balance = 0, y->avl_balance = +1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == +1| */ x->avl_balance = -1, y->avl_balance = 0; w->avl_balance = 0; w->avl_parent = y->avl_parent; x->avl_parent = y->avl_parent = w; if (x->avl_link[1] != NULL) x->avl_link[1]->avl_parent = x; if (y->avl_link[0] != NULL) y->avl_link[0]->avl_parent = y; q->avl_link[dir] = w; } else { y->avl_link[0] = x->avl_link[1]; x->avl_link[1] = y; x->avl_parent = y->avl_parent; y->avl_parent = x; if (y->avl_link[0] != NULL) y->avl_link[0]->avl_parent = y; q->avl_link[dir] = x; if (x->avl_balance == 0) { x->avl_balance = +1; y->avl_balance = -1; break; } else { x->avl_balance = y->avl_balance = 0; y = x; } } } } } tree->avl_count--; return(void *) val; }; /* Deletes from |tree| and returns an item matching |item|. Returns a null pointer if no matching item found. */ void * map_remove( map_t tree, str key) { struct avl_node *p; /* Traverses tree to find node to delete. */ int dir; /* Side of |q| on which |p| is linked. */ if (tree->avl_root == NULL) return NULL; p = tree->avl_root; for (;;) { int cmp = str_cmp(key, p->key); if (cmp == 0) break; dir = cmp > 0; p = p->avl_link[dir]; if (p == NULL) return NULL; } return delete_node( tree, p ); } /* Frees storage allocated for |tree|. If |destroy != NULL|, applies it to each data item in inorder. */ void map_destroy( map_t tree, value_destroy_func destroy) { struct avl_node *p, *q; for (p = tree->avl_root; p != NULL; p = q) if (p->avl_link[0] == NULL) { q = p->avl_link[1]; if (destroy != NULL && p->val != NULL) destroy(p->val); if( !(tree->flags & AVLMAP_NO_DUPLICATE ) ) avl_free( p->key.s,tree->flags); avl_free( p, tree->flags ); } else { q = p->avl_link[0]; p->avl_link[0] = q->avl_link[1]; q->avl_link[1] = p; } avl_free( tree, tree->flags ); } int map_size( map_t tree ) { return tree->avl_count; } void process_all( map_t tree, struct avl_node *p, process_each_func f, void * param ); void process_all( map_t tree, struct avl_node *p, process_each_func f, void * param ) { if( tree->ret_code ) return; if( p->avl_link[0] ) process_all( tree, p->avl_link[0], f ,param ); tree->ret_code |= f( param, p->key, p->val); if( p->avl_link[1] ) process_all( tree, p->avl_link[1], f ,param ); } int map_for_each( map_t tree, process_each_func f, void * param) { tree->ret_code = 0; if( tree->avl_root ) process_all( tree, tree->avl_root, f, param); return tree->ret_code; } int map_first( map_t map, map_iterator_t * it) { if( map == NULL || it == NULL ) return -1; it->map = map; it->node = map->avl_root; if( it->node ) { while( it->node->avl_link[0] ) it->node = it->node->avl_link[0]; } return 0; } int map_last( map_t map, map_iterator_t * it) { if( map == NULL || it == NULL ) return -1; it->map = map; it->node = map->avl_root; if( it->node ) { while( it->node->avl_link[1] ) it->node = it->node->avl_link[1]; } return 0; } str * iterator_key( map_iterator_t * it ) { if( it == NULL ) return NULL; return &it->node->key; } void** iterator_val( map_iterator_t * it ) { if( it == NULL ) return NULL; return &it->node->val; } int iterator_is_valid( map_iterator_t * it ) { if( it == NULL || it->map ==NULL || it->node == NULL) return 0; return 1; } int iterator_next( map_iterator_t * it ) { struct avl_node *q, *p; /* Current node and its child. */ if( it == NULL || it->map ==NULL || it->node == NULL) return -1; if( it->node->avl_link[1] ) { it->node = it->node->avl_link[1]; while( it->node->avl_link[0] ) it->node = it->node->avl_link[0]; } else { for (p = it->node, q = p->avl_parent ; ; p = q, q = q->avl_parent) if (q == NULL || p == q->avl_link[0]) { it->node = q; return 0; } } return 0; } int iterator_prev( map_iterator_t * it ) { struct avl_node *q, *p; /* Current node and its child. */ if( it == NULL || it->map ==NULL || it->node == NULL) return -1; if( it->node->avl_link[0] ) { it->node = it->node->avl_link[0]; while( it->node->avl_link[1] ) it->node = it->node->avl_link[1]; } else { for (p = it->node, q = p->avl_parent ; ; p = q, q = q->avl_parent) if (q == NULL || p == q->avl_link[1]) { it->node = q; return 0; } } return 0; } void * iterator_delete( map_iterator_t * it ) { void * ret; if( it == NULL || it->map ==NULL || it->node == NULL) return NULL; ret = delete_node( it->map, it->node ); it->node = NULL; return ret; } opensips-2.2.2/map.h000066400000000000000000000135171300170765700143200ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-09-16 initial version (andreidragus) * */ #include "str.h" #include #ifndef AVL_H #define AVL_H /* Maximum AVL tree height. */ #ifndef AVL_MAX_HEIGHT #define AVL_MAX_HEIGHT 128 #endif /* Flags that can be passed on when creating a map */ enum { AVLMAP_SHARED = 1, /* determines if the map is to be allocated in shared or private memory */ AVLMAP_NO_DUPLICATE = 2 /* determines if the map will duplicate added keys*/ }; /* Tree data structure. */ typedef struct avl_table { int flags; /* Shared memory or private memory */ struct avl_node *avl_root; /* Tree's root. */ size_t avl_count; /* Number of items in tree. */ int ret_code; } *map_t; /* Iterator data structure. */ typedef struct avl_iterator { struct avl_node * node; /* Current node. */ map_t map; /* The map that this iterator points to*/ } map_iterator_t; /* An AVL tree node. */ struct avl_node { struct avl_node * avl_link[2]; /* Subtrees. */ struct avl_node * avl_parent; /* Parent node. */ str key; /* Key */ void * val; /* Value */ signed char avl_balance; /* Balance factor. */ }; /* * Function that will be called to destroy each * value in the map. * */ typedef void (* value_destroy_func)(void *); /* * Function that will be called for each value in the map * * Should return 0. A non-zero return code will cause the processing to stop * and the it will be returned by map_for_each(); * */ typedef int (* process_each_func )(void * param, str key, void * value); /* Map functions. */ /* * Allocates and initializes a map. * Flags can be set to determine the map behaviour. Several flags can be OR-ed * together. * * AVLMAP_SHARED -> flag for shared memory * AVLMAP_NO_DUPLICATE -> flag for key duplication * */ map_t map_create ( int flags ); /* * Destroys a map and frees all memory used. * A function should be given to free the values of each node. * If the function is NULL or a value is NULL it will not be called. * */ void map_destroy( map_t, value_destroy_func ); /* * Searches for a given key in the map. * A pointer to the location of the value is returned. * If the key is not found NULL is returned. * */ void ** map_find ( map_t, str ); /* * Searches for a given key in the map. * If the key is not found it is inserted. * A pointer to the location of the value is returned. * NULL is returned if a memory allocation error occurs. * */ void ** map_get ( map_t, str ); /* * Inserts a (key;value) pair. * If the key existed the value is returned so the user can free it. * Otherwise NULL is returned. * */ void* map_put ( map_t, str, void *); /* * Deletes a key from the map. * If the key is found the value is returned so the user can free it. * Otherwise NULL is returned. * */ void* map_remove ( map_t, str); /* * Returns the size of the map. * */ int map_size ( map_t ); /* * Function that calls f() for each key and value in the map * in ascending order of the keys. If f returns an error * the method will stop and return what was returned by f. * */ int map_for_each ( map_t map, process_each_func f, void * param ); /* * Function that initializes an iterator to the first element in the map. * If the map is empty it will initialize an invalid iterator. * * Returns 0 on success and -1 on error. * */ int map_first( map_t map, map_iterator_t * it); /* * Function that initializes an iterator to the last element in the map. * If the map is empty it will initialize an invalid iterator. * * Returns 0 on success and -1 on error. * */ int map_last( map_t map, map_iterator_t * it); /* * Returns a pointer to the location where the key is stored. * Users should copy the key if they want to modify it. * * Returns NULL on error. * */ str * iterator_key( map_iterator_t * it ); /* * Returns a pointer to the location where the value is stored. * Returns NULL on error. * */ void ** iterator_val( map_iterator_t * it ); /* * Advances the iterator to the next element in alphabetical * order in the map. * If the end is reached an invalid iterator is set. * * Returns 0 on success and -1 on error. * */ int iterator_next( map_iterator_t * it ); /* * Advances the iterator to the previous element in alphabetical * order in the map. * If the end is reached an invalid iterator is set. * * Returns 0 on success and -1 on error. * */ int iterator_prev( map_iterator_t * it ); /* * Checks if an iterator is valid. * Can be used to check if the end of the map has been reached. * Invalid iterators may be returned from all functions that initialize * iterators. * * Returns 1 for valid and 0 for invalid. * */ int iterator_is_valid( map_iterator_t * it ); /* * * Deletes the node pointed by the iterator. * * The iterator itself will become invalid, and all * iterators that pointed to the same node should no longer be used * as they point to inexisting nodes. * * Returns the value stored in the node so the user can free it. * Returns NULL if no deletion occurred. * * */ void* iterator_delete( map_iterator_t * it ); #endif opensips-2.2.2/md5.c000066400000000000000000000243331300170765700142210ustar00rootroot00000000000000/* $Id$ MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ /*! * \file * \brief MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */ #include #include "md5global.h" #include "md5.h" #define USE_MEM /* Constants for MD5Transform routine. */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 #define S21 5 #define S22 9 #define S23 14 #define S24 20 #define S31 4 #define S32 11 #define S33 16 #define S34 23 #define S41 6 #define S42 10 #define S43 15 #define S44 21 static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); static void Encode PROTO_LIST ((unsigned char *, UINT4 *, unsigned int)); static void Decode PROTO_LIST ((UINT4 *, unsigned char *, unsigned int)); static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); static unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* F, G, H and I are basic MD5 functions. */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits. */ #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is separate from addition to prevent recomputation. */ #define FF(a, b, c, d, x, s, ac) { \ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) { \ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) { \ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) { \ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } /* MD5 initialization. Begins an MD5 operation, writing a new context. MD5_CTX *context - context */ void MD5Init (MD5_CTX *context) { context->count[0] = context->count[1] = 0; /* Load magic initialization constants. */ context->state[0] = 0x67452301; context->state[1] = 0xefcdab89; context->state[2] = 0x98badcfe; context->state[3] = 0x10325476; } /* MD5 block update operation. Continues an MD5 message-digest operation, processing another message block, and updating the context. MD5_CTX *context - context unsigned char *input - input block unsigned int inputLen - length of input block */ void MD5Update (MD5_CTX *context, unsigned char *input, unsigned int inputLen) { unsigned int i, index, partLen; /* Compute number of bytes mod 64 */ index = (unsigned int)((context->count[0] >> 3) & 0x3F); /* Update number of bits */ if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3)) context->count[1]++; context->count[1] += ((UINT4)inputLen >> 29); partLen = 64 - index; /* Transform as many times as possible. */ if (inputLen >= partLen) { MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen); MD5Transform (context->state, context->buffer); for (i = partLen; i + 63 < inputLen; i += 64) MD5Transform (context->state, &input[i]); index = 0; } else i = 0; /* Buffer remaining input */ MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); } /* MD5 finalization. Ends an MD5 message-digest operation, writing the the message digest and zeroizing the context. unsigned char digest[16] - message digest MD5_CTX *context - context */ void MD5Final (unsigned char digest[16], MD5_CTX *context) { unsigned char bits[8]; unsigned int index, padLen; /* Save number of bits */ Encode (bits, context->count, 8); /* Pad out to 56 mod 64. */ index = (unsigned int)((context->count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); MD5Update (context, PADDING, padLen); /* Append length (before padding) */ MD5Update (context, bits, 8); /* Store state in digest */ Encode (digest, context->state, 16); /* Zeroize sensitive information. */ MD5_memset ((POINTER)context, 0, sizeof (*context)); } /* MD5 basic transformation. Transforms state based on block. */ static void MD5Transform (UINT4 state[4], unsigned char block[64]) { UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; Decode (x, block, 64); /* Round 1 */ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; /* Zeroize sensitive information. */ MD5_memset ((POINTER)x, 0, sizeof (x)); } /* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */ static void Encode (unsigned char *output, UINT4 *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = (unsigned char)(input[i] & 0xff); output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); } } /* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */ static void Decode (UINT4 *output, unsigned char *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); } /* Note: Replace "for loop" with standard memcpy if possible. */ static void MD5_memcpy (POINTER output, POINTER input, unsigned int len) { #ifndef USE_MEM unsigned int i; for (i = 0; i < len; i++) output[i] = input[i]; #else memcpy( output, input, len ); #endif } /* Note: Replace "for loop" with standard memset if possible. */ static void MD5_memset (POINTER output, int value, unsigned int len) { #ifndef USE_MEM unsigned int i; for (i = 0; i < len; i++) ((char *)output)[i] = (char)value; #else memset( output, value, len ); #endif } opensips-2.2.2/md5.h000066400000000000000000000027141300170765700142250ustar00rootroot00000000000000/* MD5.H - header file for MD5C.C */ /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ /*! * \file * \brief MD5.H - header file for MD5C.C */ #ifndef MD5_H #define MD5_H #include "md5global.h" /* MD5 context. */ typedef struct { UINT4 state[4]; /* state (ABCD) */ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } MD5_CTX; void MD5Init PROTO_LIST ((MD5_CTX *)); void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, unsigned int)); void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); #endif /* MD5_H */ opensips-2.2.2/md5global.h000066400000000000000000000015151300170765700154040ustar00rootroot00000000000000/* GLOBAL.H - RSAREF types and constants */ /* PROTOTYPES should be set to one if and only if the compiler supports function argument prototyping. The following makes PROTOTYPES default to 0 if it has not already been defined with C compiler flags. */ #ifndef MD5GLOBAL_H #define MD5GLOBAL_H #ifndef PROTOTYPES #define PROTOTYPES 0 #endif /* POINTER defines a generic pointer type */ typedef unsigned char *POINTER; /* UINT2 defines a two byte word */ typedef unsigned short int UINT2; /* UINT4 defines a four byte word */ typedef unsigned int UINT4; /* PROTO_LIST is defined depending on how PROTOTYPES is defined above. If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it returns an empty list. */ #if PROTOTYPES #define PROTO_LIST(list) list #else #define PROTO_LIST(list) () #endif #endif /* MD5GLOBAL_H */ opensips-2.2.2/md5utils.c000066400000000000000000000055151300170765700153030ustar00rootroot00000000000000/* * Copyright (C) 2007 1&1 Internet AG * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief OpenSIPS MD5 handling functions */ #include #include #include "md5global.h" #include "md5.h" #include "md5utils.h" #include "dprint.h" #include "ut.h" /*! \brief * Calculate a MD5 digests over a string array and stores * the result in the destination char array. * This function assumes 32 bytes in the destination buffer. * \param dest destination * \param src string input array * \param size elements in the input array */ void MD5StringArray(char *dest, str src[], unsigned int size) { MD5_CTX context; unsigned char digest[16]; int i, len; char *tmp; MD5Init (&context); for (i=0; i < size; i++) { trim_len(len, tmp, src[i]); MD5Update(&context, tmp, len); } MD5Final(digest, &context); string2hex(digest, 16, dest); LM_DBG("MD5 calculated: %.*s\n", MD5_LEN, dest); } /*! \brief * Calculate a MD5 digest over a file. * This function assumes 32 bytes in the destination buffer. * \param dest destination * \param file_name file for that the digest should be calculated * \return zero on success, negative on errors */ int MD5File(char *dest, const char *file_name) { if (!dest || !file_name) { LM_ERR("invalid parameter value\n"); return -1; } MD5_CTX context; FILE *input; unsigned char buffer[32768]; unsigned char hash[16]; unsigned int counter, size; struct stat stats; if (stat(file_name, &stats) != 0) { LM_ERR("could not stat file %s\n", file_name); return -1; } size = stats.st_size; MD5Init(&context); if((input = fopen(file_name, "rb")) == NULL) { LM_ERR("could not open file %s\n", file_name); return -1; } while(size) { counter = (size > sizeof(buffer)) ? sizeof(buffer) : size; if ((counter = fread(buffer, 1, counter, input)) <= 0) { fclose(input); return -1; } MD5Update(&context, buffer, counter); size -= counter; } fclose(input); MD5Final(hash, &context); string2hex(hash, 16, dest); LM_DBG("MD5 calculated: %.*s for file %s\n", MD5_LEN, dest, file_name); return 0; } opensips-2.2.2/md5utils.h000066400000000000000000000020471300170765700153050ustar00rootroot00000000000000/* * Copyright (C) 2007 1&1 Internet AG * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MD5UTILS_H #define _MD5UTILS_H #include "str.h" #define MD5_LEN 32 void MD5StringArray (char *dst, str src[], unsigned int size); int MD5File(char *dest, const char *name); #endif /* _MD5UTILS_H */ opensips-2.2.2/mem/000077500000000000000000000000001300170765700141415ustar00rootroot00000000000000opensips-2.2.2/mem/common.h000066400000000000000000000034031300170765700156020ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- */ #ifndef mem_common_h #define mem_common_h #define oom_errorf \ "not enough free %s memory (%lu bytes left), please increase the \"-%s\" " \ "command line parameter!\n" #define oom_nostats_errorf \ "not enough free %s memory, please increase the \"-%s\" " \ "command line parameter!\n" # ifdef VQ_MALLOC # include "vq_malloc.h" extern struct vqm_block* mem_block; extern struct vqm_block* shm_block; # elif defined F_MALLOC # include "f_malloc.h" extern struct fm_block* mem_block; extern struct fm_block* shm_block; # elif defined HP_MALLOC # include "hp_malloc.h" extern struct hp_block* mem_block; extern struct hp_block* shm_block; # elif defined QM_MALLOC # include "q_malloc.h" extern struct qm_block* mem_block; extern struct qm_block* shm_block; # else # error "no memory allocator selected" # endif extern int mem_warming_enabled; extern char *mem_warming_pattern_file; extern int mem_warming_percentage; #endif opensips-2.2.2/mem/f_malloc.c000066400000000000000000000427661300170765700161000ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * created by andrei * 2003-07-06 added fm_realloc (andrei) * 2004-07-19 fragments book keeping code and support for 64 bits * memory blocks (64 bits machine & size >=2^32) * GET_HASH s/ 4Gb mem., switched to long (andrei) * 2005-03-02 added fm_info() (andrei) * 2005-12-12 fixed realloc shrink real_used accounting (andrei) * fixed initial size (andrei) */ #if !(defined VQ_MALLOC) && !defined(QM_MALLOC) && !(defined HP_MALLOC) && \ (defined F_MALLOC) #include #include #include #include "f_malloc.h" #include "common.h" #include "../dprint.h" #include "../globals.h" #include "../statistics.h" #ifdef DBG_MALLOC #include "mem_dbg_hash.h" #endif /*useful macros*/ #define max(a,b) ( (a)>(b)?(a):(b)) #define FRAG_NEXT(f) \ ((struct fm_frag*)((char*)(f)+sizeof(struct fm_frag)+(f)->size )) /* ROUNDTO= 2^k so the following works */ #define ROUNDTO_MASK (~((unsigned long)ROUNDTO-1)) #define ROUNDUP(s) (((s)+(ROUNDTO-1))&ROUNDTO_MASK) #define ROUNDDOWN(s) ((s)&ROUNDTO_MASK) /* #define ROUNDUP(s) (((s)%ROUNDTO)?((s)+ROUNDTO)/ROUNDTO*ROUNDTO:(s)) #define ROUNDDOWN(s) (((s)%ROUNDTO)?((s)-ROUNDTO)/ROUNDTO*ROUNDTO:(s)) */ /* finds the hash value for s, s=ROUNDTO multiple*/ #define GET_HASH(s) ( ((unsigned long)(s)<=F_MALLOC_OPTIMIZE)?\ (unsigned long)(s)/ROUNDTO: \ F_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \ F_MALLOC_OPTIMIZE_FACTOR+1 ) #define UN_HASH(h) ( ((unsigned long)(h)<=(F_MALLOC_OPTIMIZE/ROUNDTO))?\ (unsigned long)(h)*ROUNDTO: \ 1UL<<((unsigned long)(h)-F_MALLOC_OPTIMIZE/ROUNDTO+\ F_MALLOC_OPTIMIZE_FACTOR-1)\ ) #define MEM_FRAG_AVOIDANCE #define F_MALLOC_LARGE_LIMIT F_MALLOC_OPTIMIZE #define F_MALLOC_DEFRAG_LIMIT (F_MALLOC_LARGE_LIMIT * 5) #define F_MALLOC_DEFRAG_PERCENT 5 unsigned long frag_size(void* p){ if(!p) return 0; return (((struct fm_frag*) ((char*)p-sizeof(struct fm_frag)))->size); } static inline void free_minus(struct fm_block* qm, unsigned long size ) { if( size > F_MALLOC_LARGE_LIMIT ) qm->large_space -= size; #if defined(DBG_MALLOC) || defined(STATISTICS) qm->real_used+=size; qm->used+=size; #endif } static inline void free_plus(struct fm_block* qm, unsigned long size ) { if( size > F_MALLOC_LARGE_LIMIT ) qm->large_space += size; #if defined(DBG_MALLOC) || defined(STATISTICS) qm->real_used-=size; qm->used-=size; #endif } /* computes hash number for big buckets*/ inline static unsigned long big_hash_idx(unsigned long s) { unsigned long idx; /* s is rounded => s = k*2^n (ROUNDTO=2^n) * index= i such that 2^i > s >= 2^(i-1) * * => index = number of the first non null bit in s*/ idx=sizeof(long)*8-1; for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--); return idx; } #ifdef DBG_MALLOC #define ST_CHECK_PATTERN 0xf0f0f0f0 #define END_CHECK_PATTERN1 0xc0c0c0c0 #define END_CHECK_PATTERN2 0xabcdefed #endif static inline void fm_insert_free(struct fm_block* qm, struct fm_frag* frag) { struct fm_frag** f; int hash; hash=GET_HASH(frag->size); f=&(qm->free_hash[hash].first); if (frag->size > F_MALLOC_OPTIMIZE){ /* because of '<=' in GET_HASH, (different from 0.8.1[24] on purpose --andrei ) */ for(; *f; f=&((*f)->u.nxt_free)){ if (frag->size <= (*f)->size) break; } } /*insert it here*/ frag->prev = f; frag->u.nxt_free=*f; if( *f ) (*f)->prev = &(frag->u.nxt_free); #ifdef DBG_MALLOC /* mark fragment as "free" */ frag->is_free = 1; #endif *f=frag; qm->free_hash[hash].no++; free_plus(qm, frag->size); } static inline void fm_remove_free(struct fm_block* qm, struct fm_frag* n) { struct fm_frag** pf; int hash; pf = n->prev; hash = GET_HASH( n->size ); /* detach */ *pf=n->u.nxt_free; if( n->u.nxt_free ) n->u.nxt_free->prev = pf; qm->free_hash[hash].no--; n->prev = NULL; free_minus(qm , n->size); }; /* size should be already rounded-up */ static inline #ifdef DBG_MALLOC void fm_split_frag(struct fm_block* qm, struct fm_frag* frag, unsigned long size, const char* file, const char* func, unsigned int line) #else void fm_split_frag(struct fm_block* qm, struct fm_frag* frag, unsigned long size) #endif { unsigned long rest; struct fm_frag* n; rest=frag->size-size; #ifdef MEM_FRAG_AVOIDANCE if ((rest> (FRAG_OVERHEAD+F_MALLOC_OPTIMIZE))|| (rest>=(FRAG_OVERHEAD+size))){ /* the residue fragm. is big enough*/ #else if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){ #endif frag->size=size; /*split the fragment*/ n=FRAG_NEXT(frag); n->size=rest-FRAG_OVERHEAD; /* * The real used memory does not increase, as the frag memory is not * freed from real_used. On the other hand, the used size should * decrease, because the new fragment is not "useful data" - razvanc #if defined(DBG_MALLOC) || defined(STATISTICS) qm->real_used+=FRAG_OVERHEAD; #endif */ #if defined(DBG_MALLOC) || defined(STATISTICS) qm->used-=FRAG_OVERHEAD; #endif #ifdef DBG_MALLOC /* frag created by malloc, mark it*/ n->file=file; n->func="frag. from fm_malloc"; n->line=line; n->check=ST_CHECK_PATTERN; #endif /* reinsert n in free list*/ fm_insert_free(qm, n); }else{ /* we cannot split this fragment any more => alloc all of it*/ } } /* init malloc and return a fm_block*/ struct fm_block* fm_malloc_init(char* address, unsigned long size, char *name) { char* start; char* end; struct fm_block* qm; unsigned long init_overhead; /* make address and size multiple of 8*/ start=(char*)ROUNDUP((unsigned long) address); LM_DBG("F_OPTIMIZE=%lu, /ROUNDTO=%lu\n", F_MALLOC_OPTIMIZE, F_MALLOC_OPTIMIZE/ROUNDTO); LM_DBG("F_HASH_SIZE=%lu, fm_block size=%lu\n", F_HASH_SIZE, (long)sizeof(struct fm_block)); LM_DBG("params (%p, %lu), start=%p\n", address, size, start); if (size<(unsigned long)(start-address)) return 0; size-=(start-address); if (size <(MIN_FRAG_SIZE+FRAG_OVERHEAD)) return 0; size=ROUNDDOWN(size); init_overhead=(ROUNDUP(sizeof(struct fm_block))+ 2 * FRAG_OVERHEAD); if (size < init_overhead) { /* not enough mem to create our control structures !!!*/ return 0; } end=start+size; qm=(struct fm_block*)start; memset(qm, 0, sizeof(struct fm_block)); qm->name = name; qm->size=size; #if defined(DBG_MALLOC) || defined(STATISTICS) qm->used=size-init_overhead; qm->real_used=size; qm->max_real_used=init_overhead; qm->fragments = 0; #endif qm->first_frag=(struct fm_frag*)(start+ROUNDUP(sizeof(struct fm_block))); qm->last_frag=(struct fm_frag*)(end-sizeof(struct fm_frag)); /* init initial fragment*/ qm->first_frag->size=size-init_overhead; qm->last_frag->size=0; qm->last_frag->prev=NULL; qm->first_frag->prev=NULL; #ifdef DBG_MALLOC qm->first_frag->check=ST_CHECK_PATTERN; qm->last_frag->check=END_CHECK_PATTERN1; #endif /* link initial fragment into the free list*/ qm->large_space = 0; qm->large_limit = qm->size / 100 * F_MALLOC_DEFRAG_PERCENT; if( qm->large_limit < F_MALLOC_DEFRAG_LIMIT ) qm->large_limit = F_MALLOC_DEFRAG_LIMIT; fm_insert_free(qm, qm->first_frag); return qm; } #ifdef DBG_MALLOC void* fm_malloc(struct fm_block* qm, unsigned long size, const char* file, const char* func, unsigned int line) #else void* fm_malloc(struct fm_block* qm, unsigned long size) #endif { struct fm_frag* frag,*n; unsigned int hash; #ifdef DBG_MALLOC LM_GEN1(memlog, "%s_malloc(%lu), called from %s: %s(%d)\n", qm->name, size, file, func, line); #endif /*size must be a multiple of 8*/ size=ROUNDUP(size); /*search for a suitable free frag*/ for(hash=GET_HASH(size);hashfree_hash[hash].first; for( ; frag; frag = frag->u.nxt_free ) if ( frag->size >= size ) goto found; /* try in a bigger bucket */ } /* not found, bad! */ #if defined(DBG_MALLOC) || defined(STATISTICS) LM_ERR(oom_errorf, qm->name, qm->size - qm->real_used, qm->name[0] == 'p' ? "M" : "m"); LM_INFO("attempting defragmentation... (need %lu bytes)\n", size); #else LM_ERR(oom_nostats_errorf, qm->name, qm->name[0] == 'p' ? "M" : "m"); LM_INFO("attempting defragmentation... (need %lu bytes)\n", size); #endif for( frag = qm->first_frag; (char*)frag < (char*)qm->last_frag; ) { n = FRAG_NEXT(frag); if ( ((char*)n < (char*)qm->last_frag) && n->prev && frag->prev ) { /* detach frag*/ fm_remove_free(qm, frag); do { fm_remove_free(qm, n); frag->size += n->size + FRAG_OVERHEAD; #if defined(DBG_MALLOC) || defined(STATISTICS) //qm->real_used -= FRAG_OVERHEAD; qm->used += FRAG_OVERHEAD; #endif if( frag->size >size ) { #ifdef DBG_MALLOC /* mark it as "busy" */ frag->is_free = 0; #endif goto solved; } n = FRAG_NEXT(frag); } while ( ((char*)n < (char*)qm->last_frag) && n->prev); fm_insert_free(qm,frag); } frag = n; } LM_INFO("unable to alloc a big enough fragment!\n"); pkg_threshold_check(); return 0; found: /* we found it!*/ fm_remove_free(qm,frag); #ifdef DBG_MALLOC /* mark it as "busy" */ frag->is_free = 0; #endif /*see if we'll use full frag, or we'll split it in 2*/ #ifdef DBG_MALLOC fm_split_frag(qm, frag, size, file, func, line); frag->file=file; frag->func=func; frag->line=line; frag->check=ST_CHECK_PATTERN; LM_GEN1(memlog, "%s_malloc(%lu), returns address %p\n", qm->name, size, (char*)frag+sizeof(struct fm_frag)); #else fm_split_frag(qm, frag, size); #endif solved: #if defined(DBG_MALLOC) || defined(STATISTICS) if (qm->max_real_usedreal_used) qm->max_real_used=qm->real_used; qm->fragments += 1; #endif pkg_threshold_check(); return (char*)frag+sizeof(struct fm_frag); } #ifdef DBG_MALLOC void fm_free(struct fm_block* qm, void* p, const char* file, const char* func, unsigned int line) #else void fm_free(struct fm_block* qm, void* p) #endif { struct fm_frag* f,*n; #ifdef DBG_MALLOC LM_GEN1(memlog, "%s_free(%p), called from %s: %s(%d)\n", qm->name, p, file, func, line); if (p && (p > (void *)qm->last_frag || p < (void *)qm->first_frag)) { LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p); abort(); } #endif if (p==0) { LM_GEN1(memlog, "free(0) called\n"); return; } f=(struct fm_frag*) ((char*)p-sizeof(struct fm_frag)); #ifdef DBG_MALLOC LM_GEN1(memlog, "freeing block alloc'ed from %s: %s(%ld)\n", f->file, f->func, f->line); #endif join: if( qm->large_limit < qm->large_space ) goto no_join; n = FRAG_NEXT(f); if (((char*)n < (char*)qm->last_frag) && n->prev ) { fm_remove_free(qm, n); /* join */ f->size += n->size + FRAG_OVERHEAD; #if defined(DBG_MALLOC) || defined(STATISTICS) //qm->real_used -= FRAG_OVERHEAD; qm->used += FRAG_OVERHEAD; #endif goto join; } no_join: fm_insert_free(qm, f); #if defined(DBG_MALLOC) || defined(STATISTICS) qm->fragments -= 1; #endif pkg_threshold_check(); } #ifdef DBG_MALLOC void* fm_realloc(struct fm_block* qm, void* p, unsigned long size, const char* file, const char* func, unsigned int line) #else void* fm_realloc(struct fm_block* qm, void* p, unsigned long size) #endif { struct fm_frag *f; unsigned long diff; unsigned long orig_size; struct fm_frag *n; void *ptr; #ifdef DBG_MALLOC LM_GEN1(memlog, "%s_realloc(%p, %lu->%lu), called from %s: %s(%d)\n", qm->name, p, p ? ((struct fm_frag*)((char *)p - sizeof(struct fm_frag)))->size:0, size, file, func, line); if ((p)&&(p>(void*)qm->last_frag || p<(void*)qm->first_frag)){ LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p); abort(); } #endif if (size==0) { if (p) #ifdef DBG_MALLOC fm_free(qm, p, file, func, line); #else fm_free(qm, p); #endif pkg_threshold_check(); return 0; } if (p==0) #ifdef DBG_MALLOC return fm_malloc(qm, size, file, func, line); #else return fm_malloc(qm, size); #endif f=(struct fm_frag*) ((char*)p-sizeof(struct fm_frag)); #ifdef DBG_MALLOC LM_GEN1(memlog, "realloc'ing frag %p alloc'ed from %s: %s(%ld)\n", f, f->file, f->func, f->line); #endif size=ROUNDUP(size); orig_size=f->size; if (f->size > size){ /* shrink */ #ifdef DBG_MALLOC LM_GEN1(memlog, "shrinking from %lu to %lu\n", f->size, size); fm_split_frag(qm, f, size, file, "frag. from fm_realloc", line); #else fm_split_frag(qm, f, size); #endif }else if (f->sizesize, size); #endif diff=size-f->size; n=FRAG_NEXT(f); if (((char*)n < (char*)qm->last_frag) && n->prev && ((n->size+FRAG_OVERHEAD)>=diff)){ fm_remove_free(qm,n); /* join */ f->size += n->size + FRAG_OVERHEAD; #if defined(DBG_MALLOC) || defined(STATISTICS) //qm->real_used -= FRAG_OVERHEAD; qm->used += FRAG_OVERHEAD; #endif /* split it if necessary */ if (f->size > size){ #ifdef DBG_MALLOC fm_split_frag(qm, f, size, file, "fragm. from fm_realloc", line); #else fm_split_frag(qm, f, size); #endif } }else{ /* could not join => realloc */ #ifdef DBG_MALLOC ptr=fm_malloc(qm, size, file, func, line); #else ptr = fm_malloc(qm, size); #endif if (ptr) { /* copy, need by libssl */ memcpy(ptr, p, orig_size); #ifdef DBG_MALLOC fm_free(qm, p, file, func, line); #else fm_free(qm, p); #endif } p = ptr; } }else{ /* do nothing */ #ifdef DBG_MALLOC LM_GEN1(memlog, "doing nothing, same size: %lu - %lu\n", f->size, size); #endif } #ifdef DBG_MALLOC LM_GEN1(memlog, "returning %p\n", p); #endif #if defined(DBG_MALLOC) || defined(STATISTICS) if (qm->max_real_usedreal_used) qm->max_real_used=qm->real_used; #endif pkg_threshold_check(); return p; } void fm_status(struct fm_block* qm) { struct fm_frag* f; unsigned int i,j; unsigned int h; int unused; unsigned long size; #ifdef DBG_MALLOC mem_dbg_htable_t allocd; struct mem_dbg_entry *it; #endif LM_GEN1(memdump, "fm_status (%p):\n", qm); if (!qm) return; LM_GEN1(memdump, " heap size= %ld\n", qm->size); #if defined(DBG_MALLOC) || defined(STATISTICS) LM_GEN1(memdump, " used= %lu, used+overhead=%lu, free=%lu\n", qm->used, qm->real_used, qm->size-qm->used); LM_GEN1(memdump, " max used (+overhead)= %lu\n", qm->max_real_used); #endif #if defined(DBG_MALLOC) dbg_ht_init(allocd); for (f=qm->first_frag; (char*)f<(char*)qm->last_frag; f=FRAG_NEXT(f)) if (!f->is_free) if (dbg_ht_update(allocd, f->file, f->func, f->line, f->size) < 0) { LM_ERR("Unable to update alloc'ed. memory summary\n"); return; } LM_GEN1(memdump, " dumping summary of all alloc'ed. fragments:\n"); for(i=0; i < DBG_HASH_SIZE; i++) { it = allocd[i]; while (it) { LM_GEN1(memdump, " %10lu : %lu x [%s: %s, line %lu]\n", it->size, it->no_fragments, it->file, it->func, it->line); it = it->next; } } dbg_ht_free(allocd); #endif LM_GEN1(memdump, "dumping free list:\n"); for(h=0,i=0,size=0;hfree_hash[h].first,j=0; f; size+=f->size,f=f->u.nxt_free,i++,j++){ } if (j) LM_GEN1(memdump,"hash = %3d fragments no.: %5d, unused: %5d\n\t\t" " bucket size: %9lu - %9lu (first %9lu)\n", h, j, unused, UN_HASH(h), ((h<=F_MALLOC_OPTIMIZE/ROUNDTO)?1:2)* UN_HASH(h), qm->free_hash[h].first->size ); if (j!=qm->free_hash[h].no){ LM_CRIT("different free frag. count: %d!=%ld" " for hash %3d\n", j, qm->free_hash[h].no, h); } } LM_GEN1(memdump, "TOTAL: %6d free fragments = %6lu free bytes\n", i, size); LM_GEN1(memdump, "TOTAL: %ld large bytes\n", qm->large_space ); LM_GEN1(memdump, "TOTAL: %u overhead\n", (unsigned int)FRAG_OVERHEAD ); LM_GEN1(memdump, "-----------------------------\n"); } /* fills a malloc info structure with info about the block * if a parameter is not supported, it will be filled with 0 */ void fm_info(struct fm_block* qm, struct mem_info* info) { unsigned int r; long total_frags; #if !defined(DBG_MALLOC) && !defined(STATISTICS) struct fm_frag* f; #endif memset(info,0, sizeof(*info)); total_frags=0; info->total_size=qm->size; info->min_frag=MIN_FRAG_SIZE; #if defined(DBG_MALLOC) || defined(STATISTICS) info->free=qm->size-qm->real_used; info->used=qm->used; info->real_used=qm->real_used; info->max_used=qm->max_real_used; for(r=0;rfree_hash[r].no; } #else /* we'll have to compute it all */ for (r=0; r<=F_MALLOC_OPTIMIZE/ROUNDTO; r++){ info->free+=qm->free_hash[r].no*UN_HASH(r); total_frags+=qm->free_hash[r].no; } for(;rfree_hash[r].no; for(f=qm->free_hash[r].first;f;f=f->u.nxt_free){ info->free+=f->size; } } info->real_used=info->total_size-info->free; info->used=0; /* we don't really now */ info->max_used=0; /* we don't really now */ #endif info->total_frags=total_frags; } #endif opensips-2.2.2/mem/f_malloc.h000066400000000000000000000110621300170765700160660ustar00rootroot00000000000000/* * simple, very fast, malloc library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-05-21 on sparc64 roundto 8 even in debugging mode (so malloc'ed * long longs will be 64 bit aligned) (andrei) * 2004-07-19 support for 64 bit (2^64 mem. block) and more info * for the future de-fragmentation support (andrei) * 2004-11-10 support for > 4Gb mem., switched to long (andrei) */ #if !defined(f_malloc_h) && !defined(VQ_MALLOC) && !defined(QM_MALLOC) && \ !defined(HP_MALLOC) #define f_malloc_h #include "meminfo.h" /* defs*/ #ifdef DBG_MALLOC #if defined(__CPU_sparc64) || defined(__CPU_sparc) /* tricky, on sun in 32 bits mode long long must be 64 bits aligned * but long can be 32 bits aligned => malloc should return long long * aligned memory */ #define ROUNDTO sizeof(long long) #else #define ROUNDTO sizeof(void*) /* size we round to, must be = 2^n, and sizeof(fm_frag) must be multiple of ROUNDTO !*/ #endif #else /* DBG_MALLOC */ #define ROUNDTO 8UL #endif #define MIN_FRAG_SIZE ROUNDTO #define F_MALLOC_OPTIMIZE_FACTOR 14UL /*used below */ #define F_MALLOC_OPTIMIZE (1UL<size; } static inline unsigned long fm_get_used(struct fm_block* qm) { return qm->used; } static inline unsigned long fm_get_free(struct fm_block* qm) { return qm->size-qm->real_used; } static inline unsigned long fm_get_real_used(struct fm_block* qm) { return qm->real_used; } static inline unsigned long fm_get_max_real_used(struct fm_block* qm) { return qm->max_real_used; } static inline unsigned long fm_get_frags(struct fm_block* qm) { return qm->fragments; } #endif /*STATISTICS*/ #endif opensips-2.2.2/mem/hp_malloc.c000066400000000000000000001100311300170765700162370ustar00rootroot00000000000000/** * the truly parallel memory allocator * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-01-19 initial version (liviu) */ #if !defined(q_malloc) && !(defined VQ_MALLOC) && !(defined F_MALLOC) && \ (defined HP_MALLOC) #include #include #include #include "sys/time.h" #include "../dprint.h" #include "../globals.h" #include "../statistics.h" #include "../locking.h" #include "hp_malloc.h" #include "common.h" #ifdef DBG_MALLOC #include "mem_dbg_hash.h" #endif extern unsigned long *mem_hash_usage; /* * adaptive image of OpenSIPS's memory usage during runtime * used to fragment the shared memory pool at daemon startup */ char *mem_warming_pattern_file; int mem_warming_enabled; /* * percentage of shared memory which will be fragmented at startup * common values are between [0, 75] */ int mem_warming_percentage = -1; #if defined(HP_MALLOC) && !defined(HP_MALLOC_FAST_STATS) stat_var *shm_used; stat_var *shm_rused; stat_var *shm_frags; #endif /* ROUNDTO= 2^k so the following works */ #define ROUNDTO_MASK (~((unsigned long)ROUNDTO-1)) #define ROUNDUP(s) (((s)+(ROUNDTO-1))&ROUNDTO_MASK) #define ROUNDDOWN(s) ((s)&ROUNDTO_MASK) #define SHM_LOCK(i) lock_get(&mem_lock[i]) #define SHM_UNLOCK(i) lock_release(&mem_lock[i]) #define MEM_FRAG_AVOIDANCE #define HP_MALLOC_LARGE_LIMIT HP_MALLOC_OPTIMIZE #define HP_MALLOC_DEFRAG_LIMIT (HP_MALLOC_LARGE_LIMIT * 5) #define HP_MALLOC_DEFRAG_PERCENT 5 #define can_split_frag(frag, wanted_size) \ ((frag)->size - wanted_size > (FRAG_OVERHEAD + MIN_FRAG_SIZE)) /* * the *_split functions try to split a fragment in an attempt * to minimize memory usage */ #ifdef DBG_MALLOC #define pkg_frag_split(blk, frag, sz, fl, fnc, ln) \ do { \ if (can_split_frag(frag, sz)) { \ __pkg_frag_split(blk, frag, sz, fl, fnc, ln); \ update_stats_pkg_frag_split(blk); \ } \ } while (0) #else #define pkg_frag_split(blk, frag, sz) \ do { \ if (can_split_frag(frag, sz)) { \ __pkg_frag_split(blk, frag, sz); \ update_stats_pkg_frag_split(blk); \ } \ } while (0) #endif #ifdef DBG_MALLOC #define shm_frag_split_unsafe(blk, frag, sz, fl, fnc, ln) \ do { \ if (can_split_frag(frag, sz)) { \ __shm_frag_split_unsafe(blk, frag, sz, fl, fnc, ln); \ if (stats_are_ready()) { \ update_stats_shm_frag_split(); \ } else { \ (blk)->used -= FRAG_OVERHEAD; \ (blk)->real_used += FRAG_OVERHEAD; \ (blk)->total_fragments++; \ } \ } \ } while (0) #else #define shm_frag_split_unsafe(blk, frag, sz) \ do { \ if (can_split_frag(frag, sz)) { \ __shm_frag_split_unsafe(blk, frag, sz); \ if (stats_are_ready()) { \ update_stats_shm_frag_split(); \ } else { \ (blk)->used -= FRAG_OVERHEAD; \ (blk)->real_used += FRAG_OVERHEAD; \ (blk)->total_fragments++; \ } \ } \ } while (0) #endif /* Note: the shm lock on "hash" must be acquired when this is called */ #ifdef DBG_MALLOC #define shm_frag_split(blk, frag, sz, hash, fl, fnc, ln) \ do { \ if (can_split_frag(frag, sz)) { \ __shm_frag_split(blk, frag, sz, hash, fl, fnc, ln); \ update_stats_shm_frag_split(); \ } \ } while (0) #else #define shm_frag_split(blk, frag, sz, hash) \ do { \ if (can_split_frag(frag, sz)) { \ __shm_frag_split(blk, frag, sz, hash); \ update_stats_shm_frag_split(); \ } \ } while (0) #endif /* computes hash number for big buckets */ inline static unsigned long big_hash_idx(unsigned long s) { unsigned long idx; /* s is rounded => s = k*2^n (ROUNDTO=2^n) * index= i such that 2^i > s >= 2^(i-1) * * => index = number of the first non null bit in s*/ idx = 8 * sizeof(long) - 1; for (; !(s & (1UL << (8 * sizeof(long) - 1))); s <<= 1, idx--) ; return idx; } unsigned long frag_size(void* p){ if(!p) return 0; return ((struct hp_frag*) ((char*)p - sizeof(struct hp_frag)))->size; } static inline void hp_frag_attach(struct hp_block *hpb, struct hp_frag *frag) { struct hp_frag **f; unsigned int hash; hash = GET_HASH_RR(hpb, frag->size); f = &(hpb->free_hash[hash].first); if (frag->size > HP_MALLOC_OPTIMIZE){ /* because of '<=' in GET_HASH, purpose --andrei ) */ for(; *f; f=&((*f)->u.nxt_free)){ if (frag->size <= (*f)->size) break; } } /*insert it here*/ frag->prev = f; frag->u.nxt_free=*f; if (*f) (*f)->prev = &(frag->u.nxt_free); /* mark fragment as "free" */ #ifdef DBG_MALLOC frag->is_free = 1; #endif *f = frag; #ifdef HP_MALLOC_FAST_STATS hpb->free_hash[hash].no++; #endif } static inline void hp_frag_detach(struct hp_block *hpb, struct hp_frag *frag) { struct hp_frag **pf; pf = frag->prev; /* detach */ *pf = frag->u.nxt_free; if (frag->u.nxt_free) frag->u.nxt_free->prev = pf; frag->prev = NULL; #ifdef HP_MALLOC_FAST_STATS unsigned int hash; hash = GET_HASH_RR(hpb, frag->size); hpb->free_hash[hash].no--; #endif }; #ifdef DBG_MALLOC void __pkg_frag_split(struct hp_block *hpb, struct hp_frag *frag, unsigned long size, const char* file, const char* func, unsigned int line) #else void __pkg_frag_split(struct hp_block *hpb, struct hp_frag *frag, unsigned long size) #endif { unsigned long rest; struct hp_frag *n; rest = frag->size - size; frag->size = size; /* split the fragment */ n = FRAG_NEXT(frag); n->size = rest - FRAG_OVERHEAD; #ifdef DBG_MALLOC /* frag created by malloc or realloc, mark it*/ n->file=file; n->func=func; n->line=line; #ifndef STATISTICS hpb->used -= FRAG_OVERHEAD; hpb->real_used += FRAG_OVERHEAD; hpb->total_fragments++; #endif #endif hp_frag_attach(hpb, n); update_stats_pkg_frag_attach(hpb, n); #if defined(DBG_MALLOC) && !defined(STATISTICS) hpb->used -= n->size; hpb->real_used -= n->size + FRAG_OVERHEAD; #endif } #ifdef DBG_MALLOC void __shm_frag_split_unsafe(struct hp_block *hpb, struct hp_frag *frag, unsigned long size, const char* file, const char* func, unsigned int line) #else void __shm_frag_split_unsafe(struct hp_block *hpb, struct hp_frag *frag, unsigned long size) #endif { unsigned long rest; struct hp_frag *n; #ifdef HP_MALLOC_FAST_STATS hpb->free_hash[PEEK_HASH_RR(hpb, frag->size)].total_no--; hpb->free_hash[PEEK_HASH_RR(hpb, size)].total_no++; #endif rest = frag->size - size; frag->size = size; /* split the fragment */ n = FRAG_NEXT(frag); n->size = rest - FRAG_OVERHEAD; #ifdef DBG_MALLOC /* frag created by malloc, mark it*/ n->file=file; n->func="frag. from hp_malloc"; n->line=line; #endif #if defined(DBG_MALLOC) || defined(STATISTICS) if (stats_are_ready()) { hpb->used -= FRAG_OVERHEAD; hpb->real_used += FRAG_OVERHEAD; hpb->total_fragments++; } #endif #ifdef HP_MALLOC_FAST_STATS hpb->free_hash[PEEK_HASH_RR(hpb, n->size)].total_no++; #endif hp_frag_attach(hpb, n); if (stats_are_ready()) { update_stats_shm_frag_attach(n); #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->used -= n->size; hpb->real_used -= n->size + FRAG_OVERHEAD; #endif } else { hpb->used -= n->size; hpb->real_used -= n->size + FRAG_OVERHEAD; } } /* size should already be rounded-up */ #ifdef DBG_MALLOC void __shm_frag_split(struct hp_block *hpb, struct hp_frag *frag, unsigned long size, unsigned int old_hash, const char* file, const char* func, unsigned int line) #else void __shm_frag_split(struct hp_block *hpb, struct hp_frag *frag, unsigned long size, unsigned int old_hash) #endif { unsigned long rest, hash; struct hp_frag *n; #ifdef HP_MALLOC_FAST_STATS hpb->free_hash[PEEK_HASH_RR(hpb, frag->size)].total_no--; hpb->free_hash[PEEK_HASH_RR(hpb, size)].total_no++; #endif rest = frag->size - size; frag->size = size; /* split the fragment */ n = FRAG_NEXT(frag); n->size = rest - FRAG_OVERHEAD; #ifdef DBG_MALLOC /* frag created by malloc, mark it*/ n->file=file; n->func="frag. from hp_malloc"; n->line=line; #endif #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->used -= FRAG_OVERHEAD; hpb->real_used += FRAG_OVERHEAD; hpb->total_fragments++; #endif /* insert the newly obtained hp_frag in its free list */ hash = PEEK_HASH_RR(hpb, n->size); if (hash != old_hash) SHM_LOCK(hash); hp_frag_attach(hpb, n); update_stats_shm_frag_attach(n); #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->used -= n->size; hpb->real_used -= n->size + FRAG_OVERHEAD; #endif #ifdef HP_MALLOC_FAST_STATS hpb->free_hash[hash].total_no++; #endif if (hash != old_hash) SHM_UNLOCK(hash); } /** * dumps the current memory allocation pattern of OpenSIPS into a pattern file */ void hp_update_mem_pattern_file(void) { int i; FILE *f; unsigned long long sum = 0; double verification = 0; if (!mem_warming_enabled) return; f = fopen(mem_warming_pattern_file, "w+"); if (!f) { LM_ERR("failed to open pattern file %s for writing: %d - %s\n", mem_warming_pattern_file, errno, strerror(errno)); return; } if (fprintf(f, "%lu %lu\n", ROUNDTO, HP_HASH_SIZE) < 0) goto write_error; /* first compute sum of all malloc requests since startup */ for (i = 0; i < HP_MALLOC_OPTIMIZE / ROUNDTO; i++) sum += mem_hash_usage[i] * (i * ROUNDTO + FRAG_OVERHEAD); LM_DBG("mem warming hash sum: %llu\n", sum); /* save the usage rate of each bucket to the memory pattern file */ for (i = 0; i < HP_MALLOC_OPTIMIZE / ROUNDTO; i++) { LM_DBG("[%d] %lf %.8lf\n", i, (double)mem_hash_usage[i], (double)mem_hash_usage[i] / sum * (i * ROUNDTO + FRAG_OVERHEAD)); verification += (double)mem_hash_usage[i] / sum * (i * ROUNDTO + FRAG_OVERHEAD); if (fprintf(f, "%.12lf ", (double)mem_hash_usage[i] / sum * (i * ROUNDTO + FRAG_OVERHEAD)) < 0) goto write_error; if (i % 10 == 9) fprintf(f, "\n"); } if (verification < 0.99 || verification > 1.01) LM_INFO("memory pattern file appears to be incorrect: %lf\n", verification); LM_INFO("updated memory pattern file %s\n", mem_warming_pattern_file); fclose(f); return; write_error: LM_ERR("failed to update pattern file %s\n", mem_warming_pattern_file); fclose(f); } /** * on-demand memory fragmentation, based on an input pattern file */ int hp_mem_warming(struct hp_block *hpb) { struct size_fraction { int hash_index; double amount; unsigned long fragments; struct size_fraction *next; }; struct size_fraction *sf, *it, *sorted_sf = NULL; FILE *f; size_t rc; unsigned long roundto, hash_size; long long bucket_mem; int i, c = 0; unsigned int current_frag_size; struct hp_frag *big_frag; unsigned int optimized_buckets; f = fopen(mem_warming_pattern_file, "r"); if (!f) { LM_ERR("failed to open pattern file %s: %d - %s\n", mem_warming_pattern_file, errno, strerror(errno)); return -1; } rc = fscanf(f, "%lu %lu\n", &roundto, &hash_size); if (rc != 2) { LM_ERR("failed to read from %s: bad file format\n", mem_warming_pattern_file); goto out; } rc = 0; if (roundto != ROUNDTO || hash_size != HP_HASH_SIZE) { LM_ERR("incompatible pattern file data: [HP_HASH_SIZE: %lu-%lu] " "[ROUNDTO: %lu-%lu]\n", hash_size, HP_HASH_SIZE, roundto, ROUNDTO); rc = -1; goto out; } /* read bucket usage percentages and sort them by number of fragments */ for (i = 0; i < HP_LINEAR_HASH_SIZE; i++) { sf = malloc(sizeof *sf); if (!sf) { LM_INFO("malloc failed, skipping shm warming\n"); rc = -1; goto out_free; } sf->hash_index = i; sf->next = NULL; if (fscanf(f, "%lf", &sf->amount) != 1) { LM_CRIT("%s appears to be corrupt. Please remove it first\n", mem_warming_pattern_file); abort(); } if (i == 0) sf->fragments = 0; else sf->fragments = sf->amount * hpb->size / (ROUNDTO * i); if (!sorted_sf) sorted_sf = sf; else { for (it = sorted_sf; it->next && it->next->fragments > sf->fragments; it = it->next) ; if (it->fragments < sf->fragments) { sf->next = sorted_sf; sorted_sf = sf; } else { sf->next = it->next; it->next = sf; } } } /* only optimize the configured number of buckets */ optimized_buckets = (float)shm_hash_split_percentage / 100 * HP_LINEAR_HASH_SIZE; LM_INFO("Optimizing %u / %lu mem buckets\n", optimized_buckets, HP_LINEAR_HASH_SIZE); sf = sorted_sf; for (i = 0; i < optimized_buckets; i++) { hpb->free_hash[sf->hash_index].is_optimized = 1; sf = sf->next; } big_frag = hpb->first_frag; /* populate each free hash bucket with proper number of fragments */ for (sf = sorted_sf; sf; sf = sf->next) { LM_INFO("[%d][%s] fraction: %.12lf total mem: %llu, %lu\n", sf->hash_index, hpb->free_hash[sf->hash_index].is_optimized ? "X" : " ", sf->amount, (unsigned long long) (sf->amount * hpb->size * mem_warming_percentage / 100), ROUNDTO * sf->hash_index); current_frag_size = ROUNDTO * sf->hash_index; bucket_mem = sf->amount * hpb->size * mem_warming_percentage / 100; /* create free fragments worth of 'bucket_mem' memory */ while (bucket_mem >= FRAG_OVERHEAD + current_frag_size) { hp_frag_detach(hpb, big_frag); if (stats_are_ready()) { update_stats_shm_frag_detach(big_frag); #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->used += big_frag->size; hpb->real_used += big_frag->size + FRAG_OVERHEAD; #endif } else { hpb->used += big_frag->size; hpb->real_used += big_frag->size + FRAG_OVERHEAD; } /* trim-insert operation on the big free fragment */ #ifdef DBG_MALLOC shm_frag_split_unsafe(hpb, big_frag, current_frag_size, __FILE__, __FUNCTION__, __LINE__); #else shm_frag_split_unsafe(hpb, big_frag, current_frag_size); #endif /* * "big_frag" now points to a smaller, free and detached frag. * * With optimized buckets, inserts will be automagically * balanced within their dedicated hashes */ hp_frag_attach(hpb, big_frag); if (stats_are_ready()) { update_stats_shm_frag_attach(big_frag); #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->used -= big_frag->size; hpb->real_used -= big_frag->size + FRAG_OVERHEAD; #endif } else { hpb->used -= big_frag->size; hpb->real_used -= big_frag->size + FRAG_OVERHEAD; } big_frag = FRAG_NEXT(big_frag); bucket_mem -= FRAG_OVERHEAD + current_frag_size; if (c % 1000000 == 0) LM_INFO("%d| %lld %p\n", c, bucket_mem, big_frag); c++; } } out_free: while (sorted_sf) { sf = sorted_sf; sorted_sf = sorted_sf->next; free(sf); } out: fclose(f); return rc; } /* initialise the allocator and return its main block */ static struct hp_block *hp_malloc_init(char *address, unsigned long size, char *name) { char *start; char *end; struct hp_block *hpb; unsigned long init_overhead; /* make address and size multiple of 8*/ start = (char *)ROUNDUP((unsigned long) address); LM_DBG("HP_OPTIMIZE=%lu, HP_LINEAR_HASH_SIZE=%lu\n", HP_MALLOC_OPTIMIZE, HP_LINEAR_HASH_SIZE); LM_DBG("HP_HASH_SIZE=%lu, HP_EXTRA_HASH_SIZE=%lu, hp_block size=%zu\n", HP_HASH_SIZE, HP_EXTRA_HASH_SIZE, sizeof(struct hp_block)); LM_DBG("params (%p, %lu), start=%p\n", address, size, start); if (size < (unsigned long)(start - address)) return NULL; size -= start - address; if (size < (MIN_FRAG_SIZE+FRAG_OVERHEAD)) return NULL; size = ROUNDDOWN(size); init_overhead = (ROUNDUP(sizeof(struct hp_block)) + 2 * FRAG_OVERHEAD); if (size < init_overhead) { LM_ERR("not enough memory for the basic structures! " "need %lu bytes\n", init_overhead); /* not enough mem to create our control structures !!!*/ return NULL; } end = start + size; hpb = (struct hp_block *)start; memset(hpb, 0, sizeof(struct hp_block)); hpb->name = name; hpb->size = size; hpb->used = 0; hpb->real_used = init_overhead; hpb->max_real_used = init_overhead; gettimeofday(&hpb->last_updated, NULL); hpb->first_frag = (struct hp_frag *)(start + ROUNDUP(sizeof(struct hp_block))); hpb->last_frag = (struct hp_frag *)(end - sizeof(struct hp_frag)); /* init initial fragment*/ hpb->first_frag->size = size - init_overhead; hpb->last_frag->size = 0; hpb->last_frag->prev = NULL; hpb->first_frag->prev = NULL; /* link initial fragment into the free list*/ hpb->large_space = 0; hpb->large_limit = hpb->size / 100 * HP_MALLOC_DEFRAG_PERCENT; if (hpb->large_limit < HP_MALLOC_DEFRAG_LIMIT) hpb->large_limit = HP_MALLOC_DEFRAG_LIMIT; return hpb; } struct hp_block *hp_pkg_malloc_init(char *address, unsigned long size, char *name) { struct hp_block *hpb; hpb = hp_malloc_init(address, size, name); if (!hpb) { LM_ERR("failed to initialize shm block\n"); return NULL; } hp_frag_attach(hpb, hpb->first_frag); /* first fragment attach is the equivalent of a split */ #if defined(DBG_MALLOC) && !defined(STATISTICS) hpb->real_used += FRAG_OVERHEAD; hpb->total_fragments++; #endif return hpb; } struct hp_block *hp_shm_malloc_init(char *address, unsigned long size, char *name) { struct hp_block *hpb; hpb = hp_malloc_init(address, size, name); if (!hpb) { LM_ERR("failed to initialize shm block\n"); return NULL; } #ifdef HP_MALLOC_FAST_STATS hpb->free_hash[PEEK_HASH_RR(hpb, hpb->first_frag->size)].total_no++; #endif hp_frag_attach(hpb, hpb->first_frag); /* first fragment attach is the equivalent of a split */ if (stats_are_ready()) { #if defined(STATISTICS) && !defined(HP_MALLOC_FAST_STATS) update_stat(shm_rused, FRAG_OVERHEAD); update_stat(shm_frags, 1); #endif #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->real_used += FRAG_OVERHEAD; hpb->total_fragments++; #endif } else { hpb->real_used += FRAG_OVERHEAD; hpb->total_fragments++; } /* if memory warming is on, pre-populate the hash with free fragments */ if (mem_warming_enabled) { if (!mem_warming_pattern_file) mem_warming_pattern_file = MEM_WARMING_DEFAULT_PATTERN_FILE; if (mem_warming_percentage == -1) mem_warming_percentage = MEM_WARMING_DEFAULT_PERCENTAGE; if (hp_mem_warming(hpb) != 0) LM_INFO("skipped memory warming\n"); } #ifdef DBG_MALLOC hp_stats_lock = hp_shm_malloc_unsafe(hpb, sizeof *hp_stats_lock, __FILE__, __FUNCTION__, __LINE__); #else hp_stats_lock = hp_shm_malloc_unsafe(hpb, sizeof *hp_stats_lock); #endif if (!hp_stats_lock) { LM_ERR("failed to alloc hp statistics lock\n"); return NULL; } if (!lock_init(hp_stats_lock)) { LM_CRIT("could not initialize hp statistics lock\n"); return NULL; } return hpb; } #ifdef DBG_MALLOC void *hp_pkg_malloc(struct hp_block *hpb, unsigned long size, const char* file, const char* func, unsigned int line) #else void *hp_pkg_malloc(struct hp_block *hpb, unsigned long size) #endif { struct hp_frag *frag; unsigned int hash; /* size must be a multiple of ROUNDTO */ size = ROUNDUP(size); /* search for a suitable free frag */ for (hash = GET_HASH(size); hash < HP_HASH_SIZE; hash++) { frag = hpb->free_hash[hash].first; for (; frag; frag = frag->u.nxt_free) if (frag->size >= size) goto found; /* try in a bigger bucket */ } /* out of memory... we have to shut down */ #if defined(DBG_MALLOC) || defined(STATISTICS) LM_ERR(oom_errorf, hpb->name, hpb->size - hpb->real_used, hpb->name[0] == 'p' ? "M" : "m"); #else LM_ERR(oom_nostats_errorf, hpb->name, hpb->name[0] == 'p' ? "M" : "m"); #endif LM_INFO("Safely shutting down OpenSIPS (aborting) ...\n"); abort(); found: hp_frag_detach(hpb, frag); update_stats_pkg_frag_detach(hpb, frag); #ifdef DBG_MALLOC /* mark fragment as "busy" */ frag->is_free = 0; #ifndef STATISTICS hpb->used += frag->size; hpb->real_used += frag->size + FRAG_OVERHEAD; #endif #endif /* split the fragment if possible */ #ifdef DBG_MALLOC pkg_frag_split(hpb, frag, size, file, "fragm. from hp_malloc", line); frag->file=file; frag->func=func; frag->line=line; #else pkg_frag_split(hpb, frag, size); #endif if (hpb->real_used > hpb->max_real_used) hpb->max_real_used = hpb->real_used; pkg_threshold_check(); return (char *)frag + sizeof *frag; } /* * although there is a lot of duplicate code, we get the best performance: * * - the _unsafe version will not be used too much anyway (usually at startup) * - hp_shm_malloc is faster (no 3rd parameter, no extra if blocks) */ #ifdef DBG_MALLOC void *hp_shm_malloc_unsafe(struct hp_block *hpb, unsigned long size, const char* file, const char* func, unsigned int line) #else void *hp_shm_malloc_unsafe(struct hp_block *hpb, unsigned long size) #endif { struct hp_frag *frag; unsigned int init_hash, hash, sec_hash; int i; /* size must be a multiple of ROUNDTO */ size = ROUNDUP(size); /*search for a suitable free frag*/ for (hash = GET_HASH(size), init_hash = hash; hash < HP_HASH_SIZE; hash++) { if (!hpb->free_hash[hash].is_optimized) { frag = hpb->free_hash[hash].first; for (; frag; frag = frag->u.nxt_free) if (frag->size >= size) goto found; } else { /* optimized size. search through its own hash! */ for (i = 0, sec_hash = HP_HASH_SIZE + hash * shm_secondary_hash_size + optimized_get_indexes[hash]; i < shm_secondary_hash_size; i++, sec_hash = (sec_hash + 1) % shm_secondary_hash_size) { frag = hpb->free_hash[sec_hash].first; for (; frag; frag = frag->u.nxt_free) if (frag->size >= size) { /* free fragments are detached in a simple round-robin manner */ optimized_get_indexes[hash] = (optimized_get_indexes[hash] + i + 1) % shm_secondary_hash_size; hash = sec_hash; goto found; } } } /* try in a bigger bucket */ } /* out of memory... we have to shut down */ #if defined(DBG_MALLOC) || defined(STATISTICS) LM_ERR(oom_errorf, hpb->name, hpb->size - hpb->real_used, hpb->name[0] == 'p' ? "M" : "m"); #else LM_ERR(oom_nostats_errorf, hpb->name, hpb->name[0] == 'p' ? "M" : "m"); #endif abort(); found: hp_frag_detach(hpb, frag); if (stats_are_ready()) { update_stats_shm_frag_detach(frag); #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->used += frag->size; hpb->real_used += frag->size + FRAG_OVERHEAD; #endif } else { hpb->used += frag->size; hpb->real_used += frag->size + FRAG_OVERHEAD; } #ifdef DBG_MALLOC /* mark it as "busy" */ frag->is_free = 0; /* split the fragment if possible */ shm_frag_split_unsafe(hpb, frag, size, file, "fragm. from hp_malloc", line); frag->file=file; frag->func=func; frag->line=line; #else shm_frag_split_unsafe(hpb, frag, size); #endif #ifndef HP_MALLOC_FAST_STATS if (stats_are_ready()) { unsigned long real_used; real_used = get_stat_val(shm_rused); if (real_used > hpb->max_real_used) hpb->max_real_used = real_used; } else if (hpb->real_used > hpb->max_real_used) hpb->max_real_used = hpb->real_used; #endif if (mem_hash_usage) mem_hash_usage[init_hash]++; return (char *)frag + sizeof *frag; } /* * Note: as opposed to hp_shm_malloc_unsafe(), * hp_shm_malloc() assumes that the core statistics are initialized */ #ifdef DBG_MALLOC void *hp_shm_malloc(struct hp_block *hpb, unsigned long size, const char* file, const char* func, unsigned int line) #else void *hp_shm_malloc(struct hp_block *hpb, unsigned long size) #endif { struct hp_frag *frag; unsigned int init_hash, hash, sec_hash; int i; /* size must be a multiple of ROUNDTO */ size = ROUNDUP(size); /*search for a suitable free frag*/ for (hash = GET_HASH(size), init_hash = hash; hash < HP_HASH_SIZE; hash++) { if (!hpb->free_hash[hash].is_optimized) { SHM_LOCK(hash); frag = hpb->free_hash[hash].first; for (; frag; frag = frag->u.nxt_free) if (frag->size >= size) goto found; SHM_UNLOCK(hash); } else { /* optimized size. search through its own hash! */ for (i = 0, sec_hash = HP_HASH_SIZE + hash * shm_secondary_hash_size + optimized_get_indexes[hash]; i < shm_secondary_hash_size; i++, sec_hash = (sec_hash + 1) % shm_secondary_hash_size) { SHM_LOCK(sec_hash); frag = hpb->free_hash[sec_hash].first; for (; frag; frag = frag->u.nxt_free) if (frag->size >= size) { /* free fragments are detached in a simple round-robin manner */ optimized_get_indexes[hash] = (optimized_get_indexes[hash] + i + 1) % shm_secondary_hash_size; hash = sec_hash; goto found; } SHM_UNLOCK(sec_hash); } } /* try in a bigger bucket */ } /* out of memory... we have to shut down */ #if defined(DBG_MALLOC) || defined(STATISTICS) LM_ERR(oom_errorf, hpb->name, hpb->size - hpb->real_used, hpb->name[0] == 'p' ? "M" : "m"); #else LM_ERR(oom_nostats_errorf, hpb->name, hpb->name[0] == 'p' ? "M" : "m"); #endif abort(); found: hp_frag_detach(hpb, frag); update_stats_shm_frag_detach(frag); #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->used += (frag)->size; hpb->real_used += (frag)->size + FRAG_OVERHEAD; #endif #ifdef DBG_MALLOC /* mark fragment as "busy" */ frag->is_free = 0; /* split the fragment if possible */ shm_frag_split(hpb, frag, size, hash, file, "fragm. from hp_malloc", line); frag->file=file; frag->func=func; frag->line=line; #else shm_frag_split(hpb, frag, size, hash); #endif SHM_UNLOCK(hash); #ifndef HP_MALLOC_FAST_STATS unsigned long real_used; real_used = get_stat_val(shm_rused); if (real_used > hpb->max_real_used) hpb->max_real_used = real_used; #endif /* ignore concurrency issues, simply obtaining an estimate is enough */ mem_hash_usage[init_hash]++; return (char *)frag + sizeof *frag; } #ifdef DBG_MALLOC void hp_pkg_free(struct hp_block *hpb, void *p, const char* file, const char* func, unsigned int line) #else void hp_pkg_free(struct hp_block *hpb, void *p) #endif { struct hp_frag *f, *next; if (!p) { LM_WARN("free(0) called\n"); return; } f = FRAG_OF(p); /* * for private memory, coalesce as many consecutive fragments as possible * The same operation is not performed for shared memory, because: * - performance penalties introduced by additional locking logic * - the allocator itself actually favours fragmentation and reusage */ for (;;) { next = FRAG_NEXT(f); if (next >= hpb->last_frag || !next->prev) break; hp_frag_detach(hpb, next); update_stats_pkg_frag_detach(hpb, next); #ifdef DBG_MALLOC #ifndef STATISTICS hpb->used += next->size; hpb->real_used += next->size + FRAG_OVERHEAD; #endif hpb->used += FRAG_OVERHEAD; #endif f->size += next->size + FRAG_OVERHEAD; update_stats_pkg_frag_merge(hpb); #if defined(DBG_MALLOC) && !defined(STATISTICS) hpb->real_used -= FRAG_OVERHEAD; hpb->total_fragments--; #endif } hp_frag_attach(hpb, f); update_stats_pkg_frag_attach(hpb, f); #if defined(DBG_MALLOC) && !defined(STATISTICS) hpb->used -= f->size; hpb->real_used -= f->size + FRAG_OVERHEAD; #endif } #ifdef DBG_MALLOC void hp_shm_free_unsafe(struct hp_block *hpb, void *p, const char* file, const char* func, unsigned int line) #else void hp_shm_free_unsafe(struct hp_block *hpb, void *p) #endif { struct hp_frag *f; if (!p) { LM_WARN("free(0) called\n"); return; } f = FRAG_OF(p); hp_frag_attach(hpb, f); update_stats_shm_frag_attach(f); #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->used -= f->size; hpb->real_used -= f->size + FRAG_OVERHEAD; #endif } #ifdef DBG_MALLOC void hp_shm_free(struct hp_block *hpb, void *p, const char* file, const char* func, unsigned int line) #else void hp_shm_free(struct hp_block *hpb, void *p) #endif { struct hp_frag *f; unsigned int hash; if (!p) { LM_WARN("free(0) called\n"); return; } f = FRAG_OF(p); hash = PEEK_HASH_RR(hpb, f->size); SHM_LOCK(hash); hp_frag_attach(hpb, f); SHM_UNLOCK(hash); update_stats_shm_frag_attach(f); #if defined(DBG_MALLOC) || defined(STATISTICS) hpb->used -= f->size; hpb->real_used -= f->size + FRAG_OVERHEAD; #endif } #ifdef DBG_MALLOC void *hp_pkg_realloc(struct hp_block *hpb, void *p, unsigned long size, const char* file, const char* func, unsigned int line) #else void *hp_pkg_realloc(struct hp_block *hpb, void *p, unsigned long size) #endif { struct hp_frag *f; unsigned long diff; unsigned long orig_size; struct hp_frag *next; void *ptr; if (size == 0) { if (p) #ifdef DBG_MALLOC hp_pkg_free(hpb, p, file, func, line); #else hp_pkg_free(hpb, p); #endif return NULL; } if (!p) #ifdef DBG_MALLOC return hp_pkg_malloc(hpb, size, file, func, line); #else return hp_pkg_malloc(hpb, size); #endif f = FRAG_OF(p); size = ROUNDUP(size); orig_size = f->size; /* shrink operation */ if (orig_size > size) { #ifdef DBG_MALLOC pkg_frag_split(hpb, f, size, file, "fragm. from hp_realloc", line); #else pkg_frag_split(hpb, f, size); #endif /* grow operation */ } else if (orig_size < size) { diff = size - orig_size; next = FRAG_NEXT(f); /* try to join with a large enough adjacent free fragment */ if (next < hpb->last_frag && next->prev && (next->size + FRAG_OVERHEAD) >= diff) { hp_frag_detach(hpb, next); update_stats_pkg_frag_detach(hpb, next); #ifdef DBG_MALLOC #ifndef STATISTICS hpb->used += next->size; hpb->real_used += next->size + FRAG_OVERHEAD; #endif hpb->used += FRAG_OVERHEAD; #endif f->size += next->size + FRAG_OVERHEAD; /* split the result if necessary */ if (f->size > size) #ifdef DBG_MALLOC pkg_frag_split(hpb, f, size, file, "fragm. from hp_realloc", line); #else pkg_frag_split(hpb, f, size); #endif } else { /* could not join => realloc */ #ifdef DBG_MALLOC ptr = hp_pkg_malloc(hpb, size, file, func, line); #else ptr = hp_pkg_malloc(hpb, size); #endif if (ptr) { /* copy, need by libssl */ memcpy(ptr, p, orig_size); #ifdef DBG_MALLOC hp_pkg_free(hpb, p, file, func, line); #else hp_pkg_free(hpb, p); #endif } p = ptr; } if (hpb->real_used > hpb->max_real_used) hpb->max_real_used = hpb->real_used; } pkg_threshold_check(); return p; } #ifdef DBG_MALLOC void *hp_shm_realloc_unsafe(struct hp_block *hpb, void *p, unsigned long size, const char* file, const char* func, unsigned int line) #else void *hp_shm_realloc_unsafe(struct hp_block *hpb, void *p, unsigned long size) #endif { struct hp_frag *f; unsigned long orig_size; void *ptr; if (size == 0) { if (p) #ifdef DBG_MALLOC hp_shm_free_unsafe(hpb, p, file, func, line); #else hp_shm_free_unsafe(hpb, p); #endif return NULL; } if (!p) #ifdef DBG_MALLOC return hp_shm_malloc_unsafe(hpb, size, file, func, line); #else return hp_shm_malloc_unsafe(hpb, size); #endif f = FRAG_OF(p); size = ROUNDUP(size); orig_size = f->size; /* shrink operation? */ if (orig_size > size) #ifdef DBG_MALLOC shm_frag_split_unsafe(hpb, f, size, file, "fragm. from hp_realloc", line); #else shm_frag_split_unsafe(hpb, f, size); #endif else if (orig_size < size) { #ifdef DBG_MALLOC ptr = hp_shm_malloc_unsafe(hpb, size, file, func, line); #else ptr = hp_shm_malloc_unsafe(hpb, size); #endif if (ptr) { /* copy, need by libssl */ memcpy(ptr, p, orig_size); #ifdef DBG_MALLOC hp_shm_free_unsafe(hpb, p, file, func, line); #else hp_shm_free_unsafe(hpb, p); #endif } p = ptr; } return p; } #ifdef DBG_MALLOC void *hp_shm_realloc(struct hp_block *hpb, void *p, unsigned long size, const char* file, const char* func, unsigned int line) #else void *hp_shm_realloc(struct hp_block *hpb, void *p, unsigned long size) #endif { struct hp_frag *f; unsigned long orig_size; unsigned int hash; void *ptr; if (size == 0) { if (p) #ifdef DBG_MALLOC hp_shm_free(hpb, p, file, func, line); #else hp_shm_free(hpb, p); #endif return NULL; } if (!p) #ifdef DBG_MALLOC return hp_shm_malloc(hpb, size, file, func, line); #else return hp_shm_malloc(hpb, size); #endif f = FRAG_OF(p); size = ROUNDUP(size); hash = PEEK_HASH_RR(hpb, f->size); SHM_LOCK(hash); orig_size = f->size; if (orig_size > size) { /* shrink */ #ifdef DBG_MALLOC shm_frag_split_unsafe(hpb, f, size, file, "fragm. from hp_realloc", line); #else shm_frag_split_unsafe(hpb, f, size); #endif } else if (orig_size < size) { SHM_UNLOCK(hash); #ifdef DBG_MALLOC ptr = hp_shm_malloc(hpb, size, file, func, line); #else ptr = hp_shm_malloc(hpb, size); #endif if (ptr) { /* copy, need by libssl */ memcpy(ptr, p, orig_size); #ifdef DBG_MALLOC hp_shm_free(hpb, p, file, func, line); #else hp_shm_free(hpb, p); #endif } return ptr; } SHM_UNLOCK(hash); return p; } void hp_status(struct hp_block *hpb) { struct hp_frag *f; int i, j, si, t = 0; int h; #ifdef DBG_MALLOC mem_dbg_htable_t allocd; struct mem_dbg_entry *it; #endif #if HP_MALLOC_FAST_STATS && (defined(DBG_MALLOC) || defined(STATISTICS)) if (hpb == shm_block) update_shm_stats(hpb); #endif LM_GEN1(memdump, "hp_status (%p, ROUNDTO=%ld):\n", hpb, ROUNDTO); if (!hpb) return; LM_GEN1(memdump, "%20s : %ld\n", "HP_HASH_SIZE", HP_HASH_SIZE); LM_GEN1(memdump, "%20s : %ld\n", "HP_EXTRA_HASH_SIZE", HP_HASH_SIZE); LM_GEN1(memdump, "%20s : %ld\n", "HP_TOTAL_SIZE", HP_HASH_SIZE); LM_GEN1(memdump, "%20s : %ld\n", "total_size", hpb->size); #if defined(STATISTICS) || defined(DBG_MALLOC) LM_GEN1(memdump, "%20s : %lu\n%20s : %lu\n%20s : %lu\n", "used", hpb->used, "used+overhead", hpb->real_used, "free", hpb->size - hpb->used); LM_GEN1(memdump, "%20s : %lu\n\n", "max_used (+overhead)", hpb->max_real_used); #endif #ifdef DBG_MALLOC dbg_ht_init(allocd); for (f=hpb->first_frag; (char*)f<(char*)hpb->last_frag; f=FRAG_NEXT(f)) if (!f->is_free) if (dbg_ht_update(allocd, f->file, f->func, f->line, f->size) < 0) { LM_ERR("Unable to update alloc'ed. memory summary\n"); return; } LM_GEN1(memdump, "dumping summary of all alloc'ed. fragments:\n"); for(i=0; i < DBG_HASH_SIZE; i++) { it = allocd[i]; while (it) { LM_GEN1(memdump, " %10lu : %lu x [%s: %s, line %lu]\n", it->size, it->no_fragments, it->file, it->func, it->line); it = it->next; } } dbg_ht_free(allocd); #endif LM_GEN1(memdump, "Dumping free fragments:\n"); for (h = 0; h < HP_HASH_SIZE; h++) { if (hpb->free_hash[h].is_optimized) { LM_GEN1(memdump, "[ %4d ][ %5d B ][ frags: ", h, h * (int)ROUNDTO); for (si = HP_HASH_SIZE + h * shm_secondary_hash_size, j = 0; j < shm_secondary_hash_size; j++, si++, t++) { SHM_LOCK(si); for (i=0, f=hpb->free_hash[si].first; f; f=f->u.nxt_free, i++, t++) ; SHM_UNLOCK(si); LM_GEN1(memdump, "%s%5d ", j == 0 ? "" : "| ", i); } LM_GEN1(memdump, "]\n"); } else { SHM_LOCK(h); for (i=0, f=hpb->free_hash[h].first; f; f=f->u.nxt_free, i++, t++) ; SHM_UNLOCK(h); if (i == 0) continue; if (h > HP_LINEAR_HASH_SIZE) { LM_GEN1(memdump, "[ %4d ][ %8d B -> %7d B ][ frags: %5d ]\n", h, (int)UN_HASH(h), (int)UN_HASH(h+1) - (int)ROUNDTO, i); } else LM_GEN1(memdump, "[ %4d ][ %5d B ][ frags: %5d ]\n", h, h * (int)ROUNDTO, i); } } LM_GEN1(memdump, "TOTAL: %6d free fragments\n", t); LM_GEN1(memdump, "TOTAL: %ld large bytes\n", hpb->large_space ); LM_GEN1(memdump, "TOTAL: %u overhead\n", (unsigned int)FRAG_OVERHEAD ); LM_GEN1(memdump, "-----------------------------\n"); } /* fills a malloc info structure with info about the memory block */ void hp_info(struct hp_block *hpb, struct mem_info *info) { memset(info, 0, sizeof *info); info->total_size = hpb->size; info->min_frag = MIN_FRAG_SIZE; #ifdef HP_MALLOC_FAST_STATS if (stats_are_expired(hpb)) update_shm_stats(hpb); #endif info->used = hpb->used; info->real_used = hpb->real_used; info->free = hpb->size - hpb->real_used; info->total_frags = hpb->total_fragments; LM_DBG("mem_info: (sz: %ld | us: %ld | rus: %ld | free: %ld | frags: %ld)\n", info->total_size, info->used, info->real_used, info->free, info->total_frags); } #endif opensips-2.2.2/mem/hp_malloc.h000066400000000000000000000203451300170765700162540ustar00rootroot00000000000000/** * the truly parallel memory allocator * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-01-19 initial version (liviu) */ #if !defined(HP_MALLOC_H) && !defined(VQ_MALLOC) && !defined(QM_MALLOC) && \ !defined(F_MALLOC) #define HP_MALLOC_H #include #include "../statistics.h" #include "../config.h" #include "../globals.h" struct hp_frag; struct hp_frag_lnk; struct hp_block; #ifndef HP_MALLOC_FAST_STATS extern stat_var *shm_used; extern stat_var *shm_rused; extern stat_var *shm_frags; #endif #include "hp_malloc_stats.h" #include "meminfo.h" #define ROUNDTO 8UL #define MIN_FRAG_SIZE ROUNDTO #define FRAG_NEXT(f) ((struct hp_frag *) \ ((char *)(f) + sizeof(struct hp_frag) + ((struct hp_frag *)(f))->size)) /* get the fragment which corresponds to a pointer */ #define FRAG_OF(p) \ ((struct hp_frag *)((char *)(p) - sizeof(struct hp_frag))) #define FRAG_OVERHEAD (sizeof(struct hp_frag)) #define HP_MALLOC_OPTIMIZE_FACTOR 14UL /*used below */ #define HP_MALLOC_OPTIMIZE (1UL << HP_MALLOC_OPTIMIZE_FACTOR) /* size to optimize for, (most allocs <= this size), must be 2^k */ #define HP_LINEAR_HASH_SIZE (HP_MALLOC_OPTIMIZE/ROUNDTO) #define HP_EXPONENTIAL_HASH_SIZE ((sizeof(long)*8-HP_MALLOC_OPTIMIZE_FACTOR)+1) #define HP_HASH_SIZE (HP_LINEAR_HASH_SIZE + HP_EXPONENTIAL_HASH_SIZE) #define HP_EXTRA_HASH_SIZE (HP_LINEAR_HASH_SIZE * SHM_MAX_SECONDARY_HASH_SIZE) #define HP_TOTAL_HASH_SIZE (HP_HASH_SIZE + HP_EXTRA_HASH_SIZE) /* hash structure: * 0 .... HP_MALLOC_OPTIMIZE/ROUNDTO - small buckets, size increases with * ROUNDTO from bucket to bucket * +1 .... end - size = 2^k, big buckets * (0 0 0 0 0 ... 0) (ROUNDTO ROUNDTO ...) (2*ROUNDTO...) .... (...) - additional array of hashes! * | | | | | | | | | * | | | | | | | | | * | | | | | | | | | * | | | | | | | | | * v v v v v v v v v * * ^------- sshs ----------^ ^------ sshs -------^ ^--- sshs ---^ ^---^ * * sshs = "shm_secondary_hash_size" script parameter */ /* used when detaching free fragments */ unsigned int optimized_get_indexes[HP_HASH_SIZE]; /* used when attaching free fragments */ unsigned int optimized_put_indexes[HP_HASH_SIZE]; /* finds the hash value for s, s=ROUNDTO multiple */ #define GET_HASH(s) (((unsigned long)(s) <= HP_MALLOC_OPTIMIZE) ? \ (unsigned long)(s) / ROUNDTO : \ HP_LINEAR_HASH_SIZE + big_hash_idx((s)) - HP_MALLOC_OPTIMIZE_FACTOR + 1) /* * - for heavily used sizes (which need some optimizing) it returns * a hash entry for the given size in a round-robin manner * - for the non-optimized sizes, behaviour is identical to GET_HASH */ #define GET_HASH_RR(fmb, s) (((unsigned long)(s) <= HP_MALLOC_OPTIMIZE) ? \ ({ \ unsigned int ___hash, ___idx, ___ret; \ ___hash = (unsigned long)(s) / ROUNDTO; \ !fmb->free_hash[___hash].is_optimized ? \ ___hash : \ ({ \ ___idx = optimized_put_indexes[___hash]; \ ___ret = HP_HASH_SIZE + \ ___hash * shm_secondary_hash_size + ___idx; \ optimized_put_indexes[___hash] = \ (___idx + 1) % shm_secondary_hash_size; \ ___ret; \ }); \ }) : \ HP_LINEAR_HASH_SIZE + big_hash_idx((s)) - HP_MALLOC_OPTIMIZE_FACTOR + 1) /* * peek at the next round-robin assigned hash * * unlike GET_HASH_RR, it always returns the same result */ #define PEEK_HASH_RR(fmb, s) (((unsigned long)(s) <= HP_MALLOC_OPTIMIZE) ? \ ({ \ unsigned int ___hash; \ ___hash = (unsigned long)(s) / ROUNDTO; \ !fmb->free_hash[___hash].is_optimized ? \ ___hash : \ HP_HASH_SIZE + ___hash * shm_secondary_hash_size + \ optimized_put_indexes[___hash]; \ }) : \ HP_LINEAR_HASH_SIZE + big_hash_idx((s)) - HP_MALLOC_OPTIMIZE_FACTOR + 1) #define UN_HASH(h) (((unsigned long)(h) <= (HP_MALLOC_OPTIMIZE/ROUNDTO)) ?\ (unsigned long)(h)*ROUNDTO: \ 1UL<<((unsigned long)(h)-HP_MALLOC_OPTIMIZE/ROUNDTO+\ HP_MALLOC_OPTIMIZE_FACTOR - 1)\ ) struct hp_frag { unsigned long size; union { struct hp_frag *nxt_free; long reserved; } u; struct hp_frag **prev; #ifdef DBG_MALLOC const char* file; const char* func; unsigned long line; char is_free; #endif }; struct hp_frag_lnk { /* * optimized buckets are further split into * "shm_secondary_hash_size" buckets */ char is_optimized; struct hp_frag *first; /* * no - current number of free fragments in this bucket * total_no - (no + allocated) free fragments in this bucket */ long no; long total_no; }; struct hp_block { char *name; /* purpose of this memory block */ unsigned long size; /* total size */ unsigned long large_space; unsigned long large_limit; unsigned long used; /* alloc'ed size */ unsigned long real_used; /* used+malloc overhead */ unsigned long max_real_used; unsigned long total_fragments; struct timeval last_updated; struct hp_frag *first_frag; struct hp_frag *last_frag; /* * the extra hash further divides the heavily used buckets * in order to achieve an even finer-grained locking */ struct hp_frag_lnk free_hash[HP_HASH_SIZE + HP_EXTRA_HASH_SIZE]; }; unsigned long frag_size(void* p); struct hp_block *hp_pkg_malloc_init(char *addr, unsigned long size, char *name); struct hp_block *hp_shm_malloc_init(char *addr, unsigned long size, char *name); int hp_mem_warming(struct hp_block *); void hp_update_mem_pattern_file(void); #ifdef DBG_MALLOC void *hp_shm_malloc(struct hp_block *, unsigned long size, const char* file, const char* func, unsigned int line); #else void *hp_shm_malloc(struct hp_block *, unsigned long size); #endif #ifdef DBG_MALLOC void *hp_shm_malloc_unsafe(struct hp_block *, unsigned long size, const char* file, const char* func, unsigned int line); #else void *hp_shm_malloc_unsafe(struct hp_block *, unsigned long size); #endif #ifdef DBG_MALLOC void *hp_pkg_malloc(struct hp_block *, unsigned long size, const char* file, const char* func, unsigned int line); #else void *hp_pkg_malloc(struct hp_block *, unsigned long size); #endif #ifdef DBG_MALLOC void hp_shm_free(struct hp_block *, void *p, const char* file, const char* func, unsigned int line); #else void hp_shm_free(struct hp_block *, void *p); #endif #ifdef DBG_MALLOC void hp_shm_free_unsafe(struct hp_block *, void *p, const char* file, const char* func, unsigned int line); #else void hp_shm_free_unsafe(struct hp_block *, void *p); #endif #ifdef DBG_MALLOC void hp_pkg_free(struct hp_block *, void *p, const char* file, const char* func, unsigned int line); #else void hp_pkg_free(struct hp_block *, void *p); #endif #ifdef DBG_MALLOC void *hp_shm_realloc(struct hp_block *, void *p, unsigned long size, const char* file, const char* func, unsigned int line); #else void *hp_shm_realloc(struct hp_block *, void *p, unsigned long size); #endif #ifdef DBG_MALLOC void *hp_shm_realloc_unsafe(struct hp_block *, void *p, unsigned long size, const char* file, const char* func, unsigned int line); #else void *hp_shm_realloc_unsafe(struct hp_block *, void *p, unsigned long size); #endif #ifdef DBG_MALLOC void *hp_pkg_realloc(struct hp_block *, void *p, unsigned long size, const char* file, const char* func, unsigned int line); #else void *hp_pkg_realloc(struct hp_block *, void *p, unsigned long size); #endif void hp_status(struct hp_block *); void hp_info(struct hp_block *, struct mem_info *); #endif /* HP_MALLOC_H */ opensips-2.2.2/mem/hp_malloc_stats.c000066400000000000000000000125361300170765700174700ustar00rootroot00000000000000/** * the truly parallel memory allocator * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-01-19 initial version (liviu) */ #if !defined(q_malloc) && !(defined VQ_MALLOC) && !(defined F_MALLOC) && \ (defined HP_MALLOC) #include #include #include #include "sys/time.h" #include "../lock_ops.h" #include "hp_malloc.h" #include "hp_malloc_stats.h" gen_lock_t *hp_stats_lock; #ifdef STATISTICS int stats_are_expired(struct hp_block *hpb) { struct timeval now; gettimeofday(&now, NULL); return (now.tv_sec * 1000000L + now.tv_usec) - (hpb->last_updated.tv_sec * 1000000L + hpb->last_updated.tv_usec) > SHM_STATS_SAMPLING_PERIOD; } void update_shm_stats(struct hp_block *hpb) { struct hp_frag_lnk *bucket, *it; int i, j, used_mem; unsigned long in_use_frags; long size = 0; lock_get(hp_stats_lock); hpb->used = hpb->real_used = hpb->total_fragments = 0; for (i = 0, bucket = hpb->free_hash; bucket < &hpb->free_hash[HP_HASH_SIZE]; i++, bucket++) { size = UN_HASH(i); if (!bucket->is_optimized) { in_use_frags = bucket->total_no - bucket->no; used_mem = in_use_frags * size; hpb->used += used_mem; hpb->real_used += used_mem + bucket->total_no * FRAG_OVERHEAD; hpb->total_fragments += bucket->total_no; } else { for (j = 0, it = &hpb->free_hash[HP_HASH_SIZE + i * shm_secondary_hash_size]; j < shm_secondary_hash_size; j++, it++) { in_use_frags = it->total_no - it->no; used_mem = in_use_frags * size; hpb->used += used_mem; hpb->real_used += used_mem + it->total_no * FRAG_OVERHEAD; hpb->total_fragments += it->total_no; } } } if (hpb->real_used > hpb->max_real_used) hpb->max_real_used = hpb->real_used; LM_DBG("updated shm statistics: [ us: %ld | rus: %ld | frags: %ld ]\n", hpb->used, hpb->real_used, hpb->total_fragments); gettimeofday(&hpb->last_updated, NULL); lock_release(hp_stats_lock); } unsigned long hp_shm_get_size(struct hp_block *hpb) { return hpb->size; } #ifdef HP_MALLOC_FAST_STATS unsigned long hp_shm_get_used(struct hp_block *hpb) { if (stats_are_expired(hpb)) update_shm_stats(hpb); return hpb->used; } unsigned long hp_shm_get_real_used(struct hp_block *hpb) { if (stats_are_expired(hpb)) update_shm_stats(hpb); return hpb->real_used; } unsigned long hp_shm_get_max_real_used(struct hp_block *hpb) { if (stats_are_expired(hpb)) update_shm_stats(hpb); return hpb->max_real_used; } unsigned long hp_shm_get_free(struct hp_block *hpb) { if (stats_are_expired(hpb)) update_shm_stats(hpb); return hpb->size - hpb->real_used; } unsigned long hp_shm_get_frags(struct hp_block *hpb) { if (stats_are_expired(hpb)) update_shm_stats(hpb); return hpb->total_fragments; } void hp_init_shm_statistics(struct hp_block *hpb) { update_shm_stats(hpb); } #else /* HP_MALLOC_FAST_STATS */ void hp_init_shm_statistics(struct hp_block *hpb) { #ifdef DBG_MALLOC /* reset stats updated by mallocs before this init */ shm_used->flags &= ~STAT_NO_RESET; shm_rused->flags &= ~STAT_NO_RESET; shm_frags->flags &= ~STAT_NO_RESET; reset_stat(shm_used); reset_stat(shm_rused); reset_stat(shm_frags); shm_used->flags |= STAT_NO_RESET; shm_rused->flags |= STAT_NO_RESET; shm_frags->flags |= STAT_NO_RESET; #endif update_stat(shm_used, hpb->used); update_stat(shm_rused, hpb->real_used); update_stat(shm_frags, hpb->total_fragments); LM_DBG("initializing atomic shm statistics: " "[ us: %ld | rus: %ld | frags: %ld ]\n", hpb->used, hpb->real_used, hpb->total_fragments); } unsigned long hp_shm_get_used(struct hp_block *hpb) { return get_stat_val(shm_used); } unsigned long hp_shm_get_real_used(struct hp_block *hpb) { return get_stat_val(shm_rused); } unsigned long hp_shm_get_max_real_used(struct hp_block *hpb) { return hpb->max_real_used; } unsigned long hp_shm_get_free(struct hp_block *hpb) { return hpb->size - get_stat_val(shm_rused); } unsigned long hp_shm_get_frags(struct hp_block *hpb) { return get_stat_val(shm_frags); } #endif /* HP_MALLOC_FAST_STATS */ unsigned long hp_pkg_get_size(struct hp_block *hpb) { return hpb->size; } unsigned long hp_pkg_get_used(struct hp_block *hpb) { return hpb->used; } unsigned long hp_pkg_get_real_used(struct hp_block *hpb) { return hpb->real_used; } unsigned long hp_pkg_get_max_real_used(struct hp_block *hpb) { return hpb->max_real_used; } unsigned long hp_pkg_get_free(struct hp_block *hpb) { return hpb->size - hpb->real_used; } unsigned long hp_pkg_get_frags(struct hp_block *hpb) { return hpb->total_fragments; } #endif /* STATISTICS */ #endif opensips-2.2.2/mem/hp_malloc_stats.h000066400000000000000000000073501300170765700174730ustar00rootroot00000000000000/** * the truly parallel memory allocator * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-01-19 initial version (liviu) */ #ifndef HP_MALLOC_STATS_H #define HP_MALLOC_STATS_H #include "../lock_ops.h" /* specified in microseconds */ #define SHM_STATS_SAMPLING_PERIOD 200000L extern gen_lock_t *hp_stats_lock; #ifdef STATISTICS int stats_are_expired(struct hp_block *hpb); void update_shm_stats(struct hp_block *hpb); void hp_init_shm_statistics(struct hp_block *hpb); unsigned long hp_shm_get_size(struct hp_block *hpb); unsigned long hp_shm_get_used(struct hp_block *hpb); unsigned long hp_shm_get_free(struct hp_block *hpb); unsigned long hp_shm_get_real_used(struct hp_block *hpb); unsigned long hp_shm_get_max_real_used(struct hp_block *hpb); unsigned long hp_shm_get_frags(struct hp_block *hpb); unsigned long hp_pkg_get_size(struct hp_block *hpb); unsigned long hp_pkg_get_used(struct hp_block *hpb); unsigned long hp_pkg_get_free(struct hp_block *hpb); unsigned long hp_pkg_get_real_used(struct hp_block *hpb); unsigned long hp_pkg_get_max_real_used(struct hp_block *hpb); unsigned long hp_pkg_get_frags(struct hp_block *hpb); #define update_stats_pkg_frag_attach(blk, frag) \ do { \ (blk)->used -= (frag)->size; \ (blk)->real_used -= (frag)->size + FRAG_OVERHEAD; \ } while (0) #define update_stats_pkg_frag_detach(blk, frag) \ do { \ (blk)->used += (frag)->size; \ (blk)->real_used += (frag)->size + FRAG_OVERHEAD; \ } while (0) #define update_stats_pkg_frag_split(blk, ...) \ do { \ (blk)->used -= FRAG_OVERHEAD; \ (blk)->real_used += FRAG_OVERHEAD; \ (blk)->total_fragments++; \ } while (0) #define update_stats_pkg_frag_merge(blk, ...) \ do { \ (blk)->real_used -= FRAG_OVERHEAD; \ (blk)->total_fragments--; \ } while (0) #ifdef HP_MALLOC_FAST_STATS #define update_stats_shm_frag_attach(frag) #define update_stats_shm_frag_detach(frag) #define update_stats_shm_frag_split() #else /* HP_MALLOC_FAST_STATS */ #define update_stats_shm_frag_attach(frag) \ do { \ update_stat(shm_used, -(frag)->size); \ update_stat(shm_rused, -((frag)->size + FRAG_OVERHEAD)); \ } while (0) #define update_stats_shm_frag_detach(frag) \ do { \ update_stat(shm_used, (frag)->size); \ update_stat(shm_rused, (frag)->size + FRAG_OVERHEAD); \ } while (0) #define update_stats_shm_frag_split(...) \ do { \ update_stat(shm_used, -FRAG_OVERHEAD); \ update_stat(shm_rused, FRAG_OVERHEAD); \ update_stat(shm_frags, 1); \ } while (0) #endif /* HP_MALLOC_FAST_STATS */ #else /* STATISTICS */ #define stats_are_expired(...) 0 #define update_shm_stats(...) #define hp_init_shm_statistics(...) #define update_stats_pkg_frag_attach(blk, frag) #define update_stats_pkg_frag_detach(blk, frag) #define update_stats_pkg_frag_split(blk, ...) #define update_stats_pkg_frag_merge(blk, ...) #define update_stats_shm_frag_attach(frag) #define update_stats_shm_frag_detach(frag) #define update_stats_shm_frag_split(...) #endif #endif /* HP_MALLOC_STATS_H */ opensips-2.2.2/mem/mem.c000066400000000000000000000131521300170765700150650ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2003-04-08 init_mallocs split into init_{pkg,shm}_malloc (andrei) * */ #include #include #include "../config.h" #include "../dprint.h" #include "../globals.h" #include "mem.h" #include "shm_mem.h" #ifdef PKG_MALLOC char* mem_pool = NULL; #ifdef VQ_MALLOC struct vqm_block* mem_block; #elif defined F_MALLOC struct fm_block* mem_block; #elif defined HP_MALLOC struct hp_block* mem_block; #elif defined QM_MALLOC struct qm_block* mem_block; #else #error "no memory allocator selected" #endif #endif int init_pkg_mallocs(void) { #ifdef PKG_MALLOC /*init mem*/ mem_pool = malloc(pkg_mem_size); if (mem_pool==NULL){ LM_CRIT("could not initialize PKG memory: %ld\n", pkg_mem_size); return -1; } #ifdef VQ_MALLOC mem_block=vqm_malloc_init(mem_pool, pkg_mem_size, "pkg"); #elif F_MALLOC mem_block=fm_malloc_init(mem_pool, pkg_mem_size, "pkg"); #elif HP_MALLOC mem_block=hp_pkg_malloc_init(mem_pool, pkg_mem_size, "pkg"); #elif QM_MALLOC mem_block=qm_malloc_init(mem_pool, pkg_mem_size, "pkg"); #else #error "no memory allocator selected" #endif if (mem_block==0){ LM_CRIT("could not initialize memory pool\n"); fprintf(stderr, "Given PKG mem size is not enough: %ld\n", pkg_mem_size ); return -1; } #endif return 0; } #if defined(PKG_MALLOC) && defined(STATISTICS) void set_pkg_stats(pkg_status_holder *status) { status[0][PKG_TOTAL_SIZE_IDX] = MY_PKG_GET_SIZE(); status[0][PKG_USED_SIZE_IDX] = MY_PKG_GET_USED(); status[0][PKG_REAL_USED_SIZE_IDX] = MY_PKG_GET_RUSED(); status[0][PKG_MAX_USED_SIZE_IDX] = MY_PKG_GET_MUSED(); status[0][PKG_FREE_SIZE_IDX] = MY_PKG_GET_FREE(); status[0][PKG_FRAGMENTS_SIZE_IDX] = MY_PKG_GET_FRAGS(); } /* event interface information */ #include #include "../evi/evi_core.h" #include "../evi/evi_modules.h" /* events information */ long event_pkg_threshold = 0; // determines the last percentage triggered long event_pkg_last = 0; // determines if there is a pending event int event_pkg_pending = 0; static str pkg_usage_str = { "usage", 5 }; static str pkg_threshold_str = { "threshold", 9 }; static str pkg_used_str = { "used", 4 }; static str pkg_size_str = { "size", 4 }; static str pkg_pid_str = { "pid", 3 }; void pkg_event_raise(long used, long size, long perc) { evi_params_p list = 0; int pid; event_pkg_pending = 1; event_pkg_last = perc; // event has to be triggered - check for subscribers if (!evi_probe_event(EVI_PKG_THRESHOLD_ID)) { goto end; } if (!(list = evi_get_params())) goto end; if (evi_param_add_int(list, &pkg_usage_str, (int *)&perc)) { LM_ERR("unable to add usage parameter\n"); goto end; } if (evi_param_add_int(list, &pkg_threshold_str, (int *)&event_pkg_threshold)) { LM_ERR("unable to add threshold parameter\n"); goto end; } if (evi_param_add_int(list, &pkg_used_str, (int *)&used)) { LM_ERR("unable to add used parameter\n"); goto end; } if (evi_param_add_int(list, &pkg_size_str, (int *)&size)) { LM_ERR("unable to add size parameter\n"); goto end; } pid = getpid(); if (evi_param_add_int(list, &pkg_pid_str, (int *)&pid)) { LM_ERR("unable to add size parameter\n"); goto end; } if (evi_raise_event(EVI_PKG_THRESHOLD_ID, list)) { LM_ERR("unable to send pkg threshold event\n"); } list = 0; end: if (list) evi_free_params(list); event_pkg_pending = 0; } void pkg_threshold_check(void) { long pkg_perc, used, size; if (event_pkg_threshold == 0 || // threshold not used event_pkg_pending ) { // somebody else is raising the event // do not do anything return; } // compute the percentage used = MY_PKG_GET_RUSED(); size = MY_PKG_GET_SIZE(); pkg_perc = used * 100 / size; /* check if the event has to be raised or if it was already notified */ if ((pkg_perc < event_pkg_threshold && event_pkg_last <= event_pkg_threshold) || (pkg_perc >= event_pkg_threshold && event_pkg_last == pkg_perc)) return; pkg_event_raise(used, size, pkg_perc); } #endif int init_shm_mallocs(void) { if (shm_mem_init()<0) { LM_CRIT("could not initialize shared memory pool, exiting...\n"); fprintf(stderr, "Too much shared memory demanded: %ld\n", shm_mem_size ); return -1; } return 0; } #ifdef SYSTEM_MALLOC void * sys_malloc(size_t s, const char *file, const char *function, int line) { void *v; v = malloc(s); LM_DBG("%s:%s:%d: malloc %p size %lu end %p\n", file, function, line, v, (unsigned long)s, (char *)v + s); return v; } void * sys_realloc(void *p, size_t s, const char *file, const char *function, int line) { void *v; v = realloc(p, s); LM_DBG("%s:%s:%d: realloc old %p to %p size %lu end %p\n", file, function, line, p, v, (unsigned long)s, (char *)v + s); return v; } void sys_free(void *p, const char *file, const char *function, int line) { LM_DBG("%s:%s:%d: free %p\n", file, function, line, p); free(p); } #endif opensips-2.2.2/mem/mem.h000066400000000000000000000170371300170765700151000ustar00rootroot00000000000000/* * memory related stuff (malloc & friends) * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-10 __FUNCTION__ is a gcc-ism, defined it to "" for sun cc * (andrei) * 2003-03-07 split init_malloc into init_pkg_mallocs & init_shm_mallocs * (andrei) */ #ifndef mem_h #define mem_h #include #include "../config.h" #include "../dprint.h" #ifdef PKG_MALLOC #include "common.h" extern char *mem_pool; #ifdef STATISTICS #define PKG_TOTAL_SIZE_IDX 0 #define PKG_USED_SIZE_IDX 1 #define PKG_REAL_USED_SIZE_IDX 2 #define PKG_MAX_USED_SIZE_IDX 3 #define PKG_FREE_SIZE_IDX 4 #define PKG_FRAGMENTS_SIZE_IDX 5 typedef unsigned long pkg_status_holder[6]; void set_pkg_stats(pkg_status_holder*); #else #define set_pkg_stats( _x ) #define init_pkg_stats( _x ) 0 #endif # ifdef DBG_MALLOC #ifdef __SUNPRO_C #define __FUNCTION__ "" /* gcc specific */ #endif # ifdef VQ_MALLOC # define pkg_malloc(s) vqm_malloc(mem_block, (s),__FILE__, \ __FUNCTION__, __LINE__) # define pkg_free(p) vqm_free(mem_block, (p), __FILE__, \ __FUNCTION__, __LINE__) # warn "no proper realloc implementation, use another mem. alloc" # elif defined F_MALLOC # define pkg_malloc(s) fm_malloc(mem_block, (s),__FILE__, \ __FUNCTION__, __LINE__) # define pkg_free(p) fm_free(mem_block, (p), __FILE__, \ __FUNCTION__, __LINE__) # define pkg_realloc(p, s) fm_realloc(mem_block, (p), (s),__FILE__, \ __FUNCTION__, __LINE__) # define pkg_info(i) fm_info(mem_block,i) # elif defined HP_MALLOC # define pkg_malloc(s) hp_pkg_malloc(mem_block, (s),__FILE__, \ __FUNCTION__, __LINE__) # define pkg_free(p) hp_pkg_free(mem_block, (p), __FILE__, \ __FUNCTION__, __LINE__) # define pkg_realloc(p, s) hp_pkg_realloc(mem_block, (p), (s),__FILE__, \ __FUNCTION__, __LINE__) # define pkg_info(i) hp_info(mem_block,i) # elif defined QM_MALLOC # define pkg_malloc(s) qm_malloc(mem_block, (s),__FILE__, \ __FUNCTION__, __LINE__) # define pkg_realloc(p, s) qm_realloc(mem_block, (p), (s),__FILE__, \ __FUNCTION__, __LINE__) # define pkg_free(p) qm_free(mem_block, (p), __FILE__, \ __FUNCTION__, __LINE__) # define pkg_info(i) qm_info(mem_block,i) # else # error "no memory allocator selected" # endif # else # ifdef VQ_MALLOC # define pkg_malloc(s) vqm_malloc(mem_block, (s)) # define pkg_free(p) vqm_free(mem_block, (p)) # elif defined F_MALLOC # include "../error.h" # define pkg_malloc(s) fm_malloc(mem_block, (s)) # ifdef F_MALLOC_OPTIMIZATIONS # define pkg_realloc(p, s) fm_realloc(mem_block, (p), (s)) # define pkg_free(p) fm_free(mem_block, (p)) # else # define pkg_free(p) \ do { \ if (shm_block && \ ((void *)p >= (void *)shm_block->first_frag && \ (void *)p <= (void *)shm_block->last_frag)) { \ LM_BUG("pkg_free() on shm ptr %p - aborting!\n", p); \ abort(); \ } else if (p && ((void *)p < (void *)mem_block->first_frag || \ (void *)p > (void *)mem_block->last_frag)) { \ LM_BUG("pkg_free() on non-pkg ptr %p - aborting!\n", p); \ abort(); \ } \ fm_free(mem_block, (p)); \ } while (0) # define pkg_realloc(p, s) \ ({ \ if (shm_block && \ ((void *)p >= (void *)shm_block->first_frag && \ (void *)p <= (void *)shm_block->last_frag)) { \ LM_BUG("pkg_realloc(%lu) on shm ptr %p - aborting!\n", \ (unsigned long)s, p); \ abort(); \ } else if (p && ((void *)p < (void *)mem_block->first_frag || \ (void *)p > (void *)mem_block->last_frag)) { \ LM_BUG("pkg_realloc(%lu) on non-pkg ptr %p - abort!\n", \ (unsigned long)s, p); \ abort(); \ } \ fm_realloc(mem_block, (p), (s)); \ }) # endif # define pkg_info(i) fm_info(mem_block,i) # elif defined HP_MALLOC # define pkg_malloc(s) hp_pkg_malloc(mem_block, (s)) # define pkg_realloc(p, s) hp_pkg_realloc(mem_block, (p), (s)) # define pkg_free(p) hp_pkg_free(mem_block, (p)) # define pkg_info(i) hp_info(mem_block,i) # elif defined QM_MALLOC # define pkg_malloc(s) qm_malloc(mem_block, (s)) # define pkg_realloc(p, s) qm_realloc(mem_block, (p), (s)) # define pkg_free(p) qm_free(mem_block, (p)) # define pkg_info(i) qm_info(mem_block,i) # else # error "no memory allocator selected" # endif # endif # ifdef VQ_MALLOC # define pkg_status() vqm_status(mem_block) # elif defined F_MALLOC # define pkg_status() fm_status(mem_block) # define MY_PKG_GET_SIZE() fm_get_size(mem_block) # define MY_PKG_GET_USED() fm_get_used(mem_block) # define MY_PKG_GET_RUSED() fm_get_real_used(mem_block) # define MY_PKG_GET_MUSED() fm_get_max_real_used(mem_block) # define MY_PKG_GET_FREE() fm_get_free(mem_block) # define MY_PKG_GET_FRAGS() fm_get_frags(mem_block) # elif defined HP_MALLOC # define pkg_status() hp_status(mem_block) # define MY_PKG_GET_SIZE() hp_pkg_get_size(mem_block) # define MY_PKG_GET_USED() hp_pkg_get_used(mem_block) # define MY_PKG_GET_RUSED() hp_pkg_get_real_used(mem_block) # define MY_PKG_GET_MUSED() hp_pkg_get_max_real_used(mem_block) # define MY_PKG_GET_FREE() hp_pkg_get_free(mem_block) # define MY_PKG_GET_FRAGS() hp_pkg_get_frags(mem_block) # elif defined QM_MALLOC # define pkg_status() qm_status(mem_block) # define MY_PKG_GET_SIZE() qm_get_size(mem_block) # define MY_PKG_GET_USED() qm_get_used(mem_block) # define MY_PKG_GET_RUSED() qm_get_real_used(mem_block) # define MY_PKG_GET_MUSED() qm_get_max_real_used(mem_block) # define MY_PKG_GET_FREE() qm_get_free(mem_block) # define MY_PKG_GET_FRAGS() qm_get_frags(mem_block) # else # error "no memory allocator selected" # endif #elif defined(USE_SHM_MEM) # include "shm_mem.h" # define pkg_malloc(s) shm_malloc((s)) # define pkg_free(p) shm_free((p)) # define pkg_status() shm_status() # define MY_PKG_GET_SIZE() # define MY_PKG_GET_USED() # define MY_PKG_GET_RUSED() # define MY_PKG_GET_MUSED() # define MY_PKG_GET_FREE() # define MY_PKG_GET_FRAGS() #else # include void *sys_malloc(size_t, const char *, const char *, int); void *sys_realloc(void *, size_t, const char *, const char *, int); void sys_free(void *, const char *, const char *, int); # define SYSTEM_MALLOC # define pkg_malloc(s) sys_malloc((s), __FILE__, __FUNCTION__, __LINE__) # define pkg_realloc(ptr, s) sys_realloc((ptr), (s), __FILE__, __FUNCTION__, __LINE__) # define pkg_free(p) sys_free((p), __FILE__, __FUNCTION__, __LINE__) # define pkg_status() # define MY_PKG_GET_SIZE() # define MY_PKG_GET_USED() # define MY_PKG_GET_RUSED() # define MY_PKG_GET_MUSED() # define MY_PKG_GET_FREE() # define MY_PKG_GET_FRAGS() #endif int init_pkg_mallocs(); int init_shm_mallocs(); #endif opensips-2.2.2/mem/mem_dbg_hash.c000066400000000000000000000051011300170765700166770ustar00rootroot00000000000000/* * Copyright (C) 2015-2016 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2015-11-18 initial version (Vlad Patrascu) */ #include "mem_dbg_hash.h" #include #include void dbg_ht_init(mem_dbg_htable_t htable) { int i; for(i=0; i < DBG_HASH_SIZE; i++) htable[i] = NULL; } int dbg_ht_update(mem_dbg_htable_t htable, const char *file, const char *func, unsigned long line, unsigned long size) { unsigned int hash_code; struct mem_dbg_entry *entry, *new; hash_code = get_dbg_hash(file, func, line); entry = htable[hash_code]; if (!entry) { /* insert first entry in this bucket */ entry = malloc(sizeof(struct mem_dbg_entry)); if (!entry) return -1; entry->file = file; entry->func = func; entry->line = line; entry->size = size; entry->no_fragments = 1; entry->next = NULL; htable[hash_code] = entry; return 0; } else { /* find entry and update */ if (!strcmp(entry->file, file) && !strcmp(entry->func, func) && entry->line == line) { entry->size += size; entry->no_fragments++; return 0; } for(; entry->next; entry = entry->next) if (!strcmp(entry->next->file, file) && !strcmp(entry->next->func, func) && entry->next->line == line) { entry->next->size += size; entry->next->no_fragments++; break; } /* not found, insert new entry in this bucket */ if (!entry->next) { new = malloc(sizeof(struct mem_dbg_entry)); if (!new) return -1; new->file = file; new->func = func; new->line = line; new->size = size; new->no_fragments = 1; new->next = NULL; entry->next = new; } return 0; } } void dbg_ht_free(mem_dbg_htable_t htable) { struct mem_dbg_entry *it, *tmp; int i; for(i=0; i < DBG_HASH_SIZE; i++) { it = htable[i]; while (it) { tmp = it; it = it->next; free(tmp); } htable[i] = NULL; } }opensips-2.2.2/mem/mem_dbg_hash.h000066400000000000000000000034221300170765700167100ustar00rootroot00000000000000/* * Copyright (C) 2015-2016 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2015-11-18 initial version (Vlad Patrascu) */ #ifndef MEM_DBG_HASH_H #define MEM_DBG_HASH_H #include "../hash_func.h" #include #define DBG_HASH_SIZE 1500 struct mem_dbg_entry { const char *file; const char *func; unsigned long line; unsigned long size; /* total alloc'd size */ unsigned long no_fragments; struct mem_dbg_entry *next; }; typedef struct mem_dbg_entry* mem_dbg_htable_t[DBG_HASH_SIZE]; int dbg_ht_update(mem_dbg_htable_t htable, const char *file, const char *func, unsigned long line, unsigned long size); void dbg_ht_free(mem_dbg_htable_t htable); void dbg_ht_init(mem_dbg_htable_t htable); static inline unsigned int get_dbg_hash(const char *file, const char *func, unsigned long line) { str s1, s2; char buf[255]; s1.s = (char *)file; s1.len = strlen(file); s2.len = strlen(func); memcpy(buf, func, s2.len); buf[0] += line; buf[1] += line >> 4; s2.s = buf; return core_hash(&s1, &s2, DBG_HASH_SIZE); } #endifopensips-2.2.2/mem/meminfo.h000066400000000000000000000027541300170765700157540ustar00rootroot00000000000000/* * mem (malloc) info * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /* * History: * -------- * 2005-03-02 created (andrei) * 2006-01-05 renamed meminfo to mem_info due to name conflict on solaris */ #ifndef meminfo_h #define meminfo_h struct mem_info{ unsigned long total_size; unsigned long free; unsigned long used; unsigned long real_used; /*used + overhead*/ unsigned long max_used; unsigned long min_frag; unsigned long total_frags; /* total fragment no */ }; #if defined(PKG_MALLOC) && defined(STATISTICS) // threshold percentage checked extern long event_pkg_threshold; // events are used only if STATISTICS are used void pkg_threshold_check(void); #else #define pkg_threshold_check() #endif /* STATISTICS */ #endif opensips-2.2.2/mem/memtest.c000066400000000000000000000065461300170765700157760ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef DBG_MALLOC #include #include #include #include "../globals.h" #include "../config.h" #if 0 #ifdef PKG_MALLOC # ifdef VQ_MALLOC # include "vq_malloc.h" # define MY_MALLOC vqm_malloc # define MY_FREE vqm_free # define MY_INIT vqm_malloc_init # define MY_BLOCK vqm_block # define MY_STATUS vqm_status # else # include "q_malloc.h" # define MY_MALLOC qm_malloc # define MY_FREE qm_free # define MY_INIT qm_malloc_init # define MY_BLOCK qm_block # define MY_STATUS qm_status # endif #endif void memtest() { #define TEST_SIZE 1024*1024 #define TEST_RUN 1024 #define LONG_RUN 100000 #define ma(s) MY_MALLOC(mem_block, (s),__FILE__, __FUNCTION__, \ __LINE__); #define mf(p) MY_FREE(mem_block, (p), __FILE__, __FUNCTION__, \ __LINE__); char tst_mem[TEST_SIZE]; struct MY_BLOCK* mem_block; char *p0,*p1,*p2,*p3,*p4,*p5,*p6/*,*p7,*p8,*p9*/; int i, j, f; char *p[TEST_RUN]; int t; set_proc_log_level(7); log_stderr=1; printf("entering test\n"); mem_block=MY_INIT( tst_mem, TEST_SIZE ); /* coalescing test w/big fragments */ p0=ma(8194); p1=ma(8194); p2=ma(8194); MY_STATUS(mem_block); mf(p1); mf(p0); MY_STATUS(mem_block); mf(p2); MY_STATUS(mem_block); /* reuse test w/big fragments */ p0=ma(8194); p1=ma(4196); mf(p0); p0=ma(8190); MY_STATUS(mem_block); mf(p1); mf(p0); MY_STATUS(mem_block); exit(0); p0=ma(8); p1=ma(24); p2=ma(32); p3=ma(32); p4=ma(32); p5=ma(1024); p6=ma(2048); // MY_STATUS(mem_block); // *(p0+9)=0; mf(p0); mf(p2); mf(p5); mf(p6); // MY_STATUS(mem_block); mf(p1); mf(p4); mf(p3); // mf(p3); // MY_STATUS(mem_block); for (i=0;i f ) { f=GRANULARITY*j/LONG_RUN ; printf("%d%% done\n", f); } } printf("now I'm really done\n"); MY_STATUS(mem_block); printf("And I'm done with dumping final report too\n"); exit(0); } #endif #endif opensips-2.2.2/mem/module_info.c000066400000000000000000000046751300170765700166210ustar00rootroot00000000000000/* * Copyright (C) 2015-2016 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2015-10-01 initial version (Ionel Cerghit) */ #include #include #include "module_info.h" #include "../dprint.h" #include "shm_mem.h" char buff[60]; unsigned int mem_free_idx = 1; struct multi_str* mod_names = NULL; void* main_handle = NULL; volatile struct module_info* memory_mods_stats = NULL; int core_index; int set_mem_idx(char* mod_name, int mem_free_idx){ int *var; if(!main_handle){ main_handle = dlopen(NULL, RTLD_LAZY); if(!main_handle){ LM_CRIT("could not load main binary handle\n"); return -1; } } if(strlen(mod_name) == 4 && (strncmp("core", mod_name, 4) == 0)) core_index = mem_free_idx; strcpy(buff, mod_name); strcat(buff, STAT_SUFIX); var = (int*) dlsym(main_handle, buff); if(!var){ LM_CRIT("The module %s was not found, be sure it is the same as the NAME variable from the module's Makefile" "(without .so) and run 'make generate-mem-stat'\n", mod_name); return -1; } *var = mem_free_idx; LM_INFO("changed module variable %s = %d\n", buff, *var); return 0; } inline void update_module_stats(long mem_used, long real_used, int frags, int group_idx){ if(mem_free_idx == 1) return; #ifdef SHM_SHOW_DEFAULT_GROUP update_stat(memory_mods_stats[group_idx].fragments, frags); update_stat(memory_mods_stats[group_idx].memory_used, mem_used); update_stat(memory_mods_stats[group_idx].real_used, real_used); #else if(group_idx == 0) return; update_stat(memory_mods_stats[group_idx - 1].fragments, frags); update_stat(memory_mods_stats[group_idx - 1].memory_used, mem_used); update_stat(memory_mods_stats[group_idx - 1].real_used, real_used); #endif }opensips-2.2.2/mem/module_info.h000066400000000000000000000027721300170765700166220ustar00rootroot00000000000000/* * Copyright (C) 2015-2016 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2015-10-01 initial version (Ionel Cerghit) */ #ifndef _MODULE_INFO__ #define _MODULE_INFO__ #include "../statistics.h" #define STAT_SUFIX "_mem_stat" #define STAT_PREFIX "shmem_group_" #define STAT_PREFIX_LEN 12 extern struct multi_str* mod_names; extern unsigned int mem_free_idx; extern void* main_handle; extern volatile struct module_info* memory_mods_stats; extern int core_index; struct module_info{ stat_var* fragments; stat_var* memory_used; stat_var* real_used; }; struct multi_str{ char *s; struct multi_str* next; }; int set_mem_idx(char* mod_name, int mem_free_idx); void update_module_stats(long mem_used, long real_used, int frags, int group_idx); #endifopensips-2.2.2/mem/q_malloc.c000066400000000000000000000466351300170765700161120ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * History: * -------- * ????-??-?? created by andrei * 2003-04-14 more debugging added in DBG_QM_MALLOC mode (andrei) * 2003-06-29 added qm_realloc (andrei) * 2004-07-19 fragments book keeping code and support for 64 bits * memory blocks (64 bits machine & size>=2^32) (andrei) * GET_HASH s/ 4Gb mem., switched to long (andrei) * 2005-03-02 added qm_info() (andrei) * 2005-12-12 fixed realloc shrink real_used & used accounting; * fixed initial size (andrei) */ #if !(defined VQ_MALLOC) && !(defined F_MALLOC) && !defined(HP_MALLOC) && \ (defined QM_MALLOC) #include #include #include "q_malloc.h" #include "common.h" #include "../dprint.h" #include "../globals.h" #include "../statistics.h" #ifdef DBG_MALLOC #include "mem_dbg_hash.h" #endif /*useful macros*/ #define FRAG_END(f) \ ((struct qm_frag_end*)((char*)(f)+sizeof(struct qm_frag)+ \ (f)->size)) #define FRAG_NEXT(f) \ ((struct qm_frag*)((char*)(f)+sizeof(struct qm_frag)+(f)->size+ \ sizeof(struct qm_frag_end))) #define FRAG_PREV(f) \ ( (struct qm_frag*) ( ((char*)(f)-sizeof(struct qm_frag_end))- \ ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))->size- \ sizeof(struct qm_frag) ) ) #define PREV_FRAG_END(f) \ ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end))) #define ROUNDTO_MASK (~((unsigned long)ROUNDTO-1)) #define ROUNDUP(s) (((s)+(ROUNDTO-1))&ROUNDTO_MASK) #define ROUNDDOWN(s) ((s)&ROUNDTO_MASK) /* #define ROUNDUP(s) (((s)%ROUNDTO)?((s)+ROUNDTO)/ROUNDTO*ROUNDTO:(s)) #define ROUNDDOWN(s) (((s)%ROUNDTO)?((s)-ROUNDTO)/ROUNDTO*ROUNDTO:(s)) */ /* finds the hash value for s, s=ROUNDTO multiple*/ #define GET_HASH(s) ( ((unsigned long)(s)<=QM_MALLOC_OPTIMIZE)?\ (unsigned long)(s)/ROUNDTO: \ QM_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \ QM_MALLOC_OPTIMIZE_FACTOR+1 ) #define UN_HASH(h) ( ((unsigned long)(h)<=(QM_MALLOC_OPTIMIZE/ROUNDTO))?\ (unsigned long)(h)*ROUNDTO: \ 1UL<<((h)-QM_MALLOC_OPTIMIZE/ROUNDTO+\ QM_MALLOC_OPTIMIZE_FACTOR-1)\ ) /* mark/test used/unused frags */ #define FRAG_MARK_USED(f) #define FRAG_CLEAR_USED(f) #define FRAG_WAS_USED(f) (1) /* other frag related defines: * MEM_COALESCE_FRAGS * MEM_FRAG_AVOIDANCE */ #define MEM_FRAG_AVOIDANCE /* computes hash number for big buckets*/ inline static unsigned long big_hash_idx(unsigned long s) { int idx; /* s is rounded => s = k*2^n (ROUNDTO=2^n) * index= i such that 2^i > s >= 2^(i-1) * * => index = number of the first non null bit in s*/ idx=sizeof(long)*8-1; for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--); return idx; } #ifdef DBG_MALLOC #ifdef __CPU_x86_64 #define ST_CHECK_PATTERN 0xf0f0f0f0f0f0f0f0 #define END_CHECK_PATTERN1 0xc0c0c0c0c0c0c0c0 #define END_CHECK_PATTERN2 0xabcdefedabcdefed #else #warning "assuming sizeof(long) = 4" #define ST_CHECK_PATTERN 0xf0f0f0f0 #define END_CHECK_PATTERN1 0xc0c0c0c0 #define END_CHECK_PATTERN2 0xabcdefed #endif static void qm_debug_frag(struct qm_block* qm, struct qm_frag* f) { if (f->check!=ST_CHECK_PATTERN){ LM_CRIT("qm_*: fragm. %p (address %p) " "beginning overwritten(%lx)!\n", f, (char*)f+sizeof(struct qm_frag), f->check); qm_status(qm); abort(); }; if ((FRAG_END(f)->check1!=END_CHECK_PATTERN1)|| (FRAG_END(f)->check2!=END_CHECK_PATTERN2)){ LM_CRIT("qm_*: fragm. %p (address %p)" " end overwritten(%lx, %lx)!\n", f, (char*)f+sizeof(struct qm_frag), FRAG_END(f)->check1, FRAG_END(f)->check2); qm_status(qm); abort(); } if ((f>qm->first_frag)&& ((PREV_FRAG_END(f)->check1!=END_CHECK_PATTERN1) || (PREV_FRAG_END(f)->check2!=END_CHECK_PATTERN2) ) ){ LM_CRIT(" qm_*: prev. fragm. tail overwritten(%lx, %lx)[%p:%p] (%s, %s:%ld)!\n", PREV_FRAG_END(f)->check1, PREV_FRAG_END(f)->check2, f, (char*)f+sizeof(struct qm_frag), FRAG_PREV(f)->func, FRAG_PREV(f)->file,FRAG_PREV(f)->line); qm_status(qm); abort(); } } #endif unsigned long frag_size(void* p){ if(!p) return 0; return (((struct qm_frag*) ((char*)p-sizeof(struct qm_frag)))->size); } static inline void qm_insert_free(struct qm_block* qm, struct qm_frag* frag) { struct qm_frag* f; struct qm_frag* prev; int hash; hash=GET_HASH(frag->size); for(f=qm->free_hash[hash].head.u.nxt_free; f!=&(qm->free_hash[hash].head); f=f->u.nxt_free){ if (frag->size <= f->size) break; } /*insert it here*/ prev=FRAG_END(f)->prev_free; prev->u.nxt_free=frag; FRAG_END(frag)->prev_free=prev; frag->u.nxt_free=f; FRAG_END(f)->prev_free=frag; qm->free_hash[hash].no++; qm->real_used-=frag->size; #if defined(DBG_MALLOC) || defined(STATISTICS) qm->used-=frag->size; #endif } /* init malloc and return a qm_block*/ struct qm_block* qm_malloc_init(char* address, unsigned long size, char *name) { char* start; char* end; struct qm_block* qm; unsigned long init_overhead; int h; /* make address and size multiple of 8*/ start=(char*)ROUNDUP((unsigned long) address); LM_DBG("QM_OPTIMIZE=%lu, /ROUNDTO=%lu\n", QM_MALLOC_OPTIMIZE, QM_MALLOC_OPTIMIZE/ROUNDTO); LM_DBG("QM_HASH_SIZE=%lu, qm_block size=%lu\n", QM_HASH_SIZE, (long)sizeof(struct qm_block)); LM_DBG("params (%p, %lu), start=%p\n", address, size, start); if (sizename=name; qm->size=size; qm->used=size-init_overhead; qm->fragments = 0; qm->real_used=size; qm->max_real_used = 0; size-=init_overhead; qm->first_frag=(struct qm_frag*)(start+ROUNDUP(sizeof(struct qm_block))); qm->last_frag_end=(struct qm_frag_end*)(end-sizeof(struct qm_frag_end)); /* init initial fragment*/ qm->first_frag->size=size; qm->last_frag_end->size=size; #ifdef DBG_MALLOC qm->first_frag->check=ST_CHECK_PATTERN; qm->last_frag_end->check1=END_CHECK_PATTERN1; qm->last_frag_end->check2=END_CHECK_PATTERN2; #endif /* init free_hash* */ for (h=0; hfree_hash[h].head.u.nxt_free=&(qm->free_hash[h].head); qm->free_hash[h].tail.prev_free=&(qm->free_hash[h].head); qm->free_hash[h].head.size=0; qm->free_hash[h].tail.size=0; } /* link initial fragment into the free list*/ qm_insert_free(qm, qm->first_frag); /*qm->first_frag->u.nxt_free=&(qm->free_lst); qm->last_frag_end->prev_free=&(qm->free_lst); */ return qm; } static inline void qm_detach_free(struct qm_block* qm, struct qm_frag* frag) { struct qm_frag *prev; struct qm_frag *next; prev=FRAG_END(frag)->prev_free; next=frag->u.nxt_free; prev->u.nxt_free=next; FRAG_END(next)->prev_free=prev; qm->real_used+=frag->size; #if defined(DBG_MALLOC) || defined(STATISTICS) qm->used+=frag->size; #endif } #ifdef DBG_MALLOC static inline struct qm_frag* qm_find_free(struct qm_block* qm, unsigned long size, int *h, unsigned int *count) #else static inline struct qm_frag* qm_find_free(struct qm_block* qm, unsigned long size, int* h) #endif { int hash; struct qm_frag* f; for (hash=GET_HASH(size); hashfree_hash[hash].head.u.nxt_free; f!=&(qm->free_hash[hash].head); f=f->u.nxt_free){ #ifdef DBG_MALLOC *count+=1; /* *count++ generates a warning with gcc 2.9* -Wall */ #endif if (f->size>=size){ *h=hash; return f; } } /*try in a bigger bucket*/ } /* not found */ return 0; } /* returns 0 on success, -1 on error; * new_size < size & rounded-up already!*/ static inline #ifdef DBG_MALLOC int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size, const char* file, const char* func, unsigned int line) #else int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size) #endif { unsigned long rest; struct qm_frag* n; struct qm_frag_end* end; rest=f->size-new_size; #ifdef MEM_FRAG_AVOIDANCE if ((rest> (FRAG_OVERHEAD+QM_MALLOC_OPTIMIZE))|| (rest>=(FRAG_OVERHEAD+new_size))){/* the residue fragm. is big enough*/ #else if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){ #endif f->size=new_size; /*split the fragment*/ end=FRAG_END(f); end->size=new_size; n=(struct qm_frag*)((char*)end+sizeof(struct qm_frag_end)); n->size=rest-FRAG_OVERHEAD; FRAG_END(n)->size=n->size; FRAG_CLEAR_USED(n); /* never used */ #if defined(DBG_MALLOC) || defined(STATISTICS) qm->used-=FRAG_OVERHEAD; #endif #ifdef DBG_MALLOC end->check1=END_CHECK_PATTERN1; end->check2=END_CHECK_PATTERN2; /* frag created by malloc, mark it*/ n->file=file; n->func=func; n->line=line; n->check=ST_CHECK_PATTERN; #endif /* reinsert n in free list*/ qm_insert_free(qm, n); return 0; }else{ /* we cannot split this fragment any more */ return -1; } } #ifdef DBG_MALLOC void* qm_malloc(struct qm_block* qm, unsigned long size, const char* file, const char* func, unsigned int line) #else void* qm_malloc(struct qm_block* qm, unsigned long size) #endif { struct qm_frag* f; int hash; #ifdef DBG_MALLOC unsigned int list_cntr; list_cntr = 0; LM_GEN1(memlog, "%s_malloc (%lu), called from %s: %s(%d)\n", qm->name, size, file, func, line); #endif /*size must be a multiple of 8*/ size=ROUNDUP(size); if (size>(qm->size-qm->real_used)) { LM_ERR(oom_errorf, qm->name, qm->size - qm->real_used, qm->name[0] == 'p' ? "M" : "m"); pkg_threshold_check(); return 0; } /*search for a suitable free frag*/ #ifdef DBG_MALLOC if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){ #else if ((f=qm_find_free(qm, size, &hash))!=0){ #endif /* we found it!*/ /*detach it from the free list*/ #ifdef DBG_MALLOC qm_debug_frag(qm, f); #endif qm_detach_free(qm, f); /*mark it as "busy"*/ f->u.is_free=0; qm->free_hash[hash].no--; /* we ignore split return */ #ifdef DBG_MALLOC split_frag(qm, f, size, file, "fragm. from qm_malloc", line); #else split_frag(qm, f, size); #endif if (qm->max_real_usedreal_used) qm->max_real_used=qm->real_used; #ifdef DBG_MALLOC f->file=file; f->func=func; f->line=line; f->check=ST_CHECK_PATTERN; /* FRAG_END(f)->check1=END_CHECK_PATTERN1; FRAG_END(f)->check2=END_CHECK_PATTERN2;*/ LM_GEN1(memlog, "%s_malloc(%lu), returns address %p frag. %p " "(size=%lu) on %d -th hit\n", qm->name, size, (char*)f+sizeof(struct qm_frag), f, f->size, list_cntr ); #endif pkg_threshold_check(); qm->fragments += 1; return (char*)f+sizeof(struct qm_frag); } LM_ERR(oom_errorf, qm->name, qm->size - qm->real_used, qm->name[0] == 'p' ? "M" : "m"); pkg_threshold_check(); return 0; } #ifdef DBG_MALLOC void qm_free(struct qm_block* qm, void* p, const char* file, const char* func, unsigned int line) #else void qm_free(struct qm_block* qm, void* p) #endif { struct qm_frag* f; struct qm_frag* prev; struct qm_frag* next; unsigned long size; #ifdef DBG_MALLOC LM_GEN1(memlog, "%s_free(%p), called from %s: %s(%d)\n", qm->name, p, file, func, line); #endif if (p==0) { LM_WARN("free(0) called\n"); return; } #ifdef DBG_MALLOC if (p>(void*)qm->last_frag_end || p<(void*)qm->first_frag){ LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p); abort(); } #endif f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag)); #ifdef DBG_MALLOC qm_debug_frag(qm, f); if (f->u.is_free){ LM_CRIT("freeing already freed pointer," " first free: %s: %s(%ld) - aborting\n", f->file, f->func, f->line); abort(); } LM_GEN1( memlog, "freeing frag. %p alloc'ed from %s: %s(%ld)\n", f, f->file, f->func, f->line); #endif size=f->size; /* join packets if possible*/ prev=next=0; next=FRAG_NEXT(f); if (((char*)next < (char*)qm->last_frag_end) &&( next->u.is_free)){ /* join */ #ifdef DBG_MALLOC qm_debug_frag(qm, next); #endif qm_detach_free(qm, next); size+=next->size+FRAG_OVERHEAD; #if defined(DBG_MALLOC) || defined(STATISTICS) qm->used+=FRAG_OVERHEAD; #endif qm->free_hash[GET_HASH(next->size)].no--; /* FIXME slow */ } if (f > qm->first_frag){ prev=FRAG_PREV(f); /* (struct qm_frag*)((char*)f - (struct qm_frag_end*)((char*)f- sizeof(struct qm_frag_end))->size);*/ #ifdef DBG_MALLOC qm_debug_frag(qm, prev); #endif if (prev->u.is_free){ /*join*/ qm_detach_free(qm, prev); size+=prev->size+FRAG_OVERHEAD; #if defined(DBG_MALLOC) || defined(STATISTICS) qm->used+=FRAG_OVERHEAD; #endif qm->free_hash[GET_HASH(prev->size)].no--; /* FIXME slow */ f=prev; } } f->size=size; FRAG_END(f)->size=f->size; #ifdef DBG_MALLOC f->file=file; f->func=func; f->line=line; #endif qm_insert_free(qm, f); qm->fragments -= 1; pkg_threshold_check(); } #ifdef DBG_MALLOC void* qm_realloc(struct qm_block* qm, void* p, unsigned long size, const char* file, const char* func, unsigned int line) #else void* qm_realloc(struct qm_block* qm, void* p, unsigned long size) #endif { struct qm_frag* f; unsigned long diff; unsigned long orig_size; struct qm_frag* n; void* ptr; #ifdef DBG_MALLOC LM_GEN1(memlog, "%s_realloc(%p, %lu->%lu), called from %s: %s(%d)\n", qm->name, p, p ? ((struct qm_frag*)((char *)p - sizeof(struct qm_frag)))->size:0, size, file, func, line); if ((p)&&(p>(void*)qm->last_frag_end || p<(void*)qm->first_frag)){ LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p); abort(); } #endif if (size==0) { if (p) #ifdef DBG_MALLOC qm_free(qm, p, file, func, line); #else qm_free(qm, p); #endif pkg_threshold_check(); return 0; } if (p==0) #ifdef DBG_MALLOC return qm_malloc(qm, size, file, func, line); #else return qm_malloc(qm, size); #endif f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag)); #ifdef DBG_MALLOC qm_debug_frag(qm, f); LM_GEN1( memlog, "realloc'ing frag %p alloc'ed from %s: %s(%ld)\n", f, f->file, f->func, f->line); if (f->u.is_free){ LM_CRIT("trying to realloc an already freed " "pointer %p , fragment %p -- aborting\n", p, f); abort(); } #endif /* find first acceptable size */ size=ROUNDUP(size); if (f->size > size){ orig_size=f->size; /* shrink */ #ifdef DBG_MALLOC LM_GEN1(memlog,"shrinking from %lu to %lu\n", f->size, size); if(split_frag(qm, f, size, file, "fragm. from qm_realloc", line)!=0){ LM_GEN1(memlog,"shrinked successful\n"); } #else split_frag(qm, f, size); #endif }else if (f->size < size){ /* grow */ #ifdef DBG_MALLOC LM_GEN1( memlog, "growing from %lu to %lu\n", f->size, size); #endif orig_size=f->size; diff=size-f->size; n=FRAG_NEXT(f); if (((char*)n < (char*)qm->last_frag_end) && (n->u.is_free)&&((n->size+FRAG_OVERHEAD)>=diff)){ /* join */ qm_detach_free(qm, n); qm->free_hash[GET_HASH(n->size)].no--; /*FIXME: slow*/ f->size+=n->size+FRAG_OVERHEAD; #if defined(DBG_MALLOC) || defined(STATISTICS) qm->used+=FRAG_OVERHEAD; #endif FRAG_END(f)->size=f->size; /* end checks should be ok */ /* split it if necessary */ if (f->size > size ){ #ifdef DBG_MALLOC split_frag(qm, f, size, file, "fragm. from qm_realloc", line); #else split_frag(qm, f, size); #endif } }else{ /* could not join => realloc */ #ifdef DBG_MALLOC ptr=qm_malloc(qm, size, file, func, line); #else ptr=qm_malloc(qm, size); #endif if (ptr) { /* copy, need by libssl */ memcpy(ptr, p, orig_size); #ifdef DBG_MALLOC qm_free(qm, p, file, func, line); #else qm_free(qm, p); #endif } p=ptr; } }else{ /* do nothing */ #ifdef DBG_MALLOC LM_GEN1(memlog,"doing nothing, same size: %lu - %lu\n", f->size, size); #endif } #ifdef DBG_MALLOC LM_GEN1(memlog,"returning %p\n", p); #endif pkg_threshold_check(); return p; } void qm_status(struct qm_block* qm) { struct qm_frag* f; int i,j; int h; int unused; #ifdef DBG_MALLOC mem_dbg_htable_t allocd; struct mem_dbg_entry *it; #endif LM_GEN1(memdump, "qm_status (%p):\n", qm); if (!qm) return; #if defined(DBG_MALLOC) || defined(STATISTICS) LM_GEN1(memdump, " heap size= %lu\n", qm->size); LM_GEN1(memdump, " used= %lu, used+overhead=%lu, free=%lu\n", qm->used, qm->real_used, qm->size-qm->real_used); LM_GEN1(memdump, " max used (+overhead)= %lu\n", qm->max_real_used); #endif #ifdef DBG_MALLOC dbg_ht_init(allocd); for (f=qm->first_frag; (char*)f<(char*)qm->last_frag_end; f=FRAG_NEXT(f)) if (!f->u.is_free) if (dbg_ht_update(allocd, f->file, f->func, f->line, f->size) < 0) { LM_ERR("Unable to update alloc'ed. memory summary\n"); return; } LM_GEN1(memdump, " dumping summary of all alloc'ed. fragments:\n"); for(i=0; i < DBG_HASH_SIZE; i++) { it = allocd[i]; while (it) { LM_GEN1(memdump, " %10lu : %lu x [%s: %s, line %lu]\n", it->size, it->no_fragments, it->file, it->func, it->line); it = it->next; } } dbg_ht_free(allocd); #endif LM_GEN1(memdump, " dumping free list stats :\n"); for(h=0,i=0;hfree_hash[h].head.u.nxt_free,j=0; f!=&(qm->free_hash[h].head); f=f->u.nxt_free, i++, j++){ if (!FRAG_WAS_USED(f)){ unused++; #ifdef DBG_MALLOC LM_GEN1(memdump, "unused fragm.: hash = %3d, fragment %p," " address %p size %lu, created from %s: %s(%lu)\n", h, f, (char*)f+sizeof(struct qm_frag), f->size, f->file, f->func, f->line); #endif } } if (j) LM_GEN1(memdump, "hash= %3d. fragments no.: %5d, unused: %5d\n" "\t\t bucket size: %9lu - %9ld (first %9lu)\n", h, j, unused, UN_HASH(h), ((h<=QM_MALLOC_OPTIMIZE/ROUNDTO)?1:2)*UN_HASH(h), qm->free_hash[h].head.u.nxt_free->size ); if (j!=qm->free_hash[h].no){ LM_CRIT("different free frag. count: %d!=%lu" " for hash %3d\n", j, qm->free_hash[h].no, h); } } LM_GEN1(memdump, "-----------------------------\n"); } /* fills a malloc info structure with info about the block * if a parameter is not supported, it will be filled with 0 */ void qm_info(struct qm_block* qm, struct mem_info* info) { int r; long total_frags; total_frags=0; memset(info,0, sizeof(*info)); info->total_size=qm->size; info->min_frag=MIN_FRAG_SIZE; info->free=qm->size-qm->real_used; info->used=qm->used; info->real_used=qm->real_used; info->max_used=qm->max_real_used; for(r=0;rfree_hash[r].no; } info->total_frags=total_frags; } #ifdef DBG_MALLOC int qm_mem_check(struct qm_block *qm) { struct qm_frag *f; int i = 0; for (f = qm->first_frag; (char *)f < (char *)qm->last_frag_end; f = FRAG_NEXT(f), i++) { qm_debug_frag(qm, f); } LM_DBG("fragments: %d\n", i); return i; } #endif #endif opensips-2.2.2/mem/q_malloc.h000066400000000000000000000116551300170765700161110ustar00rootroot00000000000000/* * simple & fast malloc library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-05-21 on sparc64 roundto 8 even in debugging mode (so malloc'ed * long longs will be 64 bit aligned) (andrei) * 2004-07-19 support for 64 bit (2^64 mem. block) and more info * for the future de-fragmentation support (andrei) * 2004-11-10 support for > 4Gb mem. (switched to long) (andrei) */ #if !defined(q_malloc_h) && !defined(VQ_MALLOC) && !defined(F_MALLOC) && \ !defined(HP_MALLOC) #define q_malloc_h #include "meminfo.h" /* defs*/ #ifdef DBG_MALLOC #if defined(__CPU_sparc64) || defined(__CPU_sparc) /* tricky, on sun in 32 bits mode long long must be 64 bits aligned * but long can be 32 bits aligned => malloc should return long long * aligned memory */ #define ROUNDTO sizeof(long long) #else #define ROUNDTO sizeof(void*) /* minimum possible ROUNDTO ->heavy debugging*/ #endif #else /* DBG_MALLOC */ #define ROUNDTO 16UL /* size we round to, must be = 2^n and also sizeof(qm_frag)+sizeof(qm_frag_end) must be multiple of ROUNDTO! */ #endif #define MIN_FRAG_SIZE ROUNDTO #define QM_MALLOC_OPTIMIZE_FACTOR 14UL /*used below */ #define QM_MALLOC_OPTIMIZE ((unsigned long)(1UL<size; } static inline unsigned long qm_get_used(struct qm_block* qm) { return qm->used; } static inline unsigned long qm_get_free(struct qm_block* qm) { return qm->size-qm->real_used; } static inline unsigned long qm_get_real_used(struct qm_block* qm) { return qm->real_used; } static inline unsigned long qm_get_max_real_used(struct qm_block* qm) { return qm->max_real_used; } static inline unsigned long qm_get_frags(struct qm_block* qm) { return qm->fragments; } #endif /* STATISTICS */ #endif opensips-2.2.2/mem/shm_mem.c000066400000000000000000000327331300170765700157420ustar00rootroot00000000000000/* * Shared memory functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-12 split shm_mem_init in shm_getmem & shm_mem_init_mallocs * (andrei) * 2004-07-27 ANON mmap support, needed on darwin (andrei) * 2004-09-19 shm_mem_destroy: destroy first the lock & then unmap (andrei) */ #include #include "shm_mem.h" #include "../config.h" #include "../globals.h" #include "../mi/tree.h" #ifdef SHM_MMAP #include #include #include /*open*/ #include #include #endif #ifdef STATISTICS stat_export_t shm_stats[] = { {"total_size" , STAT_IS_FUNC, (stat_var**)shm_get_size }, #if defined(HP_MALLOC) && !defined(HP_MALLOC_FAST_STATS) {"used_size" , STAT_NO_RESET, &shm_used }, {"real_used_size" ,STAT_NO_RESET, &shm_rused }, #else {"used_size" , STAT_IS_FUNC, (stat_var**)shm_get_used }, {"real_used_size" , STAT_IS_FUNC, (stat_var**)shm_get_rused }, #endif {"max_used_size" , STAT_IS_FUNC, (stat_var**)shm_get_mused }, {"free_size" , STAT_IS_FUNC, (stat_var**)shm_get_free }, #if defined(HP_MALLOC) && !defined(HP_MALLOC_FAST_STATS) {"fragments" , STAT_NO_RESET, &shm_frags }, #else {"fragments" , STAT_IS_FUNC, (stat_var**)shm_get_frags }, #endif {0,0,0} }; #endif #ifndef SHM_MMAP static int shm_shmid=-1; /*shared memory id*/ #endif gen_lock_t *mem_lock = NULL; static void* shm_mempool=(void*)-1; #ifdef VQ_MALLOC struct vqm_block* shm_block; #elif F_MALLOC struct fm_block* shm_block; #elif HP_MALLOC struct hp_block* shm_block; #else struct qm_block* shm_block; #endif /* * - the memory fragmentation pattern of OpenSIPS * - holds the total number of shm_mallocs requested for each * different possible size since daemon startup * - allows memory warming (preserving the fragmentation pattern on restarts) */ unsigned long long *mem_hash_usage; #ifdef STATISTICS #include "../evi/evi_core.h" #include "../evi/evi_modules.h" /* events information */ long event_shm_threshold = 0; long *event_shm_last = 0; int *event_shm_pending = 0; static str shm_usage_str = { "usage", 5 }; static str shm_threshold_str = { "threshold", 9 }; static str shm_used_str = { "used", 4 }; static str shm_size_str = { "size", 4 }; void shm_event_raise(long used, long size, long perc) { evi_params_p list = 0; *event_shm_pending = 1; *event_shm_last = perc; // event has to be triggered - check for subscribers if (!evi_probe_event(EVI_SHM_THRESHOLD_ID)) { goto end; } if (!(list = evi_get_params())) goto end; if (evi_param_add_int(list, &shm_usage_str, (int *)&perc)) { LM_ERR("unable to add usage parameter\n"); goto end; } if (evi_param_add_int(list, &shm_threshold_str, (int *)&event_shm_threshold)) { LM_ERR("unable to add threshold parameter\n"); goto end; } if (evi_param_add_int(list, &shm_used_str, (int *)&used)) { LM_ERR("unable to add used parameter\n"); goto end; } if (evi_param_add_int(list, &shm_size_str, (int *)&size)) { LM_ERR("unable to add size parameter\n"); goto end; } /* * event has to be raised without the lock otherwise a deadlock will be * generated by the transport modules, or by the event_route processing */ #ifdef HP_MALLOC shm_unlock(0); #else shm_unlock(); #endif if (evi_raise_event(EVI_SHM_THRESHOLD_ID, list)) { LM_ERR("unable to send shm threshold event\n"); } #ifdef HP_MALLOC shm_lock(0); #else shm_lock(); #endif list = 0; end: if (list) evi_free_params(list); *event_shm_pending = 0; } #endif inline static void* sh_realloc(void* p, unsigned int size) { void *r; #ifndef HP_MALLOC shm_lock(); shm_free_unsafe(p); r = shm_malloc_unsafe(size); #else shm_free(p); r = shm_malloc(size); #endif shm_threshold_check(); #ifndef HP_MALLOC shm_unlock(); #endif return r; } /* look at a buffer if there is perhaps enough space for the new size (It is beneficial to do so because vq_malloc is pretty stateful and if we ask for a new buffer size, we can still make it happy with current buffer); if so, we return current buffer again; otherwise, we free it, allocate a new one and return it; no guarantee for buffer content; if allocation fails, we return NULL */ #ifdef DBG_MALLOC void* _shm_resize( void* p, unsigned int s, const char* file, const char* func, int line) #else void* _shm_resize( void* p , unsigned int s) #endif { #ifdef VQ_MALLOC struct vqm_frag *f; #endif if (p==0) { LM_DBG("resize(0) called\n"); return shm_malloc( s ); } # ifdef DBG_MALLOC # ifdef VQ_MALLOC f=(struct vqm_frag*) ((char*)p-sizeof(struct vqm_frag)); LM_DBG("params (%p, %d), called from %s: %s(%d)\n", p, s, file, func, line); VQM_DEBUG_FRAG(shm_block, f); if (p>(void *)shm_block->core_end || p<(void*)shm_block->init_core){ LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p); abort(); } #endif # endif return sh_realloc( p, s ); } int shm_getmem(void) { #ifdef SHM_MMAP int fd; #else struct shmid_ds shm_info; #endif #ifdef SHM_MMAP if (shm_mempool && (shm_mempool!=(void*)-1)){ #else if ((shm_shmid!=-1)||(shm_mempool!=(void*)-1)){ #endif LM_CRIT("shm already initialized\n"); return -1; } #ifdef SHM_MMAP #ifdef USE_ANON_MMAP shm_mempool=mmap(0, shm_mem_size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1 ,0); #else fd=open("/dev/zero", O_RDWR); if (fd==-1){ LM_CRIT("could not open /dev/zero: %s\n", strerror(errno)); return -1; } shm_mempool=mmap(0, shm_mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd ,0); /* close /dev/zero */ close(fd); #endif /* USE_ANON_MMAP */ #else shm_shmid=shmget(IPC_PRIVATE, /* SHM_MEM_SIZE */ shm_mem_size , 0700); if (shm_shmid==-1){ LM_CRIT("could not allocate shared memory segment: %s\n", strerror(errno)); return -1; } shm_mempool=shmat(shm_shmid, 0, 0); #endif if (shm_mempool==(void*)-1){ LM_CRIT("could not attach shared memory segment: %s\n", strerror(errno)); /* destroy segment*/ shm_mem_destroy(); return -1; } return 0; } int shm_mem_init_mallocs(void* mempool, unsigned long pool_size) { #ifdef HP_MALLOC int i; #endif /* init it for malloc*/ shm_block = shm_malloc_init(mempool, pool_size, "shm"); if (!shm_block){ LM_CRIT("could not initialize shared malloc\n"); shm_mem_destroy(); return -1; } #ifdef SHM_EXTRA_STATS int size_prealoc, j, one_full_entry, groups; char *start; if(mem_free_idx != 1){ #ifndef SHM_SHOW_DEFAULT_GROUP groups = mem_free_idx - 1; #else groups = mem_free_idx; #endif one_full_entry = 3 * (sizeof(stat_var) + sizeof(stat_val)); size_prealoc = groups * sizeof(struct module_info) + groups * one_full_entry; #ifndef DBG_MALLOC memory_mods_stats = MY_MALLOC_UNSAFE(shm_block, size_prealoc); #else memory_mods_stats = MY_MALLOC_UNSAFE(shm_block, size_prealoc, __FILE__, __FUNCTION__, __LINE__ ); #endif if(!memory_mods_stats){ LM_CRIT("could not alloc shared memory"); return -1; } memset( (void*)memory_mods_stats, 0, size_prealoc); start = (char*)memory_mods_stats + groups * sizeof(struct module_info); for(j = 0; j < groups; j++){ memory_mods_stats[j].fragments = (stat_var *)(start + j * one_full_entry); memory_mods_stats[j].memory_used = (stat_var *)(start + j * one_full_entry + sizeof(stat_var)); memory_mods_stats[j].real_used = (stat_var *)(start + j * one_full_entry + 2 * sizeof(stat_var)); memory_mods_stats[j].fragments->u.val = (stat_val*)(start + j * one_full_entry + 3 * sizeof(stat_var)); memory_mods_stats[j].memory_used->u.val = (stat_val*)(start + j * one_full_entry + 3 * sizeof(stat_var) + sizeof(stat_val)); memory_mods_stats[j].real_used->u.val = (stat_val*)(start + j * one_full_entry + 3 * sizeof(stat_var) + 2 * sizeof(stat_val)); } #ifndef SHM_SHOW_DEFAULT_GROUP if(core_index != 0){ update_stat(memory_mods_stats[core_index - 1].fragments, 1); update_stat(memory_mods_stats[core_index - 1].memory_used, size_prealoc); update_stat(memory_mods_stats[core_index - 1].real_used, size_prealoc + FRAG_OVERHEAD); } #else update_stat(memory_mods_stats[core_index].fragments, 1); update_stat(memory_mods_stats[core_index].memory_used, size_prealoc); update_stat(memory_mods_stats[core_index].real_used, size_prealoc + FRAG_OVERHEAD); #endif } #endif #ifdef HP_MALLOC /* lock_alloc cannot be used yet! */ mem_lock = shm_malloc_unsafe(HP_TOTAL_HASH_SIZE * sizeof *mem_lock); if (!mem_lock) { LM_CRIT("could not allocate the shm lock array\n"); shm_mem_destroy(); return -1; } for (i = 0; i < HP_TOTAL_HASH_SIZE; i++) if (!lock_init(&mem_lock[i])) { LM_CRIT("could not initialize lock\n"); shm_mem_destroy(); return -1; } mem_hash_usage = shm_malloc_unsafe(HP_TOTAL_HASH_SIZE * sizeof *mem_hash_usage); if (!mem_hash_usage) { LM_ERR("failed to allocate statistics array\n"); return -1; } memset(mem_hash_usage, 0, HP_TOTAL_HASH_SIZE * sizeof *mem_hash_usage); #else mem_lock = shm_malloc_unsafe(sizeof *mem_lock); if (!mem_lock) { LM_CRIT("could not allocate the shm lock\n"); shm_mem_destroy(); return -1; } if (!lock_init(mem_lock)) { LM_CRIT("could not initialize lock\n"); shm_mem_destroy(); return -1; } #endif #ifdef STATISTICS if (event_shm_threshold) { event_shm_last=shm_malloc_unsafe(sizeof(long)); if (event_shm_last==0){ LM_CRIT("could not allocate shm last event indicator\n"); shm_mem_destroy(); return -1; } *event_shm_last=0; event_shm_pending=shm_malloc_unsafe(sizeof(int)); if (event_shm_pending==0){ LM_CRIT("could not allocate shm pending flags\n"); shm_mem_destroy(); return -1; } *event_shm_pending=0; } #endif /* STATISTICS */ LM_DBG("success\n"); return 0; } int shm_mem_init(void) { int ret; LM_INFO("allocating SHM block\n"); ret = shm_getmem(); if (ret < 0) return ret; return shm_mem_init_mallocs(shm_mempool, shm_mem_size); } struct mi_root *mi_shm_check(struct mi_root *cmd, void *param) { #if defined(QM_MALLOC) && defined(DBG_MALLOC) struct mi_root *root; int ret; shm_lock(); ret = qm_mem_check(shm_block); shm_unlock(); /* any return means success; print the number of fragments now */ root = init_mi_tree(200, MI_SSTR(MI_OK)); if (!addf_mi_node_child(&root->node, 0, MI_SSTR("total_fragments"), "%d", ret)) { LM_ERR("failed to add MI node\n"); free_mi_tree(root); return NULL; } return root; #endif return NULL; } void init_shm_statistics(void) { #ifdef HP_MALLOC hp_init_shm_statistics(shm_block); #endif #ifdef SHM_EXTRA_STATS struct multi_str *mod_name; int i, len; char *full_name = NULL; if(mem_free_idx != 1){ #ifdef SHM_SHOW_DEFAULT_GROUP if (register_stat("shmem_group_default", "fragments", (stat_var **)&memory_mods_stats[0].fragments, STAT_NO_RESET|STAT_ONLY_REGISTER)!=0 ) { LM_CRIT("can't add stat variable"); return; } if (register_stat("shmem_group_default", "memory_used", (stat_var **)&memory_mods_stats[0].memory_used, STAT_NO_RESET|STAT_ONLY_REGISTER)!=0 ) { LM_CRIT("can't add stat variable"); return; } if (register_stat("shmem_group_default", "real_used", (stat_var **)&memory_mods_stats[0].real_used, STAT_NO_RESET|STAT_ONLY_REGISTER)!=0 ) { LM_CRIT("can't add stat variable"); return; } i = mem_free_idx - 1; #else i = mem_free_idx - 2; #endif for(mod_name = mod_names; mod_name != NULL; mod_name = mod_name->next){ len = strlen(mod_name->s); full_name = pkg_malloc((len + STAT_PREFIX_LEN + 1) * sizeof(char)); strcpy(full_name, STAT_PREFIX); strcat(full_name, mod_name->s); if (register_stat(full_name, "fragments", (stat_var **)&memory_mods_stats[i].fragments, STAT_NO_RESET|STAT_ONLY_REGISTER)!=0 ) { LM_CRIT("can't add stat variable"); return; } if (register_stat(full_name, "memory_used", (stat_var **)&memory_mods_stats[i].memory_used, STAT_NO_RESET|STAT_ONLY_REGISTER)!=0 ) { LM_CRIT("can't add stat variable"); return; } if (register_stat(full_name, "real_used", (stat_var **)&memory_mods_stats[i].real_used, STAT_NO_RESET|STAT_ONLY_REGISTER)!=0 ) { LM_CRIT("can't add stat variable"); return; } i--; } } #endif } void shm_mem_destroy(void) { #ifndef SHM_MMAP struct shmid_ds shm_info; #endif #ifdef HP_MALLOC update_mem_pattern_file(); #endif if (mem_lock){ LM_DBG("destroying the shared memory lock\n"); lock_destroy(mem_lock); /* we don't need to dealloc it*/ } #ifdef STATISTICS if (event_shm_threshold) { if (event_shm_last) shm_free(event_shm_last); if (event_shm_pending) shm_free(event_shm_pending); } #endif if (shm_mempool && (shm_mempool!=(void*)-1)) { #ifdef SHM_MMAP munmap(shm_mempool, /* SHM_MEM_SIZE */ shm_mem_size ); #else shmdt(shm_mempool); #endif shm_mempool=(void*)-1; } #ifndef SHM_MMAP if (shm_shmid!=-1) { shmctl(shm_shmid, IPC_RMID, &shm_info); shm_shmid=-1; } #endif } opensips-2.2.2/mem/shm_mem.h000066400000000000000000000327731300170765700157530ustar00rootroot00000000000000/* * shared mem stuff * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-06-29 added shm_realloc & replaced shm_resize (andrei) * 2003-11-19 reverted shm_resize to the old version, using * realloc causes terrible fragmentation (andrei) */ #include "../statistics.h" #include "../error.h" #ifndef shm_mem_h #define shm_mem_h #include #include #include #include #include #ifndef SHM_MMAP #include #endif #include #include #include #include "../dprint.h" #include "../lock_ops.h" /* we don't include locking.h on purpose */ #include "common.h" #ifdef SHM_EXTRA_STATS #include "module_info.h" #include "mem_stats.h" #endif #ifdef VQ_MALLOC # include "vq_malloc.h" # define MY_MALLOC vqm_malloc # define MY_FREE vqm_free # define MY_STATUS vqm_status # define shm_malloc_init vqm_malloc_init # warn "no proper vq_realloc implementation, try another memory allocator" #elif defined F_MALLOC # include "f_malloc.h" # define MY_MALLOC fm_malloc # define MY_FREE fm_free # define MY_REALLOC fm_realloc # define MY_STATUS fm_status # define MY_MEMINFO fm_info # ifdef STATISTICS # define MY_SHM_GET_SIZE fm_get_size # define MY_SHM_GET_USED fm_get_used # define MY_SHM_GET_RUSED fm_get_real_used # define MY_SHM_GET_MUSED fm_get_max_real_used # define MY_SHM_GET_FREE fm_get_free # define MY_SHM_GET_FRAGS fm_get_frags # endif # define shm_malloc_init fm_malloc_init # define MY_MALLOC_UNSAFE MY_MALLOC # define MY_FREE_UNSAFE MY_FREE # define MY_REALLOC_UNSAFE MY_REALLOC #elif defined HP_MALLOC # include "hp_malloc.h" # define MY_MALLOC hp_shm_malloc # define MY_MALLOC_UNSAFE hp_shm_malloc_unsafe # define MY_FREE hp_shm_free # define MY_FREE_UNSAFE hp_shm_free_unsafe # define MY_REALLOC hp_shm_realloc # define MY_REALLOC_UNSAFE hp_shm_realloc_unsafe # define MY_STATUS hp_status # define MY_MEMINFO hp_info # ifdef STATISTICS # define MY_SHM_GET_SIZE hp_shm_get_size # define MY_SHM_GET_USED hp_shm_get_used # define MY_SHM_GET_RUSED hp_shm_get_real_used # define MY_SHM_GET_MUSED hp_shm_get_max_real_used # define MY_SHM_GET_FREE hp_shm_get_free # define MY_SHM_GET_FRAGS hp_shm_get_frags # endif # define shm_malloc_init hp_shm_malloc_init # define shm_mem_warming hp_mem_warming # define update_mem_pattern_file hp_update_mem_pattern_file #elif defined QM_MALLOC # include "q_malloc.h" # define MY_MALLOC qm_malloc # define MY_FREE qm_free # define MY_REALLOC qm_realloc # define MY_STATUS qm_status # define MY_MEMINFO qm_info # define MY_MALLOC_UNSAFE MY_MALLOC # define MY_FREE_UNSAFE MY_FREE # define MY_REALLOC_UNSAFE MY_REALLOC # ifdef STATISTICS # define MY_SHM_GET_SIZE qm_get_size # define MY_SHM_GET_USED qm_get_used # define MY_SHM_GET_RUSED qm_get_real_used # define MY_SHM_GET_MUSED qm_get_max_real_used # define MY_SHM_GET_FREE qm_get_free # define MY_SHM_GET_FRAGS qm_get_frags # endif # define shm_malloc_init qm_malloc_init #else # error "no memory allocator selected" #endif extern gen_lock_t* mem_lock; int shm_mem_init(); /* calls shm_getmem & shm_mem_init_mallocs */ /* * should be called after the statistics engine is initialized * updates the atomic shm statistics with proper values */ void init_shm_statistics(void); int shm_getmem(); /* allocates the memory (mmap or sysv shmap) */ int shm_mem_init_mallocs(void* mempool, unsigned long size); /* initialize the mallocs & the lock */ void shm_mem_destroy(); #ifdef STATISTICS // threshold percentage checked extern long event_shm_threshold; // determines the last percentage triggered extern long *event_shm_last; // determines if there is a pending event extern int *event_shm_pending; // events are used only if SHM and STATISTICS are used void shm_event_raise(long used, long size, long perc); inline static void shm_threshold_check(void) { long shm_perc, used, size; if (event_shm_threshold == 0 || // threshold not used) !shm_block || !event_shm_last || !event_shm_pending || // shm not init *event_shm_pending ) { // somebody else is raising the event // do not do anything return; } // compute the percentage here to avoid a function call used = MY_SHM_GET_RUSED(shm_block); size = MY_SHM_GET_SIZE(shm_block); shm_perc = used * 100 / size; /* check if the event has to be raised or if it was already notified */ if ((shm_perc < event_shm_threshold && *event_shm_last <= event_shm_threshold) || (shm_perc >= event_shm_threshold && *event_shm_last == shm_perc)) return; shm_event_raise(used, size, shm_perc); } #else #define shm_threshold_check() #endif #ifndef HP_MALLOC #define shm_lock() lock_get(mem_lock) #define shm_unlock() lock_release(mem_lock) #else #define shm_lock(i) lock_get(&mem_lock[i]) #define shm_unlock(i) lock_release(&mem_lock[i]) #endif #ifdef SHM_EXTRA_STATS #define PASTER(_x, _y) _x ## _y #define VAR_STAT(_n) PASTER(_n, _mem_stat) #endif #ifdef DBG_MALLOC #ifdef __SUNPRO_C #define __FUNCTION__ "" /* gcc specific */ #endif inline static void* _shm_malloc_unsafe(unsigned int size, const char *file, const char *function, int line ) { void *p; p = MY_MALLOC_UNSAFE(shm_block, size, file, function, line); shm_threshold_check(); #ifdef SHM_EXTRA_STATS unsigned long size_f = frag_size(p); update_module_stats(size_f, size_f + FRAG_OVERHEAD, 1, VAR_STAT(MOD_NAME)); #endif return p; } inline static void* _shm_malloc(unsigned int size, const char *file, const char *function, int line ) { void *p; #ifndef HP_MALLOC shm_lock(); #endif p = MY_MALLOC(shm_block, size, file, function, line); shm_threshold_check(); #ifndef HP_MALLOC shm_unlock(); #endif #ifdef SHM_EXTRA_STATS unsigned long size_f = frag_size(p); update_module_stats(size_f, size_f + FRAG_OVERHEAD, 1, VAR_STAT(MOD_NAME)); #endif return p; } inline static void* _shm_realloc(void *ptr, unsigned int size, const char* file, const char* function, int line ) { void *p; #ifdef SHM_EXTRA_STATS long size_f = frag_size(ptr); #endif #ifndef HP_MALLOC shm_lock(); #endif p = MY_REALLOC(shm_block, ptr, size, file, function, line); shm_threshold_check(); #ifndef HP_MALLOC shm_unlock(); #endif #ifdef SHM_EXTRA_STATS size_f = (frag_size(p) - size_f); update_module_stats(size_f, size_f + (ptr ? 0 : FRAG_OVERHEAD), ptr ? 0 : 1 , VAR_STAT(MOD_NAME)); #endif return p; } inline static void* _shm_realloc_unsafe(void *ptr, unsigned int size, const char* file, const char* function, int line ) { void *p; #ifdef SHM_EXTRA_STATS long size_f = frag_size(ptr); #endif p = MY_REALLOC_UNSAFE(shm_block, ptr, size, file, function, line); shm_threshold_check(); #ifdef SHM_EXTRA_STATS size_f = (frag_size(p) - size_f); update_module_stats(size_f, size_f + (ptr ? 0 : FRAG_OVERHEAD), ptr ? 0 : 1 , VAR_STAT(MOD_NAME)); #endif return p; } #define shm_malloc( _size ) _shm_malloc((_size), \ __FILE__, __FUNCTION__, __LINE__ ) #define shm_malloc_unsafe(_size ) _shm_malloc_unsafe((_size), \ __FILE__, __FUNCTION__, __LINE__ ) #define shm_realloc( _ptr, _size ) _shm_realloc( (_ptr), (_size), \ __FILE__, __FUNCTION__, __LINE__ ) #define shm_realloc_unsafe( _ptr, _size ) _shm_realloc_unsafe( (_ptr), (_size), \ __FILE__, __FUNCTION__, __LINE__ ) #ifndef SHM_EXTRA_STATS #define shm_free_unsafe( _p ) \ do {\ MY_FREE_UNSAFE( shm_block, (_p), __FILE__, __FUNCTION__, __LINE__ ); \ shm_threshold_check(); \ } while(0) #ifdef HP_MALLOC #define shm_free( _p ) MY_FREE( shm_block, (_p), __FILE__, __FUNCTION__, __LINE__ ) #endif #else #define shm_free_unsafe( _p ) \ do {\ update_module_stats(-frag_size(_p), -(frag_size(_p) + FRAG_OVERHEAD), -1, VAR_STAT(MOD_NAME)); \ MY_FREE_UNSAFE( shm_block, (_p), __FILE__, __FUNCTION__, __LINE__ ); \ shm_threshold_check(); \ } while(0) #ifdef HP_MALLOC #define shm_free( _p ) \ do {\ update_module_stats(-frag_size(_p), -(frag_size(_p) + FRAG_OVERHEAD), -1, VAR_STAT(MOD_NAME)); \ MY_FREE( shm_block, (_p), __FILE__, __FUNCTION__, __LINE__ ); \ } while(0) #endif #endif #ifndef HP_MALLOC #define shm_free(_p) \ do { \ shm_lock(); \ shm_free_unsafe( (_p)); \ shm_unlock(); \ }while(0) #endif #ifndef HP_MALLOC extern unsigned long long *mem_hash_usage; #endif void* _shm_resize(void* ptr, unsigned int size, const char* f, const char* fn, int line); #define shm_resize(_p, _s ) _shm_resize((_p), (_s), \ __FILE__, __FUNCTION__, __LINE__ ) /*#define shm_resize(_p, _s ) shm_realloc( (_p), (_s))*/ #else /*DBG_MALLOC*/ inline static void* shm_malloc_unsafe(unsigned int size) { void *p; p = MY_MALLOC_UNSAFE(shm_block, size); shm_threshold_check(); #ifdef SHM_EXTRA_STATS unsigned long size_f = frag_size(p); update_module_stats(size_f, size_f + FRAG_OVERHEAD, 1, VAR_STAT(MOD_NAME)); #endif return p; } inline static void* shm_malloc(unsigned long size) { void *p; #ifndef HP_MALLOC shm_lock(); #endif p = MY_MALLOC(shm_block, size); shm_threshold_check(); #ifndef HP_MALLOC shm_unlock(); #endif #ifdef SHM_EXTRA_STATS unsigned long size_f = frag_size(p); update_module_stats(size_f, size_f + FRAG_OVERHEAD, 1, VAR_STAT(MOD_NAME)); #endif return p; } inline static void* shm_realloc(void *ptr, unsigned int size) { void *p; #ifndef HP_MALLOC shm_lock(); #if (defined F_MALLOC) && !(defined F_MALLOC_OPTIMIZATIONS) if (ptr >= (void *)mem_block->first_frag && ptr <= (void *)mem_block->last_frag) { LM_BUG("shm_realloc(%u) on pkg ptr %p - aborting!\n", size, ptr); abort(); } else if (ptr && (ptr < (void *)shm_block->first_frag || ptr > (void *)shm_block->last_frag)) { LM_BUG("shm_realloc(%u) on non-shm ptr %p - aborting!\n", size, ptr); abort(); } #endif #endif #ifdef SHM_EXTRA_STATS long size_f = frag_size(ptr); #endif p = MY_REALLOC(shm_block, ptr, size); shm_threshold_check(); #ifndef HP_MALLOC shm_unlock(); #endif #ifdef SHM_EXTRA_STATS size_f = (frag_size(p) - size_f); update_module_stats(size_f, size_f + (ptr ? 0 : FRAG_OVERHEAD), ptr ? 0 : 1 , VAR_STAT(MOD_NAME)); #endif return p; } inline static void* shm_realloc_unsafe(void *ptr, unsigned int size) { void *p; #ifdef SHM_EXTRA_STATS long size_f = frag_size(ptr); #endif p = MY_REALLOC_UNSAFE(shm_block, ptr, size); shm_threshold_check(); #ifdef SHM_EXTRA_STATS size_f = (frag_size(p) - size_f); update_module_stats(size_f, size_f + (ptr ? 0 : FRAG_OVERHEAD), ptr ? 0 : 1 , VAR_STAT(MOD_NAME)); #endif return p; } #ifndef SHM_EXTRA_STATS #define shm_free_unsafe( _p ) \ do { \ MY_FREE_UNSAFE(shm_block, (_p)); \ shm_threshold_check(); \ } while(0) #else #define shm_free_unsafe( _p ) \ do { \ update_module_stats(-frag_size(_p), -(frag_size(_p) + FRAG_OVERHEAD), -1, VAR_STAT(MOD_NAME)); \ MY_FREE_UNSAFE(shm_block, (_p)); \ shm_threshold_check(); \ } while(0) #endif /** * FIXME: tmp hacks --liviu */ inline static void shm_free(void *_p) { #ifndef HP_MALLOC shm_lock(); #if defined(F_MALLOC) && !defined(F_MALLOC_OPTIMIZATIONS) if (_p >= (void *)mem_block->first_frag && _p <= (void *)mem_block->last_frag) { LM_BUG("shm_free() on pkg ptr %p - aborting!\n", _p); abort(); } else if (_p && (_p < (void *)shm_block->first_frag || _p > (void *)shm_block->last_frag)) { LM_BUG("shm_free() on non-shm ptr %p - aborting!\n", _p); abort(); } #endif #endif #ifdef HP_MALLOC #ifdef SHM_EXTRA_STATS long size_f = frag_size(_p); update_module_stats(-size_f, -size_f - FRAG_OVERHEAD, -1, VAR_STAT(MOD_NAME)); #endif MY_FREE(shm_block, _p); #else shm_free_unsafe( (_p)); #endif #ifndef HP_MALLOC shm_unlock(); #endif } void* _shm_resize(void* ptr, unsigned int size); #define shm_resize(_p, _s) _shm_resize( (_p), (_s)) /*#define shm_resize(_p, _s) shm_realloc( (_p), (_s))*/ #endif inline static void shm_status(void) { #ifndef HP_MALLOC shm_lock(); #endif MY_STATUS(shm_block); #ifndef HP_MALLOC shm_unlock(); #endif } #define shm_info(mi) \ do{\ shm_lock(); \ MY_MEMINFO(shm_block, mi); \ shm_unlock(); \ }while(0) /* * performs a full shared memory pool scan for any corruptions or inconsistencies */ struct mi_root *mi_shm_check(struct mi_root *cmd, void *param); #ifdef STATISTICS extern stat_export_t shm_stats[]; inline static unsigned long shm_get_size(unsigned short foo) { return MY_SHM_GET_SIZE(shm_block); } inline static unsigned long shm_get_used(unsigned short foo) { return MY_SHM_GET_USED(shm_block); } inline static unsigned long shm_get_rused(unsigned short foo) { return MY_SHM_GET_RUSED(shm_block); } inline static unsigned long shm_get_mused(unsigned short foo) { return MY_SHM_GET_MUSED(shm_block); } inline static unsigned long shm_get_free(unsigned short foo) { return MY_SHM_GET_FREE(shm_block); } inline static unsigned long shm_get_frags(unsigned short foo) { return MY_SHM_GET_FRAGS(shm_block); } #endif /*STATISTICS*/ #endif opensips-2.2.2/mem/vq_malloc.c000066400000000000000000000327511300170765700162720ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * merged from Andrei's qmalloc and many fragments from Regents * University of California NetBSD malloc used; see * http://www.ajk.tele.fi/libc/stdlib/malloc.c.html#malloc for more * details including redistribution policy; this policy asks for * displaying the copyright: * Copyright (c) 1983 Regents of the University of California. * All rights reserved. * * * About: * aggressive, wasteful and very quick malloc library built for * servers that continuously allocate and release chunks of only * few sizes: * - free lists are organized by size (which eliminates long list traversal * thru short free chunks if a long one is asked) * - quite a few sizes are supported --> this results in more waste * (unused place in a chunk) however memory can be well reused after * freeing * - the last bucket holds unlikely, huge, variable length chunks; * they are maintained as stack growing from the opposite direction * of our heap; coalescing is enabled; stack-like first-fit used to * find free fragments * * TODO: possibly, some delayed coalescence wouldn't hurt; also, further * optimization will certainly speed-up the entire process a lot; using * knowledge of application as well as trying to make pipeline happy * (from which I am sadly far away now) * provides optimization space; trying to use other memory allocators * to compare would be a great thing to do too; * * also, comparing to other memory allocators (like Horde) would not * be a bad idea: those folks have been doing that for ages and specifically * Horde has been heavily optimized for multi-processor machines * * References: * - list of malloc implementations: * http://www.cs.colorado.edu/~zorn/Malloc.html * - a white-paper: http://g.oswego.edu/dl/html/malloc.html * - Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles: * ``Dynamic Storage Allocation: A Survey and Critical Review'' in * International Workshop on Memory Management, September 1995, * ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps * - ptmalloc: http://www.malloc.de/en/ * - GNU C-lib malloc: * http://www.gnu.org/manual/glibc-2.0.6/html_chapter/libc_3.html * - delorie malocs: http://www.delorie.com/djgpp/malloc/ * */ #ifdef VQ_MALLOC #include #include "../config.h" #include "../globals.h" #include "vq_malloc.h" #include "../dprint.h" #include "../globals.h" #define BIG_BUCKET(_qm) ((_qm)->max_small_bucket+1) #define IS_BIGBUCKET(_qm, _bucket) ((_bucket)==BIG_BUCKET(_qm)) #ifdef DBG_MALLOC #define ASSERT(a) \ my_assert(a, __LINE__, __FILE__, __FUNCTION__ ) #else #define ASSERT(a) #endif #ifdef DBG_MALLOC # define MORE_CORE(_q,_b,_s)\ (more_core( (_q), (_b), (_s), file, func, line )) #else # define MORE_CORE(_q,_b,_s) (more_core( (_q), (_b), (_s) )) #endif /* dimensioning buckets: define the step function constants for size2bucket */ int s2b_step[] = {8, 16, 32, 64, 128, 256, 512, 1024, 1536, 2048, 2560, MAX_FIXED_BLOCK, EO_STEP }; void my_assert( int assertation, int line, char *file, char *function ) { if (assertation) return; LM_CRIT("assertation failed in %s (%s:%d)\n", function, file, line); abort(); } #ifdef DBG_MALLOC void vqm_debug_frag(struct vqm_block* qm, struct vqm_frag* f) { if (f->check!=ST_CHECK_PATTERN){ LM_CRIT("vqm_*: fragm. %p beginning overwritten(%x)!\n", f, f->check); vqm_status(qm); abort(); }; if (memcmp(f->end_check, END_CHECK_PATTERN, END_CHECK_PATTERN_LEN)!=0) { LM_CRIT("vqm_*: fragm. %p end overwritten(%.*s)!\n", f, END_CHECK_PATTERN_LEN, f->end_check ); vqm_status(qm); abort(); } } #endif /* takes demanded size without overhead as input, returns bucket number and changed the demanded size to size really used including all possible overhead */ unsigned char size2bucket( struct vqm_block* qm, int *size ) { unsigned char b; unsigned int real_size; unsigned int exceeds; real_size = *size+ VQM_OVERHEAD; #ifdef DBG_MALLOC real_size+=END_CHECK_PATTERN_LEN; #endif real_size+=((exceeds = (real_size % 8 )) ? 8 - exceeds : 0); ASSERT( !(real_size%8) ); /* "small" chunk sizes <=1k translated using a table */ if ( real_size < MAX_FIXED_BLOCK ) { b = qm->s2b[ real_size ]; *size = qm->s2s[ real_size ]; /* there might be various allocations slightly 1>1k, I still don't want to be too aggressive and increase useless allocations in small steps */ } else { b = BIG_BUCKET(qm); *size = MAX_FIXED_BLOCK + (real_size-MAX_FIXED_BLOCK+BLOCK_STEP) / BLOCK_STEP * BLOCK_STEP; } /*size must be a multiple of 8*/ ASSERT( !(*size%8) ); return b; } /* init malloc and return a qm_block */ struct vqm_block* vqm_malloc_init(char* address, unsigned int size) { char* start; struct vqm_block* qm; unsigned int init_overhead; unsigned char b; /* bucket iterator */ unsigned int s; /* size iterator */ char *end; /* make address and size multiple of 8*/ start=(char*)( ((unsigned int)address%8)?((unsigned int)address+8)/8*8: (unsigned int)address); if (sizes2b_step[b]) b++; if (b>MAX_BUCKET) { LM_CRIT("attempt to install too many buckets, s2b_step >" "MAX_BUCKET\n"); return 0; } qm->s2b[s] = b; qm->s2s[s] = s2b_step[b]; } qm->max_small_bucket = b; /* from where we will draw memory */ qm->core = (char *) ( start + sizeof( struct vqm_block ) ); qm->free_core = size; /* remember for bound checking */ qm->init_core = qm->core; qm->core_end = end; /* allocate big chunks from end */ qm->big_chunks = end; return qm; } struct vqm_frag *more_core( struct vqm_block* qm, unsigned char bucket, unsigned int size #ifdef DBG_MALLOC , char *file, char *func, unsigned int line #endif ) { struct vqm_frag *new_chunk; struct vqm_frag_end *end; if (qm->free_corebig_chunks-=size; new_chunk = (struct vqm_frag *) qm->big_chunks ; } else { new_chunk = (struct vqm_frag *) qm->core; qm->core+=size; } qm->free_core-=size; /* initialize the new fragment */ new_chunk->u.inuse.bucket = bucket; new_chunk->size = size; end=FRAG_END( new_chunk ); end->size=size; return new_chunk; } static inline void vqm_detach_free( struct vqm_block* qm, struct vqm_frag* frag) { struct vqm_frag *prev, *next; prev=FRAG_END(frag)->prv_free; next=frag->u.nxt_free; if (prev) prev->u.nxt_free=next; else qm->next_free[BIG_BUCKET(qm)]=next; if (next) FRAG_END(next)->prv_free=prev; } #ifdef DBG_MALLOC void* vqm_malloc(struct vqm_block* qm, unsigned int size, char* file, char* func, unsigned int line) #else void* vqm_malloc(struct vqm_block* qm, unsigned int size) #endif { struct vqm_frag *new_chunk, *f; unsigned char bucket; #ifdef DBG_MALLOC unsigned int demanded_size; LM_GEN1( memlog, "params (%p, %d) called from %s: %s(%d)\n", qm, size, file, func, line); demanded_size = size; #endif new_chunk=0; /* what's the bucket? what's the total size incl. overhead? */ bucket = size2bucket( qm, &size ); if (IS_BIGBUCKET(qm, bucket)) { /* the kilo-bucket uses first-fit */ #ifdef DBG_MALLOC LM_GEN1( memlog, "processing a big fragment\n"); #endif for (f=qm->next_free[bucket] ; f; f=f->u.nxt_free ) if (f->size>=size) { /* first-fit */ new_chunk=f; VQM_DEBUG_FRAG(qm, f); vqm_detach_free(qm,f); break; } } else if ( (new_chunk=qm->next_free[ bucket ]) ) { /*fixed size bucket*/ VQM_DEBUG_FRAG(qm, new_chunk); /*detach it from the head of bucket's free list*/ qm->next_free[ bucket ] = new_chunk->u.nxt_free; } if (!new_chunk) { /* no chunk can be reused; slice one from the core */ new_chunk=MORE_CORE( qm, bucket, size ); if (!new_chunk) { #ifdef DBG_MALLOC LM_GEN1(memlog, "params (%p, %d) called from %s: %s(%d)\n", qm, size, file, func, line); #else LM_DBG("params (%p, %d) called from %s: %s(%d)\n", qm, size); #endif pkg_threshold_check(); return 0; } } new_chunk->u.inuse.magic = FR_USED; new_chunk->u.inuse.bucket=bucket; #ifdef DBG_MALLOC new_chunk->file=file; new_chunk->func=func; new_chunk->line=line; new_chunk->demanded_size=demanded_size; qm->usage[ bucket ]++; LM_GEN1( memlog,"params ( %p, %d ) returns address %p in bucket %d, " "real-size %d\n", qm, demanded_size, (char*)new_chunk+sizeof(struct vqm_frag), bucket, size ); new_chunk->end_check=(char*)new_chunk+ sizeof(struct vqm_frag)+demanded_size; memcpy( new_chunk->end_check, END_CHECK_PATTERN, END_CHECK_PATTERN_LEN ); new_chunk->check=ST_CHECK_PATTERN; #endif pkg_threshold_check(); return (char*)new_chunk+sizeof(struct vqm_frag); } #ifdef DBG_MALLOC void vqm_free(struct vqm_block* qm, void* p, char* file, char* func, unsigned int line) #else void vqm_free(struct vqm_block* qm, void* p) #endif { struct vqm_frag *f, *next, *prev, *first_big; unsigned char b; #ifdef DBG_MALLOC LM_GEN1(memlog,"params (%p, %p), called from %s: %s(%d)\n", qm, p, file, func, line); if (p>(void *)qm->core_end || p<(void*)qm->init_core){ LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p); abort(); } #endif if (p==0) { LM_WARN("free(0) called\n"); return; } f=(struct vqm_frag*) ((char*)p-sizeof(struct vqm_frag)); b=f->u.inuse.bucket; #ifdef DBG_MALLOC VQM_DEBUG_FRAG(qm, f); if ( ! FRAG_ISUSED(f) ) { LM_CRIT("freeing already freed pointer, first freed: %s: %s(%d) " "- aborting\n", f->file, f->func, f->line); abort(); } if ( b>MAX_BUCKET ) { LM_CRIT("fragment with too high bucket nr: " "%d, allocated: %s: %s(%d) - aborting\n", b, f->file, f->func, f->line); abort(); } LM_GEN1(memlog,"freeing %d bucket block alloc'ed from %s: %s(%d)\n", f->u.inuse.bucket, f->file, f->func, f->line); f->file=file; f->func=func; f->line=line; qm->usage[ f->u.inuse.bucket ]--; #endif if (IS_BIGBUCKET(qm,b)) { next=FRAG_NEXT(f); if ((char *)next +sizeof( struct vqm_frag) < qm->core_end) { VQM_DEBUG_FRAG(qm, next); if (! FRAG_ISUSED(next)) { /* coalesce with next fragment */ LM_DBG("coalesced with next\n"); vqm_detach_free(qm, next); f->size+=next->size; FRAG_END(f)->size=f->size; } } first_big = qm->next_free[b]; if (first_big && f>first_big) { prev=FRAG_PREV(f); VQM_DEBUG_FRAG(qm, prev); if (!FRAG_ISUSED(prev)) { /* coalesce with prev fragment */ LM_DBG("coalesced with prev\n"); vqm_detach_free(qm, prev ); prev->size+=f->size; f=prev; FRAG_END(f)->size=f->size; } } if ((char *)f==qm->big_chunks) { /* release unused core */ LM_DBG("big chunk released\n"); qm->free_core+=f->size; qm->big_chunks+=f->size; pkg_threshold_check(); return; } first_big = qm->next_free[b]; /* fix reverse link (used only for BIG_BUCKET */ if (first_big) FRAG_END(first_big)->prv_free=f; FRAG_END(f)->prv_free=0; } else first_big = qm->next_free[b]; f->u.nxt_free = first_big; /* also clobbers magic */ qm->next_free[b] = f; pkg_threshold_check(); } void dump_frag( struct vqm_frag* f, int i ) { LM_GEN1(memdump, " %3d. address=%p real size=%d bucket=%d\n", i, (char*)f+sizeof(struct vqm_frag), f->size, f->u.inuse.bucket); #ifdef DBG_MALLOC LM_GEN1(memdump, " demanded size=%d\n", f->demanded_size ); LM_GEN1(memdump, " alloc'd from %s: %s(%d)\n", f->file, f->func, f->line); LM_GEN1(memdump, " start check=%x, end check= %.*s\n", f->check, END_CHECK_PATTERN_LEN, f->end_check ); #endif } void vqm_status(struct vqm_block* qm) { struct vqm_frag* f; unsigned int i,on_list; LM_GEN1(memdump, "vqm_status (%p):\n", qm); if (!qm) return; LM_GEN1(memdump, " heap size= %d, available: %d\n", qm->core_end-qm->init_core, qm->free_core ); LM_GEN1(memdump, "dumping unfreed fragments:\n"); for (f=(struct vqm_frag*)qm->init_core, i=0;(char*)f<(char*)qm->core; f=FRAG_NEXT(f) ,i++) if ( FRAG_ISUSED(f) ) dump_frag(f, i); LM_GEN1(memdump, "dumping unfreed big fragments:\n"); for (f=(struct vqm_frag*)qm->big_chunks,i=0;(char*)f<(char*)qm->core_end; f=FRAG_NEXT(f) ,i++) if ( FRAG_ISUSED(f) ) dump_frag( f, i ); #ifdef DBG_MALLOC LM_GEN1(memdump,"dumping bucket statistics:\n"); for (i=0; i<=BIG_BUCKET(qm); i++) { for(on_list=0, f=qm->next_free[i]; f; f=f->u.nxt_free ) on_list++; LM_GEN1(memdump," %3d. bucket: in use: %ld, on free list: %d\n", i, qm->usage[i], on_list ); } #endif LM_GEN1(memdump, "-----------------------------\n"); } #endif opensips-2.2.2/mem/vq_malloc.h000066400000000000000000000101151300170765700162650ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(VQ_MALLOC_H) && defined(VQ_MALLOC) #define VQ_MALLOC_H #include "../config.h" #include "meminfo.h" /* indicates this fragment is not in use (must not be offset of valid aligned fragment beginning */ #define FR_USED 0xef /*useful macros*/ #define FRAG_END(f) \ ((struct vqm_frag_end*)((char*)(f)-sizeof(struct vqm_frag_end)+ \ (f)->size)) #define FRAG_NEXT(f) \ ((struct vqm_frag*)((char*)(f)+(f)->size)) #define PREV_FRAG_END(f) \ ((struct vqm_frag_end*)((char*)(f)-sizeof(struct vqm_frag_end))) #define FRAG_PREV(f) \ ( (struct vqm_frag*) ( (char*)(f) - PREV_FRAG_END(f)->size )) #define FRAG_ISUSED(f) \ ((f)->u.inuse.magic==FR_USED) /* just a bumper for the step function */ #define EO_STEP -1 #ifdef DBG_MALLOC # define ST_CHECK_PATTERN 0xf0f0f0f0 # define END_CHECK_PATTERN "sExP" # define END_CHECK_PATTERN_LEN 4 # define VQM_OVERHEAD (sizeof(struct vqm_frag)+ \ sizeof(struct vqm_frag_end)+END_CHECK_PATTERN_LEN) # define VQM_DEBUG_FRAG(qm, f) vqm_debug_frag( (qm), (f)) #else # define VQM_DEBUG_FRAG(qm, f) # define VQM_OVERHEAD (sizeof(struct vqm_frag)+ sizeof(struct vqm_frag_end)) #endif struct vqm_frag { /* XXX */ /* total chunk size including all overhead/bellowfoot/roundings/etc */ /* useless as otherwise size implied by bucket (if I really want to save bytes, I'll remove it from here */ unsigned long size; union{ /* pointer to next chunk in a bucket if free */ struct vqm_frag* nxt_free; struct { /* or bucket number if busy */ unsigned char magic; unsigned char bucket; } inuse; } u; #ifdef DBG_MALLOC /* source code info */ char* file; char* func; unsigned long line; /* your safety is important to us! safety signatures */ unsigned long check; char *end_check; /* the size user was originally asking for */ unsigned long demanded_size; #endif }; struct vqm_frag_end{ /* total chunk size including all overhead/bellowfoot/roundings/etc */ unsigned long size; /* XXX */ /* used only for variable-size chunks; might have different data structures for variable/fixed length chunks */ struct vqm_frag* prv_free; }; struct vqm_block{ /* size to bucket table */ unsigned char s2b[ MAX_FIXED_BLOCK ]; /* size to rounded size */ unsigned short s2s[ MAX_FIXED_BLOCK ]; unsigned char max_small_bucket; /* core gained on init ... */ char *core, *init_core, *core_end; /* ... and its available net amount; note that there's lot of free memory in buckets too -- this just tells about memory which has not been assigned to chunks */ unsigned long free_core; /* we allocate huge chunks from the end on; this is the pointer to big chunks */ char *big_chunks; struct vqm_frag* next_free[ MAX_BUCKET +1]; #ifdef DBG_MALLOC unsigned long usage[ MAX_BUCKET +1]; #endif }; struct vqm_block* vqm_malloc_init(char* address, unsigned int size); #ifdef DBG_MALLOC void vqm_debug_frag(struct vqm_block* qm, struct vqm_frag* f); void* vqm_malloc(struct vqm_block*, unsigned int size, char* file, char* func, unsigned int line); void vqm_free(struct vqm_block*, void* p, char* file, char* func, unsigned int line); #else void* vqm_malloc(struct vqm_block*, unsigned int size); void vqm_free(struct vqm_block*, void* p); #endif void vqm_status(struct vqm_block*); #endif opensips-2.2.2/menuconfig/000077500000000000000000000000001300170765700155155ustar00rootroot00000000000000opensips-2.2.2/menuconfig/Makefile000066400000000000000000000011071300170765700171540ustar00rootroot00000000000000MENUCONFIG_FILES= cfg.o curses.o items.o commands.o menus.o parser.o main.o MENUCONFIG_CFG_PATH?=menuconfig/configs/ MENUCONFIG_GEN_PATH?=etc/ MENUCONFIG_HAVE_SOURCES?=1 CFLAGS+=-g -Wall -DMENUCONFIG_CFG_PATH=\"$(MENUCONFIG_CFG_PATH)\" \ -DMENUCONFIG_GEN_PATH=\"$(MENUCONFIG_GEN_PATH)\" \ -DMENUCONFIG_HAVE_SOURCES=$(MENUCONFIG_HAVE_SOURCES) $(CC_EXTRA_OPTS) MY_LDFLAGS=-lncurses $(LD_EXTRA_OPTS) $(LDFLAGS) EXEC_NAME=configure all: $(MENUCONFIG_FILES) $(CC) -o $(EXEC_NAME) $(CFLAGS) $^ $(MY_LDFLAGS) .PHONY: proper proper: -rm -f $(EXEC_NAME) -rm -f $(MENUCONFIG_FILES) opensips-2.2.2/menuconfig/cfg.c000066400000000000000000000030331300170765700164170ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #include "string.h" #include "cfg.h" #include "main.h" /* Configs that will be used to generate the Menus * Add more CFG entries here */ cfg_gen_t configs[] = { {CONF_RESIDENTIAL_SCRIPT,"residential","opensips_residential_def.m4","opensips_residential.m4"}, {CONF_TRUNKING_SCRIPT,"trunking","opensips_trunking_def.m4","opensips_trunking.m4"}, {CONF_LB_SCRIPT,"loadbalancer","opensips_loadbalancer_def.m4","opensips_loadbalancer.m4"}, {0,0,0,0} }; /* Finds a cfg entry with the specified name * Returns NULL on failure */ cfg_gen_t* find_cfg_entry(char *name) { cfg_gen_t *it; for (it=configs;it->name;it++) { if (strcmp(name,it->name) == 0) return it; } return NULL; } opensips-2.2.2/menuconfig/cfg.h000066400000000000000000000025551300170765700164340ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #ifndef _cfg_script_h_ #define _cfg_script_h_ #define MACRO_INTERPRETER "m4" typedef struct cfg_generation { char *name; /* Name of the cfg entry. Used for naming actual menu entries */ char *output_name; /* Less user friendly cfg name. Used for creating output cfg file */ char *defs_m4; /* Path to the m4 file that contains the defs */ char *cfg_m4; /* Path to the m4 file that contains the actual OpenSIPS script */ } cfg_gen_t; extern cfg_gen_t configs[]; cfg_gen_t* find_cfg_entry(char *name); #endif opensips-2.2.2/menuconfig/commands.c000066400000000000000000000305251300170765700174670ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #define MENUCONFIG_CFG_PATH_LEN strlen(MENUCONFIG_CFG_PATH) /* Will globally save everything the user has altered */ int save_all_changes(select_menu *menu,void *arg) { static char name_buf[128]; select_menu *current; cfg_gen_t *it; #if MENUCONFIG_HAVE_SOURCES > 0 /* Take care of compile related options */ if (dump_make_conf(menu,arg) < 0) fprintf(output,"Failed to save all compile related options\n"); #else if (run_locally && dump_make_conf(menu,arg) < 0) fprintf(output,"Failed to save all compile related options\n"); #endif /* Save changes to all types of configs */ for (it=configs;it->name;it++) { strcpy(name_buf,"Save "); strcat(name_buf,it->name); current=find_menu(name_buf,main_menu); if (save_m4_def(current,NULL) < 0) fprintf(output,"Failed to save cfg %s\n",it->name); } return 0; } /* Resets all unsaved compile related options */ int reset_unsaved_compile(select_menu *menu,void *arg) { select_menu *current; select_item *it; current=find_menu(CONF_EXCLUDED_MODS,main_menu); for (it=current->item_list;it;it=it->next) it->enabled=it->prev_state; current->child_changed=CHILD_NO_CHANGES; current=find_menu(CONF_COMPILE_FLAGS,main_menu); for (it=current->item_list;it;it=it->next) { if (it->group_idx && it->prev_state != it->enabled) it->group_idx = -it->group_idx; it->enabled=it->prev_state; } current->child_changed=CHILD_NO_CHANGES; current=find_menu(CONF_INSTALL_PREFIX,main_menu); if (install_prefix != prev_prefix) { if (install_prefix) { free(install_prefix); install_prefix=NULL; } install_prefix=prev_prefix; } current->child_changed=CHILD_NO_CHANGES; print_notice(NOTICE_Y,NOTICE_X,1,"Changes have been reset. Press any key to continue"); return 0; } /* Runs 'make install' */ int run_make_install(select_menu *menu,void *arg) { int ret=0,status; /* save current tty modes */ def_prog_mode(); /* restore original tty modes */ endwin(); /* temporarily ignore SIGINT * in case child is killed, we do not want to also exit main app */ signal(SIGINT, SIG_IGN); ret=fork(); if (ret<0) { fprintf(output,"Failed to fork process\n"); goto end; } if (ret > 0) { /* parent */ wait(&status); if (status != 0) { fprintf(output,"Command failed to execute properly\n"); goto end; } } else { /* child */ /* Do not propagate SIGINT to parent but propagate SIGWINCH to adjust window size */ signal(SIGINT, SIG_DFL); execlp("make","make","install",(char *)0); exit(-1); } end: /* Restore SIGINT handler */ signal(SIGINT,_quit_handler); printf("\n\nPress any key to return to menuconfig\n\n"); getch(); /* restore save modes, repaint screen */ refresh(); return ret; } /* Runs 'make proper' */ int run_make_proper(select_menu *menu,void *arg) { int ret=0,status; /* save current tty modes */ def_prog_mode(); /* restore original tty modes */ endwin(); /* temporarily ignore SIGINT * in case child is killed, we do not want to also exit main app */ signal(SIGINT, SIG_IGN); ret=fork(); if (ret<0) { fprintf(output,"Failed to fork process\n"); goto end; } if (ret > 0) { /* parent */ wait(&status); if (status != 0) { fprintf(output,"Command failed to execute properly\n"); goto end; } } else { /* child */ /* Do not propagate SIGINT to parent but propagate SIGWINCH to adjust window size */ signal(SIGINT, SIG_DFL); execlp("make","make","proper",(char *)0); exit(-1); } end: /* Restore SIGINT handler */ signal(SIGINT,_quit_handler); printf("\n\nPress any key to return to menuconfig\n\n"); getch(); /* restore save modes, repaint screen */ refresh(); return ret; } /* Generates an actual cfg based on users selected * m4 defs */ int generate_cfg(select_menu *menu,void *arg) { static char generated_name[128]; static char defs_cfg_path[256]; static char cfg_path[256]; char *p; cfg_gen_t *m4_cfg; int n,ret,fd,status; time_t now; struct tm *now_tm; select_menu *items_menu = menu->prev_sibling->prev_sibling; /* Kind of bogus. Maybe menu should have backpointer to cfg entry */ p = memchr(items_menu->name,' ',strlen(items_menu->name)); if (!p) { fprintf(output,"Invalid menu name [%s]\n",items_menu->name); return -1; } p++; m4_cfg = find_cfg_entry(p); if (!m4_cfg) { fprintf(output,"Failed to find cfg entry for %s\n",items_menu->name); return -1; } /* Save everything that was configured */ if (save_m4_def(menu->prev_sibling,(void *)1) < 0) { fprintf(output,"Failed to save m4 defs\n"); return -1; } /* generate config name */ now=time(NULL); now_tm = localtime(&now); n = snprintf(generated_name,128,"%sopensips_%s_%d-%d-%d_%d:%d:%d.cfg", run_locally?"etc/":MENUCONFIG_GEN_PATH, m4_cfg->output_name,now_tm->tm_year+1900,now_tm->tm_mon+1, now_tm->tm_mday,now_tm->tm_hour,now_tm->tm_min,now_tm->tm_sec); if (n<0 || n>128) { fprintf(output,"Failed to create command to generate cfg\n"); return -1; } /* skip all the signal crap. M4 should be much much faster than make install */ ret = fork(); if (ret < 0) { fprintf(output,"Failed to fork process \n"); return -1; } else if (ret > 0) { /* parent */ wait(&status); print_notice(NOTICE_Y,NOTICE_X,1, "Config generated : %s = %s. Press any key to continue", generated_name,status?"FAILED":"SUCCESS"); } else { fd = open(generated_name,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR); if (fd < 0) { fprintf(output,"Failed to open output file\n"); exit(-1); } memcpy(cfg_path,run_locally?"menuconfig/configs/":MENUCONFIG_CFG_PATH, run_locally?19:MENUCONFIG_CFG_PATH_LEN); memcpy(cfg_path+(run_locally?19:MENUCONFIG_CFG_PATH_LEN), m4_cfg->cfg_m4,strlen(m4_cfg->cfg_m4)+1); memcpy(defs_cfg_path,run_locally?"menuconfig/configs/":MENUCONFIG_CFG_PATH, run_locally?19:MENUCONFIG_CFG_PATH_LEN); memcpy(defs_cfg_path+(run_locally?19:MENUCONFIG_CFG_PATH_LEN), m4_cfg->defs_m4,strlen(m4_cfg->defs_m4)+1); /* child */ /* redirect child output to generated file name */ dup2(fd,STDOUT_FILENO); close(fd); execlp("m4","m4",defs_cfg_path,cfg_path,(char *)0); fprintf(output,"Failed to execute\n"); exit(-1); } return 0; } /* Reads install prefix from user */ int read_install_prefix(select_menu *menu,void *arg) { #define query_msg "Enter install prefix " #define folder_ok "Folder exists and is accesible " char str[256]; int ret,len; print_notice(NOTICE_Y,NOTICE_X,0,"%s (Current = '%s') :",query_msg, install_prefix?install_prefix:DEFAULT_INSTALL_PREFIX); /* print directory that user is typing */ echo(); ret=getstr(str); if (ret != 0) { fprintf(output,"Failed to read new install prefix\n"); return -1; } /* disable echoing character on the window */ noecho(); /* Empty directory = default directory */ if (strlen(str) != 0) { prev_prefix=install_prefix; len = strlen(str); install_prefix = malloc(str[len-1]=='/'?len+1:len+2); if (!install_prefix) { fprintf(output,"No more mem\n"); return -1; } memset(install_prefix,0,str[len-1]=='/'?len+1:len+2); memcpy(install_prefix,str,len); if (str[len-1] != '/') install_prefix[len]='/'; print_notice(NOTICE_Y,NOTICE_X,0,"%s. Install prefix is currently [%s]",folder_ok, install_prefix?install_prefix:DEFAULT_INSTALL_PREFIX); clrtoeol(); print_notice(NOTICE_Y+1,NOTICE_X,1,"Press any key to continue !"); clrtoeol(); } else { /* NULL = default prefix */ prev_prefix=install_prefix; install_prefix=NULL; print_notice(NOTICE_Y,NOTICE_X,0,"%s. Install prefix is currently [%s]",folder_ok, install_prefix?install_prefix:DEFAULT_INSTALL_PREFIX); clrtoeol(); print_notice(NOTICE_Y+1,NOTICE_X,1,"Press any key to continue !"); clrtoeol(); } menu->child_changed=CHILD_CHANGED; return 0; } /* Saves all the configured changes * for the cfg entry associated to the current menu */ int save_m4_def(select_menu *menu,void *arg) { char *p; select_menu *items_menu = menu->prev_sibling; select_item *it; cfg_gen_t *m4_cfg; static char cfg_path[256]; FILE *f; /* A little bogus, maybe menu should have back-pointer to cfg entry */ p = memchr(items_menu->name,' ',strlen(items_menu->name)); if (!p) { fprintf(output,"Invalid menu name [%s]\n",items_menu->name); return -1; } p++; m4_cfg = find_cfg_entry(p); if (!m4_cfg) { fprintf(output,"Failed to find cfg entry for %s\n",items_menu->name); return -1; } memcpy(cfg_path,run_locally?"menuconfig/configs/":MENUCONFIG_CFG_PATH, run_locally?19:MENUCONFIG_CFG_PATH_LEN); memcpy(cfg_path+(run_locally?19:MENUCONFIG_CFG_PATH_LEN), m4_cfg->defs_m4,strlen(m4_cfg->defs_m4)+1); f = fopen(cfg_path,"w"); if (!f) { fprintf(output,"Failed to open m4 defs\n"); return -1; } fprintf(f,"divert(-1)\n"); for (it=items_menu->item_list;it;it=it->next) { fprintf(f,"define(`%s', `%s') #%s\n",it->name,it->enabled?"yes":"no", it->description); } fprintf(f,"divert"); if (arg == NULL) { print_notice(NOTICE_Y,NOTICE_X,1,"Script configurations saved for %s. Press any key to continue !",m4_cfg->name); } fclose(f); items_menu->child_changed=CHILD_NO_CHANGES; return 0; } /* Save all related compile options to Makefile.conf*/ int dump_make_conf(select_menu *menu,void *arg) { select_menu *current; select_item *it; int i,k=0; int start_grp=0, prev_grp=0; FILE *f = fopen(MAKE_CONF_FILE,"w"); if (!f) { fprintf(stderr,"Failed to open [%s]\n",MAKE_CONF_FILE); return -1; } /* START compile MODULES related options */ current = find_menu(CONF_EXCLUDED_MODS,main_menu); for (it=current->item_list;it;it=it->next) { for (i=0;idependency_no;i++) { if (i==0) fprintf(f,"#%s=%s|%s\n",it->name,it->description,it->dependency[i]); else fprintf(f,"#%s=%s\n",it->name,it->dependency[i]); if (it->enabled) { print_notice(NOTICE_Y+k++,NOTICE_X,0, "You have enabled the '%s' module, so please install '%s'\n", it->name,it->dependency[i]); } } it->prev_state=it->enabled; } print_notice(NOTICE_Y+k,NOTICE_X,1,"Press any key to continue\n"); fprintf(f,"\nexclude_modules?= "); for (it=current->item_list;it;it=it->next) { fprintf(f,"%s ",it->name); } fprintf(f,"\n\ninclude_modules?= "); for (it=current->item_list;it;it=it->next) { if (it->enabled) fprintf(f,"%s ",it->name); } current->child_changed=CHILD_NO_CHANGES; /* END compile MODULES related options */ fprintf(f,"\n\n"); /* START compile DEFS related options */ current = find_menu(CONF_COMPILE_FLAGS,main_menu); for (it=current->item_list;it;it=it->next) { if (it->group_idx && !start_grp) { start_grp = 1; prev_grp = it->group_idx>0 ? it->group_idx : -it->group_idx; fprintf(f, "%s\n", GRP_START_STR); } if (start_grp) if ((it->group_idx>0 && (it->group_idx != prev_grp)) || (it->group_idx<0 && (-it->group_idx != prev_grp)) || it->group_idx==0) { start_grp = 0; fprintf(f, "%s\n", GRP_END_STR); } fprintf(f,"%sDEFS+= -D%s #%s", it->enabled?"":"#",it->name,it->description); it->prev_state=it->enabled; } if (!it && start_grp) fprintf(f, "%s\n", GRP_END_STR); current->child_changed=CHILD_NO_CHANGES; /* END compile DEFS related options */ /* START install prefix related options */ current=find_menu(CONF_INSTALL_PREFIX,main_menu); fprintf(f,"\nPREFIX=%s",install_prefix?install_prefix:DEFAULT_INSTALL_PREFIX); prev_prefix=install_prefix; current->child_changed=CHILD_NO_CHANGES; /* END install prefix related options */ fclose(f); return 0; } opensips-2.2.2/menuconfig/commands.h000066400000000000000000000025051300170765700174710ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #ifndef _commands_h_ #define _commands_h_ #include "menus.h" int run_make_install(select_menu *menu,void *arg); int run_make_proper(select_menu *menu,void *arg); int generate_cfg(select_menu *menu,void *arg); int save_m4_def(select_menu *menu,void *arg); int dump_make_conf(select_menu *menu,void *arg); int read_install_prefix(select_menu *menu,void *arg); int reset_unsaved_compile(select_menu *menu,void *arg); int save_all_changes(select_menu *menu,void *arg); #endif opensips-2.2.2/menuconfig/configs/000077500000000000000000000000001300170765700171455ustar00rootroot00000000000000opensips-2.2.2/menuconfig/configs/opensips_loadbalancer.m4000066400000000000000000000167041300170765700237460ustar00rootroot00000000000000# # OpenSIPS loadbalancer script # by OpenSIPS Solutions # # This script was generated via "make menuconfig", from # the "Load Balancer" scenario. # You can enable / disable more features / functionalities by # re-generating the scenario with different options. # # Please refer to the Core CookBook at: # http://www.opensips.org/Resources/DocsCookbooks # for a explanation of possible statements, functions and parameters. # ####### Global Parameters ######### log_level=3 log_stderror=no log_facility=LOG_LOCAL0 children=4 /* uncomment the following lines to enable debugging */ #debug_mode=yes /* uncomment the next line to enable the auto temporary blacklisting of not available destinations (default disabled) */ #disable_dns_blacklist=no /* uncomment the next line to enable IPv6 lookup after IPv4 dns lookup failures (default disabled) */ #dns_try_ipv6=yes /* comment the next line to enable the auto discovery of local aliases based on revers DNS on IPs */ auto_aliases=no listen=udp:127.0.0.1:5060 # CUSTOMIZE ME ifelse(ENABLE_TCP, `yes', `listen=tcp:127.0.0.1:5060 # CUSTOMIZE ME' , `') ifelse(ENABLE_TLS,`yes',`listen=tls:127.0.0.1:5061 # CUSTOMIZE ME' , `') ifelse(USE_HTTP_MANAGEMENT_INTERFACE,`yes',`define(`HTTPD_NEEDED',`yes')', `') ####### Modules Section ######## #set module path mpath="/usr/local/lib/opensips/modules/" ifdef(`HTTPD_NEEDED',`#### HTTPD module loadmodule "httpd.so" modparam("httpd", "port", 8888)') #### SIGNALING module loadmodule "signaling.so" #### StateLess module loadmodule "sl.so" #### Transaction Module loadmodule "tm.so" modparam("tm", "fr_timeout", 5) modparam("tm", "fr_inv_timeout", 30) modparam("tm", "restart_fr_on_each_reply", 0) modparam("tm", "onreply_avp_mode", 1) #### Record Route Module loadmodule "rr.so" /* do not append from tag to the RR (no need for this script) */ modparam("rr", "append_fromtag", 0) #### MAX ForWarD module loadmodule "maxfwd.so" #### SIP MSG OPerationS module loadmodule "sipmsgops.so" #### FIFO Management Interface loadmodule "mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("mi_fifo", "fifo_mode", 0666) #### URI module loadmodule "uri.so" modparam("uri", "use_uri_table", 0) #### MYSQL module loadmodule "db_mysql.so" #### AVPOPS module loadmodule "avpops.so" #### ACCounting module loadmodule "acc.so" /* what special events should be accounted ? */ modparam("acc", "early_media", 0) modparam("acc", "report_cancels", 0) /* by default we do not adjust the direct of the sequential requests. if you enable this parameter, be sure the enable "append_fromtag" in "rr" module */ modparam("acc", "detect_direction", 0) ifelse(USE_DBACC,`yes',`modparam("acc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ', `') ifelse(USE_DISPATCHER,`no',`#### DIALOG module loadmodule "dialog.so" modparam("dialog", "dlg_match_mode", 1) modparam("dialog", "default_timeout", 21600) # 6 hours timeout modparam("dialog", "db_mode", 2) modparam("dialog", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ',`') ifelse(USE_DISPATCHER,`yes',`#### DISPATCHER module loadmodule "dispatcher.so" modparam("dispatcher", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME modparam("dispatcher", "ds_ping_method", "OPTIONS") modparam("dispatcher", "ds_probing_mode", 0) modparam("dispatcher", "flags", 2) ifelse(DISABLE_PROBING,`yes',` modparam("dispatcher", "ds_ping_interval", 0) ', ` modparam("dispatcher", "ds_ping_interval", 30) ') ', `#### LOAD BALANCER module loadmodule "load_balancer.so" modparam("load_balancer", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME modparam("load_balancer", "probing_method", "OPTIONS") ifelse(DISABLE_PROBING,`yes',` modparam("load_balancer", "probing_interval", 0) ', ` modparam("load_balancer", "probing_interval", 30) ') ') ifelse(USE_HTTP_MANAGEMENT_INTERFACE,`yes',`#### MI_HTTP module loadmodule "mi_http.so" ',`') loadmodule "proto_udp.so" ifelse(ENABLE_TCP, `yes', `loadmodule "proto_tcp.so"' , `') ifelse(ENABLE_TLS, `yes', `loadmodule "proto_tls.so" modparam("proto_tls","verify_cert", "1") modparam("proto_tls","require_cert", "0") modparam("proto_tls","tls_method", "TLSv1") modparam("proto_tls","certificate", "/usr/local/etc/opensips/tls/user/user-cert.pem") modparam("proto_tls","private_key", "/usr/local/etc/opensips/tls/user/user-privkey.pem") modparam("proto_tls","ca_list", "/usr/local/etc/opensips/tls/user/user-calist.pem") ' , `') ####### Routing Logic ######## # main request routing logic route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { ifelse(USE_DISPATCHER,`no',` # validate the sequential request against dialog if ( $DLG_status!=NULL && !validate_dialog() ) { xlog("In-Dialog $rm from $si (callid=$ci) is not valid according to dialog\n"); ## exit; } ',`') if (is_method("BYE")) { # do accounting even if the transaction fails ifelse(USE_DBACC,`yes',`do_accounting("db","failed"); ', `do_accounting("log","failed");') } else if (is_method("INVITE")) { # even if in most of the cases is useless, do RR for # re-INVITEs alos, as some buggy clients do change route set # during the dialog. record_route(); } # route it out to whatever destination was set by loose_route() # in $du (destination URI). route(RELAY); } else { if ( is_method("ACK") ) { if ( t_check_trans() ) { # non loose-route, but stateful ACK; must be an ACK after # a 487 or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction -> # ignore and discard exit; } } sl_send_reply("404","Not here"); } exit; } #### INITIAL REQUESTS # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } else if (!is_method("INVITE")) { send_reply("405","Method Not Allowed"); exit; } if ($rU==NULL) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } t_check_trans(); # preloaded route checking if (loose_route()) { xlog("L_ERR", "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]"); if (!is_method("ACK")) sl_send_reply("403","Preload Route denied"); exit; } # record routing record_route(); ifelse(USE_DBACC,`yes',`do_accounting("db"); ', `do_accounting("log");') ifelse(USE_DISPATCHER,`yes',` if ( !ds_select_dst("1","4") ) { ',` if ( !load_balance("1","channel")) { ') send_reply("500","No Destination available"); exit; } t_on_failure("GW_FAILOVER"); route(RELAY); } route[RELAY] { if (!t_relay()) { sl_reply_error(); }; exit; } failure_route[GW_FAILOVER] { if (t_was_cancelled()) { exit; } # failure detection with redirect to next available trunk if (t_check_status("(408)|([56][0-9][0-9])")) { xlog("Failed trunk $rd/$du detected \n"); ifelse(USE_DISPATCHER,`yes',` if ( ds_next_dst() ) { ',` if ( load_balance("1","channel") ) { ') t_on_failure("GW_FAILOVER"); t_relay(); exit; } send_reply("500","All GW are down"); } } ifelse(USE_DISPATCHER,`no',` local_route { if (is_method("BYE") && $DLG_dir=="UPSTREAM") { ifelse(USE_DBACC,`yes',` acc_db_request("200 Dialog Timeout", "acc"); ',` acc_log_request("200 Dialog Timeout"); ') } }',`') opensips-2.2.2/menuconfig/configs/opensips_loadbalancer_def.m4000066400000000000000000000011311300170765700245500ustar00rootroot00000000000000divert(-1) define(`ENABLE_TCP', `no') # OpenSIPS will listen on TCP for SIP requests define(`ENABLE_TLS', `no') # OpenSIPS will listen on TLS for SIP requests define(`USE_DBACC', `no') # OpenSIPS will save ACC entries in DB for all calls define(`USE_DISPATCHER', `no') # OpenSIPS will use DISPATCHER instead of Load-Balancer for distributing the traffic define(`DISABLE_PINGING', `yes') # OpenSIPS will not ping at all the destinations (otherwise it will ping when detected as failed) define(`USE_HTTP_MANAGEMENT_INTERFACE', `no') # OpenSIPS will provide a WEB Management Interface on port 8888 divertopensips-2.2.2/menuconfig/configs/opensips_residential.m4000066400000000000000000000340561300170765700236420ustar00rootroot00000000000000# # OpenSIPS residential configuration script # by OpenSIPS Solutions # # This script was generated via "make menuconfig", from # the "Residential" scenario. # You can enable / disable more features / functionalities by # re-generating the scenario with different options.# # # Please refer to the Core CookBook at: # http://www.opensips.org/Resources/DocsCookbooks # for a explanation of possible statements, functions and parameters. # ####### Global Parameters ######### log_level=3 log_stderror=no log_facility=LOG_LOCAL0 children=4 /* uncomment the following lines to enable debugging */ #debug_mode=yes /* uncomment the next line to enable the auto temporary blacklisting of not available destinations (default disabled) */ #disable_dns_blacklist=no /* uncomment the next line to enable IPv6 lookup after IPv4 dns lookup failures (default disabled) */ #dns_try_ipv6=yes /* comment the next line to enable the auto discovery of local aliases based on revers DNS on IPs */ auto_aliases=no listen=udp:127.0.0.1:5060 # CUSTOMIZE ME ifelse(ENABLE_TCP, `yes', `listen=tcp:127.0.0.1:5060 # CUSTOMIZE ME' , `') ifelse(ENABLE_TLS,`yes',`listen=tls:127.0.0.1:5061 # CUSTOMIZE ME' , `') ####### Modules Section ######## #set module path mpath="/usr/local/lib/opensips/modules/" #### SIGNALING module loadmodule "signaling.so" #### StateLess module loadmodule "sl.so" #### Transaction Module loadmodule "tm.so" modparam("tm", "fr_timeout", 5) modparam("tm", "fr_inv_timeout", 30) modparam("tm", "restart_fr_on_each_reply", 0) modparam("tm", "onreply_avp_mode", 1) #### Record Route Module loadmodule "rr.so" /* do not append from tag to the RR (no need for this script) */ modparam("rr", "append_fromtag", 0) #### MAX ForWarD module loadmodule "maxfwd.so" #### SIP MSG OPerationS module loadmodule "sipmsgops.so" #### FIFO Management Interface loadmodule "mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("mi_fifo", "fifo_mode", 0666) #### URI module loadmodule "uri.so" modparam("uri", "use_uri_table", 0) ifelse(USE_DR_PSTN,`yes',` ifelse(HAVE_INBOUND_PSTN,`yes',`define(`USE_DR_MODULE',`yes')',HAVE_OUTBOUND_PSTN,`yes',`define(`USE_DR_MODULE',`yes')',) ', `') ifelse(USE_AUTH,`yes',`define(`DB_NEEDED',`yes')',USE_MULTIDOMAIN,`yes',`define(`DB_NEEDED',`yes')',USE_PRESENCE,`yes',`define(`DB_NEEDED',`yes')',USE_DBACC,`yes',`define(`DB_NEEDED',`yes')',USE_DBUSRLOC,`yes',`define(`DB_NEEDED',`yes')',USE_DIALOG,`yes',`define(`DB_NEEDED',`yes')',USE_DIALPLAN,`yes',`define(`DB_NEEDED',`yes')',USE_DR_MODULE,`yes',`define(`DB_NEEDED',`yes')',) ifelse(USE_HTTP_MANAGEMENT_INTERFACE,`yes',`define(`HTTPD_NEEDED',`yes')', `') ifdef(`DB_NEEDED',`#### MYSQL module loadmodule "db_mysql.so"') ifdef(`HTTPD_NEEDED',`#### HTTPD module loadmodule "httpd.so" modparam("httpd", "port", 8888)') #### USeR LOCation module loadmodule "usrloc.so" modparam("usrloc", "nat_bflag", "NAT") ifelse(USE_DBUSRLOC,`yes',`modparam("usrloc", "db_mode", 2) modparam("usrloc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ', `modparam("usrloc", "db_mode", 0)') #### REGISTRAR module loadmodule "registrar.so" modparam("registrar", "tcp_persistent_flag", "TCP_PERSISTENT") ifelse(USE_NAT,`yes',`modparam("registrar", "received_avp", "$avp(received_nh)")',`') /* uncomment the next line not to allow more than 10 contacts per AOR */ #modparam("registrar", "max_contacts", 10) #### ACCounting module loadmodule "acc.so" /* what special events should be accounted ? */ modparam("acc", "early_media", 0) modparam("acc", "report_cancels", 0) /* by default we do not adjust the direct of the sequential requests. if you enable this parameter, be sure the enable "append_fromtag" in "rr" module */ modparam("acc", "detect_direction", 0) ifelse(USE_DBACC,`yes',`modparam("acc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ', `') ifelse(USE_AUTH,`yes',`#### AUTHentication modules loadmodule "auth.so" loadmodule "auth_db.so" modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") modparam("auth_db|uri", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME modparam("auth_db", "load_credentials", "") ', `') ifelse(USE_ALIASES,`yes',`#### ALIAS module loadmodule "alias_db.so" modparam("alias_db", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ', `') ifelse(USE_MULTIDOMAIN,`yes',`#### DOMAIN module loadmodule "domain.so" modparam("domain", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME modparam("domain", "db_mode", 1) # Use caching modparam("auth_db|usrloc|uri", "use_domain", 1) ', `') ifelse(USE_PRESENCE,`yes',`#### PRESENCE modules loadmodule "xcap.so" loadmodule "presence.so" loadmodule "presence_xml.so" modparam("xcap|presence", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME modparam("presence_xml", "force_active", 1) modparam("presence", "server_address", "sip:127.0.0.1:5060") # CUSTOMIZE ME ', `') ifelse(USE_DIALOG,`yes',`#### DIALOG module loadmodule "dialog.so" modparam("dialog", "dlg_match_mode", 1) modparam("dialog", "default_timeout", 21600) # 6 hours timeout modparam("dialog", "db_mode", 2) modparam("dialog", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ',`') ifelse(USE_NAT,`yes',`#### NAT modules loadmodule "nathelper.so" modparam("nathelper", "natping_interval", 10) modparam("nathelper", "ping_nated_only", 1) modparam("nathelper", "sipping_bflag", "SIP_PING_FLAG") modparam("nathelper", "sipping_from", "sip:pinger@127.0.0.1") #CUSTOMIZE ME modparam("nathelper", "received_avp", "$avp(received_nh)") loadmodule "rtpproxy.so" modparam("rtpproxy", "rtpproxy_sock", "udp:localhost:12221") # CUSTOMIZE ME ',`') ifelse(USE_DIALPLAN,`yes',`#### DIALPLAN module loadmodule "dialplan.so" modparam("dialplan", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ',`') ifelse(USE_DR_MODULE,`yes',`#### DYNAMMIC ROUTING module loadmodule "drouting.so" modparam("drouting", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ',`') ifelse(USE_HTTP_MANAGEMENT_INTERFACE,`yes',`#### MI_HTTP module loadmodule "mi_http.so" ',`') loadmodule "proto_udp.so" ifelse(ENABLE_TCP, `yes', `loadmodule "proto_tcp.so"' , `') ifelse(ENABLE_TLS, `yes', `loadmodule "proto_tls.so" modparam("proto_tls","verify_cert", "1") modparam("proto_tls","require_cert", "0") modparam("proto_tls","tls_method", "TLSv1") modparam("proto_tls","certificate", "/usr/local/etc/opensips/tls/user/user-cert.pem") modparam("proto_tls","private_key", "/usr/local/etc/opensips/tls/user/user-privkey.pem") modparam("proto_tls","ca_list", "/usr/local/etc/opensips/tls/user/user-calist.pem") ' , `') ####### Routing Logic ######## # main request routing logic route{ ifelse(USE_NAT,`yes',`force_rport(); if (nat_uac_test("23")) { if (is_method("REGISTER")) { fix_nated_register(); setbflag(NAT); } else { fix_nated_contact(); setflag(NAT); } } ',`') if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { ifelse(USE_DIALOG,`yes',` # validate the sequential request against dialog if ( $DLG_status!=NULL && !validate_dialog() ) { xlog("In-Dialog $rm from $si (callid=$ci) is not valid according to dialog\n"); ## exit; } ',`') if (is_method("BYE")) { # do accounting even if the transaction fails ifelse(USE_DBACC,`yes',`do_accounting("db","failed"); ', `do_accounting("log","failed");') } else if (is_method("INVITE")) { # even if in most of the cases is useless, do RR for # re-INVITEs alos, as some buggy clients do change route set # during the dialog. record_route(); } ifelse(USE_NAT,`yes',`if (check_route_param("nat=yes")) setflag(NAT);',`') # route it out to whatever destination was set by loose_route() # in $du (destination URI). route(relay); } else { ifelse(USE_PRESENCE,`yes', `if (is_method("SUBSCRIBE") && $rd == "127.0.0.1:5060") { # CUSTOMIZE ME # in-dialog subscribe requests route(handle_presence); exit; }',`') if ( is_method("ACK") ) { if ( t_check_trans() ) { # non loose-route, but stateful ACK; must be an ACK after # a 487 or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction -> # ignore and discard exit; } } sl_send_reply("404","Not here"); } exit; } # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); if ( !(is_method("REGISTER") ifelse(HAVE_INBOUND_PSTN,`yes',` ifelse(USE_DR_MODULE,`yes',`|| is_from_gw()',`|| (src_ip==11.22.33.44 && src_port=5060 /* CUSTOMIZE ME */)')',`') ) ) { ifelse(USE_MULTIDOMAIN,`yes',` if (is_from_local())',` if (from_uri==myself) ') { ifelse(USE_AUTH,`yes',` # authenticate if from local subscriber # authenticate all initial non-REGISTER request that pretend to be # generated by local subscriber (domain from FROM URI is local) if (!proxy_authorize("", "subscriber")) { proxy_challenge("", "0"); exit; } if (!db_check_from()) { sl_send_reply("403","Forbidden auth ID"); exit; } consume_credentials(); # caller authenticated ',`') } else { # if caller is not local, then called number must be local ifelse(USE_MULTIDOMAIN,`yes',` if (!is_uri_host_local())',` if (!uri==myself)') { send_reply("403","Rely forbidden"); exit; } } } # preloaded route checking if (loose_route()) { xlog("L_ERR", "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]"); if (!is_method("ACK")) sl_send_reply("403","Preload Route denied"); exit; } # record routing if (!is_method("REGISTER|MESSAGE")) record_route(); # account only INVITEs if (is_method("INVITE")) { ifelse(USE_DIALOG,`yes',` # create dialog with timeout if ( !create_dialog("B") ) { send_reply("500","Internal Server Error"); exit; } ',`') ifelse(USE_DBACC,`yes',`do_accounting("db"); ', `do_accounting("log");') } ifelse(USE_MULTIDOMAIN,`yes',` if (!is_uri_host_local())',` if (!uri==myself)') { append_hf("P-hint: outbound\r\n"); ifelse(ENABLE_TLS,`yes',` # if you have some interdomain connections via TLS ## CUSTOMIZE IF NEEDED ##if ($rd=="tls_domain1.net" ## || $rd=="tls_domain2.net" ##) { ## force_send_socket(tls:127.0.0.1:5061); # CUSTOMIZE ##} ',`') route(relay); } # requests for my domain ifelse(USE_PRESENCE,`yes',` if( is_method("PUBLISH|SUBSCRIBE")) route(handle_presence);',` if (is_method("PUBLISH|SUBSCRIBE")) { sl_send_reply("503", "Service Unavailable"); exit; }') if (is_method("REGISTER")) { ifelse(USE_AUTH,`yes',`# authenticate the REGISTER requests if (!www_authorize("", "subscriber")) { www_challenge("", "0"); exit; } if (!db_check_to()) { sl_send_reply("403","Forbidden auth ID"); exit; }',`') if ( ifelse(ENABLE_TCP,`yes',`proto==TCP ||',`') ifelse(ENABLE_TLS,`yes',`proto==TLS ||',`') 0 ) setflag(TCP_PERSISTENT); ifelse(USE_NAT,`yes',`if (isflagset(NAT)) { setbflag(SIP_PING_FLAG); }',`') if (!save("location")) sl_reply_error(); exit; } if ($rU==NULL) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } ifelse(USE_ALIASES,`yes',` # apply DB based aliases alias_db_lookup("dbaliases");',`') ifelse(USE_DIALPLAN,`yes',` # apply transformations from dialplan table dp_translate("0","$rU/$rU");',`') ifelse(HAVE_OUTBOUND_PSTN,`yes',` if ($rU=~"^\+[1-9][0-9]+$") { ifelse(USE_DR_MODULE,`yes',` if (!do_routing("0")) { send_reply("500","No PSTN Route found"); exit; } ',` $rd="11.22.33.44"; CUSTOMIZE ME $rp=5060; ') route(relay); exit; } ',`') # do lookup with method filtering if (!lookup("location","m")) { ifelse(USE_AUTH,`yes',`if (!db_does_uri_exist()) { send_reply("420","Bad Extension"); exit; }',`') ifelse(VM_DIVERSION,`yes',` # redirect to a different VM system $du = "sip:127.0.0.2:5060"; # CUSTOMIZE ME route(relay); ',` t_newtran(); t_reply("404", "Not Found"); exit;') } ifelse(USE_NAT,`yes',`if (isbflagset(NAT)) setflag(NAT);',`') # when routing via usrloc, log the missed calls also ifelse(USE_DBACC,`yes',`do_accounting("db","missed"); ', `do_accounting("log","missed");') route(relay); } route[relay] { # for INVITEs enable some additional helper routes if (is_method("INVITE")) { ifelse(USE_NAT,`yes',`if (isflagset(NAT)) { rtpproxy_offer("ro"); }',`') t_on_branch("per_branch_ops"); t_on_reply("handle_nat"); t_on_failure("missed_call"); } ifelse(USE_NAT,`yes',`if (isflagset(NAT)) { add_rr_param(";nat=yes"); }',`') if (!t_relay()) { send_reply("500","Internal Error"); }; exit; } ifelse(USE_PRESENCE,`yes',` # Presence route route[handle_presence] { if (!t_newtran()) { sl_reply_error(); exit; } if(is_method("PUBLISH")) { handle_publish(); } else if( is_method("SUBSCRIBE")) { handle_subscribe(); } exit; }',`') branch_route[per_branch_ops] { xlog("new branch at $ru\n"); } onreply_route[handle_nat] { ifelse(USE_NAT,`yes',`if (nat_uac_test("1")) fix_nated_contact(); if ( isflagset(NAT) ) rtpproxy_answer("ro");',`') xlog("incoming reply\n"); } failure_route[missed_call] { if (t_was_cancelled()) { exit; } # uncomment the following lines if you want to block client # redirect based on 3xx replies. ##if (t_check_status("3[0-9][0-9]")) { ##t_reply("404","Not found"); ## exit; ##} ifelse(VM_DIVERSION,`yes',` # redirect the failed to a different VM system if (t_check_status("486|408")) { $du = "sip:127.0.0.2:5060"; # CUSTOMIZE ME # do not set the missed call flag again route(relay); }',`') } ifelse(USE_DIALOG,`yes',` local_route { if (is_method("BYE") && $DLG_dir=="UPSTREAM") { ifelse(USE_DBACC,`yes',` acc_db_request("200 Dialog Timeout", "acc"); ',` acc_log_request("200 Dialog Timeout"); ') } }',`') opensips-2.2.2/menuconfig/configs/opensips_residential_def.m4000066400000000000000000000026431300170765700244550ustar00rootroot00000000000000divert(-1) define(`ENABLE_TCP', `no') # OpenSIPS will listen on TCP for SIP requests define(`ENABLE_TLS', `no') # OpenSIPS will listen on TLS for SIP requests define(`USE_ALIASES', `no') # OpenSIPS will allow the use of Aliases for SIP users define(`USE_AUTH', `no') # OpenSIPS will authenticate Register & Invite requests define(`USE_DBACC', `no') # OpenSIPS will save ACC entries in DB for all calls define(`USE_DBUSRLOC', `no') # OpenSIPS will store UsrLoc entries in the DB define(`USE_DIALOG', `no') # OpenSIPS will keep track of active dialogs define(`USE_MULTIDOMAIN', `no') # OpenSIPS will handle multiple domains for subscribers define(`USE_NAT', `no') # OpenSIPS will try to cope with NAT by fixing SIP msgs and engaging RTPProxy define(`USE_PRESENCE', `no') # OpenSIPS will act as a Presence server define(`USE_DIALPLAN', `no') # OpenSIPS will use dialplan for transformation of local numbers define(`VM_DIVERSION', `no') # OpenSIPS will redirect to VM calls not reaching the subscribers define(`HAVE_INBOUND_PSTN', `no') # OpenSIPS will accept calls from PSTN gateways (with static IP authentication) define(`HAVE_OUTBOUND_PSTN', `no') # OpenSIPS will send numerical dials to PSTN gateways (with static IP definition) define(`USE_DR_PSTN', `no') # OpenSIPS will use Dynamic Routing Support for PSTN interconnection define(`USE_HTTP_MANAGEMENT_INTERFACE', `no') # OpenSIPS will provide a WEB Management Interface on port 8888 divertopensips-2.2.2/menuconfig/configs/opensips_trunking.m4000066400000000000000000000177101300170765700231760ustar00rootroot00000000000000# # OpenSIPS trunking script # by OpenSIPS Solutions # # This script was generated via "make menuconfig", from # the "Trunking" scenario. # You can enable / disable more features / functionalities by # re-generating the scenario with different options. # # Please refer to the Core CookBook at: # http://www.opensips.org/Resources/DocsCookbooks # for a explanation of possible statements, functions and parameters. # ####### Global Parameters ######### log_level=3 log_stderror=no log_facility=LOG_LOCAL0 children=4 /* uncomment the following lines to enable debugging */ #debug_mode=yes /* uncomment the next line to enable the auto temporary blacklisting of not available destinations (default disabled) */ #disable_dns_blacklist=no /* uncomment the next line to enable IPv6 lookup after IPv4 dns lookup failures (default disabled) */ #dns_try_ipv6=yes /* comment the next line to enable the auto discovery of local aliases based on revers DNS on IPs */ auto_aliases=no listen=udp:127.0.0.1:5060 # CUSTOMIZE ME ifelse(ENABLE_TCP, `yes', `listen=tcp:127.0.0.1:5060 # CUSTOMIZE ME' , `') ifelse(ENABLE_TLS,`yes',`listen=tls:127.0.0.1:5061 # CUSTOMIZE ME' , `') ifelse(USE_HTTP_MANAGEMENT_INTERFACE,`yes',`define(`HTTPD_NEEDED',`yes')', `') ####### Modules Section ######## #set module path mpath="/usr/local/lib/opensips/modules/" ifdef(`HTTPD_NEEDED',`#### HTTPD module loadmodule "httpd.so" modparam("httpd", "port", 8888)') #### SIGNALING module loadmodule "signaling.so" #### StateLess module loadmodule "sl.so" #### Transaction Module loadmodule "tm.so" modparam("tm", "fr_timeout", 5) modparam("tm", "fr_inv_timeout", 30) modparam("tm", "restart_fr_on_each_reply", 0) modparam("tm", "onreply_avp_mode", 1) #### Record Route Module loadmodule "rr.so" /* do not append from tag to the RR (no need for this script) */ modparam("rr", "append_fromtag", 0) #### MAX ForWarD module loadmodule "maxfwd.so" #### SIP MSG OPerations module loadmodule "sipmsgops.so" #### FIFO Management Interface loadmodule "mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("mi_fifo", "fifo_mode", 0666) #### URI module loadmodule "uri.so" modparam("uri", "use_uri_table", 0) #### MYSQL module loadmodule "db_mysql.so" #### AVPOPS module loadmodule "avpops.so" #### DYNAMIC ROUTING module loadmodule "drouting.so" modparam("drouting", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME #### PERMISSIONS module loadmodule "permissions.so" modparam("permissions", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME #### ACCounting module loadmodule "acc.so" /* what special events should be accounted ? */ modparam("acc", "early_media", 0) modparam("acc", "report_cancels", 0) /* by default we do not adjust the direct of the sequential requests. if you enable this parameter, be sure the enable "append_fromtag" in "rr" module */ modparam("acc", "detect_direction", 0) ifelse(USE_DBACC,`yes',`modparam("acc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ', `') ifelse(USE_DIALOG,`yes',`#### DIALOG module loadmodule "dialog.so" modparam("dialog", "dlg_match_mode", 1) modparam("dialog", "default_timeout", 21600) # 6 hours timeout modparam("dialog", "db_mode", 2) modparam("dialog", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ifelse(DO_CALL_LIMITATION,`yes',` modparam("dialog", "profiles_with_value", "trunkCalls") ',`') ',`') ifelse(USE_DIALPLAN,`yes',`#### DIALPLAN module loadmodule "dialplan.so" modparam("dialplan", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME ',`') ifelse(USE_HTTP_MANAGEMENT_INTERFACE,`yes',`#### MI_HTTP module loadmodule "mi_http.so" ',`') loadmodule "proto_udp.so" ifelse(ENABLE_TCP, `yes', `loadmodule "proto_tcp.so"' , `') ifelse(ENABLE_TLS, `yes', `loadmodule "proto_tls.so" modparam("proto_tls","verify_cert", "1") modparam("proto_tls","require_cert", "0") modparam("proto_tls","tls_method", "TLSv1") modparam("proto_tls","certificate", "/usr/local/etc/opensips/tls/user/user-cert.pem") modparam("proto_tls","private_key", "/usr/local/etc/opensips/tls/user/user-privkey.pem") modparam("proto_tls","ca_list", "/usr/local/etc/opensips/tls/user/user-calist.pem") ' , `') ####### Routing Logic ######## # main request routing logic route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if ( check_source_address("1","$avp(trunk_attrs)") ) { # request comes from trunks setflag(IS_TRUNK); } else if ( is_from_gw() ) { # request comes from GWs } else { send_reply("403","Forbidden"); exit; } if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { ifelse(USE_DIALOG,`yes',` # validate the sequential request against dialog if ( $DLG_status!=NULL && !validate_dialog() ) { xlog("In-Dialog $rm from $si (callid=$ci) is not valid according to dialog\n"); ## exit; } ',`') if (is_method("BYE")) { # do accounting even if the transaction fails ifelse(USE_DBACC,`yes',`do_accounting("db","failed"); ', `do_accounting("log","failed");') } else if (is_method("INVITE")) { # even if in most of the cases is useless, do RR for # re-INVITEs alos, as some buggy clients do change route set # during the dialog. record_route(); } # route it out to whatever destination was set by loose_route() # in $du (destination URI). route(RELAY); } else { if ( is_method("ACK") ) { if ( t_check_trans() ) { # non loose-route, but stateful ACK; must be an ACK after # a 487 or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction -> # ignore and discard exit; } } sl_send_reply("404","Not here"); } exit; } #### INITIAL REQUESTS if ( !isflagset(IS_TRUNK) ) { ## accept new calls only from trunks send_reply("403","Not from trunk"); exit; } # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } else if (!is_method("INVITE")) { send_reply("405","Method Not Allowed"); exit; } if ($rU==NULL) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } t_check_trans(); # preloaded route checking if (loose_route()) { xlog("L_ERR", "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]"); if (!is_method("ACK")) sl_send_reply("403","Preload Route denied"); exit; } # record routing record_route(); ifelse(USE_DBACC,`yes',`do_accounting("db"); ', `do_accounting("log");') ifelse(USE_DIALOG,`yes',` # create dialog with timeout if ( !create_dialog("B") ) { send_reply("500","Internal Server Error"); exit; } ifelse(DO_CALL_LIMITATION,`yes',` if (is_avp_set("$avp(trunk_attrs)") && $avp(trunk_attrs)=~"^[0-9]+$") { get_profile_size("trunkCalls","$si","$var(size)"); if ( $(var(size){s.int}) >= $(avp(trunk_attrs){s.int}) ) { send_reply("486","Busy Here"); exit; } } set_dlg_profile("trunkCalls","$si"); ',`') ',`') ifelse(USE_DIALPLAN,`yes',` # apply transformations from dialplan table dp_translate("0","$rU/$rU");',`') # route calls based on prefix if ( !do_routing("1") ) { send_reply("404","No Route found"); exit; } t_on_failure("GW_FAILOVER"); route(RELAY); } route[RELAY] { if (!t_relay()) { sl_reply_error(); }; exit; } failure_route[GW_FAILOVER] { if (t_was_cancelled()) { exit; } # detect failure and redirect to next available GW if (t_check_status("(408)|([56][0-9][0-9])")) { xlog("Failed GW $rd detected \n"); if ( use_next_gw() ) { t_on_failure("GW_FAILOVER"); t_relay(); exit; } send_reply("500","All GW are down"); } } ifelse(USE_DIALOG,`yes',` local_route { if (is_method("BYE") && $DLG_dir=="UPSTREAM") { ifelse(USE_DBACC,`yes',` acc_db_request("200 Dialog Timeout", "acc"); ',` acc_log_request("200 Dialog Timeout"); ') } }',`') opensips-2.2.2/menuconfig/configs/opensips_trunking_def.m4000066400000000000000000000011501300170765700240030ustar00rootroot00000000000000divert(-1) define(`ENABLE_TCP', `no') # OpenSIPS will listen on TCP for SIP requests define(`ENABLE_TLS', `no') # OpenSIPS will listen on TLS for SIP requests define(`USE_DBACC', `no') # OpenSIPS will save ACC entries in DB for all calls define(`USE_DIALPLAN', `no') # OpenSIPS will use dialplan for transformation of local numbers define(`USE_DIALOG', `no') # OpenSIPS will keep track of active dialogs define(`DO_CALL_LIMITATION', `no') # OpenSIPS will limit the number of parallel calls per trunk define(`USE_HTTP_MANAGEMENT_INTERFACE', `no') # OpenSIPS will provide a WEB Management Interface on port 8888 divertopensips-2.2.2/menuconfig/curses.c000066400000000000000000000204201300170765700171630ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #include #include #include #include #include "curses.h" #include "main.h" volatile int max_x = 0; volatile int max_y = 0; /* Cleanup on exit. * Mandatory to be called on all permanent exits from menuconfig * Otherwise, console will be messed up */ void cleanup(void) { delwin(stdscr); endwin(); refresh(); fclose(output); } /* Update actual console size * Note - Console size will be updated on the spot, * as this is called on a signal, but actual re-drawing will * be done on another key press */ void set_xy_size(int sig) { struct winsize ws; /* ioctl for the window size. TODO - is this portable ? */ if (ioctl(0,TIOCGWINSZ,&ws)!=0) { fprintf(stdout,"Updating window size failed:%s\n",strerror(errno)); return; } max_x = ws.ws_col; max_y = ws.ws_row; if (max_x < MIN_X || max_y < MIN_Y) { fprintf(output, "Terminal must be at least %d x %d.\n", MIN_X, MIN_Y); max_x = MIN_X - 1; max_y = MIN_Y - 1; } } void _quit_handler(int sig) { fprintf(output,"QUIT RECEIVED\n"); cleanup(); exit(0); } int draw_sibling_menu(select_menu *menu) { int i,c,maxopt,d; char buf[40]; select_menu *it; int cur_index=0; int skip=0; int max_len=0,len; again: wclear(menu_window); /* print title in colour */ attron(COLOR_PAIR(1)); mvprintw(HIGH_NOTICE_Y,max_x/2-20,menu->parent?menu->parent->name:"OpenSIPS Main Configuration Menu"); /* print footer */ print_notice(NOTICE_Y,NOTICE_X,0,"Press h for navigation help."); attroff(COLOR_PAIR(1)); /* draw actual menu */ i=0; for (it=menu;it;it=it->next_sibling) { wmove(menu_window, max_y/4+i++, max_x / 2 - 20); snprintf(buf, sizeof(buf), " %s", it->name); waddstr(menu_window, buf); len = strlen(it->name) +6; if (len > max_len) max_len = len; } /* draw selection marker */ wmove(menu_window, max_y/4+cur_index, (max_x / 2) - 25); waddstr(menu_window, "--->"); /* print box with color */ wattron(menu_window,COLOR_PAIR(2)); for (d=-1;d 0) cur_index--; break; case KEY_DOWN: if (cur_index < maxopt) cur_index++; break; case KEY_RIGHT: case KEY_ENTER: case '\n': for (i=0,it=menu;inext_sibling) ; c = exec_menu_action(it); break; case 'h': case 'H': clear(); print_notice(max_y/2,20,0,"Use UP and DOWN arrow keys to navigate."); print_notice(max_y/2+1,20,0,"Use RIGHT arrow or ENTER key to enter a certain menu."); print_notice(max_y/2+2,20,0,"Use LEFT arror or Q key to go back."); print_notice(max_y/2+3,20,0,"Use SPACE to toggle an entry ON/OFF.\n"); print_notice(max_y/2+4,20,1,"Press any key to return to menuconfig."); refresh(); break; case KEY_LEFT: case 'q': case 'Q': for (it=menu;it;it=it->next_sibling) { if (it->child_changed == CHILD_CHANGED) { if (skip == 0) { /* have we asked before and got negative response ? */ print_notice(NOTICE_Y,NOTICE_X,0,"You have not saved changes. Go back anyway ? [y/n] "); c = getch(); if (c == 'n' || c == 'N') goto again; else { it->child_changed = CHILD_CHANGE_IGNORED; skip=1; return 0; } } else it->child_changed = CHILD_CHANGE_IGNORED; } } if (skip == 1) return 0; return 0; } goto again; } int draw_item_list(select_menu *menu) { select_item *it, *it_2; int i=0,j=0,k=0,d,sc=0; int c,curopt=0; char buf[40]; select_item *current=NULL; int should_scroll,max_display; int len,max_len=0; int disp_start=0,actual_pos=0; again: i=0;j=0;k=0; max_display=max_y/2-2; should_scroll=menu->item_no>max_display?1:0; wclear(menu_window); /* print title in colour */ attron(COLOR_PAIR(1)); mvprintw(HIGH_NOTICE_Y,max_x/2-20,menu->name); attroff(COLOR_PAIR(1)); if (should_scroll) { for (it=menu->item_list,sc=0;it;it=it->next,sc++) { /* only draw visible part of menu */ if (sc>=disp_start && i < max_display) { wmove(menu_window, max_y/4+j++, max_x / 2 - 20); i++; snprintf(buf, sizeof(buf), "%s%s%s %s", it->group_idx ? "(" : "[", it->enabled ? "*" : " ", it->group_idx ? ")" : "]", it->name); waddstr(menu_window, buf); len=strlen(it->name); if (len > max_len) max_len=len; } } } else { for (it=menu->item_list,sc=0;it;it=it->next,sc++) { /* draw everything */ wmove(menu_window, max_y/4+j++, max_x / 2 - 20); i++; snprintf(buf, sizeof(buf), "%s%s%s %s", it->group_idx ? "(" : "[", it->enabled ? "*" : " ", it->group_idx ? ")" : "]", it->name); waddstr(menu_window, buf); len=strlen(it->name); if (len > max_len) max_len=len; } /* marker is always in par with the selected option */ actual_pos=curopt; } for(it=menu->item_list;it;it=it->next) if (k++ == curopt) { current=it; break; } /* print current item description */ if (current->description) { attron(COLOR_PAIR(1)); print_notice(NOTICE_Y,NOTICE_X,0,current->description); attroff(COLOR_PAIR(1)); } move(max_y/4+actual_pos,max_x/2-19); /* draw box */ wattron(menu_window,COLOR_PAIR(2)); for (d=-1;d 0) { wmove(menu_window,max_y/4,max_x/2-5+max_len); wprintw(menu_window,"Scroll up for more"); } if (should_scroll && disp_start + max_display < menu->item_no) { wmove(menu_window,max_y/4+max_display-1,max_x/2-5+max_len); wprintw(menu_window,"Scroll down for more"); } wattroff(menu_window,COLOR_PAIR(2)); wrefresh(menu_window); k=0; while ((c = getch())) { switch (c) { case KEY_UP: if (should_scroll && curopt != 0) { if (curopt == disp_start) { disp_start--; actual_pos=0; } else actual_pos--; curopt--; } else if (curopt!=0) { curopt--; } break; case KEY_DOWN: if (should_scroll && curopt < menu->item_no-1) { if (curopt == (disp_start+max_display-1)) { disp_start++; actual_pos=i-1; } else actual_pos++; curopt++; } else if (curopt < i-1) { curopt++; } break; case ' ': for (it=menu->item_list;it;it=it->next) { if (k++ == curopt) { it->enabled=it->enabled?0:1; menu->child_changed=CHILD_CHANGED; it->group_idx = it->group_idx ? -it->group_idx : 0; if (it->group_idx<0) for(it_2=menu->item_list;it_2;it_2=it_2->next) if (it!=it_2 && it_2->group_idx<0 && it->group_idx==it_2->group_idx) { it_2->group_idx = -it_2->group_idx; it_2->enabled=0; menu->child_changed=CHILD_CHANGED; break; } } } break; case KEY_LEFT: case 'q': case 'Q': wclear(menu_window); return 0; } goto again; } return 0; }opensips-2.2.2/menuconfig/curses.h000066400000000000000000000026731300170765700172020ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #ifndef _menuconfigcurses_h_ #define _menuconfigcurses_h_ #include #include #include "menus.h" #define MIN_X 80 #define MIN_Y 20 extern volatile int max_x; extern volatile int max_y; void cleanup(); void set_xy_size(int sig); void _quit_handler(int sig); int draw_sibling_menu(select_menu *menu); int draw_item_list(select_menu *menu); #define HIGH_NOTICE_Y (max_y/16) #define NOTICE_Y (max_y*3/4) #define NOTICE_X (10) #define print_notice(y,x,prompt,...) \ do { \ mvprintw(y,x, __VA_ARGS__); \ clrtobot(); \ if (prompt) \ getch(); \ } while (0) #endif opensips-2.2.2/menuconfig/items.c000066400000000000000000000053701300170765700170070ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #include #include #include #include "items.h" #include "main.h" /* Allocate mem & init an item with passed * name & description */ select_item* create_item(char *item_name,char *description) { select_item *ret; int name_len = strlen(item_name); int desc_len = description?strlen(description):-1; int ret_len = sizeof(select_item) + name_len + desc_len + 2; ret = malloc(ret_len); if (!ret) { fprintf(output,"Failed to alloc mem\n"); return 0; } memset(ret,0,ret_len); ret->name = (char *) (ret + 1); memcpy(ret->name,item_name,name_len); if (description) { ret->description=(char *)(ret+1) + name_len+1; memcpy(ret->description,description,desc_len); } return ret; } /* Add an external dependency for the item. */ int add_dependency(select_item *item,char *desc) { int len = strlen(desc); if (item->dependency_no == MAX_DEPENDENCY_NO) { fprintf(output,"MAX dependencies reached\n"); return -1; } item->dependency[item->dependency_no] = malloc(len+1); if (item->dependency[item->dependency_no] == NULL) { fprintf(output,"Failed to alloc mem\n"); return -1; } memset(item->dependency[item->dependency_no],0,len+1); memcpy(item->dependency[item->dependency_no],desc,len); item->dependency_no++; return 0; } /* Link the current item to it's parent menu */ void link_item(select_menu *menu,select_item *item) { select_item *it; if (menu->item_list == NULL) menu->item_list = item; else { for (it=menu->item_list;it->next;it=it->next) ; it->next = item; } menu->item_no++; } /* Mark the item with the passed name as enabled */ void enable_item(select_menu *parent,char *name,int len) { select_item *it; for (it=parent->item_list;it;it=it->next) { if (memcmp(it->name,name,len) == 0) { it->enabled = 1; it->prev_state= 1; break; } } if (!it) { fprintf(output,"BUG - include modules invalid list - %.*s\n",len,name); } } opensips-2.2.2/menuconfig/items.h000066400000000000000000000033551300170765700170150ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #ifndef _items_h_ #define _items_h_ struct sel_menu; #define CHILD_NO_CHANGES 0 #define CHILD_CHANGED 1 #define CHILD_CHANGE_IGNORED 2 #define MAX_DEPENDENCY_NO 5 typedef struct sel_item { char *name; /* item display name */ char *dependency[MAX_DEPENDENCY_NO]; /* item dependencies */ char *description; /* item description */ int dependency_no; /* number of dependencies */ int enabled; /* is item selected or not */ int prev_state; /* previous item state, used for resetting */ int group_idx; /* index of group of mutually exclusive items */ struct sel_item *next; /* items that should be shown along this item */ } select_item; select_item* create_item(char *item_name,char *description); int add_dependency(select_item *item,char *desc); void link_item(struct sel_menu *menu,select_item *item); void enable_item(struct sel_menu *parent,char *name,int len); #endif opensips-2.2.2/menuconfig/main.c000066400000000000000000000163021300170765700166070ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" FILE *output; select_menu *main_menu; WINDOW *menu_window; char *install_prefix=NULL; char *prev_prefix=NULL; int run_locally=0; /* Init all the menus. Logic is hardcoded */ int init_main_menu(void) { select_menu *aux; #if MENUCONFIG_HAVE_SOURCES > 0 main_menu = init_menu(CONF_COMPILE_OPT,0,0); if (!main_menu) { fprintf(output,"Failed to create main menu\n"); return -1; } aux = init_menu(MAKE_INSTALL,INTERNAL_COMMAND,(run_action)run_make_install); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_sibling(main_menu,aux); aux = init_menu(MAKE_PROPER,INTERNAL_COMMAND,(run_action)run_make_proper); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_sibling(main_menu,aux); aux = init_menu(CONF_SCRIPT,0,0); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_sibling(main_menu,aux); if (gen_scripts_menu(aux) < 0) { fprintf(output,"Failed to get all script options\n"); return -1; } aux = init_menu(EXIT_SAVE_EVERYTHING,INTERNAL_COMMAND|EXIT_MENUCONFIG,(run_action)save_all_changes); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_sibling(main_menu,aux); aux = init_menu(CONF_COMPILE_FLAGS,0,0); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); aux = init_menu(CONF_EXCLUDED_MODS,0,0); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); aux = init_menu(CONF_INSTALL_PREFIX,INTERNAL_COMMAND,(run_action)read_install_prefix); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); aux = init_menu(CONF_RESET_CHANGES,INTERNAL_COMMAND, (run_action)reset_unsaved_compile); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); aux = init_menu(CONF_SAVE_CHANGES,INTERNAL_COMMAND, (run_action)dump_make_conf); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); if (parse_make_conf() < 0) { fprintf(output,"Failed to parse %s\n", MAKE_CONF_FILE); return -1; } #else if (run_locally) { main_menu = init_menu(CONF_COMPILE_OPT,0,0); if (!main_menu) { fprintf(output,"Failed to create main menu\n"); return -1; } aux = init_menu(MAKE_INSTALL,INTERNAL_COMMAND,(run_action)run_make_install); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_sibling(main_menu,aux); aux = init_menu(MAKE_PROPER,INTERNAL_COMMAND,(run_action)run_make_proper); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_sibling(main_menu,aux); aux = init_menu(CONF_SCRIPT,0,0); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_sibling(main_menu,aux); if (gen_scripts_menu(aux) < 0) { fprintf(output,"Failed to get all script options\n"); return -1; } aux = init_menu(EXIT_SAVE_EVERYTHING,INTERNAL_COMMAND|EXIT_MENUCONFIG,(run_action)save_all_changes); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_sibling(main_menu,aux); aux = init_menu(CONF_COMPILE_FLAGS,0,0); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); aux = init_menu(CONF_EXCLUDED_MODS,0,0); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); aux = init_menu(CONF_INSTALL_PREFIX,INTERNAL_COMMAND,(run_action)read_install_prefix); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); aux = init_menu(CONF_RESET_CHANGES,INTERNAL_COMMAND, (run_action)reset_unsaved_compile); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); aux = init_menu(CONF_SAVE_CHANGES,INTERNAL_COMMAND, (run_action)dump_make_conf); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_child(main_menu,aux); if (parse_make_conf() < 0) { fprintf(output,"Failed to parse %s\n", MAKE_CONF_FILE); return -1; } } else { main_menu = init_menu(CONF_SCRIPT,0,0); if (!main_menu) { fprintf(output,"Failed to create menu\n"); return -1; } if (gen_scripts_menu(main_menu) < 0) { fprintf(output,"Failed to get all script options\n"); return -1; } aux = init_menu(EXIT_SAVE_EVERYTHING,INTERNAL_COMMAND|EXIT_MENUCONFIG,(run_action)save_all_changes); if (!aux) { fprintf(output,"Failed to create menu\n"); return -1; } link_sibling(main_menu,aux); } #endif return 0; } int main(int argc,char **argv) { int ret=0; /* Open debugging output file */ output = fopen("curses.out","w"); if (output == NULL) { fprintf(stderr,"Error opening output file\n"); return -1; } if (argc > 1 && memcmp(argv[1],"--local",7) == 0) { run_locally=1; fprintf(output,"Running in local mode\n"); } /* Initialize ncurses */ if ( initscr() == NULL ) { fprintf(stderr, "Error initialising ncurses.\n"); return-1; } fprintf(output,"Initialized main window\n"); set_xy_size(0); signal(SIGWINCH, set_xy_size); /* handle window resizing in xterm */ signal(SIGINT, _quit_handler); /* handle forced closured */ if (max_x < MIN_X || max_y < MIN_Y) { fprintf(output, "Terminal must be at least %d x %d.\n", MIN_X, MIN_Y); ret=-1; goto cleanup; } /* don't buffer input until the enter key is pressed */ cbreak(); /* don't echo user input to the screen */ noecho(); /* allow the use of arrow keys */ keypad(stdscr, TRUE); /* Clear anything that might be on the screen */ clear(); refresh(); /* Create & bind all menu entries */ init_main_menu(); menu_window = newwin(max_y, max_x, 0, 0); /* enable colours support */ if(has_colors() != FALSE) { start_color(); init_pair(1, COLOR_GREEN, COLOR_BLACK); init_pair(2, COLOR_BLUE, COLOR_BLACK); } /* Start drawing everything on screen */ draw_sibling_menu(main_menu); /* We got here - the user exited the menu. Check for unsaved stuff */ /* TODO */ cleanup: delwin(stdscr); endwin(); refresh(); fclose(output); return ret; } opensips-2.2.2/menuconfig/main.h000066400000000000000000000036361300170765700166220ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #ifndef _main_h_ #define _main_h_ #include "commands.h" #include "parser.h" #include "curses.h" extern FILE*output; extern int run_locally; extern select_menu *main_menu; extern char *install_prefix; extern char *prev_prefix; extern WINDOW *menu_window; #define MAKE_CONF_FILE "Makefile.conf" #define MAKE_TEMP_FILE "Makefile.conf.template" #define DEFAULT_INSTALL_PREFIX "/usr/local/" #define CONF_COMPILE_OPT "Configure Compile Options" #define CONF_EXCLUDED_MODS "Configure Excluded Modules" #define CONF_COMPILE_FLAGS "Configure Compile Flags" #define CONF_INSTALL_PREFIX "Configure Install Prefix" #define CONF_RESET_CHANGES "Reset Unsaved Changes" #define CONF_SAVE_CHANGES "Save Changes" #define MAKE_INSTALL "Compile And Install OpenSIPS" #define MAKE_PROPER "Cleanup OpenSIPS sources" #define CONF_SCRIPT "Generate OpenSIPS Script" #define CONF_RESIDENTIAL_SCRIPT "Residential Script" #define CONF_TRUNKING_SCRIPT "Trunking Script" #define CONF_LB_SCRIPT "Load-Balancer Script" #define EXIT_SAVE_EVERYTHING "Exit & Save All Changes" #endif opensips-2.2.2/menuconfig/menus.c000066400000000000000000000113751300170765700170170ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #include #include #include #include "main.h" /* Finds the menu with the passed name, * starting from menu * * returns the found menu on success, or NULL on failure */ select_menu *find_menu(char *name,select_menu *menu) { select_menu *ret; if (strcmp(name,menu->name) == 0) return menu; if (menu->child) { /* search through the children */ ret=find_menu(name,menu->child); if (ret) return ret; } /* search through the siblings */ if (menu->next_sibling) return find_menu(name,menu->next_sibling); return NULL; } /* Allocate & Init a menu with the passed name, flags, & actions * * Returns a pointer to the menu or NULL in case of failure */ select_menu *init_menu(char *menu_name,int flags,run_action action) { select_menu *ret; int len; len = strlen(menu_name); ret = malloc(sizeof(select_menu)+len+1); if (!ret) { fprintf(output,"Failed to alloc mem\n"); return 0; } memset(ret,0,sizeof(select_menu)+len+1); ret->name = (char *) (ret+1); memcpy(ret->name,menu_name,len); ret->flags = flags; if (flags & INTERNAL_COMMAND) { /* save action callback only for internall commands */ ret->action = action; } return ret; } /* Link 'to_link' to 'dest' list of siblings */ void link_sibling(select_menu *dest,select_menu *to_link) { select_menu *it; for (it=dest;it->next_sibling;it=it->next_sibling) ; it->next_sibling = to_link; to_link->prev_sibling=it; to_link->parent = dest->parent; } /* Link 'to_link' to 'dest' list of childs * also link all the childs as sibling between each other */ void link_child(select_menu *dest,select_menu *to_link) { if (dest->child == NULL) { dest->child = to_link; to_link->parent = dest; return; } else { link_sibling(dest->child,to_link); } } /* Generate menu entries for all the types of * cfg entries that have been added */ int gen_scripts_menu(select_menu *parent) { static char name_buf[128]; cfg_gen_t *it; select_menu *m1,*m2,*m3,*m4; for (it=configs;it->name;it++) { /* Menu entry will have the same name as cfg entry */ m1 = init_menu(it->name,0,0); if (!m1) { fprintf(output,"Failed to init menu\n"); return -1; } link_child(parent,m1); /* Generate Configure, Save & Generate menus and * link them as children */ strcpy(name_buf,"Configure "); strcat(name_buf,it->name); m2=init_menu(name_buf,0,0); if (!m2) { fprintf(output,"Failed to init menu\n"); return -1; } link_child(m1,m2); if (parse_defs_m4(m2,it) < 0) { fprintf(output,"Failed to parse m4 for %s\n",it->name); return -1; } strcpy(name_buf,"Save "); strcat(name_buf,it->name); m3=init_menu(name_buf,INTERNAL_COMMAND,(run_action)save_m4_def); if (!m3) { fprintf(output,"Failed to init menu\n"); return -1; } link_child(m1,m3); strcpy(name_buf,"Generate "); strcat(name_buf,it->name); m4=init_menu(name_buf,INTERNAL_COMMAND,(run_action)generate_cfg); if (!m4) { fprintf(output,"Failed to init menu\n"); return -1; } link_child(m1,m4); } return 0; } /* Returns 1 if the menu parameter is a top level menu * exiting such a menu will lead to exiting the curses app */ int is_top_menu(select_menu *menu) { select_menu *it; for (it=main_menu;it;it=it->next_sibling) if (it == menu) return 1; return 0; } /* Execute menu's associated action when the user * enters the menu */ int exec_menu_action(select_menu *menu) { int ret; if (menu->flags & INTERNAL_COMMAND) { /* run internal command, just call function */ ret=menu->action(menu,NULL); if (ret < 0) fprintf(output,"Failed to run command for menu [%s]\n",menu->name); if (menu->flags & EXIT_MENUCONFIG) { cleanup(); exit(0); } } else { if (menu->child) { /* display the children of this menu */ return draw_sibling_menu(menu->child); } else if (menu->item_list) { /* display the select items bound to this menu */ return draw_item_list(menu); } } return 0; } opensips-2.2.2/menuconfig/menus.h000066400000000000000000000041521300170765700170170ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #ifndef _menus_h_ #define _menus_h_ #include "items.h" typedef int (*run_action) (void * menu,void *arg); #define INTERNAL_COMMAND (1<<0) #define EXIT_MENUCONFIG (1<<1) typedef struct sel_menu { char *name; /* menu display name */ run_action action; /* action that should be ran when menu is entered */ int flags; /* type of menu */ select_item *item_list; /* select items that menu displays after selection */ int item_no; /* number of items in item_list. For fast detection of scrolling */ int child_changed; /* did any of the childs for this menu suffer any changes ? */ struct sel_menu *parent; /* parent of the current menu */ struct sel_menu *child; /* select menus that this menu displays after selection */ struct sel_menu *next_sibling; /* menus that should be shown along this menu */ struct sel_menu *prev_sibling; /* menus that should be shown along this menu */ } select_menu; select_menu *find_menu(char *name,select_menu *menu); select_menu *init_menu(char *menu_name,int flags,run_action action); void link_sibling(select_menu *dest,select_menu *to_link); void link_child(select_menu *dest,select_menu *to_link); int gen_scripts_menu(select_menu *parent); int exec_menu_action(select_menu *menu); int is_top_menu(select_menu *menu); #endif opensips-2.2.2/menuconfig/parser.c000066400000000000000000000225701300170765700171630ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #include #include #include #include "parser.h" #include "main.h" #define MAX_MODULE_NAME_SIZE 20 #define MENUCONFIG_CFG_PATH_LEN strlen(MENUCONFIG_CFG_PATH) static char prev_module[MAX_MODULE_NAME_SIZE]; static int prev_module_len=0; static select_item *prev_item; /* Parses a single module dependency line */ int parse_dep_line(char *line,select_menu *parent) { char *mod_name,*mod_dep,*mod_desc,*p; int name_len,desc_len,dep_len; select_item *item; mod_name = line+1; int len = strlen(line)-1; p = memchr(line,'=',len); if (!p) { fprintf(output,"Malformed dep line\n"); return -1; } name_len = p - mod_name; mod_name[name_len]=0; /* Is this still the previous module ? */ if (name_len == prev_module_len && memcmp(prev_module,mod_name,name_len) == 0) { /* Previously found module with multiple deps. * Just add the new dependency */ fprintf(output,"found prev module %s with extra deps\n",mod_name); mod_dep = p+1; dep_len = (line+len) - mod_dep; mod_dep[dep_len]=0; if (add_dependency(prev_item,mod_dep) < 0) { fprintf(output,"Failed to add dependency\n"); return -1; } } else { fprintf(output,"found new module %s\n",mod_name); /* nope, new module, get description */ mod_desc=p+1; p = memchr(mod_desc,'|',line+len-mod_desc); if (!p) { fprintf(output,"Malformed desc line\n"); return -1; } desc_len = p-mod_desc; mod_desc[desc_len]=0; item = create_item(mod_name,mod_desc); if (item == NULL) { fprintf(output,"Failed to create item\n"); return -1; } mod_dep = p+1; dep_len = (line+len) - mod_dep; mod_dep[dep_len]=0; /* Add the dependency */ if (add_dependency(item,mod_dep) < 0) { fprintf(output,"Failed to add dependency\n"); return -1; } /* And link it */ link_item(parent,item); prev_item = item; strcpy(prev_module,mod_name); prev_module_len = name_len; } return 0; } /* Parse the include modules line */ int parse_include_line(char *line,select_menu *parent) { char *p,*start=NULL,*end; int len = strlen(line),mod_len=0; int found_mod=0; p=memchr(line,'=',len); if (!p) { fprintf(output,"Malformed include line\n"); return -1; } p++; for (;*p==' ';p++) ; for (;penabled=enabled; item->prev_state=enabled; item->group_idx = *start_grp ? *group_idx : 0; if (item->group_idx && enabled) item->group_idx = -item->group_idx; link_item(parent,item); return 0; } /* Parse the install prefix line */ int parse_prefix_line(char *line,select_menu *menu) { char *p; int pref_len,len = strlen(line); install_prefix=malloc(len); if (!install_prefix) { fprintf(output,"Failed to allow mem\n"); return -1; } p=memchr(line,'=',len); if (!p) { fprintf(output,"Malformed prefix line\n"); return -1; } p++; pref_len=line+len-1-p; if (p[pref_len-1] != '/') pref_len++; install_prefix=malloc(pref_len+1); if (!install_prefix) { fprintf(output,"No more memory\n"); return -1; } memset(install_prefix,0,pref_len+1); memcpy(install_prefix,p,pref_len); if (p[pref_len-1] != '/') install_prefix[pref_len-1]='/'; /* also init the prev prefix, used for * resetting changes */ prev_prefix=install_prefix; return 0; } /* Parse an m4 defs line for a cfg entry */ #define READ_BUF_SIZE 1024 static char read_buf[READ_BUF_SIZE]; int parse_defs_m4_line(char *line,select_menu *menu) { char *start,*end,*value_start,*value_end,*desc_start; select_item *item; int len=strlen(line); len--; start=memchr(line,'`',len); if (!start) { fprintf(output,"Failed to find macro start\n"); return -1; } start++; end=memchr(start,'\'',line+len-start); if (!end) { fprintf(output,"Failed to find macro end\n"); return -1; } start[end-start]=0; value_start=memchr(end,'`',line+len-end); if (!value_start) { fprintf(output,"Failed to find macro value start\n"); return -1; } value_start++; value_end=memchr(value_start,'\'',line+len-value_start); if (!value_end) { fprintf(output,"Failed to find macro value end\n"); return -1; } desc_start=memchr(value_end,'#',line+len-value_end); if (!desc_start) { fprintf(output,"Failed to find macro description\n"); return -1; } desc_start++; line[len]=0; item = create_item(start,desc_start); if (item == NULL) { fprintf(output,"Failed to create item\n"); return -1; } if (memcmp(value_start,"yes",3)==0) { item->enabled=1; item->prev_state=1; } link_item(menu,item); return 0; } int parse_defs_m4(select_menu *curr_menu,cfg_gen_t *curr_cfg) { FILE *f; char *p; static char cfg_path[256]; if (!curr_menu || !curr_cfg) { fprintf(output,"Failed to locate menu with name [%s]\n",curr_menu->name); return -1; } memcpy(cfg_path,run_locally?"menuconfig/configs/":MENUCONFIG_CFG_PATH, run_locally?19:MENUCONFIG_CFG_PATH_LEN); memcpy(cfg_path+(run_locally?19:MENUCONFIG_CFG_PATH_LEN), curr_cfg->defs_m4,strlen(curr_cfg->defs_m4)+1); f=fopen(cfg_path,"r"); if (!f) { fprintf(output,"Failed to open [%s]",curr_cfg->defs_m4); return -1; } while ( fgets( read_buf, READ_BUF_SIZE,f) != NULL) { p=strstr(read_buf,"define"); if (!p) continue; if (parse_defs_m4_line(p,curr_menu) < 0) { fprintf(output,"Failed to parse m4 line [%s]\n",p); return -1; } } fclose(f); return 0; } enum dep_states { PARSE_DEPENDENCIES, PARSE_INCLUDE_MODULES, PARSE_COMPILE_DEFS, PARSE_PREFIX }; int parse_make_conf(void) { enum dep_states state; FILE *conf = fopen(MAKE_CONF_FILE,"r"); char *p; int defs=0; int start_grp=0, group_idx=1; if (!conf) { /* if we cannot find the Makefile.conf, try the template */ conf = fopen(MAKE_TEMP_FILE, "r"); if (!conf) { fprintf(output,"Failed to open [%s]\n",MAKE_TEMP_FILE); return -1; } } state = PARSE_DEPENDENCIES; while ( fgets ( read_buf, READ_BUF_SIZE, conf ) != NULL ) { p = read_buf; if (*p=='\n') { if (state == PARSE_COMPILE_DEFS && defs==1) state=PARSE_PREFIX; continue; } switch ((unsigned char)state) { case PARSE_DEPENDENCIES: if (*p == '#') { if (parse_dep_line(p,find_menu(CONF_EXCLUDED_MODS,main_menu)) < 0) { fprintf(output,"Failed to parse dep line [%s]\n",p); } } else if (*p == 'e') { state = PARSE_INCLUDE_MODULES; } break; case PARSE_INCLUDE_MODULES: if (parse_include_line(p,find_menu(CONF_EXCLUDED_MODS,main_menu)) < 0) { fprintf(output,"Failed to parse include line [%s]\n",p); } state = PARSE_COMPILE_DEFS; break; case PARSE_COMPILE_DEFS: if (parse_defs_line(p,find_menu(CONF_COMPILE_FLAGS,main_menu),&group_idx,&start_grp) < 0) { fprintf(output,"Failed to parse compile defs [%s]\n",p); } defs=1; break; case PARSE_PREFIX: if (parse_prefix_line(p,find_menu(CONF_INSTALL_PREFIX,main_menu)) < 0) { fprintf(output,"Failed to parse prefix line [%s]\n",p); } break; } } fclose(conf); return 0; } opensips-2.2.2/menuconfig/parser.h000066400000000000000000000027031300170765700171640ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2012-01-19 created (vlad) */ #ifndef _parser_h_ #define _parser_h_ #include "menus.h" #include "cfg.h" #define GRP_START_STR "#DEFS_GROUP_START" #define GRP_END_STR "#DEFS_GROUP_END" #define SKIP_LINE_STR "##" #define SKIP_LINE_STRL 2 int parse_dep_line(char *line,select_menu *parent); int parse_include_line(char *line,select_menu *parent); int parse_defs_line(char *line,select_menu *parent,int *group_idx,int *start_grp); int parse_prefix_line(char *line,select_menu *menu); int parse_defs_m4_line(char *line,select_menu *menu); int parse_defs_m4(select_menu *curr_menu,cfg_gen_t *curr_cfg); int parse_make_conf(); #endif opensips-2.2.2/mi/000077500000000000000000000000001300170765700137705ustar00rootroot00000000000000opensips-2.2.2/mi/attr.c000066400000000000000000000067661300170765700151250ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Attributes * \ingroup mi */ #include #include #include #include "../mem/mem.h" #include "../dprint.h" #include "attr.h" #include "fmt.h" extern char *mi_ap_buf; extern int mi_ap_buf_len; struct mi_attr *add_mi_attr(struct mi_node *node, int flags, char *name, int name_len, char *value, int value_len) { struct mi_attr *new, *p; int size_mem, name_pos, value_pos; if(!node) return NULL; if (!name) name_len=0; if (!name_len) name=0; if (!value) value_len=0; if (!value_len) value=0; if(!name && !value) return NULL; size_mem = sizeof(struct mi_attr); value_pos = name_pos = 0; if(name && (flags & MI_DUP_NAME)){ name_pos = size_mem; size_mem += name_len; } if(value && (flags & MI_DUP_VALUE)){ value_pos = size_mem; size_mem += value_len; } new = (struct mi_attr *)pkg_malloc(size_mem); if (!new) { LM_ERR("no more pkg mem (%d)\n",size_mem); return NULL; } memset(new,0,size_mem); if (name) { new->name.len = name_len; if(flags & MI_DUP_NAME){ new->name.s = ((char *)new) + name_pos; strncpy(new->name.s, name, name_len); } else{ new->name.s = name; } } if (value) { new->value.len = value_len; if(flags & MI_DUP_VALUE){ new->value.s = ((char *)new) + value_pos; strncpy(new->value.s, value, value_len); }else{ new->value.s = value; } } if(flags & MI_DUP_NAME){ name_pos = size_mem; size_mem += name_len * sizeof(char); } if(flags & MI_DUP_VALUE){ value_pos = size_mem; size_mem += value_len * sizeof(char); } if(!(node->attributes)){ new->next = NULL; return (node->attributes = new); } for(p = node->attributes ; p->next ; p = p->next); new->next = NULL; p->next = new; return new; } struct mi_attr *addf_mi_attr(struct mi_node *node, int flags, char *name, int name_len, char *fmt_val, ...) { va_list ap; char *p; int len = 0; va_start(ap, fmt_val); p = mi_print_fmt( fmt_val, ap, &len); va_end(ap); if (p==NULL) return 0; return add_mi_attr(node, flags|MI_DUP_VALUE, name, name_len, p, len); } struct mi_attr *get_mi_attr_by_name(struct mi_node *node, char *name, int len) { struct mi_attr *head; if(!node || !name || !(node->attributes)) return NULL; for(head = node->attributes ; head->next ; head = head->next) if(len == head->name.len && !strncasecmp(name, head->name.s, head->name.len)) return head; return NULL; } void del_mi_attr_list(struct mi_node *node) { struct mi_attr *p, *head; if(!node || !(node->attributes)) return; for(head = node->attributes; head ;){ p = head->next; pkg_free(head); head = p; } node->attributes = NULL; } opensips-2.2.2/mi/attr.h000066400000000000000000000027051300170765700151170ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Attributes * \ingroup mi */ #ifndef _MI_ATTR_H #define _MI_ATTR_H #include #include "../str.h" #include "tree.h" struct mi_attr{ str name; str value; struct mi_attr *next; }; struct mi_attr *add_mi_attr(struct mi_node *node, int flags, char *name, int name_len, char *value, int value_len); struct mi_attr *addf_mi_attr(struct mi_node *node, int flags, char *name, int name_len, char *fmt_val, ...); struct mi_attr *get_mi_attr_by_name(struct mi_node *node, char *name, int len); void del_mi_attr_list(struct mi_node *node); #endif opensips-2.2.2/mi/fmt.c000066400000000000000000000023521300170765700147240ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Format handling * \ingroup mi */ #include #include "../dprint.h" #include "../mem/mem.h" char *mi_fmt_buf = 0; int mi_fmt_buf_len = 0; int mi_fmt_init( unsigned int size ) { mi_fmt_buf = (char*)pkg_malloc(size); if (mi_fmt_buf==NULL) { LM_ERR("no more pkg mem\n"); return -1; } mi_fmt_buf_len = size; return 0; } opensips-2.2.2/mi/fmt.h000066400000000000000000000031421300170765700147270ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Format handling * \ingroup mi */ #ifndef _MI_FMT_H_ #define _MI_FMT_H_ #include #include /*! \brief size of the buffer used for printing the FMT */ #define DEFAULT_MI_FMT_BUF_SIZE 2048 extern char *mi_fmt_buf; extern int mi_fmt_buf_len; int mi_fmt_init( unsigned int size ); static inline char* mi_print_fmt(char *fmt, va_list ap, int *len) { int n; if (mi_fmt_buf==NULL) { if (mi_fmt_init(DEFAULT_MI_FMT_BUF_SIZE)!=0) { LM_ERR("failed to init\n"); return 0; } } n = vsnprintf( mi_fmt_buf, mi_fmt_buf_len, fmt, ap); if (n<0 || n>=mi_fmt_buf_len) { LM_ERR("formatting failed with n=%d, %s\n",n,strerror(errno)); return 0; } *len = n; return mi_fmt_buf; } #endif opensips-2.2.2/mi/mi.c000066400000000000000000000124531300170765700145460ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Attributes * \ingroup mi */ /*! * \defgroup mi OpenSIPS Management Interface * * The OpenSIPS management interface (MI) is a plugin architecture with a few different * handlers that gives access to the management interface over various transports. * * The OpenSIPS core and modules register commands to the interface at runtime. * Look into the various module documentation files for information of these * commands. * */ #include #include "../dprint.h" #include "../mem/mem.h" #include "mi.h" static struct mi_cmd* mi_cmds = 0; static int mi_cmds_no = 0; mi_flush_f *crt_flush_fnct = NULL; void *crt_flush_param = NULL; static inline int get_mi_id( char *name, int len) { int n; int i; for( n=0,i=0 ; i for module %s\n", mis[i].name,mod_name); } } return 0; } int init_mi_child(void) { int i; for ( i=0 ; i\n", mi_cmds[i].name.len,mi_cmds[i].name.s); return -1; } } return 0; } int register_mi_cmd( mi_cmd_f f, char *name, char *help, void *param, mi_child_init_f in, unsigned int flags, char* mod_name) { struct mi_cmd *cmds; int id; int len; if (f==0 || name==0) { LM_ERR("invalid params f=%p, name=%s\n", f, name); return -1; } if (flags&MI_NO_INPUT_FLAG && flags&MI_ASYNC_RPL_FLAG) { LM_ERR("invalids flags for <%s> - " "async functions must take input\n",name); } len = strlen(name); id = get_mi_id(name,len); if (lookup_mi_cmd_id( id, name, len)) { LM_ERR("command <%.*s> already registered\n", len, name); return -1; } cmds = (struct mi_cmd*)pkg_realloc( mi_cmds, (mi_cmds_no+1)*sizeof(struct mi_cmd) ); if (cmds==0) { LM_ERR("no more pkg memory\n"); return -1; } mi_cmds = cmds; mi_cmds_no++; cmds = &cmds[mi_cmds_no-1]; cmds->f = f; cmds->init_f = in; cmds->flags = flags; cmds->name.s = name; cmds->name.len = len; cmds->module.s = mod_name; cmds->module.len = strlen(mod_name); cmds->help.s = help; cmds->help.len = help ? strlen(help) : 0; cmds->id = id; cmds->param = param; return 0; } struct mi_cmd* lookup_mi_cmd( char *name, int len) { int id; id = get_mi_id(name,len); return lookup_mi_cmd_id( id, name, len); } void get_mi_cmds( struct mi_cmd** cmds, int *size) { *cmds = mi_cmds; *size = mi_cmds_no; } #define MI_HELP_STR "help mi_cmd - " \ "returns information about 'mi_cmd'" #define MI_UNKNOWN_CMD "unknown MI command" #define MI_NO_HELP "not available for this command" #define MI_MODULE_STR "by \"%.*s\" module" struct mi_root *mi_help(struct mi_root *root, void *param) { struct mi_root *rpl_tree = 0; struct mi_node *node; struct mi_node *rpl; struct mi_cmd *cmd; if (!root) { LM_ERR("invalid MI command\n"); return 0; } node = root->node.kids; if (!node || !node->value.len || !node->value.s) { rpl_tree = init_mi_tree(200, MI_SSTR(MI_OK)); if (!rpl_tree) { LM_ERR("cannot init mi tree\n"); return 0; } rpl = &rpl_tree->node; if (!add_mi_node_child(rpl, 0, "Usage", 5, MI_SSTR(MI_HELP_STR))) { LM_ERR("cannot add new child\n"); goto error; } } else { /* search the command */ cmd = lookup_mi_cmd(node->value.s, node->value.len); if (!cmd) return init_mi_tree(404, MI_SSTR(MI_UNKNOWN_CMD)); rpl_tree = init_mi_tree(200, MI_SSTR(MI_OK)); if (!rpl_tree) { LM_ERR("cannot init mi tree\n"); return 0; } rpl = &rpl_tree->node; if (!addf_mi_node_child(rpl, 0, "Help", 4, "%s", cmd->help.s ? cmd->help.s : MI_NO_HELP)) { LM_ERR("cannot add new child\n"); goto error; } if (cmd->module.len && cmd->module.s && !add_mi_node_child(rpl, 0, "Exported by", 11, cmd->module.s, cmd->module.len)) { LM_ERR("cannot add new child\n"); goto error; } } return rpl_tree; error: if (rpl_tree) free_mi_tree(rpl_tree); return 0; } opensips-2.2.2/mi/mi.h000066400000000000000000000051751300170765700145560ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Management * \ingroup mi */ #ifndef _MI_MI_H_ #define _MI_MI_H_ #include "../str.h" #include "tree.h" #define MI_ASYNC_RPL_FLAG (1<<0) #define MI_NO_INPUT_FLAG (1<<1) #define MI_ROOT_ASYNC_RPL ((struct mi_root*)-1) struct mi_handler; typedef struct mi_root* (mi_cmd_f)(struct mi_root*, void *param); typedef int (mi_child_init_f)(void); typedef void (mi_handler_f)(struct mi_root *, struct mi_handler *, int); typedef int (mi_flush_f)(void *, struct mi_root *); struct mi_handler { mi_handler_f *handler_f; void * param; }; struct mi_cmd { int id; str module; str name; str help; mi_child_init_f *init_f; mi_cmd_f *f; unsigned int flags; void *param; }; typedef struct mi_export_ { char *name; char *help; mi_cmd_f *cmd; unsigned int flags; void *param; mi_child_init_f *init_f; }mi_export_t; int register_mi_cmd( mi_cmd_f f, char *name, char *help, void *param, mi_child_init_f in, unsigned int flags, char* mod_name); int register_mi_mod( char *mod_name, mi_export_t *mis); int init_mi_child(); struct mi_cmd* lookup_mi_cmd( char *name, int len); struct mi_root *mi_help(struct mi_root *cmd, void *param); extern mi_flush_f *crt_flush_fnct; extern void *crt_flush_param; static inline struct mi_root* run_mi_cmd(struct mi_cmd *cmd, struct mi_root *t, mi_flush_f *f, void *param) { struct mi_root *ret; /* set the flush function */ crt_flush_fnct = f; crt_flush_param = param; ret = cmd->f( t, cmd->param); /* reset the flush function */ crt_flush_fnct = 0; crt_flush_param = 0; return ret; } void get_mi_cmds( struct mi_cmd** cmds, int *size); static inline int flush_mi_tree(struct mi_root *t) { if(crt_flush_fnct) return crt_flush_fnct(crt_flush_param, t); else return 0; } #endif opensips-2.2.2/mi/mi_core.c000066400000000000000000000340771300170765700155640ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * Copyright (C) 2011-2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Core * \ingroup mi */ #include #include #include #include #include #include #include "../dprint.h" #include "../globals.h" #include "../ut.h" #include "../pt.h" #include "../net/net_tcp.h" #include "../mem/mem.h" #include "../cachedb/cachedb.h" #include "../evi/event_interface.h" #include "mi.h" static str up_since_ctime; static int init_mi_uptime(void) { char *p; p = ctime(&startup_time); up_since_ctime.len = strlen(p)-1; up_since_ctime.s = (char*)pkg_malloc(up_since_ctime.len); if (up_since_ctime.s==0) { LM_ERR("no more pkg mem\n"); return -1; } memcpy(up_since_ctime.s, p , up_since_ctime.len); return 0; } static struct mi_root *mi_uptime(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct mi_node *rpl; struct mi_node *node; time_t now; char *p; rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; time(&now); p = ctime(&now); node = add_mi_node_child( rpl, MI_DUP_VALUE, MI_SSTR("Now"), p, strlen(p)-1); if (node==0) goto error; node = add_mi_node_child( rpl, 0, MI_SSTR("Up since"), up_since_ctime.s, up_since_ctime.len); if (node==0) goto error; node = addf_mi_node_child( rpl, 0, MI_SSTR("Up time"), "%lu [sec]", (unsigned long)difftime(now, startup_time) ); if (node==0) goto error; return rpl_tree; error: LM_ERR("failed to add node\n"); free_mi_tree(rpl_tree); return 0; } static struct mi_root *mi_version(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct mi_node *rpl; struct mi_node *node; rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; node = add_mi_node_child( rpl, 0, MI_SSTR("Server"), SERVER_HDR+8, SERVER_HDR_LEN-8); if (node==0) { LM_ERR("failed to add node\n"); free_mi_tree(rpl_tree); return 0; } return rpl_tree; } static struct mi_root *mi_pwd(struct mi_root *cmd, void *param) { static int max_len = 0; static char *cwd_buf = 0; struct mi_root *rpl_tree; struct mi_node *rpl; struct mi_node *node; if (cwd_buf==NULL) { max_len = pathmax(); cwd_buf = pkg_malloc(max_len); if (cwd_buf==NULL) { LM_ERR("no more pkg mem\n"); return 0; } } rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; if (getcwd(cwd_buf, max_len)==0) { LM_ERR("getcwd failed = %s\n",strerror(errno)); goto error; } node = add_mi_node_child( rpl, 0, MI_SSTR("WD"), cwd_buf,strlen(cwd_buf)); if (node==0) { LM_ERR("failed to add node\n"); goto error; } return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } static struct mi_root *mi_arg(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct mi_node *rpl; struct mi_node *node; int n; rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; for ( n=0; nnode; rpl->flags |= MI_IS_ARRAY; get_mi_cmds( &cmds, &size); for ( i=0 ; inode; rpl->flags |= MI_IS_ARRAY; for ( i=0 ; inode.kids; if (node!=NULL) { if (str2sint( &node->value, &new_level) < 0) goto out_bad_param; node = node->next; if (node && str2sint( &node->value, &pid) < 0) goto out_bad_param; /* if called without arguments, print the table and exit */ } else { rpl_tree->node.flags |= MI_IS_ARRAY; for (i = 0; i < counted_processes; i++) { node = add_mi_node_child(&rpl_tree->node, 0, MI_SSTR("Process"), 0, 0); if (node==0) goto out; p = int2str((unsigned long)pt[i].pid, &len); if (!add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("PID"), p, len)) goto out; p = sint2str((unsigned long)pt[i].log_level, &len); if (!add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Log level"), p,len)) goto out; if (!add_mi_attr( node, 0, MI_SSTR("Type"), pt[i].desc, strlen(pt[i].desc))) goto out; } return rpl_tree; } p = sint2str((long)new_level, &len); if (pid) node = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, MI_SSTR("New log level"), p, len); else node = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, MI_SSTR("New global log level"), p, len); if (node==0) goto out; if (pid) { /* convert pid to OpenSIPS id */ i = id_of_pid(pid); if (i == -1) goto out_bad_param; __set_proc_default_log_level(i, new_level); __set_proc_log_level(i, new_level); } else set_global_log_level(new_level); return rpl_tree; out_bad_param: free_mi_tree(rpl_tree); return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM)); out: free_mi_tree(rpl_tree); return NULL; } static struct mi_root *mi_cachestore(struct mi_root *cmd, void *param) { str mc_system; str attr; str value; unsigned int expires = 0; struct mi_node* node= NULL; str expires_str; if(cmd == NULL) { LM_ERR("NULL command\n"); return init_mi_tree(404, MI_SSTR("NULL command")); } node = cmd->node.kids; if(node == NULL) return init_mi_tree(404, MI_SSTR("Too few arguments")); mc_system = node->value; if(mc_system.s == NULL || mc_system.len== 0) { LM_ERR( "empty memory cache system parameter\n"); return init_mi_tree(404, MI_SSTR("Empty memory cache id")); } node = node->next; if(node == NULL) return init_mi_tree(404, MI_SSTR("Too few arguments")); attr = node->value; if(attr.s == NULL || attr.len== 0) { LM_ERR( "empty attribute name parameter\n"); return init_mi_tree(404, MI_SSTR("Empty attribute name")); } node = node->next; if(node == NULL) return init_mi_tree(404, MI_SSTR("Too few arguments")); value = node->value; if(value.s == NULL || value.len== 0) { LM_ERR( "empty value parameter\n"); return init_mi_tree(404, MI_SSTR("Empty value argument")); } /* expires parameter is not compulsory */ node = node->next; if(node!= NULL) { expires_str = node->value; if(expires_str.s == NULL || expires_str.len == 0) { LM_ERR( "empty expires parameter\n"); return init_mi_tree(404, MI_SSTR("Empty expires argument")); } if(str2int(&expires_str, &expires)< 0) { LM_ERR("wrong format for expires argument- needed int\n"); return init_mi_tree(404, MI_SSTR("Bad format for expires argument")); } node = node->next; if(node!= NULL) return init_mi_tree(404, MI_SSTR("Too many parameters")); } if(cachedb_store(&mc_system, &attr, &value,expires)< 0) { LM_ERR("cachedb_store command failed\n"); return init_mi_tree(500, MI_SSTR("Cache store command failed")); } return init_mi_tree(200, MI_SSTR(MI_OK)); } static struct mi_root *mi_cachefetch(struct mi_root *cmd, void *param) { str mc_system; str attr; str value; struct mi_node* node= NULL; struct mi_root *rpl_tree= NULL; int ret; if(cmd == NULL) { LM_ERR("NULL command\n"); return init_mi_tree(404, MI_SSTR("NULL command")); } node = cmd->node.kids; if(node == NULL) return init_mi_tree(404, MI_SSTR("Too few arguments")); mc_system = node->value; if(mc_system.s == NULL || mc_system.len== 0) { LM_ERR( "empty memory cache system parameter\n"); return init_mi_tree(404, MI_SSTR("Empty memory cache id")); } node = node->next; if(node == NULL) return init_mi_tree(404, MI_SSTR("Too few arguments")); attr = node->value; if(attr.s == NULL || attr.len== 0) { LM_ERR( "empty attribute name parameter\n"); return init_mi_tree(404,MI_SSTR( "Empty attribute name")); } node = node->next; if(node != NULL) return init_mi_tree(404, MI_SSTR("Too many arguments")); ret = cachedb_fetch(&mc_system, &attr, &value); if(ret== -1) { LM_ERR("cachedb_fetch command failed\n"); return init_mi_tree(500, MI_SSTR("Cache fetch command failed")); } rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) { if(value.s) pkg_free(value.s); return 0; } if(ret == -2 || value.s == 0 || value.len == 0) { add_mi_node_child( &rpl_tree->node, 0, 0, 0, MI_SSTR("Value not found") ); goto done; } addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%.*s = [%.*s]", attr.len, attr.s, value.len, value.s); pkg_free(value.s); done: return rpl_tree; } static struct mi_root *mi_cacheremove(struct mi_root *cmd, void *param) { str mc_system; str attr; struct mi_node* node= NULL; if(cmd == NULL) { LM_ERR("NULL command\n"); return init_mi_tree(404, MI_SSTR("NULL command")); } node = cmd->node.kids; if(node == NULL) return init_mi_tree(404, MI_SSTR("Too few arguments")); mc_system = node->value; if(mc_system.s == NULL || mc_system.len== 0) { LM_ERR( "empty memory cache system parameter\n"); return init_mi_tree(404, MI_SSTR("Empty memory cache id")); } node = node->next; if(node == NULL) return init_mi_tree(404, MI_SSTR("Too few arguments")); attr = node->value; if(attr.s == NULL || attr.len== 0) { LM_ERR( "empty attribute name parameter\n"); return init_mi_tree(404, MI_SSTR("Empty attribute name")); } node = node->next; if(node != NULL) return init_mi_tree(404, MI_SSTR("Too many parameters")); if(cachedb_remove(&mc_system, &attr)< 0) { LM_ERR("cachedb_remove command failed\n"); return init_mi_tree(500, MI_SSTR("Cache remove command failed")); } return init_mi_tree(200, MI_SSTR(MI_OK)); } static mi_export_t mi_core_cmds[] = { { "uptime", "prints various time information about OpenSIPS - " "when it started to run, for how long it runs", mi_uptime, MI_NO_INPUT_FLAG, 0, init_mi_uptime }, { "version", "prints the version string of a runningOpenSIPS", mi_version, MI_NO_INPUT_FLAG, 0, 0 }, { "pwd", "prints the working directory of OpenSIPS", mi_pwd, MI_NO_INPUT_FLAG, 0, 0 }, { "arg", "returns the full list of arguments used at startup", mi_arg, MI_NO_INPUT_FLAG, 0, 0 }, { "which", "lists all avaialbe MI commands", mi_which, MI_NO_INPUT_FLAG, 0, 0 }, { "ps", "lists all processes used by OpenSIPS", mi_ps, MI_NO_INPUT_FLAG, 0, 0 }, { "kill", "terminates OpenSIPS", mi_kill, MI_NO_INPUT_FLAG, 0, 0 }, { "log_level", "gets/sets the per process or global log level in OpenSIPS", mi_log_level, 0, 0, 0 }, #if defined(QM_MALLOC) && defined(DBG_MALLOC) { "shm_check", "complete scan of the shared memory pool " "(if any error is found, OpenSIPS will abort!)", mi_shm_check, MI_NO_INPUT_FLAG, 0, 0 }, #endif { "cache_store", "stores in a cache system a string value", mi_cachestore, 0, 0, 0 }, { "cache_fetch", "queries for a cache stored value", mi_cachefetch, 0, 0, 0 }, { "cache_remove", "removes a record from the cache system", mi_cacheremove, 0, 0, 0 }, { "event_subscribe", "subscribes an event to the Event Interface", mi_event_subscribe, 0, 0, 0 }, { "events_list", "lists all the events advertised through the " "Event Interface", mi_events_list,MI_NO_INPUT_FLAG, 0, 0 }, { "subscribers_list", "lists all the Event Interface subscribers; " "Params: [ event [ subscriber ]]", mi_subscribers_list, 0, 0, 0 }, { "list_tcp_conns", "list all ongoing TCP based connections", mi_tcp_list_conns,MI_NO_INPUT_FLAG,0, 0 }, { "help", "prints information about MI commands usage", mi_help, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; int init_mi_core(void) { if (register_mi_mod( "core", mi_core_cmds)<0) { LM_ERR("unable to register core MI cmds\n"); return -1; } return 0; } opensips-2.2.2/mi/mi_core.h000066400000000000000000000017711300170765700155640ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Core * \ingroup mi */ #ifndef _MI_MI_CORE_H_ #define _MI_MI_CORE_H_ int init_mi_core(); #endif opensips-2.2.2/mi/tree.c000066400000000000000000000132261300170765700150770ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Tree * \ingroup mi */ #include #include #include #include "../mem/mem.h" #include "../mem/shm_mem.h" #include "../dprint.h" #include "tree.h" #include "fmt.h" static int use_shm = 0; struct mi_root *init_mi_tree(unsigned int code, char *reason, int reason_len) { struct mi_root *root; if (use_shm) root = (struct mi_root *)shm_malloc(sizeof(struct mi_root)); else root = (struct mi_root *)pkg_malloc(sizeof(struct mi_root)); if (!root) { LM_ERR("no more pkg mem\n"); return NULL; } memset(root,0,sizeof(struct mi_root)); root->node.next = root->node.last = &root->node; root->node.flags = MI_NOT_COMPLETED; if (reason && reason_len) { root->reason.s = reason; root->reason.len = reason_len; } root->code = code; return root; } void free_mi_node(struct mi_node *parent) { struct mi_node *p, *q; for(p = parent->kids ; p ; ){ q = p; p = p->next; free_mi_node(q); } if (use_shm) { shm_free(parent); } else { del_mi_attr_list(parent); pkg_free(parent); } } void free_mi_tree(struct mi_root *parent) { struct mi_node *p, *q; for(p = parent->node.kids ; p ; ){ q = p; p = p->next; free_mi_node(q); } if (use_shm) shm_free(parent); else pkg_free(parent); } static inline struct mi_node *create_mi_node(char *name, int name_len, char *value, int value_len, int flags) { struct mi_node *new; int size_mem; int name_pos; int value_pos; if (!name) name_len=0; if (!name_len) name=0; if (!value) value_len=0; if (!value_len) value=0; if (!name && !value) return NULL; size_mem = sizeof(struct mi_node); value_pos = name_pos = 0; if (name && (flags & MI_DUP_NAME)){ name_pos = size_mem; size_mem += name_len; } if (value && (flags & MI_DUP_VALUE)){ value_pos = size_mem; size_mem += value_len; } if (use_shm) new = (struct mi_node *)shm_malloc(size_mem); else new = (struct mi_node *)pkg_malloc(size_mem); if(!new) { LM_ERR("no more pkg mem\n"); return NULL; } memset(new,0,size_mem); if (name) { new->name.len = name_len; if(flags & MI_DUP_NAME){ new->name.s = ((char *)new) + name_pos; strncpy(new->name.s, name, name_len); } else{ new->name.s = name; } } if (value) { new->value.len = value_len; if(flags & MI_DUP_VALUE){ new->value.s = ((char *)new) + value_pos; strncpy(new->value.s, value, value_len); }else{ new->value.s = value; } } new->last = new; new->flags = flags; return new; } static inline struct mi_node *add_next(struct mi_node *brother, char *name, int name_len, char *value, int value_len, int flags) { struct mi_node *new; if(!brother) return NULL; new = create_mi_node(name, name_len, value, value_len, flags); if(!new) return NULL; brother->last->next = new; brother->last = new; return new; } struct mi_node *add_mi_node_sibling( struct mi_node *brother, int flags, char *name, int name_len, char *value, int value_len) { return add_next(brother, name, name_len, value, value_len, flags); } struct mi_node *addf_mi_node_sibling(struct mi_node *brother, int flags, char *name, int name_len, char *fmt_val, ...) { va_list ap; char *p; int len; va_start(ap, fmt_val); p = mi_print_fmt( fmt_val, ap, &len); va_end(ap); if (p==NULL) return 0; return add_mi_node_sibling( brother, flags|MI_DUP_VALUE, name, name_len, p, len); } struct mi_node *add_mi_node_child( struct mi_node *parent, int flags, char *name, int name_len, char *value, int value_len) { if(parent->kids){ return add_next(parent->kids, name, name_len, value, value_len, flags); }else{ parent->kids = create_mi_node(name, name_len, value, value_len, flags); return parent->kids; } } struct mi_node *addf_mi_node_child(struct mi_node *parent, int flags, char *name, int name_len, char *fmt_val, ...) { va_list ap; char *p; int len; va_start(ap, fmt_val); p = mi_print_fmt( fmt_val, ap, &len); va_end(ap); if (p==NULL) return 0; return add_mi_node_child( parent, flags|MI_DUP_VALUE, name, name_len, p, len); } static int clone_mi_node(struct mi_node *org, struct mi_node *parent) { struct mi_node *p, *q; for(p = org->kids ; p ; p=p->next){ q = add_mi_node_child( parent, MI_DUP_VALUE|MI_DUP_NAME, p->name.s, p->name.len, p->value.s, p->value.len); if (q==NULL) return -1; if (clone_mi_node( p, q)!=0) return -1; } return 0; } struct mi_root* clone_mi_tree(struct mi_root *org, int shm) { struct mi_root *root; use_shm = shm?1:0; root = init_mi_tree( org->code, org->reason.s, org->reason.len); if (root==NULL) goto done; if (clone_mi_node( &(org->node), &(root->node) )!=0 ) { free_mi_tree(root); root = NULL; goto done; } done: use_shm=0; return root; } void free_shm_mi_tree(struct mi_root *parent) { use_shm = 1; free_mi_tree(parent); use_shm = 0; } opensips-2.2.2/mi/tree.h000066400000000000000000000070331300170765700151030ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-08 first version (bogdan) */ /*! * \file * \brief MI :: Tree * \ingroup mi */ #ifndef _MI_TREE_H #define _MI_TREE_H #include #include "../str.h" struct mi_node; struct mi_handler; #include "attr.h" #define MI_DUP_NAME (1<<0) #define MI_DUP_VALUE (1<<1) #define MI_NOT_COMPLETED (1<<2) #define MI_WRITTEN (1<<3) /* * Known issue when building arrays into your modules' MI trees: * +----------------------------------------------------+ * | only use MI_IS_ARRAY iff all "name" | * | attributes of each array object are identical, | * | see mi_ps() or mi_which() | * +----------------------------------------------------+ * * Why? * commit 459dffba introduced a "common denominator" optimization: * - [GOOD] reduces the amount of redundant data in all mi_json output * - [BAD] breaks all code which builds arrays of items with * non-identical "name" attributes */ #define MI_IS_ARRAY (1<<4) #define MI_OK_S "OK" #define MI_OK_LEN (sizeof(MI_OK_S)-1) #define MI_INTERNAL_ERR_S "Server Internal Error" #define MI_INTERNAL_ERR_LEN (sizeof(MI_INTERNAL_ERR_S)-1) #define MI_MISSING_PARM_S "Too few or too many arguments" #define MI_MISSING_PARM_LEN (sizeof(MI_MISSING_PARM_S)-1) #define MI_BAD_PARM_S "Bad parameter" #define MI_BAD_PARM_LEN (sizeof(MI_BAD_PARM_S)-1) #define MI_SSTR(_s) _s,(sizeof(_s)-1) #define MI_OK MI_OK_S #define MI_INTERNAL_ERR MI_INTERNAL_ERR_S #define MI_MISSING_PARM MI_MISSING_PARM_S #define MI_BAD_PARM MI_BAD_PARM_S #define MI_DATE_BUF_LEN 21 struct mi_node { str value; str name; unsigned int flags; struct mi_node *kids; struct mi_node *next; struct mi_node *last; struct mi_attr *attributes; }; struct mi_root { unsigned int code; str reason; struct mi_handler *async_hdl; struct mi_node node; }; struct mi_root *init_mi_tree(unsigned int code, char *reason, int reason_len); void free_mi_tree(struct mi_root *parent); void free_mi_node(struct mi_node *parent); struct mi_node *add_mi_node_sibling(struct mi_node *brother, int flags, char *name, int name_len, char *value, int value_len); struct mi_node *addf_mi_node_sibling(struct mi_node *brother, int flags, char *name, int name_len, char *fmt_val, ...); struct mi_node *add_mi_node_child(struct mi_node *parent, int flags, char *name, int name_len, char *value, int value_len); struct mi_node *addf_mi_node_child(struct mi_node *parent, int flags, char *name, int name_len, char *fmt_val, ...); struct mi_root* clone_mi_tree(struct mi_root *org, int shm); void free_shm_mi_tree(struct mi_root *parent); #endif opensips-2.2.2/mod_fix.c000066400000000000000000000571421300170765700151650ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Generic fixup functions for module function parameter. * - \ref FixupNameFormat */ #include #include #include "mem/mem.h" #include "str.h" #include "ut.h" #include "error.h" #include "pvar.h" #include "mod_fix.h" /*! * \page FixupNameFormat Fixup Naming format * NAMING FORMAT * === fixup functions === * + fixup_type1_type2(...) * - type1 - is the type the fist parameter gets converted to * - type2 - is the type the second parameter gets converted to * + if the parameter is missing, then use 'null' * + if the parameter is not converted, then use 'none' * * === fixup free functions === * + free_fixup_type1_type2(...) * - type1 and type2 are same as for fixup function * * === helper functions === * + functions to be used internaly for fixup/free functions * + fixup_type(...) * + fixup_free_type(...) * - type - is the type of the parameter that gets converted to/freed */ /*! \brief * helper function * Convert char* parameter to str* parameter */ int fixup_str(void** param) { str* s; s = (str*)pkg_malloc(sizeof(str)); if (!s) { LM_ERR("no more pkg memory\n"); return E_UNSPEC; } s->s = (char*)*param; s->len = strlen(s->s); *param = (void*)s; return 0; } /*! \brief * - helper function * free the str* parameter */ int fixup_free_str(void** param) { if(*param) { pkg_free(*param); *param = 0; } return 0; } /*! \brief * fixup for functions that get one parameter * - first parameter is converted to str* */ int fixup_str_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_str(param); } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to str* * - second parameter is converted to str* */ int fixup_str_str(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_str(param); } /*! \brief * fixup free for functions that get one parameter * - first parameter was converted to str* */ int fixup_free_str_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_free_str(param); } /*! \brief * fixup free for functions that get two parameters * - first parameter was converted to str* * - second parameter was converted to str* */ int fixup_free_str_str(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_free_str(param); } /*! \brief * - helper function * Convert char* parameter to unsigned int * - the input parameter must be pkg-allocated and will be freed by function * (it is how it comes from the config parser) */ int fixup_uint(void** param) { unsigned int ui; str s; s.s = (char*)*param; s.len = strlen(s.s); if(str2int(&s, &ui)==0) { pkg_free(*param); *param=(void *)(unsigned long)ui; return 0; } LM_ERR("bad number <%s>\n", (char *)(*param)); return E_CFG; } /*! \brief * fixup for functions that get one parameter * - first parameter is converted to unsigned int */ int fixup_uint_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_uint(param); } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to unsigned int * - second parameter is converted to unsigned int */ int fixup_uint_uint(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_uint(param); } /*! \brief * - helper function * Convert char* parameter to signed int * - the input parameter must be pkg-allocated and will be freed by function * (it is how it comes from the config parser) */ int fixup_sint( void** param) { int si; str s; s.s = (char*)*param; s.len = strlen(s.s); if(str2sint(&s, &si)==0) { pkg_free(*param); *param=(void *)(unsigned long)si; return 0; } LM_ERR("bad number <%s>\n", (char *)(*param)); return E_CFG; } /*! \brief * fixup for functions that get one parameter * - first parameter is converted to signed int */ int fixup_sint_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_sint(param); } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to signed int * - second parameter is converted to signed int */ int fixup_sint_sint(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_sint(param); } #if 0 /*! \brief * fixup for functions that get two parameters * - first parameter is converted to signed int * - second parameter is converted to unsigned int */ int fixup_sint_uint(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } if (param_no == 1) return fixup_sint(param); return fixup_uint(param); } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to unsigned int * - second parameter is converted to signed int */ int fixup_uint_sint(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } if (param_no == 1) return fixup_uint(param); return fixup_sint(param); } #endif /*! \brief * - helper function: Convert char* parameter to regular expression structure * - the input parameter must be pkg-allocated and will be freed by function * (it is how it comes from the config parser) */ static int fixup_regexp(void** param, int rflags) { regex_t* re; if ((re=pkg_malloc(sizeof(regex_t)))==0) { LM_ERR("no more pkg memory\n"); return E_OUT_OF_MEM; } if (regcomp(re, *param, (REG_EXTENDED|REG_ICASE|REG_NEWLINE)&(~rflags))) { pkg_free(re); LM_ERR("bad re %s\n", (char*)*param); return E_BAD_RE; } /* free string */ pkg_free(*param); /* replace it with the compiled re */ *param=re; return 0; } static int fixup_regexp_dynamic(void** param,int rflags) { gparam_p gp; int ret; regex_t* re; ret = fixup_sgp(param); if (ret < 0) return ret; gp = (gparam_p)*param; if (gp->type == GPARAM_TYPE_STR) { /* we can compile the regex right now */ if ((re=pkg_malloc(sizeof(regex_t)))==0) { LM_ERR("no more pkg memory\n"); return E_OUT_OF_MEM; } if (regcomp(re, gp->v.sval.s, (REG_EXTENDED|REG_ICASE|REG_NEWLINE)&(~rflags))) { pkg_free(re); LM_ERR("bad re %s\n", (char*)*param); return E_BAD_RE; } /* replace it with the compiled re */ gp->type=GPARAM_TYPE_REGEX; gp->v.re=re; return 0; } /* regex will be compiled at runtime */ return 0; } /*! \brief * - helper function: free the regular expression parameter */ int fixup_free_regexp(void** param) { if(*param) { regfree((regex_t*)(*param)); pkg_free(*param); *param = 0; } return 0; } /*! \brief * fixup for functions that get one parameter * - first parameter is converted to regular expression structure */ int fixup_regexp_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_regexp(param, 0); } /*! \brief * fixup for functions that get one parameter * - first parameter is converted to regular expression structure - accepts non-plaintext input */ int fixup_regexp_dynamic_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_regexp_dynamic(param, 0); } static char *re_buff=NULL; static int re_buff_len = 0; regex_t* fixup_get_regex(struct sip_msg* msg, gparam_p gp,int *do_free) { pv_value_t value; str val; regex_t* ret_re; if(gp->type==GPARAM_TYPE_REGEX) { /* pre-allocated at startup - just return it */ if (do_free) *do_free=0; return gp->v.re; } if(gp->type==GPARAM_TYPE_PVS) { if(pv_get_spec_value(msg, gp->v.pvs, &value)!=0 || value.flags&PV_VAL_NULL || !(value.flags&PV_VAL_STR)){ LM_ERR("no valid PV value found (error in scripts)\n"); return NULL; } val = value.rs; goto build_re; } if(gp->type==GPARAM_TYPE_PVE){ if(pv_printf_s( msg, gp->v.pve, &val)!=0){ LM_ERR("cannot print the PV-formatted string\n"); return NULL; } goto build_re; } return NULL; build_re: if (val.len + 1 > re_buff_len) { re_buff = pkg_realloc(re_buff,val.len + 1); if (re_buff == NULL) { LM_ERR("No more pkg \n"); return NULL; } re_buff_len = val.len + 1; } memcpy(re_buff,val.s,val.len); re_buff[val.len] = 0; if ((ret_re=pkg_malloc(sizeof(regex_t)))==0) { LM_ERR("no more pkg memory\n"); return NULL; } if (regcomp(ret_re, re_buff, (REG_EXTENDED|REG_ICASE|REG_NEWLINE))) { pkg_free(ret_re); LM_ERR("bad re %s\n", re_buff); return NULL; } if (do_free) *do_free=1; return ret_re; } /*! \brief * fixup for functions that get one parameter * - first parameter is converted to regular expression structure * where "match-any-character" operators also match a newline */ int fixup_regexpNL_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_regexp(param, REG_NEWLINE); } /** * fixup free for functions that get one parameter * - first parameter was converted to regular expression */ int fixup_free_regexp_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_free_regexp(param); } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to regular expression structure * - second parameter is not converted */ int fixup_regexp_none(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } if (param_no == 1) return fixup_regexp(param, 0); return 0; } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to regular expression structure * where "match-any-character" operators also match a newline * - second parameter is not converted */ int fixup_regexpNL_none(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } if (param_no == 1) return fixup_regexp(param, REG_NEWLINE); return 0; } /** * fixup free for functions that get two parameters * - first parameter was converted to regular expression * - second parameter was notconverted */ int fixup_free_regexp_none(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } if (param_no == 1) return fixup_free_regexp(param); return 0; } /*! \brief * - helper function: Convert char* parameter to PV spec structure */ int fixup_pvar(void **param) { pv_spec_t *sp; str s; sp = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); if (sp == 0) { LM_ERR("no pkg memory left\n"); return E_UNSPEC; } s.s = (char*)*param; s.len = strlen(s.s); if (pv_parse_spec(&s, sp) == 0) { LM_ERR("parsing of pseudo variable %s failed!\n", (char*)*param); pkg_free(sp); return E_UNSPEC; } if (sp->type == PVT_NULL) { LM_ERR("bad pseudo variable\n"); pkg_free(sp); return E_UNSPEC; } *param = (void*)sp; return 0; } /*! \brief * - helper function: free the PV parameter */ int fixup_free_pvar(void** param) { if (*param) { pv_spec_free((pv_spec_t*)*param); } return 0; } /*! \brief * fixup for functions that get one parameter * - first parameter is converted to PV spec */ int fixup_pvar_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_pvar(param); } /*! \brief * fixup free for functions that get one parameter * - first parameter was converted to PV spec */ int fixup_free_pvar_null(void** param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_free_pvar(param); } /*! \brief * fixup for functions that get two parameters * - both parameters are converted to PV spec */ int fixup_pvar_pvar(void** param, int param_no) { if (param_no == 1) { return fixup_pvar(param); } if (param_no != 2) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_pvar(param); } /*! \brief * fixup free for functions that get two parameters * - both parameters were converted to PV spec */ int fixup_free_pvar_pvar(void** param, int param_no) { if(param_no == 1) { return fixup_free_pvar(param); } if (param_no != 2) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_free_pvar(param); } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to PV spec * - second parameter is converted to str* */ int fixup_pvar_str(void** param, int param_no) { if (param_no == 1) { return fixup_pvar(param); } if (param_no != 2) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_str(param); } /*! \brief * fixup free for functions that get two parameters * - first parameter was converted to PV spec * - second parameter was converted to str* */ int fixup_free_pvar_str(void** param, int param_no) { if(param_no == 1) { return fixup_free_pvar(param); } if (param_no != 2) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_free_str(param); } /*! \brief * fixup for functions that get three parameters * - first parameter is converted to PV spec * - second parameter is converted to str* * - third parameter is converted to str* */ int fixup_pvar_str_str(void** param, int param_no) { if (param_no == 1) { return fixup_pvar(param); } if (param_no != 2 && param_no != 3) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_str(param); } /*! \brief * fixup free for functions that get three parameters * - first parameter was converted to PV spec * - second parameter was converted to str* * - third parameter was converted to str* */ int fixup_free_pvar_str_str(void** param, int param_no) { if(param_no == 1) { return fixup_free_pvar(param); } if (param_no != 2 && param_no != 3) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_free_str(param); } /*! \brief * - helper function * Convert char* parameter to gparam_t (int or PV) */ int fixup_igp(void** param) { str s; gparam_p gp = NULL; gp = (gparam_p)pkg_malloc(sizeof(gparam_t)); if(gp == NULL) { LM_ERR("no more memory\n"); return E_UNSPEC; } memset(gp, 0, sizeof(gparam_t)); s.s = (char*)*param; s.len = strlen(s.s); if(s.s[0]==PV_MARKER) { gp->type = GPARAM_TYPE_PVS; gp->v.pvs = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); if (gp->v.pvs == NULL) { LM_ERR("no pkg memory left for pv_spec_t\n"); pkg_free(gp); return E_UNSPEC; } if(pv_parse_spec(&s, gp->v.pvs)==NULL) { LM_ERR("Unsupported User Field identifier\n"); pkg_free(gp->v.pvs); pkg_free(gp); return E_UNSPEC; } } else { gp->type = GPARAM_TYPE_INT; if(str2sint(&s, &gp->v.ival) != 0) { LM_ERR("Bad number <%s>\n", (char*)(*param)); return E_UNSPEC; } } *param = (void*)gp; return 0; } int fixup_sgp(void** param) { str s; gparam_p gp = NULL; gp = (gparam_p)pkg_malloc(sizeof(gparam_t)); if(gp == NULL) { LM_ERR("no more memory\n"); return E_UNSPEC; } memset(gp, 0, sizeof(gparam_t)); s.s = (char*)*param; s.len = strlen(s.s); if(s.s[0]==PV_MARKER) { gp->type = GPARAM_TYPE_PVS; gp->v.pvs = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); if (gp->v.pvs == NULL) { LM_ERR("no pkg memory left for pv_spec_t\n"); pkg_free(gp); return E_UNSPEC; } if(pv_parse_spec(&s, gp->v.pvs)==NULL) { LM_ERR("Unsupported User Field identifier\n"); pkg_free(gp->v.pvs); pkg_free(gp); return E_UNSPEC; } } else { gp->type = GPARAM_TYPE_STR; gp->v.sval = s; } *param = (void*)gp; return 0; } /*! \brief * fixup for functions that get one parameter * - first parameter is converted to gparam_t (int or PV) */ int fixup_igp_null(void** param, int param_no) { if (param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_igp(param); } int fixup_sgp_null(void** param, int param_no) { if (param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_sgp(param); } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to gparam_t (int or PV) * - second parameter is converted to gparam_t (int or PV) */ int fixup_igp_igp(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_igp(param); } /*! \brief * fixup for functions that get three parameters * - first parameter is converted to gparam_t (int or PV) * - second parameter is converted to gparam_t (int or PV) * - third parameter is converted to gparam_t (int or PV) */ int fixup_igp_igp_igp(void** param, int param_no) { if (param_no < 1 || param_no > 3 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_igp(param); } int fixup_sgp_sgp(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_sgp(param); } /*! \brief * fixup for functions that get three parameters * - first parameter is converted to gparam_t (int or PV) * - second parameter is converted to PV spec * - third parameter is converted to PV spec */ int fixup_igp_pvar_pvar(void** param, int param_no) { if(param_no == 1) { return fixup_igp(param); } if (param_no != 2 && param_no != 3) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_pvar(param); } /*! \brief * fixup free for functions that get three parameters * - first parameter was converted to gparam_t (int or PV) * - second parameter was converted to PV spec * - third parameter was converted to PV spec */ int fixup_free_igp_pvar_pvar(void** param, int param_no) { if(param_no == 1) { return 0; } if (param_no != 2 && param_no != 3) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_free_pvar(param); } /*! \brief * - helper function * Return integer value from a gparam_t */ int fixup_get_ivalue(struct sip_msg* msg, gparam_p gp, int *val) { pv_value_t value; if(gp->type==GPARAM_TYPE_INT) { *val = gp->v.ival; return 0; } if(pv_get_spec_value(msg, gp->v.pvs, &value)!=0 || value.flags&PV_VAL_NULL || !(value.flags&PV_VAL_INT)) { LM_ERR("no valid PV value found (error in scripts)\n"); return -1; } *val = value.ri; return 0; } /*! \brief * - helper function * Convert char* parameter to gparam_t (str or pv_elem_t) */ int fixup_spve(void** param) { str s; gparam_p gp = NULL; gp = (gparam_p)pkg_malloc(sizeof(gparam_t)); if(gp == NULL) { LM_ERR("no more memory\n"); return E_UNSPEC; } memset(gp, 0, sizeof(gparam_t)); s.s = (char*)(*param); s.len = strlen(s.s); if(pv_parse_format(&s, &gp->v.pve)<0) { LM_ERR("wrong format[%s]\n", s.s); return E_UNSPEC; } if(gp->v.pve->spec.getf==NULL) { gp->type = GPARAM_TYPE_STR; pv_elem_free_all(gp->v.pve); gp->v.sval = s; } else { gp->type = GPARAM_TYPE_PVE; } *param = (void*)gp; return 0; } /*! \brief * fixup for functions that get one parameter * - first parameter is converted to gparam_t (str or pv_elem_t) */ int fixup_spve_null(void** param, int param_no) { if (param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_spve(param); } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to gparam_t (str or pv_elem_t) * - second parameter is converted to gparam_t (str or pv_elem_t) */ int fixup_spve_spve(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_spve(param); } /*! \brief * fixup for functions that get two parameters * - first parameter is converted to gparam_t (str or pv_elem_t) * - second parameter is converted to uint */ int fixup_spve_uint(void** param, int param_no) { if (param_no != 1 && param_no != 2 ) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } if (param_no == 1) return fixup_spve(param); return fixup_uint(param); } /*! \brief * - helper function * Return string value from a gparam_t */ int fixup_get_svalue(struct sip_msg* msg, gparam_p gp, str *val) { pv_value_t value; if(gp->type==GPARAM_TYPE_STR) { *val = gp->v.sval; return 0; } if(gp->type==GPARAM_TYPE_PVS) { if(pv_get_spec_value(msg, gp->v.pvs, &value)!=0 || value.flags&PV_VAL_NULL || !(value.flags&PV_VAL_STR)) { LM_ERR("no valid PV value found (error in scripts)\n"); return -1; } *val = value.rs; return 0; } if(gp->type==GPARAM_TYPE_PVE) { if(pv_printf_s( msg, gp->v.pve, val)!=0) { LM_ERR("cannot print the PV-formatted string\n"); return -1; } return 0; } LM_CRIT("bogus type %d in gparam\n",gp->type); return -1; } /*! \brief * - helper function * Return string and/or int value from a gparam_t */ int fixup_get_isvalue(struct sip_msg* msg, gparam_p gp, int *i_val, str *s_val, unsigned int *flags) { pv_value_t value; *flags = 0; switch(gp->type) { case GPARAM_TYPE_INT: *i_val = gp->v.ival; *flags |= GPARAM_INT_VALUE_FLAG; break; case GPARAM_TYPE_STR: *s_val = gp->v.sval; *flags |= GPARAM_STR_VALUE_FLAG; break; case GPARAM_TYPE_PVE: if(pv_printf_s( msg, gp->v.pve, s_val)!=0) { LM_ERR("cannot print the PV-formatted string\n"); return -1; } *flags |= GPARAM_STR_VALUE_FLAG; break; case GPARAM_TYPE_PVS: if(pv_get_spec_value(msg, gp->v.pvs, &value)!=0 || value.flags&PV_VAL_NULL) { LM_ERR("no valid PV value found (error in scripts)\n"); return -1; } if(value.flags&PV_VAL_INT) { *i_val = value.ri; *flags |= GPARAM_INT_VALUE_FLAG; } if(value.flags&PV_VAL_STR) { *s_val = value.rs; *flags |= GPARAM_STR_VALUE_FLAG; } break; default: LM_ERR("unexpected gp->type=[%d]\n", gp->type); return -1; } /* Let's convert to int, if possible */ if (!(*flags & GPARAM_INT_VALUE_FLAG) && (*flags & GPARAM_STR_VALUE_FLAG) && str2sint(s_val, i_val) == 0) *flags |= GPARAM_INT_VALUE_FLAG; if (!*flags) return -1; return 0; } opensips-2.2.2/mod_fix.h000066400000000000000000000073551300170765700151730ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Generic fixup functions for module function parameter. */ #ifndef _mod_fix_h_ #define _mod_fix_h_ #include "pvar.h" #include #define GPARAM_TYPE_INT 0 #define GPARAM_TYPE_STR 1 #define GPARAM_TYPE_PVS 2 #define GPARAM_TYPE_PVE 3 #define GPARAM_TYPE_FLAGS 4 #define GPARAM_TYPE_REGEX 5 #define GPARAM_INT_VALUE_FLAG (1U<<0) #define GPARAM_STR_VALUE_FLAG (1U<<1) /*! * generic parameter that holds a string, an int or a pseudo-variable */ typedef struct _gparam { int type; union { int ival; str sval; pv_spec_t *pvs; pv_elem_t *pve; regex_t *re; } v; } gparam_t, *gparam_p; int fixup_str_null(void** param, int param_no); int fixup_str_str(void** param, int param_no); int fixup_free_str_null(void** param, int param_no); int fixup_free_str_str(void** param, int param_no); int fixup_uint_null(void** param, int param_no); int fixup_uint_uint(void** param, int param_no); int fixup_sint_null(void** param, int param_no); int fixup_sint_sint(void** param, int param_no); #if 0 int fixup_sint_uint(void** param, int param_no); int fixup_uint_sint(void** param, int param_no); #endif int fixup_regexp_null(void** param, int param_no); int fixup_regexp_dynamic_null(void** param, int param_no); int fixup_regexpNL_null(void** param, int param_no); int fixup_free_regexp_null(void** param, int param_no); int fixup_regexp_none(void** param, int param_no); int fixup_regexpNL_none(void** param, int param_no); int fixup_free_regexp_none(void** param, int param_no); int fixup_free_regexp(void** param); int fixup_pvar_null(void **param, int param_no); int fixup_free_pvar_null(void** param, int param_no); int fixup_pvar_pvar(void **param, int param_no); int fixup_free_pvar_pvar(void** param, int param_no); int fixup_pvar_str(void** param, int param_no); int fixup_free_pvar_str(void** param, int param_no); int fixup_pvar_str_str(void** param, int param_no); int fixup_free_pvar_str_str(void** param, int param_no); int fixup_igp_igp(void** param, int param_no); int fixup_igp_igp_igp(void** param, int param_no); int fixup_igp_null(void** param, int param_no); int fixup_get_ivalue(struct sip_msg* msg, gparam_p gp, int *val); int fixup_igp_pvar_pvar(void** param, int param_no); int fixup_free_igp_pvar_pvar(void** param, int param_no); int fixup_spve_spve(void** param, int param_no); int fixup_spve_null(void** param, int param_no); int fixup_spve_uint(void** param, int param_no); int fixup_get_svalue(struct sip_msg* msg, gparam_p gp, str *val); int fixup_get_isvalue(struct sip_msg* msg, gparam_p gp, int *i_val, str *s_val, unsigned int *flags); regex_t* fixup_get_regex(struct sip_msg* msg, gparam_p gp,int *do_free); int fixup_spve(void** param); int fixup_pvar(void **param); int fixup_str(void **param); int fixup_uint(void** param); int fixup_sint(void** param); int fixup_igp(void** param); int fixup_sgp(void** param); int fixup_sgp_null(void** param, int param_no); int fixup_sgp_sgp(void** param, int param_no); #endif opensips-2.2.2/modparam.c000066400000000000000000000065551300170765700153420ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-20 regex support in modparam (janakj) * 2004-03-12 extra flag USE_FUNC_PARAM added to modparam type - * instead of copying the param value, a func is called (bogdan) */ /*! * \file * \brief Module parameter configuration */ #include "modparam.h" #include "dprint.h" #include "mem/mem.h" #include "ut.h" #include #include #include int set_mod_param_regex(char* regex, char* name, modparam_t type, void* val) { struct sr_module* t; param_export_t* param; regex_t preg; int mod_found, param_found, len; char* reg; int n; len = strlen(regex); reg = pkg_malloc(len + 2 + 2 + 1); if (reg == 0) { LM_ERR("no pkg memory left\n"); return -1; } reg[0] = '^'; reg[1] = '('; memcpy(reg + 2, regex, len); reg[len + 2] = ')'; reg[len + 3] = '$'; reg[len + 4] = '\0'; if (regcomp(&preg, reg, REG_EXTENDED | REG_NOSUB | REG_ICASE)) { LM_ERR("failed to compile regular expression\n"); pkg_free(reg); return -2; } mod_found = 0; for(t = modules; t; t = t->next) { if (regexec(&preg, t->exports->name, 0, 0, 0) == 0) { LM_DBG("%s matches module %s\n",regex, t->exports->name); mod_found = 1; param_found = 0; for(param=t->exports->params;param && param->name ; param++) { if (strcmp(name, param->name) == 0) { param_found = 1; if (PARAM_TYPE_MASK(param->type) == type) { LM_DBG("found <%s> in module %s [%s]\n", name, t->exports->name, t->path); if (param->type&USE_FUNC_PARAM) { n = ((param_func_t)(param->param_pointer))(type, val ); if (n<0) return -4; } else { switch(type) { case STR_PARAM: *((char**)(param->param_pointer)) = strdup((char*)val); break; case INT_PARAM: *((int*)(param->param_pointer)) = (int)(long)val; break; } } /* register any module deps imposed by this parameter */ if (add_modparam_dependencies(t, param) != 0) { LM_ERR("failed to add modparam dependencies!\n"); return E_BUG; } break; } } } if (!param || !param->name) { if (param_found) LM_ERR("type mismatch for parameter <%s> in module <%s>\n", name, t->exports->name); else LM_ERR("parameter <%s> not found in module <%s>\n", name, t->exports->name); regfree(&preg); pkg_free(reg); return -3; } } } regfree(&preg); if (!mod_found) { LM_ERR("no module matching %s found\n", regex); pkg_free(reg); return -4; } pkg_free(reg); return 0; } opensips-2.2.2/modparam.h000066400000000000000000000017021300170765700153340ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef modparam_h #define modparam_h #include "sr_module.h" int set_mod_param_regex(char* regex, char* name, modparam_t type, void* val); #endif opensips-2.2.2/modules/000077500000000000000000000000001300170765700150335ustar00rootroot00000000000000opensips-2.2.2/modules/aaa_radius/000077500000000000000000000000001300170765700171245ustar00rootroot00000000000000opensips-2.2.2/modules/aaa_radius/Makefile000066400000000000000000000014501300170765700205640ustar00rootroot00000000000000# # radius module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=aaa_radius.so ETC_DIR?=../../etc/ # Comment the next two line and uncomment the following two if you want # to enable Radius support DEFS+= -I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib $(RADIUS_LIB) include ../../Makefile.modules install_module_custom: $(cfg_prefix)/$(cfg_dir) $(INSTALL_TOUCH) \ $(cfg_prefix)/$(cfg_dir)/dictionary.opensips.sample ; \ $(INSTALL_CFG) $(ETC_DIR)/dictionary.opensips \ $(cfg_prefix)/$(cfg_dir)/dictionary.opensips.sample ; \ if [ ! -f $(cfg_prefix)/$(cfg_dir)/dictionary.opensips ]; then \ mv -f $(cfg_prefix)/$(cfg_dir)/dictionary.opensips.sample \ $(cfg_prefix)/$(cfg_dir)/dictionary.opensips; \ fi; \ opensips-2.2.2/modules/aaa_radius/README000066400000000000000000000250501300170765700200060ustar00rootroot00000000000000AAA RADIUS MODULE Irina-Maria Stanescu OpenSIPS Solutions Edited by Irina-Maria Stanescu Copyright © 2009 Irina-Maria Stanescu Copyright © 2009 Voice Sistem SRL Revision History Revision $Revision: 5908 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. sets (string) 1.3.2. radius_config (string) 1.3.3. syslog_name (string) 1.3.4. fetch_all_values (integer) 1.4. Exported Functions 1.4.1. radius_send_auth(input_set_name, output_set_name) 1.4.2. radius_send_acct(input_set_name) 1.5. Exported Async Functions 1.5.1. radius_send_auth(input_set_name, output_set_name) 1.5.2. radius_send_acct(input_set_name) 2. Using radius async 2.1. Downloading radius-client library 2.2. Applying the patch List of Examples 1.1. Set sets parameter 1.2. Set radius_config parameter 1.3. Set syslog_name parameter 1.4. Set fetch_all_values parameter 1.5. radius_send_auth usage 1.6. radius_send_acct usage 1.7. radius_send_auth usage 1.8. radius_send_acct usage 2.1. downloading the library 2.2. How to apply the patch Chapter 1. Admin Guide 1.1. Overview This module provides a Radius implementation for the AAA API from the core. It also provides two functions to be used from the script for generating custom Radius acct and auth requests. Detection and handling of SIP-AVPs from Radius replies is automatically and transparently done by the module. Since version 2.2, aaa_radius module supports asynchronous operations. But in order to use them, one must apply the patch contained by the modules/aaa_radius folder, called radius_async_support.patch.In order to do this, you must have freeradius-client sources. In order to do this you can follow the tutorial in the end of the documentation. Any module that wishes to use it has to do the following: * include aaa.h * make a bind call with a proper radius specific url 1.2. Dependencies 1.2.1. OpenSIPS Modules None. 1.2.2. External Libraries or Applications One of the following libraries must be installed before running OpenSIPS with this module loaded: * radiusclient-ng 0.5.0 or higher See http://developer.berlios.de/projects/radiusclient-ng/. * freeradius-client See http://freeradius.org/. By default, radiusclient-ng is used. To change at compile time to freeradius, uncomment the USE_FREERADIUS=1 line in main Makefile. 1.3. Exported Parameters 1.3.1. sets (string) Sets of Radius AVPs to be used when building custom RADIUS requests (set of input RADIUS AVPs) or when fetching data from the RADIUS reply (set of output RADIUS AVPs). The format for a set definition is the following: * " set_name = ( attribute_name1 = var1 [, attribute_name2 = var2 ]* ) " The left-hand side of the assignment must be an attribute name known by the RADIUS dictionary. The right-hand side of the assignment must be a script pseudo variable or a script AVP. For more information about them see CookBooks - Scripting Variables. Example 1.1. Set sets parameter ... modparam("aaa_radius","sets","set4 = ( Sip-User-ID = $avp(10) , Sip-From-Tag=$si,Sip-To-Tag=$tt ) ") ... ... modparam("aaa_radius","sets","set1 = (User-Name=$var(usr), Sip-Group = $ var(grp), Service-Type = $var(type)) ") ... ... modparam("aaa_radius","sets","set2 = (Sip-Group = $var(sipgrup)) ") ... 1.3.2. radius_config (string) Radiusclient configuration file. This parameter is optional. It must be set only if the radius_send_acct and radius_send_auth functions are used. Example 1.2. Set radius_config parameter ... modparam("aaa_radius", "radius_config", "/etc/radiusclient-ng/radiusclie nt.conf") ... 1.3.3. syslog_name (string) Enable logging of the client library to syslog, using the given log name. This parameter is optional. Radius client libraries will try to use syslog to report errors (such as problems with dictionaries) with the given ident string .If this parameter is set, then these errors are visible in syslog. Otherwise errors are hidden. By default this parameter is not set (no logging). Example 1.3. Set syslog_name parameter ... modparam("aaa_radius", "syslog_name", "aaa-radius") ... 1.3.4. fetch_all_values (integer) For the output sets, this parameter controls if all the values (for the same RADIUS AVP) should be returned (otherwise only the first value will be returned). When enabling this options, be sure that the variable you use to get the RADIUS output can store multiple values (like the AVP variables). By default this parameter is disabled (set to 0) for backward compatibility reasons. Example 1.4. Set fetch_all_values parameter ... modparam("aaa_radius", "fetch_all_values", 1) ... 1.4. Exported Functions 1.4.1. radius_send_auth(input_set_name, output_set_name) This function can be used from the script to make custom radius authentication request. The function takes two parameters. The first parameter represents the name of the set that contains the list of attributes and pvars that will form the authentication request (see the “sets†module parameter). The second parameter represents the name of the set that contains the list of attributes and pvars that will be extracted form the authentication reply (see the “sets†module parameter). The sets must be defined using the “sets†exported parameter. The function return TRUE (retcode 1) if authentication was successful, FALSE (retcode -1) if an error (any kind of error) occured during authentication processes or FALSE (retcode -2) if authentication was rejected or denied by RADIUS server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, ERROR_ROUTE and LOCAL_ROUTE. Example 1.5. radius_send_auth usage ... radius_send_auth("set1","set2"); switch ($rc) { case 1: xlog("authentication ok \n"); break; case -1: xlog("error during authentication\n"); break; case -2: xlog("authentication denied \n"); break; } ... 1.4.2. radius_send_acct(input_set_name) This function can be used from the script to make custom radius authentication request. The function takes only one parameter that represents the name of the set that contains the list of attributes and pvars that will form the accounting request. Only one set is needed as a parameter because no AVPs can be extracted from the accounting replies. The set must be defined using the "sets" exported parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, ERROR_ROUTE and LOCAL_ROUTE. Example 1.6. radius_send_acct usage ... radius_send_acct("set1"); ... 1.5. Exported Async Functions 1.5.1. radius_send_auth(input_set_name, output_set_name) This function can be used from the script to make custom radius authentication request. The function takes two parameters. The first parameter represents the name of the set that contains the list of attributes and pvars that will form the authentication request (see the “sets†module parameter). The second parameter represents the name of the set that contains the list of attributes and pvars that will be extracted form the authentication reply (see the “sets†module parameter). The sets must be defined using the “sets†exported parameter. The function return TRUE (retcode 1) if authentication was successful, FALSE (retcode -1) if an error (any kind of error) occured during authentication processes or FALSE (retcode -2) if authentication was rejected or denied by RADIUS server. Example 1.7. radius_send_auth usage ... { async( radius_send_auth("set1","set2"), resume); } route[resume] { switch ($rc) { case 1: xlog("authentication ok \n"); break; case -1: xlog("error during authentication\n"); break; case -2: xlog("authentication denied \n"); break; } ... 1.5.2. radius_send_acct(input_set_name) This function can be used from the script to make custom radius authentication request. The function takes only one parameter that represents the name of the set that contains the list of attributes and pvars that will form the accounting request. Only one set is needed as a parameter because no AVPs can be extracted from the accounting replies. The set must be defined using the "sets" exported parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, ERROR_ROUTE and LOCAL_ROUTE. Example 1.8. radius_send_acct usage ... { async( radius_send_acct("set1","set2"), resume); } route[resume] { xlog(" accounting finished\n"); } ... Chapter 2. Using radius async 2.1. Downloading radius-client library You can download the last freeRADIUS Client Library sources from here . So the first step would be to download these sources in any folder you want. In this exaple we will consider this folder generically called freeRADIUS-client. After you download the sources, extract the contents of the archive. Example 2.1. downloading the library ........ mkdir freeRADIUS-client; cd freeRADIUS-client wget ftp://ftp.freeradius.org/pub/freeradius/freeradius-client-1.1.7.tar .gz tar -xzvf freeradius-client-1.1.7.tar.gz ........ 2.2. Applying the patch After you extracted the contents of the archive, you can apply the patch called radius_async_support.patch that you can find in modules/aaa_radius/ inside OpenSIPS sources folder. You must apply this patch to the freeRADIUS-client library and after this you can install the radius library as usual using configure and make commands and free to use the library. Example 2.2. How to apply the patch ........ cd freeRADIUS-client/freeradius-client-1.1.7.tar.gz patch -p1 < /path/to/opensips/modules/aaa_radius/radius_async_support.pa tch ./configure --any-options-you-want make sudo make install ........ opensips-2.2.2/modules/aaa_radius/aaa_radius.c000066400000000000000000000436731300170765700213760ustar00rootroot00000000000000/* * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * -------- * 2009-07-20 First version (Irina Stanescu) * 2009-07-29 Second version (Irina Stanescu) - new exported functions for * custom RADIUS commands added */ /* * This is an implementation of the generic AAA Interface that also provides * via script functions the possibility to run custom RADIUS requests and * to get information from the RADIUS reply. */ #ifndef USE_FREERADIUS #include #else #include #endif #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../aaa/aaa.h" #include "../../pvar.h" #include "rad.h" #include typedef struct _map_list { pv_spec_p pv; str name; int value; struct _map_list *next; } map_list; typedef struct _rad_set_elem { str set_name; map_list *parsed; } rad_set_elem; rad_set_elem **sets = NULL; int set_size = 0; char* config_file = NULL; char* syslog_name = NULL; rc_handle *rh = NULL; DICT_ATTR *attr; static int fetch_all_values = 0; int mod_init(void); int init_radius_handle(void); void destroy(void); int aaa_radius_bind_api(aaa_prot *rad_prot); int send_auth_func(struct sip_msg* msg, str* s1, str* s2); #ifdef RADIUS_ASYNC_SUPPORT int send_auth_func_async(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, str* s1, str* s2); #endif int send_auth_fixup(void** param, int param_no); int send_acct_func(struct sip_msg* msg, str* s); #ifdef RADIUS_ASYNC_SUPPORT int send_acct_func_async(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, str *s); #endif int send_acct_fixup(void** param, int param_no); int parse_sets_func(unsigned int type, void *val); #ifdef RADIUS_ASYNC_SUPPORT struct rad_ctx { int index2; VALUE_PAIR *send; SEND_CONTEXT *ctx; }; #endif static acmd_export_t acmds[] = { #ifdef RADIUS_ASYNC_SUPPORT {"radius_send_auth", (acmd_function) send_auth_func_async, 2, send_auth_fixup}, {"radius_send_acct", (acmd_function) send_acct_func_async, 1, send_acct_fixup}, #endif {0, 0, 0, 0} }; static cmd_export_t cmds[]= { {"aaa_bind_api", (cmd_function) aaa_radius_bind_api, 0, 0, 0, 0}, {"radius_send_auth", (cmd_function) send_auth_func, 2, send_auth_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE| ERROR_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"radius_send_acct", (cmd_function) send_acct_func, 1, send_acct_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE| ERROR_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, { 0, 0, 0, 0, 0, 0} }; static param_export_t params[] = { {"sets", STR_PARAM|USE_FUNC_PARAM, parse_sets_func}, {"radius_config", STR_PARAM, &config_file}, {"syslog_name", STR_PARAM, &syslog_name}, {"fetch_all_values", INT_PARAM, &fetch_all_values}, {0, 0, 0} }; struct module_exports exports= { "aaa_radius", /* module name */ MOD_TYPE_AAA, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ acmds, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ (init_function) mod_init, /* module initialization function */ 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ 0 /* per-child init function */ }; #define CHECK_COND(cond) \ if ((cond) == 0) { \ LM_ERR("malformed modparam\n"); \ return -1; \ } #define CHECK_ALLOC(p) \ if (!(p)) { \ LM_ERR("no memory left\n"); \ return -1; \ } int parse_set_content(str content, rad_set_elem *elem) { char *p; str *s; map_list *mp; //LM_DBG("%.*s\n", content.len, content.s); p = (char*) pkg_malloc (content.len + 1); CHECK_ALLOC(p); p[content.len] = '\0'; memcpy(p, content.s, content.len); s = (str*) pkg_malloc(sizeof(str)); CHECK_ALLOC(s); for (;*p != '\0';) { mp = (map_list*) pkg_malloc (sizeof(map_list)); CHECK_ALLOC(mp); mp->next = elem->parsed; mp->pv = (pv_spec_p) pkg_malloc (sizeof(pv_spec_t)); CHECK_ALLOC(mp->pv); for (; isspace(*p); p++); CHECK_COND(*p != '\0'); mp->name.s = p; for(; isgraph(*p) && *p != '='; p++) CHECK_COND(*p != '\0'); mp->name.len = p - mp->name.s; for (; isspace(*p); p++); CHECK_COND(*p != '\0' && *p == '='); p++; //LM_DBG("%.*s\n", mp->name.len, mp->name.s); for (; isspace(*p); p++); CHECK_COND(*p != '\0' && *p == '$'); s->s = p; s->len = strlen(p); p = pv_parse_spec(s, mp->pv); CHECK_COND(p != NULL); for (; isspace(*p); p++); if (*p != '\0') { CHECK_COND(*p == ','); p++; } elem->parsed = mp; } return 0; } int parse_sets_func(unsigned int type, void *val) { rad_set_elem *elem; char *p = (char*) val, *pp = NULL; str content; int nr; elem = (rad_set_elem*) pkg_malloc (sizeof(rad_set_elem)); CHECK_ALLOC(elem); for (; isspace(*p); p++); CHECK_COND(*p != '\0'); elem->set_name.s = p; for(;isgraph(*p) && *p != '='; p++) CHECK_COND(*p != '\0'); elem->set_name.len = p - elem->set_name.s; for (; isspace(*p); p++); CHECK_COND(*p != '\0' && *p == '='); p++; for (; isspace(*p); p++); CHECK_COND(*p != '\0' && *p == '('); p++; CHECK_COND(*p != '\0'); elem->parsed = NULL; content.s = p; nr = 1; for (; *p != '\0'; p++) { if (*p == '(') nr++; if (*p == ')') pp = p, nr--; } CHECK_COND(pp && !nr); content.len = (pp - content.s) * sizeof(char); set_size++; sets = (rad_set_elem**) pkg_realloc (sets, set_size * sizeof(rad_set_elem*)); CHECK_ALLOC(sets); sets[set_size - 1] = elem; if (parse_set_content(content, elem)) { LM_ERR("malformed modparam %.*s\n",sets[set_size - 1]->set_name.len, sets[set_size - 1]->set_name.s); return -1; } return 0; } int make_send_message(struct sip_msg* msg, int index, VALUE_PAIR **send) { pv_value_t pt; map_list *mp = sets[index]->parsed; for (; mp; mp = mp->next) { if (pv_get_spec_value(msg, mp->pv, &pt) < 0) return -1; if (pt.flags & PV_VAL_INT) { //LM_DBG("%.*s---->%d---->%d---->%d\n",mp->name.len, mp->name.s, // pt.ri, mp->value, pt.flags); if (!rc_avpair_add(rh, send, ATTRID(mp->value), &pt.ri, -1, VENDOR(mp->value))) return -1; } else if (pt.flags & PV_VAL_STR) { //LM_DBG("%.*s----->%.*s---->%d---->%d---->%d\n",mp->name.len, // mp->name.s, pt.rs.len, pt.rs.s, mp->value, pt.flags, pt.rs.len); if (rc_dict_getattr(rh,mp->value)->type == PW_TYPE_IPADDR) { uint32_t ipaddr=rc_get_ipaddr(pt.rs.s); if (!rc_avpair_add(rh, send, ATTRID(mp->value), &ipaddr, -1, VENDOR(mp->value))) return -1; } else { if (!rc_avpair_add(rh, send, ATTRID(mp->value), pt.rs.s, pt.rs.len, VENDOR(mp->value))) return -1; } } } return 0; } int send_auth_func(struct sip_msg* msg, str* s1, str* s2) { int i, res; int index1 = -1, index2 = -1; map_list *mp; pv_value_t pvt; char mess[1024]; VALUE_PAIR *send = NULL, *recv = NULL, *vp = NULL; if (!rh) { if (init_radius_handle()) { LM_ERR("invalid radius handle\n"); return -1; } } for (i = 0; i < set_size; i++) { if (sets[i]->set_name.len == s1->len && !strncmp(sets[i]->set_name.s, s1->s, s1->len)) index1 = i; if (sets[i]->set_name.len == s2->len && !strncmp(sets[i]->set_name.s, s2->s, s2->len)) index2 = i; } if (index1 == -1) { LM_ERR("the first set was not found\n"); return -1; } if (index2 == -1) { LM_ERR("the second set was not found\n"); return -1; } if (make_send_message(msg, index1, &send) < 0) { LM_ERR("make message failed\n"); return -1; } res = rc_auth(rh, SIP_PORT, send, &recv, mess); if (res!=OK_RC && res!=REJECT_RC) { LM_ERR("radius authentication message failed with %s\n", (res==TIMEOUT_RC)?"TIMEOUT":((res==BADRESP_RC)?"BAD REPLY":"ERROR")); }else{ LM_DBG("radius authentication message sent\n"); } for ( mp=sets[index2]->parsed; mp ; mp = mp->next) { vp = recv; while ( (vp=rc_avpair_get(vp, ATTRID(mp->value), VENDOR(mp->value)))!=NULL ) { memset(&pvt, 0, sizeof(pv_value_t)); if (vp->type == PW_TYPE_INTEGER) { pvt.flags = PV_VAL_INT|PV_TYPE_INT; pvt.ri = vp->lvalue; } else if (vp->type == PW_TYPE_STRING) { pvt.flags = PV_VAL_STR; pvt.rs.s = vp->strvalue; pvt.rs.len = vp->lvalue; } if (pv_set_value(msg, mp->pv, (int)EQ_T, &pvt) < 0) { LM_ERR("setting avp failed....skipping\n"); } vp = fetch_all_values ? vp->next : NULL; } } vp = recv; if (attr) for(; (vp = rc_avpair_get(vp, attr->value, 0)); vp = vp->next) extract_avp(vp); if ( res!=OK_RC && res!=REJECT_RC) goto error; if (send) rc_avpair_free(send); if (recv) rc_avpair_free(recv); return (res==OK_RC)?1:-2; error: if (send) rc_avpair_free(send); if (recv) rc_avpair_free(recv); return -1; } #ifdef RADIUS_ASYNC_SUPPORT /* TODO * when timeout mechanism will be available * rc_auth_function shall be called to try another * destination if the current one has timed out * */ int resume_send_auth(int fd, struct sip_msg *msg, void *param) { int res; map_list *mp; pv_value_t pvt; struct rad_ctx *rctx; VALUE_PAIR *recv = NULL, *vp = NULL; rctx = (struct rad_ctx *)param; if (rctx == NULL) { LM_ERR("no context given\n"); return -1; } res = rc_auth_resume(&rctx->ctx, &recv); if (res == OK_RC || res == REJECT_RC) { async_status = ASYNC_DONE; } else if (res == READBLOCK_RC) { async_status = ASYNC_CONTINUE; return 1; } else { LM_ERR("radius authentication message failed with %s\n", ((res==BADRESP_RC)?"BAD REPLY":"ERROR")); goto error; } for ( mp=sets[rctx->index2]->parsed; mp ; mp = mp->next) { vp = recv; while ( (vp=rc_avpair_get(vp, ATTRID(mp->value), VENDOR(mp->value)))!=NULL ) { memset(&pvt, 0, sizeof(pv_value_t)); if (vp->type == PW_TYPE_INTEGER) { pvt.flags = PV_VAL_INT|PV_TYPE_INT; pvt.ri = vp->lvalue; } else if (vp->type == PW_TYPE_STRING) { pvt.flags = PV_VAL_STR; pvt.rs.s = vp->strvalue; pvt.rs.len = vp->lvalue; } if (pv_set_value(msg, mp->pv, (int)EQ_T, &pvt) < 0) { LM_ERR("setting avp failed....skipping\n"); } vp = fetch_all_values ? vp->next : NULL; } } vp = recv; if (attr) for(; (vp = rc_avpair_get(vp, attr->value, 0)); vp = vp->next) extract_avp(vp); if ( res!=OK_RC && res!=REJECT_RC) goto error; if (rctx->send) rc_avpair_free(rctx->send); if (recv) rc_avpair_free(recv); pkg_free(rctx); return (res==OK_RC)?1:-2; error: pkg_free(rctx); if (rctx->send) rc_avpair_free(rctx->send); if (recv) rc_avpair_free(recv); return -1; } int send_auth_func_async(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, str* s1, str* s2) { int i, res; int index1 = -1, index2 = -1; char mess[1024]; SEND_CONTEXT *ctx = 0; struct rad_ctx *rctx; VALUE_PAIR *send = NULL, *recv = NULL; if (!rh) { if (init_radius_handle()) { LM_ERR("invalid radius handle\n"); return -1; } } for (i = 0; i < set_size; i++) { if (sets[i]->set_name.len == s1->len && !strncmp(sets[i]->set_name.s, s1->s, s1->len)) index1 = i; if (sets[i]->set_name.len == s2->len && !strncmp(sets[i]->set_name.s, s2->s, s2->len)) index2 = i; } if (index1 == -1) { LM_ERR("the first set was not found\n"); return -1; } if (index2 == -1) { LM_ERR("the second set was not found\n"); return -1; } if (make_send_message(msg, index1, &send) < 0) { LM_ERR("make message failed\n"); return -1; } res = rc_auth_async(rh, SIP_PORT, send, &recv, mess, &ctx); if (res == OK_RC) { LM_DBG("radius authentication message sent\n"); rctx = pkg_malloc(sizeof(struct rad_ctx)); if (rctx == NULL) { LM_ERR("no pkg mem\n"); if (send) rc_avpair_free(send); return -1; } rctx->index2 = index2; rctx->send = send; rctx->ctx = ctx; *resume_param = rctx; *resume_f = resume_send_auth; async_status = ctx->sockfd; return 1; } LM_ERR("radius authentication message failed with ERROR\n"); if (send) rc_avpair_free(send); return -1; } #endif int send_auth_fixup(void** param, int param_no) { str *s; if (!rh) { if (init_radius_handle()) { LM_ERR("invalid radius handle\n"); return E_UNSPEC; } } s = (str*) pkg_malloc(sizeof(str)); CHECK_ALLOC(s); if (param_no == 1 || param_no == 2) { s->s = *param; s->len = strlen(s->s); *param = s; return 0; } return E_UNSPEC; } int send_acct_func(struct sip_msg* msg, str *s) { int i, index = -1; VALUE_PAIR *send = NULL; if (!rh) { if (init_radius_handle()) { LM_ERR("invalid radius handle\n"); return -1; } } for (i = 0; i < set_size; i++) { if (sets[i]->set_name.len == s->len && !strncmp(sets[i]->set_name.s, s->s, s->len)) index = i; } if (index == -1) { LM_ERR("set not found\n"); return -1; } if (make_send_message(msg, index, &send) < 0) { LM_ERR("make message failed\n"); return -1; } if (rc_acct(rh, SIP_PORT, send) != OK_RC){ if (send) rc_avpair_free(send); LM_ERR("radius accounting message failed to send\n"); return -1; } if (send) rc_avpair_free(send); return 1; } #ifdef RADIUS_ASYNC_SUPPORT /* TODO * when timeout mechanism will be available * rc_auth_function shall be called to try another * destination if the current one has timed out * */ int resume_send_acct(int fd, struct sip_msg *msg, void *param) { int res, retval; struct rad_ctx *rctx; rctx = (struct rad_ctx *)param; if (rctx == NULL) { LM_ERR("no context given\n"); return -1; } res = rc_acct_resume(&rctx->ctx); if (res == OK_RC || res == REJECT_RC) { async_status = ASYNC_DONE; retval = 1; } else if (res == READBLOCK_RC) { async_status = ASYNC_CONTINUE; retval = 1; goto exit; } else { LM_ERR("radius authentication message failed with %s\n", ((res==BADRESP_RC)?"BAD REPLY":"ERROR")); retval = -1; } if (rctx->send) rc_avpair_free(rctx->send); pkg_free(rctx); exit: return retval; } int send_acct_func_async(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, str *s) { int i, index = -1, res; VALUE_PAIR *send = NULL; SEND_CONTEXT *ctx = 0; struct rad_ctx *rctx; if (!rh) { if (init_radius_handle()) { LM_ERR("invalid radius handle\n"); return -1; } } for (i = 0; i < set_size; i++) { if (sets[i]->set_name.len == s->len && !strncmp(sets[i]->set_name.s, s->s, s->len)) index = i; } if (index == -1) { LM_ERR("set not found\n"); return -1; } if (make_send_message(msg, index, &send) < 0) { LM_ERR("make message failed\n"); return -1; } res = rc_acct_async(rh, SIP_PORT, send, &ctx); if (res == OK_RC) { LM_DBG("radius accounting message sent\n"); rctx = pkg_malloc(sizeof(struct rad_ctx)); if (rctx == NULL) { LM_ERR("no pkg mem\n"); if (send) rc_avpair_free(send); return -1; } rctx->send = send; rctx->ctx = ctx; *resume_param = rctx; *resume_f = resume_send_acct; async_status = ctx->sockfd; return 1; } if (send) rc_avpair_free(send); return -1; } #endif int send_acct_fixup(void** param, int param_no) { str *s = (str*) pkg_malloc(sizeof(str)); CHECK_ALLOC(s); if (!rh) { if (init_radius_handle()) { LM_ERR("invalid radius handle\n"); return E_UNSPEC; } } if (param_no == 1) { s->s = *param; s->len = strlen(s->s); *param = s; return 0; } return E_UNSPEC; } int init_radius_handle(void) { int i; DICT_ATTR *da; char name[256]; map_list *mp; if (!config_file) { LM_ERR("radius configuration file not set\n"); return -1; } if ( syslog_name!=NULL && syslog_name[0]!=0 ) rc_openlog(syslog_name); if (!(rh = rc_read_config(config_file))) { LM_ERR("failed to open radius config file: %s\n", config_file); return -1; } if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"))) { LM_ERR("failed to read radius dictionary\n"); return -1; } attr = rc_dict_findattr(rh, "SIP-AVP"); /* initialize values for the attributes in sets */ for (i = 0; i < set_size; i++) { mp = sets[i]->parsed; while (mp) { sprintf(name,"%.*s", mp->name.len, mp->name.s); da = rc_dict_findattr(rh, name); if (!da) { LM_ERR("attribute not found %s\n", name); return -1; } else mp->value = da->value; mp = mp->next; } } return 0; } int mod_init(void) { LM_DBG("aaa_radius module was initiated\n"); #ifdef RADIUS_ASYNC_SUPPORT LM_INFO("async support for radius enabled\n"); #else LM_INFO("no async support\n"); #endif return 0; } void destroy(void) { int i; map_list *cur, *next; for (i = 0; i < set_size; i++) { LM_DBG("%.*s\n",sets[i]->set_name.len, sets[i]->set_name.s); cur = sets[i]->parsed; while (cur) { next = cur->next; pkg_free(cur); cur = next; } pkg_free(sets[i]); } } int aaa_radius_bind_api(aaa_prot *rad_prot) { if (!rad_prot) return -1; memset(rad_prot, 0, sizeof(aaa_prot)); rad_prot->create_aaa_message = rad_create_message; rad_prot->destroy_aaa_message = rad_destroy_message; rad_prot->send_aaa_request = rad_send_message; rad_prot->init_prot = rad_init_prot; rad_prot->dictionary_find = rad_find; rad_prot->avp_add = rad_avp_add; rad_prot->avp_get = rad_avp_get; return 0; } opensips-2.2.2/modules/aaa_radius/doc/000077500000000000000000000000001300170765700176715ustar00rootroot00000000000000opensips-2.2.2/modules/aaa_radius/doc/aaa_radius.xml000066400000000000000000000022641300170765700225100ustar00rootroot00000000000000 %docentities; ]> AAA RADIUS MODULE &osipsname; Irina-Maria Stanescu &osipssolname; Irina-Maria Stanescu 2009 Irina-Maria Stanescu 2009 &voicesystem; $Revision: 5908 $ $Date$ &admin; &tutorial; opensips-2.2.2/modules/aaa_radius/doc/aaa_radius_admin.xml000066400000000000000000000247351300170765700236670ustar00rootroot00000000000000 &adminguide;

Overview This module provides a Radius implementation for the AAA API from the core. It also provides two functions to be used from the script for generating custom Radius acct and auth requests. Detection and handling of SIP-AVPs from Radius replies is automatically and transparently done by the module. Since version 2.2, aaa_radius module supports asynchronous operations. But in order to use them, one must apply the patch contained by the modules/aaa_radius folder, called radius_async_support.patch.In order to do this, you must have freeradius-client sources. In order to do this you can follow the tutorial in the end of the documentation. Any module that wishes to use it has to do the following: include aaa.h make a bind call with a proper radius specific url
Dependencies
&osips; Modules None.
External Libraries or Applications One of the following libraries must be installed before running &osips; with this module loaded: radiusclient-ng 0.5.0 or higher See http://developer.berlios.de/projects/radiusclient-ng/. freeradius-client See http://freeradius.org/. By default, radiusclient-ng is used. To change at compile time to freeradius, uncomment the USE_FREERADIUS=1 line in main Makefile.
Exported Parameters
<varname>sets (string)</varname> Sets of Radius AVPs to be used when building custom RADIUS requests (set of input RADIUS AVPs) or when fetching data from the RADIUS reply (set of output RADIUS AVPs). The format for a set definition is the following: " set_name = ( attribute_name1 = var1 [, attribute_name2 = var2 ]* ) " The left-hand side of the assignment must be an attribute name known by the RADIUS dictionary. The right-hand side of the assignment must be a script pseudo variable or a script AVP. For more information about them see CookBooks - Scripting Variables. Set <varname>sets</varname> parameter ... modparam("aaa_radius","sets","set4 = ( Sip-User-ID = $avp(10) , Sip-From-Tag=$si,Sip-To-Tag=$tt ) ") ... ... modparam("aaa_radius","sets","set1 = (User-Name=$var(usr), Sip-Group = $var(grp), Service-Type = $var(type)) ") ... ... modparam("aaa_radius","sets","set2 = (Sip-Group = $var(sipgrup)) ") ...
<varname>radius_config (string)</varname> Radiusclient configuration file. This parameter is optional. It must be set only if the radius_send_acct and radius_send_auth functions are used. Set <varname>radius_config</varname> parameter ... modparam("aaa_radius", "radius_config", "/etc/radiusclient-ng/radiusclient.conf") ...
<varname>syslog_name (string)</varname> Enable logging of the client library to syslog, using the given log name. This parameter is optional. Radius client libraries will try to use syslog to report errors (such as problems with dictionaries) with the given ident string .If this parameter is set, then these errors are visible in syslog. Otherwise errors are hidden. By default this parameter is not set (no logging). Set <varname>syslog_name</varname> parameter ... modparam("aaa_radius", "syslog_name", "aaa-radius") ...
<varname>fetch_all_values (integer)</varname> For the output sets, this parameter controls if all the values (for the same RADIUS AVP) should be returned (otherwise only the first value will be returned). When enabling this options, be sure that the variable you use to get the RADIUS output can store multiple values (like the AVP variables). By default this parameter is disabled (set to 0) for backward compatibility reasons. Set <varname>fetch_all_values</varname> parameter ... modparam("aaa_radius", "fetch_all_values", 1) ...
Exported Functions
<function moreinfo="none">radius_send_auth(input_set_name, output_set_name)</function> This function can be used from the script to make custom radius authentication request. The function takes two parameters. The first parameter represents the name of the set that contains the list of attributes and pvars that will form the authentication request (see the sets module parameter). The second parameter represents the name of the set that contains the list of attributes and pvars that will be extracted form the authentication reply (see the sets module parameter). The sets must be defined using the sets exported parameter. The function return TRUE (retcode 1) if authentication was successful, FALSE (retcode -1) if an error (any kind of error) occured during authentication processes or FALSE (retcode -2) if authentication was rejected or denied by RADIUS server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, ERROR_ROUTE and LOCAL_ROUTE. <function>radius_send_auth</function> usage ... radius_send_auth("set1","set2"); switch ($rc) { case 1: xlog("authentication ok \n"); break; case -1: xlog("error during authentication\n"); break; case -2: xlog("authentication denied \n"); break; } ...
<function moreinfo="none">radius_send_acct(input_set_name)</function> This function can be used from the script to make custom radius authentication request. The function takes only one parameter that represents the name of the set that contains the list of attributes and pvars that will form the accounting request. Only one set is needed as a parameter because no AVPs can be extracted from the accounting replies. The set must be defined using the "sets" exported parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, ERROR_ROUTE and LOCAL_ROUTE. <function>radius_send_acct</function> usage ... radius_send_acct("set1"); ...
Exported Async Functions
<function moreinfo="none">radius_send_auth(input_set_name, output_set_name)</function> This function can be used from the script to make custom radius authentication request. The function takes two parameters. The first parameter represents the name of the set that contains the list of attributes and pvars that will form the authentication request (see the sets module parameter). The second parameter represents the name of the set that contains the list of attributes and pvars that will be extracted form the authentication reply (see the sets module parameter). The sets must be defined using the sets exported parameter. The function return TRUE (retcode 1) if authentication was successful, FALSE (retcode -1) if an error (any kind of error) occured during authentication processes or FALSE (retcode -2) if authentication was rejected or denied by RADIUS server. <function>radius_send_auth</function> usage ... { async( radius_send_auth("set1","set2"), resume); } route[resume] { switch ($rc) { case 1: xlog("authentication ok \n"); break; case -1: xlog("error during authentication\n"); break; case -2: xlog("authentication denied \n"); break; } ...
<function moreinfo="none">radius_send_acct(input_set_name)</function> This function can be used from the script to make custom radius authentication request. The function takes only one parameter that represents the name of the set that contains the list of attributes and pvars that will form the accounting request. Only one set is needed as a parameter because no AVPs can be extracted from the accounting replies. The set must be defined using the "sets" exported parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, ERROR_ROUTE and LOCAL_ROUTE. <function>radius_send_acct</function> usage ... { async( radius_send_acct("set1","set2"), resume); } route[resume] { xlog(" accounting finished\n"); } ...
opensips-2.2.2/modules/aaa_radius/doc/aaa_radius_tutorial.xml000066400000000000000000000033051300170765700244300ustar00rootroot00000000000000 Using radius async
Downloading radius-client library You can download the last freeRADIUS Client Library sources from here . So the first step would be to download these sources in any folder you want. In this exaple we will consider this folder generically called freeRADIUS-client. After you download the sources, extract the contents of the archive. downloading the library ........ mkdir freeRADIUS-client; cd freeRADIUS-client wget ftp://ftp.freeradius.org/pub/freeradius/freeradius-client-1.1.7.tar.gz tar -xzvf freeradius-client-1.1.7.tar.gz ........
Applying the patch After you extracted the contents of the archive, you can apply the patch called radius_async_support.patch that you can find in modules/aaa_radius/ inside &osips; sources folder. You must apply this patch to the freeRADIUS-client library and after this you can install the radius library as usual using configure and make commands and free to use the library. How to apply the patch ........ cd freeRADIUS-client/freeradius-client-1.1.7.tar.gz patch -p1 < /path/to/opensips/modules/aaa_radius/radius_async_support.patch ./configure --any-options-you-want make sudo make install ........
opensips-2.2.2/modules/aaa_radius/rad.c000066400000000000000000000237121300170765700200430ustar00rootroot00000000000000/* * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * -------- * 2009-07-20 First version (Irina Stanescu) * 2009-08-13 Second version (Irina Stanescu) - extract_avp added */ /* * This is the Radius implementation for the generic AAA Interface. */ #ifndef USE_FREERADIUS #include #else #include #endif #ifndef REJECT_RC #define REJECT_RC 2 #endif #include "../../aaa/aaa.h" #include "../../config.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "rad.h" #include "../../ut.h" #include "../../usr_avp.h" /* Radius implementation for the init_prot callback For Radius, initialization consists of: - the url is parsed and a configuration structure is obtained - the rest field from the configuration structure is, for the radius module, a string for the path of the radius configuration file - obtain the connection handle - initialize the dictionary For Radius, the aaa_conn is actually the rc_handle resulted by reading the Radius configuration file. */ aaa_conn* rad_init_prot(str* aaa_url) { rc_handle *rh; aaa_prot_config cfg; if (!aaa_url) { LM_ERR("null aaa url \n"); return NULL; } if (aaa_parse_url(aaa_url, &cfg)) { LM_ERR("aaa parse url error\n"); return NULL; } if (!(rh = rc_read_config((char*)(cfg.rest)))) { LM_ERR("failed to open radius config file: %s\n", (char*)(cfg.rest)); return NULL; } if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"))) { LM_ERR("failed to read radius dictionary\n"); return NULL; } return rh; } /* Radius implementation for the create_aaa_message callback */ aaa_message* rad_create_message(aaa_conn* rh, int flag) { aaa_message* message; if (!rh) { LM_ERR("invalid aaa connection argument\n"); return NULL; } if (flag != AAA_AUTH && flag != AAA_ACCT) { LM_ERR("invalid flag\n"); return NULL; } message = (aaa_message*) pkg_malloc (sizeof(aaa_message)); if (!message) { LM_ERR("no pkg memory left \n"); return NULL; } message->type = flag; message->avpair = NULL; message->last_found = NULL; return message; } /* Radius implementation for the destroy_aaa_message callback */ int rad_destroy_message(aaa_conn* rh, aaa_message* message) { if (!rh || ! message) { LM_ERR("invalid arguments\n"); return -1; } rc_avpair_free((VALUE_PAIR*) message->avpair); pkg_free(message); return 0; } /* Exctracts and generates AVPs from the Radius reply */ int extract_avp(VALUE_PAIR* vp) { static str names, values; int name; unsigned int r; char *p; char *end; int_str value; unsigned short flags = 0; /* empty? */ if (vp->lvalue == 0) return -1; p = vp->strvalue; end = vp->strvalue + vp->lvalue; /* get name */ if (*p == '#') { /* name is always a string */ ++p; } names.s = p; names.len = 0; while (p < end && *p != ':' && *p != '#') p++; if (names.s == p || p == end) { LM_ERR("empty AVP name\n"); return -1; } names.len = p - names.s; /* get value */ if (*p != '#') { /* string value */ flags |= AVP_VAL_STR; } values.s = ++p; values.len = end-values.s; if (values.len == 0) { LM_ERR("empty AVP value\n"); return -1; } if (!(flags&AVP_VAL_STR)) { /* convert value to integer */ if (str2int(&values,&r) != 0) { LM_ERR("invalid AVP numrical value '%.*s'\n", values.len,values.s); return -1; } value.n = (int)r; } else value.s = values; name = get_avp_id(&names); if (name < 0) { LM_ERR("cannot get AVP id (%.*s)\n", names.len, names.s); return -1; } if (add_avp( flags, name, value) < 0) { LM_ERR("unable to create a new AVP\n"); return -1; } else { LM_DBG("AVP '%.*s'='%.*s'/%d has been added\n", names.len, names.s, (flags&AVP_VAL_STR)?value.s.len:4, (flags&AVP_VAL_STR)?value.s.s:"null", (flags&AVP_VAL_STR)?0:value.n ); } return 0; } /* Radius implementation for the send_message callback */ int rad_send_message(aaa_conn* rh, aaa_message* request, aaa_message** reply) { char msg[4096]; VALUE_PAIR *vp; DICT_ATTR *attr; int result; if (!rh) { LM_ERR("invalid aaa connection argument\n"); return -1; } if (!request) { LM_ERR("invalid argument\n"); return -1; } if (request->type == AAA_AUTH) { *reply = (aaa_message*) pkg_malloc (sizeof(aaa_message)); if (!(*reply)) { LM_ERR("no pkg memory left \n"); return -1; } (*reply)->type = AAA_RECV; (*reply)->avpair = NULL; (*reply)->last_found = NULL; result = rc_auth(rh, SIP_PORT, (VALUE_PAIR*) request->avpair, (VALUE_PAIR**)(void*)&(*reply)->avpair, msg); if (result == OK_RC) { attr = rc_dict_findattr(rh, "SIP-AVP"); if (attr) { vp = (*reply)->avpair; for(; (vp = rc_avpair_get(vp, attr->value, 0)); vp = vp->next) if (extract_avp(vp)) { LM_ERR("extract_avp failed\n"); return -1; } return 0; } else { LM_ERR("SIP-AVP was not found in the radius dictionary\n"); return -1; } } else if (result == REJECT_RC) { LM_DBG("rc_auth function succeeded with result REJECT_RC\n"); return result; } else { LM_ERR("rc_auth function failed\n"); return -1; } } if (request->type == AAA_ACCT) { return rc_acct(rh, SIP_PORT, (VALUE_PAIR*) request->avpair); } LM_ERR("send message failure\n"); return -1; } /* Radius implementation for the dictionary_find callback The return value is: 0, if the name is found 1, if the name isn't found -1, if an error occurred */ int rad_find(aaa_conn* rh, aaa_map *map, int flag) { DICT_ATTR *attr_result; DICT_VALUE *val_result; DICT_VENDOR *vend_result; if (!rh) { LM_ERR("invalid aaa connection argument\n"); return -1; } if (!map) { LM_ERR("invalid argument\n"); return -1; } switch (flag) { case AAA_DICT_FIND_VAL: val_result = rc_dict_findval(rh, map->name); if (val_result) { map->value = val_result->value; return 0; } return 1; case AAA_DICT_FIND_ATTR: attr_result = rc_dict_findattr(rh, map->name); if (attr_result) { map->value = attr_result->value; map->type = attr_result->type; return 0; } return 1; case AAA_DICT_FIND_VEND: vend_result = rc_dict_findvend(rh, map->name); if (vend_result) { map->value = vend_result->vendorpec; return 0; } return 1; } LM_ERR("failure\n"); return -1; } /* Radius implementation for the avp_get callback The last parameter specifies the type of search in the AVPs list. If the flag is AAA_GET_FROM_CURRENT the search is made relative to the last_found field in the aaa_message structure. */ int rad_avp_get(aaa_conn* rh, aaa_message* message, aaa_map* attribute, void** value, int* val_length, int flag) { VALUE_PAIR* vp = NULL; if (!rh) { LM_ERR("invalid aaa connection argument\n"); return -1; } if (!message || !attribute || !value){ LM_ERR("invalid argument\n"); return -1; } if (flag != AAA_GET_FROM_START && flag != AAA_GET_FROM_CURRENT) { LM_CRIT("bug - no flag set for rad_avp_get\n"); return -1; } if (flag == AAA_GET_FROM_START) { vp = (VALUE_PAIR*) message->avpair; vp = rc_avpair_get(vp, attribute->value, 0); } if (flag == AAA_GET_FROM_CURRENT) { if (!message->last_found) { vp = (VALUE_PAIR*) message->avpair; vp = rc_avpair_get(vp, attribute->value, 0); } else { vp = (VALUE_PAIR*) message->last_found; vp = rc_avpair_get(vp->next, attribute->value, 0); } } if (vp) { switch (vp->type) { case PW_TYPE_STRING: *value = &vp->strvalue; *val_length = vp->lvalue; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: case PW_TYPE_DATE: *value = &vp->lvalue; *val_length = 4; break; default: LM_ERR("type unknown\n"); return -1; } message->last_found = vp; return 0; } else { *value = NULL; *val_length = 0; message->last_found = message->avpair; return -1; } return 0; } /* Radius implementation for the avp_add callback */ int rad_avp_add(aaa_conn* rh, aaa_message* message, aaa_map* name, void* value, int val_length, int vendor) { uint32_t int4_val; str s; if (!rh) { LM_ERR("invalid aaa connection argument\n"); return -1; } if (!message) { LM_ERR("invalid message argument\n"); return -1; } if (!name) { LM_ERR("invalid name argument\n"); return -1; } if (!value) { LM_ERR("invalid value argument\n"); return -1; } if (vendor) vendor = VENDOR(vendor); /* check if this might be a string, we might have to do some conversions */ if (val_length > -1) { if (name->type == PW_TYPE_IPADDR) { char ipstr[val_length + 1]; memcpy( ipstr, value, val_length); ipstr[val_length] = 0; int4_val = rc_get_ipaddr((char*)&ipstr); LM_DBG("detected TYPE_IPADDR attribute %s = %s (%u)\n", name->name, ipstr, (unsigned int)int4_val); value = (void *)&int4_val; val_length = -1; } else if (name->type == PW_TYPE_INTEGER) { LM_DBG("detected TYPE_INTEGER attribute %s = %s\n", name->name, (char*)value); s.s = (char*)value; s.len = val_length; if (str2int( &s, (unsigned int*)(void*)&int4_val) != 0 ) { LM_ERR("error converting string to integer"); return -1; } value = (void*)&int4_val; val_length = -1; } } if (rc_avpair_add (rh, (VALUE_PAIR**)(void*)&message->avpair, name->value, value, val_length, vendor)) { return 0; } LM_ERR("failure\n"); return -1; } opensips-2.2.2/modules/aaa_radius/rad.h000066400000000000000000000031161300170765700200440ustar00rootroot00000000000000/* * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * -------- * 2009-07-20 First version (Irina Stanescu) * 2009-08-13 Second version (Irina Stanescu) - extract_avp added */ #ifndef RAD_H #define RAD_H #include "../../aaa/aaa.h" aaa_conn* rad_init_prot(str* aaa_url); aaa_message* rad_create_message(aaa_conn* rh, int flag); int rad_destroy_message(aaa_conn* rh, aaa_message* message); int rad_send_message(aaa_conn* rh, aaa_message* request, aaa_message** reply); int rad_find(aaa_conn* rh, aaa_map *map, int flag); int rad_avp_get(aaa_conn* rh, aaa_message* message, aaa_map* attribute, void** value, int* val_lenth, int flag); int rad_avp_add(aaa_conn* rh, aaa_message* message, aaa_map* name, void* value, int val_length, int vendor); int extract_avp(VALUE_PAIR* vp); #endif opensips-2.2.2/modules/aaa_radius/radius_async_support.patch000066400000000000000000000526611300170765700244370ustar00rootroot00000000000000diff --git a/include/freeradius-client.h b/include/freeradius-client.h index 96c7546..6a5ec13 100644 --- a/include/freeradius-client.h +++ b/include/freeradius-client.h @@ -33,6 +33,11 @@ /* #include */ #include #include +#include + +#ifndef RADIUS_ASYNC_SUPPORT + #define RADIUS_ASYNC_SUPPORT +#endif #undef __BEGIN_DECLS #undef __END_DECLS @@ -383,6 +388,7 @@ typedef struct value_pair #define OK_RC 0 #define TIMEOUT_RC 1 #define REJECT_RC 2 +#define READBLOCK_RC 3 typedef struct send_data /* Used to pass information to sendserver() function */ { @@ -397,6 +403,26 @@ typedef struct send_data /* Used to pass information to sendserver() function */ VALUE_PAIR *receive_pairs; /* Where to place received a/v pairs */ } SEND_DATA; +typedef struct send_context /* Used to pass information to cc_aaa_receive_async() function */ +{ + int idx; //!< index to the destination that was last tried + rc_handle *rh; //!< rh a handle to parsed configuration. + char *msg; //!< will contain the concatenation of any %PW_REPLY_MESSAGE received. + unsigned type; //!< request type (accounting / authentification) + SEND_DATA *data; //!< used to pass information to sendserver() function + int again; //!< first or second pass through all destinations; + int sockfd; //!< socket to open connection + VALUE_PAIR *adt_vp; //!< internal rc_aaa parameter + struct addrinfo *auth_addr;//!< internal rc_aaa parameter + SERVER *aaaserver;//!< server description + struct sockaddr_in sinremote; //!< remote server sockaddr struct + int skip_count;//!< internal rc_aaa parameter + double start_time;//!< internal rc_aaa parameter + int request_type;//!< acct or auth + unsigned char vector[AUTH_VECTOR_LEN];//!< internal sendserver() param + char secret[MAX_SECRET_LENGTH + 1];//!< radius secret +} SEND_CONTEXT; + #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif @@ -439,13 +465,25 @@ VALUE_PAIR *rc_avpair_readin(rc_handle const *, FILE *); void rc_buildreq(rc_handle const *, SEND_DATA *, int, char *, unsigned short, char *, int, int); unsigned char rc_get_id(); int rc_auth(rc_handle *, uint32_t, VALUE_PAIR *, VALUE_PAIR **, char *); + +int rc_auth_async(rc_handle *, uint32_t, VALUE_PAIR *, VALUE_PAIR **, char *, SEND_CONTEXT**); +int rc_auth_resume(SEND_CONTEXT **, VALUE_PAIR **); + int rc_auth_proxy(rc_handle *, VALUE_PAIR *, VALUE_PAIR **, char *); int rc_acct(rc_handle *, uint32_t, VALUE_PAIR *); + +int rc_acct_async(rc_handle *, uint32_t, VALUE_PAIR *, SEND_CONTEXT **); +int rc_acct_resume(SEND_CONTEXT **); + int rc_acct_proxy(rc_handle *, VALUE_PAIR *); int rc_check(rc_handle *, char *, char *, unsigned short, char *); int rc_aaa(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **received, char *msg, int add_nas_port, int request_type); +int rc_aaa_async (rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **received, + char *msg, int add_nas_port, int request_type, SEND_CONTEXT **ctx); +int rc_aaa_receive_async(SEND_CONTEXT **ctx, VALUE_PAIR **received, int request_type); + /* clientid.c */ diff --git a/include/includes.h b/include/includes.h index 908f0e7..4b6c7d6 100644 --- a/include/includes.h +++ b/include/includes.h @@ -177,6 +177,10 @@ int sigprocmask (int, sigset_t *, sigset_t *); # endif #endif +#ifndef RADIUS_ASYNC_SUPPORT +#define RADIUS_ASYNC_SUPPORT +#endif + /* rlib/lock.c */ int do_lock_exclusive(FILE *); int do_unlock(FILE *); diff --git a/lib/buildreq.c b/lib/buildreq.c index a71b1f9..48b83e0 100644 --- a/lib/buildreq.c +++ b/lib/buildreq.c @@ -175,6 +175,234 @@ exit: return result; } +/** Builds an authentication/accounting request for port id client_port with the value_pairs send and submits it to a server; + * + * @param rh a handle to parsed configuration. + * @param client_port the client port number to use (may be zero to use any available). + * @param send a #VALUE_PAIR array of values (e.g., %PW_USER_NAME). + * @param received an allocated array of received values. + * @param msg must be an array of %PW_MAX_MSG_SIZE or %NULL; will contain the concatenation of any + * %PW_REPLY_MESSAGE received. + * @param add_nas_port if non-zero it will include %PW_NAS_PORT in sent pairs. + * @param request_type one of standard RADIUS codes (e.g., %PW_ACCESS_REQUEST). + * @param ctx the context which shall be passed to the asynchronous receive function; + * @return OK_RC on send success and populated @ctx and socket in @ctx->sockfd; + * resume shall be called + * ERROR_RC on failure + * if upper layer application detects timeout on sockfd it shall call this function + * again with the same @ctx + */ + +int rc_aaa_async (rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **received, + char *msg, int add_nas_port, int request_type, SEND_CONTEXT **ctx) +{ + SEND_DATA data; + VALUE_PAIR *adt_vp = NULL; + int result; + int i, skip_count; + int resume_send = 0; + SERVER *aaaserver; + int radius_deadtime = rc_conf_int(rh, "radius_deadtime"); + double start_time = 0; + double now = 0; + time_t dtime; + + if (*ctx != NULL) { + /* if here it means another destination is tried */ + aaaserver = (*ctx)->aaaserver; + data = *((*ctx)->data); + skip_count = (*ctx)->skip_count; + + /* resume from the next destination */ + (*ctx)->idx++; + + if (!(*ctx)->again) { + if (radius_deadtime > 0) + aaaserver->deadtime_ends[(*ctx)->idx] = + (*ctx)->start_time + radius_deadtime; + } + } else { + if (request_type != PW_ACCOUNTING_REQUEST) { + aaaserver = rc_conf_srv(rh, "authserver"); + } else { + aaaserver = rc_conf_srv(rh, "acctserver"); + } + if (aaaserver == NULL) + return ERROR_RC; + + data.send_pairs = send; + data.receive_pairs = NULL; + + if (add_nas_port != 0) { + /* + * Fill in NAS-Port + */ + if (rc_avpair_add(rh, &(data.send_pairs), PW_NAS_PORT, + &client_port, 0, 0) == NULL) + return ERROR_RC; + } + + if (request_type == PW_ACCOUNTING_REQUEST) { + /* + * Fill in Acct-Delay-Time + */ + dtime = 0; + now = rc_getctime(); + adt_vp = rc_avpair_get(data.send_pairs, PW_ACCT_DELAY_TIME, 0); + if (adt_vp == NULL) { + adt_vp = rc_avpair_add(rh, &(data.send_pairs), + PW_ACCT_DELAY_TIME, &dtime, 0, 0); + if (adt_vp == NULL) + return ERROR_RC; + start_time = now; + } else { + start_time = now - adt_vp->lvalue; + } + } + + if ((*ctx = malloc(sizeof(SEND_CONTEXT) + sizeof(SEND_DATA))) == NULL) { + rc_log(LOG_ERR, "rc_aaa_async: out of memory\n"); + return -1; + } + memset(*ctx, '\0', sizeof(SEND_CONTEXT) + sizeof(SEND_DATA)); + + (*ctx)->rh = rh; + (*ctx)->data = (SEND_DATA *)(*ctx + 1); + (*ctx)->msg = msg; + (*ctx)->idx = 0; + + skip_count = 0; + } + + if ((*ctx)->again != 1) { + result = ERROR_RC; + for (i = (*ctx)->idx; (i < aaaserver->max) && (result != OK_RC); + i++, now = rc_getctime()) { + if (aaaserver->deadtime_ends[i] != -1 && + aaaserver->deadtime_ends[i] > start_time) { + skip_count++; + continue; + } + + if (data.receive_pairs != NULL) { + rc_avpair_free(data.receive_pairs); + data.receive_pairs = NULL; + } + + rc_buildreq(rh, &data, request_type, aaaserver->name[i], + aaaserver->port[i], aaaserver->secret[i], 0, 0); + (*(*ctx)->data) = data; + + if (request_type == PW_ACCOUNTING_REQUEST) { + dtime = now - start_time; + rc_avpair_assign(adt_vp, &dtime, 0); + } + + result = rc_send_server_async (rh, &data, msg, ctx); + if (result == OK_RC) { + (*ctx)->idx = i; + (*ctx)->skip_count = skip_count; + } + } + + if (result == OK_RC) { + (*ctx)->start_time = start_time; + (*ctx)->adt_vp = adt_vp; + (*ctx)->aaaserver = aaaserver; + + return result; + } + + if (skip_count == 0) { + goto out_err; + } + + (*ctx)->again = 1; + (*ctx)->idx = 0; + } + + result = ERROR_RC; + for (i = (*ctx)->idx; (i < aaaserver->max) && (result != OK_RC); i++) { + if (aaaserver->deadtime_ends[i] != -1 || + aaaserver->deadtime_ends[i] <= start_time) { + continue; + } + + if (data.receive_pairs != NULL) { + rc_avpair_free(data.receive_pairs); + data.receive_pairs = NULL; + } + + rc_buildreq(rh, &data, request_type, aaaserver->name[i], + aaaserver->port[i], aaaserver->secret[i], 0, 0); + (*(*ctx)->data) = data; + + if (request_type == PW_ACCOUNTING_REQUEST) { + dtime = now - start_time; + rc_avpair_assign(adt_vp, &dtime, 0); + } + + result = rc_send_server_async (rh, &data, msg, ctx); + if (result == OK_RC) + (*ctx)->idx = i; + + if (result != OK_RC) + aaaserver->deadtime_ends[i] = -1; + + } + + if (result == OK_RC) { + (*ctx)->start_time = start_time; + (*ctx)->adt_vp = adt_vp; + (*ctx)->aaaserver = aaaserver; + + return result; + } + +out_err: + /* got through all entries; none OK; free ctx and exit*/ + free(*ctx); + *ctx = NULL; + + return result; +} + +/* Receives the reply from the server + * @param ctx the context that was set by rc_aaa_async function + * @param received an allocated array of received values. + * @return NULL @ctx and OK_RC(0) on success + * BLOCK_RC(3) and not NULL @ctx on EWOULDBLOCK/EAGAIN + * NULL @ctx on any other failure return code + */ +/*rc_receive async name*/ +int rc_aaa_receive_async(SEND_CONTEXT **ctx, VALUE_PAIR **received, int request_type) +{ + int i; + int result; + + if (*ctx == NULL) { + rc_log(LOG_ERR, "rc_aaa_async: context is null"); + return ERROR_RC; + } + + result = rc_receive_async(ctx); + + if (result != READBLOCK_RC) { + i=(*ctx)->idx; + (*ctx)->aaaserver->deadtime_ends[i] = -1; + if (request_type != PW_ACCOUNTING_REQUEST) { + *received = (*ctx)->data->receive_pairs; + } else { + rc_avpair_free((*ctx)->data->receive_pairs); + } + } else { + free(*ctx); + *ctx = NULL; + } + + return result; +} + /* * Function: rc_auth * @@ -193,6 +421,39 @@ int rc_auth(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR ** return rc_aaa(rh, client_port, send, received, msg, 1, PW_ACCESS_REQUEST); } +/* Builds an authentication request for port id client_port with the value_pairs send and submits it to a server + * @param rh a handle to parsed configuration. + * @param client_port the client port number to use (may be zero to use any available). + * @param send a #VALUE_PAIR array of values (e.g., %PW_USER_NAME). + * @param received an allocated array of received values. + * @param msg must be an array of %PW_MAX_MSG_SIZE or %NULL; will contain the concatenation of any + * %PW_REPLY_MESSAGE received. + * @param ctx the context which shall be passed to the asynchronous receive function. + * @return received value_pairs in @received, messages from the server in msg (if non-NULL), + * context for resume function in @ctx, sockfd in @ctx->sockfd and %OK_RC (0) on success + * negative on failure as return value. + * on failure an error code is called; function shall not be called again + * if upper layer application detects timeout on socket it shall call this function + * again with same context + */ +int rc_auth_async(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **received, + char *msg, SEND_CONTEXT **ctx) +{ + + return rc_aaa_async(rh, client_port, send, received, msg, 1, PW_ACCESS_REQUEST, ctx); +} + +/* Asynchronously receives the authentification reply from the server + * @param ctx the context that was set by rc_auth_async function + * @param received an allocated array of received values. + * @return received value_pairs in @received OK_RC(0) on success; + * BLOCK_RC and not null @ctx on EWOULDBLOCK/EAGAIN + * any other rc means failure or rejection + */ +int rc_auth_resume(SEND_CONTEXT **ctx, VALUE_PAIR ** received) { + return rc_aaa_receive_async(ctx, received, PW_ACCESS_REQUEST); +} + /* * Function: rc_auth_proxy * @@ -229,6 +490,38 @@ int rc_acct(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send) return rc_aaa(rh, client_port, send, NULL, NULL, 1, PW_ACCOUNTING_REQUEST); } +/** Builds an accounting request for port id client_port with the value_pairs at send + * + * @note NAS-IP-Address, NAS-Port and Acct-Delay-Time get filled in by this function, the rest has to be supplied. + * + * @param rh a handle to parsed configuration. + * @param client_port the client port number to use (may be zero to use any available). + * @param send a #VALUE_PAIR array of values (e.g., %PW_USER_NAME). + * @param ctx the context which shall be passed to the asynchronous receive function; + *@return received value_pairs in @received, messages from the server in msg (if non-NULL), + * context for resume function in @ctx, sockfd in @ctx->sockfd and %OK_RC (0) on success + * negative on failure as return value. + * on failure an error code is called; function shall not be called again + * if upper layer application detects timeout on socket it shall call this function + * again with same context + + */ +int rc_acct_async(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, SEND_CONTEXT **ctx) { + return rc_aaa_async(rh, client_port, send, NULL, NULL, 1, PW_ACCOUNTING_REQUEST, ctx); +} + +/* Asynchronously receives the accounting reply from the server + * @param ctx the context that was set by rc_acct_resume function + * @return NULL @ctx and OK_RC(0) on success; + * BLOCK_RC and not NULL @ctx on EWOULDBLOCK/EAGAIN + * any other rc means failure + * + */ +int rc_acct_resume(SEND_CONTEXT **ctx) { + return rc_aaa_receive_async(ctx, NULL, PW_ACCOUNTING_REQUEST); +} + + /* * Function: rc_acct_proxy * diff --git a/lib/sendserver.c b/lib/sendserver.c index bfdd9a2..dfb96a7 100644 --- a/lib/sendserver.c +++ b/lib/sendserver.c @@ -339,7 +339,7 @@ int rc_send_server (rc_handle *rh, SEND_DATA *data, char *msg) auth->length = htons ((unsigned short) total_length); } - DEBUG(LOG_ERR, "DEBUG: local %s : 0, remote %s : %u\n", + DEBUG(LOG_ERR, "DEBUG: local %s : 0, remote %s : %u\n", inet_ntoa(sinlocal.sin_addr), inet_ntoa(sinremote.sin_addr), data->svc_port); @@ -455,6 +455,260 @@ int rc_send_server (rc_handle *rh, SEND_DATA *data, char *msg) return result; } +/** Sends a request to a RADIUS server; + * + * @param rh a handle to parsed configuration + * @param data a pointer to a #SEND_DATA structure + * @param msg must be an array of %PW_MAX_MSG_SIZE or %NULL; will contain the concatenation of + * any %PW_REPLY_MESSAGE received. + * @param flags must be %AUTH or %ACCT + * @param ctx the context that is being set for the resume function + * @return %OK_RC (0) on success, %TIMEOUT_RC on timeout %REJECT_RC on acess reject, or negative + * on failure as return value. + */ +int rc_send_server_async(rc_handle *rh, SEND_DATA *data, char *msg, SEND_CONTEXT **ctx) +{ + int sockfd; + struct sockaddr_in sinlocal; + struct sockaddr_in sinremote; + AUTH_HDR *auth; + uint32_t auth_ipaddr, nas_ipaddr; + char *server_name; /* Name of server to query */ + int result = 0; + int total_length; + int sock_flags; + size_t secretlen; + char secret[MAX_SECRET_LENGTH + 1]; + unsigned char vector[AUTH_VECTOR_LEN]; + uint8_t send_buffer[BUFFER_LEN]; + unsigned discover_local_ip; + char our_addr_txt[50]; /* hold a text IP */ + char auth_addr_txt[50]; /* hold a text IP */ + VALUE_PAIR *vp; + + server_name = data->server; + if (server_name == NULL || server_name[0] == '\0') + return ERROR_RC; + + + if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE, 0)) && \ + (vp->lvalue == PW_ADMINISTRATIVE)) + { + strcpy(secret, MGMT_POLL_SECRET); + if ((auth_ipaddr = rc_get_ipaddr(server_name)) == 0) + return ERROR_RC; + } + else + { + if(data->secret != NULL) + { + strncpy(secret, data->secret, MAX_SECRET_LENGTH); + } + /* + else + { + */ + if (rc_find_server (rh, server_name, &auth_ipaddr, secret) != 0) + { + rc_log(LOG_ERR, "rc_send_server_async: unable to find server: %s", server_name); + return ERROR_RC; + } + /*}*/ + } + + DEBUG(LOG_ERR, "DEBUG: rc_send_server_async: creating socket to: %s", server_name); + + sockfd = socket (AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + { + memset (secret, '\0', sizeof (secret)); + rc_log(LOG_ERR, "rc_send_server_async: socket: %s", strerror(errno)); + return ERROR_RC; + } + + memset((char *)&sinlocal, '\0', sizeof(sinlocal)); + sinlocal.sin_family = AF_INET; + sinlocal.sin_addr.s_addr = htonl(rc_own_bind_ipaddress(rh)); + sinlocal.sin_port = htons((unsigned short) 0); + if (bind(sockfd, SA(&sinlocal), sizeof(sinlocal)) < 0) + { + close (sockfd); + memset (secret, '\0', sizeof (secret)); + rc_log(LOG_ERR, "rc_send_server_async: bind: %s: %s", server_name, strerror(errno)); + return ERROR_RC; + } + + /* set socket to nonblocking */ + sock_flags = fcntl(sockfd, F_GETFD, 0); + if (fcntl(sockfd, F_SETFL, sock_flags | O_NONBLOCK)) + return ERROR_RC; + + memset ((char *)&sinremote, '\0', sizeof(sinremote)); + sinremote.sin_family = AF_INET; + sinremote.sin_addr.s_addr = htonl (auth_ipaddr); + sinremote.sin_port = htons ((unsigned short) data->svc_port); + + /* + * Fill in NAS-IP-Address (if needed) + */ + if (rc_avpair_get(data->send_pairs, PW_NAS_IP_ADDRESS, 0) == NULL) { + if (sinlocal.sin_addr.s_addr == htonl(INADDR_ANY)) { + if (rc_get_srcaddr(SA(&sinlocal), SA(&sinremote)) != 0) { + close (sockfd); + memset (secret, '\0', sizeof (secret)); + return ERROR_RC; + } + } + nas_ipaddr = ntohl(sinlocal.sin_addr.s_addr); + rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IP_ADDRESS, + &nas_ipaddr, 0, 0); + } + + /* Build a request */ + auth = (AUTH_HDR *) send_buffer; + auth->code = data->code; + auth->id = data->seq_nbr; + + if (data->code == PW_ACCOUNTING_REQUEST) + { + total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; + + auth->length = htons ((unsigned short) total_length); + + memset((char *) auth->vector, 0, AUTH_VECTOR_LEN); + secretlen = strlen (secret); + memcpy ((char *) auth + total_length, secret, secretlen); + rc_md5_calc (vector, (unsigned char *) auth, total_length + secretlen); + memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); + } + else + { + rc_random_vector (vector); + memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); + + total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; + + auth->length = htons ((unsigned short) total_length); + } + + + DEBUG(LOG_ERR, "DEBUG: local %s : 0, remote %s : %u\n", + inet_ntoa(sinlocal.sin_addr), + inet_ntoa(sinremote.sin_addr), data->svc_port); + + sendto (sockfd, (char *) auth, (unsigned int) total_length, (int) 0, + SA(&sinremote), sizeof (struct sockaddr_in)); + + + (*ctx)->sockfd = sockfd; + (*ctx)->sinremote = sinremote; + memcpy((*ctx)->vector, vector, AUTH_VECTOR_LEN); + memcpy((*ctx)->secret, secret, MAX_SECRET_LENGTH + 1); + + return result; +} + +/** Waits for the reply from the RADIUS server asynchronously; + * if receive returns EWOULDBLOCK then resume function shall be called + * + * @param ctx the context that was set by rc_aaa_async function + * @return %OK_RC (0) on success or blocking receive, %TIMEOUT_RC + * on timeout %REJECT_RC on acess reject, or negative on failure as return value. + */ +int rc_receive_async (SEND_CONTEXT **ctx) { + int sockfd = (*ctx)->sockfd; + SEND_DATA *data = (*ctx)->data; + socklen_t salen; + int length, pos; + int result = 0; + uint8_t recv_buffer[BUFFER_LEN]; + AUTH_HDR *recv_auth; + uint8_t *attr; + char *server_name; /* Name of server to query */ + VALUE_PAIR *vp; + + server_name = (*ctx)->data->server; + if (server_name == NULL || server_name[0] == '\0') + return ERROR_RC; + + salen = sizeof((*ctx)->sinremote); + length = recvfrom (sockfd, (char *) recv_buffer, + (int) sizeof (recv_buffer), + (int) 0, SA(&(*ctx)->sinremote), &salen); + + if (length <= 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + rc_log(LOG_DEBUG, "E_WOULDBLOCK returned! Resume function shall be called\n"); + return READBLOCK_RC; + } else { + rc_log(LOG_ERR, "rc_receive_async: recvfrom: %s:%d: %s", server_name,\ + data->svc_port, strerror(errno)); + close (sockfd); + memset((*ctx)->secret, '\0', sizeof((*ctx)->secret)); + return ERROR_RC; + } + } + + recv_auth = (AUTH_HDR *)recv_buffer; + + if (length < AUTH_HDR_LEN || length < ntohs(recv_auth->length)) { + rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: reply is too short", + server_name, data->svc_port); + close(sockfd); + memset((*ctx)->secret, '\0', sizeof((*ctx)->secret)); + return ERROR_RC; + } + + result = rc_check_reply (recv_auth, BUFFER_LEN, (*ctx)->secret, + (*ctx)->vector, data->seq_nbr); + + length = ntohs(recv_auth->length) - AUTH_HDR_LEN; + if (length > 0) { + data->receive_pairs = rc_avpair_gen((*ctx)->rh, NULL, recv_auth->data, + length, 0); + } else { + data->receive_pairs = NULL; + } + + close (sockfd); + memset((*ctx)->secret, '\0', sizeof((*ctx)->secret)); + + if (result != OK_RC) return result; + + if ((*ctx)->msg) { + *((*ctx)->msg) = '\0'; + pos = 0; + vp = data->receive_pairs; + while (vp) + { + if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE, 0))) + { + strappend((*ctx)->msg, PW_MAX_MSG_SIZE, &pos, vp->strvalue); + strappend((*ctx)->msg, PW_MAX_MSG_SIZE, &pos, "\n"); + vp = vp->next; + } + } + } + + if ((recv_auth->code == PW_ACCESS_ACCEPT) || + (recv_auth->code == PW_PASSWORD_ACK) || + (recv_auth->code == PW_ACCOUNTING_RESPONSE)) + { + result = OK_RC; + } + else if ((recv_auth->code == PW_ACCESS_REJECT) || + (recv_auth->code == PW_PASSWORD_REJECT)) + { + result = REJECT_RC; + } + else + { + result = BADRESP_RC; + } + + return result; +} + /* * Function: rc_check_reply * opensips-2.2.2/modules/acc/000077500000000000000000000000001300170765700155615ustar00rootroot00000000000000opensips-2.2.2/modules/acc/Makefile000066400000000000000000000006531300170765700172250ustar00rootroot00000000000000# $Id$ # # acc module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=acc.so LIBS= # set ENABLE_DIAMETER_ACC to true if you wish to enable DIAMETER accounting # (uncomment next line or 'ENABLE_DIAMETER_ACC=true make all') #ENABLE_DIAMETER_ACC=true ifeq ($(ENABLE_DIAMETER_ACC),true) DEFS+=-DDIAM_ACC endif include ../../Makefile.modules opensips-2.2.2/modules/acc/README000066400000000000000000001126441300170765700164510ustar00rootroot00000000000000Acc Module Jiri Kuthan Bogdan-Andrei Iancu Ramona-Elena Modroiu Edited by Bogdan-Andrei Iancu Edited by Irina-Maria Stanescu Copyright © 2002-2003, 2003 FhG FOKUS Copyright © 2004-2009 Voice Sistem SRL Copyright © 2009-2013 OpenSIPS Solutions Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. General Example 1.2. Extra accounting 1.2.1. Overview 1.2.2. Definitions and syntax 1.2.3. How it works 1.3. Multi Call-Legs accounting 1.3.1. Overview 1.3.2. Configuration 1.3.3. Logged data 1.4. CDRs accounting 1.4.1. Overview 1.4.2. Configuration 1.4.3. How it works 1.5. Dependencies 1.5.1. OpenSIPS Modules 1.5.2. External Libraries or Applications 1.6. Exported Parameters 1.6.1. early_media (integer) 1.6.2. report_cancels (integer) 1.6.3. detect_direction (integer) 1.6.4. multi_leg_info (string) 1.6.5. multi_leg_bye_info (string) 1.6.6. log_level (integer) 1.6.7. log_facility (string) 1.6.8. log_extra (string) 1.6.9. log_extra_bye (string) 1.6.10. aaa_url (string) 1.6.11. service_type (integer) 1.6.12. aaa_extra (string) 1.6.13. aaa_extra_bye (string) 1.6.14. db_table_acc (string) 1.6.15. db_table_missed_calls (string) 1.6.16. db_url (string) 1.6.17. acc_method_column (string) 1.6.18. acc_from_tag_column (string) 1.6.19. acc_to_tag_column (string) 1.6.20. acc_callid_column (string) 1.6.21. acc_sip_code_column (string) 1.6.22. acc_sip_reason_column (string) 1.6.23. acc_time_column (string) 1.6.24. db_extra (string) 1.6.25. db_extra_bye (string) 1.6.26. diameter_client_host (string) 1.6.27. diameter_client_port (int) 1.6.28. diameter_extra (string) 1.6.29. evi_extra (string) 1.6.30. evi_extra_bye (string) 1.6.31. acc_created_avp_name (string) 1.7. Exported Functions 1.7.1. do_accounting(type, flags, table) 1.7.2. drop_accounting([type], [flags]) 1.7.3. acc_log_request(comment) 1.7.4. acc_db_request(comment, table) 1.7.5. acc_aaa_request(comment) 1.7.6. acc_diam_request(comment) 1.7.7. acc_evi_request(comment) 1.8. Exported Events 1.8.1. E_ACC_CDR 1.8.2. E_ACC_EVENT 1.8.3. E_ACC_MISSED_EVENT 2. Frequently Asked Questions List of Examples 1.1. early_media example 1.2. report_cancels example 1.3. detect_direction example 1.4. multi_leg_info example 1.5. multi_leg_bye_info example 1.6. log_level example 1.7. log_facility example 1.8. log_extra example 1.9. log_extra_bye example 1.10. Set aaa_url parameter 1.11. service_type example 1.12. aaa_extra example 1.13. aaa_extra_bye example 1.14. db_table_acc example 1.15. db_table_missed_calls example 1.16. db_url example 1.17. acc_method_column example 1.18. acc_from_tag_column example 1.19. acc_to_tag_column example 1.20. acc_callid_column example 1.21. acc_sip_code_column example 1.22. acc_sip_reason_column example 1.23. acc_time_column example 1.24. db_extra example 1.25. db_extra_bye example 1.26. diameter_client_host example 1.27. diameter_client_host example 1.28. diameter_extra example 1.29. evi_extra example 1.30. evi_extra_bye example 1.31. acc_created_avp_name example 1.32. do_accounting usage 1.33. drop_accounting usage 1.34. acc_log_request usage 1.35. acc_db_request usage 1.36. acc_aaa_request usage 1.37. acc_diam_request usage 1.38. acc_evi_request usage Chapter 1. Admin Guide 1.1. Overview ACC module is used to account transactions information to different backends like syslog, SQL, AAA and DIAMETER (beta version). To account a transaction and to choose which set of backends to be used, the script writer just has to use mark the transaction for accouting by using the Section 1.7.1, “ do_accounting(type, flags, table) †script function. Note that the function is not actually doing the accounting at that very process, it is just setting a marker - the actual accouting will be done later when the transaction or dialog will be completed. Even so, the module allows the script writter to force accounting on the spot in special cases via some other script functions. The accounting module will log by default a fixed set of attributes for the transaction - if you customize your accounting by adding more information to be logged, please see the next chapter about extra accounting - Section 1.2, “Extra accountingâ€. The fixed minimal accounting information is: * Request Method name * From header TAG parameter * To header TAG parameter * Call-Id * 3-digit Status code from final reply * Reason phrase from final reply * Time stamp when transaction was completed If a value is not present in request, the empty string is accounted instead. Note that: * A single INVITE may produce multiple accounting reports -- that's due to SIP forking feature. * Since version 2.2 all flags used for accounting have been replaced by the do_accounting function. No need to worry anymore of whether you have set the flags or not, or be confused by various flag names, now you only have to call the function and it will do all the work for you. * OpenSIPS now supports session/dialog accounting. It can automatically correlate INVITEs with BYEs for generating proper CDRs, for example for purpose of billing. * If a UA fails in middle of conversation, a proxy will never find out about it. In general, a better practice is to account from an end-device (such as PSTN gateway), which best knows about call status (including media status and PSTN status in case of the gateway). The SQL, Event Interface and AAA backend support are compiled in the module. For DIAMETER you need to enable it by recompiling the module with properly set defines: uncomment DIAM_ACC line in modules/acc/Makefile. The AAA client needs to be configured properly. NOTE: diameter support was developed for DISC (DIameter Server Client project at http://developer.berlios.de/projects/disc/). This project seems to be no longer maintained and DIAMETER specifications were updated in the meantime. Thus, the DIAMETER part in the module is obsolete and needs rework to be usable with opendiameter or other DIAMETER servers. 1.1.1. General Example loadmodule "modules/acc/acc.so" if (uri=~"sip:+40") /* calls to Romania */ { if (!proxy_authorize("sip_domain.net" /* realm */, "subscriber" /* table name */)) { proxy_challenge("sip_domain.net" /* realm */, "0" /* no qop */ ) ; exit; } if (is_method("INVITE") && !db_check_from()) { xlog("FROM URI != digest username\n"); sl_send_reply("403","Forbidden"); } do_accounting("log"); /* set for accounting via syslog */ t_relay(); /* enter stateful mode now */ }; 1.2. Extra accounting 1.2.1. Overview Along the static default information, ACC modules allows dynamical selection of extra information to be logged. This allows you to log any pseudo-variable (AVPs, parts of the request, parts of the reply, etc). 1.2.2. Definitions and syntax Selection of extra information is done via xxx_extra parameters by specifying the names of additional information you want to log. This information is defined via pseudo-variables and may include headers, AVPs values or other message or system values. The syntax of the parameter is: * xxx_extra = extra_definition (';'extra_definition)* * extra_definition = log_name '=' pseudo_variable ['/reply'] Each PV (pseudo variable) may be evaluated in the context of the request message (to access info from it) or in the context of the reply message (final reply for the request). Using “/reply†marker, the PV will be evaluated in the context of the reply; by default (without the marker), the evaluation is done in the contect of the request. This will allow you to automatically account information (message or network related) from both request and reply in the same time. The full list of supported pseudo-variables in OpenSIPS is availabe at: http://www.opensips.org/pmwiki.php?n=Resources.DocsCoreVar Via log_name you define how/where the data will be logged. Its meaning depends of the accounting support which is used: * LOG accounting - log_name will be just printed along with the data in log_name=data format; * DB accounting - log_name will be the name of the DB column where the data will be stored.IMPORTANT: add in db acc table the columns corresponding to each extra data; * AAA accounting - log_name will be the AVP name used for packing the data into AAA message. The log_name will be translated to AVP number via the dictionary. IMPORTANT: add in AAA dictionary the log_name attribute. * DIAMETER accounting - log_name will be the AVP code used for packing the data into DIAMETER message. The AVP code is given directly as integer, since DIAMETER has no dictionary support yet. IMPORTANT: log_name must be a number. * Events accounting - log_name will be the name of the parameter in the event raised. 1.2.3. How it works Some pseudo variables may return more than one value (like headers or AVPs). In this case, the returned values are embedded in a single string in a comma-separated format. 1.3. Multi Call-Legs accounting 1.3.1. Overview A SIP call can have multiple legs due forwarding actions. For example user A calls user B which forwards the call to user C. There is only one SIP call but with 2 legs ( A to B and B to C). Accounting the legs of a call is required for proper billing of the calls (if C is a PSTN number and the call is billed, user B must pay for the call - as last party modifing the call destination-, and not A - as initiator of the call. Call forwarding on server is only one example which shows the necessity of the having an accounting engine with multiple legs support. 1.3.2. Configuration First how it works: The idea is to have a set of AVPs and for each call leg to store a set of values in the AVPs. The meaning of the AVP content is stricly decided by the script writer - it can be the origin and source of the leg, its status or any other related information. If you have a set of 4 AVPS (AVP1, AVP2, AVP3, AVP4), then for the "A call B and B forwards to C" example, you need to set a different set of values for the AVPs for each leg ([A,B] and [B,C]) . The script writer must take care and properly insert all these AVP from the script (in proper order and with the correct type). When the accounting information for the call will be written/sent, all the call-leg pairs will be added (based on the found AVP sets). By default, the multiple call-leg support is disabled - it can be enabled just by setting the per-leg set of AVPs via the multi_leg_info and/or multi_leg_bye_info module parameters. Note that the last one only makes sense only for CDRs that are generated automatically by OpenSIPS. Important: when both multi-leg accounting is done (for INVITE and BYE), you have to make sure that the AVPs are populated with the same number of legs, otherwise the multi-leg accounting will have an unexpected behavior (values may no longer match the leg number). 1.3.3. Logged data For each call, all the values of the AVP set (which defines a call-leg) will be logged. How the information will be actually logged, depends of the data backend: * syslog -- all leg-sets will be added to one record string as AVP1=xxx, AVP2=xxxx ,... sets. * database -- each pair will be separately logged (due DB data structure constraints); several records will be written, the difference between them being only the fields corresponding to the call-leg info. Note You will need to add in your DB (all acc related tables) the colums for call-leg info (a column for each AVP of the set). * AAA -- all sets will be added to the same AAA accounting message as AAA AVPs - for each call-leg a set of AAA AVPs will be added (corresponding to the per-leg AVP set) Note You will need to add in your dictionary the AAA AVPs used in call-leg AVP set definition. * Diameter same as for AAA. * events -- each pair will appear as a different parameter-value pair in the event. Similar to the database behavior, multiple events will be raised, and the only difference between them is the leg information. Important!!! In order to use RADIUS, one must include the AVPs which are located in $(opensips_install_dir)/etc/dictionary.opensips, both in opensips radius config script dictionary and radius server dictionary. Most important are the last three AVPs (IDs : 227, 228, 229) which you won't find in any SIP dictionary (at least at this moment) because they are only used in openSips. 1.4. CDRs accounting 1.4.1. Overview ACC module can now also maintain session/dialog accounting. This allows you to log useful information like call duration, call start time and setup time. 1.4.2. Configuration In order to have CDRs accounting, first you need to set the cdr flag when calling Section 1.7.1, “ do_accounting(type, flags, table) †script function for the initial INVITE of the dialog. 1.4.3. How it works This type of accounting is based on the dialog module. When an initial INVITE is received, if the cdr flag is set, then the dialog creation time is saved. Once the call is answered and the ACK is received, other information like extra values or leg values are saved. When the corresponding BYE is received, the call duration is computed and all information is stored to the desired backend. 1.5. Dependencies 1.5.1. OpenSIPS Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): * tm -- Transaction Manager * a database module -- If SQL support is used. * rr -- Record Route, if “detect_direction†module parameter is enabled. * an aaa module * dialog -- Dialog, if “cdr†option is used 1.5.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none. 1.6. Exported Parameters 1.6.1. early_media (integer) Should be early media (any provisional reply with body) accounted too ? Default value is 0 (no). Example 1.1. early_media example modparam("acc", "early_media", 1) 1.6.2. report_cancels (integer) By default, CANCEL reporting is disabled -- most accounting applications wants to see INVITE's cancellation status. Turn on if you explicitly want to account CANCEL transactions. Default value is 0 (no). Example 1.2. report_cancels example modparam("acc", "report_cancels", 1) 1.6.3. detect_direction (integer) Controls the direction detection for sequential requests. If enabled (non zero value), for sequential requests with upstream direction (from callee to caller), the FROM and TO will be swapped (the direction will be preserved as in the original request). It affects all values related to TO and FROM headers (body, URI, username, domain, TAG). Default value is 0 (disabled). Example 1.3. detect_direction example modparam("acc", "detect_direction", 1) 1.6.4. multi_leg_info (string) Defines the AVP set to be used in per-call-leg accounting. See Section 1.3, “Multi Call-Legs accounting†for a detailed description of the Multi Call-Legs accounting. If empty, the multi-leg accounting support will be disabled. Default value is 0 (disabled). Example 1.4. multi_leg_info example # for syslog-based accounting, use any text you want to be printed modparam("acc", "multi_leg_info", "text1=$avp(src);text2=$avp(dst)") # for mysql-based accounting, use the names of the columns modparam("acc", "multi_leg_info", "leg_src=$avp(src);leg_dst=$avp(dst)") # for AAA-based accounting, use the names of the AAA AVPs modparam("acc", "multi_leg_info", "AAA_LEG_SRC=$avp(src);AAA_LEG_DST=$avp(dst)") # for DIAMETER-based accounting, use the DIAMETER AVP ID (as integer) modparam("acc", "multi_leg_info", "2345=$avp(src);2346=$avp(dst)") 1.6.5. multi_leg_bye_info (string) Defines the AVP set to be used in per-call-leg accounting. See Section 1.3, “Multi Call-Legs accounting†for a detailed description of the Multi Call-Legs accounting. This parameter evaluates the AVPs set for BYE requests. It makes sense only when the CDRs are automatically generated, using the dialog support. If empty, the multi-leg support for BYE requests is disabled. Default value is 0 (disabled). Example 1.5. multi_leg_bye_info example # for syslog-based accounting, use any text you want to be printed modparam("acc", "multi_leg_bye_info", "text1=$avp(src);text2=$avp(dst)") # for mysql-based accounting, use the names of the columns modparam("acc", "multi_leg_bye_info", "leg_src=$avp(src);leg_dst=$avp(dst)") # for AAA-based accounting, use the names of the AAA AVPs modparam("acc", "multi_leg_bye_info", "AAA_LEG_SRC=$avp(src);AAA_LEG_SRC=$avp(dst)") 1.6.6. log_level (integer) Log level at which accounting messages are issued to syslog. Default value is L_NOTICE. Example 1.6. log_level example modparam("acc", "log_level", 2) # Set log_level to 2 1.6.7. log_facility (string) Log facility to which accounting messages are issued to syslog. This allows to easily seperate the accounting specific logging from the other log messages. Default value is LOG_DAEMON. Example 1.7. log_facility example modparam("acc", "log_facility", "LOG_DAEMON") 1.6.8. log_extra (string) Extra values to be logged. Default value is NULL. Example 1.8. log_extra example modparam("acc", "log_extra", "uaA=$hdr(User-Agent);uaB=$hdr(Server)/reply;uuid=$avp(123)") 1.6.9. log_extra_bye (string) Extra values to be logged to logfile. Note that this parameter makes sense only when the cdr option is used with do_accounting(). Default value is NULL. Example 1.9. log_extra_bye example modparam("acc", "log_extra_bye", "uaA=$hdr(User-Agent);uaB=$hdr(Server)/reply;uuid=$avp(123)") 1.6.10. aaa_url (string) This is the url representing the AAA protocol used and the location of the configuration file of this protocol. If the parameter is set to empty string, the AAA accounting support will be disabled. Default value is “NULLâ€. Example 1.10. Set aaa_url parameter ... modparam("acc", "aaa_url", "radius:/etc/radiusclient-ng/radiusclient.con f") ... 1.6.11. service_type (integer) AAA service type used for accounting. Default value is not-set. Example 1.11. service_type example # Default value of service type for SIP is 15 modparam("acc", "service_type", 15) 1.6.12. aaa_extra (string) Extra values to be logged via AAA - AAA specific. Default value is NULL. Example 1.12. aaa_extra example modparam("acc", "aaa_extra", "via=$hdr(Via[*]); email=$avp(email); Bcontact=$ct / reply") 1.6.13. aaa_extra_bye (string) Extra values to be logged via AAA when a BYE message is received - AAA specific. Note that this parameter makes sense only when the cdr option is used with do_accounting. Default value is NULL. Example 1.13. aaa_extra_bye example modparam("acc", "aaa_extra_bye", "via=$hdr(Via[*]); email=$avp(email); Bcontact=$ct / reply") 1.6.14. db_table_acc (string) Table name of accounting successfull calls -- database specific. Default value is “acc†Example 1.14. db_table_acc example modparam("acc", "db_table_acc", "myacc_table") 1.6.15. db_table_missed_calls (string) Table name for accounting missed calls -- database specific. Default value is “missed_calls†Example 1.15. db_table_missed_calls example modparam("acc", "db_table_missed_calls", "myMC_table") 1.6.16. db_url (string) SQL address -- database specific. If is set to NULL or empty string, the SQL support is disabled. Default value is “NULL†(SQL disabled). Example 1.16. db_url example modparam("acc", "db_url", "mysql://user:password@localhost/opensips") 1.6.17. acc_method_column (string) Column name in accounting table to store the request's method name as string. Default value is “methodâ€. Example 1.17. acc_method_column example modparam("acc", "acc_method_column", "method") 1.6.18. acc_from_tag_column (string) Column name in accounting table to store the From header TAG parameter. Default value is “from_tagâ€. Example 1.18. acc_from_tag_column example modparam("acc", "acc_from_tag_column", "from_tag") 1.6.19. acc_to_tag_column (string) Column name in accounting table to store the To header TAG parameter. Default value is “to_tagâ€. Example 1.19. acc_to_tag_column example modparam("acc", "acc_to_tag_column", "to_tag") 1.6.20. acc_callid_column (string) Column name in accounting table to store the request's Callid value. Default value is “callidâ€. Example 1.20. acc_callid_column example modparam("acc", "acc_callid_column", "callid") 1.6.21. acc_sip_code_column (string) Column name in accounting table to store the final reply's numeric code value in string format. Default value is “sip_codeâ€. Example 1.21. acc_sip_code_column example modparam("acc", "acc_sip_code_column", "sip_code") 1.6.22. acc_sip_reason_column (string) Column name in accounting table to store the final reply's reason phrase value. Default value is “sip_reasonâ€. Example 1.22. acc_sip_reason_column example modparam("acc", "acc_sip_reason_column", "sip_reason") 1.6.23. acc_time_column (string) Column name in accounting table to store the time stamp of the transaction completion in date-time format. Default value is “timeâ€. Example 1.23. acc_time_column example modparam("acc", "acc_time_column", "time") 1.6.24. db_extra (string) Extra values to be logged into database - DB specific. Default value is NULL. Example 1.24. db_extra example modparam("acc", "db_extra", "ct=$hdr(Content-type); email=$avp(email)") 1.6.25. db_extra_bye (string) Extra values to be logged into database when a BYE message is received - DB specific. Note that this parameter makes sense only when the cdr option is used with do_accounting. Default value is NULL. Example 1.25. db_extra_bye example modparam("acc", "db_extra_bye", "ct=$hdr(Content-type); email=$avp(email )") 1.6.26. diameter_client_host (string) Hostname of the machine where the DIAMETER Client is running -- DIAMETER specific. Default value is “localhostâ€. Example 1.26. diameter_client_host example modparam("acc", "diameter_client_host", "3a_server.net") 1.6.27. diameter_client_port (int) Port number where the Diameter Client is listening -- DIAMETER specific. Default value is “3000â€. Example 1.27. diameter_client_host example modparam("acc", "diameter_client_port", 3000) 1.6.28. diameter_extra (string) Extra values to be logged via DIAMETER - DIAMETER specific. Default value is NULL. Example 1.28. diameter_extra example modparam("acc", "diameter_extra", "7846=$hdr(Content-type);7847=$avp(ema il)") 1.6.29. evi_extra (string) Extra values to be attached as event's parameters. Default value is NULL. Example 1.29. evi_extra example modparam("acc", "evi_extra", "uaA=$hdr(User-Agent);uaB=$hdr(Server)/reply;uuid=$avp(123)") 1.6.30. evi_extra_bye (string) Extra values to be attached as event's parameters for BYE messages. Note that this parameter makes sense only when the cdr option is used with do_accounting. Default value is NULL. Example 1.30. evi_extra_bye example modparam("acc", "evi_extra_bye", "uaA=$hdr(User-Agent);uaB=$hdr(Server)/reply;uuid=$avp(123)") 1.6.31. acc_created_avp_name (string) The name of the openSips avp that will be used to hold the time when the call was created. This time will only be logged on missed calls. Default value is "accX_created". Example 1.31. acc_created_avp_name example modparam("acc", "acc_created_avp_name", "call_created_avp") 1.7. Exported Functions 1.7.1. do_accounting(type, flags, table) do_accounting() replace all the *_flag and, *_missed_flag, cdr_flag, failed transaction_flag and the db_table_avp modpara. Just call do_accounting(), select where you want to do accounting and how and the function will do all the job for you. Meaning of the parameters is as follows: * type - the type of accounting you want to do. All the types have to be separated by '|'. The following parameters can be used: + log - syslog accounting; + db - database accounting; + aaa - aaa specific accounting; + evi - Event Interface accounting; + diam - Diameter accounting; * flags - flags for the accouting type you have selected. All the types have to be separated by '|'. The following parameters can be used: + cdr - also set CDR details when doing accounting; DIALOG MODULE HAS TO BE LOADED; + missed - log missed calls; + failed - flag which says if the transaction should be accounted also in case of failure (status>=300); * table - table where to do the accounting; it replaces old table_avp parameter; This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.32. do_accounting usage ... if (!has_totag()) { if (is_method("INVITE")) { /* enable cdr and missed calls accounting in the database * and to syslog; db accounting shall be done in "my_acc" table */ do_accounting("db|log", "cdr|missed", "m y_acc"); } } ... if (is_method("BYE")) { /* do normal accounting via aaa */ do_accounting("aaa"); } ... 1.7.2. drop_accounting([type], [flags]) drop_accounting() resets flags and types of accounting set with do_accounting(). If called with no arguments all accounting will be stopped. If called with only one argument all accounting for that type will be stopped. If called with two arguments normal accounting will still be enabled. Meaning of the parameters is as follows: * type - the type of accounting you want to stop. All the types have to be separated by '|'. The following parameters can be used: + log - stop syslog accounting; + db - stop database accounting; + aaa - stop aaa specific accounting; + evi - stop Event Interface accounting; + diam - stop Diameter accounting; * flags - flags to be reset for the accouting type you have selected. All the types have to be separated by '|'. The following parameters can be used: + cdr - stop CDR accounting; + missed - stop logging missed calls; + failed - stop failed transaction accounting; This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.33. drop_accounting usage ... acc_log_request("403 Destination not allowed"); if (!has_totag()) { if (is_method("INVITE")) { /* enable cdr and missed calls accounting in the database * and to syslog; db accounting shall be done in "my_acc" table */ do_accounting("db|log", "cdr|missed", "m y_acc"); } } ... /* later in your script */ if (...) { /* you don't want accounting anymore */ /* stop all syslog accounting */ drop_accounting("log"); /* or stop missed calls and cdr accounting for s yslog; * normal accounting will still be enabled */ drop_accounting("log","missed|cdr"); /* or stop all types of accounting */ drop_accounting(); } ... 1.7.3. acc_log_request(comment) acc_request reports on a request, for example, it can be used to report on missed calls to off-line users who are replied 404 - Not Found. To avoid multiple reports on UDP request retransmission, you would need to embed the action in stateful processing. Meaning of the parameters is as follows: * comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.34. acc_log_request usage ... acc_log_request("403 Destination not allowed"); ... 1.7.4. acc_db_request(comment, table) Like acc_log_request, acc_db_request reports on a request. The report is sent to database at “db_urlâ€, in the table referred to in the second action parameter. Meaning of the parameters is as follows: * comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. * table - Database table to be used. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.35. acc_db_request usage ... acc_db_request("Some comment", "Some table"); acc_db_request("$T_reply_code $(rr)","acc"); ... 1.7.5. acc_aaa_request(comment) Like acc_log_request, acc_aaa_request reports on a request. It reports to aaa server as configured in “aaa_urlâ€. Meaning of the parameters is as follows: * comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.36. acc_aaa_request usage ... acc_aaa_request("403 Destination not allowed"); ... 1.7.6. acc_diam_request(comment) Like acc_log_request, acc_diam_request reports on a request. It reports to the configured Diameter server. Meaning of the parameters is as follows: * comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.37. acc_diam_request usage ... acc_diam_request("403 Destination not allowed"); ... 1.7.7. acc_evi_request(comment) Like acc_log_request, acc_evi_request reports on a request. The report is packed as an event sent through the OpenSIPS Event Interface as E_ACC_EVENT if the reply code is a positive one (lower than 300), or E_ACC_MISSED_EVENT for negative or no codes. More information on this in Section 1.8, “Exported Eventsâ€. Meaning of the parameters is as follows: * comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.38. acc_evi_request usage ... acc_evi_request("403 Destination not allowed"); ... 1.8. Exported Events 1.8.1. E_ACC_CDR The event raised when a CDR is generated. Note that this event will only be triggered if the auto CDR accounting is used. Parameters: * method - Request method name * from_tag - From header tag parameter * to_tag - To header tag parameter * callid - Message Call-id * sip_code - The status code from the final reply * sip_reason - The status reason from the final reply * time - The timestamp when the call was established * evi_extra* - Extra parameters added by the evi_extra parameter. * evi_extra_bye* - Extra parameters added by the evi_extra_bye parameter * multi_leg_info* - Extra parameters added by the multi_leg_info parameter * multi_leg_bye_info* - Extra parameters added by the multi_leg_bye_info parameter * duration - The call duration in seconds * setuptime - The call setup time in seconds * created - The timestamp when the call was created (the initial Invite was received) 1.8.2. E_ACC_EVENT This event is triggered when old-style accounting is used. It is generated when the requests (INVITE and BYE) transaction have positive final replies, or by the acc_evi_request() function that has a positive reply code in comment. Parameters: * method - Request method name * from_tag - From header tag parameter * to_tag - To header tag parameter * callid - Message Call-id * sip_code - The status code from the final reply * sip_reason - The status reason from the final reply * time - The timestamp when the transaction was created * evi_extra* - Extra parameters added by the evi_extra parameter * multi_leg_info* - Extra parameters added by the multi_leg_info parameter 1.8.3. E_ACC_MISSED_EVENT This event is triggered when old-style accounting is used. It is generated when the requests (INVITE and BYE) transaction have negative final replies, or by the acc_evi_request() function that has a negative reply code in comment. Parameters: * method - Request method name * from_tag - From header tag parameter * to_tag - To header tag parameter * callid - Message Call-id * sip_code - The status code from the final reply * sip_reason - The status reason from the final reply * time - The timestamp when the transaction was created * evi_extra* - Extra parameters added by the evi_extra parameter * multi_leg_info* - Extra parameters added by the multi_leg_info parameter * created - Timestamp when the call was created * setuptime - The call setup time in seconds Chapter 2. Frequently Asked Questions 2.1. What happened with old report_ack parameter The parameter is considered obsolete. It was removed as acc module is doing SIP transaction based accouting and according to SIP RFC, end2end ACKs are a different transaction (still part of the same dialog). ACKs can be individually accouted as any other sequential (in-dialog) request. $ 2.2. What happened with old log_fmt parameter The parameter became obsolete with the restructure of the data logged by ACC module (refer to the Overview chapter). For similar behaviour you can use the extra accouting (see the corresponding chapter). 2.3. What happened with old multi_leg_enabled parameter The parameter became obsolete by the addition of the new multi_leg_info parameter. The multi-leg accouting is automatically enabled when multi_leg_info is defined. 2.4. What happened with old src_leg_avp_id and dst_leg_avp_id parameters The parameter was replaced by the more generic new parameter multi_leg_info. This allows logging (per-leg) of more information than just dst and src. 2.5. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.6. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.7. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/acc/acc.c000066400000000000000000001446771300170765700164760ustar00rootroot00000000000000 /* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-04 grand acc cleanup (jiri) * 2003-11-04 multidomain support for mysql introduced (jiri) * 2004-06-06 updated to the new DB api, cleanup: acc_db_{bind, init,close) * added (andrei) * 2005-05-30 acc_extra patch commited (ramona) * 2005-06-28 multi leg call support added (bogdan) * 2006-01-13 detect_direction (for sequential requests) added (bogdan) * 2006-09-08 flexible multi leg accounting support added, * code cleanup for low level functions (bogdan) * 2006-09-19 final stage of a masive re-structuring and cleanup (bogdan) */ #include #include #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" /* q_memchr */ #include "../../mem/mem.h" #include "../../usr_avp.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../parser/hf.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../parser/digest/digest.h" #include "../tm/t_funcs.h" #include "../../aaa/aaa.h" #ifdef DIAM_ACC #include "diam_dict.h" #include "diam_message.h" #include "diam_tcp.h" #endif #include "acc.h" #include "acc_mod.h" #include "acc_extra.h" #include "acc_logic.h" #define TABLE_VERSION 7 #define GET_LEN(p) (*((unsigned char*)p) | *((unsigned char*)p+1) << 8) #define MAX_LEN_VALUE 65535 #define SET_LEN(p,n) \ do { \ *(p) = (n) & 0x00FF; \ *(p+1) = (n) >> 8; \ } while(0) str created_str = str_init("accX_created"); str core_str = str_init("accX_core"); str leg_str = str_init("accX_leg"); str flags_str = str_init("accX_flags"); str table_str = str_init("accX_table"); str db_extra_str = str_init("accX_db"); str log_extra_str = str_init("accX_log"); str aaa_extra_str = str_init("accX_aaa"); str evi_extra_str = str_init("accX_evi"); extern struct acc_extra *log_extra; extern struct acc_extra *log_extra_bye; extern struct acc_extra *leg_info; extern struct acc_extra *leg_bye_info; extern struct acc_enviroment acc_env; extern struct acc_extra *aaa_extra; extern struct acc_extra *aaa_extra_bye; #ifdef DIAM_ACC extern char *diameter_client_host; extern int diameter_client_port; extern struct acc_extra *dia_extra; #endif extern struct acc_extra *evi_extra; extern struct acc_extra *evi_extra_bye; event_id_t acc_cdr_event = EVI_ERROR; event_id_t acc_event = EVI_ERROR; event_id_t acc_missed_event = EVI_ERROR; static db_func_t acc_dbf; static db_con_t* db_handle=0; extern struct acc_extra *db_extra; extern struct acc_extra *db_extra_bye; extern int acc_log_facility; /* call created avp id */ extern int acc_created_avp_id; static int build_core_dlg_values(struct dlg_cell *dlg,struct sip_msg *req); static int build_extra_dlg_values(struct acc_extra* extra, struct dlg_cell *dlg,struct sip_msg *req, struct sip_msg *reply); static int build_leg_dlg_values(struct dlg_cell *dlg,struct sip_msg *req); static void complete_dlg_values(str *stored_values,str *val_arr,short nr_vals); /* prebuild functions */ static time_t acc_get_created(struct dlg_cell *dlg); static int prebuild_core_arr(struct dlg_cell *dlg, str *buffer, struct timeval *start); static int prebuild_extra_arr(struct dlg_cell *dlg, struct sip_msg *msg, str *buffer, str *type_str, struct acc_extra * extra, int start); static int prebuild_leg_arr(struct dlg_cell *dlg, str *buffer, short *nr_legs) ; static int store_extra_values(struct acc_extra* extra, str *values_str, struct dlg_cell *dlg, struct sip_msg *req, struct sip_msg *reply); /* array used to collect the values before being * pushed to the storage backend (whatever used) */ static str val_arr[ACC_CORE_LEN+ACC_DLG_LEN+MAX_ACC_EXTRA+MAX_ACC_LEG]; /******************************************** * acc CORE function ********************************************/ #define get_ft_body( _ft_hdr) \ ((struct to_body*)_ft_hdr->parsed) #define SET_EMPTY_VAL(_i) \ do { \ c_vals[_i].s = 0; \ c_vals[_i].len = 0; \ } while(0) /* * */ static inline int get_timestamps(unsigned int* created, unsigned int* setup_time)\ { int_str _created_ts; if (search_first_avp( 0, acc_created_avp_id, &_created_ts, 0)==0) { LM_ERR("cannot find created avp\n"); return -1; } *created = (unsigned int)_created_ts.n; *setup_time = time(NULL) - *created; return 0; } /* returns: * method name * from TAG * to TAG * callid * sip_code * sip_status * */ static inline int core2strar( struct sip_msg *req, str *c_vals) { struct to_body *ft_body; struct hdr_field *from; struct hdr_field *to; /* method */ c_vals[0] = req->first_line.u.request.method; /* from/to URI and TAG */ if (req->msg_flags&FL_REQ_UPSTREAM) { LM_DBG("the flag UPSTREAM is set -> swap F/T\n"); \ from = acc_env.to; to = req->from; } else { from = req->from; to = acc_env.to; } if (from && (ft_body=get_ft_body(from)) && ft_body->tag_value.len) { c_vals[1] = ft_body->tag_value; } else { SET_EMPTY_VAL(1); } if (to && (ft_body=get_ft_body(to)) && ft_body->tag_value.len) { c_vals[2] = ft_body->tag_value; } else { SET_EMPTY_VAL(2); } /* Callid */ if (req->callid && req->callid->body.len) c_vals[3] = req->callid->body; else SET_EMPTY_VAL(3); /* SIP code */ c_vals[4] = acc_env.code_s; c_vals[5] = acc_env.reason; gettimeofday(&acc_env.ts, NULL); return ACC_CORE_LEN; } /******************************************** * LOG ACCOUNTING ********************************************/ static str log_attrs[ACC_CORE_LEN+ACC_DLG_LEN+MAX_ACC_EXTRA+MAX_ACC_LEG]; #define SET_LOG_ATTR(_n,_atr) \ do { \ log_attrs[_n].s=A_##_atr; \ log_attrs[_n].len=A_##_atr##_LEN; \ n++; \ } while(0) void acc_log_init(void) { struct acc_extra *extra; int n; n = 0; /* fixed core attributes */ SET_LOG_ATTR(n,METHOD); SET_LOG_ATTR(n,FROMTAG); SET_LOG_ATTR(n,TOTAG); SET_LOG_ATTR(n,CALLID); SET_LOG_ATTR(n,CODE); SET_LOG_ATTR(n,STATUS); /* init the extra db keys */ for(extra=log_extra; extra ; extra=extra->next) log_attrs[n++] = extra->name; for(extra=log_extra_bye; extra ; extra=extra->next) log_attrs[n++] = extra->name; /* multi leg call columns */ for( extra=leg_info ; extra ; extra=extra->next) log_attrs[n++] = extra->name; for( extra=leg_bye_info ; extra ; extra=extra->next) log_attrs[n++] = extra->name; /* cdrs columns */ SET_LOG_ATTR(n,DURATION); SET_LOG_ATTR(n,SETUPTIME); SET_LOG_ATTR(n,CREATED); } int acc_log_cdrs(struct dlg_cell *dlg, struct sip_msg *msg) { static char log_msg[MAX_SYSLOG_SIZE]; static char *log_msg_end=log_msg+MAX_SYSLOG_SIZE-2; char *p; int nr_vals, i, j, ret, res = -1, n; time_t created; struct timeval start_time,end; str core_s, leg_s, extra_s; short nr_legs; gettimeofday(&end,NULL); core_s.s = extra_s.s = leg_s.s = 0; ret = prebuild_core_arr(dlg, &core_s, &start_time); if (ret < 0) { LM_ERR("cannot copy core arguments\n"); goto end; } ret = prebuild_extra_arr(dlg, msg, &extra_s, &log_extra_str, log_extra_bye, ret); if (ret < 0) { LM_ERR("cannot copy extra arguments\n"); goto end; } /* here starts the extra leg */ nr_vals = prebuild_leg_arr(dlg, &leg_s, &nr_legs); if (nr_vals < 0) { LM_ERR("cannot compute leg values\n"); goto end; } if (!(created = acc_get_created(dlg))) { LM_ERR("cannot get created\n"); goto end; } for ( i = 0,p = log_msg ; i= log_msg_end) { LM_WARN("acc message too long, truncating..\n"); p = log_msg_end; break; } *(p++) = A_SEPARATOR_CHR; memcpy(p, log_attrs[i].s, log_attrs[i].len); p += log_attrs[i].len; *(p++) = A_EQ_CHR; memcpy(p, val_arr[i].s, val_arr[i].len); p += val_arr[i].len; } LM_DBG("core+extra = %d - nr_legs = %d - nr_vals = %d\n", ret, nr_legs, nr_vals); if (leg_info || leg_bye_info) { leg_s.len = 4; n = legs2strar(leg_bye_info,msg,val_arr+ret+nr_vals,1); for (j=0; j= log_msg_end) { LM_WARN("acc message too long, truncating..\n"); p = log_msg_end; break; } *(p++) = A_SEPARATOR_CHR; memcpy(p, log_attrs[i].s, log_attrs[i].len); p += log_attrs[i].len; *(p++) = A_EQ_CHR; memcpy(p, val_arr[i].s, val_arr[i].len); p += val_arr[i].len; } n = legs2strar(leg_bye_info,msg,val_arr+ret+nr_vals,0); } while (n) { for (i=ret; i= log_msg_end) { LM_WARN("acc message too long, truncating..\n"); p = log_msg_end; break; } *(p++) = A_SEPARATOR_CHR; memcpy(p, log_attrs[i].s, log_attrs[i].len); p += log_attrs[i].len; *(p++) = A_EQ_CHR; memcpy(p, val_arr[i].s, val_arr[i].len); p += val_arr[i].len; } n = legs2strar(leg_bye_info,msg,val_arr+ret+nr_vals,0); } } else LM_DBG("not entering\n"); /* terminating line */ *(p++) = '\n'; *(p++) = 0; LM_GEN2(acc_log_facility, acc_log_level, "%.*screated=%lu;call_start_time=%lu;duration=%lu;ms_duration=%lu;setuptime=%lu%s", acc_env.text.len, acc_env.text.s,(unsigned long)created, (unsigned long)start_time.tv_sec, (unsigned long)(end.tv_sec-start_time.tv_sec), (unsigned long)((end.tv_sec-start_time.tv_sec)*1000+(end.tv_usec-start_time.tv_usec)%1000), (unsigned long)(start_time.tv_sec - created), log_msg); res = 1; end: if (core_s.s) pkg_free(core_s.s); if (extra_s.s) pkg_free(extra_s.s); if (leg_s.s) pkg_free(leg_s.s); return res; } int acc_log_request( struct sip_msg *rq, struct sip_msg *rpl, int cdr_flag) { static char log_msg[MAX_SYSLOG_SIZE]; static char *log_msg_end=log_msg+MAX_SYSLOG_SIZE-2; char *p; int n; int m; int i; unsigned int _created=0; unsigned int _setup_time=0; if (cdr_flag && get_timestamps(&_created, &_setup_time)<0) { LM_ERR("cannot get timestamps\n"); return -1; } /* get default values */ m = core2strar( rq, val_arr); /* get extra values */ m += extra2strar( log_extra, rq, rpl, val_arr+m, 0); for ( i = 0,p = log_msg ; i= log_msg_end) { LM_WARN("acc message too long, truncating..\n"); p = log_msg_end; break; } *(p++) = A_SEPARATOR_CHR; memcpy(p, log_attrs[i].s, log_attrs[i].len); p += log_attrs[i].len; *(p++) = A_EQ_CHR; memcpy(p, val_arr[i].s, val_arr[i].len); p += val_arr[i].len; } /* get per leg attributes */ if ( leg_info ) { n = legs2strar(leg_info,rq,val_arr+m,1); do { for (i=m; i= log_msg_end) { LM_WARN("acc message too long, truncating..\n"); p = log_msg_end; break; } *(p++) = A_SEPARATOR_CHR; memcpy(p, log_attrs[i].s, log_attrs[i].len); p += log_attrs[i].len; *(p++) = A_EQ_CHR; memcpy(p, val_arr[i].s, val_arr[i].len); p += val_arr[i].len; } }while (p!=log_msg_end && (n=legs2strar(leg_info,rq,val_arr+m,0))!=0); } /* terminating line */ *(p++) = '\n'; *(p++) = 0; if (cdr_flag) { LM_GEN2(acc_log_facility, acc_log_level, "%.*stimestamp=%lu;created=%lu;setuptime=%lu%s", acc_env.text.len, acc_env.text.s, (unsigned long) acc_env.ts.tv_sec, (unsigned long) _created, (unsigned long) _setup_time, log_msg); return 1; } LM_GEN2(acc_log_facility, acc_log_level, "%.*stimestamp=%lu%s", acc_env.text.len, acc_env.text.s,(unsigned long) acc_env.ts.tv_sec, log_msg); return 1; } int store_log_extra_values(struct dlg_cell *dlg, struct sip_msg *req, struct sip_msg *reply) { return store_extra_values(log_extra, &log_extra_str, dlg, req, reply); } /******************************************** * SQL ACCOUNTING ********************************************/ /* caution: keys need to be aligned to core format */ static db_key_t db_keys_cdrs[ACC_CORE_LEN+1+ACC_DLG_LEN+MAX_ACC_EXTRA+MAX_ACC_LEG]; static db_key_t db_keys[ACC_CORE_LEN+1+ACC_DLG_LEN+MAX_ACC_EXTRA+MAX_ACC_LEG]; static db_val_t db_vals_cdrs[ACC_CORE_LEN+1+ACC_DLG_LEN+MAX_ACC_EXTRA+MAX_ACC_LEG]; static db_val_t db_vals[ACC_CORE_LEN+1+ACC_DLG_LEN+MAX_ACC_EXTRA+MAX_ACC_LEG]; static void acc_db_init_keys(void) { struct acc_extra *extra; int time_idx; int i; int n; int m; /* init the static db keys */ n = 0; m = 0; /* caution: keys need to be aligned to core format */ db_keys_cdrs[n++] = db_keys[m++] = &acc_method_col; db_keys_cdrs[n++] = db_keys[m++] = &acc_fromtag_col; db_keys_cdrs[n++] = db_keys[m++] = &acc_totag_col; db_keys_cdrs[n++] = db_keys[m++] = &acc_callid_col; db_keys_cdrs[n++] = db_keys[m++] = &acc_sipcode_col; db_keys_cdrs[n++] = db_keys[m++] = &acc_sipreason_col; db_keys_cdrs[n++] = db_keys[m++] = &acc_time_col; time_idx = n-1; /* init the extra db keys */ for(extra=db_extra; extra ; extra=extra->next) db_keys_cdrs[n++] = db_keys[m++] = &extra->name; for(extra=db_extra_bye; extra ; extra=extra->next) db_keys_cdrs[n++] = &extra->name; /* multi leg call columns */ for( extra=leg_info ; extra ; extra=extra->next) db_keys_cdrs[n++] = db_keys[m++] = &extra->name; for( extra=leg_bye_info ; extra ; extra=extra->next) db_keys_cdrs[n++] = &extra->name; /* init the values */ for(i = 0; i < n; i++) { VAL_TYPE(db_vals_cdrs + i)=DB_STR; VAL_NULL(db_vals_cdrs + i)=0; } for(i = 0; i < m; i++) { VAL_TYPE(db_vals + i)=DB_STR; VAL_NULL(db_vals + i)=0; } VAL_TYPE(db_vals_cdrs+time_idx)=VAL_TYPE(db_vals+time_idx)=DB_DATETIME; db_keys_cdrs[n++] = db_keys[m++] = &acc_setuptime_col; db_keys_cdrs[n++] = db_keys[m++] = &acc_created_col; db_keys_cdrs[n++] = &acc_duration_col; db_keys_cdrs[n++] = &acc_ms_duration_col; VAL_TYPE(db_vals_cdrs + n-1) = DB_INT; VAL_TYPE(db_vals_cdrs + n-2) = DB_INT; VAL_TYPE(db_vals_cdrs + n-3) = DB_DATETIME; VAL_TYPE(db_vals_cdrs + n-4) = DB_INT; VAL_TYPE(db_vals+m-1) = DB_DATETIME; VAL_TYPE(db_vals+m-2) = DB_INT; } /* binds to the corresponding database module * returns 0 on success, -1 on error */ int acc_db_init(const str* db_url) { if (db_bind_mod(db_url, &acc_dbf)<0){ LM_ERR("bind_db failed\n"); return -1; } /* Check database capabilities */ if (!DB_CAPABILITY(acc_dbf, DB_CAP_INSERT)) { LM_ERR("database module does not implement insert function\n"); return -1; } db_handle=acc_dbf.init(db_url); if (db_handle==0){ LM_ERR("unable to connect to the database\n"); return -1; } if (db_check_table_version(&acc_dbf, db_handle, &db_table_acc, TABLE_VERSION) < 0) { LM_ERR("error during table version check\n"); return -1; } acc_db_close(); acc_db_init_keys(); return 0; } /* initialize the database connection * returns 0 on success, -1 on error */ int acc_db_init_child(const str *db_url) { db_handle=acc_dbf.init(db_url); if (db_handle==0){ LM_ERR("unable to connect to the database\n"); return -1; } return 0; } /* close a db connection */ void acc_db_close(void) { if (db_handle && acc_dbf.close) acc_dbf.close(db_handle); db_handle = NULL; } int acc_db_request( struct sip_msg *rq, struct sip_msg *rpl, query_list_t **ins_list, int cdr_flag) { static db_ps_t my_ps_ins = NULL; static db_ps_t my_ps_ins2 = NULL; static db_ps_t my_ps = NULL; static db_ps_t my_ps2 = NULL; int m; int n; int i; unsigned int _created=0; unsigned int _setup_time=0; if (!acc_dbf.use_table || !acc_dbf.insert) { LM_ERR("database not loaded! Probably database url not defined!\n"); return -1; } if (cdr_flag && get_timestamps(&_created, &_setup_time)<0) { LM_ERR("cannot get timestamps\n"); return -1; } /* formatted database columns */ m = core2strar( rq, val_arr ); for(i = 0; i < m; i++) VAL_STR(db_vals+i) = val_arr[i]; /* time value */ VAL_TIME(db_vals+(m++)) = acc_env.ts.tv_sec; /* extra columns */ m += extra2strar( db_extra, rq, rpl, val_arr+m, 0); for( i++; i < m; i++) VAL_STR(db_vals+i) = val_arr[i]; n = legs2strar(leg_info,rq,val_arr+m,1); if (cdr_flag) { VAL_INT(db_vals+(m+n)) = _setup_time; VAL_TIME(db_vals+(m+n+1)) = _created; } acc_dbf.use_table(db_handle, &acc_env.text/*table*/); CON_PS_REFERENCE(db_handle) = cdr_flag ? (ins_list? &my_ps_ins2:&my_ps2) : (ins_list? &my_ps_ins:&my_ps); /* multi-leg columns */ if ( !leg_info ) { if (con_set_inslist(&acc_dbf,db_handle,ins_list,db_keys,m+(cdr_flag?2:0)) < 0 ) CON_RESET_INSLIST(db_handle); if (acc_dbf.insert(db_handle, db_keys, db_vals, m+(cdr_flag?2:0)) < 0) { LM_ERR("failed to insert into database\n"); return -1; } } else { do { for ( i = m; i < m + n; i++) VAL_STR(db_vals+i)=val_arr[i]; if (con_set_inslist(&acc_dbf,db_handle,ins_list,db_keys,m+n+(cdr_flag?2:0)) < 0 ) CON_RESET_INSLIST(db_handle); if (acc_dbf.insert(db_handle, db_keys, db_vals, m+n+(cdr_flag?2:0)) < 0) { LM_ERR("failed to insert into database\n"); return -1; } }while ( (n = legs2strar(leg_info,rq,val_arr+m,0))!=0 ); } return 1; } int acc_db_cdrs(struct dlg_cell *dlg, struct sip_msg *msg) { int total, nr_vals, i, ret, res = -1, nr_bye_vals = 0, j; int remaining_bye_vals = 0; time_t created; struct timeval start_time,end; str core_s, leg_s, extra_s, table; short nr_legs; static db_ps_t my_ps = NULL; static query_list_t *ins_list = NULL; if (!acc_dbf.use_table || !acc_dbf.insert) { LM_ERR("database not loaded! Probably database url not defined!\n"); return -1; } core_s.s = extra_s.s = leg_s.s = 0; gettimeofday(&end,NULL); ret = prebuild_core_arr(dlg, &core_s, &start_time); if (ret < 0) { LM_ERR("cannot copy core arguments\n"); goto end; } ret = prebuild_extra_arr(dlg, msg, &extra_s, &db_extra_str, db_extra_bye, ret); if (ret < 0) { LM_ERR("cannot copy extra arguments\n"); goto end; } /* here starts the extra leg */ nr_vals = prebuild_leg_arr(dlg, &leg_s, &nr_legs); if (nr_vals < 0) { LM_ERR("cannot compute leg values\n"); goto end; } if (!(created = acc_get_created(dlg))) { LM_ERR("cannot get created\n"); goto end; } if (dlg_api.fetch_dlg_value(dlg, &table_str, &table, 0) < 0) { LM_ERR("error getting table name\n"); return -1; } for (i=0;ilen<3) return 0; code=0; for (i=0;i<3;i++) { if (!(phrase->s[i]>='0' && phrase->s[i]<'9')) return 0; code=code*10+phrase->s[i]-'0'; } return code; } /******************************************** * AAA PROTOCOL ACCOUNTING ********************************************/ enum { RA_ACCT_STATUS_TYPE=0, RA_SERVICE_TYPE, RA_SIP_RESPONSE_CODE, RA_SIP_METHOD, RA_TIME_STAMP, RA_STATIC_MAX}; enum {RV_STATUS_START=0, RV_STATUS_STOP, RV_STATUS_ALIVE, RV_STATUS_FAILED, RV_SIP_SESSION, RV_STATIC_MAX}; static aaa_map rd_attrs[RA_STATIC_MAX+ACC_CORE_LEN+ACC_DLG_LEN-2+MAX_ACC_EXTRA+MAX_ACC_LEG]; static aaa_map rd_vals[RV_STATIC_MAX]; int init_acc_aaa(char* aaa_proto_url, int srv_type) { int n; str prot_url; memset(rd_attrs, 0, sizeof(rd_attrs)); memset(rd_vals, 0, sizeof(rd_vals)); rd_attrs[RA_ACCT_STATUS_TYPE].name = "Acct-Status-Type"; rd_attrs[RA_SERVICE_TYPE].name = "Service-Type"; rd_attrs[RA_SIP_RESPONSE_CODE].name = "Sip-Response-Code"; rd_attrs[RA_SIP_METHOD].name = "Sip-Method"; rd_attrs[RA_TIME_STAMP].name = "Event-Timestamp"; n = RA_STATIC_MAX; /* caution: keep these aligned to core acc output */ rd_attrs[n++].name = "Sip-From-Tag"; rd_attrs[n++].name = "Sip-To-Tag"; rd_attrs[n++].name = "Acct-Session-Id"; rd_vals[RV_STATUS_START].name = "Start"; rd_vals[RV_STATUS_STOP].name = "Stop"; rd_vals[RV_STATUS_ALIVE].name = "Alive"; rd_vals[RV_STATUS_FAILED].name = "Failed"; rd_vals[RV_SIP_SESSION].name = "Sip-Session"; /* add and count the extras as attributes */ n += extra2attrs( aaa_extra, rd_attrs, n); n += extra2attrs( aaa_extra_bye, rd_attrs, n); /* add and count the legs as attributes */ n += extra2attrs( leg_info, rd_attrs, n); n += extra2attrs( leg_bye_info, rd_attrs, n); rd_attrs[n++].name = "Sip-Call-Duration"; rd_attrs[n++].name = "Sip-Call-Setuptime"; rd_attrs[n++].name = "Sip-Call-Created"; prot_url.s = aaa_proto_url; prot_url.len = strlen(aaa_proto_url); if(aaa_prot_bind(&prot_url, &proto)) { LM_ERR("AAA protocol bind failure\n"); return -1; } conn = proto.init_prot(&prot_url); if (!conn) { LM_ERR("AAA protocol initialization failure\n"); return -1; } INIT_AV(proto, conn, rd_attrs, n, rd_vals, RV_STATIC_MAX, "acc", -1, -1); if (srv_type != -1) rd_vals[RV_SIP_SESSION].value = srv_type; LM_DBG("init_acc_aaa success!\n"); return 0; } static inline aaa_map *aaa_status( struct sip_msg *req, int code ) { if (req->REQ_METHOD == METHOD_INVITE && get_to(req)->tag_value.len == 0 && code>=200 && code<300) return &rd_vals[RV_STATUS_START]; if ((req->REQ_METHOD==METHOD_BYE || req->REQ_METHOD==METHOD_CANCEL)) return &rd_vals[RV_STATUS_STOP]; if (get_to(req)->tag_value.len) return &rd_vals[RV_STATUS_ALIVE]; return &rd_vals[RV_STATUS_FAILED]; } #define ADD_AAA_AVPAIR(_attr,_val,_len) \ do { \ if ( (_len)!=0 && \ proto.avp_add(conn, send, &rd_attrs[_attr], _val, _len, 0)) { \ LM_ERR("failed to add %s, %d\n", rd_attrs[_attr].name,_attr); \ goto error; \ } \ }while(0) int acc_aaa_request( struct sip_msg *req, struct sip_msg *rpl, int cdr_flag) { int attr_cnt; aaa_message *send; int offset, i, av_type; aaa_map *r_stat; unsigned int _created=0; unsigned int _setup_time=0; if ((send = proto.create_aaa_message(conn, AAA_ACCT)) == NULL) { LM_ERR("failed to create new aaa message for acct\n"); return -1; } if (cdr_flag && get_timestamps(&_created, &_setup_time)<0) { LM_ERR("cannot get timestamps\n"); return -1; } attr_cnt = core2strar( req, val_arr); /* not interested in the last 2 values */ attr_cnt -= 2; r_stat = aaa_status( req, acc_env.code); /* AAA PROTOCOL status */ ADD_AAA_AVPAIR( RA_ACCT_STATUS_TYPE, &(r_stat->value), -1); av_type = rd_vals[RV_SIP_SESSION].value; /* session*/ ADD_AAA_AVPAIR( RA_SERVICE_TYPE, &av_type, -1); av_type = (uint32_t)acc_env.code; /* status=integer */ ADD_AAA_AVPAIR( RA_SIP_RESPONSE_CODE, &av_type, -1); av_type = req->REQ_METHOD; /* method */ ADD_AAA_AVPAIR( RA_SIP_METHOD, &av_type, -1); /* unix time */ av_type = (uint32_t)acc_env.ts.tv_sec; ADD_AAA_AVPAIR( RA_TIME_STAMP, &av_type, -1); /* add extra also */ attr_cnt += extra2strar( aaa_extra, req, rpl, val_arr+attr_cnt, 0); /* add the values for the vector - start from 1 instead of * 0 to skip the first value which is the METHOD as string */ offset = RA_STATIC_MAX-1; for (i = 1; i < attr_cnt; i++) ADD_AAA_AVPAIR( offset + i, val_arr[i].s, val_arr[i].len ); if (cdr_flag) { av_type = (uint32_t)_setup_time; ADD_AAA_AVPAIR( offset + attr_cnt + 1, &av_type, -1); av_type = (uint32_t)_created; ADD_AAA_AVPAIR( offset + attr_cnt + 2, &av_type, -1); } /* call-legs attributes also get inserted */ if (leg_info) { offset += attr_cnt; attr_cnt = legs2strar(leg_info,req,val_arr,1); do { for (i = 0; i < attr_cnt; i++) ADD_AAA_AVPAIR( offset+i, val_arr[i].s, val_arr[i].len ); } while ((attr_cnt = legs2strar(leg_info,req,val_arr,0)) != 0); } if (proto.send_aaa_request(conn, send, NULL)) { LM_ERR("Radius accounting request failed for status: '%s' " "Call-Id: '%.*s' \n",r_stat->name, req->callid->body.len, req->callid->body.s); goto error; } proto.destroy_aaa_message(conn, send); return 1; error: proto.destroy_aaa_message(conn, send); return -1; } int acc_aaa_cdrs(struct dlg_cell *dlg, struct sip_msg *msg) { int nr_vals, i, j, ret, res = -1; time_t created; struct timeval start_time,end; str core_s, leg_s, extra_s; short nr_legs; aaa_message *send = NULL; int offset, av_type; aaa_map *r_stat; gettimeofday(&end,NULL); core_s.s = extra_s.s = leg_s.s = 0; ret = prebuild_core_arr(dlg, &core_s, &start_time); if (ret < 0) { LM_ERR("cannot copy core arguments\n"); goto error; } ret = prebuild_extra_arr(dlg, msg, &extra_s, &aaa_extra_str, aaa_extra_bye, ret); if (ret < 0) { LM_ERR("cannot copy extra arguments\n"); goto error; } /* here starts the extra leg */ nr_vals = prebuild_leg_arr(dlg, &leg_s, &nr_legs); if (nr_vals < 0) { LM_ERR("cannot compute leg values\n"); goto error; } if (!(created = acc_get_created(dlg))) { LM_ERR("cannot get created\n"); goto error; } if ((send = proto.create_aaa_message(conn, AAA_ACCT)) == NULL) { LM_ERR("failed to create new aaa message for acct\n"); goto error; } r_stat = &rd_vals[RV_STATUS_STOP]; /* AAA PROTOCOL status */ ADD_AAA_AVPAIR(RA_ACCT_STATUS_TYPE,&(r_stat->value),-1); av_type = rd_vals[RV_SIP_SESSION].value; /* session*/ ADD_AAA_AVPAIR( RA_SERVICE_TYPE, &av_type, -1); av_type = (uint32_t)acc_env.code; /* status=integer */ ADD_AAA_AVPAIR( RA_SIP_RESPONSE_CODE, &av_type, -1); av_type = METHOD_INVITE; /* method */ ADD_AAA_AVPAIR( RA_SIP_METHOD, &av_type, -1); av_type = (uint32_t)start_time.tv_sec; /* call start time */ ADD_AAA_AVPAIR( RA_TIME_STAMP, &av_type, -1); /* add the values for the vector - start from 1 instead of * 0 to skip the first value which is the METHOD as string */ offset = RA_STATIC_MAX-1; for (i = 1; i < ACC_CORE_LEN-2; i++) ADD_AAA_AVPAIR( offset + i, val_arr[i].s, val_arr[i].len ); for (i = ACC_CORE_LEN - 2; iname, val_arr[3].len, val_arr[3].s); goto error; } res = 1; error: if (core_s.s) pkg_free(core_s.s); if (extra_s.s) pkg_free(extra_s.s); if (leg_s.s) pkg_free(leg_s.s); return res; } int store_aaa_extra_values(struct dlg_cell *dlg, struct sip_msg *req, struct sip_msg *reply) { return store_extra_values(aaa_extra, &aaa_extra_str, dlg, req, reply); } /******************************************** * DIAMETER ACCOUNTING ********************************************/ #ifdef DIAM_ACC #define AA_REQUEST 265 #define AA_ANSWER 265 #define ACCOUNTING_REQUEST 271 #define ACCOUNTING_ANSWER 271 static int diam_attrs[ACC_CORE_LEN+MAX_ACC_EXTRA+MAX_ACC_LEG]; int acc_diam_init() { int n; int m; n = 0; /* caution: keep these aligned to core acc output */ diam_attrs[n++] = AVP_SIP_METHOD; diam_attrs[n++] = AVP_SIP_FROM_TAG; diam_attrs[n++] = AVP_SIP_TO_TAG; diam_attrs[n++] = AVP_SIP_CALLID; diam_attrs[n++] = AVP_SIP_STATUS; m = extra2int( dia_extra, diam_attrs+n); if (m<0) { LM_ERR("extra names for DIAMETER must be integer AVP codes\n"); return -1; } n += m; m = extra2int( leg_info, diam_attrs+n); if (m<0) { LM_ERR("leg info names for DIAMTER must be integer AVP codes\n"); return -1; } n += m; return 0; } inline unsigned long diam_status(struct sip_msg *rq, int code) { if ((rq->REQ_METHOD==METHOD_INVITE || rq->REQ_METHOD==METHOD_ACK) && code>=200 && code<300) return AAA_ACCT_START; if ((rq->REQ_METHOD==METHOD_BYE || rq->REQ_METHOD==METHOD_CANCEL)) return AAA_ACCT_STOP; if (code>=200 && code <=300) return AAA_ACCT_EVENT; return -1; } int acc_diam_request( struct sip_msg *req, struct sip_msg *rpl) { int attr_cnt; int cnt; AAAMessage *send = NULL; AAA_AVP *avp; struct sip_uri puri; str *uri; int ret; int i; int status; char tmp[2]; unsigned int mid; attr_cnt = core2strar( req, val_arr); /* last value is not used */ attr_cnt--; if ( (send=AAAInMessage(ACCOUNTING_REQUEST, AAA_APP_NASREQ))==NULL) { LM_ERR("failed to create new AAA request\n"); return -1; } /* AVP_ACCOUNTIG_RECORD_TYPE */ if( (status = diam_status(req, acc_env.code))<0) { LM_ERR("status unknown\n"); goto error; } tmp[0] = status+'0'; tmp[1] = 0; if( (avp=AAACreateAVP(AVP_Accounting_Record_Type, 0, 0, tmp, 1, AVP_DUPLICATE_DATA)) == 0) { LM_ERR("failed to create AVP:no more free memory!\n"); goto error; } if( AAAAddAVPToMessage(send, avp, 0)!= AAA_ERR_SUCCESS) { LM_ERR("avp not added \n"); AAAFreeAVP(&avp); goto error; } /* SIP_MSGID AVP */ mid = req->id; if( (avp=AAACreateAVP(AVP_SIP_MSGID, 0, 0, (char*)(&mid), sizeof(mid), AVP_DUPLICATE_DATA)) == 0) { LM_ERR("failed to create AVP:no more free memory!\n"); goto error; } if( AAAAddAVPToMessage(send, avp, 0)!= AAA_ERR_SUCCESS) { LM_ERR("avp not added \n"); AAAFreeAVP(&avp); goto error; } /* SIP Service AVP */ if( (avp=AAACreateAVP(AVP_Service_Type, 0, 0, SIP_ACCOUNTING, SERVICE_LEN, AVP_DUPLICATE_DATA)) == 0) { LM_ERR("failed to create AVP:no more free memory!\n"); goto error; } if( AAAAddAVPToMessage(send, avp, 0)!= AAA_ERR_SUCCESS) { LM_ERR("avp not added \n"); AAAFreeAVP(&avp); goto error; } /* also the extra attributes */ attr_cnt += extra2strar( dia_extra, rpl, req, val_arr, 0); /* add attributes */ for(i=0; is, uri->len, &puri) < 0) { LM_ERR("failed to parse From/To URI\n"); goto error; } /* Destination-Realm AVP */ if( (avp=AAACreateAVP(AVP_Destination_Realm, 0, 0, puri.host.s, puri.host.len, AVP_DUPLICATE_DATA)) == 0) { LM_ERR("failed to create AVP:no more free memory!\n"); goto error; } if( AAAAddAVPToMessage(send, avp, 0)!= AAA_ERR_SUCCESS) { LM_ERR("avp not added \n"); AAAFreeAVP(&avp); goto error; } /* prepare the message to be sent over the network */ if(AAABuildMsgBuffer(send) != AAA_ERR_SUCCESS) { LM_ERR("message buffer not created\n"); goto error; } if(sockfd==AAA_NO_CONNECTION) { sockfd = init_mytcp(diameter_client_host, diameter_client_port); if(sockfd==AAA_NO_CONNECTION) { LM_ERR("failed to reconnect to Diameter client\n"); goto error; } } /* send the message to the DIAMETER client */ ret = tcp_send_recv(sockfd, send->buf.s, send->buf.len, rb, req->id); if(ret == AAA_CONN_CLOSED) { LM_NOTICE("connection to Diameter client closed.It will be " "reopened by the next request\n"); close(sockfd); sockfd = AAA_NO_CONNECTION; goto error; } if(ret != ACC_SUCCESS) { /* a transmission error occurred */ LM_ERR("message sending to the DIAMETER backend authorization " "server failed\n"); goto error; } AAAFreeMessage(&send); return 1; error: AAAFreeMessage(&send); return -1; } #endif /******************************************** * EVENT INTERFACE ACCOUNTING ********************************************/ /* names of the parameters of the event */ static str acc_method_evi = str_init("method"); static str acc_fromtag_evi = str_init("from_tag"); static str acc_totag_evi = str_init("to_tag"); static str acc_callid_evi = str_init("callid"); static str acc_sipcode_evi = str_init("sip_code"); static str acc_sipreason_evi = str_init("sip_reason"); static str acc_time_evi = str_init("time"); static str acc_duration_evi = str_init("duration"); static str acc_ms_duration_evi= str_init("ms_duration"); static str acc_setuptime_evi = str_init("setuptime"); static str acc_created_evi = str_init("created"); static str evi_acc_name = str_init("E_ACC_CDR"); static str evi_acc_event_name = str_init("E_ACC_EVENT"); static str evi_acc_missed_name = str_init("E_ACC_MISSED_EVENT"); /* static event's list */ static evi_params_p acc_event_params; static evi_param_p evi_params[ACC_CORE_LEN+1+ACC_DLG_LEN+MAX_ACC_EXTRA+MAX_ACC_LEG]; #define EVI_CREATE_PARAM(_name) \ do { \ if (!(evi_params[n++] = \ evi_param_create(acc_event_params, &(_name)))) \ goto error; \ } while (0) int init_acc_evi(void) { struct acc_extra *extra; int n; acc_event = evi_publish_event(evi_acc_event_name); if (acc_event == EVI_ERROR) { LM_ERR("cannot register ACC event\n"); return -1; } acc_cdr_event = evi_publish_event(evi_acc_name); if (acc_cdr_event == EVI_ERROR) { LM_ERR("cannot register ACC CDR event\n"); return -1; } acc_missed_event = evi_publish_event(evi_acc_missed_name); if (acc_missed_event == EVI_ERROR) { LM_ERR("cannot register missed CDR event\n"); return -1; } /* we handle the parameters list by ourselves */ acc_event_params = pkg_malloc(sizeof(evi_params_t)); if (!acc_event_params) { LM_ERR("no more pkg mem\n"); return -1; } memset(acc_event_params, 0, sizeof(evi_params_t)); n = 0; EVI_CREATE_PARAM(acc_method_evi); EVI_CREATE_PARAM(acc_fromtag_evi); EVI_CREATE_PARAM(acc_totag_evi); EVI_CREATE_PARAM(acc_callid_evi); EVI_CREATE_PARAM(acc_sipcode_evi); EVI_CREATE_PARAM(acc_sipreason_evi); EVI_CREATE_PARAM(acc_time_evi); /* init the extra db keys */ for(extra=evi_extra; extra ; extra=extra->next) EVI_CREATE_PARAM(extra->name); for(extra=evi_extra_bye; extra ; extra=extra->next) EVI_CREATE_PARAM(extra->name); /* multi leg call columns */ for( extra=leg_info ; extra ; extra=extra->next) EVI_CREATE_PARAM(extra->name); for( extra=leg_bye_info ; extra ; extra=extra->next) EVI_CREATE_PARAM(extra->name); EVI_CREATE_PARAM(acc_duration_evi); EVI_CREATE_PARAM(acc_ms_duration_evi); EVI_CREATE_PARAM(acc_setuptime_evi); EVI_CREATE_PARAM(acc_created_evi); return 0; error: LM_ERR("error while creating parameter %d\n", n-1); return -1; } #undef EVI_CREATE_PARAM int acc_evi_request( struct sip_msg *rq, struct sip_msg *rpl, int cdr_flag) { int m; int n; int i; int backup_idx = -1, ret = -1; unsigned int _created=0; unsigned int _setup_time=0; /* * if the code is not set, choose the missed calls event * otherwise, check if the code is negative */ if (acc_env.event == EVI_ERROR) { LM_ERR("event not registered %d\n", acc_event); return -1; } /* check if someone is interested in this event */ if (!evi_probe_event(acc_env.event)) return 1; if (cdr_flag && get_timestamps(&_created, &_setup_time)<0) { LM_ERR("cannot get timestamps\n"); return -1; } m = core2strar( rq, val_arr ); for(i = 0; i < m; i++) if(evi_param_set_str(evi_params[i], &val_arr[i]) < 0) { LM_ERR("cannot set acc parameter\n"); return -1; } /* time value */ if (evi_param_set_int(evi_params[m++], &acc_env.ts) < 0) { LM_ERR("cannot set timestamp parameter\n"); return -1; } /* extra columns */ m += extra2strar( evi_extra, rq, rpl, val_arr+m, 0); for( i++; i < m; i++) if(evi_param_set_str(evi_params[i], &val_arr[i]) < 0) { LM_ERR("cannot set acc extra parameter\n"); return -1; } /* little hack to jump over the duration parameter*/ m++; if (cdr_flag && evi_param_set_int(evi_params[m++], &_setup_time) < 0) { LM_ERR("cannot set setuptime parameter\n"); goto end; } if (cdr_flag && evi_param_set_int(evi_params[m++], &_created) < 0) { LM_ERR("cannot set created parameter\n"); goto end; } /* multi-leg columns */ if ( !leg_info ) { /* * XXX: hack to skip adding the information that doesn't exist * for these requests - leg info, duration, setuptime, etc. */ backup_idx = m - 1; evi_params[backup_idx]->next = NULL; if (evi_raise_event(acc_env.event, acc_event_params) < 0) { LM_ERR("cannot raise ACC event\n"); goto end; } } else { n = legs2strar(leg_info,rq,val_arr+m,1); do { for ( i = m; i < m + n; i++) if(evi_param_set_str(evi_params[i], &val_arr[i]) < 0) { LM_ERR("cannot set acc extra parameter\n"); goto end; } /* XXX: same hack for above, but at least now we have legs */ backup_idx = i - 1; evi_params[backup_idx]->next = NULL; if (evi_raise_event(acc_env.event, acc_event_params) < 0) { LM_ERR("cannot raise ACC event\n"); goto end; } evi_params[backup_idx]->next = evi_params[backup_idx + 1]; }while ( (n = legs2strar(leg_info,rq,val_arr+m,0))!=0 ); } ret = 1; end: if (backup_idx!=-1) /* can be -1, jumped to end: label*/ evi_params[backup_idx]->next = evi_params[backup_idx + 1]; return ret; } int acc_evi_cdrs(struct dlg_cell *dlg, struct sip_msg *msg) { int nr_vals, i, ret, res = -1, nr_bye_vals = 0, j; int aux_time; time_t created; struct timeval start_time,end; str core_s, leg_s, extra_s; short nr_legs; if (acc_cdr_event == EVI_ERROR) { LM_ERR("event not registered %d\n", acc_cdr_event); return -1; } /* check if someone is interested in this event */ if (!evi_probe_event(acc_cdr_event)) return 1; gettimeofday(&end,NULL); core_s.s = extra_s.s = leg_s.s = 0; ret = prebuild_core_arr(dlg, &core_s, &start_time); if (ret < 0) { LM_ERR("cannot copy core arguments\n"); goto end; } LM_DBG("XXX: nr before extra is: %d\n", ret); ret = prebuild_extra_arr(dlg, msg, &extra_s, &evi_extra_str, evi_extra_bye, ret); if (ret < 0) { LM_ERR("cannot copy extra arguments\n"); goto end; } LM_DBG("XXX: nr after extra is: %d\n", ret); /* here starts the extra leg */ nr_vals = prebuild_leg_arr(dlg, &leg_s, &nr_legs); if (nr_vals < 0) { LM_ERR("cannot compute leg values\n"); goto end; } if (!(created = acc_get_created(dlg))) { LM_ERR("cannot get created\n"); goto end; } for (i=0;is == NULL) value->len = 0; if (cdr_buf.len + value->len + 2 > cdr_len) { if (cdr_len == 0) { cdr_len = STRING_INIT_SIZE; cdr_buf.s = (char*)pkg_malloc(cdr_len); if (!cdr_buf.s) { LM_ERR("No more memory\n"); return -1; } } else { do { /* realloc until memory is large enough */ cdr_len *= 2; } while (cdr_len < cdr_buf.len + value->len + 2); cdr_buf.s = pkg_realloc(cdr_buf.s, cdr_len); if (cdr_buf.s == NULL) { LM_ERR("No more memory\n"); return -1; } } } if (value->len > MAX_LEN_VALUE) { value->len = MAX_LEN_VALUE; LM_WARN("Value too log, truncating..\n"); } SET_LEN(cdr_buf.s + cdr_buf.len, value->len); memcpy(cdr_buf.s + cdr_buf.len + 2, value->s, value->len); cdr_buf.len += value->len + 2; return 1; } static void complete_dlg_values(str *stored_values,str *val_arr,short nr_vals) { short i; char *p = stored_values->s + stored_values->len; short len; for (i=0;ilen = p - stored_values->s; } /* stores core values and leg values into dlg */ int store_core_leg_values(struct dlg_cell *dlg, struct sip_msg *req) { if ( build_core_dlg_values(dlg, req) < 0) { LM_ERR("cannot build core value string\n"); return -1; } if ( dlg_api.store_dlg_value(dlg, &core_str, &cdr_buf) < 0) { LM_ERR("cannot store core values into dialog\n"); return -1; } if ( build_leg_dlg_values(dlg, req) < 0) { LM_ERR("cannot build legs value string\n"); return -1; } if (dlg_api.store_dlg_value(dlg,&leg_str,&cdr_buf) < 0) { LM_ERR("cannot store dialog string\n"); return -1; } return 1; } /* stores extra values into dlg */ static int store_extra_values(struct acc_extra* extra, str *values_str, struct dlg_cell *dlg, struct sip_msg *req, struct sip_msg *reply) { if ( build_extra_dlg_values(extra, dlg, req, reply) < 0) { LM_ERR("cannot build core value string\n"); return -1; } if ( dlg_api.store_dlg_value(dlg, values_str, &cdr_buf) < 0) { LM_ERR("cannot store core values into dialog\n"); return -1; } return 1; } /* builds core string */ static int build_core_dlg_values(struct dlg_cell *dlg,struct sip_msg *req) { str value; int i, count; cdr_buf.len = 0; count = core2strar( req, val_arr); for (i=0; ilen = 0; buffer->s = 0; /* fetching core string values */ if (dlg_api.fetch_dlg_value(dlg, &core_str, buffer, 1) < 0) { LM_ERR("cannot fetch core string value\n"); return -1; } buffer->len = 0; complete_dlg_values(buffer, val_arr, ACC_CORE_LEN+1); memcpy(start, val_arr[ACC_CORE_LEN].s, val_arr[ACC_CORE_LEN].len); return ACC_CORE_LEN; } /* gets extra values from dlg and stores them into val_arr array */ static int prebuild_extra_arr(struct dlg_cell *dlg, struct sip_msg *msg, str *buffer, str *type_str, struct acc_extra * extra, int start) { short extra_len; if (!start || !type_str || !buffer) { LM_ERR("invalid parameters\n"); return -1; } buffer->len = 0; buffer->s = 0; /* fetching extra string values */ if (dlg_api.fetch_dlg_value(dlg, type_str, buffer, 1) < 0) { LM_ERR("cannot fetch core string value\n"); return -1; } extra_len = GET_LEN(buffer->s); buffer->len = 2; complete_dlg_values(buffer, val_arr + start, extra_len); start += extra_len; /* populate the extra from bye */ return start + extra2strar(extra, msg, NULL, val_arr + start, 1); } /* gets leg values from dlg and stores them into val_arr array */ static int prebuild_leg_arr(struct dlg_cell *dlg, str *buffer, short *nr_legs) { if (!buffer || !nr_legs) { LM_ERR("invalid parameters\n"); return -1; } buffer->len = 0; buffer->s = 0; /* fetching leg string values */ if (dlg_api.fetch_dlg_value(dlg, &leg_str, buffer, 1) < 0) { LM_ERR("cannot fetch core string value\n"); return -1; } /* getting legs number */ *nr_legs = GET_LEN(buffer->s+2); return GET_LEN(buffer->s); } /* gets leg values from dlg and stores them into val_arr array */ static time_t acc_get_created(struct dlg_cell *dlg) { time_t created; str aux; if (dlg_api.fetch_dlg_value(dlg, &created_str, &aux, 0) < 0) { LM_ERR("error getting dialog creation time\n"); return 0; } memcpy(&created, aux.s, aux.len); return created; } opensips-2.2.2/modules/acc/acc.h000066400000000000000000000103641300170765700164640ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-04 grand acc cleanup (jiri) * 2003-11-04 multidomain support for mysql introduced (jiri) * 2004-06-06 cleanup: acc_db_{bind,init,close} added (andrei) * 2006-09-08 flexible multi leg accounting support added, * code cleanup for low level functions (bogdan) * 2006-09-19 final stage of a masive re-structuring and cleanup (bogdan) */ #ifndef _ACC_ACC_H_ #define _ACC_ACC_H_ #include "../../db/db_insertq.h" #define ACC_CORE_LEN 6 #define ACC_DLG_LEN 3 /* leading text for a request accounted from a script */ #define ACC "ACC: " #define ACC_REQUEST ACC"request accounted: " #define ACC_REQUEST_LEN (sizeof(ACC_REQUEST)-1) #define ACC_MISSED ACC"call missed: " #define ACC_MISSED_LEN (sizeof(ACC_MISSED)-1) #define ACC_ANSWERED ACC"transaction answered: " #define ACC_ANSWERED_LEN (sizeof(ACC_ANSWERED)-1) #define ACC_ACKED ACC"request acknowledged: " #define ACC_ACKED_LEN (sizeof(ACC_ACKED)-1) #define ACC_ENDED ACC"call ended: " #define ACC_ENDED_LEN (sizeof(ACC_ENDED)-1) /* syslog attribute names */ #define A_METHOD "method" #define A_METHOD_LEN (sizeof(A_METHOD)-1) #define A_FROMTAG "from_tag" #define A_FROMTAG_LEN (sizeof(A_FROMTAG)-1) #define A_TOTAG "to_tag" #define A_TOTAG_LEN (sizeof(A_TOTAG)-1) #define A_CALLID "call_id" #define A_CALLID_LEN (sizeof(A_CALLID)-1) #define A_CODE "code" #define A_CODE_LEN (sizeof(A_CODE)-1) #define A_STATUS "reason" #define A_STATUS_LEN (sizeof(A_STATUS)-1) #define A_DURATION "duration" #define A_DURATION_LEN (sizeof(A_DURATION)-1) #define A_SETUPTIME "setuptime" #define A_SETUPTIME_LEN (sizeof(A_SETUPTIME)-1) #define A_CREATED "created" #define A_CREATED_LEN (sizeof(A_CREATED)-1) #define A_SEPARATOR_CHR ';' #define A_EQ_CHR '=' #define MAX_SYSLOG_SIZE 65536 #define STRING_INIT_SIZE 128 #include "../dialog/dlg_load.h" extern struct dlg_binds dlg_api; void acc_log_init(); int acc_log_request( struct sip_msg *req, struct sip_msg *rpl, int cdr_flag); int acc_log_cdrs(struct dlg_cell *dlg, struct sip_msg *msg); int store_log_extra_values(struct dlg_cell *dlg, struct sip_msg *req, struct sip_msg *reply); int acc_db_init(const str* db_url); int acc_db_init_child(const str* db_url); void acc_db_close(); int acc_db_request( struct sip_msg *req, struct sip_msg *rpl, query_list_t **ins_list, int cdr_flag); int acc_db_cdrs(struct dlg_cell *dlg, struct sip_msg *msg); int store_db_extra_values(struct dlg_cell *dlg, struct sip_msg *req, struct sip_msg *reply); int init_acc_aaa(char* aaa_proto_url, int srv_type); int acc_aaa_request( struct sip_msg *req, struct sip_msg *rpl, int cdr_flag); int acc_aaa_cdrs_request(struct dlg_cell *dlg); int acc_aaa_cdrs(struct dlg_cell *dlg, struct sip_msg *msg); int store_aaa_extra_values(struct dlg_cell *dlg, struct sip_msg *req, struct sip_msg *reply); int store_core_leg_values(struct dlg_cell *dlg, struct sip_msg *req); int store_created_dlg_time(struct dlg_cell *dlg); int create_acc_dlg(struct sip_msg* req); int init_acc_evi(void); int acc_evi_request( struct sip_msg *req, struct sip_msg *rpl, int cdr_flag); int acc_evi_cdrs(struct dlg_cell *dlg, struct sip_msg *msg); int store_evi_extra_values(struct dlg_cell *dlg, struct sip_msg *req, struct sip_msg *reply); extern event_id_t acc_cdr_event; extern event_id_t acc_event; extern event_id_t acc_missed_event; #ifdef DIAM_ACC int acc_diam_init(); int acc_diam_request( struct sip_msg *req, struct sip_msg *rpl); #endif #endif opensips-2.2.2/modules/acc/acc_extra.c000066400000000000000000000206101300170765700176550ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2004-10-28 first version (ramona) * 2005-05-30 acc_extra patch commited (ramona) * 2005-07-13 acc_extra specification moved to use pseudo-variables (bogdan) * 2006-09-08 flexible multi leg accounting support added, * code cleanup for low level functions (bogdan) * 2006-09-19 final stage of a masive re-structuring and cleanup (bogdan) */ #include #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../usr_avp.h" #include "../../socket_info.h" #include "../../mem/mem.h" #include "acc_extra.h" #define EQUAL '=' #define SEPARATOR ';' #define REPLY_STR_S "reply" #define REPLY_STR_LEN (sizeof(REPLY_STR_S)-1) #if MAX_ACC_EXTRAnext ) { if (it->spec.type!=PVT_AVP) { LM_ERR("only AVP are accepted as leg info\n"); destroy_extras(legs); return 0; } n++; if (n>MAX_ACC_LEG) { LM_ERR("too many leg info; MAX=%d\n", MAX_ACC_LEG); destroy_extras(legs); return 0; } } return legs; } struct acc_extra *parse_acc_extra(char *extra_str, int allow_reply) { struct acc_extra *head; struct acc_extra *tail; struct acc_extra *extra; char *foo; char *s; int n; str stmp; n = 0; head = 0; extra = 0; tail = 0; s = extra_str; if (s==0) { LM_ERR("null string received\n"); goto error; } while (*s) { /* skip white spaces */ while (*s && isspace((int)*s)) s++; if (*s==0) goto parse_error; if (n==MAX_ACC_EXTRA) { LM_ERR("too many extras -> please increase the internal buffer\n"); goto error; } extra = (struct acc_extra*)pkg_malloc(sizeof(struct acc_extra)); if (extra==0) { LM_ERR("no more pkg mem 1\n"); goto error; } memset( extra, 0, sizeof(struct acc_extra)); /* link the new extra at the end */ if (tail==0) { head = extra; } else { tail->next = extra; } tail = extra; n++; /* get name */ foo = s; while (*s && !isspace((int)*s) && EQUAL!=*s) s++; if (*s==0) goto parse_error; if (*s==EQUAL) { extra->name.len = (s++) - foo; } else { extra->name.len = (s++) - foo; /* skip spaces */ while (*s && isspace((int)*s)) s++; if (*s!=EQUAL) goto parse_error; s++; } extra->name.s = foo; /* skip spaces */ while (*s && isspace((int)*s)) s++; /* get value type */ stmp.s = s; stmp.len = strlen(s); if ( (foo=pv_parse_spec(&stmp, &extra->spec))==0 ) goto parse_error; s = foo; /* skip spaces */ while (*s && isspace((int)*s)) s++; /* type of message - request or reply ? */ if (allow_reply && *s=='/') { s++; while (*s && isspace((int)*s)) s++; if (*s==0) goto parse_error; foo = s; while (*s && isalpha((int)*s)) s++; if (s-foo==REPLY_STR_LEN && strncasecmp(foo,REPLY_STR_S,REPLY_STR_LEN)==0 ) { extra->use_rpl =1; } else { LM_ERR("unsupported marker <%.*s>\n",(unsigned int)(s-foo),foo); goto error; } } /* skip spaces */ while (*s && isspace((int)*s)) s++; if (*s && (*(s++)!=SEPARATOR || *s==0)) goto parse_error; } /* go throught all extras and make the names null terminated */ for( extra=head ; extra ; extra=extra->next) extra->name.s[extra->name.len] = 0; return head; parse_error: LM_ERR("parse failed in <%s> " "around position %d\n",extra_str, (int)(long)(s-extra_str)); error: LM_ERR("error\n"); destroy_extras(head); return 0; } void destroy_extras( struct acc_extra *extra) { struct acc_extra *foo; while (extra) { foo = extra; extra = extra->next; pkg_free(foo); } } /* extra name is moved as string part of an attribute; str.len will contain an * index to the corresponding attribute */ int extra2attrs( struct acc_extra *extra, aaa_map *attrs, int offset) { int i; for(i = 0 ; extra ; i++, extra=extra->next) { attrs[offset+i].name = extra->name.s; } return i; } /* converts the name of the extra from str to integer * and stores it over str.len ; str.s is freed and made zero */ int extra2int( struct acc_extra *extra, int *attrs ) { unsigned int ui; int i; for( i=0 ; extra ; i++,extra=extra->next ) { if (str2int( &extra->name, &ui)!=0) { LM_ERR("<%s> is not a number\n", extra->name.s); return -1; } attrs[i] = (int)ui; } return i; } int extra2strar( struct acc_extra *extra, struct sip_msg *rq, struct sip_msg *rpl, str *val_arr, int idx) { pv_value_t value; int n; int r; if (idx < 0 || idx > MAX_ACC_BUFS-2 /* last one is for legs */) { LM_ERR("Invalid buffer index %d - maximum %d\n", idx, MAX_ACC_BUFS-2); return 0; } if(rq == NULL) { for (n=0; extra ; extra=extra->next,n++) { val_arr[n].s = 0; val_arr[n].len = 0; } return n; } memset(&value, 0, sizeof(pv_value_t)); for( n=0,r=0 ; extra ; extra=extra->next,n++) { /* get the value */ if (extra->use_rpl) { if (rpl==NULL || rpl==FAKED_REPLY ) { /* force a NULL value */ value.flags |= PV_VAL_NULL; } else if (pv_get_spec_value( rpl, &extra->spec, &value)!=0 ) { LM_ERR("failed to get '%.*s'\n",extra->name.len,extra->name.s); /* force a NULL value */ value.flags |= PV_VAL_NULL; } } else { if (pv_get_spec_value( rq, &extra->spec, &value)!=0) { LM_ERR("failed to get '%.*s'\n",extra->name.len,extra->name.s); /* force a NULL value */ value.flags |= PV_VAL_NULL; } } /* check for overflow */ if (n==MAX_ACC_EXTRA) { LM_WARN("array to short -> ommiting extras for accounting\n"); goto done; } if(value.flags&PV_VAL_NULL) { /* convert to empty to have consistency */ val_arr[n].s = 0; val_arr[n].len = 0; } else { /* set the value into the acc buffer */ if (value.rs.s+value.rs.len==static_detector[0] || value.rs.s==static_detector[1]) { val_arr[n].s = int_buf[idx] + r*INT2STR_MAX_LEN; val_arr[n].len = value.rs.len; memcpy(val_arr[n].s, value.rs.s, value.rs.len); r++; } else { val_arr[n] = value.rs; } } } done: return n; } int legs2strar( struct acc_extra *legs, struct sip_msg *rq, str *val_arr, int start) { static struct usr_avp *avp[MAX_ACC_LEG]; unsigned short name_type; int name; int_str value; int n; int found; int r; found = 0; r = 0; for( n=0 ; legs ; legs=legs->next,n++ ) { /* search for the AVP */ if (start) { if ( pv_get_avp_name( rq, &(legs->spec.pvp), &name, &name_type)<0 ) goto exit; avp[n] = search_first_avp( name_type, name, &value, 0); } else { avp[n] = search_next_avp( avp[n], &value); } /* set new leg record */ if (avp[n]) { found = 1; /* get its value */ if(avp[n]->flags & AVP_VAL_STR) { val_arr[n] = value.s; } else { val_arr[n].s = int2bstr( value.n, int_buf[MAX_ACC_BUFS-1]+r*INT2STR_MAX_LEN, &val_arr[n].len); r++; } } else { val_arr[n].s = 0; val_arr[n].len = 0; } } if (found || start) return n; exit: return 0; } opensips-2.2.2/modules/acc/acc_extra.h000066400000000000000000000042641300170765700176710ustar00rootroot00000000000000/* * Copyright (C) 2004 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2004-10-28 first version (ramona) * 2005-05-30 acc_extra patch commited (ramona) * 2005-07-13 acc_extra specification moved to use pseudo-variables (bogdan) * 2006-09-08 flexible multi leg accounting support added, * code cleanup for low level functions (bogdan) * 2006-09-19 final stage of a masive re-structuring and cleanup (bogdan) */ #ifndef _ACC_EXTRA_H_ #define _ACC_EXTRA_H_ #include "../../str.h" #include "../../pvar.h" #include "../../parser/msg_parser.h" struct acc_extra { str name; /* name (log comment/ column name) */ pv_spec_t spec; /* value's spec */ unsigned int use_rpl; /* if this value should be taken from reply */ struct acc_extra *next; /* next extra value */ }; #define MAX_ACC_EXTRA 64 #define MAX_ACC_LEG 16 #define MAX_ACC_BUFS 3 void init_acc_extra(); struct acc_extra *parse_acc_extra(char *extra, int allow_reply); struct acc_extra *parse_acc_leg(char *extra); void destroy_extras( struct acc_extra *extra); int extra2strar( struct acc_extra *extra, struct sip_msg *rq, struct sip_msg *rpl,str *val_arr, int idx); int legs2strar( struct acc_extra *legs, struct sip_msg *rq, str *val_arr, int start); int extra2int( struct acc_extra *extra, int *attrs ); #include "../../aaa/aaa.h" int extra2attrs( struct acc_extra *extra, aaa_map *attrs, int offset); #endif opensips-2.2.2/modules/acc/acc_logic.c000066400000000000000000001013131300170765700176270ustar00rootroot00000000000000/* * Accounting module logic * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2006-09-19 forked from the acc_mod.c file during a big re-structuring * of acc module (bogdan) */ #include #include #include "../../dprint.h" #include "../../parser/parse_from.h" #include "../../parser/parse_content.h" #include "../tm/tm_load.h" #include "../rr/api.h" #include "../dialog/dlg_load.h" #include "../dialog/dlg_hash.h" #include "../../aaa/aaa.h" #include "../../mod_fix.h" #ifdef DIAM_ACC #include "diam_dict.h" #include "diam_tcp.h" #endif #include "acc.h" #include "acc_mod.h" #include "acc_logic.h" extern struct tm_binds tmb; extern struct rr_binds rrb; extern str flags_str; extern str table_str; extern str acc_created_avp_name; extern int acc_created_avp_id; extern int acc_flags_ctx_idx; extern int acc_tm_flags_ctx_idx; struct acc_enviroment acc_env; static query_list_t *acc_ins_list = NULL; static query_list_t *mc_ins_list = NULL; static int is_cdr_enabled=0; #define is_acc_flag_set(_mask, _type, _flag) ( _mask & ((_type * _flag))) #define is_log_flag_on(_mask, _flag) is_acc_flag_set(_mask, DO_ACC_LOG, _flag) #define is_log_acc_on(_mask) is_log_flag_on(_mask, DO_ACC) #define is_log_cdr_on(_mask) is_log_flag_on(_mask, DO_ACC_CDR) #define is_log_mc_on(_mask) is_log_flag_on(_mask, DO_ACC_MISSED) #define is_log_failed_on(_mask) is_log_flag_on(_mask, DO_ACC_FAILED) #define is_aaa_flag_on(_mask, _flag) is_acc_flag_set(_mask, DO_ACC_AAA, _flag) #define is_aaa_acc_on(_mask) is_aaa_flag_on(_mask, DO_ACC) #define is_aaa_cdr_on(_mask) is_aaa_flag_on(_mask, DO_ACC_CDR) #define is_aaa_mc_on(_mask) is_aaa_flag_on(_mask, DO_ACC_MISSED) #define is_aaa_failed_on(_mask) is_aaa_flag_on(_mask, DO_ACC_FAILED) #define is_db_flag_on(_mask, _flag) is_acc_flag_set(_mask, DO_ACC_DB, _flag) #define is_db_acc_on(_mask) is_db_flag_on(_mask, DO_ACC) #define is_db_cdr_on(_mask) is_db_flag_on(_mask, DO_ACC_CDR) #define is_db_mc_on(_mask) is_db_flag_on(_mask, DO_ACC_MISSED) #define is_db_failed_on(_mask) is_db_flag_on(_mask, DO_ACC_FAILED) #define is_evi_flag_on(_mask, _flag) is_acc_flag_set(_mask, DO_ACC_EVI, _flag) #define is_evi_acc_on(_mask) is_evi_flag_on(_mask, DO_ACC) #define is_evi_cdr_on(_mask) is_evi_flag_on(_mask, DO_ACC_CDR) #define is_evi_mc_on(_mask) is_evi_flag_on(_mask, DO_ACC_MISSED) #define is_evi_failed_on(_mask) is_evi_flag_on(_mask, DO_ACC_FAILED) #ifdef DIAM_ACC #define is_diam_flag_on(_mask, _flag) is_acc_flag_set(_mask, DO_ACC_DIAM, _flag) #define is_diam_acc_on(_mask) is_diam_flag_on(_mask, DO_ACC) #define is_diam_cdr_on(_mask) is_diam_flag_on(_mask, DO_ACC_CDR) #define is_diam_mc_on(_mask) is_diam_flag_on(_mask, DO_ACC_MISSED) #define is_diam_failed_on(_mask) is_diam_flag_on(_mask, DO_ACC_FAILED) #else #define is_diam_acc_on(_mask) (0) #define is_diam_cdr_on(_mask) (0) #define is_diam_mc_on(_mask) (0) #define is_diam_failed_on(_mask) (0) #endif #define is_acc_on(_mask) \ ( (is_log_acc_on(_mask)) || (is_db_acc_on(_mask)) \ || (is_aaa_acc_on(_mask)) || (is_diam_acc_on(_mask)) \ || (is_evi_acc_on(_mask)) ) #define is_cdr_acc_on(_mask) (is_log_cdr_on(_mask) || \ is_aaa_cdr_on(_mask) || is_db_cdr_on(_mask) || \ is_evi_cdr_on(_mask)||is_diam_cdr_on(_mask)) #define is_mc_acc_on(_mask) (is_log_mc_on(_mask) || \ is_aaa_mc_on(_mask) || is_db_mc_on(_mask) || \ is_evi_mc_on(_mask) || is_diam_mc_on(_mask)) #define is_failed_acc_on(_mask) (is_log_failed_on(_mask) || \ is_aaa_failed_on(_mask) || is_db_failed_on(_mask) || \ is_evi_failed_on(_mask) || is_diam_failed_on(_mask)) #define set_dialog_context(_mask) \ (_mask) |= ACC_DIALOG_CONTEXT; #define is_dialog_context(_mask) ((_mask)&ACC_DIALOG_CONTEXT) #define set_cdr_values_registered(_mask) \ (_mask) |= ACC_CDR_REGISTERED; #define set_dlg_cb_used(_mask) \ (_mask) |= ACC_DLG_CB_USED; #define set_failure_cb_registered(_mask) \ (_mask) |= ACC_TMCB_MISSED_REGISTERED; #define was_dlg_cb_used(_mask) (_mask&ACC_DLG_CB_USED) #define cdr_values_registered(_mask) ((_mask)&ACC_CDR_REGISTERED) #define failure_cb_registered(_mask) ((_mask)&ACC_TMCB_MISSED_REGISTERED) #define reset_flags(_flags, _flags_to_reset) \ _flags &= ~_flags_to_reset; #define skip_cancel(_rq) \ (((_rq)->REQ_METHOD==METHOD_CANCEL) && report_cancels==0) /* * the 8th byte of the mask will be a reference counter for dialog callbacks * each time we enter a diallog callback(acc_dlg_callback) the reference counter shall * be increased * each time we enter a dialog callback free function(dlg_free_acc_mask) the refernce * counter shall be decreased * when the counter reaches 0 the mask shall be freed */ #define ACC_MASK_INC_REF(mask) \ do { \ mask = mask + (0x100000000000000); \ } while (0); #define ACC_MASK_DEC_REF(mask) \ do { \ if (was_dlg_cb_used(mask)) { \ if (!(mask&0xFF00000000000000)) { \ LM_BUG("More substitutions than additions in acc mask!\n"); \ return; \ } \ mask = mask - (0x100000000000000); \ } \ } while (0); /* read the value of the 8th byte as a char type value */ #define ACC_MASK_GET_REF(mask) (mask >> (8*7)) /* just for debugging purposes * read the value of the flags without the ref counter to know * that the actual flags value is not altered */ #define ACC_MASK_GET_VALUE(mask) (mask & 0x00FFFFFFFFFFFFFF) static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps ); static void acc_dlg_callback(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params); void dlg_free_acc_mask(void* param) { /* * decrease the number of references to the shm memory pointer * the free functions are executed sequentially so we know that this operation * is atomic **/ ACC_MASK_DEC_REF(*((unsigned long long*)param)); LM_DBG("flags[%p] ref counter value after dereferencing[%llu]\n", param, ACC_MASK_GET_REF(*((unsigned long long*)param))); /* * if the reference counter gets to 0 we can free * the shm pointer * */ if (ACC_MASK_GET_REF(*((unsigned long long*)param)) == 0) shm_free((unsigned long long *)param); } void tm_free_acc_mask(void* param) { if (!is_dialog_context(*(unsigned long long*)param)) { shm_free((unsigned long long *)param); } } static inline struct hdr_field* get_rpl_to( struct cell *t, struct sip_msg *reply) { if (reply==FAKED_REPLY || !reply || !reply->to) return t->uas.request->to; else return reply->to; } static inline void env_set_to(struct hdr_field *to) { acc_env.to = to; } static inline void env_set_text(char *p, int len) { acc_env.text.s = p; acc_env.text.len = len; } static inline void env_set_code_status( int code, struct sip_msg *reply) { static char code_buf[INT2STR_MAX_LEN]; acc_env.code = code; if (reply==FAKED_REPLY || reply==NULL) { /* code */ acc_env.code_s.s = int2bstr((unsigned long)code, code_buf, &acc_env.code_s.len); /* reason */ acc_env.reason.s = error_text(code); acc_env.reason.len = strlen(acc_env.reason.s); } else { acc_env.code_s = reply->first_line.u.reply.status; acc_env.reason = reply->first_line.u.reply.reason; } } static inline void env_set_comment(struct acc_param *accp) { acc_env.code = accp->code; acc_env.code_s = accp->code_s; acc_env.reason = accp->reason; } static inline void env_set_event(event_id_t ev) { acc_env.event = ev; } static inline int acc_preparse_req(struct sip_msg *req) { if ( (parse_headers(req,HDR_CALLID_F|HDR_CSEQ_F|HDR_FROM_F|HDR_TO_F,0)<0) || (parse_from_header(req)<0 ) ) { LM_ERR("failed to preparse request\n"); return -1; } return 0; } int w_acc_log_request(struct sip_msg *rq, pv_elem_t* comment, char *foo) { struct acc_param accp; if (acc_preparse_req(rq)<0) return -1; acc_pvel_to_acc_param(rq, comment, &accp); env_set_to( rq->to ); env_set_comment( &accp ); env_set_text( ACC_REQUEST, ACC_REQUEST_LEN); return acc_log_request( rq, NULL, 0); } int w_acc_aaa_request(struct sip_msg *rq, pv_elem_t* comment, char* foo) { struct acc_param accp; if (!aaa_proto_url) { LM_ERR("aaa support not configured\n"); return -1; } if (acc_preparse_req(rq)<0) return -1; acc_pvel_to_acc_param(rq, comment, &accp); env_set_to( rq->to ); env_set_comment( &accp ); return acc_aaa_request( rq, NULL, 0); } int w_acc_db_request(struct sip_msg *rq, pv_elem_t* comment, char *table) { struct acc_param accp; int table_len; if (!table) { LM_ERR("db support not configured\n"); return -1; } if (acc_preparse_req(rq)<0) return -1; table_len = strlen(table); acc_pvel_to_acc_param(rq, comment, &accp); env_set_to( rq->to ); env_set_comment( &accp ); env_set_text(table, table_len); if (table_len == db_table_mc.len && (strncmp(table, db_table_mc.s, table_len) == 0)) { return acc_db_request(rq, NULL, &mc_ins_list, 0); } if (table_len == db_table_acc.len && (strncmp(table, db_table_acc.s, table_len) == 0)) { return acc_db_request(rq, NULL, &acc_ins_list, 0); } return acc_db_request( rq, NULL,NULL, 0); } #ifdef DIAM_ACC int w_acc_diam_request(struct sip_msg *rq, pv_elem_t* comment, char *foo) { struct acc_param accp; if (acc_preparse_req(rq)<0) return -1; acc_pvel_to_acc_param(rq, comment, &accp); env_set_to( rq->to ); env_set_comment( &accp ); return acc_diam_request( rq, NULL); } #endif int w_acc_evi_request(struct sip_msg *rq, pv_elem_t* comment, char *foo) { struct acc_param accp; if (acc_preparse_req(rq)<0) return -1; acc_pvel_to_acc_param(rq, comment, &accp); env_set_to( rq->to ); env_set_comment( &accp ); #if 0 if (is_cdr_acc_on(rq) && is_evi_acc_on(rq)) { env_set_event(acc_cdr_event); } else if (is_evi_acc_on(rq) && acc_env.code < 300) { env_set_event(acc_event); } else if (is_evi_mc_on(rq)) { env_set_event(acc_missed_event); } else { LM_WARN("evi request flags not set\n"); return 1; } #endif if (acc_env.code < 300) { env_set_event(acc_event); } else { env_set_event(acc_missed_event); } return acc_evi_request( rq, NULL, 0); } int acc_pvel_to_acc_param(struct sip_msg* rq, pv_elem_t* pv_el, struct acc_param* accp) { str buf; if(pv_printf_s(rq, pv_el, &buf) < 0) { LM_ERR("Cannot parse comment\n"); return 1; } accp->reason = buf; if (accp->reason.len>=3 && isdigit((int)buf.s[0]) && isdigit((int)buf.s[1]) && isdigit((int)buf.s[2]) ) { /* reply code is in the comment string */ accp->code = (buf.s[0]-'0')*100 + (buf.s[1]-'0')*10 + (buf.s[2]-'0'); accp->code_s.s = buf.s; accp->code_s.len = 3; accp->reason.s += 3; accp->reason.len -= 3; for( ; isspace((int)accp->reason.s[0]) ; accp->reason.s++,accp->reason.len-- ); } else { /* no reply code */ accp->code = 0; accp->code_s.s = NULL; accp->code_s.len = 0; } /*Default comment if none supplied*/ if (accp->reason.len <= 0) { accp->reason.s = error_text(accp->code); accp->reason.len = strlen(accp->reason.s); } return 0; } static inline int has_totag(struct sip_msg *msg) { /* check if it has to tag */ if ( (!msg->to && parse_headers(msg, HDR_TO_F,0)<0) || !msg->to ) { LM_ERR("bad request or missing TO hdr :-/\n"); return 0; } if (get_to(msg)->tag_value.s != 0 && get_to(msg)->tag_value.len != 0) return 1; return 0; } /* is this reply of interest for accounting ? */ static inline int should_acc_reply(struct sip_msg *req,struct sip_msg *rpl, int code, unsigned long long* flags) { /* negative transactions reported otherwise only if explicitly * demanded */ if ( !is_failed_acc_on(*flags) && code >=300 ) { return 0; } if ( !is_acc_on(*flags) ) { return 0; } if ( code<200 && !(early_media && rpl!=FAKED_REPLY && parse_headers(rpl,HDR_CONTENTLENGTH_F, 0)==0 && rpl->content_length && get_content_length(rpl)>0 ) ) { return 0; } return 1; /* seed is through, we will account this reply */ } /* parse incoming replies before cloning */ static inline void acc_onreply_in(struct cell *t, struct sip_msg *req, struct sip_msg *reply, int code, unsigned long long* flags) { /* don't parse replies in which we are not interested */ /* missed calls enabled ? */ if ( (reply && reply!=FAKED_REPLY) && (should_acc_reply(req,reply,code, flags) || (is_invite(t) && code>=300 && is_mc_acc_on(*flags))) ) { parse_headers(reply, HDR_TO_F, 0 ); } } /* initiate a report if we previously enabled MC accounting for this t */ static inline void on_missed(struct cell *t, struct sip_msg *req, struct sip_msg *reply, int code, unsigned long long *flags) { str new_uri_bk={0,0}; str dst_uri_bk={0,0}; unsigned long long flags_to_reset=0; if (t->nr_of_outgoings) { /* set as new_uri the last branch */ new_uri_bk = req->new_uri; dst_uri_bk = req->dst_uri; req->new_uri = t->uac[t->nr_of_outgoings-1].uri; req->dst_uri = t->uac[t->nr_of_outgoings-1].duri; req->parsed_uri_ok = 0; } /* set env variables */ env_set_to( get_rpl_to(t,reply) ); env_set_code_status( code, reply); /* we report on missed calls when the first * forwarding attempt fails; we do not wish to * report on every attempt; so we clear the flags; */ if (is_evi_mc_on(*flags)) { env_set_event(acc_missed_event); acc_evi_request( req, reply, is_evi_cdr_on(*flags) ); flags_to_reset |= DO_ACC_EVI * DO_ACC_MISSED; } if (is_log_mc_on(*flags)) { env_set_text( ACC_MISSED, ACC_MISSED_LEN); acc_log_request( req, reply, is_log_cdr_on(*flags) ); flags_to_reset |= DO_ACC_LOG * DO_ACC_MISSED; } if (is_aaa_mc_on(*flags)) { acc_aaa_request( req, reply, is_aaa_cdr_on(*flags) ); flags_to_reset |= DO_ACC_AAA * DO_ACC_MISSED; } if (is_db_mc_on(*flags)) { env_set_text(db_table_mc.s, db_table_mc.len); acc_db_request( req, reply,&mc_ins_list, is_db_cdr_on(*flags)); flags_to_reset |= DO_ACC_DB * DO_ACC_MISSED; } /* DIAMETER */ #ifdef DIAM_ACC if (is_diam_mc_on(*flags)) { acc_diam_request( req, reply ); flags_to_reset |= DO_ACC_DIAM * DO_ACC_MISSED; } #endif /* Reset the accounting missed_flags * These can't be reset in the blocks above, because * it would skip accounting if the flags are identical */ if (t->nr_of_outgoings) { req->new_uri = new_uri_bk; req->dst_uri = dst_uri_bk; req->parsed_uri_ok = 0; } reset_flags(*flags, flags_to_reset); } /* restore callbacks */ void acc_loaded_callback(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { str flags_s; unsigned long long *flags; if (!dlg) { LM_ERR("null dialog - cannot fetch message flags\n"); return; } if (dlg_api.fetch_dlg_value(dlg, &flags_str, &flags_s, 0) < 0) { LM_DBG("flags were not saved in dialog\n"); return; } flags = shm_malloc(sizeof(unsigned long long)); if (flags == NULL) { LM_ERR("no more shm!\n"); return; } memcpy(flags, flags_s.s, sizeof(unsigned long long)); /* register database callbacks */ if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_EXPIRED, acc_dlg_callback, flags, 0)){ LM_ERR("cannot register callback for database accounting\n"); return; } } /* initiate a report if we previously enabled accounting for this t */ static inline void acc_onreply( struct cell* t, struct sip_msg *req, struct sip_msg *reply, int code, unsigned long long *flags) { str new_uri_bk; str dst_uri_bk; struct dlg_cell *dlg = NULL; str flags_s; int_str table; struct usr_avp *avp; /* acc_onreply is bound to TMCB_REPLY which may be called from _reply, like when FR hits; we should not miss this event for missed calls either */ if (is_invite(t) && code>=300 && is_mc_acc_on(*flags) ) { on_missed(t, req, reply, code, flags); } if (!should_acc_reply(req, reply, code, flags)) return; /* for reply processing, set as new_uri the winning branch */ if (t->relaied_reply_branch>=0) { new_uri_bk = req->new_uri; dst_uri_bk = req->dst_uri; req->new_uri = t->uac[t->relaied_reply_branch].uri; req->dst_uri = t->uac[t->relaied_reply_branch].duri; req->parsed_uri_ok = 0; } else { new_uri_bk.len = dst_uri_bk.len = -1; new_uri_bk.s = dst_uri_bk.s = NULL; } /* set env variables */ env_set_to( get_rpl_to(t,reply) ); env_set_code_status( code, reply); /* search for table avp */ table.s = db_table_acc; if (db_table_name != -1 && is_db_acc_on(*flags)) { avp = search_first_avp(db_table_name_type, db_table_name, &table, 0); if (!avp) { LM_DBG("table not set: using default %.*s\n", db_table_acc.len, db_table_acc.s); } else { if (!(avp->flags & AVP_VAL_STR)) { LM_WARN("invalid integer table name: using default %.*s\n", db_table_acc.len, db_table_acc.s); table.s = db_table_acc; } } } if (is_invite(t) && !has_totag(req) && is_cdr_acc_on(*flags) && code >= 200 && code < 300 && (dlg=dlg_api.get_dlg()) != NULL) { /* if dialog module loaded and INVITE and success reply */ if (store_core_leg_values(dlg, req) < 0) { LM_ERR("cannot store core and leg values\n"); return; } if(is_log_acc_on(*flags) && store_log_extra_values(dlg,req,reply)<0){ LM_ERR("cannot store string values\n"); return; } if(is_aaa_acc_on(*flags) && store_aaa_extra_values(dlg, req, reply)<0){ LM_ERR("cannot store aaa extra values\n"); return; } if (is_db_acc_on(*flags) && store_db_extra_values(dlg,req,reply)<0) { LM_ERR("cannot store database extra values\n"); return; } if (is_evi_acc_on(*flags) && store_evi_extra_values(dlg,req,reply)<0) { LM_ERR("cannot store database extra values\n"); return; } flags_s.s = (char*)flags; flags_s.len = sizeof(unsigned long long); /* store flags into dlg */ if ( dlg_api.store_dlg_value(dlg, &flags_str, &flags_s) < 0) { LM_ERR("cannot store flag value into dialog\n"); return; } /* store flags into dlg */ if ( dlg_api.store_dlg_value(dlg, &table_str, &table.s) < 0) { LM_ERR("cannot store the table name into dialog\n"); return; } /* report that flags shall be freed only by dialog module * tm must never free it */ set_dialog_context(*flags); /* register database callbacks */ if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_EXPIRED, acc_dlg_callback,flags, dlg_free_acc_mask) != 0) { LM_ERR("cannot register callback for database accounting\n"); return; } } else { /* do old accounting */ if ( is_evi_acc_on(*flags) ) { env_set_event(acc_event); acc_evi_request( req, reply, 0 ); } if ( is_log_acc_on(*flags) ) { env_set_text( ACC_ANSWERED, ACC_ANSWERED_LEN); acc_log_request( req, reply, 0 ); } if (is_aaa_acc_on(*flags)) acc_aaa_request( req, reply, 0 ); if (is_db_acc_on(*flags)) { env_set_text( table.s.s, table.s.len); acc_db_request( req, reply, &acc_ins_list, 0); } } /* DIAMETER */ #ifdef DIAM_ACC if (is_diam_acc_on(req)) acc_diam_request( req, reply ); #endif if (new_uri_bk.len>=0) { req->new_uri = new_uri_bk; req->dst_uri = dst_uri_bk; req->parsed_uri_ok = 0; } } static void acc_dlg_callback(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { unsigned long long flags; if (!_params) { LM_ERR("not enough info\n"); return; } flags = *((unsigned long long*)(*_params->param)); /** * we've read the value of the flags * increase the number of references to the shm memory pointer * we know that this operation is atomic since the dialog callbacks * are executed sequentially */ ACC_MASK_INC_REF(*((unsigned long long *)*_params->param)); LM_DBG("flags[%p] ref counter value after referencing [%llu]\n", *_params->param, ACC_MASK_GET_REF(*((unsigned long long*)*_params->param))); /* * this way we "enable" the refcount * if opensips shuts down before dialog terminated then the refcount * won't be enabled */ set_dlg_cb_used(*((unsigned long long*)*_params->param)); if (is_evi_acc_on(flags)) { env_set_event(acc_cdr_event); if (acc_evi_cdrs(dlg, _params->msg) < 0) { LM_ERR("cannot send accounting events\n"); return; } } if (is_log_acc_on(flags)) { env_set_text( ACC_ENDED, ACC_ENDED_LEN); if (acc_log_cdrs(dlg, _params->msg) < 0) { LM_ERR("Cannot log values\n"); return; } } if (is_db_acc_on(flags)) { env_set_text( db_table_acc.s, db_table_acc.len); if (acc_db_cdrs(dlg, _params->msg) < 0) { LM_ERR("Cannot insert into database\n"); return; } } if (is_aaa_acc_on(flags) && acc_aaa_cdrs(dlg, _params->msg) < 0) { LM_ERR("Cannot create radius accounting\n"); return; } } static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps ) { unsigned long long *flags = *ps->param; if (ACC_GET_TM_FLAGS(t) == NULL) ACC_PUT_TM_FLAGS(t, flags); if (type&TMCB_RESPONSE_OUT) { acc_onreply( t, ps->req, ps->rpl, ps->code, flags); } else if (type&TMCB_ON_FAILURE) { on_missed( t, ps->req, ps->rpl, ps->code, flags); } else if (type&TMCB_RESPONSE_IN) { acc_onreply_in( t, ps->req, ps->rpl, ps->code, flags); } } /* * helper function to retrieve flags from processing context or * transaction context */ static inline unsigned long long* try_fetch_flags(void) { unsigned long long* ret=NULL; struct cell* t; if ((ret=ACC_GET_FLAGS) == NULL) if (((t=tmb.t_gett()) != NULL) && (t!=T_UNDEFINED) && ((ret=ACC_GET_TM_FLAGS(t)) == NULL)) return NULL; return ret; } /* * use case * * { * do_acc("db", "cdr", "myacc") * do_acc("db|radius", "cdr|missed", "myacc") * } * * */ /* accounting type strings */ static str do_acc_log_s=str_init(DO_ACC_LOG_STR); static str do_acc_aaa_s=str_init(DO_ACC_AAA_STR); static str do_acc_db_s=str_init(DO_ACC_DB_STR); static str do_acc_diam_s=str_init(DO_ACC_DIAM_STR); static str do_acc_evi_s=str_init(DO_ACC_EVI_STR); /* accounting flags strings */ static str do_acc_cdr_s=str_init(DO_ACC_CDR_STR); static str do_acc_missed_s=str_init(DO_ACC_MISSED_STR); static str do_acc_failed_s=str_init(DO_ACC_FAILED_STR); /** * types: log, aaa, db, diam, evi * case insesitive * */ static inline unsigned long long do_acc_type_parser(str* token) { str_trim_spaces_lr(*token); if (token->len == do_acc_log_s.len && !strncasecmp(token->s, do_acc_log_s.s, token->len)) { return DO_ACC_LOG; } else if (token->len == do_acc_aaa_s.len && !strncasecmp(token->s, do_acc_aaa_s.s, token->len)) { return DO_ACC_AAA; } else if (token->len == do_acc_db_s.len && !strncasecmp(token->s, do_acc_db_s.s, token->len)) { return DO_ACC_DB; } else if (token->len == do_acc_diam_s.len && !strncasecmp(token->s, do_acc_diam_s.s, token->len)) { return DO_ACC_DIAM; } else if (token->len == do_acc_evi_s.len && !strncasecmp(token->s, do_acc_evi_s.s, token->len)) { return DO_ACC_EVI; } else { LM_ERR("Invalid token <%.*s>!\n", token->len, token->s); return DO_ACC_ERR; } } /** * types: cdr, missed * case insesitive * */ static inline unsigned long long do_acc_flags_parser(str* token) { str_trim_spaces_lr(*token); if (token->len == do_acc_cdr_s.len && !strncasecmp(token->s, do_acc_cdr_s.s, token->len)) { if (!is_cdr_enabled) { if (parse_avp_spec( &acc_created_avp_name, &acc_created_avp_id) < 0) { LM_ERR("failed to register AVP name <%s>\n", acc_created_avp_name.s); return DO_ACC_ERR; } if (load_dlg_api(&dlg_api)!=0) LM_DBG("failed to find dialog API - is dialog module loaded?\n"); if (!dlg_api.get_dlg) { LM_WARN("error loading dialog module - cdrs cannot be generated\n"); return DO_ACC_NONE; } if (dlg_api.get_dlg && dlg_api.register_dlgcb(NULL, DLGCB_LOADED,acc_loaded_callback, NULL, NULL) < 0) LM_ERR("cannot register callback for dialog loaded - accounting " "for ongoing calls will be lost after restart\n"); is_cdr_enabled=1; } return DO_ACC_CDR; } else if (token->len == do_acc_missed_s.len && !strncasecmp(token->s, do_acc_missed_s.s, token->len)) { /* load dialog module if these are used */ return DO_ACC_MISSED; } else if (token->len == do_acc_failed_s.len && !strncasecmp(token->s, do_acc_failed_s.s, token->len)) { return DO_ACC_FAILED; } else { return DO_ACC_ERR; } } static unsigned long long do_acc_parse(str* in, do_acc_parser parser) { char* found=NULL; str token; unsigned long long fret=0, ret; if (!in || !in->s || !in->len) return -1; do { found=q_memchr(in->s, DO_ACC_PARAM_DELIMITER, in->len); if (found) { token.s = in->s; token.len = found - in->s; in->len -= (found - in->s) + 1; in->s = found + 1; } else { token = *in; } if ((ret=parser(&token)) == DO_ACC_ERR) { LM_ERR("Invalid token <%.*s>!\n", token.len, token.s); return -1; } fret |= ret; } while(found); return fret; } int do_acc_fixup(void** param, int param_no) { str s; pv_elem_p el; unsigned long long ival; unsigned long long* ival_p; acc_type_param_t* acc_param; do_acc_parser parser; if (param_no < 1 || param_no > 3) { LM_ERR("invalid param_no <%d>!\n", param_no); return -1; } switch (param_no) { case 1: parser=do_acc_type_parser; s.s = *param; s.len = strlen(s.s); if (pv_parse_format(&s, &el) < 0) { LM_ERR("invalid format <%.*s>!\n", s.len, s.s); return -1; } acc_param=pkg_malloc(sizeof(acc_type_param_t)); if (acc_param == NULL) { LM_ERR("no more pkg mem!\n"); return -1; } memset(acc_param, 0, sizeof(acc_type_param_t)); if (el->next == 0 && el->spec.getf == 0) { pv_elem_free_all(el); if ( (ival=do_acc_parse(&el->text, parser)) == DO_ACC_ERR) { LM_ERR("Invalid value <%.*s>!\n", el->text.len, el->text.s); return -1; } acc_param->t = DO_ACC_PARAM_TYPE_VALUE; acc_param->u.ival = ival; } else { acc_param->t = DO_ACC_PARAM_TYPE_PV; acc_param->u.pval = el; } *param = acc_param; break; case 2: parser=do_acc_flags_parser; s.s = *param; s.len = strlen(s.s); if ( (ival=do_acc_parse(&s, parser)) == DO_ACC_ERR) { LM_ERR("Invalid value <%.*s>!\n", s.len, s.s); return -1; } if ((ival_p=pkg_malloc(sizeof(unsigned long long))) == NULL) { LM_ERR("no more pkg mem!\n"); return -1; } *ival_p = ival; *param = ival_p; break; case 3: return fixup_sgp(param); } return 0; } int w_do_acc_1(struct sip_msg* msg, char* type) { return w_do_acc_3(msg, type, NULL, NULL); } int w_do_acc_2(struct sip_msg* msg, char* type, char* flags) { return w_do_acc_3(msg, type, flags, NULL); } int w_do_acc_3(struct sip_msg* msg, char* type_p, char* flags_p, char* table_p) { unsigned long long type=0, flags=0; unsigned long long *flag_mask_p, flag_mask; acc_type_param_t* acc_param; str in; str table_name; int tmcb_types; int is_invite; int_str _avp_created_value; if (type_p == NULL) { LM_ERR("accounting type is mandatory!\n"); return -1; } acc_param = (acc_type_param_t *)type_p; if (acc_param->t == DO_ACC_PARAM_TYPE_VALUE) { type = acc_param->u.ival; } else { if (pv_printf_s(msg, acc_param->u.pval, &in) < 0) { LM_ERR("failed to fetch type value!\n"); return -1; } if ((type=do_acc_parse(&in, do_acc_type_parser)) == DO_ACC_ERR) { LM_ERR("Invalid expression <%.*s> for acc type!\n", in.len, in.s); return -1; } } if (flags_p != NULL) { flags= *(unsigned long long*)flags_p; } flag_mask = type + type * flags; if (is_cdr_acc_on(flag_mask)) { /* setting this flag will allow us to register everything * that is needed for CDR accounting only once */ set_cdr_values_registered(flag_mask); } /* is it the first time when the function was called ? */ if ((flag_mask_p=try_fetch_flags()) != NULL) { /* no the first time ; check now if CDRs are requested now * for the first time */ if (!cdr_values_registered(*flag_mask_p) && cdr_values_registered(flag_mask)) { /* CDR support requested for the first time, we need to create * the dialog support, if an initial INVITE */ if (!has_totag(msg)) { _avp_created_value.n = time(NULL); if ( add_avp(0, acc_created_avp_id, _avp_created_value) != 0) { LM_ERR("failed to add created avp value!\n"); return -1; } if (msg->REQ_METHOD == METHOD_INVITE && create_acc_dlg(msg) < 0) { LM_ERR("cannot use dialog accounting module\n"); return -1; } } } /* if it's the first time the missed calls flag was used register the callback */ if (is_mc_acc_on(flag_mask) && !failure_cb_registered(*flag_mask_p)) { if (tmb.register_tmcb( msg, 0, TMCB_ON_FAILURE, tmcb_func, flag_mask_p, 0)<=0) { LM_ERR("cannot register missed calls callback\n"); return -1; } /* don't allow the callback to be registered agian in the future */ set_failure_cb_registered(*flag_mask_p); } *flag_mask_p |= flag_mask; return 1; } /* setting accouting for the first time, allocate the flags holder */ flag_mask_p=shm_malloc(sizeof(unsigned long long)); if (flag_mask_p==NULL) { LM_ERR("No more shm mem!\n"); return -1; } /* * the first bit in each byte will just tell that we want that type of * accounting * next bits will tell extra options for that type of accounting * so we keep the first bits in each byte and on the following positions * next flags */ *flag_mask_p = flag_mask; ACC_PUT_FLAGS(flag_mask_p); if (table_p != NULL) { if (fixup_get_svalue(msg, (gparam_p)table_p, &table_name) < 0) { LM_ERR("failed to fetch table name!\n"); return -1; } } if ( msg && !skip_cancel(msg) && (is_acc_on(*flag_mask_p) || is_mc_acc_on(*flag_mask_p)) ) { /* do some parsing in advance */ if (acc_preparse_req(msg)<0) return -1; is_invite = (msg->REQ_METHOD==METHOD_INVITE)?1:0; /* install additional handlers */ tmcb_types = /* report on completed transactions */ TMCB_RESPONSE_IN; if (is_invite && is_mc_acc_on(*flag_mask_p)) { /* register it manually; see explanation below * get incoming replies ready for processing */ /* TMCB_RESPONSE_OUT | */ /* report on missed calls */ tmcb_types |= TMCB_ON_FAILURE; /* the flag will help on further do_accounting calls to know * not to register the callback twice */ set_failure_cb_registered(*flag_mask_p); } /* if cdr accounting is enabled */ if (is_cdr_acc_on(*flag_mask_p) && !has_totag(msg)) { _avp_created_value.n = time(NULL); if ( add_avp(0, acc_created_avp_id, _avp_created_value) != 0) { LM_ERR("failed to add created avp value!\n"); return -1; } if (is_invite && create_acc_dlg(msg) < 0) { LM_ERR("cannot use dialog accounting module\n"); return -1; } } /* we do register_tmcb twice because we wan't to register the free * fucntion only once */ if (tmb.register_tmcb( msg, 0, TMCB_RESPONSE_OUT, tmcb_func, flag_mask_p, tm_free_acc_mask)<=0) { LM_ERR("cannot register additional callbacks\n"); return -1; } if (tmb.register_tmcb( msg, 0, tmcb_types, tmcb_func, flag_mask_p, 0)<=0) { LM_ERR("cannot register additional callbacks\n"); return -1; } /* if required, determine request direction */ if( detect_direction && !rrb.is_direction(msg,RR_FLOW_UPSTREAM) ) { LM_DBG("detected an UPSTREAM req -> flaging it\n"); msg->msg_flags |= FL_REQ_UPSTREAM; } } return 1; } /* reset all flags */ int w_drop_acc_0(struct sip_msg* msg) { return w_drop_acc_2(msg, NULL, NULL); } int w_drop_acc_1(struct sip_msg* msg, char* type) { return w_drop_acc_2(msg, type, NULL); } int w_drop_acc_2(struct sip_msg* msg, char* type_p, char* flags_p) { unsigned long long type=0; /* if not set, we reset all flags for the type of accounting requested */ unsigned long long flags=ALL_ACC_FLAGS; unsigned long long flag_mask; unsigned long long *context_flags_p=try_fetch_flags(); acc_type_param_t* acc_param; str in; if (context_flags_p == NULL) { LM_ERR("do_accounting() not used! This function resets flags in " "do_accounting()!\n"); return -1; } if (type_p != NULL) { acc_param = (acc_type_param_t *)type_p; if (acc_param->t == DO_ACC_PARAM_TYPE_VALUE) { type = acc_param->u.ival; } else { if (pv_printf_s(msg, acc_param->u.pval, &in) < 0) { LM_ERR("failed to fetch type value!\n"); return -1; } if ((type=do_acc_parse(&in, do_acc_type_parser)) == DO_ACC_ERR) { LM_ERR("Invalid expression <%.*s> for acc type!\n", in.len, in.s); return -1; } } } if (flags_p != NULL) { flags= *(unsigned long long*)flags_p; } flag_mask = type * flags; /* reset all flags */ if (flag_mask == 0) { *context_flags_p = 0; } else { reset_flags(*context_flags_p, flag_mask); } return 1; } opensips-2.2.2/modules/acc/acc_logic.h000066400000000000000000000107021300170765700176350ustar00rootroot00000000000000/* * Accounting module * * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2005-09-19 created during a big re-structuring of acc module(bogdan) */ #ifndef _ACC_ACC_LOGIC_H #define _ACC_ACC_LOGIC_H #include "../../str.h" #include "../tm/t_hooks.h" #include "../dialog/dlg_cb.h" #define DO_ACC_NONE (0) #define DO_ACC_LOG (1<<(0*8)) #define DO_ACC_AAA (1<<(1*8)) #define DO_ACC_DB (1<<(2*8)) #define DO_ACC_DIAM (1<<(3*8)) #define DO_ACC_EVI ((unsigned long long)1<<(4*8)) #define DO_ACC_ERR ((unsigned long long)-1) #define DO_ACC (1<<0) /* generic accouting flag - internal only */ #define DO_ACC_CDR (1<<1) #define DO_ACC_MISSED (1<<2) #define DO_ACC_FAILED (1<<3) #define ALL_ACC_FLAGS (DO_ACC|DO_ACC_CDR|DO_ACC_MISSED|DO_ACC_FAILED) #define DO_ACC_PARAM_TYPE_PV (1<<0) #define DO_ACC_PARAM_TYPE_VALUE (1<<1) #define DO_ACC_LOG_STR "log" #define DO_ACC_AAA_STR "aaa" #define DO_ACC_DB_STR "db" #define DO_ACC_DIAM_STR "diam" #define DO_ACC_EVI_STR "evi" #define DO_ACC_CDR_STR "cdr" #define DO_ACC_MISSED_STR "missed" #define DO_ACC_FAILED_STR "failed" /* flags on the eigth byte - generic flags for all accounting types */ #define ACC_DIALOG_CONTEXT (((unsigned long long)1<<(8*6)) * (1<<0)) #define ACC_CDR_REGISTERED (((unsigned long long)1<<(8*6)) * (1<<1)) /* * this flag will help to know if we entered at least once * in the dialog callbacks * * this way, at shutdown, we will know that we didn't call any * dialog callbacks and value 0 in the 8th byte(ref count) * is valid */ #define ACC_DLG_CB_USED (((unsigned long long)1<<(8*6)) * (1<<2)) /* * flag to signal that the callback for missed calls has been registered; * this way if the missed calls flag is set twice, we won't register the * callback twice */ #define ACC_TMCB_MISSED_REGISTERED (((unsigned long long)1<<(8*6)) * (1<<5)) #define ACC_MASK_REF_BYTE (((unsigned long long)(0xFF)<<(8*7)) #define DO_ACC_PARAM_DELIMITER '|' #define ACC_GET_FLAGS \ context_get_ptr(CONTEXT_GLOBAL, current_processing_ctx, \ acc_flags_ctx_idx) #define ACC_PUT_FLAGS(_ptr) \ context_put_ptr(CONTEXT_GLOBAL, current_processing_ctx, \ acc_flags_ctx_idx, _ptr) #define ACC_GET_TM_FLAGS(_t) \ tmb.t_ctx_get_ptr(_t, acc_tm_flags_ctx_idx) #define ACC_PUT_TM_FLAGS(_t, _ptr) \ tmb.t_ctx_put_ptr(_t, acc_tm_flags_ctx_idx, _ptr) typedef unsigned long long (*do_acc_parser)(str*); typedef struct acc_type_param { int t; union { unsigned long long ival; pv_elem_p pval; } u; } acc_type_param_t; /* various acc variables */ struct acc_enviroment { unsigned int code; str code_s; str reason; struct hdr_field *to; str text; struct timeval ts; event_id_t event; }; /* param trasnporter*/ struct acc_param { int code; str code_s; str reason; }; int w_acc_log_request(struct sip_msg *rq, pv_elem_t* comment, char *foo); int w_acc_aaa_request(struct sip_msg *rq, pv_elem_t* comment, char *foo); int w_acc_db_request(struct sip_msg *rq, pv_elem_t* comment, char *table); int acc_pvel_to_acc_param(struct sip_msg *rq, pv_elem_t* pv_el, struct acc_param* accp); void acc_loaded_callback(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params); #ifdef DIAM_ACC int w_acc_diam_request(struct sip_msg *rq, char *comment, char *foo); #endif int w_acc_evi_request(struct sip_msg *rq, pv_elem_t* comment, char *foo); int do_acc_fixup(void** param, int param_no); int w_do_acc_1(struct sip_msg* msg, char* type); int w_do_acc_2(struct sip_msg* msg, char* type, char* flags); int w_do_acc_3(struct sip_msg* msg, char* type_p, char* flags_p, char* table_p); int w_drop_acc_0(struct sip_msg* msg); int w_drop_acc_1(struct sip_msg* msg, char* type); int w_drop_acc_2(struct sip_msg* msg, char* type, char* flags); #endif opensips-2.2.2/modules/acc/acc_mod.c000066400000000000000000000413361300170765700173210ustar00rootroot00000000000000/* * Accounting module * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-06: aligned to change in callback names (jiri) * 2003-03-06: fixed improper sql connection, now from * child_init (jiri) * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) * 2003-04-04 grand acc cleanup (jiri) * 2003-04-06: Opens database connection in child_init only (janakj) * 2003-04-24 parameter validation (0 t->uas.request) added (jiri) * 2003-11-04 multidomain support for mysql introduced (jiri) * 2003-12-04 global TM callbacks switched to per transaction callbacks * (bogdan) * 2004-06-06 db cleanup: static db_url, calls to acc_db_{bind,init,close) * (andrei) * 2005-05-30 acc_extra patch commited (ramona) * 2005-06-28 multi leg call support added (bogdan) * 2006-01-13 detect_direction (for sequential requests) added (bogdan) * 2006-09-08 flexible multi leg accounting support added (bogdan) * 2006-09-19 final stage of a masive re-structuring and cleanup (bogdan) */ #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../tm/tm_load.h" #include "../rr/api.h" #include "../../aaa/aaa.h" #include "../dialog/dlg_load.h" #ifdef DIAM_ACC #include "diam_dict.h" #include "diam_tcp.h" #endif #include "acc.h" #include "acc_mod.h" #include "acc_extra.h" #include "acc_logic.h" struct dlg_binds dlg_api; struct tm_binds tmb; struct rr_binds rrb; static int mod_init(void); static void destroy(void); static int child_init(int rank); /* ----- General purpose variables ----------- */ /* what would you like to report on */ /* should early media replies (183) be logged ? default==no */ int early_media = 0; /* would you like us to report CANCELs from upstream too? */ int report_cancels = 0; /* detect and correct direction in the sequential requests */ int detect_direction = 0; /* should failed replies (>=3xx) be logged ? default==no */ /* multi call-leg support */ static char* leg_info_str = 0; static char* leg_bye_info_str = 0; struct acc_extra *leg_info = 0; struct acc_extra *leg_bye_info = 0; /* ----- SYSLOG acc variables ----------- */ /* noisiness level logging facilities are used */ int acc_log_level = L_NOTICE; /* log facility that is used */ int acc_log_facility = LOG_DAEMON; static char * log_facility_str = 0; /* log extra variables */ static char *log_extra_str = 0; struct acc_extra *log_extra = 0; static char *log_extra_bye_str = 0; struct acc_extra *log_extra_bye = 0; /* ----- AAA PROTOCOL acc variables ----------- */ static int service_type = -1; char *aaa_proto_url = NULL; aaa_prot proto; aaa_conn *conn; /* aaa extra variables */ static char *aaa_extra_str = 0; struct acc_extra *aaa_extra = 0; static char *aaa_extra_bye_str = 0; struct acc_extra *aaa_extra_bye = 0; /* ----- DIAMETER acc variables ----------- */ #ifdef DIAM_ACC /* diameter extra variables */ static char *dia_extra_str = 0; struct acc_extra *dia_extra = 0; /* buffer used to read from TCP connection*/ rd_buf_t *rb; char* diameter_client_host="localhost"; int diameter_client_port=3000; #endif /* ----- SQL acc variables ----------- */ /* db extra variables */ static char *db_extra_str = 0; struct acc_extra *db_extra = 0; static char *db_extra_bye_str = 0; struct acc_extra *db_extra_bye = 0; /* Database url */ static str db_url = {NULL, 0}; /* name of database tables */ str db_table_acc = str_init("acc"); static str db_table_avp = {0,0}; int db_table_name = -1; unsigned short db_table_name_type = -1; str db_table_mc = str_init("missed_calls"); /* names of columns in tables acc/missed calls*/ str acc_method_col = str_init("method"); str acc_fromtag_col = str_init("from_tag"); str acc_totag_col = str_init("to_tag"); str acc_callid_col = str_init("callid"); str acc_sipcode_col = str_init("sip_code"); str acc_sipreason_col = str_init("sip_reason"); str acc_time_col = str_init("time"); str acc_duration_col = str_init("duration"); str acc_ms_duration_col= str_init("ms_duration"); str acc_setuptime_col = str_init("setuptime"); str acc_created_col = str_init("created"); /* ----- Event Interface acc variables ----------- */ /* event extra variables */ static char *evi_extra_str = 0; struct acc_extra *evi_extra = 0; static char *evi_extra_bye_str = 0; struct acc_extra *evi_extra_bye = 0; /* db avp variables */ str acc_created_avp_name = str_init("accX_created"); int acc_created_avp_id = -1; /* acc context position */ int acc_flags_ctx_idx; int acc_tm_flags_ctx_idx; /* ------------- fixup function --------------- */ static int acc_fixup(void** param, int param_no); static int free_acc_fixup(void** param, int param_no); static cmd_export_t cmds[] = { {"acc_log_request", (cmd_function)w_acc_log_request, 1, acc_fixup, free_acc_fixup, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"acc_db_request", (cmd_function)w_acc_db_request, 2, acc_fixup, free_acc_fixup, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"acc_aaa_request", (cmd_function)w_acc_aaa_request, 1, acc_fixup, free_acc_fixup, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, #ifdef DIAM_ACC {"acc_diam_request",(cmd_function)w_acc_diam_request,1, acc_fixup, free_acc_fixup, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, #endif {"acc_evi_request", (cmd_function)w_acc_evi_request, 1, acc_fixup, free_acc_fixup, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, /* only the type of acc(db,evi...) */ {"do_accounting", (cmd_function)w_do_acc_1, 1, do_acc_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, /* type of acc(db,evi...) and flags(log cdr, log missed) */ {"do_accounting", (cmd_function)w_do_acc_2, 2, do_acc_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, /* type of acc(db,evi...) and flags(log cdr, log missed) * and db table */ {"do_accounting", (cmd_function)w_do_acc_3, 3, do_acc_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"drop_accounting", (cmd_function)w_drop_acc_0, 0, 0, NULL, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, /* we use the same fixup function since the parameters * have the same meanining as for do_accounting */ {"drop_accounting", (cmd_function)w_drop_acc_1, 1, do_acc_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"drop_accounting", (cmd_function)w_drop_acc_2, 2, do_acc_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[] = { {"early_media", INT_PARAM, &early_media }, {"report_cancels", INT_PARAM, &report_cancels }, {"multi_leg_info", STR_PARAM, &leg_info_str }, {"multi_leg_bye_info", STR_PARAM, &leg_bye_info_str }, {"detect_direction", INT_PARAM, &detect_direction }, /* syslog specific */ {"log_level", INT_PARAM, &acc_log_level }, {"log_facility", STR_PARAM, &log_facility_str }, {"log_extra", STR_PARAM, &log_extra_str }, {"log_extra_bye", STR_PARAM, &log_extra_bye_str }, /* aaa specific */ {"aaa_url", STR_PARAM, &aaa_proto_url }, {"service_type", INT_PARAM, &service_type }, {"aaa_extra", STR_PARAM, &aaa_extra_str }, {"aaa_extra_bye", STR_PARAM, &aaa_extra_bye_str }, /* event interface specific */ {"evi_extra", STR_PARAM, &evi_extra_str }, {"evi_extra_bye", STR_PARAM, &evi_extra_bye_str }, /* DIAMETER specific */ #ifdef DIAM_ACC {"diameter_client_host", STR_PARAM, &diameter_client_host }, {"diameter_client_port", INT_PARAM, &diameter_client_port }, {"diameter_extra", STR_PARAM, &dia_extra_str }, #endif /* db-specific */ {"db_extra", STR_PARAM, &db_extra_str }, {"db_extra_bye", STR_PARAM, &db_extra_bye_str }, {"db_url", STR_PARAM, &db_url.s }, {"db_table_acc", STR_PARAM, &db_table_acc.s }, {"db_table_missed_calls",STR_PARAM, &db_table_mc.s }, {"db_table_avp", STR_PARAM, &db_table_avp.s }, {"acc_method_column", STR_PARAM, &acc_method_col.s }, {"acc_from_tag_column", STR_PARAM, &acc_fromtag_col.s }, {"acc_to_tag_column", STR_PARAM, &acc_totag_col.s }, {"acc_callid_column", STR_PARAM, &acc_callid_col.s }, {"acc_sip_code_column", STR_PARAM, &acc_sipcode_col.s }, {"acc_sip_reason_column",STR_PARAM, &acc_sipreason_col.s }, {"acc_time_column", STR_PARAM, &acc_time_col.s }, {"acc_created_avp_name", STR_PARAM, &acc_created_avp_name.s}, {0,0,0} }; static module_dependency_t *get_deps_aaa_url(param_export_t *param) { char *aaa_url = *(char **)param->param_pointer; if (!aaa_url || strlen(aaa_url) == 0) return NULL; return alloc_module_dep(MOD_TYPE_AAA, NULL, DEP_WARN); } static module_dependency_t *get_deps_detect_dir(param_export_t *param) { if (*(int *)param->param_pointer == 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "rr", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "db_url", get_deps_sqldb_url }, { "aaa_url", get_deps_aaa_url }, { "detect_direction", get_deps_detect_dir }, { NULL, NULL }, }, }; struct module_exports exports= { "acc", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported params */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* initialization module */ 0, /* response function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /************************** FIXUP functions ****************************/ static int acc_fixup(void** param, int param_no) { str s; pv_elem_t *model = NULL; s.s = (char*)(*param); if (s.s==0 || s.s[0]==0) { LM_ERR("first parameter is empty\n"); return E_SCRIPT; } if (param_no == 1) { if (s.s==NULL) { LM_ERR("null format in P%d\n", param_no); } s.len = strlen(s.s); if(pv_parse_format(&s, &model)<0) { LM_ERR("wrong format[%s]\n", s.s); return E_UNSPEC; } *param = (void*)model; return 0; } else if (param_no == 2) { /* only for db acc - the table name */ if (db_url.s==0) { pkg_free(s.s); *param = 0; } } return 0; } static int free_acc_fixup(void** param, int param_no) { if(*param) { pkg_free(*param); *param = 0; } return 0; } /************************** INTERFACE functions ****************************/ static int mod_init( void ) { LM_INFO("initializing...\n"); if (db_url.s) db_url.len = strlen(db_url.s); db_table_acc.len = strlen(db_table_acc.s); db_table_mc.len = strlen(db_table_mc.s); acc_method_col.len = strlen(acc_method_col.s); acc_fromtag_col.len = strlen(acc_fromtag_col.s); acc_totag_col.len = strlen(acc_totag_col.s); acc_callid_col.len = strlen(acc_callid_col.s); acc_sipcode_col.len = strlen(acc_sipcode_col.s); acc_sipreason_col.len = strlen(acc_sipreason_col.s); acc_time_col.len = strlen(acc_time_col.s); acc_created_avp_name.len = strlen(acc_created_avp_name.s); if (log_facility_str) { int tmp = str2facility(log_facility_str); if (tmp != -1) acc_log_facility = tmp; else { LM_ERR("invalid log facility configured"); return -1; } } /* load the TM API */ if (load_tm_api(&tmb)!=0) { LM_ERR("can't load TM API\n"); return -1; } /* if detect_direction is enabled, load rr also */ if (detect_direction) { if (load_rr_api(&rrb)!=0) { LM_ERR("can't load RR API\n"); return -1; } /* we need the append_fromtag on in RR */ if (!rrb.append_fromtag) { LM_ERR("'append_fromtag' RR param is not enabled!" " - required by 'detect_direction'\n"); return -1; } } /* init the extra engine */ init_acc_extra(); /* configure multi-leg accounting */ if (leg_info_str && (leg_info=parse_acc_leg(leg_info_str))==0 ) { LM_ERR("failed to parse multi_leg_info param\n"); return -1; } if (leg_bye_info_str && (leg_bye_info=parse_acc_leg(leg_bye_info_str))==0 ) { LM_ERR("failed to parse multi_leg_bye_info param\n"); return -1; } /* ----------- SYSLOG INIT SECTION ----------- */ /* parse the extra string, if any */ if (log_extra_str && (log_extra=parse_acc_extra(log_extra_str, 1))==0 ) { LM_ERR("failed to parse log_extra param\n"); return -1; } if (log_extra_bye_str && (log_extra_bye=parse_acc_extra(log_extra_bye_str, 0))==0 ) { LM_ERR("failed to parse log_extra_bye param\n"); return -1; } acc_log_init(); /* ------------ SQL INIT SECTION ----------- */ if (db_url.s) { /* parse the extra string, if any */ if (db_extra_str && (db_extra=parse_acc_extra(db_extra_str, 1))==0 ) { LM_ERR("failed to parse db_extra param\n"); return -1; } if (db_extra_bye_str && (db_extra_bye=parse_acc_extra(db_extra_bye_str, 0))==0 ) { LM_ERR("failed to parse db_extra_bye param\n"); return -1; } if (acc_db_init(&db_url)<0){ LM_ERR("failed! bad db url / missing db module ?\n"); return -1; } } /* ------------ AAA PROTOCOL INIT SECTION ----------- */ if (aaa_proto_url && aaa_proto_url[0]) { /* parse the extra string, if any */ if (aaa_extra_str && (aaa_extra = parse_acc_extra(aaa_extra_str, 1))==0) { LM_ERR("failed to parse aaa_extra param\n"); return -1; } if (aaa_extra_bye_str && (aaa_extra_bye = parse_acc_extra(aaa_extra_bye_str, 0))==0) { LM_ERR("failed to parse aaa_extra_bye param\n"); return -1; } if (init_acc_aaa(aaa_proto_url, service_type)!=0 ) { LM_ERR("failed to init radius\n"); return -1; } } else { aaa_proto_url = NULL; } /* ------------ DIAMETER INIT SECTION ----------- */ #ifdef DIAM_ACC /* parse the extra string, if any */ if (dia_extra_str && (dia_extra=parse_acc_extra(dia_extra_str))==0 ) { LM_ERR("failed to parse dia_extra param\n"); return -1; } if (acc_diam_init()!=0) { LM_ERR("failed to init diameter engine\n"); return -1; } #endif /* ----------- EVENT INTERFACE INIT SECTION ----------- */ if (evi_extra_str && (evi_extra = parse_acc_extra(evi_extra_str, 1))==0) { LM_ERR("failed to parse evi_extra param\n"); return -1; } if (evi_extra_bye_str && (evi_extra_bye = parse_acc_extra(evi_extra_bye_str, 0))==0) { LM_ERR("failed to parse evi_extra_bye param\n"); return -1; } if (init_acc_evi() < 0) { LM_ERR("cannot init acc events\n"); return -1; } acc_flags_ctx_idx = context_register_ptr(CONTEXT_GLOBAL, NULL); acc_tm_flags_ctx_idx = tmb.t_ctx_register_ptr(NULL); return 0; } static int child_init(int rank) { if(db_url.s && acc_db_init_child(&db_url)<0) { LM_ERR("could not open database connection"); return -1; } /* DIAMETER */ #ifdef DIAM_ACC /* open TCP connection */ LM_DBG("initializing TCP connection\n"); sockfd = init_mytcp(diameter_client_host, diameter_client_port); if(sockfd==-1) { LM_ERR("TCP connection not established\n"); return -1; } LM_DBG("a TCP connection was established on sockfd=%d\n", sockfd); /* every child with its buffer */ rb = (rd_buf_t*)pkg_malloc(sizeof(rd_buf_t)); if(!rb) { LM_DBG("no more pkg memory\n"); return -1; } rb->buf = 0; #endif return 0; } static void destroy(void) { if (log_extra) destroy_extras( log_extra); if (log_extra_bye) destroy_extras( log_extra_bye); acc_db_close(); if (db_extra) destroy_extras( db_extra); if (db_extra_bye) destroy_extras( db_extra_bye); if (aaa_extra) destroy_extras( aaa_extra); if (aaa_extra_bye) destroy_extras( aaa_extra_bye); #ifdef DIAM_ACC close_tcp_connection(sockfd); if (dia_extra) destroy_extras( dia_extra); #endif } opensips-2.2.2/modules/acc/acc_mod.h000066400000000000000000000044561300170765700173300ustar00rootroot00000000000000/* * Accounting module * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-04-04 grand acc cleanup (jiri) * 2003-11-04 multidomain support for mysql introduced (jiri) * 2004-06-06 removed db_url, db_handle (andrei) * 2005-06-28 multi leg call support added (bogdan) * 2006-09-19 final stage of a masive re-structuring and cleanup (bogdan) */ #ifndef _ACC_MOD_H #define _ACC_MOD_H /* module parameter declaration */ extern int report_cancels; extern int early_media; extern int failed_transaction_flag; extern int detect_direction; extern int acc_log_level; extern int acc_log_flag; extern int acc_log_missed_flag; extern int aaa_flag; extern int aaa_missed_flag; extern aaa_prot proto; extern aaa_conn *conn; extern char* aaa_proto_url; extern int cdr_flag; #ifdef DIAM_ACC #include "diam_tcp.h" extern rd_buf_t *rb; extern int diameter_flag; extern int diameter_missed_flag; #endif extern int db_flag; extern int db_missed_flag; extern str db_table_acc; extern str db_table_mc; extern str acc_method_col; extern str acc_fromuri_col; extern str acc_fromtag_col; extern str acc_touri_col; extern str acc_totag_col; extern str acc_callid_col; extern str acc_cseqno_col; extern str acc_sipcode_col; extern str acc_sipreason_col; extern str acc_time_col; extern str acc_duration_col; extern str acc_ms_duration_col; extern str acc_setuptime_col; extern str acc_created_col; extern int db_table_name; extern unsigned short db_table_name_type; extern int evi_flag; extern int evi_missed_flag; #endif opensips-2.2.2/modules/acc/diam_avp.c000066400000000000000000000261521300170765700175130ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 FhG Fokus * * This file is part of disc, a free diameter server/client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2002-10-04 created by illya (komarov@fokus.gmd.de) * 2003-03-12 converted to shm_malloc/free (andrei) * */ #ifdef DIAM_ACC #include #include #include #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "diam_message.h" /* * each AVP type has some default set/reset flags and a proper data type. * All this default values (for flags and data-type) are correct/set by this * function. */ inline void set_avp_fields( AAA_AVPCode code, AAA_AVP *avp) { switch (code) { case 1: /*AVP_User_Name*/ case 25: /*AVP_Class*/ case 263: /*AVP_Session_Id*/ case 283: /*AVP_Destination_Realm*/ case 293: /*AVP Destination Host*/ case 264: /*AVP_Origin_Host*/ case 296: /*AVP Origin_Realm*/ case 400: /* AVP_Resource */ case 401: /* AVP_Response */ case 402: /* AVP_Chalenge */ case 403: /* AVP_Method */ case 404: /* Service_Type AVP */ case 405: /* User_Group AVP*/ case 279: avp->flags = 0x40|(0x20&avp->flags); avp->type = AAA_AVP_STRING_TYPE; break; case 27: /*AVP_Session_Timeout*/ case 258: /*AVP_Auth_Aplication_Id*/ case 262: /*AVP_Redirect_Max_Cache_Time*/ case 265: /*AVP_Supported_Vendor_Id*/ case 266: /*AVP_Vendor_Id*/ case 268: /*AVP_Result_Code*/ case 270: /*AVP_Session_Binding*/ case 276: /*AVP_Auth_Grace_Period*/ case 278: /*AVP_Origin_State_Id*/ case 291: /*AVP_Authorization_Lifetime*/ avp->flags = 0x40|(0x20&avp->flags); avp->type = AAA_AVP_INTEGER32_TYPE; break; case 33: /*AVP_Proxy_State*/ avp->flags = 0x40; avp->type = AAA_AVP_STRING_TYPE; break; case 257: /*AVP_Host_IP_Address*/ avp->flags = 0x40|(0x20&avp->flags); avp->type = AAA_AVP_ADDRESS_TYPE; break; case 269: /*AVP_Product_Name*/ avp->flags = 0x00; avp->type = AAA_AVP_STRING_TYPE; break; case 281: /*AVP_Error_Message*/ avp->flags = (0x20&avp->flags); avp->type = AAA_AVP_STRING_TYPE; break; default: avp->type = AAA_AVP_DATA_TYPE; }; } /* This function creates an AVP and returns a pointer to it; */ AAA_AVP* AAACreateAVP( AAA_AVPCode code, AAA_AVPFlag flags, AAAVendorId vendorId, char *data, size_t length, AVPDataStatus data_status) { AAA_AVP *avp; /* first check the params */ if( data==0 || length==0) { LM_ERR("null value received for param data/length !!\n"); return 0; } /* allocated a new AVP struct */ avp = 0; avp = (AAA_AVP*)ad_malloc(sizeof(AAA_AVP)); if (!avp) goto error; memset( avp, 0, sizeof(AAA_AVP) ); /* set some fields */ //avp->free_it = free_it; avp->packetType = AAA_DIAMETER; avp->code=code; avp->flags=flags; avp->vendorId=vendorId; set_avp_fields( code, avp); if ( data_status==AVP_DUPLICATE_DATA ) { /* make a duplicate for data */ avp->data.len = length; avp->data.s = (void*)ad_malloc(length); if(!avp->data.s) goto error; memcpy( avp->data.s, data, length); avp->free_it = 1; } else { avp->data.s = data; avp->data.len = length; avp->free_it = (data_status==AVP_FREE_DATA)?1:0; } return avp; error: LM_ERR("no more free memoryfor a new AVP!\n"); return 0; } /* Insert the AVP avp into this avpList of a message after position */ AAAReturnCode AAAAddAVPToMessage( AAAMessage *msg, AAA_AVP *avp, AAA_AVP *position) { AAA_AVP *avp_t; if ( !msg || !avp ) { LM_ERR("param msg or avp passed null or *avpList=NULL " "and position!=NULL !!\n"); return AAA_ERR_PARAMETER; } if (!position) { /* insert at the beginning */ avp->next = msg->avpList.head; avp->prev = 0; msg->avpList.head = avp; if (avp->next) avp->next->prev = avp; else msg->avpList.tail = avp; } else { /* look after avp from position */ for(avp_t=msg->avpList.head;avp_t&&avp_t!=position;avp_t=avp_t->next); if (!avp_t) { LM_ERR("the \"position\" avp is not in \"msg\" message!!\n"); return AAA_ERR_PARAMETER; } /* insert after position */ avp->next = position->next; position->next = avp; if (avp->next) avp->next->prev = avp; else msg->avpList.tail = avp; avp->prev = position; } /* update the short-cuts */ switch (avp->code) { case AVP_Session_Id: msg->sessionId = avp;break; case AVP_Origin_Host: msg->orig_host = avp;break; case AVP_Origin_Realm: msg->orig_realm = avp;break; case AVP_Destination_Host: msg->dest_host = avp;break; case AVP_Destination_Realm: msg->dest_realm = avp;break; case AVP_Result_Code: msg->res_code = avp;break; case AVP_Auth_Session_State: msg->auth_ses_state = avp;break; } return AAA_ERR_SUCCESS; } /* This function finds an AVP with matching code and vendor id */ AAA_AVP *AAAFindMatchingAVP( AAAMessage *msg, AAA_AVP *startAvp, AAA_AVPCode avpCode, AAAVendorId vendorId, AAASearchType searchType) { AAA_AVP *avp_t; /* param checking */ if (!msg) { LM_ERR("param msg passed null !!\n"); goto error; } /* search the startAVP avp */ for(avp_t=msg->avpList.head;avp_t&&avp_t!=startAvp;avp_t=avp_t->next); if (!avp_t && startAvp) { LM_ERR("the \"position\" avp is not in \"avpList\" list!!\n"); goto error; } /* where should I start searching from ? */ if (!startAvp) avp_t=(searchType==AAA_FORWARD_SEARCH)?(msg->avpList.head): (msg->avpList.tail); else avp_t=startAvp; /* start searching */ while(avp_t) { if (avp_t->code==avpCode && avp_t->vendorId==vendorId) return avp_t; avp_t = (searchType==AAA_FORWARD_SEARCH)?(avp_t->next):(avp_t->prev); } error: return 0; } /* This function removes an AVP from a list of a message */ AAAReturnCode AAARemoveAVPFromMessage( AAAMessage *msg, AAA_AVP *avp) { AAA_AVP *avp_t; /* param check */ if ( !msg || !avp ) { LM_ERR("param AVP_LIST \"avpList\" or AVP \"avp\" passed null !!\n"); return AAA_ERR_PARAMETER; } /* search the "avp" avp */ for(avp_t=msg->avpList.head;avp_t&&avp_t!=avp;avp_t=avp_t->next); if (!avp_t) { LM_ERR("the \"avp\" avp is not in \"avpList\" avp list!!\n"); return AAA_ERR_PARAMETER; } /* remove the avp from list */ if (msg->avpList.head==avp) msg->avpList.head = avp->next; else avp->prev->next = avp->next; if (avp->next) avp->next->prev = avp->prev; else msg->avpList.tail = avp->prev; avp->next = avp->prev = 0; /* update short-cuts */ switch (avp->code) { case AVP_Session_Id: msg->sessionId = 0;break; case AVP_Origin_Host: msg->orig_host = 0;break; case AVP_Origin_Realm: msg->orig_realm = 0;break; case AVP_Destination_Host: msg->dest_host = 0;break; case AVP_Destination_Realm: msg->dest_realm = 0;break; case AVP_Result_Code: msg->res_code = 0;break; case AVP_Auth_Session_State: msg->auth_ses_state = 0;break; } return AAA_ERR_SUCCESS; } /* The function frees an AVP */ AAAReturnCode AAAFreeAVP(AAA_AVP **avp) { /* some checks */ if (!avp || !(*avp)) { LM_ERR("param avp cannot be null!!\n"); return AAA_ERR_PARAMETER; } /* free all the mem */ if ( (*avp)->free_it && (*avp)->data.s ) ad_free((*avp)->data.s); ad_free( *avp ); *avp = 0; return AAA_ERR_SUCCESS; } /* This function returns a pointer to the first AVP in the list */ AAA_AVP* AAAGetFirstAVP(AAA_AVP_LIST *avpList){ return avpList->head; } /* This function returns a pointer to the last AVP in the list */ AAA_AVP* AAAGetLastAVP(AAA_AVP_LIST *avpList) { return avpList->tail; } /* This function returns a pointer to the next AVP in the list */ AAA_AVP* AAAGetNextAVP(AAA_AVP *avp) { return avp->next; } /* This function returns a pointer to the previous AVP in the list */ AAA_AVP* AAAGetPrevAVP(AAA_AVP *avp) { return avp->prev; } /* This function converts the data in the AVP to a format suitable for * log or display functions. */ char* AAAConvertAVPToString(AAA_AVP *avp, char *dest, unsigned int destLen) { int l; int i; if (!avp || !dest || !destLen) { LM_ERR("param AVP, DEST or DESTLEN passed as null!!!\n"); return 0; } l = snprintf(dest,destLen,"AVP(%p < %p >%p):packetType=%u;code=%u," "flags=%x;\nDataType=%u;VendorID=%u;DataLen=%u;\n", avp->prev,avp,avp->next,avp->packetType,avp->code,avp->flags, avp->type,avp->vendorId,avp->data.len); switch(avp->type) { case AAA_AVP_STRING_TYPE: l+=snprintf(dest+l,destLen-l,"String: <%.*s>",avp->data.len, avp->data.s); break; case AAA_AVP_INTEGER32_TYPE: l+=snprintf(dest+l,destLen-l,"Int32: <%u>(%x)", htonl(*((unsigned int*)avp->data.s)), htonl(*((unsigned int*)avp->data.s))); break; case AAA_AVP_ADDRESS_TYPE: i = 1; switch (avp->data.len) { case 4: i=i*0; case 6: i=i*2; l+=snprintf(dest+l,destLen-l,"Address IPv4: <%d.%d.%d.%d>", (unsigned char)avp->data.s[i+0], (unsigned char)avp->data.s[i+1], (unsigned char)avp->data.s[i+2], (unsigned char)avp->data.s[i+3]); break; case 16: i=i*0; case 18: i=i*2; l+=snprintf(dest+l,destLen-l, "Address IPv6: <%x.%x.%x.%x.%x.%x.%x.%x>", ((avp->data.s[i+0]<<8)+avp->data.s[i+1]), ((avp->data.s[i+2]<<8)+avp->data.s[i+3]), ((avp->data.s[i+4]<<8)+avp->data.s[i+5]), ((avp->data.s[i+6]<<8)+avp->data.s[i+7]), ((avp->data.s[i+8]<<8)+avp->data.s[i+9]), ((avp->data.s[i+10]<<8)+avp->data.s[i+11]), ((avp->data.s[i+12]<<8)+avp->data.s[i+13]), ((avp->data.s[i+14]<<8)+avp->data.s[i+15])); break; break; } break; //case AAA_AVP_INTEGER64_TYPE: case AAA_AVP_TIME_TYPE: default: LM_WARN("don't know how to print" " this data type [%d] -> tryng hexa\n",avp->type); case AAA_AVP_DATA_TYPE: for (i=0;idata.len&&ldata.s)[i]); } return dest; } AAA_AVP* AAACloneAVP( AAA_AVP *avp , unsigned char clone_data) { AAA_AVP *n_avp; if (!avp || !(avp->data.s) || !(avp->data.len) ) goto error; /* clone the avp structure */ n_avp = (AAA_AVP*)ad_malloc( sizeof(AAA_AVP) ); if (!n_avp) { LM_ERR("cannot get free memory!!\n"); goto error; } memcpy( n_avp, avp, sizeof(AAA_AVP)); n_avp->next = n_avp->prev = 0; if (clone_data) { /* clone the avp data */ n_avp->data.s = (char*)ad_malloc( avp->data.len ); if (!(n_avp->data.s)) { LM_ERR("cannot get free memory!!\n"); ad_free( n_avp ); goto error; } memcpy( n_avp->data.s, avp->data.s, avp->data.len); n_avp->free_it = 1; } else { /* link the clone's data to the original's data */ n_avp->data.s = avp->data.s; n_avp->data.len = avp->data.len; n_avp->free_it = 0; } return n_avp; error: return 0; } #endif opensips-2.2.2/modules/acc/diam_dict.h000066400000000000000000000024011300170765700176440ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 FhG Fokus * * This file is part of disc, a free diameter server/client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- */ #ifdef DIAM_ACC #ifndef DIAM_ACC_H #define DIAM_ACC_H #define SERVICE_LEN 1 #define SIP_ACCOUNTING "9" #define vendorID 0 /* Accounting AVPs */ enum{ /*Accounting*/ AVP_SIP_CALLID = 550, /* string */ AVP_SIP_METHOD = 551, /* string */ AVP_SIP_STATUS = 552, /* string */ AVP_SIP_FROM_TAG = 553, /* string */ AVP_SIP_TO_TAG = 554, /* string */ AVP_SIP_CODE = 564 /* string */ }; #endif #endif opensips-2.2.2/modules/acc/diam_message.c000066400000000000000000000206171300170765700203510ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 FhG Fokus * * This file is part of disc, a free diameter server/client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * * 2003-04-07 created by bogdan */ #ifdef DIAM_ACC #include #include #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "diam_message.h" #define get_3bytes(_b) \ ((((unsigned int)(_b)[0])<<16)|(((unsigned int)(_b)[1])<<8)|\ (((unsigned int)(_b)[2]))) #define get_4bytes(_b) \ ((((unsigned int)(_b)[0])<<24)|(((unsigned int)(_b)[1])<<16)|\ (((unsigned int)(_b)[2])<<8)|(((unsigned int)(_b)[3]))) #define set_3bytes(_b,_v) \ {(_b)[0]=((_v)&0x00ff0000)>>16;(_b)[1]=((_v)&0x0000ff00)>>8;\ (_b)[2]=((_v)&0x000000ff);} #define set_4bytes(_b,_v) \ {(_b)[0]=((_v)&0xff000000)>>24;(_b)[1]=((_v)&0x00ff0000)>>16;\ (_b)[2]=((_v)&0x0000ff00)>>8;(_b)[3]=((_v)&0x000000ff);} #define to_32x_len( _len_ ) \ ( (_len_)+(((_len_)&3)?4-((_len_)&3):0) ) /* from a AAAMessage structure, a buffer to be send is build */ AAAReturnCode AAABuildMsgBuffer( AAAMessage *msg ) { char *p; AAA_AVP *avp; /* first let's compute the length of the buffer */ msg->buf.len = AAA_MSG_HDR_SIZE; /* AAA message header size */ /* count and add the avps */ for(avp=msg->avpList.head;avp;avp=avp->next) { msg->buf.len += AVP_HDR_SIZE(avp->flags)+ to_32x_len( avp->data.len ); } /* allocate some memory */ msg->buf.s = (char*)ad_malloc( msg->buf.len ); if (!msg->buf.s) { LM_ERR("no more pkg free memory!\n"); goto error; } memset(msg->buf.s, 0, msg->buf.len); /* fill in the buffer */ p = msg->buf.s; /* DIAMETER HEADER */ /* message length */ ((unsigned int*)p)[0] =htonl(msg->buf.len); /* Diameter Version */ *p = 1; p += VER_SIZE + MESSAGE_LENGTH_SIZE; /* command code */ ((unsigned int*)p)[0] = htonl(msg->commandCode); /* flags */ *p = (unsigned char)msg->flags; p += FLAGS_SIZE + COMMAND_CODE_SIZE; /* application-ID */ ((unsigned int*)p)[0] = htonl(msg->applicationId); p += APPLICATION_ID_SIZE; /* hop by hop id */ ((unsigned int*)p)[0] = msg->hopbyhopId; p += HOP_BY_HOP_IDENTIFIER_SIZE; /* end to end id */ ((unsigned int*)p)[0] = msg->endtoendId; p += END_TO_END_IDENTIFIER_SIZE; /* AVPS */ for(avp=msg->avpList.head;avp;avp=avp->next) { /* AVP HEADER */ /* avp code */ set_4bytes(p,avp->code); p +=4; /* flags */ (*p++) = (unsigned char)avp->flags; /* avp length */ set_3bytes(p, (AVP_HDR_SIZE(avp->flags)+avp->data.len) ); p += 3; /* vendor id */ if ((avp->flags&0x80)!=0) { set_4bytes(p,avp->vendorId); p +=4; } /* data */ memcpy( p, avp->data.s, avp->data.len); p += to_32x_len( avp->data.len ); } if ((char*)p-msg->buf.s!=msg->buf.len) { LM_ERR("mismatch between len and buf!\n"); ad_free( msg->buf.s ); msg->buf.s = 0; msg->buf.len = 0; goto error; } LM_DBG("Message: %.*s\n", msg->buf.len, msg->buf.s); return AAA_ERR_SUCCESS; error: return -1; } /* frees a message allocated through AAANewMessage() */ AAAReturnCode AAAFreeMessage(AAAMessage **msg) { AAA_AVP *avp_t; AAA_AVP *avp; /* param check */ if (!msg || !(*msg)) goto done; /* free the avp list */ avp = (*msg)->avpList.head; while (avp) { avp_t = avp; avp = avp->next; /*free the avp*/ AAAFreeAVP(&avp_t); } /* free the buffer (if any) */ if ( (*msg)->buf.s ) ad_free( (*msg)->buf.s ); /* free the AAA msg */ ad_free(*msg); msg = 0; done: return AAA_ERR_SUCCESS; } /* Sets the proper result_code into the Result-Code AVP; thus avp must already * exists into the reply message */ AAAResultCode AAASetMessageResultCode( AAAMessage *message, AAAResultCode resultCode) { if ( !is_req(message) && message->res_code) { *((unsigned int*)(message->res_code->data.s)) = htonl(resultCode); return AAA_ERR_SUCCESS; } return AAA_ERR_FAILURE; } /* This function convert message to message structure */ AAAMessage* AAATranslateMessage( unsigned char* source, unsigned int sourceLen, int attach_buf) { unsigned char *ptr; AAAMessage *msg; unsigned char version; unsigned int msg_len; AAA_AVP *avp; unsigned int avp_code; unsigned char avp_flags; unsigned int avp_len; unsigned int avp_vendorID; unsigned int avp_data_len; /* check the params */ if( !source || !sourceLen || sourceLensourceLen) { LM_ERR("AAA message len [%d] bigger then buffer len [%d]\n", msg_len,sourceLen); goto error; } /* command flags */ msg->flags = *ptr; ptr += FLAGS_SIZE; /* command code */ msg->commandCode = get_3bytes( ptr ); ptr += COMMAND_CODE_SIZE; /* application-Id */ msg->applicationId = get_4bytes( ptr ); ptr += APPLICATION_ID_SIZE; /* Hop-by-Hop-Id */ msg->hopbyhopId = *((unsigned int*)ptr); ptr += HOP_BY_HOP_IDENTIFIER_SIZE; /* End-to-End-Id */ msg->endtoendId = *((unsigned int*)ptr); ptr += END_TO_END_IDENTIFIER_SIZE; /* start decoding the AVPS */ while (ptr < source+msg_len) { if (ptr+AVP_HDR_SIZE(0x80)>source+msg_len){ LM_ERR("source buffer to short!! " "Cannot read the whole AVP header!\n"); goto error; } /* avp code */ avp_code = get_4bytes( ptr ); ptr += AVP_CODE_SIZE; /* avp flags */ avp_flags = (unsigned char)*ptr; ptr += AVP_FLAGS_SIZE; /* avp length */ avp_len = get_3bytes( ptr ); ptr += AVP_LENGTH_SIZE; if (avp_len<1) { LM_ERR("invalid AVP len [%d]\n", avp_len); goto error; } /* avp vendor-ID */ avp_vendorID = 0; if (avp_flags&AAA_AVP_FLAG_VENDOR_SPECIFIC) { avp_vendorID = get_4bytes( ptr ); ptr += AVP_VENDOR_ID_SIZE; } /* data length */ avp_data_len = avp_len-AVP_HDR_SIZE(avp_flags); /*check the data length */ if ( source+msg_lenavpList.tail); ptr += to_32x_len( avp_data_len ); } /* link the buffer to the message */ if (attach_buf) { msg->buf.s = (char*)source; msg->buf.len = msg_len; } //AAAPrintMessage( msg ); return msg; error: LM_ERR("message conversion droped!!\n"); AAAFreeMessage(&msg); return 0; } /* print as debug all info contained by an aaa message + AVPs */ void AAAPrintMessage( AAAMessage *msg) { char buf[1024]; AAA_AVP *avp; /* print msg info */ LM_DBG("AAA_MESSAGE - %p\n",msg); LM_DBG("\tCode = %u\n",msg->commandCode); LM_DBG("\tFlags = %x\n",msg->flags); /*print the AVPs */ avp = msg->avpList.head; while (avp) { AAAConvertAVPToString(avp,buf,1024); LM_DBG("\n%s\n",buf); avp=avp->next; } } AAAMessage* AAAInMessage(AAACommandCode cmdCode, AAAApplicationId appID) { AAAMessage *msg; /* allocated a new AAAMessage structure a set it to 0 */ msg = (AAAMessage*)ad_malloc(sizeof(AAAMessage)); if (!msg) { LM_ERR("no more pkg free memory!\n"); return NULL; } memset(msg, 0, sizeof(AAAMessage)); /* command code */ msg->commandCode = cmdCode; /* application ID */ msg->applicationId = appID; /* it's a new request -> set the flag */ msg->flags = 0x80; return msg; } #endif opensips-2.2.2/modules/acc/diam_message.h000066400000000000000000000250351300170765700203550ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 FhG Fokus * * This file is part of disc, a free diameter server/client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-04-07 created by bogdan */ #ifdef DIAM_ACC #ifndef _AAA_DIAMETER_MSG_H #define _AAA_DIAMETER_MSG_H #include "../../str.h" #include "../../mem/mem.h" #define ad_malloc pkg_malloc #define ad_free pkg_free /*********************************** AAA TYPES *******************************/ #define AAA_NO_VENDOR_ID 0 #define VER_SIZE 1 #define MESSAGE_LENGTH_SIZE 3 #define FLAGS_SIZE 1 #define COMMAND_CODE_SIZE 3 #define APPLICATION_ID_SIZE 4 #define HOP_BY_HOP_IDENTIFIER_SIZE 4 #define END_TO_END_IDENTIFIER_SIZE 4 #define AVP_CODE_SIZE 4 #define AVP_FLAGS_SIZE 1 #define AVP_LENGTH_SIZE 3 #define AVP_VENDOR_ID_SIZE 4 #define AAA_MSG_HDR_SIZE \ (VER_SIZE + MESSAGE_LENGTH_SIZE + FLAGS_SIZE + COMMAND_CODE_SIZE +\ APPLICATION_ID_SIZE+HOP_BY_HOP_IDENTIFIER_SIZE+END_TO_END_IDENTIFIER_SIZE) #define AVP_HDR_SIZE(_flags_) \ (AVP_CODE_SIZE+AVP_FLAGS_SIZE+AVP_LENGTH_SIZE+\ AVP_VENDOR_ID_SIZE*(((_flags_)&AAA_AVP_FLAG_VENDOR_SPECIFIC)!=0) ) /* message codes */ #ifndef WORDS_BIGENDIAN #define AS_MSG_CODE 0x12010000 #define AC_MSG_CODE 0x0f010000 #define CE_MSG_CODE 0x01010000 #define DW_MSG_CODE 0x18010000 #define DP_MSG_CODE 0x1a010000 #define RA_MSG_CODE 0x02010000 #define ST_MSG_CODE 0x13010000 #define MASK_MSG_CODE 0xffffff00 #else #error BIG endian detected!! #define AS_MSG_CODE 0x00000112 #define AC_MSG_CODE 0x0000010f #define CE_MSG_CODE 0x00000101 #define DW_MSG_CODE 0x00000118 #define DP_MSG_CODE 0x0000011a #define RA_MSG_CODE 0x00000102 #define ST_MSG_CODE 0x00000113 #define MASK_MSG_CODE 0x00ffffff #endif typedef unsigned int AAACommandCode; typedef unsigned int AAAVendorId; typedef unsigned int AAAExtensionId; typedef unsigned int AAA_AVPCode; typedef unsigned int AAAValue; typedef unsigned int AAAApplicationId; typedef void* AAAApplicationRef; typedef str AAASessionId; typedef unsigned int AAAMsgIdentifier; typedef unsigned char AAAMsgFlag; /* Status codes returned by functions in the AAA API */ typedef enum { AAA_ERR_NOT_FOUND = -2, /* handle or id not found */ AAA_ERR_FAILURE = -1, /* unspecified failure during an AAA op. */ AAA_ERR_SUCCESS = 0, /* AAA operation succeeded */ AAA_ERR_NOMEM, /* op. caused memory to be exhausted */ AAA_ERR_PROTO, /* AAA protocol error */ AAA_ERR_SECURITY, AAA_ERR_PARAMETER, AAA_ERR_CONFIG, AAA_ERR_UNKNOWN_CMD, AAA_ERR_MISSING_AVP, AAA_ERR_ALREADY_INIT, AAA_ERR_TIMED_OUT, AAA_ERR_CANNOT_SEND_MSG, AAA_ERR_ALREADY_REGISTERED, AAA_ERR_CANNOT_REGISTER, AAA_ERR_NOT_INITIALIZED, AAA_ERR_NETWORK_ERROR, } AAAReturnCode; /* The following are AVP data type codes. They correspond directly to * the AVP data types outline in the Diameter specification [1]: */ typedef enum { AAA_AVP_DATA_TYPE, AAA_AVP_STRING_TYPE, AAA_AVP_ADDRESS_TYPE, AAA_AVP_INTEGER32_TYPE, AAA_AVP_INTEGER64_TYPE, AAA_AVP_TIME_TYPE, } AAA_AVPDataType; /* The following are used for AVP header flags and for flags in the AVP * wrapper struct and AVP dictionary definitions. */ typedef enum { AAA_AVP_FLAG_NONE = 0x00, AAA_AVP_FLAG_MANDATORY = 0x40, AAA_AVP_FLAG_RESERVED = 0x1F, AAA_AVP_FLAG_VENDOR_SPECIFIC = 0x80, AAA_AVP_FLAG_END_TO_END_ENCRYPT = 0x20, } AAA_AVPFlag; /* List with all known application identifiers */ typedef enum { AAA_APP_DIAMETER_COMMON_MSG = 0, AAA_APP_NASREQ = 1, AAA_APP_MOBILE_IP = 2, AAA_APP_DIAMETER_BASE_ACC = 3, AAA_APP_RELAY = 0xffffffff, }AAA_APP_IDS; /* The following are the result codes returned from remote servers as * part of messages */ typedef enum { AAA_MUTI_ROUND_AUTH = 1001, AAA_SUCCESS = 2001, AAA_COMMAND_UNSUPPORTED = 3001, AAA_UNABLE_TO_DELIVER = 3002, AAA_REALM_NOT_SERVED = 3003, AAA_TOO_BUSY = 3004, AAA_LOOP_DETECTED = 3005, AAA_REDIRECT_INDICATION = 3006, AAA_APPLICATION_UNSUPPORTED = 3007, AAA_INVALID_HDR_BITS = 3008, AAA_INVALID_AVP_BITS = 3009, AAA_UNKNOWN_PEER = 3010, AAA_AUTHENTICATION_REJECTED = 4001, AAA_OUT_OF_SPACE = 4002, AAA_ELECTION_LOST = 4003, AAA_AVP_UNSUPPORTED = 5001, AAA_UNKNOWN_SESSION_ID = 5002, AAA_AUTHORIZATION_REJECTED = 5003, AAA_INVALID_AVP_VALUE = 5004, AAA_MISSING_AVP = 5005, AAA_RESOURCES_EXCEEDED = 5006, AAA_CONTRADICTING_AVPS = 5007, AAA_AVP_NOT_ALLOWED = 5008, AAA_AVP_OCCURS_TOO_MANY_TIMES = 5009, AAA_NO_COMMON_APPLICATION = 5010, AAA_UNSUPPORTED_VERSION = 5011, AAA_UNABLE_TO_COMPLY = 5012, AAA_INVALID_BIT_IN_HEADER = 5013, AAA_INVALIS_AVP_LENGTH = 5014, AAA_INVALID_MESSGE_LENGTH = 5015, AAA_INVALID_AVP_BIT_COMBO = 5016, AAA_NO_COMMON_SECURITY = 5017, } AAAResultCode; typedef enum { AVP_User_Name = 1, AVP_Class = 25, AVP_Session_Timeout = 27, AVP_Proxy_State = 33, AVP_Host_IP_Address = 257, AVP_Auth_Application_Id = 258, AVP_Vendor_Specific_Application_Id= 260, AVP_Redirect_Max_Cache_Time = 262, AVP_Session_Id = 263, AVP_Origin_Host = 264, AVP_Supported_Vendor_Id = 265, AVP_Vendor_Id = 266, AVP_Result_Code = 268, AVP_Product_Name = 269, AVP_Session_Binding = 270, AVP_Disconnect_Cause = 273, AVP_Auth_Request_Type = 274, AVP_Auth_Grace_Period = 276, AVP_Auth_Session_State = 277, AVP_Origin_State_Id = 278, AVP_Accounting_Record_Type = 279, AVP_Proxy_Host = 280, AVP_Error_Message = 281, AVP_Record_Route = 282, AVP_Destination_Realm = 283, AVP_Proxy_Info = 284, AVP_Re_Auth_Request_Type = 285, AVP_Authorization_Lifetime = 291, AVP_Redirect_Host = 292, AVP_Destination_Host = 293, AVP_Termination_Cause = 295, AVP_Origin_Realm = 296, AVP_Resource = 400, AVP_Response = 401, AVP_Challenge = 402, AVP_Method = 403, AVP_Service_Type = 404, AVP_User_Group = 405, AVP_SIP_MSGID = 406 }AAA_AVPCodeNr; /* The following type allows the client to specify which direction to * search for an AVP in the AVP list: */ typedef enum { AAA_FORWARD_SEARCH = 0, AAA_BACKWARD_SEARCH } AAASearchType; typedef enum { AAA_ACCT_EVENT = 1, AAA_ACCT_START = 2, AAA_ACCT_INTERIM = 3, AAA_ACCT_STOP = 4 } AAAAcctMessageType; typedef enum { AVP_DUPLICATE_DATA, AVP_DONT_FREE_DATA, AVP_FREE_DATA, } AVPDataStatus; /* The following structure contains a message AVP in parsed format */ typedef struct avp { struct avp *next; struct avp *prev; enum { AAA_RADIUS, AAA_DIAMETER } packetType; AAA_AVPCode code; AAA_AVPFlag flags; AAA_AVPDataType type; AAAVendorId vendorId; str data; unsigned char free_it; } AAA_AVP; /* The following structure is used for representing lists of AVPs on the * message: */ typedef struct _avp_list_t { AAA_AVP *head; AAA_AVP *tail; } AAA_AVP_LIST; /* The following structure contains the full AAA message: */ typedef struct _message_t { AAAMsgFlag flags; AAACommandCode commandCode; AAAApplicationId applicationId; AAAMsgIdentifier endtoendId; AAAMsgIdentifier hopbyhopId; AAASessionId *sId; AAA_AVP *sessionId; AAA_AVP *orig_host; AAA_AVP *orig_realm; AAA_AVP *dest_host; AAA_AVP *dest_realm; AAA_AVP *res_code; AAA_AVP *auth_ses_state; AAA_AVP_LIST avpList; str buf; void *in_peer; } AAAMessage; /**************************** AAA MESSAGE FUNCTIONS **************************/ /* MESSAGES */ #define is_req(_msg_) \ (((_msg_)->flags)&0x80) AAAMessage* AAAInMessage(AAACommandCode commandCode, AAAApplicationId applicationId); AAAReturnCode AAAFreeMessage( AAAMessage **message); AAAResultCode AAASetMessageResultCode( AAAMessage *message, AAAResultCode resultCode); void AAAPrintMessage( AAAMessage *msg); AAAReturnCode AAABuildMsgBuffer( AAAMessage *msg ); AAAMessage* AAATranslateMessage( unsigned char* source, unsigned int sourceLen, int attach_buf ); /* AVPS */ #define AAACreateAndAddAVPToMessage(_msg_,_code_,_flags_,_vdr_,_data_,_len_) \ ( AAAAddAVPToMessage(_msg_, \ AAACreateAVP(_code_,_flags_,_vdr_,_data_,_len_, AVP_DUPLICATE_DATA),\ (_msg_)->avpList.tail) ) AAA_AVP* AAACreateAVP( AAA_AVPCode code, AAA_AVPFlag flags, AAAVendorId vendorId, char *data, size_t length, AVPDataStatus data_status); AAA_AVP* AAACloneAVP( AAA_AVP *avp, unsigned char duplicate_data ); AAAReturnCode AAAAddAVPToMessage( AAAMessage *msg, AAA_AVP *avp, AAA_AVP *position); AAA_AVP *AAAFindMatchingAVP( AAAMessage *msg, AAA_AVP *startAvp, AAA_AVPCode avpCode, AAAVendorId vendorId, AAASearchType searchType); AAAReturnCode AAARemoveAVPFromMessage( AAAMessage *msg, AAA_AVP *avp); AAAReturnCode AAAFreeAVP( AAA_AVP **avp); AAA_AVP* AAAGetFirstAVP( AAA_AVP_LIST *avpList); AAA_AVP* AAAGetLastAVP( AAA_AVP_LIST *avpList); AAA_AVP* AAAGetNextAVP( AAA_AVP *avp); AAA_AVP* AAAGetPrevAVP( AAA_AVP *avp); char *AAAConvertAVPToString( AAA_AVP *avp, char *dest, unsigned int destLen); #endif #endif opensips-2.2.2/modules/acc/diam_tcp.c000066400000000000000000000161401300170765700175070ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef DIAM_ACC #include #include #include #include #include #include #include #include "../../dprint.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_to.h" #include "../../parser/parse_from.h" #include "../../mem/mem.h" #include "../../resolv.h" #include "diam_message.h" #include "diam_tcp.h" #include "diam_dict.h" #define M_NAME "acc" /* TCP connection setup */ int init_mytcp(char* host, int port) { int sockfd; struct sockaddr_in serv_addr; struct hostent *server; sockfd = socket(PF_INET, SOCK_STREAM, 0); if (sockfd < 0) { LM_ERR("failed to create the socket\n"); return -1; } server = resolvehost(host,0); if (server == NULL) { LM_ERR("failed to find the host\n"); return -1; } memset((char *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = PF_INET; memcpy((char *)&serv_addr.sin_addr.s_addr, (char *)server->h_addr, server->h_length); serv_addr.sin_port = htons(port); if (connect(sockfd, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { LM_ERR("failed to connec to the DIAMETER client\n"); return -1; } return sockfd; } /* send a message over an already opened TCP connection */ int tcp_send_recv(int sockfd, char* buf, int len, rd_buf_t* rb, unsigned int waited_id) { int n, number_of_tries; fd_set active_fd_set, read_fd_set; struct timeval tv; unsigned long int result_code; AAAMessage *msg; AAA_AVP *avp; char serviceType; unsigned int m_id; /* try to write the message to the Diameter client */ while( (n=write(sockfd, buf, len))==-1 ) { if (errno==EINTR) continue; LM_ERR("write returned error: %s\n", strerror(errno)); return AAA_ERROR; } if (n!=len) { LM_ERR("write gave no error but wrote less than asked\n"); return AAA_ERROR; } /* wait for the answer a limited amount of time */ tv.tv_sec = MAX_WAIT_SEC; tv.tv_usec = MAX_WAIT_USEC; /* Initialize the set of active sockets. */ FD_ZERO (&active_fd_set); FD_SET (sockfd, &active_fd_set); number_of_tries = 0; while(number_of_triesbuf, rb->buf_len, 0); if(!msg) { LM_ERR("message structure not obtained\n"); return AAA_ERROR; } avp = AAAFindMatchingAVP(msg, NULL, AVP_SIP_MSGID, vendorID, AAA_FORWARD_SEARCH); if(!avp) { LM_ERR("AVP_SIP_MSGID not found\n"); return AAA_ERROR; } m_id = *((unsigned int*)(avp->data.s)); LM_DBG("######## m_id=%d\n", m_id); if(m_id!=waited_id) { number_of_tries ++; LM_NOTICE("old message received\n"); continue; } goto next; } LM_ERR("too many old messages received\n"); return AAA_TIMEOUT; next: /* Finally die correct answer */ avp = AAAFindMatchingAVP(msg, NULL, AVP_Service_Type, vendorID, AAA_FORWARD_SEARCH); if(!avp) { LM_ERR("AVP_Service_Type not found\n"); return AAA_ERROR; } serviceType = avp->data.s[0]; result_code = ntohl(*((unsigned long int*)(msg->res_code->data.s))); switch(result_code) { case AAA_SUCCESS: /* 2001 */ return ACC_SUCCESS; default: /* error */ return ACC_FAILURE; } } void reset_read_buffer(rd_buf_t *rb) { rb->first_4bytes = 0; rb->buf_len = 0; if(rb->buf) pkg_free(rb->buf); rb->buf = 0; } /* read from a socket, an AAA message buffer */ int do_read( int socket, rd_buf_t *p) { unsigned char *ptr; unsigned int wanted_len, len; int n; if (p->buf==0) { wanted_len = sizeof(p->first_4bytes) - p->buf_len; ptr = ((unsigned char*)&(p->first_4bytes)) + p->buf_len; } else { wanted_len = p->first_4bytes - p->buf_len; ptr = p->buf + p->buf_len; } while( (n=recv( socket, ptr, wanted_len, MSG_DONTWAIT ))>0 ) { // LM_DBG("(sock=%d) -> n=%d (expected=%d)\n", p->sock,n,wanted_len); p->buf_len += n; if (nbuf==0) { /* I just finished reading the first 4 bytes from msg */ len = ntohl(p->first_4bytes)&0x00ffffff; if (lenMAX_AAA_MSG_SIZE) { LM_ERR("(sock=%d): invalid message " "length read %u (%x)\n", socket, len, p->first_4bytes); goto error; } //LM_DBG("message length = %d(%x)\n",len,len); if ( (p->buf=pkg_malloc(len))==0 ) { LM_ERR("no more pkg memory\n"); goto error; } *((unsigned int*)p->buf) = p->first_4bytes; p->buf_len = sizeof(p->first_4bytes); p->first_4bytes = len; /* update the reading position and len */ ptr = p->buf + p->buf_len; wanted_len = p->first_4bytes - p->buf_len; } else { /* I finished reading the whole message */ LM_DBG("(sock=%d): whole message read (len=%d)!\n", socket, p->first_4bytes); return CONN_SUCCESS; } } } if (n==0) { LM_INFO("(sock=%d): FIN received\n", socket); return CONN_CLOSED; } if ( n==-1 && errno!=EINTR && errno!=EAGAIN ) { LM_ERR("(on sock=%d): n=%d , errno=%d (%s)\n", socket, n, errno, strerror(errno)); goto error; } error: return CONN_ERROR; } void close_tcp_connection(int sfd) { shutdown(sfd, 2); } /* * Extract URI depending on the request from To or From header */ int get_uri(struct sip_msg* m, str** uri) { if ((REQ_LINE(m).method.len == 8) && (memcmp(REQ_LINE(m).method.s, "REGISTER", 8) == 0)) {/* REGISTER */ if (!m->to && ((parse_headers(m, HDR_TO_F, 0) == -1) || !m->to )) { LM_ERR("the To header field was not found or malformed\n"); return -1; } *uri = &(get_to(m)->uri); } else { if (parse_from_header(m)<0) { LM_ERR("failed to parse headers\n"); return -2; } *uri = &(get_from(m)->uri); } return 0; } #endif opensips-2.2.2/modules/acc/diam_tcp.h000066400000000000000000000034671300170765700175240ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef DIAM_ACC #ifndef ACC_TCP #define ACC_TCP #include "../../str.h" #include "../../parser/msg_parser.h" /* information needed for reading messages from tcp connection */ typedef struct rd_buf { unsigned int first_4bytes; unsigned int buf_len; unsigned char *buf; } rd_buf_t; #define AAA_ERROR -1 #define AAA_CONN_CLOSED -2 #define AAA_TIMEOUT -3 #define ACC_SUCCESS 0 #define ACC_FAILURE 1 #define AAA_NO_CONNECTION -1 #define MAX_WAIT_SEC 2 #define MAX_WAIT_USEC 0 #define MAX_AAA_MSG_SIZE 65536 #define CONN_SUCCESS 1 #define CONN_ERROR -1 #define CONN_CLOSED -2 #define MAX_TRIES 10 int sockfd; int do_read( int socket, rd_buf_t *p); void reset_read_buffer(rd_buf_t *rb); /* it initializes the TCP connection */ int init_mytcp(char* host, int port); /* send a message over an already opened TCP connection */ int tcp_send_recv(int sockfd, char* buf, int len, rd_buf_t* rb, unsigned int waited_id); void close_tcp_connection(int sfd); int get_uri(struct sip_msg* m, str** uri); #endif #endif opensips-2.2.2/modules/acc/doc/000077500000000000000000000000001300170765700163265ustar00rootroot00000000000000opensips-2.2.2/modules/acc/doc/acc.xml000066400000000000000000000025401300170765700175770ustar00rootroot00000000000000 %docentities; ]> Acc Module &osips; Jiri Kuthan Bogdan-Andrei Iancu Ramona-Elena Modroiu Bogdan-Andrei Iancu Irina-Maria Stanescu 2002-2003 2003 FhG FOKUS 2004-2009 &voicesystem; 2009-2013 &osipssolname; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/acc/doc/acc_admin.xml000066400000000000000000001332421300170765700207530ustar00rootroot00000000000000 &adminguide;
Overview ACC module is used to account transactions information to different backends like syslog, SQL, AAA and DIAMETER (beta version). To account a transaction and to choose which set of backends to be used, the script writer just has to use mark the transaction for accouting by using the script function. Note that the function is not actually doing the accounting at that very process, it is just setting a marker - the actual accouting will be done later when the transaction or dialog will be completed. Even so, the module allows the script writter to force accounting on the spot in special cases via some other script functions. The accounting module will log by default a fixed set of attributes for the transaction - if you customize your accounting by adding more information to be logged, please see the next chapter about extra accounting - . The fixed minimal accounting information is: Request Method name From header TAG parameter To header TAG parameter Call-Id 3-digit Status code from final reply Reason phrase from final reply Time stamp when transaction was completed If a value is not present in request, the empty string is accounted instead. Note that: A single INVITE may produce multiple accounting reports -- that's due to SIP forking feature. Since version 2.2 all flags used for accounting have been replaced by the do_accounting function. No need to worry anymore of whether you have set the flags or not, or be confused by various flag names, now you only have to call the function and it will do all the work for you. &osips; now supports session/dialog accounting. It can automatically correlate INVITEs with BYEs for generating proper CDRs, for example for purpose of billing. If a UA fails in middle of conversation, a proxy will never find out about it. In general, a better practice is to account from an end-device (such as PSTN gateway), which best knows about call status (including media status and PSTN status in case of the gateway). The SQL, Event Interface and AAA backend support are compiled in the module. For DIAMETER you need to enable it by recompiling the module with properly set defines: uncomment DIAM_ACC line in modules/acc/Makefile. The AAA client needs to be configured properly. NOTE: diameter support was developed for DISC (DIameter Server Client project at http://developer.berlios.de/projects/disc/). This project seems to be no longer maintained and DIAMETER specifications were updated in the meantime. Thus, the DIAMETER part in the module is obsolete and needs rework to be usable with opendiameter or other DIAMETER servers.
General Example loadmodule "modules/acc/acc.so" if (uri=~"sip:+40") /* calls to Romania */ { if (!proxy_authorize("sip_domain.net" /* realm */, "subscriber" /* table name */)) { proxy_challenge("sip_domain.net" /* realm */, "0" /* no qop */ ); exit; } if (is_method("INVITE") && !db_check_from()) { xlog("FROM URI != digest username\n"); sl_send_reply("403","Forbidden"); } do_accounting("log"); /* set for accounting via syslog */ t_relay(); /* enter stateful mode now */ };
Extra accounting
Overview Along the static default information, ACC modules allows dynamical selection of extra information to be logged. This allows you to log any pseudo-variable (AVPs, parts of the request, parts of the reply, etc).
Definitions and syntax Selection of extra information is done via xxx_extra parameters by specifying the names of additional information you want to log. This information is defined via pseudo-variables and may include headers, AVPs values or other message or system values. The syntax of the parameter is: xxx_extra = extra_definition (';'extra_definition)* extra_definition = log_name '=' pseudo_variable ['/reply'] Each PV (pseudo variable) may be evaluated in the context of the request message (to access info from it) or in the context of the reply message (final reply for the request). Using /reply marker, the PV will be evaluated in the context of the reply; by default (without the marker), the evaluation is done in the contect of the request. This will allow you to automatically account information (message or network related) from both request and reply in the same time. The full list of supported pseudo-variables in &osips; is availabe at: http://www.opensips.org/pmwiki.php?n=Resources.DocsCoreVar Via log_name you define how/where the data will be logged. Its meaning depends of the accounting support which is used: LOG accounting - log_name will be just printed along with the data in log_name=data format; DB accounting - log_name will be the name of the DB column where the data will be stored.IMPORTANT: add in db acc table the columns corresponding to each extra data; AAA accounting - log_name will be the AVP name used for packing the data into AAA message. The log_name will be translated to AVP number via the dictionary. IMPORTANT: add in AAA dictionary the log_name attribute. DIAMETER accounting - log_name will be the AVP code used for packing the data into DIAMETER message. The AVP code is given directly as integer, since DIAMETER has no dictionary support yet. IMPORTANT: log_name must be a number. Events accounting - log_name will be the name of the parameter in the event raised.
How it works Some pseudo variables may return more than one value (like headers or AVPs). In this case, the returned values are embedded in a single string in a comma-separated format.
Multi Call-Legs accounting
Overview A SIP call can have multiple legs due forwarding actions. For example user A calls user B which forwards the call to user C. There is only one SIP call but with 2 legs ( A to B and B to C). Accounting the legs of a call is required for proper billing of the calls (if C is a PSTN number and the call is billed, user B must pay for the call - as last party modifing the call destination-, and not A - as initiator of the call. Call forwarding on server is only one example which shows the necessity of the having an accounting engine with multiple legs support.
Configuration First how it works: The idea is to have a set of AVPs and for each call leg to store a set of values in the AVPs. The meaning of the AVP content is stricly decided by the script writer - it can be the origin and source of the leg, its status or any other related information. If you have a set of 4 AVPS (AVP1, AVP2, AVP3, AVP4), then for the "A call B and B forwards to C" example, you need to set a different set of values for the AVPs for each leg ([A,B] and [B,C]) . The script writer must take care and properly insert all these AVP from the script (in proper order and with the correct type). When the accounting information for the call will be written/sent, all the call-leg pairs will be added (based on the found AVP sets). By default, the multiple call-leg support is disabled - it can be enabled just by setting the per-leg set of AVPs via the multi_leg_info and/or multi_leg_bye_info module parameters. Note that the last one only makes sense only for CDRs that are generated automatically by &osips;. Important: when both multi-leg accounting is done (for INVITE and BYE), you have to make sure that the AVPs are populated with the same number of legs, otherwise the multi-leg accounting will have an unexpected behavior (values may no longer match the leg number).
Logged data For each call, all the values of the AVP set (which defines a call-leg) will be logged. How the information will be actually logged, depends of the data backend: syslog -- all leg-sets will be added to one record string as AVP1=xxx, AVP2=xxxx ,... sets. database -- each pair will be separately logged (due DB data structure constraints); several records will be written, the difference between them being only the fields corresponding to the call-leg info. You will need to add in your DB (all acc related tables) the colums for call-leg info (a column for each AVP of the set). AAA -- all sets will be added to the same AAA accounting message as AAA AVPs - for each call-leg a set of AAA AVPs will be added (corresponding to the per-leg AVP set) You will need to add in your dictionary the AAA AVPs used in call-leg AVP set definition. Diameter same as for AAA. events -- each pair will appear as a different parameter-value pair in the event. Similar to the database behavior, multiple events will be raised, and the only difference between them is the leg information. Important!!! In order to use RADIUS, one must include the AVPs which are located in $(opensips_install_dir)/etc/dictionary.opensips, both in opensips radius config script dictionary and radius server dictionary. Most important are the last three AVPs (IDs : 227, 228, 229) which you won't find in any SIP dictionary (at least at this moment) because they are only used in openSips.
CDRs accounting
Overview ACC module can now also maintain session/dialog accounting. This allows you to log useful information like call duration, call start time and setup time.
Configuration In order to have CDRs accounting, first you need to set the cdr flag when calling script function for the initial INVITE of the dialog.
How it works This type of accounting is based on the dialog module. When an initial INVITE is received, if the cdr flag is set, then the dialog creation time is saved. Once the call is answered and the ACK is received, other information like extra values or leg values are saved. When the corresponding BYE is received, the call duration is computed and all information is stored to the desired backend.
Dependencies
&osips; Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): tm -- Transaction Manager a database module -- If SQL support is used. rr -- Record Route, if detect_direction module parameter is enabled. an aaa module dialog -- Dialog, if cdr option is used
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none.
Exported Parameters
<varname>early_media</varname> (integer) Should be early media (any provisional reply with body) accounted too ? Default value is 0 (no). early_media example modparam("acc", "early_media", 1)
<varname>report_cancels</varname> (integer) By default, CANCEL reporting is disabled -- most accounting applications wants to see INVITE's cancellation status. Turn on if you explicitly want to account CANCEL transactions. Default value is 0 (no). report_cancels example modparam("acc", "report_cancels", 1)
<varname>detect_direction</varname> (integer) Controls the direction detection for sequential requests. If enabled (non zero value), for sequential requests with upstream direction (from callee to caller), the FROM and TO will be swapped (the direction will be preserved as in the original request). It affects all values related to TO and FROM headers (body, URI, username, domain, TAG). Default value is 0 (disabled). detect_direction example modparam("acc", "detect_direction", 1)
<varname>multi_leg_info</varname> (string) Defines the AVP set to be used in per-call-leg accounting. See for a detailed description of the Multi Call-Legs accounting. If empty, the multi-leg accounting support will be disabled. Default value is 0 (disabled). multi_leg_info example # for syslog-based accounting, use any text you want to be printed modparam("acc", "multi_leg_info", "text1=$avp(src);text2=$avp(dst)") # for mysql-based accounting, use the names of the columns modparam("acc", "multi_leg_info", "leg_src=$avp(src);leg_dst=$avp(dst)") # for AAA-based accounting, use the names of the AAA AVPs modparam("acc", "multi_leg_info", "AAA_LEG_SRC=$avp(src);AAA_LEG_DST=$avp(dst)") # for DIAMETER-based accounting, use the DIAMETER AVP ID (as integer) modparam("acc", "multi_leg_info", "2345=$avp(src);2346=$avp(dst)")
<varname>multi_leg_bye_info</varname> (string) Defines the AVP set to be used in per-call-leg accounting. See for a detailed description of the Multi Call-Legs accounting. This parameter evaluates the AVPs set for BYE requests. It makes sense only when the CDRs are automatically generated, using the dialog support. If empty, the multi-leg support for BYE requests is disabled. Default value is 0 (disabled). multi_leg_bye_info example # for syslog-based accounting, use any text you want to be printed modparam("acc", "multi_leg_bye_info", "text1=$avp(src);text2=$avp(dst)") # for mysql-based accounting, use the names of the columns modparam("acc", "multi_leg_bye_info", "leg_src=$avp(src);leg_dst=$avp(dst)") # for AAA-based accounting, use the names of the AAA AVPs modparam("acc", "multi_leg_bye_info", "AAA_LEG_SRC=$avp(src);AAA_LEG_SRC=$avp(dst)")
<varname>log_level</varname> (integer) Log level at which accounting messages are issued to syslog. Default value is L_NOTICE. log_level example modparam("acc", "log_level", 2) # Set log_level to 2
<varname>log_facility</varname> (string) Log facility to which accounting messages are issued to syslog. This allows to easily seperate the accounting specific logging from the other log messages. Default value is LOG_DAEMON. log_facility example modparam("acc", "log_facility", "LOG_DAEMON")
<varname>log_extra</varname> (string) Extra values to be logged. Default value is NULL. log_extra example modparam("acc", "log_extra", "uaA=$hdr(User-Agent);uaB=$hdr(Server)/reply;uuid=$avp(123)")
<varname>log_extra_bye</varname> (string) Extra values to be logged to logfile. Note that this parameter makes sense only when the cdr option is used with do_accounting(). Default value is NULL. log_extra_bye example modparam("acc", "log_extra_bye", "uaA=$hdr(User-Agent);uaB=$hdr(Server)/reply;uuid=$avp(123)")
<varname>aaa_url</varname> (string) This is the url representing the AAA protocol used and the location of the configuration file of this protocol. If the parameter is set to empty string, the AAA accounting support will be disabled. Default value is NULL. Set <varname>aaa_url</varname> parameter ... modparam("acc", "aaa_url", "radius:/etc/radiusclient-ng/radiusclient.conf") ...
<varname>service_type</varname> (integer) AAA service type used for accounting. Default value is not-set. service_type example # Default value of service type for SIP is 15 modparam("acc", "service_type", 15)
<varname>aaa_extra</varname> (string) Extra values to be logged via AAA - AAA specific. Default value is NULL. aaa_extra example modparam("acc", "aaa_extra", "via=$hdr(Via[*]); email=$avp(email); Bcontact=$ct / reply")
<varname>aaa_extra_bye</varname> (string) Extra values to be logged via AAA when a BYE message is received - AAA specific. Note that this parameter makes sense only when the cdr option is used with do_accounting. Default value is NULL. aaa_extra_bye example modparam("acc", "aaa_extra_bye", "via=$hdr(Via[*]); email=$avp(email); Bcontact=$ct / reply")
<varname>db_table_acc</varname> (string) Table name of accounting successfull calls -- database specific. Default value is acc db_table_acc example modparam("acc", "db_table_acc", "myacc_table")
<varname>db_table_missed_calls</varname> (string) Table name for accounting missed calls -- database specific. Default value is missed_calls db_table_missed_calls example modparam("acc", "db_table_missed_calls", "myMC_table")
<varname>db_url</varname> (string) SQL address -- database specific. If is set to NULL or empty string, the SQL support is disabled. Default value is NULL (SQL disabled). db_url example modparam("acc", "db_url", "mysql://user:password@localhost/opensips")
<varname>acc_method_column</varname> (string) Column name in accounting table to store the request's method name as string. Default value is method. acc_method_column example modparam("acc", "acc_method_column", "method")
<varname>acc_from_tag_column</varname> (string) Column name in accounting table to store the From header TAG parameter. Default value is from_tag. acc_from_tag_column example modparam("acc", "acc_from_tag_column", "from_tag")
<varname>acc_to_tag_column</varname> (string) Column name in accounting table to store the To header TAG parameter. Default value is to_tag. acc_to_tag_column example modparam("acc", "acc_to_tag_column", "to_tag")
<varname>acc_callid_column</varname> (string) Column name in accounting table to store the request's Callid value. Default value is callid. acc_callid_column example modparam("acc", "acc_callid_column", "callid")
<varname>acc_sip_code_column</varname> (string) Column name in accounting table to store the final reply's numeric code value in string format. Default value is sip_code. acc_sip_code_column example modparam("acc", "acc_sip_code_column", "sip_code")
<varname>acc_sip_reason_column</varname> (string) Column name in accounting table to store the final reply's reason phrase value. Default value is sip_reason. acc_sip_reason_column example modparam("acc", "acc_sip_reason_column", "sip_reason")
<varname>acc_time_column</varname> (string) Column name in accounting table to store the time stamp of the transaction completion in date-time format. Default value is time. acc_time_column example modparam("acc", "acc_time_column", "time")
<varname>db_extra</varname> (string) Extra values to be logged into database - DB specific. Default value is NULL. db_extra example modparam("acc", "db_extra", "ct=$hdr(Content-type); email=$avp(email)")
<varname>db_extra_bye</varname> (string) Extra values to be logged into database when a BYE message is received - DB specific. Note that this parameter makes sense only when the cdr option is used with do_accounting. Default value is NULL. db_extra_bye example modparam("acc", "db_extra_bye", "ct=$hdr(Content-type); email=$avp(email)")
<varname>diameter_client_host</varname> (string) Hostname of the machine where the DIAMETER Client is running -- DIAMETER specific. Default value is localhost. diameter_client_host example modparam("acc", "diameter_client_host", "3a_server.net")
<varname>diameter_client_port</varname> (int) Port number where the Diameter Client is listening -- DIAMETER specific. Default value is 3000. diameter_client_host example modparam("acc", "diameter_client_port", 3000)
<varname>diameter_extra</varname> (string) Extra values to be logged via DIAMETER - DIAMETER specific. Default value is NULL. diameter_extra example modparam("acc", "diameter_extra", "7846=$hdr(Content-type);7847=$avp(email)")
<varname>evi_extra</varname> (string) Extra values to be attached as event's parameters. Default value is NULL. evi_extra example modparam("acc", "evi_extra", "uaA=$hdr(User-Agent);uaB=$hdr(Server)/reply;uuid=$avp(123)")
<varname>evi_extra_bye</varname> (string) Extra values to be attached as event's parameters for BYE messages. Note that this parameter makes sense only when the cdr option is used with do_accounting. Default value is NULL. evi_extra_bye example modparam("acc", "evi_extra_bye", "uaA=$hdr(User-Agent);uaB=$hdr(Server)/reply;uuid=$avp(123)")
<varname>acc_created_avp_name</varname> (string) The name of the openSips avp that will be used to hold the time when the call was created. This time will only be logged on missed calls. Default value is "accX_created". acc_created_avp_name example modparam("acc", "acc_created_avp_name", "call_created_avp")
Exported Functions
<function moreinfo="none">do_accounting(type, flags, table)</function> do_accounting() replace all the *_flag and, *_missed_flag, cdr_flag, failed transaction_flag and the db_table_avp modpara. Just call do_accounting(), select where you want to do accounting and how and the function will do all the job for you. Meaning of the parameters is as follows: type - the type of accounting you want to do. All the types have to be separated by '|'. The following parameters can be used: log - syslog accounting; db - database accounting; aaa - aaa specific accounting; evi - Event Interface accounting; diam - Diameter accounting; flags - flags for the accouting type you have selected. All the types have to be separated by '|'. The following parameters can be used: cdr - also set CDR details when doing accounting; DIALOG MODULE HAS TO BE LOADED; missed - log missed calls; failed - flag which says if the transaction should be accounted also in case of failure (status>=300); table - table where to do the accounting; it replaces old table_avp parameter; This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. do_accounting usage ... if (!has_totag()) { if (is_method("INVITE")) { /* enable cdr and missed calls accounting in the database * and to syslog; db accounting shall be done in "my_acc" table */ do_accounting("db|log", "cdr|missed", "my_acc"); } } ... if (is_method("BYE")) { /* do normal accounting via aaa */ do_accounting("aaa"); } ...
<function moreinfo="none">drop_accounting([type], [flags])</function> drop_accounting() resets flags and types of accounting set with do_accounting(). If called with no arguments all accounting will be stopped. If called with only one argument all accounting for that type will be stopped. If called with two arguments normal accounting will still be enabled. Meaning of the parameters is as follows: type - the type of accounting you want to stop. All the types have to be separated by '|'. The following parameters can be used: log - stop syslog accounting; db - stop database accounting; aaa - stop aaa specific accounting; evi - stop Event Interface accounting; diam - stop Diameter accounting; flags - flags to be reset for the accouting type you have selected. All the types have to be separated by '|'. The following parameters can be used: cdr - stop CDR accounting; missed - stop logging missed calls; failed - stop failed transaction accounting; This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. drop_accounting usage ... acc_log_request("403 Destination not allowed"); if (!has_totag()) { if (is_method("INVITE")) { /* enable cdr and missed calls accounting in the database * and to syslog; db accounting shall be done in "my_acc" table */ do_accounting("db|log", "cdr|missed", "my_acc"); } } ... /* later in your script */ if (...) { /* you don't want accounting anymore */ /* stop all syslog accounting */ drop_accounting("log"); /* or stop missed calls and cdr accounting for syslog; * normal accounting will still be enabled */ drop_accounting("log","missed|cdr"); /* or stop all types of accounting */ drop_accounting(); } ...
<function moreinfo="none">acc_log_request(comment)</function> acc_request reports on a request, for example, it can be used to report on missed calls to off-line users who are replied 404 - Not Found. To avoid multiple reports on UDP request retransmission, you would need to embed the action in stateful processing. Meaning of the parameters is as follows: comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. acc_log_request usage ... acc_log_request("403 Destination not allowed"); ...
<function moreinfo="none">acc_db_request(comment, table)</function> Like acc_log_request, acc_db_request reports on a request. The report is sent to database at db_url, in the table referred to in the second action parameter. Meaning of the parameters is as follows: comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. table - Database table to be used. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. acc_db_request usage ... acc_db_request("Some comment", "Some table"); acc_db_request("$T_reply_code $(<reply>rr)","acc"); ...
<function moreinfo="none">acc_aaa_request(comment)</function> Like acc_log_request, acc_aaa_request reports on a request. It reports to aaa server as configured in aaa_url. Meaning of the parameters is as follows: comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. acc_aaa_request usage ... acc_aaa_request("403 Destination not allowed"); ...
<function moreinfo="none">acc_diam_request(comment)</function> Like acc_log_request, acc_diam_request reports on a request. It reports to the configured Diameter server. Meaning of the parameters is as follows: comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. acc_diam_request usage ... acc_diam_request("403 Destination not allowed"); ...
<function moreinfo="none">acc_evi_request(comment)</function> Like acc_log_request, acc_evi_request reports on a request. The report is packed as an event sent through the &osips; Event Interface as E_ACC_EVENT if the reply code is a positive one (lower than 300), or E_ACC_MISSED_EVENT for negative or no codes. More information on this in . Meaning of the parameters is as follows: comment - Comment describing how the request completed - this string has to contain a reply code followed by a reply reason phrase (ex: "404 Nobody home"). Variables are accepted in this string. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. acc_evi_request usage ... acc_evi_request("403 Destination not allowed"); ...
Exported Events
<function moreinfo="none">E_ACC_CDR</function> The event raised when a CDR is generated. Note that this event will only be triggered if the auto CDR accounting is used. Parameters: method - Request method name from_tag - From header tag parameter to_tag - To header tag parameter callid - Message Call-id sip_code - The status code from the final reply sip_reason - The status reason from the final reply time - The timestamp when the call was established evi_extra* - Extra parameters added by the evi_extra parameter. evi_extra_bye* - Extra parameters added by the evi_extra_bye parameter multi_leg_info* - Extra parameters added by the multi_leg_info parameter multi_leg_bye_info* - Extra parameters added by the multi_leg_bye_info parameter duration - The call duration in seconds setuptime - The call setup time in seconds created - The timestamp when the call was created (the initial Invite was received)
<function moreinfo="none">E_ACC_EVENT</function> This event is triggered when old-style accounting is used. It is generated when the requests (INVITE and BYE) transaction have positive final replies, or by the acc_evi_request() function that has a positive reply code in comment. Parameters: method - Request method name from_tag - From header tag parameter to_tag - To header tag parameter callid - Message Call-id sip_code - The status code from the final reply sip_reason - The status reason from the final reply time - The timestamp when the transaction was created evi_extra* - Extra parameters added by the evi_extra parameter multi_leg_info* - Extra parameters added by the multi_leg_info parameter
<function moreinfo="none">E_ACC_MISSED_EVENT</function> This event is triggered when old-style accounting is used. It is generated when the requests (INVITE and BYE) transaction have negative final replies, or by the acc_evi_request() function that has a negative reply code in comment. Parameters: method - Request method name from_tag - From header tag parameter to_tag - To header tag parameter callid - Message Call-id sip_code - The status code from the final reply sip_reason - The status reason from the final reply time - The timestamp when the transaction was created evi_extra* - Extra parameters added by the evi_extra parameter multi_leg_info* - Extra parameters added by the multi_leg_info parameter created - Timestamp when the call was created setuptime - The call setup time in seconds
opensips-2.2.2/modules/acc/doc/acc_faq.xml000066400000000000000000000053131300170765700204270ustar00rootroot00000000000000 &faqguide; What happened with old report_ack parameter The parameter is considered obsolete. It was removed as acc module is doing SIP transaction based accouting and according to SIP RFC, end2end ACKs are a different transaction (still part of the same dialog). ACKs can be individually accouted as any other sequential (in-dialog) request. $ $ $ What happened with old log_fmt parameter The parameter became obsolete with the restructure of the data logged by ACC module (refer to the Overview chapter). For similar behaviour you can use the extra accouting (see the corresponding chapter). What happened with old multi_leg_enabled parameter The parameter became obsolete by the addition of the new multi_leg_info parameter. The multi-leg accouting is automatically enabled when multi_leg_info is defined. What happened with old src_leg_avp_id and dst_leg_avp_id parameters The parameter was replaced by the more generic new parameter multi_leg_info. This allows logging (per-leg) of more information than just dst and src. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/acc/etc/000077500000000000000000000000001300170765700163345ustar00rootroot00000000000000opensips-2.2.2/modules/acc/etc/radiusclient.conf000066400000000000000000000053571300170765700217030ustar00rootroot00000000000000# General settings # specify which authentication comes first respectively which # authentication is used. possible values are: "radius" and "local". # if you specify "radius,local" then the RADIUS server is asked # first then the local one. if only one keyword is specified only # this server is asked. auth_order radius,local # maximum login tries a user has login_tries 4 # timeout for all login tries # if this time is exceeded the user is kicked out login_timeout 60 # name of the nologin file which when it exists disables logins. # it may be extended by the ttyname which will result in # a terminal specific lock (e.g. /etc/nologin.ttyS2 will disable # logins on /dev/ttyS2) nologin /etc/nologin # name of the issue file. it's only display when no username is passed # on the radlogin command line issue /usr/local/etc/radiusclient/issue # RADIUS settings # RADIUS server to use for authentication requests. this config # item can appear more then one time. if multiple servers are # defined they are tried in a round robin fashion if one # server is not answering. # optionally you can specify a the port number on which is remote # RADIUS listens separated by a colon from the hostname. if # no port is specified /etc/services is consulted of the radius # service. if this fails also a compiled in default is used. #authserver localhost authserver localhost # RADIUS server to use for accounting requests. All that I # said for authserver applies, too. # acctserver localhost # file holding shared secrets used for the communication # between the RADIUS client and server servers /home/jiri/sip_router/modules/acc/etc/servers # dictionary of allowed attributes and values # just like in the normal RADIUS distributions dictionary /home/jiri/sip_router/modules/acc/etc/sip_dictionary # program to call for a RADIUS authenticated login login_radius /usr/local/sbin/login.radius # file which holds sequence number for communication with the # RADIUS server #seqfile /var/run/radius.seq # # if opensips don't run as root, this directory should be used # the debian init script for example use this location seqfile /var/run/opensips/opensips_radius.seq # file which specifies mapping between ttyname and NAS-Port attribute mapfile /usr/local/etc/radiusclient/port-id-map # default authentication realm to append to all usernames if no # realm was explicitly specified by the user # the radiusd directly form Livingston doesn't use any realms, so leave # it blank then default_realm # time to wait for a reply from the RADIUS server radius_timeout 10 # resend request this many times before trying the next server radius_retries 3 # LOCAL settings # program to execute for local login # it must support the -f flag for preauthenticated login login_local /bin/login opensips-2.2.2/modules/acc/etc/servers000066400000000000000000000001521300170765700177460ustar00rootroot00000000000000#Server Name or Client/Server pair Key #---------------- --------------- localhost testing123 opensips-2.2.2/modules/alias_db/000077500000000000000000000000001300170765700165715ustar00rootroot00000000000000opensips-2.2.2/modules/alias_db/Makefile000066400000000000000000000003201300170765700202240ustar00rootroot00000000000000# $Id$ # # User aliases # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=alias_db.so LIBS= DEFS+= include ../../Makefile.modules opensips-2.2.2/modules/alias_db/README000066400000000000000000000163341300170765700174600ustar00rootroot00000000000000ALIAS_DB Module Daniel-Constantin Mierla Elena-Ramona Modroiu Edited by Daniel-Constantin Mierla Copyright © 2005-2009 Voice Sistem SRL __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url (str) 1.3.2. user_column (str) 1.3.3. domain_column (str) 1.3.4. alias_user_column (str) 1.3.5. alias_domain_column (str) 1.3.6. domain_prefix (str) 1.3.7. append_branches (int) 1.4. Exported Functions 1.4.1. alias_db_lookup( table_name [,flags] ) 1.4.2. alias_db_find( table_name , input, output [,flags] ) 2. Frequently Asked Questions List of Examples 1.1. Set db_url parameter 1.2. Set user_column parameter 1.3. Set domain_column parameter 1.4. Set alias_user_column parameter 1.5. Set alias_domain_column parameter 1.6. Set domain_prefix parameter 1.7. Set append_branches parameter 1.8. alias_db_lookup() usage 1.9. alias_db_find() usage Chapter 1. Admin Guide 1.1. Overview ALIAS_DB module can be used as an alternative for user aliases via usrloc. The main feature is that it does not store all adjacent data as for user location and always uses database for search (no memory caching). Having no memory caching, search speed might decrease but provisioning is easier. With very fast databases like MySQL, speed penalty can be lowered. Also, search can be performed on different tables in the same script. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * database module (mysql, dbtext, ...). 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. db_url (str) Database URL. Default value is “mysql://opensipsro:opensipsro@localhost/opensipsâ€. Example 1.1. Set db_url parameter ... modparam("alias_db", "db_url", "dbdriver://username:password@dbhost/dbna me") ... 1.3.2. user_column (str) Name of the column storing username. Default value is “usernameâ€. Example 1.2. Set user_column parameter ... modparam("alias_db", "user_column", "susername") ... 1.3.3. domain_column (str) Name of the column storing user's domain. Default value is “domainâ€. Example 1.3. Set domain_column parameter ... modparam("alias_db", "domain_column", "sdomain") ... 1.3.4. alias_user_column (str) Name of the column storing alias username. Default value is “alias_usernameâ€. Example 1.4. Set alias_user_column parameter ... modparam("alias_db", "alias_user_column", "auser") ... 1.3.5. alias_domain_column (str) Name of the column storing alias domain. Default value is “alias_domainâ€. Example 1.5. Set alias_domain_column parameter ... modparam("alias_db", "alias_domain_column", "adomain") ... 1.3.6. domain_prefix (str) Specifies the prefix to be stripped from the domain in R-URI before doing the search. Default value is “NULLâ€. Example 1.6. Set domain_prefix parameter ... modparam("alias_db", "domain_prefix", "sip.") ... 1.3.7. append_branches (int) If the alias resolves to many SIP IDs, the first is replacing the R-URI, the rest are added as branches. Default value is “0†(0 - don't add branches; 1 - add branches). Example 1.7. Set append_branches parameter ... modparam("alias_db", "append_branches", 1) ... 1.4. Exported Functions 1.4.1. alias_db_lookup( table_name [,flags] ) The function takes the R-URI and search to see whether it is an alias or not. If it is an alias for a local user, the R-URI is replaced with user's SIP uri. The function returns TRUE if R-URI is alias and it was replaced by user's SIP uri. Meaning of the parameters is as follows: * table_name - any PV (string or PV or mix) the name of the table where to search for alias. * flags (optional) - set of flags (char based flags) to control the alias lookup process: + d - do not use domain URI part in the alias lookup query (use only a username-based lookup). By default, both username and domain are used. + r - do revers alias lookup - lookup for the alias mapped to the current URI (URI 2 alias translation); normally, the function looks up for the URI mapped to the alias (alias 2 URI translation). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.8. alias_db_lookup() usage ... alias_db_lookup("dbaliases" , "rd"); alias_db_lookup("dba_$(rU{s.substr,0,1})"); ... 1.4.2. alias_db_find( table_name , input, output [,flags] ) The function is very similar to alias_db_lookup(), but instead of using fixed input (RURI) and output (RURI) is able to get the input SIP URI from a pseudo-variable and place the result back also in a pseudo-variable. The function is useful as the alias lookup does not affect the request itself (no RURI changes), can be used in a reply context (as it does not work with RURI only) and can be used for others URI than the RURI (To URI, From URI, custom URI). The function returns TRUE if any alias mapping was found and returned. Meaning of the parameters is as follows: * table_name - any PV (string or PV or mix) the name of the table where to search for alias. * input - any PV (string or PV or mix) carrying the SIP URI that needs to be looked up. * output - PV (AVP or script VAR) where to place the SIP URI resulting from the alias lookup. * flags (optional) - set of flags (char based flags) to control the alias lookup process: + d - do not use domain URI part in the alias lookup query (use only a username-based lookup). By default, both username and domain are used. + r - do revers alias lookup - lookup for the alias mapped to the current URI (URI 2 alias translation); normally, the function looks up for the URI mapped to the alias (alias 2 URI translation). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. Example 1.9. alias_db_find() usage ... # do revers alias lookup and find the alias for the FROM URI alias_db_find("dbaliases" , "$fu", "$avp(from_alias)", "r"); ... Chapter 2. Frequently Asked Questions 2.1. What happened with old use_domain parameter The global parameter (affecting the entire module) was replaced with a per lookup parameter (affecting only current lookup). See the "d" (do not used domain part) flag in the db_alias_lookup() and db_alias_find() functions. 2.2. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/alias_db/alias_db.c000066400000000000000000000145001300170765700204730ustar00rootroot00000000000000/* * ALIAS_DB Module * * Copyright (C) 2004-2009 Voice Sistem SRL * * This file is part of a module for opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-09-01: first version (ramona) * 2009-04-30: alias_db_find() added; NO_DOMAIN and REVERT flags added; * use_domain param removed (bogdan) */ #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../mod_fix.h" #include "alookup.h" /* Module destroy function prototype */ static void destroy(void); /* Module child-init function prototype */ static int child_init(int rank); /* Module initialization function prototype */ static int mod_init(void); /* Fixup function */ static int lookup_fixup(void** param, int param_no); static int find_fixup(void** param, int param_no); /* Module parameter variables */ static str db_url = {NULL,0}; str user_column = str_init("username"); str domain_column = str_init("domain"); str alias_user_column = str_init("alias_username"); str alias_domain_column = str_init("alias_domain"); str domain_prefix = {NULL, 0}; int ald_append_branches = 0; db_con_t* db_handle; /* Database connection handle */ db_func_t adbf; /* DB functions */ /* Exported functions */ static cmd_export_t cmds[] = { {"alias_db_lookup", (cmd_function)alias_db_lookup, 1, lookup_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"alias_db_lookup", (cmd_function)alias_db_lookup, 2, lookup_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"alias_db_find", (cmd_function)alias_db_find, 3, find_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE}, {"alias_db_find", (cmd_function)alias_db_find, 4, find_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &db_url.s }, {"user_column", STR_PARAM, &user_column.s }, {"domain_column", STR_PARAM, &domain_column.s }, {"alias_user_column", STR_PARAM, &alias_user_column.s }, {"alias_domain_column", STR_PARAM, &alias_domain_column.s }, {"domain_prefix", STR_PARAM, &domain_prefix.s }, {"append_branches", INT_PARAM, &ald_append_branches }, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* Module interface */ struct module_exports exports = { "alias_db", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; static int alias_flags_fixup(void** param) { char *c; unsigned int flags; c = (char*)*param; flags = 0; while (*c) { switch (*c) { case 'r': case 'R': flags |= ALIAS_REVERT_FLAG; break; case 'd': case 'D': flags |= ALIAS_NO_DOMAIN_FLAG; break; default: LM_ERR("unsupported flag '%c'\n",*c); return -1; } c++; } pkg_free(*param); *param = (void*)(unsigned long)flags; return 0; } static int lookup_fixup(void** param, int param_no) { if (param_no==1) { /* string or pseudo-var - table name */ return fixup_spve(param); } else if (param_no==2) { /* string - flags ? */ return alias_flags_fixup(param); } else { LM_CRIT(" invalid number of params %d \n",param_no); return -1; } } static int find_fixup(void** param, int param_no) { pv_spec_t *sp; if (param_no==1) { /* string or pseudo-var - table name */ return fixup_spve(param); } else if(param_no==2) { /* string or pseudo-var - source URI */ return fixup_spve(param); } else if(param_no==3) { /* pvar (AVP or VAR) - destination URI */ if (fixup_pvar(param)) return E_CFG; sp = (pv_spec_t*)*param; if (sp->setf==NULL) { LM_ERR("PV type %d (param 3) cannot be written\n", sp->type); pv_spec_free(sp); return E_CFG; } return 0; } else if (param_no==4) { /* string - flags ? */ return alias_flags_fixup(param); } else { LM_CRIT(" invalid number of params %d \n",param_no); return -1; } } /** * */ static int child_init(int rank) { db_handle = adbf.init(&db_url); if (!db_handle) { LM_ERR("unable to connect database\n"); return -1; } return 0; } /** * */ static int mod_init(void) { LM_INFO("initializing...\n"); init_db_url( db_url , 0 /*cannot be null*/); user_column.len = strlen(user_column.s); domain_column.len = strlen(domain_column.s); alias_domain_column.len = strlen(alias_domain_column.s); alias_user_column.len = strlen(alias_user_column.s); if (domain_prefix.s) domain_prefix.len = strlen(domain_prefix.s); /* Find a database module */ if (db_bind_mod(&db_url, &adbf)) { LM_ERR("unable to bind database module\n"); return -1; } if (!DB_CAPABILITY(adbf, DB_CAP_QUERY)) { LM_CRIT("database modules does not " "provide all functions needed by alias_db module\n"); return -1; } return 0; } /** * */ static void destroy(void) { if (db_handle) { adbf.close(db_handle); db_handle = 0; } } opensips-2.2.2/modules/alias_db/alias_db.h000066400000000000000000000031201300170765700204740ustar00rootroot00000000000000/* * ALIAS_DB Module * * Copyright (C) 2004 Voice Sistem * * This file is part of a module for opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-09-01: first version (ramona) * 2009-04-30: alias_db_find() added; NO_DOMAIN and REVERT flags added; * use_domain param removed (bogdan) */ #ifndef _ALIAS_DB_H_ #define _ALIAS_DB_H_ #include "../../db/db.h" #include "../../parser/msg_parser.h" /* Module parameters variables */ extern str user_column; /* 'username' column name */ extern str domain_column; /* 'domain' column name */ extern str alias_user_column; /* 'alias_username' column name */ extern str alias_domain_column; /* 'alias_domain' column name */ extern str domain_prefix; extern int ald_append_branches; /* append branches after an alias lookup */ extern db_con_t* db_handle; /* Database connection handle */ #endif /* _ALIAS_DB_H_ */ opensips-2.2.2/modules/alias_db/alookup.c000066400000000000000000000167751300170765700204270ustar00rootroot00000000000000/* * ALIAS_DB Module * * Copyright (C) 2004-2009 Voice Sistem SRL * * This file is part of a module for opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-09-01: first version (ramona) * 2009-04-30: alias_db_find() added; NO_DOMAIN and REVERT flags added; * use_domain param removed (bogdan) */ #include #include "../../dprint.h" #include "../../action.h" #include "../../config.h" #include "../../ut.h" #include "../../parser/parse_uri.h" #include "../../db/db.h" #include "../../mod_fix.h" #include "../../dset.h" #include "alias_db.h" #include "alookup.h" #define MAX_USERURI_SIZE 256 extern db_func_t adbf; /* DB functions */ char useruri_buf[MAX_USERURI_SIZE]; typedef int (*set_alias_f)(struct sip_msg* _msg, str *alias, int no, void *p); /** * */ static int alias_db_query(struct sip_msg* _msg, char* _table, struct sip_uri *puri, unsigned long flags, set_alias_f set_alias, void *param) { static db_ps_t my_ps[4] = {NULL,NULL,NULL,NULL}; str user_s, table_s; db_key_t db_keys[2]; db_val_t db_vals[2]; db_key_t db_cols[2]; db_res_t* db_res = NULL; int i; int ps_idx=0; if(_table==NULL || fixup_get_svalue(_msg, (gparam_p)_table, &table_s)!=0) { LM_ERR("invalid table parameter\n"); return -1; } if (flags&ALIAS_REVERT_FLAG) { /* revert lookup: user->alias */ db_keys[0] = &user_column; db_keys[1] = &domain_column; db_cols[0] = &alias_user_column; db_cols[1] = &alias_domain_column; ps_idx += 2; } else { /* rnormal lookup: alias->user */ db_keys[0] = &alias_user_column; db_keys[1] = &alias_domain_column; db_cols[0] = &user_column; db_cols[1] = &domain_column; } db_vals[0].type = DB_STR; db_vals[0].nul = 0; db_vals[0].val.str_val.s = puri->user.s; db_vals[0].val.str_val.len = puri->user.len; if ( (flags&ALIAS_NO_DOMAIN_FLAG)==0 ) { db_vals[1].type = DB_STR; db_vals[1].nul = 0; db_vals[1].val.str_val.s = puri->host.s; db_vals[1].val.str_val.len = puri->host.len; if (domain_prefix.s && domain_prefix.len>0 && domain_prefix.lenhost.len && strncasecmp(puri->host.s,domain_prefix.s, domain_prefix.len)==0) { db_vals[1].val.str_val.s += domain_prefix.len; db_vals[1].val.str_val.len -= domain_prefix.len; } ps_idx ++; } adbf.use_table(db_handle, &table_s); if (!ald_append_branches) CON_PS_REFERENCE(db_handle) = my_ps[ps_idx]; if(adbf.query( db_handle, db_keys, NULL, db_vals, db_cols, (flags&ALIAS_NO_DOMAIN_FLAG)?1:2 /*no keys*/, 2 /*no cols*/, NULL, &db_res)!=0) { LM_ERR("failed to query database\n"); goto err_server; } if (db_res == NULL || RES_ROW_N(db_res)<=0 || RES_ROWS(db_res)[0].values[0].nul != 0) { LM_DBG("no alias found for R-URI\n"); if (db_res!=NULL && adbf.free_result(db_handle, db_res) < 0) LM_DBG("failed to freeing result of query\n"); return -1; } memcpy(useruri_buf, "sip:", 4); for(i=0; iparsed_uri,(unsigned long)flags, set_alias_to_ruri, NULL); } static inline int set_alias_to_pvar(struct sip_msg* _msg, str *alias, int no, void *p) { pv_value_t val; pv_spec_t *pvs=(pv_spec_t*)p; if(no && !ald_append_branches) return 0; /* set the PVAR */ val.flags = PV_VAL_STR; val.ri = 0; val.rs = *alias; if(pv_set_value(_msg, pvs, (int)(no?EQ_T:COLONEQ_T), &val)<0) { LM_ERR("setting PV AVP failed\n"); return -1; } return 0; } int alias_db_find(struct sip_msg* _msg, char* _table, char* _in, char* _out, char* flags) { str _in_s; struct sip_uri puri; /* get the input value */ if(_in==NULL || fixup_get_svalue(_msg, (gparam_p)_in, &_in_s)!=0) { LM_ERR("invalid input parameter\n"); return -1; } if (parse_uri(_in_s.s, _in_s.len, &puri)<0) { LM_ERR("failed to parse uri %.*s\n",_in_s.len,_in_s.s); return -1; } return alias_db_query(_msg, _table, &puri, (unsigned long)flags, set_alias_to_pvar, _out); } opensips-2.2.2/modules/alias_db/alookup.h000066400000000000000000000025601300170765700204170ustar00rootroot00000000000000/* * ALIAS_DB Module * * Copyright (C) 2004 Voice Sistem SRL * * This file is part of a module for opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-09-01: first version (ramona) * 2009-04-30: alias_db_find() added; NO_DOMAIN and REVERT flags added; * use_domain param removed (bogdan) */ #ifndef _ALOOKUP_H_ #define _ALOOKUP_H_ #include "../../parser/msg_parser.h" #define ALIAS_REVERT_FLAG (1<<0) #define ALIAS_NO_DOMAIN_FLAG (1<<1) int alias_db_lookup(struct sip_msg* _msg, char* _table, char* use_domain); int alias_db_find(struct sip_msg* _msg, char* _table, char* _in, char* _out, char* use_domain); #endif /* _ALOOKUP_H_ */ opensips-2.2.2/modules/alias_db/doc/000077500000000000000000000000001300170765700173365ustar00rootroot00000000000000opensips-2.2.2/modules/alias_db/doc/alias_db.xml000066400000000000000000000021031300170765700216120ustar00rootroot00000000000000 %docentities; ]> ALIAS_DB Module &osipsname; Daniel-Constantin Mierla team@voice-system.ro Elena-Ramona Modroiu team@voice-system.ro Daniel-Constantin Mierla team@voice-system.ro 2005-2009 &voicesystem; &admin; &faq; opensips-2.2.2/modules/alias_db/doc/alias_db_admin.xml000066400000000000000000000210331300170765700227650ustar00rootroot00000000000000 &adminguide;
Overview ALIAS_DB module can be used as an alternative for user aliases via usrloc. The main feature is that it does not store all adjacent data as for user location and always uses database for search (no memory caching). Having no memory caching, search speed might decrease but provisioning is easier. With very fast databases like MySQL, speed penalty can be lowered. Also, search can be performed on different tables in the same script.
Dependencies
&osips; Modules The following modules must be loaded before this module: database module (mysql, dbtext, ...).
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>db_url</varname> (str) Database URL. Default value is &defaultrodb;. Set <varname>db_url</varname> parameter ... modparam("alias_db", "db_url", "&exampledb;") ...
<varname>user_column</varname> (str) Name of the column storing username. Default value is username. Set <varname>user_column</varname> parameter ... modparam("alias_db", "user_column", "susername") ...
<varname>domain_column</varname> (str) Name of the column storing user's domain. Default value is domain. Set <varname>domain_column</varname> parameter ... modparam("alias_db", "domain_column", "sdomain") ...
<varname>alias_user_column</varname> (str) Name of the column storing alias username. Default value is alias_username. Set <varname>alias_user_column</varname> parameter ... modparam("alias_db", "alias_user_column", "auser") ...
<varname>alias_domain_column</varname> (str) Name of the column storing alias domain. Default value is alias_domain. Set <varname>alias_domain_column</varname> parameter ... modparam("alias_db", "alias_domain_column", "adomain") ...
<varname>domain_prefix</varname> (str) Specifies the prefix to be stripped from the domain in R-URI before doing the search. Default value is NULL. Set <varname>domain_prefix</varname> parameter ... modparam("alias_db", "domain_prefix", "sip.") ...
<varname>append_branches</varname> (int) If the alias resolves to many SIP IDs, the first is replacing the R-URI, the rest are added as branches. Default value is 0 (0 - don't add branches; 1 - add branches). Set <varname>append_branches</varname> parameter ... modparam("alias_db", "append_branches", 1) ...
Exported Functions
<function moreinfo="none">alias_db_lookup( table_name [,flags] )</function> The function takes the R-URI and search to see whether it is an alias or not. If it is an alias for a local user, the R-URI is replaced with user's SIP uri. The function returns TRUE if R-URI is alias and it was replaced by user's SIP uri. Meaning of the parameters is as follows: table_name - any PV (string or PV or mix) the name of the table where to search for alias. flags (optional) - set of flags (char based flags) to control the alias lookup process: d - do not use domain URI part in the alias lookup query (use only a username-based lookup). By default, both username and domain are used. r - do revers alias lookup - lookup for the alias mapped to the current URI (URI 2 alias translation); normally, the function looks up for the URI mapped to the alias (alias 2 URI translation). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>alias_db_lookup()</function> usage ... alias_db_lookup("dbaliases" , "rd"); alias_db_lookup("dba_$(rU{s.substr,0,1})"); ...
<function moreinfo="none">alias_db_find( table_name , input, output [,flags] )</function> The function is very similar to alias_db_lookup(), but instead of using fixed input (RURI) and output (RURI) is able to get the input SIP URI from a pseudo-variable and place the result back also in a pseudo-variable. The function is useful as the alias lookup does not affect the request itself (no RURI changes), can be used in a reply context (as it does not work with RURI only) and can be used for others URI than the RURI (To URI, From URI, custom URI). The function returns TRUE if any alias mapping was found and returned. Meaning of the parameters is as follows: table_name - any PV (string or PV or mix) the name of the table where to search for alias. input - any PV (string or PV or mix) carrying the SIP URI that needs to be looked up. output - PV (AVP or script VAR) where to place the SIP URI resulting from the alias lookup. flags (optional) - set of flags (char based flags) to control the alias lookup process: d - do not use domain URI part in the alias lookup query (use only a username-based lookup). By default, both username and domain are used. r - do revers alias lookup - lookup for the alias mapped to the current URI (URI 2 alias translation); normally, the function looks up for the URI mapped to the alias (alias 2 URI translation). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. <function>alias_db_find()</function> usage ... # do revers alias lookup and find the alias for the FROM URI alias_db_find("dbaliases" , "$fu", "$avp(from_alias)", "r"); ...
opensips-2.2.2/modules/alias_db/doc/alias_db_faq.xml000066400000000000000000000013161300170765700224460ustar00rootroot00000000000000 &faqguide; What happened with old use_domain parameter The global parameter (affecting the entire module) was replaced with a per lookup parameter (affecting only current lookup). See the "d" (do not used domain part) flag in the db_alias_lookup() and db_alias_find() functions. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/auth/000077500000000000000000000000001300170765700157745ustar00rootroot00000000000000opensips-2.2.2/modules/auth/Makefile000066400000000000000000000003241300170765700174330ustar00rootroot00000000000000# $Id$ # # auth example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=auth.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/auth/README000066400000000000000000000437031300170765700166630ustar00rootroot00000000000000Auth Module Jan Janak FhG Fokus Juha Heinanen Song Networks Bogdan-Andrei Iancu Edited by Jan Janak Copyright © 2002, 2003 FhG FOKUS Copyright © 2005 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Nonce Security 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. secret (string) 1.4.2. nonce_expire (integer) 1.4.3. rpid_prefix (string) 1.4.4. rpid_suffix (string) 1.4.5. realm_prefix (string) 1.4.6. rpid_avp (string) 1.4.7. username_spec (string) 1.4.8. password_spec (string) 1.4.9. calculate_ha1 (integer) 1.4.10. disable_nonce_check (int) 1.5. Exported Functions 1.5.1. www_challenge(realm, qop) 1.5.2. proxy_challenge(realm, qop) 1.5.3. consume_credentials() 1.5.4. is_rpid_user_e164() 1.5.5. append_rpid_hf() 1.5.6. append_rpid_hf(prefix, suffix) 1.5.7. pv_www_authorize(realm) 1.5.8. pv_proxy_authorize(realm) List of Examples 1.1. secret parameter example 1.2. nonce_expire parameter example 1.3. rpid_prefix parameter example 1.4. rpid_suffix parameter example 1.5. realm_prefix parameter example 1.6. rpid_avp parameter example 1.7. username_spec parameter usage 1.8. password_spec parameter usage 1.9. calculate_ha1 parameter usage 1.10. disable_nonce_check parameter usage 1.11. www_challenge usage 1.12. proxy_challenge usage 1.13. consume_credentials example 1.14. is_rpid_user_e164 usage 1.15. append_rpid_hf usage 1.16. append_rpid_hf(prefix, suffix) usage 1.17. pv_www_authorize usage 1.18. pv_proxy_authorize usage Chapter 1. Admin Guide 1.1. Overview This is a module that provides common functions that are needed by other authentication related modules. Also, it can perform authentication taking username and password from pseudo-variables. 1.2. Nonce Security The authentication mechanism offers protection against sniffing intrusion. The module generates and verifies the nonces so that they can be used only once (in an auth response). This is done by having a lifetime value and an index associated with every nonce. Using only an expiration value is not good enough because,as this value has to be of few tens of seconds, it is possible for someone to sniff on the network, get the credentials and then reuse them in another packet with which to register a different contact or make calls using the others's account. The index ensures that this will never be possible since it is generated as unique through the lifetime of the nonce. The default limit for the requests that can be authenticated is 100000 in 30 seconds. If you wish to adjust this you can decrease the lifetime of a nonce( how much time to wait for a reply to a challenge). However, be aware not to set it to a too smaller value. However this mechanism does not work for architectures using a cluster of servers that share the same dns name for load balancing. In this case you can disable the nonce reusability check by setting the module parameter 'disable_nonce_check'. 1.3. Dependencies 1.3.1. OpenSIPS Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): * signaling -- Signaling module 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.4. Exported Parameters 1.4.1. secret (string) Secret phrase used to calculate the nonce value. The default is to use a random value generated from the random source in the core. If you use multiple servers in your installation, and would like to authenticate on the second server against the nonce generated at the first one its necessary to explicitly set the secret to the same value on all servers. However, the use of a shared (and fixed) secret as nonce is insecure, much better is to stay with the default. Any clients should send the reply to the server that issued the request. Example 1.1. secret parameter example modparam("auth", "secret", "johndoessecretphrase") 1.4.2. nonce_expire (integer) Nonces have limited lifetime. After a given period of time nonces will be considered invalid. This is to protect replay attacks. Credentials containing a stale nonce will be not authorized, but the user agent will be challenged again. This time the challenge will contain stale parameter which will indicate to the client that it doesn't have to disturb user by asking for username and password, it can recalculate credentials using existing username and password. The value is in seconds and default value is 30 seconds. Example 1.2. nonce_expire parameter example modparam("auth", "nonce_expire", 15) # Set nonce_expire to 15s 1.4.3. rpid_prefix (string) Prefix to be added to Remote-Party-ID header field just before the URI returned from either radius or database. Default value is “â€. Example 1.3. rpid_prefix parameter example modparam("auth", "rpid_prefix", "Whatever <") 1.4.4. rpid_suffix (string) Suffix to be added to Remote-Party-ID header field after the URI returned from either radius or database. Default value is “;party=calling;id-type=subscriber;screen=yesâ€. Example 1.4. rpid_suffix parameter example modparam("auth", "rpid_suffix", "@1.2.3.4>") 1.4.5. realm_prefix (string) Prefix to be automatically strip from realm. As an alternative to SRV records (not all SIP clients support SRV lookup), a subdomain of the master domain can be defined for SIP purposes (like sip.mydomain.net pointing to same IP address as the SRV record for mydomain.net). By ignoring the realm_prefix “sip.â€, at authentication, sip.mydomain.net will be equivalent to mydomain.net . Default value is empty string. Example 1.5. realm_prefix parameter example modparam("auth", "realm_prefix", "sip.") 1.4.6. rpid_avp (string) Full AVP specification for the AVP which stores the RPID value. It used to transport the RPID value from authentication backend modules (auth_db or auth_radius) or from script to the auth function append_rpid_hf and is_rpid_user_e164. If defined to NULL string, all RPID functions will fail at runtime. Default value is “$avp(rpid)â€. Example 1.6. rpid_avp parameter example modparam("auth", "rpid_avp", "$avp(13)") 1.4.7. username_spec (string) This name of the pseudo-variable that will hold the username. Default value is “NULLâ€. Example 1.7. username_spec parameter usage modparam("auth", "username_spec", "$var(username)") 1.4.8. password_spec (string) This name of the pseudo-variable that will hold the password. Default value is “NULLâ€. Example 1.8. password_spec parameter usage modparam("auth", "password_spec", "$avp(password)") 1.4.9. calculate_ha1 (integer) This parameter tells the server whether it should expect plaintext passwords in the pseudo-variable or a pre-calculated HA1 string. If the parameter is set to 1 then the server will assume that the “password_spec†pseudo-variable contains plaintext passwords and it will calculate HA1 strings on the fly. If the parameter is set to 0 then the server assumes the pseudo-variable contains the HA1 strings directly and will not calculate them. Default value of this parameter is 0. Example 1.9. calculate_ha1 parameter usage modparam("auth", "calculate_ha1", 1) 1.4.10. disable_nonce_check (int) By setting this parameter you disable the security mechanism that protects against intrusion sniffing and does not allow nonces to be reused. But, because of the current implementation, having this enabled breaks auth for an architecture where load is balanced by having more servers with the same dns name. This parameter has to be set in this case. Default value is “0†(enabled). Example 1.10. disable_nonce_check parameter usage modparam("auth", "disable_nonce_check", 1) 1.5. Exported Functions 1.5.1. www_challenge(realm, qop) The function challenges a user agent. It will generate a WWW-Authorize header field containing a digest challenge, it will put the header field into a response generated from the request the server is processing and will send the reply. Upon reception of such a reply the user agent should compute credentials and retry the request. For more information regarding digest authentication see RFC2617. Meaning of the parameters is as follows: * realm - Realm is an opaque string that the user agent should present to the user so it can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string Ҡis used then the server will generate it from the request. In case of REGISTER request's To header field, domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. The string may contain pseudo variables. * qop - Value of this parameter can be either “1†or “0â€. When set to 1 then the server will put a qop parameter in the challenge. When set to 0 then the server will not put the qop parameter in the challenge. It is recommended to use the qop parameter, however there are still some user agents that cannot handle qop properly so we made this optional. On the other hand there are still some user agents that cannot handle request without a qop parameter too. Enabling this parameter does not improve security at the moment, because the sequence number is not stored and therefore could not be checked. Actually there is no information kept by the module during the challenge and response requests. This function can be used from REQUEST_ROUTE. Example 1.11. www_challenge usage ... if (!www_authorize("siphub.net", "subscriber")) { www_challenge("siphub.net", "1"); }; ... 1.5.2. proxy_challenge(realm, qop) The function challenges a user agent. It will generate a Proxy-Authorize header field containing a digest challenge, it will put the header field into a response generated from the request the server is processing and will send the reply. Upon reception of such a reply the user agent should compute credentials and retry the request. For more information regarding digest authentication see RFC2617. Meaning of the parameters is as follows: * realm - Realm is an opaque string that the user agent should present to the user so it can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string Ҡis used then the server will generate it from the request. “From†header field domain will be used as realm. The string may contain pseudo variables. * qop - Value of this parameter can be either “1†or “0â€. When set to 1 then the server will put a qop parameter in the challenge. When set to 0 then the server will not put the qop parameter in the challenge. It is recommended to use the qop parameter, however there are still some user agents that cannot handle qop properly so we made this optional. On the other hand there are still some user agents that cannot handle request without a qop parameter too. Enabling this parameter don't improve the security at the moment, because the sequence number is not stored and therefore could not be checked. Actually there is no information kept by the module during the challenge and response requests. This function can be used from REQUEST_ROUTE. Example 1.12. proxy_challenge usage ... if (!proxy_authorize("", "subscriber)) { proxy_challenge("", "1"); # Realm will be autogenerated }; ... 1.5.3. consume_credentials() This function removes previously authorized credentials from the message being processed by the server. That means that the downstream message will not contain credentials there were used by this server. This ensures that the proxy will not reveal information about credentials used to downstream elements and also the message will be a little bit shorter. The function must be called after www_authorize or proxy_authorize. This function can be used from REQUEST_ROUTE. Example 1.13. consume_credentials example ... if (www_authorize("", "subscriber)) { consume_credentials(); }; ... 1.5.4. is_rpid_user_e164() The function checks if the SIP URI received from the database or radius server and will potentially be used in Remote-Party-ID header field contains an E164 number (+followed by up to 15 decimal digits) in its user part. Check fails, if no such SIP URI exists (i.e. radius server or database didn't provide this information). This function can be used from REQUEST_ROUTE. Example 1.14. is_rpid_user_e164 usage ... if (is_rpid_user_e164()) { # do something here }; ... 1.5.5. append_rpid_hf() Appends to the message a Remote-Party-ID header that contains header 'Remote-Party-ID: ' followed by the saved value of the SIP URI received from the database or radius server followed by the value of module parameter radius_rpid_suffix. The function does nothing if no saved SIP URI exists. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.15. append_rpid_hf usage ... append_rpid_hf(); # Append Remote-Party-ID header field ... 1.5.6. append_rpid_hf(prefix, suffix) This function is the same as Section 1.5.5, “ append_rpid_hf()â€. The only difference is that it accepts two parameters--prefix and suffix to be added to Remote-Party-ID header field. This function ignores rpid_prefix and rpid_suffix parameters, instead of that allows to set them in every call. Meaning of the parameters is as follows: * prefix - Prefix of the Remote-Party-ID URI. The string will be added at the beginning of body of the header field, just before the URI. * suffix - Suffix of the Remote-Party-ID header field. The string will be appended at the end of the header field. It can be used to set various URI parameters, for example. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.16. append_rpid_hf(prefix, suffix) usage ... # Append Remote-Party-ID header field append_rpid_hf("", ";party=calling;id-type=subscriber;screen=yes"); ... 1.5.7. pv_www_authorize(realm) The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call www_challenge which will challenge the user again. Negative codes may be interpreted as follows: * -5 (generic error) - some generic error occurred and no reply was sent out; * -4 (no credentials) - credentials were not found in request; * -3 (stale nonce) - stale nonce; * -2 (invalid password) - valid user, but wrong password; * -1 (invalid user) - authentication user does not exist. Meaning of the parameters is as follows: * realm - Realm is an opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string Ҡis used then the server will generate it from the request. In case of REGISTER requests To header field domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. The string may contain pseudo variables. This function can be used from REQUEST_ROUTE. Example 1.17. pv_www_authorize usage ... $var(username)="abc"; $avp(password)="xyz"; if (!pv_www_authorize("opensips.org")) { www_challenge("opensips.org", "1"); }; ... 1.5.8. pv_proxy_authorize(realm) The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call proxy_challenge which will challenge the user again. For more about the negative return codes, see the above function. Meaning of the parameters is as follows: * realm - Realm is an opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string Ҡis used then the server will generate it from the request. From header field domain will be used as realm. The string may contain pseudo variables. This function can be used from REQUEST_ROUTE. Example 1.18. pv_proxy_authorize usage ... $var(username)="abc"; $avp(password)="xyz"; if (!pv_proxy_authorize("")) { proxy_challenge("", "1"); # Realm will be autogenerated }; ... opensips-2.2.2/modules/auth/api.c000066400000000000000000000170171300170765700167170ustar00rootroot00000000000000/* * Digest Authentication Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "../../dprint.h" #include "../../parser/digest/digest.h" #include "../../sr_module.h" #include "../../str.h" #include "../../ut.h" #include "auth_mod.h" #include "nonce.h" #include "common.h" #include "api.h" #include "rpid.h" #include "index.h" static str auth_400_err = str_init(MESSAGE_400); static str auth_500_err = str_init(MESSAGE_500); /* * if realm determined from request, look if there are some * modification rules */ void strip_realm(str* _realm) { /* no param defined -- return */ if (!realm_prefix.len) return; /* prefix longer than realm -- return */ if (realm_prefix.len > _realm->len) return; /* match ? -- if so, shorten realm -*/ if (memcmp(realm_prefix.s, _realm->s, realm_prefix.len) == 0) { _realm->s += realm_prefix.len; _realm->len -= realm_prefix.len; } return; } /* * Find credentials with given realm in a SIP message header */ static inline int find_credentials(struct sip_msg* _m, str* _realm, hdr_types_t _hftype, struct hdr_field** _h) { struct hdr_field** hook, *ptr, *prev; hdr_flags_t hdr_flags; int res; str* r; /* * Determine if we should use WWW-Authorization or * Proxy-Authorization header fields, this parameter * is set in www_authorize and proxy_authorize */ switch(_hftype) { case HDR_AUTHORIZATION_T: hook = &(_m->authorization); hdr_flags=HDR_AUTHORIZATION_F; break; case HDR_PROXYAUTH_T: hook = &(_m->proxy_auth); hdr_flags=HDR_PROXYAUTH_F; break; default: hook = &(_m->authorization); hdr_flags=HDR_T2F(_hftype); break; } /* * If the credentials haven't been parsed yet, do it now */ if (*hook == 0) { /* No credentials parsed yet */ if (parse_headers(_m, hdr_flags, 0) == -1) { LM_ERR("failed to parse headers\n"); return -1; } } ptr = *hook; /* * Iterate through the credentials in the message and * find credentials with given realm */ while(ptr) { res = parse_credentials(ptr); if (res < 0) { LM_ERR("failed to parse credentials\n"); return (res == -1) ? -2 : -3; } else if (res == 0) { r = &(((auth_body_t*)(ptr->parsed))->digest.realm); if (r->len == _realm->len) { if (!strncasecmp(_realm->s, r->s, r->len)) { *_h = ptr; return 0; } } } prev = ptr; if (parse_headers(_m, hdr_flags, 1) == -1) { LM_ERR("failed to parse headers\n"); return -4; } else { if (prev != _m->last_header) { if (_m->last_header->type == _hftype) ptr = _m->last_header; else break; } else break; } } /* * Credentials with given realm not found */ return 1; } /* * Purpose of this function is to find credentials with given realm, * do sanity check, validate credential correctness and determine if * we should really authenticate (there must be no authentication for * ACK and CANCEL */ auth_result_t pre_auth(struct sip_msg* _m, str* _realm, hdr_types_t _hftype, struct hdr_field** _h) { int ret; auth_body_t* c; struct sip_uri *uri; /* ACK and CANCEL must be always authorized, there is * no way how to challenge ACK and CANCEL cannot be * challenged because it must have the same CSeq as * the request to be canceled */ if ((_m->REQ_METHOD == METHOD_ACK) || (_m->REQ_METHOD == METHOD_CANCEL)) return AUTHORIZED; if (_realm->len == 0) { if (get_realm(_m, _hftype, &uri) < 0) { LM_ERR("failed to extract realm\n"); if (send_resp(_m, 400, &auth_400_err, 0, 0) == -1) { LM_ERR("failed to send 400 reply\n"); } return ERROR; } *_realm = uri->host; strip_realm(_realm); } /* Try to find credentials with corresponding realm * in the message, parse them and return pointer to * parsed structure */ ret = find_credentials(_m, _realm, _hftype, _h); if (ret < 0) { LM_ERR("failed to find credentials\n"); if (send_resp(_m, (ret == -2) ? 500 : 400, (ret == -2) ? &auth_500_err : &auth_400_err, 0, 0) == -1) { LM_ERR("failed to send 400 reply\n"); } return ERROR; } else if (ret > 0) { LM_DBG("credentials with given realm not found\n"); return NO_CREDENTIALS; } /* Pointer to the parsed credentials */ c = (auth_body_t*)((*_h)->parsed); /* Check credentials correctness here */ if (check_dig_cred(&(c->digest)) != E_DIG_OK) { LM_DBG("received credentials are not filled properly\n"); if (send_resp(_m, 400, &auth_400_err, 0, 0) == -1) { LM_ERR("failed to send 400 reply\n"); } return ERROR; } if (mark_authorized_cred(_m, *_h) < 0) { LM_ERR("failed to mark parsed credentials\n"); if (send_resp(_m, 500, &auth_400_err, 0, 0) == -1) { LM_ERR("failed to send 400 reply\n"); } return ERROR; } if (is_nonce_stale(&c->digest.nonce)) { LM_DBG("stale nonce value received\n"); c->stale = 1; return STALE_NONCE; } if (check_nonce(&c->digest.nonce, &secret) != 0) { LM_DBG("invalid nonce value received\n"); c->stale = 1; return STALE_NONCE; } return DO_AUTHORIZATION; } /* * Purpose of this function is to do post authentication steps like * marking authorized credentials and so on. */ auth_result_t post_auth(struct sip_msg* _m, struct hdr_field* _h) { auth_body_t* c; int index; c = (auth_body_t*)((_h)->parsed); if ((_m->REQ_METHOD == METHOD_ACK) || (_m->REQ_METHOD == METHOD_CANCEL)) return AUTHORIZED; if(!disable_nonce_check) { /* Verify if it is the first time this nonce is received */ index= get_nonce_index(&c->digest.nonce); if(index== -1) { LM_ERR("failed to extract nonce index\n"); return ERROR; } LM_DBG("nonce index= %d\n", index); if(!is_nonce_index_valid(index)) { LM_DBG("nonce index not valid\n"); c->stale = 1; return STALE_NONCE; } } return AUTHORIZED; } int check_response(dig_cred_t* _cred, str* _method, char* _ha1) { HASHHEX resp, hent; /* * First, we have to verify that the response received has * the same length as responses created by us */ if (_cred->response.len != 32) { LM_DBG("receive response len != 32\n"); return 1; } /* * Now, calculate our response from parameters received * from the user agent */ calc_response(_ha1, &(_cred->nonce), &(_cred->nc), &(_cred->cnonce), &(_cred->qop.qop_str), _cred->qop.qop_parsed == QOP_AUTHINT_D, _method, &(_cred->uri), hent, resp); LM_DBG("our result = \'%s\'\n", resp); /* * And simply compare the strings, the user is * authorized if they match */ if (!memcmp(resp, _cred->response.s, 32)) { LM_DBG("authorization is OK\n"); return 0; } else { LM_DBG("authorization failed\n"); return 2; } } int bind_auth(auth_api_t* api) { if (!api) { LM_ERR("invalid parameter value\n"); return -1; } api->pre_auth = pre_auth; api->post_auth = post_auth; api->calc_HA1 = calc_HA1; api->check_response = check_response; get_rpid_avp( &api->rpid_avp, &api->rpid_avp_type ); return 0; } opensips-2.2.2/modules/auth/api.h000066400000000000000000000066471300170765700167330ustar00rootroot00000000000000/* * Digest Authentication Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AUTH_API_H #define AUTH_API_H #include "../../parser/digest/digest.h" #include "../../parser/msg_parser.h" #include "../../parser/hf.h" #include "../../str.h" #include "../../usr_avp.h" #include "rfc2617.h" typedef enum auth_result { AUTH_ERROR = -5, /* Error occurred, a reply has not been sent out */ NO_CREDENTIALS, /* Credentials missing */ STALE_NONCE, /* Stale nonce */ INVALID_PASSWORD, /* Invalid password */ USER_UNKNOWN, /* User non existant */ ERROR, /* Error occurred, a reply has been sent out -> */ /* return 0 to the opensips core */ AUTHORIZED, /* Authorized. If returned by pre_auth, */ /* no digest authorization necessary */ DO_AUTHORIZATION, /* Can only be returned by pre_auth. */ /* Means to continue doing authorization */ } auth_result_t; /* * Purpose of this function is to find credentials with given realm, * do sanity check, validate credential correctness and determine if * we should really authenticate (there must be no authentication for * ACK and CANCEL */ typedef auth_result_t (*pre_auth_t)(struct sip_msg* _m, str* _realm, hdr_types_t _hftype, struct hdr_field** _h); auth_result_t pre_auth(struct sip_msg* _m, str* _realm, hdr_types_t _hftype, struct hdr_field** _h); /* * Purpose of this function is to do post authentication steps like * marking authorized credentials and so on. */ typedef auth_result_t (*post_auth_t)(struct sip_msg* _m, struct hdr_field* _h); auth_result_t post_auth(struct sip_msg* _m, struct hdr_field* _h); /* * Calculate the response and compare with the given response string * Authorization is successful if this two strings are same */ typedef int (*check_response_t)(dig_cred_t* _cred, str* _method, char* _ha1); int check_response(dig_cred_t* _cred, str* _method, char* _ha1); typedef void (*calc_HA1_t)(ha_alg_t _alg, str* _username, str* _realm, str* _password, str* _nonce, str* _cnonce, HASHHEX _sess_key); /* * Strip the beginning of realm */ void strip_realm(str *_realm); /* * Auth module API */ typedef struct auth_api { int rpid_avp; /* Name of AVP containing Remote-Party-ID */ int rpid_avp_type; /* type of the RPID AVP */ pre_auth_t pre_auth; /* The function to be called before auth */ post_auth_t post_auth; /* The function to be called after auth */ calc_HA1_t calc_HA1; /* calculate H(A1) as per spec */ check_response_t check_response; /* check auth response */ } auth_api_t; typedef int (*bind_auth_t)(auth_api_t* api); int bind_auth(auth_api_t* api); #endif /* AUTH_API_H */ opensips-2.2.2/modules/auth/auth_mod.c000066400000000000000000000266541300170765700177550ustar00rootroot00000000000000/* * Digest Authentication Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-26 checks and group moved to separate modules (janakj) * 2003-03-10 New module interface (janakj) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) * 2003-04-28 rpid contributed by Juha Heinanen added (janakj) * 2005-05-31 general avp specification added for rpid (bogdan) * 2006-03-01 pseudo variables support for domain name (bogdan) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../error.h" #include "../../pvar.h" #include "../../ut.h" #include "../../mod_fix.h" #include "../../lock_alloc.h" #include "../signaling/signaling.h" #include "auth_mod.h" #include "challenge.h" #include "rpid.h" #include "api.h" #define RAND_SECRET_LEN 32 #define DEF_RPID_PREFIX "" #define DEF_RPID_SUFFIX ";party=calling;id-type=subscriber;screen=yes" #define DEF_STRIP_REALM "" #define DEF_RPID_AVP "$avp(rpid)" static str auth_500_err = str_init("Server Internal Error"); /* * Module destroy function prototype */ static void destroy(void); /* * Module initialization function prototype */ static int mod_init(void); int pv_proxy_authorize(struct sip_msg* msg, char* realm, char* str2); int pv_www_authorize(struct sip_msg* msg, char* realm, char* str2); /** SIGNALING binds */ struct sig_binds sigb; /* * Module parameter variables */ char* sec_param = 0; /* If the parameter was not used, the secret phrase will be auto-generated */ unsigned int nonce_expire = 30; /* Nonce lifetime - default 30 seconds */ str secret; char* sec_rand = 0; int auth_calc_ha1 = 0; /* Default Remote-Party-ID prefix */ str rpid_prefix = {DEF_RPID_PREFIX, sizeof(DEF_RPID_PREFIX) - 1}; /* Default Remote-Party-IDD suffix */ str rpid_suffix = {DEF_RPID_SUFFIX, sizeof(DEF_RPID_SUFFIX) - 1}; /* Prefix to strip from realm */ str realm_prefix = {DEF_STRIP_REALM, sizeof(DEF_STRIP_REALM) - 1}; /* definition of AVP containing rpid value */ char* rpid_avp_param = DEF_RPID_AVP; /* definition of AVP containing username value */ char* user_spec_param = 0; static pv_spec_t user_spec; /* definition of AVP containing password value */ char* passwd_spec_param = 0; static pv_spec_t passwd_spec; /* nonce index */ gen_lock_t* nonce_lock= NULL; char* nonce_buf= NULL; int* sec_monit= NULL; int* second= NULL; int* next_index= NULL; int disable_nonce_check = 0; /* * Exported functions */ static cmd_export_t cmds[] = { {"www_challenge", (cmd_function)www_challenge, 2, fixup_spve_uint, 0, REQUEST_ROUTE}, {"proxy_challenge", (cmd_function)proxy_challenge, 2, fixup_spve_uint, 0, REQUEST_ROUTE}, {"pv_www_authorize", (cmd_function)pv_www_authorize, 1, fixup_spve_null, 0, REQUEST_ROUTE}, {"pv_proxy_authorize", (cmd_function)pv_proxy_authorize, 1, fixup_spve_null, 0, REQUEST_ROUTE}, {"consume_credentials", (cmd_function)consume_credentials, 0, 0, 0, REQUEST_ROUTE}, {"is_rpid_user_e164", (cmd_function)is_rpid_user_e164, 0, 0, 0, REQUEST_ROUTE}, {"append_rpid_hf", (cmd_function)append_rpid_hf, 0, 0, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"append_rpid_hf", (cmd_function)append_rpid_hf_p, 2, fixup_str_str, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"bind_auth", (cmd_function)bind_auth, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"secret", STR_PARAM, &sec_param }, {"nonce_expire", INT_PARAM, &nonce_expire }, {"rpid_prefix", STR_PARAM, &rpid_prefix.s }, {"rpid_suffix", STR_PARAM, &rpid_suffix.s }, {"realm_prefix", STR_PARAM, &realm_prefix.s }, {"rpid_avp", STR_PARAM, &rpid_avp_param }, {"username_spec", STR_PARAM, &user_spec_param }, {"password_spec", STR_PARAM, &passwd_spec_param }, {"calculate_ha1", INT_PARAM, &auth_calc_ha1 }, {"disable_nonce_check", INT_PARAM, &disable_nonce_check}, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* * Module interface */ struct module_exports exports = { "auth", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, 0, params, 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ 0 /* child initialization function */ }; /* * Secret parameter was not used so we generate * a random value here */ static inline int generate_random_secret(void) { int i; sec_rand = (char*)pkg_malloc(RAND_SECRET_LEN); if (!sec_rand) { LM_ERR("no pkg memory left\n"); return -1; } /* the generator is seeded from the core */ for(i = 0; i < RAND_SECRET_LEN; i++) { sec_rand[i] = 32 + (int)(95.0 * rand() / (RAND_MAX + 1.0)); } secret.s = sec_rand; secret.len = RAND_SECRET_LEN; /*LM_DBG("Generated secret: '%.*s'\n", secret.len, secret.s); */ return 0; } static int mod_init(void) { str stmp; LM_INFO("initializing...\n"); /* load SIGNALING API */ if(load_sig_api(&sigb)< 0){ LM_ERR("can't load signaling functions\n"); return -1; } /* If the parameter was not used */ if (sec_param == 0) { /* Generate secret using random generator */ if (generate_random_secret() < 0) { LM_ERR("failed to generate random secret\n"); return -3; } } else { /* Otherwise use the parameter's value */ secret.s = sec_param; secret.len = strlen(secret.s); } if ( init_rpid_avp(rpid_avp_param)<0 ) { LM_ERR("failed to process rpid AVPs\n"); return -4; } rpid_prefix.len = strlen(rpid_prefix.s); rpid_suffix.len = strlen(rpid_suffix.s); realm_prefix.len = strlen(realm_prefix.s); if(user_spec_param!=0) { stmp.s = user_spec_param; stmp.len = strlen(stmp.s); if(pv_parse_spec(&stmp, &user_spec)==NULL) { LM_ERR("failed to parse username spec\n"); return -5; } switch(user_spec.type) { case PVT_NONE: case PVT_EMPTY: case PVT_NULL: case PVT_MARKER: case PVT_COLOR: LM_ERR("invalid username spec\n"); return -6; default: ; } } if(passwd_spec_param!=0) { stmp.s = passwd_spec_param; stmp.len = strlen(stmp.s); if(pv_parse_spec(&stmp, &passwd_spec)==NULL) { LM_ERR("failed to parse password spec\n"); return -7; } switch(passwd_spec.type) { case PVT_NONE: case PVT_EMPTY: case PVT_NULL: case PVT_MARKER: case PVT_COLOR: LM_ERR("invalid password spec\n"); return -8; default: ; } } if(!disable_nonce_check) { nonce_lock = (gen_lock_t*)lock_alloc(); if(nonce_lock== NULL) { LM_ERR("no more shared memory\n"); return -1; } /* initialize lock_nonce */ if(lock_init(nonce_lock)== 0) { LM_ERR("failed to init lock\n"); return -9; } nonce_buf= (char*)shm_malloc(NBUF_LEN); if(nonce_buf== NULL) { LM_ERR("no more share memory\n"); return -10; } memset(nonce_buf, 255, NBUF_LEN); sec_monit= (int*)shm_malloc((nonce_expire +1)* sizeof(int)); if(sec_monit== NULL) { LM_ERR("no more share memory\n"); return -10; } memset(sec_monit, -1, (nonce_expire +1)* sizeof(int)); second= (int*)shm_malloc(sizeof(int)); next_index= (int*)shm_malloc(sizeof(int)); if(second== NULL || next_index== NULL) { LM_ERR("no more share memory\n"); return -10; } *next_index= -1; } return 0; } static void destroy(void) { if (sec_rand) pkg_free(sec_rand); if(!disable_nonce_check) { if(nonce_lock) { lock_destroy(nonce_lock); lock_dealloc(nonce_lock); } if(nonce_buf) shm_free(nonce_buf); if(second) shm_free(second); if(sec_monit) shm_free(sec_monit); if(next_index) shm_free(next_index); } } static inline int auth_get_ha1(struct sip_msg *msg, struct username* _username, str* _domain, char* _ha1) { pv_value_t sval; /* get username from PV */ memset(&sval, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, &user_spec, &sval)==0) { if(sval.flags==PV_VAL_NONE || (sval.flags&PV_VAL_NULL) || (sval.flags&PV_VAL_EMPTY) || (!(sval.flags&PV_VAL_STR))) { pv_value_destroy(&sval); return 1; } if(sval.rs.len!= _username->whole.len || strncasecmp(sval.rs.s, _username->whole.s, sval.rs.len)) { LM_DBG("username mismatch [%.*s] [%.*s]\n", _username->whole.len, _username->whole.s, sval.rs.len, sval.rs.s); pv_value_destroy(&sval); return 1; } } else { return 1; } /* get password from PV */ memset(&sval, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, &passwd_spec, &sval)==0) { if(sval.flags==PV_VAL_NONE || (sval.flags&PV_VAL_NULL) || (sval.flags&PV_VAL_EMPTY) || (!(sval.flags&PV_VAL_STR))) { pv_value_destroy(&sval); return 1; } } else { return 1; } if (auth_calc_ha1) { /* Only plaintext passwords are stored in database, * we have to calculate HA1 */ calc_HA1(HA_MD5, &_username->whole, _domain, &sval.rs, 0, 0, _ha1); LM_DBG("HA1 string calculated: %s\n", _ha1); } else { memcpy(_ha1, sval.rs.s, sval.rs.len); _ha1[sval.rs.len] = '\0'; } return 0; } static inline int pv_authorize(struct sip_msg* msg, gparam_p realm, hdr_types_t hftype) { static char ha1[256]; int res; struct hdr_field* h; auth_body_t* cred; auth_result_t ret; str domain; if(fixup_get_svalue(msg, realm, &domain)!=0) { LM_ERR("invalid realm parameter\n"); return -1; } if (domain.len==0) domain.s = 0; ret = pre_auth(msg, &domain, hftype, &h); if (ret != DO_AUTHORIZATION) return ret; cred = (auth_body_t*)h->parsed; res = auth_get_ha1(msg, &cred->digest.username, &domain, ha1); if (res < 0) { /* Error */ if (sigb.reply(msg, 500, &auth_500_err, NULL) == -1) { LM_ERR("failed to send 500 reply\n"); } return ERROR; } if (res > 0) { /* Username not found */ return USER_UNKNOWN; } /* Recalculate response, it must be same to authorize successfully */ if (!check_response(&(cred->digest),&msg->first_line.u.request.method,ha1)) { return post_auth(msg, h); } return INVALID_PASSWORD; } int pv_proxy_authorize(struct sip_msg* msg, char* realm, char* str2) { return pv_authorize(msg, (gparam_p)realm, HDR_PROXYAUTH_T); } int pv_www_authorize(struct sip_msg* msg, char* realm, char* str2) { return pv_authorize(msg, (gparam_p)realm, HDR_AUTHORIZATION_T); } opensips-2.2.2/modules/auth/auth_mod.h000066400000000000000000000034411300170765700177470ustar00rootroot00000000000000/* * Digest Authentication Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-28 rpid contributed by Juha Heinanen added (janakj) */ #ifndef AUTH_MOD_H #define AUTH_MOD_H #include "../../str.h" #include "../../parser/msg_parser.h" /* struct sip_msg */ #include "../signaling/signaling.h" #include "../../lock_ops.h" #define MAX_NONCE_INDEX 100000 #define NBUF_LEN (MAX_NONCE_INDEX>>3) /* * Module parameters variables */ extern str secret; /* secret phrase used to generate nonce */ extern unsigned int nonce_expire; /* nonce expire interval */ extern str rpid_prefix; /* Remote-Party-ID prefix */ extern str rpid_suffix; /* Remote-Party-ID suffix */ extern str realm_prefix; /* strip off auto-generated realm */ /** SIGNALING binds */ extern struct sig_binds sigb; /* nonce index */ extern gen_lock_t* nonce_lock; extern char* nonce_buf; extern int* sec_monit; extern int* second; extern int* next_index; extern int disable_nonce_check; #endif /* AUTH_MOD_H */ opensips-2.2.2/modules/auth/challenge.c000066400000000000000000000145251300170765700200710ustar00rootroot00000000000000/* * Challenge related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-20 snprintf in build_auth_hf replaced with memcpy to avoid * possible issues with too small buffer * 2003-01-26 consume_credentials no longer complains about ACK/CANCEL(jiri) * 2006-03-01 pseudo variables support for domain name (bogdan) */ #include "../../data_lump.h" #include "../../mem/mem.h" #include "../../parser/digest/digest.h" #include "../../pvar.h" #include "../../str.h" #include "../../ut.h" #include "../../mod_fix.h" #include "auth_mod.h" #include "common.h" #include "challenge.h" #include "nonce.h" #include "index.h" #include "api.h" static str auth_400_err = str_init(MESSAGE_400); static str auth_500_err = str_init(MESSAGE_500); /* * proxy_challenge function sends this reply */ #define MESSAGE_407 "Proxy Authentication Required" #define PROXY_AUTH_CHALLENGE "Proxy-Authenticate" /* * www_challenge function send this reply */ #define MESSAGE_401 "Unauthorized" #define WWW_AUTH_CHALLENGE "WWW-Authenticate" #define QOP_PARAM ", qop=\"auth\"" #define QOP_PARAM_LEN (sizeof(QOP_PARAM)-1) #define STALE_PARAM ", stale=true" #define STALE_PARAM_LEN (sizeof(STALE_PARAM)-1) #define DIGEST_REALM ": Digest realm=\"" #define DIGEST_REALM_LEN (sizeof(DIGEST_REALM)-1) #define DIGEST_NONCE "\", nonce=\"" #define DIGEST_NONCE_LEN (sizeof(DIGEST_NONCE)-1) #define DIGEST_MD5 ", algorithm=MD5" #define DIGEST_MD5_LEN (sizeof(DIGEST_MD5)-1) /* * Create {WWW,Proxy}-Authenticate header field */ static inline char *build_auth_hf(int _retries, int _stale, str* _realm, int* _len, int _qop, char* _hf_name) { int hf_name_len; char *hf, *p; int index = 0; if(!disable_nonce_check) { /* get the nonce index and mark it as used */ index= reserve_nonce_index(); if(index == -1) { LM_ERR("no more nonces can be generated\n"); return 0; } LM_DBG("nonce index= %d\n", index); } /* length calculation */ *_len=hf_name_len=strlen(_hf_name); *_len+=DIGEST_REALM_LEN +_realm->len +DIGEST_NONCE_LEN +((!disable_nonce_check)?NONCE_LEN:NONCE_LEN-8) +1 /* '"' */ +((_qop)? QOP_PARAM_LEN:0) +((_stale)? STALE_PARAM_LEN : 0) #ifdef _PRINT_MD5 +DIGEST_MD5_LEN #endif +CRLF_LEN ; p=hf=pkg_malloc(*_len+1); if (!hf) { LM_ERR("no pkg memory left\n"); *_len=0; return 0; } memcpy(p, _hf_name, hf_name_len); p+=hf_name_len; memcpy(p, DIGEST_REALM, DIGEST_REALM_LEN);p+=DIGEST_REALM_LEN; memcpy(p, _realm->s, _realm->len);p+=_realm->len; memcpy(p, DIGEST_NONCE, DIGEST_NONCE_LEN);p+=DIGEST_NONCE_LEN; calc_nonce(p, time(0) + nonce_expire, index, &secret); p+=((!disable_nonce_check)?NONCE_LEN:NONCE_LEN-8); *p='"';p++; if (_qop) { memcpy(p, QOP_PARAM, QOP_PARAM_LEN); p+=QOP_PARAM_LEN; } if (_stale) { memcpy(p, STALE_PARAM, STALE_PARAM_LEN); p+=STALE_PARAM_LEN; } #ifdef _PRINT_MD5 memcpy(p, DIGEST_MD5, DIGEST_MD5_LEN ); p+=DIGEST_MD5_LEN; #endif memcpy(p, CRLF, CRLF_LEN ); p+=CRLF_LEN; *p=0; /* zero terminator, just in case */ LM_DBG("'%s'\n", hf); return hf; } /* * Create and send a challenge */ static inline int challenge(struct sip_msg* _msg, gparam_p _realm, int _qop, int _code, char* _message, char* _challenge_msg) { int auth_hf_len; struct hdr_field* h = NULL; auth_body_t* cred = 0; char *auth_hf; int ret; hdr_types_t hftype = 0; /* Makes gcc happy */ struct sip_uri *uri; str realm; str reason; switch(_code) { case 401: get_authorized_cred(_msg->authorization, &h); hftype = HDR_AUTHORIZATION_T; break; case 407: get_authorized_cred(_msg->proxy_auth, &h); hftype = HDR_PROXYAUTH_T; break; } if (h) cred = (auth_body_t*)(h->parsed); if(fixup_get_svalue(_msg, _realm, &realm)!=0) { LM_ERR("invalid realm parameter"); if (send_resp(_msg, 500, &auth_500_err, 0, 0)==-1) return -1; else return 0; } if (realm.len == 0) { if (get_realm(_msg, hftype, &uri) < 0) { LM_ERR("failed to extract URI\n"); if (send_resp(_msg, 400, &auth_400_err, 0, 0) == -1) { LM_ERR("failed to send the response\n"); return -1; } return 0; } realm = uri->host; strip_realm(&realm); } auth_hf = build_auth_hf(0, (cred ? cred->stale : 0), &realm, &auth_hf_len, _qop, _challenge_msg); if (!auth_hf) { LM_ERR("failed to generate nonce\n"); return -1; } reason.s = _message; reason.len = strlen(_message); ret = send_resp(_msg, _code, &reason, auth_hf, auth_hf_len); if (auth_hf) pkg_free(auth_hf); if (ret == -1) { LM_ERR("failed to send the response\n"); return -1; } return 0; } /* * Challenge a user to send credentials using WWW-Authorize header field */ int www_challenge(struct sip_msg* _msg, char* _realm, char* _qop) { return challenge(_msg, (gparam_p)_realm, (int)(long)_qop, 401, MESSAGE_401, WWW_AUTH_CHALLENGE); } /* * Challenge a user to send credentials using Proxy-Authorize header field */ int proxy_challenge(struct sip_msg* _msg, char* _realm, char* _qop) { return challenge(_msg, (gparam_p)_realm, (int)(long)_qop, 407, MESSAGE_407, PROXY_AUTH_CHALLENGE); } /* * Remove used credentials from a SIP message header */ int consume_credentials(struct sip_msg* _m, char* _s1, char* _s2) { struct hdr_field* h; int len; get_authorized_cred(_m->authorization, &h); if (!h) { get_authorized_cred(_m->proxy_auth, &h); if (!h) { if (_m->REQ_METHOD!=METHOD_ACK && _m->REQ_METHOD!=METHOD_CANCEL) { LM_ERR("no authorized credentials found (error in scripts)\n"); } return -1; } } len=h->len; if (del_lump(_m, h->name.s - _m->buf, len, 0) == 0) { LM_ERR("can't remove credentials\n"); return -1; } return 1; } opensips-2.2.2/modules/auth/challenge.h000066400000000000000000000025161300170765700200730ustar00rootroot00000000000000/* * Challenge related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CHALLENGE_H #define CHALLENGE_H #include "../../parser/msg_parser.h" /* * Challenge a user agent using WWW-Authenticate header field */ int www_challenge(struct sip_msg* _msg, char* _realm, char* _str2); /* * Challenge a user agent using Proxy-Authenticate header field */ int proxy_challenge(struct sip_msg* _msg, char* _realm, char* _str2); /* * Remove used credentials from a SIP message header */ int consume_credentials(struct sip_msg* _m, char* _s1, char* _s2); #endif /* AUTH_H */ opensips-2.2.2/modules/auth/common.c000066400000000000000000000047441300170765700174410ustar00rootroot00000000000000/* * Digest Authentication Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-15: In case of HDR_PROXYAUTH we always extract realm from From, * even for REGISTERS * 2003-09-11: updated to new build_lump_rpl() interface (bogdan) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) */ #include #include "../../dprint.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../parser/parse_uri.h" #include "../../data_lump_rpl.h" #include "auth_mod.h" #include "common.h" /* * Return parsed To or From, host part of the parsed uri is realm */ int get_realm(struct sip_msg* _m, hdr_types_t _hftype, struct sip_uri** _u) { if(_u==NULL) return -1; if ((REQ_LINE(_m).method.len == 8) && !memcmp(REQ_LINE(_m).method.s, "REGISTER", 8) && (_hftype == HDR_AUTHORIZATION_T) ) { if (!_m->to && ((parse_headers(_m, HDR_TO_F, 0)==-1) || (!_m->to))) { LM_ERR("failed to parse TO headers\n"); return -1; } /* Body of To header field is parsed automatically */ if((*_u = parse_to_uri(_m))==NULL) return -1; } else { if (parse_from_header(_m) < 0) { LM_ERR("failed to parse FROM headers\n"); return -2; } if((*_u = parse_from_uri(_m))==NULL) return -1; } return 0; } /* * Create a response with given code and reason phrase * Optionally add new headers specified in _hdr */ int send_resp(struct sip_msg* _m, int _code, str* _reason, char* _hdr, int _hdr_len) { /* Add new headers if there are any */ if ((_hdr) && (_hdr_len)) { if (add_lump_rpl( _m, _hdr, _hdr_len, LUMP_RPL_HDR)==0) { LM_ERR("unable to append hdr\n"); return -1; } } return sigb.reply(_m, _code, _reason, NULL); } opensips-2.2.2/modules/auth/common.h000066400000000000000000000025341300170765700174410ustar00rootroot00000000000000/* * Digest Authentication Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMON_H #define COMMON_H #include "../../parser/msg_parser.h" #define MESSAGE_400 "Bad Request" #define MESSAGE_500 "Server Internal Error" /* * Return parsed To or From, host part of the parsed uri is realm */ int get_realm(struct sip_msg* _m, hdr_types_t _hftype, struct sip_uri** _u); /* * Create a response with given code and reason phrase * Optionally add new headers specified in _hdr */ int send_resp(struct sip_msg* _m, int _code, str* _reason, char* _hdr, int _hdr_len); #endif /* COMMON_H */ opensips-2.2.2/modules/auth/doc/000077500000000000000000000000001300170765700165415ustar00rootroot00000000000000opensips-2.2.2/modules/auth/doc/auth.xml000066400000000000000000000026451300170765700202330ustar00rootroot00000000000000 %docentities; ]> Auth Module &osipsname; Jan Janak FhG Fokus jan@iptel.org Juha Heinanen Song Networks jh@song.fi Bogdan-Andrei Iancu bogdan@opensips.org Jan Janak jan@iptel.org 2002 2003 FhG FOKUS 2005 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/auth/doc/auth_admin.xml000066400000000000000000000524101300170765700213760ustar00rootroot00000000000000 &adminguide;
Overview This is a module that provides common functions that are needed by other authentication related modules. Also, it can perform authentication taking username and password from pseudo-variables.
Nonce Security The authentication mechanism offers protection against sniffing intrusion. The module generates and verifies the nonces so that they can be used only once (in an auth response). This is done by having a lifetime value and an index associated with every nonce. Using only an expiration value is not good enough because,as this value has to be of few tens of seconds, it is possible for someone to sniff on the network, get the credentials and then reuse them in another packet with which to register a different contact or make calls using the others's account. The index ensures that this will never be possible since it is generated as unique through the lifetime of the nonce. The default limit for the requests that can be authenticated is 100000 in 30 seconds. If you wish to adjust this you can decrease the lifetime of a nonce( how much time to wait for a reply to a challenge). However, be aware not to set it to a too smaller value. However this mechanism does not work for architectures using a cluster of servers that share the same dns name for load balancing. In this case you can disable the nonce reusability check by setting the module parameter 'disable_nonce_check'.
Dependencies
&osips; Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): signaling -- Signaling module
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters
<varname>secret</varname> (string) Secret phrase used to calculate the nonce value. The default is to use a random value generated from the random source in the core. If you use multiple servers in your installation, and would like to authenticate on the second server against the nonce generated at the first one its necessary to explicitly set the secret to the same value on all servers. However, the use of a shared (and fixed) secret as nonce is insecure, much better is to stay with the default. Any clients should send the reply to the server that issued the request. secret parameter example modparam("auth", "secret", "johndoessecretphrase")
<varname>nonce_expire</varname> (integer) Nonces have limited lifetime. After a given period of time nonces will be considered invalid. This is to protect replay attacks. Credentials containing a stale nonce will be not authorized, but the user agent will be challenged again. This time the challenge will contain stale parameter which will indicate to the client that it doesn't have to disturb user by asking for username and password, it can recalculate credentials using existing username and password. The value is in seconds and default value is 30 seconds. nonce_expire parameter example modparam("auth", "nonce_expire", 15) # Set nonce_expire to 15s
<varname>rpid_prefix</varname> (string) Prefix to be added to Remote-Party-ID header field just before the URI returned from either radius or database. Default value is . rpid_prefix parameter example modparam("auth", "rpid_prefix", "Whatever <")
<varname>rpid_suffix</varname> (string) Suffix to be added to Remote-Party-ID header field after the URI returned from either radius or database. Default value is ;party=calling;id-type=subscriber;screen=yes. rpid_suffix parameter example modparam("auth", "rpid_suffix", "@1.2.3.4>")
<varname>realm_prefix</varname> (string) Prefix to be automatically strip from realm. As an alternative to SRV records (not all SIP clients support SRV lookup), a subdomain of the master domain can be defined for SIP purposes (like sip.mydomain.net pointing to same IP address as the SRV record for mydomain.net). By ignoring the realm_prefix sip., at authentication, sip.mydomain.net will be equivalent to mydomain.net . Default value is empty string. realm_prefix parameter example modparam("auth", "realm_prefix", "sip.")
<varname>rpid_avp</varname> (string) Full AVP specification for the AVP which stores the RPID value. It used to transport the RPID value from authentication backend modules (auth_db or auth_radius) or from script to the auth function append_rpid_hf and is_rpid_user_e164. If defined to NULL string, all RPID functions will fail at runtime. Default value is $avp(rpid). rpid_avp parameter example modparam("auth", "rpid_avp", "$avp(13)")
<varname>username_spec</varname> (string) This name of the pseudo-variable that will hold the username. Default value is NULL. <varname>username_spec</varname> parameter usage modparam("auth", "username_spec", "$var(username)")
<varname>password_spec</varname> (string) This name of the pseudo-variable that will hold the password. Default value is NULL. <varname>password_spec</varname> parameter usage modparam("auth", "password_spec", "$avp(password)")
<varname>calculate_ha1</varname> (integer) This parameter tells the server whether it should expect plaintext passwords in the pseudo-variable or a pre-calculated HA1 string. If the parameter is set to 1 then the server will assume that the password_spec pseudo-variable contains plaintext passwords and it will calculate HA1 strings on the fly. If the parameter is set to 0 then the server assumes the pseudo-variable contains the HA1 strings directly and will not calculate them. Default value of this parameter is 0. <varname>calculate_ha1</varname> parameter usage modparam("auth", "calculate_ha1", 1)
<varname>disable_nonce_check</varname> (int) By setting this parameter you disable the security mechanism that protects against intrusion sniffing and does not allow nonces to be reused. But, because of the current implementation, having this enabled breaks auth for an architecture where load is balanced by having more servers with the same dns name. This parameter has to be set in this case. Default value is 0 (enabled). <varname>disable_nonce_check</varname> parameter usage modparam("auth", "disable_nonce_check", 1)
Exported Functions
<function moreinfo="none">www_challenge(realm, qop)</function> The function challenges a user agent. It will generate a WWW-Authorize header field containing a digest challenge, it will put the header field into a response generated from the request the server is processing and will send the reply. Upon reception of such a reply the user agent should compute credentials and retry the request. For more information regarding digest authentication see RFC2617. Meaning of the parameters is as follows: realm - Realm is an opaque string that the user agent should present to the user so it can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string is used then the server will generate it from the request. In case of REGISTER request's To header field, domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. The string may contain pseudo variables. qop - Value of this parameter can be either 1 or 0. When set to 1 then the server will put a qop parameter in the challenge. When set to 0 then the server will not put the qop parameter in the challenge. It is recommended to use the qop parameter, however there are still some user agents that cannot handle qop properly so we made this optional. On the other hand there are still some user agents that cannot handle request without a qop parameter too. Enabling this parameter does not improve security at the moment, because the sequence number is not stored and therefore could not be checked. Actually there is no information kept by the module during the challenge and response requests. This function can be used from REQUEST_ROUTE. www_challenge usage ... if (!www_authorize("siphub.net", "subscriber")) { www_challenge("siphub.net", "1"); }; ...
<function moreinfo="none">proxy_challenge(realm, qop)</function> The function challenges a user agent. It will generate a Proxy-Authorize header field containing a digest challenge, it will put the header field into a response generated from the request the server is processing and will send the reply. Upon reception of such a reply the user agent should compute credentials and retry the request. For more information regarding digest authentication see RFC2617. Meaning of the parameters is as follows: realm - Realm is an opaque string that the user agent should present to the user so it can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string is used then the server will generate it from the request. From header field domain will be used as realm. The string may contain pseudo variables. qop - Value of this parameter can be either 1 or 0. When set to 1 then the server will put a qop parameter in the challenge. When set to 0 then the server will not put the qop parameter in the challenge. It is recommended to use the qop parameter, however there are still some user agents that cannot handle qop properly so we made this optional. On the other hand there are still some user agents that cannot handle request without a qop parameter too. Enabling this parameter don't improve the security at the moment, because the sequence number is not stored and therefore could not be checked. Actually there is no information kept by the module during the challenge and response requests. This function can be used from REQUEST_ROUTE. proxy_challenge usage ... if (!proxy_authorize("", "subscriber)) { proxy_challenge("", "1"); # Realm will be autogenerated }; ...
<function moreinfo="none">consume_credentials()</function> This function removes previously authorized credentials from the message being processed by the server. That means that the downstream message will not contain credentials there were used by this server. This ensures that the proxy will not reveal information about credentials used to downstream elements and also the message will be a little bit shorter. The function must be called after www_authorize or proxy_authorize. This function can be used from REQUEST_ROUTE. consume_credentials example ... if (www_authorize("", "subscriber)) { consume_credentials(); }; ...
<function moreinfo="none">is_rpid_user_e164()</function> The function checks if the SIP URI received from the database or radius server and will potentially be used in Remote-Party-ID header field contains an E164 number (+followed by up to 15 decimal digits) in its user part. Check fails, if no such SIP URI exists (i.e. radius server or database didn't provide this information). This function can be used from REQUEST_ROUTE. is_rpid_user_e164 usage ... if (is_rpid_user_e164()) { # do something here }; ...
<function moreinfo="none">append_rpid_hf()</function> Appends to the message a Remote-Party-ID header that contains header 'Remote-Party-ID: ' followed by the saved value of the SIP URI received from the database or radius server followed by the value of module parameter radius_rpid_suffix. The function does nothing if no saved SIP URI exists. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. append_rpid_hf usage ... append_rpid_hf(); # Append Remote-Party-ID header field ...
<function moreinfo="none">append_rpid_hf(prefix, suffix)</function> This function is the same as . The only difference is that it accepts two parameters--prefix and suffix to be added to Remote-Party-ID header field. This function ignores rpid_prefix and rpid_suffix parameters, instead of that allows to set them in every call. Meaning of the parameters is as follows: prefix - Prefix of the Remote-Party-ID URI. The string will be added at the beginning of body of the header field, just before the URI. suffix - Suffix of the Remote-Party-ID header field. The string will be appended at the end of the header field. It can be used to set various URI parameters, for example. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. append_rpid_hf(prefix, suffix) usage ... # Append Remote-Party-ID header field append_rpid_hf("", ";party=calling;id-type=subscriber;screen=yes"); ...
<function moreinfo="none">pv_www_authorize(realm)</function> The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call www_challenge which will challenge the user again. Negative codes may be interpreted as follows: -5 (generic error) - some generic error occurred and no reply was sent out; -4 (no credentials) - credentials were not found in request; -3 (stale nonce) - stale nonce; -2 (invalid password) - valid user, but wrong password; -1 (invalid user) - authentication user does not exist. Meaning of the parameters is as follows: realm - Realm is an opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string is used then the server will generate it from the request. In case of REGISTER requests To header field domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. The string may contain pseudo variables. This function can be used from REQUEST_ROUTE. <function moreinfo="none">pv_www_authorize</function> usage ... $var(username)="abc"; $avp(password)="xyz"; if (!pv_www_authorize("opensips.org")) { www_challenge("opensips.org", "1"); }; ...
<function moreinfo="none">pv_proxy_authorize(realm)</function> The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call proxy_challenge which will challenge the user again. For more about the negative return codes, see the above function. Meaning of the parameters is as follows: realm - Realm is an opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string is used then the server will generate it from the request. From header field domain will be used as realm. The string may contain pseudo variables. This function can be used from REQUEST_ROUTE. pv_proxy_authorize usage ... $var(username)="abc"; $avp(password)="xyz"; if (!pv_proxy_authorize("")) { proxy_challenge("", "1"); # Realm will be autogenerated }; ...
opensips-2.2.2/modules/auth/index.c000066400000000000000000000111011300170765700172410ustar00rootroot00000000000000/* * Nonce index related functions * * Copyright (C)2008 Voice System S.R.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-05-29 initial version (anca) */ #include #include "../../dprint.h" #include "../../timer.h" #include "index.h" #include "auth_mod.h" #define set_buf_bit(index) \ do{\ nonce_buf[index>>3] |= (1<<(index%8));\ }while(0) #define unset_buf_bit(index) \ do{\ nonce_buf[index>>3] &= ~(1<<(index%8));\ }while(0) #define check_buf_bit(index) ( nonce_buf[index>>3] & (1<<(index%8)) ) /* * Get a valid index for the new nonce */ int reserve_nonce_index(void) { unsigned int curr_sec; int index; int i; curr_sec = get_ticks()%(nonce_expire+1); lock_get(nonce_lock); /* update last index for the previous seconds */ if(*next_index== -1) /* for the first request */ *next_index= 0; else { /* if the portion with still alive nonces is not yet reached */ if(*second!= curr_sec) { /* get the index for the next nonce */ index= (*next_index==MAX_NONCE_INDEX)?MAX_NONCE_INDEX-1:*next_index -1; /* set the interval in sec_monit vector */ if(curr_sec> *second) { for (i= *second; i< curr_sec; i++) sec_monit[i]= index; } else { for (i= *second; i<= nonce_expire; i++) sec_monit[i]= index; for (i= 0; i< curr_sec; i++) sec_monit[i]= index; } } } *second= curr_sec; if(sec_monit[curr_sec]== -1) /* if in the first second*/ { if(*next_index == MAX_NONCE_INDEX) { lock_release(nonce_lock); return -1; } goto done; } if(*next_index> sec_monit[curr_sec]) /* if at the end of the buffer */ { /* if at the end of the buffer */ if(*next_index == MAX_NONCE_INDEX) { *next_index = 0; goto index_smaller; } goto done; } index_smaller: if(*next_index== sec_monit[curr_sec]) /* no more space -> return error */ { lock_release(nonce_lock); LM_INFO("no more indexes available\n"); return -1; } done: unset_buf_bit(*next_index); index= *next_index; *next_index = *next_index + 1; LM_DBG("second= %d, sec_monit= %d, index= %d\n", *second, sec_monit[curr_sec], index); lock_release(nonce_lock); return index; } /* * Check if the nonce has been used before */ int is_nonce_index_valid(int index) { /* if greater than MAX_NONCE_INDEX ->error */ if(index>= MAX_NONCE_INDEX ) { LM_ERR("index greater than buffer length\n"); return 0; } lock_get(nonce_lock); /* if in the first 30 seconds */ if(sec_monit[*second]== -1) { if(index>= *next_index) { LM_DBG("index out of range\n"); lock_release(nonce_lock); return 0; } else { set_buf_bit(index); lock_release(nonce_lock); return 1; } } /* check if right interval */ if(*next_index < sec_monit[*second]) { if(!(index>= sec_monit[*second] || index<= *next_index)) { LM_DBG("index out of the permitted interval\n"); goto error; } } else { if(!(index >= sec_monit[*second] && index<=*next_index)) { LM_DBG("index out of the permitted interval\n"); goto error; } } /* check if the first time used */ if(check_buf_bit(index)) { LM_DBG("nonce already used\n"); goto error; } set_buf_bit(index); lock_release(nonce_lock); return 1; error: lock_release(nonce_lock); return 0; } opensips-2.2.2/modules/auth/index.h000066400000000000000000000021271300170765700172560ustar00rootroot00000000000000/* * Nonce index related functions * * Copyright (C)2008 Voice System S.R.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-05-29 initial version (anca) */ #ifndef _NONCE_INDEX_H_ #define _NONCE_INDEX_H_ /* * Get valid index for nonce */ int reserve_nonce_index(void); /* * Check index validity */ int is_nonce_index_valid(int index); #endif opensips-2.2.2/modules/auth/nonce.c000066400000000000000000000072111300170765700172430ustar00rootroot00000000000000/* * Nonce related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "../../md5global.h" #include "../../md5.h" #include "../../dprint.h" #include "../../ut.h" #include "../../timer.h" #include "nonce.h" #include "index.h" #include "auth_mod.h" /* * Convert an integer to its hex representation, * destination array must be at least 8 bytes long, * this string is NOT zero terminated */ static inline void integer2hex(char* _d, int _s) { int i; unsigned char j; char* s; _s = htonl(_s); s = (char*)&_s; for (i = 0; i < 4; i++) { j = (s[i] >> 4) & 0xf; if (j <= 9) { _d[i * 2] = (j + '0'); } else { _d[i * 2] = (j + 'a' - 10); } j = s[i] & 0xf; if (j <= 9) { _d[i * 2 + 1] = (j + '0'); } else { _d[i * 2 + 1] = (j + 'a' - 10); } } } /* * Convert hex string to integer */ static inline int hex2integer(char* _s) { unsigned int i, res = 0; for(i = 0; i < 8; i++) { res *= 16; if ((_s[i] >= '0') && (_s[i] <= '9')) { res += _s[i] - '0'; } else if ((_s[i] >= 'a') && (_s[i] <= 'f')) { res += _s[i] - 'a' + 10; } else if ((_s[i] >= 'A') && (_s[i] <= 'F')) { res += _s[i] - 'A' + 10; } else return 0; } return res; } /* * Calculate nonce value * Nonce value consists of the expires time (in seconds since 1.1 1970) * and a secret phrase */ void calc_nonce(char* _nonce, int _expires, int _index, str* _secret) { MD5_CTX ctx; unsigned char bin[16]; unsigned int offset = 8; MD5Init(&ctx); integer2hex(_nonce, _expires); if(!disable_nonce_check) { integer2hex(_nonce + 8, _index); offset = 16; } MD5Update(&ctx, _nonce, offset); MD5Update(&ctx, _secret->s, _secret->len); MD5Final(bin, &ctx); string2hex(bin, 16, _nonce + offset); _nonce[offset + 32] = '\0'; } /* * Get nonce index */ int get_nonce_index(str* _n) { return hex2integer(_n->s + 8); } /* * Get expiry time from nonce string */ time_t get_nonce_expires(str* _n) { return (time_t)hex2integer(_n->s); } /* * Check, if the nonce received from client is * correct */ int check_nonce(str* _nonce, str* _secret) { int expires; char non[NONCE_LEN + 1]; int index = 0; if (_nonce->s == 0) { return -1; /* Invalid nonce */ } if (NONCE_LEN != ((!disable_nonce_check)?_nonce->len:_nonce->len +8)) { return 1; /* Lengths must be equal */ } expires = get_nonce_expires(_nonce); if(!disable_nonce_check) index = get_nonce_index(_nonce); calc_nonce(non, expires, index, _secret); LM_DBG("comparing [%.*s] and [%.*s]\n", _nonce->len, ZSW(_nonce->s), ((!disable_nonce_check)?NONCE_LEN:NONCE_LEN-8), non); if (!memcmp(non, _nonce->s, _nonce->len)) { return 0; } return 2; } /* * Check if a nonce is stale */ int is_nonce_stale(str* _n) { if (!_n->s) return 0; if (get_nonce_expires(_n) < time(0)) { return 1; } else { return 0; } } opensips-2.2.2/modules/auth/nonce.h000066400000000000000000000026331300170765700172530ustar00rootroot00000000000000/* * Nonce related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NONCE_H #define NONCE_H #include "../../str.h" #include /* * Length of nonce string in bytes */ #define NONCE_LEN (16+32) /* * Calculate nonce value */ void calc_nonce(char* _nonce, int _expires, int _index, str* _secret); /* * Check nonce value received from UA */ int check_nonce(str* _nonce, str* _secret); /* * Get expiry time from nonce string */ time_t get_nonce_expires(str* _nonce); /* * Get index from nonce string */ int get_nonce_index(str* _nonce); /* * Check if the nonce is stale */ int is_nonce_stale(str* _nonce); #endif /* NONCE_H */ opensips-2.2.2/modules/auth/rfc2617.c000066400000000000000000000070661300170765700172430ustar00rootroot00000000000000/* * Digest response calculation as per RFC2617 * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "rfc2617.h" #include "../../md5global.h" #include "../../md5.h" inline void cvt_hex(HASH _b, HASHHEX _h) { unsigned short i; unsigned char j; for (i = 0; i < HASHLEN; i++) { j = (_b[i] >> 4) & 0xf; if (j <= 9) { _h[i * 2] = (j + '0'); } else { _h[i * 2] = (j + 'a' - 10); } j = _b[i] & 0xf; if (j <= 9) { _h[i * 2 + 1] = (j + '0'); } else { _h[i * 2 + 1] = (j + 'a' - 10); } }; _h[HASHHEXLEN] = '\0'; } /* * calculate H(A1) as per spec */ void calc_HA1(ha_alg_t _alg, str* _username, str* _realm, str* _password, str* _nonce, str* _cnonce, HASHHEX _sess_key) { MD5_CTX Md5Ctx; HASH HA1; MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, _username->s, _username->len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, _realm->s, _realm->len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, _password->s, _password->len); MD5Final(HA1, &Md5Ctx); if (_alg == HA_MD5_SESS) { MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, HA1, HASHLEN); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, _nonce->s, _nonce->len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, _cnonce->s, _cnonce->len); MD5Final(HA1, &Md5Ctx); }; cvt_hex(HA1, _sess_key); } /* * calculate request-digest/response-digest as per HTTP Digest spec */ void calc_response(HASHHEX _ha1, /* H(A1) */ str* _nonce, /* nonce from server */ str* _nc, /* 8 hex digits */ str* _cnonce, /* client nonce */ str* _qop, /* qop-value: "", "auth", "auth-int" */ int _auth_int, /* 1 if auth-int is used */ str* _method, /* method from the request */ str* _uri, /* requested URL */ HASHHEX _hentity, /* H(entity body) if qop="auth-int" */ HASHHEX _response) /* request-digest or response-digest */ { MD5_CTX Md5Ctx; HASH HA2; HASH RespHash; HASHHEX HA2Hex; /* calculate H(A2) */ MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, _method->s, _method->len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, _uri->s, _uri->len); if (_auth_int) { MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, _hentity, HASHHEXLEN); }; MD5Final(HA2, &Md5Ctx); cvt_hex(HA2, HA2Hex); /* calculate response */ MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, _ha1, HASHHEXLEN); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, _nonce->s, _nonce->len); MD5Update(&Md5Ctx, ":", 1); if (_qop->len) { MD5Update(&Md5Ctx, _nc->s, _nc->len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, _cnonce->s, _cnonce->len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, _qop->s, _qop->len); MD5Update(&Md5Ctx, ":", 1); }; MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN); MD5Final(RespHash, &Md5Ctx); cvt_hex(RespHash, _response); } opensips-2.2.2/modules/auth/rfc2617.h000066400000000000000000000042741300170765700172460ustar00rootroot00000000000000/* * Digest response calculation as per RFC2617 * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RFC2617_H #define RFC2617_H #include "../../str.h" #define HASHLEN 16 typedef char HASH[HASHLEN]; #define HASHHEXLEN 32 typedef char HASHHEX[HASHHEXLEN+1]; /* * Type of algorithm used */ typedef enum { HA_MD5, /* Plain MD5 */ HA_MD5_SESS, /* MD5-Session */ } ha_alg_t; /* * Convert to hex form */ void cvt_hex(HASH Bin, HASHHEX Hex); /* * calculate H(A1) as per HTTP Digest spec */ void calc_HA1(ha_alg_t _alg, /* Type of algorithm */ str* _username, /* username */ str* _realm, /* realm */ str* _password, /* password */ str* _nonce, /* nonce string */ str* _cnonce, /* cnonce */ HASHHEX _sess_key); /* Result will be stored here */ /* calculate request-digest/response-digest as per HTTP Digest spec */ void calc_response(HASHHEX _ha1, /* H(A1) */ str* _nonce, /* nonce from server */ str* _nc, /* 8 hex digits */ str* _cnonce, /* client nonce */ str* _qop, /* qop-value: "", "auth", "auth-int" */ int _auth_int, /* 1 if auth-int is used */ str* _method, /* method from the request */ str* _uri, /* requested URL */ HASHHEX _hentity, /* H(entity body) if qop="auth-int" */ HASHHEX _response); /* request-digest or response-digest */ #endif /* RFC2617_H */ opensips-2.2.2/modules/auth/rpid.c000066400000000000000000000143031300170765700170770ustar00rootroot00000000000000/* * Remote-Party-ID related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-28 rpid contributed by Juha Heinanen added (janakj) * 2005-05-31 general avp specification added for rpid (bogdan) */ #include #include #include "../../str.h" #include "../../data_lump.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../parser/parse_nameaddr.h" #include "../../parser/parse_uri.h" #include "../../parser/parser_f.h" #include "../../ut.h" #include "../../pvar.h" #include "auth_mod.h" #include "api.h" #include "rpid.h" #define RPID_HF_NAME "Remote-Party-ID: " #define RPID_HF_NAME_LEN (sizeof(RPID_HF_NAME) - 1) /* rpid AVP specs */ static unsigned short rpid_avp_type; static int rpid_avp_name; /* * Parse and set the RPID AVP specs */ int init_rpid_avp(char *rpid_avp_param) { pv_spec_t avp_spec; str stmp; if (rpid_avp_param && *rpid_avp_param) { stmp.s = rpid_avp_param; stmp.len = strlen(stmp.s); if (pv_parse_spec(&stmp, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %s AVP definition\n", rpid_avp_param); return -1; } if(pv_get_avp_name(0, &(avp_spec.pvp), &rpid_avp_name, &rpid_avp_type)!=0) { LM_ERR("[%s]- invalid AVP definition\n", rpid_avp_param); return -1; } } else { rpid_avp_name = -1; rpid_avp_type = 0; } return 0; } /* * Gets the RPID avp specs */ void get_rpid_avp( int *rpid_avp_p, int *rpid_avp_type_p ) { *rpid_avp_p = rpid_avp_name; *rpid_avp_type_p = rpid_avp_type; } /* * Copy of is_e164 from enum module */ static inline int is_e164(str* _user) { int i; char c; if ((_user->len > 2) && (_user->len < 17) && ((_user->s)[0] == '+')) { for (i = 1; i < _user->len; i++) { c = (_user->s)[i]; if ((c < '0') || (c > '9')) return -1; } return 1; } else { return -1; } } /* * Copy of append_hf_helper from textops. */ static inline int append_rpid_helper(struct sip_msg* _m, str *_s) { struct lump* anchor; if (parse_headers(_m, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse message\n"); return -1; } anchor = anchor_lump(_m, _m->unparsed - _m->buf, 0); if (!anchor) { LM_ERR("can't get anchor\n"); return -2; } if (!insert_new_lump_before(anchor, _s->s, _s->len, 0)) { LM_ERR("can't insert lump\n"); return -3; } return 0; } /* * Append RPID header field to the message */ int append_rpid_hf(struct sip_msg* _m, char* _s1, char* _s2) { struct usr_avp *avp; str rpid_hf, rpid; char *at; int_str val; if (rpid_avp_name==-1) { LM_ERR("rpid avp not defined\n"); return -1; } if ( (avp=search_first_avp( rpid_avp_type , rpid_avp_name, &val, 0))==0 ) { LM_DBG("no rpid AVP\n"); return -1; } if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) { LM_DBG("empty or non-string rpid, nothing to append\n"); return -1; } rpid = val.s; rpid_hf.len = RPID_HF_NAME_LEN + rpid_prefix.len + rpid.len + rpid_suffix.len + CRLF_LEN; rpid_hf.s = pkg_malloc(rpid_hf.len); if (!rpid_hf.s) { LM_ERR("no memory left\n"); return -1; } at = rpid_hf.s; memcpy(at, RPID_HF_NAME, RPID_HF_NAME_LEN); at += RPID_HF_NAME_LEN; memcpy(at, rpid_prefix.s, rpid_prefix.len); at += rpid_prefix.len; memcpy(at, rpid.s, rpid.len); at += rpid.len; memcpy(at, rpid_suffix.s, rpid_suffix.len); at += rpid_suffix.len; memcpy(at, CRLF, CRLF_LEN); if (append_rpid_helper(_m, &rpid_hf) < 0) { pkg_free(rpid_hf.s); return -1; } return 1; } /* * Append RPID header field to the message with parameters */ int append_rpid_hf_p(struct sip_msg* _m, char* _prefix, char* _suffix) { struct usr_avp *avp; str rpid_hf, rpid; char* at; str* p, *s; int_str val; if (rpid_avp_name==-1) { LM_ERR("rpid avp not defined\n"); return -1; } if ( (avp=search_first_avp( rpid_avp_type , rpid_avp_name, &val, 0))==0 ) { LM_DBG("no rpid AVP\n"); return -1; } if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) { LM_DBG("empty or non-string rpid, nothing to append\n"); return -1; } rpid = val.s; p = (str*)_prefix; s = (str*)_suffix; rpid_hf.len = RPID_HF_NAME_LEN + p->len + rpid.len + s->len + CRLF_LEN; rpid_hf.s = pkg_malloc(rpid_hf.len); if (!rpid_hf.s) { LM_ERR("no pkg memory left\n"); return -1; } at = rpid_hf.s; memcpy(at, RPID_HF_NAME, RPID_HF_NAME_LEN); at += RPID_HF_NAME_LEN; memcpy(at, p->s, p->len); at += p->len; memcpy(at, rpid.s, rpid.len); at += rpid.len; memcpy(at, s->s, s->len); at += s->len; memcpy(at, CRLF, CRLF_LEN); if (append_rpid_helper(_m, &rpid_hf) < 0) { pkg_free(rpid_hf.s); return -1; } return 1; } /* * Check if URI in RPID AVP contains an E164 user part */ int is_rpid_user_e164(struct sip_msg* _m, char* _s1, char* _s2) { struct usr_avp *avp; name_addr_t parsed; str tmp, rpid; struct sip_uri uri; int_str val; if (rpid_avp_name==-1) { LM_ERR("rpid avp not defined\n"); return -1; } if ( (avp=search_first_avp( rpid_avp_type , rpid_avp_name, &val, 0))==0 ) { LM_DBG("no rpid AVP\n"); goto err; } if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) { LM_DBG("empty or non-string rpid, nothing to append\n"); return -1; } rpid = val.s; if (find_not_quoted(&rpid, '<')) { if (parse_nameaddr(&rpid, &parsed) < 0) { LM_ERR("failed to parse RPID\n"); goto err; } tmp = parsed.uri; } else { tmp = rpid; } if (parse_uri(tmp.s, tmp.len, &uri) < 0) { LM_ERR("failed to parse RPID URI\n"); goto err; } return is_e164(&uri.user); err: return -1; } opensips-2.2.2/modules/auth/rpid.h000066400000000000000000000032671300170765700171130ustar00rootroot00000000000000/* * Remote-Party-ID related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-28 rpid contributed by Juha Heinanen added (janakj) * 2005-05-31 general avp specification added for rpid (bogdan) */ #ifndef RPID_H #define RPID_H #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../usr_avp.h" /* * Parse and init the rpid avp specification */ int init_rpid_avp(char *rpid_avp_param); /* * Get the RPID avp specs */ void get_rpid_avp( int *rpid_avp_p, int *rpid_avp_type_p ); /* * Append RPID header field to the message */ int append_rpid_hf(struct sip_msg* _m, char* _s1, char* _s2); /* * Append RPID header field to the message with parameters */ int append_rpid_hf_p(struct sip_msg* _m, char* _prefix, char* _suffix); /* * Check if SIP URI in rpid contains an e164 user part */ int is_rpid_user_e164(struct sip_msg* _m, char* _s1, char* _s2); #endif /* RPID_H */ opensips-2.2.2/modules/auth/todo.txt000066400000000000000000000004771300170765700175120ustar00rootroot00000000000000- Describe parser structure, how nonce is looked up - Describe that www,proxy_authorize must be called before any other function - Create some examples how to use digest parser - Consider MD5-Sess support(will require to store passwords in clear text) - auth-int support - Database cache - Option to use memory only opensips-2.2.2/modules/auth_aaa/000077500000000000000000000000001300170765700165765ustar00rootroot00000000000000opensips-2.2.2/modules/auth_aaa/Makefile000066400000000000000000000004041300170765700202340ustar00rootroot00000000000000# $Id$ # # Digest Authentication - AAA generic support # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=auth_aaa.so DEFS+=-I$(LOCALBASE)/include LIBS= include ../../Makefile.modules opensips-2.2.2/modules/auth_aaa/README000066400000000000000000000211441300170765700174600ustar00rootroot00000000000000Auth_aaa Module Jan Janak FhG Fokus Juha Heinanen Song Networks Stelios Sidiroglou-Douskos Bogdan-Andrei Iancu Edited by Jan Janak Edited by Irina-Maria Stanescu Copyright © 2002, 2003 FhG FOKUS Copyright © 2005, 2009 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Additional Credentials 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. aaa_url (string) 1.4.2. service_type (integer) 1.4.3. use_ruri_flag (string/integer) 1.5. Exported Functions 1.5.1. aaa_www_authorize(realm[, uri_user]) 1.5.2. aaa_proxy_authorize(realm [, uri_user]) List of Examples 1.1. “SIP-AVP†AAA AVP examples 1.2. aaa_url parameter usage 1.3. service_type parameter usage 1.4. use_ruri_flag parameter usage 1.5. aaa_www_authorize usage 1.6. proxy_authorize usage Chapter 1. Admin Guide 1.1. Overview This module contains functions that are used to perform authentication using an AAA server. Basically the proxy will pass along the credentials to the AAA server which will in turn send a reply containing result of the authentication. So basically the whole authentication is done in the AAA server. Before sending the request to the AAA server we perform some sanity checks over the credentials to make sure that only well formed credentials will get to the server. We have implemented AAA authentication according to draft-sterman-aaa-sip-00. 1.2. Additional Credentials When performing authentication, the AAA server may include in the response additional credentials. This scheme is very useful in fetching additional user information from the AAA server without making extra queries. The additional credentials are embedded in the AAA reply as AVPs “SIP-AVPâ€. The syntax of the value is: * value = SIP_AVP_NAME SIP_AVP_VALUE * SIP_AVP_NAME = STRING_NAME | '#'ID_NUMBER * SIP_AVP_VALUE = ':'STRING_VALUE | '#'NUMBER_VALUE All additional credentials will be stored as OpenSIPS AVPs (SIP_AVP_NAME = SIP_AVP_VALUE). The RPID value may be fetch via this mechanism. Example 1.1. “SIP-AVP†AAA AVP examples .... "email:joe@yahoo.com" - STRING NAME AVP (email) with STRING VALUE (joe@yahoo.com) "#14:joe@yahoo.com" - ID AVP (14) with STRING VALUE (joe@yahoo.com) "age#28" - STRING NAME AVP (age) with INTEGER VALUE (28) "#14#28" - ID AVP (14) with INTEGER VALUE (28) .... 1.3. Dependencies 1.3.1. OpenSIPS Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): * auth -- Generic authentication functions * an aaa implementing module -- for example aaa_radius 1.3.2. External Libraries or Applications This module does not depend on any external library. 1.4. Exported Parameters 1.4.1. aaa_url (string) This is the url representing the AAA protocol used and the location of the configuration file of this protocol. The syntax for the url is the following: "name_of_the_aaa_protocol_used:path_of_the_configuration_file" Example 1.2. aaa_url parameter usage modparam("auth_aaa", "aaa_url", "radius:/etc/radiusclient-ng/radiusclien t.conf") 1.4.2. service_type (integer) This is the value of the Service-Type aaa attribute to be used. The default should be fine for most people. See your aaa client include files for numbers to be put in this parameter if you need to change it. Default value is “15â€. Example 1.3. service_type parameter usage modparam("auth_aaa", "service_type", 15) 1.4.3. use_ruri_flag (string/integer) When this parameter is set to the value other than "NULL" and the request being authenticated has flag with matching number set via setflag() function, use Request URI instead of uri parameter value from the Authorization / Proxy-Authorization header field to perform AAA authentication. This is intended to provide workaround for misbehaving NAT / routers / ALGs that alter request in the transit, breaking authentication. At the time of this writing, certain versions of Linksys WRT54GL are known to do that. Default value is “NULL†(not set). Example 1.4. use_ruri_flag parameter usage modparam("auth_aaa", "use_ruri_flag", "USE_RURI_FLAG") 1.5. Exported Functions 1.5.1. aaa_www_authorize(realm[, uri_user]) The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call www_challenge which will challenge the user again. Negative codes may be interpreted as follows: * -5 (generic error) - some generic error occurred and no reply was sent out; * -4 (no credentials) - credentials were not found in request; * -3 (stale nonce) - stale nonce; This function will, in fact, perform sanity checks over the received credentials and then pass them along to the aaa server which will verify the credentials and return whether they are valid or not. Meaning of the parameter is as follows: * realm - Realm is a opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string Ҡis used then the server will generate it from the request. In case of REGISTER requests To header field domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. The string may contain pseudo variables. * uri_user - Uri_user is an optional pseudo variable parameter whose value, if present, will be given to Radius server as value of SIP-URI-User check item. If uri_user pseudo variable parameter is not present, the server will generate SIP-URI-User check item value from user part of To URI. This function can be used from REQUEST_ROUTE. Example 1.5. aaa_www_authorize usage ... if (!aaa_www_authorize("siphub.net")) { www_challenge("siphub.net", "1"); }; ... 1.5.2. aaa_proxy_authorize(realm [, uri_user]) The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call proxy_challenge which will challenge the user again. For more about the negative return codes, see the above function. This function will, in fact, perform sanity checks over the received credentials and then pass them along to the aaa server which will verify the credentials and return whether they are valid or not. Meaning of the parameters is as follows: * realm - Realm is a opaque string that the user agent should present to the user so he can decide what username and password to use. This is usually one of the domains the proxy is responsible for. If an empty string Ҡis used then the server will generate realm from host part of From header field URI. The string may contain pseudo variables. * uri_user - Uri_user is an optional pseudo variable parameter whose value, if present, will be given to Radius server as value of SIP-URI-User check item. If uri_user pseudo variable parameter is not present, the server will generate SIP-URI-User check item value from user part of From URI. This function can be used from REQUEST_ROUTE. Example 1.6. proxy_authorize usage ... if (!aaa_proxy_authorize("")) { # Realm and URI user will be autogener ated proxy_challenge("", "1"); }; ... if (!aaa_proxy_authorize("$pd", "$pU")) { # Realm and URI user are taken proxy_challenge("$pd", "1"); # from P-Preferred-Identity }; # header field ... opensips-2.2.2/modules/auth_aaa/authaaa_mod.c000066400000000000000000000145771300170765700212230ustar00rootroot00000000000000/* * Digest Authentication - generic AAA support * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../dprint.h" #include "../../config.h" #include "../../pvar.h" #include "../../aaa/aaa.h" #include "../../mem/mem.h" #include "../../ut.h" #include "authaaa_mod.h" #include "authorize.h" aaa_map attrs[A_MAX]; aaa_map vals[V_MAX]; aaa_conn *conn; aaa_prot proto; auth_api_t auth_api; static int mod_init(void); /* Module initialization function */ static int auth_fixup(void** param, int param_no); /* char* -> str* */ /* * Module parameter variables */ static char* aaa_proto_url = NULL; static int service_type = -1; int use_ruri_flag = -1; char *use_ruri_flag_str = 0; /* * Exported functions */ static cmd_export_t cmds[] = { {"aaa_www_authorize", (cmd_function)aaa_www_authorize, 1, auth_fixup, 0, REQUEST_ROUTE}, {"aaa_www_authorize", (cmd_function)aaa_www_authorize, 2, auth_fixup, 0, REQUEST_ROUTE}, {"aaa_proxy_authorize", (cmd_function)aaa_proxy_authorize_1, 1, auth_fixup, 0, REQUEST_ROUTE}, {"aaa_proxy_authorize", (cmd_function)aaa_proxy_authorize_2, 2, auth_fixup, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"aaa_url", STR_PARAM, &aaa_proto_url }, {"service_type", INT_PARAM, &service_type }, {"use_ruri_flag", STR_PARAM, &use_ruri_flag_str }, {"use_ruri_flag", INT_PARAM, &use_ruri_flag }, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "auth", DEP_ABORT }, { MOD_TYPE_AAA, NULL, DEP_WARN }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* * Module interface */ struct module_exports exports = { "auth_aaa", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0 /* child initialization function */ }; /* * Module initialization function */ static int mod_init(void) { bind_auth_t bind_auth; str proto_url; aaa_map map; LM_INFO("initializing...\n"); memset(attrs, 0, sizeof(attrs)); memset(vals, 0, sizeof(vals)); attrs[A_SERVICE_TYPE].name = "Service-Type"; attrs[A_SIP_URI_USER].name = "Sip-URI-User"; attrs[A_DIGEST_RESPONSE].name = "Digest-Response"; attrs[A_DIGEST_ALGORITHM].name = "Digest-Algorithm"; attrs[A_DIGEST_BODY_DIGEST].name = "Digest-Body-Digest"; attrs[A_DIGEST_CNONCE].name = "Digest-CNonce"; attrs[A_DIGEST_NONCE_COUNT].name = "Digest-Nonce-Count"; attrs[A_DIGEST_QOP].name = "Digest-QOP"; attrs[A_DIGEST_METHOD].name = "Digest-Method"; attrs[A_DIGEST_URI].name = "Digest-URI"; attrs[A_DIGEST_NONCE].name = "Digest-Nonce"; attrs[A_DIGEST_REALM].name = "Digest-Realm"; attrs[A_DIGEST_USER_NAME].name = "Digest-User-Name"; attrs[A_USER_NAME].name = "User-Name"; attrs[A_CISCO_AVPAIR].name = "Cisco-AVPair"; attrs[A_SIP_AVP].name = "SIP-AVP"; attrs[A_ACCT_SESSION_ID].name = "Acct-Session-Id"; vals[V_SIP_SESSION].name = "Sip-Session"; fix_flag_name(use_ruri_flag_str, use_ruri_flag); use_ruri_flag = get_flag_id_by_name(FLAG_TYPE_MSG, use_ruri_flag_str); if (!aaa_proto_url) { LM_ERR("aaa_url is empty\n"); return -1; } proto_url.s = aaa_proto_url; proto_url.len = strlen(aaa_proto_url); if(aaa_prot_bind(&proto_url, &proto)) { LM_ERR("aaa protocol bind failure\n"); return -1; } if (!(conn = proto.init_prot(&proto_url))) { LM_ERR("aaa init protocol failure\n"); return -2; } map.name = "Cisco"; if (proto.dictionary_find(conn, &map, AAA_DICT_FIND_VEND)) { LM_DBG("no `Cisco' vendor in AAA protocol dictionary\n"); attrs[A_CISCO_AVPAIR].name = NULL; } bind_auth = (bind_auth_t)find_export("bind_auth", 0, 0); if (!bind_auth) { LM_ERR("unable to find bind_auth function. Check if you load the auth module.\n"); return -1; } if (bind_auth(&auth_api) < 0) { LM_ERR("cannot bind to auth module\n"); return -4; } INIT_AV(proto, conn, attrs, A_MAX, vals, V_MAX, "auth_aaa", -5, -6); if (service_type != -1) { vals[V_SIP_SESSION].value = service_type; } return 0; } /* * Convert char* parameter to pv_elem_t* parameter */ static int auth_fixup(void** param, int param_no) { pv_elem_t *model; str s; pv_spec_t *sp; if (param_no == 1) { /* realm (string that may contain pvars) */ s.s = (char*)*param; if (s.s==0 || s.s[0]==0) { model = 0; } else { s.len = strlen(s.s); if (pv_parse_format(&s,&model)<0) { LM_ERR("pv_parse_format failed\n"); return E_OUT_OF_MEM; } } *param = (void*)model; } if (param_no == 2) { /* URI user (a pvar) */ sp = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); if (sp == 0) { LM_ERR("no pkg memory left\n"); return -1; } s.s = (char*)*param; s.len = strlen(s.s); if (pv_parse_spec(&s, sp) == 0) { LM_ERR("parsing of pseudo variable %s failed!\n", (char*)*param); pkg_free(sp); return -1; } if (sp->type == PVT_NULL) { LM_ERR("bad pseudo variable\n"); pkg_free(sp); return -1; } *param = (void*)sp; } return 0; } opensips-2.2.2/modules/auth_aaa/authaaa_mod.h000066400000000000000000000022761300170765700212210ustar00rootroot00000000000000/* * Digest Authentication - generic AAA support * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef AUTHAAA_MOD_H #define AUTHAAA_MOD_H #include "../auth/api.h" #include "../../aaa/aaa.h" extern aaa_map attrs[]; extern aaa_map vals[]; extern aaa_conn *conn; extern aaa_prot proto; extern int use_ruri_flag; extern auth_api_t auth_api; #endif /* AUTHAAA_MOD_H */ opensips-2.2.2/modules/auth_aaa/authorize.c000066400000000000000000000105451300170765700207610ustar00rootroot00000000000000/* * Digest Authentication - generic AAA support * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../../mem/mem.h" #include "../../str.h" #include "../../parser/hf.h" #include "../../parser/digest/digest.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../dprint.h" #include "../../ut.h" #include "../../pvar.h" #include "../auth/api.h" #include "authorize.h" #include "sterman.h" #include "authaaa_mod.h" /* * Extract URI depending on the request from To or From header */ static inline int get_uri_user(struct sip_msg* _m, str** _uri_user) { struct sip_uri *puri; if ((REQ_LINE(_m).method.len == 8) && (memcmp(REQ_LINE(_m).method.s, "REGISTER", 8) == 0)) { if ((puri=parse_to_uri(_m))==NULL) { LM_ERR("failed to parse To header\n"); return -1; } } else { if ((puri=parse_from_uri(_m))==NULL) { LM_ERR("parsing From header\n"); return -1; } } *_uri_user = &(puri->user); return 0; } /* * Authorize digest credentials */ static inline int authorize(struct sip_msg* _msg, pv_elem_t* _realm, pv_spec_t * _uri_user, int _hftype) { int res; auth_result_t ret; struct hdr_field* h; auth_body_t* cred; str *uri_user; str user, domain; pv_value_t pv_val; /* get pre_auth domain from _realm pvar (if exists) */ if (_realm) { if (pv_printf_s(_msg, _realm, &domain)!=0) { LM_ERR("pv_printf_s failed\n"); return AUTH_ERROR; } } else { /* get pre_auth domain from To/From header */ domain.len = 0; domain.s = 0; } ret = auth_api.pre_auth(_msg, &domain, _hftype, &h); if (ret != DO_AUTHORIZATION) return ret; cred = (auth_body_t*)h->parsed; /* get uri_user from _uri_user pvap (if exists) or from To/From URI */ if (_uri_user) { if (pv_get_spec_value(_msg, _uri_user, &pv_val) == 0) { if (pv_val.flags & PV_VAL_STR) { res = aaa_authorize_sterman(_msg, &cred->digest, &_msg->first_line.u.request.method, &pv_val.rs); } else { LM_ERR("uri_user pvar value is not string\n"); return AUTH_ERROR; } } else { LM_ERR("cannot get uri_user pvar value\n"); return AUTH_ERROR; } } else { if (get_uri_user(_msg, &uri_user) < 0) { LM_ERR("To/From URI not found\n"); return AUTH_ERROR; } user.s = (char *)pkg_malloc(uri_user->len); if (user.s == NULL) { LM_ERR("no pkg memory left for user\n"); return AUTH_ERROR; } un_escape(uri_user, &user); res = aaa_authorize_sterman(_msg, &cred->digest, &_msg->first_line.u.request.method, &user); pkg_free(user.s); } if (res == 1) { ret = auth_api.post_auth(_msg, h); return ret; } return AUTH_ERROR; } /* * Authorize using Proxy-Authorize header field (no URI user parameter given) */ int aaa_proxy_authorize_1(struct sip_msg* _msg, char* _realm, char* _s2) { /* realm parameter is converted in fixup */ return authorize(_msg, (pv_elem_t*)_realm, (pv_spec_t *)0, HDR_PROXYAUTH_T); } /* * Authorize using Proxy-Authorize header field (URI user parameter given) */ int aaa_proxy_authorize_2(struct sip_msg* _msg, char* _realm, char* _uri_user) { return authorize(_msg, (pv_elem_t*)_realm, (pv_spec_t *)_uri_user, HDR_PROXYAUTH_T); } /* * Authorize using WWW-Authorize header field */ int aaa_www_authorize(struct sip_msg* _msg, char* _realm, char* _uri_user) { return authorize(_msg, (pv_elem_t*)_realm, (pv_spec_t *)_uri_user, HDR_AUTHORIZATION_T); } opensips-2.2.2/modules/auth_aaa/authorize.h000066400000000000000000000027121300170765700207630ustar00rootroot00000000000000/* * Digest Authentication - generic AAA support * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AUTHORIZE_H #define AUTHORIZE_H #include "../../parser/msg_parser.h" /* * Authorize using Proxy-Authorize header field (no from parameter given) */ int aaa_proxy_authorize_1(struct sip_msg* _msg, char* _realm, char* _s2); /* * Authorize using Proxy-Authorize header field (from parameter given) */ int aaa_proxy_authorize_2(struct sip_msg* _msg, char* _realm, char* _from); /* * Authorize using WWW-Authorization header field */ int aaa_www_authorize(struct sip_msg* _msg, char* _realm, char* _s2); #endif /* AUTHORIZE_H */ opensips-2.2.2/modules/auth_aaa/doc/000077500000000000000000000000001300170765700173435ustar00rootroot00000000000000opensips-2.2.2/modules/auth_aaa/doc/auth_aaa.xml000066400000000000000000000031451300170765700216330ustar00rootroot00000000000000 %docentities; ]> Auth_aaa Module Jan Janak FhG Fokus jan@iptel.org Juha Heinanen Song Networks jh@song.fi Stelios Sidiroglou-Douskos Bogdan-Andrei Iancu bogdan@opensips.org Jan Janak jan@iptel.org Irina-Maria Stanescu ironmissy@gmail.com 2002 2003 FhG FOKUS 2005 2009 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/auth_aaa/doc/auth_aaa_admin.xml000066400000000000000000000244161300170765700230070ustar00rootroot00000000000000 &adminguide;
Overview This module contains functions that are used to perform authentication using an AAA server. Basically the proxy will pass along the credentials to the AAA server which will in turn send a reply containing result of the authentication. So basically the whole authentication is done in the AAA server. Before sending the request to the AAA server we perform some sanity checks over the credentials to make sure that only well formed credentials will get to the server. We have implemented AAA authentication according to draft-sterman-aaa-sip-00.
Additional Credentials When performing authentication, the AAA server may include in the response additional credentials. This scheme is very useful in fetching additional user information from the AAA server without making extra queries. The additional credentials are embedded in the AAA reply as AVPs SIP-AVP. The syntax of the value is: value = SIP_AVP_NAME SIP_AVP_VALUE SIP_AVP_NAME = STRING_NAME | '#'ID_NUMBER SIP_AVP_VALUE = ':'STRING_VALUE | '#'NUMBER_VALUE All additional credentials will be stored as &osips; AVPs (SIP_AVP_NAME = SIP_AVP_VALUE). The RPID value may be fetch via this mechanism. <quote>SIP-AVP</quote> AAA AVP examples .... "email:joe@yahoo.com" - STRING NAME AVP (email) with STRING VALUE (joe@yahoo.com) "#14:joe@yahoo.com" - ID AVP (14) with STRING VALUE (joe@yahoo.com) "age#28" - STRING NAME AVP (age) with INTEGER VALUE (28) "#14#28" - ID AVP (14) with INTEGER VALUE (28) ....
Dependencies
&osips; Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): auth -- Generic authentication functions an aaa implementing module -- for example aaa_radius
External Libraries or Applications This module does not depend on any external library.
Exported Parameters
<varname>aaa_url</varname> (string) This is the url representing the AAA protocol used and the location of the configuration file of this protocol. The syntax for the url is the following: "name_of_the_aaa_protocol_used:path_of_the_configuration_file" <varname>aaa_url</varname> parameter usage modparam("auth_aaa", "aaa_url", "radius:/etc/radiusclient-ng/radiusclient.conf")
<varname>service_type</varname> (integer) This is the value of the Service-Type aaa attribute to be used. The default should be fine for most people. See your aaa client include files for numbers to be put in this parameter if you need to change it. Default value is 15. <varname>service_type</varname> parameter usage modparam("auth_aaa", "service_type", 15)
<varname>use_ruri_flag</varname> (string/integer) When this parameter is set to the value other than "NULL" and the request being authenticated has flag with matching number set via setflag() function, use Request URI instead of uri parameter value from the Authorization / Proxy-Authorization header field to perform AAA authentication. This is intended to provide workaround for misbehaving NAT / routers / ALGs that alter request in the transit, breaking authentication. At the time of this writing, certain versions of Linksys WRT54GL are known to do that. Default value is NULL (not set). <varname>use_ruri_flag</varname> parameter usage modparam("auth_aaa", "use_ruri_flag", "USE_RURI_FLAG")
Exported Functions
<function moreinfo="none">aaa_www_authorize(realm[, uri_user])</function> The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call www_challenge which will challenge the user again. Negative codes may be interpreted as follows: -5 (generic error) - some generic error occurred and no reply was sent out; -4 (no credentials) - credentials were not found in request; -3 (stale nonce) - stale nonce; This function will, in fact, perform sanity checks over the received credentials and then pass them along to the aaa server which will verify the credentials and return whether they are valid or not. Meaning of the parameter is as follows: realm - Realm is a opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string is used then the server will generate it from the request. In case of REGISTER requests To header field domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. The string may contain pseudo variables. uri_user - Uri_user is an optional pseudo variable parameter whose value, if present, will be given to Radius server as value of SIP-URI-User check item. If uri_user pseudo variable parameter is not present, the server will generate SIP-URI-User check item value from user part of To URI. This function can be used from REQUEST_ROUTE. <function moreinfo="none">aaa_www_authorize</function> usage ... if (!aaa_www_authorize("siphub.net")) { www_challenge("siphub.net", "1"); }; ...
<function moreinfo="none">aaa_proxy_authorize(realm [, uri_user])</function> The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call proxy_challenge which will challenge the user again. For more about the negative return codes, see the above function. This function will, in fact, perform sanity checks over the received credentials and then pass them along to the aaa server which will verify the credentials and return whether they are valid or not. Meaning of the parameters is as follows: realm - Realm is a opaque string that the user agent should present to the user so he can decide what username and password to use. This is usually one of the domains the proxy is responsible for. If an empty string is used then the server will generate realm from host part of From header field URI. The string may contain pseudo variables. uri_user - Uri_user is an optional pseudo variable parameter whose value, if present, will be given to Radius server as value of SIP-URI-User check item. If uri_user pseudo variable parameter is not present, the server will generate SIP-URI-User check item value from user part of From URI. This function can be used from REQUEST_ROUTE. <function moreinfo="none">proxy_authorize</function> usage ... if (!aaa_proxy_authorize("")) { # Realm and URI user will be autogenerated proxy_challenge("", "1"); }; ... if (!aaa_proxy_authorize("$pd", "$pU")) { # Realm and URI user are taken proxy_challenge("$pd", "1"); # from P-Preferred-Identity }; # header field ...
opensips-2.2.2/modules/auth_aaa/sterman.c000066400000000000000000000170341300170765700204200ustar00rootroot00000000000000/* * Digest Authentication - generic AAA support * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "../../mem/mem.h" #include "../../dprint.h" #include "../../aaa/aaa.h" #include "../auth/api.h" #include "sterman.h" #include "authaaa_mod.h" #include #include static int add_cisco_vsa(aaa_message** send, struct sip_msg* msg) { str callid; if (!msg->callid && parse_headers(msg, HDR_CALLID_F, 0) == -1) { LM_ERR("cannot parse Call-ID header field\n"); return -1; } if (!msg->callid) { LM_ERR("call-ID header field not found\n"); return -1; } callid.len = msg->callid->body.len + 8; callid.s = pkg_malloc(callid.len); if (callid.s == NULL) { LM_ERR("no pkg memory left\n"); return -1; } memcpy(callid.s, "call-id=", 8); memcpy(callid.s + 8, msg->callid->body.s, msg->callid->body.len); if (proto.avp_add(conn, *send, &attrs[A_CISCO_AVPAIR], callid.s, callid.len, attrs[A_CISCO_AVPAIR].value)) { LM_ERR("unable to add Cisco-AVPair attribute\n"); pkg_free(callid.s); return -1; } pkg_free(callid.s); return 0; } /* * This function creates and submits aaa authentication request as per * draft-sterman-aaa-sip-00.txt. In addition, _user parameter is included * in the request as value of a SER specific attribute type SIP-URI-User, * which can be be used as a check item in the request. Service type of * the request is Authenticate-Only. */ int aaa_authorize_sterman(struct sip_msg* _msg, dig_cred_t* _cred, str* _method, str* _user) { aaa_message *send, *received; uint32_t service; str method, user, user_name; str *ruri; send = received = NULL; if (!(_cred && _method && _user)) { LM_ERR("invalid parameter value\n"); return -1; } method = *_method; user = *_user; if ((send = proto.create_aaa_message(conn, AAA_AUTH)) == NULL) { LM_ERR("failed to create new aaa message for auth\n"); return -1; } /* * Add all the user digest parameters according to the qop defined. * Most devices tested only offer support for the simplest digest. */ if (_cred->username.domain.len) { if (proto.avp_add(conn, send, &attrs[A_USER_NAME], _cred->username.whole.s, _cred->username.whole.len, 0)) { LM_ERR("unable to add User-Name attribute\n"); goto err; } } else { user_name.len = _cred->username.user.len + _cred->realm.len + 1; user_name.s = pkg_malloc(user_name.len); if (!user_name.s) { LM_ERR("no pkg memory left\n"); return -3; } memcpy(user_name.s, _cred->username.whole.s, _cred->username.whole.len); user_name.s[_cred->username.whole.len] = '@'; memcpy(user_name.s + _cred->username.whole.len + 1, _cred->realm.s, _cred->realm.len); if (proto.avp_add(conn, send, &attrs[A_USER_NAME], user_name.s, user_name.len, 0)) { LM_ERR("unable to add User-Name attribute\n"); pkg_free(user_name.s); goto err; } pkg_free(user_name.s); } if (proto.avp_add(conn, send, &attrs[A_DIGEST_USER_NAME], _cred->username.whole.s, _cred->username.whole.len, 0)) { LM_ERR("unable to add Digest-User-Name attribute\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_DIGEST_REALM], _cred->realm.s, _cred->realm.len, 0)) { LM_ERR("unable to add Digest-Realm attribute\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_DIGEST_NONCE], _cred->nonce.s, _cred->nonce.len, 0)) { LM_ERR("unable to add Digest-Nonce attribute\n"); goto err; } if (use_ruri_flag < 0 || isflagset(_msg, use_ruri_flag) != 1) { ruri = &_cred->uri; } else { ruri = GET_RURI(_msg); } if (proto.avp_add(conn, send, &attrs[A_DIGEST_URI], ruri->s, ruri->len, 0)) { LM_ERR("unable to add Digest-URI attribute\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_DIGEST_METHOD], method.s, method.len, 0)) { LM_ERR("unable to add Digest-Method attribute\n"); goto err; } /* * Add the additional authentication fields according to the QOP. */ if (_cred->qop.qop_parsed == QOP_AUTH_D) { if (proto.avp_add(conn, send, &attrs[A_DIGEST_QOP], "auth", 4, 0)) { LM_ERR("unable to add Digest-QOP attribute\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_DIGEST_NONCE_COUNT], _cred->nc.s, _cred->nc.len, 0)) { LM_ERR("unable to add Digest-CNonce-Count attribute\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_DIGEST_CNONCE], _cred->cnonce.s, _cred->cnonce.len, 0)) { LM_ERR("unable to add Digest-CNonce attribute\n"); goto err; } } else if (_cred->qop.qop_parsed == QOP_AUTHINT_D) { if (proto.avp_add(conn, send, &attrs[A_DIGEST_QOP], "auth-int", 8, 0)) { LM_ERR("unable to add Digest-QOP attribute\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_DIGEST_NONCE_COUNT], _cred->nc.s, _cred->nc.len, 0)) { LM_ERR("unable to add Digest-Nonce-Count attribute\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_DIGEST_CNONCE], _cred->cnonce.s, _cred->cnonce.len, 0)) { LM_ERR("unable to add Digest-CNonce attribute\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_DIGEST_BODY_DIGEST], _cred->opaque.s, _cred->opaque.len, 0)) { LM_ERR("unable to add Digest-Body-Digest attribute\n"); goto err; } } else { /* send nothing for qop == "" */ } /* Add the response... What to calculate against... */ if (proto.avp_add(conn, send, &attrs[A_DIGEST_RESPONSE], _cred->response.s, _cred->response.len, 0)) { LM_ERR("unable to add Digest-Response attribute\n"); goto err; } /* Indicate the service type, Authenticate only in our case */ service = vals[V_SIP_SESSION].value; if (proto.avp_add(conn, send, &attrs[A_SERVICE_TYPE], &service, -1, 0)) { LM_ERR("unable to add Service-Type attribute\n"); goto err; } /* Add SIP URI as a check item */ if (proto.avp_add(conn, send, &attrs[A_SIP_URI_USER], user.s,user.len,0)) { LM_ERR("unable to add Sip-URI-User attribute\n"); goto err; } /* Add CALL-ID in Acct-Session-Id Attribute */ if ( _msg->callid==NULL && (parse_headers(_msg, HDR_CALLID_F, 0)==-1 || _msg->callid==NULL) ) { LM_ERR("msg parsing failed or callid not present"); goto err; } if (proto.avp_add(conn, send, &attrs[A_ACCT_SESSION_ID], _msg->callid->body.s, _msg->callid->body.len, 0)) { LM_ERR("unable to add CALL-ID attribute\n"); goto err; } if (attrs[A_CISCO_AVPAIR].name != NULL) { if (add_cisco_vsa(&send, _msg)) { goto err; } } /* Send request */ if (!proto.send_aaa_request(conn, send, &received)) { LM_DBG("Success\n"); proto.destroy_aaa_message(conn, send); proto.destroy_aaa_message(conn, received); return 1; } else { #ifdef REJECT_RC /* if (i == REJECT_RC) { LM_DBG("Failure\n"); goto err; }*/ #endif LM_ERR("authorization failed\n"); } err: if (send) proto.destroy_aaa_message(conn, send); if (received) proto.destroy_aaa_message(conn, received); return -1; } opensips-2.2.2/modules/auth_aaa/sterman.h000066400000000000000000000027361300170765700204300ustar00rootroot00000000000000/* * Digest Authentication - generic AAA support * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef STERMAN_H #define STERMAN_H #include "../../str.h" #include "../../parser/digest/digest_parser.h" /* * This function creates and submits aaa authentication request as per * draft-sterman-aaa-sip-00.txt. In addition, _user parameter is included * in the request as value of a SER specific attribute type SIP-URI-User, * which can be be used as a check item in the request. Service type of * the request is Authenticate-Only. */ int aaa_authorize_sterman(struct sip_msg* _msg, dig_cred_t* _cred, str* _method, str* _user); #endif /* STERMAN_H */ opensips-2.2.2/modules/auth_db/000077500000000000000000000000001300170765700164415ustar00rootroot00000000000000opensips-2.2.2/modules/auth_db/Makefile000066400000000000000000000003431300170765700201010ustar00rootroot00000000000000# $Id$ # # Digest Authentication - Database support # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=auth_db.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/auth_db/README000066400000000000000000000264101300170765700173240ustar00rootroot00000000000000Auth_db Module Jan Janak FhG Fokus Jakob Schlyter Bogdan-Andrei Iancu Edited by Jan Janak Copyright © 2002, 2003 FhG FOKUS Copyright © 2005 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. user_column (string) 1.3.3. domain_column (string) 1.3.4. password_column (string) 1.3.5. password_column_2 (string) 1.3.6. calculate_ha1 (integer) 1.3.7. use_domain (integer) 1.3.8. load_credentials (string) 1.3.9. skip_version_check (int) 1.4. Exported Functions 1.4.1. www_authorize(realm, table) 1.4.2. proxy_authorize(realm, table) List of Examples 1.1. db_url parameter usage 1.2. user_column parameter usage 1.3. domain_column parameter usage 1.4. password_column parameter usage 1.5. password_column_2 parameter usage 1.6. calculate_ha1 parameter usage 1.7. use_domain parameter usage 1.8. load_credentials parameter usage 1.9. skip_version_check parameter usage 1.10. www_authorize usage 1.11. proxy_authorize usage Chapter 1. Admin Guide 1.1. Overview This module contains all authentication related functions that need the access to the database. This module should be used together with auth module, it cannot be used independently because it depends on the module. Select this module if you want to use database to store authentication information like subscriber usernames and passwords. If you want to use radius authentication, then use auth_radius instead. 1.2. Dependencies 1.2.1. OpenSIPS Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): * auth -- Generic authentication functions * database -- Any database module (currently mysql, postgres, dbtext) 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.3. Exported Parameters 1.3.1. db_url (string) This is URL of the database to be used. Value of the parameter depends on the database module used. For example for mysql and postgres modules this is something like mysql://username:password@host:port/database. For dbtext module (which stores data in plaintext files) it is directory in which the database resides. Default value is “mysql://opensipsro:opensipsro@localhost/opensipsâ€. Example 1.1. db_url parameter usage modparam("auth_db", "db_url", "dbdriver://username:password@dbhost/dbnam e") 1.3.2. user_column (string) This is the name of the column holding usernames. Default value is fine for most people. Use the parameter if you really need to change it. Default value is “usernameâ€. Example 1.2. user_column parameter usage modparam("auth_db", "user_column", "user") 1.3.3. domain_column (string) This is the name of the column holding domains of users. Default value is fine for most people. Use the parameter if you really need to change it. Default value is “domainâ€. Example 1.3. domain_column parameter usage modparam("auth_db", "domain_column", "domain") 1.3.4. password_column (string) This is the name of the column holding passwords. Passwords can be either stored as plain text or pre-calculated HA1 strings. HA1 strings are MD5 hashes of username, password, and realm. HA1 strings are more safe because the server doesn't need to know plaintext passwords and they cannot be obtained from HA1 strings. Default value is “ha1â€. Example 1.4. password_column parameter usage modparam("auth_db", "password_column", "password") 1.3.5. password_column_2 (string) As described in the previous section this parameter contains name of column holding pre-calculated HA1 string that were calculated including the domain in the username. This parameter is used only when calculate_ha1 is set to 0 and user agent send a credentials containing the domain in the username. Default value of the parameter is ha1b. Example 1.5. password_column_2 parameter usage modparam("auth_db", "password_column_2", "ha1_2") 1.3.6. calculate_ha1 (integer) This parameter tells the server whether it should considered the loaded password (for authentification) as plaintext passwords or a pre-calculated HA1 string. Possible meanings of this parameter are: * 1 (calculate HA1) - the loaded password is a plaintext password, so OpenSIPS will internally calculate the HA1. As the passwors will be loaded from the column specified in the “password_column†parameter, be sure this parameter points to a column holding a plaintext password (by default, this parameter points to “ha1†column); * 0 (do NOT calculate HA1) - the loaded password is an already computed HA1 value, so OpenSIPS does not have do any further computing (for HA1 value). Depending on the presence of a “@domain†part (some user agents append the domain to the username credentials parameter too), the modules will load the password (pre-computed HA1) from the “password_column_2†column (if domain present) or from the “password_column†column (if domain not present). Usually, most of the UAs do NOT include a domain part in the username credentials parameter. The “password_column_2†column contains also HA1 strings but they should be calculated including the domain in the username parameter (as opposed to password_column which (when containing HA1 strings) should always contain HA1 strings calculated without domain in username. This ensures that the authentication will always work when using pre-calculated HA1 strings, not depending on the presence of the domain in username. Default value of this parameter is 0. Example 1.6. calculate_ha1 parameter usage modparam("auth_db", "calculate_ha1", 1) 1.3.7. use_domain (integer) If true (not 0), domain will be also used when looking up in the subscriber table. If you have a multi-domain setup, it is strongly recommended to turn on this parameter to avoid username overlapping between domains. IMPORTANT: before turning on this parameter, be sure that the domain column in subscriber table is properly populated. Default value is “0 (false)â€. Example 1.7. use_domain parameter usage modparam("auth_db", "use_domain", 1) 1.3.8. load_credentials (string) This parameter specifies credentials to be fetched from database when the authentication is performed. The loaded credentials will be stored in AVPs. If the AVP name is not specificaly given, it will be used a NAME AVP with the same name as the column name. Parameter syntax: * load_credentials = credential (';' credential)* * credential = (avp_specification '=' column_name) | (column_name) * avp_specification = '$avp(' + NAME + ')' Default value of this parameter is “rpidâ€. Example 1.8. load_credentials parameter usage # load rpid column into $avp(13) and email_address column # into $avp(email_address) modparam("auth_db", "load_credentials", "$avp(13)=rpid;email_address") 1.3.9. skip_version_check (int) This parameter specifies not to check the auth table version. This parameter should be set when a custom authentication table is used. Default value is “0 (false)â€. Example 1.9. skip_version_check parameter usage modparam("auth_db", "skip_version_check", 1) 1.4. Exported Functions 1.4.1. www_authorize(realm, table) The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call www_challenge which will challenge the user again. Negative codes may be interpreted as follows: * -5 (generic error) - some generic error occurred and no reply was sent out; * -4 (no credentials) - credentials were not found in request; * -3 (stale nonce) - stale nonce; * -2 (invalid password) - valid user, but wrong password; * -1 (invalid user) - authentication user does not exist. Meaning of the parameters is as follows: * realm - Realm is an opaque string that the user agent should present to the user so it can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string Ҡis used then the server will generate it from the request. In case of REGISTER requests To header field domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. The string may contain pseudo variables. * table - Table to be used to lookup usernames and passwords (usually subscribers table). This function can be used from REQUEST_ROUTE. Example 1.10. www_authorize usage ... if (!www_authorize("siphub.net", "subscriber")) { www_challenge("siphub.net", "1"); }; ... 1.4.2. proxy_authorize(realm, table) The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call proxy_challenge which will challenge the user again. Negative codes may be interpreted as follows: * -5 (generic error) - some generic error occurred and no reply was sent out; * -4 (no credentials) - credentials were not found in request; * -3 (stale nonce) - stale nonce; * -2 (invalid password) - valid user, but wrong password; * -1 (invalid user) - authentication user does not exist. Meaning of the parameters is as follows: * realm - Realm is an opaque string that the user agent should present to the user so it can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string Ҡis used then the server will generate it from the request. From header field domain will be used as realm. The string may contain pseudo variables. * table - Table to be used to lookup usernames and passwords (usually subscribers table). This function can be used from REQUEST_ROUTE. Example 1.11. proxy_authorize usage ... if (!proxy_authorize("", "subscriber)) { proxy_challenge("", "1"); # Realm will be autogenerated }; ... opensips-2.2.2/modules/auth_db/aaa_avps.h000066400000000000000000000072631300170765700203750ustar00rootroot00000000000000/* * Copyright (C) 2005 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-05-31 created by bogdan */ #ifndef _OPENSIPS_AAA_AVPS_H_ #define _OPENSIPS_AAA_AVPS_H_ #include "../../mem/mem.h" #include "../../dprint.h" #include "../../trim.h" #include "../../pvar.h" #include struct aaa_avp { int avp_name; unsigned short avp_type; str attr_name; struct aaa_avp *next; }; static inline void free_aaa_avp(struct aaa_avp *avp) { if (avp) { if (avp->attr_name.s) pkg_free(avp->attr_name.s); pkg_free(avp); } } static inline void free_aaa_avp_list(struct aaa_avp *avp) { struct aaa_avp *tmp; while (avp) { tmp = avp; avp = avp->next; free_aaa_avp( tmp ); } } static inline int parse_aaa_avps(char *definition, struct aaa_avp **avp_def, int *cnt) { struct aaa_avp *avp; int avp_name = -1; pv_spec_t avp_spec; str foo; char *p; char *e; char *s; char t; p = definition; *avp_def = 0; *cnt = 0; if (p==0 || *p==0) return 0; /* get element by element */ while ( (e=strchr(p,';'))!=0 || (e=p+strlen(p))!=p ) { /* new aaa_avp struct */ if ( (avp=(struct aaa_avp*)pkg_malloc(sizeof(struct aaa_avp)))==0 ) { LM_ERR("no more pkg mem\n"); goto error; } memset( avp, 0, sizeof(struct aaa_avp)); /* definition is between p and e */ if ( (s=strchr(p,'='))!=0 && savp_type)!=0) { LM_ERR("[%s]- invalid AVP definition\n", foo.s); goto parse_error; } foo.s[foo.len] = t; /* copy the avp name into the avp structure */ avp->avp_name = avp_name; /* go to after the equal sign */ p = s+1; } /* attr - is between p and e*/ foo.s = p; foo.len = e-p; trim( &foo ); if (foo.len==0) goto parse_error; /* copy the attr into the avp structure */ avp->attr_name.s = (char*)pkg_malloc( foo.len+1 ); if (avp->attr_name.s==0) { LM_ERR("no more pkg mem\n"); goto error; } avp->attr_name.len = foo.len; memcpy( avp->attr_name.s, foo.s, foo.len ); avp->attr_name.s[foo.len] = 0; /* was an avp name specified? */ if (avp_name < 0) { if (parse_avp_spec(&avp->attr_name, &avp->avp_name) < 0) { LM_ERR("cannot get avp ip\n"); goto error; } } /* link the element */ avp->next = *avp_def; *avp_def = avp; (*cnt)++; avp = 0; avp_name = -1; /* go to the end */ p = e; if (*p==';') p++; if (*p==0) break; } return 0; parse_error: LM_ERR("parse failed in \"%s\" at pos %d(%s)\n", definition, (int)(long)(p-definition),p); error: free_aaa_avp( avp ); free_aaa_avp_list( *avp_def ); *avp_def = 0; *cnt = 0; return -1; } #endif opensips-2.2.2/modules/auth_db/authdb_mod.c000066400000000000000000000156671300170765700207320ustar00rootroot00000000000000/* * Digest Authentication Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-26: checks and group moved to separate modules (janakj) * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) * 2003-04-05: default_uri #define used (jiri) * 2004-06-06 cleanup: static & auth_db_{init,bind,close.ver} used (andrei) * 2005-05-31 general definition of AVPs in credentials now accepted - ID AVP, * STRING AVP, AVP aliases (bogdan) * 2006-03-01 pseudo variables support for domain name (bogdan) */ #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../mod_fix.h" #include "../../mem/mem.h" #include "../auth/api.h" #include "../signaling/signaling.h" #include "aaa_avps.h" #include "authorize.h" #define TABLE_VERSION 7 /* * Module destroy function prototype */ static void destroy(void); /* * Module child-init function prototype */ static int child_init(int rank); /* * Module initialization function prototype */ static int mod_init(void); static int auth_fixup(void** param, int param_no); /** SIGNALING binds */ struct sig_binds sigb; #define USER_COL "username" #define USER_COL_LEN (sizeof(USER_COL) - 1) #define DOMAIN_COL "domain" #define DOMAIN_COL_LEN (sizeof(DOMAIN_COL) - 1) #define PASS_COL "ha1" #define PASS_COL_LEN (sizeof(PASS_COL) - 1) #define PASS_COL_2 "ha1b" #define PASS_COL_2_LEN (sizeof(PASS_COL_2) - 1) #define DEFAULT_CRED_LIST "rpid" /* * Module parameter variables */ static str db_url = {NULL,0}; str user_column = {USER_COL, USER_COL_LEN}; str domain_column = {DOMAIN_COL, DOMAIN_COL_LEN}; str pass_column = {PASS_COL, PASS_COL_LEN}; str pass_column_2 = {PASS_COL_2, PASS_COL_2_LEN}; int calc_ha1 = 0; int use_domain = 0; /* Use also domain when looking up in table */ db_con_t* auth_db_handle = 0; /* database connection handle */ db_func_t auth_dbf; auth_api_t auth_api; char *credentials_list = DEFAULT_CRED_LIST; struct aaa_avp *credentials = 0; /* Parsed list of credentials to load */ int credentials_n = 0; /* Number of credentials in the list */ int skip_version_check = 0; /* skips version check for custom db */ /* * Exported functions */ static cmd_export_t cmds[] = { {"www_authorize", (cmd_function)www_authorize, 2, auth_fixup, 0, REQUEST_ROUTE}, {"proxy_authorize", (cmd_function)proxy_authorize, 2, auth_fixup, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &db_url.s }, {"user_column", STR_PARAM, &user_column.s }, {"domain_column", STR_PARAM, &domain_column.s }, {"password_column", STR_PARAM, &pass_column.s }, {"password_column_2", STR_PARAM, &pass_column_2.s }, {"calculate_ha1", INT_PARAM, &calc_ha1 }, {"use_domain", INT_PARAM, &use_domain }, {"load_credentials", STR_PARAM, &credentials_list }, {"skip_version_check",INT_PARAM, &skip_version_check }, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "auth", DEP_ABORT }, { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* * Module interface */ struct module_exports exports = { "auth_db", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; static int child_init(int rank) { auth_db_handle = auth_dbf.init(&db_url); if (auth_db_handle == 0){ LM_ERR("unable to connect to the database\n"); return -1; } return 0; } static int mod_init(void) { bind_auth_t bind_auth; LM_INFO("initializing...\n"); init_db_url( db_url , 0 /*cannot be null*/); user_column.len = strlen(user_column.s); domain_column.len = strlen(domain_column.s); pass_column.len = strlen(pass_column.s); pass_column_2.len = strlen(pass_column_2.s); /* Find a database module */ if (db_bind_mod(&db_url, &auth_dbf) < 0){ LM_ERR("unable to bind to a database driver\n"); return -1; } /* bind to auth module and import the API */ bind_auth = (bind_auth_t)find_export("bind_auth", 0, 0); if (!bind_auth) { LM_ERR("unable to find bind_auth function. Check if you load the auth module.\n"); return -2; } if (bind_auth(&auth_api) < 0) { LM_ERR("unable to bind auth module\n"); return -3; } /* load SIGNALING API */ if(load_sig_api(&sigb)< 0) { LM_ERR("can't load signaling functions\n"); return -1; } /* process additional list of credentials */ if (parse_aaa_avps( credentials_list, &credentials, &credentials_n)!=0) { LM_ERR("failed to parse credentials\n"); return -5; } return 0; } static void destroy(void) { if (auth_db_handle) { auth_dbf.close(auth_db_handle); auth_db_handle = 0; } if (credentials) { free_aaa_avp_list(credentials); credentials = 0; credentials_n = 0; } } /* * Convert the char* parameters */ static int auth_fixup(void** param, int param_no) { db_con_t* dbh = NULL; str name; if (param_no == 1) { return fixup_spve_null(param, 1); } else if (param_no == 2) { name.s = (char*)*param; name.len = strlen(name.s); dbh = auth_dbf.init(&db_url); if (!dbh) { LM_ERR("unable to open database connection\n"); return -1; } if(skip_version_check == 0 && db_check_table_version(&auth_dbf, dbh, &name, TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); auth_dbf.close(dbh); return -1; } } auth_dbf.close(dbh); return 0; } opensips-2.2.2/modules/auth_db/authdb_mod.h000066400000000000000000000035001300170765700207160ustar00rootroot00000000000000/* * Digest Authentication - Database support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AUTHDB_MOD_H #define AUTHDB_MOD_H #include "../../str.h" #include "../../db/db.h" #include "../auth/api.h" #include "../signaling/signaling.h" #include "../../parser/msg_parser.h" /* * Module parameters variables */ extern str user_column; /* 'username' column name */ extern str domain_column; /* 'domain' column name */ extern str pass_column; /* 'password' column name */ extern str pass_column_2; /* Column containing HA1 string constructed * of user@domain username */ extern int calc_ha1; /* if set to 1, ha1 is calculated by the server */ extern int use_domain; /* If set to 1 then the domain will be used when selecting a row */ extern db_con_t* auth_db_handle; /* database connection handle */ extern db_func_t auth_dbf; extern auth_api_t auth_api; extern struct aaa_avp* credentials; extern int credentials_n; /* * Pointer to reply function in stateless module */ extern struct sig_binds sigb; #endif /* AUTHDB_MOD_H */ opensips-2.2.2/modules/auth_db/authorize.c000066400000000000000000000164141300170765700206250ustar00rootroot00000000000000/* * Digest Authentication - Database support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * history: * --------- * 2003-02-28 scratchpad compatibility abandoned * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2004-06-06 updated to the new DB api, added auth_db_{init,bind,close,ver} * (andrei) * 2005-05-31 general definition of AVPs in credentials now accepted - ID AVP, * STRING AVP, AVP aliases (bogdan) * 2006-03-01 pseudo variables support for domain name (bogdan) * 2009-01-25 added prepared statements support in running the DB queries * (bogdan) */ #include #include "../../ut.h" #include "../../str.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../parser/digest/digest.h" #include "../../parser/hf.h" #include "../../parser/parser_f.h" #include "../../usr_avp.h" #include "../../mod_fix.h" #include "../../mem/mem.h" #include "aaa_avps.h" #include "authdb_mod.h" static str auth_500_err = str_init("Server Internal Error"); static inline int get_ha1(struct username* _username, str* _domain, const str* _table, char* _ha1, db_res_t** res) { struct aaa_avp *cred; db_key_t keys[2]; db_val_t vals[2]; db_key_t *col; str result; static db_ps_t auth_ha1_ps = NULL; static db_ps_t auth_ha1b_ps = NULL; int n, nc; col = pkg_malloc(sizeof(*col) * (credentials_n + 1)); if (col == NULL) { LM_ERR("no more pkg memory\n"); return -1; } keys[0] = &user_column; keys[1] = &domain_column; /* should we calculate the HA1, and is it calculated with domain? */ if (_username->domain.len && !calc_ha1) { col[0] = &pass_column_2 ; CON_PS_REFERENCE(auth_db_handle) = &auth_ha1b_ps; } else { col[0] = &pass_column; CON_PS_REFERENCE(auth_db_handle) = &auth_ha1_ps; } for (n = 0, cred=credentials; cred ; n++, cred=cred->next) { col[1 + n] = &cred->attr_name; } VAL_TYPE(vals) = VAL_TYPE(vals + 1) = DB_STR; VAL_NULL(vals) = VAL_NULL(vals + 1) = 0; VAL_STR(vals).s = _username->user.s; VAL_STR(vals).len = _username->user.len; if (_username->domain.len) { VAL_STR(vals + 1) = _username->domain; } else { VAL_STR(vals + 1) = *_domain; } if (auth_dbf.use_table(auth_db_handle, _table) < 0) { LM_ERR("failed to use_table\n"); pkg_free(col); return -1; } n = (use_domain ? 2 : 1); nc = 1 + credentials_n; if (auth_dbf.query(auth_db_handle, keys, 0, vals, col, n, nc, 0, res) < 0) { LM_ERR("failed to query database\n"); pkg_free(col); return -1; } pkg_free(col); if (RES_ROW_N(*res) == 0) { LM_DBG("no result for user \'%.*s@%.*s\'\n", _username->user.len, ZSW(_username->user.s), (use_domain ? (_domain->len) : 0), ZSW(_domain->s)); return 1; } result.s = (char*)ROW_VALUES(RES_ROWS(*res))[0].val.string_val; result.len = strlen(result.s); if (calc_ha1) { /* Only plaintext passwords are stored in database, * we have to calculate HA1 */ auth_api.calc_HA1(HA_MD5, &_username->whole, _domain, &result, 0, 0, _ha1); LM_DBG("HA1 string calculated: %s\n", _ha1); } else { memcpy(_ha1, result.s, result.len); _ha1[result.len] = '\0'; } return 0; } /* * Generate AVPs from the database result */ static int generate_avps(db_res_t* result) { struct aaa_avp *cred; int_str ivalue; int i; for (cred=credentials, i=1; cred; cred=cred->next, i++) { switch (result->col.types[i]) { case DB_STR: ivalue.s = VAL_STR(&(result->rows[0].values[i])); if (VAL_NULL(&(result->rows[0].values[i])) || ivalue.s.s == NULL || ivalue.s.len==0) continue; if (add_avp(cred->avp_type|AVP_VAL_STR,cred->avp_name,ivalue)!=0){ LM_ERR("failed to add AVP\n"); return -1; } LM_DBG("set string AVP %d = \"%.*s\"\n", cred->avp_name, ivalue.s.len, ZSW(ivalue.s.s)); break; case DB_STRING: ivalue.s.s = (char*)VAL_STRING(&(result->rows[0].values[i])); if (VAL_NULL(&(result->rows[0].values[i])) || ivalue.s.s == NULL || (ivalue.s.len=strlen(ivalue.s.s))==0 ) continue; if (add_avp(cred->avp_type|AVP_VAL_STR,cred->avp_name,ivalue)!=0){ LM_ERR("failed to add AVP\n"); return -1; } LM_DBG("set string AVP %d = \"%.*s\"\n", cred->avp_name, ivalue.s.len, ZSW(ivalue.s.s)); break; case DB_INT: if (VAL_NULL(&(result->rows[0].values[i]))) continue; ivalue.n = (int)VAL_INT(&(result->rows[0].values[i])); if (add_avp(cred->avp_type, cred->avp_name, ivalue)!=0) { LM_ERR("failed to add AVP\n"); return -1; } LM_DBG("set int AVP %d = %d\n",cred->avp_name, ivalue.n); break; default: LM_ERR("subscriber table column %d `%.*s' has unsupported type. " "Only string/str or int columns are supported by" "load_credentials.\n", i, result->col.names[i]->len, result->col.names[i]->s); break; } } return 0; } /* * Authorize digest credentials */ static inline int authorize(struct sip_msg* _m, gparam_p _realm, char* _table, hdr_types_t _hftype) { char ha1[256]; int res; struct hdr_field* h; auth_body_t* cred; auth_result_t ret; str domain, table; db_res_t* result = NULL; if(!_table) { LM_ERR("invalid table parameter\n"); return -1; } table.s = _table; table.len = strlen(_table); if(fixup_get_svalue(_m, _realm, &domain)!=0) { LM_ERR("invalid realm parameter\n"); return AUTH_ERROR; } if (domain.len==0) domain.s = 0; ret = auth_api.pre_auth(_m, &domain, _hftype, &h); if (ret != DO_AUTHORIZATION) return ret; cred = (auth_body_t*)h->parsed; res = get_ha1(&cred->digest.username, &domain, &table, ha1, &result); if (res < 0) { /* Error while accessing the database */ if (sigb.reply(_m, 500, &auth_500_err, NULL) == -1) { LM_ERR("failed to send 500 reply\n"); } return ERROR; } if (res > 0) { /* Username not found in the database */ auth_dbf.free_result(auth_db_handle, result); return USER_UNKNOWN; } /* Recalculate response, it must be same to authorize successfully */ if (!auth_api.check_response(&(cred->digest), &_m->first_line.u.request.method, ha1)) { ret = auth_api.post_auth(_m, h); if (ret == AUTHORIZED) generate_avps(result); auth_dbf.free_result(auth_db_handle, result); return ret; } auth_dbf.free_result(auth_db_handle, result); return INVALID_PASSWORD; } /* * Authorize using Proxy-Authorize header field */ int proxy_authorize(struct sip_msg* _m, char* _realm, char* _table) { return authorize(_m, (gparam_p)_realm, _table, HDR_PROXYAUTH_T); } /* * Authorize using WWW-Authorize header field */ int www_authorize(struct sip_msg* _m, char* _realm, char* _table) { return authorize(_m, (gparam_p)_realm, _table, HDR_AUTHORIZATION_T); } opensips-2.2.2/modules/auth_db/authorize.h000066400000000000000000000024521300170765700206270ustar00rootroot00000000000000/* * Digest Authentication - Database support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AUTHORIZE_H #define AUTHORIZE_H #include "../../parser/msg_parser.h" int auth_db_init(const str* db_url); int auth_db_bind(const str* db_url); void auth_db_close(); /* * Authorize using Proxy-Authorization header field */ int proxy_authorize(struct sip_msg* _msg, char* _realm, char* _table); /* * Authorize using WWW-Authorization header field */ int www_authorize(struct sip_msg* _msg, char* _realm, char* _table); #endif /* AUTHORIZE_H */ opensips-2.2.2/modules/auth_db/doc/000077500000000000000000000000001300170765700172065ustar00rootroot00000000000000opensips-2.2.2/modules/auth_db/doc/auth_db.xml000066400000000000000000000024721300170765700213430ustar00rootroot00000000000000 %docentities; ]> Auth_db Module Jan Janak FhG Fokus jan@iptel.org Jakob Schlyter jakob@schlyter.se Bogdan-Andrei Iancu bogdan@opensips.org Jan Janak jan@iptel.org 2002 2003 FhG FOKUS 2005 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/auth_db/doc/auth_db_admin.xml000066400000000000000000000341611300170765700225130ustar00rootroot00000000000000 &adminguide;
Overview This module contains all authentication related functions that need the access to the database. This module should be used together with auth module, it cannot be used independently because it depends on the module. Select this module if you want to use database to store authentication information like subscriber usernames and passwords. If you want to use radius authentication, then use auth_radius instead.
Dependencies
&osips; Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): auth -- Generic authentication functions database -- Any database module (currently mysql, postgres, dbtext)
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters
<varname>db_url</varname> (string) This is URL of the database to be used. Value of the parameter depends on the database module used. For example for mysql and postgres modules this is something like mysql://username:password@host:port/database. For dbtext module (which stores data in plaintext files) it is directory in which the database resides. Default value is &defaultrodb;. <varname>db_url</varname> parameter usage modparam("auth_db", "db_url", "&exampledb;")
<varname>user_column</varname> (string) This is the name of the column holding usernames. Default value is fine for most people. Use the parameter if you really need to change it. Default value is username. <varname>user_column</varname> parameter usage modparam("auth_db", "user_column", "user")
<varname>domain_column</varname> (string) This is the name of the column holding domains of users. Default value is fine for most people. Use the parameter if you really need to change it. Default value is domain. <varname>domain_column</varname> parameter usage modparam("auth_db", "domain_column", "domain")
<varname>password_column</varname> (string) This is the name of the column holding passwords. Passwords can be either stored as plain text or pre-calculated HA1 strings. HA1 strings are MD5 hashes of username, password, and realm. HA1 strings are more safe because the server doesn't need to know plaintext passwords and they cannot be obtained from HA1 strings. Default value is ha1. <varname>password_column</varname> parameter usage modparam("auth_db", "password_column", "password")
<varname>password_column_2</varname> (string) As described in the previous section this parameter contains name of column holding pre-calculated HA1 string that were calculated including the domain in the username. This parameter is used only when calculate_ha1 is set to 0 and user agent send a credentials containing the domain in the username. Default value of the parameter is ha1b. <varname>password_column_2</varname> parameter usage modparam("auth_db", "password_column_2", "ha1_2")
<varname>calculate_ha1</varname> (integer) This parameter tells the server whether it should considered the loaded password (for authentification) as plaintext passwords or a pre-calculated HA1 string. Possible meanings of this parameter are: 1 (calculate HA1) - the loaded password is a plaintext password, so OpenSIPS will internally calculate the HA1. As the passwors will be loaded from the column specified in the password_column parameter, be sure this parameter points to a column holding a plaintext password (by default, this parameter points to ha1 column); 0 (do NOT calculate HA1) - the loaded password is an already computed HA1 value, so OpenSIPS does not have do any further computing (for HA1 value). Depending on the presence of a @domain part (some user agents append the domain to the username credentials parameter too), the modules will load the password (pre-computed HA1) from the password_column_2 column (if domain present) or from the password_column column (if domain not present). Usually, most of the UAs do NOT include a domain part in the username credentials parameter. The password_column_2 column contains also HA1 strings but they should be calculated including the domain in the username parameter (as opposed to password_column which (when containing HA1 strings) should always contain HA1 strings calculated without domain in username. This ensures that the authentication will always work when using pre-calculated HA1 strings, not depending on the presence of the domain in username. Default value of this parameter is 0. <varname>calculate_ha1</varname> parameter usage modparam("auth_db", "calculate_ha1", 1)
<varname>use_domain</varname> (integer) If true (not 0), domain will be also used when looking up in the subscriber table. If you have a multi-domain setup, it is strongly recommended to turn on this parameter to avoid username overlapping between domains. IMPORTANT: before turning on this parameter, be sure that the domain column in subscriber table is properly populated. Default value is 0 (false). <varname>use_domain</varname> parameter usage modparam("auth_db", "use_domain", 1)
<varname>load_credentials</varname> (string) This parameter specifies credentials to be fetched from database when the authentication is performed. The loaded credentials will be stored in AVPs. If the AVP name is not specificaly given, it will be used a NAME AVP with the same name as the column name. Parameter syntax: load_credentials = credential (';' credential)* credential = (avp_specification '=' column_name) | (column_name) avp_specification = '$avp(' + NAME + ')' Default value of this parameter is rpid. <varname>load_credentials</varname> parameter usage # load rpid column into $avp(13) and email_address column # into $avp(email_address) modparam("auth_db", "load_credentials", "$avp(13)=rpid;email_address")
<varname>skip_version_check</varname> (int) This parameter specifies not to check the auth table version. This parameter should be set when a custom authentication table is used. Default value is 0 (false). <varname>skip_version_check</varname> parameter usage modparam("auth_db", "skip_version_check", 1)
Exported Functions
<function moreinfo="none">www_authorize(realm, table)</function> The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call www_challenge which will challenge the user again. Negative codes may be interpreted as follows: -5 (generic error) - some generic error occurred and no reply was sent out; -4 (no credentials) - credentials were not found in request; -3 (stale nonce) - stale nonce; -2 (invalid password) - valid user, but wrong password; -1 (invalid user) - authentication user does not exist. Meaning of the parameters is as follows: realm - Realm is an opaque string that the user agent should present to the user so it can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string is used then the server will generate it from the request. In case of REGISTER requests To header field domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. The string may contain pseudo variables. table - Table to be used to lookup usernames and passwords (usually subscribers table). This function can be used from REQUEST_ROUTE. <function moreinfo="none">www_authorize</function> usage ... if (!www_authorize("siphub.net", "subscriber")) { www_challenge("siphub.net", "1"); }; ...
<function moreinfo="none">proxy_authorize(realm, table)</function> The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call proxy_challenge which will challenge the user again. Negative codes may be interpreted as follows: -5 (generic error) - some generic error occurred and no reply was sent out; -4 (no credentials) - credentials were not found in request; -3 (stale nonce) - stale nonce; -2 (invalid password) - valid user, but wrong password; -1 (invalid user) - authentication user does not exist. Meaning of the parameters is as follows: realm - Realm is an opaque string that the user agent should present to the user so it can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string is used then the server will generate it from the request. From header field domain will be used as realm. The string may contain pseudo variables. table - Table to be used to lookup usernames and passwords (usually subscribers table). This function can be used from REQUEST_ROUTE. proxy_authorize usage ... if (!proxy_authorize("", "subscriber)) { proxy_challenge("", "1"); # Realm will be autogenerated }; ...
opensips-2.2.2/modules/avpops/000077500000000000000000000000001300170765700163435ustar00rootroot00000000000000opensips-2.2.2/modules/avpops/Makefile000066400000000000000000000003211300170765700177770ustar00rootroot00000000000000# $Id$ # # avpops module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=avpops.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/avpops/README000066400000000000000000000563121300170765700172320ustar00rootroot00000000000000AVPops Module Ramona-Elena Modroiu Edited by Ramona-Elena Modroiu Copyright © 2004-2008 Voice Sistem SRL Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. AVP naming format 1.4. Exported Parameters 1.4.1. db_url (string) 1.4.2. avp_table (string) 1.4.3. use_domain (integer) 1.4.4. uuid_column (string) 1.4.5. username_column (string) 1.4.6. domain_column (string) 1.4.7. attribute_column (string) 1.4.8. value_column (string) 1.4.9. type_column (string) 1.4.10. db_scheme (string) 1.4.11. buf_size (integer) 1.5. Exported Functions 1.5.1. avp_db_load(source, name[, db_id[, prefix]]) 1.5.2. avp_db_store(source,name[,db_id]) 1.5.3. avp_db_delete(source,name[,db_id]) 1.5.4. avp_db_query(query[[,dest],db_id]) 1.5.5. avp_delete(name) 1.5.6. avp_pushto(destination,name) 1.5.7. avp_check(name,op_value) 1.5.8. avp_copy(old_name,new_name) 1.5.9. avp_printf(dest, format) 1.5.10. avp_subst(avps, subst) 1.5.11. avp_op(name,op_value) 1.5.12. is_avp_set(name) 1.5.13. avp_print() 1.5.14. avp_insert(avp_name, value, index) 1.6. Exported Asynchronous Functions 1.6.1. avp_db_query(query[[,dest],db_id]) List of Examples 1.1. AVP naming examples 1.2. Set avp_url parameter 1.3. Set avp_table parameter 1.4. Set use_domain parameter 1.5. Set uuid_column parameter 1.6. Set username_column parameter 1.7. Set domain_column parameter 1.8. Set attribute_column parameter 1.9. Set value_column parameter 1.10. Set type_column parameter 1.11. Set db_scheme parameter 1.12. Set buf_size parameter 1.13. avp_db_load usage 1.14. avp_db_store usage 1.15. avp_db_delete usage 1.16. avp_db_query usage 1.17. avp_delete usage 1.18. avp_pushto usage 1.19. avp_check usage 1.20. avp_copy usage 1.21. avp_printf usage 1.22. avp_subst usage 1.23. avp_op usage 1.24. is_avp_set usage 1.25. avp_print usage 1.26. avp_print usage 1.27. async avp_db_query usage Chapter 1. Admin Guide 1.1. Overview AVPops (AVP-operations) modules implements a set of script functions which allow access and manipulation of user AVPs (preferences) and pseudo-variables. AVPs are a powerful tool for implementing services/preferences per user/domain. Now they are usable directly from configuration script. Functions for interfacing DB resources (loading/storing/removing), functions for swapping information between AVPs and SIP messages, function for testing/checking the value of an AVP. AVPs are persistent per SIP transaction, being available in "route", "branch_route" and "failure_route". To make them available in "onreply_route" armed via TM module, set "onreply_avp_mode" parameter of TM module (note that in the default "onreply_route", the AVPs of the transaction are not available). 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * Optionally a database module 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None 1.3. AVP naming format The format of the parameters specifying an AVP in functions exported by this module is: $avp(avp_name). * avp_name = string | integer string - might be any alphanumeric string, wich contain following characters: [a-z] [A-Z] [0-9] '_' Example 1.1. AVP naming examples ... $avp(11) - the AVP identified by name 11 $avp(foo) - the AVP identified by the string 'foo' ... 1.4. Exported Parameters 1.4.1. db_url (string) DB URL for database connection. As the module allows the usage of multiple DBs (DB URLs), the actual DB URL may be preceded by an reference number. This reference number is to be passed to AVPOPS function that what to explicitly use this DB connection. If no reference number is given, 0 is assumed - this is the default DB URL. This parameter is optional, it's default value being NULL. Example 1.2. Set avp_url parameter ... # default URL modparam("avpops","db_url","mysql://user:passwd@host/database") # an additional DB URL modparam("avpops","db_url","1 postgres://user:passwd@host2/opensips") ... 1.4.2. avp_table (string) DB table to be used. This parameter is optional, it's default value being NULL. Example 1.3. Set avp_table parameter ... modparam("avpops","avp_table","avptable") ... 1.4.3. use_domain (integer) If the domain part of the an URI should be used for identifying an AVP in DB operations. Default value is 0 (no). Example 1.4. Set use_domain parameter ... modparam("avpops","use_domain",1) ... 1.4.4. uuid_column (string) Name of column containing the uuid (unique user id). Default value is “uuidâ€. Example 1.5. Set uuid_column parameter ... modparam("avpops","uuid_column","uuid") ... 1.4.5. username_column (string) Name of column containing the username. Default value is “usernameâ€. Example 1.6. Set username_column parameter ... modparam("avpops","username_column","username") ... 1.4.6. domain_column (string) Name of column containing the domain name. Default value is “domainâ€. Example 1.7. Set domain_column parameter ... modparam("avpops","domain_column","domain") ... 1.4.7. attribute_column (string) Name of column containing the attribute name (AVP name). Default value is “attributeâ€. Example 1.8. Set attribute_column parameter ... modparam("avpops","attribute_column","attribute") ... 1.4.8. value_column (string) Name of column containing the AVP value. Default value is “valueâ€. Example 1.9. Set value_column parameter ... modparam("avpops","value_column","value") ... 1.4.9. type_column (string) Name of column containing the AVP type. Default value is “typeâ€. Example 1.10. Set type_column parameter ... modparam("avpops","type_column","type") ... 1.4.10. db_scheme (string) Definition of a DB scheme to be used for non-standard access to Database information. Definition of a DB scheme. Scheme syntax is: * db_scheme = name':'element[';'element]* * element = + 'uuid_col='string + 'username_col='string + 'domain_col='string + 'value_col='string + 'value_type='('integer'|'string') + 'table='string Default value is “NULLâ€. Example 1.11. Set db_scheme parameter ... modparam("avpops","db_scheme", "scheme1:table=subscriber;uuid_col=uuid;value_col=first_name") ... 1.4.11. buf_size (integer) Allocated size for AVP variables. Default value is “1024â€. Example 1.12. Set buf_size parameter ... modparam("avpops", "buf_size", 1024) ... 1.5. Exported Functions 1.5.1. avp_db_load(source, name[, db_id[, prefix]]) Loads from DB into memory the AVPs corresponding to the given source. If given, it sets the script flags for loaded AVPs. It returns true if it loaded some values in AVPs, false otherwise (db error, no avp loaded ...). AVPs may be preceded by an optional prefix, in order to avoid some conflicts. Meaning of the parameters is as follows: * source - what info is used for identifying the AVPs. Parameter syntax: + source = (pvar|str_value) ['/'('username'|'domain'|'uri'|'uuid')]) + pvar = any pseudo variable defined in OpenSIPS. If the pvar is $ru (request uri), $fu (from uri), $tu (to uri) or $ou (original uri), then the implicit flag is 'uri'. Otherwise, the implicit flag is 'uuid'. * name - which AVPs will be loaded from DB into memory. Parameter syntax is: + name = avp_spec['/'(table_name|'$'db_scheme)] + avp_spec = matching_flags|$avp(avp_name)|$avp(avp_alias) + matching_flags = 'a' | 'A' | 'i' | 'I' | 's' | 'S' [script_flags] 'a' or 'A' means matching any of AVP name types ('i' and 's'), the rest have the meaning descriped in 'AVP naming format' chapter. * db_id - reference to a defined DB URL (a numerical id) - see the “db_url†module parameter. * prefix - static string which will precede the names of the AVPs populated by this function. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.13. avp_db_load usage ... avp_db_load("$fu", "$avp(678)"); avp_db_load("$ru/domain", "i/domain_preferences"); avp_db_load("$avp(uuid)", "$avp(404fwd)/fwd_table"); avp_db_load("$ru", "$avp(123)/$some_scheme"); # use DB URL id 3 avp_db_load("$ru", "$avp(1)", "3"); # precede all loaded AVPs by the "caller_" prefix avp_db_load("$ru", "$avp(100)", "", "caller_"); xlog("Loaded: $avp(caller_100)\n"); ... 1.5.2. avp_db_store(source,name[,db_id]) Stores to DB the AVPs corresponding to the given source. The meaning and usage of the parameters are identical as for avp_db_load(source,name) function. Please refer to its description. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.14. avp_db_store usage ... avp_db_store("$tu","$avp(678)"); avp_db_store("$ru/username","$avp(email)"); # use DB URL id 3 avp_db_store("$ru","$avp(1)","3"); ... 1.5.3. avp_db_delete(source,name[,db_id]) Deletes from DB the AVPs corresponding to the given source. The meaning and usage of the parameters are identical as for avp_db_load(source,name) function. Please refer to its description. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.15. avp_db_delete usage ... avp_db_delete("$tu","$avp(678)"); avp_db_delete("$ru/username","$avp(email)"); avp_db_delete("$avp(uuid)","$avp(404fwd)/fwd_table"); # use DB URL id 3 avp_db_delete("$ru","$avp(1)","3"); ... 1.5.4. avp_db_query(query[[,dest],db_id]) Make a database query and store the result in AVPs. The meaning and usage of the parameters: * query - must be a valid SQL query. The parameter can contain pseudo-variables. You must escape any pseudo-variables manually to prevent SQL injection attacks. You can use the existing transformations escape.common and unescape.common to escape and unescape the content of any pseudo-variable. Failing to escape the variables used in the query makes you vulnerable to SQL injection, e.g. make it possible for an outside attacker to alter your database content. The function returns true if the query was successful, -2 in case the query returned an empty result set, and -1 for all other types of errors * dest - a list with AVP names where to store the result. The format is “$avp(name1);$avp(name2);...â€. If this parameter is ommited, the result is stored in “$avp(1);$avp(2);...â€. If the result gives many rows, then multiple AVPs with corresponding name will be added. The value type of the AVP (string or integer) will be derived from the type of the columns. * db_id - reference to a defined DB URL (a numerical id) - see the “db_url†module parameter. It can be either a constant, or a string/int variable. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.16. avp_db_query usage ... avp_db_query("select password, ha1 from subscriber where username='$tu'" , "$avp(678);$avp(679)"); avp_db_query("delete from subscriber"); avp_db_query("delete from subscriber","","2"); $avp(id)=2;#also works $avp(id)="2" avp_db_query("delete from subscriber","","$avp(id)"); ... 1.5.5. avp_delete(name) Deletes from memory the AVPs with name or, if *, all AVPs. Meaning of the parameters is as follows: * name - which AVPs will be deleted from memory. Parameter syntax is: + name = (matching_flags|avp_name|avp_alias)['/'flag] + matching_flags = please refer to avp_db_load() function + flag = 'g'|'G' This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.17. avp_delete usage ... avp_delete("$avp(678)/g"); avp_delete("$avp(email)"); avp_delete("i"); avp_delete("a3"); ... 1.5.6. avp_pushto(destination,name) Pushes the value of AVP(s) into the SIP message. Meaning of the parameters is as follows: * destination - as what will be the AVP value pushed into SIP message. Parameter syntax: + destination = '$ru' ['/'('username'|'domain')] | '$du' | '$br' + $ru '['/'('username'|'domain')] - write the AVP in the request URI or in username/domain part of it + $du - write the AVP in 'dst_uri' field + $br - write the AVP directly as a new branch (does not affect RURI) * name - which AVP(s)/pseudo-variable should be pushed into the SIP message. Parameter syntax is: + name = ( avp_name | avp_alias | pvar_name )['/'flags] + flags = 'g' - effective only with AVPs This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.18. avp_pushto usage ... avp_pushto("$ru/domain","$fd"); avp_pushto("$ru","$avp(678)"); avp_pushto("$ru/domain","$avp(backup_domains)/g"); avp_pushto("$du","$avp(679)"); avp_pushto("$br","$avp(680)"); ... 1.5.7. avp_check(name,op_value) Checks the value of the AVP(s) against an operator and value. Meaning of the parameters is as follows: * name - which AVP(s) should be checked. Parameter syntax is: + name = ( pseudo-variable ) * op_value - define the operator, the value and flags for checking. Parameter syntax is: + op_value = operator '/' value ['/'flags] + operator = 'eq' | 'ne' | 'lt' | 'le' | 'gt' | 'ge' | 're' | 'fm' | 'and' | 'or' | 'xor' + value = pseudo-variable | fix_value + fix_value = 'i:'integer | 's:'string | string + flags = 'g' | 'G' | 'i' | 'I' Operator meaning: + eq - equal + ne - not equal + lt - less than + le - less or equal + gt - greater than + ge - greater or equal + re - regexp (regular exression match) + fm - fast match (see: man fnmatch) + and - bitwise 'and' + or - bitwise 'or' + xor - bitwise 'xor' Integer values can be given in hexadecimal using notation: 'i:0xhex_number' (e.g.,: 'i:0xabcd'); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.19. avp_check usage ... avp_check("$avp(678)", "lt/345/g"); avp_check("$fd","eq/$td/I"); avp_check("$avp(foo)","gt/$avp($bar)/g"); avp_check("$avp(foo)","re/sip:.*@bar.net/g"); avp_check("$avp(foo)","fm/$avp(fm_avp)/g"); ... 1.5.8. avp_copy(old_name,new_name) Copy / move an avp under a new name. Meaning of the parameters is as follows: * name1 - which AVP(s) should be copied/moved. Parameter syntax is: + name = ( avp_name | avp_alias ) * name2 - the new name of the copied/moved AVP(s). Parameter syntax is: + name = ( avp_name | avp_alias ) ['/'flags] + flags = 'g' | 'G' | 'd' | 'D' | 'n' | 'N' | 's' | 'S' This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.20. avp_copy usage ... avp_copy("$avp(678)", "$avp(345)/g"); avp_copy("$avp(old)","$avp(new)/gd"); ... 1.5.9. avp_printf(dest, format) NOTE: since OpenSIPS 1.3.0 the function has been moved to core and it is an alias to pv_printf(). Prints the formatted string 'format' in the AVP 'dest'. The 'format' parameter can include any pseudo-variable defined in OpenSIPS. The list with all pseudo-variables in OpenSIPS can be found at: http://opensips.org/dokuwiki/. Meaning of the parameters is as follows: * dest - in which AVP should be stored the result. Parameter syntax is: + name = ( avp_name | avp_alias ) * format - the formatted string to be printed in 'dest' AVP. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.21. avp_printf usage ... avp_printf("$avp(20)", "This is a $rm request with call-id $hdr(call-id) "); ... 1.5.10. avp_subst(avps, subst) Perl/sed-like subst applied to AVPs having string value. Meaning of the parameters is as follows: * avps - source AVP, destination AVP and flags. Parameter syntax is: + avps = src_avp [ '/' dst_avp [ '/' flags ] ] + src_avp = ( avp_name | avp_alias ) + dst_avp = ( avp_name | avp_alias ) - if dst_avp is missing then the value of src_avp will be replaced + flags = ( d | D | g | G ) -- (d, D - delete source avp; g, G - apply to all avps matching src_avp name) * subst - perl/sed-like reqular expression. Parameter syntax is: + subst = "/regexp/replacement/flags" + regexp - regular expression + replacement - replacement string, can include pseudo-variables and \1, ..., \9 for matching tokens, \0 for whole matching text + flags = 'g' | 'G' | 'i' | 'i' (g, G - replace all matching tokens; i, I - match ignore case) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.22. avp_subst usage ... # if avp 678 has a string value in e-mail format, replace the # domain part with the value of domain part from R-URI avp_subst("$avp(678)", "/(.*)@(.*)/\1@$rd/"); # if any avp 678 has a string value in e-mail format, replace the # domain part with the value of domain part from R-URI # and place the result in avp 679 avp_subst("$avp(678)/$avp(679)/g", "/(.*)@(.*)/\1@$rd/"); ... IMPORTANT NOTE: if the replacement string includes src_avp or dst_avp you will get something that you may not expect. In case you have many src_avp and you make the substitution to be applied to all of them, after the first src_avp is processed, it will be added in avp list and next processing will use it. 1.5.11. avp_op(name,op_value) Different integer operations with avps. Meaning of the parameters is as follows: * name - 'source_avp/destination_avp' - which AVP(s) should be processed and where to store the result. If 'destination_avp' is missing, same name as 'source_avp' is used to store the result. Parameter syntax is: + name = ( source_avp[/destination_avp] ) source_avp = ( avp_name | avp_alias ) destination_avp = ( avp_name | avp_alias ) * op_value - define the operation, the value and flags. Parameter syntax is: + op_value = operator '/' value ['/'flags] + operator = 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'and' | 'or' | 'xor' | 'not' + value = pseudo-variable | fix_value + fix_value = 'i:'integer + flags = 'g' | 'G' | 'd' | 'D' Integer values can be given in hexadecimal using notation 'i:0xhex_number' (e.g.,: 'i:0xabcd'); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.23. avp_op usage ... avp_op("$avp(678)", "add/345/g"); avp_op("$avp(number)","sub/$avp(number2)/d"); ... 1.5.12. is_avp_set(name) Check if any AVP with name is set. Meaning of the parameters is as follows: * name - name of AVP to look for. Parameter syntax is: + name = avp_name|avp_alias [ '/' flags ]) flags = ('e'|'s'|'n') - e = empty value; s = value string; n = value number (int) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.24. is_avp_set usage ... if(is_avp_set("$avp(678)")) log("AVP with integer id 678 exists\n"); ... 1.5.13. avp_print() Prints the list with all the AVPs from memory. This is only a helper/debug function. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.25. avp_print usage ... avp_print(); ... 1.5.14. avp_insert(avp_name, value, index) This function inserts an avp value at a certain position specified by the last parameter. If the index is greater than the count of values the value will be inserted at the end. Example 1.26. avp_print usage ... avp_insert("avp(20)", "$hdr(From)", 2); ... 1.6. Exported Asynchronous Functions 1.6.1. avp_db_query(query[[,dest],db_id]) Make a database query and store the result in AVPs. The meaning and usage of the parameters: * query - must be a valid SQL query. The parameter can contain pseudo-variables. You must escape any pseudo-variables manually to prevent SQL injection attacks. You can use the existing transformations escape.common and unescape.common to escape and unescape the content of any pseudo-variable. Failing to escape the variables used in the query makes you vulnerable to SQL injection, e.g. make it possible for an outside attacker to alter your database content. The function returns true if the query was successful, -2 in case the query returned an empty result set, and -1 for all other types of errors * dest - a list with AVP names where to store the result. The format is “$avp(name1);$avp(name2);...â€. If this parameter is ommited, the result is stored in “$avp(1);$avp(2);...â€. If the result gives many rows, then multiple AVPs with corresponding name will be added. The value type of the AVP (string or integer) will be derived from the type of the columns. * db_id - reference to a defined DB URL (a numerical id) - see the “db_url†module parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. Example 1.27. async avp_db_query usage ... { ... /* Example of a slow MySQL query - it should take around 5 seconds */ async( avp_db_query( "SELECT table_name, table_version, SLEEP(0.1) from versi on", "$avp(tb_name); $avp(tb_ver); $avp(retcode)"), my_resume_route); /* script execution is halted right after the async() call */ } /* We will be called when data is ready - meanwhile, the worker is free */ route [my_resume_route] { xlog("Results: \n$(avp(tb_name)[*])\n -------------------\n$(avp(tb_ver)[*])\n -------------------\n$(avp(retcode)[*])\n"); } ... opensips-2.2.2/modules/avpops/avpops.c000066400000000000000000001053201300170765700200200ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2004-10-04 first version (ramona) * 2004-11-15 added support for db schemes for avp_db_load (ramona) * 2004-11-17 aligned to new AVP core global aliases (ramona) * 2005-01-30 "fm" (fast match) operator added (ramona) * 2005-01-30 avp_copy (copy/move operation) added (ramona) */ #include #include #include #include /* for regex */ #include #include "../../sr_module.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../parser/parse_hname2.h" #include "../../str.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../mod_fix.h" #include "avpops_parse.h" #include "avpops_impl.h" #include "avpops_db.h" #define AVPDB "avp_db" typedef enum {GPARAM=0, URL} db_id_type; struct db_url_container { db_id_type type; union { struct db_url *url; gparam_p gp; } u; }; char *printbuf = NULL; /* modules param variables */ static str db_table = str_init("usr_preferences"); /* table */ static int use_domain = 0; /* if domain should be use for avp matching */ static str uuid_col = str_init("uuid"); static str attribute_col = str_init("attribute"); static str value_col = str_init("value"); static str type_col = str_init("type"); static str username_col = str_init("username"); static str domain_col = str_init("domain"); static str* db_columns[6] = {&uuid_col, &attribute_col, &value_col, &type_col, &username_col, &domain_col}; static int need_db=0; unsigned buf_size=1024; static int avpops_init(void); static int avpops_child_init(int rank); static int fixup_db_load_avp(void** param, int param_no); static int fixup_db_delete_avp(void** param, int param_no); static int fixup_db_store_avp(void** param, int param_no); static int fixup_db_query_avp(void** param, int param_no); static int fixup_async_db_query_avp(void** param, int param_no); static int fixup_delete_avp(void** param, int param_no); static int fixup_copy_avp(void** param, int param_no); static int fixup_pushto_avp(void** param, int param_no); static int fixup_check_avp(void** param, int param_no); static int fixup_op_avp(void** param, int param_no); static int fixup_subst(void** param, int param_no); static int fixup_is_avp_set(void** param, int param_no); static int fixup_insert_avp(void** param, int param_no); static int w_print_avps(struct sip_msg* msg, char* foo, char *bar); static int w_dbload_avps(struct sip_msg* msg, char* source, char* param, char *url, char *prefix); static int w_dbdelete_avps(struct sip_msg* msg, char* source, char* param, char* url); static int w_dbstore_avps(struct sip_msg* msg, char* source, char* param, char* url); static int w_dbquery_avps(struct sip_msg* msg, char* query, char* dest, char* url); static int w_async_dbquery_avps(struct sip_msg* msg, async_resume_module **rf, void **rparam, char* query, char* dest, char* url); static int w_delete_avps(struct sip_msg* msg, char* param, char *foo); static int w_copy_avps(struct sip_msg* msg, char* param, char *check); static int w_pushto_avps(struct sip_msg* msg, char* destination, char *param); static int w_check_avps(struct sip_msg* msg, char* param, char *check); static int w_op_avps(struct sip_msg* msg, char* param, char *op); static int w_subst(struct sip_msg* msg, char* src, char *subst); static int w_is_avp_set(struct sip_msg* msg, char* param, char *foo); static acmd_export_t acmds[] = { { "avp_db_query", (acmd_function)w_async_dbquery_avps, 1, fixup_async_db_query_avp }, { "avp_db_query", (acmd_function)w_async_dbquery_avps, 2, fixup_async_db_query_avp }, { "avp_db_query", (acmd_function)w_async_dbquery_avps, 3, fixup_async_db_query_avp }, { 0, 0, 0, 0 } }; /*! \brief * Exported functions */ static cmd_export_t cmds[] = { {"avp_print", (cmd_function)w_print_avps, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_load", (cmd_function)w_dbload_avps, 2, fixup_db_load_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_load", (cmd_function)w_dbload_avps, 3, fixup_db_load_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_load", (cmd_function)w_dbload_avps, 4, fixup_db_load_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_delete", (cmd_function)w_dbdelete_avps, 2, fixup_db_delete_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_delete", (cmd_function)w_dbdelete_avps, 3, fixup_db_delete_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_store", (cmd_function)w_dbstore_avps, 2, fixup_db_store_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_store", (cmd_function)w_dbstore_avps, 3, fixup_db_store_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_query", (cmd_function)w_dbquery_avps, 1, fixup_db_query_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_query", (cmd_function)w_dbquery_avps, 2, fixup_db_query_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_db_query", (cmd_function)w_dbquery_avps, 3, fixup_db_query_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_delete", (cmd_function)w_delete_avps, 1, fixup_delete_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_copy", (cmd_function)w_copy_avps, 2, fixup_copy_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_pushto", (cmd_function)w_pushto_avps, 2, fixup_pushto_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_check", (cmd_function)w_check_avps, 2, fixup_check_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_op", (cmd_function)w_op_avps, 2, fixup_op_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_subst", (cmd_function)w_subst, 2, fixup_subst, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"is_avp_set", (cmd_function)w_is_avp_set, 1, fixup_is_avp_set, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"avp_insert", (cmd_function)w_insert_avp, 3, fixup_insert_avp, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {0, 0, 0, 0, 0, 0} }; /*! \brief * Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM|USE_FUNC_PARAM, (void*)add_db_url }, {"avp_table", STR_PARAM, &db_table.s }, {"use_domain", INT_PARAM, &use_domain }, {"uuid_column", STR_PARAM, &uuid_col.s }, {"attribute_column", STR_PARAM, &attribute_col.s }, {"value_column", STR_PARAM, &value_col.s }, {"type_column", STR_PARAM, &type_col.s }, {"username_column", STR_PARAM, &username_col.s }, {"domain_column", STR_PARAM, &domain_col.s }, {"db_scheme", STR_PARAM|USE_FUNC_PARAM, (void*)avp_add_db_scheme }, {"buf_size", INT_PARAM, &buf_size}, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "db_url", get_deps_sqldb_url }, { NULL, NULL }, }, }; struct module_exports exports = { "avpops", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ acmds, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ avpops_init,/* Module initialization function */ (response_function) 0, (destroy_function) 0, (child_init_function) avpops_child_init /* per-child init function */ }; static int avpops_init(void) { int i; LM_INFO("initializing...\n"); if (db_table.s) db_table.len = strlen(db_table.s); uuid_col.len = strlen(uuid_col.s); attribute_col.len = strlen(attribute_col.s); value_col.len = strlen(value_col.s); type_col.len = strlen(type_col.s); username_col.len = strlen(username_col.s); domain_col.len = strlen(domain_col.s); /* search if any avp_db_* function is used */ for (i=0; cmds[i].name != NULL; i++) { if (strncasecmp(cmds[i].name, AVPDB, sizeof(AVPDB)-1) == 0 && (is_script_func_used(cmds[i].name, cmds[i].param_no))) { need_db=1; } } for (i=0; acmds[i].name != NULL; i++) { if (strncasecmp(acmds[i].name, AVPDB, sizeof(AVPDB)-1) == 0 && (is_script_async_func_used(acmds[i].name, acmds[i].param_no))) { need_db=1; } } if (need_db) { default_db_url = get_default_db_url(); if (default_db_url==NULL) { if (db_default_url==NULL) { LM_ERR("no DB URL provision into the module!\n"); return -1; } /* if nothing explicitly set as DB URL, add automatically * the default DB URL */ if (add_db_url(STR_PARAM, db_default_url)!=0) { LM_ERR("failed to use the default DB URL!\n"); return -1; } default_db_url = get_default_db_url(); if (default_db_url==NULL) { LM_BUG("Really ?!\n"); return -1; } } /* bind to the DB module */ if (avpops_db_bind()<0) goto error; init_store_avps(db_columns); } printbuf = (char*)pkg_malloc((buf_size+1)*sizeof(char)); if(printbuf==NULL) { LM_ERR("no pkg memory left\n"); return -1; } return 0; error: return -1; } static int avpops_child_init(int rank) { /* skip main process and TCP manager process */ if (!need_db || rank==PROC_MAIN || rank==PROC_TCP_MAIN) return 0; /* init DB connection */ return avpops_db_init(&db_table, db_columns); } static int fixup_db_url(void ** param, int require_raw_query, int is_async) { struct db_url* url; unsigned int ui; str s; s.s = (char*)*param; s.len = strlen(s.s); if(str2int(&s, &ui)!=0) { LM_ERR("bad db_url number <%s>\n", (char *)(*param)); return E_CFG; } url = get_db_url(ui); if (url==NULL) { LM_ERR("no db_url with id <%s>\n", (char *)(*param)); return E_CFG; } /* * Since mod_init() is run before function fixups, all DB structs * are initialized and all DB capabilities are populated */ if (require_raw_query && !DB_CAPABILITY(url->dbf, DB_CAP_RAW_QUERY)) { LM_ERR("driver for DB URL [%u] does not support raw queries\n", ui); return -1; } if (is_async && !DB_CAPABILITY(url->dbf, DB_CAP_ASYNC_RAW_QUERY)) LM_WARN("async() calls for DB URL [%u] will work " "in normal mode due to driver limitations\n", ui); pkg_free(*param); *param=(void *)url; return 0; } static int id2db_url(int id, int require_raw_query, int is_async, struct db_url** url) { *url = get_db_url((unsigned int)id); if (*url==NULL) { LM_ERR("no db_url with id <%d>\n", id); return E_CFG; } /* * Since mod_init() is run before function fixups, all DB structs * are initialized and all DB capabilities are populated */ if (require_raw_query && !DB_CAPABILITY((*url)->dbf, DB_CAP_RAW_QUERY)) { LM_ERR("driver for DB URL [%u] does not support raw queries\n", (unsigned int)id); return -1; } if (is_async && !DB_CAPABILITY((*url)->dbf, DB_CAP_ASYNC_RAW_QUERY)) LM_WARN("async() calls for DB URL [%u] will work " "in normal mode due to driver limitations\n", (unsigned int)id); return 0; } /* parse the name avp again when adding an avp name prefix (param 4) */ struct db_param *dbp_fixup; static int fixup_avp_prefix(void **param) { str st, *name, *prefix; char *p; prefix = pkg_malloc(sizeof(*prefix)); if (!prefix) { LM_ERR("No more pkg\n"); return -1; } prefix->s = (char *)*param; prefix->len = strlen(prefix->s); name = get_avp_name_id(dbp_fixup->a.u.sval.pvp.pvn.u.isname.name.n); if (name && dbp_fixup->a.type == AVPOPS_VAL_PVAR) { p = pkg_malloc(name->len + prefix->len + 7); if (!p) { LM_ERR("No more pkg mem!\n"); return -1; } memcpy(p, "$avp(", 5); memcpy(p + 5, prefix->s, prefix->len); memcpy(p + 5 + prefix->len, name->s, name->len); p[name->len + prefix->len + 5] = ')'; p[name->len + prefix->len + 6] = '\0'; st.s = p; st.len = prefix->len + name->len + 6; pv_parse_spec(&st, &dbp_fixup->a.u.sval); } *param = prefix; return 0; } static int fixup_db_avp(void** param, int param_no, int allow_scheme) { struct fis_param *sp; struct db_param *dbp; int flags; str s; char *p; if (default_db_url==NULL) { LM_ERR("no db url defined to be used by this function\n"); return E_CFG; } flags=0; s.s = (char*)*param; if (param_no==1) { /* prepare the fis_param structure */ sp = (struct fis_param*)pkg_malloc(sizeof(struct fis_param)); if (sp==0) { LM_ERR("no more pkg mem!\n"); return E_OUT_OF_MEM; } memset( sp, 0, sizeof(struct fis_param)); if ( (p=strchr(s.s,'/'))!=0) { *(p++) = 0; /* check for extra flags/params */ if (!strcasecmp("domain",p)) { flags|=AVPOPS_FLAG_DOMAIN0; } else if (!strcasecmp("username",p)) { flags|=AVPOPS_FLAG_USER0; } else if (!strcasecmp("uri",p)) { flags|=AVPOPS_FLAG_URI0; } else if (!strcasecmp("uuid",p)) { flags|=AVPOPS_FLAG_UUID0; } else { LM_ERR("unknown flag " "<%s>\n",p); return E_UNSPEC; } } if (*s.s!='$') { /* is a constant string -> use it as uuid*/ sp->opd = ((flags==0)?AVPOPS_FLAG_UUID0:flags)|AVPOPS_VAL_STR; sp->u.s.s = (char*)pkg_malloc(strlen(s.s)+1); if (sp->u.s.s==0) { LM_ERR("no more pkg mem!!\n"); return E_OUT_OF_MEM; } sp->u.s.len = strlen(s.s); strcpy(sp->u.s.s, s.s); } else { /* is a variable $xxxxx */ s.len = strlen(s.s); p = pv_parse_spec(&s, &sp->u.sval); if (p==0 || sp->u.sval.type==PVT_NULL || sp->u.sval.type==PVT_EMPTY) { LM_ERR("bad param 1; " "expected : $pseudo-variable or int/str value\n"); return E_UNSPEC; } if(sp->u.sval.type==PVT_RURI || sp->u.sval.type==PVT_FROM || sp->u.sval.type==PVT_TO || sp->u.sval.type==PVT_OURI) { sp->opd = ((flags==0)?AVPOPS_FLAG_URI0:flags)|AVPOPS_VAL_PVAR; } else { sp->opd = ((flags==0)?AVPOPS_FLAG_UUID0:flags)|AVPOPS_VAL_PVAR; } } *param=(void*)sp; } else if (param_no==2) { /* compose the db_param structure */ dbp = (struct db_param*)pkg_malloc(sizeof(struct db_param)); if (dbp==0) { LM_ERR("no more pkg mem!!!\n"); return E_OUT_OF_MEM; } memset( dbp, 0, sizeof(struct db_param)); if ( parse_avp_db( s.s, dbp, allow_scheme)!=0 ) { LM_ERR("parse failed\n"); return E_UNSPEC; } dbp_fixup = dbp; *param=(void*)dbp; } else if (param_no==3) { return fixup_db_url(param, 0, 0); } else if (param_no==4) { return fixup_avp_prefix(param); } return 0; } static int fixup_db_load_avp(void** param, int param_no) { return fixup_db_avp( param, param_no, 1/*allow scheme*/); } static int fixup_db_delete_avp(void** param, int param_no) { return fixup_db_avp( param, param_no, 0/*no scheme*/); } static int fixup_db_store_avp(void** param, int param_no) { return fixup_db_avp( param, param_no, 0/*no scheme*/); } /** * @is_async - if set, a warning will be thrown if the underlying * driver does not support async operations, and will run queries in sync mode */ static int __fixup_db_query_avp(void** param, int param_no, int is_async) { int type; pv_elem_t *model = NULL; pvname_list_t *anlist = NULL; str s; if (default_db_url==NULL) { LM_ERR("no db url defined to be used by this function\n"); return E_CFG; } s.s = (char*)(*param); if (param_no==1) { if(s.s==NULL) { LM_ERR("null format in P%d\n", param_no); return E_UNSPEC; } s.len = strlen(s.s); if(pv_parse_format(&s, &model)<0) { LM_ERR("wrong format[%s]\n", s.s); return E_UNSPEC; } *param = (void*)model; return 0; } else if(param_no==2) { if(s.s==NULL || s.s[0]==0) { *param = NULL; return 0; } s.len = strlen(s.s); anlist = parse_pvname_list(&s, PVT_AVP); if(anlist==NULL) { LM_ERR("bad format in P%d [%s]\n", param_no, s.s); return E_UNSPEC; } *param = (void*)anlist; return 0; } else if (param_no==3) { struct db_url_container *db_id; if (*param==NULL) return 0; if (fixup_igp(param) < 0) { LM_ERR("fixup failed for param #3!\n"); return -1; } db_id=pkg_malloc(sizeof(struct db_url_container)); if (db_id==NULL) { LM_ERR("no more pkg!\n"); return -1; } if ((type=((gparam_p)*param)->type) == GPARAM_TYPE_INT) { db_id->type = URL; if (id2db_url(((gparam_p)*param)->v.ival, 1, is_async, &db_id->u.url) < 0) { LM_ERR("failed to get db url!\n"); return E_CFG; } } else if (type == GPARAM_TYPE_STR) { unsigned int int_id; if (str2int(&((gparam_p)*param)->v.sval, &int_id) < 0) { LM_ERR("failed to get db id!\n"); return -1; } db_id->type = URL; if (id2db_url(int_id, 1, is_async, &db_id->u.url) < 0) { LM_ERR("failed to get db url!\n"); return E_CFG; } } else if(type==GPARAM_TYPE_PVS||type==GPARAM_TYPE_PVE){ db_id->type = GPARAM; db_id->u.gp = (gparam_p)*param; } else { LM_ERR("invalid value for param #3!\n"); return E_CFG; } *param = db_id; } return 0; } static int fixup_db_query_avp(void** param, int param_no) { return __fixup_db_query_avp(param, param_no, 0); } static int fixup_async_db_query_avp(void** param, int param_no) { return __fixup_db_query_avp(param, param_no, 1); } static int fixup_delete_avp(void** param, int param_no) { struct fis_param *ap=NULL; char *p; char *s; unsigned int flags; str s0; s = (char*)(*param); if (param_no==1) { /* attribute name / alias */ if ( (p=strchr(s,'/'))!=0 ) *(p++)=0; if(*s=='$') { /* is variable */ ap = avpops_parse_pvar(s); if (ap==0) { LM_ERR("unable to get" " pseudo-variable in param \n"); return E_UNSPEC; } if (ap->u.sval.type!=PVT_AVP) { LM_ERR("bad param; expected : $avp(name)\n"); return E_UNSPEC; } ap->opd|=AVPOPS_VAL_PVAR; ap->type = AVPOPS_VAL_PVAR; } else { if(strlen(s)<1) { LM_ERR("bad param - expected : $avp(name), *, s or i value\n"); return E_UNSPEC; } ap = (struct fis_param*)pkg_malloc(sizeof(struct fis_param)); if (ap==0) { LM_ERR(" no more pkg mem\n"); return E_OUT_OF_MEM; } memset(ap, 0, sizeof(struct fis_param)); ap->opd|=AVPOPS_VAL_NONE; switch(*s) { case 's': case 'S': ap->opd = AVPOPS_VAL_NONE|AVPOPS_VAL_STR; break; case 'i': case 'I': ap->opd = AVPOPS_VAL_NONE|AVPOPS_VAL_INT; break; case '*': case 'a': case 'A': ap->opd = AVPOPS_VAL_NONE; break; default: LM_ERR(" bad param - expected : *, s or i AVP flag\n"); pkg_free(ap); return E_UNSPEC; } /* flags */ flags = 0; if(*(s+1)!='\0') { s0.s = s+1; s0.len = strlen(s0.s); if(str2int(&s0, &flags)!=0) { LM_ERR("bad avp flags\n"); pkg_free(ap); return E_UNSPEC; } } ap->type = AVPOPS_VAL_INT; ap->u.n = flags<<8; } /* flags */ for( ; p&&*p ; p++ ) { switch (*p) { case 'g': case 'G': ap->ops|=AVPOPS_FLAG_ALL; break; default: LM_ERR(" bad flag <%c>\n",*p); if(ap!=NULL) pkg_free(ap); return E_UNSPEC; } } /* force some flags: if no avp name is given, force "all" flag */ if (ap->opd&AVPOPS_VAL_NONE) ap->ops |= AVPOPS_FLAG_ALL; *param=(void*)ap; } return 0; } static int fixup_copy_avp(void** param, int param_no) { struct fis_param *ap; char *s; char *p; s = (char*)*param; ap = 0; p = 0; if (param_no==2) { /* avp / flags */ if ( (p=strchr(s,'/'))!=0 ) *(p++)=0; } ap = avpops_parse_pvar(s); if (ap==0) { LM_ERR("unable to get pseudo-variable in P%d\n", param_no); return E_OUT_OF_MEM; } /* attr name is mandatory */ if (ap->u.sval.type!=PVT_AVP) { LM_ERR("you must specify only AVP as parameter\n"); return E_UNSPEC; } if (param_no==2) { /* flags */ for( ; p&&*p ; p++ ) { switch (*p) { case 'g': case 'G': ap->ops|=AVPOPS_FLAG_ALL; break; case 'd': case 'D': ap->ops|=AVPOPS_FLAG_DELETE; break; case 'n': case 'N': ap->ops|=AVPOPS_FLAG_CASTN; break; case 's': case 'S': ap->ops|=AVPOPS_FLAG_CASTS; break; default: LM_ERR("bad flag <%c>\n",*p); return E_UNSPEC; } } } *param=(void*)ap; return 0; } static int fixup_pushto_avp(void** param, int param_no) { struct fis_param *ap; char *s; char *p; s = (char*)*param; ap = 0; if (param_no==1) { if ( *s!='$') { LM_ERR("bad param 1; expected : $ru $du ...\n"); return E_UNSPEC; } /* compose the param structure */ if ( (p=strchr(s,'/'))!=0 ) *(p++)=0; ap = avpops_parse_pvar(s); if (ap==0) { LM_ERR("unable to get pseudo-variable in param 1\n"); return E_OUT_OF_MEM; } switch(ap->u.sval.type) { case PVT_RURI: ap->opd = AVPOPS_VAL_NONE|AVPOPS_USE_RURI; if ( p && !( (!strcasecmp("username",p) && (ap->opd|=AVPOPS_FLAG_USER0)) || (!strcasecmp("domain",p) && (ap->opd|=AVPOPS_FLAG_DOMAIN0)) )) { LM_ERR("unknown ruri flag \"%s\"!\n",p); return E_UNSPEC; } break; case PVT_DSTURI: if ( p!=0 ) { LM_ERR("unknown duri flag \"%s\"!\n",p); return E_UNSPEC; } ap->opd = AVPOPS_VAL_NONE|AVPOPS_USE_DURI; break; case PVT_HDR: /* what's the hdr destination ? request or reply? */ LM_ERR("push to header is obsolete - use append_hf() " "or append_to_reply() from textops module!\n"); return E_UNSPEC; break; case PVT_BRANCH: if ( p!=0 ) { LM_ERR("unknown branch flag \"%s\"!\n",p); return E_UNSPEC; } ap->opd = AVPOPS_VAL_NONE|AVPOPS_USE_BRANCH; break; default: LM_ERR("unsupported destination \"%s\"; " "expected $ru,$du,$br\n",s); return E_UNSPEC; } } else if (param_no==2) { /* attribute name*/ if ( *s!='$') { LM_ERR("bad param 1; expected :$pseudo-variable ...\n"); return E_UNSPEC; } /* compose the param structure */ if ( (p=strchr(s,'/'))!=0 ) *(p++)=0; ap = avpops_parse_pvar(s); if (ap==0) { LM_ERR("unable to get pseudo-variable in param 2\n"); return E_OUT_OF_MEM; } if (ap->u.sval.type==PVT_NULL) { LM_ERR("bad param 2; expected : $pseudo-variable ...\n"); pkg_free(ap); return E_UNSPEC; } ap->opd |= AVPOPS_VAL_PVAR; /* flags */ for( ; p&&*p ; p++ ) { switch (*p) { case 'g': case 'G': ap->ops|=AVPOPS_FLAG_ALL; break; default: LM_ERR("bad flag <%c>\n",*p); pkg_free(ap); return E_UNSPEC; } } } *param=(void*)ap; return 0; } static int fixup_check_avp(void** param, int param_no) { struct fis_param *ap; regex_t* re; char *s; s = (char*)*param; ap = 0; if (param_no==1) { ap = avpops_parse_pvar(s); if (ap==0) { LM_ERR(" unable to get pseudo-variable in P1\n"); return E_OUT_OF_MEM; } /* attr name is mandatory */ if (ap->u.sval.type==PVT_NULL) { LM_ERR("null pseudo-variable in P1\n"); return E_UNSPEC; } } else if (param_no==2) { if ( (ap=parse_check_value(s))==0 ) { LM_ERR(" failed to parse checked value \n"); return E_UNSPEC; } /* if REGEXP op -> compile the expression */ if (ap->ops&AVPOPS_OP_RE) { if ( (ap->opd&AVPOPS_VAL_STR)==0 ) { LM_ERR(" regexp operation requires string value\n"); return E_UNSPEC; } re = pkg_malloc(sizeof(regex_t)); if (re==0) { LM_ERR(" no more pkg mem\n"); return E_OUT_OF_MEM; } LM_DBG("compiling regexp <%.*s>\n", ap->u.s.len, ap->u.s.s); if (regcomp(re, ap->u.s.s, REG_EXTENDED|REG_ICASE|REG_NEWLINE)) { pkg_free(re); LM_ERR("bad re <%.*s>\n", ap->u.s.len, ap->u.s.s); return E_BAD_RE; } /* free the string and link the regexp */ // pkg_free(ap->sval.p.s); ap->u.s.s = (char*)re; } else if (ap->ops&AVPOPS_OP_FM) { if ( !( ap->opd&AVPOPS_VAL_PVAR || (!(ap->opd&AVPOPS_VAL_PVAR) && ap->opd&AVPOPS_VAL_STR) ) ) { LM_ERR(" fast_match operation requires string value or " "avp name/alias (%d/%d)\n", ap->opd, ap->ops); return E_UNSPEC; } } } *param=(void*)ap; return 0; } static int fixup_subst(void** param, int param_no) { struct subst_expr* se; str subst; struct fis_param *ap; struct fis_param **av; char *s; char *p; if (param_no==1) { s = (char*)*param; ap = 0; p = 0; av = (struct fis_param**)pkg_malloc(2*sizeof(struct fis_param*)); if(av==NULL) { LM_ERR("no more pkg memory\n"); return E_UNSPEC; } memset(av, 0, 2*sizeof(struct fis_param*)); /* avp src / avp dst /flags */ if ( (p=strchr(s,'/'))!=0 ) *(p++)=0; ap = avpops_parse_pvar(s); if (ap==0) { LM_ERR("unable to get pseudo-variable in param 2 [%s]\n", s); return E_OUT_OF_MEM; } if (ap->u.sval.type!=PVT_AVP) { LM_ERR("bad attribute name <%s>\n", (char*)*param); pkg_free(av); return E_UNSPEC; } /* attr name is mandatory */ if (ap->opd&AVPOPS_VAL_NONE) { LM_ERR("you must specify a name for the AVP\n"); return E_UNSPEC; } av[0] = ap; if(p==0 || *p=='\0') { *param=(void*)av; return 0; } /* dst || flags */ s = p; if(*s==PV_MARKER) { if ( (p=strchr(s,'/'))!=0 ) *(p++)=0; if(p==0 || (p!=0 && p-s>1)) { ap = avpops_parse_pvar(s); if (ap==0) { LM_ERR("unable to get pseudo-variable in param 2 [%s]\n",s); return E_OUT_OF_MEM; } if (ap->u.sval.type!=PVT_AVP) { LM_ERR("bad attribute name <%s>!\n", s); pkg_free(av); return E_UNSPEC; } /* attr name is mandatory */ if (ap->opd&AVPOPS_VAL_NONE) { LM_ERR("you must specify a name for the AVP!\n"); return E_UNSPEC; } av[1] = ap; } if(p==0 || *p=='\0') { *param=(void*)av; return 0; } } /* flags */ for( ; p&&*p ; p++ ) { switch (*p) { case 'g': case 'G': av[0]->ops|=AVPOPS_FLAG_ALL; break; case 'd': case 'D': av[0]->ops|=AVPOPS_FLAG_DELETE; break; default: LM_ERR("bad flag <%c>\n",*p); return E_UNSPEC; } } *param=(void*)av; } else if (param_no==2) { LM_DBG("%s fixing %s\n", exports.name, (char*)(*param)); subst.s=*param; subst.len=strlen(*param); se=subst_parser(&subst); if (se==0){ LM_ERR("%s: bad subst re %s\n",exports.name, (char*)*param); return E_BAD_RE; } /* don't free string -- needed for specifiers */ /* pkg_free(*param); */ /* replace it with the compiled subst. re */ *param=se; } return 0; } static int fixup_op_avp(void** param, int param_no) { struct fis_param *ap; struct fis_param **av; char *s; char *p; s = (char*)*param; ap = 0; if (param_no==1) { av = (struct fis_param**)pkg_malloc(2*sizeof(struct fis_param*)); if(av==NULL) { LM_ERR("no more pkg memory\n"); return E_UNSPEC; } memset(av, 0, 2*sizeof(struct fis_param*)); /* avp src / avp dst */ if ( (p=strchr(s,'/'))!=0 ) *(p++)=0; av[0] = avpops_parse_pvar(s); if (av[0]==0) { LM_ERR("unable to get pseudo-variable in param 1\n"); return E_OUT_OF_MEM; } if (av[0]->u.sval.type!=PVT_AVP) { LM_ERR("bad attribute name <%s>\n", (char*)*param); pkg_free(av); return E_UNSPEC; } if(p==0 || *p=='\0') { *param=(void*)av; return 0; } s = p; ap = avpops_parse_pvar(s); if (ap==0) { LM_ERR("unable to get pseudo-variable in param 1 (2)\n"); return E_OUT_OF_MEM; } if (ap->u.sval.type!=PVT_AVP) { LM_ERR("bad attribute name/alias <%s>!\n", s); pkg_free(av); return E_UNSPEC; } av[1] = ap; *param=(void*)av; return 0; } else if (param_no==2) { if ( (ap=parse_op_value(s))==0 ) { LM_ERR("failed to parse the value \n"); return E_UNSPEC; } /* only integer values or avps */ if ( (ap->opd&AVPOPS_VAL_STR)!=0 && (ap->opd&AVPOPS_VAL_PVAR)==0) { LM_ERR("operations requires integer values\n"); return E_UNSPEC; } *param=(void*)ap; return 0; } return -1; } static int fixup_is_avp_set(void** param, int param_no) { struct fis_param *ap; char *p; char *s; s = (char*)(*param); if (param_no==1) { /* attribute name | alias / flags */ if ( (p=strchr(s,'/'))!=0 ) *(p++)=0; ap = avpops_parse_pvar(s); if (ap==0) { LM_ERR("unable to get pseudo-variable in param\n"); return E_OUT_OF_MEM; } if (ap->u.sval.type!=PVT_AVP) { LM_ERR("bad attribute name <%s>\n", (char*)*param); return E_UNSPEC; } if(p==0 || *p=='\0') ap->ops|=AVPOPS_FLAG_ALL; /* flags */ for( ; p&&*p ; p++ ) { switch (*p) { case 'e': case 'E': ap->ops|=AVPOPS_FLAG_EMPTY; break; case 'n': case 'N': if(ap->ops&AVPOPS_FLAG_CASTS) { LM_ERR("invalid flag combination <%c> and 's|S'\n",*p); return E_UNSPEC; } ap->ops|=AVPOPS_FLAG_CASTN; break; case 's': case 'S': if(ap->ops&AVPOPS_FLAG_CASTN) { LM_ERR("invalid flag combination <%c> and 'n|N'\n",*p); return E_UNSPEC; } ap->ops|=AVPOPS_FLAG_CASTS; break; default: LM_ERR("bad flag <%c>\n",*p); return E_UNSPEC; } } *param=(void*)ap; } return 0; } static int fixup_insert_avp(void** param, int param_no) { pv_elem_t* pv_elem; str s; if(param_no== 0) return 0; if(!param) { LM_ERR( "null format\n"); return E_UNSPEC; } s.s = (char*)(*param); s.len = strlen(s.s); if(param_no == 3) /* the third argumet in an integer */ { unsigned int* index; index = (unsigned int*)pkg_malloc(sizeof(unsigned int)); if(index == NULL) { LM_ERR("No more memory\n"); return E_OUT_OF_MEM; } if(str2int(&s, index) < 0) { LM_ERR("Bad format for the third argument - must be a positive integer\n"); return E_UNSPEC; } *param = (void*)index; return 0; } if(pv_parse_format(&s, &pv_elem)<0) { LM_ERR( "wrong format[%s]\n",(char*)(*param)); return E_UNSPEC; } *param = (void*)pv_elem; /* attr name is mandatory */ if (param_no == 1 && pv_elem->spec.type!=PVT_AVP) { LM_ERR("The first parameter must be an AVP name\n"); return E_UNSPEC; } *param = (void*)pv_elem; return 0; } static int w_dbload_avps(struct sip_msg* msg, char* source, char* param, char *url, char *prefix) { return ops_dbload_avps ( msg, (struct fis_param*)source, (struct db_param*)param, url?(struct db_url*)url:default_db_url, use_domain, (str *)prefix); } static int w_dbdelete_avps(struct sip_msg* msg, char* source, char* param, char *url) { return ops_dbdelete_avps ( msg, (struct fis_param*)source, (struct db_param*)param, url?(struct db_url*)url:default_db_url, use_domain); } static int w_dbstore_avps(struct sip_msg* msg, char* source, char* param, char *url) { return ops_dbstore_avps ( msg, (struct fis_param*)source, (struct db_param*)param, url?(struct db_url*)url:default_db_url, use_domain); } static inline int get_url(struct sip_msg* msg, struct db_url_container* _url_struc, struct db_url** parsed_url, int is_async) { int id=0; unsigned int gp_flags; str sid; gparam_p gp; if (_url_struc) { if (_url_struc->type == GPARAM) { gp = _url_struc->u.gp; if (fixup_get_isvalue(msg, gp, &id, &sid, &gp_flags) < 0 || !(gp_flags&GPARAM_INT_VALUE_FLAG)) { LM_ERR("Failed to fetch PVAR str value!\n"); return -1; } if (id2db_url(id,1, is_async, parsed_url)) { LM_ERR("faild to get db url!\n"); return -1; } } else { *parsed_url = _url_struc->u.url; } } else { *parsed_url = default_db_url; } return 0; } static int w_dbquery_avps(struct sip_msg* msg, char* query, char* dest, char *url) { struct db_url *parsed_url; if (get_url(msg, (struct db_url_container*)url, &parsed_url, 0) < 0) { LM_ERR("failed to get db url\n"); return -1; } return ops_dbquery_avps ( msg, (pv_elem_t*)query, parsed_url, (pvname_list_t*)dest); } static int w_async_dbquery_avps(struct sip_msg* msg, async_resume_module **rf, void **rparam, char* query, char* dest, char* url) { struct db_url *parsed_url; if (get_url(msg, (struct db_url_container*)url, &parsed_url, 1) < 0) { LM_ERR("failed to get db url\n"); return -1; } return ops_async_dbquery(msg, rf, rparam, (pv_elem_t *)query, parsed_url, (pvname_list_t *)dest); } static int w_delete_avps(struct sip_msg* msg, char* param, char* foo) { return ops_delete_avp ( msg, (struct fis_param*)param); } static int w_copy_avps(struct sip_msg* msg, char* name1, char *name2) { return ops_copy_avp ( msg, (struct fis_param*)name1, (struct fis_param*)name2); } static int w_pushto_avps(struct sip_msg* msg, char* destination, char *param) { return ops_pushto_avp ( msg, (struct fis_param*)destination, (struct fis_param*)param); } static int w_check_avps(struct sip_msg* msg, char* param, char *check) { return ops_check_avp ( msg, (struct fis_param*)param, (struct fis_param*)check); } static int w_op_avps(struct sip_msg* msg, char* param, char *op) { return ops_op_avp ( msg, (struct fis_param**)param, (struct fis_param*)op); } static int w_subst(struct sip_msg* msg, char* src, char *subst) { return ops_subst(msg, (struct fis_param**)src, (struct subst_expr*)subst); } static int w_is_avp_set(struct sip_msg* msg, char* param, char *op) { return ops_is_avp_set(msg, (struct fis_param*)param); } static int w_print_avps(struct sip_msg* msg, char* foo, char *bar) { return ops_print_avp(); } opensips-2.2.2/modules/avpops/avpops_db.c000066400000000000000000000323561300170765700204750ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2004-10-04 first version (ramona) * 2004-11-11 added support for db schemes for avp_db_load (ramona) */ #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../dprint.h" #include "../../route.h" #include "avpops_parse.h" #include "avpops_db.h" static str def_table; /* default DB table */ static str **db_columns; /* array with names of DB columns */ static db_key_t keys_cmp[3]; /* array of keys and values used in selection */ static db_val_t vals_cmp[3]; /* statement as in "select" and "delete" */ /* linked list with all defined DB schemes */ static struct db_scheme *db_scheme_list=0; struct db_url *default_db_url = NULL; /* array of db urls */ static struct db_url *db_urls = NULL; /* array of database urls */ static unsigned int no_db_urls = 0; struct db_url* get_db_url(unsigned int idx) { unsigned int i; for (i=0;idbf, DB_CAP_RAW_QUERY)) { LM_ERR("driver for DB URL [default] does not support " "raw queries!\n"); return -1; } } if (is_script_async_func_used("avp_db_query", 1) || is_script_async_func_used("avp_db_query", 2)) { if (!DB_CAPABILITY(default_db_url->dbf, DB_CAP_ASYNC_RAW_QUERY)) LM_WARN("async() calls for DB URL [default] will work " "in normal mode due to driver limitations\n"); } return 0; } int avpops_db_init(const str* db_table, str** db_cols) { int i; for(i=0;ilen, db_table->s); goto error; } } def_table.s = db_table->s; def_table.len = db_table->len; db_columns = db_cols; return 0; error: for(--i;i>=0;i--){ if (db_urls[i].hdl) { db_urls[i].dbf.close(db_urls[i].hdl); db_urls[i].hdl = NULL; } } return -1; } int avp_add_db_scheme( modparam_t type, void* val) { struct db_scheme *scheme; scheme = (struct db_scheme*)pkg_malloc( sizeof(struct db_scheme) ); if (scheme==0) { LM_ERR("no more pkg memory\n"); goto error; } memset( scheme, 0, sizeof(struct db_scheme)); /* parse the scheme */ if ( parse_avp_db_scheme( (char*)val, scheme)!=0 ) { LM_ERR("failed to parse scheme\n"); goto error; } /* check for duplicates */ if ( avp_get_db_scheme(&scheme->name)!=0 ) { LM_ERR("duplicated scheme name <%.*s>\n", scheme->name.len,scheme->name.s); goto error; } /* print scheme */ LM_DBG("new scheme <%.*s> added\n" "\t\tuuid_col=<%.*s>\n\t\tusername_col=<%.*s>\n" "\t\tdomain_col=<%.*s>\n\t\tvalue_col=<%.*s>\n" "\t\tdb_flags=%d\n\t\ttable=<%.*s>\n", scheme->name.len,scheme->name.s, scheme->uuid_col.len, scheme->uuid_col.s, scheme->username_col.len, scheme->username_col.s, scheme->domain_col.len, scheme->domain_col.s, scheme->value_col.len, scheme->value_col.s, scheme->db_flags, scheme->table.len, scheme->table.s); scheme->next = db_scheme_list; db_scheme_list = scheme; return 0; error: return -1; } struct db_scheme *avp_get_db_scheme (str *name) { struct db_scheme *scheme; for( scheme=db_scheme_list ; scheme ; scheme=scheme->next ) if ( name->len==scheme->name.len && !strcasecmp( name->s, scheme->name.s) ) return scheme; return 0; } static inline int set_table( struct db_url *url, const str *table, char *func) { if (table && table->s) { if ( url->dbf.use_table( url->hdl, table)<0 ) { LM_ERR("db-%s: cannot set table \"%.*s\"\n", func, table->len, table->s); return -1; } } else { if ( url->dbf.use_table( url->hdl, &def_table)<0 ) { LM_ERR("db-%s: cannot set table \"%.*s\"\n", func, def_table.len, def_table.s); return -1; } } return 0; } static inline int prepare_selection( str *uuid, str *username, str *domain, char *attr, struct db_scheme *scheme) { unsigned int nr_keys_cmp; nr_keys_cmp = 0; if (uuid) { /* uuid column */ keys_cmp[ nr_keys_cmp ] = (scheme&&scheme->uuid_col.s)?&scheme->uuid_col:db_columns[0]; vals_cmp[ nr_keys_cmp ].type = DB_STR; vals_cmp[ nr_keys_cmp ].nul = 0; vals_cmp[ nr_keys_cmp ].val.str_val = *uuid; nr_keys_cmp++; } else { if (username) { /* username column */ keys_cmp[ nr_keys_cmp ] = (scheme&&scheme->username_col.s)?&scheme->username_col:db_columns[4]; vals_cmp[ nr_keys_cmp ].type = DB_STR; vals_cmp[ nr_keys_cmp ].nul = 0; vals_cmp[ nr_keys_cmp ].val.str_val = *username; nr_keys_cmp++; } if (domain) { /* domain column */ keys_cmp[ nr_keys_cmp ] = (scheme&&scheme->domain_col.s)?&scheme->domain_col:db_columns[5]; vals_cmp[ nr_keys_cmp ].type = DB_STR; vals_cmp[ nr_keys_cmp ].nul = 0; vals_cmp[ nr_keys_cmp ].val.str_val = *domain; nr_keys_cmp++; } } if (attr && scheme==0) { /* attribute name column */ keys_cmp[ nr_keys_cmp ] = db_columns[1]; vals_cmp[ nr_keys_cmp ].type = DB_STRING; vals_cmp[ nr_keys_cmp ].nul = 0; vals_cmp[ nr_keys_cmp ].val.string_val = attr; nr_keys_cmp++; } return nr_keys_cmp; } db_res_t *db_load_avp(struct db_url *url, str *uuid, str *username,str *domain, char *attr, const str *table, struct db_scheme *scheme) { static db_key_t keys_ret[3]; unsigned int nr_keys_cmp; unsigned int nr_keys_ret; db_res_t *res = NULL; /* prepare DB query */ nr_keys_cmp = prepare_selection( uuid, username, domain, attr, scheme); /* set table */ if (set_table( url, scheme?&scheme->table:table ,"load")!=0) return 0; /* return keys */ if (scheme==0) { keys_ret[0] = db_columns[2]; /*value*/ keys_ret[1] = db_columns[1]; /*attribute*/ keys_ret[2] = db_columns[3]; /*type*/ nr_keys_ret = 3; } else { /* value */ keys_ret[0] = scheme->value_col.s?&scheme->value_col:db_columns[2]; nr_keys_ret = 1; } /* do the DB query */ if ( url->dbf.query( url->hdl, keys_cmp, 0/*op*/, vals_cmp, keys_ret, nr_keys_cmp, nr_keys_ret, 0/*order*/, &res) < 0) return 0; return res; } void db_close_query(struct db_url *url, db_res_t *res ) { LM_DBG("close avp query\n"); url->dbf.free_result( url->hdl, res); } int db_store_avp(struct db_url *url, db_key_t *keys, db_val_t *vals, int n, const str *table) { int r; static query_list_t *ins_list = NULL; if (set_table( url, table ,"store")!=0) return -1; if (con_set_inslist(&url->dbf,url->hdl,&ins_list,keys,n) < 0 ) CON_RESET_INSLIST(url->hdl); r = url->dbf.insert( url->hdl, keys, vals, n); if (r<0) { LM_ERR("insert failed\n"); return -1; } return 0; } int db_delete_avp(struct db_url *url, str *uuid, str *username, str *domain, char *attr, const str *table) { unsigned int nr_keys_cmp; /* prepare DB query */ nr_keys_cmp = prepare_selection( uuid, username, domain, attr, 0); /* set table */ if (set_table( url, table ,"delete")!=0) return -1; /* do the DB query */ if ( url->dbf.delete( url->hdl, keys_cmp, 0, vals_cmp, nr_keys_cmp) < 0) return 0; return 0; } int db_query_avp(struct db_url *url, struct sip_msg *msg, str *query, pvname_list_t* dest) { db_res_t* db_res = NULL; if(query==NULL) { LM_ERR("bad parameter\n"); return -1; } if(url->dbf.raw_query( url->hdl, query, &db_res)!=0) { const str *t = url->hdl&&url->hdl->table&&url->hdl->table->s ? url->hdl->table : 0; LM_ERR("raw_query failed: db%d(%.*s) %.*s...\n", url->idx, t?t->len:0, t?t->s:"", query->len > 40 ? 40 : query->len, query->s); return -1; } if(db_res==NULL || RES_ROW_N(db_res)<=0 || RES_COL_N(db_res)<=0) { LM_DBG("no result after query\n"); db_close_query( url, db_res ); return 1; } if (db_query_avp_print_results(msg, db_res, dest) != 0) { LM_ERR("failed to print results\n"); db_close_query( url, db_res ); return -1; } db_close_query( url, db_res ); return 0; } int db_query_avp_print_results(struct sip_msg *msg, const db_res_t *db_res, pvname_list_t *dest) { int_str avp_val; int_str avp_name; unsigned short avp_type; int i, j; pvname_list_t* crt; LM_DBG("rows [%d]\n", RES_ROW_N(db_res)); /* reverse order of rows so that first row get's in front of avp list */ for(i = RES_ROW_N(db_res)-1; i >= 0; i--) { LM_DBG("row [%d]\n", i); crt = dest; for(j = 0; j < RES_COL_N(db_res); j++) { avp_type = 0; if(crt==NULL) { avp_name.s.s = int2str(j+1, &avp_name.s.len); avp_name.n = get_avp_id(&avp_name.s); if (avp_name.n < 0) { LM_ERR("cannot convert avp %d\n", j+1); goto next_avp; } } else { if(pv_get_avp_name(msg, &crt->sname.pvp, &avp_name.n, &avp_type)!=0) { LM_ERR("cant get avp name [%d/%d]\n", i, j); goto next_avp; } } if(RES_ROWS(db_res)[i].values[j].nul) { avp_type |= AVP_VAL_STR; /* keep the NULL value in sync with str_null as * defined in pvar.c !! */ avp_val.s.s = ""; avp_val.s.len = 6; } else { switch(RES_ROWS(db_res)[i].values[j].type) { case DB_STRING: avp_type |= AVP_VAL_STR; avp_val.s.s= (char*)RES_ROWS(db_res)[i].values[j].val.string_val; avp_val.s.len=strlen(avp_val.s.s); if(avp_val.s.len<0) goto next_avp; break; case DB_STR: avp_type |= AVP_VAL_STR; avp_val.s.len= RES_ROWS(db_res)[i].values[j].val.str_val.len; avp_val.s.s= (char*)RES_ROWS(db_res)[i].values[j].val.str_val.s; if(avp_val.s.len<0) goto next_avp; break; case DB_BLOB: avp_type |= AVP_VAL_STR; avp_val.s.len = RES_ROWS(db_res)[i].values[j].val.blob_val.len; avp_val.s.s = (char*)RES_ROWS(db_res)[i].values[j].val.blob_val.s; if(avp_val.s.len<0) goto next_avp; break; case DB_INT: avp_val.n = (int)RES_ROWS(db_res)[i].values[j].val.int_val; break; case DB_DATETIME: avp_val.n = (int)RES_ROWS(db_res)[i].values[j].val.time_val; break; case DB_BITMAP: avp_val.n = (int)RES_ROWS(db_res)[i].values[j].val.bitmap_val; break; case DB_BIGINT: avp_val.n = (int)RES_ROWS(db_res)[i].values[j].val.bigint_val; break; default: LM_WARN("Unknown type %d\n", RES_ROWS(db_res)[i].values[j].type); goto next_avp; } } if(add_avp(avp_type, avp_name.n, avp_val)!=0) { LM_ERR("unable to add avp\n"); return -1; } next_avp: if(crt) { crt = crt->next; if(crt==NULL) break; } } } return 0; } opensips-2.2.2/modules/avpops/avpops_db.h000066400000000000000000000046031300170765700204740ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2004-10-04 first version (ramona) * 2004-11-11 added support for db schemes for avp_db_load (ramona) */ #ifndef _AVP_OPS_DB_H_ #define _AVP_OPS_DB_H_ #include "../../db/db.h" #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../sr_module.h" #include "../../pvar.h" extern struct db_url *default_db_url; struct db_url { str url; unsigned int idx; db_con_t *hdl; /* DB handler */ db_func_t dbf; /* DB functions */ }; /* definition of a DB scheme*/ struct db_scheme { str name; str uuid_col; str username_col; str domain_col; str value_col; str table; int db_flags; struct db_scheme *next; }; int add_db_url(modparam_t type, void *val); struct db_url* get_db_url(unsigned int idx); struct db_url* get_default_db_url(void); int avpops_db_bind(void); int avpops_db_init(const str* db_table, str **db_columns); db_res_t *db_load_avp(struct db_url *url,str *uuid, str *username, str *domain, char *attr, const str *table, struct db_scheme *scheme); void db_close_query( struct db_url *url, db_res_t *res ); int db_store_avp( struct db_url *url, db_key_t *keys, db_val_t *vals, int n, const str *table); int db_delete_avp( struct db_url *url, str *uuid, str *username, str *domain, char *attr, const str *table); int db_query_avp(struct db_url *url, struct sip_msg* msg, str *query, pvname_list_t* dest); int avp_add_db_scheme( modparam_t type, void* val); struct db_scheme *avp_get_db_scheme( str *name ); int db_query_avp_print_results(struct sip_msg *msg, const db_res_t *db_res, pvname_list_t *dest); #endif opensips-2.2.2/modules/avpops/avpops_impl.c000066400000000000000000001301611300170765700210420ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2004-10-04 first version (ramona) * 2005-01-30 "fm" (fast match) operator added (ramona) * 2005-01-30 avp_copy (copy/move operation) added (ramona) */ #include #include #include #include #include #include "../../ut.h" #include "../../dprint.h" #include "../../usr_avp.h" #include "../../action.h" #include "../../ip_addr.h" #include "../../config.h" #include "../../dset.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../pvar.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" #include "../../mem/mem.h" #include "avpops_impl.h" #include "avpops_db.h" #define avpops_str2int_str(a, b) \ do { \ if(a.s==0) \ b.n = a.len; \ else \ b.s = a; \ } while(0) static db_key_t store_keys[6]; static db_val_t store_vals[6]; static str empty={"",0}; extern unsigned buf_size; extern char *printbuf; void init_store_avps(str **db_columns) { /* unique user id */ store_keys[0] = db_columns[0]; /*uuid*/ store_vals[0].type = DB_STR; store_vals[0].nul = 0; /* attribute */ store_keys[1] = db_columns[1]; /*attribute*/ store_vals[1].type = DB_STR; store_vals[1].nul = 0; /* value */ store_keys[2] = db_columns[2]; /*value*/ store_vals[2].type = DB_STR; store_vals[2].nul = 0; /* type */ store_keys[3] = db_columns[3]; /*type*/ store_vals[3].type = DB_INT; store_vals[3].nul = 0; /* user name */ store_keys[4] = db_columns[4]; /*username*/ store_vals[4].type = DB_STR; store_vals[4].nul = 0; /* domain */ store_keys[5] = db_columns[5]; /*domain*/ store_vals[5].type = DB_STR; store_vals[5].nul = 0; } #define AVPOPS_ATTR_LEN 64 static char avpops_attr_buf[AVPOPS_ATTR_LEN]; /* value 0 - attr value * value 1 - attr name * value 2 - attr type */ static int dbrow2avp(struct db_row *row, struct db_param *dbp, int attr, int attr_type, int just_val_flags, str *prefix) { unsigned int uint; int db_flags; str atmp; str vtmp; int avp_attr; int_str avp_val; int flags; flags = dbp->a.opd; if (just_val_flags==-1) { /* check for null fields into the row */ if (row->values[0].nul || row->values[1].nul || row->values[2].nul ) { LM_ERR("dbrow contains NULL fields\n"); return -1; } /* check the value types */ if ( (row->values[0].type!=DB_STRING && row->values[0].type!=DB_STR) || (row->values[1].type!=DB_STRING && row->values[1].type!=DB_STR) || row->values[2].type!=DB_INT ) { LM_ERR("wrong field types in dbrow\n"); return -1; } /* check the content of flag field */ uint = (unsigned int)row->values[2].val.int_val; db_flags = ((uint&AVPOPS_DB_NAME_INT)?0:AVP_NAME_STR) | ((uint&AVPOPS_DB_VAL_INT)?0:AVP_VAL_STR); } else { /* check the validity of value column */ if (row->values[0].nul || (row->values[0].type!=DB_STRING && row->values[0].type!=DB_STR && row->values[0].type!=DB_INT) ) { LM_ERR("empty or wrong type for 'value' using scheme\n"); return -1; } db_flags = just_val_flags; } /* is the avp name already known? */ if ( (flags&AVPOPS_VAL_NONE)==0 ) { /* use the name */ avp_attr = attr; db_flags |= attr_type; } else { /* take the name from db response */ if (row->values[1].type==DB_STRING) { atmp.s = (char*)row->values[1].val.string_val; atmp.len = strlen(atmp.s); } else { atmp = row->values[1].val.str_val; } if (prefix) { if (atmp.len + prefix->len > AVPOPS_ATTR_LEN) { LM_ERR("name too long [%d/%.*s...]\n", prefix->len + atmp.len, 16, prefix->s); return -1; } memcpy(avpops_attr_buf, prefix->s, prefix->len); memcpy(avpops_attr_buf + prefix->len, atmp.s, atmp.len); atmp.s = avpops_attr_buf; atmp.len += prefix->len; } /* there is always a name here - get the ID */ avp_attr = get_avp_id(&atmp); if (avp_attr < 0) return -2; } /* now get the value as correct type */ if (row->values[0].type==DB_STRING) { vtmp.s = (char*)row->values[0].val.string_val; vtmp.len = strlen(vtmp.s); } else if (row->values[0].type==DB_STR){ vtmp = row->values[0].val.str_val; } else { vtmp.s = 0; vtmp.len = 0; } if (db_flags&AVP_VAL_STR) { /* value must be saved as string */ if (row->values[0].type==DB_INT) { vtmp.s = int2str( (unsigned long)row->values[0].val.int_val, &vtmp.len); } avp_val.s = vtmp; } else { /* value must be saved as integer */ if (row->values[0].type!=DB_INT) { if (vtmp.len==0 || vtmp.s==0 || str2int(&vtmp, &uint)==-1) { LM_ERR("value is not int as flags say <%s>\n", vtmp.s); return -1; } avp_val.n = (int)uint; } else { avp_val.n = row->values[0].val.int_val; } } /* added the avp */ db_flags |= AVP_IS_IN_DB; /* set script flags */ db_flags |= dbp->a.u.sval.pvp.pvn.u.isname.type&0xff00; return add_avp( (unsigned short)db_flags, avp_attr, avp_val); } inline static str* get_source_uri(struct sip_msg* msg,int source) { /* which uri will be used? */ if (source&AVPOPS_USE_FROM) { /* from */ if (parse_from_header( msg )<0 ) { LM_ERR("failed to parse from\n"); goto error; } return &(get_from(msg)->uri); } else if (source&AVPOPS_USE_TO) { /* to */ if (parse_headers( msg, HDR_TO_F, 0)<0) { LM_ERR("failed to parse to\n"); goto error; } return &(get_to(msg)->uri); } else if (source&AVPOPS_USE_RURI) { /* RURI */ if(msg->new_uri.s!=NULL && msg->new_uri.len>0) return &(msg->new_uri); return &(msg->first_line.u.request.uri); } else { LM_ERR("unknown source <%d>\n", source); goto error; } error: return 0; } static inline void int_str2db_val( int_str is_val, str *val, int is_s) { if (is_s) { /* val is string */ *val = is_val.s; } else { /* val is integer */ val->s = int2str((unsigned long)is_val.n, &val->len); } } static int avpops_get_aname(struct sip_msg* msg, struct fis_param *ap, int *avp_name, unsigned short *name_type) { if(ap==NULL || avp_name==NULL || name_type==NULL) { LM_ERR("bad parameters\n"); return -1; } return pv_get_avp_name(msg, &ap->u.sval.pvp, avp_name, name_type); } int ops_dbload_avps (struct sip_msg* msg, struct fis_param *sp, struct db_param *dbp, struct db_url *url, int use_domain, str *prefix) { struct sip_uri uri; db_res_t *res = NULL; str uuid; int i, n, sh_flg; str *s0, *s1, *s2; int avp_name; int avp_type = 0; pv_value_t xvalue; s0 = s1 = s2 = NULL; if (!((sp->opd&AVPOPS_VAL_PVAR)||(sp->opd&AVPOPS_VAL_STR))) { LM_CRIT("invalid flag combination (%d/%d)\n", sp->opd, sp->ops); goto error; } /* get uuid from avp */ if (sp->opd&AVPOPS_VAL_PVAR) { if(pv_get_spec_value(msg, &(sp->u.sval), &xvalue)!=0) { LM_CRIT("failed to get PVAR value (%d/%d)\n", sp->opd, sp->ops); goto error; } if(xvalue.flags&(PV_VAL_NULL|PV_VAL_EMPTY)) { LM_ERR("no value for first param\n"); goto error; } uuid = xvalue.rs; } else { uuid.s = sp->u.s.s; uuid.len = sp->u.s.len; } if(sp->opd&AVPOPS_FLAG_UUID0) { s0 = &uuid; } else { /* parse uri */ if (parse_uri(uuid.s, uuid.len, &uri)<0) { LM_ERR("failed to parse uri\n"); goto error; } if((sp->opd&AVPOPS_FLAG_URI0)||(sp->opd&AVPOPS_FLAG_USER0)) { /* check that uri contains user part */ if(!uri.user.s|| !uri.user.len) { LM_ERR("incomplet uri <%.*s> missing user\n", uuid.len, uuid.s); goto error; } else { s1 = &uri.user; } } if((sp->opd&AVPOPS_FLAG_URI0)||(sp->opd&AVPOPS_FLAG_DOMAIN0)) { /* check that uri contains host part */ if(!uri.host.len|| !uri.host.s) { LM_ERR("incomplet uri <%.*s> missing host\n", uuid.len, uuid.s); goto error; } else { s2 = &uri.host; } } } /* is dynamic avp name ? */ if(dbp->a.type==AVPOPS_VAL_PVAR) { if(pv_has_dname(&(dbp->a.u.sval))) { if(pv_get_spec_name(msg, &(dbp->a.u.sval.pvp), &xvalue)!=0) { LM_CRIT("failed to get value for P2\n"); goto error; } if(xvalue.flags&(PV_VAL_NULL|PV_VAL_EMPTY)) { LM_ERR("no value for p2\n"); goto error; } if(xvalue.flags&PV_VAL_STR) { if(xvalue.rs.len>=AVPOPS_ATTR_LEN) { LM_ERR("name too long [%d/%.*s...]\n", xvalue.rs.len, 16, xvalue.rs.s); goto error; } dbp->sa.s = avpops_attr_buf; memcpy(dbp->sa.s, xvalue.rs.s, xvalue.rs.len); dbp->sa.len = xvalue.rs.len; dbp->sa.s[dbp->sa.len] = '\0'; } else { LM_INFO("no string value for p2\n"); goto error; } } } /* do DB query */ res = db_load_avp( url, s0, s1, ((use_domain)||(sp->opd&AVPOPS_FLAG_DOMAIN0))?s2:0, dbp->sa.s, &dbp->table, dbp->scheme); /* res query ? */ if (res==0) { LM_ERR("db_load failed\n"); goto error; } sh_flg = (dbp->scheme)?dbp->scheme->db_flags:-1; /* validate row */ avp_name = -1; if(dbp->a.type==AVPOPS_VAL_PVAR) { if(pv_has_dname(&dbp->a.u.sval)) { if(xvalue.flags&PV_TYPE_INT) { avp_name = xvalue.ri; } else { if (prefix) { if (xvalue.rs.len + prefix->len > AVPOPS_ATTR_LEN) { LM_ERR("name too long [%d/%.*s...]\n", prefix->len + xvalue.rs.len, 16, prefix->s); goto error; } memcpy(avpops_attr_buf, prefix->s, prefix->len); memcpy(avpops_attr_buf + prefix->len, xvalue.rs.s, xvalue.rs.len); xvalue.rs.s = avpops_attr_buf; xvalue.rs.len = prefix->len + xvalue.rs.len; } avp_name = get_avp_id(&xvalue.rs); if (avp_name < 0) { LM_ERR("cannot get avp id\n"); return -1; } } } else { avp_name = dbp->a.u.sval.pvp.pvn.u.isname.name.n; avp_type = dbp->a.u.sval.pvp.pvn.u.isname.type; } } /* process the results */ for( n=0,i=0 ; in ; i++) { if (dbrow2avp(&res->rows[i], dbp, avp_name, avp_type, sh_flg, prefix) < 0) continue; n++; } db_close_query( url, res ); LM_DBG("loaded avps = %d\n",n); return n?1:-1; error: return -1; } int ops_dbdelete_avps (struct sip_msg* msg, struct fis_param *sp, struct db_param *dbp, struct db_url *url, int use_domain) { struct sip_uri uri; int res; str uuid; pv_value_t xvalue; str *s0, *s1, *s2; s0 = s1 = s2 = NULL; if (!((sp->opd&AVPOPS_VAL_PVAR)||(sp->opd&AVPOPS_VAL_STR))) { LM_CRIT("invalid flag combination (%d/%d)\n", sp->opd, sp->ops); goto error; } /* get uuid from avp */ if (sp->opd&AVPOPS_VAL_PVAR) { if(pv_get_spec_value(msg, &(sp->u.sval), &xvalue)!=0) { LM_CRIT("failed to get PVAR value (%d/%d)\n", sp->opd, sp->ops); goto error; } if(xvalue.flags&(PV_VAL_NULL|PV_VAL_EMPTY)) { LM_ERR("no value for first param\n"); goto error; } uuid = xvalue.rs; } else { uuid.s = sp->u.s.s; uuid.len = sp->u.s.len; } if(sp->opd&AVPOPS_FLAG_UUID0) { s0 = &uuid; } else { /* parse uri */ if (parse_uri(uuid.s, uuid.len, &uri)<0) { LM_ERR("failed to parse uri\n"); goto error; } if((sp->opd&AVPOPS_FLAG_URI0)||(sp->opd&AVPOPS_FLAG_USER0)) { /* check that uri contains user part */ if(!uri.user.s|| !uri.user.len) { LM_ERR("incomplet uri <%.*s> missing user\n", uuid.len, uuid.s); goto error; } else { s1 = &uri.user; } } if((sp->opd&AVPOPS_FLAG_URI0)||(sp->opd&AVPOPS_FLAG_DOMAIN0)) { /* check tah uri contains host part */ if(!uri.host.len|| !uri.host.s) { LM_ERR("incomplet uri <%.*s> missing host\n", uuid.len, uuid.s); goto error; } else { s2 = &uri.host; } } } /* is dynamic avp name ? */ if(dbp->a.type==AVPOPS_VAL_PVAR) { if(pv_has_dname(&dbp->a.u.sval)) { if(pv_get_spec_name(msg, &(dbp->a.u.sval.pvp), &xvalue)!=0) { LM_CRIT("failed to get value for P2\n"); goto error; } if(xvalue.flags&(PV_VAL_NULL|PV_VAL_EMPTY)) { LM_INFO("no value for p2\n"); goto error; } if(xvalue.flags&PV_VAL_STR) { if(xvalue.rs.len>=AVPOPS_ATTR_LEN) { LM_ERR("name too long [%d/%.*s...]\n", xvalue.rs.len, 16, xvalue.rs.s); goto error; } dbp->sa.s = avpops_attr_buf; memcpy(dbp->sa.s, xvalue.rs.s, xvalue.rs.len); dbp->sa.len = xvalue.rs.len; dbp->sa.s[dbp->sa.len] = '\0'; } else { LM_INFO("no string value for p2\n"); goto error; } } } /* do DB delete */ res = db_delete_avp( url, s0, s1, (use_domain||(sp->opd&AVPOPS_FLAG_DOMAIN0))?s2:0, dbp->sa.s, &dbp->table); /* res ? */ if (res<0) { LM_ERR("db_delete failed\n"); goto error; } return 1; error: return -1; } int ops_dbstore_avps (struct sip_msg* msg, struct fis_param *sp, struct db_param *dbp, struct db_url *url, int use_domain) { struct sip_uri uri; struct usr_avp **avp_list; struct usr_avp *avp; unsigned short name_type; int avp_name; int_str i_s; str uuid; int keys_nr; int n; pv_value_t xvalue; str *s0, *s1, *s2; str *sn; s0 = s1 = s2 = NULL; name_type = 0; if (!((sp->opd&AVPOPS_VAL_PVAR)||(sp->opd&AVPOPS_VAL_STR))) { LM_CRIT("invalid flag combination (%d/%d)\n", sp->opd, sp->ops); goto error; } keys_nr = 6; /* uuid, avp name, avp val, avp type, user, domain */ /* get uuid from avp */ if (sp->opd&AVPOPS_VAL_PVAR) { if(pv_get_spec_value(msg, &(sp->u.sval), &xvalue)!=0) { LM_CRIT("failed to get PVAR value (%d/%d)\n", sp->opd, sp->ops); goto error; } if(xvalue.flags&(PV_VAL_NULL|PV_VAL_EMPTY)) { LM_ERR("no value for first param\n"); goto error; } uuid = xvalue.rs; } else { uuid.s = sp->u.s.s; uuid.len = sp->u.s.len; } if(sp->opd&AVPOPS_FLAG_UUID0) { s0 = &uuid; } else { /* parse uri */ if (parse_uri(uuid.s, uuid.len, &uri)<0) { LM_ERR("failed to parse uri\n"); goto error; } if((sp->opd&AVPOPS_FLAG_URI0)||(sp->opd&AVPOPS_FLAG_USER0)) { /* check tha uri contains user part */ if(!uri.user.s|| !uri.user.len) { LM_ERR("incomplet uri <%.*s> missing user\n", uuid.len, uuid.s); goto error; } else { s1 = &uri.user; } } if((sp->opd&AVPOPS_FLAG_URI0)||(sp->opd&AVPOPS_FLAG_DOMAIN0)) { /* check that uri contains host part */ if(!uri.host.len|| !uri.host.s) { LM_ERR("incomplet uri <%.*s> missing host\n", uuid.len, uuid.s); goto error; } else { s2 = &uri.host; } } } /* set values for keys */ store_vals[0].val.str_val = (s0)?*s0:empty; store_vals[4].val.str_val = (s1)?*s1:empty; if (use_domain || sp->opd&AVPOPS_FLAG_DOMAIN0) store_vals[5].val.str_val = (s2)?*s2:empty; avp_name = -1; /* is dynamic avp name ? */ if(dbp->a.type==AVPOPS_VAL_PVAR) { if(pv_has_dname(&dbp->a.u.sval)) { /* TODO change here to be aware of the int name */ if(pv_get_spec_name(msg, &(dbp->a.u.sval.pvp), &xvalue)!=0) { LM_CRIT("failed to get value for P2\n"); goto error; } if(xvalue.flags&(PV_VAL_NULL|PV_VAL_EMPTY)) { LM_INFO("no value for P2\n"); goto error; } if(xvalue.flags&PV_TYPE_INT) { name_type = 0; avp_name = xvalue.ri; } else { name_type = AVP_NAME_STR; avp_name = -1; } if(xvalue.flags&PV_VAL_STR) { if(xvalue.rs.len>=AVPOPS_ATTR_LEN) { LM_ERR("name too long [%d/%.*s...]\n", xvalue.rs.len, 16, xvalue.rs.s); goto error; } dbp->sa.s = avpops_attr_buf; memcpy(dbp->sa.s, xvalue.rs.s, xvalue.rs.len); dbp->sa.len = xvalue.rs.len; dbp->sa.s[dbp->sa.len] = '\0'; avp_name = get_avp_id(&dbp->sa); /* search for the id only once */ if (avp_name < 0) { LM_ERR("cannot find avp\n"); goto error; } } else { LM_INFO("no string value for p2\n"); goto error; } } else { name_type = dbp->a.u.sval.pvp.pvn.u.isname.type; avp_name = dbp->a.u.sval.pvp.pvn.u.isname.name.n; } } else { LM_WARN("TODO: avp is not a dynamic name <%.*s> name is %d\n", dbp->sa.len, dbp->sa.s, avp_name); avp_name = -1; } /* set the script flags */ if(dbp->a.type==AVPOPS_VAL_PVAR) name_type |= dbp->a.u.sval.pvp.pvn.u.isname.type&0xff00; /* set uuid/(username and domain) fields */ n =0 ; if ((dbp->a.opd&AVPOPS_VAL_NONE)==0) { /* if avp wasn't found yet */ if (avp_name < 0) { avp_name = get_avp_id(&dbp->sa); /* search for the id only once */ if (avp_name < 0) { LM_ERR("cannot find avp\n"); goto error; } } /* avp name is known ->set it and its type */ store_vals[1].val.str_val = dbp->sa; /*attr name*/ avp = search_first_avp( 0, avp_name, &i_s, 0); for( ; avp; avp=search_first_avp( 0, avp_name, &i_s, avp)) { /* don't insert avps which were loaded */ if (avp->flags&AVP_IS_IN_DB) continue; /* set type */ store_vals[3].val.int_val = (avp->flags&AVP_NAME_STR?0:AVPOPS_DB_NAME_INT)| (avp->flags&AVP_VAL_STR?0:AVPOPS_DB_VAL_INT); /* set value */ int_str2db_val( i_s, &store_vals[2].val.str_val, avp->flags&AVP_VAL_STR); /* save avp */ if (db_store_avp( url, store_keys, store_vals, keys_nr, &dbp->table)==0 ) { avp->flags |= AVP_IS_IN_DB; n++; } } } else { /* avp name is unknown -> go through all list */ avp_list = get_avp_list(); avp = *avp_list; for ( ; avp ; avp=avp->next ) { /* don't insert avps which were loaded */ if (avp->flags&AVP_IS_IN_DB) continue; /* set attribute name and type */ if ( (sn=get_avp_name(avp))==0 ) i_s.n = avp->id; else i_s.s = *sn; int_str2db_val( i_s, &store_vals[1].val.str_val, AVP_NAME_STR); store_vals[3].val.int_val = (avp->flags&AVP_NAME_STR?0:AVPOPS_DB_NAME_INT)| (avp->flags&AVP_VAL_STR?0:AVPOPS_DB_VAL_INT); /* set avp value */ get_avp_val( avp, &i_s); int_str2db_val( i_s, &store_vals[2].val.str_val, avp->flags&AVP_VAL_STR); /* save avp */ if (db_store_avp( url, store_keys, store_vals, keys_nr, &dbp->table)==0) { avp->flags |= AVP_IS_IN_DB; n++; } } } LM_DBG(" %d avps were stored\n",n); return n==0?-1:1; error: return -1; } /* @return : non-zero */ int ops_dbquery_avps(struct sip_msg* msg, pv_elem_t* query, struct db_url *url, pvname_list_t* dest) { int printbuf_len; int ret; str qstr; if(msg==NULL || query==NULL) { LM_ERR("bad parameters\n"); return -1; } printbuf_len = buf_size-1; if(pv_printf(msg, query, printbuf, &printbuf_len)<0 || printbuf_len<=0) { LM_ERR("cannot print the query\n"); return -1; } qstr.s = printbuf; qstr.len = printbuf_len; LM_DBG("query [%.*s]\n", printbuf_len, printbuf); ret = db_query_avp(url, msg, &qstr, dest); //Empty return set if(ret==1) return -2; //All other failures if(ret!=0) return -1; //Have a return set return 1; } int ops_async_dbquery(struct sip_msg* msg, async_resume_module **rfunc, void **rparam, pv_elem_t *query, struct db_url *url, pvname_list_t *dest) { int printbuf_len; int rc, read_fd; query_async_param *param; str qstr; void *_priv; if (!msg || !query) { LM_ERR("bad parameters\n"); return -1; } printbuf_len = buf_size - 1; if (pv_printf(msg, query, printbuf, &printbuf_len) < 0 || printbuf_len <= 0) { LM_ERR("cannot print the query\n"); return -1; } LM_DBG("query [%s]\n", printbuf); qstr.s = printbuf; qstr.len = printbuf_len; /* No async capabilities - just run it in blocking mode */ if (!DB_CAPABILITY(url->dbf, DB_CAP_ASYNC_RAW_QUERY)) { rc = db_query_avp(url, msg, &qstr, dest); LM_DBG("sync query \"%.*s\" returned: %d\n", qstr.len, qstr.s, rc); *rparam = NULL; *rfunc = NULL; async_status = ASYNC_NO_IO; /* Empty_set / Other_errors / Success */ return rc == 1 ? -2 : (rc != 0 ? -1 : 1); } read_fd = url->dbf.async_raw_query(url->hdl, &qstr, &_priv); if (read_fd < 0) { *rparam = NULL; *rfunc = NULL; return -1; } param = pkg_malloc(sizeof *param); if (!param) { LM_ERR("no more pkg mem\n"); return E_OUT_OF_MEM; } memset(param, '\0', sizeof *param); *rparam = param; *rfunc = resume_async_dbquery; param->output_avps = dest; param->hdl = url->hdl; param->dbf = &url->dbf; param->db_param = _priv; async_status = read_fd; return 1; } int resume_async_dbquery(int fd, struct sip_msg *msg, void *_param) { db_res_t *res = NULL; query_async_param *param = (query_async_param *)_param; int rc, ret; rc = param->dbf->async_resume(param->hdl, fd, &res, param->db_param); if (async_status == ASYNC_CONTINUE || async_status == ASYNC_CHANGE_FD) { return rc; } if (rc != 0) { LM_ERR("async query returned error\n"); ret = -1; goto err_free; } if (!res || RES_ROW_N(res) <= 0 || RES_COL_N(res) <= 0) { LM_DBG("query returned no results\n"); ret = -2; goto err_free; } if (db_query_avp_print_results(msg, res, param->output_avps) != 0) { LM_ERR("failed to print results\n"); ret = -1; goto err_free; } async_status = ASYNC_DONE; param->dbf->async_free_result(param->hdl, res, param->db_param); pkg_free(param); return 1; err_free: param->dbf->async_free_result(param->hdl, res, param->db_param); pkg_free(param); return ret; } int ops_delete_avp(struct sip_msg* msg, struct fis_param *ap) { struct usr_avp **avp_list; struct usr_avp *avp; struct usr_avp *avp_next; unsigned short name_type; int avp_name; int n; n = 0; if ((ap->opd&AVPOPS_VAL_NONE)==0) { /* avp name is known ->search by name */ /* get avp name */ if(avpops_get_aname(msg, ap, &avp_name, &name_type)!=0) { LM_ERR("failed to get dst AVP name\n"); return -1; } n = destroy_avps( name_type, avp_name, ap->ops&AVPOPS_FLAG_ALL ); } else { /* avp name is not given - we have just flags */ /* -> go through all list */ avp_list = get_avp_list(); avp = *avp_list; for ( ; avp ; avp=avp_next ) { avp_next = avp->next; /* check if type match */ if ( !( (ap->opd&(AVPOPS_VAL_INT|AVPOPS_VAL_STR))==0 || ((ap->opd&AVPOPS_VAL_INT)&&((avp->flags&AVP_NAME_STR))==0) || ((ap->opd&AVPOPS_VAL_STR)&&(avp->flags&AVP_NAME_STR)) ) ) continue; if((ap->u.sval.pvp.pvn.u.isname.type&AVP_SCRIPT_MASK)!=0 && ((ap->u.sval.pvp.pvn.u.isname.type&AVP_SCRIPT_MASK) &avp->flags)==0) continue; /* remove avp */ destroy_avp( avp ); n++; if ( !(ap->ops&AVPOPS_FLAG_ALL) ) break; } } LM_DBG("%d avps were removed\n",n); return n?1:-1; } int ops_copy_avp( struct sip_msg* msg, struct fis_param* src, struct fis_param* dst) { struct usr_avp *avp; struct usr_avp *prev_avp; int_str avp_val; int_str avp_val2; unsigned short name_type1; unsigned short name_type2; int avp_name1; int avp_name2; int n; n = 0; prev_avp = 0; /* get avp src name */ if(avpops_get_aname(msg, src, &avp_name1, &name_type1)!=0) { LM_ERR("failed to get src AVP name\n"); goto error; } /* get avp dst name */ if(avpops_get_aname(msg, dst, &avp_name2, &name_type2)!=0) { LM_ERR("failed to get dst AVP name\n"); goto error; } avp = search_first_avp( name_type1, avp_name1, &avp_val, 0); while ( avp ) { /* build a new avp with new name, but old value */ /* do we need cast conversion ?!?! */ if((avp->flags&AVP_VAL_STR) && (dst->ops&AVPOPS_FLAG_CASTN)) { if(str2int(&avp_val.s, (unsigned int*)&avp_val2.n)!=0) { LM_ERR("cannot convert str to int\n"); goto error; } if ( add_avp( name_type2, avp_name2, avp_val2)==-1 ) { LM_ERR("failed to create new avp!\n"); goto error; } } else if(!(avp->flags&AVP_VAL_STR)&&(dst->ops&AVPOPS_FLAG_CASTS)) { avp_val2.s.s = int2str(avp_val.n, &avp_val2.s.len); if ( add_avp( name_type2|AVP_VAL_STR, avp_name2, avp_val2)==-1 ) { LM_ERR("failed to create new avp.\n"); goto error; } } else { if ( add_avp( name_type2|(avp->flags&AVP_VAL_STR), avp_name2, avp_val)==-1 ) { LM_ERR("failed to create new avp\n"); goto error; } } n++; /* copy all avps? */ if ( !(dst->ops&AVPOPS_FLAG_ALL) ) { /* delete the old one? */ if (dst->ops&AVPOPS_FLAG_DELETE) destroy_avp( avp ); break; } else { prev_avp = avp; avp = search_first_avp( name_type1, avp_name1, &avp_val, prev_avp); /* delete the old one? */ if (dst->ops&AVPOPS_FLAG_DELETE) destroy_avp( prev_avp ); } } return n?1:-1; error: return -1; } #define STR_BUF_SIZE 1024 static char str_buf[STR_BUF_SIZE]; inline static int append_0(str *in, str *out) { if (in->len+1>STR_BUF_SIZE) return -1; memcpy( str_buf, in->s, in->len); str_buf[in->len] = 0; out->len = in->len; out->s = str_buf; return 0; } int ops_pushto_avp (struct sip_msg* msg, struct fis_param* dst, struct fis_param* src) { struct action act; struct usr_avp *avp; unsigned short name_type; int_str avp_val; int avp_name; str val; int act_type; int n; int flags; pv_value_t xvalue; avp = NULL; flags = 0; if(src->u.sval.type==PVT_AVP) { /* search for the avp */ if(avpops_get_aname(msg, src, &avp_name, &name_type)!=0) { LM_ERR("failed to get src AVP name\n"); goto error; } avp = search_first_avp( name_type, avp_name, &avp_val, 0); if (avp==0) { LM_DBG(" no src avp found\n"); goto error; } flags = avp->flags; } else { if(pv_get_spec_value(msg, &(src->u.sval), &xvalue)!=0) { LM_ERR("cannot get src value\n"); goto error; } if(xvalue.flags&PV_TYPE_INT) { avp_val.n = xvalue.ri; } else { flags = AVP_VAL_STR; avp_val.s = xvalue.rs; } } n = 0; do { /* the avp val will be used all the time as str */ if (flags&AVP_VAL_STR) { val = avp_val.s; } else { val.s = int2str((unsigned long)avp_val.n, &val.len); } act_type = 0; /* push the value into right position */ if (dst->opd&AVPOPS_USE_RURI) { if (dst->opd&AVPOPS_FLAG_USER0) act_type = SET_USER_T; else if (dst->opd&AVPOPS_FLAG_DOMAIN0) act_type = SET_HOST_T; else act_type = SET_URI_T; if ( flags&AVP_VAL_STR && append_0( &val, &val)!=0 ) { LM_ERR("failed to make 0 term.\n"); goto error; } } else if (dst->opd&AVPOPS_USE_DURI) { if (!(flags&AVP_VAL_STR)) { goto error; } } else if (dst->opd&AVPOPS_USE_BRANCH) { if (!(flags&AVP_VAL_STR)) { goto error; } } else { LM_CRIT("destination unknown (%d/%d)\n", dst->opd, dst->ops); goto error; } if ( act_type ) { /* rewrite part of ruri */ if (n) { /* if is not the first modification, push the current uri as * branch */ if (append_branch( msg, 0, 0, 0, Q_UNSPECIFIED, 0, 0)!=1 ) { LM_ERR("append_branch action failed\n"); goto error; } } memset(&act, 0, sizeof(act)); act.elem[0].type = STR_ST; act.elem[0].u.s = val; act.type = act_type; if (do_action(&act, msg)<0) { LM_ERR("SET_XXXX_T action failed\n"); goto error; } } else if (dst->opd&AVPOPS_USE_DURI) { if(set_dst_uri(msg, &val)!=0) { LM_ERR("changing dst uri failed\n"); goto error; } } else if (dst->opd&AVPOPS_USE_BRANCH) { if (append_branch( msg, &val, 0, 0, Q_UNSPECIFIED, 0, msg->force_send_socket)!=1 ) { LM_ERR("append_branch action failed\n"); goto error; } } else { LM_ERR("unknown destination\n"); goto error; } n++; if ( !(src->ops&AVPOPS_FLAG_ALL) ) break; if(avp==NULL) break; if((avp = search_first_avp( name_type, avp_name, &avp_val, avp))!=NULL) flags = avp->flags; } while (avp);/* end while */ LM_DBG("%d avps were processed\n",n); return 1; error: return -1; } int ops_check_avp( struct sip_msg* msg, struct fis_param* src, struct fis_param* val) { unsigned short name_type1; unsigned short name_type2; struct usr_avp *avp1; struct usr_avp *avp2; regmatch_t pmatch; int avp_name1; int avp_name2; int_str avp_val; int_str check_val; int check_flags; int n, rt; int flags; pv_value_t xvalue; char backup; /* look if the required avp(s) is/are present */ if(src->u.sval.type==PVT_AVP) { /* search for the avp */ if(avpops_get_aname(msg, src, &avp_name1, &name_type1)!=0) { LM_ERR("failed to get src AVP name\n"); goto error; } avp1 = search_first_avp( name_type1, avp_name1, &avp_val, 0); if (avp1==0) { LM_DBG("no src avp found\n"); goto error; } flags = avp1->flags; } else { avp1 = 0; flags = 0; if(pv_get_spec_value(msg, &(src->u.sval), &xvalue)!=0) { LM_ERR("cannot get src value\n"); goto error; } if(xvalue.flags&PV_TYPE_INT) { avp_val.n = xvalue.ri; } else { flags = AVP_VAL_STR; avp_val.s = xvalue.rs; } } cycle1: /* copy string since pseudo-variables uses static buffer */ if(flags&AVP_VAL_STR) { if(avp_val.s.len>=STR_BUF_SIZE) { LM_ERR("src value too long\n"); goto error; } strncpy(str_buf, avp_val.s.s, avp_val.s.len); str_buf[avp_val.s.len] = '\0'; avp_val.s.s = str_buf; } if (val->opd&AVPOPS_VAL_PVAR) { /* the 2nd operator is variable -> get avp value */ check_flags = 0; if(val->u.sval.type==PVT_AVP) { /* search for the avp */ if(avpops_get_aname(msg, val, &avp_name2, &name_type2)!=0) { LM_ERR("failed to get dst AVP name\n"); goto error; } avp2 = search_first_avp( name_type2, avp_name2, &check_val, 0); if (avp2==0) { LM_DBG("no dst avp found\n"); goto error; } check_flags = avp2->flags; } else { avp2 = 0; if(pv_get_spec_value(msg, &(val->u.sval), &xvalue)!=0) { LM_ERR("cannot get dst value\n"); goto error; } if(xvalue.flags&PV_TYPE_INT) { check_val.n = xvalue.ri; } else { check_flags = AVP_VAL_STR; check_val.s = xvalue.rs; } } } else { check_flags = 0; if(val->type == AVPOPS_VAL_INT) { check_val.n = val->u.n; } else { check_val.s = val->u.s; check_flags = AVP_VAL_STR; } avp2 = 0; } cycle2: /* are both values of the same type? */ if ((flags&AVP_VAL_STR)^(check_flags&AVP_VAL_STR)) { LM_ERR("value types don't match\n"); goto next; } if (flags&AVP_VAL_STR) { /* string values to check */ LM_DBG("check <%.*s> against <%.*s> as str /%d\n", avp_val.s.len,avp_val.s.s, (val->ops&AVPOPS_OP_RE)?6:check_val.s.len, (val->ops&AVPOPS_OP_RE)?"REGEXP":check_val.s.s, val->ops); /* do check */ if (val->ops&AVPOPS_OP_EQ) { if (avp_val.s.len==check_val.s.len) { if (val->ops&AVPOPS_FLAG_CI) { if (strncasecmp(avp_val.s.s,check_val.s.s, check_val.s.len)==0) return 1; } else { if (strncmp(avp_val.s.s,check_val.s.s,check_val.s.len)==0 ) return 1; } } } else if (val->ops&AVPOPS_OP_NE) { if (avp_val.s.len!=check_val.s.len) return 1; if (val->ops&AVPOPS_FLAG_CI) { if (strncasecmp(avp_val.s.s,check_val.s.s,check_val.s.len)!=0) return 1; } else { if (strncmp(avp_val.s.s,check_val.s.s,check_val.s.len)!=0 ) return 1; } } else if (val->ops&AVPOPS_OP_LT) { n = (avp_val.s.len>=check_val.s.len)?avp_val.s.len:check_val.s.len; rt = strncasecmp(avp_val.s.s,check_val.s.s,n); if (rt<0) return 1; if(rt==0 && avp_val.s.lenops&AVPOPS_OP_LE) { n = (avp_val.s.len>=check_val.s.len)?avp_val.s.len:check_val.s.len; if (strncasecmp(avp_val.s.s,check_val.s.s,n)<=0) return 1; } else if (val->ops&AVPOPS_OP_GT) { n = (avp_val.s.len>=check_val.s.len)?avp_val.s.len:check_val.s.len; rt = strncasecmp(avp_val.s.s,check_val.s.s,n); if (rt>0) return 1; if(rt==0 && avp_val.s.len>check_val.s.len) return 1; } else if (val->ops&AVPOPS_OP_GE) { n = (avp_val.s.len>=check_val.s.len)?avp_val.s.len:check_val.s.len; if (strncasecmp(avp_val.s.s,check_val.s.s,n)>=0) return 1; } else if (val->ops&AVPOPS_OP_RE) { backup = avp_val.s.s[avp_val.s.len]; avp_val.s.s[avp_val.s.len] = '\0'; if (regexec((regex_t*)check_val.s.s, avp_val.s.s, 1, &pmatch,0)==0) { avp_val.s.s[avp_val.s.len] = backup; return 1; } avp_val.s.s[avp_val.s.len] = backup; } else if (val->ops&AVPOPS_OP_FM){ backup = avp_val.s.s[avp_val.s.len]; avp_val.s.s[avp_val.s.len] = '\0'; if (fnmatch( check_val.s.s, avp_val.s.s, #ifdef FNM_CASEFOLD (val->ops&AVPOPS_FLAG_CI)?FNM_CASEFOLD: #endif 0 )==0) { avp_val.s.s[avp_val.s.len] = backup; return 1; } avp_val.s.s[avp_val.s.len] = backup; } else { LM_CRIT("unknown operation (flg=%d/%d)\n",val->opd, val->ops); } } else { /* int values to check -> do check */ LM_DBG("check <%d> against <%d> as int /%d\n", avp_val.n, check_val.n, val->ops); if (val->ops&AVPOPS_OP_EQ) { if ( avp_val.n==check_val.n) return 1; } else if (val->ops&AVPOPS_OP_NE) { if ( avp_val.n!=check_val.n) return 1; } else if (val->ops&AVPOPS_OP_LT) { if ( avp_val.nops&AVPOPS_OP_LE) { if ( avp_val.n<=check_val.n) return 1; } else if (val->ops&AVPOPS_OP_GT) { if ( avp_val.n>check_val.n) return 1; } else if (val->ops&AVPOPS_OP_GE) { if ( avp_val.n>=check_val.n) return 1; } else if (val->ops&AVPOPS_OP_BAND) { if ( avp_val.n&check_val.n) return 1; } else if (val->ops&AVPOPS_OP_BOR) { if ( avp_val.n|check_val.n) return 1; } else if (val->ops&AVPOPS_OP_BXOR) { if ( avp_val.n^check_val.n) return 1; } else { LM_CRIT("unknown operation (flg=%d)\n",val->ops); } } next: /* cycle for the second value (only if avp can have multiple vals) */ if ((avp2!=NULL) && (avp2=search_first_avp( name_type2, avp_name2, &check_val, avp2))!=NULL) { check_flags = avp2->flags; goto cycle2; /* cycle for the first value -> next avp */ } else { if(avp1 && val->ops&AVPOPS_FLAG_ALL) { avp1=search_first_avp(name_type1, avp_name1, &avp_val, avp1); if (avp1) goto cycle1; } } LM_DBG("no match\n"); return -1; /* check failed */ error: return -1; } int ops_print_avp(void) { struct usr_avp **avp_list; struct usr_avp *avp; int_str val; str *name; /* go through all list */ avp_list = get_avp_list(); avp = *avp_list; for ( ; avp ; avp=avp->next) { LM_INFO("p=%p, flags=0x%04X\n",avp, avp->flags); name = get_avp_name(avp); LM_INFO("\t\t\tname=<%.*s>\n",name->len,name->s); LM_INFO("\t\t\tid=<%d>\n",avp->id); get_avp_val( avp, &val); if (avp->flags&AVP_VAL_STR) { LM_INFO("\t\t\tval_str=<%.*s / %d>\n",val.s.len,val.s.s, val.s.len); } else { LM_INFO("\t\t\tval_int=<%d>\n",val.n); } } return 1; } int ops_subst(struct sip_msg* msg, struct fis_param** src, struct subst_expr* se) { struct usr_avp *avp; struct usr_avp *prev_avp; int_str avp_val; unsigned short name_type1; unsigned short name_type2; int avp_name1; int avp_name2; int n; int nmatches; str* result; n = 0; prev_avp = 0; /* avp name is known ->search by name */ /* get src avp name */ if(avpops_get_aname(msg, src[0], &avp_name1, &name_type1)!=0) { LM_ERR("failed to get src AVP name\n"); return -1; } avp = search_first_avp(name_type1, avp_name1, &avp_val, 0); if(avp==NULL) return -1; if(src[1]!=0) { /* get dst avp name */ if(avpops_get_aname(msg, src[1], &avp_name2, &name_type2)!=0) { LM_ERR("failed to get dst AVP name\n"); return -1; } } else { name_type2 = name_type1; avp_name2 = avp_name1; } /* TODO: delete? if(name_type2&AVP_NAME_STR) { if(avp_name2.s.len>=STR_BUF_SIZE) { LM_ERR("dst name too long\n"); goto error; } strncpy(str_buf, avp_name2.s.s, avp_name2.s.len); str_buf[avp_name2.s.len] = '\0'; avp_name2.s.s = str_buf; } */ while(avp) { if(!is_avp_str_val(avp)) { prev_avp = avp; avp = search_first_avp(name_type1, avp_name1, &avp_val, prev_avp); continue; } result=subst_str(avp_val.s.s, msg, se, &nmatches); if(result!=NULL) { /* build a new avp with new name */ avp_val.s = *result; if(add_avp(name_type2|AVP_VAL_STR, avp_name2, avp_val)==-1 ) { LM_ERR("failed to create new avp\n"); if(result->s!=0) pkg_free(result->s); pkg_free(result); goto error; } if(result->s!=0) pkg_free(result->s); pkg_free(result); n++; /* copy all avps? */ if (!(src[0]->ops&AVPOPS_FLAG_ALL) ) { /* delete the old one? */ if (src[0]->ops&AVPOPS_FLAG_DELETE || src[1]==0) destroy_avp(avp); break; } else { prev_avp = avp; avp = search_first_avp(name_type1,avp_name1,&avp_val,prev_avp); /* delete the old one? */ if (src[0]->ops&AVPOPS_FLAG_DELETE || src[1]==0) destroy_avp( prev_avp ); } } else { prev_avp = avp; avp = search_first_avp(name_type1, avp_name1, &avp_val, prev_avp); } } LM_DBG("subst to %d avps\n", n); return n?1:-1; error: return -1; } int ops_op_avp( struct sip_msg* msg, struct fis_param** av, struct fis_param* val) { unsigned short name_type1; unsigned short name_type2; unsigned short name_type3; struct fis_param* src; struct usr_avp *avp1; struct usr_avp *avp2; struct usr_avp *prev_avp; int avp_name1; int avp_name2; int avp_name3; int_str avp_val; int_str op_val; int result; pv_value_t xvalue; src = av[0]; /* look if the required avp(s) is/are present */ /* search for the avp */ if(avpops_get_aname(msg, src, &avp_name1, &name_type1)!=0) { LM_ERR("failed to get src AVP name\n"); goto error; } avp1 = search_first_avp(name_type1, avp_name1, &avp_val, 0); if (avp1==0) { LM_DBG(" no src avp found\n"); goto error; } while(avp1!=0) { if(!(avp1->flags&AVP_VAL_STR)) break; avp1 = search_first_avp(name_type1, avp_name1, &avp_val, avp1); } if (avp1==0 && !(val->ops&AVPOPS_OP_BNOT)) { LM_DBG("no proper avp found\n"); goto error; } name_type3 = name_type1; avp_name3 = avp_name1; if(av[1]!=0) { if(avpops_get_aname(msg, av[1], &avp_name3, &name_type3)!=0) { LM_ERR("failed to get dst AVP name\n"); goto error; } } /* TODO: delete? if(name_type3&AVP_NAME_STR) { if(avp_name3.s.len>=STR_BUF_SIZE) { LM_ERR("failed to get dst name too long\n"); goto error; } strncpy(str_buf, avp_name3.s.s, avp_name3.s.len); str_buf[avp_name3.s.len] = '\0'; avp_name3.s.s = str_buf; } */ prev_avp = 0; result = 0; cycle1: if (val->opd&AVPOPS_VAL_PVAR) { /* the 2nd operator is variable -> get value */ if(val->u.sval.type==PVT_AVP) { /* search for the avp */ if(avpops_get_aname(msg, val, &avp_name2, &name_type2)!=0) { LM_ERR("failed to get dst AVP name\n"); goto error; } avp2 = search_first_avp( name_type2, avp_name2, &op_val, 0); while(avp2!=0) { if(!(avp2->flags&AVP_VAL_STR)) break; avp2 = search_first_avp( name_type2, avp_name2, &op_val, avp2); } if (avp2==0) { LM_DBG("no dst avp found\n"); goto error; } } else { avp2 = 0; if(pv_get_spec_value(msg, &(val->u.sval), &xvalue)!=0) { LM_ERR("cannot get dst value\n"); goto error; } if(xvalue.flags&PV_TYPE_INT) { op_val.n = xvalue.ri; } else { LM_ERR("dst value is str\n"); goto error; } } } else { if(val->type == AVPOPS_VAL_INT) op_val.n = val->u.n; else op_val.s = val->u.s; avp2 = 0; } cycle2: /* do operation */ LM_DBG(" use <%d> and <%d>\n", avp_val.n, op_val.n); if (val->ops&AVPOPS_OP_ADD) { result = avp_val.n+op_val.n; } else if (val->ops&AVPOPS_OP_SUB) { result = avp_val.n-op_val.n; } else if (val->ops&AVPOPS_OP_MUL) { result = avp_val.n*op_val.n; } else if (val->ops&AVPOPS_OP_DIV) { if(op_val.n!=0) result = (int)(avp_val.n/op_val.n); else { LM_ERR("division by 0\n"); result = 0; } } else if (val->ops&AVPOPS_OP_MOD) { if(op_val.n!=0) result = avp_val.n%op_val.n; else { LM_ERR("modulo by 0\n"); result = 0; } } else if (val->ops&AVPOPS_OP_BAND) { result = avp_val.n&op_val.n; } else if (val->ops&AVPOPS_OP_BOR) { result = avp_val.n|op_val.n; } else if (val->ops&AVPOPS_OP_BXOR) { result = avp_val.n^op_val.n; } else if (val->ops&AVPOPS_OP_BNOT) { result = ~op_val.n; } else { LM_CRIT("unknown operation (flg=%d)\n",val->ops); goto error; } /* add the new avp */ avp_val.n = result; if(add_avp(name_type3, avp_name3, avp_val)==-1 ) { LM_ERR("failed to create new avp\n"); goto error; } /* cycle for the second value (only if avp can have multiple vals) */ while((avp2!=NULL) &&(avp2=search_first_avp( name_type2, avp_name2, &op_val, avp2))!=0) { if(!(avp2->flags&AVP_VAL_STR)) goto cycle2; } prev_avp = avp1; /* cycle for the first value -> next avp */ while((avp1!=NULL) &&(avp1=search_first_avp(name_type1, avp_name1, &avp_val, avp1))!=0) { if (!(avp1->flags&AVP_VAL_STR)) { if(val->ops&AVPOPS_FLAG_DELETE && prev_avp!=0) { destroy_avp(prev_avp); prev_avp = 0; } goto cycle1; } } LM_DBG("done\n"); if(val->ops&AVPOPS_FLAG_DELETE && prev_avp!=0) { destroy_avp(prev_avp); prev_avp = 0; } return 1; error: return -1; } int ops_is_avp_set(struct sip_msg* msg, struct fis_param *ap) { struct usr_avp *avp; unsigned short name_type; int avp_name; int_str avp_value; int index; int findex; /* get avp name */ if(avpops_get_aname(msg, ap, &avp_name, &name_type)!=0) { LM_ERR("failed to get AVP name\n"); return -1; } /* get avp index */ if(pv_get_spec_index(msg, &ap->u.sval.pvp, &index, &findex)!=0) { LM_ERR("failed to get AVP index\n"); return -1; } avp=search_first_avp(name_type, avp_name, &avp_value, 0); if(avp==0) return -1; do { /* last index [-1] or all [*] go here as well */ if(index<=0) { if(ap->ops&AVPOPS_FLAG_ALL) return 1; if((ap->ops&AVPOPS_FLAG_CASTS && !(avp->flags&AVP_VAL_STR)) ||(ap->ops&AVPOPS_FLAG_CASTN && avp->flags&AVP_VAL_STR)) return -1; if(ap->ops&AVPOPS_FLAG_EMPTY) { if(avp->flags&AVP_VAL_STR) { if(avp_value.s.s==0 || avp_value.s.len==0) return 1; else return -1; } else { if(avp_value.n==0) return 1; else return -1; } } return 1; } index--; } while ((avp=search_first_avp(name_type, avp_name, &avp_value, avp))!=0); return -1; } int w_insert_avp(struct sip_msg* msg, char* name, char* value, char *index_char) { int index = *(int*)index_char; int avp_name; struct usr_avp *avp= NULL, *prev_avp= NULL; struct usr_avp *avp_new; unsigned short name_type; pv_value_t xvalue; int flags = 0; int_str avp_val; pv_elem_t* pv_dest = (pv_elem_t*)name; pv_elem_t* pv_src = (pv_elem_t*)value; /* get avp name */ if(pv_get_avp_name(msg, &pv_dest->spec.pvp, &avp_name, &name_type)< 0) { LM_ERR("failed to get src AVP name\n"); return -1; } /* get value to be inserted */ avp = NULL; flags = 0; if(pv_src->spec.type == PVT_NONE) { avp_val.s = pv_src->text; flags = AVP_VAL_STR; } else { if(pv_get_spec_value(msg, &(pv_src->spec), &xvalue)!=0) { LM_ERR("cannot get src value\n"); return -1; } if(xvalue.flags&PV_TYPE_INT) { avp_val.n = xvalue.ri; } else { flags = AVP_VAL_STR; avp_val.s = xvalue.rs; } } name_type |= flags; /* insert it at the right place */ if(index == 0) { if(add_avp(name_type, avp_name, avp_val) < 0) { LM_ERR("Failed to add new avp\n"); return -1; } return 1; } /* search the previous avp */ index--; avp = NULL; while ( (avp=search_first_avp( name_type, avp_name, 0, avp))!=0 ) { if( index == 0 ) { break; } index--; prev_avp = avp; } /* if the index is greater then the count */ if(avp == NULL) { if(prev_avp == NULL) { if(add_avp(name_type, avp_name, avp_val) < 0) { LM_ERR("Failed to add new avp\n"); return -1; } return 1; } avp = prev_avp; } /* if a previous record was found -> insert the new avp after it */ avp_new = new_avp(name_type, avp_name, avp_val); if(avp_new == NULL) { LM_ERR("Failed to allocate new avp structure\n"); return -1; } LM_DBG("am alocat un avp nou\n"); avp_new->next = avp->next; avp->next = avp_new; return 1; } opensips-2.2.2/modules/avpops/avpops_impl.h000066400000000000000000000116271300170765700210540ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2004-10-04 first version (ramona) */ #ifndef _AVP_OPS_IMPL_H_ #define _AVP_OPS_IMPL_H_ #include "../../str.h" #include "../../usr_avp.h" #include "../../pvar.h" #include "../../re.h" #include "../../parser/msg_parser.h" #include "avpops_db.h" /* flags used inside avps */ /* IMPORTANT: flagss 0-4 are reserved by core; 8-15 by script */ #define AVP_IS_IN_DB (1<<7) /* DB flags */ #define AVPOPS_DB_NAME_INT (1<<1) #define AVPOPS_DB_VAL_INT (1<<0) /* operand flags */ #define AVPOPS_VAL_NONE (1<<0) #define AVPOPS_VAL_INT (1<<1) #define AVPOPS_VAL_STR (1<<2) #define AVPOPS_VAL_PVAR (1<<3) #define AVPOPS_USE_FROM (1<<5) #define AVPOPS_USE_TO (1<<6) #define AVPOPS_USE_RURI (1<<7) #define AVPOPS_USE_USERNAME (1<<8) #define AVPOPS_USE_DOMAIN (1<<9) #define AVPOPS_USE_SRC_IP (1<<12) #define AVPOPS_USE_DST_IP (1<<13) #define AVPOPS_USE_DURI (1<<14) #define AVPOPS_USE_BRANCH (1<<15) /* flags for operation flags 24..31 */ #define AVPOPS_FLAG_USER0 (1<<24) #define AVPOPS_FLAG_DOMAIN0 (1<<25) #define AVPOPS_FLAG_URI0 (1<<26) #define AVPOPS_FLAG_UUID0 (1<<27) /* operation flags */ #define AVPOPS_OP_EQ (1<<0) #define AVPOPS_OP_NE (1<<1) #define AVPOPS_OP_LT (1<<2) #define AVPOPS_OP_LE (1<<3) #define AVPOPS_OP_GT (1<<4) #define AVPOPS_OP_GE (1<<5) #define AVPOPS_OP_RE (1<<6) #define AVPOPS_OP_FM (1<<7) #define AVPOPS_OP_BAND (1<<8) #define AVPOPS_OP_BOR (1<<9) #define AVPOPS_OP_BXOR (1<<10) #define AVPOPS_OP_BNOT (1<<11) #define AVPOPS_OP_ADD (1<<12) #define AVPOPS_OP_SUB (1<<13) #define AVPOPS_OP_MUL (1<<14) #define AVPOPS_OP_DIV (1<<15) #define AVPOPS_OP_MOD (1<<16) /* flags for operation flags 24..31 */ #define AVPOPS_FLAG_ALL (1<<24) #define AVPOPS_FLAG_CI (1<<25) #define AVPOPS_FLAG_DELETE (1<<26) #define AVPOPS_FLAG_CASTN (1<<27) #define AVPOPS_FLAG_CASTS (1<<28) #define AVPOPS_FLAG_EMPTY (1<<29) /* container structer for Flag+Int_Spec_value parameter */ struct fis_param { int ops; /* operation flags */ int opd; /* operand flags */ int type; union { pv_spec_t sval; /* values int or str */ int n; str s; } u; }; struct db_param { struct fis_param a; /* attribute */ str sa; /* attribute as str (for db queries) */ str table; /* DB table/scheme name */ struct db_scheme *scheme; /* DB scheme */ }; typedef struct _query_async_param { pvname_list_t *output_avps; db_con_t *hdl; db_func_t *dbf; void *db_param; } query_async_param; void init_store_avps(str **db_columns); int ops_dbload_avps (struct sip_msg* msg, struct fis_param *sp, struct db_param *dbp, struct db_url *url, int use_domain, str *prefix); int ops_dbdelete_avps(struct sip_msg* msg, struct fis_param *sp, struct db_param *dbp, struct db_url *url, int use_domain); int ops_dbstore_avps(struct sip_msg* msg, struct fis_param *sp, struct db_param *dbp, struct db_url *url, int use_domain); int ops_dbquery_avps(struct sip_msg* msg, pv_elem_t* query, struct db_url *url, pvname_list_t* dest); int ops_async_dbquery(struct sip_msg* msg, async_resume_module **rf, void **rparam, pv_elem_t *query, struct db_url *url, pvname_list_t *dest); int resume_async_dbquery(int fd, struct sip_msg *msg, void *_param); int ops_delete_avp(struct sip_msg* msg, struct fis_param *ap); int ops_copy_avp(struct sip_msg* msg, struct fis_param* name1, struct fis_param* name2); int ops_pushto_avp(struct sip_msg* msg, struct fis_param* dst, struct fis_param* ap); int ops_check_avp(struct sip_msg* msg, struct fis_param* param, struct fis_param* check); int ops_op_avp(struct sip_msg* msg, struct fis_param** param, struct fis_param* op); int ops_subst(struct sip_msg* msg, struct fis_param** src, struct subst_expr* subst); int ops_is_avp_set(struct sip_msg* msg, struct fis_param *ap); int ops_print_avp(); int w_insert_avp(struct sip_msg* msg, char* name, char* value, char *index_char); #endif opensips-2.2.2/modules/avpops/avpops_parse.c000066400000000000000000000371761300170765700212270ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2004-10-04 first version (ramona) * 2004-11-11 DB scheme added (ramona) * 2004-11-17 aligned to new AVP core global aliases (ramona) */ #include #include #include "../../ut.h" #include "../../dprint.h" #include "../../usr_avp.h" #include "../../mem/mem.h" #include "avpops_parse.h" #define SCHEME_UUID_COL "uuid_col" #define SCHEME_UUID_COL_LEN (sizeof(SCHEME_UUID_COL)-1) #define SCHEME_USERNAME_COL "username_col" #define SCHEME_USERNAME_COL_LEN (sizeof(SCHEME_USERNAME_COL)-1) #define SCHEME_DOMAIN_COL "domain_col" #define SCHEME_DOMAIN_COL_LEN (sizeof(SCHEME_DOMAIN_COL)-1) #define SCHEME_VALUE_COL "value_col" #define SCHEME_VALUE_COL_LEN (sizeof(SCHEME_VALUE_COL)-1) #define SCHEME_TABLE "table" #define SCHEME_TABLE_LEN (sizeof(SCHEME_TABLE)-1) #define SCHEME_VAL_TYPE "value_type" #define SCHEME_VAL_TYPE_LEN (sizeof(SCHEME_VAL_TYPE)-1) #define SCHEME_INT_TYPE "integer" #define SCHEME_INT_TYPE_LEN (sizeof(SCHEME_INT_TYPE)-1) #define SCHEME_STR_TYPE "string" #define SCHEME_STR_TYPE_LEN (sizeof(SCHEME_STR_TYPE)-1) char *parse_avp_attr(char *s, struct fis_param *attr, char end) { unsigned int uint; str tmp; /*LM_DBG("s=%p s=%c(%d)\n",s,*s,*s);*/ /* search for type identifier */ if ( s[0] && s[1]==':' ) { switch (s[0]) { case 'i': case 'I': attr->opd |= AVPOPS_VAL_INT; break; case 's': case 'S': attr->opd |= AVPOPS_VAL_STR; break; default: LM_ERR("invalid type '%c'\n", s[0]); goto error; } s += 2; } /* search for the avp name */ tmp.s = s; while ( *s && *s!=end && !isspace((int)*s)) s++; tmp.len = s - tmp.s; if (tmp.len==0) { attr->opd |= AVPOPS_VAL_NONE; } else { if ( attr->opd&AVPOPS_VAL_INT) { /* convert to ID (int) */ if ( str2int( &tmp, &uint)==-1 ) { LM_ERR("attribute is not int as type says <%s>\n", tmp.s); goto error; } attr->type = AVPOPS_VAL_INT; attr->u.n = (int)uint; } else { /* duplicate name as str NULL terminated */ attr->u.s.s = (char*)pkg_malloc(tmp.len + 1); if (attr->u.s.s==0) { LM_ERR("no more pkg mem\n"); goto error; } attr->type = AVPOPS_VAL_STR; attr->u.s.len = tmp.len; memcpy(attr->u.s.s, tmp.s, tmp.len); attr->u.s.s[attr->u.s.len] = 0; } } return s; error: return 0; } struct fis_param *avpops_parse_pvar(char *in) { struct fis_param *ap; str s; /* compose the param structure */ ap = (struct fis_param*)pkg_malloc(sizeof(struct fis_param)); if (ap==0) { LM_ERR("no more pkg mem\n"); return NULL; } memset( ap, 0, sizeof(struct fis_param)); s.s = in; s.len = strlen(s.s); if(pv_parse_spec(&s, &ap->u.sval)==0) { pkg_free(ap); return NULL; } ap->opd |= AVPOPS_VAL_PVAR; ap->type = AVPOPS_VAL_PVAR; return ap; } int parse_avp_db(char *s, struct db_param *dbp, int allow_scheme) { str tmp; str s0; str *s1; char have_scheme; char *p; char *p0; unsigned int flags; LM_DBG("parse: %s\n", s); tmp.s = s; /* parse the attribute name - check first if it's not an alias */ p0=strchr(tmp.s, '/'); if(p0!=NULL) *p0=0; if ( *s!='$') { if(strlen(s)<1) { LM_ERR("bad param - expected : $avp(name), *, s or i value\n"); return E_UNSPEC; } switch(*s) { /* deteleted because of the new avp format */ case 's': case 'S': case 'i': case 'I': case '*': case 'a': case 'A': dbp->a.opd = AVPOPS_VAL_NONE; break; default: LM_ERR("bad param - expected : *, s or i AVP flag\n"); return E_UNSPEC; } /* flags */ flags = 0; if(*(s+1)!='\0') { s0.s = s+1; s0.len = strlen(s0.s); if(str2int(&s0, &flags)!=0) { LM_ERR("error - bad avp flags\n"); goto error; } } dbp->a.u.sval.pvp.pvn.u.isname.type |= (flags<<8)&0xff00; dbp->a.type = AVPOPS_VAL_NONE; } else { s0.s = s; s0.len = strlen(s0.s); p = pv_parse_spec(&s0, &dbp->a.u.sval); if (p==0 || *p!='\0' || dbp->a.u.sval.type!=PVT_AVP) { LM_ERR("bad param - expected : $avp(name) or int/str value\n"); return E_UNSPEC; } dbp->a.type = AVPOPS_VAL_PVAR; } /* optimize and keep the attribute name as str also to * speed up db querie builds */ if (dbp->a.type == AVPOPS_VAL_PVAR) { dbp->a.opd = AVPOPS_VAL_PVAR; if(pv_has_iname(&dbp->a.u.sval)) { s1 = get_avp_name_id(dbp->a.u.sval.pvp.pvn.u.isname.name.n); if (!s1) { LM_ERR("cannot find avp name\n"); goto error; } dbp->sa.s=(char*)pkg_malloc(s1->len + 1); if (dbp->sa.s==0) { LM_ERR("no more pkg mem\n"); goto error; } memcpy(dbp->sa.s, s1->s, s1->len); dbp->sa.len = s1->len; dbp->sa.s[dbp->sa.len] = 0; dbp->a.opd = AVPOPS_VAL_PVAR|AVPOPS_VAL_STR; } } /* restore '/' */ if(p0) *p0 = '/'; /* is there a table name ? */ s = p0; if (s && *s) { s++; if (*s=='$') { if (allow_scheme==0) { LM_ERR("function doesn't support DB schemes\n"); goto error; } if (dbp->a.opd&AVPOPS_VAL_NONE) { LM_ERR("inconsistent usage of " "DB scheme without complet specification of AVP name\n"); goto error; } have_scheme = 1; s++; } else { have_scheme = 0; } tmp.s = s; tmp.len = 0; while ( *s ) s++; tmp.len = s - tmp.s; if (tmp.len==0) { LM_ERR("empty scheme/table name\n"); goto error; } if (have_scheme) { dbp->scheme = avp_get_db_scheme( &tmp ); if (dbp->scheme==0) { LM_ERR("scheme <%s> not found\n", tmp.s); goto error; } /* update scheme flags with AVP name type*/ dbp->scheme->db_flags|=dbp->a.opd&AVPOPS_VAL_STR?AVP_NAME_STR:0; } else { /* duplicate table str into the db_param struct */ pkg_str_dup( &dbp->table, &tmp); } } return 0; error: return -1; } struct fis_param* parse_intstr_value(char *p, int len) { struct fis_param *vp; unsigned int uint; str val_str; int flags; if (p==0 || len==0) goto error; if (len>1 && *(p+1)==':') { if (*p=='i' || *p=='I') flags = AVPOPS_VAL_INT; else if (*p=='s' || *p=='S') flags = AVPOPS_VAL_STR; else { LM_ERR("unknown value type <%c>\n",*p); goto error; } p += 2; len -= 2; if (*p==0 || len<=0 ) { LM_ERR("parse error around <%.*s>\n",len,p); goto error; } } else { flags = AVPOPS_VAL_STR; } /* get the value */ vp = (struct fis_param*)pkg_malloc(sizeof(struct fis_param)); if (vp==0) { LM_ERR("no more pkg mem\n"); goto error;; } memset( vp, 0, sizeof(struct fis_param)); vp->opd = flags; val_str.s = p; val_str.len = len; if (flags&AVPOPS_VAL_INT) { /* convert the value to integer */ if(val_str.len>2 && p[0]=='0' && (p[1]=='x' || p[1]=='X')) { if(hexstr2int(val_str.s+2, val_str.len-2, &uint)) { LM_ERR("value is not hex int as type says <%.*s>\n", val_str.len, val_str.s); goto error; } } else { if(str2sint( &val_str, (int*)&uint)==-1) { LM_ERR("value is not int" " as type says <%.*s>\n", val_str.len, val_str.s); goto error; } } vp->u.n = (int)uint; vp->type = AVPOPS_VAL_INT; } else { /* duplicate the value as string */ vp->u.s.s = (char*)pkg_malloc((val_str.len+1)*sizeof(char)); if (vp->u.s.s==0) { LM_ERR("no more pkg mem\n"); goto error; } vp->u.s.len = val_str.len; memcpy(vp->u.s.s, val_str.s, val_str.len); vp->u.s.s[vp->u.s.len] = 0; vp->type = AVPOPS_VAL_STR; } return vp; error: return 0; } #define duplicate_str(_p, _str, _error) \ do { \ _p.s = (char*)pkg_malloc(_str.len+1); \ if (_p.s==0) \ { \ LM_ERR("no more pkg memory\n");\ goto _error; \ } \ _p.len = _str.len; \ memcpy( _p.s, _str.s, _str.len); \ _p.s[_str.len] = 0; \ }while(0) int parse_avp_db_scheme( char *s, struct db_scheme *scheme) { str foo; str bar; char *p; if (s==0 || *s==0) goto error; p = s; /*parse the name */ while (*p && isspace((int)*p)) p++; foo.s = p; while (*p && *p!=':' && !isspace((int)*p)) p++; if (foo.s==p || *p==0) /* missing name or empty scheme */ goto parse_error; foo.len = p - foo.s; /* dulicate it */ duplicate_str( scheme->name, foo, error); /* parse the ':' separator */ while (*p && isspace((int)*p)) p++; if (*p!=':') goto parse_error; p++; while (*p && isspace((int)*p)) p++; if (*p==0) goto parse_error; /* set as default value type string */ scheme->db_flags = AVP_VAL_STR; /* parse the attributes */ while (*p) { /* get the attribute name */ foo.s = p; while (*p && *p!='=' && !isspace((int)*p)) p++; if (p==foo.s || *p==0) /* missing attribute name */ goto parse_error; foo.len = p - foo.s; /* parse the '=' separator */ while (*p && isspace((int)*p)) p++; if (*p!='=') goto parse_error; p++; while (*p && isspace((int)*p)) p++; if (*p==0) goto parse_error; /* parse the attribute value */ bar.s = p; while (*p && *p!=';' && !isspace((int)*p)) p++; if (p==bar.s) /* missing attribute value */ goto parse_error; bar.len = p - bar.s; /* parse the ';' separator, if any */ while (*p && isspace((int)*p)) p++; if (*p!=0 && *p!=';') goto parse_error; if (*p==';') p++; while (*p && isspace((int)*p)) p++; /* identify the attribute */ if ( foo.len==SCHEME_UUID_COL_LEN && !strncasecmp( foo.s, SCHEME_UUID_COL, foo.len) ) { if (scheme->uuid_col.s) goto parse_error; duplicate_str( scheme->uuid_col, bar, error); } else if ( foo.len==SCHEME_USERNAME_COL_LEN && !strncasecmp( foo.s, SCHEME_USERNAME_COL, foo.len) ) { if (scheme->username_col.s) goto parse_error; duplicate_str( scheme->username_col, bar, error); } else if ( foo.len==SCHEME_DOMAIN_COL_LEN && !strncasecmp( foo.s, SCHEME_DOMAIN_COL, foo.len) ) { if (scheme->domain_col.s) goto parse_error; duplicate_str( scheme->domain_col, bar, error); } else if ( foo.len==SCHEME_VALUE_COL_LEN && !strncasecmp( foo.s, SCHEME_VALUE_COL, foo.len) ) { if (scheme->value_col.s) goto parse_error; duplicate_str( scheme->value_col, bar, error); } else if ( foo.len==SCHEME_TABLE_LEN && !strncasecmp( foo.s, SCHEME_TABLE, foo.len) ) { if (scheme->table.s) goto parse_error; duplicate_str( scheme->table, bar, error); } else if ( foo.len==SCHEME_VAL_TYPE_LEN && !strncasecmp( foo.s, SCHEME_VAL_TYPE, foo.len) ) { if ( bar.len==SCHEME_INT_TYPE_LEN && !strncasecmp( bar.s, SCHEME_INT_TYPE, bar.len) ) scheme->db_flags &= (~AVP_VAL_STR); else if ( bar.len==SCHEME_STR_TYPE_LEN && !strncasecmp( bar.s, SCHEME_STR_TYPE, bar.len) ) scheme->db_flags = AVP_VAL_STR; else { LM_ERR("unknown value type <%.*s>\n",bar.len,bar.s); goto error; } } else { LM_ERR("unknown attribute <%.*s>\n",foo.len,foo.s); goto error; } } /* end while */ return 0; parse_error: LM_ERR("parse error in <%s> around %ld\n", s, (long)(p-s)); error: return -1; } struct fis_param* parse_check_value(char *s) { struct fis_param *vp; int ops; int opd; char *p; char *t; int len; ops = 0; opd = 0; vp = 0; if ( (p=strchr(s,'/'))==0 || (p-s!=2&&p-s!=3) ) goto parse_error; /* get the operation */ if (strncasecmp(s,"eq",2)==0) { ops |= AVPOPS_OP_EQ; } else if (strncasecmp(s,"ne",2)==0) { ops |= AVPOPS_OP_NE; } else if (strncasecmp(s,"lt",2)==0) { ops |= AVPOPS_OP_LT; } else if (strncasecmp(s,"le",2)==0) { ops |= AVPOPS_OP_LE; } else if (strncasecmp(s,"gt",2)==0) { ops |= AVPOPS_OP_GT; } else if (strncasecmp(s,"ge",2)==0) { ops |= AVPOPS_OP_GE; } else if (strncasecmp(s,"re",2)==0) { ops |= AVPOPS_OP_RE; } else if (strncasecmp(s,"fm",2)==0) { ops |= AVPOPS_OP_FM; } else if (strncasecmp(s,"and",3)==0) { ops |= AVPOPS_OP_BAND; } else if (strncasecmp(s,"or",2)==0) { ops |= AVPOPS_OP_BOR; } else if (strncasecmp(s,"xor",3)==0) { ops |= AVPOPS_OP_BXOR; } else { LM_ERR("unknown operation <%.*s>\n",2,s); goto error; } /* get the value */ if (*(++p)==0) goto parse_error; if ( (t=strchr(p,'/'))==0) len = strlen(p); else len = t-p; if (*p=='$') { /* is variable */ vp = avpops_parse_pvar(p); if (vp==0) { LM_ERR("unable to get pseudo-variable\n"); goto error; } if (vp->u.sval.type==PVT_NULL) { LM_ERR("bad param; expected : $pseudo-variable or int/str value\n"); goto error; } opd |= AVPOPS_VAL_PVAR; LM_DBG("flag==%d/%d\n", opd, ops); } else { /* value is explicitly given */ if ( (vp=parse_intstr_value(p,len))==0) { LM_ERR("unable to parse value\n"); goto error; } } p = t; /* any flags */ if (p!=NULL && *p!=0) { if (*p!='/' || *(++p)==0) goto parse_error; while (*p) { switch (*p) { case 'g': case 'G': ops|=AVPOPS_FLAG_ALL; break; case 'i': case 'I': ops|=AVPOPS_FLAG_CI; break; default: LM_ERR("unknown flag <%c>\n",*p); goto error; } p++; } } vp->ops |= ops; vp->opd |= opd; return vp; parse_error: LM_ERR("parse error in <%s> pos %ld\n", s,(long)(p-s)); error: if (vp) pkg_free(vp); return 0; } struct fis_param* parse_op_value(char *s) { struct fis_param *vp; int ops; int opd; char *p; char *t; int len; ops = 0; opd = 0; vp = 0; if ( (p=strchr(s,'/'))==0 || (p-s!=2&&p-s!=3) ) goto parse_error; /* get the operation */ if (strncasecmp(s,"add",3)==0) { ops |= AVPOPS_OP_ADD; } else if (strncasecmp(s,"sub",3)==0) { ops |= AVPOPS_OP_SUB; } else if (strncasecmp(s,"mul",3)==0) { ops |= AVPOPS_OP_MUL; } else if (strncasecmp(s,"div",3)==0) { ops |= AVPOPS_OP_DIV; } else if (strncasecmp(s,"mod",3)==0) { ops |= AVPOPS_OP_MOD; } else if (strncasecmp(s,"and",3)==0) { ops |= AVPOPS_OP_BAND; } else if (strncasecmp(s,"or",2)==0) { ops |= AVPOPS_OP_BOR; } else if (strncasecmp(s,"xor",3)==0) { ops |= AVPOPS_OP_BXOR; } else if (strncasecmp(s,"not",3)==0) { ops |= AVPOPS_OP_BNOT; } else { LM_ERR("unknown operation <%.*s>\n",2,s); goto error; } /* get the value */ if (*(++p)==0) goto parse_error; if ( (t=strchr(p,'/'))==0) len = strlen(p); else len = t-p; if (*p=='$') { /* is variable */ vp = avpops_parse_pvar(p); if (vp==0) { LM_ERR("unable to get pseudo-variable\n"); goto error; } if (vp->u.sval.type==PVT_NULL) { LM_ERR("bad param; expected : $pseudo-variable or int/str value\n"); goto error; } opd |= AVPOPS_VAL_PVAR; LM_DBG("flag==%d/%d\n", opd, ops); } else { /* value is explicitly given */ if ( (vp=parse_intstr_value(p,len))==0) { LM_ERR("unable to parse value\n"); goto error; } if((vp->opd&AVPOPS_VAL_INT)==0) { LM_ERR("value must be int\n"); goto error; } } /* any flags */ p = t; if (p!=0 && *p!=0 ) { if (*p!='/' || *(++p)==0) goto parse_error; while (*p) { switch (*p) { case 'g': case 'G': ops|=AVPOPS_FLAG_ALL; break; case 'd': case 'D': ops|=AVPOPS_FLAG_DELETE; break; default: LM_ERR("unknown flag <%c>\n",*p); goto error; } p++; } } vp->ops |= ops; vp->opd |= opd; return vp; parse_error: LM_ERR("parse error in <%s> pos %ld\n", s,(long)(p-s)); error: if (vp) pkg_free(vp); return 0; } opensips-2.2.2/modules/avpops/avpops_parse.h000066400000000000000000000027321300170765700212220ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2004-10-04 first version (ramona) */ #ifndef _AVPOPS_PARSE_H_ #define _AVPOPS_PARSE_H_ #include "../../str.h" #include "../../usr_avp.h" #include "avpops_impl.h" #include "avpops_db.h" char *parse_avp_attr(char *start, struct fis_param *attr, char end); struct fis_param *avpops_parse_pvar(char *s); int parse_avp_db(char *s, struct db_param *dbp, int allow_scheme); int parse_avp_aliases(char *s, char c1, char c2); struct fis_param* parse_intstr_value(char *p, int len); int parse_avp_db_scheme(char *s, struct db_scheme *scheme); struct fis_param* parse_check_value(char *s); struct fis_param* parse_op_value(char *s); #endif opensips-2.2.2/modules/avpops/doc/000077500000000000000000000000001300170765700171105ustar00rootroot00000000000000opensips-2.2.2/modules/avpops/doc/avpops.xml000066400000000000000000000020121300170765700211350ustar00rootroot00000000000000 %docentities; ]> AVPops Module &osipsname; Ramona-Elena Modroiu ramona@rosdev.ro Ramona-Elena Modroiu ramona@rosdev.ro 2004-2008 &voicesystem; $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/avpops/doc/avpops_admin.xml000066400000000000000000001037021300170765700223150ustar00rootroot00000000000000 &adminguide;
Overview AVPops (AVP-operations) modules implements a set of script functions which allow access and manipulation of user AVPs (preferences) and pseudo-variables. AVPs are a powerful tool for implementing services/preferences per user/domain. Now they are usable directly from configuration script. Functions for interfacing DB resources (loading/storing/removing), functions for swapping information between AVPs and SIP messages, function for testing/checking the value of an AVP. AVPs are persistent per SIP transaction, being available in "route", "branch_route" and "failure_route". To make them available in "onreply_route" armed via TM module, set "onreply_avp_mode" parameter of TM module (note that in the default "onreply_route", the AVPs of the transaction are not available).
Dependencies
&osips; Modules The following modules must be loaded before this module: Optionally a database module
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None
AVP naming format The format of the parameters specifying an AVP in functions exported by this module is: $avp(avp_name). avp_name = string | integer string - might be any alphanumeric string, wich contain following characters: [a-z] [A-Z] [0-9] '_' AVP naming examples ... $avp(11) - the AVP identified by name 11 $avp(foo) - the AVP identified by the string 'foo' ...
Exported Parameters
<varname>db_url</varname> (string) DB URL for database connection. As the module allows the usage of multiple DBs (DB URLs), the actual DB URL may be preceded by an reference number. This reference number is to be passed to AVPOPS function that what to explicitly use this DB connection. If no reference number is given, 0 is assumed - this is the default DB URL. This parameter is optional, it's default value being NULL. Set <varname>avp_url</varname> parameter ... # default URL modparam("avpops","db_url","mysql://user:passwd@host/database") # an additional DB URL modparam("avpops","db_url","1 postgres://user:passwd@host2/opensips") ...
<varname>avp_table</varname> (string) DB table to be used. This parameter is optional, it's default value being NULL. Set <varname>avp_table</varname> parameter ... modparam("avpops","avp_table","avptable") ...
<varname>use_domain</varname> (integer) If the domain part of the an URI should be used for identifying an AVP in DB operations. Default value is 0 (no). Set <varname>use_domain</varname> parameter ... modparam("avpops","use_domain",1) ...
<varname>uuid_column</varname> (string) Name of column containing the uuid (unique user id). Default value is uuid. Set <varname>uuid_column</varname> parameter ... modparam("avpops","uuid_column","uuid") ...
<varname>username_column</varname> (string) Name of column containing the username. Default value is username. Set <varname>username_column</varname> parameter ... modparam("avpops","username_column","username") ...
<varname>domain_column</varname> (string) Name of column containing the domain name. Default value is domain. Set <varname>domain_column</varname> parameter ... modparam("avpops","domain_column","domain") ...
<varname>attribute_column</varname> (string) Name of column containing the attribute name (AVP name). Default value is attribute. Set <varname>attribute_column</varname> parameter ... modparam("avpops","attribute_column","attribute") ...
<varname>value_column</varname> (string) Name of column containing the AVP value. Default value is value. Set <varname>value_column</varname> parameter ... modparam("avpops","value_column","value") ...
<varname>type_column</varname> (string) Name of column containing the AVP type. Default value is type. Set <varname>type_column</varname> parameter ... modparam("avpops","type_column","type") ...
<varname>db_scheme</varname> (string) Definition of a DB scheme to be used for non-standard access to Database information. Definition of a DB scheme. Scheme syntax is: db_scheme = name':'element[';'element]* element = 'uuid_col='string 'username_col='string 'domain_col='string 'value_col='string 'value_type='('integer'|'string') 'table='string Default value is NULL. Set <varname>db_scheme</varname> parameter ... modparam("avpops","db_scheme", "scheme1:table=subscriber;uuid_col=uuid;value_col=first_name") ...
<varname>buf_size</varname> (integer) Allocated size for AVP variables. Default value is 1024. Set <varname>buf_size</varname> parameter ... modparam("avpops", "buf_size", 1024) ...
Exported Functions
<function moreinfo="none">avp_db_load(source, name[, db_id[, prefix]]) </function> Loads from DB into memory the AVPs corresponding to the given source. If given, it sets the script flags for loaded AVPs. It returns true if it loaded some values in AVPs, false otherwise (db error, no avp loaded ...). AVPs may be preceded by an optional prefix, in order to avoid some conflicts. Meaning of the parameters is as follows: source - what info is used for identifying the AVPs. Parameter syntax: source = (pvar|str_value) ['/'('username'|'domain'|'uri'|'uuid')]) pvar = any pseudo variable defined in &osips;. If the pvar is $ru (request uri), $fu (from uri), $tu (to uri) or $ou (original uri), then the implicit flag is 'uri'. Otherwise, the implicit flag is 'uuid'. name - which AVPs will be loaded from DB into memory. Parameter syntax is: name = avp_spec['/'(table_name|'$'db_scheme)] avp_spec = matching_flags|$avp(avp_name)|$avp(avp_alias) matching_flags = 'a' | 'A' | 'i' | 'I' | 's' | 'S' [script_flags] 'a' or 'A' means matching any of AVP name types ('i' and 's'), the rest have the meaning descriped in 'AVP naming format' chapter. db_id - reference to a defined DB URL (a numerical id) - see the db_url module parameter. prefix - static string which will precede the names of the AVPs populated by this function. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_db_load</function> usage ... avp_db_load("$fu", "$avp(678)"); avp_db_load("$ru/domain", "i/domain_preferences"); avp_db_load("$avp(uuid)", "$avp(404fwd)/fwd_table"); avp_db_load("$ru", "$avp(123)/$some_scheme"); # use DB URL id 3 avp_db_load("$ru", "$avp(1)", "3"); # precede all loaded AVPs by the "caller_" prefix avp_db_load("$ru", "$avp(100)", "", "caller_"); xlog("Loaded: $avp(caller_100)\n"); ...
<function moreinfo="none">avp_db_store(source,name[,db_id])</function> Stores to DB the AVPs corresponding to the given source. The meaning and usage of the parameters are identical as for avp_db_load(source,name) function. Please refer to its description. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_db_store</function> usage ... avp_db_store("$tu","$avp(678)"); avp_db_store("$ru/username","$avp(email)"); # use DB URL id 3 avp_db_store("$ru","$avp(1)","3"); ...
<function moreinfo="none">avp_db_delete(source,name[,db_id])</function> Deletes from DB the AVPs corresponding to the given source. The meaning and usage of the parameters are identical as for avp_db_load(source,name) function. Please refer to its description. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_db_delete</function> usage ... avp_db_delete("$tu","$avp(678)"); avp_db_delete("$ru/username","$avp(email)"); avp_db_delete("$avp(uuid)","$avp(404fwd)/fwd_table"); # use DB URL id 3 avp_db_delete("$ru","$avp(1)","3"); ...
<function moreinfo="none">avp_db_query(query[[,dest],db_id])</function> Make a database query and store the result in AVPs. The meaning and usage of the parameters: query - must be a valid SQL query. The parameter can contain pseudo-variables. You must escape any pseudo-variables manually to prevent SQL injection attacks. You can use the existing transformations escape.common and unescape.common to escape and unescape the content of any pseudo-variable. Failing to escape the variables used in the query makes you vulnerable to SQL injection, e.g. make it possible for an outside attacker to alter your database content. The function returns true if the query was successful, -2 in case the query returned an empty result set, and -1 for all other types of errors dest - a list with AVP names where to store the result. The format is $avp(name1);$avp(name2);.... If this parameter is ommited, the result is stored in $avp(1);$avp(2);.... If the result gives many rows, then multiple AVPs with corresponding name will be added. The value type of the AVP (string or integer) will be derived from the type of the columns. db_id - reference to a defined DB URL (a numerical id) - see the db_url module parameter. It can be either a constant, or a string/int variable. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_db_query</function> usage ... avp_db_query("select password, ha1 from subscriber where username='$tu'", "$avp(678);$avp(679)"); avp_db_query("delete from subscriber"); avp_db_query("delete from subscriber","","2"); $avp(id)=2;#also works $avp(id)="2" avp_db_query("delete from subscriber","","$avp(id)"); ...
<function moreinfo="none">avp_delete(name) </function> Deletes from memory the AVPs with name or, if *, all AVPs. Meaning of the parameters is as follows: name - which AVPs will be deleted from memory. Parameter syntax is: name = (matching_flags|avp_name|avp_alias)['/'flag] matching_flags = please refer to avp_db_load() function flag = 'g'|'G' This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_delete</function> usage ... avp_delete("$avp(678)/g"); avp_delete("$avp(email)"); avp_delete("i"); avp_delete("a3"); ...
<function moreinfo="none">avp_pushto(destination,name) </function> Pushes the value of AVP(s) into the SIP message. Meaning of the parameters is as follows: destination - as what will be the AVP value pushed into SIP message. Parameter syntax: destination = '$ru' ['/'('username'|'domain')] | '$du' | '$br' $ru '['/'('username'|'domain')] - write the AVP in the request URI or in username/domain part of it $du - write the AVP in 'dst_uri' field $br - write the AVP directly as a new branch (does not affect RURI) name - which AVP(s)/pseudo-variable should be pushed into the SIP message. Parameter syntax is: name = ( avp_name | avp_alias | pvar_name )['/'flags] flags = 'g' - effective only with AVPs This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_pushto</function> usage ... avp_pushto("$ru/domain","$fd"); avp_pushto("$ru","$avp(678)"); avp_pushto("$ru/domain","$avp(backup_domains)/g"); avp_pushto("$du","$avp(679)"); avp_pushto("$br","$avp(680)"); ...
<function moreinfo="none">avp_check(name,op_value) </function> Checks the value of the AVP(s) against an operator and value. Meaning of the parameters is as follows: name - which AVP(s) should be checked. Parameter syntax is: name = ( pseudo-variable ) op_value - define the operator, the value and flags for checking. Parameter syntax is: op_value = operator '/' value ['/'flags] operator = 'eq' | 'ne' | 'lt' | 'le' | 'gt' | 'ge' | 're' | 'fm' | 'and' | 'or' | 'xor' value = pseudo-variable | fix_value fix_value = 'i:'integer | 's:'string | string flags = 'g' | 'G' | 'i' | 'I' Operator meaning: eq - equal ne - not equal lt - less than le - less or equal gt - greater than ge - greater or equal re - regexp (regular exression match) fm - fast match (see: man fnmatch) and - bitwise 'and' or - bitwise 'or' xor - bitwise 'xor' Integer values can be given in hexadecimal using notation: 'i:0xhex_number' (e.g.,: 'i:0xabcd'); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_check</function> usage ... avp_check("$avp(678)", "lt/345/g"); avp_check("$fd","eq/$td/I"); avp_check("$avp(foo)","gt/$avp($bar)/g"); avp_check("$avp(foo)","re/sip:.*@bar.net/g"); avp_check("$avp(foo)","fm/$avp(fm_avp)/g"); ...
<function moreinfo="none">avp_copy(old_name,new_name) </function> Copy / move an avp under a new name. Meaning of the parameters is as follows: name1 - which AVP(s) should be copied/moved. Parameter syntax is: name = ( avp_name | avp_alias ) name2 - the new name of the copied/moved AVP(s). Parameter syntax is: name = ( avp_name | avp_alias ) ['/'flags] flags = 'g' | 'G' | 'd' | 'D' | 'n' | 'N' | 's' | 'S' This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_copy</function> usage ... avp_copy("$avp(678)", "$avp(345)/g"); avp_copy("$avp(old)","$avp(new)/gd"); ...
<function moreinfo="none">avp_printf(dest, format) </function> NOTE: since &osips; 1.3.0 the function has been moved to core and it is an alias to pv_printf(). Prints the formatted string 'format' in the AVP 'dest'. The 'format' parameter can include any pseudo-variable defined in &osips;. The list with all pseudo-variables in &osips; can be found at: http://opensips.org/dokuwiki/. Meaning of the parameters is as follows: dest - in which AVP should be stored the result. Parameter syntax is: name = ( avp_name | avp_alias ) format - the formatted string to be printed in 'dest' AVP. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_printf</function> usage ... avp_printf("$avp(20)", "This is a $rm request with call-id $hdr(call-id)"); ...
<function moreinfo="none">avp_subst(avps, subst)</function> Perl/sed-like subst applied to AVPs having string value. Meaning of the parameters is as follows: avps - source AVP, destination AVP and flags. Parameter syntax is: avps = src_avp [ '/' dst_avp [ '/' flags ] ] src_avp = ( avp_name | avp_alias ) dst_avp = ( avp_name | avp_alias ) - if dst_avp is missing then the value of src_avp will be replaced flags = ( d | D | g | G ) -- (d, D - delete source avp; g, G - apply to all avps matching src_avp name) subst - perl/sed-like reqular expression. Parameter syntax is: subst = "/regexp/replacement/flags" regexp - regular expression replacement - replacement string, can include pseudo-variables and \1, ..., \9 for matching tokens, \0 for whole matching text flags = 'g' | 'G' | 'i' | 'i' (g, G - replace all matching tokens; i, I - match ignore case) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_subst</function> usage ... # if avp 678 has a string value in e-mail format, replace the # domain part with the value of domain part from R-URI avp_subst("$avp(678)", "/(.*)@(.*)/\1@$rd/"); # if any avp 678 has a string value in e-mail format, replace the # domain part with the value of domain part from R-URI # and place the result in avp 679 avp_subst("$avp(678)/$avp(679)/g", "/(.*)@(.*)/\1@$rd/"); ... IMPORTANT NOTE: if the replacement string includes src_avp or dst_avp you will get something that you may not expect. In case you have many src_avp and you make the substitution to be applied to all of them, after the first src_avp is processed, it will be added in avp list and next processing will use it.
<function moreinfo="none">avp_op(name,op_value) </function> Different integer operations with avps. Meaning of the parameters is as follows: name - 'source_avp/destination_avp' - which AVP(s) should be processed and where to store the result. If 'destination_avp' is missing, same name as 'source_avp' is used to store the result. Parameter syntax is: name = ( source_avp[/destination_avp] ) source_avp = ( avp_name | avp_alias ) destination_avp = ( avp_name | avp_alias ) op_value - define the operation, the value and flags. Parameter syntax is: op_value = operator '/' value ['/'flags] operator = 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'and' | 'or' | 'xor' | 'not' value = pseudo-variable | fix_value fix_value = 'i:'integer flags = 'g' | 'G' | 'd' | 'D' Integer values can be given in hexadecimal using notation 'i:0xhex_number' (e.g.,: 'i:0xabcd'); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_op</function> usage ... avp_op("$avp(678)", "add/345/g"); avp_op("$avp(number)","sub/$avp(number2)/d"); ...
<function moreinfo="none">is_avp_set(name) </function> Check if any AVP with name is set. Meaning of the parameters is as follows: name - name of AVP to look for. Parameter syntax is: name = avp_name|avp_alias [ '/' flags ]) flags = ('e'|'s'|'n') - e = empty value; s = value string; n = value number (int) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>is_avp_set</function> usage ... if(is_avp_set("$avp(678)")) log("AVP with integer id 678 exists\n"); ...
<function moreinfo="none">avp_print() </function> Prints the list with all the AVPs from memory. This is only a helper/debug function. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>avp_print</function> usage ... avp_print(); ...
<function moreinfo="none">avp_insert(avp_name, value, index) </function> This function inserts an avp value at a certain position specified by the last parameter. If the index is greater than the count of values the value will be inserted at the end. <function>avp_print</function> usage ... avp_insert("avp(20)", "$hdr(From)", 2); ...
Exported Asynchronous Functions
<function moreinfo="none">avp_db_query(query[[,dest],db_id])</function> Make a database query and store the result in AVPs. The meaning and usage of the parameters: query - must be a valid SQL query. The parameter can contain pseudo-variables. You must escape any pseudo-variables manually to prevent SQL injection attacks. You can use the existing transformations escape.common and unescape.common to escape and unescape the content of any pseudo-variable. Failing to escape the variables used in the query makes you vulnerable to SQL injection, e.g. make it possible for an outside attacker to alter your database content. The function returns true if the query was successful, -2 in case the query returned an empty result set, and -1 for all other types of errors dest - a list with AVP names where to store the result. The format is $avp(name1);$avp(name2);.... If this parameter is ommited, the result is stored in $avp(1);$avp(2);.... If the result gives many rows, then multiple AVPs with corresponding name will be added. The value type of the AVP (string or integer) will be derived from the type of the columns. db_id - reference to a defined DB URL (a numerical id) - see the db_url module parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE. <function>async avp_db_query</function> usage ... { ... /* Example of a slow MySQL query - it should take around 5 seconds */ async( avp_db_query( "SELECT table_name, table_version, SLEEP(0.1) from version", "$avp(tb_name); $avp(tb_ver); $avp(retcode)"), my_resume_route); /* script execution is halted right after the async() call */ } /* We will be called when data is ready - meanwhile, the worker is free */ route [my_resume_route] { xlog("Results: \n$(avp(tb_name)[*])\n -------------------\n$(avp(tb_ver)[*])\n -------------------\n$(avp(retcode)[*])\n"); } ...
opensips-2.2.2/modules/b2b_entities/000077500000000000000000000000001300170765700174045ustar00rootroot00000000000000opensips-2.2.2/modules/b2b_entities/Makefile000066400000000000000000000003521300170765700210440ustar00rootroot00000000000000# $Id: Makefile 806 2006-04-14 11:00:10Z bogdan_iancu $ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=b2b_entities.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/b2b_entities/README000066400000000000000000000307131300170765700202700ustar00rootroot00000000000000B2B_ENTITIES Anca-Maria Vamanu OpenSIPS Edited by Anca-Maria Vamanu Edited by Ovidiu Sas Copyright © 2009 Anca-Maria Vamanu Revision History Revision $Revision: 8103 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. server_hsize (int) 1.3.2. client_hsize (int) 1.3.3. script_req_route (str) 1.3.4. script_reply_route (str) 1.3.5. db_url (str) 1.3.6. update_period (int) 1.3.7. b2b_key_prefix (string) 1.3.8. db_mode (int) 1.3.9. db_table (str) 1.3.10. replication_mode (int) 1.4. Exported Functions 2. Developer Guide 2.1. b2b_load_api(b2b_api_t* api) 2.2. server_new 2.3. client_new 2.4. send_request 2.5. send_reply 2.6. entity_delete 2.7. restore_logic_info 2.8. update_b2bl_param List of Examples 1.1. Set server_hsize parameter 1.2. Set client_hsize parameter 1.3. Set script_req_route parameter 1.4. Set script_repl_route parameter 1.5. Set db_url parameter 1.6. Set update_period parameter 1.7. Set b2b_key_prefix parameter 1.8. Set db_mode parameter 1.9. Set db_table parameter 1.10. Set replication_mode parameter 2.1. b2b_api_t structure Chapter 1. Admin Guide 1.1. Overview The B2BUA implementation in OpenSIPS is separated in two layers: * a lower one(coded in this module)- which implements the basic functions of a UAS and UAC * a upper one - which represents the logic engine of B2BUA, responsible of actually implementing the B2BUA services using the functions offered by the low level. This module stores records corresponding to the dialogs in which the B2BUA is involved. It exports an API to be called from other modules which offers functions for creating a new dialog record, for sending requests or replies in one dialog and will also notify the upper level module when a request or reply is received inside one stored dialog. The records are separated in two types: b2b server entities and b2b client entities depending on the mode they are created. An entity created for a received initial message will be a server entity, while a entity that will send an initial request(create a new dialog) will be a b2b client entity. The name corresponds to the behavior in the first transaction - if UAS - server entity and if UAC - client entity. This module does not implement a B2BUA alone, but needs a B2B logic implementing module. The module is able to respond to authentication challanges if the uac_auth module is loaded first. The list of credentials for b2b authentication is also provided by the uac_auth module. 1.2. Dependencies 1.2.1. OpenSIPS Modules * tm * a db module * uac_auth (mandatory if authentication is required) 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.3. Exported Parameters 1.3.1. server_hsize (int) The size of the hash table that stores the b2b server entities. It is the 2 logarithmic value of the real size. Default value is “9†(512 records). Example 1.1. Set server_hsize parameter ... modparam("b2b_entities", "server_hsize", 10) ... 1.3.2. client_hsize (int) The size of the hash table that stores the b2b client entities. It is the 2 logarithmic value of the real size. Default value is “9†(512 records). Example 1.2. Set client_hsize parameter ... modparam("b2b_entities", "client_hsize", 10) ... 1.3.3. script_req_route (str) The name of the b2b script route that will be called when B2B requests are received. Example 1.3. Set script_req_route parameter ... modparam("b2b_entities", "script_req_route", "b2b_request") ... 1.3.4. script_reply_route (str) The name of the b2b script route that will be called when B2B replies are received. Example 1.4. Set script_repl_route parameter ... modparam("b2b_entities", "script_reply_route", "b2b_reply") ... 1.3.5. db_url (str) Database URL. It is not compulsory, if not set data is not stored in database. Example 1.5. Set db_url parameter ... modparam("b2b_entities", "db_url", "mysql://opensips:opensipsrw@127.0.0. 1/opensips") ... 1.3.6. update_period (int) The time interval at which to update the info in database. Default value is “100â€. Example 1.6. Set update_period parameter ... modparam("b2b_entities", "update_period", 60) ... 1.3.7. b2b_key_prefix (string) The string to use when generating the key ( it is inserted in the SIP messages as callid or to tag. It is useful to set this prefix if you use more instances of opensips B2BUA cascaded in the same architecture. Sometimes opensips B2BUA looks at the callid or totag to see if it has the format it uses to determine if the request was sent by it. Default value is “B2Bâ€. Example 1.7. Set b2b_key_prefix parameter ... modparam("b2b_entities", "b2b_key_prefix", "B2B1") ... 1.3.8. db_mode (int) The B2B modules have support for the 3 type of database storage * NO DB STORAGE - set this parameter to 0 * WRITE THROUGH (synchronous write in database) - set this parameter to 1 * WRITE BACK (update in db from time to time) - set this parameter to 2 Default value is “2†(WRITE BACK). Example 1.8. Set db_mode parameter ... modparam("b2b_entities", "db_mode", 1) ... 1.3.9. db_table (str) The name of the table that will be used for storing B2B entities Default value is “b2b_entities†Example 1.9. Set db_table parameter ... modparam("b2b_entities", "db_table", "some table name") ... 1.3.10. replication_mode (int) Controls if the callid should be the same in more instances running at the same time. 0 will lead to generating of different callid's per instance 1 will lead to generating of the same callid's in more instances Default value is “0†Example 1.10. Set replication_mode parameter ... modparam("b2b_entities", "replication_mode", 1) ... 1.4. Exported Functions The module does not export functions to be used in configuration script. Chapter 2. Developer Guide The module provides an API that can be used from other OpenSIPS modules. The API offers the functions for creating and handing dialogs. A dialog can be created on a receipt initial message, and this will correspond to a b2b server entity, or initiated by the server and in this case a client entity will be created in b2b_entities module. 2.1. b2b_load_api(b2b_api_t* api) This function binds the b2b_entities modules and fills the structure the exported functions that will be described in detail. Example 2.1. b2b_api_t structure ... typedef struct b2b_api { b2b_server_new_t server_new; b2b_client_new_t client_new; b2b_send_request_t send_request; b2b_send_reply_t send_reply; b2b_entity_delete_t entity_delete; b2b_restore_linfo_t restore_logic_info; b2b_update_b2bl_param_t update_b2bl_param; }b2b_api_t; ... 2.2. server_new Field type: ... typedef str* (*b2b_server_new_t) (struct sip_msg* ,b2b_notify_t , void* param); ... This function asks the b2b_entities modules to create a new server entity record. The internal processing actually extracts the dialog information from the message and constructs a record that will be stored in a hash table. The second parameters is a pointer to a function that the b2b_entities module will call when a event will come for that dialog (a request or reply). The third parameter is a pointer to a value that will be stored and given as a parameter when the notify function will be called(it has to be allocated in shared memory). The return value is an identifier for the record that will be mentioned when calling other functions that represent actions in the dialog(send request, send reply). The notify function has the following prototype: ... typedef int (*b2b_notify_t)(struct sip_msg* msg, str* id, int type, void * param); ... This function is called when a request or reply is received for a dialog handled by b2b_entities. The first parameter is the message, the second is the identifier for the dialog, the third is a flag that says which is the type of the message(it has two possible values - B2B_REQUEST and B2B_REPLY). The last parameter is the parameter by the upper module when the entity was created. 2.3. client_new Field type: ... typedef str* (*b2b_client_new_t) (client_info_t* , b2b_notify_t b2b_cbac k, b2b_add_dlginfo_t add_dlginfo_f, str* param); ... This function asks the b2b_entities modules to create a new client entity record and also create a new dialog by sending an initial message. The parameters are all the values needed for the initial request to which the notify function and parameter are added. The b2b_cback parameter is a pointer to the callback that must be called when an event happens(receiving a reply or request) in the dialog created with this function. The add_dlginfo_f parameter is also a function pointer to a callback that will be called when a final success response will be received for the created dialog. The callback will receive as parameter the complete dialog information for the record. It should be stored and used when calling send_request or send_reply functions. The return value is an identifier for the record that will be mentioned when calling other functions that represent actions in the dialog(send request, send reply). 2.4. send_request Field type: ... typedef int (*b2b_send_request_t)(enum b2b_entity_type ,str* b2b_key, st r* method, str* extra_headers, str* body, b2b_dlginfo_t*); ... This function asks the b2b_entities modules to send a request inside a b2b dialog identified by b2b_key. The first parameter is the entity type and can have two values: B2B_SERVER and B2B_CLIENT. The second is the identifier returned by the create function(server_new or client_new) and the next are the informations needed for the new request: method, extra_headers, body. The last parameter contains the dialog information - callid, to tag, from tag. These are needed to make a perfect match to of b2b_entities record for which a new request must be sent. The return value is 0 for success and a negative value for error. 2.5. send_reply Field type: ... typedef int (*b2b_send_reply_t)(enum b2b_entity_type et, str* b2b_key, i nt code, str* text, str* body, str* extra_headers, b2b_dlginfo_t* dlginfo); ... This function asks the b2b_entities modules to send a reply inside a b2b dialog identified by b2b_key. The first parameter is the entity type and can have two values: B2B_SERVER and B2B_CLIENT. The second is the identifier returned by the create function(server_new or client_new) and the next are the informations needed for the new reply: code, text, body, extra_headers. The last parameter contains the dialog information used for matching the right record. The return value is 0 for success and a negative value for error. 2.6. entity_delete Field type: ... typedef void (*b2b_entity_delete_t)(enum b2b_entity_type et, str* b2b_ke y, b2b_dlginfo_t* dlginfo); ... This function must be called by the upper level function to delete the records in b2b_entities. The records are not cleaned up by the b2b_entities module and the upper level module must take care to delete them. 2.7. restore_logic_info Field type: ... typedef int (*b2b_restore_linfo_t)(enum b2b_entity_type type, str* key, b2b_notify_t cback); ... This function is used at startup when loading the data from the database to restore the pointer to the callback function. 2.8. update_b2bl_param Field type: ... typedef int (*b2b_update_b2bl_param_t)(enum b2b_entity_type type, str* k ey, str* param); ... This function can be used to change the logic param stored for an entity ( useful in case an entity is moved between logic records). opensips-2.2.2/modules/b2b_entities/b2b_common.h000066400000000000000000000017641300170765700216020ustar00rootroot00000000000000/* * back-to-back entities modules * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2011-02-09 initial version (Ovidiu Sas) */ #ifndef _B2B_COMMON_H_ #define _B2B_COMMON_H_ #define B2BL_MAX_KEY_LEN 21 #endif opensips-2.2.2/modules/b2b_entities/b2b_entities.c000066400000000000000000000453631300170765700221340ustar00rootroot00000000000000/* * back-to-back entities module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) * 2011-01-04 new mi function: mi_b2be_list (Ovidiu Sas) * 2011-06-27 added authentication support (Ovidiu Sas) */ #include #include #include #include "../../db/db.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../script_cb.h" #include "../../parser/parse_from.h" #include "../dialog/dlg_load.h" #include "../uac_auth/uac_auth.h" #include "b2be_db.h" #include "b2b_entities.h" #include "server.h" #include "dlg.h" #define TABLE_VERSION 1 /** Functions declarations */ static int mod_init(void); static void mod_destroy(void); static int child_init(int rank); int b2b_entities_bind(b2b_api_t* api); static struct mi_root* mi_b2be_list(struct mi_root* cmd, void* param); /** Global variables */ unsigned int server_hsize = 9; unsigned int client_hsize = 9; static char* script_req_route = NULL; static char* script_reply_route = NULL; int req_routeid = -1; int reply_routeid = -1; int replication_mode= 0; static str db_url= {0, 0}; db_con_t *b2be_db = NULL; db_func_t b2be_dbf; str b2be_dbtable= str_init("b2b_entities"); static int b2b_update_period = 100; int uac_auth_loaded; str b2b_key_prefix = str_init("B2B"); int b2be_db_mode = WRITE_BACK; #define DB_COLS_NO 26 /* TM bind */ struct tm_binds tmb; /* UAC_AUTH bind */ uac_auth_api_t uac_auth_api; /** Exported functions */ static cmd_export_t cmds[]= { {"load_b2b", (cmd_function)b2b_entities_bind, 1, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; /** Exported parameters */ static param_export_t params[]={ { "server_hsize", INT_PARAM, &server_hsize }, { "client_hsize", INT_PARAM, &client_hsize }, { "script_req_route", STR_PARAM, &script_req_route }, { "script_reply_route", STR_PARAM, &script_reply_route }, { "replication_mode", INT_PARAM, &replication_mode }, { "db_url", STR_PARAM, &db_url.s }, { "db_table", STR_PARAM, &b2be_dbtable.s }, { "db_mode", INT_PARAM, &b2be_db_mode }, { "update_period", INT_PARAM, &b2b_update_period }, { "b2b_key_prefix", STR_PARAM, &b2b_key_prefix.s }, { 0, 0, 0 } }; /** MI commands */ static mi_export_t mi_cmds[] = { { "b2be_list", 0, mi_b2be_list, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_DEFAULT, "uac_auth", DEP_WARN }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "db_url", get_deps_sqldb_url }, { NULL, NULL }, }, }; /** Module interface */ struct module_exports exports= { "b2b_entities", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ NULL, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) mod_destroy, /* destroy function */ child_init /* per-child init function */ }; void b2be_db_timer_update(unsigned int ticks, void* param) { b2b_entities_dump(0); } /** Module initialize function */ static int mod_init(void) { /* inspect the parameters */ if(server_hsize< 1 || server_hsize> 20 || client_hsize< 1 || client_hsize> 20) { LM_ERR("Wrong hash size. Needs to be greater than 1" " and smaller than 20. Be aware that you should set the log 2" " value of the real size\n"); return -1; } server_hsize = 1< B2B_MAX_PREFIX_LEN) { LM_ERR("b2b_key_prefix [%s] too long. Maximum size %d\n", b2b_key_prefix.s, B2B_MAX_PREFIX_LEN); return -1; } } /* load all TM stuff */ if(load_tm_api(&tmb)==-1) { LM_ERR("can't load tm functions\n"); return -1; } /* load the UAC_AUTH API - FIXME it should be loaded only * if authentication is required */ if(load_uac_auth_api(&uac_auth_api)<0) { LM_INFO("authentication functionality disabled:" " load uac_auth first to enable it\n"); uac_auth_loaded = 0; } else { uac_auth_loaded = 1; } /* initialize the hash tables; they will be allocated in shared memory * to be accesible by all processes */ if(init_b2b_htables()< 0) { LM_ERR("Failed to initialize b2b table\n"); return -1; } memset(&b2be_dbf, 0, sizeof(db_func_t)); if(b2be_db_mode && db_url.s) { db_url.len = strlen(db_url.s); b2be_dbtable.len = strlen(b2be_dbtable.s); /* binding to database module */ if (db_bind_mod(&db_url, &b2be_dbf)) { LM_ERR("Database module not found\n"); return -1; } if (!DB_CAPABILITY(b2be_dbf, DB_CAP_ALL)) { LM_ERR("Database module does not implement all functions" " needed by b2b_entities module\n"); return -1; } b2be_db = b2be_dbf.init(&db_url); if(!b2be_db) { LM_ERR("connecting to database failed\n"); return -1; } /*verify table versions */ if(db_check_table_version(&b2be_dbf, b2be_db, &b2be_dbtable, TABLE_VERSION) < 0) { LM_ERR("error during table version check\n"); return -1; } b2be_initialize(); /* reload data */ if(b2b_entities_restore() < 0) { LM_ERR("Failed to restore data from database\n"); return -1; } if(b2be_db) b2be_dbf.close(b2be_db); b2be_db = NULL; } else b2be_db_mode = 0; if(register_script_cb( b2b_prescript_f, PRE_SCRIPT_CB|REQ_TYPE_CB, 0 ) < 0) { LM_ERR("Failed to register prescript function\n"); return -1; } if (script_req_route) { req_routeid = get_script_route_ID_by_name( script_req_route, rlist, RT_NO); if (req_routeid < 1) { LM_ERR("route <%s> does not exist\n",script_req_route); return -1; } } if (script_reply_route) { reply_routeid = get_script_route_ID_by_name( script_reply_route, rlist, RT_NO); if (reply_routeid < 1) { LM_ERR("route <%s> does not exist\n",script_reply_route); return -1; } } if(b2b_update_period < 0) { LM_ERR("Wrong parameter - b2b_update_period [%d]\n", b2b_update_period); return -1; } if(b2be_db_mode == WRITE_BACK) register_timer("b2be-dbupdate", b2be_db_timer_update, 0, b2b_update_period, TIMER_FLAG_SKIP_ON_DELAY); //register_timer("b2b2-clean", b2be_clean, 0, b2b_update_period); return 0; } void check_htable(b2b_table table, int hsize) { int i; b2b_dlg_t* dlg, *dlg_next; for(i= 0; i< hsize; i++) { lock_get(&table[i].lock); dlg = table[i].first; while(dlg) { dlg_next = dlg->next; if(dlg->b2b_cback == 0) { LM_ERR("Found entity not linked to any logic\n"); b2b_delete_record(dlg, table, i); } dlg = dlg_next; } lock_release(&table[i].lock); } table->checked = 1; } void check_htables(void) { if(server_htable->checked && client_htable->checked) return; if(!server_htable->checked) check_htable(server_htable, server_hsize); if(!client_htable->checked) check_htable(client_htable, client_hsize); } /** Module child initialize function */ static int child_init(int rank) { /* if database is needed */ if (b2be_db_mode && db_url.s) { if (b2be_dbf.init==0) { LM_CRIT("child_init: database not bound\n"); return -1; } b2be_db = b2be_dbf.init(&db_url); if(!b2be_db) { LM_ERR("connecting to database failed\n"); return -1; } LM_DBG("child %d: Database connection opened successfully\n", rank); } check_htables(); return 0; } /** Module destroy function */ static void mod_destroy(void) { if(b2be_db ) { if(b2be_db_mode==WRITE_BACK) b2b_entities_dump(1); b2be_dbf.close(b2be_db); } destroy_b2b_htables(); } int b2b_restore_logic_info(enum b2b_entity_type type, str* key, b2b_notify_t cback) { b2b_dlg_t* dlg; b2b_table table; unsigned int hash_index, local_index; if(server_htable== NULL) { LM_ERR("You have to load b2b_entities module before b2b_logic module\n"); return -1; } if(type == B2B_SERVER) { table = server_htable; } else { table = client_htable; } if(b2b_parse_key(key, &hash_index, &local_index) < 0) { LM_ERR("Wrong format for b2b key [%.*s]\n", key->len, key->s); return -1; } dlg = b2b_search_htable(table, hash_index, local_index); if(dlg == NULL) { LM_ERR("No dialog found for key [%.*s]\n", key->len, key->s); return -1; } dlg->b2b_cback = cback; return 0; } int b2b_update_b2bl_param(enum b2b_entity_type type, str* key, str* param) { b2b_dlg_t* dlg; b2b_table table; unsigned int hash_index, local_index; if(!param) { LM_ERR("NULL param\n"); return -1; } if(param->len > B2BL_MAX_KEY_LEN) { LM_ERR("parameter too long, received [%d], maximum [%d]\n", param->len, B2BL_MAX_KEY_LEN); return -1; } if(type == B2B_SERVER) { table = server_htable; } else { table = client_htable; } if(b2b_parse_key(key, &hash_index, &local_index) < 0) { LM_ERR("Wrong format for b2b key [%.*s]\n", key->len, key->s); return -1; } lock_get(&table[hash_index].lock); dlg = b2b_search_htable(table, hash_index, local_index); if(dlg == NULL) { LM_ERR("No dialog found\n"); lock_release(&table[hash_index].lock); return -1; } memcpy(dlg->param.s, param->s, param->len); dlg->param.len = param->len; lock_release(&table[hash_index].lock); return 0; } int b2b_get_b2bl_key(str* callid, str* from_tag, str* to_tag, str* entity_key, str* tuple_key) { b2b_dlg_t* dlg; unsigned int hash_index, local_index; b2b_table table; int ret; if(!callid || !callid->s || !callid->len){ LM_ERR("Wrong callid param\n"); return -1; } if(!from_tag || !from_tag->s || !from_tag->len){ LM_ERR("Wrong from_tag param\n"); return -1; } if(!to_tag || !to_tag->s || !to_tag->len){ LM_ERR("Wrong to_tag param\n"); return -1; } if(!tuple_key || !tuple_key->s || tuple_key->len meaning that it is a server request */ if(b2b_parse_key(to_tag, &hash_index, &local_index)>=0) { table = server_htable; lock_get(&table[hash_index].lock); dlg=b2b_search_htable_dlg(table, hash_index, local_index, to_tag, from_tag, callid); if(dlg){ memcpy(tuple_key->s, dlg->param.s, dlg->param.len); tuple_key->len = dlg->param.len; entity_key->s = to_tag->s; entity_key->len = to_tag->len; LM_DBG("got tuple [%.*s] for entity [%.*s]\n", tuple_key->len, tuple_key->s, entity_key->len, entity_key->s); ret = 0; } else { ret = -1; } lock_release(&table[hash_index].lock); return ret; } return -1; } int b2b_entities_bind(b2b_api_t* api) { if (!api) { LM_ERR("Invalid parameter value\n"); return -1; } api->server_new = server_new; api->client_new = client_new; api->send_request = b2b_send_request; api->send_reply = b2b_send_reply; api->entity_delete = b2b_entity_delete; api->restore_logic_info = b2b_restore_logic_info; api->update_b2bl_param = b2b_update_b2bl_param; api->entities_db_delete = b2b_db_delete; api->get_b2bl_key = b2b_get_b2bl_key; api->apply_lumps = b2b_apply_lumps; return 0; } static inline int mi_print_b2be_dlg(struct mi_node *rpl, b2b_table htable, unsigned int hsize) { int i, len; char* p; b2b_dlg_t* dlg; dlg_leg_t* leg; struct mi_node *node=NULL, *node1=NULL, *node_l=NULL; struct mi_attr* attr; for(i = 0; i< hsize; i++) { lock_get(&htable[i].lock); dlg = htable[i].first; while(dlg) { p = int2str((unsigned long)(dlg->id), &len); node = add_mi_node_child(rpl, MI_DUP_VALUE, "dlg", 3, p, len); if(node == NULL) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "param", 5, dlg->param.s, dlg->param.len); if(attr == NULL) goto error; p = int2str((unsigned long)(dlg->state), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "state", 5, p, len); if(attr == NULL) goto error; p = int2str((unsigned long)(dlg->last_invite_cseq), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "last_invite_cseq", 16, p, len); if(attr == NULL) goto error; p = int2str((unsigned long)(dlg->last_method), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "last_method", 11, p, len); if(attr == NULL) goto error; if (dlg->last_reply_code) { p = int2str((unsigned long)(dlg->last_reply_code), &len); attr = add_mi_attr(node,MI_DUP_VALUE,"last_reply_code",15,p,len); if(attr == NULL) goto error; } p = int2str((unsigned long)(dlg->db_flag), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "db_flag", 7, p, len); if(attr == NULL) goto error; if (dlg->ruri.len) { node1 = add_mi_node_child(node, MI_DUP_VALUE, "ruri", 4, dlg->ruri.s, dlg->ruri.len); if(node1 == NULL) goto error; } node1 = add_mi_node_child(node, MI_DUP_VALUE, "callid", 6, dlg->callid.s, dlg->callid.len); if(node1 == NULL) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "from", 4, dlg->from_dname.s, dlg->from_dname.len); if(node1 == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "uri", 3, dlg->from_uri.s, dlg->from_uri.len); if(attr == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "tag", 3, dlg->tag[0].s, dlg->tag[0].len); if(attr == NULL) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "to", 2, dlg->to_dname.s, dlg->to_dname.len); if(node1 == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "uri", 3, dlg->to_uri.s, dlg->to_uri.len); if(attr == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "tag", 3, dlg->tag[1].s, dlg->tag[1].len); if(attr == NULL) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "cseq", 4, NULL, 0); if(node1 == NULL) goto error; p = int2str((unsigned long)(dlg->cseq[0]), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "caller", 6, p, len); if(attr == NULL) goto error; p = int2str((unsigned long)(dlg->cseq[1]), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "callee", 6, p, len); if(attr == NULL) goto error; if (dlg->route_set[0].len||dlg->route_set[1].len) { node1 = add_mi_node_child(node,MI_DUP_VALUE,"route_set",9,NULL,0); if(node1 == NULL) goto error; if (dlg->route_set[0].len) { attr = add_mi_attr(node1, MI_DUP_VALUE, "caller", 6, dlg->route_set[0].s, dlg->route_set[0].len); if(attr == NULL) goto error; } if (dlg->route_set[1].len) { attr = add_mi_attr(node1, MI_DUP_VALUE, "callee", 6, dlg->route_set[1].s, dlg->route_set[1].len); if(attr == NULL) goto error; } } node1 = add_mi_node_child(node, MI_DUP_VALUE, "contact", 7, NULL, 0); if(node1 == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "caller", 6, dlg->contact[0].s, dlg->contact[0].len); if(attr == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "callee", 6, dlg->contact[1].s, dlg->contact[1].len); if(attr == NULL) goto error; if (dlg->send_sock) { node1 = add_mi_node_child(node, MI_DUP_VALUE, "send_sock", 9, dlg->send_sock->name.s, dlg->send_sock->name.len); if(node1 == NULL) goto error; } if(dlg->uac_tran||dlg->uas_tran||dlg->update_tran||dlg->cancel_tm_tran) { node1 = add_mi_node_child(node, MI_DUP_VALUE, "tm_tran", 7, NULL, 0); if(node1 == NULL) goto error; if(dlg->uac_tran) attr = add_mi_attr(node1,MI_DUP_VALUE,"uac",3,NULL,0); if(attr == NULL) goto error; if(dlg->uas_tran) attr = add_mi_attr(node1,MI_DUP_VALUE,"uas",3,NULL,0); if(attr == NULL) goto error; if(dlg->update_tran) attr = add_mi_attr(node1,MI_DUP_VALUE,"update",6,NULL,0); if(attr == NULL) goto error; if(dlg->cancel_tm_tran) attr = add_mi_attr(node1,MI_DUP_VALUE,"cancel_tm",9,NULL,0); if(attr == NULL) goto error; } if ( (leg=dlg->legs)!=NULL ) { node_l = add_mi_node_child(node, MI_IS_ARRAY, "LEGS", 4, NULL, 0); if(node_l == NULL) goto error; while(leg) { p = int2str((unsigned long)(leg->id), &len); node1 = add_mi_node_child(node_l, MI_DUP_VALUE, "leg", 3, p, len); if(node1 == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "tag", 3, leg->tag.s, leg->tag.len); if(attr == NULL) goto error; p = int2str((unsigned long)(leg->cseq), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "cseq", 4, p, len); if(attr == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "contact", 7, leg->contact.s, leg->contact.len); if(attr == NULL) goto error; if(leg->route_set.len) { attr = add_mi_attr(node1, MI_DUP_VALUE, "route_set", 9, leg->route_set.s, leg->route_set.len); if(attr == NULL) goto error; } leg=leg->next; } } dlg = dlg->next; } lock_release(&htable[i].lock); } return 0; error: lock_release(&htable[i].lock); LM_ERR("failed to add node\n"); return -1; } static struct mi_root* mi_b2be_list(struct mi_root* cmd, void* param) { struct mi_root *rpl_tree; struct mi_node *rpl=NULL; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; if (server_htable) if (mi_print_b2be_dlg(rpl, server_htable, server_hsize)!=0) goto error; if (client_htable) if (mi_print_b2be_dlg(rpl, client_htable, client_hsize)!=0) goto error; return rpl_tree; error: LM_ERR("Unable to create reply\n"); free_mi_tree(rpl_tree); return NULL; } opensips-2.2.2/modules/b2b_entities/b2b_entities.h000066400000000000000000000056311300170765700221330ustar00rootroot00000000000000/* * back-to-back entities modules * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) * 2011-06-27 added authentication support (Ovidiu Sas) */ #ifndef _B2B_H_ #define _B2B_H_ #include "../../str.h" #include "../../parser/msg_parser.h" #include "../uac_auth/uac_auth.h" #include "../tm/tm_load.h" #include "../signaling/signaling.h" #include "dlg.h" #include "client.h" #include "server.h" #include "../../db/db.h" /* modes to write in db */ #define NO_DB 0 #define WRITE_THROUGH 1 #define WRITE_BACK 2 typedef int (*b2b_restore_linfo_t)(enum b2b_entity_type type, str* key, b2b_notify_t cback); typedef int (*b2b_update_b2bl_param_t)(enum b2b_entity_type type, str* key, str* param); typedef void (*b2b_db_delete_t)(str param); typedef int (*b2b_get_b2bl_key_t)(str* callid, str* from_tag, str* to_tag, str* entity_key, str* tuple_key); extern int uac_auth_loaded; extern str b2b_key_prefix; #define B2B_MAX_PREFIX_LEN 5 typedef struct b2b_api { b2b_server_new_t server_new; b2b_client_new_t client_new; b2b_send_request_t send_request; b2b_send_reply_t send_reply; b2b_entity_delete_t entity_delete; b2b_db_delete_t entities_db_delete; b2b_restore_linfo_t restore_logic_info; b2b_update_b2bl_param_t update_b2bl_param; b2b_get_b2bl_key_t get_b2bl_key; b2b_apply_lumps_t apply_lumps; }b2b_api_t; extern unsigned int server_hsize; extern unsigned int client_hsize; extern struct tm_binds tmb; extern uac_auth_api_t uac_auth_api; extern int req_routeid; extern int reply_routeid; extern int replication_mode; extern db_con_t *b2be_db; extern db_func_t b2be_dbf; extern str b2be_dbtable; extern int b2be_db_mode; typedef int(*load_b2b_f) (b2b_api_t* api); static inline int load_b2b_api( struct b2b_api *b2b_api) { load_b2b_f load_b2b; /* import the b2b_entities auto-loading function */ if ( !(load_b2b=(load_b2b_f)find_export("load_b2b", 1, 0))) { LM_ERR("can't import load_b2b\n"); return -1; } /* let the auto-loading function load all B2B entities stuff */ return load_b2b( b2b_api ); } #endif opensips-2.2.2/modules/b2b_entities/b2be_db.c000066400000000000000000000430661300170765700210400ustar00rootroot00000000000000/* * back-to-back entities module * * Copyright (C) 2011 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2011-04-04 initial version (Anca Vamanu) */ #include #include #include #include "../../db/db.h" #include "../../dprint.h" #include "../presence/hash.h" #include "b2b_entities.h" #include "b2be_db.h" #define DB_COLS_NO 25 static str str_type_col = str_init("type"); static str str_state_col = str_init("state"); static str str_ruri_col = str_init("ruri"); static str str_from_col = str_init("from_uri"); static str str_from_dname_col = str_init("from_dname"); static str str_to_col = str_init("to_uri"); static str str_to_dname_col = str_init("to_dname"); static str str_tag0_col = str_init("tag0"); static str str_tag1_col = str_init("tag1"); static str str_callid_col = str_init("callid"); static str str_cseq0_col = str_init("cseq0"); static str str_cseq1_col = str_init("cseq1"); static str str_route0_col = str_init("route0"); static str str_route1_col = str_init("route1"); static str str_contact0_col = str_init("contact0"); static str str_contact1_col = str_init("contact1"); static str str_lm_col = str_init("lm"); static str str_lrc_col = str_init("lrc"); static str str_lic_col = str_init("lic"); static str str_leg_tag_col = str_init("leg_tag"); static str str_leg_cseq_col = str_init("leg_cseq"); static str str_leg_route_col = str_init("leg_route"); static str str_leg_contact_col = str_init("leg_contact"); static str str_sockinfo_srv_col = str_init("sockinfo_srv"); static str str_param_col = str_init("param"); static db_key_t qcols[DB_COLS_NO]; static db_val_t qvals[DB_COLS_NO]; static int n_query_update, n_start_update; void b2be_initialize(void) { memset(qvals, 0, DB_COLS_NO*sizeof(db_val_t)); qcols[0] = &str_type_col; qvals[0].type = DB_INT; qcols[1] = &str_tag0_col; qvals[1].type = DB_STR; qcols[2] = &str_tag1_col; qvals[2].type = DB_STR; qcols[3] = &str_callid_col; qvals[3].type = DB_STR; n_query_update= 4; qcols[4] = &str_ruri_col; qvals[4].type= DB_STR; qcols[5] = &str_from_col; qvals[5].type= DB_STR; qcols[6] = &str_from_dname_col; qvals[6].type= DB_STR; qcols[7] = &str_to_col; qvals[7].type= DB_STR; qcols[8] = &str_to_dname_col; qvals[8].type= DB_STR; qcols[9] = &str_route0_col; qvals[9].type= DB_STR; qcols[10] = &str_route1_col; qvals[10].type= DB_STR; qcols[11] = &str_sockinfo_srv_col; qvals[11].type= DB_STR; qcols[12] = &str_param_col; qvals[12].type= DB_STR; n_start_update= 13; qcols[13] = &str_state_col; qvals[13].type= DB_INT; qcols[14] = &str_cseq0_col; qvals[14].type= DB_INT; qcols[15] = &str_cseq1_col; qvals[15].type= DB_INT; qcols[16] = &str_lm_col; qvals[16].type= DB_INT; qcols[17] = &str_lrc_col; qvals[17].type= DB_INT; qcols[18] = &str_lic_col; qvals[18].type= DB_INT; qcols[19] = &str_contact0_col; qvals[19].type= DB_STR; qcols[20] = &str_contact1_col; qvals[20].type= DB_STR; qcols[21] = &str_leg_tag_col; qvals[21].type= DB_STR; qcols[22] = &str_leg_cseq_col; qvals[22].type= DB_INT; qcols[23] = &str_leg_contact_col; qvals[23].type= DB_STR; qcols[24] = &str_leg_route_col; qvals[24].type= DB_STR; } int b2be_db_insert(b2b_dlg_t* dlg, int type) { dlg_leg_t* leg; int cols_no; if(b2be_dbf.use_table(b2be_db, &b2be_dbtable)< 0) { LM_ERR("sql use table failed\n"); return -1; } qvals[0].val.int_val = type; qvals[1].val.str_val = dlg->tag[0]; qvals[2].val.str_val = dlg->tag[1]; qvals[3].val.str_val = dlg->callid; qvals[4].val.str_val = dlg->ruri; qvals[5].val.str_val = dlg->from_uri; qvals[6].val.str_val = dlg->from_dname; qvals[7].val.str_val = dlg->to_uri; qvals[8].val.str_val = dlg->to_dname; qvals[9].val.str_val = dlg->route_set[0]; qvals[10].val.str_val= dlg->route_set[1]; if(dlg->send_sock) qvals[11].val.str_val= dlg->send_sock->sock_str; else { qvals[11].val.str_val.s = 0; qvals[11].val.str_val.len = 0; } qvals[12].val.str_val= dlg->param; qvals[13].val.int_val = dlg->state; qvals[14].val.int_val = dlg->cseq[0]; qvals[15].val.int_val = dlg->cseq[1]; qvals[16].val.int_val = dlg->last_method; qvals[17].val.int_val = dlg->last_reply_code; qvals[18].val.int_val = dlg->last_invite_cseq; qvals[19].val.str_val = dlg->contact[0]; qvals[20].val.str_val = dlg->contact[1]; cols_no = 21; leg = dlg->legs; if(leg) /* there can only be one leg as we do not deal with dialogs in early state */ { qvals[21].val.str_val= leg->tag; qvals[22].val.int_val= leg->cseq; qvals[23].val.str_val= leg->contact; qvals[24].val.str_val= leg->route_set; cols_no = 25; } /* insert into database */ if(b2be_dbf.insert(b2be_db, qcols, qvals, cols_no)< 0) { LM_ERR("Sql insert failed\n"); return -1; } LM_DBG("INSERTED [%.*s], [%.*s]\n", dlg->tag[0].len, dlg->tag[0].s, dlg->callid.len, dlg->callid.s); return 0; } int b2be_db_update(b2b_dlg_t* dlg, int type) { dlg_leg_t* leg; int cols_no; qvals[0].val.int_val = type; if(b2be_dbf.use_table(b2be_db, &b2be_dbtable)< 0) { LM_ERR("sql use table failed\n"); return -1; } qvals[1].val.str_val = dlg->tag[0]; qvals[2].val.str_val = dlg->tag[1]; qvals[3].val.str_val = dlg->callid; /* if the state is terminated delete the record */ if(dlg->state == B2B_TERMINATED) { if(b2be_dbf.delete(b2be_db, qcols, 0, qvals, n_query_update)< 0) { LM_ERR("Sql delete failed\n"); return -1; } return 0; } LM_DBG("State= %d\n", dlg->state); qvals[13].val.int_val = dlg->state; qvals[14].val.int_val = dlg->cseq[0]; qvals[15].val.int_val = dlg->cseq[1]; qvals[16].val.int_val = dlg->last_method; qvals[17].val.int_val = dlg->last_reply_code; qvals[18].val.int_val = dlg->last_invite_cseq; qvals[19].val.str_val = dlg->contact[0]; qvals[20].val.str_val = dlg->contact[1]; cols_no = 21; leg = dlg->legs; if(leg) /* there can only be one leg as we do not deal with dialogs in early state */ { qvals[21].val.str_val= leg->tag; qvals[22].val.int_val= leg->cseq; qvals[23].val.str_val= leg->contact; qvals[24].val.str_val= leg->route_set; cols_no = 25; } if(b2be_dbf.update(b2be_db, qcols, 0, qvals, qcols+n_start_update, qvals+n_start_update, n_query_update, cols_no-n_start_update)< 0) { LM_ERR("Sql update failed\n"); return -1; } LM_DBG("UPDATED [%.*s], [%.*s]\n", dlg->tag[0].len, dlg->tag[0].s, dlg->callid.len, dlg->callid.s); return 0; } void store_b2b_dlg(b2b_table htable, unsigned int hsize, int type, int no_lock) { int i; dlg_leg_t* leg; b2b_dlg_t* dlg; if (!b2be_dbf.init) return; qvals[0].val.int_val = type; //LM_DBG("storing b2b_entities type '%d' in db\n", type); if(b2be_dbf.use_table(b2be_db, &b2be_dbtable)< 0) { LM_ERR("sql use table failed\n"); return; } for(i = 0; i< hsize; i++) { if(!no_lock) lock_get(&htable[i].lock); dlg = htable[i].first; while(dlg) { if(dlg->state < B2B_CONFIRMED || dlg->db_flag == NO_UPDATEDB_FLAG) { dlg = dlg->next; continue; } qvals[1].val.str_val = dlg->tag[0]; qvals[2].val.str_val = dlg->tag[1]; qvals[3].val.str_val = dlg->callid; if(dlg->db_flag == INSERTDB_FLAG ) { qvals[4].val.str_val = dlg->ruri; qvals[5].val.str_val = dlg->from_uri; qvals[6].val.str_val = dlg->from_dname; qvals[7].val.str_val = dlg->to_uri; qvals[8].val.str_val = dlg->to_dname; qvals[9].val.str_val = dlg->route_set[0]; qvals[10].val.str_val= dlg->route_set[1]; if(dlg->send_sock) qvals[11].val.str_val= dlg->send_sock->sock_str; else { qvals[11].val.str_val.s = 0; qvals[11].val.str_val.len = 0; } qvals[12].val.str_val= dlg->param; } qvals[13].val.int_val = dlg->state; qvals[14].val.int_val = dlg->cseq[0]; qvals[15].val.int_val = dlg->cseq[1]; qvals[16].val.int_val = dlg->last_method; qvals[17].val.int_val = dlg->last_reply_code; qvals[18].val.int_val = dlg->last_invite_cseq; qvals[19].val.str_val = dlg->contact[0]; qvals[20].val.str_val = dlg->contact[1]; leg = dlg->legs; if(leg) /* there can only be one leg as we do not deal with dialogs in early state */ { qvals[21].val.str_val= leg->tag; qvals[22].val.int_val= leg->cseq; qvals[23].val.str_val= leg->contact; qvals[24].val.str_val= leg->route_set; } if(dlg->db_flag == INSERTDB_FLAG) { /* insert into database */ if(b2be_dbf.insert(b2be_db, qcols, qvals, DB_COLS_NO)< 0) { LM_ERR("Sql insert failed\n"); if(!no_lock) lock_release(&htable[i].lock); return; } } else { if(b2be_dbf.update(b2be_db, qcols, 0, qvals, qcols+n_start_update, qvals+n_start_update, n_query_update, DB_COLS_NO-n_start_update)< 0) { LM_ERR("Sql update failed\n"); if(!no_lock) lock_release(&htable[i].lock); return; } } dlg->db_flag = NO_UPDATEDB_FLAG; dlg = dlg->next; } if(!no_lock) lock_release(&htable[i].lock); } } dlg_leg_t* b2b_dup_leg(dlg_leg_t* leg, int mem_type) { int size; dlg_leg_t* new_leg; size = sizeof(dlg_leg_t) + leg->route_set.len + leg->tag.len + leg->contact.len; if(mem_type == SHM_MEM_TYPE) new_leg = (dlg_leg_t*)shm_malloc(size); else new_leg = (dlg_leg_t*)pkg_malloc(size); if(new_leg == NULL) { LM_ERR("No more shared memory"); goto error; } memset(new_leg, 0, size); size = sizeof(dlg_leg_t); if(leg->contact.s && leg->contact.len) { new_leg->contact.s = (char*)new_leg + size; memcpy(new_leg->contact.s, leg->contact.s, leg->contact.len); new_leg->contact.len = leg->contact.len; size+= leg->contact.len; } if(leg->route_set.s) { new_leg->route_set.s = (char*)new_leg + size; memcpy(new_leg->route_set.s, leg->route_set.s, leg->route_set.len); new_leg->route_set.len = leg->route_set.len; size+= leg->route_set.len; } new_leg->tag.s = (char*)new_leg + size; memcpy(new_leg->tag.s, leg->tag.s, leg->tag.len); new_leg->tag.len = leg->tag.len; size += leg->tag.len; new_leg->cseq = leg->cseq; new_leg->id = leg->id; return new_leg; error: return 0; } int b2b_entities_restore(void) { db_res_t *result= NULL; db_row_t *rows = NULL; db_val_t *row_vals= NULL; int i; dlg_leg_t leg, *new_leg; b2b_dlg_t dlg, *shm_dlg= NULL; unsigned int hash_index, local_index; int nr_rows; str* b2b_key; str sockinfo_str; str host; int port, proto; b2b_table htable; int type; int no_rows = 10; if(b2be_db == NULL) { LM_DBG("NULL database connection\n"); return 0; } if(b2be_dbf.use_table(b2be_db, &b2be_dbtable)< 0) { LM_ERR("sql use table failed\n"); return -1; } if (DB_CAPABILITY(b2be_dbf, DB_CAP_FETCH)) { if(b2be_dbf.query(b2be_db,0,0,0,qcols, 0, DB_COLS_NO, 0, 0) < 0) { LM_ERR("Error while querying (fetch) database\n"); return -1; } no_rows = estimate_available_rows( DB_COLS_NO*128, DB_COLS_NO); if (no_rows==0) no_rows = 10; if(b2be_dbf.fetch_result(b2be_db,&result,no_rows)<0) { LM_ERR("fetching rows failed\n"); return -1; } } else { if (b2be_dbf.query (b2be_db, 0, 0, 0,qcols,0, DB_COLS_NO, 0, &result) < 0) { LM_ERR("querying presentity\n"); return -1; } } nr_rows = RES_ROW_N(result); do { LM_DBG("loading information from database %i records\n", nr_rows); rows = RES_ROWS(result); /* for every row */ for(i=0; ilegs = new_leg; } /* any more data to be fetched ?*/ if (DB_CAPABILITY(b2be_dbf, DB_CAP_FETCH)) { if (b2be_dbf.fetch_result( b2be_db, &result, no_rows) < 0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(result); } else { nr_rows = 0; } }while (nr_rows>0); b2be_dbf.free_result(b2be_db, result); return 0; error: if(result) b2be_dbf.free_result(b2be_db, result); return -1; } void b2b_entities_dump(int no_lock) { if(!server_htable || !client_htable) { LM_DBG("NULL pointers for hash tables\n"); return; } store_b2b_dlg(server_htable, server_hsize, B2B_SERVER, no_lock); store_b2b_dlg(client_htable, client_hsize, B2B_CLIENT, no_lock); } /* delete only one entity */ void b2b_entity_db_delete(int type, b2b_dlg_t* dlg) { if(!b2be_db) return; if(b2be_dbf.use_table(b2be_db, &b2be_dbtable)< 0) { LM_ERR("sql use table failed\n"); return; } qvals[0].val.int_val = type; qvals[1].val.str_val = dlg->tag[0]; qvals[2].val.str_val = dlg->tag[1]; qvals[3].val.str_val = dlg->callid; /* if the state is terminated delete the record */ if(b2be_dbf.delete(b2be_db, qcols, 0, qvals, 4)< 0) { LM_ERR("Sql delete failed\n"); } } /* delete all entities belonging to a tuple */ void b2b_db_delete(str param) { if(!b2be_db) return; qvals[12].val.str_val = param; if(b2be_dbf.use_table(b2be_db, &b2be_dbtable)< 0) { LM_ERR("sql use table failed\n"); return; } if(b2be_dbf.delete(b2be_db, qcols+12, 0, qvals+12, 1)< 0) { LM_ERR("Sql delete failed\n"); } } opensips-2.2.2/modules/b2b_entities/b2be_db.h000066400000000000000000000023671300170765700210440ustar00rootroot00000000000000/* * back-to-back entities module * * Copyright (C) 2011 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2011-04-04 initial version (Anca Vamanu) */ #ifndef _B2BE_DB_H_ #define _B2BE_DB_H_ #include "dlg.h" void b2b_entities_dump(int no_lock); int b2b_entities_restore(void); int b2be_db_insert(b2b_dlg_t* dlg, int type); int b2be_db_update(b2b_dlg_t* dlg, int type); void b2be_initialize(void); void b2b_db_delete(str param); void b2b_entity_db_delete(int type, b2b_dlg_t* dlg); #endif opensips-2.2.2/modules/b2b_entities/client.c000066400000000000000000000177131300170765700210370ustar00rootroot00000000000000/* * back-to-back entities module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP client. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #include #include #include #include "../../crc.h" #include "../tm/dlg.h" #include "../../ut.h" #include "../presence/hash.h" #include "../../parser/parse_methods.h" #include "dlg.h" #include "client.h" #include "b2b_entities.h" void b2b_client_tm_cback( struct cell *t, int type, struct tmcb_params *ps) { b2b_tm_cback(t, client_htable, ps); } #define FROM_TAG_LEN (MD5_LEN + 1 /* - */ + CRC16_LEN) /* length of FROM tags */ static char from_tag[FROM_TAG_LEN + 1]; static void generate_tag(str* tag, str* src, str* callid) { int len; MD5StringArray(from_tag, src, 1); len = MD5_LEN; /* calculate from tag from callid */ if(callid) { from_tag[len++] = '-'; crcitt_string_array(&from_tag[MD5_LEN + 1], callid, 1); len+= CRC16_LEN; } tag->s = from_tag; tag->len = len; LM_DBG("from_tag = %.*s\n", tag->len, tag->s); } /** * Function to create a new client entity a send send an initial message * method : the method of the message * to_uri : the destination URI * from_uri: the source URI * extra_headers: the extra headers to be added in the request * b2b_cback : callback function to notify the logic about a change in dialog * param : the parameter that will be used when calling b2b_cback function * * Return value: dialog key allocated in private memory * */ #define HASH_SIZE 1<<23 str* client_new(client_info_t* ci,b2b_notify_t b2b_cback, b2b_add_dlginfo_t add_dlginfo, str* param) { int result; b2b_dlg_t* dlg; unsigned int hash_index; str* callid = NULL; int size; str ehdr = {0, 0}; str* b2b_key_shm = NULL; dlg_t td; str from_tag; str random_info = {0, 0}; if(ci == NULL || b2b_cback == NULL || param== NULL) { LM_ERR("Wrong parameters.\n"); return NULL; } if(param && param->len > B2BL_MAX_KEY_LEN) { LM_ERR("parameter too long, received [%d], maximum [%d]\n", param->len, B2BL_MAX_KEY_LEN); return 0; } hash_index = core_hash(&ci->from_uri, &ci->to_uri, client_hsize); if(ci->from_tag) from_tag = *ci->from_tag; else generate_tag(&from_tag, &ci->from_uri, ci->extra_headers); /* create a dummy b2b dialog structure to be inserted in the hash table*/ size = sizeof(b2b_dlg_t) + ci->to_uri.len + ci->from_uri.len + ci->from_dname.len + ci->to_dname.len + from_tag.len + ci->local_contact.len + B2B_MAX_KEY_SIZE + B2BL_MAX_KEY_LEN; /* create record in hash table */ dlg = (b2b_dlg_t*)shm_malloc(size); if(dlg == NULL) { LM_ERR("No more shared memory\n"); return 0; } memset(dlg, 0, size); size = sizeof(b2b_dlg_t); CONT_COPY(dlg, dlg->from_uri, ci->from_uri); CONT_COPY(dlg, dlg->to_uri, ci->to_uri); if(ci->to_dname.s) CONT_COPY(dlg, dlg->to_dname, ci->to_dname); if(ci->from_dname.s) CONT_COPY(dlg, dlg->from_dname, ci->from_dname); CONT_COPY(dlg, dlg->tag[CALLER_LEG], from_tag); CONT_COPY(dlg, dlg->contact[CALLER_LEG], ci->local_contact); if(param && param->s) { dlg->param.s = (char*)dlg + size; memcpy(dlg->param.s, param->s, param->len); dlg->param.len = param->len; size+= B2BL_MAX_KEY_LEN; } dlg->b2b_cback = b2b_cback; dlg->add_dlginfo = add_dlginfo; if(parse_method(ci->method.s, ci->method.s+ci->method.len, &dlg->last_method)< 0) { LM_ERR("wrong method %.*s\n", ci->method.len, ci->method.s); shm_free(dlg); goto error; } dlg->state = B2B_NEW; dlg->cseq[CALLER_LEG] =(ci->cseq?ci->cseq:1); dlg->send_sock = ci->send_sock; /* if the callid should be the same in more instances running at the same time (replication)*/ if(!replication_mode) { srand(get_uticks()); random_info.s = int2str(rand(), &random_info.len); } dlg->send_sock = ci->send_sock; dlg->id = core_hash(&from_tag, random_info.s?&random_info:0, HASH_SIZE); /* callid must have the special format */ dlg->db_flag = NO_UPDATEDB_FLAG; callid = b2b_htable_insert(client_htable, dlg, hash_index, B2B_CLIENT, 0); if(callid == NULL) { LM_ERR("Inserting new record in hash table failed\n"); shm_free(dlg); goto error; } if(b2breq_complete_ehdr(ci->extra_headers, &ehdr, ci->body, &ci->local_contact)< 0) { LM_ERR("Failed to complete extra headers\n"); goto error; } /* copy the key in shared memory to transmit it as a parameter to the tm callback */ b2b_key_shm = b2b_key_copy_shm(callid); if(b2b_key_shm== NULL) { LM_ERR("no more shared memory\n"); goto error; } CONT_COPY(dlg, dlg->callid, (*callid)); /* create the tm dialog structure with the a costum callid */ memset(&td, 0, sizeof(dlg_t)); td.loc_seq.value = dlg->cseq[CALLER_LEG]; dlg->last_invite_cseq = dlg->cseq[CALLER_LEG]; td.loc_seq.is_set = 1; td.id.call_id = *callid; td.id.loc_tag = from_tag; td.id.rem_tag.s = 0; td.id.rem_tag.len = 0; td.rem_uri = ci->to_uri; if(ci->req_uri.s) td.rem_target = ci->req_uri; else td.rem_target = ci->to_uri; if(td.rem_target.s[0] == '<') { td.rem_target.s++; td.rem_target.len-=2; } td.rem_dname = ci->to_dname; td.loc_uri = ci->from_uri; td.loc_dname = ci->from_dname; td.state= DLG_CONFIRMED; td.T_flags=T_NO_AUTOACK_FLAG|T_PASS_PROVISIONAL_FLAG ; td.send_sock = ci->send_sock; if(ci->dst_uri.len) td.obp = ci->dst_uri; td.avps = ci->avps; tmb.setlocalTholder(&dlg->uac_tran); /* send request */ result= tmb.t_request_within (&ci->method, /* method*/ &ehdr, /* extra headers*/ ci->body, /* body*/ &td, /* dialog structure*/ b2b_client_tm_cback, /* callback function*/ b2b_key_shm, shm_free_param); /* function to release the parameter*/ if(td.route_set) pkg_free(td.route_set); if(result< 0) { LM_ERR("while sending request with t_request\n"); pkg_free(callid); shm_free(b2b_key_shm); return NULL; } tmb.setlocalTholder(NULL); LM_DBG("new client entity [%p] callid=[%.*s] tag=[%.*s] param=[%.*s]" " last method=[%d] dlg->uac_tran=[%p]\n", dlg, callid->len, callid->s, dlg->tag[CALLER_LEG].len, dlg->tag[CALLER_LEG].s, dlg->param.len, dlg->param.s, dlg->last_method, dlg->uac_tran); return callid; error: if(callid) pkg_free(callid); return NULL; } dlg_t* b2b_client_build_dlg(b2b_dlg_t* dlg, dlg_leg_t* leg) { dlg_t* td =NULL; td = (dlg_t*)pkg_malloc(sizeof(dlg_t)); if(td == NULL) { ERR_MEM(PKG_MEM_STR); } memset(td, 0, sizeof(dlg_t)); td->loc_seq.value = dlg->cseq[CALLER_LEG]; dlg->cseq[CALLER_LEG]++; td->loc_seq.is_set = 1; td->id.call_id = dlg->callid; td->id.loc_tag = dlg->tag[CALLER_LEG]; td->loc_uri = dlg->from_uri; td->rem_uri = dlg->to_uri; td->loc_dname = dlg->from_dname; td->rem_dname = dlg->to_dname; if(leg) { if(leg->route_set.s && leg->route_set.len) { if(parse_rr_body(leg->route_set.s, leg->route_set.len, &td->route_set)< 0) { LM_ERR("failed to parse record route body\n"); goto error; } } td->id.rem_tag = leg->tag; LM_DBG("Rem_target = %.*s\n", leg->contact.len, leg->contact.s); td->rem_target = leg->contact; } td->state= DLG_CONFIRMED ; td->send_sock = dlg->send_sock; if(dlg->send_sock) LM_DBG("send sock= %.*s\n", dlg->send_sock->address_str.len, dlg->send_sock->address_str.s); return td; error: if(td) pkg_free(td); return 0; } opensips-2.2.2/modules/b2b_entities/client.h000066400000000000000000000024661300170765700210430ustar00rootroot00000000000000/* * back-to-back entities modules * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #ifndef _B2B_CLIENT_H_ #define _B2B_CLIENT_H_ #include "../../str.h" #include "../../parser/msg_parser.h" #include "dlg.h" #include "b2b_entities.h" str* client_new(client_info_t* ci, b2b_notify_t b2b_cback, b2b_add_dlginfo_t add_dlginfo, str* param); void b2b_client_tm_cback( struct cell *t, int type, struct tmcb_params *ps); dlg_t* b2b_client_build_dlg(b2b_dlg_t* dlg, dlg_leg_t* leg); #endif opensips-2.2.2/modules/b2b_entities/dlg.c000066400000000000000000002151261300170765700203250ustar00rootroot00000000000000/* * back-to-back entities modules * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) * 2011-06-27 added authentication support (Ovidiu Sas) */ #include #include #include "../../data_lump_rpl.h" #include "../../parser/parse_rr.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_from.h" #include "../../parser/parse_methods.h" #include "../../parser/parse_content.h" #include "../../parser/parse_authenticate.h" #include "../../locking.h" #include "../../script_cb.h" #include "../uac_auth/uac_auth.h" #include "../presence/hash.h" #include "../../action.h" #include "../../trim.h" #include "dlg.h" #include "b2b_entities.h" #include "b2be_db.h" #define BUF_LEN 1024 str ack = str_init(ACK); str bye = str_init(BYE); /* used to make WRITE_THROUGH db mode more efficient */ b2b_dlg_t* current_dlg= NULL; #define UPDATE_DBFLAG(dlg) do{ \ if(b2be_db_mode == WRITE_BACK && dlg->db_flag==NO_UPDATEDB_FLAG) \ dlg->db_flag = UPDATEDB_FLAG; \ } while(0) void print_b2b_dlg(b2b_dlg_t *dlg) { dlg_leg_t *leg = dlg->legs; LM_DBG("dlg[%p][%p][%p]: [%.*s] id=[%d] param=[%.*s] state=[%d] db_flag=[%d]\n", dlg, dlg->prev, dlg->next, dlg->ruri.len, dlg->ruri.s, dlg->id, dlg->param.len, dlg->param.s, dlg->state, dlg->db_flag); LM_DBG(" from=[%.*s] [%.*s]\n", dlg->from_dname.len, dlg->from_dname.s, dlg->from_uri.len, dlg->from_uri.s); LM_DBG(" to=[%.*s] [%.*s]\n", dlg->to_dname.len, dlg->to_dname.s, dlg->to_uri.len, dlg->to_uri.s); LM_DBG("callid=[%.*s] tag=[%.*s][%.*s]\n", dlg->callid.len, dlg->callid.s, dlg->tag[0].len, dlg->tag[0].s, dlg->tag[1].len, dlg->tag[1].s); while(leg) { LM_DBG("leg[%p][%p] id=[%d] tag=[%.*s] cseq=[%d]\n", leg, leg->next, leg->id, leg->tag.len, leg->tag.s, leg->cseq); leg = leg->next; } return; } b2b_dlg_t* b2b_search_htable_next_dlg(b2b_dlg_t* start_dlg, b2b_table table, unsigned int hash_index, unsigned int local_index, str* to_tag, str* from_tag, str* callid) { b2b_dlg_t* dlg; str dlg_from_tag={NULL, 0}; dlg_leg_t* leg; LM_DBG("entering with start=%p, table=%p, hash=%d, label=%d \n", start_dlg,table,hash_index,local_index); if(callid) LM_DBG("searching callid %d[%.*s]\n", callid->len,callid->len, callid->s); if(to_tag) LM_DBG("searching totag %d[%.*s]\n", to_tag->len,to_tag->len, to_tag->s); if(from_tag) LM_DBG("searching fromtag %d[%.*s]\n", from_tag->len,from_tag->len, from_tag->s); dlg= start_dlg?start_dlg->next:table[hash_index].first; while(dlg) { if(dlg->id != local_index) { dlg = dlg->next; continue; } /*check if the dialog information correspond */ if(table == server_htable) { if(!from_tag) return NULL; dlg_from_tag= dlg->tag[CALLER_LEG]; /* check from tag and callid */ if(dlg_from_tag.len==from_tag->len && strncmp(dlg_from_tag.s, from_tag->s, dlg_from_tag.len)==0 && dlg->callid.len==callid->len && strncmp(dlg->callid.s, callid->s, callid->len)==0) { LM_DBG("Match for server dlg [%p] dlg->uas_tran=[%p]\n", dlg, dlg->uas_tran); return dlg; } } else { /* LM_DBG("dialog totag [%.*s] with state %d\n", dlg->tag[CALLER_LEG].len, dlg->tag[CALLER_LEG].s, dlg->state); */ /* it is an UAC dialog (callid is the key)*/ if(dlg->tag[CALLER_LEG].len == to_tag->len && strncmp(dlg->tag[CALLER_LEG].s, to_tag->s, to_tag->len)== 0) { leg = dlg->legs; if(dlg->state < B2B_CONFIRMED || dlg->state>=B2B_DESTROYED) { if(from_tag == NULL || from_tag->len==0 || leg==NULL) { LM_DBG("Match for client dlg [%p] last_method=%d" " dlg->uac_tran=[%p]\n", dlg, dlg->last_method, dlg->uac_tran); return dlg; } } if(from_tag == NULL || from_tag->s==NULL) { dlg = dlg->next; continue; } /* if it is an already confirmed dialog match the to_tag also*/ while(leg) { if(leg->tag.len == from_tag->len && strncmp(leg->tag.s, from_tag->s, from_tag->len)== 0) return dlg; leg = leg->next; } if(dlg->state < B2B_CONFIRMED || dlg->state>=B2B_DESTROYED) /* state not confirmed yet and a new leg */ return dlg; } } dlg = dlg->next; } return NULL; } b2b_dlg_t* b2b_search_htable_dlg(b2b_table table, unsigned int hash_index, unsigned int local_index, str* to_tag, str* from_tag, str* callid) { return b2b_search_htable_next_dlg(NULL, table, hash_index, local_index, to_tag, from_tag, callid); } b2b_dlg_t* b2b_search_htable_next(b2b_dlg_t* start_dlg, b2b_table table, unsigned int hash_index, unsigned int local_index) { b2b_dlg_t* dlg; dlg= start_dlg?start_dlg->next:table[hash_index].first; while(dlg && dlg->id != local_index) dlg = dlg->next; if(dlg == NULL || dlg->id!=local_index) { LM_DBG("No dialog with hash_index=[%d] and local_index=[%d] found\n", hash_index, local_index); return NULL; } return dlg; } b2b_dlg_t* b2b_search_htable(b2b_table table, unsigned int hash_index, unsigned int local_index) { return b2b_search_htable_next(NULL, table, hash_index, local_index); } /* this is only called by server new */ str* b2b_htable_insert(b2b_table table, b2b_dlg_t* dlg, int hash_index, int src, int reload) { b2b_dlg_t * it, *prev_it= NULL; str* b2b_key; if(!reload) lock_get(&table[hash_index].lock); dlg->prev = dlg->next = NULL; it = table[hash_index].first; if(it == NULL) { table[hash_index].first = dlg; } else { while(it) { prev_it = it; it = it->next; } prev_it->next = dlg; dlg->prev = prev_it; } /* if an insert in server_htable -> copy the b2b_key in the to_tag */ b2b_key = b2b_generate_key(hash_index, dlg->id); if(b2b_key == NULL) { if(!reload) lock_release(&table[hash_index].lock); LM_ERR("Failed to generate b2b key\n"); return NULL; } if(src == B2B_SERVER) { dlg->tag[CALLEE_LEG].s = (char*)shm_malloc(b2b_key->len); if(dlg->tag[CALLEE_LEG].s == NULL) { LM_ERR("No more shared memory\n"); if(!reload) lock_release(&table[hash_index].lock); return 0; } memcpy(dlg->tag[CALLEE_LEG].s, b2b_key->s, b2b_key->len); dlg->tag[CALLEE_LEG].len = b2b_key->len; if(!reload && b2be_db_mode == WRITE_THROUGH) b2be_db_insert(dlg, src); } if(!reload) lock_release(&table[hash_index].lock); return b2b_key; } /* key format : B2B.hash_index.local_index.timestamp * */ int b2b_parse_key(str* key, unsigned int* hash_index, unsigned int* local_index) { char* p; str s; if(!key || !key->s) return -1; if(strncmp(key->s, b2b_key_prefix.s, b2b_key_prefix.len) != 0 || key->len<(b2b_key_prefix.len +4) || key->s[b2b_key_prefix.len]!='.') { LM_DBG("Does not have b2b_entities prefix\n"); return -1; } s.s = key->s + b2b_key_prefix.len+1; p= strchr(s.s, '.'); if(p == NULL || ((p-s.s) > key->len) ) { LM_DBG("Wrong format for b2b key\n"); return -1; } s.len = p - s.s; if(str2int(&s, hash_index) < 0) { LM_DBG("Could not extract hash_index [%.*s]\n", key->len, key->s); return -1; } p++; s.s = p; p= strchr(s.s, '.'); if(p == NULL || ((p-s.s) > key->len) ) { LM_DBG("Wrong format for b2b key\n"); return -1; } s.len = p - s.s; if(str2int(&s, local_index)< 0) { LM_DBG("Wrong format for b2b key\n"); return -1; } /* we do not really care about the third part of the key */ LM_DBG("hash_index = [%d] - local_index= [%d]\n", *hash_index, *local_index); return 0; } str* b2b_generate_key(unsigned int hash_index, unsigned int local_index) { char buf[B2B_MAX_KEY_SIZE]; str* b2b_key; int len; len = sprintf(buf, "%s.%d.%d.%ld", b2b_key_prefix.s, hash_index, local_index, startup_time+get_ticks()); b2b_key = (str*)pkg_malloc(sizeof(str)+ len); if(b2b_key== NULL) { LM_ERR("no more private memory\n"); return NULL; } b2b_key->s = (char*)b2b_key + sizeof(str); memcpy(b2b_key->s, buf, len); b2b_key->len = len; return b2b_key; } str* b2b_key_copy_shm(str* b2b_key) { str* b2b_key_shm = NULL; b2b_key_shm = (str*)shm_malloc(sizeof(str)+ b2b_key->len); if(b2b_key_shm== NULL) { LM_ERR("no more shared memory\n"); return 0; } b2b_key_shm->s = (char*)b2b_key_shm + sizeof(str); memcpy(b2b_key_shm->s, b2b_key->s, b2b_key->len); b2b_key_shm->len = b2b_key->len; return b2b_key_shm; } b2b_dlg_t* b2b_dlg_copy(b2b_dlg_t* dlg) { b2b_dlg_t* new_dlg; int size; size = sizeof(b2b_dlg_t) + dlg->callid.len+ dlg->from_uri.len+ dlg->to_uri.len+ dlg->tag[0].len + dlg->tag[1].len+ dlg->route_set[0].len+ dlg->route_set[1].len+ dlg->contact[0].len+ dlg->contact[1].len+ dlg->ruri.len+ B2BL_MAX_KEY_LEN+ dlg->from_dname.len + dlg->to_dname.len; new_dlg = (b2b_dlg_t*)shm_malloc(size); if(new_dlg == 0) { LM_ERR("No more shared memory\n"); return 0; } memset(new_dlg, 0, size); size = sizeof(b2b_dlg_t); if(dlg->ruri.s) CONT_COPY(new_dlg, new_dlg->ruri, dlg->ruri); CONT_COPY(new_dlg, new_dlg->callid, dlg->callid); CONT_COPY(new_dlg, new_dlg->from_uri, dlg->from_uri); CONT_COPY(new_dlg, new_dlg->to_uri, dlg->to_uri); if(dlg->tag[0].len && dlg->tag[0].s) CONT_COPY(new_dlg, new_dlg->tag[0], dlg->tag[0]); if(dlg->tag[1].len && dlg->tag[1].s) CONT_COPY(new_dlg, new_dlg->tag[1], dlg->tag[1]); if(dlg->route_set[0].len && dlg->route_set[0].s) CONT_COPY(new_dlg, new_dlg->route_set[0], dlg->route_set[0]); if(dlg->route_set[1].len && dlg->route_set[1].s) CONT_COPY(new_dlg, new_dlg->route_set[1], dlg->route_set[1]); if(dlg->contact[0].len && dlg->contact[0].s) CONT_COPY(new_dlg, new_dlg->contact[0], dlg->contact[0]); if(dlg->contact[1].len && dlg->contact[1].s) CONT_COPY(new_dlg, new_dlg->contact[1], dlg->contact[1]); if(dlg->param.s) { new_dlg->param.s= (char*)new_dlg+ size; memcpy(new_dlg->param.s, dlg->param.s, dlg->param.len); new_dlg->param.len= dlg->param.len; size+= B2BL_MAX_KEY_LEN; } if(dlg->from_dname.s) CONT_COPY(new_dlg, new_dlg->from_dname, dlg->from_dname); if(dlg->to_dname.s) CONT_COPY(new_dlg, new_dlg->to_dname, dlg->to_dname); new_dlg->cseq[0] = dlg->cseq[0]; new_dlg->cseq[1] = dlg->cseq[1]; new_dlg->id = dlg->id; new_dlg->state = dlg->state; new_dlg->b2b_cback = dlg->b2b_cback; new_dlg->add_dlginfo = dlg->add_dlginfo; new_dlg->last_invite_cseq = dlg->last_invite_cseq; new_dlg->db_flag = dlg->db_flag; new_dlg->send_sock = dlg->send_sock; return new_dlg; } void b2b_delete_legs(dlg_leg_t** legs) { dlg_leg_t* leg, *aux_leg; leg = *legs; while(leg) { aux_leg = leg->next; shm_free(leg); leg = aux_leg; } *legs = NULL; } char* DLG_FLAGS_STR(int type) { switch(type){ case DLGCB_EARLY: return "DLGCB_EARLY"; case DLGCB_REQ_WITHIN: return "DLGCB_EARLY"; case DLGCB_RESPONSE_WITHIN: return "DLGCB_RESPONSE_WITHIN"; } return "Flag not known"; } void set_dlg_state(b2b_dlg_t* dlg, int meth) { switch(meth) { case METHOD_INVITE: if (dlg->state != B2B_NEW_AUTH) dlg->state= B2B_MODIFIED; break; case METHOD_CANCEL: case METHOD_BYE: dlg->state= B2B_TERMINATED; break; case METHOD_ACK: dlg->state= B2B_ESTABLISHED; break; default: break; } } b2b_dlg_t* b2bl_search_iteratively(str* callid, str* from_tag, str* ruri, unsigned int hash_index) { b2b_dlg_t* dlg= NULL; LM_DBG("Search for record with callid= %.*s, tag= %.*s\n", callid->len, callid->s, from_tag->len, from_tag->s); /* must search iteratively */ lock_get(&server_htable[hash_index].lock); dlg = server_htable[hash_index].first; while(dlg) { LM_DBG("Found callid= %.*s, tag= %.*s\n", dlg->callid.len, dlg->callid.s, dlg->tag[CALLER_LEG].len, dlg->tag[CALLER_LEG].s); if(dlg->callid.len == callid->len && strncmp(dlg->callid.s, callid->s, callid->len)== 0 && dlg->tag[CALLER_LEG].len == from_tag->len && strncmp(dlg->tag[CALLER_LEG].s, from_tag->s, from_tag->len)== 0) { if(!ruri) break; if(ruri->len == dlg->ruri.len && strncmp(ruri->s, dlg->ruri.s, ruri->len)== 0) break; } dlg = dlg->next; } return dlg; } int b2b_prescript_f(struct sip_msg *msg, void *uparam) { str b2b_key; b2b_dlg_t* dlg = 0, *aux_dlg; unsigned int hash_index, local_index; b2b_notify_t b2b_cback; str param= {NULL,0}; b2b_table table = NULL; int method_value; str from_tag; str to_tag; str callid; struct cell* tm_tran; int ret; str host; int port; int etype= B2B_NONE; int dlg_state = 0; struct sip_uri puri; struct hdr_field *route_hdr; rr_t *rt; /* check if a b2b request */ if (parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse message\n"); return SCB_RUN_ALL; } LM_DBG("start - method = %.*s\n", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s); if(msg->route) { LM_DBG("Found route headers\n"); route_hdr = msg->route; /* we accept Route hdrs only if preloaded route with out IPs */ if (parse_rr(route_hdr) < 0) { LM_ERR("failed to parse Route HF\n"); return SCB_RUN_ALL; } rt = (rr_t*)route_hdr->parsed; /* check if first route is local*/ if ( parse_uri(rt->nameaddr.uri.s,rt->nameaddr.uri.len,&puri)!=0 ) { LM_ERR("Route uri is not valid <%.*s>\n", rt->nameaddr.uri.len,rt->nameaddr.uri.s); return SCB_RUN_ALL; } if (check_self( &puri.host, puri.port_no?puri.port_no:SIP_PORT, puri.proto?puri.proto:PROTO_UDP)!= 1 ) { LM_DBG("First Route uri is not mine\n"); return SCB_RUN_ALL; /* not for b2b */ } /* check if second route is local*/ rt = rt->next; if (rt==NULL) { if (msg->route->sibling) { route_hdr = msg->route->sibling; if (parse_rr(route_hdr) < 0) { LM_ERR("failed to parse second Route HF\n"); return SCB_RUN_ALL; } rt = (rr_t*)route_hdr->parsed; } } if (rt) { if ( parse_uri(rt->nameaddr.uri.s,rt->nameaddr.uri.len,&puri)!=0 ) { LM_ERR("Second route uri is not valid <%.*s>\n", rt->nameaddr.uri.len,rt->nameaddr.uri.s); return SCB_RUN_ALL; } if (check_self( &puri.host, puri.port_no?puri.port_no:SIP_PORT, puri.proto?puri.proto:PROTO_UDP)!= 1 ) { LM_DBG("Second Route uri is not mine\n"); return SCB_RUN_ALL; /* not for b2b */ } /* check the presence of the third route */ if (rt->next || route_hdr->sibling) { LM_DBG("More than 2 route hdr -> not for me\n"); return SCB_RUN_ALL; /* not for b2b */ } } /* "route" hdr checking is ok, continue */ } method_value = msg->first_line.u.request.method_value; if(method_value == METHOD_ACK) { goto search_dialog; } if(parse_sip_msg_uri(msg)< 0) { LM_ERR("Failed to parse uri\n"); return SCB_RUN_ALL; } host = msg->parsed_uri.host; port = msg->parsed_uri.port_no; /* check if RURI points to me */ if(method_value!= METHOD_CANCEL) { LM_DBG(" host:port [%.*s][%d]\n", host.len, host.s, port); if (!check_self( &host, port ? port : SIP_PORT, msg->rcv.proto)) { LM_DBG("RURI does not point to me\n"); return SCB_RUN_ALL; } } if(method_value == METHOD_PRACK) { LM_DBG("Received a PRACK - send 200 reply\n"); str reason={"OK", 2}; /* send 200 OK and exit */ tmb.t_reply(msg, 200, &reason); tm_tran = tmb.t_gett(); if(tm_tran) tmb.unref_cell(tm_tran); /* No need to apply lumps */ if(req_routeid > 0) run_top_route(rlist[req_routeid].a, msg); goto done; } search_dialog: if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("no callid header found\n"); return SCB_RUN_ALL; } /* examine the from header */ if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); return SCB_RUN_ALL; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); return SCB_RUN_ALL; } } callid = msg->callid->body; from_tag = ((struct to_body*)msg->from->parsed)->tag_value; if (from_tag.len==0 || from_tag.s==NULL) { LM_ERR("From header has no TAG parameter\n"); return SCB_RUN_ALL; } /* if a CANCEL request - search iteratively in the server_htable*/ if(method_value == METHOD_CANCEL) { /*str ruri= msg->first_line.u.request.uri;*/ str reply_text={"canceling", 9}; /* This makes no sense - why not accepting a CANCEL that was generated by other b2b instance ? or ourselves ? - bogdan if(b2b_parse_key(&callid, &hash_index, &local_index) >= 0) { LM_DBG("received a CANCEL message that I sent\n"); return SCB_RUN_ALL; } */ hash_index = core_hash(&callid, &from_tag, server_hsize); /* As per RFC3261, the RURI must be used when matching the CANCEL against the INVITE, but we should not do it here as B2B learns a RURI that may have been changed in script (before invoking the B2B module), while the CANCEL has the original RURI (as received) */ dlg = b2bl_search_iteratively(&callid, &from_tag, NULL/*&ruri*/, hash_index); if(dlg == NULL) { lock_release(&server_htable[hash_index].lock); LM_DBG("No dialog found for cancel\n"); return SCB_RUN_ALL; } table = server_htable; /* send 200 canceling */ ret = tmb.t_newtran(msg); if(ret < 1) { if(ret== 0) { LM_DBG("It is a retransmission, drop\n"); tmb.unref_cell(tmb.t_gett()); } else LM_DBG("Error when creating tm transaction\n"); lock_release(&server_htable[hash_index].lock); return SCB_DROP_MSG; } if(tmb.t_reply(msg, 200, &reply_text) < 0) { LM_ERR("failed to send reply for CANCEL\n"); lock_release(&server_htable[hash_index].lock); return SCB_RUN_ALL; } tmb.unref_cell(tmb.t_gett()); b2b_key = dlg->tag[CALLEE_LEG]; goto logic_notify; } /* we are interested only in request inside dialog */ /* examine the to header */ if (msg->to==NULL || msg->to->parsed == NULL || ((struct to_body *)msg->to->parsed)->error != PARSE_OK ) { LM_DBG("'To' header COULD NOT parsed\n"); return SCB_DROP_MSG; } to_tag = get_to(msg)->tag_value; if(to_tag.s == NULL || to_tag.len == 0) { LM_DBG("Not an inside dialog request- not interested.\n"); return SCB_RUN_ALL; } b2b_key = to_tag; /* check if the to tag has the b2b key format -> meaning that it is a server request */ if(b2b_key.s && b2b_parse_key(&b2b_key, &hash_index, &local_index) >= 0) { LM_DBG("Received a b2b server request [%.*s]\n", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s); table = server_htable; } else { /* check if the callid is in b2b format -> meaning that this is a client request */ b2b_key = msg->callid->body; if(b2b_parse_key(&b2b_key, &hash_index, &local_index) >= 0) { LM_DBG("received a b2b client request [%.*s]\n", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s); table = client_htable; } else /* if also not a client request - not for us */ { if(method_value != METHOD_UPDATE) { LM_DBG("Not a b2b request\n"); return SCB_RUN_ALL; } else { /* for server UPDATE sent before dialog confirmed */ table = server_htable; hash_index = core_hash(&callid, &from_tag, server_hsize); dlg = b2bl_search_iteratively(&callid, &from_tag,NULL, hash_index); if(dlg == NULL) { lock_release(&server_htable[hash_index].lock); LM_DBG("No dialog found for cancel\n"); return SCB_RUN_ALL; } } } } if(!dlg) { lock_get(&table[hash_index].lock); dlg = b2b_search_htable_dlg(table, hash_index, local_index, &to_tag, &from_tag, &callid); if(dlg== NULL) { LM_DBG("No dialog found\n"); if(method_value != METHOD_ACK) { str ok = str_init("OK"); if(method_value == METHOD_BYE) tmb.t_reply(msg, 200, &ok); else LM_ERR("No dialog found, callid= [%.*s], method=%.*s\n", callid.len, callid.s,msg->first_line.u.request.method.len, msg->first_line.u.request.method.s); } lock_release(&table[hash_index].lock); return SCB_RUN_ALL; } } if(dlg->state < B2B_CONFIRMED) { if(method_value != METHOD_UPDATE) { LM_DBG("I can not accept requests if the state is not confimed\n"); lock_release(&table[hash_index].lock); return SCB_DROP_MSG; } } LM_DBG("Received request method[%.*s] for dialog[%p]\n", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s, dlg); if(method_value == METHOD_ACK && dlg->state == B2B_ESTABLISHED) { LM_DBG("It is a ACK retransmission, drop\n"); lock_release(&table[hash_index].lock); return SCB_DROP_MSG; } logic_notify: etype = (table==server_htable?B2B_SERVER:B2B_CLIENT); if(req_routeid > 0) { lock_release(&table[hash_index].lock); run_top_route(rlist[req_routeid].a, msg); if (b2b_apply_lumps(msg)) { if (parse_from_header(msg) < 0) { LM_ERR("cannot parse From header\n"); return SCB_DROP_MSG; } callid = msg->callid->body; from_tag = ((struct to_body*)msg->from->parsed)->tag_value; to_tag = get_to(msg)->tag_value; if (table == server_htable) { if (method_value != METHOD_CANCEL) b2b_key = to_tag; } else { b2b_key = msg->callid->body; } } lock_get(&table[hash_index].lock); } if(method_value != METHOD_CANCEL) { ret = tmb.t_newtran(msg); if(ret < 1 && method_value != METHOD_ACK) { if(ret== 0) { LM_DBG("It is a retransmission, drop\n"); tmb.unref_cell(tmb.t_gett()); } else LM_DBG("Error when creating tm transaction\n"); lock_release(&table[hash_index].lock); return SCB_DROP_MSG; } tm_tran = tmb.t_gett(); if(method_value != METHOD_ACK) { if(method_value == METHOD_UPDATE) { dlg->update_tran = tm_tran; } else { if(method_value == METHOD_INVITE || method_value == METHOD_BYE) { tmb.t_setkr(REQ_FWDED); } if(dlg->uas_tran && dlg->uas_tran!=T_UNDEFINED) { if(dlg->uas_tran->uas.request) /* there is another transaction for which no reply * was sent out */ { /* send reply */ LM_DBG("Received another request when the previous " "one was in process\n"); str text = str_init("Request Pending"); if(tmb.t_reply_with_body( tm_tran, 491, &text, 0, 0, &to_tag) < 0) { LM_ERR("failed to send reply with tm\n"); } LM_DBG("Sent reply [491] and unreffed the cell %p\n", tm_tran); } tmb.unref_cell(tm_tran); /* for t_newtran() */ lock_release(&table[hash_index].lock); return SCB_DROP_MSG; } dlg->uas_tran = tm_tran; LM_DBG("Saved uas_tran=[%p] for dlg[%p]\n", tm_tran, dlg); } } else { if(!tm_tran || tm_tran==T_UNDEFINED) tm_tran = tmb.t_get_e2eackt(); if(tm_tran && tm_tran!=T_UNDEFINED) tmb.unref_cell(tm_tran); } } b2b_cback = dlg->b2b_cback; if(dlg->param.s) { param.s = (char*)pkg_malloc(dlg->param.len); if(param.s == NULL) { LM_ERR("No more private memory\n"); lock_release(&table[hash_index].lock); return SCB_RUN_ALL; } memcpy(param.s, dlg->param.s, dlg->param.len); param.len = dlg->param.len; } set_dlg_state( dlg, method_value); UPDATE_DBFLAG(dlg); /* if(b2be_db_mode == WRITE_THROUGH && dlg->state>B2B_CONFIRMED) { if(b2be_db_update(dlg, etype) < 0) LM_ERR("Failed to update in database\n"); } */ current_dlg = dlg; dlg_state = dlg->state; lock_release(&table[hash_index].lock); b2b_cback(msg, &b2b_key, B2B_REQUEST, param.s?¶m:0); if(param.s) pkg_free(param.s); done: current_dlg = 0; if(b2be_db_mode == WRITE_THROUGH && etype!=B2B_NONE && dlg_state>B2B_CONFIRMED) { /* search the dialog */ lock_get(&table[hash_index].lock); for(aux_dlg = table[hash_index].first; aux_dlg; aux_dlg = aux_dlg->next) { if(aux_dlg == dlg) break; } if(!aux_dlg) { LM_DBG("Record not found anymore\n"); lock_release(&table[hash_index].lock); return SCB_DROP_MSG; } if(b2be_db_update(dlg, etype) < 0) LM_ERR("Failed to update in database\n"); lock_release(&table[hash_index].lock); } return SCB_DROP_MSG; } int init_b2b_htables(void) { int i; server_htable = (b2b_table)shm_malloc(server_hsize* sizeof(b2b_entry_t)); client_htable = (b2b_table)shm_malloc(client_hsize* sizeof(b2b_entry_t)); if(!server_htable || !client_htable) ERR_MEM(SHARE_MEM); memset(server_htable, 0, server_hsize* sizeof(b2b_entry_t)); memset(client_htable, 0, client_hsize* sizeof(b2b_entry_t)); for(i= 0; i< server_hsize; i++) { lock_init(&server_htable[i].lock); } for(i= 0; i< client_hsize; i++) { lock_init(&client_htable[i].lock); } return 0; error: return -1; } void destroy_b2b_htables(void) { int i; b2b_dlg_t* dlg, *aux; if(server_htable) { for(i= 0; i< server_hsize; i++) { lock_destroy(&server_htable[i].lock); dlg = server_htable[i].first; while(dlg) { aux = dlg->next; if(dlg->tag[CALLEE_LEG].s) shm_free(dlg->tag[CALLEE_LEG].s); if(dlg->ack_sdp.s) shm_free(dlg->ack_sdp.s); shm_free(dlg); dlg = aux; } } shm_free(server_htable); } if(client_htable) { for(i = 0; i< client_hsize; i++) { lock_destroy(&client_htable[i].lock); dlg = client_htable[i].first; while(dlg) { aux = dlg->next; b2b_delete_legs(&dlg->legs); if(dlg->ack_sdp.s) shm_free(dlg->ack_sdp.s); shm_free(dlg); dlg = aux; } } shm_free(client_htable); } } b2b_dlg_t* b2b_new_dlg(struct sip_msg* msg, str* local_contact, b2b_dlg_t* init_dlg, str* param) { struct to_body *pto, *pfrom = NULL; b2b_dlg_t dlg; contact_body_t* b; b2b_dlg_t* shm_dlg = NULL; memset(&dlg, 0, sizeof(b2b_dlg_t)); if (parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse message\n"); return 0; } /* reject CANCEL messages */ if (msg->first_line.type == SIP_REQUEST) { if(msg->first_line.u.request.method_value != METHOD_INVITE) { LM_ERR("Called b2b_init on a Cancel message\n"); return 0; } dlg.ruri = msg->first_line.u.request.uri; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_DBG("'To' header COULD NOT parsed\n"); return 0; } if(pto->tag_value.s!= 0 && pto->tag_value.len != 0) { LM_DBG("Not an initial request\n"); dlg.tag[CALLEE_LEG] = pto->tag_value; } /* examine the from header */ if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); return 0; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); return 0; } } pfrom = (struct to_body*)msg->from->parsed; if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); return 0; } dlg.tag[CALLER_LEG] = pfrom->tag_value; /* for ACK TO & FROM headers must be the same as in Invite */ if(init_dlg) { dlg.to_uri = init_dlg->to_uri; dlg.to_dname = init_dlg->to_dname; dlg.from_uri = init_dlg->from_uri; dlg.from_dname = init_dlg->from_dname; } else { dlg.to_uri= pto->uri; dlg.to_dname= pto->display; dlg.from_uri = pfrom->uri; dlg.from_dname = pfrom->display; } if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("failed to parse callid header\n"); return 0; } dlg.callid = msg->callid->body; if( msg->cseq==NULL || msg->cseq->body.s==NULL) { LM_ERR("failed to parse cseq header\n"); return 0; } if (str2int( &(get_cseq(msg)->number), &dlg.cseq[CALLER_LEG])!=0 ) { LM_ERR("failed to parse cseq number - not an integer\n"); return 0; } dlg.last_invite_cseq = dlg.cseq[CALLER_LEG]; dlg.cseq[CALLEE_LEG] = 1; if( msg->contact!=NULL && msg->contact->body.s!=NULL) { if(parse_contact(msg->contact) <0 ) { LM_ERR("failed to parse contact header\n"); return 0; } b= (contact_body_t* )msg->contact->parsed; if(b == NULL) { LM_ERR("contact header not parsed\n"); return 0; } if(init_dlg) /* called on reply */ dlg.contact[CALLEE_LEG] = b->contacts->uri; else dlg.contact[CALLER_LEG] = b->contacts->uri; } if(msg->record_route!=NULL && msg->record_route->body.s!= NULL) { if( print_rr_body(msg->record_route, &dlg.route_set[CALLER_LEG], (init_dlg?1:0), 0)!= 0) { LM_ERR("failed to process record route\n"); } } dlg.send_sock = msg->rcv.bind_address; if(init_dlg) /* called on reply */ dlg.contact[CALLER_LEG]=*local_contact; else dlg.contact[CALLEE_LEG]=*local_contact; if (!msg->content_length) { LM_ERR("no Content-Length header found!\n"); return 0; } if(!init_dlg) /* called from server_new on initial Invite */ { if(msg->via1->branch) { dlg.id = core_hash(&dlg.ruri, &msg->via1->branch->value, server_hsize); } else { dlg.id = core_hash(&dlg.ruri, &msg->callid->body, server_hsize); } } if(param) dlg.param = *param; dlg.db_flag = INSERTDB_FLAG; shm_dlg = b2b_dlg_copy(&dlg); if(shm_dlg == NULL) { LM_ERR("failed to copy dialog structure in shared memory\n"); pkg_free(dlg.route_set[CALLER_LEG].s); return 0; } if(dlg.route_set[CALLER_LEG].s) pkg_free(dlg.route_set[CALLER_LEG].s); return shm_dlg; } /* * Function to send a reply inside a b2b dialog * et : entity type - it can be B2B_SEVER or B2B_CLIENT * b2b_key : the key to identify the dialog * code : the status code for the reply * text : the reason phrase for the reply * body : the body to be included in the request(optional) * extra_headers : the extra headers to be included in the request(optional) * */ int b2b_send_reply(b2b_rpl_data_t* rpl_data) { enum b2b_entity_type et = rpl_data->et; str* b2b_key = rpl_data->b2b_key; int sip_method = rpl_data->method; int code = rpl_data->code; str* extra_headers = rpl_data->extra_headers; b2b_dlginfo_t* dlginfo = rpl_data->dlginfo; unsigned int hash_index, local_index; b2b_dlg_t* dlg; str* to_tag = NULL; struct cell* tm_tran; struct sip_msg* msg; char buffer[BUF_LEN]; int len; char* p; str ehdr; b2b_table table; str totag, fromtag; struct to_body *pto; unsigned int method_value = METHOD_UPDATE; str local_contact; if(et == B2B_SERVER) { LM_DBG("For server entity\n"); table = server_htable; if(dlginfo) { totag = dlginfo->fromtag; fromtag = dlginfo->totag; } } else { LM_DBG("For client entity\n"); table = client_htable; if(dlginfo) { totag = dlginfo->fromtag; fromtag = dlginfo->totag; } } /* parse the key and find the position in hash table */ if(b2b_parse_key(b2b_key, &hash_index, &local_index) < 0) { LM_ERR("Wrong format for b2b key\n"); return -1; } lock_get(&table[hash_index].lock); if(dlginfo == NULL) { dlg = b2b_search_htable(table, hash_index, local_index); } else { dlg = b2b_search_htable_dlg(table, hash_index, local_index, &fromtag, &totag, &dlginfo->callid); } if(dlg== NULL) { LM_ERR("No dialog found\n"); lock_release(&table[hash_index].lock); return -1; } /* if(dlg->callid.s == NULL) { LM_DBG("NULL callid. Dialog Information not completed yet\n"); lock_release(&table[hash_index].lock); return 0; } */ if(et == B2B_CLIENT) local_contact = dlg->contact[CALLER_LEG]; else local_contact = dlg->contact[CALLEE_LEG]; if(sip_method == METHOD_UPDATE) tm_tran = dlg->update_tran; else { tm_tran = dlg->uas_tran; if(tm_tran) { if(parse_method(tm_tran->method.s, tm_tran->method.s + tm_tran->method.len, &method_value)< 0) { LM_ERR("Wrong method stored in tm transaction [%.*s]\n", tm_tran->method.len, tm_tran->method.s); lock_release(&table[hash_index].lock); return -1; } if(sip_method != method_value) { LM_ERR("Mismatch between the method in tm[%d] and the method to send reply to[%d]\n", sip_method, method_value); lock_release(&table[hash_index].lock); return -1; } } } if(tm_tran == NULL) { LM_DBG("code = %d, last_method= %d\n", code, dlg->last_method); if(dlg->last_reply_code == code) { LM_DBG("it is a retransmission - nothing to do\n"); lock_release(&table[hash_index].lock); return 0; } LM_ERR("Tm transaction not saved!\n"); lock_release(&table[hash_index].lock); return -1; } LM_DBG("Send reply %d %.*s, for dlg [%p]->[%.*s]\n", code, tm_tran->method.len, tm_tran->method.s, dlg, b2b_key->len, b2b_key->s); if(method_value==METHOD_INVITE && (dlg->state==B2B_CONFIRMED || dlg->state==B2B_ESTABLISHED)) { LM_DBG("A retransmission of the reply\n"); lock_release(&table[hash_index].lock); return 0; } if(code >= 200) { if(method_value==METHOD_INVITE) { if(code < 300) dlg->state = B2B_CONFIRMED; else dlg->state= B2B_TERMINATED; UPDATE_DBFLAG(dlg); } LM_DBG("Reseted transaction- send final reply [%p], uas_tran=0\n", dlg); if(sip_method == METHOD_UPDATE) dlg->update_tran = NULL; else dlg->uas_tran = NULL; } msg = tm_tran->uas.request; if(msg== NULL) { LM_DBG("Transaction not valid anymore\n"); lock_release(&table[hash_index].lock); return 0; } if(parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("Failed to parse headers\n"); return 0; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("'To' header COULD NOT parsed\n"); return 0; } /* only for the server replies to the initial INVITE */ if(!pto || !pto->tag_value.s || !pto->tag_value.len) { to_tag = b2b_key; } else to_tag = &pto->tag_value; /* if sent reply for bye, delete the record */ if(method_value == METHOD_BYE) dlg->state = B2B_TERMINATED; dlg->last_reply_code = code; UPDATE_DBFLAG(dlg); if(b2be_db_mode==WRITE_THROUGH && current_dlg!=dlg && dlg->state>B2B_CONFIRMED) { if(b2be_db_update(dlg, et) < 0) LM_ERR("Failed to update in database\n"); } lock_release(&table[hash_index].lock); if((extra_headers?extra_headers->len:0) + 14 + local_contact.len + 20 + CRLF_LEN > BUF_LEN) { LM_ERR("Buffer overflow!\n"); goto error; } p = buffer; if(extra_headers && extra_headers->s && extra_headers->len) { memcpy(p, extra_headers->s, extra_headers->len); p += extra_headers->len; } len = sprintf(p,"Contact: <%.*s>", local_contact.len, local_contact.s); p += len; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; ehdr.len = p -buffer; ehdr.s = buffer; /* send reply */ if(tmb.t_reply_with_body(tm_tran, code, rpl_data->text, rpl_data->body, &ehdr, to_tag) < 0) { LM_ERR("failed to send reply with tm\n"); goto error; } if(code >= 200) { LM_DBG("Sent reply [%d] and unreffed the cell %p\n", code, tm_tran); tmb.unref_cell(tm_tran); } else { LM_DBG("code not >= 200\n"); } return 0; error: if(code >= 200) { tmb.unref_cell(tm_tran); } return -1; } void b2b_delete_record(b2b_dlg_t* dlg, b2b_table htable, unsigned int hash_index) { if(dlg->prev == NULL) { htable[hash_index].first = dlg->next; } else { dlg->prev->next = dlg->next; } if(dlg->next) dlg->next->prev = dlg->prev; if(htable == server_htable && dlg->tag[CALLEE_LEG].s) shm_free(dlg->tag[CALLEE_LEG].s); b2b_delete_legs(&dlg->legs); if(dlg->uac_tran) tmb.unref_cell(dlg->uac_tran); if(dlg->uas_tran) tmb.unref_cell(dlg->uas_tran); if(dlg->ack_sdp.s) shm_free(dlg->ack_sdp.s); shm_free(dlg); } void b2b_entity_delete(enum b2b_entity_type et, str* b2b_key, b2b_dlginfo_t* dlginfo, int db_del) { b2b_table table; unsigned int hash_index, local_index; b2b_dlg_t* dlg; if(et == B2B_SERVER) table = server_htable; else table = client_htable; /* parse the key and find the position in hash table */ if(b2b_parse_key(b2b_key, &hash_index, &local_index) < 0) { LM_ERR("Wrong format for b2b key\n"); return; } lock_get(&table[hash_index].lock); if(dlginfo) dlg = b2b_search_htable_dlg(table, hash_index, local_index, dlginfo->totag.s?&dlginfo->totag:NULL, dlginfo->fromtag.s?&dlginfo->fromtag:NULL, &dlginfo->callid); else dlg = b2b_search_htable(table, hash_index, local_index); if(dlg== NULL) { LM_ERR("No dialog found\n"); lock_release(&table[hash_index].lock); return; } LM_DBG("Deleted dlg [%p]->[%.*s] with dlginfo [%p]\n", dlg, b2b_key->len, b2b_key->s, dlginfo); if(db_del) b2b_entity_db_delete(et, dlg); b2b_delete_record(dlg, table, hash_index); lock_release(&table[hash_index].lock); } void shm_free_param(void* param) { shm_free(param); } dlg_t* b2b_client_dlg(b2b_dlg_t* dlg) { return b2b_client_build_dlg(dlg, dlg->legs); } void free_tm_dlg(dlg_t* td) { if(!td) return; if(td->route_set) free_rr(&td->route_set); pkg_free(td); } int b2b_send_indlg_req(b2b_dlg_t* dlg, enum b2b_entity_type et, str* b2b_key, str* method, str* ehdr, str* body, unsigned int no_cb) { str* b2b_key_shm; dlg_t* td = NULL; transaction_cb* tm_cback; build_dlg_f build_dlg; int method_value = dlg->last_method; int result; b2b_key_shm= b2b_key_copy_shm(b2b_key); if(b2b_key_shm== NULL) { LM_ERR("no more shared memory\n"); return -1; } if(et == B2B_SERVER) { build_dlg = b2b_server_build_dlg; tm_cback = b2b_server_tm_cback; } else { build_dlg = b2b_client_dlg; tm_cback = b2b_client_tm_cback; } /* build structure with dialog information */ td = build_dlg(dlg); if(td == NULL) { LM_ERR("Failed to build tm dialog structure, was asked to send [%.*s]" " request\n", method->len, method->s); shm_free(b2b_key_shm); return -1; } if(method_value == METHOD_ACK) { td->loc_seq.value = dlg->last_invite_cseq; if(et == B2B_SERVER) dlg->cseq[CALLEE_LEG]--; else dlg->cseq[CALLER_LEG]--; if(dlg->ack_sdp.s) { shm_free(dlg->ack_sdp.s); dlg->ack_sdp.s=NULL; dlg->ack_sdp.len=0; } if(body && body->s) { dlg->ack_sdp.s = (char*)shm_malloc(body->len); if(dlg->ack_sdp.s == NULL) { LM_ERR("No more memory\n"); goto error; } memcpy(dlg->ack_sdp.s, body->s, body->len); dlg->ack_sdp.len = body->len; } } else if(method_value == METHOD_INVITE) { dlg->last_invite_cseq = td->loc_seq.value +1; if(dlg->uac_tran) tmb.unref_cell(dlg->uac_tran); tmb.setlocalTholder(&dlg->uac_tran); } if (no_cb) { result= tmb.t_request_within (method, /* method*/ ehdr, /* extra headers*/ body, /* body*/ td, /* dialog structure*/ NULL, /* callback function*/ NULL, /* callback parameter*/ NULL); } else { td->T_flags = T_NO_AUTOACK_FLAG|T_PASS_PROVISIONAL_FLAG; result= tmb.t_request_within (method, /* method*/ ehdr, /* extra headers*/ body, /* body*/ td, /* dialog structure*/ tm_cback, /* callback function*/ b2b_key_shm, /* callback parameter*/ shm_free_param); } tmb.setlocalTholder(0); if(result < 0) { LM_ERR("failed to send request [%.*s] for dlg=[%p] uac_tran=[%p]\n", method->len, method->s, dlg, dlg->uac_tran); goto error; } free_tm_dlg(td); return 0; error: if(td) free_tm_dlg(td); if(b2b_key_shm) shm_free(b2b_key_shm); return -1; } /* * Function to send a request inside a b2b dialog * et : entity type - it can be B2B_SEVER or B2B_CLIENT * b2b_key : the key to identify the dialog * method : the method for the request * extra_headers : the extra headers to be included in the request(optional) * body : the body to be included in the request(optional) * dlginfo : extra params for matching the dialog * no_cb : no callback required * */ int b2b_send_request(b2b_req_data_t* req_data) { enum b2b_entity_type et = req_data->et; str* b2b_key = req_data->b2b_key; str* method = req_data->method; b2b_dlginfo_t* dlginfo = req_data->dlginfo; unsigned int hash_index, local_index; b2b_dlg_t* dlg; str ehdr = {NULL, 0}; b2b_table table; str totag={NULL,0}, fromtag={NULL, 0}; unsigned int method_value; int ret; if(et == B2B_SERVER) { table = server_htable; } else { table = client_htable; } if(dlginfo) { totag = dlginfo->totag; fromtag = dlginfo->fromtag; } /* parse the key and find the position in hash table */ if(b2b_parse_key(b2b_key, &hash_index, &local_index) < 0) { LM_ERR("Wrong format for b2b key [%.*s]\n", b2b_key->len, b2b_key->s); return -1; } lock_get(&table[hash_index].lock); if(dlginfo == NULL) { dlg = b2b_search_htable(table, hash_index, local_index); } else { dlg = b2b_search_htable_dlg(table, hash_index, local_index, totag.s?&totag:NULL, fromtag.s?&fromtag:NULL, &dlginfo->callid); } if(dlg== NULL) { LM_ERR("No dialog found for b2b key [%.*s] and dlginfo=[%p]\n", b2b_key->len, b2b_key->s, dlginfo); goto error; } parse_method(method->s, method->s+method->len, &method_value); if(dlg->state == B2B_TERMINATED) { LM_ERR("Can not send request [%.*s] for entity type [%d] " "for dlg[%p]->[%.*s] in terminated state\n", method->len, method->s, et, dlg, b2b_key->len, b2b_key->s); lock_release(&table[hash_index].lock); if(method_value==METHOD_BYE || method_value==METHOD_CANCEL) return 0; else return -1; } if(b2breq_complete_ehdr(req_data->extra_headers, &ehdr, req_data->body, ((et==B2B_SERVER)?&dlg->contact[CALLEE_LEG]:&dlg->contact[CALLER_LEG]))< 0) { LM_ERR("Failed to complete extra headers\n"); goto error; } if(dlg->state < B2B_CONFIRMED) { if(method_value == METHOD_BYE && et==B2B_CLIENT) /* send CANCEL*/ { method_value = METHOD_CANCEL; } else if(method_value!=METHOD_UPDATE && method_value!=METHOD_PRACK && method_value!=METHOD_CANCEL && (method_value!=METHOD_INVITE /**/)) { LM_ERR("State [%d] not established, can not send request %.*s, [%.*s]\n", dlg->state, method->len, method->s, b2b_key->len, b2b_key->s); lock_release(&table[hash_index].lock); return -1; } } else if(dlg->state==B2B_CONFIRMED && method_value!=METHOD_ACK && dlg->last_method == METHOD_INVITE) { /* send it ACK so that you can send the new request */ b2b_send_indlg_req(dlg, et, b2b_key, &ack, &ehdr, req_data->body, req_data->no_cb); dlg->state= B2B_ESTABLISHED; } dlg->last_method = method_value; LM_DBG("Send request [%.*s] for entity type [%d] for dlg[%p]->[%.*s]\n", method->len, method->s, et, dlg, b2b_key->len, b2b_key->s); UPDATE_DBFLAG(dlg); /* send request */ if(method_value == METHOD_CANCEL) { if(dlg->state < B2B_CONFIRMED) { if(dlg->uac_tran) { struct cell *inv_t; LM_DBG("send cancel request\n"); if (tmb.t_lookup_ident( &inv_t, dlg->uac_tran->hash_index, dlg->uac_tran->label) != 1) { LM_ERR("Invite trasaction not found\n"); goto error; } ret = tmb.t_cancel_trans( inv_t, &ehdr); tmb.unref_cell(inv_t); } else { LM_ERR("No transaction saved. Cannot send CANCEL\n"); goto error; } } else { if(dlg->state == B2B_CONFIRMED) { b2b_send_indlg_req(dlg, et, b2b_key, &ack, &ehdr, 0, req_data->no_cb); } ret = b2b_send_indlg_req(dlg, et, b2b_key, &bye, &ehdr, req_data->body, req_data->no_cb); method_value = METHOD_BYE; } } else { ret = b2b_send_indlg_req(dlg, et, b2b_key, method, &ehdr, req_data->body, req_data->no_cb); } if(ret < 0) { LM_ERR("Failed to send request\n"); goto error; } set_dlg_state(dlg, method_value); if(b2be_db_mode==WRITE_THROUGH && current_dlg!=dlg && dlg->state>B2B_CONFIRMED) { if(b2be_db_update(dlg, et) < 0) LM_ERR("Failed to update in database\n"); } lock_release(&table[hash_index].lock); return 0; error: lock_release(&table[hash_index].lock); return -1; } dlg_leg_t* b2b_find_leg(b2b_dlg_t* dlg, str to_tag) { dlg_leg_t* leg = dlg->legs; while(leg) { /* compare the tag */ if(to_tag.len == leg->tag.len && strncmp(to_tag.s, leg->tag.s, to_tag.len)==0) { LM_DBG("Found existing leg - Nothing to update\n"); return leg; } leg = leg->next; } return 0; } dlg_leg_t* b2b_new_leg(struct sip_msg* msg, str* to_tag, int mem_type) { str contact = {NULL, 0}; str route_set= {NULL, 0}; dlg_leg_t* new_leg; contact_body_t* b; int size; if( msg->contact!=NULL && msg->contact->body.s!=NULL) { if(parse_contact(msg->contact) <0 ) { LM_ERR("failed to parse contact header\n"); goto error; } b= (contact_body_t* )msg->contact->parsed; if(b == NULL) { LM_ERR("contact header not parsed\n"); goto error; } contact = b->contacts->uri; } if(msg->record_route!=NULL && msg->record_route->body.s!= NULL) { if( print_rr_body(msg->record_route, &route_set, 1, 0)!= 0) { LM_ERR("failed to process record route\n"); goto error; } } size = sizeof(dlg_leg_t) + route_set.len + to_tag->len + contact.len; if(mem_type == SHM_MEM_TYPE) new_leg = (dlg_leg_t*)shm_malloc(size); else new_leg = (dlg_leg_t*)pkg_malloc(size); if(new_leg == NULL) { LM_ERR("No more shared memory"); if(route_set.s) pkg_free(route_set.s); goto error; } memset(new_leg, 0, size); size = sizeof(dlg_leg_t); if(contact.s && contact.len) { new_leg->contact.s = (char*)new_leg + size; memcpy(new_leg->contact.s, contact.s, contact.len); new_leg->contact.len = contact.len; size+= contact.len; } if(route_set.s) { new_leg->route_set.s = (char*)new_leg + size; memcpy(new_leg->route_set.s, route_set.s, route_set.len); new_leg->route_set.len = route_set.len; size+= route_set.len; pkg_free(route_set.s); } new_leg->tag.s = (char*)new_leg + size; memcpy(new_leg->tag.s, to_tag->s, to_tag->len); new_leg->tag.len = to_tag->len; size += to_tag->len; if( msg->cseq==NULL || msg->cseq->body.s==NULL) { LM_ERR("failed to parse cseq header\n"); goto error; } if (str2int( &(get_cseq(msg)->number), &new_leg->cseq)!=0 ) { LM_ERR("failed to parse cseq number - not an integer\n"); goto error; } return new_leg; error: return 0; } dlg_leg_t* b2b_add_leg(b2b_dlg_t* dlg, struct sip_msg* msg, str* to_tag) { dlg_leg_t* new_leg; new_leg = b2b_new_leg(msg, to_tag, SHM_MEM_TYPE); if(new_leg == NULL) { LM_ERR("Failed to create new leg\n"); return 0; } if(dlg->legs != NULL) { new_leg->next = dlg->legs; new_leg->id = dlg->legs->id + 1; } dlg->legs = new_leg; return new_leg; } int b2b_send_req(b2b_dlg_t* dlg, enum b2b_entity_type etype, dlg_leg_t* leg, str* method, str* extra_headers, str* body) { dlg_t* td; int result; if(!dlg->callid.s || !dlg->callid.len) return -1; if(!dlg->callid.s || !dlg->callid.len) return -1; LM_DBG("start type=%d\n", etype); if(etype== B2B_SERVER) td = b2b_server_build_dlg(dlg); else td = b2b_client_build_dlg(dlg, leg); if(td == NULL) { LM_ERR("Failed to create dialog info structure\n"); return -1; } if(method->len == ACK_LEN && strncmp(method->s, ACK, ACK_LEN)==0) { td->loc_seq.value = dlg->last_invite_cseq; if(etype == B2B_SERVER) dlg->cseq[CALLEE_LEG]--; else dlg->cseq[CALLER_LEG]--; } /* send request */ result= tmb.t_request_within (method, /* method*/ extra_headers, /* extra headers*/ NULL, /* body*/ td, /* dialog structure*/ NULL, /* callback function*/ NULL, /* callback parameter*/ NULL); free_tm_dlg(td); return result; } static struct sip_msg dummy_msg; static int build_extra_headers_from_msg(str buf, str *extra_hdr, str *new_hdrs, str *body) { struct sip_msg req; struct hdr_field *hdr; int len; char *p; memset( &req, 0, sizeof(req) ); req.buf = buf.s; req.len = buf.len; if (parse_msg(buf.s, buf.len, &req)!=0) { LM_CRIT("BUG - buffer parsing failed!"); return -1; } /* parse all headers */ if (parse_headers(&req, HDR_EOH_F, 0 )<0) { LM_ERR("parse_headers failed\n"); goto error; } /* first calculate the total len */ len = extra_hdr->len; for( hdr=req.headers; hdr ; hdr=hdr->next ) { /* skip all headers that are by default added by build_uac_req() * We want to propagete only custom headers !!! */ switch(hdr->type){ case HDR_VIA_T: case HDR_TO_T: case HDR_FROM_T: case HDR_ROUTE_T: case HDR_CSEQ_T: case HDR_CALLID_T: case HDR_MAXFORWARDS_T: case HDR_CONTENTLENGTH_T: case HDR_USERAGENT_T: break; default: /* count the header (includes the CRLF) */ len += hdr->len; } } /* allocate */ new_hdrs->len = len; new_hdrs->s = pkg_malloc( len ); if (new_hdrs->s==NULL) { LM_ERR("failed to allocate pkg mem (needed %d)\n",len); goto error; } /* copy headers */ for( p=new_hdrs->s,hdr=req.headers; hdr ; hdr=hdr->next ) { switch(hdr->type){ case HDR_VIA_T: case HDR_TO_T: case HDR_FROM_T: case HDR_ROUTE_T: case HDR_CSEQ_T: case HDR_CALLID_T: case HDR_MAXFORWARDS_T: case HDR_CONTENTLENGTH_T: case HDR_USERAGENT_T: break; default: /* copy the header (includes the CRLF) */ memcpy( p, hdr->name.s , hdr->len); p += hdr->len; } } /* and extra */ memcpy( p, extra_hdr->s, extra_hdr->len); if (get_body( &req, body)<0) { LM_ERR("failed to extract body\n"); pkg_free( new_hdrs->s ); goto error; } free_sip_msg(&req); return 0; error: free_sip_msg(&req); new_hdrs->s = body->s = NULL; new_hdrs->len = body->len = 0; return -1; } void b2b_tm_cback(struct cell *t, b2b_table htable, struct tmcb_params *ps) { struct sip_msg * msg; str* b2b_key; unsigned int hash_index, local_index; b2b_notify_t b2b_cback; b2b_dlg_t *dlg, *previous_dlg; b2b_dlg_t *aux_dlg,*new_dlg; str param= {NULL, 0}; int statuscode = 0; dlg_leg_t* leg; struct to_body* pto; str to_tag, callid, from_tag; str extra_headers = {NULL, 0}; str body = {NULL, 0}; struct hdr_field* hdr; unsigned int method_id = 0; struct cseq_body cb; struct hdr_field cseq; enum b2b_entity_type etype=(htable==server_htable?B2B_SERVER:B2B_CLIENT); int dlg_based_search = 0; struct hdr_field callid_hdr, from_hdr, to_hdr; struct to_body to_hdr_parsed, from_hdr_parsed; int dlg_state = 0; struct uac_credential* crd; struct authenticate_body *auth = NULL; static struct authenticate_nc_cnonce auth_nc_cnonce; HASHHEX response; str *new_hdr; char status_buf[INT2STR_MAX_LEN]; to_hdr_parsed.param_lst = from_hdr_parsed.param_lst = NULL; if(ps == NULL || ps->rpl == NULL) { LM_ERR("wrong ps parameter\n"); return; } if( ps->param== NULL || *ps->param== NULL ) { LM_ERR("null callback parameter\n"); return; } statuscode = ps->code; LM_DBG("tm [%p] notification cb for [%d] reply\n", t, statuscode); if(statuscode == 100) return; msg = ps->rpl; b2b_key = (str*)*ps->param; if(b2b_parse_key(b2b_key, &hash_index, &local_index)< 0) { LM_ERR("Failed to parse b2b logic key [%.*s]\n",b2b_key->len,b2b_key->s); return; } if(parse_method(t->method.s, t->method.s + t->method.len, &method_id) < 0) { LM_ERR("Failed to parse method [%.*s\n]\n", t->method.len, t->method.s); return; } if(msg && msg!= FAKED_REPLY) { /* extract the method */ if(parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("Failed to parse headers\n"); return; } if( msg->cseq==NULL || msg->cseq->body.s==NULL) { LM_ERR("failed to parse cseq header\n"); return; } if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("no callid header found\n"); return; } /* examine the from header */ if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); return; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); return; } } if (msg->to == NULL) { LM_ERR("cannot find 'to' header!\n"); return; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("'To' header COULD NOT parsed\n"); return; } to_tag = pto->tag_value; callid = msg->callid->body; from_tag = ((struct to_body*)msg->from->parsed)->tag_value; } else if (ps->req) { from_tag = ((struct to_body*)ps->req->from->parsed)->tag_value; to_tag = ((struct to_body*)ps->req->to->parsed)->tag_value; callid = ps->req->callid->body; } else { to_tag.s = NULL; to_tag.len = 0; from_tag.s = NULL; from_tag.len = 0; callid.s = NULL; callid.len = 0; } if(callid.s) dlg_based_search = 1; lock_get(&htable[hash_index].lock); if(!dlg_based_search) { dlg = b2b_search_htable(htable, hash_index, local_index); } else { dlg = b2b_search_htable_dlg(htable, hash_index, local_index, &from_tag, (method_id==METHOD_CANCEL)?NULL:&to_tag, &callid); } if(dlg== NULL) { if(callid.s) { /* This is a special case of paralel forking: * - this is the cancelled branch of a paralel call fork * and the entity was deleted already. */ /* FIXME: we may revisit the logic of paralel forking and * properly ignore this kind of callbacks. */ if (method_id==METHOD_INVITE && statuscode==487) LM_DBG("No dialog found reply %d for method %.*s, [%.*s]\n", statuscode, msg==FAKED_REPLY?get_cseq(ps->req)->method.len: get_cseq(msg)->method.len, msg==FAKED_REPLY?get_cseq(ps->req)->method.s: get_cseq(msg)->method.s, callid.len, callid.s); else LM_ERR("No dialog found reply %d for method %.*s, [%.*s]\n", statuscode, msg==FAKED_REPLY?get_cseq(ps->req)->method.len: get_cseq(msg)->method.len, msg==FAKED_REPLY?get_cseq(ps->req)->method.s: get_cseq(msg)->method.s, callid.len, callid.s); } lock_release(&htable[hash_index].lock); return; } previous_dlg = dlg; while (dlg && method_id==METHOD_INVITE && dlg->uac_tran != t) { LM_DBG("Got unmatching dlg [%p] dlg->uac_tran=[%p] for " "transaction [%p]\n", dlg, dlg->uac_tran, t); if(dlg_based_search) dlg = b2b_search_htable_next_dlg( previous_dlg, htable, hash_index, local_index, &from_tag, (method_id==METHOD_CANCEL)?NULL:&to_tag, &callid); else dlg = b2b_search_htable_next( previous_dlg, htable, hash_index, local_index); if (dlg) { previous_dlg = dlg; } else { dlg = previous_dlg; /* if the transaction is no longer saved or is not the same as * the one that the reply belongs to => exit*/ LM_DBG("I don't care anymore about this transaction for dlg [%p]" " last_method=%d method_id=%d t=[%p] dlg->uac_tran=[%p]\n", dlg, dlg->last_method, method_id, t, dlg->uac_tran); /* if confirmed - send ACK */ if(statuscode>=200 && statuscode<300 && (dlg->state==B2B_ESTABLISHED || dlg->state==B2B_TERMINATED)) { leg = b2b_new_leg(msg, &to_tag, PKG_MEM_TYPE); if(leg == NULL) { LM_ERR("Failed to add dialog leg\n"); lock_release(&htable[hash_index].lock); return; } if(dlg->callid.s==0 || dlg->callid.len==0) dlg->callid = msg->callid->body; if(b2b_send_req(dlg, etype, leg, &ack, 0, (dlg->ack_sdp.s?&dlg->ack_sdp:0)) < 0) { LM_ERR("Failed to send ACK request\n"); } pkg_free(leg); } lock_release(&htable[hash_index].lock); return; } } b2b_cback = dlg->b2b_cback; if(dlg->param.s) { param.s = (char*)pkg_malloc(dlg->param.len); if(param.s == NULL) { LM_ERR("No more private memory\n"); lock_release(&htable[hash_index].lock); return; } memcpy(param.s, dlg->param.s, dlg->param.len); param.len = dlg->param.len; } LM_DBG("Received reply [%d] for dialog [%p], method [%.*s]\n", statuscode, dlg, t->method.len, t->method.s); if(statuscode >= 300) { if(dlg->uac_tran == t ) { tmb.unref_cell(dlg->uac_tran); dlg->uac_tran = NULL; LM_DBG("dlg=[%p], uac_tran=NULL\n", dlg); } if(method_id != METHOD_INVITE) { current_dlg = dlg; dlg_state = dlg->state; lock_release(&htable[hash_index].lock); if(msg == FAKED_REPLY) goto dummy_reply; goto done1; } if(dlg->state >= B2B_CONFIRMED) { /* if reinvite */ LM_DBG("Non final negative reply for reINVITE\n"); current_dlg = dlg; dlg_state = dlg->state; lock_release(&htable[hash_index].lock); if(msg == FAKED_REPLY) goto dummy_reply; goto done1; } else { b2b_delete_legs(&dlg->legs); if(msg && msg!= FAKED_REPLY) { if (str2int( &(get_cseq(msg)->number), &dlg->cseq[CALLER_LEG])!=0 ) { LM_ERR("failed to parse cseq number - not an integer\n"); } else { dlg->last_invite_cseq = dlg->cseq[CALLER_LEG]; } } switch(statuscode) { case 401: if (0 == parse_www_authenticate_header(msg)) auth = get_www_authenticate(msg); break; case 407: if (0 == parse_proxy_authenticate_header(msg)) auth = get_proxy_authenticate(msg); break; } if(uac_auth_loaded && auth && dlg->state == B2B_NEW) { crd = uac_auth_api._lookup_realm( &auth->realm ); if(crd) { memset(&auth_nc_cnonce, 0, sizeof(struct authenticate_nc_cnonce)); uac_auth_api._do_uac_auth(&t->method, &t->uac[0].uri, crd, auth, &auth_nc_cnonce, response); new_hdr = uac_auth_api._build_authorization_hdr(statuscode, &t->uac[0].uri, crd, auth, &auth_nc_cnonce, response); if (!new_hdr) { LM_ERR("failed to build auth hdr\n"); dlg->state = B2B_TERMINATED; lock_release(&htable[hash_index].lock); goto error; } LM_DBG("auth is [%.*s]\n", new_hdr->len, new_hdr->s); if (build_extra_headers_from_msg(t->uac[0].request.buffer, new_hdr, &extra_headers, &body) < 0 ) { LM_ERR("failed to build extra msgs after auth\n"); dlg->state = B2B_TERMINATED; lock_release(&htable[hash_index].lock); goto error; } LM_DBG("extra is [%.*s]\n", extra_headers.len, extra_headers.s); pkg_free(new_hdr->s); new_hdr->s = NULL; new_hdr->len = 0; b2b_send_indlg_req(dlg, B2B_CLIENT, b2b_key, &t->method, &extra_headers, &body, 0); pkg_free(extra_headers.s); dlg->state = B2B_NEW_AUTH; lock_release(&htable[hash_index].lock); /* run the b2b route */ if(reply_routeid > 0) { msg->flags = t->uac[0].br_flags; run_top_route(rlist[reply_routeid].a, msg); b2b_apply_lumps(msg); } goto b2b_route; } else dlg->state = B2B_TERMINATED; } else { dlg->state = B2B_TERMINATED; } } current_dlg = dlg; dlg_state = dlg->state; UPDATE_DBFLAG(dlg); lock_release(&htable[hash_index].lock); if(msg == FAKED_REPLY) { dummy_reply: memset(&dummy_msg, 0, sizeof(struct sip_msg)); dummy_msg.id = 1; dummy_msg.first_line.type = SIP_REPLY; dummy_msg.first_line.u.reply.statuscode = statuscode; dummy_msg.first_line.u.reply.status.s = int2bstr( statuscode, status_buf, &dummy_msg.first_line.u.reply.status.len); dummy_msg.first_line.u.reply.reason.s = "Timeout"; dummy_msg.first_line.u.reply.reason.len = 7; memset(&cb, 0, sizeof(struct cseq_body)); memset(&cseq, 0, sizeof(struct hdr_field)); cb.method = t->method; dummy_msg.rcv.bind_address = t->uac[0].request.dst.send_sock; dummy_msg.rcv.proto = dummy_msg.rcv.bind_address->proto; dummy_msg.rcv.src_port = dummy_msg.rcv.dst_port = dummy_msg.rcv.bind_address->port_no; dummy_msg.rcv.src_ip = dummy_msg.rcv.dst_ip = dummy_msg.rcv.bind_address->address; from_hdr.next = from_hdr.sibling = NULL; to_hdr.next = to_hdr.sibling = NULL; callid_hdr.next = callid_hdr.sibling = NULL; from_hdr.type = HDR_FROM_T; to_hdr.type = HDR_TO_T; callid_hdr.type = HDR_CALLID_T; parse_to(t->to.s+4, t->to.s+t->to.len, &to_hdr_parsed); parse_to(t->from.s+6, t->from.s+t->from.len, &from_hdr_parsed); from_hdr.parsed = (struct hdr_field*)(void*)&from_hdr_parsed; to_hdr.parsed = (struct hdr_field*)(void*)&to_hdr_parsed; from_hdr.body = t->from; from_hdr.body.len -=6; from_hdr.body.s += 6; to_hdr.body = t->to; to_hdr.body.len -=4; to_hdr.body.s += 4; from_hdr.len = t->from.len; to_hdr.len = t->to.len; from_hdr.name.s = t->from.s; from_hdr.name.len = 4; to_hdr.name.s = t->to.s; to_hdr.name.len = 2; callid_hdr.name.s = t->callid.s; callid_hdr.name.len = 7; callid_hdr.body.s = t->callid.s + 9; callid_hdr.body.len = t->callid.len - 9; trim_r(callid_hdr.body); /* callid in T contains also the CRLF */ callid_hdr.len = t->callid.len; dummy_msg.callid = &callid_hdr; dummy_msg.from = &from_hdr; dummy_msg.to = &to_hdr; dummy_msg.parsed_flag = HDR_EOH_F; cseq.parsed = &cb; dummy_msg.cseq = &cseq; /* simulate some "body" and "headers" - we fake the body with the "from" buffer */ dummy_msg.buf = t->from.s; dummy_msg.len = t->from.len; dummy_msg.eoh = dummy_msg.unparsed = t->from.s+t->from.len; dummy_msg.headers = dummy_msg.last_header = &from_hdr; msg = &dummy_msg; } goto done1; } else { /* if provisional or 200OK reply */ if(msg == FAKED_REPLY) { goto error; } if(method_id != METHOD_INVITE) { goto done; } if(dlg->uac_tran && statuscode>= 200) { tmb.unref_cell(dlg->uac_tran); dlg->uac_tran = NULL; LM_DBG("dlg=[%p], uac_tran=NULL\n", dlg); } /* if 200 OK received for INVITE and the dialog already ended -> * send ACK and terminate the call (it won't have a cback function) */ if(dlg->state == B2B_TERMINATED) { if(statuscode >= 200) { LM_DBG("Received 200 OK after the call was terminated %.*s\n", b2b_key->len, b2b_key->s); leg = b2b_new_leg(msg, &to_tag, PKG_MEM_TYPE); if(leg == NULL) { LM_ERR("Failed to add dialog leg\n"); goto error; } if(dlg->callid.s==0 || dlg->callid.len==0) dlg->callid = msg->callid->body; if(b2b_send_req(dlg, etype, leg, &ack, 0, 0) < 0) { LM_ERR("Failed to send ACK request\n"); } if(b2b_send_req(dlg, etype, leg, &bye, 0, 0) < 0) { LM_ERR("Failed to send BYE request\n"); } pkg_free(leg); } goto done; } if(dlg->legs == NULL && (dlg->state == B2B_NEW || dlg->state == B2B_NEW_AUTH || dlg->state == B2B_EARLY)) { new_dlg = b2b_new_dlg(msg, &dlg->contact[CALLER_LEG], dlg, &dlg->param); if(new_dlg == NULL) { LM_ERR("Failed to create b2b dialog structure\n"); goto error; } LM_DBG("Created new dialog structure %p\n", new_dlg); new_dlg->id = dlg->id; new_dlg->state = dlg->state; new_dlg->b2b_cback = dlg->b2b_cback; new_dlg->uac_tran = dlg->uac_tran; new_dlg->uas_tran = dlg->uas_tran; new_dlg->next = dlg->next; new_dlg->prev = dlg->prev; new_dlg->add_dlginfo = dlg->add_dlginfo; new_dlg->last_method = dlg->last_method; // dlg = b2b_search_htable(htable, hash_index, local_index); if(dlg->prev) dlg->prev->next = new_dlg; else htable[hash_index].first = new_dlg; if(dlg->next) dlg->next->prev = new_dlg; dlg->next= dlg->prev = NULL; b2b_delete_legs(&dlg->legs); shm_free(dlg); dlg = new_dlg; UPDATE_DBFLAG(dlg); } if(dlg->state == B2B_NEW || dlg->state == B2B_NEW_AUTH || dlg->state == B2B_EARLY) { if(statuscode < 200) { dlg->state = B2B_EARLY; /* search if an existing or a new leg */ leg = b2b_find_leg(dlg, to_tag); if(leg) { LM_DBG("Found existing leg - Nothing to update\n"); } else { leg = b2b_add_leg(dlg, msg, &to_tag); if(leg == NULL) { LM_ERR("Failed to add dialog leg\n"); goto error; } UPDATE_DBFLAG(dlg); } /* PRACK handling */ /* if the provisional reply contains a * Require: 100rel header -> send PRACK */ hdr = get_header_by_static_name( msg, "Require"); while(hdr) { LM_DBG("Found require hdr\n"); if ( (hdr->body.len == 6 && strncmp(hdr->body.s, "100rel", 6)==0) || (hdr->body.len == 8 && strncmp(hdr->body.s, "100rel\r\n", 8)==0) ) { LM_DBG("Found 100rel header\n"); break; } hdr = hdr->sibling; } if(hdr) { str method={"PRACK", 5}; str extra_headers; char buf[128]; str rseq, cseq; hdr = get_header_by_static_name( msg, "RSeq"); if(!hdr) { LM_ERR("RSeq header not found\n"); goto error; } rseq = hdr->body; cseq = msg->cseq->body; trim_trailing(&rseq); trim_trailing(&cseq); sprintf(buf, "RAck: %.*s %.*s\r\n", rseq.len, rseq.s, cseq.len, cseq.s); extra_headers.s = buf; extra_headers.len = strlen(buf); if(dlg->callid.s==0 || dlg->callid.len==0) dlg->callid = msg->callid->body; if(b2b_send_req(dlg, etype, leg, &method, &extra_headers, 0) < 0) { LM_ERR("Failed to send PRACK\n"); } } goto done; } else /* a final success response */ { LM_DBG("A final response\n"); /* if the dialog was already confirmed */ if(dlg->state == B2B_CONFIRMED) { LM_DBG("The state is already confirmed\n"); leg = b2b_new_leg(msg, &to_tag, PKG_MEM_TYPE); if(leg == NULL) { LM_ERR("Failed to add dialog leg\n"); goto error; } if(dlg->callid.s==0 || dlg->callid.len==0) dlg->callid = msg->callid->body; /* send an ACK followed by BYE */ if(b2b_send_req(dlg, etype, dlg->legs, &ack, 0, dlg->ack_sdp.s?&dlg->ack_sdp:0) < 0) { LM_ERR("Failed to send ACK request\n"); } if(b2b_send_req(dlg, etype, dlg->legs, &bye, 0, 0) < 0) { LM_ERR("Failed to send BYE request\n"); } lock_release(&htable[hash_index].lock); return; } else { b2b_dlginfo_t dlginfo; b2b_add_dlginfo_t add_infof= dlg->add_dlginfo; /* delete all and add the confirmed leg */ b2b_delete_legs(&dlg->legs); leg = b2b_add_leg(dlg, msg, &to_tag); if(leg == NULL) { LM_ERR("Failed to add dialog leg\n"); goto error; } dlg->tag[CALLEE_LEG] = leg->tag; dlginfo.fromtag = to_tag; dlginfo.callid = dlg->callid; dlginfo.totag = dlg->tag[CALLER_LEG]; dlg->state = B2B_CONFIRMED; if(b2be_db_mode == WRITE_THROUGH) { b2be_db_insert(dlg, etype); } current_dlg = dlg; dlg_state = dlg->state; lock_release(&htable[hash_index].lock); if(add_infof && add_infof(param.s?¶m:0, b2b_key, etype,&dlginfo)< 0) { LM_ERR("Failed to add dialoginfo\n"); goto error1; } UPDATE_DBFLAG(dlg); goto done1; } } } LM_DBG("DLG state = %d\n", dlg->state); if(dlg->state== B2B_MODIFIED && statuscode >= 200 && statuscode <300) { LM_DBG("switched the state CONFIRMED [%p]\n", dlg); dlg->state = B2B_CONFIRMED; } else if(dlg->state == B2B_CONFIRMED) { LM_DBG("Retrasmission [%p]\n", dlg); goto error; } } done: if(dlg) { current_dlg = dlg; dlg_state = dlg->state; } lock_release(&htable[hash_index].lock); /* I have to inform the logic that a reply was received */ done1: /* run the b2b route */ if(reply_routeid > 0) { msg->flags = t->uac[0].br_flags; run_top_route(rlist[reply_routeid].a, msg); if (msg != FAKED_REPLY) b2b_apply_lumps(msg); } b2b_cback(msg, b2b_key, B2B_REPLY, param.s?¶m:0); if(param.s) { pkg_free(param.s); param.s = NULL; } if (msg == &dummy_msg) { free_lump_list(msg->add_rm); free_lump_list(msg->body_lumps); } b2b_route: current_dlg = 0; if(b2be_db_mode == WRITE_THROUGH && dlg_state>B2B_CONFIRMED) { lock_get(&htable[hash_index].lock); for(aux_dlg = htable[hash_index].first; aux_dlg; aux_dlg = aux_dlg->next) { if(aux_dlg == dlg) break; } if(!aux_dlg) { lock_release(&htable[hash_index].lock); return; } b2be_db_update(dlg, etype); lock_release(&htable[hash_index].lock); } if (to_hdr_parsed.param_lst) { /* message was built on the fly from T * free side effects of parsing to hdr */ free_to_params(&to_hdr_parsed); } if (from_hdr_parsed.param_lst) { /* message was built on the fly from T * free side effects of parsing from hdr */ free_to_params(&from_hdr_parsed); } return; error: lock_release(&htable[hash_index].lock); error1: if(param.s) pkg_free(param.s); if (msg == &dummy_msg) { free_lump_list(msg->add_rm); free_lump_list(msg->body_lumps); } } static inline int is_CT_present(struct hdr_field* headers) { struct hdr_field* hf; for (hf=headers; hf; hf=hf->next) { if (hf->type == HDR_CONTENTTYPE_T) { return 1; } } return 0; } int b2breq_complete_ehdr(str* extra_headers, str* ehdr_out, str* body, str* local_contact) { str ehdr= {NULL,0}; static char buf[BUF_LEN]; static struct sip_msg foo_msg; if((extra_headers?extra_headers->len:0) + 14 + local_contact->len > BUF_LEN) { LM_ERR("Buffer too small\n"); return -1; } ehdr.s = buf; if(extra_headers && extra_headers->s && extra_headers->len) { memcpy(ehdr.s, extra_headers->s, extra_headers->len); ehdr.len = extra_headers->len; } ehdr.len += sprintf(ehdr.s+ ehdr.len, "Contact: <%.*s>\r\n", local_contact->len, local_contact->s); /* if not present and body present add content type */ if(body) { /* build a fake sip_msg to parse the headers sip-wisely */ memset(&foo_msg, 0, sizeof(struct sip_msg)); foo_msg.len = ehdr.len; foo_msg.buf = foo_msg.unparsed = ehdr.s; if (parse_headers( &foo_msg, HDR_EOH_F, 0) == -1) { LM_ERR("Failed to parse headers\n"); return -1; } if (!is_CT_present(foo_msg.headers)) { /* add content type header */ if(ehdr.len + 32 > BUF_LEN) { LM_ERR("Buffer too small, can not add Content-Type header\n"); return -1; } memcpy(ehdr.s+ ehdr.len, "Content-Type: application/sdp\r\n", 31); ehdr.len += 31; ehdr.s[ehdr.len]= '\0'; } if (foo_msg.headers) free_hdr_field_lst(foo_msg.headers); } *ehdr_out = ehdr; return 0; } int b2b_apply_lumps(struct sip_msg* msg) { str obuf; struct sip_msg tmp; str body; /* faked reply */ if (msg==NULL || msg == FAKED_REPLY || msg==&dummy_msg) return 0; if(!msg->body_lumps && !msg->add_rm) return 0; if (msg->first_line.type==SIP_REQUEST) obuf.s = build_req_buf_from_sip_req(msg, (unsigned int*)&obuf.len, msg->rcv.bind_address, msg->rcv.proto, MSG_TRANS_NOVIA_FLAG ); else obuf.s = build_res_buf_from_sip_res(msg, (unsigned int*)&obuf.len, msg->rcv.bind_address,0); if (!obuf.s) { LM_ERR("no more shm mem\n"); return -1; } /* temporary copy */ memcpy(&tmp, msg, sizeof(struct sip_msg)); /* reset dst uri and path vector to avoid freeing - restored later */ if(msg->dst_uri.s != NULL) { msg->dst_uri.s = NULL; msg->dst_uri.len = 0; } if(msg->path_vec.s != NULL) { msg->path_vec.s = NULL; msg->path_vec.len = 0; } /* free old msg structure */ free_sip_msg(msg); memset(msg, 0, sizeof(struct sip_msg)); /* restore msg fields */ msg->buf = tmp.buf; msg->id = tmp.id; msg->rcv = tmp.rcv; msg->set_global_address = tmp.set_global_address; msg->set_global_port = tmp.set_global_port; msg->flags = tmp.flags; msg->msg_flags = tmp.msg_flags; msg->hash_index = tmp.hash_index; msg->force_send_socket = tmp.force_send_socket; msg->dst_uri = tmp.dst_uri; msg->path_vec = tmp.path_vec; memcpy(msg->buf, obuf.s, obuf.len); msg->len = obuf.len; msg->buf[msg->len] = '\0'; /* free new buffer - copied in the static buffer from old struct sip_msg */ pkg_free(obuf.s); /* reparse the message */ if (parse_msg(msg->buf, msg->len, msg) != 0) LM_ERR("parse_msg failed\n"); /* if has body, check for SDP */ if (get_body(msg,&body) != 0 || body.len == 0) return 1; if (parse_sdp(msg) < 0) { LM_DBG("failed to parse SDP message\n"); return -1; } return 1; } opensips-2.2.2/modules/b2b_entities/dlg.h000066400000000000000000000144611300170765700203310ustar00rootroot00000000000000/* * back-to-back entities modules * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) * 2011-06-27 added authentication support (Ovidiu Sas) */ #ifndef _B2B_DLG_H_ #define _B2B_DLG_H_ #include "../../str.h" #include "../../lock_ops.h" #include "../tm/h_table.h" #include "../tm/dlg.h" #include "../dialog/dlg_load.h" #include "b2b_common.h" #define CALLER_LEG 0 #define CALLEE_LEG 1 #define B2B_REQUEST 0 #define B2B_REPLY 1 #define DLG_ESTABLISHED 1 #define B2B_MAX_KEY_SIZE (B2B_MAX_PREFIX_LEN+4+10+10+INT2STR_MAX_LEN) enum b2b_entity_type {B2B_SERVER=0, B2B_CLIENT, B2B_NONE}; typedef struct b2b_dlginfo { str callid; str fromtag; str totag; }b2b_dlginfo_t; typedef int (*b2b_notify_t)(struct sip_msg* , str* , int , void* ); typedef int (*b2b_add_dlginfo_t)(str* key, str* entity_key, int src, b2b_dlginfo_t* info); /* * Dialog state */ typedef enum b2b_state { B2B_UNDEFINED = 0, /* New dialog, no reply received yet */ B2B_NEW, /* New dialog, no reply received yet */ B2B_NEW_AUTH, /* New dialog with auth info, no reply received yet */ B2B_EARLY, /* Early dialog, provisional response received */ B2B_CONFIRMED, /* Confirmed dialog, 2xx received */ B2B_ESTABLISHED,/* Established dialog, sent or received ACK received */ B2B_MODIFIED, /* ReInvite inside dialog */ B2B_DESTROYED, /* Destroyed dialog */ B2B_TERMINATED, /* Terminated dialog */ B2B_LAST_STATE /* Just to know the number of states */ } b2b_state_t; typedef struct b2b_dlg_leg { int id; str tag; unsigned int cseq; str route_set; str contact; struct b2b_dlg_leg* next; }dlg_leg_t; #define NO_UPDATEDB_FLAG 0 #define UPDATEDB_FLAG 1 #define INSERTDB_FLAG 2 /** Definitions for structures used for storing dialogs */ typedef struct b2b_dlg { unsigned int id; b2b_state_t state; str ruri; str callid; str from_uri; str from_dname; str to_uri; str to_dname; str tag[2]; unsigned int cseq[2]; unsigned int last_invite_cseq; str route_set[2]; str contact[2]; enum request_method last_method; struct b2b_dlg *next; struct b2b_dlg *prev; b2b_notify_t b2b_cback; b2b_add_dlginfo_t add_dlginfo; str param; str ack_sdp; struct cell* uac_tran; struct cell* uas_tran; struct cell* update_tran; struct cell* cancel_tm_tran; dlg_leg_t* legs; struct socket_info* send_sock; unsigned int last_reply_code; int db_flag; }b2b_dlg_t; typedef struct client_info { str method; str from_uri; str from_dname; str req_uri; str dst_uri; str to_uri; str to_dname; str* extra_headers; str* body; str* from_tag; str local_contact; unsigned int cseq; struct socket_info* send_sock; struct usr_avp *avps; }client_info_t; typedef struct b2b_entry { b2b_dlg_t* first; gen_lock_t lock; int checked; }b2b_entry_t; typedef b2b_entry_t* b2b_table; typedef struct b2b_req_data { enum b2b_entity_type et; str* b2b_key; str* method; str* extra_headers; str* body; b2b_dlginfo_t* dlginfo; unsigned int no_cb; }b2b_req_data_t; typedef struct b2b_rpl_data { enum b2b_entity_type et; str* b2b_key; int method; int code; str* text; str* body; str* extra_headers; b2b_dlginfo_t* dlginfo; }b2b_rpl_data_t; /** Hash table declaration: for client and server dialogs */ b2b_table server_htable; b2b_table client_htable; void print_b2b_dlg(b2b_dlg_t *dlg); str* b2b_htable_insert(b2b_table table, b2b_dlg_t* dlg, int hash_index, int src, int reload); b2b_dlg_t* b2b_htable_search_safe(str callid, str to_tag, str from_tag); int b2b_parse_key(str* key, unsigned int* hash_index, unsigned int* local_index); str* b2b_generate_key(unsigned int hash_index, unsigned int local_index); b2b_dlg_t* b2b_dlg_copy(b2b_dlg_t* dlg); int init_b2b_htables(void); void destroy_b2b_htables(); b2b_dlg_t* b2b_new_dlg(struct sip_msg* msg, str* local_contact, b2b_dlg_t* init_dlg, str* param); int b2b_prescript_f(struct sip_msg *msg, void* param); typedef str* (*b2b_server_new_t) (struct sip_msg* , str* local_contact, b2b_notify_t , str* param); typedef str* (*b2b_client_new_t) (client_info_t* , b2b_notify_t b2b_cback, b2b_add_dlginfo_t add_dlginfo_f, str* param); int b2b_send_reply(b2b_rpl_data_t*); typedef int (*b2b_send_reply_t)(b2b_rpl_data_t*); typedef int (*b2b_send_request_t)(b2b_req_data_t*); int b2b_send_request(b2b_req_data_t*); void b2b_delete_record(b2b_dlg_t* dlg, b2b_table htable, unsigned int hash_index); typedef dlg_t* (*build_dlg_f)(b2b_dlg_t* dlg); str* b2b_key_copy_shm(str* b2b_key); void shm_free_param(void* param); void b2b_entity_delete(enum b2b_entity_type et, str* b2b_key, b2b_dlginfo_t* dlginfo, int db_del); typedef void (*b2b_entity_delete_t)(enum b2b_entity_type et, str* b2b_key, b2b_dlginfo_t* dlginfo, int db_del); b2b_dlg_t* b2b_search_htable(b2b_table table, unsigned int hash_index, unsigned int local_index); void b2b_tm_cback(struct cell* t, b2b_table htable, struct tmcb_params *ps); void print_b2b_entities(void); int b2breq_complete_ehdr(str* extra_headers, str* ehdr_out, str* body, str* contact); b2b_dlg_t* b2b_search_htable_dlg(b2b_table table, unsigned int hash_index, unsigned int local_index, str* to_tag, str* from_tag, str* callid); int b2b_apply_lumps(struct sip_msg* msg); typedef int (*b2b_apply_lumps_t)(struct sip_msg* msg); #endif opensips-2.2.2/modules/b2b_entities/doc/000077500000000000000000000000001300170765700201515ustar00rootroot00000000000000opensips-2.2.2/modules/b2b_entities/doc/b2b_entities.xml000066400000000000000000000027771300170765700232610ustar00rootroot00000000000000 %docentities; ]> B2B_ENTITIES &osipsname; Anca-Maria Vamanu &osips;
anca@opensips.org http://opensips.org
Anca-Maria Vamanu
anca@opensips.org
Ovidiu Sas
osas@voipembedded.com
2009 Anca-Maria Vamanu $Revision: 8103 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/b2b_entities/doc/b2b_entities_admin.xml000066400000000000000000000173351300170765700244250ustar00rootroot00000000000000 &adminguide;
Overview The B2BUA implementation in OpenSIPS is separated in two layers: a lower one(coded in this module)- which implements the basic functions of a UAS and UAC a upper one - which represents the logic engine of B2BUA, responsible of actually implementing the B2BUA services using the functions offered by the low level. This module stores records corresponding to the dialogs in which the B2BUA is involved. It exports an API to be called from other modules which offers functions for creating a new dialog record, for sending requests or replies in one dialog and will also notify the upper level module when a request or reply is received inside one stored dialog. The records are separated in two types: b2b server entities and b2b client entities depending on the mode they are created. An entity created for a received initial message will be a server entity, while a entity that will send an initial request(create a new dialog) will be a b2b client entity. The name corresponds to the behavior in the first transaction - if UAS - server entity and if UAC - client entity. This module does not implement a B2BUA alone, but needs a B2B logic implementing module. The module is able to respond to authentication challanges if the uac_auth module is loaded first. The list of credentials for b2b authentication is also provided by the uac_auth module.
Dependencies
&osips; Modules tm a db module uac_auth (mandatory if authentication is required)
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters
<varname>server_hsize</varname> (int) The size of the hash table that stores the b2b server entities. It is the 2 logarithmic value of the real size. Default value is 9 (512 records). Set <varname>server_hsize</varname> parameter ... modparam("b2b_entities", "server_hsize", 10) ...
<varname>client_hsize</varname> (int) The size of the hash table that stores the b2b client entities. It is the 2 logarithmic value of the real size. Default value is 9 (512 records). Set <varname>client_hsize</varname> parameter ... modparam("b2b_entities", "client_hsize", 10) ...
<varname>script_req_route</varname> (str) The name of the b2b script route that will be called when B2B requests are received. Set <varname>script_req_route</varname> parameter ... modparam("b2b_entities", "script_req_route", "b2b_request") ...
<varname>script_reply_route</varname> (str) The name of the b2b script route that will be called when B2B replies are received. Set <varname>script_repl_route</varname> parameter ... modparam("b2b_entities", "script_reply_route", "b2b_reply") ...
<varname>db_url</varname> (str) Database URL. It is not compulsory, if not set data is not stored in database. Set <varname>db_url</varname> parameter ... modparam("b2b_entities", "db_url", "mysql://opensips:opensipsrw@127.0.0.1/opensips") ...
<varname>update_period</varname> (int) The time interval at which to update the info in database. Default value is 100. Set <varname>update_period</varname> parameter ... modparam("b2b_entities", "update_period", 60) ...
<varname>b2b_key_prefix</varname> (string) The string to use when generating the key ( it is inserted in the SIP messages as callid or to tag. It is useful to set this prefix if you use more instances of opensips B2BUA cascaded in the same architecture. Sometimes opensips B2BUA looks at the callid or totag to see if it has the format it uses to determine if the request was sent by it. Default value is B2B. Set <varname>b2b_key_prefix</varname> parameter ... modparam("b2b_entities", "b2b_key_prefix", "B2B1") ...
<varname>db_mode</varname> (int) The B2B modules have support for the 3 type of database storage NO DB STORAGE - set this parameter to 0 WRITE THROUGH (synchronous write in database) - set this parameter to 1 WRITE BACK (update in db from time to time) - set this parameter to 2 Default value is 2 (WRITE BACK). Set <varname>db_mode</varname> parameter ... modparam("b2b_entities", "db_mode", 1) ...
<varname>db_table</varname> (str) The name of the table that will be used for storing B2B entities Default value is b2b_entities Set <varname>db_table</varname> parameter ... modparam("b2b_entities", "db_table", "some table name") ...
<varname>replication_mode</varname> (int) Controls if the callid should be the same in more instances running at the same time. 0 will lead to generating of different callid's per instance 1 will lead to generating of the same callid's in more instances Default value is 0 Set <varname>replication_mode</varname> parameter ... modparam("b2b_entities", "replication_mode", 1) ...
Exported Functions The module does not export functions to be used in configuration script.
opensips-2.2.2/modules/b2b_entities/doc/b2b_entities_devel.xml000066400000000000000000000166641300170765700244400ustar00rootroot00000000000000 &develguide; The module provides an API that can be used from other &osips; modules. The API offers the functions for creating and handing dialogs. A dialog can be created on a receipt initial message, and this will correspond to a b2b server entity, or initiated by the server and in this case a client entity will be created in b2b_entities module.
<function moreinfo="none">b2b_load_api(b2b_api_t* api)</function> This function binds the b2b_entities modules and fills the structure the exported functions that will be described in detail. <function>b2b_api_t</function> structure ... typedef struct b2b_api { b2b_server_new_t server_new; b2b_client_new_t client_new; b2b_send_request_t send_request; b2b_send_reply_t send_reply; b2b_entity_delete_t entity_delete; b2b_restore_linfo_t restore_logic_info; b2b_update_b2bl_param_t update_b2bl_param; }b2b_api_t; ...
<function moreinfo="none">server_new</function> Field type: ... typedef str* (*b2b_server_new_t) (struct sip_msg* ,b2b_notify_t , void* param); ... This function asks the b2b_entities modules to create a new server entity record. The internal processing actually extracts the dialog information from the message and constructs a record that will be stored in a hash table. The second parameters is a pointer to a function that the b2b_entities module will call when a event will come for that dialog (a request or reply). The third parameter is a pointer to a value that will be stored and given as a parameter when the notify function will be called(it has to be allocated in shared memory). The return value is an identifier for the record that will be mentioned when calling other functions that represent actions in the dialog(send request, send reply). The notify function has the following prototype: ... typedef int (*b2b_notify_t)(struct sip_msg* msg, str* id, int type, void* param); ... This function is called when a request or reply is received for a dialog handled by b2b_entities. The first parameter is the message, the second is the identifier for the dialog, the third is a flag that says which is the type of the message(it has two possible values - B2B_REQUEST and B2B_REPLY). The last parameter is the parameter by the upper module when the entity was created.
<function moreinfo="none">client_new</function> Field type: ... typedef str* (*b2b_client_new_t) (client_info_t* , b2b_notify_t b2b_cback, b2b_add_dlginfo_t add_dlginfo_f, str* param); ... This function asks the b2b_entities modules to create a new client entity record and also create a new dialog by sending an initial message. The parameters are all the values needed for the initial request to which the notify function and parameter are added. The b2b_cback parameter is a pointer to the callback that must be called when an event happens(receiving a reply or request) in the dialog created with this function. The add_dlginfo_f parameter is also a function pointer to a callback that will be called when a final success response will be received for the created dialog. The callback will receive as parameter the complete dialog information for the record. It should be stored and used when calling send_request or send_reply functions. The return value is an identifier for the record that will be mentioned when calling other functions that represent actions in the dialog(send request, send reply).
<function moreinfo="none">send_request</function> Field type: ... typedef int (*b2b_send_request_t)(enum b2b_entity_type ,str* b2b_key, str* method, str* extra_headers, str* body, b2b_dlginfo_t*); ... This function asks the b2b_entities modules to send a request inside a b2b dialog identified by b2b_key. The first parameter is the entity type and can have two values: B2B_SERVER and B2B_CLIENT. The second is the identifier returned by the create function(server_new or client_new) and the next are the informations needed for the new request: method, extra_headers, body. The last parameter contains the dialog information - callid, to tag, from tag. These are needed to make a perfect match to of b2b_entities record for which a new request must be sent. The return value is 0 for success and a negative value for error.
<function moreinfo="none">send_reply</function> Field type: ... typedef int (*b2b_send_reply_t)(enum b2b_entity_type et, str* b2b_key, int code, str* text, str* body, str* extra_headers, b2b_dlginfo_t* dlginfo); ... This function asks the b2b_entities modules to send a reply inside a b2b dialog identified by b2b_key. The first parameter is the entity type and can have two values: B2B_SERVER and B2B_CLIENT. The second is the identifier returned by the create function(server_new or client_new) and the next are the informations needed for the new reply: code, text, body, extra_headers. The last parameter contains the dialog information used for matching the right record. The return value is 0 for success and a negative value for error.
<function moreinfo="none">entity_delete</function> Field type: ... typedef void (*b2b_entity_delete_t)(enum b2b_entity_type et, str* b2b_key, b2b_dlginfo_t* dlginfo); ... This function must be called by the upper level function to delete the records in b2b_entities. The records are not cleaned up by the b2b_entities module and the upper level module must take care to delete them.
<function moreinfo="none">restore_logic_info</function> Field type: ... typedef int (*b2b_restore_linfo_t)(enum b2b_entity_type type, str* key, b2b_notify_t cback); ... This function is used at startup when loading the data from the database to restore the pointer to the callback function.
<function moreinfo="none">update_b2bl_param</function> Field type: ... typedef int (*b2b_update_b2bl_param_t)(enum b2b_entity_type type, str* key, str* param); ... This function can be used to change the logic param stored for an entity ( useful in case an entity is moved between logic records).
opensips-2.2.2/modules/b2b_entities/server.c000066400000000000000000000100451300170765700210560ustar00rootroot00000000000000/* * back-to-back entities modules * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #include #include #include #include "../../parser/parse_rr.h" #include "../../parser/parse_content.h" #include "../../ut.h" #include "../../mem/shm_mem.h" #include "../presence/hash.h" #include "../tm/dlg.h" #include "server.h" #include "dlg.h" #include "b2b_entities.h" /** * Function to create a new server entity * msg: SIP message * b2b_cback: callback function to notify the logic about a change in dialog * param: the parameter that will be used when calling b2b_cback function * * Return value: the dialog key allocated in private memory * */ str* server_new(struct sip_msg* msg, str* local_contact, b2b_notify_t b2b_cback, str* param) { b2b_dlg_t* dlg; unsigned int hash_index; int ret; if(param && param->len > B2BL_MAX_KEY_LEN) { LM_ERR("parameter too long, received [%d], maximum [%d]\n", param->len, B2BL_MAX_KEY_LEN); return NULL; } /* create new entry in hash table */ dlg = b2b_new_dlg(msg, local_contact, 0, param); if( dlg == NULL ) { LM_ERR("failed to create new dialog structure entry\n"); return NULL; } hash_index = core_hash(&dlg->callid, &dlg->tag[CALLER_LEG], server_hsize); /* check if record does not exist already */ dlg->state = B2B_NEW; dlg->b2b_cback = b2b_cback; /* get the pointer to the tm transaction to store it the tuple record */ dlg->uas_tran = tmb.t_gett(); if(dlg->uas_tran == NULL || dlg->uas_tran == T_UNDEFINED) { ret = tmb.t_newtran(msg); if(ret < 1) { if(ret== 0) { LM_DBG("It is a retransmission, drop\n"); } else LM_DBG("Error when creating tm transaction\n"); goto error; } dlg->uas_tran = tmb.t_gett(); } tmb.ref_cell(dlg->uas_tran); tmb.t_setkr(REQ_FWDED); LM_DBG("new server entity[%p]: callid=[%.*s] tag=[%.*s] param=[%.*s] dlg->uas_tran=[%p]\n", dlg, dlg->callid.len, dlg->callid.s, dlg->tag[CALLER_LEG].len, dlg->tag[CALLER_LEG].s, dlg->param.len, dlg->param.s, dlg->uas_tran); /* add the record in hash table */ dlg->db_flag = INSERTDB_FLAG; return b2b_htable_insert(server_htable, dlg, hash_index, B2B_SERVER, 0); error: if(dlg) shm_free(dlg); return NULL; } dlg_t* b2b_server_build_dlg(b2b_dlg_t* dlg) { dlg_t* td =NULL; td = (dlg_t*)pkg_malloc(sizeof(dlg_t)); if(td == NULL) { ERR_MEM(PKG_MEM_STR); } memset(td, 0, sizeof(dlg_t)); td->loc_seq.value = dlg->cseq[CALLEE_LEG]; td->loc_seq.is_set = 1; dlg->cseq[CALLEE_LEG]++; td->id.call_id = dlg->callid; td->id.rem_tag = dlg->tag[CALLER_LEG]; td->id.loc_tag = dlg->tag[CALLEE_LEG]; td->rem_target = dlg->contact[CALLER_LEG]; td->loc_uri = dlg->to_uri; td->rem_uri = dlg->from_uri; td->loc_dname = dlg->to_dname; td->rem_dname = dlg->from_dname; if(dlg->route_set[CALLER_LEG].s && dlg->route_set[CALLER_LEG].len) { if(parse_rr_body(dlg->route_set[CALLER_LEG].s, dlg->route_set[CALLER_LEG].len, &td->route_set)< 0) { LM_ERR("failed to parse record route body\n"); goto error; } } td->state= DLG_CONFIRMED ; td->send_sock = dlg->send_sock; return td; error: if(td) pkg_free(td); return NULL; } void b2b_server_tm_cback( struct cell *t, int type, struct tmcb_params *ps) { b2b_tm_cback(t, server_htable, ps); } opensips-2.2.2/modules/b2b_entities/server.h000066400000000000000000000023251300170765700210650ustar00rootroot00000000000000/* * back-to-back entities module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #ifndef _B2B_SERVER_H_ #define _B2B_SERVER_H_ #include "../../parser/msg_parser.h" #include "dlg.h" str* server_new(struct sip_msg*, str* local_contact, b2b_notify_t, str*); dlg_t* b2b_server_build_dlg(b2b_dlg_t* dlg); void b2b_server_tm_cback( struct cell *t, int type, struct tmcb_params *ps); #endif opensips-2.2.2/modules/b2b_logic/000077500000000000000000000000001300170765700166555ustar00rootroot00000000000000opensips-2.2.2/modules/b2b_logic/Makefile000066400000000000000000000010641300170765700203160ustar00rootroot00000000000000# $Id: Makefile 806 2006-04-14 11:00:10Z bogdan_iancu $ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=b2b_logic.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/b2b_logic/README000066400000000000000000000447221300170765700175460ustar00rootroot00000000000000B2B_LOGIC Anca-Maria Vamanu OpenSIPS Edited by Anca-Maria Vamanu Edited by Ovidiu Sas Copyright © 2009 Anca-Maria Vamanu Copyright © 2010 VoIP Embedded, Inc. Revision History Revision $Revision: 8137 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. hash_size (int) 1.3.2. script_scenario (str) 1.3.3. extern_scenario (str) 1.3.4. cleanup_period (int) 1.3.5. custom_headers_regexp (str) 1.3.6. custom_headers (str) 1.3.7. use_init_sdp (int) 1.3.8. db_url (str) 1.3.9. update_period (int) 1.3.10. max_duration (int) 1.3.11. b2bl_from_spec_param (string) 1.3.12. server_address (str) 1.3.13. init_callid_hdr (str) 1.3.14. db_mode (int) 1.3.15. db_table (str) 1.3.16. b2bl_th_init_timeout (int) 1.4. Exported Functions 1.4.1. b2b_init_request 1.4.2. b2b_bridge_request(b2bl_key,entity_no) 1.5. Exported MI Functions 1.5.1. b2b_trigger_scenario 1.5.2. b2b_bridge 1.5.3. b2b_list 2. Developer Guide 2.1. b2b_logic_bind(b2bl_api_t* api) 2.2. init 2.3. bridge 2.4. bridge_extern 2.5. bridge_2calls 2.6. terminate_call 2.7. set_state 2.8. bridge_msg List of Examples 1.1. Set server_hsize parameter 1.2. Set script_scenario parameter 1.3. Set script_scenario parameter 1.4. Set cleanup_period parameter 1.5. Set parameter 1.6. Set parameter 1.7. Set parameter 1.8. Set db_url parameter 1.9. Set update_period parameter 1.10. Set max_duration parameter 1.11. Set b2bl_from_spec_param parameter 1.12. Set server_address parameter 1.13. Set init_callid_hdr parameter 1.14. Set db_mode parameter 1.15. Set db_table parameter 1.16. Set b2bl_th_init_timeout parameter 1.17. b2b_init_request usage 1.18. b2b_bridge_request usage 2.1. b2bl_api_t structure Chapter 1. Admin Guide 1.1. Overview The B2BUA implementation in OpenSIPS is separated in two layers: * a lower one(coded in b2b_entities module)- which implements the basic functions of a UAS and UAC * an upper one - which represents the logic engine of B2BUA, responsible of actually implementing the B2BUA services using the functions offered by the low level. This module is a B2BUA upper level implementation that can be used with b2b_entities module to have B2BUA that can be configured to provide some PBX services. The B2B services are coded in an XML scenario document. The b2b_logic module examines this document and uses the functions provided by the b2b_entities module to achieve the actions specified in the document and enable the service. A scenario can be instantiated in two ways: * from the script - at the receipt of a initial message * with a extern command (MI) command - the server will connect two end points in a session(Third Party Call Control). 1.2. Dependencies 1.2.1. OpenSIPS Modules * b2b_entities, a db module 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml2-dev 1.3. Exported Parameters 1.3.1. hash_size (int) The size of the hash table that stores the scenario instatiation entities. Default value is “9†(512 records). Example 1.1. Set server_hsize parameter ... modparam("b2b_logic", "hash_size", 10) ... 1.3.2. script_scenario (str) This parameter should be set with the path of a document that contains a scenario that can be instantiated from the script at the receipt of an initial message. This parameter can be set more than once. Example 1.2. Set script_scenario parameter ... modparam("b2b_logic", "script_scenario", "/usr/local/opensips/scripts/b2 b_prepaid.xml") ... 1.3.3. extern_scenario (str) This parameter should be set with the path of a document that contains a scenario that can be instantiated with an MI command. This parameter can be set more than once. Example 1.3. Set script_scenario parameter ... modparam("b2b_logic", "extern_scenario", "/usr/local/opensips/scripts/b2 b_marketing.xml") ... 1.3.4. cleanup_period (int) The time interval at which to search for an hanged b2b context. A scenario is considered expired if the duration of a session exceeds the lifetime specified in the scenario. At that moment, BYE is sent in all the dialogs from that context and the context is deleted. Default value is “100â€. Example 1.4. Set cleanup_period parameter ... modparam("b2b_logic", "cleanup_period", 60) ... 1.3.5. custom_headers_regexp (str) Regexp to search SIP header by names that should be passed from the dialog of one side to the other side. There are a number of headers that are passed by default. They are: * Content-Type * Supported * Allow * Proxy-Require * Session-Expires * Min-SE * Require * RSeq If you wish some other headers to be passed also you should define them by setting this parameter. It can be in forms like "regexp", "/regexp/" and "/regexp/flags". Meaning of the flags is as follows: * i - Case insensitive search. * e - Use extended regexp. Default value is “NULLâ€. Example 1.5. Set parameter ... modparam("b2b_logic", "custom_headers_regexp", "/^x-/i") ... 1.3.6. custom_headers (str) A list of SIP header names delimited by ';' that should be passed from the dialog of one side to the other side. There are a number of headers that are passed by default. They are: * Max-Forwards (it is decreased by 1) * Content-Type * Supported * Allow * Proxy-Require * Session-Expires * Min-SE * Require * RSeq If you wish some other headers to be passed also you should define them by setting this parameter. Default value is “NULLâ€. Example 1.6. Set parameter ... modparam("b2b_logic", "custom_headers", "User-Agent;Date") ... 1.3.7. use_init_sdp (int) This parameter modifies the behaviour of the B2BUA when bridging and a provisional media uri is set. For playing media while the callee answers (that is connecting the caller to a media server), the bridging with the callee must start by sending an Invite to it. The correct way is to send an Invite without a body in this case, but it has been observed that not many gateways support this. So, the solution is to use the sdp received in the first Invite from the caller and put it as the body for this invite. By setting this parameter, this behavior is enabled. You can also set use_init_sdp per scenario and overwrite this global value. Default value is “0â€. Example 1.7. Set parameter ... modparam("b2b_logic", "use_init_sdp", 1) ... 1.3.8. db_url (str) Database URL. Example 1.8. Set db_url parameter ... modparam("b2b_logic", "db_url", "mysql://opensips:opensipsrw@127.0.0.1/o pensips") ... 1.3.9. update_period (int) The time interval at which to update the info in database. Default value is “100â€. Example 1.9. Set update_period parameter ... modparam("b2b_logic", "update_period", 60) ... 1.3.10. max_duration (int) The maximum duration of a call. Default value is “12 * 3600 (12 hours)â€. If you set it to 0, there will be no limitation. Example 1.10. Set max_duration parameter ... modparam("b2b_logic", "max_duration", 7200) ... 1.3.11. b2bl_from_spec_param (string) The name of the pseudo variable for storing the new “From†header. The PV must be set before calling “b2b_init_requestâ€. Default value is “NULL†(disabled). Example 1.11. Set b2bl_from_spec_param parameter ... modparam("b2b_logic", "b2bl_from_spec_param", "$var(b2bl_from)") ... route{ ... # setting the From header $var(b2bl_from) = "\"Call ID\" "; ... b2b_init_request("top hiding"); ... } 1.3.12. server_address (str) The IP address of the machine that will be used as Contact in the generated messages. This is compulsory only when using external scenarios. For the script scenarios, if it is not set, it is constructed dynamically from the socket where the initiating request was received. This socket will be used to send all the requests, replies for that scenario instantiation. Example 1.12. Set server_address parameter ... modparam("b2b_logic", "server_address", "sip:sa@10.10.10.10:5060") ... 1.3.13. init_callid_hdr (str) The module offers the possibility to insert the original callid in a header in the generated Invites. If you want this, set this parameter to the name of the header in which to insert the original callid. Example 1.13. Set init_callid_hdr parameter ... modparam("b2b_logic", "init_callid_hdr", "Init-CallID") ... 1.3.14. db_mode (int) The B2B modules have support for the 3 type of database storage * NO DB STORAGE - set this parameter to 0 * WRITE THROUGH (synchronous write in database) - set this parameter to 1 * WRITE BACK (update in db from time to time) - set this parameter to 2 Default value is “2†(WRITE BACK). Example 1.14. Set db_mode parameter ... modparam("b2b_logic", "db_mode", 1) ... 1.3.15. db_table (str) Name of the database table to be used Default value is “b2b_logic†Example 1.15. Set db_table parameter ... modparam("b2b_logic", "db_table", "some_table_name") ... 1.3.16. b2bl_th_init_timeout (int) Call setup timeout for topology hiding scenario. Default value is “60†Example 1.16. Set b2bl_th_init_timeout parameter ... modparam("b2b_logic", "b2bl_th_init_timeout", 60) ... 1.4. Exported Functions 1.4.1. b2b_init_request This is the function that must be called by the script writer on an initial INVITE for which a B2B scenario must be instantiated. It is up to the script writer to decide which are the criteria to decide for which messages certain scenarios must be instantiated. The first parameter is the identifier for the scenario and possible flags. This is defined in the XML document as an attribute of the root node or "top hiding" for internal topology hiding scenario. It can be passed as "scenario" or "scenario_name/flags". Meaning of the flags is as follows: * t[nn] - Call setup timeout for topology hiding scenario. Example: t300. * a - Transparent authentication. In this mode b2b passes your 401 or 407 authentication request to destination server. * p - Preserve To: header. Then it can take at most 4 other parameters that represent the parameters for the xml scenario. The expected number of parameters is also specified as an attribute in the root node of the XML scenario. Note If you have a multi interface setup and want to chance the outbound interface, it is mandatory to use the "force_send_socket()" core function before passing control to b2b function. If you do not do it, the requests may be correctly routed, but the SIP pacakge may be invalid (as Contact, Via, etc). Example 1.17. b2b_init_request usage ... if(is_method("INVITE") && !has_totag() && prepaid_user()) b2b_init_request("prepaid", "sip:320@opensips.org:5070", "sip:321@opensips.org:5070")); ... 1.4.2. b2b_bridge_request(b2bl_key,entity_no) This function will bridge an initial INVITE with one of the particapnts from an existing b2b dialog. Parameters: * b2bl_key - a pseudo-variable that contains the b2b_logic key * entity_no - a pseudo-variable that holds the entity of the entity/participant to bridge. Example 1.18. b2b_bridge_request usage ... modparam("b2b_entities", "script_req_route", "b2b_request") ... route[b2b_request] { # incoming requests from the B2B entities ... if ($ci~="^B2B") { #keep this aligned with b2b_key_prefix # request coming from the UAC side; # the Call-ID carries the B2B key ID if (is_method("BYE") { $var(entity) = 1; b2b_bridge_request("$ci","$var(entity)"); } } ... } ... 1.5. Exported MI Functions 1.5.1. b2b_trigger_scenario This command instantiated a B2B scenario. Name: b2b_trigger_scenario Parameters: * senario_id : the id of the scenario to be instantiated. * scenario parameters - it can take 4 more parameters that are scenario parameters MI FIFO Command Format: :b2b_trigger_scenario:fifo_reply marketing sip:bob@opensips.org sip:322@opensips.org:5070 sip:alice@opensips.org _empty_line_ 1.5.2. b2b_bridge This command can be used by an external application to tell B2BUA to bridge a call party from an on going dialog to another destination. By default the caller is bridged to the new uri and BYE is set to the callee. You can instead bridge the callee if you send 1 as the third parameter. Also, if a fourth parameter is Name: b2b_bridge Parameters: * dialog-id : the id of the dialog. If you set the module parameter dialog-id the server will include the dialogid in the generated Invites as the content of a header with name 'Dialog-ID'. * new uri - the uri of the new destination * flag to specify that the callee must be bridged to the new destination. It is optional. If not present the caller will be bridged. * provisional media uri - the uri of a media server able to play provisional media starting from the beginning of the bridging scenario to the end of it. It is optional. If not present, no other entity will be envolved in the bridging scenario MI FIFO Command Format: :b2b_bridge:fifo_reply 1020.30 sip:alice@opensips.org _empty_line_ opensipsctl Command Format: opensipsctl b2b_bridge 1020.30 sip:alice@opensips.org 1.5.3. b2b_list This command can be used to list the internals of b2b_logic entities. Name: b2b_list Parameters: none MI FIFO Command Format: :b2b_list:_reply_fifo_file_ _empty_line_ opensipsctl Command Format: opensipsctl fifo b2b_list Chapter 2. Developer Guide The module provides an API that can be used from other OpenSIPS modules. The API offers the functions for instantiating b2b scenarios from other modules (this comes as an addition to the other two means of instantiating b2b scenarios - from script and with an MI command). Also the instantiations can be dynamically controlled, by commanding the bridging of an entity involved in a call to another entity or the termination of the call or even bridging two existing calls. 2.1. b2b_logic_bind(b2bl_api_t* api) This function binds the b2b_entities modules and fills the structure the exported functions that will be described in detail. Example 2.1. b2bl_api_t structure ... typedef struct b2bl_api { b2bl_init_f init; b2bl_bridge_f bridge; b2bl_bridge_extern_f bridge_extern; b2bl_bridge_2calls_t bridge_2calls; b2bl_terminate_call_t terminate_call; b2bl_set_state_f set_state; b2bl_bridge_msg_t bridge_msg; }b2bl_api_t; ... 2.2. init Field type: ... typedef str* (*b2bl_init_f)(struct sip_msg* msg, str* name, str* args[5] , b2bl_cback_f, void* param); ... Initializing a b2b scenario. The last two parameters are the callback function and the parameter to be called in 3 situations that will be listed below. The callback function has the following definition: ... typedef int (*b2b_notify_t)(struct sip_msg* msg, str* id, int type, void * param); ... The first argument is the callback given in the init function. The second argument is a structure with some statistics about the call -start time, setup time, call time. The third argument is the current state of the scenario instantiation. The last argument is the event that triggered the callback. There are 3 events when the callback is called: * when a BYE is received from either side- event parameter will also show from which side the BYE is received, so it can be B2B_BYE_E1 or B2B_BYE_E2 * If while bridging, a negative reply is received from the second entity - the event is B2B_REJECT_E2. * When the b2b logic entity is deleted- the evnet is B2B_DESTROY The return code controls what will happen with the request/reply that caused the event (except for the last event, when the return code does not matter) * -1 - error * 0 - drop the BYE or reply * 1 - send the BYE or reply on the other side * 2 - do what the scenario tells, if no rule defined send the BYE or reply on the other side 2.3. bridge Field type: ... typedef int (*b2bl_bridge_f)(str* key, str* new_uri, str* new_from_dname ,int entity_type); ... This function allows bridging an entity that is in a call handled by b2b_logic to another entity. 2.4. bridge_extern Field type: ... typedef str* (*b2bl_bridge_extern_f)(str* scenario_name, str* args[5], b2bl_cback_f cbf, void* cb_param); ... This function allows initiating an extern scenario, when the B2BUA starts a call from the middle. 2.5. bridge_2calls Field type: ... typedef int (*b2bl_bridge_2calls_t)(str* key1, str* key2); ... With this function it is possible to bridge two existing calls. The first entity from the two calls will be connected and BYE will be sent to their peers. 2.6. terminate_call Field type: ... typedef int (*b2bl_terminate_call_t)(str* key); ... Terminate a call. 2.7. set_state Field type: ... typedef int (*b2bl_set_state_f)(str* key, int state); ... Set the scenario state. 2.8. bridge_msg Field type: ... typedef int (*b2bl_bridge_msg_t)(struct sip_msg* msg, str* key, int enti ty_no); ... This function allows bridging an incoming call to an entity from an existing call. The first argument is the INVITE message of the current incoming call. The second argument is the b2bl_key of an existing call. The third argument is the entity identifier. opensips-2.2.2/modules/b2b_logic/b2b_load.h000066400000000000000000000077661300170765700205120ustar00rootroot00000000000000#ifndef B2BUA_LOGIC_LOAD_ #define B2BUA_LOGIC_LOAD_ #include "../../sr_module.h" #include "../b2b_entities/b2b_common.h" #include "../b2b_entities/dlg.h" #define B2B_BYE_CB (1<<0) #define B2B_REJECT_CB (1<<1) #define B2B_DESTROY_CB (1<<2) #define B2B_RE_INVITE_CB (1<<3) #define B2B_CONFIRMED_CB (1<<4) #define B2B_ERROR_CB_RET -1 #define B2B_DROP_MSG_CB_RET 0 #define B2B_SEND_MSG_CB_RET 1 #define B2B_FOLLOW_SCENARIO_CB_RET 2 typedef struct b2bl_dlg_stat { str key; int start_time; int setup_time; int call_time; }b2bl_dlg_stat_t; typedef struct b2bl_cb_params { void *param; /* parameter passed at callback registration */ b2bl_dlg_stat_t *stat; /* b2bl_dlg statistics */ struct sip_msg* msg; /* the message being processed */ unsigned int entity; /* the entity for which the callback is invoked */ } b2bl_cb_params_t; typedef int (*b2bl_cback_f)(b2bl_cb_params_t *params, unsigned int b2b_event); /* * event - B2B_BYE_CB, bye received from an entity * B2B_REJECT_CB, negative reply for invite when bridging * B2B_DESTROY_CB, destroy the tuple * B2B_RE_INVITE_CB, re-invite received from an entity * Return: * B2B_ERROR_CB_RET - error * B2B_DROP_MSG_CB_RET - drop the request * B2B_SEND_MSG_CB_RET - send the request on the other side * B2B_FOLLOW_SCENARIO_CB_RET - do what the scenario tells, * if no rule defined send the request on the other side **/ typedef str* (*b2bl_init_f)(struct sip_msg* msg, str* name, str* args[5], b2bl_cback_f, void* param, unsigned int cb_mask, str* custom_hdrs); typedef int (*b2bl_bridge_f)(str* key, str* new_uri, str* new_from_dname,int entity_type); /* key - the string returned by b2bl_init_f * entity_type - 0, the server entity * 1, the client entity */ typedef int (*b2bl_set_state_f)(str* key, int state); typedef str* (*b2bl_bridge_extern_f)(str* scenario_name, str* args[5], b2bl_cback_f cbf, void* cb_param); int b2bl_terminate_call(str* key); typedef int (*b2bl_terminate_call_t)(str* key); int b2bl_bridge(str* key,str* new_uri,str* new_from_dname,int entity_no); int b2bl_set_state(str* key, int state); int b2bl_bridge_2calls(str* key1, str* key2); typedef int (*b2bl_bridge_2calls_t)(str* key1, str* key2); int b2bl_bridge_msg(struct sip_msg* msg, str* key, int entity_no); typedef int (*b2bl_bridge_msg_t)(struct sip_msg* msg, str* key, int entity_no); int b2bl_get_stats(str* key, b2bl_dlg_stat_t* stat); typedef int (*b2bl_get_stats_f)(str* key, b2bl_dlg_stat_t* stat); int b2bl_register_cb(str* key, b2bl_cback_f, void* param, unsigned int cb_mask); typedef int (*b2bl_register_cb_f)(str* key, b2bl_cback_f, void* param, unsigned int cb_mask); int b2bl_restore_upper_info(str* b2bl_key, b2bl_cback_f, void* param, unsigned int cb_mask); typedef int (*b2bl_restore_upper_info_f)(str* b2bl_key, b2bl_cback_f, void* param, unsigned int cb_mask); typedef struct b2bl_api { b2bl_init_f init; b2bl_bridge_f bridge; b2bl_bridge_extern_f bridge_extern; b2bl_bridge_2calls_t bridge_2calls; b2bl_terminate_call_t terminate_call; b2bl_set_state_f set_state; b2bl_bridge_msg_t bridge_msg; b2bl_get_stats_f get_stats; b2bl_register_cb_f register_cb; b2bl_restore_upper_info_f restore_upper_info; }b2bl_api_t; str* internal_init_scenario(struct sip_msg* msg, str* name, str* args[5], b2bl_cback_f, void* param, unsigned int cb_mask, str* custom_hdrs); typedef int(*load_b2bl_f)( b2bl_api_t *api ); int b2b_logic_bind(b2bl_api_t* api); str* b2bl_bridge_extern(str* scenario_name, str* args[5], b2bl_cback_f cbf, void* cb_param); static inline int load_b2b_logic_api( b2bl_api_t *api) { load_b2bl_f load_b2b; /* import the b2b logic auto-loading function */ if ( !(load_b2b=(load_b2bl_f)find_export("b2b_logic_bind", 1, 0))) { LM_ERR("failed to import b2b_logic_bind\n"); return -1; } /* let the auto-loading function load all B2B stuff */ if (load_b2b( api )==-1) return -1; return 0; } #endif opensips-2.2.2/modules/b2b_logic/b2b_logic.c000066400000000000000000001254631300170765700206560ustar00rootroot00000000000000/* * back-to-back logic module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) * 2010-11-02 new mi function: mi_b2b_list (Ovidiu Sas) * 2010-11-12 new cmd: b2b_bridge_request (Ovidiu Sas) */ #include #include #include #include #include "../../db/db.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../parser/parse_from.h" #include "../../parser/parse_content.h" #include "../../ut.h" #include "../../trim.h" #include "../../mem/mem.h" #include "../../mod_fix.h" #include "records.h" #include "pidf.h" #include "b2b_logic.h" #include "b2b_load.h" #include "b2bl_db.h" #define TABLE_VERSION 3 /** Functions declarations */ static int mod_init(void); static void mod_destroy(void); static int child_init(int rank); static int load_script_scenario(modparam_t type, void* val); static int load_extern_scenario(modparam_t type, void* val); static int fixup_b2b_logic(void** param, int param_no); static struct mi_root* mi_trigger_scenario(struct mi_root* cmd, void* param); static struct mi_root* mi_b2b_bridge(struct mi_root* cmd, void* param); static struct mi_root* mi_b2b_list(struct mi_root* cmd, void* param); static struct mi_root* mi_b2b_terminate_call(struct mi_root* cmd, void* param); static void b2bl_clean(unsigned int ticks, void* param); static void b2bl_db_timer_update(unsigned int ticks, void* param); int b2b_init_request(struct sip_msg* msg, str* arg1, str* arg2, str* arg3, str* arg4, str* arg5, str* arg6); int b2b_bridge_request(struct sip_msg* msg, str* arg1, str* arg2); void b2b_mark_todel( b2bl_tuple_t* tuple); /** Global variables */ b2b_api_t b2b_api; b2bl_table_t b2bl_htable; unsigned int b2bl_hsize = 10; b2b_scenario_t* script_scenarios = NULL; b2b_scenario_t* extern_scenarios = NULL; unsigned int b2b_clean_period = 100; unsigned int b2b_update_period = 100; str custom_headers = {0, 0}; str custom_headers_lst[HDR_LST_LEN]; int custom_headers_lst_len =0; str custom_headers_regexp = {0, 0}; regex_t* custom_headers_re; /* The list of the headers that are passed on the other side by default */ static str default_headers[HDR_DEFAULT_LEN]= { {"Content-Type",12}, {"Supported", 9}, {"Allow", 5}, {"Proxy-Require", 13}, {"Session-Expires", 15}, {"Min-SE", 6}, {"Require", 7}, {"RSeq", 4}, }; int use_init_sdp = 0; enum b2bl_caller_type b2bl_caller; unsigned int max_duration = 12*3600; int b2bl_key_avp_name; unsigned short b2bl_key_avp_type; static str b2bl_key_avp_param = {NULL, 0}; static str b2bl_from_spec_param = {NULL, 0}; static pv_spec_t b2bl_from_spec; static pv_value_t b2bl_from_tok; static struct to_body b2bl_from; #define B2BL_FROM_BUF_LEN 255 static char b2bl_from_buf[B2BL_FROM_BUF_LEN + 1]; str db_url= {0, 0}; db_con_t *b2bl_db = NULL; db_func_t b2bl_dbf; str b2bl_dbtable= str_init("b2b_logic"); str init_callid_hdr={0, 0}; str server_address = {0, 0}; int b2bl_db_mode = WRITE_BACK; int unsigned b2bl_th_init_timeout = 60; /** Exported functions */ static cmd_export_t cmds[]= { {"b2b_init_request", (cmd_function)b2b_init_request, 5 , fixup_b2b_logic , 0 , REQUEST_ROUTE}, {"b2b_init_request", (cmd_function)b2b_init_request, 4 , fixup_b2b_logic , 0 , REQUEST_ROUTE}, {"b2b_init_request", (cmd_function)b2b_init_request, 3 , fixup_b2b_logic , 0 , REQUEST_ROUTE}, {"b2b_init_request", (cmd_function)b2b_init_request, 2 , fixup_b2b_logic , 0 , REQUEST_ROUTE}, {"b2b_init_request", (cmd_function)b2b_init_request, 1 , fixup_b2b_logic , 0 , REQUEST_ROUTE}, {"b2b_init_request", (cmd_function)b2b_init_request, 0 , 0 , 0 , REQUEST_ROUTE}, {"b2b_bridge_request",(cmd_function)b2b_bridge_request,2,fixup_pvar_pvar , 0 , REQUEST_ROUTE}, {"b2b_logic_bind", (cmd_function)b2b_logic_bind, 1 , 0, 0, 0}, { 0, 0, 0 , 0 , 0, 0} }; /** Exported parameters */ static param_export_t params[]= { {"hash_size", INT_PARAM, &b2bl_hsize }, {"cleanup_period", INT_PARAM, &b2b_clean_period }, {"update_period", INT_PARAM, &b2b_update_period }, {"script_scenario", STR_PARAM|USE_FUNC_PARAM, (void*)load_script_scenario}, {"extern_scenario", STR_PARAM|USE_FUNC_PARAM, (void*)load_extern_scenario}, {"custom_headers", STR_PARAM, &custom_headers.s }, {"custom_headers_regexp", STR_PARAM, &custom_headers_regexp.s }, {"use_init_sdp", INT_PARAM, &use_init_sdp }, {"db_url", STR_PARAM, &db_url.s }, {"db_table", STR_PARAM, &b2bl_dbtable.s }, {"max_duration", INT_PARAM, &max_duration }, /* {"b2bl_key_avp", STR_PARAM, &b2bl_key_avp_param.s }, */ {"b2bl_from_spec_param",STR_PARAM, &b2bl_from_spec_param.s }, {"server_address", STR_PARAM, &server_address.s }, {"init_callid_hdr", STR_PARAM, &init_callid_hdr.s }, {"db_mode", INT_PARAM, &b2bl_db_mode }, {"b2bl_th_init_timeout",INT_PARAM, &b2bl_th_init_timeout }, {0, 0, 0 } }; /** MI commands */ static mi_export_t mi_cmds[] = { { "b2b_trigger_scenario", 0, mi_trigger_scenario, 0, 0, 0}, { "b2b_bridge", 0, mi_b2b_bridge, 0, 0, 0}, { "b2b_list", 0, mi_b2b_list, 0, 0, 0}, { "b2b_terminate_call", 0, mi_b2b_terminate_call, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "b2b_entities", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "db_url", get_deps_sqldb_url }, { NULL, NULL }, }, }; /** Module interface */ struct module_exports exports= { "b2b_logic", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) mod_destroy, /* destroy function */ child_init /* per-child init function */ }; /** Module init function */ static int mod_init(void) { char* p = NULL; char* flags = NULL; int regexp_flags = 0; int i = 0, j; pv_spec_t avp_spec; LM_DBG("start\n"); /* load b2b_entities api */ if(load_b2b_api(&b2b_api)< 0) { LM_ERR("Failed to load b2b api\n"); return -1; } if(b2bl_hsize< 1 || b2bl_hsize> 20) { LM_ERR("Wrong hash size. Needs to be greater than 1" " and smaller than 20. Be aware that you should set the log 2" " value of the real size\n"); return -1; } b2bl_hsize = 1< 0) { if (pv_parse_spec(&b2bl_key_avp_param, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %.*s AVP definition\n", b2bl_key_avp_param.len, b2bl_key_avp_param.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &b2bl_key_avp_name, &b2bl_key_avp_type)!=0){ LM_ERR("[%.*s]- invalid AVP definition\n", b2bl_key_avp_param.len, b2bl_key_avp_param.s); return -1; } } else { b2bl_key_avp_name = -1; b2bl_key_avp_type = 0; } if(b2bl_from_spec_param.s) { b2bl_from_spec_param.len = strlen(b2bl_from_spec_param.s); if(pv_parse_spec(&b2bl_from_spec_param, &b2bl_from_spec)==NULL) { LM_ERR("failed to parse b2bl_from spec\n"); return E_CFG; } switch(b2bl_from_spec.type) { case PVT_NONE: case PVT_EMPTY: case PVT_NULL: case PVT_MARKER: case PVT_COLOR: LM_ERR("invalid b2bl_from spec\n"); return -1; default: ; } } /* parse extra headers */ if(custom_headers.s) custom_headers.len = strlen(custom_headers.s); memset(custom_headers_lst, 0, HDR_LST_LEN*sizeof(str)); custom_headers_lst[i].s = custom_headers.s; if(custom_headers.s) { p = strchr(custom_headers.s, ';'); while(p) { custom_headers_lst[i].len = p - custom_headers_lst[i].s; /* check if this is among the default headers */ for(j = 0; j< HDR_DEFAULT_LEN; j++) { if(custom_headers_lst[i].len == default_headers[j].len && strncmp(custom_headers_lst[i].s, default_headers[j].s, default_headers[j].len)== 0) goto next_hdr; } /* check if defined twice */ for(j = 0; j< i; j++) { if(custom_headers_lst[i].len == custom_headers_lst[j].len && strncmp(custom_headers_lst[i].s, custom_headers_lst[j].s, custom_headers_lst[j].len)== 0) goto next_hdr; } i++; if(i == HDR_LST_LEN) { LM_ERR("Too many extra headers defined." " The maximum value is %d\n.", HDR_LST_LEN); return -1; } next_hdr: p++; if(p-custom_headers.s >= custom_headers.len) break; custom_headers_lst[i].s = p; p = strchr(p, ';'); } } if(p == NULL) { custom_headers_lst[i].len = custom_headers.s + custom_headers.len - custom_headers_lst[i].s; if(custom_headers_lst[i].len == 0) i--; } custom_headers_lst_len = i +1; if(custom_headers_regexp.s) { custom_headers_regexp.len = strlen(custom_headers_regexp.s); if ((custom_headers_re=pkg_malloc(sizeof(regex_t)))==0) { LM_ERR("no more pkg memory\n"); return -1; } if (*custom_headers_regexp.s == '/') { flags = (char *)memchr(custom_headers_regexp.s+1, '/', custom_headers_regexp.len-1); if (flags) { custom_headers_regexp.s++; custom_headers_regexp.len = flags - custom_headers_regexp.s; custom_headers_regexp.s[custom_headers_regexp.len] = '\0'; flags++; while(*flags != '\0') { switch (*flags) { case 'i': regexp_flags |= REG_ICASE; break; case 'e': regexp_flags |= REG_EXTENDED; break; default: LM_ERR("Unknown option '%c'\n", *flags); } flags++; } } else { LM_ERR("Second '/' missing from regexp\n"); return -1; } } if (regcomp(custom_headers_re, custom_headers_regexp.s, regexp_flags) != 0) { pkg_free(custom_headers_re); LM_ERR("bad regexp '%.*s'\n", custom_headers_regexp.len, custom_headers_regexp.s); return -1; } } if(init_callid_hdr.s) init_callid_hdr.len = strlen(init_callid_hdr.s); register_timer("b2bl-clean", b2bl_clean, 0, b2b_clean_period, TIMER_FLAG_DELAY_ON_DELAY); if(b2bl_db_mode == WRITE_BACK) register_timer("b2bl-dbupdate", b2bl_db_timer_update, 0, b2b_update_period, TIMER_FLAG_SKIP_ON_DELAY); return 0; } void b2bl_db_timer_update(unsigned int ticks, void* param) { b2b_logic_dump(0); } void b2bl_clean(unsigned int ticks, void* param) { int i; b2bl_tuple_t* tuple, *tuple_next; unsigned int now; str bye = {BYE, BYE_LEN}; b2b_req_data_t req_data; now = get_ticks(); for(i = 0; i< b2bl_hsize; i++) { lock_get(&b2bl_htable[i].lock); tuple = b2bl_htable[i].first; while(tuple) { tuple_next = tuple->next; if(tuple->lifetime > 0 && tuple->lifetime < now) { LM_INFO("Found expired tuple [%.*s]: delete and send BYEs\n", tuple->key->len, tuple->key->s); if(tuple->bridge_entities[0] && tuple->bridge_entities[1] && !tuple->to_del) { if(!tuple->bridge_entities[0]->disconnected) { memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(tuple->bridge_entities[0]); req_data.method =&bye; b2b_api.send_request(&req_data); } if(!tuple->bridge_entities[1]->disconnected) { memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(tuple->bridge_entities[1]); req_data.method =&bye; b2b_api.send_request(&req_data); } } b2bl_delete(tuple, i, 0); } tuple = tuple_next; } lock_release(&b2bl_htable[i].lock); } } static int load_scenario(b2b_scenario_t** scenario_list,char* filename) { xmlDocPtr doc; xmlNodePtr node; b2b_scenario_t* scenario = NULL; str attr; xmlNodePtr rules_node, rule_node, request_node; int request_id = 0; b2b_rule_t* rule_struct = NULL; xmlNodePtr body_node; char* body_content= 0; char* body_type= 0; doc = xmlParseFile(filename); if(doc == NULL) { LM_ERR("Failed to parse xml file\n"); return -1; } scenario = (b2b_scenario_t*)pkg_malloc(sizeof(b2b_scenario_t)); if(scenario == NULL) { LM_ERR("No more private memory\n"); xmlFreeDoc(doc); return -1; } memset(scenario, 0, sizeof(b2b_scenario_t)); /* analyze the scenario document and descompose so that * applying it will be more efficient */ /* extract scenario_id and param no */ scenario->id.s = (char*)xmlNodeGetAttrContentByName(doc->children, "id"); if(scenario->id.s == NULL) { LM_ERR("XML scenario document not well formed. No id attribute found" " for root node\n"); pkg_free(scenario); return -1; } scenario->id.len = strlen(scenario->id.s); LM_DBG("Loaded scenario with id = [%.*s]\n", scenario->id.len, scenario->id.s); attr.s = (char*)xmlNodeGetAttrContentByName(doc->children, "param"); if(attr.s == NULL) { LM_ERR("XML scenario document not well formed. No id attribute found" " for root node\n"); return -1; } attr.len = strlen(attr.s); if( str2int(&attr, &scenario->param_no) < 0) { LM_ERR("Failed to parse id attribute for scenario node. It must be an integer.\n"); xmlFree(attr.s); pkg_free(scenario); return -1; } xmlFree(attr.s); /* extract init node */ scenario->init_node = xmlDocGetNodeByName(doc, "init", NULL); if(scenario->init_node == NULL) { LM_ERR("Wrong formatted xml doc. Didn't find an init node\n"); goto error; } node = xmlNodeGetChildByName(scenario->init_node, "use_init_sdp"); if(node) { scenario->use_init_sdp = 1; body_node = xmlNodeGetChildByName(node, "body"); if(body_node) { body_type = (char *)xmlNodeGetAttrContentByName(body_node, "type"); if (body_type == NULL) { LM_ERR("Bad formatted scenario document. Empty body content type\n"); goto error; } body_content = (char*)xmlNodeGetContent(body_node); if(body_content == NULL) { LM_ERR("Bad formatted scenario document. Empty body\n"); xmlFree(body_type); goto error; } /* we move everything in pkg to be able to strip them */ scenario->body_type.len = strlen(body_type); scenario->body_type.s = body_type; scenario->body.len = strlen(body_content); scenario->body.s = body_content; } } /* go through the rules */ node = xmlDocGetNodeByName(doc, "rules", NULL); if(node == NULL) { LM_DBG("No rules defined\n"); goto done; } rules_node = xmlNodeGetChildByName(node, "request"); if(rules_node == NULL) { LM_DBG("No request rules defined\n"); goto after_req_rules; } for(request_node= rules_node->children; request_node; request_node = request_node->next) { if(xmlStrcasecmp(request_node->name, (unsigned char*)"text") == 0) continue; attr.s = (char*)request_node->name; attr.len = strlen(attr.s); request_id = b2b_get_request_id(&attr); if(request_id < 0) { LM_ERR("Bad scenario document. A rule defined for a not supported" " request type [%s]\n", request_node->name); goto error; } for(rule_node= request_node->children; rule_node; rule_node = rule_node->next) { if(xmlStrcasecmp(rule_node->name, (unsigned char*)"rule")!= 0) continue; rule_struct = (b2b_rule_t*)pkg_malloc(sizeof(b2b_rule_t)); if(rule_struct == NULL) { LM_ERR("No more memory\n"); goto error; } memset(rule_struct, 0, sizeof(b2b_rule_t)); rule_struct->next = scenario->request_rules[request_id]; scenario->request_rules[request_id] = rule_struct; attr.s = (char*)xmlNodeGetAttrContentByName(rule_node, "id"); if(attr.s == NULL) { LM_ERR("Bad scenario document. No id attribute for 'rule' node\n"); goto error; } attr.len = strlen(attr.s); if(str2int(&attr, &rule_struct->id)< 0) { LM_ERR("Bad scenario document. rules_no subschild for request rule not an integer\n"); xmlFree(attr.s); goto error; } xmlFree(attr.s); rule_struct->cond_state = -1; /* extract conditional state if present */ rule_struct->cond_node = xmlNodeGetChildByName(rule_node, "condition"); if(rule_struct->cond_node) { /* extract the condition state if any */ attr.s = (char*)xmlNodeGetNodeContentByName(rule_struct->cond_node, "state", NULL); if(attr.s) { attr.len = strlen(attr.s); if(str2int(&attr, (unsigned int*)&rule_struct->cond_state)< 0) { LM_ERR("Bad scenario. Cond state must be an integer [%s]\n",attr.s); xmlFree(attr.s); goto error; } xmlFree(attr.s); } } node = xmlNodeGetChildByName(rule_node, "action"); if(node == NULL) { LM_ERR("Bad scenario document. A rule needs an action node\n"); goto error; } rule_struct->action_node = node; } } after_req_rules: /* TODO - Analyze if there are actions for replies */ LM_DBG("scenario_id = %.*s\n", scenario->id.len, scenario->id.s); done: scenario->doc = doc; scenario->next = *scenario_list; *scenario_list = scenario; return 0; error: if(doc) xmlFree(doc); if(scenario) { int i; b2b_rule_t* prev; for(i = 0; i< B2B_METHODS_NO; i++) { rule_struct = scenario->request_rules[i]; while(rule_struct) { prev = rule_struct; rule_struct = rule_struct->next; pkg_free(prev); } } rule_struct = scenario->reply_rules; while(rule_struct) { prev = rule_struct; rule_struct = rule_struct->next; pkg_free(prev); } if(scenario->id.s) xmlFree(scenario->id.s); if(scenario->body.s) xmlFree(scenario->body.s); if(scenario->body_type.s) xmlFree(scenario->body_type.s); pkg_free(scenario); } return -1; } static int load_script_scenario(modparam_t type, void* val) { return load_scenario(&script_scenarios, (char*)val); } static int load_extern_scenario(modparam_t type, void* val) { return load_scenario(&extern_scenarios, (char*)val); } static void mod_destroy(void) { int i; b2b_rule_t* rule_struct = NULL; b2b_scenario_t* scenario, *next; if(b2bl_db) { if(b2bl_db_mode==WRITE_BACK) b2b_logic_dump(1); b2bl_dbf.close(b2bl_db); } scenario = extern_scenarios; while(scenario) { next = scenario->next; xmlFree(scenario->id.s); xmlFreeDoc(scenario->doc); pkg_free(scenario); scenario = next; } scenario = script_scenarios; while(scenario) { next = scenario->next; xmlFreeDoc(scenario->doc); b2b_rule_t* prev; for(i = 0; i< B2B_METHODS_NO; i++) { rule_struct = scenario->request_rules[i]; while(rule_struct) { prev = rule_struct; rule_struct = rule_struct->next; pkg_free(prev); } } rule_struct = scenario->reply_rules; while(rule_struct) { prev = rule_struct; rule_struct = rule_struct->next; pkg_free(prev); } if(scenario->id.s) xmlFree(scenario->id.s); if (scenario->body.s) xmlFree(scenario->body.s); if (scenario->body_type.s) xmlFree(scenario->body_type.s); pkg_free(scenario); scenario = next; } destroy_b2bl_htable(); } static int child_init(int rank) { if (b2bl_db_mode==0) return 0; if (b2bl_dbf.init==0) { LM_CRIT("child_init: database not bound\n"); return -1; } b2bl_db = b2bl_dbf.init(&db_url); if(!b2bl_db) { LM_ERR("connecting to database failed\n"); return -1; } LM_DBG("child %d: Database connection opened successfully\n", rank); return 0; } b2b_scenario_t* get_scenario_id_list(str* sid, b2b_scenario_t* list) { b2b_scenario_t* scenario; /*search first in script_scenarios */ scenario = list; while(scenario) { LM_DBG("scenario id = %.*s\n", scenario->id.len, scenario->id.s); if(scenario->id.len == sid->len && strncmp(scenario->id.s, sid->s, sid->len) == 0) { return scenario; } scenario = scenario->next; } return 0; } b2b_scenario_t* get_scenario_id(str* sid) { b2b_scenario_t* scenario; if(sid->s== 0 || sid->len== 0) return 0; if(sid->len == B2B_TOP_HIDING_SCENARY_LEN && strncmp(sid->s,B2B_TOP_HIDING_SCENARY,B2B_TOP_HIDING_SCENARY_LEN)==0) { return 0; } scenario = get_scenario_id_list(sid, script_scenarios); if(scenario) return scenario; return get_scenario_id_list(sid, extern_scenarios); } static int fixup_b2b_logic(void** param, int param_no) { pv_elem_t *model; str s; str flags_s; int st; struct b2b_scen_fl *scf; if(param_no== 0) return 0; if(*param) { s.s = (char*)(*param); s.len = strlen(s.s); if(pv_parse_format(&s, &model)<0) { LM_ERR( "wrong format[%s]\n",(char*)(*param)); return E_UNSPEC; } /* the first parameter must be the scenario id and possible flags, must be a string */ if(param_no == 1) { if(model->spec.type != PVT_NONE ) { LM_ERR("The first parameter is not a string\n"); return -1; } scf = prepare_b2b_scen_fl_struct(); if (scf == NULL) { LM_ERR("no more pkg memory\n"); return -1; } scf->params.init_timeout = b2bl_th_init_timeout; if ( (flags_s.s = strchr(s.s,'/')) != NULL) { s.len = flags_s.s - s.s; flags_s.s++; flags_s.len = strlen(flags_s.s); /* parse flags */ for( st=0 ; st< flags_s.len ; st++ ) { switch (flags_s.s[st]) { case 't': scf->params.init_timeout = 0; while (stparams.init_timeout = scf->params.init_timeout*10 + flags_s.s[st+1] - '0'; st++; } break; case 'a': scf->params.flags |= B2BL_FLAG_TRANSPARENT_AUTH; break; case 'p': scf->params.flags |= B2BL_FLAG_TRANSPARENT_TO; break; default: LM_WARN("unknown option `%c'\n", *flags_s.s); } } } if(s.len == B2B_TOP_HIDING_SCENARY_LEN && strncmp(s.s,B2B_TOP_HIDING_SCENARY,B2B_TOP_HIDING_SCENARY_LEN)==0) { scf->scenario = NULL; } else { scf->scenario = get_scenario_id_list(&s, script_scenarios); if (!scf->scenario) { LM_ERR("Wrong Scenary ID. No scenario with this ID [%.*s]\n", s.len, s.s); return E_UNSPEC; } } *param=(void*)scf; return 0; } *param = (void*)model; return 0; } LM_ERR( "null format\n"); return E_UNSPEC; } struct to_body* get_b2bl_from(struct sip_msg* msg) { int len = 0; if(b2bl_from_spec_param.s) { memset(&b2bl_from_tok, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, &b2bl_from_spec, &b2bl_from_tok) < 0) { LM_ERR("Failed to get b2bl_from value\n"); return NULL; } //LM_DBG("got b2bl_from_spec_param flags [%d]\n", b2bl_from_tok.flags); if(b2bl_from_tok.flags&PV_VAL_INT) { /* the PV might be empty */ return NULL; } if(b2bl_from_tok.flags&PV_VAL_STR) { //LM_DBG("got PV_SPEC b2bl_from [%.*s]\n", // b2bl_from_tok.rs.len, b2bl_from_tok.rs.s); if(b2bl_from_tok.rs.len+CRLF_LEN > B2BL_FROM_BUF_LEN) { LM_ERR("Buffer overflow\n"); return NULL; } trim(&b2bl_from_tok.rs); memcpy(b2bl_from_buf, b2bl_from_tok.rs.s, b2bl_from_tok.rs.len); len = b2bl_from_tok.rs.len; if(strncmp(b2bl_from_tok.rs.s + len - CRLF_LEN, CRLF, CRLF_LEN)) { memcpy(b2bl_from_buf + len, CRLF, CRLF_LEN); len+= CRLF_LEN; } parse_to(b2bl_from_buf, b2bl_from_buf+len, &b2bl_from); if (b2bl_from.error != PARSE_OK) { LM_ERR("Failed to parse PV_SPEC b2bl_from [%.*s]\n", len, b2bl_from_buf); return NULL; } if (parse_uri(b2bl_from.uri.s, b2bl_from.uri.len, &b2bl_from.parsed_uri)<0) { LM_ERR("failed to parse PV_SPEC b2bl_from uri [%.*s]\n", b2bl_from.uri.len, b2bl_from.uri.s); return NULL; } /* side effect of parsing - nobody should need them later on, * so free them right now */ free_to_params(&b2bl_from); return &b2bl_from; } } return NULL; } str* b2bl_bridge_extern(str* scenario_name, str* args[], b2bl_cback_f cbf, void* cb_param) { b2b_scenario_t* scenario_struct; unsigned int hash_index; b2bl_tuple_t* tuple= NULL; str* b2bl_key; unsigned int state = 0; xmlNodePtr xml_node; str attr; if(scenario_name== NULL || args[0] == NULL || args[1]== NULL) { LM_ERR("Wrong arguments\n"); return 0; } hash_index = core_hash(args[0], args[1], b2bl_hsize); LM_DBG("start: bridge [%.*s] with [%.*s]\n", args[0]->len, args[0]->s, args[1]->len, args[1]->s); /* find the scenario with the corresponding id */ scenario_struct = extern_scenarios; while(scenario_struct) { if(scenario_struct->id.len == scenario_name->len && strncmp(scenario_struct->id.s, scenario_name->s, scenario_name->len) == 0) { break; } scenario_struct = scenario_struct->next; } if(scenario_struct == NULL) { LM_ERR("No scenario found with the specified id\n"); return 0; } /* apply the init part of the scenario */ tuple = b2bl_insert_new(NULL, hash_index, scenario_struct, args, NULL, NULL, -1, &b2bl_key, INSERTDB_FLAG); if(tuple== NULL) { LM_ERR("Failed to insert new scenario instance record\n"); return 0; } tuple->cbf = cbf; tuple->cb_param = cb_param; tuple->lifetime = 60 + get_ticks(); /* need to get the next action */ xml_node = xmlNodeGetChildByName(scenario_struct->init_node, "state"); if(xml_node) { attr.s = (char*)xmlNodeGetContent(xml_node); if(attr.s == NULL) { LM_ERR("No state node content found\n"); goto error; } attr.len = strlen(attr.s); if(str2int(&attr, &state)< 0) { LM_ERR("Bad scenario. Scenary state not an integer\n"); xmlFree(attr.s); goto error; } LM_DBG("Next scenario state is [%d]\n", state); xmlFree(attr.s); } tuple->next_scenario_state = state; xml_node = xmlNodeGetChildByName(scenario_struct->init_node, "bridge"); if(xml_node == NULL) { LM_ERR("No bridge node found\n"); goto error; } if(process_bridge_action(0, 0, tuple, xml_node) < 0) { LM_ERR("Failed to process bridge node"); goto error; } lock_release(&b2bl_htable[hash_index].lock); return b2bl_key; error: if(tuple) lock_release(&b2bl_htable[hash_index].lock); return 0; } static struct mi_root* mi_trigger_scenario(struct mi_root* cmd, void* param) { struct mi_node* node= NULL; str* args[B2B_INIT_MAX_PARAMNO]; int i = 0; str scenario_name; node = cmd->node.kids; if(node == NULL) return 0; b2bl_caller = CALLER_MI; /* Get scenario ID */ scenario_name = node->value; if(scenario_name.s == NULL || scenario_name.len== 0) { LM_ERR("Empty scenario name parameter\n"); return init_mi_tree(404, "Empty scenario ID", 16); } node = node->next; memset(args, 0, B2B_INIT_MAX_PARAMNO* sizeof(str*)); /* get the other parameters */ while(i < B2B_INIT_MAX_PARAMNO && node) { if(node->value.s == NULL || node->value.len== 0) break; args[i++] = &node->value; node = node->next; } if(b2bl_bridge_extern(&scenario_name, args, 0, 0) == 0) { LM_ERR("Failed to initialize scenario\n"); return 0; } return init_mi_tree(200, "OK", 2); } int b2b_bridge_request(struct sip_msg* msg, str* p1, str* p2) { pv_value_t pv_val; str key = {NULL, 0}; int entity_no; if (p1 && (pv_get_spec_value(msg, (pv_spec_t *)p1, &pv_val) == 0)) { if (pv_val.flags & PV_VAL_STR) { LM_DBG("got key:'%.*s'\n", pv_val.rs.len, pv_val.rs.s); key = pv_val.rs; } else { LM_ERR("Unable to get key from PV that is not a string\n"); return -1; } } else { LM_ERR("Unable to get key from pv:%p\n", p1); return -1; } if (p2 && (pv_get_spec_value(msg, (pv_spec_t *)p2, &pv_val) == 0)) { if (pv_val.flags & PV_VAL_INT) { entity_no = pv_val.ri; LM_DBG("got entity_no %d\n", entity_no); } else if (pv_val.flags & PV_VAL_STR) { if(str2int(&(pv_val.rs), (unsigned int*)&entity_no) != 0) { LM_ERR("Unable to get entity_no from pv '%.*s'i\n", pv_val.rs.len, pv_val.rs.s); return -1; } } else { LM_ERR("second pv not a str or int type\n"); return -1; } } else { LM_ERR("Unable to get entity from pv:%p\n", p1); return -1; } return b2bl_bridge_msg(msg, &key, entity_no); } static struct mi_root* mi_b2b_terminate_call(struct mi_root* cmd, void* param) { struct mi_node* node= NULL; str key; node = cmd->node.kids; if(node == NULL) return 0; /* b2bl_key */ key = node->value; if(key.s == NULL || key.len== 0) { LM_ERR("Wrong b2b_logic key parameter\n"); return init_mi_tree(404, "Empty b2bl key", 14); } b2bl_terminate_call(&key); return init_mi_tree(200, "OK", 2); } /* * arguments: b2bl_key, new_dest, entity (1 - client) * */ static struct mi_root* mi_b2b_bridge(struct mi_root* cmd, void* param) { struct mi_node* node= NULL; str key; b2bl_tuple_t* tuple; str new_dest; str prov_media; unsigned int entity_no = 0; b2bl_entity_id_t* entity, *old_entity, *bridging_entity, *prov_entity = 0; struct sip_uri uri; str meth_inv = {INVITE, INVITE_LEN}; str meth_bye = {BYE, BYE_LEN}; unsigned int hash_index, local_index; str ok= str_init("ok"); b2b_req_data_t req_data; b2b_rpl_data_t rpl_data; node = cmd->node.kids; if(node == NULL) return 0; /* b2bl_key */ key = node->value; if(key.s == NULL || key.len== 0) { LM_ERR("Wrong b2b_logic key parameter\n"); return init_mi_tree(404, "Empty b2bl key", 14); } /* new destination- must be a valid SIP URI */ node = node->next; if(node == NULL) return 0; new_dest = node->value; if(new_dest.s == NULL || new_dest.len == 0) { LM_ERR("Empty new dest parameter\n"); return init_mi_tree(404, "Empty parameter", 15); } if(parse_uri(new_dest.s, new_dest.len, &uri)< 0) { LM_ERR("Bad argument. Not a valid uri [%.*s]\n", new_dest.len, new_dest.s); return init_mi_tree(404, "Bad parameter", 13); } /* the last parameter is optional, if present and 1 - > * means that destination from the current call must be * bridged to the new destination */ node = node->next; if(node) { if (node->value.len==1) { if(strncmp(node->value.s, "0", 1)==0) entity_no = 0; else if(strncmp(node->value.s, "1", 1)==0) entity_no = 1; else return init_mi_tree(404, "Invalid entity no parameter", 27); } else { return init_mi_tree(404, "Invalid entity no parameter", 27); } node = node->next; if (node) { /* parse new uri */ prov_media = node->value; if(parse_uri(node->value.s, node->value.len, &uri)< 0) { LM_ERR("Bad argument. Not a valid provisional media uri [%.*s]\n", new_dest.len, new_dest.s); return init_mi_tree(404, "Bad parameter", 13); } prov_entity = b2bl_create_new_entity(B2B_CLIENT, 0, &prov_media, 0, 0, 0, 0); if (!prov_entity) { LM_ERR("Failed to create new b2b entity\n"); goto free; } if (node->next) return init_mi_tree(404, MI_SSTR(MI_MISSING_PARM)); } } if(b2bl_parse_key(&key, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key '%.*s'\n", key.len, key.s); goto free; } entity = b2bl_create_new_entity(B2B_CLIENT, 0, &new_dest, 0, 0, 0, 0); if(entity == NULL) { LM_ERR("Failed to create new b2b entity\n"); goto free; } lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("No entity found\n"); goto error; } bridging_entity = tuple->bridge_entities[entity_no]; old_entity = tuple->bridge_entities[(entity_no?0:1)]; if(old_entity == NULL || bridging_entity == NULL) { LM_ERR("Wrong dialog id\n"); goto error; } if(old_entity->next || old_entity->prev) { LM_ERR("Can not disconnect entity [%p]\n", old_entity); b2bl_print_tuple(tuple, L_ERR); goto error; } /* send BYE to old client */ if(old_entity->disconnected) { memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(old_entity); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); } else { old_entity->disconnected = 1; memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(old_entity); req_data.method =&meth_bye; b2b_api.send_request(&req_data); } if (0 == b2bl_drop_entity(old_entity, tuple)) { LM_ERR("Inconsistent tuple [%p]\n", tuple); b2bl_print_tuple(tuple, L_ERR); goto error; } if (old_entity->peer->peer == old_entity) old_entity->peer->peer = NULL; else { LM_ERR("Unexpected chain: old_entity=[%p] and old_entity->peer->peer=[%p]\n", old_entity, old_entity->peer->peer); goto error; } old_entity->peer = NULL; tuple->bridge_entities[0]= bridging_entity; if (prov_entity) { tuple->bridge_entities[1]= prov_entity; tuple->bridge_entities[2]= entity; /* we don't have to free it anymore */ prov_entity = 0; } else { tuple->bridge_entities[1]= entity; bridging_entity->peer = entity; entity->peer = bridging_entity; } tuple->scenario_state = B2B_BRIDGING_STATE; bridging_entity->state = 0; memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(bridging_entity); req_data.method =&meth_inv; b2b_api.send_request(&req_data); lock_release(&b2bl_htable[hash_index].lock); return init_mi_tree(200, "OK", 2); error: if(tuple) b2b_mark_todel(tuple); lock_release(&b2bl_htable[hash_index].lock); free: if (prov_entity) shm_free(prov_entity); return 0; } static inline int internal_mi_print_b2bl_entity_id(struct mi_node *node1, b2bl_entity_id_t *c) { int len; char* p; struct mi_node *node2=NULL; struct mi_attr* attr; if (c->scenario_id.s && c->scenario_id.len != 0) { attr = add_mi_attr(node1, MI_DUP_VALUE, "scenario_id", 11, c->scenario_id.s, c->scenario_id.len); if(attr == NULL) goto error; } if (c->key.s && c->key.len != 0) { attr = add_mi_attr(node1, MI_DUP_VALUE, "key", 3, c->key.s, c->key.len); if(attr == NULL) goto error; } p = int2str((unsigned long)(c->disconnected), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "disconnected", 12, p, len); if(attr == NULL) goto error; p = int2str((unsigned long)(c->state), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "state", 5, p, len); if(attr == NULL) goto error; p = int2str((unsigned long)(c->no), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "no", 2, p, len); if(attr == NULL) goto error; p = int2str((unsigned long)(c->type), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "type", 4, p, len); if(attr == NULL) goto error; if (c->peer) { if (c->peer->key.s && c->peer->key.len != 0) { attr = add_mi_attr(node1, MI_DUP_VALUE, "peer", 4, c->peer->key.s, c->peer->key.len); if(attr == NULL) goto error; } } if (c->to_uri.s && c->to_uri.len != 0) { node2 = add_mi_node_child(node1, MI_DUP_VALUE, "to_uri", 6, c->to_uri.s, c->to_uri.len); if(node2 == NULL) goto error; } if (c->from_uri.s && c->from_uri.len != 0) { node2 = add_mi_node_child(node1, MI_DUP_VALUE, "from_uri", 8, c->from_uri.s, c->from_uri.len); if(node2 == NULL) goto error; } if (c->from_dname.s && c->from_dname.len != 0) { node2 = add_mi_node_child(node1, MI_DUP_VALUE, "from_dname", 10, c->from_dname.s, c->from_dname.len); if(node2 == NULL) goto error; } return 0; error: LM_ERR("failed to add node\n"); return -1; } static struct mi_root* mi_b2b_list(struct mi_root* cmd, void* param) { int i, len, index; char* p; b2bl_tuple_t* tuple; struct mi_root *rpl_tree; struct mi_node *node=NULL, *node1=NULL, *rpl=NULL, *node_a=NULL; struct mi_attr* attr; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; for(i = 0; i< b2bl_hsize; i++) { lock_get(&b2bl_htable[i].lock); tuple = b2bl_htable[i].first; while(tuple) { p = int2str((unsigned long)(tuple->id), &len); node = add_mi_node_child(rpl, MI_DUP_VALUE, "tuple", 5, p, len); if(node == NULL) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "key", 3, tuple->key->s, tuple->key->len); if(attr == NULL) goto error; p = int2str((unsigned long)(tuple->scenario_state), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "scenario_state", 14, p, len); if(attr == NULL) goto error; p = int2str((unsigned long)(tuple->lifetime), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "lifetime", 8, p, len); if(attr == NULL) goto error; p = int2str((unsigned long)(tuple->db_flag), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "db_flag", 7, p, len); if(attr == NULL) goto error; if (tuple->scenario) { attr = add_mi_attr(node, MI_DUP_VALUE, "scenario", 8, tuple->scenario->id.s, tuple->scenario->id.len); if(attr == NULL) goto error; p = int2str((unsigned long)(tuple->next_scenario_state), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "next_scenario_state", 19, p, len); if(attr == NULL) goto error; } for (node_a=NULL,index=0; index < MAX_B2BL_ENT; index++) { if (tuple->servers[index] != NULL) { if (node_a==NULL) { node_a = add_mi_node_child(node, MI_IS_ARRAY, "SERVERS", 7, NULL, 0); if (node_a==NULL) goto error; } p = int2str((unsigned long)(index), &len); node1 = add_mi_node_child(node_a, MI_DUP_VALUE, "server", 6, p, len); if(node1 == NULL) goto error; if (internal_mi_print_b2bl_entity_id(node1, tuple->servers[index])!=0) goto error; } } for (node_a=NULL,index=0; index < MAX_B2BL_ENT; index++) { if (tuple->clients[index] != NULL) { if (node_a==NULL) { node_a = add_mi_node_child(node, MI_IS_ARRAY, "CLIENTS", 7, NULL, 0); if (node_a==NULL) goto error; } p = int2str((unsigned long)(index), &len); node1 = add_mi_node_child(node_a, MI_DUP_VALUE, "client", 6, p, len); if(node1 == NULL) goto error; if (internal_mi_print_b2bl_entity_id(node1, tuple->clients[index])!=0) goto error; } } for (node_a=NULL,index=0; index < MAX_BRIDGE_ENT; index++) { if (tuple->bridge_entities[index] != NULL) { if (node_a==NULL) { node_a = add_mi_node_child(node, MI_IS_ARRAY, "BRIDGE_ENTITIES", 15, NULL, 0); if (node_a==NULL) goto error; } p = int2str((unsigned long)(index), &len); node1 = add_mi_node_child(node_a, MI_DUP_VALUE, "bridge_entitie", 14, p, len); if(node1 == NULL) goto error; if (internal_mi_print_b2bl_entity_id(node1, tuple->bridge_entities[index])!=0) goto error; } } tuple = tuple->next; } lock_release(&b2bl_htable[i].lock); } return rpl_tree; error: lock_release(&b2bl_htable[i].lock); LM_ERR("Unable to create reply\n"); free_mi_tree(rpl_tree); return NULL; } int b2bl_register_cb(str* key, b2bl_cback_f cbf, void* cb_param, unsigned int cb_mask) { b2bl_tuple_t* tuple; unsigned int hash_index, local_index; if(!key) { LM_ERR("null key\n"); return -1; } if(b2bl_parse_key(key, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key [%.*s]\n", key->len, key->s); return -1; } lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("No tuple found\n"); goto error; } if(tuple->cbf || tuple->cb_param || tuple->cb_mask) { LM_ERR("callback already registered\n"); goto error; } tuple->cbf = cbf; tuple->cb_mask = cb_mask; tuple->cb_param = cb_param; lock_release(&b2bl_htable[hash_index].lock); return 0; error: lock_release(&b2bl_htable[hash_index].lock); return -1; } int b2b_logic_bind(b2bl_api_t* api) { if (!api) { LM_ERR("Invalid parameter value\n"); return -1; } api->init = internal_init_scenario; api->bridge = b2bl_bridge; api->bridge_extern = b2bl_bridge_extern; api->set_state = b2bl_set_state; api->bridge_2calls = b2bl_bridge_2calls; api->bridge_msg = b2bl_bridge_msg; api->terminate_call= b2bl_terminate_call; api->get_stats = b2bl_get_stats; api->register_cb = b2bl_register_cb; api->restore_upper_info = b2bl_restore_upper_info; return 0; } int b2bl_restore_upper_info(str* b2bl_key, b2bl_cback_f cbf, void* param, unsigned int cb_mask) { b2bl_tuple_t* tuple; unsigned int local_index, hash_index; if(b2bl_key == NULL) { LM_ERR("'param' argument NULL\n"); return -1; } if(b2bl_parse_key(b2bl_key, &hash_index, &local_index)< 0) { LM_ERR("Failed to parse b2b logic key [%.*s]\n", b2bl_key->len, b2bl_key->s); return -1; } LM_DBG("hi= %d, li=%d\n", hash_index, local_index); lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("B2B logic record not found\n"); lock_release(&b2bl_htable[hash_index].lock); return -1; } tuple->cbf = cbf; tuple->cb_mask = cb_mask; tuple->cb_param = param; lock_release(&b2bl_htable[hash_index].lock); return 0; } opensips-2.2.2/modules/b2b_logic/b2b_logic.h000066400000000000000000000110371300170765700206520ustar00rootroot00000000000000/* * back-to-back logic module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #ifndef _B2B_LOGIC_H_ #define _B2B_LOGIC_H_ #include #include "../../str.h" #include "../b2b_entities/b2b_entities.h" #define B2B_INIT_MAX_PARAMNO 5 #define B2B_BRIDGING_STATE -1 #define B2B_CANCEL_STATE -2 #define B2B_NOTDEF_STATE -3 #define B2B_TOP_HIDING_SCENARY "top hiding" #define B2B_TOP_HIDING_SCENARY_LEN strlen("top hiding") #define B2BL_ENT_NEW 0 #define B2BL_ENT_CONFIRMED 1 #define b2b_peer(type) ((type+1)%2) #define HDR_LST_LEN 32 #define HDR_DEFAULT_LEN 8 /* B2BL_FLAGS constants */ #define B2BL_FLAG_TRANSPARENT_AUTH 0x01 #define B2BL_FLAG_TRANSPARENT_TO 0x02 extern b2b_api_t b2b_api; enum b2bl_caller_type { CALLER_MODULE, CALLER_SCRIPT, CALLER_MI }; extern enum b2bl_caller_type b2bl_caller; typedef struct b2b_rule { unsigned int id; int cond_state; xmlNodePtr cond_node; xmlNodePtr action_node; struct b2b_rule* next; }b2b_rule_t; enum { B2B_INVITE, B2B_ACK, B2B_BYE, B2B_MESSAGE, B2B_SUBSCRIBE, B2B_NOTIFY, B2B_REFER, B2B_CANCEL, B2B_UPDATE, B2B_METHODS_NO }; typedef struct b2b_scenario { str id; unsigned int param_no; char use_init_sdp; xmlDocPtr doc; xmlNodePtr init_node; str body; str body_type; b2b_rule_t* request_rules[B2B_METHODS_NO]; b2b_rule_t* reply_rules; struct b2b_scenario* next; }b2b_scenario_t; struct b2b_params { unsigned int flags; unsigned int init_timeout; }; struct b2b_scen_fl { b2b_scenario_t* scenario; struct b2b_params params; }; extern b2b_scenario_t* script_scenaries; extern b2b_scenario_t* extern_scenaries; extern str custom_headers_lst[HDR_LST_LEN]; extern regex_t* custom_headers_re; int custom_headers_lst_len; extern int use_init_sdp; extern str server_address; extern unsigned int max_duration; extern str init_callid_hdr; extern str db_url; extern db_con_t *b2bl_db ; extern db_func_t b2bl_dbf; extern str b2bl_dbtable; extern char* b2bl_db_buf; extern int b2bl_db_mode; extern unsigned int b2bl_th_init_timeout; static inline int b2b_get_request_id(str* request) { if(request->len ==INVITE_LEN&&strncasecmp(request->s,INVITE,INVITE_LEN)==0) return B2B_INVITE; if(request->len ==ACK_LEN && strncasecmp(request->s,ACK,ACK_LEN)==0) return B2B_ACK; if(request->len ==BYE_LEN && strncasecmp(request->s,BYE,BYE_LEN)==0) return B2B_BYE; if(request->len==REFER_LEN &&strncasecmp(request->s, REFER, REFER_LEN)==0) return B2B_REFER; if(request->len==CANCEL_LEN &&strncasecmp(request->s, CANCEL, CANCEL_LEN)==0) return B2B_CANCEL; if(request->len==SUBSCRIBE_LEN &&strncasecmp(request->s, SUBSCRIBE, SUBSCRIBE_LEN)==0) return B2B_SUBSCRIBE; if(request->len==NOTIFY_LEN &&strncasecmp(request->s, NOTIFY, NOTIFY_LEN)==0) return B2B_NOTIFY; if(request->len==MESSAGE_LEN &&strncasecmp(request->s, MESSAGE, MESSAGE_LEN)==0) return B2B_MESSAGE; if(request->len==UPDATE_LEN &&strncasecmp(request->s, UPDATE, UPDATE_LEN)==0) return B2B_UPDATE; return -1; } b2b_scenario_t* b2b_find_scenario(b2b_scenario_t* scenario, unsigned int scenario_id); int b2b_add_dlginfo(str* key, str* entity_key,int src, b2b_dlginfo_t* info); int b2b_server_notify(struct sip_msg* msg, str* key, int type, void* param); int b2b_client_notify(struct sip_msg* msg, str* key, int type, void* param); b2b_scenario_t* get_scenario_id_list(str* sid, b2b_scenario_t* list); b2b_scenario_t* get_scenario_id(str* sid); void b2bl_db_init(void); static inline struct b2b_scen_fl* prepare_b2b_scen_fl_struct(void) { struct b2b_scen_fl *scf; scf = (struct b2b_scen_fl*)pkg_malloc( sizeof(struct b2b_scen_fl) ); if (scf==0) { LM_ERR("no more pkg memory\n"); return NULL; } memset(scf, 0, sizeof(struct b2b_scen_fl)); return scf; } #endif opensips-2.2.2/modules/b2b_logic/b2bl_db.c000066400000000000000000000460721300170765700203200ustar00rootroot00000000000000/* * back-to-back logic module * * Copyright (C) 2011 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2011-04-04 initial version (Anca Vamanu) */ #include #include #include "../../db/db.h" #include "b2b_logic.h" #include "b2bl_db.h" #define B2BL_FETCH_SIZE 128 static str str_key_col = str_init("si_key"); static str str_scenario_col = str_init("scenario"); static str str_sparam0_col = str_init("sparam0"); static str str_sparam1_col = str_init("sparam1"); static str str_sparam2_col = str_init("sparam2"); static str str_sparam3_col = str_init("sparam3"); static str str_sparam4_col = str_init("sparam4"); static str str_sdp_col = str_init("sdp"); static str str_sstate_col = str_init("sstate"); static str str_next_sstate_col = str_init("next_sstate"); static str str_lifetime_col = str_init("lifetime"); static str str_e1_type_col = str_init("e1_type"); static str str_e1_sid_col = str_init("e1_sid"); static str str_e1_to_col = str_init("e1_to"); static str str_e1_from_col = str_init("e1_from"); static str str_e1_key_col = str_init("e1_key"); static str str_e2_type_col = str_init("e2_type"); static str str_e2_sid_col = str_init("e2_sid"); static str str_e2_to_col = str_init("e2_to"); static str str_e2_from_col = str_init("e2_from"); static str str_e2_key_col = str_init("e2_key"); static str str_e3_type_col = str_init("e3_type"); static str str_e3_sid_col = str_init("e3_sid"); static str str_e3_to_col = str_init("e3_to"); static str str_e3_from_col = str_init("e3_from"); static str str_e3_key_col = str_init("e3_key"); #define DB_COLS_NO 26 static db_key_t qcols[DB_COLS_NO]; static db_val_t qvals[DB_COLS_NO]; static int n_query_update; /* initialize the column names and vals type -> take care always to keep this order */ void b2bl_db_init(void) { memset(qvals, 0, DB_COLS_NO* sizeof(db_val_t)); qcols[0] = &str_key_col; qvals[0].type = DB_STR; qcols[1] = &str_scenario_col; qvals[1].type = DB_STR; qcols[2] = &str_sparam0_col; qvals[2].type = DB_STR; qcols[3] = &str_sparam1_col; qvals[3].type = DB_STR; qcols[4] = &str_sparam2_col; qvals[4].type = DB_STR; qcols[5] = &str_sparam3_col; qvals[5].type = DB_STR; qcols[6] = &str_sparam4_col; qvals[6].type = DB_STR; qcols[7] = &str_sdp_col; qvals[7].type = DB_STR; n_query_update= 8; qcols[8] = &str_sstate_col; qvals[8].type = DB_INT; qcols[9] = &str_next_sstate_col; qvals[9].type = DB_INT; qcols[10] = &str_lifetime_col; qvals[10].type= DB_INT; qcols[11] = &str_e1_type_col; qvals[11].type= DB_INT; qcols[12] = &str_e1_sid_col; qvals[12].type= DB_STR; qcols[13] = &str_e1_to_col; qvals[13].type= DB_STR; qcols[14] = &str_e1_from_col; qvals[14].type= DB_STR; qcols[15] = &str_e1_key_col; qvals[15].type= DB_STR; qcols[16] = &str_e2_type_col; qvals[16].type= DB_INT; qcols[17] = &str_e2_sid_col; qvals[17].type= DB_STR; qcols[18] = &str_e2_to_col; qvals[18].type= DB_STR; qcols[19] = &str_e2_from_col; qvals[19].type= DB_STR; qcols[20] = &str_e2_key_col; qvals[20].type= DB_STR; qcols[21] = &str_e3_type_col; qvals[21].type= DB_INT; qcols[22] = &str_e3_sid_col; qvals[22].type= DB_STR; qcols[23] = &str_e3_to_col; qvals[23].type= DB_STR; qcols[24] = &str_e3_from_col; qvals[24].type= DB_STR; qcols[25] = &str_e3_key_col; qvals[25].type= DB_STR; } void b2bl_db_delete(b2bl_tuple_t* tuple) { if(!tuple || !tuple->key || b2bl_db_mode==NO_DB || (b2bl_db_mode==WRITE_BACK && tuple->db_flag==INSERTDB_FLAG)) return; LM_DBG("Delete key = %.*s\n", tuple->key->len, tuple->key->s); if(b2bl_dbf.use_table(b2bl_db, &b2bl_dbtable)< 0) { LM_ERR("sql use table failed\n"); return; } qvals[0].val.str_val = *tuple->key; if(b2bl_dbf.delete(b2bl_db, qcols, 0, qvals, 1) < 0) { LM_ERR("Failed to delete from database table [%.*s]\n", tuple->key->len, tuple->key->s); } } void b2b_logic_dump(int no_lock) { b2bl_tuple_t* tuple; int i; int n_insert_cols; if(b2bl_dbf.use_table(b2bl_db, &b2bl_dbtable)< 0) { LM_ERR("sql use table failed\n"); return; } for(i = 0; i< b2bl_hsize; i++) { if(!no_lock) lock_get(&b2bl_htable[i].lock); tuple = b2bl_htable[i].first; while(tuple) { /* check the state of the scenario instantiation */ if(tuple->db_flag == NO_UPDATEDB_FLAG) goto next; if(tuple->key == NULL) { LM_ERR("No key stored\n"); goto next; } if(tuple->bridge_entities[0]==NULL || tuple->bridge_entities[1]== NULL) { LM_ERR("Bridge entities is NULL\n"); if(tuple->bridge_entities[0]==NULL) LM_DBG("0 NULL\n"); else LM_DBG("1 NULL\n"); goto next; } qvals[0].val.str_val = *tuple->key; if(tuple->db_flag == INSERTDB_FLAG) { if(tuple->scenario) qvals[1].val.str_val = tuple->scenario->id; else{ qvals[1].val.str_val.len = 0; qvals[1].val.str_val.s = ""; } qvals[2].val.str_val = tuple->scenario_params[0]; qvals[3].val.str_val = tuple->scenario_params[1]; qvals[4].val.str_val = tuple->scenario_params[2]; qvals[5].val.str_val = tuple->scenario_params[3]; qvals[6].val.str_val = tuple->scenario_params[4]; qvals[7].val.str_val = tuple->sdp; } qvals[8].val.int_val = tuple->scenario_state; qvals[9].val.int_val = tuple->next_scenario_state; qvals[10].val.int_val = tuple->lifetime - get_ticks() + (int)time(NULL); qvals[11].val.int_val = tuple->bridge_entities[0]->type; qvals[12].val.str_val = tuple->bridge_entities[0]->scenario_id; qvals[13].val.str_val = tuple->bridge_entities[0]->to_uri; qvals[14].val.str_val = tuple->bridge_entities[0]->from_uri; qvals[15].val.str_val = tuple->bridge_entities[0]->key; qvals[16].val.int_val = tuple->bridge_entities[1]->type; qvals[17].val.str_val = tuple->bridge_entities[1]->scenario_id; qvals[18].val.str_val = tuple->bridge_entities[1]->to_uri; qvals[19].val.str_val = tuple->bridge_entities[1]->from_uri; qvals[20].val.str_val = tuple->bridge_entities[1]->key; n_insert_cols = 21; if(tuple->bridge_entities[2]) { qvals[21].val.int_val = tuple->bridge_entities[2]->type; qvals[22].val.str_val = tuple->bridge_entities[2]->scenario_id; qvals[23].val.str_val = tuple->bridge_entities[2]->to_uri; qvals[24].val.str_val = tuple->bridge_entities[2]->from_uri; qvals[25].val.str_val = tuple->bridge_entities[2]->key; } n_insert_cols = DB_COLS_NO; /* insert into database */ if(tuple->db_flag == INSERTDB_FLAG) { if(b2bl_dbf.insert(b2bl_db, qcols, qvals, n_insert_cols)< 0) { LM_ERR("Sql insert failed\n"); if(!no_lock) lock_release(&b2bl_htable[i].lock); return; } } else { /*do update */ if(b2bl_dbf.update(b2bl_db, qcols, 0, qvals, qcols+n_query_update, qvals+n_query_update, 1, DB_COLS_NO - n_query_update)< 0) { LM_ERR("Sql update failed\n"); if(!no_lock) lock_release(&b2bl_htable[i].lock); return; } } tuple->db_flag = NO_UPDATEDB_FLAG; next: tuple = tuple->next; } if(!no_lock) lock_release(&b2bl_htable[i].lock); } } static int b2bl_add_tuple(b2bl_tuple_t* tuple, str* params[]) { b2bl_tuple_t* shm_tuple= NULL; unsigned int hash_index, local_index; str* b2bl_key; b2bl_entity_id_t* entity; int i; b2b_notify_t cback; str* client_id = NULL; unsigned int logic_restored = 0; LM_DBG("Add tuple key [%.*s]\n", tuple->key->len, tuple->key->s); if(b2bl_parse_key(tuple->key, &hash_index, &local_index)< 0) { LM_ERR("Wrong formatted b2b logic key\n"); return -1; } shm_tuple = b2bl_insert_new(NULL, hash_index, tuple->scenario, params, (tuple->sdp.s?&tuple->sdp:NULL), NULL, local_index, &b2bl_key, UPDATEDB_FLAG); if(shm_tuple == NULL) { LM_ERR("Failed to insert new tuple\n"); return -1; } shm_tuple->lifetime = tuple->lifetime; lock_release(&b2bl_htable[hash_index].lock); shm_tuple->scenario_state= tuple->scenario_state; shm_tuple->next_scenario_state= tuple->next_scenario_state; /* add entities */ for(i=0; i< MAX_BRIDGE_ENT; i++) { if(!tuple->bridge_entities[i]->to_uri.len) continue; LM_DBG("Restore logic info for tuple:entity [%.*s][%d]\n", b2bl_key->len, b2bl_key->s, i); if(tuple->bridge_entities[i]->type == B2B_SERVER) cback = b2b_server_notify; else cback = b2b_client_notify; /* restore to the entities from b2b_entities module * the parameter and callback function */ if(b2b_api.restore_logic_info(tuple->bridge_entities[i]->type, &tuple->bridge_entities[i]->key, cback)< 0) LM_WARN("Failed to restore logic info for tuple:entity [%.*s][%d]\n", b2bl_key->len, b2bl_key->s, i); else logic_restored = 1; entity= b2bl_create_new_entity(tuple->bridge_entities[i]->type, &tuple->bridge_entities[i]->key,&tuple->bridge_entities[i]->to_uri, &tuple->bridge_entities[i]->from_uri, 0, &tuple->bridge_entities[i]->scenario_id, 0); if(client_id) pkg_free(client_id); if(entity == NULL) { LM_ERR("Failed to create entity %d\n", i); goto error; } shm_tuple->bridge_entities[i]= entity; /* put the pointer in clients or servers array */ // FIXME: check if the restore logic is ok if(tuple->bridge_entities[i]->type == B2B_SERVER) { if (shm_tuple->servers[0]) shm_tuple->servers[1] = entity; else shm_tuple->servers[0] = entity; } else { if (shm_tuple->clients[0]) shm_tuple->clients[1] = entity; else shm_tuple->clients[0] = entity; } } if(shm_tuple->bridge_entities[1]) shm_tuple->bridge_entities[1]->peer = shm_tuple->bridge_entities[0]; if(shm_tuple->bridge_entities[0]) shm_tuple->bridge_entities[0]->peer = shm_tuple->bridge_entities[1]; /* Mark tuple without entities as expired */ if(logic_restored==0) shm_tuple->lifetime = 1; return 0; error: shm_free(shm_tuple); return -1; } int b2b_logic_restore(void) { int i; int nr_rows; int _time; db_res_t *result= NULL; db_row_t *rows = NULL; db_val_t *row_vals= NULL; b2bl_tuple_t tuple; str b2bl_key; str scenario_id; b2bl_entity_id_t bridge_entities[3]; str* params[MAX_SCENARIO_PARAMS]; if(b2bl_db == NULL) { LM_DBG("NULL database connection\n"); return 0; } if(b2bl_dbf.use_table(b2bl_db, &b2bl_dbtable)< 0) { LM_ERR("sql use table failed\n"); return -1; } if (DB_CAPABILITY(b2bl_dbf, DB_CAP_FETCH)) { if(b2bl_dbf.query(b2bl_db, 0, 0, 0, qcols, 0, DB_COLS_NO, 0, 0) < 0) { LM_ERR("Error while querying (fetch) database\n"); return -1; } if(b2bl_dbf.fetch_result(b2bl_db,&result,B2BL_FETCH_SIZE)<0) { LM_ERR("fetching rows failed\n"); return -1; } } else { if (b2bl_dbf.query(b2bl_db, 0, 0, 0, qcols, 0, DB_COLS_NO, 0, &result) < 0) { LM_ERR("querying presentity\n"); return -1; } } nr_rows = RES_ROW_N(result); do { LM_DBG("loading [%i] records from db\n", nr_rows); rows = RES_ROWS(result); /* for every row */ for(i=0; i0); b2bl_dbf.free_result(b2bl_db, result); LM_DBG("Finished\n"); return 0; error: if(result) b2bl_dbf.free_result(b2bl_db, result); return -1; } void b2bl_db_insert(b2bl_tuple_t* tuple) { int ci; int i; qvals[0].val.str_val = *tuple->key; if(tuple->scenario) qvals[1].val.str_val = tuple->scenario->id; else{ qvals[1].val.str_val.len = 0; qvals[1].val.str_val.s = ""; } qvals[2].val.str_val = tuple->scenario_params[0]; qvals[3].val.str_val = tuple->scenario_params[1]; qvals[4].val.str_val = tuple->scenario_params[2]; qvals[5].val.str_val = tuple->scenario_params[3]; qvals[6].val.str_val = tuple->scenario_params[4]; qvals[7].val.str_val = tuple->sdp; qvals[8].val.int_val = tuple->scenario_state; qvals[9].val.int_val = tuple->next_scenario_state; qvals[10].val.int_val= tuple->lifetime - get_ticks() + (int)time(NULL); ci = 11; for(i = 0; i< 3; i++) { if(!tuple->bridge_entities[i]) break; qvals[ci++].val.int_val = tuple->bridge_entities[i]->type; qvals[ci++].val.str_val = tuple->bridge_entities[i]->scenario_id; qvals[ci++].val.str_val = tuple->bridge_entities[i]->to_uri; qvals[ci++].val.str_val = tuple->bridge_entities[i]->from_uri; qvals[ci++].val.str_val = tuple->bridge_entities[i]->key; } if(b2bl_dbf.use_table(b2bl_db, &b2bl_dbtable)< 0) { LM_ERR("sql use table failed\n"); return; } if(b2bl_dbf.insert(b2bl_db, qcols, qvals, ci)< 0) { LM_ERR("Sql insert failed\n"); } } void b2bl_db_update(b2bl_tuple_t* tuple) { int ci; int i; if(!tuple->key) { LM_ERR("No key found\n"); return; } LM_DBG("key= %.*s\n", tuple->key->len, tuple->key->s); qvals[0].val.str_val = *tuple->key; qvals[8].val.int_val = tuple->scenario_state; qvals[9].val.int_val = tuple->next_scenario_state; qvals[10].val.int_val = tuple->lifetime -get_ticks() + (int)time(NULL); ci = 11; for(i = 0; i< 3; i++) { if(!tuple->bridge_entities[i]) break; qvals[ci++].val.int_val = tuple->bridge_entities[i]->type; qvals[ci++].val.str_val = tuple->bridge_entities[i]->scenario_id; qvals[ci++].val.str_val = tuple->bridge_entities[i]->to_uri; qvals[ci++].val.str_val = tuple->bridge_entities[i]->from_uri; qvals[ci++].val.str_val = tuple->bridge_entities[i]->key; LM_DBG("UPDATE %.*s\n", qvals[ci-1].val.str_val.len, qvals[ci-1].val.str_val.s); } if(b2bl_dbf.use_table(b2bl_db, &b2bl_dbtable)< 0) { LM_ERR("sql use table failed\n"); return; } if(b2bl_dbf.update(b2bl_db, qcols, 0, qvals, qcols+n_query_update, qvals+n_query_update, 1, ci - n_query_update)< 0) { LM_ERR("Sql update failed\n"); } } opensips-2.2.2/modules/b2b_logic/b2bl_db.h000066400000000000000000000021641300170765700203170ustar00rootroot00000000000000/* * back-to-back logic module * * Copyright (C) 2011 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2011-04-04 initial version (Anca Vamanu) */ #ifndef B2BL_DB_H #define B2BL_DB_H #include "records.h" void b2b_logic_dump(int no_lock); int b2b_logic_restore(void); void b2bl_db_insert(b2bl_tuple_t* tuple); void b2bl_db_update(b2bl_tuple_t* tuple); #endif opensips-2.2.2/modules/b2b_logic/doc/000077500000000000000000000000001300170765700174225ustar00rootroot00000000000000opensips-2.2.2/modules/b2b_logic/doc/b2b_logic.xml000066400000000000000000000032001300170765700217610ustar00rootroot00000000000000 %docentities; ]> B2B_LOGIC &osipsname; Anca-Maria Vamanu &osips;
anca@opensips.org http://opensips.org
Anca-Maria Vamanu
anca@opensips.org
Ovidiu Sas
osas@voipembedded.com
2009 Anca-Maria Vamanu 2010 VoIP Embedded, Inc. $Revision: 8137 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/b2b_logic/doc/b2b_logic_admin.xml000066400000000000000000000447551300170765700231550ustar00rootroot00000000000000 &adminguide;
Overview The B2BUA implementation in OpenSIPS is separated in two layers: a lower one(coded in b2b_entities module)- which implements the basic functions of a UAS and UAC an upper one - which represents the logic engine of B2BUA, responsible of actually implementing the B2BUA services using the functions offered by the low level. This module is a B2BUA upper level implementation that can be used with b2b_entities module to have B2BUA that can be configured to provide some PBX services. The B2B services are coded in an XML scenario document. The b2b_logic module examines this document and uses the functions provided by the b2b_entities module to achieve the actions specified in the document and enable the service. A scenario can be instantiated in two ways: from the script - at the receipt of a initial message with a extern command (MI) command - the server will connect two end points in a session(Third Party Call Control).
Dependencies
&osips; Modules b2b_entities, a db module
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml2-dev
Exported Parameters
<varname>hash_size</varname> (int) The size of the hash table that stores the scenario instatiation entities. Default value is 9 (512 records). Set <varname>server_hsize</varname> parameter ... modparam("b2b_logic", "hash_size", 10) ...
<varname>script_scenario</varname> (str) This parameter should be set with the path of a document that contains a scenario that can be instantiated from the script at the receipt of an initial message. This parameter can be set more than once. Set <varname>script_scenario</varname> parameter ... modparam("b2b_logic", "script_scenario", "/usr/local/opensips/scripts/b2b_prepaid.xml") ...
<varname>extern_scenario</varname> (str) This parameter should be set with the path of a document that contains a scenario that can be instantiated with an MI command. This parameter can be set more than once. Set <varname>script_scenario</varname> parameter ... modparam("b2b_logic", "extern_scenario", "/usr/local/opensips/scripts/b2b_marketing.xml") ...
<varname>cleanup_period</varname> (int) The time interval at which to search for an hanged b2b context. A scenario is considered expired if the duration of a session exceeds the lifetime specified in the scenario. At that moment, BYE is sent in all the dialogs from that context and the context is deleted. Default value is 100. Set <varname>cleanup_period</varname> parameter ... modparam("b2b_logic", "cleanup_period", 60) ...
<varname>custom_headers_regexp</varname> (str) Regexp to search SIP header by names that should be passed from the dialog of one side to the other side. There are a number of headers that are passed by default. They are: Content-Type Supported Allow Proxy-Require Session-Expires Min-SE Require RSeq If you wish some other headers to be passed also you should define them by setting this parameter. It can be in forms like "regexp", "/regexp/" and "/regexp/flags". Meaning of the flags is as follows: i - Case insensitive search. e - Use extended regexp. Default value is NULL. Set <varname></varname> parameter ... modparam("b2b_logic", "custom_headers_regexp", "/^x-/i") ...
<varname>custom_headers</varname> (str) A list of SIP header names delimited by ';' that should be passed from the dialog of one side to the other side. There are a number of headers that are passed by default. They are: Max-Forwards (it is decreased by 1) Content-Type Supported Allow Proxy-Require Session-Expires Min-SE Require RSeq If you wish some other headers to be passed also you should define them by setting this parameter. Default value is NULL. Set <varname></varname> parameter ... modparam("b2b_logic", "custom_headers", "User-Agent;Date") ...
<varname>use_init_sdp</varname> (int) This parameter modifies the behaviour of the B2BUA when bridging and a provisional media uri is set. For playing media while the callee answers (that is connecting the caller to a media server), the bridging with the callee must start by sending an Invite to it. The correct way is to send an Invite without a body in this case, but it has been observed that not many gateways support this. So, the solution is to use the sdp received in the first Invite from the caller and put it as the body for this invite. By setting this parameter, this behavior is enabled. You can also set use_init_sdp per scenario and overwrite this global value. Default value is 0. Set <varname></varname> parameter ... modparam("b2b_logic", "use_init_sdp", 1) ...
<varname>db_url</varname> (str) Database URL. Set <varname>db_url</varname> parameter ... modparam("b2b_logic", "db_url", "mysql://opensips:opensipsrw@127.0.0.1/opensips") ...
<varname>update_period</varname> (int) The time interval at which to update the info in database. Default value is 100. Set <varname>update_period</varname> parameter ... modparam("b2b_logic", "update_period", 60) ...
<varname>max_duration</varname> (int) The maximum duration of a call. Default value is 12 * 3600 (12 hours). If you set it to 0, there will be no limitation. Set <varname>max_duration</varname> parameter ... modparam("b2b_logic", "max_duration", 7200) ...
<varname>b2bl_from_spec_param</varname> (string) The name of the pseudo variable for storing the new From header. The PV must be set before calling b2b_init_request. Default value is NULL (disabled). Set <varname>b2bl_from_spec_param</varname> parameter ... modparam("b2b_logic", "b2bl_from_spec_param", "$var(b2bl_from)") ... route{ ... # setting the From header $var(b2bl_from) = "\"Call ID\" <sip:user@opensips.org>"; ... b2b_init_request("top hiding"); ... }
<varname>server_address</varname> (str) The IP address of the machine that will be used as Contact in the generated messages. This is compulsory only when using external scenarios. For the script scenarios, if it is not set, it is constructed dynamically from the socket where the initiating request was received. This socket will be used to send all the requests, replies for that scenario instantiation. Set <varname>server_address</varname> parameter ... modparam("b2b_logic", "server_address", "sip:sa@10.10.10.10:5060") ...
<varname>init_callid_hdr</varname> (str) The module offers the possibility to insert the original callid in a header in the generated Invites. If you want this, set this parameter to the name of the header in which to insert the original callid. Set <varname>init_callid_hdr</varname> parameter ... modparam("b2b_logic", "init_callid_hdr", "Init-CallID") ...
<varname>db_mode</varname> (int) The B2B modules have support for the 3 type of database storage NO DB STORAGE - set this parameter to 0 WRITE THROUGH (synchronous write in database) - set this parameter to 1 WRITE BACK (update in db from time to time) - set this parameter to 2 Default value is 2 (WRITE BACK). Set <varname>db_mode</varname> parameter ... modparam("b2b_logic", "db_mode", 1) ...
<varname>db_table</varname> (str) Name of the database table to be used Default value is b2b_logic Set <varname>db_table</varname> parameter ... modparam("b2b_logic", "db_table", "some_table_name") ...
<varname>b2bl_th_init_timeout</varname> (int) Call setup timeout for topology hiding scenario. Default value is 60 Set <varname>b2bl_th_init_timeout</varname> parameter ... modparam("b2b_logic", "b2bl_th_init_timeout", 60) ...
Exported Functions
<function moreinfo="none">b2b_init_request</function> This is the function that must be called by the script writer on an initial INVITE for which a B2B scenario must be instantiated. It is up to the script writer to decide which are the criteria to decide for which messages certain scenarios must be instantiated. The first parameter is the identifier for the scenario and possible flags. This is defined in the XML document as an attribute of the root node or "top hiding" for internal topology hiding scenario. It can be passed as "scenario" or "scenario_name/flags". Meaning of the flags is as follows: t[nn] - Call setup timeout for topology hiding scenario. Example: t300. a - Transparent authentication. In this mode b2b passes your 401 or 407 authentication request to destination server. p - Preserve To: header. Then it can take at most 4 other parameters that represent the parameters for the xml scenario. The expected number of parameters is also specified as an attribute in the root node of the XML scenario. If you have a multi interface setup and want to chance the outbound interface, it is mandatory to use the "force_send_socket()" core function before passing control to b2b function. If you do not do it, the requests may be correctly routed, but the SIP pacakge may be invalid (as Contact, Via, etc). <function>b2b_init_request</function> usage ... if(is_method("INVITE") && !has_totag() && prepaid_user()) b2b_init_request("prepaid", "sip:320@opensips.org:5070", "sip:321@opensips.org:5070")); ...
<function moreinfo="none">b2b_bridge_request(b2bl_key,entity_no)</function> This function will bridge an initial INVITE with one of the particapnts from an existing b2b dialog. Parameters: b2bl_key - a pseudo-variable that contains the b2b_logic key entity_no - a pseudo-variable that holds the entity of the entity/participant to bridge. <function>b2b_bridge_request</function> usage ... modparam("b2b_entities", "script_req_route", "b2b_request") ... route[b2b_request] { # incoming requests from the B2B entities ... if ($ci~="^B2B") { #keep this aligned with b2b_key_prefix # request coming from the UAC side; # the Call-ID carries the B2B key ID if (is_method("BYE") { $var(entity) = 1; b2b_bridge_request("$ci","$var(entity)"); } } ... } ...
Exported MI Functions
<function moreinfo="none">b2b_trigger_scenario</function> This command instantiated a B2B scenario. Name: b2b_trigger_scenario Parameters: senario_id : the id of the scenario to be instantiated. scenario parameters - it can take 4 more parameters that are scenario parameters MI FIFO Command Format: :b2b_trigger_scenario:fifo_reply marketing sip:bob@opensips.org sip:322@opensips.org:5070 sip:alice@opensips.org _empty_line_
<function moreinfo="none">b2b_bridge</function> This command can be used by an external application to tell B2BUA to bridge a call party from an on going dialog to another destination. By default the caller is bridged to the new uri and BYE is set to the callee. You can instead bridge the callee if you send 1 as the third parameter. Also, if a fourth parameter is Name: b2b_bridge Parameters: dialog-id : the id of the dialog. If you set the module parameter dialog-id the server will include the dialogid in the generated Invites as the content of a header with name 'Dialog-ID'. new uri - the uri of the new destination flag to specify that the callee must be bridged to the new destination. It is optional. If not present the caller will be bridged. provisional media uri - the uri of a media server able to play provisional media starting from the beginning of the bridging scenario to the end of it. It is optional. If not present, no other entity will be envolved in the bridging scenario MI FIFO Command Format: :b2b_bridge:fifo_reply 1020.30 sip:alice@opensips.org _empty_line_ opensipsctl Command Format: opensipsctl b2b_bridge 1020.30 sip:alice@opensips.org
<function moreinfo="none">b2b_list</function> This command can be used to list the internals of b2b_logic entities. Name: b2b_list Parameters: none MI FIFO Command Format: :b2b_list:_reply_fifo_file_ _empty_line_ opensipsctl Command Format: opensipsctl fifo b2b_list
opensips-2.2.2/modules/b2b_logic/doc/b2b_logic_devel.xml000066400000000000000000000137401300170765700231520ustar00rootroot00000000000000 &develguide; The module provides an API that can be used from other &osips; modules. The API offers the functions for instantiating b2b scenarios from other modules (this comes as an addition to the other two means of instantiating b2b scenarios - from script and with an MI command). Also the instantiations can be dynamically controlled, by commanding the bridging of an entity involved in a call to another entity or the termination of the call or even bridging two existing calls.
<function moreinfo="none">b2b_logic_bind(b2bl_api_t* api)</function> This function binds the b2b_entities modules and fills the structure the exported functions that will be described in detail. <function>b2bl_api_t</function> structure ... typedef struct b2bl_api { b2bl_init_f init; b2bl_bridge_f bridge; b2bl_bridge_extern_f bridge_extern; b2bl_bridge_2calls_t bridge_2calls; b2bl_terminate_call_t terminate_call; b2bl_set_state_f set_state; b2bl_bridge_msg_t bridge_msg; }b2bl_api_t; ...
<function moreinfo="none">init</function> Field type: ... typedef str* (*b2bl_init_f)(struct sip_msg* msg, str* name, str* args[5], b2bl_cback_f, void* param); ... Initializing a b2b scenario. The last two parameters are the callback function and the parameter to be called in 3 situations that will be listed below. The callback function has the following definition: ... typedef int (*b2b_notify_t)(struct sip_msg* msg, str* id, int type, void* param); ... The first argument is the callback given in the init function. The second argument is a structure with some statistics about the call -start time, setup time, call time. The third argument is the current state of the scenario instantiation. The last argument is the event that triggered the callback. There are 3 events when the callback is called: when a BYE is received from either side- event parameter will also show from which side the BYE is received, so it can be B2B_BYE_E1 or B2B_BYE_E2 If while bridging, a negative reply is received from the second entity - the event is B2B_REJECT_E2. When the b2b logic entity is deleted- the evnet is B2B_DESTROY The return code controls what will happen with the request/reply that caused the event (except for the last event, when the return code does not matter) -1 - error 0 - drop the BYE or reply 1 - send the BYE or reply on the other side 2 - do what the scenario tells, if no rule defined send the BYE or reply on the other side
<function moreinfo="none">bridge</function> Field type: ... typedef int (*b2bl_bridge_f)(str* key, str* new_uri, str* new_from_dname,int entity_type); ... This function allows bridging an entity that is in a call handled by b2b_logic to another entity.
<function moreinfo="none">bridge_extern</function> Field type: ... typedef str* (*b2bl_bridge_extern_f)(str* scenario_name, str* args[5], b2bl_cback_f cbf, void* cb_param); ... This function allows initiating an extern scenario, when the B2BUA starts a call from the middle.
<function moreinfo="none">bridge_2calls</function> Field type: ... typedef int (*b2bl_bridge_2calls_t)(str* key1, str* key2); ... With this function it is possible to bridge two existing calls. The first entity from the two calls will be connected and BYE will be sent to their peers.
<function moreinfo="none">terminate_call</function> Field type: ... typedef int (*b2bl_terminate_call_t)(str* key); ... Terminate a call.
<function moreinfo="none">set_state</function> Field type: ... typedef int (*b2bl_set_state_f)(str* key, int state); ... Set the scenario state.
<function moreinfo="none">bridge_msg</function> Field type: ... typedef int (*b2bl_bridge_msg_t)(struct sip_msg* msg, str* key, int entity_no); ... This function allows bridging an incoming call to an entity from an existing call. The first argument is the INVITE message of the current incoming call. The second argument is the b2bl_key of an existing call. The third argument is the entity identifier.
opensips-2.2.2/modules/b2b_logic/logic.c000066400000000000000000003176371300170765700201370ustar00rootroot00000000000000/* * back-to-back logic module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #include #include #include #include "../../dprint.h" #include "../../dset.h" #include "../../error.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../parser/parse_content.h" #include "../../parser/parse_methods.h" #include "../../parser/parse_hname2.h" #include "../../parser/parse_refer_to.h" #include "../../parser/parse_replaces.h" #include "../../strcommon.h" #include "../../ut.h" #include "../../trim.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../msg_translator.h" #include "../b2b_entities/dlg.h" #include "../presence/hash.h" #include "../presence/utils_func.h" #include "records.h" #include "pidf.h" #include "b2b_logic.h" #include "b2bl_db.h" #define BUF_LEN 128 #define UPDATE_DBFLAG(dlg, flag) do{ \ if(dlg->db_flag==NO_UPDATEDB_FLAG) \ dlg->db_flag = UPDATEDB_FLAG; \ }while(0) static str cancel_reason_hdr= {"Reason: SIP;cause=200;text=\"Call completed elsewhere\"\r\n", 55}; extern b2b_scenario_t* script_scenarios; extern int b2bl_key_avp_name; extern unsigned short b2bl_key_avp_type; struct to_body* get_b2bl_from(struct sip_msg* msg); int b2b_scenario_parse_uri(xmlNodePtr value_node, char* value_content, b2bl_tuple_t* tuple, struct sip_msg* msg, str* client_to); int post_cb_sanity_check(b2bl_tuple_t **tuple, unsigned int hash_index, unsigned int local_index, b2bl_entity_id_t **entity, int etype, str *ekey); b2bl_entity_id_t* b2bl_search_entity(b2bl_tuple_t* tuple, str* key, int src, b2bl_entity_id_t*** head); int udh_to_uri(str user, str host, str port, str* uri); static str method_invite= {INVITE, INVITE_LEN}; static str method_ack = {ACK, ACK_LEN}; static str method_bye = {BYE, BYE_LEN}; static str method_cancel= {CANCEL, CANCEL_LEN}; static str ok = str_init("OK"); static str notAcceptable = str_init("Not Acceptable"); int entity_add_dlginfo(b2bl_entity_id_t* entity, b2b_dlginfo_t* dlginfo) { b2b_dlginfo_t* new_dlginfo= NULL; int size; size = sizeof(b2b_dlginfo_t)+ dlginfo->callid.len; if( dlginfo->totag.s) size += dlginfo->totag.len; if(dlginfo->fromtag.s) size+= dlginfo->fromtag.len; new_dlginfo = (b2b_dlginfo_t*)shm_malloc(size); if(new_dlginfo == NULL) { LM_ERR("No more shared memory\n"); return -1; } memset(new_dlginfo, 0, size); size = sizeof(b2b_dlginfo_t); if( dlginfo->totag.s) CONT_COPY(new_dlginfo, new_dlginfo->totag, dlginfo->totag); if(dlginfo->fromtag.s) CONT_COPY(new_dlginfo, new_dlginfo->fromtag, dlginfo->fromtag); CONT_COPY(new_dlginfo, new_dlginfo->callid, dlginfo->callid); entity->dlginfo = new_dlginfo; return 0; } int b2b_add_dlginfo(str* key, str* entity_key, int src, b2b_dlginfo_t* dlginfo) { b2bl_tuple_t* tuple; b2bl_entity_id_t* entity = NULL; b2bl_entity_id_t** ent_head = NULL; unsigned int hash_index, local_index; if(b2bl_parse_key(key, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key\n"); return -1; } lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("No entity found\n"); lock_release(&b2bl_htable[hash_index].lock); return -1; } /* a connected call */ if(max_duration) tuple->lifetime = get_ticks() + max_duration; else tuple->lifetime = 0; entity = b2bl_search_entity(tuple, entity_key, src, &ent_head); if(entity == NULL) { LM_ERR("No b2b_key match found\n"); lock_release(&b2bl_htable[hash_index].lock); return -1; } if(entity->dlginfo) { shm_free(entity->dlginfo); entity->dlginfo = NULL; } if(entity_add_dlginfo(entity, dlginfo) < 0) { LM_ERR("Failed to add dialoginfo\n"); lock_release(&b2bl_htable[hash_index].lock); return -1; } /* log the dialog pair */ if(entity->peer && entity->peer->dlginfo) { LM_INFO("Dialog pair: [%.*s] - [%.*s]\n", entity->peer->dlginfo->callid.len, entity->peer->dlginfo->callid.s, dlginfo->callid.len, dlginfo->callid.s); } lock_release(&b2bl_htable[hash_index].lock); return 0; } int msg_add_dlginfo(b2bl_entity_id_t* entity, struct sip_msg* msg, str* totag) { str callid, fromtag; b2b_dlginfo_t dlginfo; if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("failed to parse callid header\n"); return -1; } callid = msg->callid->body; if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); return -1; } } fromtag = ((struct to_body*)msg->from->parsed)->tag_value; dlginfo.totag = *totag; dlginfo.callid = callid; dlginfo.fromtag= fromtag; if(entity_add_dlginfo(entity, &dlginfo) < 0) { LM_ERR("Failed to add dialoginfo\n"); return -1; } return 0; } int b2b_msg_get_to(struct sip_msg* msg, str* to_uri, int flags) { struct to_body *pto; if (flags & B2BL_FLAG_TRANSPARENT_TO) { if (!msg->to || !msg->to->body.s) { LM_ERR("cannot find 'to' header!\n"); return -1; } if (msg->to->parsed == NULL) { if ( parse_to_uri( msg )<0 ) { LM_ERR("cannot parse To header\n"); return -1; } } pto = (struct to_body*)msg->to->parsed; pkg_str_dup(to_uri, &pto->uri); } else { if( parse_sip_msg_uri(msg)< 0) { LM_ERR("failed to parse R-URI\n"); return -1; } if(udh_to_uri(msg->parsed_uri.user, msg->parsed_uri.host, msg->parsed_uri.port, to_uri)< 0) { LM_ERR("failed to construct uri from user and domain\n"); return -1; } } return 0; } int b2b_msg_get_from(struct sip_msg* msg, str* from_uri, str* from_dname) { struct to_body *pfrom; pfrom = get_b2bl_from(msg); if (pfrom) { *from_uri = pfrom->uri; *from_dname = pfrom->display; return 0; } /* examine the from header */ if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); return -1; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); return -1; } } pfrom = (struct to_body*)msg->from->parsed; *from_uri = pfrom->uri; *from_dname = pfrom->display; return 0; } b2bl_entity_id_t* b2bl_create_new_entity(enum b2b_entity_type type, str* entity_id, str* to_uri,str* from_uri,str*from_dname, str* ssid, struct sip_msg* msg) { unsigned int size; b2bl_entity_id_t* entity; size = sizeof(b2bl_entity_id_t) + ((ssid!=NULL)?ssid->len:0) + ((entity_id!=NULL)?entity_id->len:0)+ ((to_uri !=NULL)?to_uri->len:0) + ((from_uri!=NULL)?from_uri->len:0)+ ((from_dname!=NULL)?from_dname->len:0); entity = (b2bl_entity_id_t*)shm_malloc(size); if(entity == NULL) { LM_ERR("No more shared memory\n"); return NULL; } memset(entity, 0, size); size = sizeof(b2bl_entity_id_t); if(entity_id) { entity->key.s= (char*)entity+ size; memcpy(entity->key.s, entity_id->s, entity_id->len); entity->key.len= entity_id->len; size+= entity_id->len; } //CONT_COPY_P(entity, entity->key, entity_id); if(ssid) { entity->scenario_id.s= (char*)entity+ size; memcpy(entity->scenario_id.s, ssid->s, ssid->len); entity->scenario_id.len= ssid->len; size+= ssid->len; } //CONT_COPY_P(entity, entity->scenario_id, ssid); if(to_uri) { entity->to_uri.s= (char*)entity+ size; memcpy(entity->to_uri.s, to_uri->s, to_uri->len); entity->to_uri.len= to_uri->len; size+= to_uri->len; } //CONT_COPY_P(entity, entity->to_uri, to_uri); if(from_uri) { entity->from_uri.s= (char*)entity+ size; memcpy(entity->from_uri.s, from_uri->s, from_uri->len); entity->from_uri.len= from_uri->len; size+= from_uri->len; } // CONT_COPY_P(entity, entity->from_uri, from_uri); if(from_dname) { entity->from_dname.s= (char*)entity+ size; memcpy(entity->from_dname.s, from_dname->s, from_dname->len); entity->from_dname.len= from_dname->len; size+= from_dname->len; } entity->type = type; if(type == B2B_SERVER && msg) { if( msg_add_dlginfo(entity, msg, entity_id)< 0 ) { LM_ERR("Failed to add dialog information to b2b_logic entity\n"); shm_free(entity); return NULL; } } entity->stats.start_time = get_ticks(); entity->stats.call_time = 0; LM_DBG("new entity type [%d] [%p]->[%.*s]\n", entity->type, entity, entity->key.len, entity->key.s); return entity; } static inline int bridge_get_entityno(b2bl_tuple_t* tuple, b2bl_entity_id_t* entity) { int i; /*check to which entity the reply belongs to */ for(i = 0; i< 3; i++) { if(tuple->bridge_entities[i]== entity) return i; } return -1; } void b2b_end_dialog(b2bl_entity_id_t* bentity, b2bl_tuple_t* tuple) { str *method; b2b_req_data_t req_data; if(!bentity) return; if (bentity->next || bentity->prev) { LM_ERR("Inconsistent state for entity [%p]\n", bentity); b2bl_print_tuple(tuple, L_ERR); return; } if(bentity->key.s) { if(!bentity->disconnected) { if(bentity->state == B2BL_ENT_CONFIRMED) { method = &method_bye; } else { method = &method_cancel; } memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(bentity); req_data.method =method; b2b_api.send_request(&req_data); bentity->disconnected = 1; } } else { LM_DBG("It is not connected yet - delete\n"); b2bl_delete_entity(bentity, tuple); } } void b2b_mark_todel( b2bl_tuple_t* tuple) { tuple->to_del = 1; tuple->lifetime = 30 + get_ticks(); tuple->scenario_state = B2B_CANCEL_STATE; LM_DBG("%p\n", tuple); } int process_bridge_dialog_end(b2bl_tuple_t* tuple, int entity_no, b2bl_entity_id_t* bentity) { if(entity_no == 0) /* if a negative reply received from the server */ { /* send cancel or bye to the peers */ b2b_end_dialog(tuple->bridge_entities[1], tuple); b2b_end_dialog(tuple->bridge_entities[2], tuple); b2b_mark_todel(tuple); } else if(entity_no == 1) { /* if the media server in 2 stage connecting did not reply */ if(tuple->bridge_entities[2]) { /* media server did not reply with success */ b2bl_delete_entity(bentity, tuple); tuple->bridge_entities[1] = tuple->bridge_entities[0]; tuple->bridge_entities[0] = tuple->bridge_entities[2]; tuple->bridge_entities[2] = NULL; tuple->bridge_entities[1]->peer = tuple->bridge_entities[0]; tuple->bridge_entities[0]->peer = tuple->bridge_entities[1]; } else { /* the entity to connect replied with negative reply */ b2b_end_dialog(tuple->bridge_entities[0], tuple); b2b_mark_todel(tuple); } } else { /* if the final destination replied with negative reply */ b2b_end_dialog(tuple->bridge_entities[0], tuple); b2b_end_dialog(tuple->bridge_entities[1], tuple); b2b_mark_todel(tuple); } return 0; } int process_bridge_bye(struct sip_msg* msg, b2bl_tuple_t* tuple, b2bl_entity_id_t* entity) { int entity_no; b2b_rpl_data_t rpl_data; entity_no = bridge_get_entityno(tuple, entity); if(entity_no < 0) { LM_ERR("No match found\n"); return -1; } memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(entity); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); return process_bridge_dialog_end(tuple, entity_no, entity); } int process_bridge_negreply(b2bl_tuple_t* tuple, unsigned int hash_index, b2bl_entity_id_t* entity, struct sip_msg* msg) { int entity_no; int ret; unsigned int local_index; b2bl_cback_f cbf = NULL; str ekey={NULL, 0}; b2bl_cb_params_t cb_params; b2bl_dlg_stat_t stats; entity_no = bridge_get_entityno(tuple, entity); switch (entity_no) { case 0: /* mark that the first step of the bridging failed */ tuple->scenario_state = B2B_NONE; break; case 1: break; default: LM_ERR("unexpected entity_no [%d] for tuple [%p]\n", entity_no, tuple); return -1; } /* call the callback for brigding failure */ cbf = tuple->cbf; if(cbf && (tuple->cb_mask&B2B_REJECT_CB)) { memset(&cb_params, 0, sizeof(b2bl_cb_params_t)); cb_params.param = tuple->cb_param; local_index = tuple->id; stats.start_time = entity->stats.start_time; stats.setup_time = get_ticks() - entity->stats.start_time; stats.key.s = NULL; stats.key.len = 0; ekey.s = (char*)pkg_malloc(entity->key.len); if(ekey.s == NULL) { LM_ERR("No more memory\n"); return -1; } memcpy(ekey.s, entity->key.s, entity->key.len); ekey.len = entity->key.len; cb_params.stat = &stats; cb_params.msg = msg; cb_params.entity = entity_no; lock_release(&b2bl_htable[hash_index].lock); ret = cbf(&cb_params, B2B_REJECT_CB); LM_DBG("ret = %d\n", ret); lock_get(&b2bl_htable[hash_index].lock); /* must search the tuple again * you can't know what might have happened with it */ if (0!=post_cb_sanity_check(&tuple, hash_index, local_index, &entity, entity->type, &ekey)) { pkg_free(ekey.s); return 1; } pkg_free(ekey.s); if(ret == B2B_DROP_MSG_CB_RET) { /* drop the negative reply */ if(entity_no == 1) b2bl_delete_entity(entity, tuple); return 1; } } return process_bridge_dialog_end(tuple, entity_no, entity); } b2bl_entity_id_t* b2bl_new_client(str* to_uri, str* from_uri, b2bl_tuple_t* tuple, str* ssid, struct sip_msg* msg) { client_info_t ci; str* client_id; b2bl_entity_id_t* entity; memset(&ci, 0, sizeof(client_info_t)); ci.method = method_invite; ci.to_uri = *to_uri; ci.from_uri = *from_uri; ci.extra_headers = tuple->extra_headers; ci.body = (tuple->sdp.s?&tuple->sdp:NULL); ci.from_tag = NULL; ci.send_sock = msg?(msg->force_send_socket?msg->force_send_socket:msg->rcv.bind_address):NULL; if (ci.send_sock) get_local_contact( ci.send_sock, &ci.local_contact); else ci.local_contact = server_address; if(msg) { if (str2int( &(get_cseq(msg)->number), &ci.cseq)!=0 ) { LM_ERR("cannot parse cseq number\n"); return NULL; } } LM_DBG("Send Invite without a body to a new client entity\n"); client_id = b2b_api.client_new(&ci, b2b_client_notify, b2b_add_dlginfo, tuple->key); if(client_id == NULL) { LM_ERR("Failed to create client id\n"); return NULL; } /* save the client_id in the structure */ entity = b2bl_create_new_entity(B2B_CLIENT, client_id, &ci.to_uri, &ci.from_uri, 0, ssid, 0); if(entity == NULL) { LM_ERR("failed to create new client entity\n"); pkg_free(client_id); return NULL; } pkg_free(client_id); return entity; } int process_bridge_200OK(struct sip_msg* msg, str* extra_headers, str* body, b2bl_tuple_t* tuple, b2bl_entity_id_t* entity) { str* client_id; b2bl_entity_id_t* bentity0, *bentity1; client_info_t ci; int entity_no; b2b_req_data_t req_data; bentity0 = tuple->bridge_entities[0]; bentity1 = tuple->bridge_entities[1]; if(bentity0 == NULL) { LM_ERR("Bridge entities 0 is NULL\n"); b2b_mark_todel(tuple); return -1; } entity_no = bridge_get_entityno(tuple, entity); if(entity_no < 0) { LM_ERR("No match found\n"); return -1; } LM_DBG("entity_no = %d, entity=%p, be[0]= %p\n", entity_no, entity, tuple->bridge_entities[0]); if(entity_no == 0) /* the first reply -> must send INVITE on the other side */ { if(bentity1->key.s && bentity1->state < B2BL_ENT_CONFIRMED) /* already been in this step*/ { LM_ERR("A retransmission of the reply from the first leg\n"); return -1; } else if (bentity1->state == B2BL_ENT_CONFIRMED && bentity0->sdp_type == B2BL_SDP_NORMAL) { /* * if there is a 200 OK, from the first entity, and the second entity * is already confirmed, then this means that it was a reply from the * last re-invite, used to fix his SDP - simply ACK it */ /* send ACK without a body to the first entity */ memset(&req_data, 0, sizeof(b2b_req_data_t)); req_data.et =bentity0->type; req_data.b2b_key =&bentity0->key; req_data.method =&method_ack; req_data.dlginfo =bentity0->dlginfo; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Failed to send second ACK in bridging scenario\n"); return -1; } /* mark the scenario as completed */ if(tuple->next_scenario_state>= 0) { tuple->scenario_state = tuple->next_scenario_state; tuple->next_scenario_state = 0; LM_DBG("Updated tuple state = %d\n", tuple->scenario_state); } else tuple->scenario_state = B2B_NOTDEF_STATE; LM_DBG("Finished the bridging\n"); } else if(bentity1->type == B2B_CLIENT && bentity1->state!=B2BL_ENT_CONFIRMED) { LM_DBG("Send invite to %.*s\n", bentity1->to_uri.len, bentity1->to_uri.s); memset(&ci, 0, sizeof(client_info_t)); ci.method = method_invite; ci.to_uri = bentity1->to_uri; /* it matters if the entity is server or client */ if(bentity0->type == B2B_CLIENT) { ci.from_uri = bentity0->to_uri; } else if(bentity0->type == B2B_SERVER) { if(bentity1->from_uri.s) ci.from_uri = bentity1->from_uri; else ci.from_uri = bentity0->from_uri; if(bentity1->from_dname.s) ci.from_dname = bentity1->from_dname; else ci.from_dname = bentity0->from_dname; LM_DBG("From dname: %.*s\n", ci.from_dname.len, ci.from_dname.s); } ci.extra_headers = extra_headers; ci.body = body; ci.from_tag = NULL; ci.send_sock = msg->force_send_socket?msg->force_send_socket:msg->rcv.bind_address; get_local_contact( ci.send_sock, &ci.local_contact); if (str2int( &(get_cseq(msg)->number), &ci.cseq)!=0 ) { LM_ERR("cannot parse cseq number\n"); return -1; } bentity0->state = B2BL_ENT_CONFIRMED; client_id = b2b_api.client_new(&ci, b2b_client_notify, b2b_add_dlginfo, tuple->key); if(client_id == NULL) { LM_ERR("Failed to create new client entity\n"); return -1; } /* save the client_id in the structure */ entity = b2bl_create_new_entity(B2B_CLIENT, client_id, &ci.to_uri, &ci.from_uri, 0, &bentity1->scenario_id, 0); if(entity == NULL) { LM_ERR("failed to create new client entity\n"); pkg_free(client_id); return -1; } entity->no =1; pkg_free(client_id); b2bl_delete_entity(bentity1, tuple); tuple->bridge_entities[1] = entity; if (0 != b2bl_add_client(tuple, entity)) return -1; } else { /* send reInvite */ bentity1->stats.start_time = get_ticks(); bentity1->stats.call_time = 0; memset(&req_data, 0, sizeof(b2b_req_data_t)); req_data.et =bentity1->type; req_data.b2b_key =&bentity1->key; req_data.method =&method_invite; req_data.extra_headers =extra_headers; req_data.body =body; req_data.dlginfo =bentity1->dlginfo; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Failed to send second INVITE in bridging scenario\n"); return -1; } bentity1->sdp_type = body ? B2BL_SDP_NORMAL : B2BL_SDP_LATE; bentity1->state = B2BL_ENT_NEW; } tuple->bridge_entities[1]->peer = tuple->bridge_entities[0]; tuple->bridge_entities[0]->peer = tuple->bridge_entities[1]; /* store this sdp */ if(tuple->b1_sdp.s) shm_free(tuple->b1_sdp.s); if (tuple->b1_sdp.s==tuple->sdp.s) tuple->sdp.s = 0; tuple->b1_sdp.s = 0; if(body) { tuple->b1_sdp.s = (char*)shm_malloc(body->len); if(tuple->b1_sdp.s == NULL) { LM_ERR("No more memory\n"); return -1; } memcpy(tuple->b1_sdp.s, body->s, body->len); tuple->b1_sdp.len = body->len; /* XXX: make sure this is safe */ if (tuple->sdp.s && tuple->b1_sdp.s != tuple->sdp.s) shm_free(tuple->sdp.s); tuple->sdp = tuple->b1_sdp; } } else if(entity_no == 1) /* from provisional media server or from final destination */ { /* the second -> send ACK with body to the first entity and ACK without a body to the second entity*/ bentity1->state = B2BL_ENT_CONFIRMED; bentity1->stats.setup_time = get_ticks() - bentity1->stats.start_time; bentity1->stats.start_time = get_ticks(); bentity0->stats.setup_time = get_ticks() - bentity0->stats.start_time; bentity0->stats.start_time = get_ticks(); memset(&req_data, 0, sizeof(b2b_req_data_t)); req_data.et =bentity0->type; req_data.b2b_key =&bentity0->key; req_data.method =&method_ack; req_data.extra_headers =extra_headers; req_data.body = (bentity0->sdp_type == B2BL_SDP_LATE) ? body : 0; req_data.dlginfo =bentity0->dlginfo; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Failed to send first ACK in bridging scenario\n"); return -1; } /* send ACK without a body to the second entity */ memset(&req_data, 0, sizeof(b2b_req_data_t)); req_data.et =bentity1->type; req_data.b2b_key =&bentity1->key; req_data.method =&method_ack; req_data.dlginfo =bentity1->dlginfo; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Failed to send second ACK in bridging scenario\n"); return -1; } tuple->bridge_entities[1]->peer = tuple->bridge_entities[0]; tuple->bridge_entities[0]->peer = tuple->bridge_entities[1]; /* now I have finnished the BRIDGING scenario -> mark this in the record */ if(tuple->bridge_entities[2] == NULL) { /* if there was a renew SDP type, we have to challenge the first * entity again with an invite, just to update its SDP info */ if (bentity0->sdp_type == B2BL_SDP_RENEW) { memset(&req_data, 0, sizeof(b2b_req_data_t)); req_data.et =bentity0->type; req_data.b2b_key =&bentity0->key; req_data.method =&method_invite; req_data.extra_headers = extra_headers; req_data.body = body; req_data.dlginfo =bentity0->dlginfo; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Failed to send re-invite in bridging scenario\n"); return -1; } /* after sending this invite, the first endpoint should have * the proper SDP used */ bentity0->sdp_type = B2BL_SDP_NORMAL; } else { /* bridging scenario should be done */ if(tuple->next_scenario_state>= 0) { tuple->scenario_state = tuple->next_scenario_state; tuple->next_scenario_state = 0; LM_DBG("Updated tuple state = %d\n", tuple->scenario_state); } else tuple->scenario_state = B2B_NOTDEF_STATE; LM_DBG("Finished the bridging\n"); } } else { /* contact the real destination */ entity = b2bl_new_client(&tuple->bridge_entities[2]->to_uri, &bentity0->from_uri, tuple, &tuple->bridge_entities[2]->scenario_id, msg); if(entity == NULL) { LM_ERR("Failed to generate new client\n"); return -1; } entity->no = 1; b2bl_delete_entity(tuple->bridge_entities[2], tuple); if (0 != b2bl_add_client(tuple, entity)) return -1; /* original destination connected in the second step */ tuple->bridge_entities[2]= entity; } } else /* if a 200 OK from the final destination */ { b2b_end_dialog(bentity1, tuple); /* send reinvite to the initial server*/ bentity0->stats.setup_time = get_ticks() - bentity0->stats.start_time; bentity0->stats.start_time = get_ticks(); bentity0->sdp_type = B2BL_SDP_NORMAL; memset(&req_data, 0, sizeof(b2b_req_data_t)); req_data.et =bentity0->type; req_data.b2b_key =&bentity0->key; req_data.method =&method_invite; req_data.extra_headers =extra_headers; req_data.body =body; req_data.dlginfo =bentity0->dlginfo; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Failed to send second Invite in bridging scenario\n"); return -1; } bentity0->state = 0; tuple->bridge_entities[1] = tuple->bridge_entities[0]; tuple->bridge_entities[0] = tuple->bridge_entities[2]; tuple->bridge_entities[2] = NULL; tuple->bridge_entities[1]->peer = tuple->bridge_entities[0]; tuple->bridge_entities[0]->peer = tuple->bridge_entities[1]; } return 0; } b2bl_entity_id_t* b2bl_search_entity(b2bl_tuple_t* tuple, str* key, int src, b2bl_entity_id_t*** head) { int index; b2bl_entity_id_t* e; /* search the entity */ if(src == B2B_SERVER) for (index = 0; index < MAX_B2BL_ENT; index++) { *head = &tuple->servers[index]; e= tuple->servers[index]; while (e) { LM_DBG("Key [%.*s]\n",e->key.len,e->key.s); if (e->key.len == key->len && strncmp(e->key.s, key->s, key->len) == 0) return e; e = e->next; } } else for (index = 0; index < MAX_B2BL_ENT; index++) { *head = &tuple->clients[index]; e = tuple->clients[index]; while (e) { LM_DBG("Key [%.*s]\n",e->key.len,e->key.s); if (e->key.len == key->len && strncmp(e->key.s, key->s, key->len) == 0) return e; e = e->next; } } return e; } int post_cb_sanity_check(b2bl_tuple_t **tuple, unsigned int hash_index, unsigned int local_index, b2bl_entity_id_t **entity, int etype, str *ekey) { int index; int not_found = 1; b2bl_entity_id_t *e; *tuple = b2bl_search_tuple_safe(hash_index, local_index); if(*tuple == NULL) { LM_DBG("B2B logic record doesn't exist after B2B_BYE_CB\n"); return -1; } if(etype == B2B_SERVER) { for (index = 0; index < MAX_B2BL_ENT && not_found; index++) { e = (*tuple)->servers[index]; while (e) { if(e == *entity && e->key.len == ekey->len && strncmp(e->key.s, ekey->s, ekey->len)==0) { not_found = 0; break; } e = e->next; } } if(not_found) { LM_DBG("Server Entity does not exist anymore\n"); return -2; } else { return 0; } } else if(etype == B2B_CLIENT) { for (index = 0; index < MAX_B2BL_ENT && not_found; index++) { e = (*tuple)->clients[index]; while (e) { LM_DBG("[%p] vs [%p]\n", e, *entity); if (e && ekey) LM_DBG("[%.*s] vs [%.*s]\n", e->key.len, e->key.s, ekey->len, ekey->s); if(e == *entity && e->key.len == ekey->len && strncmp(e->key.s, ekey->s, ekey->len)==0) { not_found = 0; break; } e = e->next; } } if(not_found) { LM_DBG("Client Entity does not exist anymore\n"); return -3; } else { return 0; } } else { LM_ERR("Unexpected entity type [%d]\n", etype); return -4; } return -5; } #define SEND_REPLY_TO_PEER_OR_GOTO_DONE \ do{ \ memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); \ rpl_data.et =peer->type; \ rpl_data.b2b_key =&peer->key; \ rpl_data.method =method_value; \ rpl_data.code =statuscode; \ rpl_data.text =&msg->first_line.u.reply.reason; \ rpl_data.body = body->s?body:NULL; \ rpl_data.extra_headers = extra_headers->s?extra_headers:NULL;\ rpl_data.dlginfo =peer->dlginfo; \ if(b2b_api.send_reply(&rpl_data) < 0) \ { \ LM_ERR("Sending reply failed - %d, [%.*s]\n", \ statuscode, peer->key.len, peer->key.s);\ goto done; \ } \ }while(0) int b2b_logic_notify_reply(int src, struct sip_msg* msg, str* key, str* body, str* extra_headers, str* b2bl_key, unsigned int hash_index, unsigned int local_index) { b2bl_tuple_t* tuple; str method; b2b_scenario_t* scenario; b2bl_entity_id_t *entity, *peer, *e, *ent; b2bl_entity_id_t** entity_head = NULL; int statuscode; int ret; unsigned int method_value; int_str avp_val; b2bl_cback_f cbf = NULL; str ekey= {NULL, 0}; b2bl_cb_params_t cb_params; b2b_req_data_t req_data; b2b_rpl_data_t rpl_data; b2b_dlginfo_t dlginfo; if (parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse message\n"); return -1; } lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("B2B logic record not found\n"); goto error; } scenario = tuple->scenario; entity = b2bl_search_entity(tuple, key, src, &entity_head); if(entity == NULL) { LM_ERR("No b2b_key match found [%.*s], src=%d\n", key->len, key->s, src); goto error; } if (entity->no > 1) { LM_ERR("unexpected entity->no [%d] for tuple [%p]\n", entity->no, tuple); goto error; } peer = entity->peer; LM_DBG("b2b_entity key = %.*s\n", key->len, key->s); if (b2bl_key_avp_name >= 0) { destroy_avps( b2bl_key_avp_type, b2bl_key_avp_name, 1); avp_val.s = *b2bl_key; if(add_avp(AVP_VAL_STR|b2bl_key_avp_type, b2bl_key_avp_name, avp_val)!=0) { LM_ERR("failed to build b2bl_key avp\n"); return -1; } } method = get_cseq(msg)->method; if(parse_method(method.s, method.s+method.len, &method_value)< 0) { LM_ERR("Failed to parse method\n"); goto error; } statuscode = msg->first_line.u.reply.statuscode; /* if a disconnected entity -> do nothing */ if(entity->disconnected) { LM_DBG("entity [%.*s] is disconnected\n", key->len, key->s); b2bl_delete_entity(entity, tuple); if(tuple->to_del && tuple->clients[0]==NULL && tuple->clients[1]==NULL && tuple->servers[0]==NULL && tuple->servers[1]==NULL) { LM_DBG("Received reply and there are no more entities-> delete\n"); b2bl_delete(tuple, hash_index, 0); tuple = 0; } goto done; } /* if a reply from the client side was received, * tell the server side to send a reply also */ if((scenario && scenario->reply_rules) || tuple->scenario_state == B2B_BRIDGING_STATE) { if(tuple->scenario_state == B2B_BRIDGING_STATE) /* if in a predefined state */ { LM_DBG("Received a reply [%d] while in BRIDGING scenario\n", statuscode); /* if the scenario state is B2B_BRIDGING_STATE -> we should have a reply for INVITE */ /* extract the method from Cseq header */ if(method_value != METHOD_INVITE) { LM_ERR("Wrong scenario state [B2B_BRIDGING_STATE] for this" " reply(for method %d)\n", method_value); goto error; } /* if a negative reply */ if(statuscode >= 300) { ret = process_bridge_negreply(tuple, hash_index, entity, msg); if(ret < 0) { LM_ERR("Failed to process negative reply while in bridging state\n"); goto error; } else if(ret == 1) goto done1; if(!entity->peer || !entity->peer->key.s) { LM_DBG("Delete this b2bl record\n"); b2bl_delete(tuple, hash_index, 0); tuple = 0; } goto done; } else if(statuscode < 200) { goto done; } /* if a reply with 200 OK -> we have two possibilities- either the first 200OK or the final */ if(process_bridge_200OK(msg, tuple->extra_headers, (body->s?body:0), tuple, entity)< 0) { LM_ERR("Failed to process bridging 200OK for Invite\n"); goto error; } } if(scenario && scenario->reply_rules) { /* TODO -> process and apply reply rules */ } if(statuscode >= 300) { tuple->to_del = 1; tuple->lifetime = 30 + get_ticks(); } } else { if(!peer) { LM_DBG("No peer found\n"); goto done; } switch (method_value) { case METHOD_BYE: /* if no other scenario rules defined and this is the reply for BYE */ SEND_REPLY_TO_PEER_OR_GOTO_DONE; LM_DBG("Received reply for BYE - delete\n"); b2bl_delete(tuple, hash_index, 0); tuple = 0; goto done; break; case METHOD_INVITE: if(entity->state!=B2BL_ENT_CONFIRMED) { if(statuscode >= 300) { b2bl_print_tuple(tuple, L_DBG); if (entity->prev || entity->next) { LM_DBG("detaching entity[%p] from tuple[%p]\n", entity, tuple); b2bl_remove_single_entity(entity, entity_head); peer->peer = *entity_head; tuple->bridge_entities[0] = tuple->servers[0]; tuple->bridge_entities[1] = tuple->clients[0]; } else { SEND_REPLY_TO_PEER_OR_GOTO_DONE; LM_DBG("Negative reply [%d] - delete[%p]\n", statuscode, tuple); b2b_mark_todel(tuple); } b2bl_print_tuple(tuple, L_DBG); } else if(statuscode >= 200 && statuscode < 300) { b2bl_print_tuple(tuple, L_DBG); if (entity->prev || entity->next) { unchain_ent(entity, entity_head); /* send CANCEL to all other entities in the list */ e = *entity_head; while (e) { LM_DBG("Send request [%.*s]" " to entity [%.*s]\n", method_cancel.len, method_cancel.s, e->key.len, e->key.s); memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(e); req_data.method =&method_cancel; req_data.extra_headers = &cancel_reason_hdr; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Sending request" " failed [%.*s]\n", e->key.len, e->key.s); } b2b_api.entity_delete(e->type, &e->key, e->dlginfo, 0); LM_DBG("destroying dlginfo=[%p]\n", e->dlginfo); if(e->dlginfo) shm_free(e->dlginfo); ent = e->next; shm_free(e); e = ent; } *entity_head = entity; peer->peer = entity; tuple->bridge_entities[0] = tuple->servers[0]; tuple->bridge_entities[1] = tuple->clients[0]; } SEND_REPLY_TO_PEER_OR_GOTO_DONE; entity->state = B2BL_ENT_CONFIRMED; peer->state = B2BL_ENT_CONFIRMED; b2bl_print_tuple(tuple, L_DBG); entity->stats.setup_time = get_ticks() - entity->stats.start_time; entity->stats.start_time = get_ticks(); cbf = tuple->cbf; if(cbf && (tuple->cb_mask&B2B_CONFIRMED_CB)) { /* saving the entity key for later sanity check */ ekey.s = (char*)pkg_malloc(entity->key.len); if(ekey.s == NULL) { LM_ERR("No more memory\n"); goto error; } ekey.len = entity->key.len; memcpy(ekey.s, entity->key.s, entity->key.len); /* preparing the cb params */ memset(&cb_params, 0, sizeof(b2bl_cb_params_t)); cb_params.param = tuple->cb_param; cb_params.stat = NULL; cb_params.msg = msg; cb_params.entity = entity->no; lock_release(&b2bl_htable[hash_index].lock); ret = cbf(&cb_params, B2B_CONFIRMED_CB); lock_get(&b2bl_htable[hash_index].lock); /* must search the tuple again * you can't know what might have happened with it */ if (0!=post_cb_sanity_check(&tuple, hash_index, local_index, &entity, entity->type, &ekey)) { pkg_free(ekey.s); goto error; } pkg_free(ekey.s); peer = entity->peer; } } else { /* Provisional replies end up here */ if((entity->dlginfo) && !entity->dlginfo->fromtag.s) { if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("failed to parse callid header\n"); goto error; } dlginfo.callid = msg->callid->body; if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); return -1; } } dlginfo.totag = ((struct to_body*)msg->from->parsed)->tag_value; dlginfo.fromtag = get_to(msg)->tag_value; shm_free(entity->dlginfo); entity->dlginfo = NULL; if(entity_add_dlginfo(entity, &dlginfo) < 0) { LM_ERR("Failed to add dialoginfo\n"); goto error; } } SEND_REPLY_TO_PEER_OR_GOTO_DONE; } } else { /* if reINVITE and 481 or 408 reply */ SEND_REPLY_TO_PEER_OR_GOTO_DONE; if(statuscode==481 || statuscode==408) { LM_DBG("Received terminate dialog reply for reINVITE\n"); tuple->lifetime = 30 + get_ticks(); } } break; default: SEND_REPLY_TO_PEER_OR_GOTO_DONE; } } done: if(tuple) { if(b2bl_db_mode == WRITE_THROUGH) b2bl_db_update(tuple); else UPDATE_DBFLAG(tuple, tuple->db_flag); } done1: lock_release(&b2bl_htable[hash_index].lock); return 0; error: lock_release(&b2bl_htable[hash_index].lock); return -1; } int b2b_logic_notify_request(int src, struct sip_msg* msg, str* key, str* body, str* extra_headers, str* b2bl_key, unsigned int hash_index, unsigned int local_index) { b2bl_tuple_t* tuple; str method; b2b_scenario_t* scenario; b2b_rule_t* rule; b2bl_entity_id_t* entity, *peer; b2bl_entity_id_t** entity_head = NULL; xmlNodePtr bridge_node, node; int state = -1; str attr; int ret; unsigned int method_value; int_str avp_val; b2bl_cback_f cbf = NULL; str ekey= {NULL, 0}; int request_id; b2bl_cb_params_t cb_params; b2bl_dlg_stat_t stats; b2b_req_data_t req_data; b2b_rpl_data_t rpl_data; lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("B2B logic record not found\n"); goto error; } scenario = tuple->scenario; entity = b2bl_search_entity(tuple, key, src, &entity_head); if(entity == NULL) { LM_ERR("No b2b_key match found [%.*s], src=%d\n", key->len, key->s, src); goto error; } if (entity->no > 1) { LM_ERR("unexpected entity->no [%d] for tuple [%p]\n", entity->no, tuple); goto error; } peer = entity->peer; LM_DBG("b2b_entity key = %.*s\n", key->len, key->s); if (b2bl_key_avp_name >= 0) { destroy_avps( b2bl_key_avp_type, b2bl_key_avp_name, 1); avp_val.s = *b2bl_key; if(add_avp(AVP_VAL_STR|b2bl_key_avp_type, b2bl_key_avp_name, avp_val)!=0) { LM_ERR("failed to build b2bl_key avp\n"); return -1; } } method = msg->first_line.u.request.method; method_value = msg->first_line.u.request.method_value; /* extract body if it has a body */ LM_DBG("request received for tuple[%p]->[%.*s]\n", tuple, tuple->key->len, tuple->key->s); request_id = b2b_get_request_id(&method); if(request_id < 0) { LM_DBG("Not a recognized request [%d]\n", request_id); goto send_usual_request; } /* if the request is an ACK and the tuple is marked to_del -> then delete the record and return */ if(tuple->to_del) { switch (request_id) { case B2B_ACK: LM_DBG("ACK for a negative reply\n"); break; case B2B_BYE: /* BYE already sent to this entity but we got no reply */ memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(entity); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); if(entity->peer) { memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(entity->peer); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); } break; default: memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(entity); rpl_data.method =method_value; rpl_data.code =400; rpl_data.text =¬Acceptable; b2b_api.send_reply(&rpl_data); } b2bl_delete(tuple, hash_index, 0); tuple = 0; goto done; } cbf = tuple->cbf; switch (request_id) { case B2B_BYE: entity->disconnected = 1; if(cbf && (tuple->cb_mask&B2B_BYE_CB)) { memset(&cb_params, 0, sizeof(b2bl_cb_params_t)); cb_params.param = tuple->cb_param; if(tuple->scenario_state != B2B_BRIDGING_STATE) entity->stats.call_time = get_ticks() - entity->stats.start_time; else entity->stats.call_time = 0; memcpy(&stats, &entity->stats, sizeof(b2bl_dlg_stat_t)); stats.key.s = (char*)pkg_malloc(tuple->key->len); if(stats.key.s == NULL) { LM_ERR("No more memory\n"); goto error; } memcpy(stats.key.s, tuple->key->s, tuple->key->len); stats.key.len = tuple->key->len; ekey.s = (char*)pkg_malloc(entity->key.len); if(ekey.s == NULL) { LM_ERR("No more memory\n"); pkg_free(stats.key.s); goto error; } memcpy(ekey.s, entity->key.s, entity->key.len); ekey.len = entity->key.len; cb_params.stat = &stats; cb_params.msg = msg; cb_params.entity = entity->no; lock_release(&b2bl_htable[hash_index].lock); LM_DBG("entity->no = %d\n", entity->no); ret = cbf(&cb_params, B2B_BYE_CB); LM_DBG("ret = %d, peer= %p\n", ret, peer); pkg_free(stats.key.s); lock_get(&b2bl_htable[hash_index].lock); /* must search the tuple again * you can't know what might have happened with it */ if (0!=post_cb_sanity_check(&tuple, hash_index, local_index, &entity, entity->type, &ekey)) { pkg_free(ekey.s); goto error; } pkg_free(ekey.s); peer = entity->peer; if(ret< B2B_DROP_MSG_CB_RET ) { LM_ERR("The callback function was unsuccessful\n"); goto send_usual_request; } else if(ret == B2B_DROP_MSG_CB_RET) { entity->peer = 0; /* send 200 OK for BYE */ memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(entity); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); b2bl_delete_entity(entity, tuple); entity = NULL; goto done; } else if(ret == B2B_SEND_MSG_CB_RET) goto send_usual_request; } if(tuple->scenario_state == B2B_BRIDGING_STATE) { LM_DBG("Scenario is in bridging state\n"); if(process_bridge_bye(msg, tuple, entity) < 0) { LM_ERR("Failed to process BYE received in bridging state\n"); goto error; } if(tuple->to_del && entity->peer==NULL) { LM_DBG("Delete this b2bl record after process_bridge_bye\n"); b2bl_delete(tuple, hash_index, 0); tuple = 0; } goto done; } break; case B2B_INVITE: if(cbf) { /* saving the entity key for later sanity check */ ekey.s = (char*)pkg_malloc(entity->key.len); if(ekey.s == NULL) { LM_ERR("No more memory\n"); goto error; } ekey.len = entity->key.len; memcpy(ekey.s, entity->key.s, entity->key.len); LM_DBG("ekey [%p]->[%.*s]\n", &ekey, ekey.len, ekey.s); /* preparing the cb params */ memset(&cb_params, 0, sizeof(b2bl_cb_params_t)); cb_params.param = tuple->cb_param; cb_params.stat = NULL; cb_params.msg = msg; cb_params.entity = entity->no; lock_release(&b2bl_htable[hash_index].lock); LM_DBG("entity->no = %d\n", entity->no); ret = cbf(&cb_params, B2B_RE_INVITE_CB); LM_DBG("ret = %d, peer= %p\n", ret, peer); lock_get(&b2bl_htable[hash_index].lock); /* must search the tuple again * you can't know what might have happened with it */ if (0!=post_cb_sanity_check(&tuple, hash_index, local_index, &entity, entity->type, &ekey)) { pkg_free(ekey.s); goto error; } pkg_free(ekey.s); peer = entity->peer; switch (ret) { case B2B_DROP_MSG_CB_RET: /* send 400 Not Acceptable for INVITE */ memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(entity); rpl_data.method =METHOD_INVITE; rpl_data.code =400; rpl_data.text =¬Acceptable; b2b_api.send_reply(&rpl_data); goto done; break; case B2B_SEND_MSG_CB_RET: goto send_usual_request; break; case B2B_FOLLOW_SCENARIO_CB_RET: /* just continue with normal processing */ break; case B2B_ERROR_CB_RET: LM_ERR("The callback function was unsuccessful\n"); goto send_usual_request; break; default: LM_ERR("Unexpected return code [%d]\n", ret); goto send_usual_request; } } break; } if(!scenario || !scenario->request_rules[request_id]) { if(request_id == B2B_BYE) { /* even though I don;t receive a reply, I should delete this record*/ tuple->lifetime = 30 + get_ticks(); } goto send_usual_request; } else { rule = scenario->request_rules[request_id]; if(tuple->scenario_state != B2B_NOTDEF_STATE) { while(rule) { if(tuple->scenario_state == rule->cond_state) { break; } else { LM_DBG("State does not match found [%d], required [%d]\n", tuple->scenario_state, rule->cond_state); } rule = rule->next; } peer->sdp_type = body->len ? B2BL_SDP_NORMAL : B2BL_SDP_LATE; } if(!rule) { LM_DBG("Did not find a rule to apply for this request -> do normal pass through\n"); goto send_usual_request; } else { LM_DBG("Found rule with id [%d]\n", rule->id); } /* if a match was found -> check the condition part */ if(rule->cond_node) { node = xmlNodeGetChildByName(rule->cond_node, "sender"); if(node) { LM_DBG("Found a sender condition\n"); /* get the sender type */ attr.s = (char*)xmlNodeGetNodeContentByName(node, "type", NULL); if(attr.s == NULL) { LM_ERR("Bad scenario document - sender condition node" " without a type child\n"); goto error; } if(xmlStrcasecmp((unsigned char*)attr.s,(unsigned char*) "server") == 0) { /* check if it is a server request */ if(src != B2B_SERVER) { xmlFree(attr.s); goto send_usual_request; } } else if(xmlStrcasecmp((unsigned char*) attr.s, (unsigned char*)"client") == 0) { if(src != B2B_CLIENT) { xmlFree(attr.s); goto send_usual_request; } } else { LM_ERR("Bad scenario document - sender condition type not" " known\n"); xmlFree(attr.s); goto error; } xmlFree(attr.s); /* check the id */ attr.s = xmlNodeGetNodeContentByName(node, "id", NULL); if(attr.s) { attr.len = strlen(attr.s); if((attr.len != entity->scenario_id.len || strncmp(attr.s, entity->scenario_id.s, attr.len) != 0)) { LM_DBG("Scenary id did not match - do not apply the rule" " found [%.*s] , required [%s]\n", entity->scenario_id.len, entity->scenario_id.s, attr.s); xmlFree(attr.s); goto send_usual_request; } xmlFree(attr.s); } LM_DBG("Sender condition match\n"); } /* TODO - process other conditions */ } /* apply actions */ /* get next state */ node = xmlNodeGetChildByName(rule->action_node, "state"); if(node) { attr.s = (char*)xmlNodeGetContent(node); if(attr.s == NULL) { LM_ERR("No state node content found\n"); goto error; } attr.len = strlen(attr.s); if(str2int(&attr, (unsigned int*)&state)< 0) { LM_ERR("Bad scenario. Scenary state not an integer\n"); xmlFree(attr.s); goto error; } LM_DBG("Next scenario state is [%d]\n", state); xmlFree(attr.s); } /* handle bridge action */ bridge_node = xmlNodeGetChildByName(rule->action_node, "bridge"); if(bridge_node) { LM_DBG("Found a bridge node\n"); if(process_bridge_action(msg, entity, tuple, bridge_node) < 0) { LM_ERR("Failed to process bridge action\n"); goto send_usual_request; } /* save next state */ tuple->next_scenario_state = state; } else { /* set the next state now because the action has only one step */ if(state >= 0) tuple->scenario_state = state; } node = xmlNodeGetChildByName(rule->action_node, "send_reply"); if(node) { unsigned int code; LM_DBG("Found a send reply node\n"); /* get code and text */ attr.s = xmlNodeGetNodeContentByName(node, "code", NULL); if(attr.s == NULL) { LM_ERR("Bad scenario document - No code defined for send_reply node\n"); goto error; } attr.len = strlen(attr.s); if(str2int(&attr, &code) < 0) { LM_ERR("Bad scenario - wrong reply code, not an integer\n"); xmlFree(attr.s); goto error; } xmlFree(attr.s); attr.s = xmlNodeGetNodeContentByName(node, "reason", NULL); if(attr.s == NULL) { LM_ERR("Bad scenario document - No code defined for send_reply node\n"); goto error; } attr.len = strlen(attr.s); memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(entity); rpl_data.method =method_value; rpl_data.code =code; rpl_data.text =&attr; b2b_api.send_reply(&rpl_data); LM_DBG("Send reply with code [%d] and text [%s]\n", code, attr.s); xmlFree(attr.s); } /* end_dialog_leg option */ node = xmlNodeGetChildByName(rule->action_node, "end_dialog_leg"); if(node) { LM_DBG("End dialog\n"); entity->disconnected = 1; memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(entity); req_data.method =&method_bye; b2b_api.send_request(&req_data); if(entity->peer) entity->peer->peer = NULL; peer = entity->peer = NULL; } node = xmlNodeGetChildByName(rule->action_node, "delete_entity"); if(node) { if(entity->peer) entity->peer->peer = 0; b2bl_delete_entity(entity, tuple); entity = NULL; LM_DBG("Deleted current entity\n"); } } goto done; send_usual_request: switch (request_id) { case B2B_CANCEL: tuple->scenario_state = B2B_CANCEL_STATE; break; case B2B_BYE: if(!peer || !peer->key.s) { memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(entity); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); b2bl_delete(tuple, hash_index, 0); tuple = 0; goto done; } else b2b_mark_todel(tuple); break; } while (peer && peer->key.s) { LM_DBG("Send request [%.*s] to peer [%.*s]\n", method.len, method.s, peer->key.len, peer->key.s); memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(peer); req_data.method =&method; req_data.extra_headers =extra_headers->len?extra_headers:NULL; req_data.body =body->len?body:NULL; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Sending request failed [%.*s]\n", peer->key.len, peer->key.s); } peer = peer->next; } done: if(tuple) { if(b2bl_db_mode == WRITE_THROUGH) b2bl_db_update(tuple); else UPDATE_DBFLAG(tuple, tuple->db_flag); } lock_release(&b2bl_htable[hash_index].lock); return 0; error: lock_release(&b2bl_htable[hash_index].lock); return -1; } static inline int get_b2b_dialog_by_replace(str *replaces, str *u_replaces, str *entity_key, unsigned int *hash_idx, unsigned int *local_idx ) { struct replaces_body replaces_b; char tuple_buf[B2BL_MAX_KEY_LEN]; str tuple_key; //LM_DBG("Replaces=[%.*s]\n",replaces->len,replaces->s); if(unescape_param(replaces,u_replaces)!=0) { LM_ERR("unable to escape [%.*s]\n", replaces->len, replaces->s); return -1; } //LM_DBG("[%.*s]\n", u_replaces->len, u_replaces->s); if(parse_replaces_body(u_replaces->s, u_replaces->len, &replaces_b)<0 || !replaces_b.callid_val.s || !replaces_b.to_tag_val.s || !replaces_b.from_tag_val.s) { LM_ERR("unable to parse replaces header [%.*s]\n", u_replaces->len, u_replaces->s); return -1; } tuple_key.s = tuple_buf; tuple_key.len = B2BL_MAX_KEY_LEN; if(b2b_api.get_b2bl_key(&replaces_b.callid_val, &replaces_b.from_tag_val, &replaces_b.to_tag_val, entity_key, &tuple_key)!=0) { LM_ERR("no b2bl key for [%.*s][%.*s][%.*s]\n", replaces_b.callid_val.len, replaces_b.callid_val.s, replaces_b.to_tag_val.len, replaces_b.to_tag_val.s, replaces_b.from_tag_val.len, replaces_b.from_tag_val.s); return -1; } if(b2bl_parse_key(&tuple_key, hash_idx, local_idx)< 0) { LM_ERR("Failed to parse b2b logic key [%.*s]\n", tuple_key.len, tuple_key.s); return -1; } LM_DBG("Need to replace callid=[%.*s] to-tag=[%.*s] and " "from-tag=[%.*s] from b2b_logic [%.*s]\n", replaces_b.callid_val.len, replaces_b.callid_val.s, replaces_b.to_tag_val.len, replaces_b.to_tag_val.s, replaces_b.from_tag_val.len, replaces_b.from_tag_val.s, tuple_key.len, tuple_key.s); return 0; } int b2b_logic_notify(int src, struct sip_msg* msg, str* key, int type, void* param) { #define U_REPLACES_BUF_LEN 512 char u_replaces_buf[U_REPLACES_BUF_LEN]; str u_replaces = { u_replaces_buf, U_REPLACES_BUF_LEN}; unsigned int hash_index, local_index; unsigned int hash_idx, local_idx; str entity_key = {NULL, 0}; b2bl_tuple_t* tuple; str* b2bl_key = (str*)param; str body= {NULL, 0}; str extra_headers = {NULL, 0}; str new_body={NULL, 0}; int ret = -1; #define H_SIZE 2 str h_name[H_SIZE]; str h_val[H_SIZE]; str rt_header; str auth_header; str* cust_headers; str* replaces = NULL; #define RT_BUF_LEN 1024 char rt_buf[RT_BUF_LEN]; str rt; struct b2bl_entity_id* r_peer = NULL; int i; if(b2bl_key == NULL) { LM_ERR("'param' argument NULL\n"); return -1; } if(key == NULL) { LM_ERR("'key' argument NULL\n"); return -1; } if(b2bl_parse_key(b2bl_key, &hash_index, &local_index)< 0) { LM_ERR("Failed to parse b2b logic key [%.*s]\n", b2bl_key->len, b2bl_key->s); return -1; } if (parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse message\n"); return -1; } /* extract body if it has one */ /* process the body */ if(msg->content_length) { if ( get_body(msg, &body)!=0 ) { LM_ERR("cannot extract body\n"); return -1; } } LM_DBG("b2b_entities notification cb for [%.*s] with entity [%.*s]\n", b2bl_key->len, b2bl_key->s, key->len, key->s); if(type == B2B_REPLY) { cust_headers = NULL; if (b2bl_htable[hash_index].flags & B2BL_FLAG_TRANSPARENT_AUTH) { if (msg->first_line.u.reply.statuscode == 401 && msg->www_authenticate) { auth_header.s = msg->www_authenticate->name.s; auth_header.len = msg->www_authenticate->len; cust_headers = &auth_header; } if (msg->first_line.u.reply.statuscode == 407 && msg->proxy_authenticate) { auth_header.s = msg->proxy_authenticate->name.s; auth_header.len = msg->proxy_authenticate->len; cust_headers = &auth_header; } } /* build extra headers */ if(b2b_extra_headers(msg, NULL, cust_headers, &extra_headers)< 0) { LM_ERR("Failed to construct extra headers\n"); goto done; } ret = b2b_logic_notify_reply(src, msg, key, &body, &extra_headers, b2bl_key, hash_index, local_index); } else if(type == B2B_REQUEST) { if(msg->first_line.u.request.method_value==METHOD_REFER && parse_refer_to_header(msg)==0 && msg->refer_to!=NULL && get_refer_to(msg)!=NULL && parse_uri(get_refer_to(msg)->uri.s, get_refer_to(msg)->uri.len, &(get_refer_to(msg)->parsed_uri))==0) { /* We have a Refer-To header */ if(get_refer_to(msg)->parsed_uri.headers.s && parse_uri_headers(get_refer_to(msg)->parsed_uri.headers, h_name,h_val,H_SIZE)==0) { for(i=0; iservers[i] && tuple->servers[i]->key.len==entity_key.len && strncmp(tuple->servers[i]->key.s, entity_key.s, entity_key.len)==0) { r_peer = tuple->servers[i]->peer; break; } } lock_release(&b2bl_htable[hash_idx].lock); if(!r_peer) { LM_ERR("no replaces peer\n"); goto done; } LM_DBG("got replacement callid=[%.*s] " "to-tag=[%.*s] and from-tag=[%.*s]\n", r_peer->dlginfo->callid.len, r_peer->dlginfo->callid.s, r_peer->dlginfo->totag.len, r_peer->dlginfo->totag.s, r_peer->dlginfo->fromtag.len, r_peer->dlginfo->fromtag.s); /* build the escaped Replaces URI header * Note: dlginfo->totag becomes from-tag in Replaces URI header * dlginfo->fromtag becomes to-tag in Replaces URI header */ i = r_peer->dlginfo->callid.len + r_peer->dlginfo->fromtag.len + r_peer->dlginfo->totag.len + 18 /* 2x'=' + 2x'=' + ft */; if (U_REPLACES_BUF_LEN < i) { LM_ERR("not enough space in the buffer: " "U_REPLACES_BUF_LEN < %d\n", i); } memcpy(u_replaces.s, r_peer->dlginfo->callid.s, r_peer->dlginfo->callid.len); i = r_peer->dlginfo->callid.len; u_replaces_buf[i] = ';'; i++; memcpy(u_replaces.s + i, "from-tag", strlen("from-tag")); i += strlen("from-tag"); u_replaces_buf[i] = '='; i++; memcpy(u_replaces.s + i, r_peer->dlginfo->totag.s, r_peer->dlginfo->totag.len); i += r_peer->dlginfo->totag.len; u_replaces_buf[i] = ';'; i++; memcpy(u_replaces.s + i, "to-tag", strlen("to-tag")); i += strlen("to-tag"); u_replaces_buf[i] = '='; i++; memcpy(u_replaces.s + i, r_peer->dlginfo->fromtag.s, r_peer->dlginfo->fromtag.len); i += r_peer->dlginfo->fromtag.len; u_replaces.len = i; /* build the new Refer-To header * Note: for now, we ignore the "early-only" parameter */ i = (int)(replaces->s - msg->refer_to->name.s); if(i>=RT_BUF_LEN) { LM_ERR("Not enough space to build Refer-To: " "%d>=RT_BUF_LEN\n", i); goto done; } memcpy(&rt_buf[0], msg->refer_to->name.s, i); rt.s = &rt_buf[i]; rt.len = RT_BUF_LEN - i; if(escape_param(&u_replaces, &rt)!=0) { LM_ERR("Unable to escape [%.*s] with len [%d] in char[%d]\n", u_replaces.len,u_replaces.s, u_replaces.len, rt.len); goto done; } //LM_DBG("escaped replaces [%.*s]\n", rt.len, rt.s); i = (int)(msg->refer_to->name.s + msg->refer_to->len - replaces->s - replaces->len); if(RT_BUF_LEN<=(int)(rt.s - &rt_buf[0] + rt.len + i)) { LM_ERR("Not enough space to build Refer-To: " "RT_BUF_LEN<=[%d]\n", (int)(rt.s - &rt_buf[0] + rt.len + i)); goto done; } memcpy(rt.s + rt.len, replaces->s + replaces->len, i); rt.len = (int)(rt.s + rt.len + i - &rt_buf[0]); rt.s = &rt_buf[0]; LM_DBG("New Refer-To: [%.*s]\n", rt.len, rt.s); /* build extra headers */ if(b2b_extra_headers(msg, NULL, &rt, &extra_headers)< 0) { LM_ERR("Failed to construct extra headers\n"); goto done; } } else { /* build extra headers */ rt_header.s = msg->refer_to->name.s; rt_header.len = msg->refer_to->len; if(b2b_extra_headers(msg, NULL, &rt_header, &extra_headers)< 0) { LM_ERR("Failed to construct extra headers\n"); goto done; } } } else { /* build extra headers */ if(b2b_extra_headers(msg, NULL, NULL, &extra_headers)< 0) { LM_ERR("Failed to construct extra headers\n"); goto done; } } ret = b2b_logic_notify_request(src, msg, key, &body, &extra_headers, b2bl_key, hash_index, local_index); } else { LM_ERR("got notification for [%.*s] from [%.*s] with unknown event type [%d]\n", b2bl_key->len, b2bl_key->s, key->len, key->s, type); } done: if(new_body.s) pkg_free(new_body.s); if(extra_headers.s) pkg_free(extra_headers.s); return ret; } /* This function does the following actions: * - extract the entities description from the scenario document * - send invite or reInvite to one of the parties * - mark in the scenario instantiation which are the bridged entities and * that this scenario is currently taking place * */ int process_bridge_action(struct sip_msg* msg, b2bl_entity_id_t* curr_entity, b2bl_tuple_t* tuple, xmlNodePtr bridge_node) { b2bl_entity_id_t* bridge_entities[3]; b2bl_entity_id_t* entity = NULL; b2bl_entity_id_t* old_entity= NULL; b2bl_entity_id_t* e; int count = 0; int index = 0; str attr= {NULL, 0}; str entity_dest; xmlNodePtr clientid_node; xmlNodePtr dest_node; xmlNodePtr client_node; xmlNodePtr lft_node; xmlNodePtr node; str provmedia_uri={NULL,0}; client_info_t ci; str* client_id; char* fdname_content= 0; str from_dname= {NULL, 0}; xmlNodePtr value_node; char* value_content= 0; b2b_req_data_t req_data; /* extract provisional media uri if exists */ node = xmlNodeGetChildByName(bridge_node, "provisional_media"); if(node) { provmedia_uri.s = (char*)xmlNodeGetContent(node); if(provmedia_uri.s) provmedia_uri.len = strlen(provmedia_uri.s); } memset(bridge_entities, 0, 3*sizeof(b2bl_entity_id_t*)); for(client_node= bridge_node->children; client_node; client_node=client_node->next) { if(xmlStrcasecmp(client_node->name, (unsigned char*)"client")!=0 && xmlStrcasecmp(client_node->name, (unsigned char*)"server") !=0) continue; if(count == 2) { LM_ERR("Bad scenario document. Too many entities defined for" " bridge node. Only two entities should be defined\n"); break; } entity = NULL; /* there are 3 ways to identify a client: "this", "peer" or "id" */ clientid_node = xmlNodeGetChildByName(client_node, "this"); if(clientid_node) { LM_DBG("Selected current entity\n"); if(curr_entity == NULL) { LM_DBG("You are not allowed to use a 'this' client " "specification for this type of route\n"); goto error; } entity = curr_entity; goto entity_search_done; } clientid_node = xmlNodeGetChildByName(client_node, "peer"); if(clientid_node) { LM_DBG("Selected peer entity\n"); if(curr_entity == NULL) { LM_DBG("You are not allowed to use a 'this' client " "specification for this type of route\n"); goto error; } if(curr_entity->peer == NULL) { LM_ERR("Requested for the peer entity of the current entity, but it is NULL.\n"); goto error; } entity = curr_entity->peer; goto entity_search_done; } /* extract entity id */ attr.s = (char*)xmlNodeGetNodeContentByName(client_node, "id", NULL); if(attr.s == NULL) { LM_ERR("Entity specification not valid. Accepted values:" " this, peer or id\n"); goto error; } attr.len = strlen(attr.s); /* search through the entities */ for (index = 0; index < MAX_B2BL_ENT; index++) { e = tuple->servers[index]; if(e) { if(e->next || e->prev) { LM_ERR("Inconsistent entity [%p]\n", old_entity); b2bl_print_tuple(tuple, L_ERR); goto error; } if (e->scenario_id.len == attr.len && strncmp(e->scenario_id.s, attr.s, attr.len)== 0) { entity = e; LM_DBG("Found entity server [%d]\n", count); break; } } e = tuple->clients[index]; if(e) { if(e->next || e->prev) { LM_ERR("Inconsistent entity [%p]\n", old_entity); b2bl_print_tuple(tuple, L_ERR); goto error; } if (e && e->scenario_id.len == attr.len && strncmp(e->scenario_id.s, attr.s, attr.len)== 0) { entity = e; LM_DBG("Found entity client [%d]\n", count); break; } } } /* if I have the 'new' child -> alter the scenario id for the old entity */ if ((xmlNodeGetChildByName(client_node, "destination") || xmlNodeGetChildByName(client_node, "new")) && entity) { /* write '<' everywhere - it's safe since it's not accepted in xml */ memset(entity->scenario_id.s, '<', entity->scenario_id.len); entity = NULL; } entity_search_done: /* must create a new client entity */ if(entity == NULL) { /* get the destination */ LM_DBG("Entity %d for bridge - new client entity\n", count); dest_node = xmlNodeGetChildByName(client_node, "destination"); if(dest_node == NULL) { LM_ERR("Bad format for b2b scenario. New entity without a destination\n"); goto error; } value_node = xmlNodeGetChildByName(dest_node, "value"); if(value_node == NULL) { LM_ERR("Bad format for b2b scenario. New entity without a destination\n"); goto error; } value_content = (char*)xmlNodeGetContent(value_node); if(value_content == NULL) { LM_ERR("Bad formatted scenario document. URI value empty\n"); goto error; } if(b2b_scenario_parse_uri(value_node, value_content, tuple, msg, &entity_dest) < 0) { LM_ERR("Failed to parse entity destination specification\n"); xmlFree(value_content); goto error; } node = xmlNodeGetChildByName(client_node, "from_dname"); if(node) { value_node = xmlNodeGetChildByName(node, "value"); if(value_node == NULL) { LM_ERR("Bad format for b2b scenario. New entity without a destination\n"); xmlFree(value_content); goto error; } fdname_content = (char*)xmlNodeGetContent(value_node); if(fdname_content == NULL) { LM_ERR("Bad formatted scenario document. URI value empty\n"); xmlFree(value_content); goto error; } if(b2b_scenario_parse_uri(value_node, fdname_content, tuple, msg, &from_dname) < 0) { LM_DBG("Failed to parse entity destination specification\n"); xmlFree(value_content); goto error; } } LM_DBG("New entity, dest = [%.*s]\n", entity_dest.len, entity_dest.s); entity = b2bl_create_new_entity(B2B_CLIENT, 0, &entity_dest, 0, from_dname.s?&from_dname:0, &attr, 0); if(fdname_content) xmlFree(fdname_content); fdname_content = 0; from_dname.s=0; xmlFree(value_content); if(entity == NULL) { LM_ERR("Failed to create new b2b entity\n"); goto error; } } else old_entity = entity; if(attr.s) xmlFree(attr.s); attr.s = NULL; bridge_entities[count++] = entity; } if(bridge_entities[1] == bridge_entities[0]) { LM_ERR("The scenario tells to bridge the same entity\n"); goto error; } /* arrange the entities in vector to have the old first */ if(old_entity && bridge_entities[0]!= old_entity) { bridge_entities[1] = bridge_entities[0]; bridge_entities[0] = old_entity; } /* I have the two entities -> now do the first step of the bridging scenario * -> send reInvite or Invite to one of the parties */ if(old_entity) { LM_DBG("Sent reInvite without a body to old entity\n"); tuple->bridge_entities[0]= bridge_entities[0]; tuple->bridge_entities[1]= bridge_entities[1]; if(provmedia_uri.s) { tuple->bridge_entities[2]= bridge_entities[1]; tuple->bridge_entities[1] = b2bl_create_new_entity(B2B_CLIENT, 0, &provmedia_uri, 0, 0,0, 0); if(tuple->bridge_entities[1] == NULL) { LM_ERR("Failed to create new b2b entity\n"); goto error; } } old_entity->stats.start_time = get_ticks(); old_entity->stats.call_time = 0; /* TODO -> Do I need some other info here? */ memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(old_entity); req_data.method =&method_invite; req_data.extra_headers = NULL; b2b_api.send_request(&req_data); old_entity->state = 0; old_entity->sdp_type = B2BL_SDP_LATE; } else { str from_uri = bridge_entities[1]->to_uri; str to_uri = bridge_entities[0]->to_uri; str from_dname = bridge_entities[0]->from_dname; memset(&ci, 0, sizeof(client_info_t)); ci.method = method_invite; ci.to_uri = to_uri; ci.from_uri = from_uri; ci.from_dname = from_dname; ci.extra_headers = tuple->extra_headers; /* if we use init sdp and we have it, just use it */ if (tuple->scenario && tuple->scenario->use_init_sdp && tuple->scenario->body.len) { ci.body = &tuple->scenario->body; } else { ci.body = 0; } ci.from_tag = 0; ci.send_sock = msg?(msg->force_send_socket?msg->force_send_socket:msg->rcv.bind_address):0; if (ci.send_sock) get_local_contact( ci.send_sock, &ci.local_contact); else ci.local_contact = server_address; if(msg) { if (str2int( &(get_cseq(msg)->number), &ci.cseq)!=0 ) { LM_ERR("cannot parse cseq number\n"); goto error1; } } LM_DBG("Send Invite without a body to a new client entity\n"); client_id = b2b_api.client_new(&ci, b2b_client_notify, b2b_add_dlginfo, tuple->key); if(client_id == NULL) { LM_ERR("Failed to create new client entity\n"); goto error1; } /* save the client_id in the structure */ entity = b2bl_create_new_entity(B2B_CLIENT, client_id, &to_uri, &from_uri,0,&bridge_entities[0]->scenario_id, 0); if(entity == NULL) { LM_ERR("failed to create new client entity\n"); pkg_free(client_id); goto error1; } pkg_free(client_id); entity->stats.call_time = get_ticks(); entity->type = B2B_CLIENT; entity->peer = bridge_entities[1]; entity->sdp_type = ci.body ? B2BL_SDP_RENEW : B2BL_SDP_LATE; shm_free(bridge_entities[0]); tuple->bridge_entities[0] = entity; tuple->bridge_entities[1]= bridge_entities[1]; if (0 != b2bl_add_client(tuple, entity)) goto error1; } /* save the pointers to the bridged entities ; * the first (index 0) is the one we sent the first message ( reInvite or Invite)*/ tuple->scenario_state = B2B_BRIDGING_STATE; /* extract the lifetime if one is defined */ lft_node = xmlNodeGetChildByName(bridge_node, "lifetime"); if(lft_node) { attr.s = (char*)xmlNodeGetContent(lft_node); if(attr.s == NULL) { LM_ERR("Failed to extract node content\n"); goto error; } attr.len = strlen(attr.s); if(str2int(&attr, &tuple->lifetime)< 0) { LM_ERR("Wrong scenario document. The lifetime value is not an integer"); xmlFree(attr.s); goto error; } xmlFree(attr.s); attr.s = NULL; LM_DBG("Lifetime defined = [%d]\n", tuple->lifetime); tuple->lifetime+= get_ticks(); } else tuple->lifetime = -1; LM_DBG("be[0]= %p, be1=[%p]\n", tuple->bridge_entities[0], tuple->bridge_entities[1]); return 0; error1: shm_free(bridge_entities[0]); shm_free(bridge_entities[1]); error: if(attr.s) xmlFree(attr.s); return -1; } int b2b_server_notify(struct sip_msg* msg, str* key, int type, void* param) { return b2b_logic_notify(B2B_SERVER, msg, key, type, param); } int b2b_client_notify(struct sip_msg* msg, str* key, int type, void* param) { return b2b_logic_notify(B2B_CLIENT, msg, key, type, param); } static char fromtag_buf[MD5_LEN]; static void gen_fromtag(str* callid, str* fromtag, str* uri, struct sip_msg* msg, str* from_tag_uac) { int i = 0; str src[4]; from_tag_uac->len = MD5_LEN; from_tag_uac->s = fromtag_buf; src[i++] = *callid; src[i++] = *fromtag; src[i++] = *uri; if(msg) { if (msg->via1->branch) { src[i++] = msg->via1->branch->value; } else { src[i++] = msg->callid->body; } } MD5StringArray(from_tag_uac->s, src, i); LM_DBG("Gen from_tag= %s\n", fromtag_buf); } str* create_top_hiding_entities(struct sip_msg* msg, b2bl_cback_f cbf, void* cb_param, unsigned int cb_mask, str* custom_hdrs, struct b2b_params *params) { str* server_id = NULL; str* client_id = NULL; str body = {NULL, 0}; str extra_headers = {NULL, 0}; str* b2bl_key; b2bl_tuple_t* tuple; unsigned int hash_index; b2b_dlginfo_t* dlginfo, dlginfo_s; client_info_t ci; str to_uri={NULL, 0}, from_uri, from_dname; b2bl_entity_id_t* client_entity = NULL; int idx; str uri; qvalue_t q; str from_tag_gen= {0, 0}; str new_body={0, 0}; if(b2b_msg_get_from(msg, &from_uri, &from_dname)< 0 || b2b_msg_get_to(msg, &to_uri, params->flags)< 0) { LM_ERR("Failed to get to or from from the message\n"); return NULL; } /* process the body */ if(msg->content_length) { if ( get_body(msg, &body)!=0 ) { LM_ERR("cannot extract body\n"); return NULL; } } hash_index = core_hash(&to_uri, &from_uri, b2bl_hsize); b2bl_htable[hash_index].flags = params->flags; tuple = b2bl_insert_new(msg, hash_index, NULL, NULL, NULL, custom_hdrs, -1, &b2bl_key, INSERTDB_FLAG); if(tuple== NULL) { LM_ERR("Failed to insert new scenario instance record\n"); goto error; } tuple->cbf = cbf; tuple->cb_mask = cb_mask; tuple->cb_param = cb_param; /* if it will not be confirmed -> delete */ tuple->lifetime = params->init_timeout + get_ticks(); /* create new server */ server_id = b2b_api.server_new(msg, &tuple->local_contact, b2b_server_notify, b2bl_key); if(server_id == NULL) { LM_ERR("failed to create new b2b server instance\n"); goto error; } tuple->servers[0] = b2bl_create_new_entity(B2B_SERVER, server_id, &to_uri, &from_uri, 0,0, msg); if(tuple->servers[0] == NULL) { LM_ERR("Failed to create server entity\n"); goto error; } tuple->servers[0]->type = B2B_SERVER; tuple->servers[0]->no = 0; if(b2b_extra_headers(msg, b2bl_key, custom_hdrs, &extra_headers)< 0) { LM_ERR("Failed to create extra headers\n"); goto error; } /* create new client */ memset(&ci, 0, sizeof(client_info_t)); ci.method = msg->first_line.u.request.method; ci.req_uri = *(GET_RURI(msg)); ci.to_uri = to_uri; ci.from_uri = from_uri; ci.from_dname = from_dname; ci.dst_uri = msg->dst_uri; ci.extra_headers = &extra_headers; ci.body = (body.s?&body:NULL); ci.send_sock = msg->force_send_socket?msg->force_send_socket:msg->rcv.bind_address; get_local_contact( ci.send_sock, &ci.local_contact); /* grab all AVPs from the server side and push them into the client */ ci.avps = clone_avp_list( *get_avp_list() ); dlginfo = tuple->servers[0]->dlginfo; gen_fromtag(&dlginfo->callid, &dlginfo->fromtag, &ci.req_uri, msg, &from_tag_gen); ci.from_tag = &from_tag_gen; if (str2int( &(get_cseq(msg)->number), &ci.cseq)!=0 ) { LM_ERR("cannot parse cseq number\n"); goto error; } client_id = b2b_api.client_new(&ci, b2b_client_notify, b2b_add_dlginfo, b2bl_key); if(client_id == NULL) { LM_ERR("failed to create new b2b client instance\n"); goto error; } client_entity = b2bl_create_new_entity(B2B_CLIENT, client_id, &to_uri, &from_uri, 0, 0, 0); if(client_entity == NULL) { LM_ERR("Failed to create server entity\n"); goto error; } memset(&dlginfo_s, 0, sizeof(b2b_dlginfo_t)); dlginfo_s.callid = *client_id; dlginfo_s.totag = from_tag_gen; if(entity_add_dlginfo(client_entity, &dlginfo_s)< 0) { LM_ERR("Failed to add dialoginfo\n"); goto error; } client_entity->no = 1; client_entity->peer = tuple->servers[0]; tuple->clients[0] = client_entity; for( idx=0 ; (uri.s=get_branch(idx,&uri.len,&q,0,0,0,0))!=0 ; idx++ ) { LM_DBG("got branch ruri [%.*s]\n", uri.len, uri.s); gen_fromtag(&dlginfo->callid, &dlginfo->fromtag, &uri, msg, &from_tag_gen); ci.from_tag = &from_tag_gen; ci.req_uri = uri; ci.avps = clone_avp_list( *get_avp_list() ); client_id = b2b_api.client_new(&ci, b2b_client_notify, b2b_add_dlginfo, b2bl_key); if(client_id == NULL) { LM_ERR("failed to create new b2b client instance\n"); goto error; } client_entity = b2bl_create_new_entity(B2B_CLIENT, client_id, &to_uri, &from_uri, 0, 0, 0); if (client_entity == NULL) { LM_ERR("Failed to create client entity\n"); goto error; } client_entity->no = 1; client_entity->peer = tuple->servers[0]; memset(&dlginfo_s, 0, sizeof(b2b_dlginfo_t)); dlginfo_s.callid = *client_id; dlginfo_s.totag = from_tag_gen; if(entity_add_dlginfo(client_entity, &dlginfo_s)< 0) { LM_ERR("Failed to add dialoginfo\n"); goto error; } /* Add the client entity to the list */ tuple->clients[0]->prev = client_entity; client_entity->next = tuple->clients[0]; tuple->clients[0] = client_entity; } tuple->servers[0]->peer = tuple->clients[0]; tuple->bridge_entities[0] = tuple->servers[0]; tuple->bridge_entities[1] = tuple->clients[0]; b2bl_print_tuple(tuple, L_DBG); if(b2bl_db_mode == WRITE_THROUGH) { b2bl_db_insert(tuple); } lock_release(&b2bl_htable[hash_index].lock); pkg_free(to_uri.s); pkg_free(server_id); pkg_free(client_id); if(extra_headers.s) pkg_free(extra_headers.s); if(new_body.s) pkg_free(new_body.s); return b2bl_key; error: lock_release(&b2bl_htable[hash_index].lock); if(server_id) pkg_free(server_id); if(client_id) pkg_free(client_id); if(to_uri.s) pkg_free(to_uri.s); if(extra_headers.s) pkg_free(extra_headers.s); if(new_body.s) pkg_free(new_body.s); return NULL; } /* Function that processes destination node. * Accepted value types: * uri: specified inline * param: specified as a parameter * initial: the initial destination(from the initial message) * header: a header field value **/ int b2b_scenario_parse_uri(xmlNodePtr value_node, char* value_content, b2bl_tuple_t* tuple, struct sip_msg* msg, str* client_to) { str value= {value_content, strlen(value_content)}; unsigned char* value_type= NULL; unsigned int param_no; str check_uri; value_type = xmlNodeGetAttrContentByName(value_node, "type"); if(value_type == NULL) { LM_ERR("Scenary document not well formed. To type param not defined\n"); return -1; } if(xmlStrcasecmp(value_type, (unsigned char*)"uri") == 0) { LM_DBG("URI of type uri\n"); *client_to = value; } else if(xmlStrcasecmp(value_type, (unsigned char*)"param") == 0) { LM_DBG("URI of type param\n"); if(str2int(&value, ¶m_no)< 0) { LM_ERR("Scenary document not well formed. Client to param not a number\n"); goto error; } if(param_no > B2B_INIT_MAX_PARAMNO || param_no < 1) { LM_ERR("Scenary document not well formed. Client to param not valid [%d]\n", param_no); goto error; } *client_to = tuple->scenario_params[param_no-1]; LM_DBG("URI value taken from a parameter [%.*s]\n", client_to->len, client_to->s); } else if(xmlStrcasecmp(value_type, (unsigned char*)"initial") == 0) { LM_DBG("URI of type initial\n"); // FIXME: this may not exist after a transfer that will leave us with two clients if (tuple->servers[0]) *client_to = tuple->servers[0]->to_uri; } else if(xmlStrcasecmp(value_type, (unsigned char*)"header") == 0) { struct hdr_field* sip_hdr, hdr; char buf[BUF_LEN]; struct sip_uri sip_uri; LM_DBG("URI of type header value\n"); if(msg == NULL) { LM_DBG("You are not allowed to use a header specification for this type of scenario\n"); goto error; } if(BUF_LEN < value.len + 1) { LM_ERR("Buffer overflow\n"); goto error; } memcpy(buf, value.s, value.len); buf[value.len] = ':'; if(parse_hname2(buf, buf + value.len+1, &hdr) < 0) { LM_ERR("Failed to parse header name\n"); goto error; } if(hdr.type == HDR_OTHER_T) { LM_DBG("Header other\n"); sip_hdr = get_header_by_name(msg, value.s, value.len); if(sip_hdr == NULL) { LM_DBG("No header with the name [%.*s] found\n", value.len, value.s); goto error; } } else if(hdr.type == HDR_ERROR_T) { LM_DBG("Failed to parse header name\n"); goto error; } else { sip_hdr = msg->headers; while(sip_hdr && sip_hdr->type != hdr.type) sip_hdr = sip_hdr->next; if(sip_hdr == NULL) { LM_DBG("Did not find header\n"); goto error; } } check_uri = sip_hdr->body; trim(&check_uri); if(check_uri.s[0] == '<') { check_uri.s++; check_uri.len-=2; } if(parse_uri(check_uri.s, check_uri.len, &sip_uri)< 0) { LM_ERR("Not a valid sip uri [%.*s]\n", check_uri.len, check_uri.s); goto error; } *client_to = check_uri; } else { LM_ERR("Scenary document not well formed. Client to type not valid\n"); goto error; } LM_DBG("URI value = [%.*s]\n", client_to->len, client_to->s); xmlFree(value_type); return 0; error: if(value_type) xmlFree(value_type); return -1; } int udh_to_uri(str user, str host, str port, str* uri) { int size; if(uri==0) return -1; size = user.len + host.len + port.len+7; LM_DBG("user:host:port [%.*s][%.*s][%.*s]\n", user.len, user.s, host.len, host.s, port.len, port.s); uri->s = (char*)pkg_malloc(size); if(uri->s == NULL) { LM_ERR("No more memory [%d]\n", size); return -1; } uri->len = sprintf(uri->s, "sip:%.*s%.*s%.*s", user.len, user.s, user.len?1:0,"@",host.len, host.s); if(port.s) { uri->len += sprintf(uri->s+uri->len, ":%.*s", port.len, port.s); } return 0; } str* b2b_process_scenario_init(b2b_scenario_t* scenario_struct, struct sip_msg* msg, str* args[], b2bl_cback_f cbf, void* cb_param, unsigned int cb_mask, str* custom_hdrs, struct b2b_params *params) { str* server_id= NULL, *client_id= NULL; str body= {NULL, 0}; str method = {INVITE, INVITE_LEN}; str* b2bl_key = NULL; b2bl_tuple_t* tuple= NULL; xmlNodePtr node, init_node, node_aux; xmlNodePtr server_node = NULL, clients_node= NULL; str entity_sid; char* type= NULL; str client_to; b2bl_entity_id_t* client_entity = NULL; unsigned int scenario_state = B2B_NOTDEF_STATE; client_info_t ci; int clients_no = 0; unsigned int hash_index; unsigned int index; str to_uri={NULL, 0}, from_uri, from_dname; int eno = 0; str new_body={0, 0}; if(b2b_msg_get_from(msg, &from_uri, &from_dname)< 0 || b2b_msg_get_to(msg, &to_uri, params->flags)< 0) { LM_ERR("Failed to get to or from from the message\n"); return NULL; } hash_index = core_hash(&to_uri, &from_uri, b2bl_hsize); if(msg) { method = msg->first_line.u.request.method; /* extract info from the message in case there is a client entity * with type message */ /* process the body */ if(msg->content_length) { if ( get_body(msg, &body)!=0 ) { LM_ERR("cannot extract body\n"); goto error; } } } /* examine the init part in the scenario XML document */ init_node = xmlNodeGetChildByName(scenario_struct->init_node, "bridge"); if(init_node == NULL) { LM_ERR("Wrong format for b2b scenario document. No bridging node" " inside init node\n"); goto error; } server_node = xmlNodeGetChildByName(init_node, "server"); clients_node = xmlNodeGetChildByName(init_node, "client"); if(server_node == NULL && clients_node == NULL) { LM_ERR("There must be at least one client or one server entity\n"); goto error; } /* create new scenario instance record */ tuple = b2bl_insert_new(msg, hash_index, scenario_struct, args, body.s?&body:NULL, custom_hdrs, -1, &b2bl_key, INSERTDB_FLAG); if(tuple== NULL) { LM_ERR("Failed to insert new scenario instance record\n"); goto error; } tuple->lifetime = 60 + get_ticks(); /* set the state of the scenario after the init section */ node = xmlNodeGetChildByName(scenario_struct->init_node, "state"); if(node) { str state_attr; state_attr.s = (char*)xmlNodeGetContent(node); state_attr.len = strlen(state_attr.s); if(str2int(&state_attr, &scenario_state)< 0) { LM_ERR("Scenary state after init section not an integer\n"); xmlFree(state_attr.s); goto error; } xmlFree(state_attr.s); tuple->scenario_state = scenario_state; tuple->next_scenario_state = scenario_state; } /* go through the document and create the described entities */ if(server_node) { if(msg == NULL) { LM_ERR("A request for a server entity and no message\n"); goto error; } /* a server entity can only deal with a message and there can * only be one server entity */ /* extract the id */ entity_sid.s = (char*)xmlNodeGetNodeContentByName(server_node, "id", NULL); if(entity_sid.s == NULL) { LM_ERR("Wrong formatted xml document. Server node without " "id parameter\n"); goto error; } entity_sid.len = strlen(entity_sid.s); /* create new server entity */ server_id = b2b_api.server_new(msg, &tuple->local_contact, b2b_server_notify, b2bl_key); if(server_id == NULL) { LM_ERR("failed to create new b2b server instance\n"); xmlFree(entity_sid.s); goto error; } tuple->servers[0] = b2bl_create_new_entity(B2B_SERVER, server_id, &to_uri, &from_uri,0,&entity_sid,msg); tuple->servers[0]->no = eno++; tuple->bridge_entities[0] = tuple->servers[0]; if(tuple->servers[0] == NULL) { LM_ERR("failed to create new server entity\n"); xmlFree(entity_sid.s); pkg_free(server_id); goto error; } xmlFree(entity_sid.s); pkg_free(server_id); tuple->servers[0]->type = B2B_SERVER; } pkg_free(to_uri.s); to_uri.s = 0; /* create client entities */ for(node = clients_node; node; node=node->next) { char* value_content; if(xmlStrcasecmp(node->name, (unsigned char*)"client") !=0 ) continue; entity_sid.s = (char*)xmlNodeGetNodeContentByName(node, "id", NULL); if(entity_sid.s == NULL) { LM_ERR("Wrong formatted xml document. Client node without " "id parameter\n"); goto error; } entity_sid.len = strlen(entity_sid.s); /* get type*/ type = xmlNodeGetNodeContentByName(node, "type", NULL); if(type == NULL) { LM_ERR("Scenary document not well formed. Client Type " "node not found\n"); goto error1; } /* extract destination */ node_aux = xmlNodeGetChildByName(node, "destination"); if(node_aux == NULL) { LM_ERR("Scenary document not well formed. No client 'to' " "node defined\n"); goto error2; } node_aux = xmlNodeGetChildByName(node_aux, "value"); if(node_aux == NULL) { LM_ERR("Bad format for b2b scenario. New entity without a " "destination\n"); goto error2; } value_content = (char*)xmlNodeGetContent(node_aux); if(value_content == NULL) { LM_ERR("Bad formatted scenario document. URI value empty\n"); goto error2; } if(b2b_scenario_parse_uri(node_aux, value_content, tuple, msg, &client_to) < 0 || !client_to.s) { LM_ERR("Failed to get the value for the b2b client ruri\n"); xmlFree(value_content); goto error2; } xmlFree(value_content); if(xmlStrcasecmp((unsigned char*)type, (unsigned char*)"message") == 0) { memset(&ci, 0, sizeof(client_info_t)); ci.method = method; ci.to_uri = client_to; ci.from_uri = from_uri; ci.from_dname = from_dname; ci.extra_headers = tuple->extra_headers; ci.body = (body.s?&body:NULL); ci.send_sock = msg->force_send_socket? msg->force_send_socket:msg->rcv.bind_address; get_local_contact( ci.send_sock, &ci.local_contact); /* grab all AVPs from the server side */ ci.avps = clone_avp_list( *get_avp_list() ); if (str2int( &(get_cseq(msg)->number), &ci.cseq)!=0 ) { LM_ERR("cannot parse cseq number\n"); goto error; } client_id = b2b_api.client_new(&ci, b2b_client_notify, b2b_add_dlginfo, b2bl_key); if(client_id == NULL) { LM_ERR("failed to create new b2b client instance\n"); goto error2; } client_entity = b2bl_create_new_entity(B2B_CLIENT, client_id, &client_to, &from_uri,0,&entity_sid, 0); if(client_entity == NULL) { LM_ERR("failed to create new client entity\n"); xmlFree(entity_sid.s); pkg_free(client_id); goto error2; } pkg_free(client_id); if (0 != b2bl_add_client(tuple, client_entity)) goto error2; client_entity->no = eno++; clients_no++; } xmlFree(type); xmlFree(entity_sid.s); } /* If I have a server entity I consider it peer for all client entities, * and its peer is the first client */ if(tuple->servers[0]) { if(tuple->clients[0] == NULL) { LM_ERR("You have to create at least 2 entities in init part\n"); goto error; } for (index = 0; index < MAX_B2BL_ENT; index++) { if (tuple->clients[index]) tuple->clients[index]->peer = tuple->servers[0]; } tuple->bridge_entities[0] = tuple->servers[0]; tuple->bridge_entities[1] = tuple->clients[0]; } else { if(clients_no >= 2) /* make the first 2 peers */ { tuple->bridge_entities[0]= tuple->clients[0]; tuple->bridge_entities[1]= tuple->clients[1]; } else { LM_ERR("You have to create at least 2 entities in init part\n"); goto error; } } tuple->bridge_entities[0]->peer = tuple->bridge_entities[1]; tuple->bridge_entities[1]->peer = tuple->bridge_entities[0]; tuple->cbf = cbf; tuple->cb_mask = cb_mask; tuple->cb_param = cb_param; if(b2bl_db_mode == WRITE_THROUGH) b2bl_db_insert(tuple); b2bl_htable[hash_index].flags = params->flags; lock_release(&b2bl_htable[hash_index].lock); if(new_body.s) pkg_free(new_body.s); return b2bl_key; error2: xmlFree(type); error1: xmlFree(entity_sid.s); error: if(tuple) { b2bl_delete(tuple, hash_index, 0); lock_release(&b2bl_htable[hash_index].lock); } if(to_uri.s) pkg_free(to_uri.s); if(new_body.s) pkg_free(new_body.s); return NULL; } str* init_request(struct sip_msg* msg, struct b2b_scen_fl *scf, str* args[], b2bl_cback_f cbf, void* cb_param, unsigned int cb_mask, str* custom_hdrs) { str* key; int_str avp_val; /* parse message to extract needed info */ if (parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse message\n"); return NULL; } if(scf->scenario == NULL) key = create_top_hiding_entities(msg, cbf, cb_param, cb_mask, custom_hdrs, &scf->params); else key = b2b_process_scenario_init(scf->scenario, msg, args, cbf, cb_param, cb_mask, custom_hdrs, &scf->params); if(key) { if (b2bl_key_avp_name >= 0) { avp_val.s = *key; if ( add_avp(AVP_VAL_STR|b2bl_key_avp_type, b2bl_key_avp_name, avp_val)!=0) { LM_ERR("failed to build b2bl_key avp\n"); } } } return key; } str* internal_init_scenario(struct sip_msg* msg, str* name, str* args[MAX_SCENARIO_PARAMS], b2bl_cback_f cbf, void* cb_param, unsigned int cb_mask, str* custom_hdrs) { struct b2b_scen_fl *scf; if (b2bl_key_avp_name >= 0) destroy_avps( b2bl_key_avp_type, b2bl_key_avp_name, 1); scf = prepare_b2b_scen_fl_struct(); if (scf == NULL) { LM_ERR("no more pkg memory\n"); return NULL; } scf->params.init_timeout = b2bl_th_init_timeout; if(name->len == B2B_TOP_HIDING_SCENARY_LEN && strncmp(name->s,B2B_TOP_HIDING_SCENARY,B2B_TOP_HIDING_SCENARY_LEN)==0) { scf->scenario = NULL; } else { scf->scenario = get_scenario_id_list(name, script_scenarios); if(!scf->scenario) { LM_ERR("No scenario found with id [%s]\n", name->s); return NULL; } } b2bl_caller = CALLER_MODULE; return init_request(msg, scf, args, cbf, cb_param, cb_mask, custom_hdrs); } int b2b_init_request(struct sip_msg* msg, str* arg1, str* arg2, str* arg3, str* arg4, str* arg5, str* arg6) { str* args[MAX_SCENARIO_PARAMS]; struct b2b_scen_fl *scf; str* key; str auth_header; str* cust_headers; int ret = -1; if (b2bl_key_avp_name >= 0) destroy_avps( b2bl_key_avp_type, b2bl_key_avp_name, 1); /* find the scenario with the corresponding id */ scf = (struct b2b_scen_fl*)arg1; b2bl_caller = CALLER_SCRIPT; /* process the arguments */ args[0] = arg2; args[1] = arg3; args[2] = arg4; args[3] = arg5; args[4] = arg6; b2b_api.apply_lumps(msg); cust_headers = NULL; if (scf->params.flags & B2BL_FLAG_TRANSPARENT_AUTH) { if (msg->authorization) { auth_header.s = msg->authorization->name.s; auth_header.len = msg->authorization->len; cust_headers = &auth_header; } if (msg->proxy_auth) { auth_header.s = msg->proxy_auth->name.s; auth_header.len = msg->proxy_auth->len; cust_headers = &auth_header; } } /* call the scenario init processing function */ key = init_request(msg, scf, args, 0, NULL, 0, cust_headers); if(key) ret = 1; return ret; } int b2bl_bridge(str* key, str* new_dst, str* new_from_dname, int entity_no) { b2bl_tuple_t* tuple; b2bl_entity_id_t* entity = NULL, *old_entity; struct sip_uri uri; unsigned int hash_index, local_index; str* client_id; client_info_t ci; b2b_req_data_t req_data; b2b_rpl_data_t rpl_data; if(!key || !new_dst) { LM_ERR("Wrong arguments\n"); return -1; } if(entity_no == 1) { LM_WARN("Not implemented yet.\n"); return 0; } if(parse_uri(new_dst->s, new_dst->len, &uri)< 0) { LM_ERR("Bad argument. Not a valid uri [%.*s]\n", new_dst->len, new_dst->s); return -1; } if(b2bl_parse_key(key, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key\n"); return -1; } lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("No entity found\n"); goto error; } // FIXME: we may have no server at some point in time if(tuple->servers[0] == NULL) { LM_ERR("Wrong usage - no server entity present\n"); goto error; } LM_DBG("Bridge server %.*s\n",tuple->servers[0]->dlginfo->callid.len, tuple->servers[0]->dlginfo->callid.s); old_entity = tuple->servers[0]->peer; if(old_entity) { if(old_entity->next || old_entity->prev) { LM_ERR("Inconsistent entity [%p]\n", old_entity); b2bl_print_tuple(tuple, L_ERR); goto error; } LM_DBG("End peer dialog [%p]\n", old_entity); old_entity->peer = NULL; if(old_entity->disconnected && old_entity->state==B2BL_ENT_CONFIRMED) { memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(old_entity); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); b2bl_delete_entity(old_entity, tuple); } else b2b_end_dialog(old_entity, tuple); } else LM_DBG("No peer found\n"); if(tuple->scenario_state == B2B_BRIDGING_STATE && tuple->bridge_entities[0]== tuple->servers[0] && tuple->servers[0]->state== B2BL_ENT_CONFIRMED) { LM_DBG("Do the second step of the bridging\n"); /* do the second step of bridging */ memset(&ci, 0, sizeof(client_info_t)); ci.method = method_invite; ci.to_uri = *new_dst; ci.from_uri = tuple->servers[0]->to_uri; ci.from_dname = *new_from_dname; ci.extra_headers = tuple->extra_headers; ci.body = tuple->b1_sdp.s?&tuple->b1_sdp:0; ci.cseq = 1; ci.local_contact = tuple->local_contact; client_id = b2b_api.client_new(&ci, b2b_client_notify, b2b_add_dlginfo, tuple->key); if(client_id == NULL) { LM_ERR("Failed to create new client entity\n"); goto error; } /* save the client_id in the structure */ entity = b2bl_create_new_entity(B2B_CLIENT, client_id, &ci.to_uri, &ci.from_uri, 0, 0, 0); if(entity == NULL) { LM_ERR("failed to create new client entity\n"); pkg_free(client_id); goto error; } pkg_free(client_id); LM_DBG("Created new client entity [%.*s]\n", new_dst->len, new_dst->s); if (0 != b2bl_add_client(tuple, entity)) goto error; } else { entity = b2bl_create_new_entity( B2B_CLIENT, 0, new_dst, 0, new_from_dname,0,0); if(entity == NULL) { LM_ERR("Failed to create new b2b entity\n"); goto error; } LM_DBG("Created new client entity [%.*s]\n", new_dst->len, new_dst->s); tuple->scenario_state = B2B_BRIDGING_STATE; memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(tuple->servers[0]); req_data.method =&method_invite; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Failed to send INVITE request\n"); goto error; } tuple->servers[0]->sdp_type = B2BL_SDP_LATE; tuple->servers[0]->state = 0; /* mark it not as CONFIRMED */ } tuple->bridge_entities[0]= tuple->servers[0]; tuple->bridge_entities[1]= entity; tuple->servers[0]->no = 0; entity->no = 1; tuple->servers[0]->peer = entity; entity->peer = tuple->servers[0]; tuple->servers[0]->stats.start_time = get_ticks(); tuple->servers[0]->stats.call_time = 0; lock_release(&b2bl_htable[hash_index].lock); return 0; error: if(entity) shm_free(entity); lock_release(&b2bl_htable[hash_index].lock); return -1; } int b2bl_terminate_call(str* key) { unsigned int hash_index, local_index; b2bl_tuple_t* tuple; if(b2bl_parse_key(key, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key\n"); return -1; } lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_DBG("No entity found [%.*s]\n", key->len, key->s); lock_release(&b2bl_htable[hash_index].lock); return -1; } b2b_end_dialog(tuple->bridge_entities[0], tuple); b2b_end_dialog(tuple->bridge_entities[1], tuple); b2b_mark_todel(tuple); lock_release(&b2bl_htable[hash_index].lock); return 0; } int b2bl_get_stats(str* key, b2bl_dlg_stat_t* stat) { unsigned int hash_index, local_index; b2bl_tuple_t* tuple; if(b2bl_parse_key(key, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key\n"); return -1; } lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("No entity found\n"); lock_release(&b2bl_htable[hash_index].lock); return -1; } if(stat && tuple->bridge_entities[0]) { stat->start_time = tuple->bridge_entities[0]->stats.start_time; stat->setup_time = tuple->bridge_entities[0]->stats.setup_time; stat->call_time = get_ticks() - stat->start_time; stat->key.s = NULL; stat->key.len = 0; } lock_release(&b2bl_htable[hash_index].lock); return 0; } int b2bl_set_state(str* key, int state) { unsigned int hash_index, local_index; b2bl_tuple_t* tuple; if(b2bl_parse_key(key, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key\n"); return -1; } lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("No entity found\n"); lock_release(&b2bl_htable[hash_index].lock); return -1; } if(tuple->scenario_state == B2B_BRIDGING_STATE) tuple->next_scenario_state = state; else tuple->scenario_state = state; lock_release(&b2bl_htable[hash_index].lock); return 0; } int b2bl_bridge_2calls(str* key1, str* key2) { b2bl_tuple_t* tuple; unsigned int hash_index, local_index; b2bl_entity_id_t *e2= 0, *e1= 0; b2bl_entity_id_t *e; b2b_req_data_t req_data; b2b_rpl_data_t rpl_data; if(!key1 || !key2) { LM_ERR("Wrong arguments [%p] [%p]\n", key1, key2); return -1; } if(b2bl_parse_key(key2, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key [%.*s]\n", key2->len, key2->s); return -1; } /* extract the entity and delete the tuple */ lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("No entity found\n"); goto error; } if(tuple->bridge_entities[0] && !tuple->bridge_entities[0]->disconnected) { e2 = tuple->bridge_entities[0]; e = tuple->bridge_entities[1]; } else if(tuple->bridge_entities[1] && !tuple->bridge_entities[1]->disconnected) { e2 = tuple->bridge_entities[1]; e = tuple->bridge_entities[0]; } tuple->cbf = 0; if(e2 == NULL) { LM_ERR("entity not found for key 2 [%.*s]\n", key2->len, key2->s); goto error; } if(e2->state != B2BL_ENT_CONFIRMED) { LM_ERR("Wrong state for entity ek= [%.*s], tk=[%.*s]\n",e2->key.len, e2->key.s, key2->len, key2->s); goto error; } if(e) { if(e->disconnected && e->state==B2BL_ENT_CONFIRMED) { memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(e); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); } else { b2b_end_dialog(e, tuple); } e->peer = NULL; } // FIXME: this logic may need to be updated if(e2->type == B2B_SERVER) { if(e2 == tuple->servers[0]) { tuple->servers[0] = tuple->servers[1]; tuple->servers[1] = NULL; } else if(e2 == tuple->servers[1]) tuple->servers[1] = NULL; else { LM_ERR("BUG: server entity [%.*s] not found\n", e2->key.len, e2->key.s); goto error; } } else if (e2->type == B2B_CLIENT) { if(e2 == tuple->clients[0]) { tuple->clients[0] = tuple->clients[1]; tuple->clients[1] = NULL; } else if(e2 == tuple->clients[1]) tuple->clients[1] = NULL; else { LM_ERR("BUG: client entity [%.*s] not found\n", e2->key.len, e2->key.s); goto error; } } else { LM_ERR("BUG: unexpected entity type [%d] for [%.*s]\n", e2->type, e2->key.len, e2->key.s); goto error; } b2bl_delete(tuple, hash_index, 0); lock_release(&b2bl_htable[hash_index].lock); /* must restore the b2bl_key for this entity in b2b_entities */ if(b2bl_parse_key(key1, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key [%.*s]\n", key1->len, key1->s); return -1; } /* extract the entity and delete the tuple */ lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("No entity found\n"); goto error; } e1 = tuple->bridge_entities[0]; if(e1 == NULL || e1->disconnected) { LM_ERR("entity not found for key 1 [%.*s]\n", key1->len, key1->s); goto error; } e = tuple->bridge_entities[1]; if(e) { if(e->disconnected && e->state==B2BL_ENT_CONFIRMED) { memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(e); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); } b2b_end_dialog(e, tuple); e->peer = NULL; } /* put it in clients list */ e2->type = B2B_CLIENT; if (tuple->clients[0]) tuple->clients[1] = e2; else tuple->clients[0] = e2; tuple->bridge_entities[1]= e2; e1->peer = e2; e2->peer = e1; e1->no = 0; e2->no = 1; if(b2b_api.update_b2bl_param(e2->type, &e2->key, tuple->key) < 0) { LM_ERR("Failed to update b2bl parameter in b2b_entities\n"); goto error; } LM_DBG("Updated b2bl param for entity [%.*s]\n", e2->key.len, e2->key.s); e1->stats.start_time = get_ticks(); e1->stats.call_time = 0; memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(e1); req_data.method =&method_invite; req_data.extra_headers = NULL; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Failed to send reInvite\n"); goto error; } e1->sdp_type = B2BL_SDP_LATE; e1->state = 0; tuple->scenario_state = B2B_BRIDGING_STATE; if(max_duration) tuple->lifetime = get_ticks() + max_duration; else tuple->lifetime = 0; lock_release(&b2bl_htable[hash_index].lock); return 0; error: if(tuple) b2b_mark_todel(tuple); lock_release(&b2bl_htable[hash_index].lock); return -1; } /* Bridge an initial Invite with an existing dialog */ /* key and entity_no identity the existing call and the which entity from the call * to bridge (0 or 1) */ int b2bl_bridge_msg(struct sip_msg* msg, str* key, int entity_no) { b2bl_tuple_t* tuple; unsigned int hash_index, local_index; b2bl_entity_id_t *bridging_entity= NULL; b2bl_entity_id_t *old_entity; b2bl_entity_id_t *entity; str* server_id; str body, new_body = {0, 0}; str to_uri={NULL,0}, from_uri, from_dname; b2b_req_data_t req_data; b2b_rpl_data_t rpl_data; if(!msg || !key) { LM_ERR("Wrong arguments [%p] [%p]\n", msg, key); return -1; } if(b2bl_parse_key(key, &hash_index, &local_index) < 0) { LM_ERR("Failed to parse key [%.*s]\n", key->len, key->s); return -1; } /* extract the entity and delete the tuple */ lock_get(&b2bl_htable[hash_index].lock); tuple = b2bl_search_tuple_safe(hash_index, local_index); if(tuple == NULL) { LM_ERR("No entity found\n"); goto error; } if(entity_no!=0 && entity_no!=1) { LM_ERR("entity_no param can take only 0 or 1 value, got [%d]\n", entity_no); goto error; } if (!tuple->bridge_entities[entity_no] || tuple->bridge_entities[entity_no]->disconnected) { LM_ERR("Can not bridge requested entity [%p]\n", tuple->bridge_entities[entity_no]); goto error; } bridging_entity = tuple->bridge_entities[entity_no]; old_entity = tuple->bridge_entities[(entity_no?0:1)]; if(old_entity->next || old_entity->prev) { LM_ERR("Can not disconnect multiple entities\n"); goto error; } if(bridging_entity->state != B2BL_ENT_CONFIRMED) { LM_ERR("Wrong state for entity ek= [%.*s], tk=[%.*s]\n", bridging_entity->key.len,bridging_entity->key.s, key->len, key->s); goto error; } b2bl_print_tuple(tuple, L_DBG); if(old_entity) { LM_DBG("terminating b2bl_entity [%p]->[%.*s] type [%d]\n", old_entity, old_entity->key.len, old_entity->key.s, old_entity->type); if(old_entity->disconnected) { memset(&rpl_data, 0, sizeof(b2b_rpl_data_t)); PREP_RPL_DATA(old_entity); rpl_data.method =METHOD_BYE; rpl_data.code =200; rpl_data.text =&ok; b2b_api.send_reply(&rpl_data); } else { memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(old_entity); req_data.method =&method_bye; req_data.no_cb = 1; b2b_api.send_request(&req_data); old_entity->disconnected = 1; } if (old_entity->peer->peer == old_entity) old_entity->peer->peer = NULL; else { LM_ERR("Unexpected chain: old_entity=[%p] and " "old_entity->peer->peer=[%p]\n", old_entity, old_entity->peer->peer); goto error; } old_entity->peer = NULL; /* remove the disconected entity from the tuple */ if(0 == b2bl_drop_entity(old_entity, tuple)) { LM_ERR("Inconsistent entity [%p] on tuple [%p]\n", old_entity, tuple); b2bl_print_tuple(tuple, L_ERR); goto error; } /* destroy the old_entity */ b2b_api.entity_delete(old_entity->type, &old_entity->key, old_entity->dlginfo, 1); if(old_entity->dlginfo) shm_free(old_entity->dlginfo); shm_free(old_entity); old_entity = NULL; b2bl_print_tuple(tuple, L_DBG); } b2b_api.apply_lumps(msg); /* create server entity from Invite */ if (b2b_msg_get_from(msg, &from_uri, &from_dname)< 0 || b2b_msg_get_to(msg, &to_uri, b2bl_htable[hash_index].flags)< 0) { LM_ERR("Failed to get to or from from the message\n"); goto error; } server_id = b2b_api.server_new(msg, &tuple->local_contact, b2b_server_notify, tuple->key); if(server_id == NULL) { LM_ERR("failed to create new b2b server instance\n"); pkg_free(to_uri.s); goto error; } entity = b2bl_create_new_entity(B2B_SERVER, server_id, &to_uri, &from_uri, 0,0, msg); if(entity == NULL) { LM_ERR("Failed to create server entity\n"); pkg_free(to_uri.s); goto error; } pkg_free(to_uri.s); if (0 != b2bl_add_server(tuple, entity)) goto error; entity->peer = bridging_entity; bridging_entity->peer = entity; entity->stats.start_time = get_ticks(); entity->stats.call_time = 0; bridging_entity->no = 0; entity->no = 1; /* send reInvite to the old entity*/ if(msg->content_length) { if ( get_body(msg, &body)!=0 ) { LM_ERR("cannot extract body\n"); return -1; } } memset(&req_data, 0, sizeof(b2b_req_data_t)); PREP_REQ_DATA(bridging_entity); req_data.method =&method_invite; req_data.body = &body; if(b2b_api.send_request(&req_data) < 0) { LM_ERR("Failed to send reInvite\n"); goto error; } bridging_entity->sdp_type = B2BL_SDP_NORMAL; bridging_entity->state = 0; if(max_duration) tuple->lifetime = get_ticks() + max_duration; else tuple->lifetime = 0; tuple->bridge_entities[0] = bridging_entity; tuple->bridge_entities[1] = entity; b2bl_print_tuple(tuple, L_DBG); lock_release(&b2bl_htable[hash_index].lock); if(new_body.s) pkg_free(new_body.s); return 0; error: if(tuple) b2b_mark_todel(tuple); lock_release(&b2bl_htable[hash_index].lock); if(new_body.s) pkg_free(new_body.s); return -1; } opensips-2.2.2/modules/b2b_logic/pidf.c000066400000000000000000000047231300170765700177510ustar00rootroot00000000000000/* * back-to-back logic module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #include #include "pidf.h" xmlAttrPtr xmlNodeGetAttrByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = node->properties; while (attr) { if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0) return attr; attr = attr->next; } return NULL; } unsigned char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = xmlNodeGetAttrByName(node, name); if (attr) return xmlNodeGetContent(attr->children); else return NULL; } xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name) { xmlNodePtr cur = node->children; while (cur) { if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) return cur; cur = cur->next; } return NULL; } xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns) { xmlNodePtr cur = node; while (cur) { xmlNodePtr match = NULL; if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) { if (!ns || (cur->ns && xmlStrcasecmp(cur->ns->prefix, (unsigned char*)ns) == 0)) return cur; } match = xmlNodeGetNodeByName(cur->children, name, ns); if (match) return match; cur = cur->next; } return NULL; } xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns) { xmlNodePtr cur = doc->children; return xmlNodeGetNodeByName(cur, name, ns); } char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns) { xmlNodePtr node = xmlNodeGetNodeByName(root, name, ns); if (node) return (char*)xmlNodeGetContent(node->children); else return NULL; } opensips-2.2.2/modules/b2b_logic/pidf.h000066400000000000000000000024231300170765700177510ustar00rootroot00000000000000/* * back-to-back logic module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #ifndef _B2BL_PIDF_H_ #define _B2BL_PIDF_H_ unsigned char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name); xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name); xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns); char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns); #endif opensips-2.2.2/modules/b2b_logic/records.c000066400000000000000000000502471300170765700204720ustar00rootroot00000000000000/* * back-to-back logic module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #include #include #include #include "../../mem/shm_mem.h" #include "../../ut.h" #include "../presence/hash.h" #include "../presence/utils_func.h" #include "records.h" static void _print_entity(int index, b2bl_entity_id_t* e, int level) { b2bl_entity_id_t* c = e; while (c) { LM_GEN1(level, ".type=[%d] index=[%d] [%p]->[%.*s] state=%d no=%d" " dlginfo=[%p] peer=[%p] prev:next=[%p][%p]\n", c->type, index, c, c->key.len, c->key.s, c->state, c->no, c->dlginfo, c->peer, c->prev, c->next); if (c->dlginfo) LM_GEN1(level, "..........dlginfo=[%p]->[%.*s][%.*s][%.*s]\n", c->dlginfo, c->dlginfo->callid.len, c->dlginfo->callid.s, c->dlginfo->fromtag.len, c->dlginfo->fromtag.s, c->dlginfo->totag.len, c->dlginfo->totag.s); c = c->next; } } void b2bl_print_tuple(b2bl_tuple_t* tuple, int level) { int index; b2bl_entity_id_t* e; if(tuple) { LM_GEN1(level, "[%p]->[%.*s] to_del=[%d] lifetime=[%d]" " bridge_entities[%p][%p][%p]\n", tuple, tuple->key->len, tuple->key->s, tuple->to_del, tuple->lifetime, tuple->bridge_entities[0], tuple->bridge_entities[1], tuple->bridge_entities[2]); for (index = 0; index < MAX_B2BL_ENT; index++) { e = tuple->servers[index]; if (e) _print_entity(index, e, level); } for (index = 0; index < MAX_B2BL_ENT; index++) { e = tuple->clients[index]; if (e) _print_entity(index, e, level); } for (index = 0; index < MAX_BRIDGE_ENT; index++) { e = tuple->bridge_entities[index]; if (e) LM_GEN1(level, ".type=[%d] index=[%d] [%p]" " peer=[%p] prev:next=[%p][%p]\n", e->type, index, e, e->peer, e->prev, e->next); } } } /* Function that inserts a new b2b_logic record - the lock remains taken */ b2bl_tuple_t* b2bl_insert_new(struct sip_msg* msg, unsigned int hash_index, b2b_scenario_t* scenario, str* args[], str* body, str* custom_hdrs, int local_index, str** b2bl_key_s, int db_flag) { b2bl_tuple_t *it, *prev_it; b2bl_tuple_t* tuple = NULL; str* b2bl_key; int i; static char buf[256]; int buf_len= 255; int size; str extra_headers={0, 0}; str local_contact= server_address; if(msg) { if(get_local_contact(msg->rcv.bind_address, &local_contact)< 0) { LM_ERR("Failed to get received address\n"); local_contact= server_address; } } size = sizeof(b2bl_tuple_t) + local_contact.len; tuple = (b2bl_tuple_t*)shm_malloc(size); if(tuple == NULL) { LM_ERR("No more shared memory\n"); goto error; } memset(tuple, 0, size); tuple->local_contact.s = (char*)(tuple + 1); memcpy(tuple->local_contact.s, local_contact.s, local_contact.len); tuple->local_contact.len = local_contact.len; tuple->scenario = scenario; if(msg) { if(b2b_extra_headers(msg, NULL, custom_hdrs, &extra_headers)< 0) { LM_ERR("Failed to create extra headers\n"); goto error; } if(extra_headers.s) { tuple->extra_headers = (str*)shm_malloc(sizeof(str) + extra_headers.len); if(tuple->extra_headers == NULL) { LM_ERR("No more shared memory\n"); goto error; } tuple->extra_headers->s = (char*)tuple->extra_headers + sizeof(str); memcpy(tuple->extra_headers->s, extra_headers.s, extra_headers.len); tuple->extra_headers->len = extra_headers.len; pkg_free(extra_headers.s); } } if(use_init_sdp || (scenario && scenario->use_init_sdp)) { if (!body && scenario && scenario->body.len) { body = &scenario->body; /* we also have to add the content type here */ tuple->extra_headers = (str *)shm_realloc(tuple->extra_headers, sizeof(str) + extra_headers.len + 14/* "Content-Type: " */ + 2/* "\r\n\" */ + scenario->body_type.len); if (!tuple->extra_headers) { LM_ERR("cannot add extra headers\n"); goto error; } /* restore initial data */ tuple->extra_headers->s = (char*)tuple->extra_headers + sizeof(str); tuple->extra_headers->len = extra_headers.len; memcpy(tuple->extra_headers->s + tuple->extra_headers->len, "Content-Type: ", 14); tuple->extra_headers->len += 14; memcpy(tuple->extra_headers->s + tuple->extra_headers->len, scenario->body_type.s, scenario->body_type.len); tuple->extra_headers->len += scenario->body_type.len; memcpy(tuple->extra_headers->s + tuple->extra_headers->len, "\r\n", 2); tuple->extra_headers->len += 2; } if (body) { /* alloc separate memory for sdp */ tuple->sdp.s = shm_malloc(body->len); if (!tuple->sdp.s) { LM_ERR("no more shm memory for sdp body\n"); goto error; } memcpy(tuple->sdp.s, body->s, body->len); tuple->sdp.len = body->len; } } /* copy the function parameters that customize the scenario */ memset(tuple->scenario_params, 0, MAX_SCENARIO_PARAMS* sizeof(str)); if(scenario && args) { for(i = 0; i< scenario->param_no; i++) { if(args[i]==NULL) { LM_DBG("Fewer parameters, expected [%d] received [%d]\n", scenario->param_no, i); break; } /* must print the value of the argument */ if(msg && b2bl_caller != CALLER_MODULE) { buf_len= 255; if(pv_printf(msg, (pv_elem_t*)args[i], buf, &buf_len)<0) { LM_ERR("cannot print the format\n"); goto error; } tuple->scenario_params[i].s = (char*)shm_malloc(buf_len); if(tuple->scenario_params[i].s == NULL) { LM_ERR("No more shared memory\n"); goto error; } memcpy(tuple->scenario_params[i].s, buf, buf_len); tuple->scenario_params[i].len = buf_len; LM_DBG("Printed parameter [%.*s]\n", buf_len, buf); } else { if(args[i]->s==NULL || args[i]->len==0) { LM_DBG("Fewer parameters, expected [%d] received [%d]\n", scenario->param_no, i); break; } tuple->scenario_params[i].s = (char*)shm_malloc(args[i]->len); if(tuple->scenario_params[i].s == NULL) { LM_ERR("No more shared memory\n"); goto error; } memcpy(tuple->scenario_params[i].s, args[i]->s, args[i]->len); tuple->scenario_params[i].len = args[i]->len; } } } tuple->scenario_state = B2B_NOTDEF_STATE; lock_get(&b2bl_htable[hash_index].lock); if(local_index>= 0) /* a local index specified */ { tuple->id = local_index; if(b2bl_htable[hash_index].first == NULL) { b2bl_htable[hash_index].first = tuple; tuple->prev = tuple->next = NULL; } else { prev_it = 0; /*insert it in the proper place */ for(it = b2bl_htable[hash_index].first; it && it->idnext) { prev_it = it; } if(!prev_it) { b2bl_htable[hash_index].first = tuple; tuple->prev = 0; tuple->next = it; it->prev = tuple; } else { tuple->prev = prev_it; prev_it->next = tuple; tuple->next = it; if(it) it->prev = tuple; } } } else { it = b2bl_htable[hash_index].first; if(it == NULL) { b2bl_htable[hash_index].first = tuple; tuple->prev = tuple->next = NULL; tuple->id = 0; } else { while(it) { prev_it = it; it = it->next; } prev_it->next = tuple; tuple->prev = prev_it; tuple->id = prev_it->id +1; } } LM_DBG("hash index [%d]:\n", hash_index); for(it = b2bl_htable[hash_index].first; it; it=it->next) { LM_DBG("id [%d]", it->id); } b2bl_key = b2bl_generate_key(hash_index, tuple->id); if(b2bl_key == NULL) { LM_ERR("failed to generate b2b logic key\n"); goto error; } tuple->key = b2bl_key; *b2bl_key_s = b2bl_key; tuple->db_flag = db_flag; LM_DBG("new tuple [%p]->[%.*s]\n", tuple, b2bl_key->len, b2bl_key->s); return tuple; error: if (tuple) { if (tuple->sdp.s) shm_free(tuple->sdp.s); shm_free(tuple); } lock_release(&b2bl_htable[hash_index].lock); return 0; } void unchain_ent(b2bl_entity_id_t *ent, b2bl_entity_id_t **first_ent) { if(*first_ent == ent) { *first_ent = ent->next; if(ent->next) ent->next->prev = NULL; } else { if(ent->prev) ent->prev->next = ent->next; if(ent->next) ent->next->prev = ent->prev; } ent->prev = NULL; ent->next = NULL; } int b2bl_drop_entity(b2bl_entity_id_t* entity, b2bl_tuple_t* tuple) { b2bl_entity_id_t* e; unsigned int index; int found = 0; int i; for (index = 0; index < MAX_B2BL_ENT; index++) { e = tuple->servers[index]; if (e == entity) { found = 1; switch(index) { case 0: tuple->servers[0] = tuple->servers[1]; case 1: tuple->servers[1] = tuple->servers[2]; case 2: tuple->servers[2] = NULL; for ( i=0 ; iservers[index] == NULL) LM_ERR("inconsistent tuple [%p]->[%.*s]\n", tuple, tuple->key->len, tuple->key->s); } break; default: LM_CRIT("we should never end up here\n"); } break; } e = tuple->clients[index]; if (e == entity) { found = 1; switch(index) { case 0: tuple->clients[0] = tuple->clients[1]; case 1: tuple->clients[1] = tuple->clients[2]; case 2: tuple->clients[2] = NULL; for ( i=0 ; iclients[index] == NULL) LM_ERR("inconsistent tuple [%p]->[%.*s]\n", tuple, tuple->key->len, tuple->key->s); } break; default: LM_CRIT("we should never end up here\n"); } break; } } return found; } void b2bl_remove_single_entity(b2bl_entity_id_t *entity, b2bl_entity_id_t **head) { unchain_ent(entity, head); b2b_api.entity_delete(entity->type, &entity->key, entity->dlginfo, 0); LM_DBG("destroying dlginfo=[%p]\n", entity->dlginfo); if(entity->dlginfo) shm_free(entity->dlginfo); shm_free(entity); return; } void b2bl_delete_entity(b2bl_entity_id_t* entity, b2bl_tuple_t* tuple) { unsigned int i; int found = 0; if (entity->next || entity->prev) { LM_ERR("Inconsistent entity [%p]\n", entity); b2bl_print_tuple(tuple, L_CRIT); return; } found = b2bl_drop_entity(entity, tuple); if(found) { LM_DBG("delete entity [%p]->[%.*s] from tuple [%.*s]\n", entity, entity->key.len, entity->key.s, tuple->key->len, tuple->key->s); b2b_api.entity_delete(entity->type, &entity->key, entity->dlginfo, 1); } else { LM_WARN("entity [%p]->[%.*s] not found for tuple [%.*s]\n", entity, entity->key.len, entity->key.s, tuple->key->len, tuple->key->s); } if(entity->dlginfo) shm_free(entity->dlginfo); for(i = 0; i< MAX_BRIDGE_ENT; i++) if(tuple->bridge_entities[i] == entity) tuple->bridge_entities[i] = NULL; /* if(entity->peer && entity->peer->peer==entity) entity->peer->peer = NULL; */ for(i = 0; i< MAX_B2BL_ENT; i++) { if(tuple->servers[i] && tuple->servers[i]->peer==entity) tuple->servers[i]->peer= NULL; if(tuple->clients[i] && tuple->clients[i]->peer==entity) tuple->clients[i]->peer= NULL; } LM_INFO("delete tuple [%.*s], entity [%.*s]\n", tuple->key->len, tuple->key->s, entity->key.len, entity->key.s); shm_free(entity); /* for debuging */ b2bl_print_tuple(tuple, L_DBG); } int b2bl_add_client(b2bl_tuple_t* tuple, b2bl_entity_id_t* entity) { int i, pos; LM_INFO("adding entity [%p]->[%.*s] to tuple [%p]->[%.*s]\n", entity, entity->key.len, entity->key.s, tuple, tuple->key->len, tuple->key->s); for (pos = 0; pos < MAX_B2BL_ENT && tuple->clients[pos]; pos++); if (pos == MAX_B2BL_ENT) { LM_ERR("unable to add entity [%p]->[%.*s] to tuple [%p]->[%.*s], all spots taken\n", entity, entity->key.len, entity->key.s, tuple, tuple->key->len, tuple->key->s); return -1; } /* check for inconsistencies */ for (i = pos + 1; i < MAX_B2BL_ENT; i++) if (tuple->clients[i]) { LM_ERR("inconsistent clients state for tuple [%p]->[%.*s] pos %d\n", tuple, tuple->key->len, tuple->key->s, i); return -1; } tuple->clients[pos] = entity; b2bl_print_tuple(tuple, L_DBG); return 0; } int b2bl_add_server(b2bl_tuple_t* tuple, b2bl_entity_id_t* entity) { LM_INFO("adding entity [%p]->[%.*s] to tuple [%p]->[%.*s]\n", entity, entity->key.len, entity->key.s, tuple, tuple->key->len, tuple->key->s); if (tuple->servers[0] == NULL) { if (tuple->servers[1]) { LM_ERR("inconsistent servers state for tuple [%p]->[%.*s]\n", tuple, tuple->key->len, tuple->key->s); return -1; } tuple->servers[0] = entity; } else if (tuple->servers[1] == NULL) tuple->servers[1] = entity; else { LM_ERR("unable to add entity [%p]->[%.*s] to tuple [%p]->[%.*s], all spots taken\n", entity, entity->key.len, entity->key.s, tuple, tuple->key->len, tuple->key->s); return -1; } b2bl_print_tuple(tuple, L_DBG); return 0; } void b2bl_delete(b2bl_tuple_t* tuple, unsigned int hash_index, int not_del_b2be) { b2bl_entity_id_t *e; int i; int index; b2bl_cb_params_t cb_params; LM_DBG("Delete record [%p]->[%.*s], hash_index=[%d], local_index=[%d]\n", tuple, tuple->key->len, tuple->key->s, hash_index, tuple->id); /* * razvanc: if the tuple is not actually deleted, we do not have to call * the DESTROY callback */ if(!not_del_b2be && tuple->cbf && tuple->cb_mask&B2B_DESTROY_CB) { memset(&cb_params, 0, sizeof(b2bl_cb_params_t)); cb_params.param = tuple->cb_param; cb_params.stat = NULL; cb_params.msg = NULL; /* setting it to 0 but it has no meaning in this callback type */ cb_params.entity = 0; tuple->cbf(&cb_params, B2B_DESTROY_CB); } if(!not_del_b2be) b2bl_db_delete(tuple); if(b2bl_htable[hash_index].first == tuple) { b2bl_htable[hash_index].first = tuple->next; if(tuple->next) tuple->next->prev = NULL; } else { if(tuple->prev) tuple->prev->next = tuple->next; if(tuple->next) tuple->next->prev = tuple->prev; } for (index = 0; index < MAX_B2BL_ENT; index++) { e = tuple->servers[index]; if (e) { if (e->key.s && e->key.len && !not_del_b2be) b2b_api.entity_delete(e->type, &e->key, e->dlginfo, 0); if(e->dlginfo) shm_free(e->dlginfo); shm_free(e); } e = tuple->clients[index]; if (e) { if (e->key.s && e->key.len && !not_del_b2be) b2b_api.entity_delete(e->type, &e->key, e->dlginfo, 0); if(e->dlginfo) shm_free(e->dlginfo); shm_free(e); } } /* clean up all entities in b2b_entities from db */ if(!not_del_b2be) b2b_api.entities_db_delete(*tuple->key); // if(tuple->bridge_entities[1] && tuple->bridge_entities[1]->key.s != NULL) // shm_free(tuple->bridge_entities[1]->key.s); for(i = 0; i< MAX_SCENARIO_PARAMS; i++) { if(tuple->scenario_params[i].s) shm_free(tuple->scenario_params[i].s); } if(tuple->key) shm_free(tuple->key); if(tuple->extra_headers) shm_free(tuple->extra_headers); if(tuple->b1_sdp.s) shm_free(tuple->b1_sdp.s); if (tuple->sdp.s && tuple->sdp.s != tuple->b1_sdp.s) shm_free(tuple->sdp.s); shm_free(tuple); } /* key format : hash_index.local * */ int b2bl_parse_key(str* key, unsigned int* hash_index, unsigned int* local_index) { char* p; int hi_len; str s; if(!key || !key->s || !key->len) return -1; p= strchr(key->s, '.'); if(p == NULL) { LM_ERR("Wrong b2b logic key\n"); return -1; } hi_len = p - key->s; s.s = key->s; s.len = hi_len; if(str2int(&s, hash_index)< 0) return -1; s.s = p+1; s.len = key->s + key->len - s.s; if(str2int(&s, local_index)< 0) return -1; LM_DBG("hash_index = [%d] - local_index= [%d]\n", *hash_index, *local_index); return 0; } str* b2bl_generate_key(unsigned int hash_index, unsigned int local_index) { char buf[15]; str* b2b_key; int len; len = sprintf(buf, "%d.%d", hash_index, local_index); b2b_key = (str*)shm_malloc(sizeof(str)+ len); if(b2b_key== NULL) { LM_ERR("no more shared memory\n"); return NULL; } b2b_key->s = (char*)b2b_key + sizeof(str); memcpy(b2b_key->s, buf, len); b2b_key->len = len; return b2b_key; } b2bl_tuple_t* b2bl_search_tuple_safe(unsigned int hash_index, unsigned int local_index) { b2bl_tuple_t* tuple; tuple = b2bl_htable[hash_index].first; while(tuple && tuple->id != local_index) { tuple = tuple->next; } return tuple; } int init_b2bl_htable(void) { int i; b2bl_htable = (b2bl_table_t)shm_malloc(b2bl_hsize* sizeof(b2bl_entry_t)); if(!b2bl_htable) ERR_MEM(SHARE_MEM); memset(b2bl_htable, 0, b2bl_hsize* sizeof(b2bl_entry_t)); for(i= 0; i< b2bl_hsize; i++) { lock_init(&b2bl_htable[i].lock); b2bl_htable[i].first = NULL; } return 0; error: return -1; } void destroy_b2bl_htable(void) { int i; b2bl_tuple_t* tuple; if(!b2bl_htable) return; for(i= 0; i< b2bl_hsize; i++) { lock_destroy(&b2bl_htable[i].lock); tuple = b2bl_htable[i].first; while(tuple) { b2bl_delete(tuple, i, 1); tuple = b2bl_htable[i].first; } } shm_free(b2bl_htable); } /* Take headers to pass on the other side: * Content-Type: * Allow: * Supported: * Require * RSeq * Session-Expires * Min-SE */ #define HDR_BUF_SIZE 256 int b2b_extra_headers(struct sip_msg* msg, str* b2bl_key, str* custom_hdrs, str* extra_headers) { char* p; struct hdr_field* require_hdr; struct hdr_field* rseq_hdr; struct hdr_field* subscription_state_hdr; struct hdr_field* hdr; struct hdr_field* hdrs[HDR_LST_LEN + HDR_DEFAULT_LEN]; regmatch_t pmatch; int hdrs_no = 0; int len = 0; int i; int custom_hdrs_len = 0; char tmp; if(msg->content_type) hdrs[hdrs_no++] = msg->content_type; if(msg->supported) hdrs[hdrs_no++] = msg->supported; if(msg->allow) hdrs[hdrs_no++] = msg->allow; if(msg->proxy_require) hdrs[hdrs_no++] = msg->proxy_require; if(msg->session_expires) hdrs[hdrs_no++] = msg->session_expires; if(msg->min_se) hdrs[hdrs_no++] = msg->min_se; if(msg->event) hdrs[hdrs_no++] = msg->event; require_hdr = get_header_by_static_name( msg, "Require"); if(require_hdr) hdrs[hdrs_no++] = require_hdr; rseq_hdr = get_header_by_static_name( msg, "RSeq"); if(rseq_hdr) hdrs[hdrs_no++] = rseq_hdr; subscription_state_hdr = get_header_by_static_name( msg, "Subscription-state"); if(subscription_state_hdr) hdrs[hdrs_no++] = subscription_state_hdr; /* add also the custom headers */ for(i = 0; i< custom_headers_lst_len; i++) { hdr = get_header_by_name( msg, custom_headers_lst[i].s, custom_headers_lst[i].len); if(hdr) { hdrs[hdrs_no++] = hdr; } } if (custom_headers_re) { for (hdr=msg->headers; hdr; hdr=hdr->next) { tmp = hdr->name.s[hdr->name.len]; hdr->name.s[hdr->name.len] = '\0'; i = regexec(custom_headers_re, hdr->name.s, 1, &pmatch, 0); hdr->name.s[hdr->name.len] = tmp; if (i == 0) { /* check if added twice */ for(i = 0; i < hdrs_no; i++) { if ( hdrs[i]->name.len == hdr->name.len && strncmp(hdrs[i]->name.s, hdr->name.s, hdr->name.len)==0 ) break; } if (i == hdrs_no) /* Doubles not found -> add it */ hdrs[hdrs_no++] = hdr; } } } /* calculate the length*/ for(i = 0; i< hdrs_no; i++) len += hdrs[i]->len; if(init_callid_hdr.len && msg && msg->callid) len+= init_callid_hdr.len + msg->callid->len; if(custom_hdrs && custom_hdrs->s && custom_hdrs->len) { custom_hdrs_len = custom_hdrs->len; len += custom_hdrs_len; } if(len == 0) return 0; extra_headers->s = (char*)pkg_malloc(len); if(extra_headers->s == NULL) { LM_ERR("No more memory\n"); return -1; } p = extra_headers->s; /* construct the headers string */ for(i = 0; i< hdrs_no; i++) { memcpy(p, hdrs[i]->name.s, hdrs[i]->len); p += hdrs[i]->len; } if(custom_hdrs_len) { memcpy(p, custom_hdrs->s, custom_hdrs_len); p += custom_hdrs_len; } if(init_callid_hdr.s && msg && msg->callid) { memcpy(p, init_callid_hdr.s, init_callid_hdr.len); p += init_callid_hdr.len; len = sprintf(p, ": %.*s", (int)(msg->callid->name.s +msg->callid->len -msg->callid->body.s), msg->callid->body.s); p += len; } extra_headers->len = p - extra_headers->s; return 0; } opensips-2.2.2/modules/b2b_logic/records.h000066400000000000000000000106201300170765700204660ustar00rootroot00000000000000/* * back-to-back logic module * * Copyright (C) 2009 Free Software Fundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-08-03 initial version (Anca Vamanu) */ #ifndef _B2BL_RECORDS_H #define _B2BL_RECORDS_H #include #include #include "../../str.h" #include "../../lock_ops.h" #include "b2b_logic.h" #include "b2b_load.h" typedef struct b2bl_entity_id { str scenario_id; str key; str to_uri; str from_uri; str from_dname; b2b_dlginfo_t* dlginfo; int disconnected; int state; unsigned short no; unsigned short sdp_type; enum b2b_entity_type type; b2bl_dlg_stat_t stats; struct b2bl_entity_id* peer; struct b2bl_entity_id* prev; struct b2bl_entity_id* next; }b2bl_entity_id_t; #define B2BL_SDP_NORMAL 0 #define B2BL_SDP_LATE 1 #define B2BL_SDP_RENEW 2 #define NO_UPDATEDB_FLAG 0 #define UPDATEDB_FLAG 1 #define INSERTDB_FLAG 2 #define MAX_B2BL_ENT 3 #define MAX_BRIDGE_ENT 3 #define MAX_SCENARIO_PARAMS 5 typedef struct b2bl_tuple { unsigned int id; str* key; b2b_scenario_t* scenario; /* if scenario is NULL it means that the simple Topology Hiding Scenary must be applied*/ str scenario_params[MAX_SCENARIO_PARAMS]; int scenario_state; int next_scenario_state; b2bl_entity_id_t* servers[MAX_B2BL_ENT]; b2bl_entity_id_t* clients[MAX_B2BL_ENT]; b2bl_entity_id_t* bridge_entities[MAX_BRIDGE_ENT]; int to_del; str* extra_headers; struct b2bl_tuple* next; struct b2bl_tuple* prev; unsigned int lifetime; str local_contact; str sdp; str b1_sdp; /* used for multiple attempts to bridge the first entity */ int db_flag; b2bl_cback_f cbf; unsigned int cb_mask; void* cb_param; }b2bl_tuple_t; typedef struct b2bl_entry { b2bl_tuple_t* first; gen_lock_t lock; int flags; }b2bl_entry_t; typedef b2bl_entry_t* b2bl_table_t; #define PREP_REQ_DATA(entity) do{ \ req_data.et =(entity)->type; \ req_data.b2b_key =&(entity)->key; \ req_data.dlginfo =(entity)->dlginfo; \ }while(0) #define PREP_RPL_DATA(entity) do{ \ rpl_data.et =(entity)->type; \ rpl_data.b2b_key =&(entity)->key; \ rpl_data.dlginfo =(entity)->dlginfo; \ }while(0) void b2bl_print_tuple(b2bl_tuple_t* tuple, int log_level); b2bl_tuple_t* b2bl_insert_new(struct sip_msg* msg, unsigned int hash_index, b2b_scenario_t* scenario, str* args[], str* body, str* custom_hdrs, int local_index, str** b2bl_key_s, int db_flag); str* b2bl_generate_key(unsigned int hash_index, unsigned int local_index); int b2bl_parse_key(str* key, unsigned int* hash_index, unsigned int* local_index); b2bl_tuple_t* b2bl_search_tuple_safe(unsigned int hash_index, unsigned int local_index); void b2bl_delete(b2bl_tuple_t* tuple, unsigned int hash_index, int not_del_b2be); int init_b2bl_htable(void); extern b2bl_table_t b2bl_htable; extern unsigned int b2bl_hsize; int process_bridge_action(struct sip_msg* msg, b2bl_entity_id_t* curr_entity, b2bl_tuple_t* tuple, xmlNodePtr bridge_node); void destroy_b2bl_htable(void); b2bl_entity_id_t* b2bl_create_new_entity(enum b2b_entity_type type, str* entity_id, str* to_uri,str* from_uri,str* from_dname,str* ssid,struct sip_msg* msg); void unchain_ent(b2bl_entity_id_t *ent, b2bl_entity_id_t **head); void b2bl_remove_single_entity(b2bl_entity_id_t *entity, b2bl_entity_id_t **head); int b2bl_drop_entity(b2bl_entity_id_t* entity, b2bl_tuple_t* tuple); void b2bl_delete_entity(b2bl_entity_id_t* entity, b2bl_tuple_t* tuple); int b2b_extra_headers(struct sip_msg* msg, str* b2bl_key, str* custom_hdrs, str* extra_headers); int b2bl_add_client(b2bl_tuple_t* tuple, b2bl_entity_id_t* entity); int b2bl_add_server(b2bl_tuple_t* tuple, b2bl_entity_id_t* entity); void b2bl_db_delete(b2bl_tuple_t* tuple); #endif opensips-2.2.2/modules/b2b_sca/000077500000000000000000000000001300170765700163265ustar00rootroot00000000000000opensips-2.2.2/modules/b2b_sca/.gitignore000066400000000000000000000000151300170765700203120ustar00rootroot00000000000000*.o *.so *.d opensips-2.2.2/modules/b2b_sca/Makefile000066400000000000000000000003451300170765700177700ustar00rootroot00000000000000# $Id: Makefile 806 2006-04-14 11:00:10Z bogdan_iancu $ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=b2b_sca.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/b2b_sca/README000066400000000000000000000341401300170765700172100ustar00rootroot00000000000000b2b_sca Module Ovidiu Sas Edited by Ovidiu Sas Copyright © 2011-2013 VoIP Embedded, Inc. Revision History Revision $Rev: 8688 $ $Date: 2012-01-25 13:52:05 -0500 (Wed, 25 Jan 2012) $ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. To-do 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.4. Exported Parameters 1.4.1. hash_size(integer) 1.4.2. presence_server(string) 1.4.3. watchers_avp_spec(string) 1.4.4. shared_line_spec_param(string) 1.4.5. appearance_name_addr_spec_param(string) 1.4.6. db_url(string) 1.4.7. db_mode(integer) 1.4.8. table_name(string) 1.4.9. shared_line_column(string) 1.4.10. watchers_column(string) 1.4.11. app[index]_shared_entity_column(string) 1.4.12. app[index]_call_state_column(string) 1.4.13. app[index]_call_info_uri_column(string) 1.4.14. app[index]_call_info_appearance_uri_column(st ring) 1.4.15. app[index]_b2bl_key_column(string) 1.5. Exported Functions 1.5.1. sca_init_request(shared_line) 1.5.2. sca_bridge_request(shared_line_to bridge) 1.6. Exported MI Functions 1.6.1. sca_list List of Examples 1.1. Set hash_size parameter 1.2. Set presence_server parameter 1.3. Set watchers_avp_spec parameter 1.4. Set shared_line_spec_param parameter 1.5. Set appearance_name_addr_spec_param parameter 1.6. Set db_url parameter 1.7. Set db_mode parameter 1.8. Set table_name parameter 1.9. Set shared_line_column parameter 1.10. Set watchers_column parameter 1.11. Set app[index]_shared_entity_column parameter 1.12. Set app[index]_call_state_column parameter 1.13. Set app[index]_call_info_uri_column parameter 1.14. Set app[index]_call_info_appearance_uri_column parameter 1.15. Set app[index]_b2bl_key_column parameter 1.16. sca_init_request() usage Chapter 1. Admin Guide 1.1. Overview This module provides core SCA (Shared Call Appearance) functionality for OpenSIPS. It is designed to work in tandem with the presence_callinfo module. The module handles the basic SIP signalling for call controll while publishing callinfo events to a presence server. It is built on top of the b2b_logic module and it is using the 'top hiding' scenario to control SIP signalling. A typical usage example is provided below, where Alice makes a call to Bob. The call leg between Alice and the b2b_sca server is an "appearance" call of the "shared" call between the b2b_sca server and Bob. caller caller b2b_sca callee presence server alice1@example alice2@example server bob@example watcher@example | | | | | |--INV bob------------------>| | | | | |--INV bob->| | | | |--PUBLISH(alerting)--->| | | |<-----200 OK-----------| | | | | | | | |<-180 ring-| | |<-180 ring------------------| | | | | | | | | | | | | | | |<-200 OK---| | |<-200 OK--------------------|--ACK----->| | |--ACK---------------------->|--PUBLISH(active)----->| | | |<-----200 OK-----------| | | | | | |--INV bob (hold)----------->| | | | | |--INV bob->| | | | |--PUBLISH(held)------->| | | |<-----200 OK-----------| | | |<-200 OK---| | |<--200 OK-------------------| | | | | | | | | |--INV------->| | | | | |--INV bob->| | |<-BYE-----------------------|--PUBLISH(active)----->| |--200 OK------------------->|<-----200 OK-----------| | | |<-200 OK---| | | |<-200 OK-----| | * Alice calls Bob from her desk IP phone (alice1). * Bob answers the call. * Alice decide to carry the conversation from a meeting room and she put's BOB on hold. * Alice arrives to the meeting room and retrieves the call on the conference room IP phone (alice2). 1.2. To-do Features to be added in the future: * possibility to handle unlimited number of appearances. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * tm module. pua module. b2b_logic module. 1.4. Exported Parameters 1.4.1. hash_size(integer) The size of the hash table internally used to keep the shared calls. A larger table means faster acces at the expense of memory. The hash size is a power of number two. The default value is "10". Example 1.1. Set hash_size parameter ... modparam("b2b_sca", "hash_size", "5") ... 1.4.2. presence_server(string) The address of the presence server, where the PUBLISH messages should be sent (not compulsory). If not set, the PUBLISH requests will be routed based on watcher's URI. The default value is "NULL". Example 1.2. Set presence_server parameter ... modparam("b2b_sca", "presence_server", "sip:opensips.org") ... 1.4.3. watchers_avp_spec(string) AVP that will hold one or more watcher URI(s). If not set, no PUBLISH requests will be sent out. The watchers_avp_spec MUST be set before calling sca_init_request(); The default value is "NULL". Example 1.3. Set watchers_avp_spec parameter ... modparam("b2b_sca", "watchers_avp_spec", "$avp(watchers_avp_spec)") ... route { ... $avp(watchers_avp_spec) = "sip:first_watcher@opensip.org"; $avp(watchers_avp_spec) = "sip:second_watcher@opensip.org"; ... } 1.4.4. shared_line_spec_param(string) Mandatory parameter. Opaque string identifing the shared line/call. The shared_line_spec_param MUST be set before calling sca_init_request(); The default value is "NULL". Example 1.4. Set shared_line_spec_param parameter ... modparam("b2b_sca", "shared_line_spec_param", "$var(shared_line)") ... 1.4.5. appearance_name_addr_spec_param(string) Mandatory parameter. It must be a valid SIP URI. It will populate the appearance-uri SIP parameter inside the Call-Info SIP header. The appearance_name_addr_spec_param MUST be set before calling sca_init_request(); The default value is "NULL". Example 1.5. Set appearance_name_addr_spec_param parameter ... modparam("b2b_sca", "appearance_name_addr_spec_param", "") ... 1.4.6. db_url(string) This is URL of the database to be used. The default value is "NULL". Example 1.6. Set db_url parameter ... modparam("b2b_sca", "db_url", "[dbdriver]://[[username]:[password]]@[dbh ost]/[dbname]") ... 1.4.7. db_mode(integer) The b2b_sca module can utilize database for persistent call appearance storage. Using a database ensure that active call appearances will survive machine restarts or SW crashes. The following databse accessing modes are available for b2b_sca module: * NO DB STORAGE - set this parameter to 0 * WRITE THROUGH (synchronous write in database) - set this parameter to 1 The default value is 0 (NO DB STORAGE). Example 1.7. Set db_mode parameter ... modparam("b2b_sca", "db_mode", 1) ... 1.4.8. table_name(string) Identifies the table name from the defined database. The default value is "b2b_sca". Example 1.8. Set table_name parameter ... modparam("b2b_sca", "table_name", "sla") ... 1.4.9. shared_line_column(string) The column's name in the database storing the shared call/line id. See "shared_line_spec_param" parameter. The default value is "shared_line". Example 1.9. Set shared_line_column parameter ... modparam("b2b_sca", "shared_line_column", "") ... 1.4.10. watchers_column(string) The column's name in the database storing the list of watchers. See "watchers_avp_spec" parameter. The default value is "watchers". Example 1.10. Set watchers_column parameter ... modparam("b2b_sca", "watchers_column", "") ... 1.4.11. app[index]_shared_entity_column(string) The column's name in the database storing the shared entity of a particular appearance. See "sca_init_request" for more info. The default value is "app[index]_shared_entity". Index is an integer between 1 and 10. Example 1.11. Set app[index]_shared_entity_column parameter ... modparam("b2b_sca", "app1_shared_entity_column", "first_shared_entity") modparam("b2b_sca", "app2_shared_entity_column", "second_shared_entity") ... 1.4.12. app[index]_call_state_column(string) The column's name in the database storing the call state of a particular appearance. The following states are stored: * 1 - alerting, * 2 - active, * 3 - held, * 4 - held-private. The default value is "app[index]_call_state". Index is an integer between 1 and 10. Example 1.12. Set app[index]_call_state_column parameter ... modparam("b2b_sca", "app1_call_state_column", "first_call_state") modparam("b2b_sca", "app2_call_state_column", "second_call_state") ... 1.4.13. app[index]_call_info_uri_column(string) The column's name in the database storing the call info URI of a particular appearance. The default value is "app[index]_call_info_uri". Index is an integer between 1 and 10. Example 1.13. Set app[index]_call_info_uri_column parameter ... modparam("b2b_sca", "app1_call_info_uri_column", "first_call_info_uri") modparam("b2b_sca", "app2_call_info_uri_column", "second_call_info_uri") ... 1.4.14. app[index]_call_info_appearance_uri_column(string) The column's name in the database storing the call info appearance URI of a particular appearance. For each appearance, the value is extracted from the "appearance_name_addr_spec_param" parameter. The default value is "app[index]_call_info_appearance_uri". Index is an integer between 1 and 10. Example 1.14. Set app[index]_call_info_appearance_uri_column parameter ... modparam("b2b_sca", "app1_call_info_appearance_uri_column", "first_call_ info_appearance_uri") modparam("b2b_sca", "app2_call_info_appearance_uri_column", "second_call _info_appearance_uri") ... 1.4.15. app[index]_b2bl_key_column(string) The column's name in the database storing the b2b_logic key of a particular appearance. The default value is "app[index]_b2bl_key". Index is an integer between 1 and 10. Example 1.15. Set app[index]_b2bl_key_column parameter ... modparam("b2b_sca", "app1_b2bl_key_column", "first_b2bl_key") modparam("b2b_sca", "app2_b2bl_key_column", "second_b2bl_key") ... 1.5. Exported Functions 1.5.1. sca_init_request(shared_line) This is the function that must be called by the script writer on an initial INVITE for which an SCA call must be instantiated (see the call from alice1 in the above diagram). Meaning of the parameters: * shared_line - a PV (pseudo-variable) identifying the call leg as being an "appearnace" call or a "shared" call: + 0: "shared" call + 1: "appearance" call Example 1.16. sca_init_request() usage ... modparam("b2b_sca", "shared_line_spec_param","$var(shared_line)") modparam("b2b_sca", "appearance_name_addr_spec_param","$var(appearance_name_addr)") modparam("b2b_sca", "watchers_avp_spec","$avp(watchers_avp_spec)") ... # Setting the shared call identifier $var(shared_line) = "alice"; # Setting the watchers $avp(watchers_avp_spec) = "sip:alice1@example.com"; $avp(watchers_avp_spec) = "sip:alice2@example.com"; if (INCOMING_SHARED_CALL) { # The incoming call is a 'shared' call $var(shared_line_entity) = 0; # Setting the appearance name address $var(appearance_name_addr) = $fu; } else { # The incoming call is an 'appearance' call # - see Alice's initial call leg in the given example $var(shared_line_entity) = 1; # Setting the appearance name address $var(appearance_name_addr) = $tu; } # Initiate the call if (!sca_init_request("$var(shared_line_entity)")) { send_reply("403", "Internal Server Error (SLA)"); exit; } ... 1.5.2. sca_bridge_request(shared_line_to bridge) This is the function that must be called by the script writer on an initial "appearance" INVITE for an existing shared call. It will bridge the current "appearance" call with the existing "shared" call and the old "appearance" call will be disconnected (see the call from alice2 in the above diagram). Meaning of the parameters: * shared_line_to_bridge - a PV identifying the shared line/call that was previously set by sca_init_request(). ... if ($rU==NULL && is_method("INVITE") && $fU==$tU && is_present_hf("Call-Info")) { # The incoming call is an 'appearance' call # - see Alice's call from alice2 in the given example $var(shared_line_to_bridge) = "alice"; if (!sca_bridge_request("$var(shared_line_to_bridge)")) send_reply("403", "Internal SLA Error"); exit; } } ... 1.6. Exported MI Functions 1.6.1. sca_list It lists the appearances belonging to a shared line/call. Name: sca_list Parameters: none MI FIFO Command Format: :sca_list:_reply_fifo_file_ _empty_line_ opensips-2.2.2/modules/b2b_sca/b2b_sca.c000066400000000000000000000440151300170765700177710ustar00rootroot00000000000000/* * b2b_sca module * * Copyright (C) 2010 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-11-02 initial version (Ovidiu Sas) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../mod_fix.h" #include "../../trim.h" #include "../../db/db.h" #include "../../parser/parse_from.h" #include "../tm/tm_load.h" #include "../pua/pua_bind.h" #include "../b2b_logic/b2b_load.h" #include "sca_records.h" #include "sca_logic.h" #include "sca_db_handler.h" extern str app_state[]; /** Functions declarations */ static int mod_init(void); static void mod_destroy(void); static int child_init(int rank); int sca_init_request(struct sip_msg* msg); int sca_bridge_request(struct sip_msg* msg, str* arg1, str* arg2); static struct mi_root* mi_sca_list(struct mi_root* cmd, void* param); /** Global variables */ struct tm_binds tmb; pua_api_t pua_api; b2bl_api_t b2bl_api; b2b_sca_table_t b2b_sca_htable; unsigned int b2b_sca_hsize = 10; static str db_url = {NULL, 0}; int watchers_avp_name = -1; unsigned short watchers_avp_type = 0; static str watchers_avp_spec = {NULL, 0}; static pv_spec_t watchers_spec; static str shared_line_spec_param = {NULL, 0}; static pv_spec_t shared_line_spec; static pv_value_t shared_line_tok; static str appearance_name_addr_spec_param = {NULL, 0}; static pv_spec_t appearance_name_addr_spec; static pv_value_t appearance_name_addr_tok; static struct to_body appearance_name_addr; #define APPEARANCE_NAME_ADDR_BUF_LEN 255 static char appearance_name_addr_buf[APPEARANCE_NAME_ADDR_BUF_LEN + 1]; str presence_server = {NULL, 0}; /** Exported functions */ static cmd_export_t cmds[]= { {"sca_init_request" ,(cmd_function)sca_init_request ,1,fixup_pvar_pvar,0,REQUEST_ROUTE}, {"sca_bridge_request",(cmd_function)sca_bridge_request,1,fixup_pvar_pvar,0,REQUEST_ROUTE}, { 0, 0, 0 , 0 , 0, 0} }; /** Exported parameters */ static param_export_t params[]= { {"hash_size", INT_PARAM,&b2b_sca_hsize }, {"presence_server", STR_PARAM,&presence_server.s }, {"watchers_avp_spec", STR_PARAM,&watchers_avp_spec.s }, {"shared_line_spec_param", STR_PARAM,&shared_line_spec_param.s }, {"appearance_name_addr_spec_param", STR_PARAM,&appearance_name_addr_spec_param.s }, {"db_url", STR_PARAM,&db_url.s }, {"db_mode", INT_PARAM,&sca_db_mode }, {"table_name", STR_PARAM,&sca_table_name }, {"shared_line_column", STR_PARAM,&shared_line_column.s }, {"watchers_column", STR_PARAM,&watchers_column.s }, {"app1_shared_entity_column", STR_PARAM,&app_shared_entity_column[0].s }, {"app1_call_state_column", STR_PARAM,&app_call_state_column[0].s }, {"app1_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[0].s }, {"app1_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[0].s}, {"app1_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[0].s }, {"app2_shared_entity_column", STR_PARAM,&app_shared_entity_column[1].s }, {"app2_call_state_column", STR_PARAM,&app_call_state_column[1].s }, {"app2_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[1].s }, {"app2_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[1].s}, {"app2_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[1].s }, {"app3_shared_entity_column", STR_PARAM,&app_shared_entity_column[2].s }, {"app3_call_state_column", STR_PARAM,&app_call_state_column[2].s }, {"app3_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[2].s }, {"app3_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[2].s}, {"app3_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[2].s }, {"app4_shared_entity_column", STR_PARAM,&app_shared_entity_column[3].s }, {"app4_call_state_column", STR_PARAM,&app_call_state_column[3].s }, {"app4_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[3].s }, {"app4_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[3].s}, {"app4_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[3].s }, {"app5_shared_entity_column", STR_PARAM,&app_shared_entity_column[4].s }, {"app5_call_state_column", STR_PARAM,&app_call_state_column[4].s }, {"app5_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[4].s }, {"app5_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[4].s}, {"app5_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[4].s }, {"app6_shared_entity_column", STR_PARAM,&app_shared_entity_column[5].s }, {"app6_call_state_column", STR_PARAM,&app_call_state_column[5].s }, {"app6_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[5].s }, {"app6_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[5].s}, {"app6_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[5].s }, {"app7_shared_entity_column", STR_PARAM,&app_shared_entity_column[6].s }, {"app7_call_state_column", STR_PARAM,&app_call_state_column[6].s }, {"app7_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[6].s }, {"app7_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[6].s}, {"app7_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[6].s }, {"app8_shared_entity_column", STR_PARAM,&app_shared_entity_column[7].s }, {"app8_call_state_column", STR_PARAM,&app_call_state_column[7].s }, {"app8_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[7].s }, {"app8_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[7].s}, {"app8_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[7].s }, {"app9_shared_entity_column", STR_PARAM,&app_shared_entity_column[8].s }, {"app9_call_state_column", STR_PARAM,&app_call_state_column[8].s }, {"app9_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[8].s }, {"app9_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[8].s}, {"app9_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[8].s }, {"app10_shared_entity_column", STR_PARAM,&app_shared_entity_column[9].s }, {"app10_call_state_column", STR_PARAM,&app_call_state_column[9].s }, {"app10_call_info_uri_column", STR_PARAM,&app_call_info_uri_column[9].s }, {"app10_call_info_appearance_uri_column",STR_PARAM,&app_call_info_appearance_uri_column[9].s}, {"app10_b2bl_key_column", STR_PARAM,&app_b2bl_key_column[9].s }, {0, 0, 0 } }; /** MI commands */ static mi_export_t mi_cmds[] = { { "sca_list", 0, mi_sca_list, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "pua", DEP_ABORT }, { MOD_TYPE_DEFAULT, "b2b_logic", DEP_ABORT }, { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** Module interface */ struct module_exports exports= { "b2b_sca", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) mod_destroy, /* destroy function */ child_init /* per-child init function */ }; /** Module init function */ static int mod_init(void) { unsigned int i; LM_DBG("start\n"); init_db_url( db_url , 0 /*cannot be null*/); /* load tm api */ if(load_tm_api(&tmb)==-1) { LM_ERR("can't load tm functions\n"); return -1; } /* load pua api */ if(load_pua_api(&pua_api) < 0) { LM_ERR("Can't bind pua\n"); return -1; } /* add event in pua module */ if(pua_api.add_event(CALLINFO_EVENT, "call-info", NULL, 0) < 0) { LM_ERR("failed to add 'call-info' event to pua module\n"); return -1; } /* load b2b_logic api */ if(load_b2b_logic_api(&b2bl_api)< 0) { LM_ERR("Failed to load b2b_logic api\n"); return -1; } if(b2b_sca_hsize<1 || b2b_sca_hsize>20) { LM_ERR("Wrong hash size. Needs to be greater than 1" " and smaller than 20. Be aware that you should set the log 2" " value of the real size\n"); return -1; } b2b_sca_hsize = 1<PROC_MAIN || rank==PROC_MODULE)) { if (connect_sca_db(&db_url)) { LM_ERR("failed to connect to database (rank=%d)\n",rank); return -1; } } return 0; } static void mod_destroy(void) { //if (sca_db_mode != DB_MODE_NONE) { // sca_update_db(); //} destroy_b2b_sca_handlers(); destroy_b2b_sca_htable(); LM_DBG("done\n"); return; } int get_hash_index_and_shared_line(struct sip_msg* msg, unsigned int *hash_index, str **shared_line) { if(shared_line_spec_param.s) { memset(&shared_line_tok, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, &shared_line_spec, &shared_line_tok) < 0) { LM_ERR("Failed to get shared_line value\n"); return -1; } //LM_DBG("got shared_line_spec_param flags [%d]\n", shared_line_tok.flags); if(!(shared_line_tok.flags&PV_VAL_INT) && (shared_line_tok.flags&PV_VAL_STR)) { *shared_line = &shared_line_tok.rs; *hash_index = core_hash(&shared_line_tok.rs, NULL, b2b_sca_hsize); //LM_DBG("got hash_index=[%d] for PV_SPEC user [%.*s]\n", *hash_index, // shared_line_tok.rs.len, shared_line_tok.rs.s); return 0; } else { LM_ERR("No shared line PV [%.*s] defined\n", shared_line_spec_param.len, shared_line_spec_param.s); return -1; } } else { LM_ERR("No shared line PV defined\n"); return -1; } /* If the shared_line_spec_param is not set, use the username from original RURI */ /* parse_orig_ruri(msg); if (msg->parsed_orig_ruri_ok && msg->parsed_orig_ruri.user.s && msg->parsed_orig_ruri.user.len) { *shared_line = &msg->parsed_orig_ruri.user; *hash_index = core_hash(&msg->parsed_orig_ruri.user, NULL, b2b_sca_hsize); //LM_DBG("got hash_index=[%d] for RURI user [%.*s]\n", *hash_index, // msg->parsed_orig_ruri.user.len, msg->parsed_orig_ruri.user.s); return 0; } else { LM_ERR("msg->parsed_orig_ruri_ok is NULL\n"); } */ return -1; } struct to_body* get_appearance_name_addr(struct sip_msg* msg) { int len = 0; if(appearance_name_addr_spec_param.s) { memset(&appearance_name_addr_tok, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, &appearance_name_addr_spec, &appearance_name_addr_tok) < 0) { LM_ERR("Failed to get appearance_name_addr value\n"); return NULL; } //LM_DBG("got appearance_name_addr_spec_param flags [%d]\n", appearance_name_addr_tok.flags); if(!(appearance_name_addr_tok.flags&PV_VAL_INT) && (appearance_name_addr_tok.flags&PV_VAL_STR)) { //LM_DBG("got PV_SPEC appearance_name_addr [%.*s]\n", // appearance_name_addr_tok.rs.len, appearance_name_addr_tok.rs.s); if(appearance_name_addr_tok.rs.len+CRLF_LEN > APPEARANCE_NAME_ADDR_BUF_LEN) { LM_ERR("Buffer overflow\n"); return NULL; } trim(&appearance_name_addr_tok.rs); memcpy(appearance_name_addr_buf, appearance_name_addr_tok.rs.s, appearance_name_addr_tok.rs.len); len = appearance_name_addr_tok.rs.len; if(strncmp(appearance_name_addr_tok.rs.s + len - CRLF_LEN, CRLF, CRLF_LEN)) { memcpy(appearance_name_addr_buf + len, CRLF, CRLF_LEN); len+= CRLF_LEN; } parse_to(appearance_name_addr_buf, appearance_name_addr_buf+len, &appearance_name_addr); if (appearance_name_addr.error != PARSE_OK) { LM_ERR("Failed to parse PV_SPEC appearance_name_addr [%.*s]\n", len, appearance_name_addr_buf); return NULL; } if (parse_uri(appearance_name_addr.uri.s, appearance_name_addr.uri.len, &appearance_name_addr.parsed_uri)<0) { LM_ERR("failed to parse PV_SPEC appearance_name_addr uri [%.*s]\n", appearance_name_addr.uri.len, appearance_name_addr.uri.s); return NULL; } return &appearance_name_addr; } } /* If the appearance_name_addr_spec_param is not set, use the From uri */ /* if (msg->from->parsed == NULL) { if (parse_from_header(msg)<0) { LM_ERR("cannot parse From header\n"); return NULL; } } */ return msg->from->parsed; } static struct mi_root* mi_sca_list(struct mi_root* cmd, void* param) { int i, index; struct mi_root *rpl_tree; struct mi_node *node=NULL, *node1=NULL, *rpl=NULL; struct mi_attr* attr; b2b_sca_record_t *rec; b2b_sca_call_t *call; str_lst_t *watcher; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; for(index = 0; indexshared_line.s, rec->shared_line.len); if(node == NULL) goto error; watcher = rec->watchers; while (watcher) { attr = add_mi_attr(node, MI_DUP_VALUE, "watcher", 7, watcher->watcher.s, watcher->watcher.len); if(attr == NULL) goto error; watcher = watcher->next; } for (i=0; icall[i]) { call = rec->call[i]; node1 = add_mi_node_child(node, MI_DUP_VALUE, "appearance", 10, call->appearance_index_str.s, call->appearance_index_str.len); if(node1 == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "state", 5, app_state[call->call_state].s, app_state[call->call_state].len); if(attr == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "b2b_key", 7, call->b2bl_key.s, call->b2bl_key.len); if(attr == NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "app_uri", 7, call->call_info_apperance_uri.s, call->call_info_apperance_uri.len); if(attr == NULL) goto error; } } rec = rec->next; } lock_release(&b2b_sca_htable[index].lock); } return rpl_tree; error: lock_release(&b2b_sca_htable[index].lock); LM_ERR("Unable to create reply\n"); free_mi_tree(rpl_tree); return NULL; } opensips-2.2.2/modules/b2b_sca/doc/000077500000000000000000000000001300170765700170735ustar00rootroot00000000000000opensips-2.2.2/modules/b2b_sca/doc/b2b_sca.xml000066400000000000000000000021521300170765700211100ustar00rootroot00000000000000 %docentities; ]> b2b_sca Module &osipsname; Ovidiu Sas osas@voipembedded.com Ovidiu Sas 2011-2013 VoIP Embedded, Inc. $Rev: 8688 $ $Date: 2012-01-25 13:52:05 -0500 (Wed, 25 Jan 2012) $ &admin; &faq; opensips-2.2.2/modules/b2b_sca/doc/b2b_sca_admin.xml000066400000000000000000000404671300170765700222730ustar00rootroot00000000000000 &adminguide;
Overview This module provides core SCA (Shared Call Appearance) functionality for &osips;. It is designed to work in tandem with the presence_callinfo module. The module handles the basic SIP signalling for call controll while publishing callinfo events to a presence server. It is built on top of the b2b_logic module and it is using the 'top hiding' scenario to control SIP signalling. A typical usage example is provided below, where Alice makes a call to Bob. The call leg between Alice and the b2b_sca server is an "appearance" call of the "shared" call between the b2b_sca server and Bob. | | | | | |--INV bob->| | | | |--PUBLISH(alerting)--->| | | |<-----200 OK-----------| | | | | | | | |<-180 ring-| | |<-180 ring------------------| | | | | | | | | | | | | | | |<-200 OK---| | |<-200 OK--------------------|--ACK----->| | |--ACK---------------------->|--PUBLISH(active)----->| | | |<-----200 OK-----------| | | | | | |--INV bob (hold)----------->| | | | | |--INV bob->| | | | |--PUBLISH(held)------->| | | |<-----200 OK-----------| | | |<-200 OK---| | |<--200 OK-------------------| | | | | | | | | |--INV------->| | | | | |--INV bob->| | |<-BYE-----------------------|--PUBLISH(active)----->| |--200 OK------------------->|<-----200 OK-----------| | | |<-200 OK---| | | |<-200 OK-----| | ]]> Alice calls Bob from her desk IP phone (alice1). Bob answers the call. Alice decide to carry the conversation from a meeting room and she put's BOB on hold. Alice arrives to the meeting room and retrieves the call on the conference room IP phone (alice2).
To-do Features to be added in the future: possibility to handle unlimited number of appearances.
Dependencies
&osips; Modules The following modules must be loaded before this module: tm module. pua module. b2b_logic module.
Exported Parameters
<varname>hash_size</varname>(integer) The size of the hash table internally used to keep the shared calls. A larger table means faster acces at the expense of memory. The hash size is a power of number two. The default value is "10". Set <varname>hash_size</varname> parameter ... modparam("b2b_sca", "hash_size", "5") ...
<varname>presence_server</varname>(string) The address of the presence server, where the PUBLISH messages should be sent (not compulsory). If not set, the PUBLISH requests will be routed based on watcher's URI. The default value is "NULL". Set <varname>presence_server</varname> parameter ... modparam("b2b_sca", "presence_server", "sip:opensips.org") ...
<varname>watchers_avp_spec</varname>(string) AVP that will hold one or more watcher URI(s). If not set, no PUBLISH requests will be sent out. The watchers_avp_spec MUST be set before calling sca_init_request(); The default value is "NULL". Set <varname>watchers_avp_spec</varname> parameter ... modparam("b2b_sca", "watchers_avp_spec", "$avp(watchers_avp_spec)") ... route { ... $avp(watchers_avp_spec) = "sip:first_watcher@opensip.org"; $avp(watchers_avp_spec) = "sip:second_watcher@opensip.org"; ... }
<varname>shared_line_spec_param</varname>(string) Mandatory parameter. Opaque string identifing the shared line/call. The shared_line_spec_param MUST be set before calling sca_init_request(); The default value is "NULL". Set <varname>shared_line_spec_param</varname> parameter ... modparam("b2b_sca", "shared_line_spec_param", "$var(shared_line)") ...
<varname>appearance_name_addr_spec_param</varname>(string) Mandatory parameter. It must be a valid SIP URI. It will populate the appearance-uri SIP parameter inside the Call-Info SIP header. The appearance_name_addr_spec_param MUST be set before calling sca_init_request(); The default value is "NULL". Set <varname>appearance_name_addr_spec_param</varname> parameter ... modparam("b2b_sca", "appearance_name_addr_spec_param", "") ...
<varname>db_url</varname>(string) This is URL of the database to be used. The default value is "NULL". Set <varname>db_url</varname> parameter ... modparam("b2b_sca", "db_url", "[dbdriver]://[[username]:[password]]@[dbhost]/[dbname]") ...
<varname>db_mode</varname>(integer) The b2b_sca module can utilize database for persistent call appearance storage. Using a database ensure that active call appearances will survive machine restarts or SW crashes. The following databse accessing modes are available for b2b_sca module: NO DB STORAGE - set this parameter to 0 WRITE THROUGH (synchronous write in database) - set this parameter to 1 The default value is 0 (NO DB STORAGE). Set <varname>db_mode</varname> parameter ... modparam("b2b_sca", "db_mode", 1) ...
<varname>table_name</varname>(string) Identifies the table name from the defined database. The default value is "b2b_sca". Set <varname>table_name</varname> parameter ... modparam("b2b_sca", "table_name", "sla") ...
<varname>shared_line_column</varname>(string) The column's name in the database storing the shared call/line id. See "shared_line_spec_param" parameter. The default value is "shared_line". Set <varname>shared_line_column</varname> parameter ... modparam("b2b_sca", "shared_line_column", "") ...
<varname>watchers_column</varname>(string) The column's name in the database storing the list of watchers. See "watchers_avp_spec" parameter. The default value is "watchers". Set <varname>watchers_column</varname> parameter ... modparam("b2b_sca", "watchers_column", "") ...
<varname>app[index]_shared_entity_column</varname>(string) The column's name in the database storing the shared entity of a particular appearance. See "sca_init_request" for more info. The default value is "app[index]_shared_entity". Index is an integer between 1 and 10. Set <varname>app[index]_shared_entity_column</varname> parameter ... modparam("b2b_sca", "app1_shared_entity_column", "first_shared_entity") modparam("b2b_sca", "app2_shared_entity_column", "second_shared_entity") ...
<varname>app[index]_call_state_column</varname>(string) The column's name in the database storing the call state of a particular appearance. The following states are stored: 1 - alerting, 2 - active, 3 - held, 4 - held-private. The default value is "app[index]_call_state". Index is an integer between 1 and 10. Set <varname>app[index]_call_state_column</varname> parameter ... modparam("b2b_sca", "app1_call_state_column", "first_call_state") modparam("b2b_sca", "app2_call_state_column", "second_call_state") ...
<varname>app[index]_call_info_uri_column</varname>(string) The column's name in the database storing the call info URI of a particular appearance. The default value is "app[index]_call_info_uri". Index is an integer between 1 and 10. Set <varname>app[index]_call_info_uri_column</varname> parameter ... modparam("b2b_sca", "app1_call_info_uri_column", "first_call_info_uri") modparam("b2b_sca", "app2_call_info_uri_column", "second_call_info_uri") ...
<varname>app[index]_call_info_appearance_uri_column</varname>(string) The column's name in the database storing the call info appearance URI of a particular appearance. For each appearance, the value is extracted from the "appearance_name_addr_spec_param" parameter. The default value is "app[index]_call_info_appearance_uri". Index is an integer between 1 and 10. Set <varname>app[index]_call_info_appearance_uri_column</varname> parameter ... modparam("b2b_sca", "app1_call_info_appearance_uri_column", "first_call_info_appearance_uri") modparam("b2b_sca", "app2_call_info_appearance_uri_column", "second_call_info_appearance_uri") ...
<varname>app[index]_b2bl_key_column</varname>(string) The column's name in the database storing the b2b_logic key of a particular appearance. The default value is "app[index]_b2bl_key". Index is an integer between 1 and 10. Set <varname>app[index]_b2bl_key_column</varname> parameter ... modparam("b2b_sca", "app1_b2bl_key_column", "first_b2bl_key") modparam("b2b_sca", "app2_b2bl_key_column", "second_b2bl_key") ...
Exported Functions
<function moreinfo="none">sca_init_request(shared_line)</function> This is the function that must be called by the script writer on an initial INVITE for which an SCA call must be instantiated (see the call from alice1 in the above diagram). Meaning of the parameters: shared_line - a PV (pseudo-variable) identifying the call leg as being an "appearnace" call or a "shared" call: 0: "shared" call 1: "appearance" call <function>sca_init_request()</function> usage ... modparam("b2b_sca", "shared_line_spec_param","$var(shared_line)") modparam("b2b_sca", "appearance_name_addr_spec_param","$var(appearance_name_addr)") modparam("b2b_sca", "watchers_avp_spec","$avp(watchers_avp_spec)") ... # Setting the shared call identifier $var(shared_line) = "alice"; # Setting the watchers $avp(watchers_avp_spec) = "sip:alice1@example.com"; $avp(watchers_avp_spec) = "sip:alice2@example.com"; if (INCOMING_SHARED_CALL) { # The incoming call is a 'shared' call $var(shared_line_entity) = 0; # Setting the appearance name address $var(appearance_name_addr) = $fu; } else { # The incoming call is an 'appearance' call # - see Alice's initial call leg in the given example $var(shared_line_entity) = 1; # Setting the appearance name address $var(appearance_name_addr) = $tu; } # Initiate the call if (!sca_init_request("$var(shared_line_entity)")) { send_reply("403", "Internal Server Error (SLA)"); exit; } ...
<function moreinfo="none">sca_bridge_request(shared_line_to bridge)</function> This is the function that must be called by the script writer on an initial "appearance" INVITE for an existing shared call. It will bridge the current "appearance" call with the existing "shared" call and the old "appearance" call will be disconnected (see the call from alice2 in the above diagram). Meaning of the parameters: shared_line_to_bridge - a PV identifying the shared line/call that was previously set by sca_init_request(). ... if ($rU==NULL && is_method("INVITE") && $fU==$tU && is_present_hf("Call-Info")) { # The incoming call is an 'appearance' call # - see Alice's call from alice2 in the given example $var(shared_line_to_bridge) = "alice"; if (!sca_bridge_request("$var(shared_line_to_bridge)")) send_reply("403", "Internal SLA Error"); exit; } } ...
Exported MI Functions
<function moreinfo="none">sca_list</function> It lists the appearances belonging to a shared line/call. Name: sca_list Parameters: none MI FIFO Command Format: :sca_list:_reply_fifo_file_ _empty_line_
opensips-2.2.2/modules/b2b_sca/sca_db_handler.c000066400000000000000000000445301300170765700214100ustar00rootroot00000000000000/* * sca_db_handler module * * Copyright (C) 2011 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-11-21 initial version (Ovidiu Sas) */ #include #include #include "../../dprint.h" #include "../../db/db.h" #include "../../str.h" #include "sca_db_handler.h" #include "sca_logic.h" str shared_line_column = str_init(SHARED_LINE_COL); str watchers_column = str_init(WATCHERS_COL); str app_shared_entity_column[MAX_APPEARANCE_INDEX] = { str_init(SHARED_ENTITY_1_COL), str_init(SHARED_ENTITY_2_COL), str_init(SHARED_ENTITY_3_COL), str_init(SHARED_ENTITY_4_COL), str_init(SHARED_ENTITY_5_COL), str_init(SHARED_ENTITY_6_COL), str_init(SHARED_ENTITY_7_COL), str_init(SHARED_ENTITY_8_COL), str_init(SHARED_ENTITY_9_COL), str_init(SHARED_ENTITY_10_COL), }; str app_call_state_column[MAX_APPEARANCE_INDEX] = { str_init(CALL_STATE_1_COL), str_init(CALL_STATE_2_COL), str_init(CALL_STATE_3_COL), str_init(CALL_STATE_4_COL), str_init(CALL_STATE_5_COL), str_init(CALL_STATE_6_COL), str_init(CALL_STATE_7_COL), str_init(CALL_STATE_8_COL), str_init(CALL_STATE_9_COL), str_init(CALL_STATE_10_COL), }; str app_call_info_uri_column[MAX_APPEARANCE_INDEX] = { str_init(CALL_INFO_URI_1_COL), str_init(CALL_INFO_URI_2_COL), str_init(CALL_INFO_URI_3_COL), str_init(CALL_INFO_URI_4_COL), str_init(CALL_INFO_URI_5_COL), str_init(CALL_INFO_URI_6_COL), str_init(CALL_INFO_URI_7_COL), str_init(CALL_INFO_URI_8_COL), str_init(CALL_INFO_URI_9_COL), str_init(CALL_INFO_URI_10_COL), }; str app_call_info_appearance_uri_column[MAX_APPEARANCE_INDEX] = { str_init(CALL_INFO_APPEARANCE_URI_1_COL), str_init(CALL_INFO_APPEARANCE_URI_2_COL), str_init(CALL_INFO_APPEARANCE_URI_3_COL), str_init(CALL_INFO_APPEARANCE_URI_4_COL), str_init(CALL_INFO_APPEARANCE_URI_5_COL), str_init(CALL_INFO_APPEARANCE_URI_6_COL), str_init(CALL_INFO_APPEARANCE_URI_7_COL), str_init(CALL_INFO_APPEARANCE_URI_8_COL), str_init(CALL_INFO_APPEARANCE_URI_9_COL), str_init(CALL_INFO_APPEARANCE_URI_10_COL), }; str app_b2bl_key_column[MAX_APPEARANCE_INDEX] = { str_init(B2BL_KEY_1_COL), str_init(B2BL_KEY_2_COL), str_init(B2BL_KEY_3_COL), str_init(B2BL_KEY_4_COL), str_init(B2BL_KEY_5_COL), str_init(B2BL_KEY_6_COL), str_init(B2BL_KEY_7_COL), str_init(B2BL_KEY_8_COL), str_init(B2BL_KEY_9_COL), str_init(B2BL_KEY_10_COL), }; str sca_table_name = str_init(SCA_TABLE_NAME); int sca_db_mode = DB_MODE_NONE; static db_con_t *sca_db_handle = NULL; static db_func_t sca_dbf; extern b2bl_api_t b2bl_api; int connect_sca_db(const str *db_url) { if (sca_db_handle) { LM_CRIT("BUG - db connection found already open\n"); return -1; } if ((sca_db_handle = sca_dbf.init(db_url)) == NULL) return -1; return 0; } static int use_sca_table(void) { if(!sca_db_handle){ LM_ERR("invalid database handle\n"); return -1; } sca_dbf.use_table(sca_db_handle, &sca_table_name); return 0; } int delete_sca_info_from_db(b2b_sca_record_t *record) { db_key_t q_cols[1] = {&shared_line_column}; db_val_t q_vals[1]; if(use_sca_table()) return -1; memset(q_vals, 0, sizeof(db_val_t)); q_vals[0].type = DB_STR; q_vals[0].val.str_val = record->shared_line; /* Delete based on "q_cols" keys with matching "q_vals" values. */ if(sca_dbf.delete(sca_db_handle, q_cols, 0, q_vals, 1) < 0) { LM_ERR("failed to delete record\n"); return -1; } return 0; } int update_sca_info_to_db(b2b_sca_record_t *record, unsigned int appearance_index) { b2b_sca_call_t *call; unsigned int i; unsigned int n_q_cols = 0, n_q_vals = 0; unsigned int shared_line_col, watchers_col; unsigned int app_shared_entity_col[MAX_APPEARANCE_INDEX]; unsigned int app_call_state_col[MAX_APPEARANCE_INDEX]; unsigned int app_call_info_uri_col[MAX_APPEARANCE_INDEX]; unsigned int app_call_info_appearance_uri_col[MAX_APPEARANCE_INDEX]; unsigned int app_b2bl_key_col[MAX_APPEARANCE_INDEX]; db_key_t q_cols[SCA_TABLE_TOTAL_COL_NO]; db_val_t q_vals[SCA_TABLE_TOTAL_COL_NO]; LM_DBG("\n"); if(use_sca_table()) return -1; memset(q_vals, 0, SCA_TABLE_TOTAL_COL_NO * sizeof(db_val_t)); q_cols[shared_line_col = n_q_cols++] = &shared_line_column; q_vals[shared_line_col].type = DB_STR; q_cols[watchers_col = n_q_cols++] = &watchers_column; q_vals[watchers_col].type = DB_STR; for (i=0; ishared_line; i = appearance_index - 1; if (i >= MAX_APPEARANCE_INDEX) { LM_ERR("Non matching call\n"); return -1; } call = record->call[i]; if (call) { LM_DBG("update shared_entity [%d] and call_state [%d] for call[%d][%.*s]\n", call->shared_entity, call->call_state, i, call->b2bl_key.len, call->b2bl_key.s); switch(call->call_state) { case ALERTING_STATE: q_vals[app_call_info_uri_col[i]].val.str_val = call->call_info_uri; q_vals[app_call_info_appearance_uri_col[i]].val.str_val = call->call_info_apperance_uri; q_vals[app_b2bl_key_col[i]].val.str_val = call->b2bl_key; LM_DBG("update [%.*s][%.*s][%.*s]\n", call->call_info_uri.len, call->call_info_uri.s, call->call_info_apperance_uri.len, call->call_info_apperance_uri.s, call->b2bl_key.len, call->b2bl_key.s); n_q_vals += 3; default: q_vals[app_shared_entity_col[i]].val.int_val = call->shared_entity; q_vals[app_call_state_col[i]].val.int_val = call->call_state; n_q_vals += 2; } } else { n_q_vals = 5; } if(sca_dbf.update(sca_db_handle, q_cols, 0, q_vals, q_cols + app_shared_entity_col[i], q_vals + app_shared_entity_col[i], 1, n_q_vals) != 0) { LM_ERR("failed to update record\n"); return -1; } return 0; } int insert_sca_info_into_db(b2b_sca_record_t *record) { b2b_sca_call_t *call = NULL; unsigned int n_q_cols = 0; unsigned int i; unsigned int appearance_index = MAX_APPEARANCE_INDEX; unsigned int shared_line_col, watchers_col; unsigned int app_shared_entity_col[MAX_APPEARANCE_INDEX]; unsigned int app_call_state_col[MAX_APPEARANCE_INDEX]; unsigned int app_call_info_uri_col[MAX_APPEARANCE_INDEX]; unsigned int app_call_info_appearance_uri_col[MAX_APPEARANCE_INDEX]; unsigned int app_b2bl_key_col[MAX_APPEARANCE_INDEX]; db_key_t q_cols[SCA_TABLE_TOTAL_COL_NO]; db_val_t q_vals[SCA_TABLE_TOTAL_COL_NO]; LM_DBG("\n"); if(use_sca_table()) return -1; memset(q_vals, 0, SCA_TABLE_TOTAL_COL_NO * sizeof(db_val_t)); q_cols[shared_line_col = n_q_cols++] = &shared_line_column; q_vals[shared_line_col].type = DB_STR; q_cols[watchers_col = n_q_cols++] = &watchers_column; q_vals[watchers_col].type = DB_STR; for (i=0; ishared_line; /* FIXME: get all the watchers */ if (record->watchers) { q_vals[watchers_col].val.str_val = record->watchers->watcher; } for (i=0; icall[i]) { if (call) { LM_ERR("This should be an UPDATE not an INSERT\n"); return -1; } call = record->call[i]; appearance_index = i; } } if (call) { q_vals[app_shared_entity_col[appearance_index]].val.int_val = call->shared_entity; q_vals[app_call_state_col[appearance_index]].val.int_val = call->call_state; q_vals[app_call_info_uri_col[appearance_index]].val.str_val = call->call_info_uri; q_vals[app_call_info_appearance_uri_col[appearance_index]].val.str_val = call->call_info_apperance_uri; q_vals[app_b2bl_key_col[appearance_index]].val.str_val = call->b2bl_key; if((sca_dbf.insert(sca_db_handle, q_cols, q_vals, SCA_TABLE_TOTAL_COL_NO)) != 0) { LM_ERR("could not add record\n"); return -1; } } else { LM_ERR("Empty record?\n"); return -1; } return 0; } int push_sca_info_to_db(b2b_sca_record_t *record, unsigned int appearance_index, unsigned int forced_update) { unsigned int i; unsigned int no_calls = 0; b2b_sca_call_t *_call = NULL; LM_DBG("\n"); for (i=0; icall[i]) { no_calls++; _call = record->call[i]; } } switch (no_calls) { case 0: return delete_sca_info_from_db(record); break; case 1: if (_call->call_state==ALERTING_STATE) { if (forced_update) return update_sca_info_to_db(record, appearance_index); else return insert_sca_info_into_db(record); } else { return update_sca_info_to_db(record, appearance_index); } break; default: return update_sca_info_to_db(record, appearance_index); } LM_ERR("logic error\n"); return -1; } static int load_sca_info_from_db(void) { db_res_t * res = NULL; db_val_t * values; db_row_t * rows; int i, j, nr_rows; unsigned int valid_record; unsigned int n_result_cols = 0; unsigned int shared_line_col, watchers_col; unsigned int app_shared_entity_col[MAX_APPEARANCE_INDEX]; unsigned int app_call_state_col[MAX_APPEARANCE_INDEX]; unsigned int app_call_info_uri_col[MAX_APPEARANCE_INDEX]; unsigned int app_call_info_appearance_uri_col[MAX_APPEARANCE_INDEX]; unsigned int app_b2bl_key_col[MAX_APPEARANCE_INDEX]; db_key_t q_cols[SCA_TABLE_TOTAL_COL_NO]; str shared_line, watchers_csv; //str_lst_t *watchers; //unsigned int size, watcher_size, watchers_no; //unsigned int size; unsigned int hash_index; //char *p; b2b_sca_record_t *record; b2b_sca_call_t *call; unsigned int shared_entity, appearance_index, call_state; str call_info_uri, call_info_apperance_uri, b2bl_key; b2bl_cb_ctx_t *cb_params; if(use_sca_table()) return -1; q_cols[shared_line_col = n_result_cols++] = &shared_line_column; q_cols[watchers_col = n_result_cols++] = &watchers_column; for (i=0; i skipping\n", shared_line_column.len, shared_line_column.s, watchers_column.len, watchers_column.s); continue; } shared_line.s = (char*)values[shared_line_col].val.string_val; shared_line.len = strlen(shared_line.s); watchers_csv.s = (char*)values[watchers_col].val.string_val; watchers_csv.len = strlen(watchers_csv.s); record = restore_record(&shared_line, &watchers_csv); if (record == NULL) goto error; hash_index = core_hash(&shared_line, NULL, b2b_sca_hsize); j = 0; while (j < MAX_APPEARANCE_INDEX) { if( VAL_NULL(values + app_shared_entity_col[j]) || VAL_NULL(values + app_call_state_col[j]) || VAL_NULL(values + app_call_info_uri_col[j]) || VAL_NULL(values + app_call_info_appearance_uri_col[j]) || VAL_NULL(values + app_b2bl_key_col[j]) ) { goto cont; } appearance_index = j + 1; /* 1 - get shared_entity */ shared_entity = values[app_shared_entity_col[j]].val.int_val; if (shared_entity!=0 && shared_entity!=1) { LM_ERR("Unexpected shared_entity [%d] " "for shared_line [%.*s]\n", shared_entity, shared_line.len, shared_line.s); goto cont; } /* 2 - get call_state */ call_state = values[app_call_state_col[j]].val.int_val; if (call_state == IDLE_STATE) { LM_DBG("empty call[%d]\n", appearance_index); goto cont; } if (call_state > MAX_INDEX_STATE) { LM_ERR("Unexpected call_state [%d] for shared_line [%.*s]\n", call_state, shared_line.len, shared_line.s); goto cont; } /* 3 - get call_info_uri */ call_info_uri.s = (char*)values[app_call_info_uri_col[j]].val.string_val; if (call_info_uri.s) call_info_uri.len = strlen(call_info_uri.s); else { LM_ERR("Missing call_info_uri for shared_line [%.*s][%d]\n", shared_line.len, shared_line.s, appearance_index); goto cont; } LM_DBG("call_info_uri=[%.*s]\n", call_info_uri.len, call_info_uri.s); /* 4 - get call_info_apperance_uri */ call_info_apperance_uri.s = (char*) values[app_call_info_appearance_uri_col[j]].val.string_val; if (call_info_apperance_uri.s) call_info_apperance_uri.len = strlen(call_info_apperance_uri.s); else { LM_ERR("Missing call_info_apperance_uri for " "shared_line [%.*s][%d]\n", shared_line.len, shared_line.s, appearance_index); goto cont; } LM_DBG("call_info_apperance_uri=[%.*s]\n", call_info_apperance_uri.len, call_info_apperance_uri.s); /* 5 - get b2bl_key */ b2bl_key.s = (char*)values[app_b2bl_key_col[j]].val.string_val; if (b2bl_key.s) { b2bl_key.len = strlen(b2bl_key.s); if (b2bl_key.len > B2BL_MAX_KEY_LEN) { LM_ERR("buffer overflow on b2bl_key [%.*s]" " for shared_line [%.*s][%d]\n", b2bl_key.len, b2bl_key.s, shared_line.len, shared_line.s, appearance_index); goto cont; } LM_DBG("b2bl_key=[%.*s]\n", b2bl_key.len, b2bl_key.s); } else { LM_ERR("Missing b2bl_key for shared_line [%.*s][1]\n", shared_line.len, shared_line.s); goto cont; } /* restore the call */ call = restore_call(record, appearance_index, shared_entity, call_state, &call_info_uri, &call_info_apperance_uri); if (call == NULL) { goto error; } /* update record */ if (0!=b2b_sca_update_call_record_key(call, &b2bl_key)) { LM_ERR("Unable to update b2bl_key [%.*s]\n", b2bl_key.len, b2bl_key.s); shm_free(call); call = NULL; record->call[appearance_index-1] = NULL; goto cont; } /* Prepare b2b_logic callback params. */ cb_params = build_cb_params(hash_index, &shared_line, appearance_index); if (cb_params == NULL) { LM_ERR("Unable to build cb_params\n"); goto error; } /* re-register callbacks */ if(b2bl_api.register_cb(&b2bl_key, &sca_logic_notify, cb_params, B2B_RE_INVITE_CB|B2B_CONFIRMED_CB|B2B_DESTROY_CB) != 0){ LM_ERR("Unable register b2b cb\n"); shm_free(call); call = NULL; record->call[appearance_index-1] = NULL; goto cont; } cont: j++; } valid_record = j = 0; while (j < MAX_APPEARANCE_INDEX) { if (record->call[j]) { valid_record = 1; goto check_valid_record; } j++; } check_valid_record: if (valid_record) { b2b_sca_print_record(record); insert_record(hash_index, record); } else { LM_DBG("removing the record from db!\n"); delete_sca_info_from_db(record); } LM_DBG("Done\n"); } /* any more data to be fetched ?*/ if (DB_CAPABILITY(sca_dbf, DB_CAP_FETCH)) { if (sca_dbf.fetch_result(sca_db_handle, &res, SCA_FETCH_SIZE)<0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(res); } else { nr_rows = 0; } }while (nr_rows>0); sca_dbf.free_result(sca_db_handle, res); return 0; error: sca_dbf.free_result(sca_db_handle, res); return -1; } int init_sca_db(const str *db_url, int dlg_hash_size) { /* Find a database module */ if (db_bind_mod(db_url, &sca_dbf) < 0) { LM_ERR("Unable to bind to a database driver\n"); return -1; } if (connect_sca_db(db_url)!=0){ LM_ERR("unable to connect to the database\n"); return -1; } if(db_check_table_version(&sca_dbf, sca_db_handle, &sca_table_name, SCA_TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); return -1; } if(load_sca_info_from_db() !=0){ LM_ERR("unable to load the sca data\n"); return -1; } sca_dbf.close(sca_db_handle); sca_db_handle = NULL; return 0; } void destroy_sca_db(void) { /* close the DB connection */ if (sca_db_handle) { sca_dbf.close(sca_db_handle); sca_db_handle = NULL; } } opensips-2.2.2/modules/b2b_sca/sca_db_handler.h000066400000000000000000000106701300170765700214130ustar00rootroot00000000000000/* * sca_db_handler module * * Copyright (C) 2011 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-11-21 initial version (Ovidiu Sas) */ #ifndef SCA_DB_HANDLER #define SCA_DB_HANDLER #include #include #include "sca_records.h" #define SHARED_LINE_COL "shared_line" #define WATCHERS_COL "watchers" #define SHARED_ENTITY_1_COL "app1_shared_entity" #define CALL_STATE_1_COL "app1_call_state" #define CALL_INFO_URI_1_COL "app1_call_info_uri" #define CALL_INFO_APPEARANCE_URI_1_COL "app1_call_info_appearance_uri" #define B2BL_KEY_1_COL "app1_b2bl_key" #define SHARED_ENTITY_2_COL "app2_shared_entity" #define CALL_STATE_2_COL "app2_call_state" #define CALL_INFO_URI_2_COL "app2_call_info_uri" #define CALL_INFO_APPEARANCE_URI_2_COL "app2_call_info_appearance_uri" #define B2BL_KEY_2_COL "app2_b2bl_key" #define SHARED_ENTITY_3_COL "app3_shared_entity" #define CALL_STATE_3_COL "app3_call_state" #define CALL_INFO_URI_3_COL "app3_call_info_uri" #define CALL_INFO_APPEARANCE_URI_3_COL "app3_call_info_appearance_uri" #define B2BL_KEY_3_COL "app3_b2bl_key" #define SHARED_ENTITY_4_COL "app4_shared_entity" #define CALL_STATE_4_COL "app4_call_state" #define CALL_INFO_URI_4_COL "app4_call_info_uri" #define CALL_INFO_APPEARANCE_URI_4_COL "app4_call_info_appearance_uri" #define B2BL_KEY_4_COL "app4_b2bl_key" #define SHARED_ENTITY_5_COL "app5_shared_entity" #define CALL_STATE_5_COL "app5_call_state" #define CALL_INFO_URI_5_COL "app5_call_info_uri" #define CALL_INFO_APPEARANCE_URI_5_COL "app5_call_info_appearance_uri" #define B2BL_KEY_5_COL "app5_b2bl_key" #define SHARED_ENTITY_6_COL "app6_shared_entity" #define CALL_STATE_6_COL "app6_call_state" #define CALL_INFO_URI_6_COL "app6_call_info_uri" #define CALL_INFO_APPEARANCE_URI_6_COL "app6_call_info_appearance_uri" #define B2BL_KEY_6_COL "app6_b2bl_key" #define SHARED_ENTITY_7_COL "app7_shared_entity" #define CALL_STATE_7_COL "app7_call_state" #define CALL_INFO_URI_7_COL "app7_call_info_uri" #define CALL_INFO_APPEARANCE_URI_7_COL "app7_call_info_appearance_uri" #define B2BL_KEY_7_COL "app7_b2bl_key" #define SHARED_ENTITY_8_COL "app8_shared_entity" #define CALL_STATE_8_COL "app8_call_state" #define CALL_INFO_URI_8_COL "app8_call_info_uri" #define CALL_INFO_APPEARANCE_URI_8_COL "app8_call_info_appearance_uri" #define B2BL_KEY_8_COL "app8_b2bl_key" #define SHARED_ENTITY_9_COL "app9_shared_entity" #define CALL_STATE_9_COL "app9_call_state" #define CALL_INFO_URI_9_COL "app9_call_info_uri" #define CALL_INFO_APPEARANCE_URI_9_COL "app9_call_info_appearance_uri" #define B2BL_KEY_9_COL "app9_b2bl_key" #define SHARED_ENTITY_10_COL "app10_shared_entity" #define CALL_STATE_10_COL "app10_call_state" #define CALL_INFO_URI_10_COL "app10_call_info_uri" #define CALL_INFO_APPEARANCE_URI_10_COL "app10_call_info_appearance_uri" #define B2BL_KEY_10_COL "app10_b2bl_key" #define SCA_TABLE_NAME "b2b_sca" #define SCA_TABLE_VERSION 1 #define DB_MODE_NONE 0 #define DB_MODE_REALTIME 1 #define SCA_TABLE_TOTAL_COL_NO (2+(MAX_APPEARANCE_INDEX*5)) #define SCA_FETCH_SIZE 128 extern str shared_line_column; extern str watchers_column; extern str app_shared_entity_column[MAX_APPEARANCE_INDEX]; extern str app_call_state_column[MAX_APPEARANCE_INDEX]; extern str app_call_info_uri_column[MAX_APPEARANCE_INDEX]; extern str app_call_info_appearance_uri_column[MAX_APPEARANCE_INDEX]; extern str app_b2bl_key_column[MAX_APPEARANCE_INDEX]; extern str sca_table_name; extern int sca_db_mode; int init_sca_db(const str *db_url, int dlg_hash_size); int connect_sca_db(const str *db_url); void destroy_sca_db(void); int push_sca_info_to_db(b2b_sca_record_t *record, unsigned int appearance_index, unsigned int forced_update); #endif opensips-2.2.2/modules/b2b_sca/sca_logic.c000066400000000000000000000644321300170765700204260ustar00rootroot00000000000000/* * sca logic module * * Copyright (C) 2010 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-11-21 initial version (Ovidiu Sas) */ #include #include #include "../../ut.h" #include "../../trim.h" #include "../../strcommon.c" #include "../../parser/parse_from.h" #include "../../parser/sdp/sdp.h" #include "../../usr_avp.h" #include "../../parser/parse_call_info.h" #include "../pua/pua.h" #include "../pua/pua_bind.h" #include "../b2b_logic/b2b_load.h" #include "sca_records.h" #include "sca_db_handler.h" #include "sca_logic.h" #define APPEARANCE_INDEX_STR "appearance-index" #define APPEARANCE_INDEX_LEN strlen(APPEARANCE_INDEX_STR) #define APPEARANCE_STATE_STR "appearance-state" #define APPEARANCE_STATE_LEN strlen(APPEARANCE_STATE_STR) #define AUDIO_STR "audio" #define AUDIO_STR_LEN 5 extern pua_api_t pua_api; extern b2bl_api_t b2bl_api; extern str presence_server; //extern int watchers_avp_name; extern unsigned short watchers_avp_type; int get_hash_index_and_shared_line(struct sip_msg* msg, unsigned int *hash_index, str **shared_line); struct to_body* get_appearance_name_addr(struct sip_msg* msg); #define CALL_INFO_APPEARANCE_URI_LEN 64 static char call_info_apperance_uri_buf[CALL_INFO_APPEARANCE_URI_LEN]; #define CALL_INFO_URI "sip:" #define CALL_INFO_URI_LEN 64 static char call_info_uri_buf[CALL_INFO_URI_LEN] = CALL_INFO_URI; #define CALL_INFO_HDR "Call-Info: <" #define INVITE_CALL_INFO_HDR_LEN 128 #define PUBLISH_CALL_INFO_HDR_LEN 512 static char invite_call_info_hdr_buf[INVITE_CALL_INFO_HDR_LEN] = CALL_INFO_HDR; static char publish_call_info_hdr_buf[PUBLISH_CALL_INFO_HDR_LEN] = CALL_INFO_HDR; static const str callinfo_id=str_init("CALLINFO_PUBLISH"); static const str callinfo_appuri_prefix=str_init(">;appearance-uri=\""); static const str callinfo_appindex=str_init(";appearance-index="); static const str callinfo_appstate=str_init(";appearance-state="); static const str callinfo_hdr_postfix=str_init("*;appearance-state=idle\r\n"); static const str callinfo_default_uri=str_init("sip:127.0.0.1>"); static unsigned int b2b_sca_shutdown_completed = 0; #define APP_STATE_IDLE "" #define APP_STATE_ALERTING "alerting" #define APP_STATE_ACTIVE "active" #define APP_STATE_HELD "held" #define APP_STATE_HELD_PRIVATE "held-private" const str app_state[]={ str_init(APP_STATE_IDLE), str_init(APP_STATE_ALERTING), str_init(APP_STATE_ACTIVE), str_init(APP_STATE_HELD), str_init(APP_STATE_HELD_PRIVATE), }; static str scenario = str_init("top hiding"); void destroy_b2b_sca_handlers(void) { b2b_sca_shutdown_completed = 1; } unsigned int get_app_index(struct sip_msg* msg) { unsigned int appearance; struct hdr_field *call_info; struct call_info_body *callinfo_b; struct to_body *call_info_b; struct to_param *param; if (0 == parse_call_info_header(msg)) { call_info = msg->call_info; while (call_info) { //LM_DBG("BODY=[%p]->[%.*s] sibling=[%p]\n", call_info, // call_info->body.len, call_info->body.s, // call_info->sibling); callinfo_b = call_info->parsed; while (callinfo_b) { call_info_b = &(callinfo_b->call_info_body); //LM_DBG(". body=[%.*s] param_lst=[%p] " // "last_param=[%p]\n", // call_info_b->body.len, call_info_b->body.s, // call_info_b->param_lst, // call_info_b->last_param); param = call_info_b->param_lst; while (param) { //LM_DBG(".. [%p]->[%d] " // "[%.*s]=[%.*s]->[%p]\n", // param, param->type, // param->name.len, param->name.s, // param->value.len, param->value.s, // param->next); if (param->name.len==APPEARANCE_INDEX_LEN && strncmp(APPEARANCE_INDEX_STR, param->name.s, APPEARANCE_INDEX_LEN)==0) { if (strno2int(¶m->value, &appearance)<0) { LM_ERR("bad appearance-index" " [%.*s]\n", param->value.len, param->value.s); return 0; } LM_DBG("*** GOT APP-INDEX [%d]\n", appearance); return appearance; } param=param->next; } callinfo_b = callinfo_b->next; } call_info = call_info->sibling; } } else { LM_ERR("Unable to parse Call-Info header\n"); return 0; } LM_ERR("appearance index not found\n"); return 0; } int build_publish_call_info_header(b2b_sca_record_t *rec, str *publish_hdr) { unsigned int i; unsigned int size = sizeof(CALL_INFO_HDR); b2b_sca_call_t *call = NULL; char *p; rec->expires = 30; size += callinfo_default_uri.len + callinfo_appindex.len + callinfo_hdr_postfix.len; if (rec == NULL) { /* we need to build an idle Call-Info header */ publish_hdr->s = publish_call_info_hdr_buf; p = &publish_call_info_hdr_buf[sizeof(CALL_INFO_HDR) - 1]; goto default_hdr; } for (i=0; icall[i]) { call = rec->call[i]; if (call->call_state > ALERTING_STATE) rec->expires = 36000; size += call->call_info_uri.len + callinfo_appuri_prefix.len + call->call_info_apperance_uri.len + callinfo_appindex.len + call->appearance_index_str.len + callinfo_appstate.len + app_state[call->call_state].len + 2; } } if (size > PUBLISH_CALL_INFO_HDR_LEN) { LM_WARN("buffer overflow for PUBLISH Call-Info" " header: size [%d]\n", size); p = (char *)pkg_malloc(size); if (p == NULL) { LM_ERR("OOM\n"); return -1; } publish_hdr->s = p; memcpy(p, publish_call_info_hdr_buf, sizeof(CALL_INFO_HDR) - 1); p += sizeof(CALL_INFO_HDR) - 1; } else { publish_hdr->s = publish_call_info_hdr_buf; p = &publish_call_info_hdr_buf[sizeof(CALL_INFO_HDR) - 1]; } for (i=0; icall[i]) { call = rec->call[i]; memcpy(p, call->call_info_uri.s, call->call_info_uri.len); p += call->call_info_uri.len; memcpy(p, callinfo_appuri_prefix.s, callinfo_appuri_prefix.len); p += callinfo_appuri_prefix.len; memcpy(p, call->call_info_apperance_uri.s, call->call_info_apperance_uri.len); p += call->call_info_apperance_uri.len; *p = '\"'; p++; memcpy(p, callinfo_appindex.s, callinfo_appindex.len); p += callinfo_appindex.len; memcpy(p, call->appearance_index_str.s, call->appearance_index_str.len); p += call->appearance_index_str.len; memcpy(p, callinfo_appstate.s, callinfo_appstate.len); p += callinfo_appstate.len; memcpy(p, app_state[call->call_state].s, app_state[call->call_state].len); p += app_state[call->call_state].len; *p = ','; p++; *p = '<'; p++; } } default_hdr: memcpy(p, callinfo_default_uri.s, callinfo_default_uri.len); p += callinfo_default_uri.len; memcpy(p, callinfo_appindex.s, callinfo_appindex.len); p += callinfo_appindex.len; memcpy(p, callinfo_hdr_postfix.s, callinfo_hdr_postfix.len); p += callinfo_hdr_postfix.len; publish_hdr->len = p - publish_hdr->s; LM_DBG("publish_hdr [%d:%d] [%.*s]\n", size, publish_hdr->len, publish_hdr->len, publish_hdr->s); return 0; } int build_invite_call_info_header(b2b_sca_call_t *call, str* call_info_uri, str *custom_hdr) { unsigned int size; char *p; size = sizeof(CALL_INFO_HDR) + call_info_uri->len + 1 + callinfo_appindex.len + call->appearance_index_str.len + CRLF_LEN; if (size >= INVITE_CALL_INFO_HDR_LEN) { LM_WARN("buffer overflow on INVITE Call-Info header: size [%d]\n", size); p = (char *)pkg_malloc(size); if (p == NULL) { LM_ERR("OOM\n"); return -1; } custom_hdr->s = p; memcpy(p, invite_call_info_hdr_buf, sizeof(CALL_INFO_HDR) - 1); p += sizeof(CALL_INFO_HDR) - 1; } else { custom_hdr->s = invite_call_info_hdr_buf; p = &invite_call_info_hdr_buf[sizeof(CALL_INFO_HDR) - 1]; } memcpy(p, call_info_uri->s, call_info_uri->len); p += call_info_uri->len; *p = '>'; p++; memcpy(p, callinfo_appindex.s, callinfo_appindex.len); p += callinfo_appindex.len; memcpy(p, call->appearance_index_str.s, call->appearance_index_str.len); p += call->appearance_index_str.len; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; custom_hdr->len = p - custom_hdr->s; LM_DBG("custom_hdr [%d:%d] [%.*s]\n", size, custom_hdr->len, custom_hdr->len, custom_hdr->s); return 0; } int build_appearanceURI(str *display, str *uri, str *call_info_apperance_uri) { unsigned int size; int escaped_display_size; char *p; char escaped_display[256]; size = display->len + 5 + uri->len + 2; if (size > CALL_INFO_APPEARANCE_URI_LEN) { LM_WARN("buffer overflow on appearance URI param: size [%d]\n", size); p = (char *)pkg_malloc(size); if (p == NULL) { LM_ERR("OOM\n"); return -1; } call_info_apperance_uri->s = p; } else { p = call_info_apperance_uri->s = call_info_apperance_uri_buf; } if (display->len<80) { escaped_display_size = escape_common(escaped_display, display->s, display->len); if (escaped_display_size) { memcpy(p, escaped_display, escaped_display_size); p += escaped_display_size; *p = ' '; p++; } } *p = '<'; p++; memcpy(p, uri->s, uri->len); p += uri->len; *p = '>'; p++; call_info_apperance_uri->len = p - call_info_apperance_uri->s; /* LM_DBG("call_info_apperance_uri [%d:%d][%.*s]\n", size, call_info_apperance_uri->len, call_info_apperance_uri->len, call_info_apperance_uri->s); */ return 0; } int build_absoluteURI(str *host, str *port, str *call_info_uri) { unsigned int size; char *p; size = sizeof(CALL_INFO_URI) - 1 + host->len + port->len; if (size > CALL_INFO_URI_LEN) { LM_WARN("buffer overflow on absoluteURI: size [%d]\n", size); p = (char *)pkg_malloc(size); if (p == NULL) { LM_ERR("OOM\n"); return -1; } call_info_uri->s = p; memcpy(p, call_info_uri_buf, sizeof(CALL_INFO_URI) - 1); p += sizeof(CALL_INFO_URI) - 1; } else { call_info_uri->s = call_info_uri_buf; p = &call_info_uri_buf[sizeof(CALL_INFO_URI) - 1]; } memcpy(p, host->s, host->len); p += host->len; if (port->s && port->len) { *p = ':'; p++; memcpy(p, port->s, port->len); p += port->len; } call_info_uri->len = p - call_info_uri->s; /* LM_DBG("call_info_uri [%d:%d][%.*s]\n", size, call_info_uri->len, call_info_uri->len, call_info_uri->s); */ return 0; } b2bl_cb_ctx_t* build_cb_params(unsigned int hash_index, str *shared_line, unsigned int appearance_index) { unsigned int size; char *p; b2bl_cb_ctx_t *cb_params; /* Prepare b2b_logic callback params. */ size = sizeof(b2bl_cb_ctx_t) + shared_line->len; cb_params = (b2bl_cb_ctx_t *)shm_malloc(size); if (cb_params == NULL) { LM_ERR("OOM\n"); return NULL; } memset(cb_params, 0, size); cb_params->hash_index = hash_index; cb_params->appearance = appearance_index; p = (char *)(cb_params + 1); cb_params->shared_line.len = shared_line->len; cb_params->shared_line.s = p; memcpy(p, shared_line->s, shared_line->len); p += shared_line->len; return cb_params; } void sca_publish(b2b_sca_record_t *record, str *extra_hdr) { str_lst_t *new_watcher; publ_info_t publ; /* TESTING */ //return; memset(&publ, 0, sizeof(publ_info_t)); publ.id = callinfo_id; publ.body = NULL; publ.expires = record->expires; publ.flag|= UPDATE_TYPE; publ.source_flag|= CALLINFO_PUBLISH; publ.event|= CALLINFO_EVENT; publ.content_type.s = NULL; publ.content_type.len = 0; publ.etag = NULL; publ.extra_headers= extra_hdr; publ.outbound_proxy = presence_server; publ.cb_param = NULL; new_watcher = record->watchers; while (new_watcher) { publ.pres_uri = &new_watcher->watcher; if(pua_api.send_publish(&publ)< 0) { LM_ERR("sending publish failed\n"); } new_watcher = new_watcher->next; } return; } int sca_logic_notify(b2bl_cb_params_t *params, unsigned int b2b_event) { int on_hold = 0; int sdp_session_num = 0, sdp_stream_num; sdp_session_cell_t* sdp_session; sdp_stream_cell_t* sdp_stream; struct sip_msg *msg = params->msg; b2bl_cb_ctx_t *cb_params = params->param; str *shared_line; unsigned int hash_index, appearance; b2b_sca_record_t *record; b2b_sca_call_t *call = NULL; str publish_hdr; unsigned int call_info_appearance; unsigned int call_info_appearance_state = 0; struct hdr_field *call_info; struct call_info_body *callinfo_b; struct to_body *call_info_b; struct to_param *param; if (b2b_sca_shutdown_completed) return B2B_FOLLOW_SCENARIO_CB_RET; if (params == NULL) { LM_ERR("callback event [%d] without cb params\n", b2b_event); return B2B_DROP_MSG_CB_RET; } shared_line = &cb_params->shared_line; hash_index = cb_params->hash_index; appearance = cb_params->appearance; LM_DBG("*** GOT NOTIFICATION TYPE [%d] WITH cb_params [%p]->[%.*s] appearance [%d] on hash index [%d]" " for b2bl entity [%d]\n", b2b_event, cb_params, shared_line->len, shared_line->s, appearance, hash_index, params->entity); if (msg && msg->call_info) { if (0 == parse_call_info_header(msg)) { call_info = msg->call_info; if (call_info) { LM_DBG("BODY=[%p]->[%.*s] sibling=[%p]\n", call_info, call_info->body.len, call_info->body.s, call_info->sibling); callinfo_b = call_info->parsed; while (callinfo_b) { call_info_b = &(callinfo_b->call_info_body); LM_DBG(". body=[%.*s] param_lst=[%p] last_param=[%p]\n", call_info_b->body.len, call_info_b->body.s, call_info_b->param_lst, call_info_b->last_param); param = call_info_b->param_lst; while (param) { LM_DBG(".. [%p]->[%d] [%.*s]=[%.*s]->[%p]\n", param, param->type, param->name.len, param->name.s, param->value.len, param->value.s, param->next); if (param->name.len==APPEARANCE_INDEX_LEN && strncmp(APPEARANCE_INDEX_STR, param->name.s, APPEARANCE_INDEX_LEN)==0) { if (strno2int(¶m->value,&call_info_appearance)<0) { LM_ERR("bad appearance-index [%.*s]\n", param->value.len, param->value.s); return -1; } if (appearance != call_info_appearance) { LM_ERR("got appearance[%d] while expecting[%d]\n", call_info_appearance, appearance); goto next_callinfo_b; } else { LM_DBG("*** GOT APP-INDEX [%d]\n", call_info_appearance); } } else if (param->name.len==APPEARANCE_STATE_LEN && strncmp(APPEARANCE_STATE_STR, param->name.s, APPEARANCE_STATE_LEN)==0) { LM_DBG("*** GOT APP-STATE [%.*s]\n", param->value.len, param->value.s); if (param->value.len == strlen(APP_STATE_HELD_PRIVATE) && strncmp(param->value.s, app_state[HELD_PRIVATE_STATE].s, param->value.len)==0) { call_info_appearance_state = HELD_PRIVATE_STATE; } } param=param->next; } goto handle_appearance; next_callinfo_b: callinfo_b = callinfo_b->next; } call_info = call_info->sibling; } } else { LM_ERR("Unable to parse Call-Info header\n"); return B2B_DROP_MSG_CB_RET; } } handle_appearance: lock_get(&b2b_sca_htable[hash_index].lock); record = b2b_sca_search_record_safe(hash_index, shared_line); if (record == NULL) { lock_release(&b2b_sca_htable[hash_index].lock); LM_ERR("record not found for shared line [%.*s] on hash index [%d]\n", shared_line->len, shared_line->s, hash_index); return B2B_DROP_MSG_CB_RET; } b2b_sca_print_record(record); switch(b2b_event){ case B2B_DESTROY_CB: /* Destroy the sca index record */ shm_free(params->param); b2b_sca_delete_call_record(hash_index, record, appearance); break; case B2B_RE_INVITE_CB: case B2B_CONFIRMED_CB: call = b2b_sca_search_call_safe(record, appearance); if (call == NULL) { LM_ERR("call record not found for shared line [%.*s] with index [%d]\n", shared_line->len, shared_line->s, appearance); lock_release(&b2b_sca_htable[hash_index].lock); return B2B_DROP_MSG_CB_RET; } if (0 == parse_sdp(msg)) { sdp_session = get_sdp_session(msg, sdp_session_num); if(!sdp_session) break; sdp_stream_num = 0; for(;;) { sdp_stream = get_sdp_stream(msg, sdp_session_num, sdp_stream_num); if(!sdp_stream) break; if(sdp_stream->media.len==AUDIO_STR_LEN && strncmp(sdp_stream->media.s,AUDIO_STR,AUDIO_STR_LEN)==0 && sdp_stream->is_on_hold) { on_hold = 1; break; } sdp_stream_num++; } sdp_session_num++; } if (on_hold) { if (call_info_appearance_state) call->call_state = HELD_PRIVATE_STATE; else call->call_state = HELD_STATE; } else { call->call_state = ACTIVE_STATE; } break; default: LM_ERR("Unexpected event\n"); } /* Prepare PUBLISH Call-Info header. */ if (build_publish_call_info_header(record, &publish_hdr) != 0) { lock_release(&b2b_sca_htable[hash_index].lock); LM_ERR("Unable to build PUBLISH Call-Info header\n"); return B2B_FOLLOW_SCENARIO_CB_RET; } /* Save the record to db. */ if (push_sca_info_to_db(record, appearance, 1) != 0) LM_ERR("DB out of synch\n"); /* Notify the watchers. */ sca_publish(record, &publish_hdr); b2b_sca_delete_record_if_empty(record, hash_index); lock_release(&b2b_sca_htable[hash_index].lock); if (publish_hdr.s != publish_call_info_hdr_buf) pkg_free(publish_hdr.s); return B2B_FOLLOW_SCENARIO_CB_RET; } int sca_init_request(struct sip_msg* msg, str* p1, str* p2) { int method_value, ret; //unsigned int size, hash_index, shared_entity; unsigned int hash_index, shared_entity, app_index; str *b2bl_key, *host, *port, *display, *uri, *shared_line; //char *p; //uri_type scheme; struct to_body *appearance_name_addr_body; pv_value_t pv_val; b2b_sca_record_t *record = NULL; b2b_sca_call_t *call = NULL; b2bl_cb_ctx_t *cb_params; str publish_hdr = {NULL, 0}; str custom_hdr = {NULL, 0}; str call_info_uri = {NULL, 0}; str call_info_apperance_uri = {NULL, 0}; if (parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse message\n"); return -1; } method_value = msg->first_line.u.request.method_value; if (method_value != METHOD_INVITE) { LM_ERR("nonINVITE [%d] cannot initiate a call\n", method_value); return -1; } ret = tmb.t_newtran(msg); if(ret < 1) { if(ret == 0) { LM_DBG("It is a retransmission, drop\n"); tmb.unref_cell(tmb.t_gett()); } else { LM_ERR("Error when creating tm transaction\n"); } return 0; } if (p1 && (pv_get_spec_value(msg, (pv_spec_t *)p1, &pv_val) == 0)) { if (pv_val.flags & PV_VAL_INT) { shared_entity = pv_val.ri; LM_DBG("got shared_entity %d\n", shared_entity); } else if (pv_val.flags & PV_VAL_STR) { if(str2int(&(pv_val.rs), (unsigned int*)&shared_entity) != 0) { LM_ERR("Unable to get entity_no from pv '%.*s'\n", pv_val.rs.len, pv_val.rs.s); return -1; } } else { LM_ERR("shared entity not a str or int type\n"); return -1; } } else { LM_ERR("Unable to get shared entity from pv:%p\n", p1); return -1; } switch (shared_entity) { case 0: LM_DBG("Incoming call from shared line\n"); break; case 1: LM_DBG("Outgoing call via a shared line\n"); break; default: LM_ERR("shared line entity should be 0 or 1\n"); return -1; } /* Get the hash index for the shared line. */ if (get_hash_index_and_shared_line(msg, &hash_index, &shared_line)<0) return -1; LM_DBG("got hash_index=[%d] for shared line [%.*s]\n", hash_index, shared_line->len, shared_line->s); /* Get the appearance name-addr for this call. */ appearance_name_addr_body = get_appearance_name_addr(msg); if (appearance_name_addr_body == NULL) { LM_ERR("unable to get apperance of this call\n"); return -1; } //scheme = appearance_name_addr_body->parsed_uri.type; host = &appearance_name_addr_body->parsed_uri.host; port = &appearance_name_addr_body->parsed_uri.port; display = &appearance_name_addr_body->display; uri = &appearance_name_addr_body->uri; LM_DBG("display uri [%.*s][%.*s] from host:port [%.*s]:[%.*s]\n", display->len, display->s, uri->len, uri->s, host->len, host->s, port->len, port->s); /* Prepare absoluteURI for Call-Info header. */ if (build_absoluteURI(host, port, &call_info_uri) != 0) goto error1; /* Prepare appearanceURI param for Call-Info header. */ if (build_appearanceURI(display, uri, &call_info_apperance_uri) != 0) goto error1; /* Extract required appearance from the received request */ app_index = get_app_index(msg); /* Adding call to the sca_table. */ lock_get(&b2b_sca_htable[hash_index].lock); if (b2b_sca_add_call_record(hash_index, shared_line, shared_entity, app_index, &call_info_uri, &call_info_apperance_uri, &record, &call) != 0) { LM_ERR("unable to add record to sca htable\n"); goto error2; } /* Prepare INVITE Call-Info header. */ if (build_invite_call_info_header(call, &call_info_uri, &custom_hdr) != 0) goto error2; /* Prepare PUBLISH Call-Info header. */ if (build_publish_call_info_header(record, &publish_hdr) != 0) { LM_ERR("Unable to build PUBLISH Call-Info header\n"); goto error2; } /* Prepare b2b_logic callback params. */ cb_params = build_cb_params(hash_index, shared_line, call->appearance_index); if (cb_params == NULL) goto error2; LM_DBG("*** INITIALIZING \"top hiding\" SCENARIO with cb_params [%p]\n", cb_params); /* release the lock here to avoid deadlock while getting callback notifications */ lock_release(&b2b_sca_htable[hash_index].lock); b2bl_key = b2bl_api.init(msg, &scenario, NULL, &sca_logic_notify, (void *)cb_params, B2B_RE_INVITE_CB|B2B_CONFIRMED_CB|B2B_DESTROY_CB, &custom_hdr); lock_get(&b2b_sca_htable[hash_index].lock); if (!b2bl_key || !b2bl_key->s || !b2bl_key->len) goto error2; else if (b2b_sca_update_call_record_key(call, b2bl_key) != 0) goto error3; /* Save the record to db. */ if (push_sca_info_to_db(record, call->appearance_index, 0) != 0) goto error3; /* Notify the watchers. */ sca_publish(record, &publish_hdr); lock_release(&b2b_sca_htable[hash_index].lock); if (publish_hdr.s != publish_call_info_hdr_buf) pkg_free(publish_hdr.s); if (custom_hdr.s != invite_call_info_hdr_buf) pkg_free(custom_hdr.s); if (call_info_uri.s != call_info_uri_buf) pkg_free(call_info_uri.s); if (call_info_apperance_uri.s != call_info_apperance_uri_buf) pkg_free(call_info_apperance_uri.s); return 1; error3: /* Release the call */ b2bl_api.terminate_call(b2bl_key); error2: lock_release(&b2b_sca_htable[hash_index].lock); error1: if (publish_hdr.s != publish_call_info_hdr_buf) pkg_free(publish_hdr.s); if (custom_hdr.s != invite_call_info_hdr_buf) pkg_free(custom_hdr.s); if (call_info_uri.s != call_info_uri_buf) pkg_free(call_info_uri.s); if (call_info_apperance_uri.s != call_info_apperance_uri_buf) pkg_free(call_info_apperance_uri.s); return -1; } int sca_bridge_request(struct sip_msg* msg, str* p1, str* p2) { pv_value_t pv_val; str shared_line = {NULL, 0}; str publish_hdr = {NULL, 0}; int method_value, ret; //int entity_no; unsigned int hash_index; b2b_sca_record_t *record = NULL; b2b_sca_call_t *call; unsigned int appearance; if (p1 && (pv_get_spec_value(msg, (pv_spec_t *)p1, &pv_val) == 0)) { if (pv_val.flags & PV_VAL_STR) { LM_DBG("got shared_line:'%.*s'\n", pv_val.rs.len, pv_val.rs.s); shared_line = pv_val.rs; } else { LM_ERR("Unable to get shared_line from PV that is not a string\n"); return -1; } } else { LM_ERR("Unable to get shared_line from pv:%p\n", p1); return -1; } /* Get the hash index for the shared line. */ hash_index = core_hash(&shared_line, NULL, b2b_sca_hsize); LM_DBG("got hash_index=[%d] for shared line [%.*s]\n", hash_index, shared_line.len, shared_line.s); if (parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse message\n"); return -1; } method_value = msg->first_line.u.request.method_value; if (method_value != METHOD_INVITE) { LM_ERR("nonINVITE [%d] cannot bridge a call\n", method_value); return -1; } ret = tmb.t_newtran(msg); if(ret < 1) { if(ret == 0) { LM_DBG("It is a retransmission, drop\n"); tmb.unref_cell(tmb.t_gett()); } else { LM_ERR("Error when creating tm transaction\n"); } return 0; } if (!msg->call_info) { LM_ERR("No 'Call-Info' header\n"); return -1; } /* Extract required appearance from the received request */ appearance = get_app_index(msg); if (appearance==0) return -1; lock_get(&b2b_sca_htable[hash_index].lock); record = b2b_sca_search_record_safe(hash_index, &shared_line); if (record == NULL) { lock_release(&b2b_sca_htable[hash_index].lock); LM_ERR("record not found for shared line [%.*s] on hash index [%d]\n", shared_line.len, shared_line.s, hash_index); // FIXME: /* Build an empty PUBLISH header */ //if (build_publish_call_info_header(NULL, &publish_hdr) != 0) { // LM_ERR("Unable to build PUBLISH Call-Info header\n"); //} goto error; } b2b_sca_print_record(record); call = b2b_sca_search_call_safe(record, appearance); if (call == NULL) goto error; if (call->call_state != HELD_STATE) { LM_ERR("Improper call state [%d] for bridging\n", call->call_state); goto error; } /* What will happen if the b2b_logic entity doesn't exist anymore? */ LM_DBG("*** BRIDGING THE REQUEST to entity [%d] on tuple [%.*s]\n", call->shared_entity, call->b2bl_key.len, call->b2bl_key.s); ret = b2bl_api.bridge_msg(msg, &call->b2bl_key, call->shared_entity); if (ret != 0) { /* FIXME: * handle the error here */ LM_ERR("*** got ret [%d]\n", ret); goto error; } LM_DBG("*** got ret [%d]\n", ret); /* Set the state back to active */ call->call_state = ACTIVE_STATE; /* Reset the shared_entity */ call->shared_entity = 0; /* Prepare PUBLISH Call-Info header. */ if (build_publish_call_info_header(record, &publish_hdr) != 0) { lock_release(&b2b_sca_htable[hash_index].lock); LM_ERR("Unable to build PUBLISH Call-Info header\n"); return B2B_FOLLOW_SCENARIO_CB_RET; } /* Save the record to db. */ if (push_sca_info_to_db(record, appearance, 1) != 0) LM_ERR("db out of synch\n"); /* Notify the watchers. */ sca_publish(record, &publish_hdr); lock_release(&b2b_sca_htable[hash_index].lock); return 1; error: lock_release(&b2b_sca_htable[hash_index].lock); if (publish_hdr.s && publish_hdr.s != publish_call_info_hdr_buf) pkg_free(publish_hdr.s); return -1; } opensips-2.2.2/modules/b2b_sca/sca_logic.h000066400000000000000000000025271300170765700204300ustar00rootroot00000000000000/* * sca logic module * * Copyright (C) 2010 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-11-21 initial version (Ovidiu Sas) */ #ifndef B2B_SLA_LOGIC #define B2B_SLA_LOGIC #include #include #include "../../str.h" typedef struct b2bl_cb_ctx { unsigned int hash_index; str shared_line; unsigned int appearance; } b2bl_cb_ctx_t; void destroy_b2b_sca_handlers(void); b2bl_cb_ctx_t* build_cb_params(unsigned int hash_index, str *shared_line, unsigned int appearance_index); int sca_logic_notify(b2bl_cb_params_t *params, unsigned int b2b_event); #endif opensips-2.2.2/modules/b2b_sca/sca_records.c000066400000000000000000000327321300170765700207700ustar00rootroot00000000000000/* * shared call appearance module * * Copyright (C) 2010 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-11-02 initial version (Ovidiu Sas) */ #include #include #include #include "../../ut.h" #include "../../mem/shm_mem.h" #include "../../usr_avp.h" #include "../../parser/parse_uri.h" #include "sca_records.h" extern int watchers_avp_name; extern unsigned short watchers_avp_type; void add_watcher(str_lst_t **watchers, str_lst_t *new_watcher) { new_watcher->next = *watchers; *watchers = new_watcher; return; } void free_watchers(str_lst_t *watchers) { str_lst_t *watcher = watchers, *w; while (watcher) { w = watcher->next; pkg_free(watcher); watcher = w; } return; } void memcpy_watchers(str_lst_t *dest, str_lst_t *src, unsigned int size) { str_lst_t *from = src; str_lst_t *to = dest; char *p; unsigned int len, total_len=0; p = (char *)to; while (from) { len = sizeof(str_lst_t) + from->watcher.len; total_len += len; if (len > size) { LM_CRIT("buffer overflow\n"); return; } memcpy((char *)to, (char *)from, len); to->watcher.s = (char *)(to + 1); if (to->watcher.len != from->watcher.len) { LM_CRIT("error\n"); return; } if (from->next == NULL) { to->next = NULL; break; } to->next = (str_lst_t *)(p + len); from = from->next; to = to->next; } return; } void print_watchers(str_lst_t *watchers) { str_lst_t *watcher = watchers; unsigned int len = 0; while (watcher) { len += watcher->watcher.len; LM_DBG("watcher [%d] [%d][%.*s]\n", len, watcher->watcher.len, watcher->watcher.len, watcher->watcher.s); watcher = watcher->next; } } void get_watchers_from_avp(str_lst_t **watchers, unsigned int *watcher_size, unsigned int *watchers_no) { str_lst_t *new_watcher; struct usr_avp *avp; int_str val; unsigned int size; struct sip_uri parsed_uri; char *p; *watchers = NULL; *watcher_size = 0; *watchers_no = 0; for(;;) { avp = search_first_avp(watchers_avp_type, watchers_avp_name, &val, 0); if (avp == NULL) break; if(avp->flags&AVP_VAL_STR) if (parse_uri(val.s.s, val.s.len, &parsed_uri)<0) LM_WARN("discarding non URI watcher [%.*s]\n", val.s.len, val.s.s); else { LM_DBG("got watcher [%.*s]\n", val.s.len, val.s.s); size = sizeof(str_lst_t) + val.s.len; new_watcher = (str_lst_t *)pkg_malloc(size); if (new_watcher == NULL) { LM_ERR("OOM\n"); return; } memset(new_watcher, 0, size); p = (char*)(new_watcher + 1); new_watcher->watcher.len = val.s.len; new_watcher->watcher.s = p; memcpy(p, val.s.s, val.s.len); add_watcher(watchers, new_watcher); *watcher_size += size; *watchers_no += 1; } else LM_WARN("Ignoring non STR AVP\n"); destroy_avp(avp); } print_watchers(*watchers); return; } void get_watchers_from_csv(str *watchers_csv, str_lst_t **watchers, unsigned int *watcher_size, unsigned int *watcher_no) { str_lst_t *new_watcher; char *tmp; char *start = watchers_csv->s; char *end = watchers_csv->s + watchers_csv->len; unsigned int size; char *p; *watchers = NULL; *watcher_size = 0; for( tmp=watchers_csv->s; tmp<=end; tmp++) { if (*tmp == ',' || tmp==end) { LM_DBG("watcher->[%.*s]\n", (int)(tmp-start), start); size = sizeof(str_lst_t) + tmp-start; new_watcher = (str_lst_t *)pkg_malloc(size); if (new_watcher == NULL) { LM_ERR("OOM\n"); return; } memset(new_watcher, 0, size); p = (char*)(new_watcher + 1); new_watcher->watcher.len = tmp-start; new_watcher->watcher.s = p; memcpy(p, start, tmp-start); add_watcher(watchers, new_watcher); *watcher_size += size; *watcher_no += 1; start = tmp + 1; } } print_watchers(*watchers); return; } void b2b_sca_print_call_record(unsigned int i, b2b_sca_call_t *call) { LM_DBG("appearance[%d][%d:%.*s][%p]->[%.*s][%d][%d][%.*s][%.*s]\n", i, call->appearance_index, call->appearance_index_str.len, call->appearance_index_str.s, call, call->b2bl_key.len, call->b2bl_key.s, call->shared_entity, call->call_state, call->call_info_uri.len, call->call_info_uri.s, call->call_info_apperance_uri.len, call->call_info_apperance_uri.s); } void b2b_sca_print_record(b2b_sca_record_t *rec) { unsigned int i; LM_DBG("record:[%p]->[%.*s] [%d] [%p:%p]\n", rec, rec->shared_line.len, rec->shared_line.s, rec->watchers_no, rec->prev, rec->next); print_watchers(rec->watchers); for (i=0; icall[i]) { b2b_sca_print_call_record(i, rec->call[i]); } } } b2b_sca_call_t* b2b_sca_search_call_safe(b2b_sca_record_t *record, unsigned int appearance) { if (!appearance || appearance > MAX_APPEARANCE_INDEX) { LM_ERR("out of bounds index [%d]\n", appearance); return NULL; } if (record->call[appearance - 1] == NULL) { LM_ERR("non existing call for shared line [%.*s] with index [%d]\n", record->shared_line.len, record->shared_line.s, appearance); return NULL; } return record->call[appearance - 1]; } b2b_sca_record_t* b2b_sca_search_record_safe(int hash_index, str *shared_line) { b2b_sca_record_t *record; record = b2b_sca_htable[hash_index].first; while(record && (record->shared_line.len != shared_line->len || strncmp(record->shared_line.s, shared_line->s, shared_line->len) != 0)) { b2b_sca_print_record(record); record = record->next; } return record; } b2b_sca_call_t* restore_call(b2b_sca_record_t *record, unsigned int appearance_index, unsigned int shared_entity, unsigned int call_state, str *call_info_uri, str *call_info_apperance_uri) { b2b_sca_call_t *call; unsigned int size; str appearance_index_str; char *p; appearance_index_str.s = int2str((unsigned long)appearance_index, &appearance_index_str.len); size = sizeof(b2b_sca_call_t) + appearance_index_str.len + call_info_uri->len + call_info_apperance_uri->len + B2BL_MAX_KEY_LEN; call = (b2b_sca_call_t *)shm_malloc(size); if (call == NULL) { LM_ERR("OOM\n"); return NULL; } memset(call, 0, size); call->appearance_index = appearance_index; call->call_state = call_state; call->shared_entity = shared_entity; p = (char*)(call + 1); call->appearance_index_str.len = appearance_index_str.len; call->appearance_index_str.s = p; memcpy(p, appearance_index_str.s, appearance_index_str.len); p += appearance_index_str.len; call->call_info_uri.len = call_info_uri->len; call->call_info_uri.s = p; memcpy(p, call_info_uri->s, call_info_uri->len); p += call_info_uri->len; call->call_info_apperance_uri.len = call_info_apperance_uri->len; call->call_info_apperance_uri.s = p; memcpy(p, call_info_apperance_uri->s, call_info_apperance_uri->len); p += call_info_apperance_uri->len; call->b2bl_key.len = 0; call->b2bl_key.s = p; p += B2BL_MAX_KEY_LEN; record->call[appearance_index-1] = call; return call; } b2b_sca_record_t* restore_record(str *shared_line, str *watchers_csv) { str_lst_t *watchers; unsigned int size, watcher_size, watchers_no; char *p; b2b_sca_record_t *record; get_watchers_from_csv(watchers_csv, &watchers, &watcher_size, &watchers_no); size = sizeof(b2b_sca_record_t) + shared_line->len + watcher_size; record = (b2b_sca_record_t *)shm_malloc(size); if (record == NULL) { LM_ERR("OOM\n"); return NULL; } memset(record, 0, size); p = (char*)(record + 1); record->watchers_no = watchers_no; record->shared_line.len = shared_line->len; record->shared_line.s = p; memcpy(p, shared_line->s, shared_line->len); p += shared_line->len; record->watchers = (str_lst_t *)p; memcpy_watchers(record->watchers, watchers, watcher_size); if (watchers) free_watchers(watchers); return record; } int b2b_sca_update_call_record_key(b2b_sca_call_t *call, str* b2bl_key) { if (!b2bl_key || !b2bl_key->s || b2bl_key->len > B2BL_MAX_KEY_LEN) return -1; memcpy(call->b2bl_key.s, b2bl_key->s, b2bl_key->len); call->b2bl_key.len = b2bl_key->len; return 0; } int b2b_sca_add_call_record(int hash_index, str *shared_line, unsigned int shared_entity, unsigned int app_index, str *call_info_uri, str *call_info_apperance_uri, b2b_sca_record_t **record_ctx, b2b_sca_call_t **call_ctx) { //b2b_sca_record_t *rec, *prev_rec; b2b_sca_record_t *record; b2b_sca_call_t *call; unsigned int i, size, watcher_size, watchers_no; char *p; str_lst_t *watchers; if (app_index>=MAX_APPEARANCE_INDEX) { LM_ERR("Required app_index [%d] too big\n", app_index); return -1; } record = b2b_sca_search_record_safe(hash_index, shared_line); if (record) { /* We already have active calls for this shared line */ if (app_index) { i = app_index - 1; if (record->call[i]) { LM_DBG("Searching for a new slot\n"); for (i=0; icall[i] == NULL) break; } } } else { LM_DBG("no forced app_index\n"); for (i=0; icall[i] == NULL) break; } } if (i == MAX_APPEARANCE_INDEX) { LM_ERR("No available slots for this call\n"); return -1; } call = restore_call(record, i+1, shared_entity, ALERTING_STATE, call_info_uri, call_info_apperance_uri); if (call == NULL) { return -1; } } else { /* First call for this shared line */ /* Get the list of watchers */ get_watchers_from_avp(&watchers, &watcher_size, &watchers_no); size = sizeof(b2b_sca_record_t) + shared_line->len + watcher_size; record = (b2b_sca_record_t *)shm_malloc(size); if (record == NULL) { LM_ERR("OOM\n"); return -1; } memset(record, 0, size); p = (char*)(record + 1); record->watchers_no = watchers_no; record->shared_line.len = shared_line->len; record->shared_line.s = p; memcpy(p, shared_line->s, shared_line->len); p += shared_line->len; if (watchers && watcher_size) { record->watchers = (str_lst_t *)p; memcpy_watchers(record->watchers, watchers, watcher_size); if (watchers) free_watchers(watchers); } else { record->watchers = NULL; LM_WARN("We have no watchers: watchers=[%p] and watcher_size=[%d]\n", watchers, watcher_size); } /* Let's take care of the call now */ call = restore_call(record, app_index?app_index:1, shared_entity, ALERTING_STATE, call_info_uri, call_info_apperance_uri); if (call == NULL) goto error; /* Insert record into the table */ insert_record(hash_index, record); /* rec = b2b_sca_htable[hash_index].first; if (rec) { while(rec) { prev_rec = rec; rec = rec->next; } prev_rec->next = record; record->prev = prev_rec; } else { b2b_sca_htable[hash_index].first = record; record->prev = record->next = NULL; } */ } *record_ctx = record; *call_ctx = call; return 0; error: shm_free(record); return -1; } void b2b_sca_delete_call_record(int hash_index, b2b_sca_record_t *record, unsigned int appearance) { b2b_sca_call_t *call = b2b_sca_search_call_safe(record, appearance); if (call) { shm_free(call); record->call[appearance - 1] = NULL; } return; } void b2b_sca_delete_record(b2b_sca_record_t *record, unsigned int hash_index) { unsigned int i; if(b2b_sca_htable[hash_index].first == record) { b2b_sca_htable[hash_index].first = record->next; if(record->next) record->next->prev = NULL; } else { if(record->prev) record->prev->next = record->next; if(record->next) record->next->prev = record->prev; } for (i=0; icall[i]) { b2b_sca_print_call_record(i, record->call[i]); LM_WARN("delete record with active appearance [%d]\n", i+1); shm_free(record->call[i]); } shm_free(record); return; } void b2b_sca_delete_record_if_empty(b2b_sca_record_t *record, unsigned int hash_index) { unsigned int i; for (i=0; icall[i]) return; b2b_sca_delete_record(record, hash_index); return; } void insert_record(unsigned int hash_index, b2b_sca_record_t *record) { b2b_sca_record_t *rec, *prev_rec; /* Insert record into the table */ rec = b2b_sca_htable[hash_index].first; if (rec) { while(rec) { prev_rec = rec; rec = rec->next; } prev_rec->next = record; record->prev = prev_rec; } else { b2b_sca_htable[hash_index].first = record; record->prev = record->next = NULL; } } int init_b2b_sca_htable(void) { int i; b2b_sca_htable = (b2b_sca_table_t)shm_malloc(b2b_sca_hsize* sizeof(b2b_sca_entry_t)); if(!b2b_sca_htable) { LM_ERR("OOM\n"); goto error; } for(i= 0; i< b2b_sca_hsize; i++) { lock_init(&b2b_sca_htable[i].lock); b2b_sca_htable[i].first = NULL; } return 0; error: return -1; } void destroy_b2b_sca_htable(void) { int i; b2b_sca_record_t *record; if(!b2b_sca_htable) return; for(i= 0; i< b2b_sca_hsize; i++) { lock_destroy(&b2b_sca_htable[i].lock); record = b2b_sca_htable[i].first; while(record) { b2b_sca_delete_record(record, i); record = b2b_sca_htable[i].first; } } shm_free(b2b_sca_htable); return; } opensips-2.2.2/modules/b2b_sca/sca_records.h000066400000000000000000000073561300170765700210010ustar00rootroot00000000000000/* * sca logic module * * Copyright (C) 2010 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-11-21 initial version (Ovidiu Sas) */ #ifndef B2B_SLA_RECORDS #define B2B_SLA_RECORDS #include #include #include "../../usr_avp.h" #include "../b2b_logic/b2b_load.h" #define CALL_INFO_HEADER_MAX_LEN 512 #define IDLE_STATE 0 #define ALERTING_STATE 1 #define ACTIVE_STATE 2 #define HELD_STATE 3 #define HELD_PRIVATE_STATE 4 #define MAX_INDEX_STATE 4 #define MAX_APPEARANCE_INDEX 10 typedef struct str_lst{ str watcher; struct str_lst *next; } str_lst_t; typedef struct b2b_sca_call { unsigned int shared_entity; /* the entity to keep */ unsigned int appearance_index; /* appearance index */ str appearance_index_str; /* appearance index stored as str */ unsigned int call_state; /* state of the call */ str call_info_uri; /* call info: absoluteURI */ str call_info_apperance_uri; /* call info: appearance param URI */ str b2bl_key; /* key for the associated b2b_logic tuple */ } b2b_sca_call_t; typedef struct b2b_sca_record { str shared_line; /* shared line identifier */ int expires; unsigned int watchers_no; str_lst_t *watchers; /* list of watchers */ b2b_sca_call_t *call[MAX_APPEARANCE_INDEX]; /* array of appearances */ struct b2b_sca_record *prev; struct b2b_sca_record *next; } b2b_sca_record_t; typedef struct b2b_sca_entry { b2b_sca_record_t *first; gen_lock_t lock; } b2b_sca_entry_t; typedef b2b_sca_entry_t *b2b_sca_table_t; extern b2b_sca_table_t b2b_sca_htable; extern unsigned int b2b_sca_hsize; int init_b2b_sca_htable(void); void destroy_b2b_sca_htable(void); /* void memcpy_watchers(str_lst_t *dest, str_lst_t *src, unsigned int size); void get_watchers_from_csv(str *watchers_csv, str_lst_t **watchers, unsigned int *watcher_size, unsigned int *watcher_no); */ void add_watcher(str_lst_t **watchers, str_lst_t *new_watcher); void free_watchers(str_lst_t *watchers); void print_watchers(str_lst_t *watchers); void b2b_sca_print_record(b2b_sca_record_t *rec); b2b_sca_call_t* b2b_sca_search_call_safe(b2b_sca_record_t *record, unsigned int appearance); b2b_sca_record_t* b2b_sca_search_record_safe(int hash_index, str *shared_line); int b2b_sca_update_call_record_key(b2b_sca_call_t *call, str* b2bl_key); int b2b_sca_add_call_record(int hash_index, str *shared_line, unsigned int shared_entity, unsigned int app_index, str *call_info_uri, str *call_info_apperance_uri, b2b_sca_record_t **record, b2b_sca_call_t **call); void b2b_sca_delete_call_record(int hash_index, b2b_sca_record_t *record, unsigned int appearance); void b2b_sca_delete_record_if_empty(b2b_sca_record_t *record, unsigned int hash_index); b2b_sca_call_t* restore_call(b2b_sca_record_t *record, unsigned int appearance_index, unsigned int shared_entity, unsigned int call_state, str *call_info_uri, str *call_info_apperance_uri); b2b_sca_record_t* restore_record(str *shared_line, str *watchers_csv); void insert_record(unsigned int hash_index, b2b_sca_record_t *record); #endif opensips-2.2.2/modules/benchmark/000077500000000000000000000000001300170765700167655ustar00rootroot00000000000000opensips-2.2.2/modules/benchmark/Makefile000066400000000000000000000004531300170765700204270ustar00rootroot00000000000000# $Id$ # # User aliases # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=benchmark.so LIBS= DEFS+= # nanosec precision - requires realtime library #DEFS+= -DBM_CLOCK_REALTIME #LIBS+= -lrt include ../../Makefile.modules opensips-2.2.2/modules/benchmark/README000066400000000000000000000203331300170765700176460ustar00rootroot00000000000000Benchmark Module Bastian Friedrich Collax GmbH Daniel-Constantin Mierla Edited by Bastian Friedrich Copyright © 2007 Collax GmbH Copyright © 2007 Voice System SRL Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. enable (int) 1.3.2. granularity (int) 1.3.3. loglevel (int) 1.4. Exported Functions 1.4.1. bm_start_timer(name) 1.4.2. bm_log_timer(name) 1.5. Exported pseudo-variables 1.5.1. $BM_time_diff 1.6. Exported MI Functions 1.6.1. bm_enable_global 1.6.2. bm_enable_timer 1.6.3. bm_granularity 1.6.4. bm_loglevel 1.6.5. bm_poll_results 1.7. Example of usage 2. Developer Guide 2.1. Available Functions 2.1.1. bm_register(name, mode, id) 2.1.2. bm_start(id) 2.1.3. bm_log(id) 2.2. Benchmark API Example List of Examples 1.1. Set enable parameter 1.2. Set granularity parameter 1.3. Set loglevel parameter 1.4. bm_start_timer usage 1.5. bm_log_timer usage 1.6. Enabling a timer 1.7. Getting the results via FIFO interface 1.8. benchmark usage 2.1. Using the benchmark module's API from another module Chapter 1. Admin Guide 1.1. Overview This module helps developers to benchmark their module functions. By adding this module's functions via the configuration file or through its API, OpenSIPS can log profiling information for every function. The duration between calls to start_timer and log_timer is stored and logged via OpenSIPS's logging facility. Please note that all durations are given as microseconds (don't confuse with milliseconds!). 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. enable (int) Even when the module is loaded, benchmarking is not enabled per default. This variable may have three different values: * -1 - Globally disable benchmarking * 0 - Enable per-timer enabling. Single timers are inactive by default and can be activated through the MI interface as soon as that feature is implemented. * 1 - Globally enable benchmarking Default value is “0â€. Example 1.1. Set enable parameter ... modparam("benchmark", "enable", 1) ... 1.3.2. granularity (int) Logging normally is not done for every reference to the log_timer() function, but only every n'th call. n is defined through this variable. A sensible granularity seems to be 100. If granularity is set to 0, then nothing will be logged automatically. Instead bm_poll_results MI command can be used to retrieve the results and clean the local values. Default value is “100â€. Example 1.2. Set granularity parameter ... modparam("benchmark", "granularity", 500) ... 1.3.3. loglevel (int) Set the log level for the benchmark logs. These levels should be used: * -3 - L_ALERT * -2 - L_CRIT * -1 - L_ERR * 1 - L_WARN * 2 - L_NOTICE * 3 - L_INFO * 4 - L_DBG Default value is “3†(L_INFO). Example 1.3. Set loglevel parameter ... modparam("benchmark", "loglevel", 4) ... This will set the logging level to L_DBG. 1.4. Exported Functions 1.4.1. bm_start_timer(name) Start timer “nameâ€. A later call to “bm_log_timer()†logs this timer.. Example 1.4. bm_start_timer usage ... bm_start_timer("test"); ... 1.4.2. bm_log_timer(name) This function logs the timer with the given ID. The following data are logged: * Last msgs is the number of calls in the last logging interval. This equals the granularity variable. * Last sum is the accumulated duration in the current logging interval (i.e. for the last “granularity†calls). * Last min is the minimum duration between start/log_timer calls during the last interval. * Last max - maximum duration. * Last average is the average duration between bm_start_timer() and bm_log_timer() since the last logging. * Global msgs number of calls to log_timer. * Global sum total duration in microseconds. * Global min... You get the point. :) * Global max also obvious. * Global avg possibly the most interesting value. Example 1.5. bm_log_timer usage ... bm_log_timer("test"); ... 1.5. Exported pseudo-variables Exported pseudo-variables are listed in the next sections. 1.5.1. $BM_time_diff $BM_time_diff - the time difference elapsed between calls of bm_start_timer(name) and bm_log_timer(name). The value is 0 if no bm_log_timer() was called. 1.6. Exported MI Functions 1.6.1. bm_enable_global Enables/disables the module. Parameter may be -1, 0 or 1. See discription of "enable" parameter. 1.6.2. bm_enable_timer Enable or disable a single timer. The following example enables timer "test" (the second parameter must be 0 to disable): Example 1.6. Enabling a timer ... opensipsctl fifo bm_enable_timer test 1 ... 1.6.3. bm_granularity Modifies the benchmarking granularity. See "granularity" variable. 1.6.4. bm_loglevel Modifies the module log level. See "loglevel" variable. 1.6.5. bm_poll_results Returns the current and global results for each timer. This command is only available if the "granularity" variable is set to 0. It can be used to get results in stable time intervals instead of every N messages. Each timer will have 2 nodes - the local and the global values. Format of the values is the same as the one normally used in logfile. This way of getting the results allows to interface with external graphing applications like Munin. If there were no new calls to bm_log_timer since last check, then all current values of a timer will be equal 0. Each call to bm_poll_results will reset current values (but not global ones). Example 1.7. Getting the results via FIFO interface ... opensipsctl fifo bm_poll_results register_timer 3/40/12/14/13.333333 9/204/12/97/22.666667 security_check_timer 3/21/7/7/7.000000 9/98/7/41/10.888889 ... 1.7. Example of usage Measure the duration of user location lookup. Example 1.8. benchmark usage ... bm_start_timer("usrloc-lookup"); lookup("location"); bm_log_timer("usrloc-lookup"); ... Chapter 2. Developer Guide The benchmark module provides an internal API to be used by other OpenSIPS modules. The available functions are identical to the user exported functions. Please note that this module is intended mainly for developers. It should be used with caution in production environments. 2.1. Available Functions 2.1.1. bm_register(name, mode, id) This function register a new timer and/or returns the internal ID associated with the timer. mode controls the creation of new timer if not found. id is to be used by start and log timer functions. 2.1.2. bm_start(id) This function equals the user-exported function bm_start_timer. The id is passed as an integer, though. 2.1.3. bm_log(id) This function equals the user-exported function bm_log_timer. The id is passed as an integer, though. 2.2. Benchmark API Example Example 2.1. Using the benchmark module's API from another module ... #include "../benchmark/benchmark.h" ... struct bm_binds bmb; ... ... /* load the benchmarking API */ if (load_bm_api( &bmb )!=0) { LM_ERR("can't load benchmark API\n"); goto error; } ... ... /* Start/log timers during a (usually user-exported) module function */ bmb.bm_register("test", 1, &id) bmb.bm_start(id); do_something(); bmb.bm_log(id); ... opensips-2.2.2/modules/benchmark/benchmark.c000066400000000000000000000376031300170765700210740ustar00rootroot00000000000000/* * Benchmarking module for OpenSIPS * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #define _GNU_SOURCE #include #include #include #include "../../sr_module.h" #include "../../locking.h" #include "../../mi/mi.h" #include "../../mem/mem.h" #include "../../ut.h" #include "benchmark.h" #include "../../mem/shm_mem.h" #define MI_CALL_INVALID_S "Call not valid for granularity!=0" #define MI_CALL_INVALID_LEN (sizeof(MI_CALL_INVALID_S)-1) #define STARTING_MIN_VALUE 0xffffffff /* Exported functions */ static int bm_start_timer(struct sip_msg* _msg, char* timer, char *foobar); static int bm_log_timer(struct sip_msg* _msg, char* timer, char* mystr); /* * Module destroy function prototype */ static void destroy(void); /* * Module child-init function prototype */ static int child_init(int rank); /* * Module initialization function prototype */ static int mod_init(void); /* * Exported parameters * Copied to mycfg on module initialization */ static int bm_enable_global = 0; static int bm_granularity = 100; static int bm_loglevel = L_INFO; static int _bm_last_time_diff = 0; /* * Module setup */ typedef struct bm_cfg { int enable_global; int granularity; int loglevel; /* The internal timers */ int nrtimers; benchmark_timer_t *timers; benchmark_timer_t **tindex; } bm_cfg_t; /* * The setup is located in shared memory so that * all instances can access this variable */ bm_cfg_t *bm_mycfg = 0; static inline int fixup_bm_timer(void** param, int param_no); /* * Exported functions */ static cmd_export_t cmds[] = { { "bm_start_timer", (cmd_function)bm_start_timer, 1, fixup_bm_timer, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, { "bm_log_timer", (cmd_function)bm_log_timer, 1, fixup_bm_timer, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"load_bm", (cmd_function)load_bm, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0 } }; /* * Exported parameters */ static param_export_t params[] = { {"enable", INT_PARAM, &bm_enable_global}, {"granularity", INT_PARAM, &bm_granularity}, {"loglevel", INT_PARAM, &bm_loglevel}, { 0, 0, 0 } }; /* * Exported MI functions */ static struct mi_root* mi_bm_enable_global(struct mi_root *cmd, void *param); static struct mi_root* mi_bm_enable_timer(struct mi_root *cmd, void *param); static struct mi_root* mi_bm_granularity(struct mi_root *cmd, void *param); static struct mi_root* mi_bm_loglevel(struct mi_root *cmd, void *param); static struct mi_root* mi_bm_poll_results(struct mi_root *cmd, void *param); static mi_export_t mi_cmds[] = { { "bm_enable_global", 0, mi_bm_enable_global, 0, 0, 0 }, { "bm_enable_timer", 0, mi_bm_enable_timer, 0, 0, 0 }, { "bm_granularity", 0, mi_bm_granularity, 0, 0, 0 }, { "bm_loglevel", 0, mi_bm_loglevel, 0, 0, 0 }, { "bm_poll_results", 0, mi_bm_poll_results, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; static int bm_get_time_diff(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static pv_export_t mod_items[] = { { {"BM_time_diff", sizeof("BM_time_diff")-1}, 1000, bm_get_time_diff, 0, 0, 0, 0, 0 }, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; /* * Module interface */ struct module_exports exports = { "benchmark", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; /****************/ /* * mod_init * Called by opensips at init time */ static int mod_init(void) { int ret = 0; LM_INFO("benchmark: initializing\n"); bm_mycfg = (bm_cfg_t*)shm_malloc(sizeof(bm_cfg_t)); memset(bm_mycfg, 0, sizeof(bm_cfg_t)); bm_mycfg->enable_global = bm_enable_global; if (bm_granularity<0) { LM_ERR("benchmark granularity cannot be negative\n"); return -1; } bm_mycfg->granularity = bm_granularity; bm_mycfg->loglevel = bm_loglevel; return ret; } static int child_init(int rank) { LM_INFO("initing child...\n"); return 0; } /* * destroy * called by opensips at exit time */ static void destroy(void) { benchmark_timer_t *bmt = 0; benchmark_timer_t *bmp = 0; if(bm_mycfg!=NULL) { /* free timers list */ bmt = bm_mycfg->timers; while(bmt) { bmp = bmt; bmt = bmt->next; shm_free(bmp); } if(bm_mycfg->tindex) shm_free(bm_mycfg->tindex); shm_free(bm_mycfg); } } /* timer should be locked when calling this function */ static void soft_reset_timer(benchmark_timer_t *timer) { timer->calls = 0; timer->last_sum = 0; timer->last_max = 0; timer->last_min = STARTING_MIN_VALUE; } static void reset_timer(int i) { benchmark_timer_t *timer; if(bm_mycfg==NULL || (timer = bm_mycfg->tindex[i])==NULL) return; lock_get(timer->lock); timer->calls = 0; timer->sum = 0; timer->last_max = 0; timer->last_min = STARTING_MIN_VALUE; timer->last_sum = 0; timer->global_calls = 0; timer->global_max = 0; timer->global_min = STARTING_MIN_VALUE; lock_release(timer->lock); } /* * timer_active(). * Global enable mode can be: * -1 - All timing disabled * 0 - Timing enabled, watch for single timers enabled (default: off) * 1 - Timing enabled for all timers */ static inline int timer_active(unsigned int id) { if (bm_mycfg->enable_global > 0 || bm_mycfg->timers[id].enabled > 0) return 1; else return 0; } /* * start_timer() */ static int _bm_start_timer(unsigned int id) { if (timer_active(id)) { if(bm_get_time(bm_mycfg->tindex[id]->start)!=0) { LM_ERR("error getting current time\n"); return -1; } } return 1; } static int bm_start_timer(struct sip_msg* _msg, char* timer, char *foobar) { return _bm_start_timer((unsigned int)(unsigned long)timer); } /* * log_timer() */ static int _bm_log_timer(unsigned int id) { /* BM_CLOCK_REALTIME */ bm_timeval_t now; unsigned long long tdiff; benchmark_timer_t *timer; if (!timer_active(id)) return 1; if(bm_get_time(&now)<0) { LM_ERR("error getting current time\n"); return -1; } timer = bm_mycfg->tindex[id]; tdiff = bm_diff_time(timer->start, &now); _bm_last_time_diff = (int)tdiff; /* What to do * - update min, max, sum * - if granularity hit: Log, reset min/max */ lock_get(timer->lock); timer->sum += tdiff; timer->last_sum += tdiff; timer->calls++; timer->global_calls++; if (tdiff < timer->last_min) timer->last_min = tdiff; if (tdiff > timer->last_max) timer->last_max = tdiff; if (tdiff < timer->global_min) timer->global_min = tdiff; if (tdiff > timer->global_max) timer->global_max = tdiff; if (bm_mycfg->granularity > 0 && timer->calls >= bm_mycfg->granularity) { LM_GEN1(bm_mycfg->loglevel, "benchmark (timer %s [%d]): %llu [" " msgs/total/min/max/avg - LR:" " %i/%lld/%lld/%lld/%f | GB: %lld/%lld/%lld/%lld/%f]\n", timer->name, id, tdiff, timer->calls, timer->last_sum, timer->last_min, timer->last_max, ((double)timer->last_sum)/bm_mycfg->granularity, timer->global_calls, timer->sum, timer->global_min, timer->global_max, ((double)timer->sum)/timer->global_calls); soft_reset_timer(timer); } lock_release(timer->lock); return 1; } static int bm_log_timer(struct sip_msg* _msg, char* timer, char* mystr) { return _bm_log_timer((unsigned int)(unsigned long)timer); } static int _bm_register_timer(char *tname, int mode, unsigned int *id) { benchmark_timer_t *bmt = 0; benchmark_timer_t **tidx = 0; if(tname==NULL || id==NULL || bm_mycfg==NULL || strlen(tname)==0 || strlen(tname)>BM_NAME_LEN-1) return -1; bmt = bm_mycfg->timers; while(bmt) { if(strcmp(bmt->name, tname)==0) { *id = bmt->id; return 0; } bmt = bmt->next; } if(mode==0) return -1; bmt = (benchmark_timer_t*)shm_malloc(sizeof(benchmark_timer_t)); if(bmt==0) { LM_ERR("no more shm\n"); return -1; } memset(bmt, 0, sizeof(benchmark_timer_t)); bmt->lock = lock_alloc(); if(bmt->lock == NULL) { shm_free(bmt); LM_ERR("no more shm\n"); return -1; } if (!lock_init(bmt->lock)) { lock_dealloc(bmt->lock); shm_free(bmt); LM_ERR("failed to init lock\n"); return -1; } /* private memory, otherwise we have races */ bmt->start = (bm_timeval_t*)pkg_malloc(sizeof(bm_timeval_t)); if(bmt->start == NULL) { lock_dealloc(bmt->lock); shm_free(bmt); LM_ERR("no more pkg\n"); return -1; } memset(bmt->start, 0, sizeof(bm_timeval_t)); strcpy(bmt->name, tname); if(bm_mycfg->timers==0) { bmt->id = 0; bm_mycfg->timers = bmt; } else { bmt->id = bm_mycfg->timers->id+1; bmt->next = bm_mycfg->timers; bm_mycfg->timers = bmt; } /* do the indexing */ if(bmt->id%10==0) { if(bm_mycfg->tindex!=NULL) tidx = bm_mycfg->tindex; bm_mycfg->tindex = (benchmark_timer_t**)shm_malloc((10+bmt->id)* sizeof(benchmark_timer_t*)); if(bm_mycfg->tindex==0) { LM_ERR("no more share memory\n"); if(tidx!=0) shm_free(tidx); return -1; } memset(bm_mycfg->tindex, 0, (10+bmt->id)*sizeof(benchmark_timer_t*)); if(tidx!=0) { memcpy(bm_mycfg->tindex, tidx, bmt->id*sizeof(benchmark_timer_t*)); shm_free(tidx); } } bm_mycfg->tindex[bmt->id] = bmt; bm_mycfg->nrtimers = bmt->id + 1; reset_timer(bmt->id); *id = bmt->id; LM_DBG("timer [%s] added with index <%u>\n", bmt->name, bmt->id); return 0; } /* API Binding */ int load_bm( struct bm_binds *bmb) { if(bmb==NULL) return -1; bmb->bm_register = _bm_register_timer; bmb->bm_start = _bm_start_timer; bmb->bm_log = _bm_log_timer; return 1; } static inline char * pkg_strndup( char* _p, int _len) { char *s; s = (char*)pkg_malloc(_len+1); if (s==NULL) return NULL; memcpy(s,_p,_len); s[_len] = 0; return s; } /* MI functions */ /* * Expects 1 node: 0 for disable, 1 for enable */ static struct mi_root* mi_bm_enable_global(struct mi_root *cmd, void *param) { struct mi_node *node; char *p1, *e1; long int v1; node = cmd->node.kids; if ((node == NULL) || (node->next != NULL)) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); //p1 = strndup(node->value.s, node->value.len); p1 = pkg_strndup(node->value.s, node->value.len); v1 = strtol(p1, &e1, 0); if ((*e1 != '\0') || (*p1 == '\0')) { pkg_free(p1); return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } if ((v1 < -1) || (v1 > 1)) { pkg_free(p1); return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } bm_mycfg->enable_global = v1; pkg_free(p1); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } static struct mi_root* mi_bm_enable_timer(struct mi_root *cmd, void *param) { struct mi_node *node; char *p1, *p2, *e2; long int v2; unsigned int id; node = cmd->node.kids; if ((node == NULL) || (node->next == NULL) || (node->next->next != NULL)) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* replace to pkg stuff - or get rid of */ //p1 = strndup(node->value.s, node->value.len); p1 = pkg_strndup(node->value.s, node->value.len); if(_bm_register_timer(p1, 0, &id)!=0) { pkg_free(p1); return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } //p2 = strndup(node->next->value.s, node->next->value.len); p2 = pkg_strndup(node->next->value.s, node->next->value.len); v2 = strtol(p2, &e2, 0); pkg_free(p1); pkg_free(p2); if (*e2 != '\0' || *p2 == '\0') return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); if ((v2 < 0) || (v2 > 1)) return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); bm_mycfg->timers[id].enabled = v2; return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } static struct mi_root* mi_bm_granularity(struct mi_root *cmd, void *param) { struct mi_node *node; char *p1, *e1; long int v1; node = cmd->node.kids; if ((node == NULL) || (node->next != NULL)) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* replace to pkg stuff */ //p1 = strndup(node->value.s, node->value.len); p1 = pkg_strndup(node->value.s, node->value.len); v1 = strtol(p1, &e1, 0); pkg_free(p1); if ((*e1 != '\0') || (*p1 == '\0')) return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); if (v1 < 0) return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); bm_mycfg->granularity = v1; return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } static struct mi_root* mi_bm_loglevel(struct mi_root *cmd, void *param) { struct mi_node *node; char *p1, *e1; long int v1; node = cmd->node.kids; if ((node == NULL) || (node->next != NULL)) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* replace to pkg stuff */ //p1 = strndup(node->value.s, node->value.len); p1 = pkg_strndup(node->value.s, node->value.len); v1 = strtol(p1, &e1, 0); pkg_free(p1); if ((*e1 != '\0') || (*p1 == '\0')) return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); if ((v1 < -3) || (v1 > 4)) /* Maximum log levels */ return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); bm_mycfg->enable_global = v1; return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } static void add_results_node(struct mi_node *node, benchmark_timer_t *timer) { struct mi_node *timer_node; timer_node = addf_mi_node_child(node, 0, 0, 0, "%s", timer->name); addf_mi_node_child(timer_node, 0, 0, 0, "%i/%lld/%lld/%lld/%f", timer->calls, timer->last_sum, timer->last_min==STARTING_MIN_VALUE?0:timer->last_min, timer->last_max, timer->calls?((double)timer->last_sum)/timer->calls:0.); addf_mi_node_child(timer_node, 0, 0, 0, "%lld/%lld/%lld/%lld/%f", timer->global_calls, timer->sum, timer->global_min==STARTING_MIN_VALUE?0:timer->global_min, timer->global_max, timer->global_calls?((double)timer->sum)/timer->global_calls:0.); } static struct mi_root* mi_bm_poll_results(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; benchmark_timer_t *bmt; if (bm_mycfg->granularity!=0) return init_mi_tree( 400, MI_CALL_INVALID_S, MI_CALL_INVALID_LEN); rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) { LM_ERR("Could not allocate the reply mi tree"); return NULL; } rpl_tree->node.flags |= MI_IS_ARRAY; for(bmt = bm_mycfg->timers; bmt!=NULL; bmt=bmt->next) { lock_get(bmt->lock); add_results_node(&rpl_tree->node, bmt); soft_reset_timer(bmt); lock_release(bmt->lock); } return rpl_tree; } /* item functions */ static int bm_get_time_diff(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; return pv_get_sintval(msg, param, res, _bm_last_time_diff); } static inline int fixup_bm_timer(void** param, int param_no) { unsigned int tid = 0; if (param_no == 1) { if((_bm_register_timer((char*)(*param), 1, &tid))!=0) { LM_ERR("cannot register timer [%s]\n", (char*)(*param)); return E_UNSPEC; } pkg_free(*param); *param = (void*)(unsigned long)tid; } return 0; } /* End of file */ opensips-2.2.2/modules/benchmark/benchmark.h000066400000000000000000000047331300170765700210770ustar00rootroot00000000000000/* * Benchmarking module for OpenSIPS * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _BENCHMARK_MOD_H_ #define _BENCHMARK_MOD_H_ #include #include #include "benchmark_api.h" #define BM_NAME_LEN 32 #ifdef BM_CLOCK_REALTIME /* nano seconds */ typedef struct timespec bm_timeval_t; #else /* micro seconds */ typedef struct timeval bm_timeval_t; #endif typedef struct benchmark_timer { char name[BM_NAME_LEN]; unsigned int id; int enabled; bm_timeval_t *start; /* Current timer run */ int calls; /* Number of runs of this timer */ long long sum; /* Accumulated runtime of this timer */ long long last_sum; /* Accumulated runtime since last logging */ long long last_max; /* Minimum in current period (between granularity) */ long long last_min; /* Maximum ... */ long long global_calls; /* Number of runs of this timer, since start */ long long global_max; /* Global minimum, since start */ long long global_min; /* ... maximum ... */ gen_lock_t* lock; struct benchmark_timer *next; } benchmark_timer_t; static inline int bm_get_time(bm_timeval_t *t) { #ifdef BM_CLOCK_REALTIME if(clock_gettime(CLOCK_REALTIME, t)!=0) #else if(gettimeofday(t, NULL)) #endif { LM_ERR("error getting current time\n"); return -1; } return 0; } static inline unsigned long long bm_diff_time(bm_timeval_t *t1, bm_timeval_t *t2) { unsigned long long tdiff; tdiff = t2->tv_sec - t1->tv_sec; #ifdef BM_CLOCK_REALTIME tdiff = tdiff*1000000000 + t2->tv_nsec - t1->tv_nsec; #else tdiff = tdiff*1000000 + t2->tv_usec - t1->tv_usec; #endif return tdiff; } #endif /* _BENCHMARK_MOD_H_ */ opensips-2.2.2/modules/benchmark/benchmark_api.h000066400000000000000000000034511300170765700217240ustar00rootroot00000000000000/* * Benchmarking module for OpenSIPS * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef BENCHMARK_API_H #define BENCHMARK_API_H #include "../../sr_module.h" typedef int (*bm_register_timer_f)(char *tname, int mode, unsigned int *id); typedef int (*bm_start_timer_f)(unsigned int id); typedef int (*bm_log_timer_f)(unsigned int id); struct bm_binds { bm_register_timer_f bm_register; bm_start_timer_f bm_start; bm_log_timer_f bm_log; }; typedef int(*load_bm_f)(struct bm_binds *bmb); int load_bm(struct bm_binds *bmb); static inline int load_bm_api( struct bm_binds *bmb ) { load_bm_f load_bm; /* import the benchmark auto-loading function */ if ( !(load_bm=(load_bm_f)find_export("load_bm", 0, 0))) { LM_ERR("can't import load_bm\n"); return -1; } /* let the auto-loading function load all benchmarking stuff */ if (load_bm( bmb )==-1) { LM_ERR("load_bm failed\n"); return -1; } return 0; } #endif /* BENCHMARK_API_H */ opensips-2.2.2/modules/benchmark/doc/000077500000000000000000000000001300170765700175325ustar00rootroot00000000000000opensips-2.2.2/modules/benchmark/doc/benchmark.xml000066400000000000000000000026411300170765700222110ustar00rootroot00000000000000 %docentities; ]> Benchmark Module &osipsname; Bastian Friedrich Collax GmbH bastian.friedrich@collax.com Daniel-Constantin Mierla miconda@gmail.com Bastian Friedrich bastian.friedrich@collax.com 2007 Collax GmbH 2007 Voice System SRL $Revision: 5901 $ $Date$ &admin; &devel; &faq; opensips-2.2.2/modules/benchmark/doc/benchmark_admin.xml000066400000000000000000000223271300170765700233640ustar00rootroot00000000000000 &adminguide;
Overview This module helps developers to benchmark their module functions. By adding this module's functions via the configuration file or through its API, OpenSIPS can log profiling information for every function. The duration between calls to start_timer and log_timer is stored and logged via &osips;'s logging facility. Please note that all durations are given as microseconds (don't confuse with milliseconds!).
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>enable</varname> (int) Even when the module is loaded, benchmarking is not enabled per default. This variable may have three different values: -1 - Globally disable benchmarking 0 - Enable per-timer enabling. Single timers are inactive by default and can be activated through the MI interface as soon as that feature is implemented. 1 - Globally enable benchmarking Default value is 0. Set <varname>enable</varname> parameter ... modparam("benchmark", "enable", 1) ...
<varname>granularity</varname> (int) Logging normally is not done for every reference to the log_timer() function, but only every n'th call. n is defined through this variable. A sensible granularity seems to be 100. If granularity is set to 0, then nothing will be logged automatically. Instead bm_poll_results MI command can be used to retrieve the results and clean the local values. Default value is 100. Set <varname>granularity</varname> parameter ... modparam("benchmark", "granularity", 500) ...
<varname>loglevel</varname> (int) Set the log level for the benchmark logs. These levels should be used: -3 - L_ALERT -2 - L_CRIT -1 - L_ERR 1 - L_WARN 2 - L_NOTICE 3 - L_INFO 4 - L_DBG Default value is 3 (L_INFO). Set <varname>loglevel</varname> parameter ... modparam("benchmark", "loglevel", 4) ... This will set the logging level to L_DBG.
Exported Functions
<function moreinfo="none">bm_start_timer(name)</function> Start timer name. A later call to bm_log_timer() logs this timer.. <function>bm_start_timer</function> usage ... bm_start_timer("test"); ...
<function moreinfo="none">bm_log_timer(name)</function> This function logs the timer with the given ID. The following data are logged: Last msgs is the number of calls in the last logging interval. This equals the granularity variable. Last sum is the accumulated duration in the current logging interval (i.e. for the last granularity calls). Last min is the minimum duration between start/log_timer calls during the last interval. Last max - maximum duration. Last average is the average duration between bm_start_timer() and bm_log_timer() since the last logging. Global msgs number of calls to log_timer. Global sum total duration in microseconds. Global min... You get the point. :) Global max also obvious. Global avg possibly the most interesting value. <function>bm_log_timer</function> usage ... bm_log_timer("test"); ...
Exported pseudo-variables Exported pseudo-variables are listed in the next sections.
$BM_time_diff $BM_time_diff - the time difference elapsed between calls of bm_start_timer(name) and bm_log_timer(name). The value is 0 if no bm_log_timer() was called.
Exported MI Functions
<function moreinfo="none">bm_enable_global</function> Enables/disables the module. Parameter may be -1, 0 or 1. See discription of "enable" parameter.
<function moreinfo="none">bm_enable_timer</function> Enable or disable a single timer. The following example enables timer "test" (the second parameter must be 0 to disable): Enabling a timer ... opensipsctl fifo bm_enable_timer test 1 ...
<function moreinfo="none">bm_granularity</function> Modifies the benchmarking granularity. See "granularity" variable.
<function moreinfo="none">bm_loglevel</function> Modifies the module log level. See "loglevel" variable.
<function moreinfo="none">bm_poll_results</function> Returns the current and global results for each timer. This command is only available if the "granularity" variable is set to 0. It can be used to get results in stable time intervals instead of every N messages. Each timer will have 2 nodes - the local and the global values. Format of the values is the same as the one normally used in logfile. This way of getting the results allows to interface with external graphing applications like Munin. If there were no new calls to bm_log_timer since last check, then all current values of a timer will be equal 0. Each call to bm_poll_results will reset current values (but not global ones). Getting the results via FIFO interface ... opensipsctl fifo bm_poll_results register_timer 3/40/12/14/13.333333 9/204/12/97/22.666667 security_check_timer 3/21/7/7/7.000000 9/98/7/41/10.888889 ...
Example of usage Measure the duration of user location lookup. benchmark usage ... bm_start_timer("usrloc-lookup"); lookup("location"); bm_log_timer("usrloc-lookup"); ...
opensips-2.2.2/modules/benchmark/doc/benchmark_devel.xml000066400000000000000000000034751300170765700233760ustar00rootroot00000000000000 &develguide; The benchmark module provides an internal API to be used by other &osips; modules. The available functions are identical to the user exported functions. Please note that this module is intended mainly for developers. It should be used with caution in production environments.
Available Functions
<function moreinfo="none">bm_register(name, mode, id)</function> This function register a new timer and/or returns the internal ID associated with the timer. mode controls the creation of new timer if not found. id is to be used by start and log timer functions.
<function moreinfo="none">bm_start(id)</function> This function equals the user-exported function bm_start_timer. The id is passed as an integer, though.
<function moreinfo="none">bm_log(id)</function> This function equals the user-exported function bm_log_timer. The id is passed as an integer, though.
Benchmark API Example Using the benchmark module's API from another module ... #include "../benchmark/benchmark.h" ... struct bm_binds bmb; ... ... /* load the benchmarking API */ if (load_bm_api( &bmb )!=0) { LM_ERR("can't load benchmark API\n"); goto error; } ... ... /* Start/log timers during a (usually user-exported) module function */ bmb.bm_register("test", 1, &id) bmb.bm_start(id); do_something(); bmb.bm_log(id); ...
opensips-2.2.2/modules/cachedb_cassandra/000077500000000000000000000000001300170765700204235ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_cassandra/Cassandra.cpp000066400000000000000000012051611300170765700230340ustar00rootroot00000000000000/** * Autogenerated by Thrift * * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING */ #include "Cassandra.h" namespace org { namespace apache { namespace cassandra { uint32_t Cassandra_login_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_auth_request = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->auth_request.read(iprot); isset_auth_request = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_auth_request) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_login_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_login_args"); xfer += oprot->writeFieldBegin("auth_request", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->auth_request.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_login_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_login_pargs"); xfer += oprot->writeFieldBegin("auth_request", ::apache::thrift::protocol::T_STRUCT, 1); xfer += (*(this->auth_request)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_login_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->authnx.read(iprot); this->__isset.authnx = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->authzx.read(iprot); this->__isset.authzx = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_login_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_login_result"); if (this->__isset.authnx) { xfer += oprot->writeFieldBegin("authnx", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->authnx.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.authzx) { xfer += oprot->writeFieldBegin("authzx", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->authzx.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_login_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->authnx.read(iprot); this->__isset.authnx = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->authzx.read(iprot); this->__isset.authzx = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_set_keyspace_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_keyspace = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->keyspace); isset_keyspace = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_keyspace) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_set_keyspace_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_set_keyspace_args"); xfer += oprot->writeFieldBegin("keyspace", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->keyspace); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_set_keyspace_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_set_keyspace_pargs"); xfer += oprot->writeFieldBegin("keyspace", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString((*(this->keyspace))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_set_keyspace_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_set_keyspace_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_set_keyspace_result"); if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_set_keyspace_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_column_path = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_path.read(iprot); isset_column_path = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast131; xfer += iprot->readI32(ecast131); this->consistency_level = (ConsistencyLevel::type)ecast131; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column_path) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_get_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_args"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_path", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->column_path.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 3); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_pargs"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary((*(this->key))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_path", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->column_path)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 3); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->success.read(iprot); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->nfe.read(iprot); this->__isset.nfe = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRUCT, 0); xfer += this->success.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.nfe) { xfer += oprot->writeFieldBegin("nfe", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->nfe.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 4); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += (*(this->success)).read(iprot); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->nfe.read(iprot); this->__isset.nfe = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_slice_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_column_parent = false; bool isset_predicate = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_parent.read(iprot); isset_column_parent = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->predicate.read(iprot); isset_predicate = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast132; xfer += iprot->readI32(ecast132); this->consistency_level = (ConsistencyLevel::type)ecast132; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column_parent) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_predicate) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_get_slice_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_slice_args"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->column_parent.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->predicate.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_slice_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_slice_pargs"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary((*(this->key))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->column_parent)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += (*(this->predicate)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_slice_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->success.clear(); uint32_t _size133; ::apache::thrift::protocol::TType _etype136; iprot->readListBegin(_etype136, _size133); this->success.resize(_size133); uint32_t _i137; for (_i137 = 0; _i137 < _size133; ++_i137) { xfer += this->success[_i137].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_slice_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_slice_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_LIST, 0); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->success.size()); std::vector ::const_iterator _iter138; for (_iter138 = this->success.begin(); _iter138 != this->success.end(); ++_iter138) { xfer += (*_iter138).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_slice_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { (*(this->success)).clear(); uint32_t _size139; ::apache::thrift::protocol::TType _etype142; iprot->readListBegin(_etype142, _size139); (*(this->success)).resize(_size139); uint32_t _i143; for (_i143 = 0; _i143 < _size139; ++_i143) { xfer += (*(this->success))[_i143].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_count_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_column_parent = false; bool isset_predicate = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_parent.read(iprot); isset_column_parent = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->predicate.read(iprot); isset_predicate = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast144; xfer += iprot->readI32(ecast144); this->consistency_level = (ConsistencyLevel::type)ecast144; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column_parent) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_predicate) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_get_count_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_count_args"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->column_parent.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->predicate.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_count_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_count_pargs"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary((*(this->key))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->column_parent)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += (*(this->predicate)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_count_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_count_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_count_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_I32, 0); xfer += oprot->writeI32(this->success); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_count_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_multiget_slice_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_keys = false; bool isset_column_parent = false; bool isset_predicate = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->keys.clear(); uint32_t _size145; ::apache::thrift::protocol::TType _etype148; iprot->readListBegin(_etype148, _size145); this->keys.resize(_size145); uint32_t _i149; for (_i149 = 0; _i149 < _size145; ++_i149) { xfer += iprot->readBinary(this->keys[_i149]); } iprot->readListEnd(); } isset_keys = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_parent.read(iprot); isset_column_parent = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->predicate.read(iprot); isset_predicate = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast150; xfer += iprot->readI32(ecast150); this->consistency_level = (ConsistencyLevel::type)ecast150; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_keys) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column_parent) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_predicate) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_multiget_slice_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_multiget_slice_args"); xfer += oprot->writeFieldBegin("keys", ::apache::thrift::protocol::T_LIST, 1); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, this->keys.size()); std::vector ::const_iterator _iter151; for (_iter151 = this->keys.begin(); _iter151 != this->keys.end(); ++_iter151) { xfer += oprot->writeBinary((*_iter151)); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->column_parent.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->predicate.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_multiget_slice_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_multiget_slice_pargs"); xfer += oprot->writeFieldBegin("keys", ::apache::thrift::protocol::T_LIST, 1); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, (*(this->keys)).size()); std::vector ::const_iterator _iter152; for (_iter152 = (*(this->keys)).begin(); _iter152 != (*(this->keys)).end(); ++_iter152) { xfer += oprot->writeBinary((*_iter152)); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->column_parent)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += (*(this->predicate)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_multiget_slice_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->success.clear(); uint32_t _size153; ::apache::thrift::protocol::TType _ktype154; ::apache::thrift::protocol::TType _vtype155; iprot->readMapBegin(_ktype154, _vtype155, _size153); uint32_t _i157; for (_i157 = 0; _i157 < _size153; ++_i157) { std::string _key158; xfer += iprot->readBinary(_key158); std::vector & _val159 = this->success[_key158]; { _val159.clear(); uint32_t _size160; ::apache::thrift::protocol::TType _etype163; iprot->readListBegin(_etype163, _size160); _val159.resize(_size160); uint32_t _i164; for (_i164 = 0; _i164 < _size160; ++_i164) { xfer += _val159[_i164].read(iprot); } iprot->readListEnd(); } } iprot->readMapEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_multiget_slice_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_multiget_slice_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_MAP, 0); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_LIST, this->success.size()); std::map > ::const_iterator _iter165; for (_iter165 = this->success.begin(); _iter165 != this->success.end(); ++_iter165) { xfer += oprot->writeBinary(_iter165->first); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, _iter165->second.size()); std::vector ::const_iterator _iter166; for (_iter166 = _iter165->second.begin(); _iter166 != _iter165->second.end(); ++_iter166) { xfer += (*_iter166).write(oprot); } xfer += oprot->writeListEnd(); } } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_multiget_slice_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_MAP) { { (*(this->success)).clear(); uint32_t _size167; ::apache::thrift::protocol::TType _ktype168; ::apache::thrift::protocol::TType _vtype169; iprot->readMapBegin(_ktype168, _vtype169, _size167); uint32_t _i171; for (_i171 = 0; _i171 < _size167; ++_i171) { std::string _key172; xfer += iprot->readBinary(_key172); std::vector & _val173 = (*(this->success))[_key172]; { _val173.clear(); uint32_t _size174; ::apache::thrift::protocol::TType _etype177; iprot->readListBegin(_etype177, _size174); _val173.resize(_size174); uint32_t _i178; for (_i178 = 0; _i178 < _size174; ++_i178) { xfer += _val173[_i178].read(iprot); } iprot->readListEnd(); } } iprot->readMapEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_multiget_count_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_keys = false; bool isset_column_parent = false; bool isset_predicate = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->keys.clear(); uint32_t _size179; ::apache::thrift::protocol::TType _etype182; iprot->readListBegin(_etype182, _size179); this->keys.resize(_size179); uint32_t _i183; for (_i183 = 0; _i183 < _size179; ++_i183) { xfer += iprot->readBinary(this->keys[_i183]); } iprot->readListEnd(); } isset_keys = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_parent.read(iprot); isset_column_parent = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->predicate.read(iprot); isset_predicate = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast184; xfer += iprot->readI32(ecast184); this->consistency_level = (ConsistencyLevel::type)ecast184; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_keys) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column_parent) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_predicate) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_multiget_count_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_multiget_count_args"); xfer += oprot->writeFieldBegin("keys", ::apache::thrift::protocol::T_LIST, 1); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, this->keys.size()); std::vector ::const_iterator _iter185; for (_iter185 = this->keys.begin(); _iter185 != this->keys.end(); ++_iter185) { xfer += oprot->writeBinary((*_iter185)); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->column_parent.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->predicate.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_multiget_count_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_multiget_count_pargs"); xfer += oprot->writeFieldBegin("keys", ::apache::thrift::protocol::T_LIST, 1); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, (*(this->keys)).size()); std::vector ::const_iterator _iter186; for (_iter186 = (*(this->keys)).begin(); _iter186 != (*(this->keys)).end(); ++_iter186) { xfer += oprot->writeBinary((*_iter186)); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->column_parent)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += (*(this->predicate)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_multiget_count_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->success.clear(); uint32_t _size187; ::apache::thrift::protocol::TType _ktype188; ::apache::thrift::protocol::TType _vtype189; iprot->readMapBegin(_ktype188, _vtype189, _size187); uint32_t _i191; for (_i191 = 0; _i191 < _size187; ++_i191) { std::string _key192; xfer += iprot->readBinary(_key192); int32_t& _val193 = this->success[_key192]; xfer += iprot->readI32(_val193); } iprot->readMapEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_multiget_count_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_multiget_count_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_MAP, 0); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_I32, this->success.size()); std::map ::const_iterator _iter194; for (_iter194 = this->success.begin(); _iter194 != this->success.end(); ++_iter194) { xfer += oprot->writeBinary(_iter194->first); xfer += oprot->writeI32(_iter194->second); } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_multiget_count_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_MAP) { { (*(this->success)).clear(); uint32_t _size195; ::apache::thrift::protocol::TType _ktype196; ::apache::thrift::protocol::TType _vtype197; iprot->readMapBegin(_ktype196, _vtype197, _size195); uint32_t _i199; for (_i199 = 0; _i199 < _size195; ++_i199) { std::string _key200; xfer += iprot->readBinary(_key200); int32_t& _val201 = (*(this->success))[_key200]; xfer += iprot->readI32(_val201); } iprot->readMapEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_range_slices_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_column_parent = false; bool isset_predicate = false; bool isset_range = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_parent.read(iprot); isset_column_parent = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->predicate.read(iprot); isset_predicate = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->range.read(iprot); isset_range = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast202; xfer += iprot->readI32(ecast202); this->consistency_level = (ConsistencyLevel::type)ecast202; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_column_parent) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_predicate) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_range) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_get_range_slices_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_range_slices_args"); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->column_parent.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->predicate.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("range", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->range.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_range_slices_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_range_slices_pargs"); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 1); xfer += (*(this->column_parent)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->predicate)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("range", ::apache::thrift::protocol::T_STRUCT, 3); xfer += (*(this->range)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_range_slices_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->success.clear(); uint32_t _size203; ::apache::thrift::protocol::TType _etype206; iprot->readListBegin(_etype206, _size203); this->success.resize(_size203); uint32_t _i207; for (_i207 = 0; _i207 < _size203; ++_i207) { xfer += this->success[_i207].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_range_slices_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_range_slices_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_LIST, 0); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->success.size()); std::vector ::const_iterator _iter208; for (_iter208 = this->success.begin(); _iter208 != this->success.end(); ++_iter208) { xfer += (*_iter208).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_range_slices_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { (*(this->success)).clear(); uint32_t _size209; ::apache::thrift::protocol::TType _etype212; iprot->readListBegin(_etype212, _size209); (*(this->success)).resize(_size209); uint32_t _i213; for (_i213 = 0; _i213 < _size209; ++_i213) { xfer += (*(this->success))[_i213].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_indexed_slices_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_column_parent = false; bool isset_index_clause = false; bool isset_column_predicate = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_parent.read(iprot); isset_column_parent = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->index_clause.read(iprot); isset_index_clause = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_predicate.read(iprot); isset_column_predicate = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast214; xfer += iprot->readI32(ecast214); this->consistency_level = (ConsistencyLevel::type)ecast214; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_column_parent) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_index_clause) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column_predicate) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_get_indexed_slices_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_indexed_slices_args"); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->column_parent.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("index_clause", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->index_clause.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->column_predicate.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_indexed_slices_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_indexed_slices_pargs"); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 1); xfer += (*(this->column_parent)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("index_clause", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->index_clause)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += (*(this->column_predicate)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_indexed_slices_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->success.clear(); uint32_t _size215; ::apache::thrift::protocol::TType _etype218; iprot->readListBegin(_etype218, _size215); this->success.resize(_size215); uint32_t _i219; for (_i219 = 0; _i219 < _size215; ++_i219) { xfer += this->success[_i219].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_get_indexed_slices_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_get_indexed_slices_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_LIST, 0); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->success.size()); std::vector ::const_iterator _iter220; for (_iter220 = this->success.begin(); _iter220 != this->success.end(); ++_iter220) { xfer += (*_iter220).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_get_indexed_slices_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { (*(this->success)).clear(); uint32_t _size221; ::apache::thrift::protocol::TType _etype224; iprot->readListBegin(_etype224, _size221); (*(this->success)).resize(_size221); uint32_t _i225; for (_i225 = 0; _i225 < _size221; ++_i225) { xfer += (*(this->success))[_i225].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_insert_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_column_parent = false; bool isset_column = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_parent.read(iprot); isset_column_parent = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column.read(iprot); isset_column = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast226; xfer += iprot->readI32(ecast226); this->consistency_level = (ConsistencyLevel::type)ecast226; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column_parent) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_insert_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_insert_args"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->column_parent.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->column.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_insert_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_insert_pargs"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary((*(this->key))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->column_parent)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column", ::apache::thrift::protocol::T_STRUCT, 3); xfer += (*(this->column)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_insert_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_insert_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_insert_result"); if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_insert_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_add_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_column_parent = false; bool isset_column = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_parent.read(iprot); isset_column_parent = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column.read(iprot); isset_column = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast227; xfer += iprot->readI32(ecast227); this->consistency_level = (ConsistencyLevel::type)ecast227; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column_parent) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_add_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_add_args"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->column_parent.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->column.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_add_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_add_pargs"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary((*(this->key))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_parent", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->column_parent)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column", ::apache::thrift::protocol::T_STRUCT, 3); xfer += (*(this->column)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_add_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_add_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_add_result"); if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_add_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_remove_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_column_path = false; bool isset_timestamp = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_path.read(iprot); isset_column_path = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_I64) { xfer += iprot->readI64(this->timestamp); isset_timestamp = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast228; xfer += iprot->readI32(ecast228); this->consistency_level = (ConsistencyLevel::type)ecast228; this->__isset.consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_column_path) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_timestamp) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_remove_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_remove_args"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_path", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->column_path.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("timestamp", ::apache::thrift::protocol::T_I64, 3); xfer += oprot->writeI64(this->timestamp); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_remove_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_remove_pargs"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary((*(this->key))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("column_path", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->column_path)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("timestamp", ::apache::thrift::protocol::T_I64, 3); xfer += oprot->writeI64((*(this->timestamp))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_remove_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_remove_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_remove_result"); if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_remove_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_remove_counter_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_path = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->path.read(iprot); isset_path = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast229; xfer += iprot->readI32(ecast229); this->consistency_level = (ConsistencyLevel::type)ecast229; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_path) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_remove_counter_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_remove_counter_args"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("path", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->path.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 3); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_remove_counter_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_remove_counter_pargs"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary((*(this->key))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("path", ::apache::thrift::protocol::T_STRUCT, 2); xfer += (*(this->path)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 3); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_remove_counter_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_remove_counter_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_remove_counter_result"); if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_remove_counter_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_batch_mutate_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_mutation_map = false; bool isset_consistency_level = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->mutation_map.clear(); uint32_t _size230; ::apache::thrift::protocol::TType _ktype231; ::apache::thrift::protocol::TType _vtype232; iprot->readMapBegin(_ktype231, _vtype232, _size230); uint32_t _i234; for (_i234 = 0; _i234 < _size230; ++_i234) { std::string _key235; xfer += iprot->readBinary(_key235); std::map > & _val236 = this->mutation_map[_key235]; { _val236.clear(); uint32_t _size237; ::apache::thrift::protocol::TType _ktype238; ::apache::thrift::protocol::TType _vtype239; iprot->readMapBegin(_ktype238, _vtype239, _size237); uint32_t _i241; for (_i241 = 0; _i241 < _size237; ++_i241) { std::string _key242; xfer += iprot->readString(_key242); std::vector & _val243 = _val236[_key242]; { _val243.clear(); uint32_t _size244; ::apache::thrift::protocol::TType _etype247; iprot->readListBegin(_etype247, _size244); _val243.resize(_size244); uint32_t _i248; for (_i248 = 0; _i248 < _size244; ++_i248) { xfer += _val243[_i248].read(iprot); } iprot->readListEnd(); } } iprot->readMapEnd(); } } iprot->readMapEnd(); } isset_mutation_map = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast249; xfer += iprot->readI32(ecast249); this->consistency_level = (ConsistencyLevel::type)ecast249; isset_consistency_level = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_mutation_map) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_consistency_level) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_batch_mutate_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_batch_mutate_args"); xfer += oprot->writeFieldBegin("mutation_map", ::apache::thrift::protocol::T_MAP, 1); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_MAP, this->mutation_map.size()); std::map > > ::const_iterator _iter250; for (_iter250 = this->mutation_map.begin(); _iter250 != this->mutation_map.end(); ++_iter250) { xfer += oprot->writeBinary(_iter250->first); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_LIST, _iter250->second.size()); std::map > ::const_iterator _iter251; for (_iter251 = _iter250->second.begin(); _iter251 != _iter250->second.end(); ++_iter251) { xfer += oprot->writeString(_iter251->first); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, _iter251->second.size()); std::vector ::const_iterator _iter252; for (_iter252 = _iter251->second.begin(); _iter252 != _iter251->second.end(); ++_iter252) { xfer += (*_iter252).write(oprot); } xfer += oprot->writeListEnd(); } } xfer += oprot->writeMapEnd(); } } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 2); xfer += oprot->writeI32((int32_t)this->consistency_level); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_batch_mutate_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_batch_mutate_pargs"); xfer += oprot->writeFieldBegin("mutation_map", ::apache::thrift::protocol::T_MAP, 1); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_MAP, (*(this->mutation_map)).size()); std::map > > ::const_iterator _iter253; for (_iter253 = (*(this->mutation_map)).begin(); _iter253 != (*(this->mutation_map)).end(); ++_iter253) { xfer += oprot->writeBinary(_iter253->first); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_LIST, _iter253->second.size()); std::map > ::const_iterator _iter254; for (_iter254 = _iter253->second.begin(); _iter254 != _iter253->second.end(); ++_iter254) { xfer += oprot->writeString(_iter254->first); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, _iter254->second.size()); std::vector ::const_iterator _iter255; for (_iter255 = _iter254->second.begin(); _iter255 != _iter254->second.end(); ++_iter255) { xfer += (*_iter255).write(oprot); } xfer += oprot->writeListEnd(); } } xfer += oprot->writeMapEnd(); } } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("consistency_level", ::apache::thrift::protocol::T_I32, 2); xfer += oprot->writeI32((int32_t)(*(this->consistency_level))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_batch_mutate_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_batch_mutate_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_batch_mutate_result"); if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_batch_mutate_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_truncate_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_cfname = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->cfname); isset_cfname = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_cfname) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_truncate_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_truncate_args"); xfer += oprot->writeFieldBegin("cfname", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->cfname); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_truncate_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_truncate_pargs"); xfer += oprot->writeFieldBegin("cfname", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString((*(this->cfname))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_truncate_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_truncate_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_truncate_result"); if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_truncate_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_schema_versions_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_schema_versions_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_schema_versions_args"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_schema_versions_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_schema_versions_pargs"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_schema_versions_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->success.clear(); uint32_t _size256; ::apache::thrift::protocol::TType _ktype257; ::apache::thrift::protocol::TType _vtype258; iprot->readMapBegin(_ktype257, _vtype258, _size256); uint32_t _i260; for (_i260 = 0; _i260 < _size256; ++_i260) { std::string _key261; xfer += iprot->readString(_key261); std::vector & _val262 = this->success[_key261]; { _val262.clear(); uint32_t _size263; ::apache::thrift::protocol::TType _etype266; iprot->readListBegin(_etype266, _size263); _val262.resize(_size263); uint32_t _i267; for (_i267 = 0; _i267 < _size263; ++_i267) { xfer += iprot->readString(_val262[_i267]); } iprot->readListEnd(); } } iprot->readMapEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_schema_versions_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_schema_versions_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_MAP, 0); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_LIST, this->success.size()); std::map > ::const_iterator _iter268; for (_iter268 = this->success.begin(); _iter268 != this->success.end(); ++_iter268) { xfer += oprot->writeString(_iter268->first); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, _iter268->second.size()); std::vector ::const_iterator _iter269; for (_iter269 = _iter268->second.begin(); _iter269 != _iter268->second.end(); ++_iter269) { xfer += oprot->writeString((*_iter269)); } xfer += oprot->writeListEnd(); } } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_schema_versions_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_MAP) { { (*(this->success)).clear(); uint32_t _size270; ::apache::thrift::protocol::TType _ktype271; ::apache::thrift::protocol::TType _vtype272; iprot->readMapBegin(_ktype271, _vtype272, _size270); uint32_t _i274; for (_i274 = 0; _i274 < _size270; ++_i274) { std::string _key275; xfer += iprot->readString(_key275); std::vector & _val276 = (*(this->success))[_key275]; { _val276.clear(); uint32_t _size277; ::apache::thrift::protocol::TType _etype280; iprot->readListBegin(_etype280, _size277); _val276.resize(_size277); uint32_t _i281; for (_i281 = 0; _i281 < _size277; ++_i281) { xfer += iprot->readString(_val276[_i281]); } iprot->readListEnd(); } } iprot->readMapEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspaces_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspaces_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_keyspaces_args"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspaces_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_keyspaces_pargs"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspaces_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->success.clear(); uint32_t _size282; ::apache::thrift::protocol::TType _etype285; iprot->readListBegin(_etype285, _size282); this->success.resize(_size282); uint32_t _i286; for (_i286 = 0; _i286 < _size282; ++_i286) { xfer += this->success[_i286].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspaces_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_keyspaces_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_LIST, 0); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->success.size()); std::vector ::const_iterator _iter287; for (_iter287 = this->success.begin(); _iter287 != this->success.end(); ++_iter287) { xfer += (*_iter287).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspaces_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { (*(this->success)).clear(); uint32_t _size288; ::apache::thrift::protocol::TType _etype291; iprot->readListBegin(_etype291, _size288); (*(this->success)).resize(_size288); uint32_t _i292; for (_i292 = 0; _i292 < _size288; ++_i292) { xfer += (*(this->success))[_i292].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_cluster_name_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_cluster_name_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_cluster_name_args"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_cluster_name_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_cluster_name_pargs"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_cluster_name_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_cluster_name_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_cluster_name_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_cluster_name_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_version_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_version_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_version_args"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_version_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_version_pargs"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_version_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_version_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_version_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_version_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_ring_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_keyspace = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->keyspace); isset_keyspace = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_keyspace) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_describe_ring_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_ring_args"); xfer += oprot->writeFieldBegin("keyspace", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->keyspace); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_ring_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_ring_pargs"); xfer += oprot->writeFieldBegin("keyspace", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString((*(this->keyspace))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_ring_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->success.clear(); uint32_t _size293; ::apache::thrift::protocol::TType _etype296; iprot->readListBegin(_etype296, _size293); this->success.resize(_size293); uint32_t _i297; for (_i297 = 0; _i297 < _size293; ++_i297) { xfer += this->success[_i297].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_ring_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_ring_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_LIST, 0); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->success.size()); std::vector ::const_iterator _iter298; for (_iter298 = this->success.begin(); _iter298 != this->success.end(); ++_iter298) { xfer += (*_iter298).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_ring_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { (*(this->success)).clear(); uint32_t _size299; ::apache::thrift::protocol::TType _etype302; iprot->readListBegin(_etype302, _size299); (*(this->success)).resize(_size299); uint32_t _i303; for (_i303 = 0; _i303 < _size299; ++_i303) { xfer += (*(this->success))[_i303].read(iprot); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_partitioner_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_partitioner_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_partitioner_args"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_partitioner_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_partitioner_pargs"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_partitioner_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_partitioner_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_partitioner_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_partitioner_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_snitch_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_snitch_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_snitch_args"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_snitch_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_snitch_pargs"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_snitch_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_snitch_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_snitch_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_snitch_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspace_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_keyspace = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->keyspace); isset_keyspace = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_keyspace) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_describe_keyspace_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_keyspace_args"); xfer += oprot->writeFieldBegin("keyspace", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->keyspace); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspace_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_keyspace_pargs"); xfer += oprot->writeFieldBegin("keyspace", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString((*(this->keyspace))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspace_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->success.read(iprot); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->nfe.read(iprot); this->__isset.nfe = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspace_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_keyspace_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRUCT, 0); xfer += this->success.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.nfe) { xfer += oprot->writeFieldBegin("nfe", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->nfe.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_keyspace_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += (*(this->success)).read(iprot); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->nfe.read(iprot); this->__isset.nfe = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_splits_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_cfName = false; bool isset_start_token = false; bool isset_end_token = false; bool isset_keys_per_split = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->cfName); isset_cfName = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->start_token); isset_start_token = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->end_token); isset_end_token = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->keys_per_split); isset_keys_per_split = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_cfName) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_start_token) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_end_token) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_keys_per_split) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_describe_splits_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_splits_args"); xfer += oprot->writeFieldBegin("cfName", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->cfName); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("start_token", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeString(this->start_token); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("end_token", ::apache::thrift::protocol::T_STRING, 3); xfer += oprot->writeString(this->end_token); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("keys_per_split", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32(this->keys_per_split); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_splits_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_splits_pargs"); xfer += oprot->writeFieldBegin("cfName", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString((*(this->cfName))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("start_token", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeString((*(this->start_token))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("end_token", ::apache::thrift::protocol::T_STRING, 3); xfer += oprot->writeString((*(this->end_token))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("keys_per_split", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32((*(this->keys_per_split))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_splits_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->success.clear(); uint32_t _size304; ::apache::thrift::protocol::TType _etype307; iprot->readListBegin(_etype307, _size304); this->success.resize(_size304); uint32_t _i308; for (_i308 = 0; _i308 < _size304; ++_i308) { xfer += iprot->readString(this->success[_i308]); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_describe_splits_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_describe_splits_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_LIST, 0); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, this->success.size()); std::vector ::const_iterator _iter309; for (_iter309 = this->success.begin(); _iter309 != this->success.end(); ++_iter309) { xfer += oprot->writeString((*_iter309)); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_describe_splits_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_LIST) { { (*(this->success)).clear(); uint32_t _size310; ::apache::thrift::protocol::TType _etype313; iprot->readListBegin(_etype313, _size310); (*(this->success)).resize(_size310); uint32_t _i314; for (_i314 = 0; _i314 < _size310; ++_i314) { xfer += iprot->readString((*(this->success))[_i314]); } iprot->readListEnd(); } this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_add_column_family_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_cf_def = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->cf_def.read(iprot); isset_cf_def = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_cf_def) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_system_add_column_family_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_add_column_family_args"); xfer += oprot->writeFieldBegin("cf_def", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->cf_def.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_add_column_family_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_add_column_family_pargs"); xfer += oprot->writeFieldBegin("cf_def", ::apache::thrift::protocol::T_STRUCT, 1); xfer += (*(this->cf_def)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_add_column_family_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_add_column_family_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_add_column_family_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.sde) { xfer += oprot->writeFieldBegin("sde", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->sde.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_add_column_family_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_drop_column_family_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_column_family = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->column_family); isset_column_family = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_column_family) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_system_drop_column_family_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_drop_column_family_args"); xfer += oprot->writeFieldBegin("column_family", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->column_family); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_drop_column_family_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_drop_column_family_pargs"); xfer += oprot->writeFieldBegin("column_family", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString((*(this->column_family))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_drop_column_family_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_drop_column_family_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_drop_column_family_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.sde) { xfer += oprot->writeFieldBegin("sde", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->sde.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_drop_column_family_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_add_keyspace_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_ks_def = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ks_def.read(iprot); isset_ks_def = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_ks_def) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_system_add_keyspace_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_add_keyspace_args"); xfer += oprot->writeFieldBegin("ks_def", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ks_def.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_add_keyspace_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_add_keyspace_pargs"); xfer += oprot->writeFieldBegin("ks_def", ::apache::thrift::protocol::T_STRUCT, 1); xfer += (*(this->ks_def)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_add_keyspace_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_add_keyspace_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_add_keyspace_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.sde) { xfer += oprot->writeFieldBegin("sde", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->sde.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_add_keyspace_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_drop_keyspace_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_keyspace = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->keyspace); isset_keyspace = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_keyspace) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_system_drop_keyspace_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_drop_keyspace_args"); xfer += oprot->writeFieldBegin("keyspace", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->keyspace); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_drop_keyspace_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_drop_keyspace_pargs"); xfer += oprot->writeFieldBegin("keyspace", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString((*(this->keyspace))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_drop_keyspace_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_drop_keyspace_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_drop_keyspace_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.sde) { xfer += oprot->writeFieldBegin("sde", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->sde.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_drop_keyspace_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_update_keyspace_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_ks_def = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ks_def.read(iprot); isset_ks_def = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_ks_def) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_system_update_keyspace_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_update_keyspace_args"); xfer += oprot->writeFieldBegin("ks_def", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ks_def.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_update_keyspace_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_update_keyspace_pargs"); xfer += oprot->writeFieldBegin("ks_def", ::apache::thrift::protocol::T_STRUCT, 1); xfer += (*(this->ks_def)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_update_keyspace_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_update_keyspace_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_update_keyspace_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.sde) { xfer += oprot->writeFieldBegin("sde", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->sde.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_update_keyspace_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_update_column_family_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_cf_def = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->cf_def.read(iprot); isset_cf_def = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_cf_def) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_system_update_column_family_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_update_column_family_args"); xfer += oprot->writeFieldBegin("cf_def", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->cf_def.write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_update_column_family_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_update_column_family_pargs"); xfer += oprot->writeFieldBegin("cf_def", ::apache::thrift::protocol::T_STRUCT, 1); xfer += (*(this->cf_def)).write(oprot); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_update_column_family_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->success); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_system_update_column_family_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_system_update_column_family_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRING, 0); xfer += oprot->writeString(this->success); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.sde) { xfer += oprot->writeFieldBegin("sde", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->sde.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_system_update_column_family_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString((*(this->success))); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_execute_cql_query_args::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_query = false; bool isset_compression = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->query); isset_query = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast315; xfer += iprot->readI32(ecast315); this->compression = (Compression::type)ecast315; isset_compression = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_query) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_compression) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Cassandra_execute_cql_query_args::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_execute_cql_query_args"); xfer += oprot->writeFieldBegin("query", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->query); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("compression", ::apache::thrift::protocol::T_I32, 2); xfer += oprot->writeI32((int32_t)this->compression); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_execute_cql_query_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_execute_cql_query_pargs"); xfer += oprot->writeFieldBegin("query", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary((*(this->query))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("compression", ::apache::thrift::protocol::T_I32, 2); xfer += oprot->writeI32((int32_t)(*(this->compression))); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_execute_cql_query_result::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->success.read(iprot); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Cassandra_execute_cql_query_result::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Cassandra_execute_cql_query_result"); if (this->__isset.success) { xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_STRUCT, 0); xfer += this->success.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ire) { xfer += oprot->writeFieldBegin("ire", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->ire.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.ue) { xfer += oprot->writeFieldBegin("ue", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->ue.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.te) { xfer += oprot->writeFieldBegin("te", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->te.write(oprot); xfer += oprot->writeFieldEnd(); } else if (this->__isset.sde) { xfer += oprot->writeFieldBegin("sde", ::apache::thrift::protocol::T_STRUCT, 4); xfer += this->sde.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } uint32_t Cassandra_execute_cql_query_presult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 0: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += (*(this->success)).read(iprot); this->__isset.success = true; } else { xfer += iprot->skip(ftype); } break; case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ire.read(iprot); this->__isset.ire = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->ue.read(iprot); this->__isset.ue = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->te.read(iprot); this->__isset.te = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->sde.read(iprot); this->__isset.sde = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } void CassandraClient::login(const AuthenticationRequest& auth_request) { send_login(auth_request); recv_login(); } void CassandraClient::send_login(const AuthenticationRequest& auth_request) { int32_t cseqid = 0; oprot_->writeMessageBegin("login", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_login_pargs args; args.auth_request = &auth_request; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_login() { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("login") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_login_presult result; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.authnx) { throw result.authnx; } if (result.__isset.authzx) { throw result.authzx; } return; } void CassandraClient::set_keyspace(const std::string& keyspace) { send_set_keyspace(keyspace); recv_set_keyspace(); } void CassandraClient::send_set_keyspace(const std::string& keyspace) { int32_t cseqid = 0; oprot_->writeMessageBegin("set_keyspace", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_set_keyspace_pargs args; args.keyspace = &keyspace; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_set_keyspace() { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("set_keyspace") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_set_keyspace_presult result; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.ire) { throw result.ire; } return; } void CassandraClient::get(ColumnOrSuperColumn& _return, const std::string& key, const ColumnPath& column_path, const ConsistencyLevel::type consistency_level) { send_get(key, column_path, consistency_level); recv_get(_return); } void CassandraClient::send_get(const std::string& key, const ColumnPath& column_path, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("get", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_get_pargs args; args.key = &key; args.column_path = &column_path; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_get(ColumnOrSuperColumn& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("get") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_get_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.nfe) { throw result.nfe; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "get failed: unknown result"); } void CassandraClient::get_slice(std::vector & _return, const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { send_get_slice(key, column_parent, predicate, consistency_level); recv_get_slice(_return); } void CassandraClient::send_get_slice(const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("get_slice", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_get_slice_pargs args; args.key = &key; args.column_parent = &column_parent; args.predicate = &predicate; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_get_slice(std::vector & _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("get_slice") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_get_slice_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "get_slice failed: unknown result"); } int32_t CassandraClient::get_count(const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { send_get_count(key, column_parent, predicate, consistency_level); return recv_get_count(); } void CassandraClient::send_get_count(const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("get_count", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_get_count_pargs args; args.key = &key; args.column_parent = &column_parent; args.predicate = &predicate; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } int32_t CassandraClient::recv_get_count() { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("get_count") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } int32_t _return; Cassandra_get_count_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { return _return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "get_count failed: unknown result"); } void CassandraClient::multiget_slice(std::map > & _return, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { send_multiget_slice(keys, column_parent, predicate, consistency_level); recv_multiget_slice(_return); } void CassandraClient::send_multiget_slice(const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("multiget_slice", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_multiget_slice_pargs args; args.keys = &keys; args.column_parent = &column_parent; args.predicate = &predicate; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_multiget_slice(std::map > & _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("multiget_slice") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_multiget_slice_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "multiget_slice failed: unknown result"); } void CassandraClient::multiget_count(std::map & _return, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { send_multiget_count(keys, column_parent, predicate, consistency_level); recv_multiget_count(_return); } void CassandraClient::send_multiget_count(const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("multiget_count", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_multiget_count_pargs args; args.keys = &keys; args.column_parent = &column_parent; args.predicate = &predicate; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_multiget_count(std::map & _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("multiget_count") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_multiget_count_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "multiget_count failed: unknown result"); } void CassandraClient::get_range_slices(std::vector & _return, const ColumnParent& column_parent, const SlicePredicate& predicate, const KeyRange& range, const ConsistencyLevel::type consistency_level) { send_get_range_slices(column_parent, predicate, range, consistency_level); recv_get_range_slices(_return); } void CassandraClient::send_get_range_slices(const ColumnParent& column_parent, const SlicePredicate& predicate, const KeyRange& range, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("get_range_slices", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_get_range_slices_pargs args; args.column_parent = &column_parent; args.predicate = &predicate; args.range = ⦥ args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_get_range_slices(std::vector & _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("get_range_slices") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_get_range_slices_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "get_range_slices failed: unknown result"); } void CassandraClient::get_indexed_slices(std::vector & _return, const ColumnParent& column_parent, const IndexClause& index_clause, const SlicePredicate& column_predicate, const ConsistencyLevel::type consistency_level) { send_get_indexed_slices(column_parent, index_clause, column_predicate, consistency_level); recv_get_indexed_slices(_return); } void CassandraClient::send_get_indexed_slices(const ColumnParent& column_parent, const IndexClause& index_clause, const SlicePredicate& column_predicate, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("get_indexed_slices", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_get_indexed_slices_pargs args; args.column_parent = &column_parent; args.index_clause = &index_clause; args.column_predicate = &column_predicate; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_get_indexed_slices(std::vector & _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("get_indexed_slices") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_get_indexed_slices_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "get_indexed_slices failed: unknown result"); } void CassandraClient::insert(const std::string& key, const ColumnParent& column_parent, const Column& column, const ConsistencyLevel::type consistency_level) { send_insert(key, column_parent, column, consistency_level); recv_insert(); } void CassandraClient::send_insert(const std::string& key, const ColumnParent& column_parent, const Column& column, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("insert", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_insert_pargs args; args.key = &key; args.column_parent = &column_parent; args.column = &column; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_insert() { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("insert") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_insert_presult result; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } return; } void CassandraClient::add(const std::string& key, const ColumnParent& column_parent, const CounterColumn& column, const ConsistencyLevel::type consistency_level) { send_add(key, column_parent, column, consistency_level); recv_add(); } void CassandraClient::send_add(const std::string& key, const ColumnParent& column_parent, const CounterColumn& column, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("add", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_add_pargs args; args.key = &key; args.column_parent = &column_parent; args.column = &column; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_add() { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("add") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_add_presult result; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } return; } void CassandraClient::remove(const std::string& key, const ColumnPath& column_path, const int64_t timestamp, const ConsistencyLevel::type consistency_level) { send_remove(key, column_path, timestamp, consistency_level); recv_remove(); } void CassandraClient::send_remove(const std::string& key, const ColumnPath& column_path, const int64_t timestamp, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("remove", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_remove_pargs args; args.key = &key; args.column_path = &column_path; args.timestamp = ×tamp; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_remove() { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("remove") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_remove_presult result; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } return; } void CassandraClient::remove_counter(const std::string& key, const ColumnPath& path, const ConsistencyLevel::type consistency_level) { send_remove_counter(key, path, consistency_level); recv_remove_counter(); } void CassandraClient::send_remove_counter(const std::string& key, const ColumnPath& path, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("remove_counter", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_remove_counter_pargs args; args.key = &key; args.path = &path; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_remove_counter() { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("remove_counter") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_remove_counter_presult result; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } return; } void CassandraClient::batch_mutate(const std::map > > & mutation_map, const ConsistencyLevel::type consistency_level) { send_batch_mutate(mutation_map, consistency_level); recv_batch_mutate(); } void CassandraClient::send_batch_mutate(const std::map > > & mutation_map, const ConsistencyLevel::type consistency_level) { int32_t cseqid = 0; oprot_->writeMessageBegin("batch_mutate", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_batch_mutate_pargs args; args.mutation_map = &mutation_map; args.consistency_level = &consistency_level; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_batch_mutate() { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("batch_mutate") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_batch_mutate_presult result; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } return; } void CassandraClient::truncate(const std::string& cfname) { send_truncate(cfname); recv_truncate(); } void CassandraClient::send_truncate(const std::string& cfname) { int32_t cseqid = 0; oprot_->writeMessageBegin("truncate", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_truncate_pargs args; args.cfname = &cfname; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_truncate() { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("truncate") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_truncate_presult result; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } return; } void CassandraClient::describe_schema_versions(std::map > & _return) { send_describe_schema_versions(); recv_describe_schema_versions(_return); } void CassandraClient::send_describe_schema_versions() { int32_t cseqid = 0; oprot_->writeMessageBegin("describe_schema_versions", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_describe_schema_versions_pargs args; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_describe_schema_versions(std::map > & _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("describe_schema_versions") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_describe_schema_versions_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "describe_schema_versions failed: unknown result"); } void CassandraClient::describe_keyspaces(std::vector & _return) { send_describe_keyspaces(); recv_describe_keyspaces(_return); } void CassandraClient::send_describe_keyspaces() { int32_t cseqid = 0; oprot_->writeMessageBegin("describe_keyspaces", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_describe_keyspaces_pargs args; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_describe_keyspaces(std::vector & _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("describe_keyspaces") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_describe_keyspaces_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "describe_keyspaces failed: unknown result"); } void CassandraClient::describe_cluster_name(std::string& _return) { send_describe_cluster_name(); recv_describe_cluster_name(_return); } void CassandraClient::send_describe_cluster_name() { int32_t cseqid = 0; oprot_->writeMessageBegin("describe_cluster_name", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_describe_cluster_name_pargs args; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_describe_cluster_name(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("describe_cluster_name") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_describe_cluster_name_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "describe_cluster_name failed: unknown result"); } void CassandraClient::describe_version(std::string& _return) { send_describe_version(); recv_describe_version(_return); } void CassandraClient::send_describe_version() { int32_t cseqid = 0; oprot_->writeMessageBegin("describe_version", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_describe_version_pargs args; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_describe_version(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("describe_version") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_describe_version_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "describe_version failed: unknown result"); } void CassandraClient::describe_ring(std::vector & _return, const std::string& keyspace) { send_describe_ring(keyspace); recv_describe_ring(_return); } void CassandraClient::send_describe_ring(const std::string& keyspace) { int32_t cseqid = 0; oprot_->writeMessageBegin("describe_ring", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_describe_ring_pargs args; args.keyspace = &keyspace; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_describe_ring(std::vector & _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("describe_ring") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_describe_ring_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "describe_ring failed: unknown result"); } void CassandraClient::describe_partitioner(std::string& _return) { send_describe_partitioner(); recv_describe_partitioner(_return); } void CassandraClient::send_describe_partitioner() { int32_t cseqid = 0; oprot_->writeMessageBegin("describe_partitioner", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_describe_partitioner_pargs args; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_describe_partitioner(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("describe_partitioner") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_describe_partitioner_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "describe_partitioner failed: unknown result"); } void CassandraClient::describe_snitch(std::string& _return) { send_describe_snitch(); recv_describe_snitch(_return); } void CassandraClient::send_describe_snitch() { int32_t cseqid = 0; oprot_->writeMessageBegin("describe_snitch", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_describe_snitch_pargs args; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_describe_snitch(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("describe_snitch") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_describe_snitch_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "describe_snitch failed: unknown result"); } void CassandraClient::describe_keyspace(KsDef& _return, const std::string& keyspace) { send_describe_keyspace(keyspace); recv_describe_keyspace(_return); } void CassandraClient::send_describe_keyspace(const std::string& keyspace) { int32_t cseqid = 0; oprot_->writeMessageBegin("describe_keyspace", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_describe_keyspace_pargs args; args.keyspace = &keyspace; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_describe_keyspace(KsDef& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("describe_keyspace") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_describe_keyspace_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.nfe) { throw result.nfe; } if (result.__isset.ire) { throw result.ire; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "describe_keyspace failed: unknown result"); } void CassandraClient::describe_splits(std::vector & _return, const std::string& cfName, const std::string& start_token, const std::string& end_token, const int32_t keys_per_split) { send_describe_splits(cfName, start_token, end_token, keys_per_split); recv_describe_splits(_return); } void CassandraClient::send_describe_splits(const std::string& cfName, const std::string& start_token, const std::string& end_token, const int32_t keys_per_split) { int32_t cseqid = 0; oprot_->writeMessageBegin("describe_splits", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_describe_splits_pargs args; args.cfName = &cfName; args.start_token = &start_token; args.end_token = &end_token; args.keys_per_split = &keys_per_split; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_describe_splits(std::vector & _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("describe_splits") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_describe_splits_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "describe_splits failed: unknown result"); } void CassandraClient::system_add_column_family(std::string& _return, const CfDef& cf_def) { send_system_add_column_family(cf_def); recv_system_add_column_family(_return); } void CassandraClient::send_system_add_column_family(const CfDef& cf_def) { int32_t cseqid = 0; oprot_->writeMessageBegin("system_add_column_family", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_system_add_column_family_pargs args; args.cf_def = &cf_def; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_system_add_column_family(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("system_add_column_family") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_system_add_column_family_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.sde) { throw result.sde; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "system_add_column_family failed: unknown result"); } void CassandraClient::system_drop_column_family(std::string& _return, const std::string& column_family) { send_system_drop_column_family(column_family); recv_system_drop_column_family(_return); } void CassandraClient::send_system_drop_column_family(const std::string& column_family) { int32_t cseqid = 0; oprot_->writeMessageBegin("system_drop_column_family", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_system_drop_column_family_pargs args; args.column_family = &column_family; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_system_drop_column_family(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("system_drop_column_family") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_system_drop_column_family_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.sde) { throw result.sde; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "system_drop_column_family failed: unknown result"); } void CassandraClient::system_add_keyspace(std::string& _return, const KsDef& ks_def) { send_system_add_keyspace(ks_def); recv_system_add_keyspace(_return); } void CassandraClient::send_system_add_keyspace(const KsDef& ks_def) { int32_t cseqid = 0; oprot_->writeMessageBegin("system_add_keyspace", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_system_add_keyspace_pargs args; args.ks_def = &ks_def; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_system_add_keyspace(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("system_add_keyspace") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_system_add_keyspace_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.sde) { throw result.sde; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "system_add_keyspace failed: unknown result"); } void CassandraClient::system_drop_keyspace(std::string& _return, const std::string& keyspace) { send_system_drop_keyspace(keyspace); recv_system_drop_keyspace(_return); } void CassandraClient::send_system_drop_keyspace(const std::string& keyspace) { int32_t cseqid = 0; oprot_->writeMessageBegin("system_drop_keyspace", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_system_drop_keyspace_pargs args; args.keyspace = &keyspace; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_system_drop_keyspace(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("system_drop_keyspace") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_system_drop_keyspace_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.sde) { throw result.sde; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "system_drop_keyspace failed: unknown result"); } void CassandraClient::system_update_keyspace(std::string& _return, const KsDef& ks_def) { send_system_update_keyspace(ks_def); recv_system_update_keyspace(_return); } void CassandraClient::send_system_update_keyspace(const KsDef& ks_def) { int32_t cseqid = 0; oprot_->writeMessageBegin("system_update_keyspace", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_system_update_keyspace_pargs args; args.ks_def = &ks_def; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_system_update_keyspace(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("system_update_keyspace") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_system_update_keyspace_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.sde) { throw result.sde; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "system_update_keyspace failed: unknown result"); } void CassandraClient::system_update_column_family(std::string& _return, const CfDef& cf_def) { send_system_update_column_family(cf_def); recv_system_update_column_family(_return); } void CassandraClient::send_system_update_column_family(const CfDef& cf_def) { int32_t cseqid = 0; oprot_->writeMessageBegin("system_update_column_family", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_system_update_column_family_pargs args; args.cf_def = &cf_def; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_system_update_column_family(std::string& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("system_update_column_family") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_system_update_column_family_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.sde) { throw result.sde; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "system_update_column_family failed: unknown result"); } void CassandraClient::execute_cql_query(CqlResult& _return, const std::string& query, const Compression::type compression) { send_execute_cql_query(query, compression); recv_execute_cql_query(_return); } void CassandraClient::send_execute_cql_query(const std::string& query, const Compression::type compression) { int32_t cseqid = 0; oprot_->writeMessageBegin("execute_cql_query", ::apache::thrift::protocol::T_CALL, cseqid); Cassandra_execute_cql_query_pargs args; args.query = &query; args.compression = &compression; args.write(oprot_); oprot_->writeMessageEnd(); oprot_->getTransport()->writeEnd(); oprot_->getTransport()->flush(); } void CassandraClient::recv_execute_cql_query(CqlResult& _return) { int32_t rseqid = 0; std::string fname; ::apache::thrift::protocol::TMessageType mtype; iprot_->readMessageBegin(fname, mtype, rseqid); if (mtype == ::apache::thrift::protocol::T_EXCEPTION) { ::apache::thrift::TApplicationException x; x.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); throw x; } if (mtype != ::apache::thrift::protocol::T_REPLY) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } if (fname.compare("execute_cql_query") != 0) { iprot_->skip(::apache::thrift::protocol::T_STRUCT); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); } Cassandra_execute_cql_query_presult result; result.success = &_return; result.read(iprot_); iprot_->readMessageEnd(); iprot_->getTransport()->readEnd(); if (result.__isset.success) { // _return pointer has now been filled return; } if (result.__isset.ire) { throw result.ire; } if (result.__isset.ue) { throw result.ue; } if (result.__isset.te) { throw result.te; } if (result.__isset.sde) { throw result.sde; } throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "execute_cql_query failed: unknown result"); } bool CassandraProcessor::process(boost::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot, boost::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot, void* callContext) { ::apache::thrift::protocol::TProtocol* iprot = piprot.get(); ::apache::thrift::protocol::TProtocol* oprot = poprot.get(); std::string fname; ::apache::thrift::protocol::TMessageType mtype; int32_t seqid; iprot->readMessageBegin(fname, mtype, seqid); if (mtype != ::apache::thrift::protocol::T_CALL && mtype != ::apache::thrift::protocol::T_ONEWAY) { iprot->skip(::apache::thrift::protocol::T_STRUCT); iprot->readMessageEnd(); iprot->getTransport()->readEnd(); ::apache::thrift::TApplicationException x(::apache::thrift::TApplicationException::INVALID_MESSAGE_TYPE); oprot->writeMessageBegin(fname, ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return true; } return process_fn(iprot, oprot, fname, seqid, callContext); } bool CassandraProcessor::process_fn(::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, std::string& fname, int32_t seqid, void* callContext) { std::map::iterator pfn; pfn = processMap_.find(fname); if (pfn == processMap_.end()) { iprot->skip(::apache::thrift::protocol::T_STRUCT); iprot->readMessageEnd(); iprot->getTransport()->readEnd(); ::apache::thrift::TApplicationException x(::apache::thrift::TApplicationException::UNKNOWN_METHOD, "Invalid method name: '"+fname+"'"); oprot->writeMessageBegin(fname, ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return true; } (this->*(pfn->second))(seqid, iprot, oprot, callContext); return true; } void CassandraProcessor::process_login(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.login", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.login"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.login"); } Cassandra_login_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.login", bytes); } Cassandra_login_result result; try { iface_->login(args.auth_request); } catch (AuthenticationException &authnx) { result.authnx = authnx; result.__isset.authnx = true; } catch (AuthorizationException &authzx) { result.authzx = authzx; result.__isset.authzx = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.login"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("login", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.login"); } oprot->writeMessageBegin("login", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.login", bytes); } } void CassandraProcessor::process_set_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.set_keyspace", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.set_keyspace"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.set_keyspace"); } Cassandra_set_keyspace_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.set_keyspace", bytes); } Cassandra_set_keyspace_result result; try { iface_->set_keyspace(args.keyspace); } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.set_keyspace"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("set_keyspace", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.set_keyspace"); } oprot->writeMessageBegin("set_keyspace", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.set_keyspace", bytes); } } void CassandraProcessor::process_get(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.get", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.get"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.get"); } Cassandra_get_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.get", bytes); } Cassandra_get_result result; try { iface_->get(result.success, args.key, args.column_path, args.consistency_level); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (NotFoundException &nfe) { result.nfe = nfe; result.__isset.nfe = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.get"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("get", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.get"); } oprot->writeMessageBegin("get", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.get", bytes); } } void CassandraProcessor::process_get_slice(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.get_slice", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.get_slice"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.get_slice"); } Cassandra_get_slice_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.get_slice", bytes); } Cassandra_get_slice_result result; try { iface_->get_slice(result.success, args.key, args.column_parent, args.predicate, args.consistency_level); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.get_slice"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("get_slice", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.get_slice"); } oprot->writeMessageBegin("get_slice", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.get_slice", bytes); } } void CassandraProcessor::process_get_count(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.get_count", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.get_count"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.get_count"); } Cassandra_get_count_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.get_count", bytes); } Cassandra_get_count_result result; try { result.success = iface_->get_count(args.key, args.column_parent, args.predicate, args.consistency_level); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.get_count"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("get_count", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.get_count"); } oprot->writeMessageBegin("get_count", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.get_count", bytes); } } void CassandraProcessor::process_multiget_slice(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.multiget_slice", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.multiget_slice"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.multiget_slice"); } Cassandra_multiget_slice_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.multiget_slice", bytes); } Cassandra_multiget_slice_result result; try { iface_->multiget_slice(result.success, args.keys, args.column_parent, args.predicate, args.consistency_level); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.multiget_slice"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("multiget_slice", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.multiget_slice"); } oprot->writeMessageBegin("multiget_slice", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.multiget_slice", bytes); } } void CassandraProcessor::process_multiget_count(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.multiget_count", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.multiget_count"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.multiget_count"); } Cassandra_multiget_count_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.multiget_count", bytes); } Cassandra_multiget_count_result result; try { iface_->multiget_count(result.success, args.keys, args.column_parent, args.predicate, args.consistency_level); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.multiget_count"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("multiget_count", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.multiget_count"); } oprot->writeMessageBegin("multiget_count", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.multiget_count", bytes); } } void CassandraProcessor::process_get_range_slices(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.get_range_slices", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.get_range_slices"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.get_range_slices"); } Cassandra_get_range_slices_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.get_range_slices", bytes); } Cassandra_get_range_slices_result result; try { iface_->get_range_slices(result.success, args.column_parent, args.predicate, args.range, args.consistency_level); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.get_range_slices"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("get_range_slices", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.get_range_slices"); } oprot->writeMessageBegin("get_range_slices", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.get_range_slices", bytes); } } void CassandraProcessor::process_get_indexed_slices(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.get_indexed_slices", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.get_indexed_slices"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.get_indexed_slices"); } Cassandra_get_indexed_slices_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.get_indexed_slices", bytes); } Cassandra_get_indexed_slices_result result; try { iface_->get_indexed_slices(result.success, args.column_parent, args.index_clause, args.column_predicate, args.consistency_level); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.get_indexed_slices"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("get_indexed_slices", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.get_indexed_slices"); } oprot->writeMessageBegin("get_indexed_slices", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.get_indexed_slices", bytes); } } void CassandraProcessor::process_insert(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.insert", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.insert"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.insert"); } Cassandra_insert_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.insert", bytes); } Cassandra_insert_result result; try { iface_->insert(args.key, args.column_parent, args.column, args.consistency_level); } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.insert"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("insert", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.insert"); } oprot->writeMessageBegin("insert", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.insert", bytes); } } void CassandraProcessor::process_add(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.add", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.add"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.add"); } Cassandra_add_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.add", bytes); } Cassandra_add_result result; try { iface_->add(args.key, args.column_parent, args.column, args.consistency_level); } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.add"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("add", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.add"); } oprot->writeMessageBegin("add", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.add", bytes); } } void CassandraProcessor::process_remove(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.remove", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.remove"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.remove"); } Cassandra_remove_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.remove", bytes); } Cassandra_remove_result result; try { iface_->remove(args.key, args.column_path, args.timestamp, args.consistency_level); } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.remove"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("remove", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.remove"); } oprot->writeMessageBegin("remove", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.remove", bytes); } } void CassandraProcessor::process_remove_counter(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.remove_counter", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.remove_counter"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.remove_counter"); } Cassandra_remove_counter_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.remove_counter", bytes); } Cassandra_remove_counter_result result; try { iface_->remove_counter(args.key, args.path, args.consistency_level); } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.remove_counter"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("remove_counter", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.remove_counter"); } oprot->writeMessageBegin("remove_counter", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.remove_counter", bytes); } } void CassandraProcessor::process_batch_mutate(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.batch_mutate", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.batch_mutate"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.batch_mutate"); } Cassandra_batch_mutate_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.batch_mutate", bytes); } Cassandra_batch_mutate_result result; try { iface_->batch_mutate(args.mutation_map, args.consistency_level); } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.batch_mutate"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("batch_mutate", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.batch_mutate"); } oprot->writeMessageBegin("batch_mutate", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.batch_mutate", bytes); } } void CassandraProcessor::process_truncate(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.truncate", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.truncate"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.truncate"); } Cassandra_truncate_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.truncate", bytes); } Cassandra_truncate_result result; try { iface_->truncate(args.cfname); } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.truncate"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("truncate", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.truncate"); } oprot->writeMessageBegin("truncate", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.truncate", bytes); } } void CassandraProcessor::process_describe_schema_versions(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.describe_schema_versions", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.describe_schema_versions"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.describe_schema_versions"); } Cassandra_describe_schema_versions_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.describe_schema_versions", bytes); } Cassandra_describe_schema_versions_result result; try { iface_->describe_schema_versions(result.success); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.describe_schema_versions"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("describe_schema_versions", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.describe_schema_versions"); } oprot->writeMessageBegin("describe_schema_versions", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.describe_schema_versions", bytes); } } void CassandraProcessor::process_describe_keyspaces(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.describe_keyspaces", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.describe_keyspaces"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.describe_keyspaces"); } Cassandra_describe_keyspaces_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.describe_keyspaces", bytes); } Cassandra_describe_keyspaces_result result; try { iface_->describe_keyspaces(result.success); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.describe_keyspaces"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("describe_keyspaces", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.describe_keyspaces"); } oprot->writeMessageBegin("describe_keyspaces", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.describe_keyspaces", bytes); } } void CassandraProcessor::process_describe_cluster_name(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.describe_cluster_name", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.describe_cluster_name"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.describe_cluster_name"); } Cassandra_describe_cluster_name_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.describe_cluster_name", bytes); } Cassandra_describe_cluster_name_result result; try { iface_->describe_cluster_name(result.success); result.__isset.success = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.describe_cluster_name"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("describe_cluster_name", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.describe_cluster_name"); } oprot->writeMessageBegin("describe_cluster_name", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.describe_cluster_name", bytes); } } void CassandraProcessor::process_describe_version(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.describe_version", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.describe_version"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.describe_version"); } Cassandra_describe_version_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.describe_version", bytes); } Cassandra_describe_version_result result; try { iface_->describe_version(result.success); result.__isset.success = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.describe_version"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("describe_version", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.describe_version"); } oprot->writeMessageBegin("describe_version", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.describe_version", bytes); } } void CassandraProcessor::process_describe_ring(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.describe_ring", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.describe_ring"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.describe_ring"); } Cassandra_describe_ring_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.describe_ring", bytes); } Cassandra_describe_ring_result result; try { iface_->describe_ring(result.success, args.keyspace); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.describe_ring"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("describe_ring", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.describe_ring"); } oprot->writeMessageBegin("describe_ring", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.describe_ring", bytes); } } void CassandraProcessor::process_describe_partitioner(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.describe_partitioner", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.describe_partitioner"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.describe_partitioner"); } Cassandra_describe_partitioner_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.describe_partitioner", bytes); } Cassandra_describe_partitioner_result result; try { iface_->describe_partitioner(result.success); result.__isset.success = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.describe_partitioner"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("describe_partitioner", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.describe_partitioner"); } oprot->writeMessageBegin("describe_partitioner", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.describe_partitioner", bytes); } } void CassandraProcessor::process_describe_snitch(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.describe_snitch", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.describe_snitch"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.describe_snitch"); } Cassandra_describe_snitch_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.describe_snitch", bytes); } Cassandra_describe_snitch_result result; try { iface_->describe_snitch(result.success); result.__isset.success = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.describe_snitch"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("describe_snitch", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.describe_snitch"); } oprot->writeMessageBegin("describe_snitch", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.describe_snitch", bytes); } } void CassandraProcessor::process_describe_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.describe_keyspace", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.describe_keyspace"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.describe_keyspace"); } Cassandra_describe_keyspace_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.describe_keyspace", bytes); } Cassandra_describe_keyspace_result result; try { iface_->describe_keyspace(result.success, args.keyspace); result.__isset.success = true; } catch (NotFoundException &nfe) { result.nfe = nfe; result.__isset.nfe = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.describe_keyspace"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("describe_keyspace", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.describe_keyspace"); } oprot->writeMessageBegin("describe_keyspace", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.describe_keyspace", bytes); } } void CassandraProcessor::process_describe_splits(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.describe_splits", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.describe_splits"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.describe_splits"); } Cassandra_describe_splits_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.describe_splits", bytes); } Cassandra_describe_splits_result result; try { iface_->describe_splits(result.success, args.cfName, args.start_token, args.end_token, args.keys_per_split); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.describe_splits"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("describe_splits", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.describe_splits"); } oprot->writeMessageBegin("describe_splits", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.describe_splits", bytes); } } void CassandraProcessor::process_system_add_column_family(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.system_add_column_family", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.system_add_column_family"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.system_add_column_family"); } Cassandra_system_add_column_family_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.system_add_column_family", bytes); } Cassandra_system_add_column_family_result result; try { iface_->system_add_column_family(result.success, args.cf_def); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (SchemaDisagreementException &sde) { result.sde = sde; result.__isset.sde = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.system_add_column_family"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("system_add_column_family", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.system_add_column_family"); } oprot->writeMessageBegin("system_add_column_family", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.system_add_column_family", bytes); } } void CassandraProcessor::process_system_drop_column_family(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.system_drop_column_family", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.system_drop_column_family"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.system_drop_column_family"); } Cassandra_system_drop_column_family_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.system_drop_column_family", bytes); } Cassandra_system_drop_column_family_result result; try { iface_->system_drop_column_family(result.success, args.column_family); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (SchemaDisagreementException &sde) { result.sde = sde; result.__isset.sde = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.system_drop_column_family"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("system_drop_column_family", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.system_drop_column_family"); } oprot->writeMessageBegin("system_drop_column_family", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.system_drop_column_family", bytes); } } void CassandraProcessor::process_system_add_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.system_add_keyspace", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.system_add_keyspace"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.system_add_keyspace"); } Cassandra_system_add_keyspace_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.system_add_keyspace", bytes); } Cassandra_system_add_keyspace_result result; try { iface_->system_add_keyspace(result.success, args.ks_def); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (SchemaDisagreementException &sde) { result.sde = sde; result.__isset.sde = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.system_add_keyspace"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("system_add_keyspace", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.system_add_keyspace"); } oprot->writeMessageBegin("system_add_keyspace", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.system_add_keyspace", bytes); } } void CassandraProcessor::process_system_drop_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.system_drop_keyspace", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.system_drop_keyspace"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.system_drop_keyspace"); } Cassandra_system_drop_keyspace_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.system_drop_keyspace", bytes); } Cassandra_system_drop_keyspace_result result; try { iface_->system_drop_keyspace(result.success, args.keyspace); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (SchemaDisagreementException &sde) { result.sde = sde; result.__isset.sde = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.system_drop_keyspace"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("system_drop_keyspace", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.system_drop_keyspace"); } oprot->writeMessageBegin("system_drop_keyspace", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.system_drop_keyspace", bytes); } } void CassandraProcessor::process_system_update_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.system_update_keyspace", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.system_update_keyspace"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.system_update_keyspace"); } Cassandra_system_update_keyspace_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.system_update_keyspace", bytes); } Cassandra_system_update_keyspace_result result; try { iface_->system_update_keyspace(result.success, args.ks_def); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (SchemaDisagreementException &sde) { result.sde = sde; result.__isset.sde = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.system_update_keyspace"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("system_update_keyspace", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.system_update_keyspace"); } oprot->writeMessageBegin("system_update_keyspace", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.system_update_keyspace", bytes); } } void CassandraProcessor::process_system_update_column_family(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.system_update_column_family", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.system_update_column_family"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.system_update_column_family"); } Cassandra_system_update_column_family_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.system_update_column_family", bytes); } Cassandra_system_update_column_family_result result; try { iface_->system_update_column_family(result.success, args.cf_def); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (SchemaDisagreementException &sde) { result.sde = sde; result.__isset.sde = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.system_update_column_family"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("system_update_column_family", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.system_update_column_family"); } oprot->writeMessageBegin("system_update_column_family", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.system_update_column_family", bytes); } } void CassandraProcessor::process_execute_cql_query(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext) { void* ctx = NULL; if (eventHandler_.get() != NULL) { ctx = eventHandler_->getContext("Cassandra.execute_cql_query", callContext); } ::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, "Cassandra.execute_cql_query"); if (eventHandler_.get() != NULL) { eventHandler_->preRead(ctx, "Cassandra.execute_cql_query"); } Cassandra_execute_cql_query_args args; args.read(iprot); iprot->readMessageEnd(); uint32_t bytes = iprot->getTransport()->readEnd(); if (eventHandler_.get() != NULL) { eventHandler_->postRead(ctx, "Cassandra.execute_cql_query", bytes); } Cassandra_execute_cql_query_result result; try { iface_->execute_cql_query(result.success, args.query, args.compression); result.__isset.success = true; } catch (InvalidRequestException &ire) { result.ire = ire; result.__isset.ire = true; } catch (UnavailableException &ue) { result.ue = ue; result.__isset.ue = true; } catch (TimedOutException &te) { result.te = te; result.__isset.te = true; } catch (SchemaDisagreementException &sde) { result.sde = sde; result.__isset.sde = true; } catch (const std::exception& e) { if (eventHandler_.get() != NULL) { eventHandler_->handlerError(ctx, "Cassandra.execute_cql_query"); } ::apache::thrift::TApplicationException x(e.what()); oprot->writeMessageBegin("execute_cql_query", ::apache::thrift::protocol::T_EXCEPTION, seqid); x.write(oprot); oprot->writeMessageEnd(); oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); return; } if (eventHandler_.get() != NULL) { eventHandler_->preWrite(ctx, "Cassandra.execute_cql_query"); } oprot->writeMessageBegin("execute_cql_query", ::apache::thrift::protocol::T_REPLY, seqid); result.write(oprot); oprot->writeMessageEnd(); bytes = oprot->getTransport()->writeEnd(); oprot->getTransport()->flush(); if (eventHandler_.get() != NULL) { eventHandler_->postWrite(ctx, "Cassandra.execute_cql_query", bytes); } } }}} // namespace opensips-2.2.2/modules/cachedb_cassandra/Cassandra.h000066400000000000000000003514701300170765700225050ustar00rootroot00000000000000/** * Autogenerated by Thrift * * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING */ #ifndef Cassandra_H #define Cassandra_H #include #include "cassandra_types.h" namespace org { namespace apache { namespace cassandra { class CassandraIf { public: virtual ~CassandraIf() {} virtual void login(const AuthenticationRequest& auth_request) = 0; virtual void set_keyspace(const std::string& keyspace) = 0; virtual void get(ColumnOrSuperColumn& _return, const std::string& key, const ColumnPath& column_path, const ConsistencyLevel::type consistency_level) = 0; virtual void get_slice(std::vector & _return, const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) = 0; virtual int32_t get_count(const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) = 0; virtual void multiget_slice(std::map > & _return, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) = 0; virtual void multiget_count(std::map & _return, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) = 0; virtual void get_range_slices(std::vector & _return, const ColumnParent& column_parent, const SlicePredicate& predicate, const KeyRange& range, const ConsistencyLevel::type consistency_level) = 0; virtual void get_indexed_slices(std::vector & _return, const ColumnParent& column_parent, const IndexClause& index_clause, const SlicePredicate& column_predicate, const ConsistencyLevel::type consistency_level) = 0; virtual void insert(const std::string& key, const ColumnParent& column_parent, const Column& column, const ConsistencyLevel::type consistency_level) = 0; virtual void add(const std::string& key, const ColumnParent& column_parent, const CounterColumn& column, const ConsistencyLevel::type consistency_level) = 0; virtual void remove(const std::string& key, const ColumnPath& column_path, const int64_t timestamp, const ConsistencyLevel::type consistency_level) = 0; virtual void remove_counter(const std::string& key, const ColumnPath& path, const ConsistencyLevel::type consistency_level) = 0; virtual void batch_mutate(const std::map > > & mutation_map, const ConsistencyLevel::type consistency_level) = 0; virtual void truncate(const std::string& cfname) = 0; virtual void describe_schema_versions(std::map > & _return) = 0; virtual void describe_keyspaces(std::vector & _return) = 0; virtual void describe_cluster_name(std::string& _return) = 0; virtual void describe_version(std::string& _return) = 0; virtual void describe_ring(std::vector & _return, const std::string& keyspace) = 0; virtual void describe_partitioner(std::string& _return) = 0; virtual void describe_snitch(std::string& _return) = 0; virtual void describe_keyspace(KsDef& _return, const std::string& keyspace) = 0; virtual void describe_splits(std::vector & _return, const std::string& cfName, const std::string& start_token, const std::string& end_token, const int32_t keys_per_split) = 0; virtual void system_add_column_family(std::string& _return, const CfDef& cf_def) = 0; virtual void system_drop_column_family(std::string& _return, const std::string& column_family) = 0; virtual void system_add_keyspace(std::string& _return, const KsDef& ks_def) = 0; virtual void system_drop_keyspace(std::string& _return, const std::string& keyspace) = 0; virtual void system_update_keyspace(std::string& _return, const KsDef& ks_def) = 0; virtual void system_update_column_family(std::string& _return, const CfDef& cf_def) = 0; virtual void execute_cql_query(CqlResult& _return, const std::string& query, const Compression::type compression) = 0; }; class CassandraNull : virtual public CassandraIf { public: virtual ~CassandraNull() {} void login(const AuthenticationRequest& /* auth_request */) { return; } void set_keyspace(const std::string& /* keyspace */) { return; } void get(ColumnOrSuperColumn& /* _return */, const std::string& /* key */, const ColumnPath& /* column_path */, const ConsistencyLevel::type /* consistency_level */) { return; } void get_slice(std::vector & /* _return */, const std::string& /* key */, const ColumnParent& /* column_parent */, const SlicePredicate& /* predicate */, const ConsistencyLevel::type /* consistency_level */) { return; } int32_t get_count(const std::string& /* key */, const ColumnParent& /* column_parent */, const SlicePredicate& /* predicate */, const ConsistencyLevel::type /* consistency_level */) { int32_t _return = 0; return _return; } void multiget_slice(std::map > & /* _return */, const std::vector & /* keys */, const ColumnParent& /* column_parent */, const SlicePredicate& /* predicate */, const ConsistencyLevel::type /* consistency_level */) { return; } void multiget_count(std::map & /* _return */, const std::vector & /* keys */, const ColumnParent& /* column_parent */, const SlicePredicate& /* predicate */, const ConsistencyLevel::type /* consistency_level */) { return; } void get_range_slices(std::vector & /* _return */, const ColumnParent& /* column_parent */, const SlicePredicate& /* predicate */, const KeyRange& /* range */, const ConsistencyLevel::type /* consistency_level */) { return; } void get_indexed_slices(std::vector & /* _return */, const ColumnParent& /* column_parent */, const IndexClause& /* index_clause */, const SlicePredicate& /* column_predicate */, const ConsistencyLevel::type /* consistency_level */) { return; } void insert(const std::string& /* key */, const ColumnParent& /* column_parent */, const Column& /* column */, const ConsistencyLevel::type /* consistency_level */) { return; } void add(const std::string& /* key */, const ColumnParent& /* column_parent */, const CounterColumn& /* column */, const ConsistencyLevel::type /* consistency_level */) { return; } void remove(const std::string& /* key */, const ColumnPath& /* column_path */, const int64_t /* timestamp */, const ConsistencyLevel::type /* consistency_level */) { return; } void remove_counter(const std::string& /* key */, const ColumnPath& /* path */, const ConsistencyLevel::type /* consistency_level */) { return; } void batch_mutate(const std::map > > & /* mutation_map */, const ConsistencyLevel::type /* consistency_level */) { return; } void truncate(const std::string& /* cfname */) { return; } void describe_schema_versions(std::map > & /* _return */) { return; } void describe_keyspaces(std::vector & /* _return */) { return; } void describe_cluster_name(std::string& /* _return */) { return; } void describe_version(std::string& /* _return */) { return; } void describe_ring(std::vector & /* _return */, const std::string& /* keyspace */) { return; } void describe_partitioner(std::string& /* _return */) { return; } void describe_snitch(std::string& /* _return */) { return; } void describe_keyspace(KsDef& /* _return */, const std::string& /* keyspace */) { return; } void describe_splits(std::vector & /* _return */, const std::string& /* cfName */, const std::string& /* start_token */, const std::string& /* end_token */, const int32_t /* keys_per_split */) { return; } void system_add_column_family(std::string& /* _return */, const CfDef& /* cf_def */) { return; } void system_drop_column_family(std::string& /* _return */, const std::string& /* column_family */) { return; } void system_add_keyspace(std::string& /* _return */, const KsDef& /* ks_def */) { return; } void system_drop_keyspace(std::string& /* _return */, const std::string& /* keyspace */) { return; } void system_update_keyspace(std::string& /* _return */, const KsDef& /* ks_def */) { return; } void system_update_column_family(std::string& /* _return */, const CfDef& /* cf_def */) { return; } void execute_cql_query(CqlResult& /* _return */, const std::string& /* query */, const Compression::type /* compression */) { return; } }; class Cassandra_login_args { public: Cassandra_login_args() { } virtual ~Cassandra_login_args() throw() {} AuthenticationRequest auth_request; bool operator == (const Cassandra_login_args & rhs) const { if (!(auth_request == rhs.auth_request)) return false; return true; } bool operator != (const Cassandra_login_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_login_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_login_pargs { public: virtual ~Cassandra_login_pargs() throw() {} const AuthenticationRequest* auth_request; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_login_result__isset { _Cassandra_login_result__isset() : authnx(false), authzx(false) {} bool authnx; bool authzx; } _Cassandra_login_result__isset; class Cassandra_login_result { public: Cassandra_login_result() { } virtual ~Cassandra_login_result() throw() {} AuthenticationException authnx; AuthorizationException authzx; _Cassandra_login_result__isset __isset; bool operator == (const Cassandra_login_result & rhs) const { if (!(authnx == rhs.authnx)) return false; if (!(authzx == rhs.authzx)) return false; return true; } bool operator != (const Cassandra_login_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_login_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_login_presult__isset { _Cassandra_login_presult__isset() : authnx(false), authzx(false) {} bool authnx; bool authzx; } _Cassandra_login_presult__isset; class Cassandra_login_presult { public: virtual ~Cassandra_login_presult() throw() {} AuthenticationException authnx; AuthorizationException authzx; _Cassandra_login_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_set_keyspace_args { public: Cassandra_set_keyspace_args() : keyspace("") { } virtual ~Cassandra_set_keyspace_args() throw() {} std::string keyspace; bool operator == (const Cassandra_set_keyspace_args & rhs) const { if (!(keyspace == rhs.keyspace)) return false; return true; } bool operator != (const Cassandra_set_keyspace_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_set_keyspace_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_set_keyspace_pargs { public: virtual ~Cassandra_set_keyspace_pargs() throw() {} const std::string* keyspace; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_set_keyspace_result__isset { _Cassandra_set_keyspace_result__isset() : ire(false) {} bool ire; } _Cassandra_set_keyspace_result__isset; class Cassandra_set_keyspace_result { public: Cassandra_set_keyspace_result() { } virtual ~Cassandra_set_keyspace_result() throw() {} InvalidRequestException ire; _Cassandra_set_keyspace_result__isset __isset; bool operator == (const Cassandra_set_keyspace_result & rhs) const { if (!(ire == rhs.ire)) return false; return true; } bool operator != (const Cassandra_set_keyspace_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_set_keyspace_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_set_keyspace_presult__isset { _Cassandra_set_keyspace_presult__isset() : ire(false) {} bool ire; } _Cassandra_set_keyspace_presult__isset; class Cassandra_set_keyspace_presult { public: virtual ~Cassandra_set_keyspace_presult() throw() {} InvalidRequestException ire; _Cassandra_set_keyspace_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_get_args { public: Cassandra_get_args() : key("") { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_get_args() throw() {} std::string key; ColumnPath column_path; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_get_args & rhs) const { if (!(key == rhs.key)) return false; if (!(column_path == rhs.column_path)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_get_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_get_pargs { public: virtual ~Cassandra_get_pargs() throw() {} const std::string* key; const ColumnPath* column_path; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_result__isset { _Cassandra_get_result__isset() : success(false), ire(false), nfe(false), ue(false), te(false) {} bool success; bool ire; bool nfe; bool ue; bool te; } _Cassandra_get_result__isset; class Cassandra_get_result { public: Cassandra_get_result() { } virtual ~Cassandra_get_result() throw() {} ColumnOrSuperColumn success; InvalidRequestException ire; NotFoundException nfe; UnavailableException ue; TimedOutException te; _Cassandra_get_result__isset __isset; bool operator == (const Cassandra_get_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(nfe == rhs.nfe)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_get_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_presult__isset { _Cassandra_get_presult__isset() : success(false), ire(false), nfe(false), ue(false), te(false) {} bool success; bool ire; bool nfe; bool ue; bool te; } _Cassandra_get_presult__isset; class Cassandra_get_presult { public: virtual ~Cassandra_get_presult() throw() {} ColumnOrSuperColumn* success; InvalidRequestException ire; NotFoundException nfe; UnavailableException ue; TimedOutException te; _Cassandra_get_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_get_slice_args { public: Cassandra_get_slice_args() : key("") { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_get_slice_args() throw() {} std::string key; ColumnParent column_parent; SlicePredicate predicate; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_get_slice_args & rhs) const { if (!(key == rhs.key)) return false; if (!(column_parent == rhs.column_parent)) return false; if (!(predicate == rhs.predicate)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_get_slice_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_slice_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_get_slice_pargs { public: virtual ~Cassandra_get_slice_pargs() throw() {} const std::string* key; const ColumnParent* column_parent; const SlicePredicate* predicate; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_slice_result__isset { _Cassandra_get_slice_result__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_get_slice_result__isset; class Cassandra_get_slice_result { public: Cassandra_get_slice_result() { } virtual ~Cassandra_get_slice_result() throw() {} std::vector success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_get_slice_result__isset __isset; bool operator == (const Cassandra_get_slice_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_get_slice_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_slice_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_slice_presult__isset { _Cassandra_get_slice_presult__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_get_slice_presult__isset; class Cassandra_get_slice_presult { public: virtual ~Cassandra_get_slice_presult() throw() {} std::vector * success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_get_slice_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_get_count_args { public: Cassandra_get_count_args() : key("") { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_get_count_args() throw() {} std::string key; ColumnParent column_parent; SlicePredicate predicate; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_get_count_args & rhs) const { if (!(key == rhs.key)) return false; if (!(column_parent == rhs.column_parent)) return false; if (!(predicate == rhs.predicate)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_get_count_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_count_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_get_count_pargs { public: virtual ~Cassandra_get_count_pargs() throw() {} const std::string* key; const ColumnParent* column_parent; const SlicePredicate* predicate; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_count_result__isset { _Cassandra_get_count_result__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_get_count_result__isset; class Cassandra_get_count_result { public: Cassandra_get_count_result() : success(0) { } virtual ~Cassandra_get_count_result() throw() {} int32_t success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_get_count_result__isset __isset; bool operator == (const Cassandra_get_count_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_get_count_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_count_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_count_presult__isset { _Cassandra_get_count_presult__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_get_count_presult__isset; class Cassandra_get_count_presult { public: virtual ~Cassandra_get_count_presult() throw() {} int32_t* success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_get_count_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_multiget_slice_args { public: Cassandra_multiget_slice_args() { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_multiget_slice_args() throw() {} std::vector keys; ColumnParent column_parent; SlicePredicate predicate; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_multiget_slice_args & rhs) const { if (!(keys == rhs.keys)) return false; if (!(column_parent == rhs.column_parent)) return false; if (!(predicate == rhs.predicate)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_multiget_slice_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_multiget_slice_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_multiget_slice_pargs { public: virtual ~Cassandra_multiget_slice_pargs() throw() {} const std::vector * keys; const ColumnParent* column_parent; const SlicePredicate* predicate; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_multiget_slice_result__isset { _Cassandra_multiget_slice_result__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_multiget_slice_result__isset; class Cassandra_multiget_slice_result { public: Cassandra_multiget_slice_result() { } virtual ~Cassandra_multiget_slice_result() throw() {} std::map > success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_multiget_slice_result__isset __isset; bool operator == (const Cassandra_multiget_slice_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_multiget_slice_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_multiget_slice_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_multiget_slice_presult__isset { _Cassandra_multiget_slice_presult__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_multiget_slice_presult__isset; class Cassandra_multiget_slice_presult { public: virtual ~Cassandra_multiget_slice_presult() throw() {} std::map > * success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_multiget_slice_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_multiget_count_args { public: Cassandra_multiget_count_args() { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_multiget_count_args() throw() {} std::vector keys; ColumnParent column_parent; SlicePredicate predicate; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_multiget_count_args & rhs) const { if (!(keys == rhs.keys)) return false; if (!(column_parent == rhs.column_parent)) return false; if (!(predicate == rhs.predicate)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_multiget_count_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_multiget_count_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_multiget_count_pargs { public: virtual ~Cassandra_multiget_count_pargs() throw() {} const std::vector * keys; const ColumnParent* column_parent; const SlicePredicate* predicate; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_multiget_count_result__isset { _Cassandra_multiget_count_result__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_multiget_count_result__isset; class Cassandra_multiget_count_result { public: Cassandra_multiget_count_result() { } virtual ~Cassandra_multiget_count_result() throw() {} std::map success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_multiget_count_result__isset __isset; bool operator == (const Cassandra_multiget_count_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_multiget_count_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_multiget_count_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_multiget_count_presult__isset { _Cassandra_multiget_count_presult__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_multiget_count_presult__isset; class Cassandra_multiget_count_presult { public: virtual ~Cassandra_multiget_count_presult() throw() {} std::map * success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_multiget_count_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_get_range_slices_args { public: Cassandra_get_range_slices_args() { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_get_range_slices_args() throw() {} ColumnParent column_parent; SlicePredicate predicate; KeyRange range; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_get_range_slices_args & rhs) const { if (!(column_parent == rhs.column_parent)) return false; if (!(predicate == rhs.predicate)) return false; if (!(range == rhs.range)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_get_range_slices_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_range_slices_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_get_range_slices_pargs { public: virtual ~Cassandra_get_range_slices_pargs() throw() {} const ColumnParent* column_parent; const SlicePredicate* predicate; const KeyRange* range; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_range_slices_result__isset { _Cassandra_get_range_slices_result__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_get_range_slices_result__isset; class Cassandra_get_range_slices_result { public: Cassandra_get_range_slices_result() { } virtual ~Cassandra_get_range_slices_result() throw() {} std::vector success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_get_range_slices_result__isset __isset; bool operator == (const Cassandra_get_range_slices_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_get_range_slices_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_range_slices_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_range_slices_presult__isset { _Cassandra_get_range_slices_presult__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_get_range_slices_presult__isset; class Cassandra_get_range_slices_presult { public: virtual ~Cassandra_get_range_slices_presult() throw() {} std::vector * success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_get_range_slices_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_get_indexed_slices_args { public: Cassandra_get_indexed_slices_args() { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_get_indexed_slices_args() throw() {} ColumnParent column_parent; IndexClause index_clause; SlicePredicate column_predicate; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_get_indexed_slices_args & rhs) const { if (!(column_parent == rhs.column_parent)) return false; if (!(index_clause == rhs.index_clause)) return false; if (!(column_predicate == rhs.column_predicate)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_get_indexed_slices_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_indexed_slices_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_get_indexed_slices_pargs { public: virtual ~Cassandra_get_indexed_slices_pargs() throw() {} const ColumnParent* column_parent; const IndexClause* index_clause; const SlicePredicate* column_predicate; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_indexed_slices_result__isset { _Cassandra_get_indexed_slices_result__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_get_indexed_slices_result__isset; class Cassandra_get_indexed_slices_result { public: Cassandra_get_indexed_slices_result() { } virtual ~Cassandra_get_indexed_slices_result() throw() {} std::vector success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_get_indexed_slices_result__isset __isset; bool operator == (const Cassandra_get_indexed_slices_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_get_indexed_slices_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_get_indexed_slices_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_get_indexed_slices_presult__isset { _Cassandra_get_indexed_slices_presult__isset() : success(false), ire(false), ue(false), te(false) {} bool success; bool ire; bool ue; bool te; } _Cassandra_get_indexed_slices_presult__isset; class Cassandra_get_indexed_slices_presult { public: virtual ~Cassandra_get_indexed_slices_presult() throw() {} std::vector * success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_get_indexed_slices_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_insert_args { public: Cassandra_insert_args() : key("") { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_insert_args() throw() {} std::string key; ColumnParent column_parent; Column column; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_insert_args & rhs) const { if (!(key == rhs.key)) return false; if (!(column_parent == rhs.column_parent)) return false; if (!(column == rhs.column)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_insert_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_insert_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_insert_pargs { public: virtual ~Cassandra_insert_pargs() throw() {} const std::string* key; const ColumnParent* column_parent; const Column* column; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_insert_result__isset { _Cassandra_insert_result__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_insert_result__isset; class Cassandra_insert_result { public: Cassandra_insert_result() { } virtual ~Cassandra_insert_result() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_insert_result__isset __isset; bool operator == (const Cassandra_insert_result & rhs) const { if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_insert_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_insert_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_insert_presult__isset { _Cassandra_insert_presult__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_insert_presult__isset; class Cassandra_insert_presult { public: virtual ~Cassandra_insert_presult() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_insert_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_add_args { public: Cassandra_add_args() : key("") { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_add_args() throw() {} std::string key; ColumnParent column_parent; CounterColumn column; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_add_args & rhs) const { if (!(key == rhs.key)) return false; if (!(column_parent == rhs.column_parent)) return false; if (!(column == rhs.column)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_add_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_add_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_add_pargs { public: virtual ~Cassandra_add_pargs() throw() {} const std::string* key; const ColumnParent* column_parent; const CounterColumn* column; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_add_result__isset { _Cassandra_add_result__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_add_result__isset; class Cassandra_add_result { public: Cassandra_add_result() { } virtual ~Cassandra_add_result() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_add_result__isset __isset; bool operator == (const Cassandra_add_result & rhs) const { if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_add_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_add_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_add_presult__isset { _Cassandra_add_presult__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_add_presult__isset; class Cassandra_add_presult { public: virtual ~Cassandra_add_presult() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_add_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; typedef struct _Cassandra_remove_args__isset { _Cassandra_remove_args__isset() : consistency_level(false) {} bool consistency_level; } _Cassandra_remove_args__isset; class Cassandra_remove_args { public: Cassandra_remove_args() : key(""), timestamp(0) { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_remove_args() throw() {} std::string key; ColumnPath column_path; int64_t timestamp; ConsistencyLevel::type consistency_level; _Cassandra_remove_args__isset __isset; bool operator == (const Cassandra_remove_args & rhs) const { if (!(key == rhs.key)) return false; if (!(column_path == rhs.column_path)) return false; if (!(timestamp == rhs.timestamp)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_remove_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_remove_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_remove_pargs { public: virtual ~Cassandra_remove_pargs() throw() {} const std::string* key; const ColumnPath* column_path; const int64_t* timestamp; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_remove_result__isset { _Cassandra_remove_result__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_remove_result__isset; class Cassandra_remove_result { public: Cassandra_remove_result() { } virtual ~Cassandra_remove_result() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_remove_result__isset __isset; bool operator == (const Cassandra_remove_result & rhs) const { if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_remove_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_remove_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_remove_presult__isset { _Cassandra_remove_presult__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_remove_presult__isset; class Cassandra_remove_presult { public: virtual ~Cassandra_remove_presult() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_remove_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_remove_counter_args { public: Cassandra_remove_counter_args() : key("") { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_remove_counter_args() throw() {} std::string key; ColumnPath path; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_remove_counter_args & rhs) const { if (!(key == rhs.key)) return false; if (!(path == rhs.path)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_remove_counter_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_remove_counter_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_remove_counter_pargs { public: virtual ~Cassandra_remove_counter_pargs() throw() {} const std::string* key; const ColumnPath* path; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_remove_counter_result__isset { _Cassandra_remove_counter_result__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_remove_counter_result__isset; class Cassandra_remove_counter_result { public: Cassandra_remove_counter_result() { } virtual ~Cassandra_remove_counter_result() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_remove_counter_result__isset __isset; bool operator == (const Cassandra_remove_counter_result & rhs) const { if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_remove_counter_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_remove_counter_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_remove_counter_presult__isset { _Cassandra_remove_counter_presult__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_remove_counter_presult__isset; class Cassandra_remove_counter_presult { public: virtual ~Cassandra_remove_counter_presult() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_remove_counter_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_batch_mutate_args { public: Cassandra_batch_mutate_args() { consistency_level = (ConsistencyLevel::type)1; } virtual ~Cassandra_batch_mutate_args() throw() {} std::map > > mutation_map; ConsistencyLevel::type consistency_level; bool operator == (const Cassandra_batch_mutate_args & rhs) const { if (!(mutation_map == rhs.mutation_map)) return false; if (!(consistency_level == rhs.consistency_level)) return false; return true; } bool operator != (const Cassandra_batch_mutate_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_batch_mutate_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_batch_mutate_pargs { public: virtual ~Cassandra_batch_mutate_pargs() throw() {} const std::map > > * mutation_map; const ConsistencyLevel::type* consistency_level; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_batch_mutate_result__isset { _Cassandra_batch_mutate_result__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_batch_mutate_result__isset; class Cassandra_batch_mutate_result { public: Cassandra_batch_mutate_result() { } virtual ~Cassandra_batch_mutate_result() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_batch_mutate_result__isset __isset; bool operator == (const Cassandra_batch_mutate_result & rhs) const { if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; return true; } bool operator != (const Cassandra_batch_mutate_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_batch_mutate_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_batch_mutate_presult__isset { _Cassandra_batch_mutate_presult__isset() : ire(false), ue(false), te(false) {} bool ire; bool ue; bool te; } _Cassandra_batch_mutate_presult__isset; class Cassandra_batch_mutate_presult { public: virtual ~Cassandra_batch_mutate_presult() throw() {} InvalidRequestException ire; UnavailableException ue; TimedOutException te; _Cassandra_batch_mutate_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_truncate_args { public: Cassandra_truncate_args() : cfname("") { } virtual ~Cassandra_truncate_args() throw() {} std::string cfname; bool operator == (const Cassandra_truncate_args & rhs) const { if (!(cfname == rhs.cfname)) return false; return true; } bool operator != (const Cassandra_truncate_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_truncate_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_truncate_pargs { public: virtual ~Cassandra_truncate_pargs() throw() {} const std::string* cfname; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_truncate_result__isset { _Cassandra_truncate_result__isset() : ire(false), ue(false) {} bool ire; bool ue; } _Cassandra_truncate_result__isset; class Cassandra_truncate_result { public: Cassandra_truncate_result() { } virtual ~Cassandra_truncate_result() throw() {} InvalidRequestException ire; UnavailableException ue; _Cassandra_truncate_result__isset __isset; bool operator == (const Cassandra_truncate_result & rhs) const { if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; return true; } bool operator != (const Cassandra_truncate_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_truncate_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_truncate_presult__isset { _Cassandra_truncate_presult__isset() : ire(false), ue(false) {} bool ire; bool ue; } _Cassandra_truncate_presult__isset; class Cassandra_truncate_presult { public: virtual ~Cassandra_truncate_presult() throw() {} InvalidRequestException ire; UnavailableException ue; _Cassandra_truncate_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_describe_schema_versions_args { public: Cassandra_describe_schema_versions_args() { } virtual ~Cassandra_describe_schema_versions_args() throw() {} bool operator == (const Cassandra_describe_schema_versions_args & /* rhs */) const { return true; } bool operator != (const Cassandra_describe_schema_versions_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_schema_versions_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_describe_schema_versions_pargs { public: virtual ~Cassandra_describe_schema_versions_pargs() throw() {} uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_schema_versions_result__isset { _Cassandra_describe_schema_versions_result__isset() : success(false), ire(false) {} bool success; bool ire; } _Cassandra_describe_schema_versions_result__isset; class Cassandra_describe_schema_versions_result { public: Cassandra_describe_schema_versions_result() { } virtual ~Cassandra_describe_schema_versions_result() throw() {} std::map > success; InvalidRequestException ire; _Cassandra_describe_schema_versions_result__isset __isset; bool operator == (const Cassandra_describe_schema_versions_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; return true; } bool operator != (const Cassandra_describe_schema_versions_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_schema_versions_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_schema_versions_presult__isset { _Cassandra_describe_schema_versions_presult__isset() : success(false), ire(false) {} bool success; bool ire; } _Cassandra_describe_schema_versions_presult__isset; class Cassandra_describe_schema_versions_presult { public: virtual ~Cassandra_describe_schema_versions_presult() throw() {} std::map > * success; InvalidRequestException ire; _Cassandra_describe_schema_versions_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_describe_keyspaces_args { public: Cassandra_describe_keyspaces_args() { } virtual ~Cassandra_describe_keyspaces_args() throw() {} bool operator == (const Cassandra_describe_keyspaces_args & /* rhs */) const { return true; } bool operator != (const Cassandra_describe_keyspaces_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_keyspaces_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_describe_keyspaces_pargs { public: virtual ~Cassandra_describe_keyspaces_pargs() throw() {} uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_keyspaces_result__isset { _Cassandra_describe_keyspaces_result__isset() : success(false), ire(false) {} bool success; bool ire; } _Cassandra_describe_keyspaces_result__isset; class Cassandra_describe_keyspaces_result { public: Cassandra_describe_keyspaces_result() { } virtual ~Cassandra_describe_keyspaces_result() throw() {} std::vector success; InvalidRequestException ire; _Cassandra_describe_keyspaces_result__isset __isset; bool operator == (const Cassandra_describe_keyspaces_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; return true; } bool operator != (const Cassandra_describe_keyspaces_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_keyspaces_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_keyspaces_presult__isset { _Cassandra_describe_keyspaces_presult__isset() : success(false), ire(false) {} bool success; bool ire; } _Cassandra_describe_keyspaces_presult__isset; class Cassandra_describe_keyspaces_presult { public: virtual ~Cassandra_describe_keyspaces_presult() throw() {} std::vector * success; InvalidRequestException ire; _Cassandra_describe_keyspaces_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_describe_cluster_name_args { public: Cassandra_describe_cluster_name_args() { } virtual ~Cassandra_describe_cluster_name_args() throw() {} bool operator == (const Cassandra_describe_cluster_name_args & /* rhs */) const { return true; } bool operator != (const Cassandra_describe_cluster_name_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_cluster_name_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_describe_cluster_name_pargs { public: virtual ~Cassandra_describe_cluster_name_pargs() throw() {} uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_cluster_name_result__isset { _Cassandra_describe_cluster_name_result__isset() : success(false) {} bool success; } _Cassandra_describe_cluster_name_result__isset; class Cassandra_describe_cluster_name_result { public: Cassandra_describe_cluster_name_result() : success("") { } virtual ~Cassandra_describe_cluster_name_result() throw() {} std::string success; _Cassandra_describe_cluster_name_result__isset __isset; bool operator == (const Cassandra_describe_cluster_name_result & rhs) const { if (!(success == rhs.success)) return false; return true; } bool operator != (const Cassandra_describe_cluster_name_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_cluster_name_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_cluster_name_presult__isset { _Cassandra_describe_cluster_name_presult__isset() : success(false) {} bool success; } _Cassandra_describe_cluster_name_presult__isset; class Cassandra_describe_cluster_name_presult { public: virtual ~Cassandra_describe_cluster_name_presult() throw() {} std::string* success; _Cassandra_describe_cluster_name_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_describe_version_args { public: Cassandra_describe_version_args() { } virtual ~Cassandra_describe_version_args() throw() {} bool operator == (const Cassandra_describe_version_args & /* rhs */) const { return true; } bool operator != (const Cassandra_describe_version_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_version_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_describe_version_pargs { public: virtual ~Cassandra_describe_version_pargs() throw() {} uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_version_result__isset { _Cassandra_describe_version_result__isset() : success(false) {} bool success; } _Cassandra_describe_version_result__isset; class Cassandra_describe_version_result { public: Cassandra_describe_version_result() : success("") { } virtual ~Cassandra_describe_version_result() throw() {} std::string success; _Cassandra_describe_version_result__isset __isset; bool operator == (const Cassandra_describe_version_result & rhs) const { if (!(success == rhs.success)) return false; return true; } bool operator != (const Cassandra_describe_version_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_version_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_version_presult__isset { _Cassandra_describe_version_presult__isset() : success(false) {} bool success; } _Cassandra_describe_version_presult__isset; class Cassandra_describe_version_presult { public: virtual ~Cassandra_describe_version_presult() throw() {} std::string* success; _Cassandra_describe_version_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_describe_ring_args { public: Cassandra_describe_ring_args() : keyspace("") { } virtual ~Cassandra_describe_ring_args() throw() {} std::string keyspace; bool operator == (const Cassandra_describe_ring_args & rhs) const { if (!(keyspace == rhs.keyspace)) return false; return true; } bool operator != (const Cassandra_describe_ring_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_ring_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_describe_ring_pargs { public: virtual ~Cassandra_describe_ring_pargs() throw() {} const std::string* keyspace; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_ring_result__isset { _Cassandra_describe_ring_result__isset() : success(false), ire(false) {} bool success; bool ire; } _Cassandra_describe_ring_result__isset; class Cassandra_describe_ring_result { public: Cassandra_describe_ring_result() { } virtual ~Cassandra_describe_ring_result() throw() {} std::vector success; InvalidRequestException ire; _Cassandra_describe_ring_result__isset __isset; bool operator == (const Cassandra_describe_ring_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; return true; } bool operator != (const Cassandra_describe_ring_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_ring_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_ring_presult__isset { _Cassandra_describe_ring_presult__isset() : success(false), ire(false) {} bool success; bool ire; } _Cassandra_describe_ring_presult__isset; class Cassandra_describe_ring_presult { public: virtual ~Cassandra_describe_ring_presult() throw() {} std::vector * success; InvalidRequestException ire; _Cassandra_describe_ring_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_describe_partitioner_args { public: Cassandra_describe_partitioner_args() { } virtual ~Cassandra_describe_partitioner_args() throw() {} bool operator == (const Cassandra_describe_partitioner_args & /* rhs */) const { return true; } bool operator != (const Cassandra_describe_partitioner_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_partitioner_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_describe_partitioner_pargs { public: virtual ~Cassandra_describe_partitioner_pargs() throw() {} uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_partitioner_result__isset { _Cassandra_describe_partitioner_result__isset() : success(false) {} bool success; } _Cassandra_describe_partitioner_result__isset; class Cassandra_describe_partitioner_result { public: Cassandra_describe_partitioner_result() : success("") { } virtual ~Cassandra_describe_partitioner_result() throw() {} std::string success; _Cassandra_describe_partitioner_result__isset __isset; bool operator == (const Cassandra_describe_partitioner_result & rhs) const { if (!(success == rhs.success)) return false; return true; } bool operator != (const Cassandra_describe_partitioner_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_partitioner_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_partitioner_presult__isset { _Cassandra_describe_partitioner_presult__isset() : success(false) {} bool success; } _Cassandra_describe_partitioner_presult__isset; class Cassandra_describe_partitioner_presult { public: virtual ~Cassandra_describe_partitioner_presult() throw() {} std::string* success; _Cassandra_describe_partitioner_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_describe_snitch_args { public: Cassandra_describe_snitch_args() { } virtual ~Cassandra_describe_snitch_args() throw() {} bool operator == (const Cassandra_describe_snitch_args & /* rhs */) const { return true; } bool operator != (const Cassandra_describe_snitch_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_snitch_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_describe_snitch_pargs { public: virtual ~Cassandra_describe_snitch_pargs() throw() {} uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_snitch_result__isset { _Cassandra_describe_snitch_result__isset() : success(false) {} bool success; } _Cassandra_describe_snitch_result__isset; class Cassandra_describe_snitch_result { public: Cassandra_describe_snitch_result() : success("") { } virtual ~Cassandra_describe_snitch_result() throw() {} std::string success; _Cassandra_describe_snitch_result__isset __isset; bool operator == (const Cassandra_describe_snitch_result & rhs) const { if (!(success == rhs.success)) return false; return true; } bool operator != (const Cassandra_describe_snitch_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_snitch_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_snitch_presult__isset { _Cassandra_describe_snitch_presult__isset() : success(false) {} bool success; } _Cassandra_describe_snitch_presult__isset; class Cassandra_describe_snitch_presult { public: virtual ~Cassandra_describe_snitch_presult() throw() {} std::string* success; _Cassandra_describe_snitch_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_describe_keyspace_args { public: Cassandra_describe_keyspace_args() : keyspace("") { } virtual ~Cassandra_describe_keyspace_args() throw() {} std::string keyspace; bool operator == (const Cassandra_describe_keyspace_args & rhs) const { if (!(keyspace == rhs.keyspace)) return false; return true; } bool operator != (const Cassandra_describe_keyspace_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_keyspace_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_describe_keyspace_pargs { public: virtual ~Cassandra_describe_keyspace_pargs() throw() {} const std::string* keyspace; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_keyspace_result__isset { _Cassandra_describe_keyspace_result__isset() : success(false), nfe(false), ire(false) {} bool success; bool nfe; bool ire; } _Cassandra_describe_keyspace_result__isset; class Cassandra_describe_keyspace_result { public: Cassandra_describe_keyspace_result() { } virtual ~Cassandra_describe_keyspace_result() throw() {} KsDef success; NotFoundException nfe; InvalidRequestException ire; _Cassandra_describe_keyspace_result__isset __isset; bool operator == (const Cassandra_describe_keyspace_result & rhs) const { if (!(success == rhs.success)) return false; if (!(nfe == rhs.nfe)) return false; if (!(ire == rhs.ire)) return false; return true; } bool operator != (const Cassandra_describe_keyspace_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_keyspace_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_keyspace_presult__isset { _Cassandra_describe_keyspace_presult__isset() : success(false), nfe(false), ire(false) {} bool success; bool nfe; bool ire; } _Cassandra_describe_keyspace_presult__isset; class Cassandra_describe_keyspace_presult { public: virtual ~Cassandra_describe_keyspace_presult() throw() {} KsDef* success; NotFoundException nfe; InvalidRequestException ire; _Cassandra_describe_keyspace_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_describe_splits_args { public: Cassandra_describe_splits_args() : cfName(""), start_token(""), end_token(""), keys_per_split(0) { } virtual ~Cassandra_describe_splits_args() throw() {} std::string cfName; std::string start_token; std::string end_token; int32_t keys_per_split; bool operator == (const Cassandra_describe_splits_args & rhs) const { if (!(cfName == rhs.cfName)) return false; if (!(start_token == rhs.start_token)) return false; if (!(end_token == rhs.end_token)) return false; if (!(keys_per_split == rhs.keys_per_split)) return false; return true; } bool operator != (const Cassandra_describe_splits_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_splits_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_describe_splits_pargs { public: virtual ~Cassandra_describe_splits_pargs() throw() {} const std::string* cfName; const std::string* start_token; const std::string* end_token; const int32_t* keys_per_split; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_splits_result__isset { _Cassandra_describe_splits_result__isset() : success(false), ire(false) {} bool success; bool ire; } _Cassandra_describe_splits_result__isset; class Cassandra_describe_splits_result { public: Cassandra_describe_splits_result() { } virtual ~Cassandra_describe_splits_result() throw() {} std::vector success; InvalidRequestException ire; _Cassandra_describe_splits_result__isset __isset; bool operator == (const Cassandra_describe_splits_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; return true; } bool operator != (const Cassandra_describe_splits_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_describe_splits_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_describe_splits_presult__isset { _Cassandra_describe_splits_presult__isset() : success(false), ire(false) {} bool success; bool ire; } _Cassandra_describe_splits_presult__isset; class Cassandra_describe_splits_presult { public: virtual ~Cassandra_describe_splits_presult() throw() {} std::vector * success; InvalidRequestException ire; _Cassandra_describe_splits_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_system_add_column_family_args { public: Cassandra_system_add_column_family_args() { } virtual ~Cassandra_system_add_column_family_args() throw() {} CfDef cf_def; bool operator == (const Cassandra_system_add_column_family_args & rhs) const { if (!(cf_def == rhs.cf_def)) return false; return true; } bool operator != (const Cassandra_system_add_column_family_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_add_column_family_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_system_add_column_family_pargs { public: virtual ~Cassandra_system_add_column_family_pargs() throw() {} const CfDef* cf_def; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_add_column_family_result__isset { _Cassandra_system_add_column_family_result__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_add_column_family_result__isset; class Cassandra_system_add_column_family_result { public: Cassandra_system_add_column_family_result() : success("") { } virtual ~Cassandra_system_add_column_family_result() throw() {} std::string success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_add_column_family_result__isset __isset; bool operator == (const Cassandra_system_add_column_family_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(sde == rhs.sde)) return false; return true; } bool operator != (const Cassandra_system_add_column_family_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_add_column_family_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_add_column_family_presult__isset { _Cassandra_system_add_column_family_presult__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_add_column_family_presult__isset; class Cassandra_system_add_column_family_presult { public: virtual ~Cassandra_system_add_column_family_presult() throw() {} std::string* success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_add_column_family_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_system_drop_column_family_args { public: Cassandra_system_drop_column_family_args() : column_family("") { } virtual ~Cassandra_system_drop_column_family_args() throw() {} std::string column_family; bool operator == (const Cassandra_system_drop_column_family_args & rhs) const { if (!(column_family == rhs.column_family)) return false; return true; } bool operator != (const Cassandra_system_drop_column_family_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_drop_column_family_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_system_drop_column_family_pargs { public: virtual ~Cassandra_system_drop_column_family_pargs() throw() {} const std::string* column_family; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_drop_column_family_result__isset { _Cassandra_system_drop_column_family_result__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_drop_column_family_result__isset; class Cassandra_system_drop_column_family_result { public: Cassandra_system_drop_column_family_result() : success("") { } virtual ~Cassandra_system_drop_column_family_result() throw() {} std::string success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_drop_column_family_result__isset __isset; bool operator == (const Cassandra_system_drop_column_family_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(sde == rhs.sde)) return false; return true; } bool operator != (const Cassandra_system_drop_column_family_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_drop_column_family_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_drop_column_family_presult__isset { _Cassandra_system_drop_column_family_presult__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_drop_column_family_presult__isset; class Cassandra_system_drop_column_family_presult { public: virtual ~Cassandra_system_drop_column_family_presult() throw() {} std::string* success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_drop_column_family_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_system_add_keyspace_args { public: Cassandra_system_add_keyspace_args() { } virtual ~Cassandra_system_add_keyspace_args() throw() {} KsDef ks_def; bool operator == (const Cassandra_system_add_keyspace_args & rhs) const { if (!(ks_def == rhs.ks_def)) return false; return true; } bool operator != (const Cassandra_system_add_keyspace_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_add_keyspace_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_system_add_keyspace_pargs { public: virtual ~Cassandra_system_add_keyspace_pargs() throw() {} const KsDef* ks_def; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_add_keyspace_result__isset { _Cassandra_system_add_keyspace_result__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_add_keyspace_result__isset; class Cassandra_system_add_keyspace_result { public: Cassandra_system_add_keyspace_result() : success("") { } virtual ~Cassandra_system_add_keyspace_result() throw() {} std::string success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_add_keyspace_result__isset __isset; bool operator == (const Cassandra_system_add_keyspace_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(sde == rhs.sde)) return false; return true; } bool operator != (const Cassandra_system_add_keyspace_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_add_keyspace_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_add_keyspace_presult__isset { _Cassandra_system_add_keyspace_presult__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_add_keyspace_presult__isset; class Cassandra_system_add_keyspace_presult { public: virtual ~Cassandra_system_add_keyspace_presult() throw() {} std::string* success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_add_keyspace_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_system_drop_keyspace_args { public: Cassandra_system_drop_keyspace_args() : keyspace("") { } virtual ~Cassandra_system_drop_keyspace_args() throw() {} std::string keyspace; bool operator == (const Cassandra_system_drop_keyspace_args & rhs) const { if (!(keyspace == rhs.keyspace)) return false; return true; } bool operator != (const Cassandra_system_drop_keyspace_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_drop_keyspace_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_system_drop_keyspace_pargs { public: virtual ~Cassandra_system_drop_keyspace_pargs() throw() {} const std::string* keyspace; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_drop_keyspace_result__isset { _Cassandra_system_drop_keyspace_result__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_drop_keyspace_result__isset; class Cassandra_system_drop_keyspace_result { public: Cassandra_system_drop_keyspace_result() : success("") { } virtual ~Cassandra_system_drop_keyspace_result() throw() {} std::string success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_drop_keyspace_result__isset __isset; bool operator == (const Cassandra_system_drop_keyspace_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(sde == rhs.sde)) return false; return true; } bool operator != (const Cassandra_system_drop_keyspace_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_drop_keyspace_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_drop_keyspace_presult__isset { _Cassandra_system_drop_keyspace_presult__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_drop_keyspace_presult__isset; class Cassandra_system_drop_keyspace_presult { public: virtual ~Cassandra_system_drop_keyspace_presult() throw() {} std::string* success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_drop_keyspace_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_system_update_keyspace_args { public: Cassandra_system_update_keyspace_args() { } virtual ~Cassandra_system_update_keyspace_args() throw() {} KsDef ks_def; bool operator == (const Cassandra_system_update_keyspace_args & rhs) const { if (!(ks_def == rhs.ks_def)) return false; return true; } bool operator != (const Cassandra_system_update_keyspace_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_update_keyspace_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_system_update_keyspace_pargs { public: virtual ~Cassandra_system_update_keyspace_pargs() throw() {} const KsDef* ks_def; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_update_keyspace_result__isset { _Cassandra_system_update_keyspace_result__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_update_keyspace_result__isset; class Cassandra_system_update_keyspace_result { public: Cassandra_system_update_keyspace_result() : success("") { } virtual ~Cassandra_system_update_keyspace_result() throw() {} std::string success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_update_keyspace_result__isset __isset; bool operator == (const Cassandra_system_update_keyspace_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(sde == rhs.sde)) return false; return true; } bool operator != (const Cassandra_system_update_keyspace_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_update_keyspace_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_update_keyspace_presult__isset { _Cassandra_system_update_keyspace_presult__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_update_keyspace_presult__isset; class Cassandra_system_update_keyspace_presult { public: virtual ~Cassandra_system_update_keyspace_presult() throw() {} std::string* success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_update_keyspace_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_system_update_column_family_args { public: Cassandra_system_update_column_family_args() { } virtual ~Cassandra_system_update_column_family_args() throw() {} CfDef cf_def; bool operator == (const Cassandra_system_update_column_family_args & rhs) const { if (!(cf_def == rhs.cf_def)) return false; return true; } bool operator != (const Cassandra_system_update_column_family_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_update_column_family_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_system_update_column_family_pargs { public: virtual ~Cassandra_system_update_column_family_pargs() throw() {} const CfDef* cf_def; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_update_column_family_result__isset { _Cassandra_system_update_column_family_result__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_update_column_family_result__isset; class Cassandra_system_update_column_family_result { public: Cassandra_system_update_column_family_result() : success("") { } virtual ~Cassandra_system_update_column_family_result() throw() {} std::string success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_update_column_family_result__isset __isset; bool operator == (const Cassandra_system_update_column_family_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(sde == rhs.sde)) return false; return true; } bool operator != (const Cassandra_system_update_column_family_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_system_update_column_family_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_system_update_column_family_presult__isset { _Cassandra_system_update_column_family_presult__isset() : success(false), ire(false), sde(false) {} bool success; bool ire; bool sde; } _Cassandra_system_update_column_family_presult__isset; class Cassandra_system_update_column_family_presult { public: virtual ~Cassandra_system_update_column_family_presult() throw() {} std::string* success; InvalidRequestException ire; SchemaDisagreementException sde; _Cassandra_system_update_column_family_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class Cassandra_execute_cql_query_args { public: Cassandra_execute_cql_query_args() : query("") { } virtual ~Cassandra_execute_cql_query_args() throw() {} std::string query; Compression::type compression; bool operator == (const Cassandra_execute_cql_query_args & rhs) const { if (!(query == rhs.query)) return false; if (!(compression == rhs.compression)) return false; return true; } bool operator != (const Cassandra_execute_cql_query_args &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_execute_cql_query_args & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class Cassandra_execute_cql_query_pargs { public: virtual ~Cassandra_execute_cql_query_pargs() throw() {} const std::string* query; const Compression::type* compression; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_execute_cql_query_result__isset { _Cassandra_execute_cql_query_result__isset() : success(false), ire(false), ue(false), te(false), sde(false) {} bool success; bool ire; bool ue; bool te; bool sde; } _Cassandra_execute_cql_query_result__isset; class Cassandra_execute_cql_query_result { public: Cassandra_execute_cql_query_result() { } virtual ~Cassandra_execute_cql_query_result() throw() {} CqlResult success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; SchemaDisagreementException sde; _Cassandra_execute_cql_query_result__isset __isset; bool operator == (const Cassandra_execute_cql_query_result & rhs) const { if (!(success == rhs.success)) return false; if (!(ire == rhs.ire)) return false; if (!(ue == rhs.ue)) return false; if (!(te == rhs.te)) return false; if (!(sde == rhs.sde)) return false; return true; } bool operator != (const Cassandra_execute_cql_query_result &rhs) const { return !(*this == rhs); } bool operator < (const Cassandra_execute_cql_query_result & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Cassandra_execute_cql_query_presult__isset { _Cassandra_execute_cql_query_presult__isset() : success(false), ire(false), ue(false), te(false), sde(false) {} bool success; bool ire; bool ue; bool te; bool sde; } _Cassandra_execute_cql_query_presult__isset; class Cassandra_execute_cql_query_presult { public: virtual ~Cassandra_execute_cql_query_presult() throw() {} CqlResult* success; InvalidRequestException ire; UnavailableException ue; TimedOutException te; SchemaDisagreementException sde; _Cassandra_execute_cql_query_presult__isset __isset; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); }; class CassandraClient : virtual public CassandraIf { public: CassandraClient(boost::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) : piprot_(prot), poprot_(prot) { iprot_ = prot.get(); oprot_ = prot.get(); } CassandraClient(boost::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, boost::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) : piprot_(iprot), poprot_(oprot) { iprot_ = iprot.get(); oprot_ = oprot.get(); } boost::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() { return piprot_; } boost::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() { return poprot_; } void login(const AuthenticationRequest& auth_request); void send_login(const AuthenticationRequest& auth_request); void recv_login(); void set_keyspace(const std::string& keyspace); void send_set_keyspace(const std::string& keyspace); void recv_set_keyspace(); void get(ColumnOrSuperColumn& _return, const std::string& key, const ColumnPath& column_path, const ConsistencyLevel::type consistency_level); void send_get(const std::string& key, const ColumnPath& column_path, const ConsistencyLevel::type consistency_level); void recv_get(ColumnOrSuperColumn& _return); void get_slice(std::vector & _return, const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level); void send_get_slice(const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level); void recv_get_slice(std::vector & _return); int32_t get_count(const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level); void send_get_count(const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level); int32_t recv_get_count(); void multiget_slice(std::map > & _return, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level); void send_multiget_slice(const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level); void recv_multiget_slice(std::map > & _return); void multiget_count(std::map & _return, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level); void send_multiget_count(const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level); void recv_multiget_count(std::map & _return); void get_range_slices(std::vector & _return, const ColumnParent& column_parent, const SlicePredicate& predicate, const KeyRange& range, const ConsistencyLevel::type consistency_level); void send_get_range_slices(const ColumnParent& column_parent, const SlicePredicate& predicate, const KeyRange& range, const ConsistencyLevel::type consistency_level); void recv_get_range_slices(std::vector & _return); void get_indexed_slices(std::vector & _return, const ColumnParent& column_parent, const IndexClause& index_clause, const SlicePredicate& column_predicate, const ConsistencyLevel::type consistency_level); void send_get_indexed_slices(const ColumnParent& column_parent, const IndexClause& index_clause, const SlicePredicate& column_predicate, const ConsistencyLevel::type consistency_level); void recv_get_indexed_slices(std::vector & _return); void insert(const std::string& key, const ColumnParent& column_parent, const Column& column, const ConsistencyLevel::type consistency_level); void send_insert(const std::string& key, const ColumnParent& column_parent, const Column& column, const ConsistencyLevel::type consistency_level); void recv_insert(); void add(const std::string& key, const ColumnParent& column_parent, const CounterColumn& column, const ConsistencyLevel::type consistency_level); void send_add(const std::string& key, const ColumnParent& column_parent, const CounterColumn& column, const ConsistencyLevel::type consistency_level); void recv_add(); void remove(const std::string& key, const ColumnPath& column_path, const int64_t timestamp, const ConsistencyLevel::type consistency_level); void send_remove(const std::string& key, const ColumnPath& column_path, const int64_t timestamp, const ConsistencyLevel::type consistency_level); void recv_remove(); void remove_counter(const std::string& key, const ColumnPath& path, const ConsistencyLevel::type consistency_level); void send_remove_counter(const std::string& key, const ColumnPath& path, const ConsistencyLevel::type consistency_level); void recv_remove_counter(); void batch_mutate(const std::map > > & mutation_map, const ConsistencyLevel::type consistency_level); void send_batch_mutate(const std::map > > & mutation_map, const ConsistencyLevel::type consistency_level); void recv_batch_mutate(); void truncate(const std::string& cfname); void send_truncate(const std::string& cfname); void recv_truncate(); void describe_schema_versions(std::map > & _return); void send_describe_schema_versions(); void recv_describe_schema_versions(std::map > & _return); void describe_keyspaces(std::vector & _return); void send_describe_keyspaces(); void recv_describe_keyspaces(std::vector & _return); void describe_cluster_name(std::string& _return); void send_describe_cluster_name(); void recv_describe_cluster_name(std::string& _return); void describe_version(std::string& _return); void send_describe_version(); void recv_describe_version(std::string& _return); void describe_ring(std::vector & _return, const std::string& keyspace); void send_describe_ring(const std::string& keyspace); void recv_describe_ring(std::vector & _return); void describe_partitioner(std::string& _return); void send_describe_partitioner(); void recv_describe_partitioner(std::string& _return); void describe_snitch(std::string& _return); void send_describe_snitch(); void recv_describe_snitch(std::string& _return); void describe_keyspace(KsDef& _return, const std::string& keyspace); void send_describe_keyspace(const std::string& keyspace); void recv_describe_keyspace(KsDef& _return); void describe_splits(std::vector & _return, const std::string& cfName, const std::string& start_token, const std::string& end_token, const int32_t keys_per_split); void send_describe_splits(const std::string& cfName, const std::string& start_token, const std::string& end_token, const int32_t keys_per_split); void recv_describe_splits(std::vector & _return); void system_add_column_family(std::string& _return, const CfDef& cf_def); void send_system_add_column_family(const CfDef& cf_def); void recv_system_add_column_family(std::string& _return); void system_drop_column_family(std::string& _return, const std::string& column_family); void send_system_drop_column_family(const std::string& column_family); void recv_system_drop_column_family(std::string& _return); void system_add_keyspace(std::string& _return, const KsDef& ks_def); void send_system_add_keyspace(const KsDef& ks_def); void recv_system_add_keyspace(std::string& _return); void system_drop_keyspace(std::string& _return, const std::string& keyspace); void send_system_drop_keyspace(const std::string& keyspace); void recv_system_drop_keyspace(std::string& _return); void system_update_keyspace(std::string& _return, const KsDef& ks_def); void send_system_update_keyspace(const KsDef& ks_def); void recv_system_update_keyspace(std::string& _return); void system_update_column_family(std::string& _return, const CfDef& cf_def); void send_system_update_column_family(const CfDef& cf_def); void recv_system_update_column_family(std::string& _return); void execute_cql_query(CqlResult& _return, const std::string& query, const Compression::type compression); void send_execute_cql_query(const std::string& query, const Compression::type compression); void recv_execute_cql_query(CqlResult& _return); protected: boost::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot_; boost::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot_; ::apache::thrift::protocol::TProtocol* iprot_; ::apache::thrift::protocol::TProtocol* oprot_; }; class CassandraProcessor : virtual public ::apache::thrift::TProcessor { protected: boost::shared_ptr iface_; virtual bool process_fn(::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, std::string& fname, int32_t seqid, void* callContext); private: std::map processMap_; void process_login(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_set_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_get(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_get_slice(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_get_count(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_multiget_slice(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_multiget_count(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_get_range_slices(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_get_indexed_slices(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_insert(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_add(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_remove(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_remove_counter(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_batch_mutate(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_truncate(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_describe_schema_versions(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_describe_keyspaces(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_describe_cluster_name(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_describe_version(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_describe_ring(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_describe_partitioner(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_describe_snitch(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_describe_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_describe_splits(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_system_add_column_family(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_system_drop_column_family(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_system_add_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_system_drop_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_system_update_keyspace(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_system_update_column_family(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); void process_execute_cql_query(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext); public: CassandraProcessor(boost::shared_ptr iface) : iface_(iface) { processMap_["login"] = &CassandraProcessor::process_login; processMap_["set_keyspace"] = &CassandraProcessor::process_set_keyspace; processMap_["get"] = &CassandraProcessor::process_get; processMap_["get_slice"] = &CassandraProcessor::process_get_slice; processMap_["get_count"] = &CassandraProcessor::process_get_count; processMap_["multiget_slice"] = &CassandraProcessor::process_multiget_slice; processMap_["multiget_count"] = &CassandraProcessor::process_multiget_count; processMap_["get_range_slices"] = &CassandraProcessor::process_get_range_slices; processMap_["get_indexed_slices"] = &CassandraProcessor::process_get_indexed_slices; processMap_["insert"] = &CassandraProcessor::process_insert; processMap_["add"] = &CassandraProcessor::process_add; processMap_["remove"] = &CassandraProcessor::process_remove; processMap_["remove_counter"] = &CassandraProcessor::process_remove_counter; processMap_["batch_mutate"] = &CassandraProcessor::process_batch_mutate; processMap_["truncate"] = &CassandraProcessor::process_truncate; processMap_["describe_schema_versions"] = &CassandraProcessor::process_describe_schema_versions; processMap_["describe_keyspaces"] = &CassandraProcessor::process_describe_keyspaces; processMap_["describe_cluster_name"] = &CassandraProcessor::process_describe_cluster_name; processMap_["describe_version"] = &CassandraProcessor::process_describe_version; processMap_["describe_ring"] = &CassandraProcessor::process_describe_ring; processMap_["describe_partitioner"] = &CassandraProcessor::process_describe_partitioner; processMap_["describe_snitch"] = &CassandraProcessor::process_describe_snitch; processMap_["describe_keyspace"] = &CassandraProcessor::process_describe_keyspace; processMap_["describe_splits"] = &CassandraProcessor::process_describe_splits; processMap_["system_add_column_family"] = &CassandraProcessor::process_system_add_column_family; processMap_["system_drop_column_family"] = &CassandraProcessor::process_system_drop_column_family; processMap_["system_add_keyspace"] = &CassandraProcessor::process_system_add_keyspace; processMap_["system_drop_keyspace"] = &CassandraProcessor::process_system_drop_keyspace; processMap_["system_update_keyspace"] = &CassandraProcessor::process_system_update_keyspace; processMap_["system_update_column_family"] = &CassandraProcessor::process_system_update_column_family; processMap_["execute_cql_query"] = &CassandraProcessor::process_execute_cql_query; } virtual bool process(boost::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot, boost::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot, void* callContext); virtual ~CassandraProcessor() {} }; class CassandraMultiface : virtual public CassandraIf { public: CassandraMultiface(std::vector >& ifaces) : ifaces_(ifaces) { } virtual ~CassandraMultiface() {} protected: std::vector > ifaces_; CassandraMultiface() {} void add(boost::shared_ptr iface) { ifaces_.push_back(iface); } public: void login(const AuthenticationRequest& auth_request) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { ifaces_[i]->login(auth_request); } } void set_keyspace(const std::string& keyspace) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { ifaces_[i]->set_keyspace(keyspace); } } void get(ColumnOrSuperColumn& _return, const std::string& key, const ColumnPath& column_path, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->get(_return, key, column_path, consistency_level); return; } else { ifaces_[i]->get(_return, key, column_path, consistency_level); } } } void get_slice(std::vector & _return, const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->get_slice(_return, key, column_parent, predicate, consistency_level); return; } else { ifaces_[i]->get_slice(_return, key, column_parent, predicate, consistency_level); } } } int32_t get_count(const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { return ifaces_[i]->get_count(key, column_parent, predicate, consistency_level); } else { ifaces_[i]->get_count(key, column_parent, predicate, consistency_level); } } } void multiget_slice(std::map > & _return, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->multiget_slice(_return, keys, column_parent, predicate, consistency_level); return; } else { ifaces_[i]->multiget_slice(_return, keys, column_parent, predicate, consistency_level); } } } void multiget_count(std::map & _return, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->multiget_count(_return, keys, column_parent, predicate, consistency_level); return; } else { ifaces_[i]->multiget_count(_return, keys, column_parent, predicate, consistency_level); } } } void get_range_slices(std::vector & _return, const ColumnParent& column_parent, const SlicePredicate& predicate, const KeyRange& range, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->get_range_slices(_return, column_parent, predicate, range, consistency_level); return; } else { ifaces_[i]->get_range_slices(_return, column_parent, predicate, range, consistency_level); } } } void get_indexed_slices(std::vector & _return, const ColumnParent& column_parent, const IndexClause& index_clause, const SlicePredicate& column_predicate, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->get_indexed_slices(_return, column_parent, index_clause, column_predicate, consistency_level); return; } else { ifaces_[i]->get_indexed_slices(_return, column_parent, index_clause, column_predicate, consistency_level); } } } void insert(const std::string& key, const ColumnParent& column_parent, const Column& column, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { ifaces_[i]->insert(key, column_parent, column, consistency_level); } } void add(const std::string& key, const ColumnParent& column_parent, const CounterColumn& column, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { ifaces_[i]->add(key, column_parent, column, consistency_level); } } void remove(const std::string& key, const ColumnPath& column_path, const int64_t timestamp, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { ifaces_[i]->remove(key, column_path, timestamp, consistency_level); } } void remove_counter(const std::string& key, const ColumnPath& path, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { ifaces_[i]->remove_counter(key, path, consistency_level); } } void batch_mutate(const std::map > > & mutation_map, const ConsistencyLevel::type consistency_level) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { ifaces_[i]->batch_mutate(mutation_map, consistency_level); } } void truncate(const std::string& cfname) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { ifaces_[i]->truncate(cfname); } } void describe_schema_versions(std::map > & _return) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->describe_schema_versions(_return); return; } else { ifaces_[i]->describe_schema_versions(_return); } } } void describe_keyspaces(std::vector & _return) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->describe_keyspaces(_return); return; } else { ifaces_[i]->describe_keyspaces(_return); } } } void describe_cluster_name(std::string& _return) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->describe_cluster_name(_return); return; } else { ifaces_[i]->describe_cluster_name(_return); } } } void describe_version(std::string& _return) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->describe_version(_return); return; } else { ifaces_[i]->describe_version(_return); } } } void describe_ring(std::vector & _return, const std::string& keyspace) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->describe_ring(_return, keyspace); return; } else { ifaces_[i]->describe_ring(_return, keyspace); } } } void describe_partitioner(std::string& _return) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->describe_partitioner(_return); return; } else { ifaces_[i]->describe_partitioner(_return); } } } void describe_snitch(std::string& _return) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->describe_snitch(_return); return; } else { ifaces_[i]->describe_snitch(_return); } } } void describe_keyspace(KsDef& _return, const std::string& keyspace) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->describe_keyspace(_return, keyspace); return; } else { ifaces_[i]->describe_keyspace(_return, keyspace); } } } void describe_splits(std::vector & _return, const std::string& cfName, const std::string& start_token, const std::string& end_token, const int32_t keys_per_split) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->describe_splits(_return, cfName, start_token, end_token, keys_per_split); return; } else { ifaces_[i]->describe_splits(_return, cfName, start_token, end_token, keys_per_split); } } } void system_add_column_family(std::string& _return, const CfDef& cf_def) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->system_add_column_family(_return, cf_def); return; } else { ifaces_[i]->system_add_column_family(_return, cf_def); } } } void system_drop_column_family(std::string& _return, const std::string& column_family) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->system_drop_column_family(_return, column_family); return; } else { ifaces_[i]->system_drop_column_family(_return, column_family); } } } void system_add_keyspace(std::string& _return, const KsDef& ks_def) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->system_add_keyspace(_return, ks_def); return; } else { ifaces_[i]->system_add_keyspace(_return, ks_def); } } } void system_drop_keyspace(std::string& _return, const std::string& keyspace) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->system_drop_keyspace(_return, keyspace); return; } else { ifaces_[i]->system_drop_keyspace(_return, keyspace); } } } void system_update_keyspace(std::string& _return, const KsDef& ks_def) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->system_update_keyspace(_return, ks_def); return; } else { ifaces_[i]->system_update_keyspace(_return, ks_def); } } } void system_update_column_family(std::string& _return, const CfDef& cf_def) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->system_update_column_family(_return, cf_def); return; } else { ifaces_[i]->system_update_column_family(_return, cf_def); } } } void execute_cql_query(CqlResult& _return, const std::string& query, const Compression::type compression) { uint32_t sz = ifaces_.size(); for (uint32_t i = 0; i < sz; ++i) { if (i == sz - 1) { ifaces_[i]->execute_cql_query(_return, query, compression); return; } else { ifaces_[i]->execute_cql_query(_return, query, compression); } } } }; }}} // namespace #endif opensips-2.2.2/modules/cachedb_cassandra/Makefile000066400000000000000000000023241300170765700220640ustar00rootroot00000000000000include ../../Makefile.defs auto_gen= NAME=cachedb_cassandra.so CXX=g++ LD=g++ LIBS=cachedb_cassandra_dbase.o cassandra_constants.o cassandra_types.o Cassandra.o -L/usr/local/lib -lthrift CXXFLAGS=$(CFLAGS:-Wno-deprecated option=) -DHAVE_NETINET_IN_H # suppress a TON of warnings CXXFLAGS+= -Wno-write-strings -Wno-deprecated -Wno-unused-function -Wno-sign-compare -Wno-strict-aliasing include ../../Makefile.modules cassandra_constants.o: cassandra_constants.cpp cassandra_constants.h @echo "Compiling $<" $(Q)$(CXX) $(CXXFLAGS) -I/usr/include/thrift -I/usr/local/include/thrift -c $< -o $@ cassandra_types.o: cassandra_types.cpp cassandra_types.h @echo "Compiling $<" $(Q)$(CXX) $(CXXFLAGS) -I/usr/include/thrift -I/usr/local/include/thrift -c $< -o $@ Cassandra.o: Cassandra.cpp Cassandra.h @echo "Compiling $<" $(Q)$(CXX) $(CXXFLAGS) -I/usr/include/thrift -I/usr/local/include/thrift -c $< -o $@ cachedb_cassandra_dbase.o: cachedb_cassandra_dbase.cpp cachedb_cassandra_lib.h cachedb_cassandra_dbase.h @echo "Compiling $<" $(Q)$(CXX) $(CXXFLAGS) $(DEFS) -I/usr/include/thrift -I/usr/local/include/thrift -c $< -o $@ cachedb_cassandra.so: cachedb_cassandra_dbase.o cassandra_constants.o cassandra_types.o Cassandra.o opensips-2.2.2/modules/cachedb_cassandra/README000066400000000000000000000143761300170765700213160ustar00rootroot00000000000000cachedb_cassandra Module Vladut-Stefan Paiu OpenSIPS Solutions Edited by Vladut-Stefan Paiu Copyright © 2011 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Advantages 1.3. Limitations 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. cachedb_url (string) 1.5.2. connection_timeout (int) 1.5.3. send_timeout (int) 1.5.4. receive_timeout (int) 1.5.5. wr_consistency_level (int) 1.5.6. rd_consistency_level (int) 1.5.7. exec_threshold (int) 1.6. Exported Functions 1.7. Known issues List of Examples 1.1. Set cachedb_url parameter 1.2. Use Cassandra servers 1.3. Set connection_timeout parameter 1.4. Set send_timeout parameter 1.5. Set receive_timeout parameter 1.6. Set wr_consistency_level parameter 1.7. Set rd_consistency_level parameter 1.8. Set exec_threshold parameter Chapter 1. Admin Guide 1.1. Overview This module is an implementation of a cache system designed to work with Cassandra servers. It uses the Key-Value interface exported from the core. 1.2. Advantages * memory costs are no longer on the server * many servers can be used inside a cluster, so the memory is virtually unlimited * the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The Cassandra DB is also persistent so it can also be restarted without loss of information. * Cassandra is an open-source project so it can be used to exchange data with various other applications * By creating a Cassandra Cluster, multiple OpenSIPS instances can easily share key-value information 1.3. Limitations * keys (in key:value pairs) may not contain spaces or control characters 1.4. Dependencies 1.4.1. OpenSIPS Modules None. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * thrift 0.6.1 Thrift 0.6.1 can be downloaded from http://archive.apache.org/dist/thrift/ Download the archive, extract sources, run ./configure,make,sudo make install. 1.5. Exported Parameters 1.5.1. cachedb_url (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. The database part of the URL needs to be in the format Keyspace_ColumnFamily_CounterFamily Example 1.1. Set cachedb_url parameter ... modparam("cachedb_cassandra", "cachedb_url","cassandra:group1://localhos t:9061/Keyspace1_Users_Counters"); modparam("cachedb_cassandra", "cachedb_url","cassandra:cluster1://random _url:8888/Keyspace2_Keys_CounterF"); ... Example 1.2. Use Cassandra servers ... cache_store("cassandra:group1","key","$ru value"); cache_fetch("cassandra:cluster1","key",$avp(10)); cache_remove("cassandra:cluster1","key"); ... 1.5.2. connection_timeout (int) The timeout in ms that will be triggered in case a connection attempt fails. Example 1.3. Set connection_timeout parameter ... modparam("cachedb_cassandra", "connection_timeout",1000); ... 1.5.3. send_timeout (int) The timeout in ms that will be triggered in case a Cassandra write takes too long Example 1.4. Set send_timeout parameter ... modparam("cachedb_cassandra", "send_timeout",1000); ... 1.5.4. receive_timeout (int) The timeout in ms that will be triggered in case a Cassandra read takes too long Example 1.5. Set receive_timeout parameter ... modparam("cachedb_cassandra", "receive_timeout",1000); ... 1.5.5. wr_consistency_level (int) The consistency level desired for write operations. Options are : * 1 - Ensure that the write has been written to at least 1 replica's commit log and memory table before responding to the client. * 2 - Ensure that the write has been written to N / 2 + 1 replicas before responding to the client. * 3 - Ensure that the write has been written to ReplicationFactor / 2 + 1 nodes, within the local datacenter (requires NetworkTopologyStrategy) * 4 - Ensure that the write has been written to ReplicationFactor / 2 + 1 nodes in each datacenter (requires NetworkTopologyStrategy) * 5 - Ensure that the write is written to all N replicas before responding to the client. Any unresponsive replicas will fail the operation. * 6 - Ensure that the write has been written to at least 1 node, including HintedHandoff recipients. * 7 - Ensure that the write has been written to at least 2 replica's before responding to the client. * 8 - Ensure that the write has been written to at least 3 replica's before responding to the client. Default is 1 Example 1.6. Set wr_consistency_level parameter ... modparam("cachedb_cassandra", "wr_consistency_level",7); ... 1.5.6. rd_consistency_level (int) The consistency level desired for read operations. Options are the same as for write consistency level. Example 1.7. Set rd_consistency_level parameter ... modparam("cachedb_cassandra", "rd_consistency_level",7); ... 1.5.7. exec_threshold (int) The maximum number of microseconds that a cassandra cache query can last. Anything above the threshold will trigger a warning message to the log Default value is “0 ( unlimited - no warnings )â€. Example 1.8. Set exec_threshold parameter ... modparam("cachedb_cassandra", "exec_threshold", 100000) ... 1.6. Exported Functions The module does not export functions to be used in configuration script. 1.7. Known issues Due to the fact that Cassandra cannot store counters and regular columns in the same ColumnFamily, add() and sub() methods are not exported through the Key-Value interface. Also, the module does not currently support Authentication. Future realeases of this module will address this issue. opensips-2.2.2/modules/cachedb_cassandra/cachedb_cassandra.c000066400000000000000000000077771300170765700242010ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-12-xx created (vlad-paiu) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../pt.h" #include "../../cachedb/cachedb.h" #include "cachedb_cassandra_dbase.h" int conn_timeout=1000; /* ms */ int send_timeout=2000; /* ms */ int recv_timeout=2000; /* ms */ int rd_consistency_level=1; int wr_consistency_level=1; /* TODO - implement */ int cassandra_exec_threshold = 0; static int mod_init(void); static int child_init(int); static void destroy(void); static str cache_mod_name = str_init("cassandra"); struct cachedb_url *cassandra_script_urls = NULL; int set_connection(unsigned int type, void *val) { return cachedb_store_url(&cassandra_script_urls,(char *)val); } static param_export_t params[]={ { "cachedb_url", STR_PARAM|USE_FUNC_PARAM, (void *)&set_connection}, { "connection_timeout", INT_PARAM, &conn_timeout}, { "send_timeout", INT_PARAM, &send_timeout}, { "receive_timeout", INT_PARAM, &recv_timeout}, { "rd_consistency_level", INT_PARAM, &rd_consistency_level}, { "wr_consistency_level", INT_PARAM, &wr_consistency_level}, { "exec_threshold", INT_PARAM, &cassandra_exec_threshold}, {0,0,0} }; /** module exports */ struct module_exports exports= { "cachedb_cassandra", MOD_TYPE_CACHEDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, NULL, /* OpenSIPS module dependencies */ 0, 0, params, 0, 0, 0, 0, mod_init, (response_function) 0, (destroy_function)destroy, child_init }; /** * init module function */ static int mod_init(void) { cachedb_engine cde; LM_NOTICE("initializing module cachedb_cassandra ...\n"); if (rd_consistency_level<1 || rd_consistency_level > 8) rd_consistency_level=1; if (wr_consistency_level<1 || wr_consistency_level > 8) wr_consistency_level=1; memset(&cde, 0, sizeof(cachedb_engine)); cde.name = cache_mod_name; cde.cdb_func.init = cassandra_init; cde.cdb_func.destroy = cassandra_destroy; cde.cdb_func.get = cassandra_get; cde.cdb_func.get_counter = cassandra_get_counter; cde.cdb_func.set = cassandra_set; cde.cdb_func.remove = cassandra_remove; cde.cdb_func.add = cassandra_add; cde.cdb_func.sub = cassandra_sub; cde.cdb_func.capability = CACHEDB_CAP_BINARY_VALUE; if (register_cachedb(&cde) < 0) { LM_ERR("failed to initialize cachedb_cassandra\n"); return -1; } return 0; } static int child_init(int rank) { struct cachedb_url *it; cachedb_con *con; for (it = cassandra_script_urls;it;it=it->next) { LM_DBG("iterating through conns - [%.*s]\n",it->url.len,it->url.s); con = cassandra_init(&it->url); if (con == NULL) { LM_ERR("failed to open connection\n"); return -1; } if (cachedb_put_connection(&cache_mod_name,con) < 0) { LM_ERR("failed to insert connection\n"); return -1; } } cachedb_free_url(cassandra_script_urls); return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module cachedb_cassandra ...\n"); cachedb_end_connections(&cache_mod_name); return; } opensips-2.2.2/modules/cachedb_cassandra/cachedb_cassandra.h000066400000000000000000000021461300170765700241670ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-12-xx created (vlad-paiu) */ #ifndef CACHEDBCASSANDRA_H #define CACHEDBCASSANDRA_H extern int conn_timeout; extern int send_timeout; extern int recv_timeout; extern int rd_consistency_level; extern int wr_consistency_level; extern int cassandra_exec_threshold; #endif opensips-2.2.2/modules/cachedb_cassandra/cachedb_cassandra_dbase.cpp000066400000000000000000000170101300170765700256540ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-12-xx created (vlad-paiu) */ #include "cachedb_cassandra.h" #include "cachedb_cassandra_dbase.h" #include "cachedb_cassandra_lib.h" #include void* cassandra_new_connection(char *_host,int port,str *_keyspace,str* _cf, str* _counterf) { string host(_host,strlen(_host)); string keyspace(_keyspace->s,_keyspace->len); string cf(_cf->s,_cf->len); string counterf(_counterf->s,_counterf->len); CassandraConnection *con = new CassandraConnection(keyspace,cf,counterf); if (!con) { LM_ERR("failed to init CassandraConnection\n"); return NULL; } if (con->cassandra_open(host,port,conn_timeout,send_timeout, recv_timeout,rd_consistency_level,wr_consistency_level) < 0) { LM_ERR("failed to connect to Cassandra DB\n"); delete con; return NULL; } return (void *)con; } void* cassandra_init_connection(struct cachedb_id *id) { cassandra_con *con; str keyspace; str column_family; str counter_family; int db_len; char *p; if (id == NULL) { LM_ERR("null cachedb_id\n"); return 0; } if (id->flags & CACHEDB_ID_MULTIPLE_HOSTS) { LM_ERR("multiple hosts are not supported for cassandra\n"); return 0; } if (id->database == NULL) { LM_ERR("no database supplied for cassandra\n"); return 0; } db_len = strlen(id->database); p=(char *)memchr(id->database,'_',db_len); if (!p) { LM_ERR("invalid database. Should be 'keyspace_columnfamily_counterfamily'\n"); return 0; } keyspace.s=id->database; keyspace.len=p-keyspace.s; column_family.s=p+1; p = (char *)memchr(column_family.s,'_',id->database+db_len-p-1); if (!p) { LM_ERR("invalid database. Should be 'keyspace_columnfamily_counterfamily'\n"); return 0; } column_family.len=p-column_family.s; counter_family.s=p+1; counter_family.len=id->database+db_len-counter_family.s; LM_INFO("Keyspace = [%.*s]. ColumnFamily = [%.*s]. CounterFamily = [%.*s]\n", keyspace.len,keyspace.s,column_family.len,column_family.s, counter_family.len,counter_family.s); con = (cassandra_con *)pkg_malloc(sizeof(cassandra_con)); if (con == NULL) { LM_ERR("no more pkg \n"); return 0; } memset(con,0,sizeof(cassandra_con)); con->id = id; con->ref = 1; con->cass_con = cassandra_new_connection(id->host,id->port, &keyspace,&column_family,&counter_family); if (con->cass_con == NULL) { LM_ERR("failed to connect to cassandra\n"); return 0; } return con; } cachedb_con *cassandra_init(str *url) { return cachedb_do_init(url,cassandra_init_connection); } void cassandra_free_connection(cachedb_pool_con *con) { cassandra_con * c; CassandraConnection *c_con; if (!con) return; c = (cassandra_con *)con; c_con = (CassandraConnection *)c->cass_con; c_con->cassandra_close(); delete c_con; pkg_free(c); } void cassandra_destroy(cachedb_con *con) { cachedb_do_close(con,cassandra_free_connection); } int cassandra_get(cachedb_con *connection,str *attr,str *val) { cassandra_con *con; CassandraConnection *c_con; char* col_val; int len; string col_name(attr->s,attr->len); if (!attr || !val || !connection) { LM_ERR("null parameter\n"); return -1; } con = (cassandra_con *)connection->data; c_con = (CassandraConnection *)con->cass_con; col_val=c_con->cassandra_simple_get(col_name); if (col_val == NULL) { LM_ERR("failed to fetch Cassandra value\n"); return -1; } if (col_val == (char *)-1) { LM_DBG("no such key - %.*s\n",attr->len,attr->s); val->s = NULL; val->len = 0; return -2; } len=strlen(col_val); val->s = (char *)pkg_malloc(len); if (val->s == NULL) { LM_ERR("no more pkg\n"); return -1; } val->len=len; memcpy(val->s,col_val,len); return 0; } int cassandra_get_counter(cachedb_con *connection,str *attr,int *val) { cassandra_con *con; CassandraConnection *c_con; int ret; string col_name(attr->s,attr->len); if (!attr || !val || !connection) { LM_ERR("null parameter\n"); return -1; } con = (cassandra_con *)connection->data; c_con = (CassandraConnection *)con->cass_con; ret=c_con->cassandra_simple_get_counter(col_name,val); if (ret < 0) { LM_ERR("failed to fetch Cassandra value\n"); return -1; } return 0; } int cassandra_set(cachedb_con *connection,str *attr,str *val,int expires) { cassandra_con *con; CassandraConnection *c_con; string col_name(attr->s,attr->len); string col_val(val->s,val->len); int ret; if (!attr || !val || !connection) { LM_ERR("null parameter\n"); return -1; } con = (cassandra_con *)connection->data; c_con = (CassandraConnection *)con->cass_con; ret = c_con->cassandra_simple_insert(col_name,col_val,expires); if (ret<0) { LM_ERR("Failed to insert Cassandra key\n"); return -1; } LM_DBG("Succesful cassandra insert\n"); return 0; } int cassandra_remove(cachedb_con *connection,str *attr) { cassandra_con *con; CassandraConnection *c_con; string col_name(attr->s,attr->len); int ret; if (!attr || !connection) { LM_ERR("null parameter\n"); return -1; } con = (cassandra_con *)connection->data; c_con = (CassandraConnection *)con->cass_con; ret = c_con->cassandra_simple_remove(col_name); if (ret<0) { LM_ERR("Failed to remove Cassandra key\n"); return -1; } LM_DBG("Succesful cassandra remove\n"); return 0; } int cassandra_add(cachedb_con *connection,str *attr,int val,int expires,int *new_val) { cassandra_con *con; CassandraConnection *c_con; string col_name(attr->s,attr->len); int ret; if (!attr || !connection) { LM_ERR("null parameter\n"); return -1; } con = (cassandra_con *)connection->data; c_con = (CassandraConnection *)con->cass_con; /* TODO - so far no support for expiring counters */ ret = c_con->cassandra_simple_add(col_name,val); if (ret<0) { LM_ERR("Failed to add Cassandra key\n"); return -1; } LM_DBG("Succesful cassandra add\n"); ret=c_con->cassandra_simple_get_counter(col_name,new_val); if (ret < 0) { LM_ERR("failed to fetch Cassandra value\n"); return -1; } if (ret > 0) { LM_DBG("no such key - %.*s\n",attr->len,attr->s); return -2; } return 0; } int cassandra_sub(cachedb_con *connection,str *attr,int val,int expires,int *new_val) { cassandra_con *con; CassandraConnection *c_con; string col_name(attr->s,attr->len); int ret; if (!attr || !connection) { LM_ERR("null parameter\n"); return -1; } con = (cassandra_con *)connection->data; c_con = (CassandraConnection *)con->cass_con; /* TODO - so far no support for expiring counters */ ret = c_con->cassandra_simple_sub(col_name,val); if (ret<0) { LM_ERR("Failed to add Cassandra key\n"); return -1; } LM_DBG("Succesful cassandra sub\n"); ret=c_con->cassandra_simple_get_counter(col_name,new_val); if (ret < 0) { LM_ERR("failed to fetch Cassandra value\n"); return -1; } if (ret > 0) { LM_DBG("no such key - %.*s\n",attr->len,attr->s); return -2; } return 0; } opensips-2.2.2/modules/cachedb_cassandra/cachedb_cassandra_dbase.h000066400000000000000000000037161300170765700253310ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-12-xx created (vlad-paiu) */ #ifndef CACHEDBCASSANDRA_DBASE_H #define CACHEDBCASSANDRA_DBASE_H #ifdef __cplusplus extern "C" { #endif /* give up on ut.h, don't need it anyway. Also give up on inline stuff */ #define ut_h #ifdef __cplusplus #define inline #endif #include "../../str.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../cachedb/cachedb.h" #include "../../cachedb/cachedb_cap.h" #ifdef __cplusplus #undef inline #endif typedef struct { struct cachedb_id *id; unsigned int ref; struct cachedb_pool_con_t *next; void *cass_con; } cassandra_con; cachedb_con* cassandra_init(str *url); void cassandra_destroy(cachedb_con *con); int cassandra_get(cachedb_con *con,str *attr,str *val); int cassandra_get_counter(cachedb_con *con,str *attr,int *val); int cassandra_set(cachedb_con *con,str *attr,str *val,int expires); int cassandra_remove(cachedb_con *con,str *attr); int cassandra_add(cachedb_con *connection,str *attr,int val,int expires,int *new_val); int cassandra_sub(cachedb_con *connection,str *attr,int val,int expires,int *new_val); #ifdef __cplusplus } #endif #endif /* CACHEDBREDIS_DBASE_H */ opensips-2.2.2/modules/cachedb_cassandra/cachedb_cassandra_lib.h000066400000000000000000000235401300170765700250160ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-12-xx created (vlad-paiu) */ #include #include #include #include #include #include #include #include #include #include "Cassandra.h" extern "C" { /* give up on ut.h, don't need it anyway. Also give up on inline stuff */ #define ut_h #define inline #include "../../str.h" #include "../../dprint.h" #undef inline } using namespace std; using namespace boost; using namespace apache::thrift; using namespace apache::thrift::protocol; using namespace apache::thrift::transport; using namespace org::apache::cassandra; class CassandraConnection { private: string keyspace; string column_family; string counter_family; string host; int port; int conn_to; int snd_to; int rcv_to; ConsistencyLevel::type rd_level; ConsistencyLevel::type wr_level; /* actual connection to Cassandra */ CassandraClient* client; protected: /* generate a timestamp in ms. Thrift stuff :| */ long int make_cassandra_timestamp() const { struct timeval tv; long microseconds; gettimeofday(&tv, NULL); microseconds = (tv.tv_sec * 1000000) + tv.tv_usec; return microseconds; } public: CassandraConnection(const string& keyspace, const string& column_family,const string& counter_family) : keyspace(keyspace), column_family(column_family), counter_family(counter_family), host(""), port(0), client(NULL) { } virtual ~CassandraConnection() { cassandra_close(); } int cassandra_open(const string& host, int port, int connection_timeout,int receive_timeout,int send_timeout, int read_cs_level,int write_cs_level) { /* save host & port */ this->host = host; this->port = port; this->conn_to = connection_timeout; this->rcv_to = receive_timeout; this->snd_to = send_timeout; this->rd_level = (ConsistencyLevel::type)read_cs_level; this->wr_level = (ConsistencyLevel::type)write_cs_level; try { /* Create actual Thrift transport & protocol */ boost::shared_ptr socket(new TSocket(host, port)); boost::shared_ptr transport(new TFramedTransport(socket)); boost::shared_ptr protocol(new TBinaryProtocol(transport)); string version; /* Set all timeouts so we don't get stuck for too long */ socket->setConnTimeout(connection_timeout); socket->setRecvTimeout(receive_timeout); socket->setSendTimeout(send_timeout); /* create CassandraClient object */ if (!(client = new CassandraClient(protocol))) { LM_ERR("Failed to create CassandraClienta\n"); return -1; } /* open actual transport connection */ transport->open(); if (!transport->isOpen()) { LM_ERR("Failed to open transport to Cassandra\n"); return -1; } /* set Keyspace & get version for test purposes */ client->set_keyspace(keyspace); client->describe_version(version); LM_DBG("Opened connection for KeySpace [%s]." " Cassandra version = [%s]\n", keyspace.c_str(), version.c_str()); return 0; } catch (InvalidRequestException &ire) { LM_ERR("ERROR1: %s\n", ire.why.c_str()); } catch (TException &tx) { LM_ERR("ERROR2: %s\n", tx.what()); } catch (std::exception &e) { LM_ERR("ERROR3: %s\n", e.what()); } /* We failed. try & cleanup */ cassandra_close(); return -1; } void cassandra_close() { if (client) { try { delete client; LM_DBG("Cassandra connection closed\n"); } catch (TException &tx) { LM_ERR("ERROR: %s\n", tx.what()); } catch (std::exception &e) { LM_ERR("ERROR: %s\n", e.what()); } client = NULL; } } int cassandra_reopen() { cassandra_close(); return cassandra_open(host, port,conn_to,snd_to,rcv_to,rd_level,wr_level); } char* cassandra_simple_get(const string& attr) { int retry=2; if (client == NULL && cassandra_reopen() != 0) { LM_ERR("No cassandra connection\n"); return NULL; } do { try { ColumnPath cp; /* TODO - hard code this ? */ string key = "opensips"; cp.__isset.column = true; cp.column = key; cp.column_family = column_family.c_str(); cp.super_column = ""; ColumnOrSuperColumn sc; client->get(sc, attr, cp, rd_level); return (char *)sc.column.value.c_str(); } catch (InvalidRequestException &ire) { LM_ERR("ERROR1: %s\n", ire.why.c_str()); } catch (NotFoundException &nfx) { /* signal that it was a success, but not found in back-end */ return (char*)-1; } catch (TException &tx) { LM_ERR("ERROR2: %s\n", tx.what()); } catch (std::exception &e) { LM_ERR("ERROR3: %s\n", e.what()); } } while (retry-- && cassandra_reopen() == 0); LM_ERR("giving up on query\n"); return NULL; } int cassandra_simple_get_counter(const string& attr,int *value) { int retry=2; if (client == NULL && cassandra_reopen() != 0) { LM_ERR("No cassandra connection\n"); return -1; } do { try { ColumnPath cp; /* TODO - hard code this ? */ string key = "opensips"; cp.__isset.column = true; cp.column = key; cp.column_family = counter_family.c_str(); cp.super_column = ""; ColumnOrSuperColumn sc; client->get(sc, attr, cp, rd_level); if (value) *value = (int)(sc.counter_column.value); return 0; } catch (InvalidRequestException &ire) { LM_ERR("ERROR1: %s\n", ire.why.c_str()); } catch (NotFoundException &nfx) { /* if counter not found as set, return a 0 value */ if (value) *value=0; return 0; } catch (TException &tx) { LM_ERR("ERROR2: %s\n", tx.what()); } catch (std::exception &e) { LM_ERR("ERROR3: %s\n", e.what()); } } while (retry-- && cassandra_reopen() == 0); LM_ERR("giving up on query\n"); return -1; } int cassandra_simple_insert(const string& name,const string& val, int expires) { int retry=2; if (client == NULL && cassandra_reopen() != 0) { LM_ERR("No cassandra connection\n"); return -1; } do { try { /* TODO - hard code this ? */ string key = "opensips"; ColumnParent cp; cp.column_family = column_family.c_str(); Column c; c.name=key; c.value=val.c_str(); c.__isset.value = true; c.timestamp=make_cassandra_timestamp(); c.__isset.timestamp = true; if (expires > 0) { c.ttl=expires; c.__isset.ttl = true; } LM_DBG("inserting [%s] - [%s]\n",name.c_str(),val.c_str()); client->insert(name, cp,c,wr_level); return 0; } catch (InvalidRequestException &ire) { LM_ERR("ERROR: %s\n", ire.why.c_str()); } catch (TException &tx) { LM_ERR("ERROR: %s\n", tx.what()); } catch (std::exception &e) { LM_ERR("ERROR: %s\n", e.what()); } } while (retry-- && cassandra_reopen() == 0); LM_ERR("giving up on query\n"); return -1; } int cassandra_simple_remove(const string& name) { int retry=2; if (client == NULL && cassandra_reopen() != 0) { LM_ERR("No cassandra connection\n"); return -1; } do { try { /* TODO - hard code this ? */ string key = "opensips"; ColumnPath cp; cp.column_family = column_family.c_str(); cp.column=key; cp.__isset.column = true; LM_DBG("removing [%s]\n",name.c_str()); client->remove(name, cp,make_cassandra_timestamp(),wr_level); return 0; } catch (InvalidRequestException &ire) { LM_ERR("ERROR: %s\n", ire.why.c_str()); } catch (NotFoundException &nfx) { /* success */ return 0; } catch (TException &tx) { LM_ERR("ERROR: %s\n", tx.what()); } catch (std::exception &e) { LM_ERR("ERROR: %s\n", e.what()); } } while (retry-- && cassandra_reopen() == 0); LM_ERR("giving up on query\n"); return -1; } int cassandra_simple_add(const string& name,int val) { int retry=2; if (client == NULL && cassandra_reopen() != 0) { LM_ERR("No cassandra connection\n"); return -1; } do { try { /* TODO - hard code this ? */ string key = "opensips"; ColumnParent cp; cp.column_family = counter_family.c_str(); //cp.__isset.column = true; CounterColumn cc; cc.name = key; cc.value = val; client->add(name, cp,cc,wr_level); return 0; } catch (InvalidRequestException &ire) { LM_ERR("ERROR1: %s\n", ire.why.c_str()); } catch (TException &tx) { LM_ERR("ERROR2: %s\n", tx.what()); } catch (std::exception &e) { LM_ERR("ERROR3: %s\n", e.what()); } } while (retry-- && cassandra_reopen() == 0); LM_ERR("giving up on query\n"); return -1; } int cassandra_simple_sub(const string& name,int val) { int retry=2; if (client == NULL && cassandra_reopen() != 0) { LM_ERR("No cassandra connection\n"); return -1; } do { try { /* TODO - hard code this ? */ string key = "opensips"; ColumnParent cp; cp.column_family = counter_family.c_str(); //cp.__isset.column = true; CounterColumn cc; cc.name = key; cc.value = -val; client->add(name, cp,cc,wr_level); return 0; } catch (InvalidRequestException &ire) { LM_ERR("ERROR1: %s\n", ire.why.c_str()); } catch (TException &tx) { LM_ERR("ERROR2: %s\n", tx.what()); } catch (std::exception &e) { LM_ERR("ERROR3: %s\n", e.what()); } } while (retry-- && cassandra_reopen() == 0); LM_ERR("giving up on query\n"); return -1; } }; opensips-2.2.2/modules/cachedb_cassandra/cassandra_constants.cpp000066400000000000000000000005171300170765700251650ustar00rootroot00000000000000/** * Autogenerated by Thrift * * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING */ #include "cassandra_constants.h" namespace org { namespace apache { namespace cassandra { const cassandraConstants g_cassandra_constants; cassandraConstants::cassandraConstants() { VERSION = "19.19.0"; } }}} // namespace opensips-2.2.2/modules/cachedb_cassandra/cassandra_constants.h000066400000000000000000000006501300170765700246300ustar00rootroot00000000000000/** * Autogenerated by Thrift * * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING */ #ifndef cassandra_CONSTANTS_H #define cassandra_CONSTANTS_H #include "cassandra_types.h" namespace org { namespace apache { namespace cassandra { class cassandraConstants { public: cassandraConstants(); std::string VERSION; }; extern const cassandraConstants g_cassandra_constants; }}} // namespace #endif opensips-2.2.2/modules/cachedb_cassandra/cassandra_types.cpp000066400000000000000000003125111300170765700243150ustar00rootroot00000000000000/** * Autogenerated by Thrift * * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING */ #include "cassandra_types.h" namespace org { namespace apache { namespace cassandra { int _kConsistencyLevelValues[] = { ConsistencyLevel::ONE, ConsistencyLevel::QUORUM, ConsistencyLevel::LOCAL_QUORUM, ConsistencyLevel::EACH_QUORUM, ConsistencyLevel::ALL, ConsistencyLevel::ANY, ConsistencyLevel::TWO, ConsistencyLevel::THREE }; const char* _kConsistencyLevelNames[] = { "ONE", "QUORUM", "LOCAL_QUORUM", "EACH_QUORUM", "ALL", "ANY", "TWO", "THREE" }; const std::map _ConsistencyLevel_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(8, _kConsistencyLevelValues, _kConsistencyLevelNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); int _kIndexOperatorValues[] = { IndexOperator::EQ, IndexOperator::GTE, IndexOperator::GT, IndexOperator::LTE, IndexOperator::LT }; const char* _kIndexOperatorNames[] = { "EQ", "GTE", "GT", "LTE", "LT" }; const std::map _IndexOperator_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(5, _kIndexOperatorValues, _kIndexOperatorNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); int _kIndexTypeValues[] = { IndexType::KEYS, IndexType::CUSTOM }; const char* _kIndexTypeNames[] = { "KEYS", "CUSTOM" }; const std::map _IndexType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(2, _kIndexTypeValues, _kIndexTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); int _kCompressionValues[] = { Compression::GZIP, Compression::NONE }; const char* _kCompressionNames[] = { "GZIP", "NONE" }; const std::map _Compression_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(2, _kCompressionValues, _kCompressionNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); int _kCqlResultTypeValues[] = { CqlResultType::ROWS, CqlResultType::VOID, CqlResultType::INT }; const char* _kCqlResultTypeNames[] = { "ROWS", "VOID", "INT" }; const std::map _CqlResultType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(3, _kCqlResultTypeValues, _kCqlResultTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); const char* Column::ascii_fingerprint = "3EE0E1C5C844001B62F08125068292CC"; const uint8_t Column::binary_fingerprint[16] = {0x3E,0xE0,0xE1,0xC5,0xC8,0x44,0x00,0x1B,0x62,0xF0,0x81,0x25,0x06,0x82,0x92,0xCC}; uint32_t Column::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_name = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->name); isset_name = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->value); this->__isset.value = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_I64) { xfer += iprot->readI64(this->timestamp); this->__isset.timestamp = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->ttl); this->__isset.ttl = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_name) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t Column::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Column"); xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->name); xfer += oprot->writeFieldEnd(); if (this->__isset.value) { xfer += oprot->writeFieldBegin("value", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeBinary(this->value); xfer += oprot->writeFieldEnd(); } if (this->__isset.timestamp) { xfer += oprot->writeFieldBegin("timestamp", ::apache::thrift::protocol::T_I64, 3); xfer += oprot->writeI64(this->timestamp); xfer += oprot->writeFieldEnd(); } if (this->__isset.ttl) { xfer += oprot->writeFieldBegin("ttl", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32(this->ttl); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* SuperColumn::ascii_fingerprint = "470EFC558004E98D92D604898305C04E"; const uint8_t SuperColumn::binary_fingerprint[16] = {0x47,0x0E,0xFC,0x55,0x80,0x04,0xE9,0x8D,0x92,0xD6,0x04,0x89,0x83,0x05,0xC0,0x4E}; uint32_t SuperColumn::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_name = false; bool isset_columns = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->name); isset_name = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->columns.clear(); uint32_t _size0; ::apache::thrift::protocol::TType _etype3; iprot->readListBegin(_etype3, _size0); this->columns.resize(_size0); uint32_t _i4; for (_i4 = 0; _i4 < _size0; ++_i4) { xfer += this->columns[_i4].read(iprot); } iprot->readListEnd(); } isset_columns = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_name) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_columns) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t SuperColumn::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("SuperColumn"); xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->name); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("columns", ::apache::thrift::protocol::T_LIST, 2); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->columns.size()); std::vector ::const_iterator _iter5; for (_iter5 = this->columns.begin(); _iter5 != this->columns.end(); ++_iter5) { xfer += (*_iter5).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* CounterColumn::ascii_fingerprint = "1CCCF6FC31CFD1D61BBBB1BAF3590620"; const uint8_t CounterColumn::binary_fingerprint[16] = {0x1C,0xCC,0xF6,0xFC,0x31,0xCF,0xD1,0xD6,0x1B,0xBB,0xB1,0xBA,0xF3,0x59,0x06,0x20}; uint32_t CounterColumn::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_name = false; bool isset_value = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->name); isset_name = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_I64) { xfer += iprot->readI64(this->value); isset_value = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_name) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_value) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t CounterColumn::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("CounterColumn"); xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->name); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("value", ::apache::thrift::protocol::T_I64, 2); xfer += oprot->writeI64(this->value); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* CounterSuperColumn::ascii_fingerprint = "CD4C8C4BF7753E46DE417CDE369343A4"; const uint8_t CounterSuperColumn::binary_fingerprint[16] = {0xCD,0x4C,0x8C,0x4B,0xF7,0x75,0x3E,0x46,0xDE,0x41,0x7C,0xDE,0x36,0x93,0x43,0xA4}; uint32_t CounterSuperColumn::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_name = false; bool isset_columns = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->name); isset_name = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->columns.clear(); uint32_t _size6; ::apache::thrift::protocol::TType _etype9; iprot->readListBegin(_etype9, _size6); this->columns.resize(_size6); uint32_t _i10; for (_i10 = 0; _i10 < _size6; ++_i10) { xfer += this->columns[_i10].read(iprot); } iprot->readListEnd(); } isset_columns = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_name) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_columns) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t CounterSuperColumn::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("CounterSuperColumn"); xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->name); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("columns", ::apache::thrift::protocol::T_LIST, 2); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->columns.size()); std::vector ::const_iterator _iter11; for (_iter11 = this->columns.begin(); _iter11 != this->columns.end(); ++_iter11) { xfer += (*_iter11).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* ColumnOrSuperColumn::ascii_fingerprint = "2B34AC9E80F1DAA3A2A63B1AB1841E61"; const uint8_t ColumnOrSuperColumn::binary_fingerprint[16] = {0x2B,0x34,0xAC,0x9E,0x80,0xF1,0xDA,0xA3,0xA2,0xA6,0x3B,0x1A,0xB1,0x84,0x1E,0x61}; uint32_t ColumnOrSuperColumn::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column.read(iprot); this->__isset.column = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->super_column.read(iprot); this->__isset.super_column = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->counter_column.read(iprot); this->__isset.counter_column = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->counter_super_column.read(iprot); this->__isset.counter_super_column = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t ColumnOrSuperColumn::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("ColumnOrSuperColumn"); if (this->__isset.column) { xfer += oprot->writeFieldBegin("column", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->column.write(oprot); xfer += oprot->writeFieldEnd(); } if (this->__isset.super_column) { xfer += oprot->writeFieldBegin("super_column", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->super_column.write(oprot); xfer += oprot->writeFieldEnd(); } if (this->__isset.counter_column) { xfer += oprot->writeFieldBegin("counter_column", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->counter_column.write(oprot); xfer += oprot->writeFieldEnd(); } if (this->__isset.counter_super_column) { xfer += oprot->writeFieldBegin("counter_super_column", ::apache::thrift::protocol::T_STRUCT, 4); xfer += this->counter_super_column.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* NotFoundException::ascii_fingerprint = "99914B932BD37A50B983C5E7C90AE93B"; const uint8_t NotFoundException::binary_fingerprint[16] = {0x99,0x91,0x4B,0x93,0x2B,0xD3,0x7A,0x50,0xB9,0x83,0xC5,0xE7,0xC9,0x0A,0xE9,0x3B}; uint32_t NotFoundException::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t NotFoundException::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("NotFoundException"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* InvalidRequestException::ascii_fingerprint = "EFB929595D312AC8F305D5A794CFEDA1"; const uint8_t InvalidRequestException::binary_fingerprint[16] = {0xEF,0xB9,0x29,0x59,0x5D,0x31,0x2A,0xC8,0xF3,0x05,0xD5,0xA7,0x94,0xCF,0xED,0xA1}; uint32_t InvalidRequestException::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_why = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->why); isset_why = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_why) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t InvalidRequestException::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("InvalidRequestException"); xfer += oprot->writeFieldBegin("why", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->why); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* UnavailableException::ascii_fingerprint = "99914B932BD37A50B983C5E7C90AE93B"; const uint8_t UnavailableException::binary_fingerprint[16] = {0x99,0x91,0x4B,0x93,0x2B,0xD3,0x7A,0x50,0xB9,0x83,0xC5,0xE7,0xC9,0x0A,0xE9,0x3B}; uint32_t UnavailableException::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t UnavailableException::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("UnavailableException"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* TimedOutException::ascii_fingerprint = "99914B932BD37A50B983C5E7C90AE93B"; const uint8_t TimedOutException::binary_fingerprint[16] = {0x99,0x91,0x4B,0x93,0x2B,0xD3,0x7A,0x50,0xB9,0x83,0xC5,0xE7,0xC9,0x0A,0xE9,0x3B}; uint32_t TimedOutException::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t TimedOutException::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("TimedOutException"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* AuthenticationException::ascii_fingerprint = "EFB929595D312AC8F305D5A794CFEDA1"; const uint8_t AuthenticationException::binary_fingerprint[16] = {0xEF,0xB9,0x29,0x59,0x5D,0x31,0x2A,0xC8,0xF3,0x05,0xD5,0xA7,0x94,0xCF,0xED,0xA1}; uint32_t AuthenticationException::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_why = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->why); isset_why = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_why) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t AuthenticationException::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("AuthenticationException"); xfer += oprot->writeFieldBegin("why", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->why); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* AuthorizationException::ascii_fingerprint = "EFB929595D312AC8F305D5A794CFEDA1"; const uint8_t AuthorizationException::binary_fingerprint[16] = {0xEF,0xB9,0x29,0x59,0x5D,0x31,0x2A,0xC8,0xF3,0x05,0xD5,0xA7,0x94,0xCF,0xED,0xA1}; uint32_t AuthorizationException::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_why = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->why); isset_why = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_why) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t AuthorizationException::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("AuthorizationException"); xfer += oprot->writeFieldBegin("why", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->why); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* SchemaDisagreementException::ascii_fingerprint = "99914B932BD37A50B983C5E7C90AE93B"; const uint8_t SchemaDisagreementException::binary_fingerprint[16] = {0x99,0x91,0x4B,0x93,0x2B,0xD3,0x7A,0x50,0xB9,0x83,0xC5,0xE7,0xC9,0x0A,0xE9,0x3B}; uint32_t SchemaDisagreementException::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t SchemaDisagreementException::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("SchemaDisagreementException"); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* ColumnParent::ascii_fingerprint = "0A13AE61181713A4100DFFB3EC293822"; const uint8_t ColumnParent::binary_fingerprint[16] = {0x0A,0x13,0xAE,0x61,0x18,0x17,0x13,0xA4,0x10,0x0D,0xFF,0xB3,0xEC,0x29,0x38,0x22}; uint32_t ColumnParent::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_column_family = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 3: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->column_family); isset_column_family = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->super_column); this->__isset.super_column = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_column_family) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t ColumnParent::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("ColumnParent"); xfer += oprot->writeFieldBegin("column_family", ::apache::thrift::protocol::T_STRING, 3); xfer += oprot->writeString(this->column_family); xfer += oprot->writeFieldEnd(); if (this->__isset.super_column) { xfer += oprot->writeFieldBegin("super_column", ::apache::thrift::protocol::T_STRING, 4); xfer += oprot->writeBinary(this->super_column); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* ColumnPath::ascii_fingerprint = "606212895BCF63C757913CF35AEB3462"; const uint8_t ColumnPath::binary_fingerprint[16] = {0x60,0x62,0x12,0x89,0x5B,0xCF,0x63,0xC7,0x57,0x91,0x3C,0xF3,0x5A,0xEB,0x34,0x62}; uint32_t ColumnPath::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_column_family = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 3: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->column_family); isset_column_family = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->super_column); this->__isset.super_column = true; } else { xfer += iprot->skip(ftype); } break; case 5: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->column); this->__isset.column = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_column_family) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t ColumnPath::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("ColumnPath"); xfer += oprot->writeFieldBegin("column_family", ::apache::thrift::protocol::T_STRING, 3); xfer += oprot->writeString(this->column_family); xfer += oprot->writeFieldEnd(); if (this->__isset.super_column) { xfer += oprot->writeFieldBegin("super_column", ::apache::thrift::protocol::T_STRING, 4); xfer += oprot->writeBinary(this->super_column); xfer += oprot->writeFieldEnd(); } if (this->__isset.column) { xfer += oprot->writeFieldBegin("column", ::apache::thrift::protocol::T_STRING, 5); xfer += oprot->writeBinary(this->column); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* SliceRange::ascii_fingerprint = "184D24C9A0B8D4415E234DB649CAE740"; const uint8_t SliceRange::binary_fingerprint[16] = {0x18,0x4D,0x24,0xC9,0xA0,0xB8,0xD4,0x41,0x5E,0x23,0x4D,0xB6,0x49,0xCA,0xE7,0x40}; uint32_t SliceRange::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_start = false; bool isset_finish = false; bool isset_reversed = false; bool isset_count = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->start); isset_start = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->finish); isset_finish = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_BOOL) { xfer += iprot->readBool(this->reversed); isset_reversed = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->count); isset_count = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_start) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_finish) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_reversed) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_count) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t SliceRange::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("SliceRange"); xfer += oprot->writeFieldBegin("start", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->start); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("finish", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeBinary(this->finish); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("reversed", ::apache::thrift::protocol::T_BOOL, 3); xfer += oprot->writeBool(this->reversed); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("count", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32(this->count); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* SlicePredicate::ascii_fingerprint = "F59D1D81C17DFFAF09988BF1C9CE5E27"; const uint8_t SlicePredicate::binary_fingerprint[16] = {0xF5,0x9D,0x1D,0x81,0xC1,0x7D,0xFF,0xAF,0x09,0x98,0x8B,0xF1,0xC9,0xCE,0x5E,0x27}; uint32_t SlicePredicate::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->column_names.clear(); uint32_t _size12; ::apache::thrift::protocol::TType _etype15; iprot->readListBegin(_etype15, _size12); this->column_names.resize(_size12); uint32_t _i16; for (_i16 = 0; _i16 < _size12; ++_i16) { xfer += iprot->readBinary(this->column_names[_i16]); } iprot->readListEnd(); } this->__isset.column_names = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->slice_range.read(iprot); this->__isset.slice_range = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t SlicePredicate::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("SlicePredicate"); if (this->__isset.column_names) { xfer += oprot->writeFieldBegin("column_names", ::apache::thrift::protocol::T_LIST, 1); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, this->column_names.size()); std::vector ::const_iterator _iter17; for (_iter17 = this->column_names.begin(); _iter17 != this->column_names.end(); ++_iter17) { xfer += oprot->writeBinary((*_iter17)); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } if (this->__isset.slice_range) { xfer += oprot->writeFieldBegin("slice_range", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->slice_range.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* IndexExpression::ascii_fingerprint = "D9F4CFE2F293A8B1052FD3031DD2C847"; const uint8_t IndexExpression::binary_fingerprint[16] = {0xD9,0xF4,0xCF,0xE2,0xF2,0x93,0xA8,0xB1,0x05,0x2F,0xD3,0x03,0x1D,0xD2,0xC8,0x47}; uint32_t IndexExpression::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_column_name = false; bool isset_op = false; bool isset_value = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->column_name); isset_column_name = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast18; xfer += iprot->readI32(ecast18); this->op = (IndexOperator::type)ecast18; isset_op = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->value); isset_value = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_column_name) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_op) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_value) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t IndexExpression::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("IndexExpression"); xfer += oprot->writeFieldBegin("column_name", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->column_name); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("op", ::apache::thrift::protocol::T_I32, 2); xfer += oprot->writeI32((int32_t)this->op); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("value", ::apache::thrift::protocol::T_STRING, 3); xfer += oprot->writeBinary(this->value); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* IndexClause::ascii_fingerprint = "9B551B9AB86120B0EEA9005C77FD3C1F"; const uint8_t IndexClause::binary_fingerprint[16] = {0x9B,0x55,0x1B,0x9A,0xB8,0x61,0x20,0xB0,0xEE,0xA9,0x00,0x5C,0x77,0xFD,0x3C,0x1F}; uint32_t IndexClause::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_expressions = false; bool isset_start_key = false; bool isset_count = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->expressions.clear(); uint32_t _size19; ::apache::thrift::protocol::TType _etype22; iprot->readListBegin(_etype22, _size19); this->expressions.resize(_size19); uint32_t _i23; for (_i23 = 0; _i23 < _size19; ++_i23) { xfer += this->expressions[_i23].read(iprot); } iprot->readListEnd(); } isset_expressions = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->start_key); isset_start_key = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->count); isset_count = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_expressions) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_start_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_count) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t IndexClause::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("IndexClause"); xfer += oprot->writeFieldBegin("expressions", ::apache::thrift::protocol::T_LIST, 1); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->expressions.size()); std::vector ::const_iterator _iter24; for (_iter24 = this->expressions.begin(); _iter24 != this->expressions.end(); ++_iter24) { xfer += (*_iter24).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("start_key", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeBinary(this->start_key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("count", ::apache::thrift::protocol::T_I32, 3); xfer += oprot->writeI32(this->count); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* KeyRange::ascii_fingerprint = "8F248C09AF1EC3656ABD8565EA1F59C1"; const uint8_t KeyRange::binary_fingerprint[16] = {0x8F,0x24,0x8C,0x09,0xAF,0x1E,0xC3,0x65,0x6A,0xBD,0x85,0x65,0xEA,0x1F,0x59,0xC1}; uint32_t KeyRange::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_count = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->start_key); this->__isset.start_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->end_key); this->__isset.end_key = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->start_token); this->__isset.start_token = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->end_token); this->__isset.end_token = true; } else { xfer += iprot->skip(ftype); } break; case 5: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->count); isset_count = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_count) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t KeyRange::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("KeyRange"); if (this->__isset.start_key) { xfer += oprot->writeFieldBegin("start_key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->start_key); xfer += oprot->writeFieldEnd(); } if (this->__isset.end_key) { xfer += oprot->writeFieldBegin("end_key", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeBinary(this->end_key); xfer += oprot->writeFieldEnd(); } if (this->__isset.start_token) { xfer += oprot->writeFieldBegin("start_token", ::apache::thrift::protocol::T_STRING, 3); xfer += oprot->writeString(this->start_token); xfer += oprot->writeFieldEnd(); } if (this->__isset.end_token) { xfer += oprot->writeFieldBegin("end_token", ::apache::thrift::protocol::T_STRING, 4); xfer += oprot->writeString(this->end_token); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldBegin("count", ::apache::thrift::protocol::T_I32, 5); xfer += oprot->writeI32(this->count); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* KeySlice::ascii_fingerprint = "D1568675B0C135C909E3169B72A4DA3D"; const uint8_t KeySlice::binary_fingerprint[16] = {0xD1,0x56,0x86,0x75,0xB0,0xC1,0x35,0xC9,0x09,0xE3,0x16,0x9B,0x72,0xA4,0xDA,0x3D}; uint32_t KeySlice::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_columns = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->columns.clear(); uint32_t _size25; ::apache::thrift::protocol::TType _etype28; iprot->readListBegin(_etype28, _size25); this->columns.resize(_size25); uint32_t _i29; for (_i29 = 0; _i29 < _size25; ++_i29) { xfer += this->columns[_i29].read(iprot); } iprot->readListEnd(); } isset_columns = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_columns) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t KeySlice::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("KeySlice"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("columns", ::apache::thrift::protocol::T_LIST, 2); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->columns.size()); std::vector ::const_iterator _iter30; for (_iter30 = this->columns.begin(); _iter30 != this->columns.end(); ++_iter30) { xfer += (*_iter30).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* KeyCount::ascii_fingerprint = "EEBC915CE44901401D881E6091423036"; const uint8_t KeyCount::binary_fingerprint[16] = {0xEE,0xBC,0x91,0x5C,0xE4,0x49,0x01,0x40,0x1D,0x88,0x1E,0x60,0x91,0x42,0x30,0x36}; uint32_t KeyCount::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_count = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->count); isset_count = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_count) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t KeyCount::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("KeyCount"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("count", ::apache::thrift::protocol::T_I32, 2); xfer += oprot->writeI32(this->count); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* Deletion::ascii_fingerprint = "40F33ECF1C932CA77C2414C4E6C60CBE"; const uint8_t Deletion::binary_fingerprint[16] = {0x40,0xF3,0x3E,0xCF,0x1C,0x93,0x2C,0xA7,0x7C,0x24,0x14,0xC4,0xE6,0xC6,0x0C,0xBE}; uint32_t Deletion::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_I64) { xfer += iprot->readI64(this->timestamp); this->__isset.timestamp = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->super_column); this->__isset.super_column = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->predicate.read(iprot); this->__isset.predicate = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Deletion::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Deletion"); if (this->__isset.timestamp) { xfer += oprot->writeFieldBegin("timestamp", ::apache::thrift::protocol::T_I64, 1); xfer += oprot->writeI64(this->timestamp); xfer += oprot->writeFieldEnd(); } if (this->__isset.super_column) { xfer += oprot->writeFieldBegin("super_column", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeBinary(this->super_column); xfer += oprot->writeFieldEnd(); } if (this->__isset.predicate) { xfer += oprot->writeFieldBegin("predicate", ::apache::thrift::protocol::T_STRUCT, 3); xfer += this->predicate.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* Mutation::ascii_fingerprint = "E8B65DF3979C6868F80DF81F8E769E63"; const uint8_t Mutation::binary_fingerprint[16] = {0xE8,0xB6,0x5D,0xF3,0x97,0x9C,0x68,0x68,0xF8,0x0D,0xF8,0x1F,0x8E,0x76,0x9E,0x63}; uint32_t Mutation::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->column_or_supercolumn.read(iprot); this->__isset.column_or_supercolumn = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->deletion.read(iprot); this->__isset.deletion = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t Mutation::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("Mutation"); if (this->__isset.column_or_supercolumn) { xfer += oprot->writeFieldBegin("column_or_supercolumn", ::apache::thrift::protocol::T_STRUCT, 1); xfer += this->column_or_supercolumn.write(oprot); xfer += oprot->writeFieldEnd(); } if (this->__isset.deletion) { xfer += oprot->writeFieldBegin("deletion", ::apache::thrift::protocol::T_STRUCT, 2); xfer += this->deletion.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* EndpointDetails::ascii_fingerprint = "F4A50F0EC638C7F66026F9B6678FD89B"; const uint8_t EndpointDetails::binary_fingerprint[16] = {0xF4,0xA5,0x0F,0x0E,0xC6,0x38,0xC7,0xF6,0x60,0x26,0xF9,0xB6,0x67,0x8F,0xD8,0x9B}; uint32_t EndpointDetails::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->host); this->__isset.host = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->datacenter); this->__isset.datacenter = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->rack); this->__isset.rack = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); return xfer; } uint32_t EndpointDetails::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("EndpointDetails"); xfer += oprot->writeFieldBegin("host", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->host); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("datacenter", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeString(this->datacenter); xfer += oprot->writeFieldEnd(); if (this->__isset.rack) { xfer += oprot->writeFieldBegin("rack", ::apache::thrift::protocol::T_STRING, 3); xfer += oprot->writeString(this->rack); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* TokenRange::ascii_fingerprint = "832268DC4CD6B17EE8881FC57EA04679"; const uint8_t TokenRange::binary_fingerprint[16] = {0x83,0x22,0x68,0xDC,0x4C,0xD6,0xB1,0x7E,0xE8,0x88,0x1F,0xC5,0x7E,0xA0,0x46,0x79}; uint32_t TokenRange::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_start_token = false; bool isset_end_token = false; bool isset_endpoints = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->start_token); isset_start_token = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->end_token); isset_end_token = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->endpoints.clear(); uint32_t _size31; ::apache::thrift::protocol::TType _etype34; iprot->readListBegin(_etype34, _size31); this->endpoints.resize(_size31); uint32_t _i35; for (_i35 = 0; _i35 < _size31; ++_i35) { xfer += iprot->readString(this->endpoints[_i35]); } iprot->readListEnd(); } isset_endpoints = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->rpc_endpoints.clear(); uint32_t _size36; ::apache::thrift::protocol::TType _etype39; iprot->readListBegin(_etype39, _size36); this->rpc_endpoints.resize(_size36); uint32_t _i40; for (_i40 = 0; _i40 < _size36; ++_i40) { xfer += iprot->readString(this->rpc_endpoints[_i40]); } iprot->readListEnd(); } this->__isset.rpc_endpoints = true; } else { xfer += iprot->skip(ftype); } break; case 5: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->endpoint_details.clear(); uint32_t _size41; ::apache::thrift::protocol::TType _etype44; iprot->readListBegin(_etype44, _size41); this->endpoint_details.resize(_size41); uint32_t _i45; for (_i45 = 0; _i45 < _size41; ++_i45) { xfer += this->endpoint_details[_i45].read(iprot); } iprot->readListEnd(); } this->__isset.endpoint_details = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_start_token) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_end_token) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_endpoints) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t TokenRange::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("TokenRange"); xfer += oprot->writeFieldBegin("start_token", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->start_token); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("end_token", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeString(this->end_token); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("endpoints", ::apache::thrift::protocol::T_LIST, 3); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, this->endpoints.size()); std::vector ::const_iterator _iter46; for (_iter46 = this->endpoints.begin(); _iter46 != this->endpoints.end(); ++_iter46) { xfer += oprot->writeString((*_iter46)); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); if (this->__isset.rpc_endpoints) { xfer += oprot->writeFieldBegin("rpc_endpoints", ::apache::thrift::protocol::T_LIST, 4); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, this->rpc_endpoints.size()); std::vector ::const_iterator _iter47; for (_iter47 = this->rpc_endpoints.begin(); _iter47 != this->rpc_endpoints.end(); ++_iter47) { xfer += oprot->writeString((*_iter47)); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } if (this->__isset.endpoint_details) { xfer += oprot->writeFieldBegin("endpoint_details", ::apache::thrift::protocol::T_LIST, 5); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->endpoint_details.size()); std::vector ::const_iterator _iter48; for (_iter48 = this->endpoint_details.begin(); _iter48 != this->endpoint_details.end(); ++_iter48) { xfer += (*_iter48).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* AuthenticationRequest::ascii_fingerprint = "5EA2D527ECA3BA20C77AFC023EE8C05F"; const uint8_t AuthenticationRequest::binary_fingerprint[16] = {0x5E,0xA2,0xD5,0x27,0xEC,0xA3,0xBA,0x20,0xC7,0x7A,0xFC,0x02,0x3E,0xE8,0xC0,0x5F}; uint32_t AuthenticationRequest::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_credentials = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->credentials.clear(); uint32_t _size49; ::apache::thrift::protocol::TType _ktype50; ::apache::thrift::protocol::TType _vtype51; iprot->readMapBegin(_ktype50, _vtype51, _size49); uint32_t _i53; for (_i53 = 0; _i53 < _size49; ++_i53) { std::string _key54; xfer += iprot->readString(_key54); std::string& _val55 = this->credentials[_key54]; xfer += iprot->readString(_val55); } iprot->readMapEnd(); } isset_credentials = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_credentials) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t AuthenticationRequest::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("AuthenticationRequest"); xfer += oprot->writeFieldBegin("credentials", ::apache::thrift::protocol::T_MAP, 1); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_STRING, this->credentials.size()); std::map ::const_iterator _iter56; for (_iter56 = this->credentials.begin(); _iter56 != this->credentials.end(); ++_iter56) { xfer += oprot->writeString(_iter56->first); xfer += oprot->writeString(_iter56->second); } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* ColumnDef::ascii_fingerprint = "0D89CE83D7EDAD079AC3213ED1DCAA58"; const uint8_t ColumnDef::binary_fingerprint[16] = {0x0D,0x89,0xCE,0x83,0xD7,0xED,0xAD,0x07,0x9A,0xC3,0x21,0x3E,0xD1,0xDC,0xAA,0x58}; uint32_t ColumnDef::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_name = false; bool isset_validation_class = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->name); isset_name = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->validation_class); isset_validation_class = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast57; xfer += iprot->readI32(ecast57); this->index_type = (IndexType::type)ecast57; this->__isset.index_type = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->index_name); this->__isset.index_name = true; } else { xfer += iprot->skip(ftype); } break; case 5: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->index_options.clear(); uint32_t _size58; ::apache::thrift::protocol::TType _ktype59; ::apache::thrift::protocol::TType _vtype60; iprot->readMapBegin(_ktype59, _vtype60, _size58); uint32_t _i62; for (_i62 = 0; _i62 < _size58; ++_i62) { std::string _key63; xfer += iprot->readString(_key63); std::string& _val64 = this->index_options[_key63]; xfer += iprot->readString(_val64); } iprot->readMapEnd(); } this->__isset.index_options = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_name) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_validation_class) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t ColumnDef::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("ColumnDef"); xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->name); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("validation_class", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeString(this->validation_class); xfer += oprot->writeFieldEnd(); if (this->__isset.index_type) { xfer += oprot->writeFieldBegin("index_type", ::apache::thrift::protocol::T_I32, 3); xfer += oprot->writeI32((int32_t)this->index_type); xfer += oprot->writeFieldEnd(); } if (this->__isset.index_name) { xfer += oprot->writeFieldBegin("index_name", ::apache::thrift::protocol::T_STRING, 4); xfer += oprot->writeString(this->index_name); xfer += oprot->writeFieldEnd(); } if (this->__isset.index_options) { xfer += oprot->writeFieldBegin("index_options", ::apache::thrift::protocol::T_MAP, 5); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_STRING, this->index_options.size()); std::map ::const_iterator _iter65; for (_iter65 = this->index_options.begin(); _iter65 != this->index_options.end(); ++_iter65) { xfer += oprot->writeString(_iter65->first); xfer += oprot->writeString(_iter65->second); } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* CfDef::ascii_fingerprint = "0FA1E255DEA2A1E59044B44DA8536D18"; const uint8_t CfDef::binary_fingerprint[16] = {0x0F,0xA1,0xE2,0x55,0xDE,0xA2,0xA1,0xE5,0x90,0x44,0xB4,0x4D,0xA8,0x53,0x6D,0x18}; uint32_t CfDef::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_keyspace = false; bool isset_name = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->keyspace); isset_keyspace = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->name); isset_name = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->column_type); this->__isset.column_type = true; } else { xfer += iprot->skip(ftype); } break; case 5: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->comparator_type); this->__isset.comparator_type = true; } else { xfer += iprot->skip(ftype); } break; case 6: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->subcomparator_type); this->__isset.subcomparator_type = true; } else { xfer += iprot->skip(ftype); } break; case 8: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->comment); this->__isset.comment = true; } else { xfer += iprot->skip(ftype); } break; case 9: if (ftype == ::apache::thrift::protocol::T_DOUBLE) { xfer += iprot->readDouble(this->row_cache_size); this->__isset.row_cache_size = true; } else { xfer += iprot->skip(ftype); } break; case 11: if (ftype == ::apache::thrift::protocol::T_DOUBLE) { xfer += iprot->readDouble(this->key_cache_size); this->__isset.key_cache_size = true; } else { xfer += iprot->skip(ftype); } break; case 12: if (ftype == ::apache::thrift::protocol::T_DOUBLE) { xfer += iprot->readDouble(this->read_repair_chance); this->__isset.read_repair_chance = true; } else { xfer += iprot->skip(ftype); } break; case 13: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->column_metadata.clear(); uint32_t _size66; ::apache::thrift::protocol::TType _etype69; iprot->readListBegin(_etype69, _size66); this->column_metadata.resize(_size66); uint32_t _i70; for (_i70 = 0; _i70 < _size66; ++_i70) { xfer += this->column_metadata[_i70].read(iprot); } iprot->readListEnd(); } this->__isset.column_metadata = true; } else { xfer += iprot->skip(ftype); } break; case 14: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->gc_grace_seconds); this->__isset.gc_grace_seconds = true; } else { xfer += iprot->skip(ftype); } break; case 15: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->default_validation_class); this->__isset.default_validation_class = true; } else { xfer += iprot->skip(ftype); } break; case 16: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->id); this->__isset.id = true; } else { xfer += iprot->skip(ftype); } break; case 17: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->min_compaction_threshold); this->__isset.min_compaction_threshold = true; } else { xfer += iprot->skip(ftype); } break; case 18: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->max_compaction_threshold); this->__isset.max_compaction_threshold = true; } else { xfer += iprot->skip(ftype); } break; case 19: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->row_cache_save_period_in_seconds); this->__isset.row_cache_save_period_in_seconds = true; } else { xfer += iprot->skip(ftype); } break; case 20: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->key_cache_save_period_in_seconds); this->__isset.key_cache_save_period_in_seconds = true; } else { xfer += iprot->skip(ftype); } break; case 24: if (ftype == ::apache::thrift::protocol::T_BOOL) { xfer += iprot->readBool(this->replicate_on_write); this->__isset.replicate_on_write = true; } else { xfer += iprot->skip(ftype); } break; case 25: if (ftype == ::apache::thrift::protocol::T_DOUBLE) { xfer += iprot->readDouble(this->merge_shards_chance); this->__isset.merge_shards_chance = true; } else { xfer += iprot->skip(ftype); } break; case 26: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->key_validation_class); this->__isset.key_validation_class = true; } else { xfer += iprot->skip(ftype); } break; case 27: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->row_cache_provider); this->__isset.row_cache_provider = true; } else { xfer += iprot->skip(ftype); } break; case 28: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key_alias); this->__isset.key_alias = true; } else { xfer += iprot->skip(ftype); } break; case 29: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->compaction_strategy); this->__isset.compaction_strategy = true; } else { xfer += iprot->skip(ftype); } break; case 30: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->compaction_strategy_options.clear(); uint32_t _size71; ::apache::thrift::protocol::TType _ktype72; ::apache::thrift::protocol::TType _vtype73; iprot->readMapBegin(_ktype72, _vtype73, _size71); uint32_t _i75; for (_i75 = 0; _i75 < _size71; ++_i75) { std::string _key76; xfer += iprot->readString(_key76); std::string& _val77 = this->compaction_strategy_options[_key76]; xfer += iprot->readString(_val77); } iprot->readMapEnd(); } this->__isset.compaction_strategy_options = true; } else { xfer += iprot->skip(ftype); } break; case 31: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->row_cache_keys_to_save); this->__isset.row_cache_keys_to_save = true; } else { xfer += iprot->skip(ftype); } break; case 32: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->compression_options.clear(); uint32_t _size78; ::apache::thrift::protocol::TType _ktype79; ::apache::thrift::protocol::TType _vtype80; iprot->readMapBegin(_ktype79, _vtype80, _size78); uint32_t _i82; for (_i82 = 0; _i82 < _size78; ++_i82) { std::string _key83; xfer += iprot->readString(_key83); std::string& _val84 = this->compression_options[_key83]; xfer += iprot->readString(_val84); } iprot->readMapEnd(); } this->__isset.compression_options = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_keyspace) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_name) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t CfDef::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("CfDef"); xfer += oprot->writeFieldBegin("keyspace", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->keyspace); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeString(this->name); xfer += oprot->writeFieldEnd(); if (this->__isset.column_type) { xfer += oprot->writeFieldBegin("column_type", ::apache::thrift::protocol::T_STRING, 3); xfer += oprot->writeString(this->column_type); xfer += oprot->writeFieldEnd(); } if (this->__isset.comparator_type) { xfer += oprot->writeFieldBegin("comparator_type", ::apache::thrift::protocol::T_STRING, 5); xfer += oprot->writeString(this->comparator_type); xfer += oprot->writeFieldEnd(); } if (this->__isset.subcomparator_type) { xfer += oprot->writeFieldBegin("subcomparator_type", ::apache::thrift::protocol::T_STRING, 6); xfer += oprot->writeString(this->subcomparator_type); xfer += oprot->writeFieldEnd(); } if (this->__isset.comment) { xfer += oprot->writeFieldBegin("comment", ::apache::thrift::protocol::T_STRING, 8); xfer += oprot->writeString(this->comment); xfer += oprot->writeFieldEnd(); } if (this->__isset.row_cache_size) { xfer += oprot->writeFieldBegin("row_cache_size", ::apache::thrift::protocol::T_DOUBLE, 9); xfer += oprot->writeDouble(this->row_cache_size); xfer += oprot->writeFieldEnd(); } if (this->__isset.key_cache_size) { xfer += oprot->writeFieldBegin("key_cache_size", ::apache::thrift::protocol::T_DOUBLE, 11); xfer += oprot->writeDouble(this->key_cache_size); xfer += oprot->writeFieldEnd(); } if (this->__isset.read_repair_chance) { xfer += oprot->writeFieldBegin("read_repair_chance", ::apache::thrift::protocol::T_DOUBLE, 12); xfer += oprot->writeDouble(this->read_repair_chance); xfer += oprot->writeFieldEnd(); } if (this->__isset.column_metadata) { xfer += oprot->writeFieldBegin("column_metadata", ::apache::thrift::protocol::T_LIST, 13); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->column_metadata.size()); std::vector ::const_iterator _iter85; for (_iter85 = this->column_metadata.begin(); _iter85 != this->column_metadata.end(); ++_iter85) { xfer += (*_iter85).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } if (this->__isset.gc_grace_seconds) { xfer += oprot->writeFieldBegin("gc_grace_seconds", ::apache::thrift::protocol::T_I32, 14); xfer += oprot->writeI32(this->gc_grace_seconds); xfer += oprot->writeFieldEnd(); } if (this->__isset.default_validation_class) { xfer += oprot->writeFieldBegin("default_validation_class", ::apache::thrift::protocol::T_STRING, 15); xfer += oprot->writeString(this->default_validation_class); xfer += oprot->writeFieldEnd(); } if (this->__isset.id) { xfer += oprot->writeFieldBegin("id", ::apache::thrift::protocol::T_I32, 16); xfer += oprot->writeI32(this->id); xfer += oprot->writeFieldEnd(); } if (this->__isset.min_compaction_threshold) { xfer += oprot->writeFieldBegin("min_compaction_threshold", ::apache::thrift::protocol::T_I32, 17); xfer += oprot->writeI32(this->min_compaction_threshold); xfer += oprot->writeFieldEnd(); } if (this->__isset.max_compaction_threshold) { xfer += oprot->writeFieldBegin("max_compaction_threshold", ::apache::thrift::protocol::T_I32, 18); xfer += oprot->writeI32(this->max_compaction_threshold); xfer += oprot->writeFieldEnd(); } if (this->__isset.row_cache_save_period_in_seconds) { xfer += oprot->writeFieldBegin("row_cache_save_period_in_seconds", ::apache::thrift::protocol::T_I32, 19); xfer += oprot->writeI32(this->row_cache_save_period_in_seconds); xfer += oprot->writeFieldEnd(); } if (this->__isset.key_cache_save_period_in_seconds) { xfer += oprot->writeFieldBegin("key_cache_save_period_in_seconds", ::apache::thrift::protocol::T_I32, 20); xfer += oprot->writeI32(this->key_cache_save_period_in_seconds); xfer += oprot->writeFieldEnd(); } if (this->__isset.replicate_on_write) { xfer += oprot->writeFieldBegin("replicate_on_write", ::apache::thrift::protocol::T_BOOL, 24); xfer += oprot->writeBool(this->replicate_on_write); xfer += oprot->writeFieldEnd(); } if (this->__isset.merge_shards_chance) { xfer += oprot->writeFieldBegin("merge_shards_chance", ::apache::thrift::protocol::T_DOUBLE, 25); xfer += oprot->writeDouble(this->merge_shards_chance); xfer += oprot->writeFieldEnd(); } if (this->__isset.key_validation_class) { xfer += oprot->writeFieldBegin("key_validation_class", ::apache::thrift::protocol::T_STRING, 26); xfer += oprot->writeString(this->key_validation_class); xfer += oprot->writeFieldEnd(); } if (this->__isset.row_cache_provider) { xfer += oprot->writeFieldBegin("row_cache_provider", ::apache::thrift::protocol::T_STRING, 27); xfer += oprot->writeString(this->row_cache_provider); xfer += oprot->writeFieldEnd(); } if (this->__isset.key_alias) { xfer += oprot->writeFieldBegin("key_alias", ::apache::thrift::protocol::T_STRING, 28); xfer += oprot->writeBinary(this->key_alias); xfer += oprot->writeFieldEnd(); } if (this->__isset.compaction_strategy) { xfer += oprot->writeFieldBegin("compaction_strategy", ::apache::thrift::protocol::T_STRING, 29); xfer += oprot->writeString(this->compaction_strategy); xfer += oprot->writeFieldEnd(); } if (this->__isset.compaction_strategy_options) { xfer += oprot->writeFieldBegin("compaction_strategy_options", ::apache::thrift::protocol::T_MAP, 30); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_STRING, this->compaction_strategy_options.size()); std::map ::const_iterator _iter86; for (_iter86 = this->compaction_strategy_options.begin(); _iter86 != this->compaction_strategy_options.end(); ++_iter86) { xfer += oprot->writeString(_iter86->first); xfer += oprot->writeString(_iter86->second); } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); } if (this->__isset.row_cache_keys_to_save) { xfer += oprot->writeFieldBegin("row_cache_keys_to_save", ::apache::thrift::protocol::T_I32, 31); xfer += oprot->writeI32(this->row_cache_keys_to_save); xfer += oprot->writeFieldEnd(); } if (this->__isset.compression_options) { xfer += oprot->writeFieldBegin("compression_options", ::apache::thrift::protocol::T_MAP, 32); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_STRING, this->compression_options.size()); std::map ::const_iterator _iter87; for (_iter87 = this->compression_options.begin(); _iter87 != this->compression_options.end(); ++_iter87) { xfer += oprot->writeString(_iter87->first); xfer += oprot->writeString(_iter87->second); } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* KsDef::ascii_fingerprint = "28433565DB1070CD6C4372A762397EFE"; const uint8_t KsDef::binary_fingerprint[16] = {0x28,0x43,0x35,0x65,0xDB,0x10,0x70,0xCD,0x6C,0x43,0x72,0xA7,0x62,0x39,0x7E,0xFE}; uint32_t KsDef::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_name = false; bool isset_strategy_class = false; bool isset_cf_defs = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->name); isset_name = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->strategy_class); isset_strategy_class = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->strategy_options.clear(); uint32_t _size88; ::apache::thrift::protocol::TType _ktype89; ::apache::thrift::protocol::TType _vtype90; iprot->readMapBegin(_ktype89, _vtype90, _size88); uint32_t _i92; for (_i92 = 0; _i92 < _size88; ++_i92) { std::string _key93; xfer += iprot->readString(_key93); std::string& _val94 = this->strategy_options[_key93]; xfer += iprot->readString(_val94); } iprot->readMapEnd(); } this->__isset.strategy_options = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->replication_factor); this->__isset.replication_factor = true; } else { xfer += iprot->skip(ftype); } break; case 5: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->cf_defs.clear(); uint32_t _size95; ::apache::thrift::protocol::TType _etype98; iprot->readListBegin(_etype98, _size95); this->cf_defs.resize(_size95); uint32_t _i99; for (_i99 = 0; _i99 < _size95; ++_i99) { xfer += this->cf_defs[_i99].read(iprot); } iprot->readListEnd(); } isset_cf_defs = true; } else { xfer += iprot->skip(ftype); } break; case 6: if (ftype == ::apache::thrift::protocol::T_BOOL) { xfer += iprot->readBool(this->durable_writes); this->__isset.durable_writes = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_name) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_strategy_class) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_cf_defs) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t KsDef::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("KsDef"); xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeString(this->name); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("strategy_class", ::apache::thrift::protocol::T_STRING, 2); xfer += oprot->writeString(this->strategy_class); xfer += oprot->writeFieldEnd(); if (this->__isset.strategy_options) { xfer += oprot->writeFieldBegin("strategy_options", ::apache::thrift::protocol::T_MAP, 3); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_STRING, this->strategy_options.size()); std::map ::const_iterator _iter100; for (_iter100 = this->strategy_options.begin(); _iter100 != this->strategy_options.end(); ++_iter100) { xfer += oprot->writeString(_iter100->first); xfer += oprot->writeString(_iter100->second); } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); } if (this->__isset.replication_factor) { xfer += oprot->writeFieldBegin("replication_factor", ::apache::thrift::protocol::T_I32, 4); xfer += oprot->writeI32(this->replication_factor); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldBegin("cf_defs", ::apache::thrift::protocol::T_LIST, 5); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->cf_defs.size()); std::vector ::const_iterator _iter101; for (_iter101 = this->cf_defs.begin(); _iter101 != this->cf_defs.end(); ++_iter101) { xfer += (*_iter101).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); if (this->__isset.durable_writes) { xfer += oprot->writeFieldBegin("durable_writes", ::apache::thrift::protocol::T_BOOL, 6); xfer += oprot->writeBool(this->durable_writes); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* CqlRow::ascii_fingerprint = "470EFC558004E98D92D604898305C04E"; const uint8_t CqlRow::binary_fingerprint[16] = {0x47,0x0E,0xFC,0x55,0x80,0x04,0xE9,0x8D,0x92,0xD6,0x04,0x89,0x83,0x05,0xC0,0x4E}; uint32_t CqlRow::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_key = false; bool isset_columns = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readBinary(this->key); isset_key = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->columns.clear(); uint32_t _size102; ::apache::thrift::protocol::TType _etype105; iprot->readListBegin(_etype105, _size102); this->columns.resize(_size102); uint32_t _i106; for (_i106 = 0; _i106 < _size102; ++_i106) { xfer += this->columns[_i106].read(iprot); } iprot->readListEnd(); } isset_columns = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_key) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_columns) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t CqlRow::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("CqlRow"); xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1); xfer += oprot->writeBinary(this->key); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("columns", ::apache::thrift::protocol::T_LIST, 2); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->columns.size()); std::vector ::const_iterator _iter107; for (_iter107 = this->columns.begin(); _iter107 != this->columns.end(); ++_iter107) { xfer += (*_iter107).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* CqlMetadata::ascii_fingerprint = "B7C5A4AA9652C744A48EBC1C12D531E7"; const uint8_t CqlMetadata::binary_fingerprint[16] = {0xB7,0xC5,0xA4,0xAA,0x96,0x52,0xC7,0x44,0xA4,0x8E,0xBC,0x1C,0x12,0xD5,0x31,0xE7}; uint32_t CqlMetadata::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_name_types = false; bool isset_value_types = false; bool isset_default_name_type = false; bool isset_default_value_type = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->name_types.clear(); uint32_t _size108; ::apache::thrift::protocol::TType _ktype109; ::apache::thrift::protocol::TType _vtype110; iprot->readMapBegin(_ktype109, _vtype110, _size108); uint32_t _i112; for (_i112 = 0; _i112 < _size108; ++_i112) { std::string _key113; xfer += iprot->readBinary(_key113); std::string& _val114 = this->name_types[_key113]; xfer += iprot->readString(_val114); } iprot->readMapEnd(); } isset_name_types = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_MAP) { { this->value_types.clear(); uint32_t _size115; ::apache::thrift::protocol::TType _ktype116; ::apache::thrift::protocol::TType _vtype117; iprot->readMapBegin(_ktype116, _vtype117, _size115); uint32_t _i119; for (_i119 = 0; _i119 < _size115; ++_i119) { std::string _key120; xfer += iprot->readBinary(_key120); std::string& _val121 = this->value_types[_key120]; xfer += iprot->readString(_val121); } iprot->readMapEnd(); } isset_value_types = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->default_name_type); isset_default_name_type = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRING) { xfer += iprot->readString(this->default_value_type); isset_default_value_type = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_name_types) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_value_types) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_default_name_type) throw TProtocolException(TProtocolException::INVALID_DATA); if (!isset_default_value_type) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t CqlMetadata::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("CqlMetadata"); xfer += oprot->writeFieldBegin("name_types", ::apache::thrift::protocol::T_MAP, 1); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_STRING, this->name_types.size()); std::map ::const_iterator _iter122; for (_iter122 = this->name_types.begin(); _iter122 != this->name_types.end(); ++_iter122) { xfer += oprot->writeBinary(_iter122->first); xfer += oprot->writeString(_iter122->second); } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("value_types", ::apache::thrift::protocol::T_MAP, 2); { xfer += oprot->writeMapBegin(::apache::thrift::protocol::T_STRING, ::apache::thrift::protocol::T_STRING, this->value_types.size()); std::map ::const_iterator _iter123; for (_iter123 = this->value_types.begin(); _iter123 != this->value_types.end(); ++_iter123) { xfer += oprot->writeBinary(_iter123->first); xfer += oprot->writeString(_iter123->second); } xfer += oprot->writeMapEnd(); } xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("default_name_type", ::apache::thrift::protocol::T_STRING, 3); xfer += oprot->writeString(this->default_name_type); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldBegin("default_value_type", ::apache::thrift::protocol::T_STRING, 4); xfer += oprot->writeString(this->default_value_type); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } const char* CqlResult::ascii_fingerprint = "521B9CE5AF77539F7267F6952B609E81"; const uint8_t CqlResult::binary_fingerprint[16] = {0x52,0x1B,0x9C,0xE5,0xAF,0x77,0x53,0x9F,0x72,0x67,0xF6,0x95,0x2B,0x60,0x9E,0x81}; uint32_t CqlResult::read(::apache::thrift::protocol::TProtocol* iprot) { uint32_t xfer = 0; std::string fname; ::apache::thrift::protocol::TType ftype; int16_t fid; xfer += iprot->readStructBegin(fname); using ::apache::thrift::protocol::TProtocolException; bool isset_type = false; while (true) { xfer += iprot->readFieldBegin(fname, ftype, fid); if (ftype == ::apache::thrift::protocol::T_STOP) { break; } switch (fid) { case 1: if (ftype == ::apache::thrift::protocol::T_I32) { int32_t ecast124; xfer += iprot->readI32(ecast124); this->type = (CqlResultType::type)ecast124; isset_type = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_LIST) { { this->rows.clear(); uint32_t _size125; ::apache::thrift::protocol::TType _etype128; iprot->readListBegin(_etype128, _size125); this->rows.resize(_size125); uint32_t _i129; for (_i129 = 0; _i129 < _size125; ++_i129) { xfer += this->rows[_i129].read(iprot); } iprot->readListEnd(); } this->__isset.rows = true; } else { xfer += iprot->skip(ftype); } break; case 3: if (ftype == ::apache::thrift::protocol::T_I32) { xfer += iprot->readI32(this->num); this->__isset.num = true; } else { xfer += iprot->skip(ftype); } break; case 4: if (ftype == ::apache::thrift::protocol::T_STRUCT) { xfer += this->schema.read(iprot); this->__isset.schema = true; } else { xfer += iprot->skip(ftype); } break; default: xfer += iprot->skip(ftype); break; } xfer += iprot->readFieldEnd(); } xfer += iprot->readStructEnd(); if (!isset_type) throw TProtocolException(TProtocolException::INVALID_DATA); return xfer; } uint32_t CqlResult::write(::apache::thrift::protocol::TProtocol* oprot) const { uint32_t xfer = 0; xfer += oprot->writeStructBegin("CqlResult"); xfer += oprot->writeFieldBegin("type", ::apache::thrift::protocol::T_I32, 1); xfer += oprot->writeI32((int32_t)this->type); xfer += oprot->writeFieldEnd(); if (this->__isset.rows) { xfer += oprot->writeFieldBegin("rows", ::apache::thrift::protocol::T_LIST, 2); { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, this->rows.size()); std::vector ::const_iterator _iter130; for (_iter130 = this->rows.begin(); _iter130 != this->rows.end(); ++_iter130) { xfer += (*_iter130).write(oprot); } xfer += oprot->writeListEnd(); } xfer += oprot->writeFieldEnd(); } if (this->__isset.num) { xfer += oprot->writeFieldBegin("num", ::apache::thrift::protocol::T_I32, 3); xfer += oprot->writeI32(this->num); xfer += oprot->writeFieldEnd(); } if (this->__isset.schema) { xfer += oprot->writeFieldBegin("schema", ::apache::thrift::protocol::T_STRUCT, 4); xfer += this->schema.write(oprot); xfer += oprot->writeFieldEnd(); } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; } }}} // namespace opensips-2.2.2/modules/cachedb_cassandra/cassandra_types.h000066400000000000000000001265701300170765700237720ustar00rootroot00000000000000/** * Autogenerated by Thrift * * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING */ #ifndef cassandra_TYPES_H #define cassandra_TYPES_H #include #include #include #include namespace org { namespace apache { namespace cassandra { struct ConsistencyLevel { enum type { ONE = 1, QUORUM = 2, LOCAL_QUORUM = 3, EACH_QUORUM = 4, ALL = 5, ANY = 6, TWO = 7, THREE = 8 }; }; extern const std::map _ConsistencyLevel_VALUES_TO_NAMES; struct IndexOperator { enum type { EQ = 0, GTE = 1, GT = 2, LTE = 3, LT = 4 }; }; extern const std::map _IndexOperator_VALUES_TO_NAMES; struct IndexType { enum type { KEYS = 0, CUSTOM = 1 }; }; extern const std::map _IndexType_VALUES_TO_NAMES; struct Compression { enum type { GZIP = 1, NONE = 2 }; }; extern const std::map _Compression_VALUES_TO_NAMES; struct CqlResultType { enum type { ROWS = 1, VOID = 2, INT = 3 }; }; extern const std::map _CqlResultType_VALUES_TO_NAMES; typedef struct _Column__isset { _Column__isset() : value(false), timestamp(false), ttl(false) {} bool value; bool timestamp; bool ttl; } _Column__isset; class Column { public: static const char* ascii_fingerprint; // = "3EE0E1C5C844001B62F08125068292CC"; static const uint8_t binary_fingerprint[16]; // = {0x3E,0xE0,0xE1,0xC5,0xC8,0x44,0x00,0x1B,0x62,0xF0,0x81,0x25,0x06,0x82,0x92,0xCC}; Column() : name(""), value(""), timestamp(0), ttl(0) { } virtual ~Column() throw() {} std::string name; std::string value; int64_t timestamp; int32_t ttl; _Column__isset __isset; bool operator == (const Column & rhs) const { if (!(name == rhs.name)) return false; if (__isset.value != rhs.__isset.value) return false; else if (__isset.value && !(value == rhs.value)) return false; if (__isset.timestamp != rhs.__isset.timestamp) return false; else if (__isset.timestamp && !(timestamp == rhs.timestamp)) return false; if (__isset.ttl != rhs.__isset.ttl) return false; else if (__isset.ttl && !(ttl == rhs.ttl)) return false; return true; } bool operator != (const Column &rhs) const { return !(*this == rhs); } bool operator < (const Column & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class SuperColumn { public: static const char* ascii_fingerprint; // = "470EFC558004E98D92D604898305C04E"; static const uint8_t binary_fingerprint[16]; // = {0x47,0x0E,0xFC,0x55,0x80,0x04,0xE9,0x8D,0x92,0xD6,0x04,0x89,0x83,0x05,0xC0,0x4E}; SuperColumn() : name("") { } virtual ~SuperColumn() throw() {} std::string name; std::vector columns; bool operator == (const SuperColumn & rhs) const { if (!(name == rhs.name)) return false; if (!(columns == rhs.columns)) return false; return true; } bool operator != (const SuperColumn &rhs) const { return !(*this == rhs); } bool operator < (const SuperColumn & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class CounterColumn { public: static const char* ascii_fingerprint; // = "1CCCF6FC31CFD1D61BBBB1BAF3590620"; static const uint8_t binary_fingerprint[16]; // = {0x1C,0xCC,0xF6,0xFC,0x31,0xCF,0xD1,0xD6,0x1B,0xBB,0xB1,0xBA,0xF3,0x59,0x06,0x20}; CounterColumn() : name(""), value(0) { } virtual ~CounterColumn() throw() {} std::string name; int64_t value; bool operator == (const CounterColumn & rhs) const { if (!(name == rhs.name)) return false; if (!(value == rhs.value)) return false; return true; } bool operator != (const CounterColumn &rhs) const { return !(*this == rhs); } bool operator < (const CounterColumn & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class CounterSuperColumn { public: static const char* ascii_fingerprint; // = "CD4C8C4BF7753E46DE417CDE369343A4"; static const uint8_t binary_fingerprint[16]; // = {0xCD,0x4C,0x8C,0x4B,0xF7,0x75,0x3E,0x46,0xDE,0x41,0x7C,0xDE,0x36,0x93,0x43,0xA4}; CounterSuperColumn() : name("") { } virtual ~CounterSuperColumn() throw() {} std::string name; std::vector columns; bool operator == (const CounterSuperColumn & rhs) const { if (!(name == rhs.name)) return false; if (!(columns == rhs.columns)) return false; return true; } bool operator != (const CounterSuperColumn &rhs) const { return !(*this == rhs); } bool operator < (const CounterSuperColumn & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _ColumnOrSuperColumn__isset { _ColumnOrSuperColumn__isset() : column(false), super_column(false), counter_column(false), counter_super_column(false) {} bool column; bool super_column; bool counter_column; bool counter_super_column; } _ColumnOrSuperColumn__isset; class ColumnOrSuperColumn { public: static const char* ascii_fingerprint; // = "2B34AC9E80F1DAA3A2A63B1AB1841E61"; static const uint8_t binary_fingerprint[16]; // = {0x2B,0x34,0xAC,0x9E,0x80,0xF1,0xDA,0xA3,0xA2,0xA6,0x3B,0x1A,0xB1,0x84,0x1E,0x61}; ColumnOrSuperColumn() { } virtual ~ColumnOrSuperColumn() throw() {} Column column; SuperColumn super_column; CounterColumn counter_column; CounterSuperColumn counter_super_column; _ColumnOrSuperColumn__isset __isset; bool operator == (const ColumnOrSuperColumn & rhs) const { if (__isset.column != rhs.__isset.column) return false; else if (__isset.column && !(column == rhs.column)) return false; if (__isset.super_column != rhs.__isset.super_column) return false; else if (__isset.super_column && !(super_column == rhs.super_column)) return false; if (__isset.counter_column != rhs.__isset.counter_column) return false; else if (__isset.counter_column && !(counter_column == rhs.counter_column)) return false; if (__isset.counter_super_column != rhs.__isset.counter_super_column) return false; else if (__isset.counter_super_column && !(counter_super_column == rhs.counter_super_column)) return false; return true; } bool operator != (const ColumnOrSuperColumn &rhs) const { return !(*this == rhs); } bool operator < (const ColumnOrSuperColumn & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class NotFoundException : public ::apache::thrift::TException { public: static const char* ascii_fingerprint; // = "99914B932BD37A50B983C5E7C90AE93B"; static const uint8_t binary_fingerprint[16]; // = {0x99,0x91,0x4B,0x93,0x2B,0xD3,0x7A,0x50,0xB9,0x83,0xC5,0xE7,0xC9,0x0A,0xE9,0x3B}; NotFoundException() { } virtual ~NotFoundException() throw() {} bool operator == (const NotFoundException & /* rhs */) const { return true; } bool operator != (const NotFoundException &rhs) const { return !(*this == rhs); } bool operator < (const NotFoundException & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class InvalidRequestException : public ::apache::thrift::TException { public: static const char* ascii_fingerprint; // = "EFB929595D312AC8F305D5A794CFEDA1"; static const uint8_t binary_fingerprint[16]; // = {0xEF,0xB9,0x29,0x59,0x5D,0x31,0x2A,0xC8,0xF3,0x05,0xD5,0xA7,0x94,0xCF,0xED,0xA1}; InvalidRequestException() : why("") { } virtual ~InvalidRequestException() throw() {} std::string why; bool operator == (const InvalidRequestException & rhs) const { if (!(why == rhs.why)) return false; return true; } bool operator != (const InvalidRequestException &rhs) const { return !(*this == rhs); } bool operator < (const InvalidRequestException & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class UnavailableException : public ::apache::thrift::TException { public: static const char* ascii_fingerprint; // = "99914B932BD37A50B983C5E7C90AE93B"; static const uint8_t binary_fingerprint[16]; // = {0x99,0x91,0x4B,0x93,0x2B,0xD3,0x7A,0x50,0xB9,0x83,0xC5,0xE7,0xC9,0x0A,0xE9,0x3B}; UnavailableException() { } virtual ~UnavailableException() throw() {} bool operator == (const UnavailableException & /* rhs */) const { return true; } bool operator != (const UnavailableException &rhs) const { return !(*this == rhs); } bool operator < (const UnavailableException & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class TimedOutException : public ::apache::thrift::TException { public: static const char* ascii_fingerprint; // = "99914B932BD37A50B983C5E7C90AE93B"; static const uint8_t binary_fingerprint[16]; // = {0x99,0x91,0x4B,0x93,0x2B,0xD3,0x7A,0x50,0xB9,0x83,0xC5,0xE7,0xC9,0x0A,0xE9,0x3B}; TimedOutException() { } virtual ~TimedOutException() throw() {} bool operator == (const TimedOutException & /* rhs */) const { return true; } bool operator != (const TimedOutException &rhs) const { return !(*this == rhs); } bool operator < (const TimedOutException & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class AuthenticationException : public ::apache::thrift::TException { public: static const char* ascii_fingerprint; // = "EFB929595D312AC8F305D5A794CFEDA1"; static const uint8_t binary_fingerprint[16]; // = {0xEF,0xB9,0x29,0x59,0x5D,0x31,0x2A,0xC8,0xF3,0x05,0xD5,0xA7,0x94,0xCF,0xED,0xA1}; AuthenticationException() : why("") { } virtual ~AuthenticationException() throw() {} std::string why; bool operator == (const AuthenticationException & rhs) const { if (!(why == rhs.why)) return false; return true; } bool operator != (const AuthenticationException &rhs) const { return !(*this == rhs); } bool operator < (const AuthenticationException & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class AuthorizationException : public ::apache::thrift::TException { public: static const char* ascii_fingerprint; // = "EFB929595D312AC8F305D5A794CFEDA1"; static const uint8_t binary_fingerprint[16]; // = {0xEF,0xB9,0x29,0x59,0x5D,0x31,0x2A,0xC8,0xF3,0x05,0xD5,0xA7,0x94,0xCF,0xED,0xA1}; AuthorizationException() : why("") { } virtual ~AuthorizationException() throw() {} std::string why; bool operator == (const AuthorizationException & rhs) const { if (!(why == rhs.why)) return false; return true; } bool operator != (const AuthorizationException &rhs) const { return !(*this == rhs); } bool operator < (const AuthorizationException & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class SchemaDisagreementException : public ::apache::thrift::TException { public: static const char* ascii_fingerprint; // = "99914B932BD37A50B983C5E7C90AE93B"; static const uint8_t binary_fingerprint[16]; // = {0x99,0x91,0x4B,0x93,0x2B,0xD3,0x7A,0x50,0xB9,0x83,0xC5,0xE7,0xC9,0x0A,0xE9,0x3B}; SchemaDisagreementException() { } virtual ~SchemaDisagreementException() throw() {} bool operator == (const SchemaDisagreementException & /* rhs */) const { return true; } bool operator != (const SchemaDisagreementException &rhs) const { return !(*this == rhs); } bool operator < (const SchemaDisagreementException & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _ColumnParent__isset { _ColumnParent__isset() : super_column(false) {} bool super_column; } _ColumnParent__isset; class ColumnParent { public: static const char* ascii_fingerprint; // = "0A13AE61181713A4100DFFB3EC293822"; static const uint8_t binary_fingerprint[16]; // = {0x0A,0x13,0xAE,0x61,0x18,0x17,0x13,0xA4,0x10,0x0D,0xFF,0xB3,0xEC,0x29,0x38,0x22}; ColumnParent() : column_family(""), super_column("") { } virtual ~ColumnParent() throw() {} std::string column_family; std::string super_column; _ColumnParent__isset __isset; bool operator == (const ColumnParent & rhs) const { if (!(column_family == rhs.column_family)) return false; if (__isset.super_column != rhs.__isset.super_column) return false; else if (__isset.super_column && !(super_column == rhs.super_column)) return false; return true; } bool operator != (const ColumnParent &rhs) const { return !(*this == rhs); } bool operator < (const ColumnParent & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _ColumnPath__isset { _ColumnPath__isset() : super_column(false), column(false) {} bool super_column; bool column; } _ColumnPath__isset; class ColumnPath { public: static const char* ascii_fingerprint; // = "606212895BCF63C757913CF35AEB3462"; static const uint8_t binary_fingerprint[16]; // = {0x60,0x62,0x12,0x89,0x5B,0xCF,0x63,0xC7,0x57,0x91,0x3C,0xF3,0x5A,0xEB,0x34,0x62}; ColumnPath() : column_family(""), super_column(""), column("") { } virtual ~ColumnPath() throw() {} std::string column_family; std::string super_column; std::string column; _ColumnPath__isset __isset; bool operator == (const ColumnPath & rhs) const { if (!(column_family == rhs.column_family)) return false; if (__isset.super_column != rhs.__isset.super_column) return false; else if (__isset.super_column && !(super_column == rhs.super_column)) return false; if (__isset.column != rhs.__isset.column) return false; else if (__isset.column && !(column == rhs.column)) return false; return true; } bool operator != (const ColumnPath &rhs) const { return !(*this == rhs); } bool operator < (const ColumnPath & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class SliceRange { public: static const char* ascii_fingerprint; // = "184D24C9A0B8D4415E234DB649CAE740"; static const uint8_t binary_fingerprint[16]; // = {0x18,0x4D,0x24,0xC9,0xA0,0xB8,0xD4,0x41,0x5E,0x23,0x4D,0xB6,0x49,0xCA,0xE7,0x40}; SliceRange() : start(""), finish(""), reversed(false), count(100) { } virtual ~SliceRange() throw() {} std::string start; std::string finish; bool reversed; int32_t count; bool operator == (const SliceRange & rhs) const { if (!(start == rhs.start)) return false; if (!(finish == rhs.finish)) return false; if (!(reversed == rhs.reversed)) return false; if (!(count == rhs.count)) return false; return true; } bool operator != (const SliceRange &rhs) const { return !(*this == rhs); } bool operator < (const SliceRange & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _SlicePredicate__isset { _SlicePredicate__isset() : column_names(false), slice_range(false) {} bool column_names; bool slice_range; } _SlicePredicate__isset; class SlicePredicate { public: static const char* ascii_fingerprint; // = "F59D1D81C17DFFAF09988BF1C9CE5E27"; static const uint8_t binary_fingerprint[16]; // = {0xF5,0x9D,0x1D,0x81,0xC1,0x7D,0xFF,0xAF,0x09,0x98,0x8B,0xF1,0xC9,0xCE,0x5E,0x27}; SlicePredicate() { } virtual ~SlicePredicate() throw() {} std::vector column_names; SliceRange slice_range; _SlicePredicate__isset __isset; bool operator == (const SlicePredicate & rhs) const { if (__isset.column_names != rhs.__isset.column_names) return false; else if (__isset.column_names && !(column_names == rhs.column_names)) return false; if (__isset.slice_range != rhs.__isset.slice_range) return false; else if (__isset.slice_range && !(slice_range == rhs.slice_range)) return false; return true; } bool operator != (const SlicePredicate &rhs) const { return !(*this == rhs); } bool operator < (const SlicePredicate & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class IndexExpression { public: static const char* ascii_fingerprint; // = "D9F4CFE2F293A8B1052FD3031DD2C847"; static const uint8_t binary_fingerprint[16]; // = {0xD9,0xF4,0xCF,0xE2,0xF2,0x93,0xA8,0xB1,0x05,0x2F,0xD3,0x03,0x1D,0xD2,0xC8,0x47}; IndexExpression() : column_name(""), value("") { } virtual ~IndexExpression() throw() {} std::string column_name; IndexOperator::type op; std::string value; bool operator == (const IndexExpression & rhs) const { if (!(column_name == rhs.column_name)) return false; if (!(op == rhs.op)) return false; if (!(value == rhs.value)) return false; return true; } bool operator != (const IndexExpression &rhs) const { return !(*this == rhs); } bool operator < (const IndexExpression & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class IndexClause { public: static const char* ascii_fingerprint; // = "9B551B9AB86120B0EEA9005C77FD3C1F"; static const uint8_t binary_fingerprint[16]; // = {0x9B,0x55,0x1B,0x9A,0xB8,0x61,0x20,0xB0,0xEE,0xA9,0x00,0x5C,0x77,0xFD,0x3C,0x1F}; IndexClause() : start_key(""), count(100) { } virtual ~IndexClause() throw() {} std::vector expressions; std::string start_key; int32_t count; bool operator == (const IndexClause & rhs) const { if (!(expressions == rhs.expressions)) return false; if (!(start_key == rhs.start_key)) return false; if (!(count == rhs.count)) return false; return true; } bool operator != (const IndexClause &rhs) const { return !(*this == rhs); } bool operator < (const IndexClause & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _KeyRange__isset { _KeyRange__isset() : start_key(false), end_key(false), start_token(false), end_token(false) {} bool start_key; bool end_key; bool start_token; bool end_token; } _KeyRange__isset; class KeyRange { public: static const char* ascii_fingerprint; // = "8F248C09AF1EC3656ABD8565EA1F59C1"; static const uint8_t binary_fingerprint[16]; // = {0x8F,0x24,0x8C,0x09,0xAF,0x1E,0xC3,0x65,0x6A,0xBD,0x85,0x65,0xEA,0x1F,0x59,0xC1}; KeyRange() : start_key(""), end_key(""), start_token(""), end_token(""), count(100) { } virtual ~KeyRange() throw() {} std::string start_key; std::string end_key; std::string start_token; std::string end_token; int32_t count; _KeyRange__isset __isset; bool operator == (const KeyRange & rhs) const { if (__isset.start_key != rhs.__isset.start_key) return false; else if (__isset.start_key && !(start_key == rhs.start_key)) return false; if (__isset.end_key != rhs.__isset.end_key) return false; else if (__isset.end_key && !(end_key == rhs.end_key)) return false; if (__isset.start_token != rhs.__isset.start_token) return false; else if (__isset.start_token && !(start_token == rhs.start_token)) return false; if (__isset.end_token != rhs.__isset.end_token) return false; else if (__isset.end_token && !(end_token == rhs.end_token)) return false; if (!(count == rhs.count)) return false; return true; } bool operator != (const KeyRange &rhs) const { return !(*this == rhs); } bool operator < (const KeyRange & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class KeySlice { public: static const char* ascii_fingerprint; // = "D1568675B0C135C909E3169B72A4DA3D"; static const uint8_t binary_fingerprint[16]; // = {0xD1,0x56,0x86,0x75,0xB0,0xC1,0x35,0xC9,0x09,0xE3,0x16,0x9B,0x72,0xA4,0xDA,0x3D}; KeySlice() : key("") { } virtual ~KeySlice() throw() {} std::string key; std::vector columns; bool operator == (const KeySlice & rhs) const { if (!(key == rhs.key)) return false; if (!(columns == rhs.columns)) return false; return true; } bool operator != (const KeySlice &rhs) const { return !(*this == rhs); } bool operator < (const KeySlice & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class KeyCount { public: static const char* ascii_fingerprint; // = "EEBC915CE44901401D881E6091423036"; static const uint8_t binary_fingerprint[16]; // = {0xEE,0xBC,0x91,0x5C,0xE4,0x49,0x01,0x40,0x1D,0x88,0x1E,0x60,0x91,0x42,0x30,0x36}; KeyCount() : key(""), count(0) { } virtual ~KeyCount() throw() {} std::string key; int32_t count; bool operator == (const KeyCount & rhs) const { if (!(key == rhs.key)) return false; if (!(count == rhs.count)) return false; return true; } bool operator != (const KeyCount &rhs) const { return !(*this == rhs); } bool operator < (const KeyCount & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Deletion__isset { _Deletion__isset() : timestamp(false), super_column(false), predicate(false) {} bool timestamp; bool super_column; bool predicate; } _Deletion__isset; class Deletion { public: static const char* ascii_fingerprint; // = "40F33ECF1C932CA77C2414C4E6C60CBE"; static const uint8_t binary_fingerprint[16]; // = {0x40,0xF3,0x3E,0xCF,0x1C,0x93,0x2C,0xA7,0x7C,0x24,0x14,0xC4,0xE6,0xC6,0x0C,0xBE}; Deletion() : timestamp(0), super_column("") { } virtual ~Deletion() throw() {} int64_t timestamp; std::string super_column; SlicePredicate predicate; _Deletion__isset __isset; bool operator == (const Deletion & rhs) const { if (__isset.timestamp != rhs.__isset.timestamp) return false; else if (__isset.timestamp && !(timestamp == rhs.timestamp)) return false; if (__isset.super_column != rhs.__isset.super_column) return false; else if (__isset.super_column && !(super_column == rhs.super_column)) return false; if (__isset.predicate != rhs.__isset.predicate) return false; else if (__isset.predicate && !(predicate == rhs.predicate)) return false; return true; } bool operator != (const Deletion &rhs) const { return !(*this == rhs); } bool operator < (const Deletion & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _Mutation__isset { _Mutation__isset() : column_or_supercolumn(false), deletion(false) {} bool column_or_supercolumn; bool deletion; } _Mutation__isset; class Mutation { public: static const char* ascii_fingerprint; // = "E8B65DF3979C6868F80DF81F8E769E63"; static const uint8_t binary_fingerprint[16]; // = {0xE8,0xB6,0x5D,0xF3,0x97,0x9C,0x68,0x68,0xF8,0x0D,0xF8,0x1F,0x8E,0x76,0x9E,0x63}; Mutation() { } virtual ~Mutation() throw() {} ColumnOrSuperColumn column_or_supercolumn; Deletion deletion; _Mutation__isset __isset; bool operator == (const Mutation & rhs) const { if (__isset.column_or_supercolumn != rhs.__isset.column_or_supercolumn) return false; else if (__isset.column_or_supercolumn && !(column_or_supercolumn == rhs.column_or_supercolumn)) return false; if (__isset.deletion != rhs.__isset.deletion) return false; else if (__isset.deletion && !(deletion == rhs.deletion)) return false; return true; } bool operator != (const Mutation &rhs) const { return !(*this == rhs); } bool operator < (const Mutation & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _EndpointDetails__isset { _EndpointDetails__isset() : host(false), datacenter(false), rack(false) {} bool host; bool datacenter; bool rack; } _EndpointDetails__isset; class EndpointDetails { public: static const char* ascii_fingerprint; // = "F4A50F0EC638C7F66026F9B6678FD89B"; static const uint8_t binary_fingerprint[16]; // = {0xF4,0xA5,0x0F,0x0E,0xC6,0x38,0xC7,0xF6,0x60,0x26,0xF9,0xB6,0x67,0x8F,0xD8,0x9B}; EndpointDetails() : host(""), datacenter(""), rack("") { } virtual ~EndpointDetails() throw() {} std::string host; std::string datacenter; std::string rack; _EndpointDetails__isset __isset; bool operator == (const EndpointDetails & rhs) const { if (!(host == rhs.host)) return false; if (!(datacenter == rhs.datacenter)) return false; if (__isset.rack != rhs.__isset.rack) return false; else if (__isset.rack && !(rack == rhs.rack)) return false; return true; } bool operator != (const EndpointDetails &rhs) const { return !(*this == rhs); } bool operator < (const EndpointDetails & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _TokenRange__isset { _TokenRange__isset() : rpc_endpoints(false), endpoint_details(false) {} bool rpc_endpoints; bool endpoint_details; } _TokenRange__isset; class TokenRange { public: static const char* ascii_fingerprint; // = "832268DC4CD6B17EE8881FC57EA04679"; static const uint8_t binary_fingerprint[16]; // = {0x83,0x22,0x68,0xDC,0x4C,0xD6,0xB1,0x7E,0xE8,0x88,0x1F,0xC5,0x7E,0xA0,0x46,0x79}; TokenRange() : start_token(""), end_token("") { } virtual ~TokenRange() throw() {} std::string start_token; std::string end_token; std::vector endpoints; std::vector rpc_endpoints; std::vector endpoint_details; _TokenRange__isset __isset; bool operator == (const TokenRange & rhs) const { if (!(start_token == rhs.start_token)) return false; if (!(end_token == rhs.end_token)) return false; if (!(endpoints == rhs.endpoints)) return false; if (__isset.rpc_endpoints != rhs.__isset.rpc_endpoints) return false; else if (__isset.rpc_endpoints && !(rpc_endpoints == rhs.rpc_endpoints)) return false; if (__isset.endpoint_details != rhs.__isset.endpoint_details) return false; else if (__isset.endpoint_details && !(endpoint_details == rhs.endpoint_details)) return false; return true; } bool operator != (const TokenRange &rhs) const { return !(*this == rhs); } bool operator < (const TokenRange & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class AuthenticationRequest { public: static const char* ascii_fingerprint; // = "5EA2D527ECA3BA20C77AFC023EE8C05F"; static const uint8_t binary_fingerprint[16]; // = {0x5E,0xA2,0xD5,0x27,0xEC,0xA3,0xBA,0x20,0xC7,0x7A,0xFC,0x02,0x3E,0xE8,0xC0,0x5F}; AuthenticationRequest() { } virtual ~AuthenticationRequest() throw() {} std::map credentials; bool operator == (const AuthenticationRequest & rhs) const { if (!(credentials == rhs.credentials)) return false; return true; } bool operator != (const AuthenticationRequest &rhs) const { return !(*this == rhs); } bool operator < (const AuthenticationRequest & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _ColumnDef__isset { _ColumnDef__isset() : index_type(false), index_name(false), index_options(false) {} bool index_type; bool index_name; bool index_options; } _ColumnDef__isset; class ColumnDef { public: static const char* ascii_fingerprint; // = "0D89CE83D7EDAD079AC3213ED1DCAA58"; static const uint8_t binary_fingerprint[16]; // = {0x0D,0x89,0xCE,0x83,0xD7,0xED,0xAD,0x07,0x9A,0xC3,0x21,0x3E,0xD1,0xDC,0xAA,0x58}; ColumnDef() : name(""), validation_class(""), index_name("") { } virtual ~ColumnDef() throw() {} std::string name; std::string validation_class; IndexType::type index_type; std::string index_name; std::map index_options; _ColumnDef__isset __isset; bool operator == (const ColumnDef & rhs) const { if (!(name == rhs.name)) return false; if (!(validation_class == rhs.validation_class)) return false; if (__isset.index_type != rhs.__isset.index_type) return false; else if (__isset.index_type && !(index_type == rhs.index_type)) return false; if (__isset.index_name != rhs.__isset.index_name) return false; else if (__isset.index_name && !(index_name == rhs.index_name)) return false; if (__isset.index_options != rhs.__isset.index_options) return false; else if (__isset.index_options && !(index_options == rhs.index_options)) return false; return true; } bool operator != (const ColumnDef &rhs) const { return !(*this == rhs); } bool operator < (const ColumnDef & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _CfDef__isset { _CfDef__isset() : column_type(false), comparator_type(false), subcomparator_type(false), comment(false), row_cache_size(false), key_cache_size(false), read_repair_chance(false), column_metadata(false), gc_grace_seconds(false), default_validation_class(false), id(false), min_compaction_threshold(false), max_compaction_threshold(false), row_cache_save_period_in_seconds(false), key_cache_save_period_in_seconds(false), replicate_on_write(false), merge_shards_chance(false), key_validation_class(false), row_cache_provider(false), key_alias(false), compaction_strategy(false), compaction_strategy_options(false), row_cache_keys_to_save(false), compression_options(false) {} bool column_type; bool comparator_type; bool subcomparator_type; bool comment; bool row_cache_size; bool key_cache_size; bool read_repair_chance; bool column_metadata; bool gc_grace_seconds; bool default_validation_class; bool id; bool min_compaction_threshold; bool max_compaction_threshold; bool row_cache_save_period_in_seconds; bool key_cache_save_period_in_seconds; bool replicate_on_write; bool merge_shards_chance; bool key_validation_class; bool row_cache_provider; bool key_alias; bool compaction_strategy; bool compaction_strategy_options; bool row_cache_keys_to_save; bool compression_options; } _CfDef__isset; class CfDef { public: static const char* ascii_fingerprint; // = "0FA1E255DEA2A1E59044B44DA8536D18"; static const uint8_t binary_fingerprint[16]; // = {0x0F,0xA1,0xE2,0x55,0xDE,0xA2,0xA1,0xE5,0x90,0x44,0xB4,0x4D,0xA8,0x53,0x6D,0x18}; CfDef() : keyspace(""), name(""), column_type("Standard"), comparator_type("BytesType"), subcomparator_type(""), comment(""), row_cache_size(0), key_cache_size(200000), read_repair_chance(1), gc_grace_seconds(0), default_validation_class(""), id(0), min_compaction_threshold(0), max_compaction_threshold(0), row_cache_save_period_in_seconds(0), key_cache_save_period_in_seconds(0), replicate_on_write(0), merge_shards_chance(0), key_validation_class(""), row_cache_provider(""), key_alias(""), compaction_strategy(""), row_cache_keys_to_save(0) { } virtual ~CfDef() throw() {} std::string keyspace; std::string name; std::string column_type; std::string comparator_type; std::string subcomparator_type; std::string comment; double row_cache_size; double key_cache_size; double read_repair_chance; std::vector column_metadata; int32_t gc_grace_seconds; std::string default_validation_class; int32_t id; int32_t min_compaction_threshold; int32_t max_compaction_threshold; int32_t row_cache_save_period_in_seconds; int32_t key_cache_save_period_in_seconds; bool replicate_on_write; double merge_shards_chance; std::string key_validation_class; std::string row_cache_provider; std::string key_alias; std::string compaction_strategy; std::map compaction_strategy_options; int32_t row_cache_keys_to_save; std::map compression_options; _CfDef__isset __isset; bool operator == (const CfDef & rhs) const { if (!(keyspace == rhs.keyspace)) return false; if (!(name == rhs.name)) return false; if (__isset.column_type != rhs.__isset.column_type) return false; else if (__isset.column_type && !(column_type == rhs.column_type)) return false; if (__isset.comparator_type != rhs.__isset.comparator_type) return false; else if (__isset.comparator_type && !(comparator_type == rhs.comparator_type)) return false; if (__isset.subcomparator_type != rhs.__isset.subcomparator_type) return false; else if (__isset.subcomparator_type && !(subcomparator_type == rhs.subcomparator_type)) return false; if (__isset.comment != rhs.__isset.comment) return false; else if (__isset.comment && !(comment == rhs.comment)) return false; if (__isset.row_cache_size != rhs.__isset.row_cache_size) return false; else if (__isset.row_cache_size && !(row_cache_size == rhs.row_cache_size)) return false; if (__isset.key_cache_size != rhs.__isset.key_cache_size) return false; else if (__isset.key_cache_size && !(key_cache_size == rhs.key_cache_size)) return false; if (__isset.read_repair_chance != rhs.__isset.read_repair_chance) return false; else if (__isset.read_repair_chance && !(read_repair_chance == rhs.read_repair_chance)) return false; if (__isset.column_metadata != rhs.__isset.column_metadata) return false; else if (__isset.column_metadata && !(column_metadata == rhs.column_metadata)) return false; if (__isset.gc_grace_seconds != rhs.__isset.gc_grace_seconds) return false; else if (__isset.gc_grace_seconds && !(gc_grace_seconds == rhs.gc_grace_seconds)) return false; if (__isset.default_validation_class != rhs.__isset.default_validation_class) return false; else if (__isset.default_validation_class && !(default_validation_class == rhs.default_validation_class)) return false; if (__isset.id != rhs.__isset.id) return false; else if (__isset.id && !(id == rhs.id)) return false; if (__isset.min_compaction_threshold != rhs.__isset.min_compaction_threshold) return false; else if (__isset.min_compaction_threshold && !(min_compaction_threshold == rhs.min_compaction_threshold)) return false; if (__isset.max_compaction_threshold != rhs.__isset.max_compaction_threshold) return false; else if (__isset.max_compaction_threshold && !(max_compaction_threshold == rhs.max_compaction_threshold)) return false; if (__isset.row_cache_save_period_in_seconds != rhs.__isset.row_cache_save_period_in_seconds) return false; else if (__isset.row_cache_save_period_in_seconds && !(row_cache_save_period_in_seconds == rhs.row_cache_save_period_in_seconds)) return false; if (__isset.key_cache_save_period_in_seconds != rhs.__isset.key_cache_save_period_in_seconds) return false; else if (__isset.key_cache_save_period_in_seconds && !(key_cache_save_period_in_seconds == rhs.key_cache_save_period_in_seconds)) return false; if (__isset.replicate_on_write != rhs.__isset.replicate_on_write) return false; else if (__isset.replicate_on_write && !(replicate_on_write == rhs.replicate_on_write)) return false; if (__isset.merge_shards_chance != rhs.__isset.merge_shards_chance) return false; else if (__isset.merge_shards_chance && !(merge_shards_chance == rhs.merge_shards_chance)) return false; if (__isset.key_validation_class != rhs.__isset.key_validation_class) return false; else if (__isset.key_validation_class && !(key_validation_class == rhs.key_validation_class)) return false; if (__isset.row_cache_provider != rhs.__isset.row_cache_provider) return false; else if (__isset.row_cache_provider && !(row_cache_provider == rhs.row_cache_provider)) return false; if (__isset.key_alias != rhs.__isset.key_alias) return false; else if (__isset.key_alias && !(key_alias == rhs.key_alias)) return false; if (__isset.compaction_strategy != rhs.__isset.compaction_strategy) return false; else if (__isset.compaction_strategy && !(compaction_strategy == rhs.compaction_strategy)) return false; if (__isset.compaction_strategy_options != rhs.__isset.compaction_strategy_options) return false; else if (__isset.compaction_strategy_options && !(compaction_strategy_options == rhs.compaction_strategy_options)) return false; if (__isset.row_cache_keys_to_save != rhs.__isset.row_cache_keys_to_save) return false; else if (__isset.row_cache_keys_to_save && !(row_cache_keys_to_save == rhs.row_cache_keys_to_save)) return false; if (__isset.compression_options != rhs.__isset.compression_options) return false; else if (__isset.compression_options && !(compression_options == rhs.compression_options)) return false; return true; } bool operator != (const CfDef &rhs) const { return !(*this == rhs); } bool operator < (const CfDef & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _KsDef__isset { _KsDef__isset() : strategy_options(false), replication_factor(false), durable_writes(false) {} bool strategy_options; bool replication_factor; bool durable_writes; } _KsDef__isset; class KsDef { public: static const char* ascii_fingerprint; // = "28433565DB1070CD6C4372A762397EFE"; static const uint8_t binary_fingerprint[16]; // = {0x28,0x43,0x35,0x65,0xDB,0x10,0x70,0xCD,0x6C,0x43,0x72,0xA7,0x62,0x39,0x7E,0xFE}; KsDef() : name(""), strategy_class(""), replication_factor(0), durable_writes(true) { } virtual ~KsDef() throw() {} std::string name; std::string strategy_class; std::map strategy_options; int32_t replication_factor; std::vector cf_defs; bool durable_writes; _KsDef__isset __isset; bool operator == (const KsDef & rhs) const { if (!(name == rhs.name)) return false; if (!(strategy_class == rhs.strategy_class)) return false; if (__isset.strategy_options != rhs.__isset.strategy_options) return false; else if (__isset.strategy_options && !(strategy_options == rhs.strategy_options)) return false; if (__isset.replication_factor != rhs.__isset.replication_factor) return false; else if (__isset.replication_factor && !(replication_factor == rhs.replication_factor)) return false; if (!(cf_defs == rhs.cf_defs)) return false; if (__isset.durable_writes != rhs.__isset.durable_writes) return false; else if (__isset.durable_writes && !(durable_writes == rhs.durable_writes)) return false; return true; } bool operator != (const KsDef &rhs) const { return !(*this == rhs); } bool operator < (const KsDef & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class CqlRow { public: static const char* ascii_fingerprint; // = "470EFC558004E98D92D604898305C04E"; static const uint8_t binary_fingerprint[16]; // = {0x47,0x0E,0xFC,0x55,0x80,0x04,0xE9,0x8D,0x92,0xD6,0x04,0x89,0x83,0x05,0xC0,0x4E}; CqlRow() : key("") { } virtual ~CqlRow() throw() {} std::string key; std::vector columns; bool operator == (const CqlRow & rhs) const { if (!(key == rhs.key)) return false; if (!(columns == rhs.columns)) return false; return true; } bool operator != (const CqlRow &rhs) const { return !(*this == rhs); } bool operator < (const CqlRow & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; class CqlMetadata { public: static const char* ascii_fingerprint; // = "B7C5A4AA9652C744A48EBC1C12D531E7"; static const uint8_t binary_fingerprint[16]; // = {0xB7,0xC5,0xA4,0xAA,0x96,0x52,0xC7,0x44,0xA4,0x8E,0xBC,0x1C,0x12,0xD5,0x31,0xE7}; CqlMetadata() : default_name_type(""), default_value_type("") { } virtual ~CqlMetadata() throw() {} std::map name_types; std::map value_types; std::string default_name_type; std::string default_value_type; bool operator == (const CqlMetadata & rhs) const { if (!(name_types == rhs.name_types)) return false; if (!(value_types == rhs.value_types)) return false; if (!(default_name_type == rhs.default_name_type)) return false; if (!(default_value_type == rhs.default_value_type)) return false; return true; } bool operator != (const CqlMetadata &rhs) const { return !(*this == rhs); } bool operator < (const CqlMetadata & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; typedef struct _CqlResult__isset { _CqlResult__isset() : rows(false), num(false), schema(false) {} bool rows; bool num; bool schema; } _CqlResult__isset; class CqlResult { public: static const char* ascii_fingerprint; // = "521B9CE5AF77539F7267F6952B609E81"; static const uint8_t binary_fingerprint[16]; // = {0x52,0x1B,0x9C,0xE5,0xAF,0x77,0x53,0x9F,0x72,0x67,0xF6,0x95,0x2B,0x60,0x9E,0x81}; CqlResult() : num(0) { } virtual ~CqlResult() throw() {} CqlResultType::type type; std::vector rows; int32_t num; CqlMetadata schema; _CqlResult__isset __isset; bool operator == (const CqlResult & rhs) const { if (!(type == rhs.type)) return false; if (__isset.rows != rhs.__isset.rows) return false; else if (__isset.rows && !(rows == rhs.rows)) return false; if (__isset.num != rhs.__isset.num) return false; else if (__isset.num && !(num == rhs.num)) return false; if (__isset.schema != rhs.__isset.schema) return false; else if (__isset.schema && !(schema == rhs.schema)) return false; return true; } bool operator != (const CqlResult &rhs) const { return !(*this == rhs); } bool operator < (const CqlResult & ) const; uint32_t read(::apache::thrift::protocol::TProtocol* iprot); uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const; }; }}} // namespace #endif opensips-2.2.2/modules/cachedb_cassandra/doc/000077500000000000000000000000001300170765700211705ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_cassandra/doc/cachedb_cassandra.xml000066400000000000000000000017201300170765700253020ustar00rootroot00000000000000 %docentities; ]> cachedb_cassandra Module &osipsname; Vladut-Stefan Paiu OpenSIPS Solutions
vladpaiu@opensips.org &osipssol;
Vladut-Stefan Paiu
vladpaiu@opensips.org
2011 &osipssol;
&admin;
opensips-2.2.2/modules/cachedb_cassandra/doc/cachedb_cassandra_admin.xml000066400000000000000000000177721300170765700264700ustar00rootroot00000000000000 &adminguide;
Overview This module is an implementation of a cache system designed to work with Cassandra servers. It uses the Key-Value interface exported from the core.
Advantages memory costs are no longer on the server many servers can be used inside a cluster, so the memory is virtually unlimited the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The Cassandra DB is also persistent so it can also be restarted without loss of information. Cassandra is an open-source project so it can be used to exchange data with various other applications By creating a Cassandra Cluster, multiple OpenSIPS instances can easily share key-value information
Limitations keys (in key:value pairs) may not contain spaces or control characters
Dependencies
&osips; Modules None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: thrift 0.6.1 Thrift 0.6.1 can be downloaded from http://archive.apache.org/dist/thrift/ Download the archive, extract sources, run ./configure,make,sudo make install.
Exported Parameters
<varname>cachedb_url</varname> (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. The database part of the URL needs to be in the format Keyspace_ColumnFamily_CounterFamily Set <varname>cachedb_url</varname> parameter ... modparam("cachedb_cassandra", "cachedb_url","cassandra:group1://localhost:9061/Keyspace1_Users_Counters"); modparam("cachedb_cassandra", "cachedb_url","cassandra:cluster1://random_url:8888/Keyspace2_Keys_CounterF"); ... Use Cassandra servers ... cache_store("cassandra:group1","key","$ru value"); cache_fetch("cassandra:cluster1","key",$avp(10)); cache_remove("cassandra:cluster1","key"); ...
<varname>connection_timeout</varname> (int) The timeout in ms that will be triggered in case a connection attempt fails. Set <varname>connection_timeout</varname> parameter ... modparam("cachedb_cassandra", "connection_timeout",1000); ...
<varname>send_timeout</varname> (int) The timeout in ms that will be triggered in case a Cassandra write takes too long Set <varname>send_timeout</varname> parameter ... modparam("cachedb_cassandra", "send_timeout",1000); ...
<varname>receive_timeout</varname> (int) The timeout in ms that will be triggered in case a Cassandra read takes too long Set <varname>receive_timeout</varname> parameter ... modparam("cachedb_cassandra", "receive_timeout",1000); ...
<varname>wr_consistency_level</varname> (int) The consistency level desired for write operations. Options are : 1 - Ensure that the write has been written to at least 1 replica's commit log and memory table before responding to the client. 2 - Ensure that the write has been written to N / 2 + 1 replicas before responding to the client. 3 - Ensure that the write has been written to ReplicationFactor / 2 + 1 nodes, within the local datacenter (requires NetworkTopologyStrategy) 4 - Ensure that the write has been written to ReplicationFactor / 2 + 1 nodes in each datacenter (requires NetworkTopologyStrategy) 5 - Ensure that the write is written to all N replicas before responding to the client. Any unresponsive replicas will fail the operation. 6 - Ensure that the write has been written to at least 1 node, including HintedHandoff recipients. 7 - Ensure that the write has been written to at least 2 replica's before responding to the client. 8 - Ensure that the write has been written to at least 3 replica's before responding to the client. Default is 1 Set <varname>wr_consistency_level</varname> parameter ... modparam("cachedb_cassandra", "wr_consistency_level",7); ...
<varname>rd_consistency_level</varname> (int) The consistency level desired for read operations. Options are the same as for write consistency level. Set <varname>rd_consistency_level</varname> parameter ... modparam("cachedb_cassandra", "rd_consistency_level",7); ...
<varname>exec_threshold</varname> (int) The maximum number of microseconds that a cassandra cache query can last. Anything above the threshold will trigger a warning message to the log Default value is 0 ( unlimited - no warnings ). Set <varname>exec_threshold</varname> parameter ... modparam("cachedb_cassandra", "exec_threshold", 100000) ...
Exported Functions The module does not export functions to be used in configuration script.
Known issues Due to the fact that Cassandra cannot store counters and regular columns in the same ColumnFamily, add() and sub() methods are not exported through the Key-Value interface. Also, the module does not currently support Authentication. Future realeases of this module will address this issue.
opensips-2.2.2/modules/cachedb_couchbase/000077500000000000000000000000001300170765700204205ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_couchbase/Makefile000066400000000000000000000005051300170765700220600ustar00rootroot00000000000000# $Id: Makefile 6253 2009-10-12 19:00:53Z vladut_paiu $ # # Couchbase connector # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=cachedb_couchbase.so DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -lcouchbase include ../../Makefile.modules opensips-2.2.2/modules/cachedb_couchbase/README000066400000000000000000000105511300170765700213020ustar00rootroot00000000000000cachedb_couchbase Module Vladut-Stefan Paiu OpenSIPS Solutions Edited by Vladut-Stefan Paiu Copyright © 2013 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Advantages 1.3. Limitations 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. cachedb_url (string) 1.5.2. timeout (int) 1.5.3. exec_threshold (int) 1.5.4. lazy_connect (int) 1.5.5. Exported Functions List of Examples 1.1. Set cachedb_url parameter 1.2. Set timeout parameter 1.3. Set exec_threshold parameter 1.4. Set lazy_connect parameter 1.5. Use CouchBase servers Chapter 1. Admin Guide 1.1. Overview This module is an implementation of a cache system designed to work with a Couchbase server. It uses the libcouchbase client library to connect to the server instance, It uses the Key-Value interface exported from the core. 1.2. Advantages * memory costs are no longer on the server * many servers can be used inside a cluster, so the memory is virtually unlimited * the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The CouchBase DB is also persistent so it can also be restarted without loss of information. * CouchBase is an open-source project so it can be used to exchange data with various other applications * By creating a CouchBase Cluster, multiple OpenSIPS instances can easily share key-value information 1.3. Limitations * keys (in key:value pairs) may not contain spaces or control characters 1.4. Dependencies 1.4.1. OpenSIPS Modules None. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libcouchbase >= 2.0: libcoucbase can be downloaded from http://www.couchbase.com/develop/c/current 1.5. Exported Parameters 1.5.1. cachedb_url (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. The format of the URL is couchbase[:identifier]://[username:password@]IP:Port/bucket_nam e Example 1.1. Set cachedb_url parameter ... modparam("cachedb_couchbase", "cachedb_url","couchbase:group1://localhos t:6379/default"); modparam("cachedb_couchbase", "cachedb_url","couchbase:cluster1://random _url:8888/my_bucket"); #Multiple hosts modparam("cachedb_couchbase", "cachedb_url","couchbase:cluster1://random _url1:8888;random_url2:8888;random_url3:8888/my_bucket"); ... 1.5.2. timeout (int) The max duration in microseconds that a couchbase op is expected to last. Default is 3000000 ( 3 seconds ) Example 1.2. Set timeout parameter ... modparam("cachedb_couchbase", "timeout",5000000); ... 1.5.3. exec_threshold (int) The maximum number of microseconds that a couchbase query can last. Anything above the threshold will trigger a warning message to the log Default value is “0 ( unlimited - no warnings )â€. Example 1.3. Set exec_threshold parameter ... modparam("cachedb_couchbase", "exec_threshold", 100000) ... 1.5.4. lazy_connect (int) Delay connecting to a bucket until the first time it is used. Connecting to many buckets at startup can be time consuming. This option allows for faster startup by delaying connections until they are needed. This option can be dangerous for untested bucket configurations/settings. Always test first without lazy_connect. This option will show errors in the log during the first access made to a bucket. Default is 0 ( Connect to all buckets on startup ) Example 1.4. Set lazy_connect parameter ... modparam("cachedb_couchbase", "lazy_connect", 1); ... Example 1.5. Use CouchBase servers ... cache_store("couchbase:group1","key","$ru value"); cache_fetch("couchbase:cluster1","key",$avp(10)); cache_remove("couchbase:cluster1","key"); ... 1.5.5. Exported Functions The module does not export functions to be used in configuration script. opensips-2.2.2/modules/cachedb_couchbase/cachedb_couchbase.c000066400000000000000000000100571300170765700241540ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2013-01-xx created (vlad-paiu) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../pt.h" #include "../../cachedb/cachedb.h" #include "cachedb_couchbase_dbase.h" static int mod_init(void); static int child_init(int); static void destroy(void); static str cache_mod_name = str_init("couchbase"); struct cachedb_url *couchbase_script_urls = NULL; int couch_timeout_usec=3000000; /* defaults to 3 seconds */ int couch_lazy_connect=0;/*Don't be lazy, connect on start*/ int couch_exec_threshold=0; int set_connection(unsigned int type, void *val) { return cachedb_store_url(&couchbase_script_urls,(char *)val); } static param_export_t params[]={ { "cachedb_url", STR_PARAM|USE_FUNC_PARAM, (void *)&set_connection}, { "timeout", INT_PARAM, &couch_timeout_usec }, { "lazy_connect", INT_PARAM, &couch_lazy_connect }, { "exec_threshold", INT_PARAM, &couch_exec_threshold }, {0,0,0} }; /** module exports */ struct module_exports exports= { "cachedb_couchbase", /* module name */ MOD_TYPE_CACHEDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function)destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { cachedb_engine cde; LM_NOTICE("initializing module cachedb_couchbase ...\n"); cde.name = cache_mod_name; cde.cdb_func.init = couchbase_init; cde.cdb_func.destroy = couchbase_destroy; cde.cdb_func.get = couchbase_get; cde.cdb_func.get_counter = couchbase_get_counter; cde.cdb_func.set = couchbase_set; cde.cdb_func.remove = couchbase_remove; cde.cdb_func.add = couchbase_add; cde.cdb_func.sub = couchbase_sub; cde.cdb_func.capability = 0; if (register_cachedb(&cde) < 0) { LM_ERR("failed to initialize cachedb_couchbase\n"); return -1; } return 0; } static int child_init(int rank) { struct cachedb_url *it; cachedb_con *con; if(rank == PROC_MAIN || rank == PROC_TCP_MAIN) { return 0; } for (it = couchbase_script_urls;it;it=it->next) { LM_DBG("iterating through couchbase conns - [%.*s]\n",it->url.len,it->url.s); con = couchbase_init(&it->url); if (con == NULL) { LM_ERR("failed to open connection\n"); return -1; } if (cachedb_put_connection(&cache_mod_name,con) < 0) { LM_ERR("failed to insert connection\n"); return -1; } } cachedb_free_url(couchbase_script_urls); return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module cachedb_couchbase ...\n"); cachedb_end_connections(&cache_mod_name); return; } opensips-2.2.2/modules/cachedb_couchbase/cachedb_couchbase_dbase.c000066400000000000000000000406151300170765700253150ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2013-01-xx created (vlad-paiu) */ #include "../../dprint.h" #include "cachedb_couchbase_dbase.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../cachedb/cachedb.h" #include #include extern int couch_timeout_usec; extern int couch_lazy_connect; extern int couch_exec_threshold; volatile str get_res = {0,0}; volatile int arithmetic_res = 0; volatile lcb_error_t op_error = LCB_SUCCESS; static void couchbase_get_cb(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_get_resp_t *item) { op_error = error; if (error != LCB_SUCCESS) { if (error != LCB_KEY_ENOENT) { LM_ERR("Failure to get %.*s - %s\n",(int)item->v.v0.nkey,(char*)item->v.v0.key,lcb_strerror(instance, error)); } return; } get_res.s = pkg_malloc((int)item->v.v0.nbytes); if (!get_res.s) { LM_ERR("No more pkg mem\n"); return; } memcpy(get_res.s,item->v.v0.bytes,item->v.v0.nbytes); get_res.len = item->v.v0.nbytes; } static void couchbase_store_cb(lcb_t instance, const void *cookie, lcb_storage_t operation, lcb_error_t err, const lcb_store_resp_t *item) { op_error = err; if (err != LCB_SUCCESS) { LM_ERR("Failure to store %.*s - %s\n",(int)item->v.v0.nkey,(char*)item->v.v0.key,lcb_strerror(instance, err)); } } static void couchbase_remove_cb(lcb_t instance, const void *cookie, lcb_error_t err, const lcb_remove_resp_t *item) { op_error = err; if (err != LCB_SUCCESS) { if (err != LCB_KEY_ENOENT) { LM_ERR("Failure to remove %.*s - %s\n",(int)item->v.v0.nkey,(char*)item->v.v0.key,lcb_strerror(instance, err)); } } } static void couchbase_arithmetic_cb(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_arithmetic_resp_t *item) { op_error = error; if (error != LCB_SUCCESS) { LM_ERR("Failure to perform arithmetic %.*s - %s\n",(int)item->v.v0.nkey,(char*)item->v.v0.key,lcb_strerror(instance, error)); return; } arithmetic_res = item->v.v0.value; } lcb_error_t cb_connect(lcb_t instance) { lcb_error_t rc; rc = lcb_connect(instance); if (rc != LCB_SUCCESS) { return rc; } rc = lcb_wait(instance); if (rc != LCB_SUCCESS) { return rc; } rc = lcb_get_bootstrap_status(instance); return rc; } lcb_error_t cb_get(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_get_cmd_t *const *commands) { lcb_error_t rc; rc = lcb_get(instance, command_cookie, num, commands); if (rc != LCB_SUCCESS) { return rc; } rc = lcb_wait(instance); if (rc != LCB_SUCCESS) { return rc; } return op_error; } lcb_error_t cb_store(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_store_cmd_t *const *commands) { lcb_error_t rc; rc = lcb_store(instance, command_cookie, num, commands); if (rc != LCB_SUCCESS) { return rc; } rc = lcb_wait(instance); if (rc != LCB_SUCCESS) { return rc; } return op_error; } lcb_error_t cb_arithmetic(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_arithmetic_cmd_t *const *commands) { lcb_error_t rc; rc = lcb_arithmetic(instance, command_cookie, num, commands); if (rc != LCB_SUCCESS) { return rc; } rc = lcb_wait(instance); if (rc != LCB_SUCCESS) { return rc; } return op_error; } lcb_error_t cb_remove(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_remove_cmd_t *const *commands) { lcb_error_t rc; rc = lcb_remove(instance, command_cookie, num, commands); if (rc != LCB_SUCCESS) { return rc; } rc = lcb_wait(instance); if (rc != LCB_SUCCESS) { return rc; } return op_error; } couchbase_con* couchbase_connect(struct cachedb_id* id, int is_reconnect) { couchbase_con *con; struct lcb_create_st options; lcb_uint32_t tmo = 0; lcb_t instance; lcb_error_t rc; if (id == NULL) { LM_ERR("null cachedb_id\n"); return 0; } con = pkg_malloc(sizeof(couchbase_con)); if (con == NULL) { LM_ERR("no more pkg \n"); return 0; } memset(con,0,sizeof(couchbase_con)); con->id = id; con->ref = 1; /* TODO - support custom ports - couchbase expects host:port in id->host */ memset(&options,0,sizeof(struct lcb_create_st)); options.version = 0; options.v.v0.host = id->host; options.v.v0.user = id->username; options.v.v0.passwd = id->password; options.v.v0.bucket = id->database; rc=lcb_create(&instance, &options); if (rc!=LCB_SUCCESS) { LM_ERR("Failed to create libcouchbase instance: 0x%02x, %s\n", rc, lcb_strerror(NULL, rc)); return 0; } (void)lcb_set_get_callback(instance, couchbase_get_cb); (void)lcb_set_store_callback(instance, couchbase_store_cb); (void)lcb_set_remove_callback(instance, couchbase_remove_cb); (void)lcb_set_arithmetic_callback(instance,couchbase_arithmetic_cb); //Set Timeout tmo = (lcb_uint32_t)couch_timeout_usec; lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_OP_TIMEOUT, &tmo); if (couch_lazy_connect == 0 || is_reconnect == 1) { rc=cb_connect(instance); /*Check connection*/ if (rc != LCB_SUCCESS) { /*Consider these connect failurs as fatal*/ if(rc == LCB_AUTH_ERROR || rc == LCB_INVALID_HOST_FORMAT || rc == LCB_INVALID_CHAR) { LM_ERR("Fatal connection error to Couchbase. Host: %s Bucket: %s Error: %s", id->host, id->database, lcb_strerror(instance, rc)); lcb_destroy(instance); return 0; } else { /* Non-fatal errors, we may be able to connect later */ LM_ERR("Non-Fatal connection error to Couchbase. Host: %s Bucket: %s Error: %s", id->host, id->database, lcb_strerror(instance, rc)); } } else { LM_DBG("Successfully connected to Couchbase Server. Host: %s Bucket: %s\n", id->host, id->database); } } con->couchcon = instance; return con; } couchbase_con* couchbase_new_connection(struct cachedb_id* id) { return couchbase_connect(id, 0); } cachedb_con *couchbase_init(str *url) { return cachedb_do_init(url,(void *)couchbase_new_connection); } void couchbase_free_connection(cachedb_pool_con *con) { couchbase_con * c; LM_DBG("in couchbase_free_connection\n"); if (!con) return; c = (couchbase_con *)con; lcb_destroy(c->couchcon); pkg_free(c); } void couchbase_destroy(cachedb_con *con) { cachedb_do_close(con,couchbase_free_connection); } /*Conditionally reconnect based on the error code*/ int couchbase_conditional_reconnect(cachedb_con *con, lcb_error_t err) { cachedb_pool_con *tmp; void *newcon; if (!con) return -1; switch (err) { /* Error codes to attempt reconnects on */ case LCB_EINTERNAL: case LCB_CLIENT_ETMPFAIL: case LCB_EBADHANDLE: case LCB_NETWORK_ERROR: case LCB_ETIMEDOUT: break; default: /*nothing to do*/ return 0; break; } tmp = (cachedb_pool_con*)(con->data); LM_ERR("Attempting reconnect to Couchbase. Host: %s Bucket: %s On Error: %s", tmp->id->host, tmp->id->database, lcb_strerror(COUCHBASE_CON(con), err)); newcon = couchbase_connect(tmp->id, 1); /*Successful reconnect, get rid of the old handle*/ if (newcon != NULL) { LM_ERR("Successfully reconnected to Couchbase. Host: %s Bucket: %s", tmp->id->host, tmp->id->database); tmp->id = NULL; couchbase_free_connection(tmp); con->data = newcon; return 1; } LM_ERR("Failed to reconnect to Couchbase. Host: %s Bucket: %s", tmp->id->host, tmp->id->database); return -2; } int couchbase_set(cachedb_con *connection,str *attr, str *val,int expires) { lcb_t instance; lcb_error_t oprc; lcb_store_cmd_t cmd; const lcb_store_cmd_t *commands[1]; struct timeval start; start_expire_timer(start,couch_exec_threshold); instance = COUCHBASE_CON(connection); commands[0] = &cmd; memset(&cmd, 0, sizeof(cmd)); cmd.v.v0.operation = LCB_SET; cmd.v.v0.key = attr->s; cmd.v.v0.nkey = attr->len; cmd.v.v0.bytes = val->s; cmd.v.v0.nbytes = val->len; cmd.v.v0.exptime = expires; oprc = cb_store(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { LM_ERR("Set request failed - %s\n", lcb_strerror(instance, oprc)); //Attempt reconnect if(couchbase_conditional_reconnect(connection, oprc) != 1) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase set",attr->s,attr->len,0); return -2; } //Try again instance = COUCHBASE_CON(connection); oprc = cb_store(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { LM_ERR("Set command retry failed - %s\n", lcb_strerror(instance, oprc)); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase set",attr->s,attr->len,0); return -2; } LM_ERR("Set command successfully retried\n"); } LM_DBG("Successfully stored\n"); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase set",attr->s,attr->len,0); return 1; } int couchbase_remove(cachedb_con *connection,str *attr) { lcb_t instance; lcb_error_t oprc; lcb_remove_cmd_t cmd; const lcb_remove_cmd_t *commands[1]; struct timeval start; start_expire_timer(start,couch_exec_threshold); instance = COUCHBASE_CON(connection); commands[0] = &cmd; memset(&cmd, 0, sizeof(cmd)); cmd.v.v0.key = attr->s; cmd.v.v0.nkey = attr->len; oprc = cb_remove(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { if (oprc == LCB_KEY_ENOENT) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase remove",attr->s,attr->len,0); return -1; } LM_ERR("Failed to send the remove query - %s\n", lcb_strerror(instance, oprc)); if (couchbase_conditional_reconnect(connection, oprc) != 1) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase remove",attr->s,attr->len,0); return -2; }; instance = COUCHBASE_CON(connection); oprc = cb_remove(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { if (oprc == LCB_KEY_ENOENT) { LM_ERR("Remove command successfully retried\n"); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase remove",attr->s,attr->len,0); return -1; } LM_ERR("Remove command retry failed - %s\n", lcb_strerror(instance, oprc)); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase remove",attr->s,attr->len,0); return -2; } LM_ERR("Remove command successfully retried\n"); } LM_DBG("Successfully removed\n"); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase remove",attr->s,attr->len,0); return 1; } int couchbase_get(cachedb_con *connection,str *attr,str *val) { lcb_t instance; lcb_error_t oprc; lcb_get_cmd_t cmd; const lcb_get_cmd_t *commands[1]; struct timeval start; start_expire_timer(start,couch_exec_threshold); instance = COUCHBASE_CON(connection); commands[0] = &cmd; memset(&cmd, 0, sizeof(cmd)); cmd.v.v0.key = attr->s; cmd.v.v0.nkey = attr->len; oprc = cb_get(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { /* Key not present, record does not exist */ if (oprc == LCB_KEY_ENOENT) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get",attr->s,attr->len,0); return -1; } //Attempt reconnect if (couchbase_conditional_reconnect(connection, oprc) != 1) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get",attr->s,attr->len,0); return -2; } //Try Again instance = COUCHBASE_CON(connection); oprc = cb_get(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { if (oprc == LCB_KEY_ENOENT) { LM_ERR("Get command successfully retried\n"); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get",attr->s,attr->len,0); return -1; } LM_ERR("Get command retry failed - %s\n", lcb_strerror(instance, oprc)); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get",attr->s,attr->len,0); return -2; } LM_ERR("Get command successfully retried\n"); } //Incase of malloc failure if (!get_res.s) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get",attr->s,attr->len,0); return -2; } stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get",attr->s,attr->len,0); *val = get_res; return 1; } int couchbase_add(cachedb_con *connection,str *attr,int val,int expires,int *new_val) { lcb_t instance; lcb_error_t oprc; lcb_arithmetic_cmd_t cmd; const lcb_arithmetic_cmd_t *commands[1]; struct timeval start; start_expire_timer(start,couch_exec_threshold); instance = COUCHBASE_CON(connection); commands[0] = &cmd; memset(&cmd, 0, sizeof(cmd)); cmd.v.v0.key = attr->s; cmd.v.v0.nkey = attr->len; cmd.v.v0.delta = val; cmd.v.v0.create = 1; cmd.v.v0.initial = val; cmd.v.v0.exptime = expires; oprc = cb_arithmetic(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { if (oprc == LCB_KEY_ENOENT) { return -1; stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase add",attr->s,attr->len,0); } LM_ERR("Failed to send the arithmetic query - %s\n", lcb_strerror(instance, oprc)); //Attempt reconnect if (couchbase_conditional_reconnect(connection, oprc) != 1) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase add",attr->s,attr->len,0); return -2; } //Try again instance = COUCHBASE_CON(connection); oprc = cb_arithmetic(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { if (oprc == LCB_KEY_ENOENT) { LM_ERR("Arithmetic command successfully retried\n"); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase add",attr->s,attr->len,0); return -1; } LM_ERR("Arithmetic command retry failed - %s\n", lcb_strerror(instance, oprc)); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase add",attr->s,attr->len,0); return -2; } LM_ERR("Arithmetic command successfully retried\n"); } if (new_val) *new_val = arithmetic_res; stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase add",attr->s,attr->len,0); return 1; } int couchbase_sub(cachedb_con *connection,str *attr,int val,int expires,int *new_val) { return couchbase_add(connection,attr,-val,expires,new_val); } int couchbase_get_counter(cachedb_con *connection,str *attr,int *val) { lcb_t instance; lcb_error_t oprc; lcb_get_cmd_t cmd; const lcb_get_cmd_t *commands[1]; struct timeval start; start_expire_timer(start,couch_exec_threshold); instance = COUCHBASE_CON(connection); commands[0] = &cmd; memset(&cmd, 0, sizeof(cmd)); cmd.v.v0.key = attr->s; cmd.v.v0.nkey = attr->len; oprc = cb_get(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { /* Key not present, record does not exist */ if (oprc == LCB_KEY_ENOENT) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get counter",attr->s,attr->len,0); return -1; } //Attempt reconnect if (couchbase_conditional_reconnect(connection, oprc) != 1) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get counter ",attr->s,attr->len,0); return -2; } //Try Again instance = COUCHBASE_CON(connection); oprc = cb_get(instance, NULL, 1, commands); if (oprc != LCB_SUCCESS) { if (oprc == LCB_KEY_ENOENT) { LM_ERR("Get counter command successfully retried\n"); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get counter",attr->s,attr->len,0); return -1; } LM_ERR("Get counter command retry failed - %s\n", lcb_strerror(instance, oprc)); stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get counter",attr->s,attr->len,0); return -2; } LM_ERR("Get command successfully retried\n"); } //Incase of malloc failure if (!get_res.s) { stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get counter",attr->s,attr->len,0); return -2; } stop_expire_timer(start,couch_exec_threshold, "cachedb_couchbase get counter",attr->s,attr->len,0); if (str2sint((str *)&get_res,val)) { LM_ERR("Failued to convert counter [%.*s] to int\n",get_res.len,get_res.s); pkg_free(get_res.s); get_res.s = NULL; return -1; } pkg_free(get_res.s); get_res.s = NULL; return 1; } opensips-2.2.2/modules/cachedb_couchbase/cachedb_couchbase_dbase.h000066400000000000000000000036201300170765700253150ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2013-01-xx created (vlad-paiu) */ #ifndef CACHEDBCOUCHBASE_DBASE_H #define CACHEDBCOUCHBASE_DBASE_H #include #include #include "../../cachedb/cachedb.h" typedef struct { struct cachedb_id *id; unsigned int ref; struct cachedb_pool_con_t *next; lcb_t couchcon; } couchbase_con; #define COUCHBASE_CON(cdb_con) (((couchbase_con*)((cdb_con)->data))->couchcon) #define COUCH_KEY_SEPPARATOR '|' #define COUCH_KEY_SEPPARATOR_LEN 1 #define COUCH_VIEW_MARKER '?' #define COUCH_VIEW_MARKER_LEN 1 cachedb_con* couchbase_init(str *url); void couchbase_destroy(cachedb_con *con); int couchbase_set(cachedb_con *connection,str *attr,str *val,int expires); int couchbase_get(cachedb_con *con,str *attr,str *val); int couchbase_remove(cachedb_con *con,str *attr); int couchbase_add(cachedb_con *connection,str *attr,int val,int expires,int *new_val); int couchbase_sub(cachedb_con *connection,str *attr,int val,int expires,int *new_val); int couchbase_get_counter(cachedb_con *connection,str *attr,int *val); #endif /* CACHEDBCOUCHBASE_DBASE_H */ opensips-2.2.2/modules/cachedb_couchbase/doc/000077500000000000000000000000001300170765700211655ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_couchbase/doc/cachedb_couchbase.xml000066400000000000000000000020111300170765700252660ustar00rootroot00000000000000 %docentities; ]> cachedb_couchbase Module &osipsname; Vladut-Stefan Paiu OpenSIPS Solutions
vladpaiu@opensips.org http://www.opensips.org
Vladut-Stefan Paiu
vladpaiu@opensips.org
2013 &osipssol;
&admin;
opensips-2.2.2/modules/cachedb_couchbase/doc/cachedb_couchbase_admin.xml000066400000000000000000000125601300170765700264500ustar00rootroot00000000000000 &adminguide;
Overview This module is an implementation of a cache system designed to work with a Couchbase server. It uses the libcouchbase client library to connect to the server instance, It uses the Key-Value interface exported from the core.
Advantages memory costs are no longer on the server many servers can be used inside a cluster, so the memory is virtually unlimited the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The CouchBase DB is also persistent so it can also be restarted without loss of information. CouchBase is an open-source project so it can be used to exchange data with various other applications By creating a CouchBase Cluster, multiple OpenSIPS instances can easily share key-value information
Limitations keys (in key:value pairs) may not contain spaces or control characters
Dependencies
&osips; Modules None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libcouchbase >= 2.0: libcoucbase can be downloaded from http://www.couchbase.com/develop/c/current
Exported Parameters
<varname>cachedb_url</varname> (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. The format of the URL is couchbase[:identifier]://[username:password@]IP:Port/bucket_name Set <varname>cachedb_url</varname> parameter ... modparam("cachedb_couchbase", "cachedb_url","couchbase:group1://localhost:6379/default"); modparam("cachedb_couchbase", "cachedb_url","couchbase:cluster1://random_url:8888/my_bucket"); #Multiple hosts modparam("cachedb_couchbase", "cachedb_url","couchbase:cluster1://random_url1:8888;random_url2:8888;random_url3:8888/my_bucket"); ...
<varname>timeout</varname> (int) The max duration in microseconds that a couchbase op is expected to last. Default is 3000000 ( 3 seconds ) Set <varname>timeout</varname> parameter ... modparam("cachedb_couchbase", "timeout",5000000); ...
<varname>exec_threshold</varname> (int) The maximum number of microseconds that a couchbase query can last. Anything above the threshold will trigger a warning message to the log Default value is 0 ( unlimited - no warnings ). Set <varname>exec_threshold</varname> parameter ... modparam("cachedb_couchbase", "exec_threshold", 100000) ...
<varname>lazy_connect</varname> (int) Delay connecting to a bucket until the first time it is used. Connecting to many buckets at startup can be time consuming. This option allows for faster startup by delaying connections until they are needed. This option can be dangerous for untested bucket configurations/settings. Always test first without lazy_connect. This option will show errors in the log during the first access made to a bucket. Default is 0 ( Connect to all buckets on startup ) Set <varname>lazy_connect</varname> parameter ... modparam("cachedb_couchbase", "lazy_connect", 1); ...
Use CouchBase servers ... cache_store("couchbase:group1","key","$ru value"); cache_fetch("couchbase:cluster1","key",$avp(10)); cache_remove("couchbase:cluster1","key"); ...
Exported Functions The module does not export functions to be used in configuration script.
opensips-2.2.2/modules/cachedb_local/000077500000000000000000000000001300170765700175565ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_local/Makefile000066400000000000000000000004061300170765700212160ustar00rootroot00000000000000# $Id: Makefile 6253 2009-10-12 19:00:53Z bogdan_iancu $ # # Memory Cache module # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=cachedb_local.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/cachedb_local/README000066400000000000000000000054461300170765700204470ustar00rootroot00000000000000LOCALCACHE Anca-Maria Vamanu OpenSIPS Edited by Anca-Maria Vamanu Edited by Vladut-Stefan Paiu Copyright © 2009 Anca-Maria Vamanu Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. cache_table_size (int) 1.3.2. exec_threshold (int) 1.3.3. cache_clean_period (int) 1.3.4. Exported Functions 1.3.5. Exported MI Functions List of Examples 1.1. Set cache_table_size parameter 1.2. Set exec_threshold parameter 1.3. Set cache_clean_period parameter 1.4. cache_remove_chunk usage Chapter 1. Admin Guide 1.1. Overview This module is an implementation of a local cache system designed as a hash table. It uses the Key-Value interface exported by OpenSIPS core. 1.2. Dependencies 1.2.1. OpenSIPS Modules None. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.3. Exported Parameters 1.3.1. cache_table_size (int) The size of the hash table. This parameter will be used as the power of 2 when computing table size. Default value is “9 (512)â€. Example 1.1. Set cache_table_size parameter ... modparam("cachedb_local", "cache_table_size", 10) ... 1.3.2. exec_threshold (int) The maximum number of microseconds that a local cache query can last. Anything above the threshold will trigger a warning message to the log Default value is “0 ( unlimited - no warnings )â€. Example 1.2. Set exec_threshold parameter ... modparam("cachedb_local", "exec_threshold", 100000) ... 1.3.3. cache_clean_period (int) The time interval in seconds at which to go through all the records and delete the expired ones. Default value is “600 (10 minutes)â€. Example 1.3. Set cache_clean_period parameter ... modparam("cachedb_local", "cache_clean_period", 1200) ... 1.3.4. Exported Functions 1.3.4.1. cache_remove_chunk(glob) Remove all keys from local cache that match the glob pattern This function can be used from all routes Example 1.4. cache_remove_chunk usage ... cache_remove_chunk("myinfo_*"); ... 1.3.5. Exported MI Functions 1.3.5.1. cache_remove_chunk Removes all local cache entries that match the provided glob param. Parameters : * glob - keys that match glob will be removed MI FIFO Command Format: :cache_remove_chunk:_reply_fifo_file_ keyprefix* _empty_line_ opensips-2.2.2/modules/cachedb_local/cachedb_local.c000066400000000000000000000210701300170765700224450ustar00rootroot00000000000000/* * memory cache system module * * Copyright (C) 2009 Anca Vamanu * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-01-29 initial version (Anca Vamanu) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../timer.h" #include "../../error.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "../../mi/tree.h" #include "cachedb_local.h" #include "hash.h" #include str cache_mod_name = str_init("local"); static int mod_init(void); static int child_init(int rank); static void destroy(void); lcache_t* cache_htable = NULL; int cache_htable_size = 9; int cache_clean_period = 600; int local_exec_threshold = 0; static int remove_chunk_f(struct sip_msg* msg, char* glob); struct mi_root * mi_cache_remove_chunk(struct mi_root *cmd_tree,void *param); void localcache_clean(unsigned int ticks,void *param); static param_export_t params[]={ { "cache_table_size", INT_PARAM, &cache_htable_size }, { "cache_clean_period", INT_PARAM, &cache_clean_period }, { "exec_threshold", INT_PARAM, &local_exec_threshold }, {0,0,0} }; static cmd_export_t cmds[]= { {"cache_remove_chunk", (cmd_function)remove_chunk_f, 1, fixup_str_null, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE}, {0,0,0,0,0,0} }; static mi_export_t mi_cmds[] = { { "cache_remove_chunk", 0, mi_cache_remove_chunk, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; /** module exports */ struct module_exports exports= { "cachedb_local", /* module name */ MOD_TYPE_CACHEDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ child_init /* per-child init function */ }; static char *key_buff = NULL; static int key_buff_size = 0; static char *pat_buff = NULL; static int pat_buff_size = 0; static int remove_chunk_f(struct sip_msg* msg, char* glob) { int i; str *pat = (str *)glob; lcache_entry_t* me1, *me2; struct timeval start; if (pat->len+1 > pat_buff_size) { pat_buff = pkg_realloc(pat_buff,pat->len+1); if (pat_buff == NULL) { LM_ERR("No more pkg mem\n"); pat_buff_size = 0; return -1; } pat_buff_size = pat->len +1; } memcpy(pat_buff,pat->s,pat->len); pat_buff[pat->len] = 0; LM_DBG("trying to remove chunk with pattern [%s]\n",pat_buff); start_expire_timer(start,local_exec_threshold); for(i = 0; i< cache_htable_size; i++) { lock_get(&cache_htable[i].lock); me1 = cache_htable[i].entries; me2 = NULL; while(me1) { if (me1->attr.len + 1 > key_buff_size) { key_buff = pkg_realloc(key_buff,me1->attr.len+1); if (key_buff == NULL) { LM_ERR("No more pkg mem\n"); key_buff_size = 0; lock_release(&cache_htable[i].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local remove_chunk",pat->s,pat->len,0); return -1; } key_buff_size = me1->attr.len + 1; } memcpy(key_buff,me1->attr.s,me1->attr.len); key_buff[me1->attr.len] = 0; if(fnmatch(pat_buff,key_buff,0) == 0) { LM_DBG("[%.*s] matches glob [%.*s] - removing from bucket %d\n", me1->attr.len, me1->attr.s,pat_buff_size,pat_buff,i); if(me2) { me2->next = me1->next; shm_free(me1); me1 = me2->next; } else{ cache_htable[i].entries = me1->next; shm_free(me1); me1 = cache_htable[i].entries; } } else { me2 = me1; me1 = me1->next; } } lock_release(&cache_htable[i].lock); } stop_expire_timer(start,local_exec_threshold, "cachedb_local remove_chunk",pat->s,pat->len,0); return 1; } struct mi_root * mi_cache_remove_chunk(struct mi_root *cmd_tree,void *param) { struct mi_node* node; int status, msg_len; char *msg; node = cmd_tree->node.kids; if (node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if (!node->value.s || !node->value.len) return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); if (remove_chunk_f(NULL,(char *)(&node->value)) < 1) { status = 500; msg = MI_INTERNAL_ERR_S; msg_len = MI_INTERNAL_ERR_LEN; } else { status = 200; msg = MI_OK_S; msg_len = MI_OK_LEN; } return init_mi_tree(status,msg,msg_len); } lcache_con* lcache_new_connection(struct cachedb_id* id) { lcache_con *con; if (id == NULL) { LM_ERR("null db_id\n"); return 0; } if (id->flags != CACHEDB_ID_NO_URL) { LM_ERR("bogus url for local cachedb\n"); return 0; } con = pkg_malloc(sizeof(lcache_con)); if (con == NULL) { LM_ERR("no more pkg\n"); return 0; } memset(con,0,sizeof(lcache_con)); con->id = id; con->ref = 1; return con; } cachedb_con *lcache_init(str *url) { return cachedb_do_init(url,(void *)lcache_new_connection); } void lcache_free_connection(cachedb_pool_con *con) { pkg_free(con); } void lcache_destroy(cachedb_con *con) { cachedb_do_close(con,lcache_free_connection); } /** * init module function */ static int mod_init(void) { cachedb_engine cde; cachedb_con *con; str url=str_init("local://"); str name=str_init("local"); if(cache_htable_size< 1) cache_htable_size= 512; else cache_htable_size= 1<< cache_htable_size; if(lcache_htable_init(cache_htable_size) < 0) { LM_ERR("failed to initialize cache hash table\n"); return -1; } /* register the cache system */ cde.name = cache_mod_name; cde.cdb_func.init = lcache_init; cde.cdb_func.destroy = lcache_destroy; cde.cdb_func.get = lcache_htable_fetch; cde.cdb_func.get_counter = lcache_htable_fetch_counter; cde.cdb_func.set = lcache_htable_insert; cde.cdb_func.remove = lcache_htable_remove; cde.cdb_func.add = lcache_htable_add; cde.cdb_func.sub = lcache_htable_sub; cde.cdb_func.capability = CACHEDB_CAP_BINARY_VALUE; if(cache_clean_period <= 0 ) { LM_ERR("Wrong parameter cache_clean_period - need a positive value\n"); return -1; } if( register_cachedb(&cde)< 0) { LM_ERR("failed to register to core memory store interface\n"); return -1; } /* insert connection for script */ con = lcache_init(&url); if (con == NULL) { LM_ERR("failed to init connection for script\n"); return -1; } if (cachedb_put_connection(&name,con) < 0) { LM_ERR("failed to insert connection for script\n"); return -1; } /* register timer to delete the expired entries */ register_timer("localcache-expire",localcache_clean, 0, cache_clean_period, TIMER_FLAG_DELAY_ON_DELAY); return 0; } /** * Initialize children */ static int child_init(int rank) { return 0; } /* * destroy function */ static void destroy(void) { lcache_htable_destroy(); } void localcache_clean(unsigned int ticks,void *param) { int i; lcache_entry_t* me1, *me2; LM_DBG("start\n"); for(i = 0; i< cache_htable_size; i++) { lock_get(&cache_htable[i].lock); me1 = cache_htable[i].entries; me2 = NULL; while(me1) { if((me1->expires > 0) && (me1->expires < get_ticks())) { LM_DBG("deleted entry attr= [%.*s]\n", me1->attr.len, me1->attr.s); if(me2) { me2->next = me1->next; shm_free(me1); me1 = me2->next; } else { cache_htable[i].entries = me1->next; shm_free(me1); me1 = cache_htable[i].entries; } } else { me2 = me1; me1 = me1->next; } } lock_release(&cache_htable[i].lock); } } opensips-2.2.2/modules/cachedb_local/cachedb_local.h000066400000000000000000000023521300170765700224540ustar00rootroot00000000000000/* * memory cache system module * * Copyright (C) 2009 Anca Vamanu * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-01-30 initial version (Anca Vamanu) */ #ifndef _MEMCACHE_ #define _MEMCACHE_ #include "../../cachedb/cachedb.h" #include "../../cachedb/cachedb_cap.h" #include "hash.h" extern lcache_t* cache_htable; extern int cache_htable_size; extern int local_exec_threshold; typedef struct { struct cachedb_id *id; unsigned int ref; struct cachedb_pool_con_t *next; } lcache_con; #endif opensips-2.2.2/modules/cachedb_local/doc/000077500000000000000000000000001300170765700203235ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_local/doc/cachedb_local.xml000066400000000000000000000027201300170765700235710ustar00rootroot00000000000000 %docentities; ]> LOCALCACHE &osipsname; Anca-Maria Vamanu &osips;
anca@opensips.org http://opensips.org
Anca-Maria Vamanu
anca@opensips.org
Vladut-Stefan Paiu
vladpaiu@opensips.org
2009 Anca-Maria Vamanu $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/cachedb_local/doc/cachedb_local_admin.xml000066400000000000000000000067351300170765700247530ustar00rootroot00000000000000 &adminguide;
Overview This module is an implementation of a local cache system designed as a hash table. It uses the Key-Value interface exported by OpenSIPS core.
Dependencies
&osips; Modules None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters
<varname>cache_table_size</varname> (int) The size of the hash table. This parameter will be used as the power of 2 when computing table size. Default value is 9 (512). Set <varname>cache_table_size</varname> parameter ... modparam("cachedb_local", "cache_table_size", 10) ...
<varname>exec_threshold</varname> (int) The maximum number of microseconds that a local cache query can last. Anything above the threshold will trigger a warning message to the log Default value is 0 ( unlimited - no warnings ). Set <varname>exec_threshold</varname> parameter ... modparam("cachedb_local", "exec_threshold", 100000) ...
<varname>cache_clean_period</varname> (int) The time interval in seconds at which to go through all the records and delete the expired ones. Default value is 600 (10 minutes). Set <varname>cache_clean_period</varname> parameter ... modparam("cachedb_local", "cache_clean_period", 1200) ...
Exported Functions
<function moreinfo="none">cache_remove_chunk(glob)</function> Remove all keys from local cache that match the glob pattern This function can be used from all routes <function>cache_remove_chunk</function> usage ... cache_remove_chunk("myinfo_*"); ...
Exported MI Functions
<varname>cache_remove_chunk</varname> Removes all local cache entries that match the provided glob param. Parameters : glob - keys that match glob will be removed MI FIFO Command Format: :cache_remove_chunk:_reply_fifo_file_ keyprefix* _empty_line_
opensips-2.2.2/modules/cachedb_local/hash.c000066400000000000000000000237351300170765700206570ustar00rootroot00000000000000/* * memory cache system module * * Copyright (C) 2009 Anca Vamanu * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-01-29 initial version (Anca Vamanu) */ #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../timer.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "cachedb_local.h" #include "hash.h" void lcache_htable_remove_safe(str attr, lcache_entry_t** it); int lcache_htable_init(int size) { int i = 0, j; cache_htable = (lcache_t*)shm_malloc(size * sizeof(lcache_t)); if(cache_htable == NULL) { LM_ERR("no more shared memory\n"); return -1; } memset(cache_htable, 0, size * sizeof(lcache_t)); for(i= 0; i< size; i++) { if(lock_init(&cache_htable[i].lock)== 0) { LM_ERR("failed to initialize lock [%d]\n", i); goto error; } } return 0; error: for(j = 0; j< i; j++) { lock_destroy(&cache_htable[j].lock); } shm_free(cache_htable); cache_htable = NULL; return -1; } void lcache_htable_destroy(void) { int i; lcache_entry_t* me1, *me2; if(cache_htable == NULL) return; for(i = 0; i< cache_htable_size; i++) { lock_destroy(&cache_htable[i].lock); me1 = cache_htable[i].entries; while(me1) { me2 = me1->next; shm_free(me1); me1 = me2; } } shm_free(cache_htable); cache_htable = NULL; } int lcache_htable_insert(cachedb_con *con,str* attr, str* value, int expires) { lcache_entry_t* me, *it; int hash_code; int size; struct timeval start; size= sizeof(lcache_entry_t) + attr->len + value->len; me = (lcache_entry_t*)shm_malloc(size); if(me == NULL) { LM_ERR("no more shared memory\n"); return -1; } memset(me, 0, size); start_expire_timer(start,local_exec_threshold); me->attr.s = (char*)me + (sizeof(lcache_entry_t)); memcpy(me->attr.s, attr->s, attr->len); me->attr.len = attr->len; me->value.s = (char*)me + (sizeof(lcache_entry_t)) + attr->len; memcpy(me->value.s, value->s, value->len); me->value.len = value->len; if( expires != 0) me->expires = get_ticks() + expires; hash_code= core_hash( attr, 0, cache_htable_size); lock_get(&cache_htable[hash_code].lock); it = cache_htable[hash_code].entries; /* if a previous record for the same attr delete it */ lcache_htable_remove_safe( *attr, &it); me->next = it; cache_htable[hash_code].entries = me; lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local insert",attr->s,attr->len,0); return 1; } void lcache_htable_remove_safe(str attr, lcache_entry_t** it_p) { lcache_entry_t* me = NULL, *it= *it_p; while(it) { if(it->attr.len == attr.len && (strncmp(it->attr.s, attr.s, attr.len) == 0)) { if(me) me->next = it->next; else *it_p = it->next; shm_free(it); return; } me = it; it = it->next; } LM_DBG("entry not found\n"); } int lcache_htable_remove(cachedb_con *con,str* attr) { int hash_code; struct timeval start; start_expire_timer(start,local_exec_threshold); hash_code= core_hash( attr, 0, cache_htable_size); lock_get(&cache_htable[hash_code].lock); lcache_htable_remove_safe( *attr, &cache_htable[hash_code].entries); lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local remove",attr->s,attr->len,0); return 0; } int lcache_htable_add(cachedb_con *con,str *attr,int val,int expires,int *new_val) { int hash_code; lcache_entry_t *it=NULL,*it_prev=NULL; int old_value; char *new_value; int new_len; str ins_val; struct timeval start; start_expire_timer(start,local_exec_threshold); hash_code = core_hash(attr,0,cache_htable_size); lock_get(&cache_htable[hash_code].lock); it = cache_htable[hash_code].entries; while (it) { if (it->attr.len == attr->len && memcmp(it->attr.s,attr->s,attr->len) == 0) { if (it->expires !=0 && it->expires < get_ticks()) { /* found an expired entry -> delete it */ if(it_prev) it_prev->next = it->next; else cache_htable[hash_code].entries = it->next; shm_free(it); lock_release(&cache_htable[hash_code].lock); ins_val.s = sint2str(val,&ins_val.len); if (lcache_htable_insert(con,attr,&ins_val,expires) < 0) { LM_ERR("failed to insert value\n"); stop_expire_timer(start,local_exec_threshold, "cachedb_local add",attr->s,attr->len,0); return -1; } if (new_val) *new_val = val; stop_expire_timer(start,local_exec_threshold, "cachedb_local add",attr->s,attr->len,0); return 0; } /* found our valid entry */ if (str2sint(&it->value,&old_value) < 0) { LM_ERR("not an integer\n"); lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local add",attr->s,attr->len,0); return -1; } old_value+=val; expires = it->expires; new_value = sint2str(old_value,&new_len); it = shm_realloc(it,sizeof(lcache_entry_t) + attr->len +new_len); if (it == NULL) { LM_ERR("failed to realloc struct\n"); lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local add",attr->s,attr->len,0); return -1; } if (it_prev) it_prev->next = it; else cache_htable[hash_code].entries = it; it->attr.s = (char*)(it + 1); it->value.s =(char *)(it + 1) + attr->len; it->expires = expires; memcpy(it->value.s,new_value,new_len); it->value.len = new_len; lock_release(&cache_htable[hash_code].lock); if (new_val) *new_val = old_value; stop_expire_timer(start,local_exec_threshold, "cachedb_local add",attr->s,attr->len,0); return 0; } it_prev = it; it = it->next; } lock_release(&cache_htable[hash_code].lock); /* not found */ ins_val.s = sint2str(val,&ins_val.len); if (lcache_htable_insert(con,attr,&ins_val,expires) < 0) { LM_ERR("failed to insert value\n"); stop_expire_timer(start,local_exec_threshold, "cachedb_local add",attr->s,attr->len,0); return -1; } if (new_val) *new_val = val; stop_expire_timer(start,local_exec_threshold, "cachedb_local add",attr->s,attr->len,0); return 0; } int lcache_htable_sub(cachedb_con *con,str *attr,int val,int expires,int *new_val) { return lcache_htable_add(con,attr,-val,expires,new_val); } /* * return : * 1 - if found * -2 - if not found * -1 - if error * */ int lcache_htable_fetch(cachedb_con *con,str* attr, str* res) { int hash_code; lcache_entry_t* it = NULL, *it_aux = NULL; char* value; struct timeval start; start_expire_timer(start,local_exec_threshold); hash_code= core_hash( attr, 0, cache_htable_size); lock_get(&cache_htable[hash_code].lock); it = cache_htable[hash_code].entries; while(it) { if(it->attr.len == attr->len && (strncmp(it->attr.s, attr->s, attr->len) == 0)) { if( it->expires != 0 && it->expires < get_ticks()) { /* found an expired entry -> delete it */ if(it_aux) it_aux->next = it->next; else cache_htable[hash_code].entries = it->next; shm_free(it); lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local fetch",attr->s,attr->len,0); return -2; } value = (char*)pkg_malloc(it->value.len); if(value == NULL) { LM_ERR("no more memory\n"); lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local fetch",attr->s,attr->len,0); return -1; } memcpy(value, it->value.s, it->value.len); res->len = it->value.len; res->s = value; lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local fetch",attr->s,attr->len,0); return 1; } it_aux = it; it = it->next; } lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local fetch",attr->s,attr->len,0); return -2; } int lcache_htable_fetch_counter(cachedb_con* con,str* attr,int *val) { int hash_code; lcache_entry_t* it = NULL, *it_aux = NULL; int ret; struct timeval start; start_expire_timer(start,local_exec_threshold); hash_code= core_hash( attr, 0, cache_htable_size); lock_get(&cache_htable[hash_code].lock); it = cache_htable[hash_code].entries; while(it) { if(it->attr.len == attr->len && (strncmp(it->attr.s, attr->s, attr->len) == 0)) { if( it->expires != 0 && it->expires < get_ticks()) { /* found an expired entry -> delete it */ if(it_aux) it_aux->next = it->next; else cache_htable[hash_code].entries = it->next; shm_free(it); lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local fetch_counter",attr->s,attr->len,0); return -2; } if (str2sint(&it->value,&ret) != 0) { LM_ERR("Not a counter key\n"); lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local fetch_counter",attr->s,attr->len,0); return -3; } if (val) *val = ret; lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local fetch_counter",attr->s,attr->len,0); return 1; } it_aux = it; it = it->next; } lock_release(&cache_htable[hash_code].lock); stop_expire_timer(start,local_exec_threshold, "cachedb_local fetch_counter",attr->s,attr->len,0); return -2; } opensips-2.2.2/modules/cachedb_local/hash.h000066400000000000000000000033411300170765700206530ustar00rootroot00000000000000/* * memory cache system module * * Copyright (C) 2009 Anca Vamanu * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-01-29 initial version (Anca Vamanu) */ #ifndef _MEMCACHE_HASH_ #define _MEMCACHE_HASH_ #include "../../str.h" #include "../../lock_ops.h" #include "../../cachedb/cachedb.h" typedef struct lcache_entry { str attr; str value; unsigned int expires; struct lcache_entry* next; }lcache_entry_t; typedef struct lcache { lcache_entry_t* entries; gen_lock_t lock; }lcache_t; int lcache_htable_init(int size); void lcache_htable_destroy(); int lcache_htable_insert(cachedb_con *con,str* attr, str* value,int expires); int lcache_htable_remove(cachedb_con *con,str* attr); int lcache_htable_fetch(cachedb_con *con,str* attr, str* val); int lcache_htable_add(cachedb_con *con,str *attr,int val,int expires,int *new_val); int lcache_htable_sub(cachedb_con *con,str *attr,int val,int expires,int *new_val); int lcache_htable_fetch_counter(cachedb_con* con,str* attr,int *val); #endif opensips-2.2.2/modules/cachedb_memcached/000077500000000000000000000000001300170765700203725ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_memcached/Makefile000066400000000000000000000005341300170765700220340ustar00rootroot00000000000000# $Id: Makefile 5862 2009-07-15 10:06:05Z andreidragus $ # # Memcached implementation for memcache API. # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=cachedb_memcached.so DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -lmemcached include ../../Makefile.modules opensips-2.2.2/modules/cachedb_memcached/README000066400000000000000000000066751300170765700212700ustar00rootroot00000000000000MEMCACHED Andrei Dragus OpenSIPS Edited by Andrei Dragus Edited by Vladut-Stefan Paiu Copyright © 2009 Voice System Copyright © 2009 Andrei Dragus Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Advantages 1.3. Limitations 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. cachedb_url (string) 1.5.2. exec_threshold (int) 1.5.3. Exported Functions List of Examples 1.1. Set cachedb_url parameter 1.2. Use memcached servers 1.3. Set exec_threshold parameter Chapter 1. Admin Guide 1.1. Overview This module is an implementation of a cache system designed to work with a memcached server. It uses libmemcached client library to connect to several memcached servers that store data. It uses the Key-Value interface exported from the core. 1.2. Advantages * memory costs are no longer on the server * many servers may be used so the memory is virtually unlimited * the cache is persistent so a restart of the server will not affect the cache * memcached is an open-source project so it can be used to exchange data with various other applications * servers may be grouped together (e.g. for security purposes : some can be inside a private network, some can be in a public one) 1.3. Limitations * keys (in key:value pairs) may not contain spaces or control characters 1.4. Dependencies 1.4.1. OpenSIPS Modules None. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libmemcached: libmemcached can be downloaded from: http://tangent.org/552/libmemcached.html. Download the archive, extract sources, run ./configure, make,sudo make install. ... wget http://download.tangent.org/libmemcached-0.31.tar.gz tar -xzvf libmemcached-0.31.tar.gz cd libmemcached-0.31 ./configure make sudo make install ... 1.5. Exported Parameters 1.5.1. cachedb_url (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. Example 1.1. Set cachedb_url parameter ... modparam("cachedb_memcached", "cachedb_url","memcached:group1://localhos t:9999,127.0.0.1/"); modparam("cachedb_memcached", "cachedb_url","memcached:y://random_url:88 88/"); ... Example 1.2. Use memcached servers ... cache_store("memcached:group1","key","$ru value"); cache_fetch("memcached:y","key",$avp(10)); cache_remove("memcached:group1","key"); ... 1.5.2. exec_threshold (int) The maximum number of microseconds that a local cache query can last. Anything above the threshold will trigger a warning message to the log Default value is “0 ( unlimited - no warnings )â€. Example 1.3. Set exec_threshold parameter ... modparam("cachedb_memcached", "exec_threshold", 100000) ... 1.5.3. Exported Functions The module does not export functions to be used in configuration script. opensips-2.2.2/modules/cachedb_memcached/cachedb_memcached.c000066400000000000000000000275611300170765700241100ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Andrei Dragus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2009-07-15 first version (andreidragus) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../timer.h" #include "../../error.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "cachedb_memcached.h" #include static str cache_mod_name = str_init("memcached"); struct cachedb_url *memcached_script_urls = NULL; static int memcache_exec_threshold=0; int mc_set_connection(unsigned int type, void *val) { return cachedb_store_url(&memcached_script_urls,(char *)val); } typedef struct mem_server_list_t { char * servers; char * name; memcached_st * memc; struct mem_server_list_t * next; }mem_server; static int mod_init(void); static int child_init(int rank); static void destroy(void); mem_server * servers; /** module parameters */ static param_export_t params[]={ {"cachedb_url", STR_PARAM|USE_FUNC_PARAM, (void*)&mc_set_connection }, {"exec_threshold", INT_PARAM, &memcache_exec_threshold }, {0,0,0} }; /** module exports */ struct module_exports exports= { "cachedb_memcached", /* module name */ MOD_TYPE_CACHEDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ child_init /* per-child init function */ }; int wrap_memcached_insert(cachedb_con *con,str* attr, str* value,int expires) { memcached_return rc; memcached_con *connection; struct timeval start; start_expire_timer(start,memcache_exec_threshold); connection = (memcached_con *)con->data; rc = memcached_set(connection->memc,attr->s, attr->len , value->s, value->len, (time_t)expires, (uint32_t)0); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached insert",attr->s,attr->len,0); if( rc != MEMCACHED_SUCCESS) { LM_ERR("Failed to insert: %s\n",memcached_strerror(connection->memc,rc)); return -1; } return 0; } int wrap_memcached_remove(cachedb_con *connection,str* attr) { memcached_return rc; memcached_con *con; struct timeval start; start_expire_timer(start,memcache_exec_threshold); con = (memcached_con *)connection->data; rc = memcached_delete(con->memc,attr->s,attr->len,0); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached remove",attr->s,attr->len,0); if( rc != MEMCACHED_SUCCESS && rc != MEMCACHED_NOTFOUND) { LM_ERR("Failed to remove: %s\n",memcached_strerror(con->memc,rc)); return -1; } return 0; } int wrap_memcached_get(cachedb_con *connection,str* attr, str* res) { memcached_return rc; char * ret; size_t ret_len; uint32_t fl; char * err; char * value; memcached_con *con; struct timeval start; start_expire_timer(start,memcache_exec_threshold); con = (memcached_con *)connection->data; ret = memcached_get(con->memc,attr->s, attr->len, &ret_len,&fl,&rc); if(ret == NULL) { if(rc == MEMCACHED_NOTFOUND) { res->s = NULL; res->len = 0; stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached get",attr->s,attr->len,0); return -2; } else { err = (char*)memcached_strerror(con->memc,rc); LM_ERR("Failed to get: %s\n",err ); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached get",attr->s,attr->len,0); return -1; } } value = pkg_malloc(ret_len); if( value == NULL) { LM_ERR("Memory allocation"); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached get",attr->s,attr->len,0); return -1; } memcpy(value,ret,ret_len); res->s = value; res->len = ret_len; free(ret); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached get",attr->s,attr->len,0); return 0; } /* TODO - once memcached_touch gets into libmemcached, also take care of expires */ int wrap_memcached_add(cachedb_con *connection,str* attr,int val, int expires,int *new_val) { memcached_return rc; memcached_con *con; uint64_t res; str ins_val; struct timeval start; start_expire_timer(start,memcache_exec_threshold); con = (memcached_con *)connection->data; rc = memcached_increment(con->memc,attr->s,attr->len,val,&res); if( rc != MEMCACHED_SUCCESS ) { if (rc == MEMCACHED_NOTFOUND) { ins_val.s = sint2str(val,&ins_val.len); if (wrap_memcached_insert(connection,attr,&ins_val,expires) < 0) { LM_ERR("failed to insert value\n"); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached add",attr->s,attr->len,0); return -1; } if (new_val) *new_val = val; stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached add",attr->s,attr->len,0); return 0; } else { LM_ERR("Failed to add: %s\n",memcached_strerror(con->memc,rc)); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached add",attr->s,attr->len,0); return -1; } } if (new_val) *new_val = (int)res; stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached add",attr->s,attr->len,0); return 0; } /* TODO - once memcached_touch gets into libmemcached, also take care of expires */ int wrap_memcached_sub(cachedb_con *connection,str* attr,int val, int expires,int *new_val) { memcached_return rc; memcached_con *con; uint64_t res; str ins_val; struct timeval start; start_expire_timer(start,memcache_exec_threshold); con = (memcached_con *)connection->data; rc = memcached_decrement(con->memc,attr->s,attr->len,val,&res); if( rc != MEMCACHED_SUCCESS ) { if (rc == MEMCACHED_NOTFOUND) { ins_val.s = sint2str(val,&ins_val.len); if (wrap_memcached_insert(connection,attr,&ins_val,expires) < 0) { LM_ERR("failed to insert value\n"); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached sub",attr->s,attr->len,0); return -1; } if (new_val) *new_val = val; stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached sub",attr->s,attr->len,0); return 0; } else { LM_ERR("Failed to sub: %s\n",memcached_strerror(con->memc,rc)); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached sub",attr->s,attr->len,0); return -1; } } if (new_val) *new_val = (int)res; stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached sub",attr->s,attr->len,0); return 0; } int wrap_memcached_get_counter(cachedb_con *connection,str* attr, int* res) { memcached_return rc; char * ret; size_t ret_len; uint32_t fl; char * err; memcached_con *con; struct timeval start; str rpl; start_expire_timer(start,memcache_exec_threshold); con = (memcached_con *)connection->data; ret = memcached_get(con->memc,attr->s, attr->len, &ret_len,&fl,&rc); if(ret == NULL) { if(rc == MEMCACHED_NOTFOUND) { stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached counter fetch",attr->s,attr->len,0); return -2; } else { err = (char*)memcached_strerror(con->memc,rc); LM_ERR("Failed to get: %s\n",err ); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached counter fetch",attr->s,attr->len,0); return -1; } } rpl.len = (int)ret_len; rpl.s = ret; if (str2sint(&rpl,res) < 0) { LM_ERR("Failed to convert %.*s to int\n",(int)ret_len,ret); stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached counter fetch",attr->s,attr->len,0); free(ret); return -1; } stop_expire_timer(start,memcache_exec_threshold, "cachedb_memcached counter fetch",attr->s,attr->len,0); free(ret); return 0; } #define MAX_HOSTPORT_SIZE 80 static char host_buff[MAX_HOSTPORT_SIZE]; memcached_con* memcached_new_connection(struct cachedb_id *id) { memcached_con *con; memcached_server_st *server_list; memcached_return rc; char *srv_list; int ret; if (id == NULL) { LM_ERR("null cached_id\n"); return 0; } con = pkg_malloc(sizeof(memcached_con)); if (con == NULL) { LM_ERR("no more pkg\n"); return 0; } memset(con,0,sizeof(memcached_con)); con->id = id; con->ref = 1; con->memc = memcached_create(NULL); memset(host_buff,0,MAX_HOSTPORT_SIZE); if (id->flags & CACHEDB_ID_MULTIPLE_HOSTS) srv_list = id->host; else { ret = snprintf(host_buff,MAX_HOSTPORT_SIZE,"%s:%d",id->host,id->port); if (ret<0 || ret>MAX_HOSTPORT_SIZE) { LM_ERR("failed to init con\n"); pkg_free(con); return 0; } srv_list = host_buff; } server_list = memcached_servers_parse(srv_list); rc = memcached_server_push(con->memc,server_list); if( rc != MEMCACHED_SUCCESS) { LM_ERR("Push:%s\n",memcached_strerror(con->memc,rc)); pkg_free(con); return 0; } rc = memcached_behavior_set(con->memc, MEMCACHED_BEHAVIOR_NO_BLOCK,1); if( rc != MEMCACHED_SUCCESS) { LM_ERR("Behavior Set:%s\n",memcached_strerror(con->memc,rc)); pkg_free(con); return 0; } LM_DBG("successfully inited memcached connection\n"); return con; } cachedb_con *memcached_init(str *url) { return cachedb_do_init(url,(void *)memcached_new_connection); } void memcached_free_connection(cachedb_pool_con *con) { memcached_con * c; if (!con) return; c = (memcached_con *)con; memcached_free(c->memc); } void memcached_destroy(cachedb_con *con) { cachedb_do_close(con,memcached_free_connection); } /** * init module function */ static int mod_init(void) { cachedb_engine cde; LM_NOTICE("initializing module cachedb_memcached\n"); cde.name = cache_mod_name; cde.cdb_func.init = memcached_init; cde.cdb_func.destroy = memcached_destroy; cde.cdb_func.get = wrap_memcached_get; cde.cdb_func.get_counter = wrap_memcached_get_counter; cde.cdb_func.set = wrap_memcached_insert; cde.cdb_func.remove = wrap_memcached_remove; cde.cdb_func.add = wrap_memcached_add; cde.cdb_func.sub = wrap_memcached_sub; cde.cdb_func.capability = CACHEDB_CAP_BINARY_VALUE; if (register_cachedb(&cde) < 0) { LM_ERR("failed to initialize cachedb_memcached\n"); return -1; } LM_DBG("successfully inited cachedb_memcached\n"); return 0; } /** * Initialize children */ static int child_init(int rank) { struct cachedb_url *it; cachedb_con *con; if(rank == PROC_MAIN || rank == PROC_TCP_MAIN) { return 0; } for (it = memcached_script_urls;it;it=it->next) { con = memcached_init(&it->url); if (con == NULL) { LM_ERR("failed to open connection\n"); return -1; } if (cachedb_put_connection(&cache_mod_name,con) < 0) { LM_ERR("failed to insert connection\n"); return -1; } } cachedb_free_url(memcached_script_urls); return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module cachedb_memcached ...\n"); cachedb_end_connections(&cache_mod_name); return; } opensips-2.2.2/modules/cachedb_memcached/cachedb_memcached.h000066400000000000000000000005141300170765700241020ustar00rootroot00000000000000#ifndef CACHEDB_MEMCACHEDH #define CACHEDB_MEMCACHEDH #include #include "../../cachedb/cachedb.h" #include "../../cachedb/cachedb_cap.h" typedef struct { struct cachedb_id *id; unsigned int ref; struct cachedb_pool_con_t *next; memcached_st *memc; } memcached_con; #endif /* CACHEDB_MEMCACHEDH */ opensips-2.2.2/modules/cachedb_memcached/doc/000077500000000000000000000000001300170765700211375ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_memcached/doc/cachedb_memcached.xml000066400000000000000000000030471300170765700252240ustar00rootroot00000000000000 %docentities; ]> MEMCACHED &osipsname; Andrei Dragus &osips;
andreidragus@yahoo.com http://opensips.org
Andrei Dragus
andreidragus@yahoo.com
Vladut-Stefan Paiu
vladpaiu@opensips.org
2009 Voice System 2009 Andrei Dragus $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/cachedb_memcached/doc/cachedb_memcached_admin.xml000066400000000000000000000103211300170765700263650ustar00rootroot00000000000000 &adminguide;
Overview This module is an implementation of a cache system designed to work with a memcached server. It uses libmemcached client library to connect to several memcached servers that store data. It uses the Key-Value interface exported from the core.
Advantages memory costs are no longer on the server many servers may be used so the memory is virtually unlimited the cache is persistent so a restart of the server will not affect the cache memcached is an open-source project so it can be used to exchange data with various other applications servers may be grouped together (e.g. for security purposes : some can be inside a private network, some can be in a public one)
Limitations keys (in key:value pairs) may not contain spaces or control characters
Dependencies
&osips; Modules None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libmemcached: libmemcached can be downloaded from: http://tangent.org/552/libmemcached.html. Download the archive, extract sources, run ./configure, make,sudo make install. ... wget http://download.tangent.org/libmemcached-0.31.tar.gz tar -xzvf libmemcached-0.31.tar.gz cd libmemcached-0.31 ./configure make sudo make install ...
Exported Parameters
<varname>cachedb_url</varname> (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. Set <varname>cachedb_url</varname> parameter ... modparam("cachedb_memcached", "cachedb_url","memcached:group1://localhost:9999,127.0.0.1/"); modparam("cachedb_memcached", "cachedb_url","memcached:y://random_url:8888/"); ... Use memcached servers ... cache_store("memcached:group1","key","$ru value"); cache_fetch("memcached:y","key",$avp(10)); cache_remove("memcached:group1","key"); ...
<varname>exec_threshold</varname> (int) The maximum number of microseconds that a local cache query can last. Anything above the threshold will trigger a warning message to the log Default value is 0 ( unlimited - no warnings ). Set <varname>exec_threshold</varname> parameter ... modparam("cachedb_memcached", "exec_threshold", 100000) ...
Exported Functions The module does not export functions to be used in configuration script.
opensips-2.2.2/modules/cachedb_mongodb/000077500000000000000000000000001300170765700201115ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_mongodb/Makefile000066400000000000000000000013261300170765700215530ustar00rootroot00000000000000# $Id: Makefile 6253 2009-10-12 19:00:53Z bogdan_iancu $ # # Presence Agent # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=cachedb_mongodb.so ifeq ($(CROSS_COMPILE),) JSON_BUILDER = $(shell \ if pkg-config --exists json; then \ echo 'pkg-config json'; \ elif pkg-config --exists json-c; then\ echo 'pkg-config json-c'; \ fi) endif ifeq ($(JSON_BUILDER),) DEFS += -I$(LOCALBASE)/include -I$(SYSBASE)/include LIBS += -L$(LOCALBASE)/lib -ljson else DEFS += $(shell $(JSON_BUILDER) --cflags) LIBS += $(shell $(JSON_BUILDER) --libs) endif DEFS += -I$(LOCALBASE)/include LIBS += -L$(LOCALBASE)/lib -lmongoc include ../../Makefile.modules opensips-2.2.2/modules/cachedb_mongodb/README000066400000000000000000000201751300170765700207760ustar00rootroot00000000000000cachedb_mongodb Module Vladut-Stefan Paiu OpenSIPS Solutions Edited by Vladut-Stefan Paiu Copyright © 2013 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Advantages 1.3. Limitations 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. cachedb_url (string) 1.5.2. op_timeout (int) 1.5.3. slave_ok (int) 1.5.4. write_concern (string) 1.5.5. exec_threshold (int) 1.6. Exported Functions 1.7. Raw Query Syntax List of Examples 1.1. Set cachedb_url parameter 1.2. Use MongoDB servers 1.3. Set op_timeout parameter 1.4. Set slave_ok parameter 1.5. Set write_concern parameter 1.6. Set exec_threshold parameter 1.7. Mongo Raw Query Syntax 1.8. Mongo Raw Query Examples Chapter 1. Admin Guide 1.1. Overview This module is an implementation of a cache system designed to work with MongoDB servers. It uses the Key-Value interface exported from the core. 1.2. Advantages * memory costs are no longer on the server * many servers can be used inside a cluster, so the memory is virtually unlimited * the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The MongoDB is also persistent so it can also be restarted without loss of information. * MongoDB is an open-source project so it can be used to exchange data with various other applications * By creating a MongoDB Cluster, multiple OpenSIPS instances can easily share key-value information * This module also implements the CacheDB Raw query capability, thus you can run whatever query that the MongoDB back-end supports, taking full advatange of it. 1.3. Limitations * keys (in key:value pairs) may not contain spaces or control characters 1.4. Dependencies 1.4.1. OpenSIPS Modules None. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libjson The libjson library can be downloaded from: http://oss.metaparadigm.com/json-c/ * mongo-c-driver The mongo C driver can be downloaded from MongoDB's GitHub repository. Make sure to get the 0.6 version: "git clone https://github.com/mongodb/mongo-c-driver.git -b v0.6" 1.5. Exported Parameters 1.5.1. cachedb_url (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. If multiple addr:port pairs are passed, the connection is treated as going to a replica set. Example 1.1. Set cachedb_url parameter ... modparam("cachedb_mongodb", "cachedb_url","mongodb:instance1://localhost :27017/db.collection") modparam("cachedb_mongodb", "cachedb_url","mongodb:replicaset1://1.2.3.4 :27017,2.3.4.5:27017,3.4.5.6:27017/replicaSetName.db.collection") ... Example 1.2. Use MongoDB servers ... cache_store("mongodb:group1","key","$ru value"); cache_fetch("mongodb:replicaset1","key",$avp(10)); cache_remove("mongodb:cluster1","key"); ... 1.5.2. op_timeout (int) The timeout in ms that will be triggered in case a MongoDB op takes too long. Default value is 3000 ( 3 seconds ) Example 1.3. Set op_timeout parameter ... modparam("cachedb_mongodb", "op_timeout",5000) ... 1.5.3. slave_ok (int) If set to 1, read operations are allowed to go to secondary MongoDB servers Default value is 0 ( read and write requests will go to primary nodes = full consistency ) Example 1.4. Set slave_ok parameter ... modparam("cachedb_mongodb", "slave_ok",1); ... 1.5.4. write_concern (string) The JSON containing the Mongo write concern that should affect all write operations. More info can be found at http://docs.mongodb.org/manual/core/write-operations/ Example 1.5. Set write_concern parameter ... modparam("cachedb_mongodb","write_concern","{ \"getLastError\": 1, "j" : "true" }") ... 1.5.5. exec_threshold (int) The maximum number of microseconds that a mongodb query can last. Anything above the threshold will trigger a warning message to the log Default value is “0 ( unlimited - no warnings )â€. Example 1.6. Set exec_threshold parameter ... modparam("cachedb_mongodb", "exec_threshold", 100000) ... 1.6. Exported Functions The module does not export functions to be used in configuration script. 1.7. Raw Query Syntax The cachedb_mongodb module allows to run RAW queries, thus taking full advantage of the capabilities of the back-end. The query syntax is a JSON-based one, very similar to the one that one runs commands in the mongo cli. The query results are also returned as JSON documents, that one can further process in the OpenSIPS script by using the JSON module. The syntax looks like the following : Example 1.7. Mongo Raw Query Syntax ... cache_raw_query("mongodb","{ \"op\" : \"desiredOP\", \"ns\" : \"db.colle ction\", \"query\": {\"_id\" : $rU} }","$avp(mongo_result)"); ... The \"op\" JSON entry specifies the actual operation ( find,update,remove, etc ) that you want to run on the Mongo server and the \"ns\" entry specifies the namespace where you want to run that query. The \"ns\" entry can be missing, and the query will be run on the db.collection that was passed at Connection time in the cachedb_url. The \"query\" entry is the JSON document that specifies the raw query that you want to run. The last parameter is an optional AVP parameter, where the JSON documents will be returned ( a find query can return multiple JSON documents, which are correctly populated in the output AVP ). If the query does not return a result, you can ommit the last parameter. The currently suported operations that you can pass in the \"op\" JSON entry are * find - the find operation takes an optional \"fields\" JSON document, specifying the desired fields that should be returned from the mongoDB server. * update - the update operations takes an optional \"match\" JSON document, specifying that only the mongoDB documents that match this JSON will be updated ( similar to the 'where' SQL clause ) . If no \"match\" is provided, all documents will be updated. * insert * remove * count Here are a couple examples of running some mongoDB queries : Example 1.8. Mongo Raw Query Examples ... /* find documents where my_key has the value 345, and return just the en try1 and entry2 values from the matching documents */ cache_raw_query("mongodb","{ \"op\" : \"find\", \"ns\" : \"my_db.my_col\ ", \"query\": {\"my_key\" : 345},\"fields\": { \"entry1\" : 1, \"entry2\ " : 1 } }","$avp(mongo_result)"); $var(it) = 0; while ($(avp(mongo_result)[$var(it)]) != NULL) { $json(json_res) := $(avp(mongo_result)[$var(it)]); xlog("Fetched a new mongo result=$json(json_res). entry1=$json(j son_res/entry1) \n"); $var(it) = $var(it) + 1; $json(json_res) = NULL; } ... /* insert the {_id: 993, vlad: "opensips",counter: 9000 } document */ cache_raw_query("mongodb","{ \"op\" : \"insert\", \"ns\" : \"some_db.col l\", \"query\": {\"_id\" : 993, \"vlad\" : \"opensips\", \"counter\": 90 00} }"); ... /* remove all the documents where "mykey" has the "badvalue" */ cache_raw_query("mongodb","{ \"op\" : \"remove\", \"ns\" : \"db.coll\", \"query\": {\"mykey\" : "badvalue"} }"); ... /* set expired to have the value of 1 in the documents where the counter has the 345 value */ cache_raw_query("mongodb","{ \"op\" : \"update\", \"query\": { \"expired \" : 1}, \"match\" : { \"counter\" : 345 } }"); ... cache_raw_query("mongodb","{ \"op\" : \"count\",\"query\": { \"username\ " : $rU} }","$avp(mongo_count_result)"); xlog("We have $avp(mongo_count_result) documents where username = $rU \n "); ... opensips-2.2.2/modules/cachedb_mongodb/cachedb_mongodb.c000066400000000000000000000116361300170765700233420ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../pt.h" #include "../../cachedb/cachedb.h" #include "cachedb_mongodb_dbase.h" #include "cachedb_mongodb_json.h" static int mod_init(void); static int child_init(int); static void destroy(void); static str cache_mod_name = str_init("mongodb"); struct cachedb_url *mongodb_script_urls = NULL; int mongo_op_timeout=3000; /* 3000 milliseconds */ int mongo_slave_ok=0; /* not ok to send read requests to secondaries */ str mongo_write_concern_str = {0,0}; bson mongo_write_concern_b; mongo_write_concern mwc; int mongo_exec_threshold=0; int set_connection(unsigned int type, void *val) { return cachedb_store_url(&mongodb_script_urls,(char *)val); } static param_export_t params[]={ { "cachedb_url", STR_PARAM|USE_FUNC_PARAM, (void *)&set_connection}, { "op_timeout", INT_PARAM, &mongo_op_timeout}, { "slave_ok", INT_PARAM, &mongo_slave_ok}, { "write_concern", STR_PARAM, &mongo_write_concern_str }, { "exec_treshold", INT_PARAM, &mongo_exec_threshold }, {0,0,0} }; /** module exports */ struct module_exports exports= { "cachedb_mongodb", /* module name */ MOD_TYPE_CACHEDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function)destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { cachedb_engine cde; LM_NOTICE("initializing module cachedb_mongodb ...\n"); memset(&cde,0,sizeof(cachedb_engine)); cde.name = cache_mod_name; cde.cdb_func.init = mongo_con_init; cde.cdb_func.destroy = mongo_con_destroy; cde.cdb_func.get = mongo_con_get; cde.cdb_func.get_counter = mongo_con_get_counter; cde.cdb_func.set = mongo_con_set; cde.cdb_func.remove = mongo_con_remove; cde.cdb_func.add = mongo_con_add; cde.cdb_func.sub = mongo_con_sub; cde.cdb_func.raw_query = mongo_con_raw_query; cde.cdb_func.db_query_trans = mongo_db_query_trans; cde.cdb_func.db_free_trans = mongo_db_free_result_trans; cde.cdb_func.db_insert_trans = mongo_db_insert_trans; cde.cdb_func.db_delete_trans = mongo_db_delete_trans; cde.cdb_func.db_update_trans = mongo_db_update_trans; cde.cdb_func.capability = 0; if (mongo_write_concern_str.s != NULL) { /* TODO - try manually building the getlasterror bson */ memset(&mongo_write_concern_b,0,sizeof(bson)); if (json_to_bson(mongo_write_concern_str.s,&mongo_write_concern_b) != 0) { LM_ERR("Invalid write concern json\n"); return -1; } mongo_write_concern_init(&mwc); mwc.cmd = &mongo_write_concern_b; } if (mongo_slave_ok == 1) { mongo_slave_ok = MONGO_SLAVE_OK | MONGO_PARTIAL ; } if (register_cachedb(&cde) < 0) { LM_ERR("failed to initialize cachedb_redis\n"); return -1; } return 0; } static int child_init(int rank) { struct cachedb_url *it; cachedb_con *con; if(rank == PROC_MAIN || rank == PROC_TCP_MAIN) { return 0; } for (it = mongodb_script_urls;it;it=it->next) { LM_DBG("iterating through conns - [%.*s]\n",it->url.len,it->url.s); con = mongo_con_init(&it->url); if (con == NULL) { LM_ERR("failed to open connection\n"); return -1; } if (cachedb_put_connection(&cache_mod_name,con) < 0) { LM_ERR("failed to insert connection\n"); return -1; } } cachedb_free_url(mongodb_script_urls); return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module cachedb_mongodb ...\n"); cachedb_end_connections(&cache_mod_name); return; } opensips-2.2.2/modules/cachedb_mongodb/cachedb_mongodb_dbase.c000066400000000000000000001461651300170765700245060ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #include "../../dprint.h" #include "cachedb_mongodb_dbase.h" #include "cachedb_mongodb_json.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../cachedb/cachedb.h" #include extern mongo_write_concern mwc; extern str mongo_write_concern_str; extern str mongo_write_concern_b; extern int mongo_slave_ok; extern int mongo_exec_threshold; #define HEX_OID_SIZE 25 char *hex_oid_id = NULL; mongo_con* mongo_new_connection(struct cachedb_id* id) { mongo_con *con; bson version_cmd,version_out; bson_iterator it; const char *version; char *p,*p1; str database, collection,replset_name,host,port; int port_no,last=0; char hostname[64]; if (id == NULL) { LM_ERR("null cachedb_id\n"); return 0; } if (id->flags & CACHEDB_ID_MULTIPLE_HOSTS) { /* we are connecting to a replica set */ replset_name.s = id->database; p = memchr(id->database,'.',strlen(id->database)); if (!p) { LM_ERR("Malformed mongo database\n"); return 0; } replset_name.len = p-replset_name.s; database.s = replset_name.s+replset_name.len+1; p = memchr(replset_name.s+replset_name.len+1,'.', strlen(id->database)-replset_name.len-1); if (!p) { LM_ERR("Malformed mongo database\n"); return 0; } database.len = p-database.s; collection.s = p+1; collection.len = id->database+strlen(id->database) - collection.s; con = pkg_malloc(sizeof(mongo_con)+database.len+collection.len+ replset_name.len+3); if (con == NULL) { LM_ERR("no more pkg \n"); return 0; } memset(con,0,sizeof(mongo_con)+database.len+collection.len+replset_name.len+3); con->id = id; con->ref = 1; con->database = (char *)(con +1); memcpy(con->database,database.s,database.len); con->collection = con->database + database.len + 1; memcpy(con->collection,collection.s,collection.len); con->replset_name = con->database+database.len+collection.len+2; memcpy(con->replset_name,replset_name.s,replset_name.len); LM_INFO("Connecting to [%s.%s.%s]\n", con->replset_name,con->database,con->collection); if (mongo_set_op_timeout(&MONGO_CON(con),mongo_op_timeout) != MONGO_OK) LM_WARN("Failed to set timeout of %d millis\n",mongo_op_timeout); else LM_DBG("Set timeout to %d millis\n",mongo_op_timeout); mongo_replset_init(&MONGO_CON(con),con->replset_name); p = id->host; while (*p) { host.s = p; p1 = memchr(host.s,',',strlen(host.s)); if (p1 == NULL) { p1 = id->host+strlen(id->host); last=1; } host.len = p1 - host.s; port.s = memchr(host.s,':',host.len); if (!port.s) port_no = 27017; else { port.s++; port.len = host.s + host.len - port.s; host.len = host.len - port.len - 1; if (str2int(&port,(unsigned int *)&port_no) != 0) { LM_ERR("Malformed mongo URL\n"); return 0; } } memcpy(hostname,host.s,host.len); hostname[host.len] = 0; mongo_replset_add_seed(&MONGO_CON(con),hostname,port_no); if (last) break; else p = ++ p1; } if (mongo_replset_connect(&MONGO_CON(con)) != MONGO_OK) { LM_ERR("Failure to connect to mongo - %d\n",MONGO_CON(con).err); return 0; } } else { database.s = id->database; p = memchr(id->database,'.',strlen(id->database)); if (!p) { LM_ERR("Malformed mongo database\n"); return 0; } database.len = p-database.s; collection.s = p+1; collection.len = id->database+strlen(id->database) - collection.s; con = pkg_malloc(sizeof(mongo_con)+database.len+collection.len+2); if (con == NULL) { LM_ERR("no more pkg \n"); return 0; } memset(con,0,sizeof(mongo_con)+database.len+collection.len+2); con->id = id; con->ref = 1; mongo_init(&MONGO_CON(con)); con->database = (char *)(con +1); memcpy(con->database,database.s,database.len); con->collection = con->database + database.len + 1; memcpy(con->collection,collection.s,collection.len); if (mongo_set_op_timeout(&MONGO_CON(con),mongo_op_timeout) != MONGO_OK) LM_WARN("Failed to set timeout of %d millis\n",mongo_op_timeout); else LM_DBG("Set timeout to %d millis\n",mongo_op_timeout); if (mongo_connect(&MONGO_CON(con),id->host,id->port) != MONGO_OK) { LM_ERR("Failure to connect to mongo\n"); return 0; } } if (mongo_write_concern_str.s != NULL) { mongo_set_write_concern(&MONGO_CON(con), &mwc); } bson_init(&version_cmd); bson_append_int(&version_cmd, "buildinfo", 1 ); bson_finish(&version_cmd); if( mongo_run_command(&MONGO_CON(con), "admin", &version_cmd, &version_out ) == MONGO_ERROR ) { LM_ERR("Failed to get version of server\n"); return 0; } bson_iterator_init(&it, &version_out); version = bson_iterator_string(&it); LM_INFO("Connected at server %s with version %s , " "to db %s.%s\n",id->host,version, con->database,con->collection); bson_destroy(&version_cmd); bson_destroy(&version_out); return con; } cachedb_con *mongo_con_init(str *url) { return cachedb_do_init(url,(void *)mongo_new_connection); } void mongo_free_connection(cachedb_pool_con *con) { } void mongo_con_destroy(cachedb_con *con) { LM_DBG("in mongo_destroy\n"); cachedb_do_close(con,mongo_free_connection); } int mongo_con_get(cachedb_con *connection,str *attr,str *val) { bson new_b,err_b; mongo_cursor *m_cursor; bson_iterator it; const char *rez; int rez_len,i; mongo *conn = &MONGO_CDB_CON(connection); char hex_oid[HEX_OID_SIZE]; struct timeval start; LM_DBG("Get operation on namespace %s\n",MONGO_NAMESPACE(connection)); start_expire_timer(start,mongo_exec_threshold); bson_init(&new_b); if (bson_append_string_n(&new_b,"_id",attr->s,attr->len) != BSON_OK) { LM_ERR("Failed to append _id \n"); bson_destroy(&new_b); goto error; } bson_finish(&new_b); for (i=0;i<2;i++) { m_cursor = mongo_find(conn,MONGO_NAMESPACE(connection), &new_b,0,0,0,mongo_slave_ok); if (m_cursor == NULL) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(connection),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&it,&err_b); while( bson_iterator_next(&it)) { LM_DBG("Fetched key %s\n",bson_iterator_key(&it)); switch( bson_iterator_type( &it ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&it)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&it)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&it)); break; case BSON_OID: bson_oid_to_string(bson_iterator_oid(&it),hex_oid); LM_DBG("(oid) \"%s\"\n",hex_oid); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&it)); break; } bson_destroy(&new_b); } goto error; } break; } while( mongo_cursor_next(m_cursor) == MONGO_OK ) { bson_iterator_init(&it,mongo_cursor_bson(m_cursor)); if (bson_find(&it,mongo_cursor_bson(m_cursor),"opensips") == BSON_EOO) continue; switch( bson_iterator_type( &it ) ) { case BSON_INT: rez = int2str(bson_iterator_int(&it),&rez_len); if (rez == NULL) { LM_ERR("Failed to convert %d to str\n", bson_iterator_int(&it)); mongo_cursor_destroy(m_cursor); goto error; } val->s = pkg_malloc(rez_len); if (val->s == NULL) { LM_ERR("No more pkg malloc\n"); mongo_cursor_destroy(m_cursor); goto error; } memcpy(val->s,rez,rez_len); val->len = rez_len; mongo_cursor_destroy(m_cursor); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo get",attr->s,attr->len,0); return 0; break; case BSON_STRING: rez = bson_iterator_string(&it); if (rez == NULL) { LM_ERR("Got null str for mongo\n"); mongo_cursor_destroy(m_cursor); goto error; } rez_len=strlen(rez); val->s = pkg_malloc(rez_len); if (val->s == NULL) { LM_ERR("No more pkg malloc\n"); mongo_cursor_destroy(m_cursor); goto error; } memcpy(val->s,rez,rez_len); val->len = rez_len; mongo_cursor_destroy(m_cursor); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo get",attr->s,attr->len,0); return 0; break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&it)); break; } } LM_DBG("No suitable response found\n"); mongo_cursor_destroy(m_cursor); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo get",attr->s,attr->len,0); return -2; error: stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo get",attr->s,attr->len,0); return -1; } int mongo_con_set(cachedb_con *connection,str *attr,str *val,int expires) { bson new_b; int i; struct timeval start; mongo *conn = &MONGO_CDB_CON(connection); LM_DBG("Set operation on namespace %s\n",MONGO_NAMESPACE(connection)); start_expire_timer(start,mongo_exec_threshold); bson_init(&new_b); if (bson_append_string_n(&new_b,"_id",attr->s,attr->len) != BSON_OK) { LM_ERR("Failed to append _id \n"); bson_destroy(&new_b); goto error; } if (bson_append_string_n(&new_b,"opensips",val->s,val->len) != BSON_OK) { LM_ERR("Failed to append _id \n"); bson_destroy(&new_b); goto error; } bson_finish(&new_b); for (i=0;i<2;i++) { if (mongo_insert(conn,MONGO_NAMESPACE(connection), &new_b,NULL) != BSON_OK) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to do insert. Con err = %d\n", conn->err); bson_destroy(&new_b); goto error; } } stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo set",attr->s,attr->len,0); bson_destroy(&new_b); return 0; error: stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo set",attr->s,attr->len,0); return -1; } int mongo_con_remove(cachedb_con *connection,str *attr) { bson new_b; int i; struct timeval start; mongo *conn = &MONGO_CDB_CON(connection); LM_DBG("Remove operation on namespace %s\n",MONGO_NAMESPACE(connection)); start_expire_timer(start,mongo_exec_threshold); bson_init(&new_b); if (bson_append_string_n(&new_b,"_id",attr->s,attr->len) != BSON_OK) { LM_ERR("Failed to append _id \n"); bson_destroy(&new_b); goto error; } bson_finish(&new_b); for (i=0;i<2;i++) { if (mongo_remove(conn,MONGO_NAMESPACE(connection), &new_b,NULL) != BSON_OK) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to do insert. Con err = %d\n", conn->err); bson_destroy(&new_b); goto error; } } stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo remove",attr->s,attr->len,0); bson_destroy(&new_b); return 0; error: stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo remove",attr->s,attr->len,0); return -1; } void dbg_bson_print_raw( const char *data , int depth ) { bson_iterator i; const char *key; int temp; bson_timestamp_t ts; char oidhex[HEX_OID_SIZE]; bson scope; bson_iterator_from_buffer( &i, data ); while ( bson_iterator_next( &i ) ) { bson_type t = bson_iterator_type( &i ); if ( t == 0 ) break; key = bson_iterator_key( &i ); for ( temp=0; temp<=depth; temp++ ) LM_INFO( "\t" ); LM_INFO( "key [%s] : type [%d] \t " , key , t ); switch ( t ) { case BSON_DOUBLE: LM_INFO( "double ---%f---" , bson_iterator_double( &i ) ); break; case BSON_STRING: LM_INFO( "string ---%s---" , bson_iterator_string( &i ) ); break; case BSON_SYMBOL: LM_INFO( "SYMBOL: ---%s---" , bson_iterator_string( &i ) ); break; case BSON_OID: bson_oid_to_string( bson_iterator_oid( &i ), oidhex ); LM_INFO( "oid ---%s---" , oidhex ); break; case BSON_BOOL: LM_INFO( "bool ---%s---" , bson_iterator_bool( &i ) ? "true" : "false" ); break; case BSON_DATE: LM_INFO( "date ---%ld---" , ( long int )bson_iterator_date( &i ) ); break; case BSON_BINDATA: LM_INFO( "BSON_BINDATA" ); break; case BSON_UNDEFINED: LM_INFO( "BSON_UNDEFINED" ); break; case BSON_NULL: LM_INFO( "BSON_NULL" ); break; case BSON_REGEX: LM_INFO( "BSON_REGEX: ---%s---", bson_iterator_regex( &i ) ); break; case BSON_CODE: LM_INFO( "BSON_CODE: ---%s---", bson_iterator_code( &i ) ); break; case BSON_CODEWSCOPE: LM_INFO( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); bson_init( &scope ); bson_iterator_code_scope( &i, &scope ); LM_INFO( "\n\t SCOPE: " ); bson_print( &scope ); break; case BSON_INT: LM_INFO( "int ---%d---" , bson_iterator_int( &i ) ); break; case BSON_LONG: LM_INFO( "long ---%ld---" , bson_iterator_long( &i ) ); break; case BSON_TIMESTAMP: ts = bson_iterator_timestamp( &i ); LM_INFO( "i: %d, t: %d", ts.i, ts.t ); break; case BSON_OBJECT: case BSON_ARRAY: LM_INFO( "--- array ---\n" ); dbg_bson_print_raw( bson_iterator_value( &i ) , depth + 1 ); break; default: bson_errprintf( "can't print type : %d\n" , t ); } LM_INFO( "\n" ); } } void dbg_print_bson(bson *b) { dbg_bson_print_raw(b->data,0); } int mongo_raw_find(cachedb_con *connection,bson *raw_query,cdb_raw_entry ***reply,int expected_kv_no,int *reply_no) { bson_iterator i; mongo_cursor *m_cursor; const char *key,*ns=NULL; mongo *conn = &MONGO_CDB_CON(connection); bson op_b,fields_b,err_b; int have_query=0,have_fields=0,j,ret; if (!reply || expected_kv_no != 1) { LM_ERR("A find op should expect document results\n"); return -1; } bson_iterator_from_buffer( &i, raw_query->data ); while ( bson_iterator_next( &i ) ) { bson_type t = bson_iterator_type( &i ); if ( t == 0 ) break; key = bson_iterator_key( &i ); switch ( t ) { case BSON_STRING: if (strcmp(key,"op") == 0) { continue; } else if (strcmp(key,"ns") == 0) { ns = bson_iterator_string(&i); LM_DBG("found ns [%s] \n",ns); } break; case BSON_OBJECT: case BSON_ARRAY: if (strcmp(key,"query") == 0) { memset(&op_b,0,sizeof(bson)); bson_init_finished_data(&op_b,(char *)bson_iterator_value(&i)); have_query=1; } else if (strcmp(key,"fields") == 0) { memset(&fields_b,0,sizeof(bson)); bson_init_finished_data(&fields_b,(char *)bson_iterator_value(&i)); have_fields=1; } break; default: LM_DBG("Unusable type %d - ignoring \n",t); } } if (have_query == 0) { LM_ERR("Cannot proceed. Don't have the actual find query \n"); return -1; } for (j=0;j<2;j++) { m_cursor = mongo_find(conn,ns?ns:MONGO_NAMESPACE(connection), &op_b,have_fields?&fields_b:0,0,0,mongo_slave_ok); if (m_cursor == NULL) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); ret = mongo_cmd_get_last_error(conn,MONGO_DATABASE(connection),&err_b); if (ret == MONGO_OK) { LM_ERR("We had error - can't tell what it was - we're really bogus - probably mongos down\n"); return -1; } if (!bson_size(&err_b)) continue; bson_iterator_init(&i,&err_b); while( bson_iterator_next(&i)) { LM_ERR("Fetched ERR key [%s]. Val = ",bson_iterator_key(&i)); switch( bson_iterator_type( &i ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&i)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&i)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&i)); break; default: /* TODO - support more types here */ LM_DBG("(unknown type %d)\n",bson_iterator_type(&i)); break; } } return -1; } break; } ret = mongo_cursor_to_json(m_cursor,reply,expected_kv_no,reply_no); mongo_cursor_destroy(m_cursor); return ret; } int mongo_raw_count(cachedb_con *connection,bson *raw_query,cdb_raw_entry ***reply,int expected_kv_no,int *reply_no) { bson_iterator i; const char *key,*ns=NULL; mongo *conn = &MONGO_CDB_CON(connection); bson op_b,err_b; int have_query=0,j,result=0,ns_len=0; char db[32],coll[32],*p; if (!reply || expected_kv_no != 1) { LM_ERR("A count op should expect a single result\n"); return -1; } bson_iterator_from_buffer( &i, raw_query->data ); while ( bson_iterator_next( &i ) ) { bson_type t = bson_iterator_type( &i ); if ( t == 0 ) break; key = bson_iterator_key( &i ); switch ( t ) { case BSON_STRING: if (strcmp(key,"op") == 0) { continue; } else if (strcmp(key,"ns") == 0) { ns = bson_iterator_string(&i); LM_DBG("found ns [%s] \n",ns); ns_len = strlen(ns); p = memchr(ns,'.',ns_len); if (!p) { LM_ERR("Invalid provided namespace \n"); return -1; } memcpy(db,ns,p-ns); db[p-ns]=0; memcpy(coll,p+1,ns+ns_len-p-1); coll[ns+ns_len-p-1]=0; } break; case BSON_OBJECT: case BSON_ARRAY: if (strcmp(key,"query") == 0) { memset(&op_b,0,sizeof(bson)); bson_init_finished_data(&op_b,(char *)bson_iterator_value(&i)); have_query=1; } break; default: LM_DBG("Unusable type %d - ignoring \n",t); } } if (have_query == 0) { LM_ERR("Cannot proceed. Don't have the actual count query \n"); return -1; } for (j=0;j<2;j++) { result = (int)mongo_count(conn,ns?db:MONGO_DATABASE(connection), ns?coll:MONGO_COLLECTION(connection),&op_b); if (result == MONGO_ERROR) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(connection),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&i,&err_b); while( bson_iterator_next(&i)) { LM_ERR("Fetched ERR key [%s]. Val = ",bson_iterator_key(&i)); switch( bson_iterator_type( &i ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&i)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&i)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&i)); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&i)); break; } } return -1; } break; } LM_DBG("The result is [%d]\n",result); *reply = pkg_malloc(1 * sizeof(cdb_raw_entry *)); if (*reply == NULL) { LM_ERR("No more pkg mem\n"); return -1; } **reply = pkg_malloc(1 * sizeof(cdb_raw_entry)); if (**reply == NULL) { LM_ERR("No more pkg mem\n"); pkg_free(*reply); return -1; } (**reply)->type = CDB_INT; (**reply)->val.n = result; *reply_no = 1; return 0; } int mongo_raw_update(cachedb_con *connection,bson *raw_query) { bson_iterator i; const char *key,*ns=NULL; mongo *conn = &MONGO_CDB_CON(connection); bson op_b,match_b,err_b; int have_query=0,have_match=0,j,ret; bson_iterator_from_buffer( &i, raw_query->data ); while ( bson_iterator_next( &i ) ) { bson_type t = bson_iterator_type( &i ); if ( t == 0 ) break; key = bson_iterator_key( &i ); switch ( t ) { case BSON_STRING: if (strcmp(key,"op") == 0) { continue; } else if (strcmp(key,"ns") == 0) { ns = bson_iterator_string(&i); LM_DBG("found ns [%s] \n",ns); } break; case BSON_OBJECT: case BSON_ARRAY: if (strcmp(key,"query") == 0) { memset(&op_b,0,sizeof(bson)); bson_init_finished_data(&op_b,(char *)bson_iterator_value(&i)); have_query=1; } else if (strcmp(key,"match") == 0) { memset(&match_b,0,sizeof(bson)); bson_init_finished_data(&match_b,(char *)bson_iterator_value(&i)); have_match=1; } break; default: LM_DBG("Unusable type %d - ignoring \n",t); } } if (have_query == 0) { LM_ERR("Cannot proceed. Don't have the actual update query \n"); return -1; } for (j=0;j<2;j++) { /* TODO - various flags - upsert, multi etc */ ret = mongo_update(conn,ns?ns:MONGO_NAMESPACE(connection), have_match?&match_b:0,&op_b,0,0); if (ret == MONGO_ERROR) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(connection),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&i,&err_b); while( bson_iterator_next(&i)) { LM_ERR("Fetched ERR key [%s]. Val = ",bson_iterator_key(&i)); switch( bson_iterator_type( &i ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&i)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&i)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&i)); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&i)); break; } } return -1; } break; } return 0; } int mongo_raw_insert(cachedb_con *connection,bson *raw_query) { bson_iterator i; const char *key,*ns=NULL; mongo *conn = &MONGO_CDB_CON(connection); bson op_b,err_b; int have_query=0,j,ret; bson_iterator_from_buffer( &i, raw_query->data ); while ( bson_iterator_next( &i ) ) { bson_type t = bson_iterator_type( &i ); if ( t == 0 ) break; key = bson_iterator_key( &i ); switch ( t ) { case BSON_STRING: if (strcmp(key,"op") == 0) { continue; } else if (strcmp(key,"ns") == 0) { ns = bson_iterator_string(&i); LM_DBG("found ns [%s] \n",ns); } break; case BSON_OBJECT: case BSON_ARRAY: if (strcmp(key,"query") == 0) { memset(&op_b,0,sizeof(bson)); bson_init_finished_data(&op_b,(char *)bson_iterator_value(&i)); have_query=1; } break; default: LM_DBG("Unusable type %d - ignoring \n",t); } } if (have_query == 0) { LM_ERR("Cannot proceed. Don't have the actual insert query \n"); return -1; } for (j=0;j<2;j++) { ret = mongo_insert(conn,ns?ns:MONGO_NAMESPACE(connection), &op_b,0); if (ret == MONGO_ERROR) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(connection),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&i,&err_b); while( bson_iterator_next(&i)) { LM_ERR("Fetched ERR key [%s]. Val = ",bson_iterator_key(&i)); switch( bson_iterator_type( &i ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&i)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&i)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&i)); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&i)); break; } } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); return -1; } break; } return 0; } int mongo_raw_remove(cachedb_con *connection,bson *raw_query) { bson_iterator i; const char *key,*ns=NULL; mongo *conn = &MONGO_CDB_CON(connection); bson op_b,err_b; int have_query=0,j,ret; bson_iterator_from_buffer( &i, raw_query->data ); while ( bson_iterator_next( &i ) ) { bson_type t = bson_iterator_type( &i ); if ( t == 0 ) break; key = bson_iterator_key( &i ); switch ( t ) { case BSON_STRING: if (strcmp(key,"op") == 0) { continue; } else if (strcmp(key,"ns") == 0) { ns = bson_iterator_string(&i); LM_DBG("found ns [%s] \n",ns); } break; case BSON_OBJECT: case BSON_ARRAY: if (strcmp(key,"query") == 0) { memset(&op_b,0,sizeof(bson)); bson_init_finished_data(&op_b,(char *)bson_iterator_value(&i)); have_query=1; } break; default: LM_DBG("Unusable type %d - ignoring \n",t); } } if (have_query == 0) { LM_ERR("Cannot proceed. Don't have the actual remove query \n"); return -1; } for (j=0;j<2;j++) { ret = mongo_remove(conn,ns?ns:MONGO_NAMESPACE(connection), &op_b,0); if (ret == MONGO_ERROR) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(connection),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&i,&err_b); while( bson_iterator_next(&i)) { LM_ERR("Fetched ERR key [%s]. Val = ",bson_iterator_key(&i)); switch( bson_iterator_type( &i ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&i)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&i)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&i)); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&i)); break; } return -1; } } break; } return 0; } static char *raw_query_buf=NULL; static int raw_query_buf_len=0; int mongo_con_raw_query(cachedb_con *connection,str *attr,cdb_raw_entry ***reply,int expected_kv_no,int *reply_no) { bson new_b; int ret; bson_iterator i; struct timeval start; const char *op=NULL; LM_DBG("Get operation on namespace %s\n",MONGO_NAMESPACE(connection)); start_expire_timer(start,mongo_exec_threshold); if (attr->len > raw_query_buf_len) { raw_query_buf = pkg_realloc(raw_query_buf,attr->len+1); if (!raw_query_buf) { LM_ERR("No more pkg\n"); goto error; } memcpy(raw_query_buf,attr->s,attr->len); raw_query_buf[attr->len]=0; raw_query_buf_len = attr->len; } else { memcpy(raw_query_buf,attr->s,attr->len); raw_query_buf[attr->len]=0; } ret = json_to_bson(raw_query_buf,&new_b); if (ret < 0) { LM_ERR("Failed to convert [%.*s] to BSON\n",attr->len,attr->s); goto error; } if (bson_find(&i,&new_b,"op") == BSON_EOO) { LM_ERR("No \"op\" specified \n"); bson_destroy(&new_b); goto error; } if (bson_iterator_type( &i ) != BSON_STRING) { LM_ERR("The op must be a string \n"); bson_destroy(&new_b); goto error; } op = bson_iterator_string( &i ); if (strcmp(op,"find") == 0) { ret = mongo_raw_find(connection,&new_b,reply,expected_kv_no,reply_no); } else if (strcmp(op,"update") == 0) { ret = mongo_raw_update(connection,&new_b); } else if (strcmp(op,"insert") == 0) { ret = mongo_raw_insert(connection,&new_b); } else if (strcmp(op,"remove") == 0) { ret = mongo_raw_remove(connection,&new_b); } else if (strcmp(op,"count") == 0) { ret = mongo_raw_count(connection,&new_b,reply,expected_kv_no,reply_no); } else { LM_ERR("Unsupported op type [%s] \n",op); bson_destroy(&new_b); goto error; } stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo raw",attr->s,attr->len,0); bson_destroy(&new_b); return ret; error: stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo raw",attr->s,attr->len,0); return -1; } static char counter_q_buf[256]; int mongo_con_add(cachedb_con *connection,str *attr,int val,int expires,int *new_val) { bson cmd,err_b,out; int j,ret; struct timeval start; mongo *conn = &MONGO_CDB_CON(connection); bson_iterator it,it2; const char *curr_key,*inner_key; start_expire_timer(start,mongo_exec_threshold); bson_init( &cmd ); bson_append_string(&cmd,"findAndModify",MONGO_COLLECTION(connection)); bson_append_start_object(&cmd,"query"); memcpy(counter_q_buf,attr->s,attr->len); counter_q_buf[attr->len]=0; bson_append_string(&cmd,"_id",counter_q_buf); bson_append_finish_object(&cmd); bson_append_start_object(&cmd,"update"); bson_append_start_object(&cmd,"$inc"); bson_append_int(&cmd,"opensips_counter",val); bson_append_finish_object(&cmd); bson_append_finish_object(&cmd); bson_append_bool(&cmd,"upsert",1); bson_finish(&cmd); for (j=0;j<2;j++) { ret = mongo_run_command(conn,MONGO_DATABASE(connection), &cmd,&out); if (ret != MONGO_OK) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(connection),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&it,&err_b); while( bson_iterator_next(&it)) { LM_ERR("Fetched ERR key [%s]. Val = ",bson_iterator_key(&it)); switch( bson_iterator_type( &it ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&it)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&it)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&it)); break; default: /* TODO - support more types here */ LM_DBG("(unknown type %d)\n",bson_iterator_type(&it)); break; } } bson_destroy(&cmd); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo add",attr->s,attr->len,0); return -1; } break; } if (!new_val) { bson_destroy(&out); bson_destroy(&cmd); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo add",attr->s,attr->len,0); return 0; } bson_iterator_init(&it,&out); while( bson_iterator_next(&it)) { curr_key=bson_iterator_key(&it); if (memcmp(curr_key,"retval",6) == 0) { if (bson_iterator_type(&it) != BSON_OBJECT) { LM_ERR("Unexpected value type %d\n", bson_iterator_type(&it)); goto err; } bson_iterator_subiterator(&it,&it2); while (bson_iterator_next(&it2)) { inner_key=bson_iterator_key(&it2); if (memcmp(inner_key,"cval",4) == 0) { *new_val = bson_iterator_int(&it2) + val; bson_destroy(&out); bson_destroy(&cmd); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo add",attr->s,attr->len,0); return 0; } } } } err: bson_destroy(&out); bson_destroy(&cmd); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo add",attr->s,attr->len,0); return -1; } int mongo_con_sub(cachedb_con *connection,str *attr,int val,int expires,int *new_val) { return mongo_con_add(connection,attr,-val,expires,new_val); } int mongo_con_get_counter(cachedb_con *connection,str *attr,int *val) { bson new_b,err_b; mongo_cursor *m_cursor; bson_iterator it; int i; struct timeval start; mongo *conn = &MONGO_CDB_CON(connection); char hex_oid[HEX_OID_SIZE]; LM_DBG("Get counter operation on namespace %s\n",MONGO_NAMESPACE(connection)); start_expire_timer(start,mongo_exec_threshold); bson_init(&new_b); if (bson_append_string_n(&new_b,"_id",attr->s,attr->len) != BSON_OK) { LM_ERR("Failed to append _id \n"); bson_destroy(&new_b); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo get_counter",attr->s,attr->len,0); return -1; } bson_finish(&new_b); for (i=0;i<2;i++) { m_cursor = mongo_find(conn,MONGO_NAMESPACE(connection), &new_b,0,0,0,mongo_slave_ok); if (m_cursor == NULL) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(connection),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&it,&err_b); while( bson_iterator_next(&it)) { LM_DBG("Fetched key %s\n",bson_iterator_key(&it)); switch( bson_iterator_type( &it ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&it)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&it)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&it)); break; case BSON_OID: bson_oid_to_string(bson_iterator_oid(&it),hex_oid); LM_DBG("(oid) \"%s\"\n",hex_oid); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&it)); break; } } bson_destroy(&new_b); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo get_counter",attr->s,attr->len,0); return -1; } break; } while( mongo_cursor_next(m_cursor) == MONGO_OK ) { bson_iterator_init(&it,mongo_cursor_bson(m_cursor)); if (bson_find(&it,mongo_cursor_bson(m_cursor),"opensips_counter") == BSON_EOO) continue; switch( bson_iterator_type( &it ) ) { case BSON_INT: if (val) *val = bson_iterator_int(&it); mongo_cursor_destroy(m_cursor); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo get_counter",attr->s,attr->len,0); return 0; break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&it)); break; } } LM_DBG("No suitable response found\n"); mongo_cursor_destroy(m_cursor); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo get_counter",attr->s,attr->len,0); return -2; } #define MONGO_DB_KEY_TRANS(key,val,index,op,query)\ do { \ if (VAL_NULL(val+index) == 0) { \ memcpy(key_buff,key[index]->s,key[index]->len); \ key_buff[key[index]->len]=0; \ if (op != NULL && strcmp(op[index],OP_EQ)) { \ bson_append_start_object(&query,key_buff);\ if (strcmp(op[index],OP_LT) == 0) \ memcpy(key_buff,"$lt",4); \ else if (strcmp(op[index],OP_GT) == 0) \ memcpy(key_buff,"$gt",4); \ else if (strcmp(op[index],OP_LEQ) == 0) \ memcpy(key_buff,"$lte",5); \ else if (strcmp(op[index],OP_GEQ) == 0) \ memcpy(key_buff,"$gte",5); \ else if (strcmp(op[index],OP_NEQ) == 0) \ memcpy(key_buff,"$ne",4); \ } \ switch VAL_TYPE(val+index) { \ case DB_INT: \ bson_append_int(&query,key_buff,VAL_INT(val+index)); \ break; \ case DB_STRING: \ if (appendOID && key[index]->len == 3 && strncmp("_id", key[index]->s,key[index]->len) == 0) { \ LM_DBG("we got it [%.*s]\n", key[index]->len, key[index]->s); \ bson_oid_from_string(&_id, VAL_STRING(val+index)); \ bson_append_oid(&query,key_buff,&_id); \ appendOID = 0; \ } else { \ bson_append_string(&query,key_buff,VAL_STRING(val+index)); \ } \ break; \ case DB_STR: \ if (appendOID && key[index]->len == 3 && strncmp("_id", key[index]->s,key[index]->len) == 0) { \ p = VAL_STR(val+index).s + VAL_STR(val+index).len; \ _old_char = *p; \ *p = '\0'; \ bson_oid_from_string(&_id, VAL_STR(val+index).s); \ *p = _old_char; \ bson_append_oid(&query,key_buff,&_id); \ appendOID = 0; \ } else { \ bson_append_string_n(&query,key_buff,VAL_STR(val+index).s, \ VAL_STR(val+index).len); \ } \ break; \ case DB_BLOB: \ bson_append_string_n(&query,key_buff,VAL_BLOB(val+index).s, \ VAL_BLOB(val+index).len); \ break; \ case DB_DOUBLE: \ bson_append_double(&query,key_buff,VAL_DOUBLE(val+index)); \ break; \ case DB_BIGINT: \ bson_append_long(&query,key_buff,VAL_BIGINT(val+index)); \ break; \ case DB_DATETIME: \ bson_append_time_t(&query,key_buff,VAL_TIME(val+index)); \ break; \ case DB_BITMAP: \ bson_append_int(&query,key_buff,VAL_BITMAP(val+index)); \ break; \ } \ if (op != NULL && strcmp(op[index],OP_EQ)) { \ bson_append_finish_object(&query); \ } \ } \ } while (0) int mongo_db_query_trans(cachedb_con *con,const str *table,const db_key_t* _k, const db_op_t* _op,const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,const db_key_t _o, db_res_t** _r) { char key_buff[32],namespace_buff[64],*p; bson query; bson fields; bson err_b; int i,j,row_no; mongo *conn = &MONGO_CDB_CON(con); mongo_cursor *m_cursor; bson_iterator it; char hex_oid[HEX_OID_SIZE]; db_row_t *current; db_val_t *cur_val; static str dummy_string = {"", 0}; struct timeval start; char _old_char; bson_oid_t _id; int appendOID = 1; start_expire_timer(start,mongo_exec_threshold); if (!_c) { LM_ERR("The module does not support 'select *' SQL queries \n"); return -1; } bson_init(&query); if (_n) { bson_append_start_object(&query, "$query"); for (i=0;i<_n;i++) { MONGO_DB_KEY_TRANS(_k,_v,i,_op,query); } bson_append_finish_object(&query); } if (_o) { if (!_n) { bson_append_start_object(&query, "$query"); bson_append_finish_object(&query); } memcpy(key_buff,_o->s,_o->len); key_buff[_o->len]=0; bson_append_start_object(&query, "$orderby"); bson_append_int(&query,key_buff,1); bson_append_finish_object(&query); } bson_finish(&query); bson_init(&fields); for (i=0;i<_nc;i++) { memcpy(key_buff,_c[i]->s,_c[i]->len); key_buff[_c[i]->len]=0; bson_append_bool(&fields,key_buff,1); } bson_finish(&fields); p=namespace_buff; i = strlen(MONGO_DATABASE(con)); memcpy(p,MONGO_DATABASE(con),i); p +=i; *p++ = '.'; memcpy(p,table->s,table->len); p+= table->len; *p = 0; LM_DBG("Running raw mongo query on table %s\n",namespace_buff); for (i=0;i<2;i++) { m_cursor = mongo_find(conn,namespace_buff, &query,&fields,0,0,mongo_slave_ok); if (m_cursor == NULL) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(con),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&it,&err_b); while( bson_iterator_next(&it)) { LM_DBG("Fetched key %s\n",bson_iterator_key(&it)); switch( bson_iterator_type( &it ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&it)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&it)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&it)); break; case BSON_OID: bson_oid_to_string(bson_iterator_oid(&it),hex_oid); LM_DBG("(oid) \"%s\"\n",hex_oid); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&it)); break; } } goto error; } break; } MONGO_CDB_CURSOR(con) = m_cursor; *_r = db_new_result(); if (*_r == NULL) { LM_ERR("Failed to init new result \n"); goto error; } RES_COL_N(*_r) = _nc; row_no = m_cursor->reply->fields.num; LM_DBG("We have %d rows\n",row_no); if (row_no == 0) { LM_DBG("No rows returned from Mongo \n"); bson_destroy(&fields); bson_destroy(&query); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo sql_select",table->s,table->len,0); return 0; } /* on first iteration we allocate the result * we always assume the query returns exactly the number * of 'columns' as were requested */ if (db_allocate_columns(*_r,_nc) != 0) { LM_ERR("Failed to allocate columns \n"); goto error2; } /* and we initialize the names as if all are there */ for (j=0;j<_nc;j++) { /* since we don't have schema, the types will be allocated * when we fetch the actual rows */ RES_NAMES(*_r)[j]->s = _c[j]->s; RES_NAMES(*_r)[j]->len = _c[j]->len; } if (db_allocate_rows(*_r,row_no) != 0) { LM_ERR("No more private memory for rows \n"); goto error2; } RES_ROW_N(*_r) = row_no; hex_oid_id = pkg_malloc(sizeof(char) * row_no * HEX_OID_SIZE); if (hex_oid_id==NULL) { LM_ERR("oom\n"); goto error2; } i=0; while( mongo_cursor_next(m_cursor) == MONGO_OK ) { bson_iterator_init(&it,mongo_cursor_bson(m_cursor)); current = &(RES_ROWS(*_r)[i]); ROW_N(current) = RES_COL_N(*_r); for (j=0;j<_nc;j++) { memcpy(key_buff,_c[j]->s,_c[j]->len); key_buff[_c[j]->len]=0; cur_val = &ROW_VALUES(current)[j]; if (bson_find(&it,mongo_cursor_bson(m_cursor),key_buff) == BSON_EOO) { memset(cur_val,0,sizeof(db_val_t)); VAL_STRING(cur_val) = dummy_string.s; VAL_STR(cur_val) = dummy_string; VAL_BLOB(cur_val) = dummy_string; /* we treat null values as DB string */ VAL_TYPE(cur_val) = DB_STRING; VAL_NULL(cur_val) = 1; LM_DBG("Found empty [%.*s]\n", _c[j]->len, _c[j]->s); } else { switch( bson_iterator_type( &it ) ) { case BSON_INT: VAL_TYPE(cur_val) = DB_INT; VAL_INT(cur_val) = bson_iterator_int(&it); LM_DBG("Found int [%.*s]=[%d]\n", _c[j]->len, _c[j]->s, VAL_INT(cur_val)); break; case BSON_DOUBLE: VAL_TYPE(cur_val) = DB_DOUBLE; VAL_DOUBLE(cur_val) = bson_iterator_double(&it); LM_DBG("Found double [%.*s]=[%f]\n", _c[j]->len, _c[j]->s, VAL_DOUBLE(cur_val)); break; case BSON_STRING: VAL_TYPE(cur_val) = DB_STRING; VAL_STRING(cur_val) = bson_iterator_string(&it); LM_DBG("Found string [%.*s]=[%s]\n", _c[j]->len, _c[j]->s, VAL_STRING(cur_val)); break; case BSON_LONG: VAL_TYPE(cur_val) = DB_BIGINT; VAL_BIGINT(cur_val) = bson_iterator_long(&it); LM_DBG("Found long [%.*s]=[%lld]\n", _c[j]->len, _c[j]->s, VAL_BIGINT(cur_val)); break; case BSON_DATE: VAL_TYPE(cur_val) = DB_DATETIME; VAL_TIME(cur_val) = bson_iterator_time_t(&it); LM_DBG("Found time [%.*s]=[%d]\n", _c[j]->len, _c[j]->s, (int)VAL_TIME(cur_val)); break; case BSON_OID: bson_oid_to_string(bson_iterator_oid(&it), hex_oid); p = &hex_oid_id[i*HEX_OID_SIZE]; memcpy(p, hex_oid, HEX_OID_SIZE); VAL_TYPE(cur_val) = DB_STRING; VAL_STRING(cur_val) = p; LM_DBG("Found oid [%.*s]=[%s]\n", _c[j]->len, _c[j]->s, VAL_STRING(cur_val)); break; default: LM_WARN("Unsupported type [%d] for [%.*s] - treating as NULL\n", bson_iterator_type(&it), _c[j]->len, _c[j]->s); memset(cur_val,0,sizeof(db_val_t)); VAL_STRING(cur_val) = dummy_string.s; VAL_STR(cur_val) = dummy_string; VAL_BLOB(cur_val) = dummy_string; /* we treat null values as DB string */ VAL_TYPE(cur_val) = DB_STRING; VAL_NULL(cur_val) = 1; break; } } } i++; } LM_DBG("Successfully ran query\n"); bson_destroy(&query); bson_destroy(&fields); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo sql_select",table->s,table->len,0); return 0; error2: db_free_result(*_r); mongo_cursor_destroy(m_cursor); *_r = NULL; MONGO_CDB_CURSOR(con) = NULL; error: bson_destroy(&query); bson_destroy(&fields); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo sql_select",table->s,table->len,0); return -1; } int mongo_db_free_result_trans(cachedb_con* con, db_res_t* _r) { if ((!con) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } LM_DBG("freeing mongo query result \n"); if (hex_oid_id) { pkg_free(hex_oid_id); hex_oid_id = NULL; } if (db_free_result(_r) < 0) { LM_ERR("unable to free result structure\n"); return -1; } mongo_cursor_destroy(MONGO_CDB_CURSOR(con)); MONGO_CDB_CURSOR(con) = NULL; return 0; } int mongo_db_insert_trans(cachedb_con *con,const str *table,const db_key_t* _k, const db_val_t* _v,const int _n) { int i,j,ret; bson query; bson err_b; char key_buff[32],namespace_buff[64],*p; mongo *conn = &MONGO_CDB_CON(con); bson_iterator it; struct timeval start; char _old_char; bson_oid_t _id; int appendOID = 1; start_expire_timer(start,mongo_exec_threshold); bson_init(&query); for (i=0;i<_n;i++) { if (VAL_NULL(_v+i) == 0) { MONGO_DB_KEY_TRANS(_k,_v,i,((db_op_t*)0),query); } } bson_finish(&query); p=namespace_buff; i = strlen(MONGO_DATABASE(con)); memcpy(p,MONGO_DATABASE(con),i); p +=i; *p++ = '.'; memcpy(p,table->s,table->len); p+= table->len; *p = 0; LM_DBG("Running raw mongo insert on table %s\n",namespace_buff); for (j=0;j<2;j++) { ret = mongo_insert(conn,namespace_buff,&query,0); if (ret == MONGO_ERROR) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(con),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&it,&err_b); while( bson_iterator_next(&it)) { LM_ERR("Fetched ERR key [%s]. Val = ",bson_iterator_key(&it)); switch( bson_iterator_type( &it ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&it)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&it)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&it)); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&it)); break; } } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo sql_insert",table->s,table->len,0); return -1; } break; } stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo sql_insert",table->s,table->len,0); return 0; } int mongo_db_delete_trans(cachedb_con *con,const str *table,const db_key_t* _k,const db_op_t *_o, const db_val_t* _v,const int _n) { int i,j,ret; bson query; bson err_b; char key_buff[32],namespace_buff[64],*p; mongo *conn = &MONGO_CDB_CON(con); bson_iterator it; struct timeval start; char _old_char; bson_oid_t _id; int appendOID = 1; start_expire_timer(start,mongo_exec_threshold); bson_init(&query); for (i=0;i<_n;i++) { MONGO_DB_KEY_TRANS(_k,_v,i,_o,query); } bson_finish(&query); p=namespace_buff; i = strlen(MONGO_DATABASE(con)); memcpy(p,MONGO_DATABASE(con),i); p +=i; *p++ = '.'; memcpy(p,table->s,table->len); p+= table->len; *p = 0; LM_DBG("Running raw mongo delete on table %s\n",namespace_buff); for (j=0;j<2;j++) { ret = mongo_remove(conn,namespace_buff,&query,0); if (ret == MONGO_ERROR) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(con),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&it,&err_b); while( bson_iterator_next(&it)) { LM_ERR("Fetched ERR key [%s]. Val = ",bson_iterator_key(&it)); switch( bson_iterator_type( &it ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&it)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&it)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&it)); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&it)); break; } } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo sql_delete",table->s,table->len,0); return -1; } break; } stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo sql_delete",table->s,table->len,0); return 0; } int mongo_db_update_trans(cachedb_con *con,const str *table,const db_key_t* _k,const db_op_t *_o, const db_val_t* _v,const db_key_t* _uk, const db_val_t* _uv, const int _n,const int _un) { int i,j,ret; bson query,op_query; bson err_b; char key_buff[32],namespace_buff[64],*p; mongo *conn = &MONGO_CDB_CON(con); bson_iterator it; struct timeval start; char _old_char; bson_oid_t _id; int appendOID = 1; start_expire_timer(start,mongo_exec_threshold); bson_init(&query); for (i=0;i<_n;i++) { MONGO_DB_KEY_TRANS(_k,_v,i,_o,query); } bson_finish(&query); bson_init(&op_query); bson_append_start_object(&op_query, "$set"); for (i=0;i<_un;i++) { MONGO_DB_KEY_TRANS(_uk,_uv,i,((db_op_t*)NULL),op_query); } bson_append_finish_object(&op_query); bson_finish(&op_query); p=namespace_buff; i = strlen(MONGO_DATABASE(con)); memcpy(p,MONGO_DATABASE(con),i); p +=i; *p++ = '.'; memcpy(p,table->s,table->len); p+= table->len; *p = '\0'; LM_DBG("Running raw mongo update on table %s\n",namespace_buff); for (j=0;j<2;j++) { ret = mongo_update(conn,namespace_buff, &query,&op_query,MONGO_UPDATE_UPSERT|MONGO_UPDATE_MULTI,0); if (ret == MONGO_ERROR) { if (mongo_check_connection(conn) == MONGO_ERROR && mongo_reconnect(conn) == MONGO_OK && mongo_check_connection(conn) == MONGO_OK) { LM_INFO("Lost connection to Mongo but reconnected. Re-Trying\n"); continue; } LM_ERR("Failed to run query. Err = %d, %d , %d \n",conn->err,conn->errcode,conn->lasterrcode); mongo_cmd_get_last_error(conn,MONGO_DATABASE(con),&err_b); if (!bson_size(&err_b)) continue; bson_iterator_init(&it,&err_b); while( bson_iterator_next(&it)) { LM_ERR("Fetched ERR key [%s]. Val = ",bson_iterator_key(&it)); switch( bson_iterator_type( &it ) ) { case BSON_DOUBLE: LM_DBG("(double) %e\n",bson_iterator_double(&it)); break; case BSON_INT: LM_DBG("(int) %d\n",bson_iterator_int(&it)); break; case BSON_STRING: LM_DBG("(string) \"%s\"\n",bson_iterator_string(&it)); break; default: LM_DBG("(unknown type %d)\n",bson_iterator_type(&it)); break; } } return -1; stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo sql_update",table->s,table->len,0); } break; } stop_expire_timer(start,mongo_exec_threshold, "cachedb_mongo sql_update",table->s,table->len,0); return 0; } opensips-2.2.2/modules/cachedb_mongodb/cachedb_mongodb_dbase.h000066400000000000000000000063421300170765700245030ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #ifndef CACHEDBMONGO_DBASE_H #define CACHEDBMONGO_DBASE_H #include "../../cachedb/cachedb.h" #include "../../db/db.h" #define MONGO_HAVE_STDINT 1 #include #include #include #include extern int mongo_op_timeout; typedef struct { struct cachedb_id *id; unsigned int ref; struct cachedb_pool_con_t *next; /* shortcuts for raw queries */ char* database; char* collection; /* only if we connect to a repl set*/ char *replset_name; /* actual connection to mongo */ mongo connection; /* cursor result for the query */ mongo_cursor *cursor; } mongo_con; #define MONGO_CON(mon_con) ((mon_con)->connection) #define MONGO_CDB_CON(cdb_con) (((mongo_con *)((cdb_con)->data))->connection) #define MONGO_CDB_CURSOR(cdb_con) (((mongo_con *)((cdb_con)->data))->cursor) #define MONGO_NAMESPACE(cdb_con) (((mongo_con *)((cdb_con)->data))->id->database) #define MONGO_DATABASE(cdb_con) (((mongo_con *)((cdb_con)->data))->database) #define MONGO_COLLECTION(cdb_con) (((mongo_con *)((cdb_con)->data))->collection) cachedb_con* mongo_con_init(str *url); void mongo_con_destroy(cachedb_con *con); int mongo_con_get(cachedb_con *con,str *attr,str *val); int mongo_con_set(cachedb_con *con,str *attr,str *val,int expires); int mongo_con_remove(cachedb_con *connection,str *attr); int mongo_con_raw_query(cachedb_con *connection,str *attr,cdb_raw_entry ***val,int expected_kv_no,int *reply_no); int mongo_con_add(cachedb_con *connection,str *attr,int val,int expires,int *new_val); int mongo_con_sub(cachedb_con *connection,str *attr,int val,int expires,int *new_val); int mongo_con_get_counter(cachedb_con *connection,str *attr,int *val); int mongo_db_query_trans(cachedb_con *con,const str *table,const db_key_t* _k, const db_op_t* _op,const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,const db_key_t _o, db_res_t** _r); int mongo_db_free_result_trans(cachedb_con* con, db_res_t* _r); int mongo_db_insert_trans(cachedb_con *con,const str *table,const db_key_t* _k, const db_val_t* _v,const int _n); int mongo_db_delete_trans(cachedb_con *con,const str *table,const db_key_t* _k,const db_op_t *_o, const db_val_t* _v,const int _n); int mongo_db_update_trans(cachedb_con *con,const str *table,const db_key_t* _k,const db_op_t *_o, const db_val_t* _v,const db_key_t* _uk, const db_val_t* _uv, const int _n,const int _un); #endif /* CACHEDBMONGO_DBASE_H */ opensips-2.2.2/modules/cachedb_mongodb/cachedb_mongodb_json.c000066400000000000000000000233451300170765700243730ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #include "../../dprint.h" #include "../../ut.h" #include "cachedb_mongodb_json.h" #include "cachedb_mongodb_dbase.h" int json_to_bson_append_element( bson *bb , const char *k , struct json_object *v ); int json_to_bson_append_array( bson *bb , struct json_object *a ) { int i,al_len; char *al; json_object *it; for ( i=0; ihead; \ (entry ? (key = (char*)entry->k, \ val = (struct json_object*)entry->v, entry) : 0); \ entry = entry->next) int json_to_bson_append(bson *bb,struct json_object *o) { json_object_object_iterator( o,key,val ) { if (json_to_bson_append_element(bb,key,val)<0) { LM_ERR("Failed to append new element\n"); return -1; } } return 0; } int json_to_bson_append_element( bson *bb , const char *k , struct json_object *v ) { if (v==NULL) { bson_append_null(bb,k); return 0; } switch (json_object_get_type(v)) { case json_type_int: if (bson_append_int(bb,k,json_object_get_int(v)) != BSON_OK) { LM_ERR("Failed to append int\n"); return -1; } break; case json_type_boolean: if (bson_append_bool(bb,k,json_object_get_boolean(v)) != BSON_OK) { LM_ERR("Failed to append boolean\n"); return -1; } break; case json_type_double: if (bson_append_double(bb,k,json_object_get_double(v)) != BSON_OK) { LM_ERR("Failed to append double\n"); return -1; } break; case json_type_string: if (bson_append_string(bb,k,json_object_get_string(v)) != BSON_OK) { LM_ERR("Failed to append string\n"); return -1; } break; case json_type_object: if (bson_append_start_object( bb,k) != BSON_OK) { LM_ERR("Failed to append start object\n"); return -1; } if (json_to_bson_append(bb,v)<0) { LM_ERR("Failed to append to bson\n"); return -1; } if (bson_append_finish_object(bb) != BSON_OK) { LM_ERR("Failed to finish appending to BSON\n"); return -1; } break; case json_type_array: if (bson_append_start_array(bb,k) != BSON_OK) { LM_ERR("Failed to append start array\n"); return -1; } if (json_to_bson_append_array(bb,v) < 0) { LM_ERR("Failed to append array to bson\n"); return -1; } if (bson_append_finish_object(bb) != BSON_OK) { LM_ERR("Failed to finish appending array to bson\n"); return -1; } break; default: LM_ERR("Can't handle type for : %s\n",json_object_to_json_string(v)); return -1; } return 0; } int json_to_bson(char *json,bson *bb) { struct json_object *obj; LM_DBG("Trying to convert [%s]\n",json); obj=json_tokener_parse(json); if (is_error(obj)) { LM_ERR("Failed to parse JSON: %s\n",json); return -2; } if (!json_object_is_type(obj,json_type_object)) { LM_ERR("Inconsystent JSON type\n"); goto error; } bson_init(bb); if (json_to_bson_append(bb,obj) < 0) { LM_ERR("Failed to convert json to bson\n"); bson_finish(bb); bson_destroy(bb); goto error; } bson_finish(bb); json_object_put(obj); return 0; error: if (obj) json_object_put(obj); return -1; } void bson_to_json_generic(struct json_object *obj,bson_iterator *it,int type) { const char *curr_key; char *s; int len; struct json_object *obj2=NULL; bson_iterator it2; while (bson_iterator_next(it)) { curr_key=bson_iterator_key(it); switch( bson_iterator_type(it) ) { case BSON_INT: LM_DBG("Found key %s with type int\n",curr_key); if (type == BSON_OBJECT) json_object_object_add(obj,curr_key, json_object_new_int(bson_iterator_int(it))); else if (type == BSON_ARRAY) json_object_array_add(obj,json_object_new_int(bson_iterator_int(it))); break; case BSON_LONG: LM_DBG("Found key %s with type long\n",curr_key); /* no intrinsic support in OpenSIPS for 64bit integers - * converting to string */ s = int2str(bson_iterator_long(it),&len); s[len]=0; if (type == BSON_OBJECT) json_object_object_add(obj,curr_key,json_object_new_string(s)); else if (type == BSON_ARRAY) json_object_array_add(obj,json_object_new_string(s)); break; case BSON_DOUBLE: /* no intrinsic support in OpenSIPS for floating point numbers * converting to int */ LM_DBG("Found key %s with type double\n",curr_key); if (type == BSON_OBJECT) json_object_object_add(obj,curr_key, json_object_new_int((int)bson_iterator_double(it))); else if (type == BSON_ARRAY) json_object_array_add(obj,json_object_new_int((int)bson_iterator_double(it))); break; case BSON_STRING: LM_DBG("Found key %s with type string\n",curr_key); if (type == BSON_OBJECT) json_object_object_add(obj,curr_key, json_object_new_string(bson_iterator_string(it))); else if (type == BSON_ARRAY) json_object_array_add(obj,json_object_new_string(bson_iterator_string(it))); break; case BSON_BOOL: LM_DBG("Found key %s with type bool\n",curr_key); if (type == BSON_OBJECT) json_object_object_add(obj,curr_key, json_object_new_int((int)bson_iterator_bool(it))); else if (type == BSON_ARRAY) json_object_array_add(obj,json_object_new_int((int)bson_iterator_bool(it))); break; case BSON_DATE: LM_DBG("Found key %s with type date\n",curr_key); if (type == BSON_OBJECT) json_object_object_add(obj,curr_key, json_object_new_int((int)(bson_iterator_date(it)/1000))); else if (type == BSON_ARRAY) json_object_array_add(obj,json_object_new_int((int)(bson_iterator_date(it)/1000))); break; case BSON_ARRAY: LM_DBG("Found key %s with type array\n",curr_key); obj2 = json_object_new_array(); bson_iterator_subiterator(it, &it2 ); bson_to_json_generic(obj2,&it2,BSON_ARRAY); if (type == BSON_OBJECT) json_object_object_add(obj,curr_key,obj2); else if (type == BSON_ARRAY) json_object_array_add(obj,obj2); break; case BSON_OBJECT: LM_DBG("Found key %s with type object\n",curr_key); obj2 = json_object_new_object(); bson_iterator_subiterator(it, &it2 ); bson_to_json_generic(obj2,&it2,BSON_OBJECT); if (type == BSON_OBJECT) json_object_object_add(obj,curr_key,obj2); else if (type == BSON_ARRAY) json_object_array_add(obj,obj2); break; default: LM_DBG("Unsupported type %d for key %s - skipping\n", bson_iterator_type(it),curr_key); } } } int mongo_cursor_to_json(mongo_cursor *m_cursor, cdb_raw_entry ***reply,int expected_kv_no,int *reply_no) { struct json_object *obj=NULL; bson_iterator it; const char *p; int current_size=0,len; /* start with a single returned document */ *reply = pkg_malloc(1 * sizeof(cdb_raw_entry *)); if (*reply == NULL) { LM_ERR("No more PKG mem\n"); return -1; } /* expected_kv_no is always 1 for mongoDB */ **reply = pkg_malloc(expected_kv_no * sizeof(cdb_raw_entry)); if (**reply == NULL) { LM_ERR("No more pkg mem\n"); pkg_free(*reply); return -1; } while( mongo_cursor_next(m_cursor) == MONGO_OK ) { if (current_size > 0) { *reply = pkg_realloc(*reply,(current_size + 1) * sizeof(cdb_raw_entry *)); if (*reply == NULL) { LM_ERR("No more pkg\n"); goto error_cleanup; } (*reply)[current_size] = pkg_malloc(expected_kv_no * sizeof(cdb_raw_entry)); if ((*reply)[current_size] == NULL) { LM_ERR("No more pkg\n"); goto error_cleanup; } } obj = json_object_new_object(); bson_iterator_init(&it,mongo_cursor_bson(m_cursor)); bson_to_json_generic(obj,&it,BSON_OBJECT); p = json_object_to_json_string(obj); if (!p) { LM_ERR("Json failed to be translated to string\n"); goto error_cleanup; } len = strlen(p); (*reply)[current_size][0].val.s.s = pkg_malloc(len); if (! (*reply)[current_size][0].val.s.s ) { LM_ERR("No more pkg \n"); goto error_cleanup; } memcpy((*reply)[current_size][0].val.s.s,p,len); (*reply)[current_size][0].val.s.len = len; (*reply)[current_size][0].type = CDB_STR; json_object_put(obj); current_size++; } *reply_no = current_size; LM_DBG("Fetched %d results\n",current_size); if (current_size == 0) return -2; return 1; error_cleanup: if (obj) json_object_put(obj); for (len = 0;len #include int json_to_bson(char *json,bson *bb); int mongo_cursor_to_json(mongo_cursor *m_cursor, cdb_raw_entry ***reply,int expected_kv_no,int *reply_no); #endif /* CACHEDBMONGO_JSON_H */ opensips-2.2.2/modules/cachedb_mongodb/doc/000077500000000000000000000000001300170765700206565ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_mongodb/doc/cachedb_mongodb.xml000066400000000000000000000017141300170765700244610ustar00rootroot00000000000000 %docentities; ]> cachedb_mongodb Module &osipsname; Vladut-Stefan Paiu OpenSIPS Solutions
vladpaiu@opensips.org &osipssol;
Vladut-Stefan Paiu
vladpaiu@opensips.org
2013 &osipssol;
&admin;
opensips-2.2.2/modules/cachedb_mongodb/doc/cachedb_mongodb_admin.xml000066400000000000000000000235141300170765700256330ustar00rootroot00000000000000 &adminguide;
Overview This module is an implementation of a cache system designed to work with MongoDB servers. It uses the Key-Value interface exported from the core.
Advantages memory costs are no longer on the server many servers can be used inside a cluster, so the memory is virtually unlimited the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The MongoDB is also persistent so it can also be restarted without loss of information. MongoDB is an open-source project so it can be used to exchange data with various other applications By creating a MongoDB Cluster, multiple OpenSIPS instances can easily share key-value information This module also implements the CacheDB Raw query capability, thus you can run whatever query that the MongoDB back-end supports, taking full advatange of it.
Limitations keys (in key:value pairs) may not contain spaces or control characters
Dependencies
&osips; Modules None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libjson The libjson library can be downloaded from: http://oss.metaparadigm.com/json-c/ mongo-c-driver The mongo C driver can be downloaded from MongoDB's GitHub repository. Make sure to get the 0.6 version: "git clone https://github.com/mongodb/mongo-c-driver.git -b v0.6"
Exported Parameters
<varname>cachedb_url</varname> (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. If multiple addr:port pairs are passed, the connection is treated as going to a replica set. Set <varname>cachedb_url</varname> parameter ... modparam("cachedb_mongodb", "cachedb_url","mongodb:instance1://localhost:27017/db.collection") modparam("cachedb_mongodb", "cachedb_url","mongodb:replicaset1://1.2.3.4:27017,2.3.4.5:27017,3.4.5.6:27017/replicaSetName.db.collection") ... Use MongoDB servers ... cache_store("mongodb:group1","key","$ru value"); cache_fetch("mongodb:replicaset1","key",$avp(10)); cache_remove("mongodb:cluster1","key"); ...
<varname>op_timeout</varname> (int) The timeout in ms that will be triggered in case a MongoDB op takes too long. Default value is 3000 ( 3 seconds ) Set <varname>op_timeout</varname> parameter ... modparam("cachedb_mongodb", "op_timeout",5000) ...
<varname>slave_ok</varname> (int) If set to 1, read operations are allowed to go to secondary MongoDB servers Default value is 0 ( read and write requests will go to primary nodes = full consistency ) Set <varname>slave_ok</varname> parameter ... modparam("cachedb_mongodb", "slave_ok",1); ...
<varname>write_concern</varname> (string) The JSON containing the Mongo write concern that should affect all write operations. More info can be found at http://docs.mongodb.org/manual/core/write-operations/ Set <varname>write_concern</varname> parameter ... modparam("cachedb_mongodb","write_concern","{ \"getLastError\": 1, "j" : "true" }") ...
<varname>exec_threshold</varname> (int) The maximum number of microseconds that a mongodb query can last. Anything above the threshold will trigger a warning message to the log Default value is 0 ( unlimited - no warnings ). Set <varname>exec_threshold</varname> parameter ... modparam("cachedb_mongodb", "exec_threshold", 100000) ...
Exported Functions The module does not export functions to be used in configuration script.
Raw Query Syntax The cachedb_mongodb module allows to run RAW queries, thus taking full advantage of the capabilities of the back-end. The query syntax is a JSON-based one, very similar to the one that one runs commands in the mongo cli. The query results are also returned as JSON documents, that one can further process in the OpenSIPS script by using the JSON module. The syntax looks like the following : Mongo Raw Query Syntax ... cache_raw_query("mongodb","{ \"op\" : \"desiredOP\", \"ns\" : \"db.collection\", \"query\": {\"_id\" : $rU} }","$avp(mongo_result)"); ... The \"op\" JSON entry specifies the actual operation ( find,update,remove, etc ) that you want to run on the Mongo server and the \"ns\" entry specifies the namespace where you want to run that query. The \"ns\" entry can be missing, and the query will be run on the db.collection that was passed at Connection time in the cachedb_url. The \"query\" entry is the JSON document that specifies the raw query that you want to run. The last parameter is an optional AVP parameter, where the JSON documents will be returned ( a find query can return multiple JSON documents, which are correctly populated in the output AVP ). If the query does not return a result, you can ommit the last parameter. The currently suported operations that you can pass in the \"op\" JSON entry are find - the find operation takes an optional \"fields\" JSON document, specifying the desired fields that should be returned from the mongoDB server. update - the update operations takes an optional \"match\" JSON document, specifying that only the mongoDB documents that match this JSON will be updated ( similar to the 'where' SQL clause ) . If no \"match\" is provided, all documents will be updated. insert remove count Here are a couple examples of running some mongoDB queries : Mongo Raw Query Examples ... /* find documents where my_key has the value 345, and return just the entry1 and entry2 values from the matching documents */ cache_raw_query("mongodb","{ \"op\" : \"find\", \"ns\" : \"my_db.my_col\", \"query\": {\"my_key\" : 345},\"fields\": { \"entry1\" : 1, \"entry2\" : 1 } }","$avp(mongo_result)"); $var(it) = 0; while ($(avp(mongo_result)[$var(it)]) != NULL) { $json(json_res) := $(avp(mongo_result)[$var(it)]); xlog("Fetched a new mongo result=$json(json_res). entry1=$json(json_res/entry1) \n"); $var(it) = $var(it) + 1; $json(json_res) = NULL; } ... /* insert the {_id: 993, vlad: "opensips",counter: 9000 } document */ cache_raw_query("mongodb","{ \"op\" : \"insert\", \"ns\" : \"some_db.coll\", \"query\": {\"_id\" : 993, \"vlad\" : \"opensips\", \"counter\": 9000} }"); ... /* remove all the documents where "mykey" has the "badvalue" */ cache_raw_query("mongodb","{ \"op\" : \"remove\", \"ns\" : \"db.coll\", \"query\": {\"mykey\" : "badvalue"} }"); ... /* set expired to have the value of 1 in the documents where the counter has the 345 value */ cache_raw_query("mongodb","{ \"op\" : \"update\", \"query\": { \"expired\" : 1}, \"match\" : { \"counter\" : 345 } }"); ... cache_raw_query("mongodb","{ \"op\" : \"count\",\"query\": { \"username\" : $rU} }","$avp(mongo_count_result)"); xlog("We have $avp(mongo_count_result) documents where username = $rU \n"); ...
opensips-2.2.2/modules/cachedb_redis/000077500000000000000000000000001300170765700175725ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_redis/Makefile000066400000000000000000000004731300170765700212360ustar00rootroot00000000000000# $Id: Makefile 6253 2009-10-12 19:00:53Z bogdan_iancu $ # # Presence Agent # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=cachedb_redis.so DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -lhiredis include ../../Makefile.modules opensips-2.2.2/modules/cachedb_redis/README000066400000000000000000000121451300170765700204550ustar00rootroot00000000000000cachedb_redis Module Vladut-Stefan Paiu OpenSIPS Solutions Edited by Vladut-Stefan Paiu Copyright © 2011 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Advantages 1.3. Limitations 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. cachedb_url (string) 1.5.2. connect_timeout (integer) 1.5.3. query_timeout (integer) 1.6. Exported Functions 1.7. Raw Query Syntax List of Examples 1.1. Set cachedb_url parameter 1.2. Use Redis servers 1.3. Set connect_timeout parameter 1.4. Set connect_timeout parameter 1.5. Redis Raw Query Examples Chapter 1. Admin Guide 1.1. Overview This module is an implementation of a cache system designed to work with a Redis server. It uses hiredis client library to connect to either a single Redis server instance, or to a Redis Server inside a Redis Cluster. It uses the Key-Value interface exported from the core. 1.2. Advantages * memory costs are no longer on the server * many servers can be used inside a cluster, so the memory is virtually unlimited * the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The Redis DB is also persistent so it can also be restarted without loss of information. * redis is an open-source project so it can be used to exchange data with various other applications * By creating a Redis Cluster, multiple OpenSIPS instances can easily share key-value information 1.3. Limitations * keys (in key:value pairs) may not contain spaces or control characters 1.4. Dependencies 1.4.1. OpenSIPS Modules None. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * hiredis: On the latest Debian based distributions, hiredis can be installed by running 'apt-get install libhiredis-dev' Alternatively, if hiredis is not available on your OS repos, hiredis can be downloaded from: https://github.com/antirez/hiredis . Download the archive, extract sources, run make,sudo make install. 1.5. Exported Parameters 1.5.1. cachedb_url (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. Example 1.1. Set cachedb_url parameter ... modparam("cachedb_redis", "cachedb_url","redis:group1://localhost:6379/" ) modparam("cachedb_redis", "cachedb_url","redis:cluster1://random_url:888 8/") ... Example 1.2. Use Redis servers ... cache_store("redis:group1","key","$ru value"); cache_fetch("redis:cluster1","key",$avp(10)); cache_remove("redis:cluster1","key"); ... 1.5.2. connect_timeout (integer) This parameter specifies how many milliseconds OpenSIPS should wait for connecting to a Redis node. Default value is “5000 msâ€. Example 1.3. Set connect_timeout parameter ... # wait 1 seconds for Redis to connect modparam("cachedb_redis", "connect_timeout",1000) ... 1.5.3. query_timeout (integer) This parameter specifies how many milliseconds OpenSIPS should wait for a query response from a Redis node. Default value is “5000 msâ€. Example 1.4. Set connect_timeout parameter ... # wait 1 seconds for Redis queries modparam("cachedb_redis", "query_timeout",1000) ... 1.6. Exported Functions The module does not export functions to be used in configuration script. 1.7. Raw Query Syntax The cachedb_redis module allows to run RAW queries, thus taking full advantage of the capabilities of the back-end. The query syntax is the typical REDIS one. Here are a couple examples of running some Redis queries : Example 1.5. Redis Raw Query Examples ... $var(my_hash) = "my_hash_name"; $var(my_key) = "my_key_name"; $var(my_value) = "my_key_value"; cache_raw_query("redis","HSET $var(my_hash) $var(my_key) $var(my _value)"); cache_raw_query("redis","HGET $var(my_hash) $var(my_key)","$avp( result)"); xlog("We have fetched $avp(result) \n"); ... $var(my_hash) = "my_hash_name"; $var(my_key1) = "my_key1_name"; $var(my_key2) = "my_key2_name"; $var(my_value1) = "my_key1_value"; $var(my_value2) = "my_key2_value"; cache_raw_query("redis","HSET $var(my_hash) $var(my_key1) $var(m y_value1)"); cache_raw_query("redis","HSET $var(my_hash) $var(my_key2) $var(m y_value2)"); cache_raw_query("redis","HGETALL $var(my_hash)","$avp(result)"); $var(it) = 0; while ($(avp(result_final)[$var(it)]) != NULL) { xlog("Multiple key reply: - we have fetched $(avp(result _final)[$var(it)]) \n"); $var(it) = $var(it) + 1; } ... opensips-2.2.2/modules/cachedb_redis/cachedb_redis.c000066400000000000000000000075331300170765700225050ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../pt.h" #include "../../cachedb/cachedb.h" #include "cachedb_redis_dbase.h" static int mod_init(void); static int child_init(int); static void destroy(void); static str cache_mod_name = str_init("redis"); struct cachedb_url *redis_script_urls = NULL; int set_connection(unsigned int type, void *val) { return cachedb_store_url(&redis_script_urls,(char *)val); } static param_export_t params[]={ { "connect_timeout", INT_PARAM, &redis_connnection_tout}, { "query_timeout", INT_PARAM, &redis_query_tout }, { "cachedb_url", STR_PARAM|USE_FUNC_PARAM, (void *)&set_connection}, {0,0,0} }; /** module exports */ struct module_exports exports= { "cachedb_redis", /* module name */ MOD_TYPE_CACHEDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function)destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { cachedb_engine cde; LM_NOTICE("initializing module cachedb_redis ...\n"); memset(&cde,0,sizeof(cachedb_engine)); cde.name = cache_mod_name; cde.cdb_func.init = redis_init; cde.cdb_func.destroy = redis_destroy; cde.cdb_func.get = redis_get; cde.cdb_func.get_counter = redis_get_counter; cde.cdb_func.set = redis_set; cde.cdb_func.remove = redis_remove; cde.cdb_func.add = redis_add; cde.cdb_func.sub = redis_sub; cde.cdb_func.raw_query = redis_raw_query; cde.cdb_func.capability = 0; if (register_cachedb(&cde) < 0) { LM_ERR("failed to initialize cachedb_redis\n"); return -1; } return 0; } static int child_init(int rank) { struct cachedb_url *it; cachedb_con *con; if(rank == PROC_MAIN || rank == PROC_TCP_MAIN) { return 0; } for (it = redis_script_urls;it;it=it->next) { LM_DBG("iterating through conns - [%.*s]\n",it->url.len,it->url.s); con = redis_init(&it->url); if (con == NULL) { LM_ERR("failed to open connection\n"); return -1; } if (cachedb_put_connection(&cache_mod_name,con) < 0) { LM_ERR("failed to insert connection\n"); return -1; } } cachedb_free_url(redis_script_urls); return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module cachedb_redis ...\n"); cachedb_end_connections(&cache_mod_name); return; } opensips-2.2.2/modules/cachedb_redis/cachedb_redis_dbase.c000066400000000000000000000412561300170765700236430ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #include "../../dprint.h" #include "cachedb_redis_dbase.h" #include "cachedb_redis_utils.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../cachedb/cachedb.h" #include #include int redis_query_tout = CACHEDB_REDIS_DEFAULT_TIMEOUT; int redis_connnection_tout = CACHEDB_REDIS_DEFAULT_TIMEOUT; redisContext *redis_get_ctx(char *ip, int port) { struct timeval tv; static char warned = 0; redisContext *ctx; if (!redis_connnection_tout) { if (!warned++) LM_WARN("Connecting to redis without timeout might block your server\n"); ctx = redisConnect(ip,port); } else { tv.tv_sec = redis_connnection_tout / 1000; tv.tv_usec = (redis_connnection_tout * 1000) % 1000000; ctx = redisConnectWithTimeout(ip,port,tv); } if (ctx && ctx->err != REDIS_OK) { LM_ERR("failed to open redis connection %s:%hu - %s\n",ip, (unsigned short)port,ctx->errstr); return NULL; } if (redis_query_tout) { tv.tv_sec = redis_query_tout / 1000; tv.tv_usec = (redis_query_tout * 1000) % 1000000; if (redisSetTimeout(ctx, tv) != REDIS_OK) { LM_ERR("Cannot set query timeout to %dms\n", redis_query_tout); return NULL; } } return ctx; } int redis_connect_node(redis_con *con,cluster_node *node) { redisReply *rpl; node->context = redis_get_ctx(node->ip,node->port); if (!node->context) return -1; if (con->id->password) { rpl = redisCommand(node->context,"AUTH %s",con->id->password); if (rpl == NULL || rpl->type == REDIS_REPLY_ERROR) { LM_ERR("failed to auth to redis - %.*s\n", rpl?rpl->len:7,rpl?rpl->str:"FAILURE"); freeReplyObject(rpl); redisFree(node->context); return -1; } LM_DBG("AUTH [password] - %.*s\n",rpl->len,rpl->str); freeReplyObject(rpl); } if ((con->type & REDIS_SINGLE_INSTANCE) && con->id->database) { rpl = redisCommand(node->context,"SELECT %s",con->id->database); if (rpl == NULL || rpl->type == REDIS_REPLY_ERROR) { LM_ERR("failed to select database %s - %.*s\n",con->id->database, rpl?rpl->len:7,rpl?rpl->str:"FAILURE"); freeReplyObject(rpl); redisFree(node->context); return -1; } LM_DBG("SELECT [%s] - %.*s\n",con->id->database,rpl->len,rpl->str); freeReplyObject(rpl); } return 0; } int redis_reconnect_node(redis_con *con,cluster_node *node) { LM_DBG("reconnecting node %s:%d \n",node->ip,node->port); /* close the old connection */ if(node->context) redisFree(node->context); return redis_connect_node(con,node); } int redis_connect(redis_con *con) { redisContext *ctx; redisReply *rpl; cluster_node *it; int len; /* connect to redis DB */ ctx = redis_get_ctx(con->id->host,con->id->port); if (!ctx) return -1; /* auth using password, if any */ if (con->id->password) { rpl = redisCommand(ctx,"AUTH %s",con->id->password); if (rpl == NULL || rpl->type == REDIS_REPLY_ERROR) { LM_ERR("failed to auth to redis - %.*s\n", rpl?rpl->len:7,rpl?rpl->str:"FAILURE"); freeReplyObject(rpl); redisFree(ctx); return -1; } LM_DBG("AUTH [password] - %.*s\n",rpl->len,rpl->str); freeReplyObject(rpl); } rpl = redisCommand(ctx,"CLUSTER NODES"); if (rpl == NULL || rpl->type == REDIS_REPLY_ERROR) { /* single instace mode */ con->type |= REDIS_SINGLE_INSTANCE; len = strlen(con->id->host); con->nodes = pkg_malloc(sizeof(cluster_node) + len + 1); if (con->nodes == NULL) { LM_ERR("no more pkg\n"); freeReplyObject(rpl); redisFree(ctx); return -1; } con->nodes->ip = (char *)(con->nodes + 1); strcpy(con->nodes->ip,con->id->host); con->nodes->port = con->id->port; con->nodes->start_slot = 0; con->nodes->end_slot = 4096; con->nodes->context = NULL; con->nodes->next = NULL; LM_DBG("single instance mode\n"); } else { /* cluster instance mode */ con->type |= REDIS_CLUSTER_INSTANCE; con->slots_assigned = 0; LM_DBG("cluster instance mode\n"); if (build_cluster_nodes(con,rpl->str,rpl->len) < 0) { LM_ERR("failed to parse Redis cluster info\n"); return -1; } } freeReplyObject(rpl); redisFree(ctx); for (it=con->nodes;it;it=it->next) { if (it->end_slot > con->slots_assigned ) con->slots_assigned = it->end_slot; if (redis_connect_node(con,it) < 0) { LM_ERR("failed to init connection \n"); return -1; } } return 0; } redis_con* redis_new_connection(struct cachedb_id* id) { redis_con *con; if (id == NULL) { LM_ERR("null cachedb_id\n"); return 0; } if (id->flags & CACHEDB_ID_MULTIPLE_HOSTS) { LM_ERR("multiple hosts are not supported for redis\n"); return 0; } con = pkg_malloc(sizeof(redis_con)); if (con == NULL) { LM_ERR("no more pkg \n"); return 0; } memset(con,0,sizeof(redis_con)); con->id = id; con->ref = 1; if (redis_connect(con) < 0) { LM_ERR("failed to connect to DB\n"); pkg_free(con); return 0; } return con; } cachedb_con *redis_init(str *url) { return cachedb_do_init(url,(void *)redis_new_connection); } void redis_free_connection(cachedb_pool_con *con) { redis_con * c; LM_DBG("in redis_free_connection\n"); if (!con) return; c = (redis_con *)con; destroy_cluster_nodes(c); pkg_free(c); } void redis_destroy(cachedb_con *con) { LM_DBG("in redis_destroy\n"); cachedb_do_close(con,redis_free_connection); } #define redis_run_command(con,key,fmt,args...) \ do {\ con = (redis_con *)connection->data; \ node = get_redis_connection(con,key); \ if (node == NULL) { \ LM_ERR("Bad cluster configuration\n"); \ return -10; \ } \ if (node->context == NULL) { \ if (redis_reconnect_node(con,node) < 0) { \ return -1; \ } \ } \ for (i=2;i;i--) { \ reply = redisCommand(node->context,fmt,##args); \ if (reply == NULL || reply->type == REDIS_REPLY_ERROR) { \ LM_ERR("Redis operation failure - %p %.*s\n",\ reply,reply?reply->len:7,reply?reply->str:"FAILURE"); \ if (reply) \ freeReplyObject(reply); \ if (node->context->err == REDIS_OK || redis_reconnect_node(con,node) < 0) { \ i = 0; break; \ }\ } else break; \ } \ if (i==0) { \ LM_ERR("giving up on query\n"); \ return -1; \ } \ } while (0) int redis_get(cachedb_con *connection,str *attr,str *val) { redis_con *con; cluster_node *node; redisReply *reply; int i; if (!attr || !val || !connection) { LM_ERR("null parameter\n"); return -1; } redis_run_command(con,attr,"GET %b",attr->s,attr->len); if (reply->type == REDIS_REPLY_NIL || reply->str == NULL || reply->len == 0) { LM_DBG("no such key - %.*s\n",attr->len,attr->s); val->s = NULL; val->len = 0; return -2; } LM_DBG("GET %.*s - %.*s\n",attr->len,attr->s,reply->len,reply->str); val->s = pkg_malloc(reply->len); if (val->s == NULL) { LM_ERR("no more pkg\n"); freeReplyObject(reply); return -1; } memcpy(val->s,reply->str,reply->len); val->len = reply->len; freeReplyObject(reply); return 0; } int redis_set(cachedb_con *connection,str *attr,str *val,int expires) { redis_con *con; cluster_node *node; redisReply *reply; int i; if (!attr || !val || !connection) { LM_ERR("null parameter\n"); return -1; } redis_run_command(con,attr,"SET %b %b",attr->s,attr->len,val->s,val->len); LM_DBG("set %.*s to %.*s - status = %d - %.*s\n",attr->len,attr->s,val->len, val->s,reply->type,reply->len,reply->str); freeReplyObject(reply); if (expires) { redis_run_command(con,attr,"EXPIRE %b %d",attr->s,attr->len,expires); LM_DBG("set %.*s to expire in %d s - %.*s\n",attr->len,attr->s,expires, reply->len,reply->str); freeReplyObject(reply); } return 0; } /* returns 0 in case of successful remove * returns 1 in case of key not existent * return -1 in case of error */ int redis_remove(cachedb_con *connection,str *attr) { redis_con *con; cluster_node *node; redisReply *reply; int ret=0,i; if (!attr || !connection) { LM_ERR("null parameter\n"); return -1; } redis_run_command(con,attr,"DEL %b",attr->s,attr->len); if (reply->integer == 0) { LM_DBG("Key %.*s does not exist in DB\n",attr->len,attr->s); ret = 1; } else LM_DBG("Key %.*s successfully removed\n",attr->len,attr->s); freeReplyObject(reply); return ret; } /* returns the new value of the counter */ int redis_add(cachedb_con *connection,str *attr,int val,int expires,int *new_val) { redis_con *con; cluster_node *node; redisReply *reply; int i; if (!attr || !connection) { LM_ERR("null parameter\n"); return -1; } redis_run_command(con,attr,"INCRBY %b %d",attr->s,attr->len,val); if (new_val) *new_val = reply->integer; freeReplyObject(reply); if (expires) { redis_run_command(con,attr,"EXPIRE %b %d",attr->s,attr->len,expires); LM_DBG("set %.*s to expire in %d s - %.*s\n",attr->len,attr->s,expires, reply->len,reply->str); freeReplyObject(reply); } return 0; } int redis_sub(cachedb_con *connection,str *attr,int val,int expires,int *new_val) { redis_con *con; cluster_node *node; redisReply *reply; int i; if (!attr || !connection) { LM_ERR("null parameter\n"); return -1; } redis_run_command(con,attr,"DECRBY %b %d",attr->s,attr->len,val); if (new_val) *new_val = reply->integer; freeReplyObject(reply); if (expires) { redis_run_command(con,attr,"EXPIRE %b %d",attr->s,attr->len,expires); LM_DBG("set %.*s to expire in %d s - %.*s\n",attr->len,attr->s,expires, reply->len,reply->str); freeReplyObject(reply); } return 0; } int redis_get_counter(cachedb_con *connection,str *attr,int *val) { redis_con *con; cluster_node *node; redisReply *reply; int i,ret; str response; if (!attr || !val || !connection) { LM_ERR("null parameter\n"); return -1; } redis_run_command(con,attr,"GET %b",attr->s,attr->len); if (reply->type == REDIS_REPLY_NIL || reply->str == NULL || reply->len == 0) { LM_DBG("no such key - %.*s\n",attr->len,attr->s); return -2; } LM_DBG("GET %.*s - %.*s\n",attr->len,attr->s,reply->len,reply->str); response.s=reply->str; response.len=reply->len; if (str2sint(&response,&ret) != 0) { LM_ERR("Not a counter \n"); freeReplyObject(reply); return -3; } if (val) *val = ret; freeReplyObject(reply); return 0; } int redis_raw_query_handle_reply(redisReply *reply,cdb_raw_entry ***ret, int expected_kv_no,int *reply_no) { int current_size=0,len,i; /* start with a single returned document */ *ret = pkg_malloc(1 * sizeof(cdb_raw_entry *)); if (*ret == NULL) { LM_ERR("No more PKG mem\n"); goto error; } **ret = pkg_malloc(expected_kv_no * sizeof(cdb_raw_entry)); if (**ret == NULL) { LM_ERR("No more pkg mem\n"); goto error; } switch (reply->type) { case REDIS_REPLY_STRING: (*ret)[current_size][0].val.s.s = pkg_malloc(reply->len); if (! (*ret)[current_size][0].val.s.s ) { LM_ERR("No more pkg \n"); goto error; } memcpy((*ret)[current_size][0].val.s.s,reply->str,reply->len); (*ret)[current_size][0].val.s.len = reply->len; (*ret)[current_size][0].type = CDB_STR; current_size++; break; case REDIS_REPLY_INTEGER: (*ret)[current_size][0].val.n = reply->integer; (*ret)[current_size][0].type = CDB_INT; current_size++; break; case REDIS_REPLY_NIL: (*ret)[current_size][0].type = CDB_NULL; (*ret)[current_size][0].val.s.s = NULL; (*ret)[current_size][0].val.s.len = 0; current_size++; break; case REDIS_REPLY_ARRAY: for (i=0;ielements;i++) { switch (reply->element[i]->type) { case REDIS_REPLY_STRING: case REDIS_REPLY_INTEGER: case REDIS_REPLY_NIL: if (current_size > 0) { *ret = pkg_realloc(*ret,(current_size + 1) * sizeof(cdb_raw_entry *)); if (*ret == NULL) { LM_ERR("No more pkg\n"); goto error; } (*ret)[current_size] = pkg_malloc(expected_kv_no * sizeof(cdb_raw_entry)); if ((*ret)[current_size] == NULL) { LM_ERR("No more pkg\n"); goto error; } } if (reply->element[i]->type == REDIS_REPLY_INTEGER) { (*ret)[current_size][0].val.n = reply->element[i]->integer; (*ret)[current_size][0].type = CDB_INT; } else if (reply->element[i]->type == REDIS_REPLY_NIL) { (*ret)[current_size][0].val.s.s = NULL; (*ret)[current_size][0].val.s.len = 0; (*ret)[current_size][0].type = CDB_NULL; } else { (*ret)[current_size][0].val.s.s = pkg_malloc(reply->element[i]->len); if (! (*ret)[current_size][0].val.s.s ) { LM_ERR("No more pkg \n"); goto error; } memcpy((*ret)[current_size][0].val.s.s,reply->element[i]->str,reply->element[i]->len); (*ret)[current_size][0].val.s.len = reply->element[i]->len; (*ret)[current_size][0].type = CDB_STR; } current_size++; break; default: LM_DBG("Unexpected data type %d found in array - skipping \n",reply->element[i]->type); } } break; } *reply_no = current_size; freeReplyObject(reply); return 1; error: if (*ret) { pkg_free(*ret); for (len = 0;lens == NULL || query_key == NULL) return -1; trim_len(len,p,*attr); q = memchr(p,' ',len); if (q == NULL) { LM_ERR("Malformed Redis RAW query \n"); return -1; } query_key->s = q+1; r = memchr(query_key->s,' ',len - (query_key->s - p)); if (r == NULL) { query_key->len = (p+len) - query_key->s; } else { query_key->len = r-query_key->s; } return 0; } int redis_raw_query_send(cachedb_con *connection,redisReply **reply,cdb_raw_entry ***rpl,int expected_kv_no,int *reply_no,str *attr, ...) { redis_con *con; cluster_node *node; int i,end; va_list ap; str query_key; con = (redis_con *)connection->data; if (redis_raw_query_extract_key(attr,&query_key) < 0) { LM_ERR("Failed to extra Redis raw query key \n"); return -1; } node = get_redis_connection(con,&query_key); if (node == NULL) { LM_ERR("Bad cluster configuration\n"); return -10; } if (node->context == NULL) { if (redis_reconnect_node(con,node) < 0) { return -1; } } va_start(ap,attr); end = attr->s[attr->len]; attr->s[attr->len] = 0; for (i=2;i;i--) { *reply = redisvCommand(node->context,attr->s,ap); if (*reply == NULL || (*reply)->type == REDIS_REPLY_ERROR) { LM_ERR("Redis operation failure - %.*s\n", *reply?(*reply)->len:7,*reply?(*reply)->str:"FAILURE"); if (*reply) freeReplyObject(*reply); if (node->context->err == REDIS_OK || redis_reconnect_node(con,node) < 0) { i = 0; break; } } else break; } va_end(ap); attr->s[attr->len]=end; if (i==0) { LM_ERR("giving up on query\n"); return -1; } return 0; } int redis_raw_query(cachedb_con *connection,str *attr,cdb_raw_entry ***rpl,int expected_kv_no,int *reply_no) { redisReply *reply; if (!attr || !connection) { LM_ERR("null parameter\n"); return -1; } if (redis_raw_query_send(connection,&reply,rpl,expected_kv_no,reply_no,attr) < 0) { LM_ERR("Failed to send query to server \n"); return -1; } switch (reply->type) { case REDIS_REPLY_ERROR: LM_ERR("Error encountered when running Redis raw query [%.*s]\n", attr->len,attr->s); return -1; case REDIS_REPLY_NIL: LM_DBG("Redis raw query [%.*s] failed - no such key\n",attr->len,attr->s); freeReplyObject(reply); return -2; case REDIS_REPLY_STATUS: LM_DBG("Received a status of %.*s from Redis \n",reply->len,reply->str); if (reply_no) *reply_no = 0; freeReplyObject(reply); return 1; default: /* some data arrived - yay */ if (rpl == NULL) { LM_DBG("Received reply type %d but script writer not interested in it \n",reply->type); freeReplyObject(reply); return 1; } return redis_raw_query_handle_reply(reply,rpl,expected_kv_no,reply_no); } return 1; } opensips-2.2.2/modules/cachedb_redis/cachedb_redis_dbase.h000066400000000000000000000045671300170765700236540ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #ifndef CACHEDBREDIS_DBASE_H #define CACHEDBREDIS_DBASE_H #include #include "../../cachedb/cachedb.h" typedef struct cluster_nodes { char *ip; /* ip of this cluster node */ short port; /* port of this cluster node */ unsigned short start_slot; /* first slot for this server */ unsigned short end_slot; /* last slot for this server */ redisContext *context; /* actual connection to this node */ struct cluster_nodes *next; } cluster_node; #define CACHEDB_REDIS_DEFAULT_TIMEOUT 5000 extern int redis_query_tout; extern int redis_connnection_tout; #define REDIS_SINGLE_INSTANCE (1<<0) #define REDIS_CLUSTER_INSTANCE (1<<1) typedef struct { struct cachedb_id *id; unsigned int ref; struct cachedb_pool_con_t *next; int type; /* single node or cluster node */ unsigned short slots_assigned; /* total slots for cluster */ cluster_node *nodes; /* one or more Redis nodes */ } redis_con; cachedb_con* redis_init(str *url); void redis_destroy(cachedb_con *con); int redis_get(cachedb_con *con,str *attr,str *val); int redis_set(cachedb_con *con,str *attr,str *val,int expires); int redis_remove(cachedb_con *con,str *attr); int redis_add(cachedb_con *con,str *attr,int val,int expires,int *new_val); int redis_sub(cachedb_con *con,str *attr,int val,int expires,int *new_val); int redis_get_counter(cachedb_con *connection,str *attr,int *val); int redis_raw_query(cachedb_con *connection,str *attr,cdb_raw_entry ***reply,int expected_kv_no,int *reply_no); #endif /* CACHEDBREDIS_DBASE_H */ opensips-2.2.2/modules/cachedb_redis/cachedb_redis_utils.c000066400000000000000000000225301300170765700237170ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #include "../../dprint.h" #include "cachedb_redis_dbase.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../cachedb/cachedb.h" #include #include #include #define is_valid(p,end) ((p) && (p)<(end)) static const uint16_t crc16tab[256]= { 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 }; uint16_t crc16(const char *buf, int len) { int counter; uint16_t crc = 0; for (counter = 0; counter < len; counter++) crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF]; return crc; } unsigned int redisHash(redis_con *con, str* key) { return crc16(key->s,key->len) & con->slots_assigned; } cluster_node *get_redis_connection(redis_con *con,str *key) { unsigned short hash_slot; cluster_node *it; if (con->type & REDIS_SINGLE_INSTANCE) return con->nodes; else { hash_slot = redisHash(con, key); for (it=con->nodes;it;it=it->next) { if (it->start_slot <= hash_slot && it->end_slot >= hash_slot) return it; } return NULL; } } void destroy_cluster_nodes(redis_con *con) { cluster_node *new,*foo; LM_DBG("destroying cluster %p\n",con); new = con->nodes; while (new) { foo = new->next; redisFree(new->context); pkg_free(new); new = foo; } } struct datavalues { int count; char **redisdata; }; int chkmalloc1 (char *handle) { if ( handle == NULL || handle == 0) { LM_ERR("Error1 while parsing cluster redisdata \n"); return -1; } return 1; } int chkmalloc2 (struct datavalues *handle) { if ( handle == NULL || handle == 0) { LM_ERR("Error2 while parsing cluster redisdata \n"); return -1; } return 1; } int chkmalloc3 (struct datavalues **handle) { if ( handle == NULL || handle == 0) { LM_ERR("Error3 while parsing cluster redisdata \n"); return -1; } return 1; } int chkmalloc4 (char **handle) { if ( handle == NULL || handle == 0) { LM_ERR("Error4 while parsing cluster redisdata \n"); return -1; } return 1; } int explode(char *line, const char *delimeters, struct datavalues **newret) { int counter = 0; char *result = NULL; char *data = NULL; data = pkg_malloc((strlen(line) * sizeof(char)) +1); if (!chkmalloc1(data)) return 0; strcpy(data,line); result = strtok(data, delimeters); while (result != NULL ) { newret[0]->redisdata[counter] = pkg_malloc((strlen(result) * sizeof(char) ) +1 ); if (chkmalloc1(newret[0]->redisdata[counter])) { strcpy(newret[0]->redisdata[counter],result); counter++; result = strtok(NULL, delimeters); } else { return 0; } } newret[0]->count = counter-1; pkg_free(data); return 1; } int build_cluster_nodes(redis_con *con,char *info,int size) { cluster_node *new; const char *delimeters = "\n"; int i = 0, j = 0; int masters = 1, count = 0; char *ip, *block = NULL; unsigned short port,start_slot,end_slot; int len; // Define **pointers for new structures struct datavalues **newret1 = pkg_malloc(sizeof(struct datavalues *)); if (!chkmalloc3(newret1)) goto error; struct datavalues **newret2 = pkg_malloc(sizeof(struct datavalues *)); if (!chkmalloc3(newret2)) goto error; struct datavalues **newret3 = pkg_malloc(sizeof(struct datavalues *)); if (!chkmalloc3(newret3)) goto error; // Allocate space for the structures newret1[0] = pkg_malloc(sizeof(struct datavalues)); if (!chkmalloc2(newret1[0])) goto error; newret2[0] = pkg_malloc(sizeof(struct datavalues)); if (!chkmalloc2(newret2[0])) goto error; newret3[0] = pkg_malloc(sizeof(struct datavalues)); if (!chkmalloc2(newret3[0])) goto error; // Allocate space for data item "redisdata" within the structures newret1[0]->redisdata = pkg_malloc((strlen(info) * sizeof(char)) +1); if (!chkmalloc4(newret1[0]->redisdata)) goto error; newret2[0]->redisdata = pkg_malloc((strlen(info) * sizeof(char)) +1); if (!chkmalloc4(newret2[0]->redisdata)) goto error; newret3[0]->redisdata = pkg_malloc((strlen(info) * sizeof(char)) +1); if (!chkmalloc4(newret3[0]->redisdata)) goto error; // Initialise the counter newret1[0]->count = 0; newret2[0]->count = 0; newret3[0]->count = 0; // Redis really only requires two connections ("myself,master" && one other master) || (at least two masters) // but this will supply info for upto 1000 masters due to current Opensips design (hopefully representing the total hash slots) // will always connect to myself,master strstr(info,"myself,master")?(count = 999):(count = 1000); // Cluster data into Array if (explode(info,delimeters,newret1)) { for (i=0;i<=newret1[0]->count;i++) { if ((strstr(newret1[0]->redisdata[i],"master") && (masters <= count)) || strstr(newret1[0]->redisdata[i],"myself,master")) { start_slot = end_slot = port = 0; ip = NULL; masters++; // Break up the row if (explode(newret1[0]->redisdata[i]," ",newret2)) { for (j=0 ; j <= newret2[0]->count ; j++ ) { if (strstr(newret1[0]->redisdata[i],"myself") && strstr(newret2[0]->redisdata[j],"myself")) { //myself no ip ip = con->id->host; port = con->id->port; if (i==0) masters--; } else { //Get the ip and port of other master if (strstr(newret2[0]->redisdata[j],":") && (strlen(newret2[0]->redisdata[j]) > 5)) { if (explode(newret2[0]->redisdata[j],":",newret3)) { ip = (char *)newret3[0]->redisdata[0]; port = atoi(newret3[0]->redisdata[1]); } else { block = ":parsing ip/port"; goto error;} } } //Get slots if (strstr(newret2[0]->redisdata[j],"-") && (strlen(newret2[0]->redisdata[j]) > 2)) { if (explode(newret2[0]->redisdata[j],"-",newret3)) { start_slot = atoi(newret3[0]->redisdata[0]); end_slot = atoi(newret3[0]->redisdata[1]); } else {block = ":parsing slots"; goto error;} } } } else { block = "row to array"; goto error;} LM_DBG("ip port start end %s %hu %hu %hu\n",ip,port,start_slot,end_slot); if ( ip == NULL || !(port > 0) || (start_slot > end_slot) || !(end_slot > 0) ) {block = ":processing row"; goto error;} len = strlen(ip); new = pkg_malloc(sizeof(cluster_node) + len + 1); if (!new) { LM_ERR("no more pkg\n"); goto error; } memset(new,0,sizeof(cluster_node) + len + 1); new->ip = (char *)(new + 1); strcpy(new->ip,ip); new->port = port; new->start_slot = start_slot; new->end_slot = end_slot; if (con->nodes == NULL) con->nodes = new; else { new->next = con->nodes; con->nodes = new; } } } } else { block = ":initial"; goto error;} pkg_free(newret1); pkg_free(newret2); pkg_free(newret3); return 0; error: LM_ERR("Error while parsing cluster nodes in %s\n",block); destroy_cluster_nodes(con); return -1; } opensips-2.2.2/modules/cachedb_redis/cachedb_redis_utils.h000066400000000000000000000022001300170765700237140ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-xx created (vlad-paiu) */ #ifndef CACHEDB_REDIS_UTILSH #define CACHEDB_REDIS_UTILSH #include "cachedb_redis_dbase.h" int build_cluster_nodes(redis_con *con,char *info,int size); cluster_node *get_redis_connection(redis_con *con,str *key); void destroy_cluster_nodes(redis_con *con); #endif opensips-2.2.2/modules/cachedb_redis/doc/000077500000000000000000000000001300170765700203375ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_redis/doc/cachedb_redis.xml000066400000000000000000000020011300170765700236110ustar00rootroot00000000000000 %docentities; ]> cachedb_redis Module &osipsname; Vladut-Stefan Paiu OpenSIPS Solutions
vladpaiu@opensips.org http://www.opensips.org
Vladut-Stefan Paiu
vladpaiu@opensips.org
2011 &osipssol;
&admin;
opensips-2.2.2/modules/cachedb_redis/doc/cachedb_redis_admin.xml000066400000000000000000000137241300170765700247770ustar00rootroot00000000000000 &adminguide;
Overview This module is an implementation of a cache system designed to work with a Redis server. It uses hiredis client library to connect to either a single Redis server instance, or to a Redis Server inside a Redis Cluster. It uses the Key-Value interface exported from the core.
Advantages memory costs are no longer on the server many servers can be used inside a cluster, so the memory is virtually unlimited the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The Redis DB is also persistent so it can also be restarted without loss of information. redis is an open-source project so it can be used to exchange data with various other applications By creating a Redis Cluster, multiple OpenSIPS instances can easily share key-value information
Limitations keys (in key:value pairs) may not contain spaces or control characters
Dependencies
&osips; Modules None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: hiredis: On the latest Debian based distributions, hiredis can be installed by running 'apt-get install libhiredis-dev' Alternatively, if hiredis is not available on your OS repos, hiredis can be downloaded from: https://github.com/antirez/hiredis . Download the archive, extract sources, run make,sudo make install.
Exported Parameters
<varname>cachedb_url</varname> (string) The urls of the server groups that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. It can be set more than one time. The prefix part of the URL will be the identifier that will be used from the script. Set <varname>cachedb_url</varname> parameter ... modparam("cachedb_redis", "cachedb_url","redis:group1://localhost:6379/") modparam("cachedb_redis", "cachedb_url","redis:cluster1://random_url:8888/") ... Use Redis servers ... cache_store("redis:group1","key","$ru value"); cache_fetch("redis:cluster1","key",$avp(10)); cache_remove("redis:cluster1","key"); ...
<varname>connect_timeout</varname> (integer) This parameter specifies how many milliseconds &osips; should wait for connecting to a Redis node. Default value is 5000 ms. Set <varname>connect_timeout</varname> parameter ... # wait 1 seconds for Redis to connect modparam("cachedb_redis", "connect_timeout",1000) ...
<varname>query_timeout</varname> (integer) This parameter specifies how many milliseconds &osips; should wait for a query response from a Redis node. Default value is 5000 ms. Set <varname>connect_timeout</varname> parameter ... # wait 1 seconds for Redis queries modparam("cachedb_redis", "query_timeout",1000) ...
Exported Functions The module does not export functions to be used in configuration script.
Raw Query Syntax The cachedb_redis module allows to run RAW queries, thus taking full advantage of the capabilities of the back-end. The query syntax is the typical REDIS one. Here are a couple examples of running some Redis queries : Redis Raw Query Examples ... $var(my_hash) = "my_hash_name"; $var(my_key) = "my_key_name"; $var(my_value) = "my_key_value"; cache_raw_query("redis","HSET $var(my_hash) $var(my_key) $var(my_value)"); cache_raw_query("redis","HGET $var(my_hash) $var(my_key)","$avp(result)"); xlog("We have fetched $avp(result) \n"); ... $var(my_hash) = "my_hash_name"; $var(my_key1) = "my_key1_name"; $var(my_key2) = "my_key2_name"; $var(my_value1) = "my_key1_value"; $var(my_value2) = "my_key2_value"; cache_raw_query("redis","HSET $var(my_hash) $var(my_key1) $var(my_value1)"); cache_raw_query("redis","HSET $var(my_hash) $var(my_key2) $var(my_value2)"); cache_raw_query("redis","HGETALL $var(my_hash)","$avp(result)"); $var(it) = 0; while ($(avp(result_final)[$var(it)]) != NULL) { xlog("Multiple key reply: - we have fetched $(avp(result_final)[$var(it)]) \n"); $var(it) = $var(it) + 1; } ...
opensips-2.2.2/modules/cachedb_sql/000077500000000000000000000000001300170765700172635ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_sql/Makefile000066400000000000000000000004001300170765700207150ustar00rootroot00000000000000# $Id: Makefile 6253 2009-10-12 19:00:53Z bogdan_iancu $ # # DB Cache module # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=cachedb_sql.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/cachedb_sql/README000066400000000000000000000107351300170765700201510ustar00rootroot00000000000000cachedb_sql Module Vladut-Stefan Paiu OpenSIPS Solutions Edited by Vladut-Stefan Paiu Copyright © 2013 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Advantages 1.3. Limitations 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. cachedb_url (string) 1.5.2. db_table (string) 1.5.3. key_column (string) 1.5.4. value_column (string) 1.5.5. counter_column (string) 1.5.6. expires_column (string) 1.5.7. cache_clean_period (int) 1.5.8. Exported Functions 2. Frequently Asked Questions List of Examples 1.1. Set db_url parameter 1.2. Usage example 1.3. Set db_url parameter 1.4. Set key_column parameter 1.5. Set value_column parameter 1.6. Set counter_column parameter 1.7. Set expires_column parameter 1.8. Set cache_clean_period parameter Chapter 1. Admin Guide 1.1. Overview This module is an implementation of a cache system designed to work with a regular SQL-based server. It uses the internal DB interface to connect to the back-end, and also implements the Key-Value interface exported from the core. 1.2. Advantages * memory costs are no longer on the server * the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The DB is also persistent so it can also be restarted without loss of information. * Multiple OpenSIPS instances can easily share key-value information via a regular SQL-based database 1.3. Limitations * The module's counter operations ( ADD and SUB ) are currently only supported by MySQL 1.4. Dependencies 1.4.1. OpenSIPS Modules None. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none: 1.5. Exported Parameters 1.5.1. cachedb_url (string) The url of the Database that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. The format to follow is : sql:[conn_id]-dburl The parameter can be set multiple times to create multiple connections accessible from the OpenSIPS script. Example 1.1. Set db_url parameter ... modparam("cachedb_sql", "cachedb_url", "sql:1st-mysql://root:vlad@localh ost/opensips_sql") ... Example 1.2. Usage example ... modparam("cachedb_sql", "cachedb_url", "sql:1st-mysql://root:vlad@localh ost/opensips_sql") modparam("cachedb_sql", "cachedb_url", "sql:2nd-postgres://root:vlad@loc alhost/opensips_pg") ... ... cache_store("sql:1st-mysql","key","$ru value"); cache_store("sql:2nd-postgres","counter","10"); ... 1.5.2. db_table (string) The table of the Database that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. Example 1.3. Set db_url parameter ... modparam("cachedb_sql", "db_table","my_table"); ... 1.5.3. key_column (string) The column where the key will be stored Example 1.4. Set key_column parameter ... modparam("cachedb_sql", "key_column","some_name"); ... 1.5.4. value_column (string) The column where the value will be stored Example 1.5. Set value_column parameter ... modparam("cachedb_sql", "value_column","some_name"); ... 1.5.5. counter_column (string) The column where the counter value will be stored Example 1.6. Set counter_column parameter ... modparam("cachedb_sql", "counter_column","some_name"); ... 1.5.6. expires_column (string) The column where the expires will be stored Example 1.7. Set expires_column parameter ... modparam("cachedb_sql", "expires_column","some_name"); ... 1.5.7. cache_clean_period (int) The interval in seconds at which the expired keys will be removed from the database. Default value is 60 ( seconds ) Example 1.8. Set cache_clean_period parameter ... modparam("cachedb_sql", "cache_clean_period",10); ... 1.5.8. Exported Functions The module does not export functions to be used in configuration script. Chapter 2. Frequently Asked Questions 2.1. What happened with the old “db_url†module parameter? It was replaced with the “cachedb_url†parameter. See the documentation for the usage of the “cachedb_url†parameter. opensips-2.2.2/modules/cachedb_sql/cachedb_sql.c000066400000000000000000000351001300170765700216560ustar00rootroot00000000000000/* * Copyright (C) 2013 Steve Frécinaux * Be IP s.a. http://www.beip.be * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2013-01-xx created (Steve Frécinaux) * 2013-01-xx improved implementation of cachedb (vlad-paiu) * 2014-05-xx full rework of the connection management (vlad-paiu) */ #include #include #include #include #include "../../cachedb/cachedb.h" #include "../../db/db.h" #include "../../timer.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" typedef struct { struct cachedb_id *id; unsigned int ref; struct cachedb_pool_con_t *next; db_con_t* cdb_db_handle; db_func_t cdb_dbf; } cachedbsql_con; #define MAX_RAW_QUERY_SIZE 512 static str cache_mod_name = str_init("sql"); struct cachedb_url *sql_script_urls = NULL; static char query_buf[MAX_RAW_QUERY_SIZE]; static str query_str; static int mod_init(void); static int child_init(int rank); static void destroy(void); #define KEY_COL "keyname" #define KEY_COL_LEN (sizeof(KEY_COL) - 1) #define VALUE_COL "value" #define VALUE_COL_LEN (sizeof(VALUE_COL) - 1) #define COUNTER_VALUE_COL "counter" #define COUNTER_VALUE_COL_LEN (sizeof(COUNTER_VALUE_COL) - 1) #define EXPIRES_COL "expires" #define EXPIRES_COL_LEN (sizeof(EXPIRES_COL) - 1) #define CACHEDB_SQL_TABLE_VERSION 2 static str db_table = str_init("cachedb"); static str key_column = {KEY_COL, KEY_COL_LEN}; static str value_column = {VALUE_COL, VALUE_COL_LEN}; static str counter_column = {COUNTER_VALUE_COL, COUNTER_VALUE_COL_LEN}; static str expires_column = {EXPIRES_COL, EXPIRES_COL_LEN}; static int cache_clean_period = 60; int set_connection(unsigned int type, void *val) { return cachedb_store_url(&sql_script_urls,(char *)val); } static param_export_t params[] = { {"cachedb_url", STR_PARAM|USE_FUNC_PARAM, (void *)&set_connection }, {"db_table", STR_PARAM, &db_table.s }, {"key_column", STR_PARAM, &key_column.s }, {"value_column", STR_PARAM, &value_column.s }, {"counter_column", STR_PARAM, &counter_column.s }, {"expires_column", STR_PARAM, &expires_column.s }, {"cache_clean_period", INT_PARAM, &cache_clean_period }, {0, 0, 0} }; /** module exports */ struct module_exports exports = { "cachedb_sql", /* module name */ MOD_TYPE_CACHEDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ child_init /* per-child init function */ }; #define CACHEDBSQL_DB_DELIMITER '-' cachedbsql_con* dbcache_new_connection(struct cachedb_id* id) { cachedbsql_con *con; str db_url; char *p,*end; int group_name_len,scheme_len; if(id == NULL) { LM_ERR("null db_id\n"); return 0; } if((id->flags & (CACHEDB_ID_NO_URL | CACHEDB_ID_MULTIPLE_HOSTS)) != 0) { LM_ERR("bogus url for local cachedb\n"); return 0; } if (id->group_name == NULL) { LM_ERR("No sql back-end info provided \n"); return 0; } group_name_len = strlen(id->group_name); scheme_len = strlen(id->scheme); db_url.s = id->initial_url + scheme_len + 1; db_url.len = strlen(id->initial_url) - scheme_len - 1; for (p=id->group_name,end=p+group_name_len;pgroup_name) + 1; db_url.len -= (p-id->group_name) + 1; break; } } con = pkg_malloc(sizeof(cachedbsql_con)); if(con == NULL) { LM_ERR("no more pkg\n"); return 0; } memset(con,0,sizeof(cachedbsql_con)); con->id = id; con->ref = 1; if (db_bind_mod(&db_url, &con->cdb_dbf) < 0){ LM_ERR("unable to bind to a database driver\n"); pkg_free(con); return 0; } con->cdb_db_handle = con->cdb_dbf.init(&db_url); if (con->cdb_db_handle == 0) { LM_ERR("Failed to connect to the DB \n"); pkg_free(con); return 0; } if(db_check_table_version(&con->cdb_dbf, con->cdb_db_handle, &db_table, CACHEDB_SQL_TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); con->cdb_dbf.close(con->cdb_db_handle); pkg_free(con); return 0; } return con; } static cachedb_con* dbcache_init(str *url) { return cachedb_do_init(url, (void *)dbcache_new_connection); } void dbcache_free_connection(cachedb_pool_con *con) { cachedbsql_con *c; if (!con) return; c = (cachedbsql_con*)con; c->cdb_dbf.close(c->cdb_db_handle); pkg_free(c); } static void dbcache_destroy(cachedb_con *con) { cachedb_do_close(con, dbcache_free_connection); } #define CACHEDBSQL_CON(c) (((cachedbsql_con*)((c)->data))->cdb_db_handle) #define CACHEDBSQL_FUNC(c) (((cachedbsql_con*)((c)->data))->cdb_dbf) static int dbcache_set(cachedb_con *con, str* attr, str* value, int expires) { db_key_t keys[3]; db_val_t vals[3]; keys[0] = &key_column; keys[1] = &value_column; keys[2] = &expires_column; vals[0].type = DB_STR; vals[0].nul = 0; vals[0].val.str_val.s = attr->s; vals[0].val.str_val.len = attr->len; vals[1].type = DB_STR; vals[1].nul = 0; vals[1].val.str_val.s = value->s; vals[1].val.str_val.len = value->len; vals[2].type = DB_INT; vals[2].nul = 0; if (expires > 0) vals[2].val.int_val = (int)time(NULL) + expires; else vals[2].val.int_val = 0; if (CACHEDBSQL_FUNC(con).use_table(CACHEDBSQL_CON(con), &db_table) < 0) { LM_ERR("sql use_table failed\n"); return -1; } if (CACHEDBSQL_FUNC(con).insert_update(CACHEDBSQL_CON(con), keys, vals, 3) < 0) { LM_ERR("inserting cache entry in db failed\n"); return -1; } return 1; } static int dbcache_get(cachedb_con *con, str* attr, str* res) { db_key_t key; db_val_t val; db_key_t col; db_res_t* db_res = NULL; key = &key_column; val.type = DB_STR; val.nul = 0; val.val.str_val.s = attr->s; val.val.str_val.len = attr->len; col = &value_column; if (CACHEDBSQL_FUNC(con).use_table(CACHEDBSQL_CON(con), &db_table) < 0) { LM_ERR("sql use_table failed\n"); return -1; } if(CACHEDBSQL_FUNC(con).query(CACHEDBSQL_CON(con), &key, NULL, &val, &col, 1, 1, NULL, &db_res) < 0) { LM_ERR("failed to query database\n"); return -1; } if (db_res == NULL || RES_ROW_N(db_res) <= 0 || RES_ROWS(db_res)[0].values[0].nul != 0) { LM_DBG("no value found for keyI\n"); if (db_res != NULL && CACHEDBSQL_FUNC(con).free_result(CACHEDBSQL_CON(con),db_res) < 0) LM_DBG("failed to free result of query\n"); return -2; } switch(RES_ROWS(db_res)[0].values[0].type) { case DB_STRING: res->len = strlen((char*)RES_ROWS(db_res)[0].values[0].val.string_val); res->s = pkg_malloc(res->len + 1); if (res->s == NULL) { LM_ERR("no more pkg\n"); goto out_err; } memcpy(res->s, (char*)RES_ROWS(db_res)[0].values[0].val.string_val, res->len); break; case DB_STR: res->len = RES_ROWS(db_res)[0].values[0].val.str_val.len; res->s = pkg_malloc(res->len + 1); if (res->s == NULL) { LM_ERR("no more pkg\n"); goto out_err; } memcpy(res->s, (char*)RES_ROWS(db_res)[0].values[0].val.str_val.s, res->len); break; case DB_BLOB: res->len = RES_ROWS(db_res)[0].values[0].val.blob_val.len; res->s = pkg_malloc(res->len + 1); if (res->s == NULL) { LM_ERR("no more pkg\n"); goto out_err; } memcpy(res->s, (char*)RES_ROWS(db_res)[0].values[0].val.blob_val.s, res->len); break; default: LM_ERR("unknown type of DB user column\n"); goto out_err; } if (CACHEDBSQL_FUNC(con).free_result(CACHEDBSQL_CON(con), db_res) < 0) LM_DBG("failed to free result of query\n"); return 1; out_err: if (CACHEDBSQL_FUNC(con).free_result(CACHEDBSQL_CON(con), db_res) < 0) LM_DBG("failed to free result of query\n"); return -1; } static int dbcache_remove(cachedb_con *con, str* attr) { db_key_t key; db_val_t val; key = &key_column; val.type = DB_STR; val.nul = 0; val.val.str_val.s = attr->s; val.val.str_val.len = attr->len; if (CACHEDBSQL_FUNC(con).use_table(CACHEDBSQL_CON(con), &db_table) < 0) { LM_ERR("sql use_table failed\n"); return -1; } if (CACHEDBSQL_FUNC(con).delete(CACHEDBSQL_CON(con), &key, 0, &val, 1) < 0) { LM_ERR("deleting from database failed\n"); return -1; } return 0; } static int dbcache_add(cachedb_con *con, str *attr, int val, int expires, int *new_val) { int i; db_res_t* res = NULL; if (expires > 0) expires += (int)time(NULL); else expires = 0; i = snprintf(query_buf, sizeof(query_buf), "insert into %.*s (%.*s, %.*s, %.*s) values ('%.*s', %d, %d)" "on duplicate key update %.*s=%.*s %c %d, %.*s=%d", db_table.len, db_table.s, key_column.len, key_column.s, counter_column.len, counter_column.s, expires_column.len, expires_column.s, attr->len, attr->s, val, expires, counter_column.len, counter_column.s, counter_column.len, counter_column.s, val > 0 ? '+' : '-', val > 0 ? val : -val, expires_column.len, expires_column.s, expires); if(i >= sizeof(query_buf)) { LM_ERR("DB query too long\n"); return -1; } query_str.s = query_buf; query_str.len = i; if(CACHEDBSQL_FUNC(con).raw_query(CACHEDBSQL_CON(con), &query_str, &res) < 0) { LM_ERR("raw_query failed\n"); return -1; } if(res != NULL) CACHEDBSQL_FUNC(con).free_result(CACHEDBSQL_CON(con), res); /* Beware of the race conditions! */ if(new_val) { str val; if (dbcache_get(con, attr, &val) < 0) { LM_ERR("could not get the new value"); return -1; } *new_val = atoi(val.s); pkg_free(val.s); } return 0; } static int dbcache_sub(cachedb_con *con, str *attr, int val, int expires, int *new_val) { return dbcache_add(con, attr, -val, expires, new_val); } static int dbcache_fetch_counter(cachedb_con *con,str *attr,int *ret_val) { db_key_t key; db_val_t val; db_key_t col; db_res_t* db_res = NULL; key = &key_column; val.type = DB_STR; val.nul = 0; val.val.str_val.s = attr->s; val.val.str_val.len = attr->len; col = &counter_column; if (CACHEDBSQL_FUNC(con).use_table(CACHEDBSQL_CON(con), &db_table) < 0) { LM_ERR("sql use_table failed\n"); return -1; } if(CACHEDBSQL_FUNC(con).query(CACHEDBSQL_CON(con), &key, NULL, &val, &col, 1, 1, NULL, &db_res) < 0) { LM_ERR("failed to query database\n"); return -1; } if (db_res == NULL || RES_ROW_N(db_res) <= 0 || RES_ROWS(db_res)[0].values[0].nul != 0) { LM_DBG("no value found for keyI\n"); if (db_res != NULL && CACHEDBSQL_FUNC(con).free_result(CACHEDBSQL_CON(con), db_res) < 0) LM_DBG("failed to free result of query\n"); return -2; } switch(RES_ROWS(db_res)[0].values[0].type) { case DB_INT: if (ret_val) *ret_val = RES_ROWS(db_res)[0].values[0].val.int_val; if (CACHEDBSQL_FUNC(con).free_result(CACHEDBSQL_CON(con), db_res) < 0) LM_ERR("failed to freeing result of query\n"); break; default: LM_ERR("unknown type of DB user column\n"); if (db_res != NULL && CACHEDBSQL_FUNC(con).free_result(CACHEDBSQL_CON(con), db_res) < 0) LM_ERR("failed to freeing result of query\n"); return -1; } return 1; } static void dbcache_clean(unsigned int ticks, void* param) { cachedb_pool_con **lst; cachedbsql_con *c; int size=0,i; db_key_t keys[2]; db_op_t ops[2]; db_val_t vals[2]; keys[0] = &expires_column; keys[1] = &expires_column; ops[0] = OP_NEQ; ops[1] = OP_LT; vals[0].type = DB_INT; vals[0].nul = 0; vals[0].val.int_val = 0; vals[1].type = DB_INT; vals[1].nul = 0; vals[1].val.int_val = (int)time(NULL); lst = filter_pool_by_scheme(&cache_mod_name,&size); for (i=0;icdb_dbf.use_table(c->cdb_db_handle, &db_table) < 0) { LM_ERR("sql use_table failed\n"); return; } if (c->cdb_dbf.delete(c->cdb_db_handle, keys, ops, vals, 2) < 0) { LM_ERR("deleting from database failed\n"); return; } } if (lst) pkg_free(lst); } /** * init module function */ static int mod_init(void) { cachedb_engine cde; LM_INFO("initializing module cachedb_sql...\n"); db_table.len = strlen(db_table.s); key_column.len = strlen(key_column.s); value_column.len = strlen(value_column.s); counter_column.len = strlen(counter_column.s); expires_column.len = strlen(expires_column.s); /* register the cache system */ cde.name = cache_mod_name; cde.cdb_func.init = dbcache_init; cde.cdb_func.destroy = dbcache_destroy; cde.cdb_func.get = dbcache_get; cde.cdb_func.set = dbcache_set; cde.cdb_func.remove = dbcache_remove; cde.cdb_func.add = dbcache_add; cde.cdb_func.sub = dbcache_sub; cde.cdb_func.get_counter = dbcache_fetch_counter; cde.cdb_func.capability = 0; if(cache_clean_period <= 0) { LM_ERR("wrong parameter cache_clean_period - need a positive value\n"); return -1; } if(register_cachedb(&cde) < 0) { LM_ERR("failed to register to core memory store interface\n"); return -1; } /* register timer to delete the expired entries */ register_timer("cachedb_sql",dbcache_clean, 0, cache_clean_period, TIMER_FLAG_DELAY_ON_DELAY); return 0; } /** * Initialize children */ static int child_init(int rank) { struct cachedb_url *it; cachedb_con *con; for (it = sql_script_urls;it;it=it->next) { LM_DBG("iterating through conns - [%.*s]\n",it->url.len,it->url.s); con = dbcache_init(&it->url); if (con == NULL) { LM_ERR("failed to open connection\n"); return -1; } if (cachedb_put_connection(&cache_mod_name,con) < 0) { LM_ERR("failed to insert connection\n"); return -1; } } cachedb_free_url(sql_script_urls); return 0; } /* * destroy function */ static void destroy(void) { cachedb_end_connections(&cache_mod_name); } opensips-2.2.2/modules/cachedb_sql/doc/000077500000000000000000000000001300170765700200305ustar00rootroot00000000000000opensips-2.2.2/modules/cachedb_sql/doc/cachedb_sql.xml000066400000000000000000000020571300170765700230060ustar00rootroot00000000000000 %docentities; ]> cachedb_sql Module &osipsname; Vladut-Stefan Paiu OpenSIPS Solutions
vladpaiu@opensips.org http://www.opensips.org
Vladut-Stefan Paiu
vladpaiu@opensips.org
2013 &osipssol;
&admin; &faq;
opensips-2.2.2/modules/cachedb_sql/doc/cachedb_sql_admin.xml000066400000000000000000000125351300170765700241600ustar00rootroot00000000000000 &adminguide;
Overview This module is an implementation of a cache system designed to work with a regular SQL-based server. It uses the internal DB interface to connect to the back-end, and also implements the Key-Value interface exported from the core.
Advantages memory costs are no longer on the server the cache is 100% persistent. A restart of OpenSIPS server will not affect the DB. The DB is also persistent so it can also be restarted without loss of information. Multiple OpenSIPS instances can easily share key-value information via a regular SQL-based database
Limitations The module's counter operations ( ADD and SUB ) are currently only supported by MySQL
Dependencies
&osips; Modules None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none:
Exported Parameters
<varname>cachedb_url</varname> (string) The url of the Database that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. The format to follow is : sql:[conn_id]-dburl The parameter can be set multiple times to create multiple connections accessible from the OpenSIPS script. Set <varname>db_url</varname> parameter ... modparam("cachedb_sql", "cachedb_url", "sql:1st-mysql://root:vlad@localhost/opensips_sql") ... Usage example ... modparam("cachedb_sql", "cachedb_url", "sql:1st-mysql://root:vlad@localhost/opensips_sql") modparam("cachedb_sql", "cachedb_url", "sql:2nd-postgres://root:vlad@localhost/opensips_pg") ... ... cache_store("sql:1st-mysql","key","$ru value"); cache_store("sql:2nd-postgres","counter","10"); ...
<varname>db_table</varname> (string) The table of the Database that OpenSIPS will connect to in order to use the from script cache_store,cache_fetch, etc operations. Set <varname>db_url</varname> parameter ... modparam("cachedb_sql", "db_table","my_table"); ...
<varname>key_column</varname> (string) The column where the key will be stored Set <varname>key_column</varname> parameter ... modparam("cachedb_sql", "key_column","some_name"); ...
<varname>value_column</varname> (string) The column where the value will be stored Set <varname>value_column</varname> parameter ... modparam("cachedb_sql", "value_column","some_name"); ...
<varname>counter_column</varname> (string) The column where the counter value will be stored Set <varname>counter_column</varname> parameter ... modparam("cachedb_sql", "counter_column","some_name"); ...
<varname>expires_column</varname> (string) The column where the expires will be stored Set <varname>expires_column</varname> parameter ... modparam("cachedb_sql", "expires_column","some_name"); ...
<varname>cache_clean_period</varname> (int) The interval in seconds at which the expired keys will be removed from the database. Default value is 60 ( seconds ) Set <varname>cache_clean_period</varname> parameter ... modparam("cachedb_sql", "cache_clean_period",10); ...
Exported Functions The module does not export functions to be used in configuration script.
opensips-2.2.2/modules/cachedb_sql/doc/cachedb_sql_faq.xml000066400000000000000000000007051300170765700236330ustar00rootroot00000000000000 &faqguide; What happened with the old db_url module parameter? It was replaced with the cachedb_url parameter. See the documentation for the usage of the cachedb_url parameter. opensips-2.2.2/modules/call_center/000077500000000000000000000000001300170765700173065ustar00rootroot00000000000000opensips-2.2.2/modules/call_center/Makefile000077500000000000000000000002611300170765700207500ustar00rootroot00000000000000# # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=call_center.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/call_center/README000066400000000000000000000346121300170765700201740ustar00rootroot00000000000000Call-Center Module Bogdan-Andrei Iancu Edited by Bogdan-Andrei Iancu Copyright © 2014 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How it works 1.2.1. DB tables 1.2.2. Call Flows 1.2.3. Agents 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. db_url (string) 1.4.2. acc_db_url (string) 1.4.3. b2b_scenario (string) 1.4.4. wrapup_time (integer) 1.5. Exported Functions 1.5.1. cc_handle_call(flowID) 1.5.2. cc_agent_login(agentID, state) 1.6. Exported Statistics 1.6.1. Global statistics 1.6.2. Per-flow statistics (one set for each flow) 1.6.3. Per-agent statistics (one set for each agent) 1.7. Exported MI Functions 1.7.1. cc_reload 1.7.2. cc_agent_login 1.7.3. cc_list_queue 1.7.4. cc_list_flows 1.7.5. cc_list_agents 1.7.6. cc_list_calls 1.7.7. cc_reset_stats 1.8. Exported pseudo-variables 2. Developer Guide 2.1. Available Functions 3. Frequently Asked Questions List of Examples 1.1. Set db_url parameter 1.2. Set acc_db_url parameter 1.3. Set b2b_scenario parameter 1.4. Set wrapup_time parameter 1.5. cc_handle_call usage 1.6. cc_agent_login usage Chapter 1. Admin Guide 1.1. Overview The Call Center module implements an inbound call center system with call flows (for queuing the received calls) and agents (for answering the calls). The module implements the queuing system, the call distribution to agents, agents managements, CDRs for the calls, statistics on call distribution and agent's activity - basically everything except the media playback (for the queue). This part must be provided via a third party media server (FreeSwitch, Asterisk or others). 1.2. How it works The main entities in the modules are the flows (queues) and agents. 1.2.1. DB tables Each entity has a corresponding table in the database, for provisioning purposes - the cc_flows and cc_agents tables, see DB schema. Data is loaded at startup and cached into memory ; runtime reload is possible via the MI commands (see the cc_reload command in Section 1.7, “Exported MI Functionsâ€). Additionally there is a table cc_cdrs for writing the CDRs - this operation is done in realtime, after the call in completed, covering all possible cases: call was dropped while in queue, call was rejected by agent, call was accepted by agent, call terminated with error - NOTE that a call may generate more than one CDR (like call rejected by agent A, and redistributed and accepted by agent B). The cc_calls table is used to store ongoing calls, regardless it's state (in queue, to the agent, ended). It is populated at runtime by the module and queried at startup. This table should not be manually provisioned. 1.2.2. Call Flows A flow is defined by a unique alphanumerical ID - the main attribute of a flow is the skill - the skill is a capability required by the flow for an agent to be able to answer the call ; the concept of skills is the link between the flows and the agents - telling what agents are serving what flows - the flows require a skill, while the agents provide a set of skills. Agents matching the required skill of a flow will automatically receive calls from that flow. Additional, the flow has a priority - as agents may server multiple flows in the same time (based on skills), you can define priorities between the flows - if the flows has a higher priority, its calls will be pushed (in deliver to agents and queuing) in front of the calls from flows with a lower priority. Optionally, the flow may define a prependcid - a prefix to be added to the CLI (Caller ID) when the call is delivered to the agents - as an agent may receive call from multiple flows, it is important for the user to see which was the queue a call was received. In terms of media announcements, the flow defines the message_welcome (optional, to be played in the call, before doing anything with the call) and message_queue (mandatory, the looping message providing infinite on hold media IMPORTANT - this message must cycle and media server must never hung up on it. Both announcements are provided as SIP URIs (where the call has to be sent in order to get the playback). 1.2.3. Agents An agent is defined by a unique alphanumerical ID - the main attribute of an agent is its the set of skills and its SIP location. The set of skills will tell what calls to be received (from which flows, based on the skill matching); the location is a SIP URI where to call must be sent in order to be answered by the agent. Additionally, the agent has a initial logstate - if he is logged in or not (being logged in is a must in order to receive calls). The log state may be changed at runtime via a dedicated MI command cc_agent_login, see Section 1.7, “Exported MI Functionsâ€. There is a wrapup_time defined, saying the time interval for an agent before getting a new call from the system (after he finished a call). 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * b2b_logic - B2bUA module * database - one of the SQL DB modules 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.4. Exported Parameters 1.4.1. db_url (string) SQL address to the DB server -- database specific. This must be the Database holding the provisioning tables (cc_flows, cc_agents and cc_calls tables). If not explicitly set, the global OpenSIPS DB URL will be used. Example 1.1. Set db_url parameter ... modparam("call_center", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") ... 1.4.2. acc_db_url (string) SQL address to the DB server -- database specific. This must be the Database where the CDRs table (cc_cdrs) is located. If not explicitly set, the global OpenSIPS DB URL will be used. Example 1.2. Set acc_db_url parameter ... modparam("call_center", "acc_db_url", "mysql://opensips:opensipsrw@localhost/opensips_cdrs") ... 1.4.3. b2b_scenario (string) The name of the B2B scenario that is used by the module for handling the calls in the queue. This is an advanced options and you should not change it unless you really understand what you are doing. The module provides an B2B scenario file scenario_callcenter.xml located in the module directory. The name of this scenario from this file (which must be loaded via the b2b_logic module) must match the b2b_scenario parameter. Default value is “call centerâ€. Example 1.3. Set b2b_scenario parameter ... modparam("b2b_logic", "script_scenario", "/etc/opensips/scenario_callcen ter.xml") modparam("call_center", "b2b_scenario", "call center") ... 1.4.4. wrapup_time (integer) Time for an agent between finishing a call and receiving the next call from the system. Even if there are queued calls, the module will not deliver call to agent during this wrapup interval. Default value is “30 secondsâ€. Example 1.4. Set wrapup_time parameter ... modparam("call_center", "wrapup_time", 45) ... 1.5. Exported Functions 1.5.1. cc_handle_call(flowID) This must be used only for initial INVITE requests - the function pushes the call to be handled by the call center module (via a certain flow/queue). This function can be used from REQUEST_ROUTE. The flowID mandatory parameter is the ID of the flow to handle this call (push the call to that flow). This can be a variable or a static string. The function returns TRUE back to the script if the call was successfully pushed and handled by the Call Center engine. IMPORTANT: you must not do any signaling on the call (reply, relay) after this point. In case of error, FALSE is returned to the script with the following return codes: * -1 - unable to get the flow ID from the parameter; * -2 - unable to parse the FROM URI; * -3 - flow with FlowID not found; * -4 - no agents logged in the flow; * -5 - internal error; Example 1.5. cc_handle_call usage ... if (is_method("INVITE") and !has_totag()) { if (!cc_handle_call("tech_support")) { send_reply("403","Cannot handle call"); exit; } } ... 1.5.2. cc_agent_login(agentID, state) This function sets the login (on or off) state for an agent. This function can be used from REQUEST_ROUTE. The agentID mandatory parameter is the ID of the agent and the state is an integer value giving the new state - 0 means logged off, anything else means logged in. Example 1.6. cc_agent_login usage ... # log off the 'agentX' agent cc_agent_login("agentX","0"); ... 1.6. Exported Statistics 1.6.1. Global statistics 1.6.1.1. ccg_incalls Total number of received calls. (counter type) 1.6.1.2. ccg_awt Global avg. waiting time for calls. (realtime type) 1.6.1.3. ccg_load Global load (across all flows). (realtime type) 1.6.1.4. ccg_distributed_incalls Total number of distributed calls. (counter type) 1.6.1.5. ccg_answered_incalls Total number of calls answered by agents. (counter type) 1.6.1.6. ccg_abandonned_incalls Total number of calls terminated by caller before being answered by agents. (counter type) 1.6.1.7. ccg_onhold_calls Total number of calls in the queues (onhold). (realtime type) 1.6.1.8. ccg_free_agents Total number of free agents (across all flows). (realtime type) 1.6.2. Per-flow statistics (one set for each flow) 1.6.2.1. ccf_incalls_flowID Number of received calls for the flow. (counter type) 1.6.2.2. ccf_dist_incalls_flowID Number of distributed calls in this flow. (counter type) 1.6.2.3. ccf_answ_incalls_flowID Nnumber of calls from the flow answered by agents. (counter type) 1.6.2.4. ccf_aban_incalls_flowID Number of calls (from the flow) terminated by caller before being answered by agents. (counter type) 1.6.2.5. ccf_onhold_incalls_flowID Number of calls (from the flow) which were put onhold. (counter type) 1.6.2.6. ccf_queued_calls_flowID Number of calls which are queued for this flow. (realtime type) 1.6.2.7. ccf_free_agents_flowID Number of free agents serving this flow. (realtime type) 1.6.2.8. ccf_etw_flowID Estimated Time to Wait for this flow. (realtime type) 1.6.2.9. ccf_awt_flowID Avg. Wating Time for this flow. (realtime type) 1.6.2.10. ccg_load_flowID The load on the flow (number of queued calls versus number of logged agents). (realtime type) 1.6.3. Per-agent statistics (one set for each agent) 1.6.3.1. cca_dist_incalls_agnetID Number of distributed calls to this agent. (counter type) 1.6.3.2. cca_answ_incalls_agentID Nnumber of calls answered by the agent. (counter type) 1.6.3.3. cca_aban_incalls_agentID Number of calls (sent to this agent) terminated by caller before being answered by agents. (counter type) 1.6.3.4. cca_att_agentID Avg. Talk Time for this agent (realtime type) 1.7. Exported MI Functions 1.7.1. cc_reload Command to reload flows and agents definition from database. It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_reload 1.7.2. cc_agent_login Command to login an agent into the Call Center engine. It takes two mandatory parameters, the ID of the agent and the new login state (0 - log off, 1 - log in) MI FIFO Command usage: opensipsctl fifo cc_agent_login agentX 0 1.7.3. cc_list_queue Command to list all the calls in queuing - for each call, the following attributes will be printed: the flow of the call, for how long the call is in the queue, the ETW for the call, call priority and the call skill (inherited from the flow). It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_list_queue 1.7.4. cc_list_flows Command to list all the flows - for each flow, the following attributes will be printed: the flow ID, the avg. call duration, how many calls were processed, how many agents are logged, and how many onging calls are. It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_list_flows 1.7.5. cc_list_agents Command to list all the agents - for each agent, the following attributes will be printed: agent ID, agent login state and agent state (free, wrapup, incall). It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_list_agents 1.7.6. cc_list_calls Command to list all the ongoing calls - for each call, the following attributes will be printed: call ID, call state (welcome, queued, toagent, ended), call duration, flow it belongs to, agent serving the call (if any). It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_list_agents 1.7.7. cc_reset_stats Command to reset all counter-like statistics. It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_reset_stats 1.8. Exported pseudo-variables NONE Chapter 2. Developer Guide 2.1. Available Functions NONE Chapter 3. Frequently Asked Questions 3.1. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 3.2. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 3.3. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/call_center/call_center.c000077500000000000000000001112621300170765700217330ustar00rootroot00000000000000/* * call center module - call queuing and distribution * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-03-17 initial version (bogdan) */ #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../timer.h" #include "../../ut.h" #include "../../mod_fix.h" #include "../../locking.h" #include "../../flags.h" #include "../../parser/parse_from.h" #include "../b2b_logic/b2b_load.h" #include "cc_data.h" #include "cc_queue.h" #include "cc_db.h" /* db stuff */ static str db_url = {NULL, 0}; static str acc_db_url = {NULL, 0};; /* internal data (agents, flows) */ static struct cc_data *data=NULL; static str b2b_scenario = {"call center", 0}; /* b2b logic API */ b2bl_api_t b2b_api; static int mod_init(void); static void mod_destroy(void); static int child_init( int rank ); static int mi_child_init(); static struct mi_root* mi_cc_reload(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_cc_list_flows(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_cc_list_queue(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_agent_login(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_cc_list_agents(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_cc_list_calls(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_reset_stats(struct mi_root *cmd_tree, void *param); static int w_handle_call(struct sip_msg *req, char *id); static int w_agent_login(struct sip_msg *req, char *agent, char *state); static void cc_timer_agents(unsigned int ticks, void* param); static void cc_timer_cleanup(unsigned int ticks, void* param); unsigned long stg_awt(unsigned short foo); unsigned long stg_load(unsigned short foo); unsigned long stg_free_agents(unsigned short foo); stat_var *stg_incalls = 0; stat_var *stg_dist_incalls = 0; stat_var *stg_answ_incalls = 0; stat_var *stg_aban_incalls = 0; stat_var *stg_onhold_calls = 0; /* a default of 30 secs wrapup time for agents */ unsigned int wrapup_time = 30; static cmd_export_t cmds[]={ {"cc_handle_call", (cmd_function)w_handle_call, 1, fixup_sgp_null, 0, REQUEST_ROUTE}, {"cc_agent_login", (cmd_function)w_agent_login, 2, fixup_sgp_sgp, 0, REQUEST_ROUTE}, {0,0,0,0,0,0} }; static param_export_t mod_params[]={ { "db_url", STR_PARAM, &db_url.s }, { "acc_db_url", STR_PARAM, &acc_db_url.s }, { "b2b_scenario", STR_PARAM, &b2b_scenario.s }, { "wrapup_time", INT_PARAM, &wrapup_time }, { 0,0,0 } }; static mi_export_t mi_cmds[] = { { "cc_reload", "", mi_cc_reload, MI_NO_INPUT_FLAG, 0, mi_child_init}, { "cc_agent_login", "", mi_agent_login, 0, 0, 0}, { "cc_list_queue", "", mi_cc_list_queue, MI_NO_INPUT_FLAG, 0, 0}, { "cc_list_flows", "", mi_cc_list_flows, MI_NO_INPUT_FLAG, 0, 0}, { "cc_list_agents", "", mi_cc_list_agents,MI_NO_INPUT_FLAG, 0, 0}, { "cc_list_calls", "", mi_cc_list_calls, MI_NO_INPUT_FLAG, 0, 0}, { "cc_reset_stats", "", mi_reset_stats, MI_NO_INPUT_FLAG, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static stat_export_t mod_stats[] = { {"ccg_incalls", 0, &stg_incalls }, {"ccg_awt", STAT_IS_FUNC, (stat_var**)stg_awt }, {"ccg_load", STAT_IS_FUNC, (stat_var**)stg_load }, {"ccg_distributed_incalls", 0, &stg_dist_incalls }, {"ccg_answered_incalls" , 0, &stg_answ_incalls }, {"ccg_abandonned_incalls" , 0, &stg_aban_incalls }, {"ccg_onhold_calls", STAT_NO_RESET, &stg_onhold_calls }, {"ccg_free_agents", STAT_IS_FUNC, (stat_var**)stg_free_agents }, {0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "b2b_logic", DEP_ABORT }, { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "call_center", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ mod_params, /* param exports */ mod_stats, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, child_init /* per-child init function */ }; unsigned long stg_awt(unsigned short foo) { return data->avt_waittime; } unsigned long stg_load(unsigned short foo) { unsigned int free_ag; unsigned int load; struct cc_agent *agent; lock_get( data->lock ); if (data->logedin_agents==0) { lock_release( data->lock ); return 0; } free_ag = 0; for (agent = data->agents[CC_AG_ONLINE] ; agent ; agent=agent->next) { if (agent->state==CC_AGENT_FREE) free_ag++; } load = 100*( get_stat_val(stg_onhold_calls) + data->logedin_agents - free_ag ) / data->logedin_agents; lock_release( data->lock ); return load; } unsigned long stg_free_agents(unsigned short foo) { struct cc_agent *agent; unsigned int free = 0; lock_get( data->lock ); for (agent = data->agents[CC_AG_ONLINE] ; agent ; agent=agent->next) { if (agent->state==CC_AGENT_FREE) free++; } lock_release( data->lock ); return free; } unsigned long cc_flow_free_agents( void *flow) { struct cc_agent *agent; unsigned int free = 0; unsigned int i; lock_get( data->lock ); for (agent = data->agents[CC_AG_ONLINE] ; agent ; agent=agent->next) { if (agent->state==CC_AGENT_FREE) { /* iterate all skills of the agent */ for( i=0 ; ino_skills ; i++) { if (agent->skills[i]==((struct cc_flow*)flow)->skill) free++; } } } lock_release( data->lock ); return free; } static int mod_init(void) { LM_INFO("Call Center module - initializing\n"); init_db_url( db_url , 0 /*cannot be null*/); init_db_url( acc_db_url , 0 /*cannot be null*/); b2b_scenario.len = strlen(b2b_scenario.s); /* Load B2BUA API */ if (load_b2b_logic_api( &b2b_api) != 0) { LM_ERR("Can't load B2B-UA hooks, missing 'b2b_logic' module ?\n"); return -1; } if (register_timer( "cc_agents", cc_timer_agents, NULL, 1, TIMER_FLAG_DELAY_ON_DELAY)<0) { LM_ERR("failed to register agents timer function\n"); return -1; } if (register_timer( "cc_cleanup", cc_timer_cleanup, NULL, 5, TIMER_FLAG_DELAY_ON_DELAY)<0) { LM_ERR("failed to register cleaup timer function\n"); return -1; } /* main CC data */ data = init_cc_data(); if (data==0) { LM_CRIT("failed to get shm mem for data\n"); return -1; } /* init and open DB connection */ if (init_cc_db( &db_url )!=0) { LM_ERR("failed to initialize the DB support\n"); return -1; } if (init_cc_acc_db( &acc_db_url )!=0) { LM_ERR("failed to initialize the acc DB support\n"); return -1; } /* load data */ if ( cc_load_db_data( data )!=0 ) { LM_CRIT("failed to load callcenter data\n"); return -1; } clean_cc_old_data(data); /* restore calls */ if ( cc_db_restore_calls( data )!=0 ) { LM_CRIT("failed to load callcenter data\n"); return -1; } /* close DB connection */ cc_close_db(); return 0; } static int child_init( int rank ) { /* init DB connection */ if ( rankavt_waittime_no ++; data->avt_waittime = ( ((float)duration + (data->avt_waittime * (float)(data->avt_waittime_no-1))) ) / (float)data->avt_waittime_no; } static void terminate_call(struct cc_call *call, b2bl_dlg_stat_t* stat, call_state prev_state) { str un, fid, aid; int type; if(prev_state == CC_CALL_ENDED) { LM_CRIT("BUG - terminate state \n"); return; } LM_DBG("terminating call %p (stat=%p)\n",call,stat); lock_get( data->lock ); prepare_cdr( call, &un, &fid , &aid); if (prev_state==CC_CALL_TOAGENT) { /* free the agent */ if (stat && stat->call_time && prev_state==CC_CALL_TOAGENT) { call->agent->state = CC_AGENT_WRAPUP; call->agent->last_call_end = get_ticks(); call->flow->processed_calls ++; call->flow->avg_call_duration = ( ((float)stat->call_time + ((float)call->flow->avg_call_duration * (call->flow->processed_calls-1)) ) ) / call->flow->processed_calls ; /* update awt for established calls */ update_awt( stat->start_time - call->recv_time ); update_cc_flow_awt(call->flow, stat->start_time - call->recv_time); update_cc_agent_att(call->agent, stat->call_time); } else { call->agent->state = CC_AGENT_FREE; /* update awt for failed calls */ update_awt( get_ticks() - call->recv_time ); update_cc_flow_awt( call->flow, get_ticks() - call->recv_time ); } /* update last_call_end for agent */ cc_db_update_agent_end_call(call->agent); call->agent->ref_cnt--; call->agent = NULL; } else { /* update awt for failed calls */ update_awt( get_ticks() - call->recv_time ); update_cc_flow_awt( call->flow, get_ticks() - call->recv_time ); } /* remove the call from queue (if there) */ if ( is_call_in_queue(data, call) ) { cc_queue_rmv_call( data, call); call->ref_cnt--; } call->flow->ongoing_calls--; lock_release( data->lock ); if (call->setup_time==-1 && stat) call->setup_time = stat->setup_time; /* generate CDR */ type = (stat==NULL) ? -1 : ((prev_state==CC_CALL_TOAGENT && stat->call_time)? 0 : 1); cc_write_cdr( &un, &fid, &aid, type, call->recv_time, ((type==0)? stat->start_time : get_ticks()) - call->recv_time , (type==0)?stat->call_time:0 , call->setup_time, call->no_rejections, call->fst_flags, call->id); cc_db_delete_call(call); } int set_call_leg( struct sip_msg *msg, struct cc_call *call, str *new_leg); #define MAX_OUT_BUF_LEN 1024 static char out_buf[MAX_OUT_BUF_LEN]; #define OUT_BUF_LEN(_a) (_aagent->st_aban_incalls, 1); call->no_rejections++; /* put call back into queue */ call->state = CC_CALL_QUEUED; call->setup_time = -1; lock_get( data->lock ); /* prepare CDR */ prepare_cdr( call, &un, &fid , &aid); call->agent->state = CC_AGENT_WRAPUP; call->agent->last_call_end = get_ticks(); /* update last call_end for agent */ cc_db_update_agent_end_call(call->agent); call->agent->ref_cnt--; call->agent = NULL; cc_queue_push_call( data, call, 1/*top*/); if(from_customer || call->prev_state != CC_CALL_QUEUED) { out.len = OUT_BUF_LEN(call->flow->recordings[AUDIO_QUEUE].len); out.s = out_buf; memcpy( out.s, call->flow->recordings[AUDIO_QUEUE].s, out.len); } lock_release( data->lock ); if(from_customer || call->prev_state != CC_CALL_QUEUED) { /* send call to queue */ if (set_call_leg( NULL, call, &out)< 0 ) { LM_ERR("failed to set new destination for call\n"); } LM_DBG("onhold++: agent rejected [%p]\n", call); if(from_customer) { update_stat( stg_onhold_calls, 1); update_stat( call->flow->st_onhold_calls, 1); } } /* write CDR */ cc_write_cdr( &un, &fid, &aid, -2, call->recv_time, get_ticks() - call->recv_time, 0 , pickup_time, call->no_rejections-1, call->fst_flags, call->id); cc_db_update_call(call); } int b2bl_callback_customer(b2bl_cb_params_t *params, unsigned int event) { struct cc_call *call = (struct cc_call*)params->param; str leg = {NULL,0}; call_state cs; int cnt; b2bl_dlg_stat_t* stat = params->stat; LM_DBG(" call (%p) has BYE for %d, \n", call, event); lock_set_get( data->call_locks, call->lock_idx ); cs = call->state; if (event==B2B_DESTROY_CB) { LM_DBG("A delete in b2blogic, call->state=%d, %p\n", call->state, call); call->state = CC_CALL_ENDED; lock_set_release( data->call_locks, call->lock_idx ); if( cs != CC_CALL_ENDED) { /* call terminated due to some error -> cleanup here */ terminate_call( call, NULL, cs); if (cs < CC_CALL_TOAGENT) { LM_DBG("** onhold-- Destroy [%p]\n", call); update_stat( stg_onhold_calls, -1); update_stat( call->flow->st_onhold_calls, -1); } if (cs == CC_CALL_TOAGENT) { /* call no longer on wait */ //update_stat( stg_aban_incalls, 1); /*abandon from agent */ //update_stat( call->agent->st_aban_incalls, 1); } } lock_set_get( data->call_locks, call->lock_idx ); cnt = --call->ref_cnt; lock_set_release( data->call_locks, call->lock_idx ); if (cnt==0) free_cc_call( data, call); else LM_DBG("!!! Call ref not 0 - do not delete %p\n", call); return 0; } if(call->ign_cback) { lock_set_release( data->call_locks, call->lock_idx ); return 2; } if ( event==B2B_BYE_CB ) { if (cs==CC_CALL_TOAGENT && stat->call_time) { /* an established call was terminated */ update_stat( stg_answ_incalls, 1); update_stat( call->flow->st_answ_incalls, 1); call->fst_flags |= FSTAT_ANSW; update_stat( call->agent->st_answ_incalls, 1); } } if (event==B2B_BYE_CB && params->entity==1) { LM_DBG("BYE from the customer\n"); /* external caller terminated the call */ call->state = CC_CALL_ENDED; lock_set_release( data->call_locks, call->lock_idx ); if (csflow->st_onhold_calls, -1); } /* Abandon: client was not sent to agent yet, or call still ringing * on agent side */ if (cscall_time==0) { /*abandon from customer */ update_stat( stg_aban_incalls, 1); update_stat( call->flow->st_aban_incalls, 1); call->fst_flags |= FSTAT_ABAN; } terminate_call(call, stat, cs); /* route the BYE according to scenario */ return 2; } /* if reInvite to the customer failed - end the call */ if(event == B2B_REJECT_CB && params->entity==1) { lock_set_release( data->call_locks, call->lock_idx ); return 1; } if(event == B2B_REJECT_CB && params->entity>1) { if(call->state == CC_CALL_TOAGENT) { handle_agent_reject(call, 1, stat->setup_time); lock_set_release( data->call_locks, call->lock_idx ); return 0; } lock_set_release( data->call_locks, call->lock_idx ); return 1; } if (stat->call_time==0 && call->state == CC_CALL_TOAGENT) { LM_INFO("*** AGENT answered and closed immediately %.*s\n", call->agent->location.len, call->agent->location.s); handle_agent_reject(call, 1, stat->setup_time); lock_set_release( data->call_locks, call->lock_idx ); return 0; } /* right-side leg of call sent BYE -> get next state */ lock_get( data->lock ); if (cc_call_state_machine( data, call, &leg )!=0) { LM_ERR("failed to get next call destination \n"); lock_release( data->lock ); lock_set_release( data->call_locks, call->lock_idx ); /* force BYE to be sent in both parts */ return -1; } lock_release( data->lock ); LM_DBG("new destination for call(%p) is %.*s (state=%d)\n", call, leg.len, leg.s, call->state); if (call->state == CC_CALL_ENDED) { lock_set_release( data->call_locks, call->lock_idx ); terminate_call( call, stat, cs); return 2; } else if (call->state == CC_CALL_TOAGENT) { /* call no longer on wait */ LM_DBG("** onhold-- Direct to agent [%p]\n", call); update_stat( stg_onhold_calls, -1); update_stat( call->flow->st_onhold_calls, -1); } /* send call to selected destination */ if (set_call_leg( NULL, call, &leg)< 0) { LM_ERR("failed to set new destination for call\n"); lock_set_release( data->call_locks, call->lock_idx ); pkg_free(leg.s); return -1; } lock_set_release( data->call_locks, call->lock_idx ); if(cc_db_update_call(call) < 0) { LM_ERR("Failed to update call in database\n"); } pkg_free(leg.s); return 0; } int set_call_leg( struct sip_msg *msg, struct cc_call *call, str *new_leg) { str* id; LM_DBG("call %p moving to %.*s , state %d\n", call, new_leg->len, new_leg->s, call->state); if (call->b2bua_id.len==0) { /* b2b instance not initialized yet => * create new b2bua instance */ call->ref_cnt++; id = b2b_api.init( msg, &b2b_scenario, &new_leg, b2bl_callback_customer, (void*)call, B2B_DESTROY_CB|B2B_REJECT_CB|B2B_BYE_CB, NULL /* custom_hdrs */ ); if (id==NULL || id->len==0 || id->s==NULL) { LM_ERR("failed to init new b2bua call (empty ID received)\n"); return -2; } call->b2bua_id.s = (char*)shm_malloc(id->len); if (call->b2bua_id.s==NULL) { LM_ERR("failed to allocate b2bua ID\n"); return -1; } memcpy( call->b2bua_id.s, id->s, id->len); /* this must be the last, as we use it as marker for checking * if b2b entity is initialized */ call->b2bua_id.len = id->len; } else { /* call already ongoing */ if(b2b_api.bridge( &call->b2bua_id, new_leg, &call->caller_dn, 0) < 0) { LM_ERR("bridging failed\n"); b2b_api.terminate_call(&call->b2bua_id); return -1; } } /* remember last time when the call started */ call->last_start = get_ticks(); //b2b_api.set_state(&call->b2bua_id, call->state); return 0; } static inline str* build_displayname(str *prefix, struct to_body *fh) { static char buf[65]; static str dn; unsigned int l=64; unsigned int n; char *p; str *s; dn.s = p = buf; *(p++) = '"'; l --; n = prefix->len; if (n>=l) n = l; memcpy( p, prefix->s , n); p += n; l -= n; if (l<=0) goto done; *(p++) = ' '; l --; if (l<=0) goto done; if (fh->display.len) { s = &fh->display; if(s->s[0]=='"') { s->s++; s->len-=2; } } else s = &fh->parsed_uri.user; n = s->len; if (n>l) n = l; memcpy( p, s->s , n); p += n; l -= n; done: *(p++) = '"'; dn.len = p-buf; return &dn; } static int w_handle_call(struct sip_msg *msg, char *flow_var) { struct cc_flow *flow; struct cc_call *call; str leg = {NULL,0}; str *dn; str val; int dec; int ret = -1; call = NULL; dec = 0; /* get the flow name */ if (fixup_get_svalue(msg, (gparam_p)flow_var, &val)!=0) { LM_ERR("failed to avaluate the flow name variable\n"); return -1; } /* parse FROM URI */ if (parse_from_uri(msg)==NULL) { LM_ERR("failed to parse from hdr\n"); return -2; } lock_get( data->lock ); /* get the flow ID */ flow = get_flow_by_name(data, &val); if (flow==NULL) { LM_ERR("flow <%.*s> does not exists\n", val.len, val.s); ret = -3; goto error; } LM_DBG("using call flow %p\n", flow); if (flow->logged_agents==0 /* no logged agents */ ) { LM_NOTICE("flow <%.*s> closed\n",flow->id.len,flow->id.s); ret = -4; goto error; } update_stat(stg_incalls, 1); update_stat(flow->st_incalls, 1); if (flow->cid.len) { dn = build_displayname(&flow->cid, get_from(msg)); } else if (get_from(msg)->display.len) { dn = &get_from(msg)->display; } else { dn = &get_from(msg)->parsed_uri.user; } LM_DBG("cid=<%.*s>\n",dn->len,dn->s); call = new_cc_call(data, flow, dn, &get_from(msg)->parsed_uri.user); if (call==NULL) { LM_ERR("failed to create new call\n"); ret = -5; goto error; } call->fst_flags |= FSTAT_INCALL; /* get estimated wait time */ call->eta = (unsigned int) (( flow->avg_call_duration * (float)get_stat_val(flow->st_queued_calls) ) / (float)flow->logged_agents); LM_DBG("avg_call_duration=%.2f queued_calls=%lu logedin_agents=%u\n", flow->avg_call_duration, get_stat_val(flow->st_queued_calls), flow->logged_agents); LM_DBG("ETA for new call(%p) is %d\n", call, call->eta); /* one more call to process */ flow->ongoing_calls++; /* there is no need to lock the call here as it is not * yet sharead at all - just we have a ref to it */ /* get the first state */ if (cc_call_state_machine( data, call, &leg )!=0) { LM_ERR("failed to get first call destination \n"); ret = -5; goto error; } lock_release( data->lock ); LM_DBG("new destination for call(%p) is %.*s (state=%d)\n", call, leg.len, leg.s, call->state); /* call still waits for agent ? */ if (call->state!=CC_CALL_TOAGENT) { LM_DBG("** onhold++ Not to agent [%p]\n", call); update_stat( stg_onhold_calls, +1); update_stat( flow->st_onhold_calls, +1); dec = 1; } /* send call to selected destination */ if (set_call_leg( msg, call, &leg)< 0 ) { LM_ERR("failed to set new destination for call\n"); if (dec) { LM_DBG("** onhold-- Error [%p]\n", call); update_stat( stg_onhold_calls, -1); update_stat( flow->st_onhold_calls, -1); } pkg_free(leg.s); goto error1; } pkg_free(leg.s); if(cc_db_insert_call(call) < 0) { LM_ERR("Failed to insert call record in db\n"); } return 1; error: lock_release( data->lock ); error1: if (call) { free_cc_call( data, call); flow->ongoing_calls--; } return ret; } static int w_agent_login(struct sip_msg *req, char *agent_v, char *state_v) { struct cc_agent *agent, *prev_agent; str agent_s; int state; unsigned int flags; /* get state */ if (fixup_get_isvalue( req, (gparam_p)state_v, &state, &agent_s, &flags)!=0 || ((flags|GPARAM_INT_VALUE_FLAG)==0) ) { LM_ERR("unable to evaluate state spec \n"); return -1; } /* get agent */ if (fixup_get_svalue( req, (gparam_p)agent_v, &agent_s)!=0) { LM_ERR("unable to evaluate agent spec \n"); return -2; } /* block access to data */ lock_get( data->lock ); /* name of the agent */ agent = get_agent_by_name( data, &agent_s, &prev_agent); if (agent==NULL) { lock_release( data->lock ); LM_DBG("agent <%.*s> not found\n",agent_s.len,agent_s.s); return -3; } if (agent->loged_in != state) { if(state && (agent->state==CC_AGENT_WRAPUP) && (get_ticks() - agent->last_call_end > wrapup_time)) agent->state = CC_AGENT_FREE; if(state && data->agents[CC_AG_ONLINE] == NULL) data->last_online_agent = agent; agent_switch_login(data, agent, prev_agent); if(state) { data->logedin_agents++; log_agent_to_flows( data, agent, 1); } else { data->logedin_agents--; log_agent_to_flows(data, agent, 0); } } /* release access to data */ lock_release( data->lock ); return 1; } static void cc_timer_agents(unsigned int ticks, void* param) { struct cc_agent *agent, *prev_agent, *tmp_ag; struct cc_call *call; str out; str dest; if (data==NULL || data->agents[CC_AG_ONLINE]==NULL) return; do { lock_get( data->lock ); prev_agent = data->agents[CC_AG_ONLINE]; agent = data->agents[CC_AG_ONLINE]; call = NULL; /* iterate all agents*/ do { //LM_DBG("%.*s , state=%d, last_call_end=%u, ticks=%u, wrapup=%u\n", // agent->id.len, agent->id.s, agent->state, agent->last_call_end, ticks, wrapup_time); /* for agents in WRAPUP time, check if expired */ if ( (agent->state==CC_AGENT_WRAPUP) && (ticks - agent->last_call_end > wrapup_time)) { agent->state = CC_AGENT_FREE; /* move it to the end of the list*/ if(data->last_online_agent != agent) { remove_cc_agent(data, agent, prev_agent); if(!data->last_online_agent) { LM_CRIT("last_online_agent NULL\n"); if(data->agents[CC_AG_ONLINE] == NULL) data->agents[CC_AG_ONLINE] = agent; else { for (tmp_ag = data->agents[CC_AG_ONLINE]; tmp_ag; tmp_ag= tmp_ag->next) { prev_agent = tmp_ag; } prev_agent->next = agent; agent->next = NULL; data->last_online_agent = agent; } } else { data->last_online_agent->next = agent; agent->next = NULL; data->last_online_agent = agent; } goto next_ag; } } /* for free agents -> check for calls */ if ( (data->queue.calls_no!=0) && (agent->state==CC_AGENT_FREE) ) { call = cc_queue_pop_call_for_agent( data, agent); if (call) { /* found a call for the agent */ break; } } next_ag: /* next agent */ prev_agent = agent; agent = agent->next; }while(agent); lock_release( data->lock ); /* no locking here */ if (call) { lock_set_get( data->call_locks, call->lock_idx ); call->ref_cnt--; /* is the call state still valid? (as queued) */ if(call->state != CC_CALL_QUEUED) { if (call->state==CC_CALL_ENDED && call->ref_cnt==0) { lock_set_release( data->call_locks, call->lock_idx ); free_cc_call( data, call); } else { lock_set_release( data->call_locks, call->lock_idx ); } continue; } LM_DBG("Call %p ref= %d, state= %d\n", call, call->ref_cnt, call->state); lock_get( data->lock ); dest = agent->location; /* make a copy for destination to agent */ out.len = OUT_BUF_LEN(dest.len); out.s = out_buf; memcpy( out.s, dest.s, out.len); call->prev_state = call->state; call->state = CC_CALL_TOAGENT; /* call no longer on wait */ LM_DBG("** onhold-- Took out of the queue [%p]\n", call); update_stat( stg_onhold_calls, -1); update_stat( call->flow->st_onhold_calls, -1); /* mark agent as used */ agent->state = CC_AGENT_INCALL; call->agent = agent; call->agent->ref_cnt++; update_stat( stg_dist_incalls, 1); update_stat( call->flow->st_dist_incalls, 1); call->fst_flags |= FSTAT_DIST; update_stat( call->agent->st_dist_incalls, +1); /* unlock data */ lock_release( data->lock ); /* send call to selected agent */ if (set_call_leg( NULL, call, &out)< 0 ) { LM_ERR("failed to set new destination for call\n"); } lock_set_release( data->call_locks, call->lock_idx ); if(cc_db_update_call(call) < 0) { LM_ERR("Failed to update call in database\n"); } } } while (call); } static void cc_timer_cleanup(unsigned int ticks, void* param) { if (data->old_flows==NULL && data->old_agents==NULL) return; /* block access to data */ lock_get( data->lock ); clean_cc_unref_data(data); /* done with data */ lock_release( data->lock ); } /******************** MI commands ***********************/ static struct mi_root* mi_cc_reload(struct mi_root *cmd_tree, void *param) { int ret; LM_INFO("\"cc_reload\" MI command received!\n"); /* block access to data */ lock_get( data->lock ); /* do the update */ ret = cc_load_db_data( data ); if (ret<0) { LM_CRIT("failed to load CC data\n"); } clean_cc_old_data(data); /* release the readers */ lock_release( data->lock ); if (ret==0) return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); else return init_mi_tree( 500, "Failed to reload",16); } static struct mi_root* mi_cc_list_flows(struct mi_root *cmd, void *param) { struct cc_flow *flow; struct mi_root *rpl_tree; struct mi_node *node; struct mi_node *rpl; struct mi_attr *attr; int len; char *p; rpl_tree = init_mi_tree( 200, MI_SSTR("OK") ); if ( rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; /* block access to data */ lock_get( data->lock ); for( flow=data->flows; flow ; flow=flow->next ) { node = add_mi_node_child( rpl, MI_DUP_VALUE, MI_SSTR("Flow"), flow->id.s, flow->id.len ); if (node==NULL) goto error; p = int2str( (unsigned long)(flow->avg_call_duration), &len); attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Avg Call Duration"), p, len); if (attr==NULL) goto error; p = int2str( (unsigned long)(flow->processed_calls), &len); attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Processed Calls"), p, len); if (attr==NULL) goto error; p = int2str( (unsigned long)(flow->logged_agents), &len); attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Logged Agents"), p, len); if (attr==NULL) goto error; p = int2str( (unsigned long)(flow->ongoing_calls), &len); attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Ongoing Calls"), p, len); if (attr==NULL) goto error; p = int2str( (unsigned long)(flow->ref_cnt), &len); attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Ref"), p, len); if (attr==NULL) goto error; } lock_release( data->lock ); return rpl_tree; error: lock_release( data->lock ); return 0; } static struct mi_root* mi_cc_list_agents(struct mi_root *cmd_tree, void *param) { struct cc_agent *agent; struct mi_root *rpl_tree; struct mi_node *node; struct mi_node *rpl; struct mi_attr *attr; str state; static str s_free={"free", 4}; static str s_wrapup={"wrapup", 6}; static str s_incall={"incall", 6}; char *p; int len; int i; rpl_tree = init_mi_tree( 200, MI_SSTR("OK") ); if ( rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; /* block access to data */ lock_get( data->lock ); for(i=0; i< 2; i++) { for( agent=data->agents[i] ; agent ; agent=agent->next ) { node = add_mi_node_child( rpl, MI_DUP_VALUE, "Agent", 5, agent->id.s, agent->id.len ); if (node==NULL) goto error; p = int2str( (unsigned long)(agent->ref_cnt), &len); attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Ref"), p, len); if (attr==NULL) goto error; if(!agent->loged_in) attr = add_mi_attr( node, MI_DUP_VALUE, "Loged in", 8, "NO", 2); else { attr = add_mi_attr( node, MI_DUP_VALUE, "Loged in", 8, "YES", 3); if (attr==NULL) goto error; switch ( agent->state ) { case CC_AGENT_FREE: state = s_free; break; case CC_AGENT_WRAPUP: state = s_wrapup; break; case CC_AGENT_INCALL: state = s_incall; break; default: state.s =0; state.len = 0; } attr = add_mi_attr( node, MI_DUP_VALUE, "State", 5, state.s, state.len ); } if (attr==NULL) goto error; } } lock_release( data->lock ); return rpl_tree; error: lock_release( data->lock ); return 0; } static struct mi_root* mi_cc_list_calls(struct mi_root *cmd_tree, void *param) { struct cc_call *call; struct cc_agent *agent; struct mi_root *rpl_tree; struct mi_node *node; struct mi_node *rpl; struct mi_attr *attr; char *p; int len; static str call_state[12]= {{"none", 4}, {"welcome", 7}, {"queued", 6}, {"toagent", 7}, {"ended", 5}}; rpl_tree = init_mi_tree( 200, MI_SSTR("OK") ); if ( rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; /* block access to data */ lock_get( data->lock ); for( call=data->list.first ; call ; call=call->next_list ) { node = add_mi_node_child( rpl, MI_DUP_VALUE, "Call", 4, call->b2bua_id.s, call->b2bua_id.len); if (node==NULL) goto error; p = int2str( (unsigned long)(call->ref_cnt), &len); attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Ref"), p, len); if (attr==NULL) goto error; if(call->ign_cback) attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("State"), MI_SSTR("ignored")); else attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("State"), call_state[call->state].s, call_state[call->state].len); if (attr==NULL) goto error; LM_DBG("call->recv_time= %d, ticks= %d\n", call->recv_time, get_ticks()); if(call->state != CC_CALL_ENDED) { p = int2str( (unsigned long)(call->recv_time?(get_ticks() - call->recv_time):0), &len); attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Call Time"), p, len); if (attr==NULL) goto error; if(call->flow) { attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Flow"), call->flow->id.s, call->flow->id.len); if (attr==NULL) goto error; } } if(call->agent) { agent = call->agent; attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("Agent"), agent->id.s, agent->id.len); if (attr==NULL) goto error; } } lock_release( data->lock ); return rpl_tree; error: lock_release( data->lock ); return 0; } /* FORMAT : agent_id log_state */ static struct mi_root* mi_agent_login(struct mi_root *cmd_tree, void *param) { struct mi_node *node; struct cc_agent *agent; unsigned int loged_in; struct cc_agent* prev_agent= 0; node = cmd_tree->node.kids; if (node==NULL || node->next==NULL || node->next->next!=NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* block access to data */ lock_get( data->lock ); /* name of the agent */ agent = get_agent_by_name( data, &node->value, &prev_agent); if (agent==NULL) { lock_release( data->lock ); return init_mi_tree( 404, MI_SSTR("Agent not found") ); } /* login state */ node = node->next; if (str2int( &node->value , &loged_in)!=0 ) { lock_release( data->lock ); return init_mi_tree( 400, MI_SSTR("Bad loged_in state") ); } if (agent->loged_in != loged_in) { if(loged_in && (agent->state==CC_AGENT_WRAPUP) && (get_ticks() - agent->last_call_end > wrapup_time)) agent->state = CC_AGENT_FREE; if(loged_in && data->agents[CC_AG_ONLINE] == NULL) data->last_online_agent = agent; agent_switch_login(data, agent, prev_agent); if(loged_in) { data->logedin_agents++; log_agent_to_flows( data, agent, 1); } else { data->logedin_agents--; log_agent_to_flows(data, agent, 0); } } /* release the readers */ lock_release( data->lock ); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } static struct mi_root* mi_reset_stats(struct mi_root *cmd_tree, void *param) { struct cc_flow *flow; struct cc_agent *agent; int i; /* reset global stats */ reset_stat( stg_incalls) ; data->avt_waittime_no = 0; data->avt_waittime = 0; reset_stat( stg_dist_incalls ); reset_stat( stg_answ_incalls ); reset_stat( stg_aban_incalls ); /* block access to data */ lock_get( data->lock ); /* reset flow stats */ for ( flow = data->flows ; flow ; flow = flow->next ) { reset_stat( flow->st_incalls ); reset_stat( flow->st_dist_incalls ); reset_stat( flow->st_answ_incalls ); reset_stat( flow->st_aban_incalls ); reset_stat( flow->st_onhold_calls ); flow->avg_call_duration = 0; flow->processed_calls = 0; flow->avg_waittime = 0; flow->avg_waittime_no = 0; } /* reset agent stats */ for(i = 0; i< 2; i++) { for ( agent = data->agents[i] ; agent ; agent = agent->next ) { reset_stat( agent->st_dist_incalls ); reset_stat( agent->st_answ_incalls ); reset_stat( agent->st_aban_incalls ); agent->avg_talktime = 0; agent->avg_talktime_no = 0; } } /* release the readers */ lock_release( data->lock ); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } static struct mi_root* mi_cc_list_queue(struct mi_root *cmd_tree, void *param) { struct mi_root *rpl_tree; struct mi_node *node; struct mi_attr *attr; struct mi_node *rpl; struct cc_call *call; unsigned int n, now; char *p; int len; str *s; rpl_tree = init_mi_tree( 200, MI_SSTR("OK") ); if ( rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; n = 0; now = get_ticks(); /* block access to data */ lock_get( data->lock ); for ( call=data->queue.first ; call ; call=call->lower_in_queue, n++) { p = int2str( (unsigned long)n, &len); node = add_mi_node_child( rpl, MI_DUP_VALUE, "Call", 4, p, len); if (node==NULL) goto error; p = int2str( (unsigned long)(now-call->last_start), &len); attr = add_mi_attr( node, MI_DUP_VALUE, "Waiting for", 11, p, len); if (attr==NULL) goto error; p = int2str( (unsigned long)(call->eta), &len); attr = add_mi_attr( node, MI_DUP_VALUE, "ETW", 3, p, len); if (attr==NULL) goto error; /* flow data */ node = add_mi_node_child( node, MI_DUP_VALUE, "Flow", 4, call->flow->id.s, call->flow->id.len); if (node==NULL) goto error; p = int2str( (unsigned long)(call->flow->priority), &len); attr = add_mi_attr( node, MI_DUP_VALUE, "Priority", 8, p, len); if (attr==NULL) goto error; s = get_skill_by_id(data,call->flow->skill); if (s) { attr = add_mi_attr( node, MI_DUP_VALUE, "Skill", 5, s->s, s->len); if (attr==NULL) goto error; } } /* release the readers */ lock_release( data->lock ); return rpl_tree; error: lock_release( data->lock ); free_mi_tree(rpl_tree); return NULL; } opensips-2.2.2/modules/call_center/cc_data.c000077500000000000000000000627411300170765700210450ustar00rootroot00000000000000/* * call center module - call queuing and distributio * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-03-17 initial version (bogdan) */ #include #include "../../mem/shm_mem.h" #include "../../parser/parse_uri.h" #include "../../locking.h" #include "../../ut.h" #include "../../trim.h" #include "../b2b_logic/b2b_load.h" #include "cc_data.h" /* b2b logic API */ extern b2bl_api_t b2b_api; extern unsigned int wrapup_time; static void free_cc_flow( struct cc_flow *flow); static void free_cc_agent( struct cc_agent *agent); unsigned long cc_flow_free_agents( void *flow); unsigned int get_skill_id(struct cc_data *data, str *name) { struct cc_skill *skill; /* search to see if exists */ for ( skill=data->skills_map ; skill ; skill=skill->next ) { if ( (skill->name.len==name->len) && (memcmp(skill->name.s,name->s,name->len)==0) ) return skill->id; } /* none found, allocate a new one */ skill = (struct cc_skill*)shm_malloc( sizeof(struct cc_skill)+name->len ); if (skill==NULL) { LM_ERR("no enough shm mem for a new skill map\n"); return 0; } skill->is_new = 1; skill->name.s = (char*)(skill+1); skill->name.len = name->len; memcpy( skill->name.s , name->s, name->len); skill->id = ++(data->last_skill_id); /* link it */ skill->next = data->skills_map; data->skills_map = skill; return skill->id; } str* get_skill_by_id(struct cc_data *data, unsigned int id) { struct cc_skill *skill; /* search to see if exists */ for ( skill=data->skills_map ; skill ; skill=skill->next ) { if (skill->id==id) return &skill->name; } return NULL; } void free_cc_skill(struct cc_skill *skill) { shm_free(skill); } struct cc_data* init_cc_data(void) { struct cc_data *data; data = (struct cc_data*) shm_malloc( sizeof(struct cc_data) ); if (data==NULL) { LM_ERR("failed to allocate shm mem\n"); return NULL; } memset( data, 0, sizeof(struct cc_data)); /* create & init lock */ if ( (data->lock=lock_alloc())==0) { LM_CRIT("failed to alloc lock\n"); goto error; } if (lock_init(data->lock)==0 ) { LM_CRIT("failed to init lock\n"); goto error; } /* set of locks for calls */ if ( (data->call_locks=lock_set_alloc(512))==0) { LM_CRIT("failed to alloc set of call locks\n"); goto error; } if (lock_set_init(data->call_locks)==0 ) { LM_CRIT("failed to init set of call locks\n"); goto error; } return data; error: free_cc_data(data); return NULL; } void free_cc_data(struct cc_data *data) { struct cc_flow *flow, *f_flow; struct cc_agent *agent,*f_agent; int i; if (data) { /* lock */ if (data->lock) { lock_destroy( data->lock ); lock_dealloc( data->lock ); } if (data->call_locks) { lock_set_destroy( data->call_locks ); lock_set_dealloc( data->call_locks ); } /* flows */ for( flow=data->flows ; flow ; ) { f_flow = flow; flow = flow->next; free_cc_flow( f_flow ); } /* agents */ for(i = 0; i< 2; i++) { for( agent=data->agents[i] ; agent ; ) { f_agent = agent; agent = agent->next; free_cc_agent( f_agent ); } } shm_free(data); } } struct cc_flow *get_flow_by_name(struct cc_data *data, str *name) { struct cc_flow *flow; for( flow=data->flows ; flow ; flow=flow->next ) { if (name->len==flow->id.len && memcmp( name->s, flow->id.s, name->len)==0) return flow; } return NULL; } struct cc_agent* get_agent_by_name(struct cc_data *data, str *name, struct cc_agent **prev_agent) { struct cc_agent *agent; int i; for(i = 0; i< 2; i++) { *prev_agent = data->agents[i]; for( agent=data->agents[i] ; agent ; agent=agent->next ) { if (name->len==agent->id.len && memcmp( name->s, agent->id.s, name->len)==0) return agent; *prev_agent = agent; } } return NULL; } void update_cc_flow_awt(struct cc_flow *flow, unsigned long duration) { flow->avg_waittime_no ++; flow->avg_waittime = ( ((float)duration + (flow->avg_waittime * (float)(flow->avg_waittime_no-1))) ) / (float)flow->avg_waittime_no; } #ifdef STATISTICS static unsigned long cc_flow_get_etw( void *flow_p) { struct cc_flow *flow = (struct cc_flow*)flow_p; return flow->logged_agents? (unsigned long)(( flow->avg_call_duration * get_stat_val(flow->st_queued_calls) ) / (float)flow->logged_agents):0; } static unsigned long cc_flow_get_awt( void *flow_p) { return (unsigned long)((struct cc_flow*)flow_p)->avg_waittime; } static unsigned long cc_flow_get_load( void *flow_p) { struct cc_flow *flow = (struct cc_flow*)flow_p; return (flow->logged_agents==0) ? 0 : (100*(get_stat_val(flow->st_onhold_calls)+flow->logged_agents-cc_flow_free_agents(flow))/flow->logged_agents); } #endif int add_cc_flow( struct cc_data *data, str *id, int priority, str *skill, str *cid, str *recordings ) { struct cc_flow *flow, *prev_flow; unsigned int i; unsigned int skill_id; #ifdef STATISTICS char *name; str s; #endif /* is the flow a new one? - search by ID */ flow = get_flow_by_name( data, id); if (flow==NULL) { /* new flow -> create and populate one */ flow = (struct cc_flow*)shm_malloc(sizeof(struct cc_flow)+id->len); if (flow==NULL) { LM_ERR("not enough shmem for a new flow\n"); goto error; } memset( flow, 0, sizeof(struct cc_flow) ); /* id */ flow->id.s = (char*)(flow+1); memcpy( flow->id.s, id->s, id->len); flow->id.len = id->len; /* priority */ flow->priority = priority; /* skill */ flow->skill = get_skill_id( data, skill ); if (flow->skill==0) { LM_ERR("cannot get skill id\n"); goto error; } /* cid */ if (cid && cid->s && cid->len) { flow->cid.s = (char*)shm_malloc(cid->len); if (flow->cid.s==NULL) { LM_ERR("not enough shmem for the cid of the flow\n"); goto error; } memcpy( flow->cid.s, cid->s, cid->len); flow->cid.len = cid->len; } /* audio messages */ for( i=0 ; irecordings[i].s = (char*)shm_malloc(recordings[i].len); if (flow->recordings[i].s==NULL) { LM_ERR("not enough shmem for the message %d of the flow\n", i); goto error; } memcpy( flow->recordings[i].s, recordings[i].s, recordings[i].len); flow->recordings[i].len = recordings[i].len; } } #ifdef STATISTICS /* statistics */ s.s = "ccf_incalls";s.len = 11 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat("call_center", name, &flow->st_incalls, STAT_SHM_NAME)!=0 ) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "ccf_dist_incalls";s.len = 15 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat("call_center", name, &flow->st_dist_incalls, STAT_SHM_NAME)!=0 ) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "ccf_answ_incalls";s.len = 15 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat("call_center", name, &flow->st_answ_incalls, STAT_SHM_NAME)!=0 ) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "ccf_aban_incalls";s.len = 15 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat("call_center", name, &flow->st_aban_incalls, STAT_SHM_NAME)!=0 ) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "ccf_onhold_calls";s.len = 15 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat("call_center", name, &flow->st_onhold_calls, STAT_SHM_NAME)!=0 ) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "ccf_queued_calls";s.len = 16 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat("call_center", name, &flow->st_queued_calls, STAT_SHM_NAME|STAT_NO_RESET)!=0 ) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "ccf_etw";s.len = 7 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat2("call_center", name, (stat_var **)cc_flow_get_etw, STAT_SHM_NAME|STAT_IS_FUNC, (void*)flow, 0)!=0) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "ccf_awt";s.len = 7 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat2("call_center", name, (stat_var **)cc_flow_get_awt, STAT_SHM_NAME|STAT_IS_FUNC, (void*)flow, 0)!=0) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "ccf_load";s.len = 8 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat2("call_center", name, (stat_var **)cc_flow_get_load, STAT_SHM_NAME|STAT_IS_FUNC, (void*)flow, 0)!=0) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "ccf_free_agents";s.len = 15 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat2("call_center", name, (stat_var **)cc_flow_free_agents, STAT_SHM_NAME|STAT_IS_FUNC, (void*)flow, 0)!=0) { LM_ERR("failed to add stat variable\n"); goto error; } #endif flow->is_new = 1; /* insert the new flow in the list */ flow->next = data->flows; data->flows = flow; } else { /* flow already exists -> update */ /* priority */ flow->priority = priority; /* skill - needs to be changed ? */ skill_id = get_skill_id(data,skill); if (skill_id==0) { LM_ERR("cannot get skill id\n"); goto error1; } flow->skill = skill_id; /* cid - needs to be changed ? */ if ( flow->cid.len && ( cid->len==0 || cid->len>flow->cid.len || memcmp(flow->cid.s,cid->s,cid->len)!=0) ) { shm_free(flow->cid.s); flow->cid.s = NULL; flow->cid.len = 0 ; } if (flow->cid.s==NULL && cid->len!=0) { flow->cid.s = (char*)shm_malloc(cid->len); if (flow->cid.s==NULL) { LM_ERR("not enough shmem for the cid of the flow\n"); goto error1; } } if (flow->cid.s) { memcpy( flow->cid.s, cid->s, cid->len); flow->cid.len = cid->len; } /* audio messages */ for( i=0 ; irecordings[i].len && ( recordings[i].len==0 || recordings[i].len>flow->recordings[i].len || memcmp(flow->recordings[i].s,recordings[i].s,recordings[i].len) ) ) { shm_free(flow->recordings[i].s); flow->recordings[i].s = NULL; flow->recordings[i].len = 0 ; } if (flow->recordings[i].s==NULL && recordings[i].len!=0) { flow->recordings[i].s = (char*)shm_malloc(recordings[i].len); if (flow->recordings[i].s==NULL) { LM_ERR("not enough shmem for the message of the flow\n"); goto error1; } } if (flow->recordings[i].s) { memcpy( flow->recordings[i].s, recordings[i].s, recordings[i].len); flow->recordings[i].len = recordings[i].len; } } flow->is_new = 1; } return 0; error1: if(data->flows == flow) data->flows = flow->next; else for(prev_flow=data->flows; prev_flow; prev_flow=prev_flow->next) if(prev_flow->next == flow) { prev_flow->next = flow->next; break; } error: if (flow) free_cc_flow(flow); return -1; } static void free_cc_flow( struct cc_flow *flow) { int i; if (flow->cid.s) shm_free(flow->cid.s); for( i=0 ; irecordings[i].s) shm_free(flow->recordings[i].s); } shm_free(flow); } void update_cc_agent_att(struct cc_agent *agent, unsigned long duration) { agent->avg_talktime_no ++; agent->avg_talktime = ( ((float)duration + (agent->avg_talktime * (float)(agent->avg_talktime_no-1))) ) / (float)agent->avg_talktime_no; } #ifdef STATISTICS static unsigned long cc_agent_get_att( void *agent_p) { return (unsigned long)((struct cc_agent*)agent_p)->avg_talktime; } #endif int add_cc_agent( struct cc_data *data, str *id, str *location, str *skills, unsigned int logstate, unsigned int last_call_end) { struct cc_agent *agent, *prev_agent= 0; struct sip_uri uri; str skill; char *p; unsigned int n,skill_id; #ifdef STATISTICS char *name; str s; #endif /* is the agent a new one? - search by ID */ agent = get_agent_by_name( data, id, &prev_agent); if (agent==NULL) { /* new agent -> create and populate one */ agent = (struct cc_agent*)shm_malloc(sizeof(struct cc_agent)+id->len); if (agent==NULL) { LM_ERR("not enough shmem for a new agent\n"); goto error; } memset( agent, 0, sizeof(struct cc_agent) ); /* id */ agent->id.s = (char*)(agent+1); memcpy( agent->id.s, id->s, id->len); agent->id.len = id->len; /* location */ agent->location.s = (char*)shm_malloc(location->len); if (agent->location.s==NULL) { LM_ERR("not enough shmem for the location of the agent\n"); goto error; } memcpy( agent->location.s, location->s, location->len); agent->location.len = location->len; if (parse_uri( agent->location.s, agent->location.len, &uri)<0) { LM_ERR("location of the agent is not a SIP URI\n"); goto error; } agent->did = uri.user; /* LOG STATE */ agent->loged_in = logstate; /* set of skills */ if (skills && skills->len) { p = skills->s; while (p) { skill.s = p; p = q_memchr(skill.s, ',', skills->s+skills->len-skill.s); skill.len = p?(p-skill.s):(skills->s+skills->len-skill.s); trim(&skill); if (skill.len) { skill_id = get_skill_id(data,&skill); if (skill_id==0) { LM_ERR("cannot get skill id\n"); goto error; } n = agent->no_skills++; agent->skills[n] = skill_id; } if(p) p++; } } /* statistics */ #ifdef STATISTICS s.s = "cca_dist_incalls";s.len = 16 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat("call_center", name, &agent->st_dist_incalls, STAT_SHM_NAME)!=0 ) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "cca_answ_incalls";s.len = 16 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat("call_center", name, &agent->st_answ_incalls, STAT_SHM_NAME)!=0 ) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "cca_aban_incalls";s.len = 16 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat("call_center", name, &agent->st_aban_incalls, STAT_SHM_NAME)!=0 ) { LM_ERR("failed to add stat variable\n"); goto error; } s.s = "cca_att";s.len = 7 ; if ( (name=build_stat_name( &s, id->s))==0 || register_stat2("call_center", name, (stat_var **)cc_agent_get_att, STAT_SHM_NAME|STAT_IS_FUNC, (void*)agent, 0)!=0) { LM_ERR("failed to add stat variable\n"); goto error; } #endif if(last_call_end && (last_call_end + wrapup_time < (int)time(NULL))) { agent->state = CC_AGENT_WRAPUP; agent->last_call_end = last_call_end - startup_time; /* it will be a negative value */ } agent->is_new = 1; /* link the agent */ add_cc_agent_top(data, agent); data->totalnr_agents++; } else { /* agent already exists -> update only */ /* location - needs to be changed ? */ if ( agent->location.len!=location->len || memcmp(agent->location.s,location->s,location->len)!=0 ) { /* set new location */ if (agent->location.len < location->len ){ shm_free(agent->location.s); agent->location.s = (char*)shm_malloc(location->len); if (agent->location.s==NULL) { LM_ERR("not enough shmem for the location of the agent\n"); goto error1; } } memcpy( agent->location.s, location->s, location->len); agent->location.len = location->len; if (parse_uri( agent->location.s, agent->location.len, &uri)<0) { LM_ERR("location of the agent is not a SIP URI\n"); goto error1; } agent->did = uri.user; } /* if logstate changed - move between the lists TODO */ if(logstate != agent->loged_in) { agent_switch_login(data, agent, prev_agent); } /* skills - needs to be changed ? */ agent->no_skills = 0; if (skills && skills->len) { p = skills->s; while (p) { skill.s = p; p = q_memchr(skill.s, ',', skills->s+skills->len-skill.s); skill.len = p?(p-skill.s):(skills->s+skills->len-skill.s); trim(&skill); if (skill.len) { skill_id = get_skill_id(data,&skill); if (skill_id==0) { LM_ERR("cannot get skill id\n"); goto error1; } n = agent->no_skills++; agent->skills[n] = skill_id; } if(p) p++; } } agent->is_new = 1; } return 0; error1: remove_cc_agent(data, agent, prev_agent); error: if (agent) free_cc_agent(agent); return 0; } static void free_cc_agent( struct cc_agent *agent) { if (agent->location.s) shm_free(agent->location.s); shm_free(agent); } void print_call_list(struct cc_data *data) { struct cc_call *call; for( call=data->list.first ; call ; call=call->next_list ) LM_DBG("[%.*s] - %p\n", call->b2bua_id.len, call->b2bua_id.s, call); } void cc_list_insert_call(struct cc_data *data, struct cc_call *call) { if(data->list.first) data->list.first->prev_list = call; call->next_list= data->list.first; data->list.first = call; call->prev_list = NULL; data->list.calls_no++; call->id = data->list.lid++; print_call_list(data); } void cc_list_remove_call(struct cc_data *data, struct cc_call *call) { if(call->prev_list) call->prev_list->next_list = call->next_list; else data->list.first = call->next_list; if(call->next_list) call->next_list->prev_list = call->prev_list; data->list.calls_no--; print_call_list(data); } struct cc_call* new_cc_call(struct cc_data *data, struct cc_flow *flow, str *dn, str *un) { struct cc_call *call; char *p; /* new call structure */ call = (struct cc_call*)shm_malloc( sizeof(struct cc_call) + (dn?dn->len:0) + (un?un->len:0) ); if (call==NULL) { LM_ERR("no more shm mem for a new call\n"); return NULL; } memset( call, 0, sizeof(struct cc_call) ); p = (char*)(call+1); /*copy DisplayName and UserName */ if (dn) { call->caller_dn.s = p; call->caller_dn.len = dn->len; memcpy( p, dn->s, dn->len ); p += dn->len; } if (un) { call->caller_un.s = p; call->caller_un.len = un->len; memcpy( p, un->s, un->len ); p += un->len; } call->recv_time = get_ticks(); call->setup_time = -1; /* attache to flow */ call->flow = flow; flow->ref_cnt++; LM_DBG("created call %p\n", call); /* attache a lock */ call->lock_idx = data->next_lock_to_use++; if (data->next_lock_to_use==512) data->next_lock_to_use = 0; cc_list_insert_call( data, call ); return call; } void free_cc_call(struct cc_data * data, struct cc_call *call) { lock_get( data->lock ); cc_list_remove_call( data, call ); lock_release( data->lock ); LM_DBG("free call %p, [%.*s]\n", call, call->b2bua_id.len, call->b2bua_id.s); if (call->flow) call->flow->ref_cnt--; if(call->b2bua_id.s) shm_free(call->b2bua_id.s); shm_free(call); } struct cc_agent* get_free_agent_by_skill(struct cc_data *data, unsigned int skill) { struct cc_agent *agent; unsigned int n; agent = data->agents[CC_AG_ONLINE]; if (agent==NULL) return NULL; /* iterate all agents*/ do { if(agent->state==CC_AGENT_FREE) { /* iterate all skills of the agent */ for( n=0 ; nno_skills ; n++) { if (agent->skills[n]==skill) return agent; } } /* next agent */ agent = agent->next; }while(agent); return NULL; } void log_agent_to_flows(struct cc_data *data, struct cc_agent *agent, int login) { unsigned int i; struct cc_flow *flow; LM_DBG("login %d agent %.*s\n", login, agent->id.len, agent->id.s); /* iterate all skills of the agent */ for( i=0 ; ino_skills ; i++) { //LM_DBG(" agent skill is %d (%d)\n", agent->skills[i],i); /* iterate all flows */ for( flow=data->flows ; flow ; flow=flow->next ) { //LM_DBG("chekcing flow %.*s with skill %d\n", flow->id.len, flow->id.s, flow->skill); if (agent->skills[i]==flow->skill) flow->logged_agents = flow->logged_agents + (login?1:-1); } } } void clean_cc_old_data(struct cc_data *data) { struct cc_skill *skill, **prv_skill; struct cc_agent *agent, **prv_agent; struct cc_flow *flow, **prv_flow; int i; /* clean old skills */ skill = data->skills_map; prv_skill = &(data->skills_map); while(skill) { if (skill->is_new) { skill->is_new = 0; prv_skill = &(skill->next); skill = skill->next; } else { *prv_skill = skill->next; free_cc_skill(skill); skill = (*prv_skill); } } /* clean old agents */ for(i= 0; i< 2; i++) { agent = data->agents[i]; prv_agent = &data->agents[i]; while(agent) { if (agent->is_new) { agent->is_new = 0; prv_agent = &(agent->next); agent = agent->next; } else { *prv_agent = agent->next; if (agent->ref_cnt==0) { free_cc_agent(agent); } else { agent->next = data->old_agents; data->old_agents = agent; } agent = (*prv_agent); data->totalnr_agents--; } } } /* clean old flows */ flow = data->flows; prv_flow = &(data->flows); while(flow) { flow->logged_agents = 0; if (flow->is_new) { flow->is_new = 0; prv_flow = &(flow->next); flow = flow->next; } else { *prv_flow = flow->next; if (flow->ref_cnt==0) { free_cc_flow(flow); } else { /* put in a cleanup list */ flow->next = data->old_flows; data->old_flows = flow; } flow = (*prv_flow); } } /* sync flows and agents (how many agents per flow are logged) */ /* iterate all logged agents */ data->logedin_agents = 0; for( agent=data->agents[CC_AG_ONLINE] ; agent ; agent=agent->next ) { /* update last agent */ data->last_online_agent = agent; /* log_agent_to_flows() must now the call center of the * agent to count it as logged in */ log_agent_to_flows( data, agent, agent->loged_in); data->logedin_agents++; } } void clean_cc_unref_data(struct cc_data *data) { struct cc_agent *agent, **prv_agent; struct cc_flow *flow, **prv_flow; /* clean unref flows */ flow = data->old_flows; prv_flow = &(data->old_flows); while(flow) { if (flow->ref_cnt!=0) { prv_flow = &(flow->next); flow = flow->next; } else { *prv_flow = flow->next; free_cc_flow(flow); flow = (*prv_flow); } } /* clean unref agents */ agent = data->old_agents; prv_agent = &(data->old_agents); while(agent) { if (agent->ref_cnt!=0) { prv_agent = &(agent->next); agent = agent->next; } else { *prv_agent = agent->next; free_cc_agent(agent); agent = (*prv_agent); } } return; } void print_queue(struct cc_data *data) { struct cc_call *call_it; LM_DBG("QUEUE:\n"); for(call_it=data->queue.first ; call_it ; call_it=call_it->lower_in_queue) LM_DBG("[%p] ->\n", call_it); LM_DBG("0\n"); } void cc_queue_push_call(struct cc_data *data, struct cc_call *call, int top) { struct cc_call *call_it; int n = 0; LM_DBG(" QUEUE - adding call %p \n",call); if ( is_call_in_queue(data, call) ) { LM_CRIT(" QUEUE - call already in queue \n"); abort(); } if (top) { /* add the call in the very top of the queue */ call_it = NULL; } else { /* search (priority based) the place in queue */ for(call_it=data->queue.last ; call_it ; call_it=call_it->higher_in_queue){ if (call_it->flow->priority <= call->flow->priority) break; n++; } } if (call_it) { /* add before it */ if (call_it->lower_in_queue) { call_it->lower_in_queue->higher_in_queue = call; } else { data->queue.last = call; } call->lower_in_queue = call_it->lower_in_queue; call->higher_in_queue = call_it; call_it->lower_in_queue = call; } else { /* add in top of the queue */ call->lower_in_queue = data->queue.first; if (data->queue.first) { data->queue.first->higher_in_queue = call; } else { data->queue.last = call; } call->higher_in_queue = NULL; data->queue.first = call; } data->queue.calls_no++; update_stat( call->flow->st_queued_calls, +1 ); LM_DBG("adding call on pos %d (already %d calls), l=%p h=%p\n", n, data->queue.calls_no, call->lower_in_queue, call->higher_in_queue); call->ref_cnt++; if (call->queue_start==0) call->queue_start = get_ticks(); } void cc_queue_rmv_call( struct cc_data *data, struct cc_call *call) { LM_DBG(" QUEUE - removing call %p \n",call); if ( !is_call_in_queue(data, call) ) { LM_CRIT(" QUEUE - call not in queue l=%p, h=%p\n", call->lower_in_queue, call->higher_in_queue); abort(); } if (call->lower_in_queue) { call->lower_in_queue->higher_in_queue = call->higher_in_queue; } else { data->queue.last = call->higher_in_queue; } if (call->higher_in_queue) { call->higher_in_queue->lower_in_queue = call->lower_in_queue; } else { data->queue.first = call->lower_in_queue; } call->lower_in_queue = call->higher_in_queue = NULL; data->queue.calls_no--; update_stat( call->flow->st_queued_calls, -1 ); } struct cc_call *cc_queue_pop_call_for_agent(struct cc_data *data, struct cc_agent *agent) { struct cc_call *call_it; unsigned int i; /* interate all the queued calls and see * * if they mathe the agent (as skills)*/ for(call_it=data->queue.first ; call_it ; call_it=call_it->lower_in_queue){ /* check the call skill against the agent skills */ for(i=0 ; ino_skills ; i++) { /* before taking a call out, be sure that call is fully initialized * from b2bua point of view (to avoid races) -> check the b2bua id */ if (call_it->b2bua_id.len!=0 && call_it->flow->skill==agent->skills[i]) { LM_DBG("found call %p for agent %p(%.*s) with skill %d \n", call_it, agent, agent->id.len, agent->id.s, call_it->flow->skill); /* remove the call from queue */ cc_queue_rmv_call( data, call_it); return call_it; } } } return NULL; } opensips-2.2.2/modules/call_center/cc_data.h000077500000000000000000000154731300170765700210520ustar00rootroot00000000000000/* * call center module - call queuing and distributio * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- */ #ifndef CC_CC_DATA_H_ #define CC_CC_DATA_H_ #include "../../str.h" #include "../../locking.h" #include "../../hash_func.h" #include "../../parser/msg_parser.h" typedef enum { AUDIO_WELCOME, AUDIO_QUEUE, MAX_AUDIO } audio_files; struct cc_flow { str id; unsigned int is_new; /* configuration data */ unsigned int priority; unsigned int skill; str recordings[MAX_AUDIO]; str cid; /* runtime data */ int ref_cnt; float avg_call_duration; unsigned long processed_calls; unsigned int logged_agents; unsigned int ongoing_calls; /* statistics */ stat_var *st_incalls; stat_var *st_dist_incalls; stat_var *st_answ_incalls; stat_var *st_aban_incalls; stat_var *st_onhold_calls; stat_var *st_queued_calls; float avg_waittime; unsigned long avg_waittime_no; struct cc_flow *next; }; #define MAX_SKILLS_PER_AGENT 32 typedef enum { CC_AGENT_FREE, CC_AGENT_WRAPUP, CC_AGENT_INCALL }agent_state; struct cc_agent { str id; unsigned int is_new; /* configuration data */ str location; /* sip address*/ str did; /* shorcut for username in sips address */ unsigned int no_skills; unsigned int skills[MAX_SKILLS_PER_AGENT]; /* runtime data */ int ref_cnt; agent_state state; unsigned int loged_in; int last_call_end; /* statistics */ stat_var *st_dist_incalls; stat_var *st_answ_incalls; stat_var *st_aban_incalls; float avg_talktime; unsigned long avg_talktime_no; struct cc_agent *next; }; struct cc_list { unsigned int lid; unsigned int calls_no; struct cc_call *first; struct cc_call *last; }; struct cc_skill { str name; unsigned int id; unsigned int is_new; struct cc_skill *next; }; #define CC_AG_OFFLINE 0 #define CC_AG_ONLINE 1 struct cc_data { gen_lock_t *lock; /* sub-structures */ struct cc_flow *flows; struct cc_agent *agents[2]; struct cc_agent *last_online_agent; struct cc_skill *skills_map; struct cc_list queue; struct cc_list list; /* old data */ struct cc_flow *old_flows; struct cc_agent *old_agents; /* call related data */ gen_lock_set_t *call_locks; unsigned int next_lock_to_use; struct cc_call *calls; /* skills related data */ unsigned int last_skill_id; /* tracking data */ unsigned int logedin_agents; float avt_waittime; unsigned long avt_waittime_no; unsigned long totalnr_agents; }; typedef enum { CC_CALL_NONE, CC_CALL_WELCOME, CC_CALL_QUEUED, CC_CALL_TOAGENT, CC_CALL_ENDED } call_state; #define FSTAT_INCALL (1<<0) #define FSTAT_DIST (1<<1) #define FSTAT_ANSW (1<<2) #define FSTAT_ABAN (1<<3) struct cc_call { unsigned int id; unsigned int lock_idx; char ign_cback; /* ignore callbacks because agent_free was called */ int fst_flags; /* flow stats flags */ call_state state; /* call state */ call_state prev_state; short ref_cnt; short no_rejections; short setup_time; unsigned int eta; unsigned int last_start; unsigned int queue_start; unsigned int recv_time; str caller_dn; str caller_un; /* b2b id */ str b2bua_id; /* flow the call belong to */ struct cc_flow *flow; /* agent taking this call */ struct cc_agent *agent; /* queue-ing link */ struct cc_call *higher_in_queue; struct cc_call *lower_in_queue; struct cc_call *next_list; struct cc_call *prev_list; }; #define is_call_in_queue(_data, _call) ((_call)->lower_in_queue || (_call)->higher_in_queue || \ (_data->queue.first==_call && _data->queue.last==_call)) struct cc_data* init_cc_data(void); void free_cc_data(struct cc_data *data); str* get_skill_by_id(struct cc_data *data, unsigned int id); int add_cc_flow( struct cc_data *data, str *id, int priority, str *skill, str *cid, str *recordings ); void update_cc_agent_att(struct cc_agent *agent, unsigned long duration); int add_cc_agent( struct cc_data *data, str *id, str *location, str *skills, unsigned int logstate, unsigned int last_call_end); void update_cc_flow_awt(struct cc_flow *flow, unsigned long duration); struct cc_agent* get_agent_by_name(struct cc_data *data, str *name, struct cc_agent **prev_agent); struct cc_flow *get_flow_by_name(struct cc_data *data, str *name); struct cc_call* new_cc_call(struct cc_data *data, struct cc_flow *flow, str *dn, str *un); void free_cc_call(struct cc_data *data, struct cc_call *call); struct cc_agent* get_free_agent_by_skill(struct cc_data *data, unsigned int skill); void log_agent_to_flows(struct cc_data *data, struct cc_agent *agent, int login); void clean_cc_old_data(struct cc_data *data); void clean_cc_unref_data(struct cc_data *data); void cc_queue_push_call(struct cc_data *data, struct cc_call *call, int top); struct cc_call *cc_queue_pop_call_for_agent(struct cc_data *data, struct cc_agent *agent); void cc_queue_rmv_call( struct cc_data *data, struct cc_call *call); static inline void remove_cc_agent(struct cc_data* data, struct cc_agent* agent, struct cc_agent* prev_agent) { struct cc_agent* tmp_agent; if(prev_agent == agent) /* if on top of the list*/ data->agents[agent->loged_in] = agent->next; else prev_agent->next = agent->next; if(agent->loged_in && data->last_online_agent == agent) {/* if agent was the last in the list */ if(data->agents[CC_AG_ONLINE] == NULL) data->last_online_agent = NULL; else { if(prev_agent == agent) { LM_CRIT("last_online_agent pointer not correct" "- pointing to the first record in list but next not NULL\n"); /* search for the real last */ for(tmp_agent= data->agents[CC_AG_ONLINE]; tmp_agent; tmp_agent= tmp_agent->next) data->last_online_agent = tmp_agent; } else data->last_online_agent = prev_agent; } } } static inline void add_cc_agent_top( struct cc_data *data, struct cc_agent *agent) { agent->next = data->agents[agent->loged_in]; data->agents[agent->loged_in] = agent; } static inline void agent_switch_login(struct cc_data* data, struct cc_agent* agent, struct cc_agent* prev_agent) { /* take out of the current list */ remove_cc_agent(data, agent, prev_agent); agent->loged_in ^= 1; /* add on top of the new one */ add_cc_agent_top(data, agent); } #endif opensips-2.2.2/modules/call_center/cc_db.c000077500000000000000000000520731300170765700205160ustar00rootroot00000000000000/* * call center module - call queuing and distribution * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- */ #include "../../globals.h" #include "../../db/db.h" #include "../b2b_logic/b2b_load.h" #include "cc_db.h" #define CC_FETCH_ROWS 100 str cc_flow_table_name = str_init(CC_FLOW_TABLE_NAME); str ccf_flowid_column = str_init(CCF_FLOWID_COL); str ccf_priority_column = str_init(CCF_PRIORITY_ID_COL); str ccf_skill_column = str_init(CCF_SKILL_COL); str ccf_cid_column = str_init(CCF_CID_COL); str ccf_m_welcome_column = str_init(CCF_WELCOME_COL); str ccf_m_queue_column = str_init(CCF_M_QUEUE_COL); str cc_agent_table_name = str_init(CC_AGENT_TABLE_NAME); str cca_agentid_column = str_init(CCA_AGENTID_COL); str cca_location_column = str_init(CCA_LOCATION_ID_COL); str cca_skills_column = str_init(CCA_SKILLS_COL); str cca_logstate_column = str_init(CCA_LOGSTATE_COL); str cca_lastcallend_column = str_init(CCA_LASTCALLEND_COL); str cc_globals_table_name = str_init(CC_GLOBALS_TABLE_NAME); str ccg_name_column = str_init(CCG_NAME_COL); str ccg_value_column = str_init(CCG_VALUE_COL); str cc_cdrs_table_name = str_init(CC_CDRS_TABLE_NAME); str ccc_caller_column = str_init(CCC_CALLER_COL); str ccc_recv_time_column = str_init(CCC_RECV_TIME_COL); str ccc_wait_time_column = str_init(CCC_WAIT_TIME_COL); str ccc_talk_time_column = str_init(CCC_TALK_TIME_COL); str ccc_pickup_time_column = str_init(CCC_PICKUP_TIME_COL); str ccc_flow_id_column = str_init(CCC_FLOW_ID_COL); str ccc_agent_id_column = str_init(CCC_AGENT_ID_COL); str ccc_cc_id_column = str_init(CCC_CC_ID_COL); str ccc_type_column = str_init(CCC_TYPE_COL); str ccc_rejected_column = str_init(CCC_REJECTED_COL); str ccc_fstats_column = str_init(CCC_FSTATS_COL); str ccc_cid_column = str_init(CCC_CID_COL); str cc_calls_table_name = str_init(CC_CALLS_TABLE_NAME); str ccq_state_column = str_init(CCQ_STATE_COL); str ccq_ig_cback_column = str_init(CCQ_IGCBACK_COL); str ccq_no_rej_column = str_init(CCQ_NOREJ_COL); str ccq_setup_time_column = str_init(CCQ_SETUP_TIME_COL); str ccq_eta_column = str_init(CCQ_ETA_COL); str ccq_last_start_column = str_init(CCQ_LAST_START_COL); str ccq_recv_time_column = str_init(CCQ_RECV_TIME_COL); str ccq_caller_dn_column = str_init(CCQ_CALLER_DN_COL); str ccq_caller_un_column = str_init(CCQ_CALLER_UN_COL); str ccq_b2buaid_column = str_init(CCQ_B2BUAID_COL); str ccq_flow_column = str_init(CCQ_FLOW_COL); str ccq_agent_column = str_init(CCQ_AGENT_COL); #define CCQ_COLS_NO 12 static db_con_t* cc_db_handle = 0; /* database connection handle */ static db_con_t* cc_acc_db_handle = 0; /* database connection handle */ static db_func_t cc_dbf; static db_func_t cc_acc_dbf; extern b2bl_api_t b2b_api; #define check_val( _val, _type, _not_null, _is_empty_str, _c) \ do{\ if ((_val)->type!=_type) { \ LM_ERR("bad column type: %s [%d/%d]\n", _c, (_val)->type, _type);\ goto error;\ } \ if (_not_null && (_val)->nul) { \ LM_ERR("nul column: %s\n", _c);\ goto error;\ } \ if (_is_empty_str && VAL_STRING(_val)==0) { \ LM_ERR("empty str column: %s\n", _c);\ goto error;\ } \ }while(0) int cc_connect_db(const str *db_url) { if (cc_db_handle) { LM_CRIT("BUG - db connection found already open\n"); return -1; } if ((cc_db_handle = cc_dbf.init(db_url)) == 0) return -1; return 0; } int cc_connect_acc_db(const str *acc_db_url) { if (cc_acc_db_handle) { LM_CRIT("BUG - db connection found already open\n"); return -1; } if ((cc_acc_db_handle = cc_acc_dbf.init(acc_db_url)) == 0) return -1; return 0; } void cc_close_db(void) { if (cc_db_handle==NULL) return; cc_dbf.close(cc_db_handle); cc_db_handle = NULL; } int init_cc_db(const str *db_url) { /* Find a database module */ if (db_bind_mod(db_url, &cc_dbf) < 0){ LM_ERR("Unable to bind to a database driver\n"); return -1; } if (cc_connect_db(db_url)!=0){ LM_ERR("unable to connect to the database\n"); return -1; } if(db_check_table_version(&cc_dbf, cc_db_handle, &cc_flow_table_name, CC_FLOW_TABLE_VERSION) < 0) { LM_ERR("error during FLOW table version check.\n"); return -1; } if(db_check_table_version(&cc_dbf, cc_db_handle, &cc_agent_table_name, CC_AGENT_TABLE_VERSION) < 0) { LM_ERR("error during AGENT table version check.\n"); return -1; } return 0; } int init_cc_acc_db(const str *acc_db_url) { /* Find a database module */ if (db_bind_mod(acc_db_url, &cc_acc_dbf) < 0){ LM_ERR("Unable to bind to a database driver\n"); return -1; } return 0; } int cc_db_delete_call(struct cc_call *call) { db_key_t qcols[1]; db_val_t qvals[1]; if(cc_dbf.use_table( cc_db_handle, &cc_calls_table_name) < 0) { LM_ERR("SQL use table for %.*s table failed\n", cc_calls_table_name.len, cc_calls_table_name.s); return -1; } qcols[0] = &ccq_b2buaid_column; qvals[0].type = DB_STR; qvals[0].nul = 0; qvals[0].val.str_val = call->b2bua_id; if(cc_dbf.delete(cc_db_handle, qcols, 0, qvals, 1) < 0) { LM_ERR("unsuccessful sql delete operation"); return -1; } LM_DBG("Deleted call %.*s\n", call->b2bua_id.len, call->b2bua_id.s); return 0; } int cc_db_update_call(struct cc_call *call) { db_key_t qcols[1]; db_key_t ucols[5]; db_val_t qvals[1]; db_val_t uvals[5]; if(cc_dbf.use_table( cc_db_handle, &cc_calls_table_name) < 0) { LM_ERR("SQL use table for %.*s table failed\n", cc_calls_table_name.len, cc_calls_table_name.s); return -1; } memset(&uvals, 0, 5*sizeof(db_val_t)); qcols[0] = &ccq_b2buaid_column; qvals[0].type = DB_STR; qvals[0].val.str_val = call->b2bua_id; ucols[0] = &ccq_state_column; uvals[0].type = DB_INT; uvals[0].val.int_val = call->state; ucols[1] = &ccq_ig_cback_column; uvals[1].type = DB_INT; uvals[1].val.int_val = call->ign_cback; ucols[2] = &ccq_no_rej_column; uvals[2].type = DB_INT; uvals[2].val.int_val = call->no_rejections; ucols[3] = &ccq_last_start_column; uvals[3].type = DB_INT; uvals[3].val.int_val = call->last_start; ucols[4] = &ccq_agent_column; uvals[4].type = DB_STR; if(call->agent) uvals[4].val.str_val = call->agent->id; if( cc_dbf.update(cc_db_handle, qcols, 0, qvals, ucols, uvals, 1, 5)<0) { LM_ERR("updating call record in database\n"); return -1; } LM_DBG("updated call in db\n"); return 0; } int cc_db_insert_call(struct cc_call *call) { db_key_t columns[CCQ_COLS_NO]; db_val_t vals[CCQ_COLS_NO]; if(cc_dbf.use_table( cc_db_handle, &cc_calls_table_name) < 0) { LM_ERR("SQL use table for %.*s table failed\n", cc_calls_table_name.len, cc_calls_table_name.s); return -1; } memset(&vals, 0, CCQ_COLS_NO*sizeof(db_val_t)); columns[0] = &ccq_state_column; vals[0].type = DB_INT; vals[0].val.int_val = call->state; columns[1] = &ccq_ig_cback_column; vals[1].type = DB_INT; vals[1].val.int_val = call->ign_cback; columns[2] = &ccq_no_rej_column; vals[2].type = DB_INT; vals[2].val.int_val = call->no_rejections; columns[3] = &ccq_setup_time_column; vals[3].type = DB_INT; vals[3].val.int_val = call->setup_time; columns[4] = &ccq_eta_column; vals[4].type = DB_INT; vals[4].val.int_val = call->eta; columns[5] = &ccq_last_start_column; vals[5].type = DB_INT; vals[5].val.int_val = call->last_start; columns[6] = &ccq_recv_time_column; vals[6].type = DB_INT; vals[6].val.int_val = call->recv_time; columns[7] = &ccq_caller_dn_column; vals[7].type = DB_STR; vals[7].val.str_val = call->caller_dn; columns[8] = &ccq_caller_un_column; vals[8].type = DB_STR; vals[8].val.str_val = call->caller_un; columns[9] = &ccq_b2buaid_column; vals[9].type = DB_STR; vals[9].val.str_val = call->b2bua_id; columns[10] = &ccq_flow_column; vals[10].type = DB_STR; vals[10].val.str_val = call->flow->id; columns[11] = &ccq_agent_column; vals[11].type = DB_STR; if(call->agent) vals[11].val.str_val = call->agent->id; if (cc_dbf.insert(cc_db_handle, columns, vals, CCQ_COLS_NO) < 0) { LM_ERR("inserting new record in database\n"); return -1; } LM_DBG("inserted call in db\n"); return 0; } int cc_db_restore_calls( struct cc_data *data) { db_key_t columns[CCQ_COLS_NO]; db_res_t* res; db_row_t* row; str s; struct cc_flow *flow; struct cc_call *call; int i; struct cc_agent *agent = NULL; struct cc_agent *prev; str dn, un; str id; cc_dbf.use_table( cc_db_handle, &cc_calls_table_name); columns[0] = &ccq_state_column; columns[1] = &ccq_ig_cback_column; columns[2] = &ccq_no_rej_column; columns[3] = &ccq_setup_time_column; columns[4] = &ccq_eta_column; columns[5] = &ccq_last_start_column; columns[6] = &ccq_recv_time_column; columns[7] = &ccq_caller_dn_column; columns[8] = &ccq_caller_un_column; columns[9] = &ccq_b2buaid_column; columns[10] = &ccq_flow_column; columns[11] = &ccq_agent_column; if ( cc_dbf.query( cc_db_handle, 0, 0, 0, columns, 0, CCQ_COLS_NO, 0, &res)<0) { LM_ERR("DB query failed\n"); return -1; } if (RES_ROW_N(res) == 0) { LM_DBG("No calls restored\n"); return 0; } LM_DBG("%d records found in %.*s\n", RES_ROW_N(res), cc_calls_table_name.len,cc_calls_table_name.s ); for(i= RES_ROW_N(res)-1; i>= 0; i--) { row = RES_ROWS(res) + i; /* FLOW_COL */ check_val( ROW_VALUES(row)+10, DB_STRING, 1, 1, "flow"); s.s = (char*)VAL_STRING(ROW_VALUES(row)+10); s.len = strlen(s.s); flow = get_flow_by_name(data, &s); if (flow==NULL) { LM_ERR("flow <%.*s> does not exists\n", s.len, s.s); continue; } LM_DBG("using call flow %p\n", flow); /* CALLER_DN_COL */ check_val( ROW_VALUES(row)+7, DB_STRING, 1, 0, "caller_dn"); dn.s = (char*)VAL_STRING(ROW_VALUES(row)+7); if(dn.s) dn.len = strlen(dn.s); /* CALLER_UN_COL */ check_val( ROW_VALUES(row)+8, DB_STRING, 1, 0, "caller_un"); un.s = (char*)VAL_STRING(ROW_VALUES(row)+8); if(un.s) un.len = strlen(un.s); call = new_cc_call(data, flow, &dn, &un); if (call==NULL) { LM_ERR("failed to create new call\n"); goto error; } /* AGENT_COL */ check_val( ROW_VALUES(row)+11, DB_STRING, 0, 0, "agent"); s.s = (char*)VAL_STRING(ROW_VALUES(row)+11); if(s.s && strlen(s.s)) { s.len = strlen(s.s); /* name of the agent */ agent = get_agent_by_name(data, &s, &prev); if (agent==NULL) { LM_ERR("Agent <%.*s> does not exists\n", s.len, s.s); continue; } call->agent = agent; agent->state = CC_AGENT_INCALL; agent->ref_cnt++; } /* STATE_COL */ check_val( ROW_VALUES(row), DB_INT, 1, 0, "state"); call->state = VAL_INT(ROW_VALUES(row)); /* IGCBACK_COL */ check_val( ROW_VALUES(row)+1, DB_INT, 1, 0, "ig_cback"); call->ign_cback = VAL_INT(ROW_VALUES(row)+1); /* NOREJ_COL */ check_val( ROW_VALUES(row)+2, DB_INT, 1, 0, "no_rej"); call->no_rejections = VAL_INT(ROW_VALUES(row)+2); /* SETUP_TIME_COL */ check_val( ROW_VALUES(row)+3, DB_INT, 1, 0, "setup_time"); call->setup_time = VAL_INT(ROW_VALUES(row)+3); /* ETA_COL */ check_val( ROW_VALUES(row)+4, DB_INT, 1, 0, "eta"); call->eta = VAL_INT(ROW_VALUES(row)+4); /* LAST_START_COL */ check_val( ROW_VALUES(row)+5, DB_INT, 1, 0, "last_start"); call->last_start = VAL_INT(ROW_VALUES(row)+5); /* RECV_TIME_COL */ check_val( ROW_VALUES(row)+6, DB_INT, 1, 0, "recv_time"); call->recv_time = VAL_INT(ROW_VALUES(row)+6); /* B2BUAID_COL */ check_val( ROW_VALUES(row)+9, DB_STRING, 1, 1, "b2buaid"); id.s = (char*)VAL_STRING(ROW_VALUES(row)+9); if(id.s) { id.len = strlen(id.s); call->b2bua_id.len = id.len; call->b2bua_id.s = (char*)shm_malloc(id.len); if(call->b2bua_id.s == NULL) { LM_ERR("No more memory\n"); goto error; } memcpy(call->b2bua_id.s, id.s, id.len); call->ref_cnt++; /* restore logic info */ if(b2b_api.restore_upper_info(&call->b2bua_id, b2bl_callback_customer, call, B2B_DESTROY_CB|B2B_REJECT_CB|B2B_BYE_CB)< 0) { /* delete the call*/ LM_ERR("Upper info not found for [%.*s]\n", id.len, id.s); free_cc_call( data, call); continue; } } if(call->state == CC_CALL_QUEUED) { cc_queue_push_call( data, call, 0); call->ref_cnt++; } } LM_DBG("Restored calls\n"); return 0; error: return -1; } int cc_load_db_data( struct cc_data *data) { db_key_t columns[6]; db_res_t* res; db_row_t* row; int i, j, n; str id,skill,cid; str location; unsigned int priority, logstate, last_call_end; str messages[MAX_AUDIO]; cc_dbf.use_table( cc_db_handle, &cc_flow_table_name); columns[0] = &ccf_flowid_column; columns[1] = &ccf_priority_column; columns[2] = &ccf_skill_column; columns[3] = &ccf_cid_column; columns[4] = &ccf_m_welcome_column; columns[5] = &ccf_m_queue_column; if (0/*DB_CAPABILITY(cc_dbf, DB_CAP_FETCH))*/) { if ( cc_dbf.query( cc_db_handle, 0, 0, 0, columns, 0, 6, 0, 0 ) < 0) { LM_ERR("DB query failed\n"); return -1; } if(cc_dbf.fetch_result( cc_db_handle, &res, CC_FETCH_ROWS)<0) { LM_ERR("Error fetching rows\n"); return -1; } } else { if ( cc_dbf.query( cc_db_handle, 0, 0, 0, columns, 0, 6, 0, &res)<0) { LM_ERR("DB query failed\n"); return -1; } } if (RES_ROW_N(res) == 0) { LM_WARN("table \"%.*s\" empty\n", cc_flow_table_name.len, cc_flow_table_name.s ); return -1; } LM_DBG("%d records found in %.*s\n", RES_ROW_N(res), cc_flow_table_name.len,cc_flow_table_name.s ); n = 0; do { for(i=0; i < RES_ROW_N(res); i++) { row = RES_ROWS(res) + i; /* flowID column */ check_val( ROW_VALUES(row), DB_STRING, 1, 1, "flowid"); id.s = (char*)VAL_STRING(ROW_VALUES(row)); id.len = strlen(id.s); /* PRIORITY column */ check_val( ROW_VALUES(row)+1, DB_INT, 1, 0, "priority"); priority = VAL_INT(ROW_VALUES(row)+1); /* SKILL column */ check_val( ROW_VALUES(row)+2, DB_STRING, 1, 1, "skill"); skill.s = (char*)VAL_STRING(ROW_VALUES(row)+2); skill.len = strlen(skill.s); /* CID column */ check_val( ROW_VALUES(row)+3, DB_STRING, 0, 0, "prependcid"); if (VAL_NULL(ROW_VALUES(row)+3)) { cid.s = NULL; cid.len = 0; } else { cid.s = (char*)VAL_STRING(ROW_VALUES(row)+3); if (cid.s==NULL || (cid.len=strlen(cid.s))==0 ) { cid.s = NULL; cid.len = 0; } } for( j=0 ; j skipping\n", id.len,id.s); continue; } n++; } if (DB_CAPABILITY( cc_dbf, DB_CAP_FETCH)) { if(cc_dbf.fetch_result(cc_db_handle, &res, CC_FETCH_ROWS)<0) { LM_ERR( "fetching rows (1)\n"); return -1; } } else { break; } } while(RES_ROW_N(res)>0); cc_dbf.free_result(cc_db_handle, res); res = 0; cc_dbf.use_table( cc_db_handle, &cc_agent_table_name); columns[0] = &cca_agentid_column; columns[1] = &cca_location_column; columns[2] = &cca_skills_column; columns[3] = &cca_logstate_column; columns[4] = &cca_lastcallend_column; if (0/*DB_CAPABILITY(cc_dbf, DB_CAP_FETCH))*/) { if ( cc_dbf.query( cc_db_handle, 0, 0, 0, columns, 0, 5, 0, 0 ) < 0) { LM_ERR("DB query failed\n"); return -1; } if(cc_dbf.fetch_result( cc_db_handle, &res, CC_FETCH_ROWS)<0) { LM_ERR("Error fetching rows\n"); return -1; } } else { if ( cc_dbf.query( cc_db_handle, 0, 0, 0, columns, 0, 5, 0, &res)<0) { LM_ERR("DB query failed\n"); return -1; } } if (RES_ROW_N(res) == 0) { LM_WARN("table \"%.*s\" empty\n", cc_agent_table_name.len, cc_agent_table_name.s ); return -1; } LM_DBG("%d records found in %.*s\n", RES_ROW_N(res), cc_agent_table_name.len,cc_agent_table_name.s ); n = 0; do { for(i=0; i < RES_ROW_N(res); i++) { row = RES_ROWS(res) + i; /* agentID column */ check_val( ROW_VALUES(row), DB_STRING, 1, 1, "agentid"); id.s = (char*)VAL_STRING(ROW_VALUES(row)); id.len = strlen(id.s); /* LOCATION column */ check_val( ROW_VALUES(row)+1, DB_STRING, 1, 1, "location"); location.s = (char*)VAL_STRING(ROW_VALUES(row)+1); location.len = strlen(location.s); /* SKILLS column */ check_val( ROW_VALUES(row)+2, DB_STRING, 1, 1, "skills"); skill.s = (char*)VAL_STRING(ROW_VALUES(row)+2); skill.len = strlen(skill.s); /* LOGSTATE column */ check_val( ROW_VALUES(row)+3, DB_INT, 1, 0, "logstate"); logstate = VAL_INT(ROW_VALUES(row)+3); /* LAST_CALL_END column */ last_call_end = VAL_INT(ROW_VALUES(row)+4); /* add agent */ if (add_cc_agent(data,&id,&location,&skill,logstate,last_call_end)<0){ LM_ERR("failed to add agent %.*s -> skipping\n", id.len,id.s); continue; } n++; } if (DB_CAPABILITY( cc_dbf, DB_CAP_FETCH)) { if(cc_dbf.fetch_result(cc_db_handle, &res, CC_FETCH_ROWS)<0) { LM_ERR( "fetching rows (1)\n"); return -1; } } else { break; } } while(RES_ROW_N(res)>0); cc_dbf.free_result(cc_db_handle, res); res = 0; return 0; error: if (res) cc_dbf.free_result(cc_db_handle, res); return -1; } int prepare_cdr(struct cc_call *call, str *un, str *fid , str *aid) { #define CDR_BUF_LEN 2048 #define CDR_ITEM_LEN(_a) ( (p+_a>buf+CDR_BUF_LEN) ? buf+CDR_BUF_LEN-p : _a ) static char buf[CDR_BUF_LEN+1]; char *p = buf; un->len = CDR_ITEM_LEN(call->caller_un.len); un->s = p; if (un->len) { memcpy( p, call->caller_un.s, un->len ); p += un->len; } fid->len = CDR_ITEM_LEN(call->flow->id.len); fid->s = p; if (fid->len) { memcpy( p, call->flow->id.s, fid->len ); p += fid->len; } if (call->agent) { aid->len = CDR_ITEM_LEN(call->agent->id.len); aid->s = p; if (aid->len) { memcpy( p, call->agent->id.s, aid->len ); p += aid->len; } } else { aid->s = NULL; aid->len = 0; } return 0; } int cc_write_cdr( str *un, str *fid, str *aid, int type, int rt, int wt, int tt, int pt, int rej, int fst, int cid) { db_key_t columns[11]; db_val_t vals[11]; static db_ps_t my_ps = NULL; cc_acc_dbf.use_table( cc_acc_db_handle, &cc_cdrs_table_name); columns[0] = &ccc_caller_column; columns[1] = &ccc_recv_time_column; columns[2] = &ccc_wait_time_column; columns[3] = &ccc_talk_time_column; columns[4] = &ccc_pickup_time_column; columns[5] = &ccc_flow_id_column; columns[6] = &ccc_agent_id_column; columns[7] = &ccc_type_column; columns[8] = &ccc_rejected_column; columns[9]= &ccc_fstats_column; columns[10]= &ccc_cid_column; /* caller */ vals[0].nul = 0; vals[0].type = DB_STR; vals[0].val.str_val = *un; /* received timestamp */ vals[1].nul = 0; vals[1].type = DB_DATETIME; vals[1].val.time_val = startup_time + rt; /* wait time */ vals[2].nul = 0; vals[2].type = DB_INT; vals[2].val.int_val = wt; /* talk time */ vals[3].nul = 0; vals[3].type = DB_INT; vals[3].val.int_val = tt; /* pickup time */ vals[4].nul = 0; vals[4].type = DB_INT; vals[4].val.int_val = pt; /* flow ID */ vals[5].nul = 0; vals[5].type = DB_STR; vals[5].val.str_val = *fid; /* agent ID */ vals[6].type = DB_STR; if (aid->len==0) { vals[6].nul = 1; } else { vals[6].nul = 0; vals[6].val.str_val = *aid; } /* type */ vals[7].nul = 0; vals[7].type = DB_INT; vals[7].val.int_val = type; /* rej */ vals[8].nul = 0; vals[8].type = DB_INT; vals[8].val.int_val = rej; /* fstat */ vals[9].nul = 0; vals[9].type = DB_INT; vals[9].val.int_val = fst; /* cid */ vals[10].nul = 0; vals[10].type = DB_INT; vals[10].val.int_val = cid; CON_PS_REFERENCE(cc_acc_db_handle) = &my_ps; if (cc_acc_dbf.insert( cc_acc_db_handle, columns, vals, 11) < 0) { LM_ERR("CDR insert failed\n"); return -1; } return 0; } void cc_db_update_agent_end_call(struct cc_agent* agent) { db_key_t columns[2]; db_val_t vals[2]; columns[0] = &cca_agentid_column; columns[1] = &cca_lastcallend_column; vals[0].nul = 0; vals[0].type = DB_STR; vals[0].val.str_val = agent->id; vals[1].nul = 0; vals[1].type = DB_INT; vals[1].val.int_val = (int)time(NULL); cc_dbf.use_table( cc_db_handle, &cc_agent_table_name); if (cc_dbf.update( cc_db_handle, columns, 0, vals, columns+1, vals+1, 1, 1) < 0) { LM_ERR("Agent update failed\n"); } } opensips-2.2.2/modules/call_center/cc_db.h000077500000000000000000000066511300170765700205240ustar00rootroot00000000000000/* * call center module - call queuing and distribution * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- */ #ifndef CC_CC_DB_H_ #define CC_CC_DB_H_ #include "../../str.h" #include "cc_data.h" #define CC_FLOW_TABLE_NAME "cc_flows" #define CC_FLOW_TABLE_VERSION 1 #define CCF_FLOWID_COL "flowid" #define CCF_PRIORITY_ID_COL "priority" #define CCF_SKILL_COL "skill" #define CCF_CID_COL "prependcid" #define CCF_WELCOME_COL "message_welcome" #define CCF_M_QUEUE_COL "message_queue" #define CC_AGENT_TABLE_NAME "cc_agents" #define CC_AGENT_TABLE_VERSION 1 #define CCA_AGENTID_COL "agentid" #define CCA_LOCATION_ID_COL "location" #define CCA_SKILLS_COL "skills" #define CCA_LOGSTATE_COL "logstate" #define CCA_LASTCALLEND_COL "last_call_end" #define CC_GLOBALS_TABLE_NAME "cc_globals" #define CC_GLOBALS_TABLE_VERSION 1 #define CCG_NAME_COL "name" #define CCG_VALUE_COL "value" #define CC_CDRS_TABLE_NAME "cc_cdrs" #define CCC_CALLER_COL "caller" #define CCC_RECV_TIME_COL "received_timestamp" #define CCC_WAIT_TIME_COL "wait_time" #define CCC_TALK_TIME_COL "talk_time" #define CCC_PICKUP_TIME_COL "pickup_time" #define CCC_FLOW_ID_COL "flow_id" #define CCC_AGENT_ID_COL "agent_id" #define CCC_CC_ID_COL "callcenter_id" #define CCC_TYPE_COL "call_type" #define CCC_REJECTED_COL "rejected" #define CCC_FSTATS_COL "fstats" #define CCC_CID_COL "cid" #define CC_CALLS_TABLE_NAME "cc_calls" #define CCQ_STATE_COL "state" #define CCQ_IGCBACK_COL "ig_cback" #define CCQ_NOREJ_COL "no_rej" #define CCQ_SETUP_TIME_COL "setup_time" #define CCQ_ETA_COL "eta" #define CCQ_LAST_START_COL "last_start" #define CCQ_RECV_TIME_COL "recv_time" #define CCQ_CALLER_DN_COL "caller_dn" #define CCQ_CALLER_UN_COL "caller_un" #define CCQ_B2BUAID_COL "b2buaid" #define CCQ_FLOW_COL "flow" #define CCQ_AGENT_COL "agent" #define CCQ_QID_COL "qid" int init_cc_db(const str *db_url); int init_cc_acc_db(const str *acc_db_url); int cc_connect_db(const str *db_url); int cc_connect_acc_db(const str *acc_db_url); void cc_close_db(void); int cc_load_db_data( struct cc_data *data); int cc_write_cdr( str *un, str *fid, str *aid, int type, int rt, int wt, int tt , int pt, int rej, int fst, int cid); int prepare_cdr(struct cc_call *call, str *un, str *fid , str *aid); int cc_db_insert_call(struct cc_call *call); int cc_db_update_call(struct cc_call *call); int cc_db_delete_call(struct cc_call *call); int cc_db_restore_calls( struct cc_data *data); void cc_db_update_agent_end_call(struct cc_agent* agent); int b2bl_callback_customer(b2bl_cb_params_t *params, unsigned int event); int b2bl_callback_agent(b2bl_cb_params_t *params, unsigned int event); #endif opensips-2.2.2/modules/call_center/cc_queue.c000066400000000000000000000062001300170765700212410ustar00rootroot00000000000000/* * call center module - call queuing and distribution * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-03-17 initial version (bogdan) */ #include "cc_queue.h" extern stat_var *stg_terminated_calls; extern stat_var *stg_dist_incalls; /* this function must be call under * 1) general data lock as it accesses diverent data to calculate the next state * 2) call lock as it is changing the call state */ int cc_call_state_machine(struct cc_data *data, struct cc_call *call, str *leg) { struct cc_agent *agent; str *out = NULL; int state =0; switch (call->state) { case CC_CALL_NONE: /* next should be welcome msg if any */ if ( call->flow->recordings[ AUDIO_WELCOME ].len ) { LM_DBG("selecting WELCOME\n"); out = &(call->flow->recordings[ AUDIO_WELCOME ]); state = CC_CALL_WELCOME; break; } /* no Welcome message -> got for queue/agent */ case CC_CALL_WELCOME: case CC_CALL_QUEUED: /* search for an available agent */ agent = get_free_agent_by_skill( data, call->flow->skill); if (agent) { /* send it to agent */ LM_DBG("selecting AGENT %p (%.*s)\n",agent, agent->id.len, agent->id.s); state = CC_CALL_TOAGENT; out = &agent->location; LM_DBG("moved to TOAGENT from %d, out=%p\n", call->state, out); /* mark agent as used */ agent->state = CC_AGENT_INCALL; call->agent = agent; call->agent->ref_cnt++; update_stat( stg_dist_incalls, 1); update_stat( call->flow->st_dist_incalls, 1); call->fst_flags |= FSTAT_DIST; update_stat( call->agent->st_dist_incalls, +1); break; } else { /* put it into queue */ LM_DBG("selecting QUEUE\n"); out = &(call->flow->recordings[AUDIO_QUEUE]); state = CC_CALL_QUEUED; if(call->state == CC_CALL_QUEUED) { LM_DBG("State is already queued %p\n", call); break; } /* add it to queue */ cc_queue_push_call( data, call, 0); } break; case CC_CALL_TOAGENT: case CC_CALL_ENDED: LM_DBG("selecting END\n"); call->state = CC_CALL_ENDED; return 0; default: LM_CRIT("Bogus state [%p] [%d]\n", call, call->state); } if (out) { leg->s = (char*)pkg_malloc( out->len ); if (leg->s) { leg->len = out->len; memcpy(leg->s,out->s,out->len); call->prev_state = call->state; call->state = state; return 0; } } leg->s = NULL; leg->len = 0; return -1; } opensips-2.2.2/modules/call_center/cc_queue.h000066400000000000000000000021361300170765700212520ustar00rootroot00000000000000/* * call center module - call queuing and distribution * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-03-17 initial version (bogdan) */ #ifndef _CALL_CENTER_CC_QUEUE #define _CALL_CENTER_CC_QUEUE #include "cc_data.h" int cc_call_state_machine(struct cc_data *data, struct cc_call *call, str *leg); #endif opensips-2.2.2/modules/call_center/doc/000077500000000000000000000000001300170765700200535ustar00rootroot00000000000000opensips-2.2.2/modules/call_center/doc/call_center.xml000066400000000000000000000017621300170765700230560ustar00rootroot00000000000000 %docentities; ]> Call-Center Module &osipsname; Bogdan-Andrei Iancu
bogdan@opensips.org
Bogdan-Andrei Iancu
bogdan@opensips.org
2014 &osipssol;
&admin; &devel; &faq;
opensips-2.2.2/modules/call_center/doc/call_center_admin.xml000066400000000000000000000426761300170765700242370ustar00rootroot00000000000000 &adminguide;
Overview The Call Center module implements an inbound call center system with call flows (for queuing the received calls) and agents (for answering the calls). The module implements the queuing system, the call distribution to agents, agents managements, CDRs for the calls, statistics on call distribution and agent's activity - basically everything except the media playback (for the queue). This part must be provided via a third party media server (FreeSwitch, Asterisk or others).
How it works The main entities in the modules are the flows (queues) and agents.
DB tables Each entity has a corresponding table in the database, for provisioning purposes - the cc_flows and cc_agents tables, see DB schema. Data is loaded at startup and cached into memory ; runtime reload is possible via the MI commands (see the cc_reload command in ). Additionally there is a table cc_cdrs for writing the CDRs - this operation is done in realtime, after the call in completed, covering all possible cases: call was dropped while in queue, call was rejected by agent, call was accepted by agent, call terminated with error - NOTE that a call may generate more than one CDR (like call rejected by agent A, and redistributed and accepted by agent B). The cc_calls table is used to store ongoing calls, regardless it's state (in queue, to the agent, ended). It is populated at runtime by the module and queried at startup. This table should not be manually provisioned.
Call Flows A flow is defined by a unique alphanumerical ID - the main attribute of a flow is the skill - the skill is a capability required by the flow for an agent to be able to answer the call ; the concept of skills is the link between the flows and the agents - telling what agents are serving what flows - the flows require a skill, while the agents provide a set of skills. Agents matching the required skill of a flow will automatically receive calls from that flow. Additional, the flow has a priority - as agents may server multiple flows in the same time (based on skills), you can define priorities between the flows - if the flows has a higher priority, its calls will be pushed (in deliver to agents and queuing) in front of the calls from flows with a lower priority. Optionally, the flow may define a prependcid - a prefix to be added to the CLI (Caller ID) when the call is delivered to the agents - as an agent may receive call from multiple flows, it is important for the user to see which was the queue a call was received. In terms of media announcements, the flow defines the message_welcome (optional, to be played in the call, before doing anything with the call) and message_queue (mandatory, the looping message providing infinite on hold media IMPORTANT - this message must cycle and media server must never hung up on it. Both announcements are provided as SIP URIs (where the call has to be sent in order to get the playback).
Agents An agent is defined by a unique alphanumerical ID - the main attribute of an agent is its the set of skills and its SIP location. The set of skills will tell what calls to be received (from which flows, based on the skill matching); the location is a SIP URI where to call must be sent in order to be answered by the agent. Additionally, the agent has a initial logstate - if he is logged in or not (being logged in is a must in order to receive calls). The log state may be changed at runtime via a dedicated MI command cc_agent_login, see . There is a wrapup_time defined, saying the time interval for an agent before getting a new call from the system (after he finished a call).
Dependencies
&osips; Modules The following modules must be loaded before this module: b2b_logic - B2bUA module database - one of the SQL DB modules
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>db_url</varname> (string) SQL address to the DB server -- database specific. This must be the Database holding the provisioning tables (cc_flows, cc_agents and cc_calls tables). If not explicitly set, the global OpenSIPS DB URL will be used. Set <varname>db_url</varname> parameter ... modparam("call_center", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") ...
<varname>acc_db_url</varname> (string) SQL address to the DB server -- database specific. This must be the Database where the CDRs table (cc_cdrs) is located. If not explicitly set, the global OpenSIPS DB URL will be used. Set <varname>acc_db_url</varname> parameter ... modparam("call_center", "acc_db_url", "mysql://opensips:opensipsrw@localhost/opensips_cdrs") ...
<varname>b2b_scenario</varname> (string) The name of the B2B scenario that is used by the module for handling the calls in the queue. This is an advanced options and you should not change it unless you really understand what you are doing. The module provides an B2B scenario file scenario_callcenter.xml located in the module directory. The name of this scenario from this file (which must be loaded via the b2b_logic module) must match the b2b_scenario parameter. Default value is call center. Set <varname>b2b_scenario</varname> parameter ... modparam("b2b_logic", "script_scenario", "/etc/opensips/scenario_callcenter.xml") modparam("call_center", "b2b_scenario", "call center") ...
<varname>wrapup_time</varname> (integer) Time for an agent between finishing a call and receiving the next call from the system. Even if there are queued calls, the module will not deliver call to agent during this wrapup interval. Default value is 30 seconds. Set <varname>wrapup_time</varname> parameter ... modparam("call_center", "wrapup_time", 45) ...
Exported Functions
<function>cc_handle_call(flowID)</function> This must be used only for initial INVITE requests - the function pushes the call to be handled by the call center module (via a certain flow/queue). This function can be used from REQUEST_ROUTE. The flowID mandatory parameter is the ID of the flow to handle this call (push the call to that flow). This can be a variable or a static string. The function returns TRUE back to the script if the call was successfully pushed and handled by the Call Center engine. IMPORTANT: you must not do any signaling on the call (reply, relay) after this point. In case of error, FALSE is returned to the script with the following return codes: -1 - unable to get the flow ID from the parameter; -2 - unable to parse the FROM URI; -3 - flow with FlowID not found; -4 - no agents logged in the flow; -5 - internal error; <function>cc_handle_call</function> usage ... if (is_method("INVITE") and !has_totag()) { if (!cc_handle_call("tech_support")) { send_reply("403","Cannot handle call"); exit; } } ...
<function>cc_agent_login(agentID, state)</function> This function sets the login (on or off) state for an agent. This function can be used from REQUEST_ROUTE. The agentID mandatory parameter is the ID of the agent and the state is an integer value giving the new state - 0 means logged off, anything else means logged in. <function>cc_agent_login</function> usage ... # log off the 'agentX' agent cc_agent_login("agentX","0"); ...
Exported Statistics
Global statistics
ccg_incalls Total number of received calls. (counter type)
ccg_awt Global avg. waiting time for calls. (realtime type)
ccg_load Global load (across all flows). (realtime type)
ccg_distributed_incalls Total number of distributed calls. (counter type)
ccg_answered_incalls Total number of calls answered by agents. (counter type)
ccg_abandonned_incalls Total number of calls terminated by caller before being answered by agents. (counter type)
ccg_onhold_calls Total number of calls in the queues (onhold). (realtime type)
ccg_free_agents Total number of free agents (across all flows). (realtime type)
Per-flow statistics (one set for each flow)
ccf_incalls_flowID Number of received calls for the flow. (counter type)
ccf_dist_incalls_flowID Number of distributed calls in this flow. (counter type)
ccf_answ_incalls_flowID Nnumber of calls from the flow answered by agents. (counter type)
ccf_aban_incalls_flowID Number of calls (from the flow) terminated by caller before being answered by agents. (counter type)
ccf_onhold_incalls_flowID Number of calls (from the flow) which were put onhold. (counter type)
ccf_queued_calls_flowID Number of calls which are queued for this flow. (realtime type)
ccf_free_agents_flowID Number of free agents serving this flow. (realtime type)
ccf_etw_flowID Estimated Time to Wait for this flow. (realtime type)
ccf_awt_flowID Avg. Wating Time for this flow. (realtime type)
ccg_load_flowID The load on the flow (number of queued calls versus number of logged agents). (realtime type)
Per-agent statistics (one set for each agent)
cca_dist_incalls_agnetID Number of distributed calls to this agent. (counter type)
cca_answ_incalls_agentID Nnumber of calls answered by the agent. (counter type)
cca_aban_incalls_agentID Number of calls (sent to this agent) terminated by caller before being answered by agents. (counter type)
cca_att_agentID Avg. Talk Time for this agent (realtime type)
Exported MI Functions
<function moreinfo="none">cc_reload</function> Command to reload flows and agents definition from database. It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_reload
<function moreinfo="none">cc_agent_login</function> Command to login an agent into the Call Center engine. It takes two mandatory parameters, the ID of the agent and the new login state (0 - log off, 1 - log in) MI FIFO Command usage: opensipsctl fifo cc_agent_login agentX 0
<function moreinfo="none">cc_list_queue</function> Command to list all the calls in queuing - for each call, the following attributes will be printed: the flow of the call, for how long the call is in the queue, the ETW for the call, call priority and the call skill (inherited from the flow). It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_list_queue
<function moreinfo="none">cc_list_flows</function> Command to list all the flows - for each flow, the following attributes will be printed: the flow ID, the avg. call duration, how many calls were processed, how many agents are logged, and how many onging calls are. It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_list_flows
<function moreinfo="none">cc_list_agents</function> Command to list all the agents - for each agent, the following attributes will be printed: agent ID, agent login state and agent state (free, wrapup, incall). It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_list_agents
<function moreinfo="none">cc_list_calls</function> Command to list all the ongoing calls - for each call, the following attributes will be printed: call ID, call state (welcome, queued, toagent, ended), call duration, flow it belongs to, agent serving the call (if any). It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_list_agents
<function moreinfo="none">cc_reset_stats</function> Command to reset all counter-like statistics. It takes no parameter. MI FIFO Command usage: opensipsctl fifo cc_reset_stats
Exported pseudo-variables NONE
opensips-2.2.2/modules/call_center/doc/call_center_devel.xml000066400000000000000000000002511300170765700242250ustar00rootroot00000000000000 &develguide;
Available Functions NONE
opensips-2.2.2/modules/call_center/doc/call_center_faq.xml000066400000000000000000000023301300170765700236750ustar00rootroot00000000000000 &faqguide; Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/call_center/scenario_callcenter.xml000066400000000000000000000005411300170765700240270ustar00rootroot00000000000000 server1 client1 message 1 1 opensips-2.2.2/modules/call_control/000077500000000000000000000000001300170765700175065ustar00rootroot00000000000000opensips-2.2.2/modules/call_control/Makefile000066400000000000000000000003351300170765700211470ustar00rootroot00000000000000# $Id$ # # call_control module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=call_control.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/call_control/README000066400000000000000000000427751300170765700204050ustar00rootroot00000000000000Call Control Module Dan Pascu Edited by Dan Pascu Edited by Irina-Maria Stanescu Copyright © 2005-2008 Dan Pascu Revision History Revision $Revision: 6149 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Description 1.3. Features 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported parameters 1.5.1. disable (int) 1.5.2. socket_name (string) 1.5.3. socket_timeout (int) 1.5.4. signaling_ip_avp (string) 1.5.5. canonical_uri_avp (string) 1.5.6. diverter_avp (string) 1.5.7. prepaid_account_flag (string/integer) 1.5.8. call_limit_avp (string) 1.5.9. call_token_avp (string) 1.5.10. init (string) 1.5.11. start (string) 1.5.12. stop (string) 1.6. Exported Functions 1.6.1. call_control() List of Examples 1.1. Setting the disable parameter 1.2. Setting the socket_name parameter 1.3. Setting the socket_timeout parameter 1.4. Setting the signaling_ip_avp parameter 1.5. Setting the canonical_uri_avp parameter 1.6. Setting the diverter_avp parameter 1.7. Setting the prepaid_account_flag parameter 1.8. Setting the call_limit_avp parameter 1.9. Setting the call_token_avp parameter 1.10. Setting the init parameter 1.11. Setting the start parameter 1.12. Setting the stop parameter 1.13. Using the call_control function Chapter 1. Admin Guide 1.1. Overview This module allows one to limit the duration of calls and automatically end them when they exceed the imposed limit. Its main use case is to implement a prepaid system, but it can also be used to impose a global limit on all calls processed by the proxy. 1.2. Description Callcontrol consists of 3 components: * The OpenSIPS call_control module * An external application called callcontrol which keeps track of the calls that have a time limit and automatically ends them when they exceed it. This application receives requests from OpenSIPS and makes requests to a rating engine (see below) to find out if a call needs to be limited or not. When a call ends (or is ended) it will also instruct the rating engine to debit the balance for the caller with the consumed amount. The callcontrol application is available from http://callcontrol.ag-projects.com/ * A rating engine that is used to calculate the time limit based on the caller's credit and the destination price and to debit the caller's balance after a call ends. This is available as part of CDRTool from http://cdrtool.ag-projects.com/ The callcontrol application runs on the same machine as OpenSIPS and they communicate over a filesystem socket, while the rating engine can run on a different host and communicates with the callcontrol application using a TCP connection. Callcontrol is invoked by calling the call_control() function for the initial INVITE of every call we want to apply a limit to. This will end up as a request to the callcontrol application, which will interogate the rating engine for a time limit for the given caller and destination. The rating engine will determine if the destination has any associated cost and if the caller has any credit limit and if so will return the amount of time he is allowed to call that destination. Otherwise it will indicate that there is no limit associated with the call. If there is a limit, the callcontrol application will retain the session and attach a timer to it that will expire after the given time causing it to call back to OpenSIPS with a request to end the dialog. If the rating engine returns that there is no limit for the call, the session is discarded by the callcontrol application and it will allow it to go proceed any limit. An appropriate response is returned to the call_control module that is then returned by the call_control() function call and allows the script to make a decision based on the answer. 1.3. Features * Very simple API consisting of a single function that needs to be called once for the first INVITE of every call. The rest is done automatically in the background using dialog callbacks. * Gracefully end dialogs when they exceed their time by triggering a dlg_end_dlg request into the dialog module, that will generate two BYE messages towards each endpoint, ending the call cleanly. * Allow parallel sessions using one balance per subscriber * Integrates with mediaproxy's ability to detect when a call does timeout sending media and is closed. In this case the dlg_end_dlg that is triggered by mediaproxy will end the callcontrol session before it reaches the limit and consumes all the credit for a call that died and didn't actually take place. For this mediaproxy has to be used and it has to be started by engage_media_proxy() to be able to keep track of the call's dialog and end it on timeout. * Even when mediaproxy is unable to end the dialog because it was not started with engage_media_proxy(), the callcantrol application is still able to detect calls that did timeout sending media, by looking in the radius accounting records for entries recorded by mediaproxy for calls that did timeout. These calls will also be ended gracefully by the callcontrol application itself. * If the prepaid_account_flag module parameter is defined, the external application compares the OpenSIPS's and the rating engine's views on whether the account calling is prepaid or not and takes appropriate action if they conflict. This provides protection against frauds in case the rating engine malfunctions or there is an inconsistency in the database. * If the call_limit_avp is defined to a value greater than 0 it will be passed to the CallControl application, which will limit the number of concurrent calls the billing party (From user or diverter) is able to make. If the limit is reached the call_control function will return a specific error value. * The call_token_avp may be used to detect calls with a duplicated CallID that could create potential problems in call rating engines. 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * dialog module 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.5. Exported parameters 1.5.1. disable (int) Boolean flag that specifies if callcontrol should be disabled. This is useful when you want to use the same OpenSIPS configuration in two different context, one using callcontrol, the other not. In the case callcontrol is disabled, calls to the call_control() function will return a code indicating that there is no limit associated with the call, allowing the use of the same configuration without changes. Default value is “0â€. Example 1.1. Setting the disable parameter ... modparam("call_control", "disable", 1) ... 1.5.2. socket_name (string) It is the path to the filesystem socket where the callcontrol application listens for commands from the module. Default value is “/var/run/callcontrol/socketâ€. Example 1.2. Setting the socket_name parameter ... modparam("call_control", "socket_name", "/var/run/callcontrol/socket") ... 1.5.3. socket_timeout (int) How much time (in milliseconds) to wait for an answer from the callcontrol application. Default value is “500†(ms). Example 1.3. Setting the socket_timeout parameter ... modparam("call_control", "socket_timeout", 500) ... 1.5.4. signaling_ip_avp (string) Specification of the AVP which holds the IP address from where the SIP signaling originated. If this AVP is set it will be used to get the signaling IP address, else the source IP address from where the SIP message was received will be used. This AVP is meant to be used in cases where there are more than one proxy in the call setup path and the proxy that actually starts callcontrol doesn't receive the SIP messages directly from the UA and it cannot determine the NAT IP address from where the signaling originated. In such a case attaching a SIP header at the first proxy and then copying that header's value into the signaling_ip_avp on the proxy that starts callcontrol will allow it to get the correct NAT IP address from where the SIP signaling originated. This is used by the rating engine which finds the rates to apply to a call based on caller's SIP URI, caller's SIP domain or caller's IP address (whichever yields a rate forst, in this order). Default value is “$avp(cc_signaling_ip)â€. Example 1.4. Setting the signaling_ip_avp parameter ... modparam("call_control", "signaling_ip_avp", "$avp(cc_signaling_ip)") ... 1.5.5. canonical_uri_avp (string) Specification of the AVP which holds an optional application defined canonical request URI. When this is set, it will be used as the destination when computing the call price, otherwise the request URI will be used. This is useful when the username of the ruri needs to have a different, canonical form in the rating engine computation than it has in the ruri. Default value is “$avp(cc_can_uri)â€. Example 1.5. Setting the canonical_uri_avp parameter ... modparam("call_control", "canonical_uri_avp", "$avp(cc_can_uri)") ... 1.5.6. diverter_avp (string) Specification of the AVP which holds an optional application defined diverter SIP URI. When this is set, it will be used by the rating engine as the billing party when finding the rates to apply to a given call, otherwise, the caller's URI taken from the From field will be used. When set, this AVP should contain a value in the form “user@domain†(no sip: prefix should be used). This is useful when a destination diverts a call, thus becoming the new caller. In this case the billing party is the diverter and this AVP should be set to it, to allow the rating engine to pick the right rates for the call. For example, if A calls B and B diverts all its calls unconditionally to C, then the diverter AVP should the set to B's URI, because B is the billing party in the call not A after the call was diverted. Default value is “$avp(diverter)â€. Example 1.6. Setting the diverter_avp parameter ... modparam("call_control", "diverter_avp", "$avp(diverter)") route { ... # alice@example.com is paying for this call $avp(diverter) = "alice@example.com"; ... } ... 1.5.7. prepaid_account_flag (string/integer) The flag that is used to specify whether the account making the call is prepaid or postpaid. Setting this to a non-null value will determine the module to pass the flag's value to the external application. This will allow the external application to compare OpenSIPS's and the rating engine's views on whether the account calling is prepaid or not and take appropriate action if they conflict. The flag should be set from the OpenSIPS configuration for a prepaid account and reset for a postpaid one. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is “NULL†(undefined). Example 1.7. Setting the prepaid_account_flag parameter ... modparam("call_control", "prepaid_account_flag", "PP_ACC_FLAG") ... 1.5.8. call_limit_avp (string) Specification of the AVP which holds an optional application defined call limit. When this is set, it will be passed to the CallControl application and if the limit is reached the call_control function will return an error code of -4. Default value is “$avp(cc_call_limit)â€. Example 1.8. Setting the call_limit_avp parameter ... modparam("call_control", "call_limit_avp", "$avp(cc_call_limit)") ... 1.5.9. call_token_avp (string) Specification of the AVP which holds an optional application defined token. This token will be used to check if two calls with the same CallID actually refer to the same call. If call_control() is called multiple times for the same call (thus same CallID) the token needs to be the same or call_control will return -3 error, indicating that the CallID is duplicated. Default value is “$avp(cc_call_token)â€. Example 1.9. Setting the call_token_avp parameter ... modparam("call_control", "call_token_avp", "$avp(cc_call_token)") ... $avp(cc_call_token) := $RANDOM; ... 1.5.10. init (string) This parameter is used to describe custom call control initialize messages. It represents a list of key value pairs and has the following format: * "string1 = var1 [string2 = var2]*" The left-hand side of the assignment can be any string. The right-hand side of the assignment must be a script pseudo variable or a script AVP. For more information about them see CookBooks - Scripting Variables. If the parameter is not set, the default initialize message is sent. Default value is “NULLâ€. Example 1.10. Setting the init parameter ... modparam("call_control", "init", "call-id=$ci to=$tu from=$fu authruri=$du another_field = $avp(10)") ... 1.5.11. start (string) This parameter is used to describe custom call control start messages. It represents a list of key value pairs and has the following format: * "string1 = var1 [string2 = var2]*" The left-hand side of the assignment can be any string. The right-hand side of the assignment must be a script pseudo variable or a script AVP. For more information about them see CookBooks - Scripting Variables. If the parameter is not set, the default start message is sent. Default value is “NULLâ€. Example 1.11. Setting the start parameter ... modparam("call_control", "start", "call-id=$ci to=$tu from=$fu authruri=$du another_field = $avp(10)") ... 1.5.12. stop (string) This parameter is used to describe custom call control stop messages. It represents a list of key value pairs and has the following format: * "string1 = var1 [string2 = var2]*" The left-hand side of the assignment can be any string. The right-hand side of the assignment must be a script pseudo variable or a script AVP. For more information about them see CookBooks - Scripting Variables. If the parameter is not set, the default stop message is sent. Default value is “NULLâ€. Example 1.12. Setting the stop parameter ... modparam("call_control", "stop", "call-id=$ci to=$tu from=$fu authruri=$du another_field = $avp(10)") ... 1.6. Exported Functions 1.6.1. call_control() Trigger the use of callcontrol for the dialog started by the INVITE for which this function is called (the function should only be called for the first INVITE of a call). Further in-dialog requests will be processed automatically using internal bindings into the dialog state machine, allowing callcontrol to update its internal state as the dialog progresses, without any other intervention from the script. This function should be called right before the message is sent out using t_relay(), when all the request uri modifications are over and a final destination has been determined. This function has the following return codes: * +2 - call has no limit * +1 - call has limit and is traced by callcontrol * -1 - not enough credit to make the call * -2 - call is locked by another call in progress * -3 - duplicated callid * -4 - call limit has been reached * -5 - internal error (message parsing, communication, ...) This function can be used from REQUEST_ROUTE. Example 1.13. Using the call_control function ... if (is_avp_set("$avp(805)")) { # the diverter AVP is set, use it as billing party $avp(billing_party_domain) = $(avp(805){uri.domain}); } else { $avp(billing_party_domain) = $fd; } if (method==INVITE && !has_totag() && is_domain_local("$avp(billing_party_domain)")) { call_control(); switch ($retcode) { case 2: # Call with no limit case 1: # Call has limit and is under callcontrol management break; case -1: # Not enough credit (prepaid call) sl_send_reply("402", "Not enough credit"); exit; break; case -2: # Locked by another call in progress (prepaid call) sl_send_reply("403", "Call locked by another call in progress"); exit; break; case -3: # Duplicated callid sl_send_reply("400", "Duplicated callid"); exit; break; case -4: # Call limit reached sl_send_reply("503", "Too many concurrent calls"); exit; break; default: # Internal error (message parsing, communication, ...) if (PREPAID_ACCOUNT) { xlog("Call control: internal server error\n"); sl_send_reply("500", "Internal server error"); exit; } else { xlog("L_WARN", "Cannot set time limit for postpaid call\n"); } } } t_relay(); ... opensips-2.2.2/modules/call_control/call_control.c000066400000000000000000001027361300170765700223360ustar00rootroot00000000000000/* * Copyright (C) 2005-2008 Dan Pascu * * This file is part of OpenSIPS, a free SIP server. * * OpenSIPS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the OpenSIPS software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * OpenSIPS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../str.h" #include "../../pvar.h" #include "../../ut.h" #include "../../trim.h" #include "../../script_cb.h" #include "../../parser/digest/digest.h" #include "../../parser/parse_from.h" #include "../dialog/dlg_load.h" #include "../dialog/dlg_hash.h" #include "../tm/tm_load.h" #if defined(__GNUC__) && !defined(__STRICT_ANSI__) # define INLINE inline #else # define INLINE #endif #define CANONICAL_URI_AVP_SPEC "$avp(cc_can_uri)" #define SIGNALING_IP_AVP_SPEC "$avp(cc_signaling_ip)" #define DIVERTER_AVP_SPEC "$avp(cc_diverter)" #define CALL_LIMIT_AVP_SPEC "$avp(cc_call_limit)" #define CALL_TOKEN_AVP_SPEC "$avp(cc_call_token)" // Although `AF_LOCAL' is mandated by POSIX.1g, `AF_UNIX' is portable to // more systems. `AF_UNIX' was the traditional name stemming from BSD, so // even most POSIX systems support it. It is also the name of choice in // the Unix98 specification. So if there's no AF_LOCAL fallback to AF_UNIX #ifndef AF_LOCAL # define AF_LOCAL AF_UNIX #endif // Solaris does not have the MSG_NOSIGNAL flag for the send(2) syscall #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 #endif typedef int Bool; #define True 1 #define False 0 typedef struct AVP_Param { str spec; int name; unsigned short type; } AVP_Param; typedef struct AVP_List { pv_spec_p pv; str name; struct AVP_List *next; } AVP_List; #define RETRY_INTERVAL 10 #define BUFFER_SIZE 8192 typedef struct CallControlSocket { char *name; // name int sock; // socket int timeout; // how many milliseconds to wait for an answer time_t last_failure; // time of the last failure char data[BUFFER_SIZE]; // buffer for the answer data } CallControlSocket; /* Function prototypes */ static int CallControl(struct sip_msg *msg, char *str1, char *str2); static int mod_init(void); static int child_init(int rank); static void destroy(void); int parse_param_init(unsigned int type, void *val); int parse_param_start(unsigned int type, void *val); int parse_param_stop(unsigned int type, void *val); /* Local global variables */ static CallControlSocket callcontrol_socket = { "/var/run/callcontrol/socket", // name -1, // sock 500, // timeout in 500 milliseconds if there is no answer 0, // time of the last failure "" // data }; static int disable = False; /* The AVP where the diverter URI is stored (if defined) */ static AVP_Param diverter_avp = {str_init(DIVERTER_AVP_SPEC), -1, 0}; /* The AVP where the canonical URI is stored (if defined) */ static AVP_Param canonical_uri_avp = {str_init(CANONICAL_URI_AVP_SPEC), -1, 0}; /* The AVP where the caller signaling IP is stored (if defined) */ static AVP_Param signaling_ip_avp = {str_init(SIGNALING_IP_AVP_SPEC), -1, 0}; /* The AVP where the call limit is stored (if defined) */ static AVP_Param call_limit_avp = {str_init(CALL_LIMIT_AVP_SPEC), -1, 0}; /* The AVP where the call token is stored (if defined) */ static AVP_Param call_token_avp = {str_init(CALL_TOKEN_AVP_SPEC), -1, 0}; struct dlg_binds dlg_api; static int prepaid_account_flag = -1; static char *prepaid_account_str = 0; AVP_List *init_avps = NULL, *start_avps = NULL, *stop_avps = NULL; pv_elem_t *model; static cmd_export_t commands[] = { {"call_control", (cmd_function)CallControl, 0, 0, 0, REQUEST_ROUTE }, {0, 0, 0, 0, 0, 0} }; static param_export_t parameters[] = { {"init", STR_PARAM|USE_FUNC_PARAM, (void*)parse_param_init}, {"start", STR_PARAM|USE_FUNC_PARAM, (void*)parse_param_start}, {"stop", STR_PARAM|USE_FUNC_PARAM, (void*)parse_param_stop}, {"disable", INT_PARAM, &disable}, {"socket_name", STR_PARAM, &(callcontrol_socket.name)}, {"socket_timeout", INT_PARAM, &(callcontrol_socket.timeout)}, {"diverter_avp", STR_PARAM, &(diverter_avp.spec.s)}, {"canonical_uri_avp", STR_PARAM, &(canonical_uri_avp.spec.s)}, {"signaling_ip_avp", STR_PARAM, &(signaling_ip_avp.spec.s)}, {"call_limit_avp", STR_PARAM, &(call_limit_avp.spec.s)}, {"call_token_avp", STR_PARAM, &(call_token_avp.spec.s)}, {"prepaid_account_flag", STR_PARAM, &prepaid_account_str}, {"prepaid_account_flag", INT_PARAM, &prepaid_account_flag}, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "dialog", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "call_control", // module name MOD_TYPE_DEFAULT,// class of this module MODULE_VERSION, // module version DEFAULT_DLFLAGS, // dlopen flags &deps, // OpenSIPS module dependencies commands, // exported functions 0, // exported async functions parameters, // exported parameters NULL, // exported statistics NULL, // exported MI functions NULL, // exported pseudo-variables NULL, // extra processes mod_init, // module init function (before fork. kids will inherit) NULL, // reply processing function destroy, // destroy function child_init // child init function }; typedef enum CallControlAction { CAInitialize = 1, CAStart, CAStop } CallControlAction; typedef struct Contact { str username; str ip; str port; } Contact; typedef struct DialogID { unsigned int h_entry; unsigned int h_id; } DialogID; typedef struct CallInfo { CallControlAction action; DialogID dialog_id; str ruri; str diverter; str source_ip; str callid; str from; str from_tag; str call_token; char* prepaid_account; int call_limit; } CallInfo; #define CHECK_COND(cond) \ if ((cond) == 0) { \ LM_ERR("malformed modparam\n"); \ return -1; \ } #define CHECK_ALLOC(p) \ if (!(p)) { \ LM_ERR("no memory left\n"); \ return -1; \ } void destroy_list(AVP_List *list) { AVP_List *cur, *next; cur = list; while (cur) { next = cur->next; pkg_free(cur); cur = next; } } int parse_param(void *val, AVP_List** avps) { char *p; str *s, content; AVP_List *mp = NULL; //LM_DBG("%.*s\n", content.len, content.s); content.s = (char*) val; content.len = strlen(content.s); p = (char*) pkg_malloc (content.len + 1); CHECK_ALLOC(p); p[content.len] = '\0'; memcpy(p, content.s, content.len); s = (str*) pkg_malloc(sizeof(str)); CHECK_ALLOC(s); for (;*p != '\0';) { mp = (AVP_List*) pkg_malloc (sizeof(AVP_List)); CHECK_ALLOC(mp); mp->next = *avps; mp->pv = (pv_spec_p) pkg_malloc (sizeof(pv_spec_t)); CHECK_ALLOC(mp->pv); for (; isspace(*p); p++); CHECK_COND(*p != '\0'); mp->name.s = p; for(; isgraph(*p) && *p != '='; p++) CHECK_COND(*p != '\0'); mp->name.len = p - mp->name.s; for (; isspace(*p); p++); CHECK_COND(*p != '\0' && *p == '='); p++; //LM_DBG("%.*s\n", mp->name.len, mp->name.s); for (; isspace(*p); p++); CHECK_COND(*p != '\0' && *p == '$'); s->s = p; s->len = strlen(p); p = pv_parse_spec(s, mp->pv); for (; isspace(*p); p++); *avps = mp; } return 0; } int parse_param_init(unsigned int type, void *val) { if (parse_param(val, &init_avps) == -1) return E_CFG; return 0; } int parse_param_start(unsigned int type, void *val) { if (parse_param(val, &start_avps) == -1) return E_CFG; return 0; } int parse_param_stop(unsigned int type, void *val) { if (parse_param(val, &stop_avps) == -1) return E_CFG; return 0; } // Message checking and parsing // static Bool has_to_tag(struct sip_msg *msg) { str tag; if (!msg->to) { if (parse_headers(msg, HDR_TO_F, 0)==-1) { LM_ERR("cannot parse 'To' header\n"); return False; } if (!msg->to) { LM_ERR("missing 'To' header\n"); return False; } } tag = get_to(msg)->tag_value; if (tag.s==NULL || tag.len==0) { return False; } return True; } // Get canonical request URI static str get_canonical_request_uri(struct sip_msg* msg) { int_str value; if (!search_first_avp(canonical_uri_avp.type | AVP_VAL_STR, canonical_uri_avp.name, &value, NULL) || value.s.s==NULL || value.s.len==0) { return *GET_RURI(msg); } return value.s; } // Get caller signaling IP static str get_signaling_ip(struct sip_msg* msg) { int_str value; if (!search_first_avp(signaling_ip_avp.type | AVP_VAL_STR, signaling_ip_avp.name, &value, NULL) || !value.s.s || value.s.len==0) { value.s.s = ip_addr2a(&msg->rcv.src_ip); value.s.len = strlen(value.s.s); } return value.s; } static str get_diverter(struct sip_msg *msg) { struct hdr_field *header; dig_cred_t *credentials; int_str avpvalue; static str diverter; diverter.s = "None"; diverter.len = 4; if (search_first_avp(diverter_avp.type|AVP_VAL_STR, diverter_avp.name, &avpvalue, NULL)) { // have a diverted call diverter = avpvalue.s; } else { get_authorized_cred(msg->proxy_auth, &header); if (header) { credentials = &((auth_body_t*)(header->parsed))->digest; } else { if (parse_headers(msg, HDR_PROXYAUTH_F, 0) == -1) { LM_ERR("cannot parse Proxy-Authorization header\n"); return diverter; } if (!msg->proxy_auth) return diverter; if (parse_credentials(msg->proxy_auth) != 0) { LM_ERR("cannot parse credentials\n"); return diverter; } credentials = &((auth_body_t*)(msg->proxy_auth->parsed))->digest; } if (credentials->username.user.len > 0 && credentials->username.domain.len > 0 && credentials->realm.len == 0 && credentials->nonce.len == 0 && credentials->response.len == 0) { // this is a call diverted from the failure route // and sent back to proxy with append_pa_hf() diverter = credentials->username.whole; } } return diverter; } static int get_call_limit(struct sip_msg* msg) { int_str value; struct usr_avp *avp; avp = search_first_avp(call_limit_avp.type, call_limit_avp.name, &value, NULL); if (!avp) return 0; if (avp->flags & AVP_VAL_STR) { return atoi(value.s.s); } else { return value.n; } } static str get_call_token(struct sip_msg* msg) { int_str value; struct usr_avp *avp; static str call_token; call_token.s = "None"; call_token.len = 4; avp = search_first_avp(call_token_avp.type, call_token_avp.name, &value, NULL); if (avp) { if (avp->flags & AVP_VAL_STR) { call_token = value.s; } else { call_token.s = int2str(value.n, &call_token.len); } } return call_token; } static CallInfo* get_call_info(struct sip_msg *msg, CallControlAction action) { static CallInfo call_info; int headers; memset(&call_info, 0, sizeof(struct CallInfo)); switch (action) { case CAInitialize: headers = HDR_CALLID_F|HDR_FROM_F; break; case CAStart: case CAStop: headers = HDR_CALLID_F; break; default: // Invalid action. Should never get here. assert(False); return NULL; } if (parse_headers(msg, headers, 0) == -1) { LM_ERR("cannot parse required headers\n"); return NULL; } if (headers & HDR_CALLID_F) { if (msg->callid == NULL) { LM_ERR("missing Call-ID header\n"); return NULL; } call_info.callid = msg->callid->body; trim(&call_info.callid); } if (headers & HDR_FROM_F) { struct to_body *from; // yeah. suggestive structure name ;) if (msg->from == NULL) { LM_ERR("missing From header\n"); return NULL; } if (!msg->from->parsed && parse_from_header(msg)==-1) { LM_ERR("cannot parse From header\n"); return NULL; } from = get_from(msg); if (from->body.s==NULL || from->body.len==0) { LM_ERR("missing From\n"); return NULL; } if (from->tag_value.s==NULL || from->tag_value.len==0) { LM_ERR("missing From tag\n"); return NULL; } call_info.from = from->body; call_info.from_tag = from->tag_value; } if (action == CAInitialize) { call_info.ruri = get_canonical_request_uri(msg); call_info.diverter = get_diverter(msg); call_info.source_ip = get_signaling_ip(msg); call_info.call_limit = get_call_limit(msg); call_info.call_token = get_call_token(msg); if (prepaid_account_flag >= 0) { call_info.prepaid_account = isflagset(msg, prepaid_account_flag)==1 ? "true" : "false"; } else { call_info.prepaid_account = "unknown"; } } call_info.action = action; return &call_info; } static char* make_custom_request(struct sip_msg *msg, CallInfo *call) { static char request[8192]; int len = 0; AVP_List *al; pv_value_t pt; switch (call->action) { case CAInitialize: al = init_avps; break; case CAStart: al = start_avps; break; case CAStop: al = stop_avps; break; default: // should never get here, but keep gcc from complaining assert(False); return NULL; } for (; al; al = al->next) { pv_get_spec_value(msg, al->pv, &pt); if (pt.flags & PV_VAL_INT) { len += snprintf(request + len, sizeof(request), "%.*s = %d ", al->name.len, al->name.s, pt.ri); } else if (pt.flags & PV_VAL_STR) { len += snprintf(request + len, sizeof(request), "%.*s = %.*s ", al->name.len, al->name.s, pt.rs.len, pt.rs.s); } if (len >= sizeof(request)) { LM_ERR("callcontrol request is longer than %ld bytes\n", (unsigned long)sizeof(request)); return NULL; } } return request; } static char* make_default_request(CallInfo *call) { static char request[8192]; int len; switch (call->action) { case CAInitialize: len = snprintf(request, sizeof(request), "init\r\n" "ruri: %.*s\r\n" "diverter: %.*s\r\n" "sourceip: %.*s\r\n" "callid: %.*s\r\n" "from: %.*s\r\n" "fromtag: %.*s\r\n" "prepaid: %s\r\n" "call_limit: %d\r\n" "call_token: %.*s\r\n" "\r\n", call->ruri.len, call->ruri.s, call->diverter.len, call->diverter.s, call->source_ip.len, call->source_ip.s, call->callid.len, call->callid.s, call->from.len, call->from.s, call->from_tag.len, call->from_tag.s, call->prepaid_account, call->call_limit, call->call_token.len, call->call_token.s); if (len >= sizeof(request)) { LM_ERR("callcontrol request is longer than %ld bytes\n", (unsigned long)sizeof(request)); return NULL; } break; case CAStart: len = snprintf(request, sizeof(request), "start\r\n" "callid: %.*s\r\n" "dialogid: %d:%d\r\n" "\r\n", call->callid.len, call->callid.s, call->dialog_id.h_entry, call->dialog_id.h_id); if (len >= sizeof(request)) { LM_ERR("callcontrol request is longer than %ld bytes\n", (unsigned long)sizeof(request)); return NULL; } break; case CAStop: len = snprintf(request, sizeof(request), "stop\r\n" "callid: %.*s\r\n" "\r\n", call->callid.len, call->callid.s); if (len >= sizeof(request)) { LM_ERR("callcontrol request is longer than %ld bytes\n", (unsigned long)sizeof(request)); return NULL; } break; default: // should never get here, but keep gcc from complaining assert(False); return NULL; } return request; } // Functions dealing with the external call_control helper // static Bool callcontrol_connect(void) { struct sockaddr_un addr; if (callcontrol_socket.sock >= 0) return True; if (callcontrol_socket.last_failure + RETRY_INTERVAL > time(NULL)) return False; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; strncpy(addr.sun_path, callcontrol_socket.name, sizeof(addr.sun_path) - 1); #ifdef HAVE_SOCKADDR_SA_LEN addr.sun_len = strlen(addr.sun_path); #endif callcontrol_socket.sock = socket(AF_LOCAL, SOCK_STREAM, 0); if (callcontrol_socket.sock < 0) { LM_ERR("can't create socket\n"); callcontrol_socket.last_failure = time(NULL); return False; } if (connect(callcontrol_socket.sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { LM_ERR("failed to connect to %s: %s\n", callcontrol_socket.name, strerror(errno)); close(callcontrol_socket.sock); callcontrol_socket.sock = -1; callcontrol_socket.last_failure = time(NULL); return False; } return True; } static void callcontrol_disconnect(void) { if (callcontrol_socket.sock < 0) return; close(callcontrol_socket.sock); callcontrol_socket.sock = -1; callcontrol_socket.last_failure = time(NULL); } static char* send_command(char *command) { int cmd_len, bytes, tries, sent, received, count; struct timeval timeout; fd_set rset; if (!callcontrol_connect()) return NULL; cmd_len = strlen(command); for (sent=0, tries=0; sentdialog_id.h_entry = dlg->h_entry; call->dialog_id.h_id = dlg->h_id; if (!start_avps) message = make_default_request(call); else message = make_custom_request(msg, call); if (!message) return -5; result = send_command(message); if (result==NULL) { return -5; } else if (strcasecmp(result, "Ok\r\n")==0) { return 1; } else if (strcasecmp(result, "Not found\r\n")==0) { return -1; } else { return -5; } } // Called during a dialog ending to stop callcontrol // // Return codes: // 1 - Ok // -1 - Session not found // -5 - Internal error (message parsing, communication, ...) static int call_control_stop(struct sip_msg *msg, str callid) { CallInfo call; char *message, *result; call.action = CAStop; call.callid = callid; if (!stop_avps) message = make_default_request(&call); else message = make_custom_request(msg, &call); if (!message) return -5; result = send_command(message); if (result==NULL) { return -5; } else if (strcasecmp(result, "Ok\r\n")==0) { return 1; } else if (strcasecmp(result, "Not found\r\n")==0) { return -1; } else { return -5; } } // Dialog callbacks and helpers // typedef enum { CCInactive = 0, CCActive } CallControlState; static void __dialog_replies(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { struct sip_msg *reply = _params->msg; if (reply!=FAKED_REPLY && reply->REPLY_STATUS==200) { call_control_start(reply, dlg); } } static void __dialog_ended(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { if ((int)(long)*_params->param == CCActive) { call_control_stop(_params->msg, dlg->callid); *_params->param = (void *)CCInactive; } } static void __dialog_loaded(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { if (dlg_api.register_dlgcb(dlg, DLGCB_RESPONSE_FWDED, __dialog_replies, NULL, NULL) != 0) LM_ERR("cannot register callback for dialog confirmation\n"); if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED | DLGCB_DESTROY, __dialog_ended, (void*)CCActive, NULL) != 0) LM_ERR("cannot register callback for dialog termination\n"); } // Public API // // Return codes: // 2 - No limit // 1 - Limited // -1 - No credit // -2 - Locked // -3 - Duplicated callid // -4 - Call limit reached // -5 - Internal error (message parsing, communication, ...) static int CallControl(struct sip_msg *msg, char *str1, char *str2) { int result; struct dlg_cell *dlg; CallInfo *call; if (disable) return 2; if (msg->first_line.type!=SIP_REQUEST || msg->REQ_METHOD!=METHOD_INVITE || has_to_tag(msg)) { LM_WARN("call_control should only be called for the first INVITE\n"); return -5; } result = call_control_initialize(msg); if (result == 1) { // A call with a time limit that will be traced by callcontrol if (dlg_api.create_dlg(msg, 0) < 0) { LM_ERR("could not create new dialog\n"); call = get_call_info(msg, CAStop); if (!call) { LM_ERR("can't retrieve call info\n"); return -5; } call_control_stop(msg, call->callid); return -5; } dlg = dlg_api.get_dlg(); if (!dlg) { LM_CRIT("error getting dialog\n"); call = get_call_info(msg, CAStop); if (!call) { LM_ERR("can't retrieve call info\n"); return -5; } call_control_stop(msg, call->callid); return -5; } if (dlg_api.register_dlgcb(dlg, DLGCB_RESPONSE_FWDED, __dialog_replies, NULL, NULL) != 0) { LM_ERR("cannot register callback for dialog confirmation\n"); call_control_stop(msg, dlg->callid); return -5; } if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED | DLGCB_DESTROY, __dialog_ended, (void*)CCActive, NULL) != 0) { LM_ERR("cannot register callback for dialog termination\n"); call_control_stop(msg, dlg->callid); return -5; } } return result; } // Module management: initialization/destroy/function-parameter-fixing/... // static int mod_init(void) { pv_spec_t avp_spec; // initialize the canonical_uri_avp structure if (canonical_uri_avp.spec.s==NULL || *(canonical_uri_avp.spec.s)==0) { LM_ERR("missing/empty canonical_uri_avp parameter. using default.\n"); canonical_uri_avp.spec.s = CANONICAL_URI_AVP_SPEC; } canonical_uri_avp.spec.len = strlen(canonical_uri_avp.spec.s); if (pv_parse_spec(&(canonical_uri_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_CRIT("invalid AVP specification for canonical_uri_avp: `%s'\n", canonical_uri_avp.spec.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &(canonical_uri_avp.name), &(canonical_uri_avp.type))!=0) { LM_CRIT("invalid AVP specification for canonical_uri_avp: `%s'\n", canonical_uri_avp.spec.s); return -1; } // initialize the signaling_ip_avp structure if (signaling_ip_avp.spec.s==NULL || *(signaling_ip_avp.spec.s)==0) { LM_ERR("missing/empty signaling_ip_avp parameter. using default.\n"); signaling_ip_avp.spec.s = SIGNALING_IP_AVP_SPEC; } signaling_ip_avp.spec.len = strlen(signaling_ip_avp.spec.s); if (pv_parse_spec(&(signaling_ip_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_CRIT("invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &(signaling_ip_avp.name), &(signaling_ip_avp.type))!=0) { LM_CRIT("invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s); return -1; } // initialize the call_limit_avp structure if (call_limit_avp.spec.s==NULL || *(call_limit_avp.spec.s)==0) { LM_ERR("missing/empty call_limit_avp parameter. using default.\n"); call_limit_avp.spec.s = CALL_LIMIT_AVP_SPEC; } call_limit_avp.spec.len = strlen(call_limit_avp.spec.s); if (pv_parse_spec(&(call_limit_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_CRIT("invalid AVP specification for call_limit_avp: `%s'\n", call_limit_avp.spec.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &(call_limit_avp.name), &(call_limit_avp.type))!=0) { LM_CRIT("invalid AVP specification for call_limit_avp: `%s'\n", call_limit_avp.spec.s); return -1; } // initialize the call_token_avp structure if (call_token_avp.spec.s==NULL || *(call_token_avp.spec.s)==0) { LM_ERR("missing/empty call_token_avp parameter. using default.\n"); call_token_avp.spec.s = CALL_TOKEN_AVP_SPEC; } call_token_avp.spec.len = strlen(call_token_avp.spec.s); if (pv_parse_spec(&(call_token_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_CRIT("invalid AVP specification for call_token_avp: `%s'\n", call_token_avp.spec.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &(call_token_avp.name), &(call_token_avp.type))!=0) { LM_CRIT("invalid AVP specification for call_token_avp: `%s'\n", call_token_avp.spec.s); return -1; } // initialize the diverter_avp structure if (diverter_avp.spec.s==NULL || *(diverter_avp.spec.s)==0) { LM_ERR("missing/empty diverter_avp parameter. using default.\n"); diverter_avp.spec.s = DIVERTER_AVP_SPEC; } diverter_avp.spec.len = strlen(diverter_avp.spec.s); if (pv_parse_spec(&(diverter_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_CRIT("invalid AVP specification for diverter_avp: `%s'\n", diverter_avp.spec.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &(diverter_avp.name), &(diverter_avp.type))!=0) { LM_CRIT("invalid AVP specification for diverter_avp: `%s'\n", diverter_avp.spec.s); return -1; } // bind to the dialog API if (load_dlg_api(&dlg_api)!=0) { LM_CRIT("cannot load the dialog module API\n"); return -1; } // register dialog loading callback if (dlg_api.register_dlgcb(NULL, DLGCB_LOADED, __dialog_loaded, NULL, NULL) != 0) { LM_CRIT("cannot register callback for dialogs loaded from the database\n"); } fix_flag_name(prepaid_account_str, prepaid_account_flag); prepaid_account_flag = get_flag_id_by_name(FLAG_TYPE_MSG, prepaid_account_str); return 0; } static int child_init(int rank) { // initialize the connection to callcontrol if needed if (!disable) callcontrol_connect(); return 0; } static void destroy(void) { if (init_avps) destroy_list(init_avps); if (start_avps) destroy_list(start_avps); if (stop_avps) destroy_list(stop_avps); } opensips-2.2.2/modules/call_control/doc/000077500000000000000000000000001300170765700202535ustar00rootroot00000000000000opensips-2.2.2/modules/call_control/doc/call_control.xml000066400000000000000000000021671300170765700234560ustar00rootroot00000000000000 %docentities; ]> Call Control Module Dan Pascu dan@ag-projects.com Dan Pascu dan@ag-projects.com Irina-Maria Stanescu ironmissy@gmail.com 2005-2008 Dan Pascu $Revision: 6149 $ $Date$ &admin; &faq; opensips-2.2.2/modules/call_control/doc/call_control_admin.xml000066400000000000000000000560501300170765700246260ustar00rootroot00000000000000 &adminguide;
Overview This module allows one to limit the duration of calls and automatically end them when they exceed the imposed limit. Its main use case is to implement a prepaid system, but it can also be used to impose a global limit on all calls processed by the proxy.
Description Callcontrol consists of 3 components: The &osips; call_control module An external application called callcontrol which keeps track of the calls that have a time limit and automatically ends them when they exceed it. This application receives requests from &osips; and makes requests to a rating engine (see below) to find out if a call needs to be limited or not. When a call ends (or is ended) it will also instruct the rating engine to debit the balance for the caller with the consumed amount. The callcontrol application is available from http://callcontrol.ag-projects.com/ A rating engine that is used to calculate the time limit based on the caller's credit and the destination price and to debit the caller's balance after a call ends. This is available as part of CDRTool from http://cdrtool.ag-projects.com/ The callcontrol application runs on the same machine as &osips; and they communicate over a filesystem socket, while the rating engine can run on a different host and communicates with the callcontrol application using a TCP connection. Callcontrol is invoked by calling the call_control() function for the initial INVITE of every call we want to apply a limit to. This will end up as a request to the callcontrol application, which will interogate the rating engine for a time limit for the given caller and destination. The rating engine will determine if the destination has any associated cost and if the caller has any credit limit and if so will return the amount of time he is allowed to call that destination. Otherwise it will indicate that there is no limit associated with the call. If there is a limit, the callcontrol application will retain the session and attach a timer to it that will expire after the given time causing it to call back to &osips; with a request to end the dialog. If the rating engine returns that there is no limit for the call, the session is discarded by the callcontrol application and it will allow it to go proceed any limit. An appropriate response is returned to the call_control module that is then returned by the call_control() function call and allows the script to make a decision based on the answer.
Features Very simple API consisting of a single function that needs to be called once for the first INVITE of every call. The rest is done automatically in the background using dialog callbacks. Gracefully end dialogs when they exceed their time by triggering a dlg_end_dlg request into the dialog module, that will generate two BYE messages towards each endpoint, ending the call cleanly. Allow parallel sessions using one balance per subscriber Integrates with mediaproxy's ability to detect when a call does timeout sending media and is closed. In this case the dlg_end_dlg that is triggered by mediaproxy will end the callcontrol session before it reaches the limit and consumes all the credit for a call that died and didn't actually take place. For this mediaproxy has to be used and it has to be started by engage_media_proxy() to be able to keep track of the call's dialog and end it on timeout. Even when mediaproxy is unable to end the dialog because it was not started with engage_media_proxy(), the callcantrol application is still able to detect calls that did timeout sending media, by looking in the radius accounting records for entries recorded by mediaproxy for calls that did timeout. These calls will also be ended gracefully by the callcontrol application itself. If the prepaid_account_flag module parameter is defined, the external application compares the &osips;'s and the rating engine's views on whether the account calling is prepaid or not and takes appropriate action if they conflict. This provides protection against frauds in case the rating engine malfunctions or there is an inconsistency in the database. If the call_limit_avp is defined to a value greater than 0 it will be passed to the CallControl application, which will limit the number of concurrent calls the billing party (From user or diverter) is able to make. If the limit is reached the call_control function will return a specific error value. The call_token_avp may be used to detect calls with a duplicated CallID that could create potential problems in call rating engines.
Dependencies
&osips; Modules The following modules must be loaded before this module: dialog module
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported parameters
<varname>disable</varname> (int) Boolean flag that specifies if callcontrol should be disabled. This is useful when you want to use the same &osips; configuration in two different context, one using callcontrol, the other not. In the case callcontrol is disabled, calls to the call_control() function will return a code indicating that there is no limit associated with the call, allowing the use of the same configuration without changes. Default value is 0. Setting the <varname>disable</varname> parameter ... modparam("call_control", "disable", 1) ...
<varname>socket_name</varname> (string) It is the path to the filesystem socket where the callcontrol application listens for commands from the module. Default value is /var/run/callcontrol/socket. Setting the <varname>socket_name</varname> parameter ... modparam("call_control", "socket_name", "/var/run/callcontrol/socket") ...
<varname>socket_timeout</varname> (int) How much time (in milliseconds) to wait for an answer from the callcontrol application. Default value is 500 (ms). Setting the <varname>socket_timeout</varname> parameter ... modparam("call_control", "socket_timeout", 500) ...
<varname>signaling_ip_avp</varname> (string) Specification of the AVP which holds the IP address from where the SIP signaling originated. If this AVP is set it will be used to get the signaling IP address, else the source IP address from where the SIP message was received will be used. This AVP is meant to be used in cases where there are more than one proxy in the call setup path and the proxy that actually starts callcontrol doesn't receive the SIP messages directly from the UA and it cannot determine the NAT IP address from where the signaling originated. In such a case attaching a SIP header at the first proxy and then copying that header's value into the signaling_ip_avp on the proxy that starts callcontrol will allow it to get the correct NAT IP address from where the SIP signaling originated. This is used by the rating engine which finds the rates to apply to a call based on caller's SIP URI, caller's SIP domain or caller's IP address (whichever yields a rate forst, in this order). Default value is $avp(cc_signaling_ip). Setting the <varname>signaling_ip_avp</varname> parameter ... modparam("call_control", "signaling_ip_avp", "$avp(cc_signaling_ip)") ...
<varname>canonical_uri_avp</varname> (string) Specification of the AVP which holds an optional application defined canonical request URI. When this is set, it will be used as the destination when computing the call price, otherwise the request URI will be used. This is useful when the username of the ruri needs to have a different, canonical form in the rating engine computation than it has in the ruri. Default value is $avp(cc_can_uri). Setting the <varname>canonical_uri_avp</varname> parameter ... modparam("call_control", "canonical_uri_avp", "$avp(cc_can_uri)") ...
<varname>diverter_avp</varname> (string) Specification of the AVP which holds an optional application defined diverter SIP URI. When this is set, it will be used by the rating engine as the billing party when finding the rates to apply to a given call, otherwise, the caller's URI taken from the From field will be used. When set, this AVP should contain a value in the form user@domain (no sip: prefix should be used). This is useful when a destination diverts a call, thus becoming the new caller. In this case the billing party is the diverter and this AVP should be set to it, to allow the rating engine to pick the right rates for the call. For example, if A calls B and B diverts all its calls unconditionally to C, then the diverter AVP should the set to B's URI, because B is the billing party in the call not A after the call was diverted. Default value is $avp(diverter). Setting the <varname>diverter_avp</varname> parameter ... modparam("call_control", "diverter_avp", "$avp(diverter)") route { ... # alice@example.com is paying for this call $avp(diverter) = "alice@example.com"; ... } ...
<varname>prepaid_account_flag</varname> (string/integer) The flag that is used to specify whether the account making the call is prepaid or postpaid. Setting this to a non-null value will determine the module to pass the flag's value to the external application. This will allow the external application to compare &osips;'s and the rating engine's views on whether the account calling is prepaid or not and take appropriate action if they conflict. The flag should be set from the &osips; configuration for a prepaid account and reset for a postpaid one. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is NULL (undefined). Setting the <varname>prepaid_account_flag</varname> parameter ... modparam("call_control", "prepaid_account_flag", "PP_ACC_FLAG") ...
<varname>call_limit_avp</varname> (string) Specification of the AVP which holds an optional application defined call limit. When this is set, it will be passed to the CallControl application and if the limit is reached the call_control function will return an error code of -4. Default value is $avp(cc_call_limit). Setting the <varname>call_limit_avp</varname> parameter ... modparam("call_control", "call_limit_avp", "$avp(cc_call_limit)") ...
<varname>call_token_avp</varname> (string) Specification of the AVP which holds an optional application defined token. This token will be used to check if two calls with the same CallID actually refer to the same call. If call_control() is called multiple times for the same call (thus same CallID) the token needs to be the same or call_control will return -3 error, indicating that the CallID is duplicated. Default value is $avp(cc_call_token). Setting the <varname>call_token_avp</varname> parameter ... modparam("call_control", "call_token_avp", "$avp(cc_call_token)") ... $avp(cc_call_token) := $RANDOM; ...
<varname>init</varname> (string) This parameter is used to describe custom call control initialize messages. It represents a list of key value pairs and has the following format: "string1 = var1 [string2 = var2]*" The left-hand side of the assignment can be any string. The right-hand side of the assignment must be a script pseudo variable or a script AVP. For more information about them see CookBooks - Scripting Variables. If the parameter is not set, the default initialize message is sent. Default value is NULL. Setting the <varname>init</varname> parameter ... modparam("call_control", "init", "call-id=$ci to=$tu from=$fu authruri=$du another_field = $avp(10)") ...
<varname>start</varname> (string) This parameter is used to describe custom call control start messages. It represents a list of key value pairs and has the following format: "string1 = var1 [string2 = var2]*" The left-hand side of the assignment can be any string. The right-hand side of the assignment must be a script pseudo variable or a script AVP. For more information about them see CookBooks - Scripting Variables. If the parameter is not set, the default start message is sent. Default value is NULL. Setting the <varname>start</varname> parameter ... modparam("call_control", "start", "call-id=$ci to=$tu from=$fu authruri=$du another_field = $avp(10)") ...
<varname>stop</varname> (string) This parameter is used to describe custom call control stop messages. It represents a list of key value pairs and has the following format: "string1 = var1 [string2 = var2]*" The left-hand side of the assignment can be any string. The right-hand side of the assignment must be a script pseudo variable or a script AVP. For more information about them see CookBooks - Scripting Variables. If the parameter is not set, the default stop message is sent. Default value is NULL. Setting the <varname>stop</varname> parameter ... modparam("call_control", "stop", "call-id=$ci to=$tu from=$fu authruri=$du another_field = $avp(10)") ...
Exported Functions
<function moreinfo="none">call_control()</function> Trigger the use of callcontrol for the dialog started by the INVITE for which this function is called (the function should only be called for the first INVITE of a call). Further in-dialog requests will be processed automatically using internal bindings into the dialog state machine, allowing callcontrol to update its internal state as the dialog progresses, without any other intervention from the script. This function should be called right before the message is sent out using t_relay(), when all the request uri modifications are over and a final destination has been determined. This function has the following return codes: +2 - call has no limit +1 - call has limit and is traced by callcontrol -1 - not enough credit to make the call -2 - call is locked by another call in progress -3 - duplicated callid -4 - call limit has been reached -5 - internal error (message parsing, communication, ...) This function can be used from REQUEST_ROUTE. Using the <function>call_control</function> function ... if (is_avp_set("$avp(805)")) { # the diverter AVP is set, use it as billing party $avp(billing_party_domain) = $(avp(805){uri.domain}); } else { $avp(billing_party_domain) = $fd; } if (method==INVITE && !has_totag() && is_domain_local("$avp(billing_party_domain)")) { call_control(); switch ($retcode) { case 2: # Call with no limit case 1: # Call has limit and is under callcontrol management break; case -1: # Not enough credit (prepaid call) sl_send_reply("402", "Not enough credit"); exit; break; case -2: # Locked by another call in progress (prepaid call) sl_send_reply("403", "Call locked by another call in progress"); exit; break; case -3: # Duplicated callid sl_send_reply("400", "Duplicated callid"); exit; break; case -4: # Call limit reached sl_send_reply("503", "Too many concurrent calls"); exit; break; default: # Internal error (message parsing, communication, ...) if (PREPAID_ACCOUNT) { xlog("Call control: internal server error\n"); sl_send_reply("500", "Internal server error"); exit; } else { xlog("L_WARN", "Cannot set time limit for postpaid call\n"); } } } t_relay(); ...
opensips-2.2.2/modules/carrierroute/000077500000000000000000000000001300170765700175415ustar00rootroot00000000000000opensips-2.2.2/modules/carrierroute/Makefile000066400000000000000000000011651300170765700212040ustar00rootroot00000000000000# $Id$ # # carrierroute Makefile # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=carrierroute.so ifeq ($(CROSS_COMPILE),) CONFUSE_BUILDER = $(shell \ if which confuse-config >/dev/null 2>/dev/null;then \ echo 'confuse-config'; \ elif pkg-config --exists libconfuse; then \ echo 'pkg-config libconfuse'; \ fi) endif ifeq ($(CONFUSE_BUILDER),) DEFS += -I$(LOCALBASE)/include LIBS += -L$(LOCALBASE)/lib -lconfuse else DEFS += $(shell $(CONFUSE_BUILDER) --cflags) LIBS += $(shell $(CONFUSE_BUILDER) --libs) endif include ../../Makefile.modules opensips-2.2.2/modules/carrierroute/README000066400000000000000000001214231300170765700204240ustar00rootroot00000000000000carrierroute Jonas Appel 1&1 Internet AG Hardy Kahl 1&1 Internet AG Henning Westerholt 1&1 Internet AG Copyright © 2007 1&1 Internet AG Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. db_table (string) 1.3.3. id_column (string) 1.3.4. carrier_column (string) 1.3.5. scan_prefix_column (string) 1.3.6. domain_column (string) 1.3.7. flags_column (string) 1.3.8. mask_column (string) 1.3.9. prob_column (string) 1.3.10. rewrite_host_column (string) 1.3.11. strip_column (string) 1.3.12. comment_column (string) 1.3.13. carrier_table (string) 1.3.14. rewrite_prefix_column (string) 1.3.15. rewrite_suffix_column (string) 1.3.16. carrier_id_col (string) 1.3.17. carrier_name_col (string) 1.3.18. subscriber_table (string) 1.3.19. subscriber_user_col (string) 1.3.20. subscriber_domain_col (string) 1.3.21. subscriber_carrier_col (string) 1.3.22. config_source (string) 1.3.23. config_file (string) 1.3.24. default_tree (string) 1.3.25. use_domain (int) 1.3.26. fallback_default (int) 1.3.27. db_failure_table (string) 1.3.28. failure_id_column (string) 1.3.29. failure_carrier_column (string) 1.3.30. failure_scan_prefix_column (string) 1.3.31. failure_domain_column (string) 1.3.32. failure_host_name_column (string) 1.3.33. failure_reply_code_column (string) 1.3.34. failure_flags_column (string) 1.3.35. failure_mask_column (string) 1.3.36. failure_next_domain_column (string) 1.3.37. failure_comment_column (string) 1.4. Exported Functions 1.4.1. cr_user_carrier(user, domain, dstavp) 1.4.2. cr_route(carrier, domain, prefix_matching, rewrite_user, hash_source, dstavp) 1.4.3. cr_prime_route(carrier, domain, prefix_matching, rewrite_user, hash_source, dstavp) 1.4.4. cr_next_domain(carrier, domain, prefix_matching, host, reply_code, dstavp) 1.5. MI Commands 1.5.1. cr_reload_routes 1.5.2. cr_dump_routes 1.5.3. cr_replace_host 1.5.4. cr_deactivate_host 1.5.5. cr_activate_host 1.5.6. cr_add_host 1.5.7. cr_delete_host 1.6. Examples 1.7. Installation and Running 1.7.1. Database setup List of Examples 1.1. Set db_url parameter 1.2. Set db_table parameter 1.3. Set id_column parameter 1.4. Set carrier_column parameter 1.5. Set scan_prefix_column parameter 1.6. Set domain_column parameter 1.7. Set flags_column parameter 1.8. Set mask_column parameter 1.9. Set prob_column parameter 1.10. Set rewrite_host_column parameter 1.11. Set strip_column parameter 1.12. Set comment_column parameter 1.13. Set carrier_table parameter 1.14. Set rewrite_prefix_column parameter 1.15. Set rewrite_suffix_column parameter 1.16. Set id_col parameter 1.17. Set carrier_name_col parameter 1.18. Set subscriber_table parameter 1.19. Set subscriber_user_col parameter 1.20. Set subscriber_domain_col parameter 1.21. Set subscriber_carrier_col parameter 1.22. Set config_source parameter 1.23. Set config_file parameter 1.24. Set default_tree parameter 1.25. Set use_domain parameter 1.26. Set fallback_default parameter 1.27. Set db_failure_table parameter 1.28. Set failure_id_column parameter 1.29. Set failure_carrier_column parameter 1.30. Set failure_scan_prefix_column parameter 1.31. Set failure_domain_column parameter 1.32. Set failure_host_name_column parameter 1.33. Set failure_reply_code_column parameter 1.34. Set failure_flags_column parameter 1.35. Set failure_mask_column parameter 1.36. Set failure_next_domain_column parameter 1.37. Set failure_comment_column parameter 1.38. cr_replace_host usage 1.39. cr_deactivate_host usage 1.40. cr_activate_host usage 1.41. cr_add_host usage 1.42. cr_delete_host usage 1.43. Configuration example - Routing to default tree 1.44. Configuration example - Routing to user tree 1.45. Configuration example - module configuration 1.46. Example database content - carrierroute table 1.47. Example database content - simple carrierfailureroute table 1.48. Example database content - more complex carrierfailureroute table 1.49. Example database content - route_tree table 1.50. Necessary extensions for the user table Chapter 1. Admin Guide 1.1. Overview A module which provides routing, balancing and blacklisting capabilities. The module provides routing, balancing and blacklisting capabilities. It reads routing entries from a database source or from a config file at OpenSIPS startup. It can uses one routing tree (for one carrier), or if needed for every user a different routing tree (unique for each carrier) for number prefix based routing. It supports several route tree domains, e.g. for failback routes or different routing rules for VoIP and PSTN targets. Based on the tree, the module decides which number prefixes are forwarded to which gateway. It can also distribute the traffic by ratio parameters. Furthermore, the requests can be distributed by a hash funcion to predictable destinations. The hash source is configurable, two different hash functions are available. This modules scales up to more than a few million users, and is able to handle more than several hundred thousand routing table entries. It should be able to handle more, but this is not that much tested at the moment. In load balancing scenarios the usage of the config file mode is recommended, to avoid the additional complexity that the database driven routing creates. Routing tables can be reloaded and edited (in config file mode) with the MI interface, the config file is updated according the changes. This is not implemented for the db interface, because its easier to do the changes directly on the db. But the reload and dump functions works of course here too. Some module functionality is not fully available in the config file mode, as it is not possible to specify all information that can be stored in the database tables in the config file. Further information about these limitations is given in later sections. For user based routing or LCR you should use the database mode. Basically this module could be used as an replacement for the lcr and the dispatcher module, if you have certain performance, flexibility and/or integration requirements that these modules don't handle properly. But for small installations it probably make more sense to use the lcr and dispatcher module. If you want to use this module in failure routes, then you need to call “append_branch()†after rewriting the request URI in order to relay the message to the new target. Its also supportes the usage of database derived failure routing descisions with the carrierfailureroute table. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following module must be loaded before this module: * a database module, when a database is used as configuration data source. Only SQL based databases are supported, as this module needs the capability to issue raw queries. Its not possible to use the dbtext or db_berkeley module at the moment. * The tm module, when you want to use the $T_reply_code pseudo-variable in the “cr_next_domain†function. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libconfuse, a configuration file parser library. ( http://www.nongnu.org/confuse/ ) 1.3. Exported Parameters 1.3.1. db_url (string) Url to the database containing the routing data. Default value is “mysql://opensipsro:opensipsro@localhost/opensipsâ€. Example 1.1. Set db_url parameter ... modparam("carrierroute", "db_url", "dbdriver://username:password@dbhost/ dbname") ... 1.3.2. db_table (string) Name of the table where the routing data is stored. Default value is “carrierrouteâ€. Example 1.2. Set db_table parameter ... modparam("carrierroute", "db_table", "carrierroute") ... 1.3.3. id_column (string) Name of the column containing the id identifier. Default value is “idâ€. Example 1.3. Set id_column parameter ... modparam("carrierroute", "id_column", "id") ... 1.3.4. carrier_column (string) Name of the column containing the carrier id. Default value is “carrierâ€. Example 1.4. Set carrier_column parameter ... modparam("carrierroute", "carrier_column", "carrier") ... 1.3.5. scan_prefix_column (string) Name of column containing the scan prefixes. Scan prefixes define the matching portion of a phone number, e.g. when we have the scan prefixes 49721 and 49, the called number is 49721913740, it matches 49721, because the longest match is taken. If no prefix matches, the number is not routed. To prevent this, an empty prefix value of Ҡcould be added. Default value is “scan_prefixâ€. Example 1.5. Set scan_prefix_column parameter ... modparam("carrierroute", "scan_prefix_column", "scan_prefix") ... 1.3.6. domain_column (string) Name of column containing the rule domain. You can define several routing domains to have different routing rules. Maybe you use domain 0 for normal routing and domain 1 if domain 0 failed. Default value is “domainâ€. Example 1.6. Set domain_column parameter ... modparam("carrierroute", "domain_column", "domain") ... 1.3.7. flags_column (string) Name of the column containing the flags. Default value is “flagsâ€. Example 1.7. Set flags_column parameter ... modparam("carrierroute", "flags_column", "flags") ... 1.3.8. mask_column (string) Name of the column containing the flags mask. Default value is “maskâ€. Example 1.8. Set mask_column parameter ... modparam("carrierroute", "mask_column", "mask") ... 1.3.9. prob_column (string) Name of column containing probability. The probability value is used to distribute the traffic between several gateways. Let's say 70 % of the traffic shall be routed to gateway A, the other 30 % shall be routed to gateway B, we define a rule for gateway A with a prob value of 0.7 and a rule for gateway B with a prob value of 0.3. If all probabilities for a given prefix, tree and domain don't add to 100%, the prefix values will be adjusted according the given prob values. E.g. if three hosts with prob values of 0.5, 0.5 and 0.4 are defined, the resulting probabilities are 35.714, 35.714 and 28.571%. But its better to choose meaningful values in the first place because of clarity. Default value is “probâ€. Example 1.9. Set prob_column parameter ... modparam("carrierroute", "prob_column", "prob") ... 1.3.10. rewrite_host_column (string) Name of column containing rewrite host value. An empty field represents a blacklist entry, anything else is put as domain part into the Request URI of the SIP message. Default value is “rewrite_hostâ€. Example 1.10. Set rewrite_host_column parameter ... modparam("carrierroute", "rewrite_host_column", "rewrite_host") ... 1.3.11. strip_column (string) Name of the column containing the number of digits to be stripped of the userpart of an URI before prepending rewrite_prefix. Default value is “stripâ€. Example 1.11. Set strip_column parameter ... modparam("carrierroute", "strip_column", "strip") ... 1.3.12. comment_column (string) Name of the column containing an optional comment (useful in large routing tables) The comment is also displayed by the fifo cmd "cr_dump_routes". Default value is “descriptionâ€. Example 1.12. Set comment_column parameter ... modparam("carrierroute", "comment_column", "description") ... 1.3.13. carrier_table (string) The name of the table containing the existing carriers, consisting of the ids and corresponding names. Default value is “route_treeâ€. Example 1.13. Set carrier_table parameter ... modparam("carrierroute", "carrier_table", "route_tree") ... 1.3.14. rewrite_prefix_column (string) Name of column containing rewrite prefixes. Here you can define a rewrite prefix for the localpart of the SIP URI. Default value is “rewrite_prefixâ€. Example 1.14. Set rewrite_prefix_column parameter ... modparam("carrierroute", "rewrite_prefix_column", "rewrite_prefix") ... 1.3.15. rewrite_suffix_column (string) Name of column containing rewrite suffixes. Here you can define a rewrite suffix for the localpart of the SIP URI. Default value is “rewrite_suffixâ€. Example 1.15. Set rewrite_suffix_column parameter ... modparam("carrierroute", "rewrite_suffix_column", "rewrite_suffix") ... 1.3.16. carrier_id_col (string) The name of the column in the carrier table containing the carrier id. Default value is “idâ€. Example 1.16. Set id_col parameter ... modparam("carrierroute", "carrier_id_col", "id") ... 1.3.17. carrier_name_col (string) The name of the column in the carrier table containing the carrier name. Default value is “carrierâ€. Example 1.17. Set carrier_name_col parameter ... modparam("carrierroute", "carrier_name_col", "carrier") ... 1.3.18. subscriber_table (string) The name of the table containing the subscribers Default value is “subscriberâ€. Example 1.18. Set subscriber_table parameter ... modparam("carrierroute", "subscriber_table", "subscriber") ... 1.3.19. subscriber_user_col (string) The name of the column in the subscriber table containing the usernames. Default value is “usernameâ€. Example 1.19. Set subscriber_user_col parameter ... modparam("carrierroute", "subscriber_user_col", "username") ... 1.3.20. subscriber_domain_col (string) The name of the column in the subscriber table containing the domain of the subscriber. Default value is “domainâ€. Example 1.20. Set subscriber_domain_col parameter ... modparam("carrierroute", "subscriber_domain_col", "domain") ... 1.3.21. subscriber_carrier_col (string) The name of the column in the subscriber table containing the carrier id of the subscriber. Default value is “cr_preferred_carrierâ€. Example 1.21. Set subscriber_carrier_col parameter ... modparam("carrierroute", "subscriber_carrier_col", "cr_preferred_carrier ") ... 1.3.22. config_source (string) Specifies whether the module loads its config data from a file or from a database. Possible values are file or db. Default value is “fileâ€. Example 1.22. Set config_source parameter ... modparam("carrierroute", "config_source", "file") ... 1.3.23. config_file (string) Specifies the path to the config file. Default value is “/etc/opensips/carrierroute.confâ€. Example 1.23. Set config_file parameter ... modparam("carrierroute", "config_file", "/etc/opensips/carrierroute.conf ") ... 1.3.24. default_tree (string) The name of the carrier tree used per default (if the current subscriber has no preferred tree) Default value is “defaultâ€. Example 1.24. Set default_tree parameter ... modparam("carrierroute", "default_tree", "default") ... 1.3.25. use_domain (int) When using tree lookup per user, this parameter specifies whether to use the domain part for user matching or not. Default value is “0â€. Example 1.25. Set use_domain parameter ... modparam("carrierroute", "use_domain", 0) ... 1.3.26. fallback_default (int) This parameter defines the behaviour when using user-based tree lookup. If the user has a non-existing tree set and fallback_default is set to 1, the default tree is used. Otherwise, cr_user_rewrite_uri returns an error. Default value is “1â€. Example 1.26. Set fallback_default parameter ... modparam("carrierroute", "fallback_default", 1) ... 1.3.27. db_failure_table (string) Name of the table where the failure routing data is stored. Default value is “carrierfailurerouteâ€. Example 1.27. Set db_failure_table parameter ... modparam("carrierroute", "db_failure_table", "carrierfailureroute") ... 1.3.28. failure_id_column (string) Name of the column containing the id identifier. Default value is “idâ€. Example 1.28. Set failure_id_column parameter ... modparam("carrierroute", "failure_id_column", "id") ... 1.3.29. failure_carrier_column (string) Name of the column containing the carrier id. Default value is “carrierâ€. Example 1.29. Set failure_carrier_column parameter ... modparam("carrierroute", "failure_carrier_column", "carrier") ... 1.3.30. failure_scan_prefix_column (string) Name of column containing the scan prefixes. Scan prexies define the matching portion of a phone number, e.g. we have the scan prefixes 49721 and 49, the called number is 49721913740, it matches 49721, because the longest match is taken. If no prefix matches, the number is not failure routed. To prevent this, an empty prefix value of Ҡcould be added. Default value is “scan_prefixâ€. Example 1.30. Set failure_scan_prefix_column parameter ... modparam("carrierroute", "failure_scan_prefix_column", "scan_prefix") ... 1.3.31. failure_domain_column (string) Name of column containing the rule domain. You can define several routing domains to have different routing rules. Maybe you use domain 0 for normal routing and domain 1 if domain 0 failed. Default value is “domainâ€. Example 1.31. Set failure_domain_column parameter ... modparam("carrierroute", "failure_domain_column", "domain") ... 1.3.32. failure_host_name_column (string) Name of the column containing the host name of the last routing destination. Default value is “host_nameâ€. Example 1.32. Set failure_host_name_column parameter ... modparam("carrierroute", "failure_host_name_column", "host_name") ... 1.3.33. failure_reply_code_column (string) Name of the column containing the reply code. Default value is “reply_codeâ€. Example 1.33. Set failure_reply_code_column parameter ... modparam("carrierroute", "failure_reply_code_column", "reply_code") ... 1.3.34. failure_flags_column (string) Name of the column containing the flags. Default value is “flagsâ€. Example 1.34. Set failure_flags_column parameter ... modparam("carrierroute", "failure_flags_column", "flags") ... 1.3.35. failure_mask_column (string) Name of the column containing the flags mask. Default value is “maskâ€. Example 1.35. Set failure_mask_column parameter ... modparam("carrierroute", "failure_mask_column", "mask") ... 1.3.36. failure_next_domain_column (string) Name of the column containing the next routing domain. Default value is “next_domainâ€. Example 1.36. Set failure_next_domain_column parameter ... modparam("carrierroute", "failure_next_domain_column", "next_domain") ... 1.3.37. failure_comment_column (string) Name of the column containing an optional comment. Default value is “descriptionâ€. Example 1.37. Set failure_comment_column parameter ... modparam("carrierroute", "failure_comment_column", "description") ... 1.4. Exported Functions Previous versions of carrierroute had some more function. All the old semantics can be achieved by using the few new functions like this: cr_rewrite_uri(domain, hash_source) -> cr_route("default", domain, "$rU", "$rU", hash_source) cr_prime_balance_uri(domain, hash_source) -> cr_prime_route("default", domain, "$rU", "$rU", hash_source) cr_rewrite_by_to(domain, hash_source) -> cr_route("default", domain, "$tU", "$rU", hash_source) cr_prime_balance_by_to(domain, hash_source) -> cr_prime_route("default", domain, "$tU", "$rU", hash_source) cr_rewrite_by_from(domain, hash_source) -> cr_route("default", domain, "$fU", "$rU", hash_source) cr_prime_balance_by_from(domain, hash_source) -> cr_prime_route("default", domain, "$fU", "$rU", hash_source) cr_user_rewrite_uri(uri, domain) -> cr_user_carrier(user, domain, "$avp(tree_avp)") -> cr_route("$avp(tree_avp)", domain, "$rU", "$rU", "call_id") cr_tree_rewrite_uri(tree, domain) -> cr_route(tree, domain, "$rU", "$rU", "call_id") 1.4.1. cr_user_carrier(user, domain, dstavp) This function loads the carrier and stores it in an AVP. It cannot be used in the config file mode, as it needs a mapping of the given user to a certain carrier. The is derived from a database entry belonging to the user parameter. This mapping must be available in the table that is specified in the “subscriber_table†variable. This data is not cached in memory, that means for every execution of this function a database query will be done. Meaning of the parameters is as follows: * user - Name of the user for the carrier tree lookup. Additional to a string any pseudo-variable could be used as input. * domain - Name of the routing domain to be used. Additional to a string any pseudo-variable could be used as input. * dstavp - Name of the AVP where to store the carrier id. 1.4.2. cr_route(carrier, domain, prefix_matching, rewrite_user, hash_source, dstavp) This function searches for the longest match for the user given in prefix_matching at the given domain in the given carrier tree. The Request URI is rewritten using rewrite_user and the given hash source and algorithm. Returns -1 if there is no data found or an empty rewrite host on the longest match is found. Otherwise the rewritten host is stored in the given AVP (if obmitted, the host is not stored in an AVP). This function is only usable with rewrite_user and prefix_matching containing a valid numerical only string. It uses the standard crc32 algorithm to calculate the hash values. Meaning of the parameters is as follows: * carrier - The routing tree to be used. Additional to a string any pseudo-variable could be used as input. * domain - Name of the routing domain to be used. Additional to a string any pseudo-variable could be used as input. * prefix_matching - User name to be used for prefix matching in the routing tree. Additional to a string any pseudo-variable could be used as input. * rewrite_user - The user name to be used for applying the rewriting rule. Usually this is the user part of the request URI. Additional to a string any pseudo-variable could be used as input. * hash_source - The hash values of the destination set must be a contiguous range starting at 1, limited by the configuration parameter max_targets. Possible values for hash_source are: call_id, from_uri, from_user, to_uri and to_user. * dstavp - Name of the AVP where to store the rewritten host. This parameter is optional. 1.4.3. cr_prime_route(carrier, domain, prefix_matching, rewrite_user, hash_source, dstavp) This function searches for the longest match for the user given in prefix_matching at the given domain in the given carrier tree. The Request URI is rewritten using rewrite_user and the given hash source and algorithm. Returns -1 if there is no data found or an empty rewrite host on the longest match is found. Otherwise the rewritten host is stored in the given AVP (if obmitted, the host is not stored in an AVP). This function is only usable with rewrite_user and prefix_matching containing a valid numerical only string. It uses the prime hash algorithm to calculate the hash values. Meaning of the parameters is as follows: * carrier - The routing tree to be used. Additional to a string any pseudo-variable could be used as input. * domain - Name of the routing domain to be used. Additional to a string any pseudo-variable could be used as input. * prefix_matching - User name to be used for prefix matching in the routing tree. Additional to a string any pseudo-variable could be used as input. * rewrite_user - The user name to be used for applying the rewriting rule. Usually this is the user part of the request URI. Additional to a string any pseudo-variable could be used as input. * hash_source - The hash values of the destination set must be a contiguous range starting at 1, limited by the configuration parameter max_targets. Possible values for hash_source are: call_id, from_uri, from_user, to_uri and to_user. * dstavp - Name of the AVP where to store the rewritten host. This parameter is optional. 1.4.4. cr_next_domain(carrier, domain, prefix_matching, host, reply_code, dstavp) This function searches for the longest match for the user given in prefix_matching at the given domain in the given carrier failure tree. It tries to find a next domain matching the given host, reply_code and the message flags. The matching is done in this order: host, reply_code and then flags. The more wildcards in reply_code and the more bits used in flags, the lower the priority. Returns -1 if there is no data found or an empty next_domain on the longest match is found. Otherwise the next domain is stored in the given AVP. This function is only usable with prefix_matching containing a valid numerical only string. Meaning of the parameters is as follows: * carrier - The routing tree to be used. Additional to a string any pseudo-variable could be used as input. * domain - Name of the routing domain to be used. Additional to a string any pseudo-variable could be used as input. * prefix_matching - User name to be used for prefix matching in the routing tree. Additional to a string any pseudo-variable could be used as input. * host - The host name to be used for failure route rule matching. Usually this is the last tried routing destination stored in an avp by cr_route. Additional to a string any pseudo-variable could be used as input. * reply_code - The reply code to be used for failure route rule matching. Additional to a string any pseudo-variable could be used as input. * dstavp - Name of the AVP where to store the next routing domain. 1.5. MI Commands All commands understand the "-?" parameter to print a short help message. The options have to be quoted as one string to be passed to MI interface. Each option except host and new host can be wildcarded by * (but only * and not things like "-d prox*"). 1.5.1. cr_reload_routes This command reloads the routing data from the data source. Important: When new domains have been added, a restart of the server must be done, because the mapping of the ids used in the config script cannot be updated at runtime at the moment. So a reload could result in a wrong routing behaviour, because the ids used in the script could differ from the one used internally from the server. Modifying of already existing domains is no problem. 1.5.2. cr_dump_routes This command prints the route rules on the command line. 1.5.3. cr_replace_host This command can replace the rewrite_host of a route rule, it is only usable in file mode. Following options are possible: * -d - the domain containing the host * -p - the prefix containing the host * -h - the host to be replaced * -t - the new host Use the "null" prefix to specify an empty prefix. Example 1.38. cr_replace_host usage ... opensipsctl fifo cr_replace_host "-d proxy -p 49 -h proxy1 -t proxy2" ... 1.5.4. cr_deactivate_host This command deactivates the specified host, i.e. it sets its status to 0. It is only usable in file mode. Following options are possible: * -d - the domain containing the host * -p - the prefix containing the host * -h - the host to be deactivated * -t - the new host used as backup When -t (new_host) is specified, the portion of traffic for the deactivated host is routed to the host given by -t. This is indicated in the output of dump_routes. The backup route is deactivated if the host is activated again. Use the "null" prefix to specify an empty prefix. Example 1.39. cr_deactivate_host usage ... opensipsctl fifo cr_deactivate_host "-d proxy -p 49 -h proxy1" ... 1.5.5. cr_activate_host This command activates the specified host, i.e. it sets its status to 1. It is only usable in file mode. Following options are possible: * -d - the domain containing the host * -p - the prefix containing the host * -h - the host to be activated Use the "null" prefix to specify an empty prefix. Example 1.40. cr_activate_host usage ... opensipsctl fifo cr_activate_host "-d proxy -p 49 -h proxy1" ... 1.5.6. cr_add_host This command adds a route rule, it is only usable in file mode. Following options are possible: * -d - the domain containing the host * -p - the prefix containing the host * -h - the host to be added * -w - the weight of the rule * -P - an optional rewrite prefix * -S - an optional rewrite suffix * -i - an optional hash index * -s - an optional strip value Use the "null" prefix to specify an empty prefix. Example 1.41. cr_add_host usage ... opensipsctl fifo cr_add_host "-d proxy -p 49 -h proxy1 -w 0.25" ... 1.5.7. cr_delete_host This command delete the specified hosts or rules, i.e. remove them from the route tree. It is only usable in file mode. Following options are possible: * -d - the domain containing the host * -p - the prefix containing the host * -h - the host to be added * -w - the weight of the rule * -P - an optional rewrite prefix * -S - an optional rewrite suffix * -i - an optional hash index * -s - an optional strip value Use the "null" prefix to specify an empty prefix. Example 1.42. cr_delete_host usage ... opensipsctl fifo cr_delete_host "-d proxy -p 49 -h proxy1 -w 0.25" ... 1.6. Examples Example 1.43. Configuration example - Routing to default tree ... route { # route calls based on hash over callid # choose route domain 0 of the default carrier if(!cr_route("default", "0", "$rU", "$rU", "call_id", "crc32")){ sl_send_reply("403", "Not allowed"); } else { # In case of failure, re-route the request t_on_failure("1"); # Relay the request to the gateway t_relay(); } } failure_route[1] { # In case of failure, send it to an alternative route: if (t_check_status("408|5[0-9][0-9]")) { #choose route domain 1 of the default carrier if(!cr_route("default", "1", "$rU", "$rU", "call_id", "crc32")){ t_reply("403", "Not allowed"); } else { t_on_failure("2"); t_relay(); } } } failure_route[2] { # further processing } Example 1.44. Configuration example - Routing to user tree ... route[1] { cr_user_carrier("$fU", "$fd", "$avp(carrier)"); # just an example domain $avp(domain)="start"; if (!cr_route("$avp(carrier)", "$avp(domain)", "$rU", "$rU", "call_id", "$avp(host)")) { xlog("L_ERR", "cr_route failed\n"); exit; } t_on_failure("1"); if (!t_relay()) { sl_reply_error(); }; } failure_route[1] { revert_uri(); if (!cr_next_domain("$avp(carrier)", "$avp(domain)", "$rU", "$avp(host)", "$T_reply_code", "$avp(domain)")) { xlog("L_ERR", "cr_next_domain failed\n"); exit; } if (!cr_route("$avp(carrier)", "$avp(domain)", "$rU", "$rU", "call_id", "$avp(host)")) { xlog("L_ERR", "cr_route failed\n"); exit; } t_on_failure("1"); append_branch(); if (!t_relay()) { xlog("L_ERR", "t_relay failed\n"); exit; }; } ... Example 1.45. Configuration example - module configuration The following config file specifies within the default carrier two domains, each with an prefix that contains two hosts. It is not possible to specify another carrier if you use the config file as data source. All traffic will be equally distributed between the hosts, both are active. The hash algorithm will working over the [1,2] set, messages hashed to one will go to the first host, the other to the second one. Don't use a hash index value of zero. If you ommit the hash completly, the module gives them a autogenerated value, starting from one. Use the “NULL†prefix to specify an empty prefix in the config file. Please note that the prefix is matched against the request URI (or to URI), if they did not contain a valid numerical URI, no match is possible. So for loadbalancing purposes e.g. for your registrars, you should use an empty prefix. ... domain proxy { prefix 49 { max_targets = 2 target proxy1.localdomain { prob = 0.500000 hash_index = 1 status = 1 comment = "test target 1" } target proxy2.localdomain { prob = 0.500000 hash_index = 2 status = 1 comment = "test target 2" } } } domain register { prefix NULL { max_targets = 2 target register1.localdomain { prob = 0.500000 hash_index = 1 status = 1 comment = "test target 1" } target register2.localdomain { prob = 0.500000 hash_index = 2 status = 1 comment = "test target 2" } } } ... 1.7. Installation and Running 1.7.1. Database setup Before running OpenSIPS with carrierroute, you have to setup the database table where the module will store the routing data. For that, if the table was not created by the installation script or you choose to install everything by yourself you can use the carrierroute-create.sql SQL script in the database directories in the opensips/scripts folder as template. Database and table name can be set with module parameters so they can be changed, but the name of the columns must be as they are in the SQL script. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. The flags and mask columns have the same function as in the carrierfailureroute table. A zero value in the flags and mask column means that any message flags will match this rule. For a minimal configuration either use the config file given above, or insert some data into the tables of the module. Example 1.46. Example database content - carrierroute table ... +----+---------+--------+-------------+-------+------+---------------+ | id | carrier | domain | scan_prefix | flags | prob | rewrite_host | +----+---------+--------+-------------+-------+------+---------------+ | 1 | 1 | 0 | 49 | 0 | 0.5 | de-1.carrier1 | | 2 | 1 | 0 | 49 | 0 | 0.5 | de-2.carrier1 | | 3 | 1 | 0 | 49 | 16 | 1 | de-3.carrier1 | | 4 | 1 | 0 | | 0 | 1 | gw.carrier1-1 | | 5 | 1 | 1 | 49 | 0 | 1 | gw.carrier1-1 | | 6 | 1 | 2 | | 0 | 1 | gw.carrier1-2 | | 7 | 1 | 3 | | 0 | 1 | gw.carrier1-3 | | 8 | 2 | 0 | 49 | 0 | 0.5 | de-1.carrier2 | | 9 | 2 | 0 | 49 | 0 | 0.5 | de-2.carrier2 | | 10 | 2 | 0 | | 0 | 1 | gw.carrier2 | | 11 | 2 | 1 | 49 | 0 | 1 | gw.carrier2 | | 12 | 3 | start | 49 | 0 | 1 | de-gw.default | | 13 | 3 | start | | 0 | 1 | gw.default | +----+---------+--------+-------------+-------+------+---------------+ ... This table contains three routes to two gateways for the “49†prefix, and a default route for other prefixes over carrier 2 and carrier 1. The gateways for the default carrier will be used for functions that don't support the user specific carrier lookup. The routing rules for carrier 1 and carrier 2 for the “49†prefix contains a additional rule with the domain 1, that can be used for example as fallback if the gateways in domain 0 are not reachable. Two more fallback rules (domain 2 and 3) for carrier 1 are also supplied to support the functionality of the carrierfailureroute table example that is provided in the next section. The usage of strings for the domains is also possible, for example at carrier 3. This table provides also a “carrier1†routing rule for the “49†prefix, that is only choosen if some message flags are set. If this flags are not set, the other two rules are used. The “stripâ€, “mask†and “comment†colums are omitted for brevity. Example 1.47. Example database content - simple carrierfailureroute table ... +----+---------+--------+---------------+------------+-------------+ | id | carrier | domain | host_name | reply_code | next_domain | +----+---------+--------+---------------+------------+-------------+ | 1 | 1 | 0 | gw.carrier1-2 | ... | 3 | | 2 | 1 | 0 | gw.carrier1-3 | ... | 2 | +----+---------+--------+---------------+------------+-------------+ ... This table contains two failure routes for the “gw.carrier1-1†and “-2†gateways. For any (failure) reply code the respective next domain is choosen. After that no more failure routes are available, an error will be returned from the “cr_next_domain†function. Not all table colums are show here for brevity. For each failure route domain and carrier that is added to the carrierfailureroute table there must be at least one corresponding entry in the carrierroute table, otherwise the module will not load the routing data. Example 1.48. Example database content - more complex carrierfailureroute table ... +----+---------+-----------+------------+--------+-----+-------------+ | id | domain | host_name | reply_code | flags | mask | next_domain | +----+---------+-----------+------------+-------+------+-------------+ | 1 | 99 | | 408 | 16 | 16 | | | 2 | 99 | gw1 | 404 | 0 | 0 | 100 | | 3 | 99 | gw2 | 50. | 0 | 0 | 100 | | 4 | 99 | | 404 | 2048 | 2112 | asterisk-1 | +----+---------+-----------+------------+-------+------+-------------+ ... This table contains four failure routes that shows the usage of more advanced features. The first route matches to a 408, and to some flag for example that indicates that ringing has happened. If this flag is set, there will be no further forwarding, because next_domain is empty. In the second and third routes are certain gateway errors matched, if this errors have occurred, then the next domain will be chosen. The last route does forwarding according some flags, e.g. the customer came from a certain carrier, and has call-forwarding deactivated. In order to use the routing that is specified above, a matching carrierroute table must be provided, that holds domain entries for this routing rules. Not all table colums are show here for brevity. Example 1.49. Example database content - route_tree table ... +----+----------+ | id | carrier | +----+----------+ | 1 | carrier1 | | 2 | carrier2 | | 3 | default | +----+----------+ ... This table contains the mapping of the carrier id to actual names. For a functional routing the “cr_preferred_carrier†column must be added to the subscriber table (or to the table and column that you specified as modul parameter) to choose the actual carrier for the users. Example 1.50. Necessary extensions for the user table Suggested changes: ... ALTER TABLE subscriber ADD cr_preferred_carrier int(10) default NULL; ... opensips-2.2.2/modules/carrierroute/carrier_tree.c000066400000000000000000000346711300170765700223660ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file carrier_tree.c * @brief Contains the functions to manage carrier tree data. */ #include "../../mem/shm_mem.h" #include "../../ut.h" #include "carrier_tree.h" #include "carrierroute.h" #include "route_tree.h" #include "route_rule.h" #include "load_data.h" /** * points to the data loading function */ route_data_load_func_t load_data; /** * Pointer to the routing data. */ struct rewrite_data ** global_data = NULL; /** * holds the map between routing tree names and numbers */ struct tree_map ** script_trees = NULL; /** * holds the map between failure routing tree names and numbers */ struct tree_map ** script_failure_trees = NULL; static int carrier_tree_fixup(struct rewrite_data * rd); /** * Initializes the routing data, i.e. it binds the data loader, * initializes the global data pointer. * * @param source data source, can be db or file * * @return 0 on success, -1 on failure */ int init_route_data(const char * source) { if (global_data == NULL) { global_data = (struct rewrite_data **) shm_malloc(sizeof(struct rewrite_data *)); if (global_data == NULL) { LM_ERR("Out of shared memory before even " "doing anything.\n"); return -1; } } *global_data = NULL; return bind_data_loader(source, &load_data); } /** * Loads the routing data into the routing tree and sets the * global_data pointer to the new data. The old_data is removed * when it is not locked anymore. * * @return 0 on success, -1 on failure */ int prepare_route_tree(void) { struct rewrite_data * old_data; struct rewrite_data * new_data = NULL; int i; if ((new_data = shm_malloc(sizeof(struct rewrite_data))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(new_data, 0, sizeof(struct rewrite_data)); if (!load_data || load_data(new_data) < 0) { LM_ERR("could not load routing data\n"); return -1; } if (new_data == NULL) { return -1; } if (rule_fixup(new_data) < 0) { LM_ERR("could not fixup rules\n"); return -1; } if (carrier_tree_fixup(new_data) < 0){ LM_ERR("could not fixup trees\n"); return -1; } new_data->proc_cnt = 0; if (*global_data == NULL) { *global_data = new_data; } else { old_data = *global_data; *global_data = new_data; i = 0; while (old_data->proc_cnt > 0) { LM_ERR("data is still locked after %i seconds\n", i); sleep_us(i*1000000); i++; } destroy_rewrite_data(old_data); } return 0; } /** * Increases lock counter and returns a pointer to the * current routing data * * @return pointer to the global routing data on success, * NULL on failure */ struct rewrite_data * get_data(void) { struct rewrite_data *ret; if (!global_data || !*global_data) { return NULL; } ret = *global_data; lock_get(&ret->lock); ++ret->proc_cnt; lock_release(&ret->lock); if (ret == *global_data) { return ret; } else { lock_get(&ret->lock); --ret->proc_cnt; lock_release(&ret->lock); return NULL; } } /** * decrements the lock counter of the routing data * * @param data data to be released */ void release_data(struct rewrite_data *data) { lock_get(&data->lock); --data->proc_cnt; lock_release(&data->lock); } int find_tree(str tree){ struct tree_map * tmp; if (!script_trees){ return -1; } if (tree.len <= 0) { return -1; } tmp = *script_trees; while (tmp) { if (str_strcmp(&tree, &tmp->name) == 0) { return tmp->id; } tmp = tmp->next; } return -1; } /** * Adds the given route information to the route tree identified by * domain. scan_prefix identifies the number for which the information * is and the rewrite_* parameters define what to do in case of a match. * prob gives the probability with which this rule applies if there are * more than one for a given prefix. * * @param rd the route data to which the route shall be added * @param carrier_id the carrier id of the route to be added * @param domain the routing domain of the new route * @param scan_prefix the number prefix * @param flags user defined flags * @param mask mask for user defined flags * @param max_targets the number of targets * @param prob the weight of the rule * @param rewrite_hostpart the rewrite_host of the rule * @param strip the number of digits to be stripped off userpart before prepending prefix * @param rewrite_local_prefix the rewrite prefix * @param rewrite_local_suffix the rewrite suffix * @param status the status of the rule * @param hash_index the hash index of the rule * @param backup indicates if the route is backed up by another. only useful if status==0, if set, it is the hash value of another rule * @param backed_up an -1-termintated array of hash indices of the route for which this route is backup * @param comment a comment for the route rule * * @return 0 on success, -1 on error in which case it LOGs a message. */ int add_route(struct rewrite_data * rd, int carrier_id, const str * domain, const str * scan_prefix, flag_t flags, flag_t mask, int max_targets, double prob, const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix, const str * rewrite_local_suffix, int status, int hash_index, int backup, int * backed_up, const str * comment) { struct carrier_tree * ct = NULL; struct route_tree * rt = NULL; LM_INFO("adding prefix %.*s, prob %f\n", scan_prefix->len, scan_prefix->s, prob); if ((ct = get_carrier_tree(carrier_id, rd)) == NULL) { LM_ERR("could not retrieve carrier tree\n"); return -1; } if ((rt = get_route_tree(domain, ct)) == NULL) { LM_ERR("could not retrieve route tree\n"); return -1; } LM_INFO("found route, now adding\n"); return add_route_to_tree(rt->tree, scan_prefix, flags, mask, scan_prefix, max_targets, prob, rewrite_hostpart, strip, rewrite_local_prefix, rewrite_local_suffix, status, hash_index, backup, backed_up, comment); } /** * Adds the given failure route information to the failure route tree identified by * domain. scan_prefix, host, reply_code and flags identifies the number for which * the information is and the next_domain parameter defines where to continue routing * in case of a match. * * @param rd the route data to which the route shall be added * @param carrier_id the carrier id of the route to be added * @param domain the routing domain of the new route * @param scan_prefix the number prefix * @param host the hostname last tried * @param reply_code the reply code * @param flags user defined flags * @param mask for user defined flags * @param next_domain continue routing with this domain * @param comment a comment for the failure route rule * * @return 0 on success, -1 on error in which case it LOGs a message. */ int add_failure_route(struct rewrite_data * rd, int carrier_id, const str * domain, const str * scan_prefix, const str * host, const str * reply_code, flag_t flags, flag_t mask, const str * next_domain, const str * comment) { int next_domain_id; struct carrier_tree * ct = NULL; struct route_tree * rt = NULL; LM_INFO("adding prefix %.*s, reply code %.*s\n", scan_prefix->len, scan_prefix->s, reply_code->len, reply_code->s); if (reply_code->len!=3) { LM_ERR("invalid reply_code '%.*s'!\n", reply_code->len, reply_code->s); return -1; } if ((ct = get_carrier_tree(carrier_id, rd)) == NULL) { LM_ERR("could not retrieve carrier tree\n"); return -1; } if ((rt = get_route_tree(domain, ct)) == NULL) { LM_ERR("could not retrieve route tree\n"); return -1; } if ((next_domain_id = add_domain(next_domain)) < 0) { LM_ERR("add_domain failed\n"); return -1; } LM_INFO("found failure route, now adding\n"); return add_failure_route_to_tree(rt->failure_tree, scan_prefix, scan_prefix, host, reply_code, flags, mask, next_domain_id, comment); } /** * adds a carrier tree for the given carrier * * @param carrier the carrier name of desired routing tree * @param carrier_id the id of the carrier * @param rd route data to be searched * @param trees number of route_tree entries * * @return a pointer to the root node of the desired routing tree, * NULL on failure */ struct carrier_tree * add_carrier_tree(const str * carrier, int carrier_id, struct rewrite_data * rd, int trees) { int i, id; if (!rd) { LM_ERR("NULL pointer in parameter\n"); return NULL; } LM_INFO("add carrier %.*s\n", carrier->len, carrier->s); for (i=0; itree_num; i++) { if (rd->carriers[i]) { if (rd->carriers[i]->id == carrier_id) { LM_INFO("found carrier %i: %.*s\n", rd->carriers[i]->id, rd->carriers[i]->name.len, rd->carriers[i]->name.s); return rd->carriers[i]; } } } LM_INFO("carrier %.*s not found, add it\n", carrier->len, carrier->s); if ((id = add_tree(carrier, carrier_id)) < 0) { LM_ERR("could not add tree\n"); return NULL; } if (id > rd->tree_num) { LM_ERR("weird: to large tree id\n"); return NULL; } if ((rd->carriers[id] = create_carrier_tree(carrier, carrier_id, id, trees)) == NULL) { return NULL; } rd->carriers[id]->index = id; LM_INFO("created carrier tree: %.*s, with id %i and %ld trees\n", rd->carriers[id]->name.len, rd->carriers[id]->name.s, rd->carriers[id]->id, (long)rd->carriers[id]->tree_num); return rd->carriers[id]; } /** * Create a new carrier tree in shared memory and set it up. * * @param tree the name of the carrier tree * @param carrier_id id of carrier * @param id the domain id of the carrier tree * @param trees number of route_tree entries * * @return a pointer to the newly allocated route tree or NULL on * error, in which case it LOGs an error message. */ struct carrier_tree * create_carrier_tree(const str * tree, int carrier_id, int id, int trees) { struct carrier_tree * tmp; if ((tmp = shm_malloc(sizeof(struct carrier_tree))) == NULL) { LM_ERR("out of shared memory\n"); return NULL; } memset(tmp, 0, sizeof(struct carrier_tree)); if (shm_str_dup(&tmp->name, tree)!=0) { LM_ERR("cannot duplicate string\n"); shm_free(tmp); return NULL; } tmp->id = carrier_id; tmp->index = id; tmp->tree_num = trees; if(trees > 0){ if ((tmp->trees = shm_malloc(sizeof(struct route_tree *) * trees)) == NULL) { LM_ERR("out of shared memory\n"); shm_free(tmp->name.s); shm_free(tmp); return NULL; } memset(tmp->trees, 0, sizeof(struct route_tree *) * trees); } return tmp; } /** * returns the routing tree for the given domain, if domain's tree * doesn't exist, it will be created. If the trees are completely * filled and a not existing domain shall be added, an error is * returned * * @param carrier_id the id of the desired carrier tree * @param rd route data to be searched * * @return a pointer to the root node of the desired routing tree, * NULL on failure */ struct carrier_tree * get_carrier_tree(int carrier_id, struct rewrite_data * rd) { int i; if (!rd) { LM_ERR("NULL pointer in parameter\n"); return NULL; } for (i=0; itree_num; i++) { if (rd->carriers[i]->id == carrier_id) { return rd->carriers[i]; } } return NULL; } /** * Tries to add a tree to the tree map. If the given tree doesn't * exist, it is added. Otherwise, nothing happens. * * @param tree the tree to be added * @param carrier_id id of the carrier * * @return values: on success the numerical index of the given tree, * -1 on failure */ int add_tree(const str * tree, int carrier_id) { struct tree_map * tmp, * prev = NULL; int id = 0; if (!script_trees) { if ((script_trees = shm_malloc(sizeof(struct tree_map *))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } *script_trees = NULL; } tmp = *script_trees; while (tmp) { if (carrier_id == tmp->id) { return tmp->no; } id = tmp->no + 1; prev = tmp; tmp = tmp->next; } if ((tmp = shm_malloc(sizeof(struct tree_map))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(tmp, 0, sizeof(struct tree_map)); if (shm_str_dup(&tmp->name, tree)!=0) { LM_ERR("cannot duplicate string\n"); shm_free(tmp); return -1; } tmp->no = id; tmp->id = carrier_id; if (!prev) { *script_trees = tmp; } else { prev->next = tmp; } LM_INFO("tree %.*s has internal id %i\n", tree->len, tree->s, id); return id; } void destroy_route_data(void){ struct rewrite_data * rd = get_data(); struct tree_map * tmp3, * tmp4; destroy_rewrite_data(rd); destroy_route_map(); if(script_trees){ tmp3 = *script_trees; while(tmp3){ tmp4 = tmp3; tmp3 = tmp3->next; shm_free(tmp4); } shm_free(script_trees); script_trees = NULL; } if(global_data){ *global_data = NULL; shm_free(global_data); global_data = NULL; } } /** * Destroys a carrier tree * * @param tree route data to be destroyed */ static void destroy_carrier_tree(struct carrier_tree * tree) { int i; if (tree == NULL) { return; } if (tree->trees != NULL) { for (i = 0; i < tree->tree_num; ++i) { if (tree->trees[i] != NULL) { destroy_route_tree(tree->trees[i]); } } shm_free(tree->trees); } if(tree->name.s){ shm_free(tree->name.s); } shm_free(tree); return; } /** * Destroys the complete routing tree data. * * @param data route data to be destroyed */ void destroy_rewrite_data(struct rewrite_data *data) { int i; if (data == NULL) { return; } if (data->carriers != NULL) { for (i = 0; i < data->tree_num; ++i) { if (data->carriers[i] != NULL) { destroy_carrier_tree(data->carriers[i]); } } shm_free(data->carriers); } shm_free(data); return; } static int carrier_tree_fixup(struct rewrite_data * rd){ int i; str tmp; tmp = default_tree; rd->default_carrier_index = -1; for(i=0; itree_num; i++){ if(rd->carriers[i]){ if(str_strcmp(&(rd->carriers[i]->name), &tmp) == 0){ rd->default_carrier_index = i; } } } if(rd->default_carrier_index < 0){ LM_ERR("default_carrier not found\n"); } return 0; } opensips-2.2.2/modules/carrierroute/carrier_tree.h000066400000000000000000000154431300170765700223670ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file carrier_tree.h * @brief Contains the functions to manage carrier tree data. */ #ifndef SP_ROUTE_CARRIER_TREE_H #define SP_ROUTE_CARRIER_TREE_H #include #include "../../str.h" #include "route.h" /** * initialises the routing data, i.e. it binds the data loader * initialises the global data pointer * * @param source data source, can be db or file * * @return 0 on success, -1 on failure */ int init_route_data(const char * source); /** * Loads the routing data into the routing tree and sets the * global_data pointer to the new data. The old_data is removed * when it is not locked anymore. * * @return 0 on success, -1 on failure */ int prepare_route_tree(void); /** * Increases lock counter and returns a pointer to the * current routing data * * @return pointer to the global routing data on success, * NULL on failure */ struct rewrite_data * get_data(void); /** * decrements the lock counter of the routing data * * @param data data to be released */ void release_data(struct rewrite_data *data); /** * Create a new carrier tree in shared memory and set it up. * * @param tree the name of the carrier tree * @param carrier_id the id * @param id the domain id of the carrier tree * @param trees number of route_tree entries * * @return a pointer to the newly allocated route tree or NULL on * error, in which case it LOGs an error message. */ struct carrier_tree * create_carrier_tree(const str * tree, int carrier_id, int id, int trees); /** * Adds the given route information to the route tree identified by * domain. scan_prefix identifies the number for which the information * is and the rewrite_* parameters define what to do in case of a match. * prob gives the probability with which this rule applies if there are * more than one for a given prefix. * * @param rd the route data to which the route shall be added * @param carrier_id the carrier id of the route to be added * @param domain the routing domain of the new route * @param scan_prefix the number prefix * @param flags user defined flags * @param mask mask for user defined flags * @param max_targets the number of targets * @param prob the weight of the rule * @param strip the number of digits to be stripped off userpart before prepending prefix * @param rewrite_hostpart the rewrite_host of the rule * @param rewrite_local_prefix the rewrite prefix * @param rewrite_local_suffix the rewrite suffix * @param status the status of the rule * @param hash_index the hash index of the rule * @param backup indicates if the route is backed up by another. only useful if status==0, if set, it is the hash value of another rule * @param backed_up an -1-termintated array of hash indices of the route for which this route is backup * @param comment a comment for the route rule * * @return 0 on success, -1 on error in which case it LOGs a message. */ int add_route(struct rewrite_data * rd, int carrier_id, const str * domain, const str * scan_prefix, flag_t flags, flag_t mask, int max_targets, double prob, const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix, const str * rewrite_local_suffix, int status, int hash_index, int backup, int * backed_up, const str * comment); /** * Adds the given failure route information to the failure route tree identified by * domain. scan_prefix, host, reply_code and flags identifies the number for which * the information is and the next_domain parameter defines where to continue routing * in case of a match. * * @param rd the route data to which the route shall be added * @param carrier_id the carrier id of the route to be added * @param domain the routing domain of the new route * @param scan_prefix the number prefix * @param host the hostname last tried * @param reply_code the reply code * @param flags user defined flags * @param mask mask for user defined flags * @param next_domain continue routing with this domain * @param comment a comment for the failure route rule * * @return 0 on success, -1 on error in which case it LOGs a message. */ int add_failure_route(struct rewrite_data * rd, int carrier_id, const str * domain, const str * scan_prefix, const str * host, const str * reply_code, flag_t flags, flag_t mask, const str * next_domain, const str * comment); /** * Tries to add a tree to the tree map. If the given tree doesn't * exist, it is added. Otherwise, nothing happens. * * @param tree the tree to be added * * @return values: on success the numerical index of the given tree, * -1 on failure */ int add_tree(const str * tree, int carrier_id); /** * Searches for the ID for a Carrier-Name * * @param tree the carrier, we are looking for * * @return values: on success the id of for this carrier, * -1 on failure */ int find_tree(str tree); /** * adds a carrier tree for the given carrier * * @param carrier the carrier name of desired routing tree * @param carrier_id the id of the carrier * @param rd route data to be searched * @param trees number of route_tree entries * * @return a pointer to the root node of the desired routing tree, * NULL on failure */ struct carrier_tree * add_carrier_tree(const str * carrier, int carrier_id, struct rewrite_data * rd, int trees); /** * returns the routing tree for the given domain, if domain's tree * doesn't exist, it will be created. If the trees are completely * filled and a not existing domain shall be added, an error is * returned * * @param carrier_id the id of the desired carrier tree * @param rd route data to be searched * * @return a pointer to the root node of the desired routing tree, * NULL on failure */ struct carrier_tree * get_carrier_tree(int carrier_id, struct rewrite_data * rd); /** * Frees the routing data */ void destroy_route_data(); /** * Destroys the complete routing tree data. * * @param data route data to be destroyed */ void destroy_rewrite_data(struct rewrite_data *data); #endif opensips-2.2.2/modules/carrierroute/carrierroute.c000066400000000000000000000455531300170765700224270ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file carrierroute.c * @brief Contains the functions exported by the module. */ #include "../../sr_module.h" #include "../../str.h" #include "../../dset.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../ut.h" #include "../../error.h" #include "../../prime_hash.h" #include "../../db/db.h" #include "carrierroute.h" #include "load_data.h" #include "route_fifo.h" #include "carrier_tree.h" #include "route_func.h" str db_url = {NULL, 0}; str db_table = str_init("carrierroute"); str db_failure_table = str_init("carrierfailureroute"); str subscriber_table = str_init("subscriber"); str carrier_table = str_init("route_tree"); static str id_col = str_init("id"); static str carrier_col = str_init("carrier"); static str domain_col = str_init("domain"); static str scan_prefix_col = str_init("scan_prefix"); static str flags_col = str_init("flags"); static str mask_col = str_init("mask"); static str prob_col = str_init("prob"); static str rewrite_host_col = str_init("rewrite_host"); static str strip_col = str_init("strip"); static str rewrite_prefix_col = str_init("rewrite_prefix"); static str rewrite_suffix_col = str_init("rewrite_suffix"); static str comment_col = str_init("description"); static str username_col = str_init("username"); static str cr_preferred_carrier_col = str_init("cr_preferred_carrier"); static str subscriber_domain_col = str_init("domain"); static str carrier_id_col = str_init("id"); static str carrier_name_col = str_init("carrier"); static str failure_id_col = str_init("id"); static str failure_carrier_col = str_init("carrier"); static str failure_domain_col = str_init("domain"); static str failure_scan_prefix_col = str_init("scan_prefix"); static str failure_host_name_col = str_init("host_name"); static str failure_reply_code_col = str_init("reply_code"); static str failure_flags_col = str_init("flags"); static str failure_mask_col = str_init("mask"); static str failure_next_domain_col = str_init("next_domain"); static str failure_comment_col = str_init("description"); str * columns[COLUMN_NUM] = { &id_col, &carrier_col, &domain_col, &scan_prefix_col, &flags_col, &mask_col, &prob_col, &rewrite_host_col, &strip_col, &rewrite_prefix_col, &rewrite_suffix_col, &comment_col, }; str * subscriber_columns[SUBSCRIBER_COLUMN_NUM] = { &username_col, &domain_col, &cr_preferred_carrier_col, }; str * carrier_columns[CARRIER_COLUMN_NUM] = { &id_col, &carrier_col, }; str * failure_columns[FAILURE_COLUMN_NUM] = { &failure_id_col, &failure_carrier_col, &failure_domain_col, &failure_scan_prefix_col, &failure_host_name_col, &failure_reply_code_col, &failure_flags_col, &failure_mask_col, &failure_next_domain_col, &failure_comment_col }; char * config_source = "file"; char * config_file = CFG_DIR"carrierroute.conf"; str default_tree = str_init("default"); const str SP_EMPTY_PREFIX = str_init("null"); int mode = 0; int use_domain = 0; int fallback_default = 1; /************* Declaration of Interface Functions **************************/ static int mod_init(void); static int child_init(int); static void mod_destroy(void); static int route_fixup(void ** param, int param_no); static int load_user_carrier_fixup(void ** param, int param_no); static int load_next_domain_fixup(void ** param, int param_no); /************* Declaration of Helper Functions *****************************/ static enum hash_source hash_fixup(const char * domain); /************* Module Exports **********************************************/ static cmd_export_t cmds[]={ {"cr_user_carrier", (cmd_function)cr_load_user_carrier, 3, load_user_carrier_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"cr_route", (cmd_function)cr_route, 5, route_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"cr_route", (cmd_function)cr_route, 6, route_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"cr_prime_route", (cmd_function)cr_route, 5, route_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"cr_prime_route", (cmd_function)cr_route, 6, route_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"cr_next_domain", (cmd_function)cr_load_next_domain, 6, load_next_domain_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {0, 0, 0, 0, 0, 0} }; static param_export_t params[]= { {"db_url", STR_PARAM, &db_url.s }, {"db_table", STR_PARAM, &db_table.s }, {"db_failure_table", STR_PARAM, &db_failure_table.s }, {"carrier_table", STR_PARAM, &carrier_table.s }, {"subscriber_table", STR_PARAM, &subscriber_table.s }, {"id_column", STR_PARAM, &id_col.s }, {"carrier_column", STR_PARAM, &carrier_col.s }, {"domain_column", STR_PARAM, &domain_col.s }, {"scan_prefix_column", STR_PARAM, &scan_prefix_col.s }, {"flags_column", STR_PARAM, &flags_col.s }, {"mask_column", STR_PARAM, &mask_col.s }, {"prob_column", STR_PARAM, &prob_col.s }, {"rewrite_host_column", STR_PARAM, &rewrite_host_col.s }, {"strip_column", STR_PARAM, &strip_col.s }, {"rewrite_prefix_column", STR_PARAM, &rewrite_prefix_col.s }, {"rewrite_suffix_column", STR_PARAM, &rewrite_suffix_col.s }, {"comment_column", STR_PARAM, &comment_col.s }, {"failure_id_column", STR_PARAM, &failure_id_col.s }, {"failure_carrier_column", STR_PARAM, &failure_carrier_col.s }, {"failure_domain_column", STR_PARAM, &failure_domain_col.s }, {"failure_scan_prefix_column", STR_PARAM, &failure_scan_prefix_col.s }, {"failure_host_name_column", STR_PARAM, &failure_host_name_col.s }, {"failure_reply_code_column", STR_PARAM, &failure_reply_code_col.s }, {"failure_flags_column", STR_PARAM, &failure_flags_col.s }, {"failure_mask_column", STR_PARAM, &failure_mask_col.s }, {"failure_next_domain_column", STR_PARAM, &failure_next_domain_col.s }, {"failure_comment_column", STR_PARAM, &failure_comment_col.s }, {"subscriber_user_col", STR_PARAM, &username_col.s }, {"subscriber_domain_col", STR_PARAM, &subscriber_domain_col.s }, {"subscriber_carrier_col", STR_PARAM, &cr_preferred_carrier_col.s }, {"carrier_id_col", STR_PARAM, &carrier_id_col.s }, {"carrier_name_col", STR_PARAM, &carrier_name_col.s }, {"config_source", STR_PARAM, &config_source }, {"default_tree", STR_PARAM, &default_tree.s }, {"config_file", STR_PARAM, &config_file }, {"use_domain", INT_PARAM, &use_domain }, {"fallback_default", INT_PARAM, &fallback_default }, {0,0,0} }; static mi_export_t mi_cmds[] = { { "cr_reload_routes", 0, reload_fifo, MI_NO_INPUT_FLAG, 0, 0 }, { "cr_dump_routes", 0, dump_fifo, MI_NO_INPUT_FLAG, 0, 0 }, { "cr_replace_host", 0, replace_host, 0, 0, 0 }, { "cr_deactivate_host", 0, deactivate_host, 0, 0, 0 }, { "cr_activate_host", 0, activate_host, 0, 0, 0 }, { "cr_add_host", 0, add_host, 0, 0, 0 }, { "cr_delete_host", 0, delete_host, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "carrierroute", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version*/ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Export parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* Module initialization function */ 0, /* Response function */ mod_destroy,/* Destroy function */ child_init /* Child initialization function */ }; /************* Helper Functions ********************************************/ /** * Fixes the hash source to enum values * * @param my_hash_source the hash source as string * * @return the enum value on success, -1 on failure */ static enum hash_source hash_fixup(const char * my_hash_source) { if (strcasecmp("call_id", my_hash_source) == 0) { return shs_call_id; } else if (strcasecmp("from_uri", my_hash_source) == 0) { return shs_from_uri; } else if (strcasecmp("from_user", my_hash_source) == 0) { return shs_from_user; } else if (strcasecmp("to_uri", my_hash_source) == 0) { return shs_to_uri; } else if (strcasecmp("to_user", my_hash_source) == 0) { return shs_to_user; } else { return shs_error; } } /************* Interface Functions *****************************************/ /** * Initializes the module, i.e. it binds the necessary API functions * and registers the fifo commands * * @return 0 on success, -1 on failure */ static int mod_init(void) { init_db_url( db_url , 0 /*cannot be null*/); db_table.len = strlen(db_table.s); carrier_table.len = strlen(carrier_table.s); subscriber_table.len = strlen(subscriber_table.s); id_col.len = strlen(id_col.s); carrier_col.len = strlen(carrier_col.s); domain_col.len = strlen(domain_col.s); scan_prefix_col.len = strlen(scan_prefix_col.s); flags_col.len = strlen(flags_col.s); mask_col.len = strlen(mask_col.s); prob_col.len = strlen(prob_col.s); rewrite_host_col.len = strlen(rewrite_host_col.s); strip_col.len = strlen(strip_col.s); rewrite_prefix_col.len = strlen(rewrite_prefix_col.s); rewrite_suffix_col.len = strlen(rewrite_suffix_col.s); comment_col.len = strlen(comment_col.s); username_col.len = strlen(username_col.s); subscriber_domain_col.len = strlen(subscriber_domain_col.s); cr_preferred_carrier_col.len = strlen(cr_preferred_carrier_col.s); carrier_id_col.len = strlen(carrier_id_col.s); carrier_name_col.len = strlen(carrier_name_col.s); failure_id_col.len = strlen(failure_id_col.s); failure_carrier_col.len = strlen(failure_carrier_col.s); failure_domain_col.len = strlen(failure_domain_col.s); failure_scan_prefix_col.len = strlen(failure_scan_prefix_col.s); failure_host_name_col.len = strlen(failure_host_name_col.s); failure_reply_code_col.len = strlen(failure_reply_code_col.s); failure_flags_col.len = strlen(failure_flags_col.s); failure_mask_col.len = strlen(failure_mask_col.s); failure_next_domain_col.len = strlen(failure_next_domain_col.s); failure_comment_col.len = strlen(failure_comment_col.s); default_tree.len = strlen(default_tree.s); if (init_route_data(config_source) < 0) { LM_ERR("could not init route data\n"); return -1; } if (prepare_route_tree() == -1) { LM_ERR("could not prepare route tree\n"); return -1; } if(data_main_finalize() < 0) { return -1; } LM_INFO("module initialized, pid [%d]\n", getpid()); return 0; } /** * fixes the module functions' parameters with generic pseudo variable support. * * @param param the parameter * * @return 0 on success, -1 on failure */ static int pv_fixup(void ** param) { pv_elem_t *model; str s; s.s = (char *)(*param); s.len = strlen(s.s); if (s.len <= 0) return -1; /* Check the format */ if(pv_parse_format(&s, &model)<0) { LM_ERR("pv_parse_format failed for '%s'\n", (char *)(*param)); return -1; } *param = (void*)model; return 0; } /** * fixes the module functions' parameters if it is a carrier. * supports name string, pseudo-variables and AVPs. * * @param param the parameter * * @return 0 on success, -1 on failure */ static int carrier_fixup(void ** param) { pv_spec_t avp_spec; struct multiparam_t *mp; str s; mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t)); if (mp == NULL) { LM_ERR("no more memory\n"); return -1; } memset(mp, 0, sizeof(struct multiparam_t)); s.s = (char *)(*param); s.len = strlen(s.s); if (s.s[0]!='$') { /* This is a name string */ mp->type=MP_INT; /* get carrier id */ if ((mp->u.n = find_tree(s)) < 0) { LM_ERR("could not find carrier tree '%s'\n", (char *)(*param)); pkg_free(mp); return -1; } LM_INFO("carrier tree %s has id %i\n", (char *)*param, mp->u.n); pkg_free(*param); *param = (void *)mp; } else { /* This is a pseudo-variable */ if (pv_parse_spec(&s, &avp_spec)==0) { LM_ERR("pv_parse_spec failed for '%s'\n", (char *)(*param)); pkg_free(mp); return -1; } if (avp_spec.type==PVT_AVP) { /* This is an AVP - could be an id or name */ mp->type=MP_AVP; if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) { LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param)); pkg_free(mp); return -1; } } else { mp->type=MP_PVE; if(pv_parse_format(&s, &(mp->u.p))<0) { LM_ERR("pv_parse_format failed for '%s'\n", (char *)(*param)); pkg_free(mp); return -1; } } } *param = (void*)mp; return 0; } /** * fixes the module functions' parameters if it is a domain. * supports name string, and AVPs. * * @param param the parameter * * @return 0 on success, -1 on failure */ static int domain_fixup(void ** param) { pv_spec_t avp_spec; struct multiparam_t *mp; str s; mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t)); if (mp == NULL) { LM_ERR("no more memory\n"); return -1; } memset(mp, 0, sizeof(struct multiparam_t)); s.s = (char *)(*param); s.len = strlen(s.s); if (s.s[0]!='$') { /* This is a name string */ mp->type=MP_INT; /* get domain id */ if ((mp->u.n = add_domain(&s)) < 0) { LM_ERR("could not add domain\n"); pkg_free(mp); return -1; } pkg_free(*param); *param = (void *)mp; } else { /* This is a pseudo-variable */ if (pv_parse_spec(&s, &avp_spec)==0) { LM_ERR("pv_parse_spec failed for '%s'\n", (char *)(*param)); pkg_free(mp); return -1; } if (avp_spec.type==PVT_AVP) { /* This is an AVP - could be an id or name */ mp->type=MP_AVP; if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) { LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param)); pkg_free(mp); return -1; } } else { mp->type=MP_PVE; if(pv_parse_format(&s, &(mp->u.p))<0) { LM_ERR("pv_parse_format failed for '%s'\n", (char *)(*param)); pkg_free(mp); return -1; } } } *param = (void*)mp; return 0; } /** * fixes the module functions' parameters in case of AVP names. * * @param param the parameter * * @return 0 on success, -1 on failure */ static int avp_name_fixup(void ** param) { pv_spec_t avp_spec; struct multiparam_t *mp; str s; s.s = (char *)(*param); s.len = strlen(s.s); if (s.len <= 0) return -1; if (pv_parse_spec(&s, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("Malformed or non AVP definition <%s>\n", (char *)(*param)); return -1; } mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t)); if (mp == NULL) { LM_ERR("no more memory\n"); return -1; } memset(mp, 0, sizeof(struct multiparam_t)); mp->type=MP_AVP; if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) { LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param)); pkg_free(mp); return -1; } *param = (void*)mp; return 0; } /** * fixes the module functions' parameters, i.e. it maps * the routing domain names to numbers for faster access * at runtime * * @param param the parameter * @param param_no the number of the parameter * * @return 0 on success, -1 on failure */ static int route_fixup(void ** param, int param_no) { enum hash_source my_hash_source; if (param_no == 1) { /* carrier */ if (carrier_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } else if (param_no == 2) { /* domain */ if (domain_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } else if ((param_no == 3) || (param_no == 4)){ /* prefix matching */ /* rewrite user */ if (pv_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } else if (param_no == 5) { /* hash source */ if ((my_hash_source = hash_fixup((char *)*param)) == shs_error) { LM_ERR("invalid hash source\n"); return -1; } pkg_free(*param); *param = (void *)my_hash_source; } else if (param_no == 6) { /* destination avp name */ if (avp_name_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } return 0; } /** * fixes the module functions' parameters, i.e. it maps * the routing domain names to numbers for faster access * at runtime * * @param param the parameter * @param param_no the number of the parameter * * @return 0 on success, -1 on failure */ static int load_next_domain_fixup(void ** param, int param_no) { if (param_no == 1) { /* carrier */ if (carrier_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } else if (param_no == 2) { /* domain */ if (domain_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } else if ((param_no == 3) || (param_no == 4) || (param_no == 5)) { /* prefix matching */ /* host */ /* reply code */ if (pv_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } else if (param_no == 6) { /* destination avp name */ if (avp_name_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } return 0; } static int load_user_carrier_fixup(void ** param, int param_no) { if (mode == SP_ROUTE_MODE_FILE) { LM_ERR("command cr_user_rewrite_uri can't be used in file mode\n"); return -1; } if ((param_no == 1) || (param_no == 2)) { /* user */ /* domain */ if (pv_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } else if (param_no == 3) { /* destination avp name */ if (avp_name_fixup(param) < 0) { LM_ERR("cannot fixup parameter %d\n", param_no); return -1; } } return 0; } static int child_init(int rank) { return data_child_init(); } static void mod_destroy(void) { destroy_route_data(); } opensips-2.2.2/modules/carrierroute/carrierroute.h000066400000000000000000000061631300170765700224260ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file carrierroute.h * @brief Contains the functions exported by the module. */ #ifndef SP_ROUTE_H #define SP_ROUTE_H #include "../../str.h" #include "../../usr_avp.h" #include "../../pvar.h" #define SIP_URI "sip:" #define SIP_URI_LEN 4 #define SIPS_URI "sips:" #define SIPS_URI_LEN 5 #define AT_SIGN "@" #define AT_SIGN_LEN 1 #define DICE_MAX 1000 #define COLUMN_NUM 12 #define COL_ID 0 #define COL_CARRIER 1 #define COL_DOMAIN 2 #define COL_SCAN_PREFIX 3 #define COL_FLAGS 4 #define COL_MASK 5 #define COL_PROB 6 #define COL_REWRITE_HOST 7 #define COL_STRIP 8 #define COL_REWRITE_PREFIX 9 #define COL_REWRITE_SUFFIX 10 #define COL_COMMENT 11 #define FAILURE_COLUMN_NUM 10 #define FCOL_ID 0 #define FCOL_CARRIER 1 #define FCOL_DOMAIN 2 #define FCOL_SCAN_PREFIX 3 #define FCOL_HOST_NAME 4 #define FCOL_REPLY_CODE 5 #define FCOL_FLAGS 6 #define FCOL_MASK 7 #define FCOL_NEXT_DOMAIN 8 #define FCOL_COMMENT 9 #define SUBSCRIBER_COLUMN_NUM 3 #define SUBSCRIBER_USERNAME_COL 0 #define SUBSCRIBER_DOMAIN_COL 1 #define SUBSCRIBER_CARRIER_COL 2 #define CARRIER_COLUMN_NUM 2 #define CARRIER_ID_COL 0 #define CARRIER_NAME_COL 1 #define SP_ROUTE_MODE_DB 1 #define SP_ROUTE_MODE_FILE 2 #define ROUTE_TABLE_VER 3 #define CARRIER_TABLE_VER 2 #define FAILURE_TABLE_VER 2 extern str db_url; extern str db_table; extern str db_failure_table; extern str carrier_table; extern str subscriber_table; extern str * subscriber_columns[]; extern str * carrier_columns[]; extern str * columns[]; extern str * failure_columns[]; extern char * config_source; extern char * config_file; extern str default_tree; extern const str SP_EMPTY_PREFIX; extern int mode; extern int use_domain; extern int fallback_default; enum hash_algorithm { alg_crc32 = 1, /*!< hashing algorithm is CRC32 */ alg_prime, /*!< hashing algorithm is (right 18 digits of hash_source % prime_number) % max_targets + 1 */ alg_error }; /** * Generic parameter that holds a string, an int or an pseudo-variable * @todo replace this with gparam_t */ struct multiparam_t { enum { MP_INT, MP_STR, MP_AVP, MP_PVE, } type; union { int n; str s; struct { unsigned short flags; int name; } a; pv_elem_t *p; } u; }; #endif opensips-2.2.2/modules/carrierroute/doc/000077500000000000000000000000001300170765700203065ustar00rootroot00000000000000opensips-2.2.2/modules/carrierroute/doc/carrierroute.xml000066400000000000000000000027221300170765700235410ustar00rootroot00000000000000 %docentities; ]> carrierroute &osipsname; Jonas Appel 1&1 Internet AG Hardy Kahl 1&1 Internet AG Henning Westerholt 1&1 Internet AG
henning.westerholt@1und1.de
2007 1&1 Internet AG $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/carrierroute/doc/carrierroute_admin.xml000066400000000000000000001517461300170765700247240ustar00rootroot00000000000000 &adminguide;
Overview A module which provides routing, balancing and blacklisting capabilities. The module provides routing, balancing and blacklisting capabilities. It reads routing entries from a database source or from a config file at OpenSIPS startup. It can uses one routing tree (for one carrier), or if needed for every user a different routing tree (unique for each carrier) for number prefix based routing. It supports several route tree domains, e.g. for failback routes or different routing rules for VoIP and PSTN targets. Based on the tree, the module decides which number prefixes are forwarded to which gateway. It can also distribute the traffic by ratio parameters. Furthermore, the requests can be distributed by a hash funcion to predictable destinations. The hash source is configurable, two different hash functions are available. This modules scales up to more than a few million users, and is able to handle more than several hundred thousand routing table entries. It should be able to handle more, but this is not that much tested at the moment. In load balancing scenarios the usage of the config file mode is recommended, to avoid the additional complexity that the database driven routing creates. Routing tables can be reloaded and edited (in config file mode) with the MI interface, the config file is updated according the changes. This is not implemented for the db interface, because its easier to do the changes directly on the db. But the reload and dump functions works of course here too. Some module functionality is not fully available in the config file mode, as it is not possible to specify all information that can be stored in the database tables in the config file. Further information about these limitations is given in later sections. For user based routing or LCR you should use the database mode. Basically this module could be used as an replacement for the lcr and the dispatcher module, if you have certain performance, flexibility and/or integration requirements that these modules don't handle properly. But for small installations it probably make more sense to use the lcr and dispatcher module. If you want to use this module in failure routes, then you need to call append_branch() after rewriting the request URI in order to relay the message to the new target. Its also supportes the usage of database derived failure routing descisions with the carrierfailureroute table.
Dependencies
&osips; Modules The following module must be loaded before this module: a database module, when a database is used as configuration data source. Only SQL based databases are supported, as this module needs the capability to issue raw queries. Its not possible to use the dbtext or db_berkeley module at the moment. The tm module, when you want to use the $T_reply_code pseudo-variable in the cr_next_domain function.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libconfuse, a configuration file parser library. ( http://www.nongnu.org/confuse/ )
Exported Parameters
<varname>db_url</varname> (string) Url to the database containing the routing data. Default value is &defaultrodb;. Set <varname>db_url</varname> parameter ... modparam("carrierroute", "db_url", "&exampledb;") ...
<varname>db_table</varname> (string) Name of the table where the routing data is stored. Default value is carrierroute. Set <varname>db_table</varname> parameter ... modparam("carrierroute", "db_table", "carrierroute") ...
<varname>id_column</varname> (string) Name of the column containing the id identifier. Default value is id. Set <varname>id_column</varname> parameter ... modparam("carrierroute", "id_column", "id") ...
<varname>carrier_column</varname> (string) Name of the column containing the carrier id. Default value is carrier. Set <varname>carrier_column</varname> parameter ... modparam("carrierroute", "carrier_column", "carrier") ...
<varname>scan_prefix_column</varname> (string) Name of column containing the scan prefixes. Scan prefixes define the matching portion of a phone number, e.g. when we have the scan prefixes 49721 and 49, the called number is 49721913740, it matches 49721, because the longest match is taken. If no prefix matches, the number is not routed. To prevent this, an empty prefix value of could be added. Default value is scan_prefix. Set <varname>scan_prefix_column</varname> parameter ... modparam("carrierroute", "scan_prefix_column", "scan_prefix") ...
<varname>domain_column</varname> (string) Name of column containing the rule domain. You can define several routing domains to have different routing rules. Maybe you use domain 0 for normal routing and domain 1 if domain 0 failed. Default value is domain. Set <varname>domain_column</varname> parameter ... modparam("carrierroute", "domain_column", "domain") ...
<varname>flags_column</varname> (string) Name of the column containing the flags. Default value is flags. Set <varname>flags_column</varname> parameter ... modparam("carrierroute", "flags_column", "flags") ...
<varname>mask_column</varname> (string) Name of the column containing the flags mask. Default value is mask. Set <varname>mask_column</varname> parameter ... modparam("carrierroute", "mask_column", "mask") ...
<varname>prob_column</varname> (string) Name of column containing probability. The probability value is used to distribute the traffic between several gateways. Let's say 70 % of the traffic shall be routed to gateway A, the other 30 % shall be routed to gateway B, we define a rule for gateway A with a prob value of 0.7 and a rule for gateway B with a prob value of 0.3. If all probabilities for a given prefix, tree and domain don't add to 100%, the prefix values will be adjusted according the given prob values. E.g. if three hosts with prob values of 0.5, 0.5 and 0.4 are defined, the resulting probabilities are 35.714, 35.714 and 28.571%. But its better to choose meaningful values in the first place because of clarity. Default value is prob. Set <varname>prob_column</varname> parameter ... modparam("carrierroute", "prob_column", "prob") ...
<varname>rewrite_host_column</varname> (string) Name of column containing rewrite host value. An empty field represents a blacklist entry, anything else is put as domain part into the Request URI of the SIP message. Default value is rewrite_host. Set <varname>rewrite_host_column</varname> parameter ... modparam("carrierroute", "rewrite_host_column", "rewrite_host") ...
<varname>strip_column</varname> (string) Name of the column containing the number of digits to be stripped of the userpart of an URI before prepending rewrite_prefix. Default value is strip. Set <varname>strip_column</varname> parameter ... modparam("carrierroute", "strip_column", "strip") ...
<varname>comment_column</varname> (string) Name of the column containing an optional comment (useful in large routing tables) The comment is also displayed by the fifo cmd "cr_dump_routes". Default value is description. Set <varname>comment_column</varname> parameter ... modparam("carrierroute", "comment_column", "description") ...
<varname>carrier_table</varname> (string) The name of the table containing the existing carriers, consisting of the ids and corresponding names. Default value is route_tree. Set <varname>carrier_table</varname> parameter ... modparam("carrierroute", "carrier_table", "route_tree") ...
<varname>rewrite_prefix_column</varname> (string) Name of column containing rewrite prefixes. Here you can define a rewrite prefix for the localpart of the SIP URI. Default value is rewrite_prefix. Set <varname>rewrite_prefix_column</varname> parameter ... modparam("carrierroute", "rewrite_prefix_column", "rewrite_prefix") ...
<varname>rewrite_suffix_column</varname> (string) Name of column containing rewrite suffixes. Here you can define a rewrite suffix for the localpart of the SIP URI. Default value is rewrite_suffix. Set <varname>rewrite_suffix_column</varname> parameter ... modparam("carrierroute", "rewrite_suffix_column", "rewrite_suffix") ...
<varname>carrier_id_col</varname> (string) The name of the column in the carrier table containing the carrier id. Default value is id. Set <varname>id_col</varname> parameter ... modparam("carrierroute", "carrier_id_col", "id") ...
<varname>carrier_name_col</varname> (string) The name of the column in the carrier table containing the carrier name. Default value is carrier. Set <varname>carrier_name_col</varname> parameter ... modparam("carrierroute", "carrier_name_col", "carrier") ...
<varname>subscriber_table</varname> (string) The name of the table containing the subscribers Default value is subscriber. Set <varname>subscriber_table</varname> parameter ... modparam("carrierroute", "subscriber_table", "subscriber") ...
<varname>subscriber_user_col</varname> (string) The name of the column in the subscriber table containing the usernames. Default value is username. Set <varname>subscriber_user_col</varname> parameter ... modparam("carrierroute", "subscriber_user_col", "username") ...
<varname>subscriber_domain_col</varname> (string) The name of the column in the subscriber table containing the domain of the subscriber. Default value is domain. Set <varname>subscriber_domain_col</varname> parameter ... modparam("carrierroute", "subscriber_domain_col", "domain") ...
<varname>subscriber_carrier_col</varname> (string) The name of the column in the subscriber table containing the carrier id of the subscriber. Default value is cr_preferred_carrier. Set <varname>subscriber_carrier_col</varname> parameter ... modparam("carrierroute", "subscriber_carrier_col", "cr_preferred_carrier") ...
<varname>config_source</varname> (string) Specifies whether the module loads its config data from a file or from a database. Possible values are file or db. Default value is file. Set <varname>config_source</varname> parameter ... modparam("carrierroute", "config_source", "file") ...
<varname>config_file</varname> (string) Specifies the path to the config file. Default value is /etc/opensips/carrierroute.conf. Set <varname>config_file</varname> parameter ... modparam("carrierroute", "config_file", "/etc/opensips/carrierroute.conf") ...
<varname>default_tree</varname> (string) The name of the carrier tree used per default (if the current subscriber has no preferred tree) Default value is default. Set <varname>default_tree</varname> parameter ... modparam("carrierroute", "default_tree", "default") ...
<varname>use_domain</varname> (int) When using tree lookup per user, this parameter specifies whether to use the domain part for user matching or not. Default value is 0. Set <varname>use_domain</varname> parameter ... modparam("carrierroute", "use_domain", 0) ...
<varname>fallback_default</varname> (int) This parameter defines the behaviour when using user-based tree lookup. If the user has a non-existing tree set and fallback_default is set to 1, the default tree is used. Otherwise, cr_user_rewrite_uri returns an error. Default value is 1. Set <varname>fallback_default</varname> parameter ... modparam("carrierroute", "fallback_default", 1) ...
<varname>db_failure_table</varname> (string) Name of the table where the failure routing data is stored. Default value is carrierfailureroute. Set <varname>db_failure_table</varname> parameter ... modparam("carrierroute", "db_failure_table", "carrierfailureroute") ...
<varname>failure_id_column</varname> (string) Name of the column containing the id identifier. Default value is id. Set <varname>failure_id_column</varname> parameter ... modparam("carrierroute", "failure_id_column", "id") ...
<varname>failure_carrier_column</varname> (string) Name of the column containing the carrier id. Default value is carrier. Set <varname>failure_carrier_column</varname> parameter ... modparam("carrierroute", "failure_carrier_column", "carrier") ...
<varname>failure_scan_prefix_column</varname> (string) Name of column containing the scan prefixes. Scan prexies define the matching portion of a phone number, e.g. we have the scan prefixes 49721 and 49, the called number is 49721913740, it matches 49721, because the longest match is taken. If no prefix matches, the number is not failure routed. To prevent this, an empty prefix value of could be added. Default value is scan_prefix. Set <varname>failure_scan_prefix_column</varname> parameter ... modparam("carrierroute", "failure_scan_prefix_column", "scan_prefix") ...
<varname>failure_domain_column</varname> (string) Name of column containing the rule domain. You can define several routing domains to have different routing rules. Maybe you use domain 0 for normal routing and domain 1 if domain 0 failed. Default value is domain. Set <varname>failure_domain_column</varname> parameter ... modparam("carrierroute", "failure_domain_column", "domain") ...
<varname>failure_host_name_column</varname> (string) Name of the column containing the host name of the last routing destination. Default value is host_name. Set <varname>failure_host_name_column</varname> parameter ... modparam("carrierroute", "failure_host_name_column", "host_name") ...
<varname>failure_reply_code_column</varname> (string) Name of the column containing the reply code. Default value is reply_code. Set <varname>failure_reply_code_column</varname> parameter ... modparam("carrierroute", "failure_reply_code_column", "reply_code") ...
<varname>failure_flags_column</varname> (string) Name of the column containing the flags. Default value is flags. Set <varname>failure_flags_column</varname> parameter ... modparam("carrierroute", "failure_flags_column", "flags") ...
<varname>failure_mask_column</varname> (string) Name of the column containing the flags mask. Default value is mask. Set <varname>failure_mask_column</varname> parameter ... modparam("carrierroute", "failure_mask_column", "mask") ...
<varname>failure_next_domain_column</varname> (string) Name of the column containing the next routing domain. Default value is next_domain. Set <varname>failure_next_domain_column</varname> parameter ... modparam("carrierroute", "failure_next_domain_column", "next_domain") ...
<varname>failure_comment_column</varname> (string) Name of the column containing an optional comment. Default value is description. Set <varname>failure_comment_column</varname> parameter ... modparam("carrierroute", "failure_comment_column", "description") ...
Exported Functions Previous versions of carrierroute had some more function. All the old semantics can be achieved by using the few new functions like this: cr_rewrite_uri(domain, hash_source) -> cr_route("default", domain, "$rU", "$rU", hash_source) cr_prime_balance_uri(domain, hash_source) -> cr_prime_route("default", domain, "$rU", "$rU", hash_source) cr_rewrite_by_to(domain, hash_source) -> cr_route("default", domain, "$tU", "$rU", hash_source) cr_prime_balance_by_to(domain, hash_source) -> cr_prime_route("default", domain, "$tU", "$rU", hash_source) cr_rewrite_by_from(domain, hash_source) -> cr_route("default", domain, "$fU", "$rU", hash_source) cr_prime_balance_by_from(domain, hash_source) -> cr_prime_route("default", domain, "$fU", "$rU", hash_source) cr_user_rewrite_uri(uri, domain) -> cr_user_carrier(user, domain, "$avp(tree_avp)") -> cr_route("$avp(tree_avp)", domain, "$rU", "$rU", "call_id") cr_tree_rewrite_uri(tree, domain) -> cr_route(tree, domain, "$rU", "$rU", "call_id")
<function moreinfo="none">cr_user_carrier(user, domain, dstavp)</function> This function loads the carrier and stores it in an AVP. It cannot be used in the config file mode, as it needs a mapping of the given user to a certain carrier. The is derived from a database entry belonging to the user parameter. This mapping must be available in the table that is specified in the subscriber_table variable. This data is not cached in memory, that means for every execution of this function a database query will be done. Meaning of the parameters is as follows: user - Name of the user for the carrier tree lookup. Additional to a string any pseudo-variable could be used as input. domain - Name of the routing domain to be used. Additional to a string any pseudo-variable could be used as input. dstavp - Name of the AVP where to store the carrier id.
<function moreinfo="none">cr_route(carrier, domain, prefix_matching, rewrite_user, hash_source, dstavp)</function> This function searches for the longest match for the user given in prefix_matching at the given domain in the given carrier tree. The Request URI is rewritten using rewrite_user and the given hash source and algorithm. Returns -1 if there is no data found or an empty rewrite host on the longest match is found. Otherwise the rewritten host is stored in the given AVP (if obmitted, the host is not stored in an AVP). This function is only usable with rewrite_user and prefix_matching containing a valid numerical only string. It uses the standard crc32 algorithm to calculate the hash values. Meaning of the parameters is as follows: carrier - The routing tree to be used. Additional to a string any pseudo-variable could be used as input. domain - Name of the routing domain to be used. Additional to a string any pseudo-variable could be used as input. prefix_matching - User name to be used for prefix matching in the routing tree. Additional to a string any pseudo-variable could be used as input. rewrite_user - The user name to be used for applying the rewriting rule. Usually this is the user part of the request URI. Additional to a string any pseudo-variable could be used as input. hash_source - The hash values of the destination set must be a contiguous range starting at 1, limited by the configuration parameter max_targets. Possible values for hash_source are: call_id, from_uri, from_user, to_uri and to_user. dstavp - Name of the AVP where to store the rewritten host. This parameter is optional.
<function moreinfo="none">cr_prime_route(carrier, domain, prefix_matching, rewrite_user, hash_source, dstavp)</function> This function searches for the longest match for the user given in prefix_matching at the given domain in the given carrier tree. The Request URI is rewritten using rewrite_user and the given hash source and algorithm. Returns -1 if there is no data found or an empty rewrite host on the longest match is found. Otherwise the rewritten host is stored in the given AVP (if obmitted, the host is not stored in an AVP). This function is only usable with rewrite_user and prefix_matching containing a valid numerical only string. It uses the prime hash algorithm to calculate the hash values. Meaning of the parameters is as follows: carrier - The routing tree to be used. Additional to a string any pseudo-variable could be used as input. domain - Name of the routing domain to be used. Additional to a string any pseudo-variable could be used as input. prefix_matching - User name to be used for prefix matching in the routing tree. Additional to a string any pseudo-variable could be used as input. rewrite_user - The user name to be used for applying the rewriting rule. Usually this is the user part of the request URI. Additional to a string any pseudo-variable could be used as input. hash_source - The hash values of the destination set must be a contiguous range starting at 1, limited by the configuration parameter max_targets. Possible values for hash_source are: call_id, from_uri, from_user, to_uri and to_user. dstavp - Name of the AVP where to store the rewritten host. This parameter is optional.
<function moreinfo="none">cr_next_domain(carrier, domain, prefix_matching, host, reply_code, dstavp)</function> This function searches for the longest match for the user given in prefix_matching at the given domain in the given carrier failure tree. It tries to find a next domain matching the given host, reply_code and the message flags. The matching is done in this order: host, reply_code and then flags. The more wildcards in reply_code and the more bits used in flags, the lower the priority. Returns -1 if there is no data found or an empty next_domain on the longest match is found. Otherwise the next domain is stored in the given AVP. This function is only usable with prefix_matching containing a valid numerical only string. Meaning of the parameters is as follows: carrier - The routing tree to be used. Additional to a string any pseudo-variable could be used as input. domain - Name of the routing domain to be used. Additional to a string any pseudo-variable could be used as input. prefix_matching - User name to be used for prefix matching in the routing tree. Additional to a string any pseudo-variable could be used as input. host - The host name to be used for failure route rule matching. Usually this is the last tried routing destination stored in an avp by cr_route. Additional to a string any pseudo-variable could be used as input. reply_code - The reply code to be used for failure route rule matching. Additional to a string any pseudo-variable could be used as input. dstavp - Name of the AVP where to store the next routing domain.
<acronym>MI</acronym> Commands All commands understand the "-?" parameter to print a short help message. The options have to be quoted as one string to be passed to MI interface. Each option except host and new host can be wildcarded by * (but only * and not things like "-d prox*").
<function moreinfo="none">cr_reload_routes</function> This command reloads the routing data from the data source. Important: When new domains have been added, a restart of the server must be done, because the mapping of the ids used in the config script cannot be updated at runtime at the moment. So a reload could result in a wrong routing behaviour, because the ids used in the script could differ from the one used internally from the server. Modifying of already existing domains is no problem.
<function moreinfo="none">cr_dump_routes</function> This command prints the route rules on the command line.
<function moreinfo="none">cr_replace_host</function> This command can replace the rewrite_host of a route rule, it is only usable in file mode. Following options are possible: -d - the domain containing the host -p - the prefix containing the host -h - the host to be replaced -t - the new host Use the "null" prefix to specify an empty prefix. <function>cr_replace_host</function> usage ... opensipsctl fifo cr_replace_host "-d proxy -p 49 -h proxy1 -t proxy2" ...
<function moreinfo="none">cr_deactivate_host</function> This command deactivates the specified host, i.e. it sets its status to 0. It is only usable in file mode. Following options are possible: -d - the domain containing the host -p - the prefix containing the host -h - the host to be deactivated -t - the new host used as backup When -t (new_host) is specified, the portion of traffic for the deactivated host is routed to the host given by -t. This is indicated in the output of dump_routes. The backup route is deactivated if the host is activated again. Use the "null" prefix to specify an empty prefix. <function>cr_deactivate_host</function> usage ... opensipsctl fifo cr_deactivate_host "-d proxy -p 49 -h proxy1" ...
<function moreinfo="none">cr_activate_host</function> This command activates the specified host, i.e. it sets its status to 1. It is only usable in file mode. Following options are possible: -d - the domain containing the host -p - the prefix containing the host -h - the host to be activated Use the "null" prefix to specify an empty prefix. <function>cr_activate_host</function> usage ... opensipsctl fifo cr_activate_host "-d proxy -p 49 -h proxy1" ...
<function moreinfo="none">cr_add_host</function> This command adds a route rule, it is only usable in file mode. Following options are possible: -d - the domain containing the host -p - the prefix containing the host -h - the host to be added -w - the weight of the rule -P - an optional rewrite prefix -S - an optional rewrite suffix -i - an optional hash index -s - an optional strip value Use the "null" prefix to specify an empty prefix. <function>cr_add_host</function> usage ... opensipsctl fifo cr_add_host "-d proxy -p 49 -h proxy1 -w 0.25" ...
<function moreinfo="none">cr_delete_host</function> This command delete the specified hosts or rules, i.e. remove them from the route tree. It is only usable in file mode. Following options are possible: -d - the domain containing the host -p - the prefix containing the host -h - the host to be added -w - the weight of the rule -P - an optional rewrite prefix -S - an optional rewrite suffix -i - an optional hash index -s - an optional strip value Use the "null" prefix to specify an empty prefix. <function>cr_delete_host</function> usage ... opensipsctl fifo cr_delete_host "-d proxy -p 49 -h proxy1 -w 0.25" ...
Examples Configuration example - Routing to default tree ... route { # route calls based on hash over callid # choose route domain 0 of the default carrier if(!cr_route("default", "0", "$rU", "$rU", "call_id", "crc32")){ sl_send_reply("403", "Not allowed"); } else { # In case of failure, re-route the request t_on_failure("1"); # Relay the request to the gateway t_relay(); } } failure_route[1] { # In case of failure, send it to an alternative route: if (t_check_status("408|5[0-9][0-9]")) { #choose route domain 1 of the default carrier if(!cr_route("default", "1", "$rU", "$rU", "call_id", "crc32")){ t_reply("403", "Not allowed"); } else { t_on_failure("2"); t_relay(); } } } failure_route[2] { # further processing } Configuration example - Routing to user tree ... route[1] { cr_user_carrier("$fU", "$fd", "$avp(carrier)"); # just an example domain $avp(domain)="start"; if (!cr_route("$avp(carrier)", "$avp(domain)", "$rU", "$rU", "call_id", "$avp(host)")) { xlog("L_ERR", "cr_route failed\n"); exit; } t_on_failure("1"); if (!t_relay()) { sl_reply_error(); }; } failure_route[1] { revert_uri(); if (!cr_next_domain("$avp(carrier)", "$avp(domain)", "$rU", "$avp(host)", "$T_reply_code", "$avp(domain)")) { xlog("L_ERR", "cr_next_domain failed\n"); exit; } if (!cr_route("$avp(carrier)", "$avp(domain)", "$rU", "$rU", "call_id", "$avp(host)")) { xlog("L_ERR", "cr_route failed\n"); exit; } t_on_failure("1"); append_branch(); if (!t_relay()) { xlog("L_ERR", "t_relay failed\n"); exit; }; } ... Configuration example - module configuration The following config file specifies within the default carrier two domains, each with an prefix that contains two hosts. It is not possible to specify another carrier if you use the config file as data source. All traffic will be equally distributed between the hosts, both are active. The hash algorithm will working over the [1,2] set, messages hashed to one will go to the first host, the other to the second one. Don't use a hash index value of zero. If you ommit the hash completly, the module gives them a autogenerated value, starting from one. Use the NULL prefix to specify an empty prefix in the config file. Please note that the prefix is matched against the request URI (or to URI), if they did not contain a valid numerical URI, no match is possible. So for loadbalancing purposes e.g. for your registrars, you should use an empty prefix. ... domain proxy { prefix 49 { max_targets = 2 target proxy1.localdomain { prob = 0.500000 hash_index = 1 status = 1 comment = "test target 1" } target proxy2.localdomain { prob = 0.500000 hash_index = 2 status = 1 comment = "test target 2" } } } domain register { prefix NULL { max_targets = 2 target register1.localdomain { prob = 0.500000 hash_index = 1 status = 1 comment = "test target 1" } target register2.localdomain { prob = 0.500000 hash_index = 2 status = 1 comment = "test target 2" } } } ...
Installation and Running
Database setup Before running &osips; with carrierroute, you have to setup the database table where the module will store the routing data. For that, if the table was not created by the installation script or you choose to install everything by yourself you can use the carrierroute-create.sql SQL script in the database directories in the opensips/scripts folder as template. Database and table name can be set with module parameters so they can be changed, but the name of the columns must be as they are in the SQL script. You can also find the complete database documentation on the project webpage, &osipsdbdocs;. The flags and mask columns have the same function as in the carrierfailureroute table. A zero value in the flags and mask column means that any message flags will match this rule. For a minimal configuration either use the config file given above, or insert some data into the tables of the module. Example database content - carrierroute table ... +----+---------+--------+-------------+-------+------+---------------+ | id | carrier | domain | scan_prefix | flags | prob | rewrite_host | +----+---------+--------+-------------+-------+------+---------------+ | 1 | 1 | 0 | 49 | 0 | 0.5 | de-1.carrier1 | | 2 | 1 | 0 | 49 | 0 | 0.5 | de-2.carrier1 | | 3 | 1 | 0 | 49 | 16 | 1 | de-3.carrier1 | | 4 | 1 | 0 | | 0 | 1 | gw.carrier1-1 | | 5 | 1 | 1 | 49 | 0 | 1 | gw.carrier1-1 | | 6 | 1 | 2 | | 0 | 1 | gw.carrier1-2 | | 7 | 1 | 3 | | 0 | 1 | gw.carrier1-3 | | 8 | 2 | 0 | 49 | 0 | 0.5 | de-1.carrier2 | | 9 | 2 | 0 | 49 | 0 | 0.5 | de-2.carrier2 | | 10 | 2 | 0 | | 0 | 1 | gw.carrier2 | | 11 | 2 | 1 | 49 | 0 | 1 | gw.carrier2 | | 12 | 3 | start | 49 | 0 | 1 | de-gw.default | | 13 | 3 | start | | 0 | 1 | gw.default | +----+---------+--------+-------------+-------+------+---------------+ ... This table contains three routes to two gateways for the 49 prefix, and a default route for other prefixes over carrier 2 and carrier 1. The gateways for the default carrier will be used for functions that don't support the user specific carrier lookup. The routing rules for carrier 1 and carrier 2 for the 49 prefix contains a additional rule with the domain 1, that can be used for example as fallback if the gateways in domain 0 are not reachable. Two more fallback rules (domain 2 and 3) for carrier 1 are also supplied to support the functionality of the carrierfailureroute table example that is provided in the next section. The usage of strings for the domains is also possible, for example at carrier 3. This table provides also a carrier1 routing rule for the 49 prefix, that is only choosen if some message flags are set. If this flags are not set, the other two rules are used. The strip, mask and comment colums are omitted for brevity. Example database content - simple carrierfailureroute table ... +----+---------+--------+---------------+------------+-------------+ | id | carrier | domain | host_name | reply_code | next_domain | +----+---------+--------+---------------+------------+-------------+ | 1 | 1 | 0 | gw.carrier1-2 | ... | 3 | | 2 | 1 | 0 | gw.carrier1-3 | ... | 2 | +----+---------+--------+---------------+------------+-------------+ ... This table contains two failure routes for the gw.carrier1-1 and -2 gateways. For any (failure) reply code the respective next domain is choosen. After that no more failure routes are available, an error will be returned from the cr_next_domain function. Not all table colums are show here for brevity. For each failure route domain and carrier that is added to the carrierfailureroute table there must be at least one corresponding entry in the carrierroute table, otherwise the module will not load the routing data. Example database content - more complex carrierfailureroute table ... +----+---------+-----------+------------+--------+-----+-------------+ | id | domain | host_name | reply_code | flags | mask | next_domain | +----+---------+-----------+------------+-------+------+-------------+ | 1 | 99 | | 408 | 16 | 16 | | | 2 | 99 | gw1 | 404 | 0 | 0 | 100 | | 3 | 99 | gw2 | 50. | 0 | 0 | 100 | | 4 | 99 | | 404 | 2048 | 2112 | asterisk-1 | +----+---------+-----------+------------+-------+------+-------------+ ... This table contains four failure routes that shows the usage of more advanced features. The first route matches to a 408, and to some flag for example that indicates that ringing has happened. If this flag is set, there will be no further forwarding, because next_domain is empty. In the second and third routes are certain gateway errors matched, if this errors have occurred, then the next domain will be chosen. The last route does forwarding according some flags, e.g. the customer came from a certain carrier, and has call-forwarding deactivated. In order to use the routing that is specified above, a matching carrierroute table must be provided, that holds domain entries for this routing rules. Not all table colums are show here for brevity. Example database content - route_tree table ... +----+----------+ | id | carrier | +----+----------+ | 1 | carrier1 | | 2 | carrier2 | | 3 | default | +----+----------+ ... This table contains the mapping of the carrier id to actual names. For a functional routing the cr_preferred_carrier column must be added to the subscriber table (or to the table and column that you specified as modul parameter) to choose the actual carrier for the users. Necessary extensions for the user table Suggested changes: ... ALTER TABLE subscriber ADD cr_preferred_carrier int(10) default NULL; ...
opensips-2.2.2/modules/carrierroute/load_data.c000066400000000000000000000050701300170765700216170ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file load_data.c * @brief API to bind a data loading function. */ #include #include #include #include "../../globals.h" #include "load_data.h" #include "route_db.h" #include "route_config.h" #include "carrierroute.h" /** * Binds the loader function pointer api to the matching loader * function depending on source * * @param source the configuration data source, at the moment * it can be db or file * @param api pointer to the api where the loader function is * bound to * * @return 0 means everything is ok, -1 means an error */ int bind_data_loader(const char * source, route_data_load_func_t * api){ struct stat fs; if(strcmp(source, "db") == 0){ LM_INFO("use database as configuration source"); *api = load_route_data; mode = SP_ROUTE_MODE_DB; if(db_init() < 0){ return -1; } return 0; } if(strcmp(source, "file") == 0){ LM_INFO("use file as configuration source"); *api = load_config; mode = SP_ROUTE_MODE_FILE; if(stat(config_file, &fs) != 0){ LM_ERR("can't stat config file\n"); return -1; } if(fs.st_mode & S_IWOTH){ LM_WARN("insecure file permissions, routing data is world writable"); } if( !( fs.st_mode & S_IWOTH) && !((fs.st_mode & S_IWGRP) && (fs.st_gid == getegid())) && !((fs.st_mode & S_IWUSR) && (fs.st_uid == geteuid())) ) { LM_ERR("config file not writable\n"); return -1; } return 0; } LM_ERR("could not bind configuration source <%s>", source); return -1; } int data_main_finalize(void){ if(mode == SP_ROUTE_MODE_DB){ main_db_close(); } return 0; } int data_child_init(void){ if(mode == SP_ROUTE_MODE_DB){ return db_child_init(); } return 0; } void data_destroy(void){ if(mode == SP_ROUTE_MODE_DB){ db_destroy(); } return; } opensips-2.2.2/modules/carrierroute/load_data.h000066400000000000000000000030071300170765700216220ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file load_data.h * @brief API to bind a data loading function. */ #ifndef SP_ROUTE_LOAD_DATA_H #define SP_ROUTE_LOAD_DATA_H #include "route_tree.h" typedef int (*route_data_load_func_t)(struct rewrite_data * rd); /** * Binds the loader function pointer api to the matching loader * functionm depending on source * * @param source the configuration data source, at the moment * it can be db or file * @param api pointer to the api where the loader function is * bound to * * @return 0 means everything is ok, -1 means an error */ int bind_data_loader(const char * source, route_data_load_func_t * api); int data_main_finalize(void); int data_child_init(void); void data_destroy(void); #endif opensips-2.2.2/modules/carrierroute/route.h000066400000000000000000000144021300170765700210510ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file * @brief Contains the functions to manage routing rule data. * */ #ifndef SP_ROUTE_ROUTE_H #define SP_ROUTE_ROUTE_H #include #include "../../str.h" #include "../../locking.h" #include "../../flags.h" struct route_rule_p_list; /** * Second stage of processing: Try to map the end of the user part of the URI * to a given suffix. Then rewrite with given parameters. */ struct route_rule { int dice_to; /*!< prob * DICE_MAX */ double prob; /*!< The probability for that rule, only useful when using crc32 hashing */ double orig_prob; /*!< The original probability for that rule, only useful when using crc32 hashing */ str host; /*!< The new target host for the request */ int strip; /*!< the number of digits to be stripped off from uri befor prepending prefix */ str local_prefix; /*!< the pefix to be attached to the new destination */ str local_suffix; /*!< the suffix to be appended to the localpart of the new destination */ str comment; /*!< A comment for the route rule */ str prefix; /*!< The prefix for which the route ist valid */ int status; /*!< The status of the route rule, only useful when using prime number hashing */ struct route_rule_p_list * backed_up; /*!< indicates if the rule is already backup route for another */ struct route_rule_p_list * backup; /*!< if not NULL, it points to a route rule which shall be used instead (only used if status is 0) */ int hash_index; /*!< The hash index of the route rule, only useful when using prime number hashing */ struct route_rule * next; /*!< A pointer to the next route rule */ }; /** * Second stage of processing: Try to map the end of the user part of the URI * to a given suffix. Then rewrite with given parameters. */ struct failure_route_rule { str host; /*!< The new target host for the request */ str comment; /*!< A comment for the route rule */ str prefix; /*!< The prefix for which the route ist valid */ str reply_code; /*!< The reply code for which the route ist valid */ int next_domain; /*!< The domain id where to continue routing */ flag_t flags; /*!< The flags for which the route ist valid */ flag_t mask; /*!< The mask for the flags field */ struct failure_route_rule * next; /*!< A pointer to the next route rule */ }; /** * list of routing rules with hash index */ struct route_rule_p_list { struct route_rule * rr; int hash_index; struct route_rule_p_list * next; }; /** * Use route rules only if message flags match stored mask/flags. */ struct route_flags { flag_t flags; /*!< The flags for which the route ist valid */ flag_t mask; /*!< The mask for the flags field */ struct route_rule * rule_list; /*!< Each node MAY contain a rule list */ struct route_rule ** rules; /*!< The array points to the rules in order of hash indices */ int rule_num; /*!< The number of rules */ int dice_max; /*!< The DICE_MAX value for the rule set, calculated by rule_fixup */ int max_targets; /*!< upper edge of hashing via prime number algorithm, must be eqal to rule_num */ struct route_flags * next; /*!< A pointer to the next route flags struct */ }; /** * First stage of processing: The actual route tree. * Take one digit after another off the user part of the uri until the pointer * for the digit is NULL. * Note: We can only handle digits right now, ie., no letters or symbols. * Seems okay since this is for PSTN routing. */ struct route_tree_item { struct route_tree_item * nodes[10]; /*!< The Array points to child nodes if present */ struct route_flags *flag_list; }; /** * First stage of processing: The actual route tree. Take one digit after * another of the user part of the uri until the pointer for the digit is NULL. * Note: We can only handle digits right now, ie., no letters or symbols. * Seems okay since this is for PSTN routing. */ struct failure_route_tree_item { struct failure_route_tree_item * nodes[10]; /*!< The Array points to child nodes if present */ struct failure_route_rule * rule_list; /*!< Each node MAY contain a failure rule list */ }; /** * The head of each route tree. */ struct route_tree { int id; /*!< the numerical id of the routing tree */ str name; /*!< the name of the routing tree */ struct route_tree_item * tree; /*!< the root node of the routing tree */ struct failure_route_tree_item * failure_tree; /*!< the root node of the failure routing tree */ }; /** * The struct for a carrier routing tree. */ struct carrier_tree { struct route_tree ** trees; /*!< array of route trees */ size_t tree_num; /*!< number of routing trees/domains */ str name; /*!< name of the carrier */ int id; /*!< id of the carrier */ int index; /*!< index of the tree */ }; /** * contains all routing trees. */ struct rewrite_data { struct carrier_tree ** carriers; /*!< array of carrier trees */ size_t tree_num; /*!< number of carrier trees */ int default_carrier_index; int proc_cnt; /*!< a ref counter for the shm data */ gen_lock_t lock; /*!< lock for ref counter updates */ }; /** * used to map routing domain names to numbers for * faster access. */ struct route_map { str name; /*!< name of the routing domain */ int no; /*!< number of domain */ struct route_map * next; /*!< pointer to the next element */ }; /** * used to map carrier tree names to numbers for * faster access. */ struct tree_map { str name; /*!< name of the carrier tree */ int id; /*!< id of the carrier tree */ int no; /*!< number of carrier array index for rewrite_data.trees */ struct tree_map * next; /*!< pointer to the next element */ }; #endif opensips-2.2.2/modules/carrierroute/route_config.c000066400000000000000000000270311300170765700223730ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_config.c * @brief Functions for load and save routing data from a config file. */ #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../ut.h" #include "route_config.h" #include "route.h" #include "carrierroute.h" #include "carrier_tree.h" #include #include #include static int save_route_data_recursor(struct route_tree_item * rt, FILE * outfile); static cfg_t * parse_config(); static int backup_config(); /** * reports errors during config file parsing using LOG macro * * @param cfg points to the current config data structure * @param fmt a format string * @param ap format arguments */ void conf_error(cfg_t *cfg, const char * fmt, va_list ap) { // FIXME this don't seems to work reliable, produces strange error messages LM_GEN1(L_ERR, (char *) fmt, ap); } /** * Loads the routing data from the config file given in global * variable config_data and stores it in routing tree rd. * * @param rd Pointer to the route data tree where the routing data * shall be loaded into * * @return 0 means ok, -1 means an error occurred * */ int load_config(struct rewrite_data * rd) { cfg_t * cfg = NULL; int n, m, o, i, j, k,l; cfg_t * d, * p, * t; str domain; str prefix; double prob; str rewrite_prefix; str rewrite_suffix; str rewrite_host; str comment; int backed_up_size = 0; int * backed_up = NULL; int backup = 0; int status, hash_index, max_targets, strip; if ((cfg = parse_config()) == NULL) { return -1; } if ((rd->carriers = shm_malloc(sizeof(struct carrier_tree *))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(rd->carriers, 0, sizeof(struct carrier_tree *)); rd->tree_num = 1; n = cfg_size(cfg, "domain"); if (add_carrier_tree(&default_tree, 1, rd, n) == NULL) { LM_ERR("couldn't add carrier tree\n"); return -1; } memset(rd->carriers[0]->trees, 0, sizeof(struct route_tree *) * n); for (i = 0; i < n; i++) { d = cfg_getnsec(cfg, "domain", i); domain.s = (char *)cfg_title(d); if (domain.s==NULL) domain.s=""; domain.len = strlen(domain.s); m = cfg_size(d, "prefix"); LM_INFO("loading domain %.*s\n", domain.len, domain.s); for (j = 0; j < m; j++) { p = cfg_getnsec(d, "prefix", j); prefix.s = (char *)cfg_title(p); if (prefix.s==NULL) prefix.s=""; prefix.len = strlen(prefix.s); if (str_strcasecmp(&prefix, &SP_EMPTY_PREFIX) == 0) { prefix.s = ""; prefix.len = 0; } LM_INFO("loading prefix %.*s\n", prefix.len, prefix.s); max_targets = cfg_getint(p, "max_targets"); o = cfg_size(p, "target"); for (k = 0; k < o; k++) { t = cfg_getnsec(p, "target", k); rewrite_host.s = (char *)cfg_title(t); if (rewrite_host.s==NULL) rewrite_host.s=""; rewrite_host.len = strlen(rewrite_host.s); if (str_strcasecmp(&rewrite_host, &SP_EMPTY_PREFIX) == 0) { rewrite_host.s = ""; rewrite_host.len = 0; } LM_INFO("loading target %.*s\n", rewrite_host.len, rewrite_host.s); prob = cfg_getfloat(t, "prob"); strip = cfg_getint(t, "strip"); rewrite_prefix.s = (char *)cfg_getstr(t, "rewrite_prefix"); if (rewrite_prefix.s==NULL) rewrite_prefix.s=""; rewrite_prefix.len = strlen(rewrite_prefix.s); rewrite_suffix.s = (char *)cfg_getstr(t, "rewrite_suffix"); if (rewrite_suffix.s==NULL) rewrite_suffix.s=""; rewrite_suffix.len = strlen(rewrite_suffix.s); hash_index = cfg_getint(t, "hash_index"); comment.s = (char *)cfg_getstr(t, "comment"); if (comment.s==NULL) comment.s=""; comment.len = strlen(comment.s); status = cfg_getint(t, "status"); if ((backed_up_size = cfg_size(t, "backed_up")) > 0) { if ((backed_up = pkg_malloc(sizeof(int) * (backed_up_size + 1))) == NULL) { LM_ERR("out of private memory\n"); return -1; } for (l = 0; l < backed_up_size; l++) { backed_up[l] = cfg_getnint(t, "backed_up", l); } backed_up[backed_up_size] = -1; } backup = cfg_getint(t, "backup"); LM_INFO("adding route for prefix %.*s, to host %.*s, prob %f, backed up: %i, backup: %i\n", prefix.len, prefix.s, rewrite_host.len, rewrite_host.s, prob, backed_up_size, backup); if (add_route(rd, 1, &domain, &prefix, 0, 0, max_targets, prob, &rewrite_host, strip, &rewrite_prefix, &rewrite_suffix, status, hash_index, backup, backed_up, &comment) < 0) { LM_INFO("Error while adding route\n"); if (backed_up) { pkg_free(backed_up); } return -1; } if (backed_up) { pkg_free(backed_up); } backed_up = NULL; } } } cfg_free(cfg); return 0; } /** * Parses the config file * * @return a pointer to the configuration data structure, NULL on failure */ static cfg_t * parse_config(void) { cfg_t * cfg = NULL; cfg_opt_t target_opts[] = { CFG_STR("comment", 0, CFGF_NONE), CFG_INT("strip", 0, CFGF_NONE), CFG_STR("rewrite_prefix", 0, CFGF_NONE), CFG_FLOAT("prob", 0, CFGF_NONE), CFG_INT("hash_index", 0, CFGF_NONE), CFG_STR("rewrite_suffix", 0, CFGF_NONE), CFG_INT("status", 1, CFGF_NONE), CFG_INT_LIST("backed_up", NULL, CFGF_NONE), CFG_INT("backup", -1, CFGF_NONE), CFG_END() }; cfg_opt_t prefix_opts[] = { CFG_SEC("target", target_opts, CFGF_MULTI | CFGF_TITLE), CFG_INT("max_targets", -1, CFGF_NONE), CFG_END() }; cfg_opt_t domain_opts[] = { CFG_SEC("prefix", prefix_opts, CFGF_MULTI | CFGF_TITLE), CFG_END() }; cfg_opt_t opts[] = { CFG_SEC("domain", domain_opts, CFGF_MULTI | CFGF_TITLE), CFG_END() }; cfg = cfg_init(opts, CFGF_NONE); cfg_set_error_function(cfg, conf_error); switch (cfg_parse(cfg, config_file)) { case CFG_FILE_ERROR: LM_ERR("file not found: %s\n", config_file); return NULL; case CFG_PARSE_ERROR: LM_ERR("error while parsing %s in line %i, section %s\n", cfg->filename, cfg->line, cfg->name); return NULL; case CFG_SUCCESS: break; } return cfg; } /** * Stores the routing data rd in config_file * * @param rd Pointer to the routing tree which shall be saved to file * * @return 0 means ok, -1 means an error occurred */ int save_config(struct rewrite_data * rd) { FILE * outfile; int i,j; if(backup_config() < 0){ return -1; } if ((outfile = fopen(config_file, "w")) == NULL) { LM_ERR("Could not open config file %s\n", config_file); return -1; } i = 0; if (rd->tree_num>=1) { for (j=0; j< rd->carriers[i]->tree_num; j++) { fprintf(outfile, "domain %.*s {\n", rd->carriers[i]->trees[j]->name.len, rd->carriers[i]->trees[j]->name.s); if (save_route_data_recursor(rd->carriers[i]->trees[j]->tree, outfile) < 0) { goto errout; } fprintf(outfile, "}\n\n"); } } fclose(outfile); return 0; errout: fclose(outfile); LM_ERR("Cannot save config file %s\n", config_file); return -1; } /** * Does the work for save_config, traverses the routing data tree * and writes each rule to file. * * @param rt the current route tree node * @param outfile the filehandle to which the config data is written * * @return 0 on success, -1 on failure */ static int save_route_data_recursor(struct route_tree_item * rt, FILE * outfile) { int i; struct route_rule * rr; struct route_rule_p_list * rl; str *tmp_str; str null_str = str_init("NULL"); /* no support for flag lists in route config */ if (rt->flag_list && rt->flag_list->rule_list) { rr = rt->flag_list->rule_list; tmp_str = (rr->prefix.len ? &rr->prefix : &null_str); fprintf(outfile, "\tprefix %.*s {\n", tmp_str->len, tmp_str->s); fprintf(outfile, "\t\tmax_targets = %i\n\n", rt->flag_list->max_targets); while (rr) { tmp_str = (rr->host.len ? &rr->host : &null_str); fprintf(outfile, "\t\ttarget %.*s {\n", tmp_str->len, tmp_str->s); fprintf(outfile, "\t\t\tprob = %f\n", rr->orig_prob); fprintf(outfile, "\t\t\thash_index = %i\n", rr->hash_index); fprintf(outfile, "\t\t\tstatus = %i\n", rr->status); if (rr->strip > 0) { fprintf(outfile, "\t\t\tstrip = \"%i\"\n", rr->strip); } if (rr->local_prefix.len) { fprintf(outfile, "\t\t\trewrite_prefix = \"%.*s\"\n", rr->local_prefix.len, rr->local_prefix.s); } if (rr->local_suffix.len) { fprintf(outfile, "\t\t\trewrite_suffix: \"%.*s\"\n", rr->local_suffix.len, rr->local_suffix.s); } if (rr->backup) { fprintf(outfile, "\t\t\tbackup = %i\n", rr->backup->hash_index); } if (rr->backed_up) { rl = rr->backed_up; fprintf(outfile, "\t\t\tbacked_up = {"); i=0; while (rl) { if (i>0) { fprintf(outfile, ", "); } fprintf(outfile, "%i", rl->hash_index); rl = rl->next; i++; } fprintf(outfile, "}\n"); } if (rr->comment.len) { fprintf(outfile, "\t\t\tcomment = \"%.*s\"\n", rr->comment.len, rr->comment.s); } fprintf(outfile, "\t\t}\n"); rr = rr->next; } fprintf(outfile, "\t}\n"); } for (i = 0; i < 10; i++) { if (rt->nodes[i]) { if (save_route_data_recursor(rt->nodes[i], outfile) < 0) { return -1; } } } return 0; } static int backup_config(void) { FILE * from, * to; char * backup_file, ch; LM_INFO("start configuration backup\n"); if((backup_file = pkg_malloc(strlen(config_file) + strlen (".bak") + 1)) == NULL){ LM_ERR("out of private memory\n"); return -1; } if(!strcpy(backup_file, config_file)){ LM_ERR("can't copy filename\n"); goto errout; } if(!strcat(backup_file, ".bak")){ LM_ERR("can't attach suffix\n"); goto errout; } /* open source file */ if ((from = fopen(config_file, "rb"))==NULL) { LM_ERR("Cannot open source file.\n"); goto errout; } /* open destination file */ if ((to = fopen(backup_file, "wb"))==NULL) { LM_ERR("Cannot open destination file.\n"); fclose(from); goto errout; } /* copy the file */ while (!feof(from)) { ch = fgetc(from); if (ferror(from)) { LM_ERR("Error reading source file.\n"); goto errout; } if (!feof(from)) fputc(ch, to); if (ferror(to)) { LM_ERR("Error writing destination file.\n"); goto errout; } } if (fclose(from)==EOF) { LM_ERR("Error closing source file.\n"); goto errout; } if (fclose(to)==EOF) { LM_ERR("Error closing destination file.\n"); goto errout; } LM_NOTICE("backup written to %s\n", backup_file); pkg_free(backup_file); return 0; errout: pkg_free(backup_file); return -1; } opensips-2.2.2/modules/carrierroute/route_config.h000066400000000000000000000030461300170765700224000ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_config.h * @brief Functions for load and save routing data from a config file. */ #ifndef SP_ROUTE_ROUTE_CONFIG_H #define SP_ROUTE_ROUTE_CONFIG_H #include "route_tree.h" /** * Loads the routing data from the config file given in global * variable config_data and stores it in routing tree rd. * * @param rd Pointer to the route data tree where the routing data * shall be loaded into * * @return 0 means ok, -1 means an error occurred * */ int load_config(struct rewrite_data * rd); /** * Stores the routing data rd in config_file * * @param rd Pointer to the routing tree which shall be saved to file * * @return 0 means ok, -1 means an error occurred * */ int save_config(struct rewrite_data * rd); #endif opensips-2.2.2/modules/carrierroute/route_db.c000066400000000000000000000261111300170765700215110ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_db.c * @brief Functions for loading routing data from a database. */ #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "carrierroute.h" #include "route_db.h" #include #define QUERY_LEN 2048 struct carrier { int id; char * name; struct carrier * next; }; static int store_carriers(struct carrier ** start); static void destroy_carriers(struct carrier * start); static char query[QUERY_LEN]; /** * Database API */ db_func_t dbf; db_con_t * dbh = NULL; /** * Initializes the db API * * @return 0 means ok, -1 means an error occurred. */ int db_init(void) { if (!db_url.s) { LM_ERR("You have to set the db_url module parameter.\n"); return -1; } if (db_bind_mod(&db_url, &dbf) < 0) { LM_ERR("Can't bind database module.\n"); return -1; } if((dbh = dbf.init(&db_url)) == NULL){ LM_ERR("Can't connect to database.\n"); return -1; } if ( (db_check_table_version(&dbf, dbh, &db_table, ROUTE_TABLE_VER) < 0) || (db_check_table_version(&dbf, dbh, &carrier_table, CARRIER_TABLE_VER) < 0) || (db_check_table_version(&dbf, dbh, &db_failure_table, FAILURE_TABLE_VER) < 0) ) { LM_ERR("Error during table version check.\n"); return -1; } return 0; } void main_db_close(void){ dbf.close(dbh); dbh = NULL; } int db_child_init(void) { if(dbh){ dbf.close(dbh); } if((dbh = dbf.init(&db_url)) == NULL){ LM_ERR("Can't connect to database.\n"); return -1; } return 0; } void db_destroy(void){ if(dbh){ dbf.close(dbh); } return; } int load_user_carrier(str * user, str * domain) { db_res_t * res; db_key_t cols[1]; db_key_t keys[2]; db_val_t vals[2]; db_op_t op[2]; int id; if (!user || (use_domain && !domain)) { LM_ERR("NULL pointer in parameter\n"); return -1; } cols[0] = subscriber_columns[SUBSCRIBER_CARRIER_COL]; keys[0] = subscriber_columns[SUBSCRIBER_USERNAME_COL]; op[0] = OP_EQ; vals[0].type = DB_STR; vals[0].nul = 0; vals[0].val.str_val = *user; keys[1] = subscriber_columns[SUBSCRIBER_DOMAIN_COL]; op[1] = OP_EQ; vals[1].type = DB_STR; vals[1].nul = 0; vals[1].val.str_val = *domain; if (dbf.use_table(dbh, &subscriber_table) < 0) { LM_ERR("can't use table\n"); return -1; } if (dbf.query(dbh, keys, op, vals, cols, use_domain ? 2 : 1, 1, NULL, &res) < 0) { LM_ERR("can't query database\n"); return -1; } if (RES_ROW_N(res) == 0) { dbf.free_result(dbh, res); return 0; } if (VAL_NULL(ROW_VALUES(RES_ROWS(res)))) { dbf.free_result(dbh, res); return 0; } id = VAL_INT(ROW_VALUES(RES_ROWS(res))); dbf.free_result(dbh, res); return id; } /** * Loads the routing data from the database given in global * variable db_url and stores it in routing tree rd. * * @param rd Pointer to the route data tree where the routing data * shall be loaded into * * @return 0 means ok, -1 means an error occurred * */ int load_route_data(struct rewrite_data * rd) { db_res_t * res = NULL; db_row_t * row = NULL; int i, n = 0, ret; int carrier_count = 0; struct carrier * carriers = NULL, * tmp = NULL; static str query_str; str tmp_carrier; str tmp_domain; str tmp_scan_prefix; str tmp_rewrite_host; str tmp_rewrite_prefix; str tmp_rewrite_suffix; str tmp_host_name; str tmp_reply_code; str tmp_next_domain; str tmp_comment; int no_rows=10; UNUSED(n); if( (strlen("SELECT DISTINCT FROM WHERE = ") + db_table.len + columns[COL_DOMAIN]->len + columns[COL_CARRIER]->len + 20) > QUERY_LEN) { LM_ERR("query too long\n"); return -1; } if((carrier_count = store_carriers(&carriers)) <= 0){ LM_ERR("error while retrieving carriers\n"); goto errout; } if ((rd->carriers = shm_malloc(sizeof(struct carrier_tree *) * carrier_count)) == NULL) { LM_ERR("out of shared memory\n"); goto errout; } memset(rd->carriers, 0, sizeof(struct carrier_tree *) * carrier_count); rd->tree_num = carrier_count; tmp = carriers; for (i=0; ilen, columns[COL_DOMAIN]->s, db_table.len, db_table.s, columns[COL_CARRIER]->len, columns[COL_CARRIER]->s, tmp->id); if (ret < 0) { LM_ERR("error in snprintf"); goto errout; } query_str.s = query; query_str.len = ret; if (dbf.raw_query(dbh, &query_str, &res) < 0) { LM_ERR("Failed to query database.\n"); goto errout; } LM_INFO("name %s, id %i, trees: %i\n", tmp->name, tmp->id, RES_ROW_N(res)); tmp_carrier.s=tmp->name; tmp_carrier.len=strlen(tmp_carrier.s); if (add_carrier_tree(&tmp_carrier, tmp->id, rd, RES_ROW_N(res)) == NULL) { LM_ERR("can't add carrier %s\n", tmp->name); goto errout; } dbf.free_result(dbh, res); res = NULL; tmp = tmp->next; } if (dbf.use_table(dbh, &db_table) < 0) { LM_ERR("Cannot set database table '%.*s'.\n", db_table.len, db_table.s); return -1; } if (DB_CAPABILITY(dbf, DB_CAP_FETCH)) { if (dbf.query(dbh, NULL, NULL, NULL, (db_key_t *) columns, 0, COLUMN_NUM, NULL, NULL) < 0) { LM_ERR("Failed to query database to prepare fetchrow.\n"); return -1; } no_rows = estimate_available_rows( 4+64+64+64+4+4+4+64+4+64+64+128, COLUMN_NUM); if (no_rows==0) no_rows = 10; if(dbf.fetch_result(dbh, &res, no_rows) < 0) { LM_ERR("Fetching rows failed\n"); return -1; } } else { if (dbf.query(dbh, NULL, NULL, NULL, (db_key_t *)columns, 0, COLUMN_NUM, NULL, &res) < 0) { LM_ERR("Failed to query database.\n"); return -1; } } do { LM_DBG("loading, cycle %d", n++); for (i = 0; i < RES_ROW_N(res); ++i) { row = &RES_ROWS(res)[i]; tmp_domain.s=(char *)row->values[COL_DOMAIN].val.string_val; tmp_scan_prefix.s=(char *)row->values[COL_SCAN_PREFIX].val.string_val; tmp_rewrite_host.s=(char *)row->values[COL_REWRITE_HOST].val.string_val; tmp_rewrite_prefix.s=(char *)row->values[COL_REWRITE_PREFIX].val.string_val; tmp_rewrite_suffix.s=(char *)row->values[COL_REWRITE_SUFFIX].val.string_val; tmp_comment.s=(char *)row->values[COL_COMMENT].val.string_val; if (tmp_domain.s==NULL) tmp_domain.s=""; if (tmp_scan_prefix.s==NULL) tmp_scan_prefix.s=""; if (tmp_rewrite_host.s==NULL) tmp_rewrite_host.s=""; if (tmp_rewrite_prefix.s==NULL) tmp_rewrite_prefix.s=""; if (tmp_rewrite_suffix.s==NULL) tmp_rewrite_suffix.s=""; if (tmp_comment.s==NULL) tmp_comment.s=""; tmp_domain.len=strlen(tmp_domain.s); tmp_scan_prefix.len=strlen(tmp_scan_prefix.s); tmp_rewrite_host.len=strlen(tmp_rewrite_host.s); tmp_rewrite_prefix.len=strlen(tmp_rewrite_prefix.s); tmp_rewrite_suffix.len=strlen(tmp_rewrite_suffix.s); tmp_comment.len=strlen(tmp_comment.s); if (add_route(rd, row->values[COL_CARRIER].val.int_val, &tmp_domain, &tmp_scan_prefix, row->values[COL_FLAGS].val.int_val, row->values[COL_MASK].val.int_val, 0, row->values[COL_PROB].val.double_val, &tmp_rewrite_host, row->values[COL_STRIP].val.int_val, &tmp_rewrite_prefix, &tmp_rewrite_suffix, 1, 0, -1, NULL, &tmp_comment) == -1) { goto errout; } } if (DB_CAPABILITY(dbf, DB_CAP_FETCH)) { if(dbf.fetch_result(dbh, &res, no_rows) < 0) { LM_ERR("fetching rows failed\n"); dbf.free_result(dbh, res); return -1; } } else { break; } } while(RES_ROW_N(res) > 0); dbf.free_result(dbh, res); res = NULL; if (dbf.use_table(dbh, &db_failure_table) < 0) { LM_ERR("cannot set database table '%.*s'.\n", db_failure_table.len, db_failure_table.s); return -1; } if (dbf.query(dbh, NULL, NULL, NULL, (db_key_t *)failure_columns, 0, FAILURE_COLUMN_NUM, NULL, &res) < 0) { LM_ERR("failed to query database.\n"); return -1; } for (i = 0; i < RES_ROW_N(res); ++i) { row = &RES_ROWS(res)[i]; tmp_domain.s=(char *)row->values[FCOL_DOMAIN].val.string_val; tmp_scan_prefix.s=(char *)row->values[FCOL_SCAN_PREFIX].val.string_val; tmp_host_name.s=(char *)row->values[FCOL_HOST_NAME].val.string_val; tmp_reply_code.s=(char *)row->values[FCOL_REPLY_CODE].val.string_val; tmp_next_domain.s=(char *)row->values[FCOL_NEXT_DOMAIN].val.string_val; tmp_comment.s=(char *)row->values[FCOL_COMMENT].val.string_val; if (tmp_domain.s==NULL) tmp_domain.s=""; if (tmp_scan_prefix.s==NULL) tmp_scan_prefix.s=""; if (tmp_host_name.s==NULL) tmp_host_name.s=""; if (tmp_reply_code.s==NULL) tmp_reply_code.s=""; if (tmp_next_domain.s==NULL) tmp_next_domain.s=""; if (tmp_comment.s==NULL) tmp_comment.s=""; tmp_domain.len=strlen(tmp_domain.s); tmp_scan_prefix.len=strlen(tmp_scan_prefix.s); tmp_host_name.len=strlen(tmp_host_name.s); tmp_reply_code.len=strlen(tmp_reply_code.s); tmp_next_domain.len=strlen(tmp_next_domain.s); tmp_comment.len=strlen(tmp_comment.s); if (add_failure_route(rd, row->values[FCOL_CARRIER].val.int_val, &tmp_domain, &tmp_scan_prefix, &tmp_host_name, &tmp_reply_code, row->values[FCOL_FLAGS].val.int_val, row->values[FCOL_MASK].val.int_val, &tmp_next_domain, &tmp_comment) == -1) { goto errout; } } destroy_carriers(carriers); dbf.free_result(dbh, res); return 0; errout: destroy_carriers(carriers); if (res) { dbf.free_result(dbh, res); } return -1; } static int store_carriers(struct carrier ** start){ db_res_t * res = NULL; int i; int count; struct carrier * nc; if(!start){ LM_ERR("invalid parameter\n"); return -1; } if (dbf.use_table(dbh, &carrier_table) < 0) { LM_ERR("couldn't use table\n"); return -1; } if (dbf.query(dbh, 0, 0, 0, (db_key_t *)carrier_columns, 0, CARRIER_COLUMN_NUM, 0, &res) < 0) { LM_ERR("couldn't query table\n"); return -1; } count = RES_ROW_N(res); for(i=0; iid = res->rows[i].values[0].val.int_val; if((nc->name = pkg_malloc(strlen(res->rows[i].values[1].val.string_val) + 1)) == NULL){ LM_ERR("out of private memory\n"); pkg_free(nc); goto errout; } strcpy(nc->name, res->rows[i].values[1].val.string_val); nc->next = *start; *start = nc; } dbf.free_result(dbh, res); return count; errout: if(res){ dbf.free_result(dbh, res); } return -1; } static void destroy_carriers(struct carrier * start){ struct carrier * tmp, * tmp2; tmp = start; while(tmp){ tmp2 = tmp; tmp = tmp->next; pkg_free(tmp2->name); pkg_free(tmp2); } return; } opensips-2.2.2/modules/carrierroute/route_db.h000066400000000000000000000030551300170765700215200ustar00rootroot00000000000000/* * Copyright (C) 2007 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_db.h * @brief Functions for loading routing data from a database. */ #ifndef SP_ROUTE_ROUTE_DB_H #define SP_ROUTE_ROUTE_DB_H #include "../../db/db.h" #include "carrier_tree.h" /** * Initializes the db API * * @return 0 means ok, -1 means an error occurred. */ int db_init(void); void main_db_close(void); int db_child_init(void); void db_destroy(void); /** * Loads the routing data from the database given in global * variable db_url and stores it in routing tree rd. * * @param rd Pointer to the route data tree where the routing data * shall be loaded into * * @return 0 means ok, -1 means an error occurred * */ int load_route_data (struct rewrite_data * rd); int load_user_carrier(str * user, str * domain); #endif opensips-2.2.2/modules/carrierroute/route_fifo.c000066400000000000000000000671751300170765700220660ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_fifo.c * @brief Functions for modifying routing data via fifo commands. */ #include "route_fifo.h" #include "carrierroute.h" #include "route_rule.h" #include "route_config.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include #include #include "../../str.h" #include "../../ut.h" /** * @var defines the option set for the different fifo commands * Every line is for a command, * The first field defines the required options, the second field defines the * optional options and the third field defines the invalid options. */ static unsigned int opt_settings[5][3] = {{O_PREFIX|O_DOMAIN|O_HOST|O_PROB, O_R_PREFIX|O_R_SUFFIX|O_H_INDEX, O_NEW_TARGET}, {O_HOST|O_DOMAIN|O_PREFIX, O_PROB, O_R_PREFIX|O_R_SUFFIX|O_NEW_TARGET|O_H_INDEX}, {O_HOST|O_NEW_TARGET, O_PREFIX|O_DOMAIN|O_PROB, O_R_PREFIX|O_R_SUFFIX|O_H_INDEX}, {O_HOST|O_DOMAIN|O_PREFIX, O_PROB|O_NEW_TARGET, O_R_PREFIX|O_R_SUFFIX|O_H_INDEX}, {O_HOST|O_DOMAIN|O_PREFIX, O_PROB, O_R_PREFIX|O_R_SUFFIX|O_NEW_TARGET|O_H_INDEX}}; static int dump_tree_recursor (struct mi_node* msg, struct route_tree_item *tree, char *prefix); static struct mi_root* print_replace_help(void); static int get_fifo_opts(str * buf, fifo_opt_t * opts, unsigned int opt_set[]); static int update_route_data(fifo_opt_t * opts); static int update_route_data_recursor(struct route_tree_item * rt, str * act_domain, fifo_opt_t * opts); static struct mi_root* print_fifo_err(void); static int str_toklen(str * str, const char * delims) { int len; if ((str==NULL) || (str->s==NULL)) { /* No more tokens */ return -1; } len=0; while (lenlen) { if (strchr(delims,str->s[len])!=NULL) { return len; } len++; } return len; } /** * reloads the routing data * * @param cmd_tree the MI command tree * @param param the parameter * * @return code 200 on success, code 500 on failure */ struct mi_root* reload_fifo (struct mi_root* cmd_tree, void *param) { struct mi_root * tmp = NULL; if (prepare_route_tree () == -1) { tmp = init_mi_tree(500, "failed to re-built tree, see log", 33); } else { tmp = init_mi_tree(200, MI_OK_S, MI_OK_LEN); } return tmp; } int fifo_err; static int updated; /** * prints the routing data * * @param cmd_tree the MI command tree * @param param the parameter * * @return code 200 on success, code 400 or 500 on failure */ struct mi_root* dump_fifo (struct mi_root* cmd_tree, void *param) { struct rewrite_data * rd; str *tmp_str; str empty_str = str_init(""); if((rd = get_data ()) == NULL) { LM_ERR("error during retrieve data\n"); return init_mi_tree(500, "error during command processing", 31); } struct mi_root* rpl_tree; struct mi_node* node = NULL; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if(rpl_tree == NULL) return 0; rpl_tree->node.flags |= MI_IS_ARRAY; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing routing information:"); if(node == NULL) goto error; LM_DBG("start processing of data\n"); int i, j; for (i = 0; i < rd->tree_num; i++) { if (rd->carriers[i]) { tmp_str = (rd->carriers[i] ? &rd->carriers[i]->name : &empty_str); node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing tree for carrier %.*s (%i)\n", tmp_str->len, tmp_str->s, rd->carriers[i] ? rd->carriers[i]->id : 0); if(node == NULL) goto error; for (j=0; jcarriers[i]->tree_num; j++) { if (rd->carriers[i]->trees[j] && rd->carriers[i]->trees[j]->tree) { tmp_str = (rd->carriers[i]->trees[j] ? &rd->carriers[i]->trees[j]->name : &empty_str); node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing tree for domain %.*s\n", tmp_str->len, tmp_str->s); if(node == NULL) goto error; dump_tree_recursor (&rpl_tree->node, rd->carriers[i]->trees[j]->tree, ""); } } } } release_data (rd); return rpl_tree; return 0; error: release_data (rd); free_mi_tree(rpl_tree); return 0; } /** * replaces the host specified by parameters in the * fifo command, can be used only in file mode * expect one mi node that contains the command * * @param cmd_tree the MI command tree * @param param the parameter * * @return code 200 on success, code 400 or 500 on failure */ struct mi_root* replace_host (struct mi_root* cmd_tree, void *param) { struct mi_node *node = NULL; int ret; fifo_opt_t options; if(mode != SP_ROUTE_MODE_FILE) { return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70); } node = cmd_tree->node.kids; if (node==NULL || node->next!=NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* look for command */ if (node->value.s==NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_REPLACE])) < 0) { return print_fifo_err(); } options.status = 1; options.cmd = OPT_REPLACE; if(update_route_data(&options) < 0) { return init_mi_tree(500, "failed to update route data, see log", 37); } return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } /** * deactivates the host given in the command line options, * can be used only in file mode * expect one mi node that contains the command * * @param cmd_tree the MI command tree * @param param the parameter * * @return code 200 on success, code 400 or 500 on failure */ struct mi_root* deactivate_host (struct mi_root* cmd_tree, void *param) { struct mi_node *node = NULL; int ret; fifo_opt_t options; if(mode != SP_ROUTE_MODE_FILE) { return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70); } node = cmd_tree->node.kids; if (node==NULL || node->next!=NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* look for command */ if (node->value.s==NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_DEACTIVATE])) < 0) { return print_fifo_err(); } options.status = 0; options.cmd = OPT_DEACTIVATE; if(update_route_data(&options) < 0) { return init_mi_tree(500, "failed to update route data, see log", 37); } return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } /** * activates the host given in the command line options, * can be used only in file mode * expect one mi node that contains the command * * @param cmd_tree the MI command tree * @param param the parameter * * @return code 200 on success, code 400 or 500 on failure */ struct mi_root* activate_host (struct mi_root* cmd_tree, void *param) { struct mi_node *node = NULL; int ret; fifo_opt_t options; if(mode != SP_ROUTE_MODE_FILE) { return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70); } node = cmd_tree->node.kids; if (node==NULL || node->next!=NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* look for command */ if (node->value.s==NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_ACTIVATE])) < 0) { return print_fifo_err(); } options.status = 1; options.cmd = OPT_ACTIVATE; if(update_route_data(&options) < 0) { return init_mi_tree(500, "failed to update route data, see log", 37); } return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } /** * adds the host specified by the command line args, * can be used only in file mode * expect one mi node that contains the command * * @param cmd_tree the MI command tree * @param param the parameter * * @return code 200 on success, code 400 or 500 on failure */ struct mi_root* add_host (struct mi_root* cmd_tree, void *param) { struct mi_node *node = NULL; int ret; fifo_opt_t options; if(mode != SP_ROUTE_MODE_FILE) { return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70); } node = cmd_tree->node.kids; if (node==NULL || node->next!=NULL || node->value.s==NULL) { return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); } if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_ADD])) < 0) { return print_fifo_err(); } options.status = 1; options.cmd = OPT_ADD; if(update_route_data(&options) < 0) { return init_mi_tree(500, "failed to update route data, see log", 37); } return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } /** * deletes the host specified by the command line args, * can be used only in file mode * expect one mi node that contains the command * * @param cmd_tree the MI command tree * @param param the parameter * * @return code 200 on success, code 400 or 500 on failure */ struct mi_root* delete_host (struct mi_root* cmd_tree, void * param) { struct mi_node *node = NULL; int ret; fifo_opt_t options; if(mode != SP_ROUTE_MODE_FILE) { return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70); } node = cmd_tree->node.kids; if (node==NULL || node->next!=NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* look for command */ if (node->value.s==NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_REMOVE])) < 0) { return print_fifo_err(); } options.cmd = OPT_REMOVE; if(update_route_data(&options) < 0) { return init_mi_tree(500, "failed to update route data, see log", 37); } return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } /** * does the work for dump_fifo, traverses the routing tree * and prints route rules if present. * * @param msg MI node that is used to append the informations * @param tree pointer to the routing tree node * @param prefix carries the current scan prefix * * @return mi node containing the route rules */ static int dump_tree_recursor (struct mi_node* msg, struct route_tree_item *tree, char *prefix) { char s[256]; char *p; int i; struct route_flags *rf; struct route_rule *rr; struct route_rule_p_list * rl; double prob; strcpy (s, prefix); p = s + strlen (s); p[1] = '\0'; for (i = 0; i < 10; ++i) { if (tree->nodes[i] != NULL) { *p = i + '0'; dump_tree_recursor (msg->next, tree->nodes[i], s); } } *p = '\0'; for (rf = tree->flag_list; rf != NULL; rf = rf->next) { for (rr = rf->rule_list; rr != NULL; rr = rr->next) { if(rf->dice_max){ prob = (double)(rr->prob * DICE_MAX)/(double)rf->dice_max; } else { prob = rr->prob; } addf_mi_node_child(msg->next, 0, 0, 0, "%10s: %0.3f %%, '%.*s': %s, '%i', '%.*s', '%.*s', '%.*s'\n", strlen(prefix) > 0 ? prefix : "NULL", prob * 100, rr->host.len, rr->host.s, (rr->status ? "ON" : "OFF"), rr->strip, rr->local_prefix.len, rr->local_prefix.s, rr->local_suffix.len, rr->local_suffix.s, rr->comment.len, rr->comment.s); if(!rr->status && rr->backup && rr->backup->rr){ addf_mi_node_child(msg->next, 0, 0, 0, " Rule is backed up by: %.*s\n", rr->backup->rr->host.len, rr->backup->rr->host.s); } if(rr->backed_up){ rl = rr->backed_up; i=0; while(rl){ if(rl->rr){ addf_mi_node_child(msg->next, 0, 0, 0, " Rule is backup for: %.*s", rl->rr->host.len, rl->rr->host.s); } rl = rl->next; i++; } } } } return 0; } /** * parses the command line argument for options * * @param buf the command line argument * @param opts fifo options * @param opt_set set of the options * * @return 0 on success, -1 on failure * * @see dump_fifo() */ static int get_fifo_opts(str * buf, fifo_opt_t * opts, unsigned int opt_set[]) { int opt_argc = 0; str opt_argv[20]; int i, op = -1; unsigned int used_opts = 0; int toklen; memset(opt_argv, 0, sizeof(opt_argv)); memset(opts, 0, sizeof(fifo_opt_t)); opts->prob = -1; while((toklen = str_toklen(buf, " \t\r\n")) >=0 && opt_argc < 20) { buf->s[toklen] = '\0'; /* insert zero termination, since strtod might be used later on it */ opt_argv[opt_argc].len = toklen; opt_argv[opt_argc].s = buf->s; buf->s += toklen + 1; buf->len -= toklen + 1; LM_DBG("found arg[%i]: %.*s\n", opt_argc, opt_argv[opt_argc].len, opt_argv[opt_argc].s); opt_argc++; } for (i=0; i= 1) { switch(*opt_argv[i].s) { case '-': switch(opt_argv[i].s[1]) { case OPT_DOMAIN_CHR: op = OPT_DOMAIN; used_opts |= O_DOMAIN; break; case OPT_PREFIX_CHR: op = OPT_PREFIX; used_opts |= O_PREFIX; break; case OPT_HOST_CHR: op = OPT_HOST; used_opts |= O_HOST; break; case OPT_NEW_TARGET_CHR: op = OPT_NEW_TARGET; used_opts |= O_NEW_TARGET; break; case OPT_PROB_CHR: op = OPT_PROB; used_opts |= O_PROB; break; case OPT_R_PREFIX_CHR: op = OPT_R_PREFIX; used_opts |= O_R_PREFIX; break; case OPT_R_SUFFIX_CHR: op = OPT_R_SUFFIX; used_opts |= O_R_SUFFIX; break; case OPT_HASH_INDEX_CHR: op = OPT_HASH_INDEX; used_opts |= O_H_INDEX; break; case OPT_HELP_CHR: FIFO_ERR(E_HELP); return -1; default: { FIFO_ERR(E_WRONGOPT); LM_DBG("Unknown option: %.*s\n", opt_argv[i].len, opt_argv[i].s); return -1; } } break; default: switch(op) { case OPT_DOMAIN: opts->domain = opt_argv[i]; op = -1; break; case OPT_PREFIX: if (str_strcasecmp(&opt_argv[i], &SP_EMPTY_PREFIX) == 0) { opts->prefix.s = NULL; opts->prefix.len = 0; } else { opts->prefix = opt_argv[i]; } op = -1; break; case OPT_HOST: opts->host = opt_argv[i]; op = -1; break; case OPT_NEW_TARGET: opts->new_host = opt_argv[i]; op = -1; break; case OPT_PROB: opts->prob = strtod(opt_argv[i].s, NULL); /* we can use str.s since we zero terminated it earlier */ op = -1; break; case OPT_R_PREFIX: opts->rewrite_prefix = opt_argv[i]; op = -1; break; case OPT_STRIP: str2sint(&opt_argv[i], &opts->strip); op = -1; break; case OPT_R_SUFFIX: opts->rewrite_suffix = opt_argv[i]; op = -1; break; case OPT_HASH_INDEX: str2sint(&opt_argv[i], &opts->hash_index); op = -1; break; default: { LM_DBG("No option given\n"); FIFO_ERR(E_NOOPT); return -1; } } break; } } } if((used_opts & opt_set[OPT_INVALID]) != 0) { LM_DBG("invalid option\n"); FIFO_ERR(E_INVALIDOPT); return -1; } if((used_opts & opt_set[OPT_MANDATORY]) != opt_set[OPT_MANDATORY]) { LM_DBG("option missing\n"); FIFO_ERR(E_MISSOPT); return -1; } return 0; } /** * loads the config data into shared memory (but doesn't really * share it), updates the routing data and writes it to the config * file. Afterwards, the global routing data is reloaded. * * @param opts pointer to the option structure which contains * data to be modified or to be added * * @return 0 on success, -1 on failure */ static int update_route_data(fifo_opt_t * opts) { struct rewrite_data * rd; int i,j; str tmp_domain; str tmp_prefix; str tmp_host; str tmp_rewrite_prefix; str tmp_rewrite_suffix; str tmp_comment = str_init(""); if ((rd = shm_malloc(sizeof(struct rewrite_data))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(rd, 0, sizeof(struct rewrite_data)); if (load_config(rd) < 0) { LM_ERR("could not load config"); FIFO_ERR(E_LOADCONF); return -1; } if (rule_fixup(rd) < 0) { LM_ERR("could not fixup rules"); FIFO_ERR(E_RULEFIXUP); return -1; } updated = 0; if (opts->cmd == OPT_ADD) { tmp_domain=opts->domain; tmp_prefix=opts->prefix; tmp_host=opts->host; tmp_rewrite_prefix=opts->rewrite_prefix; tmp_rewrite_suffix=opts->rewrite_suffix; if (tmp_domain.s==NULL) { tmp_domain.s=""; tmp_domain.len=0; } if (tmp_prefix.s==NULL) { tmp_prefix.s=""; tmp_prefix.len=0; } if (tmp_host.s==NULL) { tmp_host.s=""; tmp_host.len=0; } if (tmp_rewrite_prefix.s==NULL) { tmp_rewrite_prefix.s=""; tmp_rewrite_prefix.len=0; } if (tmp_rewrite_suffix.s==NULL) { tmp_rewrite_suffix.s=""; tmp_rewrite_suffix.len=0; } if (add_route(rd, 1, &tmp_domain, &tmp_prefix, 0, 0, 0, opts->prob, &tmp_host, opts->strip, &tmp_rewrite_prefix, &tmp_rewrite_suffix, opts->status, opts->hash_index, -1, NULL, &tmp_comment) < 0) { goto errout; } updated = 1; if (rule_fixup(rd) < 0) { LM_ERR("could not fixup rules after route appending"); FIFO_ERR(E_RULEFIXUP); return -1; } } else { for (i=0; itree_num; i++) { if(rd->carriers[i]){ for (j=0; jcarriers[i]->tree_num; j++) { if (rd->carriers[i]->trees[j] && rd->carriers[i]->trees[j]->tree) { if (update_route_data_recursor(rd->carriers[i]->trees[j]->tree, &rd->carriers[i]->trees[j]->name, opts) < 0) { goto errout; } } } } } } if(!updated){ LM_ERR("no match for update found"); FIFO_ERR(E_NOUPDATE); goto errout; } if (save_config(rd) < 0) { LM_ERR("could not save config"); FIFO_ERR(E_SAVECONF); goto errout; } if (prepare_route_tree() == -1) { LM_ERR("could not prepare the route tree"); FIFO_ERR(E_LOADCONF); goto errout; } destroy_rewrite_data(rd); return 0; errout: destroy_rewrite_data(rd); return -1; } /** * Does the work for update_route_data by recursively * traversing the routing tree * * @param rt points to the current routing tree node * @param act_domain routing domain which is currently * searched * @param opts points to the fifo command option structure * * @see update_route_data() * * @return 0 on success, -1 on failure */ static int update_route_data_recursor(struct route_tree_item * rt, str * act_domain, fifo_opt_t * opts) { int i, hash = 0; struct route_rule * rr, * prev = NULL, * tmp, * backup; struct route_flags *rf; if (rt->flag_list && rt->flag_list->rule_list) { rf = rt->flag_list; rr = rf->rule_list; while (rr) { if ((!opts->domain.len || (strncmp(opts->domain.s, OPT_STAR, strlen(OPT_STAR)) == 0) || ((opts->domain.len == act_domain->len) && (strncmp(opts->domain.s, act_domain->s, opts->domain.len) == 0))) && ((!opts->prefix.len && !rr->prefix.len) || (strncmp(opts->prefix.s, OPT_STAR, strlen(OPT_STAR)) == 0) || (rr->prefix.len == opts->prefix.len && (strncmp(opts->prefix.s, rr->prefix.s, opts->prefix.len) == 0))) && ((!opts->host.len && !rr->host.s) || (strncmp(opts->host.s, OPT_STAR, strlen(OPT_STAR)) == 0) || ((strncmp(rr->host.s, opts->host.s, opts->host.len) == 0) && (rr->host.len == opts->host.len))) && ((opts->prob < 0) || (opts->prob == rr->prob))) { switch (opts->cmd) { case OPT_REPLACE: LM_INFO("replace host %.*s with %.*s\n", rr->host.len, rr->host.s, opts->new_host.len, opts->new_host.s); if (rr->host.s) { shm_free(rr->host.s); } if (opts->new_host.len) { if ((rr->host.s = shm_malloc(opts->new_host.len + 1)) == NULL) { LM_ERR("out of shared mem\n"); FIFO_ERR(E_NOMEM); return -1; } memmove(rr->host.s, opts->new_host.s, opts->new_host.len + 1); rr->host.len = opts->new_host.len; rr->host.s[rr->host.len] = '\0'; } else { rr->host.len = 0; } rr->status = opts->status; prev = rr; rr = rr->next; updated = 1; break; case OPT_DEACTIVATE: if (remove_backed_up(rr) < 0) { LM_ERR("could not reset backup hosts\n"); FIFO_ERR(E_RESET); return -1; } if (opts->new_host.len > 0) { LM_INFO("deactivating host %.*s\n", rr->host.len, rr->host.s); if (opts->new_host.len == 1 && opts->new_host.s[0] == 'a') { if ((backup = find_auto_backup(rf, rr)) == NULL) { LM_ERR("didn't find auto backup route\n"); FIFO_ERR(E_NOAUTOBACKUP); return -1; } } else { errno = 0; hash = strtol(opts->new_host.s, NULL, 10); if (errno == EINVAL || errno == ERANGE) { if ((backup = find_rule_by_hash(rf, hash)) == NULL) { LM_ERR("didn't find given backup route (hash %i)\n", hash); FIFO_ERR(E_NOHASHBACKUP); return -1; } } else { if ((backup = find_rule_by_host(rf, &opts->new_host)) == NULL) { LM_ERR("didn't find given backup route (host %.*s)\n", opts->new_host.len, opts->new_host.s); FIFO_ERR(E_NOHOSTBACKUP); return -1; } } } if (add_backup_route(rr, backup) < 0) { LM_ERR("couldn't set backup route\n"); FIFO_ERR(E_ADDBACKUP); return -1; } } else { if(rr->backed_up){ LM_ERR("can't deactivate route without backup route because it is backup route for others\n"); FIFO_ERR(E_DELBACKUP); return -1; } } rr->status = opts->status; prev = rr; rr = rr->next; updated = 1; break; case OPT_ACTIVATE: LM_INFO("activating host %.*s\n", rr->host.len, rr->host.s); if (remove_backed_up(rr) < 0) { LM_ERR("could not reset backup hosts\n"); FIFO_ERR(E_RESET); return -1; } rr->status = opts->status; prev = rr; rr = rr->next; updated = 1; break; case OPT_REMOVE: LM_INFO("removing host %.*s\n", rr->host.len, rr->host.s); if (rr->backed_up){ LM_ERR("cannot remove host %.*s which is backup for other hosts\n", rr->host.len, rr->host.s); FIFO_ERR(E_DELBACKUP); return -1; } if (remove_backed_up(rr) < 0) { LM_ERR("could not reset backup hosts\n"); FIFO_ERR(E_RESET); return -1; } if (prev) { prev->next = rr->next; tmp = rr; rr = prev; destroy_route_rule(tmp); prev = rr; rr = rr->next; } else { rf->rule_list = rr->next; tmp = rr; rr = rf->rule_list; destroy_route_rule(tmp); } rf->rule_num--; rf->max_targets--; updated = 1; break; default: rr = rr->next; break; } } else { prev = rr; rr = rr->next; } } } for (i=0; i<10; i++) { if (rt->nodes[i]) { if (update_route_data_recursor(rt->nodes[i], act_domain, opts) < 0) { return -1; } } } return 0; } /** * prints a short help text for fifo command usage */ static struct mi_root* print_replace_help(void) { struct mi_root* rpl_tree; struct mi_node* node; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN ); if(rpl_tree == NULL) return 0; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "carrierroute options usage:"); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c searched/new remote host\n", OPT_HOST_CHR); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c replacement/backup host", OPT_NEW_TARGET_CHR); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: searched/new domain", OPT_DOMAIN_CHR); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: searched/new prefix", OPT_PREFIX_CHR); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: searched/new weight (0..1)", OPT_PROB_CHR); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: new rewrite prefix", OPT_R_PREFIX_CHR); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: new rewrite suffix", OPT_R_SUFFIX_CHR); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: new hash index", OPT_HASH_INDEX_CHR); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: prints this help", OPT_HELP_CHR); if(node == NULL) goto error; return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } /** * interpret the fifo errors, creates a mi tree * @todo this is currently not evaluated for errors during update_route_data */ struct mi_root* print_fifo_err(void) { struct mi_root* rpl_tree; switch (fifo_err) { case E_MISC: rpl_tree = init_mi_tree( 400, "An error occurred", 17); if(rpl_tree == NULL) return 0; break; case E_NOOPT: rpl_tree = init_mi_tree( 400, "No option given", 16); if(rpl_tree == NULL) return 0; break; case E_WRONGOPT: rpl_tree = init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); if(rpl_tree == NULL) return 0; break; case E_NOMEM: rpl_tree = init_mi_tree( 500, "Out of memory", 14); if(rpl_tree == NULL) return 0; break; case E_RESET: rpl_tree = init_mi_tree( 500, "Could not reset backup routes", 30); if(rpl_tree == NULL) return 0; break; case E_NOAUTOBACKUP: rpl_tree = init_mi_tree( 400, "No auto backup route found", 27); if(rpl_tree == NULL) return 0; break; case E_NOHASHBACKUP: rpl_tree = init_mi_tree( 400, "No backup route for given hash found", 37); if(rpl_tree == NULL) return 0; break; case E_NOHOSTBACKUP: rpl_tree = init_mi_tree( 400, "No backup route for given host found", 37); if(rpl_tree == NULL) return 0; break; case E_ADDBACKUP: rpl_tree = init_mi_tree( 500, "Could not set backup route", 27); if(rpl_tree == NULL) return 0; break; case E_DELBACKUP: rpl_tree = init_mi_tree( 400, "Could not delete or deactivate route, it is backup for other routes", 68); if(rpl_tree == NULL) return 0; break; case E_LOADCONF: rpl_tree = init_mi_tree( 500, "Could not load config from file", 32); if(rpl_tree == NULL) return 0; break; case E_SAVECONF: rpl_tree = init_mi_tree( 500, "Could not save config", 22); if(rpl_tree == NULL) return 0; break; case E_INVALIDOPT: rpl_tree = init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); if(rpl_tree == NULL) return 0; break; case E_MISSOPT: rpl_tree = init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if(rpl_tree == NULL) return 0; break; case E_RULEFIXUP: rpl_tree = init_mi_tree( 500, "Could not fixup rules", 22); if(rpl_tree == NULL) return 0; break; case E_NOUPDATE: rpl_tree = init_mi_tree( 500, "No match for update found", 26); if(rpl_tree == NULL) return 0; break; case E_HELP: return print_replace_help(); break; default: rpl_tree = init_mi_tree( 500, "An error occurred", 17); if(rpl_tree == NULL) return 0; break; } return rpl_tree; } opensips-2.2.2/modules/carrierroute/route_fifo.h000066400000000000000000000072571300170765700220660ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ extern int fifo_err; #define E_MISC -1 #define E_NOOPT -2 #define E_WRONGOPT -3 #define E_NOMEM -4 #define E_RESET -5 #define E_NOAUTOBACKUP -6 #define E_NOHASHBACKUP -7 #define E_NOHOSTBACKUP -8 #define E_ADDBACKUP -9 #define E_DELBACKUP -10 #define E_LOADCONF -11 #define E_SAVECONF -12 #define E_INVALIDOPT -13 #define E_MISSOPT -14 #define E_RULEFIXUP -15 #define E_NOUPDATE -16 #define E_HELP -17 #define FIFO_ERR(e) (fifo_err = e) /** * @file route_fifo.h * @brief Functions for modifying routing data via fifo commands. */ #ifndef SP_ROUTE_ROUTE_FIFO_H #define SP_ROUTE_ROUTE_FIFO_H #include "../../mi/mi.h" #include "carrier_tree.h" /** * @struct fifo_opt * Holds values and command type when a command was passed to fifo */ typedef struct fifo_opt { unsigned int cmd; /*!< Command type passed to FIFO */ unsigned int opts; /*!< Options used */ str domain; /*!< the routing domain */ str prefix; /*!< the scan prefix */ double prob; /*!< the weight of the route rule */ str host; /*!< the hostname or address */ int strip; /*!< the number of digits to be stripped off */ str new_host; /*!< the new host when a host is going to be replaced */ str rewrite_prefix; /*!< the rewrite prefix */ str rewrite_suffix; /*!< the rewrite suffix */ int hash_index; /*!< the hash index */ int status; /*!< the status */ } fifo_opt_t; #define FBUF_SIZE 2048 #define OPT_ADD 0 #define OPT_REMOVE 1 #define OPT_REPLACE 2 #define OPT_DEACTIVATE 3 #define OPT_ACTIVATE 4 #define OPT_MANDATORY 0 #define OPT_OPTIONAL 1 #define OPT_INVALID 2 #define OPT_PREFIX 0 #define OPT_DOMAIN 1 #define OPT_HOST 2 #define OPT_NEW_TARGET 3 #define OPT_PROB 4 #define OPT_R_PREFIX 5 #define OPT_R_SUFFIX 6 #define OPT_HASH_INDEX 7 #define OPT_STRIP 8 /** * Flags for options to determine which options are used */ #define O_PREFIX 1 #define O_DOMAIN (1 << 1) #define O_HOST (1 << 2) #define O_NEW_TARGET (1 << 3) #define O_PROB (1 << 4) #define O_R_PREFIX (1 << 5) #define O_R_SUFFIX (1 << 6) #define O_H_INDEX (1 << 7) #define O_STRIP (1 << 8) /** * Constants define option characters */ #define OPT_PREFIX_CHR 'p' #define OPT_DOMAIN_CHR 'd' #define OPT_HOST_CHR 'h' #define OPT_NEW_TARGET_CHR 't' #define OPT_PROB_CHR 'w' #define OPT_R_PREFIX_CHR 'P' #define OPT_R_SUFFIX_CHR 'S' #define OPT_HASH_INDEX_CHR 'i' #define OPT_STRIP_CHR 's' #define OPT_HELP_CHR '?' #define OPT_STAR "*" struct mi_root* reload_fifo (struct mi_root* cmd_tree, void *param); struct mi_root* dump_fifo (struct mi_root* cmd_tree, void *param); struct mi_root* replace_host (struct mi_root* cmd_tree, void *param); struct mi_root* deactivate_host (struct mi_root* cmd_tree, void *param); struct mi_root* activate_host (struct mi_root* cmd_tree, void *param); struct mi_root* add_host (struct mi_root* cmd_tree, void *param); struct mi_root* delete_host (struct mi_root* cmd_tree, void * param); #endif opensips-2.2.2/modules/carrierroute/route_func.c000066400000000000000000000570131300170765700220640ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_func.c * @brief Routing and balancing functions. */ #include #include #include #include "route_func.h" #include "route_tree.h" #include "route_db.h" #include "../../sr_module.h" #include "../../action.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../ut.h" #include "../../parser/digest/digest.h" #include "../../parser/hf.h" #include "../../mem/mem.h" #include "../../qvalue.h" #include "../../dset.h" #include "../../prime_hash.h" #include "carrierroute.h" /** * Loads user carrier from subscriber table and stores it in an AVP. * * @param _msg the current SIP message * @param _user the user to determine the route tree * @param _domain the domain to determine the route tree * @param _dstavp the name of the AVP where to store the carrier tree id * * @return 1 on success, -1 on failure */ int cr_load_user_carrier(struct sip_msg * _msg, pv_elem_t *_user, pv_elem_t *_domain, struct multiparam_t *_dstavp) { str user; str domain; int_str avp_val; if (pv_printf_s(_msg, _user, &user)<0) { LM_ERR("cannot print the user\n"); return -1; } if (pv_printf_s(_msg, _domain, &domain)<0) { LM_ERR("cannot print the domain\n"); return -1; } /* get carrier id */ if ((avp_val.n = load_user_carrier(&user, &domain)) < 0) { LM_ERR("error in load user carrier"); return -1; } else { /* set avp ! */ if (add_avp(_dstavp->u.a.flags, _dstavp->u.a.name, avp_val)<0) { LM_ERR("add AVP failed\n"); return -1; } } return 1; } /** * Get the carrier id from multiparam_t structure. * * @param mp carrier id as integer, pseudo-variable or AVP name of carrier * @param _msg SIP message * @return carrier id on success, -1 otherwise * */ int mp2carrier_id(struct sip_msg * _msg, struct multiparam_t *mp) { int carrier_id; struct usr_avp *avp; int_str avp_val; str tmp; /* TODO combine the redundant parts of the logic */ switch (mp->type) { case MP_INT: return mp->u.n; break; case MP_AVP: avp = search_first_avp(mp->u.a.flags, mp->u.a.name, &avp_val, 0); if (!avp) { LM_ERR("cannot find AVP '%d'\n", mp->u.a.name); return -1; } if ((avp->flags&AVP_VAL_STR)==0) { return avp_val.n; } else { carrier_id = find_tree(avp_val.s); if (carrier_id < 0) { LM_WARN("could not find carrier tree '%.*s'\n", avp_val.s.len, avp_val.s.s); /* might be using fallback later... */ } return carrier_id; } break; case MP_PVE: /* retrieve carrier name from parameter */ if (pv_printf_s(_msg, mp->u.p, &tmp)<0) { LM_ERR("cannot print the carrier\n"); return -1; } carrier_id = find_tree(tmp); if (carrier_id < 0) { LM_WARN("could not find carrier tree '%.*s'\n", tmp.len, tmp.s); /* might be using fallback later... */ } return carrier_id; default: LM_ERR("invalid carrier type\n"); return -1; } } /** * Get the domain id from multiparam_t structure. * * @param _msg SIP message * @param mp carrier id as integer, pseudo-variable or AVP name of carrier * @return carrier id on success, -1 otherwise * */ int mp2domain_id(struct sip_msg * _msg, struct multiparam_t *mp) { int domain_id; struct usr_avp *avp; int_str avp_val; str tmp; /* TODO combine the redundant parts of the logic */ switch (mp->type) { case MP_INT: return mp->u.n; break; case MP_AVP: avp = search_first_avp(mp->u.a.flags, mp->u.a.name, &avp_val, 0); if (!avp) { LM_ERR("cannot find AVP '%d'\n", mp->u.a.name); return -1; } if ((avp->flags&AVP_VAL_STR)==0) { return avp_val.n; } else { domain_id = add_domain(&avp_val.s); if (domain_id < 0) { LM_ERR("could not find domain '%.*s'\n", avp_val.s.len, avp_val.s.s); return -1; } return domain_id; } break; case MP_PVE: /* retrieve domain name from parameter */ if (pv_printf_s(_msg, mp->u.p, &tmp)<0) { LM_ERR("cannot print the domain\n"); return -1; } domain_id = add_domain(&tmp); if (domain_id < 0) { LM_ERR("could not find domain '%.*s'\n", tmp.len, tmp.s); return -1; } return domain_id; default: LM_ERR("invalid domain type\n"); return -1; } } /** * Try to match the reply code rc to the reply code with wildcards. * * @param rcw reply code specifier with wildcards * @param rc the current reply code * * @return 0 on match, -1 otherwise */ static inline int reply_code_matcher(const str *rcw, const str *rc) { int i; if (rcw->len==0) return 0; if (rcw->len != rc->len) return -1; for (i=0; ilen; i++) { if (rcw->s[i]!='.' && rcw->s[i]!=rc->s[i]) return -1; } return 0; } /** * writes the next_domain avp using the rule list of route_tree * * @param failure_tree the current failure routing tree node * @param host last tried host * @param reply_code the last reply code * @param flags flags for the failure route rule * @param dstavp the name of the AVP where to store the next domain * * @return 0 on success, -1 on failure */ static int set_next_domain_on_rule(const struct failure_route_tree_item *failure_tree, const str *host, const str *reply_code, const flag_t flags, const struct multiparam_t *dstavp) { struct failure_route_rule * rr; int_str avp_val; assert(failure_tree != NULL); LM_DBG("searching for matching routing rules"); for (rr = failure_tree->rule_list; rr != NULL; rr = rr->next) { /* LM_DBG("rr.flags=%d rr.mask=%d flags=%d\n", rr->flags, rr->mask, flags); LM_DBG("rr.host.len=%d host.len=%d\n", rr->host.len, host->len); LM_DBG("rr.host.s='%.*s' host.s='%.*s'\n", rr->host.len, rr->host.s, host->len, host->s); LM_DBG("rr.reply_code.len=%d reply_code.len=%d\n", rr->reply_code.len, reply_code->len); LM_DBG("rr.reply_code.s='%.*s' reply_code.s='%.*s'\n", rr->reply_code.len, rr->reply_code.s, reply_code->len, reply_code->s); */ if (((rr->mask & flags) == rr->flags) && ((rr->host.len == 0) || (str_strcmp(host, &rr->host)==0)) && (reply_code_matcher(&(rr->reply_code), reply_code)==0)) { avp_val.n = rr->next_domain; if (add_avp(dstavp->u.a.flags, dstavp->u.a.name, avp_val)<0) { LM_ERR("set AVP failed\n"); return -1; } LM_INFO("next_domain is %d.\n", rr->next_domain); return 0; } } return -1; } /** * traverses the failure routing tree until a matching rule is found. * The longest match is taken, so it is possible to define * failure route rules for a single number * * @param failure_tree the current routing tree node * @param uri the uri to be rewritten at the current position * @param host last tried host * @param reply_code the last reply code * @param flags flags for the failure route rule * @param dstavp the name of the AVP where to store the next domain * * @return 0 on success, -1 on failure, 1 on no more matching child node and no rule list */ static int set_next_domain_recursor(const struct failure_route_tree_item *failure_tree, const str *uri, const str *host, const str *reply_code, const flag_t flags, const struct multiparam_t *dstavp) { int ret; struct failure_route_tree_item *re_tree; str re_uri = *uri; /* Skip over non-digits. */ while (re_uri.len > 0 && !isdigit(*re_uri.s)) { ++re_uri.s; --re_uri.len; } if (re_uri.len == 0 || failure_tree->nodes[*re_uri.s - '0'] == NULL) { if (failure_tree->rule_list == NULL) { LM_INFO("URI or route tree nodes empty, empty rule list\n"); return 1; } else { return set_next_domain_on_rule(failure_tree, host, reply_code, flags, dstavp); } } else { /* match, goto the next digit of the uri and try again */ re_tree = failure_tree->nodes[*re_uri.s - '0']; re_uri.s++; re_uri.len--; ret = set_next_domain_recursor(re_tree, &re_uri, host, reply_code, flags, dstavp); switch (ret) { case 0: return 0; case 1: if (failure_tree->rule_list != NULL) { return set_next_domain_on_rule(failure_tree, host, reply_code, flags, dstavp); } else { LM_INFO("empty rule list for host [%.*s]%.*s\n", re_uri.len, re_uri.s, host->len, host->s); return 1; } default: return -1; } } } /** * searches for a rule int rt with hash_index prob - 1 * If the rule with the desired hash index is deactivated, * the next working rule is used. * * @param rf the route_flags node to search for rule * @param prob the hash index * * @return pointer to route rule on success, NULL on failure */ static struct route_rule * get_rule_by_hash(const struct route_flags * rf, const int prob) { struct route_rule * act_hash = NULL; if (prob > rf->rule_num) { LM_WARN("too large desired hash, taking highest\n"); act_hash = rf->rules[rf->rule_num - 1]; } act_hash = rf->rules[prob - 1]; if (!act_hash->status) { if (act_hash->backup && act_hash->backup->rr) { act_hash = act_hash->backup->rr; } else { act_hash = NULL; } } LM_INFO("desired hash was %i, return %i\n", prob, act_hash ? act_hash->hash_index : -1); return act_hash; } /** * does the work for rewrite_on_rule, writes the new URI into dest * * @param rs the route rule used for rewriting * @param dest the returned new destination URI * @param msg the sip message * @param user the localpart of the uri to be rewritten * @param dstavp the name of the destination AVP where the used host name is stored * * @return 0 on success, -1 on failure * * @see rewrite_on_rule() */ static int actually_rewrite(const struct route_rule *rs, str *dest, const struct sip_msg *msg, const str * user, struct multiparam_t *dstavp) { size_t len; char *p; int_str avp_val; int strip = 0; strip = (rs->strip > user->len ? user->len : rs->strip); strip = (strip < 0 ? 0 : strip); len = rs->local_prefix.len + user->len + rs->local_suffix.len + AT_SIGN_LEN + rs->host.len - strip; if (msg->parsed_uri.type == SIPS_URI_T) { len += SIPS_URI_LEN; } else { len += SIP_URI_LEN; } dest->s = (char *)pkg_malloc(len + 1); if (dest->s == NULL) { LM_ERR("out of private memory.\n"); return -1; } dest->len = len; p = dest->s; if (msg->parsed_uri.type == SIPS_URI_T) { memcpy(p, SIPS_URI, SIPS_URI_LEN); p += SIPS_URI_LEN; } else { memcpy(p, SIP_URI, SIP_URI_LEN); p += SIP_URI_LEN; } if (user->len) { memcpy(p, rs->local_prefix.s, rs->local_prefix.len); p += rs->local_prefix.len; memcpy(p, user->s + strip, user->len - strip); p += user->len - strip; memcpy(p, rs->local_suffix.s, rs->local_suffix.len); p += rs->local_suffix.len; memcpy(p, AT_SIGN, AT_SIGN_LEN); p += AT_SIGN_LEN; } /* this could be an error, or a blacklisted destination */ if (rs->host.len == 0) { *p = '\0'; pkg_free(dest->s); return -1; } memcpy(p, rs->host.s, rs->host.len); p += rs->host.len; *p = '\0'; if (dstavp) { avp_val.s = rs->host; if (add_avp(AVP_VAL_STR | dstavp->u.a.flags, dstavp->u.a.name, avp_val)<0) { LM_ERR("set AVP failed\n"); pkg_free(dest->s); return -1; } } return 0; } /** * writes the uri dest using the rule list of route_tree * * @param route_tree the current routing tree node * @param flags user defined flags * @param dest the returned new destination URI * @param msg the sip message * @param user the localpart of the uri to be rewritten * @param hash_source the SIP header used for hashing * @param alg the algorithm used for hashing * @param dstavp the name of the destination AVP where the used host name is stored * * @return 0 on success, -1 on failure, 1 on empty rule list */ static int rewrite_on_rule(const struct route_tree_item * route_tree, flag_t flags, str * dest, struct sip_msg * msg, const str * user, const enum hash_source hash_source, const enum hash_algorithm alg, struct multiparam_t *dstavp) { struct route_flags * rf; struct route_rule * rr; int prob; assert(route_tree != NULL); assert(route_tree->flag_list != NULL); LM_DBG("searching for matching routing rules"); for (rf = route_tree->flag_list; rf != NULL; rf = rf->next) { /* LM_DBG("actual flags %i, searched flags %i, mask %i and match %i", rf->flags, flags, rf->mask, flags&rf->mask); */ if ((flags&rf->mask) == rf->flags) break; } if (rf==NULL) { LM_INFO("did not find a match for flags %d\n", flags); return -1; } if (rf->rule_list == NULL) { LM_INFO("empty rule list\n"); return 1; } switch (alg) { case alg_prime: if ((prob = prime_hash_func(msg, hash_source, rf->max_targets)) < 0) { LM_ERR("could not hash message with prime algorithm"); return -1; } if ((rr = get_rule_by_hash(rf, prob)) == NULL) { LM_CRIT("no route found\n"); return -1; } break; case alg_crc32: if(rf->dice_max == 0) { LM_ERR("invalid dice_max value\n"); return -1; } if ((prob = hash_func(msg, hash_source, rf->dice_max)) < 0) { LM_ERR("could not hash message with CRC32"); return -1; } /* This auto-magically takes the last rule if anything is broken. * Sometimes the hash result is zero. If the first rule is off * (has a probablility of zero) then it has also a dice_to of * zero and the message could not be routed at all if we use * '<' here. Thus the '<=' is necessary. */ for (rr = rf->rule_list; rr->next != NULL && rr->dice_to <= prob; rr = rr->next) {} if (!rr->status) { if (!rr->backup) { LM_ERR("all routes are off\n"); return -1; } else { if (!rr->backup->rr) { LM_ERR("all routes are off\n"); return -1; } rr = rr->backup->rr; } } break; default: LM_ERR("invalid hash algorithm\n"); return -1; } return actually_rewrite(rr, dest, msg, user, dstavp); } /** * traverses the routing tree until a matching rule is found * The longest match is taken, so it is possible to define * route rules for a single number * * @param route_tree the current routing tree node * @param pm the user to be used for prefix matching * @param flags user defined flags * @param dest the returned new destination URI * @param msg the sip message * @param user the localpart of the uri to be rewritten * @param hash_source the SIP header used for hashing * @param alg the algorithm used for hashing * @param dstavp the name of the destination AVP where the used host name is stored * * @return 0 on success, -1 on failure, 1 on no more matching child node and no rule list */ static int rewrite_uri_recursor(const struct route_tree_item * route_tree, const str * pm, flag_t flags, str * dest, struct sip_msg * msg, const str * user, const enum hash_source hash_source, const enum hash_algorithm alg, struct multiparam_t *dstavp) { struct route_tree_item *re_tree; str re_pm; re_pm=*pm; /* Skip over non-digits. */ while (re_pm.len > 0 && !isdigit(*re_pm.s)) { ++re_pm.s; --re_pm.len; } if (re_pm.len == 0 || route_tree->nodes[*re_pm.s - '0'] == NULL) { if (route_tree->flag_list == NULL) { LM_INFO("URI or route tree nodes empty, empty flag list\n"); return 1; } else { return rewrite_on_rule(route_tree, flags, dest, msg, user, hash_source, alg, dstavp); } } else { /* match, goto the next digit of the uri and try again */ re_tree = route_tree->nodes[*re_pm.s - '0']; re_pm.s = re_pm.s + 1; re_pm.len = re_pm.len - 1; switch (rewrite_uri_recursor(re_tree, &re_pm, flags, dest, msg, user, hash_source, alg, dstavp)) { case 0: return 0; case 1: if (route_tree->flag_list != NULL) { return rewrite_on_rule(route_tree, flags, dest, msg, user, hash_source, alg, dstavp); } else { LM_INFO("empty flag list for prefix [%.*s]%.*s\n", user->len - re_pm.len, user->s, re_pm.len, re_pm.s); return 1; } default: return -1; } } } /** * rewrites the request URI of msg after determining the * new destination URI * * @param _msg the current SIP message * @param _carrier the requested carrier * @param _domain the requested routing domain * @param _prefix_matching the user to be used for prefix matching * @param _rewrite_user the localpart of the URI to be rewritten * @param _hsrc the SIP header used for hashing * @param _halg the hash algorithm used for hashing * @param _dstavp the name of the destination AVP where the used host name is stored * * @return 1 on success, -1 on failure */ int cr_do_route(struct sip_msg * _msg, struct multiparam_t *_carrier, struct multiparam_t *_domain, pv_elem_t *_prefix_matching, pv_elem_t *_rewrite_user, enum hash_source _hsrc, enum hash_algorithm _halg, struct multiparam_t *_dstavp) { int carrier_id; int domain_id; str rewrite_user; str prefix_matching; flag_t flags; struct rewrite_data * rd; struct carrier_tree * ct; struct route_tree * rt; struct action act; str dest; int ret; ret = -1; carrier_id = mp2carrier_id(_msg, _carrier); domain_id = mp2domain_id(_msg, _domain); if (domain_id < 0) { LM_ERR("invalid domain id %d\n", domain_id); return -1; } if (pv_printf_s(_msg, _rewrite_user, &rewrite_user)<0) { LM_ERR("cannot print the rewrite_user\n"); return -1; } if (pv_printf_s(_msg, _prefix_matching, &prefix_matching)<0) { LM_ERR("cannot print the prefix_matching\n"); return -1; } flags = _msg->flags; do { rd = get_data(); } while (rd == NULL); ct=NULL; if (carrier_id < 0) { if (fallback_default) { LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id); ct = rd->carriers[rd->default_carrier_index]; } } else if (carrier_id == 0) { ct = rd->carriers[rd->default_carrier_index]; } else { ct = get_carrier_tree(carrier_id, rd); if (ct == NULL) { if (fallback_default) { LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id); ct = rd->carriers[rd->default_carrier_index]; } } } if (ct == NULL) { LM_ERR("cannot get carrier tree\n"); goto unlock_and_out; } rt = get_route_tree_by_id(ct, domain_id); if (rt == NULL) { LM_ERR("desired routing domain doesn't exist, prefix %.*s, carrier %d, domain %d\n", prefix_matching.len, prefix_matching.s, carrier_id, domain_id); goto unlock_and_out; } if (rewrite_uri_recursor(rt->tree, &prefix_matching, flags, &dest, _msg, &rewrite_user, _hsrc, _halg, _dstavp) != 0) { /* this is not necessarily an error, rewrite_recursor does already some error logging */ LM_INFO("rewrite_uri_recursor doesn't complete, uri %.*s, carrier %d, domain %d\n", prefix_matching.len, prefix_matching.s, carrier_id, domain_id); goto unlock_and_out; } LM_INFO("uri %.*s was rewritten to %.*s\n", rewrite_user.len, rewrite_user.s, dest.len, dest.s); act.type = SET_URI_T; act.elem[0].type= STR_ST; act.elem[0].u.s = dest; act.next = NULL; ret = do_action(&act, _msg); if (ret < 0) { LM_ERR("Error in do_action()\n"); } pkg_free(dest.s); unlock_and_out: release_data(rd); return ret; } /** * rewrites the request URI of msg after determining the * new destination URI with the crc32 hash algorithm. * * @param _msg the current SIP message * @param _carrier the requested carrier * @param _domain the requested routing domain * @param _prefix_matching the user to be used for prefix matching * @param _rewrite_user the localpart of the URI to be rewritten * @param _hsrc the SIP header used for hashing * @param _dstavp the name of the destination AVP where the used host name is stored * * @return 1 on success, -1 on failure */ int cr_route(struct sip_msg * _msg, struct multiparam_t *_carrier, struct multiparam_t *_domain, pv_elem_t *_prefix_matching, pv_elem_t *_rewrite_user, enum hash_source _hsrc, struct multiparam_t *_dstavp) { return cr_do_route(_msg, _carrier, _domain, _prefix_matching, _rewrite_user, _hsrc, alg_crc32, _dstavp); } /** * rewrites the request URI of msg after determining the * new destination URI with the prime hash algorithm. * * @param _msg the current SIP message * @param _carrier the requested carrier * @param _domain the requested routing domain * @param _prefix_matching the user to be used for prefix matching * @param _rewrite_user the localpart of the URI to be rewritten * @param _hsrc the SIP header used for hashing * @param _dstavp the name of the destination AVP where the used host name is stored * * @return 1 on success, -1 on failure */ int cr_prime_route(struct sip_msg * _msg, struct multiparam_t *_carrier, struct multiparam_t *_domain, pv_elem_t *_prefix_matching, pv_elem_t *_rewrite_user, enum hash_source _hsrc, struct multiparam_t *_dstavp) { return cr_do_route(_msg, _carrier, _domain, _prefix_matching, _rewrite_user, _hsrc, alg_prime, _dstavp); } /** * Loads next domain from failure routing table and stores it in an AVP. * * @param _msg the current SIP message * @param _carrier the requested carrier * @param _domain the requested routing domain * @param _prefix_matching the user to be used for prefix matching * @param _host the host name to be used for rule matching * @param _reply_code the reply code to be used for rule matching * @param _dstavp the name of the destination AVP * * @return 1 on success, -1 on failure */ int cr_load_next_domain(struct sip_msg * _msg, struct multiparam_t *_carrier, struct multiparam_t *_domain, pv_elem_t *_prefix_matching, pv_elem_t *_host, pv_elem_t *_reply_code, struct multiparam_t *_dstavp) { int carrier_id; int domain_id; str prefix_matching; str host; str reply_code; flag_t flags; struct rewrite_data * rd; struct carrier_tree * ct; struct route_tree * rt; int ret; ret = -1; carrier_id = mp2carrier_id(_msg, _carrier); domain_id = mp2domain_id(_msg, _domain); if (domain_id < 0) { LM_ERR("invalid domain id %d\n", domain_id); return -1; } if (pv_printf_s(_msg, _prefix_matching, &prefix_matching)<0) { LM_ERR("cannot print the prefix_matching\n"); return -1; } if (pv_printf_s(_msg, _host, &host)<0) { LM_ERR("cannot print the host\n"); return -1; } if (pv_printf_s(_msg, _reply_code, &reply_code)<0) { LM_ERR("cannot print the reply_code\n"); return -1; } flags = _msg->flags; do { rd = get_data(); } while (rd == NULL); ct=NULL; if (carrier_id < 0) { if (fallback_default) { LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id); ct = rd->carriers[rd->default_carrier_index]; } } else if (carrier_id == 0) { ct = rd->carriers[rd->default_carrier_index]; } else { ct = get_carrier_tree(carrier_id, rd); if (ct == NULL) { if (fallback_default) { LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id); ct = rd->carriers[rd->default_carrier_index]; } } } if (ct == NULL) { LM_ERR("cannot get carrier tree\n"); goto unlock_and_out; } rt = get_route_tree_by_id(ct, domain_id); if (rt == NULL) { LM_ERR("desired routing domain doesn't exist, prefix %.*s, carrier %d, domain %d\n", prefix_matching.len, prefix_matching.s, carrier_id, domain_id); goto unlock_and_out; } if (set_next_domain_recursor(rt->failure_tree, &prefix_matching, &host, &reply_code, flags, _dstavp) != 0) { LM_ERR("during set_next_domain_recursor, prefix '%.*s', carrier %d, domain %d\n", prefix_matching.len, prefix_matching.s, carrier_id, domain_id); goto unlock_and_out; } ret = 1; unlock_and_out: release_data(rd); return ret; } opensips-2.2.2/modules/carrierroute/route_func.h000066400000000000000000000072421300170765700220700ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_func.h * @brief Routing and balancing functions. */ #ifndef SP_ROUTE_ROUTE_FUNC_H #define SP_ROUTE_ROUTE_FUNC_H #include "../../parser/msg_parser.h" #include "../../pvar.h" #include "../../prime_hash.h" #include "carrierroute.h" /** * Loads user carrier from subscriber table and stores it in an AVP. * * @param _msg the current SIP message * @param _user the user to determine the route tree * @param _domain the domain to determine the route tree * @param _dstavp the name of the AVP where to store the carrier tree id * * @return 1 on success, -1 on failure */ int cr_load_user_carrier(struct sip_msg * _msg, pv_elem_t *_user, pv_elem_t *_domain, struct multiparam_t *_dstavp); /** * rewrites the request URI of msg after determining the * new destination URI * * @param _msg the current SIP message * @param _carrier the requested carrier * @param _domain the requested routing domain * @param _prefix_matching the user to be used for prefix matching * @param _rewrite_user the localpart of the URI to be rewritten * @param _hsrc the SIP header used for hashing * @param _dstavp the name of the destination AVP where the used host name is stored * * @return 1 on success, -1 on failure */ int cr_route(struct sip_msg * _msg, struct multiparam_t *_carrier, struct multiparam_t *_domain, pv_elem_t *_prefix_matching, pv_elem_t *_rewrite_user, enum hash_source _hsrc, struct multiparam_t *_dstavp); /** * rewrites the request URI of msg after determining the * new destination URI * * @param _msg the current SIP message * @param _carrier the requested carrier * @param _domain the requested routing domain * @param _prefix_matching the user to be used for prefix matching * @param _rewrite_user the localpart of the URI to be rewritten * @param _hsrc the SIP header used for hashing * @param _dstavp the name of the destination AVP where the used host name is stored * * @return 1 on success, -1 on failure */ int cr_prime_route(struct sip_msg * _msg, struct multiparam_t *_carrier, struct multiparam_t *_domain, pv_elem_t *_prefix_matching, pv_elem_t *_rewrite_user, enum hash_source _hsrc, struct multiparam_t *_dstavp); /** * Loads next domain from failure routing table and stores it in an AVP. * * @param _msg the current SIP message * @param _carrier the requested carrier * @param _domain the requested routing domain * @param _prefix_matching the user to be used for prefix matching * @param _host the host name to be used for rule matching * @param _reply_code the reply code to be used for rule matching * @param _dstavp the name of the destination AVP * * @return 1 on success, -1 on failure */ int cr_load_next_domain(struct sip_msg * _msg, struct multiparam_t *_carrier, struct multiparam_t *_domain, pv_elem_t *_prefix_matching, pv_elem_t *_host, pv_elem_t *_reply_code, struct multiparam_t *_dstavp); #endif opensips-2.2.2/modules/carrierroute/route_rule.c000066400000000000000000000350541300170765700221010ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_rule.c * @brief Contains the functions to manage routing rule data. */ #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../ut.h" #include "carrierroute.h" #include "route_rule.h" static int rule_fixup_recursor(struct route_tree_item * rt); static int fixup_rule_backup(struct route_flags * rf, struct route_rule * rr); /** * Adds a route rule to rf. prefix, rewrite_hostpart, rewrite_local_prefix, * rewrite_local_suffix, and comment must not contain NULL pointers. * * @param rf the current route_flags struct * @param prefix the whole scan prefix * @param max_targets the number of targets * @param prob the weight of the rule * @param rewrite_hostpart the rewrite_host of the rule * @param strip the strip value of the rule * @param rewrite_local_prefix the rewrite prefix * @param rewrite_local_suffix the rewrite suffix * @param status the status of the rule * @param hash_index the hash index of the rule * @param backup indicates if the route is backed up by another. only useful if status==0, if set, it is the hash value of another rule * @param backed_up an NULL-termintated array of hash indices of the route for which this route is backup * @param comment a comment for the route rule * * @return 0 on success, -1 on failure * * @see add_route_to_tree() */ int add_route_rule(struct route_flags *rf, const str * prefix, int max_targets, double prob, const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix, const str * rewrite_local_suffix, int status, int hash_index, int backup, int * backed_up, const str * comment) { struct route_rule * shm_rr, * prev = NULL, * tmp = NULL; struct route_rule_p_list * t_rl; int * t_bu; if (max_targets) { rf->max_targets = max_targets; } else { rf->max_targets++; } if ((shm_rr = shm_malloc(sizeof(struct route_rule))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(shm_rr, 0, sizeof(struct route_rule)); if (shm_str_dup(&shm_rr->host, rewrite_hostpart) != 0) { goto mem_error; } if (shm_str_dup(&shm_rr->prefix, prefix) != 0) { goto mem_error; } shm_rr->strip = strip; if (shm_str_dup(&shm_rr->local_prefix, rewrite_local_prefix) != 0) { goto mem_error; } if (shm_str_dup(&shm_rr->local_suffix, rewrite_local_suffix) != 0) { goto mem_error; } if (shm_str_dup(&shm_rr->comment, comment) != 0) { goto mem_error; } shm_rr->status = status; shm_rr->hash_index = hash_index; shm_rr->orig_prob = prob; if (shm_rr->status || backup != -1) { shm_rr->prob = prob; } else { shm_rr->prob = 0; } if (backup >= 0) { if ((shm_rr->backup = shm_malloc(sizeof(struct route_rule_p_list))) == NULL) { goto mem_error; } memset(shm_rr->backup, 0, sizeof(struct route_rule_p_list)); shm_rr->backup->hash_index = backup; } shm_rr->backed_up = NULL; t_bu = backed_up; if(!backed_up){ LM_INFO("no backed up rules\n"); } while (t_bu && *t_bu != -1) { if ((t_rl = shm_malloc(sizeof(struct route_rule_p_list))) == NULL) { goto mem_error; } memset(t_rl, 0, sizeof(struct route_rule_p_list)); t_rl->hash_index = *t_bu; t_rl->next = shm_rr->backed_up; shm_rr->backed_up = t_rl; t_bu++; } /* rules with a probability of zero are always at the beginning of the list */ tmp = rf->rule_list; while(tmp && tmp->prob == 0){ prev = tmp; tmp = tmp->next; } /* rules with prob > 0 are sorted by hash_index */ while(tmp && (tmp->hash_index < shm_rr->hash_index)){ prev = tmp; tmp = tmp->next; } if(prev){ shm_rr->next = prev->next; prev->next = shm_rr; } else { shm_rr->next = rf->rule_list; rf->rule_list = shm_rr; } return 0; mem_error: LM_ERR("out of shared memory\n"); destroy_route_rule(shm_rr); return -1; } /** * Compares the priority of two failure route rules. * * @param rr1 first failure rule * @param rr2 second failure rule * * @return 0 if rr1 and rr2 have the same priority, -1 if rr1 has higher priority than rr2, 1 if rr1 has lower priority than rr2. * * @see add_failure_route_to_tree() */ int rule_prio_cmp(struct failure_route_rule *rr1, struct failure_route_rule *rr2) { int n1, n2, i; /* host has highest priority */ if ((rr1->host.len == 0) && (rr2->host.len > 0)) { /* host1 is wildcard -> rr1 has lower priority */ return 1; } else if ((rr1->host.len > 0) && (rr2->host.len == 0)) { /* host2 is wildcard -> rr1 has higher priority */ return -1; } else { /* reply_code has second highest priority */ n1=0; n2=0; for (i=0; i < rr1->reply_code.len; i++) { if (rr1->reply_code.s[i]=='.') n1++; } for (i=0; i < rr2->reply_code.len; i++) { if (rr2->reply_code.s[i]=='.') n2++; } if (n1 < n2) { /* reply_code1 has fewer wildcards -> rr1 has higher priority */ return -1; } else if (n1 > n2) { /* reply_code1 has more wildcards -> rr1 has lower priority */ return 1; } else { /* flags have lowest priority */ if (rr1->mask > rr2->mask) { return -1; } else if (rr1->mask < rr2->mask) { return 1; } } } return 0; } /** * Adds a failure route rule to rt. prefix, host, reply_code, and comment * must not contain NULL pointers. * * @param failure_tree the current route tree node * @param prefix the whole scan prefix * @param host the hostname last tried * @param reply_code the reply code * @param flags user defined flags * @param mask mask for user defined flags * @param next_domain continue routing with this domain * @param comment a comment for the route rule * * @return 0 on success, -1 on failure * * @see add_failure_route_to_tree() */ int add_failure_route_rule(struct failure_route_tree_item * failure_tree, const str * prefix, const str * host, const str * reply_code, flag_t flags, flag_t mask, const int next_domain, const str * comment) { struct failure_route_rule * shm_rr; struct failure_route_rule * rr; struct failure_route_rule * prev; if ((shm_rr = shm_malloc(sizeof(struct failure_route_rule))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(shm_rr, 0, sizeof(struct failure_route_rule)); if (shm_str_dup(&shm_rr->host, host) != 0) { goto mem_error; } if (shm_str_dup(&shm_rr->reply_code, reply_code) != 0) { goto mem_error; } shm_rr->flags = flags; shm_rr->mask = mask; shm_rr->next_domain = next_domain; if (shm_str_dup(&shm_rr->comment, comment) != 0) { goto mem_error; } /* before inserting into list, check priorities! */ rr=failure_tree->rule_list; prev=NULL; while ((rr != NULL) && (rule_prio_cmp(shm_rr, rr) > 0)) { prev=rr; rr=rr->next; } if(prev){ shm_rr->next = prev->next; prev->next = shm_rr; } else { shm_rr->next = failure_tree->rule_list; failure_tree->rule_list = shm_rr; } return 0; mem_error: LM_ERR("out of shared memory\n"); destroy_failure_route_rule(shm_rr); return -1; } /** * Fixes the route rules by creating an array for accessing * route rules by hash index directly * * @param rd route data to be fixed * * @return 0 on success, -1 on failure */ int rule_fixup(struct rewrite_data * rd) { int i,j; for (i=0; itree_num; i++) { for (j=0; jcarriers[i]->tree_num; j++) { if (rd->carriers[i]->trees[j] && rd->carriers[i]->trees[j]->tree) { LM_INFO("fixing tree %.*s\n", rd->carriers[i]->trees[j]->name.len, rd->carriers[i]->trees[j]->name.s); if (rule_fixup_recursor(rd->carriers[i]->trees[j]->tree) < 0) { return -1; } } else { LM_NOTICE("empty tree at [%i][%i]\n", i, j); } } } return 0; } /** * Does the work for rule_fixup recursively. * First, it tries to set a pointer the rules with an existing hash index * at the marching array index. Afterward, remaining rules are populated * with incrementing hash indices. * * @param rt the route tree node to be fixed up * * @return 0 on success, -1 on failure */ static int rule_fixup_recursor(struct route_tree_item * rt) { struct route_rule * rr; struct route_flags * rf; int i; int ret = 0; int p_dice; for (rf=rt->flag_list; rf!=NULL; rf=rf->next) { p_dice = 0; if (rf->rule_list) { rr = rf->rule_list; rf->rule_num = 0; while (rr) { rf->rule_num++; rf->dice_max += rr->prob * DICE_MAX; rr = rr->next; } rr = rf->rule_list; while (rr) { rr->dice_to = (rr->prob * DICE_MAX) + p_dice; p_dice = rr->dice_to; rr = rr->next; } if (rf->rule_num != rf->max_targets) { LM_ERR("number of rules(%i) differs from max_targets(%i), maybe your config is wrong?\n", rf->rule_num, rf->max_targets); return -1; } if(rf->rules) { shm_free(rf->rules); rf->rules = NULL; } if ((rf->rules = shm_malloc(sizeof(struct route_rule *) * rf->rule_num)) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(rf->rules, 0, sizeof(struct route_rule *) * rf->rule_num); for (rr = rf->rule_list; rr; rr = rr->next) { if (rr->hash_index) { if (rr->hash_index > rf->rule_num) { LM_ERR("too large hash index %i, max is %i\n", rr->hash_index, rf->rule_num); shm_free(rf->rules); return -1; } if (rf->rules[rr->hash_index - 1]) { LM_ERR("duplicate hash index %i\n", rr->hash_index); shm_free(rf->rules); return -1; } rf->rules[rr->hash_index - 1] = rr; LM_INFO("rule with host %.*s hash has hashindex %i.\n", rr->host.len, rr->host.s, rr->hash_index); } } rr = rf->rule_list; i=0; while (rr && i < rf->rule_num) { if (!rr->hash_index) { if (rf->rules[i]) { i++; } else { rf->rules[i] = rr; rr->hash_index = i + 1; LM_INFO("hashless rule with host %.*s hash hash_index %i\n", rr->host.len, rr->host.s, i+1); rr = rr->next; } } else { rr = rr->next; } } if (rr) { LM_ERR("Could not populate rules: rr: %p\n", rr); return -1; } for(i=0; irule_num; i++){ ret += fixup_rule_backup(rf, rf->rules[i]); } } } for (i=0; i<10; i++) { if (rt->nodes[i]) { ret += rule_fixup_recursor(rt->nodes[i]); } } return ret; } static int fixup_rule_backup(struct route_flags * rf, struct route_rule * rr){ struct route_rule_p_list * rl; if(!rr->status && rr->backup){ if((rr->backup->rr = find_rule_by_hash(rf, rr->backup->hash_index)) == NULL){ LM_ERR("didn't find backup route\n"); return -1; } } rl = rr->backed_up; while(rl){ if((rl->rr = find_rule_by_hash(rf, rl->hash_index)) == NULL){ LM_ERR("didn't find backed up route\n"); return -1; } rl = rl->next; } return 0; } struct route_rule * find_rule_by_hash(struct route_flags * rf, int hash){ struct route_rule * rr; rr = rf->rule_list; while(rr){ if(rr->hash_index == hash){ return rr; } rr = rr->next; } return NULL; } struct route_rule * find_rule_by_host(struct route_flags * rf, str * host){ struct route_rule * rr; rr = rf->rule_list; while(rr){ if(str_strcmp(&(rr->host), host) == 0){ return rr; } rr = rr->next; } return NULL; } int add_backup_route(struct route_rule * rule, struct route_rule * backup){ struct route_rule_p_list * tmp = NULL; if(!backup->status){ LM_ERR("desired backup route is inactive\n"); return -1; } if((tmp = shm_malloc(sizeof(struct route_rule_p_list))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(tmp, 0, sizeof(struct route_rule_p_list)); tmp->hash_index = rule->hash_index; tmp->rr = rule; tmp->next = backup->backed_up; backup->backed_up = tmp; tmp = NULL; if((tmp = shm_malloc(sizeof(struct route_rule_p_list))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(tmp, 0, sizeof(struct route_rule_p_list)); tmp->hash_index = backup->hash_index; tmp->rr = backup; rule->backup = tmp; if(rule->backed_up){ tmp = rule->backed_up; while(tmp->next) { tmp = tmp->next; } tmp->next = backup->backed_up; backup->backed_up = rule->backed_up; rule->backed_up = NULL; } tmp = rule->backup->rr->backed_up; while(tmp) { tmp->rr->backup->hash_index = rule->backup->hash_index; tmp->rr->backup->rr = rule->backup->rr; tmp = tmp->next; } return 0; } int remove_backed_up(struct route_rule * rule){ struct route_rule_p_list * rl, * prev = NULL; if(rule->backup) { if(rule->backup->rr) { rl = rule->backup->rr->backed_up; while(rl) { if(rl->hash_index == rule->hash_index) { if(prev) { prev->next = rl->next; } else { rule->backup->rr->backed_up = rl->next; } shm_free(rl); shm_free(rule->backup); rule->backup = NULL; return 0; } prev = rl; rl = rl->next; } } return -1; } return 0; } struct route_rule * find_auto_backup(struct route_flags * rf, struct route_rule * rule){ struct route_rule * rr; rr = rf->rule_list; while(rr){ if(!rr->backed_up && (rr->hash_index != rule->hash_index) && rr->status){ return rr; } rr = rr->next; } return NULL; } /** * Destroys route rule rr by freeing all its memory. * * @param rr route rule to be destroyed */ void destroy_route_rule(struct route_rule * rr) { struct route_rule_p_list * t_rl; if (rr->host.s) { shm_free(rr->host.s); } if (rr->local_prefix.s) { shm_free(rr->local_prefix.s); } if (rr->local_suffix.s) { shm_free(rr->local_suffix.s); } if (rr->comment.s) { shm_free(rr->comment.s); } if (rr->prefix.s) { shm_free(rr->prefix.s); } if(rr->backup){ shm_free(rr->backup); } while(rr->backed_up){ t_rl = rr->backed_up->next; shm_free(rr->backed_up); rr->backed_up = t_rl; } shm_free(rr); return; } /** * Destroys failure route rule rr by freeing all its memory. * * @param rr route rule to be destroyed */ void destroy_failure_route_rule(struct failure_route_rule * rr) { if (rr->host.s) { shm_free(rr->host.s); } if (rr->comment.s) { shm_free(rr->comment.s); } if (rr->prefix.s) { shm_free(rr->prefix.s); } if (rr->reply_code.s) { shm_free(rr->reply_code.s); } shm_free(rr); return; } opensips-2.2.2/modules/carrierroute/route_rule.h000066400000000000000000000074331300170765700221060ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_rule.h * @brief Contains the functions to manage routing rule data. */ #ifndef SP_ROUTE_ROUTE_RULE_H #define SP_ROUTE_ROUTE_RULE_H #include "route.h" /** * Adds a route rule to rf * * @param rf the current route_flags struct * @param prefix the whole scan prefix * @param max_targets the number of targets * @param prob the weight of the rule * @param rewrite_hostpart the rewrite_host of the rule * @param strip the number of digits to be stripped off userpart before prepending prefix * @param rewrite_local_prefix the rewrite prefix * @param rewrite_local_suffix the rewrite suffix * @param status the status of the rule * @param hash_index the hash index of the rule * @param backup indicates if the route is backed up by another. only useful if status==0, if set, it is the hash value of another rule * @param backed_up an NULL-termintated array of hash indices of the route for which this route is backup * @param comment a comment for the route rule * * @return 0 on success, -1 on failure * * @see add_route_to_tree() */ int add_route_rule(struct route_flags *rf, const str * prefix, int max_targets, double prob, const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix, const str * rewrite_local_suffix, int status, int hash_index, int backup, int * backed_up, const str * comment); /** * Adds a failure route rule to rt * * @param failure_tree the current route tree node * @param prefix the whole scan prefix * @param host the hostname last tried * @param reply_code the reply code * @param flags user defined flags * @param mask mask for user defined flags * @param next_domain continue routing with this domain id * @param comment a comment for the route rule * * @return 0 on success, -1 on failure * * @see add_failure_route_to_tree() */ int add_failure_route_rule(struct failure_route_tree_item * failure_tree, const str * prefix, const str * host, const str * reply_code, flag_t flags, flag_t mask, const int next_domain, const str * comment); /** * Destroys route rule rr by freeing all its memory. * * @param rr route rule to be destroyed */ void destroy_route_rule(struct route_rule * rr); /** * Destroys failure route rule rr by freeing all its memory. * * @param rr route rule to be destroyed */ void destroy_failure_route_rule(struct failure_route_rule * rr); /** * Fixes the route rules by creating an array for accessing * route rules by hash index directly * * @param rd route data to be fixed * * @return 0 on success, -1 on failure */ int rule_fixup(struct rewrite_data * rd); struct route_rule * find_rule_by_hash(struct route_flags * rf, int hash); struct route_rule * find_rule_by_host(struct route_flags * rf, str * host); int add_backup_route(struct route_rule * rule, struct route_rule * backup); int remove_backed_up(struct route_rule * rule); struct route_rule * find_auto_backup(struct route_flags * rf, struct route_rule * rule); #endif opensips-2.2.2/modules/carrierroute/route_tree.c000066400000000000000000000355771300170765700221030ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_tree.c * @brief Contains the functions to manage routing tree data in a digital tree. */ #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../ut.h" #include "carrierroute.h" #include "route.h" #include "route_rule.h" #include "load_data.h" /** * holds the map between routing domain names and numbers */ struct route_map ** script_routes = NULL; static void destroy_route_tree_item(struct route_tree_item *route_tree); static void destroy_failure_route_tree_item(struct failure_route_tree_item *route_tree_item); static struct route_tree_item * create_route_tree_item(void); static struct failure_route_tree_item * create_failure_route_tree_item(void); static int add_route_tree(struct carrier_tree * ct, struct route_tree * rt); /** * returns the routing tree for the given domain, if domain's tree * doesn't exist, it will be created. If the trees are completely * filled and a not existing domain shall be added, an error is * returned * * @param domain the domain name of desired routing tree * @param rd route data to be searched * * @return a pointer to the desired routing tree, * NULL on failure */ struct route_tree * get_route_tree(const str * domain, struct carrier_tree * rd) { int i, id; struct route_tree * rt = NULL; if (!rd) { LM_ERR("NULL pointer in parameter\n"); return NULL; } for (i=0; itree_num; i++) { if (rd->trees[i] && rd->trees[i]->name.s) { if (str_strcmp(&rd->trees[i]->name, domain) == 0) { LM_INFO("found domain %.*s\n", rd->trees[i]->name.len, rd->trees[i]->name.s); return rd->trees[i]; } } } LM_INFO("domain %.*s not found, add it\n", domain->len, domain->s); if ((id = add_domain(domain)) < 0) { LM_ERR("could not add domain\n"); return NULL; } if ((rt = create_route_tree(domain, id)) == NULL) { return NULL; } if ((rt->tree = create_route_tree_item()) == NULL) { return NULL; } if ((rt->failure_tree = create_failure_route_tree_item()) == NULL) { return NULL; } if (add_route_tree(rd, rt) < 0) { LM_ERR("couldn't add route tree\n"); destroy_route_tree(rt); return NULL; } LM_INFO("created route tree: %.*s, with id %i\n", rt->name.len, rt->name.s, rt->id); return rt; } static int add_route_tree(struct carrier_tree * ct, struct route_tree * rt) { int i; LM_INFO("tree %.*s has %ld trees\n", ct->name.len, ct->name.s, (long)ct->tree_num); for (i=0; itree_num; i++) { LM_DBG("tree %p", ct->trees[i]); if (ct->trees[i] == 0) { ct->trees[i] = rt; return 0; } } return -1; } struct route_tree * get_route_tree_by_id(struct carrier_tree * ct, int id) { int i; LM_DBG("searching in carrier %.*s, id %d\n", ct->name.len, ct->name.s, ct->id); for (i=0; itree_num; i++) { if (ct->trees[i]) { LM_DBG("tree %.*s, domain %.*s : %i\n", ct->name.len, ct->name.s, ct->trees[i]->name.len, ct->trees[i]->name.s, ct->trees[i]->id); if (ct->trees[i]->id == id) { return ct->trees[i]; } } } return NULL; } /** * creates a routing tree node in shared memory and sets it up. * * @return a pointer to the newly allocated route tree item, NULL * on error, in which case it LOGs an error message. */ static struct route_tree_item * create_route_tree_item(void) { struct route_tree_item *ret; ret = (struct route_tree_item *) shm_malloc(sizeof(struct route_tree_item)); if (ret == NULL) { LM_ERR("out of shared memory while building route tree.\n"); } else { memset(ret, 0, sizeof(struct route_tree_item)); } return ret; } /** * creates a routing tree node in shared memory and sets it up. * * @return a pointer to the newly allocated route tree item, NULL * on error, in which case it LOGs an error message. */ static struct failure_route_tree_item * create_failure_route_tree_item(void) { struct failure_route_tree_item *ret; ret = (struct failure_route_tree_item *) shm_malloc(sizeof(struct failure_route_tree_item)); if (ret == NULL) { LM_ERR("out of shared memory while building route tree.\n"); } else { memset(ret, 0, sizeof(struct failure_route_tree_item)); } return ret; } /** * Create a new route tree root in shared memory and set it up. * * @param domain the domain name of the route tree * @param id the domain id of the route tree * * @return a pointer to the newly allocated route tree or NULL on * error, in which case it LOGs an error message. */ struct route_tree * create_route_tree(const str * domain, int id) { struct route_tree * tmp; if ((tmp = shm_malloc(sizeof(struct route_tree))) == NULL) { LM_ERR("out of shared memory\n"); return NULL; } memset(tmp, 0, sizeof(struct route_tree)); if (shm_str_dup(&tmp->name, domain)!=0) { LM_ERR("cannot duplicate string\n"); shm_free(tmp); return NULL; } tmp->id = id; return tmp; } /** * Try to find a matching route_flags struct in rt and return it, add it if not found. * * @param route_tree the current route tree node * @param flags user defined flags * @param mask mask for user defined flags * * @return the route_flags struct on success, NULL on failure. * */ struct route_flags * add_route_flags(struct route_tree_item * route_tree, const flag_t flags, const flag_t mask) { struct route_flags *shm_rf; struct route_flags *prev_rf = NULL; struct route_flags *tmp_rf = NULL; /* search for matching route_flags struct */ for (tmp_rf=route_tree->flag_list; tmp_rf!=NULL; tmp_rf=tmp_rf->next) { if ((tmp_rf->flags == flags) && (tmp_rf->mask == mask)) return tmp_rf; } /* not found, insert one */ for (tmp_rf=route_tree->flag_list; tmp_rf!=NULL; tmp_rf=tmp_rf->next) { if (tmp_rf->mask < mask) break; prev_rf=tmp_rf; } if ((shm_rf = shm_malloc(sizeof(struct route_flags))) == NULL) { LM_ERR("out of shared memory\n"); return NULL; } memset(shm_rf, 0, sizeof(struct route_flags)); shm_rf->flags=flags; shm_rf->mask=mask; shm_rf->next=tmp_rf; if (prev_rf) prev_rf->next = shm_rf; else route_tree->flag_list = shm_rf; return shm_rf; } /** * Adds the given route information to the route tree identified by * route_tree. scan_prefix identifies the number for which the information * is and the rewrite_* parameters define what to do in case of a match. * prob gives the probability with which this rule applies if there are * more than one for a given prefix. * * Note that this is a recursive function. It strips off digits from the * beginning of scan_prefix and calls itself. * * @param route_tree the current route tree node * @param scan_prefix the prefix at the current position * @param flags user defined flags * @param mask mask for user defined flags * @param full_prefix the whole scan prefix * @param max_targets the number of targets * @param prob the weight of the rule * @param rewrite_hostpart the rewrite_host of the rule * @param strip the number of digits to be stripped off userpart before prepending prefix * @param rewrite_local_prefix the rewrite prefix * @param rewrite_local_suffix the rewrite suffix * @param status the status of the rule * @param hash_index the hash index of the rule * @param backup indicates if the route is backed up by another. only useful if status==0, if set, it is the hash value of another rule * @param backed_up an -1-termintated array of hash indices of the route for which this route is backup * @param comment a comment for the route rule * * @return 0 on success, -1 on failure * * @see add_route() */ int add_route_to_tree(struct route_tree_item * route_tree, const str * scan_prefix, flag_t flags, flag_t mask, const str * full_prefix, int max_targets, double prob, const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix, const str * rewrite_local_suffix, int status, int hash_index, int backup, int * backed_up, const str * comment) { str next_prefix; struct route_flags *rf; if (scan_prefix->len == 0) { rf = add_route_flags(route_tree, flags, mask); if (rf==NULL) { LM_ERR("cannot add route_flags struct to route_tree\n"); return -1; } return add_route_rule(rf, full_prefix, max_targets, prob, rewrite_hostpart, strip, rewrite_local_prefix, rewrite_local_suffix, status, hash_index, backup, backed_up, comment); } else { if (route_tree->nodes[*scan_prefix->s - '0'] == NULL) { route_tree->nodes[*scan_prefix->s - '0'] = create_route_tree_item(); if (route_tree->nodes[*scan_prefix->s - '0'] == NULL) { return -1; } } next_prefix.s = scan_prefix->s + 1; next_prefix.len = scan_prefix->len - 1; return add_route_to_tree(route_tree->nodes[*scan_prefix->s - '0'], &next_prefix, flags, mask, full_prefix, max_targets, prob, rewrite_hostpart, strip, rewrite_local_prefix, rewrite_local_suffix, status, hash_index, backup, backed_up, comment); } } /** * Adds the given failure route information to the failure route tree identified by * route_tree. scan_prefix, host, reply_code, flags identifies the number for which * the information is and the next_domain parameters defines where to continue * routing in case of a match. * * Note that this is a recursive function. It strips off digits from the * beginning of scan_prefix and calls itself. * * @param failure_tree the current route tree node * @param scan_prefix the prefix at the current position * @param full_prefix the whole scan prefix * @param host the hostname last tried * @param reply_code the reply code * @param flags user defined flags * @param mask mask for user defined flags * @param next_domain continue routing with this domain id * @param comment a comment for the route rule * * @return 0 on success, -1 on failure * * @see add_route() */ int add_failure_route_to_tree(struct failure_route_tree_item * failure_tree, const str * scan_prefix, const str * full_prefix, const str * host, const str * reply_code, const flag_t flags, const flag_t mask, const int next_domain, const str * comment) { str next_prefix; if (!scan_prefix || !scan_prefix->s || *scan_prefix->s == '\0') { return add_failure_route_rule(failure_tree, full_prefix, host, reply_code, flags, mask, next_domain, comment); } else { if (failure_tree->nodes[*scan_prefix->s - '0'] == NULL) { failure_tree->nodes[*scan_prefix->s - '0'] = create_failure_route_tree_item(); if (failure_tree->nodes[*scan_prefix->s - '0'] == NULL) { return -1; } } next_prefix.s = scan_prefix->s + 1; next_prefix.len = scan_prefix->len - 1; return add_failure_route_to_tree(failure_tree->nodes[*scan_prefix->s - '0'], &next_prefix, full_prefix, host, reply_code, flags, mask, next_domain, comment); } } /** * Destroy the route map by freeing its memory. */ void destroy_route_map(void) { struct route_map * tmp, *tmp2; if (script_routes) { tmp = *script_routes; while (tmp) { tmp2 = tmp; tmp = tmp->next; shm_free(tmp2); } *script_routes = NULL; shm_free(script_routes); script_routes = NULL; } } /** * Destroys route_tree by freeing all its memory. * * @param route_tree route tree to be destroyed */ void destroy_route_tree(struct route_tree *route_tree) { destroy_route_tree_item(route_tree->tree); destroy_failure_route_tree_item(route_tree->failure_tree); shm_free(route_tree->name.s); shm_free(route_tree); return; } /** * Destroys route_flags in shared memory by freing all its memory. * * @param rf route_flags struct to be destroyed */ static void destroy_route_flags(struct route_flags *rf) { struct route_rule *rs; struct route_rule *rs_tmp; if (rf->rules) { shm_free(rf->rules); } rs = rf->rule_list; while (rs != NULL) { rs_tmp = rs->next; destroy_route_rule(rs); rs = rs_tmp; } shm_free(rf); } /** * Destroys route_tree_item in shared memory by freing all its memory. * * @param route_tree_item route tree node to be destroyed */ static void destroy_route_tree_item(struct route_tree_item *route_tree_item) { int i; struct route_flags *rf; struct route_flags *rf_tmp; if (!route_tree_item) { LM_ERR("NULL pointer in parameter\n"); } for (i = 0; i < 10; ++i) { if (route_tree_item->nodes[i] != NULL) { destroy_route_tree_item(route_tree_item->nodes[i]); } } rf=route_tree_item->flag_list; while (rf!=NULL) { rf_tmp = rf->next; destroy_route_flags(rf); rf = rf_tmp; } } /** * Destroys failure_route_tree_item in shared memory by freing all its memory. * * @param route_tree_item route tree node to be destroyed */ static void destroy_failure_route_tree_item(struct failure_route_tree_item *route_tree_item) { int i; struct failure_route_rule *rs; struct failure_route_rule *rs_tmp; if (!route_tree_item) { LM_ERR("NULL pointer in parameter\n"); } for (i = 0; i < 10; ++i) { if (route_tree_item->nodes[i] != NULL) { destroy_failure_route_tree_item(route_tree_item->nodes[i]); } } rs = route_tree_item->rule_list; while (rs != NULL) { rs_tmp = rs->next; destroy_failure_route_rule(rs); rs = rs_tmp; } shm_free(route_tree_item); return; } /** * Tries to add a domain to the domain map. If the given domain doesn't * exist, it is added. Otherwise, nothing happens. * * @param domain the domain to be added * * @return values: on success the numerical index of the given domain, * -1 on failure */ int add_domain(const str * domain) { struct route_map * tmp, * prev = NULL; int id = 0; if (!script_routes) { if ((script_routes = shm_malloc(sizeof(struct route_map *))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(script_routes, 0, sizeof(struct route_map *)); } tmp = *script_routes; while (tmp) { if (str_strcmp(&tmp->name, domain) == 0) { return tmp->no; } id = tmp->no + 1; prev = tmp; tmp = tmp->next; } if ((tmp = shm_malloc(sizeof(struct route_map))) == NULL) { LM_ERR("out of shared memory\n"); return -1; } memset(tmp, 0, sizeof(struct route_map)); if (shm_str_dup(&tmp->name, domain) != 0) { LM_ERR("cannot duplicate string\n"); shm_free(tmp); return -1; } tmp->no = id; if (!prev) { *script_routes = tmp; } else { prev->next = tmp; } LM_INFO("domain %.*s has id %i\n", domain->len, domain->s, id); return id; } opensips-2.2.2/modules/carrierroute/route_tree.h000066400000000000000000000123221300170765700220670ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file route_tree.h * @brief Contains the functions to manage routing tree data in a digital tree. */ #ifndef SP_ROUTE_ROUTE_TREE_H #define SP_ROUTE_ROUTE_TREE_H #include "route.h" /** * Adds the given route information to the route tree identified by * route_tree. scan_prefix identifies the number for which the information * is and the rewrite_* parameters define what to do in case of a match. * prob gives the probability with which this rule applies if there are * more than one for a given prefix. * * Note that this is a recursive function. It strips of digits from the * beginning of scan_prefix and calls itself. * * @param route_tree the current route tree node * @param scan_prefix the prefix at the current position * @param flags user defined flags * @param mask mask for user defined flags * @param full_prefix the whole scan prefix * @param max_targets the number of targets * @param prob the weight of the rule * @param rewrite_hostpart the rewrite_host of the rule * @param strip the number of digits to be stripped off userpart before prepending prefix * @param rewrite_local_prefix the rewrite prefix * @param rewrite_local_suffix the rewrite suffix * @param status the status of the rule * @param hash_index the hash index of the rule * @param backup indicates if the route is backed up by another. only useful if status==0, if set, it is the hash value of another rule * @param backed_up an -1-termintated array of hash indices of the route for which this route is backup * @param comment a comment for the route rule * * @return 0 on success, -1 on failure * * @see add_route() */ int add_route_to_tree(struct route_tree_item * route_tree, const str * scan_prefix, flag_t flags, flag_t mask, const str * full_prefix, int max_targets, double prob, const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix, const str * rewrite_local_suffix, int status, int hash_index, int backup, int * backed_up, const str * comment); /** * Adds the given failure route information to the failure route tree identified by * route_tree. scan_prefix, host, reply_code, flags identifies the number for which * the information is and the next_domain parameters defines where to continue * routing in case of a match. * * Note that this is a recursive function. It strips of digits from the * beginning of scan_prefix and calls itself. * * @param failure_tree the current route tree node * @param scan_prefix the prefix at the current position * @param full_prefix the whole scan prefix * @param host the hostname last tried * @param reply_code the reply code * @param flags user defined flags * @param mask mask for user defined flags * @param next_domain continue routing with this domain id * @param comment a comment for the route rule * * @return 0 on success, -1 on failure * * @see add_route() */ int add_failure_route_to_tree(struct failure_route_tree_item * failure_tree, const str * scan_prefix, const str * full_prefix, const str * host, const str * reply_code, const flag_t flags, const flag_t mask, const int next_domain, const str * comment); /** * Create a new route tree root in shared memory and set it up. * * @param domain the domain name of the route tree * @param id the domain id of the route tree * * @return a pointer to the newly allocated route tree or NULL on * error, in which case it LOGs an error message. */ struct route_tree * create_route_tree(const str * domain, int id); /** * Tries to add a domain to the domain map. If the given domain doesn't * exist, it is added. Otherwise, nothing happens. * * @param domain the domain to be added * * @return values: on success the numerical index of the given domain, * -1 on failure */ int add_domain(const str * domain); /** * returns the routing tree for the given domain, if domain's tree doesn't * exist, it will be created. If the trees are completely filled and a not * existing domain shall be added, an error is returned. * * @param domain the domain name of desired routing tree * @param rd route data to be searched * * @return a pointer to the desired routing tree, NULL on failure */ struct route_tree * get_route_tree(const str * domain, struct carrier_tree * rd); struct route_tree * get_route_tree_by_id(struct carrier_tree * ct, int id); void destroy_route_tree(struct route_tree *route_tree); void destroy_route_map(void); #endif opensips-2.2.2/modules/cfgutils/000077500000000000000000000000001300170765700166535ustar00rootroot00000000000000opensips-2.2.2/modules/cfgutils/Makefile000066400000000000000000000003251300170765700203130ustar00rootroot00000000000000# $Id$ # # cfgutils module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=cfgutils.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/cfgutils/README000066400000000000000000000620401300170765700175350ustar00rootroot00000000000000cfgutils Module Henning Westerholt 1und1 Internet AG Carsten Bock BASIS AudioNet GmbH Elena-Ramona Modroiu rosdev.ro Sergio Gutierrez Copyright © 2007, 2008 1und1 Internet AG, BASIS AudioNet GmbH, Elena-Ramona Modroiu Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.3. Exported Parameters 1.3.1. initial_probability (string) 1.3.2. hash_file (string) 1.3.3. shvset (string) 1.3.4. varset (string) 1.3.5. lock_pool_size (integer) 1.4. Exported Functions 1.4.1. rand_event([probability]) 1.4.2. rand_set_prob(probabiltiy) 1.4.3. rand_reset_prob() 1.4.4. rand_get_prob() 1.4.5. sleep(time) 1.4.6. usleep(time) 1.4.7. abort() 1.4.8. pkg_status() 1.4.9. shm_status() 1.4.10. set_count(pvar name, result pvar name) 1.4.11. set_select_weight(pseudovarible_name) 1.4.12. ts_usec_delta(t1_sec, t1_usec, t2_sec, t2_usec, delta) 1.4.13. check_time_rec(time_string) 1.4.14. get_static_lock(string) 1.4.15. release_static_lock(string) 1.4.16. get_dynamic_lock(string) 1.4.17. release_dynamic_lock(string) 1.4.18. strings_share_lock(string1, string2) 1.5. Exported Asyncronous Functions 1.5.1. sleep(seconds) 1.5.2. usleep(seconds) 1.6. MI Commands 1.6.1. rand_set_prop 1.6.2. rand_reset_prob 1.6.3. rand_get_prob 1.6.4. check_config_hash 1.6.5. get_config_hash 1.6.6. shv_set 1.6.7. shv_get 1.7. Exported pseudo-variables 1.7.1. $env(name) 1.7.2. $RANDOM 1.7.3. $ctime(name) 1.7.4. $shv(name) List of Examples 1.1. initial_probability parameter usage 1.2. hash_file parameter usage 1.3. shvset parameter usage 1.4. varset parameter usage 1.5. Setting lock_pool_size module parameter 1.6. rand_event() usage 1.7. rand_set_prob() usage 1.8. rand_reset_prob() usage 1.9. rand_get_prob() usage 1.10. sleep usage 1.11. usleep usage 1.12. abort usage 1.13. pkg_status usage 1.14. shm_status usage 1.15. set_count usage 1.16. set_select_weight usage 1.17. ts_usec_delta usage 1.18. check_time_rec usage 1.19. get_static_lock usage 1.20. release_static_lock usage 1.21. get_dynamic_lock usage 1.22. release_dynamic_lock usage 1.23. strings_share_lock usage 1.24. async sleep usage 1.25. async usleep usage 1.26. rand_set_prob usage 1.27. rand_reset_prob usage 1.28. rand_get_prob usage 1.29. check_config_hash usage 1.30. get_config_hash usage 1.31. shv_set usage 1.32. shv_get usage 1.33. env(name) pseudo-variable usage 1.34. RANDOM pseudo-variable usage 1.35. ctime(name) pseudo-variable usage 1.36. shv(name) pseudo-variable usage Chapter 1. Admin Guide 1.1. Overview Useful extensions for the server configuration. The cfgutils module can be used to introduce randomness to the behaviour of the server. It provides setup functions and the “rand_event†function. This function return either true or false, depending on a random value and a specified probability. E.g. if you set via fifo or script a probability value of 5%, then 5% of all calls to rand_event will return false. The pseudovariable “$RANDOM†could be used to introduce random values e.g. into a SIP reply. The benefit of this module is the probability of the decision can be manipulated by external applications such as web interface or command line tools. The probability must be specified as percent value, ranging from 0 to 100. The module exports commands to FIFO server that can be used to change the global settings via FIFO interface. The FIFO commands are: “set_probâ€, “reset_prob†and “get_probâ€. This module can be used for simple load-shedding, e.g. reply 5% of the Invites with a 503 error and a adequate random Retry-After value. The module provides as well functions to delay the execution of the server. The functions “sleep†and “usleep†could be used to let the server wait a specific time interval. It can also hash the config file used from the server with a (weak) cryptographic hash function on startup. This value is saved and can be later compared to the actual hash, to detect modifications of this file after the server start. This functions are available as the FIFO commands “check_config_hash†and “get_config_hashâ€. 1.2. Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): * none 1.3. Exported Parameters 1.3.1. initial_probability (string) The initial value of the probability. Default value is “10â€. Example 1.1. initial_probability parameter usage modparam("cfgutils", "initial_probability", 15) 1.3.2. hash_file (string) The config file name for that a hash value should be calculated on startup. There is no default value, is no parameter is given the hash functionality is disabled. Example 1.2. hash_file parameter usage modparam("cfgutils", "hash_file", "/etc/opensips/opensips.cfg") 1.3.3. shvset (string) Set the value of a shared variable ($shv(name)). The parameter can be set many times. The value of the parameter has the format: _name_ '=' _type_ ':' _value_ * _name_: shared variable name * _type_: type of the value + “iâ€: integer value + “sâ€: string value * _value_: value to be set Default value is “NULLâ€. Example 1.3. shvset parameter usage ... modparam("cfgutils", "shvset", "debug=i:1") modparam("cfgutils", "shvset", "pstngw=s:sip:10.10.10.10") ... 1.3.4. varset (string) Set the value of a script variable ($var(name)). The parameter can be set many times. The value of the parameter has the format: _name_ '=' _type_ ':' _value_ * _name_: shared variable name * _type_: type of the value + “iâ€: integer value + “sâ€: string value * _value_: value to be set Default value is “NULLâ€. Example 1.4. varset parameter usage ... modparam("cfgutils", "varset", "init=i:1") modparam("cfgutils", "varset", "gw=s:sip:11.11.11.11;transport=tcp") ... 1.3.5. lock_pool_size (integer) The number of dynamic script locks to be allocated at OpenSIPS startup. This number must be a power of 2. (i.e. 1, 2, 4, 8, 16, 32, 64 ...) Note that the lock_pool_size parameter only affects the number of dynamic locks created at startup. The pool of static locks only depends on the number of unique static strings supplied throughout the script to the set of static lock functions. Default value is “32â€. Example 1.5. Setting lock_pool_size module parameter modparam("cfgutils", "lock_pool_size", "64") 1.4. Exported Functions 1.4.1. rand_event([probability]) Return true or false, depending on a random value and a probability value. If probability parameter is given, it will override the global parameter set by rand_set_prob() function. Example 1.6. rand_event() usage ... if (rand_event()) { append_to_reply("Retry-After: 120\n"); sl_send_reply("503", "Try later"); exit; }; # normal message processing follows ... 1.4.2. rand_set_prob(probabiltiy) Set the “probability†of the decision. “probability†can have a value from the range 0..99. Example 1.7. rand_set_prob() usage ... rand_set_prob("4"); ... 1.4.3. rand_reset_prob() Reset the probability back to the inital value. Example 1.8. rand_reset_prob() usage ... rand_reset_prob(); ... 1.4.4. rand_get_prob() Return the current probability setting, e.g. for logging purposes. Example 1.9. rand_get_prob() usage ... rand_get_prob(); 1.4.5. sleep(time) Waits "time" seconds. Meaning of the parameters is as follows: * time - Time to wait in seconds. String may be a pseudovariable. In case that variable does not contain a numerical value, it is evaluated to zero seconds. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.10. sleep usage ... sleep("1"); ... $avp(secs)="10"; sleep("$avp(secs)"); ... 1.4.6. usleep(time) Waits "time" micro-seconds. Meaning of the parameters is as follows: * time - Time to wait in micro-seconds. The string may contain a pseudovariable. In case that pseudovar does not contain a numerical value, it is evaluated to zero seconds. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.11. usleep usage ... usleep("500000"); # sleep half of sec ... 1.4.7. abort() Debugging function that aborts the server. Depending on the configuration of the server a core dump will be created. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.12. abort usage ... abort(); ... 1.4.8. pkg_status() Debugging function that dumps the status for the private (PKG) memory. This information is logged to the default log facility, depending on the general log level and the memlog setting. You need to compile the server with activated memory debugging to get detailed informations. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.13. pkg_status usage ... pkg_status(); ... 1.4.9. shm_status() Debugging function that dumps the status for the shared (SHM) memory. This information is logged to the default log facility, depending on the general log level and the memlog setting. You need to compile the server with activated memory debugging to get detailed informations. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.14. shm_status usage ... shm_status(); ... 1.4.10. set_count(pvar name, result pvar name) Function that counts the values of a pseudovariable. It makes sense to call this function only for pseudovariables that can take more values (avp, headers). The result is returned in the second parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.15. set_count usage ... set_count("$avp(10)", "$avp(result)"); ... 1.4.11. set_select_weight(pseudovarible_name) This function selects an element from a set formed by the values of the pseudovariable name given as parameter. It applies the genetic algorithm - roulette-wheel selection to choose an element from a set. The probability of selecting a certain element is proportionate with its weight. It will return the index of that selected element. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.16. set_select_weight usage ... $avp(21) = set_select_weight("$avp(10)"); ... 1.4.12. ts_usec_delta(t1_sec, t1_usec, t2_sec, t2_usec, delta) This function returns the difference between two timestamps, specified in seconds and microseconds. The result is returned in the last parameter, expressed in microseconds. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.17. ts_usec_delta usage ... ts_usec_delta("$avp(10)", "$avp(20)", "10", "300", "$avp(result)"); ... 1.4.13. check_time_rec(time_string) The function returns a positive value if the specified time recurrence string matches the current time, or a negative value otherwise. The syntax of each field is identical to the corresponding field from RFC 2445. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE. Meaning of the parameters is as follows: * time_string - Time recurrence string which will be matched against the current time. Its fields are separated by "|" and the order in which they are given is: "dtstart | dtend | duration | freq | until | interval | byday | bymday | byyday | byweekno | bymonth". None of the fields following "freq" is used unless "freq" is defined. If the string ends in multiple null fields, they can all be ommited. The time_string parameter can have the following types: + string - the time recurrence string is statically assigned + pvar - the string is the value of an existing pseudo-variable (as string value) Example 1.18. check_time_rec usage ... # Only passing if still in 2012 if (check_time_rec("20120101T000000|20121231T235959")) { xlog("Current time matches the given interval\n"); } ... # Only passing if less than 30 days have passed from "dtstart" if (check_time_rec("20121101T000000||p30d")) { xlog("Current time matches the given interval\n"); } ... 1.4.14. get_static_lock(string) The function obtains the index of a specific lock (predetermined in the fixup phase of the OpenSIPS script) and attempts to acquire it. In case the lock is taken by another process, script execution will halt until the lock is released. Attempting to acquire the lock a second time by the same process, without releasing it first, will result in a deadlock. The static lock functions guarantee that two different strings will never point to the same lock, thus avoiding introducing unnecessary (and transparent!) synchronization between processes. Their disadvantage is the nature of their parameters (static strings), making them inappropriate in certain scenarios. Meaning of the parameters is as follows: * string - String to be used in order to obtain the index of a static lock. The string parameter can have the following types: + string - the string is statically assigned This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE. Example 1.19. get_static_lock usage # acquire and release a static lock ... get_static_lock("Zone_1"); ... release_static_lock("Zone_1"); ... 1.4.15. release_static_lock(string) The function obtains the index of a specific lock (predetermined in the fixup phase of the OpenSIPS script) and releases it. Nothing will happen if the lock is not acquired. Meaning of the parameters is as follows: * string - String to be used in order to obtain the index of a static lock. The string parameter can have the following types: + string - the string is statically assigned This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE. Example 1.20. release_static_lock usage # acquire and release a static lock ... get_static_lock("Zone_1"); ... release_static_lock("Zone_1"); ... 1.4.16. get_dynamic_lock(string) The function obtains the index of a lock from the pool by performing a hash function on the given variable string and attempts to acquire it. In case the lock is taken by another process, script execution will halt until the lock is released. Attempting to acquire the lock a second time by the same process, without releasing it first, will result in a deadlock. The dynamic lock functions have the advantage of allowing string pseudo-variables to be given as parameters, but the drawback to this is that two strings may have the same hashed value, thus pointing to the same lock. As a consequence, either two totally separate regions of the script will be synchronized (they will not execute in parallel), or a process could end up in a deadlock by acquiring two locks in a row on two different (but equally hashed) strings. To address the latter issue, use the strings_share_lock() function to test if two strings generate equal hash values. Meaning of the parameters is as follows: * string - String to be hashed in order to obtain the index of a lock from the pool. The string parameter can have the following types: + pvar - the string is the value of an existing pseudo-variable (as string value) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE. Example 1.21. get_dynamic_lock usage ... # acquire and release a dynamic lock on the "Call-ID" Header of a SIP me ssage if (!get_dynamic_lock("$ci")) { xlog("Error while getting dynamic lock!\n"); } ... if (!release_dynamic_lock("$ci") { xlog("Error while releasing dynamic lock!\n"); } ... 1.4.17. release_dynamic_lock(string) The function obtains the index of a lock from the pool by performing a hash function on the given variable string and releases it. Nothing will happen if the lock is not acquired. Meaning of the parameters is as follows: * string - String to be hashed in order to obtain the index of a lock from the pool. The string parameter can have the following types: + pvar - the string is the value of an existing pseudo-variable (as string value) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE. Example 1.22. release_dynamic_lock usage ... # acquire and release a dynamic lock on the "Call-ID" Header of a SIP me ssage if (!get_dynamic_lock("$ci")) { xlog("Error while getting dynamic lock!\n"); } ... if (!release_dynamic_lock("$ci") { xlog("Error while releasing dynamic lock!\n"); } ... 1.4.18. strings_share_lock(string1, string2) A function used to test if two strings will generate the same hash value. Its purpose is to prevent deadlocks resulted when a process successively acquires two dynamic locks on two strings which happen to point to the same lock. Theoretically, the chance of two strings generating the same hash value decreases proportionally to the increase of the lock_pool_size parameter. In other words, the more dynamic locks you configure the module with, the higher the chance that all individual protected regions of your script will run in parallel, without waiting for each other. Meaning of the parameters is as follows: * string1, string2 - Strings which will have their hash values compared. The string parameters can have the following types: + string - statically assigned + pvar - values of existing pseudo-variables (as string values) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE. Example 1.23. strings_share_lock usage ... # Proper way of acquiring two dynamic locks successively if (!get_dynamic_lock("$avp(1)")) { xlog("Error while getting dynamic lock!\n"); } if (!strings_share_lock("$avp(1)", "$avp(2)") { if (!get_dynamic_lock("$avp(2)")) { xlog("Error while getting dynamic lock!\n"); } } ... if (!strings_share_lock("$avp(1)", "$avp(2)") { if (!release_dynamic_lock("$avp(2)") { xlog("Error while releasing dynamic lock!\n"); } } if (!release_dynamic_lock("$avp(1)") { xlog("Error while releasing dynamic lock!\n"); } ... 1.5. Exported Asyncronous Functions 1.5.1. sleep(seconds) Waits a number of seconds. This function does exactly the same as Section 1.4.5, “ sleep(time) â€, but in an asynchronous way. The script execution is suspended until the waiting is done; then OpenSIPS resumes the script execution via the resume route. To read and understand more on the asynchronous functions, how to use them and what are their advantages, please refer to the OpenSIPS online Manual. Example 1.24. async sleep usage { ... async( sleep("5"), after_sleep ); } route[after_sleep] { ... } 1.5.2. usleep(seconds) Waits a number of micro-seconds. This function does exactly the same as Section 1.4.6, “ usleep(time) â€, but in an asynchronous way. The script execution is suspended until the waiting is done; then OpenSIPS resumes the script execution via the resume route. To read and understand more on the asynchronous functions, how to use them and what are their advantages, please refer to the OpenSIPS online Manual. Example 1.25. async usleep usage { ... async( usleep("1000"), after_usleep ); } route[after_usleep] { ... } 1.6. MI Commands 1.6.1. rand_set_prop Set the probability value to the given parameter. The parameter should be a percent value. The parameter value must be a number from 0 to 99. Example 1.26. rand_set_prob usage ... $ opensipsctl fifo rand_set_prob 10 ... 1.6.2. rand_reset_prob Reset the probability value to the inital start value. This command don't need a parameter. Example 1.27. rand_reset_prob usage ... $ opensipsctl fifo rand_reset_prob ... 1.6.3. rand_get_prob Return the actual probability setting. The function return the actual probability value. Example 1.28. rand_get_prob usage ... $ opensipsctl fifo get_prob The actual probability is 50 percent. ... 1.6.4. check_config_hash Check if the actual config file hash is identical to the stored one. The function returns 200 OK if the hash values are identical, 400 if there are not identical, 404 if no file for hashing has been configured and 500 on errors. Additional a short text message is printed. Example 1.29. check_config_hash usage ... $ opensipsctl fifo check_config_hash The actual config file hash is identical to the stored one. ... 1.6.5. get_config_hash Return the stored config file hash. The function returns 200 OK and the hash value on success or 404 if no file for hashing has been configured. Example 1.30. get_config_hash usage ... $ opensipsctl fifo get_config_hash 1580a37104eb4de69ab9f31ce8d6e3e0 ... 1.6.6. shv_set Set the value of a shared variable ($shv(name)). Parameters: * _name_: shared variable name * _type_: type of the value + “intâ€: integer value + “strâ€: string value * _value_: value to be set MI FIFO Command Format: :shv_set:_reply_fifo_file_ _name_ _type_ _value_ _empty_line_ Example 1.31. shv_set usage ... $ opensipsctl fifo shv_set debug int 0 ... 1.6.7. shv_get Get the value of a shared variable ($shv(name)). Parameters: * _name_: shared variable name. If this parameter is missing, all shared variables are returned. MI FIFO Command Format: :shv_get:_reply_fifo_file_ _name_ _empty_line_ Example 1.32. shv_get usage ... $ opensipsctl fifo shv_get debug $ opensipsctl fifo shv_get ... 1.7. Exported pseudo-variables 1.7.1. $env(name) This PV provides access to the environment variable 'name'. Example 1.33. env(name) pseudo-variable usage ... xlog("PATH environment variable is $env(PATH)\n"); ... 1.7.2. $RANDOM Returns a random value from the [0 - 2^31) range. Example 1.34. RANDOM pseudo-variable usage ... $avp(10) = ($RANDOM / 16777216); # 2^24 if ($avp(10) < 10) { $avp(10) = 10; } append_to_reply("Retry-After: $avp(10)\n"); sl_send_reply("503", "Try later"); exit; # normal message processing follows 1.7.3. $ctime(name) The PV provides access to broken-down time attributes. The “name†can be: * sec - return seconds (int 0-59) * min - return minutes (int 0-59) * hour - return hours (int 0-23) * mday - return the day of month (int 0-59) * mon - return the month (int 1-12) * year - return the year (int, e.g., 2008) * wday - return the day of week (int, 1=Sunday - 7=Saturday) * yday - return the day of year (int, 1-366) * isdst - return daylight saving time status (int, 0 - DST off, >0 DST on) Example 1.35. ctime(name) pseudo-variable usage ... if ($ctime(year) == 2008) { xlog("request: $rm from $fu to $ru in year 2008\n"); } ... 1.7.4. $shv(name) It is a class of pseudo-variables stored in shared memory. The value of $shv(name) is visible across all opensips processes. Each “shv†has single value and it is initialized to integer 0. You can use “shvset†parameter to initialize the shared variable. The module exports a set of MI functions to get/set the value of shared variables. Example 1.36. shv(name) pseudo-variable usage ... modparam("cfgutils", "shvset", "debug=i:1") ... if ($shv(debug) == 1) { xlog("request: $rm from $fu to $ru\n"); } ... opensips-2.2.2/modules/cfgutils/cfgutils.c000066400000000000000000000630221300170765700206420ustar00rootroot00000000000000/* * Copyright (C) 2007 1&1 Internet AG * Copyright (C) 2007 BASIS AudioNet GmbH * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-22 initial module created (Henning Westerholt) * 2007-03-29 adaption to opensips 1.2 and some cleanups * 2007-04-20 rename to cfgutils, use pseudovariable for get_random_val * add "rand_" prefix, add sleep and usleep functions * 2008-12-26 pseudovar argument for sleep and usleep functions (saguti). * 2012-11-21 added script locks (Liviu) * * cfgutils module: random probability functions for opensips; * it provide functions to make a decision in the script * of the server based on a probability function. * The benefit of this module is the value of the probability function * can be manipulated by external applications such as web interface * or command line tools. * Furthermore it provides some functions to let the server wait a * specific time interval. * */ #include #ifdef __OS_linux #include /* for GLIBC version testing */ #endif #include "../../sr_module.h" #include "../../error.h" #include "../../pvar.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "../../mod_fix.h" #include "../../md5utils.h" #include "../../globals.h" #include "../../time_rec.h" #include "../../timer.h" #include "shvar.h" #include "env_var.h" #include "script_locks.h" #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 8) #include /* for timer FD */ #define HAVE_TIMER_FD 1 #else #ifdef __OS_linux #warning Your GLIB is too old, disabling async sleep functions!!! #endif #endif /* FIFO action protocol names */ #define FIFO_SET_PROB "rand_set_prob" #define FIFO_RESET_PROB "rand_reset_prob" #define FIFO_GET_PROB "rand_get_prob" #define FIFO_GET_HASH "get_config_hash" #define FIFO_CHECK_HASH "check_config_hash" static int set_prob(struct sip_msg*, char *, char *); static int reset_prob(struct sip_msg*, char *, char *); static int get_prob(struct sip_msg*, char *, char *); static int rand_event(struct sip_msg*, char *, char *); static int m_sleep(struct sip_msg*, char *, char *); static int m_usleep(struct sip_msg*, char *, char *); static int dbg_abort(struct sip_msg*, char*,char*); static int dbg_pkg_status(struct sip_msg*, char*,char*); static int dbg_shm_status(struct sip_msg*, char*,char*); static int pv_set_count(struct sip_msg*, char*,char*); static int pv_sel_weight(struct sip_msg*, char*,char*); static struct mi_root* mi_set_prob(struct mi_root* cmd, void* param ); static struct mi_root* mi_reset_prob(struct mi_root* cmd, void* param ); static struct mi_root* mi_get_prob(struct mi_root* cmd, void* param ); static struct mi_root* mi_get_hash(struct mi_root* cmd, void* param ); static struct mi_root* mi_check_hash(struct mi_root* cmd, void* param ); static int pv_get_random_val(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int ts_usec_delta(struct sip_msg *msg, char *_t1s, char *_t1u, char *_t2s, char *_t2u, char *_res); static int check_time_rec(struct sip_msg*, char *); #ifdef HAVE_TIMER_FD static int async_sleep(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char *duration); static int async_usleep(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char *duration); #endif static int fixup_prob( void** param, int param_no); static int fixup_pv_set(void** param, int param_no); static int fixup_rand_event(void** param, int param_no); static int fixup_delta(void** param, int param_no); static int mod_init(void); static void mod_destroy(void); static int initial = 10; static int *probability; static char config_hash[MD5_LEN]; static char* hash_file = NULL; int lock_pool_size = 32; static cmd_export_t cmds[]={ {"rand_set_prob", /* action name as in scripts */ (cmd_function)set_prob, /* C function name */ 1, /* number of parameters */ fixup_prob, 0, /* */ /* can be applied to original/failed requests and replies */ REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"rand_reset_prob", (cmd_function)reset_prob, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"rand_get_prob", (cmd_function)get_prob, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"rand_event", (cmd_function)rand_event, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"rand_event", (cmd_function)rand_event, 1, fixup_rand_event, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"sleep", (cmd_function)m_sleep, 1, fixup_spve_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"usleep", (cmd_function)m_usleep, 1, fixup_spve_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"abort", (cmd_function)dbg_abort, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"pkg_status", (cmd_function)dbg_pkg_status, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"shm_status", (cmd_function)dbg_shm_status, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"set_count", (cmd_function)pv_set_count, 2, fixup_pv_set, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"set_select_weight",(cmd_function)pv_sel_weight,1, fixup_pv_set, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"ts_usec_delta", (cmd_function)ts_usec_delta, 5, fixup_delta, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"get_static_lock",(cmd_function)get_static_lock, 1, fixup_static_lock, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"check_time_rec", (cmd_function)check_time_rec, 1, fixup_sgp_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"release_static_lock",(cmd_function)release_static_lock, 1, fixup_static_lock, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE| BRANCH_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"get_dynamic_lock",(cmd_function)get_dynamic_lock, 1, fixup_sgp_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"release_dynamic_lock",(cmd_function)release_dynamic_lock, 1, fixup_sgp_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE| BRANCH_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"strings_share_lock",(cmd_function)strings_share_lock, 2, fixup_sgp_sgp, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE| BRANCH_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {0, 0, 0, 0, 0, 0} }; static acmd_export_t acmds[] = { #ifdef HAVE_TIMER_FD {"sleep", (acmd_function)async_sleep, 1, fixup_spve_null }, {"usleep", (acmd_function)async_usleep, 1, fixup_spve_null }, #endif {0, 0, 0, 0} }; static param_export_t params[]={ {"initial_probability", INT_PARAM, &initial}, {"hash_file", STR_PARAM, &hash_file }, {"shvset", STR_PARAM|USE_FUNC_PARAM, (void*)param_set_shvar }, {"varset", STR_PARAM|USE_FUNC_PARAM, (void*)param_set_var }, {"lock_pool_size", INT_PARAM, &lock_pool_size}, {0,0,0} }; static mi_export_t mi_cmds[] = { { FIFO_SET_PROB, 0, mi_set_prob, 0, 0, 0 }, { FIFO_RESET_PROB, 0, mi_reset_prob, MI_NO_INPUT_FLAG, 0, 0 }, { FIFO_GET_PROB, 0, mi_get_prob, MI_NO_INPUT_FLAG, 0, 0 }, { FIFO_GET_HASH, 0, mi_get_hash, MI_NO_INPUT_FLAG, 0, 0 }, { FIFO_CHECK_HASH, 0, mi_check_hash, MI_NO_INPUT_FLAG, 0, 0 }, { "shv_get", 0, mi_shvar_get, 0, 0, 0 }, { "shv_set" , 0, mi_shvar_set, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; static pv_export_t mod_items[] = { { {"RANDOM", sizeof("RANDOM")-1}, 1000, pv_get_random_val, 0, 0, 0, 0, 0 }, { {"shv", (sizeof("shv")-1)}, 1001, pv_get_shvar, pv_set_shvar, pv_parse_shvar_name, 0, 0, 0}, { {"ctime", (sizeof("ctime")-1)}, 1002, pv_get_time, 0, pv_parse_time_name, 0, 0, 0}, { {"env", (sizeof("env")-1)}, 1002, pv_get_env, 0, pv_parse_env_name, 0, 0, 0}, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; struct module_exports exports = { "cfgutils", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ acmds, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ mod_destroy, /* destroy function */ 0 /* per-child init function */ }; /**************************** fixup functions ******************************/ static int fixup_prob( void** param, int param_no) { unsigned int myint = 0; str param_str; /* we only fix the parameter #1 */ if (param_no!=1) return 0; param_str.s=(char*) *param; param_str.len=strlen(param_str.s); str2int(¶m_str, &myint); if (myint > 100) { LM_ERR("invalid probability <%d>\n", myint); return E_CFG; } pkg_free(*param); *param=(void *)(long)myint; return 0; } static int fixup_delta( void **param, int param_no) { if (param_no < 5) { return fixup_igp(param); } else if (param_no == 5) { if (fixup_pvar(param) < 0 && ((pv_spec_p)*param)->setf == 0) { LM_ERR("invalid pvar\n"); return E_SCRIPT; } return 0; } else { return E_UNSPEC; } } /************************** module functions **********************************/ static struct mi_root* mi_set_prob(struct mi_root* cmd, void* param ) { unsigned int percent; struct mi_node* node; node = cmd->node.kids; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if( str2int( &node->value, &percent) <0) goto error; if (percent > 100) { LM_ERR("incorrect probability <%u>\n", percent); goto error; } *probability = percent; return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } static struct mi_root* mi_reset_prob(struct mi_root* cmd, void* param ) { *probability = initial; return init_mi_tree( 200, MI_OK_S, MI_OK_LEN ); } static struct mi_root* mi_get_prob(struct mi_root* cmd, void* param ) { struct mi_root* rpl_tree= NULL; struct mi_node* node= NULL; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN ); if(rpl_tree == NULL) return 0; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "actual probability: %u percent\n",(*probability)); if(node == NULL) goto error; return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } static struct mi_root* mi_get_hash(struct mi_root* cmd, void* param ) { struct mi_root* rpl_tree= NULL; struct mi_node* node= NULL; if (!hash_file) { LM_INFO("no hash_file given, disable hash functionality\n"); rpl_tree = init_mi_tree(404, "Functionality disabled\n", 23); } else { rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN ); if(rpl_tree == NULL) return 0; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%.*s\n", MD5_LEN, config_hash); if(node == NULL) goto error; } return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } static struct mi_root* mi_check_hash(struct mi_root* cmd, void* param ) { struct mi_root* rpl_tree= NULL; struct mi_node* node= NULL; char tmp[MD5_LEN]; memset(tmp, 0, MD5_LEN); if (!hash_file) { LM_INFO("no hash_file given, disable hash functionality\n"); rpl_tree = init_mi_tree(404, "Functionality disabled\n", 23); } else { if (MD5File(tmp, hash_file) != 0) { LM_ERR("could not hash the config file"); rpl_tree = init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN ); } if (strncmp(config_hash, tmp, MD5_LEN) == 0) { rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN ); if(rpl_tree == NULL) return 0; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "The actual config file hash is identical to the stored one.\n"); } else { rpl_tree = init_mi_tree( 400, "Error", 5 ); if(rpl_tree == NULL) return 0; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "The actual config file hash is not identical to the stored one.\n"); } if(node == NULL) goto error; } return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } static int set_prob(struct sip_msg *bar, char *percent_par, char *foo) { *probability=(int)(long)percent_par; return 1; } static int reset_prob(struct sip_msg *bar, char *percent_par, char *foo) { *probability=initial; return 1; } static int get_prob(struct sip_msg *bar, char *foo1, char *foo2) { return *probability; } static int fixup_rand_event(void** param, int param_no) { pv_elem_t *model; str s; if(param_no== 0) return 0; if(*param) { s.s = (char*)(*param); s.len = strlen(s.s); if(pv_parse_format(&s, &model)<0) { LM_ERR( "wrong format[%s]\n",(char*)(*param)); return E_UNSPEC; } *param = (void*)model; return 0; } LM_ERR( "null format\n"); return E_UNSPEC; } static int rand_event(struct sip_msg *bar, char *prob_param, char *foo2) { double tmp = ((double) rand() / RAND_MAX); int prob = *probability; str pr; LM_DBG("generated random %f\n", tmp); LM_DBG("my pid is %d\n", getpid()); if (prob_param) { if (((pv_elem_p)prob_param)->spec.getf!=NULL) { if(pv_printf_s(bar, (pv_elem_p)prob_param, &pr)!=0 || pr.len <=0) return -1; } else { pr = ((pv_elem_p)prob_param)->text; } if (str2sint(&pr, &prob) < 0) { LM_ERR("invalid probability <%.*s>\n", pr.len, pr.s); return -1; } LM_DBG("new probability is %d\n", prob); } if (tmp < ((double) (prob) / 100)) { LM_DBG("return true\n"); return 1; } else { LM_DBG("return false\n"); return -1; } } static int pv_get_random_val(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int n; int l = 0; char *ch; if(msg==NULL || res==NULL) return -1; n = rand(); ch = int2str(n , &l); res->rs.s = ch; res->rs.len = l; res->ri = n; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } static int m_sleep(struct sip_msg *msg, char *time, char *str2) { str time_s={NULL,0}; long seconds; if(time == NULL || fixup_get_svalue(msg, (gparam_p)time, &time_s)!=0) { LM_ERR("Invalid time argument\n"); return -1; } seconds = atol(time_s.s); LM_DBG("sleep %d\n", (unsigned int)seconds); sleep((unsigned int)seconds); return 1; } static int m_usleep(struct sip_msg *msg, char *time, char *str2) { str time_s= { NULL, 0 }; long useconds; if(time == NULL || fixup_get_svalue(msg, (gparam_p)time, &time_s) != 0) { LM_ERR("Invalid useconds argument.\n"); return -1; } useconds = atol(time_s.s); LM_DBG("sleep %d\n", (unsigned int)useconds); sleep_us((unsigned int)useconds); return 1; } #ifdef HAVE_TIMER_FD int resume_async_sleep(int fd, struct sip_msg *msg, void *param) { unsigned long now = (unsigned long) (((unsigned long)-1) & get_uticks()); /* apply a sync correction if (for whatever reasons) the sleep * did not cover the whole interval so far */ if ( ((unsigned long)param) > (now+UTIMER_TICK) ) sleep_us((unsigned int)((unsigned long)param - now)); close (fd); async_status = ASYNC_DONE; return 1; } static int async_sleep(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char *time) { str time_s={NULL,0}; unsigned int seconds; struct itimerspec its; int fd; if(time == NULL || fixup_get_svalue(msg, (gparam_p)time, &time_s)!=0) { LM_ERR("Invalid time argument\n"); return -1; } if ( str2int( &time_s, &seconds) != 0 ) { LM_ERR("time to sleep <%.*s> is not integer\n", time_s.len,time_s.s); return -1; } LM_DBG("sleep %d seconds\n", seconds); /* create the timer fd */ if ( (fd=timerfd_create( CLOCK_REALTIME, 0))<0 ) { LM_ERR("failed to create new timer FD (%d) <%s>\n", errno, strerror(errno)); return -1; } /* set the time */ its.it_value.tv_sec = seconds; its.it_value.tv_nsec = 0; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; if (timerfd_settime( fd, 0, &its, NULL)<0) { LM_ERR("failed to set timer FD (%d) <%s>\n", errno, strerror(errno)); return -1; } /* start the async wait */ *resume_param = (void*)(unsigned long) (((unsigned long)-1) & (get_uticks()+1000000*seconds)); *resume_f = resume_async_sleep; async_status = fd; return 1; } static int async_usleep(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char *time) { str time_s={NULL,0}; unsigned int useconds; struct itimerspec its; int fd; if(time == NULL || fixup_get_svalue(msg, (gparam_p)time, &time_s)!=0) { LM_ERR("Invalid time argument\n"); return -1; } if ( str2int( &time_s, &useconds) != 0 ) { LM_ERR("time to sleep <%.*s> is not integer\n", time_s.len,time_s.s); return -1; } LM_DBG("sleep %d useconds\n", useconds); /* create the timer fd */ if ( (fd=timerfd_create( CLOCK_REALTIME, 0))<0 ) { LM_ERR("failed to create new timer FD (%d) <%s>\n", errno, strerror(errno)); return -1; } /* set the time */ its.it_value.tv_sec = (useconds / 1000000); its.it_value.tv_nsec = (useconds % 1000000) * 1000; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; if (timerfd_settime( fd, 0, &its, NULL)<0) { LM_ERR("failed to set timer FD (%d) <%s>\n", errno, strerror(errno)); return -1; } /* start the async wait */ *resume_param = (void*)(unsigned long) (((unsigned long)-1) & (get_uticks()+useconds)); *resume_f = resume_async_sleep; async_status = fd; return 1; } #endif static int dbg_abort(struct sip_msg* msg, char* foo, char* bar) { LM_CRIT("abort called\n"); abort(); return 0; } static int dbg_pkg_status(struct sip_msg* msg, char* foo, char* bar) { pkg_status(); return 1; } static int dbg_shm_status(struct sip_msg* msg, char* foo, char* bar) { shm_status(); return 1; } static int mod_init(void) { if (!hash_file) { LM_INFO("no hash_file given, disable hash functionality\n"); } else { if (MD5File(config_hash, hash_file) != 0) { LM_ERR("could not hash the config file"); return -1; } LM_DBG("config file hash is %.*s", MD5_LEN, config_hash); } if (initial > 100) { LM_ERR("invalid probability <%d>\n", initial); return -1; } LM_DBG("initial probability %d percent\n", initial); probability=(int *) shm_malloc(sizeof(int)); if (!probability) { LM_ERR("no shmem available\n"); return -1; } *probability = initial; if (lock_pool_size < 1) { LM_ERR("Invalid lock size parameter (%d)!\n", lock_pool_size); return -1; } if (create_dynamic_locks() != 0) { LM_ERR("Failed to create dynamic locks\n"); return -1; } LM_INFO("module initialized, pid [%d]\n", getpid()); return 0; } static void mod_destroy(void) { if (probability) shm_free(probability); shvar_destroy_locks(); destroy_shvars(); destroy_script_locks(); } static int fixup_pv_set(void** param, int param_no) { pv_elem_t *model; str s; if((*param == 0) || (param_no!=1 && param_no!=2)) { LM_ERR( "NULL format\n"); return E_UNSPEC; } s.s = (char*)(*param); s.len = strlen(s.s); if(pv_parse_format(&s, &model)<0) { LM_ERR( "wrong format[%s]\n",(char*)(*param)); return E_UNSPEC; } *param = (void*)model; return 0; } static int pv_set_count(struct sip_msg* msg, char* pv_name, char* pv_result) { pv_elem_t* pv_elem = (pv_elem_t*)pv_name; pv_elem_t* pv_res = (pv_elem_t*)pv_result; pv_value_t pv_val; if(pv_elem == NULL || pv_res == NULL) { LM_ERR("NULL parameter\n"); return -1; } memset(&pv_val, 0, sizeof(pv_value_t)); pv_elem->spec.pvp.pvi.type = PV_IDX_INT; pv_elem->spec.pvp.pvi.u.ival = 0; while(pv_val.flags != PV_VAL_NULL) { if(pv_get_spec_value(msg, &pv_elem->spec, &pv_val) < 0) { LM_ERR("PV get function failed\n"); return -1; } pv_elem->spec.pvp.pvi.u.ival++; } pv_val.flags = PV_TYPE_INT; pv_val.ri = pv_elem->spec.pvp.pvi.u.ival-1; if (pv_set_value( msg, &pv_res->spec, 0, &pv_val) != 0) { LM_ERR("SET output value failed.\n"); return -1; } LM_DBG("Set count = %d\n", pv_val.ri); return 1; } /* This function does selection based on the * fitness proportionate selection also known as roulette-wheel selection*/ static int pv_sel_weight(struct sip_msg* msg, char* pv_name,char* str2) { int size; int *vals = NULL; int sum = 0; int rnd_val; int prev_val; pv_elem_t* pv_elem = (pv_elem_t*)pv_name; pv_value_t pv_val; int i; /* check the value type - it must be int */ if(pv_elem == NULL) { LM_ERR("NULL parameter\n"); return -1; } memset(&pv_val, 0, sizeof(pv_value_t)); pv_elem->spec.pvp.pvi.type = PV_IDX_INT; pv_elem->spec.pvp.pvi.u.ival = 0; while(pv_val.flags != PV_VAL_NULL) { if(pv_get_spec_value(msg, &pv_elem->spec, &pv_val) < 0) { LM_ERR("PV get function failed\n"); return -1; } if((!(pv_val.flags & PV_VAL_INT)) && (pv_val.flags != PV_VAL_NULL)) { LM_ERR("Applied select weight algorithm for a varible set" " containing not only integer values\n"); return -1; } pv_elem->spec.pvp.pvi.u.ival++; } size = pv_elem->spec.pvp.pvi.u.ival - 1; if(size <= 0) return -1; if(size == 1) return 0; vals = (int*)pkg_malloc(size* sizeof(int)); if(vals == NULL) { LM_ERR("No more private memory\n"); return -1; } memset(vals, 0, size*sizeof(int)); for(i= 0; i< size; i++) { pv_elem->spec.pvp.pvi.u.ival = i; if(pv_get_spec_value(msg, &pv_elem->spec, &pv_val) < 0) { LM_ERR("PV get function failed\n"); goto error; } vals[i]= sum + pv_val.ri; sum = vals[i]; } /* generate a random value */ rnd_val = random() % sum; /* find out which segment it belongs to */ prev_val = 0; for(i = 0; i< size; i++) { if(rnd_val >= prev_val && rnd_val < vals[i]) break; prev_val = vals[i]; } LM_DBG("The interval is %d - %d\n", prev_val, vals[i]); pkg_free(vals); return i; error: if(vals) pkg_free(vals); return -1; } #define GET_INT(_msg, _p, _v) \ do { \ if (!(_p) || fixup_get_ivalue((_msg), ((gparam_p)(_p)), &(_v))< 0) { \ LM_ERR("cannot retrieve int value\n"); \ return -1; \ } \ } while (0) static int ts_usec_delta(struct sip_msg *msg, char *_t1s, char *_t1u, char *_t2s, char *_t2u, char *_res) { int t1s, t2s, t1u, t2u; pv_value_t res; GET_INT(msg, _t1s, t1s); GET_INT(msg, _t1u, t1u); GET_INT(msg, _t2s, t2s); GET_INT(msg, _t2u, t2u); res.ri = abs(1000000 * (t1s - t2s) + t1u - t2u); res.flags = PV_TYPE_INT; if (pv_set_value(msg, (pv_spec_p)_res, 0, &res)) { LM_ERR("cannot store result value\n"); return -1; } return 1; } /** * * return values: 1 - match -1 - otherwise */ int check_time_rec(struct sip_msg *msg, char *time_str) { tmrec_p time_rec = 0; char *p, *s; str ret; ac_tm_t att; if (fixup_get_svalue(msg, (gparam_p)time_str, &ret) != 0) { LM_ERR("Get fixup value failed!\n"); return E_CFG; } p = ret.s; LM_INFO("Parsing : %.*s\n", ret.len, ret.s); time_rec = tmrec_new(SHM_ALLOC); if (time_rec==0) { LM_ERR("no more shm mem\n"); goto error; } /* empty definition? */ if ( time_str==0 || *time_str==0 ) return -1; load_TR_value( p, s, time_rec, tr_parse_dtstart, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_dtend, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_duration, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_freq, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_until, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_interval, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_byday, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_bymday, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_byyday, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_byweekno, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_bymonth, parse_error, done); /* success */ LM_DBG("Time rec created\n"); done: /* shortcut: if there is no dstart, timerec is valid */ if (time_rec->dtstart==0) return 1; memset( &att, 0, sizeof(att)); /* set current time */ if ( ac_tm_set_time( &att, time(0) ) ) return -1; /* does the recv_time match the specified interval? */ if (check_tmrec( time_rec, &att, 0)!=0) return -1; return 1; parse_error: LM_ERR("parse error in <%s> around position %i\n", time_str, (int)(long)(p-time_str)); error: if (time_rec) tmrec_free( time_rec ); return -1; } opensips-2.2.2/modules/cfgutils/doc/000077500000000000000000000000001300170765700174205ustar00rootroot00000000000000opensips-2.2.2/modules/cfgutils/doc/cfgutils.xml000066400000000000000000000030041300170765700217570ustar00rootroot00000000000000 %docentities; ]> cfgutils Module Henning Westerholt 1und1 Internet AG henning.westerholt@1und1.de Carsten Bock BASIS AudioNet GmbH
bock@basis-audionet.de
Elena-Ramona Modroiu rosdev.ro ramona@rosdev.ro Sergio Gutierrez saguti@gmail.com
2007 2008 1und1 Internet AG BASIS AudioNet GmbH Elena-Ramona Modroiu $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/cfgutils/doc/cfgutils_admin.xml000066400000000000000000001023351300170765700231360ustar00rootroot00000000000000 &adminguide;
Overview Useful extensions for the server configuration. The cfgutils module can be used to introduce randomness to the behaviour of the server. It provides setup functions and the rand_event function. This function return either true or false, depending on a random value and a specified probability. E.g. if you set via fifo or script a probability value of 5%, then 5% of all calls to rand_event will return false. The pseudovariable $RANDOM could be used to introduce random values e.g. into a SIP reply. The benefit of this module is the probability of the decision can be manipulated by external applications such as web interface or command line tools. The probability must be specified as percent value, ranging from 0 to 100. The module exports commands to FIFO server that can be used to change the global settings via FIFO interface. The FIFO commands are: set_prob, reset_prob and get_prob. This module can be used for simple load-shedding, e.g. reply 5% of the Invites with a 503 error and a adequate random Retry-After value. The module provides as well functions to delay the execution of the server. The functions sleep and usleep could be used to let the server wait a specific time interval. It can also hash the config file used from the server with a (weak) cryptographic hash function on startup. This value is saved and can be later compared to the actual hash, to detect modifications of this file after the server start. This functions are available as the FIFO commands check_config_hash and get_config_hash.
Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): none
Exported Parameters
<varname>initial_probability</varname> (string) The initial value of the probability. Default value is 10. <varname>initial_probability</varname> parameter usage modparam("cfgutils", "initial_probability", 15)
<varname>hash_file</varname> (string) The config file name for that a hash value should be calculated on startup. There is no default value, is no parameter is given the hash functionality is disabled. <varname>hash_file</varname> parameter usage modparam("cfgutils", "hash_file", "/etc/opensips/opensips.cfg")
<varname>shvset</varname> (string) Set the value of a shared variable ($shv(name)). The parameter can be set many times. The value of the parameter has the format: _name_ '=' _type_ ':' _value_ _name_: shared variable name _type_: type of the value i: integer value s: string value _value_: value to be set Default value is NULL. <varname>shvset</varname> parameter usage ... modparam("cfgutils", "shvset", "debug=i:1") modparam("cfgutils", "shvset", "pstngw=s:sip:10.10.10.10") ...
<varname>varset</varname> (string) Set the value of a script variable ($var(name)). The parameter can be set many times. The value of the parameter has the format: _name_ '=' _type_ ':' _value_ _name_: shared variable name _type_: type of the value i: integer value s: string value _value_: value to be set Default value is NULL. <varname>varset</varname> parameter usage ... modparam("cfgutils", "varset", "init=i:1") modparam("cfgutils", "varset", "gw=s:sip:11.11.11.11;transport=tcp") ...
<varname>lock_pool_size</varname> (integer) The number of dynamic script locks to be allocated at &osips; startup. This number must be a power of 2. (i.e. 1, 2, 4, 8, 16, 32, 64 ...) Note that the lock_pool_size parameter only affects the number of dynamic locks created at startup. The pool of static locks only depends on the number of unique static strings supplied throughout the script to the set of static lock functions. Default value is 32. Setting lock_pool_size module parameter modparam("cfgutils", "lock_pool_size", "64")
Exported Functions
<function moreinfo="none">rand_event([probability])</function> Return true or false, depending on a random value and a probability value. If probability parameter is given, it will override the global parameter set by rand_set_prob() function. <function moreinfo="none">rand_event()</function> usage ... if (rand_event()) { append_to_reply("Retry-After: 120\n"); sl_send_reply("503", "Try later"); exit; }; # normal message processing follows ...
<function moreinfo="none">rand_set_prob(probabiltiy)</function> Set the probability of the decision. probability can have a value from the range 0..99. <function moreinfo="none">rand_set_prob()</function> usage ... rand_set_prob("4"); ...
<function moreinfo="none">rand_reset_prob()</function> Reset the probability back to the inital value. <function moreinfo="none">rand_reset_prob()</function> usage ... rand_reset_prob(); ...
<function moreinfo="none">rand_get_prob()</function> Return the current probability setting, e.g. for logging purposes. <function moreinfo="none">rand_get_prob()</function> usage ... rand_get_prob();
<function moreinfo="none">sleep(time)</function> Waits "time" seconds. Meaning of the parameters is as follows: time - Time to wait in seconds. String may be a pseudovariable. In case that variable does not contain a numerical value, it is evaluated to zero seconds. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>sleep</function> usage ... sleep("1"); ... $avp(secs)="10"; sleep("$avp(secs)"); ...
<function moreinfo="none">usleep(time)</function> Waits "time" micro-seconds. Meaning of the parameters is as follows: time - Time to wait in micro-seconds. The string may contain a pseudovariable. In case that pseudovar does not contain a numerical value, it is evaluated to zero seconds. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>usleep</function> usage ... usleep("500000"); # sleep half of sec ...
<function moreinfo="none">abort()</function> Debugging function that aborts the server. Depending on the configuration of the server a core dump will be created. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>abort</function> usage ... abort(); ...
<function moreinfo="none">pkg_status()</function> Debugging function that dumps the status for the private (PKG) memory. This information is logged to the default log facility, depending on the general log level and the memlog setting. You need to compile the server with activated memory debugging to get detailed informations. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>pkg_status</function> usage ... pkg_status(); ...
<function moreinfo="none">shm_status()</function> Debugging function that dumps the status for the shared (SHM) memory. This information is logged to the default log facility, depending on the general log level and the memlog setting. You need to compile the server with activated memory debugging to get detailed informations. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>shm_status</function> usage ... shm_status(); ...
<function moreinfo="none">set_count(pvar name, result pvar name)</function> Function that counts the values of a pseudovariable. It makes sense to call this function only for pseudovariables that can take more values (avp, headers). The result is returned in the second parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>set_count</function> usage ... set_count("$avp(10)", "$avp(result)"); ...
<function moreinfo="none">set_select_weight(pseudovarible_name)</function> This function selects an element from a set formed by the values of the pseudovariable name given as parameter. It applies the genetic algorithm - roulette-wheel selection to choose an element from a set. The probability of selecting a certain element is proportionate with its weight. It will return the index of that selected element. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>set_select_weight</function> usage ... $avp(21) = set_select_weight("$avp(10)"); ...
<function moreinfo="none">ts_usec_delta(t1_sec, t1_usec, t2_sec, t2_usec, delta)</function> This function returns the difference between two timestamps, specified in seconds and microseconds. The result is returned in the last parameter, expressed in microseconds. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>ts_usec_delta</function> usage ... ts_usec_delta("$avp(10)", "$avp(20)", "10", "300", "$avp(result)"); ...
<function moreinfo="none">check_time_rec(time_string)</function> The function returns a positive value if the specified time recurrence string matches the current time, or a negative value otherwise. The syntax of each field is identical to the corresponding field from RFC 2445. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE. Meaning of the parameters is as follows: time_string - Time recurrence string which will be matched against the current time. Its fields are separated by "|" and the order in which they are given is: "dtstart | dtend | duration | freq | until | interval | byday | bymday | byyday | byweekno | bymonth". None of the fields following "freq" is used unless "freq" is defined. If the string ends in multiple null fields, they can all be ommited. The time_string parameter can have the following types: string - the time recurrence string is statically assigned pvar - the string is the value of an existing pseudo-variable (as string value) <function>check_time_rec</function> usage ... # Only passing if still in 2012 if (check_time_rec("20120101T000000|20121231T235959")) { xlog("Current time matches the given interval\n"); } ... # Only passing if less than 30 days have passed from "dtstart" if (check_time_rec("20121101T000000||p30d")) { xlog("Current time matches the given interval\n"); } ...
<function moreinfo="none">get_static_lock(string)</function> The function obtains the index of a specific lock (predetermined in the fixup phase of the OpenSIPS script) and attempts to acquire it. In case the lock is taken by another process, script execution will halt until the lock is released. Attempting to acquire the lock a second time by the same process, without releasing it first, will result in a deadlock. The static lock functions guarantee that two different strings will never point to the same lock, thus avoiding introducing unnecessary (and transparent!) synchronization between processes. Their disadvantage is the nature of their parameters (static strings), making them inappropriate in certain scenarios. Meaning of the parameters is as follows: string - String to be used in order to obtain the index of a static lock. The string parameter can have the following types: string - the string is statically assigned This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE. <function moreinfo="none">get_static_lock</function> usage # acquire and release a static lock ... get_static_lock("Zone_1"); ... release_static_lock("Zone_1"); ...
<function moreinfo="none">release_static_lock(string)</function> The function obtains the index of a specific lock (predetermined in the fixup phase of the OpenSIPS script) and releases it. Nothing will happen if the lock is not acquired. Meaning of the parameters is as follows: string - String to be used in order to obtain the index of a static lock. The string parameter can have the following types: string - the string is statically assigned This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE. <function moreinfo="none">release_static_lock</function> usage # acquire and release a static lock ... get_static_lock("Zone_1"); ... release_static_lock("Zone_1"); ...
<function moreinfo="none">get_dynamic_lock(string)</function> The function obtains the index of a lock from the pool by performing a hash function on the given variable string and attempts to acquire it. In case the lock is taken by another process, script execution will halt until the lock is released. Attempting to acquire the lock a second time by the same process, without releasing it first, will result in a deadlock. The dynamic lock functions have the advantage of allowing string pseudo-variables to be given as parameters, but the drawback to this is that two strings may have the same hashed value, thus pointing to the same lock. As a consequence, either two totally separate regions of the script will be synchronized (they will not execute in parallel), or a process could end up in a deadlock by acquiring two locks in a row on two different (but equally hashed) strings. To address the latter issue, use the strings_share_lock() function to test if two strings generate equal hash values. Meaning of the parameters is as follows: string - String to be hashed in order to obtain the index of a lock from the pool. The string parameter can have the following types: pvar - the string is the value of an existing pseudo-variable (as string value) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE. <function moreinfo="none">get_dynamic_lock</function> usage ... # acquire and release a dynamic lock on the "Call-ID" Header of a SIP message if (!get_dynamic_lock("$ci")) { xlog("Error while getting dynamic lock!\n"); } ... if (!release_dynamic_lock("$ci") { xlog("Error while releasing dynamic lock!\n"); } ...
<function moreinfo="none">release_dynamic_lock(string)</function> The function obtains the index of a lock from the pool by performing a hash function on the given variable string and releases it. Nothing will happen if the lock is not acquired. Meaning of the parameters is as follows: string - String to be hashed in order to obtain the index of a lock from the pool. The string parameter can have the following types: pvar - the string is the value of an existing pseudo-variable (as string value) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE. <function moreinfo="none">release_dynamic_lock</function> usage ... # acquire and release a dynamic lock on the "Call-ID" Header of a SIP message if (!get_dynamic_lock("$ci")) { xlog("Error while getting dynamic lock!\n"); } ... if (!release_dynamic_lock("$ci") { xlog("Error while releasing dynamic lock!\n"); } ...
<function moreinfo="none">strings_share_lock(string1, string2)</function> A function used to test if two strings will generate the same hash value. Its purpose is to prevent deadlocks resulted when a process successively acquires two dynamic locks on two strings which happen to point to the same lock. Theoretically, the chance of two strings generating the same hash value decreases proportionally to the increase of the lock_pool_size parameter. In other words, the more dynamic locks you configure the module with, the higher the chance that all individual protected regions of your script will run in parallel, without waiting for each other. Meaning of the parameters is as follows: string1, string2 - Strings which will have their hash values compared. The string parameters can have the following types: string - statically assigned pvar - values of existing pseudo-variables (as string values) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE. <function moreinfo="none">strings_share_lock</function> usage ... # Proper way of acquiring two dynamic locks successively if (!get_dynamic_lock("$avp(1)")) { xlog("Error while getting dynamic lock!\n"); } if (!strings_share_lock("$avp(1)", "$avp(2)") { if (!get_dynamic_lock("$avp(2)")) { xlog("Error while getting dynamic lock!\n"); } } ... if (!strings_share_lock("$avp(1)", "$avp(2)") { if (!release_dynamic_lock("$avp(2)") { xlog("Error while releasing dynamic lock!\n"); } } if (!release_dynamic_lock("$avp(1)") { xlog("Error while releasing dynamic lock!\n"); } ...
Exported Asyncronous Functions
<function moreinfo="none">sleep(seconds)</function> Waits a number of seconds. This function does exactly the same as , but in an asynchronous way. The script execution is suspended until the waiting is done; then OpenSIPS resumes the script execution via the resume route. To read and understand more on the asynchronous functions, how to use them and what are their advantages, please refer to the OpenSIPS online Manual. <function moreinfo="none">async sleep</function> usage { ... async( sleep("5"), after_sleep ); } route[after_sleep] { ... }
<function moreinfo="none">usleep(seconds)</function> Waits a number of micro-seconds. This function does exactly the same as , but in an asynchronous way. The script execution is suspended until the waiting is done; then OpenSIPS resumes the script execution via the resume route. To read and understand more on the asynchronous functions, how to use them and what are their advantages, please refer to the OpenSIPS online Manual. <function moreinfo="none">async usleep</function> usage { ... async( usleep("1000"), after_usleep ); } route[after_usleep] { ... }
<acronym>MI</acronym> Commands
<function moreinfo="none">rand_set_prop</function> Set the probability value to the given parameter. The parameter should be a percent value. The parameter value must be a number from 0 to 99. <function moreinfo="none">rand_set_prob</function> usage ... $ opensipsctl fifo rand_set_prob 10 ...
<function moreinfo="none">rand_reset_prob</function> Reset the probability value to the inital start value. This command don't need a parameter. <function moreinfo="none">rand_reset_prob</function> usage ... $ opensipsctl fifo rand_reset_prob ...
<function moreinfo="none">rand_get_prob</function> Return the actual probability setting. The function return the actual probability value. <function moreinfo="none">rand_get_prob</function> usage ... $ opensipsctl fifo get_prob The actual probability is 50 percent. ...
<function moreinfo="none">check_config_hash</function> Check if the actual config file hash is identical to the stored one. The function returns 200 OK if the hash values are identical, 400 if there are not identical, 404 if no file for hashing has been configured and 500 on errors. Additional a short text message is printed. <function moreinfo="none">check_config_hash</function> usage ... $ opensipsctl fifo check_config_hash The actual config file hash is identical to the stored one. ...
<function moreinfo="none">get_config_hash</function> Return the stored config file hash. The function returns 200 OK and the hash value on success or 404 if no file for hashing has been configured. <function moreinfo="none">get_config_hash</function> usage ... $ opensipsctl fifo get_config_hash 1580a37104eb4de69ab9f31ce8d6e3e0 ...
<function moreinfo="none">shv_set</function> Set the value of a shared variable ($shv(name)). Parameters: _name_: shared variable name _type_: type of the value int: integer value str: string value _value_: value to be set MI FIFO Command Format: :shv_set:_reply_fifo_file_ _name_ _type_ _value_ _empty_line_ <function moreinfo="none">shv_set</function> usage ... $ opensipsctl fifo shv_set debug int 0 ...
<function moreinfo="none">shv_get</function> Get the value of a shared variable ($shv(name)). Parameters: _name_: shared variable name. If this parameter is missing, all shared variables are returned. MI FIFO Command Format: :shv_get:_reply_fifo_file_ _name_ _empty_line_ <function moreinfo="none">shv_get</function> usage ... $ opensipsctl fifo shv_get debug $ opensipsctl fifo shv_get ...
Exported pseudo-variables
<varname>$env(name)</varname> This PV provides access to the environment variable 'name'. <function moreinfo="none">env(name) pseudo-variable</function> usage ... xlog("PATH environment variable is $env(PATH)\n"); ...
<varname>$RANDOM</varname> Returns a random value from the [0 - 2^31) range. <function moreinfo="none">RANDOM pseudo-variable</function> usage ... $avp(10) = ($RANDOM / 16777216); # 2^24 if ($avp(10) < 10) { $avp(10) = 10; } append_to_reply("Retry-After: $avp(10)\n"); sl_send_reply("503", "Try later"); exit; # normal message processing follows
<varname>$ctime(name)</varname> The PV provides access to broken-down time attributes. The name can be: sec - return seconds (int 0-59) min - return minutes (int 0-59) hour - return hours (int 0-23) mday - return the day of month (int 0-59) mon - return the month (int 1-12) year - return the year (int, e.g., 2008) wday - return the day of week (int, 1=Sunday - 7=Saturday) yday - return the day of year (int, 1-366) isdst - return daylight saving time status (int, 0 - DST off, >0 DST on) <function moreinfo="none">ctime(name) pseudo-variable</function> usage ... if ($ctime(year) == 2008) { xlog("request: $rm from $fu to $ru in year 2008\n"); } ...
<varname>$shv(name)</varname> It is a class of pseudo-variables stored in shared memory. The value of $shv(name) is visible across all opensips processes. Each shv has single value and it is initialized to integer 0. You can use shvset parameter to initialize the shared variable. The module exports a set of MI functions to get/set the value of shared variables. <function moreinfo="none">shv(name) pseudo-variable</function> usage ... modparam("cfgutils", "shvset", "debug=i:1") ... if ($shv(debug) == 1) { xlog("request: $rm from $fu to $ru\n"); } ...
opensips-2.2.2/modules/cfgutils/env_var.c000066400000000000000000000035271300170765700204660ustar00rootroot00000000000000#include #include "../../mem/mem.h" #include "../../pvar.h" #include "../../ut.h" #include "env_var.h" static env_var_t *env_vars = 0; int pv_parse_env_name(pv_spec_p sp, str *in) { env_var_p it; if(in==NULL || in->s==NULL || sp==NULL) return -1; for (it=env_vars; it; it=it->next) { if (in->len == it->name.len && !strncmp(it->name.s, in->s, in->len)) goto end; } it = (env_var_p)pkg_malloc(sizeof(env_var_t)); if (!it) { LM_ERR("no more pkg mem\n"); return -1; } memset(it, 0, sizeof(env_var_t)); it->name.s = (char*)pkg_malloc(in->len + 1); if (!it->name.s) { LM_ERR("no more pkg mem\n"); return -1; } memcpy(it->name.s, in->s, in->len); it->name.s[in->len] = 0; it->name.len = in->len; it->next = env_vars; env_vars = it->next; end: sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void*)it; return 0; } int pv_get_env(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { env_var_p env_v = NULL; char *env_val; int len; if (!res) return -1; if (!param || !param->pvn.u.dname) return pv_get_null(msg, param, res); env_v = (env_var_p)param->pvn.u.dname; env_val = getenv(env_v->name.s); if (!env_val) { LM_DBG("env variable <%s> could not be found\n", env_v->name.s); return pv_get_null(msg, param, res); } len = strlen(env_val); if (len > env_v->value.len) { env_v->value.s = (char*)pkg_realloc(env_v->value.s, len); if (!env_v->value.s) { LM_ERR("no more pkg mem\n"); return pv_get_null(msg, param, res); } } memcpy(env_v->value.s, env_val, len); env_v->value.len = len; res->rs = env_v->value; res->flags = PV_VAL_STR; return 0; } void destroy_env_list(void) { env_var_p env_it; while (env_vars) { env_it = env_vars; env_vars = env_vars->next; pkg_free(env_it->name.s); if (env_it->value.s) pkg_free(env_it->value.s); pkg_free(env_it); } } opensips-2.2.2/modules/cfgutils/env_var.h000066400000000000000000000005411300170765700204640ustar00rootroot00000000000000#ifndef _ENV_VAR_H_ #define _ENV_VAR_H_ #include "../../script_var.h" #include "../../usr_avp.h" typedef struct env_var { str name; str value; struct env_var *next; } env_var_t, *env_var_p; int pv_parse_env_name(pv_spec_p sp, str *in); int pv_get_env(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); void destroy_env_list(void); #endif opensips-2.2.2/modules/cfgutils/script_locks.c000066400000000000000000000101131300170765700215120ustar00rootroot00000000000000/** * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2012-11-21 created (Liviu) * */ #include "script_locks.h" static static_lock *static_locks = NULL; static gen_lock_set_t *dynamic_locks; extern int lock_pool_size; int create_dynamic_locks(void) { dynamic_locks = lock_set_alloc(lock_pool_size); if (!dynamic_locks) { LM_ERR("SHM MEMORY depleted!\n"); return -1; } lock_set_init(dynamic_locks); return 0; } int fixup_static_lock(void **param, int param_no) { static_lock *lock_entry; if (param_no != 1) { LM_ERR("Invalid parameter!\n"); return -1; } if (*param && *((char *)*param) == PV_MARKER) { LM_ERR("get_static_lock() only accepts string values!\n"); return -1; } for (lock_entry = static_locks; lock_entry; lock_entry = lock_entry->next) { if (memcmp(lock_entry->name.s, *param, lock_entry->name.len) == 0) { *param = (void *)lock_entry->lock; return 1; } } lock_entry = shm_malloc(sizeof(*lock_entry)); if (!lock_entry) { LM_ERR("SHM MEMORY depleted!\n"); return -1; } lock_entry->name.s = (char *) *param; lock_entry->name.len = strlen(lock_entry->name.s); lock_entry->lock = lock_alloc(); lock_init(lock_entry->lock); lock_entry->next = static_locks; static_locks = lock_entry; *param = (void *)lock_entry->lock; return 1; } int get_static_lock(struct sip_msg *msg, char *lock) { LM_DBG("Getting static lock----- <%p>\n", lock); lock_get((gen_lock_t *)lock); LM_DBG("Got static lock----- <%p>\n", lock); return 1; } int release_static_lock(struct sip_msg *msg, char *lock) { lock_release((gen_lock_t *)lock); LM_DBG("Released static lock----- <%p>\n", lock); return 1; } int get_dynamic_lock(struct sip_msg *msg, char *string) { str ret; int hash; if (((gparam_p)string)->type == GPARAM_TYPE_STR) { LM_INFO("Static string given! get_static_lock() function is better!\n"); } if (fixup_get_svalue(msg, (gparam_p)string, &ret) != 0) { LM_ERR("Get string from fixup param failed!\n"); return -1; } hash = (int)core_hash(&ret, NULL, lock_pool_size); LM_DBG("Getting dynamic lock----- %d\n", hash); lock_set_get(dynamic_locks, hash); LM_DBG("Got dynamic lock----- %d\n", hash); return 1; } int release_dynamic_lock(struct sip_msg *msg, char *string) { str ret; int hash; if (fixup_get_svalue(msg, (gparam_p)string, &ret) != 0) { LM_ERR("Get string from fixup param failed!\n"); return -1; } hash = (int)core_hash(&ret, NULL, lock_pool_size); lock_set_release(dynamic_locks, hash); LM_DBG("Released dynamic lock----- %d\n", hash); return 1; } int strings_share_lock(struct sip_msg *msg, char *s1, char *s2) { str ret1, ret2; if (fixup_get_svalue(msg, (gparam_p)s1, &ret1) != 0) { LM_ERR("Get string from fixup param failed!\n"); return -1; } if (fixup_get_svalue(msg, (gparam_p)s2, &ret2) != 0) { LM_ERR("Get string from fixup param failed!\n"); return -1; } if (core_hash(&ret1, NULL, lock_pool_size) == core_hash(&ret2, NULL, lock_pool_size)) { return 1; } return -1; } void destroy_script_locks(void) { static_lock *lock_entry; /* Free all static locks */ while (static_locks) { lock_entry = static_locks; static_locks = static_locks->next; if (lock_entry->lock) lock_dealloc(lock_entry->lock); shm_free(lock_entry); } /* Free all dynamic locks */ if (dynamic_locks) lock_set_dealloc(dynamic_locks); } opensips-2.2.2/modules/cfgutils/script_locks.h000066400000000000000000000030301300170765700215170ustar00rootroot00000000000000/** * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2012-11-21 created (Liviu) * */ #ifndef __SCRIPT_LOCKS_H__ #define __SCRIPT_LOCKS_H__ #include "../../locking.h" #include "../../ut.h" #include "../../mod_fix.h" int fixup_static_lock(void **param, int param_no); int create_dynamic_locks(void); int get_static_lock(struct sip_msg *msg, char *lock); int release_static_lock(struct sip_msg *msg, char *lock); int get_dynamic_lock(struct sip_msg *msg, char *string); int release_dynamic_lock(struct sip_msg *msg, char *string); int strings_share_lock(struct sip_msg *msg, char *s1, char *s2); void destroy_script_locks(void); typedef struct _static_lock { str name; gen_lock_t *lock; struct _static_lock *next; } static_lock; #endif /* __SCRIPT_LOCKS_H__ */ opensips-2.2.2/modules/cfgutils/shvar.c000066400000000000000000000361371300170765700201540ustar00rootroot00000000000000/* * Copyright (C) 2007 Elena-Ramona Modroiu * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../pvar.h" #include "shvar.h" int shvar_locks_no=16; gen_lock_set_t* shvar_locks=0; static sh_var_t *sh_vars = 0; /* * Initialize locks */ int shvar_init_locks(void) { int i; i = shvar_locks_no; do { if ((( shvar_locks=lock_set_alloc(i))!=0)&& (lock_set_init(shvar_locks)!=0)) { shvar_locks_no = i; LM_INFO("locks array size %d\n", shvar_locks_no); return 0; } if (shvar_locks){ lock_set_dealloc(shvar_locks); shvar_locks=0; } i--; if(i==0) { LM_ERR("failed to allocate locks\n"); return -1; } } while (1); } void shvar_unlock_locks(void) { unsigned int i; if (shvar_locks==0) return; for (i=0;ilocks[i]); #else shvar_release_idx(i); #endif }; } void shvar_destroy_locks(void) { if (shvar_locks !=0){ lock_set_destroy(shvar_locks); lock_set_dealloc(shvar_locks); } } #ifndef GEN_LOCK_T_PREFERED void shvar_lock_idx(int idx) { lock_set_get(shvar_locks, idx); } void shvar_release_idx(int idx) { lock_set_release(shvar_locks, idx); } #endif /* * Get lock */ void lock_shvar(sh_var_t *shv) { if(shv==NULL) return; #ifdef GEN_LOCK_T_PREFERED lock_get(shv->lock); #else shvar_lock_idx(shv->lockidx); #endif } /* * Release lock */ void unlock_shvar(sh_var_t *shv) { if(shv==NULL) return; #ifdef GEN_LOCK_T_PREFERED lock_release(shv->lock); #else shvar_release_idx(shv->lockidx); #endif } sh_var_t* add_shvar(str *name) { sh_var_t *sit; if(!shvar_locks){ if(shvar_init_locks()){ LM_ERR("init shvars locks failed\n"); return 0; } } if(name==0 || name->s==0 || name->len<=0) return 0; for(sit=sh_vars; sit; sit=sit->next) { if(sit->name.len==name->len && strncmp(name->s, sit->name.s, name->len)==0) return sit; } sit = (sh_var_t*)shm_malloc(sizeof(sh_var_t)); if(sit==0) { LM_ERR("out of shm\n"); return 0; } memset(sit, 0, sizeof(sh_var_t)); sit->name.s = (char*)shm_malloc((name->len+1)*sizeof(char)); if(sit->name.s==0) { LM_ERR("out of shm!\n"); shm_free(sit); return 0; } sit->name.len = name->len; strncpy(sit->name.s, name->s, name->len); sit->name.s[sit->name.len] = '\0'; if(sh_vars!=0) sit->n = sh_vars->n + 1; else sit->n = 1; #ifdef GEN_LOCK_T_PREFERED sit->lock = &shvar_locks->locks[sit->n%shvar_locks_no]; #else sit->lockidx = sit->n%shvar_locks_no; #endif sit->next = sh_vars; sh_vars = sit; return sit; } /* call it with lock set */ sh_var_t* set_shvar_value(sh_var_t* shv, int_str *value, int flags) { if(shv==NULL) return NULL; if(value==NULL) { if(shv->v.flags&VAR_VAL_STR) { shm_free(shv->v.value.s.s); shv->v.flags &= ~VAR_VAL_STR; } memset(&shv->v.value, 0, sizeof(int_str)); return shv; } if(flags&VAR_VAL_STR) { if(shv->v.flags&VAR_VAL_STR) { /* old and new value is str */ if(value->s.len>shv->v.value.s.len) { /* not enough space to copy */ shm_free(shv->v.value.s.s); memset(&shv->v.value, 0, sizeof(int_str)); shv->v.value.s.s = (char*)shm_malloc((value->s.len+1)*sizeof(char)); if(shv->v.value.s.s==0) { LM_ERR("out of shm\n"); goto error; } } } else { memset(&shv->v.value, 0, sizeof(int_str)); shv->v.value.s.s = (char*)shm_malloc((value->s.len+1)*sizeof(char)); if(shv->v.value.s.s==0) { LM_ERR("out of shm!\n"); goto error; } shv->v.flags |= VAR_VAL_STR; } strncpy(shv->v.value.s.s, value->s.s, value->s.len); shv->v.value.s.len = value->s.len; shv->v.value.s.s[value->s.len] = '\0'; } else { if(shv->v.flags&VAR_VAL_STR) { shm_free(shv->v.value.s.s); shv->v.flags &= ~VAR_VAL_STR; memset(&shv->v.value, 0, sizeof(int_str)); } shv->v.value.n = value->n; } return shv; error: /* set the var to init value */ memset(&shv->v.value, 0, sizeof(int_str)); shv->v.flags &= ~VAR_VAL_STR; return NULL; } sh_var_t* get_shvar_by_name(str *name) { sh_var_t *it; if(name==0 || name->s==0 || name->len<=0) return 0; for(it=sh_vars; it; it=it->next) { if(it->name.len==name->len && strncmp(name->s, it->name.s, name->len)==0) return it; } return 0; } void reset_shvars(void) { sh_var_t *it; for(it=sh_vars; it; it=it->next) { if(it->v.flags&VAR_VAL_STR) { shm_free(it->v.value.s.s); it->v.flags &= ~VAR_VAL_STR; } memset(&it->v.value, 0, sizeof(int_str)); } } void destroy_shvars(void) { sh_var_t *it; sh_var_t *it0; it = sh_vars; while(it) { it0 = it; it = it->next; shm_free(it0->name.s); if(it0->v.flags&VAR_VAL_STR) shm_free(it0->v.value.s.s); shm_free(it0); } sh_vars = 0; } /********* PV functions *********/ int pv_parse_shvar_name(pv_spec_p sp, str *in) { if(in==NULL || in->s==NULL || sp==NULL) return -1; sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void*)add_shvar(in); if(sp->pvp.pvn.u.dname==NULL) { LM_ERR("cannot register shvar [%.*s]\n", in->len, in->s); return -1; } return 0; } int pv_get_shvar(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int len = 0; char *sval = NULL; sh_var_t *shv=NULL; if(msg==NULL || res==NULL) return -1; if(param==NULL || param->pvn.u.dname==0) return pv_get_null(msg, param, res); shv= (sh_var_t*)param->pvn.u.dname; lock_shvar(shv); if(shv->v.flags&VAR_VAL_STR) { if(param->pvv.s==NULL || param->pvv.len < shv->v.value.s.len) { if(param->pvv.s!=NULL) pkg_free(param->pvv.s); param->pvv.s = (char*)pkg_malloc(shv->v.value.s.len*sizeof(char)); if(param->pvv.s==NULL) { unlock_shvar(shv); LM_ERR("no more pkg mem\n"); return pv_get_null(msg, param, res); } } strncpy(param->pvv.s, shv->v.value.s.s, shv->v.value.s.len); param->pvv.len = shv->v.value.s.len; unlock_shvar(shv); res->rs = param->pvv; res->flags = PV_VAL_STR; } else { res->ri = shv->v.value.n; unlock_shvar(shv); sval = sint2str(res->ri, &len); res->rs.s = sval; res->rs.len = len; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; } return 0; } int pv_set_shvar(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { int_str isv; int flags; if(param==NULL) { LM_ERR("bad parameters\n"); return -1; } if(param->pvn.u.dname==0) { LM_ERR("error - cannot find shvar\n"); goto error; } lock_shvar((sh_var_t*)param->pvn.u.dname); if(val == NULL) { isv.n = 0; set_shvar_value((sh_var_t*)param->pvn.u.dname, &isv, 0); goto done; } flags = 0; if(val->flags&PV_TYPE_INT) { isv.n = val->ri; } else { isv.s = val->rs; flags |= VAR_VAL_STR; } if(set_shvar_value((sh_var_t*)param->pvn.u.dname, &isv, flags)==NULL) { LM_ERR("error - cannot set shvar [%.*s] \n", ((sh_var_t*)param->pvn.u.dname)->name.len, ((sh_var_t*)param->pvn.u.dname)->name.s); goto error; } done: unlock_shvar((sh_var_t*)param->pvn.u.dname); return 0; error: unlock_shvar((sh_var_t*)param->pvn.u.dname); return -1; } struct mi_root* mi_shvar_set(struct mi_root* cmd_tree, void* param) { str sp; str name; int ival; int_str isv; int flags; struct mi_node* node; sh_var_t *shv = NULL; node = cmd_tree->node.kids; if(node == NULL) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM_S)); name = node->value; if(name.len<=0 || name.s==NULL) { LM_ERR("bad shv name\n"); return init_mi_tree( 500, MI_SSTR("bad shv name")); } shv = get_shvar_by_name(&name); if(shv==NULL) return init_mi_tree(404, MI_SSTR("Not found")); node = node->next; if(node == NULL) return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM_S)); sp = node->value; if(sp.s == NULL) return init_mi_tree(500, MI_SSTR("type not found")); flags = 0; if(sp.s[0]=='s' || sp.s[0]=='S') flags = VAR_VAL_STR; node= node->next; if(node == NULL) return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM_S)); sp = node->value; if(sp.s == NULL) { return init_mi_tree(500, MI_SSTR("value not found")); } if(flags == 0) { if(str2sint(&sp, &ival)) { LM_ERR("bad integer value\n"); return init_mi_tree( 500, MI_SSTR("bad integer value")); } isv.n = ival; } else { isv.s = sp; } lock_shvar(shv); if(set_shvar_value(shv, &isv, flags)==NULL) { unlock_shvar(shv); LM_ERR("cannot set shv value\n"); return init_mi_tree( 500, MI_SSTR("cannot set shv value")); } unlock_shvar(shv); LM_DBG("$shv(%.*s) updated\n", name.len, name.s); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } struct mi_root* mi_shvar_get(struct mi_root* cmd_tree, void* param) { struct mi_root* rpl_tree = NULL; struct mi_node* node; struct mi_attr* attr = NULL; str name; int ival; sh_var_t *shv = NULL; node = cmd_tree->node.kids; if(node != NULL) { name = node->value; if(name.len<=0 || name.s==NULL) { LM_ERR("bad shv name\n"); return init_mi_tree( 500, MI_SSTR("bad shv name")); } shv = get_shvar_by_name(&name); if(shv==NULL) return init_mi_tree(404, MI_SSTR("Not found")); rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; node = add_mi_node_child(&rpl_tree->node, MI_DUP_VALUE, "VAR",3, name.s, name.len); if(node == NULL) goto error; lock_shvar(shv); if(shv->v.flags&VAR_VAL_STR) { attr = add_mi_attr (node, MI_DUP_VALUE, "type", 4, "string", 6); if(attr == 0) { unlock_shvar(shv); goto error; } attr = add_mi_attr (node, MI_DUP_VALUE, "value", 5, shv->v.value.s.s, shv->v.value.s.len); if(attr == 0) { unlock_shvar(shv); goto error; } unlock_shvar(shv); } else { ival = shv->v.value.n; unlock_shvar(shv); attr = add_mi_attr (node, MI_DUP_VALUE, "type",4, "integer", 7); if(attr == 0) goto error; name.s = sint2str(ival, &name.len); attr = add_mi_attr (node, MI_DUP_VALUE, "value",5, name.s, name.len); if(attr == 0) goto error; } goto done; } rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; for(shv=sh_vars; shv; shv=shv->next) { node = add_mi_node_child(&rpl_tree->node, MI_DUP_VALUE, "VAR", 3, shv->name.s, shv->name.len); if(node == NULL) goto error; lock_shvar(shv); if(shv->v.flags&VAR_VAL_STR) { attr = add_mi_attr (node, MI_DUP_VALUE, "type", 4, "string", 6); if(attr == 0) { unlock_shvar(shv); goto error; } attr = add_mi_attr (node, MI_DUP_VALUE, "value", 5, shv->v.value.s.s, shv->v.value.s.len); if(attr == 0) { unlock_shvar(shv); goto error; } unlock_shvar(shv); } else { ival = shv->v.value.n; unlock_shvar(shv); attr = add_mi_attr (node, MI_DUP_VALUE, "type",4, "integer", 7); if(attr == 0) goto error; name.s = sint2str(ival, &name.len); attr = add_mi_attr (node, MI_DUP_VALUE, "value",5, name.s, name.len); if(attr == 0) goto error; } } done: return rpl_tree; error: if(rpl_tree!=NULL) free_mi_tree(rpl_tree); return NULL; } int param_set_xvar( modparam_t type, void* val, int mode) { str s; char *p; int_str isv; int flags; int ival; script_var_t *sv; sh_var_t *shared_sv; s.s = (char*)val; if(s.s == NULL || s.s[0] == '\0') goto error; p = s.s; while(*p && *p!='=') p++; if(*p!='=') goto error; s.len = p - s.s; if(s.len == 0) goto error; p++; flags = 0; if(*p!='s' && *p!='S' && *p!='i' && *p!='I') goto error; if(*p=='s' || *p=='S') flags = VAR_VAL_STR; p++; if(*p!=':') goto error; p++; isv.s.s = p; isv.s.len = strlen(p); if(flags != VAR_VAL_STR) { if(str2sint(&isv.s, &ival)<0) goto error; isv.n = ival; } if(mode==0){ sv = add_var(&s); if(sv==NULL) goto error; if(set_var_value(sv, &isv, flags)==NULL) goto error; } else { shared_sv = add_shvar(&s); if(shared_sv == NULL) goto error; if(set_shvar_value(shared_sv, &isv, flags) == NULL) goto error; } return 0; error: LM_ERR("unable to set %s parameter [%s]\n", (mode == 0 ? "var" : "shv"), s.s); return -1; } int param_set_var( modparam_t type, void* val) { return param_set_xvar(type, val, 0); } int param_set_shvar( modparam_t type, void* val) { return param_set_xvar(type, val, 1); } /*** $time(name) PV class */ int pv_parse_time_name(pv_spec_p sp, str *in) { if(sp==NULL || in==NULL || in->len<=0) return -1; switch(in->len) { case 3: if(strncmp(in->s, "sec", 3)==0) sp->pvp.pvn.u.isname.name.n = 0; else if(strncmp(in->s, "min", 3)==0) sp->pvp.pvn.u.isname.name.n = 1; else if(strncmp(in->s, "mon", 3)==0) sp->pvp.pvn.u.isname.name.n = 4; else goto error; break; case 4: if(strncmp(in->s, "hour", 4)==0) sp->pvp.pvn.u.isname.name.n = 2; else if(strncmp(in->s, "mday", 4)==0) sp->pvp.pvn.u.isname.name.n = 3; else if(strncmp(in->s, "year", 4)==0) sp->pvp.pvn.u.isname.name.n = 5; else if(strncmp(in->s, "wday", 4)==0) sp->pvp.pvn.u.isname.name.n = 6; else if(strncmp(in->s, "yday", 4)==0) sp->pvp.pvn.u.isname.name.n = 7; else goto error; break; case 5: if(strncmp(in->s, "isdst", 5)==0) sp->pvp.pvn.u.isname.name.n = 8; else goto error; break; default: goto error; } sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = 0; return 0; error: LM_ERR("unknown PV time name %.*s\n", in->len, in->s); return -1; } int pv_get_time(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static struct tm stored_ts; static time_t stored_t = 0; time_t t; if(msg==NULL || param==NULL) return -1; t = time(NULL); if (t!=stored_t) { stored_t = t; if (localtime_r(&t, &stored_ts) == NULL) { LM_ERR("unable to break time to attributes\n"); return -1; } } switch(param->pvn.u.isname.name.n) { case 1: return pv_get_uintval(msg, param, res, (unsigned int)stored_ts.tm_min); case 2: return pv_get_uintval(msg, param, res, (unsigned int)stored_ts.tm_hour); case 3: return pv_get_uintval(msg, param, res, (unsigned int)stored_ts.tm_mday); case 4: return pv_get_uintval(msg, param, res, (unsigned int)(stored_ts.tm_mon+1)); case 5: return pv_get_uintval(msg, param, res, (unsigned int)(stored_ts.tm_year+1900)); case 6: return pv_get_uintval(msg, param, res, (unsigned int)(stored_ts.tm_wday+1)); case 7: return pv_get_uintval(msg, param, res, (unsigned int)(stored_ts.tm_yday+1)); case 8: return pv_get_sintval(msg, param, res, stored_ts.tm_isdst); default: return pv_get_uintval(msg, param, res, (unsigned int)stored_ts.tm_sec); } } opensips-2.2.2/modules/cfgutils/shvar.h000066400000000000000000000045151300170765700201540ustar00rootroot00000000000000/* * Copyright (C) 2007 Elena-Ramona Modroiu * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _SHVAR_H_ #define _SHVAR_H_ #include "../../sr_module.h" #include "../../locking.h" #include "../../mi/mi.h" #include "../../script_var.h" typedef struct sh_var { int n; /* Index of the variable */ str name; /* Name of the variable */ script_val_t v; /* Value of the variable */ #ifdef GEN_LOCK_T_PREFERED gen_lock_t *lock; /* Lock for hash entry - fastlock */ #else int lockidx; /* Lock index for hash entry - the rest*/ #endif struct sh_var *next; } sh_var_t, *sh_var_p; int init_shvars(void); sh_var_t* set_shvar_value(sh_var_t *shv, int_str *value, int flags); sh_var_t* get_shvar_by_name(str *name); void reset_shvars(); void destroy_shvars(); #ifndef GEN_LOCK_T_PREFERED void shvar_lock_idx(int idx); void shvar_release_idx(int idx); #endif void lock_shvar(sh_var_t *shv); void unlock_shvar(sh_var_t *shv); int pv_parse_shvar_name(pv_spec_p sp, str *in); int pv_get_shvar(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int pv_set_shvar(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val); int shvar_init_locks(void); void shvar_destroy_locks(void); struct mi_root* mi_shvar_get(struct mi_root* cmd_tree, void* param); struct mi_root* mi_shvar_set(struct mi_root* cmd_tree, void* param); int param_set_var( modparam_t type, void* val); int param_set_shvar( modparam_t type, void* val); /*** $time(name) PV class */ int pv_parse_time_name(pv_spec_p sp, str *in); int pv_get_time(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); #endif opensips-2.2.2/modules/clusterer/000077500000000000000000000000001300170765700170435ustar00rootroot00000000000000opensips-2.2.2/modules/clusterer/Makefile000066400000000000000000000002541300170765700205040ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=clusterer.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/clusterer/README000066400000000000000000000430501300170765700177250ustar00rootroot00000000000000CLUSTERER Module Marius Cristian Eseanu OpenSIPS Solutions Copyright © 2015 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url 1.3.2. db_table 1.3.3. server_id 1.3.4. persistent_mode 1.3.5. cluster_id_col 1.3.6. machine_id_col 1.3.7. clusterer_id_col 1.3.8. state_col 1.3.9. url_col 1.3.10. description_col 1.3.11. failed_attempts_col 1.3.12. last_attempt_col 1.3.13. duration_col 1.3.14. no_tries_col 1.4. Exported Functions 1.5. Exported MI Functions 1.5.1. clusterer_reload 1.5.2. clusterer_list 1.5.3. clusterer_set_status 1.6. Usage Example 2. Developer Guide 2.1. Available Functions 2.1.1. get_nodes(cluster_id, proto) 2.1.2. free_nodes(nodes) 2.1.3. set_state(cluster_id, machine_id, state, proto) 2.1.4. check(cluster_id, sockaddr, server_id, proto) 2.1.5. get_my_id() 2.1.6. send_to(cluster_id, protocol) 2.1.7. register_module(module_name, protocol, callback_function, timeout, auth_check, cluster_id) List of Examples 1.1. Set db_url parameter 1.2. Set db_table parameter 1.3. Set server_id parameter 1.4. Set persistent_mode parameter 1.5. Set cluster_id_col parameter 1.6. Set machine_id_col parameter 1.7. Set clusterer_id_col parameter 1.8. Set state_col parameter 1.9. Set url_col parameter 1.10. Set description_col parameter 1.11. Set failed_attempts_col parameter 1.12. Set last_attempt_col parameter 1.13. Set duration_col parameter 1.14. Set no_tries_col parameter 1.15. Example database content - clusterer table 1.16. Node A configuration 1.17. Node B configuration 2.1. get_nodes usage 2.2. free_nodes usage 2.3. set_state usage 2.4. check usage 2.5. get_my_id usage 2.6. send_to usage 2.7. register_module usage Chapter 1. Admin Guide 1.1. Overview The clusterer module is used to organize multiple OpenSIPS instances into groups that can communicate with each other in order to replicate, share information or perform distributed tasks. The module itself only stores information about the nodes in a group/cluster and provides an interface to check or tune their state and parameters. The distributed logic is performed by different modules that use this interface (i.e. the dialog module can replicate profiles, the ratelimit module can share pipes across multiple instances, etc). Provisioning the nodes within a cluster is done over the database but, for efficiency, the node-related information is cached into OpenSIPS memory. This information can be checked or updated by sending commands over the MI interface. The clusterer module can also detect node availability, by using certain parameters provisioned in the database. When a destination is not reachable, it is put in a probing state - it is periodically pinged until a maximum number of failed attempts is reached, when it is marked as temporarily disabled. It stays in this state for a period (equal to the duration parameter), and then the number of retries reset to 0 and the node is considered up again. Modules (like dialog or ratelimit can use nodes within the cluster to replicate information. They also register a specific timeout to invalidate data from specific nodes, in case no updates have been within an interval. The clusterer notifies the module if the timeout is reached and puts the node in a temporary disabled state. If a packet has arrived for a temporary disabled server, the packet is dropped and a temporary disabled notification is sent to the registered module. After the disabled period (2 * timeout) has passed, the server is up again. By default, the state information of the nodes is not persistent. To make them persistent via a database, one must set the persistent_mode parameter. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * a database module. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. db_url The database url. Default value is “NULLâ€. Example 1.1. Set db_url parameter ... modparam("clusterer", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") ... 1.3.2. db_table The name of the table storing the clustering information. Default value is “clustererâ€. Example 1.2. Set db_table parameter ... modparam("clusterer", "db_table", "clusterer") ... 1.3.3. server_id It specifies the server_id the current instance has. this field should correspond with one of the machine_id fields in the database. Default value is 0. Example 1.3. Set server_id parameter ... modparam("clusterer", "server_id", 2) ... 1.3.4. persistent_mode If persistent mode is enabled, a timer synchronizes the information used by the clusterer module and the information stored in the database. Default value is 0 (disabled). Example 1.4. Set persistent_mode parameter ... modparam("clusterer", "persistent_mode", 1) ... 1.3.5. cluster_id_col The name of the column in the db_table where the cluster_id is stored. Default value is “cluster_idâ€. Example 1.5. Set cluster_id_col parameter ... modparam("clusterer", "cluster_id_col", "cluster_id") ... 1.3.6. machine_id_col The name of the column in the db_table where the machine_id is stored. Default value is “machine_idâ€. Example 1.6. Set machine_id_col parameter ... modparam("clusterer", "machine_id_col", "machine_id") ... 1.3.7. clusterer_id_col The name of the column in the db_table where the machine_id is stored. Default value is “idâ€. Example 1.7. Set clusterer_id_col parameter ... modparam("clusterer", "clusterer_id_col", "clusterer_id") ... 1.3.8. state_col The name of the column in the db_table where the state is stored. Default value is “stateâ€. Example 1.8. Set state_col parameter ... modparam("clusterer", "state_col", "state") ... 1.3.9. url_col The name of the column in the db_table where the url is stored. Default value is “urlâ€. Example 1.9. Set url_col parameter ... modparam("clusterer", "url_col", "url") ... 1.3.10. description_col The name of the column in the db_table where the machine's description is stored. Default value is “descriptionâ€. Example 1.10. Set description_col parameter ... modparam("clusterer", "description_col", "description") ... 1.3.11. failed_attempts_col The name of the column in the db_table where the maximum allowed number of failed attempts is stored. Default value is “failed_attemptsâ€. Example 1.11. Set failed_attempts_col parameter ... modparam("clusterer", "failed_attempts_col", "failed_attempts") ... 1.3.12. last_attempt_col The name of the column in the db_table where the UNIX time of last last failed attempt is stored. Default value is “last_attemptâ€. Example 1.12. Set last_attempt_col parameter ... modparam("clusterer", "last_attempt_col", "last_attempt") ... 1.3.13. duration_col The name of the column in the db_table where the duration of a machine belonging to a certain cluster temporary disabled state is stored. Default value is “durationâ€. Example 1.13. Set duration_col parameter ... modparam("clusterer", "duration_col", "duration") ... 1.3.14. no_tries_col The name of the column in the db_table where the number of failed connecting tries is stored. Default value is “no_triesâ€. Example 1.14. Set no_tries_col parameter ... modparam("clusterer", "no_tries_col", "no_tries") ... 1.4. Exported Functions none 1.5. Exported MI Functions 1.5.1. clusterer_reload Reloads data from the clusterer database. If the persistent mode is disabled the changes made to the locally stored data are lost. Name: clusterer_reload Parameters:none MI FIFO Command Format: :clusterer_reload _empty_line_ 1.5.2. clusterer_list Lists in a table format all the data stored in OpenSIPS cache. Name: clusterer_list Parameters:none MI FIFO Command Format: :clusterer_list _empty_line_ 1.5.3. clusterer_set_status Sets the status(UP, DOWN) of a machine belonging to a certain cluster. Name: clusterer_set_status Parameters: * cluster_id - indicates the id of the cluster. * machine_id - indicates the id of the machine. * status - indicates the new status( 0 - permanent down, 1 - up). * protocol - indicates the protocol. MI FIFO Command Format: :clusterer_set_status: 1 2 0 bin _empty_line_ 1.6. Usage Example This section provides an usage example for replicating ratelimit pipes between two OpenSIPS instances. It uses the clusterer module to manage the replicating nodes, and the proto_bin modules to send the replicated information. The setup topology is simple: we have two OpenSIPS nodes running on two separate machines (although they could run on the same machine as well): Node A has IP 192.168.0.5 and Node B has IP 192.168.0.6. Both have, besides the traffic listeners (UDP, TCP, etc.), bin listeners bound on port 5566. These listeners will be used by the ratelimit module to replicate the pipes. Therefore, we have to provision them in the clusterer table. Example 1.15. Example database content - clusterer table +----+------------+------------+----------------------+-------+--------- -----+-----------------+----------+----------+-------------+ | id | cluster_id | machine_id | url | state | last_att empt | failed_attempts | no_tries | duration | description | +----+------------+------------+----------------------+-------+--------- -----+-----------------+----------+----------+-------------+ | 1 | 1 | 1 | bin:192.168.0.5:5566 | 1 | 0 | 0 | 0 | 30 | Node A | | 2 | 1 | 2 | bin:192.168.0.6:5566 | 1 | 0 | 0 | 0 | 30 | Node B | +----+------------+------------+----------------------+-------+--------- -----+-----------------+----------+----------+-------------+ * “cluster_id†- this column represents the identifier of the cluster. All nodes within a group/cluster should have the same id (in our example, both nodes have ID 1) * “machine_id†- this represents the identifier of the machine/node, and each instance within a cluster should have a different ID. In our example, Node A will have ID 1, and Node B ID 2 * “url†- this indicates the URL where the instance will receive the replication information. In our example, each node will receive the date over the bin protocol * “state†- this is the state of the machine: 1 means on, 0 means off, and 2 means it is in probing. Note that if you want the node to be active right away, you have to set it in state 1 * “last_attemptâ€, “failed_attempts†and “no_tries†- are fields used for the probing mechanisms, and should be set to 0 by default. They are automatically updated by the clusterer module if the persistent_mode parameter is set to 1 * “duration†- is used to specify the period a node stays in the temporary disabled state. In our example, if the node does not respond, it is disabled for 30 seconds before retrying to send data again * “description†- is an opaque value used to identify the node After provisioning the two nodes in the database, we have to configure the two instances of OpenSIPS. First, we configure Node A: Example 1.16. Node A configuration ... listen = bin:192.168.0.5:5566 # bin listener for Node A loadmodule "proto_bin.so" loadmodule "clusterer.so" modparam("clusterer", "db_url", "mysql://opensips@192.168.0.7/opensips") modparam("clusterer", "server_id", 1) # machine_id for Node A loadmodule "ratelimit.so" # replicate pipes to cluster id 1 modparam("ratelimit", "replicate_pipes_to", 1) # accept replicated data from nodes within cluster 1 modparam("ratelimit", "accept_pipes_from", 1) # if a node does not reply in a 5 seconds interval, #the information from that node is invalidated modparam("ratelimit", "accept_pipes_timeout", 5) ... Similarly, the configuration for Node B is as follows: Example 1.17. Node B configuration ... listen = bin:192.168.0.6:5566 # bin listener for Node B loadmodule "proto_bin.so" loadmodule "clusterer.so" # ideally, use the same database for both nodes modparam("clusterer", "db_url", "mysql://opensips@192.168.0.7/opensips") modparam("clusterer", "server_id", 2) # machine_id for Node B loadmodule "ratelimit.so" # replicate pipes to cluster id 1 modparam("ratelimit", "replicate_pipes_to", 1) # accept replicated data from nodes within cluster 1 modparam("ratelimit", "accept_pipes_from", 1) # if a node does not reply in a 5 seconds interval, # the information from that node is invalidated modparam("ratelimit", "accept_pipes_timeout", 5) ... Note that the server_id parameter for Node B is 2. Starting the two OpenSIPS instances with the above configurations provides your platform the ability to used shared ratelimit pipes in a very efficient and scalable way. Chapter 2. Developer Guide 2.1. Available Functions 2.1.1. get_nodes(cluster_id, proto) The function will return all a copy of all the needed information from the nodes (machine_id, state, description, sock address) stored in shm, whos state is up(1) and have a certain cluster_id and protocol. This function is usually used for replication purposes. This function returns NULL on error. Meaning of the parameters is as follows: * int cluster_id - the cluster id * int proto - the protocol Example 2.1. get_nodes usage ... get_nodes(cluster_id, proto); ... 2.1.2. free_nodes(nodes) This function will free the allocated data for the copy. Meaning of the parameters is as follows: * clusterer_node_t *nodes - the data returned by the get_nodes function Example 2.2. free_nodes usage ... free_nodes(nodes); ... 2.1.3. set_state(cluster_id, machine_id, state, proto) The function sets the state of a machine belonging to a certain cluster, which have the specified protocol. This function is usually used for replication purposes. Meaning of the parameters is as follows: * int cluster_id - cluster_id * int machine_id - machine_id * int state - the server state * int proto - protocol Example 2.3. set_state usage ... set_state(1,1,2,PROTO_BIN); ... 2.1.4. check(cluster_id, sockaddr, server_id, proto) This function is used to check if the source of a receiving packet is known. It returns 1 if the source is known, else it returns 0. Meaning of the parameters is as follows: * int cluster_id - cluster id * union sockaddr_union* sockaddr - incoming connexion socket address * int server_id - incoming connexion server_id * int proto - protocol Example 2.4. check usage ... check(1, sockaddr, 2, PROTO_BIN) ... 2.1.5. get_my_id() This function will return the server id's. Example 2.5. get_my_id usage ... get_my_id() ... 2.1.6. send_to(cluster_id, protocol) This function will replicate information to the nodes belonging to a cluster_id that have a specific protocol. Meaning of the parameters is as follows: * int cluster_id - cluster_id * int protocol - protocol Example 2.6. send_to usage ... send_to(cluster_id, protocol) ... 2.1.7. register_module(module_name, protocol, callback_function, timeout, auth_check, cluster_id) This function registers a module to a certain protocol. It acts like an intermediary: when a valid packet has arrived, if the auth_check parameter is specified then it is checked for authenticity. After that, the timestamps are updated and the callback function from the registered module is called. The clusterer module checks for every registered module if the duration between the last receiving packet and the current time is greater than the module specified timeout. If it is, the servers are temporary disabled for a period of timestamp * 2. If any packets are received for the temporary disabled servers the registered module is notified. Meaning of the parameters is as follows: * char *module_name - module name * int protocol - protocol * void (*callback_function)(int, struct receive_info *, int) - the registered module callback function * int timeout - timeput * int auth_check - 0 if the authentication check is disabled, 1 if the authentication check is enabled * int cluster_id - cluster_id Example 2.7. register_module usage ... register_module(dialog, PROTO_BIN, cb, timeout, auth_check, cluster_id) ... opensips-2.2.2/modules/clusterer/api.h000066400000000000000000000035241300170765700177710ustar00rootroot00000000000000#ifndef API_H #define API_H #include "../../str.h" #define STATUS_PERMANENT_DOWN 0 #define STATUS_UP 1 #define STATUS_TEMPORARY_DOWN 2 #define SERVER_TEMP_DISABLED -1 #define SERVER_TIMEOUT -2 enum cl_machine_state { CLUSTERER_STATE_ON = 0, CLUSTERER_STATE_PROBE = 1, CLUSTERER_STATE_OFF = 2 }; typedef struct clusterer_node_ clusterer_node_t; struct clusterer_node_ { /* machine_id */ int machine_id; /* machine state */ int state; /* description */ str description; /* protocol */ int proto; /* sock address */ union sockaddr_union addr; /* linker in list */ clusterer_node_t *next; }; typedef clusterer_node_t * (*get_nodes_f) (int, int); typedef int (*set_state_f) (int, int, enum cl_machine_state, int); typedef void (*free_nodes_f) (clusterer_node_t *); typedef int (*check_connection_f) (int, union sockaddr_union*, int, int); typedef int (*get_my_id_f) (void); typedef int (*send_to_f) (int, int); typedef int (*register_module_f) (char *, int, void (*cb)(int, struct receive_info *, int), int, int, int); struct clusterer_binds { get_nodes_f get_nodes; free_nodes_f free_nodes; set_state_f set_state; check_connection_f check; get_my_id_f get_my_id; send_to_f send_to; register_module_f register_module; }; typedef int(*load_clusterer_f)(struct clusterer_binds *binds); int load_clusterer(struct clusterer_binds *binds); static inline int load_clusterer_api(struct clusterer_binds *binds) { load_clusterer_f load_clusterer; /* import the DLG auto-loading function */ if (!(load_clusterer = (load_clusterer_f) find_export("load_clusterer", 0, 0))) return -1; /* let the auto-loading function load all DLG stuff */ if (load_clusterer(binds) == -1) return -1; return 0; } #endif /* API_H */ opensips-2.2.2/modules/clusterer/clusterer.c000066400000000000000000001171311300170765700212230ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Project * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * history: * --------- * 2015-07-07 created by Marius Cristian Eseanu */ #include #include #include #include #include "../../sr_module.h" #include "../../str.h" #include "../../dprint.h" #include "../../usr_avp.h" #include "../../db/db.h" #include "../../socket_info.h" #include "../../resolve.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../rw_locking.h" #include "../../error.h" #include "../../ut.h" #include "../../mi/mi.h" #include "../../timer.h" #include "../../bin_interface.h" #include "../../forward.h" #include "clusterer.h" #include "api.h" #define DB_CAP DB_CAP_QUERY | DB_CAP_UPDATE #define check_val( _col, _val, _type, _not_null, _is_empty_str) \ do { \ if ((_val)->type!=_type) { \ LM_ERR("column %.*s has a bad type\n", _col.len, _col.s); \ goto error; \ } \ if (_not_null && (_val)->nul) { \ LM_ERR("column %.*s is null\n", _col.len, _col.s); \ goto error; \ } \ if (_is_empty_str && !VAL_STRING(_val)) { \ LM_ERR("column %.*s (str) is empty\n", _col.len, _col.s); \ goto error; \ } \ } while (0) /* lock */ static rw_lock_t *ref_lock; /* time interval */ static unsigned int prob_interval = 1; struct clusterer_binds clusterer_api; /* Database variables */ /* DB handler */ static db_con_t *db_hdl; /* DB functions */ static db_func_t dr_dbf; /* DB URL */ str clusterer_db_url; /* DB TABLE */ str db_table = str_init("clusterer"); /* db_table columns */ /* PK column */ str id_col = str_init("id"); str cluster_id_col = str_init("cluster_id"); str machine_id_col = str_init("machine_id"); str url_col = str_init("url"); str state_col = str_init("state"); str last_attempt_col = str_init("last_attempt"); str duration_col = str_init("duration"); str failed_attempts_col = str_init("failed_attempts"); str no_tries_col = str_init("no_tries"); str description_col = str_init("description"); static db_key_t *clusterer_cluster_id_key; static db_val_t *clusterer_cluster_id_value; static db_op_t op_eq = OP_EQ; int persistent_state = 0; int server_id = -1; /* shm data*/ static table_entry_t **tdata; static struct module_list *clusterer_modules; /* initialize functions */ static int mod_init(void); static int child_init(int rank); /* destroy function */ static void destroy(void); /* loads info from the db */ table_entry_t* load_info(db_func_t *dr_dbf, db_con_t* db_hdl, str *db_table); /* deallocate memory */ void free_data(table_entry_t *data); /* reloads data from the db */ /* if persistent mode is not set the local changes are lost */ static struct mi_root* clusterer_reload(struct mi_root* root, void *param); static int reload_data(); /* sets a connection state */ static struct mi_root* clusterer_set_status(struct mi_root *cmd, void *param); static int set_state(int cluster_id, int machine_id, enum cl_machine_state state, int proto); /* lists the available connections for the specified server*/ static struct mi_root * clusterer_list(struct mi_root *root, void *param); static void update_db_handler(unsigned int ticks, void* param); static clusterer_node_t* get_nodes(int cluster_id,int proto); static int clusterer_check(int cluster_id,union sockaddr_union *su, int machine_id, int proto); static void free_nodes(clusterer_node_t *nodes); static int su_ip_cmp(union sockaddr_union* s1, union sockaddr_union* s2); static int get_my_id(void); static void update_nodes_handler(unsigned int ticks, void *param); static struct module_timestamp* create_module_timestamp(int ctime, struct module_list *module); static table_entry_value_t *clusterer_find_nodes(int cluster_id, int proto); static int su_ip_cmp(union sockaddr_union* s1, union sockaddr_union* s2) { if (s1->s.sa_family!=s2->s.sa_family) return 0; switch(s1->s.sa_family){ case AF_INET: return (memcmp(&s1->sin.sin_addr, &s2->sin.sin_addr, 4)==0); case AF_INET6: return (memcmp(&s1->sin6.sin6_addr, &s2->sin6.sin6_addr, 16)==0); default: LM_CRIT("unknown address family %d\n", s1->s.sa_family); return 0; } } /* * Exported functions */ static cmd_export_t cmds[]={ {"load_clusterer", (cmd_function)load_clusterer, 0, 0, 0, 0}, {0,0,0,0,0,0} }; /* * Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &clusterer_db_url.s }, {"db_table", STR_PARAM, &db_table.s }, {"server_id", INT_PARAM, &server_id }, {"persistent_state", INT_PARAM, &persistent_state }, {"cluster_id_col", STR_PARAM, &cluster_id_col.s }, {"machine_id_col", STR_PARAM, &machine_id_col.s }, {"clusterer_id_col", INT_PARAM, &id_col.s }, {"state_col", STR_PARAM, &state_col.s }, {"url_col", STR_PARAM, &url_col.s }, {"description_col", STR_PARAM, &description_col.s }, {"last_attempt_col", STR_PARAM, &last_attempt_col.s }, {"duration_col", STR_PARAM, &duration_col.s }, {"failed_attempts_col", STR_PARAM, &failed_attempts_col.s }, {"no_tries_col", STR_PARAM, &no_tries_col.s }, {0, 0, 0} }; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { { "clusterer_reload", "reloads stored data from the database", clusterer_reload, 0, 0, 0}, { "clusterer_set_status", "sets the status for a specified connection", clusterer_set_status, 0, 0, 0}, { "clusterer_list", "lists the available connections for the specified server", clusterer_list, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** * module exports */ struct module_exports exports= { "clusterer", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /* initialize function */ static int mod_init(void) { LM_INFO("Cluster-Info - initializing\n"); /* check the module params */ init_db_url(clusterer_db_url, 0 /*cannot be null*/); if (server_id < 1) { LM_ERR("invalid machine id\n"); return -1; } if (persistent_state < 0 || persistent_state > 1) { LM_WARN("invalid value for persistent state - presistence disabled\n"); persistent_state = 0; } db_table.len = strlen(db_table.s); cluster_id_col.len = strlen(cluster_id_col.s); machine_id_col.len = strlen(machine_id_col.s); id_col.len = strlen(id_col.s); state_col.len = strlen(state_col.s); url_col.len = strlen(url_col.s); description_col.len = strlen(description_col.s); last_attempt_col.len = strlen(last_attempt_col.s); duration_col.len = strlen(duration_col.s); failed_attempts_col.len = strlen(failed_attempts_col.s); no_tries_col.len = strlen(no_tries_col.s); /* create & init lock */ if ((ref_lock = lock_init_rw()) == NULL) { LM_CRIT("failed to init lock\n"); goto error; } /* data pointer in shm */ tdata = shm_malloc(sizeof *tdata); if (!tdata) { LM_CRIT("failed to get shm mem for data ptr\n"); goto error; } *tdata = NULL; /* bind to the mysql module */ if (db_bind_mod(&clusterer_db_url, &dr_dbf)) { LM_CRIT("cannot bind to database module! " "Did you forget to load a database module ?\n"); goto error; } if (!DB_CAPABILITY(dr_dbf, DB_CAP)) { LM_CRIT("given SQL DB does not provide query types needed by this module!\n"); goto error; } /* register timer */ if (persistent_state) { /* register function to flush changes in state */ if (register_timer("update database", update_db_handler, NULL, prob_interval, TIMER_FLAG_DELAY_ON_DELAY) < 0) { LM_CRIT("unable to synchronize with the database\n"); goto error; } } if (register_timer("update servers", update_nodes_handler, NULL, prob_interval, TIMER_FLAG_DELAY_ON_DELAY) < 0) { LM_CRIT("unable to update status for incoming clients\n"); goto error; } /* everything is OK */ return 0; error: if (ref_lock) { lock_destroy_rw(ref_lock); ref_lock = 0; } if (tdata) { shm_free(tdata); tdata = 0; } return -1; } /* initialize child */ static int child_init(int rank) { LM_DBG("initializing child %d\n", rank); if (rank == PROC_TCP_MAIN || rank == PROC_BIN) return 0; /* init DB connection */ if ((db_hdl = dr_dbf.init(&clusterer_db_url)) == 0) { LM_CRIT("cannot initialize database connection\n"); return -1; } /* child 1 load the routing info */ if ((rank == 1) && reload_data() != 0) { LM_CRIT("failed to load routing data\n"); return -1; } /* use db_table */ if (dr_dbf.use_table(db_hdl, &db_table) < 0) { LM_ERR("cannot select table \"%.*s\"\n", db_table.len, db_table.s); return -1; } return 0; } static void update_nodes_handler(unsigned int ticks, void *param) { /* data */ table_entry_t *head_table; table_entry_info_t *info; table_entry_value_t *value; struct module_timestamp *head; uint64_t ctime; if(clusterer_modules == NULL) return; ctime = time(0); lock_start_write(ref_lock); head_table = *tdata; while (head_table != NULL) { info = head_table->info; while (info != NULL) { value = info->value; while (value != NULL) { head = value->in_timestamps; while (head != NULL) { if (head->state == CLUSTERER_STATE_PROBE && (ctime - head->timestamp) > head->up->timeout) { head->up->cb(SERVER_TIMEOUT, NULL, value->id); head->timestamp = head->timestamp + head->up->timeout; head->state = CLUSTERER_STATE_OFF; } if (head->state == CLUSTERER_STATE_OFF && (ctime - head->timestamp) > head->up->duration) { LM_DBG("node c_id %d m_id %d is up again\n", head_table->cluster_id, value->machine_id); head->state = CLUSTERER_STATE_PROBE; head->timestamp = ctime; } head = head->next; } value = value->next; } info = info->next; } head_table = head_table->next; } lock_stop_write(ref_lock); } /* synchronize backend with the db */ static void update_db_handler(unsigned int ticks, void* param) { /* data */ table_entry_t *head_table; table_entry_value_t *value; table_entry_info_t *info; /* columns to be compared ( clusterer_id_col ) */ db_key_t key_cmp; /* with values */ db_val_t val_cmp; /* columns to be set */ db_key_t key_set[3]; /* with values */ db_val_t val_set[3]; int i; CON_OR_RESET(db_hdl); /* table to use*/ if (dr_dbf.use_table(db_hdl, &db_table) < 0) { LM_ERR("cannot select table \"%.*s\"\n", db_table.len, db_table.s); return; } val_cmp.type = DB_INT; val_cmp.nul = 0; for (i = 0; i < 2; ++i) { val_set[i].type = DB_INT; val_set[i].nul = 0; } val_set[2].type = DB_BIGINT; val_set[2].nul = 0; key_cmp = &id_col; key_set[0] = &state_col; key_set[1] = &no_tries_col; key_set[2] = &last_attempt_col; lock_start_write(ref_lock); head_table = *tdata; /* iterating through backend storage to find all data that * must be synchronized with the db */ while (head_table != NULL) { info = head_table->info; while (info != NULL) { value = info->value; while (value != NULL) { if (value->dirty_bit == 1) { LM_DBG("setting row with primary key %d the status %d\n", value->id, value->state); val_cmp.val.int_val = value->id; val_set[0].val.int_val = value->state; val_set[1].val.int_val = value->no_tries; val_set[2].val.int_val = value->last_attempt; /* updating */ if (dr_dbf.update(db_hdl, &key_cmp, &op_eq, &val_cmp, key_set, val_set, 1, 3) < 0) { LM_ERR("DB update failed\n"); } else { /* only if the query is successful the data is synchronized */ value->dirty_bit = 0; } } value = value->next; } info = info->next; } head_table = head_table->next; } lock_stop_write(ref_lock); } /* add a new information in the backend list*/ int add_info(table_entry_t **data, int *int_vals, unsigned long last_attempt, char **str_vals) { char *host; int hlen, port; struct hostent *he; struct module_list *module; struct module_timestamp *new_timestamp; uint64_t ctime; int proto; int cluster_id; table_entry_t *head = NULL; table_entry_info_t *info_head = NULL; table_entry_value_t *value = NULL; str st; char *url; char *description; if (int_vals[INT_VALS_MACHINE_ID_COL] == server_id) { return 0; } url = str_vals[STR_VALS_URL_COL]; if (url == NULL) { LM_ERR("no path specified\n"); goto error; } if (parse_phostport(url, strlen(url), &host, &hlen, &port, &proto) < 0) { LM_ERR("Bad replication destination IP!\n"); goto error; } if (proto == PROTO_NONE) proto = PROTO_UDP; cluster_id = int_vals[INT_VALS_CLUSTER_ID_COL]; for (head = *data; head; head = head->next) { if (head->cluster_id == cluster_id) { info_head = head->info; while (info_head && info_head->proto != proto) info_head = info_head->next; if (!info_head) { info_head = shm_malloc(sizeof *info_head); if (!info_head) { LM_ERR("no more shm memory\n"); goto error; } info_head->proto = proto; info_head->next = head->info; info_head->value = NULL; head->info = info_head; } break; } } if (!head) { head = shm_malloc(sizeof *head); if (!head) { LM_ERR("no more shm memory\n"); goto error; } head->cluster_id = cluster_id; head->info = shm_malloc(sizeof(table_entry_info_t)); if (!head->info) { LM_ERR("no more shm memory\n"); goto error; } head->info->proto = proto; head->info->next = NULL; head->info->value = NULL; info_head = head->info; info_head->proto = proto; head->next = *data; *data = head; } /* allocating memory*/ value = shm_malloc(sizeof *value); if (!value) { LM_ERR("no more shm memory\n"); goto error; } value->machine_id = int_vals[INT_VALS_MACHINE_ID_COL]; value->id = int_vals[INT_VALS_CLUSTERER_ID_COL]; value->state = int_vals[INT_VALS_STATE_COL]; value->last_attempt = last_attempt; value->duration = int_vals[INT_VALS_DURATION_COL]; value->failed_attempts = int_vals[INT_VALS_FAILED_ATTEMPTS_COL]; value->no_tries = int_vals[INT_VALS_NO_TRIES_COL]; value->dirty_bit = 0; value->prev_no_tries = -1; value->in_timestamps = NULL; description = str_vals[STR_VALS_DESCRIPTION_COL]; value->path.s = shm_malloc(strlen(url) * sizeof(char)); if (!value->path.s) { LM_ERR("insufficient shm memory\n"); goto error; } st.s = host; st.len = hlen; he = sip_resolvehost(&st, (unsigned short *) &port, (unsigned short *) &proto, 0, 0); if (!he) { LM_ERR("Cannot resolve host: %.*s\n", hlen, host); goto error; } hostent2su(&value->addr, he, 0, port); value->path.len = strlen(url); memcpy(value->path.s, url, value->path.len); if (strlen(description) != 0) { value->description.len = strlen(description); value->description.s = shm_malloc(value->description.len * sizeof(char)); if (value->description.s == NULL) { LM_ERR("no more shm memory\n"); goto error; } memcpy(value->description.s, description, value->description.len); } else { value->description.s = NULL; value->description.len = 0; } ctime = time(0); for (module = clusterer_modules; module; module = module->next) { if (cluster_id == module->accept_cluster_id && proto == module->proto) { new_timestamp = create_module_timestamp(ctime, module); if (new_timestamp == NULL) break; new_timestamp->next = value->in_timestamps; value->in_timestamps = new_timestamp; } } value->next = info_head->value; info_head->value = value; /* everything ok */ return 0; error: if (value) { if (value->description.s) shm_free(value->description.s); if (value->path.s) shm_free(value->path.s); shm_free(value); } if (info_head) { if (info_head->value == NULL) { if (head != NULL) head->info = head->info->next; shm_free(info_head); } } if (head) { if (head->info == NULL) { *tdata = (*tdata)->next; shm_free(head); } } return -1; } /* loads data from the db */ table_entry_t* load_info(db_func_t *dr_dbf, db_con_t* db_hdl, str *db_table) { int int_vals[7]; char *str_vals[2]; int no_of_results; int i, n; int no_rows = 5; int db_cols = 10; unsigned long last_attempt; static db_key_t clusterer_machine_id_key = &machine_id_col; static db_val_t clusterer_machine_id_value = { .type = DB_INT, .nul = 0, }; VAL_INT(&clusterer_machine_id_value) = server_id; /* the columns from the db table */ db_key_t columns[10]; /* result from a db query */ db_res_t* res; /* a row from the db table */ db_row_t* row; /* the processed result */ table_entry_t *data; res = 0; data = 0; columns[0] = &cluster_id_col; columns[1] = &machine_id_col; columns[2] = &state_col; columns[3] = &description_col; columns[4] = &url_col; columns[5] = &id_col; columns[6] = &last_attempt_col; columns[7] = &failed_attempts_col; columns[8] = &no_tries_col; columns[9] = &duration_col; CON_OR_RESET(db_hdl); /* checking if the table version is up to date*/ if (db_check_table_version(dr_dbf, db_hdl, db_table, 1/*version*/) != 0) goto error; /* read data */ if (dr_dbf->use_table(db_hdl, db_table) < 0) { LM_ERR("cannot select table \"%.*s\"\n", db_table->len, db_table->s); goto error; } LM_DBG("DB query - retrieve the clusters list" "in which the specified server runs\n"); /* first we see in which clusters the specified server runs*/ if (dr_dbf->query(db_hdl, &clusterer_machine_id_key, &op_eq, &clusterer_machine_id_value, columns, 1, 1, 0, &res) < 0) { LM_ERR("DB query failed - cannot retrieve the clusters list in which" " the specified server runs\n"); goto error; } LM_DBG("%d rows found in %.*s\n", RES_ROW_N(res), db_table->len, db_table->s); if (RES_ROW_N(res) == 0) { LM_WARN("No machines found in cluster %d\n", server_id); return 0; } clusterer_cluster_id_key = pkg_realloc(clusterer_cluster_id_key, RES_ROW_N(res) * sizeof(db_key_t)); if (!clusterer_cluster_id_key) { LM_ERR("no more pkg memory\n"); goto error; } for (i = 0; i < RES_ROW_N(res); i++) clusterer_cluster_id_key[i] = &cluster_id_col; clusterer_cluster_id_value = pkg_realloc(clusterer_cluster_id_value, RES_ROW_N(res) * sizeof(db_val_t)); if (!clusterer_cluster_id_value) { LM_ERR("no more pkg memory\n"); goto error; } for (i = 0; i < RES_ROW_N(res); i++) { VAL_TYPE(clusterer_cluster_id_value + i) = DB_INT; VAL_NULL(clusterer_cluster_id_value + i) = 0; } for (i = 0; i < RES_ROW_N(res); i++) { row = RES_ROWS(res) + i; check_val(cluster_id_col, ROW_VALUES(row), DB_INT, 1, 0); VAL_INT(clusterer_cluster_id_value + i) = VAL_INT(ROW_VALUES(row)); } no_of_results = RES_ROW_N(res); dr_dbf->free_result(db_hdl, res); res = 0; LM_DBG("DB query - retrieve valid connections\n"); /* fetch is the best strategy */ CON_USE_OR_OP(db_hdl); if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if (dr_dbf->query(db_hdl, clusterer_cluster_id_key, 0, clusterer_cluster_id_value, columns, no_of_results, db_cols, 0, 0) < 0) { LM_ERR("DB query failed - retrieve valid connections \n"); goto error; } no_rows = estimate_available_rows(4 + 4 + 4 + 64 + 4 + 45 + 4 + 8 + 4 + 4, db_cols); if (no_rows == 0) no_rows = 5; if (dr_dbf->fetch_result(db_hdl, &res, no_rows) < 0) { LM_ERR("Error fetching rows\n"); goto error; } } else { if (dr_dbf->query(db_hdl, clusterer_cluster_id_key, 0, clusterer_cluster_id_value, columns, no_of_results, db_cols, 0, &res) < 0) { LM_ERR("DB query failed - retrieve valid connections\n"); goto error; } } LM_DBG("%d rows found in %.*s\n", RES_ROW_N(res), db_table->len, db_table->s); n = 0; do { for (i = 0; i < RES_ROW_N(res); i++) { row = RES_ROWS(res) + i; /* CLUSTER ID column */ check_val(cluster_id_col, ROW_VALUES(row), DB_INT, 1, 0); int_vals[INT_VALS_CLUSTER_ID_COL] = VAL_INT(ROW_VALUES(row)); /* MACHINE ID column */ check_val(machine_id_col, ROW_VALUES(row) + 1, DB_INT, 1, 0); int_vals[INT_VALS_MACHINE_ID_COL] = VAL_INT(ROW_VALUES(row) + 1); /* STATE column */ check_val(state_col, ROW_VALUES(row) + 2, DB_INT, 1, 0); int_vals[INT_VALS_STATE_COL] = VAL_INT(ROW_VALUES(row) + 2); /* DESCRIPTION column */ check_val(description_col, ROW_VALUES(row) + 3, DB_STRING, 0, 0); str_vals[STR_VALS_DESCRIPTION_COL] = (char*) VAL_STRING(ROW_VALUES(row) + 3); /* URL column */ check_val(url_col, ROW_VALUES(row) + 4, DB_STRING, 1, 1); str_vals[STR_VALS_URL_COL] = (char*) VAL_STRING(ROW_VALUES(row) + 4); /* CLUSTERER_ID column */ check_val(id_col, ROW_VALUES(row) + 5, DB_INT, 1, 0); int_vals[INT_VALS_CLUSTERER_ID_COL] = VAL_INT(ROW_VALUES(row) + 5); /* LAST_ATTEMPT column */ check_val(last_attempt_col, ROW_VALUES(row) + 6, DB_BIGINT, 1, 0); last_attempt = VAL_BIGINT(ROW_VALUES(row) + 6); /* FAILED_ATTEMPTS column */ check_val(failed_attempts_col, ROW_VALUES(row) + 7, DB_INT, 1, 0); int_vals[INT_VALS_FAILED_ATTEMPTS_COL] = VAL_INT(ROW_VALUES(row) + 7); /* NO_TRIES column */ check_val(no_tries_col, ROW_VALUES(row) + 8, DB_INT, 1, 0); int_vals[INT_VALS_NO_TRIES_COL] = VAL_INT(ROW_VALUES(row) + 8); /* DURATION column */ check_val(duration_col, ROW_VALUES(row) + 9, DB_INT, 1, 0); int_vals[INT_VALS_DURATION_COL] = VAL_INT(ROW_VALUES(row) + 9); /* store data */ if (add_info(&data, int_vals, last_attempt, str_vals) < 0) { LM_DBG("error while adding info to shm\n"); goto error; } LM_DBG("machine id %d\n", int_vals[0]); LM_DBG("cluster id %d\n", int_vals[1]); LM_DBG("state %d\n", int_vals[2]); LM_DBG("clusterer_id %d\n", int_vals[3]); LM_DBG("description %s\n", str_vals[0]); LM_DBG("url %s\n", str_vals[1]); n++; } if (n == 1) LM_WARN("The server is the only one in the cluster\n"); if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if (dr_dbf->fetch_result(db_hdl, &res, no_rows) < 0) { LM_ERR("fetching rows (1)\n"); goto error; } } else { break; } } while (RES_ROW_N(res) > 0); LM_DBG("%d records found in %.*s\n", n, db_table->len, db_table->s); dr_dbf->free_result(db_hdl, res); res = 0; return data; error: if (res) dr_dbf->free_result(db_hdl, res); if (data) free_data(data); data = NULL; return 0; } /* deallocates data */ void free_data(table_entry_t *data) { table_entry_t *tmp_entry; table_entry_info_t *info; table_entry_info_t *tmp_info; table_entry_value_t *value; table_entry_value_t *tmp_value; struct module_timestamp *timestamp; struct module_timestamp *tmp_timestamp; while (data != NULL) { tmp_entry = data; data = data->next; info = tmp_entry->info; while (info != NULL) { value = info->value; while (value != NULL) { if (value->path.s) shm_free(value->path.s); if (value->description.s) shm_free(value->description.s); timestamp = value->in_timestamps; while (timestamp != NULL) { tmp_timestamp = timestamp; timestamp = timestamp->next; shm_free(tmp_timestamp); } tmp_value = value; value = value->next; shm_free(tmp_value); } tmp_info = info; info = info->next; shm_free(tmp_info); } shm_free(tmp_entry); } } /* reloads data from the db */ static int reload_data(void) { struct module_list* modules; table_entry_t *new_data; table_entry_t *old_data; table_entry_t *new_head; table_entry_t *old_head; table_entry_info_t *new_info; table_entry_info_t *old_info; table_entry_value_t *new_value; table_entry_value_t *old_value; struct module_timestamp *aux; new_data = load_info(&dr_dbf, db_hdl, &db_table); if (!new_data) { LM_CRIT("failed to load routing info\n"); return -1; } lock_start_write(ref_lock); /* no more active readers -> do the swapping */ for (old_head = *tdata; old_head; old_head = old_head->next) { for (new_head = new_data; new_head; new_head = new_head->next) { if (old_head->cluster_id != new_head->cluster_id) continue; for (old_info = old_head->info; old_info; old_info = old_info->next) { for (new_info = new_head->info; new_info; new_info = new_info->next) { if (old_info->proto != new_info->proto) continue; for (old_value = old_info->value; old_value; old_value = old_value->next) { for (new_value = new_info->value; new_value; new_value = new_value->next) { if (su_cmp(&new_value->addr, &old_value->addr)) { aux = new_value->in_timestamps; new_value->in_timestamps = old_value->in_timestamps; old_value->in_timestamps = aux; break; } } } } } } } old_data = *tdata; *tdata = new_data; for (modules = clusterer_modules; modules; modules = modules->next) modules->values = clusterer_find_nodes(modules->accept_cluster_id, modules->proto); lock_stop_write(ref_lock); /* free old data */ if (old_data) free_data(old_data); return 0; } /* destroy function */ static void destroy(void) { struct module_list *tmp; /* close DB connection */ if (db_hdl) { dr_dbf.close(db_hdl); db_hdl = NULL; } /* destroy data */ if (tdata) { if (*tdata) free_data(*tdata); shm_free(tdata); tdata = NULL; } while (clusterer_modules) { tmp = clusterer_modules; clusterer_modules = clusterer_modules->next; shm_free(tmp); } /* destroy lock */ if (ref_lock) { lock_destroy_rw(ref_lock); ref_lock = NULL; } } /* reloads data from the db */ static struct mi_root* clusterer_reload(struct mi_root* root, void *param) { LM_INFO("reload data MI command received!\n"); /* first if in persistent mode we synchronize data */ if (persistent_state) update_db_handler(0, NULL); if (reload_data() < 0) { LM_CRIT("failed to load routing data\n"); return init_mi_tree(500, "Failed to reload", 16); } return init_mi_tree(200, MI_SSTR(MI_OK)); } static void temp_disable_machine(table_entry_value_t *head) { head->dirty_bit = 1; head->no_tries++; head->last_attempt = time(0); if (head->no_tries == head->failed_attempts) { head->state = CLUSTERER_STATE_OFF; } } static struct module_timestamp* create_module_timestamp(int ctime, struct module_list *module) { struct module_timestamp *new_node; new_node = shm_malloc(sizeof *new_node); if (!new_node) { LM_ERR("not enough shm memory"); goto error; } new_node->state = CLUSTERER_STATE_PROBE; new_node->timestamp = ctime; new_node->up = module; new_node->next = NULL; return new_node; error: return NULL; } static int set_in_timestamp(struct module_list *module, int machine_id) { table_entry_value_t *values; int is_ok = 1; uint64_t ctime = time(0); struct module_timestamp *head; LM_DBG("setting timestamp for node with c_id %d m_id %d proto%d\n", module->accept_cluster_id, machine_id, module->proto); /* finding the machine */ lock_start_write(ref_lock); /* if the protocol is not specified */ for (values = module->values; values; values = values->next) { if (values->machine_id == machine_id) { is_ok = 0; for (head = values->in_timestamps; head; head = head->next) { if (head->up == module) { if (head->state == CLUSTERER_STATE_OFF) { LM_DBG("state for node with clusterer_id %d is 2\n", values->id); is_ok = -1; } else head->timestamp = ctime; break; } } break; } } lock_stop_write(ref_lock); return is_ok; } /* setting a connection status */ static int set_state(int cluster_id, int machine_id, enum cl_machine_state state, int proto) { table_entry_value_t *head_table; int is_ok = 1; LM_DBG("setting node with c_id %d m_id %d proto %d with state %d\n", cluster_id, machine_id, proto, state); /* finding the machine */ lock_start_write(ref_lock); head_table = clusterer_find_nodes(cluster_id, proto); /* if the protocol is not specified */ for (; head_table; head_table = head_table->next) { if (head_table->machine_id == machine_id) { head_table->dirty_bit = 1; if (state == CLUSTERER_STATE_OFF) { head_table->no_tries++; head_table->last_attempt = time(0); if (head_table->no_tries == head_table->failed_attempts) { head_table->state = CLUSTERER_STATE_OFF; } } else { head_table->state = state; } is_ok = 0; break; } } lock_stop_write(ref_lock); return is_ok; } /* setting a connection status command function*/ static struct mi_root* clusterer_set_status(struct mi_root *cmd, void *param) { unsigned int cluster_id; unsigned int machine_id; unsigned int state; int proto; int rc; struct mi_node *node; struct mi_node *prot_node; LM_INFO("set status MI command received!\n"); if (!cmd || !cmd->node.kids || !cmd->node.kids->value.s) { LM_DBG("no values specified\n"); return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM)); } node = cmd->node.kids; if (!node->next || !node->next->value.s) { LM_DBG("only one value specified\n"); return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM)); } if (!node->next->next || !node->next->next->value.s) { LM_DBG("no state specified\n"); return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM)); } rc = str2int(&node->value, &cluster_id); if (rc == -1 || cluster_id == 0) { LM_DBG("the cluster_id parameter is not a valid digit\n"); return init_mi_tree(400, MI_SSTR(MI_BAD_PARM)); } rc = str2int(&node->next->value, &machine_id); if (rc == -1 || machine_id == 0) { LM_DBG("the machine_id parameter is not a valid digit\n"); return init_mi_tree(400, MI_SSTR(MI_BAD_PARM)); } rc = str2int(&node->next->next->value, &state); if (rc == -1 || (state != CLUSTERER_STATE_ON && state != CLUSTERER_STATE_PROBE)) { LM_DBG("the state parameter is not valid\n"); return init_mi_tree(400, MI_SSTR(MI_BAD_PARM)); } prot_node = node->next->next->next; if (!prot_node || !prot_node->value.s) { LM_DBG("the protocol parameter is missing\n"); return init_mi_tree(400, MI_SSTR(MI_BAD_PARM)); } if (parse_proto((unsigned char*) prot_node->value.s, prot_node->value.len, &proto) < 0) { LM_DBG("the protocol parameter is not valid\n"); return init_mi_tree(400, MI_SSTR(MI_BAD_PARM)); } rc = set_state(cluster_id, machine_id, state, proto); if (rc == -1) { LM_DBG("cluster id or machine id are not smaller than 1\n"); return init_mi_tree(400, MI_SSTR(MI_BAD_PARM)); } if (rc == 1) { LM_DBG("there is no machine id %d in the cluster %d\n", machine_id, cluster_id); } return init_mi_tree(200, MI_SSTR(MI_OK)); } /* lists all valid connections */ static struct mi_root * clusterer_list(struct mi_root *cmd_tree, void *param) { table_entry_t *head_table; table_entry_info_t *info; table_entry_value_t *value; struct mi_root *rpl_tree = NULL; struct mi_node *node = NULL; struct mi_node *node_s = NULL; struct mi_attr* attr; str val; rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (!rpl_tree) return NULL; rpl_tree->node.flags |= MI_IS_ARRAY; lock_start_read(ref_lock); /* iterate through clusters */ for (head_table = *tdata; head_table; head_table = head_table->next) { val.s = int2str(head_table->cluster_id, &val.len); node = add_mi_node_child(&rpl_tree->node, MI_DUP_VALUE|MI_IS_ARRAY, MI_SSTR("Cluster"), val.s, val.len); if (!node) goto error; /* iterate through supported protocols */ for (info = head_table->info; info; info = info->next) { /* iterate through servers */ for (value = info->value; value; value = value->next) { val.s = int2str(value->machine_id, &val.len); node_s = add_mi_node_child(node, MI_DUP_VALUE, MI_SSTR("Server"), val.s, val.len); if (!node) goto error; val.s = int2str(value->id, &val.len); attr = add_mi_attr(node_s, MI_DUP_VALUE, MI_SSTR("DB_ID"), val.s, val.len); if (!attr) goto error; attr = add_mi_attr(node_s, MI_DUP_VALUE, MI_SSTR("URL"), value->path.s, value->path.len); if (!attr) goto error; val.s = int2str(value->state, &val.len); attr = add_mi_attr(node_s, MI_DUP_VALUE, MI_SSTR("State"), val.s, val.len); if (!attr) goto error; val.s = int2str(value->last_attempt, &val.len); attr = add_mi_attr(node_s, MI_DUP_VALUE, MI_SSTR("Last_failed_attempt"), val.s, val.len); if (!attr) goto error; val.s = int2str(value->failed_attempts, &val.len); attr = add_mi_attr(node_s, MI_DUP_VALUE, MI_SSTR("Max_failed_attempts"), val.s, val.len); if (!attr) goto error; val.s = int2str(value->no_tries, &val.len); attr = add_mi_attr(node_s, MI_DUP_VALUE, MI_SSTR("no_tries"), val.s, val.len); if (!attr) goto error; val.s = int2str(value->duration, &val.len); attr = add_mi_attr(node_s, MI_DUP_VALUE, MI_SSTR("Seconds_until_enabling"), val.s, val.len); if (!attr) goto error; if (value->description.s) attr = add_mi_attr(node_s, MI_DUP_VALUE, MI_SSTR("Description"), value->description.s, value->description.len); else attr = add_mi_attr(node_s, MI_DUP_VALUE, MI_SSTR("Description"), "none", 4); if (!attr) goto error; } } } lock_stop_read(ref_lock); return rpl_tree; error: lock_stop_read(ref_lock); if (rpl_tree) free_mi_tree(rpl_tree); return NULL; } static void free_node(clusterer_node_t *node) { if (node) { if (node->description.s) pkg_free(node->description.s); pkg_free(node); } } static int add_node(clusterer_node_t **nodes, table_entry_value_t *head, int proto) { clusterer_node_t *new_node = NULL; struct ip_addr ip; new_node = pkg_malloc(sizeof *new_node); if (!new_node) { LM_ERR("no more pkg memory\n"); goto error; } new_node->next = NULL; new_node->description.s = NULL; new_node->machine_id = head->machine_id; new_node->state = head->state; new_node->proto = proto; memcpy(&new_node->addr, &head->addr, sizeof(head->addr)); new_node->description.s = pkg_malloc(head->description.len * sizeof(char)); if (!new_node->description.s) { LM_ERR("no more pkg memory\n"); goto error; } memcpy(new_node->description.s, head->description.s, head->description.len); su2ip_addr(&ip, &new_node->addr); new_node->description.len = head->description.len; if (*nodes) new_node->next = *nodes; *nodes = new_node; return 0; error: free_node(new_node); return -1; } static void free_nodes(clusterer_node_t *nodes) { clusterer_node_t *tmp; LM_DBG("freeing all the nodes\n"); while (nodes) { tmp = nodes; nodes = nodes->next; free_node(tmp); } } static clusterer_node_t* get_nodes(int cluster_id, int proto) { clusterer_node_t* tmp; clusterer_node_t* nodes = NULL; table_entry_value_t* head; unsigned long ctime = time(0); lock_start_read(ref_lock); head = clusterer_find_nodes(cluster_id, proto); for (; head; head = head->next) { if (head->state == 1) { if (head->prev_no_tries != -1 && head->no_tries > 0 && head->prev_no_tries == head->no_tries) { head->no_tries = 0; } head->prev_no_tries = head->no_tries; } if (head->state == 2) { if ((ctime - head->last_attempt) >= head->duration) { head->last_attempt = ctime; head->state = 1; head->no_tries = 0; } } if (head->state == 1 && add_node(&nodes, head, proto) < 0) { goto error; } } lock_stop_read(ref_lock); return nodes; error: lock_stop_read(ref_lock); while (nodes) { tmp = nodes; nodes = nodes->next; free_node(tmp); } return NULL; } int clusterer_check(int cluster_id, union sockaddr_union* su, int machine_id, int proto) { int rc = 0; table_entry_value_t *head; lock_start_read(ref_lock); head = clusterer_find_nodes(cluster_id, proto); for (; head; head = head->next) { if (su_ip_cmp(su, &head->addr) && head->machine_id == machine_id) { rc = 1; break; } } lock_stop_read(ref_lock); return rc; } static int get_my_id(void) { return server_id; } static table_entry_value_t *clusterer_find_nodes(int cluster_id, int proto) { table_entry_t *head; table_entry_info_t *info = NULL; table_entry_value_t *value = NULL; head = *tdata; while (head && head->cluster_id != cluster_id) head = head->next; if (head) { info = head->info; while (info && info->proto != proto) info = info->next; if (info) value = info->value; } return value; } static int send_to(int cluster_id, int proto) { table_entry_value_t *value; str send_buffer; unsigned long ctime = time(0); int ok = -1; if (proto == PROTO_BIN) bin_get_buffer(&send_buffer); lock_start_read(ref_lock); value = clusterer_find_nodes(cluster_id, proto); for (; value; value = value->next) { ok = 0; if (value->state == 1) { if (value->prev_no_tries != -1 && value->no_tries > 0 && value->prev_no_tries == value->no_tries) { value->no_tries = 0; } value->prev_no_tries = value->no_tries; } if (value->state == 2) { if ((ctime - value->last_attempt) >= value->duration) { value->last_attempt = ctime; value->state = 1; value->no_tries = 0; } } if (value->state == 1) { if (proto == PROTO_BIN) { if (msg_send(NULL, PROTO_BIN, &value->addr, 0, send_buffer.s, send_buffer.len, 0) != 0) { LM_ERR("cannot send message\n"); temp_disable_machine(value); } } } } lock_stop_read(ref_lock); return ok; } static void bin_receive_packets(int packet_type, struct receive_info *ri, void *ptr) { struct module_list *module; unsigned short port; int machine_id; char *ip; int rc; rc = bin_pop_int(&machine_id); if (rc < 0) return; get_su_info(&ri->src_su.s, ip, port); LM_DBG("received bin packet from source: %s:%hu\n", ip, port); module = (struct module_list *) ptr; if (module->auth_check) { if (!clusterer_check(module->accept_cluster_id, &ri->src_su, machine_id, ri->proto)) { get_su_info(&ri->src_su.s, ip, port); LM_WARN("received bin packet from unknown source: %s:%hu\n", ip, port); return; } } rc = set_in_timestamp(module, machine_id); if (rc < 0) { module->cb(SERVER_TEMP_DISABLED, ri, machine_id); return; } module->cb(packet_type, ri, machine_id); } static int cl_register_module(char *mod_name, int proto, void (*cb)(int, struct receive_info *, int), int timeout, int auth_check, int accept_cluster_id) { struct module_list *new_module; LM_DBG("register module %s\n", mod_name); if (auth_check && !accept_cluster_id) { LM_ERR("provided bad cluster_id\n"); return -1; } new_module = shm_malloc(sizeof *new_module); if (!new_module) { LM_ERR("insufficient shm memory\n"); return -1; } new_module->mod_name.len = strlen(mod_name); new_module->mod_name.s = mod_name; new_module->proto = proto; new_module->cb = cb; new_module->timeout = timeout; new_module->auth_check = auth_check; new_module->accept_cluster_id = accept_cluster_id; new_module->duration = 2 * timeout; new_module->next = NULL; switch (proto) { case PROTO_BIN: bin_register_cb(mod_name, bin_receive_packets, new_module); break; default: LM_ERR("unidentified protocol\n"); shm_free(new_module); return -1; } new_module->values = NULL; new_module->next = clusterer_modules; clusterer_modules = new_module; return 0; } int load_clusterer(struct clusterer_binds *binds) { binds->get_nodes = get_nodes; binds->set_state = set_state; binds->free_nodes = free_nodes; binds->check = clusterer_check; binds->get_my_id = get_my_id; binds->send_to = send_to; binds->register_module = cl_register_module; /* everything ok*/ return 1; } opensips-2.2.2/modules/clusterer/clusterer.h000066400000000000000000000044661300170765700212360ustar00rootroot00000000000000#ifndef CLUSTERER_H #define CLUSTERER_H #include "../../str.h" #include "api.h" #define INT_VALS_CLUSTER_ID_COL 0 #define INT_VALS_MACHINE_ID_COL 1 #define INT_VALS_STATE_COL 2 #define STR_VALS_DESCRIPTION_COL 0 #define STR_VALS_URL_COL 1 #define INT_VALS_CLUSTERER_ID_COL 3 #define INT_VALS_FAILED_ATTEMPTS_COL 4 #define INT_VALS_NO_TRIES_COL 5 #define INT_VALS_DURATION_COL 6 extern str clusterer_db_url; extern str db_table; extern str cluster_id_col; extern str machine_id_col; extern int server_id; extern int persistent_state; extern str id_col; extern str last_attempt_col; extern str duration_col; extern str failed_attempts_col; extern str no_tries_col; /* define proper state for the machine */ typedef struct table_entry_ table_entry_t; typedef struct table_entry_info_ table_entry_info_t; typedef struct table_entry_value_ table_entry_value_t; struct module_list{ str mod_name; int proto; void (*cb)(int, struct receive_info *, int); int timeout; int duration; int auth_check; int accept_cluster_id; table_entry_value_t *values; struct module_list *next; }; struct module_timestamp{ enum cl_machine_state state; uint64_t timestamp; struct module_list *up; struct module_timestamp *next; }; struct table_entry_value_{ /* machine id */ int machine_id; /* cluster id */ int id; /* state */ int state; /* dirty bit */ int dirty_bit; /* description string */ str description; /* path */ str path; /* timestamp */ uint64_t last_attempt; /* duration */ int duration; /* previous number of tries */ int prev_no_tries; /* no of tries */ int no_tries; /* failed attempts */ int failed_attempts; /* sock address */ union sockaddr_union addr; /* module list */ struct module_timestamp *in_timestamps; /* linker in list */ table_entry_value_t *next; }; struct table_entry_info_{ /* protocol */ int proto; /* data */ table_entry_value_t *value; /* linker in the list */ table_entry_info_t *next; }; /* data list */ struct table_entry_ { /* clusterer_id */ int cluster_id; /* entry info */ table_entry_info_t *info; /* linker in list */ table_entry_t *next; }; #endif /* CLUSTERER_H */ opensips-2.2.2/modules/clusterer/doc/000077500000000000000000000000001300170765700176105ustar00rootroot00000000000000opensips-2.2.2/modules/clusterer/doc/clusterer.xml000066400000000000000000000017301300170765700223430ustar00rootroot00000000000000 %docentities; ]> CLUSTERER Module &osipsname; Marius Cristian Eseanu OpenSIPS Solutions
eseanu.cristian@gmail.com http://www.opensips.org
2015 &osipssol;
&admin; &devel;
opensips-2.2.2/modules/clusterer/doc/clusterer_admin.xml000066400000000000000000000455261300170765700235260ustar00rootroot00000000000000 &adminguide;
Overview The clusterer module is used to organize multiple &osips; instances into groups that can communicate with each other in order to replicate, share information or perform distributed tasks. The module itself only stores information about the nodes in a group/cluster and provides an interface to check or tune their state and parameters. The distributed logic is performed by different modules that use this interface (i.e. the dialog module can replicate profiles, the ratelimit module can share pipes across multiple instances, etc). Provisioning the nodes within a cluster is done over the database but, for efficiency, the node-related information is cached into &osips; memory. This information can be checked or updated by sending commands over the MI interface. The clusterer module can also detect node availability, by using certain parameters provisioned in the database. When a destination is not reachable, it is put in a probing state - it is periodically pinged until a maximum number of failed attempts is reached, when it is marked as temporarily disabled. It stays in this state for a period (equal to the duration parameter), and then the number of retries reset to 0 and the node is considered up again. Modules (like dialog or ratelimit can use nodes within the cluster to replicate information. They also register a specific timeout to invalidate data from specific nodes, in case no updates have been within an interval. The clusterer notifies the module if the timeout is reached and puts the node in a temporary disabled state. If a packet has arrived for a temporary disabled server, the packet is dropped and a temporary disabled notification is sent to the registered module. After the disabled period (2 * timeout) has passed, the server is up again. By default, the state information of the nodes is not persistent. To make them persistent via a database, one must set the persistent_mode parameter.
Dependencies
&osips; Modules The following modules must be loaded before this module: a database module.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>db_url</varname> The database url. Default value is NULL. Set <varname>db_url</varname> parameter ... modparam("clusterer", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") ...
<varname>db_table</varname> The name of the table storing the clustering information. Default value is clusterer. Set <varname>db_table</varname> parameter ... modparam("clusterer", "db_table", "clusterer") ...
<varname>server_id</varname> It specifies the server_id the current instance has. this field should correspond with one of the machine_id fields in the database. Default value is 0. Set <varname>server_id</varname> parameter ... modparam("clusterer", "server_id", 2) ...
<varname>persistent_mode</varname> If persistent mode is enabled, a timer synchronizes the information used by the clusterer module and the information stored in the database. Default value is 0 (disabled). Set <varname>persistent_mode</varname> parameter ... modparam("clusterer", "persistent_mode", 1) ...
<varname>cluster_id_col</varname> The name of the column in the db_table where the cluster_id is stored. Default value is cluster_id. Set <varname>cluster_id_col</varname> parameter ... modparam("clusterer", "cluster_id_col", "cluster_id") ...
<varname>machine_id_col</varname> The name of the column in the db_table where the machine_id is stored. Default value is machine_id. Set <varname>machine_id_col</varname> parameter ... modparam("clusterer", "machine_id_col", "machine_id") ...
<varname>clusterer_id_col</varname> The name of the column in the db_table where the machine_id is stored. Default value is id. Set <varname>clusterer_id_col</varname> parameter ... modparam("clusterer", "clusterer_id_col", "clusterer_id") ...
<varname>state_col</varname> The name of the column in the db_table where the state is stored. Default value is state. Set <varname>state_col</varname> parameter ... modparam("clusterer", "state_col", "state") ...
<varname>url_col</varname> The name of the column in the db_table where the url is stored. Default value is url. Set <varname>url_col</varname> parameter ... modparam("clusterer", "url_col", "url") ...
<varname>description_col</varname> The name of the column in the db_table where the machine's description is stored. Default value is description. Set <varname>description_col</varname> parameter ... modparam("clusterer", "description_col", "description") ...
<varname>failed_attempts_col</varname> The name of the column in the db_table where the maximum allowed number of failed attempts is stored. Default value is failed_attempts. Set <varname>failed_attempts_col</varname> parameter ... modparam("clusterer", "failed_attempts_col", "failed_attempts") ...
<varname>last_attempt_col</varname> The name of the column in the db_table where the UNIX time of last last failed attempt is stored. Default value is last_attempt. Set <varname>last_attempt_col</varname> parameter ... modparam("clusterer", "last_attempt_col", "last_attempt") ...
<varname>duration_col</varname> The name of the column in the db_table where the duration of a machine belonging to a certain cluster temporary disabled state is stored. Default value is duration. Set <varname>duration_col</varname> parameter ... modparam("clusterer", "duration_col", "duration") ...
<varname>no_tries_col</varname> The name of the column in the db_table where the number of failed connecting tries is stored. Default value is no_tries. Set <varname>no_tries_col</varname> parameter ... modparam("clusterer", "no_tries_col", "no_tries") ...
Exported Functions none
Exported MI Functions
<function moreinfo="none">clusterer_reload</function> Reloads data from the clusterer database. If the persistent mode is disabled the changes made to the locally stored data are lost. Name: clusterer_reload Parameters:none MI FIFO Command Format: :clusterer_reload _empty_line_
<function moreinfo="none">clusterer_list</function> Lists in a table format all the data stored in &osips; cache. Name: clusterer_list Parameters:none MI FIFO Command Format: :clusterer_list _empty_line_
<function moreinfo="none">clusterer_set_status</function> Sets the status(UP, DOWN) of a machine belonging to a certain cluster. Name: clusterer_set_status Parameters: cluster_id - indicates the id of the cluster. machine_id - indicates the id of the machine. status - indicates the new status( 0 - permanent down, 1 - up). protocol - indicates the protocol. MI FIFO Command Format: :clusterer_set_status: 1 2 0 bin _empty_line_
Usage Example This section provides an usage example for replicating ratelimit pipes between two &osips; instances. It uses the clusterer module to manage the replicating nodes, and the proto_bin modules to send the replicated information. The setup topology is simple: we have two &osips; nodes running on two separate machines (although they could run on the same machine as well): Node A has IP 192.168.0.5 and Node B has IP 192.168.0.6. Both have, besides the traffic listeners (UDP, TCP, etc.), bin listeners bound on port 5566. These listeners will be used by the ratelimit module to replicate the pipes. Therefore, we have to provision them in the clusterer table. Example database content - clusterer table +----+------------+------------+----------------------+-------+--------------+-----------------+----------+----------+-------------+ | id | cluster_id | machine_id | url | state | last_attempt | failed_attempts | no_tries | duration | description | +----+------------+------------+----------------------+-------+--------------+-----------------+----------+----------+-------------+ | 1 | 1 | 1 | bin:192.168.0.5:5566 | 1 | 0 | 0 | 0 | 30 | Node A | | 2 | 1 | 2 | bin:192.168.0.6:5566 | 1 | 0 | 0 | 0 | 30 | Node B | +----+------------+------------+----------------------+-------+--------------+-----------------+----------+----------+-------------+ cluster_id - this column represents the identifier of the cluster. All nodes within a group/cluster should have the same id (in our example, both nodes have ID 1) machine_id - this represents the identifier of the machine/node, and each instance within a cluster should have a different ID. In our example, Node A will have ID 1, and Node B ID 2 url - this indicates the URL where the instance will receive the replication information. In our example, each node will receive the date over the bin protocol state - this is the state of the machine: 1 means on, 0 means off, and 2 means it is in probing. Note that if you want the node to be active right away, you have to set it in state 1 last_attempt, failed_attempts and no_tries - are fields used for the probing mechanisms, and should be set to 0 by default. They are automatically updated by the clusterer module if the persistent_mode parameter is set to 1 duration - is used to specify the period a node stays in the temporary disabled state. In our example, if the node does not respond, it is disabled for 30 seconds before retrying to send data again description - is an opaque value used to identify the node After provisioning the two nodes in the database, we have to configure the two instances of &osips;. First, we configure Node A: <emphasis>Node A</emphasis> configuration ... listen = bin:192.168.0.5:5566 # bin listener for Node A loadmodule "proto_bin.so" loadmodule "clusterer.so" modparam("clusterer", "db_url", "mysql://opensips@192.168.0.7/opensips") modparam("clusterer", "server_id", 1) # machine_id for Node A loadmodule "ratelimit.so" # replicate pipes to cluster id 1 modparam("ratelimit", "replicate_pipes_to", 1) # accept replicated data from nodes within cluster 1 modparam("ratelimit", "accept_pipes_from", 1) # if a node does not reply in a 5 seconds interval, #the information from that node is invalidated modparam("ratelimit", "accept_pipes_timeout", 5) ... Similarly, the configuration for Node B is as follows: <emphasis>Node B</emphasis> configuration ... listen = bin:192.168.0.6:5566 # bin listener for Node B loadmodule "proto_bin.so" loadmodule "clusterer.so" # ideally, use the same database for both nodes modparam("clusterer", "db_url", "mysql://opensips@192.168.0.7/opensips") modparam("clusterer", "server_id", 2) # machine_id for Node B loadmodule "ratelimit.so" # replicate pipes to cluster id 1 modparam("ratelimit", "replicate_pipes_to", 1) # accept replicated data from nodes within cluster 1 modparam("ratelimit", "accept_pipes_from", 1) # if a node does not reply in a 5 seconds interval, # the information from that node is invalidated modparam("ratelimit", "accept_pipes_timeout", 5) ...
Note that the server_id parameter for Node B is 2. Starting the two &osips; instances with the above configurations provides your platform the ability to used shared ratelimit pipes in a very efficient and scalable way.
opensips-2.2.2/modules/clusterer/doc/clusterer_devel.xml000066400000000000000000000213431300170765700235240ustar00rootroot00000000000000 &develguide;
Available Functions
<function moreinfo="none">get_nodes(cluster_id, proto)</function> The function will return all a copy of all the needed information from the nodes (machine_id, state, description, sock address) stored in shm, whos state is up(1) and have a certain cluster_id and protocol. This function is usually used for replication purposes. This function returns NULL on error. Meaning of the parameters is as follows: int cluster_id - the cluster id int proto - the protocol <function>get_nodes</function> usage ... get_nodes(cluster_id, proto); ...
<function moreinfo="none">free_nodes(nodes)</function> This function will free the allocated data for the copy. Meaning of the parameters is as follows: clusterer_node_t *nodes - the data returned by the get_nodes function <function>free_nodes</function> usage ... free_nodes(nodes); ...
<function moreinfo="none">set_state(cluster_id, machine_id, state, proto)</function> The function sets the state of a machine belonging to a certain cluster, which have the specified protocol. This function is usually used for replication purposes. Meaning of the parameters is as follows: int cluster_id - cluster_id int machine_id - machine_id int state - the server state int proto - protocol <function>set_state</function> usage ... set_state(1,1,2,PROTO_BIN); ...
<function moreinfo="none">check(cluster_id, sockaddr, server_id, proto)</function> This function is used to check if the source of a receiving packet is known. It returns 1 if the source is known, else it returns 0. Meaning of the parameters is as follows: int cluster_id - cluster id union sockaddr_union* sockaddr - incoming connexion socket address int server_id - incoming connexion server_id int proto - protocol <function>check</function> usage ... check(1, sockaddr, 2, PROTO_BIN) ...
<function moreinfo="none">get_my_id()</function> This function will return the server id's. <function>get_my_id</function> usage ... get_my_id() ...
<function moreinfo="none">send_to(cluster_id, protocol)</function> This function will replicate information to the nodes belonging to a cluster_id that have a specific protocol. Meaning of the parameters is as follows: int cluster_id - cluster_id int protocol - protocol <function>send_to</function> usage ... send_to(cluster_id, protocol) ...
<function moreinfo="none">register_module(module_name, protocol, callback_function, timeout, auth_check, cluster_id)</function> This function registers a module to a certain protocol. It acts like an intermediary: when a valid packet has arrived, if the auth_check parameter is specified then it is checked for authenticity. After that, the timestamps are updated and the callback function from the registered module is called. The clusterer module checks for every registered module if the duration between the last receiving packet and the current time is greater than the module specified timeout. If it is, the servers are temporary disabled for a period of timestamp * 2. If any packets are received for the temporary disabled servers the registered module is notified. Meaning of the parameters is as follows: char *module_name - module name int protocol - protocol void (*callback_function)(int, struct receive_info *, int) - the registered module callback function int timeout - timeput int auth_check - 0 if the authentication check is disabled, 1 if the authentication check is enabled int cluster_id - cluster_id <function>register_module</function> usage ... register_module(dialog, PROTO_BIN, cb, timeout, auth_check, cluster_id) ...
opensips-2.2.2/modules/compression/000077500000000000000000000000001300170765700173745ustar00rootroot00000000000000opensips-2.2.2/modules/compression/Makefile000066400000000000000000000003341300170765700210340ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=compression.so LIBS= -lz include ../../Makefile.modules opensips-2.2.2/modules/compression/README000066400000000000000000000251641300170765700202640ustar00rootroot00000000000000compression Module Ionut-Razvan Ionita Edited by Ionut-Razvan Ionita Copyright © 2014 Voice Sistem SRL Revision History Revision $Revision: 5895 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How it works 1.3. Usage cases 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.5. External Libraries or Applications 1.6. Exported Parameters 1.6.1. mc_level (int) 1.7. Exported Functions 1.7.1. mc_compress([algo], flags, [whitelist]) 1.7.2. mc_compact([whitelist]) 1.7.3. mc_decompress() 1.8. Compression performance test for sip messages List of Tables 1.1. mc_compress performance test results List of Examples 1.1. Set mc_level parameter 1.2. mc_compress usage 1.3. mc_compress usage 1.4. mc_compress usage 1.5. mc_decompress usage Chapter 1. Admin Guide 1.1. Overview This module implements message compression/decompression and base64 encoding for sip messages using deflate and gzip algorithm/headers. Another feature of this module is reducing headers to compact for as specified in SIP RFC's, sdp body codec unnecessary description removal (for codecs 0-97), whitelist for headers not be removed (excepting necessary headers). 1.2. How it works The module is using zlib library to implement compression and base64 encoding for converting the message to human readable characters. It also uses callbacks to do the compression/compaction of the message in order for this operations to be done after all the other script functions have been applied to the message. 1.3. Usage cases As we know, udp fragmentation is a big problem these days, so this module comes to try making the message smaller by any means. The module can be used to compress the body or some headers found in the message or it can decompress compressed messages. There are more possibilities to do this: the body can be compressed along with the specified headers or the headers can be compressed isolated from the body in a specific header. Also the module does message compaction: reduction of sip header names to short form (for example "Via" becomes 'v' and so on), sdp body codec attributes unnecesary description ("a=rtpmap:0 PCMU/8000" becomes "a=rtpmap:0"), unwanted headers removal by specfing the ones you want to keep in a whitelist. The module also does message decompresion and base64 decoding. It can detect the algorithm used for compression from the Content-Encoding header. At this moment only gzip and deflate algorithms are supported. 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * None 1.5. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * zlib-dev - the development libraries of zlib. 1.6. Exported Parameters 1.6.1. mc_level (int) This parameter ranges from 1 to 9 and it specifies the level of compression you want to do. Default is 6. 9 is the best, but the longest time consuming algorithm and 1 is the worst. If, by mistake, you set a lower or a higher level, the default, 6, will be used, but you will receive a warning. Example 1.1. Set mc_level parameter ... modparam("mc", "mc_level", "3") ... 1.7. Exported Functions 1.7.1. mc_compress([algo], flags, [whitelist]) This function will compress the current message as specified in the parameters. Keep in mind that the compression is done just before the message is sent, so that all your lumps can be applied. Meaning of the parameters is as follows: * algo - The algorithm used for compression. Currently implemented are deflate ('0') and gzip ('1'). The algo parameter can have the following types: + integer - the compression algorithm is statically assigned + pvar - the compression algorithm is the value of an existing pseudo-variable (as integer value) * flags - Specifies on what to apply the compression and where to put the result of the compression. The flags parameter can have the following types: + string - the flags parameter is statically assigned + pvar - the flags paramater is the value of an existing pseudo-variable (as string value) The flags parameter can have the following values: + “b†- specifies that the body of the message shall be compressed. Notice that if the message has no body, the flag will have no effect. + “h†- specifies that all the headers, except the mandatory ones (which will be specified in "whitelist" parameter section) and the ones in the whitelist shall be compressed. + “s†- the headers and the body shall be compressed Separately, meaning that a new header named "Comp-Hdrs" will be created, and this header will keep the content of the compressed headers. Also, "Headers-Encoding" header will be created in order to keep the algorithm used to compress the headers. If this flag is not specified, the headers and the body (if 'b' and 'h' flags are specified) will be compressed alltogether in the new body of the message. + “e†- specify that you want base64 Encoding. If you do not specify this flag, by default the module will send the raw compressed message in deflate/gzip format. * whitelist - header names list, separated by '|' which will specify which headers shall not be compressed, along with the mandatory ones, which can never be compressed. The mandatory headers are the following: VIA, FROM, TO, CSEQ, ROUTE, RECORD_ROUTE, CALLID. Also, CONTENT_TYPE is mandatory only if CONTENT-LENGTH > 0. Also, in case you do not want to use body compression, the Content-Length header will become a mandatory header, which can not be compressed. In case you do want body compression, the old Content-Length Header will be compressed, and a new content length will be calculated. When you will want to do decompression, the compressed length will be removed, and the content length header will be the same as the one before the compression. This function can be used from REQUEST_ROUTE, LOCAL_ROUTE, FAILURE_ROUTE. Example 1.2. mc_compress usage ... if (!mc_compress("0", "bhs", "Max-Forwards|Subject|P-Asserted-Identity") ) xlog("compression failed\n"); ... Example 1.3. mc_compress usage ... $avp(algo) = "1"; $var(flags) = "bs"; $var(list) = "Max-Forwards | Contact"; mc_compres("$avp(algo", "$var(flags", "$var(list"); xlog("compression registered\n"); ... 1.7.2. mc_compact([whitelist]) This function will realise four different things: headers which are not mandatory and are not in the whitelist will be removed, headers of same type will be put together, separated by ',', header names which have a short form will be reduced to that short form and sdp rtpmap attribute headers which contain a value lower than 98 will be removed, because it is no longer needed. No lumps affected by this function, because it is applied after almost all the processing is done. The mc_compact supported short forms are: * “c†- Content-Type (RFC 3261) * “f†- From (RFC 3261) * “i†- Call-ID (RFC 3261) * “k†- Supported (RFC 3261) * “l†- Content-Length (RFC 3261) * “m†- Contact (RFC 3261) * “s†- Subject (RFC 3261) * “t†- To (RFC 3261) * “v†- Via (RFC 3261) * “x†- Session-Expires (RFC 4028) Meaning of the parameters is as follows: * whitelist - Whitelist of headers not to be removed, except from the mandatory ones. The whitelist header names must pe separated by '|'. The algo parameter can have the following types: + string - the whitelist is statically assigned + pvar - the whitelist is the value of an existing pseudo-variable (as integer value) This function can be used from REQUEST_ROUTE, LOCAL_ROUTE, FAILURE_ROUTE. Example 1.4. mc_compress usage ... if (!mc_compact("Max-Forwards|P-Asserted-Identity")) xlog("compaction failed\n"); ... 1.7.3. mc_decompress() This function does the reverse of mc_compress, meaning that it does base64 decoding and gzip/deflate decompression. Keep in mind that gzip decompression is a little bit more efficient because it is being known the size of the compressed buffer as against deflate which does not hold the size of the buffer, so the decompression will be made in a static buffer. This function requests no parameters. WARNING: This function replaces the original buffer of the message with the decompressed buffer, so any processing you do to the message will not be taken into consideration. Try applying the decompression function, before you do any other processing to the message. This function can be used from REQUEST_ROUTE, LOCAL_ROUTE, FAILURE_ROUTE. Example 1.5. mc_decompress usage ... if (!mc_decompress()) xlog("decompression failed\n"); ... 1.8. Compression performance test for sip messages The following results have been obtained using the compression function included in the module. Using this results, you can improve the usage of this module, in order to compress only when you think it is favorable enough for you. The algorithm used is deflate for all cases because gzip is always 16 bytes higher than deflate, which represents the uncompressed size modulo 4GB. For the subtests in the same test, the same SIP message have been used. Table 1.1. mc_compress performance test results Test Number Subtest Number Body Size Headers to Compress Size Compressed Content Compressed Content Size Compression level Compressed size Compression ratio 1 1 179 82 Body + Headers 261 1 284 0.91 1 2 179 82 Body + Headers 261 9 284 0.91 1 3 179 82 Body 179 1 196 0.91 1 4 179 82 Body 179 9 196 0.91 2 1 838 392 Body + Headers 1230 1 898 1.36 2 2 838 392 Body + Headers 1230 9 872 1.41 2 3 838 392 Body 838 1 568 1.47 2 4 838 392 Body 838 1 540 1.55 3 1 1329 607 Body + Headers 1936 1 1396 1.38 3 2 1329 607 Body + Headers 1936 9 1352 1.43 3 3 1329 607 Body 1329 1 840 1.58 3 4 1329 607 Body + Headers 1329 9 804 1.65 opensips-2.2.2/modules/compression/compression.c000066400000000000000000001400701300170765700221030ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include "zlib.h" #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../action.h" #include "../../script_var.h" #include "../../dset.h" #include "../../mem/mem.h" #include "../../mi/mi.h" #include "../../parser/parse_to.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_hname2.h" #include "../../parser/sdp/sdp_helpr_funcs.h" #include "../../mod_fix.h" #include "../../data_lump.c" #include "../../ut.h" #include "../../msg_translator.h" #include "../tm/tm_load.h" #include "../../script_cb.h" #include "../../context.h" #include "../../parser/hf.h" #include "compression.h" #include "compression_helpers.h" #include "gz_helpers.h" #include "compression_api.h" #define CL_NAME "Content-Length: " #define CL_NAME_LEN (sizeof(CL_NAME) - 1) #define CE_NAME "Content-Encoding: " #define CE_NAME_LEN (sizeof(CE_NAME) - 1) #define GZIP_CE "Content-Encoding: gzip\r\n" #define GZIP_CE_LEN (sizeof(GZIP_CE) - 1) #define DEFLATE_CE "Content-Encoding: deflate\r\n" #define DEFLATE_CE_LEN (sizeof(DEFLATE_CE) - 1) #define COMP_HDRS "Comp-Hdrs: " #define COMP_HDRS_LEN (sizeof(COMP_HDRS) - 1) #define HDRS_ENCODING "Headers-Encoding: " #define GZIP_ALGO "gzip" #define DEFLATE_ALGO "deflate" #define BASE64_ALGO "base64" #define DELIM ": " #define ATTR_DELIM ", " #define DELIM_LEN (sizeof(DELIM) - 1) #define ATTR_DELIM_LEN (sizeof(ATTR_DELIM) - 1) #define NO_FORM 255 #define is_space(p) (*(p) == ' ') #define veclen(_vec_, _type_) (sizeof(_vec_)/sizeof(_type_)) #define PARSE_CRLF 0x0a0d #define WORD(p) (*(p + 0) + (*(p + 1) << 8)) #define DWORD(p) (*(p+0) + (*(p+1) << 8) + (*(p+2) << 16) + (*(p+3) << 24)) #define LOWER_CASE(p) (*(p) & 0x20) #define BUFLEN 4096 #define COMPACT_FORMS "cfiklmstvx" #define TM_CB (1<<0) #define PROCESSING_CB (1<<1) #define COMPRESS_CB (1<<0) #define COMPACT_CB (1<<1) #define SET_GLOBAL_CTX(pos, value) \ (context_put_ptr(CONTEXT_GLOBAL, current_processing_ctx, pos, value)) #define GET_GLOBAL_CTX(pos) \ (context_get_ptr(CONTEXT_GLOBAL, current_processing_ctx, pos)) static int mod_init(void); static int child_init(int rank); static void mod_destroy(); int mc_level = 6; unsigned char* mnd_hdrs_mask = NULL; unsigned char* compact_form_mask = NULL; struct tm_binds tm_api; int compress_ctx_pos, compact_ctx_pos; int tm_compress_ctx_pos, tm_compact_ctx_pos; static int sh_fixup(void**, int); static int mc_compact_no_args(struct sip_msg*); static int mc_compact(struct sip_msg*, char*); static int mc_compact_cb(char** buf, void* param, int, int*); static int mc_compress_fixup(void**, int); static int mc_compress(struct sip_msg*, char*, char*, char*); int mc_compress_cb(char** buf, void* param, int type, int* olen); static inline int mc_ndigits(int x); static inline void parse_algo_hdr(struct hdr_field* algo_hdr, int* algo, int* b64_required); static int mc_decompress(struct sip_msg*); void wrap_tm_func(struct cell* t, int type, struct tmcb_params* p); int wrap_msg_func(str*, struct sip_msg*, int type); static char body_buf[BUFLEN]; static char hdr_buf[BUFLEN/2]; struct cell* global_tran=NULL; static str body_in = {NULL, 0}, body_out = {NULL, 0}, hdr_in = {NULL, 0}, hdr_out = {NULL, 0}, buf_out = {NULL, 0}; static param_export_t mod_params[]={ { "compression_level", INT_PARAM, &mc_level}, {0,0,0} }; static mi_export_t mi_cmds[]= { {0, 0, 0, 0, 0, 0} }; static cmd_export_t cmds[]={ {"mc_compact", (cmd_function)mc_compact_no_args, 0, NULL, 0, REQUEST_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE|FAILURE_ROUTE}, {"mc_compact", (cmd_function)mc_compact, 1, sh_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE|FAILURE_ROUTE}, {"mc_compress", (cmd_function)mc_compress, 3, mc_compress_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE|FAILURE_ROUTE}, {"mc_decompress", (cmd_function)mc_decompress, 0, NULL, 0, REQUEST_ROUTE|LOCAL_ROUTE|FAILURE_ROUTE}, {"load_compression", (cmd_function)bind_compression, 1, 0, 0, 0}, {0,0,0,0,0,0} }; struct module_exports exports= { "compression", /* module's name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ mod_params, /* param exports */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* additional processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, child_init /* pre-child init function */ }; int mnd_hdrs[]={ HDR_VIA_T, HDR_VIA2_T, HDR_FROM_T, HDR_TO_T, HDR_CSEQ_T, HDR_ROUTE_T, HDR_RECORDROUTE_T, HDR_CONTENTTYPE_T, HDR_CALLID_T, HDR_CONTACT_T }; int compact_form_hdrs[]={ HDR_CALLID_T, HDR_CONTACT_T, HDR_CONTENTLENGTH_T, HDR_CONTENTTYPE_T, HDR_FROM_T, HDR_SUBJECT_T, HDR_TO_T, HDR_VIA_T, HDR_SUPPORTED_T, HDR_OTHER_T }; /* * Function that builds a list which will contain the mandatory headers */ int build_hdr_masks(void) { int len = veclen(mnd_hdrs, int), i; mnd_hdrs_mask = pkg_malloc(HDR_MASK_SIZE); if (!mnd_hdrs_mask) goto mem; memset(mnd_hdrs_mask, 0, HDR_MASK_SIZE); /* build initial array with mandatory headers mask */ for (i = 0; i < len; i++) { mnd_hdrs_mask[mnd_hdrs[i] / MC_BYTE_SIZE] |= (unsigned char)1 << (mnd_hdrs[i] % MC_BYTE_SIZE); } compact_form_mask = pkg_malloc(HDR_MASK_SIZE); if (!compact_form_mask) goto mem; memset(compact_form_mask, 0, HDR_MASK_SIZE); len = veclen(compact_form_hdrs, int); /* Build mask with headers which will be reduced to short form */ for (i = 0; i < len; i++) { compact_form_mask[compact_form_hdrs[i] / MC_BYTE_SIZE] |= (unsigned char)1 << (compact_form_hdrs[i] % MC_BYTE_SIZE); } return 0; mem: LM_ERR("no more pkg mem\n"); return -1; } void wrap_tm_compress(struct cell* t, int type, struct tmcb_params* p) { wrap_tm_func( t, COMPRESS_CB, p); } void wrap_tm_compact(struct cell*t, int type, struct tmcb_params* p) { wrap_tm_func( t, COMPACT_CB, p); } void wrap_tm_func(struct cell* t, int type, struct tmcb_params* p) { char* buf = t->uac[p->code].request.buffer.s; int olen = t->uac[p->code].request.buffer.len; void* args; switch (type) { case COMPRESS_CB: if ((args = GET_GLOBAL_CTX(compress_ctx_pos)) == NULL) break; if (mc_compress_cb(&buf, args, TM_CB, &olen) < 0) { LM_ERR("compression failed\n"); return; } pkg_free(args); SET_GLOBAL_CTX(compress_ctx_pos, NULL); break; case COMPACT_CB: /* if not registered yet we take from global context */ if ((args = GET_GLOBAL_CTX(compact_ctx_pos)) == NULL) break; if (mc_compact_cb(&buf, args, TM_CB, &olen) < 0) { LM_ERR("compaction failed\n"); return; } pkg_free(args); SET_GLOBAL_CTX(compact_ctx_pos, NULL); break; default: LM_BUG("!!! invalid CB type arg!\n"); return; } t->uac[p->code].request.buffer.s = buf; t->uac[p->code].request.buffer.len = olen; } int wrap_msg_compress(str* buf, struct sip_msg* p_msg) { return wrap_msg_func( buf, p_msg, COMPRESS_CB); } int wrap_msg_compact(str* buf, struct sip_msg* p_msg) { return wrap_msg_func( buf, p_msg, COMPACT_CB); } int wrap_msg_func(str* buf, struct sip_msg* p_msg, int type) { void* args; int olen=buf->len; if (current_processing_ctx == NULL) { LM_DBG("null context. cb shall not be removed\n"); return 1; } switch (type) { case COMPRESS_CB: if ((args = GET_GLOBAL_CTX(compress_ctx_pos))==NULL) break; if (mc_compress_cb(&buf->s, args, PROCESSING_CB, &olen) < 0) { LM_ERR("compression failed. Probably not requested message\n"); return -1; } pkg_free(args); SET_GLOBAL_CTX(compact_ctx_pos, NULL); break; case COMPACT_CB: if ((args = GET_GLOBAL_CTX(compact_ctx_pos))==NULL) break; if (mc_compact_cb(&buf->s, args, PROCESSING_CB, &olen) < 0) { LM_ERR("compaction failed\n"); return -1; } pkg_free(args); SET_GLOBAL_CTX(compact_ctx_pos, NULL); break; } buf->len = olen; return 0; } /* * */ static int mod_init(void) { LM_INFO("Initializing module...\n"); if (build_hdr_masks()) { LM_ERR("Cannot build initial mandatory headers mask\n"); return -1; } if (mc_level > 9 || mc_level < 1) { LM_WARN("invalid level. using default 6\n"); mc_level = 6; } compress_ctx_pos = context_register_ptr(CONTEXT_GLOBAL, NULL); LM_DBG("received compress context position %d\n", compress_ctx_pos); compact_ctx_pos = context_register_ptr(CONTEXT_GLOBAL, NULL); LM_DBG("received compact context position %d\n", compact_ctx_pos); memset(&tm_api, 0, sizeof(struct tm_binds)); if (load_tm_api(&tm_api) != 0) LM_DBG("TM modules was not found\n"); return 0; } /* * */ static int child_init(int rank) { return 0; } /* * */ static void mod_destroy(void) { return; } /* * Function that gets the whitelist in the param given */ static int set_wh_param(void **param, unsigned char* def_hdrs_mask) { mc_param_p wh_param; wh_param = pkg_malloc(sizeof(mc_param_t)); if (!wh_param) { LM_ERR("no more pkg mem\n"); return -1; } if (((char*)*param)[0] != PV_MARKER) { wh_param->type = WH_TYPE_STR; if (parse_whitelist(param, &wh_param->v.lst, def_hdrs_mask)) { LM_ERR("cannot parse whitelist\n"); return -1; } } else { if (fixup_pvar(param)) { LM_ERR("parsing pvar whitelist failed\n"); return -1; } wh_param->type = WH_TYPE_PVS; wh_param->v.pvs = *param; } *param = (void*)wh_param; return 0; } /* * Fixup function for 'mc_compact' */ static int sh_fixup(void** param, int param_no) { if (param_no == 1) return set_wh_param(param, mnd_hdrs_mask); else { LM_ERR("invalid param no\n"); return -1; } } /* * Memcpy and update length wrapper */ static inline int wrap_copy_and_update(char** dest_p, const char* src, int cpy_len, int* dest_len) { memcpy(*dest_p + *dest_len, src, cpy_len); *dest_len += cpy_len; return 0; } /* * Function that jumps over the first row of the message * with the received buffer and saves a pointer in the first * arg to the first row along with the length */ static int mc_parse_first_line(str* msg_start, char** buffer) { char* buf = *buffer; int len=0; msg_start->s = buf; msg_start->len = 0; /* Jump over initial row of the message */ do { len++; } while (*(buf+len) != '\n'); len++; msg_start->len = len; *buffer = buf+len; return 0; } /* *Function that checks if header is in whitelist */ static int mc_is_in_whitelist(struct hdr_field* hf, mc_whitelist_p wh_list) { if (hf->type != HDR_OTHER_T) { return ((1 << (hf->type%MC_BYTE_SIZE))& (wh_list->hdr_mask[hf->type/MC_BYTE_SIZE])); } else { mc_other_hdr_lst_p other_hdr = wh_list->other_hdr; for ( ; other_hdr; other_hdr=other_hdr->next) { if (other_hdr->hdr_name.len != hf->name.len) continue; if (strncasecmp(hf->name.s, other_hdr->hdr_name.s, hf->name.len)) continue; return 1; } return 0; } return 0; } /* * Append header to hdr_mask elemnt */ static int append_hf2lst(struct hdr_field** lst, struct hdr_field* hf, int* msg_total_len) { struct hdr_field* temp = *lst; if (hf->type != HDR_OTHER_T) { while (temp->sibling) temp = temp->sibling; temp->sibling = hf; *msg_total_len += hf->body.len + ATTR_DELIM_LEN; } else { /* OTHER hdr type */ while (temp) { /* Header already exists */ if (!strncasecmp(temp->name.s, hf->name.s, temp->name.len)) { while (temp->sibling) temp = temp->sibling; temp->sibling = hf; *msg_total_len += hf->body.len + ATTR_DELIM_LEN; return 0; } if (!temp->next) break; temp = temp->next; } /* First occurence of this header */ temp->next = hf; *msg_total_len += hf->name.len + DELIM_LEN; *msg_total_len += hf->body.len + CRLF_LEN; } return 0; } /* * wrapper for mc_compact with zero arguments */ static int mc_compact_no_args(struct sip_msg* msg) { return mc_compact(msg, NULL); } /* * Compaction function * 1) Headers of same type will be put together * 2) Header names with compact forms will be transformed to * compact form * 3) Headers which not in whitelist will be removed * 4) Unnecessary sdp body codec attributes lower than 98 removed */ static int mc_compact(struct sip_msg* msg, char* whitelist) { mc_whitelist_p wh_list; struct mc_cmpct_args* args; mc_param_p wh_param = (mc_param_p)whitelist; if (mc_get_whitelist(msg, &wh_param, &wh_list, mnd_hdrs_mask)) { LM_ERR("Cannot get whitelist\n"); return -1; } /* args shall be created only if global contexts do not exist and * tm is not present or tm context is empty */ args=pkg_malloc(sizeof(struct mc_cmpct_args)); if (args==NULL) { LM_ERR("no more pkg mem\n"); goto err00; } args->wh_param = wh_param; args->wh_list = wh_list; SET_GLOBAL_CTX(compact_ctx_pos, (void*)args); /* register stateless callbacks */ if (register_post_raw_processing_cb(wrap_msg_compact, POST_RAW_PROCESSING, 1/*to be freed*/) < 0) { LM_ERR("failed to add raw processing cb\n"); return -1; } if (tm_api.t_gett && msg->flags&FL_TM_CB_REGISTERED) return 1; /*register tm callback if tm api */ if (tm_api.register_tmcb && tm_api.register_tmcb( msg, 0, TMCB_PRE_SEND_BUFFER, wrap_tm_compact, NULL, 0) != 1) { LM_ERR("failed to add tm TMCB_PRE_SEND_BUFFER callback\n"); msg->flags |= FL_TM_CB_REGISTERED; goto err00; } return 1; err00: if (wh_param && wh_param->type == WH_TYPE_PVS) free_whitelist(&wh_list); return -1; } /* * */ static int mc_compact_cb(char** buf_p, void* param, int type, int* olen) { int i; int msg_total_len; int rtpmap_val=0, rtpmap_len; int new_body_len; int hdr_len; str msg_start; str new_buf; char *buf=*buf_p; char *buf_cpy; char *end=buf+*olen; struct hdr_field *hf; struct hdr_field** hdr_mask; struct mc_cmpct_args* args; body_frag_p frg; body_frag_p frg_head; body_frag_p temp; mc_param_p wh_param; mc_whitelist_p wh_list; args = (struct mc_cmpct_args*)param; wh_param = args->wh_param; wh_list = args->wh_list; hdr_mask = pkg_malloc(HDR_EOH_T * sizeof(struct hdr_field*)); if (!hdr_mask) goto memerr; memset(hdr_mask, 0, HDR_EOH_T * sizeof(struct hdr_field*)); mc_parse_first_line( &msg_start, &buf); msg_total_len = msg_start.len; /* Start to parse the headers and print them*/ while (1) { hf = pkg_malloc(sizeof(struct hdr_field)); if (hf == NULL) { LM_ERR("no more pkg mem\n"); goto memerr; } memset(hf, 0, sizeof(struct hdr_field)); hf->type=HDR_ERROR_T; buf=get_hdr_field(buf, end, hf); if (hf->type == HDR_ERROR_T) { goto free_mem; } if (hf->type == HDR_EOH_T) { pkg_free(hf); break; } if (mc_is_in_whitelist(hf, wh_list)) { if (hdr_mask[hf->type]) { /* If hdr already found or hdr of type other */ if (append_hf2lst(&hdr_mask[hf->type], hf, &msg_total_len)) { LM_ERR("Cannot append hdr to lst\n"); return -1; } } else { unsigned char c; /* Get the compact form of the header */ if (hf->type != HDR_OTHER_T && (c=get_compact_form(hf)) != NO_FORM) { hf->name.s = &COMPACT_FORMS[c]; hf->name.len = 1; } /* update the len of the new buffer */ msg_total_len += hf->name.len + DELIM_LEN; msg_total_len += hf->body.len + CRLF_LEN; hdr_mask[hf->type] = hf; } } else { clean_hdr_field(hf); } hf = 0; } hdr_len = msg_total_len; buf_cpy = buf+CRLF_LEN; frg = frg_head = pkg_malloc(sizeof(body_frag_t)); if (!frg) goto memerr; frg->begin = 0; frg->end = CRLF_LEN; frg->next = NULL; /* parse the body and extract fragments */ while (buf_cpy != end) { while (*buf_cpy == ' ' || *buf_cpy == '\t') (buf_cpy++, frg->end++); if (*buf_cpy != 'a') { /* Jump over the entire row*/ goto row_jump; } else if (strncmp(buf_cpy, "a=rtpmap:", 9)) goto row_jump; /* found rtpmap */ else { buf_cpy += 9; frg->end--; /* already on 'a' char */ rtpmap_len = rtpmap_val = 0; while (*buf_cpy >= '0' && *buf_cpy <= '9') { rtpmap_val = rtpmap_val*10 + (*buf_cpy - '0'); (buf_cpy++, rtpmap_len++); } if (rtpmap_val < 98) { msg_total_len += frg->end - frg->begin + 1; frg->next = pkg_malloc(sizeof(body_frag_t)); if (!frg->next) goto memerr; frg = frg->next; frg->next = NULL; /* find the next line and set the start of the next fragment */ while (*buf_cpy != '\n') buf_cpy++; buf_cpy++; frg->end = frg->begin = buf_cpy - buf; continue; } else { /*currently on \n before rtpmap. Need to jump over \nrtpmap:RT_VAL */ frg->end += 9 + rtpmap_len + 1; } } row_jump: while (*buf_cpy != '\n') { if (*buf_cpy == '\0') { LM_ERR("BUG! Message body not containing '\\n' in the end\n"); return -1; } (buf_cpy++, frg->end++); } (buf_cpy++, frg->end++); } int foo; /* not storing '\0' at the end of the message */ (buf_cpy--, frg->end--); msg_total_len += frg->end - frg->begin + 1; new_body_len = msg_total_len - hdr_len; /* creating the new content length */ hf = pkg_malloc(sizeof(struct hdr_field)); if (hf == NULL) goto memerr; memset(hf, 0, sizeof(struct hdr_field)); hf->type = HDR_CONTENTLENGTH_T; hf->name.s = &COMPACT_FORMS[get_compact_form(hf)]; hf->name.len = 1; if (new_body_len <= CRLF_LEN) new_body_len = 0; hf->body.len = mc_ndigits(new_body_len); hf->body.s = int2str( new_body_len, &foo); if (hf->body.s == 0) { LM_ERR("failed to convert int to string\n"); goto memerr; } /* * If body is empty Content-Type is not necessary anymore * But only if Content-Type exists */ if (hdr_mask[HDR_CONTENTTYPE_T] && new_body_len == 0) { clean_hdr_field(hdr_mask[HDR_CONTENTTYPE_T]); hdr_mask[HDR_CONTENTTYPE_T] = NULL; } msg_total_len += hf->name.len + DELIM_LEN + hf->body.len + CRLF_LEN; hdr_mask[hf->type] = hf; /* build the new buffer */ if (wrap_realloc(&buf_out, msg_total_len)) goto free_mem; new_buf.s = buf_out.s; new_buf.len = 0; /* Copy the beginning of the message */ wrap_copy_and_update( &new_buf.s, msg_start.s, msg_start.len, &new_buf.len); /* Copy all the headers */ for (i = HDR_VIA_T; i <= HDR_EOH_T; i++) { /* Just to put headers of type other after all the other headers */ if (i == HDR_EOH_T) i = HDR_OTHER_T; again: if (hdr_mask[i]) { /* Compact form name so the header have to be built */ if (LOWER_CASE(hdr_mask[i]->name.s)) { /* Copy the name of the header */ wrap_copy_and_update(&new_buf.s, hdr_mask[i]->name.s, hdr_mask[i]->name.len, &new_buf.len); /* Copy the ': ' delimiter*/ wrap_copy_and_update(&new_buf.s, DELIM, DELIM_LEN, &new_buf.len); /* Copy the first field of the header*/ wrap_copy_and_update(&new_buf.s, hdr_mask[i]->body.s, hdr_mask[i]->body.len, &new_buf.len); /* Normal form header so it can be copied in one step */ } else { wrap_copy_and_update( &new_buf.s, hdr_mask[i]->name.s, /* Possible siblings. No CRLF yet */ hdr_mask[i]->len - CRLF_LEN, &new_buf.len ); } /* Copy the rest of the header fields(siblings) if they exist */ struct hdr_field* temp = hdr_mask[i]->sibling, *hdr_field; while (temp) { /* Put ', ' delimiter before header body*/ wrap_copy_and_update(&new_buf.s, ATTR_DELIM, ATTR_DELIM_LEN, &new_buf.len); /* Append the header content */ wrap_copy_and_update(&new_buf.s, temp->body.s, temp->body.len, &new_buf.len); hdr_field = temp->sibling; clean_hdr_field(temp); temp = hdr_field; } /* Copy CRLF to the end of the header */ wrap_copy_and_update(&new_buf.s, CRLF, CRLF_LEN, &new_buf.len); if (hdr_mask[i]->next) { /* If more other headers, put all of them in the new buffer and free every allocated member */ temp = hdr_mask[i]; hdr_mask[i] = hdr_mask[i]->next; clean_hdr_field(temp); goto again; } else { /* if it is not an OTHER_HDR or it is the last one in OTHER_HDR list */ pkg_free(hdr_mask[i]); hdr_mask[i] = 0; } } if (i == HDR_OTHER_T) break; } /* Copy the body of the message */ frg = frg_head; while (frg) { temp = frg; wrap_copy_and_update( &new_buf.s, buf + frg->begin, frg->end-frg->begin+1, &new_buf.len); frg = frg->next; pkg_free(temp); } switch (type) { case TM_CB: *buf_p = shm_malloc(new_buf.len); if (*buf_p == NULL) { LM_ERR("no more sh mem\n"); goto free_mem; } break; case PROCESSING_CB: *buf_p = pkg_malloc(new_buf.len); if (*buf_p == NULL) { LM_ERR("no more pkg mem\n"); goto free_mem; } break; default: LM_ERR("invalid type\n"); goto free_mem; } memcpy(*buf_p, new_buf.s, new_buf.len); *olen = new_buf.len; /* Free the vector */ pkg_free(hdr_mask); /* Free the whitelist if pvs */ if (wh_param && wh_param->type == WH_TYPE_PVS) free_whitelist(&wh_list); return 0; memerr: LM_ERR("No more pkg mem\n"); free_mem: free_hdr_mask(hdr_mask); free_whitelist(&wh_list); return -1; } /* * Fixup function for mc_compress */ static int mc_compress_fixup(void** param, int param_no) { switch (param_no) { case 1: if (fixup_igp(param)) { LM_ERR("invalid algo\n"); } break; case 2: return fixup_compression_flags(param); case 3: /* Parse the whitelist */ return set_wh_param(param, NULL); default: LM_ERR("invalid parameter\n"); return -1; } return 0; } /* * */ static inline int mc_ndigits(int x) { if (x == 0) return 1; if (x > 10) return 1 + mc_ndigits(x/10); else return 1; } /* * Compression function * 1) Only mandatory headers will be kept * 2) The rest of the headers along with the body * will form the new body which will be use for compression * 3) The Content-Encoding Header will set to gzip, probably * base64 also */ static int mc_compress(struct sip_msg* msg, char* param1, char* param2, char* param3) { int algo=0; int flags=0; int index; pv_value_t value; struct mc_comp_args* args; gparam_p gp_algo = (gparam_p)param1; gparam_p gp_flags = (gparam_p)param2; mc_param_p wh_param = (mc_param_p)param3; mc_whitelist_p hdr2compress_list; /* Default value */ if (gp_algo == NULL) { algo = 0; goto flags; } /* Get algo number */ switch (gp_algo->type) { case GPARAM_TYPE_INT: algo = gp_algo->v.ival; break; case GPARAM_TYPE_PVS: if (pv_get_spec_value(msg, gp_algo->v.pvs, &value) != 0 || !(value.flags&PV_VAL_STR)) { LM_ERR("no valid algo PV value found\n"); return -1; } while(is_space(value.rs.s)) value.rs.s++; if (*value.rs.s >= '0' && *value.rs.s <= '9') algo = *value.rs.s - '0'; else { LM_ERR("algorithm must be a digit\n"); return -1; } } flags: if (gp_flags == NULL) { LM_ERR("mandatory parameter flags not specified.\n"); return -1; } /* Get flags */ switch (gp_flags->type) { case GPARAM_TYPE_INT: flags = gp_flags->v.ival; break; case GPARAM_TYPE_PVS: if (pv_get_spec_value(msg, gp_flags->v.pvs, &value) != 0 || !(value.flags&PV_VAL_STR)) { LM_ERR("no valid flags PV value found\n"); return -1; } if (fixup_compression_flags((void**)&value.rs.s)) { LM_ERR("cannot parse flags\n"); return -1; } flags = ((gparam_p)value.rs.s)->v.ival; pkg_free(value.rs.s); break; } if (!(flags&BODY_COMP_FLG) && !(flags&HDR_COMP_FLG)) { LM_WARN("nothing requested to compress!change flags\n"); return -1; } /* Simulate an whitelist which will contain only the Content-Length header in case BODY_COMP_FLG is set */ if (!(flags&HDR_COMP_FLG)) { hdr2compress_list = pkg_malloc(sizeof(mc_whitelist_t) + HDR_MASK_SIZE); if (!hdr2compress_list) { LM_ERR("no more pkg mem\n"); return -1; } hdr2compress_list->hdr_mask = (unsigned char*)(hdr2compress_list + 1); memset( hdr2compress_list->hdr_mask, 0, HDR_MASK_SIZE); hdr2compress_list->other_hdr = NULL; goto skip_parse; } /* Get headers to compress list */ if (mc_get_whitelist(msg, &wh_param, &hdr2compress_list, NULL)) { LM_ERR("cannot headers to compress list\n"); return -1; } /* Remove mandatory headers if they have been set */ for (index=0; index < veclen(mnd_hdrs, int); index++) { if (hdr2compress_list->hdr_mask[mnd_hdrs[index]/MC_BYTE_SIZE] & (1 << mnd_hdrs[index]%MC_BYTE_SIZE)) { hdr2compress_list->hdr_mask[mnd_hdrs[index]/MC_BYTE_SIZE] ^= 1 << (mnd_hdrs[index]%MC_BYTE_SIZE); } } skip_parse: /* Content Length must be encoded if asked for body to be encoded*/ if (flags&BODY_COMP_FLG) hdr2compress_list->hdr_mask[HDR_CONTENTLENGTH_T/MC_BYTE_SIZE] |= 1 << (HDR_CONTENTLENGTH_T%MC_BYTE_SIZE); args=pkg_malloc(sizeof(struct mc_comp_args)); if (args==NULL) { LM_ERR("no more pkg mem\n"); return -1; } args->hdr2compress_list = hdr2compress_list; args->flags = flags; args->algo = algo; args->wh_param = wh_param; SET_GLOBAL_CTX(compress_ctx_pos, (void*)args); /* register stateless callbacks */ if (register_post_raw_processing_cb(wrap_msg_compress, POST_RAW_PROCESSING, 1/*to be freed*/) < 0) { LM_ERR("failed to add raw processing cb\n"); return -1; } if (tm_api.t_gett && msg->flags&FL_TM_CB_REGISTERED) return 1; /*register tm callback if tm api */ if (tm_api.register_tmcb && tm_api.register_tmcb( msg, 0, TMCB_PRE_SEND_BUFFER, wrap_tm_compress, NULL, 0) != 1) { LM_ERR("failed to add tm TMCB_PRE_SEND_BUFFER callback\n"); msg->flags |= FL_TM_CB_REGISTERED; return -1; } return 1; } /* * */ int mc_compress_cb(char** buf_p, void* param, int type, int* olen) { int rc; int len; int algo; int flags; int compress_len=0; int uncompress_len=0; int hdr_compress_len=0; str msg_start; char *buf=*buf_p; char *end=buf+strlen(buf); unsigned long temp; struct mc_comp_args *args=(struct mc_comp_args*)param; struct hdr_field *hf; struct hdr_field *mnd_hdrs=NULL; struct hdr_field *non_mnd_hdrs=NULL; struct hdr_field *mnd_hdrs_head=NULL; struct hdr_field *non_mnd_hdrs_head=NULL; mc_param_p wh_param; mc_whitelist_p hdr2compress_list; wh_param = args->wh_param; hdr2compress_list = args->hdr2compress_list; algo = args->algo; flags = args->flags; mc_parse_first_line(&msg_start, &buf); uncompress_len = msg_start.len; /* Parse the message until the body is found Build two lists one of mandatory headers and one of non mandatory headers */ while (1) { hf = pkg_malloc(sizeof(struct hdr_field)); if (hf == NULL) { LM_ERR("no more pkg mem\n"); goto free_mem_full; } memset(hf, 0, sizeof(struct hdr_field)); hf->type=HDR_ERROR_T; buf=get_hdr_field(buf, end, hf); if (hf->type == HDR_ERROR_T) { goto free_mem_full; } if (hf->type == HDR_EOH_T) { compress_len += strlen(buf); compress_len = compress_len > CRLF_LEN ? compress_len : 0; pkg_free(hf); break; } /*if Content-Length=0 then header must remain*/ if (hf->type == HDR_CONTENTLENGTH_T && hf->body.s[0] == '0') { goto set_mandatory; } if (mc_is_in_whitelist(hf, hdr2compress_list)) { if (!non_mnd_hdrs) { non_mnd_hdrs_head = non_mnd_hdrs = hf; } else { non_mnd_hdrs->next = hf; non_mnd_hdrs = non_mnd_hdrs->next; } /* in case will have a separate compressed header */ if ((flags&SEPARATE_COMP_FLG && flags&BODY_COMP_FLG && flags&HDR_COMP_FLG) || (flags&HDR_COMP_FLG && !(flags&BODY_COMP_FLG))) hdr_compress_len += hf->len; else compress_len += hf->len; } else { set_mandatory: if (!mnd_hdrs) { mnd_hdrs_head = mnd_hdrs = hf; } else { mnd_hdrs->next = hf; mnd_hdrs = mnd_hdrs->next; } uncompress_len += hf->len; } hf = 0; } str buf2compress={NULL, 0}; str hdr_buf2compress={NULL, 0}; /* Copy headers only if they exist and only if were asked*/ non_mnd_hdrs = non_mnd_hdrs_head; if (!non_mnd_hdrs || !(flags&HDR_COMP_FLG)) goto only_body; /* If body compression and header compression flags are set and they have to be together in the body */ if ((flags&BODY_COMP_FLG && flags&HDR_COMP_FLG && !(flags&SEPARATE_COMP_FLG)) || (flags&BODY_COMP_FLG && !(flags&HDR_COMP_FLG))){ if (wrap_realloc(&body_in, compress_len)) goto free_mem_full; buf2compress.s = body_in.s; buf2compress.len = 0; for (hf = non_mnd_hdrs; hf; hf = hf->next) { wrap_copy_and_update( &buf2compress.s, hf->name.s, hf->len, &buf2compress.len); } /* body compression and header compression but separately or only header compression */ } else if ((flags&BODY_COMP_FLG && flags&HDR_COMP_FLG && flags&SEPARATE_COMP_FLG) || (!(flags&BODY_COMP_FLG) && flags&HDR_COMP_FLG)) { if (wrap_realloc(&hdr_in, hdr_compress_len)) goto free_mem_full; hdr_buf2compress.s = hdr_in.s; for (hf = non_mnd_hdrs; hf; hf = hf->next) { wrap_copy_and_update( &hdr_buf2compress.s, hf->name.s, hf->len, &hdr_buf2compress.len); } } only_body: /* Copy the body of the message only if body compression is asked */ if (flags&BODY_COMP_FLG && compress_len) { if (!buf2compress.s) { if (wrap_realloc(&body_in, compress_len)) goto free_mem_full; buf2compress.s = body_in.s; } wrap_copy_and_update( &buf2compress.s, buf, strlen(buf), &buf2compress.len); } if (!buf2compress.s && !hdr_buf2compress.s) { LM_WARN("Nothing to compress. Specified headers not found\n"); goto free_mem_full; } /* Compress the message */ str bufcompressed={NULL, 0}; str hdr_bufcompressed={NULL, 0}; switch (algo) { case 0: /* deflate */ if (buf2compress.s) { bufcompressed.len = compressBound((unsigned long)buf2compress.len); if (wrap_realloc(&body_out, bufcompressed.len)) goto free_mem_full; bufcompressed.s = body_out.s; temp = (unsigned long)bufcompressed.len; rc = compress2((unsigned char*)bufcompressed.s, &temp, (unsigned char*)buf2compress.s, (unsigned long)buf2compress.len, mc_level); bufcompressed.len = (int)temp; if (check_zlib_rc(rc)) { LM_ERR("Body compression failed\n"); goto free_mem_full; } } if ((flags&HDR_COMP_FLG) && hdr_buf2compress.s) { hdr_bufcompressed.len = compressBound((unsigned long)hdr_buf2compress.len); if (wrap_realloc(&hdr_out, hdr_bufcompressed.len)) goto free_mem_full; hdr_bufcompressed.s = hdr_out.s; temp = (unsigned long)hdr_bufcompressed.len; rc = compress2((unsigned char*)hdr_bufcompressed.s, &temp, (unsigned char*)hdr_buf2compress.s, (unsigned long)hdr_buf2compress.len, mc_level); hdr_bufcompressed.len = temp; if (check_zlib_rc(rc)) { LM_ERR("Header compression failed\n"); goto free_mem_full; } } break; case 1: /* gzip */ if (buf2compress.s) { rc = gzip_compress( (unsigned char*)buf2compress.s, (unsigned long)buf2compress.len, &body_out, &temp, mc_level); if (check_zlib_rc(rc)) { LM_ERR("Body compression failed\n"); goto free_mem_full; } bufcompressed.s = body_out.s; bufcompressed.len = (int)temp; } if ((flags&HDR_COMP_FLG) && hdr_buf2compress.s) { rc = gzip_compress( (unsigned char*)hdr_buf2compress.s, (unsigned long)hdr_buf2compress.len, &hdr_out, &temp, mc_level); if (check_zlib_rc(rc)) { LM_ERR("Header compression failed\n"); goto free_mem_full; } hdr_bufcompressed.s = hdr_out.s; hdr_bufcompressed.len = temp; } break; default: LM_WARN("Invalind algo! no compression made\n"); goto free_mem_full; } str bufencoded={NULL, 0}; str hdr_bufencoded={NULL, 0}; if ((flags&B64_ENCODED_FLG) && bufcompressed.s) { bufencoded.len = calc_base64_encode_len(bufcompressed.len); if (wrap_realloc( &body_in, 2*CRLF_LEN + bufencoded.len)) goto free_mem_full; bufencoded.s = body_in.s; memcpy(bufencoded.s, CRLF, CRLF_LEN); base64encode((unsigned char*)(bufencoded.s + CRLF_LEN), (unsigned char*)bufcompressed.s, bufcompressed.len); } else if (bufcompressed.s) { if (wrap_realloc(&body_in, bufcompressed.len + 2*CRLF_LEN)) goto free_mem_full; /* !!! shift buf2compressed CRLF_LEN to the right !!! */ memcpy(body_in.s+CRLF_LEN, bufcompressed.s, bufcompressed.len); memcpy(body_in.s, CRLF, CRLF_LEN); bufencoded.len = bufcompressed.len; bufencoded.s = body_in.s; } if (hdr_bufcompressed.s) { hdr_bufencoded.len = calc_base64_encode_len(hdr_bufcompressed.len); if (wrap_realloc( &hdr_in, hdr_bufencoded.len + CRLF_LEN)) goto free_mem_full; hdr_bufencoded.s = hdr_in.s; base64encode((unsigned char*)hdr_bufencoded.s, (unsigned char*)hdr_bufcompressed.s, hdr_bufcompressed.len); wrap_copy_and_update(&hdr_bufencoded.s, CRLF, CRLF_LEN, &hdr_bufencoded.len); } /* Allocate the new buffer */ int alloc_size; str buf2send={NULL, 0}; alloc_size = msg_start.len + uncompress_len + CRLF_LEN/*the one before all headers*/; if (hdr_bufencoded.s) { alloc_size += COMP_HDRS_LEN + hdr_bufencoded.len; alloc_size += sizeof(HDRS_ENCODING) - 1; } /* if body compressed new content length and content encoding * plus if required more space for base64 in content encoding header*/ if (bufencoded.s) { alloc_size += CL_NAME_LEN + mc_ndigits(bufencoded.len) + CRLF_LEN; alloc_size += CE_NAME_LEN + CRLF_LEN; if (flags&B64_ENCODED_FLG) { alloc_size += ATTR_DELIM_LEN + (sizeof(BASE64_ALGO)-1); } } switch (algo) { case 0: /* deflate*/ if (bufencoded.s) alloc_size += DEFLATE_CE_LEN; if (hdr_bufencoded.s) alloc_size += sizeof(DEFLATE_ALGO) - 1; break; case 1: /* gzip */ if (bufencoded.s) alloc_size += GZIP_CE_LEN; if (hdr_bufencoded.s) alloc_size += sizeof(GZIP_ALGO) - 1; break; default: LM_ERR("compression algo not impelemented\n"); goto free_mem_full; } if (bufencoded.s) alloc_size += bufencoded.len + CRLF_LEN; else alloc_size += strlen(buf); if (wrap_realloc(&buf_out, alloc_size)) goto free_mem_full; buf2send.s = buf_out.s; /* Copy message start */ wrap_copy_and_update( &buf2send.s, msg_start.s, msg_start.len, &buf2send.len); /* Copy mandatory headers */ for (mnd_hdrs = mnd_hdrs_head; mnd_hdrs; mnd_hdrs = mnd_hdrs->next) { wrap_copy_and_update( &buf2send.s, mnd_hdrs->name.s, mnd_hdrs->len, &buf2send.len); } if ((flags&BODY_COMP_FLG) && bufencoded.s) { wrap_copy_and_update( &buf2send.s, CL_NAME, CL_NAME_LEN, &buf2send.len); wrap_copy_and_update( &buf2send.s, int2str(bufencoded.len, &len), mc_ndigits(bufencoded.len), &buf2send.len); wrap_copy_and_update( &buf2send.s, CRLF, CRLF_LEN, &buf2send.len); } if (hdr_bufencoded.s) { wrap_copy_and_update( &buf2send.s, COMP_HDRS, COMP_HDRS_LEN, &buf2send.len); wrap_copy_and_update( &buf2send.s, hdr_bufencoded.s, hdr_bufencoded.len, &buf2send.len); } switch (algo) { case 0: /* deflate */ if (hdr_bufencoded.s) { str hdr_name = str_init(HDRS_ENCODING), hdr_value = str_init(DEFLATE_ALGO); wrap_copy_and_update(&buf2send.s, hdr_name.s, hdr_name.len, &buf2send.len); if (flags & B64_ENCODED_FLG) { wrap_copy_and_update(&buf2send.s, BASE64_ALGO, sizeof(BASE64_ALGO)-1, &buf2send.len); wrap_copy_and_update(&buf2send.s, ATTR_DELIM, ATTR_DELIM_LEN, &buf2send.len); } wrap_copy_and_update(&buf2send.s, hdr_value.s, hdr_value.len, &buf2send.len); wrap_copy_and_update(&buf2send.s, CRLF, CRLF_LEN, &buf2send.len); } if (bufencoded.s) { wrap_copy_and_update(&buf2send.s, CE_NAME, CE_NAME_LEN, &buf2send.len); if (flags & B64_ENCODED_FLG) { wrap_copy_and_update(&buf2send.s, BASE64_ALGO, sizeof(BASE64_ALGO)-1, &buf2send.len); wrap_copy_and_update(&buf2send.s, ATTR_DELIM, ATTR_DELIM_LEN, &buf2send.len); } wrap_copy_and_update(&buf2send.s, DEFLATE_ALGO, sizeof(DEFLATE_ALGO)-1, &buf2send.len); wrap_copy_and_update(&buf2send.s, CRLF, CRLF_LEN, &buf2send.len); } break; case 1: /* gzip */ if (hdr_bufencoded.s) { str hdr_name = str_init(HDRS_ENCODING), hdr_value = str_init(GZIP_ALGO); if (flags & B64_ENCODED_FLG) { wrap_copy_and_update(&buf2send.s, BASE64_ALGO, sizeof(BASE64_ALGO)-1, &buf2send.len); wrap_copy_and_update(&buf2send.s, ATTR_DELIM, ATTR_DELIM_LEN, &buf2send.len); } wrap_copy_and_update(&buf2send.s, hdr_name.s, hdr_name.len, &buf2send.len); wrap_copy_and_update(&buf2send.s, hdr_value.s, hdr_value.len, &buf2send.len); wrap_copy_and_update(&buf2send.s, CRLF, CRLF_LEN, &buf2send.len); } if (bufencoded.s) { wrap_copy_and_update(&buf2send.s, CE_NAME, CE_NAME_LEN, &buf2send.len); if (flags & B64_ENCODED_FLG) { wrap_copy_and_update(&buf2send.s, BASE64_ALGO, sizeof(BASE64_ALGO)-1, &buf2send.len); wrap_copy_and_update(&buf2send.s, ATTR_DELIM, ATTR_DELIM_LEN, &buf2send.len); } wrap_copy_and_update(&buf2send.s, GZIP_ALGO, sizeof(GZIP_ALGO)-1, &buf2send.len); wrap_copy_and_update(&buf2send.s, CRLF, CRLF_LEN, &buf2send.len); } break; default: LM_ERR("compression algo not impelemented\n"); goto free_mem_full; } /* Copy message body */ if (bufencoded.s) { wrap_copy_and_update( &buf2send.s, bufencoded.s, bufencoded.len+CRLF_LEN, &buf2send.len); wrap_copy_and_update( &buf2send.s, CRLF, CRLF_LEN, &buf2send.len); } else { wrap_copy_and_update( &buf2send.s, buf, strlen(buf), &buf2send.len); } switch (type) { case TM_CB: shm_free(*buf_p); *buf_p = shm_malloc(buf2send.len+1); if (*buf_p == NULL) { LM_ERR("no more sh mem\n"); goto free_mem_full; } break; case PROCESSING_CB: *buf_p = pkg_malloc(buf2send.len+1); if (*buf_p == NULL) { LM_ERR("no more pkg mem\n"); goto free_mem_full; } break; default: LM_ERR("invalid type\n"); goto free_mem_full; } memcpy(*buf_p, buf2send.s, buf2send.len); (*buf_p)[buf2send.len] = '\0'; *olen = buf2send.len; free_hdr_list(&mnd_hdrs_head); free_hdr_list(&non_mnd_hdrs_head); if (wh_param && wh_param->type == WH_TYPE_PVS) free_whitelist(&hdr2compress_list); return 0; free_mem_full: free_hdr_list(&mnd_hdrs_head); free_hdr_list(&non_mnd_hdrs_head); if (wh_param && wh_param->type == WH_TYPE_PVS) free_whitelist(&hdr2compress_list); return -1; } /* * */ static int is_content_encoding(struct hdr_field* hf) { #define CONT 0x746e6f43 #define ENT 0x2d746e65 #define ENCO 0x6f636e45 #define DING 0x676e6964 char* name = hf->name.s; if (DWORD(name) == CONT) { name += 4; if (DWORD(name) == ENT) { name += 4; if (DWORD(name) == ENCO) { name += 4; if (DWORD(name) == DING) { return 1; } } } } return 0; } /* * */ static int get_algo(str* tok) { #define GZIP 0x70697a67 #define DEFL 0x6C666564 #define ATE 0x00657461 #define BASE 0x65736162 #define B64 0x00003436 /* actually only 64 */ #define FIRST_THREE(_str_) (_str_ & 0xFFFFFF) #define FIRST_TWO(_str_) (_str_ & 0xFFFF) switch (DWORD(tok->s)) { case DEFL: break; case GZIP: return 1; case BASE: goto check_b64; default: return -1; } if (FIRST_THREE(DWORD(tok->s+4)) == ATE) return 0; return -1; check_b64: if (FIRST_TWO(DWORD(tok->s+4)) == B64) return 2; return -1; #undef GZIP #undef DEFL #undef ATE #undef BASE #undef B64 #undef FIRST_TWO #undef FIRST_THREE } /* * Function to decompress a compressed message */ static int mc_decompress(struct sip_msg* msg) { #define HDRS_TO_SKIP 4 int i; int j; int rc; int algo=-1; int hdrs_algo=-1; int b64_required=-1; str msg_body; str msg_final; str b64_decode={NULL, 0}; str hdr_b64_decode={NULL,0}; str uncomp_body={NULL,0}; str uncomp_hdrs={NULL,0}; char *new_buf; unsigned long temp; /* hdr_vec allows to sort the headers. This will help skipping these headers when building the new message */ struct hdr_field *hf; struct hdr_field *hdr_vec[HDRS_TO_SKIP]; /*hdr_vec : 0 Content-Length 1 Comp-Hdrs 2 Headers-Algo 3 Content-Encoding*/ memset(hdr_vec, 0, HDRS_TO_SKIP * sizeof(struct hdr_field*)); if (parse_headers(msg, HDR_EOH_F, 0) != 0) { LM_ERR("failed to parse SIP message\n"); return -1; } /*If compressed with this module there are great chances that Content-Encoding is last*/ hdr_vec[3] = msg->last_header; if (!is_content_encoding(hdr_vec[3])) { hdr_vec[3] = NULL; for (hf = msg->headers; hf; hf = hf->next) { if (is_content_encoding(hf)) { hdr_vec[3] = hf; continue; } if (hf->type == HDR_OTHER_T && !strncasecmp(hf->name.s, COMP_HDRS,COMP_HDRS_LEN)) { hdr_vec[1] = hf; continue; } if (hf->type == HDR_OTHER_T && !strncasecmp(hf->name.s, HDRS_ENCODING, sizeof(HDRS_ENCODING)-1)) { hdr_vec[2] = hf; } if (hdr_vec[1] && hdr_vec[2] && hdr_vec[3]) break; } } else { for (hf = msg->headers; hf; hf = hf->next) { if (!hdr_vec[1] && hf->type == HDR_OTHER_T && !strncasecmp(hf->name.s, COMP_HDRS,COMP_HDRS_LEN)) { hdr_vec[1] = hf; continue; } if (!hdr_vec[2] && hf->type == HDR_OTHER_T && !strncasecmp(hf->name.s, HDRS_ENCODING, sizeof(HDRS_ENCODING)-1)) hdr_vec[2] = hf; if (hdr_vec[2] && hdr_vec[3] && hdr_vec[1]) break; } } /* Only if content-encoding present, Content-Length will be replaced with the one in the compressed body or in compressed headers*/ if (hdr_vec[3]) { hdr_vec[0] = msg->content_length; parse_algo_hdr(hdr_vec[3], &algo, &b64_required); } if (b64_required > 0 && hdr_vec[3]) { msg_body.s = msg->last_header->name.s + msg->last_header->len + CRLF_LEN; msg_body.len = strlen(msg_body.s); /* Cutting CRLF'S at the end of the message */ while (WORD(msg_body.s + msg_body.len-CRLF_LEN) == PARSE_CRLF) { msg_body.len -= CRLF_LEN; } if (wrap_realloc(&body_in, calc_max_base64_decode_len(msg_body.len))) return -1; b64_decode.s = body_in.s; b64_decode.len = base64decode((unsigned char*)b64_decode.s, (unsigned char*)msg_body.s, msg_body.len); } else if (hdr_vec[3]) { if (get_body(msg, &msg_body) < 0) { LM_ERR("failed to get body\n"); return -1; } b64_decode.s = msg_body.s; b64_decode.len = msg_body.len; } b64_required=0; if (hdr_vec[2]) { parse_algo_hdr(hdr_vec[3], &algo, &b64_required); } if (b64_required > 0 && hdr_vec[1]) { if (wrap_realloc(&hdr_in, calc_max_base64_decode_len(hdr_vec[1]->body.len))) return -1; hdr_b64_decode.s = hdr_in.s; hdr_b64_decode.len = base64decode( (unsigned char*)hdr_b64_decode.s, (unsigned char*)hdr_vec[1]->body.s, hdr_vec[1]->body.len ); } else if (hdr_vec[1]) { hdr_b64_decode.s = hdr_vec[1]->body.s; hdr_b64_decode.len = hdr_vec[1]->body.len; } switch (hdrs_algo) { case 0: /* deflate */ temp = (unsigned long)BUFLEN; rc = uncompress((unsigned char*)hdr_buf, &temp, (unsigned char*)hdr_b64_decode.s, (unsigned long)hdr_b64_decode.len); uncomp_hdrs.s = hdr_buf; uncomp_hdrs.len = temp; if (check_zlib_rc(rc)) { LM_ERR("header decompression failed\n"); return -1; } break; case 1: /* gzip */ rc = gzip_uncompress( (unsigned char*)hdr_b64_decode.s, (unsigned long)hdr_b64_decode.len, &hdr_out, &temp); if (check_zlib_rc(rc)) { LM_ERR("header decompression failed\n"); return -1; } uncomp_hdrs.s = hdr_out.s; uncomp_hdrs.len = temp; break; case -1: break; default: return -1; } switch (algo) { case 0: /* deflate */ temp = (unsigned long)BUFLEN; rc = uncompress((unsigned char*)body_buf, &temp, (unsigned char*)b64_decode.s, (unsigned long)b64_decode.len); if (check_zlib_rc(rc)) { LM_ERR("body decompression failed\n"); return -1; } uncomp_body.s = body_buf; uncomp_body.len = temp; break; case 1: /* gzip */ rc = gzip_uncompress( (unsigned char*)b64_decode.s, (unsigned long)b64_decode.len, &body_out, &temp); if (check_zlib_rc(rc)) { LM_ERR("body decompression failed\n"); return -1; } uncomp_body.s = body_out.s; uncomp_body.len = temp; break; case -1: LM_DBG("no body\n"); break; default: LM_ERR("invalid algo\n"); return -1; } /* Sort to have the headers in order */ for (i = 0; i < HDRS_TO_SKIP - 1; i++) { for (j = i + 1; j < HDRS_TO_SKIP; j++) { if (!hdr_vec[j]) continue; if (!hdr_vec[i] && hdr_vec[j]) { hdr_vec[i] = hdr_vec[j]; hdr_vec[j] = NULL; } if ((hdr_vec[i] && hdr_vec[j]) && (hdr_vec[i]->name.s > hdr_vec[j]->name.s)) { hf = hdr_vec[i]; hdr_vec[i] = hdr_vec[j]; hdr_vec[j] = hf; } } } int msg_final_len = 0; int msg_ptr=0; for ( i = 0; i < HDRS_TO_SKIP; i++) { if (hdr_vec[i]) { msg_final_len += hdr_vec[i]->name.s - (msg->buf+msg_ptr); msg_ptr += hdr_vec[i]->name.s+hdr_vec[i]->len - (msg->buf+msg_ptr); } } msg_final_len += msg->last_header->name.s + msg->last_header->len - (msg->buf + msg_ptr); if (hdrs_algo >= 0) msg_final_len += uncomp_hdrs.len; if (algo >= 0) msg_final_len += uncomp_body.len; else msg_final_len += strlen(msg->eoh); if (wrap_realloc(&buf_out, msg_final_len)) return -1; msg_ptr = 0; msg_final.len = 0; msg_final.s = buf_out.s; for ( i = 0; i < HDRS_TO_SKIP; i++) { if (hdr_vec[i]) { wrap_copy_and_update(&msg_final.s, msg->buf+msg_ptr, hdr_vec[i]->name.s-(msg->buf+msg_ptr), &msg_final.len); msg_ptr += (hdr_vec[i]->name.s+hdr_vec[i]->len) - (msg->buf+msg_ptr); } } wrap_copy_and_update( &msg_final.s, msg->buf+msg_ptr, (msg->last_header->name.s+msg->last_header->len)- (msg->buf+msg_ptr), &msg_final.len ); if (hdrs_algo >= 0) { wrap_copy_and_update(&msg_final.s, uncomp_hdrs.s, uncomp_hdrs.len,&msg_final.len); } if (algo >= 0) { wrap_copy_and_update(&msg_final.s, uncomp_body.s, uncomp_body.len, &msg_final.len); } else { wrap_copy_and_update(&msg_final.s, msg->eoh, strlen(msg->eoh), &msg_final.len); } /* new buffer because msg_final(out_buf) will * be overwritten at next iteration */ #ifdef DYN_BUF new_buf = pkg_malloc(msg_final.len+1); if (new_buf == NULL) { LM_ERR("no more pkg mem\n"); return -1; } #else new_buf = msg->buf; #endif memcpy(new_buf, msg_final.s, msg_final.len); new_buf[msg_final.len] = '\0'; struct sip_msg tmp; memcpy(&tmp, msg, sizeof(struct sip_msg)); /*reset dst_uri and path_vec to avoid free*/ if (msg->dst_uri.s != NULL) { msg->dst_uri.s = NULL; msg->dst_uri.len = 0; } if (msg->path_vec.s != NULL) { msg->path_vec.s = NULL; msg->path_vec.len = 0; } free_sip_msg(msg); memset(msg, 0, sizeof(struct sip_msg)); /* restore msg fields */ msg->id = tmp.id; msg->rcv = tmp.rcv; msg->set_global_address = tmp.set_global_address; msg->set_global_port = tmp.set_global_port; msg->flags = tmp.flags; msg->msg_flags = tmp.msg_flags; msg->hash_index = tmp.hash_index; msg->force_send_socket = tmp.force_send_socket; msg->dst_uri = tmp.dst_uri; msg->path_vec = tmp.path_vec; /* set the new ones */ msg->buf = new_buf; msg->len = msg_final.len; /* reparse the message */ if (parse_msg(msg->buf, msg->len, msg) != 0) LM_ERR("parse_msg failed\n"); return 1; } static inline void parse_algo_hdr(struct hdr_field* algo_hdr, int* algo, int* b64_required) { int rc; char* delim=NULL; str tok; str s_tok; s_tok.s = algo_hdr->body.s; s_tok.len = algo_hdr->body.len; do { delim = q_memchr(s_tok.s, ATTR_DELIM[0], s_tok.len); if (delim==NULL) { trim_spaces_lr(s_tok); rc = get_algo(&s_tok); } else { tok.s = s_tok.s; tok.len = delim - s_tok.s; s_tok.s = delim+1; s_tok.len = (delim-tok.s+1); trim_spaces_lr(tok); rc = get_algo(&tok); } if (rc < 2 && rc >=0) *algo = rc; else *b64_required = rc; } while(delim); } opensips-2.2.2/modules/compression/compression.h000066400000000000000000000033541300170765700221130ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef _MC_H #define _MC_H #include "../../parser/msg_parser.h" #define MC_BYTE_SIZE 8 #define HDR_MASK_SIZE (((HDR_EOH_T+1)/(sizeof(char) * MC_BYTE_SIZE)) + 1) #define WH_TYPE_STR 0 #define WH_TYPE_PVS 1 #define HDR_TYPE_STR 0 #define HDR_TYPE_INT 1 #include "../../pvar.h" typedef struct mc_other_hdr_lst { str hdr_name; struct mc_other_hdr_lst* next; } mc_other_hdr_lst_t, *mc_other_hdr_lst_p; typedef struct mc_whitelist { unsigned char* hdr_mask; struct mc_other_hdr_lst* other_hdr; } mc_whitelist_t, *mc_whitelist_p; typedef struct mc_param { int type; union { mc_whitelist_p lst; pv_spec_t *pvs; } v; } mc_param_t, *mc_param_p; typedef struct body_fragm { int begin, end; struct body_fragm* next; } body_frag_t, *body_frag_p; struct mc_comp_args { mc_whitelist_p hdr2compress_list; int flags; int algo; mc_param_p wh_param; }; struct mc_cmpct_args { mc_whitelist_p wh_list; mc_param_p wh_param; }; #endif opensips-2.2.2/modules/compression/compression_api.c000066400000000000000000000022501300170765700227310ustar00rootroot00000000000000/* * Copyright (C) 2015 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "compression_api.h" #include "gz_helpers.h" #include "compression_helpers.h" extern int mc_level; int bind_compression(compression_api_t *api) { if (!api) { LM_ERR("invalid parameter value!\n"); return -1; } api->level = mc_level; api->compress = gzip_compress; api->decompress = gzip_uncompress; api->check_rc = check_zlib_rc; return 0; } opensips-2.2.2/modules/compression/compression_api.h000066400000000000000000000035231300170765700227420ustar00rootroot00000000000000/* * Copyright (C) 2015 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef _MC_API_H #define _MC_API_H #include "../../sr_module.h" typedef int (*compress_t)(unsigned char* in, unsigned long ilen, str* out, unsigned long* olen, int level); typedef int (*decompress_t)(unsigned char* in, unsigned long ilen, str* out, unsigned long* olen); typedef int (*check_rc_t)(int rc); typedef struct compression_api { int level; check_rc_t check_rc; compress_t compress; decompress_t decompress; } compression_api_t; typedef int (*bind_compression_t)(compression_api_t* api); int bind_compression(compression_api_t *api); typedef int (*load_compression_f)(compression_api_t *api); static inline int load_compression_api(compression_api_t* api ) { load_compression_f load_compression; /* import the TM auto-loading function */ if ( !(load_compression=(load_compression_f)find_export("load_compression", 0, 0))) { LM_ERR("failed to import load_compression\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_compression( api )==-1) return -1; return 0; } #endif opensips-2.2.2/modules/compression/compression_helpers.c000066400000000000000000000216771300170765700236400ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include "compression_helpers.h" #include "../../parser/hf.h" #include "../../ut.h" #include "../../parser/parse_hname2.h" #include "../../mod_fix.h" #include "zlib.h" #include "../../parser/msg_parser.h" #define CONTENT_ENCODING "Content-Encoding" #define HDR_DELIM '|' #define MAX_HDR_NAME 50 #define NO_FORM 255 static char parse_hdr_name[MAX_HDR_NAME]; /* * Function that receives header type and returns the * compact form character if exists or '\0' instead */ unsigned char get_compact_form(struct hdr_field* hf) { // str content_encoding = str_init(CONTENT_ENCODING); /* Less comparations*/ if (!(compact_form_mask[hf->type/MC_BYTE_SIZE] & (1 << (hf->type%MC_BYTE_SIZE)))) return NO_FORM; switch (hf->type) { case HDR_CONTENTTYPE_T : return 0; case HDR_FROM_T : return 1; case HDR_CALLID_T : return 2; case HDR_SUPPORTED_T : return 3; case HDR_CONTENTLENGTH_T : return 4; case HDR_CONTACT_T : return 5; case HDR_SUBJECT_T : return 6; case HDR_TO_T : return 7; case HDR_VIA_T : return 8; case HDR_SESSION_EXPIRES_T : return 9; /* case HDR_OTHER_T : if (strncasecmp(hf->name.s, content_encoding.s, content_encoding.len)) break; return 'e';*/ default : return NO_FORM; } return 255; } /* * Function that gets header enum value * if exists */ int search_hdr(mc_whitelist_p* wh_list, str* hdr_name) { struct hdr_field hdr; str temp; temp.len = hdr_name->len; temp.s = parse_hdr_name; memcpy(temp.s, hdr_name->s, hdr_name->len); temp.s[temp.len++] = ':'; if (parse_hname2(temp.s, temp.s + temp.len, &hdr) == 0) { LM_ERR("parsing header name\n"); return E_UNSPEC; } if (hdr.type!=HDR_OTHER_T && hdr.type!=HDR_ERROR_T) { (*wh_list)->hdr_mask[hdr.type/MC_BYTE_SIZE] |= (unsigned char)1 << (hdr.type % MC_BYTE_SIZE); LM_DBG("Using flag for hdr\n"); } else { /* if other header put the string */ mc_other_hdr_lst_p hdr_lst = NULL; /* if list null alloc the head of the list*/ if (! (*wh_list)->other_hdr) { hdr_lst = pkg_malloc(sizeof(mc_other_hdr_lst_t)); if ( !hdr_lst ) goto mem; hdr_lst->next = NULL; (*wh_list)->other_hdr = hdr_lst; /* else create the next element and append the string*/ } else { hdr_lst = pkg_malloc(sizeof(mc_other_hdr_lst_t)); if (!hdr_lst) goto mem; hdr_lst->next = NULL; (*wh_list)->other_hdr->next = hdr_lst; (*wh_list)->other_hdr = (*wh_list)->other_hdr->next; } (*wh_list)->other_hdr->hdr_name.s = hdr_name->s; (*wh_list)->other_hdr->hdr_name.len = hdr_name->len; LM_DBG("Using str for hdr\n"); } return 0; mem : LM_ERR("no more pkg mem\n"); return -1; } /* * Function that parses whitelist string */ int parse_whitelist(void** param, mc_whitelist_p* wh_list_p, unsigned char* def_hdrs_mask) { mc_whitelist_p wh_list = *wh_list_p; char* sparam; str hdr_name; int new_hdr = 1, eoh=0; mc_other_hdr_lst_p head = NULL; wh_list = pkg_malloc(sizeof(mc_whitelist_t)); if (!wh_list) { LM_ERR("no more pkg mem\n"); return -1; } wh_list->other_hdr = NULL; wh_list->hdr_mask = pkg_malloc(HDR_MASK_SIZE); if (!wh_list->hdr_mask) { LM_ERR("no more pkg mem\n"); return -1; } if (def_hdrs_mask) memcpy(wh_list->hdr_mask, def_hdrs_mask, HDR_MASK_SIZE); else memset(wh_list->hdr_mask, 0, HDR_MASK_SIZE); if (param == NULL) goto end; sparam = *param; for ( ; *sparam != '\0'; sparam++) { switch (*sparam) { case ' ' : case ';' : case HDR_DELIM : /* The first charcter after header name have have to be ':' for parse_hname2 */ if (eoh) { eoh = 0; if (search_hdr(&wh_list, &hdr_name)) { LM_ERR("cannot find given header\n"); return -1; } /* We must keep the head of the list */ if (!head && wh_list->other_hdr) head = wh_list->other_hdr; } if (*sparam == ' ' || *sparam == ';') break; /* A new header name was found if ','*/ new_hdr = 1; break; default : /* found the first char in header name */ if (new_hdr) { new_hdr = 0; hdr_name.len = 1; hdr_name.s = sparam; eoh = 1; } else { hdr_name.len++; } break; } } /* Last header name which may not have been moved to wh_list */ if (eoh) { if (search_hdr(&wh_list, &hdr_name)) { LM_ERR("cannot find given header\n"); return -1; } } if (head) wh_list->other_hdr = head; end: *wh_list_p = wh_list; return 0; } /* * */ int mc_get_whitelist(struct sip_msg* msg, mc_param_p* wh_param_p, mc_whitelist_p* wh_list_p, unsigned char* def_hdrs_mask) { mc_param_p wh_param = *wh_param_p; pv_value_t value; if (wh_param == NULL) { if (parse_whitelist(NULL, wh_list_p, def_hdrs_mask) != 0) return-1; return 0; } /* Get the whitelist value*/ if (wh_param->type == WH_TYPE_PVS) { if (pv_get_spec_value(msg, wh_param->v.pvs, &value) != 0 || !(value.flags&PV_VAL_STR)) { LM_ERR("no valid PV value found\n"); return -1; } if (parse_whitelist((void**)&value.rs.s, wh_list_p, def_hdrs_mask)) { LM_ERR("Cannot parse whitelist\n"); return -1; } } else { *wh_list_p = wh_param->v.lst; } return 0; } /* * */ int fixup_compression_flags(void** param) { gparam_p gp; char* it; if (!*param) { LM_ERR("NULL parameter given\n"); return -1; } it = *param; gp = pkg_malloc(sizeof(gparam_t)); if (!gp) { LM_ERR("no more pkg mem\n"); return -1; } memset(gp, 0, sizeof(gparam_t)); for ( ; *it != '\0'; it++) { switch (*it) { case 'b' : gp->type = GPARAM_TYPE_INT; gp->v.ival |= BODY_COMP_FLG; break; case 'e': gp->type = GPARAM_TYPE_INT; gp->v.ival |= B64_ENCODED_FLG; break; case 'h' : gp->type = GPARAM_TYPE_INT; gp->v.ival |= HDR_COMP_FLG; break; case 's' : gp->type = GPARAM_TYPE_INT; gp->v.ival |= SEPARATE_COMP_FLG; break; case PV_MARKER: gp->type = GPARAM_TYPE_PVS; if (fixup_pvar(param)) { LM_ERR("parsing pvar failed\n"); return -1; } gp->v.pvs = *param; *param = (void*)gp; return 0; default : LM_ERR("Invalid flags definition\n"); return -1; } } *param = (void*)gp; return 0; } int free_hdr_list(struct hdr_field** hf_p) { struct hdr_field *hf = *hf_p, *temp; while (hf) { temp = hf; hf = hf->next; clean_hdr_field(temp); pkg_free(temp); } return 0; } int free_whitelist(mc_whitelist_p* whitelist_p) { mc_whitelist_p whitelist = *whitelist_p; mc_other_hdr_lst_p temp; while (whitelist->other_hdr) { temp = whitelist->other_hdr; whitelist->other_hdr = whitelist->other_hdr->next; pkg_free(temp); } pkg_free(whitelist->hdr_mask); pkg_free(whitelist); return 0; } int free_hdr_mask(struct hdr_field** hdr_mask) { int i; struct hdr_field *hf1, *hf2; for (i = 0; i < HDR_EOH_T; i++) { if (hdr_mask[i]) { try_again: hf1 = hdr_mask[i]; if (hf1->sibling) { hf2 = hf1->sibling; while (hf2) { hf1 = hf2; hf2 = hf2->sibling; pkg_free(hf1); } } hf1 = hdr_mask[i]; if (hf1->name.s[0] >= 'a') { pkg_free(hf1->name.s); } if (hf1->next) { hdr_mask[i] = hf1->next; pkg_free(hf1); goto try_again; } else { pkg_free(hdr_mask[i]); } } } pkg_free(hdr_mask); return 0; } int check_zlib_rc(int rc) { switch (rc) { case Z_OK: LM_DBG("compression succesfull\n"); return 0; case Z_MEM_ERROR: LM_ERR("not enough memory in compressed buffer\n"); return -1; case Z_BUF_ERROR: LM_ERR("not enough room in output buffer\n"); return -1; case Z_STREAM_ERROR: LM_ERR("invalid compression level\n"); return -1; case Z_DATA_ERROR: LM_ERR("input data incomplete or corrupted\n"); return -1; default: LM_ERR("invalid return code from zlib\n"); return -1; } } int wrap_realloc(str* buf, int new_len) { if (buf->s==NULL) { buf->s = pkg_malloc(new_len); if (!buf->s) goto memerr; buf->len = new_len; } else if (buf->s != NULL && new_len > buf->len) { memset(buf->s, 0, buf->len); buf->s = pkg_realloc(buf->s, new_len); if (!buf->s) goto memerr; buf->len = new_len; } return 0; memerr: LM_ERR("no more pkg mem\n"); return -1; } opensips-2.2.2/modules/compression/compression_helpers.h000066400000000000000000000031101300170765700236230ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef _MC_HLP_H #define _MC_HLP_H #include "compression.h" extern unsigned char* compact_form_mask; extern unsigned char* mnd_hdrs_mask; #define B64_ENCODED_FLG 1 << 0 #define BODY_COMP_FLG 1 << 1 #define HDR_COMP_FLG 1 << 2 #define SEPARATE_COMP_FLG 1 << 3 unsigned char get_compact_form(struct hdr_field*); int search_hdr(mc_whitelist_p*, str*); int build_hdr_masks(void); int parse_whitelist(void**, mc_whitelist_p*, unsigned char*); int mc_get_whitelist(struct sip_msg*, mc_param_p*, mc_whitelist_p*, unsigned char*); int fixup_compression_flags(void**); int free_whitelist(mc_whitelist_p* whitelist); int free_hdr_list(struct hdr_field** hdr_lst_p); int free_hdr_mask(struct hdr_field** hdr_mask); int check_zlib_rc(int rc); int wrap_realloc(str* buf, int new_len); #endif opensips-2.2.2/modules/compression/doc/000077500000000000000000000000001300170765700201415ustar00rootroot00000000000000opensips-2.2.2/modules/compression/doc/compression.xml000066400000000000000000000016031300170765700232240ustar00rootroot00000000000000 %docentities; ]> compression Module &osipsname; Ionut-Razvan Ionita Ionut-Razvan Ionita 2014 &voicesystem; $Revision: 5895 $ $Date$ &admin; opensips-2.2.2/modules/compression/doc/compression_admin.xml000066400000000000000000000434041300170765700244010ustar00rootroot00000000000000 &adminguide;
Overview This module implements message compression/decompression and base64 encoding for sip messages using deflate and gzip algorithm/headers. Another feature of this module is reducing headers to compact for as specified in SIP RFC's, sdp body codec unnecessary description removal (for codecs 0-97), whitelist for headers not be removed (excepting necessary headers).
How it works The module is using zlib library to implement compression and base64 encoding for converting the message to human readable characters. It also uses callbacks to do the compression/compaction of the message in order for this operations to be done after all the other script functions have been applied to the message.
Usage cases As we know, udp fragmentation is a big problem these days, so this module comes to try making the message smaller by any means. The module can be used to compress the body or some headers found in the message or it can decompress compressed messages. There are more possibilities to do this: the body can be compressed along with the specified headers or the headers can be compressed isolated from the body in a specific header. Also the module does message compaction: reduction of sip header names to short form (for example "Via" becomes 'v' and so on), sdp body codec attributes unnecesary description ("a=rtpmap:0 PCMU/8000" becomes "a=rtpmap:0"), unwanted headers removal by specfing the ones you want to keep in a whitelist. The module also does message decompresion and base64 decoding. It can detect the algorithm used for compression from the Content-Encoding header. At this moment only gzip and deflate algorithms are supported.
Dependencies
&osips; Modules The following modules must be loaded before this module: None
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: zlib-dev - the development libraries of zlib.
Exported Parameters
<varname>mc_level</varname> (int) This parameter ranges from 1 to 9 and it specifies the level of compression you want to do. Default is 6. 9 is the best, but the longest time consuming algorithm and 1 is the worst. If, by mistake, you set a lower or a higher level, the default, 6, will be used, but you will receive a warning. Set <varname>mc_level</varname> parameter ... modparam("mc", "mc_level", "3") ...
Exported Functions
<function moreinfo="none">mc_compress([algo], flags, [whitelist])</function> This function will compress the current message as specified in the parameters. Keep in mind that the compression is done just before the message is sent, so that all your lumps can be applied. Meaning of the parameters is as follows: algo - The algorithm used for compression. Currently implemented are deflate ('0') and gzip ('1'). The algo parameter can have the following types: integer - the compression algorithm is statically assigned pvar - the compression algorithm is the value of an existing pseudo-variable (as integer value) flags - Specifies on what to apply the compression and where to put the result of the compression. The flags parameter can have the following types: string - the flags parameter is statically assigned pvar - the flags paramater is the value of an existing pseudo-variable (as string value) The flags parameter can have the following values: b - specifies that the body of the message shall be compressed. Notice that if the message has no body, the flag will have no effect. h - specifies that all the headers, except the mandatory ones (which will be specified in "whitelist" parameter section) and the ones in the whitelist shall be compressed. s - the headers and the body shall be compressed Separately, meaning that a new header named "Comp-Hdrs" will be created, and this header will keep the content of the compressed headers. Also, "Headers-Encoding" header will be created in order to keep the algorithm used to compress the headers. If this flag is not specified, the headers and the body (if 'b' and 'h' flags are specified) will be compressed alltogether in the new body of the message. e - specify that you want base64 Encoding. If you do not specify this flag, by default the module will send the raw compressed message in deflate/gzip format. whitelist - header names list, separated by '|' which will specify which headers shall not be compressed, along with the mandatory ones, which can never be compressed. The mandatory headers are the following: VIA, FROM, TO, CSEQ, ROUTE, RECORD_ROUTE, CALLID. Also, CONTENT_TYPE is mandatory only if CONTENT-LENGTH > 0. Also, in case you do not want to use body compression, the Content-Length header will become a mandatory header, which can not be compressed. In case you do want body compression, the old Content-Length Header will be compressed, and a new content length will be calculated. When you will want to do decompression, the compressed length will be removed, and the content length header will be the same as the one before the compression. This function can be used from REQUEST_ROUTE, LOCAL_ROUTE, FAILURE_ROUTE. <function>mc_compress</function> usage ... if (!mc_compress("0", "bhs", "Max-Forwards|Subject|P-Asserted-Identity")) xlog("compression failed\n"); ... <function>mc_compress</function> usage ... $avp(algo) = "1"; $var(flags) = "bs"; $var(list) = "Max-Forwards | Contact"; mc_compres("$avp(algo", "$var(flags", "$var(list"); xlog("compression registered\n"); ...
<function moreinfo="none">mc_compact([whitelist])</function> This function will realise four different things: headers which are not mandatory and are not in the whitelist will be removed, headers of same type will be put together, separated by ',', header names which have a short form will be reduced to that short form and sdp rtpmap attribute headers which contain a value lower than 98 will be removed, because it is no longer needed. No lumps affected by this function, because it is applied after almost all the processing is done. The mc_compact supported short forms are: c - Content-Type (RFC 3261) f - From (RFC 3261) i - Call-ID (RFC 3261) k - Supported (RFC 3261) l - Content-Length (RFC 3261) m - Contact (RFC 3261) s - Subject (RFC 3261) t - To (RFC 3261) v - Via (RFC 3261) x - Session-Expires (RFC 4028) Meaning of the parameters is as follows: whitelist - Whitelist of headers not to be removed, except from the mandatory ones. The whitelist header names must pe separated by '|'. The algo parameter can have the following types: string - the whitelist is statically assigned pvar - the whitelist is the value of an existing pseudo-variable (as integer value) This function can be used from REQUEST_ROUTE, LOCAL_ROUTE, FAILURE_ROUTE. <function>mc_compress</function> usage ... if (!mc_compact("Max-Forwards|P-Asserted-Identity")) xlog("compaction failed\n"); ...
<function moreinfo="none">mc_decompress()</function> This function does the reverse of mc_compress, meaning that it does base64 decoding and gzip/deflate decompression. Keep in mind that gzip decompression is a little bit more efficient because it is being known the size of the compressed buffer as against deflate which does not hold the size of the buffer, so the decompression will be made in a static buffer. This function requests no parameters. WARNING: This function replaces the original buffer of the message with the decompressed buffer, so any processing you do to the message will not be taken into consideration. Try applying the decompression function, before you do any other processing to the message. This function can be used from REQUEST_ROUTE, LOCAL_ROUTE, FAILURE_ROUTE. <function>mc_decompress</function> usage ... if (!mc_decompress()) xlog("decompression failed\n"); ...
Compression performance test for sip messages The following results have been obtained using the compression function included in the module. Using this results, you can improve the usage of this module, in order to compress only when you think it is favorable enough for you. The algorithm used is deflate for all cases because gzip is always 16 bytes higher than deflate, which represents the uncompressed size modulo 4GB. For the subtests in the same test, the same SIP message have been used. mc_compress performance test results Test Number Subtest Number Body Size Headers to Compress Size Compressed Content Compressed Content Size Compression level Compressed size Compression ratio 1 1 179 82 Body + Headers 261 1 284 0.91 1 2 179 82 Body + Headers 261 9 284 0.91 1 3 179 82 Body 179 1 196 0.91 1 4 179 82 Body 179 9 196 0.91 2 1 838 392 Body + Headers 1230 1 898 1.36 2 2 838 392 Body + Headers 1230 9 872 1.41 2 3 838 392 Body 838 1 568 1.47 2 4 838 392 Body 838 1 540 1.55 3 1 1329 607 Body + Headers 1936 1 1396 1.38 3 2 1329 607 Body + Headers 1936 9 1352 1.43 3 3 1329 607 Body 1329 1 840 1.58 3 4 1329 607 Body + Headers 1329 9 804 1.65
opensips-2.2.2/modules/compression/gz_helpers.c000066400000000000000000000107431300170765700217070ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include "zlib.h" #include "compression_helpers.h" #include "../../ut.h" /* * */ int gzip_compress(unsigned char* in, unsigned long ilen, str* out, unsigned long* olen, int level) { z_stream zlibStream; int rc, neededSize; if (!in || ilen == 0) { LM_ERR("nothing to compress\n"); return -1; } zlibStream.zalloc = Z_NULL; //deflateInit2 will set all the funcions zlibStream.zfree = Z_NULL; //set now with Z_NULL zlibStream.opaque = Z_NULL; zlibStream.total_out = 0; //Total number of out bytes produced so far zlibStream.next_in = in; zlibStream.avail_in = ilen; /* Deflate init parameters: zlibStream - input data level - compression level(1-9) Z_DEFLATED - compression method(only Z_DEFLATED) (15+16) - base two log for the history buffer. For simple deflate should be between 8-15.16 is added for gzip compression level - memory allocated for internal compression state. 8 is default, 1 means less mem 9 maximum mem for better performance Z_DEFAULT_STRATEGY - tune the algorithm.Also Z_FILTERED(data prduced by a filter), Z_HUFFMAN_ONLY force huffman encoding only */ rc = deflateInit2(&zlibStream, level, Z_DEFLATED, (15+16), level, Z_DEFAULT_STRATEGY); if (rc != Z_OK) { return rc; } /* zlib doc states that dest buffer size must be 10% +12 larger than the input buffer */ neededSize = (int)((float)ilen * 1.1 + 12); if (!out->s) { out->s = pkg_malloc(neededSize); out->len = neededSize; if (!out) goto memerr; } else if (ilen > out->len) { out->s = pkg_realloc(out->s, neededSize); out->len = neededSize; if (!out->s) goto memerr; } do { zlibStream.next_out = (unsigned char*)(out->s + zlibStream.total_out); zlibStream.avail_out = neededSize - zlibStream.total_out; rc = deflate(&zlibStream, Z_FINISH); } while (rc == Z_OK); if (rc != Z_STREAM_END) { deflateEnd(&zlibStream); return rc; } *olen = zlibStream.total_out; deflateEnd(&zlibStream); return Z_OK; memerr: LM_ERR("no more pkg mem\n"); return -1; } /* * */ int gzip_uncompress(unsigned char* in, unsigned long ilen, str* out, unsigned long* olen) { z_stream zlibStream; int rc, neededSize; if (!in || !ilen) { LM_ERR("nothing to compress\n"); return -1; } /* Gzip holds the length of the original message in the last 4 bytes */ *olen = (in[ilen-1] << 24) + (in[ilen-2] << 16) + (in[ilen-3] << 8) + in[ilen-4]; neededSize = *olen+1; /*'\0'*/ zlibStream.zalloc = Z_NULL; zlibStream.zfree = Z_NULL; zlibStream.opaque = Z_NULL; zlibStream.avail_in = 0; zlibStream.next_in = Z_NULL; zlibStream.total_out = 0; /* zlib doc says that window for inflateInit must be at least equal with the window used for compression */ rc = inflateInit2(&zlibStream, (15+16)); if (rc != Z_OK) return rc; if (!out->s) { out->s = pkg_malloc(neededSize); out->len = neededSize; if (!out->s) goto memerr; } else if (*olen > out->len) { out->s = pkg_realloc(out->s, neededSize); out->len = neededSize; if (!out->s) goto memerr; } zlibStream.avail_in = ilen; zlibStream.next_in = in; do { zlibStream.avail_out = neededSize - zlibStream.total_out; zlibStream.next_out = (unsigned char*)out->s + zlibStream.total_out; rc = inflate(&zlibStream, Z_NO_FLUSH); switch (rc) { case Z_NEED_DICT: rc = Z_DATA_ERROR; case Z_DATA_ERROR: case Z_MEM_ERROR: case Z_BUF_ERROR: inflateEnd(&zlibStream); return rc; } } while (rc != Z_STREAM_END); if (rc != Z_STREAM_END) { deflateEnd(&zlibStream); return rc; } deflateEnd(&zlibStream); return Z_OK; memerr: inflateEnd(&zlibStream); LM_ERR("no more pkg mem\n"); return -1; } opensips-2.2.2/modules/compression/gz_helpers.h000066400000000000000000000020751300170765700217130ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef GZ_HELPERS #define GZ_HELPERS #include "compression.h" int gzip_compress(unsigned char* in, unsigned long ilen, str* out, unsigned long* olen, int level); int gzip_uncompress(unsigned char* in, unsigned long ilen, str* out, unsigned long* olen); #endif opensips-2.2.2/modules/cpl_c/000077500000000000000000000000001300170765700161135ustar00rootroot00000000000000opensips-2.2.2/modules/cpl_c/CPL_tree.h000066400000000000000000000176251300170765700177340ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CPL_TREE_DEFINITION_H #define _CPL_TREE_DEFINITION_H #define CPL_NODE 1 #define INCOMING_NODE 2 #define OUTGOING_NODE 3 #define ANCILLARY_NODE 4 #define SUBACTION_NODE 5 #define ADDRESS_SWITCH_NODE 6 #define ADDRESS_NODE 7 #define BUSY_NODE 8 #define DEFAULT_NODE 9 #define FAILURE_NODE 10 #define LOG_NODE 11 #define LOOKUP_NODE 12 #define LOCATION_NODE 13 #define LANGUAGE_NODE 14 #define LANGUAGE_SWITCH_NODE 15 #define MAIL_NODE 16 #define NOTFOUND_NODE 17 #define NOANSWER_NODE 18 #define PROXY_NODE 19 #define PRIORITY_NODE 20 #define PRIORITY_SWITCH_NODE 21 #define REJECT_NODE 22 #define REDIRECT_NODE 23 #define REDIRECTION_NODE 24 #define REMOVE_LOCATION_NODE 25 #define SUB_NODE 26 #define SUCCESS_NODE 27 #define STRING_NODE 28 #define STRING_SWITCH_NODE 29 #define TIME_NODE 30 #define TIME_SWITCH_NODE 31 #define OTHERWISE_NODE 32 #define NOT_PRESENT_NODE 33 /* attributes and values fro ADDRESS-SWITCH node */ #define FIELD_ATTR 0 /*shared with STRING_SWITCH*/ #define SUBFIELD_ATTR 1 #define ORIGIN_VAL 0 #define DESTINATION_VAL 1 #define ORIGINAL_DESTINATION_VAL 2 #define ADDRESS_TYPE_VAL 0 #define USER_VAL 1 #define HOST_VAL 2 #define PORT_VAL 3 #define TEL_VAL 4 #define DISPLAY_VAL 5 /*shared with STRING*/ /* attributes and values for ADDRESS node */ #define IS_ATTR 0 /*shared with STRING*/ #define CONTAINS_ATTR 1 /*shared with STRING*/ #define SUBDOMAIN_OF_ATTR 2 /* attributes and values for STRING-SWITCH node */ #define SUBJECT_VAL 0 #define ORGANIZATION_VAL 1 #define USER_AGENT_VAL 2 /* attributes and values for LANGUAGE node */ #define MATCHES_TAG_ATTR 0 #define MATCHES_SUBTAG_ATTR 1 /* attributes and values for TIME-SWITCH node */ #define TZID_ATTR 0 #define TZURL_ATTR 1 /* attributes and values for TIME node */ #define DTSTART_ATTR 0 #define DTEND_ATTR 1 #define DURATION_ATTR 2 #define FREQ_ATTR 3 #define INTERVAL_ATTR 4 #define UNTIL_ATTR 5 #define COUNT_ATTR 6 #define BYSECOND_ATTR 7 #define BYMINUTE_ATTR 8 #define BYHOUR_ATTR 9 #define BYDAY_ATTR 10 #define BYMONTHDAY_ATTR 11 #define BYYEARDAY_ATTR 12 #define BYWEEKNO_ATTR 13 #define BYMONTH_ATTR 14 #define WKST_ATTR 15 #define BYSETPOS_ATTR 16 /* attributes and values for PRIORITY node */ #define LESS_ATTR 0 #define GREATER_ATTR 1 #define EQUAL_ATTR 2 #define PRIOSTR_ATTR 3 #define EMERGENCY_VAL 0 #define EMERGENCY_STR "emergency" #define EMERGENCY_STR_LEN (sizeof(EMERGENCY_STR)-1) #define URGENT_VAL 1 #define URGENT_STR "urgent" #define URGENT_STR_LEN (sizeof(URGENT_STR)-1) #define NORMAL_VAL 2 #define NORMAL_STR "normal" #define NORMAL_STR_LEN (sizeof(NORMAL_STR)-1) #define NON_URGENT_VAL 3 #define NON_URGENT_STR "non-urgent" #define NON_URGENT_STR_LEN (sizeof(NON_URGENT_STR)-1) #define UNKNOWN_PRIO_VAL 4 /* attributes and values for LOCATION node */ #define URL_ATTR 0 #define PRIORITY_ATTR 1 #define CLEAR_ATTR 2 /*shared with LOOKUP node*/ #define NO_VAL 0 /*shared with LOOKUP node*/ #define YES_VAL 1 /*shared with LOOKUP node*/ /* attributes and values for LOOKUP node */ #define SOURCE_ATTR 0 #define TIMEOUT_ATTR 1 /*shared with PROXY node*/ #define SOURCE_REG_STR "registration" #define SOURCE_REG_STR_LEN (sizeof("registration")-1) /* attributes and values for REMOVE_LOCATION node */ #define LOCATION_ATTR 0 #define PARAM_ATTR 1 #define VALUE_ATTR 2 /* attributes and values for PROXY node */ #define RECURSE_ATTR 2 #define ORDERING_ATTR 3 #define PARALLEL_VAL 0 #define SEQUENTIAL_VAL 1 #define FIRSTONLY_VAL 2 /* attributes and values for REDIRECT node */ #define PERMANENT_ATTR 0 /* attributes and values for REJECT node */ #define STATUS_ATTR 0 #define REASON_ATTR 1 #define BUSY_VAL 486 #define BUSY_STR "busy" #define BUSY_STR_LEN (sizeof(BUSY_STR)-1) #define NOTFOUND_VAL 404 #define NOTFOUND_STR "notfound" #define NOTFOUND_STR_LEN (sizeof(NOTFOUND_STR)-1) #define REJECT_VAL 603 #define REJECT_STR "reject" #define REJECT_STR_LEN (sizeof(REJECT_STR)-1) #define ERROR_VAL 500 #define ERROR_STR "error" #define ERROR_STR_LEN (sizeof(ERROR_STR)-1) /* attributes and values for LOG node */ #define NAME_ATTR 0 #define MAX_NAME_SIZE 32 #define COMMENT_ATTR 1 #define MAX_COMMENT_SIZE 128 /* attributes and values for EMAIL node */ #define TO_ATTR 0 #define SUBJECT_ATTR 1 #define SUBJECT_EMAILHDR_STR "subject" #define SUBJECT_EMAILHDR_LEN (sizeof(SUBJECT_EMAILHDR_STR)-1) #define BODY_ATTR 2 #define BODY_EMAILHDR_STR "body" #define BODY_EMAILHDR_LEN (sizeof(BODY_EMAILHDR_STR)-1) #define URL_MAILTO_STR "mailto:" #define URL_MAILTO_LEN (sizeof(URL_MAILTO_STR)-1) /* attributes and values for SUB node */ #define REF_ATTR 0 /* node = | type(1) | nr_kids(1) | nr_attrs(1) | unused(1) | * | x*kids_offset(2) | y*attrs(2*n) | */ #define NODE_TYPE(_p) ( *((unsigned char*)(_p)) ) #define NR_OF_KIDS(_p) ( *((unsigned char* )((_p)+1)) ) #define NR_OF_ATTR(_p) ( *((unsigned char* )((_p)+1+1)) ) #define KID_OFFSET_PTR(_p,_n) ( (unsigned short*)((_p)+4+2*(_n)) ) #define ATTR_PTR(_p) ( (_p)+4+2*NR_OF_KIDS(_p) ) #define SIMPLE_NODE_SIZE(_p) ( 4+2*NR_OF_KIDS(_p) ) #define GET_NODE_SIZE(_n) ( 4+2*(_n) ) #define BASIC_ATTR_SIZE 4 #define SET_KID_OFFSET(_p,_n,_o) *KID_OFFSET_PTR(_p,_n)=htons(_o) #define KID_OFFSET(_p,_n) ntohs(*KID_OFFSET_PTR(_p,_n)) #endif opensips-2.2.2/modules/cpl_c/Makefile000066400000000000000000000010001300170765700175420ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=cpl_c.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/cpl_c/README000066400000000000000000000361441300170765700170030ustar00rootroot00000000000000cpl_c Module Bogdan-Andrei Iancu Edited by Bogdan-Andrei Iancu Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. db_table (string) 1.3.3. username_column (string) 1.3.4. domain_column (string) 1.3.5. cpl_xml_column (string) 1.3.6. cpl_bin_column (string) 1.3.7. cpl_dtd_file (string) 1.3.8. log_dir (string) 1.3.9. proxy_recurse (int) 1.3.10. proxy_route (string) 1.3.11. case_sensitive (int) 1.3.12. realm_prefix (string) 1.3.13. lookup_domain (string) 1.3.14. lookup_append_branches (int) 1.3.15. use_domain (integer) 1.4. Exported Functions 1.4.1. cpl_run_script(type,mode) 1.4.2. cpl_process_register() 1.4.3. cpl_process_register_norpl() 1.5. Exported MI Functions 1.5.1. LOAD_CPL 1.5.2. REMOVE_CPL 1.5.3. GET_CPL 1.6. Installation and Running 1.6.1. Database setup List of Examples 1.1. Set db_url parameter 1.2. Set db_table parameter 1.3. Set username_column parameter 1.4. Set domain_column parameter 1.5. Set cpl_xml_column parameter 1.6. Set cpl_bin_column parameter 1.7. Set cpl_dtd_file parameter 1.8. Set log_dir parameter 1.9. Set proxy_recurse parameter 1.10. Set proxy_route parameter 1.11. Set case_sensitive parameter 1.12. Set realm_prefix parameter 1.13. Set lookup_domain parameter 1.14. Set lookup_append_branches parameter 1.15. Set use_domain parameter 1.16. cpl_run_script usage 1.17. cpl_process_register usage 1.18. cpl_process_register_norpl usage Chapter 1. Admin Guide 1.1. Overview cpl_c modules implements a CPL (Call Processing Language) interpreter. Support for uploading/downloading/removing scripts via SIP REGISTER method is present. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * any DB module- a DB module for interfacing the DB operations (modules like mysql, postgres, dbtext, etc) * TM (Transaction) module- used for proxying/forking requests * SL (StateLess) module - used for sending stateless reply when responding to REGISTER request or for sending back error responses * USRLOC (User Location) module - used for implementing lookup("registration") tag (adding into location set of the users' contact) 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml2 and libxml2-devel - on some SO, these to packages are merged into libxml2. This library contains an engine for XML parsing, DTD validation and DOM manipulation. 1.3. Exported Parameters 1.3.1. db_url (string) A SQL URL have to be given to the module for knowing where the database containing the table with CPL scripts is locates. If required a user name and password can be specified for allowing the module to connect to the database server. Default value is “mysql://opensips:opensipsrw@localhost/opensipsâ€. Example 1.1. Set db_url parameter ... modparam("cpl_c","db_url","dbdriver://username:password@dbhost/dbname") ... 1.3.2. db_table (string) Indicates the name of the table that store the CPL scripts. This table must be locate into the database specified by “db_url†parameter. For more about the format of the CPL table please see the modules/cpl_c/init.mysql file. Default value is “cplâ€. Example 1.2. Set db_table parameter ... modparam("cpl_c","cpl_table","cpl") ... 1.3.3. username_column (string) Indicates the name of the column used for storing the username. Default value is “usernameâ€. Example 1.3. Set username_column parameter ... modparam("cpl_c","username_column","username") ... 1.3.4. domain_column (string) Indicates the name of the column used for storing the domain. Default value is “domainâ€. Example 1.4. Set domain_column parameter ... modparam("cpl_c","domain_column","domain") ... 1.3.5. cpl_xml_column (string) Indicates the name of the column used for storing the the XML version of the cpl script. Default value is “cpl_xmlâ€. Example 1.5. Set cpl_xml_column parameter ... modparam("cpl_c","cpl_xml_column","cpl_xml") ... 1.3.6. cpl_bin_column (string) Indicates the name of the column used for storing the the binary version of the cpl script (compiled version). Default value is “cpl_binâ€. Example 1.6. Set cpl_bin_column parameter ... modparam("cpl_c","cpl_bin_column","cpl_bin") ... 1.3.7. cpl_dtd_file (string) Points to the DTD file describing the CPL grammar. The file name may include also the path to the file. This path can be absolute or relative (be careful the path will be relative to the starting directory of OpenSIPS). This parameter is MANDATORY! Example 1.7. Set cpl_dtd_file parameter ... modparam("cpl_c","cpl_dtd_file","/etc/opensips/cpl-06.dtd") ... 1.3.8. log_dir (string) Points to a directory where should be created all the log file generated by the LOG CPL node. A log file per user will be created (on demand) having the name username.log. If this parameter is absent, the logging will be disabled without generating error on execution. Example 1.8. Set log_dir parameter ... modparam("cpl_c","log_dir","/var/log/opensips/cpl") ... 1.3.9. proxy_recurse (int) Tells for how many time is allow to have recurse for PROXY CPL node If it has value 2, when doing proxy, only twice the proxy action will be re-triggered by a redirect response; the third time, the proxy execution will end by going on REDIRECTION branch. The recurse feature can be disable by setting this parameter to 0 Default value of this parameter is 0. Example 1.9. Set proxy_recurse parameter ... modparam("cpl_c","proxy_recurse",2) ... 1.3.10. proxy_route (string) Before doing proxy (forward), a script route can be executed. All modifications made by that route will be reflected only for the current branch. Default value of this parameter is NULL (none). Example 1.10. Set proxy_route parameter ... modparam("cpl_c","proxy_route", "1") ... 1.3.11. case_sensitive (int) Tells if the username matching should be perform case sensitive or not. Set it to a non zero value to force a case sensitive handling of usernames. Default value of this parameter is 0. Example 1.11. Set case_sensitive parameter ... modparam("cpl_c","case_sensitive",1) ... 1.3.12. realm_prefix (string) Defines a prefix for the domain part which should be ignored in handling users and scripts. Default value of this parameter is empty string. Example 1.12. Set realm_prefix parameter ... modparam("cpl_c","realm_prefix","sip.") ... 1.3.13. lookup_domain (string) Used by lookup tag to indicate where to perform user location. Basically this is the name of the usrloc domain (table) where the user registrations are kept. If set to empty string, the lookup node will be disabled - no user location will be performed. Default value of this parameter is NULL. Example 1.13. Set lookup_domain parameter ... modparam("cpl_c","lookup_domain","location") ... 1.3.14. lookup_append_branches (int) Tells if the lookup tag should append branches (to do parallel forking) if user_location lookup returns more than one contact. Set it to a non zero value to enable parallel forking for location lookup tag. Default value of this parameter is 0. Example 1.14. Set lookup_append_branches parameter ... modparam("cpl_c","lookup_append_branches",1) ... 1.3.15. use_domain (integer) Indicates if the domain part of the URI should be used in user identification (otherwise only username part will be used). Default value is “0 (disabled)â€. Example 1.15. Set use_domain parameter ... modparam("cpl_c","use_domain",1) ... 1.4. Exported Functions 1.4.1. cpl_run_script(type,mode) Starts the execution of the CPL script. The user name is fetched from new_uri or requested uri or from To header -in this order- (for incoming execution) or from FROM header (for outgoing execution). Regarding the stateful/stateless message processing, the function is very flexible, being able to run in different modes (see below the"mode" parameter). Normally this function will end script execution. There is no guaranty that the CPL script interpretation ended when OpenSIPS script ended also (for the same INVITE ;-)) - this can happen when the CPL script does a PROXY and the script interpretation pause after proxying and it will be resume when some reply is received (this can happen in a different process of OpenSIPS). If the function returns true to script, if value "1" is returned, the SIP server should continue with the normal behavior as if no script existed; if value (2) is returned, it means no script was found, so nothing was done. When some error is reported (a false return code), the function itself haven't sent any SIP error reply (this can be done from script). Meaning of the parameters is as follows: * type - which part of the script should be run; set it to "incoming" for having the incoming part of script executed (when an INVITE is received) or to "outgoing" for running the outgoing part of script (when a user is generating an INVITE - call). * mode - sets the interpreter mode as stateless/stateful behavior. The following modes are accepted: + IS_STATELESS - the current INVITE has no transaction created yet. All replies (redirection or deny) will be done is a stateless way. The execution will switch to stateful only when proxy is done. So, if the function returns, will be in stateless mode. + IS_STATEFUL - the current INVITE has already a transaction associated. All signaling operations (replies or proxy) will be done in stateful way.So, if the function returns, will be in stateful mode. + FORCE_STATEFUL - the current INVITE has no transaction created yet. All signaling operations will be done is a stateful way (on signaling, the transaction will be created from within the interpreter). So, if the function returns, will be in stateless mode. HINT: is_stateful is very difficult to manage from the routing script (script processing can continue in stateful mode); is_stateless is the fastest and less resources consumer (transaction is created only if proxying is done), but there is minimal protection against retransmissions (since replies are send stateless); force_stateful is a good compromise - all signaling is done stateful (retransmission protection) and in the same time, if returning to script, it will be in stateless mode (easy to continue the routing script execution) This function can be used from REQUEST_ROUTE. Example 1.16. cpl_run_script usage ... cpl_run_script("incoming","force_stateful"); ... 1.4.2. cpl_process_register() This function MUST be called only for REGISTER requests. It checks if the current REGISTER request is related or not with CPL script upload/download/ remove. If it is, all the needed operation will be done. For checking if the REGISTER is CPL related, the function looks fist to "Content-Type" header. If it exists and has a the mime type set to "application/cpl+xml" means this is a CPL script upload/remove operation. The distinction between to case is made by looking at "Content-Disposition" header; id its value is "script;action=store", means it's an upload; if it's "script;action=remove", means it's a remove operation; other values are considered to be errors. If no "Content-Type" header is present, the function looks to "Accept" header and if it contains the "*" or "application/cpl-xml" the request it will be consider one for downloading CPL scripts. The functions returns to script only if the REGISTER is not related to CPL. In other case, the function will send by itself the necessary replies (stateless - using sl), including for errors. This function can be used from REQUEST_ROUTE. Example 1.17. cpl_process_register usage ... if (method=="REGISTER") { cpl_process_register(); } ... 1.4.3. cpl_process_register_norpl() Same as “cpl_process_register†without internally generating the reply. All information (script) is appended to the reply but without sending it out. Main purpose of this function is to allow integration between CPL and UserLocation services via same REGISTER messages. This function can be used from REQUEST_ROUTE. Example 1.18. cpl_process_register_norpl usage ... if (method=="REGISTER") { cpl_process_register(); # continue with usrloc part save("location"); } ... 1.5. Exported MI Functions 1.5.1. LOAD_CPL For the given user, loads the XML cpl file, compiles it into binary format and stores both format into database. Name: LOAD_CPL Parameters: * username : name of the user * cpl_filename: file name MI FIFO Command format: :LOAD_CPL:_reply_fifo_file_ username cpl_filename _empty_line_ 1.5.2. REMOVE_CPL For the given user, removes the entire database record (XML cpl and binary cpl); user with empty cpl scripts are not accepted. Name: REMOVE_CPL Parameters: * username : name of the user MI FIFO Command format: :REMOVE_CPL:_reply_fifo_file_ username _empty_line_ 1.5.3. GET_CPL For the given user, returns the CPL script in XML format. Name: GET_CPL Parameters: * username : name of the user MI FIFO Command format: :GET_CPL:_reply_fifo_file_ username _empty_line_ 1.6. Installation and Running 1.6.1. Database setup Before running OpenSIPS with cpl_c, you have to setup the database table where the module will store the CPL scripts. For that, if the table was not created by the installation script or you choose to install everything by yourself you can use the cpc-create.sql SQL script in the database directories in the opensips/scripts folder as template. Database and table name can be set with module parameters so they can be changed, but the name of the columns must be as they are in the SQL script. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. opensips-2.2.2/modules/cpl_c/cpl-06.dtd000066400000000000000000000163461300170765700176230ustar00rootroot00000000000000 opensips-2.2.2/modules/cpl_c/cpl.c000066400000000000000000000577401300170765700170520ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) * 2004-06-06 updated to the new DB api (andrei) * 2004-06-14: all global variables merged into cpl_env and cpl_fct; * case_sensitive and realm_prefix added for building AORs - see * build_userhost (bogdan) * 2004-10-09: added process_register_norpl to allow register processing * without sending the reply(bogdan) - based on a patch sent by * Christopher Crawford */ #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../str.h" #include "../../ut.h" #include "../../dprint.h" #include "../../data_lump_rpl.h" #include "../../pvar.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../parser/parse_content.h" #include "../../parser/parse_disposition.h" #include "../../db/db.h" #include "../../mi/mi.h" #include "../sl/sl_api.h" #include "cpl_run.h" #include "cpl_env.h" #include "cpl_db.h" #include "cpl_loader.h" #include "cpl_parser.h" #include "cpl_nonsig.h" #include "loc_set.h" #define MAX_PROXY_RECURSE 10 #define MAX_USERHOST_LEN 256 /* modules param variables */ static str db_url = {NULL, 0}; /* database url */ static str db_table = str_init("cpl"); /* database table */ static char *dtd_file = 0; /* name of the DTD file for CPL parser */ static char *lookup_domain = 0; static char* proxy_route = NULL; struct cpl_enviroment cpl_env = { 0, /* no cpl logging */ 0, /* recurse proxy level is 0 */ 0, /* no script route to be run before proxy */ 0, /* user part is not case sensitive */ {0,0}, /* no domain prefix to be ignored */ {-1,-1}, /* communication pipe to aux_process */ {0,0}, /* original TZ \0 terminated "TZ=value" format */ 0, /* udomain */ 0, /* no branches on lookup */ 0 /* use_domain */ }; struct cpl_functions cpl_fct; static str cpl_ok_rpl = str_init("OK"); static int cpl_invoke_script (struct sip_msg* msg, char* str, char* str2); static int w_process_register(struct sip_msg* msg, char* str, char* str2); static int w_process_register_norpl(struct sip_msg* msg, char* str,char* str2); static int cpl_process_register(struct sip_msg* msg, int no_rpl); static int fixup_cpl_run_script(void** param, int param_no); static int cpl_init(void); static int cpl_child_init(int rank); static int cpl_exit(void); static void cpl_process(int rank); /* * Exported processes */ static proc_export_t cpl_procs[] = { {"CPL Aux", 0, 0, cpl_process, 1, 0 }, {0,0,0,0,0,0} }; /* * Exported functions */ static cmd_export_t cmds[] = { {"cpl_run_script", (cmd_function)cpl_invoke_script, 2, fixup_cpl_run_script, 0, REQUEST_ROUTE}, {"cpl_process_register", (cmd_function)w_process_register, 0, 0, 0, REQUEST_ROUTE}, {"cpl_process_register_norpl",(cmd_function)w_process_register_norpl, 0, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &db_url.s }, {"db_table", STR_PARAM, &db_table.s }, {"cpl_dtd_file", STR_PARAM, &dtd_file }, {"proxy_recurse", INT_PARAM, &cpl_env.proxy_recurse }, {"proxy_route", STR_PARAM, &proxy_route }, {"log_dir", STR_PARAM, &cpl_env.log_dir }, {"case_sensitive", INT_PARAM, &cpl_env.case_sensitive }, {"realm_prefix", STR_PARAM, &cpl_env.realm_prefix.s }, {"lookup_domain", STR_PARAM, &lookup_domain }, {"lookup_append_branches", INT_PARAM, &cpl_env.lu_append_branches}, {"username_column",STR_PARAM, &cpl_username_col }, {"domain_column", STR_PARAM, &cpl_domain_col }, {"cpl_xml_column", STR_PARAM, &cpl_xml_col }, {"cpl_bin_column", STR_PARAM, &cpl_bin_col }, {"use_domain", INT_PARAM, &cpl_env.use_domain }, {0, 0, 0} }; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { { "LOAD_CPL", 0, mi_cpl_load, 0, 0, 0 }, { "REMOVE_CPL", 0, mi_cpl_remove, 0, 0, 0 }, { "GET_CPL", 0, mi_cpl_get, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; static module_dependency_t *get_deps_lookup_domain(param_export_t *param) { char *domain = *(char **)param->param_pointer; if (!domain || strlen(domain) == 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "usrloc", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "lookup_domain", get_deps_lookup_domain }, { NULL, NULL }, }, }; struct module_exports exports = { "cpl_c", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ cpl_procs,/* extra processes */ cpl_init, /* Module initialization function */ (response_function) 0, (destroy_function) cpl_exit, (child_init_function) cpl_child_init /* per-child init function */ }; static int fixup_cpl_run_script(void** param, int param_no) { long flag; if (param_no==1) { if (!strcasecmp( "incoming", *param)) flag = CPL_RUN_INCOMING; else if (!strcasecmp( "outgoing", *param)) flag = CPL_RUN_OUTGOING; else { LM_ERR("script directive \"%s\" unknown!\n",(char*)*param); return E_UNSPEC; } pkg_free(*param); *param=(void*)flag; return 0; } else if (param_no==2) { if ( !strcasecmp("is_stateless", *param) ) { flag = 0; } else if ( !strcasecmp("is_stateful", *param) ) { flag = CPL_IS_STATEFUL; } else if ( !strcasecmp("force_stateful", *param) ) { flag = CPL_FORCE_STATEFUL; } else { LM_ERR("flag \"%s\" (second param) unknown!\n",(char*)*param); return E_UNSPEC; } pkg_free(*param); *param=(void*)flag; } return 0; } static int cpl_init(void) { bind_usrloc_t bind_usrloc; struct stat stat_t; char *ptr; int val; init_db_url( db_url , 0 /*cannot be null*/); db_table.len = strlen(db_table.s); LM_INFO("initializing...\n"); if (proxy_route && proxy_route[0]) { cpl_env.proxy_route = get_script_route_ID_by_name( proxy_route, rlist, RT_NO); if (cpl_env.proxy_route==-1) { LM_ERR("route <%s> does not exist\n",proxy_route); return -1; } } if (cpl_env.proxy_recurse>MAX_PROXY_RECURSE) { LM_CRIT("value of proxy_recurse param (%d) exceeds " "the maximum safety value (%d)\n", cpl_env.proxy_recurse,MAX_PROXY_RECURSE); goto error; } if (dtd_file==0) { LM_CRIT("mandatory parameter \"cpl_dtd_file\" found empty\n"); goto error; } else { /* check if the dtd file exists */ if (stat( dtd_file, &stat_t)==-1) { LM_ERR("checking file \"%s\" status failed; stat returned %s\n", dtd_file,strerror(errno)); goto error; } if ( !S_ISREG( stat_t.st_mode ) ) { LM_ERR("dir \"%s\" is not a regular file!\n", dtd_file); goto error; } if (access( dtd_file, R_OK )==-1) { LM_ERR("checking file \"%s\" for permissions " "failed; access returned %s\n",dtd_file,strerror(errno)); goto error; } } if (cpl_env.log_dir==0) { LM_INFO("log_dir param found empty -> logging disabled!\n"); } else { if ( strlen(cpl_env.log_dir)>MAX_LOG_DIR_SIZE ) { LM_ERR("dir \"%s\" has a too long name :-(!\n", cpl_env.log_dir); goto error; } /* check if the dir exists */ if (stat( cpl_env.log_dir, &stat_t)==-1) { LM_ERR("checking dir \"%s\" status failed;" " stat returned %s\n",cpl_env.log_dir,strerror(errno)); goto error; } if ( !S_ISDIR( stat_t.st_mode ) ) { LM_ERR("dir \"%s\" is not a directory!\n", cpl_env.log_dir); goto error; } if (access( cpl_env.log_dir, R_OK|W_OK )==-1) { LM_ERR("checking dir \"%s\" for permissions failed; access " "returned %s\n", cpl_env.log_dir, strerror(errno)); goto error; } } /* bind to the mysql module */ if (cpl_db_bind(&db_url, &db_table)<0) goto error; /* load TM API */ if (load_tm_api(&cpl_fct.tmb)!=0) { LM_ERR("can't load TM API\n"); goto error; } /* load SIGNALING API */ if(load_sig_api(&cpl_fct.sigb)< 0) { LM_ERR("can't load signaling functions\n"); return -1; } /* bind to usrloc module if requested */ if (lookup_domain) { /* import all usrloc functions */ bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (!bind_usrloc) { LM_ERR("can't bind usrloc\n"); goto error; } if (bind_usrloc( &(cpl_fct.ulb) ) < 0) { LM_ERR("importing usrloc failed\n"); goto error; } /* convert lookup_domain from char* to udomain_t* pointer */ if (cpl_fct.ulb.register_udomain( lookup_domain, &cpl_env.lu_domain) < 0) { LM_ERR("failed to register domain <%s>\n",lookup_domain); goto error; } } else { LM_NOTICE("no lookup_domain given -> disable lookup node\n"); } /* build a pipe for sending commands to aux process */ if ( pipe( cpl_env.cmd_pipe )==-1 ) { LM_CRIT("cannot create command pipe: %s!\n", strerror(errno) ); goto error; } /* set the writing non blocking */ if ( (val=fcntl(cpl_env.cmd_pipe[1], F_GETFL, 0))<0 ) { LM_ERR("getting flags from pipe[1] failed: fcntl said %s!\n", strerror(errno)); goto error; } if ( fcntl(cpl_env.cmd_pipe[1], F_SETFL, val|O_NONBLOCK) ) { LM_ERR("setting flags to pipe[1] failed: fcntl said %s!\n", strerror(errno)); goto error; } /* init the CPL parser */ if (init_CPL_parser( dtd_file )!=1 ) { LM_ERR("init_CPL_parser failed!\n"); goto error; } /* make a copy of the original TZ env. variable */ ptr = getenv("TZ"); cpl_env.orig_tz.len = 3/*"TZ="*/ + (ptr?(strlen(ptr)+1):0); if ( (cpl_env.orig_tz.s=shm_malloc( cpl_env.orig_tz.len ))==0 ) { LM_ERR("no more shm mem. for saving TZ!\n"); goto error; } memcpy(cpl_env.orig_tz.s,"TZ=",3); if (ptr) strcpy(cpl_env.orig_tz.s+3,ptr); /* convert realm_prefix from string null terminated to str */ if (cpl_env.realm_prefix.s) { cpl_env.realm_prefix.len = strlen(cpl_env.realm_prefix.s); /* convert the realm_prefix to lower cases */ strlower( &cpl_env.realm_prefix ); } return 0; error: return -1; } static int cpl_child_init(int rank) { return cpl_db_init(&db_url, &db_table); } static void cpl_process(int rank) { cpl_aux_process( cpl_env.cmd_pipe[0], cpl_env.log_dir); exit(-1); } static int cpl_exit(void) { /* free the TZ orig */ if (cpl_env.orig_tz.s) shm_free(cpl_env.orig_tz.s); return 0; } static inline int build_user_AOR(str *username, str *domain, str *uh, int sip) { unsigned char do_strip; char *p; int i; /* calculate the len (without terminating \0) */ uh->len = 4*(sip!=0) + username->len; do_strip = 0; if (sip || cpl_env.use_domain) { /* do we need to strip realm prefix? */ if (cpl_env.realm_prefix.len && cpl_env.realm_prefix.lenlen){ for( i=cpl_env.realm_prefix.len-1 ; i>=0 ; i-- ) if ( cpl_env.realm_prefix.s[i]!=tolower(domain->s[i]) ) break; if (i==-1) do_strip = 1; } uh->len += 1 + domain->len - do_strip*cpl_env.realm_prefix.len; } uh->s = (char*)shm_malloc( uh->len + 1 ); if (!uh->s) { LM_ERR("no more shm memory.\n"); return -1; } /* build user@host */ p = uh->s; if (sip) { memcpy( uh->s, "sip:", 4); p += 4; } /* user part */ if (cpl_env.case_sensitive) { memcpy( p, username->s, username->len); p += username->len; } else { for(i=0;ilen;i++) *(p++) = tolower(username->s[i]); } if (sip || cpl_env.use_domain) { *(p++) = '@'; /* host part in lower cases */ for( i=do_strip*cpl_env.realm_prefix.len ; i< domain->len ; i++ ) *(p++) = tolower(domain->s[i]); } *(p++) = 0; /* sanity check */ if (p-uh->s!=uh->len+1) { LM_CRIT("buffer overflow l=%d,w=%ld\n", uh->len,(long)(p-uh->s)); return -1; } return 0; } static inline int get_dest_user(struct sip_msg *msg, str *username, str *domain) { struct sip_uri uri; /* get the user_name from new_uri/RURI/To */ LM_DBG("trying to get user from new_uri\n"); if ( !msg->new_uri.s || parse_uri( msg->new_uri.s,msg->new_uri.len,&uri)<0 || !uri.user.len ) { LM_DBG("trying to get user from R_uri\n"); if ( parse_uri( msg->first_line.u.request.uri.s, msg->first_line.u.request.uri.len ,&uri)==-1 || !uri.user.len ) { LM_DBG("trying to get user from To\n"); if ( (!msg->to&&((parse_headers(msg,HDR_TO_F,0)==-1)||!msg->to))|| parse_uri( get_to(msg)->uri.s, get_to(msg)->uri.len, &uri)<0 || !uri.user.len) { LM_ERR("unable to extract user name from RURI or To header!\n"); return -1; } } } *username = uri.user; *domain = uri.host; return 0; } static inline int get_orig_user(struct sip_msg *msg, str *username, str *domain) { struct to_body *from; struct sip_uri uri; /* if it's outgoing -> get the user_name from From */ /* parsing from header */ LM_DBG("trying to get user from From\n"); if ( parse_from_header( msg )==-1 ) { LM_ERR("unable to extract URI from FROM header\n"); return -1; } from = (struct to_body*)msg->from->parsed; /* parse the extracted uri from From */ if (parse_uri( from->uri.s, from->uri.len, &uri)||!uri.user.len) { LM_ERR("unable to extract user name from URI (From header)\n"); return -1; } *username = uri.user; *domain = uri.host; return 0; } /* Params: * str1 - as unsigned int - can be CPL_RUN_INCOMING or CPL_RUN_OUTGOING * str2 - as unsigned int - flags regarding state(less)|(ful) */ static int cpl_invoke_script(struct sip_msg* msg, char* str1, char* str2) { struct cpl_interpreter *cpl_intr; str username = {0,0}; str domain = {0,0}; str loc; str script; /* get the user_name */ if ( ((unsigned long)str1)&CPL_RUN_INCOMING ) { /* if it's incoming -> get the destination user name */ if (get_dest_user( msg, &username, &domain)==-1) goto error0; } else { /* if it's outgoing -> get the origin user name */ if (get_orig_user( msg, &username, &domain)==-1) goto error0; } /* get the script for this user */ if (get_user_script(&username, cpl_env.use_domain?&domain:0, &script, &cpl_bin_col)==-1) goto error0; /* has the user a non-empty script? if not, return normally, allowing the * script execution to continue */ if ( !script.s || !script.len ) return 2; /* build a new script interpreter */ if ( (cpl_intr=new_cpl_interpreter(msg,&script))==0 ) goto error1; /* set the flags */ cpl_intr->flags =(unsigned int)((unsigned long)str1)|((unsigned long)str2); /* build user AOR */ if (build_user_AOR( &username, &domain, &(cpl_intr->user), 0)!=0 ) goto error2; /* for OUTGOING we need also the destination user for init. with him * the location set */ if ( ((unsigned long)str1)&CPL_RUN_OUTGOING ) { /* build user initial location -> get the destination user name */ if (get_dest_user( msg, &username, &domain)==-1) goto error2; if (build_user_AOR( &username, &domain, &loc, 1)!=0 ) goto error2; if (add_location( &(cpl_intr->loc_set), &loc, 0, 10,CPL_LOC_DUPL)==-1){ shm_free(loc.s); goto error2; } shm_free(loc.s); } /* run the script */ switch (cpl_run_script( cpl_intr )) { case SCRIPT_DEFAULT: if ( cpl_intr->flags&CPL_DO_NOT_FREE ) cpl_intr->flags |= CPL_ENDED; else free_cpl_interpreter( cpl_intr ); return 1; /* execution of ser's script will continue */ case SCRIPT_END: if ( cpl_intr->flags&CPL_DO_NOT_FREE ) cpl_intr->flags |= CPL_ENDED; else free_cpl_interpreter( cpl_intr ); case SCRIPT_TO_BE_CONTINUED: return 0; /* break the SER script */ case SCRIPT_RUN_ERROR: case SCRIPT_FORMAT_ERROR: goto error2; } return 1; error2: if ( cpl_intr->flags&CPL_DO_NOT_FREE ) cpl_intr->flags |= CPL_ENDED; else free_cpl_interpreter( cpl_intr ); return -1; error1: shm_free(script.s); error0: return -1; } #define CPL_SCRIPT "script" #define CPL_SCRIPT_LEN (sizeof(CPL_SCRIPT)-1) #define ACTION_PARAM "action" #define ACTION_PARAM_LEN (sizeof(ACTION_PARAM)-1) #define STORE_ACTION "store" #define STORE_ACTION_LEN (sizeof(STORE_ACTION)-1) #define REMOVE_ACTION "remove" #define REMOVE_ACTION_LEN (sizeof(REMOVE_ACTION)-1) #define REMOVE_SCRIPT 0xcaca #define STORE_SCRIPT 0xbebe #define CONTENT_TYPE_HDR ("Content-Type: application/cpl-xml"CRLF) #define CONTENT_TYPE_HDR_LEN (sizeof(CONTENT_TYPE_HDR)-1) struct cpl_error { int err_code; str err_msg; }; static struct cpl_error bad_req = {400,str_init("Bad request")}; static struct cpl_error intern_err = {500,str_init("Internal server error")}; static struct cpl_error bad_cpl = {400,str_init("Bad CPL script")}; static struct cpl_error *cpl_err = &bad_req; static inline int do_script_action(struct sip_msg *msg, int action) { str body = {0,0}; str bin = {0,0}; str log = {0,0}; str username = {0,0}; str domain = {0,0}; if ( get_body(msg, &body)!=0 ) { LM_ERR("failed to look for body!\n"); goto error; } /* get the user name */ if (get_dest_user( msg, &username, &domain)==-1) goto error; /* we have the script and the user */ switch (action) { case STORE_SCRIPT : /* check the len -> it must not be 0 */ if (body.len==0) { LM_ERR("0 content-len found for store\n"); goto error_1; } /* now compile the script and place it into database */ /* get the binary coding for the XML file */ if ( encodeCPL( &body, &bin, &log)!=1) { cpl_err = &bad_cpl; goto error_1; } /* write both the XML and binary formats into database */ if (write_to_db( &username, cpl_env.use_domain?&domain:0, &body,&bin)!=1) { cpl_err = &intern_err; goto error_1; } break; case REMOVE_SCRIPT: /* check the len -> it must be 0 */ if (body.len!=0) { LM_ERR("non-0 content-len found for remove\n"); goto error_1; } /* remove the script for the user */ if (rmv_from_db( &username, cpl_env.use_domain?&domain:0)!=1) { cpl_err = &intern_err; goto error_1; } break; } if (log.s) pkg_free( log.s ); return 0; error_1: if (log.s) pkg_free( log.s ); error: return -1; } static inline int do_script_download(struct sip_msg *msg) { str username = {0,0}; str domain = {0,0}; str script = {0,0}; /* get the destination user name */ if (get_dest_user( msg, &username, &domain)!=0) goto error; /* get the user's xml script from the database */ if (get_user_script( &username, cpl_env.use_domain?&domain:0, &script, &cpl_xml_col)==-1) goto error; /* add a lump with content-type hdr */ if (add_lump_rpl( msg, CONTENT_TYPE_HDR, CONTENT_TYPE_HDR_LEN, LUMP_RPL_HDR)==0) { LM_ERR("cannot build hdr lump\n"); cpl_err = &intern_err; goto error; } if (script.s!=0) { /* user has a script -> add a body lump */ if ( add_lump_rpl( msg, script.s, script.len, LUMP_RPL_BODY)==0) { LM_ERR("cannot build body lump\n"); cpl_err = &intern_err; goto error; } /* build_lump_rpl duplicates the added text, so free the original */ shm_free( script.s ); } return 0; error: if (script.s) shm_free(script.s); return -1; } static int w_process_register(struct sip_msg* msg, char* str, char* str2) { return cpl_process_register( msg, 0); } static int w_process_register_norpl(struct sip_msg* msg, char* str,char* str2) { return cpl_process_register( msg, 1); } static int cpl_process_register(struct sip_msg* msg, int no_rpl) { struct disposition *disp; struct disposition_param *param; int ret; int mime; int *mimes; /* make sure that is a REGISTER ??? */ /* here should be the CONTACT- hack */ /* is there a CONTENT-TYPE hdr ? */ mime = parse_content_type_hdr( msg ); if (mime==-1) goto error; /* check the mime type */ LM_DBG("Content-Type mime found %u, %u\n", mime>>16,mime&0x00ff); if ( mime && mime==(TYPE_APPLICATION<<16)+SUBTYPE_CPLXML ) { /* can be an upload or remove -> check for the content-purpose and * content-action headers */ LM_DBG("carrying CPL -> look at Content-Disposition\n"); if (parse_content_disposition( msg )!=0) { LM_ERR("Content-Disposition missing or corrupted\n"); goto error; } disp = get_content_disposition(msg); print_disposition( disp ); /* just for DEBUG */ /* check if the type of disposition is SCRIPT */ if (disp->type.len!=CPL_SCRIPT_LEN || strncasecmp(disp->type.s,CPL_SCRIPT,CPL_SCRIPT_LEN) ) { LM_ERR("bogus message - Content-Type" "says CPL_SCRIPT, but Content-Disposition something else\n"); goto error; } /* disposition type is OK -> look for action parameter */ for(param=disp->params;param;param=param->next) { if (param->name.len==ACTION_PARAM_LEN && !strncasecmp(param->name.s,ACTION_PARAM,ACTION_PARAM_LEN)) break; } if (param==0) { LM_ERR("bogus message - " "Content-Disposition has no action param\n"); goto error; } /* action param found -> check its value: store or remove */ if (param->body.len==STORE_ACTION_LEN && !strncasecmp( param->body.s, STORE_ACTION, STORE_ACTION_LEN)) { /* it's a store action -> get the script from body message and store * it into database (CPL and BINARY format) */ if (do_script_action( msg, STORE_SCRIPT)==-1) goto error; } else if (param->body.len==REMOVE_ACTION_LEN && !strncasecmp( param->body.s, REMOVE_ACTION, REMOVE_ACTION_LEN)) { /* it's a remove action -> remove the script from database */ if (do_script_action( msg, REMOVE_SCRIPT)==-1) goto error; } else { LM_ERR("unknown action <%.*s>\n", param->body.len,param->body.s); goto error; } /* do I have to send to reply? */ if (no_rpl) goto resume_script; /* send a 200 OK reply back */ cpl_fct.sigb.reply( msg, 200, &cpl_ok_rpl,NULL); /* I send the reply and I don't want to return to script execution, so * I return 0 to do break */ goto stop_script; } /* is there an ACCEPT hdr ? */ if ( (ret=parse_accept_hdr(msg))<0) goto error; if (ret==0 || (mimes=get_accept(msg))==0 ) /* accept header not present or no mimes found */ goto resume_script; /* looks if the REGISTER accepts cpl-xml or * */ while (*mimes) { LM_DBG("accept mime found %u, %u\n", (*mimes)>>16,(*mimes)&0x00ff); if (*mimes==(TYPE_ALL<<16)+SUBTYPE_ALL || *mimes==(TYPE_APPLICATION<<16)+SUBTYPE_CPLXML ) break; mimes++; } if (*mimes==0) /* no accept mime that matched cpl */ goto resume_script; /* get the user name from msg, retrieve the script from db * and appended to reply */ if (do_script_download( msg )==-1) goto error; /* do I have to send to reply? */ if (no_rpl) goto resume_script; /* send a 200 OK reply back */ cpl_fct.sigb.reply( msg, 200, &cpl_ok_rpl,NULL); stop_script: return 0; resume_script: return 1; error: /* send a error reply back */ cpl_fct.sigb.reply( msg, cpl_err->err_code, &cpl_err->err_msg, NULL); /* I don't want to return to script execution, so I return 0 to do break */ return 0; } opensips-2.2.2/modules/cpl_c/cpl_db.c000066400000000000000000000146411300170765700175100ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * History: * -------- * 2004-06-06 updated to the new DB api (andrei) */ #include "../../mem/shm_mem.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../str.h" #include "cpl_db.h" #define TABLE_VERSION 2 static db_con_t* db_hdl=0; static db_func_t cpl_dbf; str cpl_username_col = str_init("username"); str cpl_domain_col = str_init("domain"); str cpl_xml_col = str_init("cpl_xml"); str cpl_bin_col = str_init("cpl_bin"); int cpl_db_bind(const str* db_url, const str *db_table) { if (db_bind_mod(db_url, &cpl_dbf )) { LM_CRIT("cannot bind to database module! " "Did you forget to load a database module ?\n"); return -1; } /* CPL module uses all database functions */ if (!DB_CAPABILITY(cpl_dbf, DB_CAP_ALL)) { LM_CRIT("Database modules does not " "provide all functions needed by cpl_c module\n"); return -1; } if ( cpl_db_init( db_url, db_table) ) return -1; if(db_check_table_version(&cpl_dbf, db_hdl, db_table, TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); cpl_db_close(); return -1; } cpl_db_close(); return 0; } int cpl_db_init(const str* db_url, const str* db_table) { if (cpl_dbf.init==0){ LM_CRIT("BUG - unbound database module\n"); return -1; } db_hdl=cpl_dbf.init(db_url); if (db_hdl==0){ LM_CRIT("cannot initialize database connection\n"); return -1; } if (cpl_dbf.use_table(db_hdl, db_table)<0) { LM_CRIT("cannot select table \"%.*s\"\n",db_table->len, db_table->s); cpl_db_close(); return -1; } return 0; } void cpl_db_close(void) { if (db_hdl && cpl_dbf.close){ cpl_dbf.close(db_hdl); db_hdl=0; } } /* gets from database the cpl script in binary format; the returned script is * allocated in shared memory * Returns: 1 - success * -1 - error */ int get_user_script(str *username, str *domain, str *script, str* key) { db_key_t keys_cmp[2]; db_key_t keys_ret[1]; db_val_t vals[2]; db_res_t *res = NULL; int n; keys_cmp[0] = &cpl_username_col; keys_cmp[1] = &cpl_domain_col; keys_ret[0] = key; LM_DBG("fetching script for user <%.*s>\n", username->len,username->s); vals[0].type = DB_STR; vals[0].nul = 0; vals[0].val.str_val = *username; n = 1; if (domain) { vals[1].type = DB_STR; vals[1].nul = 0; vals[1].val.str_val = *domain; n++; } if (cpl_dbf.query(db_hdl, keys_cmp, 0, vals, keys_ret, n, 1, NULL, &res) < 0){ LM_ERR("db_query failed\n"); goto error; } if (res->n==0) { LM_DBG("user <%.*s> not found in db -> probably " "he has no script\n",username->len, username->s); script->s = 0; script->len = 0; } else { if (res->rows[0].values[0].nul) { LM_DBG("user <%.*s> has a NULL script\n", username->len, username->s); script->s = 0; script->len = 0; } else { LM_DBG("we got the script len=%d\n", res->rows[0].values[0].val.blob_val.len); script->len = res->rows[0].values[0].val.blob_val.len; script->s = shm_malloc( script->len ); if (!script->s) { LM_ERR("no free sh_mem\n"); goto error; } memcpy( script->s, res->rows[0].values[0].val.blob_val.s, script->len); } } cpl_dbf.free_result( db_hdl, res); return 1; error: if (res) cpl_dbf.free_result( db_hdl, res); script->s = 0; script->len = 0; return -1; } /* inserts into database a cpl script in XML format(xml) along with its binary * format (bin) * Returns: 1 - success * -1 - error */ int write_to_db(str *username, str *domain, str *xml, str *bin) { db_key_t keys[4]; db_val_t vals[4]; db_res_t *res = NULL; int n; /* lets see if the user is already in database */ keys[2] = &cpl_username_col; vals[2].type = DB_STR; vals[2].nul = 0; vals[2].val.str_val = *username; n = 1; if (domain) { keys[3] = &cpl_domain_col; vals[3].type = DB_STR; vals[3].nul = 0; vals[3].val.str_val = *domain; n++; } if (cpl_dbf.query(db_hdl, keys+2, 0, vals+2, keys+2, n, 1, NULL, &res)<0) { LM_ERR("db_query failed\n"); goto error; } if (res->n>1) { LM_ERR("Inconsistent CPL database:" " %d records for user %.*s\n",res->n,username->len,username->s); goto error; } /* cpl text */ keys[0] = &cpl_xml_col; vals[0].type = DB_BLOB; vals[0].nul = 0; vals[0].val.blob_val.s = xml->s; vals[0].val.blob_val.len = xml->len; n++; /* cpl bin */ keys[1] = &cpl_bin_col; vals[1].type = DB_BLOB; vals[1].nul = 0; vals[1].val.blob_val.s = bin->s; vals[1].val.blob_val.len = bin->len; n++; /* insert or update ? */ if (res->n==0) { LM_DBG("no user %.*s in CPL database->insert\n", username->len,username->s); if (cpl_dbf.insert(db_hdl, keys, vals, n) < 0) { LM_ERR("insert failed !\n"); goto error; } } else { LM_DBG("user %.*s already in CPL database ->" " update\n",username->len,username->s); if (cpl_dbf.update(db_hdl, keys+2, 0, vals+2, keys, vals, n-2, 2) < 0) { LM_ERR("update failed !\n"); goto error; } } return 1; error: return -1; } /* delete from database the entity record for a given user - if a user has no * script, he will be removed completely from db; users without script are not * allowed into db ;-) * Returns: 1 - success * -1 - error */ int rmv_from_db(str *username, str *domain) { db_key_t keys[2]; db_val_t vals[2]; int n; /* username */ keys[0] = &cpl_username_col; vals[0].type = DB_STR; vals[0].nul = 0; vals[0].val.str_val = *username; n = 1; if (domain) { keys[1] = &cpl_domain_col; vals[1].type = DB_STR; vals[1].nul = 0; vals[1].val.str_val = *domain; n++; } if (cpl_dbf.delete(db_hdl, keys, NULL, vals, n) < 0) { LM_ERR("failed to delete script for " "user \"%.*s\"\n",username->len,username->s); return -1; } return 1; } opensips-2.2.2/modules/cpl_c/cpl_db.h000066400000000000000000000034311300170765700175100ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CPL_DB_H #define _CPL_DB_H #include "../../db/db.h" int cpl_db_bind(const str* db_url, const str* db_table); int cpl_db_init(const str* db_url, const str* db_table); void cpl_db_close(); extern str cpl_username_col; extern str cpl_domain_col; extern str cpl_xml_col; extern str cpl_bin_col; /* inserts into database a cpl script in XML format(xml) along with its binary * format (bin) * Returns: 1 - success * -1 - error */ int write_to_db(str *username, str*domain, str *xml, str *bin); /* fetch from database the binary format of the cpl script for a given user * Returns: 1 - success * -1 - error */ int get_user_script(str *username, str*domain, str *script, str *key); /* delete from database the entire record for a given user - if a user has no * script, he will be removed completely from db; users without script are not * allowed into db ;-) * Returns: 1 - success * -1 - error */ int rmv_from_db(str *username, str *domain); #endif opensips-2.2.2/modules/cpl_c/cpl_env.h000066400000000000000000000041511300170765700177130ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 11-06-1004: created (bogdan) */ #ifndef _CPL_C_ENV_H #define _CPL_C_ENV_H #include "../../str.h" #include "../../usr_avp.h" #include "../usrloc/usrloc.h" #include "../tm/tm_load.h" #include "../signaling/signaling.h" struct cpl_enviroment { char *log_dir; /* dir where the user log should be dumped */ int proxy_recurse; /* numbers of proxy redirection accepted */ int proxy_route; /* script route to be run before proxy */ int case_sensitive; /* is user part case sensitive ? */ str realm_prefix; /* domain prefix to be ignored */ int cmd_pipe[2]; /* communication pipe with aux. process */ str orig_tz; /* a copy of the original TZ; kept as a null * terminated string in "TZ=value" format; * used only by run_time_switch */ udomain_t* lu_domain; /* domain used for lookup */ int lu_append_branches; /* how many branches lookup should add */ int use_domain; }; struct cpl_functions { struct tm_binds tmb; /* Structure with pointers to tm funcs */ usrloc_api_t ulb; /* Structure with pointers to usrloc funcs */ struct sig_binds sigb; /* Structure with pointers to signaling funcs */ }; extern struct cpl_enviroment cpl_env; extern struct cpl_functions cpl_fct; #endif opensips-2.2.2/modules/cpl_c/cpl_loader.c000066400000000000000000000211511300170765700203630ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice-Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2003-08-21: cpl_remove() added (bogdan) * 2003-06-24: file created (bogdan) */ #include #include #include #include #include #include #include #include #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_uri.h" #include "../../mi/mi.h" #include "cpl_db.h" #include "cpl_env.h" #include "cpl_parser.h" #include "cpl_loader.h" extern db_con_t* db_hdl; #if 0 /* debug function -> write into a file the content of a str struct. */ int write_to_file(char *filename, str *buf) { int fd; int ret; fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0644); if (!fd) { LM_ERR("cannot open file : %s\n", strerror(errno)); goto error; } while ( (ret=write( fd, buf->s, buf->len))!=buf->len) { if ((ret==-1 && errno!=EINTR)|| ret!=-1) { LM_ERR("cannot write to file:" "%s write_ret=%d\n",strerror(errno), ret ); goto error; } } close(fd); return 0; error: return -1; } #endif /* Loads a file into a buffer; first the file length will be determined for * allocated an exact buffer len for storing the file content into. * Returns: 1 - success * -1 - error */ int load_file( char *filename, str *xml) { int n; int offset; int fd; xml->s = 0; xml->len = 0; /* open the file for reading */ fd = open(filename,O_RDONLY); if (fd==-1) { LM_ERR("cannot open file for reading:" " %s\n",strerror(errno)); goto error; } /* get the file length */ if ( (xml->len=lseek(fd,0,SEEK_END))==-1) { LM_ERR("cannot get file length (lseek):" " %s\n", strerror(errno)); goto error; } LM_DBG("file size = %d\n",xml->len); if ( lseek(fd,0,SEEK_SET)==-1 ) { LM_ERR("cannot go to beginning (lseek):" " %s\n",strerror(errno)); goto error; } /* get some memory */ xml->s = (char*)pkg_malloc( xml->len+1/*null terminated*/ ); if (!xml->s) { LM_ERR("no more free pkg memory\n"); goto error; } /*start reading */ offset = 0; while ( offsetlen ) { n=read( fd, xml->s+offset, xml->len-offset); if (n==-1) { if (errno!=EINTR) { LM_ERR("read failed:" " %s\n", strerror(errno)); goto error; } } else { if (n==0) break; offset += n; } } if (xml->len!=offset) { LM_ERR("couldn't read all file!\n"); goto error; } xml->s[xml->len] = 0; close(fd); return 1; error: if (fd!=-1) close(fd); if (xml->s) pkg_free( xml->s); return -1; } /* Writes an array of texts into the given response file. * Accepts also empty texts, case in which it will be created an empty * response file. */ void write_to_file( char *file, str *txt, int n ) { int fd; /* open file for write */ fd = open( file, O_WRONLY|O_CREAT|O_TRUNC/*|O_NOFOLLOW*/, 0600 ); if (fd==-1) { LM_ERR("cannot open response file " "<%s>: %s\n", file, strerror(errno)); return; } /* write the txt, if any */ if (n>0) { again: if ( writev( fd, (struct iovec*)txt, n)==-1) { if (errno==EINTR) { goto again; } else { LM_ERR("write_logs_to_file: writev failed: " "%s\n", strerror(errno) ); } } } /* close the file*/ close( fd ); return; } /**************************** MI ****************************/ #define FILE_LOAD_ERR_S "Cannot read CPL file" #define FILE_LOAD_ERR_LEN (sizeof(FILE_LOAD_ERR_S)-1) #define DB_SAVE_ERR_S "Cannot save CPL to database" #define DB_SAVE_ERR_LEN (sizeof(DB_SAVE_ERR_S)-1) #define CPLFILE_ERR_S "Bad CPL file" #define CPLFILE_ERR_LEN (sizeof(CPLFILE_ERR_S)-1) #define USRHOST_ERR_S "Bad user@host" #define USRHOST_ERR_LEN (sizeof(USRHOST_ERR_S)-1) #define DB_RMV_ERR_S "Database remove failed" #define DB_RMV_ERR_LEN (sizeof(DB_RMV_ERR_S)-1) #define DB_GET_ERR_S "Database query failed" #define DB_GET_ERR_LEN (sizeof(DB_GET_ERR_S)-1) struct mi_root* mi_cpl_load(struct mi_root *cmd_tree, void *param) { struct mi_root *rpl_tree; struct mi_node *cmd; struct sip_uri uri; str xml = {0,0}; str bin = {0,0}; str enc_log = {0,0}; str val; char *file; LM_DBG("\"LOAD_CPL\" MI command received!\n"); cmd = &cmd_tree->node; /* check user+host */ if((cmd->kids==NULL) ||(cmd->kids->next==NULL) || (cmd->kids->next->next)) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); val = cmd->kids->value; if (parse_uri( val.s, val.len, &uri)!=0){ LM_ERR("invalid sip URI [%.*s]\n", val.len, val.s); return init_mi_tree( 400, USRHOST_ERR_S, USRHOST_ERR_LEN ); } LM_DBG("user@host=%.*s@%.*s\n", uri.user.len,uri.user.s,uri.host.len,uri.host.s); /* second argument is the cpl file */ val = cmd->kids->next->value; file = pkg_malloc(val.len+1); if (file==NULL) { LM_ERR("no more pkg mem\n"); return 0; } memcpy( file, val.s, val.len); file[val.len]= '\0'; /* load the xml file - this function will allocated a buff for the loading * the cpl file and attach it to xml.s -> don't forget to free it! */ if (load_file( file, &xml)!=1) { pkg_free(file); return init_mi_tree( 500, FILE_LOAD_ERR_S, FILE_LOAD_ERR_LEN ); } LM_DBG("cpl file=%s loaded\n",file); pkg_free(file); /* get the binary coding for the XML file */ if (encodeCPL( &xml, &bin, &enc_log)!=1) { rpl_tree = init_mi_tree( 500, CPLFILE_ERR_S, CPLFILE_ERR_LEN ); goto error; } /* write both the XML and binary formats into database */ if (write_to_db( &uri.user,cpl_env.use_domain?&uri.host:0, &xml, &bin)!=1){ rpl_tree = init_mi_tree( 500, DB_SAVE_ERR_S, DB_SAVE_ERR_LEN ); goto error; } /* everything was OK */ rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: if (rpl_tree && enc_log.len) add_mi_node_child(&rpl_tree->node,MI_DUP_VALUE,"Log",3,enc_log.s,enc_log.len); if (enc_log.s) pkg_free ( enc_log.s ); if (xml.s) pkg_free ( xml.s ); return rpl_tree; } struct mi_root * mi_cpl_remove(struct mi_root *cmd_tree, void *param) { struct mi_node *cmd; struct sip_uri uri; str user; LM_DBG("\"REMOVE_CPL\" MI command received!\n"); cmd = &cmd_tree->node; /* check if there is only one parameter*/ if(!(cmd->kids && cmd->kids->next== NULL)) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); user = cmd->kids->value; /* check user+host */ if (parse_uri( user.s, user.len, &uri)!=0){ LM_ERR("invalid SIP uri [%.*s]\n", user.len,user.s); return init_mi_tree( 400, USRHOST_ERR_S, USRHOST_ERR_LEN ); } LM_DBG("user@host=%.*s@%.*s\n", uri.user.len,uri.user.s,uri.host.len,uri.host.s); if (rmv_from_db( &uri.user, cpl_env.use_domain?&uri.host:0)!=1) return init_mi_tree( 500, DB_RMV_ERR_S, DB_RMV_ERR_LEN ); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } struct mi_root * mi_cpl_get(struct mi_root *cmd_tree, void *param) { struct mi_node *cmd; struct sip_uri uri; struct mi_root* rpl_tree; str script = {0,0}; str user; cmd = &cmd_tree->node; /* check if there is only one parameter*/ if(!(cmd->kids && cmd->kids->next== NULL)) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* check user+host */ user = cmd->kids->value; if (parse_uri( user.s, user.len, &uri)!=0) { LM_ERR("invalid user@host [%.*s]\n", user.len,user.s); return init_mi_tree( 400, USRHOST_ERR_S, USRHOST_ERR_LEN ); } LM_DBG("user@host=%.*s@%.*s\n", uri.user.len,uri.user.s,uri.host.len,uri.host.s); /* get the script for this user */ str query_str = str_init("cpl_xml"); if (get_user_script( &uri.user, cpl_env.use_domain?&uri.host:0, &script, &query_str)==-1) return init_mi_tree( 500, DB_GET_ERR_S, DB_GET_ERR_LEN ); /* write the response into response file - even if script is null */ rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree!=NULL) add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, 0, 0, script.s, script.len); if (script.s) shm_free( script.s ); return rpl_tree; } opensips-2.2.2/modules/cpl_c/cpl_loader.h000066400000000000000000000021771300170765700203770ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2003-06-24: file created (bogdan) */ #ifndef _CPL_LOADER_H #define _CPL_LOADER_H #include "../../mi/mi.h" struct mi_root *mi_cpl_load(struct mi_root *cmd, void *param); struct mi_root *mi_cpl_remove(struct mi_root *cmd, void *param); struct mi_root *mi_cpl_get(struct mi_root *cmd, void *param); #endif opensips-2.2.2/modules/cpl_c/cpl_log.c000066400000000000000000000035601300170765700177020ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2003-09-22: created (bogdan) * */ #include #include #include "cpl_log.h" #include "../../mem/mem.h" #include "../../dprint.h" static str cpl_logs[MAX_LOG_NR]; static int nr_logs; void reset_logs(void) { nr_logs = 0; } void append_log( int nr, ...) { va_list ap; int i; if ( nr_logs+nr>MAX_LOG_NR ) { LM_ERR("no more space for logging\n"); return; } va_start(ap, nr); for(i=0;is = 0; log->len = 0; if (nr_logs==0) /* no logs */ return; /* compile the total len */ for(i=0;ilen += cpl_logs[i].len; /* get a buffer */ log->s = (char*)pkg_malloc(log->len); if (log->s==0) { LM_ERR("no more pkg mem\n"); log->len = 0; return; } /*copy all logs into buffer */ p = log->s; for(i=0;i #include "../../str.h" #define MAX_LOG_NR 64 #define ERR "Error: " #define ERR_LEN (sizeof(ERR)-1) #define WARN "Warning: " #define WARN_LEN (sizeof(WARN)-1) #define NOTE "Notice: " #define NOTE_LEN (sizeof(NOTE)-1) #define LF "\n" #define LF_LEN (1) void reset_logs(); void append_log( int nr, ...); void compile_logs( str *log); #endif opensips-2.2.2/modules/cpl_c/cpl_nonsig.c000066400000000000000000000152551300170765700204220ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-06-27: file created (bogdan) */ #include #include #include #include #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "cpl_nonsig.h" #include "CPL_tree.h" #define MAX_LOG_FILE_NAME 32 #define FILE_NAME_SUFIX ".log" #define FILE_NAME_SUFIX_LEN (sizeof(FILE_NAME_SUFIX)-1) #define LOG_SEPARATOR ": " #define LOG_SEPARATOR_LEN (sizeof(LOG_SEPARATOR)-1) #define DEFAULT_LOG_NAME "default_log" #define DEFAULT_LOG_NAME_LEN (sizeof(DEFAULT_LOG_NAME)-1) #define LOG_TERMINATOR "\n" #define LOG_TERMINATOR_LEN (sizeof(LOG_TERMINATOR)-1) static char file[MAX_LOG_DIR_SIZE+1+MAX_LOG_FILE_NAME+FILE_NAME_SUFIX_LEN+1]; static char *file_ptr; static inline void write_log( struct cpl_cmd *cmd) { struct iovec wr_vec[5]; time_t now; char *time_ptr; int fd; int ret; /* build file name (cmd->s1 is the user name)*/ if (cmd->s1.len>MAX_LOG_FILE_NAME) cmd->s1.len = MAX_LOG_FILE_NAME; memcpy(file_ptr, cmd->s1.s, cmd->s1.len ); memcpy(file_ptr+cmd->s1.len,FILE_NAME_SUFIX,FILE_NAME_SUFIX_LEN); file_ptr[cmd->s1.len+FILE_NAME_SUFIX_LEN] = 0; /* get current date+time -> wr_vec[0] */ time( &now ); time_ptr = ctime( &now ); wr_vec[0].iov_base = time_ptr; wr_vec[0].iov_len = strlen( time_ptr ); /* ctime_r adds a \n at the end -> overwrite it with space */ time_ptr[ wr_vec[0].iov_len-1 ] = ' '; /* log name (cmd->s2) -> wr_vec[1] */ if (cmd->s2.s==0 || cmd->s2.len==0) { wr_vec[1].iov_base = DEFAULT_LOG_NAME; wr_vec[1].iov_len = DEFAULT_LOG_NAME_LEN; } else { wr_vec[1].iov_base = cmd->s2.s; wr_vec[1].iov_len = cmd->s2.len; } /* log separator -> wr_vec[2] */ wr_vec[2].iov_base = LOG_SEPARATOR; wr_vec[2].iov_len = LOG_SEPARATOR_LEN; /* log comment (cmd->s3) -> wr_vec[3] */ wr_vec[3].iov_base = cmd->s3.s; wr_vec[3].iov_len = cmd->s3.len; /* log terminator -> wr_vec[2] */ wr_vec[4].iov_base = LOG_TERMINATOR; wr_vec[4].iov_len = LOG_TERMINATOR_LEN; /* [create+]open the file */ fd = open( file, O_CREAT|O_APPEND|O_WRONLY, 0664); if (fd==-1) { LM_ERR("cannot open file [%s] : %s\n", file, strerror(errno) ); return; } /* get the log */ LM_DBG("logging into [%s]... \n",file); /* I'm really not interested in the return code for write ;-) */ while ( (ret=writev( fd, wr_vec, 5))==-1 ) { if (errno==EINTR) continue; LM_ERR("writing to log file [%s] : %s\n", file, strerror(errno) ); } close (fd); shm_free( cmd->s1.s ); } static inline void send_mail( struct cpl_cmd *cmd) { char *argv[5]; int pfd[2]; pid_t pid; int i; if (pipe(pfd) < 0) { LM_ERR("pipe failed: %s\n",strerror(errno)); return; } /* even if I haven't fork yet, I push the date on the pipe just to get * rid of one more malloc + copy */ if (cmd->s3.len && cmd->s3.s) { if ( (i=write( pfd[1], cmd->s3.s, cmd->s3.len ))!=cmd->s3.len ) { LM_ERR("write returned error %s\n", strerror(errno)); goto error; } } if ( (pid = fork()) < 0) { LM_ERR("fork failed: %s\n",strerror(errno)); goto error; } else if (pid==0) { /* child -> close all descriptors excepting pfd[0] */ /* 32 is the maximum number of inherited open file descriptors */ for (i=3; i < 32; i++) if (i!=pfd[0]) close(i); if (pfd[0] != STDIN_FILENO) { dup2(pfd[0], STDIN_FILENO); close(pfd[0]); } /* set the argument vector*/ argv[0] = "mail"; argv[1] = "-s"; if (cmd->s2.s && cmd->s2.len) { /* put the subject in this format : <"$subject"\0> */ if ( (argv[2]=(char*)pkg_malloc(1+cmd->s2.len+1+1))==0) { LM_ERR("cannot get pkg memory\n"); goto child_exit; } argv[2][0] = '\"'; memcpy(argv[2]+1,cmd->s2.s,cmd->s2.len); argv[2][cmd->s2.len+1] = '\"'; argv[2][cmd->s2.len+2] = 0; } else { argv[2] = "\"[CPL notification]\""; } /* put the TO in <$to\0> format*/ if ( (argv[3]=(char*)pkg_malloc(cmd->s1.len+1))==0) { LM_ERR("cannot get pkg memory\n"); goto child_exit; } memcpy(argv[3],cmd->s1.s,cmd->s1.len); argv[3][cmd->s1.len] = 0; /* last element in vector mist be a null pointer */ argv[4] = (char*)0; /* just debug */ for(i=0;i free the shm */ shm_free( cmd->s1.s ); /* set an alarm -> sending the email shouldn't take more than 10 sec */ alarm(10); /* run the external mailer */ LM_DBG("new forked process created -> " "doing execv..\n"); execv("/usr/bin/mail",argv); /* if we got here means execv exit with error :-( */ LM_ERR("execv failed! (%s)\n",strerror(errno)); child_exit: _exit(127); } /* parent -> close both ends of pipe */ close(pfd[0]); close(pfd[1]); return; error: shm_free( cmd->s1.s ); close(pfd[0]); close(pfd[1]); return; } void cpl_aux_process( int cmd_out, char *log_dir) { struct cpl_cmd cmd; int len; /* this process will ignore SIGCHLD signal */ if (signal( SIGCHLD, SIG_IGN)==SIG_ERR) { LM_ERR("cannot set to IGNORE SIGCHLD signal\n"); } /* set the path for logging */ if (log_dir) { strcpy( file, log_dir); file_ptr = file + strlen(log_dir); *(file_ptr++) = '/'; } while(1) { /* let's read a command from pipe */ len = read( cmd_out, &cmd, sizeof(struct cpl_cmd)); if (len!=sizeof(struct cpl_cmd)) { if (len>=0) { LM_ERR("truncated message" " read from pipe! -> discarded\n"); } else if (errno!=EAGAIN) { LM_ERR("pipe reading failed: " " : %s\n",strerror(errno)); } sleep(1); continue; } /* process the command*/ switch (cmd.code) { case CPL_LOG_CMD: write_log( &cmd ); break; case CPL_MAIL_CMD: send_mail( &cmd ); break; default: LM_ERR("unknown command (%d) " "received! -> ignoring\n",cmd.code); } /* end switch*/ } } opensips-2.2.2/modules/cpl_c/cpl_nonsig.h000066400000000000000000000027271300170765700204270ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-06-27: file created (bogdan) */ #ifndef _CPL_NONSIG_H_ #define _CPL_NONSIG_H_ #include #include "../../str.h" #include "cpl_env.h" struct cpl_cmd { unsigned int code; str s1; str s2; str s3; }; #define CPL_LOG_CMD 1 #define CPL_MAIL_CMD 2 #define MAX_LOG_DIR_SIZE 256 void cpl_aux_process( int cmd_out, char *log_dir); static inline void write_cpl_cmd(unsigned int code, str *s1, str *s2, str *s3) { static struct cpl_cmd cmd; cmd.code = code; cmd.s1 = *s1; cmd.s2 = *s2; cmd.s3 = *s3; if (write( cpl_env.cmd_pipe[1], &cmd, sizeof(struct cpl_cmd) )==-1) LM_ERR("write ret: %s\n",strerror(errno)); } #endif opensips-2.2.2/modules/cpl_c/cpl_parser.c000066400000000000000000001210721300170765700204140ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include "../../parser/parse_uri.h" #include "../../dprint.h" #include "../../str.h" #include "../../ut.h" #include "CPL_tree.h" #include "sub_list.h" #include "cpl_log.h" static struct node *list = 0; static xmlDtdPtr dtd; /* DTD file */ static xmlValidCtxt cvp; /* validating context */ typedef unsigned short length_type ; typedef length_type* length_type_ptr; enum {EMAIL_TO,EMAIL_HDR_NAME,EMAIL_KNOWN_HDR_BODY,EMAIL_UNKNOWN_HDR_BODY}; #define ENCONDING_BUFFER_SIZE 65536 #define FOR_ALL_ATTR(_node,_attr) \ for( (_attr)=(_node)->properties ; (_attr) ; (_attr)=(_attr)->next) #define check_overflow(_p_,_offset_,_end_,_error_) \ do{\ if ((_p_)+(_offset_)>=(_end_)) { \ LM_ERR("%s:%d: overflow -> buffer to small\n",\ __FILE__,__LINE__);\ goto _error_;\ }\ }while(0)\ #define set_attr_type(_p_,_type_,_end_,_error_) \ do{\ check_overflow(_p_,sizeof(length_type),_end_,_error_);\ *((length_type_ptr)(_p_)) = htons((length_type)(_type_));\ (_p_) += sizeof(length_type);\ }while(0)\ #define append_short_attr(_p_,_n_,_end_,_error_) \ do{\ check_overflow(_p_,sizeof(length_type),_end_,_error_);\ *((length_type_ptr)(_p_)) = htons((length_type)(_n_));\ (_p_) += sizeof(length_type);\ }while(0) #define append_str_attr(_p_,_s_,_end_,_error_) \ do{\ check_overflow(_p_,(_s_).len + 1*((((_s_).len)&0x0001)==1),\ _end_,_error_);\ *((length_type_ptr)(_p_)) = htons((length_type)(_s_).len);\ (_p_) += sizeof(length_type);\ memcpy( (_p_), (_s_).s, (_s_).len);\ (_p_) += (_s_).len + 1*((((_s_).len)&0x0001)==1);\ }while(0) #define append_double_str_attr(_p_,_s1_,_s2_,_end_,_error_) \ do{\ check_overflow(_p_,(_s1_).len + (_s2_).len +\ 1*((((_s2_).len+(_s2_).len)&0x0001)==1), _end_, _error_);\ *((length_type_ptr)(_p_))=htons((length_type)((_s1_).len)+(_s2_).len);\ (_p_) += sizeof(length_type);\ memcpy( (_p_), (_s1_).s, (_s1_).len);\ (_p_) += (_s1_).len;\ memcpy( (_p_), (_s2_).s, (_s2_).len);\ (_p_) += (_s2_).len + 1*((((_s1_).len+(_s2_).len)&0x0001)==1);\ }while(0) #define get_attr_val(_attr_name_,_val_,_error_) \ do { \ (_val_).s = (char*)xmlGetProp(node,(_attr_name_));\ (_val_).len = strlen((_val_).s);\ /* remove all spaces from begin and end */\ trim_spaces_lr( (_val_) );\ if ((_val_).len==0) {\ LM_ERR("%s:%d: attribute <%s> has an "\ "empty value\n",__FILE__,__LINE__,(_attr_name_));\ goto _error_;\ }\ }while(0)\ #define MAX_EMAIL_HDR_SIZE 7 /*we are looking only for SUBJECT and BODY ;-)*/ #define MAX_EMAIL_BODY_SIZE 512 #define MAX_EMAIL_SUBJECT_SIZE 32 static inline char *decode_mail_url(char *p, char *p_end, char *url, unsigned char *nr_attr) { static char buf[ MAX_EMAIL_HDR_SIZE ]; char c; char foo; unsigned short hdr_len; unsigned short *len; int max_len; int status; /* init */ hdr_len = 0; max_len = 0; status = EMAIL_TO; (*nr_attr) ++; set_attr_type(p, TO_ATTR, p_end, error); /* attr type */ len = ((unsigned short*)(p)); /* attr val's len */ *len = 0; /* init the len */ p += 2; /* parse the whole url */ do { /* extract a char from the encoded url */ if (*url=='+') { /* substitute a blank for a plus */ c=' '; url++; /* Look for a hex encoded character */ } else if ( (*url=='%') && *(url+1) && *(url+2) ) { /* hex encoded - convert to a char */ c = hex2int(url[1]); foo = hex2int(url[2]); if (c==-1 || foo==-1) { LM_ERR("non-ASCII escaped " "character in mail url [%.*s]\n", 3, url); goto error; } c = c<<4 | foo; url += 3; } else { /* normal character - just copy it without changing */ c = *url; url++; } /* finally we got a character !! */ switch (c) { case '?': switch (status) { case EMAIL_TO: if (*len==0) { LM_ERR("empty TO " "address found in MAIL node!\n"); goto error; } if (((*len)&0x0001)==1) p++; *len = htons(*len); hdr_len = 0; status = EMAIL_HDR_NAME; break; default: goto parse_error; } break; case '=': switch (status) { case EMAIL_HDR_NAME: LM_DBG("hdr [%.*s] found\n", hdr_len,buf); if ( hdr_len==BODY_EMAILHDR_LEN && strncasecmp(buf,BODY_EMAILHDR_STR,hdr_len)==0 ) { /* BODY hdr found */ set_attr_type( p, BODY_ATTR, p_end, error); max_len = MAX_EMAIL_BODY_SIZE; } else if ( hdr_len==SUBJECT_EMAILHDR_LEN && strncasecmp(buf,SUBJECT_EMAILHDR_STR,hdr_len)==0 ) { /* SUBJECT hdr found */ set_attr_type( p, SUBJECT_ATTR, p_end, error); max_len = MAX_EMAIL_SUBJECT_SIZE; } else { LM_DBG("unknown hdr -> ignoring\n"); status = EMAIL_UNKNOWN_HDR_BODY; break; } (*nr_attr) ++; len = ((unsigned short*)(p)); /* attr val's len */ *len = 0; /* init the len */ p += 2; status = EMAIL_KNOWN_HDR_BODY; break; default: goto parse_error; } break; case '&': switch (status) { case EMAIL_KNOWN_HDR_BODY: if (((*len)&0x0001)==1) p++; *len = htons(*len); case EMAIL_UNKNOWN_HDR_BODY: hdr_len = 0; status = EMAIL_HDR_NAME; break; default: goto parse_error; } break; case 0: switch (status) { case EMAIL_TO: if (*len==0) { LM_ERR("empty TO " "address found in MAIL node!\n"); goto error; } case EMAIL_KNOWN_HDR_BODY: if (((*len)&0x0001)==1) p++; *len = htons(*len); case EMAIL_UNKNOWN_HDR_BODY: break; default: goto parse_error; } break; default: switch (status) { case EMAIL_TO: (*len)++; *(p++) = c; if (*len==URL_MAILTO_LEN && !strncasecmp(p-(*len),URL_MAILTO_STR,(*len))) { LM_DBG("MAILTO: found at" " the beginning of TO -> removed\n"); p -= (*len); *len = 0; } break; case EMAIL_KNOWN_HDR_BODY: if ((*len)name[0]) { case 'i': case 'I': set_attr_type(p, IS_ATTR, buf_end, error); break; case 'c': case 'C': set_attr_type(p, CONTAINS_ATTR, buf_end, error); break; case 's': case 'S': set_attr_type(p, SUBDOMAIN_OF_ATTR, buf_end, error); break; default: LM_ERR("unknown attribute " "<%s>\n",attr->name); goto error; } /* get the value of the attribute */ get_attr_val( attr->name , val, error); /* copy also the \0 from the end of string */ val.len++; append_str_attr(p, val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for ADDRESS_SWITCH node: * | attr1_t(2) attr1_val(2) | FIELD attr * [| attr2_t(2) attr2_val(2) |]? SUBFILED attr */ static inline int encode_address_switch_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'F': case 'f': set_attr_type(p, FIELD_ATTR, buf_end, error); if (val.s[0]=='D' || val.s[0]=='d') append_short_attr(p, DESTINATION_VAL, buf_end, error); else if (val.s[6]=='A' || val.s[6]=='a') append_short_attr(p,ORIGINAL_DESTINATION_VAL,buf_end,error); else if (!val.s[6]) append_short_attr(p, ORIGIN_VAL, buf_end, error); else { LM_ERR("unknown" " value <%s> for FIELD attr\n",val.s); goto error; }; break; case 'S': case 's': set_attr_type(p, SUBFIELD_ATTR, buf_end, error); switch (val.s[0]) { case 'u': case 'U': append_short_attr(p, USER_VAL, buf_end, error); break; case 'h': case 'H': append_short_attr(p, HOST_VAL, buf_end, error); break; case 'p': case 'P': append_short_attr(p, PORT_VAL, buf_end, error); break; case 't': case 'T': append_short_attr(p, TEL_VAL, buf_end, error); break; case 'd': case 'D': /*append_short_attr(p, DISPLAY_VAL, buf_end, error); break;*/ /* NOT YET SUPPORTED BY INTERPRETER */ case 'a': case 'A': /*append_short_attr(p, ADDRESS_TYPE_VAL, buf_end,error); break;*/ /* NOT YET SUPPORTED BY INTERPRETER */ default: LM_ERR("unknown value <%s> for SUBFIELD attr\n",val.s); goto error; } break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for LANGUAGE node: * | attr_t(2) attr_len(2) attr_val(2*x) | MATCHES attr (NNT) */ static inline int encode_lang_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; char *end; char *val_bk; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { /* there is only one attribute -> MATCHES */ if (attr->name[0]!='M' && attr->name[0]!='m') { LM_ERR("unknown attribute " "<%s>\n",attr->name); goto error; } val.s = val_bk = (char*)xmlGetProp(node,attr->name); /* parse the language-tag */ for(end=val.s,val.len=0;;end++) { /* trim all spaces from the beginning of the tag */ if (!val.len && (*end==' ' || *end=='\t')) continue; /* we cannot have more than 2 attrs - LANG_TAG and LANG_SUBTAG */ if ((*nr_attr)>=2) goto lang_error; if (((*end)|0x20)>='a' && ((*end)|0x20)<='z') { val.len++; continue; } else if (*end=='*' && val.len==0 && (*nr_attr)==0 && (*end==' '|| *end=='\t' || *end==0)) { val.len++; set_attr_type(p, MATCHES_TAG_ATTR, buf_end, error); } else if (val.len && (*nr_attr)==0 && *end=='-' ) { set_attr_type(p, MATCHES_TAG_ATTR, buf_end, error); } else if (val.len && ((*nr_attr)==0 || (*nr_attr)==1) && (*end==' '|| *end=='\t' || *end==0)) { set_attr_type(p, (!(*nr_attr))?MATCHES_TAG_ATTR:MATCHES_SUBTAG_ATTR, buf_end, error ); } else goto lang_error; (*nr_attr)++; /*LM_DBG("----> language tag=%d; %d [%.*s]\n",*(p-1), val.len,val.len,end-val.len);*/ val.s = end-val.len; append_str_attr(p, val, buf_end, error); val.len = 0; if (*end==0) break; } } return p-p_orig; lang_error: LM_ERR("bad value for language_tag <%s>\n",val_bk); error: return -1; } /* Attr. encoding for PRIORITY node: * | attr1_t(2) attr1_val(2) | LESS/GREATER/EQUAL attr * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? PRIOSTR attr (NT) */ static inline int encode_priority_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* attribute's name */ switch(attr->name[0]) { case 'L': case 'l': set_attr_type(p, LESS_ATTR, buf_end, error); break; case 'G': case 'g': set_attr_type(p, GREATER_ATTR, buf_end, error); break; case 'E': case 'e': set_attr_type(p, EQUAL_ATTR, buf_end, error); break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } /* attribute's encoded value */ get_attr_val( attr->name , val, error); if ( val.len==EMERGENCY_STR_LEN && !strncasecmp(val.s,EMERGENCY_STR,val.len) ) { append_short_attr(p, EMERGENCY_VAL, buf_end, error); } else if ( val.len==URGENT_STR_LEN && !strncasecmp(val.s,URGENT_STR,val.len) ) { append_short_attr(p, URGENT_VAL, buf_end, error); } else if ( val.len==NORMAL_STR_LEN && !strncasecmp(val.s,NORMAL_STR,val.len) ) { append_short_attr(p, NORMAL_VAL, buf_end, error); } else if ( val.len==NON_URGENT_STR_LEN && !strncasecmp(val.s,NON_URGENT_STR,val.len) ) { append_short_attr(p, NON_URGENT_VAL, buf_end, error); } else { append_short_attr(p, UNKNOWN_PRIO_VAL, buf_end, error); set_attr_type(p, PRIOSTR_ATTR, buf_end, error); val.len++; /* append \0 also */ append_str_attr(p, val, buf_end, error); } } return p-p_orig; error: return -1; } /* Attr. encoding for STRING_SWITCH node: * [| attr1_t(2) attr1_len(2) attr_val(2*x) |]? IS attr (NT) * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? CONTAINS attr (NT) */ static inline int encode_string_switch_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* there is only one attribute -> MATCHES */ if (attr->name[0]!='F' && attr->name[0]!='f') { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } set_attr_type(p, FIELD_ATTR, buf_end, error); /* attribute's encoded value */ get_attr_val( attr->name , val, error); switch (val.s[0]) { case 'S': case 's': append_short_attr(p, SUBJECT_VAL, buf_end, error); break; case 'O': case 'o': append_short_attr(p, ORGANIZATION_VAL, buf_end, error); break; case 'U': case 'u': append_short_attr(p, USER_AGENT_VAL, buf_end, error); break; case 'D': case 'd': append_short_attr(p, DISPLAY_VAL, buf_end, error); break; default: LM_ERR("unknown " "value <%s> for FIELD\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for STRING node: * [| attr1_t(2) attr1_len(2) attr_val(2*x) |]? IS attr (NT) * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? CONTAINS attr (NT) */ static inline int encode_string_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch(attr->name[0]) { case 'I': case 'i': set_attr_type(p, IS_ATTR, buf_end, error); break; case 'C': case 'c': set_attr_type(p, CONTAINS_ATTR, buf_end, error); break; default: LM_ERR("unknown " "attribute <%s>\n",attr->name); goto error; } /* attribute's encoded value */ get_attr_val( attr->name , val, error); val.len++; /* grab also the \0 */ append_str_attr(p,val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for TIME_SWITCH node: * [| attr1_t(2) attr1_len(2) attr_val(2*x) |]? TZID attr (NT) * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? TZURL attr (NT) */ static inline int encode_time_switch_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { static str tz_str = {"TZ=",3}; xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch(attr->name[2]) { case 'I': case 'i': set_attr_type(p, TZID_ATTR, buf_end, error); /* attribute's encoded value */ get_attr_val( attr->name , val, error); val.len++; /* grab also the \0 */ append_double_str_attr(p,tz_str,val, buf_end, error); break; case 'U': case 'u': /* set_attr_type(p, TZURL_ATTR, buf_end, error); * is a waste of space to copy the url - the interpreter doesn't * use it at all ;-) */ break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for TIME node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | DSTART attr (NT) * [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]? DTEND attr (NT) * [| attr3_t(2) attr3_len(2) attr3_val(2*x) |]? DURATION attr (NT) * [| attr4_t(2) attr4_len(2) attr4_val(2*x) |]? FREQ attr (NT) * [| attr5_t(2) attr5_len(2) attr5_val(2*x) |]? WKST attr (NT) * [| attr6_t(2) attr6_len(2) attr6_val(2*x) |]? BYYEARDAY attr (NT) * [| attr7_t(2) attr7_len(2) attr7_val(2*x) |]? COUNT attr (NT) * [| attr8_t(2) attr8_len(2) attr8_val(2*x) |]? BYSETPOS attr (NT) * [| attr9_t(2) attr9_len(2) attr9_val(2*x) |]? BYMONTH attr (NT) * [| attr10_t(2) attr10_len(2) attr_val10(2*x) |]? BYMONTHDAY attr (NT) * [| attr11_t(2) attr11_len(2) attr_val11(2*x) |]? BYMINUTE attr (NT) * [| attr12_t(2) attr12_len(2) attr_val12(2*x) |]? INTERVAL attr (NT) * [| attr13_t(2) attr13_len(2) attr_val13(2*x) |]? UNTIL attr (NT) * [| attr14_t(2) attr14_len(2) attr_val14(2*x) |]? BYSECOND attr (NT) * [| attr15_t(2) attr15_len(2) attr_val15(2*x) |]? BYHOUR attr (NT) * [| attr16_t(2) attr16_len(2) attr_val16(2*x) |]? BYDAY attr (NT) * [| attr17_t(2) attr17_len(2) attr_val17(2*x) |]? BYWEEKNO attr (NT) */ static inline int encode_time_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch (attr->name[4]) { case 0: if (attr->name[0]=='F' || attr->name[0]=='f') set_attr_type(p, FREQ_ATTR, buf_end, error); else if (attr->name[0]=='W' || attr->name[0]=='w') set_attr_type(p, WKST_ATTR, buf_end, error); break; case 'a': case 'A': if (attr->name[0]=='D' || attr->name[0]=='d') set_attr_type(p, DTSTART_ATTR, buf_end, error); else if (attr->name[0]=='B' || attr->name[0]=='b') set_attr_type(p, BYYEARDAY_ATTR, buf_end, error); break; case 't': case 'T': if (attr->name[0]=='D' || attr->name[0]=='d') set_attr_type(p, DURATION_ATTR, buf_end, error); else if (attr->name[0]=='C' || attr->name[0]=='c') set_attr_type(p, COUNT_ATTR, buf_end, error); else if (attr->name[0]=='B' || attr->name[0]=='b') set_attr_type(p, BYSETPOS_ATTR, buf_end, error); break; case 'n': case 'N': if (!attr->name[7]) set_attr_type(p, BYMONTH_ATTR, buf_end, error); else if (attr->name[7]=='D' || attr->name[7]=='d') set_attr_type(p, BYMONTHDAY_ATTR, buf_end, error); else if (attr->name[7]=='e' || attr->name[7]=='E') set_attr_type(p, BYMINUTE_ATTR, buf_end, error); break; case 'd': case 'D': set_attr_type(p, DTEND_ATTR, buf_end, error); break; case 'r': case 'R': set_attr_type(p, INTERVAL_ATTR, buf_end, error); break; case 'l': case 'L': set_attr_type(p, UNTIL_ATTR, buf_end, error); break; case 'c': case 'C': set_attr_type(p, BYSECOND_ATTR, buf_end, error); break; case 'u': case 'U': set_attr_type(p, BYHOUR_ATTR, buf_end, error); break; case 'y': case 'Y': set_attr_type(p, BYDAY_ATTR, buf_end, error); break; case 'e': case 'E': set_attr_type(p, BYWEEKNO_ATTR, buf_end, error); break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } /* attribute's encoded value */ get_attr_val( attr->name , val, error); val.len++; /* grab also the \0 */ append_str_attr(p,val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for LOOKUP node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | SOURCE attr (NT) * [| attr2_t(2) attr2_val(2) |]? CLEAR attr */ static inline int encode_lookup_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { /* get attribute's value */ get_attr_val( attr->name , val, error); if ( !strcasecmp((const char*)attr->name,"source") ) { /* this param will not be copied, since it has only one value ;-)*/ if ( val.len!=SOURCE_REG_STR_LEN || strncasecmp( val.s, SOURCE_REG_STR, val.len) ) { LM_ERR("unsupported value" " <%.*s> in SOURCE param\n",val.len,val.s); goto error; } } else if ( !strcasecmp((const char*)attr->name,"clear") ) { (*nr_attr)++; set_attr_type(p, CLEAR_ATTR, buf_end, error); if ( val.len==3 && !strncasecmp(val.s,"yes",3) ) append_short_attr(p, YES_VAL, buf_end, error); else if ( val.len==2 && !strncasecmp(val.s,"no",2) ) append_short_attr(p, NO_VAL, buf_end, error); else { LM_ERR("unknown value " "<%.*s> for attribute CLEAR\n",val.len,val.s); goto error; } } else if ( !strcasecmp((const char*)attr->name,"timeout") ) { LM_WARN("unsupported param TIMEOUT; skipping\n"); } else { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for LOCATION node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | URL attr (NT) * [| attr2_t(2) attr2_val(2) |]? PRIORITY attr * [| attr3_t(2) attr3_val(2) |]? CLEAR attr */ static inline int encode_location_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { struct sip_uri uri; xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; unsigned short nr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get attribute's value */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'U': case 'u': set_attr_type(p, URL_ATTR, buf_end, error); /* check if it's a valid SIP URL -> just call * parse uri function and see if returns error ;-) */ if (parse_uri( val.s, val.len, &uri)!=0) { LM_ERR("<%s> is not a valid SIP URL\n",val.s); goto error; } val.len++; /*copy also the \0 */ append_str_attr(p,val, buf_end, error); break; case 'P': case 'p': set_attr_type(p, PRIORITY_ATTR, buf_end, error); if (val.s[0]=='0') nr=0; else if (val.s[0]=='1') nr=10; else goto prio_error; if (val.s[1]!='.') goto prio_error; if (val.s[2]<'0' || val.s[2]>'9') goto prio_error; nr += val.s[2] - '0'; if (nr>10) goto prio_error; append_short_attr(p, nr, buf_end, error); break; case 'C': case 'c': set_attr_type(p, CLEAR_ATTR, buf_end, error); if (val.s[0]=='y' || val.s[0]=='Y') append_short_attr(p, YES_VAL, buf_end, error); else append_short_attr(p, NO_VAL, buf_end, error); break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; prio_error: LM_ERR("invalid priority <%s>\n",val.s); error: return -1; } /* Attr. encoding for REMOVE_LOCATION node: * [| attr1_t(2) attr1_len(2) attr1_val(2*x) |]? LOCATION attr (NT) */ static inline int encode_rmvloc_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { struct sip_uri uri; xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch(attr->name[0]) { case 'L': case 'l': set_attr_type(p, LOCATION_ATTR, buf_end, error); /* get the value of the attribute */ get_attr_val( attr->name , val, error); /* check if it's a valid SIP URL -> just call * parse uri function and see if returns error ;-) */ if (parse_uri( val.s, val.len, &uri)!=0) { LM_ERR("<%s> is not a valid SIP URL\n",val.s); goto error; } val.len++; /*copy also the \0 */ append_str_attr(p,val, buf_end, error); break; case 'P': case 'p': case 'V': case 'v': /* as the interpreter ignores PARAM and VALUE attributes, we will * do the same ;-) */ break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for PROXY node: * [| attr1_t(2) attr1_val(2) |]? RECURSE attr * [| attr2_t(2) attr2_val(2) |]? TIMEOUT attr * [| attr3_t(2) attr3_val(2) |]? ORDERING attr */ static inline int encode_proxy_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; unsigned int nr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'R': case 'r': set_attr_type(p, RECURSE_ATTR, buf_end, error); if (val.s[0]=='y' || val.s[0]=='Y') append_short_attr(p, YES_VAL, buf_end, error); else if (val.s[0]=='n' || val.s[0]=='N') append_short_attr(p, NO_VAL, buf_end, error); else { LM_ERR("unknown value " "<%s> for attribute RECURSE\n",val.s); goto error; } break; case 'T': case 't': set_attr_type(p, TIMEOUT_ATTR, buf_end, error); if (str2int(&val,&nr)==-1) { LM_ERR("bad value <%.*s>" " for attribute TIMEOUT\n",val.len,val.s); goto error; } append_short_attr(p, (unsigned short)nr, buf_end, error); break; case 'O': case 'o': set_attr_type(p, ORDERING_ATTR, buf_end, error); switch (val.s[0]) { case 'p': case'P': append_short_attr(p, PARALLEL_VAL, buf_end, error); break; case 'S': case 's': append_short_attr(p, SEQUENTIAL_VAL, buf_end, error); break; case 'F': case 'f': append_short_attr(p, FIRSTONLY_VAL, buf_end, error); break; default: LM_ERR("unknown " "value <%s> for attribute ORDERING\n",val.s); goto error; } break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for REJECT node: * | attr1_t(2) attr1_val(2) | STATUS attr * [| attr2_t(2) attr2_len(2) attr2_val(2*x)|]? REASON attr (NT) */ static inline int encode_reject_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; unsigned int nr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'R': case 'r': set_attr_type(p, REASON_ATTR, buf_end, error); val.len++; /* grab also the /0 */ append_str_attr(p, val, buf_end, error); break; case 'S': case 's': set_attr_type(p, STATUS_ATTR, buf_end, error); if (str2int(&val,&nr)==-1) { /*it was a non numeric value */ if (val.len==BUSY_STR_LEN && !strncasecmp(val.s,BUSY_STR,val.len)) { append_short_attr(p, BUSY_VAL, buf_end, error); } else if (val.len==NOTFOUND_STR_LEN && !strncasecmp(val.s,NOTFOUND_STR,val.len)) { append_short_attr(p, NOTFOUND_VAL, buf_end, error); } else if (val.len==ERROR_STR_LEN && !strncasecmp(val.s,ERROR_STR,val.len)) { append_short_attr(p, ERROR_VAL, buf_end, error); } else if (val.len==REJECT_STR_LEN && !strncasecmp(val.s,REJECT_STR,val.len)) { append_short_attr(p, REJECT_VAL, buf_end, error); } else { LM_ERR("bad val. <%s> for STATUS\n",val.s); goto error; } } else if (nr<400 || nr>700) { LM_ERR("bad code <%d> for STATUS\n",nr); goto error; } else { append_short_attr(p, nr, buf_end, error); } break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for REDIRECT node: * | attr1_t(2) attr1_val(2) | STATUS attr * [| attr2_t(2) attr2_len(2) attr2_val(2*x)|]? REASON attr (NT) */ static inline int encode_redirect_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; if (attr->name[0]=='p' || attr->name[0]=='P') { set_attr_type(p, PERMANENT_ATTR, buf_end, error); /* get the value */ get_attr_val( attr->name , val, error); if (val.s[0]=='y' || val.s[0]=='Y') append_short_attr( p, YES_VAL, buf_end, error); else if (val.s[0]=='n' || val.s[0]=='N') append_short_attr( p, NO_VAL, buf_end, error); else { LM_ERR("bad val. <%s> for PERMANENT\n",val.s); goto error; } } else { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for LOG node: * [| attr1_t(2) attr1_len(2) attr1_val(2*x) |]? NAME attr (NT) * [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]? COMMENT attr (NT) */ static inline int encode_log_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch (attr->name[0] ) { case 'n': case 'N': if (val.len>MAX_NAME_SIZE) val.len=MAX_NAME_SIZE; set_attr_type(p, NAME_ATTR, buf_end, error); break; case 'c': case 'C': if (val.len>MAX_COMMENT_SIZE) val.len=MAX_COMMENT_SIZE; set_attr_type(p, COMMENT_ATTR, buf_end, error); break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } /* be sure there is a \0 at the end of string */ val.s[val.len++]=0; append_str_attr(p,val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for MAIL node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | TO_ATTR attr (NNT) * [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]? SUBJECT_ATTR attr (NNT) * [| attr3_t(2) attr3_len(2) attr3_val(2*x) |]? BODY_ATTR attr (NNT) */ static inline int encode_mail_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { /* there is only one attribute -> URL */ if (attr->name[0]!='u' && attr->name[0]!='U') { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } p = decode_mail_url( p, buf_end, (char*)xmlGetProp(node,attr->name), nr_attr); if (p==0) goto error; } return p-p_orig; error: return -1; } /* Attr. encoding for SUBACTION node: */ static inline int encode_subaction_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; str val; FOR_ALL_ATTR(node,attr) { /* there is only one attribute -> ID */ if ((attr->name[0]|0x20)=='i' && ((attr->name[1]|0x20)=='d') && attr->name[2]==0 ) { /* get the value of the attribute */ get_attr_val( attr->name , val, error); if ((list = append_to_list(list, node_ptr,val.s))==0) { LM_ERR("failed to add " "subaction into list -> pkg_malloc failed?\n"); goto error; } } else { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return 0; error: return -1; } /* Attr. encoding for SUB node: * | attr1_t(2) attr1_val(2) | REF_ATTR attr */ static inline int encode_sub_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; char *sub_ptr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* there is only one attribute -> REF */ if ( strcasecmp("ref",(char*)attr->name)!=0 ) { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } set_attr_type(p, REF_ATTR, buf_end, error); /* get the value of the attribute */ get_attr_val( attr->name , val, error); if ( (sub_ptr=search_the_list(list, val.s))==0 ) { LM_ERR("unable to find declaration " "of subaction <%s>\n",val.s); goto error; } append_short_attr(p,(unsigned short)(node_ptr-sub_ptr),buf_end,error); } return p-p_orig; error: return -1; } /* Returns : -1 - error * >0 - subtree size of the given node */ int encode_node( xmlNodePtr node, char *p, char *p_end) { xmlNodePtr kid; unsigned short sub_tree_size; int attr_size; int kid_size; int foo; /* counting the kids */ for(kid=node->children,foo=0;kid;kid=kid->next) if (kid->type==XML_ELEMENT_NODE) foo++; check_overflow(p,GET_NODE_SIZE(foo),p_end,error); NR_OF_KIDS(p) = foo; /* size of the encoded attributes */ attr_size = 0; /* init the number of attributes */ NR_OF_ATTR(p) = 0; /* encode node name */ switch (node->name[0]) { case 'a':case 'A': switch (node->name[7]) { case 0: NODE_TYPE(p) = ADDRESS_NODE; attr_size = encode_address_attr( node, p, p_end); break; case '-': NODE_TYPE(p) = ADDRESS_SWITCH_NODE; attr_size = encode_address_switch_attr( node, p, p_end); break; default: NODE_TYPE(p) = ANCILLARY_NODE; break; } break; case 'B':case 'b': NODE_TYPE(p) = BUSY_NODE; break; case 'c':case 'C': NODE_TYPE(p) = CPL_NODE; break; case 'd':case 'D': NODE_TYPE(p) = DEFAULT_NODE; break; case 'f':case 'F': NODE_TYPE(p) = FAILURE_NODE; break; case 'i':case 'I': NODE_TYPE(p) = INCOMING_NODE; break; case 'l':case 'L': switch (node->name[2]) { case 'g':case 'G': NODE_TYPE(p) = LOG_NODE; attr_size = encode_log_attr( node, p, p_end); break; case 'o':case 'O': NODE_TYPE(p) = LOOKUP_NODE; attr_size = encode_lookup_attr( node, p, p_end); break; case 'c':case 'C': NODE_TYPE(p) = LOCATION_NODE; attr_size = encode_location_attr( node, p, p_end); break; default: if (node->name[8]) { NODE_TYPE(p) = LANGUAGE_SWITCH_NODE; } else { NODE_TYPE(p) = LANGUAGE_NODE; attr_size = encode_lang_attr( node, p, p_end); } break; } break; case 'm':case 'M': NODE_TYPE(p) = MAIL_NODE; attr_size = encode_mail_attr( node, p, p_end); break; case 'n':case 'N': switch (node->name[3]) { case 'F':case 'f': NODE_TYPE(p) = NOTFOUND_NODE; break; case 'N':case 'n': NODE_TYPE(p) = NOANSWER_NODE; break; default: NODE_TYPE(p) = NOT_PRESENT_NODE; break; } break; case 'o':case 'O': if (node->name[1]=='t' || node->name[1]=='T') { NODE_TYPE(p) = OTHERWISE_NODE; } else { NODE_TYPE(p) = OUTGOING_NODE; } break; case 'p':case 'P': if (node->name[2]=='o' || node->name[2]=='O') { NODE_TYPE(p) = PROXY_NODE; attr_size = encode_proxy_attr( node, p, p_end); } else if (node->name[8]) { NODE_TYPE(p) = PRIORITY_SWITCH_NODE; } else { NODE_TYPE(p) = PRIORITY_NODE; attr_size = encode_priority_attr( node, p, p_end); } break; case 'r':case 'R': switch (node->name[2]) { case 'j':case 'J': NODE_TYPE(p) = REJECT_NODE; attr_size = encode_reject_attr( node, p, p_end); break; case 'm':case 'M': NODE_TYPE(p) = REMOVE_LOCATION_NODE; attr_size = encode_rmvloc_attr( node, p, p_end); break; default: if (node->name[8]) { NODE_TYPE(p) = REDIRECTION_NODE; } else { NODE_TYPE(p) = REDIRECT_NODE; attr_size = encode_redirect_attr( node, p, p_end); } break; } break; case 's':case 'S': switch (node->name[3]) { case 0: NODE_TYPE(p) = SUB_NODE; attr_size = encode_sub_attr( node, p, p_end); break; case 'c':case 'C': NODE_TYPE(p) = SUCCESS_NODE; break; case 'a':case 'A': NODE_TYPE(p) = SUBACTION_NODE; attr_size = encode_subaction_attr( node, p, p_end); break; default: if (node->name[6]) { NODE_TYPE(p) = STRING_SWITCH_NODE; attr_size = encode_string_switch_attr( node, p, p_end); } else { NODE_TYPE(p) = STRING_NODE; attr_size = encode_string_attr( node, p, p_end); } break; } break; case 't':case 'T': if (node->name[4]) { NODE_TYPE(p) = TIME_SWITCH_NODE; attr_size = encode_time_switch_attr( node, p, p_end); } else { NODE_TYPE(p) = TIME_NODE; attr_size = encode_time_attr( node, p, p_end); } break; default: LM_ERR("unknown node <%s>\n",node->name); goto error; } /* compute the total length of the node (including attributes) */ if (attr_size<0) goto error; sub_tree_size = SIMPLE_NODE_SIZE(p) + (unsigned short)attr_size; /* encrypt all the kids */ for(kid = node->children,foo=0;kid;kid=kid->next) { if (kid->type!=XML_ELEMENT_NODE) continue; SET_KID_OFFSET( p, foo, sub_tree_size); kid_size = encode_node( kid, p+sub_tree_size, p_end); if (kid_size<=0) goto error; sub_tree_size += (unsigned short)kid_size; foo++; } return sub_tree_size; error: return -1; } #define BAD_XML "CPL script is not a valid XML document" #define BAD_XML_LEN (sizeof(BAD_XML)-1) #define BAD_CPL "CPL script doesn't respect CPL grammar" #define BAD_CPL_LEN (sizeof(BAD_CPL)-1) #define NULL_CPL "Empty CPL script" #define NULL_CPL_LEN (sizeof(NULL_CPL)-1) #define ENC_ERR "Encoding of the CPL script failed" #define ENC_ERR_LEN (sizeof(ENC_ERR)-1) int encodeCPL( str *xml, str *bin, str *log) { static char buf[ENCONDING_BUFFER_SIZE]; xmlDocPtr doc; xmlNodePtr cur; doc = 0; list = 0; /* reset all the logs (if any) to catch some possible err/warn/notice * from the parser/validater/encoder */ reset_logs(); /* parse the xml */ doc = xmlParseDoc( (unsigned char*)xml->s ); if (!doc) { append_log( 1, ERR BAD_XML LF, ERR_LEN+BAD_XML_LEN+LF_LEN); LM_ERR( BAD_XML "\n"); goto error; } /* check the xml against dtd */ if (xmlValidateDtd(&cvp, doc, dtd)!=1) { append_log( 1, ERR BAD_CPL LF, ERR_LEN+BAD_CPL_LEN+LF_LEN); LM_ERR( BAD_CPL "\n"); goto error; } cur = xmlDocGetRootElement(doc); if (!cur) { append_log( 1, ERR NULL_CPL LF, ERR_LEN+NULL_CPL_LEN+LF_LEN); LM_ERR( NULL_CPL "\n"); goto error; } bin->len = encode_node( cur, buf, buf+ENCONDING_BUFFER_SIZE); if (bin->len<0) { append_log( 1, ERR ENC_ERR LF, ERR_LEN+ENC_ERR_LEN+LF_LEN); LM_ERR( ENC_ERR "\n"); goto error; } xmlFreeDoc(doc); if (list) delete_list(list); /* compile the log buffer */ compile_logs( log ); bin->s = buf; return 1; error: if (doc) xmlFreeDoc(doc); if (list) delete_list(list); /* compile the log buffer */ compile_logs( log ); return 0; } /* loads and parse the dtd file; a validating context is created */ int init_CPL_parser( char* DTD_filename ) { dtd = xmlParseDTD( NULL, (unsigned char*)DTD_filename); if (!dtd) { LM_ERR("DTD not parsed successfully\n"); return -1; } cvp.userData = (void *) stderr; cvp.error = (xmlValidityErrorFunc) /*err_print*/ fprintf; cvp.warning = (xmlValidityWarningFunc) /*err_print*/ fprintf; return 1; } opensips-2.2.2/modules/cpl_c/cpl_parser.h000066400000000000000000000017221300170765700204200ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CPL_PARSER_H #define _CPL_PARSER_H #include "../../str.h" int init_CPL_parser( char* DTD_filename ); int encodeCPL(str *xml, str *bin, str *log); #endif opensips-2.2.2/modules/cpl_c/cpl_proxy.h000066400000000000000000000370421300170765700203110ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-07-29: file created (bogdan) * 2004-06-14: flag CPL_IS_STATEFUL is set now immediately after the * transaction is created (bogdan) */ #include "../tm/h_table.h" #include "../../parser/contact/parse_contact.h" #define duplicate_str( _orig_ , _new_ ) \ do {\ (_new_) = (str*)shm_malloc(sizeof(str)+(_orig_)->len);\ if (!(_new_)) goto mem_error;\ (_new_)->len = (_orig_)->len;\ (_new_)->s = (char*)((_new_))+sizeof(str);\ memcpy((_new_)->s,(_orig_)->s,(_orig_)->len);\ } while(0) #define search_and_duplicate_hdr( _intr_ , _field_ , _name_ , _sfoo_ ) \ do {\ if (!(_intr_)->_field_) {\ if (!(_intr_)->msg->_field_) { \ if (parse_headers((_intr_)->msg,_name_,0)==-1) {\ LM_ERR("bad %llx hdr\n",_name_);\ goto runtime_error;\ } else if ( !(_intr_)->msg->_field_) {\ (_intr_)->_field_ = STR_NOT_FOUND;\ } else {\ (_sfoo_) = &((_intr_)->msg->_field_->body);\ duplicate_str( (_sfoo_) , (_intr_)->_field_ );\ }\ } else {\ (_sfoo_) = &((_intr_)->msg->_field_->body);\ duplicate_str( (_sfoo_) , (_intr_)->_field_ );\ }\ } else {\ (_sfoo_) = (_intr_)->_field_;\ duplicate_str( (_sfoo_) , (_intr_)->_field_ );\ }\ }while(0) static inline int parse_q(str *q, unsigned int *prio) { if (q->s[0]=='0') *prio=0; else if (q->s[0]=='1') *prio=10; else goto error; if (q->s[1]!='.') goto error; if (q->s[2]<'0' || q->s[2]>'9') goto error; *prio += q->s[2] - '0'; if (*prio>10) goto error; return 0; error: LM_ERR("bad q param <%.*s>\n",q->len,q->s); return -1; } static inline int add_contacts_to_loc_set(struct sip_msg* msg, struct location **loc_set) { struct sip_uri uri; struct contact *contacts; unsigned int prio; /* we need to have the contact header */ if (msg->contact==0) { /* find and parse the Contact header */ if ((parse_headers(msg, HDR_CONTACT_F, 0)==-1) || (msg->contact==0)) { LM_ERR("error parsing or no Contact hdr found!\n"); goto error; } } /* extract from contact header the all the addresses */ if (parse_contact( msg->contact )!=0) { LM_ERR("unable to parse Contact hdr!\n"); goto error; } /* in contact hdr, in parsed attr, we should have a list of contacts */ if ( msg->contact->parsed ) { contacts = ((struct contact_body*)msg->contact->parsed)->contacts; for( ; contacts ; contacts=contacts->next) { /* check if the contact is a valid sip uri */ if (parse_uri( contacts->uri.s, contacts->uri.len , &uri)!=0) { continue; } /* convert the q param to int value (if any) */ if (contacts->q) { if (parse_q( &(contacts->q->body), &prio )!=0) continue; } else { prio = 10; /* set default to minimum */ } /* add the uri to location set */ if (add_location(loc_set,&contacts->uri,0,prio,CPL_LOC_DUPL)!=0) { LM_ERR("unable to add <%.*s>\n", contacts->uri.len,contacts->uri.s); } } } return 0; error: return -1; } static void reply_callback( struct cell* t, int type, struct tmcb_params* ps) { struct cpl_interpreter *intr = (struct cpl_interpreter*)(*(ps->param)); struct location *loc = 0; int rez; if (intr==0 || (intr->flags&CPL_ENDED) ) { LM_DBG("param=0 for callback %d, transaction=%p \n",type,t); return; } LM_DBG("negativ reply received\n"); intr->flags |= CPL_PROXY_DONE; intr->msg = ps->req; /* is the negative reply triggered by a cancel from UAC side? */ if (was_cancelled(t)) { /* stop whole interpretation */ return; } /* if it's a redirect-> do I have to added to the location set ? */ if (intr->proxy.recurse && (ps->code)/100==3) { LM_DBG("recurse level %d processing..\n",intr->proxy.recurse); intr->proxy.recurse--; /* get the locations from the Contact */ add_contacts_to_loc_set( ps->rpl, &(intr->loc_set)); switch (intr->proxy.ordering) { case SEQUENTIAL_VAL: /* update the last_to_proxy to last location from set */ if (intr->proxy.last_to_proxy==0) { /* the pointer went through entire old set -> set it to the * updated set, from the beginning */ if (intr->loc_set==0) /* the updated set is also empty -> proxy ended */ break; intr->proxy.last_to_proxy = intr->loc_set; } while(intr->proxy.last_to_proxy->next) intr->proxy.last_to_proxy=intr->proxy.last_to_proxy->next; break; case PARALLEL_VAL: /* push the whole new location set to be proxy */ intr->proxy.last_to_proxy = intr->loc_set; break; case FIRSTONLY_VAL: intr->proxy.last_to_proxy = 0; break; } } /* the current proxying failed -> do I have another location to try ? * This applies only for SERIAL forking or if RECURSE is set */ if (intr->proxy.last_to_proxy && !(no_new_branches(t)) ) { /* continue proxying */ LM_DBG("resuming proxying....\n"); switch (intr->proxy.ordering) { case PARALLEL_VAL: /* I get here only if I got a 3xx and RECURSE in on -> * forward to all location from location set */ intr->proxy.last_to_proxy = 0; cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags ); break; case SEQUENTIAL_VAL: /* place a new branch to the next location from loc. set*/ loc = remove_first_location( &(intr->loc_set) ); /*print_location_set(intr->loc_set);*/ /* update (if necessary) the last_to_proxy location */ if (intr->proxy.last_to_proxy==loc) intr->proxy.last_to_proxy = 0; cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags ); break; default: LM_CRIT("unexpected ordering found " "when continuing proxying (%d)\n",intr->proxy.ordering); goto exit; } /* nothing more to be done */ return; } else { /* done with proxying.... -> process the final response */ LM_DBG("final_reply: got a final %d\n",ps->code); intr->ip = 0; if (ps->code==486 || ps->code==600) { /* busy response */ intr->ip = intr->proxy.busy; } else if (ps->code==408) { /* request timeout -> no response */ intr->ip = intr->proxy.noanswer; } else if (((ps->code)/100)==3) { /* redirection */ /* add to the location list all the addresses from Contact */ add_contacts_to_loc_set( ps->rpl, &(intr->loc_set)); print_location_set( intr->loc_set ); intr->ip = intr->proxy.redirect; } else { /* generic failure */ intr->ip = intr->proxy.failure; } if (intr->ip==0) intr->ip = (intr->proxy.default_)? intr->proxy.default_:DEFAULT_ACTION; if (intr->ip!=DEFAULT_ACTION) intr->ip = get_first_child( intr->ip ); if( intr->ip==DEFAULT_ACTION) rez = run_default(intr); else rez = cpl_run_script(intr); switch ( rez ) { case SCRIPT_END: /* we don't need to free the interpreter here since it will * be freed in the final_reply callback */ case SCRIPT_TO_BE_CONTINUED: return; case SCRIPT_RUN_ERROR: case SCRIPT_FORMAT_ERROR: goto exit; default: LM_CRIT("improper result %d\n", rez); goto exit; } } exit: /* in case of error the default response chosen by ser at the last * proxying will be forwarded to the UAC */ if ( intr->flags&CPL_DO_NOT_FREE ) intr->flags |= CPL_ENDED; else free_cpl_interpreter( intr ); /* set to zero the param callback*/ *(ps->param) = 0; return; } /* if hooked to a transaction (via callbacks), this is the only place where we actually destroy the interpreter -> when callback is destroied */ static void destroy_cpl_intr(void *param) { struct cpl_interpreter *intr = (struct cpl_interpreter*)(param); free_cpl_interpreter( intr ); } static inline char *run_proxy( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; char *kid; char *p; int i; int timeout; str *s; struct location *loc; intr->proxy.ordering = PARALLEL_VAL; intr->proxy.recurse = (unsigned short)cpl_env.proxy_recurse; timeout = 0; /* identify the attributes */ for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case TIMEOUT_ATTR: timeout = (int)n; break; case RECURSE_ATTR: switch (n) { case NO_VAL: intr->proxy.recurse = 0; break; case YES_VAL: /* already set as default */ break; default: LM_ERR("invalid value (%u) found" " for attr. RECURSE in PROXY node!\n",n); goto script_error; } break; case ORDERING_ATTR: if (n!=PARALLEL_VAL && n!=SEQUENTIAL_VAL && n!=FIRSTONLY_VAL){ LM_ERR("invalid value (%u) found" " for attr. ORDERING in PROXY node!\n",n); goto script_error; } intr->proxy.ordering = n; break; default: LM_ERR("unknown attribute (%d) in" "PROXY node\n",attr_name); goto script_error; } } intr->proxy.busy = intr->proxy.noanswer = 0; intr->proxy.redirect = intr->proxy.failure = intr->proxy.default_ = 0; /* this is quite an "expensive" node to run, so let's make some checking * before getting deeply into it */ for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case BUSY_NODE : intr->proxy.busy = kid; break; case NOANSWER_NODE: intr->proxy.noanswer = kid; break; case REDIRECTION_NODE: intr->proxy.redirect = kid; break; case FAILURE_NODE: intr->proxy.failure = kid; break; case DEFAULT_NODE: intr->proxy.default_ = kid; break; default: LM_ERR("unknown output node type" " (%d) for PROXY node\n",NODE_TYPE(kid)); goto script_error; } } /* if the location set if empty, I will go directly on failure/default */ if (intr->loc_set==0) { LM_DBG("location set found empty -> going on " "failure/default branch\n"); if (intr->proxy.failure) return get_first_child(intr->proxy.failure); else if (intr->proxy.default_) return get_first_child(intr->proxy.default_); else return DEFAULT_ACTION; } /* if it's the first execution of a proxy node, force parsing of the needed * headers and duplicate them in shared memory */ if (!(intr->flags&CPL_PROXY_DONE)) { /* user name is already in shared memory */ /* requested URI - mandatory in SIP msg (cannot be STR_NOT_FOUND) */ s = GET_RURI( intr->msg ); duplicate_str( s , intr->ruri ); intr->flags |= CPL_RURI_DUPLICATED; /* TO header - mandatory in SIP msg (cannot be STR_NOT_FOUND) */ if (!intr->to) { if (!intr->msg->to && (parse_headers(intr->msg,HDR_TO_F,0)==-1 || !intr->msg->to)) { LM_ERR("bad msg or missing TO header\n"); goto runtime_error; } s = &(get_to(intr->msg)->uri); } else { s = intr->to; } duplicate_str( s , intr->to ); intr->flags |= CPL_TO_DUPLICATED; /* FROM header - mandatory in SIP msg (cannot be STR_NOT_FOUND) */ if (!intr->from) { if (parse_from_header( intr->msg )<0) goto runtime_error; s = &(get_from(intr->msg)->uri); } else { s = intr->from; } duplicate_str( s , intr->from ); intr->flags |= CPL_FROM_DUPLICATED; /* SUBJECT header - optional in SIP msg (can be STR_NOT_FOUND) */ if (intr->subject!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,subject,HDR_SUBJECT_F,s); if (intr->subject!=STR_NOT_FOUND) intr->flags |= CPL_SUBJECT_DUPLICATED; } /* ORGANIZATION header - optional in SIP msg (can be STR_NOT_FOUND) */ if ( intr->organization!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,organization,HDR_ORGANIZATION_F,s); if ( intr->organization!=STR_NOT_FOUND) intr->flags |= CPL_ORGANIZATION_DUPLICATED; } /* USER_AGENT header - optional in SIP msg (can be STR_NOT_FOUND) */ if (intr->user_agent!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,user_agent,HDR_USERAGENT_F,s); if (intr->user_agent!=STR_NOT_FOUND) intr->flags |= CPL_USERAGENT_DUPLICATED; } /* ACCEPT_LANGUAGE header - optional in SIP msg * (can be STR_NOT_FOUND) */ if (intr->accept_language!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,accept_language, HDR_ACCEPTLANGUAGE_F,s); if (intr->accept_language!=STR_NOT_FOUND) intr->flags |= CPL_ACCEPTLANG_DUPLICATED; } /* PRIORITY header - optional in SIP msg (can be STR_NOT_FOUND) */ if (intr->priority!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,priority,HDR_PRIORITY_F,s); if (intr->priority!=STR_NOT_FOUND) intr->flags |= CPL_PRIORITY_DUPLICATED; } /* now is the first time doing proxy, so I can still be stateless; * as proxy is done all the time stateful, I have to switch from * stateless to stateful if necessary. */ if ( !(intr->flags&CPL_IS_STATEFUL) ) { i = cpl_fct.tmb.t_newtran( intr->msg ); if (i<0) { LM_ERR("failed to build new transaction!\n"); goto runtime_error; } else if (i==0) { LM_ERR("processed INVITE is a retransmission!\n"); /* instead of generating an error is better just to break the * script by returning EO_SCRIPT */ return EO_SCRIPT; } intr->flags |= CPL_IS_STATEFUL; } /* as I am interested in getting the responses back - I need to install * some callback functions for replies */ if (cpl_fct.tmb.register_tmcb(intr->msg,0, TMCB_ON_FAILURE,reply_callback,(void*)intr,destroy_cpl_intr) <= 0 ) { LM_ERR("failed to register TMCB_RESPONSE_OUT callback\n"); goto runtime_error; } intr->flags |= CPL_DO_NOT_FREE; } cpl_fct.tmb.t_gett()->fr_inv_timeout = timeout; switch (intr->proxy.ordering) { case FIRSTONLY_VAL: /* forward the request only to the first address from loc. set */ /* location set cannot be empty -> was checked before */ loc = remove_first_location( &(intr->loc_set) ); intr->proxy.last_to_proxy = 0; /* set the new ip before proxy -> otherwise race cond with rpls */ intr->ip = CPL_TO_CONTINUE; if (cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags )==-1) goto runtime_error; break; case PARALLEL_VAL: /* forward to all location from location set */ intr->proxy.last_to_proxy = 0; /* set the new ip before proxy -> otherwise race cond with rpls */ intr->ip = CPL_TO_CONTINUE; if (cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags) ==-1) goto runtime_error; break; case SEQUENTIAL_VAL: /* forward the request one at the time to all addresses from * loc. set; location set cannot be empty -> was checked before */ /* use the first location from set */ loc = remove_first_location( &(intr->loc_set) ); /* set as the last_to_proxy the last location from set */ intr->proxy.last_to_proxy = intr->loc_set; while (intr->proxy.last_to_proxy&&intr->proxy.last_to_proxy->next) intr->proxy.last_to_proxy = intr->proxy.last_to_proxy->next; /* set the new ip before proxy -> otherwise race cond with rpls */ intr->ip = CPL_TO_CONTINUE; if (cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags)==-1) goto runtime_error; break; } return CPL_TO_CONTINUE; script_error: return CPL_SCRIPT_ERROR; mem_error: LM_ERR("no more free shm memory\n"); runtime_error: return CPL_RUNTIME_ERROR; } opensips-2.2.2/modules/cpl_c/cpl_run.c000066400000000000000000000650701300170765700177310ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-01-23 : created (bogdan) * 2003-09-11 : build_lump_rpl() merged into add_lump_rpl() (bogdan) * 2004-06-14 : all global variables merged into cpl_env and cpl_fct; * append_branches param added to lookup node (bogdan) * 2004-06-14 : flag CPL_IS_STATEFUL is set now immediately after the * transaction is created (bogdan) */ #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../str.h" #include "../../ut.h" #include "../../dprint.h" #include "../../parser/msg_parser.h" #include "../../data_lump_rpl.h" #include "../tm/tm_load.h" #include "../usrloc/usrloc.h" #include "CPL_tree.h" #include "loc_set.h" #include "cpl_utils.h" #include "cpl_nonsig.h" #include "cpl_sig.h" #include "cpl_env.h" #include "cpl_run.h" #define EO_SCRIPT ((char*)0xffffffff) #define DEFAULT_ACTION ((char*)0xfffffffe) #define CPL_SCRIPT_ERROR ((char*)0xfffffffd) #define CPL_RUNTIME_ERROR ((char*)0xfffffffc) #define CPL_TO_CONTINUE ((char*)0xfffffffb) #define HDR_NOT_FOUND ((char*)0xffffffff) #define UNDEF_CHAR (0xff) static str cpl_301_reason = str_init("Moved permanently"); static str cpl_302_reason = str_init("Moved temporarily"); #define check_overflow_by_ptr(_ptr_,_intr_,_error_) \ do {\ if ( (char*)(_ptr_)>(_intr_)->script.len+(_intr_)->script.s ) {\ LM_ERR("overflow detected ip=%p ptr=%p in "\ "func. %s, line %d\n",(_intr_)->ip,_ptr_,__FILE__,__LINE__);\ goto _error_; \ } \ }while(0) #define check_overflow_by_offset(_len_,_intr_,_error_) \ do {\ if ( (char*)((_intr_)->ip+(_len_)) > \ (_intr_)->script.len+(_intr_)->script.s ) {\ LM_ERR("overflow detected ip=%p offset=%d in "\ "func. %s, line %d\n",(_intr_)->ip,_len_,__FILE__,__LINE__);\ goto _error_; \ } \ }while(0) #define get_first_child(_node_) \ ((NR_OF_KIDS(_node_)==0)?DEFAULT_ACTION:(_node_)+KID_OFFSET(_node_,0)) #define get_basic_attr(_p_,_code_,_n_,_intr_,_error_) \ do{\ check_overflow_by_ptr( (_p_)+BASIC_ATTR_SIZE, _intr_, _error_);\ _code_ = ntohs( *((unsigned short*)(_p_)) );\ _n_ = ntohs( *((unsigned short*)((_p_)+2)) );\ (_p_) += 4;\ }while(0) #define get_str_attr(_p_,_s_,_len_,_intr_,_error_,_FIXUP_) \ do{\ if ( ((int)(_len_))-(_FIXUP_)<=0 ) {\ LM_ERR("%s:%d: attribute is an empty string\n",\ __FILE__,__LINE__);\ goto _error_; \ } else {\ check_overflow_by_ptr( (_p_)+(_len_), _intr_, _error_);\ _s_ = _p_;\ (_p_) += (_len_) + 1*(((_len_)&0x0001)==1);\ (_len_) -= (_FIXUP_);\ }\ }while(0) struct cpl_interpreter* new_cpl_interpreter( struct sip_msg *msg, str *script) { struct cpl_interpreter *intr = 0; intr = (struct cpl_interpreter*)shm_malloc(sizeof(struct cpl_interpreter)); if (!intr) { LM_ERR("no more shm free memory!\n"); goto error; } memset( intr, 0, sizeof(struct cpl_interpreter)); /* init the interpreter*/ intr->script.s = script->s; intr->script.len = script->len; intr->recv_time = time(0); intr->ip = script->s; intr->msg = msg; /* check the beginning of the script */ if ( NODE_TYPE(intr->ip)!=CPL_NODE ) { LM_ERR("first node is not CPL!!\n"); goto error; } return intr; error: return 0; } void free_cpl_interpreter(struct cpl_interpreter *intr) { if (intr) { empty_location_set( &(intr->loc_set) ); if (intr->script.s) shm_free( intr->script.s); if (intr->user.s) shm_free(intr->user.s); if (intr->flags&CPL_RURI_DUPLICATED) shm_free(intr->ruri); if (intr->flags&CPL_TO_DUPLICATED) shm_free(intr->to); if (intr->flags&CPL_FROM_DUPLICATED) shm_free(intr->from); if (intr->flags&CPL_SUBJECT_DUPLICATED) shm_free(intr->subject); if (intr->flags&CPL_ORGANIZATION_DUPLICATED) shm_free(intr->organization); if (intr->flags&CPL_USERAGENT_DUPLICATED) shm_free(intr->user_agent); if (intr->flags&CPL_ACCEPTLANG_DUPLICATED) shm_free(intr->accept_language); if (intr->flags&CPL_PRIORITY_DUPLICATED) shm_free(intr->priority); shm_free(intr); } } /* UPDATED + CHECKED */ static inline char *run_cpl_node( struct cpl_interpreter *intr ) { char *kid; unsigned char start; int i; start = (intr->flags&CPL_RUN_INCOMING)?INCOMING_NODE:OUTGOING_NODE; /* look for the starting node (incoming or outgoing) */ for(i=0;iip);i++) { kid= intr->ip + KID_OFFSET(intr->ip,i); if ( NODE_TYPE(kid)==start ) { return get_first_child(kid); } else if (NODE_TYPE(kid)==SUBACTION_NODE || NODE_TYPE(kid)==ANCILLARY_NODE || NODE_TYPE(kid)==INCOMING_NODE || NODE_TYPE(kid)==OUTGOING_NODE ) { continue; } else { LM_ERR("unknown child type (%d) " "for CPL node!!\n",NODE_TYPE(kid)); return CPL_SCRIPT_ERROR; } } LM_DBG("CPL node has no %d subnode -> default\n", start); return DEFAULT_ACTION; } /* UPDATED + CHECKED */ static inline char *run_lookup( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; unsigned char clear; char *p; char *kid; char *failure_kid = 0; char *success_kid = 0; char *notfound_kid = 0; int i; time_t tc; urecord_t* r; ucontact_t* contact; clear = NO_VAL; /* check the params */ for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr(p,attr_name,n,intr,script_error); switch (attr_name) { case CLEAR_ATTR: if (n!=YES_VAL && n!=NO_VAL) LM_WARN("invalid value (%u) found" " for param. CLEAR in LOOKUP node -> using " "default (%u)!\n",n,clear); else clear = n; break; default: LM_ERR("unknown attribute (%d) in LOOKUP node\n",attr_name); goto script_error; } } /* check the kids */ for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case SUCCESS_NODE : success_kid = kid; break; case NOTFOUND_NODE: notfound_kid = kid; break; case FAILURE_NODE: failure_kid = kid; break; default: LM_ERR("unknown output node type" " (%d) for LOOKUP node\n",NODE_TYPE(kid)); goto script_error; } } kid = failure_kid; if (cpl_env.lu_domain) { /* fetch user's contacts via usrloc */ tc = time(0); cpl_fct.ulb.lock_udomain( cpl_env.lu_domain, &intr->user ); i = cpl_fct.ulb.get_urecord( cpl_env.lu_domain, &intr->user, &r); if (i < 0) { /* failure */ LM_ERR("failed to query usrloc\n"); cpl_fct.ulb.unlock_udomain( cpl_env.lu_domain, &intr->user ); } else if (i > 0) { /* not found */ LM_DBG("'%.*s' Not found in usrloc\n", intr->user.len, intr->user.s); cpl_fct.ulb.unlock_udomain( cpl_env.lu_domain, &intr->user ); kid = notfound_kid; } else { contact = r->contacts; /* skip expired contacts */ while ((contact) && (contact->expires <= tc)) contact = contact->next; /* any contacts left? */ if (contact) { /* clear loc set if requested */ if (clear) empty_location_set( &(intr->loc_set) ); /* start adding locations to set */ do { LM_DBG("adding <%.*s>q=%d\n", contact->c.len,contact->c.s, (int)((contact->q==Q_UNSPECIFIED)?10:10*contact->q)); if (add_location( &(intr->loc_set), &contact->c, &contact->received, (int)((contact->q==Q_UNSPECIFIED)?10:10*contact->q), CPL_LOC_DUPL| ((contact->cflags&cpl_fct.ulb.nat_flag)?CPL_LOC_NATED:0) )==-1) { LM_ERR("unable to add location to set :-(\n"); cpl_fct.ulb.unlock_udomain( cpl_env.lu_domain, &intr->user ); goto runtime_error; } contact = contact->next; }while( contact && cpl_env.lu_append_branches); /* set the flag for modifying the location set */ intr->flags |= CPL_LOC_SET_MODIFIED; /* we found a valid contact */ kid = success_kid; } else { /* no valid contact found */ kid = notfound_kid; } cpl_fct.ulb.unlock_udomain( cpl_env.lu_domain, &intr->user ); } } if (kid) return get_first_child(kid); return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_location( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; char *p; unsigned char prio; unsigned char clear; str url; int i; clear = NO_VAL; prio = 10; url.s = (char*)UNDEF_CHAR; url.len = 0; /* sanity check */ if (NR_OF_KIDS(intr->ip)>1) { LM_ERR("LOCATION node suppose to have max " "one child, not %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr(p,attr_name,n,intr,script_error); switch (attr_name) { case URL_ATTR: url.len = n; get_str_attr( p, url.s, url.len, intr, script_error,1); break; case PRIORITY_ATTR: if ( n>10) LM_WARN("invalid value (%u) found" " for param. PRIORITY in LOCATION node -> using " "default (%u)!\n",n,prio); else prio = n; break; case CLEAR_ATTR: if (n!=YES_VAL && n!=NO_VAL) LM_WARN("invalid value (%u) found" " for param. CLEAR in LOCATION node -> using " "default (%u)!\n",n,clear); else clear = n; break; default: LM_ERR("unknown attribute (%d) in " "LOCATION node\n",attr_name); goto script_error; } } if (url.s==(char*)UNDEF_CHAR) { LM_ERR("param. URL missing in LOCATION node\n"); goto script_error; } if (clear) empty_location_set( &(intr->loc_set) ); if (add_location( &(intr->loc_set), &url, 0, prio, 0/*no dup*/ )==-1) { LM_ERR("unable to add location to set :-(\n"); goto runtime_error; } /* set the flag for modifying the location set */ intr->flags |= CPL_LOC_SET_MODIFIED; return get_first_child(intr->ip); runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_remove_location( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; char *p; str url; int i; url.s = (char*)UNDEF_CHAR; /* sanity check */ if (NR_OF_KIDS(intr->ip)>1) { LM_ERR("REMOVE_LOCATION node suppose to have max one child, not %d!\n", NR_OF_KIDS(intr->ip)); goto script_error; } /* dirty hack to speed things up in when loc set is already empty */ if (intr->loc_set==0) goto done; for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr(p,attr_name,n,intr,script_error); switch (attr_name) { case LOCATION_ATTR: url.len = n; get_str_attr( p, url.s, url.len, intr, script_error,1); break; default: LM_ERR("unknown attribute " "(%d) in REMOVE_LOCATION node\n",attr_name); goto script_error; } } if (url.s==(char*)UNDEF_CHAR) { LM_DBG("remove all locs from loc_set\n"); empty_location_set( &(intr->loc_set) ); } else { remove_location( &(intr->loc_set), url.s, url.len ); } /* set the flag for modifying the location set */ intr->flags |= CPL_LOC_SET_MODIFIED; done: return get_first_child(intr->ip); script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_sub( struct cpl_interpreter *intr ) { char *p; unsigned short offset; unsigned short attr_name; int i; /* sanity check */ if (NR_OF_KIDS(intr->ip)!=0) { LM_ERR("SUB node doesn't suppose to have any " "sub-nodes. Found %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } /* check the number of attr */ i = NR_OF_ATTR( intr->ip ); if (i!=1) { LM_ERR("incorrect nr. of attr. %d (<>1) in SUB node\n",i); goto script_error; } /* get attr's name */ p = ATTR_PTR(intr->ip); get_basic_attr( p, attr_name, offset, intr, script_error); if (attr_name!=REF_ATTR) { LM_ERR("invalid attr. %d (expected %d)in " "SUB node\n", attr_name, REF_ATTR); goto script_error; } /* make the jump */ p = intr->ip - offset; /* check the destination pointer -> are we still inside the buffer ;-) */ if (((char*)p)script.s) { LM_ERR("jump offset lower than the script " "beginning -> underflow!\n"); goto script_error; } check_overflow_by_ptr( p+SIMPLE_NODE_SIZE(intr->ip), intr, script_error); /* check to see if we hit a subaction node */ if ( NODE_TYPE(p)!=SUBACTION_NODE ) { LM_ERR("sub. jump hit a nonsubaction node!\n"); goto script_error; } if ( NR_OF_ATTR(p)!=0 ) { LM_ERR("invalid subaction node reached " "(attrs=%d); expected (0)!\n",NR_OF_ATTR(p)); goto script_error; } return get_first_child(p); script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_reject( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short status; unsigned short n; str reason; char *p; int i; reason.s = (char*)UNDEF_CHAR; status = UNDEF_CHAR; /* sanity check */ if (NR_OF_KIDS(intr->ip)!=0) { LM_ERR("REJECT node doesn't suppose to have " "any sub-nodes. Found %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case STATUS_ATTR: status = n; break; case REASON_ATTR: reason.len = n; get_str_attr( p, reason.s, reason.len, intr, script_error,1); break; default: LM_ERR("unknown attribute " "(%d) in REJECT node\n",attr_name); goto script_error; } } if (status==UNDEF_CHAR) { LM_ERR("mandatory attribute STATUS not found\n"); goto script_error; } if (status<400 || status>=700) { LM_ERR("bad attribute STATUS (%d)\n",status); goto script_error; } if (reason.s==(char*)UNDEF_CHAR ) { switch (status) { case 486: reason.s = "Busy Here"; reason.len = 9; break; case 404: reason.s = "Not Found"; reason.len = 9; break; case 603: reason.s = "Decline"; reason.len = 7; break; case 500: reason.s = "Internal Server Error"; reason.len = 21; break; default: reason.s = "Generic Error"; reason.len = 13; } } /* if still stateless and FORCE_STATEFUL set -> build the transaction */ if ( !(intr->flags&CPL_IS_STATEFUL) && intr->flags&CPL_FORCE_STATEFUL) { i = cpl_fct.tmb.t_newtran( intr->msg ); if (i<0) { LM_ERR("failed to build new transaction!\n"); goto runtime_error; } else if (i==0) { LM_ERR(" processed INVITE is a retransmission!\n"); /* instead of generating an error is better just to break the * script by returning EO_SCRIPT */ return EO_SCRIPT; } intr->flags |= CPL_IS_STATEFUL; } /* send the reply */ if (cpl_fct.sigb.reply(intr->msg, (int)status, &reason, NULL )!=1) { LM_ERR("unable to send reject reply!\n"); goto runtime_error; } return EO_SCRIPT; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_redirect( struct cpl_interpreter *intr ) { struct location *loc; struct lump_rpl *lump; unsigned short attr_name; unsigned short permanent; unsigned short n; char *p; str lump_str; char *cp; int i; permanent = NO_VAL; /* sanity check */ if (NR_OF_KIDS(intr->ip)!=0) { LM_ERR("REDIRECT node doesn't suppose " "to have any sub-nodes. Found %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } /* read the attributes of the REDIRECT node*/ for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case PERMANENT_ATTR: if (n!=YES_VAL && n!=NO_VAL) { LM_ERR("unsupported value (%d)" " in attribute PERMANENT for REDIRECT node",n); goto script_error; } permanent = n; break; default: LM_ERR("unknown attribute " "(%d) in REDIRECT node\n",attr_name); goto script_error; } } /* build the lump for Contact header */ lump_str.len = 9 /*"Contact: "*/; for(loc=intr->loc_set;loc;loc=loc->next) lump_str.len += 1/*"<"*/ + loc->addr.uri.len + 7/*">;q=x.x"*/ + 2*(loc->next!=0)/*" ,"*/; lump_str.len += CRLF_LEN; lump_str.s = pkg_malloc( lump_str.len ); if(!lump_str.s) { LM_ERR("out of pkg memory!\n"); goto runtime_error; } cp = lump_str.s; memcpy( cp , "Contact: " , 9); cp += 9; for(loc=intr->loc_set;loc;loc=loc->next) { *(cp++) = '<'; memcpy(cp,loc->addr.uri.s,loc->addr.uri.len); cp += loc->addr.uri.len; memcpy(cp,">;q=",4); cp += 4; *(cp++) = (loc->addr.priority!=10)?'0':'1'; *(cp++) = '.'; *(cp++) = '0'+(loc->addr.priority%10); if (loc->next) { *(cp++) = ' '; *(cp++) = ','; } } memcpy(cp,CRLF,CRLF_LEN); /* if still stateless and FORCE_STATEFUL set -> build the transaction */ if ( !(intr->flags&CPL_IS_STATEFUL) && intr->flags&CPL_FORCE_STATEFUL) { i = cpl_fct.tmb.t_newtran( intr->msg ); if (i<0) { LM_ERR("failed to build new transaction!\n"); pkg_free( lump_str.s ); goto runtime_error; } else if (i==0) { LM_ERR("processed INVITE is a retransmission!\n"); /* instead of generating an error is better just to break the * script by returning EO_SCRIPT */ pkg_free( lump_str.s ); return EO_SCRIPT; } intr->flags |= CPL_IS_STATEFUL; } /* add the lump to the reply */ lump = add_lump_rpl( intr->msg, lump_str.s , lump_str.len , LUMP_RPL_HDR); if(!lump) { LM_ERR("unable to add lump_rpl! \n"); pkg_free( lump_str.s ); goto runtime_error; } /* send the reply */ if (permanent) i = cpl_fct.sigb.reply( intr->msg,301,&cpl_301_reason, NULL); else i = cpl_fct.sigb.reply( intr->msg,302,&cpl_302_reason, NULL); /* msg which I'm working on can be in private memory or is a clone into * shared memory (if I'm after a failed proxy); So, it's better to removed * by myself the lump that I added previously */ unlink_lump_rpl( intr->msg, lump); free_lump_rpl( lump ); if (i!=1) { LM_ERR("unable to send redirect reply!\n"); goto runtime_error; } return EO_SCRIPT; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_log( struct cpl_interpreter *intr ) { char *p; unsigned short attr_name; unsigned short n; str name = {0,0}; str comment = {0,0}; str user; int i; /* sanity check */ if (NR_OF_KIDS(intr->ip)>1) { LM_ERR("LOG node suppose to have max one child" ", not %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } /* is logging enabled? */ if ( cpl_env.log_dir==0 ) goto done; /* read the attributes of the LOG node*/ p = ATTR_PTR(intr->ip); for( i=NR_OF_ATTR(intr->ip); i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case NAME_ATTR: get_str_attr( p, name.s, n, intr, script_error,1); name.len = n; break; case COMMENT_ATTR: get_str_attr( p, comment.s, n, intr, script_error,1); comment.len = n; break; default: LM_ERR("unknown attribute " "(%d) in LOG node\n",attr_name); goto script_error; } } if (comment.len==0) { LM_NOTICE("LOG node has no comment attr -> skipping\n"); goto done; } user.len = intr->user.len + name.len + comment.len; /* duplicate the attrs in shm memory */ user.s = p = (char*)shm_malloc( user.len ); if (!user.s) { LM_ERR("no more shm memory!\n"); goto runtime_error; } /* copy the user name */ memcpy( p, intr->user.s, intr->user.len); user.len = intr->user.len; p += intr->user.len; /* copy the log name */ if (name.len) { memcpy( p, name.s, name.len ); name.s = p; p += name.len; } /* copy the comment */ memcpy( p, comment.s, comment.len); comment.s = p; /* send the command */ write_cpl_cmd( CPL_LOG_CMD, &user, &name, &comment ); done: return get_first_child(intr->ip); runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_mail( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; char *p; str subject = {0,0}; str body = {0,0}; str to = {0,0}; int i; /* sanity check */ if (NR_OF_KIDS(intr->ip)>1) { LM_ERR("MAIL node suppose to have max one" " child, not %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } /* read the attributes of the MAIL node*/ for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr(p, attr_name, n, intr, script_error); switch (attr_name) { case TO_ATTR: get_str_attr(p, to.s, n, intr, script_error,0); to.len = n; break; case SUBJECT_ATTR: get_str_attr(p, subject.s, n, intr, script_error,0); subject.len = n; break; case BODY_ATTR: get_str_attr(p, body.s, n, intr, script_error,0); body.len = n; break; default: LM_ERR("unknown attribute (%d) in MAIL node\n",attr_name); goto script_error; } } if (to.len==0) { LM_ERR("email has an empty TO hdr!\n"); goto script_error; } if (body.len==0 && subject.len==0) { LM_WARN("I refuse to send email with no " "body and no subject -> skipping...\n"); goto done; } /* duplicate the attrs in shm memory */ p = (char*)shm_malloc( to.len + subject.len + body.len ); if (!p) { LM_ERR("no more shm memory!\n"); goto runtime_error; } /* copy the TO */ memcpy( p, to.s, to.len ); to.s = p; p += to.len; /* copy the subject */ if (subject.len) { memcpy( p, subject.s, subject.len ); subject.s = p; p += subject.len; } /* copy the body */ if (body.len) { memcpy( p, body.s, body.len ); body.s = p; p += body.len; } /* send the command */ write_cpl_cmd( CPL_MAIL_CMD, &to, &subject, &body); done: return get_first_child(intr->ip); runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } static inline int run_default( struct cpl_interpreter *intr ) { if (!(intr->flags&CPL_PROXY_DONE)) { /* no signaling operations */ if ( !(intr->flags&CPL_LOC_SET_MODIFIED) ) { /* no location modifications */ if (intr->loc_set==0 ) { /* case 1 : no location modifications or signaling operations * performed, location set empty -> * Look up the user's location through whatever mechanism the * server would use if no CPL script were in effect */ return SCRIPT_DEFAULT; } else { /* case 2 : no location modifications or signaling operations * performed, location set non-empty: (This can only happen * for outgoing calls.) -> * Proxy the call to the address in the location set. * With other words, let ser to continue processing the * request as nothing happened */ return SCRIPT_DEFAULT; } } else { /* case 3 : location modifications performed, no signaling * operations -> * Proxy the call to the addresses in the location set */ if (!cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags)) return SCRIPT_END; return SCRIPT_RUN_ERROR; } } else { /* case 4 : proxy operation previously taken -> return whatever the * "best" response is of all accumulated responses to the call to this * point, according to the rules of the underlying signaling * protocol. */ /* we will let ser to choose and forward one of the replies -> for this * nothing must be done */ return SCRIPT_END; } /*return SCRIPT_RUN_ERROR;*/ } /* include all inline functions for processing the switches */ #include "cpl_switches.h" /* include inline function for running proxy node */ #include "cpl_proxy.h" int cpl_run_script( struct cpl_interpreter *intr ) { char *new_ip; do { check_overflow_by_offset( SIMPLE_NODE_SIZE(intr->ip), intr, error); switch ( NODE_TYPE(intr->ip) ) { case CPL_NODE: LM_DBG("processing CPL node \n"); new_ip = run_cpl_node( intr ); /*UPDATED&TESTED*/ break; case ADDRESS_SWITCH_NODE: LM_DBG("processing address-switch node\n"); new_ip = run_address_switch( intr ); /*UPDATED&TESTED*/ break; case STRING_SWITCH_NODE: LM_DBG("processing string-switch node\n"); new_ip = run_string_switch( intr ); /*UPDATED&TESTED*/ break; case PRIORITY_SWITCH_NODE: LM_DBG("processing priority-switch node\n"); new_ip = run_priority_switch( intr ); /*UPDATED&TESTED*/ break; case TIME_SWITCH_NODE: LM_DBG("processing time-switch node\n"); new_ip = run_time_switch( intr ); /*UPDATED&TESTED*/ break; case LANGUAGE_SWITCH_NODE: LM_DBG("processing language-switch node\n"); new_ip = run_language_switch( intr ); /*UPDATED&TESTED*/ break; case LOOKUP_NODE: LM_DBG("processing lookup node\n"); new_ip = run_lookup( intr ); /*UPDATED&TESTED*/ break; case LOCATION_NODE: LM_DBG("processing location node\n"); new_ip = run_location( intr ); /*UPDATED&TESTED*/ break; case REMOVE_LOCATION_NODE: LM_DBG("processing remove_location node\n"); new_ip = run_remove_location( intr ); /*UPDATED&TESTED*/ break; case PROXY_NODE: LM_DBG("processing proxy node\n"); new_ip = run_proxy( intr );/*UPDATED&TESTED*/ break; case REJECT_NODE: LM_DBG("processing reject node\n"); new_ip = run_reject( intr ); /*UPDATED&TESTED*/ break; case REDIRECT_NODE: LM_DBG("processing redirect node\n"); new_ip = run_redirect( intr ); /*UPDATED&TESTED*/ break; case LOG_NODE: LM_DBG("processing log node\n"); new_ip = run_log( intr ); /*UPDATED&TESTED*/ break; case MAIL_NODE: LM_DBG("processing mail node\n"); new_ip = run_mail( intr ); /*UPDATED&TESTED*/ break; case SUB_NODE: LM_DBG("processing sub node\n"); new_ip = run_sub( intr ); /*UPDATED&TESTED*/ break; default: LM_ERR("unknown type node (%d)\n", NODE_TYPE(intr->ip)); goto error; } if (new_ip==CPL_RUNTIME_ERROR) { LM_ERR("runtime error\n"); return SCRIPT_RUN_ERROR; } else if (new_ip==CPL_SCRIPT_ERROR) { LM_ERR("script error\n"); return SCRIPT_FORMAT_ERROR; } else if (new_ip==DEFAULT_ACTION) { LM_DBG("running default action\n"); return run_default(intr); } else if (new_ip==EO_SCRIPT) { LM_DBG("script interpretation done!\n"); return SCRIPT_END; } else if (new_ip==CPL_TO_CONTINUE) { LM_DBG("done for the moment; waiting after signaling!\n"); return SCRIPT_TO_BE_CONTINUED; } /* move to the new instruction */ intr->ip = new_ip; }while(1); error: return SCRIPT_FORMAT_ERROR; } opensips-2.2.2/modules/cpl_c/cpl_run.h000066400000000000000000000061511300170765700177310ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CPL_RUN_H #define _CPL_RUN_H #include "../../str.h" #include "../../parser/msg_parser.h" #define SCRIPT_END 0 #define SCRIPT_DEFAULT 1 #define SCRIPT_TO_BE_CONTINUED 2 #define SCRIPT_RUN_ERROR -1 #define SCRIPT_FORMAT_ERROR -2 #define CPL_RUN_OUTGOING (1<<0) #define CPL_RUN_INCOMING (1<<1) #define CPL_IS_STATEFUL (1<<2) #define CPL_FORCE_STATEFUL (1<<3) #define CPL_LOC_SET_MODIFIED (1<<5) #define CPL_PROXY_DONE (1<<6) #define CPL_RURI_DUPLICATED (1<<10) #define CPL_TO_DUPLICATED (1<<11) #define CPL_FROM_DUPLICATED (1<<12) #define CPL_SUBJECT_DUPLICATED (1<<13) #define CPL_ORGANIZATION_DUPLICATED (1<<14) #define CPL_USERAGENT_DUPLICATED (1<<15) #define CPL_ACCEPTLANG_DUPLICATED (1<<16) #define CPL_PRIORITY_DUPLICATED (1<<17) #define CPL_DO_NOT_FREE (1<<18) #define CPL_ENDED (1<<19) #define STR_NOT_FOUND ((str*)0xffffffff) struct cpl_interpreter { unsigned int flags; str user; /* user */ str script; /* CPL script */ char *ip; /* instruction pointer */ int recv_time; /* receiving time stamp */ struct sip_msg *msg; struct location *loc_set; /* location set */ /* pointers to the string-headers needed for switches; can point directly * into the sip_msg structure (if no proxy took places) or to private * buffers into shm_memory (after a proxy happened); if a hdr is copy into a * private buffer, a corresponding flag will be set (xxxx_DUPLICATED) */ str *ruri; str *to; str *from; str *subject; str *organization; str *user_agent; str *accept_language; str *priority; /* grouped date the is needed when doing proxy */ struct proxy_st { unsigned short ordering; unsigned short recurse; /* I have to know which will be the last location that will be proxy */ struct location *last_to_proxy; /* shortcuts to the subnodes */ char *busy; char *noanswer; char *redirect; char *failure; char *default_; }proxy; }; struct cpl_interpreter* new_cpl_interpreter( struct sip_msg *msg, str *script); void free_cpl_interpreter(struct cpl_interpreter *intr); int cpl_run_script( struct cpl_interpreter *cpl_intr ); #endif opensips-2.2.2/modules/cpl_c/cpl_sig.c000066400000000000000000000062031300170765700177000ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../action.h" #include "../../dset.h" #include "../tm/tm_load.h" #include "loc_set.h" #include "cpl_sig.h" #include "cpl_env.h" /* forwards the msg to the given location set; if flags has set the * CPL_PROXY_DONE, all locations will be added as branches, otherwise, the * first one will set as RURI (this is ha case when this is the first proxy * of the message) * The given list of location will be freed, returning 0 instead. * Returns: 0 - OK * -1 - error */ int cpl_proxy_to_loc_set( struct sip_msg *msg, struct location **locs, unsigned char flag) { struct location *foo; int bflags; int r; if (!*locs) { LM_ERR("empty loc set!!\n"); goto error; } /* use the first addr in loc_set to rewrite the RURI */ LM_DBG("rewriting Request-URI with <%s>\n",(*locs)->addr.uri.s); /* set RURI*/ if ( set_ruri( msg, &((*locs)->addr.uri))==-1 ) { LM_ERR("failed to set new RURI\n"); goto error; } /* set DST URI */ if((*locs)->addr.received.s && (*locs)->addr.received.len) { LM_DBG("rewriting Destination URI " "with <%s>\n",(*locs)->addr.received.s); if (set_dst_uri( msg, &((*locs)->addr.received) ) ) { LM_ERR("failed to set destination URI\n"); goto error; } } /* is the location NATED? */ bflags = ((*locs)->flags&CPL_LOC_NATED) ? cpl_fct.ulb.nat_flag : 0 ; setb0flags(msg,bflags); /* free the location and point to the next one */ foo = (*locs)->next; free_location( *locs ); *locs = foo; /* add the rest of the locations as branches */ while(*locs) { bflags = ((*locs)->flags&CPL_LOC_NATED) ? cpl_fct.ulb.nat_flag : 0 ; LM_DBG("appending branch <%.*s>, flags %d\n", (*locs)->addr.uri.len, (*locs)->addr.uri.s, bflags); if(append_branch(msg, &(*locs)->addr.uri, &(*locs)->addr.received,0, Q_UNSPECIFIED, bflags, 0)==-1){ LM_ERR("failed when appending branch <%s>\n",(*locs)->addr.uri.s); goto error; } /* free the location and point to the next one */ foo = (*locs)->next; free_location( *locs ); *locs = foo; } /* run what proxy route is set */ if (cpl_env.proxy_route) { /* do not alter route type - it might be REQUEST or FAILURE */ run_top_route( rlist[cpl_env.proxy_route].a, msg); } /* do t_forward */ if ((r = cpl_fct.tmb.t_relay(msg, 0, 0, 0, 0, 0, 0)) < 0) { LM_ERR("t_relay failed! error=%d\n",r); goto error; } return 0; error: return -1; } opensips-2.2.2/modules/cpl_c/cpl_sig.h000066400000000000000000000017521300170765700177110ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CPL_CPL_SIG_H_ #define _CPL_CPL_SIG_H_ #include "cpl_run.h" int cpl_proxy_to_loc_set( struct sip_msg *msg, struct location **loc_set, unsigned char flag); #endif opensips-2.2.2/modules/cpl_c/cpl_switches.h000066400000000000000000000751361300170765700207670ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-06-27: file created (bogdan) */ #include "../../time_rec.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" /* UPDATED + CHECKED */ static inline char *run_address_switch( struct cpl_interpreter *intr ) { static str def_port_str = {"5060",4}; unsigned short field, subfield; char *p; char *kid; unsigned short attr_name; unsigned short n; int i; int k; str cpl_val; str *msg_val; str *uri; struct sip_uri parsed_uri; field = subfield = UNDEF_CHAR; msg_val = 0; p=ATTR_PTR(intr->ip); /* parse the attributes */ for( i=NR_OF_ATTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case FIELD_ATTR: if (field!=UNDEF_CHAR) { LM_ERR("multiple FIELD attrs found\n"); goto script_error; } field = n; break; case SUBFIELD_ATTR: if (subfield!=UNDEF_CHAR) { LM_ERR("multiple SUBFIELD attrs found\n"); goto script_error; } subfield = n; break; default: LM_ERR("unknown attribute " "(%d) in ADDRESS_SWITCH node\n",*p); goto script_error; } } if (field==UNDEF_CHAR) { LM_ERR("mandatory param FIELD no found\n"); goto script_error; } /* test the condition from all the sub-nodes */ for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: LM_DBG("NOT_PRESENT node found ->" "skipping (useless in this case)\n"); break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LM_ERR("OTHERWISE node not found as the last sub-node!\n"); goto script_error; } LM_DBG("matching on OTHERWISE node\n"); return get_first_child(kid); case ADDRESS_NODE : /* check the number of attributes */ if (NR_OF_ATTR(kid)!=1) { LM_ERR("incorrect nr of attrs " "(%d) in ADDRESS node\n",NR_OF_ATTR(kid)); goto script_error; } /* get the attribute name */ p = ATTR_PTR(kid); get_basic_attr( p, attr_name, cpl_val.len, intr, script_error); if (attr_name!=IS_ATTR && attr_name!=CONTAINS_ATTR && attr_name!=SUBDOMAIN_OF_ATTR) { LM_ERR("unknown attribute " "(%d) in ADDRESS node\n",attr_name); goto script_error; } /* get attribute value */ get_str_attr( p, cpl_val.s, cpl_val.len, intr, script_error,1); LM_DBG("testing ADDRESS branch " " attr_name=%d attr_val=[%.*s](%d)..\n", attr_name,cpl_val.len,cpl_val.s,cpl_val.len); /* extract the needed value from the message */ if (!msg_val) { switch (field) { case ORIGIN_VAL: /* FROM */ if (!intr->from) { /* get the header */ if (parse_from_header( intr->msg )<0) goto runtime_error; intr->from = &(get_from(intr->msg)->uri); } uri = intr->from; break; case DESTINATION_VAL: /* RURI */ if (!intr->ruri) intr->ruri = GET_RURI( intr->msg ); uri = intr->ruri; break; case ORIGINAL_DESTINATION_VAL: /* TO */ if (!intr->to) { /* get and parse the header */ if (!intr->msg->to && (parse_headers(intr->msg,HDR_TO_F,0)==-1 || !intr->msg->to)) { LM_ERR("bad msg or missing TO header\n"); goto runtime_error; } intr->to = &(get_to(intr->msg)->uri); } uri = intr->to; break; default: LM_ERR("unknown " "attribute (%d) in ADDRESS node\n",field); goto script_error; } LM_DBG("extracted uri is <%.*s>\n", uri->len, uri->s); switch (subfield) { case UNDEF_CHAR: msg_val = uri; break; case USER_VAL: if (parse_uri( uri->s, uri->len, &parsed_uri)<0) goto runtime_error; msg_val = &(parsed_uri.user); break; case HOST_VAL: if (parse_uri( uri->s, uri->len, &parsed_uri)<0) goto runtime_error; msg_val = &(parsed_uri.host); break; case PORT_VAL: if (parse_uri( uri->s, uri->len, &parsed_uri)<0) goto runtime_error; if (parsed_uri.port.len!=0) msg_val = &(parsed_uri.port); else msg_val = &def_port_str; break; case TEL_VAL: if (parse_uri( uri->s, uri->len, &parsed_uri)<0) goto runtime_error; if (parsed_uri.user_param_val.len==5 && memcmp(parsed_uri.user_param_val.s,"phone",5)==0) msg_val = &(parsed_uri.user); break; case ADDRESS_TYPE_VAL: case DISPLAY_VAL: default: LM_ERR("unsupported " "value attribute (%d) in ADDRESS node\n", subfield); goto script_error; } LM_DBG("extracted val. is <%.*s>\n", (msg_val==0)?0:msg_val->len, (msg_val==0)?0:msg_val->s); } /* does the value from script match the one from message? */ switch (attr_name) { case IS_ATTR: if ( (!msg_val && !cpl_val.s) || (msg_val && msg_val->len==cpl_val.len && strncasecmp(msg_val->s,cpl_val.s,cpl_val.len)==0)) { LM_DBG("matching on ADDRESS node (IS)\n"); return get_first_child(kid); } break; case CONTAINS_ATTR: if (subfield!=DISPLAY_VAL) { LM_WARN("operator " "CONTAINS applies only to DISPLAY -> ignored\n"); } else { if ( msg_val && cpl_val.len<=msg_val->len && strcasestr_str(msg_val, &cpl_val)!=0 ) { LM_DBG("matching on " "ADDRESS node (CONTAINS)\n"); return get_first_child(kid); } } break; case SUBDOMAIN_OF_ATTR: switch (subfield) { case HOST_VAL: k = msg_val->len - cpl_val.len; if (k>=0 && (k==0 || msg_val->s[k-1]=='.') && !strncasecmp(cpl_val.s,msg_val->s+k,cpl_val.len) ) { LM_DBG("matching on " "ADDRESS node (SUBDOMAIN_OF)\n"); return get_first_child(kid); } break; case TEL_VAL: if (msg_val==0) break; if (msg_val->len>=cpl_val.len && !strncasecmp( cpl_val.s,msg_val->s,cpl_val.len)) { LM_DBG("matching on " "ADDRESS node (SUBDOMAIN_OF)\n"); return get_first_child(kid); } break; default: LM_WARN("operator SUBDOMAIN_OF applies " "only to HOST or TEL -> ignored\n"); } break; } break; default: LM_ERR("unknown output node type " "(%d) for ADDRESS_SWITCH node\n",NODE_TYPE(kid)); goto script_error; } } /* none of the branches of ADDRESS_SWITCH matched -> go for default */ return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_string_switch( struct cpl_interpreter *intr ) { unsigned short field; char *p; char *kid; char *not_present_node; unsigned short attr_name; int i; str cpl_val; str msg_val; not_present_node = 0; msg_val.s = 0; msg_val.len = 0; /* parse the attribute */ if (NR_OF_ATTR(intr->ip)!=1) { LM_ERR("node should have 1 attr, not" " (%d)\n",NR_OF_ATTR(intr->ip)); goto script_error; } p=ATTR_PTR(intr->ip); get_basic_attr( p, attr_name, field, intr, script_error); if (attr_name!=FIELD_ATTR) { LM_ERR("unknown param type (%d)" " for STRING_SWITCH node\n",*p); goto script_error; } for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: if (not_present_node) { LM_ERR("NOT_PRESENT node found twice!\n"); goto script_error; } not_present_node = kid; break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LM_ERR("OTHERWISE node " "not found as the last sub-node!\n"); goto script_error; } LM_DBG("matching on OTHERWISE node\n"); return get_first_child(kid); case STRING_NODE : /* check the number of attributes */ if (NR_OF_ATTR(kid)!=1) { LM_ERR("incorrect nr of attrs " "(%d) in STRING node (expected 1)\n",NR_OF_ATTR(kid)); goto script_error; } /* get the attribute name */ p = ATTR_PTR(kid); get_basic_attr( p, attr_name, cpl_val.len, intr, script_error); if (attr_name!=IS_ATTR && attr_name!=CONTAINS_ATTR ) { LM_ERR("unknown attribute " "(%d) in STRING node\n",attr_name); goto script_error; } /* get attribute value */ get_str_attr( p, cpl_val.s, cpl_val.len, intr, script_error,1); LM_DBG("testing STRING branch " "attr_name=%d attr_val=[%.*s](%d)..\n", attr_name,cpl_val.len,cpl_val.s,cpl_val.len); if (!msg_val.s) { switch (field) { case SUBJECT_VAL: /* SUBJECT */ if (intr->subject==STR_NOT_FOUND) goto not_present; if (!intr->subject) { /* get the subject header */ if (!intr->msg->subject) { if (parse_headers(intr->msg, HDR_SUBJECT_F,0)==-1) { LM_ERR("bad SUBJECT header\n"); goto runtime_error; } else if (!intr->msg->subject) { /* hdr not present */ intr->subject = STR_NOT_FOUND; goto not_present; } } intr->subject = &(intr->msg->subject->body); } trim_len( msg_val.len,msg_val.s, *(intr->subject)); break; case ORGANIZATION_VAL: /* ORGANIZATION */ if (intr->organization==STR_NOT_FOUND) goto not_present; if (!intr->organization) { /* get the organization header */ if (!intr->msg->organization) { if (parse_headers(intr->msg, HDR_ORGANIZATION_F,0)==-1) { LM_ERR("bad ORGANIZATION hdr\n"); goto runtime_error; } else if (!intr->msg->organization) { /* hdr not present */ intr->organization = STR_NOT_FOUND; goto not_present; } } intr->organization = &(intr->msg->organization->body); } trim_len( msg_val.len,msg_val.s, *(intr->organization)); break; case USER_AGENT_VAL: /* User Agent */ if (intr->user_agent==STR_NOT_FOUND) goto not_present; if (!intr->user_agent) { /* get the header */ if (!intr->msg->user_agent) { if (parse_headers(intr->msg, HDR_USERAGENT_F,0)==-1) { LM_ERR("bad USERAGENT hdr\n"); goto runtime_error; } else if (!intr->msg->user_agent) { /* hdr not present */ intr->user_agent = STR_NOT_FOUND; goto not_present; } } intr->user_agent = &(intr->msg->user_agent->body); } trim_len( msg_val.len,msg_val.s, *(intr->user_agent)); break; default: LM_ERR("unknown " "attribute (%d) in STRING node\n",field); goto script_error; } LM_DBG("extracted msg string is " "<%.*s>\n",msg_val.len, msg_val.s); } /* does the value from script match the one from message? */ switch (attr_name) { case IS_ATTR: if ( (!msg_val.s && !cpl_val.s) || (msg_val.len==cpl_val.len && strncasecmp(msg_val.s,cpl_val.s,cpl_val.len)==0)) { LM_DBG("matching on STRING node (IS)\n"); return get_first_child(kid); } break; case CONTAINS_ATTR: if (cpl_val.len<=msg_val.len && strcasestr_str(&msg_val, &cpl_val)!=0 ) { LM_DBG("matching on STRING node (CONTAINS)\n"); return get_first_child(kid); } break; } break; default: LM_ERR("unknown output node type " "(%d) for STRING_SWITCH node\n",NODE_TYPE(kid)); goto script_error; } } /* none of the branches of STRING_SWITCH matched -> go for default */ return DEFAULT_ACTION; not_present: LM_DBG("required hdr not present in sip msg\n"); if (not_present_node) return get_first_child(not_present_node); /* look for the NOT_PRESENT node */ LM_DBG("searching for NOT_PRESENT sub-node..\n"); for(; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); if (NODE_TYPE(kid)==NOT_PRESENT_NODE) return get_first_child(kid); if (NODE_TYPE(kid)==OTHERWISE_NODE) return get_first_child(kid); } return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_priority_switch( struct cpl_interpreter *intr ) { static str default_val={"normal",6}; unsigned short n; char *p; char *kid; char *not_present_node; unsigned short attr_name; unsigned short attr_val; unsigned short msg_attr_val; unsigned short msg_prio; int i; str cpl_val = {0,0}; str msg_val = {0,0}; not_present_node = 0; msg_attr_val = NORMAL_VAL; for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: if (not_present_node) { LM_ERR("NOT_PRESENT node found twice!\n"); goto script_error; } not_present_node = kid; break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LM_ERR("OTHERWISE node not found as the last sub-node!\n"); goto script_error; } LM_DBG("matching on OTHERWISE node\n"); return get_first_child(kid); case PRIORITY_NODE : if (NR_OF_ATTR(kid)!=1) goto script_error; /* get the attribute */ p = ATTR_PTR(kid); get_basic_attr( p, attr_name, attr_val, intr, script_error); if (attr_name!=LESS_ATTR && attr_name!=GREATER_ATTR && attr_name!=EQUAL_ATTR){ LM_ERR("unknown attribute " "(%d) in PRIORITY node\n",attr_name); goto script_error; } /* attribute's encoded value */ if (attr_val!=EMERGENCY_VAL && attr_val!=URGENT_VAL && attr_val!=NORMAL_VAL && attr_val!=NON_URGENT_VAL && attr_val!=UNKNOWN_PRIO_VAL) { LM_ERR("unknown encoded " "value (%d) for attribute (*d) in PRIORITY node\n",*p); goto script_error; } if (attr_val==UNKNOWN_PRIO_VAL) { if (attr_name!=EQUAL_ATTR) { LM_ERR("bad PRIORITY" " branch: attr=EQUAL doesn't match val=UNKNOWN\n"); goto script_error; } /* if the attr is UNKNOWN, its string value is present */ get_basic_attr(p, n,cpl_val.len, intr, script_error); if (n!=PRIOSTR_ATTR) { LM_ERR("expected PRIOSTR" "(%d) attr, found (%d)\n",PRIOSTR_ATTR,n); goto script_error; } get_str_attr(p, cpl_val.s, cpl_val.len,intr,script_error,1); } LM_DBG("testing PRIORITY branch " "(attr=%d,val=%d) [%.*s](%d)..\n", attr_name,attr_val,cpl_val.len,cpl_val.s,cpl_val.len); if (!msg_val.s) { if (!intr->priority) { /* get the PRIORITY header from message */ if (!intr->msg->priority) { if (parse_headers(intr->msg,HDR_PRIORITY_F, 0)==-1) { LM_ERR("bad sip msg or PRIORITY header !\n"); goto runtime_error; } else if (!intr->msg->priority) { LM_NOTICE("missing PRIORITY header -> using " "default value \"normal\"!\n"); intr->priority = &default_val; } else { intr->priority = &(intr->msg->priority->body); } } else { intr->priority = &(intr->msg->priority->body); } } trim_len( msg_val.len, msg_val.s, *(intr->priority)); /* encode attribute's value from SIP message */ if ( msg_val.len==EMERGENCY_STR_LEN && !strncasecmp(msg_val.s,EMERGENCY_STR,msg_val.len) ) { msg_attr_val = EMERGENCY_VAL; } else if ( msg_val.len==URGENT_STR_LEN && !strncasecmp(msg_val.s,URGENT_STR,msg_val.len) ) { msg_attr_val = URGENT_VAL; } else if ( msg_val.len==NORMAL_STR_LEN && !strncasecmp(msg_val.s,NORMAL_STR,msg_val.len) ) { msg_attr_val = NORMAL_VAL; } else if ( msg_val.len==NON_URGENT_STR_LEN && !strncasecmp(msg_val.s,NON_URGENT_STR,msg_val.len) ) { msg_attr_val = NON_URGENT_VAL; } else { msg_attr_val = UNKNOWN_PRIO_VAL; } LM_DBG("extracted msg priority is " "<%.*s> decoded as [%d]\n", msg_val.len,msg_val.s,msg_attr_val); } LM_DBG("using msg string <%.*s>\n", msg_val.len, msg_val.s); /* attr_val (from cpl) cannot be UNKNOWN - we already * check it -> check only for msg_attr_val for non-EQUAL op */ if (msg_attr_val==UNKNOWN_PRIO_VAL && attr_name!=EQUAL_ATTR) { LM_NOTICE("UNKNOWN " "value found in sip_msg when string a LESS/GREATER " "cmp -> force the value to default \"normal\"\n"); msg_prio = NORMAL_VAL; } else { msg_prio = msg_attr_val; } /* does the value from script match the one from message? */ switch (attr_name) { case LESS_ATTR: switch (attr_val) { case EMERGENCY_VAL: if (msg_prio!=EMERGENCY_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ case URGENT_VAL: if (msg_prio!=EMERGENCY_VAL && msg_prio!=URGENT_VAL) break; /* OK */ else continue; /* for cycle for all kids */ case NORMAL_VAL: if (msg_prio==NON_URGENT_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ case NON_URGENT_VAL: continue; /* for cycle for all kids */ } break; case GREATER_ATTR: switch (attr_val) { case EMERGENCY_VAL: continue; /* for cycle for all kids */ case URGENT_VAL: if (msg_prio!=EMERGENCY_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ case NORMAL_VAL: if (msg_prio!=NON_URGENT_VAL && msg_prio!=NORMAL_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ case NON_URGENT_VAL: if (msg_prio!=NON_URGENT_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ } break; case EQUAL_ATTR: if ( attr_val==msg_prio ) { if (attr_val==UNKNOWN_PRIO_VAL) { if ( msg_val.len==cpl_val.len && !strncasecmp(msg_val.s,cpl_val.s,msg_val.len)){ break; /* OK */ } } else { break; /* OK */ } } continue; /* for cycle for all kids */ break; } /* end switch for attr_name */ LM_DBG("matching current PRIORITY node\n"); return get_first_child(kid); break; default: LM_ERR("unknown output node type" " (%d) for PRIORITY_SWITCH node\n",NODE_TYPE(kid)); goto script_error; } /* end switch for NODE_TYPE */ } /* end for for all kids */ /* none of the branches of PRIORITY_SWITCH matched -> go for default */ return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } inline static int set_TZ(char *tz_env) { LM_DBG("switching TZ as \"%s\"\n",tz_env); if (putenv( tz_env )==-1) { LM_ERR("setenv failed -> unable to set TZ " " \"%s\"\n",tz_env); return -1; } tzset(); /* just to be sure */ return 0; } /* UPDATED + CHECKED */ static inline char *run_time_switch( struct cpl_interpreter *intr ) { char *p; char *kid; char *attr_str; unsigned short attr_name; unsigned short attr_len; unsigned char flags = 0; int nr_attrs; int i,j; str user_tz = {0,0}; ac_tm_t att; tmrec_p trt = 0; LM_DBG("checking recv. time stamp <%d>\n", intr->recv_time); switch (NR_OF_ATTR(intr->ip)) { case 1: p = ATTR_PTR(intr->ip); get_basic_attr( p, attr_name, user_tz.len, intr, script_error); if (attr_name!=TZID_ATTR) { LM_ERR("bad attribute -> " " expected=%d, found=%d\n",TZID_ATTR,attr_name); goto script_error; } get_str_attr( p, user_tz.s, user_tz.len, intr, script_error, 1); case 0: break; default: LM_ERR("incorrect number of attr ->" " found=%d expected=(0,1)\n",NR_OF_ATTR(intr->ip)); goto script_error; } if (user_tz.s && user_tz.len) { if (set_TZ(user_tz.s)==-1) goto runtime_error; flags |= (1<<7); } for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: LM_DBG("NOT_PRESENT node found ->" "skipping (useless in this case)\n"); break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LM_ERR("OTHERWISE node " "not found as the last sub-node!\n"); goto script_error; } LM_DBG("matching on OTHERWISE node\n"); return get_first_child(kid); case TIME_NODE : /* init structures */ trt = tmrec_new(PKG_ALLOC); if (!trt) { LM_ERR("no more pkg error\n"); goto script_error; } if(ac_tm_set_time( &att, intr->recv_time)) goto runtime_error; /* let's see how many attributes we have */ nr_attrs = NR_OF_ATTR(kid); /* get the attributes */ p = ATTR_PTR(kid); for(j=0;j go for default */ tmrec_free( trt ); return DEFAULT_ACTION; runtime_error: if ( flags&(1<<7) ) set_TZ(cpl_env.orig_tz.s); tmrec_free( trt ); return CPL_RUNTIME_ERROR; parse_err: LM_ERR("error parsing attr [%d][%s]\n", attr_name,attr_str?(char*)attr_str:"NULL"); script_error: if ( flags&(1<<7) ) set_TZ(cpl_env.orig_tz.s); tmrec_free( trt ); return CPL_SCRIPT_ERROR; } inline static int is_lang_tag_matching(str *range,str *cpl_tag,str *cpl_subtag) { char *c; char *end; str tag = {0,0}; str subtag = {0,0}; c = range->s; end = range->s + range->len; while(c='a' && ((*c)|0x20)<='z' ) { /*DBG("--- tag ---> <%c>[%d]\n",*c,*c);*/ tag.len++; c++; } if (tag.len==0) goto error; if (c='a' && ((*c)|0x20)<='z' ) { /*DBG("--- subtag ---> <%c>[%d]\n",*c,*c);*/ subtag.len++; c++; } if (subtag.len==0) goto error; } else { subtag.s = 0; } if (clen,cpl_tag->s,cpl_subtag->len,cpl_subtag->s); /* language range of "*" is ignored for the purpose of matching*/ if ( !(tag.len==1 && *tag.s=='*') ) { /* does the language tag matches ? */ if (tag.len==cpl_tag->len && !strncasecmp(tag.s,cpl_tag->s, tag.len)) { /* if the subtag of the range is void -> matche */ if (subtag.len==0) return 1; /* the subtags equals -> matche */ if (subtag.len==cpl_subtag->len && !strncasecmp(subtag.s,cpl_subtag->s,subtag.len) ) return 1; } } /* if ',' go for the next language range */ if (*c==',') c++; } else { goto error; } } no_matche: return 0; error: LM_ERR("parse error in Accept-" "Language body <%.*s> at char <%c>[%d] offset %ld!\n", range->len,range->s,*c,*c,(long)(c-range->s)); return -1; } /* UPDATED + CHECKED */ static inline char *run_language_switch( struct cpl_interpreter *intr ) { char *p; char *kid; char *not_present_node; unsigned short attr_name; int nr_attr; int i,j; str attr = {0,0}; str msg_val = {0,0}; str lang_tag = {0,0}; str lang_subtag = {0,0}; not_present_node = 0; for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: if (not_present_node) { LM_ERR("NOT_PRESENT node found twice!\n"); goto script_error; } not_present_node = kid; break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LM_ERR("OTHERWISE node " "not found as the last sub-node!\n"); goto script_error; } LM_DBG("matching on OTHERWISE node\n"); return get_first_child(kid); case LANGUAGE_NODE : /* check the number of attributes */ nr_attr = NR_OF_ATTR(kid); if (nr_attr<1 || nr_attr>2) { LM_ERR("incorrect nr of attrs " "(%d) in LANGUAGE node (1 or 2)\n",NR_OF_ATTR(kid)); goto script_error; } /* get the attributes */ p = ATTR_PTR(kid); lang_tag.s = lang_subtag.s = 0; lang_tag.len = lang_subtag.len = 0; for(j=0;j if not yet, do it now * and remember it for the next times */ if (!msg_val.s) { if (intr->accept_language==STR_NOT_FOUND) goto not_present; if (!intr->accept_language) { /* get the accept_language header */ if (!intr->msg->accept_language) { if (parse_headers(intr->msg, HDR_ACCEPTLANGUAGE_F,0)==-1) { LM_ERR("bad ACCEPT_LANGUAGE header\n"); goto runtime_error; } else if (!intr->msg->accept_language) { /* hdr not present */ intr->accept_language = STR_NOT_FOUND; goto not_present; } } intr->subject = &(intr->msg->accept_language->body); } } trim_len( msg_val.len,msg_val.s, *(intr->subject)); LM_DBG("extracted msg string is " "<%.*s>\n",msg_val.len, msg_val.s); /* does the value from script match the one from message? */ if (msg_val.len && msg_val.s) { j = is_lang_tag_matching(&msg_val,&lang_tag,&lang_subtag); if (j==1) { LM_DBG("matching on LANGUAGE node\n"); return get_first_child(kid); }else if (j==-1) { goto runtime_error; } } break; default: LM_ERR("unknown output " "node type (%d) for LANGUAGE_SWITCH node\n", NODE_TYPE(kid)); goto script_error; } /* end switch for NODE_TYPE */ } /* end for for all kids */ return DEFAULT_ACTION; not_present: LM_DBG("required hdr not present in sip msg\n"); if (not_present_node) return get_first_child(not_present_node); /* look for the NOT_PRESENT node */ LM_DBG("searching for NOT_PRESENT sub-node..\n"); for(; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); if (NODE_TYPE(kid)==NOT_PRESENT_NODE) return get_first_child(kid); } return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } opensips-2.2.2/modules/cpl_c/cpl_utils.h000066400000000000000000000024331300170765700202640ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2003-06-24: file created (bogdan) */ #ifndef _CPL_UTILS_H #define _CPL_UTILS_H #include #include "../../str.h" /* looks for s2 into s1 */ static inline char *strcasestr_str(str *s1, str *s2) { int i,j; for(i=0;ilen-s2->len;i++) { for(j=0;jlen;j++) { if ( !((s1->s[i+j]==s2->s[j]) || ( isalpha((int)s1->s[i+j]) && ((s1->s[i+j])^(s2->s[j]))==0x20 )) ) break; } if (j==s2->len) return s1->s+i; } return 0; } #endif opensips-2.2.2/modules/cpl_c/doc/000077500000000000000000000000001300170765700166605ustar00rootroot00000000000000opensips-2.2.2/modules/cpl_c/doc/cpl_c.xml000066400000000000000000000020651300170765700204650ustar00rootroot00000000000000 %docentities; ]> cpl_c Module &osipsname; Bogdan-Andrei Iancu
bogdan@opensips.org
Bogdan-Andrei Iancu
bogdan@opensips.org
2003 &fhg; $Revision: 8740 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/cpl_c/doc/cpl_c_admin.xml000066400000000000000000000470571300170765700216470ustar00rootroot00000000000000 &adminguide;
Overview cpl_c modules implements a CPL (Call Processing Language) interpreter. Support for uploading/downloading/removing scripts via SIP REGISTER method is present.
Dependencies
&osips; Modules The following modules must be loaded before this module: any DB module- a DB module for interfacing the DB operations (modules like mysql, postgres, dbtext, etc) TM (Transaction) module- used for proxying/forking requests SL (StateLess) module - used for sending stateless reply when responding to REGISTER request or for sending back error responses USRLOC (User Location) module - used for implementing lookup("registration") tag (adding into location set of the users' contact)
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml2 and libxml2-devel - on some SO, these to packages are merged into libxml2. This library contains an engine for XML parsing, DTD validation and DOM manipulation.
Exported Parameters
<varname>db_url</varname> (string) A SQL URL have to be given to the module for knowing where the database containing the table with CPL scripts is locates. If required a user name and password can be specified for allowing the module to connect to the database server. Default value is &defaultdb;. Set <varname>db_url</varname> parameter ... modparam("cpl_c","db_url","&exampledb;") ...
<varname>db_table</varname> (string) Indicates the name of the table that store the CPL scripts. This table must be locate into the database specified by db_url parameter. For more about the format of the CPL table please see the modules/cpl_c/init.mysql file. Default value is cpl. Set <varname>db_table</varname> parameter ... modparam("cpl_c","cpl_table","cpl") ...
<varname>username_column</varname> (string) Indicates the name of the column used for storing the username. Default value is username. Set <varname>username_column</varname> parameter ... modparam("cpl_c","username_column","username") ...
<varname>domain_column</varname> (string) Indicates the name of the column used for storing the domain. Default value is domain. Set <varname>domain_column</varname> parameter ... modparam("cpl_c","domain_column","domain") ...
<varname>cpl_xml_column</varname> (string) Indicates the name of the column used for storing the the XML version of the cpl script. Default value is cpl_xml. Set <varname>cpl_xml_column</varname> parameter ... modparam("cpl_c","cpl_xml_column","cpl_xml") ...
<varname>cpl_bin_column</varname> (string) Indicates the name of the column used for storing the the binary version of the cpl script (compiled version). Default value is cpl_bin. Set <varname>cpl_bin_column</varname> parameter ... modparam("cpl_c","cpl_bin_column","cpl_bin") ...
<varname>cpl_dtd_file</varname> (string) Points to the DTD file describing the CPL grammar. The file name may include also the path to the file. This path can be absolute or relative (be careful the path will be relative to the starting directory of &osips;). This parameter is MANDATORY! Set <varname>cpl_dtd_file</varname> parameter ... modparam("cpl_c","cpl_dtd_file","/etc/opensips/cpl-06.dtd") ...
<varname>log_dir</varname> (string) Points to a directory where should be created all the log file generated by the LOG CPL node. A log file per user will be created (on demand) having the name username.log. If this parameter is absent, the logging will be disabled without generating error on execution. Set <varname>log_dir</varname> parameter ... modparam("cpl_c","log_dir","/var/log/opensips/cpl") ...
<varname>proxy_recurse</varname> (int) Tells for how many time is allow to have recurse for PROXY CPL node If it has value 2, when doing proxy, only twice the proxy action will be re-triggered by a redirect response; the third time, the proxy execution will end by going on REDIRECTION branch. The recurse feature can be disable by setting this parameter to 0 Default value of this parameter is 0. Set <varname>proxy_recurse</varname> parameter ... modparam("cpl_c","proxy_recurse",2) ...
<varname>proxy_route</varname> (string) Before doing proxy (forward), a script route can be executed. All modifications made by that route will be reflected only for the current branch. Default value of this parameter is NULL (none). Set <varname>proxy_route</varname> parameter ... modparam("cpl_c","proxy_route", "1") ...
<varname>case_sensitive</varname> (int) Tells if the username matching should be perform case sensitive or not. Set it to a non zero value to force a case sensitive handling of usernames. Default value of this parameter is 0. Set <varname>case_sensitive</varname> parameter ... modparam("cpl_c","case_sensitive",1) ...
<varname>realm_prefix</varname> (string) Defines a prefix for the domain part which should be ignored in handling users and scripts. Default value of this parameter is empty string. Set <varname>realm_prefix</varname> parameter ... modparam("cpl_c","realm_prefix","sip.") ...
<varname>lookup_domain</varname> (string) Used by lookup tag to indicate where to perform user location. Basically this is the name of the usrloc domain (table) where the user registrations are kept. If set to empty string, the lookup node will be disabled - no user location will be performed. Default value of this parameter is NULL. Set <varname>lookup_domain</varname> parameter ... modparam("cpl_c","lookup_domain","location") ...
<varname>lookup_append_branches</varname> (int) Tells if the lookup tag should append branches (to do parallel forking) if user_location lookup returns more than one contact. Set it to a non zero value to enable parallel forking for location lookup tag. Default value of this parameter is 0. Set <varname>lookup_append_branches</varname> parameter ... modparam("cpl_c","lookup_append_branches",1) ...
<varname>use_domain</varname> (integer) Indicates if the domain part of the URI should be used in user identification (otherwise only username part will be used). Default value is 0 (disabled). Set <varname>use_domain</varname> parameter ... modparam("cpl_c","use_domain",1) ...
Exported Functions
<function moreinfo="none">cpl_run_script(type,mode)</function> Starts the execution of the CPL script. The user name is fetched from new_uri or requested uri or from To header -in this order- (for incoming execution) or from FROM header (for outgoing execution). Regarding the stateful/stateless message processing, the function is very flexible, being able to run in different modes (see below the"mode" parameter). Normally this function will end script execution. There is no guaranty that the CPL script interpretation ended when &osips; script ended also (for the same INVITE ;-)) - this can happen when the CPL script does a PROXY and the script interpretation pause after proxying and it will be resume when some reply is received (this can happen in a different process of OpenSIPS). If the function returns true to script, if value "1" is returned, the SIP server should continue with the normal behavior as if no script existed; if value (2) is returned, it means no script was found, so nothing was done. When some error is reported (a false return code), the function itself haven't sent any SIP error reply (this can be done from script). Meaning of the parameters is as follows: type - which part of the script should be run; set it to "incoming" for having the incoming part of script executed (when an INVITE is received) or to "outgoing" for running the outgoing part of script (when a user is generating an INVITE - call). mode - sets the interpreter mode as stateless/stateful behavior. The following modes are accepted: IS_STATELESS - the current INVITE has no transaction created yet. All replies (redirection or deny) will be done is a stateless way. The execution will switch to stateful only when proxy is done. So, if the function returns, will be in stateless mode. IS_STATEFUL - the current INVITE has already a transaction associated. All signaling operations (replies or proxy) will be done in stateful way.So, if the function returns, will be in stateful mode. FORCE_STATEFUL - the current INVITE has no transaction created yet. All signaling operations will be done is a stateful way (on signaling, the transaction will be created from within the interpreter). So, if the function returns, will be in stateless mode. HINT: is_stateful is very difficult to manage from the routing script (script processing can continue in stateful mode); is_stateless is the fastest and less resources consumer (transaction is created only if proxying is done), but there is minimal protection against retransmissions (since replies are send stateless); force_stateful is a good compromise - all signaling is done stateful (retransmission protection) and in the same time, if returning to script, it will be in stateless mode (easy to continue the routing script execution) This function can be used from REQUEST_ROUTE. <function>cpl_run_script</function> usage ... cpl_run_script("incoming","force_stateful"); ...
<function moreinfo="none">cpl_process_register()</function> This function MUST be called only for REGISTER requests. It checks if the current REGISTER request is related or not with CPL script upload/download/ remove. If it is, all the needed operation will be done. For checking if the REGISTER is CPL related, the function looks fist to "Content-Type" header. If it exists and has a the mime type set to "application/cpl+xml" means this is a CPL script upload/remove operation. The distinction between to case is made by looking at "Content-Disposition" header; id its value is "script;action=store", means it's an upload; if it's "script;action=remove", means it's a remove operation; other values are considered to be errors. If no "Content-Type" header is present, the function looks to "Accept" header and if it contains the "*" or "application/cpl-xml" the request it will be consider one for downloading CPL scripts. The functions returns to script only if the REGISTER is not related to CPL. In other case, the function will send by itself the necessary replies (stateless - using sl), including for errors. This function can be used from REQUEST_ROUTE. <function>cpl_process_register</function> usage ... if (method=="REGISTER") { cpl_process_register(); } ...
<function moreinfo="none">cpl_process_register_norpl() </function> Same as cpl_process_register without internally generating the reply. All information (script) is appended to the reply but without sending it out. Main purpose of this function is to allow integration between CPL and UserLocation services via same REGISTER messages. This function can be used from REQUEST_ROUTE. <function>cpl_process_register_norpl</function> usage ... if (method=="REGISTER") { cpl_process_register(); # continue with usrloc part save("location"); } ...
Exported MI Functions
<function moreinfo="none">LOAD_CPL</function> For the given user, loads the XML cpl file, compiles it into binary format and stores both format into database. Name: LOAD_CPL Parameters: username : name of the user cpl_filename: file name MI FIFO Command format: :LOAD_CPL:_reply_fifo_file_ username cpl_filename _empty_line_
<function moreinfo="none">REMOVE_CPL</function> For the given user, removes the entire database record (XML cpl and binary cpl); user with empty cpl scripts are not accepted. Name: REMOVE_CPL Parameters: username : name of the user MI FIFO Command format: :REMOVE_CPL:_reply_fifo_file_ username _empty_line_
<function moreinfo="none">GET_CPL</function> For the given user, returns the CPL script in XML format. Name: GET_CPL Parameters: username : name of the user MI FIFO Command format: :GET_CPL:_reply_fifo_file_ username _empty_line_
Installation and Running
Database setup Before running &osips; with cpl_c, you have to setup the database table where the module will store the CPL scripts. For that, if the table was not created by the installation script or you choose to install everything by yourself you can use the cpc-create.sql SQL script in the database directories in the opensips/scripts folder as template. Database and table name can be set with module parameters so they can be changed, but the name of the columns must be as they are in the SQL script. You can also find the complete database documentation on the project webpage, &osipsdbdocslink;.
opensips-2.2.2/modules/cpl_c/loc_set.h000066400000000000000000000104361300170765700177200ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CPL_LOC_SET_H_ #define _CPL_LOC_SET_H_ #include #include #include #include #include "../../mem/shm_mem.h" #include "../../str.h" #include "../../dprint.h" #define CPL_LOC_DUPL (1<<0) #define CPL_LOC_NATED (1<<1) struct location { struct address { str uri; str received; unsigned int priority; }addr; int flags; struct location *next; }; static inline void free_location( struct location *loc) { shm_free( loc ); } /* insert a new location into the set maintaining order by the prio val * - the list starts with the largest prio * - for locations having the same prio val, the adding order will be kept */ static inline int add_location(struct location **loc_set, str *uri, str *received, unsigned int prio, int flags) { struct location *loc; struct location *foo, *bar; if(received && received->s && received->len) loc = (struct location*)shm_malloc(sizeof(struct location)+ ((flags&CPL_LOC_DUPL)?uri->len+1+received->len+1:0) ); else loc = (struct location*)shm_malloc( sizeof(struct location)+((flags&CPL_LOC_DUPL)?uri->len+1:0) ); if (!loc) { LM_ERR("no more free shm memory!\n"); return -1; } if (flags&CPL_LOC_DUPL) { loc->addr.uri.s = ((char*)loc)+sizeof(struct location); memcpy(loc->addr.uri.s,uri->s,uri->len); loc->addr.uri.s[uri->len] = 0; } else { loc->addr.uri.s = uri->s; } loc->addr.uri.len = uri->len; loc->addr.priority = prio; loc->flags = flags; if(received && received->s && received->len) { if (flags&CPL_LOC_DUPL) { loc->addr.received.s = ((char*)loc)+sizeof(struct location)+ uri->len+1; memcpy(loc->addr.received.s,received->s,received->len); loc->addr.received.s[received->len] = 0; } else { loc->addr.received.s = received->s; } loc->addr.received.len = received->len; } else { loc->addr.received.s = 0; loc->addr.received.len = 0; } /* find the proper place for the new location */ foo = *loc_set; bar = 0; while(foo && foo->addr.priority>=prio) { bar = foo; foo = foo->next; } if (!bar) { /* insert at the beginning */ loc->next = *loc_set; *loc_set = loc; } else { /* insert after bar, before foo */ loc->next = foo; bar->next = loc; } return 0; } static inline void remove_location(struct location **loc_set, char *uri_s, int uri_len) { struct location *loc = *loc_set; struct location *prev_loc = 0; for( ; loc ; prev_loc=loc,loc=loc->next ) { if (loc->addr.uri.len==uri_len && !strncasecmp(loc->addr.uri.s,uri_s,uri_len) ) break; } if (loc) { LM_DBG("removing from loc_set <%.*s>\n", uri_len,uri_s); if (prev_loc) prev_loc->next=loc->next; else (*loc_set)=loc->next; shm_free( loc ); } else { LM_DBG("no matching in loc_set for <%.*s>\n", uri_len,uri_s); } } static inline struct location *remove_first_location(struct location **loc_set) { struct location *loc; if (!*loc_set) return 0; loc = *loc_set; *loc_set = (*loc_set)->next; loc->next = 0; LM_DBG("removing <%.*s>\n", loc->addr.uri.len,loc->addr.uri.s); return loc; } static inline void empty_location_set(struct location **loc_set) { struct location *loc; while (*loc_set) { loc = (*loc_set)->next; shm_free(*loc_set); *loc_set = loc; } *loc_set = 0; } static inline void print_location_set(struct location *loc_set) { while (loc_set) { LM_DBG("uri=<%s> received=<%s> q=%d\n", loc_set->addr.uri.s, loc_set->addr.received.s, loc_set->addr.priority); loc_set=loc_set->next; } } #endif opensips-2.2.2/modules/cpl_c/sub_list.c000066400000000000000000000030731300170765700201060ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* History: * -------- * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) */ #include #include #include "../../mem/mem.h" #include "sub_list.h" struct node* append_to_list(struct node *head, char *offset, char *name) { struct node *new_node; new_node = pkg_malloc(sizeof(struct node)); if (!new_node) return 0; new_node->offset = offset; new_node->name = name; new_node->next = head; return new_node; } char* search_the_list(struct node *head, char *name) { struct node *n; n = head; while (n) { if (strcasecmp(n->name,name)==0) return n->offset; n = n->next; } return 0; } void delete_list(struct node* head) { struct node *n; ; while (head) { n=head->next; pkg_free(head); head = n; } } opensips-2.2.2/modules/cpl_c/sub_list.h000066400000000000000000000021611300170765700201100ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CPL_SUB_LIST_H #define _CPL_SUB_LIST_H struct node { char *offset; char *name; struct node *next; }; struct node* append_to_list(struct node *head, char *offdet, char *name); char* search_the_list(struct node *head, char *name); void delete_list(struct node *head ); #endif opensips-2.2.2/modules/db_berkeley/000077500000000000000000000000001300170765700173025ustar00rootroot00000000000000opensips-2.2.2/modules/db_berkeley/Makefile000066400000000000000000000037621300170765700207520ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile # extra debug messages # -DBDB_EXTRA_DEBUG is optional include ../../Makefile.defs auto_gen= NAME=db_berkeley.so SCRIPTS_DIR?=../../scripts/ UTILS_DIR?=../../utils/ DEFS +=-I$(LOCALBASE)/include -I$(LOCALBASE)/BerkeleyDB.4.6/include \ -I$(SYSBASE)/include -I$(LOCALBASE)/include/db46 IS_BSD= $(shell echo "$(OS)" | sed -e 's/^.*bsd/yes/i' ) ifeq ($(IS_BSD), yes) DB_LIB=ldb-4.6 else DB_LIB=ldb endif LIBS=-L$(LOCALBASE)/lib -L$(SYSBASE)/lib -L$(LOCALBASE)/BerkeleyDB.4.6/lib \ -$(DB_LIB) include ../../Makefile.modules .PHONY: $(UTILS_DIR)/db_berkeley/bdb_recover $(UTILS_DIR)/db_berkeley/bdb_recover: make -C $(UTILS_DIR)/db_berkeley/ install_module_custom: $(bin_prefix)/$(bin_dir) $(UTILS_DIR)/db_berkeley/bdb_recover mkdir -p $(modules_prefix)/$(lib_dir)/opensipsctl ; \ sed -e "s#/usr/local/share/opensips/#$(data_target)#g" \ < $(SCRIPTS_DIR)/opensipsctl.db_berkeley > /tmp/opensipsctl.db_berkeley ; \ $(INSTALL_CFG) /tmp/opensipsctl.db_berkeley \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.db_berkeley ; \ rm -fr /tmp/opensipsctl.db_berkeley ; \ sed -e "s#/usr/local/share/opensips#$(data_target)#g" \ < $(SCRIPTS_DIR)/opensipsdbctl.db_berkeley > /tmp/opensipsdbctl.db_berkeley ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsdbctl.db_berkeley ; \ $(INSTALL_CFG) /tmp/opensipsdbctl.db_berkeley $(modules_prefix)/$(lib_dir)/opensipsctl/ ; \ rm -fr /tmp/opensipsdbctl.db_berkeley ; \ mkdir -p $(data_prefix)/$(data_dir)/db_berkeley/opensips ; \ for FILE in $(wildcard $(SCRIPTS_DIR)/db_berkeley/opensips/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/db_berkeley/opensips/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/db_berkeley/opensips/`basename "$$FILE"` ; \ fi ;\ done ; \ $(INSTALL_BIN) $(UTILS_DIR)/db_berkeley/bdb_recover $(bin_prefix)/$(bin_dir) ; \ opensips-2.2.2/modules/db_berkeley/README000066400000000000000000000377241300170765700201770ustar00rootroot00000000000000Berkeley DB Module Will Quan Cisco Systems Edited by Will Quan Copyright © 2007 Cisco Systems Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. auto_reload (integer) 1.3.2. log_enable (integer) 1.3.3. journal_roll_interval (integer seconds) 1.4. Exported Functions 1.5. Exported MI Functions 1.5.1. bdb_reload 1.6. Installation and Running 1.7. Database Schema and Metadata 1.8. METADATA_COLUMNS (required) 1.9. METADATA_KEYS (required) 1.10. METADATA_READONLY (optional) 1.11. METADATA_LOGFLAGS (optional) 1.12. DB Maintaince Script : opensipsdbctl 1.13. DB Recovery : bdb_recover 1.14. Known Limitations List of Examples 1.1. Set auto_reload parameter 1.2. Set log_enable parameter 1.3. Set journal_roll_interval parameter 1.4. METADATA_COLUMNS 1.5. contents of version table 1.6. METADATA_COLUMNS 1.7. METADATA_KEYS 1.8. METADATA_LOGFLAGS 1.9. opensipsdbctl 1.10. bdb_recover usage Chapter 1. Admin Guide 1.1. Overview This is a module which integrates the Berkeley DB into OpenSIPS. It implements the DB API defined in OpenSIPS. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * Berkeley Berkeley DB 4.6 - an embedded database. 1.3. Exported Parameters 1.3.1. auto_reload (integer) The auto-reload will close and reopen a Berkeley DB when the files inode has changed. The operation occurs only duing a query. Other operations such as insert or delete, do not invoke auto_reload. Default value is 0 (1 - on / 0 - off). Example 1.1. Set auto_reload parameter ... modparam("db_berkeley", "auto_reload", 1) ... 1.3.2. log_enable (integer) The log_enable boolean controls when to create journal files. The following operations can be journaled: INSERT, UPDATE, DELETE. Other operations such as SELECT, do not. This journaling are required if you need to recover from a corrupt DB file. That is, bdb_recover requires these to rebuild the db file. If you find this log feature useful, you may also be interested in the METADATA_LOGFLAGS bitfield that each table has. It will allow you to control which operations to journal, and the destination (like syslog, stdout, local-file). Refer to bdblib_log() and documentation on METADATA. Default value is 0 (1 - on / 0 - off). Example 1.2. Set log_enable parameter ... modparam("db_berkeley", "log_enable", 1) ... 1.3.3. journal_roll_interval (integer seconds) The journal_roll_interval will close and open a new log file. The roll operation occurs only at the end of writing a log, so it is not guaranteed to to roll 'on time'. Default value is 0 (off). Example 1.3. Set journal_roll_interval parameter ... modparam("db_berkeley", "journal_roll_interval", 3600) ... 1.4. Exported Functions No function exported to be used from configuration file. 1.5. Exported MI Functions 1.5.1. bdb_reload Causes db_berkeley module to re-read the contents of specified table (or dbenv). The db_berkeley DB actually loads each table on demand, as opposed to loading all at mod_init time. The bdb_reload operation is implemented as a close followed by a reopen. Note- bdb_reload will fail if a table has not been accessed before (because the close will fail). Name: bdb_reload Parameters: tablename (or db_path); to reload a particular table provide the tablename as the arguement (eg subscriber); to reload all tables provide the db_path to the db files. The path can be found in opensipsctlrc DB_PATH variable. 1.6. Installation and Running First download, compile and install the Berkeley DB. This is outside the scope of this document. Documentation for this procedure is available on the Internet. Next, prepare to compile OpenSIPS with the db_berkeley module. In the directory /modules/db_berkeley, modify the Makefile to point to your distribution of Berkeley DB. You may also define 'BDB_EXTRA_DEBUG' to compile in extra debug logs. However, it is not a recommended deployment to production servers. Because the module dependes on an external library, the db_berkeley module is not compiled and installed by default. You can use one of the next options. * edit the "Makefile" and remove "db_berkeley" from "excluded_modules" list. Then follow the standard procedure to install OpenSIPS: "make all; make install". * from command line use: 'make all include_modules="db_berkeley"; make install include_modules="db_berkeley"'. Installation of OpenSIPS is performed by simply running make install as root user of the main directory. This will install the binaries in /usr/local/sbin/. If this was successful, OpenSIPS control engine files should now be installed as /usr/local/sbin/opensipsdbctl. Decide where (on the filesystem) you want to install the Berkeley DB files. For instance, '/usr/local/etc/opensips/db_berkeley' directory. Make note of this directory as we need to add this path to the opensipsctlrc file. Note: OpenSIPS will not startup without these DB files. Edit opensipsctlrc - There are two parameters in this file that should be configured before opensipsctldb script can work properly: DBENGINE and DB_PATH. Edit file: '/usr/local/etc/opensips/opensipsctlrc' ## database type: MYSQL, PGSQL, DB_BERKELEY, or DBTEXT, by default none is loaded # DBENGINE=DB_BERKELEY ## database path used by dbtext or db_berkeley # DB_PATH="/usr/local/etc/opensips/db_berkeley" (Optional) Pre creation step- Customize your meta-data. The DB files are initially seeded with necessary meta-data. This is a good time to review the meta-data section details, before making modifications to your tables dbschema. By default, the files are installed in '/usr/local/share/opensips/db_berkeley/opensips' By default these tables are created Read/Write and without any journalling as shown. These settings can be modified on a per table basis. Note: If you plan to use bdb_recover, you must change the LOGFLAGS. METADATA_READONLY 0 METADATA_LOGFLAGS 0 Execute opensipsdbctl - There are three (3) groups of tables you may need depending on your situation. opensipsdbctl create (required) opensipsdbctl presence (optional) opensipsdbctl extra (optional) Modify the OpenSIPS configuration file to use db_berkeley module. The database URL for modules must be the path to the directory where the Berkeley DB table-files are located, prefixed by "berkeley://", e.g., "berkeley:///usr/local/etc/opensips/db_berkeley". A couple other IMPORTANT things to consider are the 'db_mode' and the 'use_domain' modparams. The description of these parameters are found in usrloc documentation. Note on db_mode- The db_berkeley module will only journal the moment usrloc writes back to the DB. The safest mode is mode 3 , since the db_berkeley journal files will always be up-to-date. The main point is the db_mode vs. recovery by journal file interaction. Writing journal entries is 'best effort'. So if the hard drive becomes full, the attempt to write a journal entry may fail. Note on use_domain- The db_berkeley module will attempt natural joins when performing a query. This is basically a lexigraphical string compare using the keys provided. In most places in the db_berkeley dbschema (unless you customize), the domainname is identified as a natural key. Consider an example where use_domain = 0. In table subscriber, the db will be keying on 'username|NULL' because the default value will be used when that key column is not provided. This effectivly means that later queries must consistently use the username (w.o domain) in order to find a result to that particular subscriber query. The main point is 'use_domain' can not be changed once the db_berkeley is setup. 1.7. Database Schema and Metadata All Berkeley DB tables are created via the opensipsdbctl script. This section provides details as to the content and format of the DB file upon creation. Since the Berkeley DB stores key value pairs, the database is seeded with a few meta-data rows . The keys to these rows must begin with 'METADATA'. Here is an example of table meta-data, taken from the table 'version'. Note on reserved character- The '|' pipe character is used as a record delimiter within the Berkeley DB implementation and must not be present in any DB field. Example 1.4. METADATA_COLUMNS METADATA_COLUMNS table_name(str) table_version(int) METADATA_KEY 0 In the above example, the row METADATA_COLUMNS defines the column names and type, and the row METADATA_KEY defines which column(s) form the key. Here the value of 0 indicates that column 0 is the key(ie table_name). With respect to column types, the db_berkeley modules only has the following types: string, str, int, double, and datetime. The default type is string, and is used when one of the others is not specified. The columns of the meta-data are delimited by whitespace. The actual column data is stored as a string value, and delimited by the '|' pipe character. Since the code tokenizes on this delimiter, it is important that this character not appear in any valid data field. The following is the output of the 'db_berkeley.sh dump version' command. It shows contents of table 'version' in plain text. Example 1.5. contents of version table VERSION=3 format=print type=hash h_nelem=21 db_pagesize=4096 HEADER=END METADATA_READONLY 1 address| address|3 aliases| aliases|1004 dbaliases| dbaliases|1 domain| domain|1 speed_dial| speed_dial|2 subscriber| subscriber|6 uri| uri|1 METADATA_COLUMNS table_name(str) table_version(int) METADATA_KEY 0 acc| acc|4 grp| grp|2 location| location|1004 missed_calls| missed_calls|3 re_grp| re_grp|1 silo| silo|5 trusted| trusted|4 usr_preferences| usr_preferences|2 DATA=END 1.8. METADATA_COLUMNS (required) The METADATA_COLUMNS row contains the column names and types. Each is space delimited. Here is an example of the data taken from table subscriber : Example 1.6. METADATA_COLUMNS METADATA_COLUMNS username(str) domain(str) password(str) ha1(str) ha1b(str) first_name(st r) last_name(str) email_address(str) datetime_created(datetime) timezone (str) rpid(str) Related (hardcoded) limitations: * maximum of 32 columns per table. * maximum tablename size is 64. * maximum data length is 2048 Currently supporting these five types: str, datetime, int, double, string. 1.9. METADATA_KEYS (required) The METADATA_KEYS row indicates the indexes of the key columns, with respect to the order specified in METADATA_COLUMNS. Here is an example taken from table subscriber that brings up a good point: Example 1.7. METADATA_KEYS METADATA_KEY 0 1 The point is that both the username and domain name are require as the key to this record. Thus, usrloc modparam use_domain = 1 must be set for this to work. 1.10. METADATA_READONLY (optional) The METADATA_READONLY row contains a boolean 0 or 1. By default, its value is 0. On startup the DB will open initially as read-write (loads metadata) and then if this is set=1, it will close and reopen as read only (ro). I found this useful because readonly has impacts on the internal db locking etc. 1.11. METADATA_LOGFLAGS (optional) The METADATA_LOGFLAGS row contains a bitfield that customizes the journaling on a per table basis. If not present the default value is taken as 0. Here are the masks so far (taken from bdb_lib.h): Example 1.8. METADATA_LOGFLAGS #define JLOG_NONE 0 #define JLOG_INSERT 1 #define JLOG_DELETE 2 #define JLOG_UPDATE 4 #define JLOG_STDOUT 8 #define JLOG_SYSLOG 16 This means that if you want to journal INSERTS to local file and syslog the value should be set to 1+16=17. Or if you do not want to journal at all, set this to 0. 1.12. DB Maintaince Script : opensipsdbctl Use the opensipsdbctl script for maintaining OpenSIPS Berkeley DB tables. This script assumes you have DBENGINE and DB_PATH setup correctly in opensipsctlrc. Note Unsupported commands are- backup, restore, migrate, copy, serweb. Example 1.9. opensipsdbctl usage: opensipsdbctl create opensipsdbctl presence opensipsdbctl extra opensipsdbctl drop opensipsdbctl reinit opensipsdbctl bdb list (lists the underlying db files in DB_PATH) opensipsdbctl bdb cat db (prints the contents of db file to STDOUT in plain-text) opensipsdbctl bdb swap db (installs db.new by db -> db.old; db.new -> db) opensipsdbctl bdb append db datafile (appends data to a new in stance of db; output DB_PATH/db.new) opensipsdbctl bdb newappend db datafile (appends data to a new in stance of db; output DB_PATH/db.new) 1.13. DB Recovery : bdb_recover The db_berkeley module uses the Concurrent Data Store (CDS) architecture. As such, no transaction or journaling is provided by the DB natively. The application bdb_recover is specifically written to recover data from journal files that OpenSIPS creates. The bdb_recover application requires an additional text file that contains the table schema. The schema is loaded with the '-s' option and is required for all operations. Provide the path to the db_berkeley plain-text schema files. By default, these install to '/usr/local/share/opensips/db_berkeley/opensips/'. The '-h' home option is the DB_PATH path. Unlike the Berkeley utilities, this application does not look for the DB_PATH environment variable, so you have to specify it. If not specified, it will assume the current working directory. The last argument is the operation. There are fundamentally only two operations- create and recover. The following illustrates the four operations available to the administrator. Example 1.10. bdb_recover usage usage: ./bdb_recover -s schemadir [-h home] [-c tablename] This will create a brand new DB file with metadata. usage: ./bdb_recover -s schemadir [-h home] [-C all] This will create all the core tables, each with metadata. usage: ./bdb_recover -s schemadir [-h home] [-r journal-file] This will rebuild a DB and populate it with operation from journ al-file. The table name is embedded in the journal-file name by conventio n. usage: ./bdb_recover -s schemadir [-h home] [-R lastN] This will iterate over all core tables enumerated. If journal fi les exist in 'home', a new DB file will be created and populated with the data found in the last N files. The files are 'replayed' in chronological order (oldest to newes t). This allows the administrator to rebuild the db with a subset of all possible operations if needed. For example, you may only be interested in the last hours data in table location. Important note- A corrupted DB file must be moved out of the way before bdb_recover is executed. 1.14. Known Limitations The Berkeley DB does not nativly support an autoincrement (or sequence) mechanism. Consequently, this version does not support surragate keys in dbschema. These are the id columns in the tables. opensips-2.2.2/modules/db_berkeley/bdb_lib.c000066400000000000000000000632311300170765700210300ustar00rootroot00000000000000/* * db_berkeley module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #include #include #include #include #include #include "../../ut.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "bdb_util.h" #include "bdb_lib.h" #include "bdb_val.h" static database_p *_cachedb = NULL; static db_parms_p _db_parms = NULL; /** * */ int bdblib_init(db_parms_p _p) { if (!_cachedb) { _cachedb = pkg_malloc( sizeof(database_p) ); if (!_cachedb) { LM_CRIT("not enough private memory\n"); return -1; } *_cachedb = NULL; /*create default parms*/ db_parms_p dp = (db_parms_p) pkg_malloc( sizeof(db_parms_t) ); if (!dp) { LM_CRIT("not enough private memory\n"); return -1; } if(_p) { dp->cache_size = _p->cache_size; dp->auto_reload = _p->auto_reload; dp->log_enable = _p->log_enable; dp->journal_roll_interval = _p->journal_roll_interval; } else { dp->cache_size = (4 * 1024 * 1024); //4Mb dp->auto_reload = 0; dp->log_enable = 0; dp->journal_roll_interval = 3600; } _db_parms = dp; } return 0; } /** * close all DBs and then the DBENV; free all memory */ int bdblib_destroy(void) { if (_cachedb) db_free(*_cachedb); if(_db_parms) pkg_free(_db_parms); return 0; } /** closes the underlying Berkeley DB. assumes the lib data-structures are already initialzed; used to sync and reload the db file. */ int bdblib_close(char* _n) { str s; int rc; tbl_cache_p _tbc; DB* _db = NULL; DB_ENV* _env = NULL; database_p _db_p = *_cachedb; if (!_cachedb || !_n) return -1; rc = 0; s.s = (char*)_n; s.len = strlen(_n); if (_db_p) { _env = _db_p->dbenv; _tbc = _db_p->tables; LM_DBG("ENV %.*s \n" , _db_p->name.len , _db_p->name.s); if(s.len == _db_p->name.len && !strncasecmp(s.s, _db_p->name.s, _db_p->name.len)) { //close the whole dbenv LM_DBG("ENV %.*s \n", s.len, s.s); while(_tbc) { if(_tbc->dtp) { lock_get(&_tbc->dtp->sem); _db = _tbc->dtp->db; if(_db) rc = _db->close(_db, 0); if(rc != 0) LM_CRIT("error closing %s\n", _tbc->dtp->name.s); _tbc->dtp->db = NULL; lock_release(&_tbc->dtp->sem); } _tbc = _tbc->next; } _env->close(_env, 0); _db_p->dbenv = NULL; return 0; } //close a particular db while(_tbc) { if(_tbc->dtp) { LM_DBG("checking DB %.*s \n" , _tbc->dtp->name.len , _tbc->dtp->name.s); if(_tbc->dtp->name.len == s.len && !strncasecmp(_tbc->dtp->name.s, s.s, s.len )) { LM_DBG("DB %.*s \n", s.len, s.s); lock_get(&_tbc->dtp->sem); _db = _tbc->dtp->db; if(_db) rc = _db->close(_db, 0); if(rc != 0) LM_CRIT("error closing %s\n", _tbc->dtp->name.s); _tbc->dtp->db = NULL; lock_release(&_tbc->dtp->sem); return 0; } } _tbc = _tbc->next; } } LM_DBG("DB not found %.*s \n", s.len, s.s); return 1; /*table not found*/ } /** opens the underlying Berkeley DB. assumes the lib data-structures are already initialzed; used to sync and reload the db file. */ int bdblib_reopen(char* _n) { str s; int rc, flags; tbl_cache_p _tbc; DB* _db = NULL; DB_ENV* _env = NULL; database_p _db_p = *_cachedb; rc = flags = 0; _tbc = NULL; if (!_cachedb || !_n) return -1; s.s = (char*)_n; s.len = strlen(_n); if (_db_p) { _env = _db_p->dbenv; _tbc = _db_p->tables; if(s.len ==_db_p->name.len && !strncasecmp(s.s, _db_p->name.s,_db_p->name.len)) { //open the whole dbenv LM_DBG("-- bdblib_reopen ENV %.*s \n", s.len, s.s); if(!_db_p->dbenv) { rc = bdblib_create_dbenv(&_env, _n); _db_p->dbenv = _env; } if(rc!=0) return rc; _env = _db_p->dbenv; _tbc = _db_p->tables; while(_tbc) { if(_tbc->dtp) { lock_get(&_tbc->dtp->sem); if(!_tbc->dtp->db) { if ((rc = db_create(&_db, _env, 0)) != 0) { _env->err(_env, rc, "db_create"); LM_CRIT("error in db_create, db error: %s.\n",db_strerror(rc)); bdblib_recover(_tbc->dtp, rc); } } if ((rc = _db->open(_db, NULL, _n, NULL, DB_HASH, DB_CREATE, 0664)) != 0) { _db->dbenv->err(_env, rc, "DB->open: %s", _n); LM_CRIT("error in db_open: %s.\n",db_strerror(rc)); bdblib_recover(_tbc->dtp, rc); } _tbc->dtp->db = _db; lock_release(&_tbc->dtp->sem); } _tbc = _tbc->next; } _env->close(_env, 0); return rc; } //open a particular db while(_tbc) { if(_tbc->dtp) { LM_DBG("checking DB %.*s \n" , _tbc->dtp->name.len , _tbc->dtp->name.s); if(_tbc->dtp->name.len == s.len && !strncasecmp(_tbc->dtp->name.s, s.s, s.len )) { LM_DBG("DB %.*s \n", s.len, s.s); lock_get(&_tbc->dtp->sem); if(!_tbc->dtp->db) { if ((rc = db_create(&_db, _env, 0)) != 0) { _env->err(_env, rc, "db_create"); LM_CRIT("error in db_create, db error: %s.\n",db_strerror(rc)); bdblib_recover(_tbc->dtp, rc); } } if ((rc = _db->open(_db, NULL, _n, NULL, DB_HASH, DB_CREATE, 0664)) != 0) { _db->dbenv->err(_env, rc, "DB->open: %s", _n); LM_CRIT("bdb open: %s.\n",db_strerror(rc)); bdblib_recover(_tbc->dtp, rc); } _tbc->dtp->db = _db; lock_release(&_tbc->dtp->sem); return rc; } } _tbc = _tbc->next; } } LM_DBG("DB not found %.*s \n", s.len, s.s); return 1; /*table not found*/ } /** * */ int bdblib_create_dbenv(DB_ENV **_dbenv, char* _home) { DB_ENV *env; char *progname; int rc, flags; progname = "opensips"; /* Create an environment and initialize it for additional error * reporting. */ if ((rc = db_env_create(&env, 0)) != 0) { LM_ERR("db_env_create failed! bdb error: %s.\n", db_strerror(rc)); return (rc); } env->set_errpfx(env, progname); /* Specify the shared memory buffer pool cachesize */ if ((rc = env->set_cachesize(env, 0, _db_parms->cache_size, 0)) != 0) { LM_ERR("dbenv set_cachsize failed! bdb error: %s.\n", db_strerror(rc)); env->err(env, rc, "set_cachesize"); goto err; } /* Concurrent Data Store flags */ flags = DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL | DB_THREAD; /* Transaction Data Store flags ; not supported yet */ /* flags = DB_CREATE | DB_RECOVER | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_MPOOL | DB_THREAD | DB_INIT_TXN; */ /* Open the environment */ if ((rc = env->open(env, _home, flags, 0)) != 0) { LM_ERR("dbenv is not initialized! bdb error: %s.\n",db_strerror(rc)); env->err(env, rc, "environment open: %s", _home); goto err; } *_dbenv = env; return (0); err: (void)env->close(env, 0); return (rc); } /** */ database_p bdblib_get_db(str *_s) { int rc; database_p _db_p=NULL; char name[512]; if(!_s || !_s->s || _s->len<=0 || _s->len > 512) return NULL; if( !_cachedb) { LM_ERR("db_berkeley cache is not initialized! Check if you loaded db_berkeley " "before any other module that uses it.\n"); return NULL; } _db_p = *_cachedb; if(_db_p) { LM_DBG("db already cached!\n"); return _db_p; } if(!bdb_is_database(_s)) { LM_ERR("database [%.*s] does not exists!\n" ,_s->len , _s->s); return NULL; } _db_p = (database_p)pkg_malloc(sizeof(database_t)); if(!_db_p) { LM_ERR("no private memory for dbenv_t.\n"); pkg_free(_db_p); return NULL; } _db_p->name.s = (char*)pkg_malloc(_s->len*sizeof(char)); memcpy(_db_p->name.s, _s->s, _s->len); _db_p->name.len = _s->len; strncpy(name, _s->s, _s->len); name[_s->len] = 0; if ((rc = bdblib_create_dbenv(&(_db_p->dbenv), name)) != 0) { LM_ERR("bdblib_create_dbenv failed"); pkg_free(_db_p->name.s); pkg_free(_db_p); return NULL; } _db_p->tables=NULL; *_cachedb = _db_p; return _db_p; } /** * look thru a linked list for the table. if dne, create a new one * and add to the list */ tbl_cache_p bdblib_get_table(database_p _db, str *_s) { tbl_cache_p _tbc = NULL; table_p _tp = NULL; if(!_db || !_s || !_s->s || _s->len<=0) return NULL; if(!_db->dbenv) { return NULL; } _tbc = _db->tables; while(_tbc) { if(_tbc->dtp) { if(_tbc->dtp->name.len == _s->len && !strncasecmp(_tbc->dtp->name.s, _s->s, _s->len )) { return _tbc; } } _tbc = _tbc->next; } _tbc = (tbl_cache_p)pkg_malloc(sizeof(tbl_cache_t)); if(!_tbc) return NULL; if(!lock_init(&_tbc->sem)) { pkg_free(_tbc); return NULL; } _tp = bdblib_create_table(_db, _s); #ifdef BDB_EXTRA_DEBUG LM_DBG("table: %.*s\n", _s->len, _s->s); #endif if(!_tp) { LM_ERR("failed to create table.\n"); pkg_free(_tbc); return NULL; } lock_get(&_tbc->sem); _tbc->dtp = _tp; if(_db->tables) (_db->tables)->prev = _tbc; _tbc->next = _db->tables; _db->tables = _tbc; lock_release(&_tbc->sem); return _tbc; } void bdblib_log(int op, table_p _tp, char* _msg, int len) { if(!_tp || !len) return; if(! _db_parms->log_enable) return; if (_tp->logflags == JLOG_NONE) return; if ((_tp->logflags & op) == op) { int op_len=7; char buf[MAX_ROW_SIZE + op_len]; char *c; time_t now = time(NULL); if( _db_parms->journal_roll_interval) { if((_tp->t) && (now - _tp->t) > _db_parms->journal_roll_interval) { /*try to roll logfile*/ if(bdblib_create_journal(_tp)) { LM_ERR("Journaling has FAILED !\n"); return; } } } c = buf; switch (op) { case JLOG_INSERT: strncpy(c, "INSERT|", op_len); break; case JLOG_UPDATE: strncpy(c, "UPDATE|", op_len); break; case JLOG_DELETE: strncpy(c, "DELETE|", op_len); break; } c += op_len; strncpy(c, _msg, len); c +=len; *c = '\n'; c++; *c = '\0'; if ((_tp->logflags & JLOG_STDOUT) == JLOG_STDOUT) puts(buf); if ((_tp->logflags & JLOG_SYSLOG) == JLOG_SYSLOG) syslog(LOG_LOCAL6, "%s", buf); if(_tp->fp) { if(!fputs(buf, _tp->fp) ) fflush(_tp->fp); } } } /** * The function is called to create a handle to a db table. * * On startup, we do not create any of the db handles. * Instead it is done on first-use (lazy-initialized) to only create handles to * files (db) that we require. * * There is one db file per opensips table (eg. acc), and they should exist * in your DB_PATH (refer to opensipsctlrc) directory. * * This function does _not_ create the underlying binary db tables. * Creating the tables MUST be manually performed before * opensips startup by 'opensipsdbctl create' * * Function returns NULL on error, which will cause opensips to exit. * */ table_p bdblib_create_table(database_p _db, str *_s) { int rc,i,flags; DB *bdb = NULL; table_p tp = NULL; char tblname[MAX_TABLENAME_SIZE]; if(!_db || !_db->dbenv) { LM_ERR("no database_p or dbenv.\n"); return NULL; } tp = (table_p)pkg_malloc(sizeof(table_t)); if(!tp) { LM_ERR("no private memory for table_t.\n"); return NULL; } if ((rc = db_create(&bdb, _db->dbenv, 0)) != 0) { _db->dbenv->err(_db->dbenv, rc, "database create"); LM_ERR("error in db_create, bdb error: %s.\n",db_strerror(rc)); pkg_free(tp); return NULL; } memset(tblname, 0, MAX_TABLENAME_SIZE); strncpy(tblname, _s->s, _s->len); #ifdef BDB_EXTRA_DEBUG LM_DBG("CREATE TABLE = %s\n", tblname); #endif flags = DB_THREAD; if ((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664)) != 0) { _db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname); LM_ERR("bdb open failed: %s.\n",db_strerror(rc)); pkg_free(tp); return NULL; } if(!lock_init(&tp->sem)) { goto error; } tp->name.s = (char*)pkg_malloc(_s->len*sizeof(char)); memcpy(tp->name.s, _s->s, _s->len); tp->name.len = _s->len; tp->db=bdb; tp->ncols=0; tp->nkeys=0; tp->ro=0; /*0=ReadWrite ; 1=ReadOnly*/ tp->ino=0; /*inode*/ tp->logflags=0; /*bitmap; 4=Delete, 2=Update, 1=Insert, 0=None*/ tp->fp=0; tp->t=0; for(i=0;icolp[i] = NULL; /*load metadata; seeded\db_loaded when database are created*/ /*initialize columns with metadata*/ rc = load_metadata_columns(tp); if(rc!=0) { LM_ERR("FAILED to load METADATA COLS in table: %s.\n", tblname); goto error; } /*initialize columns default values from metadata*/ rc = load_metadata_defaults(tp); if(rc!=0) { LM_ERR("FAILED to load METADATA DEFAULTS in table: %s.\n", tblname); goto error; } rc = load_metadata_keys(tp); if(rc!=0) { LM_ERR("FAILED to load METADATA KEYS in table: %s.\n", tblname); /*will have problems later figuring column types*/ goto error; } /*opened RW by default; Query to set the RO flag */ rc = load_metadata_readonly(tp); if(rc!=0) { LM_INFO("No METADATA_READONLY in table: %s.\n", tblname); /*non-critical; table will default to READWRITE*/ } if(tp->ro) { /*schema defines this table RO readonly*/ #ifdef BDB_EXTRA_DEBUG LM_DBG("TABLE %.*s is changing to READONLY mode\n" , tp->name.len, tp->name.s); #endif if ((rc = bdb->close(bdb,0)) != 0) { _db->dbenv->err(_db->dbenv, rc, "DB->close: %s", tblname); LM_ERR("bdb close: %s.\n",db_strerror(rc)); goto error; } bdb = NULL; if ((rc = db_create(&bdb, _db->dbenv, 0)) != 0) { _db->dbenv->err(_db->dbenv, rc, "database create"); LM_ERR("error in db_create.\n"); goto error; } flags = DB_THREAD | DB_RDONLY; if ((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664)) != 0) { _db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname); LM_ERR("bdb open: %s.\n",db_strerror(rc)); goto error; } tp->db=bdb; } /* set the journaling flags; flags indicate which operations need to be journalled. (e.g possible to only journal INSERT.) */ rc = load_metadata_logflags(tp); if(rc!=0) LM_INFO("No METADATA_LOGFLAGS in table: %s.\n", tblname); if ((tp->logflags & JLOG_FILE) == JLOG_FILE) bdblib_create_journal(tp); return tp; error: if(tp) { pkg_free(tp->name.s); pkg_free(tp); } return NULL; } int bdblib_create_journal(table_p _tp) { char *s; char fn[1024]; char d[64]; FILE *fp = NULL; struct tm *t; int bl; database_p _db_p = *_cachedb; time_t tim = time(NULL); if(! _db_p || ! _tp) return -1; if(! _db_parms->log_enable) return 0; /* journal filename ; e.g. '/var/opensips/db/location-YYYYMMDDhhmmss.jnl' */ s=fn; strncpy(s, _db_p->name.s, _db_p->name.len); s+=_db_p->name.len; *s = '/'; s++; strncpy(s, _tp->name.s, _tp->name.len); s+=_tp->name.len; t = localtime( &tim ); bl=strftime(d,128,"-%Y%m%d%H%M%S.jnl",t); strncpy(s, d, bl); s+= bl; *s = 0; if(_tp->fp) { /* must be rolling. */ if( fclose(_tp->fp) ) { LM_ERR("Failed to Close Log in table: %.*s .\n", _tp->name.len, _tp->name.s); return -1; } } if( (fp = fopen(fn, "w")) != NULL ) { _tp->fp = fp; } else { LM_ERR("Failed to Open Log in table: %.*s .\n",_tp->name.len, _tp->name.s); return -1; } _tp->t = tim; return 0; } int load_metadata_columns(table_p _tp) { int ret,n,len; char dbuf[MAX_ROW_SIZE]; char *tmp; char *s = NULL; char cn[64], ct[16]; DB *db = NULL; DBT key, data; column_p col; ret = n = len = 0; if(!_tp || !_tp->db) return -1; if(_tp->ncols!=0) return 0; db = _tp->db; memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); memset(dbuf, 0, MAX_ROW_SIZE); key.data = METADATA_COLUMNS; key.size = strlen(METADATA_COLUMNS); /*memory for the result*/ data.data = dbuf; data.ulen = MAX_ROW_SIZE; data.flags = DB_DBT_USERMEM; if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) { db->err(db, ret, "load_metadata_columns DB->get failed"); LM_ERR("FAILED to find METADATA_COLUMNS in DB \n"); return -1; } /* eg: dbuf = "table_name(str) table_version(int)" */ LM_DBG("Found: [%s]\n",dbuf); tmp = dbuf; s = strsep(&tmp, " "); while(s!=NULL && nname.s = (char*)pkg_malloc(len * sizeof(char)); memcpy(col->name.s, cn, len ); col->name.len = len; /*set column type*/ if(strncmp(ct, "str", 3)==0) { col->type = DB_STRING; } else if(strncmp(ct, "int", 3)==0) { col->type = DB_INT; } else if (strncmp(ct, "number", 6)==0) { col->type = DB_BIGINT; } else if(strncmp(ct, "double", 6)==0) { col->type = DB_DOUBLE; } else if(strncmp(ct, "datetime", 8)==0) { col->type = DB_DATETIME; } else { col->type = DB_STRING; } col->flag = 0; _tp->colp[n] = col; n++; _tp->ncols++; s = strsep(&tmp, " "); } return 0; } int load_metadata_keys(table_p _tp) { int ret,n,ci; char dbuf[MAX_ROW_SIZE]; char *tmp; char *s = NULL; DB *db = NULL; DBT key, data; ret = n = ci = 0; if(!_tp || !_tp->db) return -1; db = _tp->db; memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); memset(dbuf, 0, MAX_ROW_SIZE); key.data = METADATA_KEY; key.size = strlen(METADATA_KEY); data.data = dbuf; data.ulen = MAX_ROW_SIZE; data.flags = DB_DBT_USERMEM; if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) { db->err(db, ret, "load_metadata_keys DB->get failed"); LM_ERR("FAILED to find METADATA in table \n"); return ret; } LM_DBG("Found: [%s]\n",dbuf); tmp = dbuf; s = strsep(&tmp, " "); while(s!=NULL && strlen(s) && n< _tp->ncols) { ret = sscanf(s,"%i", &ci); if(ret != 1) return -1; if( _tp->colp[ci] ) { _tp->colp[ci]->flag = 1; _tp->nkeys++; } n++; s = strsep(&tmp, " "); } return 0; } int load_metadata_readonly(table_p _tp) { int i, ret; char dbuf[MAX_ROW_SIZE]; DB *db = NULL; DBT key, data; i = 0; if(!_tp || !_tp->db) return -1; db = _tp->db; memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); memset(dbuf, 0, MAX_ROW_SIZE); key.data = METADATA_READONLY; key.size = strlen(METADATA_READONLY); data.data = dbuf; data.ulen = MAX_ROW_SIZE; data.flags = DB_DBT_USERMEM; if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) { return ret; } if( 1 == sscanf(dbuf,"%i", &i) ) _tp->ro=(i>0)?1:0; return 0; } int load_metadata_logflags(table_p _tp) { int i, ret; char dbuf[MAX_ROW_SIZE]; DB *db = NULL; DBT key, data; i = 0; if(!_tp || !_tp->db) return -1; db = _tp->db; memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); memset(dbuf, 0, MAX_ROW_SIZE); key.data = METADATA_LOGFLAGS; key.size = strlen(METADATA_LOGFLAGS); data.data = dbuf; data.ulen = MAX_ROW_SIZE; data.flags = DB_DBT_USERMEM; if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) { return ret; } if( 1 == sscanf(dbuf,"%i", &i) ) _tp->logflags=i; return 0; } int load_metadata_defaults(table_p _tp) { int ret,n,len; char dbuf[MAX_ROW_SIZE]; char * tmp; char *s = NULL; char cv[512]; DB *db = NULL; DBT key, data; column_p col; ret = n = len = 0; if(!_tp || !_tp->db) return -1; db = _tp->db; memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); memset(dbuf, 0, MAX_ROW_SIZE); key.data = METADATA_DEFAULTS; key.size = strlen(METADATA_DEFAULTS); /*memory for the result*/ data.data = dbuf; data.ulen = MAX_ROW_SIZE; data.flags = DB_DBT_USERMEM; if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) { #ifdef BDB_EXTRA_DEBUG LM_DBG("NO DEFAULTS ; SETTING ALL columns to NULL! \n" ); #endif /*no defaults in DB; make some up.*/ for(n=0; n<_tp->ncols; n++) { col = _tp->colp[n]; if( col ) { /*set all columns default value to 'NULL' */ len = strlen("NULL"); col->dv.s = (char*)pkg_malloc(len * sizeof(char)); memcpy(col->dv.s, "NULL", len); col->dv.len = len; } } return 0; } /* use the defaults provided*/ LM_DBG("Found: [%s]\n",dbuf); tmp = dbuf; s = strsep(&tmp, DELIM); while(s!=NULL && n< _tp->ncols) { strcpy(cv,s); col = _tp->colp[n]; if( col ) { /*set column default*/ len = strlen(s); col->dv.s = (char*)pkg_malloc(len * sizeof(char)); memcpy(col->dv.s, cv, len); col->dv.len = len; #ifdef BDB_EXTRA_DEBUG LM_DBG("COLUMN DEFAULT is %.*s for column[%.*s] \n" , col->dv.len , ZSW(col->dv.s) , col->name.len , ZSW(col->name.s) ); #endif } n++; s = strsep( &tmp, DELIM); } return 0; } /*creates a composite key _k of length _klen from n values of _v; provide your own initialized memory for target _k and _klen; resulting value: _k = "KEY1 | KEY2" ko = key only */ int bdblib_valtochar(table_p _tp, int* _lres, char* _k, int* _klen, db_val_t* _v, int _n, int _ko) { char *p; char sk[MAX_ROW_SIZE]; // subkey(sk) val char* delim = DELIM; char* cNULL = "NULL"; int len, total, sum; int i, j, k; p = _k; len = sum = total = 0; i = j = k = 0; if(!_tp) return -1; if(!_v || (_n<1) ) return -1; if(!_k || !_klen ) return -1; if( *_klen < 1) return -1; memset(sk, 0, MAX_ROW_SIZE); total = *_klen; *_klen = 0; //sum if(! _lres) { #ifdef BDB_EXTRA_DEBUG LM_DBG("schema has NOT specified any keys! \n"); #endif /* schema has not specified keys just use the provided data in order provided */ for(i=0;i<_n;i++) { len = total - sum; if ( bdb_val2str(&_v[i], sk, &len) != 0 ) { LM_ERR("error building composite key \n"); return -2; } sum += len; if(sum > total) { LM_ERR("Destination buffer too short for subval %s\n",sk); return -2; } /* write sk */ strncpy(p, sk, len); p += len; *_klen = sum; sum += DELIM_LEN; if(sum > total) { LM_ERR("Destination buffer too short for delim \n"); return -3; } /* write delim */ strncpy(p, delim, DELIM_LEN); p += DELIM_LEN; *_klen = sum;; } return 0; } /* schema has specified keys verify all schema keys are provided use 'NULL' for those that are missing. */ for(i=0; i<_tp->ncols; i++) { /* i indexes columns in schema order */ if( _ko) { /* keymode; skip over non-key columns */ if( ! _tp->colp[i]->flag) continue; } for(j=0; j<_n; j++) { /* j indexes the columns provided in _k which may be less than the total required by the schema. the app does not know the order of the columns in our schema! */ k = (_lres) ? _lres[j] : j; /* * k index will remap back to our schema order; like i */ if(i == k) { /* KEY was provided; append to buffer; _k[j] contains a key, but its a key that corresponds to column k of our schema. now we know its a match, and we dont need index k for anything else */ #ifdef BDB_EXTRA_DEBUG LM_DBG("KEY PROVIDED[%i]: %.*s.%.*s \n", i , _tp->name.len , ZSW(_tp->name.s) , _tp->colp[i]->name.len, ZSW(_tp->colp[i]->name.s) ); #endif len = total - sum; if ( bdb_val2str(&_v[j], sk, &len) != 0) { LM_ERR("Error while converting to str %s\n",sk); return -4; } sum += len; if(sum > total) { LM_ERR("Destination buffer too short for subval %s\n",sk); return -5; } strncpy(p, sk, len); p += len; *_klen = sum; sum += DELIM_LEN; if(sum > total) { LM_ERR("Destination buffer too short for delim \n"); return -5; } /* append delim */ strncpy(p, delim, DELIM_LEN); p += DELIM_LEN; *_klen = sum; /* take us out of inner for loop and at the end of the outer loop to look for our next schema key */ goto next; } } /* NO KEY provided; use the column default value (dv) i.e _tp->colp[i]->dv */ #ifdef BDB_EXTRA_DEBUG LM_DBG("Missing KEY[%i]: %.*s.%.*s using default [%.*s] \n", i , _tp->name.len , ZSW(_tp->name.s) , _tp->colp[i]->name.len, ZSW(_tp->colp[i]->name.s) , _tp->colp[i]->dv.len , ZSW(_tp->colp[i]->dv.s) ); #endif len = _tp->colp[i]->dv.len; sum += len; if(sum > total) { LM_ERR("Destination buffer too short for subval %s\n",cNULL); return -5; } strncpy(p, _tp->colp[i]->dv.s, len); p += len; *_klen = sum; sum += DELIM_LEN; if(sum > total) { LM_ERR("Destination buffer too short for delim \n"); return -5; } strncpy(p, delim, DELIM_LEN); p += DELIM_LEN; *_klen = sum; next: continue; } return 0; } /** * */ int db_free(database_p _dbp) { tbl_cache_p _tbc = NULL, _tbc0=NULL; if(!_dbp) return -1; _tbc = _dbp->tables; while(_tbc) { _tbc0 = _tbc->next; tbl_cache_free(_tbc); _tbc = _tbc0; } if(_dbp->dbenv) _dbp->dbenv->close(_dbp->dbenv, 0); if(_dbp->name.s) pkg_free(_dbp->name.s); pkg_free(_dbp); return 0; } /** * */ int tbl_cache_free(tbl_cache_p _tbc) { if(!_tbc) return -1; lock_get(&_tbc->sem); if(_tbc->dtp) tbl_free(_tbc->dtp); lock_destroy(&_tbc->sem); pkg_free(_tbc); return 0; } /** * close DB (sync data to disk) and free mem */ int tbl_free(table_p _tp) { int i; if(!_tp) return -1; if(_tp->db) _tp->db->close(_tp->db, 0); if(_tp->fp) fclose(_tp->fp); if(_tp->name.s) pkg_free(_tp->name.s); for(i=0;i<_tp->ncols;i++) { if(_tp->colp[i]) { pkg_free(_tp->colp[i]->name.s); pkg_free(_tp->colp[i]->dv.s); pkg_free(_tp->colp[i]); } } pkg_free(_tp); return 0; } int bdblib_recover(table_p _tp, int _rc) { switch(_rc) { case DB_LOCK_DEADLOCK: LM_ERR("DB_LOCK_DEADLOCK detected !!\n"); case DB_RUNRECOVERY: LM_ERR("DB_RUNRECOVERY detected !! \n"); bdblib_destroy(); exit(1); break; } return 0; } opensips-2.2.2/modules/db_berkeley/bdb_lib.h000066400000000000000000000071541300170765700210370ustar00rootroot00000000000000/* * db_berkeley module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #ifndef _BDB_LIB_H_ #define _BDB_LIB_H_ #include #include #include #include #include "../../str.h" #include "../../db/db.h" #include "../../db/db_val.h" #include "../../locking.h" /*max number of columns in a table*/ #define MAX_NUM_COLS 32 /*max char width of a table row*/ #define MAX_ROW_SIZE 4096 /*max char width of a table name*/ #define MAX_TABLENAME_SIZE 64 #define METADATA_COLUMNS "METADATA_COLUMNS" #define METADATA_KEY "METADATA_KEY" #define METADATA_READONLY "METADATA_READONLY" #define METADATA_LOGFLAGS "METADATA_LOGFLAGS" #define METADATA_DEFAULTS "METADATA_DEFAULTS" /*journal logging flag masks */ #define JLOG_NONE 0 #define JLOG_INSERT 1 #define JLOG_DELETE 2 #define JLOG_UPDATE 4 #define JLOG_FILE 8 #define JLOG_STDOUT 16 #define JLOG_SYSLOG 32 #define DELIM "|" #define DELIM_LEN (sizeof(DELIM)-1) typedef db_val_t bdb_val_t, *bdb_val_p; typedef struct _row { bdb_val_p fields; struct _row *prev; struct _row *next; } row_t, *row_p; typedef struct _column { str name; str dv; /* default value */ int type; int flag; } column_t, *column_p; typedef struct _table { str name; DB *db; gen_lock_t sem; column_p colp [MAX_NUM_COLS]; int ncols; int nkeys; int ro; /*db readonly flag*/ int logflags; /*flags indication what-where to journal log */ FILE* fp; /*jlog file pointer */ time_t t; /*jlog creation time */ ino_t ino; } table_t, *table_p; typedef struct _tbl_cache { gen_lock_t sem; table_p dtp; struct _tbl_cache *prev; struct _tbl_cache *next; } tbl_cache_t, *tbl_cache_p; typedef struct _database { str name; DB_ENV *dbenv; tbl_cache_p tables; } database_t, *database_p; typedef struct _db_parms { u_int32_t cache_size; int auto_reload; int log_enable; int journal_roll_interval; } db_parms_t, *db_parms_p; int bdblib_init(db_parms_p _parms); int bdblib_destroy(void); int bdblib_close(char* _n); int bdblib_reopen(char* _n); int bdblib_recover(table_p _tp, int error_code); void bdblib_log(int op, table_p _tp, char* _msg, int len); int bdblib_create_dbenv(DB_ENV **dbenv, char* home); int bdblib_create_journal(table_p _tp); database_p bdblib_get_db(str *_s); tbl_cache_p bdblib_get_table(database_p _db, str *_s); table_p bdblib_create_table(database_p _db, str *_s); int db_free(database_p _dbp); int tbl_cache_free(tbl_cache_p _tbc); int tbl_free(table_p _tp); int load_metadata_columns(table_p _tp); int load_metadata_keys(table_p _tp); int load_metadata_readonly(table_p _tp); int load_metadata_logflags(table_p _tp); int load_metadata_defaults(table_p _tp); int bdblib_valtochar(table_p _tp, int* _lres, char* _k, int* _klen, db_val_t* _v, int _n, int _ko); #endif opensips-2.2.2/modules/db_berkeley/bdb_mi.c000066400000000000000000000031261300170765700206640ustar00rootroot00000000000000/* * db_berkeley MI functions * * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-11-05 created (wiquan) */ #include "../../dprint.h" #include "../../db/db.h" #include "db_berkeley.h" #include "bdb_mi.h" /* * MI function to reload db table or env * expects 1 node: the tablename or dbenv name to reload */ struct mi_root* mi_bdb_reload(struct mi_root *cmd, void *param) { struct mi_node *node; str *db_path; node = cmd->node.kids; if (node && node->next) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); db_path = &node->value; if (!db_path || db_path->len == 0) return init_mi_tree( 400, "bdb_reload missing db arg", 21); if (bdb_reload(db_path->s) == 0) { return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } else { return init_mi_tree( 500, "db_berkeley Reload Failed", 26); } } opensips-2.2.2/modules/db_berkeley/bdb_mi.h000066400000000000000000000020121300170765700206620ustar00rootroot00000000000000/* * Header file for db_berkeley MI functions * * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _BDB_MI_H_ #define _BDB_MI_H_ #include "../../mi/mi.h" #define MI_BDB_RELOAD "bdb_reload" struct mi_root* mi_bdb_reload(struct mi_root *cmd, void *param); #endif opensips-2.2.2/modules/db_berkeley/bdb_res.c000066400000000000000000000264551300170765700210620ustar00rootroot00000000000000/* * db_berkeley module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #include #include #include #include "../../mem/mem.h" #include "bdb_res.h" int bdb_get_columns(table_p _tp, db_res_t* _res, int* _lres, int _nc) { int col; if (!_res) { LM_ERR("invalid parameter\n"); return -1; } if (_nc < 0 ) { LM_ERR("_nc parameter cannot be negative \n"); return -1; } /* the number of rows (tuples) in the query result. */ RES_NUM_ROWS(_res) = 1; if (!_lres) _nc = _tp->ncols; /* Save number of columns in the result structure */ RES_COL_N(_res) = _nc; if (db_allocate_columns(_res, RES_COL_N(_res)) != 0) { LM_ERR("could not allocate columns"); return -2; } /* * For each column both the name and the data type are saved. */ for(col = 0; col < RES_COL_N(_res); col++) { column_p cp = NULL; cp = (_lres) ? _tp->colp[_lres[col]] : _tp->colp[col]; /* The pointer that is here returned is part of the result structure.*/ RES_NAMES(_res)[col]->s = cp->name.s; RES_NAMES(_res)[col]->len = cp->name.len; LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_res)[col] , col, RES_NAMES(_res)[col]->len, RES_NAMES(_res)[col]->s); RES_TYPES(_res)[col] = cp->type; } return 0; } /** * Convert rows from Berkeley DB to db API representation */ int bdb_convert_row(db_res_t* _res, char *bdb_result, int* _lres) { int col, len, i, j; char **row_buf, *s; col = len = i = j = 0; struct db_row* row = NULL; if (!_res) { LM_ERR("invalid parameter\n"); return -1; } /* Save the number of rows in the current fetch */ RES_ROW_N(_res) = 1; row = RES_ROWS(_res); /* Save the number of columns in the ROW structure */ ROW_N(row) = RES_COL_N(_res); /* * Allocate an array of pointers one per column. * It that will be used to hold the address of the string * representation of each column. */ len = sizeof(char *) * RES_COL_N(_res); row_buf = (char **)pkg_malloc(len); if (!row_buf) { LM_ERR("no private memory left\n"); return -1; } LM_DBG("allocate for %d columns %d bytes in row buffer at %p\n", RES_COL_N(_res), len, row_buf); memset(row_buf, 0, len); /*populate the row_buf with bdb_result*/ /*bdb_result is memory from our callers stack so we copy here*/ LM_DBG("Found: [%s]\n",bdb_result); s = strsep(&bdb_result, DELIM); while( s!=NULL) { if(_lres) { /*only requested cols (_c was specified)*/ for(i=0; i= RES_COL_N(_res)) break; len = strlen(s); row_buf[col] = pkg_malloc(len+1); if (!row_buf[col]) { LM_ERR("no private memory left\n"); return -1; } LM_DBG("allocated %d bytes for row_buf[%d] at %p\n", len, col, row_buf[col]); memset(row_buf[col], 0, len+1); strncpy(row_buf[col], s, len); } s = strsep(&bdb_result, DELIM); col++; } /*do the type conversion per col*/ for(col = 0; col < ROW_N(row); col++) { /*skip the unrequested cols (as already specified)*/ if(!row_buf[col]) continue; /* Convert the string representation into the value representation */ if (bdb_str2val(RES_TYPES(_res)[col], &(ROW_VALUES(row)[col]) , row_buf[col], strlen(row_buf[col])) < 0) { LM_ERR("while converting value\n"); goto error; } if( row->values[col].nul || row->values[col].type == DB_INT || row->values[col].type == DB_BIGINT || row->values[col].type == DB_DOUBLE || row->values[col].type == DB_DATETIME ) pkg_free(row_buf[col]); } LM_DBG("freeing row buffer at %p\n", row_buf); if( row_buf[col]) pkg_free(row_buf); row_buf = NULL; return 0; error: for(col = 0; col < ROW_N(row); col++) if( row_buf[col]) pkg_free(row_buf[col]); if( row_buf ) pkg_free(row_buf); return -1; } /*rx is row index*/ int bdb_append_row(db_res_t* _res, char *bdb_result, int* _lres, int _rx) { int col, len, i, j; char **row_buf, *s; db_row_t* row = NULL; col = len = i = j = 0; if (!_res) { LM_ERR("invalid parameter"); return -1; } row = &(RES_ROWS(_res)[_rx]); /* Save the number of columns in the ROW structure */ ROW_N(row) = RES_COL_N(_res); /* * Allocate an array of pointers one per column. * It that will be used to hold the address of the string representation of each column. */ len = sizeof(char *) * RES_COL_N(_res); row_buf = (char **)pkg_malloc(len); if (!row_buf) { LM_ERR("no private memory left\n"); return -1; } LM_DBG("allocate for %d columns %d bytes in row buffer at %p\n", RES_COL_N(_res), len, row_buf); memset(row_buf, 0, len); /*populate the row_buf with bdb_result*/ /*bdb_result is memory from our callers stack so we copy here*/ LM_DBG("Found: [%s]\n",bdb_result); s = strsep(&bdb_result, DELIM); while( s!=NULL) { if(_lres) { /*only requested cols (_c was specified)*/ for(i=0; i= RES_COL_N(_res)) break; len = strlen(s); #ifdef BDB_EXTRA_DEBUG LM_DBG("col[%i] = [%.*s]\n", col , len, s ); #endif LM_ERR("Allocated2 for %d\n",col); row_buf[col] = (char*)pkg_malloc(len+1); if (!row_buf[col]) { LM_ERR("no private memory left\n"); return -1; } memset(row_buf[col], 0, len+1); strncpy(row_buf[col], s, len); } s = strsep(&bdb_result, DELIM); col++; } /*do the type conversion per col*/ for(col = 0; col < ROW_N(row); col++) { #ifdef BDB_EXTRA_DEBUG LM_DBG("tc 1: col[%i] == ", col ); #endif /*skip the unrequested cols (as already specified)*/ if(!row_buf[col]) continue; #ifdef BDB_EXTRA_DEBUG LM_DBG("tc 2: col[%i] \n", col ); #endif /* Convert the string representation into the value representation */ if (bdb_str2val(RES_TYPES(_res)[col], &(ROW_VALUES(row)[col]) , row_buf[col], strlen(row_buf[col])) < 0) { LM_DBG("freeing row at %p\n", row); goto error; } if( row->values[col].nul || row->values[col].type == DB_INT || row->values[col].type == DB_BIGINT || row->values[col].type == DB_DOUBLE || row->values[col].type == DB_DATETIME ) pkg_free(row_buf[col]); } if( row_buf ) pkg_free(row_buf); row_buf = NULL; return 0; error: for(col = 0; col < ROW_N(row); col++) if( row_buf[col]) pkg_free(row_buf[col]); if( row_buf ) pkg_free(row_buf); return -1; } int* bdb_get_colmap(table_p _dtp, db_key_t* _k, int _n) { int i, j, *_lref=NULL; if(!_dtp || !_k || _n < 0) return NULL; _lref = (int*)pkg_malloc(_n*sizeof(int)); if(!_lref) return NULL; for(i=0; i < _n; i++) { for(j=0; j<_dtp->ncols; j++) { if(_k[i]->len==_dtp->colp[j]->name.len && !strncasecmp(_k[i]->s, _dtp->colp[j]->name.s, _dtp->colp[j]->name.len)) { _lref[i] = j; break; } } if(i>=_dtp->ncols) { LM_DBG("ERROR column <%.*s> not found\n", _k[i]->len, _k[i]->s); pkg_free(_lref); return NULL; } } return _lref; } int bdb_is_neq_type(db_type_t _t0, db_type_t _t1) { if(_t0 == _t1) return 0; switch(_t1) { case DB_INT: if(_t0==DB_BIGINT || _t0==DB_DATETIME || _t0==DB_BITMAP) return 0; case DB_BIGINT: if(_t0==DB_INT || _t0==DB_DATETIME || _t0==DB_BITMAP) return 0; case DB_DATETIME: if(_t0==DB_INT) return 0; if(_t0==DB_BITMAP) return 0; case DB_DOUBLE: break; case DB_STRING: if(_t0==DB_STR || _t0==DB_BLOB) return 0; case DB_STR: if(_t0==DB_STRING || _t0==DB_BLOB) return 0; case DB_BLOB: if(_t0==DB_STR || _t0==DB_STRING) return 0; case DB_BITMAP: if (_t0==DB_INT) return 0; } return 1; } /* */ int bdb_row_match(db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n, db_res_t* _r, int* _lkey ) { int i, res; db_row_t* row = NULL; if(!_r || !_lkey) return 1; row = RES_ROWS(_r); for(i=0; i<_n; i++) { res = bdb_cmp_val(&(ROW_VALUES(row)[_lkey[i]]), &_v[i]); if(!_op || !strcmp(_op[i], OP_EQ)) { if(res!=0) return 0; } else { if(!strcmp(_op[i], OP_LT)) { if(res!=-1) return 0; } else { if(!strcmp(_op[i], OP_GT)) { if(res!=1) return 0; } else { if(!strcmp(_op[i], OP_LEQ)) { if(res==1) return 0; } else { if(!strcmp(_op[i], OP_GEQ)) { if(res==-1) return 0; } else { return res; }}}}} } return 1; } /* */ int bdb_cmp_val(db_val_t* _vp, db_val_t* _v) { int _l, _n; if(!_vp && !_v) return 0; if(!_v) return 1; if(!_vp) return -1; if(_vp->nul && _v->nul) return 0; if(_v->nul) return 1; if(_vp->nul) return -1; switch(VAL_TYPE(_v)) { case DB_INT: return (_vp->val.int_val<_v->val.int_val)?-1: (_vp->val.int_val>_v->val.int_val)?1:0; case DB_BIGINT: return (_vp->val.bigint_val<_v->val.bigint_val)?-1: (_vp->val.bigint_val>_v->val.bigint_val)?1:0; case DB_DOUBLE: return (_vp->val.double_val<_v->val.double_val)?-1: (_vp->val.double_val>_v->val.double_val)?1:0; case DB_DATETIME: return (_vp->val.int_val<_v->val.time_val)?-1: (_vp->val.int_val>_v->val.time_val)?1:0; case DB_STRING: _l = strlen(_v->val.string_val); _l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l; _n = strncasecmp(_vp->val.str_val.s, _v->val.string_val, _l); if(_n) return _n; if(_vp->val.str_val.len == strlen(_v->val.string_val)) return 0; if(_l==_vp->val.str_val.len) return -1; return 1; case DB_STR: _l = _v->val.str_val.len; _l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l; _n = strncasecmp(_vp->val.str_val.s, _v->val.str_val.s, _l); if(_n) return _n; if(_vp->val.str_val.len == _v->val.str_val.len) return 0; if(_l==_vp->val.str_val.len) return -1; return 1; case DB_BLOB: _l = _v->val.blob_val.len; _l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l; _n = strncasecmp(_vp->val.str_val.s, _v->val.blob_val.s, _l); if(_n) return _n; if(_vp->val.str_val.len == _v->val.blob_val.len) return 0; if(_l==_vp->val.str_val.len) return -1; return 1; case DB_BITMAP: return (_vp->val.int_val<_v->val.bitmap_val)?-1: (_vp->val.int_val>_v->val.bitmap_val)?1:0; } return -2; } opensips-2.2.2/modules/db_berkeley/bdb_res.h000066400000000000000000000036051300170765700210570ustar00rootroot00000000000000/* * sleepycat module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #ifndef _BDB_RES_H_ #define _BDB_RES_H_ #include "../../db/db_op.h" #include "../../db/db_res.h" #include "../../db/db_con.h" #include "bdb_lib.h" #include "bdb_val.h" typedef struct _con { database_p con; db_res_t* res; row_p row; } bdb_con_t, *bdb_con_p; #define BDB_CON_CONNECTION(db_con) (((bdb_con_p)((db_con)->tail))->con) #define BDB_CON_RESULT(db_con) (((bdb_con_p)((db_con)->tail))->res) #define BDB_CON_ROW(db_con) (((bdb_con_p)((db_con)->tail))->row) int bdb_get_columns(table_p _tp, db_res_t* _res, int* _lres, int _nc); int bdb_convert_row( db_res_t* _res, char *bdb_result, int* _lres); int bdb_append_row(db_res_t* _res, char *bdb_result, int* _lres, int _rx); int* bdb_get_colmap(table_p _tp, db_key_t* _k, int _n); int bdb_is_neq_type(db_type_t _t0, db_type_t _t1); int bdb_row_match(db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n, db_res_t* _r, int* lkey ); int bdb_cmp_val(db_val_t* _vp, db_val_t* _v); #endif opensips-2.2.2/modules/db_berkeley/bdb_util.c000066400000000000000000000024611300170765700212350ustar00rootroot00000000000000/* * db_berkeley module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #include #include #include #include "bdb_util.h" /** * */ int bdb_is_database(str *_s) { DIR *dirp = NULL; char buf[512]; if(!_s || !_s->s || _s->len <= 0 || _s->len > 510) return 0; strncpy(buf, _s->s, _s->len); buf[_s->len] = 0; dirp = opendir(buf); if(!dirp) return 0; closedir(dirp); return 1; } opensips-2.2.2/modules/db_berkeley/bdb_util.h000066400000000000000000000020661300170765700212430ustar00rootroot00000000000000/* * db_berkeley module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #ifndef _BDB_UTIL_H_ #define _BDB_UTIL_H_ #include "../../str.h" int bdb_is_database(str *); #endif opensips-2.2.2/modules/db_berkeley/bdb_val.c000066400000000000000000000145011300170765700210400ustar00rootroot00000000000000/* * db_berkeley module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #include "../../db/db_val.h" #include "../../db/db_ut.h" #include "db_berkeley.h" #include "bdb_res.h" #include "bdb_val.h" #include /** * A copy of db_ut::db_time2str EXCEPT does not wrap the date in single-quotes * * Convert a time_t value to string (w.o single-quote) * \param _v source value * \param _s target string * \param _l available length and target length * \return -1 on error, 0 on success * \todo This functions add quotes to the time value. This * should be done in the val2str function, as some databases * like db_berkeley don't need or like this at all. */ static inline int bdb_time2str(time_t _v, char* _s, int* _l) { struct tm* t; int l; if ((!_s) || (!_l) || (*_l < 2)) { LM_ERR("Invalid parameter value\n"); return -1; } // *_s++ = '\''; /* Convert time_t structure to format accepted by the database */ t = localtime(&_v); l = strftime(_s, *_l -1, "%Y-%m-%d %H:%M:%S", t); if (l == 0) { LM_ERR("Error during time conversion\n"); /* the value of _s is now unspecified */ _s = NULL; _l = 0; return -1; } *_l = l; // *(_s + l) = '\''; // *_l = l + 2; return 0; } /** * Does not copy strings */ int bdb_str2val(db_type_t _t, db_val_t* _v, char* _s, int _l) { static str dummy_string = {"", 0}; if(!_s) { memset(_v, 0, sizeof(db_val_t)); /* Initialize the string pointers to a dummy empty * string so that we do not crash when the NULL flag * is set but the module does not check it properly */ VAL_STRING(_v) = dummy_string.s; VAL_STR(_v) = dummy_string; VAL_BLOB(_v) = dummy_string; VAL_TYPE(_v) = _t; VAL_NULL(_v) = 1; return 0; } VAL_NULL(_v) = 0; switch(_t) { case DB_INT: if (db_str2int(_s, &VAL_INT(_v)) < 0) { LM_ERR("Error while converting INT value from string\n"); return -2; } else { VAL_TYPE(_v) = DB_INT; return 0; } break; case DB_BIGINT: if (db_str2bigint(_s, &VAL_BIGINT(_v)) < 0) { LM_ERR("Error while converting BIGINT value from string\n"); return -2; } else { VAL_TYPE(_v) = DB_BIGINT; return 0; } break; case DB_BITMAP: if (db_str2int(_s, &VAL_INT(_v)) < 0) { LM_ERR("Error while converting BITMAP value from string\n"); return -3; } else { VAL_TYPE(_v) = DB_BITMAP; return 0; } break; case DB_DOUBLE: if (db_str2double(_s, &VAL_DOUBLE(_v)) < 0) { LM_ERR("Error while converting DOUBLE value from string\n"); return -4; } else { VAL_TYPE(_v) = DB_DOUBLE; return 0; } break; case DB_STRING: if( strlen(_s)==4 && !strncasecmp(_s, "NULL", 4) ) VAL_NULL(_v) = 1; else { VAL_STRING(_v) = _s; VAL_TYPE(_v) = DB_STRING; VAL_FREE(_v) = 1; } return 0; case DB_STR: if( strlen(_s)==4 && !strncasecmp(_s, "NULL", 4) ) VAL_NULL(_v) = 1; else { VAL_STR(_v).s = (char*)_s; VAL_STR(_v).len = _l; VAL_TYPE(_v) = DB_STR; VAL_FREE(_v) = 1; } return 0; case DB_DATETIME: if( *_s == '\'') _s++; if (db_str2time(_s, &VAL_TIME(_v)) < 0) { LM_ERR("Error converting datetime\n"); return -5; } else { VAL_TYPE(_v) = DB_DATETIME; return 0; } break; case DB_BLOB: VAL_BLOB(_v).s = _s; VAL_BLOB(_v).len = _l; VAL_TYPE(_v) = DB_BLOB; VAL_FREE(_v) = 1; LM_DBG("got blob len %d\n", _l); return 0; } return -6; } /* * Used when converting result from a query */ int bdb_val2str(db_val_t* _v, char* _s, int* _len) { int l; if (VAL_NULL(_v)) { *_len = snprintf(_s, *_len, "NULL"); return 0; } switch(VAL_TYPE(_v)) { case DB_INT: if (db_int2str(VAL_INT(_v), _s, _len) < 0) { LM_ERR("Error while converting int to string\n"); return -2; } else { LM_DBG("Converted int to string\n"); return 0; } break; case DB_BIGINT: if (db_bigint2str(VAL_BIGINT(_v), _s, _len) < 0) { LM_ERR("Error while converting bigint to string\n"); return -2; } else { LM_DBG("Converted bigint to string\n"); return 0; } break; case DB_BITMAP: if (db_int2str(VAL_INT(_v), _s, _len) < 0) { LM_ERR("Error while converting bitmap to string\n"); return -3; } else { LM_DBG("Converted bitmap to string\n"); return 0; } break; case DB_DOUBLE: if (db_double2str(VAL_DOUBLE(_v), _s, _len) < 0) { LM_ERR("Error while converting double to string\n"); return -3; } else { LM_DBG("Converted double to string\n"); return 0; } break; case DB_STRING: l = strlen(VAL_STRING(_v)); if (*_len < l ) { LM_ERR("Destination buffer too short for string\n"); return -4; } else { LM_DBG("Converted string to string\n"); strncpy(_s, VAL_STRING(_v) , l); _s[l] = 0; *_len = l+1; /* count the 0 also */ return 0; } break; case DB_STR: l = VAL_STR(_v).len; if (*_len < l) { LM_ERR("Destination buffer too short for str\n"); return -5; } else { LM_DBG("Converted str to string\n"); strncpy(_s, VAL_STR(_v).s , l); *_len = l; return 0; } break; case DB_DATETIME: if (bdb_time2str(VAL_TIME(_v), _s, _len) < 0) { LM_ERR("Error while converting time_t to string\n"); return -6; } else { LM_DBG("Converted time_t to string\n"); return 0; } break; case DB_BLOB: l = VAL_BLOB(_v).len; if (*_len < l) { LM_ERR("Destination buffer too short for blob\n"); return -7; } else { strncpy(_s, VAL_BLOB(_v).s , l); LM_DBG("Converting BLOB [%.*s]\n", l,_s); *_len = l; return 0; } break; default: LM_DBG("Unknown data type\n"); return -8; } } opensips-2.2.2/modules/db_berkeley/bdb_val.h000066400000000000000000000023121300170765700210420ustar00rootroot00000000000000/* * db_berkeley module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #ifndef _BDB_VAL_H_ #define _BDB_VAL_H_ #include "../../db/db_op.h" #include "../../db/db_res.h" #include "../../db/db_con.h" int bdb_val2str(db_val_t* _v, char* _s, int* _len); int bdb_str2val(db_type_t _t, db_val_t* _v, char* _s, int _l); #endif opensips-2.2.2/modules/db_berkeley/db_berkeley.c000066400000000000000000000617461300170765700217330ustar00rootroot00000000000000/* * db_berkeley module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #include #include #include #include "../../sr_module.h" #include "../../str.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../db/db_res.h" #include "../../db/db.h" #include "db_berkeley.h" #include "bdb_lib.h" #include "bdb_res.h" #include "bdb_mi.h" #ifndef CFG_DIR #define CFG_DIR "/tmp" #endif #define BDB_ID "berkeley://" #define BDB_ID_LEN (sizeof(BDB_ID)-1) #define BDB_PATH_LEN 256 #define BDB_KEY 1 #define BDB_VALUE 0 int auto_reload = 0; int log_enable = 0; int journal_roll_interval = 0; static int mod_init(void); static void destroy(void); int bdb_bind_api(const str* mod, db_func_t *dbb); /* * Exported functions */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)bdb_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"auto_reload", INT_PARAM, &auto_reload }, {"log_enable", INT_PARAM, &log_enable }, {"journal_roll_interval", INT_PARAM, &journal_roll_interval }, {0, 0, 0} }; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { { MI_BDB_RELOAD, 0, mi_bdb_reload, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; struct module_exports exports = { "db_berkeley", MOD_TYPE_SQLDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ destroy, /* destroy function */ 0 /* per-child init function */ }; static int mod_init(void) { db_parms_t p; p.auto_reload = auto_reload; p.log_enable = log_enable; p.cache_size = (4 * 1024 * 1024); //4Mb p.journal_roll_interval = journal_roll_interval; if(bdblib_init(&p)) return -1; return 0; } static void destroy(void) { bdblib_destroy(); } int bdb_bind_api(const str* mod, db_func_t *dbb) { if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->use_table = bdb_use_table; dbb->init = bdb_init; dbb->close = bdb_close; dbb->query = (db_query_f)bdb_query; dbb->free_result = bdb_free_query; dbb->insert = (db_insert_f)bdb_insert; dbb->delete = (db_delete_f)bdb_delete; dbb->update = (db_update_f)bdb_update; return 0; } int bdb_use_table(db_con_t* _h, const str* _t) { return db_use_table(_h, _t); } /* * Initialize database connection */ db_con_t* bdb_init(const str* _sqlurl) { db_con_t* _res; str _s; char bdb_path[BDB_PATH_LEN]; if (!_sqlurl || !_sqlurl->s) { LM_ERR("invalid parameter value\n"); return 0; } _s.s = _sqlurl->s; _s.len = _sqlurl->len; if(_s.len <= BDB_ID_LEN || strncmp(_s.s, BDB_ID, BDB_ID_LEN)!=0) { LM_ERR("invalid database URL - should be:" " <%s[/]path/to/directory>\n", BDB_ID); return NULL; } _s.s += BDB_ID_LEN; _s.len -= BDB_ID_LEN; if(_s.s[0]!='/') { if(sizeof(CFG_DIR)+_s.len+2 > BDB_PATH_LEN) { LM_ERR("path to database is too long\n"); return NULL; } strcpy(bdb_path, CFG_DIR); bdb_path[sizeof(CFG_DIR)] = '/'; strncpy(&bdb_path[sizeof(CFG_DIR)+1], _s.s, _s.len); _s.len += sizeof(CFG_DIR); _s.s = bdb_path; } _res = pkg_malloc(sizeof(db_con_t)+sizeof(bdb_con_t)); if (!_res) { LM_ERR("No private memory left\n"); return NULL; } memset(_res, 0, sizeof(db_con_t) + sizeof(bdb_con_t)); _res->tail = (unsigned long)((char*)_res+sizeof(db_con_t)); LM_INFO("using database at: %.*s\n", _s.len, _s.s); BDB_CON_CONNECTION(_res) = bdblib_get_db(&_s); if (!BDB_CON_CONNECTION(_res)) { LM_ERR("cannot get the link to database\n"); return NULL; } return _res; } /* * Close a database connection */ void bdb_close(db_con_t* _h) { if(BDB_CON_RESULT(_h)) db_free_result(BDB_CON_RESULT(_h)); pkg_free(_h); } /* * n can be the dbenv path or a table name */ int bdb_reload(char* _n) { int rc = 0; #ifdef BDB_EXTRA_DEBUG LM_DBG("[bdb_reload] Initiate RELOAD in %s\n", _n); #endif if ((rc = bdblib_close(_n)) != 0) { LM_ERR("[bdb_reload] Error while closing db_berkeley DB.\n"); return rc; } if ((rc = bdblib_reopen(_n)) != 0) { LM_ERR("[bdb_reload] Error while reopening db_berkeley DB.\n"); return rc; } #ifdef BDB_EXTRA_DEBUG LM_DBG("[bdb_reload] RELOAD successful in %s\n", _n); #endif return rc; } /* * Attempts to reload a Berkeley database; reloads when the inode changes */ void bdb_check_reload(db_con_t* _con) { str s; char* p; int rc, len; struct stat st; database_p db; char n[MAX_ROW_SIZE]; char t[MAX_TABLENAME_SIZE]; table_p tp = NULL; tbl_cache_p tbc = NULL; p=n; rc = len = 0; /*get dbenv name*/ db = BDB_CON_CONNECTION(_con); if(!db->dbenv) return; s.s = db->name.s; s.len = db->name.len; len+=s.len; if(len > MAX_ROW_SIZE) { LM_ERR("dbenv name too long \n"); return; } strncpy(p, s.s, s.len); p+=s.len; len++; if(len > MAX_ROW_SIZE) { LM_ERR("dbenv name too long \n"); return; } /*append slash */ *p = '/'; p++; /*get table name*/ s.s = CON_TABLE(_con)->s; s.len = CON_TABLE(_con)->len; len+=s.len; if((len>MAX_ROW_SIZE) || (s.len > MAX_TABLENAME_SIZE) ) { LM_ERR("table name too long \n"); return; } strncpy(t, s.s, s.len); t[s.len] = 0; strncpy(p, s.s, s.len); p+=s.len; *p=0; if( (tbc = bdblib_get_table(db, &s)) == NULL) return; if( (tp = tbc->dtp) == NULL) return; LM_DBG("stat file [%.*s]\n", len, n); rc = stat(n, &st); if(!rc) { if((tp->ino!=0) && (st.st_ino != tp->ino)) bdb_reload(t); /*file changed on disk*/ tp->ino = st.st_ino; } } /* * Free all memory allocated by get_result */ int bdb_free_query(db_con_t* _h, db_res_t* _r) { if(_r) db_free_result(_r); if(_h) BDB_CON_RESULT(_h) = NULL; return 0; } /* * Query table for specified rows * _con: structure representing database connection * _k: key names * _op: operators * _v: values of the keys that must match * _c: column names to return * _n: number of key=values pairs to compare * _nc: number of columns to return * _o: order by the specified column */ int bdb_query(db_con_t* _con, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r) { tbl_cache_p _tbc = NULL; table_p _tp = NULL; char kbuf[MAX_ROW_SIZE]; char dbuf[MAX_ROW_SIZE]; u_int32_t i; int ret; int klen=MAX_ROW_SIZE; int *lkey=NULL, *lres=NULL; DBT key, data; DB *db; DBC *dbcp; if ((!_con) || (!_r) || !CON_TABLE(_con)) { #ifdef BDB_EXTRA_DEBUG LM_ERR("Invalid parameter value\n"); #endif return -1; } *_r = NULL; /*check if underlying DB file has changed inode */ if(auto_reload) bdb_check_reload(_con); _tbc = bdblib_get_table(BDB_CON_CONNECTION(_con), (str*)CON_TABLE(_con)); if(!_tbc) { LM_WARN("table does not exist!\n"); return -1; } _tp = _tbc->dtp; if(!_tp) { LM_WARN("table not loaded!\n"); return -1; } #ifdef BDB_EXTRA_DEBUG LM_DBG("QUERY in %.*s\n", _tp->name.len, _tp->name.s); if (_o) LM_DBG("DONT-CARE : _o: order by the specified column \n"); if (_op) LM_DBG("DONT-CARE : _op: operators for refining query \n"); #endif db = _tp->db; if(!db) return -1; memset(&key, 0, sizeof(DBT)); memset(kbuf, 0, MAX_ROW_SIZE); memset(&data, 0, sizeof(DBT)); memset(dbuf, 0, MAX_ROW_SIZE); data.data = dbuf; data.ulen = MAX_ROW_SIZE; data.flags = DB_DBT_USERMEM; /* if _c is NULL and _nc is zero, you will get all table columns in the result */ if (_c) { lres = bdb_get_colmap(_tbc->dtp, _c, _nc); if(!lres) { ret = -1; goto error; } } if(_k) { lkey = bdb_get_colmap(_tbc->dtp, _k, _n); if(!lkey) { ret = -1; goto error; } } else { DB_HASH_STAT st; memset(&st, 0, sizeof(DB_HASH_STAT)); i =0 ; #ifdef BDB_EXTRA_DEBUG LM_DBG("SELECT * FROM %.*s\n", _tp->name.len, _tp->name.s); #endif /* Acquire a cursor for the database. */ if ((ret = db->cursor(db, NULL, &dbcp, 0)) != 0) { LM_ERR("Error creating cursor\n"); goto error; } /*count the number of records*/ while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0) { if(!strncasecmp((char*)key.data,"METADATA",8)) continue; i++; } dbcp->c_close(dbcp); ret=0; #ifdef BDB_EXTRA_DEBUG LM_DBG("%i = SELECT COUNT(*) FROM %.*s\n", i, _tp->name.len, _tp->name.s); #endif *_r = db_new_result(); if (!*_r) { LM_ERR("no memory left for result \n"); ret = -2; goto error; } if(i == 0) { /*return empty table*/ RES_ROW_N(*_r) = 0; BDB_CON_RESULT(_con) = *_r; return 0; } RES_ROW_N(*_r) = i; /*fill in the column part of db_res_t (metadata) */ if ((ret = bdb_get_columns(_tbc->dtp, *_r, lres, _nc)) < 0) { LM_ERR("Error while getting column names\n"); goto error; } /*allocate N rows in the result*/ if (db_allocate_rows(*_r, i)!=0) { LM_ERR("failed to allocated rows\n"); goto error; } /* Acquire a cursor for the database. */ if ((ret = db->cursor(db, NULL, &dbcp, 0)) != 0) { LM_ERR("Error creating cursor\n"); goto error; } /*convert each record into a row in the result*/ i =0 ; while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0) { if(!strncasecmp((char*)key.data,"METADATA",8)) continue; #ifdef BDB_EXTRA_DEBUG LM_DBG("KEY: [%.*s]\nDATA: [%.*s]\n" , (int) key.size , (char *)key.data , (int) data.size , (char *)data.data); #endif /*fill in the row part of db_res_t */ if ((ret=bdb_append_row( *_r, dbuf, lres, i)) < 0) { LM_ERR("Error while converting row\n"); goto error; } i++; } dbcp->c_close(dbcp); BDB_CON_RESULT(_con) = *_r; return 0; } if ( (ret = bdblib_valtochar(_tp, lkey, kbuf, &klen, _v, _n, BDB_KEY)) != 0 ) { LM_ERR("error in query key \n"); goto error; } key.data = kbuf; key.ulen = MAX_ROW_SIZE; key.flags = DB_DBT_USERMEM; key.size = klen; data.data = dbuf; data.ulen = MAX_ROW_SIZE; data.flags = DB_DBT_USERMEM; /*create an empty db_res_t which gets returned even if no result*/ *_r = db_new_result(); if (!*_r) { LM_ERR("no memory left for result \n"); ret = -2; goto error; } RES_ROW_N(*_r) = 0; BDB_CON_RESULT(_con) = *_r; #ifdef BDB_EXTRA_DEBUG LM_DBG("SELECT KEY: [%.*s]\n" , (int) key.size , (char *)key.data ); #endif /*query Berkely DB*/ if ((ret = db->get(db, NULL, &key, &data, 0)) == 0) { #ifdef BDB_EXTRA_DEBUG LM_DBG("RESULT\nKEY: [%.*s]\nDATA: [%.*s]\n" , (int) key.size , (char *)key.data , (int) data.size , (char *)data.data); #endif /*fill in the col part of db_res_t */ if ((ret = bdb_get_columns(_tbc->dtp, *_r, lres, _nc)) < 0) { LM_ERR("Error while getting column names\n"); goto error; } /*allocate N rows in the result*/ if (db_allocate_rows(*_r, 1)!=0) { LM_ERR("failed to allocated rows\n"); goto error; } /*fill in the row part of db_res_t */ if ((ret=bdb_convert_row( *_r, dbuf, lres)) < 0) { LM_ERR("Error while converting row\n"); goto error; } } else { /*Berkeley DB error handler*/ switch(ret) { case DB_NOTFOUND: #ifdef BDB_EXTRA_DEBUG LM_DBG("NO RESULT for QUERY \n"); #endif ret=0; break; /*The following are all critical/fatal */ case DB_LOCK_DEADLOCK: // The operation was selected to resolve a deadlock. case DB_SECONDARY_BAD: // A secondary index references a nonexistent primary key. case DB_RUNRECOVERY: default: LM_CRIT("DB->get error: %s.\n", db_strerror(ret)); bdblib_recover(_tp,ret); goto error; } } if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); return ret; error: if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); if(*_r) db_free_result(*_r); *_r = NULL; return ret; } /* * Raw SQL query */ int bdb_raw_query(db_con_t* _h, char* _s, db_res_t** _r) { LM_CRIT("DB RAW QUERY not implemented!\n"); return -1; } /* * Insert a row into table */ int bdb_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n) { tbl_cache_p _tbc = NULL; table_p _tp = NULL; char kbuf[MAX_ROW_SIZE]; char dbuf[MAX_ROW_SIZE]; int i, j, ret, klen, dlen; int *lkey=NULL; DBT key, data; DB *db; i = j = ret = 0; klen=MAX_ROW_SIZE; dlen=MAX_ROW_SIZE; if ((!_h) || (!_v) || !CON_TABLE(_h)) { return -1; } if (!_k) { #ifdef BDB_EXTRA_DEBUG LM_ERR("DB INSERT without KEYs not implemented! \n"); #endif return -2; } _tbc = bdblib_get_table(BDB_CON_CONNECTION(_h), (str*)CON_TABLE(_h)); if(!_tbc) { LM_WARN("table does not exist!\n"); return -3; } _tp = _tbc->dtp; if(!_tp) { LM_WARN("table not loaded!\n"); return -4; } #ifdef BDB_EXTRA_DEBUG LM_DBG("INSERT in %.*s\n", _tp->name.len, _tp->name.s ); #endif db = _tp->db; memset(&key, 0, sizeof(DBT)); memset(kbuf, 0, klen); if(_tp->ncols<_n) { LM_WARN("more values than columns!!\n"); return -5; } lkey = bdb_get_colmap(_tp, _k, _n); if(!lkey) return -7; /* verify col types provided */ for(i=0; i<_n; i++) { j = (lkey)?lkey[i]:i; if(bdb_is_neq_type(_tp->colp[j]->type, _v[i].type)) { LM_WARN("incompatible types v[%d] - c[%d]!\n", i, j); ret = -8; goto error; } } /* make the key */ if ( (ret = bdblib_valtochar(_tp, lkey, kbuf, &klen, _v, _n, BDB_KEY)) != 0 ) { LM_ERR("Error in bdblib_valtochar\n"); ret = -9; goto error; } key.data = kbuf; key.ulen = MAX_ROW_SIZE; key.flags = DB_DBT_USERMEM; key.size = klen; //make the value (row) memset(&data, 0, sizeof(DBT)); memset(dbuf, 0, MAX_ROW_SIZE); if ( (ret = bdblib_valtochar(_tp, lkey, dbuf, &dlen, _v, _n, BDB_VALUE)) != 0 ) { LM_ERR("Error in bdblib_valtochar\n"); ret = -9; goto error; } data.data = dbuf; data.ulen = MAX_ROW_SIZE; data.flags = DB_DBT_USERMEM; data.size = dlen; if ((ret = db->put(db, NULL, &key, &data, 0)) == 0) { bdblib_log(JLOG_INSERT, _tp, dbuf, dlen); #ifdef BDB_EXTRA_DEBUG LM_DBG("INSERT\nKEY: [%.*s]\nDATA: [%.*s]\n" , (int) key.size , (char *)key.data , (int) data.size , (char *)data.data); #endif } else { /*Berkeley DB error handler*/ switch(ret) { /*The following are all critical/fatal */ case DB_LOCK_DEADLOCK: /* The operation was selected to resolve a deadlock. */ case DB_RUNRECOVERY: default: LM_CRIT("DB->put error: %s.\n", db_strerror(ret)); bdblib_recover(_tp, ret); goto error; } } error: if(lkey) pkg_free(lkey); return ret; } /* * Delete a row from table * * To delete ALL rows: * do Not specify any keys, or values, and _n <=0 * */ int bdb_delete(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n) { tbl_cache_p _tbc = NULL; table_p _tp = NULL; char kbuf[MAX_ROW_SIZE]; int ret, klen; int *lkey=NULL; DBT key,data; DB *db; DBC *dbcp; ret = 0; klen=MAX_ROW_SIZE; if (_op) return ( _bdb_delete_cursor(_h, _k, _op, _v, _n) ); if ((!_h) || !CON_TABLE(_h)) return -1; _tbc = bdblib_get_table(BDB_CON_CONNECTION(_h), (str*)CON_TABLE(_h)); if(!_tbc) { LM_WARN("table does not exist!\n"); return -3; } _tp = _tbc->dtp; if(!_tp) { LM_WARN("table not loaded!\n"); return -4; } #ifdef BDB_EXTRA_DEBUG LM_DBG("DELETE in %.*s\n", _tp->name.len, _tp->name.s ); #endif db = _tp->db; memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); memset(kbuf, 0, klen); if(!_k || !_v || _n<=0) { /* Acquire a cursor for the database. */ if ((ret = db->cursor(db, NULL, &dbcp, DB_WRITECURSOR) ) != 0) { LM_ERR("Error creating cursor\n"); goto error; } while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0) { if(!strncasecmp((char*)key.data,"METADATA",8)) continue; #ifdef BDB_EXTRA_DEBUG LM_DBG("KEY: [%.*s]\n" , (int) key.size , (char *)key.data); #endif ret = dbcp->c_del(dbcp, 0); } dbcp->c_close(dbcp); return 0; } lkey = bdb_get_colmap(_tp, _k, _n); if(!lkey) return -5; /* make the key */ if ( (ret = bdblib_valtochar(_tp, lkey, kbuf, &klen, _v, _n, BDB_KEY)) != 0 ) { LM_ERR("Error in bdblib_makekey\n"); ret = -6; goto error; } key.data = kbuf; key.ulen = MAX_ROW_SIZE; key.flags = DB_DBT_USERMEM; key.size = klen; if ((ret = db->del(db, NULL, &key, 0)) == 0) { bdblib_log(JLOG_DELETE, _tp, kbuf, klen); #ifdef BDB_EXTRA_DEBUG LM_DBG("DELETED ROW \n KEY: %s \n", (char *)key.data); #endif } else { /*Berkeley DB error handler*/ switch(ret){ case DB_NOTFOUND: ret = 0; break; /*The following are all critical/fatal */ case DB_LOCK_DEADLOCK: /* The operation was selected to resolve a deadlock. */ case DB_SECONDARY_BAD: /* A secondary index references a nonexistent primary key. */ case DB_RUNRECOVERY: default: LM_CRIT("DB->del error: %s.\n" , db_strerror(ret)); bdblib_recover(_tp, ret); goto error; } } ret = 0; error: if(lkey) pkg_free(lkey); return ret; } /* _bdb_delete_cursor -- called from bdb_delete when the query involves operators other than equal '='. Adds support for queries like this: DELETE from SomeTable WHERE _k[0] < _v[0] In this case, the keys _k are not the actually schema keys, so we need to iterate via cursor to perform this operation. */ int _bdb_delete_cursor(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n) { tbl_cache_p _tbc = NULL; table_p _tp = NULL; db_res_t* _r = NULL; char kbuf[MAX_ROW_SIZE]; char dbuf[MAX_ROW_SIZE]; int ret, klen=MAX_ROW_SIZE; DBT key, data; DB *db; DBC *dbcp; int *lkey=NULL; ret = 0; if ((!_h) || !CON_TABLE(_h)) return -1; _tbc = bdblib_get_table(BDB_CON_CONNECTION(_h), (str*)CON_TABLE(_h)); if(!_tbc) { LM_WARN("table does not exist!\n"); return -3; } _tp = _tbc->dtp; if(!_tp) { LM_WARN("table not loaded!\n"); return -4; } #ifdef BDB_EXTRA_DEBUG LM_DBG("DELETE by cursor in %.*s\n", _tp->name.len, _tp->name.s ); #endif if(_k) { lkey = bdb_get_colmap(_tp, _k, _n); if(!lkey) { ret = -1; goto error; } } /* create an empty db_res_t which gets returned even if no result */ _r = db_new_result(); if (!_r) { LM_ERR("no memory for result \n"); } RES_ROW_N(_r) = 0; /* fill in the col part of db_res_t */ if ((ret = bdb_get_columns(_tp, _r, 0, 0)) != 0) { LM_ERR("Error while getting column names\n"); goto error; } db = _tp->db; memset(&key, 0, sizeof(DBT)); memset(kbuf, 0, klen); memset(&data, 0, sizeof(DBT)); memset(dbuf, 0, MAX_ROW_SIZE); data.data = dbuf; data.ulen = MAX_ROW_SIZE; data.flags = DB_DBT_USERMEM; /* Acquire a cursor for the database. */ if ((ret = db->cursor(db, NULL, &dbcp, DB_WRITECURSOR)) != 0) { LM_ERR("Error creating cursor\n"); } while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0) { if (db_allocate_rows(_r, 1)!=0) { LM_ERR("failed to allocated rows\n"); goto error; } RES_ROW_N(_r) = 1; if(!strncasecmp((char*)key.data,"METADATA",8)) continue; /*fill in the row part of db_res_t */ if ((ret=bdb_convert_row( _r, dbuf, 0)) < 0) { LM_ERR("Error while converting row\n"); goto error; } if(bdb_row_match(_k, _op, _v, _n, _r, lkey )) { #ifdef BDB_EXTRA_DEBUG LM_DBG("DELETE ROW by KEY: [%.*s]\n", (int) key.size, (char *)key.data); #endif if((ret = dbcp->c_del(dbcp, 0)) != 0) { /* Berkeley DB error handler */ LM_CRIT("DB->get error: %s.\n", db_strerror(ret)); bdblib_recover(_tp,ret); } } memset(dbuf, 0, MAX_ROW_SIZE); db_free_rows( _r); } ret = 0; error: if(dbcp) dbcp->c_close(dbcp); if(_r) db_free_result(_r); if(lkey) pkg_free(lkey); return ret; } /* * Updates a row in table * Limitation: only knows how to update a single row * * _con: structure representing database connection * _k: key names * _op: operators * _v: values of the keys that must match * _uk: update keys; cols that need to be updated * _uv: update values; col values that need to be commited * _un: number of rows to update */ int bdb_update(db_con_t* _con, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un) { char *c, *t; int ret, i, qcol, len, sum; int *lkey=NULL; tbl_cache_p _tbc = NULL; table_p _tp = NULL; char kbuf[MAX_ROW_SIZE]; char qbuf[MAX_ROW_SIZE]; char ubuf[MAX_ROW_SIZE]; char * tmp; DBT key, qdata, udata; DB *db; sum = ret = i = qcol = len = 0; if (!_con || !CON_TABLE(_con) || !_uk || !_uv || _un <= 0) return -1; _tbc = bdblib_get_table(BDB_CON_CONNECTION(_con), (str*)CON_TABLE(_con)); if(!_tbc) { LM_ERR("table does not exist\n"); return -1; } _tp = _tbc->dtp; if(!_tp) { LM_ERR("table not loaded\n"); return -1; } db = _tp->db; if(!db) { LM_ERR("DB null ptr\n"); return -1; } #ifdef BDB_EXTRA_DEBUG LM_DBG("UPDATE in %.*s\n", _tp->name.len, _tp->name.s); if (_op) LM_DBG("DONT-CARE : _op: operators for refining query \n"); #endif memset(&key, 0, sizeof(DBT)); memset(kbuf, 0, MAX_ROW_SIZE); memset(&qdata, 0, sizeof(DBT)); memset(qbuf, 0, MAX_ROW_SIZE); qdata.data = qbuf; qdata.ulen = MAX_ROW_SIZE; qdata.flags = DB_DBT_USERMEM; if(_k) { lkey = bdb_get_colmap(_tbc->dtp, _k, _n); if(!lkey) return -4; } else { LM_ERR("Null keys in update _k=0 \n"); return -1; } len = MAX_ROW_SIZE; if ( (ret = bdblib_valtochar(_tp, lkey, kbuf, &len, _v, _n, BDB_KEY)) != 0 ) { LM_ERR("Error in query key \n"); goto cleanup; } if(lkey) pkg_free(lkey); key.data = kbuf; key.ulen = MAX_ROW_SIZE; key.flags = DB_DBT_USERMEM; key.size = len; /*stage 1: QUERY Berkely DB*/ if ((ret = db->get(db, NULL, &key, &qdata, 0)) == 0) { #ifdef BDB_EXTRA_DEBUG LM_DBG("RESULT\nKEY: [%.*s]\nDATA: [%.*s]\n" , (int) key.size , (char *)key.data , (int) qdata.size , (char *)qdata.data); #endif } else { goto db_error; } /* stage 2: UPDATE row with new values */ /* map the provided keys to those in our schema */ lkey = bdb_get_colmap(_tbc->dtp, _uk, _un); if(!lkey) return -4; /* build a new row for update data (udata) */ memset(&udata, 0, sizeof(DBT)); memset(ubuf, 0, MAX_ROW_SIZE); /* loop over each column of the qbuf and copy it to our new ubuf unless its a field that needs to update */ LM_DBG("Found: [%s]\n",qbuf); tmp = qbuf; c = strsep(&tmp, DELIM); t = ubuf; while( c!=NULL) { char* delim = DELIM; int k; len = strlen(c); sum+=len; if(sum > MAX_ROW_SIZE) { LM_ERR("value too long for string \n"); ret = -3; goto cleanup; } for(i=0;i<_un;i++) { k = lkey[i]; if (qcol == k) { /* update this col */ len = MAX_ROW_SIZE - sum; if( bdb_val2str( &_uv[i], t, &len) ) { LM_ERR("value too long for string \n"); ret = -3; goto cleanup; } goto next; } } /* copy original column to the new column */ strncpy(t, c, len); next: t+=len; /* append DELIM */ sum += DELIM_LEN; if(sum > MAX_ROW_SIZE) { LM_ERR("value too long for string \n"); ret = -3; goto cleanup; } strncpy(t, delim, DELIM_LEN); t += DELIM_LEN; c = strsep(&tmp, DELIM); qcol++; } ubuf[sum] = '0'; udata.data = ubuf; udata.ulen = MAX_ROW_SIZE; udata.flags = DB_DBT_USERMEM; udata.size = sum; #ifdef BDB_EXTRA_DEBUG LM_DBG("MODIFIED Data\nKEY: [%.*s]\nDATA: [%.*s]\n" , (int) key.size , (char *)key.data , (int) udata.size , (char *)udata.data); #endif /* stage 3: DELETE old row using key*/ if ((ret = db->del(db, NULL, &key, 0)) == 0) { #ifdef BDB_EXTRA_DEBUG LM_DBG("DELETED ROW\nKEY: %s \n", (char *)key.data); #endif } else { goto db_error; } /* stage 4: INSERT new row with key*/ if ((ret = db->put(db, NULL, &key, &udata, 0)) == 0) { bdblib_log(JLOG_UPDATE, _tp, ubuf, sum); #ifdef BDB_EXTRA_DEBUG LM_DBG("INSERT \nKEY: [%.*s]\nDATA: [%.*s]\n" , (int) key.size , (char *)key.data , (int) udata.size , (char *)udata.data); #endif } else { goto db_error; } #ifdef BDB_EXTRA_DEBUG LM_DBG("UPDATE COMPLETE \n"); #endif cleanup: if(lkey) pkg_free(lkey); return ret; db_error: /*Berkeley DB error handler*/ switch(ret) { case DB_NOTFOUND: #ifdef BDB_EXTRA_DEBUG LM_DBG("NO RESULT \n"); #endif return -1; /* The following are all critical/fatal */ case DB_LOCK_DEADLOCK: /* The operation was selected to resolve a deadlock. */ case DB_SECONDARY_BAD: /* A secondary index references a nonexistent primary key.*/ case DB_RUNRECOVERY: default: LM_CRIT("DB->get error: %s.\n", db_strerror(ret)); bdblib_recover(_tp,ret); } if(lkey) pkg_free(lkey); return ret; } opensips-2.2.2/modules/db_berkeley/db_berkeley.h000066400000000000000000000043251300170765700217260ustar00rootroot00000000000000/* * db_berkeley module, portions of this code were templated using * the dbtext and postgres modules. * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #ifndef _BDB_H_ #define _BDB_H_ #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" /* reloads the berkeley db */ int bdb_reload(char* _n); void bdb_check_reload(db_con_t* _con); int bdb_use_table(db_con_t* _h, const str* _t); /* * Initialize database connection */ db_con_t* bdb_init(const str* _sqlurl); /* * Close a database connection */ void bdb_close(db_con_t* _h); /* * Free all memory allocated by get_result */ int bdb_free_query(db_con_t* _h, db_res_t* _r); /* * Do a query */ int bdb_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r); /* * Raw SQL query */ int bdb_raw_query(db_con_t* _h, char* _s, db_res_t** _r); /* * Insert a row into table */ int bdb_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n); /* * Delete a row from table */ int bdb_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n); int _bdb_delete_cursor(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n); /* * Update a row in table */ int bdb_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un); #endif opensips-2.2.2/modules/db_berkeley/doc/000077500000000000000000000000001300170765700200475ustar00rootroot00000000000000opensips-2.2.2/modules/db_berkeley/doc/db_berkeley.xml000066400000000000000000000023601300170765700230410ustar00rootroot00000000000000 %docentities; ]> Berkeley DB Module &osipsname; Will Quan Cisco Systems
wiquan@cisco.com http://www.cisco.com
Will Quan
wiquan@cisco.com
2007 Cisco Systems $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/db_berkeley/doc/db_berkeley_admin.xml000066400000000000000000000425551300170765700242230ustar00rootroot00000000000000 &adminguide;
Overview This is a module which integrates the Berkeley DB into OpenSIPS. It implements the DB API defined in OpenSIPS.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: Berkeley Berkeley DB 4.6 - an embedded database.
Exported Parameters
<varname>auto_reload</varname> (integer) The auto-reload will close and reopen a Berkeley DB when the files inode has changed. The operation occurs only duing a query. Other operations such as insert or delete, do not invoke auto_reload. Default value is 0 (1 - on / 0 - off). Set <varname>auto_reload</varname> parameter ... modparam("db_berkeley", "auto_reload", 1) ...
<varname>log_enable</varname> (integer) The log_enable boolean controls when to create journal files. The following operations can be journaled: INSERT, UPDATE, DELETE. Other operations such as SELECT, do not. This journaling are required if you need to recover from a corrupt DB file. That is, bdb_recover requires these to rebuild the db file. If you find this log feature useful, you may also be interested in the METADATA_LOGFLAGS bitfield that each table has. It will allow you to control which operations to journal, and the destination (like syslog, stdout, local-file). Refer to bdblib_log() and documentation on METADATA. Default value is 0 (1 - on / 0 - off). Set <varname>log_enable</varname> parameter ... modparam("db_berkeley", "log_enable", 1) ...
<varname>journal_roll_interval</varname> (integer seconds) The journal_roll_interval will close and open a new log file. The roll operation occurs only at the end of writing a log, so it is not guaranteed to to roll 'on time'. Default value is 0 (off). Set <varname>journal_roll_interval</varname> parameter ... modparam("db_berkeley", "journal_roll_interval", 3600) ...
Exported Functions No function exported to be used from configuration file.
Exported MI Functions
<function moreinfo="none">bdb_reload</function> Causes db_berkeley module to re-read the contents of specified table (or dbenv). The db_berkeley DB actually loads each table on demand, as opposed to loading all at mod_init time. The bdb_reload operation is implemented as a close followed by a reopen. Note- bdb_reload will fail if a table has not been accessed before (because the close will fail). Name: bdb_reload Parameters: tablename (or db_path); to reload a particular table provide the tablename as the arguement (eg subscriber); to reload all tables provide the db_path to the db files. The path can be found in opensipsctlrc DB_PATH variable.
Installation and Running First download, compile and install the Berkeley DB. This is outside the scope of this document. Documentation for this procedure is available on the Internet. Next, prepare to compile OpenSIPS with the db_berkeley module. In the directory /modules/db_berkeley, modify the Makefile to point to your distribution of Berkeley DB. You may also define 'BDB_EXTRA_DEBUG' to compile in extra debug logs. However, it is not a recommended deployment to production servers. Because the module dependes on an external library, the db_berkeley module is not compiled and installed by default. You can use one of the next options. edit the "Makefile" and remove "db_berkeley" from "excluded_modules" list. Then follow the standard procedure to install &osips;: "make all; make install". from command line use: 'make all include_modules="db_berkeley"; make install include_modules="db_berkeley"'. Installation of OpenSIPS is performed by simply running make install as root user of the main directory. This will install the binaries in /usr/local/sbin/. If this was successful, &osips; control engine files should now be installed as /usr/local/sbin/opensipsdbctl. Decide where (on the filesystem) you want to install the Berkeley DB files. For instance, '/usr/local/etc/opensips/db_berkeley' directory. Make note of this directory as we need to add this path to the opensipsctlrc file. Note: OpenSIPS will not startup without these DB files. Edit opensipsctlrc - There are two parameters in this file that should be configured before opensipsctldb script can work properly: DBENGINE and DB_PATH. Edit file: '/usr/local/etc/opensips/opensipsctlrc' ## database type: MYSQL, PGSQL, DB_BERKELEY, or DBTEXT, by default none is loaded # DBENGINE=DB_BERKELEY ## database path used by dbtext or db_berkeley # DB_PATH="/usr/local/etc/opensips/db_berkeley" (Optional) Pre creation step- Customize your meta-data. The DB files are initially seeded with necessary meta-data. This is a good time to review the meta-data section details, before making modifications to your tables dbschema. By default, the files are installed in '/usr/local/share/opensips/db_berkeley/opensips' By default these tables are created Read/Write and without any journalling as shown. These settings can be modified on a per table basis. Note: If you plan to use bdb_recover, you must change the LOGFLAGS. METADATA_READONLY 0 METADATA_LOGFLAGS 0 Execute opensipsdbctl - There are three (3) groups of tables you may need depending on your situation. opensipsdbctl create (required) opensipsdbctl presence (optional) opensipsdbctl extra (optional) Modify the OpenSIPS configuration file to use db_berkeley module. The database URL for modules must be the path to the directory where the Berkeley DB table-files are located, prefixed by "berkeley://", e.g., "berkeley:///usr/local/etc/opensips/db_berkeley". A couple other IMPORTANT things to consider are the 'db_mode' and the 'use_domain' modparams. The description of these parameters are found in usrloc documentation. Note on db_mode- The db_berkeley module will only journal the moment usrloc writes back to the DB. The safest mode is mode 3 , since the db_berkeley journal files will always be up-to-date. The main point is the db_mode vs. recovery by journal file interaction. Writing journal entries is 'best effort'. So if the hard drive becomes full, the attempt to write a journal entry may fail. Note on use_domain- The db_berkeley module will attempt natural joins when performing a query. This is basically a lexigraphical string compare using the keys provided. In most places in the db_berkeley dbschema (unless you customize), the domainname is identified as a natural key. Consider an example where use_domain = 0. In table subscriber, the db will be keying on 'username|NULL' because the default value will be used when that key column is not provided. This effectivly means that later queries must consistently use the username (w.o domain) in order to find a result to that particular subscriber query. The main point is 'use_domain' can not be changed once the db_berkeley is setup.
Database Schema and Metadata All Berkeley DB tables are created via the opensipsdbctl script. This section provides details as to the content and format of the DB file upon creation. Since the Berkeley DB stores key value pairs, the database is seeded with a few meta-data rows . The keys to these rows must begin with 'METADATA'. Here is an example of table meta-data, taken from the table 'version'. Note on reserved character- The '|' pipe character is used as a record delimiter within the Berkeley DB implementation and must not be present in any DB field. METADATA_COLUMNS METADATA_COLUMNS table_name(str) table_version(int) METADATA_KEY 0 In the above example, the row METADATA_COLUMNS defines the column names and type, and the row METADATA_KEY defines which column(s) form the key. Here the value of 0 indicates that column 0 is the key(ie table_name). With respect to column types, the db_berkeley modules only has the following types: string, str, int, double, and datetime. The default type is string, and is used when one of the others is not specified. The columns of the meta-data are delimited by whitespace. The actual column data is stored as a string value, and delimited by the '|' pipe character. Since the code tokenizes on this delimiter, it is important that this character not appear in any valid data field. The following is the output of the 'db_berkeley.sh dump version' command. It shows contents of table 'version' in plain text. contents of version table VERSION=3 format=print type=hash h_nelem=21 db_pagesize=4096 HEADER=END METADATA_READONLY 1 address| address|3 aliases| aliases|1004 dbaliases| dbaliases|1 domain| domain|1 speed_dial| speed_dial|2 subscriber| subscriber|6 uri| uri|1 METADATA_COLUMNS table_name(str) table_version(int) METADATA_KEY 0 acc| acc|4 grp| grp|2 location| location|1004 missed_calls| missed_calls|3 re_grp| re_grp|1 silo| silo|5 trusted| trusted|4 usr_preferences| usr_preferences|2 DATA=END
METADATA_COLUMNS (required) The METADATA_COLUMNS row contains the column names and types. Each is space delimited. Here is an example of the data taken from table subscriber : METADATA_COLUMNS METADATA_COLUMNS username(str) domain(str) password(str) ha1(str) ha1b(str) first_name(str) last_name(str) email_address(str) datetime_created(datetime) timezone(str) rpid(str) Related (hardcoded) limitations: maximum of 32 columns per table. maximum tablename size is 64. maximum data length is 2048 Currently supporting these five types: str, datetime, int, double, string.
METADATA_KEYS (required) The METADATA_KEYS row indicates the indexes of the key columns, with respect to the order specified in METADATA_COLUMNS. Here is an example taken from table subscriber that brings up a good point: METADATA_KEYS METADATA_KEY 0 1 The point is that both the username and domain name are require as the key to this record. Thus, usrloc modparam use_domain = 1 must be set for this to work.
METADATA_READONLY (optional) The METADATA_READONLY row contains a boolean 0 or 1. By default, its value is 0. On startup the DB will open initially as read-write (loads metadata) and then if this is set=1, it will close and reopen as read only (ro). I found this useful because readonly has impacts on the internal db locking etc.
METADATA_LOGFLAGS (optional) The METADATA_LOGFLAGS row contains a bitfield that customizes the journaling on a per table basis. If not present the default value is taken as 0. Here are the masks so far (taken from bdb_lib.h): METADATA_LOGFLAGS #define JLOG_NONE 0 #define JLOG_INSERT 1 #define JLOG_DELETE 2 #define JLOG_UPDATE 4 #define JLOG_STDOUT 8 #define JLOG_SYSLOG 16 This means that if you want to journal INSERTS to local file and syslog the value should be set to 1+16=17. Or if you do not want to journal at all, set this to 0.
DB Maintaince Script : opensipsdbctl Use the opensipsdbctl script for maintaining OpenSIPS Berkeley DB tables. This script assumes you have DBENGINE and DB_PATH setup correctly in opensipsctlrc. Note Unsupported commands are- backup, restore, migrate, copy, serweb. opensipsdbctl usage: opensipsdbctl create opensipsdbctl presence opensipsdbctl extra opensipsdbctl drop opensipsdbctl reinit opensipsdbctl bdb list (lists the underlying db files in DB_PATH) opensipsdbctl bdb cat db (prints the contents of db file to STDOUT in plain-text) opensipsdbctl bdb swap db (installs db.new by db -> db.old; db.new -> db) opensipsdbctl bdb append db datafile (appends data to a new instance of db; output DB_PATH/db.new) opensipsdbctl bdb newappend db datafile (appends data to a new instance of db; output DB_PATH/db.new)
DB Recovery : bdb_recover The db_berkeley module uses the Concurrent Data Store (CDS) architecture. As such, no transaction or journaling is provided by the DB natively. The application bdb_recover is specifically written to recover data from journal files that OpenSIPS creates. The bdb_recover application requires an additional text file that contains the table schema. The schema is loaded with the '-s' option and is required for all operations. Provide the path to the db_berkeley plain-text schema files. By default, these install to '/usr/local/share/opensips/db_berkeley/opensips/'. The '-h' home option is the DB_PATH path. Unlike the Berkeley utilities, this application does not look for the DB_PATH environment variable, so you have to specify it. If not specified, it will assume the current working directory. The last argument is the operation. There are fundamentally only two operations- create and recover. The following illustrates the four operations available to the administrator. bdb_recover usage usage: ./bdb_recover -s schemadir [-h home] [-c tablename] This will create a brand new DB file with metadata. usage: ./bdb_recover -s schemadir [-h home] [-C all] This will create all the core tables, each with metadata. usage: ./bdb_recover -s schemadir [-h home] [-r journal-file] This will rebuild a DB and populate it with operation from journal-file. The table name is embedded in the journal-file name by convention. usage: ./bdb_recover -s schemadir [-h home] [-R lastN] This will iterate over all core tables enumerated. If journal files exist in 'home', a new DB file will be created and populated with the data found in the last N files. The files are 'replayed' in chronological order (oldest to newest). This allows the administrator to rebuild the db with a subset of all possible operations if needed. For example, you may only be interested in the last hours data in table location. Important note- A corrupted DB file must be moved out of the way before bdb_recover is executed.
Known Limitations The Berkeley DB does not nativly support an autoincrement (or sequence) mechanism. Consequently, this version does not support surragate keys in dbschema. These are the id columns in the tables.
opensips-2.2.2/modules/db_cachedb/000077500000000000000000000000001300170765700170515ustar00rootroot00000000000000opensips-2.2.2/modules/db_cachedb/Makefile000066400000000000000000000001471300170765700205130ustar00rootroot00000000000000include ../../Makefile.defs auto_gen= NAME=db_cachedb.so LIBS= DEFS+= include ../../Makefile.modules opensips-2.2.2/modules/db_cachedb/README000066400000000000000000000107131300170765700177330ustar00rootroot00000000000000db_cachedb Module Vladut-Stefan Paiu OpenSIPS Solutions Edited by Vladut-Stefan Paiu Copyright © 2013 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. The idea 1.1.2. OpenSIPS Modules 1.1.3. External Libraries or Applications 1.2. Exported Parameters 1.2.1. cachedb_url (str) 1.3. Examples of Usage 1.3.1. Distributed Subscriber Base 1.4. Current Limitations 1.4.1. CacheDB modules integration 1.4.2. Extensive Testing Needed 1.4.3. CacheDB Specific 'schema' and other incompatibilities List of Examples 1.1. Set cachedb_url parameter 1.2. OpenSIPS CFG Snippet for using DB_CACHEDB Chapter 1. Admin Guide 1.1. Overview 1.1. Overview 1.1.1. The idea The db_cachedb module will expose the same front db api, however it will run on top of a NoSQL back-end, emulating the SQL calls to the back-end specific queries. Thus, any OpenSIPS module that would regularily need a regular SQL-based database, will now be able to run over a NoSQL back-end, allowing for a much easier distribution and integration of the currently existing OpenSIPS modules in a distributed environment. 1.1.2. OpenSIPS Modules The following modules must be loaded before this module: * At least one NoSQL cachedb_* module. 1.1.3. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.2. Exported Parameters 1.2.1. cachedb_url (str) The URL for the CacheDB back-end to be used. It can be set more than one time. Example 1.1. Set cachedb_url parameter ... modparam("db_cachedb","cachedb_url","mongodb:mycluster://127.0.0.1:27017 /db.col") ... 1.3. Examples of Usage 1.3.1. Distributed Subscriber Base In order to achieve such a setup, one would have to set the db_url parameter of the auth_db module to point to the DB_CACHEDB URL. Example 1.2. OpenSIPS CFG Snippet for using DB_CACHEDB loadmodule "auth_db.so" modparam("auth_db", "load_credentials", "$avp(user_rpid)=rpid") loadmodule "db_cachedb.so" loadmodule "cachedb_mongodb.so" ... modparam("db_cachedb","cachedb_url","mongodb:mycluster://127.0.0.1:27017 /my_db.col") modparam("auth_db","db_url","cachedb://mongodb:mycluster") ... With such a setup, the auth_db module will load the subscribers from the MongoDB cluster, in the 'my_db' database, in the 'subscriber' collection. The same mechanism/setup can be used to run other modules ( like usrloc, dialog, permissions, drouting, etc ) on top of a cachedb cluster. 1.4. Current Limitations 1.4.1. CacheDB modules integration Currently the only cachedb_* module that implements this functionality is the cachedb_mongodb module, so currently you can only emulate SQL queries to a MongoDB instance/cluster. There are plans to also extend this functionality to other cachedb_* backends, like Cassandra and CouchBase. 1.4.2. Extensive Testing Needed Since there are many OpenSIPS modules that currently use the DB interface, it wasn't feasible to test all scenarios with all modules, and there still might be some incompatibilities. The module was tested with some regularily used modules ( like usrloc, dialog, permissions, drouting ), but more testing is very much welcome, and feedback is appreciated. 1.4.3. CacheDB Specific 'schema' and other incompatibilities Since the NoSQL backends do not usually have a strict schema involved, we do not provide scripts for creating such schemas, since the insertion ops will trigger the dynamically creation of the schema and info. Still, a specific data collection needs to be present, and that is the equivalent of the 'version' table from the SQL. Since most modules check the version table at the module setup, it's the user's responsability to setup such a 'version' collection in the respective NoSQL back-end. For example, for the MongoDB cluster, 'version' is a reserved keyword, so one would have to change the default version table that OpenSIPS uses ( via the 'db_version_table' global parameter ) and then manually insert the version number with something like db.my_version_table.insert({table_version : NumberInt(5), table_name : "address"}) opensips-2.2.2/modules/db_cachedb/db_cachedb.c000066400000000000000000000064221300170765700212370ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2013-02-xx created (vlad-paiu) */ #include "../../sr_module.h" #include "../../db/db.h" #include "dbase.h" #include "../../mi/mi.h" #include "../../pt.h" #include #include "../../mem/shm_mem.h" #include static int mod_init(void); static void destroy(void); int db_cachedb_bind_api(const str* mod, db_func_t *dbb); struct cachedb_url *db_cachedb_script_urls = NULL; int set_connection(unsigned int type, void *val) { return cachedb_store_url(&db_cachedb_script_urls,(char *)val); } /* * Virtual database module interface */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)db_cachedb_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"cachedb_url", STR_PARAM|USE_FUNC_PARAM,(void*)&set_connection}, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_CACHEDB, NULL, DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "db_cachedb", MOD_TYPE_SQLDB, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, NULL, params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ destroy, /* destroy function */ 0 /* per-child init function */ }; int db_cachedb_bind_api(const str* mod, db_func_t *dbb) { LM_DBG("BINDING API for : %.*s\n", mod->len, mod->s); if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->use_table = db_cachedb_use_table; dbb->init = db_cachedb_init; dbb->close = db_cachedb_close; dbb->query = db_cachedb_query; dbb->free_result = db_cachedb_free_result; dbb->insert = db_cachedb_insert; dbb->delete = db_cachedb_delete; dbb->update = db_cachedb_update; dbb->raw_query = db_cachedb_raw_query; return 0; } /** * init module function */ static int mod_init(void) { LM_NOTICE("initializing module db_cachedb ...\n"); return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module db_cachedb ...\n"); return; } opensips-2.2.2/modules/db_cachedb/dbase.c000066400000000000000000000117501300170765700202770ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2013-02-xx created (vlad-paiu) */ #include "../../mem/mem.h" #include "../../dprint.h" #include "../../db/db_query.h" #include "../../db/db_ut.h" #include "../../db/db_id.h" #include "../../timer.h" #include "../../cachedb/cachedb.h" #include "dbase.h" extern struct cachedb_url *db_cachedb_script_urls; db_con_t* db_cachedb_init(const str* _url) { char *p; int len; struct cachedb_url *it; cachedb_funcs cdbf; cachedb_con *cdbc = NULL; struct db_cachedb_con* ptr; db_con_t *res; if (!_url) { LM_ERR("invalid parameter value\n"); return 0; } res = pkg_malloc(sizeof(db_con_t)); if (!res) { LM_ERR("No more pkg mem\n"); return NULL; } memset(res,0,sizeof(db_con_t)); p=_url->s+sizeof("cachedb:/"); len=_url->len-sizeof("cachedb:/"); for (it=db_cachedb_script_urls;it;it=it->next) { if (memcmp(it->url.s,p,len) == 0) { LM_DBG("Found matching URL : [%.*s]\n",it->url.len,it->url.s); if (cachedb_bind_mod(&it->url,&cdbf) < 0) { LM_ERR("Cannot bind cachedb functions for URL [%.*s]\n", it->url.len,it->url.s); return NULL; } cdbc = cdbf.init(&it->url); if (cdbc == NULL) { LM_ERR("Failed to connect to the cachedb back-end\n"); return NULL; } ptr = pkg_malloc(sizeof(struct db_cachedb_con)); if (!ptr) { LM_ERR("no private memory left\n"); pkg_free(res); return 0; } memset(ptr, 0, sizeof(struct db_cachedb_con)); ptr->ref = 1; ptr->cdbc = cdbc; ptr->cdbf = cdbf; res->tail = (unsigned long)ptr; LM_DBG("Successfully initiated connection to [%.*s] \n",len,p); return res; } } LM_ERR("No match for url [%.*s]\n",_url->len,_url->s); return NULL; } void db_cachedb_close(db_con_t* _h) { struct db_cachedb_con* ptr = (struct db_cachedb_con *)_h->tail; LM_DBG("closing db_cachedb con \n"); ptr->cdbf.destroy(ptr->cdbc); pkg_free(_h); } int db_cachedb_free_result(db_con_t* _h, db_res_t* _r) { struct db_cachedb_con* ptr = (struct db_cachedb_con *)_h->tail; if (ptr->cdbf.db_free_trans == NULL) { LM_ERR("The selected NoSQL driver cannot convert free result queries\n"); return -1; } return ptr->cdbf.db_free_trans(ptr->cdbc,_r); } int db_cachedb_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r) { struct db_cachedb_con* ptr = (struct db_cachedb_con *)_h->tail; if (ptr->cdbf.db_query_trans == NULL) { LM_ERR("The selected NoSQL driver cannot convert select queries\n"); return -1; } return ptr->cdbf.db_query_trans(ptr->cdbc,_h->table,_k,_op,_v,_c,_n,_nc,_o,_r); } int db_cachedb_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { struct db_cachedb_con* ptr = (struct db_cachedb_con *)_h->tail; if (ptr->cdbf.db_insert_trans == NULL) { LM_ERR("The selected NoSQL driver cannot convert insert queries\n"); return -1; } return ptr->cdbf.db_insert_trans(ptr->cdbc,_h->table,_k,_v,_n); } int db_cachedb_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n) { struct db_cachedb_con* ptr = (struct db_cachedb_con *)_h->tail; if (ptr->cdbf.db_delete_trans == NULL) { LM_ERR("The selected NoSQL driver cannot convert delete queries\n"); return -1; } return ptr->cdbf.db_delete_trans(ptr->cdbc,_h->table,_k,_o,_v,_n); } int db_cachedb_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un) { struct db_cachedb_con* ptr = (struct db_cachedb_con *)_h->tail; if (ptr->cdbf.db_update_trans == NULL) { LM_ERR("The selected NoSQL driver cannot convert update queries\n"); return -1; } return ptr->cdbf.db_update_trans(ptr->cdbc,_h->table,_k,_o,_v,_uk,_uv,_n,_un); } int db_cachedb_use_table(db_con_t* _h, const str* _t) { if (!_h || !_t || !_t->s) { LM_ERR("invalid parameter value %p, %p\n", _h, _t); return -1; } CON_TABLE(_h) = _t; return 0; } int db_cachedb_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r) { /* This will most likely never be supported :( */ LM_ERR("RAW query not support by db_cachedb \n"); return -1; } opensips-2.2.2/modules/db_cachedb/dbase.h000066400000000000000000000055151300170765700203060ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2013-02-xx created (vlad-paiu) */ #ifndef DB_CACHEDB_DBASE_H #define DB_CACHEDB_DBASE_H #include "../../db/db_val.h" #include "../../cachedb/cachedb.h" #include "../../str.h" struct db_cachedb_con { struct db_id* id; /**< Connection identifier */ unsigned int ref; /**< Reference count */ struct pool_con *async_pool; /**< Subpool of identical database handles */ int no_transfers; /**< Number of async queries to this backend */ struct db_transfer *transfers; /**< Array of ongoing async operations */ struct pool_con *next; /**< Next element in the pool (different db_id) */ cachedb_funcs cdbf; /* pointers to the NoSQL specific functions */ cachedb_con *cdbc; /* connection to actual NoSQL back-end */ }; /* * Initialize database connection */ db_con_t* db_cachedb_init(const str* _sqlurl); /* * Close a database connection */ void db_cachedb_close(db_con_t* _h); /* * Free all memory allocated by get_result */ int db_cachedb_free_result(db_con_t* _h, db_res_t* _r); /* * Do a query */ int db_cachedb_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r); /* * Insert a row into table */ int db_cachedb_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /* * Delete a row from table */ int db_cachedb_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n); /* * Update a row in table */ int db_cachedb_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un); /* * Store name of table that will be used by * subsequent database functions */ int db_cachedb_use_table(db_con_t* _h, const str* _t); /* * Raw SQL query */ int db_cachedb_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r); #endif /* DB_CACHEDB_DBASE_H */ opensips-2.2.2/modules/db_cachedb/doc/000077500000000000000000000000001300170765700176165ustar00rootroot00000000000000opensips-2.2.2/modules/db_cachedb/doc/db_cachedb.xml000066400000000000000000000020561300170765700223610ustar00rootroot00000000000000 %docentities; ]> db_cachedb Module &osipsname; Vladut-Stefan Paiu OpenSIPS Solutions
vladpaiu@opensips.org http://www.opensips.org
Vladut-Stefan Paiu
vladpaiu@opensips.org
2013 &osipssol;
&admin;
opensips-2.2.2/modules/db_cachedb/doc/db_cachedb_admin.xml000066400000000000000000000135101300170765700235260ustar00rootroot00000000000000 &adminguide;
Overview
The idea The db_cachedb module will expose the same front db api, however it will run on top of a NoSQL back-end, emulating the SQL calls to the back-end specific queries. Thus, any OpenSIPS module that would regularily need a regular SQL-based database, will now be able to run over a NoSQL back-end, allowing for a much easier distribution and integration of the currently existing OpenSIPS modules in a distributed environment.
Dependencies
&osips; Modules The following modules must be loaded before this module: At least one NoSQL cachedb_* module.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>cachedb_url</varname> (str) The URL for the CacheDB back-end to be used. It can be set more than one time. Set <varname>cachedb_url</varname> parameter ... modparam("db_cachedb","cachedb_url","mongodb:mycluster://127.0.0.1:27017/db.col") ...
Examples of Usage
<varname>Distributed Subscriber Base</varname> In order to achieve such a setup, one would have to set the db_url parameter of the auth_db module to point to the DB_CACHEDB URL. OpenSIPS CFG Snippet for using DB_CACHEDB loadmodule "auth_db.so" modparam("auth_db", "load_credentials", "$avp(user_rpid)=rpid") loadmodule "db_cachedb.so" loadmodule "cachedb_mongodb.so" ... modparam("db_cachedb","cachedb_url","mongodb:mycluster://127.0.0.1:27017/my_db.col") modparam("auth_db","db_url","cachedb://mongodb:mycluster") ... With such a setup, the auth_db module will load the subscribers from the MongoDB cluster, in the 'my_db' database, in the 'subscriber' collection. The same mechanism/setup can be used to run other modules ( like usrloc, dialog, permissions, drouting, etc ) on top of a cachedb cluster.
Current Limitations
<varname>CacheDB modules integration</varname> Currently the only cachedb_* module that implements this functionality is the cachedb_mongodb module, so currently you can only emulate SQL queries to a MongoDB instance/cluster. There are plans to also extend this functionality to other cachedb_* backends, like Cassandra and CouchBase.
<varname>Extensive Testing Needed</varname> Since there are many OpenSIPS modules that currently use the DB interface, it wasn't feasible to test all scenarios with all modules, and there still might be some incompatibilities. The module was tested with some regularily used modules ( like usrloc, dialog, permissions, drouting ), but more testing is very much welcome, and feedback is appreciated.
<varname>CacheDB Specific 'schema' and other incompatibilities</varname> Since the NoSQL backends do not usually have a strict schema involved, we do not provide scripts for creating such schemas, since the insertion ops will trigger the dynamically creation of the schema and info. Still, a specific data collection needs to be present, and that is the equivalent of the 'version' table from the SQL. Since most modules check the version table at the module setup, it's the user's responsability to setup such a 'version' collection in the respective NoSQL back-end. For example, for the MongoDB cluster, 'version' is a reserved keyword, so one would have to change the default version table that OpenSIPS uses ( via the 'db_version_table' global parameter ) and then manually insert the version number with something like db.my_version_table.insert({table_version : NumberInt(5), table_name : "address"})
opensips-2.2.2/modules/db_flatstore/000077500000000000000000000000001300170765700175035ustar00rootroot00000000000000opensips-2.2.2/modules/db_flatstore/Makefile000066400000000000000000000002621300170765700211430ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=db_flatstore.so include ../../Makefile.modules opensips-2.2.2/modules/db_flatstore/README000066400000000000000000000147331300170765700203730ustar00rootroot00000000000000Flatstore Module Jan Janak FhG FOKUS Edited by Jan Janak Copyright © 2004, 2005 FhG FOKUS Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Rotating Log Files 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. flush (integer) 1.3.2. delimiter (char) 1.3.3. suffix (string) 1.3.4. prefix (string) 1.3.5. single_file (integer) 1.4. Exported Functions 1.5. Exported MI Functions 1.5.1. flat_rotate 2. Developer Guide List of Examples 1.1. Set “flush†parameter 1.2. Set “delimiter†parameter 1.3. Set “suffix†parameter 1.4. Set “prefix†parameter 1.5. Set “single_file†parameter Chapter 1. Admin Guide 1.1. Overview Flatstore is one of so-called OpenSIPS database modules. It does not export any functions executable from the configuration scripts, but it exports a subset of functions from the database API and thus other module can use it instead of, for example, mysql module. The module does not export all functions of the database API, it supports only one function, insert. The module is limited but very fast. It is especially suitable for storing accounting information on sites with extremely high traffic. If MySQL is too slow or if you get a huge amount of accounting data then you can consider using this module. Note that the acc module is the only module that was tested with flastore. The format of the files produced by this module is plain text. Each line consists of several fields, fields are separated by default by the | character. New information is always appended at the end of the file, searching, deleting and updating of existing data is not supported by the module. The acc module can be configured to use flatstore module as database backend using the db_url_parameter: modparam("acc", "db_url", "flatstore:/var/log/acc") This configuration options tells acc module that it should use the flatstore module and the flatstore module should create all files in /var/log/acc directory. The directory must exist and OpenSIPS processes must have permissions to create files in that directory. Name of files in that directory will follow the following pattern: [_] For example, without setting any module parameter, the entries writen by OpenSIPS process 8 into acc table would be written in file acc_8.log. For each table there will be several files, one file for every OpenSIPS process that wrote some data into that table. The main reason why there are several files for each table is that it is much faster to have one file per process, because it does not require any locking and thus OpenSIPS processes will not block each other. To get the complete data for a table you can simply concatenate the contents of files with the same table name but different process id. Alternatively, you can use the single_file parameter, and all processes will dump the data into the same file. Note that this will induce some latency. 1.1.1. Rotating Log Files There is a new OpenSIPS MI (management interface) command called flat_rotate. When OpenSIPS receives the command then it will close and reopen all files used by flatstore module. The rotation itself has to be done by another application (such as logrotate). Follow these steps to rotate files generated by flatstore module: * Rename the files that you want to rotate: cd /var/log/acc mv acc_1.log acc_1.log.20050605 mv acc_2.log acc_2.log.20050605 mv acc_4.log acc_3.log.20050605 ... Note that at this point OpenSIPS will still be writing all data into the renamed files. * Send OpenSIPS the MI command to close and reopen the renamed files. For example, using FIFO: opensipsctl fifo flat_rotate This will force OpenSIPS to close the renamed files and open new ones with original names, such as acc_1.log. New files will be open at the point when OpenSIPS has some data to write. It is normal that the files will be not created immediately if there is no traffic on the proxy server. Note that the suffix and prefix parameters are re-evaluated each time the flat_rotate command is issued. Therefore, after a rotate command, it is possible to open a different file than previous one. * Move the renamed files somewhere else and process them. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. flush (integer) Enable or disable flushing after each write. Default value is 1. Example 1.1. Set “flush†parameter ... modparam("db_flatstore", "flush", 0) ... 1.3.2. delimiter (char) Delimiter used to separate the values. Default value is '|'. Example 1.2. Set “delimiter†parameter ... modparam("db_flatstore", "delimiter", ";") ... 1.3.3. suffix (string) The suffix appended to the table name. Can be a pseudo variable. Default value is ".log". Example 1.3. Set “suffix†parameter ... modparam("db_flatstore", "suffix", "$time(%H)") ... 1.3.4. prefix (string) The table name prefix. Can be a pseudo variable. Defaul value is none. Example 1.4. Set “prefix†parameter ... modparam("db_flatstore", "prefix", "$time(%H)") ... 1.3.5. single_file (integer) Specifies if all the processes should dump the data into a single file. Default value is 0. Example 1.5. Set “single_file†parameter ... modparam("db_flatstore", "single_file", 1) ... 1.4. Exported Functions There are no function exported to routing script. 1.5. Exported MI Functions 1.5.1. flat_rotate It changes the name of the files where it is written. Name: flat_rotate Parameters: none MI FIFO Command Format: :flat_rotate:_reply_fifo_file_ _empty_line_ Chapter 2. Developer Guide The module implements the DB API. opensips-2.2.2/modules/db_flatstore/doc/000077500000000000000000000000001300170765700202505ustar00rootroot00000000000000opensips-2.2.2/modules/db_flatstore/doc/db_flatstore.xml000066400000000000000000000022411300170765700234410ustar00rootroot00000000000000 %docentities; ]> Flatstore Module &osipsname; Jan Janak FhG FOKUS
jan@iptel.org
Jan Janak
jan@iptel.org
2004 2005 FhG FOKUS $Revision: 5901 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/db_flatstore/doc/db_flatstore_admin.xml000066400000000000000000000171321300170765700246160ustar00rootroot00000000000000 &adminguide;
Overview Flatstore is one of so-called &osips; database modules. It does not export any functions executable from the configuration scripts, but it exports a subset of functions from the database API and thus other module can use it instead of, for example, mysql module. The module does not export all functions of the database API, it supports only one function, insert. The module is limited but very fast. It is especially suitable for storing accounting information on sites with extremely high traffic. If MySQL is too slow or if you get a huge amount of accounting data then you can consider using this module. Note that the acc module is the only module that was tested with flastore. The format of the files produced by this module is plain text. Each line consists of several fields, fields are separated by default by the | character. New information is always appended at the end of the file, searching, deleting and updating of existing data is not supported by the module. The acc module can be configured to use flatstore module as database backend using the db_url_parameter: modparam("acc", "db_url", "flatstore:/var/log/acc") This configuration options tells acc module that it should use the flatstore module and the flatstore module should create all files in /var/log/acc directory. The directory must exist and &osips; processes must have permissions to create files in that directory. Name of files in that directory will follow the following pattern: <prefix><table_name>[_<process_name>]<suffix> For example, without setting any module parameter, the entries writen by &osips; process 8 into acc table would be written in file acc_8.log. For each table there will be several files, one file for every &osips; process that wrote some data into that table. The main reason why there are several files for each table is that it is much faster to have one file per process, because it does not require any locking and thus &osips; processes will not block each other. To get the complete data for a table you can simply concatenate the contents of files with the same table name but different process id. Alternatively, you can use the single_file parameter, and all processes will dump the data into the same file. Note that this will induce some latency.
Rotating Log Files There is a new &osips; MI (management interface) command called flat_rotate. When &osips; receives the command then it will close and reopen all files used by flatstore module. The rotation itself has to be done by another application (such as logrotate). Follow these steps to rotate files generated by flatstore module: Rename the files that you want to rotate: cd /var/log/acc mv acc_1.log acc_1.log.20050605 mv acc_2.log acc_2.log.20050605 mv acc_4.log acc_3.log.20050605 ... Note that at this point &osips; will still be writing all data into the renamed files. Send &osips; the MI command to close and reopen the renamed files. For example, using FIFO: opensipsctl fifo flat_rotate This will force &osips; to close the renamed files and open new ones with original names, such as acc_1.log. New files will be open at the point when &osips; has some data to write. It is normal that the files will be not created immediately if there is no traffic on the proxy server. Note that the suffix and prefix parameters are re-evaluated each time the flat_rotate command is issued. Therefore, after a rotate command, it is possible to open a different file than previous one. Move the renamed files somewhere else and process them.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>flush</varname> (integer) Enable or disable flushing after each write. Default value is 1. Set <quote>flush</quote> parameter ... modparam("db_flatstore", "flush", 0) ...
<varname>delimiter</varname> (char) Delimiter used to separate the values. Default value is '|'. Set <quote>delimiter</quote> parameter ... modparam("db_flatstore", "delimiter", ";") ...
<varname>suffix</varname> (string) The suffix appended to the table name. Can be a pseudo variable. Default value is ".log". Set <quote>suffix</quote> parameter ... modparam("db_flatstore", "suffix", "$time(%H)") ...
<varname>prefix</varname> (string) The table name prefix. Can be a pseudo variable. Defaul value is none. Set <quote>prefix</quote> parameter ... modparam("db_flatstore", "prefix", "$time(%H)") ...
<varname>single_file</varname> (integer) Specifies if all the processes should dump the data into a single file. Default value is 0. Set <quote>single_file</quote> parameter ... modparam("db_flatstore", "single_file", 1) ...
Exported Functions There are no function exported to routing script.
Exported MI Functions
<function moreinfo="none">flat_rotate</function> It changes the name of the files where it is written. Name: flat_rotate Parameters: none MI FIFO Command Format: :flat_rotate:_reply_fifo_file_ _empty_line_
opensips-2.2.2/modules/db_flatstore/doc/db_flatstore_devel.xml000066400000000000000000000002371300170765700246230ustar00rootroot00000000000000 &develguide; The module implements the DB API. opensips-2.2.2/modules/db_flatstore/flat_con.c000066400000000000000000000100761300170765700214400ustar00rootroot00000000000000/* * Flastore module connection structure * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../ut.h" #include "flatstore_mod.h" #include "flat_con.h" /* returns a pkg_malloc'ed file name */ static char* get_name(struct flat_id* id) { char* buf; int buf_len; char* num, *ptr; int num_len; int total_len; str prefix, suffix; static struct sip_msg flat_dummy_msg; buf_len=pathmax(); if (!id) { LM_ERR("invalid parameter value\n"); return 0; } if (flat_suffix) { if (fixup_get_svalue(&flat_dummy_msg, flat_suffix, &suffix) < 0) { LM_ERR("bad suffix - using default \"%s\"\n", FILE_SUFFIX); suffix.s = FILE_SUFFIX; suffix.len = FILE_SUFFIX_LEN; } } else { suffix.s = 0; suffix.len = 0; } if (flat_prefix) { if (fixup_get_svalue(&flat_dummy_msg, flat_prefix, &prefix) < 0) { LM_ERR("bad prefix - discarding\n"); prefix.s = 0; prefix.len = 0; } } else { prefix.s = 0; prefix.len = 0; } total_len = id->dir.len + 1 /* / */ + prefix.len /* table prefix */ + id->table.len /* table name */ + suffix.len /* table suffix */ + flat_single_file ? 2 : 1 /* _ needed? + '\0' */; /* without pid */ if (buf_lendir.s, id->dir.len); ptr += id->dir.len; *ptr++ = '/'; memcpy(ptr, prefix.s, prefix.len); ptr += prefix.len; memcpy(ptr, id->table.s, id->table.len); ptr += id->table.len; if (!flat_single_file) { *ptr++ = '_'; num = int2str(flat_pid, &num_len); if (buf_len<(total_len+num_len)){ LM_ERR("the path is too long (%d and PATHMAX is" " %d)\n", total_len+num_len, buf_len); pkg_free(buf); return 0; } memcpy(ptr, num, num_len); ptr += num_len; } memcpy(ptr, suffix.s, suffix.len); ptr += suffix.len; *ptr = '\0'; return buf; } struct flat_con* flat_new_connection(struct flat_id* id) { char* fn; struct flat_con* res; if (!id) { LM_ERR("invalid parameter value\n"); return 0; } res = (struct flat_con*)pkg_malloc(sizeof(struct flat_con)); if (!res) { LM_ERR("no pkg memory left\n"); return 0; } memset(res, 0, sizeof(struct flat_con)); res->ref = 1; res->id = id; fn = get_name(id); if (fn==0){ LM_ERR("get_name() failed\n"); return 0; } res->file = fopen(fn, "a"); pkg_free(fn); /* we don't need fn anymore */ if (!res->file) { LM_ERR(" %s\n", strerror(errno)); pkg_free(res); return 0; } return res; } /* * Close the connection and release memory */ void flat_free_connection(struct flat_con* con) { if (!con) return; if (con->id) free_flat_id(con->id); if (con->file) { fclose(con->file); } pkg_free(con); } /* * Reopen a connection */ int flat_reopen_connection(struct flat_con* con) { char* fn; if (!con) { LM_ERR("invalid parameter value\n"); return -1; } if (con->file) { fclose(con->file); con->file = 0; fn = get_name(con->id); if (fn == 0) { LM_ERR("failed to get_name\n"); return -1; } con->file = fopen(fn, "a"); pkg_free(fn); if (!con->file) { LM_ERR("invalid parameter value\n"); return -1; } } return 0; } opensips-2.2.2/modules/db_flatstore/flat_con.h000066400000000000000000000033711300170765700214450ustar00rootroot00000000000000/* * Flatstore module connection structure * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _FLAT_CON_H #define _FLAT_CON_H #include #include #include "flat_id.h" struct flat_con { struct flat_id* id; /* Connection identifier */ int ref; /* Reference count */ FILE* file; /* File descriptor structure */ struct flat_con* next; /* Next connection in the pool */ }; /* * Some convenience wrappers */ #define CON_FILE(db_con) (((struct flat_con*)((db_con)->tail))->file) /* returns the filename of the table */ #define CON_FILENAME(db_con) (((struct flat_con*)((db_con)->tail))->id->table) /* * Create a new connection structure, * open the MySQL connection and set reference count to 1 */ struct flat_con* flat_new_connection(struct flat_id* id); /* * Close the connection and release memory */ void flat_free_connection(struct flat_con* con); /* * Reopen a connection */ int flat_reopen_connection(struct flat_con* con); #endif /* _FLAT_CON_H */ opensips-2.2.2/modules/db_flatstore/flat_id.c000066400000000000000000000040651300170765700212560ustar00rootroot00000000000000/* * Flatstore module connection identifier * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "../../dprint.h" #include "../../mem/mem.h" #include "flat_id.h" /* * Create a new connection identifier */ struct flat_id* new_flat_id(const str* dir, const str* table) { struct flat_id* ptr; if (!dir || !table || !dir->len || !table->len) { LM_ERR("invalid parameter(s)\n"); return 0; } ptr = (struct flat_id*)pkg_malloc(sizeof(struct flat_id) + dir->len + table->len); if (!ptr) { LM_ERR("no pkg memory left\n"); return 0; } memset(ptr, 0, sizeof(struct flat_id)); ptr->dir.s = (char *)(ptr + 1); ptr->dir.len = dir->len; memcpy(ptr->dir.s, dir->s, dir->len); ptr->table.s = ptr->dir.s + dir->len; ptr->table.len = table->len; memcpy(ptr->table.s, table->s, table->len); return ptr; } /* * Compare two connection identifiers */ unsigned char cmp_flat_id(struct flat_id* id1, struct flat_id* id2) { if (!id1 || !id2) return 0; if (id1->dir.len != id2->dir.len) return 0; if (id1->table.len != id2->table.len) return 0; if (memcmp(id1->dir.s, id2->dir.s, id1->dir.len)) return 0; if (memcmp(id1->table.s, id2->table.s, id1->table.len)) return 0; return 1; } /* * Free a connection identifier */ void free_flat_id(struct flat_id* id) { if (!id) return; pkg_free(id); } opensips-2.2.2/modules/db_flatstore/flat_id.h000066400000000000000000000024621300170765700212620ustar00rootroot00000000000000/* * Flatstore connection identifier * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _FLAT_ID_H #define _FLAT_ID_H #include "../../str.h" struct flat_id { str dir; /* Database directory */ str table; /* Name of table */ }; /* * Create a new connection identifier */ struct flat_id* new_flat_id(const str* dir, const str* table); /* * Compare two connection identifiers */ unsigned char cmp_flat_id(struct flat_id* id1, struct flat_id* id2); /* * Free a connection identifier */ void free_flat_id(struct flat_id* id); #endif /* _FLAT_ID_H */ opensips-2.2.2/modules/db_flatstore/flat_mi.c000066400000000000000000000021711300170765700212630ustar00rootroot00000000000000/* * Flatstore module MI interface * * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "flatstore_mod.h" #include "flat_mi.h" struct mi_root* mi_flat_rotate_cmd(struct mi_root* cmd_tree, void* param) { struct mi_root *rpl_tree; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if(rpl_tree == NULL) return rpl_tree; *flat_rotate = time(0); return rpl_tree; } opensips-2.2.2/modules/db_flatstore/flat_mi.h000066400000000000000000000020531300170765700212670ustar00rootroot00000000000000/* * Flatstore module MI interface * * Copyright (C) 2006 Voice Sistem RL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _FLATSTORE_MI_H_ #define _FLATSTORE_MI_H_ #include "../../mi/mi.h" #define MI_FLAT_ROTATE "flat_rotate" struct mi_root* mi_flat_rotate_cmd(struct mi_root* cmd, void* param); #endif /* _FLATSTORE_MI_H_ */ opensips-2.2.2/modules/db_flatstore/flat_pool.c000066400000000000000000000060201300170765700216240ustar00rootroot00000000000000/* * Flatstore module connection pool * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "../../dprint.h" #include "flat_pool.h" #include "flat_id.h" /* The head of the pool */ static struct flat_con* pool = 0; /* * Pid of the process that added the last * connection to the pool. This is used to * check for inherited database connections. */ static int pool_pid; /* * Get a connection from the pool, reuse existing * if possible, otherwise create a new one */ struct flat_con* flat_get_connection(const str* dir, const str* table) { struct flat_id* id; struct flat_con* ptr; int pid; if (!dir || !table) { LM_ERR("invalid parameter value\n"); return 0; } pid = getpid(); if (pool && (pool_pid != pid)) { LM_ERR("inherited open database connections, " "this is not a good idea\n"); return 0; } pool_pid = pid; id = new_flat_id(dir, table); if (!id) return 0; ptr = pool; while (ptr) { if (cmp_flat_id(id, ptr->id)) { LM_DBG("connection found in the pool\n"); ptr->ref++; free_flat_id(id); return ptr; } ptr = ptr->next; } LM_DBG("connection not found in the pool\n"); ptr = flat_new_connection(id); if (!ptr) { free_flat_id(id); return 0; } ptr->next = pool; pool = ptr; return ptr; } /* * Release a connection, the connection will be left * in the pool if ref count != 0, otherwise it * will be delete completely */ void flat_release_connection(struct flat_con* con) { struct flat_con* ptr; if (!con) return; if (con->ref > 1) { /* There are still other users, just * decrease the reference count and return */ LM_DBG("connection still kept in the pool\n"); con->ref--; return; } LM_DBG("removing connection from the pool\n"); if (pool == con) { pool = pool->next; } else { ptr = pool; while(ptr) { if (ptr->next == con) break; ptr = ptr->next; } if (!ptr) { LM_ERR("weird, connection not found in the pool\n"); } else { /* Remove the connection from the pool */ ptr->next = con->next; } } flat_free_connection(con); } /* * Close and reopen all opened connections */ int flat_rotate_logs(void) { struct flat_con* ptr; ptr = pool; while(ptr) { if (flat_reopen_connection(ptr)) { return -1; } ptr = ptr->next; } return 0; } opensips-2.2.2/modules/db_flatstore/flat_pool.h000066400000000000000000000025451300170765700216410ustar00rootroot00000000000000/* * Flatstore module connection pool * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _FLAT_POOL_H #define _FLAT_POOL_H #include "flat_con.h" /* * Get a connection from the pool, reuse existing * if possible, otherwise create a new one */ struct flat_con* flat_get_connection(const str* dir, const str* table); /* * Release a connection, the connection will be left * in the pool if ref count != 0, otherwise it * will be delete completely */ void flat_release_connection(struct flat_con* con); /* * Close and reopen all opened connections */ int flat_rotate_logs(void); #endif /* _FLAT_POOL_H */ opensips-2.2.2/modules/db_flatstore/flatstore.c000066400000000000000000000231721300170765700216570ustar00rootroot00000000000000/* * Flatstore module interface * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) */ #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "flat_pool.h" #include "flat_con.h" #include "flatstore_mod.h" #include "flatstore.h" static int parse_flat_url(const str* url, str* path) { struct stat st_buf; if (!url || !url->s || !path) { LM_ERR("invalid parameter value\n"); return -1; } path->s = strchr(url->s, ':') + 1; path->len = strlen(path->s); /* check if the directory exists */ if (stat(path->s, &st_buf) < 0) { LM_ERR("cannot stat %s: %s [%d]\n", path->s, strerror(errno), errno); return -1; } if (!S_ISDIR (st_buf.st_mode)) { LM_ERR("%s is not a directory\n", path->s); return -1; } return 0; } /* * Initialize database module * No function should be called before this */ db_con_t* flat_db_init(const str* url) { db_con_t* res; str* path; if (!url || !url->s) { LM_ERR("invalid parameter value\n"); return 0; } /* We do not know the name of the table (and the name of the corresponding * file) at this point, we will simply store the path taken from the url * parameter in the table variable, flat_use_table will then pick that * value and open the file */ /* as the table (path) is a substring of the received str, we need to * allocate a separate str struct for it -bogdan */ res = pkg_malloc(sizeof(db_con_t)+sizeof(struct flat_con*)+sizeof(str)); if (!res) { LM_ERR("no pkg memory left\n"); return 0; } memset(res, 0, sizeof(db_con_t) + sizeof(struct flat_con*) + sizeof(str)); path = (str*)(((char*)res) + sizeof(db_con_t) + sizeof(struct flat_con*)); if (parse_flat_url(url, path) < 0) { pkg_free(res); return 0; } res->table = path; return res; } /* * Store name of table that will be used by * subsequent database functions */ int flat_use_table(db_con_t* h, const str* t) { struct flat_con* con; if (!h || !t || !t->s) { LM_ERR("invalid parameter value\n"); return -1; } if (!CON_TAIL(h) || !(CON_FILENAME(h).len == t->len && !memcmp(CON_FILENAME(h).s, t->s, t->len))) { if (CON_TAIL(h)) { /* Decrement the reference count * of the connection but do not remove * it from the connection pool */ con = (struct flat_con*)CON_TAIL(h); con->ref--; } CON_TAIL(h) = (unsigned long) flat_get_connection(CON_TABLE(h), t); if (!CON_TAIL(h)) { return -1; } } return 0; } void flat_db_close(db_con_t* h) { struct flat_con* con; if (!h) { LM_ERR("invalid parameter value\n"); return; } con = (struct flat_con*)CON_TAIL(h); if (con) { flat_release_connection(con); } pkg_free(h); } #ifdef FLAT_USE_FILE_LOCK /* used for file locking */ static struct flock flat_file_lock = { 0, SEEK_SET, 0, 0, 0 }; static inline void FLAT_LOCK(int f) { if (flat_single_file) return; flat_file_lock.l_type = F_WRLCK; if (fcntl(f, F_SETLKW, &flat_file_lock) < 0) LM_CRIT("cannot lock file (%s:%d)\n", strerror(errno), errno); } static inline void FLAT_UNLOCK(int f) { if (flat_single_file) return; flat_file_lock.l_type = F_UNLCK; if (fcntl(f, F_SETLK, &flat_file_lock) < 0) LM_CRIT("cannot unlock file (%s:%d)\n", strerror(errno), errno); } #else #define FLAT_LOCK(f) #define FLAT_UNLOCK(f) #endif /* FLAT_USE_FILE_LOCK */ static struct iovec *flat_iov = 0; static int flat_iov_len = 0; static str flat_iov_buf = { 0, 0 }; static int flat_iov_buf_len = 0; static int flat_prepare_iovec(const int n) { int i; LM_DBG("Needing %d fields, got %d\n", 2 * n, flat_iov_len); flat_iov_buf.len = 0; /* resize the buffer */ flat_iov = pkg_realloc(flat_iov, 2 * n * sizeof(struct iovec)); if (!flat_iov) { LM_ERR("not enough pkg mem for iov\n"); flat_iov_len = 0; return -1; } for (i = !flat_iov_len ? flat_iov_len + 1: flat_iov_len - 1; i < 2 * n - 1; i += 2) { flat_iov[i].iov_base = flat_delimiter; flat_iov[i].iov_len = 1; } flat_iov_len = 2*n; flat_iov[flat_iov_len - 1].iov_base = "\n"; flat_iov[flat_iov_len - 1].iov_len = 1; LM_DBG("Successfully allocated %d fields", flat_iov_len); return 0; } /* buffer operations */ #define FLAT_BUF (flat_iov_buf.s + flat_iov_buf.len) #define FLAT_LEN (flat_iov_buf_len - flat_iov_buf.len) #define FLAT_INC(_l) (flat_iov_buf.len += (_l)) #define FLAT_RESET() (flat_iov_buf.len = 0) #define FLAT_ALLOC(_l) \ do { \ if (!flat_iov_buf_len) { \ flat_iov_buf_len = (_l); \ flat_iov_buf.s = pkg_malloc((_l)); \ } else if (flat_iov_buf.len + (_l) > flat_iov_buf_len) { \ do { \ flat_iov_buf_len *= 2; \ } while (flat_iov_buf_len < (_l)); \ flat_iov_buf.s = pkg_realloc(flat_iov_buf.s, flat_iov_buf_len); \ LM_DBG("reallocated to %d, needed %d\n", flat_iov_buf_len, (_l)); \ } \ } while (0) /* #define FLAT_ALLOC(_l) \ flat_iov_buf.s = pkg_realloc(flat_iov_buf.s, (_l) + flat_iov_buf.len); \ flat_iov_buf_len = (_l) + flat_iov_buf.len; */ #define FLAT_SET_STR(_i, _s) flat_iov[2 * (_i)].iov_base = (_s) #define FLAT_SET_LEN(_i, _l) flat_iov[2 * (_i)].iov_len = (_l) #define FLAT_GET_LEN(_i) (flat_iov[2 * (_i)].iov_len) /* prints into buffer */ #define FLAT_PRINTF(_f, _v, _i) \ do { \ aux.len = snprintf(FLAT_BUF, FLAT_LEN, _f, _v); \ if (aux.len < 0) { \ LM_ERR("cannot print " #_v "\n"); \ aux.len = 0; \ } else if (aux.len >= FLAT_LEN) { \ LM_ERR("not enough space to print " #_v " ... truncating\n"); \ aux.len = FLAT_LEN - 1 /* '\0' at the end */; \ }\ FLAT_SET_LEN((_i), aux.len); \ FLAT_INC(aux.len); \ } while(0) #define FLAT_COPY(_i, _s, _l) \ do { \ str aux; \ int len = 0; \ int l = _l; \ const char *s = _s; \ const char *p = _s; \ while (l--) { \ if ( !(isprint((int)*s) && *s != '\\' && *s != flat_delimiter[0])) { \ aux.len = snprintf(FLAT_BUF, FLAT_LEN,"%.*s\\x%02X", \ (int)(s-p),p,(*s & 0xff)); \ p = s+1; \ if (aux.len < 0) { \ LM_ERR("error while writing blob %d\n", i); \ aux.len = 0; \ } \ len += aux.len; \ FLAT_INC(aux.len); \ } \ ++s; \ } \ if (p!=s) { \ aux.len = snprintf(FLAT_BUF, FLAT_LEN,"%.*s", (int)(s-p), p); \ if (aux.len < 0) { \ LM_ERR("error while writing blob %d\n", i); \ aux.len = 0; \ } \ len += aux.len; \ FLAT_INC(aux.len); \ } \ FLAT_SET_LEN(i, len); \ } while (0) /* * Insert a row into specified table * h: structure representing database connection * k: key names * v: values of the keys * n: number of key=value pairs */ int flat_db_insert(const db_con_t* h, const db_key_t* k, const db_val_t* v, const int n) { FILE* f; int i; int auxl; str aux; char * begin = flat_iov_buf.s; if (local_timestamp < *flat_rotate) { flat_rotate_logs(); local_timestamp = *flat_rotate; } if ( !h || !CON_TAIL(h) || (f=CON_FILE(h))==NULL ) { LM_ERR("uninitialized connection\n"); return -1; } if (flat_prepare_iovec(n) < 0) { LM_ERR("cannot insert row\n"); return -1; } FLAT_LOCK(f); for(i = 0; i < n; i++) { if (VAL_NULL(v + i)) { FLAT_SET_STR(i, ""); FLAT_SET_LEN(i, 0); continue; } FLAT_SET_STR(i, FLAT_BUF); switch(VAL_TYPE(v + i)) { case DB_INT: /* guess this is 20 */ FLAT_ALLOC(20); FLAT_PRINTF("%d", VAL_INT(v+i), i); break; case DB_DOUBLE: /* guess there are max 20 digits */ FLAT_ALLOC(40); FLAT_PRINTF("%f", VAL_DOUBLE(v+i), i); break; case DB_BIGINT: /* guess there are max 20 digits */ FLAT_ALLOC(40); FLAT_PRINTF("%llu", VAL_BIGINT(v+i), i); break; case DB_STRING: auxl = strlen(VAL_STRING(v + i)); FLAT_ALLOC(auxl * 4); FLAT_COPY(i, VAL_STRING(v + i), auxl); break; case DB_STR: FLAT_ALLOC(VAL_STR(v + i).len * 4); FLAT_COPY(i, VAL_STR(v + i).s, VAL_STR(v + i).len); break; case DB_DATETIME: /* guess this is 20 */ FLAT_ALLOC(20); FLAT_PRINTF("%lu", VAL_TIME(v+i), i); break; case DB_BLOB: auxl = VAL_BLOB(v+i).len; /* the maximum size is 4l - if all chars were not printable */ FLAT_ALLOC(4 * auxl); FLAT_COPY(i, VAL_BLOB(v+i).s, auxl); break; case DB_BITMAP: /* guess this is 20 */ FLAT_ALLOC(20); FLAT_PRINTF("%u", VAL_BITMAP(v+i), i); break; } } /* reorder pointers in case they were altered by (re)allocation */ if (flat_iov_buf.s != begin && flat_iov_buf.len) { FLAT_RESET(); for (i = 0; i < n; i++) { if (!VAL_NULL(v + i)) { FLAT_SET_STR(i, FLAT_BUF); FLAT_INC(FLAT_GET_LEN(i)); } } } do { auxl = writev(fileno(f), flat_iov, 2 * n); } while (auxl < 0 && errno == EINTR); if (auxl < 0) { LM_ERR("unable to write to file: %s - %d\n", strerror(errno), errno); return -1; } /* XXX does this make sense any more? */ if (flat_flush && fflush(f) < 0) { LM_ERR("cannot flush buffer: %s - %d\n", strerror(errno), errno); } FLAT_UNLOCK(f); return 0; } opensips-2.2.2/modules/db_flatstore/flatstore.h000066400000000000000000000032501300170765700216570ustar00rootroot00000000000000/* * Flatstore module interface * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) */ #ifndef _FLATSTORE_H #define _FLATSTORE_H #include "../../db/db_val.h" #include "../../db/db_key.h" #include "../../db/db_con.h" /* * Initialize database module * No function should be called before this */ db_con_t* flat_db_init(const str* _url); /* * Store name of table that will be used by * subsequent database functions */ int flat_use_table(db_con_t* h, const str* t); void flat_db_close(db_con_t* h); /* * Insert a row into specified table * h: structure representing database connection * k: key names * v: values of the keys * n: number of key=value pairs */ int flat_db_insert(const db_con_t* h, const db_key_t* k, const db_val_t* v, const int n); #endif /* _FLATSTORE_H */ opensips-2.2.2/modules/db_flatstore/flatstore_mod.c000066400000000000000000000106471300170765700225210ustar00rootroot00000000000000/* * Flatstore module interface * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) */ #include "../../sr_module.h" #include "../../mem/shm_mem.h" #include "../../db/db.h" #include "flatstore.h" #include "flat_mi.h" #include "flatstore_mod.h" static int child_init(int rank); static int mod_init(void); static void mod_destroy(void); int db_flat_bind_api(const str* mod, db_func_t *dbb); /* * Process number used in filenames */ int flat_pid; /* * Should we flush after each write to the database ? */ int flat_flush = 1; /* * Should we store all accounting into a single file ? */ int flat_single_file = 0; /* * Delimiter delimiting columns */ char* flat_delimiter = "|"; /* * suffix and prefix of the logging file * can be a formatted string */ char * flat_suffix_s = FILE_SUFFIX; gparam_p flat_suffix; char * flat_prefix_s = NULL; gparam_p flat_prefix; /* * Timestamp of the last log rotation request from * the FIFO interface */ time_t* flat_rotate; time_t local_timestamp; /* * Flatstore database module interface */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)db_flat_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"flush", INT_PARAM, &flat_flush}, {"delimiter", STR_PARAM, &flat_delimiter}, {"suffix", STR_PARAM, &flat_suffix_s}, {"prefix", STR_PARAM, &flat_prefix_s}, {"single_file", INT_PARAM, &flat_single_file}, {0, 0, 0} }; #define MI_FLAT_HELP "Params: none ; Rotates the logging file." /* * Exported parameters */ static mi_export_t mi_cmds[] = { { MI_FLAT_ROTATE, MI_FLAT_HELP, mi_flat_rotate_cmd, MI_NO_INPUT_FLAG, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; struct module_exports exports = { "db_flatstore", MOD_TYPE_SQLDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, 0, params, /* module parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ mod_destroy, /* destroy function */ child_init /* per-child init function */ }; static int mod_init(void) { if (strlen(flat_delimiter) != 1) { LM_ERR("delimiter has to be exactly one character\n"); return -1; } flat_rotate = (time_t*)shm_malloc(sizeof(time_t)); if (!flat_rotate) { LM_ERR("no shared memory left\n"); return -1; } *flat_rotate = time(0); local_timestamp = *flat_rotate; /* parse prefix and suffix */ if (flat_suffix_s && strlen(flat_suffix_s)) { if (fixup_spve((void **)&flat_suffix_s)) { LM_ERR("cannot parse log suffix\n"); return -1; } flat_suffix = (gparam_p)flat_suffix_s; } else { flat_suffix = 0; } if (flat_prefix_s && strlen(flat_prefix_s)) { if (fixup_spve((void **)&flat_prefix_s)) { LM_ERR("cannot parse log prefix\n"); return -1; } flat_prefix = (gparam_p)flat_prefix_s; } else { flat_prefix = 0; } return 0; } static void mod_destroy(void) { if (flat_rotate) shm_free(flat_rotate); } static int child_init(int rank) { if (rank <= 0) { flat_pid = - rank; } else { flat_pid = rank - PROC_TCP_MAIN; } return 0; } int db_flat_bind_api(const str* mod, db_func_t *dbb) { if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->use_table = flat_use_table; dbb->init = flat_db_init; dbb->close = flat_db_close; dbb->insert = flat_db_insert; return 0; } opensips-2.2.2/modules/db_flatstore/flatstore_mod.h000066400000000000000000000034041300170765700225170ustar00rootroot00000000000000/* * Flatstore module interface * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) */ #ifndef FLATSTORE_MOD_H #define FLATSTORE_MOD_H #include #include "../../mod_fix.h" /* * Process number used in filenames */ extern int flat_pid; /* * Should we flush after each write to the database ? */ extern int flat_flush; /* * Delmiter delimiting columns */ extern char* flat_delimiter; /* * The timestamp of log rotation request from * the FIFO interface */ extern time_t* flat_rotate; /* * Local timestamp marking the time of the * last log rotation in the process */ extern time_t local_timestamp; /* * Default suffix for logs */ #define FILE_SUFFIX ".log" #define FILE_SUFFIX_LEN (sizeof(FILE_SUFFIX)-1) /* * Suffix and prefix for logs */ extern gparam_p flat_suffix; extern gparam_p flat_prefix; extern int flat_single_file; #endif /* FLATSTORE_MOD_H */ opensips-2.2.2/modules/db_http/000077500000000000000000000000001300170765700164575ustar00rootroot00000000000000opensips-2.2.2/modules/db_http/Makefile000066400000000000000000000011001300170765700201070ustar00rootroot00000000000000# $Id: Makefile 5862 2009-07-15 10:06:05Z andreidragus $ # # HTTP implementation for DB API. # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=db_http.so HAS_BUILDER = $(shell if which curl-config >/dev/null 2>/dev/null;then echo YES; fi) ifeq ($(HAS_BUILDER),YES) # use autodetection DEFS += $(shell curl-config --cflags) LIBS = $(shell curl-config --libs) else # use standard know paths DEFS +=-I$(LOCALBASE)/include LIBS =-L$(LOCALBASE)/lib -lcurl endif include ../../Makefile.modules opensips-2.2.2/modules/db_http/README000066400000000000000000000264301300170765700173440ustar00rootroot00000000000000DB_HTTP Module Andrei Dragus OpenSIPS Solutions Edited by Andrei Dragus Copyright © 2009 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. SSL(int) 1.3.2. cap_raw_query(int) 1.3.3. cap_replace(int) 1.3.4. cap_insert_update(int) 1.3.5. cap_last_inserted_id(int) 1.3.6. field_delimiter (str) 1.3.7. row_delimiter (str) 1.3.8. quote_delimiter (str) 1.3.9. value_delimiter (str) 1.3.10. timeout (int) 1.4. Exported Functions 1.5. Server specifications 1.5.1. Queries 1.5.2. Variables 1.5.3. Query Types 1.5.4. NULL values in queries 1.5.5. Server Replies 1.5.6. Reply Quoting 1.5.7. Last inserted id 1.5.8. Authentication and SSL List of Examples 1.1. Setting db_url for a module 1.2. Set SSL parameter 1.3. Set cap_raw_query parameter 1.4. Set cap_replace parameter 1.5. Set cap_insert_update parameter 1.6. Set cap_last_inserted_id parameter 1.7. Set field_delimiter parameter 1.8. Set row_delimiter parameter 1.9. Set quote_delimiter parameter 1.10. Set value_delimiter parameter 1.11. Set timeout parameter 1.12. Example query. 1.13. Example query with variables. 1.14. More query examples. 1.15. NULL query example. 1.16. Example Reply. 1.17. Quoting Example. Chapter 1. Admin Guide 1.1. Overview This module provides access to a database that is implemented as a HTTP server. It may be used in special cases where traversing firewalls is a problem, or where data encryption is required. In order to use this module you must have a server that can communicate via HTTP or HTTPS with this module that follows exactly the format decribed in the specifications section. The module can provide SSL, authentication, and all the functionalities of an opensips db as long as the server supports them ( except result_fetch). There is a slight difference between the url of db_http and the urls of the other db modules. The url doesn't have to contain the database name. Instead, everything that is after the address is considered to be a path to the db resource, it may be missing. Even if using HTTPS the url must begin with "http://" , and the SSL parameter for the module must be set to 1. Example 1.1. Setting db_url for a module ... modparam("presence", "db_url","http://user:pass@localhost:13100") or modparam("presence", "db_url","http://user:pass@www.some.com/some/some") ... 1.2. Dependencies 1.2.1. OpenSIPS Modules This module does not depend on other modules. 1.2.2. External Libraries or Applications * libcurl. 1.3. Exported Parameters 1.3.1. SSL(int) Whether or not to use SSL. If value is 1 the module will use https otherwise it will use http. Default value is “ 0 â€. Example 1.2. Set SSL parameter ... modparam("db_http", "SSL",1) ... 1.3.2. cap_raw_query(int) Whether or not the server supports raw queries. Default value is “0â€. Example 1.3. Set cap_raw_query parameter ... modparam("db_http", "cap_raw_query", 1) ... 1.3.3. cap_replace(int) Whether or not the server supports replace capabilities. Default value is “0â€. Example 1.4. Set cap_replace parameter ... modparam("db_http", "cap_replace", 1) ... 1.3.4. cap_insert_update(int) Whether or not the server supports insert_update capabilities. Default value is “0â€. Example 1.5. Set cap_insert_update parameter ... modparam("db_http", "cap_insert_update", 1) ... 1.3.5. cap_last_inserted_id(int) Whether or not the server supports last_inserted_id capabilities. Default value is “0â€. Example 1.6. Set cap_last_inserted_id parameter ... modparam("db_http", "cap_last_inserted_id", 1) ... 1.3.6. field_delimiter (str) Character to be used to delimit fields in the reply.Only one char may be set. Default value is “;†Example 1.7. Set field_delimiter parameter ... modparam("db_http", "field_delimiter",";") ... 1.3.7. row_delimiter (str) Character to be used to delimit rows in the reply.Only one char may be set. Default value is “\n†Example 1.8. Set row_delimiter parameter ... modparam("db_http", "row_delimiter","\n") ... 1.3.8. quote_delimiter (str) Character to be used to quote fields that require quoting in the reply.Only one char may be set. Default value is “|†Example 1.9. Set quote_delimiter parameter ... modparam("db_http", "quote_delimiter","|") ... 1.3.9. value_delimiter (str) The delimiter used to separate multiple fields of a single variable (see Section 1.5.2, “Variablesâ€). Only one char may be set. Default value is “,†Example 1.10. Set value_delimiter parameter ... modparam("db_http", "value_delimiter",";") ... 1.3.10. timeout (int) The maximum number of milliseconds that the HTTP ops are allowed to last Default value is “30000 ( 30 seconds )†Example 1.11. Set timeout parameter ... modparam("db_http", "timeout",5000) ... 1.4. Exported Functions This module does not export any functions. 1.5. Server specifications 1.5.1. Queries The server must accept queries as HTTP queries. The queries are of 2 types : GET and POST.Both set variables that must be interpreted by the server. All values are URL-encoded. There are several types of queries and the server can tell them apart by the query_type variable. Each type of query uses specific variables simillar to those in the opensips db_api. Example 1.12. Example query. ... GET /presentity/?c=username,domain,event,expires HTTP/1.1 ... 1.5.2. Variables A description of all the variables. Each variable can have either a single value or a comma-separated list of values. Each variable has a special meaning and can be used only with certain queries. The table on which operations will take place will be encoded in the url as the end of the url ( www.some.com/users will point to the users table). * k= Describes the keys (columns) that will be used for comparison.Can have multiple values. * op= Describes the operators that will be used for comparison.Can have multiple values. * v= Describes the values that columns will be compaired against. Can have multiple values. * c= Describes the columns that will be selected from the result.Can have multiple values. * o= The column that the result will be ordered by. Has a single value. * uk= The keys(columns) that will be updated. Can have multiple values. * uv= The new values that will be put in the columns. Can have multiple values. * q= Describes a raw query. Will only be used if the server supports raw queries. Has a single value. * query_type= Describes the type of the current query. Can have a single value as described in the Query Types section.Has a single value. Will be present in all queries except the "SELECT" (normal query). Example 1.13. Example query with variables. ... GET /presentity/?c=username,domain,event,expires HTTP/1.1 GET /version/?k=table_name&v=xcap&c=table_version HTTP/1.1 ... ... POST /active_watchers HTTP/1.1 k=id&v=100&query_type=insert ... 1.5.3. Query Types The types of the queries are described by the query_type variable. The value of the variable will be set to the exact name of the query. Queries for "SELECT" use GET and the rest use POST (insert, update, delete, replace, insert_update). * normal query Uses the k, op, v, c and o variables. This will not set the query_type variable and will use GET. * delete Uses the k, op and v variables. * insert Uses the k and v variables. * update Uses the k,op,v,uk and uv variables. * replace Uses the k and v variables. This is an optional type of query. If the module is not configured to use it it will not. * insert_update Uses the k and v variables. This is an optional type of query. If the module is not configured to use it it will not. * custom Uses the q variable. This is an optional type of query. If the module is not configured to use it it will not. Example 1.14. More query examples. ... POST /active_watchers HTTP/1.1 k=id&op=%3D&v=100&query_type=delete ... ... POST /active_watchers HTTP/1.1 k=id&op=%3D&v=100&uk=id&uv=101&query_type=update ... 1.5.4. NULL values in queries NULL values in queries are represented as a string of length 1 containing a single character with value '\0'. Example 1.15. NULL query example. ... POST /active_watchers HTTP/1.1 k=id&op=%3D&v=%00&query_type=delete ... 1.5.5. Server Replies If the query is ok (even if the answer is empty) the server must reply with a 200 OK HTTP reply with a body containing the types and values of the columns. The server must reply with a delimiter separated list of values and columns. Each element in the list must be seperated from the one before it by a field delimiter that must be the same as the one set as a parameter from the script for the module. The last element of each line must not be followed by a field delimiter, but by a row delimiter. The first line of the reply must contain a list of the types of values of each column. The types can be any from the list: integer, string, str, blob, date. Each following line contains the values of each row from the result. If the query produced an error the server must reply with a HTTP 500 reply, or with a corresponding error code (404, 401). Example 1.16. Example Reply. ... int;string;blob 6;something=something;1000 100;mine;10002030 ... 1.5.6. Reply Quoting Because the values may contain delimiters inside, the server must perform quoting when necessary (there is no problem if it does it even when it is not necessary). A quote delimiter must be defined and must be the same as the one set from the script ( by default it is "|" ). If a value contains a field , row or a quote delimiter it must be placed under quotes. A quote delimiter inside a value must be preceeded by another quote delimiter. Example 1.17. Quoting Example. ... int;string;blob 6;|ana;maria|;1000 100;mine;10002030 3;mine;|some||more;| ... 1.5.7. Last inserted id This is an optional feature and may be enabled if one wants to use it. In order to use this feature the server must place the id of the last insert in the 200 reply for each insert query. 1.5.8. Authentication and SSL If the server supports authentication and SSL, the module can be enabled to use SSL. Authentication will always be used if needed. The module will try to use the most secure type of authentication that is provided by the server from: Basic, Digest,GSSNEGOTIATE and NTLM. opensips-2.2.2/modules/db_http/db_http.c000066400000000000000000000072111300170765700202500ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Andrei Dragus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2009-08-12 first version (andreidragus) */ #include "../../sr_module.h" #include "../../db/db.h" #include "http_dbase.h" static int http_mod_init(void); int db_http_bind_api( const str* mod, db_func_t *dbb); int cap_raw_query = 0; int cap_id = 0; int cap_replace = 0; int cap_insert_update = 0; int use_ssl = 0 ; unsigned int db_http_timeout = 30000; /* Default is 30 seconds */ /* * MySQL database module interface */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)db_http_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"SSL", INT_PARAM ,&use_ssl}, {"cap_raw_query", INT_PARAM ,&cap_raw_query}, {"cap_replace", INT_PARAM , &cap_replace}, {"cap_last_inserted_id", INT_PARAM , &cap_id}, {"cap_insert_update", INT_PARAM , &cap_insert_update}, {"field_delimiter", STR_PARAM | USE_FUNC_PARAM ,set_col_delim}, {"row_delimiter", STR_PARAM | USE_FUNC_PARAM ,set_line_delim}, {"quote_delimiter", STR_PARAM | USE_FUNC_PARAM ,set_quote_delim}, {"value_delimiter", STR_PARAM | USE_FUNC_PARAM ,set_value_delim}, {"timeout", INT_PARAM,&db_http_timeout}, {0, 0, 0} }; struct module_exports exports = { "db_http", MOD_TYPE_SQLDB, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, 0, params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ http_mod_init, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0 /* per-child init function */ }; static int http_mod_init(void) { return 0; } int db_http_bind_api( const str* mod, db_func_t *dbb) { if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->cap = DB_CAP_QUERY | DB_CAP_INSERT | DB_CAP_DELETE | DB_CAP_UPDATE ; if( cap_id) { dbb->cap |= DB_CAP_LAST_INSERTED_ID; dbb->last_inserted_id = db_last_inserted_id; } if( cap_raw_query) { dbb->cap |= DB_CAP_RAW_QUERY; dbb->raw_query = db_http_raw_query; } if( cap_replace) { dbb->cap |= DB_CAP_REPLACE; dbb->replace = db_http_replace; } if( cap_insert_update) { dbb->cap |= DB_CAP_INSERT_UPDATE; dbb->insert_update = db_insert_update; } dbb->use_table = db_http_use_table; dbb->init = db_http_init; dbb->close = db_http_close; dbb->query = db_http_query; dbb->fetch_result = NULL; dbb->free_result = db_http_free_result; dbb->insert = db_http_insert; dbb->delete = db_http_delete; dbb->update = db_http_update; return 0; } opensips-2.2.2/modules/db_http/db_http.h000066400000000000000000000017451300170765700202630ustar00rootroot00000000000000/* * Copyright (C) 2012 Guillaume Bour (Orange-Vallee) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2012-02-25 first version (gbour) */ #ifndef DB_HTTP_H #define DB_HTTP_H extern unsigned int db_http_timeout; #endif /* DB_HTTP_H */ opensips-2.2.2/modules/db_http/doc/000077500000000000000000000000001300170765700172245ustar00rootroot00000000000000opensips-2.2.2/modules/db_http/doc/db_http.xml000066400000000000000000000017401300170765700213740ustar00rootroot00000000000000 %docentities; ]> DB_HTTP Module &osipsname; Andrei Dragus &osipssolname; Andrei Dragus 2009 &voicesystem; $Revision: 8740 $ $Date$ &admin; opensips-2.2.2/modules/db_http/doc/db_http_admin.xml000066400000000000000000000347741300170765700225610ustar00rootroot00000000000000 &adminguide;
Overview This module provides access to a database that is implemented as a HTTP server. It may be used in special cases where traversing firewalls is a problem, or where data encryption is required. In order to use this module you must have a server that can communicate via HTTP or HTTPS with this module that follows exactly the format decribed in the specifications section. The module can provide SSL, authentication, and all the functionalities of an opensips db as long as the server supports them ( except result_fetch). There is a slight difference between the url of db_http and the urls of the other db modules. The url doesn't have to contain the database name. Instead, everything that is after the address is considered to be a path to the db resource, it may be missing. Even if using HTTPS the url must begin with "http://" , and the SSL parameter for the module must be set to 1. Setting db_url for a module ... modparam("presence", "db_url","http://user:pass@localhost:13100") or modparam("presence", "db_url","http://user:pass@www.some.com/some/some") ...
Dependencies
&osips; Modules This module does not depend on other modules.
External Libraries or Applications libcurl.
Exported Parameters
<varname>SSL</varname>(int) Whether or not to use SSL. If value is 1 the module will use https otherwise it will use http. Default value is 0 . Set <varname>SSL</varname> parameter ... modparam("db_http", "SSL",1) ...
<varname>cap_raw_query</varname>(int) Whether or not the server supports raw queries. Default value is 0. Set <varname>cap_raw_query</varname> parameter ... modparam("db_http", "cap_raw_query", 1) ...
<varname>cap_replace</varname>(int) Whether or not the server supports replace capabilities. Default value is 0. Set <varname>cap_replace</varname> parameter ... modparam("db_http", "cap_replace", 1) ...
<varname>cap_insert_update</varname>(int) Whether or not the server supports insert_update capabilities. Default value is 0. Set <varname>cap_insert_update</varname> parameter ... modparam("db_http", "cap_insert_update", 1) ...
<varname>cap_last_inserted_id</varname>(int) Whether or not the server supports last_inserted_id capabilities. Default value is 0. Set <varname>cap_last_inserted_id</varname> parameter ... modparam("db_http", "cap_last_inserted_id", 1) ...
<varname>field_delimiter</varname> (str) Character to be used to delimit fields in the reply.Only one char may be set. Default value is ; Set <varname>field_delimiter</varname> parameter ... modparam("db_http", "field_delimiter",";") ...
<varname>row_delimiter</varname> (str) Character to be used to delimit rows in the reply.Only one char may be set. Default value is \n Set <varname>row_delimiter</varname> parameter ... modparam("db_http", "row_delimiter","\n") ...
<varname>quote_delimiter</varname> (str) Character to be used to quote fields that require quoting in the reply.Only one char may be set. Default value is | Set <varname>quote_delimiter</varname> parameter ... modparam("db_http", "quote_delimiter","|") ...
<varname>value_delimiter</varname> (str) The delimiter used to separate multiple fields of a single variable (see ). Only one char may be set. Default value is , Set <varname>value_delimiter</varname> parameter ... modparam("db_http", "value_delimiter",";") ...
<varname>timeout</varname> (int) The maximum number of milliseconds that the HTTP ops are allowed to last Default value is 30000 ( 30 seconds ) Set <varname>timeout</varname> parameter ... modparam("db_http", "timeout",5000) ...
Exported Functions This module does not export any functions.
Server specifications
Queries The server must accept queries as HTTP queries. The queries are of 2 types : GET and POST.Both set variables that must be interpreted by the server. All values are URL-encoded. There are several types of queries and the server can tell them apart by the query_type variable. Each type of query uses specific variables simillar to those in the opensips db_api. Example query. ... GET /presentity/?c=username,domain,event,expires HTTP/1.1 ...
Variables A description of all the variables. Each variable can have either a single value or a comma-separated list of values. Each variable has a special meaning and can be used only with certain queries. The table on which operations will take place will be encoded in the url as the end of the url ( www.some.com/users will point to the users table). k= Describes the keys (columns) that will be used for comparison.Can have multiple values. op= Describes the operators that will be used for comparison.Can have multiple values. v= Describes the values that columns will be compaired against. Can have multiple values. c= Describes the columns that will be selected from the result.Can have multiple values. o= The column that the result will be ordered by. Has a single value. uk= The keys(columns) that will be updated. Can have multiple values. uv= The new values that will be put in the columns. Can have multiple values. q= Describes a raw query. Will only be used if the server supports raw queries. Has a single value. query_type= Describes the type of the current query. Can have a single value as described in the Query Types section.Has a single value. Will be present in all queries except the "SELECT" (normal query). Example query with variables. ... GET /presentity/?c=username,domain,event,expires HTTP/1.1 GET /version/?k=table_name&v=xcap&c=table_version HTTP/1.1 ... ... POST /active_watchers HTTP/1.1 k=id&v=100&query_type=insert ...
Query Types The types of the queries are described by the query_type variable. The value of the variable will be set to the exact name of the query. Queries for "SELECT" use GET and the rest use POST (insert, update, delete, replace, insert_update). normal query Uses the k, op, v, c and o variables. This will not set the query_type variable and will use GET. delete Uses the k, op and v variables. insert Uses the k and v variables. update Uses the k,op,v,uk and uv variables. replace Uses the k and v variables. This is an optional type of query. If the module is not configured to use it it will not. insert_update Uses the k and v variables. This is an optional type of query. If the module is not configured to use it it will not. custom Uses the q variable. This is an optional type of query. If the module is not configured to use it it will not. More query examples. ... POST /active_watchers HTTP/1.1 k=id&op=%3D&v=100&query_type=delete ... ... POST /active_watchers HTTP/1.1 k=id&op=%3D&v=100&uk=id&uv=101&query_type=update ...
NULL values in queries NULL values in queries are represented as a string of length 1 containing a single character with value '\0'. NULL query example. ... POST /active_watchers HTTP/1.1 k=id&op=%3D&v=%00&query_type=delete ...
Server Replies If the query is ok (even if the answer is empty) the server must reply with a 200 OK HTTP reply with a body containing the types and values of the columns. The server must reply with a delimiter separated list of values and columns. Each element in the list must be seperated from the one before it by a field delimiter that must be the same as the one set as a parameter from the script for the module. The last element of each line must not be followed by a field delimiter, but by a row delimiter. The first line of the reply must contain a list of the types of values of each column. The types can be any from the list: integer, string, str, blob, date. Each following line contains the values of each row from the result. If the query produced an error the server must reply with a HTTP 500 reply, or with a corresponding error code (404, 401). Example Reply. ... int;string;blob 6;something=something;1000 100;mine;10002030 ...
Reply Quoting Because the values may contain delimiters inside, the server must perform quoting when necessary (there is no problem if it does it even when it is not necessary). A quote delimiter must be defined and must be the same as the one set from the script ( by default it is "|" ). If a value contains a field , row or a quote delimiter it must be placed under quotes. A quote delimiter inside a value must be preceeded by another quote delimiter. Quoting Example. ... int;string;blob 6;|ana;maria|;1000 100;mine;10002030 3;mine;|some||more;| ...
Last inserted id This is an optional feature and may be enabled if one wants to use it. In order to use this feature the server must place the id of the last insert in the 200 reply for each insert query.
Authentication and SSL If the server supports authentication and SSL, the module can be enabled to use SSL. Authentication will always be used if needed. The module will try to use the most secure type of authentication that is provided by the server from: Basic, Digest,GSSNEGOTIATE and NTLM.
opensips-2.2.2/modules/db_http/http_dbase.c000066400000000000000000000520461300170765700207470ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Andrei Dragus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2009-08-12 first version (andreidragus) */ #include "http_dbase.h" #include "db_http.h" #include "../../db/db_id.h" #include "../../db/db_ut.h" #include "../../db/db_row.h" #include typedef struct _http_conn { CURL * handle; str start; int last_id; } http_conn_t; typedef struct var_str_t { char * s; int len; int allocated; } var_str; enum { HTTPDB_QUERY, HTTPDB_INSERT, HTTPDB_DELETE, HTTPDB_UPDATE, HTTPDB_REPLACE, HTTPDB_INSERT_UPDATE, HTTPDB_CUSTOM }; enum { IN = 0, OUT = 1, ESC = 2 }; int next_state[3][256]; char line_delim = '\n'; char col_delim = ';'; char *val_delim_s = ","; char quote_delim = '|'; extern int use_ssl; char error_buffer[CURL_ERROR_SIZE]; #define CHECK( val,expected,err_tag) \ { \ if( (val) != (expected) ) \ goto err_tag; \ } int set_col_delim( unsigned int type, void *val) { char * v = (char*) val; if( strlen(val) != 1) { LM_ERR("Only one field delimiter may be set\n"); return -1; } col_delim = v[0]; return 0; } int set_line_delim( unsigned int type, void *val) { char * v = (char*) val; if( strlen(val) != 1) { LM_ERR("Only one field delimiter may be set\n"); return -1; } line_delim = v[0]; return 0; } int set_quote_delim( unsigned int type, void *val) { char * v = (char*) val; if( strlen(val) != 1) { LM_ERR("Only one field delimiter may be set\n"); return -1; } quote_delim = v[0]; return 0; } int set_value_delim( unsigned int type, void *val) { if( strlen(val) != 1) { LM_ERR("Only one values delimiter may be set\n"); return -1; } val_delim_s = val; return 0; } str value_to_string(const db_val_t * v); str url_encode(str s); static int append_str( var_str * to, str from) { if(to->len + from.len > to->allocated) { to->s = (char*) pkg_realloc(to->s, to->len + from.len + 1); to->allocated = to->len + from.len; if( to->s == NULL) { LM_ERR("Out of memory\n"); return -1; } } memcpy( to->s+to->len, from.s, from.len ); to->len += from.len; to->s[to->len] = 0; return 0; } static int append_const(var_str* to, char * from) { static str temp; temp.s = from; temp.len = strlen(from); return append_str(to,temp); } static int append_keys (var_str * q,const char * name, const db_key_t* k, int n, int * started ) { int i; if( k != NULL) { if( *started ) CHECK(append_const(q,"&"),0,error); CHECK(append_const(q,(char*)name),0,error); CHECK(append_const(q,"="),0,error); for(i=0;icol.n = cols; if( db_allocate_rows(res,rows) < 0 ) { LM_ERR("Error allocating db result rows\n"); db_free_columns( res ); pkg_free(res); return NULL; } res->n = rows; res->res_rows = rows; res->last_row = rows; for( i=0;irows[i].n = cols; return res; } int put_type_in_result( char * start, int len , db_res_t * res , int cur_col ) { int ok = 0; LM_DBG("Found type: %.*s %d\n",len,start,len); if( len == 3 && !strncmp (start,"int",len)) { res->col.types[cur_col] = DB_INT; ok = 1; } if( len == 6 && !strncmp (start,"bigint",len)) { res->col.types[cur_col] = DB_BIGINT; ok = 1; } if( len == 6 && !strncmp (start,"double",len)) { res->col.types[cur_col] = DB_DOUBLE; ok = 1; } if( len == 6 && !strncmp (start,"string",len)) { res->col.types[cur_col] = DB_STRING; ok = 1; } if( len == 3 && !strncmp (start,"str",len)) { res->col.types[cur_col] = DB_STR; ok = 1; } if( len == 4 && !strncmp (start,"blob",len)) { res->col.types[cur_col] = DB_BLOB; ok = 1; } if( len == 4 && !strncmp (start,"date",len)) { res->col.types[cur_col] = DB_DATETIME; ok = 1; } if( !ok ) LM_ERR("Unknown datatype\n"); return 1 - ok; } int put_value_in_result( char * start, int len , db_res_t * res , int cur_col, int cur_line ) { db_val_t * row; LM_DBG("Found value: %.*s\n",len,start); row = res->rows[cur_line].values; row[cur_col].type = res->col.types[cur_col]; if( len == 0 && (res->col.types[cur_col] != DB_BLOB ) && (res->col.types[cur_col] != DB_STRING ) && (res->col.types[cur_col] != DB_STR ) ) { row[cur_col].nul = 1; return 0; } switch(res->col.types[cur_col]) { case( DB_INT): CHECK( sscanf(start,"%d",&row[cur_col].val.int_val), 1, error); break; case( DB_BIGINT): CHECK( sscanf(start,"%lld",&row[cur_col].val.bigint_val), 1, error); break; case( DB_DOUBLE): CHECK( sscanf(start,"%lf",&row[cur_col].val.double_val), 1, error); break; case( DB_STRING): row[cur_col].val.string_val = start; break; case( DB_STR): case( DB_BLOB): row[cur_col].val.blob_val.s = start; row[cur_col].val.blob_val.len = len; break; case( DB_DATETIME): CHECK( db_str2time(start,&row[cur_col].val.time_val), 0, error); break; default: break; } return 0; error: LM_ERR("Unable to parse value: %.*s\n",len,start); return -1; } int form_result(var_str buff, db_res_t** r) { db_res_t * res; char * cur, * dest, * start, * end; int col_count, cur_col, line_count, cur_line, delim_count, len; int state, next, consume; LM_DBG("Called with : %.*s\n",buff.len,buff.s); end = buff.s + buff.len; res = NULL; if( buff.len == 0 ) { *r = new_full_db_res(0,0); return 0; } state = OUT; cur = buff.s; col_count = 0; cur_col = 0; cur_line = -1; delim_count = 0; while( cur < end ) { next = next_state[ state ][ (int)((unsigned char)*cur) ]; consume = 1; if( state == OUT ) { if( *cur == col_delim ) { cur_col++; delim_count++; } if( *cur == line_delim ) { cur_col++; if( cur_line == -1 ) col_count = cur_col; else if(cur_col != col_count) goto error_before; delim_count++; cur_line++; cur_col = 0; } } if( state == ESC ) { /* do not consume other characters than 'quote_delim' */ if( *cur != quote_delim ) consume = 0; } if( consume) cur++; state = next; } line_count = cur_line; if( col_count == 0 || line_count == 0 ) goto error_before; /* validate input */ if( delim_count != (line_count+1)*col_count) goto error_before; /* allocate all necessary info */ res = new_full_db_res(line_count,col_count); if( res == NULL ) return -1; state = OUT; cur = buff.s; dest = buff.s; cur_col = 0; cur_line = -1; start = dest; while( cur < end ) { next = next_state[ state ][ (int)((unsigned char)*cur) ]; consume = 1; if( state == OUT ) { if( *cur == col_delim ) { len = dest - start; start[len] = 0; if( cur_line == -1 ) CHECK( put_type_in_result(start,len, res,cur_col), 0, error) else CHECK( put_value_in_result(start,len, res,cur_col,cur_line),0,error) dest = start + len + 1; start = dest; cur_col++; } else if( *cur == line_delim ) { len = dest - start; start[len] = 0; if( cur_line == -1 ) put_type_in_result(start,len,res,cur_col); else put_value_in_result(start,len,res,cur_col,cur_line); dest = start + len + 1; start = dest; cur_line++; cur_col = 0; } else if( *cur != quote_delim ) { *dest++ = *cur; } } if( state == ESC ) { if( *cur != quote_delim ) consume = 0; else *dest++ = *cur; } if( state == IN ) { if( *cur != quote_delim ) *dest++ = *cur; } if( consume ) cur++; state = next; } LM_DBG("Finished query\n"); *r = res; return 0; error: db_http_free_result(NULL,res); error_before: LM_ERR("Error parsing HTTP reply\n"); return -1; } int do_http_op ( const db_con_t* h, const db_key_t* k, const db_op_t* op, const db_val_t* v, const int n, const db_key_t* c, const int nc, const db_key_t* uk, const db_val_t* uv, const int nu, const db_key_t o, const str* custom, db_res_t** r, int db_op ) { LM_DBG("Called with db_op=%d\n",db_op); static var_str q = {0,0,0}; static var_str buff = {0,0,0}; http_conn_t * conn ; int started = 0; int middle_poz; CURLcode ret; ret = 0; q.len = 0; buff.len = 0; middle_poz = 0; conn = (http_conn_t*) h->tail; /* put the http address */ CHECK( append_str(&q,conn->start), 0, error); if( h->table->s == NULL) { LM_ERR("No table selected for op\n"); goto error; } /* put the table name */ CHECK( append_str(&q,*h->table), 0, error); /* for operations other than querie use POST */ if( db_op == HTTPDB_QUERY || db_op == HTTPDB_CUSTOM ) { /* put the queries */ CHECK( append_const(&q,"/?"), 0, error); } else { str tmp; tmp.s="\0"; tmp.len = 1; CHECK( append_str(&q,tmp), 0, error); middle_poz = q.len; } CHECK( append_keys(&q,"k",k,n,&started), 0, error); CHECK( append_ops(&q,"op",op,n,&started), 0, error); CHECK( append_values(&q,"v",v,n,&started), 0, error); CHECK( append_keys(&q,"c",c,nc,&started), 0, error); CHECK( append_keys(&q,"uk",uk,nu,&started), 0, error); CHECK( append_values(&q,"uv",uv,nu,&started), 0, error); if( o != NULL) { if( started) CHECK( append_const(&q,"&"), 0, error); CHECK( append_const(&q,"o="), 0, error); CHECK( append_str(&q,url_encode(*o)) ,0, error); started = 1; } if( custom != NULL) { if( started) CHECK( append_const(&q,"&"), 0, error); CHECK( append_const(&q,"q="), 0, error); CHECK( append_str(&q,url_encode(*custom)) ,0, error); started = 1; } if( started && db_op != HTTPDB_QUERY) { CHECK( append_const(&q,"&"), 0, error); } switch(db_op) { case(HTTPDB_QUERY): break; case(HTTPDB_INSERT): CHECK( append_const(&q,"query_type=insert"), 0, error); break; case(HTTPDB_DELETE): CHECK( append_const(&q,"query_type=delete"), 0, error); break; case(HTTPDB_UPDATE): CHECK( append_const(&q,"query_type=update"), 0, error); break; case(HTTPDB_REPLACE): CHECK( append_const(&q,"query_type=replace"), 0, error); break; case(HTTPDB_INSERT_UPDATE): CHECK( append_const(&q,"query_type=insert_update"), 0, error); break; case(HTTPDB_CUSTOM): CHECK( append_const(&q,"query_type=custom"), 0, error); break; default: LM_ERR("Unknown db operation\n"); return -1; } q.s[q.len] = 0 ; LM_DBG("Sent:%s \n",q.s); curl_easy_setopt(conn->handle, CURLOPT_HTTPGET, 1); curl_easy_setopt(conn->handle, CURLOPT_URL, q.s); curl_easy_setopt(conn->handle, CURLOPT_WRITEFUNCTION, receive); curl_easy_setopt(conn->handle, CURLOPT_WRITEDATA, &buff); if( db_op != HTTPDB_QUERY && db_op != HTTPDB_CUSTOM) { LM_DBG("Posted:%s \n",&q.s[middle_poz]); curl_easy_setopt(conn->handle, CURLOPT_POSTFIELDS, &q.s[middle_poz]); } curl_easy_setopt(conn->handle, CURLOPT_FAILONERROR,1); ret = curl_easy_perform(conn->handle); if( ret ) { LM_ERR( "Error in CURL: %s\n", curl_easy_strerror(ret) ); LM_ERR( "Description : %s\n",error_buffer); return -1; } if( db_op == HTTPDB_QUERY || db_op == HTTPDB_CUSTOM ) { if( form_result(buff,r) ) return -1; } if( db_op == HTTPDB_INSERT ) { if( buff.len > 0) sscanf(buff.s,"%d",&conn->last_id); } return 0; error: LM_ERR("Error while appending to buffer\n"); return -1; } str value_to_string(const db_val_t * v) { static char buff[64]; str rez; rez.s = NULL; rez.len = 0; if( v->nul ) { rez.s = "\0"; rez.len = 1; return rez; } switch ( v->type) { case (DB_INT): sprintf(buff,"%d",v->val.int_val); rez.s = buff; rez.len = strlen(rez.s); break; case (DB_BIGINT): sprintf(buff,"%lld",v->val.bigint_val); rez.s = buff; rez.len = strlen(rez.s); break; case( DB_DOUBLE): sprintf(buff,"%f",v->val.double_val); rez.s = buff; rez.len = strlen(rez.s); break; case( DB_STRING): rez.s = (char*) v -> val.string_val; rez.len = strlen(rez.s); break; case(DB_STR): rez = v ->val.str_val; break; case(DB_DATETIME): sprintf(buff,"%s",ctime(&v->val.time_val)); rez.s = buff; rez.len = strlen(rez.s); break; case(DB_BLOB): rez = v->val.blob_val; break; case(DB_BITMAP): sprintf(buff,"%d",v -> val.bitmap_val); rez.s = buff; rez.len = strlen(rez.s); break; } if( rez.s == NULL ) { rez.s = ""; rez.len = 0; } return rez; } /* Converts an integer value to its hex character*/ char to_hex(char code) { static char hex[] = "0123456789abcdef"; return hex[code & 15]; } /* Returns a url-encoded version of str */ str url_encode(str s) { static char *buf = NULL; static int size = 0; char *pstr ; char *pbuf; str rez; int i; pstr = s.s; if( s.len * 3 + 1 > size) { buf = pkg_realloc(buf, s.len * 3 + 1); size = s.len * 3 + 1; } pbuf = buf; i = 0; while ( i < s.len ) { if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') *pbuf++ = *pstr; else { *pbuf++ = '%'; *pbuf++ = to_hex(*pstr >> 4); *pbuf++ = to_hex(*pstr & 15); } pstr++; i++; } rez.s = buf; rez.len = pbuf - buf; return rez; } db_con_t* db_http_init(const str* url) { #define DB_HTTP_BUFF_SIZE 1024 char* path; char user_pass[DB_HTTP_BUFF_SIZE]; char modified_url[DB_HTTP_BUFF_SIZE]; str tmp; int off, ret; db_con_t * ans = NULL; http_conn_t * curl = NULL; int i; struct db_id * id; memset(modified_url,0,DB_HTTP_BUFF_SIZE); memcpy(modified_url,url->s,url->len); strcat(modified_url,"/x"); tmp.s = modified_url; tmp.len = strlen(tmp.s); user_pass[0] = 0; path = (char*)pkg_malloc(DB_HTTP_BUFF_SIZE); if( path == NULL ) { LM_ERR("Out of memory\n"); return NULL; } memset(path,0,DB_HTTP_BUFF_SIZE); id = new_db_id( &tmp ); if( id == NULL) { pkg_free(path); LM_ERR("Incorrect db_url\n"); return NULL; } if( id->username && id->password) { ret = snprintf(user_pass, DB_HTTP_BUFF_SIZE, "%s:%s", id->username, id->password); if (ret < 0 || ret >= DB_HTTP_BUFF_SIZE) goto error; } curl = (http_conn_t * ) pkg_malloc(sizeof(http_conn_t)); if( curl == NULL ) { pkg_free(path); LM_ERR("Out of memory\n"); return NULL; } curl->handle = curl_easy_init(); curl_easy_setopt(curl->handle,CURLOPT_SSL_VERIFYPEER,0); curl_easy_setopt(curl->handle,CURLOPT_SSL_VERIFYHOST,0); curl_easy_setopt(curl->handle,CURLOPT_USERPWD,user_pass); curl_easy_setopt(curl->handle,CURLOPT_HTTPAUTH,CURLAUTH_ANY); curl_easy_setopt(curl->handle,CURLOPT_ERRORBUFFER,error_buffer); #if LIBCURL_VERSION_NUM >= 0x071002 LM_DBG("timeout set to %d", db_http_timeout); curl_easy_setopt(curl->handle,CURLOPT_TIMEOUT_MS,db_http_timeout); #endif ret = snprintf(path, DB_HTTP_BUFF_SIZE, "http"); if (ret < 0 || ret >= DB_HTTP_BUFF_SIZE) goto error; off = ret; if (use_ssl) { ret = snprintf(path + off, DB_HTTP_BUFF_SIZE - off, "s"); if (ret < 0 || ret >= (DB_HTTP_BUFF_SIZE - off)) goto error; off += ret; } ret = snprintf(path + off, DB_HTTP_BUFF_SIZE - off, "://%s", id->host); if (ret < 0 || ret >= (DB_HTTP_BUFF_SIZE - off)) goto error; off += ret; if (id->port) { ret = snprintf(path + off, DB_HTTP_BUFF_SIZE - off, ":%d", id->port); if (ret < 0 || ret >= (DB_HTTP_BUFF_SIZE - off)) goto error; off += ret; } ret = snprintf(path + off, DB_HTTP_BUFF_SIZE - off, "/"); if (ret < 0 || ret >= (DB_HTTP_BUFF_SIZE - off)) goto error; off += ret; if( strlen(id->database) > 2 ) { id->database[strlen(id->database)-2] = 0; ret = snprintf(path + off, DB_HTTP_BUFF_SIZE - off, "%s/", id->database); if (ret < 0 || ret >= (DB_HTTP_BUFF_SIZE - off)) goto error; off += ret; } curl->start.s = path; curl->start.len = strlen(path); ans = (db_con_t *)pkg_malloc(sizeof(db_con_t)); if( ans == NULL ) { pkg_free(path); curl_easy_cleanup(curl->handle); pkg_free(curl); LM_ERR("Out of memory\n"); return NULL; } ans ->tail = (long)curl; for( i=0 ; i< 256;i++) next_state[IN][i] = IN; for( i=0 ; i< 256;i++) next_state[OUT][i] = OUT; for( i=0 ; i< 256;i++) next_state[ESC][i] = OUT; next_state[ OUT ][ (int)quote_delim ] = IN; next_state[ IN ][ (int)quote_delim ] = ESC; next_state[ ESC ][ (int) quote_delim ] = IN; return ans; error: if (path) pkg_free(path); if (curl) { curl_easy_cleanup(curl->handle); pkg_free(curl); } LM_CRIT("Initialization error\n"); return NULL; } void db_http_close(db_con_t* _h) { http_conn_t* conn = (http_conn_t*) _h->tail; curl_easy_cleanup(conn->handle); pkg_free(_h); } /* * Free all memory allocated by get_result */ int db_http_free_result(db_con_t* _h, db_res_t* _r) { db_free_columns( _r ); db_free_rows( _r ); pkg_free(_r); return 0; } /* * Do a query */ int db_http_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r) { return do_http_op ( _h, _k, _op, _v, _n, _c, _nc, NULL , NULL, 0, NULL, NULL, _r, HTTPDB_QUERY ); return 0; } /* * Raw SQL query */ int db_http_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r) { return do_http_op ( _h, NULL, NULL, NULL, 0, NULL , 0 , NULL , NULL , 0, NULL, _s, _r, HTTPDB_CUSTOM ); } /* * Insert a row into table */ int db_http_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { return do_http_op ( _h, _k, NULL, _v, _n, NULL , 0 , NULL , NULL , 0, NULL, NULL, NULL, HTTPDB_INSERT ); } /* * Delete a row from table */ int db_http_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n) { return do_http_op ( _h, _k, _o,_v, _n, NULL, 0, NULL,NULL,0, NULL, NULL, NULL, HTTPDB_DELETE ); } /* * Update a row in table */ int db_http_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un) { return do_http_op ( _h, _k, _o, _v, _n, NULL, 0, _uk, _uv, _un, NULL, NULL, NULL, HTTPDB_UPDATE ); } /* * Just like insert, but replace the row if it exists */ int db_http_replace(const db_con_t* handle, const db_key_t* keys, const db_val_t* vals, const int n) { return do_http_op ( handle, keys,NULL,vals, n, NULL, 0, NULL,NULL,0, NULL, NULL, NULL, HTTPDB_REPLACE ); } /* * Returns the last inserted ID */ int db_last_inserted_id(const db_con_t* _h) { http_conn_t* conn = (http_conn_t*) _h->tail; return conn->last_id; } /* * Insert a row into table, update on duplicate key */ int db_insert_update(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { return do_http_op ( _h, _k, NULL, _v, _n, NULL , 0, NULL, NULL, 0, NULL, NULL, NULL, HTTPDB_INSERT_UPDATE ); } /* * Store name of table that will be used by * subsequent database functions */ int db_http_use_table(db_con_t* _h, const str* _t) { _h->table = _t; return 0; } opensips-2.2.2/modules/db_http/http_dbase.h000066400000000000000000000057171300170765700207570ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Andrei Dragus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2009-08-12 first version (andreidragus) */ #ifndef HTTP_DBASE_H #define HTTP_DBASE_H #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" #include "../../str.h" /* parameter setting methods */ int set_col_delim( unsigned int type, void *val); int set_line_delim( unsigned int type, void *val); int set_quote_delim( unsigned int type, void *val); int set_value_delim( unsigned int type, void *val); /* * Initialize database connection */ db_con_t* db_http_init(const str* _sqlurl); /* * Close a database connection */ void db_http_close(db_con_t* _h); /* * Free all memory allocated by get_result */ int db_http_free_result(db_con_t* _h, db_res_t* _r); /* * Do a query */ int db_http_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r); /* * Raw SQL query */ int db_http_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r); /* * Insert a row into table */ int db_http_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /* * Delete a row from table */ int db_http_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n); /* * Update a row in table */ int db_http_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un); /* * Just like insert, but replace the row if it exists */ int db_http_replace(const db_con_t* handle, const db_key_t* keys, const db_val_t* vals, const int n); /* * Returns the last inserted ID */ int db_last_inserted_id(const db_con_t* _h); /* * Insert a row into table, update on duplicate key */ int db_insert_update(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /* * Store name of table that will be used by * subsequent database functions */ int db_http_use_table(db_con_t* _h, const str* _t); #endif /* HTTP_DBASE_H */ opensips-2.2.2/modules/db_mysql/000077500000000000000000000000001300170765700166455ustar00rootroot00000000000000opensips-2.2.2/modules/db_mysql/Makefile000066400000000000000000000041131300170765700203040ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=db_mysql.so SCRIPTS_DIR?=../../scripts/ # set CROSS_COMPILE to true if you want to skip # the autodetection # CROSS_COMPILE=true ifeq ($(CROSS_COMPILE),) HAS_MYSQLCFG=$(shell if which mysql_config >/dev/null 2>/dev/null;then echo YES; fi) endif ifeq ($(HAS_MYSQLCFG),YES) # use autodetection DEFS += $(shell mysql_config --include | sed 's/\(-I.*\)\/mysql/\1/g' ) LIBS = $(shell mysql_config --libs) else # use standard know paths # mysql.h locations (freebsd,openbsd solaris) DEFS +=-I$(LOCALBASE)/include -I$(LOCALBASE)/include/mysql \ -I$(LOCALBASE)/mysql/include/mysql -I$(LOCALBASE)/mysql/include \ -I$(SYSBASE)/include/mysql # libmysqlclient locations on RH/Suse, Solaris /OpenBSD, FreeBSD # (Debian does the right thing and puts it in /usr/lib) LIBS=-L$(SYSBASE)/lib/mysql -L$(LOCALBASE)/lib -L$(LOCALBASE)/lib/mysql \ -L$(LOCALBASE)/mysql/lib/mysql/ \ -L$(LOCALBASE)/mysql/lib \ -L$(SYSBASE)/lib64/mysql \ -lm -lmysqlclient -lz endif include ../../Makefile.modules install_module_custom: mkdir -p $(modules_prefix)/$(lib_dir)/opensipsctl ; \ sed -e "s#/usr/local/sbin#$(bin-target)#g" \ < $(SCRIPTS_DIR)/opensipsctl.mysql > /tmp/opensipsctl.mysql ; \ $(INSTALL_CFG) /tmp/opensipsctl.mysql \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.mysql ; \ rm -fr /tmp/opensipsctl.mysql ; \ sed -e "s#/usr/local/share/opensips#$(data_target)#g" \ < $(SCRIPTS_DIR)/opensipsdbctl.mysql > /tmp/opensipsdbctl.mysql ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsdbctl.mysql ; \ $(INSTALL_CFG) /tmp/opensipsdbctl.mysql $(modules_prefix)/$(lib_dir)/opensipsctl/ ; \ rm -fr /tmp/opensipsdbctl.mysql ; \ mkdir -p $(data_prefix)/$(data_dir)/mysql ; \ for FILE in $(wildcard $(SCRIPTS_DIR)/mysql/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/mysql/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/mysql/`basename "$$FILE"` ; \ fi ;\ done ; \ opensips-2.2.2/modules/db_mysql/README000066400000000000000000000075331300170765700175350ustar00rootroot00000000000000mysql Module Jan Janak Edited by Daniel-Constantin Mierla Copyright © 2006 Voice Sistem SRL Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. exec_query_threshold (integer) 1.3.2. timeout_interval (integer) 1.3.3. max_db_queries (integer) 1.3.4. max_db_retries (integer) 1.4. Exported Functions 1.5. Installation 1.6. Exported Events 1.6.1. E_MYSQL_CONNECTION List of Examples 1.1. Set exec_query_threshold parameter 1.2. Set timeout_interval parameter 1.3. Set max_db_queries parameter 1.4. Set max_db_retries parameter Chapter 1. Admin Guide 1.1. Overview This is a module which provides MySQL connectivity for OpenSIPS. It implements the DB API defined in OpenSIPS. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libmysqlclient-dev - the development libraries of mysql-client. 1.3. Exported Parameters 1.3.1. exec_query_threshold (integer) If queries take longer than 'exec_query_threshold' microseconds, warning messages will be written to logging facility. Default value is 0 - disabled. Example 1.1. Set exec_query_threshold parameter ... modparam("db_mysql", "exec_query_threshold", 60000) ... 1.3.2. timeout_interval (integer) Time interval after which a connection attempt (read or write request) is aborted. The value counts three times, as several retries are done from the driver before it gives up. The read timeout parameter is ignored on driver versions prior to “5.1.12â€, “5.0.25†and “4.1.22â€. The write timeout parameter is ignored on version prior to “5.1.12†and “5.0.25â€, the “4.1†release don't support it at all. Default value is 2 (6 sec). Example 1.2. Set timeout_interval parameter ... modparam("db_mysql", "timeout_interval", 2) ... 1.3.3. max_db_queries (integer) The maximum number of database queries to be executed. If this parameter is set improperly, it is set to default value. Default value is 2. Example 1.3. Set max_db_queries parameter ... modparam("db_mysql", "max_db_queries", 2) ... 1.3.4. max_db_retries (integer) The maximum number of database connection retries. If this parameter is set improperly, it is set to default value. Default value is 3. Example 1.4. Set max_db_retries parameter ... modparam("db_mysql", "max_db_retries", 2) ... 1.4. Exported Functions No function exported to be used from configuration file. 1.5. Installation Because it dependes on an external library, the mysql module is not compiled and installed by default. You can use one of the next options. * - edit the "Makefile" and remove "db_mysql" from "excluded_modules" list. Then follow the standard procedure to install OpenSIPS: "make all; make install". * - from command line use: 'make all include_modules="db_mysql"; make install include_modules="db_mysql"'. 1.6. Exported Events 1.6.1. E_MYSQL_CONNECTION This event is raised when a MySQL connection is lost or recovered. Parameters: * url - the URL of the connection as specified by the db_url parameter. * status - connected if the connection recovered, or disconnected if the connection was lost. opensips-2.2.2/modules/db_mysql/db_mysql.c000066400000000000000000000077031300170765700206320ustar00rootroot00000000000000/* * MySQL module interface * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) */ #include "../../sr_module.h" #include "../../db/db.h" #include "../../db/db_cap.h" #include "dbase.h" #include "db_mysql.h" #include unsigned int db_mysql_timeout_interval = 2; /* Default is 6 seconds */ unsigned int db_mysql_exec_query_threshold = 0; /* Warning in case DB query takes too long disabled by default*/ int max_db_retries = 3; int max_db_queries = 2; static int mysql_mod_init(void); int db_mysql_bind_api(const str* mod, db_func_t *dbb); /* * MySQL database module interface */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)db_mysql_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"timeout_interval", INT_PARAM, &db_mysql_timeout_interval}, {"exec_query_threshold", INT_PARAM, &db_mysql_exec_query_threshold}, {"max_db_retries", INT_PARAM, &max_db_retries}, {"max_db_queries", INT_PARAM, &max_db_queries}, {0, 0, 0} }; struct module_exports exports = { "db_mysql", MOD_TYPE_SQLDB, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mysql_mod_init, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0 /* per-child init function */ }; static int mysql_mod_init(void) { LM_DBG("mysql: MySQL client version is %s\n", mysql_get_client_info()); /* also register the event */ if (mysql_register_event() < 0) { LM_ERR("Cannot register mysql event\n"); return -1; } if(max_db_queries < 1){ LM_WARN("Invalid number for max_db_queries\n"); max_db_queries = 2; } if(max_db_retries < 0){ LM_WARN("Invalid number for max_db_retries\n"); max_db_retries = 3; } return 0; } int db_mysql_bind_api(const str* mod, db_func_t *dbb) { if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->use_table = db_mysql_use_table; dbb->init = db_mysql_init; dbb->close = db_mysql_close; dbb->query = db_mysql_query; dbb->fetch_result = db_mysql_fetch_result; dbb->raw_query = db_mysql_raw_query; dbb->free_result = db_mysql_free_result; dbb->insert = db_mysql_insert; dbb->delete = db_mysql_delete; dbb->update = db_mysql_update; dbb->replace = db_mysql_replace; dbb->last_inserted_id = db_last_inserted_id; dbb->insert_update = db_insert_update; dbb->async_raw_query = db_mysql_async_raw_query; dbb->async_resume = db_mysql_async_resume; dbb->async_free_result = db_mysql_async_free_result; dbb->cap |= DB_CAP_MULTIPLE_INSERT; return 0; } opensips-2.2.2/modules/db_mysql/db_mysql.h000066400000000000000000000023701300170765700206320ustar00rootroot00000000000000/* * MySQL module interface * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) */ #ifndef DB_MOD_H #define DB_MOD_H extern unsigned int db_mysql_timeout_interval; extern unsigned int db_mysql_exec_query_threshold; extern int max_db_retries; extern int max_db_queries; int mysql_register_event(void); #endif /* DB_MOD_H */ opensips-2.2.2/modules/db_mysql/dbase.c000066400000000000000000001126731300170765700201010ustar00rootroot00000000000000/* * MySQL module core functions * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \file * \brief Implementation of core functions for the MySQL driver. * * This file contains the implementation of core functions for the MySQL * database driver, for example to submit a query or fetch a result. */ #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../db/db_query.h" #include "../../db/db_async.h" #include "../../db/db_ut.h" #include "../../db/db_insertq.h" #include "val.h" #include "my_con.h" #include "res.h" #include "row.h" #include "db_mysql.h" #include "dbase.h" static str mysql_event_name = str_init("E_MYSQL_CONNECTION"); static str mysql_url_str = str_init("url"); static str mysql_stat_str = str_init("status"); static str mysql_stat_connected_str = str_init("connected"); static str mysql_stat_disconnected_str = str_init("disconnected"); static event_id_t mysql_evi_id = EVI_ERROR; static int mysql_last_event = 0; /* ensures an event is raised only when status changes */ int mysql_register_event(void) { mysql_evi_id = evi_publish_event(mysql_event_name); if (mysql_evi_id == EVI_ERROR) { LM_ERR("cannot register event\n"); return -1; } return 0; } static inline void mysql_raise_event(const db_con_t *conn) { evi_params_p list = NULL; if (mysql_evi_id == EVI_ERROR) { LM_DBG("event not registered %d\n", mysql_evi_id); return; } if (!conn) { LM_ERR("no connection specified\n"); return; } if (mysql_last_event == CON_DISCON(conn)) { LM_DBG("MySQL status has not changed: %s\n", mysql_last_event ? "disconnected" : "connected"); return; } mysql_last_event = CON_DISCON(conn); if (evi_probe_event(mysql_evi_id)) { if (!(list = evi_get_params())) return; if (evi_param_add_str(list, &mysql_url_str, (void*)&conn->url)) { LM_ERR("unable to add url parameter\n"); goto free; } if (evi_param_add_str(list, &mysql_stat_str, CON_DISCON(conn) ? &mysql_stat_disconnected_str : &mysql_stat_connected_str)) { LM_ERR("unable to add status parameter\n"); goto free; } if (evi_raise_event(mysql_evi_id, list)) { LM_ERR("unable to send event\n"); } } else { LM_DBG("no event sent\n"); } return; free: evi_free_params(list); } static inline int wrapper_single_mysql_stmt_prepare(const db_con_t *conn, const struct my_stmt_ctx *ctx) { int code, error; if (CON_DISCON(conn)) return -1; code = mysql_stmt_prepare(ctx->stmt, ctx->query.s, ctx->query.len); if (code == 0) return 0; error = mysql_stmt_errno(ctx->stmt); switch (error) { case CR_SERVER_GONE_ERROR: case CR_SERVER_LOST: case CR_COMMANDS_OUT_OF_SYNC: return -1; /* reconnection error -> <0 */ default: LM_CRIT("driver error (%i): %s\n", error, mysql_stmt_error(ctx->stmt)); /* do not rely on libmysqlclient implementation * specification says non-zero code on error, not positive code */ return 1; } } static inline int wrapper_single_mysql_stmt_execute(const db_con_t *conn, MYSQL_STMT *stmt) { int code, error; if (CON_DISCON(conn)) return -1; code = mysql_stmt_execute(stmt); if (code == 0) return 0; error = mysql_stmt_errno(stmt); switch (error) { case CR_SERVER_GONE_ERROR: case CR_SERVER_LOST: case CR_COMMANDS_OUT_OF_SYNC: return -1; /* reconnection error -> <0 */ default: LM_CRIT("driver error (%i): %s\n", error, mysql_stmt_error(stmt)); /* do not rely on libmysqlclient implementation * specification says non-zero code on error, not positive code */ return 1; } } static inline int wrapper_single_mysql_real_query(const db_con_t *conn, const str *query) { int code, error; if (CON_DISCON(conn)) return -1; code = mysql_real_query(CON_CONNECTION(conn), query->s, query->len); if (code == 0) return 0; error = mysql_errno(CON_CONNECTION(conn)); switch (error) { case CR_SERVER_GONE_ERROR: case CR_SERVER_LOST: case CR_COMMANDS_OUT_OF_SYNC: return -1; /* reconnection error -> <0 */ default: LM_CRIT("driver error (%i): %s\n", error, mysql_error(CON_CONNECTION(conn))); /* do not rely on libmysqlclient implementation * specification says non-zero code on error, not positive code */ return 1; } } static inline int wrapper_single_mysql_send_query(const db_con_t *conn, const str *query) { int code, error; if (CON_DISCON(conn)) return -1; code = mysql_send_query(CON_CONNECTION(conn), query->s, query->len); if (code == 0) return 0; error = mysql_errno(CON_CONNECTION(conn)); switch (error) { case CR_SERVER_GONE_ERROR: case CR_SERVER_LOST: case CR_COMMANDS_OUT_OF_SYNC: return -1; /* reconnection error -> <0 */ default: LM_CRIT("driver error (%i): %s\n", error, mysql_error(CON_CONNECTION(conn))); /* do not rely on libmysqlclient implementation * specification says non-zero code on error, not positive code */ return 1; } } static inline int connect_with_retry(const db_con_t *conn, const int max_tries) { int try, code; for (try = 0 ; trytail)) == 0) { /* we reconnected back */ CON_DISCON(conn) = 0; LM_INFO("re-connected successful for %p\n", (void*)conn->tail); return 0; } else { LM_INFO("temporary re-connect failure for %p\n",(void*)conn->tail); } } LM_ERR("permanent re-connect failure for %p\n", (void*)conn->tail); return 1; } static inline void reset_all_statements(const db_con_t* conn) { struct prep_stmt *pq_ptr; struct my_stmt_ctx *ctx; LM_INFO("reseting all statements on connection: (%p) %p\n", conn,(void*)conn->tail); for( pq_ptr=CON_PS_LIST(conn); pq_ptr ; pq_ptr=pq_ptr->next ) { for (ctx = pq_ptr->stmts ; ctx ; ctx=ctx->next ) { LM_DBG("resetting statement (%p,%p) for context %p (%.*s)\n", pq_ptr,ctx->stmt, ctx, ctx->table.len,ctx->table.s); if (ctx->stmt) { mysql_stmt_close(ctx->stmt); ctx->stmt = NULL; ctx->has_out = 0; } } } } static inline void switch_state_to_disconnected(const db_con_t *conn) { LM_INFO("disconnect event for %p\n",(void*)conn->tail); if (CON_DISCON(conn) == 0) { CON_DISCON(conn) = 1; reset_all_statements(conn); } } /** * \brief Send a SQL query to the server. * * Send a SQL query to the database server. * * \param _h handle for the db * \param _s executed query * \return zero on success, negative value on failure */ static int db_mysql_submit_query(const db_con_t* _h, const str* _s) { int code, i; struct timeval start; if (!_h || !_s || !_s->s) { LM_ERR("invalid parameter value\n"); return -1; } /* screws up the terminal when the query contains a BLOB :-( (by bogdan) * LM_DBG("submit_query(): %.*s\n", _s->len, _s->s); */ for (i=0; is,_s->len,0); if (code < 0) { /* got disconnected during call */ switch_state_to_disconnected(_h); if (connect_with_retry(_h, max_db_retries) != 0) { /* mysql reconnection problem */ LM_ERR("failed to reconnect before trying " "mysql_stmt_prepare()\n"); break; } /* if reconnected, run the loop again */ } else if (code > 0) { /* other problems - error already logged by the wrapper */ return -2; } else { mysql_raise_event(_h); return 0; /* success */ } } mysql_raise_event(_h); LM_CRIT("too many mysql server reconnection failures\n"); return -2; } /* * Actually free prep_stmt structure */ static void db_mysql_free_pq(struct prep_stmt *pq_ptr) { struct my_stmt_ctx *ctx; struct my_stmt_ctx *ctx2; if ( pq_ptr == NULL ) return; for(ctx=pq_ptr->stmts ; ctx ; ) { ctx2 = ctx; ctx = ctx->next; if (ctx2->stmt) mysql_stmt_close(ctx2->stmt); pkg_free(ctx2); } /* free out part */ if (pq_ptr->bind_out) pkg_free(pq_ptr->bind_out); /* free in part and the struct */ pkg_free(pq_ptr); } /* ** Free all allocated prep_stmt structures */ void db_mysql_free_stmt_list(struct prep_stmt *head) { struct prep_stmt *pq_ptr; while ( head!= NULL ) { pq_ptr = head; head = head->next; db_mysql_free_pq(pq_ptr); } } static int has_stmt_ctx(const db_con_t* conn, struct my_stmt_ctx **ctx_p) { struct my_stmt_ctx *ctx; if (CON_MYSQL_PS(conn) != NULL) { /* search for the context */ for ( ctx=CON_PS_STMTS(conn) ; ctx ; ctx=ctx->next ) { if (ctx->table.len== CON_TABLE(conn)->len && memcmp(ctx->table.s, CON_TABLE(conn)->s, CON_TABLE(conn)->len)==0){ LM_DBG("ctx found for %.*s\n", ctx->table.len,ctx->table.s); *ctx_p = ctx; return 1; } } } *ctx_p = NULL; LM_DBG("ctx not found for %.*s\n", CON_TABLE(conn)->len, CON_TABLE(conn)->s); return 0; } static int re_init_statement(const db_con_t* conn, struct prep_stmt *pq_ptr, struct my_stmt_ctx *ctx, int free_ctx) { struct my_stmt_ctx *ctx1, *ctx2; int code; int i; LM_DBG(" query is <%.*s>, ptr=%p\n", ctx->query.len, ctx->query.s, ctx->stmt); for( i=0 ; i< max_db_queries ; i++ ) { /* re-init the statement */ if ( !(ctx->stmt=mysql_stmt_init(CON_CONNECTION(conn))) ) { LM_ERR("failed while mysql_stmt_init()\n"); goto error; } code = wrapper_single_mysql_stmt_prepare(conn, ctx); if (code < 0) { /* got disconnected during call */ switch_state_to_disconnected(conn); if (connect_with_retry(conn, max_db_retries) != 0) { /* mysql reconnection problem */ LM_ERR("failed to reconnect before trying " "mysql_stmt_prepare()\n"); break; } /* if reconnected, run the loop again */ } else if (code > 0) { /* other problems */ goto error; } else { mysql_raise_event(conn); return 0; /* success */ } } mysql_raise_event(conn); /* destroy the statement only, but keep the context */ if (ctx->stmt) mysql_stmt_close(ctx->stmt); else LM_ERR("statement already uninitialised while trying to clean up\n"); ctx->stmt = NULL; return -1; error: /* error -> destroy the context only */ if (ctx->stmt) mysql_stmt_close(ctx->stmt); else LM_ERR("statement already uninitialised while trying to " "clean up after error\n"); if (free_ctx) { /* remove the context from STMT list */ for( ctx1=NULL,ctx2=pq_ptr->stmts ; ctx2 ; ) { if (ctx2==ctx) { if (ctx1) ctx1->next = ctx2->next; else pq_ptr->stmts = ctx2->next; break; } ctx1 = ctx2; ctx2 = ctx2->next; } pkg_free(ctx); } else ctx->stmt = NULL; return -1; } static struct my_stmt_ctx * get_new_stmt_ctx(const db_con_t* conn, const str *query) { struct my_stmt_ctx *ctx; /* new one */ ctx = (struct my_stmt_ctx*)pkg_malloc ( sizeof(struct my_stmt_ctx) + CON_TABLE(conn)->len + query->len); if (ctx==NULL) { LM_ERR("no more pkg mem for statement context\n"); return NULL; } memset( ctx, 0, sizeof(struct my_stmt_ctx) + CON_TABLE(conn)->len + query->len); ctx->table.s = (char*)(ctx+1); ctx->table.len = CON_TABLE(conn)->len; memcpy( ctx->table.s, CON_TABLE(conn)->s, ctx->table.len); ctx->query.s = ctx->table.s + ctx->table.len; ctx->query.len = query->len; memcpy( ctx->query.s, query->s, query->len); ctx->next = 0; ctx->has_out = 0; if (re_init_statement(conn, NULL, ctx, 0) == 0) return ctx; else { /* make sure ctx is freed on every error in re_init_statement */ pkg_free(ctx); return NULL; } } static struct prep_stmt* alloc_new_prepared_stmt(const db_con_t *conn,const db_val_t* v, int n, const db_val_t* uv, int un) { struct prep_stmt *pq_ptr; MYSQL_TIME *mt; int total; int time_no; int i; int len; if (query_buffer_size > 1 && CON_HAS_INSLIST(conn)) total = n*query_buffer_size; else total = n+un; time_no = 0; for(i=0 ; i 1 && CON_HAS_INSLIST(conn)) time_no *= query_buffer_size; for(i=0 ; ibind_in = (MYSQL_BIND*)(pq_ptr+1); pq_ptr->in_bufs = (struct bind_icontent*)(pq_ptr->bind_in + total); mt = (MYSQL_TIME*)(pq_ptr->in_bufs + total); if (query_buffer_size > 1 && CON_HAS_INSLIST(conn)) { for (i=0;ibind_in[i].length = &pq_ptr->in_bufs[i].len; pq_ptr->bind_in[i].is_null = &pq_ptr->in_bufs[i].null; if (VAL_TYPE(v+i%n)==DB_DATETIME) pq_ptr->bind_in[i].buffer = &mt[--time_no]; } } else { for( i=0 ; ibind_in[i].length = &pq_ptr->in_bufs[i].len; pq_ptr->bind_in[i].is_null = &pq_ptr->in_bufs[i].null; if (VAL_TYPE(v+i)==DB_DATETIME) pq_ptr->bind_in[i].buffer = &mt[--time_no]; } for( i=0 ; ibind_in[n+i].length = &pq_ptr->in_bufs[n+i].len; pq_ptr->bind_in[n+i].is_null = &pq_ptr->in_bufs[n+i].null; if (VAL_TYPE(uv+i)==DB_DATETIME) pq_ptr->bind_in[n+i].buffer = &mt[--time_no]; } } if (time_no!=0) { LM_CRIT("bug - time_no=%d\n",time_no); pkg_free(pq_ptr); return NULL; } } return pq_ptr; } /** Try to exec SQL query using prepared statements API ** ** All query templates and pointers to in/out params are stored in ** prep_stmt structure. This structures organized in two-connected ** list and query template searches in this list fistly. ** */ static int db_mysql_do_prepared_query(const db_con_t* conn, const str *query, const db_val_t* v, int n, const db_val_t* uv, int un) { int i,j,code, cols; struct prep_stmt *pq_ptr; struct my_stmt_ctx *ctx; MYSQL_BIND *mysql_bind; struct timeval start; db_val_t **buffered_rows = NULL; LM_DBG("conn=%p (tail=%ld) MC=%p\n",conn, conn->tail,CON_CONNECTION(conn)); if ( CON_MYSQL_PS(conn) == NULL ) { /* First time when this query is run, so we need to init it -> ** allocate new structure for prepared statemet and its values */ LM_DBG("new query=|%.*s|\n", query->len, query->s); pq_ptr = alloc_new_prepared_stmt(conn,v, n, uv, un); if (pq_ptr==NULL) { LM_ERR("failed to allocated a new statement\n"); return -1; } /* get a new context */ ctx = get_new_stmt_ctx(conn, query); if (ctx==NULL) { LM_ERR("failed to create new context\n"); pkg_free(pq_ptr); return -1; } /* link it */ pq_ptr->stmts = ctx; /* set it as current */ pq_ptr->ctx = ctx; pq_ptr->cols_out = -1; /* set the in bind array */ mysql_bind = pq_ptr->bind_in; /* link it to the connection */ pq_ptr->next = CON_PS_LIST(conn); CON_PS_LIST(conn) = pq_ptr; LM_DBG("new statement(%p) on connection: (%p) %p\n", pq_ptr, conn, (void*)conn->tail); /* also return it for direct future usage */ CON_CURR_PS(conn) = pq_ptr; } else { pq_ptr = CON_MYSQL_PS(conn); mysql_bind = pq_ptr->bind_in; if (pq_ptr->ctx==NULL) { /* get a new context */ ctx = get_new_stmt_ctx(conn, query); if (ctx==NULL) { LM_ERR("failed to create new context\n"); return -1; } /* link it */ ctx->next = pq_ptr->stmts; pq_ptr->stmts = ctx; /* set it as current */ pq_ptr->ctx = ctx; } else { ctx = pq_ptr->ctx; if ( ctx->stmt==NULL && re_init_statement(conn, pq_ptr, ctx, 1)!=0 ) { LM_ERR("failed to re-init statement!\n"); return -1; } } } if (query_buffer_size > 1 && CON_HAS_INSLIST(conn)) { if (ql_row_add(conn->ins_list,v,&buffered_rows) < 0) { LM_ERR("failed to insert row to buffered list\n"); return -1; } /* if add succesfull but not yet insertion time return success and wait for number of query rows to pile up */ if (buffered_rows == NULL) return 0; } LM_DBG("set values for the statement run\n"); if (query_buffer_size > 1 && CON_HAS_INSLIST(conn)) { /* got here, we need to push data to DB * first bind data */ for (j=0;jstmt, mysql_bind) ) { LM_ERR("mysql_stmt_bind_param() failed: %s\n", mysql_stmt_error(ctx->stmt)); for(i=0;istmt); if ( ! CON_RESULT(conn) ) { cols = 0; } else { cols = mysql_num_fields(CON_RESULT(conn)); } start_expire_timer(start,db_mysql_exec_query_threshold); code = wrapper_single_mysql_stmt_execute(conn, ctx->stmt); stop_expire_timer(start,db_mysql_exec_query_threshold,"mysql prep stmt",query->s,query->len,0); if (code < 0) { /* got disconnected during call */ switch_state_to_disconnected(conn); if (connect_with_retry(conn, max_db_retries) != 0) { /* mysql reconnection problem */ LM_ERR("failed to reconnect before trying mysql_stmt_prepare()\n"); break; } /* if reconnected, run the loop again */ LM_INFO("reconnected to mysql server -> re-init the statement\n"); if ( re_init_statement(conn, pq_ptr, ctx, 1)!=0 ) { LM_ERR("failed to re-init statement!\n"); cleanup_rows(buffered_rows); return -1; } i++; } else if (code > 0) { /* other problems */ cleanup_rows(buffered_rows); return -1; } } while (code!=0 && i< max_db_queries ); mysql_raise_event(conn); if (code != 0) { LM_CRIT("too many mysql server reconnection failures\n"); cleanup_rows(buffered_rows); return -1; } cleanup_rows(buffered_rows); /* check and get results */ if ( cols>0 ) { LM_DBG("prepared statement has %d columns in result\n",cols); /* set the out bind array ? */ if (pq_ptr->cols_out==-1) { pq_ptr->cols_out = cols; pq_ptr->bind_out = (MYSQL_BIND*)pkg_malloc ( cols*(sizeof(struct bind_ocontent) + sizeof(MYSQL_BIND)) ); if (pq_ptr->bind_out==NULL) { db_mysql_free_pq(pq_ptr); CON_CURR_PS(conn) = NULL; LM_ERR("no more pkg mem for the a new prepared statement\n"); return -1; } memset(pq_ptr->bind_out, 0 , cols*(sizeof(struct bind_ocontent) + sizeof(MYSQL_BIND))); pq_ptr->out_bufs = (struct bind_ocontent*)(pq_ptr->bind_out+cols); mysql_bind = pq_ptr->bind_out; /* prepare the pointers */ for( i=0 ; icols_out ; i++ ) { mysql_bind[i].buffer = pq_ptr->out_bufs[i].buf; mysql_bind[i].buffer_type = MYSQL_TYPE_STRING; mysql_bind[i].buffer_length = PREP_STMT_VAL_LEN; mysql_bind[i].length = &pq_ptr->out_bufs[i].len; mysql_bind[i].is_null = &pq_ptr->out_bufs[i].null; #if (MYSQL_VERSION_ID >= 50030) mysql_bind[i].error = &pq_ptr->out_bufs[i].error; #endif } /* bind out values to the statement */ LM_DBG("doing to BIND_PARAM out ...\n"); if ( mysql_stmt_bind_result(ctx->stmt, mysql_bind) ) { LM_ERR("mysql_stmt_bind_result() failed: %s\n", mysql_stmt_error(ctx->stmt)); return -1; } ctx->has_out = 1; } else if (!ctx->has_out) { /* bind out values to the statement */ LM_DBG("doing to BIND_PARAM out ...\n"); if ( mysql_stmt_bind_result(ctx->stmt, pq_ptr->bind_out) ) { LM_ERR("mysql_stmt_bind_result() failed: %s\n", mysql_stmt_error(ctx->stmt)); return -1; } ctx->has_out = 1; } if ( mysql_stmt_store_result(ctx->stmt) ) { LM_ERR("mysql_stmt_store_result() failed: %s (%d)\n", mysql_stmt_error(ctx->stmt), mysql_stmt_errno(ctx->stmt)); return -1; } } return 0; } static str query_holder = {NULL,0}; static int db_mysql_submit_dummy_query(const db_con_t* _h, const str* _s) { query_holder = *_s; return 0; } /** * Initialize the database module. * No function should be called before this * \param _url URL used for initialization * \return zero on success, negative value on failure */ db_con_t* db_mysql_init(const str* _url) { return db_do_init(_url, (void *)db_mysql_new_connection); } /** * Shut down the database module. * No function should be called after this * \param _h handle to the closed connection * \return zero on success, negative value on failure */ void db_mysql_close(db_con_t* _h) { db_do_close(_h, db_mysql_free_connection); } /** * Retrieve a result set * \param _h handle to the database * \param _r result set that should be retrieved * \return zero on success, negative value on failure */ static int db_mysql_store_result(const db_con_t* _h, db_res_t** _r) { if ((!_h) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } *_r = db_new_result(); if (*_r == 0) { LM_ERR("no memory left\n"); return -2; } if (!CON_HAS_PS(_h)) CON_RESULT(_h) = mysql_store_result(CON_CONNECTION(_h)); if (!CON_RESULT(_h)) { if (mysql_field_count(CON_CONNECTION(_h)) == 0) { (*_r)->col.n = 0; (*_r)->n = 0; goto done; } else { LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h))); db_free_result(*_r); *_r = 0; return -3; } } if (db_mysql_convert_result(_h, *_r) < 0) { LM_ERR("error while converting result\n"); pkg_free(*_r); *_r = 0; /* all mem on opensips API side is already freed by * db_mysql_convert_result in case of error, but we also need * to free the mem from the mysql lib side */ mysql_free_result(CON_RESULT(_h)); #if (MYSQL_VERSION_ID >= 40100) while( mysql_next_result( CON_CONNECTION(_h) ) > 0 ) { MYSQL_RES *res = mysql_store_result( CON_CONNECTION(_h) ); mysql_free_result(res); } #endif CON_RESULT(_h) = 0; return -4; } done: #if (MYSQL_VERSION_ID >= 40100) while( mysql_next_result( CON_CONNECTION(_h) ) > 0 ) { MYSQL_RES *res = mysql_store_result( CON_CONNECTION(_h) ); mysql_free_result(res); } #endif return 0; } /** * Release a result set from memory. * \param _h handle to the database * \param _r result set that should be freed * \return zero on success, negative value on failure */ int db_mysql_free_result(db_con_t* _h, db_res_t* _r) { if ((!_h) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } if (db_free_result(_r) < 0) { LM_ERR("unable to free result structure\n"); return -1; } mysql_free_result(CON_RESULT(_h)); CON_RESULT(_h) = 0; return 0; } /** * Query a table for specified rows. * \param _h structure representing database connection * \param _k key names * \param _op operators *\param _v values of the keys that must match * \param _c column names to return * \param _n number of key=values pairs to compare * \param _nc number of columns to return * \param _o order by the specified column * \param _r pointer to a structure representing the result * \return zero on success, negative value on failure */ int db_mysql_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r) { int ret; if (CON_HAS_PS(_h)) { if (CON_HAS_UNINIT_PS(_h)||!has_stmt_ctx(_h,&(CON_MYSQL_PS(_h)->ctx))) { ret = db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, NULL, db_mysql_val2str, db_mysql_submit_dummy_query, NULL); if (ret != 0) { CON_RESET_CURR_PS(_h); if (_r) *_r = NULL; return ret; } } ret = db_mysql_do_prepared_query(_h, &query_holder, _v, _n, NULL, 0); if (ret != 0) { CON_RESET_CURR_PS(_h); if (_r) *_r = NULL; return ret; } ret = db_mysql_store_result(_h, _r); CON_RESET_CURR_PS(_h); return ret; } return db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r, db_mysql_val2str, db_mysql_submit_query, db_mysql_store_result); } /** * Gets a partial result set. * \param _h structure representing the database connection * \param _r pointer to a structure representing the result * \param nrows number of fetched rows * \return zero on success, negative value on failure */ int db_mysql_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows) { int rows, i; if (!_h || !_r || nrows < 0) { LM_ERR("Invalid parameter value\n"); return -1; } /* exit if the fetch count is zero */ if (nrows == 0) { db_free_result(*_r); *_r = 0; return 0; } if(*_r==0) { /* Allocate a new result structure */ *_r = db_new_result(); if (*_r == 0) { LM_ERR("no memory left\n"); return -2; } if (!CON_HAS_PS(_h)) CON_RESULT(_h) = mysql_store_result(CON_CONNECTION(_h)); if (!CON_RESULT(_h)) { if (mysql_field_count(CON_CONNECTION(_h)) == 0) { (*_r)->col.n = 0; (*_r)->n = 0; return 0; } else { LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h))); db_free_result(*_r); *_r = 0; return -3; } } if (db_mysql_get_columns(_h, *_r) < 0) { LM_ERR("error while getting column names\n"); return -4; } if (CON_HAS_PS(_h)) { RES_NUM_ROWS(*_r) = mysql_stmt_num_rows(CON_PS_STMT(_h)); } else { RES_NUM_ROWS(*_r) = mysql_num_rows(CON_RESULT(_h)); } if (!RES_NUM_ROWS(*_r)) { LM_DBG("no rows returned from the query\n"); RES_ROWS(*_r) = 0; return 0; } } else { /* free old rows */ if(RES_ROWS(*_r)!=0) db_free_rows(*_r); RES_ROWS(*_r) = 0; RES_ROW_N(*_r) = 0; } /* determine the number of rows remaining to be processed */ rows = RES_NUM_ROWS(*_r) - RES_LAST_ROW(*_r); /* If there aren't any more rows left to process, exit */ if(rows<=0) return 0; /* if the fetch count is less than the remaining rows to process */ /* set the number of rows to process (during this call) equal to the fetch count */ if(nrows < rows) rows = nrows; RES_ROW_N(*_r) = rows; LM_DBG("converting row %d of %d count %d\n", RES_LAST_ROW(*_r), RES_NUM_ROWS(*_r), RES_ROW_N(*_r)); if (db_allocate_rows(*_r, rows)!=0) { LM_ERR("no memory left\n"); return -5; } for(i = 0; i < rows; i++) { if (CON_HAS_PS(_h)) { if ( mysql_stmt_fetch(CON_PS_STMT(_h)) == 1 ) { LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h))); RES_ROW_N(*_r) = i; db_free_rows(*_r); return -6; } } else { CON_ROW(_h) = mysql_fetch_row(CON_RESULT(_h)); if (!CON_ROW(_h)) { LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h))); RES_ROW_N(*_r) = i; db_free_rows(*_r); return -6; } } if (db_mysql_convert_row(_h, *_r, &(RES_ROWS(*_r)[i])) < 0) { LM_ERR("error while converting row #%d\n", i); RES_ROW_N(*_r) = i; db_free_rows(*_r); return -7; } } /* update the total number of rows processed */ RES_LAST_ROW(*_r) += rows; return 0; } /** * Execute a raw SQL query. * \param _h handle for the database * \param _s raw query string * \param _r result set for storage * \return zero on success, negative value on failure */ int db_mysql_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r) { CON_RESET_CURR_PS(_h); /* no prepared statements support */ return db_do_raw_query(_h, _s, _r, db_mysql_submit_query, db_mysql_store_result); } static inline int db_mysql_get_con_fd(void *con) { return ((struct my_con *)con)->con->net.fd; } /** * Begins execution of a raw SQL query. Returns immediately. * * \param _h handle for the database * \param _s raw query string * \param _priv internal parameter; holds the conn that the query is bound to * \return * success: Unix FD for polling * failure: negative error code */ int db_mysql_async_raw_query(db_con_t *_h, const str *_s, void **_priv) { int *fd_ref; int code, i; struct timeval start; struct my_con *con; if (!_h || !_s || !_s->s) { LM_ERR("invalid parameter value\n"); return -1; } con = (struct my_con *)db_init_async(_h, db_mysql_get_con_fd, &fd_ref, (void *)db_mysql_new_connection); *_priv = con; if (!con) LM_INFO("Failed to open new connection (current: 1 + %d). Running " "in sync mode!\n", ((struct pool_con *)_h->tail)->no_transfers); /* no prepared statements support */ CON_RESET_CURR_PS(_h); for (i = 0; i < max_db_queries; i++) { start_expire_timer(start, db_mysql_exec_query_threshold); /* async mode */ if (con) { code = wrapper_single_mysql_send_query(_h, _s); /* sync mode */ } else { code = wrapper_single_mysql_real_query(_h, _s); } stop_expire_timer(start, db_mysql_exec_query_threshold, "mysql async query", _s->s, _s->len, 0); if (code < 0) { /* got disconnected during call */ switch_state_to_disconnected(_h); if (connect_with_retry(_h, max_db_retries) != 0) { /* mysql reconnection problem */ LM_ERR("failed to reconnect before trying " "mysql_stmt_prepare()\n"); break; } /* if reconnected, run the loop again */ } else if (code > 0) { /* other problems - error already logged by the wrapper */ goto out; } else { /* success */ mysql_raise_event(_h); if (!con) return -1; *fd_ref = db_mysql_get_con_fd(con); db_switch_to_sync(_h); return *fd_ref; } } mysql_raise_event(_h); LM_CRIT("too many mysql server reconnection failures\n"); out: if (!con) return -1; db_switch_to_sync(_h); db_store_async_con(_h, (struct pool_con *)con); return -2; } int db_mysql_async_resume(db_con_t *_h, int fd, db_res_t **_r, void *_priv) { struct pool_con *con = (struct pool_con *)_priv; int rc; #ifdef EXTRA_DEBUG if (!db_match_async_con(fd, _h)) { LM_BUG("no conn match for fd %d", fd); abort(); } #endif db_switch_to_async(_h, con); rc = mysql_read_query_result(CON_CONNECTION(_h)); LM_DBG("mysql_read_query_result: %d, %s - \"%s\"\n", mysql_errno(CON_CONNECTION(_h)), mysql_sqlstate(CON_CONNECTION(_h)), mysql_error(CON_CONNECTION(_h))); /* error status (most likely from a bad query) */ if (rc != 0) { LM_ERR("error [%d, %s]: %s\n", mysql_errno(CON_CONNECTION(_h)), mysql_sqlstate(CON_CONNECTION(_h)), mysql_error(CON_CONNECTION(_h))); mysql_free_result(CON_RESULT(_h)); CON_RESULT(_h) = NULL; db_switch_to_sync(_h); db_store_async_con(_h, con); return -1; } if (_r) { if (db_mysql_store_result(_h, _r) != 0) { LM_ERR("failed to store result\n"); db_switch_to_sync(_h); db_store_async_con(_h, con); return -2; } } db_switch_to_sync(_h); db_store_async_con(_h, con); return 0; } int db_mysql_async_free_result(db_con_t *_h, db_res_t *_r, void *_priv) { struct my_con *con = (struct my_con *)_priv; if (_r && db_free_result(_r) < 0) { LM_ERR("error while freeing result structure\n"); return -1; } mysql_free_result(con->res); con->res = NULL; return 0; } /** * Insert a row into a specified table. * \param _h structure representing database connection * \param _k key names * \param _v values of the keys * \param _n number of key=value pairs * \return zero on success, negative value on failure */ int db_mysql_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { int ret; if (CON_HAS_PS(_h)) { if (CON_HAS_UNINIT_PS(_h)||!has_stmt_ctx(_h,&(CON_MYSQL_PS(_h)->ctx))){ ret = db_do_insert(_h, _k, _v, _n, db_mysql_val2str, db_mysql_submit_dummy_query); if (ret!=0) goto res_ps; } ret = db_mysql_do_prepared_query(_h, &query_holder, _v, _n, NULL, 0); goto res_ps; } ret = db_do_insert(_h, _k, _v, _n, db_mysql_val2str, db_mysql_submit_query); goto res_ins; res_ps: CON_RESET_CURR_PS(_h); res_ins: if (CON_HAS_INSLIST(_h)) CON_RESET_INSLIST(_h); return ret; } /** * Delete a row from the specified table * \param _h structure representing database connection * \param _k key names * \param _o operators * \param _v values of the keys that must match * \param _n number of key=value pairs * \return zero on success, negative value on failure */ int db_mysql_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n) { int ret; if (CON_HAS_PS(_h)) { if (CON_HAS_UNINIT_PS(_h)||!has_stmt_ctx(_h,&(CON_MYSQL_PS(_h)->ctx))){ ret = db_do_delete(_h, _k, _o, _v, _n, db_mysql_val2str, db_mysql_submit_dummy_query); if (ret!=0) {CON_RESET_CURR_PS(_h);return ret;} } ret = db_mysql_do_prepared_query(_h, &query_holder, _v, _n, NULL, 0); CON_RESET_CURR_PS(_h); return ret; } return db_do_delete(_h, _k, _o, _v, _n, db_mysql_val2str, db_mysql_submit_query); } /** * Update some rows in the specified table * \param _h structure representing database connection * \param _k key names * \param _o operators * \param _v values of the keys that must match * \param _uk updated columns * \param _uv updated values of the columns * \param _n number of key=value pairs * \param _un number of columns to update * \return zero on success, negative value on failure */ int db_mysql_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un) { int ret; if (CON_HAS_PS(_h)) { if (CON_HAS_UNINIT_PS(_h)||!has_stmt_ctx(_h,&(CON_MYSQL_PS(_h)->ctx))){ ret = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_mysql_val2str, db_mysql_submit_dummy_query); if (ret!=0) {CON_RESET_CURR_PS(_h);return ret;} } ret = db_mysql_do_prepared_query(_h, &query_holder, _uv, _un, _v, _n); CON_RESET_CURR_PS(_h); return ret; } return db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_mysql_val2str, db_mysql_submit_query); } /** * Just like insert, but replace the row if it exists. * \param _h database handle * \param _k key names * \param _v values of the keys that must match * \param _n number of key=value pairs * \return zero on success, negative value on failure */ int db_mysql_replace(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { int ret; if (CON_HAS_PS(_h)) { if (CON_HAS_UNINIT_PS(_h)||!has_stmt_ctx(_h,&(CON_MYSQL_PS(_h)->ctx))){ ret = db_do_replace(_h, _k, _v, _n, db_mysql_val2str, db_mysql_submit_dummy_query); if (ret!=0) {CON_RESET_CURR_PS(_h);return ret;} } ret = db_mysql_do_prepared_query(_h, &query_holder, _v, _n, NULL, 0); CON_RESET_CURR_PS(_h); return ret; } return db_do_replace(_h, _k, _v, _n, db_mysql_val2str, db_mysql_submit_query); } /** * Returns the last inserted ID. * \param _h database handle * \return returns the ID as integer or returns 0 if the previous statement * does not use an AUTO_INCREMENT value. */ int db_last_inserted_id(const db_con_t* _h) { if (!_h) { LM_ERR("invalid parameter value\n"); return -1; } return mysql_insert_id(CON_CONNECTION(_h)); } /** * Insert a row into a specified table, update on duplicate key. * \param _h structure representing database connection * \param _k key names * \param _v values of the keys * \param _n number of key=value pairs */ int db_insert_update(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { int off, ret; static str sql_str; static char sql_buf[SQL_BUF_LEN]; if ((!_h) || (!_k) || (!_v) || (!_n)) { LM_ERR("invalid parameter value\n"); return -1; } CON_RESET_CURR_PS(_h); /* no prepared statements support */ ret = snprintf(sql_buf, SQL_BUF_LEN, "insert into %.*s (", CON_TABLE(_h)->len, CON_TABLE(_h)->s); if (ret < 0 || ret >= SQL_BUF_LEN) goto error; off = ret; ret = db_print_columns(sql_buf + off, SQL_BUF_LEN - off, _k, _n); if (ret < 0) return -1; off += ret; ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values ("); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; ret = db_print_values(_h, sql_buf + off, SQL_BUF_LEN - off, _v, _n, db_mysql_val2str); if (ret < 0) return -1; off += ret; *(sql_buf + off++) = ')'; ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, " on duplicate key update "); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; ret = db_print_set(_h, sql_buf + off, SQL_BUF_LEN - off, _k, _v, _n, db_mysql_val2str); if (ret < 0) return -1; off += ret; sql_str.s = sql_buf; sql_str.len = off; if (db_mysql_submit_query(_h, &sql_str) < 0) { LM_ERR("error while submitting query\n"); return -2; } return 0; error: LM_ERR("error while preparing insert_update operation\n"); return -1; } /** * Store the name of table that will be used by subsequent database functions * \param _h database handle * \param _t table name * \return zero on success, negative value on failure */ int db_mysql_use_table(db_con_t* _h, const str* _t) { return db_use_table(_h, _t); } opensips-2.2.2/modules/db_mysql/dbase.h000066400000000000000000000074321300170765700201020ustar00rootroot00000000000000/* * MySQL module core functions * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DBASE_H #define DBASE_H #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" #include "../../str.h" #include "my_con.h" /* * Initialize database connection */ db_con_t* db_mysql_init(const str* _sqlurl); /* * Close a database connection */ void db_mysql_close(db_con_t* _h); /* * Free all memory allocated by get_result */ int db_mysql_free_result(db_con_t* _h, db_res_t* _r); /* * Do a query */ int db_mysql_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r); /* * fetch rows from a result */ int db_mysql_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows); /* * Raw SQL query */ int db_mysql_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r); /* * Insert a row into table */ int db_mysql_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /* * Delete a row from table */ int db_mysql_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n); /* * Update a row in table */ int db_mysql_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un); /* * Just like insert, but replace the row if it exists */ int db_mysql_replace(const db_con_t* handle, const db_key_t* keys, const db_val_t* vals, const int n); /* * Returns the last inserted ID */ int db_last_inserted_id(const db_con_t* _h); /* * Begins execution of an asynchronous, raw MySQL query. Possibly opens new TCP * connections up to "db_max_async_connections". Returns immediately. * * \return * success: Unix FD for polling * failure: negative error code */ int db_mysql_async_raw_query(db_con_t *_h, const str *_s, void** _data); /* * Reads data from the given connection file descriptor. If the query is fully * completed, the global "async_status" will be equal to ASYNC_DONE. * * \return: * -> 0 on success, negative on failure * -> also populates the global "async_status": ASYNC_CONTINUE / ASYNC_DONE */ int db_mysql_async_resume(db_con_t *_h, int fd, db_res_t **_r, void* _data); /* * Cleans up asynchronous query results along with other associated structures * * \return: * -> 0 on success, negative on failure */ int db_mysql_async_free_result(db_con_t *_h, db_res_t *_r, void *_data); /* * Insert a row into table, update on duplicate key */ int db_insert_update(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /* * Store name of table that will be used by * subsequent database functions */ int db_mysql_use_table(db_con_t* _h, const str* _t); /* * Free all allocated prep_stmt structures */ void db_mysql_free_stmt_list(struct prep_stmt *head); #endif /* DBASE_H */ opensips-2.2.2/modules/db_mysql/doc/000077500000000000000000000000001300170765700174125ustar00rootroot00000000000000opensips-2.2.2/modules/db_mysql/doc/db_mysql.xml000066400000000000000000000020461300170765700217500ustar00rootroot00000000000000 %docentities; ]> mysql Module &osipsname; Jan Janak jan@iptel.org Daniel-Constantin Mierla miconda@gmail.com 2006 &voicesystem; $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/db_mysql/doc/db_mysql_admin.xml000066400000000000000000000115111300170765700231150ustar00rootroot00000000000000 &adminguide;
Overview This is a module which provides MySQL connectivity for OpenSIPS. It implements the DB API defined in OpenSIPS.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libmysqlclient-dev - the development libraries of mysql-client.
Exported Parameters
<varname>exec_query_threshold</varname> (integer) If queries take longer than 'exec_query_threshold' microseconds, warning messages will be written to logging facility. Default value is 0 - disabled. Set <varname>exec_query_threshold</varname> parameter ... modparam("db_mysql", "exec_query_threshold", 60000) ...
<varname>timeout_interval</varname> (integer) Time interval after which a connection attempt (read or write request) is aborted. The value counts three times, as several retries are done from the driver before it gives up. The read timeout parameter is ignored on driver versions prior to 5.1.12, 5.0.25 and 4.1.22. The write timeout parameter is ignored on version prior to 5.1.12 and 5.0.25, the 4.1 release don't support it at all. Default value is 2 (6 sec). Set <varname>timeout_interval</varname> parameter ... modparam("db_mysql", "timeout_interval", 2) ...
<varname>max_db_queries</varname> (integer) The maximum number of database queries to be executed. If this parameter is set improperly, it is set to default value. Default value is 2. Set <varname>max_db_queries</varname> parameter ... modparam("db_mysql", "max_db_queries", 2) ...
<varname>max_db_retries</varname> (integer) The maximum number of database connection retries. If this parameter is set improperly, it is set to default value. Default value is 3. Set <varname>max_db_retries</varname> parameter ... modparam("db_mysql", "max_db_retries", 2) ...
Exported Functions No function exported to be used from configuration file.
Installation Because it dependes on an external library, the mysql module is not compiled and installed by default. You can use one of the next options. - edit the "Makefile" and remove "db_mysql" from "excluded_modules" list. Then follow the standard procedure to install &osips;: "make all; make install". - from command line use: 'make all include_modules="db_mysql"; make install include_modules="db_mysql"'.
Exported Events
<function moreinfo="none">E_MYSQL_CONNECTION</function> This event is raised when a MySQL connection is lost or recovered. Parameters: url - the URL of the connection as specified by the db_url parameter. status - connected if the connection recovered, or disconnected if the connection was lost.
opensips-2.2.2/modules/db_mysql/doc/db_mysql_parser.dia000066400000000000000000000046351300170765700232670ustar00rootroot00000000000000‹í]Ûrã6}ÏW¨4¯WL6³•‡­L*ž<«h‰–¹KS*Š+µûíÛ Ê7™’²bI®š‹ ñ°Éî>@£»á?~¿Ê:ß’bžNó“.#´ÛIòÑtœæ““îŸ_ÿùÞt<ýáã8?ÀŸI_uàŠ|Žßt/Ërö¡ß¿¹¹!Ùí<.§ÉÒk2Oúÿ‹³,îÇúÝÓÇWã2®~—e‘ž_—I'¯’“îy<úϤ˜^çãÅ£i6-:ßâì¤ûîÂ}uû§ûO®­‡šÅ³¤¸G¹šMç)Œ•·³¥±åëðïÅÐFòÉé»Oò]uËÅÝ¿¼Š‹Iš/ Š$ΪÇàÄpΛ>Åy”,J%gÓ¢,â´\ O§YçXY\'M¡æ£8}<ˆ5¸HËrZ'ÅEœÍkÅè?1§fw™éx…!>Z¾ê&——Ãïí®¸mð-§çYr/Cš—-n!´z·×é8™¯z»—¯¼\ŒöWˆþ0¼I®~-µeñmR,À~~ µÎâ,,½úìôüßɨ\H}VÆù8.Æ÷ÏY–ÎÀȨ9Ÿt¿ÐOHà^wÏ<›Þ¿ñˆ°§Ä6UŸßÏ„šdÉUfÔ€ Â{\5EM²äj8šù=ý¾@Dæ¬ü™kÐ æ2I'—å3¹§ÿ µõüš|/kÔÇüÕg{Þµ £?ã´)ÄÕqK| zw{4ôŒ©ÝĹ4­ž}þyöùáïŸÎÎZL°ÓüîvøßÎE|•f·p¯8Ÿw;óò ^xõéL¯‹¬®ác®°‡ÆÌF‰nõS³¢î«) L’“ü*¹[I~}UÑ64ÜØöW³÷5¦ÁD{`®Q@8‡6àX¬Ç"àµÖBÐW 1AìVhLxëÑx;r…8Þj\ 1­P‘ŒÃwŠìˆÊņ¿ùãëAP™¿"÷•ˤ¯N˜qf6Gàk)­´o˜Ò´·6%ÑH›^5AIã´ ÞÆwµP;”5Z=î+¥™6'PÅà"TÈ)wÁ ðL9àÃN_iß0¥YomZbá…3"i³Â£D€×$U=X'Ó]1Ú/?FÌÙB‰ûÊgŒ¶;Å ;¹£Z…R8B vzKûmFY V¤/ƒi´š¢„Ó(Rš ¦MoI×h3‚i.Œ6WÄÕ/Nïxop E˜rÜF•%ª2"šã µƒÙéóo¿Æö¿þÞöÔô¯4¯e±6)m¸Ä=KÊY˜Õ¬Å¤æÕÆSX8n4†ü* Êóa’°Í$_þ'Œéo,­hla ÂѬuÑżŒ‹rÅôæ™Mð Ã,É'5,L‰iƒVOékÀP  _0³ùò:—` .(§.±Øy=éÞë¤æ¶¸„>½„?ªøxtßûE›4ÀýÈ0 DœZZp XÁ€)=ªö–•¢:”K<zi£^ôÈ$=Á×X½'È-™µw¾D‚F@š@‰a;¢„È -[®Ì@J°éˆáŽ#gÌ„²íÉ—­±@÷(ˆŽF¾uºž9m@÷òé%zK~µ ÏaÉBѽ¶#n­vto”÷”Štÿ t ÝWƒGOغ'°¥eŒhà æu¦Ý""0 ‰ÖÂ:BÀ&¹‰"(!Ñ?$Õ:`D°,ùªˆ@‚‘=:ƶÃøO4ìñ©Õ¹…wFJ&¸T¾ÁäÚ•á®;²L 4±>ne˜`>Q#öªOøùÄèºø– Ç)˜R>Jj6÷ ¢}d³ÐûˆÚŽxçù8nÐc+Õ—ŠHÚž!Zhª1m+£ªJˆSÆCyÈS¡Wm2¯òæÛñœ"$nŽ®áFÓ­Ø9÷OæÝEš`¡¢hN b@c·€gh蔆Û0­‘úyg k÷¹ñ&kç°Ø‹Žö¾y*5»¡~õ–\„µ"À®•ŒBõHqŒi5@ºžƒH˜&ØB‡Ä~>]hô"ÏÛè#Œ}¤MH¡üCí—†+ÒÎÜ;í̱#Ùq$ˆª9Wƒ·a1¬P»¨‡úï篟:ýÿDæÙO…o;é¼Êîý“ÎÍÕ4§íµÓ‚Óhõ¦°ÓyÂ.Œÿ¯‡aôÞúÛO»÷N*cf ¨9 ÕG[Á)lš€pä}øg– tßBƒoÛöWĪE‚Mº]– G`XX{kÎ{šèHJ3pµ“¸óO1I¬&Ø ½4h±’ÂëÄŒ 1€%Ý(yŒæ¾ûŸlK zï42“DõL(zG0]‘{D8¬kìn¸ý§áv_åíe )÷NsFžH¬Y cW*-Vv*i-öø0ªÞs Õ¦ÿe¹Ÿ£Óa7ÿ1QüÊ»:¬Á®ÎRr ÚÒ$`Zt»¬~‹µºLÞì[dØíƒ-¥L×øÛltóÓß~F·þÙ_=ÝÑa&‡æz„ÜàQ®²ù¸þÙÞV¦·þöÒðmÓÞ Öé³ã¾©»×ú-_`»my¦†ÞÌ?˜­üjÜË@°u?.'°d9Ô²ª«@°2Á0ë*ª}²®›k–_Uql*ØI ТBm'Þé]Éð¤Þæ-0kOñ©°Ü&ÿ@ržúÛÏåh¸f_*ЮHú!àÇ ×V£Oíí§Ùûw cß#.Déà¬;ƒOh<¼ £±»’ž±ÿjÜÇ ¯ðo-r–Õi ¡Z‹’- ©”b 8qMdV©`Ÿ5b¯ú„$«ø|5ÖÇ™6ÆMb‚èU…w†[ƒ1ŠU¡Î•ÔXgšBhì½§¼j4\k4~*ôªÆ|?÷8FÈm‹¢× wu‹C´¸«’ÎÐ⮸-âî -UaÛcæk«Çhyjp?×þÞ)_iI„/.È¢§ÂRn§Vý¥˜§?ü×Égcxopensips-2.2.2/modules/db_mysql/my_con.c000066400000000000000000000070271300170765700203030ustar00rootroot00000000000000/* * Copyright (C) 2001-2004 iptel.org * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "my_con.h" #include "db_mysql.h" #include "dbase.h" #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../ut.h" int db_mysql_connect(struct my_con* ptr) { /* if connection already in use, close it first*/ if (ptr->init) mysql_close(ptr->con); mysql_init(ptr->con); ptr->init = 1; /* set connect, read and write timeout, the value counts three times */ mysql_options(ptr->con, MYSQL_OPT_CONNECT_TIMEOUT, &db_mysql_timeout_interval); mysql_options(ptr->con, MYSQL_OPT_READ_TIMEOUT, &db_mysql_timeout_interval); mysql_options(ptr->con, MYSQL_OPT_WRITE_TIMEOUT, &db_mysql_timeout_interval); if (ptr->id->port) { LM_DBG("opening connection: mysql://xxxx:xxxx@%s:%d/%s\n", ZSW(ptr->id->host), ptr->id->port, ZSW(ptr->id->database)); } else { LM_DBG("opening connection: mysql://xxxx:xxxx@%s/%s\n", ZSW(ptr->id->host), ZSW(ptr->id->database)); } if (!mysql_real_connect(ptr->con, ptr->id->host, ptr->id->username, ptr->id->password, ptr->id->database, ptr->id->port, 0, #if (MYSQL_VERSION_ID >= 40100) CLIENT_MULTI_STATEMENTS|CLIENT_REMEMBER_OPTIONS #else CLIENT_REMEMBER_OPTIONS #endif )) { LM_ERR("driver error(%d): %s\n", mysql_errno(ptr->con), mysql_error(ptr->con)); mysql_close(ptr->con); return -1; } /* force no auto reconnection */ ptr->con->reconnect = 0; LM_DBG("connection type is %s\n", mysql_get_host_info(ptr->con)); LM_DBG("protocol version is %d\n", mysql_get_proto_info(ptr->con)); LM_DBG("server version is %s\n", mysql_get_server_info(ptr->con)); return 0; } /** * Create a new connection structure, * open the MySQL connection and set reference count to 1 */ struct my_con* db_mysql_new_connection(const struct db_id* id) { struct my_con* ptr; if (!id) { LM_ERR("invalid parameter value\n"); return 0; } ptr = (struct my_con*)pkg_malloc(sizeof(struct my_con)); if (!ptr) { LM_ERR("no private memory left\n"); return 0; } memset(ptr, 0, sizeof(struct my_con)); ptr->ref = 1; ptr->con = (MYSQL*)pkg_malloc(sizeof(MYSQL)); if (!ptr->con) { LM_ERR("no private memory left\n"); goto err; } ptr->id = (struct db_id*)id; if (db_mysql_connect(ptr)!=0) { LM_ERR("initial connect failed\n"); goto err; } return ptr; err: if (ptr && ptr->con) pkg_free(ptr->con); if (ptr) pkg_free(ptr); return 0; } /** * Close the connection and release memory */ void db_mysql_free_connection(struct pool_con* con) { if (!con) return; struct my_con * _c; _c = (struct my_con*) con; if (_c->ps_list) db_mysql_free_stmt_list(_c->ps_list); if (_c->res) mysql_free_result(_c->res); if (_c->id) free_db_id(_c->id); if (_c->con) { mysql_close(_c->con); pkg_free(_c->con); } pkg_free(_c); } opensips-2.2.2/modules/db_mysql/my_con.h000066400000000000000000000064701300170765700203110ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MY_CON_H #define MY_CON_H #include "../../db/db_pool.h" #include "../../db/db_id.h" #include #include #define PREP_STMT_VAL_LEN 1024 struct bind_icontent { unsigned long len; my_bool null; }; struct bind_ocontent { char buf[PREP_STMT_VAL_LEN]; unsigned long len; my_bool null; my_bool error; }; struct my_stmt_ctx { MYSQL_STMT *stmt; str table; str query; int has_out; struct my_stmt_ctx *next; }; struct prep_stmt { struct my_stmt_ctx *stmts; struct my_stmt_ctx *ctx; /*in*/ MYSQL_BIND *bind_in; struct bind_icontent *in_bufs; /*out*/ int cols_out; MYSQL_BIND *bind_out; struct bind_ocontent *out_bufs; /*linking*/ struct prep_stmt *next; }; struct my_con { struct db_id* id; /**< Connection identifier */ unsigned int ref; /**< Reference count */ struct pool_con *async_pool; /**< Subpool of identical database handles */ int no_transfers; /**< Number of async queries to this backend */ struct db_transfer *transfers; /**< Array of ongoing async operations */ struct pool_con *next; /**< Next element in the pool (different db_id) */ MYSQL_RES* res; /* Actual result */ MYSQL* con; /* Connection representation */ MYSQL_ROW row; /* Actual row in the result */ unsigned int init; /* If the mysql conn was initialized */ struct prep_stmt *ps_list; /* list of prepared statements */ unsigned int disconnected; /* (CR_CONNECTION_ERROR) was detected */ }; /* * Some convenience wrappers */ #define CON_RESULT(db_con) (((struct my_con*)((db_con)->tail))->res) #define CON_CONNECTION(db_con) (((struct my_con*)((db_con)->tail))->con) #define CON_ROW(db_con) (((struct my_con*)((db_con)->tail))->row) #define CON_PS_LIST(db_con) (((struct my_con*)((db_con)->tail))->ps_list) #define CON_DISCON(db_con) (((struct my_con*)((db_con)->tail))->disconnected) #define CON_MYSQL_PS(db_con) \ ((struct prep_stmt*)(CON_CURR_PS(db_con))) #define CON_PS_STMT(db_con) \ (CON_MYSQL_PS(db_con)->ctx->stmt) #define CON_PS_STMTS(db_con) \ (CON_MYSQL_PS(db_con)->stmts) #define CON_PS_OUTCOL(_db_con, _i) \ ((CON_MYSQL_PS(_db_con)->out_bufs)[_i]) int db_mysql_connect(struct my_con* ptr); /* * Create a new connection structure, * open the MySQL connection and set reference count to 1 */ struct my_con* db_mysql_new_connection(const struct db_id* id); /* * Close the connection and release memory */ void db_mysql_free_connection(struct pool_con* con); #endif /* MY_CON_H */ opensips-2.2.2/modules/db_mysql/res.c000066400000000000000000000121421300170765700176020ustar00rootroot00000000000000/* * MySQL module result related functions * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../../db/db_res.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "row.h" #include "my_con.h" #include "res.h" /** * Get and convert columns from a result */ int db_mysql_get_columns(const db_con_t* _h, db_res_t* _r) { int col; MYSQL_FIELD* fields; if ((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } if (CON_HAS_PS(_h)) { RES_COL_N(_r) = CON_MYSQL_PS(_h)->cols_out; } else { RES_COL_N(_r) = mysql_field_count(CON_CONNECTION(_h)); } if (!RES_COL_N(_r)) { LM_ERR("no columns returned from the query\n"); return -2; } else { LM_DBG("%d columns returned from the query\n", RES_COL_N(_r)); } if (db_allocate_columns(_r, RES_COL_N(_r)) != 0) { LM_ERR("could not allocate columns\n"); return -3; } fields = mysql_fetch_fields(CON_RESULT(_h)); for(col = 0; col < RES_COL_N(_r); col++) { /* The pointer that is here returned is part of the result structure */ RES_NAMES(_r)[col]->s = fields[col].name; RES_NAMES(_r)[col]->len = strlen(fields[col].name); LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_r)[col], col, RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s); switch(fields[col].type) { case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_INT24: case MYSQL_TYPE_DECIMAL: #if MYSQL_VERSION_ID > 49999 case MYSQL_TYPE_NEWDECIMAL: #endif case MYSQL_TYPE_TIMESTAMP: LM_DBG("use DB_INT result type\n"); RES_TYPES(_r)[col] = DB_INT; break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: LM_DBG("use DB_DOUBLE result type\n"); RES_TYPES(_r)[col] = DB_DOUBLE; break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_DATE: LM_DBG("use DB_DATETIME result type\n"); RES_TYPES(_r)[col] = DB_DATETIME; break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: LM_DBG("use DB_BLOB result type\n"); RES_TYPES(_r)[col] = DB_BLOB; break; case FIELD_TYPE_SET: LM_DBG("use DB_BITMAP result type\n"); RES_TYPES(_r)[col] = DB_BITMAP; break; case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: LM_DBG("use DB_STRING result type\n"); RES_TYPES(_r)[col] = DB_STRING; break; case MYSQL_TYPE_LONGLONG: LM_DBG("use DB_BIGINT result type\n"); RES_TYPES(_r)[col] = DB_BIGINT; break; default: LM_WARN("unhandled data type column (%.*s) type id (%d), " "use DB_STRING as default\n", RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s, fields[col].type); RES_TYPES(_r)[col] = DB_STRING; break; } } return 0; } /** * Convert rows from mysql to db API representation */ static inline int db_mysql_convert_rows(const db_con_t* _h, db_res_t* _r) { int row; if ((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } if (CON_HAS_PS(_h)) { RES_ROW_N(_r) = mysql_stmt_num_rows(CON_PS_STMT(_h)); } else { RES_ROW_N(_r) = mysql_num_rows(CON_RESULT(_h)); } if (!RES_ROW_N(_r)) { LM_DBG("no rows returned from the query\n"); RES_ROWS(_r) = 0; return 0; } if (db_allocate_rows( _r, RES_ROW_N(_r))!=0) { LM_ERR("no private memory left\n"); return -2; } for(row = 0; row < RES_ROW_N(_r); row++) { if (CON_HAS_PS(_h)) { mysql_stmt_fetch(CON_PS_STMT(_h)); //if(mysql_stmt_fetch(CON_PS_STMT(_h))!=1) // LM_ERR("STMT ERR=%s\n",mysql_stmt_error(CON_PS_STMT(_h))); } else { CON_ROW(_h) = mysql_fetch_row(CON_RESULT(_h)); if (!CON_ROW(_h)) { LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h))); RES_ROW_N(_r) = row; db_free_rows(_r); return -3; } } if (db_mysql_convert_row(_h, _r, &(RES_ROWS(_r)[row])) < 0) { LM_ERR("error while converting row #%d\n", row); RES_ROW_N(_r) = row; db_free_rows(_r); return -4; } } return 0; } /** * Fill the structure with data from database */ int db_mysql_convert_result(const db_con_t* _h, db_res_t* _r) { if ((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } if (db_mysql_get_columns(_h, _r) < 0) { LM_ERR("error while getting column names\n"); return -2; } if (db_mysql_convert_rows(_h, _r) < 0) { LM_ERR("error while converting rows\n"); db_free_columns(_r); return -3; } return 0; } opensips-2.2.2/modules/db_mysql/res.h000066400000000000000000000022411300170765700176060ustar00rootroot00000000000000/* * MySQL module result related functions * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RES_H #define RES_H #include "../../db/db_res.h" #include "../../db/db_con.h" /* * Fill the structure with data from database */ int db_mysql_convert_result(const db_con_t* _h, db_res_t* _r); int db_mysql_get_columns(const db_con_t* _h, db_res_t* _r); #endif /* RES_H */ opensips-2.2.2/modules/db_mysql/row.c000066400000000000000000000040561300170765700176250ustar00rootroot00000000000000/* * MySQL module row related functions * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../dprint.h" #include "../../mem/mem.h" #include "../../db/db_row.h" #include "../../db/db_ut.h" #include "my_con.h" #include "val.h" #include "row.h" /** * Convert a row from result into db API representation */ int db_mysql_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r) { unsigned long* lengths; int i; if ((!_h) || (!_res) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } /* Save the number of columns in the ROW structure */ ROW_N(_r) = RES_COL_N(_res); if (CON_HAS_PS(_h)) { for(i=0; i < CON_MYSQL_PS(_h)->cols_out; i++) { if (db_mysql_str2val(RES_TYPES(_res)[i], &(ROW_VALUES(_r)[i]), CON_PS_OUTCOL(_h, i).null?NULL:CON_PS_OUTCOL(_h, i).buf, CON_PS_OUTCOL(_h,i).len) < 0) { LM_ERR("failed to convert value from stmt\n"); db_free_row(_r); return -3; } } } else { lengths = mysql_fetch_lengths(CON_RESULT(_h)); for(i = 0; i < RES_COL_N(_res); i++) { if (db_mysql_str2val(RES_TYPES(_res)[i], &(ROW_VALUES(_r)[i]), ((MYSQL_ROW)CON_ROW(_h))[i], lengths[i]) < 0) { LM_ERR("failed to convert value\n"); LM_DBG("free row at %p\n", _r); db_free_row(_r); return -3; } } } return 0; } opensips-2.2.2/modules/db_mysql/row.h000066400000000000000000000022211300170765700176220ustar00rootroot00000000000000/* * MySQL module row related functions * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ROW_H #define ROW_H #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_row.h" /** * Convert a row from result into db API representation */ int db_mysql_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r); #endif /* ROW_H */ opensips-2.2.2/modules/db_mysql/val.c000066400000000000000000000202741300170765700176000ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../dprint.h" #include "../../db/db_ut.h" #include "val.h" #include "my_con.h" #include #include /* * Convert str to db value, does not copy strings */ int db_mysql_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l) { static str dummy_string = {"", 0}; if (!_v) { LM_ERR("invalid parameter value\n"); return -1; } if (!_s) { memset(_v, 0, sizeof(db_val_t)); /* Initialize the string pointers to a dummy empty * string so that we do not crash when the NULL flag * is set but the module does not check it properly */ VAL_STRING(_v) = dummy_string.s; VAL_STR(_v) = dummy_string; VAL_BLOB(_v) = dummy_string; VAL_TYPE(_v) = _t; VAL_NULL(_v) = 1; return 0; } VAL_NULL(_v) = 0; switch(_t) { case DB_INT: LM_DBG("converting INT [%s]\n", _s); if (db_str2int(_s, &VAL_INT(_v)) < 0) { LM_ERR("error while converting integer value from string\n"); return -2; } else { VAL_TYPE(_v) = DB_INT; return 0; } break; case DB_BIGINT: LM_DBG("converting INT BIG[%s]\n", _s); if (db_str2bigint(_s, &VAL_BIGINT(_v)) < 0) { LM_ERR("error while converting big integer value from string\n"); return -2; } else { VAL_TYPE(_v) = DB_BIGINT; return 0; } break; case DB_BITMAP: LM_DBG("converting BITMAP [%s]\n", _s); if (db_str2int(_s, &VAL_INT(_v)) < 0) { LM_ERR("error while converting bitmap value from string\n"); return -3; } else { VAL_TYPE(_v) = DB_BITMAP; return 0; } break; case DB_DOUBLE: LM_DBG("converting DOUBLE [%s]\n", _s); if (db_str2double(_s, &VAL_DOUBLE(_v)) < 0) { LM_ERR("error while converting double value from string\n"); return -4; } else { VAL_TYPE(_v) = DB_DOUBLE; return 0; } break; case DB_STRING: LM_DBG("converting STRING [%s]\n", _s); VAL_STRING(_v) = _s; VAL_TYPE(_v) = DB_STRING; return 0; case DB_STR: LM_DBG("converting STR [%.*s]\n", _l, _s); VAL_STR(_v).s = (char*)_s; VAL_STR(_v).len = _l; VAL_TYPE(_v) = DB_STR; return 0; case DB_DATETIME: LM_DBG("converting DATETIME [%s]\n", _s); if (db_str2time(_s, &VAL_TIME(_v)) < 0) { LM_ERR("error while converting datetime value from string\n"); return -5; } else { VAL_TYPE(_v) = DB_DATETIME; return 0; } break; case DB_BLOB: LM_DBG("converting BLOB [%.*s]\n", _l, _s); VAL_BLOB(_v).s = (char*)_s; VAL_BLOB(_v).len = _l; VAL_TYPE(_v) = DB_BLOB; return 0; } return -6; } /* * Used when converting values to be used in a DB query */ int db_mysql_val2str(const db_con_t* _c, const db_val_t* _v, char* _s, int* _len) { int l; char* old_s; if (!_c || !_v || !_s || !_len || !*_len) { LM_ERR("invalid parameter value\n"); return -1; } if (VAL_NULL(_v)) { if (*_len < sizeof("NULL")) { LM_ERR("buffer too small\n"); return -1; } *_len = snprintf(_s, *_len, "NULL"); return 0; } switch(VAL_TYPE(_v)) { case DB_INT: if (db_int2str(VAL_INT(_v), _s, _len) < 0) { LM_ERR("error while converting string to int\n"); return -2; } else { return 0; } break; case DB_BIGINT: if (db_bigint2str(VAL_BIGINT(_v), _s, _len) < 0) { LM_ERR("error while converting bigint to string\n"); return -2; } else { return 0; } break; case DB_BITMAP: if (db_int2str(VAL_BITMAP(_v), _s, _len) < 0) { LM_ERR("error while converting string to int\n"); return -3; } else { return 0; } break; case DB_DOUBLE: if (db_double2str(VAL_DOUBLE(_v), _s, _len) < 0) { LM_ERR("error while converting string to double\n"); return -4; } else { return 0; } break; case DB_STRING: l = strlen(VAL_STRING(_v)); if (*_len < (l * 2 + 3)) { LM_ERR("destination buffer too short\n"); return -5; } else { old_s = _s; *_s++ = '\''; _s += mysql_real_escape_string(CON_CONNECTION(_c), _s, VAL_STRING(_v), l); *_s++ = '\''; *_s = '\0'; /* FIXME */ *_len = _s - old_s; return 0; } break; case DB_STR: if (*_len < (VAL_STR(_v).len * 2 + 3)) { LM_ERR("destination buffer too short\n"); return -6; } else { old_s = _s; *_s++ = '\''; _s += mysql_real_escape_string(CON_CONNECTION(_c), _s, VAL_STR(_v).s, VAL_STR(_v).len); *_s++ = '\''; *_s = '\0'; *_len = _s - old_s; return 0; } break; case DB_DATETIME: if (db_time2str(VAL_TIME(_v), _s, _len) < 0) { LM_ERR("error while converting string to time_t\n"); return -7; } else { return 0; } break; case DB_BLOB: l = VAL_BLOB(_v).len; if (*_len < (l * 2 + 3)) { LM_ERR("destination buffer too short\n"); return -8; } else { old_s = _s; *_s++ = '\''; _s += mysql_real_escape_string(CON_CONNECTION(_c), _s, VAL_STR(_v).s, l); *_s++ = '\''; *_s = '\0'; *_len = _s - old_s; return 0; } break; default: LM_DBG("unknown data type\n"); return -9; } /*return -8; --not reached*/ } int db_mysql_val2bind(const db_val_t* v, MYSQL_BIND *binds, unsigned int i) { struct tm *t; MYSQL_TIME *mt; if (VAL_NULL(v)) { *(binds[i].is_null) = 1; *(binds[i].length) = 0; binds[i].buffer= NULL; switch(VAL_TYPE(v)) { case DB_INT: binds[i].buffer_type= MYSQL_TYPE_LONG; break; case DB_BIGINT: binds[i].buffer_type= MYSQL_TYPE_LONGLONG; break; case DB_BITMAP: binds[i].buffer_type= MYSQL_TYPE_LONG; break; case DB_DOUBLE: binds[i].buffer_type= MYSQL_TYPE_DOUBLE; break; case DB_STRING: binds[i].buffer_type= MYSQL_TYPE_STRING; break; case DB_STR: binds[i].buffer_type= MYSQL_TYPE_STRING; break; case DB_DATETIME: binds[i].buffer_type= MYSQL_TYPE_DATETIME; break; case DB_BLOB: binds[i].buffer_type= MYSQL_TYPE_BLOB; break; default: LM_ERR("unknown NULL data type (%d)\n",VAL_TYPE(v)); return -10; } return 0; } else { *(binds[i].is_null) = 0; } switch(VAL_TYPE(v)) { case DB_INT: binds[i].buffer_type= MYSQL_TYPE_LONG; binds[i].buffer= (char*)&(VAL_INT(v)); *binds[i].length= sizeof(VAL_INT(v)); break; case DB_BIGINT: binds[i].buffer_type= MYSQL_TYPE_LONGLONG; binds[i].buffer= (char*)&(VAL_BIGINT(v)); *binds[i].length= sizeof(VAL_BIGINT(v)); break; case DB_BITMAP: binds[i].buffer_type= MYSQL_TYPE_LONG; binds[i].buffer= (char*)&(VAL_BITMAP(v)); *binds[i].length= sizeof(VAL_BITMAP(v)); break; case DB_DOUBLE: binds[i].buffer_type= MYSQL_TYPE_DOUBLE; binds[i].buffer= (char*)&(VAL_DOUBLE(v)); *binds[i].length= sizeof(VAL_DOUBLE(v)); break; case DB_STRING: binds[i].buffer_type= MYSQL_TYPE_STRING; binds[i].buffer= (char*)VAL_STRING(v); *binds[i].length= strlen(VAL_STRING(v)); break; case DB_STR: binds[i].buffer_type= MYSQL_TYPE_STRING; binds[i].buffer= VAL_STR(v).s; *binds[i].length= VAL_STR(v).len; break; case DB_DATETIME: binds[i].buffer_type= MYSQL_TYPE_DATETIME; t = localtime( &VAL_TIME(v) ); mt = (MYSQL_TIME*)binds[i].buffer; mt->year = 1900 + t->tm_year; mt->month = (t->tm_mon)+1; mt->day = t->tm_mday; mt->hour = t->tm_hour; mt->minute = t->tm_min; mt->second = t->tm_sec; *binds[i].length= sizeof(MYSQL_TIME); break; case DB_BLOB: binds[i].buffer_type= MYSQL_TYPE_BLOB; binds[i].buffer= VAL_BLOB(v).s; *binds[i].length= VAL_BLOB(v).len; break; default: LM_ERR("unknown data type (%d)\n",VAL_TYPE(v)); return -9; } LM_DBG("added val (%d): len=%ld; type=%d; is_null=%d\n", i, *(binds[i].length), binds[i].buffer_type, *(binds[i].is_null)); return 0; } opensips-2.2.2/modules/db_mysql/val.h000066400000000000000000000024461300170765700176060ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VAL_H #define VAL_H #include #include "../../db/db_val.h" #include "../../db/db.h" /** * Does not copy strings */ int db_mysql_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l); /** * Used when converting result from a query */ int db_mysql_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len); int db_mysql_val2bind(const db_val_t* v, MYSQL_BIND *binds, unsigned int i); #endif /* VAL_H */ opensips-2.2.2/modules/db_oracle/000077500000000000000000000000001300170765700167455ustar00rootroot00000000000000opensips-2.2.2/modules/db_oracle/Makefile000066400000000000000000000107461300170765700204150ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=db_oracle.so SCRIPTS_DIR?=../../scripts/ UTILS_DIR?=../../utils/ # can be defined for non standard placement of oracle so's ORAPATH= LIBS= # use for multiple client sdk version install ifneq ($(ORAVERSION),) ORAVERDIR=/$(ORAVERSION) endif # use include/library path's for full client installation ifneq ($(ORAHOME),) DEFS +=-I$(ORAHOME)/include LIBS +=-L$(ORAHOME)/lib ifeq ($(ORAPATH),) ORAPATH=$(ORAHOME)/lib endif else # use standard know paths oci.h locations (linux) DEFS +=-I$(LOCALBASE)/include/oracle$(ORAVERDIR) \ -I$(SYSBASE)/include/oracle$(ORAVERDIR) endif # search 'so' path if it non standard (possible liboclntsh locations on linux) ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(LOCALBASE)/lib64/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(LOCALBASE)/lib64/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib64/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(SYSBASE)/lib64/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(LOCALBASE)/lib64/oracle$(ORAVERDIR)/lib/libocci.so ] && \ echo $(LOCALBASE)/lib64/oracle$(ORAVERDIR)/lib ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib64/oracle$(ORAVERDIR)/lib/libocci.so ] && \ echo $(SYSBASE)/lib64/oracle$(ORAVERDIR)/lib ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(LOCALBASE)/lib/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(LOCALBASE)/lib/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(SYSBASE)/lib/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(LOCALBASE)/lib/oracle$(ORAVERDIR)/lib/libocci.so ] && \ echo $(LOCALBASE)/lib/oracle$(ORAVERDIR)/lib ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib/oracle$(ORAVERDIR)/lib/libocci.so ] && \ echo $(SYSBASE)/lib/oracle$(ORAVERDIR)/lib ) endif ifneq ($(ORAPATH),) LIBS +=-L$(ORAPATH) endif LIBS +=-locci -lclntsh #DEFS+=-DLINUX -D_GNU_SOURCE -D_REENTRANT #LIBS+=-lpthread ifneq ($(ORAPATH),) LIBS +=-Wl,-rpath $(ORAPATH) endif include ../../Makefile.modules .PHONY: $(UTILS_DIR)/db_oracle/opensips_orasel $(UTILS_DIR)/db_oracle/opensips_orasel: make -C $(UTILS_DIR)/db_oracle install_module_custom: $(bin_prefix)/$(bin_dir) $(UTILS_DIR)/db_oracle/opensips_orasel mkdir -p $(modules_prefix)/$(lib_dir)/opensipsctl ; \ sed -e "s#/usr/local/sbin#$(bin-target)#g" \ < $(SCRIPTS_DIR)/opensipsctl.oracle > /tmp/opensipsctl.oracle ; \ $(INSTALL_CFG) /tmp/opensipsctl.oracle \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.oracle ; \ rm -fr /tmp/opensipsctl.oracle ; \ sed -e "s#/usr/local/share/opensips#$(data_target)#g" \ < $(SCRIPTS_DIR)/opensipsdbctl.oracle > /tmp/opensipsdbctl.oracle ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsdbctl.oracle ; \ $(INSTALL_CFG) /tmp/opensipsdbctl.oracle $(modules_prefix)/$(lib_dir)/opensipsctl/ ; \ rm -fr /tmp/opensipsdbctl.oracle ; \ sed -e "s#/usr/local/share/opensips#$(data_target)#g" \ < $(SCRIPTS_DIR)/opensipsdbfunc.oracle > /tmp/opensipsdbfunc.oracle ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsdbfunc.oracle ; \ $(INSTALL_CFG) /tmp/opensipsdbfunc.oracle $(modules_prefix)/$(lib_dir)/opensipsctl/ ; \ rm -fr /tmp/opensipsdbfunc.oracle ; \ mkdir -p $(data_prefix)/$(data_dir)/oracle ; \ for FILE in $(wildcard $(SCRIPTS_DIR)/oracle/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/oracle/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/oracle/`basename "$$FILE"` ; \ fi ;\ done ; \ mkdir -p $(data_prefix)/$(data_dir)/oracle/inc ; \ for FILE in $(wildcard $(SCRIPTS_DIR)/oracle/inc/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/oracle/inc/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/oracle/inc/`basename "$$FILE"` ; \ fi ;\ done ; \ mkdir -p $(data_prefix)/$(data_dir)/oracle/admin ; \ for FILE in $(wildcard $(SCRIPTS_DIR)/oracle/admin/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/oracle/admin/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/oracle/admin/`basename "$$FILE"` ; \ fi ;\ done ; \ $(INSTALL_BIN) $(UTILS_DIR)/db_oracle/opensips_orasel $(bin_prefix)/$(bin_dir) ; \ opensips-2.2.2/modules/db_oracle/README000066400000000000000000000071121300170765700176260ustar00rootroot00000000000000oracle Module Iouri Kharon Yury Skandarov Iakov Kharon Edited by Iouri Kharon Copyright © 2007,2008 TRUNK MOBILE, INC. Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. User's Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. timeout (fixedpoint) 1.3.2. reconnect (fixedpoint) 1.4. Exported Functions 1.5. Installation 1.6. Utility opensips_orasel List of Examples 1.1. Set timeout parameter 1.2. Disable asynchronous mode 1.3. Set reconnect parameter Chapter 1. User's Guide 1.1. Overview This is a module which provides Oracle connectivity for OpenSIPS. It implements the DB API defined in OpenSIPS. If you want to use the nathelper module, or any other modules that calls the get_all_ucontacts API export from usrloc, then you need to set the DORACLE_USRLOC define in the Makefile.defs file before compilation. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * instantclient-sdk-10.2.0.3 - the development headers and libraries of OCI. 1.3. Exported Parameters 1.3.1. timeout (fixedpoint) Timeout value for any operation with BD. Possible values is from 0.1 to 10.0 seconds. Default value is 3.0 (3 second). If value of timeout parameter set to 0, module use synchronous mode (without timeout). Example 1.1. Set timeout parameter ... modparam("db_oracle", "timeout", 1.5) ... Example 1.2. Disable asynchronous mode ... modparam("db_oracle", "timeout", 0) ... 1.3.2. reconnect (fixedpoint) Timeout value for connect (create session) operation. Possible values is from 0.1 to 10.0 seconds. Default value is 0.2 (200 milliseconds). Example 1.3. Set reconnect parameter ... modparam("db_oracle", "reconnect", 0.5) ... 1.4. Exported Functions No function exported to be used from configuration file. 1.5. Installation Because it dependes on an external library, the oracle module is not compiled and installed by default. You can use one of the next options. * - edit the "Makefile" and remove "db_oracle" from "excluded_modules" list. Then follow the standard procedure to install OpenSIPS: "make all; make install". * - from command line use: 'make all include_modules="db_oracle"; make install include_modules="db_oracle"'. 1.6. Utility opensips_orasel For working with opensipsctl script, should be able to print the 'query' results to the terminal in a user-readable form. The standard command-line Oracle client (sqlplus) is not quite suitable for this, as it cannot align row width to real (received) data's (it always prints a cell width as described in the db scheme). This problem has been solved by inclusion the utility opensips_orasel, which formats printing approximately in the same way as the 'mysql' client utility. In addition, this utility known about the "agreements and types" in DB that are used in OpenSIPS for the work with Oracle and formats printing taking these into account. opensips-2.2.2/modules/db_oracle/asynch.c000066400000000000000000000130361300170765700204010ustar00rootroot00000000000000/* * Oracle module interface * * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * History: * -------- */ #include #include #include #include #include "../../dprint.h" #include "../../sr_module.h" #include "ora_con.h" #include "asynch.h" #define MAX_TIMEOUT_S 10 #define MIN_TIMEOUT_MS 100 /* Default is 3.0 second */ static struct timeval request_tm = { .tv_sec = 3, .tv_usec = 0 }; /* Default is 0.2 second */ static struct timeval restore_tm = { .tv_sec = 0, .tv_usec = 200*1000 }; static const struct timeval defrest_tm = { .tv_sec = 0, .tv_usec = 200*1000 }; static int synch_mode; static int cur_asynch_mode; static struct timeval wtm; static __inline__ int is_zero_tm(const struct timeval* tv) { return !tv->tv_usec && !tv->tv_sec; } /* * parse timeout value in syntax: nnn.mmm (sec/ms) */ static int set_tv(unsigned type, const char* val, struct timeval* tv) { char *eptr; unsigned long s, ms; double dv; if (type != STR_PARAM) { LM_ERR("type of parameter is no STR\n"); return -1; } if (!val || !*val) { LM_ERR("empty parameter\n"); return -1; } errno = 0; dv = strtod(val, &eptr); if (*eptr) { LM_ERR("invalid parameter string\n"); return -2; } if ( errno || dv > (double)MAX_TIMEOUT_S || (dv && dv < ((double)MIN_TIMEOUT_MS)/1000)) { LM_ERR("value must be between 0.%u and %u.0\n", MIN_TIMEOUT_MS, MAX_TIMEOUT_S); return -3; } s = (unsigned)dv; dv -= (double)s; ms = (unsigned)(dv * 1000); tv->tv_sec = (time_t)s; tv->tv_usec = (suseconds_t)ms; return 0; } /* * set operation timeout */ int set_timeout(unsigned type, const char* val) { int rc = set_tv(type, val, &request_tm); if (!rc) { synch_mode = is_zero_tm(&request_tm); if (!synch_mode && is_zero_tm(&restore_tm)) restore_tm = defrest_tm; } return rc; } /* * set (re)connect timeout */ int set_reconnect(unsigned type, const char* val) { int rc = set_tv(type, val, &restore_tm); if (!synch_mode && is_zero_tm(&restore_tm)) { LM_WARN("in asyncronus mode reconnect time can't be zero. " "Set default value\n"); restore_tm = defrest_tm; } return rc; } static sword change_mode(ora_con_t* con) { return OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, NULL, 0, OCI_ATTR_NONBLOCKING_MODE, con->errhp); } /* * start timelimited operation (if work in synch mode return SUCCESS) */ sword begin_timelimit(ora_con_t* con, int connect) { struct timeval* tv; sword status; if (synch_mode) return OCI_SUCCESS; if (connect || cur_asynch_mode) { ub1 mode; status = OCIAttrGet(con->svchp, OCI_HTYPE_SVCCTX, &mode, NULL, OCI_ATTR_NONBLOCKING_MODE, con->errhp); if (status != OCI_SUCCESS) return status; if (mode) { status = change_mode(con); if (status != OCI_SUCCESS) return status; } cur_asynch_mode = 0; } status = change_mode(con); if (status != OCI_SUCCESS && connect >= 0) return status; cur_asynch_mode = 1; gettimeofday(&wtm, NULL); tv = &request_tm; if (connect) tv = &restore_tm; wtm.tv_sec += tv->tv_sec; wtm.tv_usec += tv->tv_usec; if (wtm.tv_usec >= 1000000) { wtm.tv_usec -= 1000000; ++wtm.tv_sec; } return OCI_SUCCESS; } static sword remap_status(ora_con_t* con, sword status) { sword code; if ( status == OCI_ERROR && OCIErrorGet(con->errhp, 1, NULL, &code, NULL, 0, OCI_HTYPE_ERROR) == OCI_SUCCESS && (code == 3123 /*|| code == 3127*/)) { status = OCI_STILL_EXECUTING; } return status; } /* * check completion of timelimited operation (if work in synch mode return 0) */ int wait_timelimit(ora_con_t* con, sword status) { struct timeval cur; if (!cur_asynch_mode) return 0; if (remap_status(con, status) != OCI_STILL_EXECUTING) return 0; gettimeofday(&cur, NULL); return ( cur.tv_sec < wtm.tv_sec || (cur.tv_sec == wtm.tv_sec && cur.tv_usec < wtm.tv_usec)); } /* * close current timelimited operation and disconnect if timeout occurred * return true only if work in asynch mode and timeout detect */ int done_timelimit(ora_con_t* con, sword status) { int ret = 0; if (!cur_asynch_mode) return 0; if (remap_status(con, status) == OCI_STILL_EXECUTING) { sword code; status = OCIBreak(con->svchp, con->errhp); if (status != OCI_SUCCESS) LM_ERR("driver: %s\n", db_oracle_error(con, status)); status = OCIReset(con->svchp, con->errhp); if ( status == OCI_ERROR && OCIErrorGet(con->errhp, 1, NULL, &code, NULL, 0, OCI_HTYPE_ERROR) == OCI_SUCCESS && code == 1013) { status = OCI_SUCCESS; } if (status != OCI_SUCCESS) LM_ERR("driver: %s\n", db_oracle_error(con, status)); db_oracle_disconnect(con); ++ret; } else { status = change_mode(con); if (status != OCI_SUCCESS) { LM_ERR("driver: %s\n", db_oracle_error(con, status)); ++ret; } else { cur_asynch_mode = 0; } } return ret; } opensips-2.2.2/modules/db_oracle/asynch.h000066400000000000000000000032141300170765700204030ustar00rootroot00000000000000/* * Oracle module interface * * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- */ #ifndef ASYNCH_H #define ASYNCH_H #include #include "ora_con.h" /* * parse timeout value for operation in syntax: nnn.mmm (sec/ms) */ int set_timeout(unsigned type, const char* val); /* * parse timeout value for reconnect in syntax: nnn.mmm (sec/ms) */ int set_reconnect(unsigned type, const char* val); /* * start timelimited operation (if work in synch mode return SUCCESS) */ sword begin_timelimit(ora_con_t* con, int connect); /* * check completion of timelimited operation (if work in synch mode return 0) */ int wait_timelimit(ora_con_t* con, sword status); /* * close current timelimited operation and disconnect if timeout occurred * return true only if work in asynch mode and timeout detect */ int done_timelimit(ora_con_t* con, sword status); #endif /* ASYNCH_H */ opensips-2.2.2/modules/db_oracle/db_oracle.c000066400000000000000000000057111300170765700210270ustar00rootroot00000000000000/* * Oracle module interface * * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * History: * -------- */ #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "dbase.h" #include "asynch.h" static int oracle_mod_init(void); static void destroy(void); static int db_oracle_bind_api(const str* mod, db_func_t *dbb); /* * Oracle database module interface */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)db_oracle_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"timeout", STR_PARAM|USE_FUNC_PARAM, (void*)&set_timeout }, {"reconnect", STR_PARAM|USE_FUNC_PARAM, (void*)&set_reconnect }, {0, 0, 0} }; struct module_exports exports = { "db_oracle", MOD_TYPE_SQLDB, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, 0, params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ oracle_mod_init, /* module initialization function */ 0, /* response function*/ destroy, /* destroy function */ 0 /* per-child init function */ }; static int oracle_mod_init(void) { sword major, minor, update, patch, port; OCIClientVersion(&major, &minor, &update, &patch, &port); LM_DBG("Oracle client version is %d.%d.%d.%d.%d\n", major, minor, update, patch, port); return 0; } static void destroy(void) { LM_INFO("Oracle terminate\n"); OCITerminate(OCI_DEFAULT); } static int db_oracle_bind_api(const str* mod, db_func_t *dbb) { if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->use_table = db_oracle_use_table; dbb->init = db_oracle_init; dbb->close = db_oracle_close; dbb->query = db_oracle_query; dbb->raw_query = db_oracle_raw_query; dbb->free_result = db_oracle_free_result; dbb->insert = db_oracle_insert; dbb->delete = db_oracle_delete; dbb->update = db_oracle_update; return 0; } opensips-2.2.2/modules/db_oracle/dbase.c000066400000000000000000000316251300170765700201760ustar00rootroot00000000000000/* * Oracle module core functions * * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../db/db_pool.h" #include "../../db/db_ut.h" #include "../../db/db_res.h" #include "../../db/db_query.h" #include "val.h" #include "ora_con.h" #include "res.h" #include "asynch.h" #include "dbase.h" #define MAX_BIND_HANDLES 128 char st_buf[STATIC_BUF_LEN]; /* * Make error message. Always return negative value */ int sql_buf_small(void) { LM_ERR("static buffer too small\n"); return -11; } /* * Decode error */ static char errbuf[512]; static const char* db_oracle_errorinfo(ora_con_t* con) { sword errcd; if (OCIErrorGet(con->errhp, 1, NULL, &errcd, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR) != OCI_SUCCESS) errbuf[0] = '\0'; else switch (errcd) { case 28: /* your session has been killed */ case 30: /* session ID does not exists */ case 31: /* session marked for kill */ case 41: /* active time limit exceeded session terminated */ case 107: /* failed to connect to oracle listener */ case 115: /* connection refused; dispatcher table is full */ case 1033: /* init/shutdown in progress */ case 1034: /* not available (startup) */ case 1089: /* server shutdown */ case 1090: /* shutdown wait after command */ case 1092: /* oracle instance terminated. Disconnection forced */ case 1573: /* shutdown instance, no futher change allowed */ case 2049: /* timeout: distributed transaction waiting lock */ case 3113: /* EOF on communication channel */ case 3114: /* not connected */ case 3135: /* lost connection */ case 6033: /* connect failed, partner rejected connection */ case 6034: /* connect failed, partner exited unexpectedly */ case 6037: /* connect failed, node unrecheable */ case 6039: /* connect failed */ case 6042: /* msgrcv failure (DNT) */ case 6043: /* msgsend failure (DNT) */ case 6107: /* network server not found */ case 6108: /* connect to host failed */ case 6109: /* msgrcv failure (TCP) */ case 6110: /* msgsend failure (TCP) */ case 6114: /* SID lookup failure */ case 6124: /* TCP timeout */ case 6135: /* connect rejected; server is stopping (TCP) */ case 6144: /* SID unavaliable (TCP) */ case 6413: /* connection not open */ case 12150: /* tns can't send data, probably disconnect */ case 12152: /* tns unable to send break message */ case 12153: /* tns not connected */ case 12161: /* tns internal error */ case 12170: /* tns connect timeout */ case 12224: /* tns no listener */ case 12225: /* tns destination host unrecheable */ case 12230: /* tns network error */ case 12525: /* tns (internal) timeout */ case 12521: /* tns can't resolve db name */ case 12537: /* tns connection cloed */ case 12541: /* tns not running */ case 12543: /* tns destination host unrecheable */ case 12547: /* tns lost contact */ case 12560: /* tns protocol(transport) error */ case 12561: /* tns unknown error */ case 12608: /* tns send timeount */ case 12609: /* tns receive timeount */ LM_ERR("conneciom dropped\n"); db_oracle_disconnect(con); default: break; } return errbuf; } const char* db_oracle_error(ora_con_t* con, sword status) { switch (status) { case OCI_SUCCESS: return "internal (success)"; case OCI_SUCCESS_WITH_INFO: case OCI_ERROR: return db_oracle_errorinfo(con); case OCI_NEED_DATA: return "need data"; case OCI_NO_DATA: return "no data"; case OCI_INVALID_HANDLE: return "invalid handle"; case OCI_STILL_EXECUTING: // ORA-3123 return "executing (logic)"; case OCI_CONTINUE: return "continue (library)"; default: snprintf(errbuf, sizeof(errbuf), "unknown status %u", status); return errbuf; } } /* * Initialize database module * No function should be called before this */ db_con_t* db_oracle_init(const str* _url) { return db_do_init(_url, (void *)db_oracle_new_connection); } /* * Shut down database module * No function should be called after this */ void db_oracle_close(db_con_t* _h) { db_do_close(_h, db_oracle_free_connection); } /* * Release a result set from memory */ int db_oracle_free_result(db_con_t* _h, db_res_t* _r) { ub4 i; if (!_h || !_r) { LM_ERR("invalid parameter value\n"); return -1; } if (RES_NAMES(_r)) for (i=0; i < RES_COL_N(_r); ++i) if (RES_NAMES(_r)[i]->s) pkg_free(RES_NAMES(_r)[i]->s); if (db_free_result(_r) < 0) { LM_ERR("failed to free result structure\n"); return -1; } return 0; } /* * Send an SQL query to the server */ static int db_oracle_submit_query(const db_con_t* _h, const str* _s) { OCIBind* bind[MAX_BIND_HANDLES]; OCIDate odt[sizeof(bind)/sizeof(bind[0])]; str tmps; sword status; int pass; ora_con_t* con = CON_ORA(_h); query_data_t* pqd = con->pqdata; size_t hc = pqd->_n + pqd->_nw; OCIStmt *stmthp; if (hc >= sizeof(bind)/sizeof(bind[0])) { LM_ERR("too many bound. Rebuild with MAX_BIND_HANDLES >= %u\n", (unsigned)hc); return -1; } if (!pqd->_rs) { /* * This method is at ~25% faster as set OCI_COMMIT_ON_SUCCESS * in StmtExecute */ tmps.len = snprintf(st_buf, sizeof(st_buf), "begin %.*s; commit write batch nowait; end;", _s->len, _s->s); if ((unsigned)tmps.len >= sizeof(st_buf)) return sql_buf_small(); tmps.s = st_buf; _s = &tmps; } pass = 1; if (!con->connected) { status = db_oracle_reconnect(con); if (status != OCI_SUCCESS) { LM_ERR("can't restore connection: %s\n", db_oracle_error(con, status)); return -2; } LM_INFO("connection restored\n"); --pass; } repeat: stmthp = NULL; status = OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&stmthp, OCI_HTYPE_STMT, 0, NULL); if (status != OCI_SUCCESS) goto ora_err; status = OCIStmtPrepare(stmthp, con->errhp, (text*)_s->s, _s->len, OCI_NTV_SYNTAX, OCI_DEFAULT); if (status != OCI_SUCCESS) goto ora_err; if (hc) { bmap_t bmap; size_t pos = 1; int i; memset(bind, 0, hc*sizeof(bind[0])); for (i = 0; i < pqd->_n; i++) { if (db_oracle_val2bind(&bmap, &pqd->_v[i], &odt[pos]) < 0) goto bind_err; status = OCIBindByPos(stmthp, &bind[pos], con->errhp, pos, bmap.addr, bmap.size, bmap.type, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT); if (status != OCI_SUCCESS) goto ora_err; ++pos; } for (i = 0; i < pqd->_nw; i++) { if (db_oracle_val2bind(&bmap, &pqd->_w[i], &odt[pos]) < 0) { bind_err: OCIHandleFree(stmthp, OCI_HTYPE_STMT); LM_ERR("can't map values\n"); return -3; } status = OCIBindByPos(stmthp, &bind[pos], con->errhp, pos, bmap.addr, bmap.size, bmap.type, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT); if (status != OCI_SUCCESS) goto ora_err; ++pos; } } // timelimited operation status = begin_timelimit(con, 0); if (status != OCI_SUCCESS) goto ora_err; do status = OCIStmtExecute(con->svchp, stmthp, con->errhp, !pqd->_rs, 0, NULL, NULL, pqd->_rs ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT); while (wait_timelimit(con, status)); if (done_timelimit(con, status)) goto stop_exec; switch (status) { case OCI_SUCCESS_WITH_INFO: LM_WARN("driver: %s\n", db_oracle_errorinfo(con)); //PASS THRU case OCI_SUCCESS: if (pqd->_rs) *pqd->_rs = stmthp; else OCIHandleFree(stmthp, OCI_HTYPE_STMT); return 0; default: pass = -pass; break; } ora_err: LM_ERR("driver: %s\n", db_oracle_error(con, status)); stop_exec: if (stmthp) OCIHandleFree(stmthp, OCI_HTYPE_STMT); if (pass == -1 && !con->connected) { /* Attemtp to reconnect */ if (db_oracle_reconnect(con) == OCI_SUCCESS) { LM_NOTICE("attempt repeat after reconnect\n"); pass = 0; goto repeat; } LM_ERR("connection loss\n"); } return -4; } /* * Query table for specified rows * _h: structure representing database connection * _k: key names * _op: operators * _v: values of the keys that must match * _c: column names to return * _n: number of key=values pairs to compare * _nc: number of columns to return * _o: order by the specified column */ int db_oracle_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, int _n, int _nc, const db_key_t _o, db_res_t** _r) { query_data_t cb; OCIStmt* reshp; int rc; if (!_h || !CON_TABLE(_h) || !_r) { LM_ERR("invalid parameter value\n"); return -1; } cb._rs = &reshp; cb._v = _v; cb._n = _n; cb._w = NULL; cb._nw = 0; CON_ORA(_h)->pqdata = &cb; CON_ORA(_h)->bindpos = 0; CON_RESET_CURR_PS(_h); /* no prepared statements support */ rc = db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r, db_oracle_val2str, db_oracle_submit_query, db_oracle_store_result); CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */ return rc; } /* * Execute a raw SQL query */ int db_oracle_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r) { query_data_t cb; OCIStmt* reshp; int len; const char *p; if (!_h || !_s || !_s->s) { badparam: LM_ERR("invalid parameter value\n"); return -1; } CON_RESET_CURR_PS(_h); /* no prepared statements support */ memset(&cb, 0, sizeof(cb)); p = _s->s; len = _s->len; while (len && *p == ' ') ++p, --len; #define _S_DIFF(p, l, S) (l <= sizeof(S)-1 || strncasecmp(p, S, sizeof(S)-1)) if (!_S_DIFF(p, len, "select ")) { if (!_r) goto badparam; cb._rs = &reshp; } else { if ( _S_DIFF(p, len, "insert ") && _S_DIFF(p, len, "delete ") && _S_DIFF(p, len, "update ")) { LM_ERR("unknown raw_query: '%.*s'\n", _s->len, _s->s); return -2; } #undef _S_DIFF if (_r) goto badparam; cb._rs = NULL; } len = db_do_raw_query(_h, _s, _r, db_oracle_submit_query, db_oracle_store_result); CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */ return len; } /* * Insert a row into specified table * _h: structure representing database connection * _k: key names * _v: values of the keys * _n: number of key=value pairs */ int db_oracle_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, int _n) { query_data_t cb; int rc; if (!_h || !CON_TABLE(_h)) { LM_ERR("invalid parameter value\n"); return -1; } cb._rs = NULL; cb._v = _v; cb._n = _n; cb._w = NULL; cb._nw = 0; CON_ORA(_h)->pqdata = &cb; CON_ORA(_h)->bindpos = 0; CON_RESET_CURR_PS(_h); /* no prepared statements support */ rc = db_do_insert(_h, _k, _v, _n, db_oracle_val2str, db_oracle_submit_query); CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */ return rc; } /* * Delete a row from the specified table * _h: structure representing database connection * _k: key names * _o: operators * _v: values of the keys that must match * _n: number of key=value pairs */ int db_oracle_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, int _n) { query_data_t cb; int rc; if (!_h || !CON_TABLE(_h)) { LM_ERR("invalid parameter value\n"); return -1; } cb._rs = NULL; cb._v = _v; cb._n = _n; cb._w = NULL; cb._nw = 0; CON_ORA(_h)->pqdata = &cb; CON_ORA(_h)->bindpos = 0; CON_RESET_CURR_PS(_h); /* no prepared statements support */ rc = db_do_delete(_h, _k, _o, _v, _n, db_oracle_val2str, db_oracle_submit_query); CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */ return rc; } /* * Update some rows in the specified table * _h: structure representing database connection * _k: key names * _o: operators * _v: values of the keys that must match * _uk: updated columns * _uv: updated values of the columns * _n: number of key=value pairs * _un: number of columns to update */ int db_oracle_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, int _n, int _un) { query_data_t cb; int rc; if (!_h || !CON_TABLE(_h)) { LM_ERR("invalid parameter value\n"); return -1; } cb._rs = NULL; cb._v = _uv; cb._n = _un; cb._w = _v; cb._nw = _n; CON_ORA(_h)->pqdata = &cb; CON_ORA(_h)->bindpos = 0; CON_RESET_CURR_PS(_h); /* no prepared statements support */ rc = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_oracle_val2str, db_oracle_submit_query); CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */ return rc; } /* * Store name of table that will be used by * subsequent database functions */ int db_oracle_use_table(db_con_t* _h, const str* _t) { return db_use_table(_h, _t); } opensips-2.2.2/modules/db_oracle/dbase.h000066400000000000000000000044221300170765700201760ustar00rootroot00000000000000/* * Oracle module core functions * * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DBASE_H #define DBASE_H #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" /* * Initialize database connection */ db_con_t* db_oracle_init(const str* _sqlurl); /* * Close a database connection */ void db_oracle_close(db_con_t* _h); /* * Free all memory allocated by get_result */ int db_oracle_free_result(db_con_t* _h, db_res_t* _r); /* * Do a query */ int db_oracle_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, int _n, int _nc, const db_key_t _o, db_res_t** _r); /* * Raw SQL query */ int db_oracle_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r); /* * Insert a row into table */ int db_oracle_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, int _n); /* * Delete a row from table */ int db_oracle_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, int _n); /* * Update a row in table */ int db_oracle_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, int _n, int _un); /* * Store name of table that will be used by * subsequent database functions */ int db_oracle_use_table(db_con_t* _h, const str* _t); /* * Make error message. Always return negative value */ int sql_buf_small(void); #endif /* DBASE_H */ opensips-2.2.2/modules/db_oracle/doc/000077500000000000000000000000001300170765700175125ustar00rootroot00000000000000opensips-2.2.2/modules/db_oracle/doc/db_oracle.xml000066400000000000000000000024471300170765700221550ustar00rootroot00000000000000 %docentities; ]> oracle Module &osipsname; Iouri Kharon yjh@styx.cabel.net Yury Skandarov kandman@trunkmobile.com Iakov Kharon jyh@trunkmobile.com Iouri Kharon yjh@styx.cabel.net 2007,2008 TRUNK MOBILE, INC. $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/db_oracle/doc/db_oracle_admin.xml000066400000000000000000000077611300170765700233310ustar00rootroot00000000000000 User's Guide
Overview This is a module which provides Oracle connectivity for OpenSIPS. It implements the DB API defined in OpenSIPS. If you want to use the nathelper module, or any other modules that calls the get_all_ucontacts API export from usrloc, then you need to set the DORACLE_USRLOC define in the Makefile.defs file before compilation.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: instantclient-sdk-10.2.0.3 - the development headers and libraries of OCI.
Exported Parameters
<varname>timeout</varname> (fixedpoint) Timeout value for any operation with BD. Possible values is from 0.1 to 10.0 seconds. Default value is 3.0 (3 second). If value of timeout parameter set to 0, module use synchronous mode (without timeout). Set <varname>timeout</varname> parameter ... modparam("db_oracle", "timeout", 1.5) ... Disable asynchronous mode ... modparam("db_oracle", "timeout", 0) ...
<varname>reconnect</varname> (fixedpoint) Timeout value for connect (create session) operation. Possible values is from 0.1 to 10.0 seconds. Default value is 0.2 (200 milliseconds). Set <varname>reconnect</varname> parameter ... modparam("db_oracle", "reconnect", 0.5) ...
Exported Functions No function exported to be used from configuration file.
Installation Because it dependes on an external library, the oracle module is not compiled and installed by default. You can use one of the next options. - edit the "Makefile" and remove "db_oracle" from "excluded_modules" list. Then follow the standard procedure to install &osips;: "make all; make install". - from command line use: 'make all include_modules="db_oracle"; make install include_modules="db_oracle"'.
Utility opensips_orasel For working with opensipsctl script, should be able to print the 'query' results to the terminal in a user-readable form. The standard command-line Oracle client (sqlplus) is not quite suitable for this, as it cannot align row width to real (received) data's (it always prints a cell width as described in the db scheme). This problem has been solved by inclusion the utility opensips_orasel, which formats printing approximately in the same way as the 'mysql' client utility. In addition, this utility known about the "agreements and types" in DB that are used in OpenSIPS for the work with Oracle and formats printing taking these into account.
opensips-2.2.2/modules/db_oracle/ora_con.c000066400000000000000000000153531300170765700205400ustar00rootroot00000000000000/* * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "asynch.h" #include "ora_con.h" /*************************************************************************/ /* * Create a new connection structure, * open the Oracle connection and set reference count to 1 */ ora_con_t* db_oracle_new_connection(const struct db_id* id) { ora_con_t* con; char buf[512]; size_t uri_len; sword status; if (!id || !id->username || !*id->username || !id->password || !*id->password || !id->database || !*id->database) { bad_param: LM_ERR("invalid parameter value\n"); return NULL; } if (!id->host || !*id->host) { if (id->port) goto bad_param; uri_len = snprintf(buf, sizeof(buf), "%s", id->database); } else if (id->port) { uri_len = snprintf(buf, sizeof(buf), "%s:%u/%s", id->host, id->port, id->database); } else { uri_len = snprintf(buf, sizeof(buf), "%s/%s", id->host, id->database); } if (uri_len >= sizeof(buf)) goto bad_param; LM_DBG("opening connection: oracle://xxxx:xxxx@%s\n", buf); con = (ora_con_t*)pkg_malloc(sizeof(*con) + uri_len+1); if (!con) { LM_ERR("no private memory left\n"); return NULL; } memset(con, 0, sizeof(*con)); con->ref = 1; con->id = (struct db_id*)id; /* set here - freed on error */ con->uri_len = uri_len; memcpy(con->uri, buf, uri_len+1); if ( OCIEnvCreate(&con->envhp, OCI_DEFAULT | OCI_NEW_LENGTH_SEMANTICS, NULL, NULL, NULL, NULL, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->errhp, OCI_HTYPE_ERROR, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->srvhp, OCI_HTYPE_SERVER, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->svchp, OCI_HTYPE_SVCCTX, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->authp, OCI_HTYPE_SESSION, 0, NULL) != OCI_SUCCESS) { LM_ERR("no oracle memory left\n"); db_oracle_free_connection(con); return NULL; } status = OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, con->srvhp, 0, OCI_ATTR_SERVER, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIAttrSet(con->authp, OCI_HTYPE_SESSION, id->username, (ub4)strlen(id->username), OCI_ATTR_USERNAME, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIAttrSet(con->authp, OCI_HTYPE_SESSION, id->password, (ub4)strlen(id->password), OCI_ATTR_PASSWORD, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, con->authp, 0, OCI_ATTR_SESSION, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = db_oracle_reconnect(con); if (status != OCI_SUCCESS) { connect_err: if ( (status != OCI_ERROR && status != OCI_SUCCESS_WITH_INFO) || OCIErrorGet(con->errhp, 1, NULL, &status, (OraText*)buf, sizeof(buf), OCI_HTYPE_ERROR) != OCI_SUCCESS) { LM_ERR("internal driver error\n"); } else { LM_ERR("driver: %s\n", buf); } drop_connection: db_oracle_free_connection(con); return NULL; } // timelimited operation status = begin_timelimit(con, 0); if (status != OCI_SUCCESS) goto connect_err; do status = OCIServerVersion(con->svchp, con->errhp, (OraText*)buf, (ub4)sizeof(buf), OCI_HTYPE_SVCCTX); while (wait_timelimit(con, status)); if (done_timelimit(con, status)) goto drop_connection; if (status != OCI_SUCCESS) goto connect_err; LM_INFO("server version is %s\n", buf); return con; } /* * Close the connection and release memory */ void db_oracle_free_connection(ora_con_t* con) { if (!con) return; if (con->connected) db_oracle_disconnect(con); if (con->svchp) OCIHandleFree(con->svchp, OCI_HTYPE_SVCCTX); if (con->authp) OCIHandleFree(con->authp, OCI_HTYPE_SESSION); if (con->srvhp) OCIHandleFree(con->srvhp, OCI_HTYPE_SERVER); if (con->errhp) OCIHandleFree(con->errhp, OCI_HTYPE_ERROR); if (con->envhp) OCIHandleFree(con->envhp, OCI_HTYPE_ENV); free_db_id(con->id); pkg_free(con); } /* * Disconnect after network error */ void db_oracle_disconnect(ora_con_t* con) { sword status; switch (con->connected) { default: status = OCISessionEnd(con->svchp, con->errhp, con->authp, OCI_DEFAULT); if (status != OCI_SUCCESS) LM_ERR("driver: %s\n", db_oracle_error(con, status)); case 1: status = OCIServerDetach(con->srvhp, con->errhp, OCI_DEFAULT); if (status != OCI_SUCCESS) LM_ERR("driver: %s\n", db_oracle_error(con, status)); con->connected = 0; case 0: break; } } /* * Reconnect to server (after error) */ sword db_oracle_reconnect(ora_con_t* con) { sword status; if (con->connected) db_oracle_disconnect(con); /* timelimited operation, but OCI tcp-network does not support it :( */ status = OCIServerAttach(con->srvhp, con->errhp, (OraText*)con->uri, con->uri_len, 0); if (status == OCI_SUCCESS) { ++con->connected; /* * timelimited operation, but OCI has BUG in asynch * implementation of OCISessionBegin :(. * * Next code is 'empiric hack' that work (tested) in v10/v11. */ status = begin_timelimit(con, 1); if (status != OCI_SUCCESS) goto done; status = OCISessionBegin(con->svchp, con->errhp, con->authp, OCI_CRED_RDBMS, OCI_DEFAULT); while (wait_timelimit(con, status)) { sword code; status = OCIServerVersion(con->svchp, con->errhp, NULL, 0, OCI_HTYPE_SVCCTX); if ( status != OCI_ERROR || OCIErrorGet(con->errhp, 1, NULL, &code, NULL, 0, OCI_HTYPE_ERROR) != OCI_SUCCESS) break; switch (code) { case 24909: /* other call in progress */ status = OCI_STILL_EXECUTING; continue; case 3127: /* no new operation until active ends */ status = OCISessionBegin(con->svchp, con->errhp, con->authp, OCI_CRED_RDBMS, OCI_DEFAULT); default: break; } break; } if (done_timelimit(con, status)) goto done; if (status == OCI_SUCCESS) ++con->connected; } done: return status; } opensips-2.2.2/modules/db_oracle/ora_con.h000066400000000000000000000052011300170765700205340ustar00rootroot00000000000000/* * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ORA_CON_H #define ORA_CON_H #include #include "../../db/db_pool.h" #include "../../db/db_id.h" #include "../../db/db_val.h" /* Temporary -- callback data for submit_query/store_result */ struct query_data { OCIStmt** _rs; const db_val_t* _v; int _n; const db_val_t* _w; int _nw; }; typedef struct query_data query_data_t; struct ora_con { struct db_id* id; /**< Connection identifier */ unsigned int ref; /**< Reference count */ struct pool_con *async_pool; /**< Subpool of identical database handles */ int no_transfers; /**< Number of async queries to this backend */ struct db_transfer *transfers; /**< Array of ongoing async operations */ struct pool_con *next; /**< Next element in the pool (different db_id) */ OCIError *errhp; /* Error */ OCISvcCtx *svchp; /* Server Context */ OCIEnv *envhp; /* Environment */ OCISession *authp; /* Authorized Session */ OCIServer *srvhp; /* Server */ int connected; /* Authorized session started */ int bindpos; /* Last Bind handle position */ query_data_t* pqdata; /* Temporary: cb data for submit_query/store_result */ int uri_len; char uri[]; }; typedef struct ora_con ora_con_t; /* * Some convenience wrappers */ #define CON_ORA(db_con) ((ora_con_t*)db_con->tail) /* * Create a new connection structure, * open the Oracle connection and set reference count to 1 */ ora_con_t* db_oracle_new_connection(const struct db_id* id); /* * Close the connection and release memory */ void db_oracle_free_connection(ora_con_t* con); /* * Disconnect after network error */ void db_oracle_disconnect(ora_con_t* con); /* * Reconnect to server (after error) */ sword db_oracle_reconnect(ora_con_t* con); /* * Decode oracle error */ const char* db_oracle_error(ora_con_t* con, sword status); #endif /* ORA_CON_H */ opensips-2.2.2/modules/db_oracle/res.c000066400000000000000000000263361300170765700177140ustar00rootroot00000000000000/* * Oracle module result related functions * * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "../../db/db_res.h" #include "../../db/db_row.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "ora_con.h" #include "dbase.h" #include "asynch.h" #include "res.h" #define MAX_DEF_HANDLES 64 struct dmap { OCIDefine* defh[MAX_DEF_HANDLES]; union { dvoid* v; double* f; int* i; char* c; OCIDate* o; }pv[MAX_DEF_HANDLES]; dvoid* pval[MAX_DEF_HANDLES]; ub2 ilen[MAX_DEF_HANDLES]; sb2 ind[MAX_DEF_HANDLES]; ub2 len[MAX_DEF_HANDLES]; }; typedef struct dmap dmap_t; /* * Get and convert columns from a result. Define handlers and buffers */ static int get_columns(ora_con_t* con, db_res_t* _r, OCIStmt* _c, dmap_t* _d) { OCIParam *param; size_t tsz; ub4 i, n; sword status; status = OCIAttrGet(_c, OCI_HTYPE_STMT, &n, NULL, OCI_ATTR_PARAM_COUNT, con->errhp); if (status != OCI_SUCCESS) { LM_ERR("driver: %s\n", db_oracle_error(con, status)); return -1; } if (!n) { LM_ERR("no columns\n"); return -2; } if (n >= MAX_DEF_HANDLES) { LM_ERR("too many res. Rebuild with MAX_DEF_HANDLES >= %u\n", n); return -3; } if (db_allocate_columns(_r, n) != 0) { LM_ERR("could not allocate columns\n"); return -4; } for (i = 0; i < n; ++i) memset(RES_NAMES(_r)[i], 0, sizeof(db_key_t)); RES_COL_N(_r) = n; tsz = 0; memset(_d->defh, 0, sizeof(_d->defh[0]) * n); for (i = 0; i < n; i++) { ub4 len; ub2 dtype; status = OCIParamGet(_c, OCI_HTYPE_STMT, con->errhp, (dvoid**)(dvoid*)¶m, i+1); if (status != OCI_SUCCESS) goto ora_err; { text* name; status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&name, &len, OCI_ATTR_NAME, con->errhp); if (status != OCI_SUCCESS) goto ora_err; RES_NAMES(_r)[i]->s = (char*)pkg_malloc(len+1); if (!RES_NAMES(_r)[i]->s) { db_free_columns(_r); LM_ERR("no private memory left\n"); return -5; } RES_NAMES(_r)[i]->len = len; memcpy(RES_NAMES(_r)[i]->s, name, len); RES_NAMES(_r)[i]->s[len] = '\0'; } status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE, con->errhp); if (status != OCI_SUCCESS) goto ora_err; switch (dtype) { case SQLT_UIN: /* unsigned integer */ set_bitmap: LM_DBG("use DB_BITMAP type\n"); RES_TYPES(_r)[i] = DB_BITMAP; len = sizeof(VAL_BITMAP((db_val_t*)NULL)); break; case SQLT_INT: /* (ORANET TYPE) integer */ set_int: LM_DBG("use DB_INT result type\n"); RES_TYPES(_r)[i] = DB_INT; len = sizeof(VAL_INT((db_val_t*)NULL)); break; case SQLT_LNG: /* long */ case SQLT_VNU: /* NUM with preceding length byte */ case SQLT_NUM: /* (ORANET TYPE) oracle numeric */ len = 0; /* PRECISION is ub1 */ status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&len, NULL, OCI_ATTR_PRECISION, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (len <= 11) { sb1 sc; status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&sc, NULL, OCI_ATTR_SCALE, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (!sc) { dtype = SQLT_INT; if (len != 11) goto set_int; dtype = SQLT_UIN; goto set_bitmap; } } LM_DBG("use DB_BIGINT result type\n"); RES_TYPES(_r)[i] = DB_BIGINT; len = sizeof(VAL_BIGINT((db_val_t*)NULL)); dtype = SQLT_NUM; break; case SQLT_FLT: /* (ORANET TYPE) Floating point number */ case SQLT_BFLOAT: /* Native Binary float*/ case SQLT_BDOUBLE: /* NAtive binary double */ case SQLT_IBFLOAT: /* binary float canonical */ case SQLT_IBDOUBLE: /* binary double canonical */ case SQLT_PDN: /* (ORANET TYPE) Packed Decimal Numeric */ LM_DBG("use DB_DOUBLE result type\n"); RES_TYPES(_r)[i] = DB_DOUBLE; len = sizeof(VAL_DOUBLE((db_val_t*)NULL)); dtype = SQLT_FLT; break; // case SQLT_TIME: /* TIME */ // case SQLT_TIME_TZ: /* TIME WITH TIME ZONE */ case SQLT_DATE: /* ANSI Date */ case SQLT_DAT: /* date in oracle format */ case SQLT_ODT: /* OCIDate type */ case SQLT_TIMESTAMP: /* TIMESTAMP */ case SQLT_TIMESTAMP_TZ: /* TIMESTAMP WITH TIME ZONE */ case SQLT_TIMESTAMP_LTZ:/* TIMESTAMP WITH LOCAL TZ */ // case SQLT_INTERVAL_YM: /* INTERVAL YEAR TO MONTH */ // case SQLT_INTERVAL_DS: /* INTERVAL DAY TO SECOND */ LM_DBG("use DB_DATETIME result type\n"); RES_TYPES(_r)[i] = DB_DATETIME; len = sizeof(OCIDate); dtype = SQLT_ODT; break; case SQLT_CLOB: /* character lob */ case SQLT_BLOB: /* binary lob */ // case SQLT_BFILEE: /* binary file lob */ // case SQLT_CFILEE: /* character file lob */ // case SQLT_BIN: /* binary data(DTYBIN) */ // case SQLT_LBI: /* long binary */ LM_DBG("use DB_BLOB result type\n"); RES_TYPES(_r)[i] = DB_BLOB; goto dyn_str; case SQLT_CHR: /* (ORANET TYPE) character string */ case SQLT_STR: /* zero terminated string */ case SQLT_VST: /* OCIString type */ case SQLT_VCS: /* Variable character string */ case SQLT_AFC: /* Ansi fixed char */ case SQLT_AVC: /* Ansi Var char */ // case SQLT_RID: /* rowid */ LM_DBG("use DB_STR result type\n"); RES_TYPES(_r)[i] = DB_STR; dyn_str: dtype = SQLT_CHR; len = 0; /* DATA_SIZE is ub2 */ status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&len, NULL, OCI_ATTR_DATA_SIZE, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (len >= 4000) { LM_DBG("use DB_BLOB result type\n"); RES_TYPES(_r)[i] = DB_BLOB; } ++len; break; default: LM_ERR("unsupported datatype %d\n", dtype); goto stop_load; } _d->ilen[i] = (ub2)len; _d->pv[i].v = st_buf + tsz; tsz += len; status = OCIDefineByPos(_c, &_d->defh[i], con->errhp, i+1, _d->pv[i].v, len, dtype, &_d->ind[i], &_d->len[i], NULL, OCI_DEFAULT); if (status != OCI_SUCCESS) goto ora_err; } #if STATIC_BUF_LEN < 65536 #error #endif if (tsz > 65536) { LM_ERR("Row size exceed 65K. IOB's are not supported\n"); goto stop_load; } return 0; ora_err: LM_ERR("driver: %s\n", db_oracle_error(con, status)); stop_load: db_free_columns(_r); return -6; } /* * Convert data fron db format to internal format */ static int convert_row(db_res_t* _res, db_row_t* _r, dmap_t* _d) { unsigned i, n = RES_COL_N(_res); ROW_N(_r) = n; for (i = 0; i < n; i++) { static const str dummy_string = {"", 0}; db_val_t* v = &ROW_VALUES(_r)[i]; db_type_t t = RES_TYPES(_res)[i]; if (_d->ind[i] == -1) { /* Initialize the string pointers to a dummy empty * string so that we do not crash when the NULL flag * is set but the module does not check it properly */ VAL_STRING(v) = dummy_string.s; VAL_STR(v) = dummy_string; VAL_BLOB(v) = dummy_string; VAL_TYPE(v) = t; VAL_NULL(v) = 1; continue; } if (_d->ind[i]) LM_WARN("truncated value in DB\n"); VAL_TYPE(v) = t; switch (t) { case DB_INT: VAL_INT(v) = *_d->pv[i].i; break; case DB_BIGINT: VAL_BIGINT(v) = *_d->pv[i].i; break; case DB_BITMAP: VAL_BITMAP(v) = *_d->pv[i].i; break; case DB_DOUBLE: VAL_DOUBLE(v) = *_d->pv[i].f; break; case DB_DATETIME: { struct tm tm; memset(&tm, 0, sizeof(tm)); OCIDateGetTime(_d->pv[i].o, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); OCIDateGetDate(_d->pv[i].o, &tm.tm_year, &tm.tm_mon, &tm.tm_mday); if (tm.tm_mon) --tm.tm_mon; if (tm.tm_year >= 1900) tm.tm_year -= 1900; VAL_TIME(v) = mktime(&tm); } break; case DB_STR: case DB_BLOB: case DB_STRING: { size_t len = _d->len[i]; char *pstr = pkg_malloc(len+1); if (pstr == NULL) return -1; memcpy(pstr, _d->pv[i].c, len); pstr[len] = '\0'; VAL_FREE(v) = 1; if (t == DB_STR) { VAL_STR(v).s = pstr; VAL_STR(v).len = len; } else if (t == DB_BLOB) { VAL_BLOB(v).s = pstr; VAL_BLOB(v).len = len; } else { VAL_STRING(v) = pstr; } } break; default: LM_ERR("unknown type mapping (%u)\n", t); return -2; } } return 0; } /* * Get rows and convert it from oracle to db API representation */ static int get_rows(ora_con_t* con, db_res_t* _r, OCIStmt* _c, dmap_t* _d) { ub4 rcnt; sword status; unsigned n = RES_COL_N(_r); memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n); // timelimited operation status = begin_timelimit(con, 0); if (status != OCI_SUCCESS) goto ora_err; do status = OCIStmtFetch2(_c, con->errhp, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT); while (wait_timelimit(con, status)); if (done_timelimit(con, status)) goto stop_load; if (status != OCI_SUCCESS) { if (status != OCI_NO_DATA) goto ora_err; RES_ROW_N(_r) = 0; RES_ROWS(_r) = NULL; return 0; } status = OCIAttrGet(_c, OCI_HTYPE_STMT, &rcnt, NULL, OCI_ATTR_CURRENT_POSITION, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (!rcnt) { LM_ERR("lastpos==0\n"); goto stop_load; } RES_ROW_N(_r) = rcnt; if (db_allocate_rows( _r, rcnt)!=0) { LM_ERR("no private memory left\n"); return -1; } while ( 1 ) { if (convert_row(_r, &RES_ROWS(_r)[--rcnt], _d) < 0) { LM_ERR("error convert row\n"); goto stop_load; } if (!rcnt) return 0; memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n); // timelimited operation status = begin_timelimit(con, 0); if (status != OCI_SUCCESS) goto ora_err; do status = OCIStmtFetch2(_c, con->errhp, 1, OCI_FETCH_PRIOR, 0, OCI_DEFAULT); while (wait_timelimit(con, status)); if (done_timelimit(con, status)) goto stop_load; if (status != OCI_SUCCESS) break; } ora_err: LM_ERR("driver: %s\n", db_oracle_error(con, status)); stop_load: db_free_rows(_r); RES_ROW_N(_r) = 0; /* TODO: skipped in db_res.c :) */ return -3; } /* * Read database answer and fill the structure */ int db_oracle_store_result(const db_con_t* _h, db_res_t** _r) { dmap_t dmap; int rc; db_res_t* r; ora_con_t* con; OCIStmt* hs; if (!_h || !_r) { badparam: LM_ERR("invalid parameter\n"); return -1; } con = CON_ORA(_h); { query_data_t *pcb = con->pqdata; if (!pcb || !pcb->_rs) goto badparam; hs = *pcb->_rs; pcb->_rs = NULL; /* paranoid for next call */ } rc = -1; if (_r) *_r = NULL; /* unification for all errors */ r = db_new_result(); if (!r) { LM_ERR("no memory left\n"); goto done; } if (get_columns(con, r, hs, &dmap) < 0) { LM_ERR("error while getting column names\n"); goto done; } if (get_rows(con, r, hs, &dmap) < 0) { LM_ERR("error while converting rows\n"); db_free_columns(r); goto done; } rc = 0; *_r = r; done: OCIHandleFree(hs, OCI_HTYPE_STMT); return rc; } opensips-2.2.2/modules/db_oracle/res.h000066400000000000000000000022011300170765700177020ustar00rootroot00000000000000/* * Oracle module result related functions * * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RES_H #define RES_H #include "../../db/db_res.h" #include "../../db/db_con.h" #define STATIC_BUF_LEN 65536 extern char st_buf[STATIC_BUF_LEN]; /* * Read database answer and fill the structure */ int db_oracle_store_result(const db_con_t* _h, db_res_t** _r); #endif /* RES_H */ opensips-2.2.2/modules/db_oracle/val.c000066400000000000000000000070561300170765700177030ustar00rootroot00000000000000/* * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "../../dprint.h" #include "ora_con.h" #include "dbase.h" #include "val.h" /* * Convert value to sql-string as db bind index */ int db_oracle_val2str(const db_con_t* _c, const db_val_t* _v, char* _s, int* _len) { int ret; if (!_c || !_v || !_s || !_len || *_len <= 0) { LM_ERR("invalid parameter value\n"); return -1; } ret = snprintf(_s, *_len, ":%u", ++CON_ORA(_c)->bindpos); if ((unsigned)ret >= (unsigned)*_len) return sql_buf_small(); *_len = ret; return 0; } /* * Called after val2str to realy binding */ int db_oracle_val2bind(bmap_t* _m, const db_val_t* _v, OCIDate* _o) { if (VAL_NULL(_v)) { _m->addr = NULL; _m->size = 0; switch(VAL_TYPE(_v)) { case DB_INT: _m->type = SQLT_INT; break; case DB_BIGINT: _m->type = SQLT_NUM; break; case DB_BITMAP: _m->type = SQLT_UIN; break; case DB_DOUBLE: _m->type = SQLT_FLT; break; case DB_STRING: _m->type = SQLT_STR; break; case DB_STR: _m->type = SQLT_CHR; break; case DB_DATETIME: _m->type = SQLT_ODT; break; case DB_BLOB: _m->type = SQLT_CLOB; break; default: LM_ERR("unknown data type\n"); return -1; } return 0; } switch (VAL_TYPE(_v)) { case DB_INT: _m->addr = (int*)&VAL_INT(_v); _m->size = sizeof(VAL_INT(_v)); _m->type = SQLT_INT; break; case DB_BIGINT: _m->addr = (int*)&VAL_BIGINT(_v); _m->size = sizeof(VAL_BIGINT(_v)); _m->type = SQLT_NUM; break; case DB_BITMAP: _m->addr = (unsigned*)&VAL_BITMAP(_v); _m->size = sizeof(VAL_BITMAP(_v)); _m->type = SQLT_UIN; break; case DB_DOUBLE: _m->addr = (double*)&VAL_DOUBLE(_v); _m->size = sizeof(VAL_DOUBLE(_v)); _m->type = SQLT_FLT; break; case DB_STRING: _m->addr = (char*)VAL_STRING(_v); _m->size = strlen(VAL_STRING(_v))+1; _m->type = SQLT_STR; break; case DB_STR: { unsigned len = VAL_STR(_v).len; char *estr, *pstr = VAL_STR(_v).s; estr = (char*)memchr(pstr, 0, len); if (estr) { LM_WARN("truncate STR len from %u to: '%s'\n", len, pstr); len = (unsigned)(estr - pstr) + 1; } _m->size = len; _m->addr = pstr; _m->type = SQLT_CHR; } break; case DB_DATETIME: { struct tm* tm = localtime(&VAL_TIME(_v)); if (tm->tm_sec == 60) --tm->tm_sec; OCIDateSetDate(_o, (ub2)(tm->tm_year + 1900), (ub1)(tm->tm_mon + 1), (ub1)tm->tm_mday); OCIDateSetTime(_o, (ub1)tm->tm_hour, (ub1)tm->tm_min, (ub1)tm->tm_sec); _m->addr = _o; _m->size = sizeof(*_o); _m->type = SQLT_ODT; } break; case DB_BLOB: _m->addr = VAL_BLOB(_v).s; _m->size = VAL_BLOB(_v).len; _m->type = SQLT_CLOB; break; default: LM_ERR("unknown data type\n"); return -1; } return 0; } opensips-2.2.2/modules/db_oracle/val.h000066400000000000000000000024121300170765700176770ustar00rootroot00000000000000/* * Copyright (C) 2007,2008 TRUNK MOBILE * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VAL_H #define VAL_H #include #include "../../db/db_val.h" #include "../../db/db.h" struct bmap_t { dvoid *addr; ub4 size; ub2 type; }; typedef struct bmap_t bmap_t; /* * Convert value to sql-string as db bind index */ int db_oracle_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len); /* * Called after val2str to realy binding */ int db_oracle_val2bind(bmap_t* _m, const db_val_t* _v, OCIDate* _o); #endif /* VAL_H */ opensips-2.2.2/modules/db_perlvdb/000077500000000000000000000000001300170765700171365ustar00rootroot00000000000000opensips-2.2.2/modules/db_perlvdb/Makefile000066400000000000000000000005301300170765700205740ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=db_perlvdb.so ifeq ($(CC_NAME), gcc) DEFS+=-Wno-unused -Wno-redundant-decls endif LIBS=$(shell perl -MExtUtils::Embed -e ldopts) DEFS+=$(shell perl -MExtUtils::Embed -e ccopts) include ../../Makefile.modules opensips-2.2.2/modules/db_perlvdb/README000066400000000000000000000215511300170765700200220ustar00rootroot00000000000000Perl Virtual Database Module Bastian Friedrich Collax GmbH Edited by Bastian Friedrich Copyright © 2007 Collax GmbH Revision History Revision $Revision: 9599 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.4. Exported Functions 2. Developer Guide 2.1. Introduction 2.2. Base class OpenSIPS::VDB 2.3. Data types 2.3.1. OpenSIPS::VDB::Value 2.3.2. OpenSIPS::VDB::Pair 2.3.3. OpenSIPS::VDB::ReqCond 2.3.4. OpenSIPS::VDB::Column 2.3.5. OpenSIPS::VDB::Result 2.4. Adapters 2.4.1. Function parameters 2.5. VTabs Chapter 1. Admin Guide 1.1. Overview The Perl Virtual Database (VDB) provides a virtualization framework for OpenSIPS's database access. It does not handle a particular database engine itself but lets the user relay database requests to arbitrary Perl functions. This module cannot be used "out of the box". The user has to supply functionality dedicated to the client module. See below for options. The module can be used in all current OpenSIPS modules that need database access. Relaying of insert, update, query and delete operations is supported. Modules can be configured to use the db_perlvdb module as database backend using the db_url_parameter: modparam("acc", "db_url", "perlvdb:OpenSIPS::VDB::Adapter::AccountingSIP trace") This configuration options tells acc module that it should use the db_perlvdb module which will in turn use the Perl class OpenSIPS::VDB::Adapter::AccountingSIPtrace to relay the database requests. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * perl -- Perl module 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None (Besides the ones mentioned in the perl module documentation). 1.3. Exported Parameters None. 1.4. Exported Functions None. Chapter 2. Developer Guide 2.1. Introduction OpenSIPS uses a database API for requests of numerous different types of data. Four primary operations are supported: * query * insert * update * delete This module relays these database requests to user implemented Perl functions. 2.2. Base class OpenSIPS::VDB A client module has to be configured to use the db_perlvdb module in conjunction with a Perl class to provide the functions. The configured class needs to inherit from the base class OpenSIPS::VDB. Derived classes have to implement the necessary functions "query", "insert", "update" and/or "delete". The client module specifies the necessary functions. To find out which functions are called from a module, its processes may be evaluated with the OpenSIPS::VDB::Adapter::Describe class which will log incoming requests (without actually providing any real functionality). While users can directly implement their desired functionality in a class derived from OpenSIPS::VDB, it is advisable to split the implementation into an Adapter that transforms the relational structured parameters into pure Perl function arguments, and add a virtual table (VTab) to provide the relaying to an underlying technology. 2.3. Data types Before introducing the higher level concepts of this module, the used datatypes will briefly be explained. The OpenSIPS Perl library includes some data types that have to be used in this module: 2.3.1. OpenSIPS::VDB::Value A value includes a data type flag and a value. Valid data types are DB_INT, DB_DOUBLE, DB_STRING, DB_STR, DB_DATETIME, DB_BLOB, DB_BITMAP. A new variable may be created with my $val = new OpenSIPS::VDB::Value(DB_STRING, "foobar"); Value objects contain the type() and data() methods to get or set the type and data attributes. 2.3.2. OpenSIPS::VDB::Pair The Pair class is derived from the Value class and additionally contains a column name (key). A new variable may be created with my $pair = new OpenSIPS::VDB::Pair("foo", DB_STRING, "bar"); where foo is the key and bar is the value. Additonally to the methods of the Value class, it contains a key() method to get or set the key attribute. 2.3.3. OpenSIPS::VDB::ReqCond The ReqCond class is used for select condition and is derived from the Pair class. It contains an addtional operator attribute. A new variable may be created with my $cond = new OpenSIPS::VDB::ReqCond("foo", ">", DB_INT, 5); where foo is the key, "greater" is the operator and 5 is the value to compare. Additonally to the methods of the Pair class, it contains an op() method to get or set the operator attribute. 2.3.4. OpenSIPS::VDB::Column This class represents a column definition or database schema. It contains an array for the column names and an array for the column types. Both arrays need to have the same length. A new variable may be created with my @types = { DB_INT, DB_STRING }; my @names = { "id", "vals" }; my $cols = new OpenSIPS::VDB::Column(\@types, \@names); The class contains the methods type() and name() to get or set the type and name arrays. 2.3.5. OpenSIPS::VDB::Result The Result class represents a query result. It contains a schema (class Column) and an array of rows, where each row is an array of Values. The object methods coldefs() and rows() may be used to get and set the object attributes. 2.4. Adapters Adapters should be used to turn the relational structured database request into pure Perl function arguments. The alias_db function alias_db_lookup for example takes a user/host pair, and turns it into another user/host pair. The Alias adapter turns the ReqCond array into two separate scalars that are used as parameters for a VTab call. Adapter classes have to inherit from the OpenSIPS::VDB base class and may provide one or more functions with the names insert, update, replace, query and/or delete, depending on the module which is to be used with the adapter. While modules such as alias_db only require a query function, others -- such as siptrace -- depend on inserts only. 2.4.1. Function parameters The implemented functions need to deal with the correct data types. The parameter and return types are listed in this section. insert() is passed an array of OpenSIPS::VDB::Pair objects. It should return an integer value. replace() is passed an array of OpenSIPS::VDB::Pair objects. This function is currently not used by any publicly available modules. It should return an integer value. delete() is passed an array of OpenSIPS::VDB::ReqCond objects. It should return an integer value. update() is passed an array of OpenSIPS::VDB::ReqCond objects (which rows to update) and an array of OpenSIPS::VDB::Pair objects (new data). It should return an integer value. query() is passed an array of OpenSIPS::VDB::ReqCond objects (which rows to select), an array of strings (which column names to return) and a single string by which column to sort. It should return an object of type OpenSIPS::VDB::Result. 2.5. VTabs VTabs (virtual tables) provide a particular implementation for an adapter. The Alias adapter e.g. calls a function with two parameters (user, host) and expects a hash to be returned with the two elements username and domain, or undef (when no result is found). A sample VTab implementation for the Alias adapter demonstrates this technique with a Perl hash that contains the alias data. The standard Adapter/VTab pattern lets the user choose between three options on how to implement VTabs: * Single function. When a function is used as a virtual table, it is passed the operation name (insert, replace, update, query, delete) as its first parameter. The function may be implemented in the main namespace. * Package/class. The defined class needs to have an init() function. It will be called during the first call of that VTab. Addtionally, the package has to define the necessary functions insert, replace, update, delete and/or query. These functions will be called in a function context (first parameter is the class name). * Object. The defined class needs to have a new() function which will return a reference to the newly created object. This object needs to define the necessary functions insert, replace, update, delete and/or query. These functions will be called in a method context (first parameter is a reference to the object). opensips-2.2.2/modules/db_perlvdb/doc/000077500000000000000000000000001300170765700177035ustar00rootroot00000000000000opensips-2.2.2/modules/db_perlvdb/doc/db_perlvdb.xml000066400000000000000000000023071300170765700225320ustar00rootroot00000000000000 %docentities; ]> Perl Virtual Database Module &osipsname; Bastian Friedrich Collax GmbH
bastian.friedrich@collax.com
Bastian Friedrich
bastian.friedrich@collax.com
2007 Collax GmbH $Revision: 9599 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/db_perlvdb/doc/db_perlvdb_admin.xml000066400000000000000000000040601300170765700237000ustar00rootroot00000000000000 &adminguide;
Overview The Perl Virtual Database (VDB) provides a virtualization framework for &osips;'s database access. It does not handle a particular database engine itself but lets the user relay database requests to arbitrary Perl functions. This module cannot be used "out of the box". The user has to supply functionality dedicated to the client module. See below for options. The module can be used in all current &osips; modules that need database access. Relaying of insert, update, query and delete operations is supported. Modules can be configured to use the db_perlvdb module as database backend using the db_url_parameter: modparam("acc", "db_url", "perlvdb:OpenSIPS::VDB::Adapter::AccountingSIPtrace") This configuration options tells acc module that it should use the db_perlvdb module which will in turn use the Perl class OpenSIPS::VDB::Adapter::AccountingSIPtrace to relay the database requests.
Dependencies
&osips; Modules The following modules must be loaded before this module: perl -- Perl module
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None (Besides the ones mentioned in the perl module documentation).
Exported Parameters None.
Exported Functions None.
opensips-2.2.2/modules/db_perlvdb/doc/db_perlvdb_devel.xml000066400000000000000000000200221300170765700237030ustar00rootroot00000000000000 &develguide;
Introduction OpenSIPS uses a database API for requests of numerous different types of data. Four primary operations are supported: query insert update delete This module relays these database requests to user implemented Perl functions.
Base class OpenSIPS::VDB A client module has to be configured to use the db_perlvdb module in conjunction with a Perl class to provide the functions. The configured class needs to inherit from the base class OpenSIPS::VDB. Derived classes have to implement the necessary functions "query", "insert", "update" and/or "delete". The client module specifies the necessary functions. To find out which functions are called from a module, its processes may be evaluated with the OpenSIPS::VDB::Adapter::Describe class which will log incoming requests (without actually providing any real functionality). While users can directly implement their desired functionality in a class derived from OpenSIPS::VDB, it is advisable to split the implementation into an Adapter that transforms the relational structured parameters into pure Perl function arguments, and add a virtual table (VTab) to provide the relaying to an underlying technology.
Data types Before introducing the higher level concepts of this module, the used datatypes will briefly be explained. The OpenSIPS Perl library includes some data types that have to be used in this module:
OpenSIPS::VDB::Value A value includes a data type flag and a value. Valid data types are DB_INT, DB_DOUBLE, DB_STRING, DB_STR, DB_DATETIME, DB_BLOB, DB_BITMAP. A new variable may be created with my $val = new OpenSIPS::VDB::Value(DB_STRING, "foobar"); Value objects contain the type() and data() methods to get or set the type and data attributes.
OpenSIPS::VDB::Pair The Pair class is derived from the Value class and additionally contains a column name (key). A new variable may be created with my $pair = new OpenSIPS::VDB::Pair("foo", DB_STRING, "bar"); where foo is the key and bar is the value. Additonally to the methods of the Value class, it contains a key() method to get or set the key attribute.
OpenSIPS::VDB::ReqCond The ReqCond class is used for select condition and is derived from the Pair class. It contains an addtional operator attribute. A new variable may be created with my $cond = new OpenSIPS::VDB::ReqCond("foo", ">", DB_INT, 5); where foo is the key, "greater" is the operator and 5 is the value to compare. Additonally to the methods of the Pair class, it contains an op() method to get or set the operator attribute.
OpenSIPS::VDB::Column This class represents a column definition or database schema. It contains an array for the column names and an array for the column types. Both arrays need to have the same length. A new variable may be created with my @types = { DB_INT, DB_STRING }; my @names = { "id", "vals" }; my $cols = new OpenSIPS::VDB::Column(\@types, \@names); The class contains the methods type() and name() to get or set the type and name arrays.
OpenSIPS::VDB::Result The Result class represents a query result. It contains a schema (class Column) and an array of rows, where each row is an array of Values. The object methods coldefs() and rows() may be used to get and set the object attributes.
Adapters Adapters should be used to turn the relational structured database request into pure Perl function arguments. The alias_db function alias_db_lookup for example takes a user/host pair, and turns it into another user/host pair. The Alias adapter turns the ReqCond array into two separate scalars that are used as parameters for a VTab call. Adapter classes have to inherit from the OpenSIPS::VDB base class and may provide one or more functions with the names insert, update, replace, query and/or delete, depending on the module which is to be used with the adapter. While modules such as alias_db only require a query function, others -- such as siptrace -- depend on inserts only.
Function parameters The implemented functions need to deal with the correct data types. The parameter and return types are listed in this section. insert() is passed an array of OpenSIPS::VDB::Pair objects. It should return an integer value. replace() is passed an array of OpenSIPS::VDB::Pair objects. This function is currently not used by any publicly available modules. It should return an integer value. delete() is passed an array of OpenSIPS::VDB::ReqCond objects. It should return an integer value. update() is passed an array of OpenSIPS::VDB::ReqCond objects (which rows to update) and an array of OpenSIPS::VDB::Pair objects (new data). It should return an integer value. query() is passed an array of OpenSIPS::VDB::ReqCond objects (which rows to select), an array of strings (which column names to return) and a single string by which column to sort. It should return an object of type OpenSIPS::VDB::Result.
VTabs VTabs (virtual tables) provide a particular implementation for an adapter. The Alias adapter e.g. calls a function with two parameters (user, host) and expects a hash to be returned with the two elements username and domain, or undef (when no result is found). A sample VTab implementation for the Alias adapter demonstrates this technique with a Perl hash that contains the alias data. The standard Adapter/VTab pattern lets the user choose between three options on how to implement VTabs: Single function. When a function is used as a virtual table, it is passed the operation name (insert, replace, update, query, delete) as its first parameter. The function may be implemented in the main namespace. Package/class. The defined class needs to have an init() function. It will be called during the first call of that VTab. Addtionally, the package has to define the necessary functions insert, replace, update, delete and/or query. These functions will be called in a function context (first parameter is the class name). Object. The defined class needs to have a new() function which will return a reference to the newly created object. This object needs to define the necessary functions insert, replace, update, delete and/or query. These functions will be called in a method context (first parameter is a reference to the object).
opensips-2.2.2/modules/db_perlvdb/doc/samples/000077500000000000000000000000001300170765700213475ustar00rootroot00000000000000opensips-2.2.2/modules/db_perlvdb/doc/samples/alias_ldap.pm000066400000000000000000000012561300170765700240020ustar00rootroot00000000000000package alias_ldap; use OpenSIPS::LDAPUtils::LDAPConf; use OpenSIPS::LDAPUtils::LDAPConnection; use OpenSIPS::Constants; sub init {} sub query { my $self = shift; my $alias_username = shift; my $alias_domain = shift; my $uri = "$alias_username\@$alias_domain"; my $ldap = new OpenSIPS::LDAPUtils::LDAPConnection(); OpenSIPS::log(L_INFO, "Trying LDAP request with $uri\n"); my @ldaprows = $ldap->search("(&(ObjectClass=inetOrgPerson)(mail=$uri))", "ou=people,dc=example,dc=com", "uid"); if (@ldaprows[0]) { OpenSIPS::log(L_INFO, "Got a row: ".@ldaprows[0]."\n"); my $ret; $ret->{username} = @ldaprows[0]; $ret->{domain} = "voip"; return $ret; } return; } 1; opensips-2.2.2/modules/db_perlvdb/doc/samples/flatstoresimulator.pm000066400000000000000000000005521300170765700256520ustar00rootroot00000000000000package flatstoresimulator; sub init {} sub insert { my $sub = shift; my $vals = shift; my $logline = ""; open F, ">>/tmp/myfile"; for my $v (@$vals) { $logline .= $v->data()." | "; #$logline .= "keys are ".$v->key().", type is ".$v->type().", data is ".$v->data()."\n"); } $logline .= "\n"; print F $logline; close F; return 0; } 1; opensips-2.2.2/modules/db_perlvdb/doc/samples/simplealias.pm000066400000000000000000000012251300170765700242100ustar00rootroot00000000000000package simplealias; sub init {} my @aliases = ( { alias_username => "a1", alias_domain => "example.com", username => "user", domain => "example.com" }, { alias_username => "a2", alias_domain => "example.com", username => "anotheruser", domain => "example.com" }, ); sub query { my $self = shift; my $alias_username = shift; my $alias_domain = shift; foreach my $entry (@aliases) { if (($entry->{alias_username} eq $alias_username) && ((!defined alias_domain) || ($entry->{alias_domain} == $alias_domain))) { my $ret; $ret->{username} = $entry->{username}; $ret->{domain} = $entry->{domain}; return $ret; } } } 1; opensips-2.2.2/modules/db_perlvdb/doc/samples/simpleauth.pm000066400000000000000000000005321300170765700240600ustar00rootroot00000000000000package simpleauth; sub init {} sub version { 6 }; my @accounts = ( { username => "foo", password => "abc", }, { username => "bar", password => "def", }, ); sub query { my $self = shift; my $username = shift; foreach my $entry (@accounts) { if ($entry->{username} eq $username) { return $entry->{password}; } } } 1; opensips-2.2.2/modules/db_perlvdb/doc/samples/simplespeeddial.pm000066400000000000000000000006261300170765700250550ustar00rootroot00000000000000package simplespeeddial; sub init {} my @dials = ( { un => "user1", sd => 44, na => "sip:someone\@somewhere.com" }, { un => "user2", sd => 45, na => "sip:someoneelse\@somewheredifferent.com" }, ); sub query { my $self = shift; my $un = shift; my $sd = shift; foreach my $entry (@dials) { if (($entry->{un} eq $un) && ($entry->{sd} == $sd)) { return $entry->{na}; } } } 1; opensips-2.2.2/modules/db_perlvdb/perlvdb.c000066400000000000000000000053361300170765700207470ustar00rootroot00000000000000/* * Perl virtual database module * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "../../sr_module.h" #include "perlvdb.h" static int child_init(int rank); static int mod_init(void); static void mod_destroy(void); SV* vdbmod; /* * Perl virtual database module interface */ static cmd_export_t cmds[] = { {"db_use_table", (cmd_function)perlvdb_use_table, 2, 0, 0, 0}, {"db_init", (cmd_function)perlvdb_db_init, 1, 0, 0, 0}, {"db_close", (cmd_function)perlvdb_db_close, 2, 0, 0, 0}, {"db_insert", (cmd_function)perlvdb_db_insert, 2, 0, 0, 0}, {"db_update", (cmd_function)perlvdb_db_update, 2, 0, 0, 0}, {"db_delete", (cmd_function)perlvdb_db_delete, 2, 0, 0, 0}, {"db_query", (cmd_function)perlvdb_db_query, 2, 0, 0, 0}, {"db_free_result", (cmd_function)perlvdb_db_free_result, 2, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "perl", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "db_perlvdb", MOD_TYPE_SQLDB,/* class of this module */ MODULE_VERSION, RTLD_NOW | RTLD_GLOBAL, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, 0, params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ mod_destroy, /* destroy function */ child_init /* per-child init function */ }; static int mod_init(void) { if (!module_loaded("perl")) { LM_CRIT("perl module not loaded. Exiting.\n"); return -1; } return 0; } static void mod_destroy(void) { } static int child_init(int rank) { return 0; } opensips-2.2.2/modules/db_perlvdb/perlvdb.h000066400000000000000000000036551300170765700207560ustar00rootroot00000000000000/* * Perl virtual database module interface * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PERLVDB_H #define _PERLVDB_H #include "../../db/db.h" #include "../../sr_module.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" /* lock_ops.h defines union semun, perl does not need to redefine it */ #ifdef USE_SYSV_SEM # define HAS_UNION_SEMUN #endif #undef OP_LT #undef OP_GT #undef OP_EQ #undef load_module #include #include #include "perlvdb_conv.h" #include "perlvdb_oohelpers.h" #include "perlvdbfunc.h" #define PERL_VDB_BASECLASS "OpenSIPS::VDB" #define PERL_VDB_USETABLEMETHOD "use_table" #define PERL_VDB_INSERTMETHOD "_insert" #define PERL_VDB_REPLACEMETHOD "_replace" #define PERL_VDB_UPDATEMETHOD "_update" #define PERL_VDB_DELETEMETHOD "_delete" #define PERL_VDB_QUERYMETHOD "_query" #define PERL_VDB_COLDEFSMETHOD "coldefs" #define PERL_VDB_TYPEMETHOD "type" #define PERL_VDB_NAMEMETHOD "name" #define PERL_VDB_ROWSMETHOD "rows" #define PERL_VDB_DATAMETHOD "data" extern PerlInterpreter* my_perl; extern SV* vdbmod; #endif /* _PERLVDB_H */ opensips-2.2.2/modules/db_perlvdb/perlvdb_conv.c000066400000000000000000000232131300170765700217660ustar00rootroot00000000000000/* * Perl virtual database module interface * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "perlvdb_conv.h" #include "perlvdb_oohelpers.h" #include "../../dprint.h" #include "../../mem/mem.h" /* Converts a set of pairs to perl SVs. * For insert, and update (second half) */ AV *pairs2perlarray(db_key_t* keys, db_val_t* vals, int n) { AV *array = newAV(); SV *element; int i; for (i = 0; i < n; i++) { element = pair2perlpair(*(keys + i), vals + i); av_push(array, element); } return array; } /* Converts a set of cond's to perl SVs. * For delete, update (first half), query */ AV *conds2perlarray(db_key_t* keys, db_op_t* ops, db_val_t* vals, int n) { AV *array = NULL; SV *element = NULL; int i = 0; array = newAV(); for (i = 0; i < n; i++) { if (ops) { if (ops + i) if (*(ops + i)){ element = cond2perlcond(*(keys + i), *(ops + i), vals + i); } } else { /* OP_EQ is defined in OpenSIPS _and_ perl. Includes collide :( */ #ifdef OP_EQ element = cond2perlcond(*(keys + i), OP_EQ, vals + i); #else element = cond2perlcond(*(keys + i), "=", vals + i); #endif } av_push(array, element); } return array; } /* Converts a set of key names to a perl array. * Needed in query. */ AV *keys2perlarray(db_key_t* keys, int n) { AV *array = newAV(); SV *element; int i; for (i = 0; i < n; i++) { element = newSVpv((keys[i])->s, (keys[i])->len); av_push(array, element); } return array; } inline SV *valdata(db_val_t* val) { SV *data = &PL_sv_undef; const char* stringval; switch(VAL_TYPE(val)) { case DB_BIGINT: case DB_INT: data = newSViv(VAL_INT(val)); break; case DB_DOUBLE: data = newSVnv(VAL_DOUBLE(val)); break; case DB_STRING: stringval = VAL_STRING(val); if (strlen(stringval) > 0) data = newSVpv(stringval, strlen(stringval)); else data = &PL_sv_undef; break; case DB_STR: if (VAL_STR(val).len > 0) data = newSVpv(VAL_STR(val).s, VAL_STR(val).len); else data = &PL_sv_undef; break; case DB_DATETIME: data = newSViv((unsigned int)VAL_TIME(val)); break; case DB_BLOB: if (VAL_BLOB(val).len > 0) data = newSVpv(VAL_BLOB(val).s, VAL_BLOB(val).len); else data = &PL_sv_undef; break; case DB_BITMAP: data = newSViv(VAL_BITMAP(val)); break; } return data; } SV *val2perlval(db_val_t* val) { SV* retval; SV *class; SV *p_data; SV *p_type; class = newSVpv(PERL_CLASS_VALUE, 0); p_data = valdata(val); p_type = newSViv(val->type); retval = perlvdb_perlmethod(class, PERL_CONSTRUCTOR_NAME, p_type, p_data, NULL, NULL); return retval; } SV *pair2perlpair(db_key_t key, db_val_t* val) { SV* retval; SV *class; SV *p_key; SV *p_type; SV *p_data; class = newSVpv(PERL_CLASS_PAIR, 0); p_key = newSVpv(key->s, key->len); p_type = newSViv(val->type); p_data = valdata(val); retval = perlvdb_perlmethod(class, PERL_CONSTRUCTOR_NAME, p_key, p_type, p_data, NULL); SvREFCNT_dec(class); return retval; } SV *cond2perlcond(db_key_t key, db_op_t op, db_val_t* val) { SV* retval; SV *class; SV *p_key; SV *p_op; SV *p_type; SV *p_data; ENTER; SAVETMPS; class = newSVpv(PERL_CLASS_REQCOND, 0); p_key = newSVpv(key->s, key->len); p_op = newSVpv(op, strlen(op)); p_type = newSViv(val->type); p_data = valdata(val); retval = perlvdb_perlmethod(sv_2mortal(class), PERL_CONSTRUCTOR_NAME, sv_2mortal(p_key), sv_2mortal(p_op), sv_2mortal(p_type), sv_2mortal(p_data)); FREETMPS; LEAVE; return retval; } int perlresult2dbres(SV *perlres, db_res_t **r) { HV * result = NULL; SV *colarrayref = NULL; AV *colarray = NULL; SV *acol = NULL; int colcount = 0; SV *rowarrayref = NULL; AV *rowarray = NULL; int rowcount = 0; SV *arowref = NULL; AV *arow = NULL; int arowlen = 0; SV *aelement = NULL; SV *atypesv = 0; int atype = 0; SV *aval = NULL; char *charbuf; char *currentstring; int i, j; int retval = 0; STRLEN len; SV *d1; /* helper variables */ /*db_val_t cur_val;*/ /* Abbreviation in "switch" below. The currently modified db result value. */ if (!(SvROK(perlres) && (sv_derived_from(perlres, "OpenSIPS::VDB::Result")))) { goto error; } result = (HV*)SvRV(perlres); /* Memory allocation for C side result structure */ *r = db_new_result(); /* Fetch column definitions */ colarrayref = *hv_fetchs(result, PERL_VDB_COLDEFSMETHOD, 0); /* colarrayref = perlvdb_perlmethod(perlres, PERL_VDB_COLDEFSMETHOD, NULL, NULL, NULL, NULL); */ if (!(SvROK(colarrayref))) goto error; colarray = (AV *)SvRV(colarrayref); /* SvREFCNT_dec(colarray); */ if (!(SvTYPE(colarray) == SVt_PVAV)) goto error; colcount = av_len(colarray) + 1; RES_COL_N(*r) = colcount; db_allocate_columns(*r, colcount); /* reverse direction, as elements are removed by "SvREFCNT_dec" */ for (i = colcount-1; i >= 0; i--) { acol = *av_fetch(colarray, i, 0); d1 = perlvdb_perlmethod(acol, PERL_VDB_TYPEMETHOD, NULL, NULL, NULL, NULL); if (!SvIOK(d1)) goto error; (*r)->col.types[i] = SvIV(d1); SvREFCNT_dec(d1); d1 = perlvdb_perlmethod(acol, PERL_VDB_NAMEMETHOD, NULL, NULL, NULL, NULL); if (!SvPOK(d1)) goto error; currentstring = SvPV(d1, len); charbuf = pkg_malloc(len+1); /* Column names buffers are freed in the perlvdb free function */ strncpy(charbuf, currentstring, len+1); (*r)->col.names[i]->s = charbuf; (*r)->col.names[i]->len = strlen(charbuf); SvREFCNT_dec(d1); } if(hv_exists(result, "rows", 4)){ rowarrayref =(SV*) hv_fetchs(result, "rows", 0); }else{ (*r)->n = 0; (*r)->res_rows = 0; (*r)->last_row = 0; goto end; } if(rowarrayref){ rowarrayref = *((SV**)rowarrayref); }else{ (*r)->n = 0; (*r)->res_rows = 0; (*r)->last_row = 0; goto end; } if (!(SvROK(rowarrayref))) { /* Empty result set */ (*r)->n = 0; (*r)->res_rows = 0; (*r)->last_row = 0; goto end; } rowarray = (AV *)SvRV(rowarrayref); if (!(SvTYPE(rowarray) == SVt_PVAV)) goto error; rowcount = av_len(rowarray) + 1; (*r)->n = rowcount; (*r)->res_rows = rowcount; (*r)->last_row = rowcount; db_allocate_rows(*r, rowcount); /* (rows * (sizeof(db_row_t) + sizeof(db_val_t) * RES_COL_N(_res)) */ /* LM_DBG("We got %d rows each row requres %d bytes because the row struct is %d and" "the values in that row take up %d. That is %d values each size is %d\n", rowcount, sizeof(db_row_t) + sizeof(db_val_t) * RES_COL_N(*r), sizeof(db_row_t), sizeof(db_val_t) * RES_COL_N(*r), RES_COL_N(*r), sizeof(db_val_t)); */ for (i = 0; i < rowcount; i++) { arowref = *av_fetch(rowarray, i, 0); if (!SvROK(arowref)) goto error; arow = (AV *)SvRV(arowref); if (!(SvTYPE(colarray) == SVt_PVAV)) goto error; arowlen = av_len(arow) + 1; (*r)->rows[i].n = arowlen; for (j = 0; j < arowlen; j++) { aelement = *av_fetch(arow, j, 0); #define cur_val (((*r)->rows)[i].values)[j] /*cur_val = (((*r)->rows)[i].values)[j];*/ /* cur_val is just an "abbreviation" */ if (!(sv_isobject(aelement) && sv_derived_from(aelement, PERL_CLASS_VALUE))) { cur_val.nul = 1; continue; } atypesv = *hv_fetchs((HV*)SvRV(aelement),PERL_VDB_TYPEMETHOD,0); /*aelement->{type} */ atype = SvIV(atypesv); /*atypesv = perlvdb_perlmethod(aelement, PERL_VDB_TYPEMETHOD, NULL, NULL, NULL, NULL);*/ aval = perlvdb_perlmethod(aelement, PERL_VDB_DATAMETHOD, NULL, NULL, NULL, NULL); (*r)->rows[i].values[j].type = atype; /* SvREFCNT_dec(atypesv); */ if (!SvOK(aval)) { cur_val.nul = 1; } else { switch (atype) { case DB_INT: cur_val.val.int_val = SvIV(aval); cur_val.nul = 0; break; case DB_DOUBLE: cur_val.val.double_val = SvNV(aval); cur_val.nul = 0; break; case DB_STRING: case DB_STR: /* We dont support DB_STR for now. * Set DB_STRING instead */ cur_val.type = DB_STRING; currentstring = SvPV(aval, len); charbuf = pkg_malloc(len+1); strncpy(charbuf, currentstring, len+1); cur_val.val.string_val = charbuf; cur_val.nul = 0; break; case DB_DATETIME: cur_val.val.time_val = (time_t)SvIV(aval); cur_val.nul = 0; break; case DB_BLOB: currentstring = SvPV(aval, len); charbuf = pkg_malloc(len+1); strncpy(charbuf, currentstring, len+1); cur_val.val.blob_val.s = charbuf; cur_val.val.blob_val.len = len; cur_val.nul = 0; break; case DB_BITMAP: cur_val.val.bitmap_val = SvIV(aval); cur_val.nul = 0; break; default: LM_CRIT("cannot handle this data type.\n"); return -1; break; } } SvREFCNT_dec(aval); } } end: return retval; error: LM_CRIT("broken result set. Exiting, leaving OpenSIPS in unknown state.\n"); return -1; } opensips-2.2.2/modules/db_perlvdb/perlvdb_conv.h000066400000000000000000000036171300170765700220010ustar00rootroot00000000000000/* * Perl virtual database module interface * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PERLVDB_CONV_H #define _PERLVDB_CONV_H #include "../../db/db_op.h" #include "../../db/db_val.h" #include "../../db/db_key.h" #include "perlvdb.h" #include #define PERL_CLASS_VALUE "OpenSIPS::VDB::Value" #define PERL_CLASS_PAIR "OpenSIPS::VDB::Pair" #define PERL_CLASS_REQCOND "OpenSIPS::VDB::ReqCond" #define PERL_CONSTRUCTOR_NAME "new" /* Converts a set of pairs to perl SVs. * For insert, and update (second half) */ AV *pairs2perlarray(db_key_t* keys, db_val_t* vals, int n); /* Converts a set of cond's to perl SVs. * For delete, update (first half), query */ AV *conds2perlarray(db_key_t* keys, db_op_t* ops, db_val_t* vals, int n); /* Converts a set of key names to a perl array. * Needed in query. */ AV *keys2perlarray(db_key_t* keys, int n); SV *val2perlval(db_val_t* val); SV *pair2perlpair(db_key_t key, db_val_t* val); SV *cond2perlcond(db_key_t key, db_op_t op, db_val_t* val); int perlresult2dbres(SV *perlres, db_res_t **r); #endif /* _PERLVDB_CONV_H */ opensips-2.2.2/modules/db_perlvdb/perlvdb_oohelpers.c000066400000000000000000000040211300170765700230150ustar00rootroot00000000000000/* * Perl virtual database module interface * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "perlvdb_oohelpers.h" #include "../../mem/mem.h" SV *perlvdb_perlmethod(SV *class, const char* method, SV *param1, SV *param2, SV *param3, SV *param4) { I32 res; SV *retval = NULL; dSP; ENTER; SAVETMPS; PUSHMARK(SP); /* passed stack: * class, and optionally parameters */ XPUSHs(class); if (param1) { XPUSHs(param1); } if (param2) { XPUSHs(param2); } if (param3) { XPUSHs(param3); } if (param4) { XPUSHs(param4); } PUTBACK; res = call_method(method, G_SCALAR | G_EVAL); SPAGAIN; if (res == 0) { /* should never happened - G_EVAL and G_SCALAR specified */ retval = &PL_sv_undef; } else if (res == 1) { /* This is the only return from call_method with G_SCALAR | G_EVAL */ retval = POPs; } else { /* More than one result in Scalar context??? */ LM_CRIT("got more than one result from scalar method!"); while (res--) { /* Try to clean stack. This should never happen anyway.*/ retval = POPs; } } SvREFCNT_inc(retval); PUTBACK; FREETMPS; LEAVE; return retval; } opensips-2.2.2/modules/db_perlvdb/perlvdb_oohelpers.h000066400000000000000000000022501300170765700230240ustar00rootroot00000000000000/* * Perl virtual database module interface * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PERLVDB_OOHELPERS_H #define _PERLVDB_OOHELPERS_H #include "perlvdb.h" SV *perlvdb_perlmethod(SV *ref, const char* method, SV *param1, SV *param2, SV *param3, SV *param4); #endif /* _PERLVDB_OOHELPERS_H */ opensips-2.2.2/modules/db_perlvdb/perlvdbfunc.c000066400000000000000000000202131300170765700216120ustar00rootroot00000000000000/* * Perl virtual database module interface * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include "perlvdb.h" #include "perlvdbfunc.h" #include "../../str.h" /* * Simple conversion IV -> int * including decreasing ref cnt */ static inline long IV2int(SV *in) { int ret = -1; if (SvOK(in)) { if (SvIOK(in)) { ret = SvIV(in); } SvREFCNT_dec(in); } return ret; } /* * Returns the class part of the URI */ str *parseurl(const str* url) { static str cn; cn.s = q_memchr(url->s,':',url->len); if (cn.s && ((cn.s+1)<(url->s+url->len)) ) { cn.s++; cn.len = url->len - (cn.s-url->s); return &cn; } return NULL; } SV *newvdbobj(const str* cn) { SV* obj; SV *class; class = newSVpvn(cn->s, cn->len); obj = perlvdb_perlmethod(class, PERL_CONSTRUCTOR_NAME, NULL, NULL, NULL, NULL); return obj; } SV *getobj(db_con_t *con) { return ((SV*)CON_TAIL(con)); } /* * Checks whether the passed SV is a valid VDB object: * - not null * - not undef * - an object * - derived from OpenSIPS::VDB */ int checkobj(SV* obj) { if (obj != NULL) { if (obj != &PL_sv_undef) { if (sv_isobject(obj)) { if (sv_derived_from(obj, PERL_VDB_BASECLASS)) { return 1; } } } } return 0; } /* * Initialize database module * No function should be called before this */ db_con_t* perlvdb_db_init(const str* url) { db_con_t* res; str *cn; SV *obj = NULL; int consize = sizeof(db_con_t) + sizeof(SV); if (!url || !url->s | !url->len) { LM_ERR("invalid parameter value\n"); return NULL; } cn = parseurl(url); if (!cn) { LM_ERR("invalid perl vdb url.\n"); return NULL; } obj = newvdbobj(cn); if (!checkobj(obj)) { LM_ERR("could not initialize module. Not inheriting from %s?\n", PERL_VDB_BASECLASS); return NULL; } res = pkg_malloc(consize); if (!res) { LM_ERR("no pkg memory left\n"); return NULL; } memset(res, 0, consize); CON_TAIL(res) = (unsigned int)(unsigned long)obj; return res; } /* * Store name of table that will be used by * subsequent database functions */ int perlvdb_use_table(db_con_t* h, const str* t) { SV *ret; SV *table; int res = -1; if (!h || !t || !t->s) { LM_ERR("invalid parameter value\n"); return -1; } table = newSVpv(t->s, t->len); ret = perlvdb_perlmethod(getobj(h), PERL_VDB_USETABLEMETHOD, table, NULL, NULL, NULL); SvREFCNT_dec(table); res = IV2int(ret); return res; } void perlvdb_db_close(db_con_t* h) { if (!h) { LM_ERR("invalid parameter value\n"); return; } pkg_free(h); } /* * Insert a row into specified table * h: structure representing database connection * k: key names * v: values of the keys * n: number of key=value pairs */ int perlvdb_db_insertreplace(db_con_t* h, db_key_t* k, db_val_t* v, int n, char *insertreplace) { AV *arr; SV *arrref; SV *ret; arr = pairs2perlarray(k, v, n); arrref = newRV_noinc((SV*)arr); ret = perlvdb_perlmethod(getobj(h), insertreplace, arrref, NULL, NULL, NULL); av_undef(arr); return IV2int(ret); } int perlvdb_db_insert(db_con_t* h, db_key_t* k, db_val_t* v, int n) { return perlvdb_db_insertreplace(h, k, v, n, PERL_VDB_INSERTMETHOD); } /* * Just like insert, but replace the row if it exists */ int perlvdb_db_replace(db_con_t* h, db_key_t* k, db_val_t* v, int n) { return perlvdb_db_insertreplace(h, k, v, n, PERL_VDB_REPLACEMETHOD); } /* * Delete a row from the specified table * h: structure representing database connection * k: key names * o: operators * v: values of the keys that must match * n: number of key=value pairs */ int perlvdb_db_delete(db_con_t* h, db_key_t* k, db_op_t* o, db_val_t* v, int n) { AV *arr; SV *arrref; SV *ret; arr = conds2perlarray(k, o, v, n); arrref = newRV_noinc((SV*)arr); ret = perlvdb_perlmethod(getobj(h), PERL_VDB_DELETEMETHOD, arrref, NULL, NULL, NULL); av_undef(arr); return IV2int(ret); } /* * Update some rows in the specified table * _h: structure representing database connection * _k: key names * _o: operators * _v: values of the keys that must match * _uk: updated columns * _uv: updated values of the columns * _n: number of key=value pairs * _un: number of columns to update */ int perlvdb_db_update(db_con_t* h, db_key_t* k, db_op_t* o, db_val_t* v, db_key_t* uk, db_val_t* uv, int n, int un) { AV *condarr; AV *updatearr; SV *condarrref; SV *updatearrref; SV *ret; condarr = conds2perlarray(k, o, v, n); updatearr = pairs2perlarray(uk, uv, un); condarrref = newRV_noinc((SV*)condarr); updatearrref = newRV_noinc((SV*)updatearr); ret = perlvdb_perlmethod(getobj(h), PERL_VDB_UPDATEMETHOD, condarrref, updatearrref, NULL, NULL); av_undef(condarr); av_undef(updatearr); return IV2int(ret); } /* * Query table for specified rows * h: structure representing database connection * k: key names * op: operators * v: values of the keys that must match * c: column names to return * n: number of key=values pairs to compare * nc: number of columns to return * o: order by the specified column */ int perlvdb_db_query(db_con_t* h, db_key_t* k, db_op_t* op, db_val_t* v, db_key_t* c, int n, int nc, db_key_t o, db_res_t** r) { AV *condarr; AV *retkeysarr; SV *order; SV *condarrref; SV *retkeysref; SV *resultset; int retval = 0; int i; /* Create parameter set */ condarr = conds2perlarray(k, op, v, n); retkeysarr = keys2perlarray(c, nc); if (o) order = newSVpv(o->s, o->len); else order = &PL_sv_undef; condarrref = newRV_noinc((SV*)condarr); retkeysref = newRV_noinc((SV*)retkeysarr); /* Call perl method */ resultset = perlvdb_perlmethod(getobj(h), PERL_VDB_QUERYMETHOD, condarrref, retkeysref, order, NULL); SvREFCNT_dec(condarrref); SvREFCNT_dec(retkeysref); if(SvOK(order)) SvREFCNT_dec(order); /* Transform perl result set to OpenSIPS result set */ if (!resultset) { /* No results. */ retval = -1; } else { if (sv_isa(resultset, "OpenSIPS::VDB::Result")) { retval = perlresult2dbres(resultset, r); /* Nested refs are decreased/deleted inside the routine */ SvREFCNT_dec(resultset); } else { LM_ERR("invalid result set retrieved from perl call.\n"); retval = -1; } } return retval; } /* * Release a result set from memory */ int perlvdb_db_free_result(db_con_t* _h, db_res_t* _r) { int i,j; SV* temp; /* free result set * use the order of allocation * first free values */ if(_r){ /* for each row */ for(i=0; i < RES_ROW_N(_r); i++){ /* for each column in row i */ for(j=0; j < RES_ROWS(_r)[i].n; j++){ switch ( (RES_ROWS(_r)[i].values)[j].type ) { /* the type of a value j in row i */ case DB_STRING: case DB_STR: pkg_free((RES_ROWS(_r)[i].values)[j].val.str_val.s); break; case DB_BLOB: pkg_free((RES_ROWS(_r)[i].values)[j].val.blob_val.s) ; break; case DB_INT: case DB_BIGINT: case DB_DOUBLE: case DB_BITMAP: case DB_DATETIME: break; } } /* for each column in row i*/ } /* for each row */ for(i=0; i< RES_COL_N(_r); i++){ pkg_free(RES_NAMES(_r)[i]->s); } db_free_result(_r); } return 0; } opensips-2.2.2/modules/db_perlvdb/perlvdbfunc.h000066400000000000000000000037411300170765700216260ustar00rootroot00000000000000/* * Perl virtual database module interface * * Copyright (C) 2007 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PERLVDBFUNC_H #define _PERLVDBFUNC_H #include "../../db/db_val.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../str.h" /* * Initialize/close database module * No function should be called before/after this */ db_con_t* perlvdb_db_init(const str* _url); void perlvdb_db_close(db_con_t* h); /* * Store name of table that will be used by * subsequent database functions */ int perlvdb_use_table(db_con_t* h, const str* t); int perlvdb_db_insert(db_con_t* h, db_key_t* k, db_val_t* v, int n); int perlvdb_db_replace(db_con_t* h, db_key_t* k, db_val_t* v, int n); int perlvdb_db_delete(db_con_t* h, db_key_t* k, db_op_t* o, db_val_t* v, int n); int perlvdb_db_update(db_con_t* h, db_key_t* k, db_op_t* o, db_val_t* v, db_key_t* uk, db_val_t* uv, int n, int un); int perlvdb_db_query(db_con_t* h, db_key_t* k, db_op_t* op, db_val_t* v, db_key_t* c, int n, int nc, db_key_t o, db_res_t** r); int perlvdb_db_free_result(db_con_t* _h, db_res_t* _r); #endif /* _PERLVDBFUNC_H */ opensips-2.2.2/modules/db_postgres/000077500000000000000000000000001300170765700173465ustar00rootroot00000000000000opensips-2.2.2/modules/db_postgres/Makefile000066400000000000000000000035431300170765700210130ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=db_postgres.so SCRIPTS_DIR?=../../scripts/ # set CROSS_COMPILE to true if you want to skip # the autodetection # CROSS_COMPILE=true ifeq ($(CROSS_COMPILE),) HAS_PGCFG=$(shell if which pg_config >/dev/null 2>/dev/null;then echo YES; fi) endif ifeq ($(HAS_PGCFG),YES) # use autodetection DEFS += -I$(shell pg_config --includedir) LIBS = -L$(shell pg_config --libdir) -lpq else # use standard know paths # libpq-fe.h locations DEFS +=-I$(LOCALBASE)/include -I$(LOCALBASE)/pgsql/include \ -I$(SYSBASE)/include/pgsql -I$(SYSBASE)/include/postgresql \ -I$(SYSBASE)/include/postgresql/8.0 -I$(SYSBASE)/postgres/8.4/include LIBS=-L$(LOCALBASE)/lib -L$(LOCALBASE)/pgsql/lib -L$(LOCALBASE)/lib/pgsql \ -lpq endif include ../../Makefile.modules install_module_custom: mkdir -p $(modules_prefix)/$(lib_dir)/opensipsctl ; \ sed -e "s#/usr/local/sbin#$(bin-target)#g" \ < $(SCRIPTS_DIR)/opensipsctl.pgsql > /tmp/opensipsctl.pgsql ; \ $(INSTALL_CFG) /tmp/opensipsctl.pgsql \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.pgsql ; \ rm -fr /tmp/opensipsctl.pgsql ; \ sed -e "s#/usr/local/share/opensips#$(data_target)#g" \ < $(SCRIPTS_DIR)/opensipsdbctl.pgsql > /tmp/opensipsdbctl.pgsql ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsdbctl.pgsql ; \ $(INSTALL_CFG) /tmp/opensipsdbctl.pgsql $(modules_prefix)/$(lib_dir)/opensipsctl/ ; \ rm -fr /tmp/opensipsdbctl.pgsql ; \ mkdir -p $(data_prefix)/$(data_dir)/postgres ; \ for FILE in $(wildcard $(SCRIPTS_DIR)/postgres/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/postgres/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/postgres/`basename "$$FILE"` ; \ fi ;\ done ; \ opensips-2.2.2/modules/db_postgres/README000066400000000000000000000036321300170765700202320ustar00rootroot00000000000000postgres Module Greg Fausak August.net Edited by Greg Fausak Copyright © 2003 Greg Fausak Revision History Revision $Revision: 5898 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. exec_query_threshold (integer) 1.3.2. max_db_queries (integer) 1.4. Exported Functions 1.5. Installation and Running List of Examples 1.1. Set exec_query_threshold parameter 1.2. Set max_db_queries parameter Chapter 1. Admin Guide 1.1. Overview Module description 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * PostgreSQL library - e.g., libpq5. * PostgreSQL devel library - to compile the module (e.g., libpq-dev). 1.3. Exported Parameters 1.3.1. exec_query_threshold (integer) If queries take longer than 'exec_query_threshold' microseconds, warning messages will be written to logging facility. Default value is 0 - disabled. Example 1.1. Set exec_query_threshold parameter ... modparam("db_postgres", "exec_query_threshold", 60000) ... 1.3.2. max_db_queries (integer) The maximum number of database queries to be executed. If this parameter is set improperly, it is set to default value. Default value is 2. Example 1.2. Set max_db_queries parameter ... modparam("db_postgres", "max_db_queries", 2) ... 1.4. Exported Functions NONE 1.5. Installation and Running Notes about installation and running. opensips-2.2.2/modules/db_postgres/db_postgres.c000066400000000000000000000063551300170765700220360ustar00rootroot00000000000000/* * Postgres module interface * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) */ #include #include "../../sr_module.h" #include "../../db/db_con.h" #include "../../db/db.h" #include "../../db/db_cap.h" #include "dbase.h" int db_postgres_exec_query_threshold = 0; /* Warning in case DB query takes too long disabled by default*/ int max_db_queries = 2; int db_postgres_bind_api(const str* mod, db_func_t *dbb); static int mod_init(void); /* * PostgreSQL database module interface */ static cmd_export_t cmds[]={ {"db_bind_api", (cmd_function)db_postgres_bind_api, 0, 0, 0, 0}, {0,0,0,0,0,0} }; /* * Exported parameters */ static param_export_t params[] = { {"exec_query_threshold", INT_PARAM, &db_postgres_exec_query_threshold}, {"max_db_queries", INT_PARAM, &max_db_queries}, {0, 0, 0} }; struct module_exports exports = { "db_postgres", MOD_TYPE_SQLDB, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* module functions */ 0, /* module async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0 /* per-child init function */ }; static int mod_init(void) { LM_INFO("initializing...\n"); if(max_db_queries < 1){ LM_WARN("Invalid number for max_db_queries\n"); max_db_queries = 2; } return 0; } int db_postgres_bind_api(const str* mod, db_func_t *dbb) { if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->use_table = db_postgres_use_table; dbb->init = db_postgres_init; dbb->close = db_postgres_close; dbb->query = db_postgres_query; dbb->fetch_result = db_postgres_fetch_result; dbb->raw_query = db_postgres_raw_query; dbb->free_result = db_postgres_free_result; dbb->insert = db_postgres_insert; dbb->delete = db_postgres_delete; dbb->update = db_postgres_update; dbb->cap |= DB_CAP_MULTIPLE_INSERT; return 0; } opensips-2.2.2/modules/db_postgres/dbase.c000066400000000000000000000401371300170765700205750ustar00rootroot00000000000000/* * POSTGRES module, portions of this code were templated using * the mysql module, thus it's similarity. * * Copyright (C) 2003 August.Net Services, LLC * Copyright (C) 2006 Norman Brandinger * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --- * * History * ------- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford) * 2006-07-28 within pg_get_result(): added check to immediatly return of no * result set was returned added check to only execute * convert_result() if PGRES_TUPLES_OK added safety check to avoid * double pg_free_result() (norm) * 2006-08-07 Rewrote pg_get_result(). * Additional debugging lines have been placed through out the code. * Added Asynchronous Command Processing (PQsendQuery/PQgetResult) * instead of PQexec. this was done in preparation of adding FETCH * support. Note that PQexec returns a result pointer while * PQsendQuery does not. The result set pointer is obtained from * a call (or multiple calls) to PQgetResult. * Removed transaction processing calls (BEGIN/COMMIT/ROLLBACK) as * they added uneeded overhead. Klaus' testing showed in excess of * 1ms gain by removing each command. In addition, OpenSIPS only * issues single queries and is not, at this time transaction aware. * The transaction processing routines have been left in place * should this support be needed in the future. * Updated logic in pg_query / pg_raw_query to accept a (0) result * set (_r) parameter. In this case, control is returned * immediately after submitting the query and no call to * pg_get_results() is performed. This is a requirement for * FETCH support. (norm) * 2006-10-27 Added fetch support (norm) * Removed dependency on aug_* memory routines (norm) * Added connection pooling support (norm) * Standardized API routines to pg_* names (norm) * 2006-11-01 Updated pg_insert(), pg_delete(), pg_update() and * pg_get_result() to handle failed queries. Detailed warnings * along with the text of the failed query is now displayed in the * log. Callers of these routines can now assume that a non-zero * rc indicates the query failed and that remedial action may need * to be taken. (norm) */ #define MAXCOLUMNS 512 #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../db/db.h" #include "../../db/db_ut.h" #include "../../db/db_query.h" #include "../../db/db_insertq.h" #include "dbase.h" #include "pg_con.h" #include "val.h" #include "res.h" extern int db_postgres_exec_query_threshold; extern int max_db_queries; static int submit_func_called; static int free_query(const db_con_t* _con); /* ** pg_init initialize database for future queries ** ** Arguments : ** char *_url; sql database to open ** ** Returns : ** db_con_t * NULL upon error ** db_con_t * if successful ** ** Notes : ** pg_init must be called prior to any database functions. */ db_con_t *db_postgres_init(const str* _url) { return db_do_init(_url, (void*) db_postgres_new_connection); } /* ** pg_close last function to call when db is no longer needed ** ** Arguments : ** db_con_t * the connection to shut down, as supplied by pg_init() ** ** Returns : ** (void) ** ** Notes : ** All memory and resources are freed. */ void db_postgres_close(db_con_t* _h) { db_do_close(_h, db_postgres_free_connection); } /* ** submit_query run a query ** ** Arguments : ** db_con_t * as previously supplied by pg_init() ** char *_s the text query to run ** ** Returns : ** 0 upon success ** negative number upon failure */ static int db_postgres_submit_query(const db_con_t* _con, const str* _s) { int i,ret=0; ExecStatusType result; PGresult *res = NULL; struct timeval start; if(! _con || !_s || !_s->s) { LM_ERR("invalid parameter value\n"); return(-1); } submit_func_called = 1; /* this bit of nonsense in case our connection get screwed up */ switch(PQstatus(CON_CONNECTION(_con))) { case CONNECTION_OK: break; case CONNECTION_BAD: LM_DBG("connection reset\n"); PQreset(CON_CONNECTION(_con)); break; case CONNECTION_STARTED: case CONNECTION_MADE: case CONNECTION_AWAITING_RESPONSE: case CONNECTION_AUTH_OK: case CONNECTION_SETENV: case CONNECTION_SSL_STARTUP: case CONNECTION_NEEDED: default: LM_ERR("%p PQstatus(%s) invalid: %.*s\n", _con, PQerrorMessage(CON_CONNECTION(_con)), _s->len, _s->s); return -1; } for (i=0;is); stop_expire_timer(start,db_postgres_exec_query_threshold,"pgsql query",_s->s,_s->len,0); /* exec the query */ if (ret) { LM_DBG("%p PQsendQuery(%.*s)\n", _con, _s->len, _s->s); while (1) { if ((res = PQgetResult(CON_CONNECTION(_con)))) { CON_RESULT(_con) = res; } else { break; } } result = PQresultStatus(CON_RESULT(_con)); if(result==PGRES_FATAL_ERROR) goto reconnect; else return 0; } else { reconnect: /* reconnection attempt - if this is the case */ LM_DBG("%p PQsendQuery failed: %s Query: %.*s\n", _con, PQerrorMessage(CON_CONNECTION(_con)), _s->len, _s->s); if(PQstatus(CON_CONNECTION(_con))!=CONNECTION_OK) { LM_DBG("connection reset\n"); PQreset(CON_CONNECTION(_con)); } else { /* failure not due to connection loss - no point in retrying */ if(CON_RESULT(_con)) { free_query(_con); } break; } } } LM_ERR("%p PQsendQuery Error: %s Query: %.*s\n", _con, PQerrorMessage(CON_CONNECTION(_con)), _s->len, _s->s); return -1; } /* * * pg_fetch_result: Gets a partial result set. * */ int db_postgres_fetch_result(const db_con_t* _con, db_res_t** _res, const int nrows) { int rows; ExecStatusType pqresult; if (!_con || !_res || nrows < 0) { LM_ERR("invalid parameter value\n"); return -1; } /* exit if the fetch count is zero */ if (nrows == 0) { if (*_res) db_free_result(*_res); *_res = 0; return 0; } if (*_res == NULL) { /* Allocate a new result structure */ *_res = db_new_result(); pqresult = PQresultStatus(CON_RESULT(_con)); LM_DBG("%p PQresultStatus(%s) PQgetResult(%p)\n", _con, PQresStatus(pqresult), CON_RESULT(_con)); switch(pqresult) { case PGRES_COMMAND_OK: /* Successful completion of a command returning no data * (such as INSERT or UPDATE). */ return 0; case PGRES_TUPLES_OK: /* Successful completion of a command returning data * (such as a SELECT or SHOW). */ if (db_postgres_get_columns(_con, *_res) < 0) { LM_ERR("failed to get column names\n"); return -2; } break; case PGRES_FATAL_ERROR: LM_ERR("%p - invalid query, execution aborted\n", _con); LM_ERR("%p - PQresultStatus(%s)\n",_con,PQresStatus(pqresult)); LM_ERR("%p: %s\n",_con,PQresultErrorMessage(CON_RESULT(_con))); if (*_res) { db_free_result(*_res); *_res = 0; } return -3; case PGRES_EMPTY_QUERY: /* notice or warning */ case PGRES_NONFATAL_ERROR: /* status for COPY command, not used */ case PGRES_COPY_OUT: case PGRES_COPY_IN: /* unexpected response */ case PGRES_BAD_RESPONSE: default: LM_ERR("%p - probable invalid query\n", _con); LM_ERR("%p - PQresultStatus(%s)\n",_con,PQresStatus(pqresult)); LM_ERR("%p: %s\n",_con,PQresultErrorMessage(CON_RESULT(_con))); if (*_res) db_free_result(*_res); *_res = 0; return -4; } } else { if(RES_ROWS(*_res) != NULL) { db_free_rows(*_res); } RES_ROWS(*_res) = 0; RES_ROW_N(*_res) = 0; } /* Get the number of rows (tuples) in the query result. */ RES_NUM_ROWS(*_res) = PQntuples(CON_RESULT(_con)); /* determine the number of rows remaining to be processed */ rows = RES_NUM_ROWS(*_res) - RES_LAST_ROW(*_res); /* If there aren't any more rows left to process, exit */ if (rows <= 0) return 0; /* if the fetch count is less than the remaining rows to process */ /* set the number of rows to process (during this call) equal to * the fetch count */ if (nrows < rows) rows = nrows; RES_ROW_N(*_res) = rows; LM_DBG("converting row %d of %d count %d\n", RES_LAST_ROW(*_res), RES_NUM_ROWS(*_res), RES_ROW_N(*_res)); if (db_postgres_convert_rows(_con, *_res) < 0) { LM_ERR("failed to convert rows\n"); if (*_res) db_free_result(*_res); *_res = 0; return -3; } /* update the total number of rows processed */ RES_LAST_ROW(*_res) += rows; return 0; } /* ** free_query clear the db channel and clear any old query result status ** ** Arguments : ** db_con_t * as previously supplied by pg_init() ** ** Returns : ** 0 upon success ** negative number upon failure */ static int free_query(const db_con_t* _con) { if(CON_RESULT(_con)) { LM_DBG("PQclear(%p) result set\n", CON_RESULT(_con)); PQclear(CON_RESULT(_con)); CON_RESULT(_con) = 0; } return 0; } /* ** db_free_result free the query and free the result memory ** ** Arguments : ** db_con_t * as previously supplied by pg_init() ** db_res_t * the result of a query ** ** Returns : ** 0 upon success ** negative number upon failure */ int db_postgres_free_result(db_con_t* _con, db_res_t* _r) { free_query(_con); if (_r) db_free_result(_r); _r = 0; return 0; } /* * Query table for specified rows * _con: structure representing database connection * _k: key names * _op: operators * _v: values of the keys that must match * _c: column names to return * _n: nmber of key=values pairs to compare * _nc: number of columns to return * _o: order by the specified column */ int db_postgres_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r) { CON_RESET_CURR_PS(_h); /* no prepared statements support */ return db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r, db_postgres_val2str, db_postgres_submit_query, db_postgres_store_result); } /* * Execute a raw SQL query */ int db_postgres_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r) { CON_RESET_CURR_PS(_h); /* no prepared statements support */ return db_do_raw_query(_h, _s, _r, db_postgres_submit_query, db_postgres_store_result); } /* * Retrieve result set * * Input: * db_con_t* _con Structure representing the database connection * db_res_t** _r pointer to a structure represending the result set * * Output: * return 0: If the status of the last command produced a result set and, * If the result set contains data or the convert_result() routine * completed successfully. * * return < 0: If the status of the last command was not handled or if the * convert_result() returned an error. * * Notes: * A new result structure is allocated on every call to this routine. * * If this routine returns 0, it is the callers responsbility to free the * result structure. If this routine returns < 0, then the result structure * is freed before returning to the caller. * */ int db_postgres_store_result(const db_con_t* _con, db_res_t** _r) { ExecStatusType pqresult; int rc = 0; *_r = db_new_result(); if (*_r==NULL) { LM_ERR("failed to init new result\n"); rc = -1; goto done; } pqresult = PQresultStatus(CON_RESULT(_con)); LM_DBG("%p PQresultStatus(%s) PQgetResult(%p)\n", _con, PQresStatus(pqresult), CON_RESULT(_con)); switch(pqresult) { case PGRES_COMMAND_OK: /* Successful completion of a command returning no data * (such as INSERT or UPDATE). */ rc = 0; break; case PGRES_TUPLES_OK: /* Successful completion of a command returning data * (such as a SELECT or SHOW). */ if (db_postgres_convert_result(_con, *_r) < 0) { LM_ERR("%p Error returned from convert_result()\n", _con); db_free_result(*_r); *_r = 0; rc = -4; break; } rc = 0; break; /* query failed */ case PGRES_FATAL_ERROR: LM_ERR("%p - invalid query, execution aborted\n", _con); LM_ERR("%p: %s\n", _con, PQresStatus(pqresult)); LM_ERR("%p: %s\n", _con, PQresultErrorMessage(CON_RESULT(_con))); db_free_result(*_r); *_r = 0; rc = -3; break; case PGRES_EMPTY_QUERY: /* notice or warning */ case PGRES_NONFATAL_ERROR: /* status for COPY command, not used */ case PGRES_COPY_OUT: case PGRES_COPY_IN: /* unexpected response */ case PGRES_BAD_RESPONSE: default: LM_ERR("%p Probable invalid query\n", _con); LM_ERR("%p: %s\n", _con, PQresStatus(pqresult)); LM_ERR("%p: %s\n", _con, PQresultErrorMessage(CON_RESULT(_con))); db_free_result(*_r); *_r = 0; rc = -4; break; } done: free_query(_con); return (rc); } /* * Insert a row into specified table * _con: structure representing database connection * _k: key names * _v: values of the keys * _n: number of key=value pairs */ int db_postgres_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { db_res_t* _r = NULL; CON_RESET_CURR_PS(_h); /* no prepared statements support */ int tmp = db_do_insert(_h, _k, _v, _n, db_postgres_val2str, db_postgres_submit_query); if (submit_func_called) { /* finish the async query, * otherwise the next query will not complete */ /* only call this if the DB API has effectively called * our submit_query function * * in case of insert queueing, * it may postpone calling the insert func until * enough rows have piled up */ if (db_postgres_store_result(_h, &_r) != 0) LM_WARN("unexpected result returned\n"); submit_func_called = 0; } if (_r) db_free_result(_r); if (CON_HAS_INSLIST(_h)) CON_RESET_INSLIST(_h); return tmp; } /* * Delete a row from the specified table * _con: structure representing database connection * _k: key names * _o: operators * _v: values of the keys that must match * _n: number of key=value pairs */ int db_postgres_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n) { db_res_t* _r = NULL; CON_RESET_CURR_PS(_h); /* no prepared statements support */ int tmp = db_do_delete(_h, _k, _o, _v, _n, db_postgres_val2str, db_postgres_submit_query); if (db_postgres_store_result(_h, &_r) != 0) LM_WARN("unexpected result returned"); if (_r) db_free_result(_r); return tmp; } /* * Update some rows in the specified table * _con: structure representing database connection * _k: key names * _o: operators * _v: values of the keys that must match * _uk: updated columns * _uv: updated values of the columns * _n: number of key=value pairs * _un: number of columns to update */ int db_postgres_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un) { db_res_t* _r = NULL; CON_RESET_CURR_PS(_h); /* no prepared statements support */ int tmp = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_postgres_val2str, db_postgres_submit_query); if (db_postgres_store_result(_h, &_r) != 0) LM_WARN("unexpected result returned"); if (_r) db_free_result(_r); return tmp; } /* * Store name of table that will be used by * subsequent database functions */ int db_postgres_use_table(db_con_t* _con, const str* _t) { return db_use_table(_con, _t); } opensips-2.2.2/modules/db_postgres/dbase.h000066400000000000000000000052661300170765700206060ustar00rootroot00000000000000/* * POSTGRES module, portions of this code were templated using * the mysql module, thus it's similarity. * * Copyright (C) 2003 August.Net Services, LLC * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --- * * History * ------- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford) * */ #ifndef DBASE_H #define DBASE_H #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" /** * Initialize database connection */ db_con_t* db_postgres_init(const str* _url); /** * Close a database connection */ void db_postgres_close(db_con_t* _h); /** * Return result of previous query */ int db_postgres_store_result(const db_con_t* _h, db_res_t** _r); /** * Free all memory allocated by get_result */ int db_postgres_free_result(db_con_t* _h, db_res_t* _r); /** * Do a query */ int db_postgres_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r); /** * Raw SQL query */ int db_postgres_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r); /** * Insert a row into table */ int db_postgres_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /** * Delete a row from table */ int db_postgres_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n); /** * Update a row in table */ int db_postgres_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un); /** * fetch rows from a result */ int db_postgres_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows); /** * Store name of table that will be used by * subsequent database functions */ int db_postgres_use_table(db_con_t* _h, const str* _t); #endif /* DBASE_H */ opensips-2.2.2/modules/db_postgres/doc/000077500000000000000000000000001300170765700201135ustar00rootroot00000000000000opensips-2.2.2/modules/db_postgres/doc/db_postgres.xml000066400000000000000000000022201300170765700231440ustar00rootroot00000000000000 %docentities; ]> postgres Module &osipsname; Greg Fausak August.net
greg@august.net
Greg Fausak
greg@august.net
2003 Greg Fausak $Revision: 5898 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/db_postgres/doc/db_postgres_admin.xml000066400000000000000000000044131300170765700243220ustar00rootroot00000000000000 &adminguide;
Overview Module description
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: PostgreSQL library - e.g., libpq5. PostgreSQL devel library - to compile the module (e.g., libpq-dev).
Exported Parameters
<varname>exec_query_threshold</varname> (integer) If queries take longer than 'exec_query_threshold' microseconds, warning messages will be written to logging facility. Default value is 0 - disabled. Set <varname>exec_query_threshold</varname> parameter ... modparam("db_postgres", "exec_query_threshold", 60000) ...
<varname>max_db_queries</varname> (integer) The maximum number of database queries to be executed. If this parameter is set improperly, it is set to default value. Default value is 2. Set <varname>max_db_queries</varname> parameter ... modparam("db_postgres", "max_db_queries", 2) ...
Exported Functions NONE
Installation and Running Notes about installation and running.
opensips-2.2.2/modules/db_postgres/pg_con.c000066400000000000000000000054201300170765700207600ustar00rootroot00000000000000/* * Copyright (C) 2001-2004 iptel.org * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pg_con.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../ut.h" #include #include /* * Create a new connection structure, * open the PostgreSQL connection and set reference count to 1 */ struct pg_con* db_postgres_new_connection(struct db_id* id) { struct pg_con* ptr; char *ports; LM_DBG("db_id = %p\n", id); if (!id) { LM_ERR("invalid db_id parameter value\n"); return 0; } ptr = (struct pg_con*)pkg_malloc(sizeof(struct pg_con)); if (!ptr) { LM_ERR("failed trying to allocated %lu bytes for connection structure." "\n", (unsigned long)sizeof(struct pg_con)); return 0; } LM_DBG("%p=pkg_malloc(%lu)\n", ptr, (unsigned long)sizeof(struct pg_con)); memset(ptr, 0, sizeof(struct pg_con)); ptr->ref = 1; if (id->port) { ports = int2str(id->port, 0); LM_DBG("opening connection: postgres://xxxx:xxxx@%s:%d/%s\n", ZSW(id->host), id->port, ZSW(id->database)); } else { ports = NULL; LM_DBG("opening connection: postgres://xxxx:xxxx@%s/%s\n", ZSW(id->host), ZSW(id->database)); } ptr->con = PQsetdbLogin(id->host, ports, NULL, NULL, id->database, id->username, id->password); LM_DBG("PQsetdbLogin(%p)\n", ptr->con); if( (ptr->con == 0) || (PQstatus(ptr->con) != CONNECTION_OK) ) { LM_ERR("%s\n", PQerrorMessage(ptr->con)); PQfinish(ptr->con); goto err; } ptr->connected = 1; ptr->timestamp = time(0); ptr->id = id; return ptr; err: if (ptr) { LM_ERR("cleaning up %p=pkg_free()\n", ptr); pkg_free(ptr); } return 0; } /* * Close the connection and release memory */ void db_postgres_free_connection(struct pool_con* con) { if (!con) return; struct pg_con * _c; _c = (struct pg_con*)con; if (_c->res) { LM_DBG("PQclear(%p)\n", _c->res); PQclear(_c->res); _c->res = 0; } if (_c->id) free_db_id(_c->id); if (_c->con) { LM_DBG("PQfinish(%p)\n", _c->con); PQfinish(_c->con); _c->con = 0; } LM_DBG("pkg_free(%p)\n", _c); pkg_free(_c); } opensips-2.2.2/modules/db_postgres/pg_con.h000066400000000000000000000053231300170765700207670ustar00rootroot00000000000000/* * POSTGRES module, portions of this code were templated using * the mysql module, thus it's similarity. * * Copyright (C) 2003 August.Net Services, LLC * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --- * * History * ------- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford) * */ #ifndef PG_CON_H #define PG_CON_H #include "../../db/db_pool.h" #include "../../db/db_id.h" #include #include /* * Postgres specific connection data */ struct pg_con { struct db_id* id; /**< Connection identifier */ unsigned int ref; /**< Reference count */ struct pool_con *async_pool; /**< Subpool of identical database handles */ int no_transfers; /**< Number of async queries to this backend */ struct db_transfer *transfers; /**< Array of ongoing async operations */ struct pool_con *next; /**< Next element in the pool (different db_id) */ int connected; char *sqlurl; /* the url we are connected to, all connection memory parents from this */ PGconn *con; /* this is the postgres connection */ PGresult *res; /* this is the current result */ char** row; /* Actual row in the result */ time_t timestamp; /* Timestamp of last query */ }; #define CON_SQLURL(db_con) (((struct pg_con*)((db_con)->tail))->sqlurl) #define CON_RESULT(db_con) (((struct pg_con*)((db_con)->tail))->res) #define CON_CONNECTION(db_con) (((struct pg_con*)((db_con)->tail))->con) #define CON_CONNECTED(db_con) (((struct pg_con*)((db_con)->tail))->connected) #define CON_ROW(db_con) (((struct pg_con*)((db_con)->tail))->row) #define CON_TIMESTAMP(db_con) (((struct pg_con*)((db_con)->tail))->timestamp) #define CON_ID(db_con) (((struct pg_con*)((db_con)->tail))->id) /* * Create a new connection structure, * open the PostgreSQL connection and set reference count to 1 */ struct pg_con* db_postgres_new_connection(struct db_id* id); /* * Close the connection and release memory */ void db_postgres_free_connection(struct pool_con* con); #endif /* PG_CON_H */ opensips-2.2.2/modules/db_postgres/pg_type.h000066400000000000000000000051251300170765700211710ustar00rootroot00000000000000/* * POSTGRES module, portions of this code were templated using * the mysql module, thus it's similarity. * * Copyright (C) 2003 August.Net Services, LLC * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford) * */ /* * OID definitions, copied from postgresql/catalog/pg_types.h. * It would be probably more correct to use the definitions from there. */ #define BOOLOID 16 #define BYTEAOID 17 #define CHAROID 18 #define NAMEOID 19 #define INT8OID 20 #define INT2OID 21 #define INT2VECTOROID 22 #define INT4OID 23 #define REGPROCOID 24 #define TEXTOID 25 #define OIDOID 26 #define TIDOID 27 #define XIDOID 28 #define CIDOID 29 #define OIDVECTOROID 30 #define JSONOID 114 #define POINTOID 600 #define LSEGOID 601 #define PATHOID 602 #define BOXOID 603 #define POLYGONOID 604 #define LINEOID 628 #define FLOAT4OID 700 #define FLOAT8OID 701 #define ABSTIMEOID 702 #define RELTIMEOID 703 #define TINTERVALOID 704 #define UNKNOWNOID 705 #define CIRCLEOID 718 #define CASHOID 790 #define MACADDROID 829 #define INETOID 869 #define CIDROID 650 #define ACLITEMOID 1033 #define BPCHAROID 1042 #define VARCHAROID 1043 #define DATEOID 1082 #define TIMEOID 1083 #define TIMESTAMPOID 1114 #define TIMESTAMPTZOID 1184 #define INTERVALOID 1186 #define TIMETZOID 1266 #define BITOID 1560 #define VARBITOID 1562 #define NUMERICOID 1700 #define REFCURSOROID 1790 #define REGPROCEDUREOID 2202 #define REGOPEROID 2203 #define REGOPERATOROID 2204 #define REGCLASSOID 2205 #define REGTYPEOID 2206 #define RECORDOID 2249 #define CSTRINGOID 2275 #define ANYOID 2276 #define ANYARRAYOID 2277 #define VOIDOID 2278 #define TRIGGEROID 2279 #define LANGUAGE_HANDLEROID 2280 #define INTERNALOID 2281 #define OPAQUEOID 2282 opensips-2.2.2/modules/db_postgres/res.c000066400000000000000000000242331300170765700203070ustar00rootroot00000000000000/* * POSTGRES module, portions of this code were templated using * the mysql module, thus it's similarity. * * Copyright (C) 2003 August.Net Services, LLC * Copyright (C) 2006 Norman Brandinger * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --- * * History * ------- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford) * * 2006-07-26 added BPCHAROID as a valid type for DB_STRING conversions * this removes the "unknown type 1042" log messages (norm) * * 2006-10-27 Added fetch support (norm) * Removed dependency on aug_* memory routines (norm) * Added connection pooling support (norm) * Standardized API routines to pg_* names (norm) * */ #include #include #include "../../db/db_id.h" #include "../../db/db_res.h" #include "../../db/db_con.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "res.h" #include "val.h" #include "pg_con.h" #include "pg_type.h" /** * Fill the result structure with data from the query */ int db_postgres_convert_result(const db_con_t* _h, db_res_t* _r) { if (!_h || !_r) { LM_ERR("invalid parameter value\n"); return -1; } if (db_postgres_get_columns(_h, _r) < 0) { LM_ERR("failed to get column names\n"); return -2; } if (db_postgres_convert_rows(_h, _r) < 0) { LM_ERR("failed to convert rows\n"); db_free_columns(_r); return -3; } return 0; } /** * Get and convert columns from a result set */ int db_postgres_get_columns(const db_con_t* _h, db_res_t* _r) { int col, datatype; if (!_h || !_r) { LM_ERR("invalid parameter value\n"); return -1; } /* Get the number of rows (tuples) in the query result. */ RES_ROW_N(_r) = PQntuples(CON_RESULT(_h)); /* Get the number of columns (fields) in each row of the query result. */ RES_COL_N(_r) = PQnfields(CON_RESULT(_h)); if (!RES_COL_N(_r)) { LM_DBG("no columns returned from the query\n"); return -2; } else { LM_DBG("%d columns returned from the query\n", RES_COL_N(_r)); } if (db_allocate_columns(_r, RES_COL_N(_r)) != 0) { LM_ERR("could not allocate columns\n"); return -3; } /* For each column both the name and the OID number of the * data type are saved. */ for(col = 0; col < RES_COL_N(_r); col++) { /* The pointer that is here returned is part of the result structure.*/ RES_NAMES(_r)[col]->s = PQfname(CON_RESULT(_h), col); RES_NAMES(_r)[col]->len = strlen(PQfname(CON_RESULT(_h), col)); LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_r)[col], col, RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s); /* get the datatype of the column */ switch(datatype = PQftype(CON_RESULT(_h),col)) { case INT2OID: case INT4OID: LM_DBG("use DB_INT result type\n"); RES_TYPES(_r)[col] = DB_INT; break; case INT8OID: LM_DBG("use DB_BIGINT result type\n"); RES_TYPES(_r)[col] = DB_BIGINT; break; case FLOAT4OID: case FLOAT8OID: case NUMERICOID: LM_DBG("use DB_DOUBLE result type\n"); RES_TYPES(_r)[col] = DB_DOUBLE; break; case DATEOID: case TIMESTAMPOID: case TIMESTAMPTZOID: LM_DBG("use DB_DATETIME result type\n"); RES_TYPES(_r)[col] = DB_DATETIME; break; case BOOLOID: case CHAROID: case VARCHAROID: case BPCHAROID: case TEXTOID: case JSONOID: LM_DBG("use DB_STRING result type\n"); RES_TYPES(_r)[col] = DB_STRING; break; case BYTEAOID: LM_DBG("use DB_BLOB result type\n"); RES_TYPES(_r)[col] = DB_BLOB; break; case BITOID: case VARBITOID: LM_DBG("use DB_BITMAP result type\n"); RES_TYPES(_r)[col] = DB_BITMAP; break; default: LM_WARN("unhandled data type column (%.*s) type id (%d), " "use DB_STRING as default\n", RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s, datatype); RES_TYPES(_r)[col] = DB_STRING; break; } } return 0; } /** * Convert rows from PostgreSQL to db API representation */ int db_postgres_convert_rows(const db_con_t* _h, db_res_t* _r) { char **row_buf, *s; int row, col, len; if (!_h || !_r) { LM_ERR("invalid parameter\n"); return -1; } if (!RES_ROW_N(_r)) { LM_DBG("no rows returned from the query\n"); RES_ROWS(_r) = 0; return 0; } /* Allocate an array of pointers per column to holds the string * representation */ len = sizeof(char *) * RES_COL_N(_r); row_buf = (char**)pkg_malloc(len); if (!row_buf) { LM_ERR("no private memory left\n"); return -1; } LM_DBG("allocate for %d columns %d bytes in row buffer at %p\n", RES_COL_N(_r), len, row_buf); memset(row_buf, 0, len); if (db_allocate_rows( _r, RES_ROW_N(_r))!=0) { LM_ERR("no private memory left\n"); return -2; } for(row=RES_LAST_ROW(_r); row<(RES_LAST_ROW(_r)+RES_ROW_N(_r)) ; row++) { for(col = 0; col < RES_COL_N(_r); col++) { /* * The row data pointer returned by PQgetvalue points to * storage that is part of the PGresult structure. One should * not modify the data it points to, and one must explicitly * copy the data into other storage if it is to be used past * the lifetime of the PGresult structure itself. */ /* * There's a weird bug (or just weird behavior) in the postgres * API - if the result is a BLOB (like 'text') and is with * zero length, we get a pointer to nowhere, which is not * null-terminated. The fix for this is to check what does the * DB think about the length and use that as a correction. */ if (PQgetisnull(CON_RESULT(_h), row, col) == 0) { /* not null value */ if ( (len=PQgetlength(CON_RESULT(_h), row, col))==0 ) { s=""; LM_DBG("PQgetvalue(%p,%d,%d)=[], zero len\n", _h, row,col); } else { s = PQgetvalue(CON_RESULT(_h), row, col); LM_DBG("PQgetvalue(%p,%d,%d)=[%.*s]\n", _h, row,col,len,s); } row_buf[col] = pkg_malloc(len+1); if (!row_buf[col]) { LM_ERR("no private memory left\n"); return -1; } memset(row_buf[col], 0, len+1); LM_DBG("allocated %d bytes for row_buf[%d] at %p\n", len, col, row_buf[col]); strncpy(row_buf[col], s, len); LM_DBG("[%d][%d] Column[%.*s]=[%s]\n", row, col, RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s, row_buf[col]); } } /* ASSERT: row_buf contains an entire row in strings */ if(db_postgres_convert_row(_h, _r, &(RES_ROWS(_r)[row - RES_LAST_ROW(_r)]), row_buf)<0){ LM_ERR("failed to convert row #%d\n", row); RES_ROW_N(_r) = row - RES_LAST_ROW(_r); for (col = 0; col < RES_COL_N(_r); col++) { LM_DBG("freeing row_buf[%d] at %p\n", col, row_buf[col]); if (row_buf[col] && !row_buf[col][0]) pkg_free(row_buf[col]); } LM_DBG("freeing row buffer at %p\n", row_buf); pkg_free(row_buf); return -4; } /* * pkg_free() must be done for the above allocations now that the row * has been converted. During pg_convert_row (and subsequent pg_str2val) * processing, data types that don't need to be converted (namely STRINGS * and STR) have their addresses saved. These data types should not have * their pkg_malloc() allocations freed here because they are still * needed. However, some data types (ex: INT, DOUBLE) should have their * pkg_malloc() allocations freed because during the conversion process, * their converted values are saved in the union portion of the db_val_t * structure. BLOB will be copied during PQunescape in str2val, thus it * has to be freed here AND in pg_free_row(). * * Warning: when the converted row is no longer needed, the data types * whose addresses were saved in the db_val_t structure must be freed * or a memory leak will happen. This processing should happen in the * pg_free_row() subroutine. The caller of this routine should ensure * that pg_free_rows(), pg_free_row() or pg_free_result() is eventually * called. */ for (col = 0; col < RES_COL_N(_r); col++) { switch (RES_TYPES(_r)[col]) { case DB_STRING: case DB_STR: break; default: LM_DBG("freeing row_buf[%d] at %p\n", col, row_buf[col]); if (row_buf[col]) pkg_free(row_buf[col]); } /* * The following housekeeping may not be technically required, but it * is a good practice to NULL pointer fields that are no longer valid. * Note that DB_STRING fields have not been pkg_free(). NULLing DB_STRING * fields would normally not be good to do because a memory leak would * occur. However, the pg_convert_row() routine has saved the DB_STRING * pointer in the db_val_t structure. The db_val_t structure will * eventually be used to pkg_free() the DB_STRING storage. */ row_buf[col] = (char *)NULL; } } LM_DBG("freeing row buffer at %p\n", row_buf); pkg_free(row_buf); row_buf = NULL; return 0; } /** * Convert a row from the result query into db API representation */ int db_postgres_convert_row(const db_con_t* _h, db_res_t* _r, db_row_t* _row, char **row_buf) { int col, len; if (!_h || !_r || !_row) { LM_ERR("invalid parameter value\n"); return -1; } /* Save the number of columns in the ROW structure */ ROW_N(_row) = RES_COL_N(_r); /* For each column in the row */ for(col = 0; col < ROW_N(_row); col++) { /* compute the len of the value */ if ( row_buf[col]==NULL || row_buf[col][0]=='\0') len = 0; else len = strlen(row_buf[col]); /* Convert the string representation into the value representation */ if (db_postgres_str2val(RES_TYPES(_r)[col], &(ROW_VALUES(_row)[col]), row_buf[col], len) < 0) { LM_ERR("failed to convert value\n"); LM_DBG("free row at %pn", _row); db_free_row(_row); return -3; } } return 0; } opensips-2.2.2/modules/db_postgres/res.h000066400000000000000000000022461300170765700203140ustar00rootroot00000000000000/* * Copyright (C) 2007 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef DB_PG_RES_H #define DB_PG_RES_H #include "../../db/db_row.h" int db_postgres_convert_result(const db_con_t* _h, db_res_t* _r); int db_postgres_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r, char **row_buf); int db_postgres_get_columns(const db_con_t* _h, db_res_t* _r); int db_postgres_convert_rows(const db_con_t* _h, db_res_t* _r); #endif opensips-2.2.2/modules/db_postgres/val.c000066400000000000000000000170411300170765700202770ustar00rootroot00000000000000/* * POSTGRES module, portions of this code were templated using * the mysql module, thus it's similarity. * * Copyright (C) 2003 August.Net Services, LLC * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --- * * History * ------- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford) * 2003-04-14 gmtime changed to localtime because mktime later * expects localtime, changed daylight saving bug * previously found in mysql module (janakj) * */ #include "../../db/db_val.h" #include "../../db/db_ut.h" #include "../../dprint.h" #include "pg_con.h" #include "../../mem/mem.h" #include "val.h" #include #include #include /* * Convert a str to a db value, does not copy strings * The postgresql module uses a custom escape function for BLOBs. * If the _s is linked in the db_val result, it will be returned zero */ int db_postgres_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l) { static str dummy_string = {"", 0}; char *x; if (!_v) { LM_ERR("invalid parameter value\n"); return -1; } if (!_s) { memset(_v, 0, sizeof(db_val_t)); /* Initialize the string pointers to a dummy empty * string so that we do not crash when the NULL flag * is set but the module does not check it properly */ VAL_STR(_v) = dummy_string; VAL_TYPE(_v) = _t; VAL_NULL(_v) = 1; return 0; } VAL_NULL(_v) = 0; switch(_t) { case DB_INT: LM_DBG("converting INT [%s]\n", _s); if (db_str2int(_s, &VAL_INT(_v)) < 0) { LM_ERR("failed to convert INT value from string\n"); return -2; } else { VAL_TYPE(_v) = DB_INT; return 0; } break; case DB_BIGINT: LM_DBG("converting BIGINT [%s]\n", _s); if (db_str2bigint(_s, &VAL_BIGINT(_v)) < 0) { LM_ERR("failed to convert BIGINT value from string\n"); return -2; } else { VAL_TYPE(_v) = DB_BIGINT; return 0; } break; case DB_BITMAP: LM_DBG("converting BITMAP [%s]\n", _s); if (db_str2int(_s, &VAL_INT(_v)) < 0) { LM_ERR("failed to convert BITMAP value from string\n"); return -3; } else { VAL_TYPE(_v) = DB_BITMAP; return 0; } break; case DB_DOUBLE: LM_DBG("converting DOUBLE [%s]\n", _s); if (db_str2double(_s, &VAL_DOUBLE(_v)) < 0) { LM_ERR("failed to convert DOUBLE value from string\n"); return -4; } else { VAL_TYPE(_v) = DB_DOUBLE; return 0; } break; case DB_STRING: LM_DBG("converting STRING [%s]\n", _s); VAL_STRING(_v) = _s; VAL_TYPE(_v) = DB_STRING; VAL_FREE(_v) = 1; return 0; case DB_STR: LM_DBG("converting STR [%.*s]\n", _l, _s); VAL_STR(_v).s = (char*)_s; VAL_STR(_v).len = _l; VAL_TYPE(_v) = DB_STR; VAL_FREE(_v) = 1; return 0; case DB_DATETIME: LM_DBG("converting DATETIME [%s]\n", _s); if (db_str2time(_s, &VAL_TIME(_v)) < 0) { LM_ERR("failed to convert datetime\n"); return -5; } else { VAL_TYPE(_v) = DB_DATETIME; return 0; } break; case DB_BLOB: LM_DBG("converting BLOB [%.*s]\n", _l, _s); /* PQunescapeBytea: Converts a string representation of binary data * into binary data - the reverse of PQescapeBytea. * This is needed when retrieving bytea data in text format, * but not when retrieving it in binary format. */ x = (char*)PQunescapeBytea((unsigned char*)_s, (size_t*)(void*)&(VAL_BLOB(_v).len) ); VAL_BLOB(_v).s = pkg_malloc( VAL_BLOB(_v).len+1 ); if (VAL_BLOB(_v).s==NULL) { LM_ERR("failed to allocate pkg for BLOB\n"); return -6; } memcpy( VAL_BLOB(_v).s, x, VAL_BLOB(_v).len); VAL_BLOB(_v).s[VAL_BLOB(_v).len]='\0'; free(x); VAL_TYPE(_v) = DB_BLOB; VAL_FREE(_v) = 1; LM_DBG("got blob len %d\n", _l); return 0; } return -6; } /* * Used when converting result from a query */ int db_postgres_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len) { int l, ret; int pgret; char *tmp_s; size_t tmp_len; char* old_s; if ((!_v) || (!_s) || (!_len) || (!*_len)) { LM_ERR("invalid parameter value\n"); return -1; } if (VAL_NULL(_v)) { if ( *_len < (l=(int)sizeof("NULL")-1)) { LM_ERR("buffer too short to print NULL\n"); return -1; } memcpy(_s, "NULL", l); *_len = l; return 0; } switch(VAL_TYPE(_v)) { case DB_INT: if (db_int2str(VAL_INT(_v), _s, _len) < 0) { LM_ERR("failed to convert string to int\n"); return -2; } else { return 0; } break; case DB_BIGINT: if (db_bigint2str(VAL_BIGINT(_v), _s, _len) < 0) { LM_ERR("failed to convert string to big int\n"); return -2; } else { return 0; } break; case DB_BITMAP: if (db_int2str(VAL_BITMAP(_v), _s, _len) < 0) { LM_ERR("failed to convert string to int\n"); return -3; } else { return 0; } break; case DB_DOUBLE: if (db_double2str(VAL_DOUBLE(_v), _s, _len) < 0) { LM_ERR("failed to convert string to double\n"); return -3; } else { return 0; } break; case DB_STRING: l = strlen(VAL_STRING(_v)); if (*_len < (l * 2 + 3)) { LM_ERR("destination buffer too short for string\n"); return -4; } else { old_s = _s; *_s++ = '\''; ret = PQescapeStringConn(CON_CONNECTION(_con), _s, VAL_STRING(_v), l, &pgret); if(pgret!=0) { LM_ERR("PQescapeStringConn failed\n"); return -4; } LM_DBG("PQescapeStringConn: in: %d chars," " out: %d chars\n", l, ret); _s += ret; *_s++ = '\''; *_s = '\0'; /* FIXME */ *_len = _s - old_s; return 0; } break; case DB_STR: l = VAL_STR(_v).len; if (*_len < (l * 2 + 3)) { LM_ERR("destination buffer too short for str\n"); return -5; } else { old_s = _s; *_s++ = '\''; ret = PQescapeStringConn(CON_CONNECTION(_con), _s, VAL_STRING(_v), l, &pgret); if(pgret!=0) { LM_ERR("PQescapeStringConn failed \n"); return -5; } LM_DBG("PQescapeStringConn: in: %d chars, out: %d chars\n", l, ret); _s += ret; *_s++ = '\''; *_s = '\0'; /* FIXME */ *_len = _s - old_s; return 0; } break; case DB_DATETIME: if (db_time2str(VAL_TIME(_v), _s, _len) < 0) { LM_ERR("failed to convert string to time_t\n"); return -6; } else { return 0; } break; case DB_BLOB: l = VAL_BLOB(_v).len; /* this estimation is not always correct, thus we need to check later again */ if (*_len < (l * 2 + 3)) { LM_ERR("destination buffer too short for blob\n"); return -7; } else { *_s++ = '\''; tmp_s = (char*)PQescapeByteaConn(CON_CONNECTION(_con), (unsigned char*)VAL_STRING(_v), (size_t)l, (size_t*)&tmp_len); if(tmp_s==NULL) { LM_ERR("PQescapeBytea failed\n"); return -7; } if (tmp_len > *_len) { LM_ERR("escaped result too long\n"); return -7; } memcpy(_s, tmp_s, tmp_len); PQfreemem(tmp_s); tmp_len = strlen(_s); *(_s + tmp_len) = '\''; *(_s + tmp_len + 1) = '\0'; *_len = tmp_len + 2; return 0; } break; default: LM_DBG("unknown data type\n"); return -7; } } opensips-2.2.2/modules/db_postgres/val.h000066400000000000000000000020261300170765700203010ustar00rootroot00000000000000/* * Copyright (C) 2007 1&1 Internet AG * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef DB_PG_VAL_H #define DB_PG_VAL_H int db_postgres_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l); int db_postgres_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len); #endif opensips-2.2.2/modules/db_sqlite/000077500000000000000000000000001300170765700170015ustar00rootroot00000000000000opensips-2.2.2/modules/db_sqlite/Makefile000066400000000000000000000023101300170765700204350ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=db_sqlite.so SCRIPTS_DIR?=../../scripts/ LIBS+=-lsqlite3 include ../../Makefile.modules install_module_custom: mkdir -p $(modules_prefix)/$(lib_dir)/opensipsctl ; \ sed -e "s#/usr/local/sbin#$(bin-target)#g" \ < $(SCRIPTS_DIR)/opensipsctl.sqlite > /tmp/opensipsctl.sqlite ; \ $(INSTALL_CFG) /tmp/opensipsctl.sqlite \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.sqlite ; \ rm -fr /tmp/opensipsctl.sqlite ; \ sed -e "s#/usr/local/share/opensips#$(data_target)#g" \ < $(SCRIPTS_DIR)/opensipsdbctl.sqlite > /tmp/opensipsdbctl.sqlite ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsdbctl.sqlite ; \ $(INSTALL_CFG) /tmp/opensipsdbctl.sqlite $(modules_prefix)/$(lib_dir)/opensipsctl/ ; \ rm -fr /tmp/opensipsdbctl.sqlite ; \ mkdir -p $(data_prefix)/$(data_dir)/sqlite ; \ for FILE in $(wildcard $(SCRIPTS_DIR)/sqlite/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/sqlite/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/sqlite/`basename "$$FILE"` ; \ fi ;\ done ; \ opensips-2.2.2/modules/db_sqlite/README000066400000000000000000000102701300170765700176610ustar00rootroot00000000000000sqlite Module Ionut-Razvan Ionita Copyright © 2015 Voice Sistem SRL Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. alloc_limit (integer) 1.3.2. load_extension (string) 1.4. Exported Functions 1.5. Installation List of Examples 1.1. Set alloc_limit parameter 1.2. Set db_sqlite_alloc_limit parameter Chapter 1. Admin Guide 1.1. Overview This is a module which provides SQLite support for OpenSIPS. It implements the DB API defined in OpenSIPS. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. Also this module provides two ways of creating the query. One is to use sqlite3_bind_* functions after opensips creates the prepared statement query. The second one directly uses only sqlite3_snprintf function to print the values into the opensips created query. In theory, the second one should be faster and should allow you to make more queries to the database in the same time, so by default this one will be active. You can use the sqlite3_bind_* interface by simply uncommenting the SQLITE_BIND line the Makefile. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libsqlite3-dev - the development libraries of sqlite. 1.3. Exported Parameters 1.3.1. alloc_limit (integer) Since the library does not support a function to return the number of rows in a query, this number is obtained using "count(*)" query. If we use multiple processes there is the risk ,since "count(*)" query and the actual "select" query, the number of rows in the result query to have changed, so realloc will be needed if the number is bigger. Using alloc_limit parameter you can specify the number with which the number of allocated rows in the result is raised. Default value is 10. Example 1.1. Set alloc_limit parameter ... modparam("db_sqlite", "alloc_limit", 25) ... 1.3.2. load_extension (string) Since the library does not support a function to return the number of rows in a query, this number is obtained using "count(*)" query. If we use multiple processes there is the risk ,since "count(*)" query and the actual "select" query, the number of rows in the result query to have changed, so realloc will be needed if the number is bigger. Using alloc_limit parameter you can specify the number with which the number of allocated rows in the result is raised. This parameter enables extension loading, similiar to ".load" functionality in sqlite3, extenions like sqlite3-pcre which enables REGEX function. In order to use this functionality you must specify the library path(.so file) and the entry point which represents the function to be called by the sqlite library (read more at sqlite load_extension official documentation), separated by ";" delimiter. The entry point paramter can miss, so you won't need to use the delimitier in this case. Default no extenion is loaded. Example 1.2. Set db_sqlite_alloc_limit parameter ... modparam("db_sqlite", "load_extension", "/usr/lib/sqlite3/pcre.so") modparam("db_sqlite", "load_extension", "/usr/lib/sqlite3/pcre.so;sqlite 3_extension_init") ... 1.4. Exported Functions No function exported to be used from configuration file. 1.5. Installation Because it dependes on an external library, the sqlite module is not compiled and installed by default. You can use one of the next options. * - edit the "Makefile" and remove "db_sqlite" from "excluded_modules" list. Then follow the standard procedure to install OpenSIPS: "make all; make install". * - from command line use: 'make all include_modules="db_sqlite"; make install include_modules="db_sqlite"'. opensips-2.2.2/modules/db_sqlite/db_sqlite.c000066400000000000000000000101471300170765700211160ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #include "../../sr_module.h" #include "../../db/db.h" #include "../../db/db_cap.h" #include "db_sqlite.h" #include "dbase.h" #include #define ALLOC_LIMIT 10 #define LDEXT_LIST_DELIM ';' unsigned int db_sqlite_timeout_interval = 2; /* Default is 6 seconds */ unsigned int db_sliqte_exec_query_threshold = 0; /* Warning in case DB query takes too long disabled by default*/ int db_sqlite_alloc_limit=ALLOC_LIMIT; static int sqlite_mod_init(void); static void sqlite_mod_destroy(void); static int db_sqlite_add_extension(modparam_t type, void *val); struct db_sqlite_extension_list *extension_list=0; /* * MySQL database module interface */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)db_sqlite_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"alloc_limit", INT_PARAM, &db_sqlite_alloc_limit}, {"load_extension", STR_PARAM|USE_FUNC_PARAM, (void *)db_sqlite_add_extension}, {0, 0, 0} }; struct module_exports exports = { "db_sqlite", MOD_TYPE_SQLDB, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ sqlite_mod_init, /* module initialization function */ 0, /* response function*/ sqlite_mod_destroy, /* destroy function */ 0 /* per-child init function */ }; static int sqlite_mod_init(void) { return 0; } static void sqlite_mod_destroy(void) { struct db_sqlite_extension_list *foo=NULL; while (extension_list) { foo=extension_list; extension_list=extension_list->next; pkg_free(foo); } } int db_sqlite_bind_api(const str* mod, db_func_t *dbb) { if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->use_table = db_sqlite_use_table; dbb->init = db_sqlite_init; dbb->close = db_sqlite_close; dbb->query = db_sqlite_query; dbb->fetch_result = db_sqlite_fetch_result; dbb->raw_query = db_sqlite_raw_query; dbb->free_result = db_sqlite_free_result; dbb->insert = db_sqlite_insert; dbb->delete = db_sqlite_delete; dbb->update = db_sqlite_update; dbb->replace = db_sqlite_replace; dbb->last_inserted_id = db_last_inserted_id; dbb->insert_update = db_insert_update; dbb->cap = DB_CAP_ALL ^ DB_CAP_ASYNC_RAW_QUERY; return 0; } static int db_sqlite_add_extension(modparam_t type, void *val) { struct db_sqlite_extension_list *node; int len; node=pkg_malloc(sizeof(struct db_sqlite_extension_list)); if (!node) goto out; len = strlen((char *)val); node->ldpath=(char *)val; node->entry_point=q_memchr(node->ldpath, LDEXT_LIST_DELIM, len); if (node->entry_point) { /* sqlite requires null terminated strings */ (node->entry_point++)[0] = '\0'; } /* Reduce the overhead of introducing in the end */ node->next=extension_list; extension_list=node; return 0; out: LM_ERR("no more pkg mem\n"); return -1; } opensips-2.2.2/modules/db_sqlite/db_sqlite.h000066400000000000000000000021311300170765700211150ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #ifndef DB_SQLITE_H_ #define DB_SQLITE_H_ struct db_sqlite_extension_list { char *ldpath; char *entry_point; struct db_sqlite_extension_list *next; }; int db_sqlite_bind_api(const str* mod, db_func_t *dbb); #endif opensips-2.2.2/modules/db_sqlite/dbase.c000066400000000000000000000525411300170765700202320ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #include #include #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../db/db_query.h" #include "../../db/db_async.h" #include "../../db/db_ut.h" #include "../../db/db_insertq.h" #include "../../db/db_res.h" #include "my_con.h" #include "val.h" #include "res.h" #include "row.h" #include "dbase.h" #define COUNT_QUERY "select count(*)" #define COUNT_BUF_SIZE 2048 static str query_holder = {NULL,0}; extern int db_sqlite_alloc_limit; char count_buf[COUNT_BUF_SIZE]="select count(*)"; str count_str = {count_buf, sizeof(COUNT_QUERY)-1}; static inline int db_copy_rest_of_count(const str* _qh, str* count_query); static int db_sqlite_store_result(const db_con_t* _h, db_res_t** _r, const db_val_t* v, const int n); #ifdef SQLITE_BIND static int db_sqlite_bind_values(sqlite3_stmt* stmt, const db_val_t* _v, const int _n); #endif static int db_sqlite_free_result_internal(const db_con_t* _h, db_res_t* _r); static int db_sqlite_submit_dummy_query(const db_con_t* _h, const str* _s) { query_holder = *_s; return 0; } /** * Initialize the database module. * No function should be called before this * \param _url URL used for initialization * \return zero on success, negative value on failure */ db_con_t* db_sqlite_init(const str* _url) { return db_do_init(_url, (void *)db_sqlite_new_connection); } /** * Shut down the database module. * No function should be called after this * \param _h handle to the closed connection * \return zero on success, negative value on failure */ void db_sqlite_close(db_con_t* _h) { db_do_close(_h, db_sqlite_free_connection); } static inline int db_copy_rest_of_count(const str* _qh, str* count_query) { char* found; const str searched_str = {" from ", sizeof(" from ")-1}; count_query->len = sizeof(COUNT_QUERY)-1; if ((found=str_strstr(_qh, &searched_str)) != NULL) { const int len=_qh->len-(found-_qh->s); /* check for overflow */ if (len > COUNT_BUF_SIZE-(sizeof(COUNT_QUERY)-1)) { LM_ERR("query too big! try reducing the size of your query!" "Current max size [%d]!\n", COUNT_BUF_SIZE); return -1; } memcpy(count_query->s+count_query->len, found, len); count_query->len += len; return 0; } return -1; } static inline int db_sqlite_get_query_rows(const db_con_t* _h, const str* query, const db_val_t* _v, const int _n) { int ret; sqlite3_stmt* stmt; again: ret=sqlite3_prepare_v2(CON_CONNECTION(_h), query->s, query->len, &stmt, NULL); if (ret == SQLITE_BUSY) goto again; if (ret != SQLITE_OK) { LM_ERR("failed to prepare query\n"); return -1; } #ifdef SQLITE_BIND if (db_sqlite_bind_values(stmt, _v, _n) != SQLITE_OK) { LM_ERR("failed to bind values\n"); return -1; } #endif again2: ret=sqlite3_step(stmt); if (ret == SQLITE_BUSY) goto again2; if (ret != SQLITE_ROW) { sqlite3_finalize(stmt); LM_ERR("failed to fetch query size\n"); return -1; } ret=sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); return ret; } int db_sqlite_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r) { int ret=-1; #ifdef SQLITE_BIND db_ps_t ps; CON_SET_CURR_PS(_h, &ps); #else CON_RESET_CURR_PS(_h); #endif CON_RAW_QUERY(_h) = 0; ret = db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, NULL, db_sqlite_val2str, db_sqlite_submit_dummy_query, NULL); if (ret != 0) { if (_r) *_r = NULL; return ret; } if (db_copy_rest_of_count(&query_holder, &count_str)) { LM_ERR("failed to build row counter query\n"); return -1; } again: ret=sqlite3_prepare_v2(CON_CONNECTION(_h), query_holder.s, query_holder.len, &CON_SQLITE_PS(_h), NULL); if (ret==SQLITE_BUSY) goto again; if (ret!=SQLITE_OK) LM_ERR("failed to prepare: (%s)\n", sqlite3_errmsg(CON_CONNECTION(_h))); #ifdef SQLITE_BIND if (db_sqlite_bind_values(CON_SQLITE_PS(_h), _v, _n) != SQLITE_OK) { LM_ERR("failed to bind values\n"); sqlite3_finalize(CON_SQLITE_PS(_h)); return -1; } #endif if (_r) { ret = db_sqlite_store_result(_h, _r, _v, _n); } else { /* need to fetch now the total number of rows in query * because later won't have the query string */ ret = CON_PS_ROWS(_h) = db_sqlite_get_query_rows(_h, &count_str, _v, _n); } if( ret < 0 ){ db_sqlite_free_result_internal(_h,*_r); } return ret; } /** * Gets a partial result set. * \param _h structure representing the database connection * \param _r pointer to a structure representing the result * \param nrows number of fetched rows * \return zero on success, negative value on failure */ int db_sqlite_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows) { int ret; int rows, i; sqlite3_stmt* stmt; if (!_h || !_r || nrows < 0) { LM_ERR("Invalid parameter value\n"); db_sqlite_free_result_internal(_h,*_r); return -1; } /* exit if the fetch count is zero */ if (nrows == 0) { db_sqlite_free_result_internal(_h,*_r); *_r = 0; return 0; } if(*_r==0) { /* Allocate a new result structure */ *_r = db_new_result(); if (*_r == 0) { LM_ERR("no memory left\n"); return -2; } if (db_sqlite_get_columns(_h, *_r) < 0) { LM_ERR("error while getting column names\n"); db_sqlite_free_result_internal(_h,*_r); return -4; } RES_NUM_ROWS(*_r) = CON_PS_ROWS(_h); if (!RES_NUM_ROWS(*_r)) { LM_DBG("no rows returned from the query\n"); RES_ROWS(*_r) = 0; return 0; } } else { /* free old rows */ if(RES_ROWS(*_r)!=0) db_free_rows(*_r); RES_ROWS(*_r) = 0; RES_ROW_N(*_r) = 0; } /* determine the number of rows remaining to be processed */ rows = RES_NUM_ROWS(*_r) - RES_LAST_ROW(*_r); /* If there aren't any more rows left to process, exit */ if(rows<=0) return 0; /* if the fetch count is less than the remaining rows to process */ /* set the number of rows to process (during this call) equal to the fetch count */ if(nrows < rows) rows = nrows; RES_ROW_N(*_r) = rows; if (db_sqlite_allocate_rows(*_r, rows)!=0) { LM_ERR("no memory left\n"); db_sqlite_free_result_internal(_h,*_r); return -5; } i = 0; ret=-1; stmt = CON_SQLITE_PS(_h); while (ret != SQLITE_DONE) { if (i == nrows) { RES_LAST_ROW(*_r) = i - 1; break; } ret = sqlite3_step(stmt); if (ret == SQLITE_DONE) { RES_ROW_N(*_r) = RES_LAST_ROW(*_r) = RES_NUM_ROWS(*_r) = i; sqlite3_finalize(CON_SQLITE_PS(_h)); CON_SQLITE_PS(_h) = NULL; break; } if (i >= RES_ROW_N(*_r) && i < nrows) { db_sqlite_realloc_rows(*_r, RES_ROW_N(*_r) + db_sqlite_alloc_limit); RES_ROW_N(*_r) += db_sqlite_alloc_limit; } if ((ret=db_sqlite_convert_row(_h, *_r, &(RES_ROWS(*_r)[i]))) < 0) { LM_ERR("error while converting row #%d\n", i); RES_ROW_N(*_r) = i; db_sqlite_free_result_internal(_h,*_r); return -4; } i++; } return 0; } /** * Execute a raw SQL query. * \param _h handle for the database * \param _s raw query string * \param _r result set for storage * \return zero on success, negative value on failure */ int db_sqlite_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r) { static char sql_str[SQL_BUF_LEN]; int ret=-1, i=0; char* errmsg; str select_str={"select", 6}; str _scpy; CON_RESET_CURR_PS(_h); while (i < _s->len && !isalpha(_s->s[i])) i++; /* if any blank spaces or anything else before the actual query */ if (i) { _scpy.s = _s->s+i; _scpy.len = _s->len-i; } else { _scpy = *_s; } if (_scpy.len >= select_str.len && str_strncasecmp(&_scpy, &select_str, select_str.len)) { /* not a select statement; can execute the query and exit*/ if (_s->len + 1 > SQL_BUF_LEN) { LM_ERR("query too big! try reducing the size of your query!" "Current max size [%d]!\n", SQL_BUF_LEN); return -1; } memcpy(sql_str, _s->s, _s->len); sql_str[_s->len] = '\0'; if (sqlite3_exec(CON_CONNECTION(_h), sql_str, NULL, NULL, &errmsg)) { LM_ERR("query failed: %s\n", errmsg); sqlite3_free(errmsg); return -2; } return 0; } CON_RAW_QUERY(_h) = 1; if (db_copy_rest_of_count(&_scpy, &count_str)) { LM_ERR("failed to build count str!\n"); return -1; } again: ret=sqlite3_prepare_v2(CON_CONNECTION(_h), _s->s, _s->len, &CON_SQLITE_PS(_h), NULL); if (ret==SQLITE_BUSY) goto again; if (ret!=SQLITE_OK) LM_ERR("failed to prepare: (%s)\n", sqlite3_errmsg(CON_CONNECTION(_h))); if (_r) { ret = db_sqlite_store_result(_h, _r, NULL, 0); } else { /* need to fetch now the total number of rows in query * because later won't have the query string */ ret = CON_PS_ROWS(_h) = db_sqlite_get_query_rows(_h, &count_str, NULL, 0); } if( ret < 0 ){ db_sqlite_free_result_internal(_h,*_r); } return ret; } /** * Insert a row into a specified table. * \param _h structure representing database connection * \param _k key names * \param _v values of the keys * \param _n number of key=value pairs * \return zero on success, negative value on failure */ int db_sqlite_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { int ret=-1; sqlite3_stmt* stmt; #ifdef SQLITE_BIND db_ps_t ps; CON_SET_CURR_PS(_h, &ps); #else CON_RESET_CURR_PS(_h); #endif ret = db_do_insert(_h, _k, _v, _n, db_sqlite_val2str, db_sqlite_submit_dummy_query); if (ret != 0) { return ret; } again: ret=sqlite3_prepare_v2(CON_CONNECTION(_h), query_holder.s, query_holder.len, &stmt, NULL); if (ret==SQLITE_BUSY) goto again; if (ret!=SQLITE_OK) LM_ERR("failed to prepare: (%s)\n", sqlite3_errmsg(CON_CONNECTION(_h))); #ifdef SQLITE_BIND if ((ret=db_sqlite_bind_values(stmt, _v, _n)) != SQLITE_OK) { LM_ERR("failed to bind values (%d)\n", ret); return -1; } #endif again2: ret = sqlite3_step(stmt); if (ret==SQLITE_BUSY) goto again2; if (ret != SQLITE_DONE) { LM_ERR("insert query failed %s\n", sqlite3_errmsg(CON_CONNECTION(_h))); return -1; } sqlite3_finalize(stmt); return 0; } /** * Delete a row from the specified table * \param _h structure representing database connection * \param _k key names * \param _o operators * \param _v values of the keys that must match * \param _n number of key=value pairs * \return zero on success, negative value on failure */ int db_sqlite_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n) { int ret; sqlite3_stmt* stmt; #ifdef SQLITE_BIND db_ps_t ps; CON_SET_CURR_PS(_h, &ps); #else CON_RESET_CURR_PS(_h); #endif ret = db_do_delete(_h, _k, _o, _v, _n, db_sqlite_val2str, db_sqlite_submit_dummy_query); if (ret != 0) { return ret; } again: ret=sqlite3_prepare_v2(CON_CONNECTION(_h), query_holder.s, query_holder.len, &stmt, NULL); if (ret==SQLITE_BUSY) goto again; if (ret!=SQLITE_OK) LM_ERR("failed to prepare: (%s)\n", sqlite3_errmsg(CON_CONNECTION(_h))); #ifdef SQLITE_BIND if (db_sqlite_bind_values(stmt, _v, _n) != SQLITE_OK) { LM_ERR("failed to bind values\n"); return -1; } #endif again2: ret = sqlite3_step(stmt); if (ret==SQLITE_BUSY) goto again2; if (ret != SQLITE_DONE) { LM_ERR("insert query failed %s\n", sqlite3_errmsg(CON_CONNECTION(_h))); return -1; } sqlite3_finalize(stmt); return 0; } /** * Update some rows in the specified table * \param _h structure representing database connection * \param _k key names * \param _o operators * \param _v values of the keys that must match * \param _uk updated columns * \param _uv updated values of the columns * \param _n number of key=value pairs * \param _un number of columns to update * \return zero on success, negative value on failure */ int db_sqlite_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un) { int ret; sqlite3_stmt* stmt; #ifdef SQLITE_BIND db_ps_t ps; CON_SET_CURR_PS(_h, &ps); #else CON_RESET_CURR_PS(_h); #endif ret = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_sqlite_val2str, db_sqlite_submit_dummy_query); if (ret != 0) { return ret; } again: ret=sqlite3_prepare_v2(CON_CONNECTION(_h), query_holder.s, query_holder.len, &stmt, NULL); if (ret==SQLITE_BUSY) goto again; if (ret!=SQLITE_OK) LM_ERR("failed to prepare: (%s)\n", sqlite3_errmsg(CON_CONNECTION(_h))); #ifdef SQLITE_BIND if (db_sqlite_bind_values(stmt, _uv, _un) != SQLITE_OK && db_sqlite_bind_values(stmt, _v, _n)) { LM_ERR("failed to bind values\n"); return -1; } #endif again2: ret = sqlite3_step(stmt); if (ret==SQLITE_BUSY) goto again2; if (ret != SQLITE_DONE) { LM_ERR("insert query failed %s\n", sqlite3_errmsg(CON_CONNECTION(_h))); return -1; } sqlite3_finalize(stmt); return 0; } /** * Just like insert, but replace the row if it exists. * \param _h database handle * \param _k key names * \param _v values of the keys that must match * \param _n number of key=value pairs * \return zero on success, negative value on failure */ int db_sqlite_replace(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { int ret; sqlite3_stmt* stmt; #ifdef SQLITE_BIND db_ps_t ps; CON_SET_CURR_PS(_h, &ps); #else CON_RESET_CURR_PS(_h); #endif ret = db_do_replace(_h, _k, _v, _n, db_sqlite_val2str, db_sqlite_submit_dummy_query); if (ret != 0) { return ret; } again: ret=sqlite3_prepare_v2(CON_CONNECTION(_h), query_holder.s, query_holder.len, &stmt, NULL); if (ret==SQLITE_BUSY) goto again; if (ret!=SQLITE_OK) LM_ERR("failed to prepare: (%s)\n", sqlite3_errmsg(CON_CONNECTION(_h))); #ifdef SQLITE_BIND if (db_sqlite_bind_values(stmt, _v, _n) != SQLITE_OK) { LM_ERR("failed to bind values\n"); return -1; } #endif again2: ret = sqlite3_step(stmt); if (ret==SQLITE_BUSY) goto again2; if (ret != SQLITE_DONE) { LM_ERR("insert query failed %s\n", sqlite3_errmsg(CON_CONNECTION(_h))); return -1; } sqlite3_finalize(stmt); return 0; } /** * Returns the last inserted ID. * \param _h database handle * \return returns the ID as integer or returns 0 if the previous statement * does not use an AUTO_INCREMENT value. */ int db_last_inserted_id(const db_con_t* _h) { if (!_h) { LM_ERR("invalid parameter value\n"); return -1; } return sqlite3_last_insert_rowid(CON_CONNECTION(_h)); } /** * Insert a row into a specified table, update on duplicate key. * \param _h structure representing database connection * \param _k key names * \param _v values of the keys * \param _n number of key=value pairs */ int db_insert_update(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { #define SQL_BUF_LEN 65536 int off, ret; static str sql_str; static char sql_buf[SQL_BUF_LEN]; sqlite3_stmt* stmt; if ((!_h) || (!_k) || (!_v) || (!_n)) { LM_ERR("invalid parameter value\n"); return -1; } #ifdef SQLITE_BIND db_ps_t ps; CON_SET_CURR_PS(_h, &ps); #endif ret = snprintf(sql_buf, SQL_BUF_LEN, "insert into %.*s (", CON_TABLE(_h)->len, CON_TABLE(_h)->s); if (ret < 0 || ret >= SQL_BUF_LEN) goto error; off = ret; ret = db_print_columns(sql_buf + off, SQL_BUF_LEN - off, _k, _n); if (ret < 0) return -1; off += ret; ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values ("); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; ret = db_print_values(_h, sql_buf + off, SQL_BUF_LEN - off, _v, _n, db_sqlite_val2str); if (ret < 0) return -1; off += ret; *(sql_buf + off++) = ')'; ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, " on duplicate key update "); if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error; off += ret; ret = db_print_set(_h, sql_buf + off, SQL_BUF_LEN - off, _k, _v, _n, db_sqlite_val2str); if (ret < 0) return -1; off += ret; sql_str.s = sql_buf; sql_str.len = off; again: ret=sqlite3_prepare_v2(CON_CONNECTION(_h), sql_str.s, sql_str.len, &stmt, NULL); if (ret==SQLITE_BUSY) goto again; if (ret!=SQLITE_OK) LM_ERR("failed to prepare: (%s)\n", sqlite3_errmsg(CON_CONNECTION(_h))); #ifdef SQLITE_BIND if (db_sqlite_bind_values(stmt, _v, _n) != SQLITE_OK) { LM_ERR("failed to bind values\n"); return -1; } #endif again2: ret = sqlite3_step(stmt); if (ret==SQLITE_BUSY) goto again2; if (ret != SQLITE_DONE) { LM_ERR("insert query failed %s\n", sqlite3_errmsg(CON_CONNECTION(_h))); return -1; } sqlite3_finalize(stmt); return 0; #undef SQL_BUF_LEN error: LM_ERR("error while preparing insert_update operation\n"); return -1; } static int db_sqlite_free_result_internal(const db_con_t* _h, db_res_t* _r) { return db_sqlite_free_result(*(db_con_t**)&_h, _r); } /** * Release a result set from memory. * \param _h handle to the database * \param _r result set that should be freed * \return zero on success, negative value on failure */ int db_sqlite_free_result(db_con_t* _h, db_res_t* _r) { int i, j; db_val_t* val; if (!_h) { LM_ERR("invalid database handle\n"); return -1; } if( CON_SQLITE_PS(_h) ){ sqlite3_finalize(CON_SQLITE_PS(_h)); CON_SQLITE_PS(_h) = NULL; } if (!_r) { LM_DBG("nothing to free!\n"); return 0; } db_free_columns(_r); /* for each row iterate through all the values and free them * the values array(RES_ROW_N rows with RES_COL_N values for * each row) is allocated using a single chunk so in order * to free the array(check db_sqlite_allocate_rows function ) */ if (RES_ROWS(_r)) { for(i=0; i < RES_ROW_N(_r); i++) { for (j=0; j < RES_COL_N(_r); j++) { val = &(_r->rows[i].values[j]); if (VAL_NULL(val) || !VAL_FREE(val)) continue; switch (VAL_TYPE(val)) { case DB_STRING: case DB_STR: /* * FIXME * see row.c +121 ( last comment ) for * explanation why this will work * */ pkg_free(VAL_STR(val).s); VAL_STR(val).s = 0; break; case DB_BLOB: pkg_free(VAL_BLOB(val).s); VAL_BLOB(val).s = 0; break; default: break; } } } /* free all the columns; they are all allocated at once */ pkg_free( _r->rows[0].values); /* free the rows */ pkg_free( _r->rows); _r->rows = NULL; } RES_ROW_N(_r) = 0; pkg_free(_r); _r = NULL; return 0; } /** * Retrieve a result set * \param _h handle to the database * \param _r result set that should be retrieved * \return zero on success, negative value on failure */ static int db_sqlite_store_result(const db_con_t* _h, db_res_t** _r, const db_val_t* _v, const int _n) { int rows; if ((!_h) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } *_r = db_new_result(); if (*_r == 0) { LM_ERR("no memory left\n"); return -2; } rows=db_sqlite_get_query_rows(_h, &count_str, _v, _n); /* reset the length to initial for future uses */ if (rows < 0) { LM_ERR("failed to fetch number of rows\n"); return -1; } /* trying to fetch all rows * these values are not final values as, in the * meantime, the db can be changed by another process */ RES_NUM_ROWS(*_r) = RES_ROW_N(*_r) = rows; if (db_sqlite_convert_result(_h, *_r) < 0) { LM_ERR("error while converting result\n"); pkg_free(*_r); *_r = 0; return -4; } return 0; } /** * Store the name of table that will be used by subsequent database functions * \param _h database handle * \param _t table name * \return zero on success, negative value on failure */ int db_sqlite_use_table(db_con_t* _h, const str* _t) { return db_use_table(_h, _t); } #ifdef SQLITE_BIND static int db_sqlite_bind_values(sqlite3_stmt* stmt, const db_val_t* v, const int n) { int i, ret; if (n>0 && v) { for (i=0; i %docentities; ]> sqlite Module &osipsname; Ionut-Razvan Ionita ionutionita@opensips.org 2015 &voicesystem; $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/db_sqlite/doc/db_sqlite_admin.xml000066400000000000000000000112631300170765700234110ustar00rootroot00000000000000 &adminguide;
Overview This is a module which provides SQLite support for OpenSIPS. It implements the DB API defined in OpenSIPS.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules. Also this module provides two ways of creating the query. One is to use sqlite3_bind_* functions after opensips creates the prepared statement query. The second one directly uses only sqlite3_snprintf function to print the values into the opensips created query. In theory, the second one should be faster and should allow you to make more queries to the database in the same time, so by default this one will be active. You can use the sqlite3_bind_* interface by simply uncommenting the SQLITE_BIND line the Makefile.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libsqlite3-dev - the development libraries of sqlite.
Exported Parameters
<varname>alloc_limit</varname> (integer) Since the library does not support a function to return the number of rows in a query, this number is obtained using "count(*)" query. If we use multiple processes there is the risk ,since "count(*)" query and the actual "select" query, the number of rows in the result query to have changed, so realloc will be needed if the number is bigger. Using alloc_limit parameter you can specify the number with which the number of allocated rows in the result is raised. Default value is 10. Set <varname>alloc_limit</varname> parameter ... modparam("db_sqlite", "alloc_limit", 25) ...
<varname>load_extension</varname> (string) Since the library does not support a function to return the number of rows in a query, this number is obtained using "count(*)" query. If we use multiple processes there is the risk ,since "count(*)" query and the actual "select" query, the number of rows in the result query to have changed, so realloc will be needed if the number is bigger. Using alloc_limit parameter you can specify the number with which the number of allocated rows in the result is raised. This parameter enables extension loading, similiar to ".load" functionality in sqlite3, extenions like sqlite3-pcre which enables REGEX function. In order to use this functionality you must specify the library path(.so file) and the entry point which represents the function to be called by the sqlite library (read more at sqlite load_extension official documentation), separated by ";" delimiter. The entry point paramter can miss, so you won't need to use the delimitier in this case. Default no extenion is loaded. Set <varname>db_sqlite_alloc_limit</varname> parameter ... modparam("db_sqlite", "load_extension", "/usr/lib/sqlite3/pcre.so") modparam("db_sqlite", "load_extension", "/usr/lib/sqlite3/pcre.so;sqlite3_extension_init") ...
Exported Functions No function exported to be used from configuration file.
Installation Because it dependes on an external library, the sqlite module is not compiled and installed by default. You can use one of the next options. - edit the "Makefile" and remove "db_sqlite" from "excluded_modules" list. Then follow the standard procedure to install &osips;: "make all; make install". - from command line use: 'make all include_modules="db_sqlite"; make install include_modules="db_sqlite"'.
opensips-2.2.2/modules/db_sqlite/my_con.c000066400000000000000000000071231300170765700204340ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #include #include #include #include #include "../../dprint.h" #include "../../db/db_query.h" #include "../../db/db_async.h" #include "../../db/db_ut.h" #include "../../db/db_insertq.h" #include "../../db/db_id.h" #include "../../mem/mem.h" #include "my_con.h" #include "db_sqlite.h" extern struct db_sqlite_extension_list *extension_list; #define SQLITE_ID "sqlite:/" #define URL_BUFSIZ 1024 char url_buf[URL_BUFSIZ]; int db_sqlite_connect(struct my_con* ptr) { sqlite3* con; char* errmsg; struct db_sqlite_extension_list *iter; /* if connection already in use, close it first*/ if (ptr->init) sqlite3_close(ptr->con); ptr->init = 1; memcpy(url_buf, ptr->id->url.s+sizeof(SQLITE_ID)-1, ptr->id->url.len - (sizeof(SQLITE_ID)-1)); url_buf[ptr->id->url.len - (sizeof(SQLITE_ID)-1)] = '\0'; if (sqlite3_open(url_buf, &con) != SQLITE_OK) { LM_ERR("Can't open database: %s\n", sqlite3_errmsg((sqlite3*)ptr->con)); return -1; } /* trying to load extensions */ if (extension_list) { if (sqlite3_enable_load_extension(con, 1)) { LM_ERR("failed to enable extension loading\n"); return -1; } iter=extension_list; for (iter=extension_list; iter; iter=iter->next) { if (sqlite3_load_extension(con, iter->ldpath, iter->entry_point, &errmsg)) { LM_ERR("failed to load!" "Extension [%s]! Entry point [%s]!" "Errmsg [%s]!\n", iter->ldpath, iter->entry_point, errmsg); goto out_free; } LM_DBG("Extension [%s] loaded!\n", iter->ldpath); } if (sqlite3_enable_load_extension(con, 0)) { LM_ERR("failed to enable extension loading\n"); return -1; } } ptr->con = con; return 0; out_free: while (extension_list) { iter=extension_list; extension_list=extension_list->next; pkg_free(iter); } return -1; } /** * Create a new connection structure, * open the sqlite connection and set reference count to 1 */ struct my_con* db_sqlite_new_connection(const struct db_id* id) { struct my_con* ptr; if (!id) { LM_ERR("invalid parameter value\n"); return 0; } ptr = (struct my_con*)pkg_malloc(sizeof(struct my_con)); if (!ptr) { LM_ERR("no private memory left\n"); return 0; } memset(ptr, 0, sizeof(struct my_con)); ptr->ref = 1; ptr->id = (struct db_id*)id; if (db_sqlite_connect(ptr)!=0) { LM_ERR("initial connect failed\n"); goto err; } return ptr; err: if (ptr && ptr->con) pkg_free(ptr->con); if (ptr) pkg_free(ptr); return 0; } /** * Close the connection and release memory */ void db_sqlite_free_connection(struct pool_con* con) { if (!con) return; struct my_con * _c; _c = (struct my_con*) con; if (_c->id) free_db_id(_c->id); if (_c->con) { sqlite3_close(_c->con); } pkg_free(_c); } opensips-2.2.2/modules/db_sqlite/my_con.h000066400000000000000000000047771300170765700204550ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #ifndef MY_CON_H_ #define MY_CON_H_ #define PREP_STMT_VAL_LEN 1024 #include struct my_con { struct db_id* id; /**< Connection identifier */ unsigned int ref; /**< Reference count */ struct pool_con *async_pool; /**< Subpool of identical database handles */ int no_transfers; /**< Number of async queries to this backend */ struct db_transfer *transfers; /**< Array of ongoing async operations */ struct pool_con *next; /**< Next element in the pool (different db_id) */ int raw_query; /**< indicates whether a select query is a raw query*/ sqlite3* con; /* Connection representation */ sqlite3_stmt* curr_ps; int curr_ps_rows; unsigned int init; /* If the mysql conn was initialized */ struct prep_stmt *ps_list; /* list of prepared statements */ }; struct my_stmt_ctx { sqlite3_stmt *stmt; str query; int query_rows; struct my_stmt_ctx *next; }; struct prep_stmt { struct my_stmt_ctx *stmt_list; struct my_stmt_ctx *ctx; }; #define CON_CONNECTION(db_con) (((struct my_con*)((db_con)->tail))->con) #define CON_ROW(db_con) (((struct my_con*)((db_con)->tail))->row) #define CON_SQLITE_PS(db_con) (((struct my_con*)((db_con)->tail))->curr_ps) #define CON_RAW_QUERY(db_con) (((struct my_con*)((db_con)->tail))->raw_query) #define CON_PS_ROWS(db_con) (((struct my_con*)((db_con)->tail))->curr_ps_rows) #define CON_DISCON(db_con) (((struct my_con*)((db_con)->tail))->disconnected) int db_sqlite_connect(struct my_con* ptr); struct my_con* db_sqlite_new_connection(const struct db_id* id); void db_sqlite_free_connection(struct pool_con* con); #endif opensips-2.2.2/modules/db_sqlite/res.c000066400000000000000000000231271300170765700177430ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-03-03 initial version (Ionut Ionita) */ #define DWORD(p) (*(p+0) + (*(p+1) << 8) + (*(p+2) << 16) + (*(p+3) << 24)) #define DWORD_LEN 4 #include #include #include "../../db/db_query.h" #include "../../db/db_async.h" #include "../../db/db_ut.h" #include "../../db/db_insertq.h" #include "../../db/db_res.h" #include "../../ut.h" #include "my_con.h" #include "row.h" extern int db_sqlite_alloc_limit; static db_type_t get_type_from_decltype(const char *decltype) { /* DB_INT datatypes*/ #define INT 0x00746e69 /* INT */ #define INTE 0x65746e69 /* INTEGER */ #define TINY 0x796e6974 /* TINYINT */ #define SMAL 0x6c616d73 /* SMALLINT */ #define MEDI 0x6964656d /* MEDIUMINT */ #define BIGI 0x69676962 /* BIGINT */ #define UNSI 0x69736e75 /* UNSIGNED BIG INT */ #define INT2 0x32746e69 /* INT2 */ #define INT8 0x38746e69 /* INT8 */ #define NUME 0x656d756e /* NUMERIC */ #define BOOL 0x6c6f6f62 /* BOOLEAN */ #define DECI 0x69636564 /* DECIMAL */ /*******************/ /* DB_STRING datatypes */ #define CHAR 0x72616863 /* CHARACTER*/ #define VARC 0x63726176 /* VARCHAR */ #define VARY 0x79726176 /* VARYING CHARACTER */ #define NCHA 0x6168636e /* NCHAR */ #define NATI 0x6974616e /* NATIVE CHARACTER */ #define NVAR 0x7261766e /* NVARCHAR */ #define TEXT 0x74786574 /* TEXT */ #define CLOB 0x626f6c63 /* CLOB */ /***********************/ /* DB_BLOB datatypes */ #define BLOB 0x626f6c62 /* BLOB */ /*********************/ /* DB_DOUBLE datatypes */ #define REAL 0x6c616572 /* REAL */ #define DOUB 0x62756f64 /* DUBLE or DOULBE PRECISION */ #define FLOA 0x616f6c66 /* FLOAT */ /***********************/ /* DB_DATETIME datatypes */ #define DATE 0x65746164 /* DATE or DATETIME */ /*************************/ str s; char dword[DWORD_LEN]; s.s = dword; s.len = DWORD_LEN; /* avoid memory corruption if decltype size is smaller than DWORD_LEN */ memset(s.s, 0, DWORD_LEN); memcpy(s.s, decltype, s.len); strlower(&s); switch (DWORD(s.s)) { case INT: case INTE: case TINY: case SMAL: case MEDI: case UNSI: case INT2: case INT8: case NUME: case BOOL: case DECI: return DB_INT; case BIGI: return DB_BIGINT; case CHAR: case VARC: case VARY: case NCHA: case NATI: case NVAR: case TEXT: case CLOB: return DB_STRING; case BLOB: return DB_BLOB; case REAL: case DOUB: case FLOA: return DB_DOUBLE; case DATE: return DB_DATETIME; default: /* check again for INT */ if ((DWORD(s.s) & 0x00FFFFFF) == INT) return DB_INT; LM_BUG("invalid datatype! this should not happen " "since all sqlite datatypes are defined here!\n"); return -1; } return 0; /* undefine all datatypes; let someone else use them if needed */ /* DB_INT datatypes*/ #undef INT #undef INTE #undef TINY #undef SMAL #undef MEDI #undef BIGI #undef UNSI #undef INT2 #undef INT8 #undef NUME #undef BOOL #undef DECI /*******************/ /* DB_STRING datatypes */ #undef CHAR #undef VARC #undef VARY #undef NCHA #undef NATI #undef NVAR #undef TEXT #undef CLOB /***********************/ /* DB_BLOB datatypes */ #undef BLOB /*********************/ /* DB_DOUBLE datatypes */ #undef REAL #undef DOUB #undef FLOA /***********************/ /* DB_DATETIME datatypes */ #undef DATE /*************************/ } /** * Get and convert columns from a result */ int db_sqlite_get_columns(const db_con_t* _h, db_res_t* _r) { int col; int autoincrement; const char *decltype; const char* name; char stable[256]; if ((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } RES_COL_N(_r) = sqlite3_column_count(CON_SQLITE_PS(_h)); if (!RES_COL_N(_r)) { LM_ERR("no columns returned from the query\n"); return -2; } else { LM_DBG("%d columns returned from the query\n", RES_COL_N(_r)); } if (db_allocate_columns(_r, RES_COL_N(_r)) != 0) { LM_ERR("could not allocate columns\n"); return -3; } for(col = 0; col < RES_COL_N(_r); col++) { /* The pointer that is here returned is part of the result structure */ name = sqlite3_column_name(CON_SQLITE_PS(_h), col); RES_NAMES(_r)[col]->s = *((char**)&name); RES_NAMES(_r)[col]->len = strlen(RES_NAMES(_r)[col]->s); /* check if column is autoincrement only for normal queries; * for raw queries we can't know the table name */ if (!CON_RAW_QUERY(_h)) { /* sanity check */ if (CON_TABLE(_h)->len > 255) { LM_ERR("table name too big [%d]\n", CON_TABLE(_h)->len); return -1; } /* fix possible non '\0' terminated table name */ memcpy(stable, CON_TABLE(_h)->s, CON_TABLE(_h)->len); stable[CON_TABLE(_h)->len] = '\0'; if (sqlite3_table_column_metadata( CON_CONNECTION(_h), NULL, /* db name*/ stable, /* table name */ name, /* column name */ NULL, NULL, NULL, NULL, &autoincrement) != 0) { LM_ERR("failed to fetch metadata for column [%s]\n", name); return -1; } } /* since DB_BITMAP not used in SQLITE we will use it * here to know if value is PRIMARY KEY AUTOINCREMENT */ if (!CON_RAW_QUERY(_h) && autoincrement) { RES_TYPES(_r)[col] = DB_BITMAP; continue; } decltype = sqlite3_column_decltype(CON_SQLITE_PS(_h), col); RES_TYPES(_r)[col] = get_type_from_decltype(decltype); LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_r)[col], col, RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s); /* types will be determined at runtime */ } return 0; } /* * specific alloc type for this module * it's easier to realloc */ int db_sqlite_allocate_rows(db_res_t* res, const unsigned int rows) { unsigned int i; /* first allocate the rows */ res->rows = (struct db_row*)pkg_malloc(rows * (sizeof(db_row_t))); if (!res->rows) goto out; memset( res->rows, 0 , rows * (sizeof(db_row_t))); /* and then the values */ res->rows[0].values = pkg_malloc(rows * sizeof(db_val_t) * RES_COL_N(res)); if (!res->rows[0].values) goto out; memset( res->rows[0].values, 0, rows * sizeof(db_val_t) * RES_COL_N(res)); for( i=1 ; irows[i].values = res->rows[0].values + RES_COL_N(res) * i; res->rows[i].n = RES_COL_N(res); } return 0; out: LM_ERR("no memory left\n"); return -1; } /* * realloc function * used when entries added to the db since count(*) query */ int db_sqlite_realloc_rows(db_res_t* res, const unsigned int rows) { unsigned int i; struct db_row* res_rows; res->rows = pkg_realloc(RES_ROWS(res),rows * (sizeof(db_row_t))); memset( res->rows + RES_ROW_N(res), 0 , (rows - RES_ROW_N(res)) * (sizeof(db_row_t))); res_rows = res->rows; if (!res_rows) { LM_ERR("no memory left\n"); return -1; } res_rows[0].values = pkg_realloc(res_rows[0].values, rows * sizeof(db_val_t) * RES_COL_N(res)); memset( res_rows[0].values + RES_COL_N(res)*sizeof(db_val_t)*RES_ROW_N(res), 0, (rows - RES_ROW_N(res)) * sizeof(db_val_t) * RES_COL_N(res)); if (! res_rows[0].values) { LM_ERR("no memory left\n"); return -1; } for( i=RES_ROW_N(res) ; irows[i].n = RES_COL_N(res); } return 0; } /** * Convert rows from sqlite to db API representation * */ static inline int db_sqlite_convert_rows(const db_con_t* _h, db_res_t* _r) { int row; int ret; if ((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } if (!CON_SQLITE_PS(_h)) { LM_ERR(" all sqlite queries should have a ps!\n"); return -1; } if (!RES_ROW_N(_r)) { LM_DBG("no rows returned from the query\n"); RES_ROWS(_r) = 0; return 0; } if (db_sqlite_allocate_rows( _r, RES_ROW_N(_r))!=0) { LM_ERR("no private memory left\n"); return -2; } row=0; ret=-1; while (ret != SQLITE_DONE) { ret = sqlite3_step(CON_SQLITE_PS(_h)); if (ret == SQLITE_BUSY) continue; if (ret == SQLITE_DONE) { RES_ROW_N(_r) = RES_LAST_ROW(_r) = RES_NUM_ROWS(_r) = row; sqlite3_reset(CON_SQLITE_PS(_h)); sqlite3_clear_bindings(CON_SQLITE_PS(_h)); break; } if (row == RES_ROW_N(_r)) { db_sqlite_realloc_rows(_r, RES_ROW_N(_r) + db_sqlite_alloc_limit); RES_ROW_N(_r) += db_sqlite_alloc_limit; } if ((ret=db_sqlite_convert_row(_h, _r, &(RES_ROWS(_r)[row]))) < 0) { LM_ERR("error while converting row #%d\n", row); RES_ROW_N(_r) = row; db_free_rows(_r); return -1; } row++; } return ret; } /** * Fill the structure with data from database */ int db_sqlite_convert_result(const db_con_t* _h, db_res_t* _r) { if ((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } if (db_sqlite_get_columns(_h, _r) < 0) { LM_ERR("error while getting column names\n"); return -2; } if (db_sqlite_convert_rows(_h, _r) < 0) { LM_ERR("error while converting rows\n"); db_free_columns(_r); return -3; } return 0; } #undef DWORD opensips-2.2.2/modules/db_sqlite/res.h000066400000000000000000000024451300170765700177500ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-03-03 initial version (Ionut Ionita) */ #ifndef RES_H #define RES_H #include "../../db/db_res.h" #include "../../db/db_con.h" /* * Fill the structure with data from database */ int db_sqlite_convert_result(const db_con_t* _h, db_res_t* _r); int db_sqlite_get_columns(const db_con_t* _h, db_res_t* _r); int db_sqlite_allocate_rows(db_res_t* res, const unsigned int rows); int db_sqlite_realloc_rows(db_res_t* res, const unsigned int rows); #endif /* RES_H */ opensips-2.2.2/modules/db_sqlite/row.c000066400000000000000000000071371300170765700177640ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-03-03 initial version (Ionut Ionita) */ #include "../../dprint.h" #include "../../mem/mem.h" #include "../../db/db_row.h" #include "../../db/db_ut.h" #include "../../db/db_val.h" #include "../../db/db_row.h" #include "my_con.h" #include "val.h" #include "row.h" #define DB_UNDEFINED 1024 /** * Convert a row from result into db API representation */ int db_sqlite_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r) { int col,len; db_val_t* _v; const char* db_value; if ((!_h) || (!_res) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } if (!CON_SQLITE_PS(_h)) { LM_ERR("conn has no prepared statement! sqlite requires one\n"); return -1; } /* Save the number of columns in the ROW structure */ ROW_N(_r) = RES_COL_N(_res); for(col=0; col < RES_COL_N(_res); col++) { _v = &(ROW_VALUES(_r)[col]); if (sqlite3_column_type(CON_SQLITE_PS(_h), col) == SQLITE_NULL) { VAL_NULL(_v) = 1; continue; } switch (RES_TYPES(_res)[col]) { case DB_BITMAP: /* value considered to be int; but stored as bigint; * can be used as VAL_INT() to be called * also can be used as VAL_BIGINT() */ VAL_BIGINT(_v) = sqlite3_column_int64(CON_SQLITE_PS(_h), col); VAL_TYPE(_v) = DB_INT; break; case DB_INT: VAL_BIGINT(_v) =sqlite3_column_int64(CON_SQLITE_PS(_h), col); VAL_TYPE(_v) = DB_INT; break; case DB_BIGINT: VAL_BIGINT(_v) = sqlite3_column_int64(CON_SQLITE_PS(_h), col); VAL_TYPE(_v) = DB_BIGINT; break; case DB_DATETIME: db_value = (char *)sqlite3_column_text(CON_SQLITE_PS(_h), col); if (db_str2time(db_value, &VAL_TIME(_v)) < 0) { LM_ERR("error while converting datetime value from string\n"); return -1; } VAL_TYPE(_v) = DB_DATETIME; break; case DB_DOUBLE: VAL_DOUBLE(_v) = sqlite3_column_double(CON_SQLITE_PS(_h), col); VAL_TYPE(_v) = DB_DOUBLE; break; case DB_BLOB: VAL_BLOB(_v).len = sqlite3_column_bytes(CON_SQLITE_PS(_h), col); db_value = sqlite3_column_blob(CON_SQLITE_PS(_h), col); VAL_BLOB(_v).s = pkg_malloc(VAL_BLOB(_v).len+1); if (VAL_BLOB(_v).s == NULL) { LM_ERR("no more pkg mem!\n"); return -1; } memcpy(VAL_BLOB(_v).s, db_value, VAL_BLOB(_v).len); VAL_BLOB(_v).s[VAL_BLOB(_v).len]='\0'; VAL_TYPE(_v) = DB_BLOB; VAL_FREE(_v) = 1; break; case DB_STRING: len = sqlite3_column_bytes(CON_SQLITE_PS(_h), col); db_value = (char *)sqlite3_column_text(CON_SQLITE_PS(_h), col); if ((VAL_STRING(_v) = pkg_malloc(len+1)) == NULL) { LM_ERR("no more pkg mem!\n"); return -1; } memcpy((char*)VAL_STRING(_v), db_value, len+1); VAL_TYPE(_v) = DB_STRING; VAL_FREE(_v) = 1; break; default: LM_ERR("invalid type for sqlite!\n"); return -1; } } return 0; } opensips-2.2.2/modules/db_sqlite/row.h000066400000000000000000000022231300170765700177600ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-03-03 initial version (Ionut Ionita) */ #ifndef ROW_H #define ROW_H #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_row.h" /** * Convert a row from result into db API representation */ int db_sqlite_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r); #endif /* ROW_H */ opensips-2.2.2/modules/db_sqlite/val.c000066400000000000000000000063701300170765700177350ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #include "../../dprint.h" #include "../../db/db_ut.h" #include "../../db/db_query.h" #include "val.h" #include "my_con.h" #include #include /* * Used when converting values to be used in a DB query */ int db_sqlite_val2str(const db_con_t* _c, const db_val_t* _v, char* _s, int* _len) { int l; if (!_c || !_v || !_s || !_len || !*_len) { LM_ERR("invalid parameter value\n"); return -1; } if (VAL_NULL(_v)) { if (*_len < sizeof("NULL")) { LM_ERR("buffer too small\n"); return -1; } *_len = snprintf(_s, *_len, "NULL"); return 0; } switch(VAL_TYPE(_v)) { case DB_INT: if (db_int2str(VAL_INT(_v), _s, _len) < 0) { LM_ERR("error while converting string to int\n"); return -2; } else { return 0; } break; case DB_BIGINT: if (db_bigint2str(VAL_BIGINT(_v), _s, _len) < 0) { LM_ERR("error while converting bigint to string\n"); return -2; } else { return 0; } break; case DB_BITMAP: if (db_int2str(VAL_BITMAP(_v), _s, _len) < 0) { LM_ERR("error while converting string to int\n"); return -3; } else { return 0; } break; case DB_DOUBLE: if (db_double2str(VAL_DOUBLE(_v), _s, _len) < 0) { LM_ERR("error while converting string to double\n"); return -4; } else { return 0; } break; case DB_STRING: l = strlen(VAL_STRING(_v)); if (*_len < l ) { LM_ERR("Destination buffer too short for string\n"); return -4; } else { sqlite3_snprintf(SQL_BUF_LEN, _s, "'%q'", VAL_STRING(_v)); *_len = strlen(_s); _s += strlen(_s); return 0; } break; case DB_STR: l = VAL_STR(_v).len; if (*_len < l) { LM_ERR("Destination buffer too short for str\n"); return -5; } else { sqlite3_snprintf(SQL_BUF_LEN, _s, "'%.*q'", VAL_STR(_v).len, VAL_STR(_v).s); *_len = strlen(_s); _s += strlen(_s); return 0; } break; case DB_DATETIME: if (db_time2str(VAL_TIME(_v), _s, _len) < 0) { LM_ERR("error while converting string to time_t\n"); return -7; } else { return 0; } break; case DB_BLOB: l = VAL_BLOB(_v).len; if (*_len < l) { LM_ERR("Destination buffer too short for blob\n"); return -7; } else { sqlite3_snprintf(SQL_BUF_LEN, _s, "'%.*q'", VAL_BLOB(_v).len, VAL_BLOB(_v).s); *_len = strlen(_s); _s += strlen(_s); return 0; } break; default: LM_DBG("unknown data type\n"); return -9; } } opensips-2.2.2/modules/db_sqlite/val.h000066400000000000000000000021571300170765700177410ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #ifndef VAL_H #define VAL_H #include "../../db/db_val.h" #include "../../db/db.h" /** * Used when converting result from a query */ int db_sqlite_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len); #endif /* VAL_H */ opensips-2.2.2/modules/db_text/000077500000000000000000000000001300170765700164645ustar00rootroot00000000000000opensips-2.2.2/modules/db_text/Makefile000066400000000000000000000030751300170765700201310ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile # extra debug messages DEFS+=-DDBT_EXTRA_DEBUG SCRIPTS_DIR?=../../scripts/ include ../../Makefile.defs auto_gen= NAME=db_text.so LIBS= include ../../Makefile.modules install_module_custom: mkdir -p $(modules_prefix)/$(lib_dir)/opensipsctl ; \ sed -e "s#/usr/local/share/opensips/#$(data_target)#g" \ < $(SCRIPTS_DIR)/opensipsctl.dbtext > /tmp/opensipsctl.dbtext ; \ $(INSTALL_CFG) /tmp/opensipsctl.dbtext \ $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsctl.dbtext ; \ rm -fr /tmp/opensipsctl.dbtext ; \ sed -e "s#/usr/local/share/opensips#$(data_target)#g" \ < $(SCRIPTS_DIR)/opensipsdbctl.dbtext > /tmp/opensipsdbctl.dbtext ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/opensipsctl/opensipsdbctl.dbtext ; \ $(INSTALL_CFG) /tmp/opensipsdbctl.dbtext $(modules_prefix)/$(lib_dir)/opensipsctl/ ; \ rm -fr /tmp/opensipsdbctl.dbtext ; \ mkdir -p $(modules_prefix)/$(lib_dir)/opensipsctl/dbtextdb ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/opensipsctl/dbtextdb/dbtextdb.py ; \ $(INSTALL_BIN) $(SCRIPTS_DIR)/dbtextdb/dbtextdb.py $(modules_prefix)/$(lib_dir)/opensipsctl/dbtextdb/ ; \ mkdir -p $(data_prefix)/$(data_dir)/dbtext/opensips ; \ for FILE in $(wildcard $(SCRIPTS_DIR)/dbtext/opensips/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/dbtext/opensips/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/dbtext/opensips/`basename "$$FILE"` ; \ fi ;\ done ;\ opensips-2.2.2/modules/db_text/README000066400000000000000000000303221300170765700173440ustar00rootroot00000000000000DBTEXT Module Daniel-Constantin Mierla Edited by Ovidiu Sas Edited by Daniel-Constantin Mierla Copyright © 2003, 2004 FhG FOKUS Revision History Revision $Revision: 9528 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Design of db_text engine 1.1.2. Internal format of a db_text table 1.1.3. Existing limitations 1.2. Dependencies 1.2.1. OpenSIPS modules 1.2.2. External libraries or applications 1.3. Exported Parameters 1.3.1. db_mode (integer) 1.4. Exported Functions 1.5. Exported MI Functions 1.5.1. dbt_dump 1.5.2. dbt_reload 1.6. Installation and Running 1.6.1. Using db_text with basic OpenSIPS configuration 2. Developer Guide List of Examples 1.1. Sample of a db_text table 1.2. Minimal OpenSIPS location db_text table definition 1.3. Minimal OpenSIPS subscriber db_text table example 1.4. Set db_mode parameter 1.5. Load the db_text module 1.6. Definition of 'subscriber' table (one line) 1.7. Definition of 'location' and 'aliases' tables (one line) 1.8. Definition of 'version' table and sample records 1.9. Configuration file Chapter 1. Admin Guide 1.1. Overview The module implements a simplified database engine based on text files. It can be used by OpenSIPS DB interface instead of other database module (like MySQL). The module is meant for use in demos or small devices that do not support other DB modules. It keeps everything in memory and if you deal with large amount of data you may run quickly out of memory. Also, it has not implemented all standard database facilities (like order by), it includes minimal functionality to work properly with OpenSIPS NOTE: the timestamp is printed in an integer value from time_t structure. If you use it in a system that cannot do this conversion, it will fail (support for such situation is in to-do list). NOTE: even when is in non-caching mode, the module does not write back to hard drive after changes. In this mode, the module checks if the corresponding file on disk has changed, and reloads it. The write on disk happens at OpenSIPS shut down. 1.1.1. Design of db_text engine The db_text database system architecture: * a database is represented by a directory in the local file system. NOTE: when you use db_text in OpenSIPS, the database URL for modules must be the path to the directory where the table-files are located, prefixed by “text://â€, e.g., “text:///var/dbtext/opensipsâ€. If there is no “/†after “text://†then “CFG_DIR/†is inserted at the beginning of the database path. So, either you provide an absolute path to database directory or a relative one to “CFG_DIR†directory. * a table is represented by a text file inside database directory. 1.1.2. Internal format of a db_text table First line is the definition of the columns. Each column must be declared as follows: * the name of column must not include white spaces. * the format of a column definition is: name(type,attr). * between two column definitions must be a white space, e.g., “first_name(str) last_name(str)â€. * the type of a column can be: + int - integer numbers. + double - real numbers with two decimals. + str - strings with maximum size of 4KB. * a column can have one of the attributes: + auto - only for 'int' columns, the maximum value in that column is incremented and stored in this field if it is not provided in queries. + null - accept null values in column fields. + if no attribute is set, the fields of the column cannot have null value. * each other line is a row with data. The line ends with “\nâ€. * the fields are separated by “:â€. * no value between two ':' (or between ':' and start/end of a row) means “null†value. * next characters must be escaped in strings: “\nâ€, “\râ€, “\tâ€, “:â€. * 0 -- the zero value must be escaped too. Example 1.1. Sample of a db_text table ... id(int,auto) name(str) flag(double) desc(str,null) 1:nick:0.34:a\tgood\: friend 2:cole:-3.75:colleague 3:bob:2.50: ... Example 1.2. Minimal OpenSIPS location db_text table definition ... username(str) contact(str) expires(int) q(double) callid(str) cseq(int) ... Example 1.3. Minimal OpenSIPS subscriber db_text table example ... username(str) password(str) ha1(str) domain(str) ha1b(str) suser:supasswd:xxx:alpha.org:xxx ... 1.1.3. Existing limitations This database interface don't support the data insertion with default values. All such values specified in the database template are ignored. So its advisable to specify all data for a column at insertion operations. 1.2. Dependencies 1.2.1. OpenSIPS modules The next modules must be loaded before this module: * none. 1.2.2. External libraries or applications The next libraries or applications must be installed before running OpenSIPS with this module: * none. 1.3. Exported Parameters None. 1.3.1. db_mode (integer) Set caching mode (0) or non-caching mode (1). In caching mode, data is loaded at startup. In non-caching mode, the module check every time a table is requested whether the corresponding file on disk has changed, and if yes, will re-load table from file. Default value is “0â€. Example 1.4. Set db_mode parameter ... modparam("db_text", "db_mode", 1) ... 1.4. Exported Functions None. 1.5. Exported MI Functions 1.5.1. dbt_dump Write back to hard drive modified tables. Name: dbt_dump. Parameters: none MI FIFO Command Format: :dbt_dump:_reply_fifo_file_ _empty_line_ 1.5.2. dbt_reload Causes db_text module to reload cached tables from disk. Depending on parameters it could be a whole cache or a specified database or a single table. If any table cannot be reloaded from disk - the old version preserved and error reported. Name: dbt_reload. Parameters: * db_name (optional) - database name to reload. * table_name (optional, but cannot be present without the db_name parameter) - specific table to reload. MI FIFO Command Format: :dbt_reload:_reply_fifo_file_ _empty_line_ :dbt_reload:_reply_fifo_file_ /path/to/dbtext/database _empty_line_ :dbt_reload:_reply_fifo_file_ /path/to/dbtext/database table_name _empty_line_ 1.6. Installation and Running Compile the module and load it instead of mysql or other DB modules. REMINDER: when you use db_text in OpenSIPS, the database URL for modules must be the path to the directory where the table-files are located, prefixed by “text://â€, e.g., “text:///var/dbtext/opensipsâ€. If there is no “/†after “text://†then “CFG_DIR/†is inserted at the beginning of the database path. So, either you provide an absolute path to database directory or a relative one to “CFG_DIR†directory. Example 1.5. Load the db_text module ... loadmodule "/path/to/opensips/modules/db_text.so" ... modparam("module_name", "database_URL", "text:///path/to/dbtext/database ") ... 1.6.1. Using db_text with basic OpenSIPS configuration Here are the definitions for most important table as well as a basic configuration file to use db_text with OpenSIPS. The table structures may change in time and you will have to adjust next examples. You have to populate the table 'subscriber' by hand with user profiles in order to have authentication. To use with the given configuration file, the table files must be placed in the '/tmp/opensipsdb' directory. Example 1.6. Definition of 'subscriber' table (one line) ... username(str) domain(str) password(str) first_name(str) last_name(str) p hone(str) email_address(str) datetime_created(int) datetime_modified(int ) confirmation(str) flag(str) sendnotification(str) greeting(str) ha1(st r) ha1b(str) perms(str) allow_find(str) timezone(str,null) rpid(str,null ) ... Example 1.7. Definition of 'location' and 'aliases' tables (one line) ... username(str) domain(str,null) contact(str,null) received(str) expires(i nt,null) q(double,null) callid(str,null) cseq(int,null) last_modified(st r) flags(int) user_agent(str) socket(str) ... Example 1.8. Definition of 'version' table and sample records ... table_name(str) table_version(int) subscriber:3 location:6 aliases:6 ... Example 1.9. Configuration file ... # # simple quick-start config script with dbtext # # ----------- global configuration parameters ------------------------ #debug_mode=yes children=4 check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) listen=udp:10.100.100.1:5060 # ------------------ module loading ---------------------------------- # use dbtext database loadmodule "modules/dbtext/dbtext.so" loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/textops/textops.so" loadmodule "modules/textops/mi_fifo.so" # modules for digest authentication loadmodule "modules/auth/auth.so" loadmodule "modules/auth_db/auth_db.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- usrloc params -- # use dbtext database for persistent storage modparam("usrloc", "db_mode", 2) modparam("usrloc|auth_db", "db_url", "text:///tmp/opensipsdb") # -- auth params -- # modparam("auth_db", "calculate_ha1", 1) modparam("auth_db", "password_column", "password") modparam("auth_db", "user_column", "username") modparam("auth_db", "domain_column", "domain") # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); exit; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); exit; }; if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); route(1); exit; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { # digest authentication if (!www_authorize("", "subscriber")) { www_challenge("", "0"); exit; }; save("location"); exit; }; lookup("aliases"); if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); exit; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; }; append_hf("P-hint: usrloc applied\r\n"); route(1); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } ... Chapter 2. Developer Guide Once you have the module loaded, you can use the API specified by OpenSIPS DB interface. opensips-2.2.2/modules/db_text/dbt_api.c000066400000000000000000000135501300170765700202360ustar00rootroot00000000000000/* * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-05 created by Daniel * */ #include #include "../../db/db.h" #include "../../mem/mem.h" #include "dbt_res.h" #include "dbt_api.h" int dbt_use_table(db_con_t* _h, const str* _t) { return db_use_table(_h, _t); } /* * Get and convert columns from a result */ static int dbt_get_columns(db_con_t* _h, db_res_t* _r) { int col; if (!_h || !_r) { LM_ERR("invalid parameter\n"); return -1; } RES_COL_N(_r) = DBT_CON_RESULT(_h)->nrcols; if (!RES_COL_N(_r)) { LM_ERR("no columns\n"); return -2; } if (db_allocate_columns(_r, RES_COL_N(_r)) != 0) { LM_ERR("could not allocate columns"); return -3; } for(col = 0; col < RES_COL_N(_r); col++) { RES_NAMES(_r)[col]->s = DBT_CON_RESULT(_h)->colv[col].name.s; RES_NAMES(_r)[col]->len = DBT_CON_RESULT(_h)->colv[col].name.len; switch(DBT_CON_RESULT(_h)->colv[col].type) { case DB_STR: case DB_STRING: case DB_BLOB: case DB_INT: case DB_BIGINT: case DB_DATETIME: case DB_DOUBLE: RES_TYPES(_r)[col] = DBT_CON_RESULT(_h)->colv[col].type; break; default: LM_WARN("unhandled data type column (%.*s) type id (%d), " "use STR as default\n", RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s, DBT_CON_RESULT(_h)->colv[col].type); RES_TYPES(_r)[col] = DB_STR; break; } } return 0; } /* * Convert a row from result into db API representation */ static int dbt_convert_row(db_con_t* _h, db_res_t* _res, db_row_t* _r) { int i; if (!_h || !_r || !_res) { LM_ERR("invalid parameter value\n"); return -1; } ROW_N(_r) = RES_COL_N(_res); for(i = 0; i < RES_COL_N(_res); i++) { (ROW_VALUES(_r)[i]).nul = DBT_CON_ROW(_h)->fields[i].nul; switch(RES_TYPES(_res)[i]) { case DB_INT: VAL_INT(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.int_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_INT; break; case DB_BIGINT: VAL_BIGINT(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.bigint_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_BIGINT; break; case DB_DOUBLE: VAL_DOUBLE(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.double_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_DOUBLE; break; case DB_STRING: VAL_STR(&(ROW_VALUES(_r)[i])).s = DBT_CON_ROW(_h)->fields[i].val.str_val.s; VAL_STR(&(ROW_VALUES(_r)[i])).len = DBT_CON_ROW(_h)->fields[i].val.str_val.len; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_STRING; VAL_FREE(&(ROW_VALUES(_r)[i])) = 0; break; case DB_STR: VAL_STR(&(ROW_VALUES(_r)[i])).s = DBT_CON_ROW(_h)->fields[i].val.str_val.s; VAL_STR(&(ROW_VALUES(_r)[i])).len = DBT_CON_ROW(_h)->fields[i].val.str_val.len; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_STR; VAL_FREE(&(ROW_VALUES(_r)[i])) = 0; break; case DB_DATETIME: VAL_INT(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.int_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_DATETIME; break; case DB_BLOB: VAL_STR(&(ROW_VALUES(_r)[i])).s = DBT_CON_ROW(_h)->fields[i].val.str_val.s; VAL_STR(&(ROW_VALUES(_r)[i])).len = DBT_CON_ROW(_h)->fields[i].val.str_val.len; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_BLOB; VAL_FREE(&(ROW_VALUES(_r)[i])) = 0; break; case DB_BITMAP: VAL_INT(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.bitmap_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_INT; break; } } return 0; } /* * Convert rows from internal to db API representation */ static int dbt_convert_rows(db_con_t* _h, db_res_t* _r) { int col; dbt_row_p _rp = NULL; if (!_h || !_r) { LM_ERR("invalid parameter\n"); return -1; } RES_ROW_N(_r) = DBT_CON_RESULT(_h)->nrrows; if (!RES_ROW_N(_r)) { return 0; } if (db_allocate_rows( _r, RES_ROW_N(_r))!=0) { LM_ERR("no private memory left\n"); return -2; } col = 0; _rp = DBT_CON_RESULT(_h)->rows; while(_rp) { DBT_CON_ROW(_h) = _rp; if (!DBT_CON_ROW(_h)) { LM_ERR("failed to get current row\n"); RES_ROW_N(_r) = col; db_free_rows(_r); return -3; } if (dbt_convert_row(_h, _r, &(RES_ROWS(_r)[col])) < 0) { LM_ERR("failed to convert row #%d\n", col); RES_ROW_N(_r) = col; db_free_rows(_r); return -4; } col++; _rp = _rp->next; } return 0; } /* * Fill the structure with data from database */ static int dbt_convert_result(db_con_t* _h, db_res_t* _r) { if (!_h || !_r) { LM_ERR("invalid parameter\n"); return -1; } if (dbt_get_columns(_h, _r) < 0) { LM_ERR("failed to get column names\n"); return -2; } if (dbt_convert_rows(_h, _r) < 0) { LM_ERR("failed to convert rows\n"); db_free_columns(_r); return -3; } return 0; } /* * Retrieve result set */ int dbt_get_result(db_con_t* _h, db_res_t** _r) { if (!_h || !_r) { LM_ERR("invalid parameter value\n"); return -1; } if (!DBT_CON_RESULT(_h)) { LM_ERR("failed to get result\n"); *_r = 0; return -3; } *_r = db_new_result(); if (*_r == 0) { LM_ERR("no private memory left\n"); return -2; } if (dbt_convert_result(_h, *_r) < 0) { LM_ERR("failed to convert result\n"); pkg_free(*_r); return -4; } return 0; } opensips-2.2.2/modules/db_text/dbt_api.h000066400000000000000000000022461300170765700202430ustar00rootroot00000000000000/* * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-05 created by Daniel * */ #ifndef _DBT_API_H_ #define _DBT_API_H_ #include "../../db/db_op.h" #include "../../db/db_res.h" #include "../../db/db_con.h" #include "../../db/db_row.h" /* * Retrieve result set */ int dbt_get_result(db_con_t* _h, db_res_t** _r); int dbt_use_table(db_con_t* _h, const str* _t); #endif opensips-2.2.2/modules/db_text/dbt_base.c000066400000000000000000000245171300170765700204040ustar00rootroot00000000000000/* * DBText module core functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-30 created by Daniel * */ #include #include "../../str.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "dbtext.h" #include "dbt_res.h" #include "dbt_api.h" #ifndef CFG_DIR #define CFG_DIR "/tmp" #endif #define DBT_ID "text://" #define DBT_ID_LEN (sizeof(DBT_ID)-1) #define DBT_PATH_LEN 256 /* * Initialize database connection */ db_con_t* dbt_init(const str* _sqlurl) { db_con_t* _res; str _s; char dbt_path[DBT_PATH_LEN]; if (!_sqlurl || !_sqlurl->s) { LM_ERR("invalid parameter value\n"); return NULL; } _s.s = _sqlurl->s; _s.len = _sqlurl->len; if(_s.len <= DBT_ID_LEN || strncmp(_s.s, DBT_ID, DBT_ID_LEN)!=0) { LM_ERR("invalid database URL - should be:" " <%s[/]path/to/directory>\n", DBT_ID); return NULL; } /* * it would be possible to use the _sqlurl here, but the core API is * defined with a const str*, so this code would be not valid. */ _s.s += DBT_ID_LEN; _s.len -= DBT_ID_LEN; if(_s.s[0]!='/') { if(sizeof(CFG_DIR)+_s.len+2 > DBT_PATH_LEN) { LM_ERR("path to database is too long\n"); return NULL; } strcpy(dbt_path, CFG_DIR); dbt_path[sizeof(CFG_DIR)] = '/'; strncpy(&dbt_path[sizeof(CFG_DIR)+1], _s.s, _s.len); _s.len += sizeof(CFG_DIR); _s.s = dbt_path; } _res = pkg_malloc(sizeof(db_con_t)+sizeof(dbt_con_t)); if (!_res) { LM_ERR("no pkg memory left\n"); return NULL; } memset(_res, 0, sizeof(db_con_t) + sizeof(dbt_con_t)); _res->tail = (unsigned long)((char*)_res+sizeof(db_con_t)); LM_INFO("using database at: %.*s\n", _s.len, _s.s); DBT_CON_CONNECTION(_res) = dbt_cache_get_db(&_s); if (!DBT_CON_CONNECTION(_res)) { LM_ERR("cannot get the link to database\n"); return NULL; } return _res; } /* * Close a database connection */ void dbt_close(db_con_t* _h) { if (!_h) { LM_ERR("invalid parameter value\n"); return; } if (DBT_CON_RESULT(_h)) dbt_result_free(DBT_CON_RESULT(_h)); pkg_free(_h); return; } /* * Free all memory allocated by get_result */ int dbt_free_result(db_con_t* _h, db_res_t* _r) { if ((!_h) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } if(db_free_result(_r) < 0) { LM_ERR("unable to free result structure\n"); return -1; } if(dbt_result_free(DBT_CON_RESULT(_h)) < 0) { LM_ERR("unable to free internal structure\n"); return -1; } DBT_CON_RESULT(_h) = NULL; return 0; } /* * Query table for specified rows * _h: structure representing database connection * _k: key names * _op: operators * _v: values of the keys that must match * _c: column names to return * _n: number of key=values pairs to compare * _nc: number of columns to return * _o: order by the specified column */ int dbt_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r) { dbt_table_p _tbc = NULL; dbt_row_p _drp = NULL; dbt_result_p _dres = NULL; int *lkey=NULL, *lres=NULL; if ((!_h) || (!_r) || !CON_TABLE(_h)) { LM_ERR("invalid parameters\n"); return -1; } *_r = NULL; /* lock database */ _tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(!_tbc) { LM_ERR("table '%.*s' does not exist!\n", CON_TABLE(_h)->len, CON_TABLE(_h)->s); return -1; } if(_tbc->nrcols < _nc) { LM_ERR("bad columns for table '%.*s' (have %d, need %d)\n", CON_TABLE(_h)->len, CON_TABLE(_h)->s, _tbc->nrcols, _nc); goto error; } if(_k) { lkey = dbt_get_refs(_tbc, _k, _n); if(!lkey) goto error; } if(_c) { lres = dbt_get_refs(_tbc, _c, _nc); if(!lres) goto error; } LM_DBG("new res with %d cols\n", _nc); _dres = dbt_result_new(_tbc, lres, _nc); if(!_dres) goto error; _drp = _tbc->rows; while(_drp) { if(dbt_row_match(_tbc, _drp, lkey, _op, _v, _n)) { if(dbt_result_extract_fields(_tbc, _drp, lres, _dres)) { LM_ERR("failed to extract result fields!\n"); goto clean; } } _drp = _drp->next; } dbt_table_update_flags(_tbc, DBT_TBFL_ZERO, DBT_FL_IGN, 1); /* unlock database */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); /* dbt_result_print(_dres); */ DBT_CON_RESULT(_h) = _dres; if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); return dbt_get_result(_h, _r); error: /* unlock database */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); LM_ERR("failed to query the table!\n"); return -1; clean: /* unlock database */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); if(_dres) dbt_result_free(_dres); return -1; } /* * Raw SQL query -- is not the case to have this method */ int dbt_raw_query(db_con_t* _h, char* _s, db_res_t** _r) { *_r = NULL; return -1; } /* * Insert a row into table */ int dbt_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n) { dbt_table_p _tbc = NULL; dbt_row_p _drp = NULL; int *lkey=NULL, i, j; if (!_h || !CON_TABLE(_h)) { LM_ERR("invalid parameter\n"); return -1; } if(!_k || !_v || _n<=0) { LM_ERR("no key-value to insert\n"); return -1; } /* lock database */ _tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(!_tbc) { LM_ERR("table does not exist!\n"); return -1; } if(_tbc->nrcols<_n) { LM_ERR("more values than columns!!\n"); goto error; } if(_k) { lkey = dbt_get_refs(_tbc, _k, _n); if(!lkey) goto error; } _drp = dbt_row_new(_tbc->nrcols); if(!_drp) { LM_ERR("no shm memory for a new row!!\n"); goto error; } for(i=0; i<_n; i++) { j = (lkey)?lkey[i]:i; if(dbt_is_neq_type(_tbc->colv[j]->type, _v[i].type)) { LM_ERR("incompatible types v[%d] - c[%d]!\n", i, j); goto clean; } if(_v[i].type == DB_STRING && !_v[i].nul) _v[i].val.str_val.len = strlen(_v[i].val.string_val); if(dbt_row_set_val(_drp, &(_v[i]), _tbc->colv[j]->type, j)) { LM_ERR("cannot set v[%d] in c[%d]!\n", i, j); goto clean; } } if(dbt_table_add_row(_tbc, _drp)) { LM_ERR("cannot insert the new row!!\n"); goto clean; } /* dbt_print_table(_tbc, NULL); */ /* unlock databse */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(lkey) pkg_free(lkey); return 0; error: /* unlock database */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(lkey) pkg_free(lkey); LM_ERR("failed to insert row in table!\n"); return -1; clean: if(lkey) pkg_free(lkey); if(_drp) // free row dbt_row_free(_tbc, _drp); /* unlock database */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); return -1; } /* * Delete a row from table */ int dbt_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n) { dbt_table_p _tbc = NULL; dbt_row_p _drp = NULL, _drp0 = NULL; int *lkey = NULL; if (!_h || !CON_TABLE(_h)) { LM_ERR("invalid parameters\n"); return -1; } /* lock database */ _tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(!_tbc) { LM_ERR("failed to load table <%.*s>!\n", CON_TABLE(_h)->len, CON_TABLE(_h)->s); return -1; } if(!_k || !_v || _n<=0) { LM_DBG("deleting all records\n"); dbt_table_free_rows(_tbc); /* unlock databse */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); return 0; } lkey = dbt_get_refs(_tbc, _k, _n); if(!lkey) goto error; _drp = _tbc->rows; while(_drp) { _drp0 = _drp->next; if(dbt_row_match(_tbc, _drp, lkey, _o, _v, _n)) { // delete row if(_drp->prev) (_drp->prev)->next = _drp->next; else _tbc->rows = _drp->next; if(_drp->next) (_drp->next)->prev = _drp->prev; _tbc->nrrows--; // free row dbt_row_free(_tbc, _drp); } _drp = _drp0; } dbt_table_update_flags(_tbc, DBT_TBFL_MODI, DBT_FL_SET, 1); /* dbt_print_table(_tbc, NULL); */ /* unlock database */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(lkey) pkg_free(lkey); return 0; error: /* unlock database */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); LM_ERR("failed to delete from table!\n"); return -1; } /* * Update a row in table */ int dbt_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un) { dbt_table_p _tbc = NULL; dbt_row_p _drp = NULL; int i; int *lkey=NULL, *lres=NULL; if (!_h || !CON_TABLE(_h) || !_uk || !_uv || _un <= 0) { LM_ERR("invalid parameters\n"); return -1; } /* lock database */ _tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(!_tbc) { LM_ERR("table does not exist!\n"); return -1; } if(_k) { lkey = dbt_get_refs(_tbc, _k, _n); if(!lkey) goto error; } lres = dbt_get_refs(_tbc, _uk, _un); if(!lres) goto error; _drp = _tbc->rows; while(_drp) { if(dbt_row_match(_tbc, _drp, lkey, _o, _v, _n)) { // update fields for(i=0; i<_un; i++) { if(dbt_is_neq_type(_tbc->colv[lres[i]]->type, _uv[i].type)) { LM_ERR("incompatible types!\n"); goto error; } if(dbt_row_update_val(_drp, &(_uv[i]), _tbc->colv[lres[i]]->type, lres[i])) { LM_ERR("cannot set v[%d] in c[%d]!\n", i, lres[i]); goto error; } } } _drp = _drp->next; } dbt_table_update_flags(_tbc, DBT_TBFL_MODI, DBT_FL_SET, 1); /* dbt_print_table(_tbc, NULL); */ /* unlock database */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); return 0; error: /* unlock database */ dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h)); if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); LM_ERR("failed to update the table!\n"); return -1; } opensips-2.2.2/modules/db_text/dbt_file.c000066400000000000000000000337071300170765700204120ustar00rootroot00000000000000/* * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-03 created by Daniel * */ #include #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "dbt_util.h" #include "dbt_lib.h" /** * -1 - error * 0 - no change * 1 - changed */ int dbt_check_mtime(const str *tbn, const str *dbn, time_t *mt) { char path[512]; struct stat s; int ret = 0; path[0] = 0; if(dbn && dbn->s && dbn->len>0) { if(dbn->len+tbn->len<511) { strncpy(path, dbn->s, dbn->len); path[dbn->len] = '/'; strncpy(path+dbn->len+1, tbn->s, tbn->len); path[dbn->len+tbn->len+1] = 0; } } if(path[0] == 0) { strncpy(path, tbn->s, tbn->len); path[tbn->len] = 0; } if(stat(path, &s) == 0) { if((int)s.st_mtime > (int)*mt) { ret = 1; *mt = s.st_mtime; LM_DBG("[%.*s] was updated\n", tbn->len, tbn->s); } } else { LM_DBG("stat failed [%d, %s] on [%.*s]\n", errno, strerror(errno), tbn->len, tbn->s); ret = -1; } return ret; } /** * */ dbt_table_p dbt_load_file(const str *tbn, const str *dbn) { FILE *fin=NULL; char path[512], buf[4096]; int c, crow, ccol, bp, sign, max_auto; dbt_val_t dtval; dbt_table_p dtp = NULL; dbt_column_p colp, colp0 = NULL; dbt_row_p rowp, rowp0 = NULL; enum {DBT_FLINE_ST, DBT_NLINE_ST, DBT_DATA_ST} state; LM_DBG("request for table [%.*s]\n", tbn->len, tbn->s); if(!tbn || !tbn->s || tbn->len<=0 || tbn->len>=255) return NULL; path[0] = 0; if(dbn && dbn->s && dbn->len>0) { LM_DBG("db is [%.*s]\n", dbn->len, dbn->s); if(dbn->len+tbn->len<511) { strncpy(path, dbn->s, dbn->len); path[dbn->len] = '/'; strncpy(path+dbn->len+1, tbn->s, tbn->len); path[dbn->len+tbn->len+1] = 0; } } if(path[0] == 0) { strncpy(path, tbn->s, tbn->len); path[tbn->len] = 0; } LM_DBG("loading file [%s]\n", path); fin = fopen(path, "rt"); if(!fin) return NULL; dtp = dbt_table_new(tbn, dbn, path); if(!dtp) goto done; state = DBT_FLINE_ST; crow = ccol = -1; colp = colp0 = NULL; rowp = rowp0 = NULL; c = fgetc(fin); max_auto = 0; while(c!=EOF) { switch(state) { case DBT_FLINE_ST: //LM_DBG("state FLINE!\n"); bp = 0; while(c==DBT_DELIM_C) c = fgetc(fin); if(c==DBT_DELIM_R && !colp0) goto clean; if(c==DBT_DELIM_R) { if(dtp->nrcols <= 0) goto clean; dtp->colv = (dbt_column_p*) shm_malloc(dtp->nrcols*sizeof(dbt_column_p)); if(!dtp->colv) goto clean; colp0 = dtp->cols; for(ccol=0; ccolnrcols && colp0; ccol++) { dtp->colv[ccol] = colp0; colp0 = colp0->next; } state = DBT_NLINE_ST; break; } while(c!=DBT_DELIM_C && c!='(' && c!=DBT_DELIM_R) { if(c==EOF) goto clean; buf[bp++] = c; c = fgetc(fin); } colp = dbt_column_new(buf, bp); if(!colp) goto clean; //LM_DBG("new col [%.*s]\n", bp, buf); while(c==DBT_DELIM_C) c = fgetc(fin); if(c!='(') goto clean; c = fgetc(fin); while(c==DBT_DELIM_C) c = fgetc(fin); switch(c) { case 's': case 'S': colp->type = DB_STR; LM_DBG("column[%d] is STR!\n", ccol+1); break; case 'i': case 'I': colp->type = DB_INT; LM_DBG("column[%d] is INT!\n", ccol+1); break; case 'l': case 'L': colp->type = DB_BIGINT; LM_DBG("column[%d] is BIGINT!\n", ccol+1); break; case 'd': case 'D': colp->type = DB_DOUBLE; LM_DBG("column[%d] is DOUBLE!\n", ccol+1); break; case 'b': case 'B': colp->type = DB_BLOB; LM_DBG("column[%d] is BLOB!\n", ccol+1); break; case 't': case 'T': colp->type = DB_DATETIME; LM_DBG("column[%d] is TIME!\n", ccol+1); break; default: LM_DBG("wrong column type!\n"); goto clean; } while(c!='\n' && c!=EOF && c!=')' && c!= ',') { if(colp->type == DB_STR && (c=='i'|| c=='I')) { colp->type = DB_STRING; LM_DBG("column[%d] is actually STRING!\n", ccol+1); } c = fgetc(fin); } if(c==',') { //LM_DBG("c=%c!\n", c); c = fgetc(fin); while(c==DBT_DELIM_C) c = fgetc(fin); if(c=='N' || c=='n') { //LM_DBG("NULL flag set!\n"); colp->flag |= DBT_FLAG_NULL; } else if((colp->type==DB_INT || colp->type==DB_BIGINT) && dtp->auto_col<0 && (c=='A' || c=='a')) { //LM_DBG("AUTO flag set!\n"); colp->flag |= DBT_FLAG_AUTO; dtp->auto_col = ccol+1; } else goto clean; while(c!=')' && c!=DBT_DELIM_R && c!=EOF) c = fgetc(fin); } if(c == ')') { //LM_DBG("c=%c!\n", c); if(colp0) { colp->prev = colp0; colp0->next = colp; } else dtp->cols = colp; colp0 = colp; dtp->nrcols++; c = fgetc(fin); /* at least 1 column was found. eat all trailing spaces */ while (c == DBT_DELIM_C) c = fgetc(fin); /* properly load files which do not end in a newline */ if (c == EOF) c = DBT_DELIM_R; } else goto clean; ccol++; break; case DBT_NLINE_ST: //LM_DBG("state NLINE!\n"); while(c==DBT_DELIM_R) c = fgetc(fin); if(rowp) { if(dbt_table_check_row(dtp, rowp)) goto clean; if(!rowp0) dtp->rows = rowp; else { rowp0->next = rowp; rowp->prev = rowp0; } rowp0 = rowp; dtp->nrrows++; } if(c==EOF) break; crow++; ccol = 0; rowp = dbt_row_new(dtp->nrcols); if(!rowp) goto clean; state = DBT_DATA_ST; break; case DBT_DATA_ST: //LM_DBG("state DATA!\n"); //while(c==DBT_DELIM) // c = fgetc(fin); if(ccol == dtp->nrcols && (c==DBT_DELIM_R || c==EOF)) { state = DBT_NLINE_ST; break; } if(ccol>= dtp->nrcols) goto clean; switch(dtp->colv[ccol]->type) { case DB_INT: case DB_BIGINT: case DB_DATETIME: //LM_DBG("INT/BIGINT/DATETIME value!\n"); dtval.val.bigint_val = 0; dtval.type = dtp->colv[ccol]->type; if(c==DBT_DELIM || (ccol==dtp->nrcols-1 && (c==DBT_DELIM_R || c==EOF))) dtval.nul = 1; else { dtval.nul = 0; sign = 1; if(c=='-') { sign = -1; c = fgetc(fin); } if(c<'0' || c>'9') goto clean; while(c>='0' && c<='9') { dtval.val.bigint_val=dtval.val.bigint_val*10+c-'0'; c = fgetc(fin); } dtval.val.bigint_val *= sign; //LM_DBG("data[%d,%d]=%d\n", crow, // ccol, dtval.val.bigint_val); } if(c!=DBT_DELIM && c!=DBT_DELIM_R && c!=EOF) goto clean; if(dbt_row_set_val(rowp,&dtval,dtp->colv[ccol]->type, ccol)) goto clean; if(ccol == dtp->auto_col) max_auto = (max_autonrcols-1 && (c==DBT_DELIM_R || c==EOF))) dtval.nul = 1; else { dtval.nul = 0; sign = 1; if(c=='-') { sign = -1; c = fgetc(fin); } if(c<'0' || c>'9') goto clean; while(c>='0' && c<='9') { dtval.val.double_val = dtval.val.double_val*10 + c - '0'; c = fgetc(fin); } if(c=='.') { c = fgetc(fin); bp = 1; while(c>='0' && c<='9') { bp *= 10; dtval.val.double_val+=((double)(c-'0'))/bp; c = fgetc(fin); } } dtval.val.double_val *= sign; //LM_DBG("data[%d,%d]=%10.2f\n", // crow, ccol, dtval.val.double_val); } if(c!=DBT_DELIM && c!=DBT_DELIM_R && c!=EOF) goto clean; if(dbt_row_set_val(rowp,&dtval,DB_DOUBLE,ccol)) goto clean; break; case DB_STR: case DB_STRING: case DB_BLOB: //LM_DBG("STR value!\n"); dtval.val.str_val.s = NULL; dtval.val.str_val.len = 0; dtval.type = dtp->colv[ccol]->type; bp = 0; if(c==DBT_DELIM || (ccol == dtp->nrcols-1 && (c == DBT_DELIM_R || c==EOF))) dtval.nul = 1; else { dtval.nul = 0; while(c!=DBT_DELIM && c!=DBT_DELIM_R && c!=EOF) { if(c=='\\') { c = fgetc(fin); switch(c) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case '\\': c = '\\'; break; case DBT_DELIM: c = DBT_DELIM; break; case '0': c = 0; break; default: goto clean; } } buf[bp++] = c; c = fgetc(fin); } dtval.val.str_val.s = buf; dtval.val.str_val.len = bp; //LM_DBG("data[%d,%d]=%.*s\n", /// crow, ccol, bp, buf); } if(c!=DBT_DELIM && c!=DBT_DELIM_R && c!=EOF) goto clean; if(dbt_row_set_val(rowp,&dtval,dtp->colv[ccol]->type, ccol)) goto clean; break; default: goto clean; } /* do not skip last row if it does not end with a newline */ if (c == EOF) c = DBT_DELIM_R; if(c==DBT_DELIM) c = fgetc(fin); ccol++; break; // state DBT_DATA_ST } } if(max_auto) dtp->auto_val = max_auto; done: if(fin) fclose(fin); return dtp; clean: /// ????? FILL IT IN - incomplete row/column // memory leak?!?! with last incomplete row if(fin) fclose(fin); LM_ERR("error at row=%d col=%d c=%c\n", crow+1, ccol+1, c); if(dtp) dbt_table_free(dtp); return NULL; } /** * */ int dbt_print_table(dbt_table_p _dtp, str *_dbn) { dbt_column_p colp = NULL; dbt_row_p rowp = NULL; FILE *fout = NULL; int ccol; char *p, path[512]; if(!_dtp || !_dtp->name.s || _dtp->name.len <= 0) return -1; if(!_dbn || !_dbn->s || _dbn->len <= 0) { fout = stdout; fprintf(fout, "\n Content of [%.*s::%.*s]\n", _dtp->dbname.len, _dtp->dbname.s, _dtp->name.len, _dtp->name.s); } else { if(_dtp->name.len+_dbn->len > 510) return -1; strncpy(path, _dbn->s, _dbn->len); path[_dbn->len] = '/'; strncpy(path+_dbn->len+1, _dtp->name.s, _dtp->name.len); path[_dbn->len+_dtp->name.len+1] = 0; fout = fopen(path, "wt"); if(!fout) return -1; } colp = _dtp->cols; while(colp) { switch(colp->type) { case DB_INT: fprintf(fout, "%.*s(int", colp->name.len, colp->name.s); break; case DB_BIGINT: fprintf(fout, "%.*s(long", colp->name.len, colp->name.s); break; case DB_DOUBLE: fprintf(fout, "%.*s(double", colp->name.len, colp->name.s); break; case DB_STR: fprintf(fout, "%.*s(str", colp->name.len, colp->name.s); break; case DB_STRING: fprintf(fout, "%.*s(string", colp->name.len, colp->name.s); break; case DB_BLOB: fprintf(fout, "%.*s(blob", colp->name.len, colp->name.s); break; case DB_DATETIME: fprintf(fout, "%.*s(time", colp->name.len, colp->name.s); break; default: if(fout!=stdout) fclose(fout); return -1; } if(colp->flag & DBT_FLAG_NULL) fprintf(fout,",null"); else if(colp->type==DB_INT && colp->flag & DBT_FLAG_AUTO) fprintf(fout,",auto"); fprintf(fout,")"); colp = colp->next; if(colp) fprintf(fout,"%c", DBT_DELIM_C); } fprintf(fout, "%c", DBT_DELIM_R); rowp = _dtp->rows; while(rowp) { for(ccol=0; ccol<_dtp->nrcols; ccol++) { switch(_dtp->colv[ccol]->type) { case DB_DATETIME: case DB_INT: if(!rowp->fields[ccol].nul) fprintf(fout,"%d", rowp->fields[ccol].val.int_val); break; case DB_BIGINT: if(!rowp->fields[ccol].nul) fprintf(fout,"%lld", rowp->fields[ccol].val.bigint_val); break; case DB_DOUBLE: if(!rowp->fields[ccol].nul) fprintf(fout, "%.2f", rowp->fields[ccol].val.double_val); break; case DB_STR: case DB_STRING: case DB_BLOB: if(!rowp->fields[ccol].nul) { p = rowp->fields[ccol].val.str_val.s; while(p < rowp->fields[ccol].val.str_val.s + rowp->fields[ccol].val.str_val.len) { switch(*p) { case '\n': fprintf(fout, "\\n"); break; case '\r': fprintf(fout, "\\r"); break; case '\t': fprintf(fout, "\\t"); break; case '\\': fprintf(fout, "\\\\"); break; case DBT_DELIM: fprintf(fout, "\\%c", DBT_DELIM); break; case '\0': fprintf(fout, "\\0"); break; default: fprintf(fout, "%c", *p); } p++; } } break; default: if(fout!=stdout) fclose(fout); return -1; } if(ccol<_dtp->nrcols-1) fprintf(fout, "%c",DBT_DELIM); } fprintf(fout, "%c", DBT_DELIM_R); rowp = rowp->next; } if(fout!=stdout) fclose(fout); return 0; } opensips-2.2.2/modules/db_text/dbt_lib.c000066400000000000000000000304271300170765700202350ustar00rootroot00000000000000/* * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-30 created by Daniel * */ #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "dbt_util.h" #include "dbt_lib.h" static dbt_cache_p *_dbt_cachedb = NULL; static gen_lock_t *_dbt_cachesem = NULL; static dbt_tbl_cachel_p _dbt_cachetbl = NULL; #define DBT_CACHETBL_SIZE 16 /** * */ int dbt_init_cache(void) { int i, j; if(!_dbt_cachesem) { /* init locks */ _dbt_cachesem = lock_alloc(); if(!_dbt_cachesem) { LM_CRIT("could not alloc a lock\n"); return -1; } if (lock_init(_dbt_cachesem)==0) { LM_CRIT("could not initialize a lock\n"); lock_dealloc(_dbt_cachesem); return -1; } } /* init pointer to caches list */ if (!_dbt_cachedb) { _dbt_cachedb = shm_malloc( sizeof(dbt_cache_p) ); if (!_dbt_cachedb) { LM_CRIT("no enough shm mem\n"); lock_dealloc(_dbt_cachesem); return -1; } *_dbt_cachedb = NULL; } /* init tables' hash table */ if (!_dbt_cachetbl) { _dbt_cachetbl = (dbt_tbl_cachel_p)shm_malloc(DBT_CACHETBL_SIZE* sizeof(dbt_tbl_cachel_t)); if(_dbt_cachetbl==NULL) { LM_CRIT("no enough shm mem\n"); lock_dealloc(_dbt_cachesem); shm_free(_dbt_cachedb); return -1; } memset(_dbt_cachetbl, 0, DBT_CACHETBL_SIZE*sizeof(dbt_tbl_cachel_t)); for(i=0; i=0; j--) lock_destroy(&_dbt_cachetbl[j].sem); lock_dealloc(_dbt_cachesem); shm_free(_dbt_cachedb); return -1; } } } return 0; } /** * */ dbt_cache_p dbt_cache_get_db(str *_s) { dbt_cache_p _dcache=NULL;; if(!_dbt_cachesem || !_dbt_cachedb) { LM_ERR("dbtext cache is not initialized! Check if you loaded" " dbtext before any other module that uses it\n"); return NULL; } if(!_s || !_s->s || _s->len<=0) return NULL; LM_DBG("looking for db %.*s!\n",_s->len,_s->s); lock_get(_dbt_cachesem); _dcache = *_dbt_cachedb; while(_dcache) { if(_dcache->name.len==_s->len && !strncasecmp(_dcache->name.s, _s->s, _s->len)) { LM_DBG("db already cached!\n"); goto done; } _dcache = _dcache->next; } if(!dbt_is_database(_s)) { LM_ERR("database [%.*s] does not exist!\n", _s->len, _s->s); goto done; } LM_DBG("new db!\n"); _dcache = (dbt_cache_p)shm_malloc(sizeof(dbt_cache_t)); if(!_dcache) { LM_ERR(" no shm memory for dbt_cache_t.\n"); goto done; } memset(_dcache, 0, sizeof(dbt_cache_t)); _dcache->name.s = (char*)shm_malloc((_s->len+1)*sizeof(char)); if(!_dcache->name.s) { LM_ERR(" no shm memory for s!!\n"); shm_free(_dcache); _dcache = NULL; goto done; } memcpy(_dcache->name.s, _s->s, _s->len); _dcache->name.s[_s->len] = '\0'; _dcache->name.len = _s->len; if(*_dbt_cachedb) _dcache->next = *_dbt_cachedb; *_dbt_cachedb = _dcache; done: lock_release(_dbt_cachesem); return _dcache; } /** * */ int dbt_cache_check_db(str *_s) { dbt_cache_p _dcache=NULL;; if(!_dbt_cachesem || !(*_dbt_cachedb) || !_s || !_s->s || _s->len<=0) return -1; lock_get(_dbt_cachesem); _dcache = *_dbt_cachedb; while(_dcache) { if(_dcache->name.len == _s->len && !strncasecmp(_dcache->name.s, _s->s, _s->len)) { lock_release(_dbt_cachesem); return 0; } _dcache = _dcache->next; } lock_release(_dbt_cachesem); return -1; } /** * */ int dbt_db_del_table(dbt_cache_p _dc, const str *_s, int sync) { dbt_table_p _tbc = NULL; int hash; int hashidx; if(!_dbt_cachetbl || !_dc || !_s || !_s->s || _s->len<=0) return -1; hash = core_hash(&_dc->name, _s, DBT_CACHETBL_SIZE); hashidx = hash % DBT_CACHETBL_SIZE; if(sync) lock_get(&_dbt_cachetbl[hashidx].sem); _tbc = _dbt_cachetbl[hashidx].dtp; while(_tbc) { if(_tbc->hash==hash && _tbc->dbname.len == _dc->name.len && _tbc->name.len == _s->len && !strncasecmp(_tbc->dbname.s, _dc->name.s, _dc->name.len) && !strncasecmp(_tbc->name.s, _s->s, _s->len)) { if(_tbc->prev) (_tbc->prev)->next = _tbc->next; else _dbt_cachetbl[hashidx].dtp = _tbc->next; if(_tbc->next) (_tbc->next)->prev = _tbc->prev; break; } _tbc = _tbc->next; } if(sync) lock_release(&_dbt_cachetbl[hashidx].sem); dbt_table_free(_tbc); return 0; } /** * */ dbt_table_p dbt_db_get_table(dbt_cache_p _dc, const str *_s) { dbt_table_p _tbc = NULL; int hash; int hashidx; if(!_dbt_cachetbl || !_dc || !_s || !_s->s || _s->len<=0) return NULL; hash = core_hash(&_dc->name, _s, DBT_CACHETBL_SIZE); hashidx = hash % DBT_CACHETBL_SIZE; lock_get(&_dbt_cachetbl[hashidx].sem); _tbc = _dbt_cachetbl[hashidx].dtp; while(_tbc) { if(_tbc->hash==hash && _tbc->dbname.len == _dc->name.len && _tbc->name.len == _s->len && !strncasecmp(_tbc->dbname.s, _dc->name.s, _dc->name.len) && !strncasecmp(_tbc->name.s, _s->s, _s->len)) { /* found - if cache mode or no-change, return */ if(db_mode==0 || dbt_check_mtime(_s, &(_dc->name), &(_tbc->mt))!=1) { LM_DBG("cache or mtime succeeded for [%.*s]\n", _tbc->name.len, _tbc->name.s); return _tbc; } break; } _tbc = _tbc->next; } /* new table */ if(_tbc) /* free old one */ { dbt_db_del_table(_dc, _s, 0); } _tbc = dbt_load_file(_s, &(_dc->name)); if(!_tbc) { lock_release(&_dbt_cachetbl[hashidx].sem); return NULL; } _tbc->hash = hash; _tbc->next = _dbt_cachetbl[hashidx].dtp; if(_dbt_cachetbl[hashidx].dtp) _dbt_cachetbl[hashidx].dtp->prev = _tbc; _dbt_cachetbl[hashidx].dtp = _tbc; /* table is locked */ return _tbc; } int dbt_release_table(dbt_cache_p _dc, const str *_s) { int hash; int hashidx; if(!_dbt_cachetbl || !_dc || !_s || !_s->s || _s->len<=0) return -1; hash = core_hash(&_dc->name, _s, DBT_CACHETBL_SIZE); hashidx = hash % DBT_CACHETBL_SIZE; lock_release(&_dbt_cachetbl[hashidx].sem); return 0; } /** * */ int dbt_cache_destroy(void) { int i; dbt_cache_p _dc=NULL, _dc0=NULL; dbt_table_p _tbc = NULL; dbt_table_p _tbc0 = NULL; if(!_dbt_cachesem) return -1; lock_get(_dbt_cachesem); if( _dbt_cachedb!=NULL ) { _dc = *_dbt_cachedb; while(_dc) { _dc0 = _dc; _dc = _dc->next; shm_free(_dc0->name.s); shm_free(_dc0); } shm_free(_dbt_cachedb); } lock_destroy(_dbt_cachesem); lock_dealloc(_dbt_cachesem); /* destroy tables' hash table*/ if(_dbt_cachetbl==0) return 0; for(i=0; inext; dbt_table_free(_tbc0); } _dbt_cachetbl[i].dtp = NULL; } shm_free(_dbt_cachetbl); return 0; } /** * */ int dbt_cache_print(int _f) { int i; dbt_table_p _tbc; if(!_dbt_cachetbl) return -1; for(i=0; i< DBT_CACHETBL_SIZE; i++) { lock_get(&_dbt_cachetbl[i].sem); _tbc = _dbt_cachetbl[i].dtp; while(_tbc) { if(_f) fprintf(stdout, "\n--- Database [%.*s]\n", _tbc->dbname.len, _tbc->dbname.s); if(_f) { fprintf(stdout, "\n----- Table [%.*s]\n", _tbc->name.len, _tbc->name.s); fprintf(stdout, "------- LA=<%d> FL=<%x> AC=<%d>" " AV=<%d>\n", _tbc->mark, _tbc->flag, _tbc->auto_col, _tbc->auto_val); dbt_print_table(_tbc, NULL); } else { if(_tbc->flag & DBT_TBFL_MODI) { dbt_print_table(_tbc, &(_tbc->dbname)); dbt_table_update_flags(_tbc,DBT_TBFL_MODI, DBT_FL_UNSET, 0); } } _tbc = _tbc->next; } lock_release(&_dbt_cachetbl[i].sem); } return 0; } int dbt_cache_reload(const str *dbname, const str *name) { dbt_cache_p _dc; dbt_table_p _tbc, _tbc_new; int hash, hashidx; int i, res = 0; if( !_dbt_cachesem || !_dbt_cachedb || !_dbt_cachetbl ) { LM_ERR("dbtext cache is not initialized! Check if you loaded" " dbtext before attempting to reload it\n"); return -2; } if( dbname && !(dbname->s && (dbname->len > 0)) ) { LM_ERR("request to reload an invalid dbtext database.\n"); return -1; } if( name && !(name->s && (name->len > 0)) ) { LM_ERR("request to reload an invalid dbtext table.\n"); return -1; } // lock to disable any cache modfifications while in reload lock_get(_dbt_cachesem); // resolve dbname in database cache if specified _dc = NULL; if( dbname ) { _dc = *_dbt_cachedb; while( _dc ) { if( (_dc->name.len == dbname->len) && !strncasecmp(_dc->name.s, dbname->s, dbname->len) ) { LM_DBG("resolved database [%.*s]\n", _dc->name.len, _dc->name.s); break; } _dc = _dc->next; } if( !_dc ) { LM_ERR("dbtext database [%.*s] not exists and cannot be reloaded!\n", dbname->len, dbname->s); lock_release(_dbt_cachesem); return -1; } } // calculate hash & hashidx if dbname & name specified hash = hashidx = -1; if( _dc && name ) { hash = core_hash(&_dc->name, name, DBT_CACHETBL_SIZE); hashidx = hash % DBT_CACHETBL_SIZE; } for(i=0; ihash == hash)) && // match table name if specified (!name || ((_tbc->name.len == name->len) && !strncasecmp(_tbc->name.s, name->s, name->len))) && // match database name if specified (!_dc || ((_tbc->dbname.len == _dc->name.len) && !strncasecmp(_tbc->dbname.s, _dc->name.s, _dc->name.len))) ) { // load new version from disk _tbc_new = dbt_load_file(&(_tbc->name), &(_tbc->dbname)); if( _tbc_new ) { // inherit old version properties _tbc_new->hash = _tbc->hash; _tbc_new->prev = _tbc->prev; _tbc_new->next = _tbc->next; // update linked list links if( _tbc_new->prev ) (_tbc_new->prev)->next = _tbc_new; if( _tbc_new->next ) (_tbc_new->next)->prev = _tbc_new; if( _dbt_cachetbl[i].dtp == _tbc ) _dbt_cachetbl[i].dtp = _tbc_new; // delete old version and update iterator dbt_table_free(_tbc); _tbc = _tbc_new; LM_INFO("dbtext table [%.*s]->[%.*s] successfully reloaded\n", _tbc->dbname.len, _tbc->dbname.s, _tbc->name.len, _tbc->name.s); } else { // report failure if any reload fails res = -2; LM_ERR("dbtext table [%.*s]->[%.*s] loading of a new version failed and old version preserved!\n", _tbc->dbname.len, _tbc->dbname.s, _tbc->name.len, _tbc->name.s); } // exit loop if name specified and in 'match only one' scenario if( name ) break; } _tbc = _tbc->next; } lock_release(&_dbt_cachetbl[i].sem); if( name && !_tbc ) { LM_ERR("dbtext table [%.*s]->[%.*s] not exists and cannot be reloaded!\n", dbname->len, dbname->s, name->len, name->s); lock_release(_dbt_cachesem); return -1; } } } lock_release(_dbt_cachesem); return res; } int dbt_is_neq_type(db_type_t _t0, db_type_t _t1) { // LM_DBG("t0=%d t1=%d!\n", _t0, _t1); if(_t0 == _t1) return 0; switch(_t1) { case DB_INT: if(_t0==DB_DATETIME || _t0==DB_BITMAP || _t0==DB_BIGINT) return 0; case DB_DATETIME: if(_t0==DB_INT||_t0==DB_BIGINT) return 0; if(_t0==DB_BITMAP) return 0; case DB_DOUBLE: break; case DB_STRING: if(_t0==DB_STR) return 0; case DB_STR: if(_t0==DB_STRING || _t0==DB_BLOB) return 0; case DB_BLOB: if(_t0==DB_STR || _t0==DB_STRING) return 0; case DB_BITMAP: if (_t0==DB_INT) return 0; case DB_BIGINT: if (_t0==DB_INT || _t0==DB_DATETIME || _t0==DB_BITMAP) return 0; } return 1; } opensips-2.2.2/modules/db_text/dbt_lib.h000066400000000000000000000064211300170765700202370ustar00rootroot00000000000000/* * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-30 created by Daniel * */ #ifndef _DBT_LIB_H_ #define _DBT_LIB_H_ #include "../../str.h" #include "../../db/db_val.h" #include "../../locking.h" #define DBT_FLAG_UNSET 0 #define DBT_FLAG_NULL 1 #define DBT_FLAG_AUTO 2 #define DBT_TBFL_ZERO 0 #define DBT_TBFL_MODI 1 #define DBT_FL_IGN -1 #define DBT_FL_SET 0 #define DBT_FL_UNSET 1 #define DBT_DELIM ':' #define DBT_DELIM_C ' ' #define DBT_DELIM_R '\n' /* * * Module parameters variables * */ extern int db_mode; /* Database usage mode: 0 = no cache, 1 = cache */ typedef db_val_t dbt_val_t, *dbt_val_p; typedef struct _dbt_row { dbt_val_p fields; struct _dbt_row *prev; struct _dbt_row *next; } dbt_row_t, *dbt_row_p; typedef struct _dbt_column { str name; int type; int flag; struct _dbt_column *prev; struct _dbt_column *next; } dbt_column_t, *dbt_column_p; typedef struct _dbt_table { str dbname; str name; int hash; int mark; int flag; int auto_col; int auto_val; int nrcols; dbt_column_p cols; dbt_column_p *colv; int nrrows; dbt_row_p rows; time_t mt; struct _dbt_table *next; struct _dbt_table *prev; } dbt_table_t, *dbt_table_p; typedef struct _dbt_tbl_cachel { gen_lock_t sem; dbt_table_p dtp; } dbt_tbl_cachel_t, *dbt_tbl_cachel_p; typedef struct _dbt_cache { str name; int flags; struct _dbt_cache *next; } dbt_cache_t, *dbt_cache_p; int dbt_init_cache(); int dbt_cache_destroy(); int dbt_cache_print(int); int dbt_cache_reload(const str *dbname, const str *name); dbt_cache_p dbt_cache_get_db(str*); int dbt_cache_check_db(str*); int dbt_cache_del_db(str*); dbt_table_p dbt_db_get_table(dbt_cache_p, const str*); int dbt_release_table(dbt_cache_p, const str*); int dbt_cache_free(dbt_cache_p); dbt_column_p dbt_column_new(char*, int); dbt_row_p dbt_row_new(int); dbt_table_p dbt_table_new(const str*, const str*, const char*); int dbt_row_free(dbt_table_p, dbt_row_p); int dbt_column_free(dbt_column_p); int dbt_table_free_rows(dbt_table_p); int dbt_table_free(dbt_table_p); int dbt_row_set_val(dbt_row_p, dbt_val_p, int, int); int dbt_row_update_val(dbt_row_p, dbt_val_p, int, int); int dbt_table_add_row(dbt_table_p, dbt_row_p); int dbt_table_check_row(dbt_table_p, dbt_row_p); int dbt_table_update_flags(dbt_table_p, int, int, int); int dbt_check_mtime(const str *, const str *, time_t *); dbt_table_p dbt_load_file(const str *, const str *); int dbt_print_table(dbt_table_p, str *); int dbt_is_neq_type(db_type_t _t0, db_type_t _t1); #endif opensips-2.2.2/modules/db_text/dbt_res.c000066400000000000000000000261161300170765700202600ustar00rootroot00000000000000/* * DBText module core functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-06-05 fixed bug: when comparing two values and the first was less than * the second one, the result of 'dbt_row_match' was always true, * thanks to Gabriel, (Daniel) * 2003-02-04 created by Daniel * */ #include #include #include #include "../../mem/mem.h" #include "dbt_res.h" dbt_result_p dbt_result_new(dbt_table_p _dtp, int *_lres, int _sz) { dbt_result_p _dres = NULL; int i, n; char *p; if(!_dtp || _sz < 0) return NULL; if(!_lres) _sz = _dtp->nrcols; _dres = (dbt_result_p)pkg_malloc(sizeof(dbt_result_t)); if(!_dres) return NULL; _dres->colv = (dbt_column_p)pkg_malloc(_sz*sizeof(dbt_column_t)); if(!_dres->colv) { LM_DBG("no pkg memory!\n"); pkg_free(_dres); return NULL; } memset(_dres->colv, 0, _sz*sizeof(dbt_column_t)); LM_DBG("new res with %d cols\n", _sz); for(i = 0; i < _sz; i++) { n = (_lres)?_dtp->colv[_lres[i]]->name.len:_dtp->colv[i]->name.len; p = (_lres)?_dtp->colv[_lres[i]]->name.s:_dtp->colv[i]->name.s; _dres->colv[i].name.s = (char*)pkg_malloc((n+1)*sizeof(char)); if(!_dres->colv[i].name.s) { LM_DBG("no pkg memory\n"); goto clean; } _dres->colv[i].name.len = n; strncpy(_dres->colv[i].name.s, p, n); _dres->colv[i].name.s[n] = 0; _dres->colv[i].type = (_lres)?_dtp->colv[_lres[i]]->type:_dtp->colv[i]->type; } _dres->nrcols = _sz; _dres->nrrows = 0; _dres->rows = NULL; _dres->last = NULL; return _dres; clean: while(i>=0) { if(_dres->colv[i].name.s) pkg_free(_dres->colv[i].name.s); i--; } pkg_free(_dres->colv); pkg_free(_dres); return NULL; } int dbt_result_free(dbt_result_p _dres) { dbt_row_p _rp=NULL, _rp0=NULL; int i; if(!_dres) return -1; _rp = _dres->rows; while(_rp) { _rp0=_rp; if(_rp0->fields) { for(i=0; i<_dres->nrcols; i++) { if((_dres->colv[i].type==DB_STR || _dres->colv[i].type==DB_STRING) && _rp0->fields[i].val.str_val.s) pkg_free(_rp0->fields[i].val.str_val.s); } pkg_free(_rp0->fields); } pkg_free(_rp0); _rp=_rp->next; } if(_dres->colv) { for(i=0; i<_dres->nrcols; i++) { if(_dres->colv[i].name.s) pkg_free(_dres->colv[i].name.s); } pkg_free(_dres->colv); } pkg_free(_dres); return 0; } int dbt_result_add_row(dbt_result_p _dres, dbt_row_p _drp) { if(!_dres || !_drp) return -1; _dres->nrrows++; if(_dres->rows) (_dres->rows)->prev = _drp; _drp->next = _dres->rows; _dres->rows = _drp; return 0; } int* dbt_get_refs(dbt_table_p _dtp, db_key_t* _k, int _n) { int i, j, *_lref=NULL; if(!_dtp || !_k || _n < 0) return NULL; _lref = (int*)pkg_malloc(_n*sizeof(int)); if(!_lref) return NULL; for(i=0; i < _n; i++) { for(j=0; j<_dtp->nrcols; j++) { if(_k[i]->len==_dtp->colv[j]->name.len && !strncasecmp(_k[i]->s, _dtp->colv[j]->name.s, _dtp->colv[j]->name.len)) { _lref[i] = j; break; } } if(j>=_dtp->nrcols) { LM_ERR("column <%.*s> not found\n", _k[i]->len, _k[i]->s); pkg_free(_lref); return NULL; } } return _lref; } int dbt_row_match(dbt_table_p _dtp, dbt_row_p _drp, int* _lkey, db_op_t* _op, db_val_t* _v, int _n) { int i, res; if(!_dtp || !_drp) return 0; if(!_lkey) return 1; for(i=0; i<_n; i++) { res = dbt_cmp_val(&_drp->fields[_lkey[i]], &_v[i]); if(!_op || !strcmp(_op[i], OP_EQ)) { if(res!=0) return 0; }else{ if(!strcmp(_op[i], OP_LT)) { if(res!=-1) return 0; }else{ if(!strcmp(_op[i], OP_GT)) { if(res!=1) return 0; }else{ if(!strcmp(_op[i], OP_LEQ)) { if(res==1) return 0; }else{ if(!strcmp(_op[i], OP_GEQ)) { if(res==-1) return 0; }else{ return 0; }}}}} } return 1; } int dbt_result_extract_fields(dbt_table_p _dtp, dbt_row_p _drp, int* _lres, dbt_result_p _dres) { dbt_row_p _rp=NULL; int i, n; if(!_dtp || !_drp || !_dres || _dres->nrcols<=0) return -1; _rp = dbt_result_new_row(_dres); if(!_rp) return -1; for(i=0; i<_dres->nrcols; i++) { n = (_lres)?_lres[i]:i; if(dbt_is_neq_type(_dres->colv[i].type, _dtp->colv[n]->type)) { LM_DBG("wrong types!\n"); goto clean; } _rp->fields[i].nul = _drp->fields[n].nul; if(_rp->fields[i].nul) { memset(&(_rp->fields[i].val), 0, sizeof(_rp->fields[i].val)); continue; } switch(_dres->colv[i].type) { case DB_INT: case DB_DATETIME: case DB_BITMAP: _rp->fields[i].type = _dres->colv[i].type; _rp->fields[i].val.int_val = _drp->fields[n].val.int_val; break; case DB_BIGINT: _rp->fields[i].type = _dres->colv[i].type; _rp->fields[i].val.bigint_val = _drp->fields[n].val.bigint_val; break; case DB_DOUBLE: _rp->fields[i].type = DB_DOUBLE; _rp->fields[i].val.double_val=_drp->fields[n].val.double_val; break; case DB_STRING: case DB_STR: case DB_BLOB: _rp->fields[i].type = _dres->colv[i].type; _rp->fields[i].val.str_val.len = _drp->fields[n].val.str_val.len; _rp->fields[i].val.str_val.s =(char*)pkg_malloc(sizeof(char)* (_drp->fields[n].val.str_val.len+1)); if(!_rp->fields[i].val.str_val.s) goto clean; memcpy(_rp->fields[i].val.str_val.s, _drp->fields[n].val.str_val.s, _rp->fields[i].val.str_val.len); _rp->fields[i].val.str_val.s[_rp->fields[i].val.str_val.len]=0; break; default: goto clean; } } _rp->next = NULL; if (_dres->last) { _dres->last->next = _rp; _rp->prev = _dres->last; } else { _dres->rows = _rp; } _dres->last = _rp; _dres->nrrows++; return 0; clean: LM_DBG("make clean!\n"); while(i>=0) { if((_rp->fields[i].type == DB_STRING || _rp->fields[i].type == DB_STR || _rp->fields[i].type == DB_BLOB) && !_rp->fields[i].nul && _rp->fields[i].val.str_val.s) pkg_free(_rp->fields[i].val.str_val.s); i--; } pkg_free(_rp->fields); pkg_free(_rp); return -1; } int dbt_result_print(dbt_result_p _dres) { #if 0 int i; FILE *fout = stdout; dbt_row_p rowp = NULL; char *p; if(!_dres || _dres->nrcols<=0) return -1; fprintf(fout, "\nContent of result\n"); for(i=0; i<_dres->nrcols; i++) { switch(_dres->colv[i].type) { case DB_INT: fprintf(fout, "%.*s(int", _dres->colv[i].name.len, _dres->colv[i].name.s); if(_dres->colv[i].flag & DBT_FLAG_NULL) fprintf(fout, ",null"); fprintf(fout, ") "); break; case DB_DOUBLE: fprintf(fout, "%.*s(double", _dres->colv[i].name.len, _dres->colv[i].name.s); if(_dres->colv[i].flag & DBT_FLAG_NULL) fprintf(fout, ",null"); fprintf(fout, ") "); break; case DB_STR: fprintf(fout, "%.*s(str", _dres->colv[i].name.len, _dres->colv[i].name.s); if(_dres->colv[i].flag & DBT_FLAG_NULL) fprintf(fout, ",null"); fprintf(fout, ") "); break; default: return -1; } } fprintf(fout, "\n"); rowp = _dres->rows; while(rowp) { for(i=0; i<_dres->nrcols; i++) { switch(_dres->colv[i].type) { case DB_INT: if(rowp->fields[i].nul) fprintf(fout, "N "); else fprintf(fout, "%d ", rowp->fields[i].val.int_val); break; case DB_DOUBLE: if(rowp->fields[i].nul) fprintf(fout, "N "); else fprintf(fout, "%.2f ", rowp->fields[i].val.double_val); break; case DB_STR: fprintf(fout, "\""); if(!rowp->fields[i].nul) { p = rowp->fields[i].val.str_val.s; while(p < rowp->fields[i].val.str_val.s + rowp->fields[i].val.str_val.len) { switch(*p) { case '\n': fprintf(fout, "\\n"); break; case '\r': fprintf(fout, "\\r"); break; case '\t': fprintf(fout, "\\t"); break; case '\\': fprintf(fout, "\\\\"); break; case '"': fprintf(fout, "\\\""); break; case '\0': fprintf(fout, "\\0"); break; default: fprintf(fout, "%c", *p); } p++; } } fprintf(fout, "\" "); break; default: return -1; } } fprintf(fout, "\n"); rowp = rowp->next; } #endif return 0; } int dbt_cmp_val(dbt_val_p _vp, db_val_t* _v) { int _l, _n; if(!_vp && !_v) return 0; if(!_v) return 1; if(!_vp) return -1; if(_vp->nul && _v->nul) return 0; if(_v->nul) return 1; if(_vp->nul) return -1; switch(VAL_TYPE(_v)) { case DB_INT: return (_vp->val.int_val<_v->val.int_val)?-1: (_vp->val.int_val>_v->val.int_val)?1:0; case DB_BIGINT: return (_vp->val.bigint_val<_v->val.bigint_val)?-1: (_vp->val.bigint_val>_v->val.bigint_val)?1:0; case DB_DOUBLE: return (_vp->val.double_val<_v->val.double_val)?-1: (_vp->val.double_val>_v->val.double_val)?1:0; case DB_DATETIME: return (_vp->val.int_val<_v->val.time_val)?-1: (_vp->val.int_val>_v->val.time_val)?1:0; case DB_STRING: _l = strlen(_v->val.string_val); _l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l; _n = strncasecmp(_vp->val.str_val.s, _v->val.string_val, _l); if(_n) return _n; if(_vp->val.str_val.len == strlen(_v->val.string_val)) return 0; if(_l==_vp->val.str_val.len) return -1; return 1; case DB_STR: _l = _v->val.str_val.len; _l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l; _n = strncasecmp(_vp->val.str_val.s, _v->val.str_val.s, _l); if(_n) return _n; if(_vp->val.str_val.len == _v->val.str_val.len) return 0; if(_l==_vp->val.str_val.len) return -1; return 1; case DB_BLOB: _l = _v->val.blob_val.len; _l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l; _n = strncasecmp(_vp->val.str_val.s, _v->val.blob_val.s, _l); if(_n) return _n; if(_vp->val.str_val.len == _v->val.blob_val.len) return 0; if(_l==_vp->val.str_val.len) return -1; return 1; case DB_BITMAP: return (_vp->val.int_val<_v->val.bitmap_val)?-1: (_vp->val.int_val>_v->val.bitmap_val)?1:0; } return -2; } dbt_row_p dbt_result_new_row(dbt_result_p _dres) { dbt_row_p _drp = NULL; if(!_dres || _dres->nrcols<=0) return NULL; _drp = (dbt_row_p)pkg_malloc(sizeof(dbt_row_t)); if(!_drp) return NULL; memset(_drp, 0, sizeof(dbt_row_t)); _drp->fields = (dbt_val_p)pkg_malloc(_dres->nrcols*sizeof(dbt_val_t)); if(!_drp->fields) { pkg_free(_drp); return NULL; } memset(_drp->fields, 0, _dres->nrcols*sizeof(dbt_val_t)); _drp->next = _drp->prev = NULL; return _drp; } opensips-2.2.2/modules/db_text/dbt_res.h000066400000000000000000000037611300170765700202660ustar00rootroot00000000000000/* * DBText module core functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-04 created by Daniel * */ #ifndef _DBT_RES_H_ #define _DBT_RES_H_ #include "../../db/db_op.h" #include "../../db/db_res.h" #include "dbt_lib.h" typedef struct _dbt_result { int nrcols; int nrrows; dbt_column_p colv; dbt_row_p rows; dbt_row_p last; } dbt_result_t, *dbt_result_p; //typedef db_res_t dbt_result_t, *dbt_result_p; typedef struct _dbt_con { dbt_cache_p con; dbt_result_p res; dbt_row_p row; } dbt_con_t, *dbt_con_p; #define DBT_CON_CONNECTION(db_con) (((dbt_con_p)((db_con)->tail))->con) #define DBT_CON_RESULT(db_con) (((dbt_con_p)((db_con)->tail))->res) #define DBT_CON_ROW(db_con) (((dbt_con_p)((db_con)->tail))->row) dbt_result_p dbt_result_new(dbt_table_p, int*, int); int dbt_result_free(dbt_result_p); int dbt_row_match(dbt_table_p _dtp, dbt_row_p _drp, int* _lkey, db_op_t* _op, db_val_t* _v, int _n); int dbt_result_extract_fields(dbt_table_p _dtp, dbt_row_p _drp, int* lres, dbt_result_p _dres); int dbt_result_print(dbt_result_p _dres); int* dbt_get_refs(dbt_table_p, db_key_t*, int); int dbt_cmp_val(dbt_val_p _vp, db_val_t* _v); dbt_row_p dbt_result_new_row(dbt_result_p _dres); #endif opensips-2.2.2/modules/db_text/dbt_tb.c000066400000000000000000000237331300170765700200760ustar00rootroot00000000000000/* * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-03 created by Daniel * */ #include #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../locking.h" #include "dbt_util.h" #include "dbt_lib.h" /** * */ dbt_column_p dbt_column_new(char *_s, int _l) { dbt_column_p dcp = NULL; if(!_s || _l <=0) return NULL; dcp = (dbt_column_p)shm_malloc(sizeof(dbt_column_t)); if(!dcp) return NULL; dcp->name.s = (char*)shm_malloc((_l+1)*sizeof(char)); if(!dcp->name.s) { shm_free(dcp); return NULL; } dcp->name.len = _l; strncpy(dcp->name.s, _s, _l); dcp->name.s[_l] = '\0'; dcp->next = dcp->prev = NULL; dcp->type = 0; dcp->flag = DBT_FLAG_UNSET; return dcp; } /** * */ int dbt_column_free(dbt_column_p dcp) { if(!dcp) return -1; if(dcp->name.s) shm_free(dcp->name.s); shm_free(dcp); return 0; } /** * */ dbt_row_p dbt_row_new(int _nf) { int i; dbt_row_p _drp = NULL; _drp = (dbt_row_p)shm_malloc(sizeof(dbt_row_t)); if(!_drp) return NULL; _drp->fields = (dbt_val_p)shm_malloc(_nf*sizeof(dbt_val_t)); if(!_drp->fields) { shm_free(_drp); return NULL; } memset(_drp->fields, 0, _nf*sizeof(dbt_val_t)); for(i=0; i<_nf; i++) _drp->fields[i].nul = 1; _drp->next = _drp->prev = NULL; return _drp; } /** * */ int dbt_row_free(dbt_table_p _dtp, dbt_row_p _drp) { int i; if(!_dtp || !_drp) return -1; if(_drp->fields) { for(i=0; i<_dtp->nrcols; i++) if((_dtp->colv[i]->type==DB_STR || _dtp->colv[i]->type==DB_STRING || _dtp->colv[i]->type==DB_BLOB) && _drp->fields[i].val.str_val.s) shm_free(_drp->fields[i].val.str_val.s); shm_free(_drp->fields); } shm_free(_drp); return 0; } /** * */ dbt_table_p dbt_table_new(const str *_tbname, const str *_dbname, const char *path) { struct stat s; dbt_table_p dtp = NULL; if(!_tbname || !_dbname || !path) return NULL; dtp = (dbt_table_p)shm_malloc(sizeof(dbt_table_t)); if(!dtp) goto done; memset(dtp, 0, sizeof *dtp); dtp->name.s = (char*)shm_malloc((_tbname->len+1)*sizeof(char)); if(!dtp->name.s) { shm_free(dtp); dtp = NULL; goto done; } memcpy(dtp->name.s, _tbname->s, _tbname->len); dtp->name.s[_tbname->len] = '\0'; dtp->name.len = _tbname->len; dtp->dbname.s = (char*)shm_malloc((_dbname->len+1)*sizeof(char)); if(!dtp->dbname.s) { shm_free(dtp->name.s); shm_free(dtp); dtp = NULL; goto done; } memcpy(dtp->dbname.s, _dbname->s, _dbname->len); dtp->dbname.s[_dbname->len] = '\0'; dtp->dbname.len = _dbname->len; dtp->mark = (int)time(NULL); dtp->flag = DBT_TBFL_ZERO; dtp->auto_col = -1; if(stat(path, &s) == 0) { dtp->mt = s.st_mtime; LM_DBG("mtime is %d\n", (int)s.st_mtime); } done: return dtp; } /** * */ int dbt_table_free_rows(dbt_table_p _dtp) { dbt_row_p _rp=NULL, _rp0=NULL; if(!_dtp || !_dtp->rows || !_dtp->colv) return -1; _rp = _dtp->rows; while(_rp) { _rp0=_rp; _rp=_rp->next; dbt_row_free(_dtp, _rp0); } dbt_table_update_flags(_dtp, DBT_TBFL_MODI, DBT_FL_SET, 1); _dtp->rows = NULL; _dtp->nrrows = 0; return 0; } /** * */ int dbt_table_add_row(dbt_table_p _dtp, dbt_row_p _drp) { if(!_dtp || !_drp) return -1; if(dbt_table_check_row(_dtp, _drp)) return -1; dbt_table_update_flags(_dtp, DBT_TBFL_MODI, DBT_FL_SET, 1); if(_dtp->rows) (_dtp->rows)->prev = _drp; _drp->next = _dtp->rows; _dtp->rows = _drp; _dtp->nrrows++; return 0; } /** * */ int dbt_table_free(dbt_table_p _dtp) { dbt_column_p _cp=NULL, _cp0=NULL; if(!_dtp) return -1; if(_dtp->name.s) shm_free(_dtp->name.s); if(_dtp->dbname.s) shm_free(_dtp->dbname.s); if(_dtp->rows && _dtp->nrrows>0) dbt_table_free_rows(_dtp); _cp = _dtp->cols; while(_cp) { _cp0=_cp; _cp=_cp->next; dbt_column_free(_cp0); } if(_dtp->colv) shm_free(_dtp->colv); shm_free(_dtp); return 0; } /** * */ int dbt_row_set_val(dbt_row_p _drp, dbt_val_p _vp, int _t, int _idx) { if(!_drp || !_vp || _idx<0) return -1; _drp->fields[_idx].nul = _vp->nul; _drp->fields[_idx].type = _t; if(!_vp->nul) { switch(_t) { case DB_STR: case DB_BLOB: _drp->fields[_idx].type = _t; _drp->fields[_idx].val.str_val.s = (char*)shm_malloc((_vp->val.str_val.len+1)*sizeof(char)); if(!_drp->fields[_idx].val.str_val.s) { _drp->fields[_idx].nul = 1; return -1; } memcpy(_drp->fields[_idx].val.str_val.s, _vp->val.str_val.s, _vp->val.str_val.len); _drp->fields[_idx].val.str_val.s[_vp->val.str_val.len] = '\0'; _drp->fields[_idx].val.str_val.len = _vp->val.str_val.len; break; case DB_STRING: _drp->fields[_idx].type = _t; _drp->fields[_idx].val.str_val.len=_vp->val.str_val.len; _drp->fields[_idx].val.str_val.s = (char*)shm_malloc((_drp->fields[_idx].val.str_val.len+1) *sizeof(char)); if(!_drp->fields[_idx].val.str_val.s) { _drp->fields[_idx].nul = 1; return -1; } memcpy(_drp->fields[_idx].val.str_val.s, _vp->val.string_val, _drp->fields[_idx].val.str_val.len); _drp->fields[_idx].val.str_val.s[_drp->fields[_idx].val.str_val.len] = '\0'; break; case DB_DOUBLE: _drp->fields[_idx].type = DB_DOUBLE; _drp->fields[_idx].val.double_val = _vp->val.double_val; break; case DB_INT: _drp->fields[_idx].type = DB_INT; _drp->fields[_idx].val.int_val = _vp->val.int_val; break; case DB_BIGINT: _drp->fields[_idx].type = DB_BIGINT; _drp->fields[_idx].val.bigint_val = _vp->val.bigint_val; break; case DB_DATETIME: _drp->fields[_idx].type = _t; _drp->fields[_idx].val.int_val = (int)_vp->val.time_val; break; case DB_BITMAP: _drp->fields[_idx].type = DB_INT; _drp->fields[_idx].val.int_val = (int)_vp->val.bitmap_val; break; default: _drp->fields[_idx].nul = 1; return -1; } } return 0; } /** * */ int dbt_row_update_val(dbt_row_p _drp, dbt_val_p _vp, int _t, int _idx) { if(!_drp || !_vp || _idx<0) return -1; _drp->fields[_idx].nul = _vp->nul; _drp->fields[_idx].type = _t; if(!_vp->nul) { switch(_t) { case DB_BLOB: case DB_STR: _drp->fields[_idx].type = _t; // free if already exists if(_drp->fields[_idx].val.str_val.s) shm_free(_drp->fields[_idx].val.str_val.s); _drp->fields[_idx].val.str_val.s = (char*)shm_malloc((_vp->val.str_val.len+1)*sizeof(char)); if(!_drp->fields[_idx].val.str_val.s) { _drp->fields[_idx].nul = 1; return -1; } memcpy(_drp->fields[_idx].val.str_val.s, _vp->val.str_val.s, _vp->val.str_val.len); _drp->fields[_idx].val.str_val.s[_vp->val.str_val.len] = '\0'; _drp->fields[_idx].val.str_val.len = _vp->val.str_val.len; break; case DB_STRING: /* free if already exists */ if(_drp->fields[_idx].val.str_val.s) shm_free(_drp->fields[_idx].val.str_val.s); _drp->fields[_idx].type = _t; if(_vp->type==DB_STR) _drp->fields[_idx].val.str_val.len=_vp->val.str_val.len; else _drp->fields[_idx].val.str_val.len =strlen(_vp->val.string_val); _drp->fields[_idx].val.str_val.s = (char*)shm_malloc((_drp->fields[_idx].val.str_val.len+1) *sizeof(char)); if(!_drp->fields[_idx].val.str_val.s) { _drp->fields[_idx].nul = 1; return -1; } memcpy(_drp->fields[_idx].val.str_val.s, _vp->val.string_val, _drp->fields[_idx].val.str_val.len); _drp->fields[_idx].val.str_val.s[_vp->val.str_val.len] = '\0'; break; case DB_DOUBLE: _drp->fields[_idx].type = _t; _drp->fields[_idx].val.double_val = _vp->val.double_val; break; case DB_INT: _drp->fields[_idx].type = _t; _drp->fields[_idx].val.int_val = _vp->val.int_val; break; case DB_BIGINT: _drp->fields[_idx].type = _t; _drp->fields[_idx].val.bigint_val = _vp->val.bigint_val; break; case DB_DATETIME: _drp->fields[_idx].type = _t; _drp->fields[_idx].val.int_val = (int)_vp->val.time_val; break; case DB_BITMAP: _drp->fields[_idx].type = _t; _drp->fields[_idx].val.int_val = (int)_vp->val.bitmap_val; break; default: LM_ERR("unsupported type %d in update\n",_t); _drp->fields[_idx].nul = 1; return -1; } } return 0; } /** * */ int dbt_table_check_row(dbt_table_p _dtp, dbt_row_p _drp) { int i; if(!_dtp || _dtp->nrcols <= 0 || !_drp) return -1; for(i=0; i<_dtp->nrcols; i++) { if(!_drp->fields[i].nul && dbt_is_neq_type(_dtp->colv[i]->type, _drp->fields[i].type)) { LM_ERR("incompatible types - field %d [%d/%d]\n",i, _dtp->colv[i]->type, _drp->fields[i].type); return -1; } if(_dtp->colv[i]->flag & DBT_FLAG_NULL) continue; if(!_drp->fields[i].nul) continue; if(_dtp->colv[i]->type==DB_INT && (_dtp->colv[i]->flag & DBT_FLAG_AUTO) && i==_dtp->auto_col) { _drp->fields[i].nul = 0; _drp->fields[i].val.int_val = ++_dtp->auto_val; continue; } LM_ERR("null value not allowed - field %d\n",i); return -1; } return 0; } /** * */ int dbt_table_update_flags(dbt_table_p _dtp, int _f, int _o, int _m) { if(!_dtp) return -1; if(_o == DBT_FL_SET) _dtp->flag |= _f; else if(_o == DBT_FL_UNSET) _dtp->flag &= ~_f; if(_m) _dtp->mark = (int)time(NULL); return 0; } opensips-2.2.2/modules/db_text/dbt_util.c000066400000000000000000000023421300170765700204370ustar00rootroot00000000000000/* * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-01-30 created by Daniel * */ #include #include #include #include "dbt_util.h" /** * */ int dbt_is_database(str *_s) { DIR *dirp = NULL; char buf[512]; if(!_s || !_s->s || _s->len <= 0 || _s->len > 510) return 0; strncpy(buf, _s->s, _s->len); buf[_s->len] = 0; dirp = opendir(buf); if(!dirp) return 0; closedir(dirp); return 1; } opensips-2.2.2/modules/db_text/dbt_util.h000066400000000000000000000017501300170765700204460ustar00rootroot00000000000000/* * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-30 created by Daniel * */ #ifndef _DBT_UTIL_H_ #define _DBT_UTIL_H_ #include "../../str.h" int dbt_is_database(str *); #endif opensips-2.2.2/modules/db_text/dbtext.c000066400000000000000000000100241300170765700201170ustar00rootroot00000000000000/* * DBText module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-30 created by Daniel * 2003-03-11 New module interface (janakj) * 2003-03-16 flags export parameter added (janakj) * */ #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "dbtext.h" #include "dbt_lib.h" #include "dbt_api.h" static int mod_init(void); static void destroy(void); static struct mi_root* mi_dbt_dump(struct mi_root* cmd, void* param); static struct mi_root* mi_dbt_reload(struct mi_root* cmd, void* param); /* * Module parameter variables */ int db_mode = 0; /* Database usage mode: 0 = cache, 1 = no cache */ int dbt_bind_api(const str* mod, db_func_t *dbb); /* * Exported functions */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)dbt_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"db_mode", INT_PARAM, &db_mode}, {0, 0, 0} }; /** MI commands */ static mi_export_t mi_cmds[] = { {"dbt_dump", 0, mi_dbt_dump, 0, 0, 0}, {"dbt_reload", 0, mi_dbt_reload, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; struct module_exports exports = { "db_text", MOD_TYPE_SQLDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ NULL, /* Exported async functions */ params, /* Exported parameters */ NULL, /* exported statistics */ mi_cmds, /* exported MI functions */ NULL, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ NULL, /* response function*/ destroy, /* destroy function */ NULL /* per-child init function */ }; static int mod_init(void) { if(dbt_init_cache()) return -1; /* return make_demo(); */ return 0; } static void destroy(void) { LM_DBG("destroy ...\n"); dbt_cache_print(0); dbt_cache_destroy(); } int dbt_bind_api(const str* mod, db_func_t *dbb) { if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->use_table = dbt_use_table; dbb->init = dbt_init; dbb->close = dbt_close; dbb->query = (db_query_f)dbt_query; dbb->free_result = dbt_free_result; dbb->insert = (db_insert_f)dbt_insert; dbb->delete = (db_delete_f)dbt_delete; dbb->update = (db_update_f)dbt_update; return 0; } static struct mi_root* mi_dbt_dump(struct mi_root* cmd, void* param) { struct mi_root *rpl_tree; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; if (dbt_cache_print(0)!=0) { free_mi_tree(rpl_tree); return NULL; } return rpl_tree; } static struct mi_root* mi_dbt_reload(struct mi_root* cmd, void* param) { struct mi_node *node; str *dbname, *name; int res; dbname = name = NULL; if( (node = cmd->node.kids) ) { dbname = &(node->value); if( (node = node->next) ) { name = &(node->value); if( node->next ) return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM_S)); } } if( (res = dbt_cache_reload(dbname, name)) >= 0 ) { return init_mi_tree(200, MI_SSTR(MI_OK_S)); } else if( res == -1 ) { return init_mi_tree(400, MI_SSTR(MI_BAD_PARM_S)); } else { return init_mi_tree(500, MI_SSTR(MI_INTERNAL_ERR_S)); } } opensips-2.2.2/modules/db_text/dbtext.h000066400000000000000000000036641300170765700201400ustar00rootroot00000000000000/* * DBText module core functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-30 created by Daniel * */ #ifndef _DBTEXT_H_ #define _DBTEXT_H_ #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" /* * Initialize database connection */ db_con_t* dbt_init(const str* _sqlurl); /* * Close a database connection */ void dbt_close(db_con_t* _h); /* * Free all memory allocated by get_result */ int dbt_free_result(db_con_t* _h, db_res_t* _r); /* * Do a query */ int dbt_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r); /* * Raw SQL query */ int dbt_raw_query(db_con_t* _h, char* _s, db_res_t** _r); /* * Insert a row into table */ int dbt_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n); /* * Delete a row from table */ int dbt_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n); /* * Update a row in table */ int dbt_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un); #endif opensips-2.2.2/modules/db_text/doc/000077500000000000000000000000001300170765700172315ustar00rootroot00000000000000opensips-2.2.2/modules/db_text/doc/db_text.cfg000066400000000000000000000065341300170765700213530ustar00rootroot00000000000000# # simple quick-start config script with dbtext # # ----------- global configuration parameters ------------------------ #debug_mode=yes children=4 check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) listen=udp:10.100.100.1:5060 # ------------------ module loading ---------------------------------- # use dbtext database loadmodule "modules/dbtext/dbtext.so" loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/textops/textops.so" loadmodule "modules/textops/mi_fifo.so" # modules for digest authentication loadmodule "modules/auth/auth.so" loadmodule "modules/auth_db/auth_db.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- usrloc params -- # use dbtext database for persistent storage modparam("usrloc", "db_mode", 2) modparam("usrloc|auth_db", "db_url", "text:///tmp/opensipsdb") # -- auth params -- # modparam("auth_db", "calculate_ha1", 1) modparam("auth_db", "password_column", "password") modparam("auth_db", "user_column", "username") modparam("auth_db", "domain_column", "domain") # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); exit; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); exit; }; if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); route(1); exit; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { # digest authentication if (!www_authorize("", "subscriber")) { www_challenge("", "0"); exit; }; save("location"); exit; }; lookup("aliases"); if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); exit; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; }; append_hf("P-hint: usrloc applied\r\n"); route(1); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } opensips-2.2.2/modules/db_text/doc/db_text.xml000066400000000000000000000024401300170765700214040ustar00rootroot00000000000000 %docentities; ]> DBTEXT Module &osipsname; Daniel-Constantin Mierla miconda@gmail.com Ovidiu Sas osas@voipembedded.com Daniel-Constantin Mierla miconda@gmail.com 2003 2004 &fhg; $Revision: 9528 $ $Date$ &admin; &devel; &faq; opensips-2.2.2/modules/db_text/doc/db_text_admin.xml000066400000000000000000000261431300170765700225620ustar00rootroot00000000000000 &adminguide;
Overview The module implements a simplified database engine based on text files. It can be used by &osips; DB interface instead of other database module (like MySQL). The module is meant for use in demos or small devices that do not support other DB modules. It keeps everything in memory and if you deal with large amount of data you may run quickly out of memory. Also, it has not implemented all standard database facilities (like order by), it includes minimal functionality to work properly with &osips; NOTE: the timestamp is printed in an integer value from time_t structure. If you use it in a system that cannot do this conversion, it will fail (support for such situation is in to-do list). NOTE: even when is in non-caching mode, the module does not write back to hard drive after changes. In this mode, the module checks if the corresponding file on disk has changed, and reloads it. The write on disk happens at OpenSIPS shut down.
Design of db_text engine The db_text database system architecture: a database is represented by a directory in the local file system. NOTE: when you use db_text in &osips;, the database URL for modules must be the path to the directory where the table-files are located, prefixed by text://, e.g., text:///var/dbtext/opensips. If there is no / after text:// then CFG_DIR/ is inserted at the beginning of the database path. So, either you provide an absolute path to database directory or a relative one to CFG_DIR directory. a table is represented by a text file inside database directory.
Internal format of a db_text table First line is the definition of the columns. Each column must be declared as follows: the name of column must not include white spaces. the format of a column definition is: name(type,attr). between two column definitions must be a white space, e.g., first_name(str) last_name(str). the type of a column can be: int - integer numbers. double - real numbers with two decimals. str - strings with maximum size of 4KB. a column can have one of the attributes: auto - only for 'int' columns, the maximum value in that column is incremented and stored in this field if it is not provided in queries. null - accept null values in column fields. if no attribute is set, the fields of the column cannot have null value. each other line is a row with data. The line ends with \n. the fields are separated by :. no value between two ':' (or between ':' and start/end of a row) means null value. next characters must be escaped in strings: \n, \r, \t, :. 0 -- the zero value must be escaped too. Sample of a db_text table ... id(int,auto) name(str) flag(double) desc(str,null) 1:nick:0.34:a\tgood\: friend 2:cole:-3.75:colleague 3:bob:2.50: ... Minimal &osips; location db_text table definition ... username(str) contact(str) expires(int) q(double) callid(str) cseq(int) ... Minimal &osips; subscriber db_text table example ... username(str) password(str) ha1(str) domain(str) ha1b(str) suser:supasswd:xxx:alpha.org:xxx ...
Existing limitations This database interface don't support the data insertion with default values. All such values specified in the database template are ignored. So its advisable to specify all data for a column at insertion operations.
Dependencies
&osips; modules The next modules must be loaded before this module: none.
External libraries or applications The next libraries or applications must be installed before running &osips; with this module: none.
Exported Parameters None.
<varname>db_mode</varname> (integer) Set caching mode (0) or non-caching mode (1). In caching mode, data is loaded at startup. In non-caching mode, the module check every time a table is requested whether the corresponding file on disk has changed, and if yes, will re-load table from file. Default value is 0. Set <varname>db_mode</varname> parameter ... modparam("db_text", "db_mode", 1) ...
Exported Functions None.
Exported MI Functions
<varname>dbt_dump</varname> Write back to hard drive modified tables. Name: dbt_dump. Parameters: none MI FIFO Command Format: :dbt_dump:_reply_fifo_file_ _empty_line_
<varname>dbt_reload</varname> Causes db_text module to reload cached tables from disk. Depending on parameters it could be a whole cache or a specified database or a single table. If any table cannot be reloaded from disk - the old version preserved and error reported. Name: dbt_reload. Parameters: db_name (optional) - database name to reload. table_name (optional, but cannot be present without the db_name parameter) - specific table to reload. MI FIFO Command Format: :dbt_reload:_reply_fifo_file_ _empty_line_ :dbt_reload:_reply_fifo_file_ /path/to/dbtext/database _empty_line_ :dbt_reload:_reply_fifo_file_ /path/to/dbtext/database table_name _empty_line_
Installation and Running Compile the module and load it instead of mysql or other DB modules. REMINDER: when you use db_text in &osips;, the database URL for modules must be the path to the directory where the table-files are located, prefixed by text://, e.g., text:///var/dbtext/opensips. If there is no / after text:// then CFG_DIR/ is inserted at the beginning of the database path. So, either you provide an absolute path to database directory or a relative one to CFG_DIR directory. Load the db_text module ... loadmodule "/path/to/opensips/modules/db_text.so" ... modparam("module_name", "database_URL", "text:///path/to/dbtext/database") ...
Using db_text with basic &osips; configuration Here are the definitions for most important table as well as a basic configuration file to use db_text with &osips;. The table structures may change in time and you will have to adjust next examples. You have to populate the table 'subscriber' by hand with user profiles in order to have authentication. To use with the given configuration file, the table files must be placed in the '/tmp/opensipsdb' directory. Definition of 'subscriber' table (one line) ... username(str) domain(str) password(str) first_name(str) last_name(str) phone(str) email_address(str) datetime_created(int) datetime_modified(int) confirmation(str) flag(str) sendnotification(str) greeting(str) ha1(str) ha1b(str) perms(str) allow_find(str) timezone(str,null) rpid(str,null) ... Definition of 'location' and 'aliases' tables (one line) ... username(str) domain(str,null) contact(str,null) received(str) expires(int,null) q(double,null) callid(str,null) cseq(int,null) last_modified(str) flags(int) user_agent(str) socket(str) ... Definition of 'version' table and sample records ... table_name(str) table_version(int) subscriber:3 location:6 aliases:6 ... Configuration file ... &dbtextsercfg; ...
opensips-2.2.2/modules/db_text/doc/db_text_devel.xml000066400000000000000000000003171300170765700225640ustar00rootroot00000000000000 &develguide; Once you have the module loaded, you can use the API specified by &osips; DB interface. opensips-2.2.2/modules/db_unixodbc/000077500000000000000000000000001300170765700173135ustar00rootroot00000000000000opensips-2.2.2/modules/db_unixodbc/Makefile000066400000000000000000000006551300170765700207610ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=db_unixodbc.so # sql.h locations (freebsd,openbsd solaris) DEFS += -I$(LOCALBASE)/include -I$(LOCALBASE)/include/odbc # libodbc locations on RH/Suse, Solaris /OpenBSD, FreeBSD # (Debian does the right thing and puts it in /usr/lib) LIBS= -L$(LOCALBASE)/lib -lodbc include ../../Makefile.modules opensips-2.2.2/modules/db_unixodbc/README000066400000000000000000000073511300170765700202010ustar00rootroot00000000000000unixodbc Module Marco Lorrai abbeynet.it Edited by Marco Lorrai Copyright © 2005, 2006 Marco Lorrai Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. auto_reconnect (int) 1.3.2. use_escape_common (int) 1.4. Exported Functions 1.5. Installation and Running 1.5.1. Installing 1.5.2. Configuring and Running 2. Developer Guide List of Examples 1.1. Set the “auto_reconnect†parameter 1.2. Set the “use_escape_common†parameter Chapter 1. Admin Guide 1.1. Overview This module allows to use the unixodbc package with OpenSIPS. It have been tested with mysql and the odbc connector, but it should work also with other database. The auth_db module works. For more information, see the http://www.unixodbc.org/ project web page. To see what DB engines can be used via unixodbc, look at http://www.unixodbc.org/drivers.html. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. auto_reconnect (int) Turns on or off the auto_reconnect mode. Default value is “1â€, this means it is enabled. Example 1.1. Set the “auto_reconnect†parameter ... modparam("db_unixodbc", "auto_reconnect", 0) ... 1.3.2. use_escape_common (int) Escape values in query using internal escape_common() function. It escapes single quote ''', double quote '"', backslash '\', and NULL characters. You should enable this parameter if you know that the ODBC driver considers the above characters as special (for marking begin and end of a value, escape other characters ...). It prevents against SQL injection. Default value is “0†(0 = disabled; 1 = enabled). Example 1.2. Set the “use_escape_common†parameter ... modparam("db_unixodbc", "use_escape_common", 1) ... 1.4. Exported Functions NONE 1.5. Installation and Running 1.5.1. Installing Prerequirement: you should first install unixodbc (or another program that implements the odbc standard, such iodbc), your database, and the right connector. Set the DSN in the odbc.ini file and the connector drivers in the odbcinst.ini file. 1.5.2. Configuring and Running In the opensips.conf file, add the line: .... loadmodule "/usr/local/lib/opensips/modules/db_unixodbc.so" .... You should also uncomment this: .... loadmodule "/usr/local/lib/opensips/modules/auth.so" loadmodule "/usr/local/lib/opensips/modules/auth_db.so" modparam("usrloc", "db_mode", 2) modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") .... and setting the DSN specified in the odbc.ini, inserting this with the url adding this line: .... modparam("usrloc|auth_db", "db_url", "unixodbc://opensips:opensipsrw@localhost/my_dsn") .... replacing my_dsn with the correct value. HINT: if unixodbc don't want to connect to mysql server, try restarting mysql server with: shell>safe_mysqld --user=mysql --socket=/var/lib/mysql/mysql.sock The connector search the socket in /var/lib/mysql/mysql.sock and not in /tmp/mysql.sock Chapter 2. Developer Guide The module implements the OpenSIPS DB API, in order to be used by other modules. opensips-2.2.2/modules/db_unixodbc/con.c000066400000000000000000000125701300170765700202430ustar00rootroot00000000000000/* * UNIXODBC module * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) * 2006-01-10 UID (username) and PWD (password) attributes added to * connection string (bogdan) * 2006-05-05 extract_error passes back last error state on return (sgupta) */ #include "con.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../ut.h" #include #define DSN_ATTR "DSN=" #define DSN_ATTR_LEN (sizeof(DSN_ATTR)-1) #define UID_ATTR "UID=" #define UID_ATTR_LEN (sizeof(UID_ATTR)-1) #define PWD_ATTR "PWD=" #define PWD_ATTR_LEN (sizeof(PWD_ATTR)-1) char *db_unixodbc_build_conn_str(const struct db_id* id, char *buf) { int len, ld, lu, lp; char *p; if (!buf) return 0; ld = id->database?strlen(id->database):0; lu = id->username?strlen(id->username):0; lp = id->password?strlen(id->password):0; len = (ld?(DSN_ATTR_LEN + ld + 1):0) + (lu?(UID_ATTR_LEN + lu + 1):0) + PWD_ATTR_LEN + lp + 1; if ( len>=MAX_CONN_STR_LEN ){ LM_ERR("connection string too long! Increase MAX_CONN_STR_LEN" " and recompile\n"); return 0; } p = buf; if (ld) { memcpy( p , DSN_ATTR, DSN_ATTR_LEN); p += DSN_ATTR_LEN; memcpy( p, id->database, ld); p += ld; } if (lu) { *(p++) = ';'; memcpy( p , UID_ATTR, UID_ATTR_LEN); p += UID_ATTR_LEN; memcpy( p, id->username, lu); p += lu; } if (lp) { *(p++) = ';'; memcpy( p , PWD_ATTR, PWD_ATTR_LEN); p += PWD_ATTR_LEN; memcpy( p, id->password, lp); p += lp; } *(p++) = ';'; *p = 0 ; /* make it null terminated */ return buf; } /* * Create a new connection structure, * open the UNIXODBC connection and set reference count to 1 */ struct my_con* db_unixodbc_new_connection(struct db_id* id) { SQLCHAR outstr[1024]; SQLSMALLINT outstrlen; int ret; struct my_con* ptr; char conn_str[MAX_CONN_STR_LEN]; if (!id) { LM_ERR("invalid parameter value\n"); return 0; } ptr = (struct my_con*)pkg_malloc(sizeof(struct my_con)); if (!ptr) { LM_ERR("no more memory left\n"); return 0; } memset(ptr, 0, sizeof(struct my_con)); ptr->ref = 1; // allocate environment handle ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(ptr->env)); if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) { LM_ERR("could not alloc a SQL handle\n"); if (ptr) pkg_free(ptr); return 0; } // set the environment ret = SQLSetEnvAttr(ptr->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) { LM_ERR("could not set the environment\n"); goto err1; } // allocate connection handle ret = SQLAllocHandle(SQL_HANDLE_DBC, ptr->env, &(ptr->dbc)); if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) { LM_ERR("could not alloc a connection handle %d\n", ret); goto err1; } if (!db_unixodbc_build_conn_str(id, conn_str)) { LM_ERR("failed to build connection string\n"); goto err2; } LM_DBG("opening connection: unixodbc://xxxx:xxxx@%s/%s\n", ZSW(id->host), ZSW(id->database)); ret = SQLDriverConnect(ptr->dbc, NULL, (SQLCHAR*)conn_str, SQL_NTS, outstr, sizeof(outstr), &outstrlen, SQL_DRIVER_COMPLETE); if (SQL_SUCCEEDED(ret)) { LM_DBG("connection succeeded with reply <%s>\n", outstr); if (ret == SQL_SUCCESS_WITH_INFO) { LM_DBG("driver reported the following diagnostics\n"); db_unixodbc_extract_error("SQLDriverConnect", ptr->dbc, SQL_HANDLE_DBC, NULL); } } else { LM_ERR("failed to connect\n"); db_unixodbc_extract_error("SQLDriverConnect", ptr->dbc, SQL_HANDLE_DBC, NULL); goto err2; } ptr->stmt_handle = NULL; ptr->timestamp = time(0); ptr->id = id; return ptr; err1: SQLFreeHandle(SQL_HANDLE_ENV, &(ptr->env)); if (ptr) pkg_free(ptr); return 0; err2: SQLFreeHandle(SQL_HANDLE_ENV, &(ptr->env)); SQLFreeHandle(SQL_HANDLE_DBC, &(ptr->dbc)); if (ptr) pkg_free(ptr); return 0; } /* * Close the connection and release memory */ void db_unixodbc_free_connection(struct my_con* con) { if (!con) return; SQLFreeHandle(SQL_HANDLE_ENV, con->env); SQLDisconnect(con->dbc); SQLFreeHandle(SQL_HANDLE_DBC, con->dbc); pkg_free(con); } void db_unixodbc_extract_error(const char *fn, const SQLHANDLE handle, const SQLSMALLINT type, char* stret) { SQLINTEGER i = 0; SQLINTEGER native; SQLCHAR state[ 7 ]; SQLCHAR text[256]; SQLSMALLINT len; SQLRETURN ret; do { ret = SQLGetDiagRec(type, handle, ++i, state, &native, text, sizeof(text), &len ); if (SQL_SUCCEEDED(ret)) { LM_ERR("unixodbc:%s=%s:%ld:%ld:%s\n", fn, state, (long)i, (long)native, text); if(stret) strcpy( stret, (char*)state ); } } while( ret == SQL_SUCCESS ); } opensips-2.2.2/modules/db_unixodbc/con.h000066400000000000000000000054431300170765700202510ustar00rootroot00000000000000/* * UNIXODBC module * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) */ #ifndef MY_CON_H #define MY_CON_H #include "../../db/db_pool.h" #include "../../db/db_id.h" #include #include #include #include #include #include #define STRN_LEN 1024 typedef struct strn { char s[STRN_LEN]; } strn; struct my_con { struct db_id* id; /**< Connection identifier */ unsigned int ref; /**< Reference count */ struct pool_con *async_pool; /**< Subpool of identical database handles */ int no_transfers; /**< Number of async queries to this backend */ struct db_transfer *transfers; /**< Array of ongoing async operations */ struct pool_con *next; /**< Next element in the pool (different db_id) */ SQLHENV env; SQLHSTMT stmt_handle; /* Actual result */ SQLHDBC dbc; /* Connection representation */ char** row; /* Actual row in the result */ time_t timestamp; /* Timestamp of last query */ }; /* * Some convenience wrappers */ #define CON_RESULT(db_con) (((struct my_con*)((db_con)->tail))->stmt_handle) #define CON_CONNECTION(db_con) (((struct my_con*)((db_con)->tail))->dbc) #define CON_ROW(db_con) (((struct my_con*)((db_con)->tail))->row) #define CON_TIMESTAMP(db_con) (((struct my_con*)((db_con)->tail))->timestamp) #define CON_ID(db_con) (((struct my_con*)((db_con)->tail))->id) #define CON_ENV(db_con) (((struct my_con*)((db_con)->tail))->env) #define MAX_CONN_STR_LEN 2048 /* * Create a new connection structure, * open the UNIXODBC connection and set reference count to 1 */ struct my_con* db_unixodbc_new_connection(struct db_id* id); /* * Close the connection and release memory */ void db_unixodbc_free_connection(struct my_con* con); char *db_unixodbc_build_conn_str(const struct db_id* id, char *buf); void db_unixodbc_extract_error(const char *fn, const SQLHANDLE handle, const SQLSMALLINT type, char* stret); #endif /* MY_CON_H */ opensips-2.2.2/modules/db_unixodbc/db_unixodbc.c000066400000000000000000000053071300170765700217440ustar00rootroot00000000000000/* * UNIXODBC module interface * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) */ #include "../../sr_module.h" #include "../../db/db.h" #include "dbase.h" #include "db_unixodbc.h" int auto_reconnect = 1; /* Default is enabled */ int use_escape_common = 0; /* Enable common escaping */ int db_unixodbc_bind_api(const str* mod, db_func_t *dbb); /* * MySQL database module interface */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)db_unixodbc_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"auto_reconnect", INT_PARAM, &auto_reconnect}, {"use_escape_common", INT_PARAM, &use_escape_common}, {0, 0, 0} }; struct module_exports exports = { "db_unixodbc", MOD_TYPE_SQLDB,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, 0, params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ 0, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0 /* per-child init function */ }; int db_unixodbc_bind_api(const str* mod, db_func_t *dbb) { if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); dbb->use_table = db_unixodbc_use_table; dbb->init = db_unixodbc_init; dbb->close = db_unixodbc_close; dbb->query = db_unixodbc_query; dbb->raw_query = db_unixodbc_raw_query; dbb->free_result = db_unixodbc_free_result; dbb->insert = db_unixodbc_insert; dbb->delete = db_unixodbc_delete; dbb->update = db_unixodbc_update; dbb->replace = db_unixodbc_replace; return 0; } opensips-2.2.2/modules/db_unixodbc/db_unixodbc.h000066400000000000000000000020551300170765700217460ustar00rootroot00000000000000/* * UNIXODBC module interface * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) */ #ifndef DB_MOD_H #define DB_MOD_H extern int auto_reconnect; extern int use_escape_common; #endif /* DB_MOD_H */ opensips-2.2.2/modules/db_unixodbc/dbase.c000066400000000000000000000211731300170765700205410ustar00rootroot00000000000000/* * UNIXODBC module core functions * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) * 2006-04-03 fixed invalid handle to extract error (sgupta) * 2006-04-04 removed deprecated ODBC functions, closed cursors on error * (sgupta) * 2006-05-05 Fixed reconnect code to actually work on connection loss * (sgupta) */ #include "../../mem/mem.h" #include "../../dprint.h" #include "../../db/db_query.h" #include "val.h" #include "con.h" #include "res.h" #include "db_unixodbc.h" #include "dbase.h" /* * Reconnect if connection is broken */ static int reconnect(const db_con_t* _h) { int ret = 0; SQLCHAR outstr[1024]; SQLSMALLINT outstrlen; char conn_str[MAX_CONN_STR_LEN]; LM_ERR("Attempting DB reconnect\n"); /* Disconnect */ SQLDisconnect (CON_CONNECTION(_h)); /* Reconnect */ if (!db_unixodbc_build_conn_str(CON_ID(_h), conn_str)) { LM_ERR("failed to build connection string\n"); return ret; } ret = SQLDriverConnect(CON_CONNECTION(_h), (void *)1, (SQLCHAR*)conn_str, SQL_NTS, outstr, sizeof(outstr), &outstrlen, SQL_DRIVER_COMPLETE); if (!SQL_SUCCEEDED(ret)) { LM_ERR("failed to connect\n"); db_unixodbc_extract_error("SQLDriverConnect", CON_CONNECTION(_h), SQL_HANDLE_DBC, NULL); return ret; } ret = SQLAllocHandle(SQL_HANDLE_STMT, CON_CONNECTION(_h), &CON_RESULT(_h)); if (!SQL_SUCCEEDED(ret)) { LM_ERR("Statement allocation error %d\n", (int)(long)CON_CONNECTION(_h)); db_unixodbc_extract_error("SQLAllocStmt", CON_CONNECTION(_h), SQL_HANDLE_DBC,NULL); return ret; } return ret; } /* * Send an SQL query to the server */ static int db_unixodbc_submit_query(const db_con_t* _h, const str* _s) { int ret = 0; SQLCHAR sqlstate[7]; if (!_h || !_s || !_s->s) { LM_ERR("invalid parameter value\n"); return -1; } /* first do some cleanup if required */ if(CON_RESULT(_h)) { SQLCloseCursor(CON_RESULT(_h)); SQLFreeHandle(SQL_HANDLE_STMT, CON_RESULT(_h)); } ret = SQLAllocHandle(SQL_HANDLE_STMT, CON_CONNECTION(_h), &CON_RESULT(_h)); if (!SQL_SUCCEEDED(ret)) { LM_ERR("statement allocation error %d\n", (int)(long)CON_CONNECTION(_h)); db_unixodbc_extract_error("SQLAllocStmt", CON_CONNECTION(_h), SQL_HANDLE_DBC, (char*)sqlstate); /* Connection broken */ if( !strncmp((char*)sqlstate,"08003",5) || !strncmp((char*)sqlstate,"08S01",5) ) { ret = reconnect(_h); if( !SQL_SUCCEEDED(ret) ) return ret; } else { return ret; } } ret=SQLExecDirect(CON_RESULT(_h), (SQLCHAR*)_s->s, _s->len); if (!SQL_SUCCEEDED(ret)) { SQLCHAR sqlstate[7]; LM_ERR("rv=%d. Query= %.*s\n", ret, _s->len, _s->s); db_unixodbc_extract_error("SQLExecDirect", CON_RESULT(_h), SQL_HANDLE_STMT, (char*)sqlstate); /* Connection broken */ if( !strncmp((char*)sqlstate,"08003",5) || !strncmp((char*)sqlstate,"08S01",5) ) { ret = reconnect(_h); if( SQL_SUCCEEDED(ret) ) { /* Try again */ ret=SQLExecDirect(CON_RESULT(_h), (SQLCHAR*)_s->s, _s->len); if (!SQL_SUCCEEDED(ret)) { LM_ERR("rv=%d. Query= %.*s\n", ret, _s->len, _s->s); db_unixodbc_extract_error("SQLExecDirect", CON_RESULT(_h), SQL_HANDLE_STMT, (char*)sqlstate); /* Close the cursor */ SQLCloseCursor(CON_RESULT(_h)); SQLFreeHandle(SQL_HANDLE_STMT, CON_RESULT(_h)); } } } else { /* Close the cursor */ SQLCloseCursor(CON_RESULT(_h)); SQLFreeHandle(SQL_HANDLE_STMT, CON_RESULT(_h)); } } return ret; } /* * Initialize database module * No function should be called before this */ db_con_t* db_unixodbc_init(const str* _url) { return db_do_init(_url, (void*)db_unixodbc_new_connection); } /* * Shut down database module * No function should be called after this */ void db_unixodbc_close(db_con_t* _h) { db_do_close(_h, db_unixodbc_free_connection); } /* * Retrieve result set */ static int db_unixodbc_store_result(const db_con_t* _h, db_res_t** _r) { SQLSMALLINT cols; SQLLEN aff_cols; if ((!_h) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } *_r = db_new_result(); if (*_r == 0) { LM_ERR("no memory left\n"); return -2; } SQLNumResultCols(CON_RESULT(_h), &cols); if (!cols) { SQLRowCount(CON_RESULT(_h), &aff_cols); if (aff_cols > 0) { (*_r)->col.n = 0; (*_r)->n = 0; return 0; } else { LM_ERR(" invalid SQL query\n"); db_free_result(*_r); *_r = 0; return -3; } } if (db_unixodbc_convert_result(_h, *_r) < 0) { LM_ERR("failed to convert result\n"); pkg_free(*_r); *_r = 0; return -4; } return 0; } /* * Release a result set from memory */ int db_unixodbc_free_result(db_con_t* _h, db_res_t* _r) { if ((!_h) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } if (db_free_result(_r) < 0) { LM_ERR("failed to free result structure\n"); return -1; } SQLFreeHandle(SQL_HANDLE_STMT, CON_RESULT(_h)); CON_RESULT(_h) = NULL; return 0; } /* * Query table for specified rows * _h: structure representing database connection * _k: key names * _op: operators * _v: values of the keys that must match * _c: column names to return * _n: number of key=values pairs to compare * _nc: number of columns to return * _o: order by the specified column */ int db_unixodbc_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r) { CON_RESET_CURR_PS(_h); /* no prepared statements support */ return db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r, db_unixodbc_val2str, db_unixodbc_submit_query, db_unixodbc_store_result); } /* * Execute a raw SQL query */ int db_unixodbc_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r) { CON_RESET_CURR_PS(_h); /* no prepared statements support */ return db_do_raw_query(_h, _s, _r, db_unixodbc_submit_query, db_unixodbc_store_result); } /* * Insert a row into specified table * _h: structure representing database connection * _k: key names * _v: values of the keys * _n: number of key=value pairs */ int db_unixodbc_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { CON_RESET_CURR_PS(_h); /* no prepared statements support */ return db_do_insert(_h, _k, _v, _n, db_unixodbc_val2str, db_unixodbc_submit_query); } /* * Delete a row from the specified table * _h: structure representing database connection * _k: key names * _o: operators * _v: values of the keys that must match * _n: number of key=value pairs */ int db_unixodbc_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n) { CON_RESET_CURR_PS(_h); /* no prepared statements support */ return db_do_delete(_h, _k, _o, _v, _n, db_unixodbc_val2str, db_unixodbc_submit_query); } /* * Update some rows in the specified table * _h: structure representing database connection * _k: key names * _o: operators * _v: values of the keys that must match * _uk: updated columns * _uv: updated values of the columns * _n: number of key=value pairs * _un: number of columns to update */ int db_unixodbc_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un) { CON_RESET_CURR_PS(_h); /* no prepared statements support */ return db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_unixodbc_val2str, db_unixodbc_submit_query); } /* * Just like insert, but replace the row if it exists */ int db_unixodbc_replace(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { CON_RESET_CURR_PS(_h); /* no prepared statements support */ return db_do_replace(_h, _k, _v, _n, db_unixodbc_val2str, db_unixodbc_submit_query); } /* * Store name of table that will be used by * subsequent database functions */ int db_unixodbc_use_table(db_con_t* _h, const str* _t) { return db_use_table(_h, _t); } opensips-2.2.2/modules/db_unixodbc/dbase.h000066400000000000000000000050461300170765700205470ustar00rootroot00000000000000/* * UNIXODBC module core functions * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) */ #ifndef DBASE_H #define DBASE_H #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" /* * Initialize database connection */ db_con_t* db_unixodbc_init(const str* _sqlurl); /* * Close a database connection */ void db_unixodbc_close(db_con_t* _h); /* * Free all memory allocated by get_result */ int db_unixodbc_free_result(db_con_t* _h, db_res_t* _r); /* * Do a query */ int db_unixodbc_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r); /* * Raw SQL query */ int db_unixodbc_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r); /* * Insert a row into table */ int db_unixodbc_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /* * Delete a row from table */ int db_unixodbc_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n); /* * Update a row in table */ int db_unixodbc_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un); /* * Just like insert, but replace the row if it exists */ int db_unixodbc_replace(const db_con_t* handle, const db_key_t* keys, const db_val_t* vals, const int n); /* * Store name of table that will be used by * subsequent database functions */ int db_unixodbc_use_table(db_con_t* _h, const str* _t); #endif /* DBASE_H */ opensips-2.2.2/modules/db_unixodbc/doc/000077500000000000000000000000001300170765700200605ustar00rootroot00000000000000opensips-2.2.2/modules/db_unixodbc/doc/db_unixodbc.xml000066400000000000000000000022771300170765700230720ustar00rootroot00000000000000 %docentities; ]> unixodbc Module &osipsname; Marco Lorrai abbeynet.it
marco.lorrai@abbeynet.it
Marco Lorrai
marco.lorrai@abbeynet.it
2005 2006 Marco Lorrai $Revision: 5901 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/db_unixodbc/doc/db_unixodbc_admin.xml000066400000000000000000000104601300170765700242330ustar00rootroot00000000000000 &adminguide;
Overview This module allows to use the unixodbc package with &osips;. It have been tested with mysql and the odbc connector, but it should work also with other database. The auth_db module works. For more information, see the http://www.unixodbc.org/ project web page. To see what DB engines can be used via unixodbc, look at http://www.unixodbc.org/drivers.html.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>auto_reconnect</varname> (int) Turns on or off the auto_reconnect mode. Default value is 1, this means it is enabled. Set the <quote>auto_reconnect</quote> parameter ... modparam("db_unixodbc", "auto_reconnect", 0) ...
<varname>use_escape_common</varname> (int) Escape values in query using internal escape_common() function. It escapes single quote ''', double quote '"', backslash '\', and NULL characters. You should enable this parameter if you know that the ODBC driver considers the above characters as special (for marking begin and end of a value, escape other characters ...). It prevents against SQL injection. Default value is 0 (0 = disabled; 1 = enabled). Set the <quote>use_escape_common</quote> parameter ... modparam("db_unixodbc", "use_escape_common", 1) ...
Exported Functions NONE
Installation and Running
Installing Prerequirement: you should first install unixodbc (or another program that implements the odbc standard, such iodbc), your database, and the right connector. Set the DSN in the odbc.ini file and the connector drivers in the odbcinst.ini file.
Configuring and Running In the opensips.conf file, add the line: .... loadmodule "/usr/local/lib/opensips/modules/db_unixodbc.so" .... You should also uncomment this: .... loadmodule "/usr/local/lib/opensips/modules/auth.so" loadmodule "/usr/local/lib/opensips/modules/auth_db.so" modparam("usrloc", "db_mode", 2) modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") .... and setting the DSN specified in the odbc.ini, inserting this with the url adding this line: .... modparam("usrloc|auth_db", "db_url", "unixodbc://opensips:opensipsrw@localhost/my_dsn") .... replacing my_dsn with the correct value. HINT: if unixodbc don't want to connect to mysql server, try restarting mysql server with: shell>safe_mysqld --user=mysql --socket=/var/lib/mysql/mysql.sock The connector search the socket in /var/lib/mysql/mysql.sock and not in /tmp/mysql.sock
opensips-2.2.2/modules/db_unixodbc/doc/db_unixodbc_devel.xml000066400000000000000000000003171300170765700242420ustar00rootroot00000000000000 &develguide; The module implements the &osips; DB API, in order to be used by other modules. opensips-2.2.2/modules/db_unixodbc/res.c000066400000000000000000000135231300170765700202540ustar00rootroot00000000000000/* * UNIXODBC module result related functions * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) * 2006-04-04 fixed memory leak in convert_rows (sgupta) * 2006-05-05 removed static allocation of 1k per column data (sgupta) */ #include "../../mem/mem.h" #include "../../dprint.h" #include "row.h" #include "../../db/db_res.h" #include "con.h" #include "res.h" #include #include /* * Get and convert columns from a result */ static inline int db_unixodbc_get_columns(const db_con_t* _h, db_res_t* _r) { int col; SQLSMALLINT cols; //columns number if ((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } SQLNumResultCols(CON_RESULT(_h), &cols); if (!cols) { LM_ERR("no columns returned from the query\n"); return -2; } else { LM_DBG("%d columns returned from the query\n", cols); } /* Save number of columns in the result structure */ RES_COL_N(_r) = cols; if (db_allocate_columns(_r, cols) != 0) { LM_ERR("could not allocate columns\n"); return -3; } for(col = 0; col < cols; col++) { char columnname[80]; SQLRETURN ret; SQLSMALLINT namelength, datatype, decimaldigits, nullable; SQLULEN columnsize; ret = SQLDescribeCol(CON_RESULT(_h), col + 1, (SQLCHAR *)columnname, 80, &namelength, &datatype, &columnsize, &decimaldigits, &nullable); if(!SQL_SUCCEEDED(ret)) { LM_ERR("SQLDescribeCol failed: %d\n", ret); db_unixodbc_extract_error("SQLExecDirect", CON_RESULT(_h), SQL_HANDLE_STMT, NULL); // FIXME should we fail here completly? } /* The pointer that is here returned is part of the result structure. */ RES_NAMES(_r)[col]->s = columnname; RES_NAMES(_r)[col]->len = namelength; LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_r)[col], col, RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s); switch(datatype) { case SQL_SMALLINT: case SQL_INTEGER: case SQL_TINYINT: case SQL_DECIMAL: case SQL_NUMERIC: LM_DBG("use DB_INT result type\n"); RES_TYPES(_r)[col] = DB_INT; break; case SQL_BIGINT: LM_DBG("use DB_BIGINT result type\n"); RES_TYPES(_r)[col] = DB_BIGINT; break; case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: LM_DBG("use DB_DOUBLE result type\n"); RES_TYPES(_r)[col] = DB_DOUBLE; break; case SQL_TYPE_TIMESTAMP: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: case SQL_TYPE_DATE: case SQL_TYPE_TIME: LM_DBG("use DB_DATETIME result type\n"); RES_TYPES(_r)[col] = DB_DATETIME; break; case SQL_CHAR: case SQL_VARCHAR: case SQL_WCHAR: case SQL_WVARCHAR: LM_DBG("use DB_STRING result type\n"); RES_TYPES(_r)[col] = DB_STRING; break; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: case SQL_BIT: case SQL_LONGVARCHAR: case SQL_WLONGVARCHAR: LM_DBG("use DB_BLOB result type\n"); RES_TYPES(_r)[col] = DB_BLOB; break; default: LM_WARN("unhandled data type column (%.*s) type id (%d), " "use DB_STRING as default\n", RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s, datatype); RES_TYPES(_r)[col] = DB_STRING; break; } } return 0; } /* * Convert rows from UNIXODBC to db API representation */ static inline int db_unixodbc_convert_rows(const db_con_t* _h, db_res_t* _r) { int row_n = 0, i = 0, ret = 0; SQLSMALLINT columns; strn* temp_row = NULL; str *rows = NULL; if((!_h) || (!_r)) { LM_ERR("invalid parameter\n"); return -1; } SQLNumResultCols(CON_RESULT(_h), (SQLSMALLINT *)&columns); temp_row = (strn*)pkg_malloc( columns*sizeof(strn) ); if(!temp_row) { LM_ERR("no private memory left\n"); return E_OUT_OF_MEM; } while (SQL_SUCCEEDED(ret = SQLFetch(CON_RESULT(_h)))) { for(i=0; i < columns; i++) { SQLLEN indicator; ret = SQLGetData(CON_RESULT(_h), i+1, SQL_C_CHAR, temp_row[i].s, STRN_LEN, &indicator); if (SQL_SUCCEEDED(ret)) { if (indicator == SQL_NULL_DATA) strcpy(temp_row[i].s, "NULL"); } else { LM_ERR("SQLGetData failed\n"); } } rows = db_unixodbc_dup_row(temp_row, row_n, columns); if (!rows) { LM_ERR("no more pkg mem\n"); return E_OUT_OF_MEM; } row_n++; } /* free temporary row data */ pkg_free(temp_row); CON_ROW(_h) = NULL; RES_ROW_N(_r) = row_n; if (row_n == 0) { RES_ROWS(_r) = NULL; return 0; } if (db_allocate_rows(_r, row_n) != 0) { LM_ERR("no private memory left\n"); return E_OUT_OF_MEM; } for (i = 0; i < row_n; i++) { if (db_unixodbc_convert_row(&rows[i * columns], _r, &RES_ROWS(_r)[i]) < 0) { LM_ERR("converting row failed #%d\n", i); RES_ROW_N(_r) = 0; db_free_rows(_r); return -4; } } return 0; } /* * Fill the structure with data from database */ int db_unixodbc_convert_result(const db_con_t* _h, db_res_t* _r) { if (!_h || !_r) { LM_ERR("invalid parameter\n"); return -1; } if (db_unixodbc_get_columns(_h, _r) < 0) { LM_ERR("getting column names failed\n"); return -2; } if (db_unixodbc_convert_rows(_h, _r) < 0) { LM_ERR("converting rows failed\n"); db_free_columns(_r); return -3; } return 0; } opensips-2.2.2/modules/db_unixodbc/res.h000066400000000000000000000024511300170765700202570ustar00rootroot00000000000000/* * UNIXODBC module result related functions * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2007-2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) */ #ifndef RES_H #define RES_H #include "../../db/db_res.h" #include "../../db/db_con.h" /** * Fill the result structure with data from the database. * \param _h database handle * \param _r result structure * \return zero on success, negative on errors */ int db_unixodbc_convert_result(const db_con_t* _h, db_res_t* _r); #endif /* RES_H */ opensips-2.2.2/modules/db_unixodbc/row.c000066400000000000000000000053411300170765700202710ustar00rootroot00000000000000/* * UNIXODBC module row related functions * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) * 2006-05-05 passing proper lengths of column data (sgupta) */ #include "../../dprint.h" #include "../../mem/mem.h" #include "../../db/db_row.h" #include "../../db/db_ut.h" #include "val.h" #include "row.h" #include "con.h" /* gradually growing buffer; holds MAX(rows x columns) pointers */ static str *rows; static int rows_size; /** * duplicate each column in pkg mem * * row : index of the given row (0-indexed) * columns : total columns in the given row */ str *db_unixodbc_dup_row(strn *in, int row, int columns) { int last, i; int len; i = row * columns + columns; if (rows_size < i) { if (rows_size == 0) rows_size = i; else if (rows_size + rows_size >= i) rows_size += rows_size; else rows_size = i; rows = pkg_realloc(rows, rows_size * sizeof *rows); if (!rows) return NULL; } last = row * columns; for (i = 0; i < columns; i++) { len = strlen(in[i].s) + 1; rows[last + i].s = pkg_malloc(len); if (!rows[last + i].s) goto out_free; memcpy(rows[last + i].s, in[i].s, len); rows[last + i].len = len; } return rows; out_free: for (i = 0; i < last; i++) pkg_free(rows[last + i].s); pkg_free(rows); rows = NULL; rows_size = 0; return NULL; } /* * Convert a row from result into db API representation */ int db_unixodbc_convert_row(const str *row, const db_res_t *_res, db_row_t *_r) { int i; if ((!row) || (!_res) || (!_r)) { LM_ERR("invalid parameter value\n"); return -1; } /* Save the number of columns in the ROW structure */ ROW_N(_r) = RES_COL_N(_res); for(i = 0; i < RES_COL_N(_res); i++) { if (db_unixodbc_str2val(RES_TYPES(_res)[i], &(ROW_VALUES(_r)[i]), row[i].s, row[i].len) < 0) { LM_ERR("failed to convert value\n"); LM_DBG("free row at %p\n", _r); db_free_row(_r); return -3; } } return 0; } opensips-2.2.2/modules/db_unixodbc/row.h000066400000000000000000000025621300170765700203000ustar00rootroot00000000000000/* * UNIXODBC module row related functions * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) */ #ifndef ROW_H #define ROW_H #include "../../db/db_con.h" #include "../../db/db_res.h" #include "../../db/db_row.h" #include "con.h" /* * Duplicate result columns in pkg memory, stack them linearly */ str *db_unixodbc_dup_row(strn *in, int row, int columns); /* * Convert a row from result into db API representation */ int db_unixodbc_convert_row(const str *row, const db_res_t *_res, db_row_t *_r); #endif /* ROW_H */ opensips-2.2.2/modules/db_unixodbc/val.c000066400000000000000000000146231300170765700202470ustar00rootroot00000000000000/* * UNIXODBC module * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) */ #include "../../dprint.h" #include "../../strcommon.h" #include "../../db/db_ut.h" #include "db_unixodbc.h" #include "val.h" #include "con.h" #include #include /* * Convert str to db value, does not copy strings */ int db_unixodbc_str2val(const db_type_t _t, db_val_t* _v, char* _s, const int _l) { static str dummy_string = {"", 0}; if (!_v) { LM_ERR("invalid parameter value\n"); return -1; } if (!_s || !strcmp(_s, "NULL")) { memset(_v, 0, sizeof(db_val_t)); /* Initialize the string pointers to a dummy empty * string so that we do not crash when the NULL flag * is set but the module does not check it properly */ VAL_STRING(_v) = dummy_string.s; VAL_STR(_v) = dummy_string; VAL_BLOB(_v) = dummy_string; VAL_TYPE(_v) = _t; VAL_NULL(_v) = 1; pkg_free(_s); return 0; } VAL_NULL(_v) = 0; switch(_t) { case DB_INT: LM_DBG("converting INT [%s]\n", _s); if (db_str2int(_s, &VAL_INT(_v)) < 0) { LM_ERR("converting integer value from string failed\n"); return -2; } else { VAL_TYPE(_v) = DB_INT; pkg_free(_s); return 0; } break; case DB_BIGINT: LM_DBG("converting BIGINT [%s]\n", _s); if (db_str2bigint(_s, &VAL_BIGINT(_v)) < 0) { LM_ERR("converting big integer value from string failed\n"); return -2; } else { VAL_TYPE(_v) = DB_BIGINT; pkg_free(_s); return 0; } break; case DB_BITMAP: LM_DBG("converting BITMAP [%s]\n", _s); if (db_str2int(_s, &VAL_INT(_v)) < 0) { LM_ERR("converting bitmap value from string failed\n"); return -3; } else { VAL_TYPE(_v) = DB_BITMAP; pkg_free(_s); return 0; } break; case DB_DOUBLE: LM_DBG("converting DOUBLE [%s]\n", _s); if (db_str2double(_s, &VAL_DOUBLE(_v)) < 0) { LM_ERR("converting double value from string failed\n"); return -4; } else { VAL_TYPE(_v) = DB_DOUBLE; pkg_free(_s); return 0; } break; case DB_STRING: LM_DBG("converting STRING [%s]\n", _s); VAL_STRING(_v) = _s; VAL_TYPE(_v) = DB_STRING; VAL_FREE(_v) = 1; return 0; case DB_STR: LM_DBG("converting STR [%.*s]\n", _l, _s); VAL_STR(_v).s = (char*)_s; VAL_STR(_v).len = _l; VAL_TYPE(_v) = DB_STR; VAL_FREE(_v) = 1; return 0; case DB_DATETIME: LM_DBG("converting DATETIME [%s]\n", _s); if (db_str2time(_s, &VAL_TIME(_v)) < 0) { LM_ERR("converting datetime value from string failed\n"); return -5; } else { VAL_TYPE(_v) = DB_DATETIME; pkg_free(_s); return 0; } break; case DB_BLOB: LM_DBG("converting BLOB [%.*s]\n", _l, _s); VAL_BLOB(_v).s = (char*)_s; VAL_BLOB(_v).len = _l; VAL_TYPE(_v) = DB_BLOB; VAL_FREE(_v) = 1; return 0; } return -6; } /* * Used when converting result from a query */ int db_unixodbc_val2str(const db_con_t* _c, const db_val_t* _v, char* _s, int* _len) { int l; char* old_s; if (!_c || !_v || !_s || !_len || !*_len) { LM_ERR("invalid parameter value\n"); return -1; } if (VAL_NULL(_v)) { if (*_len < sizeof("NULL")) { LM_ERR("buffer too small\n"); return -1; } *_len = snprintf(_s, *_len, "NULL"); return 0; } switch(VAL_TYPE(_v)) { case DB_INT: if (db_int2str(VAL_INT(_v), _s, _len) < 0) { LM_ERR("converting string to int failed\n"); return -2; } else { return 0; } break; case DB_BIGINT: if (db_bigint2str(VAL_BIGINT(_v), _s, _len) < 0) { LM_ERR("converting string to big int failed\n"); return -2; } else { return 0; } break; case DB_BITMAP: if (db_int2str(VAL_BITMAP(_v), _s, _len) < 0) { LM_ERR("converting string to int failed\n"); return -3; } else { return 0; } break; case DB_DOUBLE: if (db_double2str(VAL_DOUBLE(_v), _s, _len) < 0) { LM_ERR("converting string to double failed\n"); return -4; } else { return 0; } break; case DB_STRING: l = strlen(VAL_STRING(_v)); if (*_len < (l * 2 + 3)) { LM_ERR("destination buffer too short\n"); return -5; } else { old_s = _s; *_s++ = '\''; if(use_escape_common) { _s += escape_common(_s, (char*)VAL_STRING(_v), l); } else { memcpy(_s, VAL_STRING(_v), l); _s += l; } *_s++ = '\''; *_s = '\0'; /* FIXME */ *_len = _s - old_s; return 0; } break; case DB_STR: l = VAL_STR(_v).len; if (*_len < (l * 2 + 3)) { LM_ERR("destination buffer too short\n"); return -6; } else { old_s = _s; *_s++ = '\''; if(use_escape_common) { _s += escape_common(_s, VAL_STR(_v).s, l); } else { memcpy(_s, VAL_STR(_v).s, l); _s += l; } *_s++ = '\''; *_s = '\0'; /* FIXME */ *_len = _s - old_s; return 0; } break; case DB_DATETIME: if (db_time2str(VAL_TIME(_v), _s, _len) < 0) { LM_ERR("converting string to time_t failed\n"); return -7; } else { return 0; } break; case DB_BLOB: l = VAL_BLOB(_v).len; if (*_len < (l * 2 + 3)) { LM_ERR("destination buffer too short\n"); return -8; } else { old_s = _s; *_s++ = '\''; if(use_escape_common) { _s += escape_common(_s, VAL_BLOB(_v).s, l); } else { memcpy(_s, VAL_BLOB(_v).s, l); _s += l; } *_s++ = '\''; *_s = '\0'; /* FIXME */ *_len = _s - old_s; return 0; } break; default: LM_DBG("unknown data type\n"); return -9; } /*return -8; --not reached*/ } opensips-2.2.2/modules/db_unixodbc/val.h000066400000000000000000000025451300170765700202540ustar00rootroot00000000000000/* * UNIXODBC module * * Copyright (C) 2005-2006 Marco Lorrai * Copyright (C) 2008 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2005-12-01 initial commit (chgen) */ #ifndef VAL_H #define VAL_H #include #include #include #include #include "../../db/db_val.h" #include "../../db/db.h" /* * Does not copy strings */ int db_unixodbc_str2val(const db_type_t _t, db_val_t* _v, char* _s, const int _l); /* * Used when converting result from a query */ int db_unixodbc_val2str(const db_con_t* _c, const db_val_t* _v, char* _s, int* _len); #endif /* VAL_H */ opensips-2.2.2/modules/db_virtual/000077500000000000000000000000001300170765700171665ustar00rootroot00000000000000opensips-2.2.2/modules/db_virtual/Makefile000066400000000000000000000001471300170765700206300ustar00rootroot00000000000000include ../../Makefile.defs auto_gen= NAME=db_virtual.so LIBS= DEFS+= include ../../Makefile.modules opensips-2.2.2/modules/db_virtual/README000066400000000000000000000155451300170765700200600ustar00rootroot00000000000000virtual_db Module Razvan Pistolea Edited by Razvan Pistolea Copyright © 2009 Voice Sistem SRL Revision History Revision $Revision: 5917 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. The idea 1.1.2. Modes 1.1.3. Failures 1.1.4. The timer process 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_urls (str) 1.3.2. db_probe_time (integer) 1.3.3. db_max_consec_retrys (integer) 1.4. Exported MI Functions 1.4.1. db_get 1.4.2. db_set List of Examples 1.1. Set db_urls parameter 1.2. Set db_probe_time parameter 1.3. Set db_max_consec_retrys parameter Chapter 1. Admin Guide 1.1. Overview 1.1.1. The idea A virtual db will expose the same front db api however, it will backed by many real db. This means that a virtual db url translates to many real db urls. This virtual layer also enables us to use the real dbs in multiple ways such as: parallel, failover(hotswap), round-robin. Therefore: each virtual db url with associated real dbs and a way to use(mode) it's real dbs must be specified. 1.1.2. Modes The implemented modes are: * FAILOVER Use the first url; if it fails, use the next one, redo operation. * PARALLEL Use all the urls in the virtual db url. Fails if all the urls fail. * ROUND (round-robin) Use the next url each time; if it fails, use the next one, redo operation. There are conceptual limitations to the above modes with respect to the operation. For example in parallel mode it is ok to insert into multiple dbs the same value but it is bad to query multiple dbs into the same result. This implementation threats such operation as it would be in failover mode. Conceptual allowed(1) and not allowed(0) operations parallel round dbb->use_table dbb->init dbb->close dbb->query 0 1 dbb->fetch_result 0 0 dbb->raw_query 0 1 dbb->free_result 0 0 dbb->insert 1 1 dbb->delete 1 0 dbb->update 1 0 dbb->replace 1 0 dbb->last_inserted_id 0 0 dbb->insert_update 1 1 dbb->async_raw_query 0 1 dbb->async_raw_resume 0 1 Note 1: The capabilities returned are the minimum common denominator of all the dbs in the set. The capabilities are reduced even more based on the mode of the set (PARALLEL, ROUND). Note 2: The capabilities will not be reduced for PARALLEL mode but conceptual not allowed operations will be done on a single db. Ex: query will only query one db. Note 3: Since version 2.2 db_virtual supports async_raw_query and async_raw_resume functions currently implemented only by the mysql database engine. 1.1.3. Failures When an operation from a process on a real db fails: it is marked (global and local CAN flag down) its connection closed Later a timer process (probe): foreach virtual db url foreach real db_url if global CAN down try to connect if ok global CAN up close connection Later each process: if local CAN down and global CAN up if db_max_consec_retrys * try to connect if ok local CAN up Note *: there could be inconsistencies between the probe and each process so a retry limit is in order. It is reset and ignored by an MI command. 1.1.4. The timer process The timer process(probe) is a process that tries to reconnect to failed dbs from time to time. It is a separate process so that when it blocks (for a timeout on the connection) it doesn't matter. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * At least one real db module. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. db_urls (str) Multiple value parameter used for virtual db urls declaration. Example 1.1. Set db_urls parameter ... modparam("group","db_url","virtual://set1") modparam("presence|presence_xml", "db_url","virtual://set2") modparam("db_virtual", "db_urls", "define set1 PARALLEL") modparam("db_virtual", "db_urls", "mysql://opensips:opensipsrw@localhost /testa") modparam("db_virtual", "db_urls", "postgres://opensips:opensipsrw@localh ost/opensips") modparam("db_virtual", "db_urls", "define set2 FAILOVER") modparam("db_virtual", "db_urls", "mysql://opensips:opensipsrw@localhost /testa") ... 1.3.2. db_probe_time (integer) Time interval after which a registered timer process attempts to check failed(as reported by other processes) connections to real dbs. The probe will connect and disconnect to the failed real db and announce others. Default value is 10 (10 sec). Example 1.2. Set db_probe_time parameter ... modparam("db_virtual", "db_probe_time", 20) ... 1.3.3. db_max_consec_retrys (integer) After the timer process has reported that it can connect to the real db, other processes will try to reconnect to it. There are cases where although the probe could connect some might fail. This parameter represents the number of consecutive failed retries that a process will do before it gives up. This value is reset and suppressed by a MI function(db_set). Default value is 10 (10 consecutive times). Example 1.3. Set db_max_consec_retrys parameter ... modparam("db_virtual", "db_max_consec_retrys", 20) ... 1.4. Exported MI Functions 1.4.1. db_get Return information about global state of the real dbs. Name: db_get Parameters: * None. MI FIFO Command Format: db_get _empty_line_ 1.4.2. db_set Sets the permissions for real dbs access per set per db. Sets the reconnect reset flag. Name: db_set Parameters: * set_index [int] * db_url_index [int] * may_use_db_flag [boolean] * ignore db_max_consec_retrys[boolean](optional) db_set 3 2 0 1 means: * 3 - the fourth set (must exist) * 2 - the third url in the fourth set(must exist) * 0 - processes are not allowed to use that url * 1 - reset and suppress db_max_consec_retrys MI FIFO Command Format: db_set 3 2 0 1 _empty_line_ opensips-2.2.2/modules/db_virtual/db_virtual.c000066400000000000000000000444311300170765700214730ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Razvan * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-07-29 initial version (razvan) */ #include "../../sr_module.h" #include "../../db/db.h" #include "dbase.h" #include "db_virtual.h" #include "../../mi/mi.h" #include "../../pt.h" #include #include "../../mem/shm_mem.h" #include #include "../../timer.h" #define MAX_BUF 1028 /* probing time interval */ int db_probe_time = 10; /* max consecutive retries before give up */ int db_max_consec_retrys = 10; /* for debug.. try_reconect with or without a timer process(probe) */ int db_reconnect_with_timer = 1; /* exactly once condition keeper */ /*char is_initialized = 0;*/ /* dbs state in shared memory seen global */ info_global_t * global = NULL; /* dbs handles in private memory local to each process */ //handle_set_t * private_handles = NULL; handle_private_t * private = NULL; /* db_urls pointer older until initialization */ char* db_urls_list[100]; int db_urls_count=0; int init_global(void); static void destroy(void); int db_virtual_bind_api(const str* mod, db_func_t *dbb); struct mi_root *db_get_info(struct mi_root *cmd, void *param); struct mi_root* db_set_info(struct mi_root* cmd, void* param); //struct mi_root* db_add_url(struct mi_root* cmd, void* param); static int store_urls( modparam_t type, void* val); /* * Virtual database module interface */ static cmd_export_t cmds[] = { {"db_bind_api", (cmd_function)db_virtual_bind_api, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { //{"db_file", STR_PARAM, &db_file.s}, {"db_probe_time", INT_PARAM, &db_probe_time}, {"db_max_consec_retrys", INT_PARAM, &db_max_consec_retrys}, {"db_urls", STR_PARAM|USE_FUNC_PARAM,(void*)store_urls}, {0, 0, 0} }; /* * MI */ static mi_export_t mi_cmds[] = { {"db_get", 0, db_get_info, MI_NO_INPUT_FLAG, 0, 0 }, {"db_set", 0, db_set_info, 0, 0, 0 }, //{"db_add", db_add_url, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "db_virtual", MOD_TYPE_SQLDB, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, 0, params, /* module parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ virtual_mod_init, /* module initialization function */ 0, /* response function*/ destroy, /* destroy function */ 0 /* per-child init function */ }; int add_url(int index, char * name){ LM_DBG("add url (%i . %s)\n", index, name); int i; LM_DBG("add another url %p\n", global->set_list[index].db_list); /* realoc */ i = global->set_list[index].size; /* db_list realloc */ global->set_list[index].db_list = (info_db_t *) shm_realloc(global->set_list[index].db_list, (i+1)* sizeof(info_db_t)); if(!global->set_list[index].db_list) MEM_ERR(MEM_SHM); global->set_list[index].size++; /* db_url */ global->set_list[index].db_list[i].db_url.s = (char *) shm_malloc(strlen(name) * sizeof(char)); global->set_list[index].db_list[i].db_url.len = strlen(name); memcpy(global->set_list[index].db_list[i].db_url.s, name, strlen(name)); global->set_list[index].db_list[i].flags = CAN_USE | MAY_USE; return 0; error: return 1; } int add_set(char * name, char * mode){ int nmode = 0; if(strncmp(mode, "FAILOVER", strlen("FAILOVER")) == 0) nmode = FAILOVER; else if(strncmp(mode, "PARALLEL", strlen("PARALLEL")) == 0) nmode = PARALLEL; else if(strncmp(mode, "ROUND", strlen("ROUND")) == 0) nmode = ROUND; LM_DBG("add set=%s mode=%i\n", name, nmode); if (!global) { global = shm_malloc(sizeof *global); if (!global) MEM_ERR(MEM_SHM); memset(global, 0, sizeof *global); } /* realloc set_list */ int i = global->size; global->set_list = (info_set_t *)shm_realloc(global->set_list, (i+1)*sizeof(info_set_t)); if(!global->set_list) MEM_ERR(MEM_SHM); memset(&global->set_list[i], 0, sizeof *global->set_list); global->size++; global->set_list[i].set_name.s = (char *) shm_malloc(strlen(name)*sizeof(char)); global->set_list[i].set_name.len = strlen(name); memcpy(global->set_list[i].set_name.s, name, strlen(name)); /* set mode */ global->set_list[i].set_mode = nmode; global->set_list[i].size = 0; return 0; error: return 1; } static int store_urls( modparam_t type, void* val){ db_urls_list[db_urls_count] = val; db_urls_count++; return 0; } int init_global(void){//str *info_set_mapping){ int i, j; char *s, *p; int count = -1; for(i=0; i< db_urls_count; i++){ s = db_urls_list[i]; LM_DBG("line = %s\n", s); if(s && strlen(s) && s[0]!='#'){ if(strncmp("define", s, strlen("define")) == 0){ s += strlen("define")+1; p = strchr(s, ' '); /* set1=FAILOVER */ *p = 0; p++; LM_DBG("set_mode = {%s}, mode = {%s}\n", s, p); add_set(s, p); /*LM_ERR("done\n"); */ count++; } else{ if (count == -1) { LM_ERR("db_virtual module cannot start with no DB sets defined!\n"); return -1; } /* mysql:........ */ LM_DBG("db = %s\n", s); add_url(count, s); } } } if (!global) { LM_ERR("db_virtual module cannot start with no DB URLs defined!\n"); return -1; } for(i = 0; i< global->size; i++) for(j=0; jset_list[i].size; j++){ global->set_list[i].db_list[j].dbf.cap = 0; if(db_bind_mod(&global->set_list[i].db_list[j].db_url, &global->set_list[i].db_list[j].dbf)){ LM_ERR("cant bind db : %.*s", global->set_list[i].db_list[j].db_url.len, global->set_list[i].db_list[j].db_url.s); goto error; } } LM_DBG("global done\n"); /*is_initialized = 1; */ return 0; error: destroy(); return -1; } int init_private_handles(void){ LM_DBG("Init private handles\n"); private = (handle_private_t* ) pkg_malloc(sizeof(handle_private_t)); if(!private) MEM_ERR(MEM_PKG); memset(private, 0, sizeof(handle_private_t)); private->size = global->size; private->hset_list = (handle_set_t*)pkg_malloc(private->size * sizeof(handle_set_t)); if(!private->hset_list) MEM_ERR(MEM_PKG); memset(private->hset_list, 0, private->size * sizeof(handle_set_t)); return 0; error: return -1; } static void reconnect_timer(unsigned int ticks, void *data) { LM_DBG("reconnect with timer\n"); int i,j; db_con_t * con; for(i=0; i < global-> size; i++){ for(j=0; j < global->set_list[i].size; j++){ /* if CAN DOWN */ if(!(global->set_list[i].db_list[j].flags & CAN_USE)){ con = global->set_list[i].db_list[j].dbf.init( &global->set_list[i].db_list[j].db_url); if(!con){ LM_DBG("Cant reconnect on timer to db %.*s, %i\n", global->set_list[i].db_list[j].db_url.len, global->set_list[i].db_list[j].db_url.s, global->set_list[i].db_list[j].flags); }else{ LM_DBG("Can reconnect on timer to db %.*s\n", global->set_list[i].db_list[j].db_url.len, global->set_list[i].db_list[j].db_url.s); global->set_list[i].db_list[j].dbf.close(con); global->set_list[i].db_list[j].flags |= CAN_USE; } } } } } int virtual_mod_init(void){ LM_DBG("VIRTUAL client version is %s\n","1.33"); if(!global){ int i,j; if (init_global() || init_private_handles()) return -1; //print structure for(i = 0; i< global->size; i++){ LM_DBG("set {%.*s}\n", global->set_list[i].set_name.len, global->set_list[i].set_name.s); for(j=0; j< global->set_list[i].size; j++){ LM_DBG("url \t{%.*s}%p\n", global->set_list[i].db_list[j].db_url.len, global->set_list[i].db_list[j].db_url.s, &global->set_list[i].db_list[j].dbf); } } if(db_reconnect_with_timer){ if (register_timer("db_virtual-reconnect", reconnect_timer, NULL, db_probe_time, TIMER_FLAG_DELAY_ON_DELAY)<0) { LM_ERR("failed to register keepalive timer\n"); } } } return 0; } static void destroy(void){ LM_NOTICE("destroying module...\n"); int i, j; if(global){ if(global->set_list){ for(i=0; i< global->size; i++){ if(global->set_list[i].db_list){ for(j=0; j< global->set_list[i].size; j++){ if(global->set_list[i].db_list[j].db_url.s){ shm_free(global->set_list[i].db_list[j].db_url.s); } } shm_free(global->set_list[i].db_list); } } shm_free(global->set_list); } shm_free(global); } } int db_virtual_bind_api(const str* mod, db_func_t *dbb) { LM_DBG("BINDING API for virtual url: %.*s\n", mod->len, mod->s); int i, j; str s; //int len; if(!global) if(virtual_mod_init()) return 1; if(dbb==NULL) return -1; memset(dbb, 0, sizeof(db_func_t)); /* virtual://set5 * p */ s.s = strchr(mod->s, '/'); s.s +=2; for(i=0; i< global->size; i++){ if(strncmp(s.s, global->set_list[i].set_name.s, global->set_list[i].set_name.len) == 0) break; } LM_DBG("REDUCING capabilities for %.*s\n", global->set_list[i].set_name.len, global->set_list[i].set_name.s); dbb->cap = DB_CAP_FAILOVER; for(j=0; j< global->set_list[i].size; j++){ dbb->cap &= global->set_list[i].db_list[j].dbf.cap; } if(global->set_list[i].set_mode == FAILOVER){ dbb->cap &= DB_CAP_FAILOVER; }else if(global->set_list[i].set_mode == PARALLEL){ dbb->cap &= DB_CAP_PARALLEL; }else if(global->set_list[i].set_mode == ROUND){ dbb->cap &= DB_CAP_ROUND; } dbb->use_table = db_virtual_use_table; dbb->init = db_virtual_init; dbb->close = db_virtual_close; dbb->query = db_virtual_query; dbb->fetch_result = db_virtual_fetch_result; dbb->raw_query = db_virtual_raw_query; dbb->free_result = db_virtual_free_result; dbb->insert = db_virtual_insert; dbb->delete = db_virtual_delete; dbb->update = db_virtual_update; dbb->replace = db_virtual_replace; dbb->last_inserted_id = db_virtual_last_inserted_id; dbb->insert_update = db_virtual_insert_update; dbb->async_raw_query = db_virtual_async_raw_query; dbb->async_resume = db_virtual_async_resume; dbb->async_free_result = db_virtual_async_free_result; return 0; } struct mi_root *db_get_info(struct mi_root *cmd, void *param){ int i,j; struct mi_root *rpl_tree; struct mi_node *rpl; struct mi_node *node; struct mi_node *node2; struct mi_attr *attr; char *p; int len; int can_use; int may_use; int recon; char buf[MAX_BUF]; rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; for(i=0; i < global->size; i++ ){ node = add_mi_node_child(rpl, MI_IS_ARRAY, MI_SSTR("SET"), 0, 0 ); if (node==0) goto error; p = int2str((unsigned long)i, &len); attr = add_mi_attr(node, MI_DUP_VALUE, MI_SSTR("i"), p, len); if (attr==0) goto error; attr = add_mi_attr(node, 0, MI_SSTR("name"), global->set_list[i].set_name.s,global->set_list[i].set_name.len); if (attr==0) goto error; switch(global->set_list[i].set_mode){ case FAILOVER: sprintf(buf, "%s", "FAILOVER"); break; case PARALLEL: sprintf(buf, "%s", "PARALLEL"); break; case ROUND: sprintf(buf, "%s", "ROUND"); break; } attr = add_mi_attr(node, MI_DUP_VALUE, MI_SSTR("mode"), buf,strlen(buf)); if (attr==0) goto error; for(j=0; j< global->set_list[i].size; j++){ node2 = add_mi_node_child(node, 0, MI_SSTR("DB"), 0, 0); if(node2 == 0) goto error; p = int2str((unsigned long)j, &len); attr = add_mi_attr(node2, MI_DUP_VALUE, MI_SSTR("j"), p, len); if (attr==0) goto error; attr = add_mi_attr(node2, 0, MI_SSTR("name"), global->set_list[i].db_list[j].db_url.s, global->set_list[i].db_list[j].db_url.len); if (attr==0) goto error; can_use = (global->set_list[i].db_list[j].flags & CAN_USE) ? 1 : 0; may_use = (global->set_list[i].db_list[j].flags & MAY_USE) ? 1 : 0; recon = (global->set_list[i].db_list[j].flags & RERECONNECT) ? 1 :0; p = int2str((unsigned long)can_use, &len); LM_DBG("can flag %.*s\n", len, p); attr = add_mi_attr(node2, MI_DUP_VALUE, MI_SSTR("can"), p, len); if (attr==0) goto error; p = int2str((unsigned long)may_use, &len); LM_DBG("may flag%.*s\n", len, p); attr = add_mi_attr(node2, MI_DUP_VALUE, MI_SSTR("may"), p, len); if (attr==0) goto error; p = int2str((unsigned long)recon, &len); LM_DBG("reset_recon flag %.*s\n", len, p); attr = add_mi_attr(node2, MI_DUP_VALUE, MI_SSTR("r_rec"), p, len); if (attr==0) goto error; } } return rpl_tree; error: LM_ERR("failed to add node\n"); free_mi_tree(rpl_tree); return 0; } struct mi_root* db_set_info(struct mi_root* cmd, void* param){ struct mi_node* node= NULL; str index1 = {0,0}; str index2 = {0,0}; str state = {0,0}; str recon = {0,0}; unsigned int nindex1; unsigned int nindex2; unsigned int nstate; unsigned int nrecon = 0; int flags; // get index node = cmd->node.kids; if(node == NULL){ LM_ERR("no index1\n"); return 0; } index1 = node->value; if(index1.s == NULL){ LM_ERR("empty index1\n"); return 0; } if(str2int(&index1, &nindex1)){ LM_ERR("invalid index1(not int)\n"); return 0; } if(nindex1 >= global->size){ LM_ERR("invalid index1 value\n"); // fa un return la rezultat return 0; } // get set node = node->next; if(node == NULL){ LM_ERR("no index\n"); return 0; } index2 = node->value; if(index2.s == NULL){ LM_ERR("empty index\n"); return 0; } if(str2int(&index2, &nindex2)){ LM_ERR("invalid index(not int)\n"); return 0; } if(nindex2 >= global->set_list[nindex1].size){ LM_ERR("invalid index value\n"); /* fa un return la rezultat */ return 0; } /* get may state 1=UP 0=DOWN */ node = node->next; if(node == NULL){ LM_ERR("no state\n"); return 0; } state= node->value; if(state.s == NULL){ LM_ERR("empty state\n"); return 0; } if(str2int(&state, &nstate)){ LM_ERR("invalid state(not int)\n"); return 0; } if(!(nstate==1 || nstate==0)){ LM_ERR("invalid state value\n"); return 0; } flags = global->set_list[nindex1].db_list[nindex2].flags; /* get possible rerecon state 1=UP 0= DOWN */ node = node->next; if(node != NULL){ recon= node->value; if(recon.s == NULL){ LM_ERR("empty recon\n"); return 0; } if(str2int(&recon, &nrecon)){ LM_ERR("invalid recon(not int)\n"); return 0; } if(!(nrecon==1 || nrecon==0)){ LM_ERR("invalid recon value\n"); return 0; } if(nrecon) flags |= RERECONNECT; else flags &= NOT_RERECONNECT; } if(nstate) flags |= MAY_USE; else flags &=NOT_MAY_USE; global->set_list[nindex1].db_list[nindex2].flags = flags; /* dont worry about race conditions */ return init_mi_tree( 200, MI_SSTR(MI_OK)); } opensips-2.2.2/modules/db_virtual/db_virtual.h000066400000000000000000000055521300170765700215010ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Razvan * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-07-29 initial version (razvan) */ #ifndef DB_MOD_H #define DB_MOD_H #include "../../db/db_val.h" #include "../../str.h" #include "../../db/db_cap.h" #define CAN_USE 0x0001 #define MAY_USE 0x0002 #define RERECONNECT 0x0010 #define NOT_CAN_USE ~CAN_USE #define NOT_MAY_USE ~MAY_USE #define NOT_RERECONNECT ~RERECONNECT #define CLOSED 0x0020 #define NOT_CLOSED ~CLOSED #define DB_CAP_FAILOVER (0 | DB_CAP_QUERY | DB_CAP_RAW_QUERY | DB_CAP_INSERT | \ DB_CAP_DELETE | DB_CAP_UPDATE | DB_CAP_REPLACE | DB_CAP_FETCH | \ DB_CAP_LAST_INSERTED_ID | DB_CAP_INSERT_UPDATE | DB_CAP_ASYNC_RAW_QUERY) #define DB_CAP_PARALLEL (0 | DB_CAP_QUERY | DB_CAP_RAW_QUERY | DB_CAP_INSERT | \ DB_CAP_DELETE | DB_CAP_UPDATE | DB_CAP_REPLACE | DB_CAP_FETCH | \ DB_CAP_LAST_INSERTED_ID | DB_CAP_INSERT_UPDATE | DB_CAP_ASYNC_RAW_QUERY) #define DB_CAP_ROUND (0 | DB_CAP_QUERY | DB_CAP_RAW_QUERY | DB_CAP_INSERT | \ DB_CAP_FETCH | \ DB_CAP_LAST_INSERTED_ID | DB_CAP_INSERT_UPDATE | DB_CAP_ASYNC_RAW_QUERY) enum DB_MODE {FAILOVER=0, PARALLEL, ROUND}; #define MEM_PKG "pkg" #define MEM_SHM "share" #define MEM_ERR(mem_type) \ do { LM_ERR("No more %s memory\n",mem_type);\ goto error;\ } while(0) /* * global info * * info_db * url * func * flags * * info_set * name * mode * * db_list * size * * info_global * * hset_list * size */ typedef struct info_db{ str db_url; /* url to real db */ db_func_t dbf; /* db functions and capabilities */ int flags; /* global CAN, MAY flags */ }info_db_t; typedef struct info_set{ str set_name; /* name of the set; ex: set1, set2...*/ char set_mode; /* mode of the set: PARALLEL, FAILOVER, ... */ info_db_t* db_list; int size; }info_set_t; typedef struct info_global{ info_set_t* set_list; int size; }info_global_t; int virtual_mod_init(void); int init_private_handles(void); #endif /* DB_MOD_H */ opensips-2.2.2/modules/db_virtual/dbase.c000066400000000000000000000726701300170765700204240ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Razvan * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-07-29 initial version (razvan) */ #include "../../mem/mem.h" #include "../../dprint.h" #include "../../db/db_query.h" #include "../../db/db_ut.h" #include "db_virtual.h" #include "dbase.h" #include "../../timer.h" #define MAXBUF (1<<14) /* Conceptual allowed operations * parallel round dbb->use_table dbb->init dbb->close * dbb->query 0 1 dbb->fetch_result 0 0.5 dbb->raw_query 0 1 dbb->free_result 0 0.5 dbb->insert 1 1 dbb->delete 1 0 dbb->update 1 0 dbb->replace 1 0 dbb->last_inserted_id 0 0 dbb->insert_update 1 1 * * Explanation: * it makes sense to insert in multiple dbs * but not to query and fetch from multiple dbs. * */ extern int db_access_mode; extern info_global_t* global; //extern handle_set_t * private_handles; extern handle_private_t* private; extern int db_reconnect_with_timer; extern int db_max_consec_retrys; str use_table={0,0}; void get_update_flags(handle_set_t * private_handles){ int i; for(i=0; i< global->set_list[private_handles->set_index].size; i++){ if(global->set_list[ private_handles->set_index].db_list[i].flags & MAY_USE){ private_handles->con_list[i].flags |= MAY_USE; }else{ private_handles->con_list[i].flags &= NOT_MAY_USE; } } } void set_update_flags(int db_index, handle_set_t * private_handles){ if(0<=db_index && db_index < global->set_list[private_handles->set_index].size){ if(private_handles->con_list[db_index].flags & CAN_USE){ if(!db_reconnect_with_timer) global->set_list[ private_handles->set_index].db_list[db_index].flags |= CAN_USE; }else{ global->set_list[ private_handles->set_index].db_list[db_index].flags &= NOT_CAN_USE; } } } void try_reconnect(handle_set_t * p){ LM_DBG("try reconnect\n"); int i; //handle_set_t * p = (handle_set_t*)_h->tail;//private_handles; for(i=0; i< global->set_list[p->set_index].size; i++){ if(!(p->con_list[i].flags & CAN_USE) && global->set_list[p->set_index].db_list[i].flags & CAN_USE){ if( global->set_list[p->set_index].db_list[i].flags & RERECONNECT){ p->con_list[i].no_retries = db_max_consec_retrys; } if(p->con_list[i].no_retries-- > 0){ p->con_list[i].con = global->set_list[p->set_index].db_list[i].dbf.init( &global->set_list[p->set_index].db_list[i].db_url); if(!p->con_list[i].con){ LM_DBG("cant reconnect to db %.*s\n", global->set_list[p->set_index].db_list[i].db_url.len, global->set_list[p->set_index].db_list[i].db_url.s); continue; } global->set_list[p->set_index].db_list[i].dbf.use_table( p->con_list[i].con, &use_table); p->con_list[i].flags |= CAN_USE; set_update_flags(i, p); p->con_list[i].no_retries = db_max_consec_retrys; } } } } #define db_generic_operation2(FUNCTION_WITH_PARAMS, is_parallel, is_roundable, use_rc)\ do{ \ LM_DBG("f call \n"); \ int i; \ int rc=0, rc2=1; \ int max_loop; \ handle_con_t * handle; \ db_func_t * f; \ handle_set_t * p = (handle_set_t*)_h->tail; \ \ LM_DBG("f call handle size = %i\n", p->size); \ \ max_loop = p->size; \ \ get_update_flags(p); \ try_reconnect(p); \ \ switch(global->set_list[p->set_index].set_mode){ \ \ case ROUND: /* ROBIN HOOD = ROB_IN_WOOD, ROBE_N' HOOD*/ \ if(is_roundable) \ p->curent_con = (p->curent_con+1) % p->size; \ \ case FAILOVER: \ do{ \ /* get next valid handle*/ \ handle = &p->con_list[p->curent_con]; \ f = &global->set_list[p->set_index].db_list[p->curent_con].dbf; \ \ if((handle->flags & CAN_USE) && (handle->flags & MAY_USE)){ \ LM_DBG("flags1 = %i\n", p->con_list[p->curent_con].flags); \ \ \ \ /* call f*/ \ rc = f->FUNCTION_WITH_PARAMS; \ if((rc && use_rc)){ \ LM_DBG("failover call failed\n"); \ /* set local can not use flag*/ \ handle->flags &= NOT_CAN_USE; \ \ \ /* close connection*/ \ f->close(handle->con); \ } \ set_update_flags(p->curent_con, p); \ }else{ \ LM_DBG("flags2 = %i\n", p->con_list[p->curent_con].flags); \ \ /* try next*/ \ rc = 1; \ p->curent_con = (p->curent_con+1)%p->size; \ } \ LM_DBG("curent_con = %i\n", p->curent_con); \ }while((rc && use_rc) && max_loop--); \ \ rc2=rc; \ break; \ \ case PARALLEL: \ if(is_parallel){ \ for(i=0; i< max_loop; i++){ \ handle = &p->con_list[i]; \ f = &global->set_list[p->set_index].db_list[i].dbf; \ if((handle->flags & CAN_USE) && (handle->flags & MAY_USE)){ \ \ \ rc = f->FUNCTION_WITH_PARAMS; \ if((rc && use_rc)){ \ LM_DBG("parallel call failed\n"); \ handle->flags &= NOT_CAN_USE; \ \ \ \ f->close(handle->con); \ } \ set_update_flags(i, p); \ } \ else{ \ rc = 1; \ } \ \ if(use_rc) \ rc2 &= rc; \ else \ rc2 = rc; \ \ \ \ } \ }else{ \ do{ \ /* get next valid handle*/ \ handle = &p->con_list[p->curent_con]; \ f = &global->set_list[p->set_index].db_list[p->curent_con].dbf; \ \ if((handle->flags & CAN_USE) && (handle->flags & MAY_USE)){ \ LM_DBG("flags1 = %i\n", p->con_list[p->curent_con].flags); \ \ \ \ /* call f*/ \ rc = f->FUNCTION_WITH_PARAMS; \ if(rc){ \ /* set local can not use flag*/ \ handle->flags &= NOT_CAN_USE; \ \ /* set global can not use flag*/ \ set_update_flags(p->curent_con, p); \ \ /* close connection*/ \ f->close(handle->con); \ } \ }else{ \ LM_DBG("flags2 = %i\n", p->con_list[p->curent_con].flags); \ \ /* try next*/ \ rc = 1; \ p->curent_con = (p->curent_con+1)%p->size; \ } \ LM_DBG("curent_con = %i\n", p->curent_con); \ }while((rc && use_rc) && max_loop--); \ \ rc2=rc ; \ } \ break; \ \ default: \ rc2 = 1; \ break; \ } \ \ return rc2; \ }while(0) \ db_con_t* db_virtual_init(const str* _set_url) { /* find set_url in global state get index allocate populate with db_init */ LM_DBG("INIT set_name, %.*s\n", _set_url->len, _set_url->s); int i; int index = -1; db_con_t * res=NULL; char buffer[256]; char * token; handle_set_t* p; if(!_set_url || !_set_url->s){ LM_ERR("url or url.s NULL\n"); return NULL; } /* so that loadmodule order does not matter */ if(!global){ if(virtual_mod_init()) return NULL; } //if(!private_handles){ if(!private || !private->hset_list){ LM_ERR("private handles NULL %p %p \n", private, private->hset_list); return NULL; } /* find set_name in global */ memset(buffer, 0, 256); memcpy(buffer, _set_url->s, _set_url->len); token = strtok(buffer, "/"); token = strtok(NULL, "/"); LM_DBG("token = %s\n", token); //for(i=0; i< global->size; i++){ for(i=0; i< private->size; i++){ if(strncmp(token, global->set_list[i].set_name.s, global->set_list[i].set_name.len) == 0){ LM_DBG("found set_name: %s\n", token); index = i; break; } } if(index < 0){ LM_ERR("set_name: %.*s not found\n", _set_url->len, _set_url->s); return NULL; } p = &private->hset_list[index]; /* alocat res */ res = (db_con_t*)pkg_malloc(sizeof(db_con_t)); if (!res) { MEM_ERR(MEM_PKG); } memset(res, 0, sizeof(db_con_t)); /* if refcount > 1 just return*/ p->refcount++; if(p->refcount > 1){ res->tail = (unsigned long)&(private->hset_list[index]); } //p = &private->hset_list[index]; /* else allocate */ p->set_index = index; p->curent_con = 0; p->size = global->set_list[index].size; p->con_list = (handle_con_t*) pkg_malloc(p->size * sizeof(handle_con_t)); if(!p->con_list) MEM_ERR(MEM_PKG); memset(p->con_list, 0, p->size * sizeof(handle_con_t)); /* populate */ for(i=0; i< p->size; i++){ p->con_list[i].flags = global->set_list[p->set_index].db_list[i].flags; if((p->con_list[i].flags & CAN_USE) && (p->con_list[i].flags & MAY_USE)) p->con_list[i].con = global->set_list[p->set_index].db_list[i].dbf.init( &global->set_list[p->set_index].db_list[i].db_url); if(!p->con_list[i].con){ LM_ERR("cant init db %.*s\n", global->set_list[p->set_index].db_list[i].db_url.len, global->set_list[p->set_index].db_list[i].db_url.s); p->con_list[i].flags &=NOT_CAN_USE; set_update_flags(i, p); } p->con_list[i].no_retries = db_max_consec_retrys; } /* link the private handles */ res->tail = (unsigned long)p; return res; error: if(p->con_list) pkg_free(p->con_list); if(res) pkg_free(res); return NULL; } void db_virtual_close(db_con_t* _h) { LM_DBG("CLOSE\n"); int i; //handle_set_t * p = private_handles; handle_set_t * p = (handle_set_t*)_h->tail; p->refcount--; /* if recount is zero close all and free structure */ /* else return */ if(p->refcount == 0){ for(i=0; i < p->size; i++){ if(p->con_list[i].flags & CAN_USE){ global->set_list[p->set_index].db_list[i].dbf.close( p->con_list[i].con); } } pkg_free(p->con_list); } return; } int db_virtual_use_table(db_con_t* _h, const str* _t) { LM_DBG("USE TABLE\n"); int i; int rc=0; int rc2=0; handle_set_t * p = (handle_set_t*)_h->tail;//private_handles; for(i=0; i < p->size; i++){ if(p->con_list[i].flags & CAN_USE){ rc = global->set_list[p->set_index].db_list[i].dbf.use_table( p->con_list[i].con, _t); if(rc) LM_ERR("USE TABLE failed: %.*s\n", _t->len, _t->s); rc2 |=rc; } } /* store the string for later use */ if(use_table.s) pkg_free(use_table.s); use_table.s = (char*) pkg_malloc(_t->len * sizeof(char)); use_table.len = _t->len; memcpy(use_table.s, _t->s, _t->len); return rc2; } int db_virtual_free_result(db_con_t* _h, db_res_t* _r) { db_generic_operation2(free_result(handle->con, _r), 0, 0, 1); } int db_virtual_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r) { db_generic_operation2(query(handle->con, _k, _op, _v, _c, _n, _nc, _o, _r), 0, 1, 1); } int db_virtual_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows) { db_generic_operation2(fetch_result(handle->con, _r, nrows), 0, 0, 1); } int db_virtual_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r) { db_generic_operation2(raw_query(handle->con, _s, _r),0, 1, 1); } int db_virtual_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { db_generic_operation2(insert(handle->con, _k, _v, _n),1, 1, 1); } int db_virtual_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n) { db_generic_operation2(delete(handle->con, _k, _o, _v, _n), 1, 0, 1); } int db_virtual_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un) { db_generic_operation2(update(handle->con, _k, _o, _v, _uk, _uv, _n, _un),1, 0, 1); } int db_virtual_replace(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n) { db_generic_operation2(replace(handle->con, _k, _v, _n),1, 0, 1); } int db_virtual_last_inserted_id(const db_con_t* _h) { db_generic_operation2(last_inserted_id(handle->con),0, 0, 0); } int db_virtual_insert_update(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v,const int _n) { db_generic_operation2(insert_update(handle->con, _k, _v, _n),1, 1, 1); } #define CURRCON(_ah) (_ah->current_con) #define db_generic_async_operation(_h,_ah, _resume_f, FUNC, ...) \ do { \ int mode; \ int rc=0; \ handle_con_t * handle; \ db_func_t * f; \ handle_set_t * p = (handle_set_t*)_h->tail; \ \ LM_DBG("f call handle size = %i\n", p->size); \ \ get_update_flags(p); \ try_reconnect(p); \ \ mode = global->set_list[p->set_index].set_mode; \ \ if (mode == PARALLEL) { \ LM_WARN("PARALLEL not supported in async! using FAILOVER!\n"); \ } else if (mode != FAILOVER && mode != ROUND) { \ LM_ERR("mode %d not supported!\n", mode); \ return -1; \ } \ \ do { \ handle = &p->con_list[CURRCON(_ah)]; \ f = &global->set_list[p->set_index].db_list[CURRCON(_ah)].dbf; \ \ if((handle->flags & CAN_USE) && (handle->flags & MAY_USE)){ \ LM_DBG("flags1 = %i\n", p->con_list[CURRCON(_ah)].flags); \ \ if (f == NULL || f->FUNC == NULL) { \ LM_ERR("async not supported for this backend!\n"); \ return -1; \ } \ rc=f->FUNC(__VA_ARGS__); \ \ if (rc<0) { \ /* FIXME quite a complicated case \ * if the db disconected by any means then \ * anything shall be ok if continue with other DB \ * if cannot open new connections to mysql \ * then things are gonna be messed up if continuing */ \ LM_ERR("failover call failed rc:%d\n", rc); \ /* set local can not use flag*/ \ handle->flags &= NOT_CAN_USE; \ \ /* close connection*/ \ set_update_flags(CURRCON(_ah), p); \ \ f->close(handle->con); \ /* if failed before placing the fd in reactor \ * we keep on */ \ if ((--_ah->cons_rem) == 0) { \ LM_ERR("All databases failed!! No hope for you!\n"); \ return -1; \ } \ \ /* try next*/ \ rc = -1; \ CURRCON(_ah) = (CURRCON(_ah)+1)%p->size; \ } else { \ if (_resume_f) \ async_status = ASYNC_CHANGE_FD; \ set_update_flags(CURRCON(_ah), p); \ return rc; \ } \ } else { \ LM_DBG("flags2 = %i\n", p->con_list[CURRCON(_ah)].flags); \ if ((--_ah->cons_rem) == 0) { \ LM_ERR("All databases failed!! No hope for you!\n"); \ return -1; \ } \ \ /* try next*/ \ rc = -1; \ CURRCON(_ah) = (CURRCON(_ah)+1)%p->size; \ } \ LM_DBG("curent_con = %i\n", CURRCON(_ah)); \ } while ((_ah)->cons_rem); /* should never exit here */ \ \ return rc; \ }while (0); int db_virtual_async_raw_query(db_con_t *_h, const str *_s, void **_priv) { handle_async_t* _ah; handle_con_t * _handle; handle_set_t * _p = (handle_set_t*)_h->tail; if (_s->len > MAXBUF) { LM_ERR("query exceeds buffer size(%d)!\n", MAXBUF); return -1; } if ((_ah=pkg_malloc(sizeof(handle_async_t)+_s->len)) == NULL) { LM_ERR("no more pkg\n"); return -1; } else { /* automatically jump to next DB destination only for ROUND ROBIN * else, for failover, will jump only if something goes wrong */ if (global->set_list[_p->set_index].set_mode == ROUND) _p->curent_con = (_p->curent_con+1)%_p->size; _ah->current_con = _p->curent_con; _ah->cons_rem = _p->size; /* store the query for further calls */ _ah->query.len = _s->len; _ah->query.s = (char*)(_ah+1); memcpy(_ah->query.s, _s->s, _s->len); *_priv = _ah; } _handle = &_p->con_list[CURRCON(_ah)]; db_generic_async_operation(_h, _ah,0, async_raw_query, _handle->con, _s, &_ah->_priv ); return 0; } int db_virtual_async_resume(db_con_t *_h, int fd, db_res_t **_r, void *_priv) { handle_async_t *_ah; db_func_t * _f; handle_con_t * _handle; handle_set_t * _p = (handle_set_t*)_h->tail; if (!_priv) { LM_ERR("Expecting async handle! Nothing received!\n"); return -1; } _ah = (handle_async_t *)_priv; _handle = &_p->con_list[CURRCON(_ah)]; _f = &global->set_list[_p->set_index].db_list[CURRCON(_ah)].dbf; /* call the resume function */ if (_f->async_resume(_handle->con, fd, _r, _ah->_priv) < 0) { _handle->flags &= NOT_CAN_USE; /* close connection*/ _f->close(_handle->con); /* we did all we could, but no con worked * do something to those DBs */ if ((--_ah->cons_rem) == 0) { LM_ERR("All databases failed!! No hope for you!\n"); return -1; } /* try next DB; no matter RR or FAILOVER */ CURRCON(_ah) = (CURRCON(_ah) +1)%_p->size; _handle = &_p->con_list[CURRCON(_ah)]; /* try the next database connection */ db_generic_async_operation(_h, _ah,1, async_raw_query, _handle->con, &_ah->query, _ah->_priv ); } /* if here means it worked; we set this connection as current connection * for other messages to come */ _p->curent_con = CURRCON(_ah); async_status = ASYNC_DONE; return 0; } int db_virtual_async_free_result(db_con_t *_h, db_res_t *_r, void *_priv) { handle_async_t *_ah = (handle_async_t *)_priv; db_func_t *_f; handle_con_t *_handle; handle_set_t *_p = (handle_set_t *)_h->tail; if (!_ah) { LM_ERR("Expecting async handle! Nothing received!\n"); return -1; } _handle = &_p->con_list[CURRCON(_ah)]; _f = &global->set_list[_p->set_index].db_list[CURRCON(_ah)].dbf; if (_f->async_free_result(_handle->con, _r, _ah->_priv) < 0) { LM_ERR("error while freeing async query result\n"); return -1; } pkg_free(_ah); return 0; } #undef CURRCON opensips-2.2.2/modules/db_virtual/dbase.h000066400000000000000000000113541300170765700204210ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Razvan * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-07-29 initial version (razvan) */ #ifndef DBASE_H #define DBASE_H #include "../../db/db_val.h" #include "../../str.h" /* * private handle * * handle_con * * con * flags * no_retries * * * * handle_set * curent * * con_list * size * * refcount * * * * handle_private * * hset_list * size * */ /* * global info * * info_db * url * func * flags * * info_set * name * mode * * db_list * size * * info_global * * hset_list * size */ /* * Each process has "private handle". * There is a global shared "global info". * Each "private handle" coresponds to "global info". * */ typedef struct handle_con { db_con_t* con; /* handle for using a real database */ int flags; /* private CAN, MAY flags */ int no_retries; /* failed retries left before giving up */ } handle_con_t; typedef struct handle_set { /* index in the info_global list; used for the 1 to 1 relationship */ int set_index; /* index in con_list; used for FAILOVER and ROUNDROBIN mode */ int curent_con; handle_con_t* con_list; int size; /* used for exactly once call of real init() and close() */ int refcount; } handle_set_t; typedef struct handle_private { handle_set_t* hset_list; int size; } handle_private_t; typedef struct handle_async { int current_con; /* current connection index */ int cons_rem; /* number of cons to try */ str query; /* the query for this function call */ void *_priv; /* backend-specific data related to the async query */ } handle_async_t; /* * Initialize database connection */ db_con_t* db_virtual_init(const str* _sqlurl); /* * Close a database connection */ void db_virtual_close(db_con_t* _h); /* * Free all memory allocated by get_result */ int db_virtual_free_result(db_con_t* _h, db_res_t* _r); /* * Do a query */ int db_virtual_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op, const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc, const db_key_t _o, db_res_t** _r); /* * fetch rows from a result */ int db_virtual_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows); /* * Raw SQL query */ int db_virtual_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r); /* * Insert a row into table */ int db_virtual_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /* * Delete a row from table */ int db_virtual_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const int _n); /* * Update a row in table */ int db_virtual_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un); /* * Just like insert, but replace the row if it exists */ int db_virtual_replace(const db_con_t* handle, const db_key_t* keys, const db_val_t* vals, const int n); /* * Returns the last inserted ID */ int db_virtual_last_inserted_id(const db_con_t* _h); /* * Insert a row into table, update on duplicate key */ int db_virtual_insert_update(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n); /* * Async raw SQL query */ int db_virtual_async_raw_query(db_con_t *_h, const str *_s, void **_priv); /* * Async SQL query resume function */ int db_virtual_async_resume(db_con_t *_h, int fd, db_res_t **_r, void *_priv); /* * Cleans up anything related to (and including) an async SQL result */ int db_virtual_async_free_result(db_con_t *_h, db_res_t *_r, void *_priv); /* * Store name of table that will be used by * subsequent database functions */ int db_virtual_use_table(db_con_t* _h, const str* _t); #endif /* DBASE_H */ opensips-2.2.2/modules/db_virtual/doc/000077500000000000000000000000001300170765700177335ustar00rootroot00000000000000opensips-2.2.2/modules/db_virtual/doc/db_virtual.xml000066400000000000000000000020601300170765700226060ustar00rootroot00000000000000 %docentities; ]> virtual_db Module &osipsname; Razvan Pistolea razvy000@yahoo.com Razvan Pistolea razvy000@yahoo.com 2009 &voicesystem; $Revision: 5917 $ $Date$ &admin; &faq; opensips-2.2.2/modules/db_virtual/doc/db_virtual_admin.xml000066400000000000000000000260351300170765700237660ustar00rootroot00000000000000 &adminguide;
Overview
The idea A virtual db will expose the same front db api however, it will backed by many real db. This means that a virtual db url translates to many real db urls. This virtual layer also enables us to use the real dbs in multiple ways such as: parallel, failover(hotswap), round-robin. Therefore: each virtual db url with associated real dbs and a way to use(mode) it's real dbs must be specified.
Modes The implemented modes are: FAILOVER Use the first url; if it fails, use the next one, redo operation. PARALLEL Use all the urls in the virtual db url. Fails if all the urls fail. ROUND (round-robin) Use the next url each time; if it fails, use the next one, redo operation. There are conceptual limitations to the above modes with respect to the operation. For example in parallel mode it is ok to insert into multiple dbs the same value but it is bad to query multiple dbs into the same result. This implementation threats such operation as it would be in failover mode. Conceptual allowed(1) and not allowed(0) operations parallel round dbb->use_table dbb->init dbb->close dbb->query 0 1 dbb->fetch_result 0 0 dbb->raw_query 0 1 dbb->free_result 0 0 dbb->insert 1 1 dbb->delete 1 0 dbb->update 1 0 dbb->replace 1 0 dbb->last_inserted_id 0 0 dbb->insert_update 1 1 dbb->async_raw_query 0 1 dbb->async_raw_resume 0 1 Note 1: The capabilities returned are the minimum common denominator of all the dbs in the set. The capabilities are reduced even more based on the mode of the set (PARALLEL, ROUND). Note 2: The capabilities will not be reduced for PARALLEL mode but conceptual not allowed operations will be done on a single db. Ex: query will only query one db. Note 3: Since version 2.2 db_virtual supports async_raw_query and async_raw_resume functions currently implemented only by the mysql database engine.
Failures When an operation from a process on a real db fails: it is marked (global and local CAN flag down) its connection closed Later a timer process (probe): foreach virtual db url foreach real db_url if global CAN down try to connect if ok global CAN up close connection Later each process: if local CAN down and global CAN up if db_max_consec_retrys * try to connect if ok local CAN up Note *: there could be inconsistencies between the probe and each process so a retry limit is in order. It is reset and ignored by an MI command.
The timer process The timer process(probe) is a process that tries to reconnect to failed dbs from time to time. It is a separate process so that when it blocks (for a timeout on the connection) it doesn't matter.
Dependencies
&osips; Modules The following modules must be loaded before this module: At least one real db module.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>db_urls</varname> (str) Multiple value parameter used for virtual db urls declaration. Set <varname>db_urls</varname> parameter ... modparam("group","db_url","virtual://set1") modparam("presence|presence_xml", "db_url","virtual://set2") modparam("db_virtual", "db_urls", "define set1 PARALLEL") modparam("db_virtual", "db_urls", "mysql://opensips:opensipsrw@localhost/testa") modparam("db_virtual", "db_urls", "postgres://opensips:opensipsrw@localhost/opensips") modparam("db_virtual", "db_urls", "define set2 FAILOVER") modparam("db_virtual", "db_urls", "mysql://opensips:opensipsrw@localhost/testa") ...
<varname>db_probe_time</varname> (integer) Time interval after which a registered timer process attempts to check failed(as reported by other processes) connections to real dbs. The probe will connect and disconnect to the failed real db and announce others. Default value is 10 (10 sec). Set <varname>db_probe_time</varname> parameter ... modparam("db_virtual", "db_probe_time", 20) ...
<varname>db_max_consec_retrys</varname> (integer) After the timer process has reported that it can connect to the real db, other processes will try to reconnect to it. There are cases where although the probe could connect some might fail. This parameter represents the number of consecutive failed retries that a process will do before it gives up. This value is reset and suppressed by a MI function(db_set). Default value is 10 (10 consecutive times). Set <varname>db_max_consec_retrys</varname> parameter ... modparam("db_virtual", "db_max_consec_retrys", 20) ...
Exported MI Functions
<function moreinfo="none">db_get</function> Return information about global state of the real dbs. Name: db_get Parameters: None. MI FIFO Command Format: db_get _empty_line_
<function moreinfo="none">db_set</function> Sets the permissions for real dbs access per set per db. Sets the reconnect reset flag. Name: db_set Parameters: set_index [int] db_url_index [int] may_use_db_flag [boolean] ignore db_max_consec_retrys[boolean](optional) db_set 3 2 0 1 means: 3 - the fourth set (must exist) 2 - the third url in the fourth set(must exist) 0 - processes are not allowed to use that url 1 - reset and suppress db_max_consec_retrys MI FIFO Command Format: db_set 3 2 0 1 _empty_line_
opensips-2.2.2/modules/dialog/000077500000000000000000000000001300170765700162725ustar00rootroot00000000000000opensips-2.2.2/modules/dialog/Makefile000066400000000000000000000002631300170765700177330ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=dialog.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/dialog/README000066400000000000000000001627441300170765700171700ustar00rootroot00000000000000dialog Module Bogdan-Andrei Iancu Edited by Bogdan-Andrei Iancu Vladut-Stefan Paiu Copyright © 2006-2009 Voice Sistem SRL Revision History Revision $Revision: 8771 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How it works 1.3. Dialog profiling 1.4. Dialog replication 1.5. Dependencies 1.5.1. OpenSIPS Modules 1.5.2. External Libraries or Applications 1.6. Exported Parameters 1.6.1. enable_stats (integer) 1.6.2. hash_size (integer) 1.6.3. log_profile_hash_size (integer) 1.6.4. rr_param (string) 1.6.5. default_timeout (integer) 1.6.6. dlg_extra_hdrs (string) 1.6.7. dlg_match_mode (integer) 1.6.8. db_url (string) 1.6.9. db_mode (integer) 1.6.10. db_update_period (integer) 1.6.11. options_ping_interval (integer) 1.6.12. reinvite_ping_interval (integer) 1.6.13. table_name (string) 1.6.14. call_id_column (string) 1.6.15. from_uri_column (string) 1.6.16. from_tag_column (string) 1.6.17. to_uri_column (string) 1.6.18. to_tag_column (string) 1.6.19. from_cseq_column (string) 1.6.20. to_cseq_column (string) 1.6.21. from_route_column (string) 1.6.22. to_route_column (string) 1.6.23. from_contact_column (string) 1.6.24. to_contact_column (string) 1.6.25. from_sock_column (string) 1.6.26. to_sock_column (string) 1.6.27. dlg_id_column (string) 1.6.28. state_column (string) 1.6.29. start_time_column (string) 1.6.30. timeout_column (string) 1.6.31. profiles_column (string) 1.6.32. vars_column (string) 1.6.33. sflags_column (string) 1.6.34. mflags_column (string) 1.6.35. flags_column (string) 1.6.36. profiles_with_value (string) 1.6.37. profiles_no_value (string) 1.6.38. db_flush_vals_profiles (int) 1.6.39. timer_bulk_del_no (int) 1.6.40. cachedb_url (string) 1.6.41. profile_value_prefix (string) 1.6.42. profile_no_value_prefix (string) 1.6.43. profile_size_prefix (string) 1.6.44. profile_timeout (int) 1.6.45. accept_replicated_dialogs (int) 1.6.46. replicate_dialogs_to (int) 1.6.47. accept_replicated_profiles (int) 1.6.48. replicate_profiles_to (int) 1.6.49. accept_replicated_profile_timeout (int) 1.6.50. auth_check (int) 1.6.51. replicate_profiles_buffer (string) 1.6.52. replicate_profiles_check (string) 1.6.53. replicate_profiles_timer (string) 1.6.54. replicate_profiles_expire (string) 1.7. Exported Functions 1.7.1. create_dialog() 1.7.2. match_dialog() 1.7.3. validate_dialog() 1.7.4. fix_route_dialog() 1.7.5. get_dialog_info(attr,var,key,key_val) 1.7.6. set_dlg_profile(profile,[value]) 1.7.7. unset_dlg_profile(profile,[value]) 1.7.8. is_in_profile(profile,[value]) 1.7.9. get_profile_size(profile,[value],size) 1.7.10. set_dlg_flag(idx) 1.7.11. test_and_set_dlg_flag(idx, value) 1.7.12. reset_dlg_flag(idx) 1.7.13. is_dlg_flag_set(idx) 1.7.14. store_dlg_value(name,val) 1.7.15. fetch_dlg_value(name,pvar) 1.8. Exported statistics 1.8.1. active_dialogs 1.8.2. early_dialogs 1.8.3. processed_dialogs 1.8.4. expired_dialogs 1.8.5. failed_dialogs 1.8.6. create_sent 1.8.7. update_sent 1.8.8. delete_sent 1.8.9. create_recv 1.8.10. update_recv 1.8.11. delete_recv 1.9. Exported MI Functions 1.9.1. dlg_list 1.9.2. dlg_list_ctx 1.9.3. dlg_end_dlg 1.9.4. profile_get_size 1.9.5. profile_list_dlgs 1.9.6. profile_get_values 1.9.7. profile_end_dlgs 1.9.8. dlg_db_sync 1.9.9. dlg_restore_db 1.9.10. list_all_profiles 1.10. Exported pseudo-variables 1.10.1. $DLG_count 1.10.2. $DLG_status 1.10.3. $DLG_lifetime 1.10.4. $DLG_flags 1.10.5. $DLG_dir 1.10.6. $DLG_did 1.10.7. $DLG_end_reason 1.10.8. $DLG_timeout 1.10.9. $dlg_val(name) 1.11. Exported Events 1.11.1. E_DLG_STATE_CHANGED 2. Developer Guide 2.1. Available Functions 2.1.1. register_dlgcb (dialog, type, cb, param, free_param_cb) 3. Frequently Asked Questions List of Examples 1.1. Set enable_stats parameter 1.2. Set hash_size parameter 1.3. Set hash_size parameter 1.4. Set rr_param parameter 1.5. Set default_timeout parameter 1.6. Set dlf_extra_hdrs parameter 1.7. Set dlg_match_mode parameter 1.8. Set db_url parameter 1.9. Set db_mode parameter 1.10. Set db_update_period parameter 1.11. Set options_ping_interval parameter 1.12. Set reinvite_ping_interval parameter 1.13. Set table_name parameter 1.14. Set call_id_column parameter 1.15. Set from_uri_column parameter 1.16. Set from_tag_column parameter 1.17. Set to_uri_column parameter 1.18. Set to_tag_column parameter 1.19. Set from_cseq_column parameter 1.20. Set to_cseq_column parameter 1.21. Set from_route_column parameter 1.22. Set to_route_column parameter 1.23. Set from_contact_column parameter 1.24. Set to_contact_column parameter 1.25. Set from_sock_column parameter 1.26. Set to_sock_column parameter 1.27. Set dlg_id_column parameter 1.28. Set state_column parameter 1.29. Set start_time_column parameter 1.30. Set timeout_column parameter 1.31. Set profiles_column parameter 1.32. Set vars_column parameter 1.33. Set sflags_column parameter 1.34. Set mflags_column parameter 1.35. Set flags_column parameter 1.36. Set profiles_with_value parameter 1.37. Set profiles_no_value parameter 1.38. Set db_flush_vals_profiles parameter 1.39. Set timer_bulk_del_no parameter 1.40. Set cachedb_url parameter 1.41. Set profile_value_prefix parameter 1.42. Set profile_no_value_prefix parameter 1.43. Set profile_size_prefix parameter 1.44. Set profile_timeout parameter 1.45. Set accept_replicated_dialogs parameter 1.46. Set replicate_dialogs_to parameter 1.47. Set accept_replicated_profiles parameter 1.48. Set replicate_profiles_to parameter 1.49. Set accept_replicated_profile_timeout parameter 1.50. Set auth_check parameter 1.51. Set replicate_profiles_buffer parameter 1.52. Set replicate_profiles_check parameter 1.53. Set replicate_profiles_timer parameter 1.54. Set replicate_profiles_expire parameter 1.55. create_dialog() usage 1.56. match_dialog() usage 1.57. validate_dialog() usage 1.58. fix_route_dialog() usage 1.59. get_dialog_info usage 1.60. set_dlg_profile usage 1.61. unset_dlg_profile usage 1.62. is_in_profile usage 1.63. get_profile_size usage 1.64. set_dlg_flag usage 1.65. test_and_set_dlg_flag usage 1.66. reset_dlg_flag usage 1.67. is_dlg_flag_set usage 1.68. store_dlg_value usage 1.69. fetch_dlg_value usage Chapter 1. Admin Guide 1.1. Overview The dialog module provides dialog awareness to the OpenSIPS proxy. Its functionality is to keep trace of the current dialogs, to offer information about them (like how many dialogs are active). Aside tracking, the dialog module offers functionalities like flags and attributes per dialog (persistent data across dialog), dialog profiling and dialog termination (on timeout base or external triggered). The module, via an internal API, also provide the foundation to build on top of it more complex dialog-based functionalities via other OpenSIPS modules. 1.2. How it works To create the dialog associated to an initial request, you must call the create_dialog() function, with or without parameter.. The dialog is automatically destroyed when a “BYE†is received. In case of no “BYEâ€, the dialog lifetime is controlled via the default timeout (see “default_timeout†- Section 1.6.5, “default_timeout (integer)â€) and custom timeout (see “$DLG_timeout†- Section 1.10.8, “$DLG_timeoutâ€). 1.3. Dialog profiling Dialog profiling is a mechanism that helps in classifying, sorting and keeping trace of certain types of dialogs, using whatever properties of the dialog (like caller, destination, type of calls, etc). Dialogs can be dynamically added in different (and several) profile tables - logically, each profile table can have a special meaning (like dialogs outside the domain, dialogs terminated to PSTN, etc). There are two types of profiles: * with no value - a dialog simply belongs to a profile. (like outbound calls profile). There is no other additional information to describe the dialog's belonging to the profile; * with value - a dialog belongs to a profile having a certain value (like in caller profile, where the value is the caller ID). The belonging of the dialog to the profile is strictly related to the value. A dialog can be added to multiple profiles in the same time. Profiles are visible (at the moment) in the request route (for initial and sequential requests) and in the branch, failure and reply routes of the original request. Dialog profiles can also be used in distributed systems, using the OpenSIPS CacheDB Interface. This feature allows you to share dialog profile information with multiple OpenSIPS instaces that use the same CacheDB backend. In order to do that, the cachedb_url parameter must be defined. Also, the profile must be marked as shared, by adding the '/s' suffix to the name of the profile in the profiles_with_value or profiles_no_value parameters. 1.4. Dialog replication Dialog replication is a mechanism used to mirror all dialog changes taking place in one OpenSIPS instance to one or more other different instances. The logic is simplified by using the core Binary Internal Interface to build and send the replication-related UDP packets. The feature is especially useful when dealing with very large systems, where failover through a database backend is no longer feasible, due to the high amount of time required for the backup instance to load the dialog information stored in a typical dialog table by the crashed instance. Configuring both receival and sending of dialog replication packets is trivial and can be done by using the accept_replicated_dialogs and replicate_dialogs_to parameters of the module. In addition to this, the module also exports several statistics regarding the number of replication packets sent/received. Profiles replication can also be achieved using the accept_replicated_profiles and replicate_profiles_to parameters. 1.5. Dependencies 1.5.1. OpenSIPS Modules The following modules must be loaded before this module: * TM - Transaction module * RR - Record-Route module 1.5.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.6. Exported Parameters 1.6.1. enable_stats (integer) If the statistics support should be enabled or not. Via statistic variables, the module provide information about the dialog processing. Set it to zero to disable or to non-zero to enable it. Default value is “1 (enabled)â€. Example 1.1. Set enable_stats parameter ... modparam("dialog", "enable_stats", 0) ... 1.6.2. hash_size (integer) The size of the hash table internally used to keep the dialogs. A larger table is much faster but consumes more memory. The hash size must be a power of 2 number. IMPORTANT: If dialogs' information should be stored in a database, a constant hash_size should be used, otherwise the restored process will not take place. If you really want to modify the hash_size you must delete all table's rows before restarting OpenSIPS. Default value is “4096â€. Example 1.2. Set hash_size parameter ... modparam("dialog", "hash_size", 1024) ... 1.6.3. log_profile_hash_size (integer) The size of the hash table internally used to store profile->dialog associations. A larger table can provide more parallel operations but consumes more memory. The hash size is provided as the base 2 logarithm(e.g. log_profile_hash_size =4 means the table has 2^4 entries). Default value is “4â€. Example 1.3. Set hash_size parameter ... modparam("dialog", "log_profile_hash_size", 5) #set a table size of 32 ... 1.6.4. rr_param (string) Name of the Record-Route parameter to be added with the dialog cookie. It is used for fast dialog matching of the sequential requests. Default value is “didâ€. Example 1.4. Set rr_param parameter ... modparam("dialog", "rr_param", "xyz") ... 1.6.5. default_timeout (integer) The default dialog timeout (in seconds) if no custom one is set. Default value is “43200 (12 hours)â€. Example 1.5. Set default_timeout parameter ... modparam("dialog", "default_timeout", 21600) ... 1.6.6. dlg_extra_hdrs (string) A string containing the extra headers (full format, with EOH) to be added in the requests generated by the module (like BYEs). Default value is “NULLâ€. Example 1.6. Set dlf_extra_hdrs parameter ... modparam("dialog", "dlg_extra_hdrs", "Hint: credit expired\r\n") ... 1.6.7. dlg_match_mode (integer) How the seqential requests should be matched against the known dialogs. The modes are a combination between matching based on a cookie (DID) stored as cookie in Record-Route header and the matching based on SIP elements (as in RFC3261). The supported modes are: * 0 - DID_ONLY - the match is done exclusively based on DID; * 1 - DID_FALLBACK - the match is first tried based on DID and if not present, it will fallback to SIP matching; * 2 - DID_NONE - the match is done exclusively based on SIP elements; no DID information is added in RR. Default value is “0 (DID_ONLY)â€. Example 1.7. Set dlg_match_mode parameter ... modparam("dialog", "dlg_match_mode", 1) ... 1.6.8. db_url (string) If you want to store the information about the dialogs in a database a database url must be specified. Default value is “mysql://opensips:opensipsrw@localhost/opensipsâ€. Example 1.8. Set db_url parameter ... modparam("dialog", "db_url", "dbdriver://username:password@dbhost/dbname ") ... 1.6.9. db_mode (integer) Describe how to push into the DB the dialogs' information from memory. The supported modes are: * 0 - NO_DB - the memory content is not flushed into DB; * 1 - REALTIME - any dialog information changes will be reflected into the database immediately. * 2 - DELAYED - the dialog information changes will be flushed into the DB periodically, based on a timer routine. * 3 - SHUTDOWN - the dialog information will be flushed into DB only at shutdown - no runtime updates. Default value is “0â€. Example 1.9. Set db_mode parameter ... modparam("dialog", "db_mode", 1) ... 1.6.10. db_update_period (integer) The interval (seconds) at which to update dialogs' information if you chose to store the dialogs' info at a given interval. A too short interval will generate intensive database operations, a too large one will not notice short dialogs. Default value is “60â€. Example 1.10. Set db_update_period parameter ... modparam("dialog", "db_update_period", 120) ... 1.6.11. options_ping_interval (integer) The interval (seconds) at which OpenSIPS will generate in-dialog OPTIONS pings for dialogs. Default value is “30â€. Example 1.11. Set options_ping_interval parameter ... modparam("dialog", "options_ping_interval", 20) ... 1.6.12. reinvite_ping_interval (integer) The interval (seconds) at which OpenSIPS will generate in-dialog Re-INVITE pings for dialogs. Default value is “300â€. Example 1.12. Set reinvite_ping_interval parameter ... modparam("dialog", "reinvite_ping_interval", 600) ... 1.6.13. table_name (string) If you want to store the information about the dialogs in a database a table name must be specified. Default value is “dialogâ€. Example 1.13. Set table_name parameter ... modparam("dialog", "table_name", "my_dialog") ... 1.6.14. call_id_column (string) The column's name in the database to store the dialogs' callid. Default value is “callidâ€. Example 1.14. Set call_id_column parameter ... modparam("dialog", "call_id_column", "callid_c_name") ... 1.6.15. from_uri_column (string) The column's name in the database to store the caller's sip address. Default value is “from_uriâ€. Example 1.15. Set from_uri_column parameter ... modparam("dialog", "from_uri_column", "from_uri_c_name") ... 1.6.16. from_tag_column (string) The column's name in the database to store the From tag from the Invite request. Default value is “from_tagâ€. Example 1.16. Set from_tag_column parameter ... modparam("dialog", "from_tag_column", "from_tag_c_name") ... 1.6.17. to_uri_column (string) The column's name in the database to store the calee's sip address. Default value is “to_uriâ€. Example 1.17. Set to_uri_column parameter ... modparam("dialog", "to_uri_column", "to_uri_c_name") ... 1.6.18. to_tag_column (string) The column's name in the database to store the To tag from the 200 OK response to the Invite request, if present. Default value is “to_tagâ€. Example 1.18. Set to_tag_column parameter ... modparam("dialog", "to_tag_column", "to_tag_c_name") ... 1.6.19. from_cseq_column (string) The column's name in the database to store the cseq from caller side. Default value is “caller_cseqâ€. Example 1.19. Set from_cseq_column parameter ... modparam("dialog", "from_cseq_column", "from_cseq_c_name") ... 1.6.20. to_cseq_column (string) The column's name in the database to store the cseq from callee side. Default value is “callee_cseqâ€. Example 1.20. Set to_cseq_column parameter ... modparam("dialog", "to_cseq_column", "to_cseq_c_name") ... 1.6.21. from_route_column (string) The column's name in the database to store the route records from caller side (proxy to caller). Default value is “caller_route_setâ€. Example 1.21. Set from_route_column parameter ... modparam("dialog", "from_route_column", "from_route_c_name") ... 1.6.22. to_route_column (string) The column's name in the database to store the route records from callee side (proxy to callee). Default value is “callee_route_setâ€. Example 1.22. Set to_route_column parameter ... modparam("dialog", "to_route_column", "to_route_c_name") ... 1.6.23. from_contact_column (string) The column's name in the database to store the caller's contact uri. Default value is “caller_contactâ€. Example 1.23. Set from_contact_column parameter ... modparam("dialog", "from_contact_column", "from_contact_c_name") ... 1.6.24. to_contact_column (string) The column's name in the database to store the callee's contact uri. Default value is “callee_contactâ€. Example 1.24. Set to_contact_column parameter ... modparam("dialog", "to_contact_column", "to_contact_c_name") ... 1.6.25. from_sock_column (string) The column's name in the database to store the information about the local interface receiving the traffic from caller. Default value is “caller_sockâ€. Example 1.25. Set from_sock_column parameter ... modparam("dialog", "from_sock_column", "from_sock_c_name") ... 1.6.26. to_sock_column (string) The column's name in the database to store information about the local interface receiving the traffic from callee. Default value is “callee_sockâ€. Example 1.26. Set to_sock_column parameter ... modparam("dialog", "to_sock_column", "to_sock_c_name") ... 1.6.27. dlg_id_column (string) The column's name in the database to store the dialogs' id information. Default value is “hash_idâ€. Example 1.27. Set dlg_id_column parameter ... modparam("dialog", "dlg_id_column", "dlg_id_c_name") ... 1.6.28. state_column (string) The column's name in the database to store the dialogs' state information. Default value is “stateâ€. Example 1.28. Set state_column parameter ... modparam("dialog", "state_column", "state_c_name") ... 1.6.29. start_time_column (string) The column's name in the database to store the dialogs' start time information. Default value is “start_timeâ€. Example 1.29. Set start_time_column parameter ... modparam("dialog", "start_time_column", "start_time_c_name") ... 1.6.30. timeout_column (string) The column's name in the database to store the dialogs' timeout. Default value is “timeoutâ€. Example 1.30. Set timeout_column parameter ... modparam("dialog", "timeout_column", "timeout_c_name") ... 1.6.31. profiles_column (string) The column's name in the database to store the dialogs' profiles. Default value is “profilesâ€. Example 1.31. Set profiles_column parameter ... modparam("dialog", "profiles_column", "profiles_c_name") ... 1.6.32. vars_column (string) The column's name in the database to store the dialogs' vars. Default value is “varsâ€. Example 1.32. Set vars_column parameter ... modparam("dialog", "vars_column", "vars_c_name") ... 1.6.33. sflags_column (string) The column's name in the database to store the dialogs' script flags. Default value is “script_flagsâ€. Example 1.33. Set sflags_column parameter ... modparam("dialog", "sflags_column", "sflags_c_name") ... 1.6.34. mflags_column (string) The column's name in the database to store the dialogs' module flags. Default value is “module_flagsâ€. Example 1.34. Set mflags_column parameter ... modparam("dialog", "mflags_column", "mflags_c_name") ... 1.6.35. flags_column (string) The column's name in the database to store the dialogs' flags. Default value is “flagsâ€. Example 1.35. Set flags_column parameter ... modparam("dialog", "flags_column", "flags_c_name") ... 1.6.36. profiles_with_value (string) List of names for profiles with values. Flag /b allows replicating dialogs over the bin interface. Before, all of them were replicated. Default value is “emptyâ€. Example 1.36. Set profiles_with_value parameter ... modparam("dialog", "profiles_with_value", "caller ; my_profile; share/s; repl/b;") ... 1.6.37. profiles_no_value (string) List of names for profiles without values. Flag /b allows replicating dialogs over the bin interface. Before, all of them were replicated. Default value is “emptyâ€. Example 1.37. Set profiles_no_value parameter ... modparam("dialog", "profiles_no_value", "inbound ; outbound ; shared/s; repl/b;") ... 1.6.38. db_flush_vals_profiles (int) Pushes dialog values, profiles and flags into the database along with other dialog state information (see db_mode 1 and 2). Default value is “emptyâ€. Example 1.38. Set db_flush_vals_profiles parameter ... modparam("dialog", "db_flush_vals_profiles", 1) ... 1.6.39. timer_bulk_del_no (int) The number of dialogs that should be attempted to be deleted at the same time ( a single query ) from the DB back-end. Default value is “1â€. Example 1.39. Set timer_bulk_del_no parameter ... modparam("dialog", "timer_bulk_del_no", 10) ... 1.6.40. cachedb_url (string) Enables distributed dialog profiles and specifies the backend that should be used by the CacheDB interface. Default value is “emptyâ€. Example 1.40. Set cachedb_url parameter ... modparam("dialog", "cachedb_url", "redis://127.0.0.1:6379") ... 1.6.41. profile_value_prefix (string) Specifies what prefix should be added to the profiles with value when they are inserted into CacheDB backed. This is only used when distributed profiles are enabled. Default value is “dlg_val_â€. Example 1.41. Set profile_value_prefix parameter ... modparam("dialog", "profile_value_prefix", "dlgv_") ... 1.6.42. profile_no_value_prefix (string) Specifies what prefix should be added to the profiles without value when they are inserted into CacheDB backed. This is only used when distributed profiles are enabled. Default value is “dlg_noval_â€. Example 1.42. Set profile_no_value_prefix parameter ... modparam("dialog", "profile_no_value_prefix", "dlgnv_") ... 1.6.43. profile_size_prefix (string) Specifies what prefix should be added to the entity that holds the profiles with value size in CacheDB backed. This is only used when distributed profiles are enabled. Default value is “dlg_size_â€. Example 1.43. Set profile_size_prefix parameter ... modparam("dialog", "profile_size_prefix", "dlgs_") ... 1.6.44. profile_timeout (int) Specifies how long a dialog profile should be kept in the CacheDB until it expires. This is only used when distributed profiles are enabled. Default value is “86400â€. Example 1.44. Set profile_timeout parameter ... modparam("dialog", "profile_timeout", "43200") ... 1.6.45. accept_replicated_dialogs (int) Registers the dialog module to the OpenSIPS Binary Internal Interface. It specifies the instances, that belong to a certain cluster, from which we should expect incoming packets. Default value is 0 (not registered). Example 1.45. Set accept_replicated_dialogs parameter ... modparam("dialog", "accept_replicated_dialogs", 1) ... 1.6.46. replicate_dialogs_to (int) Adds dialog replication destinations,that belong to the specified cluster id. The destination will receive all dialog-related events (creation, updating and deletion) over TCP, using the Binary Internal Interface. Default value is “0†(no replication destinations). Example 1.46. Set replicate_dialogs_to parameter ... modparam("dialog", "replicate_dialogs_to", 1) ... 1.6.47. accept_replicated_profiles (int) Registers the dialog module to the OpenSIPS Binary Internal Interface for profiles replication. It specifies the instances, that belong to a certain cluster, from which we should expect incoming packets. Default value is 0 (not registered). Example 1.47. Set accept_replicated_profiles parameter ... modparam("dialog", "accept_replicated_profiles", 1) ... 1.6.48. replicate_profiles_to (int) Adds profiles replication destinations, that belong to the specified cluster id. The destination will receive all dialog-related events (creation, updating and deletion) over TCP, using the Binary Internal Interface. Default value is “0†(no replication destinations). Example 1.48. Set replicate_profiles_to parameter ... modparam("dialog", "replicate_profiles_to", 1) ... 1.6.49. accept_replicated_profile_timeout (int) The time between two succesive incoming packets. Default value is “10â€. Example 1.49. Set accept_replicated_profile_timeout parameter ... modparam("dialog", "accept_replicated_profile_timeout", 30) ... 1.6.50. auth_check (int) Authentication check for incoming packets. Default value is “0†(disabled). Example 1.50. Set auth_check parameter ... modparam("dialog", "auth_check", 1) ... 1.6.51. replicate_profiles_buffer (string) Used to specify the length of the buffer used by the binary replication, in bytes. Usually this should be big enough to hold as much data as possible, but small enough to avoid UDP fragmentation. The recommended value is the smallest MTU between all the replication instances. Default value is 1400 bytes. Example 1.51. Set replicate_profiles_buffer parameter ... modparam("dialog", "replicate_profiles_buffer", 500) ... 1.6.52. replicate_profiles_check (string) Timer in seconds, used to specify how often the module should check whether old, replicated profiles values are obsolete and should be removed. should replicate its profiles to the other instances. Default value is 10 s. Example 1.52. Set replicate_profiles_check parameter ... modparam("dialog", "replicate_profiles_check", 100) ... 1.6.53. replicate_profiles_timer (string) Timer in milliseconds, used to specify how often the module should replicate its profiles to the other instances. Default value is 10 ms. Example 1.53. Set replicate_profiles_timer parameter ... modparam("dialog", "replicate_profiles_timer", 100) ... 1.6.54. replicate_profiles_expire (string) Timer in seconds, used to specify when the profiles counters received from a different instance should no longer be taken into account. This is used to prevent obsolete values, in case an instance stops replicating its counters. Default value is 10 s. Example 1.54. Set replicate_profiles_expire parameter ... modparam("dialog", "replicate_profiles_expire", 10) ... 1.7. Exported Functions 1.7.1. create_dialog() The function creats the dialog for the currently processed request. The request must be an initial request. Optionally,the function also receives a string parameter, which specifies whether the dialog end-points should be pinged via SIP options messages. The parameter can be "P" to specify to only ping the caller, "p" to only ping the callee or "Pp" to ping both dialog sides. If the extra string parameter is provided and one end-point fails to respond to a options ping, OpenSIPS will terminate the dialog from the middle. Also, the end-points can be pinged via in-dialog Re-INVITE SIP messages. This behaviour is controlled via the "R" flags for pinging the caller side, and the "r" flag for re-invite pinging the callee side. If one end-points fails to re-negociate the session via the Re-INVITE pings, OpenSIPS will terminate the dialog from the middle The string parameter can also contain "B" to activate the bye on timeout behavior. The function returns true if the dialog was successfully created or if the dialog was previously created. This function can be used from REQUEST_ROUTE. Example 1.55. create_dialog() usage ... create_dialog(); ... #ping caller create_dialog("P"); ... #ping caller and callee create_dialog("Pp"); #bye on timeout create_dialog("B"); ... 1.7.2. match_dialog() This function is to be used to match a sequential (in-dialog) request to an existing dialog. In other words, the function will try to match the current request to an known ongoing dialog. The function tries to use (for dialog matching) the DID (Dialog ID) from Route header and also falls back to SIP-wise matching. As sequential requests are automatically matched to the dialog when doing "loose_route()" from script, this function is intended to: (A) control the place in your script where the dialog matching is done and (B) to cope with bogus sequential requests that do not have Route headers, so they are not handled by loose_route(). The function returns true if a dialog exists for the request. This function can be used from REQUEST_ROUTE. Example 1.56. match_dialog() usage ... if (has_totag()) { loose_route(); if ($DLG_status==NULL && !match_dialog() ) { xlog(" cannot match request to a dialog \n"); } } ... 1.7.3. validate_dialog() The function checks the current received requests against the dialog (internal data) it belongs to. Performing several tests, the function will help to detect the bogus injected in-dialog requests (like malicious BYEs). The performed tests are related to CSEQ sequence checking and routing information checking (contact and route set). The function returns true if a dialog exists for the request and if the request is valid (according to dialog data). If the request is invalid, the following return codes are returned : * -1 - invalid cseq * -2 - invalid remote target * -3 - invalid route set * -4 - other errors ( parsing, no dlg, etc ) This function can be used from REQUEST_ROUTE. Example 1.57. validate_dialog() usage ... if (has_totag()) { loose_route(); if ($DLG_status!=NULL && !validate_dialog() ) { xlog(" in-dialog bogus request \n"); } else { xlog(" in-dialog valid request - $DLG_dir !\n"); } } ... 1.7.4. fix_route_dialog() The function forces an in dialog SIP message to contain the ruri, route headers and dst_uri, as specified by the internal data of the dialog it belongs to. The function will prevent the existence of bogus injected in-dialog requests ( like malicious BYEs ) This function can be used from REQUEST_ROUTE. Example 1.58. fix_route_dialog() usage ... if (has_totag()) { loose_route(); if ($DLG_status!=NULL) if (!validate_dialog()) fix_route_dialog(); } ... 1.7.5. get_dialog_info(attr,var,key,key_val) The function extracts a dialog value from another dialog. It first searches through all existing (ongoing) dialogs for a dialog that has a dialog variable named "key" with the value "key_val" (so a dialog where $dlg_val(key)=="key_val"). If found, it returns the value of the dialog variable "attr" from the found dialog in the "var" pseudo-variable, otherwise nothing is written in "var", and a negative error code is returned. NOTE: the function does not require to be called in the context of a dialog - you can use it whenever / whereever for searching for other dialogs. Meaning of the parameters is as follows: * attr - the name of the dialog variable (from the found dialog) to be returned; * var - a pvar where to store the value of the "attr" dialog variable * key - name of a dialog variable to be used a search key (when looking after the target dialog) * key_val - the value of the dialog variable that is used as key in searching the target dialog. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE. Example 1.59. get_dialog_info usage ... if ( get_dialog_info("callee","$var(x)","caller","$fu") ) { xlog("caller $fU has another ongoing, talking to callee $var(x)\ n") } # create dialog for current call and place the caller and callee attribu tes create_dialog(); $dlg_val(caller) = $fu; $dlg_val(callee) = $ru; ... 1.7.6. set_dlg_profile(profile,[value]) Inserts the current dialog into a profile. Note that if the profile does not support values, this will be silently discarded. A dialog may be inserted in the same profile multiple times. NOTE: the dialog must be created before using this function (use create_dialog() function before). Meaning of the parameters is as follows: * profile - name of the profile to be added to; * value (optional) - string value to define the belonging of the dialog to the profile - note that the profile must support values. Pseudo-variables are supported. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.60. set_dlg_profile usage ... set_dlg_profile("inbound_call"); set_dlg_profile("caller","$fu"); ... 1.7.7. unset_dlg_profile(profile,[value]) Removes the current dialog from a profile. NOTE: the dialog must be created before using this function (use create_dialog() function before). Meaning of the parameters is as follows: * profile - name of the profile to be removed from; * value (optional) - string value to define the belonging of the dialog to the profile - note that the profile must support values. Pseudo-variables are supported. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.61. unset_dlg_profile usage ... unset_dlg_profile("inbound_call"); unset_dlg_profile("caller","$fu"); ... 1.7.8. is_in_profile(profile,[value]) Checks if the current dialog belongs to a profile. If the profile supports values, the check can be reinforced to take into account a specific value - if the dialog was inserted into the profile for a specific value. If no value is passed, only simply belonging of the dialog to the profile is checked. Note that if the profile does not support values, this will be silently discarded. NOTE: the dialog must be created before using this function (use create_dialog() function before). Meaning of the parameters is as follows: * profile - name of the profile to be checked against; * value (optional) - string value to toughen the check. Pseudo-variables are supported. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.62. is_in_profile usage ... if (is_in_profile("inbound_call")) { log("this request belongs to a inbound call\n"); } ... if (is_in_profile("caller","XX")) { log("this request belongs to a call of user XX\n"); } ... 1.7.9. get_profile_size(profile,[value],size) Returns the number of dialogs belonging to a profile. If the profile supports values, the check can be reinforced to take into account a specific value - how many dialogs were inserted into the profile with a specific value. If not value is passed, only simply belonging of the dialog to the profile is checked. Note that the profile does not supports values, this will be silently discarded. Meaning of the parameters is as follows: * profile - name of the profile to get the size for; * value (optional) - string value to toughen the check. Pseudo-variables are supported; * size - an AVP or script variable to return the profile size in. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.63. get_profile_size usage ... get_profile_size("inbound_call","$avp(size)"); xlog("currently there are $avp(size) inbound calls\n"); ... get_profile_size("caller","$fu"); xlog("currently, the user %fu has $avp(size) active outgoing calls\n"); ... 1.7.10. set_dlg_flag(idx) Sets the dialog flag index idx to true. The dialog flags are dialog persistent and they can be accessed (set and test) for all requests belonging to the dialog. The flag index can be between 0 and 31. NOTE: the dialog must be created before using this function (use create_dialog() function before). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.64. set_dlg_flag usage ... set_dlg_flag("3"); ... 1.7.11. test_and_set_dlg_flag(idx, value) Atomically checks if the dialog flag index idx is equal to value. If true, changes the value with the opposite one. This operation is done under the dialog lock. The flag index can be between 0 and 31. The value should be 0 (false) or 1 (true). NOTE: the dialog must be created before using this function (use create_dialog() function before). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.65. test_and_set_dlg_flag usage ... test_and_set_dlg_flag("3", "0"); ... 1.7.12. reset_dlg_flag(idx) Resets the dialog flag index idx to false. The dialog flags are dialog persistent and they can be accessed (set and test) for all requests belonging to the dialog. The flag index can be between 0 and 31. NOTE: the dialog must be created before using this function (use create_dialog() function before). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.66. reset_dlg_flag usage ... reset_dlg_flag("16"); ... 1.7.13. is_dlg_flag_set(idx) Returns true if the dialog flag index idx is set. The dialog flags are dialog persistent and they can be accessed (set and test) for all requests belonging to the dialog. The flag index can be between 0 and 31. NOTE: the dialog must be created before using this function (use create_dialog() function before). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.67. is_dlg_flag_set usage ... if (is_dlg_flag_set("16")) { xlog("dialog flag 16 is set\n"); } ... 1.7.14. store_dlg_value(name,val) Attaches to the dialog the value val under the name name. The values attached to dialogs are dialog persistent and they can be accessed (read and write) for all requests belonging to the dialog. Parameter val may contain pseudo-variables. NOTE: the dialog must be created before using this function (use create_dialog() function before). Same functionality may be obtain by assigning a value to pseudo variable $dlg_val(name). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.68. store_dlg_value usage ... store_dlg_value("inv_src_ip","$si"); store_dlg_value("account type","prepaid"); # or $dlg_val(account_type) = "prepaid"; ... 1.7.15. fetch_dlg_value(name,pvar) Fetches from the dialog the value of attribute named name. The values attached to dialogs are dialog persistent and they can be accessed (read and write) for all requests belonging to the dialog. Parameter pvar may be a script var ($var) or and avp ($avp). NOTE: the dialog must be created before using this function (use create_dialog() function before). Same functionality may be obtain by reading the pseudo variable $dlg_val(name). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. Example 1.69. fetch_dlg_value usage ... fetch_dlg_value("inv_src_ip","$avp(2)"); fetch_dlg_value("account type","$var(account)"); # or $var(account) = $dlg_val(account_type); ... 1.8. Exported statistics 1.8.1. active_dialogs Returns the number of current active dialogs (may be confirmed or not). 1.8.2. early_dialogs Returns the number of early dialogs. 1.8.3. processed_dialogs Returns the total number of processed dialogs (terminated, expired or active) from the startup. 1.8.4. expired_dialogs Returns the total number of expired dialogs from the startup. 1.8.5. failed_dialogs Returns the number of failed dialogs ( dialogs were never established due to whatever reasons - internal error, negative reply, cancelled, etc ) 1.8.6. create_sent Returns the number of replicated dialog create requests send to other OpenSIPS instances. 1.8.7. update_sent Returns the number of replicated dialog update requests send to other OpenSIPS instances. 1.8.8. delete_sent Returns the number of replicated dialog delete requests send to other OpenSIPS instances. 1.8.9. create_recv Returns the number of dialog create events received from other OpenSIPS instances. 1.8.10. update_recv Returns the number of dialog update events received from other OpenSIPS instances. 1.8.11. delete_recv Returns the number of dialog delete events received from other OpenSIPS instances. 1.9. Exported MI Functions 1.9.1. dlg_list Lists the description of the dialogs (calls). If no parameter is given, all dialogs will be listed. If a dialog identifier is passed as parameter (callid and fromtag), only that dialog will be listed. If a index and conter parameter is passed, it will list only a number of "counter" dialogs starting with index (as offset) - this is used to get only section of dialogs. Name: dlg_list Parameters (with dialog idetification): * callid (optional) - callid if a single dialog to be listed. * from_tag (optional, but cannot be present without the callid parameter) - fromtag (as per initial request) of the dialog to be listed. entry Parameters (with dialog counting): * index - offset where the dialog listing should start. * counter - how many dialogs should be listed (starting from the offset) MI FIFO Command Format: :dlg_list:_reply_fifo_file_ _empty_line_ :dlg_list:_reply_fifo_file_ abcdrssfrs122444@192.168.1.1 AAdfeEFF33 :dlg_list:_reply_fifo_file_ 40 10 1.9.2. dlg_list_ctx The same as the “dlg_list†but including in the dialog description the associated context from modules sitting on top of the dialog module. This function also prints the dialog's values. In case of binary values, the non-printable chars are represented in hex (e.g. \x00) Name: dlg_list_ctx Parameters: see “dlg_list†MI FIFO Command Format: :dlg_list_ctx:_reply_fifo_file_ _empty_line_ 1.9.3. dlg_end_dlg Terminates an ongoing dialog. If dialog is established, BYEs are sent in both directions. If dialog is in unconfirmed or early state, a CANCEL will be sent to the callee side, that will trigger a 487 from the callee, which, when relayed, will also end the dialog on the caller's side. Name: dlg_end_dlg The accepted formats (as parameters) may be: * dlg_end_dlg h_entry h_id [extra_hdrs] * dlg_end_dlg dialog_id [extra_hdrs] Where: * h_entry - numerical hash entry of the dialog in the internal dialog table (provided by dlg_list) * h_id - numerical hash id of the dialog on the hash entry (provided by dlg_list) * dialog_id - numerical unique ID of the dialog (as provided by dlg_list) * extra_hdrs - (optional) string containg the extra headers (full format) to be added to the BYE requests. The values for the h_entry and h_id or dialog_id can be get via the "dlg_list" MI command. MI FIFO Command Format: :dlg_end_dlg:_reply_fifo_file_ 342 56 _empty_line_ 1.9.4. profile_get_size Returns the number of dialogs belonging to a profile. If the profile supports values, the check can be reinforced to take into account a specific value - how many dialogs were inserted into the profile with a specific value. If not value is passed, only simply belonging of the dialog to the profile is checked. Note that the profile does not supports values, this will be silently discarded. Name: profile_get_size Parameters: * profile - name of the profile to get the value for. * value (optional)- string value to toughen the check; MI FIFO Command Format: :profile_get_size:_reply_fifo_file_ inbound_calls _empty_line_ 1.9.5. profile_list_dlgs Lists all the dialogs belonging to a profile. If the profile supports values, the check can be reinforced to take into account a specific value - list only the dialogs that were inserted into the profile with that specific value. If not value is passed, all dialogs belonging to the profile will be listed. Note that the profile does not supports values, this will be silently discarded. Also, when using shared profiles using the CacheDB interface, this command will only display the local dialogs. Name: profile_list_dlgs Parameters: * profile - name of the profile to list the dialog for. * value (optional)- string value to toughen the check; MI FIFO Command Format: :profile_list_dlgs:_reply_fifo_file_ inbound_calls _empty_line_ 1.9.6. profile_get_values Lists all the values belonging to a profile along with their count. If the profile does not support values a total count will be returned. Note that this function does not work for shared profiles over the CacheDB interface. Name: profile_get_values Parameters: * profile - name of the profile to list the dialog for. MI FIFO Command Format: :profile_get_values:_reply_fifo_file_ inbound_calls _empty_line_ 1.9.7. profile_end_dlgs Terminate all ongoing dialogs from a specified profile, on a single dialog it performs the same operations as the command dlg_end_dlg Name: profile_end_dlgs Parameters: * profile - name of the profile that will have its dialogs termianted * value - (optional) if the profile supports values terminate only the dialogs with the specified value MI FIFO Command Format: :profile_end_dlgs:_reply_fifo_file_ inbound_calls _empty_line_ 1.9.8. dlg_db_sync Will synchronize the information about the dialogs from the database with the OpenSIPS internal memory. To be used mainly for transferring OpenSIPS dialog information from one server to another. Name: dlg_db_sync It takes no parameters MI FIFO Command Format: :dlg_db_sync:_reply_fifo_file_ _empty_line_ 1.9.9. dlg_restore_db Restores the dialog table after a potential desynchronization event. The table is truncated, then populated with CONFIRMED dialogs from memory. Name: dlg_restore_db It takes no parameters MI FIFO Command Format: :dlg_restore_db:_reply_fifo_file_ _empty_line_ 1.9.10. list_all_profiles Lists all the dialog profiles, along with 1 or 0 if the given profile has/does not have an associated value. Name: list_all_profiles Parameters: It takes no parameters MI FIFO Command Format: :list_all_profiles:_reply_fifo_file_ _empty_line_ 1.10. Exported pseudo-variables 1.10.1. $DLG_count Returns the number of current active dialogs (may be confirmed or not). 1.10.2. $DLG_status Returns the status of the dialog corresponding to the processed sequential request. This PV will be available only for sequential requests, after doing loose_route(). Value may be: * NULL - Dialog not found. * 1 - Dialog unconfirmed (created but no reply received at all) * 2 - Dialog in early state (created provisional reply received, but no final reply received yet) * 3 - Confirmed by a final reply but no ACK received yet. * 4 - Confirmed by a final reply and ACK received. * 5 - Dialog ended. 1.10.3. $DLG_lifetime Returns the duration (in seconds) of the dialog corresponding to the processed sequential request. The duration is calculated from the dialog confirmation and the current moment. This PV will be available only for sequential requests, after doing loose_route(). NULL will be returned if there is no dialog for the request. 1.10.4. $DLG_flags Returns the dialog flags array (as a single integer value) of the dialog corresponding to the processed sequential request. This PV will be available only for sequential requests, after doing loose_route(). NULL will be returned if there is no dialog for the request. 1.10.5. $DLG_dir Returns the direction of the request in dialog (as "UPSTREAM" or "DOWNSTREAM" string) - to be used for sequential request. This PV will be available only for sequential requests (on replies), after doing loose_route(). NULL will be returned if there is no dialog for the request. 1.10.6. $DLG_did Returns the id of the dialog corresponding to the processed sequential request. The output format is entry ':' id (as returned by the dlg_list MI function). This PV will be available only for sequential requests, after doing loose_route(). NULL will be returned if there is no dialog for the request. 1.10.7. $DLG_end_reason Returns the reason for the dialog termination. It can be one of the following : * Upstream BYE - Callee has sent a BYE * Downstream BYE - Caller has sent a BYE * Lifetime Timeout - Dialog lifetime expired * MI Termination - Dialog ended via the MI interface * Ping Timeout - Dialog ended because no reply to in-dialog pings * RTPProxy Timeout - Media timeout signaled by RTPProxy NULL will be returned if there is no dialog for the request, or if the dialog is not ended in the current context. 1.10.8. $DLG_timeout Used to set the dialog lifetime (in seconds). When read, the variable returns the number of seconds until the dialog expires and is destroyed. Note that reading the variable is only possible after the dialog is created (for initial requests) or after doing loose_route() (for sequential requests). Important notice: using this variable with a REALTIME db_mode is very inefficient, because every time the dialog value is changed, a database update is done. NULL will be returned if there is no dialog for the request, otherwise the number of seconds until the dialog expiration. 1.10.9. $dlg_val(name) This is a read/write variable that allows access to the dialog attribute named name. This PV will be available only for sequential requests, after doing loose_route(). NULL will be returned if there is no dialog for the request. 1.11. Exported Events 1.11.1. E_DLG_STATE_CHANGED This event is raised when the dialog state is changed. Parameters: * ostate - the old state of the dialog. * nstate - the new state of the dialog. Chapter 2. Developer Guide 2.1. Available Functions 2.1.1. register_dlgcb (dialog, type, cb, param, free_param_cb) Register a new callback to the dialog. Meaning of the parameters is as follows: * struct dlg_cell* dlg - dialog to register callback to. If maybe NULL only for DLG_CREATED callback type, which is not a per dialog type. * int type - types of callbacks; more types may be register for the same callback function; only DLG_CREATED must be register alone. Possible types: + DLGCB_LOADED + DLGCB_SAVED + DLG_CREATED - called when a new dialog is created - it's a global type (not associated to any dialog) + DLG_FAILED - called when the dialog was negatively replied (non-2xx) - it's a per dialog type. + DLG_CONFIRMED - called when the dialog is confirmed (2xx replied) - it's a per dialog type. + DLG_REQ_WITHIN - called when the dialog matches a sequential request - it's a per dialog type. + DLG_TERMINATED - called when the dialog is terminated via BYE - it's a per dialog type. + DLG_EXPIRED - called when the dialog expires without receiving a BYE - it's a per dialog type. + DLGCB_EARLY - called when the dialog is created in an early state (18x replied) - it's a per dialog type. + DLGCB_RESPONSE_FWDED - called when the dialog matches a reply to the initial INVITE request - it's a per dialog type. + DLGCB_RESPONSE_WITHIN - called when the dialog matches a reply to a subsequent in dialog request - it's a per dialog type. + DLGCB_MI_CONTEXT - called when the mi dlg_list_ctx command is invoked - it's a per dialog type. + DLGCB_DESTROY * dialog_cb cb - callback function to be called. Prototype is: “void (dialog_cb) (struct dlg_cell* dlg, int type, struct dlg_cb_params * params); †* void *param - parameter to be passed to the callback function. * param_free callback_param_free - callback function to be called to free the param. Prototype is: “void (param_free_cb) (void *param);†Chapter 3. Frequently Asked Questions 3.1. What happened with “topology_hiding()†function? The respective functionality was moved into the topology_hiding module. Function prototype has remained the same. 3.2. What happened with “use_tight_match†parameter? The parameter was removed with version 1.3 as the option of tight matching became mandatory and not configurable. Now, the tight matching is done all the time (when using DID matching). 3.3. What happened with “bye_on_timeout_flag†parameter? The parameter was removed in a dialog module parameter restructuring. To keep the bye on timeout behavior, you need to provide a "B" string parameter to the create_dialog() function. 3.4. What happened with “dlg_flag†parameter? The parameter is considered obsolete. The only way to create a dialog is to call the create_dialog() function 3.5. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 3.6. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 3.7. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/dialog/dialog.c000066400000000000000000001346161300170765700177100ustar00rootroot00000000000000/* * dialog module - basic support for dialog tracking * * Copyright (C) 2008-2014 OpenSIPS Solutions * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-14 initial version (bogdan) * 2006-11-28 Added statistic support for the number of early and failed * dialogs. (Jeffrey Magder - SOMA Networks) * 2007-04-30 added dialog matching without DID (dialog ID), but based only * on RFC3261 elements - based on an original patch submitted * by Michel Bensoussan (bogdan) * 2007-05-15 added saving dialogs' information to database (ancuta) * 2007-07-04 added saving dialog cseq, contact, record route * and bind_addresses(sock_info) for caller and callee (ancuta) * 2008-04-14 added new type of callback to be triggered when dialogs are * loaded from DB (bogdan) */ #include #include #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../pvar.h" #include "../../mod_fix.h" #include "../../context.h" #include "../../script_cb.h" #include "../../script_var.h" #include "../../mem/mem.h" #include "../../mi/mi.h" #include "../tm/tm_load.h" #include "../rr/api.h" #include "../../bin_interface.h" #include "../clusterer/api.h" #include "dlg_hash.h" #include "dlg_timer.h" #include "dlg_handlers.h" #include "dlg_load.h" #include "dlg_cb.h" #include "dlg_db_handler.h" #include "dlg_req_within.h" #include "dlg_profile.h" #include "dlg_vals.h" #include "dlg_replication.h" #include "dlg_repl_profile.h" static int mod_init(void); static int child_init(int rank); static void mod_destroy(void); /* module parameter */ int log_profile_hash_size = 4; str rr_param = {"did",3}; static int dlg_hash_size = 4096; static str timeout_spec = {NULL, 0}; static int default_timeout = 60 * 60 * 12; /* 12 hours */ static int options_ping_interval = 30; /* seconds */ static int reinvite_ping_interval = 300; /* seconds */ static char* profiles_wv_s = NULL; static char* profiles_nv_s = NULL; int dlg_bulk_del_no = 1; /* delete one by one */ int seq_match_mode = SEQ_MATCH_STRICT_ID; str dlg_extra_hdrs = {NULL,0}; /* statistic variables */ int dlg_enable_stats = 1; int active_dlgs_cnt = 0; int early_dlgs_cnt = 0; int db_flush_vp = 0; stat_var *active_dlgs = 0; stat_var *processed_dlgs = 0; stat_var *expired_dlgs = 0; stat_var *failed_dlgs = 0; stat_var *early_dlgs = 0; stat_var *create_sent = 0; stat_var *update_sent = 0; stat_var *delete_sent = 0; stat_var *create_recv = 0; stat_var *update_recv = 0; stat_var *delete_recv = 0; struct tm_binds d_tmb; struct rr_binds d_rrb; /* db stuff */ static str db_url = {NULL,0}; static unsigned int db_update_period = DB_DEFAULT_UPDATE_PERIOD; /* cachedb stuff */ str cdb_url = {0,0}; /* dialog replication using the bpi interface */ int accept_replicated_dlg=0; int dialog_replicate_cluster = 0; int profile_replicate_cluster = 0; int accept_repl_profiles=0; int accept_replicated_profile_timeout = 10; int repl_prof_auth_check = 0; static int pv_get_dlg_count( struct sip_msg *msg, pv_param_t *param, pv_value_t *res); /* commands wrappers and fixups */ static int fixup_profile(void** param, int param_no); static int fixup_get_profile2(void** param, int param_no); static int fixup_get_profile3(void** param, int param_no); static int w_create_dialog(struct sip_msg*); static int w_create_dialog2(struct sip_msg*,char *); static int w_match_dialog(struct sip_msg*); static int fixup_create_dlg2(void **param,int param_no); static int w_validate_dialog(struct sip_msg*); static int w_fix_route_dialog(struct sip_msg*); static int w_set_dlg_profile(struct sip_msg*, char*, char*); static int w_unset_dlg_profile(struct sip_msg*, char*, char*); static int w_is_in_profile(struct sip_msg*, char*, char*); static int w_get_profile_size(struct sip_msg*, char*, char*, char*); static int fixup_dlg_flag(void** param, int param_no); static int w_set_dlg_flag(struct sip_msg*, char*); static int w_reset_dlg_flag(struct sip_msg*, char*); static int w_is_dlg_flag_set(struct sip_msg*, char*); static int fixup_dlg_sval(void** param, int param_no); static int fixup_dlg_fval(void** param, int param_no); static int w_store_dlg_value(struct sip_msg*, char*, char*); static int w_fetch_dlg_value(struct sip_msg*, char*, char*); static int fixup_get_info(void** param, int param_no); static int w_get_dlg_info(struct sip_msg*, char*, char*, char*, char*); static int w_tsl_dlg_flag(struct sip_msg *msg, char *_idx, char *_val); /* item/pseudo-variables functions */ int pv_get_dlg_lifetime(struct sip_msg *msg,pv_param_t *param,pv_value_t *res); int pv_get_dlg_status(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int pv_get_dlg_flags(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int pv_get_dlg_timeout(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int pv_get_dlg_dir(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int pv_get_dlg_did(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int pv_get_dlg_end_reason(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int pv_set_dlg_flags(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); int pv_set_dlg_timeout(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); static cmd_export_t cmds[]={ {"create_dialog", (cmd_function)w_create_dialog, 0,NULL, 0, REQUEST_ROUTE}, {"create_dialog", (cmd_function)w_create_dialog2, 1,fixup_create_dlg2, 0, REQUEST_ROUTE}, {"set_dlg_profile", (cmd_function)w_set_dlg_profile, 1,fixup_profile, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, {"set_dlg_profile", (cmd_function)w_set_dlg_profile, 2,fixup_profile, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, {"unset_dlg_profile", (cmd_function)w_unset_dlg_profile,1,fixup_profile, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, {"unset_dlg_profile", (cmd_function)w_unset_dlg_profile,2,fixup_profile, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, {"is_in_profile", (cmd_function)w_is_in_profile, 1,fixup_profile, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE }, {"is_in_profile", (cmd_function)w_is_in_profile, 2,fixup_profile, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE }, {"get_profile_size",(cmd_function)w_get_profile_size, 2,fixup_get_profile2, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE }, {"get_profile_size",(cmd_function)w_get_profile_size, 3,fixup_get_profile3, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE}, {"set_dlg_flag",(cmd_function)w_set_dlg_flag, 1,fixup_dlg_flag, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE}, {"test_and_set_dlg_flag",(cmd_function)w_tsl_dlg_flag,2,fixup_uint_uint, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE }, {"reset_dlg_flag",(cmd_function)w_reset_dlg_flag, 1,fixup_dlg_flag, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE }, {"is_dlg_flag_set",(cmd_function)w_is_dlg_flag_set, 1,fixup_dlg_flag, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE }, {"store_dlg_value",(cmd_function)w_store_dlg_value, 2,fixup_dlg_sval, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE }, {"fetch_dlg_value",(cmd_function)w_fetch_dlg_value, 2,fixup_dlg_fval, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE }, {"validate_dialog",(cmd_function)w_validate_dialog, 0, NULL, 0, REQUEST_ROUTE}, {"fix_route_dialog",(cmd_function)w_fix_route_dialog,0,NULL, 0, REQUEST_ROUTE}, {"get_dialog_info",(cmd_function)w_get_dlg_info, 4,fixup_get_info, 0, REQUEST_ROUTE| FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE }, {"match_dialog", (cmd_function)w_match_dialog, 0,NULL, 0, REQUEST_ROUTE}, {"load_dlg", (cmd_function)load_dlg, 0, 0, 0, 0}, {0,0,0,0,0,0} }; static param_export_t mod_params[]={ { "enable_stats", INT_PARAM, &dlg_enable_stats }, { "hash_size", INT_PARAM, &dlg_hash_size }, { "log_profile_hash_size", INT_PARAM, &log_profile_hash_size }, { "rr_param", STR_PARAM, &rr_param.s }, { "default_timeout", INT_PARAM, &default_timeout }, { "options_ping_interval", INT_PARAM, &options_ping_interval }, { "reinvite_ping_interval",INT_PARAM, &reinvite_ping_interval }, { "dlg_extra_hdrs", STR_PARAM, &dlg_extra_hdrs.s }, { "dlg_match_mode", INT_PARAM, &seq_match_mode }, { "db_url", STR_PARAM, &db_url.s }, { "db_mode", INT_PARAM, &dlg_db_mode }, { "table_name", STR_PARAM, &dialog_table_name }, { "dlg_id_column", STR_PARAM, &dlg_id_column.s }, { "call_id_column", STR_PARAM, &call_id_column.s }, { "from_uri_column", STR_PARAM, &from_uri_column.s }, { "from_tag_column", STR_PARAM, &from_tag_column.s }, { "to_uri_column", STR_PARAM, &to_uri_column.s }, { "to_tag_column", STR_PARAM, &to_tag_column.s }, { "state_column", STR_PARAM, &state_column.s }, { "start_time_column", STR_PARAM, &start_time_column.s }, { "timeout_column", STR_PARAM, &timeout_column.s }, { "to_cseq_column", STR_PARAM, &to_cseq_column.s }, { "from_cseq_column", STR_PARAM, &from_cseq_column.s }, { "to_route_column", STR_PARAM, &to_route_column.s }, { "from_route_column", STR_PARAM, &from_route_column.s }, { "to_contact_column", STR_PARAM, &to_contact_column.s }, { "from_contact_column", STR_PARAM, &from_contact_column.s }, { "to_sock_column", STR_PARAM, &to_sock_column.s }, { "from_sock_column", STR_PARAM, &from_sock_column.s }, { "profiles_column", STR_PARAM, &profiles_column.s }, { "vars_column", STR_PARAM, &vars_column.s }, { "sflags_column", STR_PARAM, &sflags_column.s }, { "mflags_column", STR_PARAM, &mflags_column.s }, { "flags_column", STR_PARAM, &flags_column.s }, { "db_update_period", INT_PARAM, &db_update_period }, { "profiles_with_value", STR_PARAM, &profiles_wv_s }, { "profiles_no_value", STR_PARAM, &profiles_nv_s }, { "db_flush_vals_profiles",INT_PARAM, &db_flush_vp }, { "timer_bulk_del_no", INT_PARAM, &dlg_bulk_del_no }, /* distributed profiles stuff */ { "cachedb_url", STR_PARAM, &cdb_url.s }, { "profile_value_prefix", STR_PARAM, &cdb_val_prefix.s }, { "profile_no_value_prefix", STR_PARAM, &cdb_noval_prefix.s }, { "profile_size_prefix", STR_PARAM, &cdb_size_prefix.s }, { "profile_timeout", INT_PARAM, &profile_timeout }, /* dialog replication through UDP binary packets */ { "accept_replicated_dialogs",INT_PARAM, &accept_replicated_dlg }, { "replicate_dialogs_to", INT_PARAM, &dialog_replicate_cluster }, { "accept_replicated_profiles",INT_PARAM, &accept_repl_profiles }, { "replicate_profiles_timer", INT_PARAM, &repl_prof_utimer }, { "replicate_profiles_check", INT_PARAM, &repl_prof_timer_check }, { "replicate_profiles_buffer",INT_PARAM, &repl_prof_buffer_th }, { "replicate_profiles_expire",INT_PARAM, &repl_prof_timer_expire}, { "replicate_profiles_to", INT_PARAM, &profile_replicate_cluster }, { "accept_replicated_profile_timeout", INT_PARAM, &accept_replicated_profile_timeout}, { "auth_check", INT_PARAM, &repl_prof_auth_check}, { 0,0,0 } }; static stat_export_t mod_stats[] = { {"active_dialogs" , STAT_NO_RESET, &active_dlgs }, {"early_dialogs", STAT_NO_RESET, &early_dlgs }, {"processed_dialogs" , 0, &processed_dlgs }, {"expired_dialogs" , 0, &expired_dlgs }, {"failed_dialogs", 0, &failed_dlgs }, {"create_sent", 0, &create_sent }, {"update_sent", 0, &update_sent }, {"delete_sent", 0, &delete_sent }, {"create_recv", 0, &create_recv }, {"update_recv", 0, &update_recv }, {"delete_recv", 0, &delete_recv }, {0,0,0} }; static mi_export_t mi_cmds[] = { { "dlg_list", 0, mi_print_dlgs, 0, 0, 0}, { "dlg_list_ctx", 0, mi_print_dlgs_ctx, 0, 0, 0}, { "dlg_end_dlg", 0, mi_terminate_dlg, 0, 0, 0}, { "dlg_db_sync", 0, mi_sync_db_dlg, 0, 0, 0}, { "dlg_restore_db", 0, mi_restore_dlg_db, 0, 0, 0}, { "profile_get_size", 0, mi_get_profile, 0, 0, 0}, { "profile_list_dlgs", 0, mi_profile_list, 0, 0, 0}, { "profile_get_values", 0, mi_get_profile_values, 0, 0, 0}, { "list_all_profiles", 0, mi_list_all_profiles, 0, 0, 0}, { "profile_end_dlgs", 0, mi_profile_terminate, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static pv_export_t mod_items[] = { { {"DLG_count", sizeof("DLG_count")-1}, 1000, pv_get_dlg_count, 0, 0, 0, 0, 0 }, { {"DLG_lifetime",sizeof("DLG_lifetime")-1}, 1000, pv_get_dlg_lifetime, 0, 0, 0, 0, 0 }, { {"DLG_status", sizeof("DLG_status")-1}, 1000, pv_get_dlg_status, 0, 0, 0, 0, 0 }, { {"DLG_dir", sizeof("DLG_dir")-1}, 1000, pv_get_dlg_dir, 0, 0, 0, 0, 0}, { {"DLG_flags", sizeof("DLG_flags")-1}, 1000, pv_get_dlg_flags, pv_set_dlg_flags, 0, 0, 0, 0 }, { {"dlg_val", sizeof("dlg_val")-1}, 1000, pv_get_dlg_val, pv_set_dlg_val, pv_parse_name, 0, 0, 0}, { {"DLG_did", sizeof("DLG_did")-1}, 1000, pv_get_dlg_did, 0, 0, 0, 0, 0}, { {"DLG_end_reason", sizeof("DLG_end_reason")-1}, 1000, pv_get_dlg_end_reason,0,0, 0, 0, 0}, { {"DLG_timeout", sizeof("DLG_timeout")-1}, 1000, pv_get_dlg_timeout, pv_set_dlg_timeout, 0, 0, 0, 0 }, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; static module_dependency_t *get_deps_db_mode(param_export_t *param) { int db_mode = *(int *)param->param_pointer; if (db_mode == DB_MODE_NONE || (db_mode != DB_MODE_REALTIME && db_mode != DB_MODE_DELAYED && db_mode != DB_MODE_SHUTDOWN)) return NULL; return alloc_module_dep(MOD_TYPE_SQLDB, NULL, DEP_ABORT); } static module_dependency_t *get_deps_cachedb_url(param_export_t *param) { char *cdb_url = *(char **)param->param_pointer; if (!cdb_url || strlen(cdb_url) == 0) return NULL; return alloc_module_dep(MOD_TYPE_CACHEDB, NULL, DEP_ABORT); } static module_dependency_t *get_deps_clusterer(param_export_t *param) { int cluster_id = *(int *)param->param_pointer; if (cluster_id <= 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "clusterer", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_DEFAULT, "rr", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "db_mode", get_deps_db_mode }, { "cachedb_url", get_deps_cachedb_url }, { "accept_replicated_dialogs", get_deps_clusterer }, { "replicate_dialogs_to", get_deps_clusterer }, { "accept_replicated_profiles", get_deps_clusterer }, { "replicate_profiles_to", get_deps_clusterer }, { NULL, NULL }, }, }; struct module_exports exports= { "dialog", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ mod_params, /* param exports */ mod_stats, /* exported statistics */ mi_cmds, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, child_init /* per-child init function */ }; static int fixup_profile(void** param, int param_no) { struct dlg_profile_table *profile; pv_elem_t *model=NULL; str s; s.s = (char*)(*param); s.len = strlen(s.s); if(s.len==0) { LM_ERR("param %d is empty string!\n", param_no); return E_CFG; } if (param_no==1) { profile = search_dlg_profile( &s ); if (profile==NULL) { LM_CRIT("profile <%s> not defined\n",s.s); return E_CFG; } pkg_free(*param); *param = (void*)profile; return 0; } else if (param_no==2) { if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for value param!\n", s.s); return E_CFG; } *param = (void*)model; } return 0; } static int fixup_get_profile2(void** param, int param_no) { pv_spec_t *sp; int ret; action_elem_t * p; if (param_no==1) { return fixup_profile(param, 1); } else if (param_no==2) { ret = fixup_pvar(param); if (ret<0) return ret; sp = (pv_spec_t*)(*param); if (sp->type!=PVT_AVP && sp->type!=PVT_SCRIPTVAR) { LM_ERR("return must be an AVP or SCRIPT VAR!\n"); return E_SCRIPT; } p = list_entry(param, action_elem_t, u.data); p++; p->u.data = *param; *param = NULL; } return 0; } static int fixup_get_profile3(void** param, int param_no) { int ret; pv_spec_t *sp; if (param_no==1) { return fixup_profile(param, 1); } else if (param_no==2) { return fixup_profile(param, 2); } else if (param_no==3) { ret = fixup_pvar(param); if (ret<0) return ret; sp = (pv_spec_t*)(*param); if (sp->type!=PVT_AVP && sp->type!=PVT_SCRIPTVAR) { LM_ERR("return must be an AVP or SCRIPT VAR!\n"); return E_SCRIPT; } } return 0; } static int fixup_dlg_flag(void** param, int param_no) { unsigned int ui; str s; s.s = (char*)*param; s.len = strlen(s.s); if (str2int(&s, &ui)!=0) { LM_ERR("flag index must be a number <%s>\n", (char *)(*param)); return E_CFG; } if ( ui>=8*sizeof(unsigned int) ) { LM_ERR("flag index too high <%u> (max=%u)\n", ui, (unsigned int)(8*sizeof(unsigned int)-1) ); return E_CFG; } pkg_free(*param); *param=(void *)(unsigned long)(1<type!=PVT_AVP && sp->type!=PVT_SCRIPTVAR) { LM_ERR("return must be an AVP or SCRIPT VAR!\n"); return E_SCRIPT; } } return 0; } static int fixup_get_info(void** param, int param_no) { pv_elem_t *model=NULL; pv_spec_t *sp; str s; int ret; if (param_no==1) { /* name of the dlg val to be returned */ return fixup_str(param); } else if (param_no==2) { /* var to store the dlg_val value */ ret = fixup_pvar(param); if (ret<0) return ret; sp = (pv_spec_t*)(*param); if (sp->type!=PVT_AVP && sp->type!=PVT_SCRIPTVAR) { LM_ERR("return must be an AVP or SCRIPT VAR!\n"); return E_SCRIPT; } } else if (param_no==3) { /* name of the dlg val to identify the dialog */ return fixup_str(param); } else if (param_no==4) { /* var to hold the value of the indeification dlg val */ s.s = (char*)*param; s.len = strlen(s.s); if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for value param!\n", s.s); return E_CFG; } *param = (void*)model; } return 0; } static int create_dialog_wrapper(struct sip_msg *req,int flags) { struct cell *t; struct dlg_cell *dlg; /* is the dialog already created? */ if ((dlg = get_current_dialog())!=NULL) { dlg->flags |= flags; return 1; } t = d_tmb.t_gett(); if (dlg_create_dialog( (t==T_UNDEFINED)?NULL:t, req,flags)!=0) return -1; return 1; } static void set_mod_flag_wrapper (struct dlg_cell *dlg, unsigned int flags) { dlg->mod_flags |= flags; } static int is_mod_flag_set_wrapper (struct dlg_cell *dlg, unsigned int flags) { return (dlg->mod_flags & flags) > 0; } static str* get_rr_param(void) { return &rr_param; } int load_dlg( struct dlg_binds *dlgb ) { dlgb->register_dlgcb = register_dlgcb; dlgb->create_dlg = create_dialog_wrapper; dlgb->get_dlg = get_current_dialog; dlgb->add_profiles = add_profile_definitions; dlgb->search_profile = search_dlg_profile; dlgb->set_profile = set_dlg_profile; dlgb->unset_profile = unset_dlg_profile; dlgb->get_profile_size = get_profile_size; dlgb->store_dlg_value = store_dlg_value; dlgb->fetch_dlg_value = fetch_dlg_value; dlgb->terminate_dlg = terminate_dlg; dlgb->match_dialog = w_match_dialog; dlgb->fix_route_dialog = fix_route_dialog; dlgb->validate_dialog = dlg_validate_dialog; dlgb->set_mod_flag = set_mod_flag_wrapper; dlgb->is_mod_flag_set = is_mod_flag_set_wrapper; dlgb->ref_dlg = ref_dlg; dlgb->unref_dlg = unref_dlg_destroy_safe; dlgb->get_rr_param = get_rr_param; return 1; } static int pv_get_dlg_count(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int n; int l; char *ch; if(res==NULL) return -1; n = active_dlgs ? get_stat_val(active_dlgs) : 0; l = 0; ch = int2str( n, &l); res->rs.s = ch; res->rs.len = l; res->ri = n; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } static void ctx_dlg_idx_destroy(void *v) { unref_dlg((struct dlg_cell*)v, 1); /* reset the pointer to make sure no-one is trying to free it anymore */ if (current_processing_ctx) ctx_dialog_set(NULL); } static int mod_init(void) { unsigned int n; LM_INFO("Dialog module - initializing\n"); if (timeout_spec.s) timeout_spec.len = strlen(timeout_spec.s); init_db_url( db_url , 1 /*can be null*/); dlg_id_column.len = strlen(dlg_id_column.s); call_id_column.len = strlen(call_id_column.s); from_uri_column.len = strlen(from_uri_column.s); from_tag_column.len = strlen(from_tag_column.s); to_uri_column.len = strlen(to_uri_column.s); to_tag_column.len = strlen(to_tag_column.s); state_column.len = strlen(state_column.s); start_time_column.len = strlen(start_time_column.s); timeout_column.len = strlen(timeout_column.s); to_cseq_column.len = strlen(to_cseq_column.s); from_cseq_column.len = strlen(from_cseq_column.s); to_route_column.len = strlen(to_route_column.s); from_route_column.len = strlen(from_route_column.s); to_contact_column.len = strlen(to_contact_column.s); from_contact_column.len = strlen(from_contact_column.s); to_sock_column.len = strlen(to_sock_column.s); from_sock_column.len = strlen(from_sock_column.s); profiles_column.len = strlen(profiles_column.s); vars_column.len = strlen(vars_column.s); sflags_column.len = strlen(sflags_column.s); mflags_column.len = strlen(mflags_column.s); flags_column.len = strlen(flags_column.s); dialog_table_name.len = strlen(dialog_table_name.s); /* param checkings */ if( log_profile_hash_size <= 0) { LM_ERR("invalid value for log_profile_hash_size:%d!!\n", log_profile_hash_size); return -1; } if (rr_param.s==0 || rr_param.s[0]==0) { LM_ERR("empty rr_param!!\n"); return -1; } rr_param.len = strlen(rr_param.s); if (rr_param.len>MAX_DLG_RR_PARAM_NAME) { LM_ERR("rr_param too long (max=%d)!!\n", MAX_DLG_RR_PARAM_NAME); return -1; } if (default_timeout<=0) { LM_ERR("0 default_timeout not accepted!!\n"); return -1; } if (options_ping_interval<=0 || reinvite_ping_interval<=0) { LM_ERR("Non-positive ping interval not accepted!!\n"); return -1; } /* update the len of the extra headers */ if (dlg_extra_hdrs.s) dlg_extra_hdrs.len = strlen(dlg_extra_hdrs.s); if (seq_match_mode!=SEQ_MATCH_NO_ID && seq_match_mode!=SEQ_MATCH_FALLBACK && seq_match_mode!=SEQ_MATCH_STRICT_ID ) { LM_ERR("invalid value %d for seq_match_mode param!!\n",seq_match_mode); return -1; } /* if statistics are disabled, prevent their registration to core */ if (dlg_enable_stats==0) exports.stats = 0; /* we are only interested in these parameters if the cachedb url was defined */ if (cdb_url.s) { cdb_url.len = strlen(cdb_url.s); if (init_cachedb_utils() <0) { LM_ERR("cannot init cachedb utils\n"); return -1; } cdb_val_prefix.len = strlen(cdb_val_prefix.s); cdb_noval_prefix.len = strlen(cdb_noval_prefix.s); cdb_size_prefix.len = strlen(cdb_size_prefix.s); } /* allocate a slot in the processing context */ ctx_dlg_idx = context_register_ptr(CONTEXT_GLOBAL, ctx_dlg_idx_destroy); ctx_timeout_idx = context_register_int(CONTEXT_GLOBAL, NULL); ctx_lastdstleg_idx = context_register_int(CONTEXT_GLOBAL, NULL); /* create dialog state changed event */ if (state_changed_event_init() < 0) { LM_ERR("cannot create dialog state changed event\n"); return -1; } /* create profile hashes */ if (add_profile_definitions( profiles_nv_s, 0)!=0 ) { LM_ERR("failed to add profiles without value\n"); return -1; } if (add_profile_definitions( profiles_wv_s, 1)!=0 ) { LM_ERR("failed to add profiles with value\n"); return -1; } /* load the TM API */ if (load_tm_api(&d_tmb)!=0) { LM_ERR("can't load TM API\n"); return -1; } /* load RR API also */ if (load_rr_api(&d_rrb)!=0) { LM_ERR("can't load RR API\n"); return -1; } /* register callbacks*/ /* listen for all incoming requests */ if ( d_tmb.register_tmcb( 0, 0, TMCB_REQUEST_IN, dlg_onreq, 0, 0 ) <=0 ) { LM_ERR("cannot register TMCB_REQUEST_IN callback\n"); return -1; } /* listen for all routed requests */ if ( d_rrb.register_rrcb( dlg_onroute, 0, 1 ) <0 ) { LM_ERR("cannot register RR callback\n"); return -1; } if (register_script_cb( dialog_cleanup, POST_SCRIPT_CB|REQ_TYPE_CB|RPL_TYPE_CB,0)<0) { LM_ERR("cannot register script callback"); return -1; } if( accept_replicated_dlg < 0 ) accept_replicated_dlg = 0; if( accept_repl_profiles < 0 ) accept_repl_profiles = 0; if (accept_replicated_dlg && bin_register_cb("dialog", receive_dlg_binary_packet, NULL) < 0) { LM_ERR("Cannot register binary packet callback!\n"); return -1; } if ( (dialog_replicate_cluster > 0 || profile_replicate_cluster>0 || accept_replicated_dlg || accept_repl_profiles ) && load_clusterer_api(&clusterer_api) != 0 ){ LM_DBG("failed to find clusterer API - is clusterer module loaded?\n"); return -1; } if (repl_prof_auth_check < 0) repl_prof_auth_check = 0; if (accept_replicated_profile_timeout <= 0) accept_replicated_profile_timeout = 10; if(accept_repl_profiles && clusterer_api.register_module("dialog", PROTO_BIN, receive_prof_binary_packet, accept_replicated_profile_timeout, repl_prof_auth_check, accept_repl_profiles) < 0){ LM_ERR("Cannot register binary packet callback!\n"); return -1; } if( dialog_replicate_cluster < 0 ) dialog_replicate_cluster = 0; if( profile_replicate_cluster < 0 ) profile_replicate_cluster = 0; if ( register_timer( "dlg-timer", dlg_timer_routine, NULL, 1, TIMER_FLAG_DELAY_ON_DELAY)<0 ) { LM_ERR("failed to register timer\n"); return -1; } if ( register_timer( "dlg-options-pinger", dlg_options_routine, NULL, options_ping_interval, TIMER_FLAG_DELAY_ON_DELAY)<0) { LM_ERR("failed to register timer 2\n"); return -1; } if ( register_timer( "dlg-reinvite-pinger", dlg_reinvite_routine, NULL, reinvite_ping_interval, TIMER_FLAG_DELAY_ON_DELAY)<0) { LM_ERR("failed to register timer 2\n"); return -1; } /* init handlers */ init_dlg_handlers(default_timeout); /* init timer */ if (init_dlg_timer(dlg_ontimeout)!=0) { LM_ERR("cannot init timer list\n"); return -1; } if (init_dlg_ping_timer()!=0) { LM_ERR("cannot init ping timer\n"); return -1; } if (init_dlg_reinvite_ping_timer()!=0) { LM_ERR("cannot init ping timer\n"); return -1; } /* initialized the hash table */ for( n=0 ; n<(8*sizeof(n)) ; n++) { if (dlg_hash_size==(1< rounding from %d to %d\n", dlg_hash_size, 1<<(n-1)); dlg_hash_size = 1<<(n-1); break; } } if ( init_dlg_table(dlg_hash_size)<0 ) { LM_ERR("failed to create hash table\n"); return -1; } if (repl_prof_init() < 0) { LM_ERR("cannot initialize profile replication\n"); return -1; } /* if a database should be used to store the dialogs' information */ if (dlg_db_mode==DB_MODE_NONE) { db_url.s = 0; db_url.len = 0; } else { if (dlg_db_mode!=DB_MODE_REALTIME && dlg_db_mode!=DB_MODE_DELAYED && dlg_db_mode!=DB_MODE_SHUTDOWN ) { LM_ERR("unsupported db_mode %d\n", dlg_db_mode); return -1; } if ( !db_url.s || db_url.len==0 ) { LM_ERR("db_url not configured for db_mode %d\n", dlg_db_mode); return -1; } if (init_dlg_db(&db_url, dlg_hash_size, db_update_period)!=0) { LM_ERR("failed to initialize the DB support\n"); return -1; } run_load_callbacks(); } mark_dlg_loaded_callbacks_run(); destroy_cachedb(0); return 0; } static int child_init(int rank) { if (rank==1) { if_update_stat(dlg_enable_stats, active_dlgs, active_dlgs_cnt); if_update_stat(dlg_enable_stats, early_dlgs, early_dlgs_cnt); } if ( (dlg_db_mode==DB_MODE_REALTIME && (rank>=PROC_MAIN||rank==PROC_MODULE)) || (dlg_db_mode==DB_MODE_SHUTDOWN && (rank==PROC_MAIN||rank==PROC_MODULE)) || (dlg_db_mode==DB_MODE_DELAYED && (rank>=PROC_MAIN||rank==PROC_MODULE)) ){ if ( dlg_connect_db(&db_url) ) { LM_ERR("failed to connect to database (rank=%d)\n",rank); return -1; } } if (cdb_url.s && cdb_url.len && init_cachedb() < 0) { LM_ERR("cannot init cachedb feature\n"); return -1; } return 0; } static void mod_destroy(void) { if (dlg_db_mode != DB_MODE_NONE) { dialog_update_db(0, 0); destroy_dlg_db(); } /* no DB interaction from now on */ dlg_db_mode = DB_MODE_NONE; destroy_dlg_table(); destroy_dlg_timer(); destroy_ping_timer(); destroy_dlg_callbacks( DLGCB_CREATED|DLGCB_LOADED ); destroy_dlg_handlers(); destroy_dlg_profiles(); destroy_cachedb(1); /* free DLG_STATE_CHANGED event */ state_changed_event_destroy(); } static int w_create_dialog(struct sip_msg *req) { struct cell *t; /* is the dialog already created? */ if (get_current_dialog()!=NULL) return 1; t = d_tmb.t_gett(); if (dlg_create_dialog( (t==T_UNDEFINED)?NULL:t, req,0)!=0) return -1; return 1; } static int w_create_dialog2(struct sip_msg *req,char *param) { struct dlg_cell *dlg; struct cell *t; str res = {0,0}; int flags; if (fixup_get_svalue(req, (gparam_p)param, &res) !=0) { LM_ERR("no create dialog flags\n"); return -1; } flags = parse_create_dlg_flags(res); /* is the dialog already created? */ if ( (dlg=get_current_dialog())!=NULL ) { /*Clear current flags before setting new ones*/ dlg->flags &= ~(DLG_FLAG_PING_CALLER | DLG_FLAG_PING_CALLEE | DLG_FLAG_BYEONTIMEOUT | DLG_FLAG_REINVITE_PING_CALLER | DLG_FLAG_REINVITE_PING_CALLEE); dlg->flags |= flags; return 1; } t = d_tmb.t_gett(); if (dlg_create_dialog( (t==T_UNDEFINED)?NULL:t, req,flags)!=0) return -1; return 1; } static int w_match_dialog(struct sip_msg *msg) { int backup,i; void *match_param = NULL; struct sip_uri *r_uri; /* dialog already found ? */ if (get_current_dialog()!=NULL) return 1; /* small trick to force SIP-wise matching */ backup = seq_match_mode; seq_match_mode = SEQ_MATCH_FALLBACK; /* See if we can force DID matching, for the case of topo * hiding, where we have the DID as param of the contact */ if (parse_sip_msg_uri(msg)<0) { LM_ERR("Failed to parse request URI\n"); goto sipwise; } if (parse_headers(msg, HDR_ROUTE_F, 0) == -1) { LM_ERR("failed to parse route headers\n"); goto sipwise; } r_uri = &msg->parsed_uri; if (check_self(&r_uri->host,r_uri->port_no ? r_uri->port_no : SIP_PORT, 0) == 1 && msg->route == NULL) { /* Seems we are in the topo hiding case : * we are in the R-URI and there are no other route headers */ for (i=0;iu_params_no;i++) if (r_uri->u_name[i].len == rr_param.len && memcmp(rr_param.s,r_uri->u_name[i].s,rr_param.len)==0) { LM_DBG("We found DID param in R-URI with value of %.*s\n", r_uri->u_val[i].len,r_uri->u_val[i].s); /* pass the param value to the matching funcs */ match_param = (void *)(&r_uri->u_val[i]); } } sipwise: dlg_onroute( msg, NULL, match_param); seq_match_mode = backup; return (get_current_dialog()==NULL)?-1:1; } static int w_validate_dialog(struct sip_msg *req) { struct dlg_cell *dlg; int ret; dlg = get_current_dialog(); if (dlg==NULL) { LM_ERR("null dialog\n"); return -4; } ret = dlg_validate_dialog(req,dlg); if (ret == 0) ret = 1; return ret; } static int w_fix_route_dialog(struct sip_msg *req) { struct dlg_cell *dlg; dlg = get_current_dialog(); if (dlg==NULL) return -1; if (fix_route_dialog( req, dlg )!=0) return -1; return 1; } static int w_set_dlg_profile(struct sip_msg *msg, char *profile, char *value) { struct dlg_cell *dlg; pv_elem_t *pve = (pv_elem_t *)value; str val_s; if ( (dlg=get_current_dialog())==NULL ) { LM_CRIT("BUG - setting profile from script, but no dialog found\n"); return -1; } if (((struct dlg_profile_table*)profile)->has_value) { if ( pve==NULL || pv_printf_s(msg, pve, &val_s)!=0 || val_s.len == 0 || val_s.s == NULL) { LM_WARN("cannot get string for value\n"); return -1; } if ( set_dlg_profile( dlg, &val_s, (struct dlg_profile_table*)profile, 0) < 0 ) { LM_ERR("failed to set profile\n"); return -1; } } else { if ( set_dlg_profile( dlg, NULL, (struct dlg_profile_table*)profile, 0) < 0 ) { LM_ERR("failed to set profile\n"); return -1; } } return 1; } static int w_unset_dlg_profile(struct sip_msg *msg, char *profile, char *value) { struct dlg_cell *dlg; pv_elem_t *pve = (pv_elem_t *)value; str val_s; if ( (dlg=get_current_dialog())==NULL ) { LM_CRIT("BUG - setting profile from script, but no dialog found\n"); return -1; } if (((struct dlg_profile_table*)profile)->has_value) { if ( pve==NULL || pv_printf_s(msg, pve, &val_s)!=0 || val_s.len == 0 || val_s.s == NULL) { LM_WARN("cannot get string for value\n"); return -1; } if ( unset_dlg_profile( dlg, &val_s, (struct dlg_profile_table*)profile) < 0 ) { LM_ERR("failed to unset profile\n"); return -1; } } else { if ( unset_dlg_profile( dlg, NULL, (struct dlg_profile_table*)profile) < 0 ) { LM_ERR("failed to unset profile\n"); return -1; } } return 1; } static int w_is_in_profile(struct sip_msg *msg, char *profile, char *value) { struct dlg_cell *dlg; pv_elem_t *pve = (pv_elem_t *)value; str val_s; if ( (dlg=get_current_dialog())==NULL ) { LM_CRIT("BUG - setting profile from script, but no dialog found\n"); return -1; } if ( pve!=NULL && ((struct dlg_profile_table*)profile)->has_value) { if ( pv_printf_s(msg, pve, &val_s)!=0 || val_s.len == 0 || val_s.s == NULL) { LM_WARN("cannot get string for value\n"); return -1; } return is_dlg_in_profile( dlg, (struct dlg_profile_table*)profile, &val_s); } else { return is_dlg_in_profile( dlg, (struct dlg_profile_table*)profile, NULL); } } static int w_get_profile_size(struct sip_msg *msg, char *profile, char *value, char *result) { pv_elem_t *pve; str val_s; pv_spec_t *sp_dest; unsigned int size; int_str res; int avp_name; unsigned short avp_type; script_var_t * sc_var; pve = (pv_elem_t *)value; sp_dest = (pv_spec_t *)result; if ( pve!=NULL && ((struct dlg_profile_table*)profile)->has_value) { if ( pv_printf_s(msg, pve, &val_s)!=0 || val_s.len == 0 || val_s.s == NULL) { LM_WARN("cannot get string for value\n"); return -1; } size = get_profile_size( (struct dlg_profile_table*)profile ,&val_s ); } else { size = get_profile_size( (struct dlg_profile_table*)profile, NULL ); } switch (sp_dest->type) { case PVT_AVP: if (pv_get_avp_name( msg, &(sp_dest->pvp), &avp_name, &avp_type)!=0){ LM_CRIT("BUG in getting AVP name\n"); return -1; } res.n = size; if (add_avp(avp_type, avp_name, res)<0){ LM_ERR("cannot add AVP\n"); return -1; } break; case PVT_SCRIPTVAR: if(sp_dest->pvp.pvn.u.dname == 0){ LM_ERR("cannot find svar name\n"); return -1; } res.n = size; sc_var = (script_var_t *)sp_dest->pvp.pvn.u.dname; if(!set_var_value(sc_var, &res, 0)){ LM_ERR("cannot set svar\n"); return -1; } break; default: LM_CRIT("BUG: invalid pvar type\n"); return -1; } return 1; } static int w_set_dlg_flag(struct sip_msg *msg, char *mask) { struct dlg_cell *dlg; if ( (dlg=get_current_dialog())==NULL ) return -1; dlg->user_flags |= (unsigned int)(unsigned long)mask; dlg->flags |= DLG_FLAG_VP_CHANGED; return 1; } static int w_reset_dlg_flag(struct sip_msg *msg, char *mask) { struct dlg_cell *dlg; if ( (dlg=get_current_dialog())==NULL ) return -1; dlg->user_flags &= ~((unsigned int)(unsigned long)mask); return 1; } static int w_is_dlg_flag_set(struct sip_msg *msg, char *mask) { struct dlg_cell *dlg; if ( (dlg=get_current_dialog())==NULL ) return -1; return (dlg->user_flags&((unsigned int)(unsigned long)mask))?1:-1; } static int w_tsl_dlg_flag(struct sip_msg *msg, char *_idx, char *_val) { struct dlg_cell *dlg; if ( (dlg=get_current_dialog())==NULL ) return -2; return test_and_set_dlg_flag(dlg, (unsigned long)(void *)_idx, (unsigned long)(void *) _val); } int w_store_dlg_value(struct sip_msg *msg, char *name, char *val) { struct dlg_cell *dlg; pv_elem_t *pve = (pv_elem_t *)val; str val_s; if ( (dlg=get_current_dialog())==NULL ) return -1; if ( pve==NULL || pv_printf_s(msg, pve, &val_s)!=0 || val_s.len == 0 || val_s.s == NULL) { LM_WARN("cannot get string for value\n"); return -1; } return (store_dlg_value( dlg, (str*)name, &val_s)==0)?1:-1; } int w_fetch_dlg_value(struct sip_msg *msg, char *name, char *result) { struct dlg_cell *dlg; str val; pv_spec_t *sp_dest; int_str res; int avp_name; unsigned short avp_type; script_var_t * sc_var; sp_dest = (pv_spec_t *)result; if ( (dlg=get_current_dialog())==NULL ) return -1; if (fetch_dlg_value( dlg, (str*)name, &val, 0) ) { LM_DBG("failed to fetch dialog value <%.*s>\n", ((str*)name)->len, ((str*)name)->s); return -1; } switch (sp_dest->type) { case PVT_AVP: if (pv_get_avp_name( msg, &(sp_dest->pvp), &avp_name, &avp_type)!=0){ LM_CRIT("BUG in getting AVP name\n"); return -1; } res.s = val; if (add_avp(avp_type|AVP_VAL_STR, avp_name, res)<0){ LM_ERR("cannot add AVP\n"); return -1; } break; case PVT_SCRIPTVAR: if(sp_dest->pvp.pvn.u.dname == 0){ LM_ERR("cannot find svar name\n"); return -1; } res.s = val; sc_var = (script_var_t *)sp_dest->pvp.pvn.u.dname; if(!set_var_value(sc_var, &res, VAR_VAL_STR)){ LM_ERR("cannot set svar\n"); return -1; } break; default: LM_CRIT("BUG: invalid pvar type\n"); return -1; } return 1; } static int w_get_dlg_info(struct sip_msg *msg, char *attr, char *attr_val, char *key, char *key_val) { struct dlg_cell *dlg; pv_elem_t *pve = (pv_elem_t *)key_val; pv_spec_t *dst = (pv_spec_t *)attr_val; pv_value_t val; str val_s; int n; if ( pve==NULL || pv_printf_s(msg, pve, &val_s)!=0 || val_s.len == 0 || val_s.s == NULL) { LM_WARN("cannot get string for value\n"); return -1; } dlg = get_dlg_by_val( (str*)key, &val_s); if (dlg==NULL) { /* nothing found */ LM_DBG("no dialog found\n"); return -1; } /* dlg found - NOTE you have a ref! */ LM_DBG("dialog found, fetching variable\n"); if (fetch_dlg_value( dlg, (str*)attr, &val.rs, 0) ) { LM_DBG("failed to fetch dialog value <%.*s>\n", ((str*)attr)->len, ((str*)attr)->s); n = -1 ; } else { val.flags = PV_VAL_STR; n = (dst->setf( msg, &dst->pvp, 0, &val )==0)?1:-1; } unref_dlg(dlg, 1); return n; } /* item/pseudo-variables functions */ int pv_get_dlg_lifetime(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int l = 0; char *ch = NULL; struct dlg_cell *dlg; if(res==NULL) return -1; if ( (dlg=get_current_dialog())==NULL ) return pv_get_null( msg, param, res); res->ri = (unsigned int)(dlg->state>2?((time(0))-dlg->start_ts):0); ch = int2str( (unsigned long)res->ri, &l); res->rs.s = ch; res->rs.len = l; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } int pv_get_dlg_status(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int l = 0; char *ch = NULL; struct dlg_cell *dlg; if(res==NULL) return -1; if ( (dlg=get_current_dialog())==NULL ) return pv_get_null( msg, param, res); res->ri = dlg->state; ch = int2str( (unsigned long)res->ri, &l); res->rs.s = ch; res->rs.len = l; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } int pv_get_dlg_flags(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int l = 0; char *ch = NULL; struct dlg_cell *dlg; if(res==NULL) return -1; if ( (dlg=get_current_dialog())==NULL ) return pv_get_null( msg, param, res); res->ri = dlg->user_flags; ch = int2str( (unsigned long)res->ri, &l); res->rs.s = ch; res->rs.len = l; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } int pv_get_dlg_timeout(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int l = 0; char *ch = NULL; struct dlg_cell *dlg; if(res==NULL) return -1; if ( (dlg=get_current_dialog())!=NULL ) { dlg_lock_dlg(dlg); if (dlg->state == DLG_STATE_DELETED) l = 0; else if (dlg->state < DLG_STATE_CONFIRMED_NA) l = dlg->lifetime; else l = dlg->tl.timeout - get_ticks(); dlg_unlock_dlg(dlg); } else if (current_processing_ctx) { if ((l=ctx_timeout_get())==0) return pv_get_null( msg, param, res); } else { return pv_get_null( msg, param, res); } res->ri = l; ch = int2str( (unsigned long)res->ri, &l); res->rs.s = ch; res->rs.len = l; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } int pv_get_dlg_dir(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct dlg_cell *dlg; if(res==NULL) return -1; if ( (dlg=get_current_dialog())==NULL || ctx_lastdstleg_get()<0) return pv_get_null( msg, param, res); if (ctx_lastdstleg_get()==0) { res->rs.s = "upstream"; res->rs.len = 8; } else { res->rs.s = "downstream"; res->rs.len = 10; } res->flags = PV_VAL_STR; return 0; } /* the maximum value we can have is 2 ints + ':' */ static char buf_get_did[2 * INT2STR_MAX_LEN]; int pv_get_dlg_did(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct dlg_cell *dlg; str aux; if(res==NULL) return -1; if ( (dlg=get_current_dialog())==NULL ) return pv_get_null( msg, param, res); res->rs.s = buf_get_did; aux.s = int2str( (unsigned long)dlg->h_entry, &aux.len); if (!aux.s || !aux.len) { LM_ERR("invalid hash entry\n"); return -1; } memcpy(buf_get_did, aux.s, aux.len); buf_get_did[aux.len] = ':'; res->rs.len = aux.len + 1; aux.s = int2str( (unsigned long)dlg->h_id, &aux.len); if (!aux.s || !aux.len) { LM_ERR("invalid hash id\n"); return -1; } memcpy(buf_get_did + res->rs.len, aux.s, aux.len); res->rs.len += aux.len; res->flags = PV_VAL_STR; return 0; } int pv_get_dlg_end_reason(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct dlg_cell *dlg; if(res==NULL) return -1; if ( (dlg=get_current_dialog())==NULL || dlg->terminate_reason.s == NULL) { return pv_get_null( msg, param, res); } res->rs = dlg->terminate_reason; res->flags = PV_VAL_STR; return 0; } int pv_set_dlg_flags(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) { struct dlg_cell *dlg; if ( (dlg=get_current_dialog())==NULL ) return -1; if (val==NULL) { dlg->user_flags = 0; return 0; } if (!(val->flags&PV_VAL_INT)){ LM_ERR("assigning non-int value to dialog flags\n"); return -1; } dlg->user_flags = val->ri; return 0; } int pv_set_dlg_timeout(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) { struct dlg_cell *dlg; int timeout, db_update = 0, timer_update = 0; if (val==NULL || val->flags & PV_VAL_NULL) { LM_ERR("cannot assign dialog timeout to NULL\n"); return -1; } if (!(val->flags&PV_VAL_INT)){ /* try parsing the string */ if (str2sint(&val->rs, &timeout) < 0) { LM_ERR("assigning non-int value to dialog flags\n"); return -1; } } else { timeout = val->ri; } if (timeout < 0) { LM_ERR("cannot set a negative timeout\n"); return -1; } if ((dlg = get_current_dialog()) != NULL) { dlg_lock_dlg(dlg); dlg->lifetime = timeout; /* update now only if realtime and the dialog is confirmed */ if (dlg->state >= DLG_STATE_CONFIRMED && dlg_db_mode == DB_MODE_REALTIME) db_update = 1; else dlg->flags |= DLG_FLAG_CHANGED; if (dlg->state == DLG_STATE_CONFIRMED_NA || dlg->state == DLG_STATE_CONFIRMED) timer_update = 1; dlg_unlock_dlg(dlg); if (db_update) update_dialog_timeout_info(dlg); if (dialog_replicate_cluster) replicate_dialog_updated(dlg); if (timer_update) { switch ( update_dlg_timer(&dlg->tl, timeout) ) { case -1: LM_ERR("failed to update timer\n"); return -1; case 1: /* dlg inserted in timer list with new expire (reference it)*/ ref_dlg(dlg,1); case 0: /* timeout value was updated */ break; } } } else if (current_processing_ctx) { /* store it until we match the dialog */ ctx_timeout_set( timeout ); } else { LM_CRIT("BUG - no proicessing context found !\n"); return -1; } return 0; } opensips-2.2.2/modules/dialog/dlg_cb.c000066400000000000000000000142221300170765700176510ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-14 initial version (bogdan) * 2008-04-04 added direction reporting in dlg callbacks (bogdan) * 2008-04-14 DLGCB_CREATED may be registered before the module * initialization (bogdan) * 2008-04-15 added new type of callback to be triggered when dialogs are * loaded from DB (bogdan) */ #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "dlg_hash.h" #include "dlg_cb.h" static struct dlg_head_cbl* create_cbs = 0; static int dlg_load_cbs_run = 0; static struct dlg_head_cbl* load_cbs = 0; static struct dlg_cb_params params = {NULL, DLG_DIR_NONE, NULL, NULL}; #define POINTER_CLOSED_MARKER ((void *)(-1)) static void run_load_callback(struct dlg_callback *cb); static struct dlg_head_cbl* init_dlg_callback(void) { struct dlg_head_cbl *new_cbs; new_cbs = (struct dlg_head_cbl*)shm_malloc(sizeof(struct dlg_head_cbl)); if (new_cbs==0) { LM_ERR("no more shm mem\n"); return 0; } new_cbs->first = 0; new_cbs->types = 0; return new_cbs; } void destroy_dlg_callbacks_list(struct dlg_callback *cb) { struct dlg_callback *cb_t; while(cb) { cb_t = cb; cb = cb->next; if (cb_t->callback_param_free && cb_t->param) { cb_t->callback_param_free(cb_t->param); cb_t->param = NULL; } shm_free(cb_t); } } void mark_dlg_loaded_callbacks_run(void) { dlg_load_cbs_run = 1; } void destroy_dlg_callbacks(unsigned int types) { if (types&DLGCB_CREATED) { if (create_cbs && create_cbs!=POINTER_CLOSED_MARKER) { destroy_dlg_callbacks_list(create_cbs->first); shm_free(create_cbs); } create_cbs = POINTER_CLOSED_MARKER; } if (types&DLGCB_LOADED) { if (load_cbs && load_cbs!=POINTER_CLOSED_MARKER) { destroy_dlg_callbacks_list(load_cbs->first); shm_free(load_cbs); } load_cbs = POINTER_CLOSED_MARKER; } } int register_dlgcb(struct dlg_cell *dlg, int types, dialog_cb f, void *param, param_free_cb ff ) { struct dlg_callback *cb; if ( types&DLGCB_LOADED ) { if (types!=DLGCB_LOADED) { LM_CRIT("DLGCB_LOADED type must be register alone!\n"); return -1; } } else if ( types&DLGCB_CREATED ) { if (types!=DLGCB_CREATED) { LM_CRIT("DLGCB_CREATED type must be register alone!\n"); return -1; } } else { if (dlg==0) { LM_CRIT("non-DLGCB_CREATED type " "must be register to a dialog (dlg missing)!\n"); return -1; } } cb = (struct dlg_callback*)shm_malloc(sizeof(struct dlg_callback)); if (cb==0) { LM_ERR("no more shm mem\n"); return -1; } cb->types = types; cb->callback = f; cb->param = param; cb->callback_param_free = ff; cb->next = NULL; if ( types==DLGCB_CREATED ) { if (create_cbs==POINTER_CLOSED_MARKER) { LM_CRIT("DLGCB_CREATED type registered after shutdown!?!\n"); goto error; } if (create_cbs==0) { /* not initialized yet */ if ( (create_cbs=init_dlg_callback())==NULL ) { LM_ERR("no more shm mem\n"); goto error; } } cb->next = create_cbs->first; create_cbs->first = cb; create_cbs->types |= types; } else if (types==DLGCB_LOADED) { if (dlg_load_cbs_run) { /* run the callback on the spot */ run_load_callback(cb); return 0; } if (load_cbs==0) { /* not initialized yet */ if ( (load_cbs=init_dlg_callback())==NULL ) { LM_ERR("no more shm mem\n"); goto error; } } cb->next = load_cbs->first; load_cbs->first = cb; load_cbs->types |= types; } else { cb->next = dlg->cbs.first; dlg->cbs.first = cb; dlg->cbs.types |= types; } return 0; error: shm_free(cb); return -1; } static void run_load_callback(struct dlg_callback *cb) { struct dlg_cell *dlg; unsigned int i; params.msg = NULL; params.direction = DLG_DIR_NONE; params.param = &cb->param; for( i=0 ; isize ; i++ ) { for( dlg=d_table->entries[i].first ; dlg ; dlg=dlg->next ) cb->callback( dlg, DLGCB_LOADED, ¶ms ); } return; } void run_load_callbacks( void ) { struct dlg_callback *cb; if (load_cbs && load_cbs!=POINTER_CLOSED_MARKER) { for ( cb=load_cbs->first; cb; cb=cb->next ) run_load_callback( cb ); } return; } void run_load_callback_per_dlg(struct dlg_cell *dlg) { struct dlg_callback *cb; params.msg = NULL; params.direction = DLG_DIR_NONE; if (load_cbs && load_cbs!=POINTER_CLOSED_MARKER) { for ( cb=load_cbs->first; cb; cb=cb->next ) { params.param = &cb->param; cb->callback( dlg, DLGCB_LOADED, ¶ms ); } } return; } void run_create_callbacks(struct dlg_cell *dlg, struct sip_msg *msg) { struct dlg_callback *cb; if (create_cbs==NULL || create_cbs->first==NULL) return; params.msg = msg; /* initial request goes DOWNSTREAM all the time */ params.direction = DLG_DIR_DOWNSTREAM; /* avoid garbage due static structure */ params.param = NULL; params.dlg_data = NULL; for ( cb=create_cbs->first; cb; cb=cb->next) { LM_DBG("dialog=%p\n",dlg); params.param = &cb->param; cb->callback( dlg, DLGCB_CREATED, ¶ms ); } return; } void run_dlg_callbacks(int type , struct dlg_cell *dlg, struct sip_msg *msg, unsigned int dir, void *dlg_data) { struct dlg_callback *cb; params.msg = msg; params.direction = dir; params.dlg_data = dlg_data; if (dlg->cbs.first==0 || ((dlg->cbs.types)&type)==0 ) return; for ( cb=dlg->cbs.first; cb; cb=cb->next) { if ( (cb->types)&type ) { LM_DBG("dialog=%p, type=%d\n", dlg, type); params.param = &cb->param; cb->callback( dlg, type, ¶ms ); } } return; } opensips-2.2.2/modules/dialog/dlg_cb.h000066400000000000000000000062231300170765700176600ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRLs * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-11 initial version (bogdan) * 2008-04-04 added direction reporting in dlg callbacks (bogdan) * 2008-04-14 added new type of callback to be triggered when dialogs are * loaded from DB (bogdan) * 2008-04-17 added new type of callback to be triggered right before the * dialog is destroyed (deleted from memory) (bogdan) */ #ifndef _DIALOG_DLG_CB_H_ #define _DIALOG_DLG_CB_H_ #include "../../parser/msg_parser.h" struct dlg_cell; struct dlg_cb_params { struct sip_msg* msg; /* sip msg related to the callback event */ unsigned int direction; /* direction of the sip msg */ void *dlg_data; /* generic parameter, specific to callback */ void **param; /* parameter passed at callback registration*/ }; /* callback function prototype */ typedef void (dialog_cb) (struct dlg_cell* dlg, int type, struct dlg_cb_params * params); /* function to free the callback param */ typedef void (param_free_cb) (void *param); /* register callback function prototype */ typedef int (*register_dlgcb_f)(struct dlg_cell* dlg, int cb_types, dialog_cb f, void *param, param_free_cb ff); #define DLGCB_LOADED (1<<0) #define DLGCB_CREATED (1<<1) #define DLGCB_FAILED (1<<2) #define DLGCB_CONFIRMED (1<<3) #define DLGCB_REQ_WITHIN (1<<4) #define DLGCB_TERMINATED (1<<5) #define DLGCB_EXPIRED (1<<6) #define DLGCB_EARLY (1<<7) #define DLGCB_RESPONSE_FWDED (1<<8) #define DLGCB_RESPONSE_WITHIN (1<<9) #define DLGCB_MI_CONTEXT (1<<10) #define DLGCB_DESTROY (1<<11) #define DLGCB_SAVED (1<<12) struct dlg_callback { int types; dialog_cb* callback; void *param; param_free_cb* callback_param_free; struct dlg_callback* next; }; struct dlg_head_cbl { struct dlg_callback *first; int types; }; void destroy_dlg_callbacks(unsigned int type); void destroy_dlg_callbacks_list(struct dlg_callback *cb); void mark_dlg_loaded_callbacks_run(void); int register_dlgcb( struct dlg_cell* dlg, int types, dialog_cb f, void *param, param_free_cb ff); void run_create_callbacks(struct dlg_cell *dlg, struct sip_msg *msg); void run_dlg_callbacks( int type , struct dlg_cell *dlg, struct sip_msg *msg, unsigned int dir, void *dlg_data); void run_load_callbacks( void ); void run_load_callback_per_dlg(struct dlg_cell *dlg); #endif opensips-2.2.2/modules/dialog/dlg_db_handler.c000066400000000000000000001672301300170765700213570ustar00rootroot00000000000000/* * Copyright (C) 2009-2014 OpenSIPS Solutions * Copyright (C) 2007-2009 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-05-10 initial version (ancuta) * 2007-07-06 additional information saved in the database: cseq, contact, * route set and socket_info for both caller and callee (ancuta) * 2009-09-09 support for early dialogs added; proper handling of cseq * while PRACK is used (bogdan) */ #include #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../timer.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../str.h" #include "../../socket_info.h" #include "dlg_hash.h" #include "dlg_db_handler.h" #include "dlg_cb.h" #include "dlg_profile.h" str dlg_id_column = str_init(DLG_ID_COL); str call_id_column = str_init(CALL_ID_COL); str from_uri_column = str_init(FROM_URI_COL); str from_tag_column = str_init(FROM_TAG_COL); str to_uri_column = str_init(TO_URI_COL); str to_tag_column = str_init(TO_TAG_COL); str state_column = str_init(STATE_COL); str start_time_column = str_init(START_TIME_COL); str timeout_column = str_init(TIMEOUT_COL); str to_cseq_column = str_init(TO_CSEQ_COL); str from_cseq_column = str_init(FROM_CSEQ_COL); str to_ping_cseq_column = str_init(TO_PING_CSEQ_COL); str from_ping_cseq_column = str_init(FROM_PING_CSEQ_COL); str to_route_column = str_init(TO_ROUTE_COL); str from_route_column = str_init(FROM_ROUTE_COL); str to_contact_column = str_init(TO_CONTACT_COL); str from_contact_column = str_init(FROM_CONTACT_COL); str to_sock_column = str_init(TO_SOCK_COL); str from_sock_column = str_init(FROM_SOCK_COL); str mangled_fu_column = str_init(MANGLED_FU_COL); str mangled_tu_column = str_init(MANGLED_TU_COL); str vars_column = str_init(VARS_COL); str profiles_column = str_init(PROFILES_COL); str sflags_column = str_init(SFLAGS_COL); str mflags_column = str_init(MFLAGS_COL); str flags_column = str_init(FLAGS_COL); str dialog_table_name = str_init(DIALOG_TABLE_NAME); int dlg_db_mode = DB_MODE_NONE; static db_con_t* dialog_db_handle = 0; /* database connection handle */ static db_func_t dialog_dbf; extern int dlg_enable_stats; extern int active_dlgs_cnt; extern int early_dlgs_cnt; extern stat_var *active_dlgs; extern stat_var *early_dlgs; extern int dlg_bulk_del_no; static inline void set_final_update_cols(db_val_t *, struct dlg_cell *, int); #define SET_BIGINT_VALUE(_val, _bigint)\ do{\ VAL_BIGINT(_val) = _bigint;\ VAL_NULL(_val) = 0;\ }while(0); #define SET_INT_VALUE(_val, _int)\ do{\ VAL_INT(_val) = _int;\ VAL_NULL(_val) = 0;\ }while(0); #define SET_STR_VALUE(_val, _str)\ do{\ if ( (_str).len != 0) { \ VAL_STR((_val)).s = (_str).s;\ VAL_STR((_val)).len = (_str).len;\ VAL_NULL(_val) = 0;\ } else { \ VAL_STR((_val)).s = NULL;\ VAL_STR((_val)).len = 0;\ VAL_NULL(_val) = 1;\ }\ }while(0); #define GET_STR_VALUE(_res, _values, _index, _not_null, _unref)\ do{\ if (VAL_NULL((_values)+ (_index))) { \ if (_not_null) {\ if (_unref) unref_dlg(dlg,1);\ goto next_dialog; \ } else { \ (_res).s = 0; \ (_res).len = 0; \ }\ } else { \ (_res).s = VAL_STR((_values)+ (_index)).s;\ (_res).len = strlen(VAL_STR((_values)+ (_index)).s);\ } \ }while(0); static int load_dialog_info_from_db(int dlg_hash_size); int dlg_connect_db(const str *db_url) { if (dialog_db_handle) { LM_CRIT("BUG - db connection found already open\n"); return -1; } if ((dialog_db_handle = dialog_dbf.init(db_url)) == 0) return -1; return 0; } static int use_dialog_table(void) { if(!dialog_db_handle){ LM_ERR("invalid database handle\n"); return -1; } dialog_dbf.use_table(dialog_db_handle, &dialog_table_name); return 0; } static int remove_all_dialogs_from_db(void) { if (use_dialog_table()!=0) return -1; if(dialog_dbf.delete(dialog_db_handle, NULL, NULL, NULL, 0) < 0) { LM_ERR("failed to delete database information\n"); return -1; } return 0; } int init_dlg_db(const str *db_url, int dlg_hash_size , int db_update_period) { /* Find a database module */ if (db_bind_mod(db_url, &dialog_dbf) < 0){ LM_ERR("Unable to bind to a database driver\n"); return -1; } if (dlg_connect_db(db_url)!=0){ LM_ERR("unable to connect to the database\n"); return -1; } if(db_check_table_version(&dialog_dbf, dialog_db_handle, &dialog_table_name, DLG_TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); return -1; } if (dlg_db_mode == DB_MODE_DELAYED) { if (register_timer("dlg-dbupdate",dialog_update_db, 0, db_update_period, TIMER_FLAG_SKIP_ON_DELAY)<0 ) { LM_ERR("failed to register update db\n"); return -1; } } if( (load_dialog_info_from_db(dlg_hash_size) ) !=0 ){ LM_ERR("unable to load the dialog data\n"); return -1; } if (dlg_db_mode==DB_MODE_SHUTDOWN && remove_all_dialogs_from_db()!=0) { LM_WARN("failed to properly remove all the dialogs form DB\n"); } dialog_dbf.close(dialog_db_handle); dialog_db_handle = 0; return 0; } void destroy_dlg_db(void) { /* close the DB connection */ if (dialog_db_handle) { dialog_dbf.close(dialog_db_handle); dialog_db_handle = 0; } } static int select_entire_dialog_table(db_res_t ** res, int *no_rows) { db_key_t query_cols[DIALOG_TABLE_TOTAL_COL_NO] = { &dlg_id_column, &call_id_column, &from_uri_column, &from_tag_column, &to_uri_column, &to_tag_column, &start_time_column, &state_column, &timeout_column, &from_cseq_column, &to_cseq_column, &from_route_column, &to_route_column, &from_contact_column, &to_contact_column, &from_sock_column, &to_sock_column, &vars_column, &profiles_column, &sflags_column, &from_ping_cseq_column, &to_ping_cseq_column,&flags_column, &mangled_fu_column,&mangled_tu_column, &mflags_column}; if(use_dialog_table() != 0){ return -1; } /* select the whole tabel and all the columns */ if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH)) { if(dialog_dbf.query(dialog_db_handle,0,0,0,query_cols, 0, DIALOG_TABLE_TOTAL_COL_NO, 0, 0) < 0) { LM_ERR("Error while querying (fetch) database\n"); return -1; } *no_rows = estimate_available_rows( 4+255+128+64+128+64+64+64+11+11+4+4 +512+512+128+128+64+64+4+4+4+4096+512+4+4+4 ,DIALOG_TABLE_TOTAL_COL_NO ); if (*no_rows==0) *no_rows = 10; if(dialog_dbf.fetch_result(dialog_db_handle,res,*no_rows)<0){ LM_ERR("fetching rows failed\n"); return -1; } } else { if(dialog_dbf.query(dialog_db_handle,0,0,0,query_cols, 0, DIALOG_TABLE_TOTAL_COL_NO, 0, res) < 0) { LM_ERR("Error while querying database\n"); return -1; } } return 0; } struct socket_info * create_socket_info(db_val_t * vals, int n){ struct socket_info * sock; str host, p; int port, proto; /* socket name */ p.s = (VAL_STR(vals+n)).s; p.len = strlen(p.s); if (VAL_NULL(vals+n) || p.s==0 || p.s[0]==0){ sock = 0; } else { if (parse_phostport( p.s, p.len, &host.s, &host.len, &port, &proto)!=0) { LM_ERR("bad socket <%.*s>\n", p.len, p.s); return 0; } sock = grep_sock_info( &host, (unsigned short)port, proto); if (sock==0) { LM_WARN("non-local socket <%.*s>...ignoring\n", p.len, p.s); } } return sock; } static inline void strip_esc(str *s) { char *c = s->s; int len = s->len; for ( ; len > 0; len--, c++) { if (*c == '\\' && len > 0 && (*(c+1)=='\\' || *(c+1)=='#' || *(c+1)=='|')) { memmove(c, c + 1, len - 1); s->len--; len--; } } } static inline char* read_pair(char *b, char *end, str *name, str *val) { /* read name */ name->s = b; while( blen = b - name->s; if (name->len==0) goto skip; strip_esc(name); /*LM_DBG("-----read name <%.*s>(%d)\n",name->len,name->s,name->len);*/ /* read # */ b++; /* read value */ val->s = b; while( blen = b - val->s; if (val->len==0) val->s = 0; strip_esc(val); /*LM_DBG("-----read value <%.*s>(%d)\n",val->len,val->s,val->len);*/ /* read | */ b++; return b; skip: while(b=<%.*s>\n",name.len,name.s,val.len,val.s); /* add the variable */ if (store_dlg_value_unsafe( dlg, &name, &val)!=0) LM_ERR("failed to add val, skipping...\n"); } while(p!=end); } void read_dialog_profiles(char *b, int l, struct dlg_cell *dlg,int double_check, char is_replicated) { struct dlg_profile_table *profile; struct dlg_profile_link *it; str name, val,double_check_name; char *end; char *p,*s,*e; char bk; unsigned repl_type; end = b + l; p = b; do { /* read a new pair from input string */ p = read_pair( p, end, &name, &val); if (p==NULL) break; LM_DBG("new profile found <%.*s>=<%.*s>\n",name.len,name.s,val.len,val.s); if (double_check) { LM_DBG("Double checking profile - if it exists we'll skip it \n"); repl_type = REPL_NONE; /* check if this is a shared profile, and remove /s for manual * matching */ double_check_name = name; s = memchr(name.s, '/', name.len); if (s) { e = double_check_name.s + double_check_name.len; double_check_name.len = s - double_check_name.s; trim_spaces_lr( double_check_name ); /* skip spaces after p */ for (++s; *s == ' ' && s < e; s++); if ( s < e && *s == 's') repl_type = REPL_CACHEDB; else if (s < e && *s == 'b') repl_type = REPL_PROTOBIN; } for (it=dlg->profile_links;it;it=it->next) { if (it->profile->repl_type == repl_type && it->profile->name.len == double_check_name.len && memcmp(it->profile->name.s,double_check_name.s, double_check_name.len) == 0) { LM_DBG("Profile is already linked into the dlg\n"); goto next; } } } /* add to the profile */ profile = search_dlg_profile( &name ); if (profile==NULL) { LM_DBG("profile <%.*s> does not exist now, creating it\n",name.len,name.s); /* create a new one */ bk = name.s[name.len]; name.s[name.len] = 0; if (add_profile_definitions(name.s, (val.len && val.s)?1:0 ) != 0) { LM_ERR("failed to add dialog profile <%.*s>\n", name.len, name.s); name.s[name.len] = bk; continue; } name.s[name.len] = bk; /* double check the created profile */ profile = search_dlg_profile(&name); if (profile == NULL) { LM_CRIT("BUG - cannot find just added dialog profile <%.*s>\n", name.len, name.s); continue; } } if (set_dlg_profile( dlg, profile->has_value ? &val : NULL, profile, is_replicated) < 0 ) LM_ERR("failed to add to profile, skipping....\n"); next: ; } while(p!=end); return; } int remove_ended_dlgs_from_db(void) { static db_ps_t my_ps = NULL; db_val_t values[1]; db_key_t match_keys[1] = { &state_column}; if (use_dialog_table()!=0) return -1; VAL_TYPE(values) = DB_INT; VAL_NULL(values) = 0; VAL_INT(values) = DLG_STATE_DELETED ; CON_PS_REFERENCE(dialog_db_handle) = &my_ps; if(dialog_dbf.delete(dialog_db_handle, match_keys, 0, values, 1) < 0) { LM_ERR("failed to delete database information\n"); return -1; } return 0; } static int load_dialog_info_from_db(int dlg_hash_size) { db_res_t * res; db_val_t * values; db_row_t * rows; int i, nr_rows; struct dlg_cell *dlg; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; unsigned int next_id; int no_rows = 10; struct socket_info *caller_sock,*callee_sock; int found_ended_dlgs=0; unsigned int hash_entry,hash_id; res = 0; if((nr_rows = select_entire_dialog_table(&res,&no_rows)) < 0) goto error; nr_rows = RES_ROW_N(res); do { LM_DBG("loading information from database for %i dialogs\n", nr_rows); rows = RES_ROWS(res); /* for every row---dialog */ for(i=0; i skipping\n", dlg_id_column.len,dlg_id_column.s,VAL_TYPE(values)); continue; } hash_entry = (unsigned int )(VAL_BIGINT(values) >> 32); hash_id = (unsigned int)(VAL_BIGINT(values) & 0x00000000ffffffff); if (VAL_NULL(values+6) || VAL_NULL(values+7)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", start_time_column.len, start_time_column.s, state_column.len, state_column.s); continue; } if ( VAL_INT(values+7) == DLG_STATE_DELETED ) { LM_INFO("dialog already terminated -> skipping\n"); found_ended_dlgs=1; continue; } caller_sock = create_socket_info(values, 15); callee_sock = create_socket_info(values, 16); if (caller_sock == NULL || callee_sock == NULL) { LM_ERR("Dialog in DB doesn't match any listening sockets"); continue; } /*restore the dialog info*/ GET_STR_VALUE(callid, values, 1, 1, 0); GET_STR_VALUE(from_uri, values, 2, 1, 0); GET_STR_VALUE(from_tag, values, 3, 1, 0); GET_STR_VALUE(to_uri, values, 4, 1, 0); if((dlg=build_new_dlg(&callid, &from_uri, &to_uri, &from_tag))==0){ LM_ERR("failed to build new dialog\n"); goto error; } if(dlg->h_entry != hash_entry){ LM_ERR("inconsistent hash data in the dialog database: " "you may have restarted opensips using a different " "hash_size: please erase %.*s database and restart\n" "dlg : %u, db : %u\n", dialog_table_name.len, dialog_table_name.s, dlg->h_entry,hash_entry); shm_free(dlg); goto error; } /*link the dialog*/ link_dlg(dlg, 0); dlg->h_id = hash_id; next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id <= dlg->h_id) ? (dlg->h_id+1) : next_id; GET_STR_VALUE(to_tag, values, 5, 1, 1); dlg->start_ts = VAL_INT(values+6); dlg->state = VAL_INT(values+7); if (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED) { active_dlgs_cnt++; } else if (dlg->state==DLG_STATE_EARLY) { early_dlgs_cnt++; } GET_STR_VALUE(cseq1, values, 9 , 1, 1); GET_STR_VALUE(cseq2, values, 10 , 1, 1); GET_STR_VALUE(rroute1, values, 11, 0, 0); GET_STR_VALUE(rroute2, values, 12, 0, 0); GET_STR_VALUE(contact1, values, 13, 0, 1); GET_STR_VALUE(contact2, values, 14, 0, 1); GET_STR_VALUE(mangled_fu, values, 23,0,1); GET_STR_VALUE(mangled_tu, values, 24,0,1); /* add the 2 legs */ /* TODO - store SDP */ if ( (dlg_add_leg_info( dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock,0,0,0)!=0) || (dlg_add_leg_info( dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock,&mangled_fu,&mangled_tu,0)!=0) ) { LM_ERR("dlg_set_leg_info failed\n"); /* destroy the dialog */ unref_dlg(dlg,1); continue; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* script variables */ if (!VAL_NULL(values+17)) { if (VAL_TYPE(values+17) == DB_BLOB) { read_dialog_vars( VAL_BLOB(values+17).s, VAL_BLOB(values+17).len, dlg); } else { LM_ERR("non-blob variables column - cannot store dialog variables\n"); } } /* profiles */ if (!VAL_NULL(values+18)) read_dialog_profiles( VAL_STR(values+18).s, strlen(VAL_STR(values+18).s), dlg, 0, 0); /* script flags */ if (!VAL_NULL(values+19)) { dlg->user_flags = VAL_INT(values+19); } /* module flags */ if (!VAL_NULL(values+25)) { dlg->mod_flags = VAL_INT(values+25); } /* top hiding */ dlg->flags = VAL_INT(values+22); if (dlg_db_mode==DB_MODE_SHUTDOWN) dlg->flags |= DLG_FLAG_NEW; /* calculcate timeout */ dlg->tl.timeout = (unsigned int)(VAL_INT(values+8)) + get_ticks(); if (dlg->tl.timeout<=(unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (0 != insert_dlg_timer( &(dlg->tl), (int)dlg->tl.timeout )) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /* destroy the dialog */ unref_dlg(dlg,1); continue; } /* reference the dialog as kept in the timer list */ ref_dlg(dlg,1); LM_DBG("current dialog timeout is %u\n", dlg->tl.timeout); dlg->lifetime = 0; dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+20)); dlg->legs[callee_idx(dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (0 != insert_ping_timer(dlg)) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { /* reference dialog as kept in ping timer list */ ref_dlg(dlg,1); } } if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg(dlg,1); } next_dialog: ; } /* any more data to be fetched ?*/ if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH)) { if (dialog_dbf.fetch_result( dialog_db_handle, &res,no_rows) < 0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(res); } else { nr_rows = 0; } }while (nr_rows>0); dialog_dbf.free_result(dialog_db_handle, res); if (found_ended_dlgs) remove_ended_dlgs_from_db(); return 0; error: dialog_dbf.free_result(dialog_db_handle, res); if (found_ended_dlgs) remove_ended_dlgs_from_db(); return -1; } static struct dlg_cell **dlg_del_holder=NULL; static int dlg_del_curr_no=0; static db_val_t *dlg_del_values=NULL; static db_key_t *dlg_del_keys=NULL; int dlg_timer_remove_from_db(struct dlg_cell *cell) { static db_ps_t my_ps = NULL; int i; /* here we are in the context of the dlg timer * dialog d_entry is locked and we also have an extra ref * that must be released when we attempt the delete */ if (dlg_del_holder == NULL) { LM_DBG("First time dialog del is attempted\n"); /* allocate all needed structures */ dlg_del_holder = pkg_malloc(dlg_bulk_del_no * sizeof(struct dlg_cell *)); if (!dlg_del_holder) { LM_ERR("No more pkg for dlg delete holders\n"); return -1; } memset(dlg_del_holder,0,dlg_bulk_del_no*sizeof(struct dlg_cell *)); dlg_del_values = pkg_malloc(dlg_bulk_del_no * sizeof(db_val_t)); if (!dlg_del_values) { LM_ERR("No more pkg for dlg delete values\n"); pkg_free(dlg_del_holder); return -1; } for (i=0;ih_entry << 32) | (cell->h_id); dlg_del_holder[dlg_del_curr_no]=cell; /* mark is as deleted so we don't care about it later * in the timer */ cell->flags |= DLG_FLAG_DB_DELETED; dlg_del_curr_no++; if (dlg_del_curr_no == dlg_bulk_del_no) { LM_DBG("triggering delete for %d dialogs\n",dlg_del_curr_no); CON_PS_REFERENCE(dialog_db_handle) = &my_ps; CON_USE_OR_OP(dialog_db_handle); if(dialog_dbf.delete(dialog_db_handle, dlg_del_keys, 0, dlg_del_values, dlg_bulk_del_no) < 0) LM_ERR("failed to delete bulk database information !!!\n"); /* from timer point of view, we are done with the dialogs */ for (i=0;ientries[cell->h_entry])); } dlg_del_curr_no = 0; } /* still not enough piled up dialogs - wait for more */ return 0; } int dlg_timer_flush_del(void) { struct dlg_cell *cell; int i; /* here we are in the context of the dlg timer * we also have an extra ref * that must be released when we attempt the delete */ if (dlg_del_curr_no > 0) { CON_USE_OR_OP(dialog_db_handle); if(dialog_dbf.delete(dialog_db_handle, dlg_del_keys, 0, dlg_del_values, dlg_del_curr_no) < 0) LM_ERR("failed to delete bulk database information !!!\n"); /* from timer point of view, we are done with the dialogs */ for (i=0;iflags); if (cell->flags & DLG_FLAG_NEW) return 0; if (use_dialog_table()!=0) return -1; VAL_TYPE(values) = DB_BIGINT; VAL_NULL(values) = 0; VAL_BIGINT(values) = ((long long)cell->h_entry << 32) | (cell->h_id); CON_PS_REFERENCE(dialog_db_handle) = &my_ps; if(dialog_dbf.delete(dialog_db_handle, match_keys, 0, values, 1) < 0) { LM_ERR("failed to delete database information\n"); return -1; } LM_DBG("callid was %.*s\n", cell->callid.len, cell->callid.s ); /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); return 0; } int update_dialog_timeout_info(struct dlg_cell * cell) { static db_ps_t my_ps_update = NULL; struct dlg_entry entry; db_val_t values[2]; db_key_t insert_keys[DIALOG_TABLE_TOTAL_COL_NO] = { &dlg_id_column, &timeout_column}; if(use_dialog_table()!=0) return -1; if (!(cell->flags & DLG_FLAG_CHANGED)) return 0; /* save only dialog's state and timeout */ VAL_TYPE(values) = DB_BIGINT; VAL_TYPE(values+1) = DB_INT; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_BIGINT_VALUE(values, (((long long)cell->h_entry << 32) | cell->h_id)); SET_INT_VALUE(values+1, (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+1), (values+1), 1, 1)) !=0){ LM_ERR("could not update database timeout info\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_CHANGED); dlg_unlock( d_table, &entry); return 0; error: dlg_unlock( d_table, &entry); return -1; } int update_dialog_dbinfo(struct dlg_cell * cell) { static db_ps_t my_ps_insert = NULL; static db_ps_t my_ps_update = NULL; static db_ps_t my_ps_update_vp = NULL; struct dlg_entry entry; db_val_t values[DIALOG_TABLE_TOTAL_COL_NO]; int callee_leg; db_key_t insert_keys[DIALOG_TABLE_TOTAL_COL_NO] = { &dlg_id_column, &call_id_column, &from_uri_column, &from_tag_column, &to_uri_column, &to_tag_column, &from_sock_column, &to_sock_column, &start_time_column, &mangled_fu_column, &mangled_tu_column, &state_column, &timeout_column, &from_cseq_column, &to_cseq_column, &from_ping_cseq_column, &to_ping_cseq_column,&flags_column, &vars_column, &profiles_column, &sflags_column, &mflags_column, &from_route_column, &to_route_column, &from_contact_column,&to_contact_column}; if(use_dialog_table()!=0) return -1; callee_leg= callee_idx(cell); if((cell->flags & DLG_FLAG_NEW) != 0){ /* save all the current dialogs information*/ VAL_TYPE(values) = DB_BIGINT; VAL_TYPE(values+8) = VAL_TYPE(values+11) = VAL_TYPE(values+12) = VAL_TYPE(values+15) =VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+20) = VAL_TYPE(values+21) = DB_INT; VAL_TYPE(values+1) = VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = VAL_TYPE(values+5) = VAL_TYPE(values+6) = VAL_TYPE(values+7) = VAL_TYPE(values+9) = VAL_TYPE(values+10) = VAL_TYPE(values+13) = VAL_TYPE(values+14) = VAL_TYPE(values+19) = VAL_TYPE(values+22) = VAL_TYPE(values+23) = VAL_TYPE(values+24) = VAL_TYPE(values+25) = DB_STR; VAL_TYPE(values+18) = DB_BLOB; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_BIGINT_VALUE(values, (((long long)cell->h_entry << 32) | cell->h_id)); /* to be later removed by timer */SET_STR_VALUE(values+1, cell->callid); SET_STR_VALUE(values+2, cell->from_uri); SET_STR_VALUE(values+3, cell->legs[DLG_CALLER_LEG].tag); SET_STR_VALUE(values+4, cell->to_uri); SET_STR_VALUE(values+5, cell->legs[callee_leg].tag); SET_STR_VALUE(values+6, cell->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (cell->legs[callee_leg].bind_addr) { SET_STR_VALUE(values+7, cell->legs[callee_leg].bind_addr->sock_str); } else { VAL_NULL(values+7) = 1; } SET_INT_VALUE(values+8, cell->start_ts); SET_STR_VALUE(values+9,cell->legs[callee_leg].from_uri); SET_STR_VALUE(values+10,cell->legs[callee_leg].to_uri); SET_INT_VALUE(values+11, cell->state); SET_INT_VALUE(values+12, (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+13, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+14, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+15,cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+16,cell->legs[callee_leg].last_gen_cseq); SET_INT_VALUE(values+17, cell->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); set_final_update_cols(values+18, cell, 0); SET_STR_VALUE(values+22, cell->legs[DLG_CALLER_LEG].route_set); SET_STR_VALUE(values+23, cell->legs[callee_leg].route_set); SET_STR_VALUE(values+24, cell->legs[DLG_CALLER_LEG].contact); SET_STR_VALUE(values+25, cell->legs[callee_leg].contact); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_insert; if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, DIALOG_TABLE_TOTAL_COL_NO)) !=0){ LM_ERR("could not add another dialog to db\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if((cell->flags & DLG_FLAG_CHANGED) != 0) { /* save only dialog's state and timeout */ VAL_TYPE(values) = DB_BIGINT; VAL_TYPE(values+11) = VAL_TYPE(values+12) = VAL_TYPE(values+15) = VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+20) = VAL_TYPE(values+21) = DB_INT; VAL_TYPE(values+13) = VAL_TYPE(values+14) = VAL_TYPE(values+19) = DB_STR; VAL_TYPE(values+18) = DB_BLOB; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_BIGINT_VALUE(values, (((long long)cell->h_entry << 32) | cell->h_id)); SET_INT_VALUE(values+11, cell->state); SET_INT_VALUE(values+12, (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+13, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+14, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+15,cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+16,cell->legs[callee_leg].last_gen_cseq); SET_INT_VALUE(values+17, cell->flags); set_final_update_cols(values+18, cell, 1); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+11), (values+11), 1, 11)) !=0){ LM_ERR("could not update database info\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if (cell->flags & DLG_FLAG_VP_CHANGED) { VAL_TYPE(values) = DB_BIGINT; VAL_TYPE(values+18) = DB_BLOB; VAL_TYPE(values+19) = DB_STR; VAL_TYPE(values+20) = DB_INT; VAL_TYPE(values+21) = DB_INT; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_BIGINT_VALUE(values, (((long long)cell->h_entry << 32) | cell->h_id)); set_final_update_cols(values+18, cell, 0); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update_vp; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+18), (values+18), 1, 4)) !=0){ LM_ERR("could not update database info\n"); goto error; } run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~DLG_FLAG_VP_CHANGED; } else { return 0; } dlg_unlock( d_table, &entry); return 0; error: dlg_unlock( d_table, &entry); return -1; } static inline unsigned int write_pair( char *b, str *name, str *name_suffix, str *val) { int i,j; for( i=0,j=0 ; ilen ; i++) { if (name->s[i]=='|' || name->s[i]=='#' || name->s[i]=='\\') b[j++] = '\\'; b[j++] = name->s[i]; } if (name_suffix) { memcpy(b+j,name_suffix->s,name_suffix->len); j+=name_suffix->len; } b[j++] = '#'; for( i=0 ; val && ilen ; i++) { if (val->s[i]=='|' || val->s[i]=='#' || val->s[i]=='\\') b[j++] = '\\'; b[j++] = val->s[i]; } b[j++] = '|'; return j; } str* write_dialog_vars( struct dlg_val *vars) { static str o = {NULL,0}; static int o_l=0; struct dlg_val *v; unsigned int l,i; char *p; /* compute the required len */ for ( v=vars,l=0 ; v ; v=v->next) { l += v->name.len + 1 + v->val.len + 1; for( i=0 ; iname.len ; i++ ) if (v->name.s[i]=='|' || v->name.s[i]=='#' || v->name.s[i]=='\\') l++; for( i=0 ; ival.len ; i++ ) if (v->val.s[i]=='|' || v->val.s[i]=='#' || v->val.s[i]=='\\') l++; } /* allocate the string to be stored */ if ( o.s==NULL || o_lnext) { p += write_pair( p, &v->name,NULL, &v->val); } if (o.len!=p-o.s) { LM_CRIT("BUG - buffer overflow allocated %d, written %d\n", o.len,(int)(p-o.s)); return NULL; } LM_DBG("var string is <%.*s>(%d)\n", l,o.s,l); return &o; } /* needs to be run under the dialog lock , since it iterates on the profile links, which might get * deallocated if the dialog ends */ str* write_dialog_profiles( struct dlg_profile_link *links) { static str o = {NULL,0},cached_marker={"/s",2}, bin_marker={"/b", 2}; static int o_l = 0; struct dlg_profile_link *link; unsigned int l,i; char *p; /* compute the required len */ for ( link=links,l=0 ; link ; link=link->next) { l += link->profile->name.len + 1 + link->value.len + 1; for( i=0 ; iprofile->name.len ; i++ ) if (link->profile->name.s[i]=='|' || link->profile->name.s[i]=='#' || link->profile->name.s[i]=='\\') l++; for( i=0 ; ivalue.len ; i++ ) if (link->value.s[i]=='|' || link->value.s[i]=='#' || link->value.s[i]=='\\') l++; if (link->profile->repl_type!=REPL_NONE/*==(CACHEDB||PROTOBIN)*/) l+=cached_marker.len; /* same length for both */ } /* allocate the string to be stored */ if ( o.s==NULL || o_lnext) { if (link->profile->repl_type == REPL_CACHEDB) p += write_pair( p, &link->profile->name, &cached_marker, &link->value); else if (link->profile->repl_type == REPL_PROTOBIN) p += write_pair( p, &link->profile->name, &bin_marker, &link->value); else p += write_pair( p, &link->profile->name, NULL, &link->value); } if (o.len!=p-o.s) { LM_CRIT("BUG - buffer overflow allocated %d, written %d\n", o.len,(int)(p-o.s)); return NULL; } LM_DBG("profile string is <%.*s>(%d)\n", l,o.s,l); return &o; } static inline void set_final_update_cols(db_val_t *vals, struct dlg_cell *cell, int on_shutdown) { str *s; LM_DBG("DLG vals and profiles should %s[%x:%d]\n", (db_flush_vp && (cell->flags & DLG_FLAG_VP_CHANGED)) ? "be saved" : "not be saved", cell->flags, db_flush_vp); if (on_shutdown || (db_flush_vp && (cell->flags & DLG_FLAG_VP_CHANGED))) { if (cell->vals==NULL) { VAL_NULL(vals) = 1; } else { s = write_dialog_vars( cell->vals ); if (s==NULL) { VAL_NULL(vals) = 1; } else { SET_STR_VALUE(vals, *s); } } if (cell->profile_links==NULL) { VAL_NULL(vals+1) = 1; } else { s = write_dialog_profiles( cell->profile_links ); if (s==NULL) { VAL_NULL(vals+1) = 1; } else { SET_STR_VALUE(vals+1, *s); } } SET_INT_VALUE(vals+2, cell->user_flags); SET_INT_VALUE(vals+3, cell->mod_flags); } else { VAL_NULL(vals) = 1; VAL_NULL(vals+1) = 1; SET_INT_VALUE(vals+2, 0); SET_INT_VALUE(vals+3, 0); } } void dialog_update_db(unsigned int ticks, void * param) { static db_ps_t my_ps_update = NULL; static db_ps_t my_ps_insert = NULL; static db_ps_t my_ps_update_vp = NULL; int index; db_val_t values[DIALOG_TABLE_TOTAL_COL_NO]; struct dlg_entry *entry; struct dlg_cell * cell,*next_cell; unsigned char on_shutdown; int callee_leg,ins_done=0; static query_list_t *ins_list = NULL; db_key_t insert_keys[DIALOG_TABLE_TOTAL_COL_NO] = { &dlg_id_column, &call_id_column, &from_uri_column, &from_tag_column, &to_uri_column, &to_tag_column, &from_sock_column, &to_sock_column, &start_time_column, &from_route_column, &to_route_column, &from_contact_column, &to_contact_column, &mangled_fu_column, &mangled_tu_column, /*update chunk */ &state_column, &timeout_column, &from_cseq_column, &to_cseq_column, &from_ping_cseq_column, &to_ping_cseq_column, &vars_column, &profiles_column, &sflags_column, &mflags_column, &flags_column}; if (dialog_db_handle==0 || use_dialog_table()!=0) return; on_shutdown = (ticks==0); /*save the current dialogs information*/ VAL_TYPE(values) = DB_BIGINT; VAL_TYPE(values+8) = VAL_TYPE(values+15) = VAL_TYPE(values+16) = VAL_TYPE(values+19) = VAL_TYPE(values+20) = VAL_TYPE(values+23) = VAL_TYPE(values+24)= VAL_TYPE(values+25) = DB_INT; VAL_TYPE(values+1) = VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = VAL_TYPE(values+5) = VAL_TYPE(values+6) = VAL_TYPE(values+7) = VAL_TYPE(values+9) = VAL_TYPE(values+10) = VAL_TYPE(values+11) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+14) = VAL_TYPE(values+17) = VAL_TYPE(values+18) = VAL_TYPE(values+22) = DB_STR; VAL_TYPE(values+21) = DB_BLOB; for(index = 0; index< d_table->size; index++){ /* lock the whole entry */ entry = &((d_table->entries)[index]); dlg_lock( d_table, entry); for(cell = entry->first; cell != NULL;){ callee_leg = callee_idx(cell); if( (cell->flags & DLG_FLAG_NEW) != 0 ) { if ( cell->state == DLG_STATE_DELETED ) { if (!(cell->flags & DLG_FLAG_DB_DELETED)) { /* first time we see this dialog */ /* save pointer to next dialog */ next_cell=cell->next; /* mark it as deleted so as we don't deal with it later */ cell->flags |= DLG_FLAG_DB_DELETED; /* timer is done with this dialog */ unref_dlg_unsafe(cell,1,entry); cell=next_cell; continue; } /* timer was done with the dialog but somebody else * is still holding the ref, just skip over it */ cell=cell->next; continue; } LM_DBG("inserting new dialog %p\n",cell); SET_BIGINT_VALUE(values, (((long long)cell->h_entry << 32) | cell->h_id)); SET_STR_VALUE(values+1, cell->callid); SET_STR_VALUE(values+2, cell->from_uri); SET_STR_VALUE(values+3, cell->legs[DLG_CALLER_LEG].tag); SET_STR_VALUE(values+4, cell->to_uri); SET_STR_VALUE(values+5, cell->legs[callee_leg].tag); SET_STR_VALUE(values+6, cell->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (cell->legs[callee_leg].bind_addr) { SET_STR_VALUE(values+7, cell->legs[callee_leg].bind_addr->sock_str); } else { VAL_NULL(values+7) = 1; } SET_INT_VALUE(values+8, cell->start_ts); SET_STR_VALUE(values+9, cell->legs[DLG_CALLER_LEG].route_set); SET_STR_VALUE(values+10, cell->legs[callee_leg].route_set); SET_STR_VALUE(values+11, cell->legs[DLG_CALLER_LEG].contact); SET_STR_VALUE(values+12, cell->legs[callee_leg].contact); SET_STR_VALUE(values+13,cell->legs[callee_leg].from_uri); SET_STR_VALUE(values+14,cell->legs[callee_leg].to_uri); SET_INT_VALUE(values+15, cell->state); SET_INT_VALUE(values+16, (unsigned int)((unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+17, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+18, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+19, cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+20, cell->legs[callee_leg].last_gen_cseq); set_final_update_cols(values+21, cell, (on_shutdown) || (cell->flags&DLG_FLAG_CHANGED) ); SET_INT_VALUE(values+25, cell->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_insert; if (con_set_inslist(&dialog_dbf,dialog_db_handle, &ins_list,insert_keys,DIALOG_TABLE_TOTAL_COL_NO) < 0 ) CON_RESET_INSLIST(dialog_db_handle); if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, DIALOG_TABLE_TOTAL_COL_NO)) !=0){ LM_ERR("could not add another dialog to db\n"); cell = cell->next; continue; } if (ins_done==0) ins_done=1; /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_NEW |DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if (cell->state == DLG_STATE_DELETED && !(cell->flags & DLG_FLAG_DB_DELETED)) { /* save pointer to next dialog * delete might swipe cell from under our feet */ next_cell=cell->next; dlg_timer_remove_from_db(cell); cell=next_cell; continue; } else if ( (cell->flags & DLG_FLAG_CHANGED)!=0 || on_shutdown ){ LM_DBG("updating existing dialog %p\n",cell); SET_BIGINT_VALUE(values, (((long long)cell->h_entry << 32) | cell->h_id)); SET_INT_VALUE(values+15, cell->state); SET_INT_VALUE(values+16, (unsigned int)((unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+17, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+18, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+19, cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+20, cell->legs[callee_leg].last_gen_cseq); set_final_update_cols(values+21, cell, 1); SET_INT_VALUE(values+25, cell->flags); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+15), (values+15), 1, 11)) !=0) { LM_ERR("could not update database info\n"); cell = cell->next; continue; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if (db_flush_vp && (cell->flags & DLG_FLAG_VP_CHANGED)) { SET_BIGINT_VALUE(values, (((long long)cell->h_entry << 32) | cell->h_id)); set_final_update_cols(values+21, cell, 0); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update_vp; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+21), (values+21), 1, 4)) !=0) { LM_ERR("could not update database info\n"); cell = cell->next; continue; } run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~DLG_FLAG_VP_CHANGED; } cell = cell->next; } dlg_unlock( d_table, entry); } if (ins_done) { LM_DBG("dlg timer attempting to flush rows to DB\n"); /* flush everything to DB * so that next-time timer fires * we are sure that DB updates will be successful */ if (ql_flush_rows(&dialog_dbf,dialog_db_handle,ins_list) < 0) LM_ERR("failed to flush rows to DB\n"); } dlg_timer_flush_del(); return; } static int sync_dlg_db_mem(void) { db_res_t * res; db_val_t * values; db_row_t * rows; struct dlg_entry *d_entry; struct dlg_cell *it,*known_dlg,*dlg=NULL; int i, nr_rows,callee_leg_idx,next_id,db_timeout; int no_rows = 10; unsigned int db_caller_cseq = 0, db_callee_cseq = 0; unsigned int dlg_caller_cseq = 0, dlg_callee_cseq = 0; struct socket_info *caller_sock,*callee_sock; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; int hash_entry,hash_id; res = 0; if((nr_rows = select_entire_dialog_table(&res,&no_rows)) < 0) goto error; nr_rows = RES_ROW_N(res); do { LM_DBG("loading information from database for %i dialogs\n", nr_rows); rows = RES_ROWS(res); /* for every row---dialog */ for(i=0; i skipping\n", dlg_id_column.len, dlg_id_column.s); continue; } hash_entry = (int)(VAL_BIGINT(values) >> 32); hash_id = (int)(VAL_BIGINT(values) & 0x00000000ffffffff); if (VAL_NULL(values+6) || VAL_NULL(values+7)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", start_time_column.len, start_time_column.s, state_column.len, state_column.s); continue; } if ( VAL_INT(values+7) == DLG_STATE_DELETED ) { LM_DBG("dialog already terminated -> skipping\n"); continue; } /*restore the dialog info*/ GET_STR_VALUE(callid, values, 1, 1, 0); GET_STR_VALUE(from_tag, values, 3, 1, 0); GET_STR_VALUE(to_tag, values, 5, 1, 1); /* TODO - check about hash resize ? maybe hash was lowered & we overflow the hash */ known_dlg = 0; d_entry = &(d_table->entries[hash_entry]); /* lock the whole entry */ dlg_lock( d_table, d_entry); for (it=d_entry->first;it;it=it->next) if (it->callid.len == callid.len && it->legs[DLG_CALLER_LEG].tag.len == from_tag.len && memcmp(it->callid.s,callid.s,callid.len)==0 && memcmp(it->legs[DLG_CALLER_LEG].tag.s,from_tag.s,from_tag.len)==0) { /* callid & ftag match */ callee_leg_idx = callee_idx(it); if (it->legs[callee_leg_idx].tag.len == to_tag.len && memcmp(it->legs[callee_leg_idx].tag.s,to_tag.s,to_tag.len)==0) { /* full dlg match */ known_dlg = it; break; } } if (known_dlg == 0) { /* we can safely unlock here */ dlg_unlock( d_table, d_entry); LM_DBG("First seen dialog - load all stuff - callid = [%.*s]\n",callid.len,callid.s); GET_STR_VALUE(from_uri, values, 2, 1, 0); GET_STR_VALUE(to_uri, values, 4, 1, 0); caller_sock = create_socket_info(values, 15); callee_sock = create_socket_info(values, 16); if (caller_sock == NULL || callee_sock == NULL) { LM_ERR("Dialog in DB doesn't match any listening sockets"); continue; } /* first time we see this dialog - build it from scratch */ if((dlg=build_new_dlg(&callid, &from_uri, &to_uri, &from_tag))==0){ LM_ERR("failed to build new dialog\n"); goto error; } if(dlg->h_entry != hash_entry){ LM_ERR("inconsistent hash data in the dialog database: " "you may have restarted opensips using a different " "hash_size: please erase %.*s database and restart\n", dialog_table_name.len, dialog_table_name.s); shm_free(dlg); goto error; } /*link the dialog*/ link_dlg(dlg, 0); dlg->h_id = hash_id; next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id <= dlg->h_id) ? (dlg->h_id+1) : next_id; dlg->start_ts = VAL_INT(values+6); dlg->state = VAL_INT(values+7); if (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED) { if_update_stat(dlg_enable_stats, active_dlgs, 1); } else if (dlg->state==DLG_STATE_EARLY) { if_update_stat(dlg_enable_stats, early_dlgs, 1); } GET_STR_VALUE(cseq1, values, 9 , 1, 1); GET_STR_VALUE(cseq2, values, 10 , 1, 1); GET_STR_VALUE(rroute1, values, 11, 0, 0); GET_STR_VALUE(rroute2, values, 12, 0, 0); GET_STR_VALUE(contact1, values, 13, 0, 1); GET_STR_VALUE(contact2, values, 14, 0, 1); GET_STR_VALUE(mangled_fu, values, 23,0,1); GET_STR_VALUE(mangled_tu, values, 24,0,1); /* add the 2 legs */ /* TODO SDP here */ if ( (dlg_add_leg_info( dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock,0,0,0)!=0) || (dlg_add_leg_info( dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock,&mangled_fu,&mangled_tu,0)!=0) ) { LM_ERR("dlg_set_leg_info failed\n"); /* destroy the dialog */ unref_dlg(dlg,1); continue; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* script variables */ if (!VAL_NULL(values+17)) { if (VAL_TYPE(values+17) == DB_BLOB) { read_dialog_vars( VAL_BLOB(values+17).s, VAL_BLOB(values+17).len, dlg); } else { LM_ERR("non-blob variables column - cannot store dialog variables\n"); } } /* profiles */ if (!VAL_NULL(values+18)) read_dialog_profiles( VAL_STR(values+18).s, strlen(VAL_STR(values+18).s), dlg, 0, 0); /* script flags */ if (!VAL_NULL(values+19)) { dlg->user_flags = VAL_INT(values+19); } /* module flags */ if (!VAL_NULL(values+25)) { dlg->mod_flags = VAL_INT(values+25); } /* top hiding */ dlg->flags = VAL_INT(values+22); if (dlg_db_mode==DB_MODE_SHUTDOWN) dlg->flags |= DLG_FLAG_NEW; /* calculcate timeout */ dlg->tl.timeout = (unsigned int)(VAL_INT(values+8)) + get_ticks(); if (dlg->tl.timeout<=(unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (0 != insert_dlg_timer( &(dlg->tl), (int)dlg->tl.timeout )) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /* destroy the dialog */ unref_dlg(dlg,1); continue; } /* reference the dialog as kept in the timer list */ ref_dlg(dlg,1); LM_DBG("current dialog timeout is %u\n", dlg->tl.timeout); dlg->lifetime = 0; dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+20)); dlg->legs[callee_idx(dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (0 != insert_ping_timer(dlg)) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { /* reference dialog as kept in ping timer list */ ref_dlg(dlg,1); } } if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg(dlg,1); } run_load_callback_per_dlg(dlg); } else { /* we already saw this dialog before * check which is the newer version */ if (known_dlg->state > VAL_INT(values+7)) { LM_DBG("mem has a newer state - ignore \n"); /* we know a newer version compared to the DB * ignore it */ dlg_unlock( d_table, d_entry); goto next_dialog; } else if (known_dlg->state == VAL_INT(values+7)) { LM_DBG("mem has same state as DB \n"); /* same state :-( no way to tell which is newer */ /* play nice and store longest timeout, although not always correct*/ db_timeout = (unsigned int)(VAL_INT(values+8)) + get_ticks(); if (db_timeout<=(unsigned int)time(0)) db_timeout = 0; else db_timeout -= (unsigned int)time(0); if (known_dlg->tl.timeout < db_timeout) known_dlg->tl.timeout = db_timeout; /* check with is newer cseq for caller leg */ if (!VAL_NULL(values+9)) { cseq1.s = VAL_STR(values+9).s; cseq1.len = strlen(cseq1.s); str2int(&cseq1,&db_caller_cseq); str2int(&known_dlg->legs[DLG_CALLER_LEG].r_cseq,&dlg_caller_cseq); /* Is DB cseq newer ? */ if (db_caller_cseq > dlg_caller_cseq) { if (known_dlg->legs[DLG_CALLER_LEG].r_cseq.len < cseq1.len) { known_dlg->legs[DLG_CALLER_LEG].r_cseq.s = shm_realloc(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.len); if (!known_dlg->legs[DLG_CALLER_LEG].r_cseq.s) { LM_ERR("no more shm\n"); dlg_unlock( d_table, d_entry); goto next_dialog; } } memcpy(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.s,cseq1.len); known_dlg->legs[DLG_CALLER_LEG].r_cseq.len = cseq1.len; } } else { /* DB has a null cseq - just keep * what we have so far */ ; } /* check with is newer cseq for caller leg */ if (!VAL_NULL(values+10)) { cseq2.s = VAL_STR(values+10).s; cseq2.len = strlen(cseq2.s); callee_leg_idx = callee_idx(known_dlg); str2int(&cseq2,&db_callee_cseq); str2int(&known_dlg->legs[callee_leg_idx].r_cseq,&dlg_callee_cseq); /* Is DB cseq newer ? */ if (db_callee_cseq > dlg_callee_cseq) { if (known_dlg->legs[callee_leg_idx].r_cseq.len < cseq2.len) { known_dlg->legs[callee_leg_idx].r_cseq.s = shm_realloc(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.len); if (!known_dlg->legs[callee_leg_idx].r_cseq.s) { LM_ERR("no more shm\n"); dlg_unlock( d_table, d_entry); goto next_dialog; } } memcpy(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.s,cseq2.len); known_dlg->legs[callee_leg_idx].r_cseq.len = cseq2.len; } } else { /* DB has a null cseq - just keep * what we have so far */ ; } /* update ping cseqs, whichever is newer */ if (known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq < (unsigned int)(VAL_INT(values+20))) known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+20)); if (known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq < (unsigned int)(VAL_INT(values+21))) known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); /* update script variables * if already found, delete the old ones * and replace with new one */ if (!VAL_NULL(values+17)) { if (VAL_TYPE(values+17) == DB_BLOB) { read_dialog_vars( VAL_BLOB(values+17).s, VAL_BLOB(values+17).len, known_dlg); } else { LM_ERR("non-blob variables column - cannot store dialog variables\n"); } } /* skip flags - keep what we have - anyway can't tell which is new */ /* profiles - do not insert into a profile * is dlg is already in that profile*/ if (!VAL_NULL(values+18)) read_dialog_profiles( VAL_STR(values+18).s, strlen(VAL_STR(values+18).s), known_dlg, 1, 0); dlg_unlock( d_table, d_entry); } else { /* DB has newer state, just update fields from DB */ LM_DBG("DB has newer state \n"); /* set new state */ known_dlg->state = VAL_INT(values+7); /* update timeout */ known_dlg->tl.timeout = (unsigned int)(VAL_INT(values+8)) + get_ticks(); if (known_dlg->tl.timeout<=(unsigned int)time(0)) known_dlg->tl.timeout = 0; else known_dlg->tl.timeout -= (unsigned int)time(0); /* update cseqs */ if (!VAL_NULL(values+9)) { cseq1.s = VAL_STR(values+9).s; cseq1.len = strlen(cseq1.s); if (known_dlg->legs[DLG_CALLER_LEG].r_cseq.len < cseq1.len) { known_dlg->legs[DLG_CALLER_LEG].r_cseq.s = shm_realloc(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.len); if (!known_dlg->legs[DLG_CALLER_LEG].r_cseq.s) { LM_ERR("no more shm\n"); dlg_unlock( d_table, d_entry); goto next_dialog; } } memcpy(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.s,cseq1.len); known_dlg->legs[DLG_CALLER_LEG].r_cseq.len = cseq1.len; } if (!VAL_NULL(values+10)) { cseq2.s = VAL_STR(values+10).s; cseq2.len = strlen(cseq1.s); callee_leg_idx = callee_idx(known_dlg); if (known_dlg->legs[callee_leg_idx].r_cseq.len < cseq2.len) { known_dlg->legs[callee_leg_idx].r_cseq.s = shm_realloc(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.len); if (!known_dlg->legs[callee_leg_idx].r_cseq.s) { LM_ERR("no more shm\n"); dlg_unlock( d_table, d_entry); goto next_dialog; } } memcpy(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.s,cseq2.len); known_dlg->legs[callee_leg_idx].r_cseq.len = cseq2.len; } /* update ping cseqs */ known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq = (unsigned int)(VAL_INT(values+20)); known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq = (unsigned int)(VAL_INT(values+21)); /* update flags */ known_dlg->flags = VAL_INT(values+22); if (dlg_db_mode==DB_MODE_SHUTDOWN) known_dlg->flags |= DLG_FLAG_NEW; /* update script variables * if already found, delete the old one * and replace with new one */ if (!VAL_NULL(values+17)) { if (VAL_TYPE(values+17) == DB_BLOB) { read_dialog_vars( VAL_BLOB(values+17).s, VAL_BLOB(values+17).len, known_dlg); } else { LM_ERR("non-blob variables column - cannot store dialog variables\n"); } } /* profiles - do not insert into a profile * is dlg is already in that profile*/ if (!VAL_NULL(values+18)) read_dialog_profiles( VAL_STR(values+18).s, strlen(VAL_STR(values+18).s), known_dlg, 1, 0); dlg_unlock( d_table, d_entry); } } next_dialog: ; } /* any more data to be fetched ?*/ if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH)) { if (dialog_dbf.fetch_result( dialog_db_handle, &res,no_rows) < 0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(res); } else { nr_rows = 0; } }while (nr_rows>0); dialog_dbf.free_result(dialog_db_handle, res); return 0; error: dialog_dbf.free_result(dialog_db_handle, res); return -1; } /* * truncates and restores the dialog table with CONFIRMED dialogs from memory */ static int restore_dlg_db(void) { int i, callee_leg, ins_done = 0; struct dlg_entry *e; struct dlg_cell *cell; static query_list_t *ins_list = NULL; static db_ps_t my_ps_insert = NULL; db_val_t values[DIALOG_TABLE_TOTAL_COL_NO]; db_key_t insert_keys[DIALOG_TABLE_TOTAL_COL_NO] = { &dlg_id_column, &call_id_column, &from_uri_column, &from_tag_column, &to_uri_column, &to_tag_column, &from_sock_column, &to_sock_column, &start_time_column, &from_route_column, &to_route_column, &from_contact_column, &to_contact_column, &mangled_fu_column, &mangled_tu_column, &state_column, &timeout_column, &from_cseq_column, &to_cseq_column, &from_ping_cseq_column, &to_ping_cseq_column, &vars_column, &profiles_column, &sflags_column, &mflags_column, &flags_column}; VAL_TYPE(values) = DB_BIGINT; VAL_TYPE(values+8) = VAL_TYPE(values+15) = VAL_TYPE(values+16) = VAL_TYPE(values+19) = VAL_TYPE(values+20) = VAL_TYPE(values+23) = VAL_TYPE(values+24)= VAL_TYPE(values+25) = DB_INT; VAL_TYPE(values+1) = VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = VAL_TYPE(values+5) = VAL_TYPE(values+6) = VAL_TYPE(values+7) = VAL_TYPE(values+9) = VAL_TYPE(values+10) = VAL_TYPE(values+11) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+14) = VAL_TYPE(values+17) = VAL_TYPE(values+18) = VAL_TYPE(values+22) = DB_STR; VAL_TYPE(values+21) = DB_BLOB; if (remove_all_dialogs_from_db() != 0) { LM_ERR("Failed to truncate dialog table!\n"); return -1; } for (i = 0; i < d_table->size; i++) { e = d_table->entries + i; dlg_lock(d_table, e); for (cell = e->first; cell; cell = cell->next) { if (cell->state != DLG_STATE_CONFIRMED && cell->state != DLG_STATE_CONFIRMED_NA) continue; callee_leg = callee_idx(cell); SET_BIGINT_VALUE(values, (((long long)cell->h_entry << 32) | cell->h_id)); SET_STR_VALUE(values+1, cell->callid); SET_STR_VALUE(values+2, cell->from_uri); SET_STR_VALUE(values+3, cell->legs[DLG_CALLER_LEG].tag); SET_STR_VALUE(values+4, cell->to_uri); SET_STR_VALUE(values+5, cell->legs[callee_leg].tag); SET_STR_VALUE(values+6, cell->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (cell->legs[callee_leg].bind_addr) { SET_STR_VALUE(values+7, cell->legs[callee_leg].bind_addr->sock_str); } else { VAL_NULL(values+7) = 1; } SET_INT_VALUE(values+8, cell->start_ts); SET_STR_VALUE(values+9, cell->legs[DLG_CALLER_LEG].route_set); SET_STR_VALUE(values+10, cell->legs[callee_leg].route_set); SET_STR_VALUE(values+11, cell->legs[DLG_CALLER_LEG].contact); SET_STR_VALUE(values+12, cell->legs[callee_leg].contact); SET_STR_VALUE(values+13,cell->legs[callee_leg].from_uri); SET_STR_VALUE(values+14,cell->legs[callee_leg].to_uri); SET_INT_VALUE(values+15, cell->state); SET_INT_VALUE(values+16, (unsigned int)((unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+17, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+18, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+19, cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+20, cell->legs[callee_leg].last_gen_cseq); set_final_update_cols(values+21, cell, 1); SET_INT_VALUE(values+25, cell->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED| DLG_FLAG_VP_CHANGED)); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_insert; if (con_set_inslist(&dialog_dbf,dialog_db_handle, &ins_list,insert_keys,DIALOG_TABLE_TOTAL_COL_NO) < 0 ) CON_RESET_INSLIST(dialog_db_handle); if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, DIALOG_TABLE_TOTAL_COL_NO)) !=0){ LM_ERR("could not add another dialog to db\n"); continue; } if (ins_done == 0) ins_done = 1; /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_NEW |DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } dlg_unlock(d_table, e); } if (ins_done) { LM_DBG("attempting to flush rows to DB\n"); /* flush everything to DB * so that next-time timer fires * we are sure that DB updates will be successful */ if (ql_flush_rows(&dialog_dbf,dialog_db_handle,ins_list) < 0) LM_ERR("failed to flush rows to DB\n"); } return 0; } struct mi_root* mi_sync_db_dlg(struct mi_root *cmd, void *param) { if (dlg_db_mode == 0) return init_mi_tree( 400, MI_SSTR("Cannot sync in no-db mode")); if (sync_dlg_db_mem() < 0) return init_mi_tree( 400, MI_SSTR("Sync mem with DB failed")); else return init_mi_tree( 200, MI_SSTR(MI_OK)); } struct mi_root* mi_restore_dlg_db(struct mi_root *cmd, void *param) { if (dlg_db_mode == 0) return init_mi_tree( 400, MI_SSTR("Cannot restore db in no-db mode!")); if (restore_dlg_db() < 0) return init_mi_tree( 400, MI_SSTR("Restore dlg DB failed!")); else return init_mi_tree( 200, MI_SSTR(MI_OK)); } opensips-2.2.2/modules/dialog/dlg_db_handler.h000066400000000000000000000076361300170765700213670ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-05-10 initial version (ancuta) */ #ifndef _DLG_DB_HANDLER_H_ #define _DLG_DB_HANDLER_H_ #include "../../str.h" #include "../../db/db.h" #define DLG_ID_COL "dlg_id" #define CALL_ID_COL "callid" #define FROM_URI_COL "from_uri" #define FROM_TAG_COL "from_tag" #define TO_URI_COL "to_uri" #define TO_TAG_COL "to_tag" #define HASH_ID_COL "hash_id" #define HASH_ENTRY_COL "hash_entry" #define USER_FLAGS_COL "user_flags" #define STATE_COL "state" #define START_TIME_COL "start_time" #define TIMEOUT_COL "timeout" #define TO_CSEQ_COL "callee_cseq" #define FROM_CSEQ_COL "caller_cseq" #define FROM_PING_CSEQ_COL "caller_ping_cseq" #define TO_PING_CSEQ_COL "callee_ping_cseq" #define TO_ROUTE_COL "callee_route_set" #define FROM_ROUTE_COL "caller_route_set" #define TO_CONTACT_COL "callee_contact" #define FROM_CONTACT_COL "caller_contact" #define FROM_SOCK_COL "caller_sock" #define TO_SOCK_COL "callee_sock" #define MANGLED_FU_COL "mangled_from_uri" #define MANGLED_TU_COL "mangled_to_uri" #define VARS_COL "vars" #define PROFILES_COL "profiles" #define SFLAGS_COL "script_flags" #define MFLAGS_COL "module_flags" #define FLAGS_COL "flags" #define DIALOG_TABLE_NAME "dialog" #define DLG_TABLE_VERSION 10 /*every minute the dialogs' information will be refreshed*/ #define DB_DEFAULT_UPDATE_PERIOD 60 #define DB_MODE_NONE 0 #define DB_MODE_REALTIME 1 #define DB_MODE_DELAYED 2 #define DB_MODE_SHUTDOWN 3 #define DIALOG_TABLE_TOTAL_COL_NO 26 extern str dlg_id_column; extern str call_id_column; extern str from_uri_column; extern str from_tag_column; extern str to_uri_column; extern str to_tag_column; extern str state_column; extern str start_time_column; extern str timeout_column; extern str to_cseq_column; extern str from_cseq_column; extern str to_ping_cseq_column; extern str from_ping_cseq_column; extern str to_route_column; extern str from_route_column; extern str to_contact_column; extern str from_contact_column; extern str to_sock_column; extern str from_sock_column; extern str profiles_column; extern str vars_column; extern str sflags_column; extern str mflags_column; extern str flags_column; extern str th_column; extern str dialog_table_name; extern int dlg_db_mode; extern int db_flush_vp; #define should_remove_dlg_db() (dlg_db_mode==DB_MODE_REALTIME) int init_dlg_db(const str *db_url, int dlg_hash_size, int db_update_period); int dlg_connect_db(const str *db_url); void destroy_dlg_db(); int remove_dialog_from_db(struct dlg_cell * cell); int update_dialog_dbinfo(struct dlg_cell * cell); int update_dialog_timeout_info(struct dlg_cell * cell); void dialog_update_db(unsigned int ticks, void * param); void read_dialog_vars(char *b, int l, struct dlg_cell *dlg); void read_dialog_profiles(char *b, int l, struct dlg_cell *dlg, int double_check, char is_replicated); str* write_dialog_vars(struct dlg_val *vars); str* write_dialog_profiles(struct dlg_profile_link *links); struct mi_root* mi_sync_db_dlg(struct mi_root *cmd, void *param); struct mi_root* mi_restore_dlg_db(struct mi_root *cmd, void *param); #endif opensips-2.2.2/modules/dialog/dlg_handlers.c000066400000000000000000001743311300170765700210750ustar00rootroot00000000000000/* * Copyright (C) 2009-2014 OpenSIPS Solutions * Copyright (C) 2006-2009 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-14 initial version (bogdan) * 2006-11-28 Added support for tracking the number of early dialogs, and the * number of failed dialogs. This involved updates to dlg_onreply() * (Jeffrey Magder - SOMA Networks) * 2007-03-06 syncronized state machine added for dialog state. New tranzition * design based on events; removed num_1xx and num_2xx (bogdan) * 2007-04-30 added dialog matching without DID (dialog ID), but based only * on RFC3261 elements - based on an original patch submitted * by Michel Bensoussan (bogdan) * 2007-05-17 new feature: saving dialog info into a database if * realtime update is set(ancuta) * 2007-07-06 support for saving additional dialog info : cseq, contact, * route_set and socket_info for both caller and callee (ancuta) * 2007-07-10 Optimized dlg_match_mode 2 (DID_NONE), it now employs a proper * hash table lookup and isn't dependant on the is_direction * function (which requires an RR param like dlg_match_mode 0 * anyways.. ;) ; based on a patch from * Tavis Paquette * and Peter Baer (bogdan) * 2008-04-04 added direction reporting in dlg callbacks (bogdan) * 2009-09-09 support for early dialogs added; proper handling of cseq * while PRACK is used (bogdan) */ #include #include #include "../../pvar.h" #include "../../timer.h" #include "../../statistics.h" #include "../../data_lump.h" #include "../../parser/parse_to.h" #include "../../parser/parse_cseq.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_rr.h" #include "../../parser/parse_cseq.h" #include "../../parser/parse_hname2.h" #include "../../parser/parser_f.h" #include "../tm/tm_load.h" #include "../rr/api.h" #include "dlg_hash.h" #include "dlg_timer.h" #include "dlg_cb.h" #include "dlg_handlers.h" #include "dlg_db_handler.h" #include "dlg_profile.h" #include "dlg_req_within.h" #include "dlg_replication.h" extern str rr_param; static int default_timeout; static int shutdown_done = 0; extern int seq_match_mode; extern struct rr_binds d_rrb; /* statistic variables */ extern stat_var *early_dlgs; extern stat_var *processed_dlgs; extern stat_var *expired_dlgs; extern stat_var *failed_dlgs; int ctx_lastdstleg_idx = -1; int ctx_timeout_idx = -1; void init_dlg_handlers(int default_timeout_p) { default_timeout = default_timeout_p; } void destroy_dlg_handlers(void) { shutdown_done = 1; } static inline int add_dlg_rr_param(struct sip_msg *req, unsigned int entry, unsigned int id) { static char buf[RR_DLG_PARAM_SIZE]; str s; int n; char *p; s.s = p = buf; *(p++) = ';'; memcpy(p, rr_param.s, rr_param.len); p += rr_param.len; *(p++) = '='; n = RR_DLG_PARAM_SIZE - (p-buf); if (int2reverse_hex( &p, &n, entry)==-1) return -1; *(p++) = DLG_SEPARATOR; n = RR_DLG_PARAM_SIZE - (p-buf); if (int2reverse_hex( &p, &n, id)==-1) return -1; s.len = p-buf; if (d_rrb.add_rr_param( req, &s)<0) { LM_ERR("failed to add rr param\n"); return -1; } return 0; } static inline void get_routing_info(struct sip_msg *msg, int is_req, unsigned int *skip_rrs, str *contact, str *rr_set) { /* extract the contact address */ if (!msg->contact&&(parse_headers(msg,HDR_CONTACT_F,0)<0||!msg->contact)){ //LM_ERR("bad sip message or missing Contact hdr\n"); contact->s = NULL; contact->len = 0; } else { if ( parse_contact(msg->contact)<0 || ((contact_body_t *)msg->contact->parsed)->contacts==NULL || ((contact_body_t *)msg->contact->parsed)->contacts->next!=NULL ) { LM_ERR("bad Contact HDR\n"); contact->s = NULL; contact->len = 0; } else { *contact = ((contact_body_t *)msg->contact->parsed)->contacts->uri; } } /* extract the RR parts - parse all headers as we can have multiple RR headers in the same message */ if( parse_headers(msg,HDR_EOH_F,0)<0 ){ LM_ERR("failed to parse record route header\n"); rr_set->s = 0; rr_set->len = 0; } else { if(msg->record_route){ if( print_rr_body(msg->record_route, rr_set, !is_req, skip_rrs) != 0 ){ LM_ERR("failed to print route records \n"); rr_set->s = 0; rr_set->len = 0; } } else { rr_set->s = 0; rr_set->len = 0; } } } /*usage: dlg: the dialog to add cseq, contact & record_route * msg: sip message * flag: 0-for a request(INVITE), * 1- for a reply(200 ok) * * for a request: get record route in normal order * for a reply : get in reverse order, skipping the ones from the request and * the proxies' own */ static int init_leg_info( struct dlg_cell *dlg, struct sip_msg *msg, struct cell* t, str *tag,str *mangled_from,str *mangled_to) { unsigned int skip_recs; str cseq; str contact; str rr_set; int is_req; is_req = (msg->first_line.type==SIP_REQUEST)?1:0; /* extract the cseq number as string */ if (is_req) { /* cseq */ if((!msg->cseq && (parse_headers(msg,HDR_CSEQ_F,0)<0 || !msg->cseq)) || !msg->cseq->parsed){ LM_ERR("bad sip message or missing CSeq hdr :-/\n"); goto error0; } cseq = (get_cseq(msg))->number; /* routing info */ skip_recs = 0; get_routing_info(msg, is_req, &skip_recs, &contact, &rr_set); dlg->from_rr_nb = skip_recs; } else { /* use the same as in invite cseq in caller leg */ cseq = dlg->legs[DLG_CALLER_LEG].inv_cseq; if ((dlg->mod_flags & TOPOH_ONGOING) && (msg->REPLY_STATUS<200 && msg->REPLY_STATUS>100)) { /* save contact && rr_set , may need need to route requests * before the INVITE transaction terminates */ get_routing_info(msg,is_req,0,&contact,&rr_set); } else { /* no need to save these here, wait for final response */ rr_set.len = contact.len = 0; rr_set.s = contact.s = NULL; } } LM_DBG("route_set %.*s, contact %.*s, cseq %.*s and bind_addr %.*s\n", rr_set.len, ZSW(rr_set.s), contact.len, ZSW(contact.s), cseq.len, cseq.s, msg->rcv.bind_address->sock_str.len, msg->rcv.bind_address->sock_str.s); if (dlg_add_leg_info( dlg, tag, &rr_set, &contact, &cseq, msg->rcv.bind_address,mangled_from,mangled_to,0)!=0) { LM_ERR("dlg_add_leg_info failed\n"); if (rr_set.s) pkg_free(rr_set.s); goto error0; } if (rr_set.s) pkg_free(rr_set.s); return 0; error0: return -1; } static str extracted_to_uri; static inline str* extract_mangled_touri(str *mangled_to_hdr) { struct to_body to_b; struct hdr_field hdr; char *tmp,*end; if (mangled_to_hdr->len == 0 || mangled_to_hdr->s == NULL) return NULL; end = mangled_to_hdr->s+mangled_to_hdr->len; tmp=parse_hname2(mangled_to_hdr->s,end,&hdr); if (hdr.type==HDR_ERROR_T) { LM_ERR("bad to header\n"); return NULL; } tmp = eat_lws_end(tmp,end); if (tmp >= end) { LM_ERR("empty header\n"); return NULL; } parse_to(tmp,end,&to_b); if (to_b.error == PARSE_ERROR) { LM_ERR("bad to header [%.*s]\n",mangled_to_hdr->len,mangled_to_hdr->s); return NULL; } extracted_to_uri = to_b.uri; free_to_params(&to_b); LM_DBG("extracted to uri [%.*s]\n",extracted_to_uri.len,extracted_to_uri.s); return &extracted_to_uri; } static str extracted_from_uri; static inline str* extract_mangled_fromuri(str *mangled_from_hdr) { struct to_body from_b; struct hdr_field hdr; char *tmp,*end; if (mangled_from_hdr->len == 0 || mangled_from_hdr->s == NULL) return NULL; end = mangled_from_hdr->s+mangled_from_hdr->len; tmp=parse_hname2(mangled_from_hdr->s,end,&hdr); if (hdr.type==HDR_ERROR_T) { LM_ERR("bad from header\n"); return NULL; } tmp=eat_lws_end(tmp, end); if (tmp >= end) { LM_ERR("empty header\n"); return NULL; } parse_to(tmp,end,&from_b); if (from_b.error == PARSE_ERROR) { LM_ERR("bad from header [%.*s]\n",mangled_from_hdr->len,mangled_from_hdr->s); return NULL; } extracted_from_uri = from_b.uri; free_to_params(&from_b); LM_DBG("extracted from uri [%.*s]\n",extracted_from_uri.len,extracted_from_uri.s); return &extracted_from_uri; } static inline void push_reply_in_dialog(struct sip_msg *rpl, struct cell* t, struct dlg_cell *dlg,str *mangled_from,str *mangled_to) { str tag,contact,rr_set; unsigned int leg, skip_rrs,cseq_no; /* get to tag*/ if ( !rpl->to && ((parse_headers(rpl,HDR_TO_F,0)<0) || !rpl->to) ){ LM_ERR("bad reply or missing TO hdr :-/\n"); tag.s = 0; tag.len = 0; } else { tag = get_to(rpl)->tag_value; if (tag.s==0 || tag.len==0) { /* Don't print error for final replies in DLG_STATE_UNCONFIRMED */ if (!(dlg->state == DLG_STATE_UNCONFIRMED && rpl->first_line.u.reply.statuscode >= 300)) { LM_ERR("[%d] reply in dlg state [%d]: missing TAG param in TO hdr\n", rpl->first_line.u.reply.statuscode, dlg->state); } tag.s = 0; tag.len = 0; } } LM_DBG("%p totag in rpl is <%.*s> (%d)\n", dlg, tag.len,tag.s,tag.len); /* ignore provisional replies replies without totag */ if (tag.len==0 && rpl->REPLY_STATUS<200 ) return; /* is the totag already known ?? */ for(leg=DLG_FIRST_CALLEE_LEG ; leglegs_no[DLG_LEGS_USED] ; leg++ ) { if ( dlg->legs[leg].tag.len==tag.len && strncmp(dlg->legs[leg].tag.s,tag.s,tag.len)==0 ) { /* we have a match -> branch already known... */ LM_DBG("branch with tag <%.*s> already exists\n",tag.len,tag.s); goto routing_info; } } /* coooool :D ....new totag learned !! -> store it */ /* save callee's tag and cseq */ LM_DBG("new branch with tag <%.*s>\n",tag.len,tag.s); if (init_leg_info( dlg, rpl, t, &tag,extract_mangled_fromuri(mangled_from), extract_mangled_touri(mangled_to)) !=0) { LM_ERR("could not add further info to the dialog\n"); return; } leg = dlg->legs_no[DLG_LEGS_USED] - 1; /* idx of last created leg */ routing_info: /* update dlg info only if 2xx reply and if not already done so */ if (rpl->REPLY_STATUS>=200 && rpl->REPLY_STATUS<300 && dlg->legs_no[DLG_LEG_200OK] != leg) { /* set this branch as primary */ if (!dlg->legs_no[DLG_LEG_200OK]) dlg->legs_no[DLG_LEG_200OK] = leg; if (dlg->flags & DLG_FLAG_CSEQ_ENFORCE) { /* increase all future requests going to this leg */ if (str2int(&(((struct cseq_body *)t->uas.request->cseq->parsed)->number),&cseq_no) < 0) { LM_ERR("Failed to convert cseq to integer \n"); } else { /* XXX - fix this */ dlg->legs[dlg->legs_no[DLG_LEG_200OK]].last_gen_cseq = cseq_no + 1; } } /* update routing info */ if(dlg->mod_flags & TOPOH_ONGOING) skip_rrs = 0; /* changed here for contact - it was 1 */ else skip_rrs = dlg->from_rr_nb + ((t->relaied_reply_branch>=0)? (t->uac[t->relaied_reply_branch].added_rr):0); LM_DBG("Skipping %d ,%d, %d, %d \n",skip_rrs, dlg->from_rr_nb,t->relaied_reply_branch,t->uac[t->relaied_reply_branch].added_rr); get_routing_info(rpl, 0, &skip_rrs, &contact, &rr_set); dlg_update_routing( dlg, leg, &rr_set, &contact); if( rr_set.s ) pkg_free( rr_set.s); } } static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param) { struct sip_msg *rpl,*req; struct dlg_cell *dlg; int new_state; int old_state; int unref; int event; str mangled_from = {0,0}; str mangled_to = {0,0}; str *req_out_buff; dlg = (struct dlg_cell *)(*param->param); if (shutdown_done || dlg==0) return; rpl = param->rpl; req = param->req; if (type==TMCB_RESPONSE_FWDED) { /* this callback is under transaction lock (by TM), so it is save to operate at write level, but we need to take care on write-read conflicts -bogdan */ if (rpl!=FAKED_REPLY) { if (req->msg_flags & (FL_USE_UAC_FROM | FL_USE_UAC_TO ) ) { req_out_buff = &t->uac[d_tmb.get_branch_index()].request.buffer; if (extract_ftc_hdrs(req_out_buff->s,req_out_buff->len, (req->msg_flags & FL_USE_UAC_FROM )?&mangled_from:0, (req->msg_flags & FL_USE_UAC_TO )?&mangled_to:0,0,0) != 0) { LM_ERR("failed to extract mangled FROM and TO hdrs\n"); mangled_from.len = 0; mangled_from.s = NULL; mangled_to.len = 0; mangled_to.s = NULL; } else { if ((req->msg_flags & FL_USE_UAC_FROM) && (mangled_from.len == 0 || mangled_from.s == NULL)) LM_CRIT("extract_ftc_hdrs ok but no from extracted : [%.*s]\n",req_out_buff->len,req_out_buff->s); if ((req->msg_flags & FL_USE_UAC_TO) && (mangled_to.len == 0 || mangled_to.s == NULL)) LM_CRIT("extract_ftc_hdrs ok but no to extracted : [%.*s]\n",req_out_buff->len,req_out_buff->s); } } push_reply_in_dialog( rpl, t, dlg,&mangled_from,&mangled_to); } else { LM_DBG("dialog replied from script - cannot get callee info\n"); } /* The state does not change, but the msg is mutable in this callback*/ run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, rpl, DLG_DIR_UPSTREAM, 0); return; } if (type==TMCB_TRANS_CANCELLED) { /* only if we did force match the Cancel to the * dialog before ( from the script ) */ if ( ctx_dialog_get()==NULL) { /* reference and attached to script */ ref_dlg(dlg,1); ctx_dialog_set(t->dialog_ctx); } return; } if (type==TMCB_RESPONSE_OUT) { if (dlg->state == DLG_STATE_CONFIRMED_NA && dialog_replicate_cluster) replicate_dialog_created(dlg); return; } if (type==TMCB_TRANS_DELETED) { event = DLG_EVENT_TDEL; } else if (param->code<200) { event = DLG_EVENT_RPL1xx; ctx_lastdstleg_set(DLG_CALLER_LEG); } else if (param->code<300) { event = DLG_EVENT_RPL2xx; ctx_lastdstleg_set(DLG_CALLER_LEG); } else { event = DLG_EVENT_RPL3xx; ctx_lastdstleg_set(DLG_CALLER_LEG); } next_state_dlg(dlg, event, DLG_DIR_UPSTREAM, &old_state, &new_state, &unref, DLG_CALLER_LEG, 0); if (new_state==DLG_STATE_EARLY && old_state!=DLG_STATE_EARLY) { run_dlg_callbacks(DLGCB_EARLY, dlg, rpl, DLG_DIR_UPSTREAM, 0); if_update_stat(dlg_enable_stats, early_dlgs, 1); return; } if (new_state==DLG_STATE_CONFIRMED_NA && old_state!=DLG_STATE_CONFIRMED_NA && old_state!=DLG_STATE_CONFIRMED ) { LM_DBG("dialog %p confirmed\n",dlg); /* set start time */ dlg->start_ts = (unsigned int)(time(0)); if (0 != insert_dlg_timer( &dlg->tl, dlg->lifetime )) { LM_CRIT("Unable to insert dlg %p [%u:%u] on event %d [%d->%d] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else { /* reference dialog as kept in timer list */ ref_dlg(dlg,1); } if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (0 != insert_ping_timer( dlg)) { LM_CRIT("Unable to insert ping dlg %p [%u:%u] on event %d [%d->%d] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else { /* reference dialog as kept in ping timer list */ ref_dlg(dlg,1); } } if (dlg->flags & DLG_FLAG_REINVITE_PING_CALLER || dlg->flags & DLG_FLAG_REINVITE_PING_CALLEE) { if (0 != insert_reinvite_ping_timer( dlg)) { LM_CRIT("Unable to insert ping dlg %p [%u:%u] on event %d [%d->%d] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else { /* reference dialog as kept in reinvite ping timer list */ ref_dlg(dlg,1); } } /* save the settings to the database, * if realtime saving mode configured- save dialog now * else: the next time the timer will fire the update*/ dlg->flags |= DLG_FLAG_NEW; if ( dlg_db_mode==DB_MODE_REALTIME ) update_dialog_dbinfo(dlg); /* dialog confirmed */ run_dlg_callbacks( DLGCB_CONFIRMED, dlg, rpl, DLG_DIR_UPSTREAM, 0); if (old_state==DLG_STATE_EARLY) if_update_stat(dlg_enable_stats, early_dlgs, -1); if_update_stat(dlg_enable_stats, active_dlgs, 1); return; } if ( old_state!=DLG_STATE_DELETED && new_state==DLG_STATE_DELETED ) { LM_DBG("dialog %p failed (negative reply)\n", dlg); /*destroy linkers */ dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); /* dialog setup not completed (3456XX) */ run_dlg_callbacks( DLGCB_FAILED, dlg, rpl, DLG_DIR_UPSTREAM, 0); /* do unref */ if (unref) unref_dlg(dlg,unref); if (old_state==DLG_STATE_EARLY) if_update_stat(dlg_enable_stats, early_dlgs, -1); if_update_stat(dlg_enable_stats, failed_dlgs, 1); return; } /* in any other case, check if the dialog state machine requests to unref the dialog */ if (unref) unref_dlg(dlg,unref); return; } /* modifies the sip_msg, setting the cseq header to * new_cseq.s + value OR * value if new_cseq is NULL */ static inline int update_msg_cseq(struct sip_msg *msg,str *new_cseq, unsigned int value) { int offset,len; struct lump *tmp; char *buf; unsigned int loc_cseq; str final_cseq; str pkg_cseq; if (!msg) { LM_ERR("null pointer provided\n"); return -1; } if(parse_headers(msg, HDR_CSEQ_F, 0) <0 ) { LM_ERR("failed to parse headers \n"); return -1; } if (new_cseq == 0 || new_cseq->s == 0 || new_cseq->len == 0) { LM_DBG("null str provided. Using only int value for cseq\n"); final_cseq.s = int2str(value,&final_cseq.len); } else { if( str2int(new_cseq, &loc_cseq) != 0){ LM_ERR("could not convert string cseq to number\n"); return -1; } loc_cseq += value; final_cseq.s = int2str(loc_cseq,&final_cseq.len); } buf = msg->buf; len = ((struct cseq_body *)msg->cseq->parsed)->number.len; offset = ((struct cseq_body *)msg->cseq->parsed)->number.s - buf; if ((tmp = del_lump(msg,offset,len,0)) == 0) { LM_ERR("failed to remove the existing CSEQ\n"); return -1; } /* Make pkg copy of cseq */ pkg_cseq.s = pkg_malloc(final_cseq.len); if (pkg_cseq.s == 0) { LM_ERR("no more pkg mem\n"); return -1; } pkg_cseq.len = final_cseq.len; memcpy(pkg_cseq.s,final_cseq.s,final_cseq.len); LM_DBG("Message CSEQ translated from [%.*s] to [%.*s]\n", ((struct cseq_body *)msg->cseq->parsed)->number.len, ((struct cseq_body *)msg->cseq->parsed)->number.s,pkg_cseq.len, pkg_cseq.s); if (insert_new_lump_after(tmp,pkg_cseq.s,pkg_cseq.len,0) == 0) { LM_ERR("failed to insert new CSEQ\n"); pkg_free(pkg_cseq.s); return -1; } return 0; } static void dlg_update_sdp(struct dlg_leg *leg,struct sip_msg *msg) { str sdp; if (get_body(msg,&sdp) < 0) { LM_ERR("Failed to extract SDP \n"); sdp.s = NULL; sdp.len = 0; } if (leg->sdp.len < sdp.len) { leg->sdp.s = shm_realloc(leg->sdp.s,sdp.len); if (!leg->sdp.s) { LM_ERR("Failed to reallocate sdp \n"); return; } } leg->sdp.len = sdp.len; memcpy(leg->sdp.s,sdp.s,sdp.len); } static void dlg_update_callee_sdp(struct cell* t, int type, struct tmcb_params *ps) { struct sip_msg *rpl,*msg; int statuscode; struct dlg_cell *dlg; str buffer; if(ps == NULL || ps->rpl == NULL) { LM_ERR("Wrong tmcb params\n"); return; } if( ps->param== NULL ) { LM_ERR("Null callback parameter\n"); return; } rpl = ps->rpl; statuscode = ps->code; dlg = *(ps->param); if(rpl==NULL || rpl==FAKED_REPLY) { /* we only care about actual replayed replies */ return; } LM_DBG("Status Code received = [%d]\n", statuscode); if (statuscode == 200) { buffer.s = ((str*)ps->extra1)->s; buffer.len = ((str*)ps->extra1)->len; msg=pkg_malloc(sizeof(struct sip_msg)); if (msg==0) { LM_ERR("no pkg mem left for sip_msg\n"); return; } memset(msg,0, sizeof(struct sip_msg)); msg->buf=buffer.s; msg->len=buffer.len; if (parse_msg(buffer.s,buffer.len, msg)!=0) { pkg_free(msg); return; } dlg_update_sdp(&dlg->legs[callee_idx(dlg)],msg); free_sip_msg(msg); pkg_free(msg); } } static void dlg_update_caller_sdp(struct cell* t, int type, struct tmcb_params *ps) { struct sip_msg *rpl,*msg; int statuscode; struct dlg_cell *dlg; str buffer; if(ps == NULL || ps->rpl == NULL) { LM_ERR("Wrong tmcb params\n"); return; } if( ps->param== NULL ) { LM_ERR("Null callback parameter\n"); return; } rpl = ps->rpl; statuscode = ps->code; dlg = *(ps->param); if(rpl==NULL || rpl==FAKED_REPLY) { /* we only care about actual replayed replies */ return; } LM_DBG("Status Code received = [%d]\n", statuscode); if (statuscode == 200) { buffer.s = ((str*)ps->extra1)->s; buffer.len = ((str*)ps->extra1)->len; msg=pkg_malloc(sizeof(struct sip_msg)); if (msg==0) { LM_ERR("no pkg mem left for sip_msg\n"); return; } memset(msg,0, sizeof(struct sip_msg)); msg->buf=buffer.s; msg->len=buffer.len; if (parse_msg(buffer.s,buffer.len, msg)!=0) { pkg_free(msg); return; } dlg_update_sdp(&dlg->legs[DLG_CALLER_LEG],msg); free_sip_msg(msg); pkg_free(msg); } } static void dlg_seq_up_onreply_mod_cseq(struct cell* t, int type, struct tmcb_params *param) { struct dlg_cell *dlg; dlg = ((dlg_cseq_wrapper*)*param->param)->dlg; if (shutdown_done || dlg==0) return; if (update_msg_cseq((struct sip_msg *)param->rpl,&((dlg_cseq_wrapper *)*param->param)->cseq,0) != 0) LM_ERR("failed to update CSEQ in msg\n"); if (type==TMCB_RESPONSE_FWDED && (dlg->cbs.types)&DLGCB_RESPONSE_WITHIN) { run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, dlg, param->rpl, DLG_DIR_UPSTREAM, 0); return; } return; } static void dlg_seq_up_onreply(struct cell* t, int type, struct tmcb_params *param) { struct dlg_cell *dlg; dlg = (struct dlg_cell *)(*param->param); if (shutdown_done || dlg==0) return; if (type==TMCB_RESPONSE_FWDED && (dlg->cbs.types)&DLGCB_RESPONSE_WITHIN) { run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, dlg, param->rpl, DLG_DIR_UPSTREAM, 0); return; } return; } static void dlg_seq_down_onreply_mod_cseq(struct cell* t, int type, struct tmcb_params *param) { struct dlg_cell *dlg; dlg = ((dlg_cseq_wrapper*)*param->param)->dlg; if (shutdown_done || dlg==0) return; if (update_msg_cseq((struct sip_msg *)param->rpl,&((dlg_cseq_wrapper *)*param->param)->cseq,0) != 0) LM_ERR("failed to update CSEQ in msg\n"); if (type==TMCB_RESPONSE_FWDED && (dlg->cbs.types)&DLGCB_RESPONSE_WITHIN) { run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, dlg, param->rpl, DLG_DIR_DOWNSTREAM, 0); return; } return; } static void fix_final_cseq(struct cell *t,int type, struct tmcb_params *param) { str cseq; cseq.s = (char *)(*param->param); cseq.len = strlen(cseq.s); if (update_msg_cseq((struct sip_msg *)param->rpl,&cseq,0) != 0) LM_ERR("failed to update CSEQ in msg\n"); shm_free(cseq.s); return ; } static void dlg_seq_down_onreply(struct cell* t, int type, struct tmcb_params *param) { struct dlg_cell *dlg; dlg = (struct dlg_cell *)(*param->param); if (shutdown_done || dlg==0) return; if (type==TMCB_RESPONSE_FWDED && (dlg->cbs.types)&DLGCB_RESPONSE_WITHIN) { run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, dlg, param->rpl, DLG_DIR_DOWNSTREAM, 0); return; } return; } inline static int get_dlg_timeout(struct sip_msg *msg) { return (current_processing_ctx && (ctx_timeout_get()!=0)) ? ctx_timeout_get() : default_timeout; } static void unreference_dialog_cseq(void *cseq_wrap) { /* if the dialog table is gone, it means the system is shutting down.*/ if (!d_table) return; dlg_cseq_wrapper *wrap = (dlg_cseq_wrapper *)cseq_wrap; unref_dlg(wrap->dlg, 1); shm_free(wrap); } void unreference_dialog(void *dialog) { /* if the dialog table is gone, it means the system is shutting down.*/ unref_dlg_destroy_safe((struct dlg_cell*)dialog, 1); } static void unreference_dialog_create(void *dialog) { struct tmcb_params params; memset(¶ms, 0, sizeof(struct tmcb_params)); params.param = (void*)&dialog; /* just a wapper */ dlg_onreply( 0, TMCB_TRANS_DELETED, ¶ms); } static void tmcb_unreference_dialog(struct cell* t, int type, struct tmcb_params *param) { unref_dlg_destroy_safe((struct dlg_cell*)*param->param, 1); } static void dlg_onreply_out(struct cell* t, int type, struct tmcb_params *ps) { struct sip_msg *msg,*rpl; struct dlg_cell *dlg; str buffer,contact,sdp; dlg = (struct dlg_cell *)(*ps->param); rpl = ps->rpl; if(rpl==NULL || rpl==FAKED_REPLY) { /* we only care about actual replayed replies */ return; } if (ps->code == 200) { buffer.s = ((str*)ps->extra1)->s; buffer.len = ((str*)ps->extra1)->len; msg=pkg_malloc(sizeof(struct sip_msg)); if (msg==0) { LM_ERR("no pkg mem left for sip_msg\n"); return; } memset(msg,0, sizeof(struct sip_msg)); msg->buf=buffer.s; msg->len=buffer.len; if (parse_msg(buffer.s,buffer.len, msg)!=0) { pkg_free(msg); return; } /* extract SDP */ if (get_body(msg,&sdp) < 0) { LM_ERR("Failed to extract SDP from outgoing invite \n"); sdp.s = NULL; sdp.len = 0; } dlg->legs[callee_idx(dlg)].sdp.s = shm_malloc(sdp.len); if (!dlg->legs[callee_idx(dlg)].sdp.s) { LM_ERR("No more shm \n"); free_sip_msg(msg); pkg_free(msg); return; } dlg->legs[callee_idx(dlg)].sdp.len = sdp.len; memcpy(dlg->legs[callee_idx(dlg)].sdp.s,sdp.s,sdp.len); /* extract the contact address */ if (!msg->contact&&(parse_headers(msg,HDR_CONTACT_F,0)<0||!msg->contact)){ LM_ERR("There is no contact header in the outgoing 200OK \n"); } else { contact.s = msg->contact->name.s; contact.len = msg->contact->len; dlg->legs[callee_idx(dlg)].th_sent_contact.s = shm_malloc(contact.len); if (!dlg->legs[callee_idx(dlg)].th_sent_contact.s) { LM_ERR("No more shm mem for outgoing contact hdr\n"); free_sip_msg(msg); pkg_free(msg); return; } dlg->legs[callee_idx(dlg)].th_sent_contact.len = contact.len; memcpy(dlg->legs[callee_idx(dlg)].th_sent_contact.s,contact.s,contact.len); } free_sip_msg(msg); pkg_free(msg); } } static void dlg_caller_reinv_onreq_out(struct cell* t, int type, struct tmcb_params *ps) { struct sip_msg *msg; struct dlg_cell *dlg; str buffer; buffer.s = ((str*)ps->extra1)->s; buffer.len = ((str*)ps->extra1)->len; dlg = (struct dlg_cell *)(*ps->param); msg=pkg_malloc(sizeof(struct sip_msg)); if (msg==0) { LM_ERR("no pkg mem left for sip_msg\n"); return; } memset(msg,0, sizeof(struct sip_msg)); msg->buf=buffer.s; msg->len=buffer.len; if (parse_msg(buffer.s,buffer.len, msg)!=0) { pkg_free(msg); return; } dlg_update_sdp(&dlg->legs[DLG_CALLER_LEG],msg); free_sip_msg(msg); pkg_free(msg); } static void dlg_callee_reinv_onreq_out(struct cell* t, int type, struct tmcb_params *ps) { struct sip_msg *msg; struct dlg_cell *dlg; str buffer; buffer.s = ((str*)ps->extra1)->s; buffer.len = ((str*)ps->extra1)->len; dlg = (struct dlg_cell *)(*ps->param); msg=pkg_malloc(sizeof(struct sip_msg)); if (msg==0) { LM_ERR("no pkg mem left for sip_msg\n"); return; } memset(msg,0, sizeof(struct sip_msg)); msg->buf=buffer.s; msg->len=buffer.len; if (parse_msg(buffer.s,buffer.len, msg)!=0) { pkg_free(msg); return; } dlg_update_sdp(&dlg->legs[callee_idx(dlg)],msg); free_sip_msg(msg); pkg_free(msg); } void dlg_onreq(struct cell* t, int type, struct tmcb_params *param) { struct dlg_cell *dlg; /* is the dialog already created? */ if ( (dlg=ctx_dialog_get())!=NULL ) { /* new, un-initialized dialog ? */ if ( dlg->flags & DLG_FLAG_ISINIT ) { /* fully init dialog -> check if attached to the transaction */ if (t->dialog_ctx==NULL) { /* set a callback to remove the ref when transaction * is destroied */ if ( d_tmb.register_tmcb( NULL, t, TMCB_TRANS_DELETED, tmcb_unreference_dialog, (void*)dlg, NULL)<0){ LM_ERR("failed to register TMCB\n"); return; } /* and attached the dialog to the transaction */ t->dialog_ctx = (void*)dlg; /* and keep a reference on it */ ref_dlg( dlg , 1); } return; } /* dialog was previously created by create_dialog() -> just do the last settings */ run_create_callbacks( dlg, param->req); LM_DBG("t hash_index = %u, t label = %u\n",t->hash_index,t->label); dlg->initial_t_hash_index = t->hash_index; dlg->initial_t_label = t->label; t->dialog_ctx = (void*)dlg; /* dialog is fully initialized */ dlg->flags |= DLG_FLAG_ISINIT; if (dlg->flags & DLG_FLAG_REINVITE_PING_CALLER || dlg->flags & DLG_FLAG_REINVITE_PING_CALLEE) { if(d_tmb.register_tmcb( 0, t, TMCB_RESPONSE_OUT, dlg_onreply_out, (void *)dlg, 0) <=0) { LM_ERR("can't register trace_onreply_out\n"); } } } } static void dlg_onreq_out(struct cell* t, int type, struct tmcb_params *ps) { struct sip_msg *msg; struct dlg_cell *dlg; str buffer,contact,sdp; buffer.s = ((str*)ps->extra1)->s; buffer.len = ((str*)ps->extra1)->len; dlg = (struct dlg_cell *)(*ps->param); msg=pkg_malloc(sizeof(struct sip_msg)); if (msg==0) { LM_ERR("no pkg mem left for sip_msg\n"); return; } memset(msg,0, sizeof(struct sip_msg)); msg->buf=buffer.s; msg->len=buffer.len; if (parse_msg(buffer.s,buffer.len, msg)!=0) { pkg_free(msg); return; } /* extract SDP */ if (get_body(msg,&sdp) < 0) { LM_ERR("Failed to extract SDP from outgoing invite \n"); sdp.s = NULL; sdp.len = 0; } dlg->legs[DLG_CALLER_LEG].sdp.s = shm_malloc(sdp.len); if (!dlg->legs[DLG_CALLER_LEG].sdp.s) { LM_ERR("No more shm \n"); free_sip_msg(msg); pkg_free(msg); return; } dlg->legs[DLG_CALLER_LEG].sdp.len = sdp.len; memcpy(dlg->legs[DLG_CALLER_LEG].sdp.s,sdp.s,sdp.len); /* extract the contact address */ if (!msg->contact&&(parse_headers(msg,HDR_CONTACT_F,0)<0||!msg->contact)){ LM_ERR("No outgoing contact in the initial INVITE \n"); } else { contact.s = msg->contact->name.s; contact.len = msg->contact->len; dlg->legs[DLG_CALLER_LEG].th_sent_contact.s = shm_malloc(contact.len); if (!dlg->legs[DLG_CALLER_LEG].th_sent_contact.s) { LM_ERR("No more shm for INVITE outgoing contact \n"); free_sip_msg(msg); pkg_free(msg); return; } dlg->legs[DLG_CALLER_LEG].th_sent_contact.len = contact.len; memcpy(dlg->legs[DLG_CALLER_LEG].th_sent_contact.s,contact.s,contact.len); } free_sip_msg(msg); pkg_free(msg); } int dlg_create_dialog(struct cell* t, struct sip_msg *req,unsigned int flags) { struct dlg_cell *dlg; str s; int extra_ref,types; /* module is stricly designed for dialog calls */ if (req->first_line.u.request.method_value!=METHOD_INVITE) return -1; if ( (!req->to && parse_headers(req, HDR_TO_F,0)<0) || !req->to ) { LM_ERR("bad request or missing TO hdr :-/\n"); return -1; } s = get_to(req)->tag_value; if (s.s!=0 && s.len!=0) return -1; if ( parse_from_header(req)) { LM_ERR("bad request or missing FROM hdr :-/\n"); return -1; } if ((!req->callid && parse_headers(req,HDR_CALLID_F,0)<0) || !req->callid){ LM_ERR("bad request or missing CALLID hdr :-/\n"); return -1; } s = req->callid->body; trim(&s); /* some sanity checks */ if (s.len==0 || get_from(req)->tag_value.len==0) { LM_ERR("invalid request -> callid (%d) or from TAG (%d) empty\n", s.len, get_from(req)->tag_value.len); return -1; } dlg = build_new_dlg( &s /*callid*/, &(get_from(req)->uri) /*from uri*/, &(get_to(req)->uri) /*to uri*/, &(get_from(req)->tag_value)/*from_tag*/ ); if (dlg==0) { LM_ERR("failed to create new dialog\n"); return -1; } dlg->flags |= flags; /* save caller's tag, cseq, contact and record route*/ if (init_leg_info(dlg, req, t, &(get_from(req)->tag_value),NULL,NULL ) !=0) { LM_ERR("could not add further info to the dialog\n"); shm_free(dlg); return -1; } /* set current dialog */ ctx_dialog_set(dlg); ctx_lastdstleg_set(DLG_FIRST_CALLEE_LEG); extra_ref=2; /* extra ref for the callback and current dlg hook */ if (dlg_db_mode == DB_MODE_DELAYED) extra_ref++; /* extra ref for the timer to delete the dialog */ link_dlg( dlg , extra_ref); if ( seq_match_mode!=SEQ_MATCH_NO_ID && add_dlg_rr_param( req, dlg->h_entry, dlg->h_id)<0 ) { LM_ERR("failed to add RR param\n"); goto error; } types = TMCB_RESPONSE_PRE_OUT|TMCB_RESPONSE_FWDED|TMCB_TRANS_CANCELLED; /* replicate dialogs after the 200 OK was fwded - speed & after all msg * processing was done ( eg. ACC ) */ if (dialog_replicate_cluster) types |= TMCB_RESPONSE_OUT; if ( d_tmb.register_tmcb( req, t,types,dlg_onreply, (void*)dlg, unreference_dialog_create)<0 ) { LM_ERR("failed to register TMCB\n"); goto error; } /* complete the dialog setup only if transaction aleady exists; if not, wait for the TMCB_REQUEST_IN callback to do this job */ if (t) { /* first INVITE seen (dialog created, unconfirmed) */ run_create_callbacks( dlg, req); LM_DBG("t hash_index = %u, t label = %u\n",t->hash_index,t->label); dlg->initial_t_hash_index = t->hash_index; dlg->initial_t_label = t->label; t->dialog_ctx = (void*) dlg; dlg->flags |= DLG_FLAG_ISINIT; } dlg->lifetime = get_dlg_timeout(req); if (dlg->flags & DLG_FLAG_REINVITE_PING_CALLER || dlg->flags & DLG_FLAG_REINVITE_PING_CALLEE) { /* register out callback in order to save Contact and SDP */ if(d_tmb.register_tmcb( req, 0, TMCB_REQUEST_BUILT, dlg_onreq_out, (void *)dlg, 0) <=0) { LM_ERR("can't register trace_onreq_out\n"); return -1; } } if_update_stat( dlg_enable_stats, processed_dlgs, 1); return 0; error: unref_dlg(dlg,extra_ref); dialog_cleanup( req, NULL); if_update_stat(dlg_enable_stats, failed_dlgs, 1); return -1; } /* update inv_cseq field if update_field=1 * else update r_cseq */ static inline int update_cseqs(struct dlg_cell *dlg, struct sip_msg *req, unsigned int leg, int update_field) { if ( (!req->cseq && parse_headers(req,HDR_CSEQ_F,0)<0) || !req->cseq || !req->cseq->parsed) { LM_ERR("bad sip message or missing CSeq hdr :-/\n"); return -1; } return dlg_update_cseq(dlg, leg, &((get_cseq(req))->number),update_field); } /* move r_cseq to prev_cseq in leg */ static inline int switch_cseqs(struct dlg_cell *dlg,unsigned int leg_no) { str* r_cseq,*prev_cseq; r_cseq = &dlg->legs[leg_no].r_cseq; prev_cseq = &dlg->legs[leg_no].prev_cseq; if ( prev_cseq->s ) { if (prev_cseq->len < r_cseq->len) { prev_cseq->s = (char*)shm_realloc(prev_cseq->s,r_cseq->len); if (prev_cseq->s==NULL) { LM_ERR("no more shm mem for realloc (%d)\n",r_cseq->len); return -1; } } } else { prev_cseq->s = (char*)shm_malloc(r_cseq->len); if (prev_cseq->s==NULL) { LM_ERR("no more shm mem for malloc (%d)\n",r_cseq->len); return -1; } } memcpy( prev_cseq->s, r_cseq->s, r_cseq->len ); prev_cseq->len = r_cseq->len; LM_DBG("prev_cseq = %.*s for leg %d\n",prev_cseq->len,prev_cseq->s,leg_no); return 0; } static inline void log_bogus_dst_leg(struct dlg_cell *dlg) { if (ctx_lastdstleg_get()>=dlg->legs_no[DLG_LEGS_USED]) LM_CRIT("bogus dst leg %d in state %d for dlg %p [%u:%u] with " "clid '%.*s' and tags '%.*s' '%.*s'. legs used %d\n", ctx_lastdstleg_get(),dlg->state, dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag),dlg->legs_no[DLG_LEGS_USED]); } void dlg_onroute(struct sip_msg* req, str *route_params, void *param) { struct dlg_cell *dlg; str val = {0,0}; str callid; str ftag; str ttag; int h_entry; int h_id; int new_state; int old_state; int unref; int event; unsigned int update_val; unsigned int dir,dst_leg,src_leg; int ret = 0,ok = 1; struct dlg_entry *d_entry; str *msg_cseq; char *final_cseq; /* as this callback is triggered from loose_route, which can be accidentaly called more than once from script, we need to be sure we do this only once !*/ if (ctx_dialog_get()) return; /* skip initial requests - they may end up here because of the * preloaded route */ if ( (!req->to && parse_headers(req, HDR_TO_F,0)<0) || !req->to ) { LM_ERR("bad request or missing TO hdr :-/\n"); return; } if ( get_to(req)->tag_value.len==0 ) return; dlg = 0; dir = DLG_DIR_NONE; dst_leg = -1; /* From RR callback, param will be NULL * From match_dialog, param might have a value, if we * are in the topology hiding case & we were able to extract the * DID from the R-URI */ if (param) val = *((str *)param); if ( seq_match_mode!=SEQ_MATCH_NO_ID ) { if( val.s == NULL && d_rrb.get_route_param( req, &rr_param, &val)!=0) { LM_DBG("Route param '%.*s' not found\n", rr_param.len,rr_param.s); if (seq_match_mode==SEQ_MATCH_STRICT_ID ) return; } else { LM_DBG("route param is '%.*s' (len=%d)\n",val.len,val.s,val.len); if ( parse_dlg_rr_param( val.s, val.s+val.len, &h_entry, &h_id)<0 ) return; dlg = lookup_dlg( h_entry, h_id); if (dlg==0) { LM_DBG("unable to find dialog for %.*s " "with route param '%.*s'\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s, val.len,val.s); } else { /* lookup_dlg has incremented the ref count by 1 */ if (pre_match_parse( req, &callid, &ftag, &ttag)<0) { unref_dlg(dlg, 1); return; } if (match_dialog(dlg,&callid,&ftag,&ttag,&dir, &dst_leg )==0){ if (!accept_replicated_dlg) { /* not an error when accepting replicating dialogs - we might have generated a different h_id when accepting the replicated dialog */ LM_WARN("tight matching failed for %.*s with " "callid='%.*s'/%d," " ftag='%.*s'/%d, ttag='%.*s'/%d and direction=%d\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s, callid.len, callid.s, callid.len, ftag.len, ftag.s, ftag.len, ttag.len, ttag.s, ttag.len, dir); LM_WARN("dialog identification elements are " "callid='%.*s'/%d, " "caller tag='%.*s'/%d, callee tag='%.*s'/%d\n", dlg->callid.len, dlg->callid.s, dlg->callid.len, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s), dlg->legs[callee_idx(dlg)].tag.len); } unref_dlg(dlg, 1); /* potentially fall through to SIP-wise dialog matching, depending on seq_match_mode */ dlg = NULL; } } if (dlg==NULL && seq_match_mode==SEQ_MATCH_STRICT_ID ) return; } } if (dlg==0) { if (pre_match_parse( req, &callid, &ftag, &ttag)<0) return; /* TODO - try to use the RR dir detection to speed up here the * search -bogdan */ dlg = get_dlg(&callid, &ftag, &ttag, &dir, &dst_leg); if (!dlg){ LM_DBG("Callid '%.*s' not found\n", req->callid->body.len, req->callid->body.s); return; } } /* run state machine */ switch ( req->first_line.u.request.method_value ) { case METHOD_PRACK: event = DLG_EVENT_REQPRACK; break; case METHOD_ACK: event = DLG_EVENT_REQACK; break; case METHOD_BYE: event = DLG_EVENT_REQBYE; break; default: event = DLG_EVENT_REQ; } next_state_dlg( dlg, event, dir, &old_state, &new_state, &unref, dst_leg, 0); /* set current dialog - it will keep a ref! */ ctx_dialog_set(dlg); ctx_lastdstleg_set(dst_leg); log_bogus_dst_leg(dlg); d_entry = &(d_table->entries[dlg->h_entry]); /* run actions for the transition */ if (event==DLG_EVENT_REQBYE && new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) { /*destroy linkers */ dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); if (!dlg->terminate_reason.s) { if (dst_leg == 0) init_dlg_term_reason(dlg,"Upstream BYE",sizeof("Upstream BYE")-1); else init_dlg_term_reason(dlg,"Downstream BYE",sizeof("Downstream BYE")-1); } LM_DBG("BYE successfully processed - dst_leg = %d\n",dst_leg); if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE || dlg->flags & DLG_FLAG_REINVITE_PING_CALLER || dlg->flags & DLG_FLAG_REINVITE_PING_CALLEE || dlg->flags & DLG_FLAG_CSEQ_ENFORCE) { dlg_lock (d_table,d_entry); if (dlg->legs[dst_leg].last_gen_cseq) { update_val = ++(dlg->legs[dst_leg].last_gen_cseq); dlg_unlock (d_table,d_entry); if (update_msg_cseq(req,0,update_val) != 0) LM_ERR("failed to update BYE msg cseq\n"); msg_cseq = &((struct cseq_body *)req->cseq->parsed)->number; final_cseq = shm_malloc(msg_cseq->len + 1); if (final_cseq == 0) { LM_ERR("no more shm mem\n"); goto after_unlock5; } memcpy(final_cseq,msg_cseq->s,msg_cseq->len); final_cseq[msg_cseq->len] = 0; if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_FWDED, fix_final_cseq, (void*)final_cseq, 0)<0 ) { LM_ERR("failed to register TMCB (2)\n"); } } else dlg_unlock (d_table,d_entry); } after_unlock5: /* remove from timer */ ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else if (ret > 0) { LM_DBG("dlg expired (not in timer list) on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else { /* dialog sucessfully removed from timer -> unref */ unref++; } /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, req, dir, 0); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); /* destroy dialog */ unref_dlg(dlg, unref); if_update_stat( dlg_enable_stats, active_dlgs, -1); return; } if ( (event==DLG_EVENT_REQ || event==DLG_EVENT_REQACK) && (new_state==DLG_STATE_CONFIRMED || new_state==DLG_STATE_CONFIRMED_NA) ) { LM_DBG("sequential request successfully processed (dst_leg=%d)\n", dst_leg); /* update the dialog timeout from the processing context */ if (current_processing_ctx && (ctx_timeout_get()!=0) ) { dlg->lifetime = ctx_timeout_get(); dlg->lifetime_dirty = 1; } else { dlg->lifetime_dirty = 0; } /* within dialog request */ run_dlg_callbacks( DLGCB_REQ_WITHIN, dlg, req, dir, 0); /* update timer during sequential request? */ if (dlg->lifetime_dirty) { switch ( update_dlg_timer( &dlg->tl, dlg->lifetime ) ) { case -1: LM_ERR("failed to update dialog lifetime\n"); case 0: /* timeout value was updated */ break; case 1: /* dlg inserted in timer list with new expire (reference it)*/ ref_dlg(dlg,1); } } LM_DBG("dialog_timeout: %d\n", dlg->lifetime); if ( event!=DLG_EVENT_REQACK ) { if (dst_leg==-1 || switch_cseqs(dlg, dst_leg) != 0 || update_cseqs(dlg,req,dst_leg,0)) { ok = 0; LM_ERR("cseqs update failed on leg=%d\n",dst_leg); } if (req->first_line.u.request.method_value == METHOD_INVITE) { if (dst_leg == DLG_CALLER_LEG) src_leg = callee_idx(dlg); else src_leg = DLG_CALLER_LEG; if (update_cseqs(dlg,req,src_leg,1) != 0) { ok=0; LM_ERR("failed to update inv cseq on leg %d\n",src_leg); } if (dlg->flags & DLG_FLAG_REINVITE_PING_CALLER || dlg->flags & DLG_FLAG_REINVITE_PING_CALLEE ) { /* we need to update the SDP for this leg and involve TM to update the SDP for the other side as well */ if(d_tmb.register_tmcb( req, 0, TMCB_REQUEST_BUILT, (dir==DLG_DIR_UPSTREAM)?dlg_callee_reinv_onreq_out:dlg_caller_reinv_onreq_out, (void *)dlg, 0) <=0) { LM_ERR("can't register trace_onreq_out\n"); ok = 0; } if (ok) { ref_dlg( dlg , 1); if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_OUT, (dir==DLG_DIR_UPSTREAM)?dlg_update_caller_sdp:dlg_update_callee_sdp, (void*)dlg, unreference_dialog)<0 ) { LM_ERR("failed to register TMCB (2)\n"); unref_dlg( dlg , 1); } } } } if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE || dlg->flags & DLG_FLAG_REINVITE_PING_CALLER || dlg->flags & DLG_FLAG_REINVITE_PING_CALLEE || dlg->flags & DLG_FLAG_CSEQ_ENFORCE ) { dlg_lock (d_table, d_entry); if (dlg->legs[dst_leg].last_gen_cseq) { update_val = ++(dlg->legs[dst_leg].last_gen_cseq); if (req->first_line.u.request.method_value == METHOD_INVITE) { /* save INVITE cseq, in case any requests follow after this ( pings or other in-dialog requests until the ACK comes in */ dlg->legs[dst_leg].last_inv_gen_cseq = dlg->legs[dst_leg].last_gen_cseq; } dlg_unlock( d_table, d_entry ); if (update_msg_cseq(req,0,update_val) != 0) { LM_ERR("failed to update sequential request msg cseq\n"); ok = 0; } } else { if (req->first_line.u.request.method_value == METHOD_INVITE) { /* we did not generate any pings yet - still we need to store the INV cseq, in case there's a race between the ACK for the INVITE and sending of new pings */ str2int(&((struct cseq_body *)req->cseq->parsed)->number, &dlg->legs[dst_leg].last_inv_gen_cseq); } dlg_unlock( d_table, d_entry ); } } if (ok) { dlg->flags |= DLG_FLAG_CHANGED; if ( dlg_db_mode==DB_MODE_REALTIME ) update_dialog_dbinfo(dlg); if (dialog_replicate_cluster) replicate_dialog_updated(dlg); } } else { if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE || dlg->flags & DLG_FLAG_REINVITE_PING_CALLER || dlg->flags & DLG_FLAG_REINVITE_PING_CALLEE || dlg->flags & DLG_FLAG_CSEQ_ENFORCE) { dlg_lock (d_table, d_entry); if (dlg->legs[dst_leg].last_gen_cseq || dlg->legs[dst_leg].last_inv_gen_cseq ) { if (dlg->legs[dst_leg].last_inv_gen_cseq) update_val = dlg->legs[dst_leg].last_inv_gen_cseq; else update_val = dlg->legs[dst_leg].last_gen_cseq; dlg_unlock( d_table, d_entry ); if (update_msg_cseq(req,0,update_val) != 0) { LM_ERR("failed to update ACK msg cseq\n"); } } else dlg_unlock( d_table, d_entry ); } } if ( event!=DLG_EVENT_REQACK) { /* register callback for the replies of this request */ if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE || dlg->flags & DLG_FLAG_REINVITE_PING_CALLER || dlg->flags & DLG_FLAG_REINVITE_PING_CALLEE || dlg->flags & DLG_FLAG_CSEQ_ENFORCE) { dlg_lock( d_table, d_entry); if (dlg->legs[dst_leg].last_gen_cseq) { /* ref the dialog as registered into the transaction callback. * unref will be done when the callback will be destroyed */ ref_dlg_unsafe( dlg, 1); dlg_unlock( d_table,d_entry); if(parse_headers(req, HDR_CSEQ_F, 0) <0 ) { LM_ERR("failed to parse cseq header \n"); unref_dlg(dlg,1); goto early_check; } msg_cseq = &((struct cseq_body *)req->cseq->parsed)->number; dlg_cseq_wrapper *wrap = shm_malloc(sizeof(dlg_cseq_wrapper) + msg_cseq->len); if (wrap == 0){ LM_ERR("No more shm mem\n"); unref_dlg(dlg, 1); goto early_check; } wrap->dlg = dlg; wrap->cseq.s = (char *)(wrap + 1); wrap->cseq.len = msg_cseq->len; memcpy(wrap->cseq.s,msg_cseq->s,msg_cseq->len); if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_FWDED, (dir==DLG_DIR_UPSTREAM)?dlg_seq_down_onreply_mod_cseq:dlg_seq_up_onreply_mod_cseq, (void*)wrap, unreference_dialog_cseq)<0 ) { LM_ERR("failed to register TMCB (2)\n"); unref_dlg( dlg , 1); shm_free(wrap); } } else { /* dialog is in ping timer list * but no pings have been generated yet */ dlg_unlock ( d_table, d_entry ); goto regular_indlg_req; } } else { regular_indlg_req: if (dlg->cbs.types & DLGCB_RESPONSE_WITHIN) { ref_dlg( dlg , 1); if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_FWDED, (dir==DLG_DIR_UPSTREAM)?dlg_seq_down_onreply:dlg_seq_up_onreply, (void*)dlg, unreference_dialog)<0 ) { LM_ERR("failed to register TMCB (2)\n"); unref_dlg( dlg , 1); } } } } } early_check: if ( (event==DLG_EVENT_REQPRACK || event == DLG_EVENT_REQ || event == DLG_EVENT_REQBYE) && new_state==DLG_STATE_EARLY) { /* within dialog request */ run_dlg_callbacks( DLGCB_REQ_WITHIN, dlg, req, dir, 0); LM_DBG("EARLY event %d successfully processed (dst_leg=%d)\n",event,dst_leg); if (dst_leg==-1 || switch_cseqs(dlg, dst_leg) != 0 || update_cseqs(dlg,req,dst_leg,0)) LM_ERR("cseqs update failed on leg=%d\n",dst_leg); } if(new_state==DLG_STATE_CONFIRMED && old_state==DLG_STATE_CONFIRMED_NA){ dlg->flags |= DLG_FLAG_CHANGED; if(dlg_db_mode == DB_MODE_REALTIME) update_dialog_dbinfo(dlg); if (dialog_replicate_cluster) replicate_dialog_updated(dlg); } return; } #define get_dlg_tl_payload(_tl_) ((struct dlg_cell*)((char *)(_tl_)- \ (unsigned long)(&((struct dlg_cell*)0)->tl))) /* When done, this function also has the job to unref the dialog as removed * from timer list. This must be done in all cases!! */ void dlg_ontimeout( struct dlg_tl *tl) { struct sip_msg *fake_msg; context_p old_ctx; context_p *new_ctx; struct dlg_cell *dlg; int new_state; int old_state; int unref; dlg = get_dlg_tl_payload(tl); LM_DBG("byeontimeout ? %d , state = %d\n",dlg->flags,dlg->state); if ( (dlg->flags&DLG_FLAG_BYEONTIMEOUT) && (dlg->state==DLG_STATE_CONFIRMED_NA || dlg->state==DLG_STATE_CONFIRMED)) { init_dlg_term_reason(dlg,"Lifetime Timeout",sizeof("Lifetime Timeout")-1); /* we just send the BYEs in both directions */ dlg_end_dlg( dlg, NULL); /* dialog is no longer refed by timer; from now one it is refed by the send_bye functions */ unref_dlg( dlg, 1); /* is not 100% sure, but do it */ if_update_stat( dlg_enable_stats, expired_dlgs, 1); return ; } /* act like as if we've received a BYE from caller */ next_state_dlg( dlg, DLG_EVENT_REQBYE, DLG_DIR_DOWNSTREAM, &old_state, &new_state, &unref, dlg->legs_no[DLG_LEG_200OK], 0); if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) { LM_DBG("timeout for dlg with CallID '%.*s' and tags '%.*s' '%.*s'\n", dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); /*destroy linkers */ dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); /* dialog timeout */ if (push_new_processing_context( dlg, &old_ctx, &new_ctx, &fake_msg)==0) { run_dlg_callbacks( DLGCB_EXPIRED, dlg, fake_msg, DLG_DIR_NONE, 0); if (current_processing_ctx == NULL) *new_ctx = NULL; else context_destroy(CONTEXT_GLOBAL, *new_ctx); /* reset the processing context */ current_processing_ctx = old_ctx; } /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); unref_dlg(dlg, unref + 1 /*timer list*/); if_update_stat( dlg_enable_stats, expired_dlgs, 1); if_update_stat( dlg_enable_stats, active_dlgs, -1); } else { unref_dlg(dlg, 1 /*just timer list*/); } return; } #define ROUTE_STR "Route: " #define CRLF "\r\n" #define ROUTE_LEN (sizeof(ROUTE_STR) - 1) #define CRLF_LEN (sizeof(CRLF) - 1) #define ROUTE_PREF "Route: <" #define ROUTE_PREF_LEN (sizeof(ROUTE_PREF) -1) #define ROUTE_SUFF ">\r\n" #define ROUTE_SUFF_LEN (sizeof(ROUTE_SUFF) -1) int fix_route_dialog(struct sip_msg *req,struct dlg_cell *dlg) { struct dlg_leg *leg; struct hdr_field *it; char * buf,*route,*hdrs,*remote_contact; struct lump* lmp = NULL; int size; rr_t *head = NULL; struct sip_uri fru; int next_strict = 0; if (ctx_lastdstleg_get()<0 || ctx_lastdstleg_get()>=dlg->legs_no[DLG_LEGS_USED]) { log_bogus_dst_leg(dlg); LM_ERR("Script error - validate function before having a dialog\n"); return -1; } leg = & dlg->legs[ ctx_lastdstleg_get() ]; /* check in the stored routes */ if ( leg->route_set.len && leg->route_set.s) { if(parse_uri(leg->route_uris[0].s, leg->route_uris[0].len, &fru) < 0) { LM_ERR("Failed to parse SIP uri\n"); return -1; } LM_DBG("Next params [%.*s]\n", fru.params.len, fru.params.s); if(is_strict(&fru.params)) next_strict = 1; } if (req->dst_uri.s && req->dst_uri.len) { /* reset dst_uri if previously set * either by loose route or manually */ pkg_free(req->dst_uri.s); req->dst_uri.s = NULL; req->dst_uri.len = 0; } /* r-uri is taken care of in all below cases, * no need for manual resetting */ //if ((*(d_rrb.routing_type) == ROUTING_LL) || (*d_rrb.routing_type) == ROUTING_SL ) if (!next_strict) { LM_DBG("Fixing message. Next hop is Loose router\n"); if (leg->contact.len && leg->contact.s) { LM_DBG("Setting new URI to <%.*s> \n",leg->contact.len, leg->contact.s); if (set_ruri(req,&leg->contact) != 0) { LM_ERR("failed setting ruri\n"); return -1; } } if( parse_headers( req, HDR_EOH_F, 0)<0 ) { LM_ERR("failed to parse headers when looking after ROUTEs\n"); return -1; } buf = req->buf; if (req->route) { for (it=req->route;it;it=it->sibling) { if (it->parsed && ((rr_t*)it->parsed)->deleted) continue; if ((lmp = del_lump(req,it->name.s - buf,it->len,HDR_ROUTE_T)) == 0) { LM_ERR("del_lump failed \n"); return -1; } } } if ( leg->route_set.len !=0 && leg->route_set.s) { lmp = anchor_lump(req,req->headers->name.s - buf,0); if (lmp == 0) { LM_ERR("failed anchoring new lump\n"); return -1; } size = leg->route_set.len + ROUTE_LEN + CRLF_LEN; route = pkg_malloc(size+1); if (route == 0) { LM_ERR("no more pkg memory\n"); return -1; } memcpy(route,ROUTE_STR,ROUTE_LEN); memcpy(route+ROUTE_LEN,leg->route_set.s,leg->route_set.len); memcpy(route+ROUTE_LEN+leg->route_set.len,CRLF,CRLF_LEN); route[size] = 0; if ((lmp = insert_new_lump_after(lmp,route,size,HDR_ROUTE_T)) == 0) { LM_ERR("failed inserting new route set\n"); pkg_free(route); return -1; } LM_DBG("Setting route header to <%s> \n",route); if (parse_rr_body(leg->route_set.s,leg->route_set.len,&head) != 0) { LM_ERR("failed parsing route set\n"); return -1; } LM_DBG("setting dst_uri to <%.*s> \n",head->nameaddr.uri.len, head->nameaddr.uri.s); if (set_dst_uri(req,&head->nameaddr.uri) !=0 ) { LM_ERR("failed setting new dst uri\n"); free_rr(&head); return -1; } free_rr(&head); } } else { LM_DBG("Fixing message. Next hop is Strict router\n"); if( parse_headers( req, HDR_EOH_F, 0)<0 ) { LM_ERR("failed to parse headers when looking after ROUTEs\n"); return -1; } buf = req->buf; if (req->route) { for (it=req->route;it;it=it->sibling) if (it->parsed && ((rr_t*)it->parsed)->deleted) continue; if ((lmp = del_lump(req,it->name.s - buf,it->len,HDR_ROUTE_T)) == 0) { LM_ERR("del_lump failed \n"); return -1; } } if ( leg->route_set.len !=0 && leg->route_set.s) { LM_DBG("setting R-URI to <%.*s> \n",leg->route_uris->len, leg->route_uris->s); if (set_ruri(req,leg->route_uris) !=0 ) { LM_ERR("failed setting new dst uri\n"); return -1; } /* If there are more routes other than the first, add them */ if (leg->nr_uris > 1) { /* FIXME - find a better way to skip the first route header. * Instead or parsing again the entire route set, maybe remmember * the needed pointer at the initial parsing of the route_set */ if (parse_rr_body(leg->route_set.s,leg->route_set.len,&head) != 0) { LM_ERR("failed parsing route set\n"); return -1; } lmp = anchor_lump(req,req->headers->name.s - buf,0); if (lmp == 0) { LM_ERR("failed anchoring new lump\n"); free_rr(&head); return -1; } hdrs = leg->route_set.s + head->len + 1; size = leg->route_set.len - head->len - 1 + ROUTE_LEN + CRLF_LEN; route = pkg_malloc(size); if (route == 0) { LM_ERR("no more pkg memory\n"); free_rr(&head); return -1; } memcpy(route,ROUTE_STR,ROUTE_LEN); memcpy(route+ROUTE_LEN,hdrs,leg->route_set.len - head->len-1); memcpy(route+ROUTE_LEN+leg->route_set.len - head->len-1,CRLF,CRLF_LEN); LM_DBG("Adding Route header : [%.*s] \n",size,route); if ((lmp = insert_new_lump_after(lmp,route,size,HDR_ROUTE_T)) == 0) { LM_ERR("failed inserting new route set\n"); pkg_free(route); free_rr(&head); return -1; } free_rr(&head); } if (lmp == NULL) { lmp = anchor_lump(req,req->headers->name.s - buf,0); if (lmp == 0) { LM_ERR("failed anchoring new lump\n"); return -1; } } if (leg->contact.len && leg->contact.s) { size = leg->contact.len + ROUTE_PREF_LEN + ROUTE_SUFF_LEN; remote_contact = pkg_malloc(size); if (remote_contact == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memcpy(remote_contact,ROUTE_PREF,ROUTE_PREF_LEN); memcpy(remote_contact+ROUTE_PREF_LEN,leg->contact.s,leg->contact.len); memcpy(remote_contact+ROUTE_PREF_LEN+leg->contact.len, ROUTE_SUFF,ROUTE_SUFF_LEN); LM_DBG("Adding remote contact route header : [%.*s]\n", size,remote_contact); if (insert_new_lump_after(lmp,remote_contact,size,HDR_ROUTE_T) == 0) { LM_ERR("failed inserting remote contact route\n"); pkg_free(remote_contact); return -1; } } } } return 0; } int dlg_validate_dialog( struct sip_msg* req, struct dlg_cell *dlg) { struct dlg_leg *leg; unsigned int n,m; int nr_routes,i,src_leg; str *rr_uri,*route_uris; if (ctx_lastdstleg_get()<0 || ctx_lastdstleg_get()>=dlg->legs_no[DLG_LEGS_USED]) { log_bogus_dst_leg(dlg); LM_ERR("Script error - validate function before having a dialog\n"); return -4; } leg = & dlg->legs[ ctx_lastdstleg_get() ]; /* first check the cseq */ if ( (!req->cseq && parse_headers(req,HDR_CSEQ_F,0)<0) || !req->cseq || !req->cseq->parsed) { LM_ERR("bad sip message or missing CSeq hdr :-/\n"); return -4; } n = m = 0; if (req->first_line.u.request.method_value == METHOD_ACK) { /* ACKs should have the same cseq as INVITEs */ if (ctx_lastdstleg_get() == DLG_CALLER_LEG) src_leg = callee_idx(dlg); else src_leg = DLG_CALLER_LEG; if ( str2int( &((get_cseq(req))->number), &n)!=0 || str2int( &(dlg->legs[src_leg].inv_cseq), &m)!=0 || n!=m ) { LM_DBG("cseq test for ACK falied recv=%d, old=%d\n",n,m); return -1; } } else { if ( str2int( &((get_cseq(req))->number), &n)!=0 || (leg->prev_cseq.s ? str2int( &(leg->prev_cseq), &m)!=0 : str2int( &(leg->r_cseq), &m)!=0 ) || n<=m ) { LM_DBG("cseq test falied recv=%d, old=%d\n",n,m); return -1; } } LM_DBG("CSEQ validation passed\n"); /* because fix_routing was called on the request */ if(dlg->mod_flags & TOPOH_ONGOING) return 0; if (dlg->state <= DLG_STATE_EARLY) return 0; if (leg->contact.len) { rr_uri = d_rrb.get_remote_target(req); if (rr_uri == NULL) { LM_ERR("failed fetching remote target from msg\n"); return -4; } if (compare_uris(rr_uri,0,&leg->contact,0)) { LM_ERR("failed to validate remote contact: dlg=[%.*s] , req=[%.*s]\n", leg->contact.len,leg->contact.s,rr_uri->len,rr_uri->s); return -2; } } LM_DBG("Remote contact successfully validated\n"); /* check the route set - is the the same as in original request */ /* the route set (without the first Route) must be the same as the one stored in the destination leg */ /* extract the RR parts */ if( parse_headers( req, HDR_EOH_F, 0)<0 ) { LM_ERR("failed to parse headers when looking after ROUTEs\n"); return -4; } if ( req->route==NULL) { if ( leg->route_set.len!=0) { LM_DBG("route check failed (req has no route, but dialog has\n"); return -3; } } else { route_uris = d_rrb.get_route_set(req,&nr_routes); if (route_uris == NULL) { LM_ERR("failed fetching route URIs from the msg\n"); return -4; } if (nr_routes != leg->nr_uris) { LM_ERR("Different number of routes found in msg. req=%d, dlg=%d\n", nr_routes,leg->nr_uris); return -3; } for (i=0;iroute_uris[i].len, leg->route_uris[i].s); if (compare_uris(&route_uris[i],0,&leg->route_uris[i],0)) { LM_ERR("Check failed for route number %d. req=[%.*s],dlg=[%.*s]\n", i,route_uris[i].len,route_uris[i].s,leg->route_uris[i].len, leg->route_uris[i].s); return -3; } } } LM_DBG("Route Headers successfully validated\n"); return 0; } int terminate_dlg(unsigned int h_entry, unsigned int h_id,str *reason) { struct dlg_cell * dlg = NULL; int ret = 0; dlg = lookup_dlg(h_entry, h_id); if(!dlg) return 0; init_dlg_term_reason(dlg,reason->s,reason->len); if ( dlg_end_dlg( dlg, 0) ) { LM_ERR("Failed to end dialog"); ret = -1; } unref_dlg(dlg, 1); return ret; } int test_and_set_dlg_flag(struct dlg_cell *dlg, unsigned long index, unsigned long value) { int ret = -1; struct dlg_entry *d_entry = NULL; if (index > 31) { LM_ERR("invalid index %lu\n", index); goto end; } if (value > 1) { LM_ERR("Only binary values accepted - received %lu\n", value); goto end; } value = value << index; index = 1 << index; d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock (d_table,d_entry); if ((dlg->user_flags & index) == value) { ret = 1; if (value) dlg->user_flags &= ~index; else dlg->user_flags |= index; } dlg_unlock (d_table,d_entry); end: return ret; } opensips-2.2.2/modules/dialog/dlg_handlers.h000066400000000000000000000121721300170765700210740ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-14 initial version (bogdan) * 2007-03-06 syncronized state machine added for dialog state. New tranzition * design based on events; removed num_1xx and num_2xx (bogdan) */ #ifndef _DIALOG_DLG_HANDLERS_H_ #define _DIALOG_DLG_HANDLERS_H_ #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../trim.h" #include "../../str.h" #include "../../pvar.h" #include "../../ut.h" #include "../tm/t_hooks.h" #include "dlg_timer.h" #define MAX_DLG_RR_PARAM_NAME 32 /* values for the sequential match mode */ #define SEQ_MATCH_STRICT_ID 0 #define SEQ_MATCH_FALLBACK 1 #define SEQ_MATCH_NO_ID 2 #define RR_DLG_PARAM_SIZE (2*2*sizeof(int)+3+MAX_DLG_RR_PARAM_NAME) #define DLG_SEPARATOR '.' struct _dlg_cseq{ struct dlg_cell *dlg; str cseq; }; typedef struct _dlg_cseq dlg_cseq_wrapper; typedef int (*create_dlg_f)(struct sip_msg *req,int flags); typedef void (*set_mod_flag_f)(struct dlg_cell *dlg, unsigned int flags); typedef int (*is_mod_flag_set_f)(struct dlg_cell *dlg, unsigned int flags); typedef void (*ref_dlg_f)(struct dlg_cell *dlg, unsigned int cnt); typedef void (*unref_dlg_f)(struct dlg_cell *dlg, unsigned int cnt); typedef str* (*get_rr_param_f)(void); extern int ctx_timeout_idx; #define ctx_timeout_get() \ context_get_int(CONTEXT_GLOBAL,current_processing_ctx,ctx_timeout_idx) #define ctx_timeout_set(_timeout) \ context_put_int(CONTEXT_GLOBAL,current_processing_ctx, ctx_timeout_idx, _timeout) /* IMPORTANT - as the default value for INT in context is 0, we shift the last leg idx with +1 to avoid having idx 0; this shifting is hidden by the get / set functions, so transparent for the usage */ extern int ctx_lastdstleg_idx; #define ctx_lastdstleg_get() \ (context_get_int(CONTEXT_GLOBAL,current_processing_ctx,ctx_lastdstleg_idx)-1) #define ctx_lastdstleg_set(_lastleg) \ context_put_int(CONTEXT_GLOBAL,current_processing_ctx, ctx_lastdstleg_idx, _lastleg+1) void init_dlg_handlers(int default_timeout); void destroy_dlg_handlers(); int dlg_create_dialog(struct cell* t, struct sip_msg *req,unsigned int flags); void dlg_onreq(struct cell* t, int type, struct tmcb_params *param); void dlg_onroute(struct sip_msg* req, str *rr_param, void *param); void dlg_ontimeout( struct dlg_tl *tl); typedef int (*validate_dialog_f) (struct sip_msg* req, struct dlg_cell *dlg); int dlg_validate_dialog( struct sip_msg* req, struct dlg_cell *dlg); typedef int (*fix_route_dialog_f) (struct sip_msg *req,struct dlg_cell *dlg); int fix_route_dialog(struct sip_msg *req,struct dlg_cell *dlg); int terminate_dlg(unsigned int h_entry, unsigned int h_id,str *reason); typedef int (*terminate_dlg_f)(unsigned int h_entry, unsigned int h_id,str *reason); void unreference_dialog(void *dialog); static inline int parse_dlg_rr_param(char *p, char *end, int *h_entry, int *h_id) { char *s; for ( s=p ; pcallid || !req->to || !req->from) { LM_ERR("bad request or missing CALLID/TO hdr :-/\n"); return -1; } if (get_to(req)->tag_value.len==0) { /* out of dialog request with preloaded Route headers; ignore. */ return -1; } if (parse_from_header(req)<0 || get_from(req)->tag_value.len==0) { LM_ERR("failed to get From header(%.*s) (hdr=%p,parsed=%p,tag_len=%d) " "callid=<%.*s>\n",req->from->body.len, req->from->body.s, req->from, req->from?req->from->parsed:NULL, req->from?(req->from->parsed?get_from(req)->tag_value.len:0):0, req->callid->body.len, req->callid->body.s); return -1; } /* callid */ *callid = req->callid->body; trim(callid); /* to tag */ *ttag = get_to(req)->tag_value; /* from tag */ *ftag = get_from(req)->tag_value; return 0; } int test_and_set_dlg_flag(struct dlg_cell *dlg, unsigned long index, unsigned long value); #endif opensips-2.2.2/modules/dialog/dlg_hash.c000066400000000000000000001140621300170765700202130ustar00rootroot00000000000000/* * Copyright (C) 2006-2009 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-14 initial version (bogdan) * 2007-03-06 syncronized state machine added for dialog state. New tranzition * design based on events; removed num_1xx and num_2xx (bogdan) * 2007-04-30 added dialog matching without DID (dialog ID), but based only * on RFC3261 elements - based on an original patch submitted * by Michel Bensoussan (bogdan) * 2007-07-06 additional information stored in order to save it in the db: * cseq, route_set, contact and sock_info for both caller and * callee (ancuta) * 2007-07-10 Optimized dlg_match_mode 2 (DID_NONE), it now employs a proper * hash table lookup and isn't dependant on the is_direction * function (which requires an RR param like dlg_match_mode 0 * anyways.. ;) ; based on a patch from * Tavis Paquette * and Peter Baer (bogdan) * 2008-04-17 added new type of callback to be triggered right before the * dialog is destroyed (deleted from memory) (bogdan) * 2008-04-17 added new dialog flag to avoid state tranzitions from DELETED to * CONFIRMED_NA due delayed "200 OK" (bogdan) * 2009-09-09 support for early dialogs added; proper handling of cseq * while PRACK is used (bogdan) */ #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../hash_func.h" #include "../../mi/mi.h" #include "../../route.h" #include "../../md5utils.h" #include "../../parser/parse_to.h" #include "../tm/tm_load.h" #include "../../script_cb.h" #include "dlg_hash.h" #include "dlg_profile.h" #include "dlg_replication.h" #include "../../evi/evi_params.h" #include "../../evi/evi_modules.h" #define MAX_LDG_LOCKS 2048 #define MIN_LDG_LOCKS 2 extern struct tm_binds d_tmb; struct dlg_table *d_table = NULL; int ctx_dlg_idx = 0; static inline void raise_state_changed_event( unsigned int h_entry, unsigned int h_id, unsigned int ostate, unsigned int nstate); static str ei_st_ch_name = str_init("E_DLG_STATE_CHANGED"); static evi_params_p event_params; static str ei_h_entry = str_init("hash_entry"); static str ei_h_id = str_init("hash_id"); static str ei_old_state = str_init("old_state"); static str ei_new_state = str_init("new_state"); static event_id_t ei_st_ch_id = EVI_ERROR; static evi_param_p hentry_p, hid_p, ostate_p, nstate_p; int dialog_cleanup( struct sip_msg *msg, void *param ) { if (current_processing_ctx && ctx_dialog_get()) { unref_dlg( ctx_dialog_get(), 1); ctx_dialog_set(NULL); } return SCB_RUN_ALL; } struct dlg_cell *get_current_dialog(void) { struct cell *trans; if (current_processing_ctx && ctx_dialog_get()) { /* use the processing context */ return ctx_dialog_get(); } /* look into transaction */ trans = d_tmb.t_gett(); if (trans==NULL || trans==T_UNDEFINED) { /* no transaction */ return NULL; } if (current_processing_ctx && trans->dialog_ctx) { /* if we have context, but no dlg info, and we found dlg info into transaction, populate the dialog too */ ref_dlg( trans->dialog_ctx, 1); ctx_dialog_set(trans->dialog_ctx); } return (struct dlg_cell*)trans->dialog_ctx; } int init_dlg_table(unsigned int size) { unsigned int n; unsigned int i; d_table = (struct dlg_table*)shm_malloc ( sizeof(struct dlg_table) + size*sizeof(struct dlg_entry)); if (d_table==0) { LM_ERR("no more shm mem (1)\n"); goto error0; } memset( d_table, 0, sizeof(struct dlg_table) ); d_table->size = size; d_table->entries = (struct dlg_entry*)(d_table+1); n = (size=MIN_LDG_LOCKS ; n-- ) { d_table->locks = lock_set_alloc(n); if (d_table->locks==0) continue; if (lock_set_init(d_table->locks)==0) { lock_set_dealloc(d_table->locks); d_table->locks = 0; continue; } d_table->locks_no = n; break; } if (d_table->locks==0) { LM_ERR("unable to allocted at least %d locks for the hash table\n", MIN_LDG_LOCKS); goto error1; } for( i=0 ; ientries[i]), 0, sizeof(struct dlg_entry) ); d_table->entries[i].next_id = rand(); d_table->entries[i].lock_idx = i % d_table->locks_no; } return 0; error1: shm_free( d_table ); error0: return -1; } static inline void free_dlg_dlg(struct dlg_cell *dlg) { struct dlg_val *dv; unsigned int i; if (dlg->cbs.first) destroy_dlg_callbacks_list(dlg->cbs.first); if (dlg->profile_links) destroy_linkers(dlg->profile_links, 0); if (dlg->legs) { for( i=0 ; ilegs_no[DLG_LEGS_USED] ; i++) { shm_free(dlg->legs[i].tag.s); shm_free(dlg->legs[i].r_cseq.s); if (dlg->legs[i].inv_cseq.s) shm_free(dlg->legs[i].inv_cseq.s); if (dlg->legs[i].prev_cseq.s) shm_free(dlg->legs[i].prev_cseq.s); if (dlg->legs[i].contact.s) shm_free(dlg->legs[i].contact.s); /* + route_set */ if (dlg->legs[i].th_sent_contact.s) shm_free(dlg->legs[i].th_sent_contact.s); if (dlg->legs[i].from_uri.s) shm_free(dlg->legs[i].from_uri.s); if (dlg->legs[i].to_uri.s) shm_free(dlg->legs[i].to_uri.s); if (dlg->legs[i].sdp.s) shm_free(dlg->legs[i].sdp.s); } shm_free(dlg->legs); } while (dlg->vals) { dv = dlg->vals; dlg->vals = dlg->vals->next; shm_free(dv); } if (dlg->terminate_reason.s) shm_free(dlg->terminate_reason.s); shm_free(dlg); } void destroy_dlg(struct dlg_cell *dlg) { int ret = 0; LM_DBG("destroying dialog %p\n",dlg); ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } else if (ret > 0) { LM_DBG("dlg expired or not in list - dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } run_dlg_callbacks( DLGCB_DESTROY , dlg, 0, DLG_DIR_NONE, 0); free_dlg_dlg(dlg); } void destroy_dlg_table(void) { struct dlg_cell *dlg, *l_dlg; unsigned int i; if (d_table==0) return; if (d_table->locks) { lock_set_destroy(d_table->locks); lock_set_dealloc(d_table->locks); } for( i=0 ; isize; i++ ) { dlg = d_table->entries[i].first; while (dlg) { l_dlg = dlg; dlg = dlg->next; free_dlg_dlg(l_dlg); } } shm_free(d_table); d_table = 0; return; } struct dlg_cell* build_new_dlg( str *callid, str *from_uri, str *to_uri, str *from_tag) { struct dlg_cell *dlg; int len; char *p; len = sizeof(struct dlg_cell) + callid->len + from_uri->len + to_uri->len; dlg = (struct dlg_cell*)shm_malloc( len ); if (dlg==0) { LM_ERR("no more shm mem (%d)\n",len); return 0; } memset( dlg, 0, len); dlg->state = DLG_STATE_UNCONFIRMED; dlg->h_entry = dlg_hash( callid); LM_DBG("new dialog %p (c=%.*s,f=%.*s,t=%.*s,ft=%.*s) on hash %u\n", dlg, callid->len,callid->s, from_uri->len, from_uri->s, to_uri->len,to_uri->s, from_tag->len, from_tag->s, dlg->h_entry); p = (char*)(dlg+1); dlg->callid.s = p; dlg->callid.len = callid->len; memcpy( p, callid->s, callid->len); p += callid->len; dlg->from_uri.s = p; dlg->from_uri.len = from_uri->len; memcpy( p, from_uri->s, from_uri->len); p += from_uri->len; dlg->to_uri.s = p; dlg->to_uri.len = to_uri->len; memcpy( p, to_uri->s, to_uri->len); p += to_uri->len; return dlg; } /* first time it will called for a CALLER leg - at that time there will be no leg allocated, so automatically CALLER gets the first position, while the CALLEE legs will follow into the array in the same order they came */ int dlg_add_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact,str *cseq, struct socket_info *sock, str *mangled_from,str *mangled_to,str *sdp) { struct dlg_leg* leg,*new_legs; rr_t *head = NULL, *rrp; if ( (dlg->legs_no[DLG_LEGS_ALLOCED]-dlg->legs_no[DLG_LEGS_USED])==0) { new_legs = (struct dlg_leg*)shm_realloc(dlg->legs, (dlg->legs_no[DLG_LEGS_ALLOCED]+2)*sizeof(struct dlg_leg)); if (new_legs==NULL) { LM_ERR("Failed to resize legs array\n"); return -1; } dlg->legs=new_legs; dlg->legs_no[DLG_LEGS_ALLOCED] += 2; memset( dlg->legs+dlg->legs_no[DLG_LEGS_ALLOCED]-2, 0, 2*sizeof(struct dlg_leg)); } leg = &dlg->legs[ dlg->legs_no[DLG_LEGS_USED] ]; leg->tag.s = (char*)shm_malloc(tag->len); leg->r_cseq.s = (char*)shm_malloc( cseq->len ); if ( leg->tag.s==NULL || leg->r_cseq.s==NULL) { LM_ERR("no more shm mem\n"); if (leg->tag.s) shm_free(leg->tag.s); if (leg->r_cseq.s) shm_free(leg->r_cseq.s); return -1; } if (dlg->legs_no[DLG_LEGS_USED] == 0) { /* first leg = caller. also store inv cseq */ leg->inv_cseq.s = (char *)shm_malloc( cseq->len); if (leg->inv_cseq.s == NULL) { LM_ERR("no more shm mem\n"); shm_free(leg->tag.s); shm_free(leg->r_cseq.s); return -1; } } if (contact->len) { /* contact */ leg->contact.s = shm_malloc(rr->len + contact->len); if (leg->contact.s==NULL) { LM_ERR("no more shm mem\n"); shm_free(leg->tag.s); shm_free(leg->r_cseq.s); return -1; } leg->contact.len = contact->len; memcpy( leg->contact.s, contact->s, contact->len); /* rr */ if (rr->len) { leg->route_set.s = leg->contact.s + contact->len; leg->route_set.len = rr->len; memcpy( leg->route_set.s, rr->s, rr->len); if (parse_rr_body(leg->route_set.s,leg->route_set.len,&head) != 0) { LM_ERR("failed parsing route set\n"); shm_free(leg->tag.s); shm_free(leg->r_cseq.s); shm_free(leg->contact.s); return -1; } rrp = head; leg->nr_uris = 0; while (rrp) { leg->route_uris[leg->nr_uris++] = rrp->nameaddr.uri; rrp = rrp->next; } free_rr(&head); } } /* save mangled from URI, if any */ if (mangled_from && mangled_from->s && mangled_from->len) { leg->from_uri.s = shm_malloc(mangled_from->len); if (!leg->from_uri.s) { LM_ERR("no more shm\n"); shm_free(leg->tag.s); shm_free(leg->r_cseq.s); if (leg->contact.s) shm_free(leg->contact.s); return -1; } leg->from_uri.len = mangled_from->len; memcpy(leg->from_uri.s,mangled_from->s,mangled_from->len); } if (mangled_to && mangled_to->s && mangled_to->len) { leg->to_uri.s = shm_malloc(mangled_to->len); if (!leg->to_uri.s) { LM_ERR("no more shm\n"); shm_free(leg->tag.s); shm_free(leg->r_cseq.s); if (leg->contact.s) shm_free(leg->contact.s); if (leg->from_uri.s) shm_free(leg->from_uri.s); return -1; } leg->to_uri.len = mangled_to->len; memcpy(leg->to_uri.s,mangled_to->s,mangled_to->len); } if (sdp && sdp->s && sdp->len) { leg->sdp.s = shm_malloc(sdp->len); if (!leg->sdp.s) { LM_ERR("no more shm\n"); shm_free(leg->tag.s); shm_free(leg->r_cseq.s); if (leg->contact.s) shm_free(leg->contact.s); if (leg->from_uri.s) shm_free(leg->from_uri.s); return -1; } leg->sdp.len = sdp->len; memcpy(leg->sdp.s,sdp->s,sdp->len); } /* tag */ leg->tag.len = tag->len; memcpy( leg->tag.s, tag->s, tag->len); /* socket */ leg->bind_addr = sock; if (dlg->legs_no[DLG_LEGS_USED] == 0) { /* first leg = caller . store inv cseq */ leg->inv_cseq.len = cseq->len; memcpy(leg->inv_cseq.s,cseq->s,cseq->len); /* set cseq for caller to 0 * future requests to the caller leg will update this * needed for proper validation of in-dialog requests * * TM also increases this value by one, if dialog * is terminated from the middle, so 0 is ok*/ leg->r_cseq.len = 1; leg->r_cseq.s[0]='0'; } else { /* cseq */ leg->r_cseq.len = cseq->len; memcpy( leg->r_cseq.s, cseq->s, cseq->len); } /* make leg visible for searchers */ dlg->legs_no[DLG_LEGS_USED]++; LM_DBG("set leg %d for %p: tag=<%.*s> rcseq=<%.*s>\n", dlg->legs_no[DLG_LEGS_USED]-1, dlg, leg->tag.len,leg->tag.s, leg->r_cseq.len,leg->r_cseq.s ); return 0; } /* update cseq filed in leg * if inv = 1, update the inv_cseq field * else, update the r_cseq */ int dlg_update_cseq(struct dlg_cell * dlg, unsigned int leg, str *cseq,int inv) { str* update_cseq; if (inv == 1) update_cseq = &dlg->legs[leg].inv_cseq; else update_cseq = &dlg->legs[leg].r_cseq; if ( update_cseq->s ) { if (update_cseq->len < cseq->len) { update_cseq->s = (char*)shm_realloc(update_cseq->s,cseq->len); if (update_cseq->s==NULL) { LM_ERR("no more shm mem for realloc (%d)\n",cseq->len); goto error; } } } else { update_cseq->s = (char*)shm_malloc(cseq->len); if (update_cseq->s==NULL) { LM_ERR("no more shm mem for malloc (%d)\n",cseq->len); goto error; } } memcpy( update_cseq->s, cseq->s, cseq->len ); update_cseq->len = cseq->len; if (inv == 1) LM_DBG("dlg %p[%d]: last invite cseq is %.*s\n", dlg,leg, dlg->legs[leg].inv_cseq.len, dlg->legs[leg].inv_cseq.s); else LM_DBG("dlg %p[%d]: cseq is %.*s\n", dlg,leg, dlg->legs[leg].r_cseq.len, dlg->legs[leg].r_cseq.s); return 0; error: LM_ERR("not more shm mem\n"); return -1; } int dlg_update_routing(struct dlg_cell *dlg, unsigned int leg, str *rr, str *contact) { rr_t *head = NULL, *rrp; LM_DBG("dialog %p[%d]: rr=<%.*s> contact=<%.*s>\n", dlg, leg, rr->len,rr->s, contact->len,contact->s ); if (dlg->legs[leg].contact.s) shm_free(dlg->legs[leg].contact.s); dlg->legs[leg].contact.s = shm_malloc(rr->len + contact->len); if (dlg->legs[leg].contact.s==NULL) { LM_ERR("no more shm mem\n"); return -1; } dlg->legs[leg].contact.len = contact->len; memcpy( dlg->legs[leg].contact.s, contact->s, contact->len); /* rr */ if (rr->len) { dlg->legs[leg].route_set.s = dlg->legs[leg].contact.s + contact->len; dlg->legs[leg].route_set.len = rr->len; memcpy( dlg->legs[leg].route_set.s, rr->s, rr->len); /* also update URI pointers */ if (parse_rr_body(dlg->legs[leg].route_set.s, dlg->legs[leg].route_set.len,&head) != 0) { LM_ERR("failed parsing route set\n"); shm_free(dlg->legs[leg].contact.s); return -1; } rrp = head; dlg->legs[leg].nr_uris = 0; while (rrp) { dlg->legs[leg].route_uris[dlg->legs[leg].nr_uris++] = rrp->nameaddr.uri; rrp = rrp->next; } free_rr(&head); } return 0; } struct dlg_cell* lookup_dlg( unsigned int h_entry, unsigned int h_id) { struct dlg_cell *dlg; struct dlg_entry *d_entry; if (h_entry>=d_table->size) goto not_found; d_entry = &(d_table->entries[h_entry]); dlg_lock( d_table, d_entry); for( dlg=d_entry->first ; dlg ; dlg=dlg->next ) { if (dlg->h_id == h_id) { if (dlg->state==DLG_STATE_DELETED) { dlg_unlock( d_table, d_entry); goto not_found; } dlg->ref++; LM_DBG("ref dlg %p with 1 -> %d\n", dlg, dlg->ref); dlg_unlock( d_table, d_entry); LM_DBG("dialog id=%u found on entry %u\n", h_id, h_entry); return dlg; } } dlg_unlock( d_table, d_entry); not_found: LM_DBG("no dialog id=%u found on entry %u\n", h_id, h_entry); return 0; } /* Get dialog that correspond to CallId, From Tag and To Tag */ /* See RFC 3261, paragraph 4. Overview of Operation: */ /* "The combination of the To tag, From tag, and Call-ID completely */ /* defines a peer-to-peer SIP relationship between [two UAs] and is */ /* referred to as a dialog."*/ struct dlg_cell* get_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir, unsigned int *dst_leg) { struct dlg_cell *dlg; struct dlg_entry *d_entry; unsigned int h_entry; h_entry = dlg_hash(callid); d_entry = &(d_table->entries[h_entry]); dlg_lock( d_table, d_entry); LM_DBG("input ci=<%.*s>(%d), tt=<%.*s>(%d), ft=<%.*s>(%d)\n", callid->len,callid->s, callid->len, ftag->len, ftag->s, ftag->len, ttag->len, ttag->s, ttag->len); for( dlg = d_entry->first ; dlg ; dlg = dlg->next ) { /* Check callid / fromtag / totag */ #ifdef EXTRA_DEBUG LM_DBG("DLG (%p)(%d): ci=<%.*s>(%d), ft=<%.*s>(%d), tt=<%.*s>(%d)," "ct_er=%d, ct_ee=%d\n", dlg,dlg->state,dlg->callid.len,dlg->callid.s, dlg->callid.len, dlg->legs[DLG_CALLER_LEG].tag.len,dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[callee_idx(dlg)].tag.len,dlg->legs[callee_idx(dlg)].tag.s, dlg->legs[callee_idx(dlg)].tag.len, dlg->legs[DLG_CALLER_LEG].contact.len, dlg->legs[DLG_CALLER_LEG].contact.len); #endif if (match_dialog( dlg, callid, ftag, ttag, dir, dst_leg)==1) { if (dlg->state==DLG_STATE_DELETED) /* even if matched, skip the deleted dialogs as they may be a previous unsuccessfull attempt of established call with the same callid and fromtag - like in auth/challenge case -bogdan */ continue; dlg->ref++; LM_DBG("ref dlg %p with 1 -> %d\n", dlg, dlg->ref); dlg_unlock( d_table, d_entry); LM_DBG("dialog callid='%.*s' found\n on entry %u, dir=%d\n", callid->len, callid->s,h_entry,*dir); return dlg; } } dlg_unlock( d_table, d_entry); LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s); return 0; } struct dlg_cell* get_dlg_by_val(str *attr, str *val) { struct dlg_entry *d_entry; struct dlg_cell *dlg; unsigned int h; /* go through all hash entries (entire table) */ for ( h=0 ; hsize ; h++ ) { d_entry = &(d_table->entries[h]); dlg_lock( d_table, d_entry); /* go through all dialogs on entry */ for( dlg = d_entry->first ; dlg ; dlg = dlg->next ) { LM_DBG("dlg in state %d to check\n",dlg->state); if ( dlg->state>DLG_STATE_CONFIRMED ) continue; if (check_dlg_value_unsafe( dlg, attr, val)==0) { ref_dlg_unsafe( dlg, 1); dlg_unlock( d_table, d_entry); return dlg; } } dlg_unlock( d_table, d_entry); } return NULL; } void link_dlg(struct dlg_cell *dlg, int n) { struct dlg_entry *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock( d_table, d_entry); dlg->h_id = d_entry->next_id++; if (d_entry->first==0) { d_entry->first = d_entry->last = dlg; } else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref += 1 + n; d_entry->cnt++; LM_DBG("ref dlg %p with %d -> %d in h_entry %p - %d \n", dlg, n+1, dlg->ref, d_entry,dlg->h_entry); dlg_unlock( d_table, d_entry); return; } void unlink_unsafe_dlg(struct dlg_entry *d_entry, struct dlg_cell *dlg) { if (dlg->next) dlg->next->prev = dlg->prev; else d_entry->last = dlg->prev; if (dlg->prev) dlg->prev->next = dlg->next; else d_entry->first = dlg->next; dlg->next = dlg->prev = 0; d_entry->cnt--; return; } void ref_dlg(struct dlg_cell *dlg, unsigned int cnt) { struct dlg_entry *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock( d_table, d_entry); ref_dlg_unsafe( dlg, cnt); dlg_unlock( d_table, d_entry); } void unref_dlg(struct dlg_cell *dlg, unsigned int cnt) { struct dlg_entry *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock( d_table, d_entry); unref_dlg_unsafe( dlg, cnt, d_entry); dlg_unlock( d_table, d_entry); } /* * create DLG_STATE_CHANGED_EVENT */ int state_changed_event_init(void) { /* publish the event */ ei_st_ch_id = evi_publish_event(ei_st_ch_name); if (ei_st_ch_id == EVI_ERROR) { LM_ERR("cannot register dialog state changed event\n"); return -1; } event_params = pkg_malloc(sizeof(evi_params_t)); if (event_params == NULL) { LM_ERR("no more pkg mem\n"); return -1; } memset(event_params, 0, sizeof(evi_params_t)); hentry_p = evi_param_create(event_params, &ei_h_entry); if (hentry_p == NULL) goto create_error; hid_p = evi_param_create(event_params, &ei_h_id); if (hid_p == NULL) goto create_error; ostate_p = evi_param_create(event_params, &ei_old_state); if (ostate_p == NULL) goto create_error; nstate_p = evi_param_create(event_params, &ei_new_state); if (nstate_p == NULL) goto create_error; return 0; create_error: LM_ERR("cannot create event parameter\n"); return -1; } /* * destroy DLG_STATE_CHANGED event */ void state_changed_event_destroy(void) { evi_free_params(event_params); } /* * raise DLG_STATE_CHANGED event */ static void raise_state_changed_event(unsigned int h_entry, unsigned int h_id, unsigned int ostate, unsigned int nstate) { char b1[INT2STR_MAX_LEN], b2[INT2STR_MAX_LEN]; str s1, s2; s1.s = int2bstr( (unsigned long)h_entry, b1, &s1.len ); s2.s = int2bstr( (unsigned long)h_id, b2, &s2.len ); if (s1.s==NULL || s2.s==NULL) { LM_ERR("cannot convert hash params\n"); return; } if (evi_param_set_str(hentry_p, &s1) < 0) { LM_ERR("cannot set hash entry parameter\n"); return; } if (evi_param_set_str(hid_p, &s2) < 0) { LM_ERR("cannot set hash id parameter\n"); return; } if (evi_param_set_int(ostate_p, &ostate) < 0) { LM_ERR("cannot set old state parameter\n"); return; } if (evi_param_set_int(nstate_p, &nstate) < 0) { LM_ERR("cannot set new state parameter\n"); return; } if (evi_raise_event(ei_st_ch_id, event_params) < 0) LM_ERR("cannot raise event\n"); } /** * Small logging helper functions for next_state_dlg. * \param event logged event * \param dlg dialog data * \see next_state_dlg */ static inline void log_next_state_dlg(const int event, const struct dlg_cell *dlg) { LM_WARN("bogus event %d in state %d for dlg %p [%u:%u] with " "clid '%.*s' and tags '%.*s' '%.*s'\n", event, dlg->state, dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } void next_state_dlg(struct dlg_cell *dlg, int event, int dir, int *old_state, int *new_state, int *unref, int last_dst_leg, char is_replicated) { struct dlg_entry *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); *unref = 0; dlg_lock( d_table, d_entry); *old_state = dlg->state; switch (event) { case DLG_EVENT_TDEL: switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_DELETED; unref_dlg_unsafe(dlg,1,d_entry); /* unref from TM CBs*/ *unref = 1; /* unref from hash -> t failed */ break; case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: unref_dlg_unsafe(dlg,1,d_entry); /* unref from TM CBs*/ break; case DLG_STATE_DELETED: /* as the dialog aleady is in DELETE state, it is dangerous to directly unref it from here as it might be last ref -> dialog will be destroied and we will end up with a dangling pointer :D - bogdan */ *unref = 1; /* unref from TM CBs*/ break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_RPL1xx: switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_EARLY; break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_RPL3xx: switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_DELETED; *unref = 1; /* unref from hash -> t failed */ break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_RPL2xx: switch (dlg->state) { case DLG_STATE_DELETED: if (dlg->flags&DLG_FLAG_HASBYE) { log_next_state_dlg(event, dlg); break; } ref_dlg_unsafe(dlg,1); /* back in hash */ case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_CONFIRMED_NA; break; case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQACK: switch (dlg->state) { case DLG_STATE_CONFIRMED_NA: dlg->state = DLG_STATE_CONFIRMED; break; case DLG_STATE_CONFIRMED: break; case DLG_STATE_DELETED: break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQBYE: switch (dlg->state) { case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: if (dir == DLG_DIR_DOWNSTREAM && last_dst_leg!=dlg->legs_no[DLG_LEG_200OK] ) /* to end the call, the BYE must be received * on the same leg as the 200 OK for INVITE */ break; dlg->flags |= DLG_FLAG_HASBYE; dlg->state = DLG_STATE_DELETED; *unref = 1; /* unref from hash -> dialog ended */ break; case DLG_STATE_DELETED: break; default: /* only case for BYEs in early or unconfirmed states * is for requests generate by caller or callee. * We never internally generate BYEs for early dialogs * * RFC says caller may send BYEs for early dialogs, * while the callee side MUST not send such requests*/ if (last_dst_leg == 0) log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQPRACK: switch (dlg->state) { case DLG_STATE_EARLY: case DLG_STATE_CONFIRMED_NA: break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQ: switch (dlg->state) { case DLG_STATE_EARLY: case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: break; default: log_next_state_dlg(event, dlg); } break; default: LM_INFO("unknown event %d in state %d " "for dlg %p [%u:%u] with clid '%.*s' and tags '%.*s' '%.*s'\n", event, dlg->state, dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } *new_state = dlg->state; dlg_unlock( d_table, d_entry); if (*old_state != *new_state) raise_state_changed_event( dlg->h_entry, dlg->h_id, (unsigned int)(*old_state), (unsigned int)(*new_state) ); if ( !is_replicated && dialog_replicate_cluster && (*old_state==DLG_STATE_CONFIRMED_NA || *old_state==DLG_STATE_CONFIRMED) && dlg->state==DLG_STATE_DELETED ) replicate_dialog_deleted(dlg); LM_DBG("dialog %p changed from state %d to " "state %d, due event %d\n",dlg,*old_state,*new_state,event); } /**************************** MI functions ******************************/ static char *dlg_val_buf; static inline int internal_mi_print_dlg(struct mi_node *rpl, struct dlg_cell *dlg, int with_context) { struct mi_node* node= NULL; struct mi_node* node1 = NULL; struct mi_node* node2 = NULL; struct mi_node* node3 = NULL; struct mi_attr* attr= NULL; struct dlg_profile_link *dl; struct dlg_val* dv; int len; char* p; int i, j; time_t _ts; struct tm* t; char date_buf[MI_DATE_BUF_LEN]; int date_buf_len; node = add_mi_node_child(rpl, 0, "dialog",6 , 0, 0 ); if (node==0) goto error; attr = addf_mi_attr( node, 0, "hash", 4, "%u:%u", dlg->h_entry, dlg->h_id ); if (attr==0) goto error; attr = addf_mi_attr( node, 0, "dialog_id", 9, "%llu", (((long long unsigned)dlg->h_entry)<<(8*sizeof(int)))+dlg->h_id ); if (attr==0) goto error; p= int2str((unsigned long)dlg->state, &len); node1 = add_mi_node_child( node, MI_DUP_VALUE, "state", 5, p, len); if (node1==0) goto error; p= int2str((unsigned long)dlg->user_flags, &len); node1 = add_mi_node_child( node, MI_DUP_VALUE, "user_flags", 10, p, len); if (node1==0) goto error; _ts = (time_t)dlg->start_ts; p= int2str((unsigned long)_ts, &len); node1 = add_mi_node_child(node,MI_DUP_VALUE,"timestart",9, p, len); if (node1==0) goto error; if (_ts) { t = localtime(&_ts); date_buf_len = strftime(date_buf, MI_DATE_BUF_LEN - 1, "%Y-%m-%d %H:%M:%S", t); if (date_buf_len != 0) { node1 = add_mi_node_child(node,MI_DUP_VALUE, "datestart", 9, date_buf, date_buf_len); if (node1==0) goto error; } } _ts = (time_t)(dlg->tl.timeout?((unsigned int)time(0) + dlg->tl.timeout - get_ticks()):0); p= int2str((unsigned long)_ts, &len); node1 = add_mi_node_child(node,MI_DUP_VALUE, "timeout", 7, p, len); if (node1==0) goto error; if (_ts) { t = localtime(&_ts); date_buf_len = strftime(date_buf, MI_DATE_BUF_LEN - 1, "%Y-%m-%d %H:%M:%S", t); if (date_buf_len != 0) { node1 = add_mi_node_child(node,MI_DUP_VALUE, "dateout", 7, date_buf, date_buf_len); if (node1==0) goto error; } } node1 = add_mi_node_child(node, MI_DUP_VALUE, "callid", 6, dlg->callid.s, dlg->callid.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "from_uri", 8, dlg->from_uri.s, dlg->from_uri.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "to_uri", 6, dlg->to_uri.s, dlg->to_uri.len); if(node1 == 0) goto error; if (dlg->legs_no[DLG_LEGS_USED]>0) { node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_tag", 10, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[DLG_CALLER_LEG].tag.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_contact", 14, dlg->legs[DLG_CALLER_LEG].contact.s, dlg->legs[DLG_CALLER_LEG].contact.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE, "callee_cseq", 11, dlg->legs[DLG_CALLER_LEG].r_cseq.s, dlg->legs[DLG_CALLER_LEG].r_cseq.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE,"caller_route_set",16, dlg->legs[DLG_CALLER_LEG].route_set.s, dlg->legs[DLG_CALLER_LEG].route_set.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, 0,"caller_bind_addr",16, dlg->legs[DLG_CALLER_LEG].bind_addr->sock_str.s, dlg->legs[DLG_CALLER_LEG].bind_addr->sock_str.len); if(node1 == 0) goto error; node1 = add_mi_node_child(node, MI_DUP_VALUE,"caller_sdp",10, dlg->legs[DLG_CALLER_LEG].sdp.s, dlg->legs[DLG_CALLER_LEG].sdp.len); if(node1 == 0) goto error; } node1 = add_mi_node_child(node, MI_IS_ARRAY, "CALLEES", 7, NULL, 0); if(node1 == 0) goto error; for( i=1 ; i < dlg->legs_no[DLG_LEGS_USED] ; i++ ) { node2 = add_mi_node_child(node1, 0, "callee", 6, NULL, 0); if(node2 == 0) goto error; node3 = add_mi_node_child(node2, MI_DUP_VALUE, "callee_tag", 10, dlg->legs[i].tag.s, dlg->legs[i].tag.len); if(node3 == 0) goto error; node3 = add_mi_node_child(node2, MI_DUP_VALUE, "callee_contact", 14, dlg->legs[i].contact.s, dlg->legs[i].contact.len); if(node3 == 0) goto error; node3 = add_mi_node_child(node2, MI_DUP_VALUE, "caller_cseq", 11, dlg->legs[i].r_cseq.s, dlg->legs[i].r_cseq.len); if(node3 == 0) goto error; node3 = add_mi_node_child(node2, MI_DUP_VALUE,"callee_route_set",16, dlg->legs[i].route_set.s, dlg->legs[i].route_set.len); if(node3 == 0) goto error; if (dlg->legs[i].bind_addr) { node3 = add_mi_node_child(node2, 0, "callee_bind_addr",16, dlg->legs[i].bind_addr->sock_str.s, dlg->legs[i].bind_addr->sock_str.len); } else { node3 = add_mi_node_child(node2, 0, "callee_bind_addr",16,0,0); } if(node3 == 0) goto error; node3 = add_mi_node_child(node2, MI_DUP_VALUE,"callee_sdp",10, dlg->legs[i].sdp.s, dlg->legs[i].sdp.len); if(node3 == 0) goto error; } if (with_context) { node1 = add_mi_node_child(node, 0, "context", 7, 0, 0); if(node1 == 0) goto error; if (dlg->vals) { node2 = add_mi_node_child(node1, 0, "values", 6, 0, 0); if(node2 == 0) goto error; /* print dlg values -> iterate the list */ for( dv=dlg->vals ; dv ; dv=dv->next) { /* escape non-printable chars */ p = pkg_realloc(dlg_val_buf, 4 * dv->val.len + 1); if (!p) { LM_ERR("not enough mem to allocate: %d\n", dv->val.len); continue; } for (i = 0, j = 0; i < dv->val.len; i++) { if (dv->val.s[i] < 0x20 || dv->val.s[i] >= 0x7F) { p[j++] = '\\'; switch ((unsigned char)dv->val.s[i]) { case 0x8: p[j++] = 'b'; break; case 0x9: p[j++] = 't'; break; case 0xA: p[j++] = 'n'; break; case 0xC: p[j++] = 'f'; break; case 0xD: p[j++] = 'r'; break; default: p[j++] = 'x'; j += snprintf(&p[j], 3, "%02x", (unsigned char)dv->val.s[i]); break; } } else { p[j++] = dv->val.s[i]; } } add_mi_node_child(node2, MI_DUP_NAME|MI_DUP_VALUE,dv->name.s,dv->name.len, p,j); dlg_val_buf = p; } } /* print dlg profiles */ if (dlg->profile_links) { node3 = add_mi_node_child(node1, MI_IS_ARRAY, "profiles", 8, 0, 0); if(node3 == 0) goto error; for( dl=dlg->profile_links ; dl ; dl=dl->next) { add_mi_node_child(node3, MI_DUP_NAME|MI_DUP_VALUE, dl->profile->name.s,dl->profile->name.len, ZSW(dl->value.s),dl->value.len); } } /* print external context info */ run_dlg_callbacks( DLGCB_MI_CONTEXT, dlg, NULL, DLG_DIR_NONE, (void *)node1); } return 0; error: LM_ERR("failed to add node\n"); return -1; } int mi_print_dlg(struct mi_node *rpl, struct dlg_cell *dlg, int with_context) { return internal_mi_print_dlg( rpl, dlg, with_context); } static int internal_mi_print_dlgs(struct mi_root *rpl_tree,struct mi_node *rpl, int with_context, unsigned int idx, unsigned int cnt) { struct dlg_cell *dlg; unsigned int i; unsigned int n; unsigned int total; char *p; total = 0; if (cnt) { for(i=0;isize ; total+=d_table->entries[i++].cnt ); p = int2str((unsigned long)total, (int*)&n); if (add_mi_node_child(rpl,MI_DUP_VALUE,"dlg_counter",11,p,n)==0) return -1; } LM_DBG("printing %i dialogs, idx=%d, cnt=%d\n", total,idx,cnt); rpl->flags |= MI_NOT_COMPLETED; for( i=0,n=0 ; isize ; i++ ) { dlg_lock( d_table, &(d_table->entries[i]) ); for( dlg=d_table->entries[i].first ; dlg ; dlg=dlg->next ) { if (cnt && n=idx+cnt) { dlg_unlock( d_table, &(d_table->entries[i]) ); return 0; } if ( (n % 50) == 0 ) flush_mi_tree(rpl_tree); } dlg_unlock( d_table, &(d_table->entries[i]) ); } return 0; error: dlg_unlock( d_table, &(d_table->entries[i]) ); LM_ERR("failed to print dialog\n"); return -1; } static int match_downstream_dialog(struct dlg_cell *dlg, str *callid, str *ftag) { if (dlg->callid.len!=callid->len || (ftag && dlg->legs[DLG_CALLER_LEG].tag.len!=ftag->len) || strncmp(dlg->callid.s,callid->s,callid->len)!=0 || (ftag && strncmp(dlg->legs[DLG_CALLER_LEG].tag.s,ftag->s,ftag->len))) return 0; return 1; } /* * IMPORTANT: if a dialog reference is returned, the dialog hash entry will be kept locked when this function returns NOTE: if a reply tree is returned, no dialog reference is returned. */ static inline struct mi_root* process_mi_params(struct mi_root *cmd_tree, struct dlg_cell **dlg_p, unsigned int *idx, unsigned int *cnt) { struct mi_node* node; struct dlg_entry *d_entry; struct dlg_cell *dlg; str *p1; str *p2; unsigned int h_entry; node = cmd_tree->node.kids; if (node == NULL) { /* no parameters at all */ *dlg_p = NULL; *idx = *cnt = 0; return NULL; } /* we have params -> get p1 and p2 */ p1 = &node->value; LM_DBG("p1='%.*s'\n", p1->len, p1->s); node = node->next; if ( !node || !node->value.s || !node->value.len) { p2 = NULL; } else { p2 = &node->value; LM_DBG("p2='%.*s'\n", p2->len, p2->s); if ( node->next!=NULL ) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); } /* check the params */ if (p2 && str2int(p1,idx)==0 && str2int(p2,cnt)==0) { /* 2 numerical params -> index and counter */ *dlg_p = NULL; return NULL; } *idx = *cnt = 0; if (!p1->s) return init_mi_tree( 400, "Invalid Call-ID specified", 25); h_entry = dlg_hash( p1/*callid*/ ); d_entry = &(d_table->entries[h_entry]); dlg_lock( d_table, d_entry); for( dlg = d_entry->first ; dlg ; dlg = dlg->next ) { if (match_downstream_dialog( dlg, p1/*callid*/, p2/*from_tag*/)==1) { if (dlg->state==DLG_STATE_DELETED) { *dlg_p = NULL; break; } else { *dlg_p = dlg; return 0; } } } dlg_unlock( d_table, d_entry); return init_mi_tree( 404, MI_SSTR("No such dialog")); } struct mi_root * mi_print_dlgs(struct mi_root *cmd_tree, void *param ) { struct mi_root* rpl_tree= NULL; struct mi_node* rpl = NULL; struct dlg_cell* dlg = NULL; unsigned int idx,cnt; rpl_tree = process_mi_params( cmd_tree, &dlg, &idx, &cnt); if (rpl_tree) /* param error - no dialog returned */ return rpl_tree; rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) goto error; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; if (dlg==NULL) { if ( internal_mi_print_dlgs(rpl_tree, rpl, 0, idx, cnt)!=0 ) goto error; } else { if ( internal_mi_print_dlg(rpl,dlg,0)!=0 ) goto error; /* done with the dialog -> unlock it */ dlg_unlock_dlg(dlg); } return rpl_tree; error: /* if a dialog ref was returned, unlock it now */ if (dlg) dlg_unlock_dlg(dlg); /* trash everything that was built so far */ if (rpl_tree) free_mi_tree(rpl_tree); return NULL; } struct mi_root * mi_print_dlgs_ctx(struct mi_root *cmd_tree, void *param ) { struct mi_root* rpl_tree= NULL; struct mi_node* rpl = NULL; struct dlg_cell* dlg = NULL; unsigned int idx,cnt; rpl_tree = process_mi_params( cmd_tree, &dlg, &idx, &cnt); if (rpl_tree) /* param error */ return rpl_tree; rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) goto error; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; if (dlg==NULL) { if ( internal_mi_print_dlgs(rpl_tree, rpl, 1, idx, cnt)!=0 ) goto error; } else { if ( internal_mi_print_dlg(rpl,dlg,1)!=0 ) goto error; /* done with the dialog -> unlock it */ dlg_unlock_dlg(dlg); } return rpl_tree; error: /* if a dialog ref was returned, unlock it now */ if (dlg) dlg_unlock_dlg(dlg); /* trash everything that was built so far */ if (rpl_tree) free_mi_tree(rpl_tree); return NULL; } opensips-2.2.2/modules/dialog/dlg_hash.h000066400000000000000000000362061300170765700202230ustar00rootroot00000000000000/* * Copyright (C) 2009-2014 OpenSIPS Solutions * Copyright (C) 2006-2009 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-14 initial version (bogdan) * 2006-11-28 Added num_100s and num_200s to dlg_cell, to aid in adding * statistics tracking of the number of early, and active dialogs. * (Jeffrey Magder - SOMA Networks) * 2007-03-06 syncronized state machine added for dialog state. New tranzition * design based on events; removed num_1xx and num_2xx (bogdan) * 2007-07-06 added flags, cseq, contact, route_set and bind_addr * to struct dlg_cell in order to store these information into db * (ancuta) * 2008-04-17 added new dialog flag to avoid state tranzitions from DELETED to * CONFIRMED_NA due delayed "200 OK" (bogdan) * 2009-09-09 support for early dialogs added; proper handling of cseq * while PRACK is used (bogdan) */ #ifndef _DIALOG_DLG_HASH_H_ #define _DIALOG_DLG_HASH_H_ #include "../../locking.h" #include "../../context.h" #include "../../mi/mi.h" #include "dlg_timer.h" #include "dlg_cb.h" #include "dlg_vals.h" #define DLG_STATE_UNCONFIRMED 1 #define DLG_STATE_EARLY 2 #define DLG_STATE_CONFIRMED_NA 3 #define DLG_STATE_CONFIRMED 4 #define DLG_STATE_DELETED 5 #define DLG_EVENT_TDEL 1 #define DLG_EVENT_RPL1xx 2 #define DLG_EVENT_RPL2xx 3 #define DLG_EVENT_RPL3xx 4 #define DLG_EVENT_REQPRACK 5 #define DLG_EVENT_REQACK 6 #define DLG_EVENT_REQBYE 7 #define DLG_EVENT_REQ 8 #define DLG_FLAG_NEW (1<<0) #define DLG_FLAG_CHANGED (1<<1) #define DLG_FLAG_HASBYE (1<<2) #define DLG_FLAG_BYEONTIMEOUT (1<<3) #define DLG_FLAG_ISINIT (1<<4) #define DLG_FLAG_PING_CALLER (1<<5) #define DLG_FLAG_PING_CALLEE (1<<6) #define DLG_FLAG_TOPHIDING (1<<7) #define DLG_FLAG_VP_CHANGED (1<<8) #define DLG_FLAG_DB_DELETED (1<<9) #define DLG_FLAG_CSEQ_ENFORCE (1<<10) #define DLG_FLAG_REINVITE_PING_CALLER (1<<11) #define DLG_FLAG_REINVITE_PING_CALLEE (1<<12) #define DLG_CALLER_LEG 0 #define DLG_FIRST_CALLEE_LEG 1 #define DLG_DIR_NONE 0 #define DLG_DIR_DOWNSTREAM 1 #define DLG_DIR_UPSTREAM 2 struct dlg_leg { int id; str tag; str r_cseq; /* last cseq received targeting this leg */ str prev_cseq; /* prev cseq received targeting this leg */ str inv_cseq; /* last cseq of invite received from this leg */ str from_uri; /* FROM URI for this leg, in case of FROM URI mangling*/ str to_uri; /* TO URI for this leg, in case of TO URI mangling */ str route_set; str contact; str th_sent_contact; /* topology hiding advertised contact towards this leg - full header */ str route_uris[64]; str sdp; /* latest SDP provided by this leg ( full body ), after all OpenSIPS changes */ int nr_uris; unsigned int last_gen_cseq; /* FIXME - think this can be atomic_t to avoid locking */ unsigned int last_inv_gen_cseq; /* used when translating ACKs */ char reply_received; char reinvite_confirmed; struct socket_info *bind_addr; }; #define DLG_LEGS_USED 0 #define DLG_LEGS_ALLOCED 1 #define DLG_LEG_200OK 2 /* ALL module flags to be listed here - we have to centralize here : - in order to make it easier for future modules to use the flags - sometimes, dialog internal behavior changes, depending on whether a certain module is loaded or not ( eg. topology_hiding ) */ #define SST_DIALOG_FLAG (1 << 0) #define TOPOH_ONGOING (1 << 1) #define TOPOH_KEEP_USER (1 << 2) #define TOPOH_HIDE_CALLID (1 << 3) struct dlg_cell { volatile int ref; struct dlg_cell *next; struct dlg_cell *prev; unsigned int h_id; unsigned int h_entry; unsigned int state; unsigned int lifetime; unsigned int lifetime_dirty; /* 1 if lifetime timer should be updated */ unsigned int start_ts; /* start time (absolute UNIX ts)*/ unsigned int flags; unsigned int from_rr_nb; unsigned int user_flags; unsigned int mod_flags; unsigned int initial_t_hash_index; unsigned int initial_t_label; struct dlg_tl tl; struct dlg_ping_list *pl; struct dlg_ping_list *reinvite_pl; str terminate_reason; str callid; str from_uri; str to_uri; struct dlg_leg *legs; unsigned char legs_no[4]; struct dlg_head_cbl cbs; struct dlg_profile_link *profile_links; struct dlg_val *vals; }; struct dlg_entry { struct dlg_cell *first; struct dlg_cell *last; unsigned int next_id; unsigned int cnt; unsigned int lock_idx; }; struct dlg_table { unsigned int size; struct dlg_entry *entries; unsigned int locks_no; gen_lock_set_t *locks; }; extern struct dlg_table *d_table; extern int ctx_dlg_idx; #define callee_idx(_dlg) \ (((_dlg)->legs_no[DLG_LEG_200OK]==0)? \ DLG_FIRST_CALLEE_LEG : (_dlg)->legs_no[DLG_LEG_200OK]) #define ctx_dialog_get() \ ((struct dlg_cell*)context_get_ptr(CONTEXT_GLOBAL,current_processing_ctx,ctx_dlg_idx) ) #define ctx_dialog_set(_dlg) \ context_put_ptr(CONTEXT_GLOBAL,current_processing_ctx, ctx_dlg_idx, _dlg) struct dlg_cell *get_current_dialog(); #define dlg_hash(_callid) core_hash(_callid, 0, d_table->size) #define dlg_lock(_table, _entry) \ lock_set_get( (_table)->locks, (_entry)->lock_idx); #define dlg_unlock(_table, _entry) \ lock_set_release( (_table)->locks, (_entry)->lock_idx); #define dlg_leg_print_info(_dlg, _leg, _field) \ ((_dlg)->legs_no[DLG_LEGS_USED]>_leg)?(_dlg)->legs[_leg]._field.len:4, \ ((_dlg)->legs_no[DLG_LEGS_USED]>_leg)?(_dlg)->legs[_leg]._field.s:"NULL" #define dlg_lock_dlg(_dlg) \ dlg_lock( d_table, &(d_table->entries[_dlg->h_entry])) #define dlg_unlock_dlg(_dlg) \ dlg_unlock( d_table, &(d_table->entries[_dlg->h_entry])) static inline str* dlg_leg_from_uri(struct dlg_cell *dlg,int leg_no) { /* no mangling possible on caller leg */ if (leg_no == DLG_CALLER_LEG) return &dlg->from_uri; /* if we saved mangled from URI at leg creation, return that */ if (dlg->legs[leg_no].from_uri.s && dlg->legs[leg_no].from_uri.len) return &dlg->legs[leg_no].from_uri; /* if there was no mangling for this leg, return original from URI */ return &dlg->from_uri; } static inline str* dlg_leg_to_uri(struct dlg_cell *dlg,int leg_no) { /* no mangling possible on caller leg */ if (leg_no == DLG_CALLER_LEG) return &dlg->to_uri; /* if we saved mangled to URI at leg creation, return that */ if (dlg->legs[leg_no].to_uri.s && dlg->legs[leg_no].to_uri.len) return &dlg->legs[leg_no].to_uri; /* if there was no mangling for this leg, return original to URI */ return &dlg->to_uri; } void unlink_unsafe_dlg(struct dlg_entry *d_entry, struct dlg_cell *dlg); void destroy_dlg(struct dlg_cell *dlg); #define ref_dlg_unsafe(_dlg,_cnt) \ do { \ (_dlg)->ref += (_cnt); \ LM_DBG("ref dlg %p with %d -> %d\n", \ (_dlg),(_cnt),(_dlg)->ref); \ }while(0) #define unref_dlg_unsafe(_dlg,_cnt,_d_entry) \ do { \ (_dlg)->ref -= (_cnt); \ LM_DBG("unref dlg %p with %d -> %d in entry %p\n",\ (_dlg),(_cnt),(_dlg)->ref,(_d_entry));\ if ((_dlg)->ref<0) {\ LM_CRIT("bogus ref %d with cnt %d for dlg %p [%u:%u] "\ "with clid '%.*s' and tags '%.*s' '%.*s'\n",\ (_dlg)->ref, _cnt, _dlg,\ (_dlg)->h_entry, (_dlg)->h_id,\ (_dlg)->callid.len, (_dlg)->callid.s,\ dlg_leg_print_info(_dlg, DLG_CALLER_LEG, tag), \ dlg_leg_print_info(_dlg, callee_idx(_dlg), tag)); \ }\ if ((_dlg)->ref<=0) { \ unlink_unsafe_dlg( _d_entry, _dlg);\ LM_DBG("ref <=0 for dialog %p\n",_dlg);\ destroy_dlg(_dlg);\ }\ }while(0) /* * @input - str * @return - integer flag bitmask */ #define parse_create_dlg_flags(input) \ ({ \ char *___p; \ int ___flags = 0; \ for (___p=(input).s; ___p < (input).s + (input).len; ___p++) \ { \ switch (*___p) \ { \ case 'P': \ ___flags |= DLG_FLAG_PING_CALLER; \ LM_DBG("will ping caller\n"); \ break; \ case 'p': \ ___flags |= DLG_FLAG_PING_CALLEE; \ LM_DBG("will ping callee\n"); \ break; \ case 'B': \ ___flags |= DLG_FLAG_BYEONTIMEOUT; \ LM_DBG("bye on timeout activated\n"); \ break; \ case 'R': \ ___flags |= DLG_FLAG_REINVITE_PING_CALLER; \ LM_DBG("re-invite ping caller activated\n"); \ break; \ case 'r': \ ___flags |= DLG_FLAG_REINVITE_PING_CALLEE; \ LM_DBG("re-invite ping callee activated\n"); \ break; \ default: \ LM_DBG("unknown create_dialog flag : [%c] ." \ "Skipping\n", *___p); \ } \ } \ ___flags; \ }) int dialog_cleanup( struct sip_msg *msg, void *param ); int init_dlg_table(unsigned int size); void destroy_dlg_table(); struct dlg_cell* build_new_dlg(str *callid, str *from_uri, str *to_uri, str *from_tag); int dlg_add_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact,str *cseq, struct socket_info *sock, str *mangled_from,str *mangled_to,str *sdp); int dlg_update_cseq(struct dlg_cell *dlg, unsigned int leg, str *cseq, int field_type); int dlg_update_routing(struct dlg_cell *dlg, unsigned int leg,str *rr, str *contact); struct dlg_cell* lookup_dlg( unsigned int h_entry, unsigned int h_id); struct dlg_cell* get_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir, unsigned int *dst_leg); struct dlg_cell* get_dlg_by_val(str *attr, str *val); void link_dlg(struct dlg_cell *dlg, int n); void unref_dlg(struct dlg_cell *dlg, unsigned int cnt); void ref_dlg(struct dlg_cell *dlg, unsigned int cnt); void next_state_dlg(struct dlg_cell *dlg, int event, int dir, int *old_state, int *new_state, int *unref, int last_dst_leg, char is_replicated); struct mi_root * mi_print_dlgs(struct mi_root *cmd, void *param ); struct mi_root * mi_print_dlgs_ctx(struct mi_root *cmd, void *param ); static inline void unref_dlg_destroy_safe(struct dlg_cell *dlg, unsigned int cnt) { if (d_table) unref_dlg(dlg, cnt); } static inline int match_dialog(struct dlg_cell *dlg, str *callid, str *ftag, str *ttag, unsigned int *dir, unsigned int *dst_leg) { str *tag; unsigned int i; /* first check dialog callid */ if (dlg->callid.len!=callid->len || strncmp(dlg->callid.s, callid->s, callid->len)!=0 ) /* callid not matching */ return 0; /* check the dialog from tag */ if (dlg->legs[DLG_CALLER_LEG].tag.len == ftag->len && strncmp(dlg->legs[DLG_CALLER_LEG].tag.s, ftag->s, ftag->len)==0 ) { /* from tag = from tag matching */ *dir = DLG_DIR_DOWNSTREAM; tag = ttag; } else if (dlg->legs[DLG_CALLER_LEG].tag.len == ttag->len && strncmp(dlg->legs[DLG_CALLER_LEG].tag.s, ttag->s, ttag->len)==0 ) { /* from tag = to tag matching */ *dir = DLG_DIR_UPSTREAM; *dst_leg = 0; /* destination is the caller */ tag = ftag; } else { /* dialog from tag does not match */ return 0; } /* check the dialog to tag - interate through all the stored to-tags */ if (dlg->legs_no[DLG_LEGS_USED] > DLG_FIRST_CALLEE_LEG) { for ( i=DLG_FIRST_CALLEE_LEG ; ilegs_no[DLG_LEGS_USED] ; i++) { if (dlg->legs[i].tag.len == tag->len && strncmp(dlg->legs[i].tag.s, tag->s, tag->len)==0 ) { if (*dst_leg==-1) *dst_leg = i; /* destination is callee */ return 1; } } /* no matching */ return 0; } /* no to tag in dialog */ return (tag->len==0)?1:0; /* if (dlg->tag[DLG_CALLEE_LEG].len == 0) { if (*dir==DLG_DIR_DOWNSTREAM) { if (dlg->callid.len == callid->len && dlg->tag[DLG_CALLER_LEG].len == ftag->len && strncmp(dlg->callid.s, callid->s, callid->len)==0 && strncmp(dlg->tag[DLG_CALLER_LEG].s, ftag->s, ftag->len)==0) { return 1; } } else if (*dir==DLG_DIR_UPSTREAM) { if (dlg->callid.len == callid->len && dlg->tag[DLG_CALLER_LEG].len == ttag->len && strncmp(dlg->callid.s, callid->s, callid->len)==0 && strncmp(dlg->tag[DLG_CALLER_LEG].s, ttag->s, ttag->len)==0) { return 1; } } else { if (dlg->callid.len != callid->len) return 0; if (dlg->tag[DLG_CALLER_LEG].len == ttag->len && strncmp(dlg->tag[DLG_CALLER_LEG].s, ttag->s, ttag->len)==0 && strncmp(dlg->callid.s, callid->s, callid->len)==0) { *dir = DLG_DIR_UPSTREAM; return 1; } else if (dlg->tag[DLG_CALLER_LEG].len == ftag->len && strncmp(dlg->tag[DLG_CALLER_LEG].s, ftag->s, ftag->len)==0 && strncmp(dlg->callid.s, callid->s, callid->len)==0) { *dir = DLG_DIR_DOWNSTREAM; return 1; } } } else { if (*dir==DLG_DIR_DOWNSTREAM) { if (dlg->callid.len == callid->len && dlg->tag[DLG_CALLER_LEG].len == ftag->len && dlg->tag[DLG_CALLEE_LEG].len == ttag->len && strncmp(dlg->callid.s, callid->s, callid->len)==0 && strncmp(dlg->tag[DLG_CALLER_LEG].s, ftag->s, ftag->len)==0 && strncmp(dlg->tag[DLG_CALLEE_LEG].s, ttag->s, ttag->len)==0) { return 1; } } else if (*dir==DLG_DIR_UPSTREAM) { if (dlg->callid.len == callid->len && dlg->tag[DLG_CALLEE_LEG].len == ftag->len && dlg->tag[DLG_CALLER_LEG].len == ttag->len && strncmp(dlg->callid.s, callid->s, callid->len)==0 && strncmp(dlg->tag[DLG_CALLEE_LEG].s, ftag->s, ftag->len)==0 && strncmp(dlg->tag[DLG_CALLER_LEG].s, ttag->s, ttag->len)==0) { return 1; } } else { if (dlg->callid.len != callid->len) return 0; if (dlg->tag[DLG_CALLEE_LEG].len == ftag->len && dlg->tag[DLG_CALLER_LEG].len == ttag->len && strncmp(dlg->tag[DLG_CALLEE_LEG].s, ftag->s, ftag->len)==0 && strncmp(dlg->tag[DLG_CALLER_LEG].s, ttag->s, ttag->len)==0 && strncmp(dlg->callid.s, callid->s, callid->len)==0) { *dir = DLG_DIR_UPSTREAM; return 1; } else if (dlg->tag[DLG_CALLER_LEG].len == ftag->len && dlg->tag[DLG_CALLEE_LEG].len == ttag->len && strncmp(dlg->tag[DLG_CALLER_LEG].s, ftag->s, ftag->len)==0 && strncmp(dlg->tag[DLG_CALLEE_LEG].s, ttag->s, ttag->len)==0 && strncmp(dlg->callid.s, callid->s, callid->len)==0) { *dir = DLG_DIR_DOWNSTREAM; return 1; } } } */ } int mi_print_dlg(struct mi_node *rpl, struct dlg_cell *dlg, int with_context); static inline void init_dlg_term_reason(struct dlg_cell *dlg,char *reason,int reason_len) { if (!dlg->terminate_reason.s) { dlg->terminate_reason.s = shm_malloc(reason_len); if (dlg->terminate_reason.s) { dlg->terminate_reason.len = reason_len; memcpy(dlg->terminate_reason.s,reason, reason_len); LM_DBG("Setting DLG term reason to [%.*s] \n", dlg->terminate_reason.len,dlg->terminate_reason.s); } else LM_ERR("Failed to initialize the terminate reason \n"); } } int state_changed_event_init(void); void state_changed_event_destroy(void); #endif opensips-2.2.2/modules/dialog/dlg_load.h000066400000000000000000000044561300170765700202210ustar00rootroot00000000000000/* * dialog module - basic support for dialog tracking * * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-14 initial version (bogdan) */ #ifndef _DIALOG_DLG_LOAD_H_ #define _DIALOG_DLG_LOAD_H_ #include "dlg_cb.h" #include "dlg_handlers.h" #include "dlg_profile.h" #include "dlg_vals.h" #include "../../sr_module.h" typedef struct dlg_cell *(*get_dlg_f) (void); typedef int (*match_dialog_f) (struct sip_msg *); struct dlg_binds { register_dlgcb_f register_dlgcb; create_dlg_f create_dlg; get_dlg_f get_dlg; add_profiles_f add_profiles; search_dlg_profile_f search_profile; set_dlg_profile_f set_profile; unset_dlg_profile_f unset_profile; get_profile_size_f get_profile_size; store_dlg_value_f store_dlg_value; fetch_dlg_value_f fetch_dlg_value; terminate_dlg_f terminate_dlg; match_dialog_f match_dialog; validate_dialog_f validate_dialog; fix_route_dialog_f fix_route_dialog; set_mod_flag_f set_mod_flag; is_mod_flag_set_f is_mod_flag_set; ref_dlg_f ref_dlg; unref_dlg_f unref_dlg; get_rr_param_f get_rr_param; }; typedef int(*load_dlg_f)( struct dlg_binds *dlgb ); int load_dlg( struct dlg_binds *dlgb); static inline int load_dlg_api( struct dlg_binds *dlgb ) { load_dlg_f load_dlg; /* import the DLG auto-loading function */ if ( !(load_dlg=(load_dlg_f)find_export("load_dlg", 0, 0))) return -1; /* let the auto-loading function load all DLG stuff */ if (load_dlg( dlgb )==-1) return -1; return 0; } #endif opensips-2.2.2/modules/dialog/dlg_profile.c000066400000000000000000001001461300170765700207260ustar00rootroot00000000000000/* * Copyright (C) 2009-2014 OpenSIPS Solutions * Copyright (C) 2008 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-04-20 initial version (bogdan) * 2008-09-16 speed optimization (andreidragus) * */ #include #include "../../cachedb/cachedb.h" #include "../../cachedb/cachedb_cap.h" #include "../../mem/shm_mem.h" #include "../../hash_func.h" #include "../../dprint.h" #include "../../ut.h" #include "dlg_hash.h" #include "dlg_profile.h" #include "dlg_repl_profile.h" #include "dlg_req_within.h" #define PROFILE_HASH_SIZE 16 struct dlg_profile_table *profiles = NULL; static struct lock_set_list * all_locks = NULL; static struct lock_set_list * cur_lock = NULL; static int finished_allocating_locks = 0; extern int log_profile_hash_size; static struct dlg_profile_table* new_dlg_profile( str *name, unsigned int size, unsigned int has_value, unsigned repl_type); /* used by cachedb interface */ static cachedb_funcs cdbf; static cachedb_con *cdbc = 0; str cdb_val_prefix = str_init("dlg_val_"); str cdb_noval_prefix = str_init("dlg_noval_"); str cdb_size_prefix = str_init("dlg_size_"); int profile_timeout = 60 * 60 * 24; /* 24 hours */ str dlg_prof_val_buf = {0, 0}; str dlg_prof_noval_buf = {0, 0}; str dlg_prof_size_buf = {0, 0}; /* TODO if needed to change the separator */ str dlg_prof_sep = str_init("_"); /* method that tries to get a new lock_set, if one cannot be allocated * an older one is reused */ static gen_lock_set_t * get_a_lock_set(int no ) { gen_lock_set_t *ret, *new; struct lock_set_list * node; if( ! finished_allocating_locks ) { new = lock_set_alloc(no); if( new == NULL ) { LM_ERR("Unable to allocate locks\n"); return NULL; } ret = lock_set_init( new ); if( ret == NULL ) { lock_set_dealloc( new ); finished_allocating_locks = 1; } else { node = (struct lock_set_list *)shm_malloc( sizeof * node); if( node == NULL ) { LM_ERR("Unable to allocate list\n"); return NULL; } node->locks = ret; node->next = all_locks; all_locks = node; } } if( finished_allocating_locks ) { if( all_locks == NULL ) { LM_ERR("Unable to init any locks\n"); return NULL; } if ( !cur_lock ) cur_lock = all_locks; if ( cur_lock ) { ret = cur_lock->locks; cur_lock = cur_lock->next; } } return ret; } void destroy_all_locks(void) { struct lock_set_list * node; while( all_locks ) { node = all_locks; all_locks = all_locks -> next; lock_set_destroy( node->locks); lock_set_dealloc( node->locks); shm_free(node); } } int add_profile_definitions( char* profiles, unsigned int has_value) { char *p; char *d; char *e; str name; unsigned int i; enum repl_types type; if (profiles==NULL || strlen(profiles)==0 ) return 0; p = profiles; do { /* By default no replication (no CACHEDB nor BIN)*/ type = REPL_NONE; /* locate name of profile */ name.s = p; d = strchr( p, ';'); if (d) { name.len = d-p; d++; } else { name.len = strlen(p); } /* we have the name -> trim it for spaces */ trim_spaces_lr( name ); e = name.s + name.len; /* check len name */ if (name.len==0) /* ignore */ continue; /* check if it should be shared with cachedb */ p = memchr(name.s, '/', name.len); if (p) { name.len = p - name.s; trim_spaces_lr( name ); /* skip spaces after p */ for (++p; *p == ' ' && p < e; p++); if ( p < e && *p == 's') { if (cdb_url.len && cdb_url.s) { if (type==REPL_PROTOBIN) goto repl_error; type= REPL_CACHEDB; } else { LM_WARN("profile %.*s configured to be stored in CacheDB, " "but the cachedb_url was not defined\n", name.len, name.s); } } else if ( p < e && *p == 'b') { if (profile_replicate_cluster) { if (type==REPL_CACHEDB) goto repl_error; type = REPL_PROTOBIN; } else { LM_WARN("profile %.*s configured to be replicated over BIN, " "but replicate_profiles_to param is not defined\n", name.len, name.s); } } else if (isalnum(*p)) { LM_ERR("Invalid letter in profile definitition !\n", *p); return -1; } } /* check the name format */ for(i=0;i, char %c - use only " "alphanumerical characters\n", name.len,name.s,name.s[i]); return -1; } } /* name ok -> create the profile */ LM_DBG("creating profile <%.*s> %s\n", name.len, name.s, type ==REPL_CACHEDB ? "cached" : (type==REPL_PROTOBIN ? "bin replicated": "")); if (new_dlg_profile( &name, 1 << log_profile_hash_size, has_value, type)==NULL) { LM_ERR("failed to create new profile <%.*s>\n",name.len,name.s); return -1; } }while( (p=d)!=NULL ); return 0; repl_error: LM_ERR("Can't use both bin replication and cachedb!\n"); return -1; } #define DLG_COPY(_d, _s) \ do { \ memcpy((_d).s + (_d).len, (_s)->s, (_s)->len); \ (_d).len += (_s)->len; \ } while (0) static inline char * dlg_prof_realloc(char * ptr, int size) { ptr = pkg_realloc(ptr, size); if (!ptr) { LM_ERR("not enough memory for cachedb buffer\n"); return NULL; } return ptr; } static int dlg_fill_value(str *name, str *value) { char * buf; int val_len = calc_base64_encode_len(value->len); int len = cdb_val_prefix.len /* prefix */ + name->len /* profile name */ + dlg_prof_sep.len /* value separator */ + val_len /* profile value, b64 encoded */; /* reallocate the appropriate size */ if (!(buf = dlg_prof_realloc(dlg_prof_val_buf.s, len))) { LM_ERR("cannot realloc profile with value buffer\n"); return -1; } dlg_prof_val_buf.s = buf; dlg_prof_val_buf.len = cdb_val_prefix.len; DLG_COPY(dlg_prof_val_buf, name); DLG_COPY(dlg_prof_val_buf, &dlg_prof_sep); base64encode((unsigned char*)dlg_prof_val_buf.s + dlg_prof_val_buf.len, (unsigned char *)value->s, value->len); dlg_prof_val_buf.len += val_len; return 0; } static int dlg_fill_name(str *name) { char * buf; if (!(buf = dlg_prof_realloc(dlg_prof_noval_buf.s, cdb_noval_prefix.len /* prefix */ + name->len /* profile name */))) { LM_ERR("cannot realloc buffer profile name writing\n"); return -1; } dlg_prof_noval_buf.s = buf; dlg_prof_noval_buf.len = cdb_noval_prefix.len; DLG_COPY(dlg_prof_noval_buf, name); return 0; } static int dlg_fill_size(str *name) { char * buf; if (!(buf = dlg_prof_realloc(dlg_prof_size_buf.s, cdb_size_prefix.len + name->len))) { LM_ERR("cannot realloc profile size buffer\n"); return -1; } dlg_prof_size_buf.s = buf; dlg_prof_size_buf.len = cdb_size_prefix.len; DLG_COPY(dlg_prof_size_buf, name); return 0; } int init_cachedb(void) { if (!cdbf.init) { LM_ERR("cachedb function not initialized\n"); return -1; } cdbc = cdbf.init(&cdb_url); if (!cdbc) { LM_ERR("cannot connect to cachedb_url %.*s\n", cdb_url.len, cdb_url.s); return -1; } LM_DBG("Inited cachedb \n"); return 0; } void destroy_cachedb(int final) { if (cdbc) cdbf.destroy(cdbc); cdbc = NULL; if (!final) return; if (dlg_prof_val_buf.s) pkg_free(dlg_prof_val_buf.s); if (dlg_prof_noval_buf.s) pkg_free(dlg_prof_noval_buf.s); if (dlg_prof_size_buf.s) pkg_free(dlg_prof_size_buf.s); } int init_cachedb_utils(void) { if (profile_timeout<=0) { LM_ERR("0 or negative profile_timeout not accepted!!\n"); return -1; } if (cachedb_bind_mod(&cdb_url, &cdbf) < 0) { LM_ERR("cannot bind functions for cachedb_url %.*s\n", cdb_url.len, cdb_url.s); return -1; } if (!CACHEDB_CAPABILITY(&cdbf, CACHEDB_CAP_GET|CACHEDB_CAP_ADD|CACHEDB_CAP_SUB)) { LM_ERR("not enough capabilities\n"); return -1; } cdbc = cdbf.init(&cdb_url); if (!cdbc) { LM_ERR("cannot connect to cachedb_url %.*s\n", cdb_url.len, cdb_url.s); return -1; } dlg_prof_val_buf.s = pkg_malloc(cdb_val_prefix.len + 32); if (!dlg_prof_val_buf.s) { LM_ERR("no more memory to allocate buffer\n"); return -1; } dlg_prof_noval_buf.s = pkg_malloc(cdb_noval_prefix.len + 32); if (!dlg_prof_noval_buf.s) { LM_ERR("no more memory to allocate buffer\n"); return -1; } dlg_prof_size_buf.s = pkg_malloc(cdb_size_prefix.len + 32); if (!dlg_prof_size_buf.s) { LM_ERR("no more memory to allocate buffer\n"); return -1; } /* copy prefixes in buffer */ memcpy(dlg_prof_val_buf.s, cdb_val_prefix.s, cdb_val_prefix.len); memcpy(dlg_prof_noval_buf.s, cdb_noval_prefix.s, cdb_noval_prefix.len); memcpy(dlg_prof_size_buf.s, cdb_size_prefix.s, cdb_size_prefix.len); return 0; } /* faster method to match a profile by name, no other checks */ struct dlg_profile_table *get_dlg_profile(str *name) { struct dlg_profile_table *profile; for (profile=profiles ;profile ;profile=profile->next) { if (name->len == profile->name.len && memcmp(name->s, profile->name.s, name->len) == 0) return profile; } return NULL; } struct dlg_profile_table* search_dlg_profile(str *name) { struct dlg_profile_table *profile; char *p,*e; unsigned repl_type=REPL_NONE; str profile_name = *name; /* check if this is a shared profile, and remove /s for lookup */ p = memchr(profile_name.s, '/', profile_name.len); if (p) { e = profile_name.s + profile_name.len; profile_name.len = p - profile_name.s; trim_spaces_lr( profile_name ); /* skip spaces after p */ for (++p; *p == ' ' && p < e; p++); if ( p < e && *p == 's') repl_type=REPL_CACHEDB; else if (p < e && *p == 'b') repl_type=REPL_PROTOBIN; } for( profile=profiles ; profile ; profile=profile->next ) { if (profile->repl_type == repl_type && profile_name.len ==profile->name.len && memcmp(profile_name.s,profile->name.s,profile_name.len)==0 ) return profile; } return NULL; } static struct dlg_profile_table* new_dlg_profile( str *name, unsigned int size, unsigned int has_value, unsigned repl_type) { struct dlg_profile_table *profile; struct dlg_profile_table *ptmp; unsigned int len; unsigned int i; if ( name->s==NULL || name->len==0 || size==0 ) { LM_ERR("invalid parameters\n"); return NULL; } for( len=0,i=0 ; i<8*sizeof(size) ; i++ ) { if ( size & (1<\n", name->len, name->s); return NULL; } len = sizeof(struct dlg_profile_table) + name->len + 1; /* anything else than only CACHEDB */ if (repl_type != REPL_CACHEDB) len += size * ((has_value==0) ? sizeof(int):sizeof(map_t)); profile = (struct dlg_profile_table *)shm_malloc(len); if (profile==NULL) { LM_ERR("no more shm mem\n"); return NULL; } memset( profile , 0 , len); if (!has_value) profile->repl = repl_prof_allocate(); profile->size = size; profile->has_value = (has_value==0)?0:1; profile->repl_type = repl_type; /* init locks */ if (repl_type != REPL_CACHEDB) { profile->locks = get_a_lock_set(size) ; if( !profile->locks ) { LM_ERR("failed to init lock\n"); shm_free(profile); return NULL; } } if( repl_type == REPL_CACHEDB ) { profile->name.s = (char *)(profile + 1); } else if (has_value ) { /* set inner pointers */ profile->entries = ( map_t *)(profile + 1); for( i= 0; i < size; i++) { profile->entries[i] = map_create(AVLMAP_SHARED); if( !profile->entries[i] ) { LM_ERR("Unable to create a map\n"); shm_free(profile); return NULL; } } profile->name.s = ((char*)profile->entries) + size*sizeof( map_t ); } else { profile->counts = ( int *)(profile + 1); profile->name.s = (char*) (profile->counts) + size*sizeof( int ) ; } /* copy the name of the profile */ memcpy( profile->name.s, name->s, name->len ); profile->name.len = name->len; profile->name.s[profile->name.len] = 0; /* link profile */ for( ptmp=profiles ; ptmp && ptmp->next; ptmp=ptmp->next ); if (ptmp==NULL) profiles = profile; else ptmp->next = profile; return profile; } static void destroy_dlg_profile(struct dlg_profile_table *profile) { int i; if (profile==NULL) return; if( profile->has_value && !(profile->repl_type==REPL_CACHEDB) ) { for( i= 0; i < profile->size; i++) map_destroy( profile->entries[i], free_profile_val); } shm_free( profile ); return; } void destroy_dlg_profiles(void) { struct dlg_profile_table *profile; while(profiles) { profile = profiles; profiles = profiles->next; destroy_dlg_profile( profile ); } destroy_all_locks(); return; } void destroy_linkers(struct dlg_profile_link *linker, char is_replicated) { map_t entry; struct dlg_profile_link *l; void ** dest; while(linker) { l = linker; linker = linker->next; /* unlink from profile table */ if (!(l->profile->repl_type==REPL_CACHEDB)) { lock_set_get( l->profile->locks, l->hash_idx); if( l->profile->has_value) { entry = l->profile->entries[l->hash_idx]; dest = map_find( entry, l->value ); if( dest ) { repl_prof_dec(dest); if( *dest == 0 ) { /* warn everybody we are deleting */ /* XXX: we should queue these */ repl_prof_remove(&l->profile->name, &l->value); map_remove(entry,l->value ); } } } else l->profile->counts[l->hash_idx]--; lock_set_release( l->profile->locks, l->hash_idx ); } else if (!is_replicated) { if (!cdbc) { LM_WARN("CacheDB not initialized - some information might" " not be deleted from the cachedb engine\n"); return; } /* prepare buffers */ if( l->profile->has_value) { if (dlg_fill_value(&l->profile->name, &l->value) < 0) return; if (dlg_fill_size(&l->profile->name) < 0) return; /* not really interested in the new val */ if (cdbf.sub(cdbc, &dlg_prof_val_buf, 1, profile_timeout, NULL) < 0) { LM_ERR("cannot remove profile from CacheDB\n"); return; } /* fill size into name */ if (cdbf.sub(cdbc, &dlg_prof_size_buf, 1, profile_timeout, NULL) < 0) { LM_ERR("cannot remove size profile from CacheDB\n"); return; } } else { if (dlg_fill_name(&l->profile->name) < 0) return; if (cdbf.sub(cdbc, &dlg_prof_noval_buf, 1, profile_timeout, NULL) < 0) { LM_ERR("cannot remove profile from CacheDB\n"); return; } } } /* free memory */ shm_free(l); } } inline static unsigned int calc_hash_profile( str *value, struct dlg_cell *dlg, struct dlg_profile_table *profile ) { if (profile->has_value) { /* do hash over the value */ return core_hash( value, NULL, profile->size); } else { /* do hash over dialog pointer */ return ((unsigned long)dlg) % profile->size ; } } static void link_dlg_profile(struct dlg_profile_link *linker, struct dlg_cell *dlg, char is_replicated) { unsigned int hash; map_t p_entry; struct dlg_entry *d_entry; void ** dest; /* add the linker to the dialog */ /* FIXME zero h_id is not 100% for testing if the dialog is inserted * into the hash table -> we need circular lists -bogdan */ if (dlg->h_id) { d_entry = &d_table->entries[dlg->h_entry]; dlg_lock( d_table, d_entry); linker->next = dlg->profile_links; dlg->profile_links =linker; dlg_unlock( d_table, d_entry); } else { linker->next = dlg->profile_links; dlg->profile_links =linker; } /* insert into profile hash table */ /* but only if cachedb is not used */ if (!(linker->profile->repl_type==REPL_CACHEDB)) { /* calculate the hash position */ hash = calc_hash_profile(&linker->value, dlg, linker->profile); linker->hash_idx = hash; lock_set_get( linker->profile->locks, hash ); LM_DBG("Entered here with hash = %d \n",hash); if( linker->profile->has_value) { p_entry = linker->profile->entries[hash]; dest = map_get( p_entry, linker->value ); /* if we accept replicated stuff, we have to allocate the * structure for it and treat the counter differently */ repl_prof_inc(dest); } else linker->profile->counts[hash]++; lock_set_release( linker->profile->locks,hash ); } else if (!is_replicated) { if (!cdbc) { LM_WARN("Cachedb not initialized yet - cannot update profile\n"); LM_WARN("Make sure that the dialog profile information is persistent\n"); LM_WARN(" in your cachedb storage, because otherwise you might loose profile data\n"); return; } /* prepare buffers */ if( linker->profile->has_value) { if (dlg_fill_value(&linker->profile->name, &linker->value) < 0) return; if (dlg_fill_size(&linker->profile->name) < 0) return; /* not really interested in the new val */ if (cdbf.add(cdbc, &dlg_prof_val_buf, 1, profile_timeout, NULL) < 0) { LM_ERR("cannot insert profile into CacheDB\n"); return; } /* fill size into name */ if (cdbf.add(cdbc, &dlg_prof_size_buf, 1, profile_timeout, NULL) < 0) { LM_ERR("cannot insert size profile into CacheDB\n"); return; } } else { if (dlg_fill_name(&linker->profile->name) < 0) return; if (cdbf.add(cdbc, &dlg_prof_noval_buf, 1, profile_timeout, NULL) < 0) { LM_ERR("cannot insert profile into CacheDB\n"); return; } } } } int set_dlg_profile(struct dlg_cell *dlg, str *value, struct dlg_profile_table *profile, char is_replicated) { struct dlg_profile_link *linker; /* get current dialog */ if (dlg==NULL) { LM_ERR("dialog was not yet created - script error\n"); return -1; } /* build new linker */ linker = (struct dlg_profile_link*)shm_malloc( sizeof(struct dlg_profile_link) + (profile->has_value?value->len:0) ); if (linker==NULL) { LM_ERR("no more shm memory\n"); return -1; } memset(linker, 0, sizeof(struct dlg_profile_link)); /* set backpointer to profile */ linker->profile = profile; /* set the value */ if (profile->has_value) { linker->value.s = (char*)(linker+1); memcpy( linker->value.s, value->s, value->len); linker->value.len = value->len; } /* add linker to the dialog and profile */ link_dlg_profile( linker, dlg, is_replicated); dlg->flags |= DLG_FLAG_VP_CHANGED; return 0; } int unset_dlg_profile(struct dlg_cell *dlg, str *value, struct dlg_profile_table *profile) { struct dlg_profile_link *linker; struct dlg_profile_link *linker_prev; struct dlg_entry *d_entry; /* get current dialog */ if (dlg==NULL) { LM_ERR("dialog was not yet created - script error\n"); return -1; } /* check the dialog linkers */ d_entry = &d_table->entries[dlg->h_entry]; dlg_lock( d_table, d_entry); linker = dlg->profile_links; linker_prev = NULL; for( ; linker ; linker_prev=linker,linker=linker->next) { if (linker->profile==profile) { if (profile->has_value==0) { goto found; } else if (value && value->len==linker->value.len && memcmp(value->s,linker->value.s,value->len)==0){ goto found; } /* allow further search - maybe the dialog is inserted twice in * the same profile, but with different values -bogdan */ } } dlg_unlock( d_table, d_entry); return -1; found: /* table still locked */ /* remove the linker element from dialog */ if (linker_prev==NULL) { dlg->profile_links = linker->next; } else { linker_prev->next = linker->next; } linker->next = NULL; dlg->flags |= DLG_FLAG_VP_CHANGED; dlg_unlock( d_table, d_entry); /* remove linker from profile table and free it */ destroy_linkers(linker, 0); return 1; } int is_dlg_in_profile(struct dlg_cell *dlg, struct dlg_profile_table *profile, str *value) { struct dlg_profile_link *linker; struct dlg_entry *d_entry; /* get current dialog */ if (dlg==NULL) return -1; /* check the dialog linkers */ d_entry = &d_table->entries[dlg->h_entry]; dlg_lock( d_table, d_entry); for( linker=dlg->profile_links ; linker ; linker=linker->next) { if (linker->profile==profile) { if (profile->has_value==0) { dlg_unlock( d_table, d_entry); return 1; } else if (value && value->len==linker->value.len && memcmp(value->s,linker->value.s,value->len)==0){ dlg_unlock( d_table, d_entry); return 1; } /* allow further search - maybe the dialog is inserted twice in * the same profile, but with different values -bogdan */ } } dlg_unlock( d_table, d_entry); return -1; } unsigned int get_profile_size(struct dlg_profile_table *profile, str *value) { unsigned int n = 0, i; map_t entry ; void ** dest; int ret; if (profile->has_value==0) { /* iterate through the hash and count all records */ if (cdbc && (profile->repl_type == REPL_CACHEDB)) { if (dlg_fill_name(&profile->name) < 0) goto failed; ret = cdbf.get_counter(cdbc, &dlg_prof_noval_buf, (int *)&n); if (ret == -2) { n = 0; } else if (ret < 0) { LM_ERR("cannot fetch profile from CacheDB\n"); goto failed; } } else { for( i=0; isize; i++ ) { lock_set_get( profile->locks, i); n += profile->counts[i]; lock_set_release( profile->locks, i); } } n += replicate_profiles_count(profile->repl); } else { if( value==NULL ) { if (cdbc && (profile->repl_type == REPL_CACHEDB)) { if (dlg_fill_size(&profile->name) < 0) goto failed; ret = cdbf.get_counter(cdbc, &dlg_prof_size_buf, (int *)&n); if (ret == -2) { n = 0; } else if (ret < 0) { LM_ERR("cannot fetch profile from CacheDB\n"); goto failed; } } else { for( i=0; isize; i++ ) { lock_set_get( profile->locks, i); n += map_size(profile->entries[i]); lock_set_release( profile->locks, i); } } } else { if (cdbc && (profile->repl_type == REPL_CACHEDB)) { if (dlg_fill_value(&profile->name, value) < 0) goto failed; ret = cdbf.get_counter(cdbc, &dlg_prof_val_buf, (int *)&n); if (ret == -2) { n = 0; } else if (ret < 0) { LM_ERR("cannot fetch profile from CacheDB\n"); goto failed; } } else { /* iterate through the hash entry and count only matching */ /* calculate the hash position */ i = calc_hash_profile( value, NULL, profile); n = 0; lock_set_get( profile->locks, i); entry = profile->entries[i]; dest = map_find(entry,*value); if( dest ) n = repl_prof_get_all(dest); lock_set_release( profile->locks, i); } } } return n; failed: LM_ERR("error while fetching cachedb key\n"); return 0; } /****************************** MI commands *********************************/ struct mi_root * mi_get_profile(struct mi_root *cmd_tree, void *param ) { struct mi_node* node; struct mi_root* rpl_tree= NULL; struct mi_node* rpl = NULL; struct mi_attr* attr; struct dlg_profile_table *profile; str *value; str *profile_name; unsigned int size; int len; char *p; node = cmd_tree->node.kids; if (node==NULL || !node->value.s || !node->value.len) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); profile_name = &node->value; if (node->next) { node = node->next; if (!node->value.s || !node->value.len) return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM)); if (node->next) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); value = &node->value; } else { value = NULL; } /* search for the profile */ profile = search_dlg_profile( profile_name ); if (profile==NULL) return init_mi_tree( 404, MI_SSTR("Profile not found")); size = get_profile_size( profile , value ); rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; node = add_mi_node_child(rpl, MI_DUP_VALUE, "profile", 7, NULL, 0); if (node==0) { free_mi_tree(rpl_tree); return NULL; } attr = add_mi_attr(node, MI_DUP_VALUE, "name", 4, profile->name.s, profile->name.len); if(attr == NULL) { goto error; } if (value) { attr = add_mi_attr(node, MI_DUP_VALUE, "value", 5, value->s, value->len); } else { attr = add_mi_attr(node, MI_DUP_VALUE, "value", 5, NULL, 0); } if(attr == NULL) { goto error; } p= int2str((unsigned long)size, &len); attr = add_mi_attr(node, MI_DUP_VALUE, "count", 5, p, len); if(attr == NULL) { goto error; } if (profile->repl_type == REPL_CACHEDB) { attr = add_mi_attr(node, MI_DUP_VALUE, "shared", 6, "yes", 3); } else { attr = add_mi_attr(node, MI_DUP_VALUE, "shared", 6, "no", 2); } if (attr == NULL) { goto error; } if (profile->repl_type == REPL_PROTOBIN) { attr = add_mi_attr(node, MI_DUP_VALUE, "replicated", 10, "yes", 3); } else { attr = add_mi_attr(node, MI_DUP_VALUE, "replicated", 10, "no", 2); } if (attr == NULL) { goto error; } return rpl_tree; error: free_mi_tree(rpl_tree); return NULL; } static inline int add_val_to_rpl(void * param, str key, void * val) { struct mi_node* rpl = (struct mi_node* ) param; struct mi_node* node; struct mi_attr* attr; int len; char *p; int counter; node = add_mi_node_child(rpl, MI_DUP_VALUE, "value", 5, key.s , key.len ); if( node == NULL ) return -1; counter = repl_prof_get_all(&val); p= int2str((unsigned long)counter, &len); attr = add_mi_attr(node, MI_DUP_VALUE, "count", 5, p, len ); if( attr == NULL ) return -1; return 0; } struct mi_root * mi_get_profile_values(struct mi_root *cmd_tree, void *param ) { struct mi_node* node; struct mi_root* rpl_tree= NULL; struct mi_node* rpl = NULL; struct dlg_profile_table *profile; str *profile_name; int i, ret,n; str tmp; node = cmd_tree->node.kids; if (node==NULL || !node->value.s || !node->value.len) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); profile_name = &node->value; if (node->next) { node = node->next; if (!node->value.s || !node->value.len) return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM)); if (node->next) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); } profile = search_dlg_profile( profile_name ); if (profile==NULL) return init_mi_tree( 404, MI_SSTR("Profile not found")); if (profile->repl_type == REPL_CACHEDB) return init_mi_tree( 405, MI_SSTR("Unsupported command for shared profiles")); /* gather dialog count for all values in this profile */ rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) goto error; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; ret = 0; if( profile->has_value ) { for( i=0; isize; i++ ) { lock_set_get( profile->locks, i); ret |= map_for_each( profile->entries[i], add_val_to_rpl, rpl); lock_set_release( profile->locks, i); } } else { n = 0; for( i=0; isize; i++ ) { lock_set_get( profile->locks, i); n += profile->counts[i]; lock_set_release( profile->locks, i); } tmp.s = "WITHOUT VALUE"; tmp.len = sizeof("WITHOUT VALUE")-1; ret = add_val_to_rpl(rpl, tmp , (void *)(long)n ); } if ( ret ) goto error; return rpl_tree; error: if (rpl_tree) free_mi_tree(rpl_tree); return NULL; } struct mi_root * mi_profile_list(struct mi_root *cmd_tree, void *param ) { struct mi_node* node; struct mi_root* rpl_tree= NULL; struct mi_node* rpl = NULL; struct dlg_profile_table *profile; str *profile_name; str *value; unsigned int i,found,n; struct dlg_entry *d_entry; struct dlg_cell *cur_dlg; struct dlg_profile_link *cur_link; node = cmd_tree->node.kids; if (node==NULL || !node->value.s || !node->value.len) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); profile_name = &node->value; if (node->next) { node = node->next; if (!node->value.s || !node->value.len) return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM)); if (node->next) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); value = &node->value; } else { value = NULL; } /* search for the profile */ profile = search_dlg_profile( profile_name ); if (profile==NULL) return init_mi_tree( 404, MI_SSTR("Profile not found")); rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; /* go through the hash and print the dialogs */ for( n=0,i=0; isize; i++) { d_entry = &(d_table->entries[i]); lock_set_get(d_table->locks,d_entry->lock_idx); cur_dlg = d_entry->first; while( cur_dlg ) { found = 0; cur_link = cur_dlg ->profile_links; while(cur_link) { if( cur_link->profile == profile && ( value == NULL || ( value->len == cur_link->value.len && !strncmp(value->s,cur_link->value.s, value->len)) )) { found = 1; break; } cur_link = cur_link->next; } if( found ) { if( mi_print_dlg( rpl, cur_dlg, 0) ) { lock_set_release(d_table->locks,d_entry->lock_idx); goto error; } n++; if ( (n % 50) == 0 ) flush_mi_tree(rpl_tree); } cur_dlg = cur_dlg->next; } lock_set_release(d_table->locks,d_entry->lock_idx); } return rpl_tree; error: free_mi_tree(rpl_tree); return NULL; } struct mi_root * mi_list_all_profiles(struct mi_root *cmd_tree, void *param ) { struct mi_node* node; struct mi_root* rpl_tree= NULL; struct mi_node* rpl = NULL; struct dlg_profile_table *profile; node = cmd_tree->node.kids; if (node!=NULL) return init_mi_tree( 401, MI_SSTR(MI_MISSING_PARM)); rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; profile = profiles; while (profile) { if (add_mi_node_child(rpl, 0, profile->name.s, profile->name.len, (profile->has_value? "1" : "0"), 1) == NULL) { LM_ERR("Out of mem\n"); free_mi_tree(rpl_tree); return init_mi_tree( 401, MI_SSTR(MI_INTERNAL_ERR)); } profile = profile->next; } return rpl_tree; } struct mi_root * mi_profile_terminate(struct mi_root *cmd_tree, void *param ) { struct mi_node* node; struct dlg_profile_table *profile; str *profile_name; str *value; unsigned int i; struct dlg_entry *d_entry; struct dlg_cell *cur_dlg; struct dlg_profile_link *cur_link; struct dialog_list *deleted = NULL, *delete_entry ; node = cmd_tree->node.kids; if (node==NULL || !node->value.s || !node->value.len) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); profile_name = &node->value; if (node->next) { node = node->next; if (!node->value.s || !node->value.len) return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM)); if (node->next) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); value = &node->value; } else { value = NULL; } profile = search_dlg_profile( profile_name ); if (profile==NULL) return init_mi_tree( 404, MI_SSTR("Profile not found")); for (i = 0; i < d_table->size; i++) { d_entry = &(d_table->entries[i]); lock_set_get(d_table->locks,d_entry->lock_idx); cur_dlg = d_entry->first; while( cur_dlg ) { cur_link = cur_dlg ->profile_links; while(cur_link) { if( cur_link->profile == profile && ( value == NULL || ( value->len == cur_link->value.len && !strncmp(value->s,cur_link->value.s, value->len)) )) { delete_entry = pkg_malloc(sizeof(struct dialog_list)); if (!delete_entry) { LM_CRIT("no more pkg memory\n"); lock_set_release(d_table->locks,d_entry->lock_idx); return init_mi_tree( 400, MI_SSTR(MI_INTERNAL_ERR)); } delete_entry->dlg = cur_dlg; delete_entry->next = deleted; deleted = delete_entry; ref_dlg_unsafe(cur_dlg, 1); break; } cur_link = cur_link->next; } cur_dlg = cur_dlg->next; } lock_set_release(d_table->locks,d_entry->lock_idx); delete_entry = deleted; while(delete_entry){ init_dlg_term_reason(delete_entry->dlg,"MI Termination",sizeof("MI Termination")-1); if ( dlg_end_dlg( delete_entry->dlg, NULL) ) { while(delete_entry){ deleted = delete_entry; delete_entry = delete_entry->next; pkg_free(deleted); } LM_CRIT("eror while terminating dlg\n"); return init_mi_tree( 400, MI_SSTR("Dialog internal error")); } unref_dlg(delete_entry->dlg, 1); deleted = delete_entry; delete_entry = delete_entry->next; pkg_free(deleted); } deleted = NULL; } return init_mi_tree(400, MI_SSTR(MI_OK)); } opensips-2.2.2/modules/dialog/dlg_profile.h000066400000000000000000000077101300170765700207360ustar00rootroot00000000000000/* * Copyright (C) 2009-2014 OpenSIPS Solutions * Copyright (C) 2008 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-04-20 initial version (bogdan) * 2009-09-16 speed optimization (andreidragus) * */ #include "../../map.h" #ifndef _DIALOG_DLG_PROFILE_H_ #define _DIALOG_DLG_PROFILE_H_ #include "../../parser/msg_parser.h" #include "../../locking.h" #include "../../str.h" struct lock_set_list { gen_lock_set_t * locks; struct lock_set_list * next; }; struct dlg_profile_link { str value; int hash_idx; struct dlg_profile_link *next; struct dlg_profile_table *profile; }; struct repl_prof_novalue; enum repl_types {REPL_NONE=0, REPL_CACHEDB=1, REPL_PROTOBIN}; struct dlg_profile_table { str name; unsigned int has_value; enum repl_types repl_type; unsigned int size; gen_lock_set_t * locks; /* * information for profiles with values */ map_t * entries; /* * information for profiles without values */ int * counts; /* * information used for profile replication without values */ struct repl_prof_novalue *repl; struct dlg_profile_table *next; }; struct dialog_list{ struct dlg_cell *dlg; struct dialog_list *next; }; typedef int (*set_dlg_profile_f)(struct dlg_cell *dlg, str *value, struct dlg_profile_table *profile, char is_replicated); typedef int (*unset_dlg_profile_f)(struct dlg_cell *dlg, str *value, struct dlg_profile_table *profile); typedef unsigned int (*get_profile_size_f)(struct dlg_profile_table *profile, str *value); typedef int (*add_profiles_f)(char* profiles, unsigned int has_value); typedef struct dlg_profile_table* (*search_dlg_profile_f)(str *name); struct dlg_profile_value_name { int size; str **values_string; int *values_count; }; int add_profile_definitions( char* profiles, unsigned int has_value); void destroy_dlg_profiles(); struct dlg_profile_table* search_dlg_profile(str *name); struct dlg_profile_table *get_dlg_profile(str *name); void destroy_linkers(struct dlg_profile_link *linker, char is_replicated); int set_dlg_profile(struct dlg_cell *dlg, str *value, struct dlg_profile_table *profile, char is_replicated); int unset_dlg_profile(struct dlg_cell *dlg, str *value, struct dlg_profile_table *profile); int is_dlg_in_profile(struct dlg_cell *dlg, struct dlg_profile_table *profile, str *value); unsigned int get_profile_size(struct dlg_profile_table *profile, str *value); struct mi_root * mi_get_profile(struct mi_root *cmd_tree, void *param ); struct mi_root * mi_get_profile_values(struct mi_root *cmd_tree, void *param ); struct mi_root * mi_profile_list(struct mi_root *cmd_tree, void *param ); struct mi_root * mi_list_all_profiles(struct mi_root *cmd_tree, void *param ); struct mi_root * mi_profile_terminate(struct mi_root *cmd_tree, void *param ); void get_value_names(struct dlg_profile_table *profile, struct dlg_profile_value_name *); /* cachedb interface */ extern str cdb_val_prefix; extern str cdb_noval_prefix; extern str cdb_size_prefix; extern str cdb_url; extern int profile_timeout; extern int profile_replicate_cluster; extern struct dlg_profile_table *profiles; int init_cachedb(); void destroy_cachedb(int); int init_cachedb_utils(void); #endif opensips-2.2.2/modules/dialog/dlg_repl_profile.h000066400000000000000000000102371300170765700217560ustar00rootroot00000000000000/* * dialog module - basic support for dialog tracking * * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2015-06-10 initial version (razvanc) */ #include "../../ut.h" #include "../../bin_interface.h" #include "../../socket_info.h" #include "../../timer.h" #ifndef _DIALOG_DLG_PROFILE_REPLICATION_H_ #define _DIALOG_DLG_PROFILE_REPLICATION_H_ typedef struct repl_prof_count { int counter; time_t update; int machine_id; struct repl_prof_count *next; } repl_prof_count_t; typedef struct repl_prof_novalue { gen_lock_t lock; struct repl_prof_count *dsts; } repl_prof_novalue_t; typedef struct repl_prof_value { int counter; repl_prof_novalue_t *noval; } repl_prof_value_t; /* profiles functions */ extern int accept_repl_profiles; extern int accept_replicated_profile_timeout; extern int repl_prof_auth_check; extern int repl_prof_buffer_th; extern int repl_prof_utimer; extern int repl_prof_timer_check; extern int repl_prof_timer_expire; int repl_prof_init(void); int repl_prof_remove(str *name, str *value); int repl_prof_dest(modparam_t type, void *val); int replicate_profiles_count(repl_prof_novalue_t *rp); #define REPLICATION_DLG_PROFILE 4 #define DLG_REPL_PROF_TIMER 10 #define DLG_REPL_PROF_EXPIRE_TIMER 10 #define DLG_REPL_PROF_BUF_THRESHOLD 1400 static void free_profile_val_t (repl_prof_value_t *val){ repl_prof_count_t *head = val->noval->dsts; repl_prof_count_t *tmp; while(head){ tmp = head; head = head->next; shm_free(tmp); } shm_free(val); } static inline void free_profile_val(void *val){ free_profile_val_t(( repl_prof_value_t*) val); } static inline repl_prof_novalue_t *repl_prof_allocate(void) { repl_prof_novalue_t *rp; rp = shm_malloc(sizeof(repl_prof_novalue_t)); if (!rp) { /* if there is no more shm memory, there's not much that you can do * anyway */ LM_WARN("no more shm mem\n"); return NULL; } memset(rp, 0, sizeof(repl_prof_novalue_t)); lock_init(&rp->lock); return rp; } static inline void repl_prof_inc(void **dst) { repl_prof_value_t *rp; if (accept_repl_profiles) { /* if the destination does not exist, create it */ if (!*dst) { rp = shm_malloc(sizeof(repl_prof_value_t)); if (!rp) { LM_ERR("no more shm memory to allocate repl_prof_value\n"); return; } memset(rp, 0, sizeof(repl_prof_value_t)); *dst = rp; } else { rp = (repl_prof_value_t *)(*dst); } rp->counter++; } else { (*dst) = (void*)((long)(*dst) + 1); } } static inline int repl_prof_get_all(void **dst) { repl_prof_value_t *rp; if (accept_repl_profiles) { rp = (repl_prof_value_t *)(*dst); if (!rp->noval) return rp->counter; return rp->counter + replicate_profiles_count(rp->noval); } else { return (int)(long)(*dst); } } static inline void repl_prof_dec(void **dst) { repl_prof_value_t *rp; int counter; if (accept_repl_profiles) { rp = (repl_prof_value_t *)(*dst); rp->counter--; /* check all the others to see if we should delete the profile */ counter = repl_prof_get_all(dst); if (counter == 0) { if (rp->noval) shm_free(rp->noval); shm_free(rp); *dst = 0; } } else { (*dst) = (void*)((long)(*dst) - 1); } } static inline int repl_prof_get(void **dst) { repl_prof_value_t *rp; if (accept_repl_profiles) { rp = (repl_prof_value_t *)(*dst); return rp->counter; } else { return (int)(long)(*dst); } } #endif /* _DIALOG_DLG_PROFILE_REPLICATION_H_ */ opensips-2.2.2/modules/dialog/dlg_replication.c000066400000000000000000000700211300170765700215750ustar00rootroot00000000000000/* * dialog module - basic support for dialog tracking * * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2013-04-12 initial version (Liviu) */ #include "dlg_hash.h" #include "dlg_db_handler.h" #include "dlg_profile.h" #include "dlg_replication.h" #include "dlg_repl_profile.h" #include "../../resolve.h" #include "../../forward.h" extern int active_dlgs_cnt; extern int early_dlgs_cnt; extern int dlg_enable_stats; extern stat_var *active_dlgs; extern stat_var *processed_dlgs; extern stat_var *create_sent; extern stat_var *update_sent; extern stat_var *delete_sent; extern stat_var *create_recv; extern stat_var *update_recv; extern stat_var *delete_recv; struct clusterer_binds clusterer_api; static void dlg_replicated_profiles(struct receive_info *ri, int server_id); static struct socket_info * fetch_socket_info(str *addr) { struct socket_info *sock; int port, proto; str host; if (!addr->s || addr->s[0] == 0) return NULL; if (parse_phostport(addr->s, addr->len, &host.s, &host.len, &port, &proto) != 0) { LM_ERR("bad socket <%.*s>\n", addr->len, addr->s); return NULL; } sock = grep_sock_info(&host, (unsigned short) port, (unsigned short) proto); if (!sock) { LM_WARN("non-local socket <%.*s>...ignoring\n", addr->len, addr->s); } return sock; } /* Binary Packet receiving functions */ /** * replicates a confirmed dialog from another OpenSIPS instance * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_create(struct dlg_cell *cell, str *ftag, str *ttag, int safe) { int next_id, h_entry; unsigned int dir, dst_leg; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1, cseq2, contact1, contact2, rroute1, rroute2, mangled_fu, mangled_tu; str sock, vars, profiles; struct dlg_cell *dlg = NULL; struct socket_info *caller_sock, *callee_sock; struct dlg_entry *d_entry; if_update_stat(dlg_enable_stats, processed_dlgs, 1); LM_DBG("Received replicated dialog!\n"); if (!cell) { bin_pop_str(&callid); bin_pop_str(&from_tag); bin_pop_str(&to_tag); bin_pop_str(&from_uri); bin_pop_str(&to_uri); dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); if (dlg) { LM_DBG("Dialog with ci '%.*s' is already created\n", callid.len, callid.s); unref_dlg_unsafe(dlg, 1, d_entry); dlg_unlock(d_table, d_entry); return 0; } dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto pre_linking_error; } } else { h_entry = dlg_hash(&cell->callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); from_tag = *ftag; to_tag = *ttag; dlg = cell; } bin_pop_int(&dlg->h_id); bin_pop_int(&dlg->start_ts); bin_pop_int(&dlg->state); next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id <= dlg->h_id) ? (dlg->h_id + 1) : next_id; if (bin_pop_str(&sock)) goto pre_linking_error; caller_sock = fetch_socket_info(&sock); if (bin_pop_str(&sock)) goto pre_linking_error; callee_sock = fetch_socket_info(&sock); if (!caller_sock || !callee_sock) { LM_ERR("Dialog in DB doesn't match any listening sockets\n"); goto pre_linking_error; } bin_pop_str(&cseq1); bin_pop_str(&cseq2); bin_pop_str(&rroute1); bin_pop_str(&rroute2); bin_pop_str(&contact1); bin_pop_str(&contact2); bin_pop_str(&mangled_fu); bin_pop_str(&mangled_tu); /* add the 2 legs */ /* TODO - sdp here */ if (dlg_add_leg_info(dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock, 0, 0,0) != 0 || dlg_add_leg_info(dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock, &mangled_fu, &mangled_tu,0) != 0) { LM_ERR("dlg_set_leg_info failed\n"); goto pre_linking_error; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* link the dialog into the hash */ dlg->h_id = d_entry->next_id++; if (!d_entry->first) d_entry->first = d_entry->last = dlg; else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref++; d_entry->cnt++; bin_pop_str(&vars); bin_pop_str(&profiles); bin_pop_int(&dlg->user_flags); bin_pop_int(&dlg->flags); bin_pop_int((void *) &dlg->tl.timeout); bin_pop_int(&dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_pop_int(&dlg->legs[callee_idx(dlg)].last_gen_cseq); if (dlg->tl.timeout <= (unsigned int) time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int) time(0); /* restore the timer values */ if (insert_dlg_timer(&dlg->tl, (int) dlg->tl.timeout) != 0) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); goto error; } if (dlg->state == DLG_STATE_CONFIRMED_NA || dlg->state == DLG_STATE_CONFIRMED) active_dlgs_cnt++; /* reference the dialog as kept in the timer list */ ref_dlg_unsafe(dlg, 1); LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n", dlg->tl.timeout, callid.len, callid.s, safe); dlg->lifetime = 0; /* Do not replicate the pinging - we might terminate dialogs badly when running as backup if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (insert_ping_timer(dlg) != 0) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { ref_dlg_unsafe(dlg, 1); } } */ if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg_unsafe(dlg, 1); } if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1); if_update_stat(dlg_enable_stats, active_dlgs, 1); run_load_callback_per_dlg(dlg); return 0; pre_linking_error: dlg_unlock(d_table, d_entry); if (dlg) destroy_dlg(dlg); return -1; error: dlg_unlock(d_table, d_entry); if (dlg) unref_dlg(dlg, 1); return -1; } /** * replicates the remote update of an ongoing dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_update(void) { struct dlg_cell *dlg; str call_id, from_tag, to_tag, from_uri, to_uri, vars, profiles; unsigned int dir, dst_leg; int timeout, h_entry; str st; struct dlg_entry *d_entry; bin_pop_str(&call_id); bin_pop_str(&from_tag); bin_pop_str(&to_tag); bin_pop_str(&from_uri); bin_pop_str(&to_uri); LM_DBG("replicated update for ['%.*s' '%.*s' '%.*s' '%.*s' '%.*s']\n", call_id.len, call_id.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s, from_uri.len, from_uri.s, to_uri.len, to_uri.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&call_id); d_entry = &d_table->entries[h_entry]; dlg_lock(d_table, d_entry); if (!dlg) { LM_DBG("dialog not found, building new\n"); dlg = build_new_dlg(&call_id, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto error; } return dlg_replicated_create(dlg, &from_tag, &to_tag, 0); } bin_skip_int(2); bin_pop_int(&dlg->state); bin_skip_str(2); bin_pop_str(&st); if (dlg_update_cseq(dlg, DLG_CALLER_LEG, &st, 0) != 0) { LM_ERR("failed to update caller cseq\n"); goto error; } bin_pop_str(&st); if (dlg_update_cseq(dlg, callee_idx(dlg), &st, 0) != 0) { LM_ERR("failed to update callee cseq\n"); goto error; } bin_skip_str(6); bin_pop_str(&vars); bin_pop_str(&profiles); bin_pop_int(&dlg->user_flags); bin_pop_int(&dlg->flags); bin_pop_int(&timeout); bin_skip_int(2); timeout -= time(0); LM_DBG("Received updated timeout of %d for dialog %.*s\n", timeout, call_id.len, call_id.s); if (dlg->lifetime != timeout) { dlg->lifetime = timeout; switch (update_dlg_timer(&dlg->tl, dlg->lifetime) ) { case -1: LM_ERR("failed to update dialog lifetime!\n"); /* continue */ case 0: /* timeout value was updated */ break; case 1: /* dlg inserted in timer list with new expire (reference it)*/ ref_dlg(dlg,1); } } unref_dlg_unsafe(dlg, 1, d_entry); if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 1, 1); return 0; error: dlg_unlock(d_table, d_entry); return -1; } /** * replicates the remote deletion of a dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_delete(void) { str call_id, from_tag, to_tag; unsigned int dir, dst_leg; struct dlg_cell *dlg; int old_state, new_state, unref, ret; bin_pop_str(&call_id); bin_pop_str(&from_tag); bin_pop_str(&to_tag); LM_DBG("Deleting dialog with callid: %.*s\n", call_id.len, call_id.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); if (!dlg) { LM_ERR("dialog not found (callid: |%.*s| ftag: |%.*s|\n", call_id.len, call_id.s, from_tag.len, from_tag.s); return -1; } dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 1); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); /* simulate BYE received from caller */ next_state_dlg(dlg, DLG_EVENT_REQBYE, DLG_DIR_DOWNSTREAM, &old_state, &new_state, &unref, dlg->legs_no[DLG_LEG_200OK], 1); if (old_state == new_state) { LM_ERR("duplicate dialog delete request (callid: |%.*s|" "ftag: |%.*s|\n", call_id.len, call_id.s, from_tag.len, from_tag.s); return -1; } ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else if (ret > 0) { LM_DBG("dlg expired (not in timer list) on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); } else { /* dialog sucessfully removed from timer -> unref */ unref++; } unref_dlg(dlg, 1 + unref); if_update_stat(dlg_enable_stats, active_dlgs, -1); return 0; } /* Binary Packet sending functions */ /** * replicates a locally created dialog to all the destinations * specified with the 'replicate_dialogs' modparam */ void replicate_dialog_created(struct dlg_cell *dlg) { static str module_name = str_init("dialog"); int callee_leg; str *vars, *profiles; if (bin_init(&module_name, REPLICATION_DLG_CREATED, BIN_VERSION) != 0) goto error; bin_push_int(clusterer_api.get_my_id()); callee_leg = callee_idx(dlg); bin_push_str(&dlg->callid); bin_push_str(&dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(&dlg->legs[callee_leg].tag); bin_push_str(&dlg->from_uri); bin_push_str(&dlg->to_uri); bin_push_int(dlg->h_id); bin_push_int(dlg->start_ts); bin_push_int(dlg->state); bin_push_str(&dlg->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (dlg->legs[callee_leg].bind_addr) bin_push_str(&dlg->legs[callee_leg].bind_addr->sock_str); else bin_push_str(NULL); bin_push_str(&dlg->legs[DLG_CALLER_LEG].r_cseq); bin_push_str(&dlg->legs[callee_leg].r_cseq); bin_push_str(&dlg->legs[DLG_CALLER_LEG].route_set); bin_push_str(&dlg->legs[callee_leg].route_set); bin_push_str(&dlg->legs[DLG_CALLER_LEG].contact); bin_push_str(&dlg->legs[callee_leg].contact); bin_push_str(&dlg->legs[callee_leg].from_uri); bin_push_str(&dlg->legs[callee_leg].to_uri); /* XXX: on shutdown only? */ vars = write_dialog_vars(dlg->vals); dlg_lock_dlg(dlg); profiles = write_dialog_profiles(dlg->profile_links); dlg_unlock_dlg(dlg); bin_push_str(vars); bin_push_str(profiles); bin_push_int(dlg->user_flags); bin_push_int(dlg->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); bin_push_int((unsigned int)time(0) + dlg->tl.timeout - get_ticks()); bin_push_int(dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_push_int(dlg->legs[callee_leg].last_gen_cseq); if (clusterer_api.send_to(dialog_replicate_cluster, PROTO_BIN) < 0) goto error; if_update_stat(dlg_enable_stats,create_sent,1); return; error: LM_ERR("Failed to replicate created dialog\n"); } /** * replicates a local dialog update to all the destinations * specified with the 'replicate_dialogs' modparam */ void replicate_dialog_updated(struct dlg_cell *dlg) { static str module_name = str_init("dialog"); int callee_leg; str *vars, *profiles; if (bin_init(&module_name, REPLICATION_DLG_UPDATED, BIN_VERSION) != 0) goto error; callee_leg = callee_idx(dlg); bin_push_int(clusterer_api.get_my_id()); bin_push_str(&dlg->callid); bin_push_str(&dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(&dlg->legs[callee_leg].tag); bin_push_str(&dlg->from_uri); bin_push_str(&dlg->to_uri); bin_push_int(dlg->h_id); bin_push_int(dlg->start_ts); bin_push_int(dlg->state); bin_push_str(&dlg->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (dlg->legs[callee_leg].bind_addr) bin_push_str(&dlg->legs[callee_leg].bind_addr->sock_str); else bin_push_str(NULL); bin_push_str(&dlg->legs[DLG_CALLER_LEG].r_cseq); bin_push_str(&dlg->legs[callee_leg].r_cseq); bin_push_str(&dlg->legs[DLG_CALLER_LEG].route_set); bin_push_str(&dlg->legs[callee_leg].route_set); bin_push_str(&dlg->legs[DLG_CALLER_LEG].contact); bin_push_str(&dlg->legs[callee_leg].contact); bin_push_str(&dlg->legs[callee_leg].from_uri); bin_push_str(&dlg->legs[callee_leg].to_uri); /* XXX: on shutdown only? */ vars = write_dialog_vars(dlg->vals); dlg_lock_dlg(dlg); profiles = write_dialog_profiles(dlg->profile_links); dlg_unlock_dlg(dlg); bin_push_str(vars); bin_push_str(profiles); bin_push_int(dlg->user_flags); bin_push_int(dlg->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); bin_push_int((unsigned int)time(0) + dlg->tl.timeout - get_ticks()); bin_push_int(dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_push_int(dlg->legs[callee_leg].last_gen_cseq); if (clusterer_api.send_to(dialog_replicate_cluster, PROTO_BIN) < 0) { LM_ERR("replicate dialog updated failed\n"); return; } if_update_stat(dlg_enable_stats,update_sent,1); return; error: LM_ERR("Failed to replicate updated dialog\n"); } /** * replicates a local dialog delete event to all the destinations * specified with the 'replicate_dialogs' modparam */ void replicate_dialog_deleted(struct dlg_cell *dlg) { static str module_name = str_init("dialog"); if (bin_init(&module_name, REPLICATION_DLG_DELETED, BIN_VERSION) != 0) goto error; bin_push_int(clusterer_api.get_my_id()); bin_push_str(&dlg->callid); bin_push_str(&dlg->legs[DLG_CALLER_LEG].tag); bin_push_str(&dlg->legs[callee_idx(dlg)].tag); if (clusterer_api.send_to(dialog_replicate_cluster, PROTO_BIN) < 0) { goto error; } return; error: LM_ERR("Failed to replicate deleted dialog\n"); } /** * receive_binary_packet (callback) - receives a cmd_type, specifying the * purpose of the data encoded in the received UDP packet */ void receive_prof_binary_packet(int packet_type, struct receive_info *ri, int server_id) { char *ip; unsigned short port; if (packet_type == SERVER_TEMP_DISABLED) { get_su_info(&ri->src_su.s, ip, port); LM_INFO("server: %s:%hu temporary disabled\n", ip, port); return; } if (packet_type == SERVER_TIMEOUT) { LM_INFO("server with clusterer id %d timeout\n", server_id); return; } if (packet_type != REPLICATION_DLG_PROFILE) { LM_WARN("bad packet type\n"); return; } dlg_replicated_profiles(ri, server_id); } void receive_dlg_binary_packet(int packet_type, struct receive_info *ri, void *att) { int rc; char *ip; unsigned short port; int server_id; rc = bin_pop_int(&server_id); if (rc < 0) return; LM_DBG("Received a binary packet!\n"); if(get_bin_pkg_version() != BIN_VERSION){ LM_ERR("incompatible bin protocol version\n"); return; } if (!accept_replicated_dlg) { get_su_info(&ri->src_su.s, ip, port); LM_WARN("Unwanted dialog packet received from %s:%hu (type=%d)\n", ip, port, packet_type); return; } if(!clusterer_api.check(accept_replicated_dlg, &ri->src_su, server_id, ri->proto)) return; switch (packet_type) { case REPLICATION_DLG_CREATED: LM_DBG("AAAA dlg_replicated_create\n"); rc = dlg_replicated_create(NULL, NULL, NULL, 1); if_update_stat(dlg_enable_stats, create_recv, 1); break; case REPLICATION_DLG_UPDATED: LM_DBG("AAAA dlg_replicated_update\n"); rc = dlg_replicated_update(); if_update_stat(dlg_enable_stats, update_recv, 1); break; case REPLICATION_DLG_DELETED: LM_DBG("AAAA dlg_replicated_deleted\n"); rc = dlg_replicated_delete(); if_update_stat(dlg_enable_stats, delete_recv, 1); break; default: rc = -1; get_su_info(&ri->src_su.s, ip, port); LM_WARN("Invalid dialog binary packet command: %d (from %s:%hu)\n", packet_type, ip, port); } if (rc != 0) LM_ERR("Failed to process a binary packet!\n"); } /** * From now on, we only have replication for dialog profiles */ typedef struct repl_prof_repl_dst { int id; str dst; time_t *last_msg; union sockaddr_union to; } repl_prof_repl_dst_t; typedef struct repl_prof_repl_dst_new { int id; str dst; time_t *last_msg; } repl_prof_repl_dst_new_t; int repl_prof_buffer_th = DLG_REPL_PROF_BUF_THRESHOLD; int repl_prof_utimer = DLG_REPL_PROF_TIMER; int repl_prof_timer_check = DLG_REPL_PROF_TIMER; int repl_prof_timer_expire = DLG_REPL_PROF_EXPIRE_TIMER; static void repl_prof_utimer_f(utime_t ticks, void *param); static void repl_prof_timer_f(unsigned int ticks, void *param); int repl_prof_init(void) { if (!profile_replicate_cluster && !accept_repl_profiles) return 0; if (repl_prof_timer_check < 0) { LM_ERR("negative replicate timer for profiles check %d\n", repl_prof_timer_check); return -1; } if (repl_prof_timer_expire < 0) { LM_ERR("negative replicate expire timer for profiles %d\n", repl_prof_timer_expire); return -1; } if (register_timer("dialog-repl-profiles-timer", repl_prof_timer_f, NULL, repl_prof_timer_check, TIMER_FLAG_DELAY_ON_DELAY) < 0) { LM_ERR("failed to register profiles utimer\n"); return -1; } if (!profile_replicate_cluster) return 0; if (repl_prof_utimer < 0) { LM_ERR("negative replicate timer for profiles %d\n", repl_prof_utimer); return -1; } if (repl_prof_buffer_th < 0) { LM_ERR("negative replicate buffer threshold for profiles %d\n", repl_prof_buffer_th); return -1; } if (register_utimer("dialog-repl-profiles-utimer", repl_prof_utimer_f, NULL, repl_prof_utimer * 1000, TIMER_FLAG_DELAY_ON_DELAY) < 0) { LM_ERR("failed to register profiles utimer\n"); return -1; } if (repl_prof_buffer_th > (BUF_SIZE * 0.9)) { LM_WARN("Buffer size too big %d - profiles information might get lost", repl_prof_buffer_th); return -1; } return 0; } /* profiles replication */ static inline void dlg_replicate_profiles(void) { if (clusterer_api.send_to(profile_replicate_cluster, PROTO_BIN) < 0) { goto error; } return; error: LM_ERR("Failed to replicate profile dialog\n"); } static repl_prof_count_t* find_destination(repl_prof_novalue_t *noval, int machine_id) { repl_prof_count_t *head; head = noval->dsts; while(head != NULL){ if( head->machine_id == machine_id ) break; head=head->next; } if(head == NULL){ head = shm_malloc(sizeof(repl_prof_count_t)); if(head == NULL){ LM_ERR("no more shm memory\n"); goto error; } head->machine_id = machine_id; head->next = noval->dsts; noval->dsts = head; } return head; error: return NULL; } static void dlg_replicated_profiles(struct receive_info *ri, int server_id) { time_t now; str name; str value; char *ip; unsigned short port; unsigned int counter; struct dlg_profile_table *profile; int has_value; int i; void **dst; repl_prof_value_t *rp; repl_prof_count_t *destination; /* optimize profile search */ struct dlg_profile_table *old_profile = NULL; str old_name; now = time(0); //*repl_prof_dests[index].last_msg = now; for (;;) { if (bin_pop_str(&name) == 1) break; /* pop'ed all pipes */ /* check if the same profile was sent */ if (!old_profile || old_name.len != name.len || memcmp(name.s, old_name.s, name.len) != 0) { old_profile = get_dlg_profile(&name); if (!old_profile) { get_su_info(&ri->src_su.s, ip, port); LM_WARN("received unknown profile <%.*s> from %s:%hu\n", name.len, name.s, ip, port); } old_name = name; } profile = old_profile; if (bin_pop_int(&has_value) < 0) { LM_ERR("cannot pop profile's has_value int\n"); return; } if (has_value) { if (!profile->has_value) { get_su_info(&ri->src_su.s, ip, port); LM_WARN("The other end does not have a value for this profile:" "<%.*s> [%s:%hu]\n", profile->name.len, profile->name.s, ip, port); profile = NULL; } if (bin_pop_str(&value)) { LM_ERR("cannot pop the value of the profile\n"); return; } } if (bin_pop_int(&counter) < 0) { LM_ERR("cannot pop profile's counter\n"); return; } if (profile) { if (!profile->has_value) { lock_get(&profile->repl->lock); destination = find_destination(profile->repl, server_id); if(destination == NULL){ lock_release(&profile->repl->lock); return; } destination->counter = counter; destination->update = now; lock_release(&profile->repl->lock); } else { /* XXX: hack to make sure we find the proper index */ i = core_hash(&value, NULL, profile->size); lock_set_get(profile->locks, i); /* if counter is 0 and we don't have it, don't try to create */ if (!counter) { dst = map_find(profile->entries[i], value); if (!dst) goto release; } else { dst = map_get(profile->entries[i], value); } if (!*dst) { rp = shm_malloc(sizeof(repl_prof_value_t)); if (!rp) { LM_ERR("no more shm memory to allocate repl_prof_value\n"); goto release; } memset(rp, 0, sizeof(repl_prof_value_t)); *dst = rp; } else { rp = (repl_prof_value_t *) * dst; } if (!rp->noval) rp->noval = repl_prof_allocate(); if (rp->noval) { lock_release(&rp->noval->lock); destination = find_destination(rp->noval, server_id); if (destination == NULL) { lock_release(&rp->noval->lock); lock_set_release(profile->locks, i); return; } destination->counter = counter; destination ->update = now; lock_release(&rp->noval->lock); } release: lock_set_release(profile->locks, i); } } } return; } static int repl_prof_add(str *name, int has_value, str *value, unsigned int count) { int ret = 0; if (bin_push_str(name) < 0) return -1; /* extra size to add the value indication but it's good * for servers profiles consistency checks */ if (bin_push_int(has_value) < 0) return -1; /* the other end should already know if the profile has a value or not */ if (value && bin_push_str(value) < 0) return -1; if ((ret = bin_push_int(count)) < 0) return -1; return ret; } int repl_prof_remove(str *name, str *value) { static str module_name = str_init("dialog"); if (profile_replicate_cluster <= 0) return 0; if (bin_init(&module_name, REPLICATION_DLG_PROFILE, BIN_VERSION) < 0) { LM_ERR("cannot initiate bin buffer\n"); return -1; } bin_push_int(clusterer_api.get_my_id()); if (repl_prof_add(name, value?1:0, value, 0) < 0) return -1; dlg_replicate_profiles(); return 0; } int replicate_profiles_count(repl_prof_novalue_t *rp) { int counter = 0; time_t now = time(0); repl_prof_count_t *head; lock_get(&rp->lock); head = rp->dsts; while (head != NULL) { /* if the replication expired, reset its counter */ if ((head->update + repl_prof_timer_expire) < now) head->counter = 0; counter += head->counter; head = head->next; } lock_release(&rp->lock); return counter; } static void repl_prof_timer_f(unsigned int ticks, void *param) { map_iterator_t it, del; unsigned int count; struct dlg_profile_table *profile; repl_prof_value_t *rp; void **dst; int i; for (profile = profiles; profile; profile = profile->next) { if (!profile->has_value || profile->repl_type != REPL_PROTOBIN) continue; for (i = 0; i < profile->size; i++) { lock_set_get(profile->locks, i); if (map_first(profile->entries[i], &it) < 0) { LM_ERR("map does not exist\n"); goto next_entry; } while (iterator_is_valid(&it)) { dst = iterator_val(&it); if (!dst || !*dst) { LM_ERR("[BUG] bogus map[%d] state\n", i); goto next_val; } count = repl_prof_get_all(dst); if (!count) { del = it; if (iterator_next(&it) < 0) LM_DBG("cannot find next iterator\n"); rp = (repl_prof_value_t *) iterator_delete(&del); if (rp) { free_profile_val_t(rp); /*if (rp->noval) shm_free(rp->noval); shm_free(rp);*/ } continue; } next_val: if (iterator_next(&it) < 0) break; } next_entry: lock_set_release(profile->locks, i); } } } static void repl_prof_utimer_f(utime_t ticks, void *param) { #define REPL_PROF_TRYSEND() \ do { \ if (ret > repl_prof_buffer_th) { \ /* send the buffer */ \ dlg_replicate_profiles(); \ replicated = 1; \ if (bin_init(&module_name, REPLICATION_DLG_PROFILE, BIN_VERSION) < 0) { \ LM_ERR("cannot initiate bin buffer\n"); \ return; \ } \ bin_push_int(clusterer_api.get_my_id()); \ } \ } while (0) struct dlg_profile_table *profile; static str module_name = str_init("dialog"); map_iterator_t it; unsigned int count; int replicated = 0; int i; int ret; void **dst; str *value; if (bin_init(&module_name, REPLICATION_DLG_PROFILE, BIN_VERSION) < 0) { LM_ERR("cannot initiate bin buffer\n"); return; } bin_push_int(clusterer_api.get_my_id()); for (profile = profiles; profile; profile = profile->next) { if (!(profile->repl_type&REPL_PROTOBIN)) continue; count = 0; if (!profile->has_value) { for (i = 0; i < profile->size; i++) { lock_set_get(profile->locks, i); count += profile->counts[i]; lock_set_release(profile->locks, i); } if ((ret = repl_prof_add(&profile->name, 0, NULL, count)) < 0) goto error; /* check if the profile should be sent */ REPL_PROF_TRYSEND(); } else { for (i = 0; i < profile->size; i++) { lock_set_get(profile->locks, i); if (map_first(profile->entries[i], &it) < 0) { LM_ERR("map does not exist\n"); goto next_entry; } while (iterator_is_valid(&it)) { dst = iterator_val(&it); if (!dst || !*dst) { LM_ERR("[BUG] bogus map[%d] state\n", i); goto next_val; } value = iterator_key(&it); if (!value) { LM_ERR("cannot retrieve profile's key\n"); goto next_val; } count = repl_prof_get(dst); if ((ret = repl_prof_add(&profile->name, 1, value, count)) < 0) goto error; /* check if the profile should be sent */ REPL_PROF_TRYSEND(); next_val: if (iterator_next(&it) < 0) break; } next_entry: lock_set_release(profile->locks, i); } } } goto done; error: LM_ERR("cannot add any more profiles in buffer\n"); done: /* check if there is anything else left to replicate */ if (!replicated) dlg_replicate_profiles(); #undef REPL_PROF_TRYSEND } opensips-2.2.2/modules/dialog/dlg_replication.h000066400000000000000000000036041300170765700216050ustar00rootroot00000000000000/* * dialog module - basic support for dialog tracking * * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2013-04-12 initial version (Liviu) */ #include "../../ut.h" #include "../../bin_interface.h" #include "../../socket_info.h" #include "../../timer.h" #include "../clusterer/api.h" #ifndef _DIALOG_DLG_REPLICATION_H_ #define _DIALOG_DLG_REPLICATION_H_ #define REPLICATION_DLG_CREATED 1 #define REPLICATION_DLG_UPDATED 2 #define REPLICATION_DLG_DELETED 3 #define BIN_VERSION 1 extern int accept_replicated_dlg; extern int dialog_replicate_cluster; extern struct clusterer_binds clusterer_api; void replicate_dialog_created(struct dlg_cell *dlg); void replicate_dialog_updated(struct dlg_cell *dlg); void replicate_dialog_deleted(struct dlg_cell *dlg); int dlg_replicated_create(struct dlg_cell *cell, str *ftag, str *ttag, int safe); int dlg_replicated_update(void); int dlg_replicated_delete(void); void receive_dlg_binary_packet(int packet_type, struct receive_info *ri,void *att); void receive_prof_binary_packet(int packet_type, struct receive_info *ri, int server_id); #endif /* _DIALOG_DLG_REPLICATION_H_ */ opensips-2.2.2/modules/dialog/dlg_req_within.c000066400000000000000000000401101300170765700214310ustar00rootroot00000000000000/* * Copyright (C) 2007-2009 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-07-10 initial version (ancuta) * 2008-04-04 added direction reporting in dlg callbacks (bogdan) * 2009-09-09 support for early dialogs added; proper handling of cseq * while PRACK is used (bogdan) */ #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../config.h" #include "../../socket_info.h" #include "../../parser/parse_methods.h" #include "../tm/dlg.h" #include "../tm/tm_load.h" #include "../../mi/tree.h" #include "dlg_hash.h" #include "dlg_req_within.h" #include "dlg_db_handler.h" #include "dlg_profile.h" #include "dlg_handlers.h" extern str dlg_extra_hdrs; int free_tm_dlg(dlg_t *td) { if(td) { if(td->route_set) free_rr(&td->route_set); pkg_free(td); } return 0; } dlg_t * build_dlg_t(struct dlg_cell * cell, int dst_leg, int src_leg) { dlg_t* td = NULL; str cseq; unsigned int loc_seq; td = (dlg_t*)pkg_malloc(sizeof(dlg_t)); if(!td){ LM_ERR("out of pkg memory\n"); return NULL; } memset(td, 0, sizeof(dlg_t)); if ((dst_leg == DLG_CALLER_LEG && (cell->flags & DLG_FLAG_PING_CALLER)) || (dst_leg == callee_idx(cell) && (cell->flags & DLG_FLAG_PING_CALLEE)) || (dst_leg == DLG_CALLER_LEG && (cell->flags & DLG_FLAG_REINVITE_PING_CALLER)) || (dst_leg == callee_idx(cell) && (cell->flags & DLG_FLAG_REINVITE_PING_CALLEE)) || cell->flags & DLG_FLAG_CSEQ_ENFORCE) { dlg_lock_dlg(cell); if (cell->legs[dst_leg].last_gen_cseq == 0) { /* no OPTIONS pings for this dlg yet */ dlg_unlock_dlg(cell); goto before_strcseq; } else { /* OPTIONS pings sent, use new cseq */ td->loc_seq.value = ++(cell->legs[dst_leg].last_gen_cseq); td->loc_seq.is_set=1; dlg_unlock_dlg(cell); goto after_strcseq; } } before_strcseq: /*local sequence number*/ cseq = cell->legs[dst_leg].r_cseq; if( !cseq.s || !cseq.len || str2int(&cseq, &loc_seq) != 0){ LM_ERR("invalid cseq\n"); goto error; } /*we don not increase here the cseq as this will be done by TM*/ td->loc_seq.value = loc_seq; td->loc_seq.is_set = 1; after_strcseq: /*route set*/ if( cell->legs[dst_leg].route_set.s && cell->legs[dst_leg].route_set.len){ if( parse_rr_body(cell->legs[dst_leg].route_set.s, cell->legs[dst_leg].route_set.len, &td->route_set) !=0){ LM_ERR("failed to parse route set\n"); goto error; } } /*remote target--- Request URI*/ if (cell->legs[dst_leg].contact.s==0 || cell->legs[dst_leg].contact.len==0){ LM_ERR("no contact available\n"); goto error; } td->rem_target = cell->legs[dst_leg].contact; td->rem_uri = (dst_leg==DLG_CALLER_LEG)? *dlg_leg_from_uri(cell,dst_leg): *dlg_leg_to_uri(cell,dst_leg); td->loc_uri = (dst_leg==DLG_CALLER_LEG)? *dlg_leg_to_uri(cell,dst_leg): *dlg_leg_from_uri(cell,dst_leg); td->id.call_id = cell->callid; td->id.rem_tag = cell->legs[dst_leg].tag; td->id.loc_tag = cell->legs[src_leg].tag; td->state= DLG_CONFIRMED; td->send_sock = cell->legs[dst_leg].bind_addr; /* link the dialog cell here - it will eventually be linked * within the upcoming created transaction */ td->dialog_ctx = cell; return td; error: free_tm_dlg(td); return NULL; } dlg_t * build_dialog_info(struct dlg_cell * cell, int dst_leg, int src_leg,char *reply_marker) { dlg_t* td = NULL; str cseq; unsigned int loc_seq; td = (dlg_t*)pkg_malloc(sizeof(dlg_t)); if(!td){ LM_ERR("out of pkg memory\n"); return NULL; } memset(td, 0, sizeof(dlg_t)); /*local sequence number*/ cseq = cell->legs[dst_leg].r_cseq; if( !cseq.s || !cseq.len || str2int(&cseq, &loc_seq) != 0){ LM_ERR("invalid cseq\n"); goto error; } if (cell->legs[dst_leg].last_gen_cseq == 0) cell->legs[dst_leg].last_gen_cseq = loc_seq+1; else cell->legs[dst_leg].last_gen_cseq++; *reply_marker = 0; td->loc_seq.value = cell->legs[dst_leg].last_gen_cseq -1; td->loc_seq.is_set = 1; /*route set*/ if( cell->legs[dst_leg].route_set.s && cell->legs[dst_leg].route_set.len){ if( parse_rr_body(cell->legs[dst_leg].route_set.s, cell->legs[dst_leg].route_set.len, &td->route_set) !=0){ LM_ERR("failed to parse route set\n"); goto error; } } /*remote target--- Request URI*/ if (cell->legs[dst_leg].contact.s==0 || cell->legs[dst_leg].contact.len==0){ LM_ERR("no contact available\n"); goto error; } td->rem_target = cell->legs[dst_leg].contact; td->rem_uri = (dst_leg==DLG_CALLER_LEG)? *dlg_leg_from_uri(cell,dst_leg): *dlg_leg_to_uri(cell,dst_leg); td->loc_uri = (dst_leg==DLG_CALLER_LEG)? *dlg_leg_to_uri(cell,dst_leg): *dlg_leg_from_uri(cell,dst_leg); td->id.call_id = cell->callid; td->id.rem_tag = cell->legs[dst_leg].tag; td->id.loc_tag = cell->legs[src_leg].tag; td->state= DLG_CONFIRMED; td->send_sock = cell->legs[dst_leg].bind_addr; /* link the dialog cell here - it will eventually be linked * within the upcoming created transaction */ td->dialog_ctx = cell; return td; error: free_tm_dlg(td); return NULL; } static void dual_bye_event(struct dlg_cell* dlg, struct sip_msg *req, int extra_unref) { int event, old_state, new_state, unref, ret; struct sip_msg *fake_msg=NULL; context_p old_ctx; context_p *new_ctx; event = DLG_EVENT_REQBYE; next_state_dlg(dlg, event, DLG_DIR_DOWNSTREAM, &old_state, &new_state, &unref, dlg->legs_no[DLG_LEG_200OK], 0); unref += extra_unref; if(new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED){ LM_DBG("removing dialog with h_entry %u and h_id %u\n", dlg->h_entry, dlg->h_id); /*destroy linkers */ dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); /* remove from timer */ ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } else if (ret > 0) { LM_DBG("dlg already expired (not in timer list) %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } else { /* successfully removed from timer list */ unref++; } if (req==NULL) { /* set new msg & processing context */ if (push_new_processing_context( dlg, &old_ctx, &new_ctx, &fake_msg)==0) { /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, fake_msg, DLG_DIR_NONE, 0); /* reset the processing context */ if (current_processing_ctx == NULL) *new_ctx = NULL; else context_destroy(CONTEXT_GLOBAL, *new_ctx); current_processing_ctx = old_ctx; } /* no CB run in case of failure FIXME */ } else { /* we should have the msg and context from upper levels */ /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, req, DLG_DIR_NONE, 0); } LM_DBG("first final reply\n"); /* derefering the dialog */ unref_dlg(dlg, unref); if_update_stat( dlg_enable_stats, active_dlgs, -1); } if(new_state == DLG_STATE_DELETED && old_state == DLG_STATE_DELETED ) { /* trash the dialog from DB and memory */ LM_DBG("second final reply\n"); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); /* force delete from mem */ unref_dlg(dlg, unref); } } /*callback function to handle responses to the BYE request */ void bye_reply_cb(struct cell* t, int type, struct tmcb_params* ps) { if(ps->param == NULL || *ps->param == NULL){ LM_ERR("invalid parameter\n"); return; } if(ps->code < 200){ LM_DBG("receiving a provisional reply\n"); return; } LM_DBG("receiving a final reply %d\n",ps->code); /* mark the transaction as belonging to this dialog */ t->dialog_ctx = *(ps->param); dual_bye_event( (struct dlg_cell *)(*(ps->param)), ps->req, 1); } static inline int build_extra_hdr(struct dlg_cell * cell, str *extra_hdrs, str *str_hdr) { char *p; str_hdr->len = dlg_extra_hdrs.len + (extra_hdrs?extra_hdrs->len:0); str_hdr->s = (char*)pkg_malloc( str_hdr->len * sizeof(char) ); if(!str_hdr->s){ LM_ERR("out of pkg memory\n"); goto error; } p = str_hdr->s; if (dlg_extra_hdrs.len) { memcpy( p, dlg_extra_hdrs.s, dlg_extra_hdrs.len); p += dlg_extra_hdrs.len; } if (extra_hdrs) { memcpy( p, extra_hdrs->s, extra_hdrs->len); p += extra_hdrs->len; } if (str_hdr->len != p-str_hdr->s ) LM_CRIT("BUG in computing extra hdrs: computed len = %d ;" " build len = %d",str_hdr->len,(int)(long)(p-str_hdr->s) ); return 0; error: return -1; } /* cell- pointer to a struct dlg_cell * leg - a dialog leg to be BYE'ed : * = 0: caller leg * > 0: callee legs */ static inline int send_leg_bye(struct dlg_cell *cell, int dst_leg, int src_leg, str *extra_hdrs) { context_p old_ctx; context_p *new_ctx; dlg_t* dialog_info; str met = {"BYE", 3}; int result; if ((dialog_info = build_dlg_t(cell, dst_leg, src_leg)) == 0){ LM_ERR("failed to create dlg_t\n"); goto err; } LM_DBG("sending BYE to %s (%d)\n", (dst_leg==DLG_CALLER_LEG)?"caller":"callee", dst_leg); /* set new processing context */ if (push_new_processing_context( cell, &old_ctx, &new_ctx, NULL)!=0) goto err; ctx_lastdstleg_set(dst_leg); ref_dlg(cell, 1); result = d_tmb.t_request_within (&met, /* method*/ extra_hdrs, /* extra headers*/ NULL, /* body*/ dialog_info, /* dialog structure*/ bye_reply_cb, /* callback function*/ (void*)cell, /* callback parameter*/ NULL); /* release function*/ /* reset the processing contect */ if (current_processing_ctx == NULL) *new_ctx = NULL; else context_destroy(CONTEXT_GLOBAL, *new_ctx); current_processing_ctx = old_ctx; if(result < 0){ LM_ERR("failed to send the BYE request\n"); goto err1; } free_tm_dlg(dialog_info); LM_DBG("BYE sent to %s\n", (dst_leg==DLG_CALLER_LEG)?"caller":"callee"); return 0; err1: unref_dlg(cell, 1); err: return -1; } /* sends BYE in both directions * returns 0 if both BYEs were successful */ int dlg_end_dlg(struct dlg_cell *dlg, str *extra_hdrs) { str str_hdr = {NULL,0}; struct cell* t; int i,res = 0; int callee; /* lookup_dlg has incremented the reference count !! */ if (dlg->state == DLG_STATE_UNCONFIRMED || dlg->state == DLG_STATE_EARLY) { /* locate initial transaction */ LM_DBG("trying to find transaction with hash_index = %u and label = %u\n", dlg->initial_t_hash_index,dlg->initial_t_label); if (d_tmb.t_lookup_ident(&t,dlg->initial_t_hash_index,dlg->initial_t_label) < 0) { LM_ERR("Initial transaction does not exist any more\n"); return -1; } if (d_tmb.t_cancel_trans(t,NULL) < 0) { LM_ERR("Failed to send cancels\n"); d_tmb.unref_cell(t); return -1; } /* lookup_ident refs the transaction */ d_tmb.unref_cell(t); return 0; } if ((build_extra_hdr(dlg, extra_hdrs, &str_hdr)) != 0){ LM_ERR("failed to create extra headers\n"); return -1; } callee = callee_idx(dlg); if ( send_leg_bye( dlg, DLG_CALLER_LEG, callee, &str_hdr)!=0) { res--; } if (send_leg_bye( dlg, callee, DLG_CALLER_LEG, &str_hdr)!=0 ) { res--; } for( i=res ; i<0 ; i++) dual_bye_event( dlg, NULL, 0); pkg_free(str_hdr.s); return res; } /*parameters from MI: h_entry, h_id of the requested dialog*/ struct mi_root * mi_terminate_dlg(struct mi_root *cmd_tree, void *param ){ struct mi_node* node; unsigned int h_entry, h_id; unsigned long long d_id; struct dlg_cell * dlg = NULL; str *mi_extra_hdrs = NULL; int status, msg_len; char *msg; char *end; char bkp; if( d_table ==NULL) goto end; node = cmd_tree->node.kids; h_entry = h_id = 0; if (node==NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* parse first param as long long (hash or dialog_id ) */ if ( node->value.s==NULL || node->value.len==0 ) goto error; /* make value null terminated (in an ugly way) */ bkp = node->value.s[node->value.len]; node->value.s[node->value.len] = 0; /* conver to long long */ d_id = strtoll( node->value.s, &end, 10); node->value.s[node->value.len] = bkp; if (end-node->value.s!=node->value.len) goto error; /* not a number*/ /* check the second parameter - NULL , number or string ?? */ node = node->next; if (node==NULL) { /* as we have only one param, we suppose it is a dialog id, no hdrs */ h_entry = (unsigned int)(d_id>>(8*sizeof(int))); h_id = (unsigned int)(d_id & (((unsigned long long)1<<(8*sizeof(int)))-1) ); } else { if (node->value.s==NULL || node->value.len==0) goto error; if (strno2int(&node->value,&h_id)<0) { /* second param is string -> hdrs */ h_entry = (unsigned int)(d_id>>(8*sizeof(int))); h_id = (unsigned int)(d_id & (((unsigned long long)1<<(8*sizeof(int)))-1) ); mi_extra_hdrs = &node->value; } else { /* second param is number -> h_id */ h_entry = (unsigned int)d_id; /* any third one (hdrs) ? */ if (node->next) { node = node->next; if (node->value.len && node->value.s) mi_extra_hdrs = &node->value; } } } LM_DBG("h_entry %u h_id %u\n", h_entry, h_id); dlg = lookup_dlg(h_entry, h_id); /* lookup_dlg has incremented the reference count !! */ if(dlg){ init_dlg_term_reason(dlg,"MI Termination",sizeof("MI Termination")-1); if ( dlg_end_dlg( dlg, mi_extra_hdrs) ) { status = 500; msg = MI_DLG_OPERATION_ERR; msg_len = MI_DLG_OPERATION_ERR_LEN; } else { status = 200; msg = MI_OK_S; msg_len = MI_OK_LEN; } unref_dlg(dlg, 1); return init_mi_tree(status, msg, msg_len); } end: return init_mi_tree(404, MI_DIALOG_NOT_FOUND, MI_DIALOG_NOT_FOUND_LEN); error: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } int send_leg_msg(struct dlg_cell *dlg,str *method,int src_leg,int dst_leg, str *hdrs,str *body,dlg_request_callback func, void *param,dlg_release_func release,char *reply_marker) { context_p old_ctx; context_p *new_ctx; dlg_t* dialog_info; int result; unsigned int method_type; if (parse_method(method->s,method->s+method->len,&method_type) == 0) { LM_ERR("Failed to parse method - [%.*s]\n",method->len,method->s); return -1; } if (method_type == METHOD_INVITE && (body == NULL || body->s == NULL || body->len == 0)) { LM_ERR("Cannot send INVITE without SDP body\n"); return -1; } if ((dialog_info = build_dialog_info(dlg, dst_leg, src_leg,reply_marker)) == 0) { LM_ERR("failed to create dlg_t\n"); return -1; } LM_DBG("sending [%.*s] to %s (%d)\n",method->len,method->s, (dst_leg==DLG_CALLER_LEG)?"caller":"callee", dst_leg); /* set new processing context */ if (push_new_processing_context( dlg, &old_ctx, &new_ctx, NULL)!=0) return -1; //dialog_info->T_flags=T_NO_AUTOACK_FLAG; result = d_tmb.t_request_within (method, /* method*/ hdrs, /* extra headers*/ body, /* body*/ dialog_info, /* dialog structure*/ func, /* callback function*/ param, /* callback parameter*/ release); /* release function*/ /* reset the processing contect */ if (current_processing_ctx == NULL) *new_ctx = NULL; else context_destroy(CONTEXT_GLOBAL, *new_ctx); current_processing_ctx = old_ctx; if(result < 0) { LM_ERR("failed to send the in-dialog request\n"); free_tm_dlg(dialog_info); return -1; } free_tm_dlg(dialog_info); return 0; } opensips-2.2.2/modules/dialog/dlg_req_within.h000066400000000000000000000066411300170765700214510ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-07-10 initial version (ancuta) */ #ifndef DLG_REQUEST_WITHIN_H #define DLG_REQUEST_WITHIN_H #include "../../statistics.h" #include "../../str.h" #include "../../context.h" #include "../../mi/mi.h" #include "../tm/tm_load.h" #include "dlg_hash.h" #define MAX_FWD "70" #define MAX_SIZE 256 #define RCV_BYE_REPLY 1 #define MI_DIALOG_NOT_FOUND "Requested Dialog not found" #define MI_DIALOG_NOT_FOUND_LEN (sizeof(MI_DIALOG_NOT_FOUND)-1) #define MI_DLG_OPERATION_ERR "Operation failed" #define MI_DLG_OPERATION_ERR_LEN (sizeof(MI_DLG_OPERATION_ERR)-1) extern struct tm_binds d_tmb; extern int dlg_enable_stats; extern stat_var * active_dlgs; typedef void (dlg_request_callback)(struct cell *t,int type, struct tmcb_params* ps); typedef void (dlg_release_func)(void *param); static inline int push_new_processing_context( struct dlg_cell *dlg, context_p *old_ctx, context_p **new_ctx, struct sip_msg **fake_msg) { static context_p my_ctx = NULL; static struct sip_msg *my_msg = NULL; *old_ctx = current_processing_ctx; if (my_ctx==NULL) { my_ctx = context_alloc(CONTEXT_GLOBAL); if (my_ctx==NULL) { LM_ERR("failed to alloc new ctx in pkg\n"); return -1; } } if (current_processing_ctx==my_ctx) { LM_CRIT("BUG - nested setting of my_ctx\n"); return -1; } if (fake_msg) { if (my_msg==NULL) { my_msg = (struct sip_msg*)pkg_malloc(sizeof(struct sip_msg)); if (my_msg==NULL) { LM_ERR("No more pkg memory for a a fake msg\n"); return -1; } } else { free_sip_msg(my_msg); } memset(my_msg, 0, sizeof(struct sip_msg)); my_msg->first_line.type = SIP_REQUEST; my_msg->first_line.u.request.method.s= "DUMMY"; my_msg->first_line.u.request.method.len= 5; my_msg->first_line.u.request.uri.s= "sip:user@domain.com"; my_msg->first_line.u.request.uri.len= 19; my_msg->rcv.src_ip.af = AF_INET; my_msg->rcv.dst_ip.af = AF_INET; *fake_msg = my_msg; } /* reset the new to-be-used CTX */ memset( my_ctx, 0, context_size(CONTEXT_GLOBAL) ); /* set the new CTX as current one */ current_processing_ctx = my_ctx; /* store the value from the newly created context */ *new_ctx = &my_ctx; /* set this dialog in the ctx */ ctx_dialog_set(dlg); /* ref it, and it will be unreffed in context destroy */ ref_dlg(dlg, 1); return 0; } int dlg_end_dlg(struct dlg_cell *dlg, str *extra_hdrs); struct mi_root * mi_terminate_dlg(struct mi_root *cmd_tree, void *param ); int send_leg_msg(struct dlg_cell *dlg,str *method,int src_leg,int dst_leg, str *hdrs,str *body,dlg_request_callback func,void *param, dlg_release_func release,char *reply_marker); #endif opensips-2.2.2/modules/dialog/dlg_timer.c000066400000000000000000000577651300170765700204300ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-14 initial version (bogdan) * 2007-03-06 to avoid races, tests on timer links are done under locks * (bogdan) */ #include "../../mem/shm_mem.h" #include "../../timer.h" #include "dlg_timer.h" #include "dlg_hash.h" #include "dlg_req_within.h" struct dlg_timer *d_timer = 0; dlg_timer_handler timer_hdl = 0; struct dlg_ping_timer *ping_timer=0; struct dlg_reinvite_ping_timer *reinvite_ping_timer=0; str options_str=str_init("OPTIONS"); str invite_str=str_init("INVITE"); /* for the dlg timer, there are 3 possible states : * prev=next=0 -> dialog not in timer list * prev=0 -> dialog expired * otherwise - dialog still in timer list */ #define FAKE_DIALOG_TL ((struct dlg_tl*)-1) int init_dlg_timer( dlg_timer_handler hdl ) { d_timer = (struct dlg_timer*)shm_malloc(sizeof(struct dlg_timer)); if (d_timer==0) { LM_ERR("no more shm mem\n"); return -1; } memset( d_timer, 0, sizeof(struct dlg_timer) ); d_timer->first.next = d_timer->first.prev = &(d_timer->first); d_timer->lock = lock_alloc(); if (d_timer->lock==0) { LM_ERR("failed to alloc lock\n"); goto error0; } if (lock_init(d_timer->lock)==0) { LM_ERR("failed to init lock\n"); goto error1; } timer_hdl = hdl; return 0; error1: lock_dealloc(d_timer->lock); error0: shm_free(d_timer); d_timer = 0; return -1; } #ifdef EXTRA_DEBUG #define tl_get_dlg(_tl_) ((struct dlg_cell*)((char *)(_tl_)- \ (unsigned long)(&((struct dlg_cell*)0)->tl))) void debug_detached_timer_list(struct dlg_tl *detached) { struct dlg_cell *dlg; LM_DBG("Debugging detached timer list\n"); /* check the detached list is not circular */ while (detached != FAKE_DIALOG_TL) { if (detached->prev != NULL || detached->visited==1) { dlg = tl_get_dlg(detached); LM_ERR("Detected something wrong with dialog %p [%.*s]. Aborting. Visited = %d \n", dlg,dlg->callid.len,dlg->callid.s,detached->visited); abort(); } detached->visited = 1; detached = detached->next; } } /* assumed to be always called under timer lock */ void debug_main_timer_list(void) { struct dlg_tl *start,*finish; int visited=1; start = finish = &(d_timer->first); LM_DBG("testing forward loop with visited = %d\n",visited); /* check the main list is circular in both directions from start to end, * with no loops in the middle */ while (start) { start->visited=visited; start = start->next; if (start == finish) break; if (start == NULL || start->visited == visited) { LM_ERR("Detected something wrong with main timer list on forward linking for entry %p \n",start); abort(); } } visited++; start = &(d_timer->first); LM_DBG("testing backward loop with visited = %d\n",visited); while (start) { start->visited=visited; start = start->prev; if (start == finish) break; if (start == NULL || start->visited == visited) { LM_ERR("Detected something wrong with main timer list on backward linking for entry %p \n",start); abort(); } } } #endif int init_dlg_ping_timer(void) { ping_timer = (struct dlg_ping_timer*)shm_malloc(sizeof(struct dlg_timer)); if (ping_timer==0) { LM_ERR("no more shm mem\n"); return -1; } memset(ping_timer,0,sizeof(struct dlg_ping_timer)); ping_timer->lock = lock_alloc(); if (ping_timer->lock == 0) { LM_ERR("failed to alloc lock\n"); goto error0; } if (lock_init(ping_timer->lock) == 0) { LM_ERR("failed to init lock\n"); goto error1; } return 0; error1: lock_dealloc(ping_timer->lock); error0: shm_free(ping_timer); ping_timer=0; return -1; } int init_dlg_reinvite_ping_timer(void) { reinvite_ping_timer = (struct dlg_reinvite_ping_timer*)shm_malloc(sizeof(struct dlg_timer)); if (reinvite_ping_timer==0) { LM_ERR("no more shm mem\n"); return -1; } memset(reinvite_ping_timer,0,sizeof(struct dlg_reinvite_ping_timer)); reinvite_ping_timer->lock = lock_alloc(); if (reinvite_ping_timer->lock == 0) { LM_ERR("failed to alloc lock\n"); goto error0; } if (lock_init(reinvite_ping_timer->lock) == 0) { LM_ERR("failed to init lock\n"); goto error1; } return 0; error1: lock_dealloc(reinvite_ping_timer->lock); error0: shm_free(reinvite_ping_timer); reinvite_ping_timer=0; return -1; } void destroy_ping_timer(void) { if (ping_timer ==0) return; lock_destroy(ping_timer->lock); lock_dealloc(ping_timer->lock); shm_free(ping_timer); ping_timer=0; } void destroy_dlg_timer(void) { if (d_timer==0) return; lock_destroy(d_timer->lock); lock_dealloc(d_timer->lock); shm_free(d_timer); d_timer = 0; } static inline void insert_dlg_timer_unsafe(struct dlg_tl *tl) { struct dlg_tl* ptr; #ifdef EXTRA_DEBUG debug_main_timer_list(); #endif for(ptr = d_timer->first.prev; ptr != &d_timer->first ; ptr = ptr->prev) { if ( ptr->timeout <= tl->timeout ) break; } LM_DBG("inserting %p for %d\n", tl,tl->timeout); tl->prev = ptr; tl->next = ptr->next; tl->prev->next = tl; tl->next->prev = tl; #ifdef EXTRA_DEBUG debug_main_timer_list(); #endif } int insert_dlg_timer(struct dlg_tl *tl, int interval) { lock_get( d_timer->lock); if (tl->next!=0 || tl->prev!=0) { lock_release( d_timer->lock); LM_CRIT("Trying to insert a bogus dlg tl=%p tl->next=%p tl->prev=%p\n", tl, tl->next, tl->prev); return -1; } tl->timeout = get_ticks()+interval; insert_dlg_timer_unsafe( tl ); lock_release( d_timer->lock); return 0; } int insert_ping_timer(struct dlg_cell* dlg) { struct dlg_ping_list *node; node = shm_malloc(sizeof(struct dlg_ping_list)); if (node == 0) { LM_ERR("no more shm mem\n"); return -1; } node->dlg = dlg; node->next = 0; node->prev = 0; lock_get( ping_timer->lock ); dlg->pl = node; if (ping_timer->first == 0) ping_timer->first = node; else { node->next = ping_timer->first; ping_timer->first->prev = node; ping_timer->first = node; } dlg->legs[DLG_CALLER_LEG].reply_received = 1; dlg->legs[callee_idx(dlg)].reply_received = 1; lock_release( ping_timer->lock); LM_DBG("Inserted dlg [%p] in ping timer list\n",dlg); return 0; } int insert_reinvite_ping_timer(struct dlg_cell* dlg) { struct dlg_ping_list *node; node = shm_malloc(sizeof(struct dlg_ping_list)); if (node == 0) { LM_ERR("no more shm mem\n"); return -1; } node->dlg = dlg; node->next = 0; node->prev = 0; lock_get( reinvite_ping_timer->lock ); dlg->reinvite_pl = node; if (reinvite_ping_timer->first == 0) reinvite_ping_timer->first = node; else { node->next = reinvite_ping_timer->first; reinvite_ping_timer->first->prev = node; reinvite_ping_timer->first = node; } dlg->legs[DLG_CALLER_LEG].reinvite_confirmed = 1; dlg->legs[callee_idx(dlg)].reinvite_confirmed = 1; lock_release( reinvite_ping_timer->lock); LM_DBG("Inserted dlg [%p] in reinvite ping timer list\n",dlg); return 0; } static inline void remove_dlg_timer_unsafe(struct dlg_tl *tl) { #ifdef EXTRA_DEBUG debug_main_timer_list(); #endif tl->prev->next = tl->next; tl->next->prev = tl->prev; #ifdef EXTRA_DEBUG debug_main_timer_list(); #endif } /* returns: 0 - dialog OK and removed from timer list 1 - dialog OK but not found in timer list -1 - dialog not OK */ int remove_dlg_timer(struct dlg_tl *tl) { lock_get( d_timer->lock); if (tl->prev==NULL && tl->timeout==0) { /* dialog is not in timer list; either it is completly removed (prev=next=timeout=0), either is in process by timeout routine (prev=timeout=0;next!=0) */ lock_release( d_timer->lock); return 1; } if (tl->prev==NULL || tl->next==NULL || tl->next == FAKE_DIALOG_TL) { LM_CRIT("bogus tl=%p tl->prev=%p tl->next=%p\n", tl, tl->prev, tl->next); lock_release( d_timer->lock); return -1; } remove_dlg_timer_unsafe(tl); /* mark that this dialog was one a part of the timer list */ tl->next = FAKE_DIALOG_TL; tl->prev = NULL; tl->timeout = 0; lock_release( d_timer->lock); return 0; } static inline void detach_ping_node_unsafe(struct dlg_ping_list *it,int reinvite) { if (it->next && it->prev) { it->prev->next = it->next; it->next->prev = it->prev; } else if (it->next) { it->next->prev = 0; if (reinvite) reinvite_ping_timer->first = it->next; else ping_timer->first = it->next; } else if (it->prev) { it->prev->next = 0; } else { if (reinvite) reinvite_ping_timer->first = 0; else ping_timer->first = 0; } it->next = it->prev = 0; } /* returns : 0 - dialog was inserted in timer list with the new timeout -1 - failure (dialog is expired, so it cannot be added again) */ int update_dlg_timer( struct dlg_tl *tl, int timeout ) { int ret; lock_get( d_timer->lock); if ( tl->next == FAKE_DIALOG_TL ) { /* previously removed from timer list - we will not add it again */ lock_release( d_timer->lock); return 0; } if ( tl->next ) { if (tl->prev==0) { lock_release( d_timer->lock); return -1; } remove_dlg_timer_unsafe(tl); ret = 0; } else { ret = 1; } tl->timeout = get_ticks()+timeout; insert_dlg_timer_unsafe( tl ); lock_release( d_timer->lock); return ret; } static inline struct dlg_tl* get_expired_dlgs(unsigned int time) { struct dlg_tl *tl , *end, *ret; lock_get( d_timer->lock); if (d_timer->first.next==&(d_timer->first) || d_timer->first.next->timeout > time ) { lock_release( d_timer->lock); return FAKE_DIALOG_TL; } #ifdef EXTRA_DEBUG debug_main_timer_list(); #endif end = &d_timer->first; tl = d_timer->first.next; LM_DBG("start with tl=%p tl->prev=%p tl->next=%p (%d) at %d " "and end with end=%p end->prev=%p end->next=%p\n", tl,tl->prev,tl->next,tl->timeout,time, end,end->prev,end->next); while( tl!=end && tl->timeout <= time) { LM_DBG("getting tl=%p tl->prev=%p tl->next=%p with %d\n", tl,tl->prev,tl->next,tl->timeout); tl->prev = 0; tl->timeout = 0; tl=tl->next; } LM_DBG("end with tl=%p tl->prev=%p tl->next=%p and " "d_timer->first.next->prev=%p\n", tl,tl->prev,tl->next,d_timer->first.next->prev); if (tl==end && d_timer->first.next->prev) { LM_DBG("no dialog to return\n"); ret = FAKE_DIALOG_TL; } else { ret = d_timer->first.next; tl->prev->next = FAKE_DIALOG_TL; d_timer->first.next = tl; tl->prev = &d_timer->first; } #ifdef EXTRA_DEBUG debug_main_timer_list(); #endif lock_release( d_timer->lock); #ifdef EXTRA_DEBUG debug_detached_timer_list(ret); #endif return ret; } void dlg_timer_routine(unsigned int ticks , void * attr) { struct dlg_tl *tl, *ctl; tl = get_expired_dlgs( ticks ); while (tl != FAKE_DIALOG_TL) { ctl = tl; tl = tl->next; /* keep dialog as expired (next is still set) */ ctl->next = FAKE_DIALOG_TL; LM_DBG("tl=%p next=%p\n", ctl, tl); timer_hdl( ctl ); } } /* removes expired dlgs from main ping_timer list * and links them back into a new list */ void get_timeout_dlgs(struct dlg_ping_list **expired, struct dlg_ping_list **to_be_deleted,int reinvite) { struct dlg_ping_list *exp = NULL,*del=NULL,*it=NULL,*next=NULL; struct dlg_cell *current; int detached; if (reinvite) lock_get(reinvite_ping_timer->lock); else lock_get(ping_timer->lock); for (it=reinvite?reinvite_ping_timer->first:ping_timer->first;it;it=next) { current = it->dlg; next = it->next; detached = 0; if (current->state == DLG_STATE_DELETED) { /* the dialog has terminated - we remove it as well * since we also have a ref */ detach_ping_node_unsafe(it,reinvite); if (reinvite) it->dlg->reinvite_pl = 0; else it->dlg->pl = 0; if (del == NULL) del = it; else { it->next = del; del = it; } continue; } if (reinvite?(current->flags & DLG_FLAG_REINVITE_PING_CALLER): (current->flags & DLG_FLAG_PING_CALLER)) { if (reinvite?(current->legs[DLG_CALLER_LEG].reinvite_confirmed == 0): (current->legs[DLG_CALLER_LEG].reply_received == 0)) { detach_ping_node_unsafe(it,reinvite); detached=1; if (reinvite) it->dlg->reinvite_pl = 0; else it->dlg->pl = 0; if (exp == NULL) exp = it; else { it->next = exp; exp = it; } } } if (detached == 0) { if (reinvite?(current->flags & DLG_FLAG_REINVITE_PING_CALLEE): (current->flags & DLG_FLAG_PING_CALLEE)) { if (reinvite?(current->legs[callee_idx(current)].reinvite_confirmed == 0): current->legs[callee_idx(current)].reply_received == 0) { detach_ping_node_unsafe(it,reinvite); if (reinvite) it->dlg->reinvite_pl = 0; else it->dlg->pl = 0; if (exp == NULL) exp = it; else { it->next = exp; exp = it; } } } } } if (reinvite) lock_release(reinvite_ping_timer->lock); else lock_release(ping_timer->lock); *to_be_deleted = del; *expired = exp; } void reply_from_caller(struct cell* t, int type, struct tmcb_params* ps) { struct sip_msg *rpl; int statuscode; struct dlg_cell *dlg; if(ps == NULL || ps->rpl == NULL) { LM_ERR("Wrong tmcb params\n"); return; } if( ps->param== NULL ) { LM_ERR("Null callback parameter\n"); return; } rpl = ps->rpl; statuscode = ps->code; dlg = *(ps->param); LM_DBG("Status Code received = [%d]\n", statuscode); if (rpl == FAKED_REPLY || statuscode == 408) { /* timeout occurred, nothing else to do now * next time timer fires, it will detect ping reply was not received */ LM_INFO("terminating dialog ( due to timeout ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } if (statuscode == 481) { /* call/transaction does not exist * terminate the dialog */ LM_INFO("terminating dialog ( due to 481 ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } dlg->legs[DLG_CALLER_LEG].reply_received = 1; } void reinvite_reply_from_caller(struct cell* t, int type, struct tmcb_params* ps) { struct sip_msg *rpl; int statuscode; struct dlg_cell *dlg; if(ps == NULL || ps->rpl == NULL) { LM_ERR("Wrong tmcb params\n"); return; } if( ps->param== NULL ) { LM_ERR("Null callback parameter\n"); return; } rpl = ps->rpl; statuscode = ps->code; dlg = *(ps->param); LM_DBG("Status Code received = [%d]\n", statuscode); if (rpl == FAKED_REPLY || statuscode == 408) { /* timeout occured, nothing else to do now * next time timer fires, it will detect ping reply was not received */ LM_INFO("terminating dialog ( due to timeout ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } if (statuscode == 481) { /* call/transaction does not exist * terminate the dialog */ LM_INFO("terminating dialog ( due to 481 ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } dlg->legs[DLG_CALLER_LEG].reinvite_confirmed = 1; } /* Duplicate code for the sake of quickly knowing where the reply came from, * without any further checks */ void reply_from_callee(struct cell* t, int type, struct tmcb_params* ps) { struct sip_msg *rpl; int statuscode; struct dlg_cell *dlg; if(ps == NULL || ps->rpl == NULL) { LM_ERR("Wrong tmcb params\n"); return; } if( ps->param== NULL ) { LM_ERR("Null callback parameter\n"); return; } rpl = ps->rpl; statuscode = ps->code; dlg = *(ps->param); LM_DBG("Status Code received = [%d]\n", statuscode); if (rpl == FAKED_REPLY || statuscode == 408) { /* timeout occurred, nothing else to do now * next time timer fires, it will detect ping reply was not received */ LM_INFO("terminating dialog ( due to timeout ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } if (statuscode == 481) { /* call/transaction does not exist * terminate the dialog */ LM_INFO("terminating dialog ( due to 481 ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } dlg->legs[callee_idx(dlg)].reply_received = 1; } /* Duplicate code for the sake of quickly knowing where the reply came from, * without any further checks */ void reinvite_reply_from_callee(struct cell* t, int type, struct tmcb_params* ps) { struct sip_msg *rpl; int statuscode; struct dlg_cell *dlg; if(ps == NULL || ps->rpl == NULL) { LM_ERR("Wrong tmcb params\n"); return; } if( ps->param== NULL ) { LM_ERR("Null callback parameter\n"); return; } rpl = ps->rpl; statuscode = ps->code; dlg = *(ps->param); LM_DBG("Status Code received = [%d]\n", statuscode); if (rpl == FAKED_REPLY || statuscode == 408) { /* timeout occured, nothing else to do now * next time timer fires, it will detect ping reply was not received */ LM_INFO("terminating dialog ( due to timeout ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } if (statuscode == 481) { /* call/transaction does not exist * terminate the dialog */ LM_INFO("terminating dialog ( due to 481 ) " "with callid = [%.*s] \n",dlg->callid.len,dlg->callid.s); return; } dlg->legs[callee_idx(dlg)].reinvite_confirmed = 1; } void unref_dlg_cb(void *dlg) { unref_dlg_destroy_safe((struct dlg_cell*)dlg,1); } void dlg_options_routine(unsigned int ticks , void * attr) { struct dlg_ping_list *expired,*to_be_deleted,*it,*curr; struct dlg_cell *dlg; get_timeout_dlgs(&expired,&to_be_deleted,0); it = expired; while (it) { dlg = it->dlg; LM_DBG("dialog %p-%.*s has expired\n",dlg,dlg->callid.len,dlg->callid.s); curr = it->next; shm_free(it); it = curr; init_dlg_term_reason(dlg,"Ping Timeout",sizeof("Ping Timeout")-1); /* FIXME - maybe better not to send BYE both ways as we know for * sure one end in down . */ dlg_end_dlg(dlg,0); /* no longer reffed in list */ unref_dlg(dlg,1); } it = to_be_deleted; while (it) { dlg = it->dlg; LM_DBG("dialog %p-%.*s has terminated\n",dlg,dlg->callid.len,dlg->callid.s); curr = it->next; /* if marked as to be deleted, we let it go * for the ping timer list as well */ unref_dlg(dlg,1); shm_free(it); it = curr; } tcp_no_new_conn = 1; /* ping_timer->first now contains all active dialogs */ it = ping_timer->first; while (it) { dlg = it->dlg; /* do not ping ended dialogs - we might have missed them earlier or * might have terminated in the mean time - we'll clean them up on * our next iteration */ if (dlg->state != DLG_STATE_DELETED) { if (dlg->flags & DLG_FLAG_PING_CALLER) { ref_dlg(dlg,1); if (send_leg_msg(dlg,&options_str,callee_idx(dlg), DLG_CALLER_LEG,0,0,reply_from_caller,dlg,unref_dlg_cb, &dlg->legs[DLG_CALLER_LEG].reply_received) < 0) { LM_ERR("failed to ping caller\n"); unref_dlg(dlg,1); } } if (dlg->flags & DLG_FLAG_PING_CALLEE) { ref_dlg(dlg,1); if (send_leg_msg(dlg,&options_str,DLG_CALLER_LEG, callee_idx(dlg),0,0,reply_from_callee,dlg,unref_dlg_cb, &dlg->legs[callee_idx(dlg)].reply_received) < 0) { LM_ERR("failed to ping callee\n"); unref_dlg(dlg,1); } } } it = it->next; } tcp_no_new_conn = 0; } #define CONTACT_STR_START "Contact: <" #define CONTACT_STR_START_LEN (sizeof(CONTACT_STR_START)-1) #define HEADERS_STR_END_NOCRLF "Content-Type: application/sdp\r\n" #define HEADERS_STR_END_NOCRLF_LEN (sizeof(HEADERS_STR_END_NOCRLF)-1) #define HEADERS_STR_END ">\r\nContent-Type: application/sdp\r\n" #define HEADERS_STR_END_LEN (sizeof(HEADERS_STR_END)-1) void dlg_reinvite_routine(unsigned int ticks , void * attr) { struct dlg_ping_list *expired,*to_be_deleted,*it,*curr; struct dlg_cell *dlg; str extra_headers; char *p; get_timeout_dlgs(&expired,&to_be_deleted,1); it = expired; while (it) { dlg = it->dlg; LM_DBG("dialog %p-%.*s has expired\n",dlg,dlg->callid.len,dlg->callid.s); curr = it->next; shm_free(it); it = curr; init_dlg_term_reason(dlg,"ReINVITE Ping Timeout",sizeof("ReINVITE Ping Timeout")-1); /* FIXME - maybe better not to send BYE both ways as we know for * sure one end in down . */ dlg_end_dlg(dlg,0); /* no longer reffed in list */ unref_dlg(dlg,1); } it = to_be_deleted; while (it) { dlg = it->dlg; LM_DBG("dialog %p-%.*s has terminated\n",dlg,dlg->callid.len,dlg->callid.s); curr = it->next; /* if marked as to be deleted, we let it go * for the ping timer list as well */ unref_dlg(dlg,1); shm_free(it); it = curr; } tcp_no_new_conn = 1; /* reinvite_ping_timer->first now contains all active dialogs */ it = reinvite_ping_timer->first; while (it) { dlg = it->dlg; /* do not ping ended dialogs - we might have missed them earlier or * might have terminated in the mean time - we'll clean them up on * our next iteration */ if (dlg->state != DLG_STATE_DELETED) { if (dlg->flags & DLG_FLAG_REINVITE_PING_CALLER) { if (dlg->legs[callee_idx(dlg)].th_sent_contact.len) extra_headers.len = dlg->legs[callee_idx(dlg)].th_sent_contact.len + HEADERS_STR_END_NOCRLF_LEN; else extra_headers.len = CONTACT_STR_START_LEN + dlg->legs[callee_idx(dlg)].contact.len + HEADERS_STR_END_LEN; extra_headers.s = pkg_malloc(extra_headers.len); if (!extra_headers.s) { LM_ERR("No more pkg for extra headers \n"); it = it->next; return; } if (dlg->legs[callee_idx(dlg)].th_sent_contact.len) { p = extra_headers.s; memcpy(p,dlg->legs[callee_idx(dlg)].th_sent_contact.s, dlg->legs[callee_idx(dlg)].th_sent_contact.len); p+= dlg->legs[callee_idx(dlg)].th_sent_contact.len; memcpy(p,HEADERS_STR_END_NOCRLF,HEADERS_STR_END_NOCRLF_LEN); } else { p = extra_headers.s; memcpy(p,CONTACT_STR_START,CONTACT_STR_START_LEN); p += CONTACT_STR_START_LEN; memcpy(p,dlg->legs[callee_idx(dlg)].contact.s, dlg->legs[callee_idx(dlg)].contact.len); p += dlg->legs[callee_idx(dlg)].contact.len; memcpy(p,HEADERS_STR_END,HEADERS_STR_END_LEN); } ref_dlg(dlg,1); if (send_leg_msg(dlg,&invite_str,callee_idx(dlg), DLG_CALLER_LEG,&extra_headers,&dlg->legs[callee_idx(dlg)].sdp, reinvite_reply_from_caller,dlg,unref_dlg_cb, &dlg->legs[DLG_CALLER_LEG].reinvite_confirmed) < 0) { LM_ERR("failed to ping caller\n"); unref_dlg(dlg,1); } pkg_free(extra_headers.s); } if (dlg->flags & DLG_FLAG_REINVITE_PING_CALLEE) { if (dlg->legs[DLG_CALLER_LEG].th_sent_contact.len) extra_headers.len = dlg->legs[DLG_CALLER_LEG].th_sent_contact.len + HEADERS_STR_END_NOCRLF_LEN; else extra_headers.len = CONTACT_STR_START_LEN + dlg->legs[DLG_CALLER_LEG].contact.len + HEADERS_STR_END_LEN; extra_headers.s = pkg_malloc(extra_headers.len); if (!extra_headers.s) { LM_ERR("No more pkg for extra headers \n"); it = it->next; return ; } if (dlg->legs[DLG_CALLER_LEG].th_sent_contact.len) { p = extra_headers.s; memcpy(extra_headers.s, dlg->legs[DLG_CALLER_LEG].th_sent_contact.s, dlg->legs[DLG_CALLER_LEG].th_sent_contact.len); p+= dlg->legs[DLG_CALLER_LEG].th_sent_contact.len; memcpy(p,HEADERS_STR_END_NOCRLF,HEADERS_STR_END_NOCRLF_LEN); } else { p = extra_headers.s; memcpy(p,CONTACT_STR_START,CONTACT_STR_START_LEN); p += CONTACT_STR_START_LEN; memcpy(p,dlg->legs[DLG_CALLER_LEG].contact.s, dlg->legs[DLG_CALLER_LEG].contact.len); p += dlg->legs[DLG_CALLER_LEG].contact.len; memcpy(p,HEADERS_STR_END,HEADERS_STR_END_LEN); } ref_dlg(dlg,1); if (send_leg_msg(dlg,&invite_str,DLG_CALLER_LEG, callee_idx(dlg),&extra_headers,&dlg->legs[DLG_CALLER_LEG].sdp,reinvite_reply_from_callee, dlg,unref_dlg_cb,&dlg->legs[callee_idx(dlg)].reinvite_confirmed) < 0) { LM_ERR("failed to ping callee\n"); unref_dlg(dlg,1); } pkg_free(extra_headers.s); } } it = it->next; } tcp_no_new_conn = 0; } opensips-2.2.2/modules/dialog/dlg_timer.h000066400000000000000000000041661300170765700204200ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-14 initial version (bogdan) */ #ifndef _DIALOG_DLG_TIMER_H_ #define _DIALOG_DLG_TIMER_H_ #include "../../locking.h" struct dlg_tl { struct dlg_tl *next; struct dlg_tl *prev; #ifdef EXTRA_DEBUG int visited; #endif volatile unsigned int timeout; }; struct dlg_timer { struct dlg_tl first; gen_lock_t *lock; }; struct dlg_ping_list { struct dlg_cell* dlg; struct dlg_ping_list *next; struct dlg_ping_list *prev; }; struct dlg_ping_timer { struct dlg_ping_list *first; gen_lock_t *lock; }; struct dlg_reinvite_ping_timer { struct dlg_ping_list *first; gen_lock_t *lock; }; typedef void (*dlg_timer_handler)(struct dlg_tl *); int init_dlg_timer( dlg_timer_handler ); int init_dlg_ping_timer(); int init_dlg_reinvite_ping_timer(); void destroy_dlg_timer(); void destroy_ping_timer(); int insert_dlg_timer(struct dlg_tl *tl, int interval); int insert_ping_timer(struct dlg_cell *dlg); int insert_reinvite_ping_timer(struct dlg_cell *dlg); int remove_dlg_timer(struct dlg_tl *tl); int remove_ping_timer(struct dlg_cell *dlg); int update_dlg_timer( struct dlg_tl *tl, int timeout ); void dlg_timer_routine(unsigned int ticks , void * attr); void dlg_options_routine(unsigned int ticks , void * attr); void dlg_reinvite_routine(unsigned int ticks , void * attr); #endif opensips-2.2.2/modules/dialog/dlg_vals.c000066400000000000000000000153721300170765700202410ustar00rootroot00000000000000/* * dialog module - basic support for dialog tracking * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-01-12 initial version (bogdan) */ #include "../../mem/shm_mem.h" #include "dlg_vals.h" #include "dlg_hash.h" static inline unsigned int _get_name_id(str *name) { char *p; unsigned short id; id=0; for( p=name->s+name->len-1 ; p>=name->s ; p-- ) id ^= *p; return id; } static inline struct dlg_val *new_dlg_val(str *name, str *val) { struct dlg_val *dv; LM_DBG("inserting <%.*s>=<%.*s>\n",name->len,name->s,val->len,val->s); dv =(struct dlg_val*)shm_malloc(sizeof(struct dlg_val)+name->len+val->len); if (dv==NULL) { LM_ERR("no more shm mem\n"); return NULL; } dv->id = _get_name_id(name); dv->next = NULL; /* set name */ dv->name.len = name->len; dv->name.s = (char*)(dv + 1); memcpy(dv->name.s, name->s, name->len); /* set value */ dv->val.len = val->len; dv->val.s = ((char*)(dv + 1)) + name->len; memcpy(dv->val.s, val->s, val->len); return dv; } int store_dlg_value_unsafe(struct dlg_cell *dlg, str *name, str *val) { struct dlg_val *dv=NULL; struct dlg_val *it; struct dlg_val *it_prev; unsigned int id; if ( val && (dv=new_dlg_val(name,val))==NULL) { LM_ERR("failed to create new dialog value\n"); return -1; } id = _get_name_id(name); /* iterate the list */ for( it_prev=NULL, it=dlg->vals ; it ; it_prev=it,it=it->next) { if (id==it->id && name->len==it->name.len && memcmp(name->s,it->name.s,name->len)==0 ) { LM_DBG("var found-> <%.*s>!\n",it->val.len,it->val.s); /* found -> replace or delete it */ if (val==NULL) { /* delete it */ if (it_prev) it_prev->next = it->next; else dlg->vals = it->next; } else { /* replace the current it with dv and free the it */ dv->next = it->next; if (it_prev) it_prev->next = dv; else dlg->vals = dv; } dlg->flags |= DLG_FLAG_VP_CHANGED; shm_free(it); return 0; } } /* not found */ if (val==NULL) return 0; /* has value ? -> simply add a new one */ /* insert at the beginning of the list */ dv->next = dlg->vals; dlg->vals = dv; dlg->flags |= DLG_FLAG_VP_CHANGED; return 0; } int store_dlg_value(struct dlg_cell *dlg, str *name, str *val) { int ret; /* lock dialog */ dlg_lock_dlg( dlg ); ret = store_dlg_value_unsafe(dlg,name,val); /* unlock dialog */ dlg_unlock_dlg( dlg ); return ret; } static str val_buf = { NULL, 0}; static int val_buf_size; int fetch_dlg_value(struct dlg_cell *dlg, str *name,str *ival, int val_has_buf) { struct dlg_val *dv; unsigned int id; str *val; LM_DBG("looking for <%.*s>\n",name->len,name->s); id = _get_name_id(name); if (!val_has_buf) { val = &val_buf; val->len = val_buf_size; } else val = ival; /* lock dialog */ dlg_lock_dlg( dlg ); /* iterate the list */ for( dv=dlg->vals ; dv ; dv=dv->next) { if (id==dv->id && name->len==dv->name.len && memcmp(name->s,dv->name.s,name->len)==0 ) { LM_DBG("var found-> <%.*s>!\n",dv->val.len,dv->val.s); /* found -> make a copy of the value under lock */ if (dv->val.len > val->len) { val->s = (char*)pkg_realloc(val->s,dv->val.len); if (val->s==NULL) { if (!val_has_buf) val_buf_size = 0; dlg_unlock_dlg( dlg ); LM_ERR("failed to do realloc for %d\n",dv->val.len); return -1; } if (!val_has_buf) val_buf_size = dv->val.len; } memcpy( val->s, dv->val.s, dv->val.len ); val->len = dv->val.len; *ival = *val; /* unlock dialog */ dlg_unlock_dlg( dlg ); return 0; } } /* unlock dialog */ dlg_unlock_dlg( dlg ); LM_DBG("var NOT found!\n"); return -1; } int check_dlg_value_unsafe(struct dlg_cell *dlg, str *name, str *val) { struct dlg_val *dv; unsigned int id; LM_DBG("looking for <%.*s> with <%.*s>\n", name->len, name->s, val->len, val->s); id = _get_name_id(name); /* iterate the list */ for( dv=dlg->vals ; dv ; dv=dv->next) { if (id==dv->id && name->len==dv->name.len && memcmp(name->s,dv->name.s,name->len)==0 ) { LM_DBG("var found with val <%.*s>!\n",dv->val.len,dv->val.s); if ( val->len==dv->val.len && memcmp(val->s,dv->val.s,val->len)==0) { LM_DBG("var found!\n"); return 0; } break; } } LM_DBG("var NOT found!\n"); return -1; } int pv_parse_name(pv_spec_p sp, str *in) { if(in==NULL || in->s==NULL || sp==NULL) return -1; sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = AVP_NAME_STR; sp->pvp.pvn.u.isname.name.s = *in; return 0; } int pv_get_dlg_val(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct dlg_cell *dlg; if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL ) { LM_CRIT("BUG - bad parameters\n"); return -1; } if ( (dlg=get_current_dialog())==NULL ) return pv_get_null(msg, param, res); if (fetch_dlg_value( dlg, ¶m->pvn.u.isname.name.s, ¶m->pvv, 1)!=0) return pv_get_null(msg, param, res); res->flags = PV_VAL_STR; res->rs = param->pvv; return 0; } int pv_set_dlg_val(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { struct dlg_cell *dlg; if ( (dlg=get_current_dialog())==NULL ) return -1; if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL ) { LM_CRIT("BUG - bad parameters\n"); return -1; } if (val==NULL || val->flags&(PV_VAL_NONE|PV_VAL_NULL|PV_VAL_EMPTY)) { /* if NULL, remove the value */ if (store_dlg_value( dlg, ¶m->pvn.u.isname.name.s, NULL)!=0) { LM_ERR("failed to delete dialog values <%.*s>\n", param->pvn.u.isname.name.s.len,param->pvn.u.isname.name.s.s); return -1; } } else { /* if value, must be string */ if ( !(val->flags&PV_VAL_STR)) { LM_ERR("non-string values are not supported\n"); return -1; } if (store_dlg_value( dlg, ¶m->pvn.u.isname.name.s, &val->rs)!=0) { LM_ERR("failed to store dialog values <%.*s>\n", param->pvn.u.isname.name.s.len,param->pvn.u.isname.name.s.s); return -1; } } return 0; } opensips-2.2.2/modules/dialog/dlg_vals.h000066400000000000000000000034241300170765700202410ustar00rootroot00000000000000/* * dialog module - basic support for dialog tracking * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-01-12 initial version (bogdan) */ #ifndef _DIALOG_DLG_VALS_H #define _DIALOG_DLG_VALS_H #include "../../pvar.h" #include "dlg_hash.h" struct dlg_val { unsigned int id; str name; str val; struct dlg_val *next; }; int pv_parse_name(pv_spec_p sp, str *in); int pv_get_dlg_val(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int pv_set_dlg_val(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val); typedef int (*store_dlg_value_f)(struct dlg_cell *dlg, str *name, str *val); typedef int (*fetch_dlg_value_f)(struct dlg_cell *dlg, str *name, str *val, int val_has_buf); int store_dlg_value(struct dlg_cell *dlg, str *name, str *val); int store_dlg_value_unsafe(struct dlg_cell *dlg, str *name, str *val); int fetch_dlg_value(struct dlg_cell *dlg, str *name, str *val,int val_has_buf); int check_dlg_value_unsafe(struct dlg_cell *dlg, str *name, str *val); #endif opensips-2.2.2/modules/dialog/doc/000077500000000000000000000000001300170765700170375ustar00rootroot00000000000000opensips-2.2.2/modules/dialog/doc/dialog.xml000066400000000000000000000023711300170765700210230ustar00rootroot00000000000000 %docentities; ]> dialog Module &osipsname; Bogdan-Andrei Iancu
bogdan@opensips.org
Bogdan-Andrei Iancu
bogdan@opensips.org
Vladut-Stefan Paiu
vladpaiu@opensips.org
2006-2009 &voicesystem; $Revision: 8771 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/dialog/doc/dialog_admin.xml000066400000000000000000002145351300170765700222020ustar00rootroot00000000000000 &adminguide;
Overview The dialog module provides dialog awareness to the &osips; proxy. Its functionality is to keep trace of the current dialogs, to offer information about them (like how many dialogs are active). Aside tracking, the dialog module offers functionalities like flags and attributes per dialog (persistent data across dialog), dialog profiling and dialog termination (on timeout base or external triggered). The module, via an internal API, also provide the foundation to build on top of it more complex dialog-based functionalities via other &osips; modules.
How it works To create the dialog associated to an initial request, you must call the create_dialog() function, with or without parameter.. The dialog is automatically destroyed when a BYE is received. In case of no BYE, the dialog lifetime is controlled via the default timeout (see default_timeout - ) and custom timeout (see $DLG_timeout - ).
Dialog profiling Dialog profiling is a mechanism that helps in classifying, sorting and keeping trace of certain types of dialogs, using whatever properties of the dialog (like caller, destination, type of calls, etc). Dialogs can be dynamically added in different (and several) profile tables - logically, each profile table can have a special meaning (like dialogs outside the domain, dialogs terminated to PSTN, etc). There are two types of profiles: with no value - a dialog simply belongs to a profile. (like outbound calls profile). There is no other additional information to describe the dialog's belonging to the profile; with value - a dialog belongs to a profile having a certain value (like in caller profile, where the value is the caller ID). The belonging of the dialog to the profile is strictly related to the value. A dialog can be added to multiple profiles in the same time. Profiles are visible (at the moment) in the request route (for initial and sequential requests) and in the branch, failure and reply routes of the original request. Dialog profiles can also be used in distributed systems, using the &osips; CacheDB Interface. This feature allows you to share dialog profile information with multiple &osips; instaces that use the same CacheDB backend. In order to do that, the cachedb_url parameter must be defined. Also, the profile must be marked as shared, by adding the '/s' suffix to the name of the profile in the profiles_with_value or profiles_no_value parameters.
Dialog replication Dialog replication is a mechanism used to mirror all dialog changes taking place in one OpenSIPS instance to one or more other different instances. The logic is simplified by using the core Binary Internal Interface to build and send the replication-related UDP packets. The feature is especially useful when dealing with very large systems, where failover through a database backend is no longer feasible, due to the high amount of time required for the backup instance to load the dialog information stored in a typical dialog table by the crashed instance. Configuring both receival and sending of dialog replication packets is trivial and can be done by using the accept_replicated_dialogs and replicate_dialogs_to parameters of the module. In addition to this, the module also exports several statistics regarding the number of replication packets sent/received. Profiles replication can also be achieved using the accept_replicated_profiles and replicate_profiles_to parameters.
Dependencies
&osips; Modules The following modules must be loaded before this module: TM - Transaction module RR - Record-Route module
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>enable_stats</varname> (integer) If the statistics support should be enabled or not. Via statistic variables, the module provide information about the dialog processing. Set it to zero to disable or to non-zero to enable it. Default value is 1 (enabled). Set <varname>enable_stats</varname> parameter ... modparam("dialog", "enable_stats", 0) ...
<varname>hash_size</varname> (integer) The size of the hash table internally used to keep the dialogs. A larger table is much faster but consumes more memory. The hash size must be a power of 2 number. IMPORTANT: If dialogs' information should be stored in a database, a constant hash_size should be used, otherwise the restored process will not take place. If you really want to modify the hash_size you must delete all table's rows before restarting &osips;. Default value is 4096. Set <varname>hash_size</varname> parameter ... modparam("dialog", "hash_size", 1024) ...
<varname>log_profile_hash_size</varname> (integer) The size of the hash table internally used to store profile->dialog associations. A larger table can provide more parallel operations but consumes more memory. The hash size is provided as the base 2 logarithm(e.g. log_profile_hash_size =4 means the table has 2^4 entries). Default value is 4. Set <varname>hash_size</varname> parameter ... modparam("dialog", "log_profile_hash_size", 5) #set a table size of 32 ...
<varname>rr_param</varname> (string) Name of the Record-Route parameter to be added with the dialog cookie. It is used for fast dialog matching of the sequential requests. Default value is did. Set <varname>rr_param</varname> parameter ... modparam("dialog", "rr_param", "xyz") ...
<varname>default_timeout</varname> (integer) The default dialog timeout (in seconds) if no custom one is set. Default value is 43200 (12 hours). Set <varname>default_timeout</varname> parameter ... modparam("dialog", "default_timeout", 21600) ...
<varname>dlg_extra_hdrs</varname> (string) A string containing the extra headers (full format, with EOH) to be added in the requests generated by the module (like BYEs). Default value is NULL. Set <varname>dlf_extra_hdrs</varname> parameter ... modparam("dialog", "dlg_extra_hdrs", "Hint: credit expired\r\n") ...
<varname>dlg_match_mode</varname> (integer) How the seqential requests should be matched against the known dialogs. The modes are a combination between matching based on a cookie (DID) stored as cookie in Record-Route header and the matching based on SIP elements (as in RFC3261). The supported modes are: 0 - DID_ONLY - the match is done exclusively based on DID; 1 - DID_FALLBACK - the match is first tried based on DID and if not present, it will fallback to SIP matching; 2 - DID_NONE - the match is done exclusively based on SIP elements; no DID information is added in RR. Default value is 0 (DID_ONLY). Set <varname>dlg_match_mode</varname> parameter ... modparam("dialog", "dlg_match_mode", 1) ...
<varname>db_url</varname> (string) If you want to store the information about the dialogs in a database a database url must be specified. Default value is &defaultdb;. Set <varname>db_url</varname> parameter ... modparam("dialog", "db_url", "&exampledb;") ...
<varname>db_mode</varname> (integer) Describe how to push into the DB the dialogs' information from memory. The supported modes are: 0 - NO_DB - the memory content is not flushed into DB; 1 - REALTIME - any dialog information changes will be reflected into the database immediately. 2 - DELAYED - the dialog information changes will be flushed into the DB periodically, based on a timer routine. 3 - SHUTDOWN - the dialog information will be flushed into DB only at shutdown - no runtime updates. Default value is 0. Set <varname>db_mode</varname> parameter ... modparam("dialog", "db_mode", 1) ...
<varname>db_update_period</varname> (integer) The interval (seconds) at which to update dialogs' information if you chose to store the dialogs' info at a given interval. A too short interval will generate intensive database operations, a too large one will not notice short dialogs. Default value is 60. Set <varname>db_update_period</varname> parameter ... modparam("dialog", "db_update_period", 120) ...
<varname>options_ping_interval</varname> (integer) The interval (seconds) at which OpenSIPS will generate in-dialog OPTIONS pings for dialogs. Default value is 30. Set <varname>options_ping_interval</varname> parameter ... modparam("dialog", "options_ping_interval", 20) ...
<varname>reinvite_ping_interval</varname> (integer) The interval (seconds) at which OpenSIPS will generate in-dialog Re-INVITE pings for dialogs. Default value is 300. Set <varname>reinvite_ping_interval</varname> parameter ... modparam("dialog", "reinvite_ping_interval", 600) ...
<varname>table_name</varname> (string) If you want to store the information about the dialogs in a database a table name must be specified. Default value is dialog. Set <varname>table_name</varname> parameter ... modparam("dialog", "table_name", "my_dialog") ...
<varname>call_id_column</varname> (string) The column's name in the database to store the dialogs' callid. Default value is callid. Set <varname>call_id_column</varname> parameter ... modparam("dialog", "call_id_column", "callid_c_name") ...
<varname>from_uri_column</varname> (string) The column's name in the database to store the caller's sip address. Default value is from_uri. Set <varname>from_uri_column</varname> parameter ... modparam("dialog", "from_uri_column", "from_uri_c_name") ...
<varname>from_tag_column</varname> (string) The column's name in the database to store the From tag from the Invite request. Default value is from_tag. Set <varname>from_tag_column</varname> parameter ... modparam("dialog", "from_tag_column", "from_tag_c_name") ...
<varname>to_uri_column</varname> (string) The column's name in the database to store the calee's sip address. Default value is to_uri. Set <varname>to_uri_column</varname> parameter ... modparam("dialog", "to_uri_column", "to_uri_c_name") ...
<varname>to_tag_column</varname> (string) The column's name in the database to store the To tag from the 200 OK response to the Invite request, if present. Default value is to_tag. Set <varname>to_tag_column</varname> parameter ... modparam("dialog", "to_tag_column", "to_tag_c_name") ...
<varname>from_cseq_column</varname> (string) The column's name in the database to store the cseq from caller side. Default value is caller_cseq. Set <varname>from_cseq_column</varname> parameter ... modparam("dialog", "from_cseq_column", "from_cseq_c_name") ...
<varname>to_cseq_column</varname> (string) The column's name in the database to store the cseq from callee side. Default value is callee_cseq. Set <varname>to_cseq_column</varname> parameter ... modparam("dialog", "to_cseq_column", "to_cseq_c_name") ...
<varname>from_route_column</varname> (string) The column's name in the database to store the route records from caller side (proxy to caller). Default value is caller_route_set. Set <varname>from_route_column</varname> parameter ... modparam("dialog", "from_route_column", "from_route_c_name") ...
<varname>to_route_column</varname> (string) The column's name in the database to store the route records from callee side (proxy to callee). Default value is callee_route_set. Set <varname>to_route_column</varname> parameter ... modparam("dialog", "to_route_column", "to_route_c_name") ...
<varname>from_contact_column</varname> (string) The column's name in the database to store the caller's contact uri. Default value is caller_contact. Set <varname>from_contact_column</varname> parameter ... modparam("dialog", "from_contact_column", "from_contact_c_name") ...
<varname>to_contact_column</varname> (string) The column's name in the database to store the callee's contact uri. Default value is callee_contact. Set <varname>to_contact_column</varname> parameter ... modparam("dialog", "to_contact_column", "to_contact_c_name") ...
<varname>from_sock_column</varname> (string) The column's name in the database to store the information about the local interface receiving the traffic from caller. Default value is caller_sock. Set <varname>from_sock_column</varname> parameter ... modparam("dialog", "from_sock_column", "from_sock_c_name") ...
<varname>to_sock_column</varname> (string) The column's name in the database to store information about the local interface receiving the traffic from callee. Default value is callee_sock. Set <varname>to_sock_column</varname> parameter ... modparam("dialog", "to_sock_column", "to_sock_c_name") ...
<varname>dlg_id_column</varname> (string) The column's name in the database to store the dialogs' id information. Default value is hash_id. Set <varname>dlg_id_column</varname> parameter ... modparam("dialog", "dlg_id_column", "dlg_id_c_name") ...
<varname>state_column</varname> (string) The column's name in the database to store the dialogs' state information. Default value is state. Set <varname>state_column</varname> parameter ... modparam("dialog", "state_column", "state_c_name") ...
<varname>start_time_column</varname> (string) The column's name in the database to store the dialogs' start time information. Default value is start_time. Set <varname>start_time_column</varname> parameter ... modparam("dialog", "start_time_column", "start_time_c_name") ...
<varname>timeout_column</varname> (string) The column's name in the database to store the dialogs' timeout. Default value is timeout. Set <varname>timeout_column</varname> parameter ... modparam("dialog", "timeout_column", "timeout_c_name") ...
<varname>profiles_column</varname> (string) The column's name in the database to store the dialogs' profiles. Default value is profiles. Set <varname>profiles_column</varname> parameter ... modparam("dialog", "profiles_column", "profiles_c_name") ...
<varname>vars_column</varname> (string) The column's name in the database to store the dialogs' vars. Default value is vars. Set <varname>vars_column</varname> parameter ... modparam("dialog", "vars_column", "vars_c_name") ...
<varname>sflags_column</varname> (string) The column's name in the database to store the dialogs' script flags. Default value is script_flags. Set <varname>sflags_column</varname> parameter ... modparam("dialog", "sflags_column", "sflags_c_name") ...
<varname>mflags_column</varname> (string) The column's name in the database to store the dialogs' module flags. Default value is module_flags. Set <varname>mflags_column</varname> parameter ... modparam("dialog", "mflags_column", "mflags_c_name") ...
<varname>flags_column</varname> (string) The column's name in the database to store the dialogs' flags. Default value is flags. Set <varname>flags_column</varname> parameter ... modparam("dialog", "flags_column", "flags_c_name") ...
<varname>profiles_with_value</varname> (string) List of names for profiles with values. Flag /b allows replicating dialogs over the bin interface. Before, all of them were replicated. Default value is empty. Set <varname>profiles_with_value</varname> parameter ... modparam("dialog", "profiles_with_value", "caller ; my_profile; share/s; repl/b;") ...
<varname>profiles_no_value</varname> (string) List of names for profiles without values. Flag /b allows replicating dialogs over the bin interface. Before, all of them were replicated. Default value is empty. Set <varname>profiles_no_value</varname> parameter ... modparam("dialog", "profiles_no_value", "inbound ; outbound ; shared/s; repl/b;") ...
<varname>db_flush_vals_profiles</varname> (int) Pushes dialog values, profiles and flags into the database along with other dialog state information (see db_mode 1 and 2). Default value is empty. Set <varname>db_flush_vals_profiles</varname> parameter ... modparam("dialog", "db_flush_vals_profiles", 1) ...
<varname>timer_bulk_del_no</varname> (int) The number of dialogs that should be attempted to be deleted at the same time ( a single query ) from the DB back-end. Default value is 1. Set <varname>timer_bulk_del_no</varname> parameter ... modparam("dialog", "timer_bulk_del_no", 10) ...
<varname>cachedb_url</varname> (string) Enables distributed dialog profiles and specifies the backend that should be used by the CacheDB interface. Default value is empty. Set <varname>cachedb_url</varname> parameter ... modparam("dialog", "cachedb_url", "redis://127.0.0.1:6379") ...
<varname>profile_value_prefix</varname> (string) Specifies what prefix should be added to the profiles with value when they are inserted into CacheDB backed. This is only used when distributed profiles are enabled. Default value is dlg_val_. Set <varname>profile_value_prefix</varname> parameter ... modparam("dialog", "profile_value_prefix", "dlgv_") ...
<varname>profile_no_value_prefix</varname> (string) Specifies what prefix should be added to the profiles without value when they are inserted into CacheDB backed. This is only used when distributed profiles are enabled. Default value is dlg_noval_. Set <varname>profile_no_value_prefix</varname> parameter ... modparam("dialog", "profile_no_value_prefix", "dlgnv_") ...
<varname>profile_size_prefix</varname> (string) Specifies what prefix should be added to the entity that holds the profiles with value size in CacheDB backed. This is only used when distributed profiles are enabled. Default value is dlg_size_. Set <varname>profile_size_prefix</varname> parameter ... modparam("dialog", "profile_size_prefix", "dlgs_") ...
<varname>profile_timeout</varname> (int) Specifies how long a dialog profile should be kept in the CacheDB until it expires. This is only used when distributed profiles are enabled. Default value is 86400. Set <varname>profile_timeout</varname> parameter ... modparam("dialog", "profile_timeout", "43200") ...
<varname>accept_replicated_dialogs</varname> (int) Registers the dialog module to the &osips; Binary Internal Interface. It specifies the instances, that belong to a certain cluster, from which we should expect incoming packets. Default value is 0 (not registered). Set <varname>accept_replicated_dialogs</varname> parameter ... modparam("dialog", "accept_replicated_dialogs", 1) ...
<varname>replicate_dialogs_to</varname> (int) Adds dialog replication destinations,that belong to the specified cluster id. The destination will receive all dialog-related events (creation, updating and deletion) over TCP, using the Binary Internal Interface. Default value is 0 (no replication destinations). Set <varname>replicate_dialogs_to</varname> parameter ... modparam("dialog", "replicate_dialogs_to", 1) ...
<varname>accept_replicated_profiles</varname> (int) Registers the dialog module to the &osips; Binary Internal Interface for profiles replication. It specifies the instances, that belong to a certain cluster, from which we should expect incoming packets. Default value is 0 (not registered). Set <varname>accept_replicated_profiles</varname> parameter ... modparam("dialog", "accept_replicated_profiles", 1) ...
<varname>replicate_profiles_to</varname> (int) Adds profiles replication destinations, that belong to the specified cluster id. The destination will receive all dialog-related events (creation, updating and deletion) over TCP, using the Binary Internal Interface. Default value is 0 (no replication destinations). Set <varname>replicate_profiles_to</varname> parameter ... modparam("dialog", "replicate_profiles_to", 1) ...
<varname>accept_replicated_profile_timeout</varname> (int) The time between two succesive incoming packets. Default value is 10. Set <varname>accept_replicated_profile_timeout</varname> parameter ... modparam("dialog", "accept_replicated_profile_timeout", 30) ...
<varname>auth_check</varname> (int) Authentication check for incoming packets. Default value is 0 (disabled). Set <varname>auth_check</varname> parameter ... modparam("dialog", "auth_check", 1) ...
<varname>replicate_profiles_buffer</varname> (string) Used to specify the length of the buffer used by the binary replication, in bytes. Usually this should be big enough to hold as much data as possible, but small enough to avoid UDP fragmentation. The recommended value is the smallest MTU between all the replication instances. Default value is 1400 bytes. Set <varname>replicate_profiles_buffer</varname> parameter ... modparam("dialog", "replicate_profiles_buffer", 500) ...
<varname>replicate_profiles_check</varname> (string) Timer in seconds, used to specify how often the module should check whether old, replicated profiles values are obsolete and should be removed. should replicate its profiles to the other instances. Default value is 10 s. Set <varname>replicate_profiles_check</varname> parameter ... modparam("dialog", "replicate_profiles_check", 100) ...
<varname>replicate_profiles_timer</varname> (string) Timer in milliseconds, used to specify how often the module should replicate its profiles to the other instances. Default value is 10 ms. Set <varname>replicate_profiles_timer</varname> parameter ... modparam("dialog", "replicate_profiles_timer", 100) ...
<varname>replicate_profiles_expire</varname> (string) Timer in seconds, used to specify when the profiles counters received from a different instance should no longer be taken into account. This is used to prevent obsolete values, in case an instance stops replicating its counters. Default value is 10 s. Set <varname>replicate_profiles_expire</varname> parameter ... modparam("dialog", "replicate_profiles_expire", 10) ...
Exported Functions
<function moreinfo="none">create_dialog()</function> The function creats the dialog for the currently processed request. The request must be an initial request. Optionally,the function also receives a string parameter, which specifies whether the dialog end-points should be pinged via SIP options messages. The parameter can be "P" to specify to only ping the caller, "p" to only ping the callee or "Pp" to ping both dialog sides. If the extra string parameter is provided and one end-point fails to respond to a options ping, OpenSIPS will terminate the dialog from the middle. Also, the end-points can be pinged via in-dialog Re-INVITE SIP messages. This behaviour is controlled via the "R" flags for pinging the caller side, and the "r" flag for re-invite pinging the callee side. If one end-points fails to re-negociate the session via the Re-INVITE pings, OpenSIPS will terminate the dialog from the middle The string parameter can also contain "B" to activate the bye on timeout behavior. The function returns true if the dialog was successfully created or if the dialog was previously created. This function can be used from REQUEST_ROUTE. <function>create_dialog()</function> usage ... create_dialog(); ... #ping caller create_dialog("P"); ... #ping caller and callee create_dialog("Pp"); #bye on timeout create_dialog("B"); ...
<function moreinfo="none">match_dialog()</function> This function is to be used to match a sequential (in-dialog) request to an existing dialog. In other words, the function will try to match the current request to an known ongoing dialog. The function tries to use (for dialog matching) the DID (Dialog ID) from Route header and also falls back to SIP-wise matching. As sequential requests are automatically matched to the dialog when doing "loose_route()" from script, this function is intended to: (A) control the place in your script where the dialog matching is done and (B) to cope with bogus sequential requests that do not have Route headers, so they are not handled by loose_route(). The function returns true if a dialog exists for the request. This function can be used from REQUEST_ROUTE. <function>match_dialog()</function> usage ... if (has_totag()) { loose_route(); if ($DLG_status==NULL && !match_dialog() ) { xlog(" cannot match request to a dialog \n"); } } ...
<function moreinfo="none">validate_dialog()</function> The function checks the current received requests against the dialog (internal data) it belongs to. Performing several tests, the function will help to detect the bogus injected in-dialog requests (like malicious BYEs). The performed tests are related to CSEQ sequence checking and routing information checking (contact and route set). The function returns true if a dialog exists for the request and if the request is valid (according to dialog data). If the request is invalid, the following return codes are returned : -1 - invalid cseq -2 - invalid remote target -3 - invalid route set -4 - other errors ( parsing, no dlg, etc ) This function can be used from REQUEST_ROUTE. <function>validate_dialog()</function> usage ... if (has_totag()) { loose_route(); if ($DLG_status!=NULL && !validate_dialog() ) { xlog(" in-dialog bogus request \n"); } else { xlog(" in-dialog valid request - $DLG_dir !\n"); } } ...
<function moreinfo="none">fix_route_dialog()</function> The function forces an in dialog SIP message to contain the ruri, route headers and dst_uri, as specified by the internal data of the dialog it belongs to. The function will prevent the existence of bogus injected in-dialog requests ( like malicious BYEs ) This function can be used from REQUEST_ROUTE. <function>fix_route_dialog()</function> usage ... if (has_totag()) { loose_route(); if ($DLG_status!=NULL) if (!validate_dialog()) fix_route_dialog(); } ...
<function moreinfo="none">get_dialog_info(attr,var,key,key_val)</function> The function extracts a dialog value from another dialog. It first searches through all existing (ongoing) dialogs for a dialog that has a dialog variable named "key" with the value "key_val" (so a dialog where $dlg_val(key)=="key_val"). If found, it returns the value of the dialog variable "attr" from the found dialog in the "var" pseudo-variable, otherwise nothing is written in "var", and a negative error code is returned. NOTE: the function does not require to be called in the context of a dialog - you can use it whenever / whereever for searching for other dialogs. Meaning of the parameters is as follows: attr - the name of the dialog variable (from the found dialog) to be returned; var - a pvar where to store the value of the "attr" dialog variable key - name of a dialog variable to be used a search key (when looking after the target dialog) key_val - the value of the dialog variable that is used as key in searching the target dialog. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE. <function>get_dialog_info</function> usage ... if ( get_dialog_info("callee","$var(x)","caller","$fu") ) { xlog("caller $fU has another ongoing, talking to callee $var(x)\n") } # create dialog for current call and place the caller and callee attributes create_dialog(); $dlg_val(caller) = $fu; $dlg_val(callee) = $ru; ...
<function moreinfo="none">set_dlg_profile(profile,[value])</function> Inserts the current dialog into a profile. Note that if the profile does not support values, this will be silently discarded. A dialog may be inserted in the same profile multiple times. NOTE: the dialog must be created before using this function (use create_dialog() function before). Meaning of the parameters is as follows: profile - name of the profile to be added to; value (optional) - string value to define the belonging of the dialog to the profile - note that the profile must support values. Pseudo-variables are supported. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>set_dlg_profile</function> usage ... set_dlg_profile("inbound_call"); set_dlg_profile("caller","$fu"); ...
<function moreinfo="none">unset_dlg_profile(profile,[value])</function> Removes the current dialog from a profile. NOTE: the dialog must be created before using this function (use create_dialog() function before). Meaning of the parameters is as follows: profile - name of the profile to be removed from; value (optional) - string value to define the belonging of the dialog to the profile - note that the profile must support values. Pseudo-variables are supported. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>unset_dlg_profile</function> usage ... unset_dlg_profile("inbound_call"); unset_dlg_profile("caller","$fu"); ...
<function moreinfo="none">is_in_profile(profile,[value])</function> Checks if the current dialog belongs to a profile. If the profile supports values, the check can be reinforced to take into account a specific value - if the dialog was inserted into the profile for a specific value. If no value is passed, only simply belonging of the dialog to the profile is checked. Note that if the profile does not support values, this will be silently discarded. NOTE: the dialog must be created before using this function (use create_dialog() function before). Meaning of the parameters is as follows: profile - name of the profile to be checked against; value (optional) - string value to toughen the check. Pseudo-variables are supported. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>is_in_profile</function> usage ... if (is_in_profile("inbound_call")) { log("this request belongs to a inbound call\n"); } ... if (is_in_profile("caller","XX")) { log("this request belongs to a call of user XX\n"); } ...
<function moreinfo="none">get_profile_size(profile,[value],size)</function> Returns the number of dialogs belonging to a profile. If the profile supports values, the check can be reinforced to take into account a specific value - how many dialogs were inserted into the profile with a specific value. If not value is passed, only simply belonging of the dialog to the profile is checked. Note that the profile does not supports values, this will be silently discarded. Meaning of the parameters is as follows: profile - name of the profile to get the size for; value (optional) - string value to toughen the check. Pseudo-variables are supported; size - an AVP or script variable to return the profile size in. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>get_profile_size</function> usage ... get_profile_size("inbound_call","$avp(size)"); xlog("currently there are $avp(size) inbound calls\n"); ... get_profile_size("caller","$fu"); xlog("currently, the user %fu has $avp(size) active outgoing calls\n"); ...
<function moreinfo="none">set_dlg_flag(idx)</function> Sets the dialog flag index idx to true. The dialog flags are dialog persistent and they can be accessed (set and test) for all requests belonging to the dialog. The flag index can be between 0 and 31. NOTE: the dialog must be created before using this function (use create_dialog() function before). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>set_dlg_flag</function> usage ... set_dlg_flag("3"); ...
<function moreinfo="none">test_and_set_dlg_flag(idx, value)</function> Atomically checks if the dialog flag index idx is equal to value. If true, changes the value with the opposite one. This operation is done under the dialog lock. The flag index can be between 0 and 31. The value should be 0 (false) or 1 (true). NOTE: the dialog must be created before using this function (use create_dialog() function before). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>test_and_set_dlg_flag</function> usage ... test_and_set_dlg_flag("3", "0"); ...
<function moreinfo="none">reset_dlg_flag(idx)</function> Resets the dialog flag index idx to false. The dialog flags are dialog persistent and they can be accessed (set and test) for all requests belonging to the dialog. The flag index can be between 0 and 31. NOTE: the dialog must be created before using this function (use create_dialog() function before). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>reset_dlg_flag</function> usage ... reset_dlg_flag("16"); ...
<function moreinfo="none">is_dlg_flag_set(idx)</function> Returns true if the dialog flag index idx is set. The dialog flags are dialog persistent and they can be accessed (set and test) for all requests belonging to the dialog. The flag index can be between 0 and 31. NOTE: the dialog must be created before using this function (use create_dialog() function before). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>is_dlg_flag_set</function> usage ... if (is_dlg_flag_set("16")) { xlog("dialog flag 16 is set\n"); } ...
<function moreinfo="none">store_dlg_value(name,val)</function> Attaches to the dialog the value val under the name name. The values attached to dialogs are dialog persistent and they can be accessed (read and write) for all requests belonging to the dialog. Parameter val may contain pseudo-variables. NOTE: the dialog must be created before using this function (use create_dialog() function before). Same functionality may be obtain by assigning a value to pseudo variable $dlg_val(name). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>store_dlg_value</function> usage ... store_dlg_value("inv_src_ip","$si"); store_dlg_value("account type","prepaid"); # or $dlg_val(account_type) = "prepaid"; ...
<function moreinfo="none">fetch_dlg_value(name,pvar)</function> Fetches from the dialog the value of attribute named name. The values attached to dialogs are dialog persistent and they can be accessed (read and write) for all requests belonging to the dialog. Parameter pvar may be a script var ($var) or and avp ($avp). NOTE: the dialog must be created before using this function (use create_dialog() function before). Same functionality may be obtain by reading the pseudo variable $dlg_val(name). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, REPLY_ROUTE and FAILURE_ROUTE. <function>fetch_dlg_value</function> usage ... fetch_dlg_value("inv_src_ip","$avp(2)"); fetch_dlg_value("account type","$var(account)"); # or $var(account) = $dlg_val(account_type); ...
Exported statistics
<varname>active_dialogs</varname> Returns the number of current active dialogs (may be confirmed or not).
<varname>early_dialogs</varname> Returns the number of early dialogs.
<varname>processed_dialogs</varname> Returns the total number of processed dialogs (terminated, expired or active) from the startup.
<varname>expired_dialogs</varname> Returns the total number of expired dialogs from the startup.
<varname>failed_dialogs</varname> Returns the number of failed dialogs ( dialogs were never established due to whatever reasons - internal error, negative reply, cancelled, etc )
<varname>create_sent</varname> Returns the number of replicated dialog create requests send to other OpenSIPS instances.
<varname>update_sent</varname> Returns the number of replicated dialog update requests send to other OpenSIPS instances.
<varname>delete_sent</varname> Returns the number of replicated dialog delete requests send to other OpenSIPS instances.
<varname>create_recv</varname> Returns the number of dialog create events received from other OpenSIPS instances.
<varname>update_recv</varname> Returns the number of dialog update events received from other OpenSIPS instances.
<varname>delete_recv</varname> Returns the number of dialog delete events received from other OpenSIPS instances.
Exported MI Functions
<varname>dlg_list</varname> Lists the description of the dialogs (calls). If no parameter is given, all dialogs will be listed. If a dialog identifier is passed as parameter (callid and fromtag), only that dialog will be listed. If a index and conter parameter is passed, it will list only a number of "counter" dialogs starting with index (as offset) - this is used to get only section of dialogs. Name: dlg_list Parameters (with dialog idetification): callid (optional) - callid if a single dialog to be listed. from_tag (optional, but cannot be present without the callid parameter) - fromtag (as per initial request) of the dialog to be listed. entry Parameters (with dialog counting): index - offset where the dialog listing should start. counter - how many dialogs should be listed (starting from the offset) MI FIFO Command Format: :dlg_list:_reply_fifo_file_ _empty_line_ :dlg_list:_reply_fifo_file_ abcdrssfrs122444@192.168.1.1 AAdfeEFF33 :dlg_list:_reply_fifo_file_ 40 10
<varname>dlg_list_ctx</varname> The same as the dlg_list but including in the dialog description the associated context from modules sitting on top of the dialog module. This function also prints the dialog's values. In case of binary values, the non-printable chars are represented in hex (e.g. \x00) Name: dlg_list_ctx Parameters: see dlg_list MI FIFO Command Format: :dlg_list_ctx:_reply_fifo_file_ _empty_line_
<varname>dlg_end_dlg</varname> Terminates an ongoing dialog. If dialog is established, BYEs are sent in both directions. If dialog is in unconfirmed or early state, a CANCEL will be sent to the callee side, that will trigger a 487 from the callee, which, when relayed, will also end the dialog on the caller's side. Name: dlg_end_dlg The accepted formats (as parameters) may be: dlg_end_dlg h_entry h_id [extra_hdrs] dlg_end_dlg dialog_id [extra_hdrs] Where: h_entry - numerical hash entry of the dialog in the internal dialog table (provided by dlg_list) h_id - numerical hash id of the dialog on the hash entry (provided by dlg_list) dialog_id - numerical unique ID of the dialog (as provided by dlg_list) extra_hdrs - (optional) string containg the extra headers (full format) to be added to the BYE requests. The values for the h_entry and h_id or dialog_id can be get via the "dlg_list" MI command. MI FIFO Command Format: :dlg_end_dlg:_reply_fifo_file_ 342 56 _empty_line_
<varname>profile_get_size</varname> Returns the number of dialogs belonging to a profile. If the profile supports values, the check can be reinforced to take into account a specific value - how many dialogs were inserted into the profile with a specific value. If not value is passed, only simply belonging of the dialog to the profile is checked. Note that the profile does not supports values, this will be silently discarded. Name: profile_get_size Parameters: profile - name of the profile to get the value for. value (optional)- string value to toughen the check; MI FIFO Command Format: :profile_get_size:_reply_fifo_file_ inbound_calls _empty_line_
<varname>profile_list_dlgs</varname> Lists all the dialogs belonging to a profile. If the profile supports values, the check can be reinforced to take into account a specific value - list only the dialogs that were inserted into the profile with that specific value. If not value is passed, all dialogs belonging to the profile will be listed. Note that the profile does not supports values, this will be silently discarded. Also, when using shared profiles using the CacheDB interface, this command will only display the local dialogs. Name: profile_list_dlgs Parameters: profile - name of the profile to list the dialog for. value (optional)- string value to toughen the check; MI FIFO Command Format: :profile_list_dlgs:_reply_fifo_file_ inbound_calls _empty_line_
<varname>profile_get_values</varname> Lists all the values belonging to a profile along with their count. If the profile does not support values a total count will be returned. Note that this function does not work for shared profiles over the CacheDB interface. Name: profile_get_values Parameters: profile - name of the profile to list the dialog for. MI FIFO Command Format: :profile_get_values:_reply_fifo_file_ inbound_calls _empty_line_
<varname>profile_end_dlgs</varname> Terminate all ongoing dialogs from a specified profile, on a single dialog it performs the same operations as the command Name: profile_end_dlgs Parameters: profile - name of the profile that will have its dialogs termianted value - (optional) if the profile supports values terminate only the dialogs with the specified value MI FIFO Command Format: :profile_end_dlgs:_reply_fifo_file_ inbound_calls _empty_line_
<varname>dlg_db_sync</varname> Will synchronize the information about the dialogs from the database with the OpenSIPS internal memory. To be used mainly for transferring OpenSIPS dialog information from one server to another. Name: dlg_db_sync It takes no parameters MI FIFO Command Format: :dlg_db_sync:_reply_fifo_file_ _empty_line_
<varname>dlg_restore_db</varname> Restores the dialog table after a potential desynchronization event. The table is truncated, then populated with CONFIRMED dialogs from memory. Name: dlg_restore_db It takes no parameters MI FIFO Command Format: :dlg_restore_db:_reply_fifo_file_ _empty_line_
<varname>list_all_profiles</varname> Lists all the dialog profiles, along with 1 or 0 if the given profile has/does not have an associated value. Name: list_all_profiles Parameters: It takes no parameters MI FIFO Command Format: :list_all_profiles:_reply_fifo_file_ _empty_line_
Exported pseudo-variables
<varname>$DLG_count</varname> Returns the number of current active dialogs (may be confirmed or not).
<varname>$DLG_status</varname> Returns the status of the dialog corresponding to the processed sequential request. This PV will be available only for sequential requests, after doing loose_route(). Value may be: NULL - Dialog not found. 1 - Dialog unconfirmed (created but no reply received at all) 2 - Dialog in early state (created provisional reply received, but no final reply received yet) 3 - Confirmed by a final reply but no ACK received yet. 4 - Confirmed by a final reply and ACK received. 5 - Dialog ended.
<varname>$DLG_lifetime</varname> Returns the duration (in seconds) of the dialog corresponding to the processed sequential request. The duration is calculated from the dialog confirmation and the current moment. This PV will be available only for sequential requests, after doing loose_route(). NULL will be returned if there is no dialog for the request.
<varname>$DLG_flags</varname> Returns the dialog flags array (as a single integer value) of the dialog corresponding to the processed sequential request. This PV will be available only for sequential requests, after doing loose_route(). NULL will be returned if there is no dialog for the request.
<varname>$DLG_dir</varname> Returns the direction of the request in dialog (as "UPSTREAM" or "DOWNSTREAM" string) - to be used for sequential request. This PV will be available only for sequential requests (on replies), after doing loose_route(). NULL will be returned if there is no dialog for the request.
<varname>$DLG_did</varname> Returns the id of the dialog corresponding to the processed sequential request. The output format is entry ':' id (as returned by the dlg_list MI function). This PV will be available only for sequential requests, after doing loose_route(). NULL will be returned if there is no dialog for the request.
<varname>$DLG_end_reason</varname> Returns the reason for the dialog termination. It can be one of the following : Upstream BYE - Callee has sent a BYE Downstream BYE - Caller has sent a BYE Lifetime Timeout - Dialog lifetime expired MI Termination - Dialog ended via the MI interface Ping Timeout - Dialog ended because no reply to in-dialog pings RTPProxy Timeout - Media timeout signaled by RTPProxy NULL will be returned if there is no dialog for the request, or if the dialog is not ended in the current context.
<varname>$DLG_timeout</varname> Used to set the dialog lifetime (in seconds). When read, the variable returns the number of seconds until the dialog expires and is destroyed. Note that reading the variable is only possible after the dialog is created (for initial requests) or after doing loose_route() (for sequential requests). Important notice: using this variable with a REALTIME db_mode is very inefficient, because every time the dialog value is changed, a database update is done. NULL will be returned if there is no dialog for the request, otherwise the number of seconds until the dialog expiration.
<varname>$dlg_val(name)</varname> This is a read/write variable that allows access to the dialog attribute named name. This PV will be available only for sequential requests, after doing loose_route(). NULL will be returned if there is no dialog for the request.
Exported Events
<function moreinfo="none">E_DLG_STATE_CHANGED</function> This event is raised when the dialog state is changed. Parameters: ostate - the old state of the dialog. nstate - the new state of the dialog.
opensips-2.2.2/modules/dialog/doc/dialog_devel.xml000066400000000000000000000067471300170765700222150ustar00rootroot00000000000000 &develguide;
Available Functions
<function moreinfo="none">register_dlgcb (dialog, type, cb, param, free_param_cb)</function> Register a new callback to the dialog. Meaning of the parameters is as follows: struct dlg_cell* dlg - dialog to register callback to. If maybe NULL only for DLG_CREATED callback type, which is not a per dialog type. int type - types of callbacks; more types may be register for the same callback function; only DLG_CREATED must be register alone. Possible types: DLGCB_LOADED DLGCB_SAVED DLG_CREATED - called when a new dialog is created - it's a global type (not associated to any dialog) DLG_FAILED - called when the dialog was negatively replied (non-2xx) - it's a per dialog type. DLG_CONFIRMED - called when the dialog is confirmed (2xx replied) - it's a per dialog type. DLG_REQ_WITHIN - called when the dialog matches a sequential request - it's a per dialog type. DLG_TERMINATED - called when the dialog is terminated via BYE - it's a per dialog type. DLG_EXPIRED - called when the dialog expires without receiving a BYE - it's a per dialog type. DLGCB_EARLY - called when the dialog is created in an early state (18x replied) - it's a per dialog type. DLGCB_RESPONSE_FWDED - called when the dialog matches a reply to the initial INVITE request - it's a per dialog type. DLGCB_RESPONSE_WITHIN - called when the dialog matches a reply to a subsequent in dialog request - it's a per dialog type. DLGCB_MI_CONTEXT - called when the mi dlg_list_ctx command is invoked - it's a per dialog type. DLGCB_DESTROY dialog_cb cb - callback function to be called. Prototype is: void (dialog_cb) (struct dlg_cell* dlg, int type, struct dlg_cb_params * params); void *param - parameter to be passed to the callback function. param_free callback_param_free - callback function to be called to free the param. Prototype is: void (param_free_cb) (void *param);
opensips-2.2.2/modules/dialog/doc/dialog_faq.xml000066400000000000000000000050121300170765700216450ustar00rootroot00000000000000 &faqguide; What happened with topology_hiding() function? The respective functionality was moved into the topology_hiding module. Function prototype has remained the same. What happened with use_tight_match parameter? The parameter was removed with version 1.3 as the option of tight matching became mandatory and not configurable. Now, the tight matching is done all the time (when using DID matching). What happened with bye_on_timeout_flag parameter? The parameter was removed in a dialog module parameter restructuring. To keep the bye on timeout behavior, you need to provide a "B" string parameter to the create_dialog() function. What happened with dlg_flag parameter? The parameter is considered obsolete. The only way to create a dialog is to call the create_dialog() function Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/dialplan/000077500000000000000000000000001300170765700166175ustar00rootroot00000000000000opensips-2.2.2/modules/dialplan/Makefile000066400000000000000000000013061300170765700202570ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=dialplan.so # set CROSS_COMPILE to true if you want to skip # the autodetection # CROSS_COMPILE=true ifeq ($(CROSS_COMPILE),) PCRE_BUILDER = $(shell \ if which pcre-config >/dev/null 2>/dev/null; then \ echo 'pcre-config'; \ elif pkg-config --exists libcre; then \ echo 'pkg-config libpcre'; \ fi) endif ifeq ($(PCRE_BUILDER),) DEFS += -I$(SYSBASE)/include \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/lib \ -L$(LOCALBASE)/lib -lpcre else DEFS += $(shell $(PCRE_BUILDER) --cflags) LIBS += $(shell $(PCRE_BUILDER) --libs) endif include ../../Makefile.modules opensips-2.2.2/modules/dialplan/README000066400000000000000000000405631300170765700175070ustar00rootroot00000000000000dialplan Module Andreea-Ancuta Onofrei Edited by Andreea-Ancuta Onofrei Copyright © 2007-2008 Voice Sistem SRL Revision History Revision $Revision: 5895 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How it works 1.3. Usage cases 1.4. Database structure and usage 1.4.1. What to place in table 1.5. Dependencies 1.5.1. OpenSIPS Modules 1.5.2. External Libraries or Applications 1.6. Exported Parameters 1.6.1. partition (string) 1.6.2. db_url (string) 1.6.3. table_name (string) 1.6.4. dpid_col (string) 1.6.5. pr_col (string) 1.6.6. match_op_col (string) 1.6.7. match_exp_col (string) 1.6.8. match_flags_col (string) 1.6.9. subst_exp_col (string) 1.6.10. repl_exp_col (string) 1.6.11. timerec_col (integer) 1.6.12. disabled_col (integer) 1.6.13. attrs_col (string) 1.7. Exported Functions 1.7.1. dp_translate([partition:]id, src/dest[, attrs_pvar]) 1.8. Exported MI Functions 1.8.1. dp_reload 1.8.2. dp_translate 1.8.3. dp_show_partiton 1.9. Installation 2. Developer's Guide List of Examples 1.1. Set partition parameter 1.2. Set default partition with partition parameter 1.3. Set db_url parameter 1.4. Set table_name parameter 1.5. Set dpid_col parameter 1.6. Set pr_col parameter 1.7. Set match_op_col parameter 1.8. Set match_exp_col parameter 1.9. Set match_flags_col parameter 1.10. Set subs_exp_col parameter 1.11. Set repl_exp_col parameter 1.12. Set timerec_col parameter 1.13. Set disabled_col parameter 1.14. Set attrs_col parameter 1.15. dp_translate usage 1.16. dp_translate usage 1.17. dp_translate usage 1.18. dp_translate usage Chapter 1. Admin Guide 1.1. Overview This module implements generic string translations based on matching and replacement rules. It can be used to manipulate R-URI or a PV and to translated to a new format/value. 1.2. How it works At startup, the module will load all transformation rules from one or more dialplan-compatible tables. The data of each table will be stored in a partition which is defined by the "db_url" and "table_name" parameters. Every table row will be stored in memory as a translation rule. Each rule will describe how the matching should be made, how the input value should be modified and which attributes should be set for the matching transformation. A dialplan rule can be of two types: * "String matching" rule - performs a string equality test against the input string. The case of the characters can be ignored by enabling bit 1 of the rule's "match_flags" bitmask column (i.e. set the column value to 1 or 0, for insensitive or sensitive) * "Regex matching" rule - uses Perl Compatible Regular Expressions, and will attempt to match the rule's expression against an input string. The regex maching can be done in a caseless manner by enabling bit 1 of the rule's "match_flags" bitmask column (i.e. set the column value to 1 or 0, for insensitive or sensitive) The module provides the dp_translate() script function, which expects an input string value that will be matched, at worst, against all rules of a partition. Internally, the module groups a partition's rules into two sets, "string" and "regex". The matching logic will attempt to find the first match within each of these two sets of rules. Each set will be iterated in ascending order of priority. If an input string happens to match a rule in each of the two sets, the rule with the smallest priority will be chosen. Furthermore, should these two matching rules also have equal priorities, the one with the smallest "id" field (the unique key) will be chosen. Once a single rule is decided upon, the defined transformation (if any) is applied and the result is returned as output value. Also, if any string attribute is associated to the rule, this will be returned to the script along with the output value. 1.3. Usage cases The module can be used to implement dialplans - to do auto completion of the dialed numbers (e.g. national to international), to convert generic numbers to specific numbers (e.g. for emergency numbers). Also the module can be used for detecting ranges or sets of numbers mapped on a service/case - the "attributes" string column can be used here to store extra information about the service/case. Non-SIP string translation can also be implemented - like converting country names from all possible formats to a canonical format: (UK, England, United Kingdom) -> GB. Any other string-based translation or detection for whatever other purposes. 1.4. Database structure and usage Depending what kind of operation (translation, matching, etc) you want to do with the module, you need to populate the appropriate DB records. The definition of the tables used by the dialplan module can be found at http://www.opensips.org/html/docs/db/db-schema-devel.html#AEN15 01 1.4.1. What to place in table 1.4.1.1. String translation (regexp detection, subst translation) Recognize a number block in all forms (international, national) and convert it to a canonical format (E.164) * match_op = 1 (regexp) * match_exp = "^(0040|\+40|0|40)21[0-9]+" ; regular expression that will be used to match with this rule (if the rule should be applied for the input string) * match_flags = 0 (0 - case sensitive, 1 - case insensitive matching) * subst_exp = "^(0040|\+40|0|40)(.+)" ; regular expression used to do the transformation (first part of the subst operation) * repl_exp = "40\2" ; second part of the subst (output) - linked to the subst_exp field; when both defined, they work as a subst() 1.4.1.2. String translation (regexp detection, replacement) Recognize the name of a country (multiple languages) and convert it to a single, fixed value * match_op = 1 (regexp) * match_exp = "^((Germany)|(Germania)|(Deutschland)|(DE))" ; regular expression that will be used to match with this rule (if the rule should be applied for the input string) * match_flags = 0 (0 - case sensitive, 1 - case insensitive matching) * subst_exp = NULL ; when translation is actually a replacement, this field must be NULL. * repl_exp = "DE" ; static string to replace the input - whenever this rule will match, it will return this string as output. 1.4.1.3. Number detection (regexp detection, no replacement) Recognize a block of numbers as belong to a single service and signalize this via an attribute. * match_op = 1 (regexp) * match_exp = "^021456[0-9]{5}" ; regular expression that will be used to match with this rule (if the rule should be applied for the input string) * match_flags = 0 (0 - case sensitive, 1 - case insensitive matching) * subst_exp = NULL ; no translation * repl_exp = NULL ; no translation * attrs = "serviceX" ; whatever string you will get into OpenSIPS script and it will provide you more information (totally custom) 1.4.1.4. String conversion (equal detection, replacement) Recognize a fixed string/number and replace it with something fixed. * match_op = 0 (equal) * match_exp = "SIP server" ; string to be matched * match_flags = 0 (0 - case sensitive, 1 - case insensitive matching) * subst_exp = NULL ; no subst translation * repl_exp = "OpenSIPS" ; output string 1.5. Dependencies 1.5.1. OpenSIPS Modules The following modules must be loaded before this module: * None 1.5.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libpcre-dev - the development libraries of PCRE. 1.6. Exported Parameters 1.6.1. partition (string) This can be used to define new db_url and table_name parameters from which to load the translation rules. These parameters will be held in partitions. The db_url parameter is mandatory. The order of the parameters does not matter. Multiple partitions can be defined and you can also define the default partition here. The name of this partition is "default". In order to be able to use a table from a partition, its name must be found in the "version" table belonging to the database defined in the partition's db_url. Example 1.1. Set partition parameter ... modparam("dialplan", "partition", "part2:table_name=dialplan;db_url=mysql://user:pw@localhost/db;") ... Example 1.2. Set default partition with partition parameter ... modparam("dialplan", "partition", "default:table_name=dialplan;db_url=mysql://user:pw@localhost/db;") ... 1.6.2. db_url (string) The translation rules will be loaded using this database url.This will be the db_url parameter value for the default partition. NOTE: if you intend to use the default partition you have to explicity set this default db_url, otherwise OpenSIPS will not start (he value of global default db_url is not inherited here! ). Example 1.3. Set db_url parameter ... modparam("dialplan", "db_url", "mysql://user:passwb@localhost/db") ... 1.6.3. table_name (string) The table's name from which to load the translation rules.This will be the table_name parameter value for the default partition. The db_url must be defined for the default partition in order to be able to define it's table name. Default value is “dialplanâ€. Example 1.4. Set table_name parameter ... modparam("dialplan", "table_name", "my_table") ... 1.6.4. dpid_col (string) The column name to store the dialplan ID group. Default value is “dpidâ€. Example 1.5. Set dpid_col parameter ... modparam("dialplan", "dpid_col", "column_name") ... 1.6.5. pr_col (string) The column name to store the priority of the corresponding rule from the table row. Smaller priority values have higher precedence. Default value is “prâ€. Example 1.6. Set pr_col parameter ... modparam("dialplan", "pr_col", "column_name") ... 1.6.6. match_op_col (string) The column name to store the type of matching of the rule. Default value is “match_opâ€. Example 1.7. Set match_op_col parameter ... modparam("dialplan", "match_op_col", "column_name") ... 1.6.7. match_exp_col (string) The column name to store the rule match expression. Default value is “match_expâ€. Example 1.8. Set match_exp_col parameter ... modparam("dialplan", "match_exp_col", "column_name") ... 1.6.8. match_flags_col (string) The column name to store various matching flags. Currently 0 - case sensitive matching, 1 - case insensitive matching. Default value is “match_flagsâ€. Example 1.9. Set match_flags_col parameter ... modparam("dialplan", "match_flags_col", "column_name") ... 1.6.9. subst_exp_col (string) The column name to store the rule's substitution expression. Default value is “subst_expâ€. Example 1.10. Set subs_exp_col parameter ... modparam("dialplan", "subst_exp_col", "column_name") ... 1.6.10. repl_exp_col (string) The column name to store the rule's replacement expression. Default value is “repl_expâ€. Example 1.11. Set repl_exp_col parameter ... modparam("dialplan", "repl_exp_col", "column_name") ... 1.6.11. timerec_col (integer) The column name that indicates an additional time recurrence check within the rule. (column values are RFC 2445-compatible strings) Default value is “timerecâ€. Example 1.12. Set timerec_col parameter ... modparam("dialplan", "timerec_col", "month_match") ... 1.6.12. disabled_col (integer) The column name that indicates if the dialplan rule is disabled. Default value is “disabledâ€. Example 1.13. Set disabled_col parameter ... modparam("dialplan", "disabled_col", "disabled_column") ... 1.6.13. attrs_col (string) The column name to store rule-specific attributes. Default value is “attrsâ€. Example 1.14. Set attrs_col parameter ... modparam("dialplan", "attrs_col", "column_name") ... 1.7. Exported Functions 1.7.1. dp_translate([partition:]id, src/dest[, attrs_pvar]) Will try to translate the src string into dest string according to the translation rules with dialplan ID equal to id. Meaning of the parameters is as follows: * id - the dialplan id of possible matching rules. The id parameter can have the following types: + integer - the dialplan id is statically assigned + pvar - the dialplan id is the value of an existing pseudo-variable (as integer value) * partition - Specifies the partition where the search will take place. This parameter can be ommited. If the paramater is omitted the default partition will be used if exists. The partition parameter can have the following types: + string - the table name is statically assigned + pvar - the partition name is the value of an existing pseudo-variable (as string value) * src/dest - input and output of the function. If this parameter is missing the default parameter “ruri.user/ruri.user†will be used, thus translating the request uri. The “src†variable can be any type of pseudo-variable. The “dest†variable can be also any type of pseudo-variable, but it must be a writable one. * attrs_pvar (output, optional) - a pseudo-variable which will hold the attributes of the matched translation rule. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE. Example 1.15. dp_translate usage ... dp_translate("240", "$ruri.user/$avp(dest)"); xlog("translated to var $avp(dest) \n"); ... Example 1.16. dp_translate usage ... $avp(src) = $ruri.user; dp_translate("$var(x)", "$avp(src)/$var(y)", "$var(attrs)"); xlog("translated to var $var(y) with attributes: '$var(attrs)'\n"); ... Example 1.17. dp_translate usage ... $avp(src) = $uri.user; dp_translate("example_partition:$var(x)", "$avp(src)/$var(y)", "$avp(att rs)"); xlog("translated to var $var(y) with attributes: '$avp(attrs)'\n"); ... Example 1.18. dp_translate usage ... $var(partition) = "default"; $var(id) = 10; dp_translate("$var(partition):$var(id)", "$avp(src)/$var(y)"); xlog("translate to var $var(y) partition $var(partition)") ... 1.8. Exported MI Functions 1.8.1. dp_reload It will update the translation rules, loading the database info. Name: dp_reload Parameters: 1 * table_name - Partition to be reloaded. If no table is specified, the table specified in the "partition" parameter (default "default") will be reloaded. MI DATAGRAM Command Format: :dp_reload: _empty_line_ 1.8.2. dp_translate It will apply a translation rule identified by a dialplan id and an input string. Name: dp_translate Parameters: 2 * [Partition Name:]Dialplan ID - The dpid of the rules used to match the input string. The table name can be ommited. The default table is dialplan. * Input String MI DATAGRAM Command Format: :dp_translate: dpid input _empty_line_ 1.8.3. dp_show_partiton Display partition(s) details. Name: dp_show_partiton Parameters: 2 * Partition Name - The partition name. If no partition is specified, all known partitions will be listed. MI DATAGRAM Command Format: :dp_translate: default _empty_line_ 1.9. Installation The modules requires one table in OpenSIPS database: dialplan.The SQL syntax to create them can be found in dialplan-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. Chapter 2. Developer's Guide Revision History Revision $Revision: 5895 $ $Date$ The module does not provide any API to use in other OpenSIPS modules. opensips-2.2.2/modules/dialplan/dialplan.c000066400000000000000000000714321300170765700205560ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-08-01 initial version (ancuta onofrei) */ #include #include #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../action.h" #include "../../pvar.h" #include "../../script_var.h" #include "../../dset.h" #include "../../mem/mem.h" #include "../../mi/mi.h" #include "../../parser/parse_to.h" #include "../../mod_fix.h" #include "dialplan.h" #include "dp_db.h" #define DEFAULT_PARAM "$ruri.user" #define DEFAULT_PARTITION "default" #define PARAM_URL "db_url" #define PARAM_TABLE "table_name" #define DP_CHAR_COLON ':' #define DP_CHAR_SLASH '/' #define DP_CHAR_EQUAL '=' #define DP_CHAR_SCOLON ';' #define DP_TYPE_URL 0 #define DP_TYPE_TABLE 1 #define is_space(p) (*(p) == ' ' || *(p) == '\t' || \ *(p) == '\r' || *(p) == '\n') static int mod_init(void); static int child_init(int rank); static int mi_child_init(void); static void mod_destroy(); static struct mi_root * mi_reload_rules(struct mi_root *cmd_tree,void *param); static struct mi_root * mi_translate(struct mi_root *cmd_tree, void *param); static struct mi_root * mi_show_partition(struct mi_root *cmd_tree, void *param); static int dp_translate_f(struct sip_msg *m, char *id, char *out, char *attrs); static int dp_trans_fixup(void ** param, int param_no); static int dp_set_partition(modparam_t type, void* val); static void dp_print_list(void); str default_param_s = str_init(DEFAULT_PARAM); str default_dp_partition = {NULL, 0}; dp_param_p default_par2 = NULL; static str database_url = {NULL, 0}; static param_export_t mod_params[]={ { "partition", STR_PARAM|USE_FUNC_PARAM, (void*)dp_set_partition}, { "db_url", STR_PARAM, &default_dp_db_url.s}, { "table_name", STR_PARAM, &default_dp_table.s }, { "dpid_col", STR_PARAM, &dpid_column.s }, { "pr_col", STR_PARAM, &pr_column.s }, { "match_op_col", STR_PARAM, &match_op_column.s }, { "match_exp_col", STR_PARAM, &match_exp_column.s }, { "match_flags_col", STR_PARAM, &match_flags_column.s }, { "subst_exp_col", STR_PARAM, &subst_exp_column.s }, { "repl_exp_col", STR_PARAM, &repl_exp_column.s }, { "attrs_col", STR_PARAM, &attrs_column.s }, { "timerec_col", STR_PARAM, &timerec_column.s }, { "disabled_col", STR_PARAM, &disabled_column.s}, {0,0,0} }; static mi_export_t mi_cmds[] = { { "dp_reload", 0, mi_reload_rules, 0, 0, mi_child_init}, { "dp_translate", 0, mi_translate, 0, 0, 0}, { "dp_show_partition", 0, mi_show_partition, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static cmd_export_t cmds[]={ {"dp_translate",(cmd_function)dp_translate_f, 3, dp_trans_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|BRANCH_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"dp_translate",(cmd_function)dp_translate_f, 2, dp_trans_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|BRANCH_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"dp_translate",(cmd_function)dp_translate_f, 1, dp_trans_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|BRANCH_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {0,0,0,0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_WARN }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "dialplan", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ mod_params, /* param exports */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* additional processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, child_init /* per-child init function */ }; static inline char* strchrchr(char* str, char c1, char c2) { char* ret = NULL; if (!str) return NULL; for (ret = str; (ret = *ret == '\0' ? NULL : ret) && *ret != c1 && *ret != c2; ret++); return ret; } static inline char* memchrchr(char* str, char c1, char c2, int len) { int i; if (len == 0) return NULL; for (i = 0; i < len; i++) { register char c = *(str + i); if(c == c1 || c == c2) return str + i; } return NULL; } static dp_head_p dp_get_head(str part_name){ dp_head_p start; for (start = dp_hlist; start && str_strcmp(&part_name, &start->partition); start = start->next); return start; } /*Inserts table_name/db url into the list of heads*/ static int dp_head_insert(int dp_insert_type, str *content, str *partition) { #define h_insert(type, url_str, table_str, ins_str ) \ do{ \ if( type == DP_TYPE_URL ) { \ url_str = ins_str; \ } else { \ table_str = ins_str; \ } \ }while(0); dp_head_p start = dp_hlist; dp_head_p tmp = NULL; if ((!content && (!content->s || !content->len)) || (!partition && (!partition->s || !partition->len))) { LM_ERR("invalid insert in partition!\n"); return -1; } /*First Insertion*/ if (!dp_hlist) { dp_hlist = pkg_malloc(sizeof *dp_hlist); if (!dp_hlist) { LM_ERR("No more pkg mem\n"); return -1; } memset(dp_hlist, 0, sizeof *dp_hlist); dp_hlist->partition = *partition; h_insert( dp_insert_type, dp_hlist->dp_db_url, dp_hlist->dp_table_name, *content); return 0; } /* start can't be null here, should exit on first IF instruction * if null*/ do { if (!str_strcmp(partition, &start->partition)) { h_insert( dp_insert_type, start->dp_db_url, start->dp_table_name, *content); return 0; } /* always want second condition to be true since only the * first condition is valid; the second is just an assignment * in case the first one succeeds */ } while (start->next != NULL && (start=start->next,1)); tmp = pkg_malloc(sizeof(dp_head_t)); if (!tmp) { LM_ERR("No more pkg mem\n"); return -1; } memset(tmp, 0, sizeof(dp_head_t)); tmp->partition = *partition; h_insert( dp_insert_type, tmp->dp_db_url, tmp->dp_table_name, *content); start->next = tmp; return 0; #undef h_insert } static int dp_create_head(str part_desc) { str tmp; str partition; str param_type, param_value; char* end, *start; int ulen = strlen(PARAM_URL), tlen = strlen(PARAM_TABLE); tmp.s = part_desc.s; end = q_memchr(part_desc.s, DP_CHAR_COLON, part_desc.len); if (end == NULL) { LM_ERR("[[%s]]\n", tmp.s); goto out_err; } tmp.len = end - tmp.s; str_trim_spaces_lr(tmp); partition = tmp; do { start = ++end; end = q_memchr(start, DP_CHAR_SCOLON, part_desc.s + part_desc.len - start); if (end == NULL) break; param_type.s = start; param_value.s = q_memchr(start, DP_CHAR_EQUAL, part_desc.len + part_desc.s - start); if (param_value.s == 0) { LM_ERR("[[%s]]!\n", param_value.s); goto out_err; } param_type.len = param_value.s - param_type.s; param_value.len = end - (++param_value.s); str_trim_spaces_lr(param_type); str_trim_spaces_lr(param_value); if (param_type.len == ulen && !memcmp(param_type.s, PARAM_URL, ulen)) { dp_head_insert( DP_TYPE_URL, ¶m_value, &partition); } else if ( param_type.len == tlen && !memcmp( param_type.s, PARAM_TABLE, tlen)) { dp_head_insert( DP_TYPE_TABLE, ¶m_value, &partition); } else { LM_ERR("Invalid parameter type definition [[%.*s]]\n", param_type.len, param_type.s); return -1; } } while(1); return 0; out_err: LM_ERR("invalid partition param definition\n"); return -1; } static int dp_set_partition(modparam_t type, void* val) { str p; p.s = (char *)val; p.len = strlen(val); if (dp_create_head(p)) { LM_ERR("Error creating head!\n"); return -1; } return 0; } static void dp_print_list(void) { dp_head_p start = dp_hlist; if (!start) LM_DBG("List is empty\n"); while (start != NULL) { LM_DBG("Partition=[%.*s] url=[%.*s] table=[%.*s] next=[%p]\n", start->partition.len, start->partition.s, start->dp_db_url.len, start->dp_db_url.s, start->dp_table_name.len, start->dp_table_name.s, start->next); start = (dp_head_p)start->next; } } static int mod_init(void) { str def_str = str_init(DEFAULT_PARTITION); dp_head_p el = dp_get_head(def_str); LM_INFO("initializing module...\n"); dpid_column.len = strlen(dpid_column.s); pr_column.len = strlen(pr_column.s); match_op_column.len = strlen(match_op_column.s); match_exp_column.len = strlen(match_exp_column.s); match_flags_column.len = strlen(match_flags_column.s); subst_exp_column.len = strlen(subst_exp_column.s); repl_exp_column.len = strlen(repl_exp_column.s); attrs_column.len = strlen(attrs_column.s); timerec_column.len = strlen(timerec_column.s); disabled_column.len = strlen(disabled_column.s); if (default_dp_db_url.s) { default_dp_db_url.len = strlen(default_dp_db_url.s); if (!el) { default_dp_partition.len = sizeof(DEFAULT_PARTITION) - 1; default_dp_partition.s = pkg_malloc(default_dp_partition.len); if (!default_dp_partition.s) { LM_ERR("No more pkg memory\n"); return -1; } memcpy(default_dp_partition.s, DEFAULT_PARTITION, default_dp_partition.len); } else { default_dp_partition.s = el->partition.s; default_dp_partition.len = el->partition.len; } dp_head_insert( DP_TYPE_URL, &default_dp_db_url, &default_dp_partition); } if (default_dp_table.s) { if (!default_dp_partition.s) { if (!el) { LM_ERR("DB URL not defined for default partition!\n"); return -1; } else { default_dp_partition.s = el->partition.s; default_dp_partition.len = el->partition.len; } } default_dp_table.len = strlen(default_dp_table.s); dp_head_insert( DP_TYPE_TABLE, &default_dp_table, &default_dp_partition); } el = dp_hlist; for (el = dp_hlist; el ; el = el->next) { //db_url must be set if (!el->dp_db_url.s) { LM_ERR("DB URL is not defined for partition %.*s!\n", el->partition.len,el->partition.s); return -1; } if (!el->dp_table_name.s) { el->dp_table_name.len = sizeof(DP_TABLE_NAME) - 1; el->dp_table_name.s = pkg_malloc(el->dp_table_name.len); if(!el->dp_table_name.s){ LM_ERR("No more pkg mem\n"); return -1; } memcpy(el->dp_table_name.s, DP_TABLE_NAME, el->dp_table_name.len); } } default_par2 = (dp_param_p)shm_malloc(sizeof(dp_param_t)); if(default_par2 == NULL){ LM_ERR("no shm more memory\n"); return -1; } memset(default_par2, 0, sizeof(dp_param_t)); default_param_s.len = strlen(default_param_s.s); if (pv_parse_spec( &default_param_s, &default_par2->v.sp[0])==NULL) { LM_ERR("input pv is invalid\n"); return -1; } default_param_s.len = strlen(default_param_s.s); if (pv_parse_spec( &default_param_s, &default_par2->v.sp[1])==NULL) { LM_ERR("output pv is invalid\n"); return -1; } dp_print_list(); if(init_data() != 0) { LM_ERR("could not initialize data\n"); return -1; } return 0; #undef init_db_url_part } static int child_init(int rank) { dp_connection_list_p el; /* only process with rank 1 loads data */ if (rank != 1) return 0; /*Connect to DB s and get rules*/ for(el = dp_conns; el; el = el->next){ if (init_db_data(el) != 0) { /* all el shall be freed in mod destroy */ LM_ERR("Unable to init db data\n"); return -1; } } dp_disconnect_all_db(); return 0; } static int mi_child_init(void) { dp_connection_list_p el; /*Connect to DB s and get rules*/ for(el = dp_conns; el; el = el->next){ if (dp_connect_db(el) != 0) { /* all el shall be freed in mod destroy */ LM_ERR("Unable to init db data\n"); return -1; } } return 0; } static void mod_destroy(void) { dp_connection_list_p el; /*destroy shared memory*/ if(default_par2){ shm_free(default_par2); default_par2 = NULL; } LM_DBG("Disconnecting from all databases\n"); for(el = dp_conns; el ; el = el->next){ dp_disconnect_db(el); LM_DBG("Successfully disconnected from DB %.*s\n" , el->db_url.len, el->db_url.s); } destroy_data(); } static int dp_get_ivalue(struct sip_msg* msg, dp_param_p dp, int *val) { pv_value_t value; switch (dp->type) { case DP_VAL_STR : *val = dp->v.id; return 0; case DP_VAL_INT : *val = dp->v.pv_id.id; return 0; default : break; } LM_DBG("searching %d\n",dp->v.sp[0].type); if (pv_get_spec_value( msg, &dp->v.sp[0], &value)!=0) { LM_ERR("no PV found (error in script)\n"); return -1; } if (value.flags&(PV_VAL_NULL|PV_VAL_EMPTY)) { LM_ERR("NULL or empty val found (error in script)\n"); return -1; } if (value.flags&PV_VAL_INT) { *val = value.ri; } else if (value.flags&PV_VAL_STR) { if (str2sint(&value.rs, val) != 0) { LM_ERR("Unbale to convert to INT [%.*s]\n", value.rs.len, value.rs.s); return -1; } } else { LM_ERR("non-INT/STR val found (error in script)\n"); return -1; } return 0; } static int dp_get_svalue(struct sip_msg * msg, pv_spec_t spec, str* val) { pv_value_t value; LM_DBG("searching %d \n", spec.type); if ( pv_get_spec_value(msg,&spec,&value)!=0 || value.flags&PV_VAL_NULL || value.flags&PV_VAL_EMPTY || !(value.flags&PV_VAL_STR)){ LM_ERR("no PV or NULL or non-STR val found (error in script)\n"); return -1; } *val = value.rs; return 0; } static int dp_update(struct sip_msg * msg, pv_spec_t * src, pv_spec_t * dest, str * repl) { pv_value_t val; if (repl->s && repl->len) { val.flags = PV_VAL_STR; val.rs = *repl; if (pv_set_value( msg, dest, 0, &val)!=0) { LM_ERR("falied to set the output value!\n"); return -1; } } return 0; } static int dp_translate_f(struct sip_msg *msg, char *str1, char *str2, char *attr_spec) { int dpid; str input, output; dpl_id_p idp; dp_param_p id_par, repl_par; str attrs, *attrs_par; dp_connection_list_p connection; pv_value_t pval; str partition_name; if (!msg) return -1; /* verify first param's value */ id_par = (dp_param_p) str1; if (dp_get_ivalue(msg, id_par, &dpid) != 0){ LM_ERR("no dpid value\n"); return -1; } switch( id_par->type ) { case DP_VAL_INT : if (dp_get_svalue(msg, id_par->v.pv_id.partition, &partition_name)) { LM_ERR("invalid partition\n"); return -1; } goto GET_CONN; case DP_VAL_SPEC : if (dp_get_svalue(msg, id_par->v.sp[1], &partition_name)) { LM_ERR("invalid partition\n"); return -1; } GET_CONN: if (!(id_par->hash = dp_get_connection(&partition_name))) { LM_ERR("invalid partition\n"); return -1; } break; default : break; } LM_DBG("dpid is %i partition is %.*s\n", dpid, id_par->hash->partition.len, id_par->hash->partition.s); repl_par = (str2!=NULL) ? ((dp_param_p)str2) : default_par2; if (dp_get_svalue(msg, repl_par->v.sp[0], &input)!=0){ LM_ERR("invalid param 2\n"); return -1; } LM_DBG("input is %.*s\n", input.len, input.s); connection = id_par->hash; /* ref the data for reading */ lock_start_read( connection->ref_lock ); if ((idp = select_dpid(connection, dpid, connection->crt_index)) == 0) { LM_DBG("no information available for dpid %i\n", dpid); goto error; } LM_DBG("Checking with dpid %i\n", idp->dp_id); attrs_par = attr_spec ? &attrs : NULL; if (translate(msg, input, &output, idp, attrs_par) != 0) { LM_DBG("could not translate %.*s " "with dpid %i\n", input.len, input.s, idp->dp_id); goto error; } LM_DBG("input %.*s with dpid %i => output %.*s\n", input.len, input.s, idp->dp_id, output.len, output.s); /* set the output */ if (dp_update(msg, &repl_par->v.sp[0], &repl_par->v.sp[1], &output) != 0) { LM_ERR("cannot set the output\n"); goto error; } /* we are done reading -> unref the data */ lock_stop_read( connection->ref_lock ); if (attr_spec && attrs.s && attrs.len) { pval.flags = PV_VAL_STR; pval.rs = attrs; if (pv_set_value(msg, (pv_spec_p)attr_spec, 0, &pval) != 0) { LM_ERR("failed to set value '%.*s' for the attr pvar!\n", attrs.len, attrs.s); goto error; } } return 1; error: /* we are done reading -> unref the data */ lock_stop_read( connection->ref_lock ); return -1; } #define verify_par_type(_spec)\ do{\ if( ( ((_spec).type==PVT_NULL) || ((_spec).type==PVT_EMPTY) \ || ((_spec).type==PVT_NONE) )) { \ LM_ERR("NULL/EMPTY Parameter TYPE\n");\ return E_UNSPEC;\ }\ }while(0); /** * Parses a dp command of the type "table_name/dpid". Skips all whitespaces. */ static char *parse_dp_command(char * p, int len, str * partition_name) { char *s, *q; while (is_space(p)) { p++; len--; } if (len <= 0) { s = strchrchr(p, DP_CHAR_SLASH, DP_CHAR_COLON); } else { s = memchrchr(p, DP_CHAR_SLASH, DP_CHAR_COLON, len); } if (s != 0) { q = s+1; while (s > p && is_space(s-1)) s--; if (s == p || (*q == '\0')) return NULL; partition_name->s = p; partition_name->len = s-p; p = q; while (is_space(p)) p++; } else { partition_name->s = 0; partition_name->len = 0; } return p; } #undef is_space /* first param: DPID: type: INT, AVP, SVAR * second param: SRC/DST type: RURI, RURI_USERNAME, AVP, SVAR * default value for the second param: $ru.user/$ru.user */ static int dp_trans_fixup(void ** param, int param_no){ int dpid; dp_param_p dp_par= NULL; char *p, *s = NULL; str lstr, partition_name; dp_connection_list_t *list = NULL; if (param_no < 1 || param_no > 3) return 0; p = (char*)*param; if(!p || (*p == '\0')){ LM_DBG("null param %i\n", param_no); return E_CFG; } dp_par = (dp_param_p)pkg_malloc(sizeof(dp_param_t)); if(dp_par == NULL){ LM_ERR("no more pkg memory\n"); return E_OUT_OF_MEM; } memset(dp_par, 0, sizeof(dp_param_t)); switch (param_no) { case 1: p = parse_dp_command(p, -1, &partition_name); if (p == NULL) { LM_ERR("Invalid dp command\n"); return E_CFG; } if (!partition_name.s && !partition_name.len) { partition_name.s = DEFAULT_PARTITION; partition_name.len = sizeof(DEFAULT_PARTITION) - 1; } if (*partition_name.s != PV_MARKER) { list = dp_get_connection(&partition_name); if(!list){ LM_ERR("Partition with name [%.*s] is not defined\n", partition_name.len, partition_name.s ); return -1; } dp_par->type = DP_VAL_STR; } else { dp_par->type = DP_VAL_SPEC; } if (*p != PV_MARKER) { lstr.s = p; lstr.len = strlen(p); if(str2sint(&lstr, &dpid) != 0) { LM_ERR("bad number <%s>\n",(char *)(*param)); pkg_free(dp_par); return E_CFG; } if(dp_par->type == DP_VAL_SPEC){ /*int dpid and pv partition_name*/ dp_par->type = DP_VAL_INT; dp_par->v.pv_id.id = dpid; if( !pv_parse_spec( &partition_name, &dp_par->v.pv_id.partition)) goto error; } else { /*DP_VAL_STR remains DP_VAL_STR ( int dpid and str partition_name)*/ dp_par->v.id = dpid; } } else { if (dp_par->type == DP_VAL_STR) { /*pv dpid and str partition_name*/ dp_par->type = DP_VAL_STR_SPEC; } else { /*DP_VAL_SPEC remains DP_VAL_SPEC ( pv dpid and pv partition_name) */ if( !pv_parse_spec( &partition_name, &dp_par->v.sp[1])) goto error; } lstr.s = p; lstr.len = strlen(p); if (pv_parse_spec( &lstr, &dp_par->v.sp[0])==NULL) goto error; verify_par_type(dp_par->v.sp[0]); } dp_par->hash = list; break; case 2: if( ((s = strchr(p, '/')) == 0) ||( *(s+1)=='\0')) goto error; *s = '\0'; s++; lstr.s = p; lstr.len = strlen(p); if(pv_parse_spec( &lstr, &dp_par->v.sp[0])==NULL) goto error; verify_par_type(dp_par->v.sp[0]); lstr.s = s; lstr.len = strlen(s); if (pv_parse_spec( &lstr, &dp_par->v.sp[1] )==NULL) goto error; verify_par_type(dp_par->v.sp[1]); if (dp_par->v.sp[1].setf==NULL) { LM_ERR("the output PV is read-only!!\n"); return E_CFG; } dp_par->type = DP_VAL_SPEC; break; case 3: return fixup_pvar(param); } *param = (void *)dp_par; return 0; error: LM_ERR("failed to parse param %i\n", param_no); return E_INVALID_PARAMS; } /* creates an url string without password field*/ static void db_get_url(const str* url){ struct db_id* id = new_db_id(url); static str scheme_delimiter={"://",3}; static str port_delimiter={":",1}; static str host_delimiter={"@",1}; static str database_delimiter={"/",1}; str port; /* allocate memory for the database url if necessary*/ database_url.len = 0; /* sanity checks */ if (id == NULL) return; database_url.s = pkg_realloc(database_url.s, url->len * sizeof(char)); if (database_url.s == NULL) { free_db_id(id); return; } /* shortest database_url is s://a/b so we always need the scheme delimiter*/ if (id->scheme != NULL) { memcpy(database_url.s + database_url.len, id->scheme, strlen(id->scheme)); database_url.len += strlen(id->scheme); memcpy(database_url.s + database_url.len, scheme_delimiter.s, scheme_delimiter.len); database_url.len += scheme_delimiter.len; } if (id->username != NULL) { memcpy(database_url.s + database_url.len, id->username, strlen(id->username)); database_url.len += strlen(id->username); } if (id->host != NULL) { memcpy(database_url.s + database_url.len, host_delimiter.s, host_delimiter.len); database_url.len += host_delimiter.len; memcpy(database_url.s + database_url.len, id->host, strlen(id->host)); database_url.len += strlen(id->host); } if (id->port > 0) { port.s = int2str(id->port,&port.len); memcpy(database_url.s + database_url.len, port_delimiter.s, port_delimiter.len); database_url.len += port_delimiter.len; memcpy(database_url.s + database_url.len, port.s, port.len); database_url.len += port.len; } if (id->database != NULL){ memcpy(database_url.s + database_url.len, database_delimiter.s, database_delimiter.len); database_url.len += database_delimiter.len; memcpy(database_url.s + database_url.len, id->database, strlen(id->database)); database_url.len += strlen(id->database); } /* free alocated memory */ free_db_id(id); } static struct mi_root * mi_show_partition(struct mi_root *cmd_tree, void *param) { struct mi_node *node = NULL; struct mi_root *rpl_tree = NULL; struct mi_node* root= NULL; struct mi_attr* attr; dp_connection_list_t *el; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; if (cmd_tree) node = cmd_tree->node.kids; if (node == NULL) { el = dp_get_connections(); root = &rpl_tree->node; while (el) { node = add_mi_node_child(root, 0, "Partition", 9, el->partition.s, el->partition.len); if( node == NULL) goto error; attr = add_mi_attr(node, 0, "table", 5, el->table_name.s, el->table_name.len); if(attr == NULL) goto error; db_get_url(&el->db_url); if(database_url.len == 0) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "db_url", 6, database_url.s, database_url.len); if(attr == NULL) goto error; el = el->next; } } else { el = dp_get_connection(&node->value); if (!el) goto error; root = &rpl_tree->node; node = add_mi_node_child(root, 0, "Partition", 9, el->partition.s, el->partition.len); if( node == NULL) goto error; attr = add_mi_attr(node, 0, "table", 5, el->table_name.s, el->table_name.len); if(attr == NULL) goto error; db_get_url(&el->db_url); if(database_url.len == 0) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "db_url", 6, database_url.s, database_url.len); if(attr == NULL) goto error; } return rpl_tree; error: if(rpl_tree) free_mi_tree(rpl_tree); return NULL; } static struct mi_root * mi_reload_rules(struct mi_root *cmd_tree, void *param) { struct mi_node *node = NULL; struct mi_root *rpl_tree = NULL; dp_connection_list_t *el; if (cmd_tree) node = cmd_tree->node.kids; if (node == NULL) { /* Reload rules from all partitions */ if(dp_load_all_db() != 0){ LM_ERR("failed to reload database\n"); return 0; } } else if (node->value.s == NULL || node->value.len == 0) { return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } else { el = dp_get_connection(&node->value); if (!el) return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); /* Reload rules from specified partition */ LM_DBG("Reloading rules from table %.*s\n", node->value.len, node->value.s); if(dp_load_db(el) != 0){ LM_ERR("failed to reload database data\n"); return 0; } } rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; return rpl_tree; } /* * mi cmd: dp_translate * * * * */ static struct mi_root * mi_translate(struct mi_root *cmd, void *param) { struct mi_root* rpl= NULL; struct mi_node* root, *node; char *p; dpl_id_p idp; str dpid_str, partition_str; str input; int dpid; str attrs; str output= {0, 0}; dp_connection_list_p connection = NULL; node = cmd->node.kids; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* Get the id parameter */ dpid_str = node->value; if(dpid_str.s == NULL || dpid_str.len== 0) { LM_ERR( "empty idp parameter\n"); return init_mi_tree(404, "Empty id parameter", 18); } p = parse_dp_command(dpid_str.s, dpid_str.len, &partition_str); if (p == NULL) { LM_ERR("Invalid dp command\n"); return init_mi_tree(404, "Invalid dp command", 18); } if (partition_str.s == NULL || partition_str.len == 0) { partition_str.s = DEFAULT_PARTITION; partition_str.len = sizeof(DEFAULT_PARTITION) - 1; } connection = dp_get_connection(&partition_str); dpid_str.len -= (p - dpid_str.s); dpid_str.s = p; if (!connection) { LM_ERR("Unable to get connection\n"); return init_mi_tree(400, "Wrong db connection parameter", 24); } if(str2sint(&dpid_str, &dpid) != 0) { LM_ERR("Wrong id parameter - should be an integer\n"); return init_mi_tree(404, "Wrong id parameter", 18); } node = node->next; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if(node->next!= NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); input = node->value; if(input.s == NULL || input.len== 0) { LM_ERR( "empty input parameter\n"); return init_mi_tree(404, "Empty input parameter", 21); } /* ref the data for reading */ lock_start_read( connection->ref_lock ); if ((idp = select_dpid(connection, dpid, connection->crt_index)) ==0 ){ LM_ERR("no information available for dpid %i\n", dpid); lock_stop_read( connection->ref_lock ); return init_mi_tree(404, "No information available for dpid", 33); } if (translate(NULL, input, &output, idp, &attrs)!=0){ LM_DBG("could not translate %.*s with dpid %i\n", input.len, input.s, idp->dp_id); lock_stop_read( connection->ref_lock ); return init_mi_tree(404, "No translation", 14); } /* we are done reading -> unref the data */ lock_stop_read( connection->ref_lock ); LM_DBG("input %.*s with dpid %i => output %.*s\n", input.len, input.s, idp->dp_id, output.len, output.s); rpl = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl==0) goto error; root= &rpl->node; node = add_mi_node_child(root, 0, "Output", 6, output.s, output.len ); if( node == NULL) goto error; node = add_mi_node_child(root, 0, "ATTRIBUTES", 10, attrs.s, attrs.len); if( node == NULL) goto error; return rpl; error: if(rpl) free_mi_tree(rpl); return 0; } void * wrap_shm_malloc(size_t size) { return shm_malloc(size); } void wrap_shm_free(void * p ) { shm_free(p); } pcre * wrap_pcre_compile(char * pattern, int flags) { pcre * ret ; func_malloc old_malloc ; func_free old_free; const char * error; int erroffset; int pcre_flags = 0; old_malloc = pcre_malloc; old_free = pcre_free; pcre_malloc = wrap_shm_malloc; pcre_free = wrap_shm_free; if (flags & DP_CASE_INSENSITIVE) pcre_flags |= PCRE_CASELESS; ret = pcre_compile( pattern, /* the pattern */ pcre_flags, /* default options */ &error, /* for error message */ &erroffset, /* for error offset */ NULL); pcre_malloc = old_malloc; pcre_free = old_free; return ret; } void wrap_pcre_free( pcre* re) { shm_free(re); } opensips-2.2.2/modules/dialplan/dialplan.h000066400000000000000000000065421300170765700205630ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-08-01 initial version (ancuta onofrei) */ #ifndef _DP_DIALPLAN_H #define _DP_DIALPLAN_H #include "../../parser/msg_parser.h" #include "../../rw_locking.h" #include "../../time_rec.h" #include "../../db/db.h" #include "../../re.h" #include #define REGEX_OP 1 #define EQUAL_OP 0 #define DP_CASE_INSENSITIVE 1 #define DP_INDEX_HASH_SIZE 16 typedef struct dpl_node{ int dpid; int table_id; /*choose between matching regexp/strings with same priority*/ int pr; int matchop; int match_flags; str match_exp, subst_exp, repl_exp; /*keeping the original strings*/ pcre * match_comp, * subst_comp; /*compiled patterns*/ struct subst_expr * repl_comp; str attrs; str timerec; tmrec_t *parsed_timerec; struct dpl_node * next; /*next rule*/ }dpl_node_t, *dpl_node_p; /* HASH_SIZE buckets of matching strings (lowercase hashing) 1 bucket of regexps (index: HASH_SIZE) */ typedef struct dpl_index{ dpl_node_t * first_rule; dpl_node_t * last_rule; }dpl_index_t, *dpl_index_p; /*For every DPID*/ typedef struct dpl_id{ int dp_id; dpl_index_t* rule_hash;/*fast access :string rules are hashed*/ struct dpl_id * next; }dpl_id_t,*dpl_id_p; typedef struct dp_connection_list { dpl_id_t *hash[2]; str table_name; str partition; str db_url; int crt_index, next_index; db_con_t** dp_db_handle; db_func_t dp_dbf; rw_lock_t *ref_lock; struct dp_connection_list * next; } dp_connection_list_t, *dp_connection_list_p; #define DP_VAL_INT 0 #define DP_VAL_SPEC 1 #define DP_VAL_STR 2 #define DP_VAL_STR_SPEC 3 typedef struct dp_pv_int { int id; pv_spec_t partition; } dp_pv_int_t; typedef struct dp_param{ int type; union { int id; pv_spec_t sp[2]; dp_pv_int_t pv_id; } v; dp_connection_list_p hash; }dp_param_t, *dp_param_p; int init_data(); void destroy_data(); int dp_load_db(dp_connection_list_p dp_table); int dp_load_all_db(void); void dp_disconnect_all_db(void); dpl_id_p select_dpid(dp_connection_list_p table, int id, int index); struct subst_expr* repl_exp_parse(str subst); void repl_expr_free(struct subst_expr *se); int translate(struct sip_msg *msg, str user_name, str* repl_user, dpl_id_p idp, str *); int rule_translate(struct sip_msg *msg, str , dpl_node_t * rule, str *); int test_match(str string, pcre * exp, int * out, int out_max); typedef void * (*func_malloc)(size_t ); typedef void (*func_free)(void * ); void * wrap_shm_malloc(size_t size); void wrap_shm_free(void *); pcre * wrap_pcre_compile(char * pattern, int flags); void wrap_pcre_free( pcre*); extern rw_lock_t *ref_lock; #endif opensips-2.2.2/modules/dialplan/doc/000077500000000000000000000000001300170765700173645ustar00rootroot00000000000000opensips-2.2.2/modules/dialplan/doc/dialplan.xml000066400000000000000000000016751300170765700217030ustar00rootroot00000000000000 %docentities; ]> dialplan Module &osipsname; Andreea-Ancuta Onofrei Andreea-Ancuta Onofrei 2007-2008 &voicesystem; $Revision: 5895 $ $Date$ &admin; &devel; opensips-2.2.2/modules/dialplan/doc/dialplan_admin.xml000066400000000000000000000540441300170765700230510ustar00rootroot00000000000000 &adminguide;
Overview This module implements generic string translations based on matching and replacement rules. It can be used to manipulate R-URI or a PV and to translated to a new format/value.
How it works At startup, the module will load all transformation rules from one or more dialplan-compatible tables. The data of each table will be stored in a partition which is defined by the "db_url" and "table_name" parameters. Every table row will be stored in memory as a translation rule. Each rule will describe how the matching should be made, how the input value should be modified and which attributes should be set for the matching transformation. A dialplan rule can be of two types: "String matching" rule - performs a string equality test against the input string. The case of the characters can be ignored by enabling bit 1 of the rule's "match_flags" bitmask column (i.e. set the column value to 1 or 0, for insensitive or sensitive) "Regex matching" rule - uses Perl Compatible Regular Expressions, and will attempt to match the rule's expression against an input string. The regex maching can be done in a caseless manner by enabling bit 1 of the rule's "match_flags" bitmask column (i.e. set the column value to 1 or 0, for insensitive or sensitive) The module provides the dp_translate() script function, which expects an input string value that will be matched, at worst, against all rules of a partition. Internally, the module groups a partition's rules into two sets, "string" and "regex". The matching logic will attempt to find the first match within each of these two sets of rules. Each set will be iterated in ascending order of priority. If an input string happens to match a rule in each of the two sets, the rule with the smallest priority will be chosen. Furthermore, should these two matching rules also have equal priorities, the one with the smallest "id" field (the unique key) will be chosen. Once a single rule is decided upon, the defined transformation (if any) is applied and the result is returned as output value. Also, if any string attribute is associated to the rule, this will be returned to the script along with the output value.
Usage cases The module can be used to implement dialplans - to do auto completion of the dialed numbers (e.g. national to international), to convert generic numbers to specific numbers (e.g. for emergency numbers). Also the module can be used for detecting ranges or sets of numbers mapped on a service/case - the "attributes" string column can be used here to store extra information about the service/case. Non-SIP string translation can also be implemented - like converting country names from all possible formats to a canonical format: (UK, England, United Kingdom) -> GB. Any other string-based translation or detection for whatever other purposes.
Database structure and usage Depending what kind of operation (translation, matching, etc) you want to do with the module, you need to populate the appropriate DB records. The definition of the tables used by the dialplan module can be found at &osipsdbdocslink;#AEN1501
What to place in table
String translation (regexp detection, subst translation) Recognize a number block in all forms (international, national) and convert it to a canonical format (E.164) match_op = 1 (regexp) match_exp = "^(0040|\+40|0|40)21[0-9]+" ; regular expression that will be used to match with this rule (if the rule should be applied for the input string) match_flags = 0 (0 - case sensitive, 1 - case insensitive matching) subst_exp = "^(0040|\+40|0|40)(.+)" ; regular expression used to do the transformation (first part of the subst operation) repl_exp = "40\2" ; second part of the subst (output) - linked to the subst_exp field; when both defined, they work as a subst()
String translation (regexp detection, replacement) Recognize the name of a country (multiple languages) and convert it to a single, fixed value match_op = 1 (regexp) match_exp = "^((Germany)|(Germania)|(Deutschland)|(DE))" ; regular expression that will be used to match with this rule (if the rule should be applied for the input string) match_flags = 0 (0 - case sensitive, 1 - case insensitive matching) subst_exp = NULL ; when translation is actually a replacement, this field must be NULL. repl_exp = "DE" ; static string to replace the input - whenever this rule will match, it will return this string as output.
Number detection (regexp detection, no replacement) Recognize a block of numbers as belong to a single service and signalize this via an attribute. match_op = 1 (regexp) match_exp = "^021456[0-9]{5}" ; regular expression that will be used to match with this rule (if the rule should be applied for the input string) match_flags = 0 (0 - case sensitive, 1 - case insensitive matching) subst_exp = NULL ; no translation repl_exp = NULL ; no translation attrs = "serviceX" ; whatever string you will get into OpenSIPS script and it will provide you more information (totally custom)
String conversion (equal detection, replacement) Recognize a fixed string/number and replace it with something fixed. match_op = 0 (equal) match_exp = "SIP server" ; string to be matched match_flags = 0 (0 - case sensitive, 1 - case insensitive matching) subst_exp = NULL ; no subst translation repl_exp = "OpenSIPS" ; output string
Dependencies
&osips; Modules The following modules must be loaded before this module: None
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libpcre-dev - the development libraries of PCRE.
Exported Parameters
<varname>partition</varname> (string) This can be used to define new db_url and table_name parameters from which to load the translation rules. These parameters will be held in partitions. The db_url parameter is mandatory. The order of the parameters does not matter. Multiple partitions can be defined and you can also define the default partition here. The name of this partition is "default". In order to be able to use a table from a partition, its name must be found in the "version" table belonging to the database defined in the partition's db_url. Set <varname>partition</varname> parameter ... modparam("dialplan", "partition", "part2:table_name=dialplan;db_url=mysql://user:pw@localhost/db;") ... Set <varname>default partition</varname> with partition parameter ... modparam("dialplan", "partition", "default:table_name=dialplan;db_url=mysql://user:pw@localhost/db;") ...
<varname>db_url</varname> (string) The translation rules will be loaded using this database url.This will be the db_url parameter value for the default partition. NOTE: if you intend to use the default partition you have to explicity set this default db_url, otherwise OpenSIPS will not start (he value of global default db_url is not inherited here! ). Set <varname>db_url</varname> parameter ... modparam("dialplan", "db_url", "mysql://user:passwb@localhost/db") ...
<varname>table_name</varname> (string) The table's name from which to load the translation rules.This will be the table_name parameter value for the default partition. The db_url must be defined for the default partition in order to be able to define it's table name. Default value is dialplan. Set <varname>table_name</varname> parameter ... modparam("dialplan", "table_name", "my_table") ...
<varname>dpid_col</varname> (string) The column name to store the dialplan ID group. Default value is dpid. Set <varname>dpid_col</varname> parameter ... modparam("dialplan", "dpid_col", "column_name") ...
<varname>pr_col</varname> (string) The column name to store the priority of the corresponding rule from the table row. Smaller priority values have higher precedence. Default value is pr. Set <varname>pr_col</varname> parameter ... modparam("dialplan", "pr_col", "column_name") ...
<varname>match_op_col</varname> (string) The column name to store the type of matching of the rule. Default value is match_op. Set <varname>match_op_col</varname> parameter ... modparam("dialplan", "match_op_col", "column_name") ...
<varname>match_exp_col</varname> (string) The column name to store the rule match expression. Default value is match_exp. Set <varname>match_exp_col</varname> parameter ... modparam("dialplan", "match_exp_col", "column_name") ...
<varname>match_flags_col</varname> (string) The column name to store various matching flags. Currently 0 - case sensitive matching, 1 - case insensitive matching. Default value is match_flags. Set <varname>match_flags_col</varname> parameter ... modparam("dialplan", "match_flags_col", "column_name") ...
<varname>subst_exp_col</varname> (string) The column name to store the rule's substitution expression. Default value is subst_exp. Set <varname>subs_exp_col</varname> parameter ... modparam("dialplan", "subst_exp_col", "column_name") ...
<varname>repl_exp_col</varname> (string) The column name to store the rule's replacement expression. Default value is repl_exp. Set <varname>repl_exp_col</varname> parameter ... modparam("dialplan", "repl_exp_col", "column_name") ...
<varname>timerec_col</varname> (integer) The column name that indicates an additional time recurrence check within the rule. (column values are RFC 2445-compatible strings) Default value is timerec. Set <varname>timerec_col</varname> parameter ... modparam("dialplan", "timerec_col", "month_match") ...
<varname>disabled_col</varname> (integer) The column name that indicates if the dialplan rule is disabled. Default value is disabled. Set <varname>disabled_col</varname> parameter ... modparam("dialplan", "disabled_col", "disabled_column") ...
<varname>attrs_col</varname> (string) The column name to store rule-specific attributes. Default value is attrs. Set <varname>attrs_col</varname> parameter ... modparam("dialplan", "attrs_col", "column_name") ...
Exported Functions
<function moreinfo="none">dp_translate([partition:]id, src/dest[, attrs_pvar])</function> Will try to translate the src string into dest string according to the translation rules with dialplan ID equal to id. Meaning of the parameters is as follows: id - the dialplan id of possible matching rules. The id parameter can have the following types: integer - the dialplan id is statically assigned pvar - the dialplan id is the value of an existing pseudo-variable (as integer value) partition - Specifies the partition where the search will take place. This parameter can be ommited. If the paramater is omitted the default partition will be used if exists. The partition parameter can have the following types: string - the table name is statically assigned pvar - the partition name is the value of an existing pseudo-variable (as string value) src/dest - input and output of the function. If this parameter is missing the default parameter ruri.user/ruri.user will be used, thus translating the request uri. The src variable can be any type of pseudo-variable. The dest variable can be also any type of pseudo-variable, but it must be a writable one. attrs_pvar (output, optional) - a pseudo-variable which will hold the attributes of the matched translation rule. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE. <function>dp_translate</function> usage ... dp_translate("240", "$ruri.user/$avp(dest)"); xlog("translated to var $avp(dest) \n"); ... <function>dp_translate</function> usage ... $avp(src) = $ruri.user; dp_translate("$var(x)", "$avp(src)/$var(y)", "$var(attrs)"); xlog("translated to var $var(y) with attributes: '$var(attrs)'\n"); ... <function>dp_translate</function> usage ... $avp(src) = $uri.user; dp_translate("example_partition:$var(x)", "$avp(src)/$var(y)", "$avp(attrs)"); xlog("translated to var $var(y) with attributes: '$avp(attrs)'\n"); ... <function>dp_translate</function> usage ... $var(partition) = "default"; $var(id) = 10; dp_translate("$var(partition):$var(id)", "$avp(src)/$var(y)"); xlog("translate to var $var(y) partition $var(partition)") ...
Exported MI Functions
<varname>dp_reload</varname> It will update the translation rules, loading the database info. Name: dp_reload Parameters: 1 table_name - Partition to be reloaded. If no table is specified, the table specified in the "partition" parameter (default "default") will be reloaded. MI DATAGRAM Command Format: :dp_reload: _empty_line_
<varname>dp_translate</varname> It will apply a translation rule identified by a dialplan id and an input string. Name: dp_translate Parameters: 2 [Partition Name:]Dialplan ID - The dpid of the rules used to match the input string. The table name can be ommited. The default table is dialplan. Input String MI DATAGRAM Command Format: :dp_translate: dpid input _empty_line_
<varname>dp_show_partiton</varname> Display partition(s) details. Name: dp_show_partiton Parameters: 2 Partition Name - The partition name. If no partition is specified, all known partitions will be listed. MI DATAGRAM Command Format: :dp_translate: default _empty_line_
Installation The modules requires one table in OpenSIPS database: dialplan.The SQL syntax to create them can be found in dialplan-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, &osipsdbdocslink;.
opensips-2.2.2/modules/dialplan/doc/dialplan_devel.xml000066400000000000000000000005341300170765700230530ustar00rootroot00000000000000 $Revision: 5895 $ $Date$ Developer's Guide The module does not provide any API to use in other &osips; modules. opensips-2.2.2/modules/dialplan/dp_db.c000066400000000000000000000470721300170765700200450ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-08-01 initial version (ancuta onofrei) */ #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../time_rec.h" #include "dp_db.h" dp_head_p dp_hlist = NULL; str default_dp_db_url = {NULL, 0}; str default_dp_table = {NULL, 0}; str dp_table_name = str_init(DP_TABLE_NAME); str dpid_column = str_init(DPID_COL); str pr_column = str_init(PR_COL); str match_op_column = str_init(MATCH_OP_COL); str match_exp_column = str_init(MATCH_EXP_COL); str match_flags_column = str_init(MATCH_FLAGS_COL); str subst_exp_column = str_init(SUBST_EXP_COL); str repl_exp_column = str_init(REPL_EXP_COL); str disabled_column = str_init(DISABLED_COL); str attrs_column = str_init(ATTRS_COL); str timerec_column = str_init(TIMEREC_COL); #define GET_STR_VALUE(_res, _values, _index, _null)\ do{\ if ( VAL_NULL((_values)+ (_index))) { \ if ( !_null) { \ LM_ERR(" values %d is NULL - not allowed\n",_index);\ goto err;\ } else { \ (_res).s = NULL; \ (_res).len = 0; \ } \ } else { \ (_res).s = VAL_STR((_values)+ (_index)).s;\ (_res).len = strlen(VAL_STR((_values)+ (_index)).s);\ }\ }while(0); void destroy_rule(dpl_node_t * rule); void destroy_hash(dpl_id_t **rules_hash); dpl_node_t * build_rule(db_val_t * values); int add_rule2hash(dpl_node_t * rule, dp_connection_list_t *table, int index); void list_rule(dpl_node_t * ); void list_hash(dpl_id_t * , rw_lock_t *); dp_connection_list_p dp_conns = NULL; int test_db(dp_connection_list_p dp_connection){ if (dp_connection->partition.s == 0) { LM_ERR("invalid partition name\n"); return -1; } if (db_bind_mod(&dp_connection->db_url, &dp_connection->dp_dbf) < 0){ LM_ERR("unable to bind to a database driver\n"); return -1; } if (dp_connect_db(dp_connection) !=0) return -1; if (db_check_table_version(&dp_connection->dp_dbf, *dp_connection->dp_db_handle, &dp_connection->table_name, DP_TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); goto error; } dp_disconnect_db(dp_connection); return 0; error: dp_disconnect_db(dp_connection); return -1; } int init_db_data(dp_connection_list_p dp_connection) { if (dp_connection->partition.s == 0) { LM_ERR("invalid partition name\n"); return -1; } if (dp_connect_db(dp_connection) !=0) return -1; if (db_check_table_version(&dp_connection->dp_dbf, *dp_connection->dp_db_handle, &dp_connection->table_name, DP_TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); goto error; } if(dp_load_db(dp_connection) != 0){ LM_ERR("failed to load database data\n"); goto error; } return 0; error: dp_disconnect_db(dp_connection); return -1; } int dp_connect_db(dp_connection_list_p conn) { if (*conn->dp_db_handle) { LM_CRIT("BUG: connection to DB already open\n"); return -1; } if ((*conn->dp_db_handle = conn->dp_dbf.init(&conn->db_url)) == 0) { LM_ERR("unable to connect to the database\n"); return -1; } return 0; } void dp_disconnect_db(dp_connection_list_p dp_conn) { if (*dp_conn->dp_db_handle) { dp_conn->dp_dbf.close(*dp_conn->dp_db_handle); *dp_conn->dp_db_handle = 0; } } int init_data(void) { dp_head_p start, tmp = NULL; start = dp_hlist; if (!start) { LM_BUG("not even default partition defined!" "An error occured!\n"); return -1; } while (start) { LM_DBG("Adding partition with name [%.*s]\n", start->partition.len, start->partition.s); if (!dp_add_connection(start)) { LM_ERR("failed to initialize partition '%.*s'\n", start->partition.len, start->partition.s); return -1; } tmp = start; start = start->next; pkg_free(tmp); } return 0; } void destroy_data(void) { dp_connection_list_t *el, *next; LM_DBG("Destroying data\n"); for (el = dp_conns; el && (next = el->next, 1); el = next) { destroy_hash(&el->hash[0]); destroy_hash(&el->hash[1]); lock_destroy_rw(el->ref_lock); shm_free(el); } } int dp_load_all_db(void) { dp_connection_list_t *el; for (el = dp_conns; el; el = el->next) { if (dp_load_db(el) < 0) { LM_ERR("unable to load %.*s table\n", el->table_name.len, el->table_name.s); return -1; } } return 0; } void dp_disconnect_all_db(void) { dp_connection_list_t *el; for (el = dp_conns; el; el = el->next) dp_disconnect_db(el); } /*load rules from DB*/ int dp_load_db(dp_connection_list_p dp_conn) { int i, nr_rows; db_res_t * res = 0; db_val_t * values; db_row_t * rows; db_key_t query_cols[DP_TABLE_COL_NO] = { &dpid_column, &pr_column, &match_op_column, &match_exp_column, &match_flags_column, &subst_exp_column, &repl_exp_column, &attrs_column, &timerec_column }; db_key_t order = &pr_column; /* disabled condition */ db_key_t cond_cols[1] = { &disabled_column }; db_val_t cond_val[1]; dpl_node_t *rule; int no_rows = 10; lock_start_write( dp_conn->ref_lock ); if( dp_conn->crt_index != dp_conn->next_index){ LM_WARN("a load command already generated, aborting reload...\n"); lock_stop_write( dp_conn->ref_lock ); return 0; } dp_conn->next_index = dp_conn->crt_index == 0 ? 1 : 0; lock_stop_write( dp_conn->ref_lock ); if (dp_conn->dp_dbf.use_table(*dp_conn->dp_db_handle, &dp_conn->table_name) < 0){ LM_ERR("error in use_table\n"); goto err1; } VAL_TYPE(cond_val) = DB_INT; VAL_NULL(cond_val) = 0; VAL_INT(cond_val) = 0; if (DB_CAPABILITY(dp_conn->dp_dbf, DB_CAP_FETCH)) { if(dp_conn->dp_dbf.query(*dp_conn->dp_db_handle,cond_cols, 0,cond_val,query_cols,1, DP_TABLE_COL_NO, order, 0) < 0){ LM_ERR("failed to query database!\n"); goto err1; } no_rows = estimate_available_rows( 4+4+4+64+4+64+64+128, DP_TABLE_COL_NO); if (no_rows==0) no_rows = 10; if(dp_conn->dp_dbf.fetch_result(*dp_conn->dp_db_handle, &res, no_rows)<0) { LM_ERR("failed to fetch\n"); if (res) dp_conn->dp_dbf.free_result(*dp_conn->dp_db_handle, res); goto err1; } } else { /*select the whole table and all the columns*/ if(dp_conn->dp_dbf.query(*dp_conn->dp_db_handle, cond_cols,0,cond_val,query_cols,1, DP_TABLE_COL_NO, order, &res) < 0){ LM_ERR("failed to query database\n"); goto err1; } } nr_rows = RES_ROW_N(res); if(nr_rows == 0){ LM_WARN("no data in the db\n"); goto end; } do { for(i=0; i skipping\n"); continue; } rule->table_id = i; if(add_rule2hash(rule , dp_conn, dp_conn->next_index) != 0) { LM_ERR("add_rule2hash failed\n"); goto err2; } } if (DB_CAPABILITY(dp_conn->dp_dbf, DB_CAP_FETCH)) { if(dp_conn->dp_dbf.fetch_result(*dp_conn->dp_db_handle, &res, no_rows)<0) { LM_ERR("failure while fetching!\n"); if (res) dp_conn->dp_dbf.free_result(*dp_conn->dp_db_handle, res); goto err1; } } else { break; } } while(RES_ROW_N(res)>0); end: /*update data*/ lock_start_write( dp_conn->ref_lock ); destroy_hash(&dp_conn->hash[dp_conn->crt_index]); dp_conn->crt_index = dp_conn->next_index; lock_stop_write( dp_conn->ref_lock ); list_hash(dp_conn->hash[dp_conn->crt_index], dp_conn->ref_lock); dp_conn->dp_dbf.free_result(*dp_conn->dp_db_handle, res); return 0; err1: lock_start_write( dp_conn->ref_lock ); dp_conn->next_index = dp_conn->crt_index; lock_stop_write( dp_conn->ref_lock ); return -1; err2: if(rule) destroy_rule(rule); destroy_hash(&dp_conn->hash[dp_conn->next_index]); dp_conn->dp_dbf.free_result(*dp_conn->dp_db_handle, res); lock_start_write( dp_conn->ref_lock ); dp_conn->next_index = dp_conn->crt_index; /* if lock defined - release the exclusive writing access */ lock_stop_write( dp_conn->ref_lock ); return -1; } int str_to_shm(str src, str * dest) { if (src.len ==0 || src.s ==0) return 0; dest->s = (char*)shm_malloc((src.len+1) * sizeof(char)); if (!dest->s) { LM_ERR("out of shm memory\n"); return -1; } memcpy(dest->s, src.s, src.len); dest->s[src.len] = '\0'; dest->len = src.len; return 0; } static inline tmrec_t* parse_time_def(char *time_str) { tmrec_p time_rec; char *p,*s; p = time_str; time_rec = 0; time_rec = tmrec_new(SHM_ALLOC); if (time_rec==0) { LM_ERR("no more shm mem\n"); goto error; } /* empty definition? */ if ( time_str==0 || *time_str==0 ) goto done; load_TR_value( p, s, time_rec, tr_parse_dtstart, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_duration, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_freq, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_until, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_interval, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_byday, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_bymday, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_byyday, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_byweekno, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_bymonth, parse_error, done); /* success */ done: return time_rec; parse_error: LM_ERR("parse error in <%s> around position %i\n", time_str, (int)(long)(p-time_str)); error: if (time_rec) tmrec_free( time_rec ); return 0; } /*compile the expressions, and if ok, build the rule */ dpl_node_t * build_rule(db_val_t * values) { tmrec_t *parsed_timerec; pcre * match_comp, *subst_comp; struct subst_expr * repl_comp; dpl_node_t * new_rule; str match_exp, subst_exp, repl_exp, attrs, timerec; int matchop; int namecount; matchop = VAL_INT(values+2); if((matchop != REGEX_OP) && (matchop!=EQUAL_OP)){ LM_ERR("invalid value for match operator\n"); return NULL; } parsed_timerec = 0; match_comp = subst_comp = 0; repl_comp = 0; new_rule = 0; GET_STR_VALUE(match_exp, values, 3, 0); if(matchop == REGEX_OP){ LM_DBG("Compiling %.*s expression with flag: %d\n", match_exp.len, match_exp.s, VAL_INT(values+4)); match_comp = wrap_pcre_compile(match_exp.s, VAL_INT(values+4)); if(!match_comp){ LM_ERR("failed to compile match expression \"%.*s\"\n", match_exp.len, match_exp.s); goto err; } } LM_DBG("building subst rule\n"); GET_STR_VALUE(subst_exp, values, 5, 1); if(!VAL_NULL(values+5) && subst_exp.s && subst_exp.len){ /* subst regexp */ subst_comp = wrap_pcre_compile(subst_exp.s, VAL_INT(values+4)); if(subst_comp == NULL){ LM_ERR("failed to compile subst expression \"%.*s\"\n", subst_exp.len, subst_exp.s); goto err; } } /* replace exp */ GET_STR_VALUE(repl_exp, values, 6, 1); if(!VAL_NULL(values+6) && repl_exp.len && repl_exp.s){ repl_comp = repl_exp_parse(repl_exp); if(!repl_comp){ LM_ERR("failed to compile replacing expression \"%.*s\"\n", repl_exp.len, repl_exp.s); goto err; } } pcre_fullinfo( subst_comp, /* the compiled pattern */ NULL, /* no extra data - we didn't study the pattern */ PCRE_INFO_CAPTURECOUNT, /* number of named substrings */ &namecount); /* where to put the answer */ LM_DBG("references:%d , max:%d\n",namecount, repl_comp?repl_comp->max_pmatch:0); if ( (repl_comp!=NULL) && (namecountmax_pmatch) && (repl_comp->max_pmatch!=0) ){ LM_ERR("repl_exp uses a non existing subexpression\n"); goto err; } new_rule = (dpl_node_t *)shm_malloc(sizeof(dpl_node_t)); if(!new_rule){ LM_ERR("out of shm memory(new_rule)\n"); goto err; } memset(new_rule, 0, sizeof(dpl_node_t)); if(str_to_shm(match_exp, &new_rule->match_exp)!=0) goto err; if (subst_comp) if(str_to_shm(subst_exp, &new_rule->subst_exp)!=0) goto err; if (repl_comp) if(str_to_shm(repl_exp, &new_rule->repl_exp)!=0) goto err; /*set the rest of the rule fields*/ new_rule->dpid = VAL_INT(values); new_rule->pr = VAL_INT(values+1); new_rule->match_flags = VAL_INT(values+4); new_rule->matchop = matchop; /* attributes */ GET_STR_VALUE(attrs, values, 7, 1); if( !VAL_NULL(values+7) && attrs.len && attrs.s) { if(str_to_shm(attrs, &new_rule->attrs)!=0) goto err; LM_DBG("attrs are %.*s\n", new_rule->attrs.len, new_rule->attrs.s); } /* Retrieve and Parse Timerec Matching Pattern */ GET_STR_VALUE(timerec, values, 8, 1); if( !VAL_NULL(values+8) && timerec.len && timerec.s) { parsed_timerec = parse_time_def(timerec.s); if(!parsed_timerec) { LM_ERR("failed to parse timerec pattern %.*s\n", timerec.len, timerec.s); goto err; } if(str_to_shm(timerec, &new_rule->timerec) != 0) goto err; new_rule->parsed_timerec = parsed_timerec; LM_DBG("timerecs are %.*s\n", new_rule->timerec.len, new_rule->timerec.s); } if (match_comp) new_rule->match_comp = match_comp; if (subst_comp) new_rule->subst_comp = subst_comp; if (repl_comp) new_rule->repl_comp = repl_comp; return new_rule; err: if(parsed_timerec) shm_free(parsed_timerec); if(match_comp) wrap_pcre_free(match_comp); if(subst_comp) wrap_pcre_free(subst_comp); if(repl_comp) repl_expr_free(repl_comp); if(new_rule) destroy_rule(new_rule); return NULL; } int add_rule2hash(dpl_node_t * rule, dp_connection_list_t *conn, int index) { dpl_id_p crt_idp; dpl_index_p indexp; int new_id, bucket = 0; if(!conn){ LM_ERR("data not allocated\n"); return -1; } new_id = 0; crt_idp = select_dpid(conn, rule->dpid, index); /*didn't find a dpl_id*/ if(!crt_idp){ crt_idp = shm_malloc(sizeof(dpl_id_t) + (DP_INDEX_HASH_SIZE+1) * sizeof(dpl_index_t)); if(!crt_idp){ LM_ERR("out of shm memory (crt_idp)\n"); return -1; } memset(crt_idp, 0, sizeof(dpl_id_t) + (DP_INDEX_HASH_SIZE+1) * sizeof(dpl_index_t)); crt_idp->dp_id = rule->dpid; crt_idp->rule_hash = (dpl_index_t*)(crt_idp + 1); new_id = 1; LM_DBG("new dpl_id %i\n", rule->dpid); } switch (rule->matchop) { case REGEX_OP: indexp = &crt_idp->rule_hash[DP_INDEX_HASH_SIZE]; break; case EQUAL_OP: if (rule->match_exp.s == NULL || rule->match_exp.len == 0) { LM_ERR("NULL matching expressions in database not accepted!!!\n"); return -1; } bucket = core_case_hash(&rule->match_exp, NULL, DP_INDEX_HASH_SIZE); indexp = &crt_idp->rule_hash[bucket]; break; default: LM_ERR("SKIPPED RULE. Unsupported match operator (%d).\n", rule->matchop); goto err; } /* Add the new rule to the corresponding bucket */ rule->next = 0; if(!indexp->first_rule) indexp->first_rule = rule; if(indexp->last_rule) indexp->last_rule->next = rule; indexp->last_rule = rule; if(new_id){ crt_idp->next = conn->hash[conn->next_index]; conn->hash[conn->next_index] = crt_idp; } LM_DBG("added the rule id %i pr %i next %p to the " " %i bucket\n", rule->dpid, rule->pr, rule->next, rule->matchop == REGEX_OP ? DP_INDEX_HASH_SIZE : bucket); return 0; err: if(new_id) shm_free(crt_idp); return -1; } void destroy_hash(dpl_id_t **rules_hash) { dpl_id_p crt_idp; dpl_index_p indexp; dpl_node_p rulep; int i; if(!rules_hash || !*rules_hash) return; for(crt_idp = *rules_hash; crt_idp; crt_idp = *rules_hash) { for (i = 0, indexp = &crt_idp->rule_hash[i]; i <= DP_INDEX_HASH_SIZE; i++, indexp = &crt_idp->rule_hash[i]) { for (rulep = indexp->first_rule; rulep; rulep=indexp->first_rule) { destroy_rule(rulep); indexp->first_rule = rulep->next; shm_free(rulep); rulep = NULL; } } *rules_hash = crt_idp->next; shm_free(crt_idp); crt_idp = NULL; } *rules_hash = NULL; } void destroy_rule(dpl_node_t * rule){ if(!rule) return; LM_DBG("destroying rule with priority %i\n", rule->pr); if(rule->match_comp) wrap_pcre_free(rule->match_comp); if(rule->subst_comp) wrap_pcre_free(rule->subst_comp); /*destroy repl_exp*/ if(rule->repl_comp) repl_expr_free(rule->repl_comp); if(rule->match_exp.s) shm_free(rule->match_exp.s); if(rule->subst_exp.s) shm_free(rule->subst_exp.s); if(rule->repl_exp.s) shm_free(rule->repl_exp.s); if(rule->attrs.s) shm_free(rule->attrs.s); if(rule->timerec.s) shm_free(rule->timerec.s); if(rule->parsed_timerec) shm_free(rule->parsed_timerec); } dpl_id_p select_dpid(dp_connection_list_p conn, int id, int index) { dpl_id_p idp; if(!conn || !conn->hash[index]) return NULL; for(idp = conn->hash[index]; idp!=NULL; idp = idp->next) if(idp->dp_id == id) return idp; return NULL; } /* FOR DEBUG PURPOSES */ void list_hash(dpl_id_t * hash, rw_lock_t * ref_lock) { dpl_id_p crt_idp; dpl_node_p rulep; int i; if(!hash) return; /* lock the data for reading */ lock_start_read( ref_lock ); for(crt_idp = hash; crt_idp; crt_idp = crt_idp->next) { LM_DBG("DPID: %i, pointer %p\n", crt_idp->dp_id, crt_idp); for (i = 0; i <= DP_INDEX_HASH_SIZE; i++) { LM_DBG("BUCKET %d rules:\n", i); for(rulep = crt_idp->rule_hash[i].first_rule; rulep; rulep = rulep->next) { list_rule(rulep); } } } /* we are done reading -> unref the data */ lock_stop_read( ref_lock ); } void list_rule(dpl_node_t * rule) { LM_DBG("RULE %p: pr %i next %p match_exp %.*s match_flags %d, " "subst_exp %.*s, repl_exp %.*s and attrs %.*s and timerec %.*s\n", rule, rule->pr, rule->next, rule->match_exp.len, rule->match_exp.s, rule->match_flags, rule->subst_exp.len, rule->subst_exp.s, rule->repl_exp.len, rule->repl_exp.s, rule->attrs.len, rule->attrs.s, rule->timerec.len, rule->timerec.s); } /* Retrieves the corresponding entry of the given partition name */ dp_connection_list_p dp_get_connection(str * partition) { dp_connection_list_t *el; el = dp_conns; while (el && str_strcmp(partition, &el->partition)) { el = el->next; } return el; } dp_connection_list_p dp_get_connections(void) { return dp_conns; } /* Adds a new separate partition and loads all rules from database in shm */ dp_connection_list_p dp_add_connection(dp_head_p head) { dp_connection_list_t *el; if ((el = dp_get_connection(&head->partition)) != NULL){ return el; } el = shm_malloc(sizeof(dp_connection_list_t)); if (!el) { LM_ERR("No more shm mem\n"); return NULL; } memset(el, 0, sizeof(dp_connection_list_t)); /* create & init lock */ if((el->ref_lock = lock_init_rw()) == NULL) { LM_ERR("Failed to init lock\n"); shm_free(el); return NULL; } /*Set table name*/ el->table_name = head->dp_table_name; /*Set partition*/ el->partition = head->partition; /*Set db_url*/ el->db_url = head->dp_db_url; el->dp_db_handle = pkg_malloc(sizeof(db_con_t*)); if (!el->dp_db_handle) { LM_ERR("No more shm mem\n"); return NULL; } *el->dp_db_handle = 0; /* *el->dp_db_handle is set to null at the end of test_db; * no need to do it again here */ if (test_db(el) != 0) { LM_ERR("Unable to test db\n"); shm_free(el); return NULL; } el->next = dp_conns; dp_conns = el; LM_DBG("Added dialplan partition [%.*s] table [%.*s].\n", head->partition.len, head->partition.s, head->dp_table_name.len, head->dp_table_name.s); return el; } opensips-2.2.2/modules/dialplan/dp_db.h000066400000000000000000000046461300170765700200520ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-08-01 initial version (ancuta onofrei) */ #ifndef _DP_DB_H_ #define _DP_DB_H_ #include "../../str.h" #include "../../db/db.h" #include "dialplan.h" #define DP_PARTITION "default" #define DP_TABLE_NAME "dialplan" #define DPID_COL "dpid" #define PR_COL "pr" #define MATCH_OP_COL "match_op" #define MATCH_EXP_COL "match_exp" #define MATCH_FLAGS_COL "match_flags" #define SUBST_EXP_COL "subst_exp" #define REPL_EXP_COL "repl_exp" #define DISABLED_COL "disabled" #define ATTRS_COL "attrs" #define TIMEREC_COL "timerec" #define DP_TABLE_VERSION 5 #define DP_TABLE_COL_NO 9 typedef struct dp_head{ str partition;/*Attribute that uniquely identifies head*/ str dp_db_url; str dp_table_name; struct dp_head* next; } dp_head_t, *dp_head_p; extern dp_head_p dp_hlist; extern dp_connection_list_p dp_conns; extern str default_dp_db_url; extern str default_dp_table; extern str dp_table_name; extern str dpid_column; extern str pr_column; extern str match_op_column; extern str match_exp_column; extern str match_flags_column; extern str subst_exp_column; extern str repl_exp_column; extern str attrs_column; extern str timerec_column; extern str disabled_column; struct dp_param_list; int init_db_data(); //int dp_connect_db(dp_connection_list_p conn, dp_head_p head); struct dp_connection_list * dp_add_connection(dp_head_p head ); struct dp_connection_list * dp_get_connections(void); struct dp_connection_list * dp_get_connection(str * partition); struct dp_connection_list * dp_get_default_connection(); int dp_connect_db(dp_connection_list_p conn); void dp_disconnect_db(); #endif opensips-2.2.2/modules/dialplan/dp_repl.c000066400000000000000000000330451300170765700204150ustar00rootroot00000000000000/* * Copyright (C) 2007-2008 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-08-01 initial version (ancuta onofrei) */ #include "../../re.h" #include "../../time_rec.h" #include "dialplan.h" #define MAX_REPLACE_WITH 10 void repl_expr_free(struct subst_expr *se) { if(!se) return; if(se->replacement.s){ shm_free(se->replacement.s); se->replacement.s = 0; } shm_free(se); se = 0; } struct subst_expr* repl_exp_parse(str subst) { struct replace_with rw[MAX_REPLACE_WITH]; int rw_no; struct subst_expr * se; int replace_all; char * p, *end, *repl, *repl_end; int max_pmatch, r; se = 0; replace_all = 0; p = subst.s; end = p + subst.len; rw_no = 0; repl = p; if((rw_no = parse_repl(rw, &p, end, &max_pmatch, WITHOUT_SEP))< 0) goto error; repl_end=p; /* construct the subst_expr structure */ se = shm_malloc(sizeof(struct subst_expr)+ ((rw_no)?(rw_no-1)*sizeof(struct replace_with):0)); /* 1 replace_with structure is already included in subst_expr */ if (se==0){ LM_ERR("out of shm memory (subst_expr)\n"); goto error; } memset((void*)se, 0, sizeof(struct subst_expr)); se->replacement.len=repl_end-repl; if (!(se->replacement.s=shm_malloc(se->replacement.len * sizeof(char))) ){ LM_ERR("out of shm memory \n"); goto error; } if(!rw_no){ replace_all = 1; } /* start copying */ memcpy(se->replacement.s, repl, se->replacement.len); se->re=0; se->replace_all=replace_all; se->n_escapes=rw_no; se->max_pmatch=max_pmatch; /*replace_with is a simple structure, no shm alloc needed*/ for (r=0; rreplace[r]=rw[r]; return se; error: if (se) { repl_expr_free(se);} return NULL; } #define MAX_PHONE_NB_DIGITS 127 #define MAX_MATCHES (100 * 3) static char dp_output_buf[MAX_PHONE_NB_DIGITS+1]; static int matches[MAX_MATCHES]; int rule_translate(struct sip_msg *msg, str string, dpl_node_t * rule, str * result) { int repl_nb, offset, match_nb; struct replace_with token; pcre * subst_comp; struct subst_expr * repl_comp; pv_value_t sv; str* uri; int capturecount; char *match_begin; int match_len; dp_output_buf[0] = '\0'; result->s = dp_output_buf; result->len = 0; subst_comp = rule->subst_comp; repl_comp = rule->repl_comp; if(!repl_comp){ LM_DBG("null replacement\n"); return 0; } if(subst_comp){ pcre_fullinfo( subst_comp, /* the compiled pattern */ NULL, /* no extra data - we didn't study the pattern */ PCRE_INFO_CAPTURECOUNT , /* number of named substrings */ &capturecount); /* where to put the answer */ /*just in case something went wrong at load time*/ if(repl_comp->max_pmatch > capturecount){ LM_ERR("illegal access to the " "%i-th subexpr of the subst expr\n", repl_comp->max_pmatch); return -1; } /*search for the pattern from the compiled subst_exp*/ if(test_match(string, rule->subst_comp,matches,MAX_MATCHES) <= 0){ LM_ERR("the string %.*s " "matched the match_exp %.*s but not the subst_exp %.*s!\n", string.len, string.s, rule->match_exp.len, rule->match_exp.s, rule->subst_exp.len, rule->subst_exp.s); return -1; } } /*simply copy from the replacing string*/ if(!subst_comp || (repl_comp->n_escapes <=0)){ if(!repl_comp->replacement.s || repl_comp->replacement.len == 0){ LM_ERR("invalid replacing string\n"); goto error; } LM_DBG("simply replace the string, " "subst_comp %p, n_escapes %i\n",subst_comp, repl_comp->n_escapes); memcpy(result->s, repl_comp->replacement.s, repl_comp->replacement.len); result->len = repl_comp->replacement.len; result->s[result->len] = '\0'; return 0; } /* offset- offset in the replacement string */ result->len = repl_nb = offset = 0; while( repl_nb < repl_comp->n_escapes){ token = repl_comp->replace[repl_nb]; if(offset< token.offset){ if((repl_comp->replacement.len < offset)|| (result->len + token.offset -offset >= MAX_PHONE_NB_DIGITS)){ LM_ERR("invalid length\n"); goto error; } /*copy from the replacing string*/ memcpy(result->s + result->len, repl_comp->replacement.s + offset, token.offset-offset); result->len += (token.offset - offset); offset += token.offset-offset; /*update the offset*/ } switch(token.type) { case REPLACE_NMATCH: /*copy from the match subexpression*/ match_nb = token.u.nmatch; match_begin = string.s + matches[2*match_nb]; match_len = matches[2*match_nb+1] - matches[2*match_nb]; if(result->len + match_len >= MAX_PHONE_NB_DIGITS){ LM_ERR("overflow\n"); goto error; } memcpy(result->s + result->len, match_begin, match_len); result->len += match_len; offset += token.size; /*update the offset*/ break; case REPLACE_CHAR: if(result->len + 1>= MAX_PHONE_NB_DIGITS){ LM_ERR("overflow\n"); goto error; } *result->s=repl_comp->replace[repl_nb].u.c; result->len++; break; case REPLACE_URI: if ( msg== NULL || msg->first_line.type!=SIP_REQUEST){ LM_CRIT("uri substitution attempt on no request" " message\n"); break; /* ignore, we can continue */ } uri= (msg->new_uri.s)?(&msg->new_uri): (&msg->first_line.u.request.uri); if(result->len+uri->len>=MAX_PHONE_NB_DIGITS){ LM_ERR("overflow\n"); goto error; } memcpy(result->s + result->len, uri->s, uri->len); result->len+=uri->len; break; case REPLACE_SPEC: if (msg== NULL) { LM_DBG("replace spec attempted on no message\n"); break; } if(pv_get_spec_value(msg, &repl_comp->replace[repl_nb].u.spec, &sv)!=0){ LM_CRIT( "item substitution returned error\n"); break; /* ignore, we can continue */ } if(result->len+sv.rs.len>=MAX_PHONE_NB_DIGITS){ LM_ERR("ERROR:dialplan: rule_translate: overflow\n"); goto error; } memcpy(result->s + result->len, sv.rs.s, sv.rs.len); result->len+=sv.rs.len; break; default: LM_CRIT("BUG: unknown type %d\n", repl_comp->replace[repl_nb].type); /* ignore it */ } repl_nb++; } /* anything left? */ if( repl_nb && token.offset+token.size < repl_comp->replacement.len){ /*copy from the replacing string*/ memcpy(result->s + result->len, repl_comp->replacement.s + token.offset+token.size, repl_comp->replacement.len -(token.offset+token.size) ); result->len +=repl_comp->replacement.len-(token.offset+token.size); } result->s[result->len] = '\0'; return 0; error: result->s = 0; result->len = 0; return -1; } int timerec_print(tmrec_p _trp) { static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; int i; UNUSED(_wdays); if(!_trp) { LM_DBG("\n(null)\n"); return -1; } LM_DBG("Recurrence definition\n-- start time ---\n"); LM_DBG("Sys time: %d\n", (int)_trp->dtstart); LM_DBG("Time: %02d:%02d:%02d\n", _trp->ts.tm_hour, _trp->ts.tm_min, _trp->ts.tm_sec); LM_DBG("Date: %s, %04d-%02d-%02d\n", _wdays[_trp->ts.tm_wday], _trp->ts.tm_year+1900, _trp->ts.tm_mon+1, _trp->ts.tm_mday); LM_DBG("---\n"); LM_DBG("End time: %d\n", (int)_trp->dtend); LM_DBG("Duration: %d\n", (int)_trp->duration); LM_DBG("Until: %d\n", (int)_trp->until); LM_DBG("Freq: %d\n", (int)_trp->freq); LM_DBG("Interval: %d\n", (int)_trp->interval); if(_trp->byday) { LM_DBG("Byday: "); for(i=0; i<_trp->byday->nr; i++) LM_DBG(" %d%s", _trp->byday->req[i], _wdays[_trp->byday->xxx[i]]); LM_DBG("\n"); } if(_trp->bymday) { LM_DBG("Bymday: %d:", _trp->bymday->nr); for(i=0; i<_trp->bymday->nr; i++) LM_DBG(" %d", _trp->bymday->xxx[i]*_trp->bymday->req[i]); LM_DBG("\n"); } if(_trp->byyday) { LM_DBG("Byyday:"); for(i=0; i<_trp->byyday->nr; i++) LM_DBG(" %d", _trp->byyday->xxx[i]*_trp->byyday->req[i]); LM_DBG("\n"); } if(_trp->bymonth) { LM_DBG("Bymonth: %d:", _trp->bymonth->nr); for(i=0; i< _trp->bymonth->nr; i++) LM_DBG(" %d", _trp->bymonth->xxx[i]*_trp->bymonth->req[i]); LM_DBG("\n"); } if(_trp->byweekno) { LM_DBG("Byweekno: "); for(i=0; i<_trp->byweekno->nr; i++) LM_DBG(" %d", _trp->byweekno->xxx[i]*_trp->byweekno->req[i]); LM_DBG("\n"); } LM_DBG("Weekstart: %d\n", _trp->wkst); return 0; } // Validate Passed Time Recurrence Instance static inline int check_time(tmrec_t *time_rec) { ac_tm_t att; // No TimeRec: Rule is Valid if(time_rec->dtstart == 0) return 1; // Uncomment to enable Debug // timerec_print(time_rec); // Set Current Time memset(&att, 0, sizeof(att)); if(ac_tm_set_time(&att, time(0))) return -1; // Check_Tmrec will return 0 on successfully time recurrence match if(check_tmrec(time_rec, &att, 0) != 0) return 0; // Recurrence Matched -- Validating Rule return 1; } #define DP_MAX_ATTRS_LEN 32 static char dp_attrs_buf[DP_MAX_ATTRS_LEN+1]; int translate(struct sip_msg *msg, str input, str * output, dpl_id_p idp, str * attrs) { dpl_node_p rulep, rrulep; int string_res = -1, regexp_res = -1, bucket; if(!input.s || !input.len) { LM_ERR("invalid input string\n"); return -1; } bucket = core_case_hash(&input, NULL, DP_INDEX_HASH_SIZE); /* try to match the input in the corresponding string bucket */ for (rulep = idp->rule_hash[bucket].first_rule; rulep; rulep=rulep->next) { LM_DBG("Equal operator testing\n"); if(rulep->match_exp.len != input.len) continue; LM_DBG("Comparing (input %.*s) with (rule %.*s) [%d] and timerec %.*s\n", input.len, input.s, rulep->match_exp.len, rulep->match_exp.s, rulep->match_flags, rulep->timerec.len, rulep->timerec.s); // Check for Time Period if Set if(rulep->parsed_timerec) { LM_DBG("Timerec exists for rule checking: %.*s\n", rulep->timerec.len, rulep->timerec.s); // Doesn't matches time period continue with next rule if(!check_time(rulep->parsed_timerec)) { LM_DBG("Time rule doesn't match: skip next!\n"); continue; } } if (rulep->match_flags & DP_CASE_INSENSITIVE) { string_res = strncasecmp(rulep->match_exp.s,input.s,input.len); } else { string_res = strncmp(rulep->match_exp.s,input.s,input.len); } if (string_res == 0) { break; } } /* try to match the input in the regexp bucket */ for (rrulep = idp->rule_hash[DP_INDEX_HASH_SIZE].first_rule; rrulep; rrulep=rrulep->next) { // Check for Time Period if Set if(rrulep->parsed_timerec) { LM_DBG("Timerec exists for rule checking: %.*s\n", rrulep->timerec.len, rrulep->timerec.s); // Doesn't matches time period continue with next rule if(!check_time(rrulep->parsed_timerec)) { LM_DBG("Time rule doesn't match: skip next!\n"); continue; } } regexp_res = (test_match(input, rrulep->match_comp, matches, MAX_MATCHES) >= 0 ? 0 : -1); LM_DBG("Regex operator testing. Got result: %d\n", regexp_res); if (regexp_res == 0) { break; } } if (string_res != 0 && regexp_res != 0) { LM_DBG("No matching rule for input %.*s\n", input.len, input.s); return -1; } /* pick the rule with lowest table index if both match and prio are equal */ if (string_res == 0 && regexp_res == 0) { if (rrulep->pr < rulep->pr) { rulep = rrulep; } else if (rrulep->pr == rulep->pr && rrulep->table_id < rulep->table_id) { rulep = rrulep; } } if (!rulep) rulep = rrulep; LM_DBG("Found a matching rule %p: pr %i, match_exp %.*s\n", rulep, rulep->pr, rulep->match_exp.len, rulep->match_exp.s); if(attrs){ attrs->len = 0; attrs->s = 0; if(rulep->attrs.len>0) { LM_DBG("the rule's attrs are %.*s\n", rulep->attrs.len, rulep->attrs.s); if(rulep->attrs.len >= DP_MAX_ATTRS_LEN) { LM_ERR("EXCEEDED Max attribute length.\n"); return -1; } attrs->s = dp_attrs_buf; memcpy(attrs->s, rulep->attrs.s, rulep->attrs.len*sizeof(char)); attrs->len = rulep->attrs.len; attrs->s[attrs->len] = '\0'; LM_DBG("the copied attributes are: %.*s\n", attrs->len, attrs->s); } } if(rule_translate(msg, input, rulep, output)!=0){ LM_ERR("could not build the output\n"); return -1; } return 0; } int test_match(str string, pcre * exp, int * out, int out_max) { int i, result_count; char *substring_start; int substring_length; UNUSED(substring_start); UNUSED(substring_length); if(!exp){ LM_ERR("invalid compiled expression\n"); return -1; } result_count = pcre_exec( exp, /* the compiled pattern */ NULL, /* no extra data - we didn't study the pattern */ string.s, /* the subject string */ string.len, /* the length of the subject */ 0, /* start at offset 0 in the subject */ 0, /* default options */ out, /* output vector for substring information */ out_max); /* number of elements in the output vector */ if( result_count < 0 ) return result_count; if( result_count == 0) { LM_ERR("Not enough space for mathing\n"); return result_count; } for (i = 0; i < result_count; i++) { substring_start = string.s + out[2 * i]; substring_length = out[2 * i + 1] - out[2 * i]; LM_DBG("test_match:[%d] %.*s\n",i, substring_length, substring_start); } return result_count; } opensips-2.2.2/modules/dispatcher/000077500000000000000000000000001300170765700171615ustar00rootroot00000000000000opensips-2.2.2/modules/dispatcher/Makefile000066400000000000000000000003271300170765700206230ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=dispatcher.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/dispatcher/README000066400000000000000000001036701300170765700200500ustar00rootroot00000000000000DISPATCHER Module Daniel-Constantin Mierla Edited by Ovidiu Sas Edited by Daniel-Constantin Mierla Edited by Carsten Bock BASIS AudioNet GmbH Copyright © 2004 FhG FOKUS Copyright © 2005-2010 Voice-System.RO Revision History Revision $Revision: 6771 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS modules 1.2.2. External libraries or applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. attrs_avp (str) 1.3.3. hash_pvar (str) 1.3.4. setid_pvar (str) 1.3.5. ds_ping_method (string) 1.3.6. ds_ping_from (string) 1.3.7. ds_ping_interval (int) 1.3.8. ds_probing_sock (str) 1.3.9. ds_probing_threshhold (int) 1.3.10. ds_probing_mode (int) 1.3.11. ds_probing_list (str) 1.3.12. ds_define_blacklist (str) 1.3.13. options_reply_codes (str) 1.3.14. dst_avp (str) 1.3.15. grp_avp (str) 1.3.16. cnt_avp (str) 1.3.17. sock_avp (str) 1.3.18. pvar_algo_pattern (str) 1.3.19. persistent_state (int) 1.3.20. table_name (string) 1.3.21. partition (string) 1.3.22. setid_col (string) 1.3.23. destination_col (string) 1.3.24. state_col (string) 1.3.25. weight_col (string) 1.3.26. priority_col (string) 1.3.27. attrs_col (string) 1.3.28. socket_col (string) 1.4. Exported Functions 1.4.1. ds_select_dst(set, alg [, (flags M max_results)*]) 1.4.2. ds_select_domain(set, alg [, "[flags] [M max_results]"]) 1.4.3. ds_next_dst([partition_name]) 1.4.4. ds_next_domain([partition_name]) 1.4.5. ds_mark_dst() 1.4.6. ds_mark_dst([partition_name], "s") 1.4.7. ds_count(set, filter, result) 1.4.8. ds_is_in_list(ip, port [,set [,active_only]]) 1.5. Exported MI Functions 1.5.1. ds_set_state 1.5.2. ds_list 1.5.3. ds_reload 1.6. Exported Events 1.6.1. E_DISPATCHER_STATUS 1.7. Installation and Running 1.7.1. OpenSIPS config file 2. Frequently Asked Questions List of Examples 1.1. Set the 'default' partition's“db_url†parameter 1.2. Set the 'default' partition's “attrs_avp†parameter 1.3. Use $avp(273) for hashing: 1.4. Use combination of PVs for hashing: 1.5. Set the “setid_pvar†parameter 1.6. Set the “ds_ping_method†parameter 1.7. Set the “ds_ping_from†parameter 1.8. Set the “ds_ping_interval†parameter 1.9. Set the “ds_probing_sock†parameter 1.10. Set the “ds_probing_threshhold†parameter 1.11. Set the “ds_probing_mode†parameter 1.12. Set the “ds_probing_list†parameter 1.13. Set the 'default' partition's “ds_define_blacklist†parameter 1.14. Set the “options_reply_codes†parameter 1.15. Set the 'default' partition's “dst_avp†parameter 1.16. Set the 'default' partition's “grp_avp†parameter 1.17. Set the 'default' partition's “cnt_avp†parameter 1.18. Set the 'default' partition's “sock_avp†parameter 1.19. Set the “pvar_algo_pattern†parameter 1.20. Set the persistent_state parameter 1.21. Set the 'default' partition's “table_name†parameter 1.22. Create a new partition called 'part2' 1.23. Set “setid_col†parameter 1.24. Set “destination_col†parameter 1.25. Set “state_col†parameter 1.26. Set “weight_col†parameter 1.27. Set “priority_col†parameter 1.28. Set “attrs_col†parameter 1.29. Set “socket_col†parameter 1.30. ds_select_dst usage 1.31. ds_count usage 1.32. ds_is_in_list usage 1.33. OpenSIPS config script - sample dispatcher usage Chapter 1. Admin Guide 1.1. Overview This modules implements a dispatcher for destination addresses. It computes hashes over various parts of the request and selects an address from a destination set. The selected address is then used as outbound proxy. The module can be used as a stateless load balancer, having no guarantee of fair distribution. For the distribution algorithm, the module allows the definition of weights for the destination. This is useful in order to get a different ratio of traffic between destinations. Since version 1.12 the dispatcher module keeps its destination sets into different partitions. Each partition is described by its own db_url, table_name, dst_avp, grp_avp, cnt_avp, sock_avp, attr_avp and blacklists. Setting any of this parameters using modparam will alter the default partition's properties. In order to create a new partition the "partition" parameter should be used (see below for more details). If none of the 8 partition specific parameters are defined for the default partition, then this partition will not be created. If the default partition is created each undefined parameter from all other partitions will take the value of the corresponding one from the default partition. If there is no default partition, the default value specified in the parameter's description will be used. Functions taking set arguments will now take a set number preceded by a partition name and colon(i.e "part_name: 5"). If a set is not preceded by any partition name the default partition will be used. Thus, the following arguments are equivalent: "default : 4" vs "4". Remember that in order to be able to use a table from a partition, its name must be found in the "version" table belonging to the database defined in the partition's db_url. Also, in version 1.12 the "flags" parameter has been moved to ds_select_dst and ds_select_domain along with "force_dst" and "use_default" flags. 1.2. Dependencies 1.2.1. OpenSIPS modules The following modules must be loaded before this module: * TM - only if active recovery of failed hosts is required. 1.2.2. External libraries or applications The following libraries or applications must be installed before running OpenSIPS with this module: * none. 1.3. Exported Parameters 1.3.1. db_url (string) Database where to load the destinations from. Setting this parameter will only change the default partition's db_url. Use the partition parameter to create and alter other partitions. Default value is “NULLâ€. At least one db_url should be defined for the dispatcher module to work. Example 1.1. Set the 'default' partition's“db_url†parameter ... modparam("dispatcher", "db_url", "mysql://user:passwb@localhost/database ") ... 1.3.2. attrs_avp (str) The name of the avp to contain the attributes string of the current destination. When a destination is selected, automatically, this AVP will provide the attributes string - this is an opaque string (from OpenSIPS point of view) : it is loaded from destination definition ( via DB) and blindly provided in the script. Setting this parameter will only change the default partition's attrs_avp. Use the partition parameter to create and alter other partitions. Note Default value is “null†- don't provide ATTRIBUTEs. Example 1.2. Set the 'default' partition's “attrs_avp†parameter ... modparam("dispatcher", "attrs_avp", "$avp(272)") ... 1.3.3. hash_pvar (str) String with PVs used for the hashing algorithm 7. Note You must set this parameter if you want do hashing over custom message parts. Default value is “null†- disabled. Example 1.3. Use $avp(273) for hashing: ... modparam("dispatcher", "hash_pvar", "$avp(273)") ... Example 1.4. Use combination of PVs for hashing: ... modparam("dispatcher", "hash_pvar", "hash the $fU@$ci") ... 1.3.4. setid_pvar (str) The name of the PV where to store the set ID (group ID) when calling ds_is_in_list() without group parameter (third parameter). Default value is “null†- don't set PV. Example 1.5. Set the “setid_pvar†parameter ... modparam("dispatcher", "setid_pvar", "$var(setid)") ... 1.3.5. ds_ping_method (string) With this Method you can define, with which method you want to probe the failed gateways. This method is only available, if compiled with the probing of failed gateways enabled. Default value is “OPTIONSâ€. Example 1.6. Set the “ds_ping_method†parameter ... modparam("dispatcher", "ds_ping_method", "INFO") ... 1.3.6. ds_ping_from (string) With this Method you can define the "From:"-Line for the request, sent to the failed gateways. This method is only available, if compiled with the probing of failed gateways enabled. Default value is “sip:dispatcher@localhostâ€. Example 1.7. Set the “ds_ping_from†parameter ... modparam("dispatcher", "ds_ping_from", "sip:proxy@sip.somehost.com") ... 1.3.7. ds_ping_interval (int) With this Method you can define the interval for sending a request to a failed gateway. This parameter is only used, when the TM-Module is loaded. If set to “0â€, the pinging of failed requests is disabled. Default value is “0†(disabled). Example 1.8. Set the “ds_ping_interval†parameter ... modparam("dispatcher", "ds_ping_interval", 30) ... 1.3.8. ds_probing_sock (str) A socket description [proto:]host[:port] of the local socket (which is used by OpenSIPS for SIP traffic) to be used (if multiple) for sending the probing messages from. Default value is “NULL(none)â€. Example 1.9. Set the “ds_probing_sock†parameter ... modparam("dispatcher", "ds_probing_sock", "udp:192.168.1.100:5077") ... 1.3.9. ds_probing_threshhold (int) If you want to set a gateway into probing mode, you will need a specific number of requests until it will change from "active" to probing. The number of attempts can be set with this parameter. Default value is “3â€. Example 1.10. Set the “ds_probing_threshhold†parameter ... modparam("dispatcher", "ds_probing_threshhold", 10) ... 1.3.10. ds_probing_mode (int) Controls what gateways are tested to see if they are reachable. If set to 0, only the gateways with state PROBING are tested, if set to 1, all gateways are tested. If set to 1 and the response is 408 (timeout), an active gateway is set to PROBING state. Default value is “0â€. Example 1.11. Set the “ds_probing_mode†parameter ... modparam("dispatcher", "ds_probing_mode", 1) ... 1.3.11. ds_probing_list (str) Defines a list of one or more setids that limits which destinations are probed if probing is active. This is useful when multiple proxies share the same dispatcher table, but you want to limit which ones are responsible for probing specific destinations. Default value is “NULL(none)â€. Example 1.12. Set the “ds_probing_list†parameter ... modparam("dispatcher", "ds_probing_list", "1,2,3") ... 1.3.12. ds_define_blacklist (str) Defines a blacklist based on a dispatching setid from the 'default' partition. This list will contain the IPs (no port, all protocols) of the destinations matching the given setid. Use the 'partition' parameter if you want to define blacklists based on other partitions' sets. Multiple instances of this param are allowed. Default value is “NULLâ€. Example 1.13. Set the 'default' partition's “ds_define_blacklist†parameter ... modparam("dispatcher", "ds_define_blacklist", "list= 1,4,3") modparam("dispatcher", "ds_define_blacklist", "blist2= 2,10,6") ... 1.3.13. options_reply_codes (str) This parameter must contain a list of SIP reply codes separated by comma. The codes defined here will be considered as valid reply codes for OPTIONS messages used for pinging, apart for 200. Default value is “NULLâ€. Example 1.14. Set the “options_reply_codes†parameter ... modparam("dispatcher", "options_reply_codes", "501, 403") ... 1.3.14. dst_avp (str) This is mainly for internal usage and represents the name of the avp which will hold the list with addresses, in the order they have been selected by the chosen algorithm. If use_default is 1, the value of last dst_avp_id is the last address in destination set. The first dst_avp_id is the selected destinations. All the other addresses from the destination set will be added in the avp list to be able to implement serial forking. Setting this parameter will only change the default partition's dst_avp. Use the partition parameter to create and alter other partitions. For the 'default' partition the default value is “$avp(ds_dst_failover)â€. For any other partition, the default value is “$avp(ds_dst_failover_partitionname)â€. Example 1.15. Set the 'default' partition's “dst_avp†parameter ... modparam("dispatcher", "dst_avp", "$avp(271)") ... 1.3.15. grp_avp (str) This is mainly for internal usage and represents the name of the avp storing the group id of the destination set. Good to have it for later usage or checks. Setting this parameter will only change the default partition's grp_avp. Use the partition parameter to create and alter other partitions. For the 'default' partition the default value is “$avp(ds_grp_failover)â€. For any other partition, the default value is “$avp(ds_grp_failover_partitionname)â€. Example 1.16. Set the 'default' partition's “grp_avp†parameter ... modparam("dispatcher", "grp_avp", "$avp(273)") ... 1.3.16. cnt_avp (str) This is mainly for internal usage and represents the name of the avp storing the number of destination addresses kept in dst_avp avps. Setting this parameter will only change the default partition's cnt_avp. Use the partition parameter to create and alter other partitions. For the 'default' partition the default value is “$avp(ds_cnt_failover)â€. For any other partition, the default value is “$avp(ds_cnt_failover_partitionname)â€. Example 1.17. Set the 'default' partition's “cnt_avp†parameter ... modparam("dispatcher", "cnt_avp", "$avp(274)") ... 1.3.17. sock_avp (str) This is mainly for internal usage and represents the name of the avp storing the sockets to be used for the destination addresses kept in dst_avp avps. Setting this parameter will only change the default partition's sock_avp. Use the partition parameter to create and alter other partitions. For the 'default' partition the default value is “$avp(ds_sock_failover)â€. For any other partition, the default value is “$avp(ds_sock_failover_partitionname)â€. Example 1.18. Set the 'default' partition's “sock_avp†parameter ... modparam("dispatcher", "sock_avp", "$avp(275)") ... 1.3.18. pvar_algo_pattern (str) This parameter is used by the PVAR(9) algorithm to specify the pseudovariable pattern used to detect the load of each destination. The name of the pseudovariable should contain the string “%uâ€, which will be internally replaced by the module with the uri of the destination. Default value is “noneâ€. Example 1.19. Set the “pvar_algo_pattern†parameter ... modparam("dispatcher", "pvar_algo_pattern", "$stat(load_%u)") ... 1.3.19. persistent_state (int) Specifies whether the state column should be loaded at startup and flushed during runtime or not. Default value is “1†(enabled). Example 1.20. Set the persistent_state parameter ... # disable all DB operations with the state of a destination modparam("dispatcher", "persistent_state", 0) ... 1.3.20. table_name (string) If you want to load the sets of gateways from the database you must set this parameter as the database name. Setting this parameter will only change the default partition's table_name. Use the partition parameter to create and alter other partitions. For every partition the default value is “dispatcherâ€. Example 1.21. Set the 'default' partition's “table_name†parameter ... modparam("dispatcher", "table_name", "my_dispatcher") ... 1.3.21. partition (string) Using this parameter the partition specific parameters (db_url, table_name, dst_avp, grp_avp, cnt_avp, sock_avp, attrs_avp, ds_define_blacklist) can be defined. The syntax is: "partition_name: param1 = value1; param2 = value2; param3 = value3". Each value format is the same as the one used to define a specific parameter using modparam. Whenever a new partition_name is provided, a new partition will be automatically created. The 'default' partition can also be defined using this parameter. Example 1.22. Create a new partition called 'part2' ... modparam("dispatcher", "partition", "part2: db_url = mysql://user:passwd@localhost/database; table_name = ds_table; attrs_avp = $avp(ds_attr_part2); ds_define_blacklist = list2 = 4,6;") ... 1.3.22. setid_col (string) The column's name in the database storing the gateway's group id. Default value is “setidâ€. Example 1.23. Set “setid_col†parameter ... modparam("dispatcher", "setid_col", "groupid") ... 1.3.23. destination_col (string) The column's name in the database storing the destination's sip uri. Default value is “destinationâ€. Example 1.24. Set “destination_col†parameter ... modparam("dispatcher", "destination_col", "uri") ... 1.3.24. state_col (string) The column's name in the database storing the state of the destination uri. Default value is “stateâ€. Example 1.25. Set “state_col†parameter ... modparam("dispatcher", "state_col", "dststate") ... 1.3.25. weight_col (string) The column's name in the database storing the weight for destination uri. Default value is “weightâ€. Example 1.26. Set “weight_col†parameter ... modparam("dispatcher", "weight_col", "dstweight") ... 1.3.26. priority_col (string) The column's name in the database storing the priority for destination uri. Default value is “priorityâ€. Example 1.27. Set “priority_col†parameter ... modparam("dispatcher", "priority_col", "dstprio") ... 1.3.27. attrs_col (string) The column's name in the database storing the attributes (opaque string) for destination uri. Default value is “attrsâ€. Example 1.28. Set “attrs_col†parameter ... modparam("dispatcher", "attrs_col", "dstattrs") ... 1.3.28. socket_col (string) The column's name in the database storing the socket (as string) for destination uri. Default value is “socketâ€. Example 1.29. Set “socket_col†parameter ... modparam("dispatcher", "socket_col", "my_sock") ... 1.4. Exported Functions 1.4.1. ds_select_dst(set, alg [, (flags M max_results)*]) The method selects a destination from the given set of addresses. It will overwrite the "destination URI" of a SIP request ($du). Meaning of the parameters is as follows: * set - a partition name followed by colon and an id of the set or a list of sets from where to pick up destination address (variables are accepted). If the partition name is missing, the default partition will be used. * alg - the algorithm(s) used to select the destination address (variables are accepted). + “0†- hash over callid + “1†- hash over from uri. + “2†- hash over to uri. + “3†- hash over request-uri. + “4†- round-robin (next destination). + “5†- hash over authorization-username (Proxy-Authorization or "normal" authorization). If no username is found, round robin is used. + “6†- random (using rand()). + “7†- hash over the content of PVs string. Note: This works only when the parameter hash_pvar is set. + “8†- the first entry in set is chosen. + “9†- The pvar_algo_pattern parameter is used to determine the load on each server. If the parameter is not specified, then the first entry in the set is chosen. + “X†- if the algorithm is not implemented, the first entry in set is chosen. * flags M max_results - If specified, this will be the flags which in previous versions were specified at startup. The flags are: + 'f'/'F' - the failover support flag, + 'u'/'U' - the user only flag - will specify that only the uri user part will be used for hashing, + 'S'/'s' - the force destination flag - which will skip overwriting the destination address if it is already set, + 'D'/'d' - the use default flag('D','d') - which will use the last address in destination set as last option to send the message. You can also specify these flags using PVs. The flags are being kept per partition. The second paramater, max_results represents that only a maximum of max_results will be put into the specified avp for failover. This allows having many destinations but limit the useless traffic in case of a number that is bound to fail everywhere. Since version 1.12, the last paramater cand be represented by a list of flags and max_results, separated by comma. You can specify either only the flags, either only the max_results paramater, but if you want to specify them together you have to use the 'M' character like this: flags M max_results. If the character 'f' in 'flags' is set, the rest of the addresses from the destination set is stored in AVP list. You can use 'ds_next_dst()' to use next address to achieve serial forking to all possible destinations. If multiple dispatching groups are used, the AVP list is constructed based on the position of each dispatching id in the list: first one has the higher priority, followed by the second one and so on. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.30. ds_select_dst usage ... ds_select_dst("1", "0"); ... ds_select_dst("part2 : 1", "0", "5"); ... ds_select_dst("part3 : 1", "0", "fUsD"); ... ds_select_dst("part4 : 2,3", "0,1", "fuD M 5, fuS M 2"); ... # dispatch over multiple dispatching groups $var(part_name) = "p4" $var(setid) = "1, 2"; $var(alg) = "4, 2"; $var(flags) = " sFDU M 2, fuS M 3"; ds_select_dst("$var(part_name):$var(setid)","$var(alg)","$var(flags)"); ... 1.4.2. ds_select_domain(set, alg [, "[flags] [M max_results]"]) The method selects a destination from addresses set and rewrites the host and port from R-URI. The parameters have same meaning as for ds_select_dst(). If the character 'f' in 'flags' is set, the rest of the addresses from the destination set is stored in AVP list. You can use 'ds_next_domain()' to use next address to achieve serial forking to all possible destinations. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. 1.4.3. ds_next_dst([partition_name]) Takes the next destination address from the AVPs with id partition.'dst_avp_id' and sets the dst_uri (outbound proxy address). If partition_name is omitted, the default partition will be used.This function is using the flags set in ds_select_dst or ds_select_domain. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. 1.4.4. ds_next_domain([partition_name]) Takes the next destination address from the AVPs with id partition.'dst_avp_id' and sets the domain part of the request uri. If partition_name is omitted, the default partition will be used.This function is using the flags set in ds_select_dst or ds_select_domain. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. 1.4.5. ds_mark_dst() Mark the last used address from the 'default' partition's destination set as inactive, in order to be ignored in the future. In this way it can be implemented an automatic detection of failed gateways. When an address is marked as inactive, it will be ignored by 'ds_select_dst' and 'ds_select_domain'. This function is using the flags set in ds_select_dst or ds_select_domain. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. 1.4.6. ds_mark_dst([partition_name], "s") Mark the last used address from partition's destination set as inactive ("i"/"I"/"0"), active ("a"/"A"/"1") or probing ("p"/"P"/"2"). With this function, an automatic detection of failed gateways can be implemented. When an address is marked as inactive or probing, it will be ignored by 'ds_select_dst' and 'ds_select_domain'. If partition_name is omitted, the default partition will be used. This function is using the flags set in ds_select_dst or ds_select_domain. possible parameters: * "i", "I" or "0" - the last destination should be set to inactive and will be ignored in future requests. * "a", "A" or "1" - the last destination should be set to active. * "p", "P" or "2" - the last destination will be set to probing. Note: You will need to call this function "threshold"-times, before it will be actually set to probing. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. 1.4.7. ds_count(set, filter, result) Returns the number of active, inactive or probing destinations in a partition's set, or combinations between these properties. Meaning of the parameters: * set - a partition name followed by colon and an id of a set of dispatching destinations (variables are accepted). If the partition name is missing, the default partition will be used. * filter - which destinations should be counted. Either active destinations("a", "A" or "1"), inactive destinations("i", "I" or "0") or probing ones("p", "P" or "2") or different combinations between these flags, such as "pI", "1i", "ipA"... The filter parameter can have the following types: + string - the filtering flags are statically assigned * result - A pseudovariable for storing the result. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, TIMER_ROUTE, EVENT_ROUTE Example 1.31. ds_count usage ... if (ds_count("1", "a", "$avp(result)")) { ... } ... if (ds_count("$avp(partition) : $avp(set)", "ip", "$avp(result)")) { ... } ... 1.4.8. ds_is_in_list(ip, port [,set [,active_only]]) This function returns true, if the parameters ip and port point to a host from the dispatcher-list; otherwise false. Meaning of the parameters: * ip - string / pseudo-variable (as string) containing the IP to test against the dispatcher list. This cannot be empty. * port - int / pseudo-variable (as int) containing the PORT to test against the dispatcher list. This can be empty - in this case the port will excluded from the matching of IP against the dispatcher list. * set (optional) - a partition name followed by colon and the set ID of a dispatcher list to test against. If the partition name is omitted the default partition will be used. If the set id is missing, all the dispatching sets will the checked. If a partition name is specified then it must be followed by colon regardless of the set id being specified or not. “-1†can be used as a wildcard to check in all sets. * active_only (optional) - search only through the active destinations (ignore the ones in probing and inactive mode). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and ONREPLY_ROUTE. Example 1.32. ds_is_in_list usage ... if (ds_is_in_list("$si", "$sp")) { # source IP:PORT is in a dispatcher list } ... if (ds_is_in_list("$rd", "$rp", "2")) { # source RURI (ip and port) is in the dispatcher list id "2" of the default partition } ... if (ds_is_in_list("$rd", "$rp", "part2:2")) { # source RURI (ip and port) is in the dispatcher list id "2" of the partition called 'part2' } ... 1.5. Exported MI Functions 1.5.1. ds_set_state Sets the status for a destination address (can be use to mark the destination as active or inactive). Name: ds_set_state Parameters: * _state_ : state of the destination address + “aâ€: active + “iâ€: inactive + “pâ€: probing * _group_: partition name followed by colon and destination group id. If the partition name is omitted, the default partition will be used * _address_: address of the destination in the _group_ MI FIFO Command Format: :ds_set_state:_reply_fifo_file_ _state_ _group_ _address_ _empty_line_ 1.5.2. ds_list It lists the groups and included destinations of all the partitions. Name: ds_list Parameters: * full (optional) - adds the weight, priority and description fields to the listing MI FIFO Command Format: :ds_list:_reply_fifo_file_ _empty_line_ 1.5.3. ds_reload It reloads the groups and included destinations for a specified partition or all partitions. Name: ds_reload Parameters: * partition (optional) - name of the partition to be reloaded. MI DATAGRAM Command Format: ":ds_reload:\n." 1.6. Exported Events 1.6.1. E_DISPATCHER_STATUS This event is raised when the dispatcher module marks a destination as activated or deactivated. Parameters: * partition - the partition name of the destination. * group - the group of the destination. * address - the address of the destination. * status - active if the destination gets activated or inactive if the destination is detected unresponsive. 1.7. Installation and Running 1.7.1. OpenSIPS config file Next picture displays a sample usage of dispatcher. Example 1.33. OpenSIPS config script - sample dispatcher usage ... # # sample config file for dispatcher module # debug_mode=yes children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 # for more info: opensips -h # ------------------ module loading ---------------------------------- mpath="/usr/local/lib/opensips/modules/" loadmodule "maxfwd.so" loadmodule "signaling.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "db_mysql.so" loadmodule "dispatcher.so" # ----------------- setting module-specific parameters --------------- # -- dispatcher params -- modparam("dispatcher", "db_url", "mysql://opensips:opensipsrw@localhost/ opensips") route{ if ( !mf_process_maxfwd_header("10") ) { send_reply("483","To Many Hops"); exit; }; if ( !ds_select_dst("2", "0") ) { send_reply("500","Unable to route"); exit; } t_relay(); } ... Chapter 2. Frequently Asked Questions 2.1. Does dispatcher provide a fair distribution? There is no guarantee of that. You should do some measurements to decide what distribution algorithm fits better in your environment. 2.2. Is dispatcher dialog stateful? No. Dispatcher is stateless, although some distribution algorithms are designed to select same destination for subsequent requests of the same dialog (e.g., hashing the call-id). 2.3. What happened with the ds_is_from_list() function? The function was replaced by the more generic ds_is_in_list() function that takes as parameters the IP and PORT to test against the dispatcher list. ds_is_from_list() == ds_is_in_list("$si","$sp") 2.4. How is weight and priority used by the dispatcher in selecting a destination? The weight of a destination is currently used in the hashing algorithms and it increases the probability of it to be chosen(if we have two destinations with weights 1 respectively 4 than the second one is 4 times more likely to be selected than the other). The sum of all weights does not need to add up to a specific number. It is important to understand that the weights are not used in the round-robin algorithm at the moment. The priority field is used at ordering the destinations from a set. It does not affect the overall probability of a destination to be chosen. It is reflected when listing the destination, the field can definetly be used in further selecting algorithms. 2.5. What happened with the list_file module parameter ? The support for text file (for provisioning destinations) was dropped. Only the DB support (provisioning via a DB table) is now available - if you still want to use a text file for provisioning, use db_text DB driver (DB emulated via text files) 2.6. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.7. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable version should be sent to and e-mail regarding development versions or SVN snapshots should be send to . If you want to keep the mail private, send it to . 2.8. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues opensips-2.2.2/modules/dispatcher/dispatch.c000066400000000000000000001615511300170765700211350ustar00rootroot00000000000000/* * dispatcher module * * Copyright (C) 2010-2015 OpenSIPS Solutions * Copyright (C) 2005-2010 Voice-System.ro * Copyright (C) 2004-2006 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2004-07-31 first version, by daniel * 2005-04-22 added ruri & to_uri hashing (andrei) * 2005-12-10 added failover support via avp (daniel) * 2006-08-15 added support for authorization username hashing (carsten) * 2007-01-11 Added a function to check if a specific gateway is in a * group (carsten) * 2007-01-12 Added a threshhold for automatic deactivation (carsten) * 2007-02-09 Added active probing of failed destinations and automatic * re-enabling of destinations (carsten) * 2007-05-08 Ported the changes to SVN-Trunk, renamed ds_is_domain to * ds_is_from_list and modified the function to work with IPv6 adresses. * 2007-07-18 removed index stuff * added DB support to load/reload data(ancuta) * 2007-09-17 added list-file support for reload data (carstenbock) * 2009-05-18 Added support for weights for the destinations; * added support for custom "attrs" (opaque string) (bogdan) * 2013-12-02 Added support state persistency (restart and reload) (bogdan) * 2013-12-05 Added a safer reload mechanism based on locking read/writter (bogdan) */ #include #include #include #include "../../ut.h" #include "../../trim.h" #include "../../dprint.h" #include "../../action.h" #include "../../route.h" #include "../../dset.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../usr_avp.h" #include "../../mi/mi.h" #include "../../parser/digest/digest.h" #include "../../resolve.h" #include "../tm/tm_load.h" #include "../../db/db.h" #include "../../db/db_res.h" #include "../../str.h" #include "../../rw_locking.h" #include "dispatch.h" #include "ds_fixups.h" #include "ds_bl.h" #define DS_TABLE_VERSION 7 extern ds_partition_t *partitions; extern struct socket_info *probing_sock; extern event_id_t dispatch_evi_id; extern ds_partition_t *default_partition; #define dst_is_active(_dst) \ (!((_dst).flags&(DS_INACTIVE_DST|DS_PROBING_DST))) int init_ds_data(ds_partition_t *partition) { partition->data = (ds_data_t**)shm_malloc( sizeof(ds_data_t*) ); if (partition->data==NULL) { LM_ERR("failed to allocate data holder in shm\n"); return -1; } *partition->data = NULL; /* create & init lock */ if ((partition->lock = lock_init_rw()) == NULL) { LM_CRIT("failed to init reader/writer lock\n"); return -1; } return 0; } /* destroy entire dispatching data */ static void ds_destroy_data_set( ds_data_t *d) { ds_set_p sp; ds_set_p sp_curr; ds_dest_p dest; /* free the list of sets */ sp = d->sets; while(sp) { sp_curr = sp; sp = sp->next; dest = sp_curr->dlist; if (dest) { do { if(dest->uri.s!=NULL) shm_free(dest->uri.s); if(dest->param) shm_free(dest->param); dest = dest->next; }while(dest); shm_free(sp_curr->dlist); } shm_free(sp_curr); } /* free the data holder */ shm_free(d); } /* destroy current dispatching data */ void ds_destroy_data(ds_partition_t *partition) { if (partition->data && *partition->data) ds_destroy_data_set( *partition->data ); /* destroy rw lock */ if (partition->lock) { lock_destroy_rw( partition->lock ); partition->lock = 0; } } int add_dest2list(int id, str uri, struct socket_info *sock, int state, int weight, int prio, str attrs, str description, ds_data_t *d_data) { ds_dest_p dp = NULL; ds_set_p sp = NULL; short new_set = 0; ds_dest_p dp_it, dp_prev; struct sip_uri puri; /* For DNS-Lookups */ struct proxy_l *proxy; union sockaddr_union sau; /* check uri */ if(parse_uri(uri.s, uri.len, &puri)!=0 || puri.host.len>254) { LM_ERR("bad uri [%.*s]\n", uri.len, uri.s); goto err; } /* get dest set */ for( sp=d_data->sets ; sp ; sp=sp->next) { if(sp->id == id) break; } if(sp==NULL) { sp = (ds_set_p)shm_malloc(sizeof(ds_set_t)); if(sp==NULL) { LM_ERR("no more memory.\n"); return -1; } new_set = 1; memset(sp, 0, sizeof(ds_set_t)); sp->id = id; } dp = (ds_dest_p)shm_malloc(sizeof(ds_dest_t)); if(dp==NULL) { LM_ERR("no more memory!\n"); goto err; } memset(dp, 0, sizeof(ds_dest_t)); /* store uri and attrs strings */ dp->uri.len = uri.len; if (puri.user.len == 0 && puri.passwd.len == 0 && puri.params.len == 0 && puri.headers.len == 0) { /* The uri from db is good for ds_select_dst */ dp->uri.s = shm_malloc(uri.len + 1 + attrs.len + 1 + description.len + 1); if(dp->uri.s==NULL){ LM_ERR("no more shm memory!\n"); goto err; } dp->dst_uri = dp->uri; dp->attrs.s = dp->uri.s + dp->uri.len + 1; dp->description.s = dp->uri.s + dp->uri.len + 1 + attrs.len + 1; } else { dp->dst_uri.len = uri_typestrlen(puri.type) + 1 + puri.host.len + (puri.port.len ? puri.port.len + 1 : 0); dp->uri.s = shm_malloc(uri.len+1 + dp->dst_uri.len + 1 + attrs.len+1 + description.len + 1); if(dp->uri.s==NULL){ LM_ERR("no more shm memory!\n"); goto err; } dp->description.s = dp->uri.s + dp->uri.len + 1 + dp->dst_uri.len + 1 + attrs.len + 1; dp->attrs.s = dp->uri.s + dp->uri.len + 1 + dp->dst_uri.len + 1; dp->dst_uri.s = dp->uri.s + dp->uri.len + 1; char *p = uri_type2str(puri.type, dp->dst_uri.s); *(p++) = ':'; memcpy(p, puri.host.s, puri.host.len); p += puri.host.len; if (puri.port.len) { *(p++) = ':'; memcpy(p, puri.port.s, puri.port.len); } dp->dst_uri.s[dp->dst_uri.len]='\0'; } memcpy(dp->uri.s, uri.s, dp->uri.len); if (attrs.len) { memcpy(dp->attrs.s, attrs.s, attrs.len); dp->attrs.s[attrs.len]='\0'; dp->attrs.len = attrs.len; } else dp->attrs.s = NULL; if(description.len){ memcpy(dp->description.s, description.s, description.len); dp->description.s[description.len]='\0'; dp->description.len = description.len; } /* copy state, weight & socket */ dp->sock = sock; dp->weight = weight; switch (state) { case 0: dp->flags = 0; break; case 1: dp->flags = DS_INACTIVE_DST; break; case 2: dp->flags = DS_PROBING_DST; break; default: LM_CRIT("BUG: unknown state %d for destination %.*s\n", state, uri.len, uri.s); } /* Do a DNS-Lookup for the Host-Name: */ proxy = mk_proxy( &puri.host, puri.port_no, puri.proto, (puri.type==SIPS_URI_T)); if (proxy==NULL) { LM_ERR("could not resolve %.*s, skipping it\n", puri.host.len, puri.host.s); goto err; } hostent2ip_addr( &dp->ips[0], &proxy->host, proxy->addr_idx); dp->ports[0] = proxy->port; dp->protos[0] = proxy->proto; dp->ips_cnt = 1; dp->priority = prio; LM_DBG("first gw ip addr [%s]:%d\n", ip_addr2a(&dp->ips[0]), dp->ports[0]); /* get the next available IPs from DNS */ while (dp->ips_cntips[dp->ips_cnt], &sau); dp->ports[dp->ips_cnt] = proxy->port; dp->protos[dp->ips_cnt] = proxy->proto; LM_DBG("additional gw ip addr [%s]:%d, proto %d\n", ip_addr2a(&dp->ips[dp->ips_cnt]), dp->ports[dp->ips_cnt], dp->protos[dp->ips_cnt]); /* one more IP found */ dp->ips_cnt++; } /* free al the helper structures */ free_proxy(proxy); pkg_free(proxy); /* * search the proper place based on priority * put them in reverse order, since they will be reindexed */ for (dp_prev = NULL, dp_it = sp->dlist; dp_it && dp_it->priority < prio; dp_prev = dp_it, dp_it = dp_it->next); if (!dp_prev) { dp->next = sp->dlist; sp->dlist = dp; } else { dp->next = dp_prev->next; dp_prev->next = dp; } sp->nr++; if (new_set) { sp->next = d_data->sets; d_data->sets = sp; d_data->sets_no++; } LM_DBG("dest [%d/%d] <%.*s> <%.*s> successfully loaded\n", sp->id, sp->nr, dp->uri.len, dp->uri.s, dp->dst_uri.len, dp->dst_uri.s); return 0; err: /* free allocated memory */ if(dp!=NULL) { if(dp->uri.s!=NULL) shm_free(dp->uri.s); shm_free(dp); } if (sp != NULL && new_set) shm_free(sp); return -1; } /* iterates the whole set and calculates (1) the number of active destinations and (2) the running and total weight sum for the active destinations */ static inline void re_calculate_active_dsts(ds_set_p sp) { int j,i; /* pre-calculate the running weights for each destination */ for( j=0,i=-1,sp->active_nr=sp->nr ; jnr ; j++ ) { /* running weight is the current weight plus the running weight of * the previous element */ sp->dlist[j].running_weight = sp->dlist[j].weight + ((j==0) ? 0 : sp->dlist[j-1].running_weight); /* now the running weight for the active destinations */ if ( dst_is_active(sp->dlist[j]) ) { sp->dlist[j].active_running_weight = sp->dlist[j].weight + ((i==-1) ? 0 : sp->dlist[i].active_running_weight); i = j; /* last active destination */ } else { sp->dlist[j].active_running_weight = ((i==-1) ? 0 : sp->dlist[i].active_running_weight); sp->active_nr --; } LM_DBG("destination i=%d, j=%d, weight=%d, sum=%d, active_sum=%d\n", i,j, sp->dlist[j].weight, sp->dlist[j].running_weight,sp->dlist[j].active_running_weight); } } /* compact destinations from sets for fast access */ int reindex_dests( ds_data_t *d_data) { int j; ds_set_p sp = NULL; ds_dest_p dp = NULL, dp0= NULL; for( sp=d_data->sets ; sp!= NULL ; sp=sp->next ) { if (sp->nr == 0) { dp0 = NULL; continue; } dp0 = (ds_dest_p)shm_malloc(sp->nr*sizeof(ds_dest_t)); if(dp0==NULL) { LM_ERR("no more memory!\n"); goto err1; } memset(dp0, 0, sp->nr*sizeof(ds_dest_t)); /*copy from the old pointer to destination, and then free it*/ for(j=sp->nr-1; j>=0 && sp->dlist!= NULL; j--) { memcpy(&dp0[j], sp->dlist, sizeof(ds_dest_t)); if(j==sp->nr-1) dp0[j].next = NULL; else dp0[j].next = &dp0[j+1]; dp = sp->dlist; sp->dlist = dp->next; shm_free(dp); dp=NULL; } sp->dlist=dp0; re_calculate_active_dsts(sp); } LM_DBG("found [%d] dest sets\n", d_data->sets_no); return 0; err1: return -1; } /* variables used to generate the pvar name */ static int ds_has_pattern = 0; static str ds_pattern_prefix = str_init(""); static str ds_pattern_suffix = str_init(""); void ds_pvar_parse_pattern(str pattern) { char *p, *end; ds_pattern_prefix = pattern; end = pattern.s + pattern.len - DS_PV_ALGO_MARKER_LEN + 1; /* first try to see if we have the marker */ for (p = pattern.s; p < end && memcmp(p, DS_PV_ALGO_MARKER, DS_PV_ALGO_MARKER_LEN); p++); /* if reached end - pattern not present => pure pvar */ if (p == end) { LM_DBG("Pattern not found\n"); return; } ds_has_pattern = 1; ds_pattern_prefix.len = p - pattern.s; /* skip marker */ ds_pattern_suffix.s = p + DS_PV_ALGO_MARKER_LEN; ds_pattern_suffix.len = pattern.s + pattern.len - ds_pattern_suffix.s; } ds_pvar_param_p ds_get_pvar_param(str uri) { str name; int len = ds_pattern_prefix.len + uri.len + ds_pattern_suffix.len; char buf[len]; /* XXX: check if this works for all compilers */ ds_pvar_param_p param; if (ds_has_pattern) { name.len = 0; name.s = buf; memcpy(buf, ds_pattern_prefix.s, ds_pattern_prefix.len); name.len = ds_pattern_prefix.len; memcpy(name.s + name.len, uri.s, uri.len); name.len += uri.len; memcpy(name.s + name.len, ds_pattern_suffix.s, ds_pattern_suffix.len); name.len += ds_pattern_suffix.len; } param = shm_malloc(sizeof(ds_pvar_param_t)); if (!param) { LM_ERR("no more shm memory\n"); return NULL; } if (!pv_parse_spec(ds_has_pattern ? &name : &ds_pattern_prefix, ¶m->pvar)) { LM_ERR("cannot parse pattern spec\n"); shm_free(param); return NULL; } return param; } int ds_pvar_algo(struct sip_msg *msg, ds_set_p set, ds_dest_p **sorted_set, int ds_use_default) { pv_value_t val; int i, j, k, end_idx, cnt; ds_dest_p *sset; ds_pvar_param_p param; if (!set) { LM_ERR("invalid set\n"); return -1; } sset = shm_realloc(*sorted_set, set->nr * sizeof(ds_dest_p)); if (!sset) { LM_ERR("no more shm memory\n"); return -1; } *sorted_set = sset; end_idx = set->nr - 1; if (ds_use_default) { sset[end_idx] = &set->dlist[end_idx]; end_idx--; } for (i = 0, cnt = 0; i < set->nr - (ds_use_default?1:0); i++) { if ( !dst_is_active(set->dlist[i]) ) { /* move to the end of the list */ sset[end_idx--] = &set->dlist[i]; continue; } /* if pvar not set - try to evaluate it */ if (set->dlist[i].param == NULL) { param = ds_get_pvar_param(set->dlist[i].uri); if (param == NULL) { LM_ERR("cannot parse pvar for uri %.*s\n", set->dlist[i].uri.len, set->dlist[i].uri.s); continue; } set->dlist[i].param = (void *)param; } else { param = (ds_pvar_param_p)set->dlist[i].param; } if (pv_get_spec_value(msg, ¶m->pvar, &val) < 0) { LM_ERR("cannot get spec value for spec %.*s\n", set->dlist[i].uri.len, set->dlist[i].uri.s); continue; } if (!(val.flags & PV_VAL_NULL)) { if (!(val.flags & PV_VAL_INT)) { /* last attempt to retrieve value */ if (!str2sint(&val.rs, ¶m->value)) { LM_ERR("invalid pvar value type - not int\n"); continue; } } else { param->value = val.ri; } } else { param->value = 0; } /* search the proper position */ j = 0; for (; j < cnt && ((ds_pvar_param_p)sset[j]->param)->value <= param->value; j++); /* make space for the new entry */ for (k = cnt; k > j; k--) sset[k] = sset[k - 1]; sset[j] = &set->dlist[i]; cnt++; } return cnt; } int ds_connect_db(ds_partition_t *partition) { if(!partition->db_url.s) return -1; if (*partition->db_handle) { LM_CRIT("BUG - db connection found already open\n"); return -1; } if ((*partition->db_handle = partition->dbf.init(&partition->db_url)) == 0) return -1; return 0; } void ds_disconnect_db(ds_partition_t *partition) { if(*partition->db_handle) { partition->dbf.close(*partition->db_handle); *partition->db_handle = 0; } } /*initialize and verify DB stuff*/ int init_ds_db(ds_partition_t *partition) { int _ds_table_version; if(partition->table_name.s == 0){ LM_ERR("invalid database name\n"); return -1; } /* Find a database module */ if (db_bind_mod(&partition->db_url, &partition->dbf) < 0) { LM_ERR("Unable to bind to a database driver\n"); return -1; } if(ds_connect_db(partition)!=0){ LM_ERR("unable to connect to the database\n"); return -1; } _ds_table_version = db_table_version(&partition->dbf,*partition->db_handle, &partition->table_name); if (_ds_table_version < 0) { LM_ERR("failed to query table version\n"); return -1; } else if (_ds_table_version != DS_TABLE_VERSION) { LM_ERR("invalid table version (found %d , required %d)\n" "(use opensipsdbctl reinit)\n", _ds_table_version, DS_TABLE_VERSION ); return -1; } return 0; } static void ds_inherit_state( ds_data_t *old_data , ds_data_t *new_data) { ds_set_p new_set, old_set; ds_dest_p new_ds, old_ds; /* search the new sets through the old sets */ for ( new_set=new_data->sets ; new_set ; new_set=new_set->next ) { for ( old_set=old_data->sets ; old_set ; old_set=old_set->next ) { if (new_set->id==old_set->id) break; } if (old_set==NULL) { LM_DBG("new set id %d not found in old sets\n",new_set->id); continue; } LM_DBG("set id %d found in old sets\n",new_set->id); /* sets are matching, try to match the destinations, one by one */ for ( new_ds=new_set->dlist ; new_ds ; new_ds=new_ds->next ) { for ( old_ds=old_set->dlist ; old_ds ; old_ds=old_ds->next ) { if (new_ds->uri.len==old_ds->uri.len && strncasecmp(new_ds->uri.s, old_ds->uri.s, old_ds->uri.len)==0 ) { LM_DBG("DST <%.*s> found in old set, copying state\n", new_ds->uri.len,new_ds->uri.s); new_ds->flags = old_ds->flags; break; } } if (old_ds==NULL) LM_DBG("DST <%.*s> not found in old set\n", new_ds->uri.len,new_ds->uri.s); } } } void ds_flusher_routine(unsigned int ticks, void* param) { db_key_t key_cmp[2]; db_val_t val_cmp[2]; db_key_t key_set; db_val_t val_set; ds_set_p list; int j; ds_partition_t *partition; for (partition = partitions; partition; partition = partition->next){ if (*partition->db_handle==NULL) continue; val_cmp[0].type = DB_INT; val_cmp[0].nul = 0; val_cmp[1].type = DB_STR; val_cmp[1].nul = 0; val_set.type = DB_INT; val_set.nul = 0; /* update the gateways */ if (partition->dbf.use_table(*partition->db_handle, &partition->table_name) < 0) { LM_ERR("cannot select table \"%.*s\"\n", partition->table_name.len, partition->table_name.s); continue; } key_cmp[0] = &ds_set_id_col; key_cmp[1] = &ds_dest_uri_col; key_set = &ds_dest_state_col; if (*partition->data) { /* Iterate over the groups and the entries of each group */ for(list = (*partition->data)->sets; list!= NULL; list=list->next){ for(j=0; jnr; j++) { /* If the Flag of the entry is STATE_DIRTY -> flush do db*/ if ( (list->dlist[j].flags&DS_STATE_DIRTY_DST)==0 ) /* nothing to do for this destination */ continue; /* populate the update */ val_cmp[0].val.int_val = list->id; val_cmp[1].val.str_val = list->dlist[j].uri; val_set.val.int_val = (list->dlist[j].flags&DS_INACTIVE_DST) ? 1 : ((list->dlist[j].flags&DS_PROBING_DST)?2:0); /* update the state of this destination */ LM_DBG("updating the state of destination <%.*s> to %d\n", list->dlist[j].uri.len, list->dlist[j].uri.s, val_set.val.int_val); if (partition->dbf.update(*partition->db_handle,key_cmp,0, val_cmp,&key_set,&val_set,2,1)<0 ) { LM_ERR("DB update failed\n"); } else { list->dlist[j].flags &= ~DS_STATE_DIRTY_DST; } } } } } return; } /*load groups of destinations from DB*/ static ds_data_t* ds_load_data(ds_partition_t *partition, int use_state_col) { ds_data_t *d_data; int i, id, nr_rows, cnt, nr_cols = 8; int state; int weight; int prio; struct socket_info *sock; str uri; str attrs; str host; str description; int port, proto; db_res_t * res = NULL; db_val_t * values; db_row_t * rows; db_key_t query_cols[8] = {&ds_set_id_col, &ds_dest_uri_col, &ds_dest_sock_col, &ds_dest_weight_col, &ds_dest_attrs_col, &ds_dest_prio_col, &ds_dest_description_col, &ds_dest_state_col}; if (!use_state_col) nr_cols--; if(*partition->db_handle == NULL){ LM_ERR("invalid DB handler\n"); return NULL; } if (partition->dbf.use_table(*partition->db_handle, &partition->table_name) < 0) { LM_ERR("error in use_table\n"); return NULL; } d_data = (ds_data_t*)shm_malloc( sizeof(ds_data_t) ); if (d_data==NULL) { LM_ERR("failed to allocate new data structure in shm\n"); return NULL; } memset( d_data, 0, sizeof(ds_data_t)); /*select the whole table and all the columns*/ if(partition->dbf.query(*partition->db_handle,0,0,0,query_cols,0,nr_cols, 0,&res) < 0) { LM_ERR("error while querying database\n"); goto error; } nr_rows = RES_ROW_N(res); rows = RES_ROWS(res); if(nr_rows == 0) { LM_WARN("no dispatching data in the db -- empty destination set\n"); goto load_done; } cnt = 0; for(i=0; i skipping\n"); continue; } id = VAL_INT(values); /* uri */ get_str_from_dbval( "URI", values+1, 1/*not_null*/, 1/*not_empty*/, uri, error2); /* sock */ get_str_from_dbval( "SOCKET", values+2, 0/*not_null*/, 0/*not_empty*/, attrs, error2); if ( attrs.len ) { if (parse_phostport( attrs.s, attrs.len, &host.s, &host.len, &port, &proto)!=0){ LM_ERR("socket description <%.*s> is not valid -> ignoring\n", attrs.len,attrs.s); sock = NULL; } else { sock = grep_sock_info( &host, port, proto); if (sock == NULL) { LM_ERR("socket <%.*s> is not local to opensips (we must " "listen on it) -> ignoring it\n", attrs.len, attrs.s); } } } else { sock = NULL; } /* weight */ if (VAL_NULL(values+3)) weight = 1; else weight = VAL_INT(values+3); /* attrs */ get_str_from_dbval( "ATTRIBUTES", values+4, 0/*not_null*/, 0/*not_empty*/, attrs, error2); /* priority */ if (VAL_NULL(values+5)) prio = 0; else prio = VAL_INT(values+5); /* state */ if (!use_state_col || VAL_NULL(values+7)) /* active state */ state = 0; else state = VAL_INT(values+7); get_str_from_dbval( "DESCIPTION", values+6, 0/*not_null*/, 0/*not_empty*/, description, error2); if (add_dest2list(id, uri, sock, state, weight, prio, attrs, description, d_data) != 0) { LM_WARN("failed to add destination <%.*s> in group %d\n", uri.len,uri.s,id); continue; } else { cnt++; } } if (cnt==0) { LM_WARN("No record loaded from db, running on empty sets\n"); } else { if(reindex_dests( d_data )!=0) { LM_ERR("error on reindex\n"); goto error2; } } load_done: partition->dbf.free_result(*partition->db_handle, res); return d_data; error: ds_destroy_data_set( d_data ); return NULL; error2: ds_destroy_data_set( d_data ); partition->dbf.free_result(*partition->db_handle, res); return NULL; } int ds_reload_db(ds_partition_t *partition) { ds_data_t *old_data; ds_data_t *new_data; new_data = ds_load_data(partition, ds_persistent_state); if (new_data==NULL) { LM_ERR("failed to load the new data, dropping the reload\n"); return -1; } lock_start_write( partition->lock ); /* no more activ readers -> do the swapping */ old_data = *partition->data; *partition->data = new_data; lock_stop_write( partition->lock ); /* destroy old data */ if (old_data) { /* copy the state of the destinations from the old set * (for the matching ids) */ ds_inherit_state( old_data, new_data); ds_destroy_data_set( old_data ); } /* update the Black Lists with the new gateways */ populate_ds_bls( new_data->sets, partition->name); return 0; } /** * */ unsigned int ds_get_hash(str *x, str *y) { char* p; register unsigned v; register unsigned h; if(!x && !y) return 0; h=0; if(x) { p=x->s; if (x->len>=4) { for (; p<=(x->s+x->len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } } v=0; for (;p<(x->s+x->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } if(y) { p=y->s; if (y->len>=4) { for (; p<=(y->s+y->len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } } v=0; for (;p<(y->s+y->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } h=((h)+(h>>11))+((h>>13)+(h>>23)); return (h)?h:1; } /* * gets the part of the uri we will use as a key for hashing * params: key1 - will be filled with first part of the key * (uri user or "" if no user) * key2 - will be filled with the second part of the key * (uri host:port) * uri - str with the whole uri * parsed_uri - struct sip_uri pointer with the parsed uri * (it must point inside uri). It can be null * (in this case the uri will be parsed internally). * flags - if & DS_HASH_USER_ONLY, only the user part of the uri * will be used * returns: -1 on error, 0 on success */ static inline int get_uri_hash_keys(str* key1, str* key2, str* uri, struct sip_uri* parsed_uri, int flags) { struct sip_uri tmp_p_uri; /* used only if parsed_uri==0 */ unsigned short proto; if (parsed_uri==0) { if (parse_uri(uri->s, uri->len, &tmp_p_uri)<0) { LM_ERR("invalid uri %.*s\n", uri->len, uri->len?uri->s:""); goto error; } parsed_uri=&tmp_p_uri; } /* uri sanity checks */ if (parsed_uri->host.s==0) { LM_ERR("invalid uri, no host present: %.*s\n", uri->len, uri->len?uri->s:""); goto error; } /* we want: user@host:port if port is not the defaut one * user@host if port is the default one * user if the user flag is set*/ *key1=parsed_uri->user; key2->s=0; key2->len=0; if (!(flags & DS_HASH_USER_ONLY)) { /* key2=host */ *key2=parsed_uri->host; /* add port if needed */ if (parsed_uri->port.s!=0) { /* uri has a port */ /* skip port if the default one ( first extract proto from URI) */ if ( get_uri_port(parsed_uri, &proto) && parsed_uri->port_no != protos[proto].default_port ) key2->len+=parsed_uri->port.len+1 /* ':' */; } } if (key1->s==0) { LM_WARN("empty username in: %.*s\n", uri->len, uri->len?uri->s:""); } return 0; error: return -1; } /** * */ int ds_hash_fromuri(struct sip_msg *msg, unsigned int *hash, int ds_flags) { str from; str key1; str key2; if(msg==NULL || hash == NULL) { LM_ERR("bad parameters\n"); return -1; } if(parse_from_header(msg)<0) { LM_ERR("cannot parse From hdr\n"); return -1; } if(msg->from==NULL || get_from(msg)==NULL) { LM_ERR("cannot get From uri\n"); return -1; } from = get_from(msg)->uri; trim(&from); if (get_uri_hash_keys(&key1, &key2, &from, 0, ds_flags)<0) return -1; *hash = ds_get_hash(&key1, &key2); return 0; } /** * */ int ds_hash_touri(struct sip_msg *msg, unsigned int *hash, int ds_flags) { str to; str key1; str key2; if(msg==NULL || hash == NULL) { LM_ERR("bad parameters\n"); return -1; } if ((msg->to==0) && ((parse_headers(msg, HDR_TO_F, 0)==-1) || (msg->to==0))) { LM_ERR("cannot parse To hdr\n"); return -1; } to = get_to(msg)->uri; trim(&to); if (get_uri_hash_keys(&key1, &key2, &to, 0, ds_flags)<0) return -1; *hash = ds_get_hash(&key1, &key2); return 0; } /** * */ int ds_hash_callid(struct sip_msg *msg, unsigned int *hash) { str cid; if(msg==NULL || hash == NULL) { LM_ERR("bad parameters\n"); return -1; } if(msg->callid==NULL && ((parse_headers(msg, HDR_CALLID_F, 0)==-1) || (msg->callid==NULL)) ) { LM_ERR("cannot parse Call-Id\n"); return -1; } cid.s = msg->callid->body.s; cid.len = msg->callid->body.len; trim(&cid); *hash = ds_get_hash(&cid, NULL); return 0; } int ds_hash_ruri(struct sip_msg *msg, unsigned int *hash, int ds_flags) { str* uri; str key1; str key2; if(msg==NULL || hash == NULL) { LM_ERR("bad parameters\n"); return -1; } if (parse_sip_msg_uri(msg)<0){ LM_ERR("bad request uri\n"); return -1; } uri=GET_RURI(msg); if (get_uri_hash_keys(&key1, &key2, uri, &msg->parsed_uri, ds_flags)<0) return -1; *hash = ds_get_hash(&key1, &key2); return 0; } int ds_hash_authusername(struct sip_msg *msg, unsigned int *hash) { /* Header, which contains the authorization */ struct hdr_field* h = 0; /* The Username */ str username = {0, 0}; /* The Credentials from this request */ auth_body_t* cred; if(msg==NULL || hash == NULL) { LM_ERR("bad parameters\n"); return -1; } if (parse_headers(msg, HDR_PROXYAUTH_F, 0) == -1) { LM_ERR("error parsing headers!\n"); return -1; } if (msg->proxy_auth && !msg->proxy_auth->parsed) parse_credentials(msg->proxy_auth); if (msg->proxy_auth && msg->proxy_auth->parsed) { h = msg->proxy_auth; } if (!h) { if (parse_headers(msg, HDR_AUTHORIZATION_F, 0) == -1) { LM_ERR("error parsing headers!\n"); return -1; } if (msg->authorization && !msg->authorization->parsed) parse_credentials(msg->authorization); if (msg->authorization && msg->authorization->parsed) { h = msg->authorization; } } if (!h) { LM_DBG("No Authorization-Header!\n"); return 1; } cred=(auth_body_t*)(h->parsed); if (!cred || !cred->digest.username.user.len) { LM_ERR("No Authorization-Username or Credentials!\n"); return 1; } username.s = cred->digest.username.user.s; username.len = cred->digest.username.user.len; trim(&username); *hash = ds_get_hash(&username, NULL); return 0; } int ds_hash_pvar(struct sip_msg *msg, unsigned int *hash) { /* The String to create the hash */ str hash_str = {0, 0}; if(msg==NULL || hash == NULL || hash_param_model == NULL) { LM_ERR("bad parameters\n"); return -1; } if (pv_printf_s(msg, hash_param_model, &hash_str)<0) { LM_ERR("error - cannot print the format\n"); return -1; } /* Remove empty spaces */ trim(&hash_str); if (hash_str.len <= 0) { LM_ERR("String is empty!\n"); return -1; } LM_DBG("Hashing %.*s!\n", hash_str.len, hash_str.s); *hash = ds_get_hash(&hash_str, NULL); return 0; } static inline int ds_get_index(int group, ds_set_p *index, ds_partition_t *partition) { ds_set_p si = NULL; if(index==NULL || group<0 || (*partition->data)->sets==NULL) return -1; /* get the index of the set */ for ( si=(*partition->data)->sets ; si ; si = si->next ) { if(si->id == group) { *index = si; break; } } if(si==NULL) { LM_ERR("destination set [%d] not found in partition [%.*s]\n", group, partition->name.len, partition->name.s); return -1; } return 0; } int ds_update_dst(struct sip_msg *msg, str *uri, struct socket_info *sock, int mode) { struct action act; uri_type utype; int typelen; switch(mode) { case 1: act.type = SET_HOSTPORT_T; act.elem[0].type = STR_ST; utype = str2uri_type(uri->s); if (utype == ERROR_URI_T) { LM_ERR("Unknown uri type\n"); return -1; } typelen = uri_typestrlen(utype); act.elem[0].u.s.s = uri->s + typelen + 1; act.elem[0].u.s.len = uri->len - typelen - 1; act.next = 0; if (do_action(&act, msg) < 0) { LM_ERR("error while setting host\n"); return -1; } break; default: if (set_dst_uri(msg, uri) < 0) { LM_ERR("error while setting dst uri\n"); return -1; } break; } if (sock) msg->force_send_socket = sock; return 0; } static int is_default_destination_entry(ds_set_p idx,int i, int ds_use_default) { return ds_use_default!=0 && i==(idx->nr-1); } static int count_inactive_destinations(ds_set_p idx, int ds_use_default) { int count = 0, i; for(i=0; inr; i++) if( !dst_is_active(idx->dlist[i]) ) /* only count inactive entries that are not default */ if(!is_default_destination_entry(idx, i, ds_use_default)) count++; return count; } static inline int push_ds_2_avps( ds_dest_t *ds, ds_partition_t *partition ) { char buf[PTR_STRING_SIZE]; /* a hexa string */ int_str avp_val; avp_val.s.len = 1 + snprintf( buf, PTR_STR_SIZE, "%p", ds->sock ); avp_val.s.s = buf; if(add_avp(AVP_VAL_STR| partition->sock_avp_type, partition->sock_avp_name, avp_val)!=0) { LM_ERR("failed to add SOCK avp\n"); return -1; } avp_val.s = ds->dst_uri; if(add_avp(AVP_VAL_STR| partition->dst_avp_type, partition->dst_avp_name, avp_val)!=0) { LM_ERR("failed to add DST avp\n"); return -1; } if (partition->attrs_avp_name >= 0) { avp_val.s = ds->attrs; if(add_avp(AVP_VAL_STR| partition->attrs_avp_type, partition->attrs_avp_name, avp_val)!=0) { LM_ERR("failed to add ATTR avp\n"); return -1; } } return 0; } /** * */ int ds_select_dst(struct sip_msg *msg, ds_select_ctl_p ds_select_ctl, ds_selected_dst_p selected_dst, int ds_flags) { int i, j, cnt, i_unwrapped, set_size; unsigned int ds_hash, ds_rand; int_str avp_val; int ds_id; ds_set_p idx = NULL; int inactive_dst_count = 0; int destination_entries_to_skip = 0; /* used to sort the destinations for LB algo */ ds_dest_p dest = NULL; ds_dest_p selected = NULL; static ds_dest_p *sorted_set = NULL; int rc; if(msg==NULL) { LM_ERR("bad parameters\n"); return -1; } if ( (*ds_select_ctl->partition->data)->sets==NULL) { LM_DBG("empty destination set\n"); return -1; } if((ds_select_ctl->mode==0) && (ds_flags&DS_FORCE_DST) && (msg->dst_uri.s!=NULL || msg->dst_uri.len>0)) { LM_ERR("destination already set [%.*s]\n", msg->dst_uri.len, msg->dst_uri.s); return -1; } /* access ds data under reader's lock */ lock_start_read( ds_select_ctl->partition->lock ); /* get the index of the set */ if(ds_get_index(ds_select_ctl->set, &idx, ds_select_ctl->partition)!=0) { LM_ERR("destination set [%d] not found\n", ds_select_ctl->set); goto error; } if (idx->nr == 0) { LM_DBG("destination set [%d] is empty!\n", idx->id); goto error; } if (idx->active_nr == 0) { LM_DBG("no active destinations in set [%d] !\n", idx->id); goto error; } /* calculate the real size of the set, depending on the USE_DEFAULT value * This size will be all the time higher than 0 (>=1) */ set_size = (ds_flags&DS_USE_DEFAULT && idx->nr>1) ? idx->nr-1 : idx->nr ; /* at this point we know for sure that we have * at least one active destination */ LM_DBG("set [%d], using alg [%d], size [%d], used size [%d], " "active size [%d]\n", ds_select_ctl->set, ds_select_ctl->alg, idx->nr, set_size, idx->active_nr); /* hash value used for picking the destination */ ds_hash = 0; /* id of the destination candidate (still to check if active) */ ds_id = -1; /* final selected destination */ selected = NULL; switch(ds_select_ctl->alg) { case 0: if(ds_hash_callid(msg, &ds_hash)!=0) { LM_ERR("can't get callid hash\n"); goto error; } break; case 1: if(ds_hash_fromuri(msg, &ds_hash, ds_flags)!=0) { LM_ERR("can't get From uri hash\n"); goto error; } break; case 2: if(ds_hash_touri(msg, &ds_hash, ds_flags)!=0) { LM_ERR("can't get To uri hash\n"); goto error; } break; case 3: if (ds_hash_ruri(msg, &ds_hash, ds_flags)!=0) { LM_ERR("can't get ruri hash\n"); goto error; } break; case 4: /* round robin */ ds_id = (idx->last+1) % set_size; break; case 5: i = ds_hash_authusername(msg, &ds_hash); switch (i) { case 0: /* Authorization-Header found: Nothing to be done here */ break; case 1: /* No Authorization found: Use round robin */ ds_id = idx->last; idx->last = (idx->last+1) % set_size; break; default: LM_ERR("can't get authorization hash\n"); goto error; break; } break; case 6: ds_hash = rand(); break; case 7: if (ds_hash_pvar(msg, &ds_hash)!=0) { LM_ERR("can't get PV hash\n"); goto error; } break; case 8: ds_id = 0; break; case 9: if (!ds_has_pattern && ds_pattern_prefix.len == 0 ) { LM_WARN("no pattern specified - using first entry...\n"); ds_select_ctl->alg = 8; break; } if (ds_pvar_algo(msg, idx, &sorted_set, ds_flags&DS_USE_DEFAULT) <= 0) { LM_ERR("can't get destination index\n"); goto error; } selected = sorted_set[0]; break; default: LM_WARN("dispatching via [%d] with unknown algo [%d]" ": defaulting to 0 - first entry\n", ds_select_ctl->set, ds_select_ctl->alg); ds_id = 0; } /* any destination selected yet? */ if (selected==NULL) { LM_DBG("hash [%u], candidate [%d], weight sum [%u]\n", ds_hash, ds_id, idx->dlist[set_size-1].running_weight); /* any candidate selected yet */ if (ds_id==-1) { /* no candidate yet -> do it based on hash and weights */ if (idx->dlist[set_size-1].running_weight) { ds_rand = ds_hash % idx->dlist[set_size-1].running_weight; /* get the ds id based on weights */ for( ds_id=0 ; ds_iddlist[ds_id].running_weight) break; } else { /* get a candidate simply based on hash */ ds_id = ds_hash % set_size; } } LM_DBG("candidate is [%u]\n",ds_id); /* now we have a candidate, so we need to check if active or not */ i=ds_id; while ( !dst_is_active(idx->dlist[i]) ) { /* get a next candidate */ if (ds_hash==0) { /* for algs with no hash, simple get the next in the list */ i = (i+1) % set_size; } else { /* use the hash and weights over active destinations only ; * if USE_DEFAULT is set, do a -1 if the default (last) * destination is active (we want to skip it) */ cnt = idx->active_nr - ((ds_flags&DS_USE_DEFAULT && dst_is_active(idx->dlist[idx->nr-1]))?1:0); if (cnt) { /* weights or not ? */ if (idx->dlist[set_size-1].active_running_weight) { ds_rand = ds_hash % idx->dlist[set_size-1].active_running_weight; /* get the ds id based on active weights */ for( i=0 ; idlist[i]) && (ds_randdlist[i].active_running_weight) ) break; } else { j = ds_hash % cnt; /* translate this index to the full set of dsts */ for ( i=0 ; idlist[i]) ) j--; if (j<0) break; } } } /* i reflects the new candidate */ } LM_DBG("new candidate is [%u]\n",i); if(i==ds_id) { if (ds_flags&DS_USE_DEFAULT) { i = idx->nr-1; if (!dst_is_active(idx->dlist[i])) goto error; break; } else { goto error; } } } LM_DBG("using destination [%u]\n",i); ds_id = i; selected = &idx->dlist[ds_id]; } /* remember the last used destination */ idx->last = ds_id; /* start pushing the destinations to SIP level */ cnt = 0; rc = 1; if(ds_select_ctl->set_destination && ((rc = ds_update_dst(msg, &selected->dst_uri, selected->sock, ds_select_ctl->mode)) != 0) ) { LM_ERR("cannot set dst addr\n"); goto error; } if(rc == 0){ selected->chosen_count++; LM_DBG("aici_intrat [%hu]\n", selected->chosen_count); } /* Save the selected destination for multilist failover */ if (selected_dst->uri.s != NULL) { pkg_free(selected_dst->uri.s); memset(&selected_dst->uri, 0, sizeof(str)); } if (pkg_str_dup(&selected_dst->uri, &selected->dst_uri) != 0) { LM_ERR("cannot set selected_dst uri\n"); goto error; } if (selected->sock) { selected_dst->socket.len = 1 + snprintf( selected_dst->socket.s, PTR_STR_SIZE, "%p", selected->sock ); } else { selected_dst->socket.len = 0; } LM_DBG("selected [%d-%d/%d] <%.*s>\n", ds_select_ctl->alg, ds_select_ctl->set, ds_id, selected->dst_uri.len, selected->dst_uri.s); if(!(ds_flags&DS_FAILOVER_ON)) goto done; if(ds_select_ctl->reset_AVP) { /* do some AVP cleanup before start populating new ones */ destroy_avps(0/*all types*/, ds_select_ctl->partition->dst_avp_name,1); destroy_avps(0/*all types*/, ds_select_ctl->partition->grp_avp_name,1); destroy_avps(0/*all types*/, ds_select_ctl->partition->cnt_avp_name,1); destroy_avps(0/*all types*/,ds_select_ctl->partition->sock_avp_name,1); if (ds_select_ctl->partition->attrs_avp_name>0) destroy_avps( 0 /*all types*/, ds_select_ctl->partition->attrs_avp_name, 1 /*all*/); ds_select_ctl->reset_AVP = 0; } if((ds_flags&DS_USE_DEFAULT) && ds_id!=idx->nr-1) { if (push_ds_2_avps( &idx->dlist[idx->nr-1], ds_select_ctl->partition ) != 0 ) goto error; cnt++; } inactive_dst_count = count_inactive_destinations(idx, ds_flags&DS_USE_DEFAULT); /* don't count inactive and default entries into total */ destination_entries_to_skip = idx->nr - inactive_dst_count - (ds_flags&DS_USE_DEFAULT?1:0); destination_entries_to_skip -= ds_select_ctl->max_results; /* add to avp */ for(i_unwrapped = ds_id-1+idx->nr; i_unwrapped>ds_id; i_unwrapped--) { i = i_unwrapped % idx->nr; dest = (ds_select_ctl->alg == 9 ? sorted_set[i] : &idx->dlist[i]); if ( !dst_is_active(*dest) || ((ds_flags&DS_USE_DEFAULT) && i==(idx->nr-1)) ) continue; if(destination_entries_to_skip > 0) { LM_DBG("skipped entry [%d/%d] (would create more than %i " "results)\n", ds_select_ctl->set, i, ds_select_ctl->max_results); destination_entries_to_skip--; continue; } LM_DBG("using entry [%d/%d]\n", ds_select_ctl->set, i); if (push_ds_2_avps( dest, ds_select_ctl->partition ) != 0 ) goto error; cnt++; } /* add to avp the first used dst */ avp_val.s = selected->uri; if(add_avp(AVP_VAL_STR|ds_select_ctl->partition->dst_avp_type, ds_select_ctl->partition->dst_avp_name, avp_val)!=0) goto error; cnt++; done: if (ds_select_ctl->partition->attrs_avp_name>0) { avp_val.s = selected->attrs; if(add_avp(AVP_VAL_STR | ds_select_ctl->partition->attrs_avp_type, ds_select_ctl->partition->attrs_avp_name,avp_val)!=0) goto error; } /* add to avp the group id */ avp_val.n = ds_select_ctl->set; if(add_avp(ds_select_ctl->partition->grp_avp_type, ds_select_ctl->partition->grp_avp_name, avp_val)!=0) goto error; /* add to avp the number of dst */ avp_val.n = cnt; if(add_avp(ds_select_ctl->partition->cnt_avp_type, ds_select_ctl->partition->cnt_avp_name, avp_val)!=0) goto error; lock_stop_read( ds_select_ctl->partition->lock ); return 1; error: lock_stop_read( ds_select_ctl->partition->lock ); return -1; } int ds_next_dst(struct sip_msg *msg, int mode, ds_partition_t *partition) { struct socket_info *sock; struct usr_avp *avp; struct usr_avp *tmp_avp; struct usr_avp *attr_avp; int_str avp_value; int_str sock_avp_value; tmp_avp = search_first_avp(partition->dst_avp_type, partition->dst_avp_name, NULL, 0); if(tmp_avp==NULL) return -1; /* used avp deleted -- strange */ /* get AVP with next destination URI */ avp = search_next_avp(tmp_avp, &avp_value); destroy_avp(tmp_avp); /* remove old attribute AVP (from prev destination) */ if (partition->attrs_avp_name >= 0) { attr_avp = search_first_avp(partition->attrs_avp_type, partition->attrs_avp_name, NULL, 0); if (attr_avp) destroy_avp(attr_avp); } if(avp==NULL || !(avp->flags&AVP_VAL_STR)) return -1; /* no more avps or value is int */ /* get AVP with next destination socket */ tmp_avp = search_first_avp(partition->sock_avp_type, partition->sock_avp_name, &sock_avp_value, 0); if (!tmp_avp) { /* this shuold not happen, it is a bogus state */ sock = NULL; } else { if (sscanf( sock_avp_value.s.s, "%p", (void**)&sock ) != 1) sock = NULL; destroy_avp(tmp_avp); } LM_DBG("using [%.*s]\n", avp_value.s.len, avp_value.s.s); if( ds_update_dst(msg, &avp_value.s, sock, mode) != 0) { LM_ERR("cannot set dst addr\n"); return -1; } return 1; } int ds_mark_dst(struct sip_msg *msg, int mode, ds_partition_t *partition) { int group, ret; struct usr_avp *prev_avp; int_str avp_value; prev_avp = search_first_avp(partition->grp_avp_type, partition->grp_avp_name, &avp_value, 0); if(prev_avp==NULL || prev_avp->flags&AVP_VAL_STR) return -1; /* grp avp deleted -- strange */ group = avp_value.n; prev_avp = search_first_avp(partition->dst_avp_type, partition->dst_avp_name, &avp_value, 0); if(prev_avp==NULL || !(prev_avp->flags&AVP_VAL_STR)) return -1; /* dst avp deleted -- strange */ if(mode==1) { /* set as "active" */ ret = ds_set_state(group, &avp_value.s, DS_INACTIVE_DST|DS_PROBING_DST, 0, partition); } else if(mode==2) { /* set as "probing" */ ret = ds_set_state(group, &avp_value.s, DS_PROBING_DST, 1, partition); if (ret == 0) ret = ds_set_state(group, &avp_value.s, DS_INACTIVE_DST, 0, partition); } else { /* set as "inactive" */ ret = ds_set_state(group, &avp_value.s, DS_INACTIVE_DST, 1, partition); if (ret == 0) ret = ds_set_state(group, &avp_value.s, DS_PROBING_DST, 0, partition); } LM_DBG("mode [%d] grp [%d] dst [%.*s]\n", mode, group, avp_value.s.len, avp_value.s.s); return (ret==0)?1:-1; } /* event parameters */ static str partition_str = str_init("partition"); static str group_str = str_init("group"); static str address_str = str_init("address"); static str status_str = str_init("status"); static str inactive_str = str_init("inactive"); static str active_str = str_init("active"); int ds_set_state(int group, str *address, int state, int type, ds_partition_t *partition) { int i=0; ds_set_p idx = NULL; evi_params_p list = NULL; int old_flags; if ( (*partition->data)->sets==NULL ){ LM_DBG("empty destination set\n"); return -1; } /* access ds data under reader's lock */ lock_start_read( partition->lock ); /* get the index of the set */ if(ds_get_index(group, &idx, partition)!=0) { LM_ERR("destination set [%d] not found\n", group); lock_stop_read( partition->lock ); return -1; } while(inr) { if(idx->dlist[i].uri.len==address->len && strncasecmp(idx->dlist[i].uri.s, address->s, address->len)==0) { /* remove the Probing/Inactive-State? Set the fail-count to 0. */ if (state == DS_PROBING_DST) { if (type) { if (idx->dlist[i].flags & DS_INACTIVE_DST) { LM_INFO("Ignoring the request to set this destination" " to probing: It is already inactive!\n"); lock_stop_read( partition->lock ); return 0; } idx->dlist[i].failure_count++; /* Fire only, if the Threshold is reached. */ if (idx->dlist[i].failure_count < probing_threshhold) { lock_stop_read( partition->lock ); return 0; } if (idx->dlist[i].failure_count > probing_threshhold) idx->dlist[i].failure_count = probing_threshhold; } } /* Reset the Failure-Counter */ if ((state & DS_RESET_FAIL_DST) > 0) { idx->dlist[i].failure_count = 0; state &= ~DS_RESET_FAIL_DST; } /* set the new state of the destination */ old_flags = idx->dlist[i].flags; if(type) idx->dlist[i].flags |= state; else idx->dlist[i].flags &= ~state; if ( idx->dlist[i].flags != old_flags) { /* state actually changed -> do all updates */ idx->dlist[i].flags |= DS_STATE_DIRTY_DST; /* update info on active destinations */ if ( ((old_flags&(DS_PROBING_DST|DS_INACTIVE_DST))?0:1) != ((idx->dlist[i].flags&(DS_PROBING_DST|DS_INACTIVE_DST))?0:1) ) /* this destination switched state between * disabled <> enabled -> update active info */ re_calculate_active_dsts( idx ); } if (dispatch_evi_id == EVI_ERROR) { LM_ERR("event not registered %d\n", dispatch_evi_id); } else if (evi_probe_event(dispatch_evi_id)) { if (!(list = evi_get_params())) { lock_stop_read( partition->lock ); return 0; } if (partition != default_partition && evi_param_add_str(list,&partition_str,&partition->name)){ LM_ERR("unable to add partition parameter\n"); evi_free_params(list); lock_stop_read( partition->lock ); return 0; } if (evi_param_add_int(list, &group_str, &group)) { LM_ERR("unable to add group parameter\n"); evi_free_params(list); lock_stop_read( partition->lock ); return 0; } if (evi_param_add_str(list, &address_str, address)) { LM_ERR("unable to add address parameter\n"); evi_free_params(list); lock_stop_read( partition->lock ); return 0; } if (evi_param_add_str(list, &status_str, type ? &inactive_str : &active_str)) { LM_ERR("unable to add status parameter\n"); evi_free_params(list); lock_stop_read( partition->lock ); return 0; } if (evi_raise_event(dispatch_evi_id, list)) { LM_ERR("unable to send event\n"); } } else { LM_DBG("no event sent\n"); } lock_stop_read( partition->lock ); return 0; } i++; } lock_stop_read( partition->lock ); return -1; } /* Checks, if the request (sip_msg *_m) comes from a host in a set * (set-id or -1 for all sets) */ int ds_is_in_list(struct sip_msg *_m, gparam_t *gp_ip, gparam_t *gp_port, int set, int active_only, ds_partition_t *partition) { pv_value_t val; ds_set_p list; struct ip_addr *ip; int_str avp_val; int port; int j,k; /* get the address to test */ if (fixup_get_svalue(_m, gp_ip, &val.rs) != 0) { LM_ERR("bad IP pseudo-variable!\n"); return -1; } if ( (ip=str2ip( &val.rs ))==NULL ) { LM_ERR("IP val is not IP <%.*s>\n",val.rs.len,val.rs.s); return -1; } /* get the port to test */ if (gp_port) { if (fixup_get_ivalue(_m, gp_port, &val.ri) != 0) { LM_ERR("bad port pseudo-variable!\n"); return -1; } port = val.ri; } else { port = 0; } memset(&val, 0, sizeof(pv_value_t)); val.flags = PV_VAL_INT|PV_TYPE_INT; /* access ds data under reader's lock */ lock_start_read( partition->lock ); for(list = (*partition->data)->sets ; list!= NULL; list= list->next) { if ((set == -1) || (set == list->id)) { /* interate through all elements/destinations in the list */ for(j=0; jnr; j++) { /* interate through all IPs of each destination */ for(k=0 ; kdlist[j].ips_cnt ; k++ ) { if ( (list->dlist[j].ports[k]==0 || port==0 || port==list->dlist[j].ports[k]) && ip_addr_cmp( ip, &list->dlist[j].ips[k]) ) { /* matching destination */ if (active_only && !dst_is_active(list->dlist[j]) ) continue; if(set==-1 && ds_setid_pvname.s!=0) { val.ri = list->id; if(pv_set_value(_m, &ds_setid_pv, (int)EQ_T, &val)<0) { LM_ERR("setting PV failed\n"); goto error; } } if (partition->attrs_avp_name>= 0) { avp_val.s = list->dlist[j].attrs; if(add_avp(AVP_VAL_STR|partition->attrs_avp_type, partition->attrs_avp_name,avp_val)!=0) goto error; } lock_stop_read( partition->lock ); return 1; } } } } } error: lock_stop_read( partition->lock ); return -1; } int ds_print_mi_list(struct mi_node* rpl, ds_partition_t *partition, int flags) { int len, j; char* p; ds_set_p list; struct mi_node* node = NULL; struct mi_node* node1; struct mi_node* set_node = NULL; struct mi_attr* attr = NULL; if ( (*partition->data)->sets==NULL ) { LM_DBG("empty destination sets\n"); return 0; } /* access ds data under reader's lock */ lock_start_read( partition->lock ); for(list = (*partition->data)->sets ; list!= NULL; list= list->next) { p = int2str(list->id, &len); set_node= add_mi_node_child(rpl, MI_IS_ARRAY|MI_DUP_VALUE, "SET", 3, p, len); if(set_node == NULL) goto error; for(j=0; jnr; j++) { node= add_mi_node_child(set_node, MI_DUP_VALUE, "URI", 3, list->dlist[j].uri.s, list->dlist[j].uri.len); if(node == NULL) goto error; if (list->dlist[j].flags & DS_INACTIVE_DST) attr = add_mi_attr (node, 0, "state",5, "Inactive", 8); else if (list->dlist[j].flags & DS_PROBING_DST) attr = add_mi_attr (node, 0, "state",5, "Probing", 7); else attr = add_mi_attr (node, 0, "state",5, "Active", 6); if(attr == NULL) goto error; p = int2str(list->dlist[j].chosen_count, &len); attr = add_mi_attr (node, MI_DUP_VALUE, "first_hit_counter", 17, p, len); if(attr == NULL) goto error; if (list->dlist[j].sock) { p = socket2str(list->dlist[j].sock, NULL, &len, 0); if (p) { node1= add_mi_node_child(node, MI_DUP_VALUE, "socket", 6, p, len); if(node1 == NULL) goto error; } } if (list->dlist[j].attrs.s) { node1= add_mi_node_child(node, MI_DUP_VALUE, "attr", 4, list->dlist[j].attrs.s, list->dlist[j].attrs.len); if(node1 == NULL) goto error; } if (flags & MI_FULL_LISTING) { p = int2str(list->dlist[j].weight, &len); node1= add_mi_node_child(node, MI_DUP_VALUE, "weight", 6, p, len); if(node1 == NULL) goto error; p = int2str(list->dlist[j].priority, &len); node1 = add_mi_node_child(node, MI_DUP_VALUE, "priority", 8, p, len); if(node1 == NULL) goto error; if (list->dlist[j].description.len) { node1= add_mi_node_child(node, MI_DUP_VALUE, "description", 11, list->dlist[j].description.s, list->dlist[j].description.len); if(node1 == NULL) goto error; } } } } lock_stop_read( partition->lock ); return 0; error: lock_stop_read( partition->lock ); return -1; } /** * Callback-Function for the OPTIONS-Request * This Function is called, as soon as the Transaction is finished * (e. g. a Response came in, the timeout was hit, ...) * */ static void ds_options_callback( struct cell *t, int type, struct tmcb_params *ps ) { str uri = {0, 0}; /* The Param does contain the group, in which the failed host * can be found.*/ if (!ps->param) { LM_DBG("No parameter provided, OPTIONS-Request was finished" " with code %d\n", ps->code); return; } /* The param is a (void*) Pointer, so we need to dereference it and * cast it to an int. */ ds_options_callback_param_t *cb_param = (ds_options_callback_param_t*)(*ps->param); /* The SIP-URI is taken from the Transaction. * Remove the "To: " (s+4) and the trailing new-line (s - 4 (To: ) * - 2 (\r\n)). */ uri.s = t->to.s + 4; uri.len = t->to.len - 6; LM_DBG("OPTIONS-Request was finished with code %d (to %.*s, group %d)\n", ps->code, uri.len, uri.s, cb_param->set_id); /* ps->code contains the result-code of the request; * We accept "200 OK" by default and the custom codes * defined in options_reply_codes parameter*/ if ((ps->code == 200) || check_options_rplcode(ps->code)) { /* Set the according entry back to "Active": * remove the Probing/Inactive Flag and reset the failure counter. */ if (ds_set_state(cb_param->set_id, &uri, DS_INACTIVE_DST|DS_PROBING_DST|DS_RESET_FAIL_DST, 0, cb_param->partition) != 0) { LM_ERR("Setting the state failed (%.*s, group %d)\n", uri.len, uri.s, cb_param->set_id); } } /* if we always probe, and we get a timeout * or a reponse that is not within the allowed * reply codes, then disable*/ if(ds_probing_mode==1 && ps->code != 200 && (ps->code == 408 || !check_options_rplcode(ps->code))) { if (ds_set_state(cb_param->set_id, &uri, DS_PROBING_DST, 1, cb_param->partition) != 0) { LM_ERR("Setting the probing state failed (%.*s, group %d)\n", uri.len, uri.s, cb_param->set_id); } } return; } void shm_free_cb_param(void *param) { shm_free(param); } /* * Timer for checking inactive destinations * * This timer is regularly fired. */ void ds_check_timer(unsigned int ticks, void* param) { dlg_t *dlg; ds_set_p list; int j; ds_partition_t *partition = partitions; for (partition = partitions; partition; partition = partition->next){ /* Check for the list. */ if ( (*partition->data)->sets==NULL ) continue; /* access ds data under reader's lock */ lock_start_read( partition->lock ); /* Iterate over the groups and the entries of each group: */ for( list=(*partition->data)->sets ; list!= NULL ; list= list->next) { for(j=0; jnr; j++) { /* If list is probed by this proxy and the Flag of * the entry has "Probing" set, send a probe: */ if ( (!ds_probing_list || in_int_list(ds_probing_list, list->id)==0) && ((list->dlist[j].flags&DS_INACTIVE_DST)==0) && (ds_probing_mode==1 || (list->dlist[j].flags&DS_PROBING_DST)!=0 )) { LM_DBG("probing set #%d, URI %.*s\n", list->id, list->dlist[j].uri.len, list->dlist[j].uri.s); /* Execute the Dialog using the "request"-Method of the * TM-Module.*/ if (tmb.new_auto_dlg_uac(&ds_ping_from, &list->dlist[j].uri, list->dlist[j].sock?list->dlist[j].sock:probing_sock, &dlg) != 0 ) { LM_ERR("failed to create new TM dlg\n"); continue; } dlg->state = DLG_CONFIRMED; ds_options_callback_param_t *cb_param = shm_malloc(sizeof(*cb_param)); if (cb_param == NULL) { LM_CRIT("No more shared memory\n"); continue; } cb_param->partition = partition; cb_param->set_id = list->id; if (tmb.t_request_within(&ds_ping_method, NULL, NULL, dlg, ds_options_callback, (void*)cb_param, shm_free_cb_param) < 0) { LM_ERR("unable to execute dialog\n"); } tmb.free_dlg(dlg); } } } lock_stop_read( partition->lock ); } } int ds_count(struct sip_msg *msg, int set_id, const char *cmp, pv_spec_p ret, ds_partition_t *partition) { pv_value_t pv_val; ds_set_p set; ds_dest_p dst; int count, active = 0, inactive = 0, probing = 0; LM_DBG("Searching for set: %d, filtering: %d\n", set_id, *cmp); /* access ds data under reader's lock */ lock_start_read( partition->lock ); if ( ds_get_index( set_id, &set, partition)!=0 ) { LM_ERR("INVALID SET %d (not found)!\n",set_id); lock_stop_read( partition->lock ); return -1; } for (dst = set->dlist; dst; dst = dst->next) { if ( dst_is_active(*dst) ) { active++; } else if (dst->flags & DS_INACTIVE_DST) { inactive++; } else if (dst->flags & DS_PROBING_DST) { probing++; } } lock_stop_read( partition->lock ); switch (*cmp) { case DS_COUNT_ACTIVE: count = active; break; case DS_COUNT_ACTIVE|DS_COUNT_INACTIVE: case DS_COUNT_ACTIVE|DS_COUNT_PROBING: count = (*cmp & DS_COUNT_INACTIVE ? active + inactive : active + probing); break; case DS_COUNT_INACTIVE: case DS_COUNT_PROBING: count = (*cmp == DS_COUNT_INACTIVE ? inactive : probing); break; case DS_COUNT_INACTIVE|DS_COUNT_PROBING: count = inactive + probing; break; default: count = active + inactive + probing; } pv_val.flags = PV_TYPE_INT; pv_val.ri = count; if (pv_set_value(msg, ret, 0, &pv_val) != 0) { LM_ERR("SET OUTPUT value failed!\n"); return -1; } return 1; } opensips-2.2.2/modules/dispatcher/dispatch.h000066400000000000000000000155221300170765700211360ustar00rootroot00000000000000/** * dispatcher module * * Copyright (C) 2004-2006 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2004-07-31 first version, by daniel * 2007-01-11 Added a function to check if a specific gateway is in * a group (carsten) * 2007-02-09 Added active probing of failed destinations and automatic * re-enabling of destinations * 2007-05-08 Ported the changes to SVN-Trunk and renamed ds_is_domain * to ds_is_from_list. * 2009-05-18 Added support for weights for the destinations; * added support for custom "attrs" (opaque string) (bogdan) * 2013-12-02 Added support state persistency (restart and reload) (bogdan) * 2013-12-05 Added a safer reload mechanism based on locking read/writter (bogdan) */ #ifndef _DISPATCH_H_ #define _DISPATCH_H_ #include #include "../../pvar.h" #include "../../mod_fix.h" #include "../../parser/msg_parser.h" #include "../tm/tm_load.h" #include "../../db/db.h" #include "../../rw_locking.h" #define DS_HASH_USER_ONLY 1 /* use only the uri user part for hashing */ #define DS_FAILOVER_ON 2 /* store the other dest in avps */ #define DS_USE_DEFAULT 4 /* use last address in destination set as last option */ #define DS_FORCE_DST 8 /* if not set it will force overwriting the destination address if already set */ #define DS_INACTIVE_DST 1 /* inactive destination */ #define DS_PROBING_DST 2 /* checking destination */ #define DS_RESET_FAIL_DST 4 /* Reset-Failure-Counter */ #define DS_STATE_DIRTY_DST 8 /* STATE is dirty */ #define DS_PV_ALGO_MARKER "%u" /* Marker to indicate where the URI should be inserted in the pvar */ #define DS_PV_ALGO_MARKER_LEN (sizeof(DS_PV_ALGO_MARKER) - 1) #define DS_MAX_IPS 32 #define DS_COUNT_ACTIVE 1 #define DS_COUNT_INACTIVE 2 #define DS_COUNT_PROBING 4 #define DS_PARTITION_DELIM ':' #define DS_DEFAULT_PARTITION_NAME "default" #define MI_FULL_LISTING (1<<0) extern int ds_persistent_state; typedef struct _ds_dest { str uri; str dst_uri; /* Actual uri used in ds_select_dst ds_select_domain */ str attrs; str description; int flags; unsigned short weight; unsigned short running_weight; unsigned short active_running_weight; unsigned short priority; struct socket_info *sock; struct ip_addr ips[DS_MAX_IPS]; /* IP-Address of the entry */ unsigned short int ports[DS_MAX_IPS]; /* Port of the request URI */ unsigned short int protos[DS_MAX_IPS]; /* Protocol of the request URI */ unsigned short ips_cnt; unsigned short failure_count; unsigned short chosen_count; void *param; struct _ds_dest *next; } ds_dest_t, *ds_dest_p; typedef struct _ds_set { int id; /* id of dst set */ int nr; /* number of items in dst set */ int active_nr; /* number of active items in dst set */ int last; /* last used item in dst set */ ds_dest_p dlist; struct _ds_set *next; } ds_set_t, *ds_set_p; typedef struct _ds_data { ds_set_t *sets; unsigned int sets_no; } ds_data_t; typedef struct _ds_pvar_param { pv_spec_t pvar; int value; } ds_pvar_param_t, *ds_pvar_param_p; typedef struct _ds_partition { str name; /* Partition name */ str table_name; /* Table name */ str db_url; /* DB url */ db_con_t **db_handle; db_func_t dbf; ds_data_t **data; /* dispatching data holder */ rw_lock_t *lock; /* reader-writers lock for reloading the data */ int dst_avp_name; unsigned short dst_avp_type; int grp_avp_name; unsigned short grp_avp_type; int cnt_avp_name; unsigned short cnt_avp_type; int sock_avp_name; unsigned short sock_avp_type; int attrs_avp_name; unsigned short attrs_avp_type; struct _ds_partition *next; } ds_partition_t; typedef struct _ds_select_ctl { int set; /* set id to process */ ds_partition_t *partition; /* partition of set_id */ int alg; /* algorith to aply */ int mode; /* set destination uri */ int max_results; /* max destinaitons to process */ int reset_AVP; /* reset AVPs flag */ int set_destination; /* set destination flag */ int ds_flags; } ds_select_ctl_t, *ds_select_ctl_p; typedef struct { ds_partition_t *partition; int set_id; } ds_options_callback_param_t; typedef struct _ds_selected_dst { str uri; str socket; } ds_selected_dst, *ds_selected_dst_p; extern str ds_set_id_col; extern str ds_dest_uri_col; extern str ds_dest_sock_col; extern str ds_dest_state_col; extern str ds_dest_weight_col; extern str ds_dest_prio_col; extern str ds_dest_attrs_col; extern str ds_dest_description_col; extern pv_elem_t * hash_param_model; extern str ds_setid_pvname; extern pv_spec_t ds_setid_pv; /* Structure containing pointers to TM-functions */ struct tm_binds tmb; extern str ds_ping_method; extern str ds_ping_from; extern int probing_threshhold; /* number of failed requests, before a destination is taken into probing */ extern int ds_probing_mode; int init_ds_db(ds_partition_t *partition); int ds_connect_db(ds_partition_t *partition); void ds_disconnect_db(ds_partition_t *partition); int ds_reload_db(ds_partition_t *partition); int init_ds_data(ds_partition_t *partition); void ds_destroy_data(ds_partition_t *partition); int ds_update_dst(struct sip_msg *msg, str *uri, struct socket_info *sock, int mode); int ds_select_dst(struct sip_msg *msg, ds_select_ctl_p ds_select_ctl, ds_selected_dst_p selected_dst, int ds_flags); int ds_next_dst(struct sip_msg *msg, int mode, ds_partition_t *partition); int ds_set_state(int group, str *address, int state, int type, ds_partition_t *partition); int ds_mark_dst(struct sip_msg *msg, int mode, ds_partition_t *partition); int ds_print_mi_list(struct mi_node* rpl, ds_partition_t *partition, int flags); int ds_count(struct sip_msg *msg, int set_id, const char *cmp, pv_spec_p ret, ds_partition_t *partition); int ds_is_in_list(struct sip_msg *_m, gparam_t *addr, gparam_t *port, int set, int active_only, ds_partition_t *partition); /* * Timer for checking inactive destinations */ void ds_check_timer(unsigned int ticks, void* param); void ds_flusher_routine(unsigned int ticks, void* param); int check_options_rplcode(int code); /* pvar algorithm pattern parser */ void ds_pvar_parse_pattern(str); #endif opensips-2.2.2/modules/dispatcher/dispatcher.c000066400000000000000000001212041300170765700214530ustar00rootroot00000000000000/** * dispatcher module -- stateless load balancing * * Copyright (C) 2004-2005 FhG Fokus * Copyright (C) 2006-2010 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2004-07-31 first version, by daniel * 2007-01-11 Added a function to check if a specific gateway is in a group * (carsten - Carsten Bock, BASIS AudioNet GmbH) * 2007-02-09 Added active probing of failed destinations and automatic * re-enabling of destinations (carsten) * 2007-05-08 Ported the changes to SVN-Trunk and renamed ds_is_domain * to ds_is_from_list. (carsten) * 2007-07-18 Added support for load/reload groups from DB * reload triggered from ds_reload MI_Command (ancuta) * 2009-05-18 Added support for weights for the destinations; * added support for custom "attrs" (opaque string) (bogdan) * 2013-12-02 Added support state persistency (restart and reload) (bogdan) * 2013-12-05 Added a safer reload mechanism based on locking read/writter (bogdan) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../mi/mi.h" #include "../../dprint.h" #include "../../error.h" #include "../../trim.h" #include "../../route.h" #include "../../mem/mem.h" #include "../../mod_fix.h" #include "../../db/db.h" #include "dispatch.h" #include "ds_bl.h" #include "ds_fixups.h" #define DS_SET_ID_COL "setid" #define DS_DEST_URI_COL "destination" #define DS_DEST_SOCK_COL "socket" #define DS_DEST_STATE_COL "state" #define DS_DEST_WEIGHT_COL "weight" #define DS_DEST_PRIO_COL "priority" #define DS_DEST_ATTRS_COL "attrs" #define DS_DEST_DESCRIPTION_COL "description" #define DS_TABLE_NAME "dispatcher" #define DS_PARTITION_DELIM ':' /** parameters */ static str pvar_algo_param = str_init(""); str hash_pvar_param = {NULL, 0}; pv_elem_t * hash_param_model = NULL; int probing_threshhold = 3; /* number of failed requests, before a destination is taken into probing */ str ds_ping_method = {"OPTIONS",7}; str ds_ping_from = {"sip:dispatcher@localhost", 24}; static int ds_ping_interval = 0; int ds_probing_mode = 0; int ds_persistent_state = 1; int_list_t *ds_probing_list = NULL; /* db partiton info */ typedef struct _ds_db_head { str partition_name; str db_url; str table_name; str dst_avp; str grp_avp; str cnt_avp; str sock_avp; str attrs_avp; struct _ds_db_head *next; } ds_db_head_t; ds_db_head_t default_db_head = { str_init(DS_DEFAULT_PARTITION_NAME), {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, NULL }; ds_db_head_t *ds_db_heads = NULL; typedef struct { str name; str default_value; str* (*getter_func)(ds_db_head_t*); } partition_specific_param_t; #define DEF_GETTER_FUNC(PARAM_FIELD) str* getter_ ## PARAM_FIELD (ds_db_head_t \ *head) { \ return &(head-> PARAM_FIELD);} #define GETTER_FUNC(PARAM_FIELD) &getter_ ## PARAM_FIELD #define PARTITION_SPECIFIC_PARAM(PARAM_NAME, DEFAULT_VALUE) \ {str_init(#PARAM_NAME), str_init(DEFAULT_VALUE), GETTER_FUNC(PARAM_NAME)} /*db common attributes*/ str ds_set_id_col = str_init(DS_SET_ID_COL); str ds_dest_uri_col = str_init(DS_DEST_URI_COL); str ds_dest_sock_col = str_init(DS_DEST_SOCK_COL); str ds_dest_state_col = str_init(DS_DEST_STATE_COL); str ds_dest_weight_col= str_init(DS_DEST_WEIGHT_COL); str ds_dest_prio_col = str_init(DS_DEST_PRIO_COL); str ds_dest_attrs_col = str_init(DS_DEST_ATTRS_COL); str ds_dest_description_col = str_init(DS_DEST_DESCRIPTION_COL); str ds_setid_pvname = {NULL, 0}; pv_spec_t ds_setid_pv; static str options_reply_codes_str= {0, 0}; static int* options_reply_codes = NULL; static int options_codes_no; static char *probing_sock_s = NULL; struct socket_info *probing_sock = NULL; ds_partition_t *partitions = NULL, *default_partition = NULL; /* event */ static str dispatcher_event = str_init("E_DISPATCHER_STATUS"); event_id_t dispatch_evi_id; /** module functions */ static int mod_init(void); static int ds_child_init(int rank); static int w_ds_select_dst(struct sip_msg*, char*, char*); static int w_ds_select_dst_limited(struct sip_msg*, char*, char*, char*); static int w_ds_select_domain(struct sip_msg*, char*, char*); static int w_ds_select_domain_limited(struct sip_msg*, char*, char*, char*); static int w_ds_next_dst(struct sip_msg*, char*); static int w_ds_next_domain(struct sip_msg*, char*); static int w_ds_mark_dst(struct sip_msg*, char*, char*); static int w_ds_mark_dst1(struct sip_msg*, char *); static int w_ds_count(struct sip_msg*, char*, const char *, char*); static int w_ds_is_in_list(struct sip_msg*, char*, char*, char*, char*); static void destroy(void); static struct mi_root* ds_mi_set(struct mi_root* cmd, void* param); static struct mi_root* ds_mi_list(struct mi_root* cmd, void* param); static struct mi_root* ds_mi_reload(struct mi_root* cmd_tree, void* param); static int mi_child_init(void); /* Parameters setters */ static int set_partition_arguments(unsigned int type, void * val); static int set_probing_list(unsigned int type, void * val); static cmd_export_t cmds[]={ {"ds_select_dst", (cmd_function)w_ds_select_dst, 2, ds_select_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_select_dst", (cmd_function)w_ds_select_dst_limited, 3, ds_select_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_select_domain", (cmd_function)w_ds_select_domain, 2, ds_select_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_select_domain", (cmd_function)w_ds_select_domain_limited, 3, ds_select_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_next_dst", (cmd_function)w_ds_next_dst, 0, NULL , NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_next_dst", (cmd_function)w_ds_next_dst, 1, ds_next_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_next_domain", (cmd_function)w_ds_next_domain, 0, NULL , NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_next_domain", (cmd_function)w_ds_next_domain, 1, ds_next_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_mark_dst", (cmd_function)w_ds_mark_dst, 0, NULL , NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_mark_dst", (cmd_function)w_ds_mark_dst1, 1, fixup_sgp_null, NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_mark_dst", (cmd_function)w_ds_mark_dst, 2, ds_mark_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_is_in_list", (cmd_function)w_ds_is_in_list, 2, in_list_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"ds_is_in_list", (cmd_function)w_ds_is_in_list, 3, in_list_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"ds_is_in_list", (cmd_function)w_ds_is_in_list, 4, in_list_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"ds_count", (cmd_function)w_ds_count, 3, ds_count_fixup, NULL, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[]={ {"partition", STR_PARAM | USE_FUNC_PARAM, (void*)&set_partition_arguments}, {"db_url", STR_PARAM, &default_db_head.db_url.s}, {"table_name", STR_PARAM, &default_db_head.table_name.s}, {"setid_col", STR_PARAM, &ds_set_id_col.s}, {"destination_col", STR_PARAM, &ds_dest_uri_col.s}, {"socket_col", STR_PARAM, &ds_dest_sock_col.s}, {"state_col", STR_PARAM, &ds_dest_state_col.s}, {"weight_col", STR_PARAM, &ds_dest_weight_col.s}, {"priority_col", STR_PARAM, &ds_dest_prio_col.s}, {"attrs_col", STR_PARAM, &ds_dest_attrs_col.s}, {"description_col", STR_PARAM, &ds_dest_description_col.s}, {"dst_avp", STR_PARAM, &default_db_head.dst_avp.s}, {"grp_avp", STR_PARAM, &default_db_head.grp_avp.s}, {"cnt_avp", STR_PARAM, &default_db_head.cnt_avp.s}, {"sock_avp", STR_PARAM, &default_db_head.sock_avp.s}, {"attrs_avp", STR_PARAM, &default_db_head.attrs_avp.s}, {"hash_pvar", STR_PARAM, &hash_pvar_param.s}, {"setid_pvar", STR_PARAM, &ds_setid_pvname.s}, {"pvar_algo_pattern", STR_PARAM, &pvar_algo_param.s}, {"ds_probing_threshhold", INT_PARAM, &probing_threshhold}, {"ds_ping_method", STR_PARAM, &ds_ping_method.s}, {"ds_ping_from", STR_PARAM, &ds_ping_from.s}, {"ds_ping_interval", INT_PARAM, &ds_ping_interval}, {"ds_probing_mode", INT_PARAM, &ds_probing_mode}, {"options_reply_codes", STR_PARAM, &options_reply_codes_str.s}, {"ds_probing_sock", STR_PARAM, &probing_sock_s}, {"ds_probing_list", STR_PARAM|USE_FUNC_PARAM, (void*)set_probing_list}, {"ds_define_blacklist", STR_PARAM|USE_FUNC_PARAM, (void*)set_ds_bl}, {"persistent_state", INT_PARAM, &ds_persistent_state}, {0,0,0} }; static module_dependency_t *get_deps_ds_ping_interval(param_export_t *param) { if (*(int *)param->param_pointer <= 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "tm", DEP_ABORT); } static mi_export_t mi_cmds[] = { { "ds_set_state", 0, ds_mi_set, 0, 0, 0 }, { "ds_list", 0, ds_mi_list, 0, 0, 0 }, { "ds_reload", 0, ds_mi_reload, 0, 0, mi_child_init}, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "ds_ping_interval", get_deps_ds_ping_interval }, { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "dispatcher", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, 0, params, 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, (destroy_function) destroy, ds_child_init, /* per-child init function */ }; DEF_GETTER_FUNC(db_url); DEF_GETTER_FUNC(table_name); DEF_GETTER_FUNC(dst_avp); DEF_GETTER_FUNC(grp_avp); DEF_GETTER_FUNC(cnt_avp); DEF_GETTER_FUNC(sock_avp); DEF_GETTER_FUNC(attrs_avp); static partition_specific_param_t partition_params[] = { {str_init("db_url"), {NULL, 0}, GETTER_FUNC(db_url)}, PARTITION_SPECIFIC_PARAM (table_name, DS_TABLE_NAME), PARTITION_SPECIFIC_PARAM (dst_avp, "$avp(ds_dst_failover)"), PARTITION_SPECIFIC_PARAM (grp_avp, "$avp(ds_grp_failover)"), PARTITION_SPECIFIC_PARAM (cnt_avp, "$avp(ds_cnt_failover)"), PARTITION_SPECIFIC_PARAM (sock_avp, "$avp(ds_sock_failover)"), PARTITION_SPECIFIC_PARAM (attrs_avp, ""), }; static const unsigned int partition_param_count = sizeof (partition_params) / sizeof (partition_specific_param_t); /* Splits the arg from "partition_name[DELIM]value" to partition_name and value. The arg is modified and will contain only value */ static int split_partition_argument(str *arg, str *partition_name) { char *delim_pos = memchr(arg->s, DS_PARTITION_DELIM, arg->len); partition_name->s = NULL; partition_name->len = 0; if (delim_pos == NULL) { /* No delim so the default partition is used */ return 0; } else if (delim_pos - arg->s + 1 == arg->len){ LM_WARN("possibly empty parameter %.*s\n", arg->len, arg->s); return 0; } else { switch (DS_PARTITION_DELIM) { case ':': if (*(delim_pos + 1) == '/'){ /* Fake delimiter as in mysql://... */ return 0; } /* else An actual delimiter has been found */ break; default: LM_CRIT("Partition delimiter %c was not properly implemented\n", DS_PARTITION_DELIM); return -1; break; } } partition_name->s = arg->s; partition_name->len = delim_pos - arg->s; arg->s = delim_pos + 1; arg->len -= partition_name->len + 1; trim(partition_name); for (;arg->s[0] == ' ' && arg->len; ++arg->s, --arg->len); return 0; } /* Parse an argument "partition_name[DELIM]arg_value". The arg string will be modified and will contain only "arg_value" The found_head will contain the head which has the name "partition_name" If the head doesn't exist it will be created */ static int parse_partition_argument(str *arg, ds_db_head_t **found_head) { str partition_name; if (split_partition_argument(arg, &partition_name) != 0) return -1; if (partition_name.len == 0 || str_strcmp(&default_db_head.partition_name, &partition_name) == 0){ *found_head = &default_db_head; return 0; } /* There is a partition name in arg so we won't use default head*/ ds_db_head_t *heads_it; for (heads_it = ds_db_heads; heads_it; heads_it = heads_it->next) if (memcmp(partition_name.s, heads_it->partition_name.s, partition_name.len) == 0){ /* This partition already exists */ *found_head = heads_it; return 0; } /* The partition does not exist - we create it */ ds_db_head_t *new_partition = pkg_malloc(sizeof (ds_db_head_t)); if (new_partition == NULL) { LM_ERR("failed to alocate data in shm\n"); return -1; } /* Set default head values */ memset(new_partition, 0, sizeof(ds_db_head_t)); new_partition->next = ds_db_heads; ds_db_heads = new_partition; new_partition->partition_name = partition_name; *found_head = new_partition; return 0; } /* Find partition by name. Return null if no partition is matching the name */ static ds_partition_t* find_partition_by_name (const str *partition_name) { if (partition_name->len == 0) return default_partition; ds_partition_t *part_it; for (part_it = partitions; part_it; part_it = part_it->next) if (str_strcmp(&part_it->name, partition_name) == 0) break; return part_it; //and NULL if there's no partition matching the name } /* Load setids this proxy is responsible for probing into list */ static int set_probing_list(unsigned int type, void *val) { str input = {(char*)val, strlen(val)}; if (set_list_from_string(input, &ds_probing_list) != 0 || ds_probing_list == NULL) { LM_ERR("Invalid set_probing_list input\n"); return -1; } return 0; } /* We parse the "partition" argument as: partition_name:arg1=val1; arg2=val2;*/ static int set_partition_arguments(unsigned int type, void *val) { static const char end_pair_delim = ';'; static const char eq_val_delim = '='; static const str blacklist_param = str_init("ds_define_blacklist"); unsigned int i; str raw_line = {(char*)val, strlen(val)}; str arg, value; ds_db_head_t *head = NULL; if (raw_line.s[raw_line.len - 1] != end_pair_delim) raw_line.s[raw_line.len++] = end_pair_delim; if (parse_partition_argument(&raw_line, &head) != 0) return -1; char *first_pos = raw_line.s; /* just for error messages */ char *end_pair_pos = q_memchr(raw_line.s, end_pair_delim, raw_line.len); char *eq_pos = q_memchr(raw_line.s, eq_val_delim, raw_line.len); while (end_pair_pos != NULL && eq_pos != NULL) { arg.s = raw_line.s; arg.len = eq_pos - arg.s; value.s = eq_pos + 1; value.len = end_pair_pos - eq_pos - 1; trim(&arg); trim(&value); if (arg.len <= 0 || value.len <= 0) { LM_ERR("Wrong format in partition arguments specifier at pos %d\n", (int)(arg.s - first_pos + 1)); return -1; } for (i = 0; i < partition_param_count; ++i) if (str_strcmp(&arg, &partition_params[i].name) == 0) { *(partition_params[i].getter_func(head)) = value; break; } if ( i == partition_param_count) { if (str_strcmp(&blacklist_param, &arg) == 0) { value.s[value.len] = 0; if (set_ds_bl_partition(value.s, head->partition_name) != 0) return -1; } else{ /* No paramater found */ LM_ERR("No such parameter known: %.*s\n", arg.len, arg.s); return -1; } } raw_line.s = end_pair_pos + 1; end_pair_pos = q_memchr(raw_line.s, end_pair_delim, raw_line.len); eq_pos = q_memchr(raw_line.s, eq_val_delim, raw_line.len); } return 0; } static int partition_init(ds_db_head_t *db_head, ds_partition_t *partition) { /* Load stuff from DB. URL cannot be null!*/ if (db_head->db_url.s == NULL){ LM_ERR("[%.*s] DB URL is not defined!\n", db_head->partition_name.len, db_head->partition_name.s); return -1; } memset(partition, 0, sizeof(ds_partition_t)); partition->name = db_head->partition_name; partition->table_name = db_head->table_name; partition->db_url = db_head->db_url; partition->db_handle = pkg_malloc(sizeof(struct db_con_t *)); if (partition->db_handle == NULL) { LM_ERR("Failed to allocate private data\n"); return -1; } *partition->db_handle = NULL; /* handle AVPs spec */ pv_spec_t avp_spec; if (pv_parse_spec(&db_head->dst_avp, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %.*s AVP definition\n", db_head->dst_avp.len, db_head->dst_avp.s); return -1; } if(pv_get_avp_name(0, &(avp_spec.pvp), &partition->dst_avp_name, &partition->dst_avp_type)!=0) { LM_ERR("[%.*s]- invalid AVP definition\n", db_head->dst_avp.len, db_head->dst_avp.s); return -1; } if (pv_parse_spec(&db_head->grp_avp, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %.*s AVP definition\n", db_head->grp_avp.len, db_head->grp_avp.s); return -1; } if(pv_get_avp_name(0, &(avp_spec.pvp), &partition->grp_avp_name, &partition->grp_avp_type)!=0) { LM_ERR("[%.*s]- invalid AVP definition\n", db_head->grp_avp.len, db_head->grp_avp.s); return -1; } if (pv_parse_spec(&db_head->cnt_avp, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %.*s AVP definition\n", db_head->cnt_avp.len, db_head->cnt_avp.s); return -1; } if(pv_get_avp_name(0, &(avp_spec.pvp), &partition->cnt_avp_name, &partition->cnt_avp_type)!=0) { LM_ERR("[%.*s]- invalid AVP definition\n", db_head->cnt_avp.len, db_head->cnt_avp.s); return -1; } if (pv_parse_spec(&db_head->sock_avp, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %.*s AVP definition\n", db_head->sock_avp.len, db_head->sock_avp.s); return -1; } if(pv_get_avp_name(0, &(avp_spec.pvp), &partition->sock_avp_name, &partition->sock_avp_type)!=0){ LM_ERR("[%.*s]- invalid AVP definition\n", db_head->sock_avp.len, db_head->sock_avp.s); return -1; } if (db_head->attrs_avp.s && db_head->attrs_avp.len > 0) { if (pv_parse_spec(&db_head->attrs_avp, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %.*s AVP definition\n", db_head->attrs_avp.len, db_head->attrs_avp.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &partition->attrs_avp_name, &partition->attrs_avp_type)!=0){ LM_ERR("[%.*s]- invalid AVP definition\n", db_head->attrs_avp.len, db_head->attrs_avp.s); return -1; } } else { partition->attrs_avp_name = -1; partition->attrs_avp_type = 0; } return 0; } static int inherit_from_default_head(ds_db_head_t *head) { unsigned int i; if (head == &default_db_head) return 0; for (i = 0; i < partition_param_count; ++i) { str *def_param = partition_params[i].getter_func(&default_db_head); str *p_param = partition_params[i].getter_func(head); if (p_param->len == 0 && def_param->len > 0) { /* Paramater not specified for function */ if (strstr(partition_params[i].name.s, "avp") && def_param->len > 0) { char *avp_end = q_memrchr(def_param->s, ')', def_param->len); if (avp_end == NULL) { LM_ERR ("wrong avp name %.*s\n", def_param->len, def_param->s); return -1; } p_param->len = def_param->len + 1 + head->partition_name.len; p_param->s = pkg_malloc(p_param->len); if (p_param->s == NULL) { LM_ERR ("no more private memory\n"); return -1; } int fix_len = avp_end - def_param->s; int rem_len = def_param->len - fix_len; memcpy(p_param->s, def_param->s, fix_len); p_param->s[fix_len] = '_'; memcpy(p_param->s + fix_len + 1, head->partition_name.s, head->partition_name.len); memcpy(p_param->s + fix_len + 1 + head->partition_name.len, def_param->s + fix_len, rem_len); } else memcpy(p_param, def_param, sizeof(str)); } } return 0; } void set_default_head_values(ds_db_head_t *head) { unsigned int i; for (i = 0; i < partition_param_count; ++i) { str *p_val = partition_params[i].getter_func(head); if (p_val->s == NULL) *p_val = partition_params[i].default_value; else p_val->len = strlen(p_val -> s); } } static inline int check_if_default_head_is_ok(void) { unsigned int i; for (i = 0; i < partition_param_count; ++i) if (partition_params[i].getter_func(&default_db_head)->s != NULL) return 1; return 0; } /** * init module function */ static int mod_init(void) { LM_DBG("initializing ...\n"); if (check_if_default_head_is_ok()) { default_db_head.next = ds_db_heads; ds_db_heads = &default_db_head; } set_default_head_values(&default_db_head); ds_set_id_col.len = strlen(ds_set_id_col.s); ds_dest_uri_col.len = strlen(ds_dest_uri_col.s); ds_dest_sock_col.len = strlen(ds_dest_sock_col.s); ds_dest_state_col.len = strlen(ds_dest_state_col.s); ds_dest_weight_col.len = strlen(ds_dest_weight_col.s); ds_dest_attrs_col.len = strlen(ds_dest_attrs_col.s); if(hash_pvar_param.s && (hash_pvar_param.len=strlen(hash_pvar_param.s))>0){ if(pv_parse_format(&hash_pvar_param, &hash_param_model) < 0 || hash_param_model==NULL) { LM_ERR("malformed PV string: %s\n", hash_pvar_param.s); return -1; } } else { hash_param_model = NULL; } if(ds_setid_pvname.s && (ds_setid_pvname.len=strlen(ds_setid_pvname.s))>0){ if(pv_parse_spec(&ds_setid_pvname, &ds_setid_pv)==NULL || !pv_is_w(&ds_setid_pv)) { LM_ERR("[%s]- invalid setid_pvname\n", ds_setid_pvname.s); return -1; } } pvar_algo_param.len = strlen(pvar_algo_param.s); if (pvar_algo_param.len) ds_pvar_parse_pattern(pvar_algo_param); if (init_ds_bls()!=0) { LM_ERR("failed to init DS blacklists\n"); return E_CFG; } /* Creating partitions from head */ ds_db_head_t *head_it = ds_db_heads; while (head_it){ if (inherit_from_default_head(head_it) != 0) return -1; ds_partition_t *partition = shm_malloc (sizeof(ds_partition_t)); if (partition_init(head_it, partition) != 0) return -1; partition->next = partitions; partitions = partition; if (init_ds_data(partition)!=0) { LM_ERR("failed to init DS data holder\n"); return -1; } /* open DB connection to load provisioning data */ if (init_ds_db(partition)!= 0) { LM_ERR("failed to init database support\n"); return -1; } /* do the actual data load */ if (ds_reload_db(partition)!=0) { LM_ERR("failed to load data from DB\n"); return -1; } /* close DB connection */ ds_disconnect_db(partition); ds_db_head_t *aux = head_it; /* We keep track of corespondig default parition */ if (head_it == &default_db_head) default_partition = partition; head_it = head_it->next; if (aux != &default_db_head) pkg_free(aux); } /* Only, if the Probing-Timer is enabled the TM-API needs to be loaded: */ if (ds_ping_interval > 0) { load_tm_f load_tm; str host; int port,proto; if (ds_ping_from.s) ds_ping_from.len = strlen(ds_ping_from.s); if (ds_ping_method.s) ds_ping_method.len = strlen(ds_ping_method.s); /* parse the list of reply codes to be counted as success */ if(options_reply_codes_str.s) { options_reply_codes_str.len = strlen(options_reply_codes_str.s); if(parse_reply_codes(&options_reply_codes_str,&options_reply_codes, &options_codes_no )< 0) { LM_ERR("Bad format for options_reply_code parameter" " - Need a code list separated by commas\n"); return -1; } } /* parse and look for the socket to ping from */ if (probing_sock_s && probing_sock_s[0]!=0 ) { if (parse_phostport( probing_sock_s, strlen(probing_sock_s), &host.s, &host.len, &port, &proto)!=0 ) { LM_ERR("socket description <%s> is not valid\n", probing_sock_s); return -1; } probing_sock = grep_sock_info( &host, port, proto); if (probing_sock==NULL) { LM_ERR("socket <%s> is not local to opensips (we must listen " "on it\n", probing_sock_s); return -1; } } /* TM-Bindings */ load_tm=(load_tm_f)find_export("load_tm", 0, 0); if (load_tm==NULL) { LM_ERR("failed to bind to the TM-Module - required for probing\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb ) == -1) { LM_ERR("could not load the TM-functions - disable DS ping\n"); return -1; } /* Register the PING-Timer */ if (register_timer("ds-pinger", ds_check_timer, NULL, ds_ping_interval, TIMER_FLAG_DELAY_ON_DELAY)<0) { LM_ERR("failed to register timer for probing!\n"); return -1; } } /* register timer to flush the state of destination back to DB */ if (ds_persistent_state && register_timer("ds-flusher", ds_flusher_routine, NULL, 30 , TIMER_FLAG_SKIP_ON_DELAY)<0) { LM_ERR("failed to register timer for DB flushing!\n"); return -1; } dispatch_evi_id = evi_publish_event(dispatcher_event); if (dispatch_evi_id == EVI_ERROR) LM_ERR("cannot register dispatcher event\n"); return 0; } /* * Per process init function */ #include "../../pt.h" static int ds_child_init(int rank) { /* we need DB connection from the worker procs (for the flushing) * and from the main proc (for final flush on shutdown) */ if ( rank>=PROC_MAIN ) { ds_partition_t *partition_it; for (partition_it = partitions; partition_it; partition_it = partition_it->next){ if (partition_it->db_url.s) if (ds_connect_db(partition_it) != 0) { LM_ERR("failed to do DB connect\n"); return -1; } } } return 0; } static int mi_child_init(void) { ds_partition_t *partition_it; for (partition_it = partitions; partition_it; partition_it = partition_it->next) if (partition_it->db_url.s) if (ds_connect_db(partition_it) != 0) return -1; return 0; } /** * destroy function */ static void destroy(void) { LM_DBG("destroying module ...\n"); /* flush the state of the destinations */ if (ds_persistent_state) ds_flusher_routine(0, NULL); ds_partition_t *part_it = partitions, *aux; while (part_it) { ds_destroy_data(part_it); aux = part_it; part_it = part_it->next; ds_disconnect_db(aux); pkg_free(aux->db_handle); shm_free(aux); } /* destroy blacklists */ destroy_ds_bls(); /* destroy probing list */ if (ds_probing_list) free_int_list(ds_probing_list, NULL); } #define CHECK_AND_EXPAND_LIST(_list_) \ do{\ if (_list_->type == GPARAM_TYPE_PVS) { \ _list_ ## _exp_end = _list_->next; \ _list_ ## _exp_start = set_list_from_pvs(msg, _list_->v.pvs,\ _list_->next);\ if (_list_ ## _exp_start == NULL) {\ LM_ERR("error when expanding " #_list_ " variable\n");\ return -1;\ }\ _list_ = _list_ ## _exp_start;\ }\ } while (0) #define TRY_FREE_EXPANDED_LIST(_list_) \ do {\ if (_list_ ## _exp_start && _list_ == _list_ ## _exp_end) {\ free_int_list(_list_ ## _exp_start, _list_ ## _exp_end);\ _list_ ## _exp_start = NULL; \ }\ } while (0) /** * */ static int w_ds_select(struct sip_msg* msg, char* part_set, char* alg, char* max_results_flags, int mode) { int ret = -1; int _ret; int run_prev_ds_select = 0; ds_select_ctl_t prev_ds_select_ctl, ds_select_ctl; char selected_dst_sock_buf[PTR_STRING_SIZE]; /* a hexa string */ ds_selected_dst selected_dst; struct socket_info *sock = NULL; if(msg==NULL) return -1; ds_select_ctl.mode = mode; ds_select_ctl.max_results = 1000; ds_select_ctl.reset_AVP = 1; ds_select_ctl.set_destination = 1; ds_select_ctl.ds_flags = 0; memset(&selected_dst, 0, sizeof(ds_selected_dst)); selected_dst.socket.s = selected_dst_sock_buf; /* Retrieve dispatcher set */ ds_param_t *part_set_param = (ds_param_t*)part_set; if (fixup_get_partition(msg, &part_set_param->partition, &ds_select_ctl.partition) != 0 ||ds_select_ctl.partition == NULL) { LM_ERR("unknown partition\n"); return -1; } int_list_t *set_list = part_set_param->sets; int_list_t *set_list_exp_start = NULL, *set_list_exp_end = NULL; /* Retrieve dispatcher algorithm */ int_list_t *alg_list = (int_list_t *)alg; int_list_t *alg_list_exp_start = NULL, *alg_list_exp_end = NULL; /* In case this parameter is not specified */ max_list_param_p max_param = (max_list_param_p)max_results_flags; str max_list_str; int_list_t *max_list=NULL, *max_list_free; if (max_param && max_param->type == MAX_LIST_TYPE_STR) { max_list = (int_list_t*)max_param->lst.list; } else if (max_param && max_param->type == MAX_LIST_TYPE_PV) { if (pv_printf_s(msg, max_param->lst.elem, &max_list_str) != 0) { LM_ERR("cannot get max list from pv\n"); return -1; } if (set_list_from_string(max_list_str, &max_list) != 0 || max_list == NULL) return -1; } /* Avoid compiler warning */ memset(&prev_ds_select_ctl, 0, sizeof(ds_select_ctl_t)); ds_select_ctl.set_destination = 0; /* Parse the params in reverse order. * We need to runt the first entry last to properly populate ds_select_dst * AVPs. * On the first ds_select_dst run we need to reset AVPs. * On the last ds_select_dst run we need to set destination. */ do { CHECK_AND_EXPAND_LIST(set_list); ds_select_ctl.set = set_list->v.ival; CHECK_AND_EXPAND_LIST(alg_list); ds_select_ctl.alg = alg_list->v.ival; if (max_results_flags) { ds_select_ctl.max_results = max_list->v.ival; ds_select_ctl.ds_flags = max_list->flags; } if (run_prev_ds_select) { LM_DBG("ds_select: %d %d %d %d %d\n", prev_ds_select_ctl.set, prev_ds_select_ctl.alg, prev_ds_select_ctl.max_results, prev_ds_select_ctl.reset_AVP, prev_ds_select_ctl.set_destination); _ret = ds_select_dst(msg, &prev_ds_select_ctl, &selected_dst, prev_ds_select_ctl.ds_flags); if (_ret>=0) ret = _ret; /* stop resetting AVPs. */ ds_select_ctl.reset_AVP = 0; } else { /* Enable running ds_select_dst on next loop. */ run_prev_ds_select = 1; } prev_ds_select_ctl = ds_select_ctl; set_list = set_list->next; alg_list = alg_list->next; if (max_results_flags) { max_list_free = max_list; max_list = max_list->next; if (max_param->type == MAX_LIST_TYPE_PV) pkg_free(max_list_free); } TRY_FREE_EXPANDED_LIST(set_list); TRY_FREE_EXPANDED_LIST(alg_list); } while (set_list && alg_list && (max_results_flags ? max_list : set_list)); if (max_results_flags && max_list != NULL) { LM_ERR("extra max slot(s) and/or flag(s)\n"); ret = -2; goto error; } if (set_list != NULL) { LM_ERR("extra set(s)\n"); ret = -2; goto error; } if (alg_list != NULL) { LM_ERR("extra algorithm(s)\n"); ret = -2; goto error; } /* last ds_select_dst run: setting destination. */ ds_select_ctl.set_destination = 1; LM_DBG("ds_select: %d %d %d %d %d\n", ds_select_ctl.set, ds_select_ctl.alg, ds_select_ctl.max_results, ds_select_ctl.reset_AVP, ds_select_ctl.set_destination); _ret = ds_select_dst(msg, &ds_select_ctl, &selected_dst, ds_select_ctl.ds_flags); if (_ret>=0) { ret = _ret; } else { if (selected_dst.uri.s != NULL) { if (selected_dst.socket.len != 0) { if (sscanf( selected_dst.socket.s, "%p", (void**)&sock ) != 1){ LM_ERR("unable to read forced destination socket\n"); ret = -4; goto error; } } if (ds_update_dst(msg, &selected_dst.uri, sock, ds_select_ctl.mode) != 0) { LM_ERR("cannot set dst addr\n"); ret = -3; goto error; } } else { ret = -1; goto error; } } error: if (selected_dst.uri.s != NULL) pkg_free(selected_dst.uri.s); return ret; } /** * */ static int w_ds_select_all(struct sip_msg* msg, char* set, char* alg, int mode) { return w_ds_select(msg, set, alg, NULL, mode); } /** * max_results can also mean the flags parameter */ static int w_ds_select_limited(struct sip_msg* msg, char* set, char* alg, char* max_results, int mode) { return w_ds_select(msg, set, alg, max_results, mode); } /** * */ static int w_ds_select_dst(struct sip_msg* msg, char* set, char* alg) { return w_ds_select_all(msg, set, alg, 0); } /** * same wrapper as w_ds_select_dst, but it allows cutting down the result set * max_results can also mean flags */ static int w_ds_select_dst_limited(struct sip_msg* msg, char* set, char* alg, char* max_results) { return w_ds_select_limited(msg, set, alg, max_results, 0); } /** * */ static int w_ds_select_domain(struct sip_msg* msg, char* set, char* alg) { return w_ds_select_all(msg, set, alg, 1); } /** * same wrapper as w_ds_select_domain, but it allows cutting down the * result set * max_results can also mean the flags parameter */ static int w_ds_select_domain_limited(struct sip_msg* msg, char* set, char* alg, char* max_results) { return w_ds_select_limited(msg, set, alg, max_results, 1); } #define GET_AND_CHECK_PARTITION(_param_, _part_) \ do {\ if (_param_ == NULL) \ _part_ = default_partition; \ else \ if(fixup_get_partition(msg, (gpartition_t *)_param_, &_part_)!=0) \ return -1; \ if (_part_ == NULL) { \ LM_ERR("Unknown partition\n"); \ return -1; \ } \ } while (0) /** * */ static int w_ds_next_dst(struct sip_msg *msg, char *part_param) { ds_partition_t *partition; GET_AND_CHECK_PARTITION(part_param, partition); return ds_next_dst(msg, 0, partition); } /** * */ static int w_ds_next_domain(struct sip_msg *msg, char *part_param) { ds_partition_t *partition; GET_AND_CHECK_PARTITION(part_param, partition); return ds_next_dst(msg, 1, partition); } /** * */ static int w_ds_mark_dst(struct sip_msg *msg, char *str1, char *str2) { str arg = {NULL, 0}; ds_partition_t *partition = default_partition; if (str2 != NULL) { /* We have two args */ if (str1 != NULL) GET_AND_CHECK_PARTITION(str1, partition); if (fixup_get_svalue(msg, (gparam_p)str2, &arg) != 0) goto error; } else { if (str1 != NULL && fixup_get_svalue(msg, (gparam_p)str1, &arg) != 0) goto error; } if (arg.len > 1) { LM_ERR ("unknown option %.*s\n", arg.len, arg.s); return -1; } if (partition == NULL) { LM_ERR ("unknown partition\n"); return -1; } if((arg.s == NULL || arg.s[0]=='i' || arg.s[0]=='I' || arg.s[0]=='0')) return ds_mark_dst(msg, 0, partition); else if(arg.s && (arg.s[0]=='p' || arg.s[0]=='P' || arg.s[0]=='2')) return ds_mark_dst(msg, 2, partition); else if(arg.s && (arg.s[0]=='a' || arg.s[0]=='A' || arg.s[0]=='1')) return ds_mark_dst(msg, 1, partition); else { LM_ERR ("unknown option %.*s\n", arg.len, arg.s); return -1; } error: LM_ERR("wrong arguments\n"); return -1; } static int w_ds_mark_dst1(struct sip_msg *msg, char *flags) { return w_ds_mark_dst(msg, flags, NULL); } /************************** MI STUFF ************************/ #define MI_ERR_RELOAD "ERROR Reloading data" #define MI_NOT_SUPPORTED "DB mode not configured" #define MI_UNK_PARTITION "ERROR Unknown partition" static struct mi_root* ds_mi_set(struct mi_root* cmd_tree, void* param) { str sp, partition_name; int ret; unsigned int group, state; struct mi_node* node; ds_partition_t *partition; node = cmd_tree->node.kids; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); sp = node->value; if(sp.len<=0 || !sp.s) { LM_ERR("bad state value\n"); return init_mi_tree( 500, MI_SSTR("Bad state value") ); } if(sp.s[0]=='0' || sp.s[0]=='I' || sp.s[0]=='i') state = 0; else if(sp.s[0]=='p' || sp.s[0]=='P' || sp.s[0]=='2') state = 2; else if(sp.s[0]=='a' || sp.s[0]=='A' || sp.s[0]=='1') state = 1; else return init_mi_tree( 500, MI_SSTR("Bad state value") ); node = node->next; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); sp = node->value; if(sp.s == NULL) { return init_mi_tree(500, MI_SSTR("group not found")); } if (split_partition_argument(&sp, &partition_name) != 0) { LM_ERR("bad group format\n"); return init_mi_tree(500, MI_SSTR("bad group format")); } partition = find_partition_by_name(&partition_name); if (partition == NULL) { LM_ERR("partition does not exist\n"); return init_mi_tree(404, MI_SSTR(MI_UNK_PARTITION) ); } if(str2int(&sp, &group)) { LM_ERR("bad group value\n"); return init_mi_tree( 500, MI_SSTR("bad group value")); } node= node->next; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); sp = node->value; if(sp.s == NULL) { return init_mi_tree(500, MI_SSTR("address not found")); } if (state==1) { /* set active */ ret = ds_set_state(group, &sp, DS_INACTIVE_DST|DS_PROBING_DST, 0, partition); } else if (state==2) { /* set probing */ ret = ds_set_state(group, &sp, DS_PROBING_DST, 1, partition); if (ret==0) ret = ds_set_state(group, &sp, DS_INACTIVE_DST, 0, partition); } else { /* set inactive */ ret = ds_set_state(group, &sp, DS_INACTIVE_DST, 1, partition); if (ret == 0) ret = ds_set_state(group, &sp, DS_PROBING_DST, 0, partition); } if(ret!=0) return init_mi_tree(404, MI_SSTR("destination not found")); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } static struct mi_root* ds_mi_list(struct mi_root* cmd_tree, void* param) { struct mi_root* rpl_tree; struct mi_node* part_node; int flags = 0; if (cmd_tree->node.kids){ if(cmd_tree->node.kids->value.len == 4 && memcmp(cmd_tree->node.kids->value.s,"full",4)==0) flags |= MI_FULL_LISTING; else return init_mi_tree(400, MI_SSTR(MI_BAD_PARM_S)); } rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return 0; rpl_tree->node.flags |= MI_IS_ARRAY; ds_partition_t *part_it; for (part_it = partitions; part_it; part_it = part_it->next) { part_node = add_mi_node_child(&rpl_tree->node, MI_IS_ARRAY,"PARTITION", 9, part_it->name.s, part_it->name.len); if (part_node == NULL || ds_print_mi_list(part_node, part_it, flags) < 0) { LM_ERR("failed to add node\n"); free_mi_tree(rpl_tree); return 0; } } return rpl_tree; } static struct mi_root* ds_mi_reload(struct mi_root* cmd_tree, void* param) { struct mi_node* node = cmd_tree->node.kids; if(node != NULL){ ds_partition_t *partition = find_partition_by_name(&node->value); if (partition == NULL) return init_mi_tree(500, MI_SSTR(MI_UNK_PARTITION) ); if (ds_reload_db(partition) < 0) return init_mi_tree(500, MI_SSTR(MI_ERR_RELOAD)); else return init_mi_tree(200, MI_SSTR(MI_OK_S) ); } ds_partition_t *part_it; for (part_it = partitions; part_it; part_it = part_it->next) if (ds_reload_db(part_it)<0) return init_mi_tree(500, MI_SSTR(MI_ERR_RELOAD)); return init_mi_tree(200, MI_SSTR(MI_OK_S)); } static int w_ds_is_in_list(struct sip_msg *msg,char *ip,char *port,char *set, char *active_only) { ds_partition_t *partition = default_partition; int i_set = -1; if (set != NULL) { ds_param_t *setparam = (ds_param_t*)set; if (fixup_get_partition(msg, &setparam->partition, &partition) != 0) goto wrong_set_arg; if (setparam->sets == NULL) i_set = -1; else if (setparam->sets->type == GPARAM_TYPE_INT) { if (setparam->sets->next == NULL) i_set = setparam->sets->v.ival; else { LM_ERR("Only one set is allowed\n"); return -1; } } else { int_list_t *tmp_lst = set_list_from_pvs(msg, setparam->sets->v.pvs, NULL); if (tmp_lst == NULL){ LM_ERR("Wrong set var value\n"); return -1; } if (tmp_lst->next != NULL) { LM_ERR("Only one set is allowd\n"); return -1; } i_set = tmp_lst->v.ival; free_int_list(tmp_lst, NULL); } } if (partition == NULL) { LM_ERR ("unknown partition\n"); return -1; } return ds_is_in_list(msg, (gparam_t *)ip, (gparam_t *)port, i_set, (int)(long)active_only, partition); wrong_set_arg: LM_ERR("wrong format for set argument\n"); return -1; } static int w_ds_count(struct sip_msg* msg, char *set, const char *cmp, char *res) { unsigned int s = 0; gparam_p ret = (gparam_p) res; ds_partition_t *partition; if (fixup_get_partition_set(msg, (ds_param_t*)set, &partition, &s) != 0){ LM_ERR("wrong format for set argument. Only one set is accepted\n"); return -1; } if (ret->type != GPARAM_TYPE_PVS && ret->type != GPARAM_TYPE_PVE) { LM_ERR("Result must be a pvar!\n"); return -1; } return ds_count(msg, s, cmp, ret->v.pvs, partition); } int check_options_rplcode(int code) { int i; for (i =0; i< options_codes_no; i++) { if(options_reply_codes[i] == code) return 1; } return 0; } opensips-2.2.2/modules/dispatcher/doc/000077500000000000000000000000001300170765700177265ustar00rootroot00000000000000opensips-2.2.2/modules/dispatcher/doc/dispatcher.cfg000066400000000000000000000015321300170765700225360ustar00rootroot00000000000000# # sample config file for dispatcher module # debug_mode=yes children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 # for more info: opensips -h # ------------------ module loading ---------------------------------- mpath="/usr/local/lib/opensips/modules/" loadmodule "maxfwd.so" loadmodule "signaling.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "db_mysql.so" loadmodule "dispatcher.so" # ----------------- setting module-specific parameters --------------- # -- dispatcher params -- modparam("dispatcher", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") route{ if ( !mf_process_maxfwd_header("10") ) { send_reply("483","To Many Hops"); exit; }; if ( !ds_select_dst("2", "0") ) { send_reply("500","Unable to route"); exit; } t_relay(); } opensips-2.2.2/modules/dispatcher/doc/dispatcher.list000066400000000000000000000003661300170765700227560ustar00rootroot00000000000000# $Id$ # dispatcher destination sets # # line format # setit(integer) destination(sip uri) flags (integer, optional) # proxies 2 sip:127.0.0.1:5080 2 sip:127.0.0.1:5082 # gateways 1 sip:127.0.0.1:7070 1 sip:127.0.0.1:7072 1 sip:127.0.0.1:7074 opensips-2.2.2/modules/dispatcher/doc/dispatcher.xml000066400000000000000000000030301300170765700225720ustar00rootroot00000000000000 %docentities; ]> DISPATCHER Module &osipsname; Daniel-Constantin Mierla team@voice-system.ro Ovidiu Sas osas@voipembedded.com Daniel-Constantin Mierla team@voice-system.ro Carsten Bock BASIS AudioNet GmbH
bock@basis-audionet.de
2004 &fhg; 2005-2010 Voice-System.RO $Revision: 6771 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/dispatcher/doc/dispatcher_admin.xml000066400000000000000000001121341300170765700237500ustar00rootroot00000000000000 &adminguide;
Overview This modules implements a dispatcher for destination addresses. It computes hashes over various parts of the request and selects an address from a destination set. The selected address is then used as outbound proxy. The module can be used as a stateless load balancer, having no guarantee of fair distribution. For the distribution algorithm, the module allows the definition of weights for the destination. This is useful in order to get a different ratio of traffic between destinations. Since version 2.1 the dispatcher module keeps its destination sets into different partitions. Each partition is described by its own db_url, table_name, dst_avp, grp_avp, cnt_avp, sock_avp, attr_avp and blacklists. Setting any of this parameters using modparam will alter the default partition's properties. In order to create a new partition the "partition" parameter should be used (see below for more details). If none of the 8 partition specific parameters are defined for the default partition, then this partition will not be created. If the default partition is created each undefined parameter from all other partitions will take the value of the corresponding one from the default partition. If there is no default partition, the default value specified in the parameter's description will be used. Functions taking set arguments will now take a set number preceded by a partition name and colon(i.e "part_name: 5"). If a set is not preceded by any partition name the default partition will be used. Thus, the following arguments are equivalent: "default : 4" vs "4". Remember that in order to be able to use a table from a partition, its name must be found in the "version" table belonging to the database defined in the partition's db_url. Also, in version 2.1 the "flags" parameter has been moved to ds_select_dst() and ds_select_domain() along with "force_dst" and "use_default" flags.
Dependencies
&osips; modules The following modules must be loaded before this module: TM - only if active recovery of failed hosts is required.
External libraries or applications The following libraries or applications must be installed before running &osips; with this module: none.
Exported Parameters
<varname>db_url</varname> (string) Database where to load the destinations from. Setting this parameter will only change the default partition's db_url. Use the partition parameter to create and alter other partitions. NOTE: if you intend to use the default partition you have to explicity set this default db_url, otherwise OpenSIPS will not start (he value of global default db_url is not inherited here! ). Default value is NULL. At least one db_url should be defined for the dispatcher module to work. Set the 'default' partition's<quote>db_url</quote> parameter ... modparam("dispatcher", "db_url", "mysql://user:passwb@localhost/database") ...
<varname>attrs_avp</varname> (str) The name of the avp to contain the attributes string of the current destination. When a destination is selected, automatically, this AVP will provide the attributes string - this is an opaque string (from OpenSIPS point of view) : it is loaded from destination definition ( via DB) and blindly provided in the script. Setting this parameter will only change the default partition's attrs_avp. Use the partition parameter to create and alter other partitions. Default value is null - don't provide ATTRIBUTEs. Set the 'default' partition's <quote>attrs_avp</quote> parameter ... modparam("dispatcher", "attrs_avp", "$avp(272)") ...
<varname>hash_pvar</varname> (str) String with PVs used for the hashing algorithm 7. You must set this parameter if you want do hashing over custom message parts. Default value is null - disabled. Use $avp(273) for hashing: ... modparam("dispatcher", "hash_pvar", "$avp(273)") ... Use combination of PVs for hashing: ... modparam("dispatcher", "hash_pvar", "hash the $fU@$ci") ...
<varname>setid_pvar</varname> (str) The name of the PV where to store the set ID (group ID) when calling ds_is_in_list() without group parameter (third parameter). Default value is null - don't set PV. Set the <quote>setid_pvar</quote> parameter ... modparam("dispatcher", "setid_pvar", "$var(setid)") ...
<varname>ds_ping_method</varname> (string) With this Method you can define, with which method you want to probe the failed gateways. This method is only available, if compiled with the probing of failed gateways enabled. Default value is OPTIONS. Set the <quote>ds_ping_method</quote> parameter ... modparam("dispatcher", "ds_ping_method", "INFO") ...
<varname>ds_ping_from</varname> (string) With this Method you can define the "From:"-Line for the request, sent to the failed gateways. This method is only available, if compiled with the probing of failed gateways enabled. Default value is sip:dispatcher@localhost. Set the <quote>ds_ping_from</quote> parameter ... modparam("dispatcher", "ds_ping_from", "sip:proxy@sip.somehost.com") ...
<varname>ds_ping_interval</varname> (int) With this Method you can define the interval for sending a request to a failed gateway. This parameter is only used, when the TM-Module is loaded. If set to 0, the pinging of failed requests is disabled. Default value is 0 (disabled). Set the <quote>ds_ping_interval</quote> parameter ... modparam("dispatcher", "ds_ping_interval", 30) ...
<varname>ds_probing_sock</varname> (str) A socket description [proto:]host[:port] of the local socket (which is used by OpenSIPS for SIP traffic) to be used (if multiple) for sending the probing messages from. Default value is NULL(none). Set the <quote>ds_probing_sock</quote> parameter ... modparam("dispatcher", "ds_probing_sock", "udp:192.168.1.100:5077") ...
<varname>ds_probing_threshhold</varname> (int) If you want to set a gateway into probing mode, you will need a specific number of requests until it will change from "active" to probing. The number of attempts can be set with this parameter. Default value is 3. Set the <quote>ds_probing_threshhold</quote> parameter ... modparam("dispatcher", "ds_probing_threshhold", 10) ...
<varname>ds_probing_mode</varname> (int) Controls what gateways are tested to see if they are reachable. If set to 0, only the gateways with state PROBING are tested, if set to 1, all gateways are tested. If set to 1 and the response is 408 (timeout), an active gateway is set to PROBING state. Default value is 0. Set the <quote>ds_probing_mode</quote> parameter ... modparam("dispatcher", "ds_probing_mode", 1) ...
<varname>ds_probing_list</varname> (str) Defines a list of one or more setids that limits which destinations are probed if probing is active. This is useful when multiple proxies share the same dispatcher table, but you want to limit which ones are responsible for probing specific destinations. Default value is NULL(none). Set the <quote>ds_probing_list</quote> parameter ... modparam("dispatcher", "ds_probing_list", "1,2,3") ...
<varname>ds_define_blacklist</varname> (str) Defines a blacklist based on a dispatching setid from the 'default' partition. This list will contain the IPs (no port, all protocols) of the destinations matching the given setid. Use the 'partition' parameter if you want to define blacklists based on other partitions' sets. Multiple instances of this param are allowed. Default value is NULL. Set the 'default' partition's <quote>ds_define_blacklist</quote> parameter ... modparam("dispatcher", "ds_define_blacklist", "list= 1,4,3") modparam("dispatcher", "ds_define_blacklist", "blist2= 2,10,6") ...
<varname>options_reply_codes</varname> (str) This parameter must contain a list of SIP reply codes separated by comma. The codes defined here will be considered as valid reply codes for OPTIONS messages used for pinging, apart for 200. Default value is NULL. Set the <quote>options_reply_codes</quote> parameter ... modparam("dispatcher", "options_reply_codes", "501, 403") ...
<varname>dst_avp</varname> (str) This is mainly for internal usage and represents the name of the avp which will hold the list with addresses, in the order they have been selected by the chosen algorithm. If use_default is 1, the value of last dst_avp_id is the last address in destination set. The first dst_avp_id is the selected destinations. All the other addresses from the destination set will be added in the avp list to be able to implement serial forking. Setting this parameter will only change the default partition's dst_avp. Use the partition parameter to create and alter other partitions. For the 'default' partition the default value is $avp(ds_dst_failover). For any other partition, the default value is $avp(ds_dst_failover_partitionname). Set the 'default' partition's <quote>dst_avp</quote> parameter ... modparam("dispatcher", "dst_avp", "$avp(271)") ...
<varname>grp_avp</varname> (str) This is mainly for internal usage and represents the name of the avp storing the group id of the destination set. Good to have it for later usage or checks. Setting this parameter will only change the default partition's grp_avp. Use the partition parameter to create and alter other partitions. For the 'default' partition the default value is $avp(ds_grp_failover). For any other partition, the default value is $avp(ds_grp_failover_partitionname). Set the 'default' partition's <quote>grp_avp</quote> parameter ... modparam("dispatcher", "grp_avp", "$avp(273)") ...
<varname>cnt_avp</varname> (str) This is mainly for internal usage and represents the name of the avp storing the number of destination addresses kept in dst_avp avps. Setting this parameter will only change the default partition's cnt_avp. Use the partition parameter to create and alter other partitions. For the 'default' partition the default value is $avp(ds_cnt_failover). For any other partition, the default value is $avp(ds_cnt_failover_partitionname). Set the 'default' partition's <quote>cnt_avp</quote> parameter ... modparam("dispatcher", "cnt_avp", "$avp(274)") ...
<varname>sock_avp</varname> (str) This is mainly for internal usage and represents the name of the avp storing the sockets to be used for the destination addresses kept in dst_avp avps. Setting this parameter will only change the default partition's sock_avp. Use the partition parameter to create and alter other partitions. For the 'default' partition the default value is $avp(ds_sock_failover). For any other partition, the default value is $avp(ds_sock_failover_partitionname). Set the 'default' partition's <quote>sock_avp</quote> parameter ... modparam("dispatcher", "sock_avp", "$avp(275)") ...
<varname>pvar_algo_pattern</varname> (str) This parameter is used by the PVAR(9) algorithm to specify the pseudovariable pattern used to detect the load of each destination. The name of the pseudovariable should contain the string %u, which will be internally replaced by the module with the uri of the destination. Default value is none. Set the <quote>pvar_algo_pattern</quote> parameter ... modparam("dispatcher", "pvar_algo_pattern", "$stat(load_%u)") ...
<varname>persistent_state</varname> (int) Specifies whether the state column should be loaded at startup and flushed during runtime or not. Default value is 1 (enabled). Set the <varname>persistent_state</varname> parameter ... # disable all DB operations with the state of a destination modparam("dispatcher", "persistent_state", 0) ...
<varname>table_name</varname> (string) If you want to load the sets of gateways from the database you must set this parameter as the database name. Setting this parameter will only change the default partition's table_name. Use the partition parameter to create and alter other partitions. For every partition the default value is dispatcher. Set the 'default' partition's <quote>table_name</quote> parameter ... modparam("dispatcher", "table_name", "my_dispatcher") ...
<varname>partition</varname> (string) Using this parameter the partition specific parameters (db_url, table_name, dst_avp, grp_avp, cnt_avp, sock_avp, attrs_avp, ds_define_blacklist) can be defined. The syntax is: "partition_name: param1 = value1; param2 = value2; param3 = value3". Each value format is the same as the one used to define a specific parameter using modparam. Whenever a new partition_name is provided, a new partition will be automatically created. The 'default' partition can also be defined using this parameter. Create a new partition called 'part2' ... modparam("dispatcher", "partition", "part2: db_url = mysql://user:passwd@localhost/database; table_name = ds_table; attrs_avp = $avp(ds_attr_part2); ds_define_blacklist = list2 = 4,6;") ...
<varname>setid_col</varname> (string) The column's name in the database storing the gateway's group id. Default value is setid. Set <quote>setid_col</quote> parameter ... modparam("dispatcher", "setid_col", "groupid") ...
<varname>destination_col</varname> (string) The column's name in the database storing the destination's sip uri. Default value is destination. Set <quote>destination_col</quote> parameter ... modparam("dispatcher", "destination_col", "uri") ...
<varname>state_col</varname> (string) The column's name in the database storing the state of the destination uri. Default value is state. Set <quote>state_col</quote> parameter ... modparam("dispatcher", "state_col", "dststate") ...
<varname>weight_col</varname> (string) The column's name in the database storing the weight for destination uri. Default value is weight. Set <quote>weight_col</quote> parameter ... modparam("dispatcher", "weight_col", "dstweight") ...
<varname>priority_col</varname> (string) The column's name in the database storing the priority for destination uri. Default value is priority. Set <quote>priority_col</quote> parameter ... modparam("dispatcher", "priority_col", "dstprio") ...
<varname>attrs_col</varname> (string) The column's name in the database storing the attributes (opaque string) for destination uri. Default value is attrs. Set <quote>attrs_col</quote> parameter ... modparam("dispatcher", "attrs_col", "dstattrs") ...
<varname>socket_col</varname> (string) The column's name in the database storing the socket (as string) for destination uri. Default value is socket. Set <quote>socket_col</quote> parameter ... modparam("dispatcher", "socket_col", "my_sock") ...
Exported Functions
<function moreinfo="none">ds_select_dst(set, alg [, (flags M max_results)*])</function> The method selects a destination from the given set of addresses. It will overwrite the "destination URI" of a SIP request ($du). Meaning of the parameters is as follows: set - a partition name followed by colon and an id of the set or a list of sets from where to pick up destination address (variables are accepted). If the partition name is missing, the default partition will be used. alg - the algorithm(s) used to select the destination address (variables are accepted). 0 - hash over callid 1 - hash over from uri. 2 - hash over to uri. 3 - hash over request-uri. 4 - round-robin (next destination). 5 - hash over authorization-username (Proxy-Authorization or "normal" authorization). If no username is found, round robin is used. 6 - random (using rand()). 7 - hash over the content of PVs string. Note: This works only when the parameter hash_pvar is set. 8 - the first entry in set is chosen. 9 - The pvar_algo_pattern parameter is used to determine the load on each server. If the parameter is not specified, then the first entry in the set is chosen. X - if the algorithm is not implemented, the first entry in set is chosen. flags M max_results - If specified, this will be the flags which in previous versions were specified at startup. The flags are: 'f'/'F' - the failover support flag, 'u'/'U' - the user only flag - will specify that only the uri user part will be used for hashing, 'S'/'s' - the force destination flag - which will skip overwriting the destination address if it is already set, 'D'/'d' - the use default flag('D','d') - which will use the last address in destination set as last option to send the message. You can also specify these flags using PVs. The flags are being kept per partition. The second paramater, max_results represents that only a maximum of max_results will be put into the specified avp for failover. This allows having many destinations but limit the useless traffic in case of a number that is bound to fail everywhere. Since version 2.1, the last paramater cand be represented by a list of flags and max_results, separated by comma. You can specify either only the flags, either only the max_results paramater, but if you want to specify them together you have to use the 'M' character like this: flags M max_results. If the character 'f' in 'flags' is set, the rest of the addresses from the destination set is stored in AVP list. You can use 'ds_next_dst()' to use next address to achieve serial forking to all possible destinations. If multiple dispatching groups are used, the AVP list is constructed based on the position of each dispatching id in the list: first one has the higher priority, followed by the second one and so on. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. <function>ds_select_dst</function> usage ... ds_select_dst("1", "0"); ... ds_select_dst("part2 : 1", "0", "5"); ... ds_select_dst("part3 : 1", "0", "fUsD"); ... ds_select_dst("part4 : 2,3", "0,1", "fuD M 5, fuS M 2"); ... # dispatch over multiple dispatching groups $var(part_name) = "p4" $var(setid) = "1, 2"; $var(alg) = "4, 2"; $var(flags) = " sFDU M 2, fuS M 3"; ds_select_dst("$var(part_name):$var(setid)","$var(alg)","$var(flags)"); ...
<function moreinfo="none">ds_select_domain(set, alg [, "[flags] [M max_results]"])</function> The method selects a destination from addresses set and rewrites the host and port from R-URI. The parameters have same meaning as for ds_select_dst(). If the character 'f' in 'flags' is set, the rest of the addresses from the destination set is stored in AVP list. You can use 'ds_next_domain()' to use next address to achieve serial forking to all possible destinations. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
<function moreinfo="none">ds_next_dst([partition_name])</function> Takes the next destination address from the AVPs with id partition.'dst_avp_id' and sets the dst_uri (outbound proxy address). If partition_name is omitted, the default partition will be used.This function is using the flags set in ds_select_dst or ds_select_domain. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
<function moreinfo="none">ds_next_domain([partition_name])</function> Takes the next destination address from the AVPs with id partition.'dst_avp_id' and sets the domain part of the request uri. If partition_name is omitted, the default partition will be used.This function is using the flags set in ds_select_dst or ds_select_domain. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
<function moreinfo="none">ds_mark_dst()</function> Mark the last used address from the 'default' partition's destination set as inactive, in order to be ignored in the future. In this way it can be implemented an automatic detection of failed gateways. When an address is marked as inactive, it will be ignored by 'ds_select_dst' and 'ds_select_domain'. This function is using the flags set in ds_select_dst or ds_select_domain. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
<function moreinfo="none">ds_mark_dst([partition_name], "s")</function> Mark the last used address from partition's destination set as inactive ("i"/"I"/"0"), active ("a"/"A"/"1") or probing ("p"/"P"/"2"). With this function, an automatic detection of failed gateways can be implemented. When an address is marked as inactive or probing, it will be ignored by 'ds_select_dst' and 'ds_select_domain'. If partition_name is omitted, the default partition will be used. This function is using the flags set in ds_select_dst or ds_select_domain. possible parameters: "i", "I" or "0" - the last destination should be set to inactive and will be ignored in future requests. "a", "A" or "1" - the last destination should be set to active. "p", "P" or "2" - the last destination will be set to probing. Note: You will need to call this function "threshold"-times, before it will be actually set to probing. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
<function moreinfo="none">ds_count(set, filter, result)</function> Returns the number of active, inactive or probing destinations in a partition's set, or combinations between these properties. Meaning of the parameters: set - a partition name followed by colon and an id of a set of dispatching destinations (variables are accepted). If the partition name is missing, the default partition will be used. filter - which destinations should be counted. Either active destinations("a", "A" or "1"), inactive destinations("i", "I" or "0") or probing ones("p", "P" or "2") or different combinations between these flags, such as "pI", "1i", "ipA"... The filter parameter can have the following types: string - the filtering flags are statically assigned result - A pseudovariable for storing the result. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, TIMER_ROUTE, EVENT_ROUTE <function>ds_count</function> usage ... if (ds_count("1", "a", "$avp(result)")) { ... } ... if (ds_count("$avp(partition) : $avp(set)", "ip", "$avp(result)")) { ... } ...
<function moreinfo="none">ds_is_in_list(ip, port [,set [,active_only]])</function> This function returns true, if the parameters ip and port point to a host from the dispatcher-list; otherwise false. Meaning of the parameters: ip - string / pseudo-variable (as string) containing the IP to test against the dispatcher list. This cannot be empty. port - int / pseudo-variable (as int) containing the PORT to test against the dispatcher list. This can be empty - in this case the port will excluded from the matching of IP against the dispatcher list. set (optional) - a partition name followed by colon and the set ID of a dispatcher list to test against. If the partition name is omitted the default partition will be used. If the set id is missing, all the dispatching sets will the checked. If a partition name is specified then it must be followed by colon regardless of the set id being specified or not. -1 can be used as a wildcard to check in all sets. active_only (optional) - search only through the active destinations (ignore the ones in probing and inactive mode). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and ONREPLY_ROUTE. <function>ds_is_in_list</function> usage ... if (ds_is_in_list("$si", "$sp")) { # source IP:PORT is in a dispatcher list } ... if (ds_is_in_list("$rd", "$rp", "2")) { # source RURI (ip and port) is in the dispatcher list id "2" of the default partition } ... if (ds_is_in_list("$rd", "$rp", "part2:2")) { # source RURI (ip and port) is in the dispatcher list id "2" of the partition called 'part2' } ...
Exported MI Functions
<function moreinfo="none">ds_set_state</function> Sets the status for a destination address (can be use to mark the destination as active or inactive). Name: ds_set_state Parameters: _state_ : state of the destination address a: active i: inactive p: probing _group_: partition name followed by colon and destination group id. If the partition name is omitted, the default partition will be used _address_: address of the destination in the _group_ MI FIFO Command Format: :ds_set_state:_reply_fifo_file_ _state_ _group_ _address_ _empty_line_
<function moreinfo="none">ds_list</function> It lists the groups and included destinations of all the partitions. Name: ds_list Parameters: full (optional) - adds the weight, priority and description fields to the listing MI FIFO Command Format: :ds_list:_reply_fifo_file_ _empty_line_
<function moreinfo="none">ds_reload</function> It reloads the groups and included destinations for a specified partition or all partitions. Name: ds_reload Parameters: partition (optional) - name of the partition to be reloaded. MI DATAGRAM Command Format: ":ds_reload:\n."
Exported Events
<function moreinfo="none">E_DISPATCHER_STATUS</function> This event is raised when the dispatcher module marks a destination as activated or deactivated. Parameters: partition - the partition name of the destination. group - the group of the destination. address - the address of the destination. status - active if the destination gets activated or inactive if the destination is detected unresponsive.
Installation and Running
&osips; config file Next picture displays a sample usage of dispatcher. &osips; config script - sample dispatcher usage ... &dispatchercfg; ...
opensips-2.2.2/modules/dispatcher/doc/dispatcher_faq.xml000066400000000000000000000073461300170765700234370ustar00rootroot00000000000000 &faqguide; Does dispatcher provide a fair distribution? There is no guarantee of that. You should do some measurements to decide what distribution algorithm fits better in your environment. Is dispatcher dialog stateful? No. Dispatcher is stateless, although some distribution algorithms are designed to select same destination for subsequent requests of the same dialog (e.g., hashing the call-id). What happened with the ds_is_from_list() function? The function was replaced by the more generic ds_is_in_list() function that takes as parameters the IP and PORT to test against the dispatcher list. ds_is_from_list() == ds_is_in_list("$si","$sp") How is weight and priority used by the dispatcher in selecting a destination? The weight of a destination is currently used in the hashing algorithms and it increases the probability of it to be chosen(if we have two destinations with weights 1 respectively 4 than the second one is 4 times more likely to be selected than the other). The sum of all weights does not need to add up to a specific number. It is important to understand that the weights are not used in the round-robin algorithm at the moment. The priority field is used at ordering the destinations from a set. It does not affect the overall probability of a destination to be chosen. It is reflected when listing the destination, the field can definetly be used in further selecting algorithms. What happened with the list_file module parameter ? The support for text file (for provisioning destinations) was dropped. Only the DB support (provisioning via a DB table) is now available - if you still want to use a text file for provisioning, use db_text DB driver (DB emulated via text files) Where can I find more about &osips;? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable version should be sent to &osipsusersmail; and e-mail regarding development versions or SVN snapshots should be send to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink; opensips-2.2.2/modules/dispatcher/ds_bl.c000066400000000000000000000126661300170765700204230ustar00rootroot00000000000000/** * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2012-09-24 created (liviuchircu) * */ #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../ut.h" #include "../../trim.h" #include "../../ip_addr.h" #include "ds_bl.h" static struct ds_bl *dsbl_lists = NULL; static ds_bl_temp_t *blacklists = NULL; int set_ds_bl(modparam_t type, void *val) { static const str default_part_name = str_init(DS_DEFAULT_PARTITION_NAME); return set_ds_bl_partition((char*)val, default_part_name); } int set_ds_bl_partition(char *val, str partition_name) { ds_bl_temp_t *new_bl = pkg_malloc(sizeof (ds_bl_temp_t)); if (new_bl == NULL) { LM_ERR ("no more private memory\n"); return -1; } new_bl->text = val; new_bl->partition_name = partition_name; new_bl->next = blacklists; blacklists = new_bl; return 0; } int init_ds_bls(void) { struct ds_bl *dsbl; str name; str val; char *p; ds_bl_temp_t *bs_it = blacklists, *aux; LM_DBG("Initialising ds blacklists\n"); if (blacklists == NULL) return 0; while (bs_it) { LM_DBG("processing bl definition <%s>\n", bs_it->text); /* get name */ p = strchr( bs_it->text, '='); if (p==NULL || p==bs_it->text) { LM_ERR("blacklist definition <%s> has no name", bs_it->text); return -1; } name.s = bs_it->text; name.len = p - name.s; trim(&name); if (name.len == 0) { LM_ERR("empty name in blacklist definition <%s>\n", bs_it->text); return -1; } LM_DBG("found list name <%.*s>\n", name.len, name.s); /* alloc structure */ dsbl = shm_malloc(sizeof(*dsbl)); if (dsbl == NULL) { LM_ERR("no more shme memory\n"); return -1; } memset(dsbl, 0, sizeof(*dsbl)); dsbl->partition_name = bs_it->partition_name; /* fill in the types */ p++; do { if (dsbl->no_sets == DS_BL_MAX_SETS) { LM_ERR("too many types per rule <%s>\n", bs_it->text); shm_free(dsbl); return -1; } val.s = p; p = strchr( p, ','); if (p == NULL) { val.len = strlen(val.s); } else { val.len = (int)(long)(p - val.s); p++; } trim(&val); if (val.len == 0) { LM_ERR("invalid types listing in <%s>\n", bs_it->text); shm_free(dsbl); return -1; } LM_DBG("found type <%.*s>\n", val.len, val.s); if (str2int( &val, &dsbl->sets[dsbl->no_sets])!=0) { LM_ERR("nonnumerical type <%.*s>\n", val.len, val.s); shm_free(dsbl); return -1; } dsbl->no_sets++; } while(p != NULL); /* create backlist for it */ dsbl->bl = create_bl_head( 313131, 0/*flags*/, NULL, NULL, &name); if (dsbl->bl == NULL) { LM_ERR("CREATE bl <%.*s> failed.\n", name.len, name.s); shm_free(dsbl); return -1; } aux = bs_it; bs_it = bs_it->next; pkg_free(aux); /* link it */ dsbl->next = dsbl_lists; dsbl_lists = dsbl; } blacklists = NULL; return 0; } void destroy_ds_bls(void) { struct ds_bl *dsbl; while ((dsbl = dsbl_lists)) { dsbl_lists = dsbl_lists->next; shm_free(dsbl); } } int populate_ds_bls(ds_set_t *sets, str partition_name) { unsigned int i,k; struct ds_bl *dsbl; ds_set_p set; ds_dest_p dst; struct bl_rule *dsbl_first; struct bl_rule *dsbl_last; struct net *set_net; LM_DBG("Updating ds blacklists...\n"); //TODO this could be done better /* each bl list at a time */ for(dsbl = dsbl_lists; dsbl; dsbl = dsbl->next) { if (str_strcmp(&partition_name, &dsbl->partition_name) != 0) continue; dsbl_first = dsbl_last = NULL; /* each blacklisted set at a time */ for (i = 0; i < dsbl->no_sets; i++) { /* search if any set matches the one above */ for( set=sets ; set ; set = set->next) { if (set->id == dsbl->sets[i]) { LM_DBG("Set [%d] matches. Adding all destinations:\n", set->id); for (dst = set->dlist; dst; dst = dst->next) { /* and add all IPs for each destination */ for( k=0 ; kips_cnt ; k++ ) { //print_ip(NULL, &dst->ips[k], "\n"); set_net = mk_net_bitlen( &dst->ips[k], dst->ips[k].len*8); if (set_net == NULL) { LM_ERR("BUILD netmask failed.\n"); continue; } /* add this destination to the BL */ add_rule_to_list( &dsbl_first, &dsbl_last, set_net, NULL/*body*/, dst->ports[k], dst->protos[k], 0/*flags*/); pkg_free(set_net); } } } } } /* the new content for the BL */ if (dsbl->bl && add_list_to_head( dsbl->bl, dsbl_first, dsbl_last, 1, 0) != 0) { LM_ERR("UPDATE blacklist failed for list <%.*s> in partition <%.*s>." " Possibly, none of the sets in this list exists\n", dsbl->bl->name.len, dsbl->bl->name.s, partition_name.len, partition_name.s); return -1; } } return 0; } opensips-2.2.2/modules/dispatcher/ds_bl.h000066400000000000000000000027121300170765700204170ustar00rootroot00000000000000/** * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2012-09-24 created (liviuchircu) * */ #ifndef _DS_BL_H_ #define _DS_BL_H_ #include "../../blacklists.h" #include "dispatch.h" #define DS_BL_MAX_SETS 32 typedef struct _ds_bl_temp { char *text; str partition_name; struct _ds_bl_temp *next; } ds_bl_temp_t; struct ds_bl { unsigned int no_sets; unsigned int sets[DS_BL_MAX_SETS]; str partition_name; struct bl_head *bl; struct ds_bl *next; }; int set_ds_bl(modparam_t type, void *val); int set_ds_bl_partition(char *val, str partition_name); int init_ds_bls(void); void destroy_ds_bls(void); int populate_ds_bls(ds_set_t *sets, str partition_name); #endif /* _DS_BL_H_ */ opensips-2.2.2/modules/dispatcher/ds_fixups.c000066400000000000000000000402521300170765700213340ustar00rootroot00000000000000/** * dispatcher module fixup functions * * Copyright (C) 2004-2005 FhG Fokus * Copyright (C) 2006-2010 Voice Sistem SRL * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-07-08 initial version (Andrei Datcu) */ #include "ds_fixups.h" #include "../../trim.h" #define LIST_DELIM ',' #define FLAGS_DELIM 'M' extern ds_partition_t *default_partition; extern ds_partition_t *partitions; /* * Expand a pvar into a list of ints */ int_list_t *set_list_from_pvs(struct sip_msg *msg, pv_spec_t *pvs, int_list_t *end) { int_list_t *result = end, *new_el; pv_value_t value; if (pv_get_spec_value(msg, pvs, &value) != 0 || value.flags&PV_VAL_NULL || (!(value.flags&PV_VAL_INT) && !(value.flags&PV_VAL_STR))) { LM_ERR("no valid PV value found (error in scripts)\n"); return NULL; } if (value.flags & PV_VAL_INT) { /* Just one element */ new_el = pkg_malloc(sizeof(int_list_t)); if (new_el == NULL) { LM_ERR("no more shared memory\n"); return NULL; } new_el->v.ival = value.ri; new_el->type = GPARAM_TYPE_INT; new_el->next = end; return new_el; } str sval = value.rs; if (sval.s == NULL) goto wrong_value; char * delim; do{ delim = q_memchr(sval.s, LIST_DELIM, sval.len); str s_num = {sval.s, delim ? delim - sval.s : sval.len}; sval.len -= s_num.len + 1; sval.s = delim + 1; trim(&s_num); int u_num; if (s_num.len == 0 || str2sint(&s_num, &u_num) != 0) goto wrong_value; new_el = pkg_malloc(sizeof(int_list_t)); if (new_el == NULL) { goto no_memory; } new_el->v.ival = u_num; new_el->type = GPARAM_TYPE_INT; new_el->next = result; result = new_el; } while (delim); if (sval.len > 0) goto wrong_value; return result; no_memory: while(result != end) { if (result->type == GPARAM_TYPE_PVS) pkg_free(result->v.pvs); int_list_t *aux = result; result = result->next; pkg_free(aux); } LM_ERR("no more private memory\n"); return NULL; wrong_value: while(result != end) { if (result->type == GPARAM_TYPE_PVS) pkg_free(result->v.pvs); int_list_t *aux = result; result = result->next; pkg_free(aux); } LM_ERR("wrong var value <%.*s>\n", value.rs.len, value.rs.s); return NULL; } /* * Create an int list from a string. Eg ("1, 2, 4") */ int set_list_from_string(str input, int_list_t **result) { str original_input = input; int_list_t *new_el=NULL; int flags=0; int uset; *result = NULL; if (input.s == NULL || input.len == 0) return 0; if (str2sint(&input, &uset) == 0) { /* Just one set in the list */ *result = pkg_malloc(sizeof(int_list_t)); if (*result == NULL) goto no_memory; (*result)->v.ival = uset; (*result)->type = GPARAM_TYPE_INT; (*result)->next = NULL; return 0; } char * delim, *pvdelim, *flagsdelim=NULL; str flg_tok; unsigned int u_num=0; int def_val = -1; do{ delim = q_memchr(input.s, LIST_DELIM, input.len); str s_tok = {input.s, delim ? delim - input.s : input.len}; int full_tok_len = s_tok.len; trim(&s_tok); /* search if only max results */ if (s_tok.s[0] >= '0' && s_tok.s[0] <= '9') { flags = 0; goto only_max_res; } /*search for flags flags/maxlist delimiter*/ flagsdelim=q_memchr(s_tok.s, FLAGS_DELIM, s_tok.len); if (flagsdelim == NULL) { /* search for only flags */ if ((s_tok.s[0] >= 'a' && s_tok.s[0] <= 'z') || (s_tok.s[0] >= 'A' && s_tok.s[0] <= 'Z')) { flg_tok.s = s_tok.s; flg_tok.len=0; if ((flg_tok.s[flg_tok.len] >= 'a' && flg_tok.s[flg_tok.len] <= 'z') || (flg_tok.s[flg_tok.len] >= 'A' && flg_tok.s[flg_tok.len] <= 'Z')) flg_tok.len=s_tok.len; goto only_flags00; } } /* if found parse the flags */ if (flagsdelim != NULL) { flg_tok.s = s_tok.s; flg_tok.len = flagsdelim - s_tok.s; only_flags00: /* update list token */ s_tok.s += flg_tok.len +1; s_tok.len -= (flg_tok.len +1); new_el = pkg_malloc(sizeof(int_list_t)); if (new_el == NULL) goto no_memory; memset(new_el, 0, sizeof(int_list_t)); trim(&flg_tok); /* must fixup flags string value */ if ((flags = fixup_flags(&flg_tok)) < 0) { LM_ERR("cannot fixup flags\n"); return -1; } trim(&s_tok); /* default value for max results */ def_val = 1000; } only_max_res: if (s_tok.len == 0) { if (flags > 0) { goto only_flags01; } else goto wrong_value; } else if (s_tok.s[0] == PV_MARKER) { if (new_el == NULL) { new_el = pkg_malloc(sizeof(int_list_t)); if (new_el == NULL) goto no_memory; } new_el->type = GPARAM_TYPE_PVS; new_el->v.pvs = pkg_malloc(sizeof(pv_spec_t)); if (new_el->v.pvs == NULL) { pkg_free(new_el); goto no_memory; } if ((pvdelim = pv_parse_spec(&s_tok, new_el->v.pvs)) == NULL) { pkg_free(new_el->v.pvs); pkg_free(new_el); goto wrong_value; } new_el->next = *result; *result = new_el; new_el = NULL; if (delim) if (delim != pvdelim) goto wrong_value; else { input.len -= delim - input.s + 1; input.s = delim + 1; } else { input.len -= pvdelim - input.s + 1; input.s = pvdelim; } } else if (str2int(&s_tok, &u_num) == 0) { /* * don't alloc twice * if both flags and max_results defined * it is already allocated * */ if (new_el == NULL) { new_el = pkg_malloc(sizeof(int_list_t)); if (new_el == NULL) goto no_memory; } only_flags01: new_el->v.ival = def_val > 0 ? def_val : u_num; new_el->type = GPARAM_TYPE_INT; if (flags>0) new_el->flags = flags; new_el->next = *result; *result = new_el; new_el = NULL; input.len -= full_tok_len + 1; input.s = delim + 1; } else goto wrong_value; } while (delim); if (input.len > 0) goto wrong_value; return 0; no_memory: while(*result) { if ((*result)->type == GPARAM_TYPE_PVS) pkg_free((*result)->v.pvs); int_list_t *aux = *result; *result = (*result)->next; pkg_free(aux); } LM_ERR("no more shared memory\n"); return -1; wrong_value: while(*result) { if ((*result)->type == GPARAM_TYPE_PVS) pkg_free((*result)->v.pvs); int_list_t *aux = *result; *result = (*result)->next; pkg_free(aux); } LM_ERR("wrong format for set/set list. Token <%.*s>\n", original_input.len, original_input.s); return -1; } /* * Create a general partition from a string (variable or plain-text name) */ static int get_gpart(str *input, gpartition_t *partition) { if (input->s == NULL) { partition->type = GPART_TYPE_POINTER; partition->v.p = default_partition; return 0; } if (input->s[0] == PV_MARKER) { partition->type = GPART_TYPE_PVS; partition->v.pvs = shm_malloc(sizeof(pv_spec_t)); if (partition->v.pvs == NULL) { LM_ERR ("no more shared memory\n"); return -1; } char *end; if ((end = pv_parse_spec(input, partition->v.pvs)) == NULL) { LM_ERR ("cannot parse variable\n"); return -1; } if (end - input->s != input->len) { LM_ERR ("wrong format for partition\n"); return -1; } return 0; } /* We have a static partition name */ ds_partition_t *part_it = partitions; for (; part_it; part_it = part_it->next) if (str_strcmp(&part_it->name, input) == 0) { partition->type = GPART_TYPE_POINTER; partition->v.p = part_it; return 0; } LM_ERR ("partition <%.*s> not found\n", input->len, input->s); return -1; } /* * Fixup for a string like "partition_name:set1, set2 * The set list may be missing" */ static int fixup_partition_sets_null(void **param) { str s_param = {(char*)*param, strlen(*param)}; str part_name = {NULL, 0}; char *delim = q_memchr(s_param.s, DS_PARTITION_DELIM, s_param.len); if (delim) { part_name.s = s_param.s; part_name.len = delim - s_param.s; s_param.s = delim + 1; s_param.len -= part_name.len + 1; trim(&part_name); } trim(&s_param); ds_param_t *final_param = shm_malloc(sizeof (ds_param_t)); if (final_param == NULL) { LM_CRIT ("no more shared memory!\n"); return -1; } if (get_gpart(&part_name, &final_param->partition) != 0) { shm_free(final_param); return -1; } if ((set_list_from_string(s_param, &final_param->sets)) != 0){ shm_free(final_param); return -1; } *param = (void*)final_param; return 0; } /* * Fixup for a string like "partition_name:set1, set2 * The set list is mandatory" */ int fixup_partition_sets(void **param) { if (fixup_partition_sets_null(param) != 0) return -1; if (((ds_param_t*)*param)->sets == NULL) { /* Null sets are not allowed */ LM_ERR("A set must be specified!\n"); return -1; } return 0; } /* * Fixup for a string like "partition_name:set_no" * * Only one set number is allowed and it must not be missing */ int fixup_partition_one_set(void **param) { if (fixup_partition_sets(param) != 0) return -1; if (((ds_param_t*)*param)->sets->next != NULL) { LM_ERR("Only one set is accepted\n"); return -1; } return 0; } /* * Fixup for partition_name. * Turns char* into gpartition_t (i.e. pvspec or partition_t*) */ int fixup_partition(void **param) { gpartition_t *partition = shm_malloc (sizeof(gpartition_t)); str input = {(char*)(*param), strlen((char*)(*param))}; trim(&input); if (get_gpart(&input, partition) != 0) { shm_free(partition); return -1; } *param = (void*)partition; return 0; } /* * Get the actual partition from a gpartition_t */ int fixup_get_partition(struct sip_msg *msg, const gpartition_t *gpart, ds_partition_t **partition) { if (gpart->type == GPART_TYPE_POINTER) { *partition = gpart->v.p; return 0; } pv_value_t value; if(pv_get_spec_value(msg, gpart->v.pvs, &value)!=0 || value.flags&PV_VAL_NULL || !(value.flags&PV_VAL_STR)) { LM_ERR("no valid PV value found (error in scripts)\n"); return -1; } if (value.rs.len == 0) { *partition = default_partition; return 0; } ds_partition_t *part_it = partitions; for (; part_it; part_it = part_it->next) if (part_it->name.len == value.rs.len && memcmp(part_it->name.s, value.rs.s, value.rs.len) == 0) { *partition = part_it; return 0; } *partition = NULL; return 0; } /* * Fixup for an int list */ int fixup_int_list(void **param) { str input = {(char*)(*param), strlen((char*)(*param))}; int_list_t *lst; if (set_list_from_string(input, &lst) != 0 || lst == NULL) return -1; *param = (void*)(lst); return 0; } /* * Fixup for flags */ int fixup_flags(str* param) { #define PV_DELIM ')' #define FLAG_ERR(_flag_msg_)\ do{\ LM_ERR("Cannot set " #_flag_msg_ " flag\n");\ return -1;\ } while(0); int index, ret=0; for(index=0; index < param->len; index++) { switch (param->s[index]) { case ' ': break; case 'f': case 'F': if (ret & DS_FAILOVER_ON) { FLAG_ERR(failover (F)); } else { ret |= DS_FAILOVER_ON; } break; case 'u': case 'U': if (ret & DS_HASH_USER_ONLY) { FLAG_ERR(hash user (U)); } else { ret |= DS_HASH_USER_ONLY; } break; case 'd': case 'D': if (ret & DS_USE_DEFAULT) { FLAG_ERR(use default (D)); } else { ret |= DS_USE_DEFAULT; } break; case 's': case 'S': if (ret & DS_FORCE_DST) { FLAG_ERR(force dst (S)); } else { ret |= DS_FORCE_DST; } break; default : LM_ERR("Invalid definition\n"); return -1; } } return ret; #undef FLAG_ERR } /* * Free an expanded list (obtained with set_list_from_pvs) * Delete everything in the range [start, end). * Do not use this function to erase any other lists */ void free_int_list(int_list_t *start, int_list_t *end) { int_list_t *aux; while (start != end) { aux = start; start = start->next; pkg_free(aux); } } /* * Search for value in int_list_t */ int in_int_list(int_list_t *list, int val) { int_list_t *tmp; for (tmp=list;tmp!=NULL;tmp=tmp->next) { if (tmp->type == GPARAM_TYPE_INT && tmp->v.ival == val) return 0; } return -1; } /* * Get a partition and a set from a general ds_param structure */ int fixup_get_partition_set(struct sip_msg *msg, const ds_param_t *param, ds_partition_t **partition, unsigned int *uset) { if (fixup_get_partition(msg, ¶m->partition, partition) != 0) return -1; if (*partition == NULL) { LM_ERR("unknown partition\n"); return -1; } if (param->sets->type == GPARAM_TYPE_INT) { *uset = param->sets->v.ival; return 0; } int_list_t *tmp = set_list_from_pvs(msg, param->sets->v.pvs, NULL); if (tmp == NULL || tmp->next != NULL) { LM_ERR("Wrong variable value for set\n"); return -1; } *uset = tmp->v.ival; free_int_list(tmp, NULL); return 0; } /* Fixup function for ds_next_dst and ds_next_domain functions */ int ds_next_fixup(void **param, int param_no) { if (param_no > 1) { LM_CRIT ("Too many parameters for ds_next_dst/ds_next_domain\n"); return -1; } return fixup_partition(param); } /* Fixup function for ds_mark_dst command */ int ds_mark_fixup(void **param, int param_no) { if (param_no == 1) return fixup_partition(param); else if (param_no == 2) return fixup_sgp(param); else return -1; } /* Fixup function for ds_is_in_list command */ int in_list_fixup(void** param, int param_no) { if (param_no==1) { /* the ip to test */ return fixup_sgp(param); } else if (param_no==2) { /* the port to test */ if (*param==NULL) { return 0; } else if ( *((char*)*param)==0 ) { pkg_free(*param); *param = NULL; return 0; } return fixup_igp(param); } else if (param_no==3) { if (fixup_partition_sets_null(param) != 0) return -1; int_list_t *sets = ((ds_param_t*)*param)->sets; if (sets && sets->next) { LM_ERR("Only one set is accepted\n"); return -1; } return 0; } else if (param_no==4) { /* active only check ? */ return fixup_sint(param); } else { LM_CRIT("bug - too many params (%d) in is_in_list()\n",param_no); return -1; } } /* Fixup function for ds_select_dst and ds_select_domain commands */ int ds_select_fixup(void** param, int param_no) { if (param_no > 3) { LM_CRIT("Too many params for ds_select_*\n"); return -1; } pv_elem_t* elem; str s; max_list_param_p maxlst = NULL; int rc = 0; switch (param_no) { case 1: return fixup_partition_sets(param); case 2: return fixup_int_list(param); case 3: s.s = *param; s.len = strlen(s.s); trim_spaces_lr(s); if (s.len == 0) { LM_ERR("3rd parameter (flags max_results) is empty\n"); return -1; } if (pv_parse_format(&s, &elem)) { LM_ERR("wrong format [%s] for param no %d!\n", (char*)(*param), param_no); } maxlst = pkg_malloc(sizeof(max_list_param_t)); if (maxlst == NULL) { LM_ERR("no mem\n"); return -1; } if (elem->text.len > 0 && elem->spec.setf == NULL && elem->next == NULL) { rc=fixup_int_list(param); maxlst->lst.list = *param; maxlst->type = MAX_LIST_TYPE_STR; } else { maxlst->lst.elem = elem; maxlst->type = MAX_LIST_TYPE_PV; rc = 0; } break; } *param = maxlst; return rc; } /* Fixup function for ds_count command */ int ds_count_fixup(void** param, int param_no) { char *s; int i, code = 0; if (param_no > 3) return 0; s = (char *)*param; i = strlen(s); switch (param_no) { case 1: return fixup_partition_one_set(param); case 2: while (i--) { switch (s[i]) { /* active */ case 'a': case 'A': case '1': code |= DS_COUNT_ACTIVE; break; /* inactive */ case 'i': case 'I': case '0': code |= DS_COUNT_INACTIVE; break; /* probing */ case 'p': case 'P': case '2': code |= DS_COUNT_PROBING; break; } } break; case 3: return fixup_igp(param); } s[0] = (char)code; s[1] = '\0'; return 0; } opensips-2.2.2/modules/dispatcher/ds_fixups.h000066400000000000000000000054071300170765700213440ustar00rootroot00000000000000/** * dispatcher module fixup functions * * Copyright (C) 2004-2005 FhG Fokus * Copyright (C) 2006-2010 Voice Sistem SRL * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-07-08 initial version (Andrei Datcu) */ #ifndef _DS_FIXUPS_H_ #define _DS_FIXUPS_H_ #include "dispatch.h" #include "../../mod_fix.h" #define MAX_LIST_TYPE_STR (1<<0) #define MAX_LIST_TYPE_PV (1<<1) /* Structure that contains a general description of a partition: * either its name through a pv_spec or a pointer to the coresponding * ds_partition_t partition */ typedef struct { union { ds_partition_t *p; pv_spec_t *pvs; } v; enum gparttype_t {GPART_TYPE_POINTER, GPART_TYPE_PVS} type; } gpartition_t; typedef struct _int_list_t { union { int ival; pv_spec_t *pvs; } v; int type; int flags; struct _int_list_t *next; } int_list_t; /* Structure that describes a general pair of a partition and a set list */ typedef struct { gpartition_t partition; int_list_t *sets; } ds_param_t; typedef struct max_list_param { union { int_list_t* list; pv_elem_t* elem; } lst; int type; } max_list_param_t, *max_list_param_p; extern int_list_t *ds_probing_list; int_list_t *set_list_from_pvs(struct sip_msg *msg, pv_spec_t *pvs, int_list_t *end); void free_int_list(int_list_t *start, int_list_t *end); int in_int_list(int_list_t *list, int val); int fixup_get_partition(struct sip_msg *msg, const gpartition_t *gpart, ds_partition_t **partition); int fixup_get_partition_set(struct sip_msg *msg, const ds_param_t *param, ds_partition_t **partition, unsigned int *uset); int fixup_partition_sets(void **param); int fixup_partition_one_set(void **param); int fixup_int_list(void **param); int ds_next_fixup(void **param, int param_no); int ds_mark_fixup(void **param, int param_no); int in_list_fixup(void** param, int param_no); int ds_select_fixup(void** param, int param_no); int ds_count_fixup(void** param, int param_no); int fixup_flags(str* param); int set_list_from_string(str input, int_list_t **result); #endif opensips-2.2.2/modules/diversion/000077500000000000000000000000001300170765700170355ustar00rootroot00000000000000opensips-2.2.2/modules/diversion/Makefile000066400000000000000000000003341300170765700204750ustar00rootroot00000000000000# $Id$ # # Diversion Header Field Support2 # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=diversion.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/diversion/README000066400000000000000000000120401300170765700177120ustar00rootroot00000000000000Diversion Module Jan Janak FhG FOKUS Edited by Jan Janak Edited by Saul Ibarra Corretge Copyright © 2004 FhG FOKUS Revision History Revision $Revision: 9064 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. suffix (string) 1.4. Exported Functions 1.4.1. add_diversion(reason [[,uri], counter]) 1.5. Diversion Example 2. Developer Guide List of Examples 1.1. suffix usage 1.2. add_diversion usage Chapter 1. Admin Guide 1.1. Overview The module implements the Diversion extensions as per draft-levy-sip-diversion-08. The diversion extensions are useful in various scenarios involving call forwarding. Typically one needs to communicate the original recipient of the call to the PSTN gateway and this is what the diversion extensions can be used for. Warning The draft-levy-sip-diversion-08 is expired!! See IETF I-D tracker. 1.2. Dependencies 1.2.1. OpenSIPS Modules None. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. suffix (string) The suffix to be appended to the end of the header field. You can use the parameter to specify additional parameters to be added to the header field, see the example. Default value is Ҡ(empty string). Example 1.1. suffix usage modparam("diversion", "suffix", ";privacy=full") 1.4. Exported Functions 1.4.1. add_diversion(reason [[,uri], counter]) The function adds a new diversion header field before any other existing Diversion header field in the message (the newly added Diversion header field will become the topmost Diversion header field). The inbound (without any modifications done by the proxy server) Request-URI will be used as the Diversion URI. Meaning of the parameters is as follows: * reason - The reason string to be added as the reason parameter * uri - The URI to be added in the header. If missing the unchanged RURI from the original message will be used. * counter - Diversion counter to be added to the header, as defined by the standard. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.2. add_diversion usage ... add_diversion("user-busy"); ... 1.5. Diversion Example The following example shows a Diversion header field added to INVITE message. The original INVITE received by the user agent of sip:bob@sip.org is: INVITE sip:bob@sip.org SIP/2.0 Via: SIP/2.0/UDP 1.2.3.4:5060 From: "mark" ;tag=ldgheoihege To: "Bob" Call-ID: adgasdkgjhkjha@1.2.3.4 CSeq: 3 INVITE Contact: Content-Length: 0 The INVITE message is diverted by the user agent of sip:bob@sip.org because the user was talking to someone else and the new destination is sip:alice@sip.org : INVITE sip:alice@sip.org SIP/2.0 Via: SIP/2.0/UDP 5.6.7.8:5060 Via: SIP/2.0/UDP 1.2.3.4:5060 From: "mark" ;tag=ldgheoihege To: "Bob" Call-ID: adgasdkgjhkjha@1.2.3.4 CSeq: 3 INVITE Diversion: ;reason=user-busy Contact: Content-Length: 0 Chapter 2. Developer Guide According to the specification new Diversion header field should be inserted as the topmost Diversion header field in the message, that means before any other existing Diversion header field in the message. In addition to that, add_diversion function can be called several times and each time it should insert the new Diversion header field as the topmost one. In order to implement this, add_diversion function creates the anchor in data_lump lists as a static variable to ensure that the next call of the function will use the same anchor and would insert new Diversion headers before the one created in the previous execution. To my knowledge this is the only way of inserting the diversion header field before any other created in previous runs of the function. The anchor kept this way is only valid for a single message and we have to invalidate it when another message is being processed. For this reason, the function also stores the id of the message in another static variable and compares the value of that variable with the id of the SIP message being processed. If they differ then the anchor will be invalidated and the function creates a new one. The following code snippet shows the code that invalidates the anchor, new anchor will be created when the anchor variable is set to 0. static inline int add_diversion_helper(struct sip_msg* msg, str* s) { static struct lump* anchor = 0; static int msg_id = 0; if (msg_id != msg->id) { msg_id = msg->id; anchor = 0; } ... } opensips-2.2.2/modules/diversion/diversion.c000066400000000000000000000125541300170765700212120ustar00rootroot00000000000000/* * Diversion Header Field Support * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../data_lump.h" #include "../../mod_fix.h" #include "../../ut.h" #define DIVERSION_HF "Diversion" #define DIVERSION_HF_LEN (sizeof(DIVERSION_HF) - 1) #define DIVERSION_PREFIX DIVERSION_HF ": <" #define DIVERSION_PREFIX_LEN (sizeof(DIVERSION_PREFIX) - 1) #define DIVERSION_SUFFIX ">;reason=" #define DIVERSION_SUFFIX_LEN (sizeof(DIVERSION_SUFFIX) - 1) #define DIVERSION_COUNTER ";counter=" #define DIVERSION_COUNTER_LEN (sizeof(DIVERSION_COUNTER) - 1) str suffix = {"", 0}; int add_diversion(struct sip_msg* msg, char* _s1, char* _s2, char* _s3); static int fixup_diversion_params(void **param, int param_no); /* * Module initialization function prototype */ static int mod_init(void); /* * Exported functions */ static cmd_export_t cmds[] = { {"add_diversion", (cmd_function)add_diversion, 1, fixup_diversion_params, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"add_diversion", (cmd_function)add_diversion, 2, fixup_diversion_params, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"add_diversion", (cmd_function)add_diversion, 3, fixup_diversion_params, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"suffix", STR_PARAM, &suffix.s}, {0, 0, 0} }; /* * Module interface */ struct module_exports exports = { "diversion", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0 /* child initialization function */ }; static int mod_init(void) { LM_INFO("initializing...\n"); suffix.len = strlen(suffix.s); return 0; } static int fixup_diversion_params(void** param, int param_no) { if (param_no == 1) { /* diversion reason */ return fixup_str(param); } else if (param_no == 2) { /* diversion uri */ return fixup_spve(param); } else { /* diversion counter */ return fixup_uint(param); } } static inline int add_diversion_helper(struct sip_msg* msg, str* s) { char *ptr; static struct lump* anchor = 0; static unsigned int msg_id = 0; if (msg_id != msg->id) { msg_id = msg->id; anchor = 0; } if (!msg->diversion && parse_headers(msg, HDR_DIVERSION_F, 0) == -1) { LM_ERR("header parsing failed\n"); return -1; } if (msg->diversion) { /* Insert just before the topmost Diversion header */ ptr = msg->diversion->name.s; } else { /* Insert at the end */ ptr = msg->unparsed; } if (!anchor) { anchor = anchor_lump(msg, ptr - msg->buf, 0); if (!anchor) { LM_ERR("can't get anchor\n"); return -2; } } if (!insert_new_lump_before(anchor, s->s, s->len, 0)) { LM_ERR("can't insert lump\n"); return -3; } return 0; } int add_diversion(struct sip_msg* msg, char* _s1, char* _s2, char* _s3) { str div_hf; char *at; str uri; str* reason; char *counter_s; int counter_len; reason = (str*)_s1; if (_s2 == NULL || fixup_get_svalue(msg, (gparam_p)_s2, &uri) != 0) uri = msg->first_line.u.request.uri; if (_s3) { counter_s = int2str((unsigned long)(void *)_s3, &counter_len); } else { counter_len = -1; counter_s = NULL; } div_hf.len = DIVERSION_PREFIX_LEN + uri.len + DIVERSION_SUFFIX_LEN + reason->len; if (counter_len != -1) { div_hf.len += DIVERSION_COUNTER_LEN + counter_len; } div_hf.len += CRLF_LEN; div_hf.s = pkg_malloc(div_hf.len); if (!div_hf.s) { LM_ERR("no pkg memory left\n"); return -1; } at = div_hf.s; memcpy(at, DIVERSION_PREFIX, DIVERSION_PREFIX_LEN); at += DIVERSION_PREFIX_LEN; memcpy(at, uri.s, uri.len); at += uri.len; memcpy(at, DIVERSION_SUFFIX, DIVERSION_SUFFIX_LEN); at += DIVERSION_SUFFIX_LEN; memcpy(at, reason->s, reason->len); at += reason->len; if (counter_len != -1) { memcpy(at, DIVERSION_COUNTER, DIVERSION_COUNTER_LEN); at += DIVERSION_COUNTER_LEN; memcpy(at, counter_s, counter_len); at += counter_len; } memcpy(at, CRLF, CRLF_LEN); if (add_diversion_helper(msg, &div_hf) < 0) { pkg_free(div_hf.s); return -1; } return 1; } opensips-2.2.2/modules/diversion/doc/000077500000000000000000000000001300170765700176025ustar00rootroot00000000000000opensips-2.2.2/modules/diversion/doc/diversion.xml000066400000000000000000000025301300170765700223260ustar00rootroot00000000000000 %docentities; ]> Diversion Module &osipsname; Jan Janak &fhg;
jan@iptel.org
Jan Janak
jan@iptel.org
Saul Ibarra Corretge
saul@ag-projects.com
2004 &fhg; $Revision: 9064 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/diversion/doc/diversion_admin.xml000066400000000000000000000101551300170765700235000ustar00rootroot00000000000000 &adminguide;
Overview The module implements the Diversion extensions as per draft-levy-sip-diversion-08. The diversion extensions are useful in various scenarios involving call forwarding. Typically one needs to communicate the original recipient of the call to the PSTN gateway and this is what the diversion extensions can be used for. The draft-levy-sip-diversion-08 is expired!! See IETF I-D tracker.
Dependencies
&osips; Modules None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>suffix</varname> (string) The suffix to be appended to the end of the header field. You can use the parameter to specify additional parameters to be added to the header field, see the example. Default value is (empty string). <varname>suffix</varname> usage modparam("diversion", "suffix", ";privacy=full")
Exported Functions
<function moreinfo="none">add_diversion(reason [[,uri], counter])</function> The function adds a new diversion header field before any other existing Diversion header field in the message (the newly added Diversion header field will become the topmost Diversion header field). The inbound (without any modifications done by the proxy server) Request-URI will be used as the Diversion URI. Meaning of the parameters is as follows: reason - The reason string to be added as the reason parameter uri - The URI to be added in the header. If missing the unchanged RURI from the original message will be used. counter - Diversion counter to be added to the header, as defined by the standard. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function moreinfo="none">add_diversion</function> usage ... add_diversion("user-busy"); ...
Diversion Example The following example shows a Diversion header field added to INVITE message. The original INVITE received by the user agent of sip:bob@sip.org is: INVITE sip:bob@sip.org SIP/2.0 Via: SIP/2.0/UDP 1.2.3.4:5060 From: "mark" <sip:mark@sip.org>;tag=ldgheoihege To: "Bob" <sip:bob@sip.org> Call-ID: adgasdkgjhkjha@1.2.3.4 CSeq: 3 INVITE Contact: <sip:mark@1.2.3.4> Content-Length: 0 The INVITE message is diverted by the user agent of sip:bob@sip.org because the user was talking to someone else and the new destination is sip:alice@sip.org : INVITE sip:alice@sip.org SIP/2.0 Via: SIP/2.0/UDP 5.6.7.8:5060 Via: SIP/2.0/UDP 1.2.3.4:5060 From: "mark" <sip:mark@sip.org>;tag=ldgheoihege To: "Bob" <sip:bob@sip.org> Call-ID: adgasdkgjhkjha@1.2.3.4 CSeq: 3 INVITE Diversion: <sip:bob@sip.org>;reason=user-busy Contact: <sip:mark@1.2.3.4> Content-Length: 0
opensips-2.2.2/modules/diversion/doc/diversion_devel.xml000066400000000000000000000034571300170765700235160ustar00rootroot00000000000000 &develguide; According to the specification new Diversion header field should be inserted as the topmost Diversion header field in the message, that means before any other existing Diversion header field in the message. In addition to that, add_diversion function can be called several times and each time it should insert the new Diversion header field as the topmost one. In order to implement this, add_diversion function creates the anchor in data_lump lists as a static variable to ensure that the next call of the function will use the same anchor and would insert new Diversion headers before the one created in the previous execution. To my knowledge this is the only way of inserting the diversion header field before any other created in previous runs of the function. The anchor kept this way is only valid for a single message and we have to invalidate it when another message is being processed. For this reason, the function also stores the id of the message in another static variable and compares the value of that variable with the id of the SIP message being processed. If they differ then the anchor will be invalidated and the function creates a new one. The following code snippet shows the code that invalidates the anchor, new anchor will be created when the anchor variable is set to 0. static inline int add_diversion_helper(struct sip_msg* msg, str* s) { static struct lump* anchor = 0; static int msg_id = 0; if (msg_id != msg->id) { msg_id = msg->id; anchor = 0; } ... } opensips-2.2.2/modules/dns_cache/000077500000000000000000000000001300170765700167425ustar00rootroot00000000000000opensips-2.2.2/modules/dns_cache/Makefile000066400000000000000000000004521300170765700204030ustar00rootroot00000000000000# $Id: Makefile 6253 2009-10-12 19:00:53Z bogdan_iancu $ # # DNS caching # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=dns_cache.so DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib include ../../Makefile.modules opensips-2.2.2/modules/dns_cache/README000066400000000000000000000036111300170765700176230ustar00rootroot00000000000000dns_cache Module Vladut-Stefan Paiu OpenSIPS Solutions Edited by Vladut-Stefan Paiu Copyright © 2012 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.3. Exported Parameters 1.3.1. cachedb_url (string) 1.3.2. blacklist_timeout (int) 1.4. Exported Functions List of Examples 1.1. Set cachedb_url parameter 1.2. Set blacklist_timeout parameter Chapter 1. Admin Guide 1.1. Overview This module is an implementation of a cache system designed for DNS records. For succesfull DNS queries of all types, the module will store in a cache/db backend the mappings, for TTL number of seconds received in the DNS answer. Failed DNS queries will also be stored in the back-end, with a TTL that can be specified by the user. The module uses the Key-Value interface exported from the core. 1.2. Dependencies 1.2.1. OpenSIPS Modules A cachedb_* type module must be loaded before loading the dns_cache module. 1.3. Exported Parameters 1.3.1. cachedb_url (string) The url of the key-value back-end that will be used for storing the DNS records. Example 1.1. Set cachedb_url parameter ... #use internal cachedb_local module modparam("dns_cache", "cachedb_url","local://") #use cachedb_memcached module with memcached server at 192.168.2.130 modparam("dns_cache", "cachedb_url","memcached://192.168.2.130:8888/") ... 1.3.2. blacklist_timeout (int) The number of seconds that a failed DNS query will be kept in cache. Default is 3600. Example 1.2. Set blacklist_timeout parameter ... modparam("dns_cache", "blacklist_timeout",7200) # 2 hours ... 1.4. Exported Functions The module does not export functions to be used in configuration script. opensips-2.2.2/modules/dns_cache/dns_cache.c000066400000000000000000000523331300170765700210230ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2012-02-01 created (vlad) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../pt.h" #include "../../resolve.h" #include "../../cachedb/cachedb.h" #include "../../cachedb/cachedb_cap.h" static int mod_init(void); static int child_init(int); static void destroy(void); int put_dnscache_value(char *name,int r_type,void *record,int rdata_len, int failure,int ttl); void* get_dnscache_value(char *name,int r_type,int name_len); static cachedb_funcs cdbf; static cachedb_con *cdbc = 0; static int blacklist_timeout=3600; /* seconds */ static str cachedb_url = {0,0}; static param_export_t params[]={ { "cachedb_url", STR_PARAM, &cachedb_url.s}, { "blacklist_timeout", INT_PARAM, &blacklist_timeout}, {0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_CACHEDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "dns_cache", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function)destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { LM_NOTICE("initializing module dns_cache ...\n"); if (cachedb_url.s == NULL) { LM_ERR("no cachedb_url set !\n"); return -1; } else { cachedb_url.len = strlen(cachedb_url.s); LM_DBG("using CacheDB URL: %s\n", cachedb_url.s); } /* set pointers that resolver will use for caching */ dnscache_fetch_func=get_dnscache_value; dnscache_put_func=put_dnscache_value; return 0; } static int child_init(int rank) { if (cachedb_bind_mod(&cachedb_url, &cdbf) < 0) { LM_ERR("cannot bind functions for db_url %.*s\n", cachedb_url.len, cachedb_url.s); return -1; } if (!CACHEDB_CAPABILITY(&cdbf, CACHEDB_CAP_GET|CACHEDB_CAP_SET)) { LM_ERR("not enough capabilities\n"); return -1; } cdbc = cdbf.init(&cachedb_url); if (!cdbc) { LM_ERR("cannot connect to db_url %.*s\n", cachedb_url.len, cachedb_url.s); return -1; } return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module dns_cache ...\n"); if (cdbc) cdbf.destroy(cdbc); } static int rdata_struct_len=sizeof(struct rdata)-sizeof(void *) - sizeof(struct rdata *); static unsigned char *he_buf=NULL; static int he_buf_len=0; static char* serialize_he_rdata(struct hostent *he,int *buf_len,int do_encoding) { unsigned char *p; int i,len=0,needed_len=0,base64_len=0,alias_no=0,addr_no=0; /* addr_type, name_len, alias_no, addr_no */ len+=sizeof(int)*4; /* compute needed buffer length */ if (he->h_name) len+=strlen(he->h_name)+1; if (he->h_aliases) for (i=0;he->h_aliases[i];i++) { /* integer with len + len bytes of alias */ len+=strlen(he->h_aliases[i])+1+sizeof(int); alias_no++; } i=0; if (he->h_addr_list) for (i=0;he->h_addr_list[i];i++) { len+=he->h_length; addr_no++; } if (do_encoding) { /* backend does not support binary values - allocate continous buffer for encoding */ base64_len = calc_base64_encode_len(len); needed_len=len+base64_len; } else needed_len = len; if (he_buf == NULL || needed_len > he_buf_len) { /* realloc if not enough space */ he_buf = pkg_realloc(he_buf,needed_len); if (he_buf == NULL) { LM_ERR("No more pkg\n"); return NULL; } he_buf_len = needed_len; } p = he_buf; /* copy address type */ memcpy(p,&he->h_addrtype,sizeof(int)); p+=sizeof(int); /* copy h_name len */ len=strlen(he->h_name)+1; memcpy(p,&len,sizeof(int)); p+=sizeof(int); /* copy h_name */ memcpy(p,he->h_name,len); p+=len; /* copy number of aliases */ memcpy(p,&alias_no,sizeof(int)); p+=sizeof(int); /* copy aliases, if any */ if (he->h_aliases) for (i=0;he->h_aliases[i];i++) { len=strlen(he->h_aliases[i])+1; /* copy alias length */ memcpy(p,&len,sizeof(int)); p+=sizeof(int); /* copy alias */ memcpy(p,he->h_aliases[i],len); p+=len; } /* copy address no */ memcpy(p,&addr_no,sizeof(int)); p+=sizeof(int); /* copy addresses */ if (he->h_addr_list) for (i=0;he->h_addr_list[i];i++) { /* copy addreses. length will be known from the addrtype field */ len=he->h_length; memcpy(p,he->h_addr_list[i],len); p+=len; } if (do_encoding) { len = needed_len - base64_len; if (buf_len) *buf_len=base64_len; /* do encoding, and return pointer after unencoded data */ base64encode(p,he_buf,len); return (char *)p; } else { if (buf_len) *buf_len = needed_len; return (char *)he_buf; } } static unsigned char *dec_he_buf=NULL; static int dec_he_buf_len=0; static struct hostent dec_global_he; #define MAXALIASES 36 #define MAXADDRS 36 static char *h_addr_ptrs[MAXADDRS]; static char *host_aliases[MAXALIASES]; static struct hostent* deserialize_he_rdata(char *buff,int buf_len,int do_decoding) { char **ap,**hap; unsigned char *p; int max_len=0; int i,alias_no=0,addr_no=0,len=0; /* max estimation of needed buffer */ if (do_decoding) { max_len=calc_max_base64_decode_len(buf_len); } else { max_len = buf_len; } if (dec_he_buf == NULL || max_len > dec_he_buf_len) { /* realloc buff if not enough space */ dec_he_buf = pkg_realloc(dec_he_buf,max_len); if (dec_he_buf == NULL) { LM_ERR("No more pkg\n"); return NULL; } dec_he_buf_len = max_len; } /* set pointer in dec_global_he */ ap = host_aliases; *ap = NULL; dec_global_he.h_aliases = host_aliases; hap = h_addr_ptrs; *hap = NULL; dec_global_he.h_addr_list = h_addr_ptrs; if (do_decoding) { /* decode base64 buf */ base64decode(dec_he_buf,(unsigned char *)buff,buf_len); p = dec_he_buf; } else { memcpy(dec_he_buf,buff,buf_len); p = dec_he_buf; } /* set address type & length */ memcpy(&dec_global_he.h_addrtype,p,sizeof(int)); p+=sizeof(int); if (dec_global_he.h_addrtype == AF_INET) dec_global_he.h_length=4; else dec_global_he.h_length=16; /* set name */ memcpy(&len,p,sizeof(int)); p+=sizeof(int); dec_global_he.h_name = (char *)p; p+=len; /* get number of aliases */ memcpy(&alias_no,p,sizeof(int)); p+=sizeof(int); for (i=0;i rdata_buf_len) { rdata_buf = pkg_realloc(rdata_buf,needed_len); if (rdata_buf == NULL) { LM_ERR("No more pkg\n"); return NULL; } rdata_buf_len = needed_len; } p = rdata_buf; for (it=head;it;it=it->next) { /* copy non-pointer fields of the struct */ memcpy(p,it,rdata_struct_len); p+=rdata_struct_len; switch (it->type) { case T_A: /* copy all 4 bytes */ memcpy(p,it->rdata,sizeof(struct a_rdata)); p+=sizeof(struct a_rdata); break; case T_AAAA: /* copy all 16 bytes */ memcpy(p,it->rdata,sizeof(struct aaaa_rdata)); p+=sizeof(struct aaaa_rdata); break; case T_CNAME: cname_rd=(struct cname_rdata *)it->rdata; entry_len=strlen(cname_rd->name); /* copy len of alias */ memcpy(p,&entry_len,sizeof(int)); p+=sizeof(int); /* copy alias */ memcpy(p,cname_rd->name,entry_len+1); p+=entry_len+1; break; case T_NAPTR: /* copy priority, etc */ memcpy(p,it->rdata,2*sizeof(unsigned short) + sizeof(unsigned int)); p+=2*sizeof(unsigned short) + sizeof(unsigned int); naptr_rd=it->rdata; /* copy flags, flags_len was copied above */ memcpy(p,naptr_rd->flags,naptr_rd->flags_len+1); p+=naptr_rd->flags_len+1; /* copy services & len */ memcpy(p,&naptr_rd->services_len,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(p,naptr_rd->services,naptr_rd->services_len+1); p+=naptr_rd->services_len+1; /* copy regexp & len */ memcpy(p,&naptr_rd->regexp_len,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(p,naptr_rd->regexp,naptr_rd->regexp_len+1); p+=naptr_rd->regexp_len+1; /* copy repl & len */ memcpy(p,&naptr_rd->repl_len,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(p,naptr_rd->repl,naptr_rd->repl_len+1); p+=naptr_rd->repl_len+1; break; case T_SRV: srv_rd=it->rdata; memcpy(p,srv_rd,4*sizeof(unsigned short) + sizeof(unsigned int)); p+=4*sizeof(unsigned short) + sizeof(unsigned int); memcpy(p,srv_rd->name,srv_rd->name_len+1); p+=srv_rd->name_len+1; break; case T_TXT: txt_rd=it->rdata; entry_len=strlen(txt_rd->txt); memcpy(p,&entry_len,sizeof(int)); p+=sizeof(int); memcpy(p,txt_rd->txt,entry_len+1); p+=entry_len+1; break; case T_EBL: ebl_rd=it->rdata; memcpy(p,ebl_rd,sizeof(unsigned char) + sizeof(unsigned int)); p+=sizeof(unsigned char) + sizeof(unsigned int); memcpy(p,ebl_rd->separator,ebl_rd->separator_len+1); p+=ebl_rd->separator_len+1; memcpy(p,&ebl_rd->apex_len,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(p,ebl_rd->apex,ebl_rd->apex_len+1); p+=ebl_rd->apex_len+1; break; default: LM_ERR("Unexpected DNS record type\n"); return NULL; } } if (do_encoding) { if (*len) *len = base64_len; /* encode and return beggining of encoding */ base64encode(p,rdata_buf,buf_len); return (char *)p; } else { if (*len) *len = needed_len; return (char *)rdata_buf; } } static unsigned char *dec_rdata_buf=NULL; static int dec_rdata_buf_len=0; static struct rdata* deserialize_dns_rdata(char *buff,int buf_len,int do_decoding) { unsigned char *p; int max_len=0,actual_len=0,entry_len=0; struct rdata *head,*it,**last; struct naptr_rdata *naptr_rd; struct srv_rdata *srv_rd; struct txt_rdata *txt_rd; struct ebl_rdata *ebl_rd; head=it=NULL; last=&head; if (do_decoding) { max_len = calc_max_base64_decode_len(buf_len); } else { max_len = buf_len; } if (dec_rdata_buf == NULL || max_len > dec_rdata_buf_len) { /* realloc buff if not enough space */ dec_rdata_buf = pkg_realloc(dec_rdata_buf,max_len); if (dec_rdata_buf == NULL) { LM_ERR("No more pkg\n"); return NULL; } dec_rdata_buf_len = max_len; } if (do_decoding) { /* decode base64 buf */ actual_len = base64decode(dec_rdata_buf,(unsigned char *)buff,buf_len); p = dec_rdata_buf; } else { memcpy(dec_rdata_buf,buff,buf_len); actual_len = buf_len; p = dec_rdata_buf; } while ( p < dec_rdata_buf+actual_len) { it = pkg_malloc(sizeof(struct rdata)); if (it == 0) { LM_ERR("no more pkg mem\n"); goto it_alloc_error; } /* copy type, class & ttl */ memcpy(it,p,rdata_struct_len); p+=rdata_struct_len; it->next=0; it->rdata=0; switch (it->type) { case T_A: it->rdata = pkg_malloc(sizeof(struct a_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } memcpy(p,it->rdata,sizeof(struct a_rdata)); p+=sizeof(struct a_rdata); *last=it; last=&(it->next); break; case T_AAAA: it->rdata = pkg_malloc(sizeof(struct aaaa_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } memcpy(p,it->rdata,sizeof(struct aaaa_rdata)); p+=sizeof(struct aaaa_rdata); *last=it; last=&(it->next); break; case T_CNAME: it->rdata = pkg_malloc(sizeof(struct cname_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } memcpy(&entry_len,p,sizeof(int)); p+=sizeof(int); memcpy(((struct cname_rdata*)it->rdata)->name, p,entry_len+1); p+=entry_len+1; *last=it; last=&(it->next); break; case T_NAPTR: it->rdata = pkg_malloc(sizeof(struct naptr_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } naptr_rd = (struct naptr_rdata*)it->rdata; memcpy(naptr_rd,p,2*sizeof(unsigned short) + sizeof(unsigned int)); p+=2*sizeof(unsigned short) + sizeof(unsigned int); memcpy(naptr_rd->flags,p,naptr_rd->flags_len+1); p+=naptr_rd->flags_len+1; memcpy(&naptr_rd->services_len,p,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(naptr_rd->services,p,naptr_rd->services_len+1); p+=naptr_rd->services_len+1; memcpy(&naptr_rd->regexp_len,p,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(naptr_rd->regexp,p,naptr_rd->regexp_len+1); p+=naptr_rd->regexp_len+1; memcpy(&naptr_rd->repl_len,p,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(naptr_rd->repl,p,naptr_rd->repl_len+1); p+=naptr_rd->repl_len+1; *last=it; last=&(it->next); break; case T_SRV: it->rdata = pkg_malloc(sizeof(struct srv_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } srv_rd = (struct srv_rdata*)it->rdata; memcpy(srv_rd,p,4*sizeof(unsigned short) + sizeof(unsigned int)); p+=4*sizeof(unsigned short) + sizeof(unsigned int); memcpy(srv_rd->name,p,srv_rd->name_len+1); p+=srv_rd->name_len+1; *last=it; last=&(it->next); break; case T_TXT: it->rdata = pkg_malloc(sizeof(struct txt_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } txt_rd = (struct txt_rdata*)it->rdata; memcpy(&entry_len,p,sizeof(int)); p+=sizeof(int); memcpy(txt_rd->txt,p,entry_len+1); p+=entry_len+1; *last=it; last=&(it->next); break; case T_EBL: it->rdata = pkg_malloc(sizeof(struct ebl_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } ebl_rd = (struct ebl_rdata*)it->rdata; memcpy(ebl_rd,p,sizeof(unsigned char) + sizeof(unsigned int)); p+=sizeof(unsigned char)+sizeof(unsigned int); memcpy(ebl_rd->separator,p,ebl_rd->separator_len+1); p+=ebl_rd->separator_len+1; memcpy(&ebl_rd->apex_len,p,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(ebl_rd->apex,p,ebl_rd->apex_len+1); p+=ebl_rd->apex_len+1; *last=it; last=&(it->next); break; } } return head; rdata_alloc_error: if (it) pkg_free(it); it_alloc_error: if (head) free_rdata_list(head); return NULL; } static char keyname_buff[300]; /* TODO - size ?*/ char* create_keyname_for_record(char *name,int r_type,int name_len,int *res_len) { char *p; int n,x; p=keyname_buff; *res_len = 0; memcpy(p,"dnscache_",9); *res_len += 9; p+=9; if (r_type != T_PTR) { /* query is plain text , go ahead and copy it */ n=strlen(name); memcpy(p,name,n); *res_len += n; p+=n; } else { /* binary key, convert to str */ inet_ntop(name_len==4?AF_INET:AF_INET6,name,p,name_len==4? INET_ADDRSTRLEN:INET6_ADDRSTRLEN); x=strlen(p); *res_len += x; p+=x; } switch (r_type) { case T_SRV: memcpy(p,"_srv",4); *res_len += 4; break; case T_A: memcpy(p,"_a",2); *res_len += 2; break; case T_AAAA: memcpy(p,"_aaaa",5); *res_len += 5; break; case T_CNAME: memcpy(p,"_cname",6); *res_len += 6; break; case T_NAPTR: memcpy(p,"_naptr",6); *res_len += 6; break; case T_TXT: memcpy(p,"_txt",4); *res_len += 4; break; case T_EBL: memcpy(p,"_ebl",4); *res_len += 4; break; case T_PTR: memcpy(p,"_ptr",4); *res_len +=4; p+=4; /* one can request PTR for IP and IPv6 */ x = name_len==4?2:5; memcpy(p,name_len==4?"_a":"_aaaa",x); *res_len +=x; break; default: LM_ERR("invalid r_type %d\n",r_type); return NULL; } return keyname_buff; } /* gets value from cache for the corresponding entry * Params : * name - what is wished to be resolved - binary IP for PTR and strings for other queries * r_type - type of DNS query * name_len - only used in case of PTR */ int get_dnscache_strvalue(char *name,int r_type,int name_len,str *res) { str key; /* generate key */ key.s=create_keyname_for_record(name,r_type,name_len,&key.len); if (key.s == NULL) { LM_ERR("failed to create key\n"); return -1; } LM_DBG("gen key [%.*s]\n",key.len,key.s); /* fetch from backend */ if (cdbf.get(cdbc, &key, res) < 0) { LM_DBG("cannot retrieve key\n"); return -1; } return 0; } #define FAILURE_MARKER_CHAR '|' #define FAILURE_MARKER "|" #define FAILURE_MARKER_LEN 1 /* Returns hostent or rdata struct, based on what callers needs */ void* get_dnscache_value(char *name,int r_type,int name_len) { str value; struct hostent *he; struct rdata *head; if (cdbc == NULL) { /* assume dns request before forking - cache is not ready yet */ return NULL; } if (get_dnscache_strvalue(name,r_type,name_len,&value) < 0) { LM_DBG("failed to fetch from cache\n"); return NULL; } if (value.len == FAILURE_MARKER_LEN && value.s[0] == FAILURE_MARKER_CHAR) { LM_DBG("blacklisted value %s for type %d\n",name,r_type); pkg_free(value.s); return (void *)-1; } if (r_type == T_A || r_type == T_AAAA || r_type == T_PTR) { he = deserialize_he_rdata(value.s,value.len, CACHEDB_CAPABILITY(&cdbf,CACHEDB_CAP_BINARY_VALUE)?0:1); if (he == NULL) { LM_ERR("failed to deserialize he struct\n"); pkg_free(value.s); return NULL; } pkg_free(value.s); return he; } else { head = deserialize_dns_rdata(value.s,value.len, CACHEDB_CAPABILITY(&cdbf,CACHEDB_CAP_BINARY_VALUE)?0:1); if (head == NULL) { LM_ERR("failed to deserialize rdata struct\n"); pkg_free(value.s); return NULL; } pkg_free(value.s); return head; } } /* Pushes internal structure ( hostent or rdata ) to a cache backend * Params : * name - what query to be saved - binary IP for PTR and strings for other queries * r_type - type of DNS query * record - pointer to hostent or rdata * rdata_len - If rdata record, rdata_len holds the actual length of rdata buf, in order to avoid double iterations on the rdata struct. If it's a PTR record, rdata_len is used to differentiate between IP and IPv6 * failure - should we blacklist or not * ttl - seconds the key should be kept in cache */ int put_dnscache_value(char *name,int r_type,void *record,int rdata_len, int failure,int ttl) { str key,value; int key_ttl; if (cdbc == NULL) { /* assume dns request before forking - cache is not ready yet */ return -1; } /* generate key */ key.s=create_keyname_for_record(name,r_type,rdata_len,&key.len); if (key.s == NULL) { LM_ERR("failed to create key\n"); return -1; } if (failure) { /* just set value as failure marker, and push to back-end * with the default timeout */ value.s = FAILURE_MARKER; value.len= FAILURE_MARKER_LEN; key_ttl = blacklist_timeout; } else { if (r_type == T_A || r_type == T_AAAA || r_type == T_PTR) { value.s = serialize_he_rdata((struct hostent *)record, &value.len,CACHEDB_CAPABILITY(&cdbf,CACHEDB_CAP_BINARY_VALUE)?0:1); if (value.s == NULL) { LM_ERR("failed to serialize he rdata\n"); return -1; } } else { value.s = serialize_dns_rdata((struct rdata *)record, rdata_len,&value.len,CACHEDB_CAPABILITY(&cdbf,CACHEDB_CAP_BINARY_VALUE)?0:1); if (value.s == NULL) { LM_ERR("failed to serialize rdata record\n"); return -1; } } key_ttl = ttl; } LM_DBG("putting value [%.*s] with ttl = %d\n",key.len,key.s,key_ttl); if (cdbf.set(cdbc,&key,&value,key_ttl) < 0) { LM_ERR("failed to set dns key\n"); return -1; } return 0; } opensips-2.2.2/modules/dns_cache/doc/000077500000000000000000000000001300170765700175075ustar00rootroot00000000000000opensips-2.2.2/modules/dns_cache/doc/dns_cache.xml000066400000000000000000000016331300170765700221430ustar00rootroot00000000000000 %docentities; ]> dns_cache Module &osipsname; Vladut-Stefan Paiu OpenSIPS Solutions
vladpaiu@opensips.org
Vladut-Stefan Paiu
vladpaiu@opensips.org
2012 &osipssol;
&admin;
opensips-2.2.2/modules/dns_cache/doc/dns_cache_admin.xml000066400000000000000000000037151300170765700233160ustar00rootroot00000000000000 &adminguide;
Overview This module is an implementation of a cache system designed for DNS records. For succesfull DNS queries of all types, the module will store in a cache/db backend the mappings, for TTL number of seconds received in the DNS answer. Failed DNS queries will also be stored in the back-end, with a TTL that can be specified by the user. The module uses the Key-Value interface exported from the core.
Dependencies
&osips; Modules A cachedb_* type module must be loaded before loading the dns_cache module.
Exported Parameters
<varname>cachedb_url</varname> (string) The url of the key-value back-end that will be used for storing the DNS records. Set <varname>cachedb_url</varname> parameter ... #use internal cachedb_local module modparam("dns_cache", "cachedb_url","local://") #use cachedb_memcached module with memcached server at 192.168.2.130 modparam("dns_cache", "cachedb_url","memcached://192.168.2.130:8888/") ...
<varname>blacklist_timeout</varname> (int) The number of seconds that a failed DNS query will be kept in cache. Default is 3600. Set <varname>blacklist_timeout</varname> parameter ... modparam("dns_cache", "blacklist_timeout",7200) # 2 hours ...
Exported Functions The module does not export functions to be used in configuration script.
opensips-2.2.2/modules/domain/000077500000000000000000000000001300170765700163025ustar00rootroot00000000000000opensips-2.2.2/modules/domain/Makefile000066400000000000000000000003451300170765700177440ustar00rootroot00000000000000# Makefile v 1.0 2002/12/27 # # Domain module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=domain.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/domain/README000066400000000000000000000164541300170765700171740ustar00rootroot00000000000000Domain Module Juha Heinanen Edited by Juha Heinanen Copyright © 2002-2008 Juha Heinanen Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. db_mode (integer) 1.3.3. domain_table (string) 1.3.4. domain_col (string) 1.3.5. attrs_col (string) 1.4. Exported Functions 1.4.1. is_from_local([attrs_pvar]) 1.4.2. is_uri_host_local([attrs_pvar]) 1.4.3. is_domain_local(pseudo_variable [, attrs_pvar]) 1.5. Exported MI Functions 1.5.1. domain_reload 1.5.2. domain_dump 1.6. Known Limitations 2. Developer Guide 2.1. Available Functions 2.1.1. is_domain_local(domain) List of Examples 1.1. Setting db_url parameter 1.2. db_mode example 1.3. Setting domain_table parameter 1.4. Setting domain_col parameter 1.5. Setting attrs_col parameter 1.6. is_from_local usage 1.7. is_uri_host_local usage 1.8. is_domain_local usage Chapter 1. Admin Guide 1.1. Overview Domain module implements checks that based on domain table determine if a host part of an URI is “local†or not. A “local†domain is one that the proxy is responsible for. Domain module operates in caching or non-caching mode depending on value of module parameter db_mode. In caching mode domain module reads the contents of domain table into cache memory when the module is loaded. After that domain table is re-read only when module is given domain_reload fifo command. Any changes in domain table must thus be followed by “domain_reload†command in order to reflect them in module behavior. In non-caching mode domain module always queries domain table in the database. Caching is implemented using a hash table. The size of the hash table is given by HASH_SIZE constant defined in domain_mod.h. Its “factory default†value is 128. 1.2. Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): * database -- Any database module 1.3. Exported Parameters 1.3.1. db_url (string) This is URL of the database to be used. Default value is “mysql://opensipsro:opensipsro@localhost/opensips†Example 1.1. Setting db_url parameter modparam("domain", "db_url", "mysql://ser:pass@db_host/ser") 1.3.2. db_mode (integer) Database mode: 0 means non-caching, 1 means caching. Default value is 0 (non-caching). Example 1.2. db_mode example modparam("domain", "db_mode", 1) # Use caching 1.3.3. domain_table (string) Name of table containing names of local domains that the proxy is responsible for. Local users must have in their sip uri a host part that is equal to one of these domains. Default value is “domainâ€. Example 1.3. Setting domain_table parameter modparam("domain", "domain_table", "new_name") 1.3.4. domain_col (string) Name of column containing domains in domain table. Default value is “domainâ€. Example 1.4. Setting domain_col parameter modparam("domain", "domain_col", "domain_name") 1.3.5. attrs_col (string) Name of column containing attributes in domain table. Default value is “attrsâ€. Example 1.5. Setting attrs_col parameter modparam("domain", "attrs_col", "attributes") 1.4. Exported Functions 1.4.1. is_from_local([attrs_pvar]) Checks based on domain table if host part of From header uri is one of the local domains that the proxy is responsible for. The argument is optional and if present it should contain a writable pseudo variable that will be populated with the attributes from the database. This function can be used from REQUEST_ROUTE. Example 1.6. is_from_local usage ... if (is_from_local()) { ... }; ... if (is_from_local("$var(attrs)")) { xlog("Domain attributes are $var(attrs)\n"); ... }; ... 1.4.2. is_uri_host_local([attrs_pvar]) If called from route or failure route block, checks based on domain table if host part of Request-URI is one of the local domains that the proxy is responsible for. If called from branch route, the test is made on host part of URI of first branch, which thus must have been appended to the transaction before is_uri_host_local() is called. The argument is optional and if present it should contain a writable pseudo variable that will be populated with the attributes from the database. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.7. is_uri_host_local usage ... if (is_uri_host_local()) { ... }; ... if (is_uri_host_local("$var(attrs)")) { xlog("Domain attributes are $var(attrs)\n"); ... }; 1.4.3. is_domain_local(pseudo_variable [, attrs_pvar]) This function checks if the domain contained in the pseudo_variable is local. This function is a generalized form of the is_from_local() and is_uri_host_local() functions, being able to completely replace them and also extends them by allowing the domain to be taken from any of the above mentioned sources. The following equivalences exist: * is_domain_local("$rd") is same as is_uri_host_local() * is_domain_local("$fd") is same as is_from_local() The second argument is optional and if present it should contain a writable pseudo variable that will be populated with the attributes from the database. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.8. is_domain_local usage ... if (is_domain_local("$rd")) { ... }; if (is_domain_local("$fd")) { ... }; if (is_domain_local("$avp(some_avp_alias)")) { ... }; if (is_domain_local("$avp(850)")) { ... }; if (is_domain_local("$avp(some_avp)")) { ... }; if (is_domain_local("$avp(some_avp)", "$avp(attrs)")) { xlog("Domain attributes are $avp(attrs)\n"); ... }; ... 1.5. Exported MI Functions 1.5.1. domain_reload Causes domain module to re-read the contents of domain table into cache memory. Name: domain_reload Parameters: none MI FIFO Command Format: :domain_reload:_reply_fifo_file_ _empty_line_ 1.5.2. domain_dump Causes domain module to dump hash indexes and domain names in its cache memory. Name: domain_dump Parameters: none MI FIFO Command Format: :domain_dump:_reply_fifo_file_ _empty_line_ 1.6. Known Limitations There is an unlikely race condition on domain list update. If a process uses a table, which is reloaded at the same time twice through FIFO, the second reload will delete the original table still in use by the process. Chapter 2. Developer Guide The module provides is_domain_local API function for use by other OpenSIPS modules. 2.1. Available Functions 2.1.1. is_domain_local(domain) Checks if domain given in str* parameter is local. The function returns 1 if domain is local and -1 if domain is not local or if an error occurred. opensips-2.2.2/modules/domain/api.c000066400000000000000000000020021300170765700172110ustar00rootroot00000000000000/* * Copyright (C) 2008 Juha Heinanen * * This file is part of OpenSIPS, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "api.h" #include "domain.h" int bind_domain(domain_api_t* api) { if (!api) { LM_ERR("invalid parameter value\n"); return -1; } api->is_domain_local = is_domain_local; return 0; } opensips-2.2.2/modules/domain/api.h000066400000000000000000000021511300170765700172230ustar00rootroot00000000000000/* * Copyright (C) 2008 Juha Heinanen * * This file is part of OpenSIPS, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef DOMAIN_API_H_ #define DOMAIN_API_H_ #include "../../str.h" #include "domain.h" typedef int (*is_domain_local_t)(str* _domain); typedef struct domain_api { is_domain_local_t is_domain_local; } domain_api_t; typedef int (*bind_domain_t)(domain_api_t* api); int bind_domain(domain_api_t* api); #endif opensips-2.2.2/modules/domain/doc/000077500000000000000000000000001300170765700170475ustar00rootroot00000000000000opensips-2.2.2/modules/domain/doc/domain.xml000066400000000000000000000020261300170765700210400ustar00rootroot00000000000000 %docentities; ]> Domain Module Juha Heinanen jh@tutpro.com Juha Heinanen jh@tutpro.com 2002-2008 Juha Heinanen $Revision: 5901 $ $Date$ &admin; &devel; &faq; opensips-2.2.2/modules/domain/doc/domain_admin.xml000066400000000000000000000201551300170765700222130ustar00rootroot00000000000000 &adminguide;
Overview Domain module implements checks that based on domain table determine if a host part of an URI is local or not. A local domain is one that the proxy is responsible for. Domain module operates in caching or non-caching mode depending on value of module parameter db_mode. In caching mode domain module reads the contents of domain table into cache memory when the module is loaded. After that domain table is re-read only when module is given domain_reload fifo command. Any changes in domain table must thus be followed by domain_reload command in order to reflect them in module behavior. In non-caching mode domain module always queries domain table in the database. Caching is implemented using a hash table. The size of the hash table is given by HASH_SIZE constant defined in domain_mod.h. Its factory default value is 128.
Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): database -- Any database module
Exported Parameters
<varname>db_url</varname> (string) This is URL of the database to be used. Default value is mysql://opensipsro:opensipsro@localhost/opensips Setting db_url parameter modparam("domain", "db_url", "mysql://ser:pass@db_host/ser")
<varname>db_mode</varname> (integer) Database mode: 0 means non-caching, 1 means caching. Default value is 0 (non-caching). db_mode example modparam("domain", "db_mode", 1) # Use caching
<varname>domain_table</varname> (string) Name of table containing names of local domains that the proxy is responsible for. Local users must have in their sip uri a host part that is equal to one of these domains. Default value is domain. Setting domain_table parameter modparam("domain", "domain_table", "new_name")
<varname>domain_col</varname> (string) Name of column containing domains in domain table. Default value is domain. Setting domain_col parameter modparam("domain", "domain_col", "domain_name")
<varname>attrs_col</varname> (string) Name of column containing attributes in domain table. Default value is attrs. Setting attrs_col parameter modparam("domain", "attrs_col", "attributes")
Exported Functions
<function moreinfo="none">is_from_local([attrs_pvar])</function> Checks based on domain table if host part of From header uri is one of the local domains that the proxy is responsible for. The argument is optional and if present it should contain a writable pseudo variable that will be populated with the attributes from the database. This function can be used from REQUEST_ROUTE. is_from_local usage ... if (is_from_local()) { ... }; ... if (is_from_local("$var(attrs)")) { xlog("Domain attributes are $var(attrs)\n"); ... }; ...
<function moreinfo="none">is_uri_host_local([attrs_pvar])</function> If called from route or failure route block, checks based on domain table if host part of Request-URI is one of the local domains that the proxy is responsible for. If called from branch route, the test is made on host part of URI of first branch, which thus must have been appended to the transaction before is_uri_host_local() is called. The argument is optional and if present it should contain a writable pseudo variable that will be populated with the attributes from the database. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. is_uri_host_local usage ... if (is_uri_host_local()) { ... }; ... if (is_uri_host_local("$var(attrs)")) { xlog("Domain attributes are $var(attrs)\n"); ... };
<function moreinfo="none">is_domain_local(pseudo_variable [, attrs_pvar])</function> This function checks if the domain contained in the pseudo_variable is local. This function is a generalized form of the is_from_local() and is_uri_host_local() functions, being able to completely replace them and also extends them by allowing the domain to be taken from any of the above mentioned sources. The following equivalences exist: is_domain_local("$rd") is same as is_uri_host_local() is_domain_local("$fd") is same as is_from_local() The second argument is optional and if present it should contain a writable pseudo variable that will be populated with the attributes from the database. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. is_domain_local usage ... if (is_domain_local("$rd")) { ... }; if (is_domain_local("$fd")) { ... }; if (is_domain_local("$avp(some_avp_alias)")) { ... }; if (is_domain_local("$avp(850)")) { ... }; if (is_domain_local("$avp(some_avp)")) { ... }; if (is_domain_local("$avp(some_avp)", "$avp(attrs)")) { xlog("Domain attributes are $avp(attrs)\n"); ... }; ...
Exported MI Functions
<function moreinfo="none">domain_reload</function> Causes domain module to re-read the contents of domain table into cache memory. Name: domain_reload Parameters: none MI FIFO Command Format: :domain_reload:_reply_fifo_file_ _empty_line_
<function moreinfo="none">domain_dump</function> Causes domain module to dump hash indexes and domain names in its cache memory. Name: domain_dump Parameters: none MI FIFO Command Format: :domain_dump:_reply_fifo_file_ _empty_line_
Known Limitations There is an unlikely race condition on domain list update. If a process uses a table, which is reloaded at the same time twice through FIFO, the second reload will delete the original table still in use by the process.
opensips-2.2.2/modules/domain/doc/domain_devel.xml000066400000000000000000000011231300170765700222140ustar00rootroot00000000000000 &develguide; The module provides is_domain_local API function for use by other &osips; modules.
Available Functions
<function moreinfo="none">is_domain_local(domain)</function> Checks if domain given in str* parameter is local. The function returns 1 if domain is local and -1 if domain is not local or if an error occurred.
opensips-2.2.2/modules/domain/domain.c000066400000000000000000000162631300170765700177250ustar00rootroot00000000000000/* * Domain table related functions * * Copyright (C) 2002-2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-06-07 updated to the new DB api, moved reload_table here, created * domain_db_{init.bind,ver,close} (andrei) * 2004-09-06 is_uri_host_local() can now be called also from * failure route (juhe) */ #include "domain_mod.h" #include "hash.h" #include "../../db/db.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../ut.h" #include "../../dset.h" #include "../../route.h" #include "../../pvar.h" #include "../../str.h" #define DOMAIN_TABLE_VERSION 3 static db_con_t* db_handle=0; static db_func_t domain_dbf; /* helper db functions*/ int domain_db_bind(const str* db_url) { if (db_bind_mod(db_url, &domain_dbf )) { LM_ERR("Cannot bind to database module!"); return -1; } return 0; } int domain_db_init(const str* db_url) { if (domain_dbf.init==0){ LM_ERR("Unbound database module\n"); goto error; } db_handle=domain_dbf.init(db_url); if (db_handle==0){ LM_ERR("Cannot initialize database connection\n"); goto error; } return 0; error: return -1; } void domain_db_close(void) { if (db_handle && domain_dbf.close){ domain_dbf.close(db_handle); db_handle=0; } } int domain_db_ver(str* name, int version) { if (db_handle==0){ LM_ERR("null database handler\n"); return -1; } return db_check_table_version(&domain_dbf, db_handle, name, version); } /* * Check if domain is local and store attributes in a pvar */ int is_domain_local_pvar(struct sip_msg *msg, str* _host, char *pvar) { pv_spec_t *pv = (pv_spec_t *)pvar; pv_value_t val; db_val_t *values; if (db_mode == 0) { db_key_t keys[1]; db_val_t vals[1]; db_key_t cols[2]; db_res_t* res = NULL; keys[0] = &domain_col; cols[0] = &domain_col; cols[1] = &domain_attrs_col; if (domain_dbf.use_table(db_handle, &domain_table) < 0) { LM_ERR("Error while trying to use domain table\n"); return -3; } VAL_TYPE(vals) = DB_STR; VAL_NULL(vals) = 0; VAL_STR(vals).s = _host->s; VAL_STR(vals).len = _host->len; if (domain_dbf.query(db_handle, keys, 0, vals, cols, 1, 2, 0, &res) < 0 ) { LM_ERR("Error while querying database\n"); return -3; } if (RES_ROW_N(res) == 0) { LM_DBG("Realm '%.*s' is not local\n", _host->len, ZSW(_host->s)); domain_dbf.free_result(db_handle, res); return -1; } else { LM_DBG("Realm '%.*s' is local\n", _host->len, ZSW(_host->s)); if (pvar) { /* XXX: what shall we do if there are duplicate entries? */ /* we only check the first row - razvanc */ values = ROW_VALUES(RES_ROWS(res)); if (!VAL_NULL(values +1)) { if (VAL_TYPE(values + 1) == DB_STR) { val.rs = VAL_STR(values + 1); } else { val.rs.s = (char *)VAL_STRING(values + 1); val.rs.len = strlen(val.rs.s); } val.flags = PV_VAL_STR; if (pv_set_value(msg, pv, 0, &val) != 0) LM_ERR("Cannot set attributes value\n"); } } domain_dbf.free_result(db_handle, res); return 1; } } else { return hash_table_lookup (msg, _host, pv); } } /* * Check if domain is local */ int is_domain_local(str* _host) { return is_domain_local_pvar(NULL, _host, NULL); } /* * Check if host in From uri is local */ int is_from_local(struct sip_msg* _msg, char* _s1, char* _s2) { struct sip_uri *puri; if ((puri=parse_from_uri(_msg))==NULL) { LM_ERR("Error while parsing From header\n"); return -2; } return is_domain_local_pvar(_msg, &(puri->host), _s1); } /* * Check if host in Request URI is local */ int is_uri_host_local(struct sip_msg* _msg, char* _s1, char* _s2) { if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("Error while parsing R-URI\n"); return -1; } return is_domain_local_pvar(_msg, &(_msg->parsed_uri.host), _s1); } /* * Check if domain given as value of pseudo variable parameter is local */ int w_is_domain_local(struct sip_msg* _msg, char* _sp, char* _s2) { pv_spec_t *sp; pv_value_t pv_val; sp = (pv_spec_t *)_sp; if (sp && (pv_get_spec_value(_msg, sp, &pv_val) == 0)) { if (pv_val.flags & PV_VAL_STR) { if (pv_val.rs.len == 0 || pv_val.rs.s == NULL) { LM_DBG("Missing domain name\n"); return -1; } return is_domain_local_pvar(_msg, &(pv_val.rs), _s2); } else { LM_DBG("Pseudo variable value is not string\n"); return -1; } } else { LM_DBG("Cannot get pseudo variable value\n"); return -1; } } /* * Reload domain table to new hash table and when done, make new hash table * current one. */ int reload_domain_table ( void ) { db_key_t cols[2]; db_res_t* res = NULL; db_row_t* row; db_val_t* val; struct domain_list **new_hash_table; int i; str domain, attrs; cols[0] = &domain_col; cols[1] = &domain_attrs_col; if (domain_dbf.use_table(db_handle, &domain_table) < 0) { LM_ERR("Error while trying to use domain table\n"); return -3; } if (domain_dbf.query(db_handle, NULL, 0, NULL, cols, 0, 2, 0, &res) < 0) { LM_ERR("Error while querying database\n"); return -3; } /* Choose new hash table and free its old contents */ if (*hash_table == hash_table_1) { hash_table_free(hash_table_2); new_hash_table = hash_table_2; } else { hash_table_free(hash_table_1); new_hash_table = hash_table_1; } row = RES_ROWS(res); LM_DBG("Number of rows in domain table: %d\n", RES_ROW_N(res)); for (i = 0; i < RES_ROW_N(res); i++) { val = ROW_VALUES(row + i); if (VAL_TYPE(val) == DB_STRING) { domain.s = (char *)VAL_STRING(val); domain.len = strlen(domain.s); } else if (VAL_TYPE(val) == DB_STR) { domain = VAL_STR(val); } else { LM_ERR("Database problem on domain column\n"); domain_dbf.free_result(db_handle, res); return -3; } if (VAL_NULL(val + 1)) { /* add a marker to determine whether the attributes exist or not */ attrs.len = 0; attrs.s = NULL; } else if (VAL_TYPE(val + 1) == DB_STRING) { attrs.s = (char *)VAL_STRING(val + 1); attrs.len = strlen(attrs.s); } else if (VAL_TYPE(val + 1) == DB_STR) { attrs = VAL_STR(val + 1); } else { LM_ERR("Database problem on attrs column\n"); domain_dbf.free_result(db_handle, res); return -3; } LM_DBG("Value: %s inserted into domain hash table\n",VAL_STRING(val)); if (hash_table_install(new_hash_table, &domain, &attrs)==-1){ LM_ERR("Hash table problem\n"); domain_dbf.free_result(db_handle, res); return -3; } } domain_dbf.free_result(db_handle, res); *hash_table = new_hash_table; return 1; } opensips-2.2.2/modules/domain/domain.h000066400000000000000000000034201300170765700177210ustar00rootroot00000000000000/* domain.h v 0.1 2002/12/27 * Header file for domain table relates functions * * Copyright (C) 2002-2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DOMAIN_H #define DOMAIN_H #include "../../parser/msg_parser.h" /* * Check if host in From uri is local */ int is_from_local(struct sip_msg* _msg, char* _s1, char* _s2); /* * Check if host in Request URI is local */ int is_uri_host_local(struct sip_msg* _msg, char* _s1, char* _s2); /* * Check if domain given by parameter is local * * parameter can be one of: * - $ruri - check domain from request uri * - $from - check domain from From header * - avp name or alias - check the domain given by the value * pointed by the avp name/alias */ int w_is_domain_local(struct sip_msg* _msg, char* _s1, char* _s2); int is_domain_local(str* domain); int domain_db_bind(const str* db_url); int domain_db_init(const str* db_url); void domain_db_close(); int domain_db_ver(str* name, int version); int reload_domain_table(); #endif /* DOMAIN_H */ opensips-2.2.2/modules/domain/domain_mod.c000066400000000000000000000210531300170765700205550ustar00rootroot00000000000000/* * Domain module * * Copyright (C) 2002-2008 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) * 2003-04-05: default_uri #define used (jiri) * 2003-04-06: db connection closed in mod_init (janakj) * 2004-06-06: updated to the new DB api, cleanup: static dbf & handler, * calls to domain_db_{bind,init,close,ver} (andrei) * 2006-01-22: added is_domain_local(variable) function (dan) * */ #include #include "../../sr_module.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../pvar.h" #include "../../mod_fix.h" #include "../../name_alias.h" #include "domain_mod.h" #include "domain.h" #include "mi.h" #include "hash.h" #include "api.h" /* * Module management function prototypes */ static int mod_init(void); static void destroy(void); static int child_init(int rank); static int mi_child_init(void); /* * Version of domain table required by the module, * increment this value if you change the table in * an backwards incompatible way */ #define TABLE_VERSION 3 #define DOMAIN_TABLE "domain" #define DOMAIN_TABLE_LEN (sizeof(DOMAIN_TABLE) - 1) #define DOMAIN_COL "domain" #define DOMAIN_COL_LEN (sizeof(DOMAIN_COL) - 1) #define DOMAIN_ATTRS_COL "attrs" #define DOMAIN_ATTRS_COL_LEN (sizeof(DOMAIN_ATTRS_COL) - 1) /* * Module parameter variables */ static str db_url = {NULL, 0}; int db_mode = 0; /* Database usage mode: 0 = no cache, 1 = cache */ str domain_table = {DOMAIN_TABLE, DOMAIN_TABLE_LEN}; /* Name of domain table */ str domain_col = {DOMAIN_COL, DOMAIN_COL_LEN}; /* Name of domain column */ str domain_attrs_col = {DOMAIN_ATTRS_COL, DOMAIN_ATTRS_COL_LEN}; /* Name of attributes column */ /* * Other module variables */ struct domain_list ***hash_table = 0; /* Pointer to current hash table pointer */ struct domain_list **hash_table_1 = 0; /* Pointer to hash table 1 */ struct domain_list **hash_table_2 = 0; /* Pointer to hash table 2 */ static int is_domain_alias(char* name, int len, unsigned short port, unsigned short proto); static int fixup_wpvar_null(void **param, int param_no); static int fixup_pvar_wpvar(void **param, int param_no); /* * Exported functions */ static cmd_export_t cmds[] = { {"is_from_local", (cmd_function)is_from_local, 0, 0, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"is_from_local", (cmd_function)is_from_local, 1, fixup_wpvar_null, fixup_free_pvar_null, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE| LOCAL_ROUTE}, {"is_uri_host_local", (cmd_function)is_uri_host_local, 0, 0, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"is_uri_host_local", (cmd_function)is_uri_host_local, 1, fixup_wpvar_null, fixup_free_pvar_null, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE| LOCAL_ROUTE}, {"is_domain_local", (cmd_function)w_is_domain_local, 1, fixup_pvar_null, fixup_free_pvar_null, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"is_domain_local", (cmd_function)w_is_domain_local, 2, fixup_pvar_wpvar, fixup_free_pvar_pvar, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"bind_domain", (cmd_function)bind_domain, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &db_url.s }, {"db_mode", INT_PARAM, &db_mode }, {"domain_table", STR_PARAM, &domain_table.s}, {"domain_col", STR_PARAM, &domain_col.s }, {"attrs_col", STR_PARAM, &domain_attrs_col.s }, {0, 0, 0} }; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { { MI_DOMAIN_RELOAD, 0, mi_domain_reload, MI_NO_INPUT_FLAG, 0, mi_child_init }, { MI_DOMAIN_DUMP, 0, mi_domain_dump, MI_NO_INPUT_FLAG, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; /* * Module interface */ struct module_exports exports = { "domain", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ destroy, /* destroy function */ child_init /* per-child init function */ }; static int fixup_wpvar(void **param) { int ret; pv_spec_t *spec; ret = fixup_pvar(param); if (ret != 0) { LM_ERR("cannot parse pvar\n"); return -1; } spec = *(pv_spec_t **)param; if (!spec) { LM_BUG("cannot find spec"); return -1; } if (!spec->setf) { LM_ERR("pvar not writable\n"); return -1; } return 0; } static int fixup_wpvar_null(void **param, int param_no) { if(param_no != 1) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_wpvar(param); } static int fixup_pvar_wpvar(void **param, int param_no) { if (param_no == 1) { return fixup_pvar(param); } if (param_no != 2) { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } return fixup_wpvar(param); } static int mod_init(void) { int i; LM_DBG("Initializing\n"); init_db_url( db_url , 0 /*cannot be null*/); domain_table.len = strlen(domain_table.s); domain_col.len = strlen(domain_col.s); domain_attrs_col.len = strlen(domain_attrs_col.s); /* Check if database module has been loaded */ if (domain_db_bind(&db_url) < 0) return -1; /* Check if cache needs to be loaded from domain table */ if (db_mode != 0) { if (domain_db_init(&db_url)<0) return -1; /* Check table version */ if (domain_db_ver(&domain_table, TABLE_VERSION) < 0) { LM_ERR("error during check of domain table version\n"); goto error; } /* Initializing hash tables and hash table variable */ hash_table_1 = (struct domain_list **)shm_malloc (sizeof(struct domain_list *) * DOM_HASH_SIZE); if (hash_table_1 == 0) { LM_ERR("No memory for hash table\n"); goto error; } hash_table_2 = (struct domain_list **)shm_malloc (sizeof(struct domain_list *) * DOM_HASH_SIZE); if (hash_table_2 == 0) { LM_ERR("No memory for hash table\n"); goto error; } for (i = 0; i < DOM_HASH_SIZE; i++) { hash_table_1[i] = hash_table_2[i] = (struct domain_list *)0; } hash_table = (struct domain_list ***)shm_malloc (sizeof(struct domain_list *)); *hash_table = hash_table_1; if (reload_domain_table() == -1) { LM_ERR("Domain table reload failed\n"); goto error; } domain_db_close(); } /* register the alias check function to core */ if (register_alias_fct(is_domain_alias)!=0) { LM_ERR("failed to register the alias check function\n"); goto error; } return 0; error: domain_db_close(); return -1; } static int child_init(int rank) { /* Check if database is needed by worker processes only */ if ( db_mode==0 && (rank>PROC_MAIN) ) { if (domain_db_init(&db_url)<0) { LM_ERR("Unable to connect to the database\n"); return -1; } } return 0; } static int mi_child_init(void) { return domain_db_init(&db_url); } static void destroy(void) { /* Destroy is called from the main process only, * there is no need to close database here because * it is closed in mod_init already */ if (hash_table) { shm_free(hash_table); hash_table = 0; } if (hash_table_1) { hash_table_free(hash_table_1); shm_free(hash_table_1); hash_table_1 = 0; } if (hash_table_2) { hash_table_free(hash_table_2); shm_free(hash_table_2); hash_table_2 = 0; } } static int is_domain_alias(char* name, int len, unsigned short port, unsigned short proto) { str domain; domain.s = name; domain.len = len; if (is_domain_local(&domain)==1) { return 1; } return 0; } opensips-2.2.2/modules/domain/domain_mod.h000066400000000000000000000040731300170765700205650ustar00rootroot00000000000000/* * Domain module headers * * Copyright (C) 2002-2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DOMAIN_MOD_H #define DOMAIN_MOD_H #include "../../db/db.h" #include "../../str.h" #include "../../usr_avp.h" /* * Constants */ #define DOM_HASH_SIZE 128 /* flags for param source for is_domain_local() */ #define PARAM_SOURCE_NONE (0) #define PARAM_SOURCE_AVP (1<<0) #define PARAM_SOURCE_RURI (1<<1) #define PARAM_SOURCE_FROM (1<<2) /* * Type definitions */ struct domain_list { str domain; str attrs; struct domain_list *next; }; typedef struct param_source { int source; /* One of PARAM_SOURCE_XXX from above */ int avp_type; /* If source is an avp, the avp type else 0 */ int_str avp_name; /* If source is an avp, the avp name else NULL */ } param_source; /* * Module parameters variables */ extern int db_mode; /* Database usage mode: 0 = no cache, 1 = cache */ extern str domain_table; /* Domain table name */ extern str domain_col; /* Domain column name */ extern str domain_attrs_col; /* Domain attributes column name */ /* * Other module variables */ extern struct domain_list **hash_table_1; /* Hash table for domains */ extern struct domain_list **hash_table_2; /* Hash table for domains */ extern struct domain_list ***hash_table; /* Current hash table */ #endif /* DOMAIN_MOD_H */ opensips-2.2.2/modules/domain/hash.c000066400000000000000000000064401300170765700173750ustar00rootroot00000000000000/* * Hash functions for cached domain table * * Copyright (C) 2002-2008 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../dprint.h" #include "../../ut.h" #include "../../hash_func.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "domain_mod.h" #include #include #include #include #define dom_hash(_s) core_case_hash( _s, 0, DOM_HASH_SIZE) /* Add domain to hash table */ int hash_table_install (struct domain_list **hash_table, str *d, str *a) { struct domain_list *np; unsigned int hash_val; np = (struct domain_list *) shm_malloc(sizeof(*np) + d->len + a->len); if (np == NULL) { LM_ERR("Cannot allocate memory for hash table entry\n"); return -1; } memset(np, 0, sizeof(*np)); np->domain.len = d->len; np->domain.s = (char *)(np + 1); memcpy(np->domain.s, d->s, d->len); np->attrs.len = a->len; /* check to see if there is a value there */ if (a->s) { np->attrs.s = np->domain.s + d->len; memcpy(np->attrs.s, a->s, a->len); } else { np->attrs.s = NULL; } hash_val = dom_hash(&np->domain); np->next = hash_table[hash_val]; hash_table[hash_val] = np; return 1; } /* Check if domain exists in hash table */ int hash_table_lookup (struct sip_msg *msg, str *domain, pv_spec_t *pv) { struct domain_list *np; pv_value_t val; for (np = (*hash_table)[dom_hash(domain)]; np != NULL; np = np->next) { if ((np->domain.len == domain->len) && (strncasecmp(np->domain.s, domain->s, domain->len) == 0)) { if (pv && np->attrs.s) { val.rs = np->attrs; val.flags = PV_VAL_STR; if (pv_set_value(msg, pv, 0, &val) != 0) LM_ERR("cannot set attributes value\n"); } return 1; } } return -1; } int hash_table_mi_print(struct domain_list **hash_table, struct mi_node* rpl) { int i; struct domain_list *np; struct mi_node* node; if(hash_table==0) return -1; for (i = 0; i < DOM_HASH_SIZE; i++) { np = hash_table[i]; while (np) { node = add_mi_node_child(rpl, 0, 0, 0, np->domain.s, np->domain.len); if(node == 0) return -1; if (np->attrs.s) { if (!add_mi_attr(node, 0, "attributes", 10, np->attrs.s, np->attrs.len)) { LM_ERR("cannot add attributes\n"); return -1; } } np = np->next; } } return 0; } /* Free contents of hash table */ void hash_table_free (struct domain_list **hash_table) { int i; struct domain_list *np, *next; if(hash_table==0) return; for (i = 0; i < DOM_HASH_SIZE; i++) { np = hash_table[i]; while (np) { next = np->next; shm_free(np); np = next; } hash_table[i] = NULL; } } opensips-2.2.2/modules/domain/hash.h000066400000000000000000000023561300170765700174040ustar00rootroot00000000000000/* * Header file for hash table functions * * Copyright (C) 2002-2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _DOM_HASH_H_ #define _DOM_HASH_H_ #include #include "domain_mod.h" #include "../../mi/mi.h" int hash_table_install (struct domain_list **hash_table, str *d, str *a); int hash_table_lookup (struct sip_msg *msg, str *domain, pv_spec_t *pv); int hash_table_mi_print(struct domain_list **hash_table, struct mi_node* rpl); void hash_table_free (struct domain_list **hash_table); #endif opensips-2.2.2/modules/domain/mi.c000066400000000000000000000035731300170765700170630ustar00rootroot00000000000000/* * Domain MI functions * * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-10-05 created (bogdan) */ #include "../../dprint.h" #include "../../db/db.h" #include "domain_mod.h" #include "domain.h" #include "hash.h" #include "mi.h" /* * MI function to reload domain table */ struct mi_root* mi_domain_reload(struct mi_root *cmd_tree, void *param) { if(db_mode==0) return init_mi_tree( 500, "command not activated", 21); if (reload_domain_table () == 1) { return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } else { return init_mi_tree( 500, "Domain table reload failed", 26); } } /* * MI function to print domains from current hash table */ struct mi_root* mi_domain_dump(struct mi_root *cmd_tree, void *param) { struct mi_root* rpl_tree; if(db_mode==0) return init_mi_tree( 500, "command not activated", 21); rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return 0; rpl_tree->node.flags |= MI_IS_ARRAY; if(hash_table_mi_print(*hash_table, &rpl_tree->node)< 0) { LM_ERR("Error while adding node\n"); free_mi_tree(rpl_tree); return 0; } return rpl_tree; } opensips-2.2.2/modules/domain/mi.h000066400000000000000000000022021300170765700170540ustar00rootroot00000000000000/* * Header file for domain MI functions * * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _DOMAIN_MI_H_ #define _DOMAIN_MI_H_ #include "../../mi/mi.h" #define MI_DOMAIN_RELOAD "domain_reload" #define MI_DOMAIN_DUMP "domain_dump" struct mi_root* mi_domain_reload(struct mi_root *cmd, void *param); struct mi_root* mi_domain_dump(struct mi_root *cmd, void *param); #endif opensips-2.2.2/modules/domainpolicy/000077500000000000000000000000001300170765700175225ustar00rootroot00000000000000opensips-2.2.2/modules/domainpolicy/Makefile000066400000000000000000000003621300170765700211630ustar00rootroot00000000000000# Makefile v 1.0 2002/12/27 # # Domain Policy module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=domainpolicy.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/domainpolicy/README000066400000000000000000000425151300170765700204110ustar00rootroot00000000000000Domain Policy Module Otmar Lendl Klaus Darilion Edited by Otmar Lendl Edited by Klaus Darilion Copyright © 2002, 2003, 2006 Juha Heinanen, Otmar Lendl, Klaus Darilion Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. dp_table (string) 1.3.3. dp_col_rule (string) 1.3.4. dp_col_type (string) 1.3.5. dp_col_att (string) 1.3.6. dp_col_val (string) 1.3.7. port_override_avp (string) 1.3.8. transport_override_avp (string) 1.3.9. domain_replacement_avp (string) 1.3.10. domain_prefix_avp (string) 1.3.11. domain_suffix_avp (string) 1.3.12. send_socket_avp (string) 1.4. Exported Functions 1.4.1. dp_can_connect() 1.4.2. dp_apply_policy() 1.5. FIFO Commands 1.6. Usage Scenarios 1.6.1. TLS Based Federation 1.6.2. SIP Hub based Federation 1.6.3. Walled Garden Federation 1.7. Known Limitations List of Examples 1.1. Setting db_url parameter 1.2. Setting dp_table parameter 1.3. Setting dp_col_rule parameter 1.4. Setting dp_col_rule parameter 1.5. Setting dp_col_att parameter 1.6. Setting dp_col_val parameter 1.7. Setting port_override_avp parameter 1.8. Setting transport_override_avp parameter 1.9. Setting domain_replacement_avp parameter 1.10. Setting domain_prefix_avp parameter 1.11. Setting domain_suffix_avp parameter 1.12. Setting send_socket_avp parameter 1.13. dp_can_connect usage 1.14. dp_apply_policy usage Chapter 1. Admin Guide 1.1. Overview The Domain Policy module implements draft-lendl-domain-policy-ddds-02 in combination with draft-lendl-speermint-federations-02 and draft-lendl-speermint-technical-policy-00. These drafts define DNS records with which a domain can announce its federation memberships. A local database can be used to map policy rules to routing policy decisions. This database can also contain rules concerning destination domains independently of draft-lendl-domain-policy-ddds-02. This module requires a database. No caching is implemented. 1.2. Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): * database -- Any database module 1.3. Exported Parameters 1.3.1. db_url (string) This is URL of the database to be used. Default value is “mysql://opensipsro:opensipsro@localhost/opensips†Example 1.1. Setting db_url parameter modparam("domainpolicy", "db_url", "postgresql://user:pass@db_host/opens ips") 1.3.2. dp_table (string) Name of table containing the local support domain policy setup. Default value is “domainpolicyâ€. Example 1.2. Setting dp_table parameter modparam("domainpolicy", "dp_table", "supportedpolicies") 1.3.3. dp_col_rule (string) Name of column containing the domain policy rule name which is equal to the URI as published in the domain policy NAPTRs. Default value is “ruleâ€. Example 1.3. Setting dp_col_rule parameter modparam("domainpolicy", "dp_col_rule", "rules") 1.3.4. dp_col_type (string) Name of column containing the domain policy rule type. In the case of federation names, this is "fed". For standard referrals according to draft-lendl-speermint-technical-policy-00, this is "std". For direct domain lookups, this is "dom". Default value is “typeâ€. Example 1.4. Setting dp_col_rule parameter modparam("domainpolicy", "dp_col_type", "type") 1.3.5. dp_col_att (string) Name of column containing the AVP's name. If the rule stored in this row triggers, than dp_can_connect() will add an AVP with that name. Default value is “attâ€. Example 1.5. Setting dp_col_att parameter modparam("domainpolicy", "dp_col_att", "attribute") 1.3.6. dp_col_val (string) Name of column containing the value for AVPs created by dp_can_connect(). Default value is “valâ€. Example 1.6. Setting dp_col_val parameter modparam("domainpolicy", "dp_col_val", "values") 1.3.7. port_override_avp (string) This parameter defines the name of the AVP where dp_apply_policy() will look for an override port number. Default value is “portoverrideâ€. Example 1.7. Setting port_override_avp parameter # string named AVP modparam("domainpolicy", "port_override_avp", "portoverride") 1.3.8. transport_override_avp (string) Name of the AVP which contains the override transport setting. Default value is “transportoverrideâ€. Example 1.8. Setting transport_override_avp parameter # string named AVP modparam("domainpolicy", "transport_override_avp", "transportoverride") 1.3.9. domain_replacement_avp (string) Name of the AVP which contains a domain replacement. Default value is “domainreplacementâ€. Example 1.9. Setting domain_replacement_avp parameter # string named AVP modparam("domainpolicy", "domain_replacement_avp", "domainreplacement") 1.3.10. domain_prefix_avp (string) Name of the AVP which contains a domain prefix. Default value is “domainprefixâ€. Example 1.10. Setting domain_prefix_avp parameter # string named AVP modparam("domainpolicy", "domain_prefix_avp", "domainprefix") 1.3.11. domain_suffix_avp (string) Name of the AVP which contains a domain suffix. Default value is “domainsuffixâ€. Example 1.11. Setting domain_suffix_avp parameter # string named AVP modparam("domainpolicy", "domain_suffix_avp", "domainsuffix") 1.3.12. send_socket_avp (string) Name of the AVP which contains a send_socket. The format of the send socket (the payload of this AVP) must be in the format [proto:]ip_address[:port]. The function dp_apply_policy will look for this AVP and if defined, it will force the send socket to its value (smilar to the force_send_socket core function). Default value is “sendsocketâ€. Example 1.12. Setting send_socket_avp parameter # string named AVP modparam("domainpolicy", "send_socket_avp", "sendsocket") 1.4. Exported Functions 1.4.1. dp_can_connect() Checks the interconnection policy of the caller. It uses the domain in the request URI to perform the DP-DDDS algorithm according to draft-lendl-domain-policy-ddds-02 to retrieve the domain's policy announcements. As of this version, only records conforming to draft-lendl-speermint-federations-02 and draft-lendl-speermint-technical-policy-00 are supported. Non-terminal NAPTR records will cause recursion to the replacement domain. dp_can_connect() will thus look for policy rules in the referenced domain. Furthermore, an AVP for "domainreplacement" (containing the new domain) will be added to the call. This will redirect SRV/A record lookups to the new domain. In order to simplify direct domain-based peerings all destination domains are treated as if they contain a top priority "D2P+SIP:dom" rule with the domain itself as the value of the rule. Thus any database row with type = 'dom' and rule = 'example.com' will override any dynamic DNS-discovered rules. For NAPTRs with service-type "D2P+SIP:fed", the federation IDs (as extracted from the regexp field) are used to retrieve policy records from a local local database (basically: "SELECT dp_col_att, dp_col_val FROM dp_table WHERE dp_col_rule = '[federationID]' AND type = 'fed'). If records are found (and all other records with the same order value are fulfillable) then AVPs will be created from the dp_col_att and dp_col_val columns. For NAPTRs with service-type "D2P+SIP:std", the same procedure is performed. This time, the database lookup searched for type = 'std', though. "D2P+SIP:fed" and "D2P+SIP:std" can be mixed freely. If two rules with the same "order" match and try to set the same AVP, then the behaviour is undefined. The dp_col_att column specifies the AVP's name. If the AVP start with "s:" or "i:", the corresponding AVP type (string named or integer named) will be generated. If the excat specifier is omited, the AVP type will be guessed. The dp_col_val column will always be interpreted as string. Thus, the AVP's value is always string based. dp_can_connect returns: * -2: on errors during the evaluation. (DNS, DB, ...) * -1: D2P+SIP records were found, but the policy is not fullfillable. * 1: D2P+SIP records were found and a call is possible * 2: No D2P+SIP records were found. The destination domain does not announce a policy for incoming SIP calls. This function can be used from REQUEST_ROUTE. Example 1.13. dp_can_connect usage ... dp_can_connect(); switch(retcode) { case -2: xlog("L_INFO","Errors during the DP evaluation\n"); sl_send_reply("404", "We can't connect you."); break; case -1: xlog("L_INFO","We can't connect to that domain\n"); sl_send_reply("404", "We can't connect you."); break; case 1: xlog("L_INFO","We found matching policy records\n"); avp_print(); dp_apply_policy(); t_relay(); break; case 2: xlog("L_INFO","No DP records found\n"); t_relay(); break; } ... 1.4.2. dp_apply_policy() This function sets the destination URI according to the policy returned from the dp_can_connect() function. Parameter exchange between dp_can_connect() and dp_apply_policy() is done via AVPs. The AVPs can be configured in the module's parameter section. Note: The name of the AVPs must correspond with the names in the att column in the domainpolicy table. Setting the following AVPs in dp_can_connect() (or by any other means) cause the following actions in dp_apply_policy(): * port_override_avp: If this AVP is set, the port in the destination URI is set to this port. Setting an override port disables NAPTR and SRV lookups according to RFC 3263. * transport_override_avp: If this AVP is set, the transport parameter in the destination URI is set to the specified transport ("udp", "tcp", "tls"). Setting an override transport also disables NAPTR lookups, but retains an SRV lookup according to RFC 3263. * domain_replacement_avp: If this AVP is set, the domain in the destination URI will be replaced by this domain. A non-terminal NAPTR and thus a referral to a new domain implicitly sets domain_replacement_avp to the new domain. * domain_prefix_avp: If this AVP is set, the domain in the destination URI will be prefixed with this "subdomain". E.g. if the domain in the request URI is "example.com" and the domain_prefix_avp contains "inbound", the domain in the destinaton URI is set to "inbound.example.com". * domain_suffix_avp: If this AVP is set, the domain in the destination URI will have the content of the AVP appended to it. E.g. if the domain in the request URI is "example.com" and the domain_suffix_avp contains "myroot.com", the domain in the destination URI is set to "example.com.myroot.com". * send_socket_avp: If this AVP is set, the sending socket will be forced to the socket in the AVP. The payload format of this AVP must be [proto:]ip_address[:port]. If both prefix/suffix and domain replacements are used, then the replacement is performed first and the prefix/suffix are applied to the new domain. This function can be used from REQUEST_ROUTE. Example 1.14. dp_apply_policy usage ... if (dp_apply_policy()) { t_relay(); } ... 1.5. FIFO Commands 1.6. Usage Scenarios This section describes how this module can be use to implement selective VoIP peerings. 1.6.1. TLS Based Federation This example shows how a secure peering fabric can be configured based on TLS and Domain Policies. Let's assume that an organization called "TLSFED.org" acts as an umbrella for VoIP providers who want to peer with each other but don't want to run open SIP proxies. TLSFED.org's secretary acts as an X.509 Certification Authority that signs the TLS keys of all member's SIP proxies. Each member should automatically allow incoming calls from other members. On the other hand, the configuration for this federation must not interfere with a member's participation in other VoIP peering fabrics. All this can be achieved by the following configuration for a participating VoIP operation called example.com: * Incoming SIP configuration Calls from other members are expected to use TLS and authenticate using a client-CERT. To implement this, we cannot share a TCP/TLS port with other incoming connection. Thus we need to use tls_server_domain[] to dedicate a TCP port for this federation. tls_server_domain[1.2.3.4:5066] { tls_certificate = "/path/to/tlsfed/example-com.key" tls_private_key = "/path/to/tlsfed/example-com.crt" tls_ca_list = "/path/to/tlsfed/ca.pem" tls_method = tlsv1 tls_verify_client = 1 tls_require_cleint_certificate = 1 } * Outgoing SIP configuration Calls to other members also must use the proper client cert. Therefore, a TLS client domain must be configured. We use the federation name as TLS client domain identifier. Therefore, the content of the "tls_client_domain_avp" must be set to this identifier (e.g. by putting it as rule into the domainpolicy table). tls_client_domain["tlsfed"] { tls_certificate = "/path/to/tlsfed/example-com.key" tls_private_key = "/path/to/tlsfed/example-com.crt" tls_ca_list = "/path/to/tlsfed/ca.pem" tls_method = tlsv1 tls_verify_server = 1 } 1.6.2. SIP Hub based Federation This example shows how a peering fabric based on a central SIP hub can be configured. Let's assume that an organization called "HUBFED.org" acts as an umbrella for VoIP providers who want to peer with each other but don't want to run open SIP proxies. Instead, HUBFED.org operates a central SIP proxy which will relay calls between all participating members. Each member thus only needs to allow incoming calls from that central hub (which could be done by firewalling). All this can be achieved by the following configuration for a participating VoIP operation called example.com: * DNS configuration The destination network announces its membership in this federation. $ORIGIN destination.example.org @ IN NAPTR 10 50 "U" "D2P+SIP:fed" ( "!^.*$!http://HUBFED.org/!" . ) * Outgoing SIP configuration Calls to other members need to be redirected to the central proxy. The domainpolicy table just needs to list the federation and link it to the central proxy's domain name: mysql> select * from domainpolicy; +----+--------------------+------+-------------------+----------------+ | id | rule | type | att | val | +----+--------------------+------+-------------------+----------------+ | 1 | http://HUBFED.org/ | fed | domainreplacement | sip.HUBFED.org | +----+--------------------+------+-------------------+----------------+ 1.6.3. Walled Garden Federation This example assumes that a set of SIP providers have established a secure Layer 3 network between their proxies. It does not matter whether this network is build by means of IPsec, a private Layer 2 network, or by simple firewalling. We will use the 10.x network (for the walled garden net) and "http://l3fed.org/" (as federation identifier) in this example. A member of this federation (e.g. example.com) can not announce its SIP proxy's 10.x address in the standard SRV / A records of his domain, as this address is only meaningful for other members of this federation. In order to facilite different IP address resolution paths within the federation vs. outside the federation, all members of "http://l3fed.org/" agree to prefix the destination domains with "l3fed" before the SRV (or A) lookup. Here is the configuration for example.com: * DNS configuration The destination network announces its membership in this federation. $ORIGIN example.com @ IN NAPTR 10 50 "U" "D2P+SIP:fed" ( "!^.*$!http://l3fed.org/!" . ) _sip._udp IN SRV 10 10 5060 publicsip.example.com. _sip._udp.l3fe IN SRV 10 10 5060 l3fedsip.example.com. publicsip IN A 193.XXX.YYY.ZZZ l3fedsip IN A 10.0.0.42 * Outgoing SIP configuration The domainpolicy table just needs to link the federation identifier to the agreed apon prefix: mysql> select * from domainpolicy; +----+-------------------+------+--------------+-------+ | id | rule | type | att | val | +----+-------------------+------+--------------+-------+ | 1 | http://l3fed.org/ | fed | domainprefix | l3fed | +----+-------------------+------+--------------+-------+ 1.7. Known Limitations opensips-2.2.2/modules/domainpolicy/doc/000077500000000000000000000000001300170765700202675ustar00rootroot00000000000000opensips-2.2.2/modules/domainpolicy/doc/domainpolicy.xml000066400000000000000000000024771300170765700235120ustar00rootroot00000000000000 %docentities; ]> Domain Policy Module Otmar Lendl otmar.lendl@enum.at Klaus Darilion klaus.darilion@enum.at Otmar Lendl otmar.lendl@enum.at Klaus Darilion klaus.darilion@enum.at 2002 2003 2006 Juha Heinanen, Otmar Lendl, Klaus Darilion $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/domainpolicy/doc/domainpolicy_admin.xml000066400000000000000000000507221300170765700246560ustar00rootroot00000000000000 &adminguide;
Overview The Domain Policy module implements draft-lendl-domain-policy-ddds-02 in combination with draft-lendl-speermint-federations-02 and draft-lendl-speermint-technical-policy-00. These drafts define DNS records with which a domain can announce its federation memberships. A local database can be used to map policy rules to routing policy decisions. This database can also contain rules concerning destination domains independently of draft-lendl-domain-policy-ddds-02. This module requires a database. No caching is implemented.
Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): database -- Any database module
Exported Parameters
<varname>db_url</varname> (string) This is URL of the database to be used. Default value is &defaultrodb; Setting db_url parameter modparam("domainpolicy", "db_url", "postgresql://user:pass@db_host/opensips")
<varname>dp_table</varname> (string) Name of table containing the local support domain policy setup. Default value is domainpolicy. Setting dp_table parameter modparam("domainpolicy", "dp_table", "supportedpolicies")
<varname>dp_col_rule</varname> (string) Name of column containing the domain policy rule name which is equal to the URI as published in the domain policy NAPTRs. Default value is rule. Setting dp_col_rule parameter modparam("domainpolicy", "dp_col_rule", "rules")
<varname>dp_col_type</varname> (string) Name of column containing the domain policy rule type. In the case of federation names, this is "fed". For standard referrals according to draft-lendl-speermint-technical-policy-00, this is "std". For direct domain lookups, this is "dom". Default value is type. Setting dp_col_rule parameter modparam("domainpolicy", "dp_col_type", "type")
<varname>dp_col_att</varname> (string) Name of column containing the AVP's name. If the rule stored in this row triggers, than dp_can_connect() will add an AVP with that name. Default value is att. Setting dp_col_att parameter modparam("domainpolicy", "dp_col_att", "attribute")
<varname>dp_col_val</varname> (string) Name of column containing the value for AVPs created by dp_can_connect(). Default value is val. Setting dp_col_val parameter modparam("domainpolicy", "dp_col_val", "values")
<varname>port_override_avp</varname> (string) This parameter defines the name of the AVP where dp_apply_policy() will look for an override port number. Default value is portoverride. Setting port_override_avp parameter # string named AVP modparam("domainpolicy", "port_override_avp", "portoverride")
<varname>transport_override_avp</varname> (string) Name of the AVP which contains the override transport setting. Default value is transportoverride. Setting transport_override_avp parameter # string named AVP modparam("domainpolicy", "transport_override_avp", "transportoverride")
<varname>domain_replacement_avp</varname> (string) Name of the AVP which contains a domain replacement. Default value is domainreplacement. Setting domain_replacement_avp parameter # string named AVP modparam("domainpolicy", "domain_replacement_avp", "domainreplacement")
<varname>domain_prefix_avp</varname> (string) Name of the AVP which contains a domain prefix. Default value is domainprefix. Setting domain_prefix_avp parameter # string named AVP modparam("domainpolicy", "domain_prefix_avp", "domainprefix")
<varname>domain_suffix_avp</varname> (string) Name of the AVP which contains a domain suffix. Default value is domainsuffix. Setting domain_suffix_avp parameter # string named AVP modparam("domainpolicy", "domain_suffix_avp", "domainsuffix")
<varname>send_socket_avp</varname> (string) Name of the AVP which contains a send_socket. The format of the send socket (the payload of this AVP) must be in the format [proto:]ip_address[:port]. The function dp_apply_policy will look for this AVP and if defined, it will force the send socket to its value (smilar to the force_send_socket core function). Default value is sendsocket. Setting send_socket_avp parameter # string named AVP modparam("domainpolicy", "send_socket_avp", "sendsocket")
Exported Functions
<function moreinfo="none">dp_can_connect()</function> Checks the interconnection policy of the caller. It uses the domain in the request URI to perform the DP-DDDS algorithm according to draft-lendl-domain-policy-ddds-02 to retrieve the domain's policy announcements. As of this version, only records conforming to draft-lendl-speermint-federations-02 and draft-lendl-speermint-technical-policy-00 are supported. Non-terminal NAPTR records will cause recursion to the replacement domain. dp_can_connect() will thus look for policy rules in the referenced domain. Furthermore, an AVP for "domainreplacement" (containing the new domain) will be added to the call. This will redirect SRV/A record lookups to the new domain. In order to simplify direct domain-based peerings all destination domains are treated as if they contain a top priority "D2P+SIP:dom" rule with the domain itself as the value of the rule. Thus any database row with type = 'dom' and rule = 'example.com' will override any dynamic DNS-discovered rules. For NAPTRs with service-type "D2P+SIP:fed", the federation IDs (as extracted from the regexp field) are used to retrieve policy records from a local local database (basically: "SELECT dp_col_att, dp_col_val FROM dp_table WHERE dp_col_rule = '[federationID]' AND type = 'fed'). If records are found (and all other records with the same order value are fulfillable) then AVPs will be created from the dp_col_att and dp_col_val columns. For NAPTRs with service-type "D2P+SIP:std", the same procedure is performed. This time, the database lookup searched for type = 'std', though. "D2P+SIP:fed" and "D2P+SIP:std" can be mixed freely. If two rules with the same "order" match and try to set the same AVP, then the behaviour is undefined. The dp_col_att column specifies the AVP's name. If the AVP start with "s:" or "i:", the corresponding AVP type (string named or integer named) will be generated. If the excat specifier is omited, the AVP type will be guessed. The dp_col_val column will always be interpreted as string. Thus, the AVP's value is always string based. dp_can_connect returns: -2: on errors during the evaluation. (DNS, DB, ...) -1: D2P+SIP records were found, but the policy is not fullfillable. 1: D2P+SIP records were found and a call is possible 2: No D2P+SIP records were found. The destination domain does not announce a policy for incoming SIP calls. This function can be used from REQUEST_ROUTE. dp_can_connect usage ... dp_can_connect(); switch(retcode) { case -2: xlog("L_INFO","Errors during the DP evaluation\n"); sl_send_reply("404", "We can't connect you."); break; case -1: xlog("L_INFO","We can't connect to that domain\n"); sl_send_reply("404", "We can't connect you."); break; case 1: xlog("L_INFO","We found matching policy records\n"); avp_print(); dp_apply_policy(); t_relay(); break; case 2: xlog("L_INFO","No DP records found\n"); t_relay(); break; } ...
<function moreinfo="none">dp_apply_policy()</function> This function sets the destination URI according to the policy returned from the dp_can_connect() function. Parameter exchange between dp_can_connect() and dp_apply_policy() is done via AVPs. The AVPs can be configured in the module's parameter section. Note: The name of the AVPs must correspond with the names in the att column in the domainpolicy table. Setting the following AVPs in dp_can_connect() (or by any other means) cause the following actions in dp_apply_policy(): port_override_avp: If this AVP is set, the port in the destination URI is set to this port. Setting an override port disables NAPTR and SRV lookups according to RFC 3263.   transport_override_avp: If this AVP is set, the transport parameter in the destination URI is set to the specified transport ("udp", "tcp", "tls"). Setting an override transport also disables NAPTR lookups, but retains an SRV lookup according to RFC 3263.   domain_replacement_avp: If this AVP is set, the domain in the destination URI will be replaced by this domain. A non-terminal NAPTR and thus a referral to a new domain implicitly sets domain_replacement_avp to the new domain.   domain_prefix_avp: If this AVP is set, the domain in the destination URI will be prefixed with this "subdomain". E.g. if the domain in the request URI is "example.com" and the domain_prefix_avp contains "inbound", the domain in the destinaton URI is set to "inbound.example.com".   domain_suffix_avp: If this AVP is set, the domain in the destination URI will have the content of the AVP appended to it. E.g. if the domain in the request URI is "example.com" and the domain_suffix_avp contains "myroot.com", the domain in the destination URI is set to "example.com.myroot.com".   send_socket_avp: If this AVP is set, the sending socket will be forced to the socket in the AVP. The payload format of this AVP must be [proto:]ip_address[:port]. If both prefix/suffix and domain replacements are used, then the replacement is performed first and the prefix/suffix are applied to the new domain. This function can be used from REQUEST_ROUTE. dp_apply_policy usage ... if (dp_apply_policy()) { t_relay(); } ...
<acronym>FIFO</acronym> Commands
Usage Scenarios This section describes how this module can be use to implement selective VoIP peerings.
TLS Based Federation This example shows how a secure peering fabric can be configured based on TLS and Domain Policies. Let's assume that an organization called "TLSFED.org" acts as an umbrella for VoIP providers who want to peer with each other but don't want to run open SIP proxies. TLSFED.org's secretary acts as an X.509 Certification Authority that signs the TLS keys of all member's SIP proxies. Each member should automatically allow incoming calls from other members. On the other hand, the configuration for this federation must not interfere with a member's participation in other VoIP peering fabrics. All this can be achieved by the following configuration for a participating VoIP operation called example.com: Incoming SIP configuration Calls from other members are expected to use TLS and authenticate using a client-CERT. To implement this, we cannot share a TCP/TLS port with other incoming connection. Thus we need to use tls_server_domain[] to dedicate a TCP port for this federation.   tls_server_domain[1.2.3.4:5066] { tls_certificate = "/path/to/tlsfed/example-com.key" tls_private_key = "/path/to/tlsfed/example-com.crt" tls_ca_list = "/path/to/tlsfed/ca.pem" tls_method = tlsv1 tls_verify_client = 1 tls_require_cleint_certificate = 1 }   Outgoing SIP configuration Calls to other members also must use the proper client cert. Therefore, a TLS client domain must be configured. We use the federation name as TLS client domain identifier. Therefore, the content of the "tls_client_domain_avp" must be set to this identifier (e.g. by putting it as rule into the domainpolicy table).   tls_client_domain["tlsfed"] { tls_certificate = "/path/to/tlsfed/example-com.key" tls_private_key = "/path/to/tlsfed/example-com.crt" tls_ca_list = "/path/to/tlsfed/ca.pem" tls_method = tlsv1 tls_verify_server = 1 }
SIP Hub based Federation This example shows how a peering fabric based on a central SIP hub can be configured. Let's assume that an organization called "HUBFED.org" acts as an umbrella for VoIP providers who want to peer with each other but don't want to run open SIP proxies. Instead, HUBFED.org operates a central SIP proxy which will relay calls between all participating members. Each member thus only needs to allow incoming calls from that central hub (which could be done by firewalling). All this can be achieved by the following configuration for a participating VoIP operation called example.com: DNS configuration The destination network announces its membership in this federation.   $ORIGIN destination.example.org @ IN NAPTR 10 50 "U" "D2P+SIP:fed" ( "!^.*$!http://HUBFED.org/!" . )   Outgoing SIP configuration Calls to other members need to be redirected to the central proxy. The domainpolicy table just needs to list the federation and link it to the central proxy's domain name:   mysql> select * from domainpolicy; +----+--------------------+------+-------------------+----------------+ | id | rule | type | att | val | +----+--------------------+------+-------------------+----------------+ | 1 | http://HUBFED.org/ | fed | domainreplacement | sip.HUBFED.org | +----+--------------------+------+-------------------+----------------+  
Walled Garden Federation This example assumes that a set of SIP providers have established a secure Layer 3 network between their proxies. It does not matter whether this network is build by means of IPsec, a private Layer 2 network, or by simple firewalling. We will use the 10.x network (for the walled garden net) and "http://l3fed.org/" (as federation identifier) in this example. A member of this federation (e.g. example.com) can not announce its SIP proxy's 10.x address in the standard SRV / A records of his domain, as this address is only meaningful for other members of this federation. In order to facilite different IP address resolution paths within the federation vs. outside the federation, all members of "http://l3fed.org/" agree to prefix the destination domains with "l3fed" before the SRV (or A) lookup. Here is the configuration for example.com: DNS configuration The destination network announces its membership in this federation.   $ORIGIN example.com @ IN NAPTR 10 50 "U" "D2P+SIP:fed" ( "!^.*$!http://l3fed.org/!" . ) _sip._udp IN SRV 10 10 5060 publicsip.example.com. _sip._udp.l3fe IN SRV 10 10 5060 l3fedsip.example.com. publicsip IN A 193.XXX.YYY.ZZZ l3fedsip IN A 10.0.0.42   Outgoing SIP configuration The domainpolicy table just needs to link the federation identifier to the agreed apon prefix:   mysql> select * from domainpolicy; +----+-------------------+------+--------------+-------+ | id | rule | type | att | val | +----+-------------------+------+--------------+-------+ | 1 | http://l3fed.org/ | fed | domainprefix | l3fed | +----+-------------------+------+--------------+-------+  
Known Limitations
opensips-2.2.2/modules/domainpolicy/domainpolicy.c000066400000000000000000000577701300170765700223750ustar00rootroot00000000000000/* * Domain Policy related functions * * Copyright (C) 2006 Otmar Lendl & Klaus Darilion * * Based on the ENUM and domain module. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-04-20 Initial Version * 2006-09-08 Updated to -02 version, added support for D2P+SIP:std */ #include "domainpolicy_mod.h" #include "domainpolicy.h" #include "../../db/db.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../ut.h" #include "../../dset.h" #include "../../route.h" #include "../../ip_addr.h" #include "../../socket_info.h" #include "../../resolve.h" #include "../../regexp.h" #define IS_D2PNAPTR(naptr) ((naptr->services_len >= 7) && (!strncasecmp("D2P+SIP", naptr->services, 7))) static db_con_t* db_handle=0; static db_func_t domainpolicy_dbf; /* * some helper structs + functions to help build up the AVPs. * We can't immediately store them in AVPs as a later non-matched * rule can result in junking all the AVPs added up to that moment. * * Thus we store them temporarily in an avp_stack. */ #define AVPMAXSIZE 120 #define AVPSTACKSIZE 32 struct avp { char att[AVPMAXSIZE]; char val[AVPMAXSIZE]; }; struct avp_stack { int succeeded; int i; struct avp avp[AVPSTACKSIZE]; }; /* * Push avp-pair on stack. * * return 0 on failure. */ static int stack_push(struct avp_stack *stack, char *att, char *val) { int i; if (stack->i >= (AVPSTACKSIZE-1)) { LM_ERR("exceeded stack size.!\n"); return(0); } i = (stack->i)++; strncpy(stack->avp[i].att, att, AVPMAXSIZE - 1); strncpy(stack->avp[i].val, val, AVPMAXSIZE - 1); stack->succeeded = 1; return(1); } static void stack_reset(struct avp_stack *stack) { stack->i = 0; stack->succeeded = 0; } static int stack_succeeded(struct avp_stack *stack) { return(stack->succeeded); } static void stack_to_avp(struct avp_stack *stack) { int j; int avp_att; int_str avp_val; for(j=0; j< stack->i; j++) { /* AVP names can be integer or string based */ LM_DBG("process AVP: name='%s' value='%s'\n", stack->avp[j].att, stack->avp[j].val); /* we will only use string avps * so all names are strings */ avp_val.s.s = stack->avp[j].att; avp_val.s.len = strlen(avp_val.s.s); avp_att = get_avp_id(&avp_val.s); if (avp_att < 0) { LM_ERR("cannot find %s avp\n", avp_val.s.s); continue; } LM_DBG("create string named AVP \n", avp_val.s.len, ZSW(avp_val.s.s)); avp_val.s.s = stack->avp[j].val; avp_val.s.len = strlen(avp_val.s.s); /* string type explicitely forced with s: */ if(add_avp(AVP_VAL_STR, avp_att, avp_val)) LM_ERR("cannot add avp\n"); } } /* helper db functions*/ int domainpolicy_db_bind(const str* db_url) { if (db_bind_mod(db_url, &domainpolicy_dbf )) { LM_CRIT("cannot bind to database module! " "Did you forget to load a database module ?\n"); return -1; } return 0; } int domainpolicy_db_init(const str* db_url) { if (domainpolicy_dbf.init==0){ LM_CRIT("unbound database module\n"); goto error; } db_handle=domainpolicy_dbf.init(db_url); if (db_handle==0){ LM_CRIT("cannot initialize database connection\n"); goto error; } return 0; error: return -1; } void domainpolicy_db_close(void) { if (db_handle && domainpolicy_dbf.close){ domainpolicy_dbf.close(db_handle); db_handle=0; } } int domainpolicy_db_ver(const str* db_url, const str* name) { int ver; db_con_t* dbh; if (domainpolicy_dbf.init==0){ LM_CRIT("unbound database\n"); return -1; } dbh=domainpolicy_dbf.init(db_url); if (dbh==0){ LM_CRIT("null database handler\n"); return -1; } ver=db_table_version(&domainpolicy_dbf, dbh, name); domainpolicy_dbf.close(dbh); return ver; } /***************************/ /* * * code from enum.c * * should be moved to some DDDS support module instead of code-duplication * * */ /* Parse NAPTR regexp field of the form !pattern!replacement! and return its * components in pattern and replacement parameters. Regexp field starts at * address first and is len characters long. */ static inline int parse_naptr_regexp(char* first, int len, str* pattern, str* replacement) { char *second, *third; if (len > 0) { if (*first == '!') { second = (char *)memchr((void *)(first + 1), '!', len - 1); if (second) { len = len - (second - first + 1); if (len > 0) { third = memchr(second + 1, '!', len); if (third) { pattern->len = second - first - 1; pattern->s = first + 1; replacement->len = third - second - 1; replacement->s = second + 1; return 1; } else { LM_ERR("third ! missing from regexp\n"); return -1; } } else { LM_ERR("third ! missing from regexp\n"); return -2; } } else { LM_ERR("second ! missing from regexp\n"); return -3; } } else { LM_ERR("first ! missing from regexp\n"); return -4; } } else { LM_ERR("regexp missing\n"); return -5; } } /* * Tests if one result record is "greater" that the other. Non-NAPTR records * greater that NAPTR record. An invalid NAPTR record is greater than a * valid one. Valid NAPTR records are compared based on their * (order,preference). * * Naptrs without D2P+SIP service field are greater. * */ static inline int naptr_greater(struct rdata* a, struct rdata* b) { struct naptr_rdata *na, *nb; if (a->type != T_NAPTR) return 1; if (b->type != T_NAPTR) return 0; na = (struct naptr_rdata*)a->rdata; if (na == 0) return 1; nb = (struct naptr_rdata*)b->rdata; if (nb == 0) return 0; if (!IS_D2PNAPTR(na)) return 1; if (!IS_D2PNAPTR(nb)) return 0; return (((na->order) << 16) + na->pref) > (((nb->order) << 16) + nb->pref); } /* * Bubble sorts result record list according to naptr (order,preference). */ static inline void naptr_sort(struct rdata** head) { struct rdata *p, *q, *r, *s, *temp, *start; /* r precedes p and s points to the node up to which comparisons are to be made */ s = NULL; start = *head; while ( s != start -> next ) { r = p = start ; q = p -> next ; while ( p != s ) { if ( naptr_greater(p, q) ) { if ( p == start ) { temp = q -> next ; q -> next = p ; p -> next = temp ; start = q ; r = q ; } else { temp = q -> next ; q -> next = p ; p -> next = temp ; r -> next = q ; r = q ; } } else { r = p ; p = p -> next ; } q = p -> next ; if ( q == s ) s = p ; } } *head = start; } /* * input: rule straight from the DDDS + avp-stack. * * output: adds found rules to the stack and return * 1 on success * 0 on failure */ static int check_rule(str *rule, char *service, int service_len, struct avp_stack *stack) { /* for the select */ db_key_t keys[2]; db_val_t vals[2]; db_key_t cols[4]; db_res_t* res; db_row_t* row; db_val_t* val; int i; char *type; int type_len; LM_INFO("checking for '%.*s'.\n", rule->len, ZSW(rule->s)); if ((service_len != 11) || (strncasecmp("d2p+sip:fed", service, 11) && strncasecmp("d2p+sip:std", service, 11) && strncasecmp("d2p+sip:dom", service, 11))) { LM_ERR("can only cope with d2p+sip:fed, d2p+sip:std,and d2p+sip:dom " "for now (and not %.*s).\n", service_len, service); return(0); } type = service + 8; type_len = service_len - 8; if (domainpolicy_dbf.use_table(db_handle, &domainpolicy_table) < 0) { LM_ERR("failed to domainpolicy table\n"); return -1; } keys[0]=&domainpolicy_col_rule; keys[1]=&domainpolicy_col_type; cols[0]=&domainpolicy_col_rule; cols[1]=&domainpolicy_col_type; cols[2]=&domainpolicy_col_att; cols[3]=&domainpolicy_col_val; VAL_TYPE(&vals[0]) = DB_STR; VAL_NULL(&vals[0]) = 0; VAL_STR(&vals[0]).s = rule->s; VAL_STR(&vals[0]).len = rule->len; VAL_TYPE(&vals[1]) = DB_STR; VAL_NULL(&vals[1]) = 0; VAL_STR(&vals[1]).s = type; VAL_STR(&vals[1]).len = type_len; /* * SELECT rule, att, val from domainpolicy where rule = "..." */ if (domainpolicy_dbf.query(db_handle, keys, 0, vals, cols, 2, 4, 0, &res) < 0 ) { LM_ERR("querying database\n"); return -1; } LM_INFO("querying database OK\n"); if (RES_ROW_N(res) == 0) { LM_DBG("rule '%.*s' is not know.\n", rule->len, ZSW(rule->s)); domainpolicy_dbf.free_result(db_handle, res); return 0; } else { LM_DBG("rule '%.*s' is known\n", rule->len, ZSW(rule->s)); row = RES_ROWS(res); for(i = 0; i < RES_ROW_N(res); i++) { if (ROW_N(row + i) != 4) { LM_ERR("unexpected cell count\n"); return(-1); } val = ROW_VALUES(row + i); if ((VAL_TYPE(val) != DB_STRING) || (VAL_TYPE(val+1) != DB_STRING) || (VAL_TYPE(val+2) != DB_STRING) || (VAL_TYPE(val+3) != DB_STRING)) { LM_ERR("unexpected cell types\n"); return(-1); } if (VAL_NULL(val+2) || VAL_NULL(val+3)) { LM_INFO("db returned NULL values. Fine with us.\n"); continue; } LM_INFO("DB returned %s/%s \n",VAL_STRING(val+2),VAL_STRING(val+3)); if (!stack_push(stack, (char *) VAL_STRING(val+2), (char *) VAL_STRING(val+3))) { return(-1); } } domainpolicy_dbf.free_result(db_handle, res); return 1; } } int dp_can_connect_str(str *domain, int rec_level) { struct rdata* head; struct rdata* l; struct naptr_rdata* naptr; struct naptr_rdata* next_naptr; int ret; str newdomain; char uri[MAX_URI_SIZE]; struct avp_stack stack; int last_order = -1; int failed = 0; int found_anything = 0; str pattern, replacement, result; stack_reset(&stack); /* If we're in a recursive call, set the domain-replacement */ if ( rec_level > 0 ) { stack_push(&stack, domain_replacement_avp.s, domain->s); stack.succeeded = 0; } if (rec_level > MAX_DDDS_RECURSIONS) { LM_ERR("too many indirect NAPTRs. Aborting at %.*s.\n", domain->len, ZSW(domain->s)); return(DP_DDDS_RET_DNSERROR); } LM_INFO("looking up Domain itself: %.*s\n",domain->len, ZSW(domain->s)); ret = check_rule(domain,"D2P+sip:dom", 11, &stack); if (ret == 1) { LM_INFO("found a match on domain itself\n"); stack_to_avp(&stack); return(DP_DDDS_RET_POSITIVE); } else if (ret == 0) { LM_INFO("no match on domain itself.\n"); stack_reset(&stack); /* If we're in a recursive call, set the domain-replacement */ if ( rec_level > 0 ) { stack_push(&stack, domain_replacement_avp.s, (char *) domain->s); stack.succeeded = 0; } } else { return(DP_DDDS_RET_DNSERROR); /* actually: DB error */ } LM_INFO("doing DDDS with %.*s\n",domain->len, ZSW(domain->s)); head = get_record(domain->s, T_NAPTR); if (head == 0) { LM_NOTICE("no NAPTR record found for %.*s.\n", domain->len, ZSW(domain->s)); return(DP_DDDS_RET_NOTFOUND); } LM_DBG("found the following NAPTRs: \n"); for (l = head; l; l = l->next) { if (l->type != T_NAPTR) { LM_DBG("found non-NAPTR record.\n"); continue; /*should never happen*/ } naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LM_CRIT("null rdata\n"); continue; } LM_DBG("order %u, pref %u, flen %u, flags '%.*s', slen %u, " "services '%.*s', rlen %u, regexp '%.*s', repl '%s'\n", naptr->order, naptr->pref, naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len, (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len, (int)(naptr->regexp_len), ZSW(naptr->regexp), ZSW(naptr->repl) ); } LM_DBG("sorting...\n"); naptr_sort(&head); for (l = head; l; l = l->next) { if (l->type != T_NAPTR) continue; /*should never happen*/ naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LM_CRIT("null rdata\n"); continue; } LM_DBG("considering order %u, pref %u, flen %u, flags '%.*s', slen %u, " "services '%.*s', rlen %u, regexp '%.*s', repl '%s'\n", naptr->order, naptr->pref, naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len, (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len, (int)(naptr->regexp_len), ZSW(naptr->regexp), ZSW(naptr->repl) ); /* * New order? then we check whether the had success during the last one. * If yes, we can leave the loop. */ if (last_order != naptr->order) { last_order = naptr->order; failed = 0; if (stack_succeeded(&stack)) { LM_INFO("we don't need to consider further orders " "(starting with %d).\n",last_order); break; } } else if (failed) { LM_INFO("order %d has already failed.\n",last_order); continue; } /* * NAPTRs we don't care about */ if (!IS_D2PNAPTR(naptr)) continue; /* * once we've been here, don't return DP_DDDS_RET_NOTFOUND */ found_anything = 1; next_naptr = NULL; if (l->next && (l->next->type == T_NAPTR)) { next_naptr = (struct naptr_rdata*)l->next->rdata; } /* * Non-terminal? */ if ((naptr->services_len == 7) && !strncasecmp("D2P+SIP", naptr->services,7) && (naptr->flags_len == 0)){ LM_INFO("found non-terminal NAPTR\n"); /* * This needs to be the only record with this order. */ if (next_naptr && (next_naptr->order == naptr->order) && IS_D2PNAPTR(next_naptr)) { LM_ERR("non-terminal NAPTR needs to be the only one " "with this order %.*s.\n", domain->len, ZSW(domain->s)); return(DP_DDDS_RET_DNSERROR); } newdomain.s = naptr->repl; newdomain.len = strlen(naptr->repl); ret = dp_can_connect_str(&newdomain, rec_level + 1); if (ret == DP_DDDS_RET_POSITIVE) /* succeeded, we're done. */ return(ret); if (ret == DP_DDDS_RET_NEGATIVE) /* found rules, did not work */ continue; /* look for more rules */ if (ret == DP_DDDS_RET_DNSERROR) /* errors during lookup */ return(ret); /* report them */ if (ret == DP_DDDS_RET_NOTFOUND) /* no entries in linked domain? */ return(ret); /* ok, fine. go with that */ continue; /* not reached */ } /* * wrong kind of terminal */ if ((naptr->flags_len != 1) || (tolower(naptr->flags[0]) != 'u')) { LM_ERR("terminal NAPTR needs flag = 'u' and not '%.*s'.\n", (int)naptr->flags_len, ZSW(naptr->flags)); /* * It's not that clear what we should do now: Ignore this records or regard it as failed. * We go with "ignore" for now. */ continue; } if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len, &pattern, &replacement) < 0) { LM_ERR("parsing of NAPTR regexp failed\n"); continue; } result.s = &(uri[0]); result.len = MAX_URI_SIZE; /* Avoid making copies of pattern and replacement */ pattern.s[pattern.len] = (char)0; replacement.s[replacement.len] = (char)0; if (reg_replace(pattern.s, replacement.s, domain->s, &result) < 0) { pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; LM_ERR("regexp replace failed\n"); continue; } LM_INFO("resulted in replacement: '%.*s'\n", result.len, ZSW(result.s)); pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; ret = check_rule(&result,naptr->services,naptr->services_len, &stack); if (ret == 1) { LM_INFO("positive return\n"); } else if (ret == 0) { LM_INFO("check_rule failed.\n"); stack_reset(&stack); /* If we're in a recursive call, set the domain-replacement */ if ( rec_level > 0 ) { stack_push(&stack, domain_replacement_avp.s, (char *) domain->s); stack.succeeded = 0; } failed = 1; } else { return(DP_DDDS_RET_DNSERROR); } } if (stack_succeeded(&stack)) { LM_INFO("calling stack_to_avp.\n"); stack_to_avp(&stack); return(DP_DDDS_RET_POSITIVE); } LM_INFO("returning %d.\n", (found_anything ? DP_DDDS_RET_NEGATIVE : DP_DDDS_RET_NOTFOUND)); return( found_anything ? DP_DDDS_RET_NEGATIVE : DP_DDDS_RET_NOTFOUND ); } int dp_can_connect(struct sip_msg* _msg, char* _s1, char* _s2) { static char domainname[MAX_DOMAIN_SIZE]; str domain; int ret; if (route_type != REQUEST_ROUTE) { LM_ERR("unsupported route type\n"); return -1; } if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("failed to parse R-URI\n"); return -1; } if (_msg->parsed_uri.host.len >= MAX_DOMAIN_SIZE) { LM_ERR("domain buffer to small\n"); return -1; } /* copy domain into static buffer as later we sometimes need \0 * terminated strings */ domain.s = (char *) &(domainname[0]); domain.len = _msg->parsed_uri.host.len; memcpy(domain.s, _msg->parsed_uri.host.s, domain.len); domainname[domain.len] = '\0'; LM_DBG("domain is %.*s.\n", domain.len, ZSW(domain.s)); ret = dp_can_connect_str(&domain,0); LM_DBG("returning %d.\n", ret); return(ret); } int dp_apply_policy(struct sip_msg* _msg, char* _s1, char* _s2) { str *domain; int_str val; struct usr_avp *avp; char duri[MAX_URI_SIZE]; str duri_str; int len, didsomething; char *at; /* pointer to current location inside duri */ str host; int port, proto; struct socket_info* si; if (route_type != REQUEST_ROUTE) { LM_ERR("unsupported route type\n"); return -1; } /* * set the send_socket */ /* search for send_socket AVP */ avp = search_first_avp(0, send_socket_name, &val, 0); if (avp) { if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) { LM_ERR("empty or non-string send_socket_avp, " "return with error ...\n"); return -1; } LM_DBG("send_socket_avp found = '%.*s'\n", val.s.len, ZSW(val.s.s)); /* parse phostport */ if (parse_phostport(val.s.s, val.s.len, &(host.s), &(host.len), &port, &proto)) { LM_ERR("could not parse send_socket, return with error ...\n"); return -1; } si = grep_sock_info( &host, (unsigned short) port, (unsigned short) proto); if (si) { _msg->force_send_socket = si; } else { LM_WARN("could not find socket for" "send_socket '%.*s'\n", val.s.len, ZSW(val.s.s)); } } else { LM_DBG("send_socket_avp not found\n"); } /* * set the destination URI */ didsomething = 0; /* if no AVP is set, there is no need to set the DURI in the end */ if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("failed to parse R-URI\n"); return -1; } at = (char *)&(duri[0]); len = 0; if ( (len + 4) > MAX_URI_SIZE) { LM_ERR("duri buffer to small to add uri schema\n"); return -1; } memcpy(at, "sip:", 4); at = at + 4; len = len + 4; domain = &(_msg->parsed_uri.host); LM_DBG("domain is %.*s.\n", domain->len, ZSW(domain->s)); /* search for prefix and add it to duri buffer */ avp = search_first_avp(0, domain_prefix_name, &val, 0); if (avp) { if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) { LM_ERR("empty or non-string domain_prefix_avp, return with error ...\n"); return -1; } LM_DBG("domain_prefix_avp found = '%.*s'\n", val.s.len, ZSW(val.s.s)); if ( (len + val.s.len +1) > MAX_URI_SIZE) { LM_ERR("duri buffer to small to add domain prefix\n"); return -1; } memcpy(at, val.s.s, val.s.len); at = at + val.s.len; *at = '.'; at = at + 1; /* add . as delimiter between prefix and domain */ didsomething = 1; } else { LM_DBG("domain_prefix_avp not found\n"); } /* add domain to duri buffer */ avp = search_first_avp(0, domain_replacement_name, &val, 0); if (avp) { if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) { LM_ERR("empty or non-string domain_replacement_avp, return with" "error ...\n"); return -1; } LM_DBG("domain_replacement_avp found='%.*s'\n",val.s.len, ZSW(val.s.s)); if ( (len + val.s.len +1) > MAX_URI_SIZE) { LM_ERR("duri buffer to small to add domain replacement\n"); return -1; } memcpy(at, val.s.s, val.s.len); at = at + val.s.len; didsomething = 1; } else { LM_DBG("domain_replacement_avp not found, using original domain '" "%.*s'\n",domain->len, domain->s); if ( (len + domain->len) > MAX_URI_SIZE) { LM_ERR("duri buffer to small to add domain\n"); return -1; } memcpy(at, domain->s, domain->len); at = at + domain->len; } /* search for suffix and add it to duri buffer */ avp = search_first_avp(0, domain_suffix_name, &val, 0); if (avp) { if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) { LM_ERR("empty or non-string domain_suffix_avp,return with error .." "\n"); return -1; } LM_DBG("domain_suffix_avp found = '%.*s'\n", val.s.len, ZSW(val.s.s)); if ( (len + val.s.len + 1) > MAX_URI_SIZE) { LM_ERR("duri buffer to small to add domain suffix\n"); return -1; } *at = '.'; at = at + 1; /* add . as delimiter between domain and suffix */ memcpy(at, val.s.s, val.s.len); at = at + val.s.len; didsomething = 1; } else { LM_DBG("domain_suffix_avp not found\n"); } /* search for port override and add it to duri buffer */ avp = search_first_avp(0, port_override_name, &val, 0); if (avp) { if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) { LM_ERR("empty or non-string port_override_avp, return with error ...\n"); return -1; } LM_DBG("port_override_avp found = '%.*s'\n", val.s.len, ZSW(val.s.s)); /* We do not check if the port is valid */ if ( (len + val.s.len + 1) > MAX_URI_SIZE) { LM_ERR("duri buffer to small to add domain suffix\n"); return -1; } *at = ':'; at = at + 1; /* add : as delimiter between domain and port */ memcpy(at, val.s.s, val.s.len); at = at + val.s.len; didsomething = 1; } else { LM_DBG("port_override_avp not found, using original port\n"); if (_msg->parsed_uri.port.len) { LM_DBG("port found in RURI, reusing it for DURI\n"); if ( (len + _msg->parsed_uri.port.len + 1) > MAX_URI_SIZE) { LM_ERR("duri buffer to small to copy port\n"); return -1; } *at = ':'; at = at + 1; /* add : as delimiter between domain and port */ memcpy(at, _msg->parsed_uri.port.s, _msg->parsed_uri.port.len); at = at + _msg->parsed_uri.port.len; } else { LM_DBG("port not found in RURI, no need to copy it to DURI\n"); } } /* search for transport override and add it to duri buffer */ avp = search_first_avp(0, transport_override_name, &val, 0); if (avp) { if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) { LM_ERR("empty or non-string transport_override_avp, " "return with error ...\n"); return -1; } LM_DBG("transport_override_avp found='%.*s'\n",val.s.len, ZSW(val.s.s)); if ( (len + val.s.len + 11) > MAX_URI_SIZE) { LM_ERR("duri buffer to small to add transport override\n"); return -1; } /* add : as transport parameter to duri; NOTE: no checks if transport parameter is valid */ memcpy(at, ";transport=", 11); at = at + 11; memcpy(at, val.s.s, val.s.len); at = at + val.s.len; didsomething = 1; } else { LM_DBG("transport_override_avp not found, using original transport\n"); if (_msg->parsed_uri.transport.len) { LM_DBG("transport found in RURI, reusing it for DURI\n"); if ( (len + _msg->parsed_uri.transport.len + 1) > MAX_URI_SIZE) { LM_ERR("duri buffer to small to copy transport\n"); return -1; } *at = ';'; at = at + 1; /* add : as delimiter between domain and port */ memcpy(at, _msg->parsed_uri.transport.s, _msg->parsed_uri.transport.len); at = at + _msg->parsed_uri.transport.len; } else { LM_DBG("transport not found in RURI, no need to copy it to DURI\n"); } } /* write new target DURI into DURI */ if (didsomething == 0) { LM_DBG("no domainpolicy AVP set, no need to push new DURI\n"); return 2; } duri_str.s = (char *)&(duri[0]); duri_str.len = at - duri_str.s; LM_DBG("new DURI is '%.*s'\n",duri_str.len, ZSW(duri_str.s)); set_dst_uri(_msg, &duri_str); return 1; } opensips-2.2.2/modules/domainpolicy/domainpolicy.h000066400000000000000000000037531300170765700223720ustar00rootroot00000000000000/* domainpolicy.h v 0.1 2002/12/27 * Header file for domainpolicy table relates functions * * Copyright (C) 2002-2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DOMAINPOLICY_H #define DOMAINPOLICY_H #include "../../parser/msg_parser.h" #define MAX_DOMAIN_SIZE 512 /* * how many indirect DDDS naptr are we going to follow? */ #define MAX_DDDS_RECURSIONS 5 /* * dp_can_connect return codes */ #define DP_DDDS_RET_DNSERROR -2 #define DP_DDDS_RET_NEGATIVE -1 /* negative values means false, 0 stops execution and discards the SIP request */ #define DP_DDDS_RET_POSITIVE 1 #define DP_DDDS_RET_NOTFOUND 2 /* * Check if host in Request URI has DP-DDDS NAPTRs and if we can connect to them */ int dp_can_connect(struct sip_msg* _msg, char* _s1, char* _s2); /* * Apply DP-DDDS policy to current SIP message. This means * build a new destination URI from the policy AVP and export it * as AVP. Then in opensips.cfg this new target AVP can be pushed * into the destination URI $duri */ int dp_apply_policy(struct sip_msg* _msg, char* _s1, char* _s2); int domainpolicy_db_bind(const str* db_url); int domainpolicy_db_init(const str* db_url); void domainpolicy_db_close(); int domainpolicy_db_ver(const str* db_url, const str* name); #endif /* DOMAINPOLICY_H */ opensips-2.2.2/modules/domainpolicy/domainpolicy_mod.c000066400000000000000000000172041300170765700232200ustar00rootroot00000000000000/* * Domain Policy related functions * * Copyright (C) 2006 Otmar Lendl & Klaus Darilion * * Based on the ENUM and domain module. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2006-04-20 Initial Version * 2006-09-08 Updated to -02 version, added support for D2P+SIP:std */ #include #include "../../sr_module.h" #include "domainpolicy_mod.h" #include "../../mem/shm_mem.h" #include "../../ut.h" #include "domainpolicy.h" /* * Module management function prototypes */ static int mod_init(void); static void destroy(void); static int child_init(int rank); /* * Version of gw and lcr tables required by the module, * increment this value if you change the table in * an backwards incompatible way */ #define DOMAINPOLICY_TABLE_VERSION 3 #define DOMAINPOLICY_TABLE "domainpolicy" #define DOMAINPOLICY_COL_RULE "rule" #define DOMAINPOLICY_COL_TYPE "type" #define DOMAINPOLICY_COL_ATT "att" #define DOMAINPOLICY_COL_VAL "val" /* Default avp names */ #define DEF_PORT_OVERRIDE_AVP "portoverride" #define DEF_TRANSPORT_OVERRIDE_AVP "transportoverride" #define DEF_DOMAIN_PREFIX_AVP "domainprefix" #define DEF_DOMAIN_SUFFIX_AVP "domainsuffix" #define DEF_DOMAIN_REPLACEMENT_AVP "domainreplacement" #define DEF_SEND_SOCKET_AVP "sendsocket" /* * Module parameter variables */ static str db_url = {NULL, 0}; /* Name of domainpolicy table */ str domainpolicy_table = str_init(DOMAINPOLICY_TABLE); str domainpolicy_col_rule = str_init(DOMAINPOLICY_COL_RULE); str domainpolicy_col_type = str_init(DOMAINPOLICY_COL_TYPE); str domainpolicy_col_att = str_init(DOMAINPOLICY_COL_ATT); str domainpolicy_col_val = str_init(DOMAINPOLICY_COL_VAL); str port_override_avp = str_init(DEF_PORT_OVERRIDE_AVP); str transport_override_avp = str_init(DEF_TRANSPORT_OVERRIDE_AVP); str domain_prefix_avp = str_init(DEF_DOMAIN_PREFIX_AVP); str domain_suffix_avp = str_init(DEF_DOMAIN_SUFFIX_AVP); str domain_replacement_avp = str_init(DEF_DOMAIN_REPLACEMENT_AVP); str send_socket_avp = str_init(DEF_SEND_SOCKET_AVP); /* * Other module variables */ int port_override_name, transport_override_name, domain_prefix_name, domain_suffix_name, domain_replacement_name, send_socket_name; /* * Exported functions */ static cmd_export_t cmds[] = { {"dp_can_connect", (cmd_function)dp_can_connect, 0, 0, 0, REQUEST_ROUTE}, {"dp_apply_policy", (cmd_function)dp_apply_policy, 0, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &db_url.s }, {"dp_table", STR_PARAM, &domainpolicy_table.s }, {"dp_col_rule", STR_PARAM, &domainpolicy_col_rule.s }, {"dp_col_type", STR_PARAM, &domainpolicy_col_type.s }, {"dp_col_att", STR_PARAM, &domainpolicy_col_att.s }, {"dp_col_val", STR_PARAM, &domainpolicy_col_val.s }, {"port_override_avp", STR_PARAM, &port_override_avp.s }, {"transport_override_avp", STR_PARAM, &transport_override_avp.s }, {"domain_prefix_avp", STR_PARAM, &domain_prefix_avp.s }, {"domain_suffix_avp", STR_PARAM, &domain_suffix_avp.s }, {"domain_replacement_avp", STR_PARAM, &domain_replacement_avp.s }, {"send_socket_avp", STR_PARAM, &send_socket_avp.s }, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* * Module interface */ struct module_exports exports = { "domainpolicy", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ destroy, /* destroy function */ child_init /* per-child init function */ }; static int mod_init(void) { int ver; LM_INFO("initializing...\n"); init_db_url( db_url , 0 /*cannot be null*/); domainpolicy_table.len = strlen(domainpolicy_table.s); domainpolicy_col_rule.len = strlen(domainpolicy_col_rule.s); domainpolicy_col_type.len = strlen(domainpolicy_col_type.s); domainpolicy_col_att.len = strlen(domainpolicy_col_att.s); domainpolicy_col_val.len = strlen(domainpolicy_col_val.s); LM_INFO("check for DB module\n"); /* Check if database module has been loaded */ if (domainpolicy_db_bind(&db_url)<0) { LM_ERR("no database module loaded!" " Please make sure that a DB module is loaded first\n"); return -1; } LM_INFO("update length of module variables\n"); /* Update length of module variables */ port_override_avp.len = strlen(port_override_avp.s); transport_override_avp.len = strlen(transport_override_avp.s); domain_prefix_avp.len = strlen(domain_prefix_avp.s); domain_suffix_avp.len = strlen(domain_suffix_avp.s); domain_replacement_avp.len = strlen(domain_replacement_avp.s); send_socket_avp.len = strlen(send_socket_avp.s); /* Check table version */ ver = domainpolicy_db_ver(&db_url, &domainpolicy_table); if (ver < 0) { LM_ERR("failed to query table version\n"); return -1; } else if (ver < DOMAINPOLICY_TABLE_VERSION) { LM_ERR("invalid table version of domainpolicy table\n"); return -1; } /* Assign AVP parameter names */ LM_INFO("AVP\n"); if (parse_avp_spec(&port_override_avp, &port_override_name) < 0) { LM_ERR("invalid port_override_avp!\n"); return -1; } if (parse_avp_spec(&transport_override_avp, &transport_override_name) < 0) { LM_ERR("invalid transport_override_avp!\n"); return -1; } if (parse_avp_spec(&domain_prefix_avp, &domain_prefix_name) < 0) { LM_ERR("invalid domain_prefix_avp!\n"); return -1; } if (parse_avp_spec(&domain_suffix_avp, &domain_suffix_name) < 0) { LM_ERR("invalid domain_suffix_avp!\n"); return -1; } if (parse_avp_spec(&domain_replacement_avp, &domain_replacement_name) < 0) { LM_ERR("invalid domain_replacement_avp!\n"); return -1; } if (parse_avp_spec(&send_socket_avp, &send_socket_name) < 0) { LM_ERR("invalid send_socket_avp!\n"); return -1; } return 0; } static int child_init(int rank) { LM_DBG("initializing\n"); /* Check if database is needed by child */ if (rank!=PROC_MAIN && rank!=PROC_TCP_MAIN) { if (domainpolicy_db_init(&db_url)<0) { LM_ERR("unable to connect to the database\n"); return -1; } } return 0; } static void destroy(void) { /* Destroy is called from the main process only, * there is no need to close database here because * it is closed in mod_init already */ } opensips-2.2.2/modules/domainpolicy/domainpolicy_mod.h000066400000000000000000000030571300170765700232260ustar00rootroot00000000000000/* * Domain module headers * * Copyright (C) 2002-2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DOMAINPOLICY_MOD_H #define DOMAINPOLICY_MOD_H #include "../../db/db.h" #include "../../str.h" #include "../../usr_avp.h" /* * Module parameters variables */ extern str domainpolicy_table; /* Domainpolicy table name */ extern str domainpolicy_col_rule; /* Rule column name */ extern str domainpolicy_col_type; /* Type column name */ extern str domainpolicy_col_att; /* Attribute column name */ extern str domainpolicy_col_val; /* Value column name */ /* * Other module variables */ extern int port_override_name, transport_override_name, domain_prefix_name, domain_suffix_name, domain_replacement_name, send_socket_name, target_name; extern str domain_replacement_avp; #endif /* DOMAINPOLICY_MOD_H */ opensips-2.2.2/modules/drouting/000077500000000000000000000000001300170765700166665ustar00rootroot00000000000000opensips-2.2.2/modules/drouting/Makefile000066400000000000000000000002651300170765700203310ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=drouting.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/drouting/README000066400000000000000000001463011300170765700175530ustar00rootroot00000000000000Dynamic Routing Module Bogdan-Andrei Iancu www.opensips-solutions.com Edited by Bogdan-Andrei Iancu Edited by Anca-Maria Vamanu Copyright © 2005-2008 Voice Sistem SRL Copyright © 2009-2012 www.opensips-solutions.com Revision History Revision $Revision: 8834 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Introduction 1.1.2. Features 1.1.3. Performance 1.1.4. Dynamic Routing Concepts 1.1.5. Routing Rule Processing 1.1.6. Probing and Disabling destinations 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url(str) 1.3.2. drd_table(str) 1.3.3. drr_table(str) 1.3.4. drg_table(str) 1.3.5. drc_table(str) 1.3.6. ruri_avp (str) 1.3.7. gw_id_avp (str) 1.3.8. gw_priprefix_avp (str) 1.3.9. rule_id_avp (str) 1.3.10. rule_prefix_avp (str) 1.3.11. carrier_id_avp (str) 1.3.12. gw_sock_avp (str) 1.3.13. rule_attrs_avp (str) 1.3.14. define_blacklist (str) 1.3.15. default_group (int) 1.3.16. force_dns (int) 1.3.17. persistent_state (int) 1.3.18. no_concurrent_reload (int) 1.3.19. probing_interval (integer) 1.3.20. probing_method (string) 1.3.21. probing_from (string) 1.3.22. probing_reply_codes (string) 1.3.23. use_domain (int) 1.3.24. drg_user_col (str) 1.3.25. drg_domain_col (str) 1.3.26. drg_grpid_col (str) 1.3.27. use_partitions (int) 1.3.28. db_partitions_url (int) 1.3.29. db_partitions_table (int) 1.3.30. partition_id_pvar (pvar) 1.4. Exported Functions 1.4.1. do_routing([part_and_or_groupID], [flags], [gw_whitelist], [rule_attrs_pvar], [gw_attrs_pvar], [carrier_attrs_pvar]) 1.4.2. route_to_carrier(part_and_or_carrier_id, [gw_attrs_pvar], [carrier_attrs_pvar]) 1.4.3. route_to_gw(gw_id, [gw_attrs_pvar]) 1.4.4. use_next_gw([partition','] [rule_attrs_pvar], [gw_attrs_pvar], [carrier_attrs_pvar])/next_routing() 1.4.5. goes_to_gw([partition','] [type], [flags], [gw_attrs_pvar]) 1.4.6. is_from_gw([partition','] [type], [flag], [gw_attrs_pvar]) 1.4.7. dr_is_gw([partition,] src_avp, [type], [flag], [gw_attrs_pvar]) 1.4.8. dr_disable() 1.5. Exported MI Functions 1.5.1. dr_reload 1.5.2. dr_gw_status 1.5.3. dr_carrier_status 1.5.4. dr_reload_status 1.5.5. dr_number_routing 1.6. Exported Events 1.6.1. E_DROUTING_STATUS 1.7. Installation 2. Developer Guide List of Examples 1.1. Set db_url parameter 1.2. Set drd_table parameter 1.3. Set drr_table parameter 1.4. Set drg_table parameter 1.5. Set drc_table parameter 1.6. Set ruri_avp parameter 1.7. Set gw_id_avp parameter 1.8. Set gw_priprefix_avp parameter 1.9. Set rule_id_avp parameter 1.10. Set rule_prefix_avp parameter 1.11. Set carrier_id_avp parameter 1.12. Set gw_sock_avp parameter 1.13. Set rule_attrs_avp parameter 1.14. Set define_blacklist parameter 1.15. Set default_group parameter 1.16. Set force_dns parameter 1.17. Set the persistent_state parameter 1.18. Set no_concurrent_reload parameter 1.19. Set probing_interval parameter 1.20. Set probing_method parameter 1.21. Set probing_from parameter 1.22. Set probing_reply_codes parameter 1.23. Set use_domain parameter 1.24. Set drg_user_col parameter 1.25. Set drg_domain_col parameter 1.26. Set drg_grpid_col parameter 1.27. Set use_partitions parameter 1.28. Set db_partitions_url parameter 1.29. Set db_partitions_table parameter 1.30. Set partition_id_pvar parameter 1.31. do_routing usage 1.32. route_to_carrier usage when use_partitions parameter is 0 1.33. route_to_carrier usage when use_partitions parameter is 1 1.34. route_to_carrier usage when use_partitions parameter is 1 with pseudovariables 1.35. route_to_gw usage when use_partition parameter is 0 1.36. route_to_gw usage when use_partition parameter is 1 1.37. use_next_gw usage 1.38. use_next_gw usage when use_partition parameter is 1 1.39. goes_to_gw usage when use_partitions parameter is 0 1.40. goes_to_gw usage, when use_partitions parameter is 1 1.41. is_from_gw usage when use_partitions is 0 1.42. is_from_gw usage when use_partitions is 1 1.43. dr_is_gw usage when use_partitions is 0 1.44. dr_is_gw usage when use_partitions is 1 1.45. dr_disable() usage when use_partitions is 0 1.46. dr_disable() usage when use_partitions is 1 1.47. dr_gw_status usage when use_partitions is set to 0 1.48. dr_gw_status usage when use_partitionsis set to 1 1.49. dr_carrier_status usage when use_partitions is 0 1.50. dr_carrier_status usage when use_partitions is 1 1.51. dr_reload_status usage when use_partitions is 0 1.52. dr_reload_status usage when use_partitions is 1 Chapter 1. Admin Guide 1.1. Overview 1.1.1. Introduction Dynamic Routing is a module for selecting (based on multiple criteria) the best gateway/destination to be used for delivering a certain call. Least Cost Routing (LCR) is a special case of dynamic routing - when the rules are ordered based on costs. Dynamic Routing comes with many features regarding routing rule selection: * prefix based * caller/group based * time based * priority based , processing : * stripping and prefixing * default rules * inbound and outbound processing * script route triggering and failure handling: * serial forking * weight based GW selection * random GW selection * GW probing for crashes 1.1.2. Features The dynamic routing implementation for OpenSIPS is designed with the following properties: * The routing info (destinations, carriers, rules, groups) is stored in a database and loaded into memory at start up time; reload at runtime via a Management Interface command. * weight-based or random selection of the destinations (from a rule or from a carrier), failure detection of gateways (with switching to next available gateway). * able to handle large volume of routing info (10M of rules) with minimal speed/time and memory consumption penalties * script integration - Pseudo-variable support in functions; scripting route triggering when rules are matched * bidirectional behavior - inbound and outbound processing (strip and prefixing when sending and receiving from a destination/GW) * blacklisting - the module allows definition of blacklists based on the destination IPs. This blacklists are to be used to prevent malicious forwarding to GWs (based on DNS lookups) when the script logic does none-GE forwarding (like foreign domains). * loading routing information from multiple databases - the gateways, rules, groups and carriers can be grouped by partitions, and each partition may be loaded from different databases/tables. This makes the routing process partition based. In order to be able to use a table from a partition, its name must be found in the "version" table belonging to the database defined in the partition's db_url. 1.1.3. Performance There were several tests performed regarding the performance of the module when dealing with a large number of routing rules. The tests were performed with a set of 383000 rules and measured: * time to load from DB * used shared memory The time to load was varying between 4 seconds and 8 seconds, depending of the caching of the DB client - the first load was the slowest (as the DB query hits the disk drive); the following are faster as data is already cached in the DB client. So technically speaking, the time to load (without the time to query which is DB type dependent) is ~4 seconds After loading the data into shared memory ~ 96M of memory were used exclusively for the DR data. 1.1.4. Dynamic Routing Concepts DR engine uses several concepts in order to define how the routing should be done (describing all the dependencies between destinations and routing rules). 1.1.4.1. Destination/Gateways These are the end SIP entities where actually the traffic needs to be sent after routing. They are stored in a table called “dr_gatewaysâ€. Gateway addresses are stored in a separate table because of the need to access them independent of Dynamic Routing processing (e.g., adding/ removing gateway PRI prefix before/after performing other operation -- receiving/relaying to gateway). In DR, a gateway is defined by: * id (string) * SIP address (SIP URI) * type (number to allow to group GW based on purpose, like inbound, outbound, etc) * strip value (number of digits) from dialled number * prefix (string) to be added to dialled number * attributes (not used by DR engine, but only pushed to script level when routing to this GW) * probing mode (how the GW should be probed at SIP level - see the probing chapter) The Gateways are to be used from the routing rule or from the carrier definition. They are all the time referred by their ID. 1.1.4.2. Carriers The carrier concept is used if you need to group gateways in order to have a better control on how the GWs will be used by DR rules; like in what order the GWs will be used. Basically, a carrier is a set of gateways which have its own sorting algorithm and its own attribute string. They are by default defined in the “dr_carriers†table. In DR, a carrier is defined by: * id (string) * list of gateways with/without weights (string) (Ex:“gw1=10,gw4=10†or “gw1,gw2†* flags : 0x1 - use weight for sorting the list and not definition order; 0x2 - use only the first gateway from the carrier (depending on the sorting); 0x4 - disable the usage of this carrier * attributes (not used by DR engine, but only pushed to script level when routing to this carrier) The Carriers are to be used only from the routing rule definition. They are all the time referred by their ID. 1.1.4.3. Routing Rules These are the actual rules which control the routing. Using different criterias (prefix, time, priority, etc), they will decide to which gateways the call will be sent. Default name for the table storing rule definitions is “dr_rulesâ€. In DR, a carrier is defined by: * group (list of numbers) - rules can be grouped (a rule may belong to multiple groups in the same time ) and you can use only a certain group at a point; like having a “premium†or “standard†or “interstate†or “intrastate†groups of rules to be used in different cases * prefix (string with digits only) - prefix to be used for matching this rule (longest prefix matching) * time validity (time recurrence string) - when this rule is valid from time point of view (see RFC 2445) * priority (number) - priority of the rule - higher value, higher priority (see rule section alg) * script route ID (string) - if defined, then execute the route with the specified ID when this rule is matched. That's it, a route which can be used to perform custom operations on message. NOTE that no modification is performed at signaling level and you must NOT do any signaling operations in that script route * list of GWs/carriers (string) - a comma separated list of gateways or carriers (defined by IDs) to be used for this rule; the carrier IDs are prefixed with “#†sign. For each ID (GW or carrier) you may specify a weight. For how this list will be interpreted (as order) see the rule selection section. Example of list: “gw1,gw4,#cr3†or “gw1=10,gw4=10,#cr3=80†* attributes (not used by DR engine, but only pushed to script level when this rule matched and been used) More on time recurrence: * A date-time expression that defines the time recurrence to be matched for current rule. Time recurrences are based closely on the recurring time intervals from the Internet Calendaring and Scheduling Core Object Specification (calendar COS), RFC 2445. The set of attributes used in routing rule specification is a subset of time recurrence attributes. * The value stored in database has the format of: ||||||||| * When an attribute is not specified, the corresponding place must be left empty, whenever another attribute that follows in the list has to be specified. 1.1.5. Routing Rule Processing The module can be used to find out which is the best gateway to use for new calls terminated to PSTN. The algorithm to select the rule is as follows: * the module discovers the routing group of the originating user. This step is skipped if a routing group is passed from the script as parameter. * once the group is known, in the subset of the rules for this group the module looks for the one that matches the destination based on "prefix" column. The set of rules with the longest prefix is chosen. If no digit from the prefix matches, the default rules are used (rules with no prefix) * within the set of rules is applied the time criteria, and the rule which has the highest priority and matches the time criteria is selected to drive the routing. * Once found the rule, it may contain a route ID to execute. If a certain flag is set, then the processing is stopped after executing the route block. * The rule must contain a chain of gateways and carriers. The module will execute serial forking for each address in the chain (ordering is either done by simply using the definition order or it may weight-based - weight selection must be enabled). The next address in chain is used only if the previously has failed. * With the right gateway address found, the prefix (PRI) of the gateway is added to the request URI and then the request is forwarded. If no rule is found to match the selection criteria an default action must be taken (e.g., error response sent back). If the gateway in the chain has no prefix the request is forwarded without adding any prefix to the request URI. 1.1.6. Probing and Disabling destinations The module has the capability to monitor the status of the destinations by doing SIP probing (sending SIP requests like OPTIONS). For each destination, you can configure what kind of probing should be done (probe_mode column): * (0) - no probing at all; * (1) - probing only when the destination is in disabled mode (disabling via MI command will completely stop the probing also). The destination will be automatically re-enabled when the probing will succeed next time; * (2) - probing all the time. If disabled, the destination will be automatically re-enabled when the probing will succeed next time; A destination can become disabled in two ways: * script detection - by calling from script the dr_disable() function after trying the destination. In this case, if probing mode for the destination is (1) or (2), the destination will be automatically re-enabled when the probing will succeed. * MI command - by calling the dr_gw_status MI command for disabling (on demand) the destination. If so, the probing and re-enabling of this destination will be completly disabled until you re-enable it again via MI command - this is designed to allow controlled and complete disabling of some destination during maintenance. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * a database module. * tm module. 1.2.2. External Libraries or Applications * none. 1.3. Exported Parameters 1.3.1. db_url(str) The database url. Default value is “NULLâ€. Example 1.1. Set db_url parameter ... modparam("drouting", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") ... 1.3.2. drd_table(str) The name of the db table storing gateway addresses. Default value is “dr_gatewaysâ€. Example 1.2. Set drd_table parameter ... modparam("drouting", "drd_table", "dr_gateways") ... 1.3.3. drr_table(str) The name of the db table storing routing rules. Default value is “dr_rulesâ€. Example 1.3. Set drr_table parameter ... modparam("drouting", "drr_table", "rules") ... 1.3.4. drg_table(str) The name of the db table storing groups. Default value is “dr_groupsâ€. Example 1.4. Set drg_table parameter ... modparam("drouting", "drg_table", "groups") ... 1.3.5. drc_table(str) The name of the db table storing definitions of the carriers that will be used directly by the routing rules. Default value is “dr_carriersâ€. Example 1.5. Set drc_table parameter ... modparam("drouting", "drc_table", "my_dr_carriers") ... 1.3.6. ruri_avp (str) The name of the avp for storing Request URIs to be later used (alternative destiantions for the current one). Default value is “$avp(___dr_ruri__)†if use_partitions parameter is 0 or “$avp(___dr_ruri__partition_name)†where partition_name is the name of the partition containing the AVP (as fetched from the database) if use_partitions parameter is 1. Example 1.6. Set ruri_avp parameter ... modparam("drouting", "ruri_avp", '$avp(dr_ruri)') modparam("drouting", "ruri_avp", '$avp(33)') ... 1.3.7. gw_id_avp (str) The name of the avp for storing the id of the current selected gateway/destination - once a new destination is selected (via the use_next_gw() function), the AVP will be updated with the ID of the new selected gateway/destination. Default value is “$avp(___dr_gw_id__)†if use_partitions parameter is 0 or “$avp(___dr_gw_id__partition_name)†where partition_name is the name of the partition containing the AVP (as fetched from the database) if use_partitions parameter is 1. Example 1.7. Set gw_id_avp parameter ... modparam("drouting", "gw_id_avp", '$avp(gw_id)') modparam("drouting", "gw_id_avp", '$avp(334)') ... 1.3.8. gw_priprefix_avp (str) The name of the avp for storing the PRI prefix of the current selected destination/gateway - once a new destination is selected (via the use_next_gw() function), the AVP will be updated with the PRI prefix of the new used destination. Default value is “NULLâ€. Example 1.8. Set gw_priprefix_avp parameter ... modparam("drouting", "gw_priprefix_avp", '$avp(gw_priprefix)') ... 1.3.9. rule_id_avp (str) The name of the avp for storing the id of the current matched routing rule (see dr_rules table). Default value is “NULLâ€. Example 1.9. Set rule_id_avp parameter ... modparam("drouting", "rule_id_avp", '$avp(rule_id)') modparam("drouting", "rule_id_avp", '$avp(335)') ... 1.3.10. rule_prefix_avp (str) The actual prefix that matched the routing rule (the part from RURI username that matched the routing rule). Default value is “NULLâ€. Example 1.10. Set rule_prefix_avp parameter ... modparam("drouting", "rule_prefix_avp", '$avp(dr_prefix)') ... 1.3.11. carrier_id_avp (str) AVP to be populate with the ID string for the carrier the current GW belongs to. Default value is “NULLâ€. Example 1.11. Set carrier_id_avp parameter ... modparam("drouting", "carrier_id_avp", '$avp(carrier_id)') ... 1.3.12. gw_sock_avp (str) The name of the avp for storing sockets for alternative destinations defined by ruri_avp. Default value is “$avp(___dr_sock__)†if use_partitions parameter is 0 or “$avp(___dr_sock__partition_name)†where partition_name is the name of the partition containing the AVP (as fetched from the database) if use_partitions parameter is 1. Example 1.12. Set gw_sock_avp parameter ... modparam("drouting", "gw_sock_avp", '$avp(dr_sock)') modparam("drouting", "gw_sock_avp", '$avp(77)') ... 1.3.13. rule_attrs_avp (str) The name of the avp for storing rule attrs in case they are requested at least once in the script. Default value is “$avp(___dr_ru_att__)†if use_partitions parameter is 0 or “$avp(___dr_ru_att__partition_name)†where partition_name is the name of the partition containing the AVP (as fetched from the database) if use_partitions parameter is 1. Example 1.13. Set rule_attrs_avp parameter ... modparam("drouting", "rule_attrs_avp", '$avp(dr_rule_attr)') modparam("drouting", "rule_attrs_avp", '$avp(11)') ... 1.3.14. define_blacklist (str) Defines a blacklist based on a list of GW types - the list will contain the IPs (no port, all protocols) of the GWs with the specified types. Multiple instances of this param are allowed. Default value is “NULLâ€. Example 1.14. Set define_blacklist parameter ... modparam("drouting", "define_blacklist", 'bl_name= 3,5,25,23') modparam("drouting", "define_blacklist", 'list= 4,2') ... 1.3.15. default_group (int) Group to be used if the caller (FROM user) is not found in the GROUP table. Default value is “NONEâ€. Example 1.15. Set default_group parameter ... modparam("drouting", "default_group", 4) ... 1.3.16. force_dns (int) Force DNS resolving of GW/destination names (if not IPs) during startup. If not enabled, the GW name will be blindly used during routing. Default value is “1 (enabled)â€. Example 1.16. Set force_dns parameter ... modparam("drouting", "force_dns", 0) ... 1.3.17. persistent_state (int) Specifies whether the state column should be loaded at startup and flushed during runtime or not. Default value is “1†(enabled). Example 1.17. Set the persistent_state parameter ... # disable all DB operations with the state of a gateway modparam("drouting", "persistent_state", 0) ... 1.3.18. no_concurrent_reload (int) If enabled, the module will not allow do run multiple dr_reload MI commands in parallel (with overlapping) Any new reload will be rejected (and discarded) while an existing reload is in progress. If you have a large routing set (millions of rules/prefixes), you should consider disabling concurrent reload as they will exhaust the shared memory (by reloading into memory, in the same time, multiple instances of routing data). Default value is “0 (disabled)â€. Example 1.18. Set no_concurrent_reload parameter ... # do not allow parallel reload operations modparam("drouting", "no_concurrent_reload", 1) ... 1.3.19. probing_interval (integer) How often (in seconds) the probing of a destination should be done. If set to 0, the probing will be disabled as functionality (for all destinations) Default value is “30â€. Example 1.19. Set probing_interval parameter ... modparam("drouting", "probing_interval", 60) ... 1.3.20. probing_method (string) The SIP method to be used for the probing requests. Default value is “"OPTIONS"â€. Example 1.20. Set probing_method parameter ... modparam("drouting", "probing_method", "INFO") ... 1.3.21. probing_from (string) The FROM SIP URI to be advertised in the SIP probing requests. Default value is “"sip:prober@localhost"â€. Example 1.21. Set probing_from parameter ... modparam("drouting", "probing_from", "sip:pinger@192.168.2.10") ... 1.3.22. probing_reply_codes (string) A comma separted list of SIP reply codes. The codes defined here will be considered as valid reply codes for probing messages, apart for 200. Default value is “NULLâ€. Example 1.22. Set probing_reply_codes parameter ... modparam("drouting", "probing_reply_codes", "501, 403") ... 1.3.23. use_domain (int) Flag to configure whether to use domain match when querying database for user's routing group. Default value is “1â€. Example 1.23. Set use_domain parameter ... modparam("drouting", "use_domain", 0) ... 1.3.24. drg_user_col (str) The name of the column in group db table where the username is stored. Default value is “usernameâ€. Example 1.24. Set drg_user_col parameter ... modparam("drouting", "drg_user_col", "user") ... 1.3.25. drg_domain_col (str) The name of the column in group db table where the domain is stored. Default value is “domainâ€. Example 1.25. Set drg_domain_col parameter ... modparam("drouting", "drg_domain_col", "host") ... 1.3.26. drg_grpid_col (str) The name of the column in group db table where the group id is stored. Default value is “groupidâ€. Example 1.26. Set drg_grpid_col parameter ... modparam("drouting", "drg_grpid_col", "grpid") ... 1.3.27. use_partitions (int) Flag to configure whether to use partitions for routing. If this flag is set then the db_partitions_url and db_partitions_table variables become mandatory. Default value is “0â€. Example 1.27. Set use_partitions parameter ... modparam("drouting", "use_partitions", 1) ... 1.3.28. db_partitions_url (int) The url to the database containing partition-specific information. (partition-specific information includes partition name, url to the database where information about the partition is preserved, the names of the tables in which it is preserved and the AVPs that can be accessed using the .cfg script). The use_partitions parameter must be set to 1. Default value is “"NULL"â€. Example 1.28. Set db_partitions_url parameter ... modparam("drouting", "db_partitions_url", "mysql://user:password@localho st/opensips_partitions") ... 1.3.29. db_partitions_table (int) The name of the table containing partition definitions. To be used with use_partitions and db_partitions_url. Default value is “dr_partitionsâ€. Example 1.29. Set db_partitions_table parameter ... modparam("drouting", "db_partitions_table", "partition_defs") ... 1.3.30. partition_id_pvar (pvar) Variable which will store the name of the name partition when wildcard(*) operatior is used. Use_partitions must be set in order to use this parameter. NOTE: The variable must be WRITABLE! Default value is “null(not used)â€. Example 1.30. Set partition_id_pvar parameter ... modparam("drouting", "partition_id_pvar", "$var(matched_partition)") ... 1.4. Exported Functions 1.4.1. do_routing([part_and_or_groupID], [flags], [gw_whitelist], [rule_attrs_pvar], [gw_attrs_pvar], [carrier_attrs_pvar]) Function to trigger routing of the message according to the rules in the database table and the configured parameters. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE. If you set use_partitions to 1 the part_or_groupID parameter becomes mandatory. All parameters are optional. Any of them may be ignored, provided the necessary separation marks "," are properly placed. * part_and_or_groupID - Specifies the group of the caller for routing purposes. Depending on the value of the use_partitions parameter, it contains: + the routing group the caller belongs to if use_partitions is 0 - this may be a statical numerical value or an AVP specification (value must be numerical type, string types are ignored!). If none specified the function will automatically try to query the dr_group table to get this information + the partition and routing group the caller belongs to, the format is: "partition':'[groupID]" if use_partitions parameter is 1 - both the partition name and the groupId may be statical values or AVP specifications. If no group is specified the function will try to query the dr_group table for the given partition to get this information. If * (wildcard) operator is used all partitions shall be checked. * flags - Controls the behavior of the function. Possible flags are: + W - Instead of using the destination (from the rule definition) in the given order, sort them based on their weight. + F - Enable rule fallback; normally the engine is using a single rule for routing a call; by setting this flag, the engine will fallback and use rules with less priority or shorter prefix when all the destination from the current rules failed. + L - Do strict length matching over the prefix - actually DR engine will do full number matching and not prefix matching anymore. + C - Only check if the dialed number matches any routing rule, without loading / applying any routing info (no GW is set, the RURI is not altered) * gw_whitelist - a comma separated white list of gateways. This will force routing over, at most, this list of carriers or gateways (in other words, the whitelist will be intersected with the results of the search through the rules). * rule_attrs_pvar (output, optional)- a writable pseudo-variable which will be populated with the attributes of the matched dynamic routing rule. * gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. * carrier_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched carrier. Example 1.31. do_routing usage ... # all groups, sort on order, use_partitions is 0 do_routing(); ... # all groups, sort on order, use_partitions is 1, route by partition nam ed "part" do_routing("part:"); ... # group id 0, sort on order, use_partitions is 0 do_routing("0"); ... # group id 0, sort on order, use_partitions is 1, route by partition nam ed "part" do_routing("part:0"); ... # group id from $avp(10), sort on order, use_partitions is 0 do_routing("$avp(10)"); ... # all groups, sort on weights, use_partitions is 0 do_routing("", "W"); ... # all groups, use_partitions is 1, partition and group supplied by AVPs, do strict length matching do_routing("$avp(partition):$avp(grp)","L") ... # group id 2, sort on order, fallback rule and also return the gateway a ttributes do_routing("2", "F", , , "$var(gw_attributes)"); ... 1.4.2. route_to_carrier(part_and_or_carrier_id, [gw_attrs_pvar], [carrier_attrs_pvar]) Function to trigger the direct routing to a given carrier. In this case the routing is not done prefix based, but carrier based (call will be sent to the GWs of that carrier, based on carrier policy). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE.. If you set use_partitions parameter to 1 you must supply the partition in which the carrier has been defined. * part_and_or_carrier_id (mandatory): + the ID (name) of the carrier to be used, if use_partitions parameter is 0; pseudo-variables are accepted. + the partition and carrier to be used, if use_partitions parameter is 1. The format is "partition_name':'carrierId"; pseudo-variables are accepted. * gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the currently matched gateway of this carrier. * carrier_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of this carrier. Example 1.32. route_to_carrier usage when use_partitions parameter is 0 ... if ( route_to_carrier("my_top_carrier", , "$var(carrier_att)") ) { xlog("Routing to \"my_top_carrier\" - $var(carrier_att)\n"); t_on_failure("next_gw"); t_relay(); exit; } ... Example 1.33. route_to_carrier usage when use_partitions parameter is 1 ... if ( route_to_carrier("my_partition:my_top_carrier", , "$var(carrier_att )") ) { xlog("Routing to \"my_top_carrier\" - $var(carrier_att)\n"); t_on_failure("next_gw"); t_relay(); exit; } ... Example 1.34. route_to_carrier usage when use_partitions parameter is 1 with pseudovariables ... if ( route_to_carrier("$var(my_partition):$var(carrierId)") ) { xlog("Routing to \"my_top_carrier\" - $var(carrier_att)\n"); t_on_failure("next_gw"); t_relay(); exit; } ... 1.4.3. route_to_gw(gw_id, [gw_attrs_pvar]) Function to trigger the direct routing to a given gateway (or list of gateways). Attributes and per-gw processing will be available. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE. If you set use_partitions parameter to 1 you must supply the partition in which the gateway has been defined. * gw_id (mandatory) - the list of gateways to be used. + a comma separated list of gateway ID's to be used, if no use_partition parameter is 0. Pseudo-variables are accepted. + the desired partition, followed by a comma separated list of gateway ID's from that partition to be used, if use_partition parameter is 1. The format is: "partition_name':'gwId1, gwId2, gwId3". Pseudo-variables are accepted. * gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the currently matched gateway. Example 1.35. route_to_gw usage when use_partition parameter is 0 ... if ( route_to_gw("gw_europe") ) { t_relay(); exit; } ... if ( route_to_gw("gw1,gw2,gw3", "$var(gw_attrs)") ) { xlog("Relaying to first gateway from our list - $var(gw_attrs)\n "); t_relay(); exit; } ... Example 1.36. route_to_gw usage when use_partition parameter is 1 ... if ( route_to_gw("my_partition:gw_europe") ) { t_relay(); exit; } ... if ( route_to_gw("my_partition:gw1,gw2,gw3", "$var(gw_attrs)") ) { xlog("Relaying to first gateway from our list - $var(gw_attrs)\n "); t_relay(); exit; } ... 1.4.4. use_next_gw([partition','] [rule_attrs_pvar], [gw_attrs_pvar], [carrier_attrs_pvar])/next_routing() The function takes the next available destination (set by do_routing, as alternative destinations) and pushes it into the RURI. Note that the function just sets the RURI (nothing more). If a new RURI is set, the used destination is removed from the pending set of alternative destinations. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE. The function returns true only if a new RURI was set. False is returned is no other alternative destinations are found or in case of an internal processing error. It may take the following optional parameters: If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateways have been defined. * partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) It is the partition in which the gateways have been defined. * rule_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched dynamic routing rule. * gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. * carrier_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched carrier. Example 1.37. use_next_gw usage ... if (use_next_gw()) { t_relay(); exit; } ... # Also fetch the carrier attributes, if any if (use_next_gw(, , "$var(carrier_attrs)")) { xlog("Carrier attributes of current gateway: $var(carrier_attrs) \n"); t_relay(); exit; } ... Example 1.38. use_next_gw usage when use_partition parameter is 1 ... if (use_next_gw("my_partition")) { t_relay(); exit; } ... # Also fetch the carrier attributes, if any if (use_next_gw("my_partition", , "$var(carrier_attrs)")) { xlog("Carrier attributes of current gateway: $var(carrier_attrs) \n"); t_relay(); exit; } ... 1.4.5. goes_to_gw([partition','] [type], [flags], [gw_attrs_pvar]) Function returns true if the destination of the current request (destination URI or Request URI) points (as IP) to one of the gateways. There no DNS lookups done if the domain part of the URI is not an IP. This function does not change anything in the message. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateways have been defined. If use_partitions parameter is 0 all parameters are optional. Any of them may be ignored, provided the necessary separation marks "," are properly placed. * partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) - the name of the partition containing the gateway/destination to be checked. * type (optional) - GW/destination type to be checked; when omitting this parameter or specifying a negative value i.e. "-1", matching will be done against all groups (in a given partition if use_partition parameter is 1; if use_partitions is 1 the partition being mandatory at this point, it is not possible to do matching against all the partitions) * flags (optional) - what operations should be performed when a GW matches: + 's' (Strip) - apply to the username of RURI the strip defined by the GW + 'p' (Prefix) - apply to the username of RURI the prefix defined by the GW + 'i' (Gateway ID) - return the gateway id into gw_id_avp AVP + 'n' (Ignore port) - ignores port number during matching + 'c' (Carrier ID) - return the carrier id into carrier_id_avp AVP * gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. Example 1.39. goes_to_gw usage when use_partitions parameter is 0 ... if (goes_to_gw("1", , "$var(gw_attrs)")) { sl_send_reply("403","Forbidden"); exit; } ... Example 1.40. goes_to_gw usage, when use_partitions parameter is 1 ... if (goes_to_gw("my_partition", "1", , "$var(gw_attrs)")) { sl_send_reply("403","Forbidden"); exit; } ... 1.4.6. is_from_gw([partition','] [type], [flag], [gw_attrs_pvar]) The function checks if the sender of the message (its source IP) is a gateway from a certain group. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateways have been defined. If use_partitions parameter is 0 all parameters are optional. Any of them may be ignored, provided the necessary separation marks "," are properly placed. * partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) - Partition containing the destination/gw to be checked. If *(wildcard) operator is used all partitions shall be checked. * type (optional) - GW/destination type to be checked; when omitting this parameter or specifying a negative value i.e. "-1", matching will be done against all groups * flags (optional) - what operations should be performed when a GW matches: + 's' (Strip) - apply to the username of RURI the strip defined by the GW + 'p' (Prefix) - apply to the username of RURI the prefix defined by the GW + 'i' (Gateway ID) - return the gateway id into gw_id_avp AVP + 'n' (Ignore port) - ignores port number during matching + 'c' (Carrier ID) - return the carrier id into carrier_id_avp AVP * gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. Example 1.41. is_from_gw usage when use_partitions is 0 ... if (is_from_gw("3","1") { } ... Example 1.42. is_from_gw usage when use_partitions is 1 ... if (is_from_gw("my_partition","3","1") { } ... 1.4.7. dr_is_gw([partition,] src_avp, [type], [flag], [gw_attrs_pvar]) The function checks if the ip address in pvar src_pv is a gateway from a certain group. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE and EVENT_ROUTE. If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateways have been defined. Meaning of the parameters is as follows: * partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) - Partition containing the destinations/gateways to be checked. * src_avp (mandatory) - avp containing a SIP URI. Does not support other OpenSIPS pseudo-variables. * type (optional) - GW/destination type to be checked; when omitting this parameter or specifying a negative value i.e. "-1", matching will be done against all groups * flags (optional) - what operations should be performed when a GW matches: + 's' (Strip) - apply to the username of RURI the strip defined by the GW + 'p' (Prefix) - apply to the username of RURI the prefix defined by the GW + 'i' (Gateway ID) - return the gateway id into gw_id_avp pvar + 'n' (Ignore port) - ignores port number + 'c' (Carrier ID) - return the carrier id into carrier_id_avp AVP * gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. Example 1.43. dr_is_gw usage when use_partitions is 0 ... if (dr_is_gw("$avp(uac)","3") { } ... Example 1.44. dr_is_gw usage when use_partitions is 1 ... if (dr_is_gw("my_partition","$avp(uac)","3") { } ... 1.4.8. dr_disable() Marks as disabled the last destination that was used for the current call. The disabling done via this function will prevent the destination to be used for usage from now on. The probing mechanism can re-enable this peer (see the probing section in the beginning) This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateway to be disabled is defined. * partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) - Partition containing the destination/gateway to be disabled. Example 1.45. dr_disable() usage when use_partitions is 0 ... if (t_check_status("(408)|(5[0-9][0-9])")) { dr_disable(); } ... Example 1.46. dr_disable() usage when use_partitions is 1 ... if (t_check_status("(408)|(5[0-9][0-9])")) { dr_disable("my_partition"); } ... 1.5. Exported MI Functions 1.5.1. dr_reload Command to reload routing rules from database. If use_partitions is set to 1 you can reload just a partition given a parameter, if no parameter is supplied then all the partitions will be reloaded. If use_partitions is 0 it takes no parameter. MI FIFO Command Format: :dr_reload:fifo_reply partition_name (optional) _empty_line_ 1.5.2. dr_gw_status Gets or sets the status (enabled or disabled) of a gateway. The function may take from 0 to 3 parameters. * if use_partitions is set to 0 - if no parameter is provided, it will list all gateways along with their status. If one parameter is provided, that must be the id of a gateway and the function will return the status of that gateway. If 2 parameters are provided, first must be the ID of the ID of a GW and the second must be the new status to be forced for that GW (0 - disable, 1 - enable). * if use_partitions is set to 1 - the first parameter must be the partition (the partition is mandatory). If just one parameter is provided it will the display the statuses of all the gateways in the given partition. If two parameters are provided, the first must be the partition, and the second must be the gateway Id. If three parameters are provided, the first must be the partition, the second must be the gateway and the third will be the new status to be forced for tat GW (0 - disable, 1 - enable) MI FIFO Command Format: :dr_gw_status:_reply_fifo_file_ partition_name (mandatory if use_partitions is 1, otherw ise will be omitted altogether) GW_id status (optional) _empty_line_ Example 1.47. dr_gw_status usage when use_partitions is set to 0 $ ./opensipsctl fifo dr_gw_status 2 State:: Active $ ./opensipsctl fifo dr_gw_status 2 0 $ ./opensipsctl fifo dr_gw_status 2 Enabled:: Disabled MI $ ./opensipsctl fifo dr_gw_status 3 Enabled:: Inactive Example 1.48. dr_gw_status usage when use_partitionsis set to 1 $ ./opensipsctl fifo dr_gw_status part_1 my_gw State:: Active $ ./opensipsctl fifo dr_gw_status my_partition 3 0 $ ./opensipsctl fifo dr_gw_status partition7 dsbl_gw 2 Enabled:: Disabled MI $ ./opensipsctl fifo dr_gw_status partition8 gw3 Enabled:: Inactive 1.5.3. dr_carrier_status Gets or sets the status (enabled or disabled) of a carrier. The function may take from 0 to 3 parameters. * if use_partition is set to 0 - if no parameter is provided it will list all the carriers along with their status. If one parameter is provided, that must be the id of carrier and the function will return the status of that carrier. If 2 parameters are provided, first must be the Id of a carrier and the second must be the new status to be forced for that carrier * if use_partition is set to 1 - the first parameter must be the partition (the partition becomes mandatory). If one parameter is supplied, it will be the partition, and it will display the statuses of the carriers contained in that partition. If two parameters are supplied, the second must be the carrierId, and the command will display the status of the selected carrier. If three parameters are supplied, the first two will be the partition name and the carrierId while the third parameter will be the new status to be forced for that carrier. MI FIFO Command Format: :dr_carrier_status:_reply_fifo_file_ partition_name (mandatory if use_partition is 1, otherwi se it will be omitted) carrier_id status (optional) _empty_line_ Example 1.49. dr_carrier_status usage when use_partitions is 0 $ ./opensipsctl fifo dr_carrier_status CR1 Enabled:: no $ ./opensipsctl fifo dr_carrier_status CR1 1 $ ./opensipsctl fifo dr_carrier_status CR1 Enabled:: yes Example 1.50. dr_carrier_status usage when use_partitions is 1 $ ./opensipsctl fifo dr_carrier_status my_partition CR1 Enabled:: no $ ./opensipsctl fifo dr_carrier_status partition_1 CR1 1 $ ./opensipsctl fifo dr_carrier_status partition_3 CR1 Enabled:: yes 1.5.4. dr_reload_status Gets the time of the last reload for any partition. The function may take at most one parameter. * if use_partition is set to 0 - the function doesn't receive any parameter. It will list the date of the last reload for the default (and only) partition. * if use_partition is set to 1 - if no parameter is supplied it will list the time of the last update for every partition. If one parameter is supplied, then this must be the partition name, and the function will list the time of the last reload for that given partition. MI FIFO Command Format: :dr_reload_status:_reply_fifo_file_ partition_name (if use_partition is 1 it may be omitted, but if use_partition is 0 it must be omitted) _empty_line_ Example 1.51. dr_reload_status usage when use_partitions is 0 $ ./opensipsctl fifo dr_reload_status Date:: Tue Aug 12 12:26:00 2014 Example 1.52. dr_reload_status usage when use_partitions is 1 $ ./opensipsctl fifo dr_reload_status Partition:: part_test Date=Tue Aug 12 12:24:13 2014 Partition:: part_2 Date=Tue Aug 12 12:24:13 2014 $ ./opensipsctl fifo dr_reload_status part_test Partition:: part_test Date=Tue Aug 12 12:24:13 2014 1.5.5. dr_number_routing Gets the matched prefix along with the list of the gateways / carriers to which a number would be routed when using the do_routing function * if use_partition is set to 1 the function will have 3 parameters: + partition name + group id - the group id of the rules to check against + number - the number to test against * if use_partition is set to 0 the function will have 2 parameters: + group id - the group id of the rules to check against + number - the number to test against Note: The group id may be omitted - just as with the do_routing function. 1.6. Exported Events 1.6.1. E_DROUTING_STATUS This event is raised when the module changes the state of a gateway, either through MI or probing. Parameters: * gwid - the gateway identifier. * address - the address of the gateway. * status - disabled MI if the gateway was disabled using MI commands, probing if the gateway is being pinged, inactive if it was disabled from the script or active if the gateway is enabled. 1.7. Installation The module requires 4 tables in the OpenSIPS database: dr_groups, dr_gateways, dr_carriers, dr_rules. The SQL syntax to create them can be found in the drouting-create.sql script, located in the database directories of the opensips/scripts folder. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. Chapter 2. Developer Guide The module provides no function to be used by other OpenSIPS modules. opensips-2.2.2/modules/drouting/doc/000077500000000000000000000000001300170765700174335ustar00rootroot00000000000000opensips-2.2.2/modules/drouting/doc/drouting.xml000066400000000000000000000023451300170765700220140ustar00rootroot00000000000000 %docentities; ]> Dynamic Routing Module &osipsname; Bogdan-Andrei Iancu &osipssol; Bogdan-Andrei Iancu
bogdan@opensips.org
Anca-Maria Vamanu
2005-2008 &voicesystem; 2009-2012 &osipssol; $Revision: 8834 $ $Date$
&admin; &devel;
opensips-2.2.2/modules/drouting/doc/drouting_admin.xml000066400000000000000000002032541300170765700231660ustar00rootroot00000000000000 &adminguide;
Overview
Introduction Dynamic Routing is a module for selecting (based on multiple criteria) the best gateway/destination to be used for delivering a certain call. Least Cost Routing (LCR) is a special case of dynamic routing - when the rules are ordered based on costs. Dynamic Routing comes with many features regarding routing rule selection: prefix based caller/group based time based priority based , processing : stripping and prefixing default rules inbound and outbound processing script route triggering and failure handling: serial forking weight based GW selection random GW selection GW probing for crashes
Features The dynamic routing implementation for &osips; is designed with the following properties: The routing info (destinations, carriers, rules, groups) is stored in a database and loaded into memory at start up time; reload at runtime via a Management Interface command. weight-based or random selection of the destinations (from a rule or from a carrier), failure detection of gateways (with switching to next available gateway). able to handle large volume of routing info (10M of rules) with minimal speed/time and memory consumption penalties script integration - Pseudo-variable support in functions; scripting route triggering when rules are matched bidirectional behavior - inbound and outbound processing (strip and prefixing when sending and receiving from a destination/GW) blacklisting - the module allows definition of blacklists based on the destination IPs. This blacklists are to be used to prevent malicious forwarding to GWs (based on DNS lookups) when the script logic does none-GE forwarding (like foreign domains). loading routing information from multiple databases - the gateways, rules, groups and carriers can be grouped by partitions, and each partition may be loaded from different databases/tables. This makes the routing process partition based. In order to be able to use a table from a partition, its name must be found in the "version" table belonging to the database defined in the partition's db_url.
Performance There were several tests performed regarding the performance of the module when dealing with a large number of routing rules. The tests were performed with a set of 383000 rules and measured: time to load from DB used shared memory The time to load was varying between 4 seconds and 8 seconds, depending of the caching of the DB client - the first load was the slowest (as the DB query hits the disk drive); the following are faster as data is already cached in the DB client. So technically speaking, the time to load (without the time to query which is DB type dependent) is ~4 seconds After loading the data into shared memory ~ 96M of memory were used exclusively for the DR data.
Dynamic Routing Concepts DR engine uses several concepts in order to define how the routing should be done (describing all the dependencies between destinations and routing rules).
Destination/Gateways These are the end SIP entities where actually the traffic needs to be sent after routing. They are stored in a table called dr_gateways. Gateway addresses are stored in a separate table because of the need to access them independent of Dynamic Routing processing (e.g., adding/ removing gateway PRI prefix before/after performing other operation -- receiving/relaying to gateway). In DR, a gateway is defined by: id (string) SIP address (SIP URI) type (number to allow to group GW based on purpose, like inbound, outbound, etc) strip value (number of digits) from dialled number prefix (string) to be added to dialled number attributes (not used by DR engine, but only pushed to script level when routing to this GW) probing mode (how the GW should be probed at SIP level - see the probing chapter) The Gateways are to be used from the routing rule or from the carrier definition. They are all the time referred by their ID.
Carriers The carrier concept is used if you need to group gateways in order to have a better control on how the GWs will be used by DR rules; like in what order the GWs will be used. Basically, a carrier is a set of gateways which have its own sorting algorithm and its own attribute string. They are by default defined in the dr_carriers table. In DR, a carrier is defined by: id (string) list of gateways with/without weights (string) (Ex:gw1=10,gw4=10 or gw1,gw2 flags : 0x1 - use weight for sorting the list and not definition order; 0x2 - use only the first gateway from the carrier (depending on the sorting); 0x4 - disable the usage of this carrier attributes (not used by DR engine, but only pushed to script level when routing to this carrier) The Carriers are to be used only from the routing rule definition. They are all the time referred by their ID.
Routing Rules These are the actual rules which control the routing. Using different criterias (prefix, time, priority, etc), they will decide to which gateways the call will be sent. Default name for the table storing rule definitions is dr_rules. In DR, a carrier is defined by: group (list of numbers) - rules can be grouped (a rule may belong to multiple groups in the same time ) and you can use only a certain group at a point; like having a premium or standard or interstate or intrastate groups of rules to be used in different cases prefix (string with digits only) - prefix to be used for matching this rule (longest prefix matching) time validity (time recurrence string) - when this rule is valid from time point of view (see RFC 2445) priority (number) - priority of the rule - higher value, higher priority (see rule section alg) script route ID (string) - if defined, then execute the route with the specified ID when this rule is matched. That's it, a route which can be used to perform custom operations on message. NOTE that no modification is performed at signaling level and you must NOT do any signaling operations in that script route list of GWs/carriers (string) - a comma separated list of gateways or carriers (defined by IDs) to be used for this rule; the carrier IDs are prefixed with # sign. For each ID (GW or carrier) you may specify a weight. For how this list will be interpreted (as order) see the rule selection section. Example of list: gw1,gw4,#cr3 or gw1=10,gw4=10,#cr3=80 attributes (not used by DR engine, but only pushed to script level when this rule matched and been used) More on time recurrence: A date-time expression that defines the time recurrence to be matched for current rule. Time recurrences are based closely on the recurring time intervals from the Internet Calendaring and Scheduling Core Object Specification (calendar COS), RFC 2445. The set of attributes used in routing rule specification is a subset of time recurrence attributes. The value stored in database has the format of: ||||||||| ]]> When an attribute is not specified, the corresponding place must be left empty, whenever another attribute that follows in the list has to be specified.
Routing Rule Processing The module can be used to find out which is the best gateway to use for new calls terminated to PSTN. The algorithm to select the rule is as follows: the module discovers the routing group of the originating user. This step is skipped if a routing group is passed from the script as parameter. once the group is known, in the subset of the rules for this group the module looks for the one that matches the destination based on "prefix" column. The set of rules with the longest prefix is chosen. If no digit from the prefix matches, the default rules are used (rules with no prefix) within the set of rules is applied the time criteria, and the rule which has the highest priority and matches the time criteria is selected to drive the routing. Once found the rule, it may contain a route ID to execute. If a certain flag is set, then the processing is stopped after executing the route block. The rule must contain a chain of gateways and carriers. The module will execute serial forking for each address in the chain (ordering is either done by simply using the definition order or it may weight-based - weight selection must be enabled). The next address in chain is used only if the previously has failed. With the right gateway address found, the prefix (PRI) of the gateway is added to the request URI and then the request is forwarded. If no rule is found to match the selection criteria an default action must be taken (e.g., error response sent back). If the gateway in the chain has no prefix the request is forwarded without adding any prefix to the request URI.
Probing and Disabling destinations The module has the capability to monitor the status of the destinations by doing SIP probing (sending SIP requests like OPTIONS). For each destination, you can configure what kind of probing should be done (probe_mode column): (0) - no probing at all; (1) - probing only when the destination is in disabled mode (disabling via MI command will completely stop the probing also). The destination will be automatically re-enabled when the probing will succeed next time; (2) - probing all the time. If disabled, the destination will be automatically re-enabled when the probing will succeed next time; A destination can become disabled in two ways: script detection - by calling from script the dr_disable() function after trying the destination. In this case, if probing mode for the destination is (1) or (2), the destination will be automatically re-enabled when the probing will succeed. MI command - by calling the dr_gw_status MI command for disabling (on demand) the destination. If so, the probing and re-enabling of this destination will be completly disabled until you re-enable it again via MI command - this is designed to allow controlled and complete disabling of some destination during maintenance.
Dependencies
&osips; Modules The following modules must be loaded before this module: a database module. tm module.
External Libraries or Applications none.
Exported Parameters
<varname>db_url</varname>(str) The database url. Default value is NULL. Set <varname>db_url</varname> parameter ... modparam("drouting", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") ...
<varname>drd_table</varname>(str) The name of the db table storing gateway addresses. Default value is dr_gateways. Set <varname>drd_table</varname> parameter ... modparam("drouting", "drd_table", "dr_gateways") ...
<varname>drr_table</varname>(str) The name of the db table storing routing rules. Default value is dr_rules. Set <varname>drr_table</varname> parameter ... modparam("drouting", "drr_table", "rules") ...
<varname>drg_table</varname>(str) The name of the db table storing groups. Default value is dr_groups. Set <varname>drg_table</varname> parameter ... modparam("drouting", "drg_table", "groups") ...
<varname>drc_table</varname>(str) The name of the db table storing definitions of the carriers that will be used directly by the routing rules. Default value is dr_carriers. Set <varname>drc_table</varname> parameter ... modparam("drouting", "drc_table", "my_dr_carriers") ...
<varname>ruri_avp</varname> (str) The name of the avp for storing Request URIs to be later used (alternative destiantions for the current one). Default value is $avp(___dr_ruri__) if use_partitions parameter is 0 or $avp(___dr_ruri__partition_name) where partition_name is the name of the partition containing the AVP (as fetched from the database) if use_partitions parameter is 1. Set <varname>ruri_avp</varname> parameter ... modparam("drouting", "ruri_avp", '$avp(dr_ruri)') modparam("drouting", "ruri_avp", '$avp(33)') ...
<varname>gw_id_avp</varname> (str) The name of the avp for storing the id of the current selected gateway/destination - once a new destination is selected (via the use_next_gw() function), the AVP will be updated with the ID of the new selected gateway/destination. Default value is $avp(___dr_gw_id__) if use_partitions parameter is 0 or $avp(___dr_gw_id__partition_name) where partition_name is the name of the partition containing the AVP (as fetched from the database) if use_partitions parameter is 1. Set <varname>gw_id_avp</varname> parameter ... modparam("drouting", "gw_id_avp", '$avp(gw_id)') modparam("drouting", "gw_id_avp", '$avp(334)') ...
<varname>gw_priprefix_avp</varname> (str) The name of the avp for storing the PRI prefix of the current selected destination/gateway - once a new destination is selected (via the use_next_gw() function), the AVP will be updated with the PRI prefix of the new used destination. Default value is NULL. Set <varname>gw_priprefix_avp</varname> parameter ... modparam("drouting", "gw_priprefix_avp", '$avp(gw_priprefix)') ...
<varname>rule_id_avp</varname> (str) The name of the avp for storing the id of the current matched routing rule (see dr_rules table). Default value is NULL. Set <varname>rule_id_avp</varname> parameter ... modparam("drouting", "rule_id_avp", '$avp(rule_id)') modparam("drouting", "rule_id_avp", '$avp(335)') ...
<varname>rule_prefix_avp</varname> (str) The actual prefix that matched the routing rule (the part from RURI username that matched the routing rule). Default value is NULL. Set <varname>rule_prefix_avp</varname> parameter ... modparam("drouting", "rule_prefix_avp", '$avp(dr_prefix)') ...
<varname>carrier_id_avp</varname> (str) AVP to be populate with the ID string for the carrier the current GW belongs to. Default value is NULL. Set <varname>carrier_id_avp</varname> parameter ... modparam("drouting", "carrier_id_avp", '$avp(carrier_id)') ...
<varname>gw_sock_avp</varname> (str) The name of the avp for storing sockets for alternative destinations defined by ruri_avp. Default value is $avp(___dr_sock__) if use_partitions parameter is 0 or $avp(___dr_sock__partition_name) where partition_name is the name of the partition containing the AVP (as fetched from the database) if use_partitions parameter is 1. Set <varname>gw_sock_avp</varname> parameter ... modparam("drouting", "gw_sock_avp", '$avp(dr_sock)') modparam("drouting", "gw_sock_avp", '$avp(77)') ...
<varname>rule_attrs_avp</varname> (str) The name of the avp for storing rule attrs in case they are requested at least once in the script. Default value is $avp(___dr_ru_att__) if use_partitions parameter is 0 or $avp(___dr_ru_att__partition_name) where partition_name is the name of the partition containing the AVP (as fetched from the database) if use_partitions parameter is 1. Set <varname>rule_attrs_avp</varname> parameter ... modparam("drouting", "rule_attrs_avp", '$avp(dr_rule_attr)') modparam("drouting", "rule_attrs_avp", '$avp(11)') ...
<varname>define_blacklist</varname> (str) Defines a blacklist based on a list of GW types - the list will contain the IPs (no port, all protocols) of the GWs with the specified types. Multiple instances of this param are allowed. Default value is NULL. Set <varname>define_blacklist</varname> parameter ... modparam("drouting", "define_blacklist", 'bl_name= 3,5,25,23') modparam("drouting", "define_blacklist", 'list= 4,2') ...
<varname>default_group</varname> (int) Group to be used if the caller (FROM user) is not found in the GROUP table. Default value is NONE. Set <varname>default_group</varname> parameter ... modparam("drouting", "default_group", 4) ...
<varname>force_dns</varname> (int) Force DNS resolving of GW/destination names (if not IPs) during startup. If not enabled, the GW name will be blindly used during routing. Default value is 1 (enabled). Set <varname>force_dns</varname> parameter ... modparam("drouting", "force_dns", 0) ...
<varname>persistent_state</varname> (int) Specifies whether the state column should be loaded at startup and flushed during runtime or not. Default value is 1 (enabled). Set the <varname>persistent_state</varname> parameter ... # disable all DB operations with the state of a gateway modparam("drouting", "persistent_state", 0) ...
<varname>no_concurrent_reload</varname> (int) If enabled, the module will not allow do run multiple dr_reload MI commands in parallel (with overlapping) Any new reload will be rejected (and discarded) while an existing reload is in progress. If you have a large routing set (millions of rules/prefixes), you should consider disabling concurrent reload as they will exhaust the shared memory (by reloading into memory, in the same time, multiple instances of routing data). Default value is 0 (disabled). Set <varname>no_concurrent_reload</varname> parameter ... # do not allow parallel reload operations modparam("drouting", "no_concurrent_reload", 1) ...
<varname>probing_interval</varname> (integer) How often (in seconds) the probing of a destination should be done. If set to 0, the probing will be disabled as functionality (for all destinations) Default value is 30. Set <varname>probing_interval</varname> parameter ... modparam("drouting", "probing_interval", 60) ...
<varname>probing_method</varname> (string) The SIP method to be used for the probing requests. Default value is "OPTIONS". Set <varname>probing_method</varname> parameter ... modparam("drouting", "probing_method", "INFO") ...
<varname>probing_from</varname> (string) The FROM SIP URI to be advertised in the SIP probing requests. Default value is "sip:prober@localhost". Set <varname>probing_from</varname> parameter ... modparam("drouting", "probing_from", "sip:pinger@192.168.2.10") ...
<varname>probing_reply_codes</varname> (string) A comma separted list of SIP reply codes. The codes defined here will be considered as valid reply codes for probing messages, apart for 200. Default value is NULL. Set <varname>probing_reply_codes</varname> parameter ... modparam("drouting", "probing_reply_codes", "501, 403") ...
<varname>use_domain</varname> (int) Flag to configure whether to use domain match when querying database for user's routing group. Default value is 1. Set <varname>use_domain</varname> parameter ... modparam("drouting", "use_domain", 0) ...
<varname>drg_user_col</varname> (str) The name of the column in group db table where the username is stored. Default value is username. Set <varname>drg_user_col</varname> parameter ... modparam("drouting", "drg_user_col", "user") ...
<varname>drg_domain_col</varname> (str) The name of the column in group db table where the domain is stored. Default value is domain. Set <varname>drg_domain_col</varname> parameter ... modparam("drouting", "drg_domain_col", "host") ...
<varname>drg_grpid_col</varname> (str) The name of the column in group db table where the group id is stored. Default value is groupid. Set <varname>drg_grpid_col</varname> parameter ... modparam("drouting", "drg_grpid_col", "grpid") ...
<varname>use_partitions</varname> (int) Flag to configure whether to use partitions for routing. If this flag is set then the db_partitions_url and db_partitions_table variables become mandatory. Default value is 0. Set <varname>use_partitions</varname> parameter ... modparam("drouting", "use_partitions", 1) ...
<varname>db_partitions_url</varname> (int) The url to the database containing partition-specific information. (partition-specific information includes partition name, url to the database where information about the partition is preserved, the names of the tables in which it is preserved and the AVPs that can be accessed using the .cfg script). The use_partitions parameter must be set to 1. Default value is "NULL". Set <varname>db_partitions_url</varname> parameter ... modparam("drouting", "db_partitions_url", "mysql://user:password@localhost/opensips_partitions") ...
<varname>db_partitions_table</varname> (int) The name of the table containing partition definitions. To be used with use_partitions and db_partitions_url. Default value is dr_partitions. Set <varname>db_partitions_table</varname> parameter ... modparam("drouting", "db_partitions_table", "partition_defs") ...
<varname>partition_id_pvar</varname> (pvar) Variable which will store the name of the name partition when wildcard(*) operatior is used. Use_partitions must be set in order to use this parameter. NOTE: The variable must be WRITABLE! Default value is null(not used). Set <varname>partition_id_pvar</varname> parameter ... modparam("drouting", "partition_id_pvar", "$var(matched_partition)") ...
Exported Functions
<function moreinfo="none">do_routing([part_and_or_groupID], [flags], [gw_whitelist], [rule_attrs_pvar], [gw_attrs_pvar], [carrier_attrs_pvar])</function> Function to trigger routing of the message according to the rules in the database table and the configured parameters. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE. If you set use_partitions to 1 the part_or_groupID parameter becomes mandatory. All parameters are optional. Any of them may be ignored, provided the necessary separation marks "," are properly placed. part_and_or_groupID - Specifies the group of the caller for routing purposes. Depending on the value of the use_partitions parameter, it contains: the routing group the caller belongs to if use_partitions is 0 - this may be a statical numerical value or an AVP specification (value must be numerical type, string types are ignored!). If none specified the function will automatically try to query the dr_group table to get this information the partition and routing group the caller belongs to, the format is: "partition':'[groupID]" if use_partitions parameter is 1 - both the partition name and the groupId may be statical values or AVP specifications. If no group is specified the function will try to query the dr_group table for the given partition to get this information. If * (wildcard) operator is used all partitions shall be checked. flags - Controls the behavior of the function. Possible flags are: W - Instead of using the destination (from the rule definition) in the given order, sort them based on their weight. F - Enable rule fallback; normally the engine is using a single rule for routing a call; by setting this flag, the engine will fallback and use rules with less priority or shorter prefix when all the destination from the current rules failed. L - Do strict length matching over the prefix - actually DR engine will do full number matching and not prefix matching anymore. C - Only check if the dialed number matches any routing rule, without loading / applying any routing info (no GW is set, the RURI is not altered) gw_whitelist - a comma separated white list of gateways. This will force routing over, at most, this list of carriers or gateways (in other words, the whitelist will be intersected with the results of the search through the rules). rule_attrs_pvar (output, optional)- a writable pseudo-variable which will be populated with the attributes of the matched dynamic routing rule. gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. carrier_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched carrier. <function>do_routing</function> usage ... # all groups, sort on order, use_partitions is 0 do_routing(); ... # all groups, sort on order, use_partitions is 1, route by partition named "part" do_routing("part:"); ... # group id 0, sort on order, use_partitions is 0 do_routing("0"); ... # group id 0, sort on order, use_partitions is 1, route by partition named "part" do_routing("part:0"); ... # group id from $avp(10), sort on order, use_partitions is 0 do_routing("$avp(10)"); ... # all groups, sort on weights, use_partitions is 0 do_routing("", "W"); ... # all groups, use_partitions is 1, partition and group supplied by AVPs, do strict length matching do_routing("$avp(partition):$avp(grp)","L") ... # group id 2, sort on order, fallback rule and also return the gateway attributes do_routing("2", "F", , , "$var(gw_attributes)"); ...
<function moreinfo="none">route_to_carrier(part_and_or_carrier_id, [gw_attrs_pvar], [carrier_attrs_pvar])</function> Function to trigger the direct routing to a given carrier. In this case the routing is not done prefix based, but carrier based (call will be sent to the GWs of that carrier, based on carrier policy). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE.. If you set use_partitions parameter to 1 you must supply the partition in which the carrier has been defined. part_and_or_carrier_id (mandatory): the ID (name) of the carrier to be used, if use_partitions parameter is 0; pseudo-variables are accepted. the partition and carrier to be used, if use_partitions parameter is 1. The format is "partition_name':'carrierId"; pseudo-variables are accepted. gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the currently matched gateway of this carrier. carrier_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of this carrier. <function>route_to_carrier</function> usage when <varname>use_partitions</varname> parameter is 0 ... if ( route_to_carrier("my_top_carrier", , "$var(carrier_att)") ) { xlog("Routing to \"my_top_carrier\" - $var(carrier_att)\n"); t_on_failure("next_gw"); t_relay(); exit; } ... <function>route_to_carrier</function> usage when <varname>use_partitions</varname> parameter is 1 ... if ( route_to_carrier("my_partition:my_top_carrier", , "$var(carrier_att)") ) { xlog("Routing to \"my_top_carrier\" - $var(carrier_att)\n"); t_on_failure("next_gw"); t_relay(); exit; } ... <function>route_to_carrier</function> usage when <varname>use_partitions</varname> parameter is 1 with pseudovariables ... if ( route_to_carrier("$var(my_partition):$var(carrierId)") ) { xlog("Routing to \"my_top_carrier\" - $var(carrier_att)\n"); t_on_failure("next_gw"); t_relay(); exit; } ...
<function moreinfo="none">route_to_gw(gw_id, [gw_attrs_pvar])</function> Function to trigger the direct routing to a given gateway (or list of gateways). Attributes and per-gw processing will be available. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE. If you set use_partitions parameter to 1 you must supply the partition in which the gateway has been defined. gw_id (mandatory) - the list of gateways to be used. a comma separated list of gateway ID's to be used, if no use_partition parameter is 0. Pseudo-variables are accepted. the desired partition, followed by a comma separated list of gateway ID's from that partition to be used, if use_partition parameter is 1. The format is: "partition_name':'gwId1, gwId2, gwId3". Pseudo-variables are accepted. gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the currently matched gateway. <function>route_to_gw</function> usage when <varname>use_partition</varname> parameter is 0 ... if ( route_to_gw("gw_europe") ) { t_relay(); exit; } ... if ( route_to_gw("gw1,gw2,gw3", "$var(gw_attrs)") ) { xlog("Relaying to first gateway from our list - $var(gw_attrs)\n"); t_relay(); exit; } ... <function>route_to_gw</function> usage when <varname>use_partition</varname> parameter is 1 ... if ( route_to_gw("my_partition:gw_europe") ) { t_relay(); exit; } ... if ( route_to_gw("my_partition:gw1,gw2,gw3", "$var(gw_attrs)") ) { xlog("Relaying to first gateway from our list - $var(gw_attrs)\n"); t_relay(); exit; } ...
<function moreinfo="none">use_next_gw([partition','] [rule_attrs_pvar], [gw_attrs_pvar], [carrier_attrs_pvar])/next_routing()</function> The function takes the next available destination (set by do_routing, as alternative destinations) and pushes it into the RURI. Note that the function just sets the RURI (nothing more). If a new RURI is set, the used destination is removed from the pending set of alternative destinations. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and LOCAL_ROUTE. The function returns true only if a new RURI was set. False is returned is no other alternative destinations are found or in case of an internal processing error. It may take the following optional parameters: If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateways have been defined. partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) It is the partition in which the gateways have been defined. rule_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched dynamic routing rule. gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. carrier_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched carrier. <function>use_next_gw</function> usage ... if (use_next_gw()) { t_relay(); exit; } ... # Also fetch the carrier attributes, if any if (use_next_gw(, , "$var(carrier_attrs)")) { xlog("Carrier attributes of current gateway: $var(carrier_attrs)\n"); t_relay(); exit; } ... <function>use_next_gw</function> usage when <varname>use_partition</varname> parameter is 1 ... if (use_next_gw("my_partition")) { t_relay(); exit; } ... # Also fetch the carrier attributes, if any if (use_next_gw("my_partition", , "$var(carrier_attrs)")) { xlog("Carrier attributes of current gateway: $var(carrier_attrs)\n"); t_relay(); exit; } ...
<function moreinfo="none">goes_to_gw([partition','] [type], [flags], [gw_attrs_pvar])</function> Function returns true if the destination of the current request (destination URI or Request URI) points (as IP) to one of the gateways. There no DNS lookups done if the domain part of the URI is not an IP. This function does not change anything in the message. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateways have been defined. If use_partitions parameter is 0 all parameters are optional. Any of them may be ignored, provided the necessary separation marks "," are properly placed. partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) - the name of the partition containing the gateway/destination to be checked. type (optional) - GW/destination type to be checked; when omitting this parameter or specifying a negative value i.e. "-1", matching will be done against all groups (in a given partition if use_partition parameter is 1; if use_partitions is 1 the partition being mandatory at this point, it is not possible to do matching against all the partitions) flags (optional) - what operations should be performed when a GW matches: 's' (Strip) - apply to the username of RURI the strip defined by the GW 'p' (Prefix) - apply to the username of RURI the prefix defined by the GW 'i' (Gateway ID) - return the gateway id into gw_id_avp AVP 'n' (Ignore port) - ignores port number during matching 'c' (Carrier ID) - return the carrier id into carrier_id_avp AVP gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. <function>goes_to_gw</function> usage when <varname>use_partitions</varname> parameter is 0 ... if (goes_to_gw("1", , "$var(gw_attrs)")) { sl_send_reply("403","Forbidden"); exit; } ... <function>goes_to_gw</function> usage, when <varname>use_partitions</varname> parameter is 1 ... if (goes_to_gw("my_partition", "1", , "$var(gw_attrs)")) { sl_send_reply("403","Forbidden"); exit; } ...
<function moreinfo="none">is_from_gw([partition','] [type], [flag], [gw_attrs_pvar])</function> The function checks if the sender of the message (its source IP) is a gateway from a certain group. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateways have been defined. If use_partitions parameter is 0 all parameters are optional. Any of them may be ignored, provided the necessary separation marks "," are properly placed. partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) - Partition containing the destination/gw to be checked. If *(wildcard) operator is used all partitions shall be checked. type (optional) - GW/destination type to be checked; when omitting this parameter or specifying a negative value i.e. "-1", matching will be done against all groups flags (optional) - what operations should be performed when a GW matches: 's' (Strip) - apply to the username of RURI the strip defined by the GW 'p' (Prefix) - apply to the username of RURI the prefix defined by the GW 'i' (Gateway ID) - return the gateway id into gw_id_avp AVP 'n' (Ignore port) - ignores port number during matching 'c' (Carrier ID) - return the carrier id into carrier_id_avp AVP gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. <function>is_from_gw</function> usage when <varname>use_partitions</varname> is 0 ... if (is_from_gw("3","1") { } ... <function>is_from_gw</function> usage when <varname>use_partitions</varname> is 1 ... if (is_from_gw("my_partition","3","1") { } ...
<function moreinfo="none">dr_is_gw([partition,] src_avp, [type], [flag], [gw_attrs_pvar])</function> The function checks if the ip address in pvar src_pv is a gateway from a certain group. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE and EVENT_ROUTE. If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateways have been defined. Meaning of the parameters is as follows: partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) - Partition containing the destinations/gateways to be checked. src_avp (mandatory) - avp containing a SIP URI. Does not support other OpenSIPS pseudo-variables. type (optional) - GW/destination type to be checked; when omitting this parameter or specifying a negative value i.e. "-1", matching will be done against all groups flags (optional) - what operations should be performed when a GW matches: 's' (Strip) - apply to the username of RURI the strip defined by the GW 'p' (Prefix) - apply to the username of RURI the prefix defined by the GW 'i' (Gateway ID) - return the gateway id into gw_id_avp pvar 'n' (Ignore port) - ignores port number 'c' (Carrier ID) - return the carrier id into carrier_id_avp AVP gw_attrs_pvar (output, optional) - a writable pseudo-variable which will be populated with the attributes of the matched gateway. <function>dr_is_gw</function> usage when <varname>use_partitions</varname> is 0 ... if (dr_is_gw("$avp(uac)","3") { } ... <function>dr_is_gw</function> usage when <varname>use_partitions</varname> is 1 ... if (dr_is_gw("my_partition","$avp(uac)","3") { } ...
<function moreinfo="none">dr_disable()</function> Marks as disabled the last destination that was used for the current call. The disabling done via this function will prevent the destination to be used for usage from now on. The probing mechanism can re-enable this peer (see the probing section in the beginning) This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. If you set use_partitions parameter to 1 you must supply the partition (the partition becomes mandatory) in which the gateway to be disabled is defined. partition (mandatory if use_partitions parameter is 1, otherwise it will be omitted altogether) - Partition containing the destination/gateway to be disabled. <function>dr_disable()</function> usage when <varname>use_partitions</varname> is 0 ... if (t_check_status("(408)|(5[0-9][0-9])")) { dr_disable(); } ... <function>dr_disable()</function> usage when <varname>use_partitions</varname> is 1 ... if (t_check_status("(408)|(5[0-9][0-9])")) { dr_disable("my_partition"); } ...
Exported MI Functions
<function moreinfo="none">dr_reload</function> Command to reload routing rules from database. If use_partitions is set to 1 you can reload just a partition given a parameter, if no parameter is supplied then all the partitions will be reloaded. If use_partitions is 0 it takes no parameter. MI FIFO Command Format: :dr_reload:fifo_reply partition_name (optional) _empty_line_
<varname>dr_gw_status</varname> Gets or sets the status (enabled or disabled) of a gateway. The function may take from 0 to 3 parameters. if use_partitions is set to 0 - if no parameter is provided, it will list all gateways along with their status. If one parameter is provided, that must be the id of a gateway and the function will return the status of that gateway. If 2 parameters are provided, first must be the ID of the ID of a GW and the second must be the new status to be forced for that GW (0 - disable, 1 - enable). if use_partitions is set to 1 - the first parameter must be the partition (the partition is mandatory). If just one parameter is provided it will the display the statuses of all the gateways in the given partition. If two parameters are provided, the first must be the partition, and the second must be the gateway Id. If three parameters are provided, the first must be the partition, the second must be the gateway and the third will be the new status to be forced for tat GW (0 - disable, 1 - enable) MI FIFO Command Format: :dr_gw_status:_reply_fifo_file_ partition_name (mandatory if use_partitions is 1, otherwise will be omitted altogether) GW_id status (optional) _empty_line_ <function>dr_gw_status</function> usage when <varname>use_partitions</varname> is set to 0 $ ./opensipsctl fifo dr_gw_status 2 State:: Active $ ./opensipsctl fifo dr_gw_status 2 0 $ ./opensipsctl fifo dr_gw_status 2 Enabled:: Disabled MI $ ./opensipsctl fifo dr_gw_status 3 Enabled:: Inactive <function>dr_gw_status</function> usage when <varname>use_partitions</varname>is set to 1 $ ./opensipsctl fifo dr_gw_status part_1 my_gw State:: Active $ ./opensipsctl fifo dr_gw_status my_partition 3 0 $ ./opensipsctl fifo dr_gw_status partition7 dsbl_gw 2 Enabled:: Disabled MI $ ./opensipsctl fifo dr_gw_status partition8 gw3 Enabled:: Inactive
<varname>dr_carrier_status</varname> Gets or sets the status (enabled or disabled) of a carrier. The function may take from 0 to 3 parameters. if use_partition is set to 0 - if no parameter is provided it will list all the carriers along with their status. If one parameter is provided, that must be the id of carrier and the function will return the status of that carrier. If 2 parameters are provided, first must be the Id of a carrier and the second must be the new status to be forced for that carrier if use_partition is set to 1 - the first parameter must be the partition (the partition becomes mandatory). If one parameter is supplied, it will be the partition, and it will display the statuses of the carriers contained in that partition. If two parameters are supplied, the second must be the carrierId, and the command will display the status of the selected carrier. If three parameters are supplied, the first two will be the partition name and the carrierId while the third parameter will be the new status to be forced for that carrier. MI FIFO Command Format: :dr_carrier_status:_reply_fifo_file_ partition_name (mandatory if use_partition is 1, otherwise it will be omitted) carrier_id status (optional) _empty_line_ <function>dr_carrier_status</function> usage when <varname>use_partitions</varname> is 0 $ ./opensipsctl fifo dr_carrier_status CR1 Enabled:: no $ ./opensipsctl fifo dr_carrier_status CR1 1 $ ./opensipsctl fifo dr_carrier_status CR1 Enabled:: yes <function>dr_carrier_status</function> usage when <varname>use_partitions</varname> is 1 $ ./opensipsctl fifo dr_carrier_status my_partition CR1 Enabled:: no $ ./opensipsctl fifo dr_carrier_status partition_1 CR1 1 $ ./opensipsctl fifo dr_carrier_status partition_3 CR1 Enabled:: yes
<varname>dr_reload_status</varname> Gets the time of the last reload for any partition. The function may take at most one parameter. if use_partition is set to 0 - the function doesn't receive any parameter. It will list the date of the last reload for the default (and only) partition. if use_partition is set to 1 - if no parameter is supplied it will list the time of the last update for every partition. If one parameter is supplied, then this must be the partition name, and the function will list the time of the last reload for that given partition. MI FIFO Command Format: :dr_reload_status:_reply_fifo_file_ partition_name (if use_partition is 1 it may be omitted, but if use_partition is 0 it must be omitted) _empty_line_ <function>dr_reload_status</function> usage when <varname>use_partitions</varname> is 0 $ ./opensipsctl fifo dr_reload_status Date:: Tue Aug 12 12:26:00 2014 <function>dr_reload_status</function> usage when <varname>use_partitions</varname> is 1 $ ./opensipsctl fifo dr_reload_status Partition:: part_test Date=Tue Aug 12 12:24:13 2014 Partition:: part_2 Date=Tue Aug 12 12:24:13 2014 $ ./opensipsctl fifo dr_reload_status part_test Partition:: part_test Date=Tue Aug 12 12:24:13 2014
<varname>dr_number_routing</varname> Gets the matched prefix along with the list of the gateways / carriers to which a number would be routed when using the do_routing function if use_partition is set to 1 the function will have 3 parameters: partition name group id - the group id of the rules to check against number - the number to test against if use_partition is set to 0 the function will have 2 parameters: group id - the group id of the rules to check against number - the number to test against Note: The group id may be omitted - just as with the do_routing function.
Exported Events
<function moreinfo="none">E_DROUTING_STATUS</function> This event is raised when the module changes the state of a gateway, either through MI or probing. Parameters: gwid - the gateway identifier. address - the address of the gateway. status - disabled MI if the gateway was disabled using MI commands, probing if the gateway is being pinged, inactive if it was disabled from the script or active if the gateway is enabled.
Installation The module requires 4 tables in the OpenSIPS database: dr_groups, dr_gateways, dr_carriers, dr_rules. The SQL syntax to create them can be found in the drouting-create.sql script, located in the database directories of the opensips/scripts folder. You can also find the complete database documentation on the project webpage, &osipsdbdocslink;.
opensips-2.2.2/modules/drouting/doc/drouting_devel.xml000066400000000000000000000002721300170765700231700ustar00rootroot00000000000000 &develguide; The module provides no function to be used by other &osips; modules. opensips-2.2.2/modules/drouting/dr_api.h000066400000000000000000000037471300170765700203100ustar00rootroot00000000000000/** * drouting module developer api * * Copyright (C) 2014-2016 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-08-13 initial version (Andrei Datcu) */ #ifndef _DROUTING_API_H_ #define _DROUTING_API_H_ #include "routing.h" #include "dr_cb.h" #include "../../lock_ops.h" #include "../../rw_locking.h" #include "../../sr_module.h" typedef struct _dr_head_t { ptree_t *pt; ptree_node_t noprefix; } dr_head_t, *dr_head_p; /*Easier to spot outside dr */ typedef rt_info_t* (*match_number_f) (dr_head_p partition, unsigned int gr_id, const str *number, unsigned int *matched_len); typedef dr_head_p (*create_head_f) (void); typedef void (*free_head_f)(dr_head_p partition); typedef int (*add_rule_f)(dr_head_p partition, unsigned int rid, str *prefix, unsigned int gr_id, unsigned int priority, tmrec_t *time_rec, void *attr); struct dr_binds { create_head_f create_head; free_head_f free_head; match_number_f match_number; add_rule_f add_rule; register_drcb_f register_drcb; }; typedef int (*load_dr_api_f)(struct dr_binds *drb); static inline int load_dr_api(struct dr_binds *drb) { load_dr_api_f load_dr; if ( !(load_dr = (load_dr_api_f)find_export("load_dr", 0, 0))) return -1; return load_dr(drb); } #endif opensips-2.2.2/modules/drouting/dr_api_internal.c000066400000000000000000000112651300170765700221710ustar00rootroot00000000000000/** * drouting module developer api * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-08-13 initial version (Andrei Datcu) */ #include "dr_api_internal.h" #include "dr_api.h" #include "../../str.h" static rt_info_t *match_number (dr_head_p partition, unsigned int grp_id, const str *number, unsigned int *matched_len); static dr_head_p create_dr_head(void); static void free_dr_head(dr_head_p partition); static int add_rule_api(dr_head_p partition, unsigned int rid, str *prefix, unsigned int gr_id, unsigned int priority, tmrec_t *time_rec, void *attr); /* Warning this function assumes the lock is already taken */ rt_info_t* find_rule_by_prefix_unsafe(ptree_t *pt, ptree_node_t *noprefix, str prefix, unsigned int grp_id, unsigned int *matched_len) { unsigned int rule_idx = 0; rt_info_t *rt_info; rt_info = get_prefix(pt, &prefix, grp_id,matched_len, &rule_idx); if (rt_info==NULL) { LM_DBG("no matching for prefix \"%.*s\"\n", prefix.len, prefix.s); /* try prefixless rules */ rt_info = check_rt( noprefix, grp_id); if (rt_info == NULL) LM_DBG("no prefixless matching for " "grp %d\n", grp_id); } return rt_info; } int load_dr (struct dr_binds *drb) { drb->match_number = match_number; drb->create_head = create_dr_head; drb->free_head = free_dr_head; drb->add_rule = add_rule_api; drb->register_drcb = register_dr_cb; return 0; } /* Function which will try to match a number and return the rule id */ static rt_info_t *match_number (dr_head_p partition, unsigned int grp_id, const str *number, unsigned int *matched_len) { return find_rule_by_prefix_unsafe(partition->pt, &(partition->noprefix), *number, grp_id, matched_len); } static dr_head_p create_dr_head(void) { dr_head_p new = shm_malloc(sizeof(dr_head_t)); if( new == NULL ) { LM_ERR(" no more shm memory\n"); return NULL; } memset( new, 0, sizeof(dr_head_t)); /* data pointer in shm */ new->pt = shm_malloc(sizeof (ptree_t)); if (new->pt == NULL) { LM_ERR ("no more shm memory"); shm_free(new); return NULL; } memset(new->pt, 0, sizeof(ptree_t)); return new; } static void del_rt_list_api(rt_info_wrp_t *rwl) { rt_info_wrp_t* t=rwl; while(rwl!=NULL) { t=rwl; rwl=rwl->next; if ( (--t->rtl->ref_cnt)==0) shm_free(t->rtl); shm_free(t); } } static void del_tree_api(ptree_t* t) { int i,j; if(NULL == t) return; /* delete all the children */ for(i=0; i< PTREE_CHILDREN; i++) { /* shm_free the rg array of rt_info */ if(NULL!=t->ptnode[i].rg) { for(j=0;jptnode[i].rg_pos;j++) { /* if non intermediate delete the routing info */ if(t->ptnode[i].rg[j].rtlw !=NULL) del_rt_list_api(t->ptnode[i].rg[j].rtlw); } shm_free(t->ptnode[i].rg); } /* if non leaf delete all the children */ if(t->ptnode[i].next != NULL) del_tree_api(t->ptnode[i].next); } shm_free(t); } static void free_dr_head(dr_head_p partition) { int j; del_tree_api(partition->pt); if(NULL!=partition->noprefix.rg) { for(j=0;jnoprefix.rg_pos;j++) { if(partition->noprefix.rg[j].rtlw !=NULL) { del_rt_list_api(partition->noprefix.rg[j].rtlw); partition->noprefix.rg[j].rtlw = 0; } } shm_free(partition->noprefix.rg); partition->noprefix.rg = 0; } shm_free(partition); } static int add_rule_api(dr_head_p partition,unsigned int rid, str *prefix, unsigned int gr_id, unsigned int priority, tmrec_t *time_rec, void *attr) { rt_info_t * rule = shm_malloc(sizeof(rt_info_t)); if (rule == NULL){ LM_ERR("no more shm mem(1)\n"); return -1; } memset(rule, 0, sizeof(rt_info_t)); rule->id = rid; rule->priority = priority; rule->time_rec = time_rec; rule->attrs.s = (char*) attr; if (prefix->len) { if ( add_prefix(partition->pt, prefix, rule, gr_id)!=0 ) { LM_ERR("failed to add prefix route\n"); return -1; } } else { if ( add_rt_info( &partition->noprefix, rule, gr_id)!=0 ) { LM_ERR("failed to add prefixless route\n"); return -1; } } return 0; } opensips-2.2.2/modules/drouting/dr_api_internal.h000066400000000000000000000022551300170765700221750ustar00rootroot00000000000000/** * drouting module developer api * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-08-13 initial version (Andrei Datcu) */ #ifndef _DROUTING_INTERNAL_API_H_ #define _DROUTING_INTERNAL_API_H_ #include "dr_api.h" int load_dr (struct dr_binds *drb); rt_info_t* find_rule_by_prefix_unsafe(ptree_t *pt, ptree_node_t *noprefix, str prefix, unsigned int grp_id, unsigned int *matched_len); #endif opensips-2.2.2/modules/drouting/dr_bl.c000066400000000000000000000152431300170765700201210ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2009-01-19 first version (bogdan) */ #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../ut.h" #include "../../trim.h" #include "prefix_tree.h" #include "dr_bl.h" #include "dr_partitions.h" /* * link list for black_list definitions * obtained via modparam */ //extern int use_partitions; typedef struct blk_list_raw { char * def; struct blk_list_raw * next; }blk_list_raw_t; static blk_list_raw_t *bl_lists = NULL, *bl_lists_end=NULL; static struct dr_bl *drbl_lists = NULL; int set_dr_bl( modparam_t type, void* val) { blk_list_raw_t * new_bl_def = pkg_malloc(sizeof(blk_list_raw_t)); if (new_bl_def==NULL) { LM_ERR("failed to alloc element for blacklist (linked-list)\n"); return -1; } memset(new_bl_def, 0, sizeof(blk_list_raw_t)); new_bl_def->def = (char*)val; if( bl_lists==NULL ) { /* first time functions is called */ bl_lists = new_bl_def; bl_lists_end = bl_lists; } else { /* the list is not empty. the function was called before */ bl_lists_end->next = new_bl_def; bl_lists_end =new_bl_def; } return 0; } int init_dr_bls(struct head_db * head_db_start) { struct dr_bl *drbl; str name; str val; str part_name; char *p = NULL; blk_list_raw_t *it_blk, *to_clean; struct head_db * current_partition; if (bl_lists==NULL) return 0; it_blk = bl_lists; while( it_blk!=NULL ) { LM_DBG("processing bl definition <%s>\n",it_blk->def); /* get name */ if( use_partitions ) { p = strchr(it_blk->def, ':'); part_name.s = it_blk->def; part_name.len = p-part_name.s; if( p==NULL || p==it_blk->def ) { LM_ERR("blacklist definition <%s> has no partition name\n", it_blk->def); return -1; } trim(&part_name); p = NULL; if( (current_partition = get_partition(&part_name))==NULL ) { LM_ERR("could not find partition name <%.*s> from blacklist " "definition <%s>\n", part_name.len, part_name.s, it_blk->def); return -1; } } else { current_partition = head_db_start; if( current_partition == 0 ) { LM_CRIT("Default partition not registered\n"); } } p = strchr( it_blk->def, '='); if (p==NULL || p== it_blk->def) { LM_ERR("blacklist definition <%s> has no name",it_blk->def); return -1; } name.s = it_blk->def; name.len = p - name.s; trim(&name); if (name.len==0) { LM_ERR("empty name in blacklist definition <%s>\n",it_blk->def); return -1; } LM_DBG("found list name <%.*s>\n",name.len,name.s); /* alloc structure */ drbl = (struct dr_bl*)shm_malloc( sizeof(struct dr_bl) ); if (drbl==NULL) { LM_ERR("no more shme memory\n"); return -1; } memset( drbl, 0, sizeof(struct dr_bl)); /* fill in the types */ p++; do { if (drbl->no_types==MAX_TYPES_PER_BL) { LM_ERR("too many types per rule <%s>\n",it_blk->def); shm_free(drbl); return -1; } val.s = p; p = strchr( p, ','); if (p==NULL) { val.len = strlen(val.s); } else { val.len = (int)(long)(p - val.s); p++; } trim(&val); if (val.len==0) { LM_ERR("invalid types listing in <%s>\n",it_blk->def); shm_free(drbl); return -1; } LM_DBG("found type <%.*s>\n",val.len,val.s); if (str2int( &val, &drbl->types[drbl->no_types])!=0) { LM_ERR("nonnumerical type <%.*s>\n",val.len,val.s); shm_free(drbl); return -1; } drbl->no_types++; }while(p!=NULL); /* create backlist for it */ drbl->bl = create_bl_head( 131313, 0/*flags*/, NULL, NULL, &name); drbl->part = current_partition; to_clean = it_blk; it_blk = it_blk->next; if (drbl->bl==NULL) { LM_ERR("failed to create bl <%.*s>\n",name.len,name.s); shm_free(drbl); return -1; } if( to_clean ) { if( to_clean->def ) { pkg_free(to_clean->def); } memset( to_clean, 0, sizeof(blk_list_raw_t)); pkg_free(to_clean); } /* link it */ drbl->next = drbl_lists; drbl_lists = drbl; } bl_lists = NULL; bl_lists_end = NULL; return 0; } void destroy_dr_bls(void) { struct dr_bl *drbl; struct dr_bl *drbl1; for( drbl=drbl_lists ; drbl ; ) { drbl1 = drbl; drbl = drbl->next; shm_free(drbl1); } } int populate_dr_bls(map_t pgw_tree) { unsigned int i,j; struct dr_bl *drbl; pgw_t *gw; struct bl_rule *drbl_first; struct bl_rule *drbl_last; struct net *gw_net; void** dest; map_iterator_t it; /* each bl list at a time */ for( drbl=drbl_lists ; drbl ; drbl = drbl->next ) { if( drbl->part && (*drbl->part->rdata) && (*drbl->part->rdata)->pgw_tree == pgw_tree) { /* check if list applies to current partition */ drbl_first = drbl_last = NULL; /* each type at a time */ for ( i=0 ; ino_types ; i++ ) { /* search in the GW list all GWs of this type */ for (map_first(pgw_tree, &it); iterator_is_valid(&it); iterator_next(&it)) { dest = iterator_val(&it); if (dest==NULL) break; gw = (pgw_t*)*dest; if (gw->type==drbl->types[i]) { for ( j=0 ; jips_no ; j++ ) { gw_net = mk_net_bitlen( &gw->ips[j], gw->ips[j].len*8); if (gw_net==NULL) { LM_ERR("failed to build net mask\n"); continue; } /* add this destination to the BL */ if( add_rule_to_list( &drbl_first, &drbl_last, gw_net, NULL/*body*/, gw->ports[j], gw->protos[j], 0/*flags*/) != 0) { LM_ERR("Something went wrong in add_rule_to_list\n"); } else { } pkg_free(gw_net); } } } } /* the new content for the BL */ if (drbl->bl!=NULL && add_list_to_head( drbl->bl, drbl_first, drbl_last, 1, 0)!=0) { LM_ERR("failed to update bl\n"); return -1; } } } return 0; } opensips-2.2.2/modules/drouting/dr_bl.h000066400000000000000000000030261300170765700201220ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2009-01-19 first version (bogdan) */ #ifndef _DR_DR_BL_H #define _DR_DR_BL_H #include "../../sr_module.h" #include "../../blacklists.h" #include "prefix_tree.h" #include "dr_partitions.h" #define MAX_TYPES_PER_BL 32 struct dr_bl { unsigned int no_types; unsigned int types[MAX_TYPES_PER_BL]; struct head_db * part; struct bl_head *bl; struct dr_bl *next; }; int set_dr_bl( modparam_t type, void* val); int init_dr_bls( struct head_db *); void destroy_dr_bls(void); int populate_dr_bls(map_t pgw_tree); #endif opensips-2.2.2/modules/drouting/dr_cb.c000066400000000000000000000075521300170765700201140ustar00rootroot00000000000000/** * * drouting module callbacks * * Copyright (C) 2014-2106 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History * ------- * 2014-09-13 initial version (Mihai Tiganus) * 2016-02-18 ported to 2.2 (bogdan) */ #include #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "dr_cb.h" #define POINTER_CLOSED_MARKER ((void *)(-1)) unsigned char sort_algs[N_MAX_SORT_CBS] = {0,'O','W','Q'}; /* the array with all the cb lists (per type) */ static struct dr_callback *dr_cbs[DRCB_MAX]; /* an array of sorting cbs registered by different modules */ static struct dr_callback *dr_sort_cbs[N_MAX_SORT_CBS]; static void destroy_dr_callbacks_list(struct dr_callback *cb) { struct dr_callback *cb_t; while(cb) { cb_t = cb; cb = cb->next; if(cb_t->callback_param_free && cb_t->param) { cb_t->callback_param_free(cb_t->param); cb_t->param = NULL; } shm_free(cb_t); } } void destroy_dr_cbs(void) { int i; struct dr_callback *dr_sort_cb_it; for( i=0 ; icallback_param_free && dr_sort_cb_it->param) { dr_sort_cb_it->callback_param_free(dr_sort_cb_it->param); dr_sort_cb_it->param = NULL; } } } /* TODO: param will be the index in the array */ int register_dr_cb(enum drcb_types type, dr_cb f, void *param, dr_param_free_cb ff) { long int cb_sort_index = 0; struct dr_callback *cb; cb = (struct dr_callback*)shm_malloc(sizeof(struct dr_callback)); if (cb == 0) { LM_ERR("no more shm memory\n"); return -1; } cb->callback = f; cb->callback_param_free = ff; cb->next = NULL; if (type!=DRCB_SORT_DST) { cb->param = param; /* because now param holds the type of the * sorting function */ /* insert callback to the right list (based on type) */ if( dr_cbs[type]==POINTER_CLOSED_MARKER) { LM_CRIT("DRCB_SORT_DST registered after shut down!\n"); goto error; } cb->next = dr_cbs[type]; dr_cbs[type] = cb; } else { cb->param = NULL; if(param == NULL) { LM_ERR("no index supplied for sort callback registered at dr\n"); goto error; } cb_sort_index = (long int)param; if(cb_sort_index > N_MAX_SORT_CBS) { LM_ERR("Sort cbs array not large enough to accomodate cb at dr\n"); goto error; } if(dr_sort_cbs[cb_sort_index] != NULL) { LM_WARN("[dr] sort callback at index '%ld' will be overwritten\n", cb_sort_index); } dr_sort_cbs[cb_sort_index] = cb; } return 0; error: shm_free(cb); return -1; } /* runs a callback from an array - sort_cb_type will represent * the index within the array */ int run_dr_sort_cbs(sort_cb_type type, void *param) { if(dr_sort_cbs[type] == NULL) { LM_WARN("callback type '%d' not registered\n", type); return -1; } dr_sort_cbs[type]->callback(param); return 0; } int run_dr_cbs(enum drcb_types type, void *param) { struct dr_callback *it; it = dr_cbs[type]; while(it) { it->callback(param); it = it->next; } return 0; } opensips-2.2.2/modules/drouting/dr_cb.h000066400000000000000000000053421300170765700201140ustar00rootroot00000000000000/** * * drouting module callbacks header * * Copyright (C) 2014-2106 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History * ------- * 2014-09-24 initial version (Mihai Tiganus) * 2016-02-18 ported to 2.2 (bogdan) */ #ifndef _DR_CB_H_ #define _DR_CB_H_ /* callback types used on top of DRouting */ enum drcb_types { DRCB_REG_CREATE_PARTS_LIST /* create a partitions list */, /* params: */ DRCB_REG_INIT_RULE, DRCB_REG_GW, DRCB_REG_CR, DRCB_REG_ADD_RULE, DRCB_REG_MARK_AS_RULE_LIST, DRCB_REG_LINK_LISTS, DRCB_REG_FREE_LIST, DRCB_ACC_CALL, DRCB_SORT_DST, DRCB_SET_PROFILE, DRCB_MAX /*keep this at the end*/ }; /* callback function prototype */ typedef void (dr_cb) (void *param); /* function to free callback param */ typedef void(dr_param_free_cb) (void *param); /* register callback function protoype */ typedef int (*register_drcb_f)(enum drcb_types, dr_cb f, void *param, dr_param_free_cb ff); typedef int (*register_drcb_to_array_f)(enum drcb_types, dr_cb f, void *param, dr_param_free_cb ff); struct dr_callback { dr_cb* callback; void *param; dr_param_free_cb* callback_param_free; struct dr_callback * next; }; /* sorting related data */ /* if new callbacks are added you must increase the N_MAX_SORT_CBS * constant accordingly, add the letter which will be provided in the db * to the sort_algs array, add the corresponding sorting algorithm id to the * enum an register the callback to the dr_sort_cbs in the appropriate position*/ /* The maximum number of sorting functions provided by dr */ #define N_MAX_SORT_CBS 4 typedef enum { NO_SORT = 1, WEIGHT_BASED_SORT = 2, QR_BASED_SORT = 3} sort_cb_type; /* used for mapping the db information (sort_alg = a letter) to an index * in the sort_cb_type enum */ extern unsigned char sort_algs[N_MAX_SORT_CBS]; int register_dr_cb(enum drcb_types type, dr_cb f, void *param, dr_param_free_cb ff); int run_dr_cbs(enum drcb_types type, void *params); int run_dr_sort_cbs( sort_cb_type type, void *params); void destroy_dr_cbs(void); #endif opensips-2.2.2/modules/drouting/dr_cb_sorting.h000066400000000000000000000066501300170765700216640ustar00rootroot00000000000000/** * * drouting module sorting callbacks header * * Copyright (C) 2014-2016 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Sui * 2016-02-18 ported to 2.2 (bogdan) te 330, Boston, MA 02111-1307 USA * * History * ------- * 2014-10-10 initial version (Mihai Tiganus) * 2016-02-18 ported to 2.2 (bogdan) */ #ifndef _DR_SORTING_CBS_H_ #define _DR_SORTING_CBS_H_ #include "prefix_tree.h" /* if new callbacks are added you must increase the N_MAX_SORT_CBS * constant accordingly, add the letter which will be provided in the db * to the sort_algs array, add the corresponding sorting algorithm id to the * enum an register the callback to the dr_sort_cbs in the appropriate position*/ /* The maximum number of sorting functions provided by dr */ #define N_MAX_SORT_CBS 4 typedef enum { NO_SORT = 1, WEIGHT_BASED_SORT = 2, QR_BASED_SORT = 3} sort_cb_type; /* used for mapping the db information (sort_alg = a letter) to an index * in the sort_cb_type enum */ extern unsigned char sort_algs[N_MAX_SORT_CBS]; /* parameters needed for the registration of a gw */ struct dr_reg_param { void *rule; int n_dst; /* the index of the destination within the rule */ void *cr_or_gw; }; struct dr_reg_init_rule_params { void *rule; /* created at qr, set to dr */ int n_dst; /* the number of destination for the new rule; sent by dr */ int r_id; /* the rule id: sent by dr */ }; struct dr_acc_call_params { void *rule; /* qr_handler/rule */ int cr_id; /* destination id */ int gw_id; /* in the case the destination is a carrier */ struct sip_msg *msg; }; struct dr_sort_params { rt_info_t *dr_rule; /* dr_rule which contains the dst to be sorted */ unsigned short dst_id; /* the size of pgwl */ unsigned short *sorted_dst; /* returns an array with the * indexes of the sorted dest */ int rc; /* return code for the funciton */ }; struct dr_set_profile_params { void *qr_rule; /* qr_rule_t * to which the profile will be added. * provided by dr */ unsigned int profile; /* profile id, sent by dr to qr */ }; struct dr_add_rule_params { int part_index; /* partition index */ str part_name; void *qr_rule; /* rule to be added to list */ void *qr_parts; /* the partitions list to which the rule will be added */ }; struct dr_link_rule_list_params { void **first_list; /* list to be appended to */ void *second_list; /* list to be appended */ }; struct dr_mark_as_main_list_params{ void *qr_parts_new_list; /* list to become qr rules list */ void **qr_parts_old_list; /* old list: to be freed */ }; struct dr_free_qr_list_params { void *old_list; }; struct dr_create_partition_list_params { void **part_list; /* the list of partitions created at QR returned for DR*/ int n_parts; /* the number of partitions:provided by dr */ }; #endif opensips-2.2.2/modules/drouting/dr_db_def.c000066400000000000000000000062251300170765700207270ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "../../ut.h" #include "dr_db_def.h" /* DR group table related defs */ str drg_table = str_init("dr_groups"); str drg_user_col = str_init("username"); str drg_domain_col = str_init("domain"); str drg_grpid_col = str_init("groupid"); /* DR gateway table related defs */ #define ID_DRD_COL "id" #define GWID_DRD_COL "gwid" #define ADDRESS_DRD_COL "address" #define STRIP_DRD_COL "strip" #define PREFIX_DRD_COL "pri_prefix" #define TYPE_DRD_COL "type" #define ATTRS_DRD_COL "attrs" #define PROBE_DRD_COL "probe_mode" #define SOCKET_DRD_COL "socket" #define STATE_DRD_COL "state" str drd_table = str_init("dr_gateways"); str id_drd_col = str_init(ID_DRD_COL); str gwid_drd_col = str_init(GWID_DRD_COL); str address_drd_col = str_init(ADDRESS_DRD_COL); str strip_drd_col = str_init(STRIP_DRD_COL); str prefix_drd_col = str_init(PREFIX_DRD_COL); str type_drd_col = str_init(TYPE_DRD_COL); str attrs_drd_col = str_init(ATTRS_DRD_COL); str probe_drd_col = str_init(PROBE_DRD_COL); str sock_drd_col = str_init(SOCKET_DRD_COL); str state_drd_col = str_init(STATE_DRD_COL); /* DR rule table related defs */ #define RULE_ID_DRR_COL "ruleid" #define GROUP_DRR_COL "groupid" #define PREFIX_DRR_COL "prefix" #define TIME_DRR_COL "timerec" #define PRIORITY_DRR_COL "priority" #define ROUTEID_DRR_COL "routeid" #define DSTLIST_DRR_COL "gwlist" #define ATTRS_DRR_COL "attrs" str drr_table = str_init("dr_rules"); str rule_id_drr_col = str_init(RULE_ID_DRR_COL); str group_drr_col = str_init(GROUP_DRR_COL); str prefix_drr_col = str_init(PREFIX_DRR_COL); str time_drr_col = str_init(TIME_DRR_COL); str priority_drr_col = str_init(PRIORITY_DRR_COL); str routeid_drr_col = str_init(ROUTEID_DRR_COL); str dstlist_drr_col = str_init(DSTLIST_DRR_COL); str attrs_drr_col = str_init(ATTRS_DRR_COL); /* DR carrier table related defs */ #define ID_DRC_COL "id" #define CID_DRC_COL "carrierid" #define FLAGS_DRC_COL "flags" #define GWLIST_DRC_COL "gwlist" #define ATTRS_DRC_COL "attrs" #define STATE_DRC_COL "state" str drc_table = str_init("dr_carriers"); str id_drc_col = str_init(ID_DRC_COL); str cid_drc_col = str_init(CID_DRC_COL); str flags_drc_col = str_init(FLAGS_DRC_COL); str gwlist_drc_col = str_init(GWLIST_DRC_COL); str attrs_drc_col = str_init(ATTRS_DRC_COL); str state_drc_col = str_init(STATE_DRC_COL); opensips-2.2.2/modules/drouting/dr_db_def.h000066400000000000000000000035041300170765700207310ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef _DR_DB_DEFS #define _DR_DB_DEFS #include "../../str.h" /* DR group table related defs */ extern str drg_table; extern str drg_user_col; extern str drg_domain_col; extern str drg_grpid_col; /* DR gateway table related defs */ extern str drd_table; extern str id_drd_col; extern str gwid_drd_col; extern str address_drd_col; extern str strip_drd_col; extern str prefix_drd_col; extern str type_drd_col; extern str attrs_drd_col; extern str probe_drd_col; extern str sock_drd_col; extern str state_drd_col; /* DR rule table related defs */ extern str drr_table; extern str rule_id_drr_col; extern str group_drr_col; extern str prefix_drr_col; extern str time_drr_col; extern str priority_drr_col; extern str routeid_drr_col; extern str dstlist_drr_col; extern str attrs_drr_col; /* DR carrier table related defs */ extern str drc_table; extern str id_drc_col; extern str cid_drc_col; extern str flags_drc_col; extern str gwlist_drc_col; extern str attrs_drc_col; extern str state_drc_col; #endif opensips-2.2.2/modules/drouting/dr_load.c000066400000000000000000000463021300170765700204430ustar00rootroot00000000000000/* * Copyright (C) 2005-2008 Voice Sistem SRL * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2005-02-20 first version (cristian) * 2005-02-27 ported to 0.9.0 (bogdan) */ #include #include #include #include #include #include "../../dprint.h" #include "../../route.h" #include "../../db/db.h" #include "../../mem/shm_mem.h" #include "../../time_rec.h" #include "../../socket_info.h" #include "dr_load.h" #include "routing.h" #include "prefix_tree.h" #include "parse.h" #include "dr_db_def.h" #define check_val( _col, _val, _type, _not_null, _is_empty_str) \ do{\ if ((_val)->type!=_type) { \ LM_ERR("column %.*s has a bad type\n", _col.len, _col.s); \ goto error;\ } \ if (_not_null && (_val)->nul) { \ LM_ERR("column %.*s is null\n", _col.len, _col.s); \ goto error;\ } \ if (_is_empty_str && VAL_STRING(_val)==0) { \ LM_ERR("column %.*s (str) is empty\n", _col.len, _col.s); \ goto error;\ } \ }while(0) static inline tmrec_t* parse_time_def(char *time_str) { tmrec_p time_rec; char *p,*s; p = time_str; time_rec = 0; /* time_rec = (tmrec_t*)shm_malloc(sizeof(tmrec_t)); */ time_rec = tmrec_new(SHM_ALLOC); if (time_rec==0) { LM_ERR("no more shm mem\n"); goto error; } /* memset( time_rec, 0, sizeof(tmrec_t)); */ /* empty definition? */ if ( time_str==0 || *time_str==0 ) goto done; load_TR_value( p, s, time_rec, tr_parse_dtstart, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_duration, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_freq, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_until, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_interval, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_byday, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_bymday, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_byyday, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_byweekno, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_bymonth, parse_error, done); /* success */ done: return time_rec; parse_error: LM_ERR("parse error in <%s> around position %i\n", time_str, (int)(long)(p-time_str)); error: if (time_rec) tmrec_free( time_rec ); return 0; } static int add_rule(rt_data_t *rdata, char *grplst, str *prefix, rt_info_t *rule) { long int t; char *tmp; char *ep; int n; tmp=grplst; n=0; /* parse the grplst */ while(tmp && (*tmp!=0)) { errno = 0; t = strtol(tmp, &ep, 10); if (ep == tmp) { LM_ERR("bad grp id '%c' (%d)[%s]\n", *ep, (int)(ep-grplst), grplst); goto error; } if ((!IS_SPACE(*ep)) && (*ep != SEP) && (*ep != SEP1) && (*ep!=0)) { LM_ERR("bad char %c (%d) [%s]\n", *ep, (int)(ep-grplst), grplst); goto error; } if (errno == ERANGE && (t== LONG_MAX || t== LONG_MIN)) { LM_ERR("out of bounds\n"); goto error; } n++; /* add rule -> has prefix? */ if (prefix->len) { /* add the routing rule */ if ( add_prefix(rdata->pt, prefix, rule, (unsigned int)t)!=0 ) { LM_ERR("failed to add prefix route\n"); goto error; } } else { if ( add_rt_info( &rdata->noprefix, rule, (unsigned int)t)!=0 ) { LM_ERR("failed to add prefixless route\n"); goto error; } } /* keep parsing */ if(IS_SPACE(*ep)) EAT_SPACE(ep); if(ep && (*ep == SEP || *ep == SEP1)) ep++; tmp = ep; } if(n==0) { LM_ERR("no id in grp list [%s]\n", grplst); goto error; } return 0; error: return -1; } /* dr_gateways table */ #define INT_VALS_STRIP_DRD_COL 0 #define INT_VALS_TYPE_DRD_COL 1 #define INT_VALS_PROBE_DRD_COL 2 #define INT_VALS_STATE_DRD_COL 3 #define STR_VALS_ADDRESS_DRD_COL 0 #define STR_VALS_PREFIX_DRD_COL 1 #define STR_VALS_ATTRS_DRD_COL 2 #define STR_VALS_GWID_DRD_COL 3 #define STR_VALS_ID_DRD_COL 4 /* dr_carriers table */ #define INT_VALS_FLAGS_DRC_COL 0 #define INT_VALS_STATE_DRC_COL 1 #define STR_VALS_CID_DRC_COL 0 #define STR_VALS_GWLIST_DRC_COL 1 #define STR_VALS_ATTRS_DRC_COL 2 #define STR_VALS_ID_DRC_COL 3 /* dr_rules table */ #define INT_VALS_RULE_ID_DRR_COL 0 #define INT_VALS_BLANK_1 1 #define INT_VALS_PRIORITY_DRR_COL 2 #define INT_VALS_SCRIPT_ROUTE_ID 3 #define STR_VALS_GROUP_DRR_COL 0 #define STR_VALS_PREFIX_DRR_COL 1 #define STR_VALS_TIME_DRR_COL 2 #define STR_VALS_ROUTEID_DRR_COL 3 #define STR_VALS_DSTLIST_DRR_COL 4 #define STR_VALS_ATTRS_DRR_COL 5 /* loads routing info for given partition; if partition_name is NULL * loads all partitions */ rt_data_t* dr_load_routing_info(struct head_db *current_partition , int persistent_state) { int int_vals[5]; char * str_vals[6]; str tmp; db_func_t *dr_dbf = ¤t_partition->db_funcs; db_con_t* db_hdl = *current_partition->db_con; str *drd_table = ¤t_partition->drd_table; str *drc_table = ¤t_partition->drc_table; str *drr_table = ¤t_partition->drr_table; db_key_t columns[10]; db_res_t* res; db_row_t* row; rt_info_t *ri; rt_data_t *rdata; tmrec_t *time_rec; int i,n; int no_rows = 10; int db_cols; struct socket_info *sock; str s_sock, host; int proto, port; char id_buf[INT2STR_MAX_LEN]; res = 0; ri = 0; rdata = 0; /* init new data structure */ if ( (rdata=build_rt_data())==0 ) { LM_ERR("failed to build rdata\n"); goto error; } if (db_check_table_version(dr_dbf, db_hdl, drd_table, 6/*version*/ )!= 0) goto error; /* read the destinations */ if (dr_dbf->use_table( db_hdl, drd_table) < 0) { LM_ERR("cannot select table \"%.*s\"\n", drd_table->len,drd_table->s); goto error; } columns[0] = &id_drd_col; columns[1] = &gwid_drd_col; columns[2] = &address_drd_col; columns[3] = &strip_drd_col; columns[4] = &prefix_drd_col; columns[5] = &type_drd_col; columns[6] = &attrs_drd_col; columns[7] = &probe_drd_col; columns[8] = &sock_drd_col; if (persistent_state) { columns[9] = &state_drd_col; db_cols = 10; } else { db_cols = 9; } if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, db_cols, 0, 0 ) < 0) { LM_ERR("DB query failed\n"); goto error; } no_rows = estimate_available_rows( 4+32+15+4+32+4+128+4+32+4, db_cols); if (no_rows==0) no_rows = 10; if(dr_dbf->fetch_result(db_hdl, &res, no_rows )<0) { LM_ERR("Error fetching rows\n"); goto error; } } else { if ( dr_dbf->query(db_hdl,0,0,0,columns,0,db_cols,0,&res) < 0) { LM_ERR("DB query failed\n"); goto error; } } LM_DBG("%d records found in %.*s\n", RES_ROW_N(res), drd_table->len,drd_table->s); n = 0; do { for(i=0; i < RES_ROW_N(res); i++) { row = RES_ROWS(res) + i; /* DB ID column */ if ( VAL_TYPE( ROW_VALUES(row) ) == DB_INT ) { /* if INT type, convert it to string */ check_val( id_drd_col, ROW_VALUES(row), DB_INT, 1, 0); /* int2bstr returns a null terminated string */ str_vals[STR_VALS_ID_DRD_COL] = int2bstr((unsigned long)VAL_INT(ROW_VALUES(row)), id_buf, &int_vals[0]/*useless*/); } else { /* if not INT, accept only STRING type */ check_val( id_drd_col, ROW_VALUES(row), DB_STRING, 1, 0); str_vals[STR_VALS_ID_DRD_COL] = (char*)VAL_STRING(ROW_VALUES(row)); } /* GW ID column */ check_val( gwid_drd_col, ROW_VALUES(row)+1, DB_STRING, 1, 1); str_vals[STR_VALS_GWID_DRD_COL] = (char*)VAL_STRING(ROW_VALUES(row)+1); /* ADDRESS column */ check_val( address_drd_col, ROW_VALUES(row)+2, DB_STRING, 1, 1); str_vals[STR_VALS_ADDRESS_DRD_COL] = (char*)VAL_STRING(ROW_VALUES(row)+2); /* STRIP column */ check_val( strip_drd_col, ROW_VALUES(row)+3, DB_INT, 1, 0); int_vals[INT_VALS_STRIP_DRD_COL] = VAL_INT (ROW_VALUES(row)+3); /* PREFIX column */ check_val( prefix_drd_col, ROW_VALUES(row)+4, DB_STRING, 0, 0); str_vals[STR_VALS_PREFIX_DRD_COL] = (char*)VAL_STRING(ROW_VALUES(row)+4); /* TYPE column */ check_val( type_drd_col, ROW_VALUES(row)+5, DB_INT, 1, 0); int_vals[INT_VALS_TYPE_DRD_COL] = VAL_INT(ROW_VALUES(row)+5); /* ATTRS column */ check_val( attrs_drd_col, ROW_VALUES(row)+6, DB_STRING, 0, 0); str_vals[STR_VALS_ATTRS_DRD_COL] = (char*)VAL_STRING(ROW_VALUES(row)+6); /*PROBE_MODE column */ check_val( probe_drd_col, ROW_VALUES(row)+7, DB_INT, 1, 0); int_vals[INT_VALS_PROBE_DRD_COL] = VAL_INT(ROW_VALUES(row)+7); /*SOCKET column */ check_val( sock_drd_col, ROW_VALUES(row)+8, DB_STRING, 0, 0); if ( !VAL_NULL(ROW_VALUES(row)+8) && (s_sock.s=(char*)VAL_STRING(ROW_VALUES(row)+8))[0]!=0 ) { s_sock.len = strlen(s_sock.s); if (parse_phostport( s_sock.s, s_sock.len, &host.s, &host.len, &port, &proto)!=0){ LM_ERR("GW <%s>(%s): socket description <%.*s> " "is not valid -> ignoring socket\n", str_vals[STR_VALS_GWID_DRD_COL], str_vals[STR_VALS_ID_DRD_COL], s_sock.len,s_sock.s); sock = NULL; } else { sock = grep_sock_info( &host, port, proto); if (sock == NULL) { LM_ERR("GW <%s>(%s): socket <%.*s> is not local to " "OpenSIPS (we must listen on it) -> ignoring socket\n", str_vals[STR_VALS_GWID_DRD_COL], str_vals[STR_VALS_ID_DRD_COL], s_sock.len,s_sock.s); } } } else { sock = NULL; } /*STATE column */ if (persistent_state) { check_val( state_drd_col, ROW_VALUES(row)+9, DB_INT, 1, 0); int_vals[INT_VALS_STATE_DRD_COL] = VAL_INT(ROW_VALUES(row)+9); } else { int_vals[INT_VALS_STATE_DRD_COL] = 0; /* by default enabled */ } /* add the destinaton definition in */ if ( add_dst( rdata, str_vals[STR_VALS_GWID_DRD_COL], str_vals[STR_VALS_ADDRESS_DRD_COL], int_vals[INT_VALS_STRIP_DRD_COL], str_vals[STR_VALS_PREFIX_DRD_COL], int_vals[INT_VALS_TYPE_DRD_COL], str_vals[STR_VALS_ATTRS_DRD_COL], int_vals[INT_VALS_PROBE_DRD_COL], sock, int_vals[INT_VALS_STATE_DRD_COL] )<0 ) { LM_ERR("failed to add destination <%s>(%s) -> skipping\n", str_vals[STR_VALS_GWID_DRD_COL], str_vals[STR_VALS_ID_DRD_COL]); continue; } n++; } if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if(dr_dbf->fetch_result(db_hdl, &res, no_rows)<0) { LM_ERR( "fetching rows (1)\n"); goto error; } } else { break; } } while(RES_ROW_N(res)>0); dr_dbf->free_result(db_hdl, res); res = 0; /* read the carriers, if any */ if (dr_dbf->use_table( db_hdl, drc_table) < 0) { LM_ERR("cannot select table \"%.*s\"\n", drc_table->len,drc_table->s); goto error; } columns[0] = &id_drc_col; columns[1] = &cid_drc_col; columns[2] = &flags_drc_col; columns[3] = &gwlist_drc_col; columns[4] = &attrs_drc_col; if (persistent_state) { columns[5] = &state_drc_col; db_cols = 6; } else { db_cols = 5; } if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, db_cols, 0, 0 ) < 0) { LM_ERR("DB query failed\n"); goto error; } no_rows = estimate_available_rows( 4+4+32+64+64, db_cols); if (no_rows==0) no_rows = 10; if(dr_dbf->fetch_result(db_hdl, &res, no_rows)<0) { LM_ERR("Error fetching rows\n"); goto error; } } else { if ( dr_dbf->query(db_hdl,0,0,0,columns,0,db_cols,0,&res) < 0) { LM_ERR("DB query failed\n"); goto error; } } if (RES_ROW_N(res) == 0) { LM_DBG("table \"%.*s\" empty\n", drc_table->len,drc_table->s ); } else { LM_DBG("%d records found in %.*s\n", RES_ROW_N(res), drc_table->len,drc_table->s); do { for(i=0; i < RES_ROW_N(res); i++) { row = RES_ROWS(res) + i; /* DB ID column */ if ( VAL_TYPE( ROW_VALUES(row) ) == DB_INT ) { /* if INT type, convert it to string */ check_val( id_drc_col, ROW_VALUES(row), DB_INT, 1, 0); /* int2bstr returns a null terminated string */ str_vals[STR_VALS_ID_DRC_COL] = int2bstr((unsigned long)VAL_INT(ROW_VALUES(row)), id_buf, &int_vals[0]/*useless*/); } else { /* if not INT, accept only STRING type */ check_val( id_drd_col, ROW_VALUES(row), DB_STRING, 1, 0); str_vals[STR_VALS_ID_DRC_COL] = (char*)VAL_STRING(ROW_VALUES(row)); } /* CARRIER_ID column */ check_val( cid_drc_col, ROW_VALUES(row)+1, DB_STRING, 1, 1); str_vals[STR_VALS_CID_DRC_COL] = (char*)VAL_STRING(ROW_VALUES(row)+1); /* flags column */ check_val( flags_drc_col, ROW_VALUES(row)+2, DB_INT, 1, 0); int_vals[INT_VALS_FLAGS_DRC_COL] = VAL_INT(ROW_VALUES(row)+2); /* GWLIST column */ check_val( gwlist_drc_col, ROW_VALUES(row)+3, DB_STRING, 1, 1); str_vals[STR_VALS_GWLIST_DRC_COL] = (char*)VAL_STRING(ROW_VALUES(row)+3); /* ATTRS column */ check_val( attrs_drc_col, ROW_VALUES(row)+4, DB_STRING, 0, 0); str_vals[STR_VALS_ATTRS_DRC_COL] = (char*)VAL_STRING(ROW_VALUES(row)+4); /* STATE column */ if (persistent_state) { check_val( state_drc_col, ROW_VALUES(row)+5, DB_INT, 1, 0); int_vals[INT_VALS_STATE_DRC_COL] = VAL_INT(ROW_VALUES(row)+5); } else { /* by default enabled */ int_vals[INT_VALS_STATE_DRC_COL] = 0; } /* add the new carrier */ if ( add_carrier( str_vals[STR_VALS_CID_DRC_COL], int_vals[INT_VALS_FLAGS_DRC_COL], str_vals[STR_VALS_GWLIST_DRC_COL], str_vals[STR_VALS_ATTRS_DRC_COL], int_vals[INT_VALS_STATE_DRC_COL], rdata) != 0 ) { LM_ERR("failed to add carrier db_id <%s> -> skipping\n", str_vals[STR_VALS_ID_DRC_COL]); continue; } } if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if(dr_dbf->fetch_result(db_hdl, &res, no_rows)<0) { LM_ERR( "fetching rows (1)\n"); goto error; } } else { break; } } while(RES_ROW_N(res)>0); } dr_dbf->free_result(db_hdl, res); res = 0; /* read the routing rules */ if (dr_dbf->use_table( db_hdl, drr_table) < 0) { LM_ERR("cannot select table \"%.*s\"\n", drr_table->len, drr_table->s); goto error; } columns[0] = &rule_id_drr_col; columns[1] = &group_drr_col; columns[2] = &prefix_drr_col; columns[3] = &time_drr_col; columns[4] = &priority_drr_col; columns[5] = &routeid_drr_col; columns[6] = &dstlist_drr_col; columns[7] = &attrs_drr_col; if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, 8, 0, 0) < 0) { LM_ERR("DB query failed\n"); goto error; } no_rows = estimate_available_rows( 4+32+32+128+32+64+128, 8/*cols*/); if (no_rows==0) no_rows = 10; if(dr_dbf->fetch_result(db_hdl, &res, no_rows)<0) { LM_ERR("Error fetching rows\n"); goto error; } } else { if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, 8, 0, &res) < 0) { LM_ERR("DB query failed\n"); goto error; } } if (RES_ROW_N(res) == 0) { LM_WARN("table \"%.*s\" is empty\n", drr_table->len, drr_table->s); } LM_DBG("initial %d records found in %.*s\n", RES_ROW_N(res), drr_table->len, drr_table->s); n = 0; do { for(i=0; i < RES_ROW_N(res); i++) { row = RES_ROWS(res) + i; /* RULE_ID column */ check_val( rule_id_drr_col, ROW_VALUES(row), DB_INT, 1, 0); int_vals[INT_VALS_RULE_ID_DRR_COL] = VAL_INT (ROW_VALUES(row)); /* GROUP column */ check_val( group_drr_col, ROW_VALUES(row)+1, DB_STRING, 1, 1); str_vals[STR_VALS_GROUP_DRR_COL] = (char*)VAL_STRING(ROW_VALUES(row)+1); /* PREFIX column - it may be null or empty */ check_val( prefix_drr_col, ROW_VALUES(row)+2, DB_STRING, 0, 0); if ((ROW_VALUES(row)+2)->nul || VAL_STRING(ROW_VALUES(row)+2)==0){ tmp.s = NULL; tmp.len = 0; } else { str_vals[STR_VALS_PREFIX_DRR_COL] = (char*)VAL_STRING(ROW_VALUES(row)+2); tmp.s = str_vals[STR_VALS_PREFIX_DRR_COL]; tmp.len = strlen(str_vals[STR_VALS_PREFIX_DRR_COL]); } /* TIME column */ check_val( time_drr_col, ROW_VALUES(row)+3, DB_STRING, 0, 0); str_vals[STR_VALS_TIME_DRR_COL] = (char*)VAL_STRING(ROW_VALUES(row)+3); /* PRIORITY column */ check_val( priority_drr_col, ROW_VALUES(row)+4, DB_INT, 1, 0); int_vals[INT_VALS_PRIORITY_DRR_COL] = VAL_INT (ROW_VALUES(row)+4); /* ROUTE_ID column */ check_val( routeid_drr_col, ROW_VALUES(row)+5, DB_STRING, 0, 0); str_vals[STR_VALS_ROUTEID_DRR_COL] = (char*)VAL_STRING(ROW_VALUES(row)+5); /* DSTLIST column */ check_val( dstlist_drr_col, ROW_VALUES(row)+6, DB_STRING, 1, 1); str_vals[STR_VALS_DSTLIST_DRR_COL] = (char*)VAL_STRING(ROW_VALUES(row)+6); /* ATTRS column */ check_val( attrs_drr_col, ROW_VALUES(row)+7, DB_STRING, 0, 0); str_vals[STR_VALS_ATTRS_DRR_COL] = (char*)VAL_STRING(ROW_VALUES(row)+7); /* parse the time definition */ if (str_vals[STR_VALS_TIME_DRR_COL] == NULL || *(str_vals[STR_VALS_TIME_DRR_COL]) == 0) time_rec = NULL; else if ((time_rec=parse_time_def(str_vals[STR_VALS_TIME_DRR_COL]))==0) { LM_ERR("bad time definition <%s> for rule id %d -> skipping\n", str_vals[STR_VALS_TIME_DRR_COL], int_vals[INT_VALS_RULE_ID_DRR_COL]); continue; } /* lookup for the script route ID */ if (str_vals[STR_VALS_ROUTEID_DRR_COL] && str_vals[STR_VALS_ROUTEID_DRR_COL][0]) { int_vals[INT_VALS_SCRIPT_ROUTE_ID] = get_script_route_ID_by_name( str_vals[STR_VALS_ROUTEID_DRR_COL], rlist, RT_NO); if (int_vals[INT_VALS_SCRIPT_ROUTE_ID]==-1) { LM_WARN("route <%s> does not exist\n", str_vals[STR_VALS_ROUTEID_DRR_COL]); int_vals[INT_VALS_SCRIPT_ROUTE_ID] = 0; } } else { int_vals[INT_VALS_SCRIPT_ROUTE_ID] = 0; } /* build the routing rule */ if ((ri = build_rt_info( int_vals[INT_VALS_RULE_ID_DRR_COL], int_vals[INT_VALS_PRIORITY_DRR_COL], time_rec, int_vals[INT_VALS_SCRIPT_ROUTE_ID], str_vals[STR_VALS_DSTLIST_DRR_COL], str_vals[STR_VALS_ATTRS_DRR_COL], rdata))== 0 ) { LM_ERR("failed to add routing info for rule id %d -> " "skipping\n", int_vals[INT_VALS_RULE_ID_DRR_COL]); tmrec_free( time_rec ); continue; } /* add the rule */ if (add_rule( rdata, str_vals[STR_VALS_GROUP_DRR_COL], &tmp, ri)!=0) { LM_ERR("failed to add rule id %d -> skipping\n", int_vals[INT_VALS_RULE_ID_DRR_COL]); free_rt_info( ri ); continue; } n++; } if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if(dr_dbf->fetch_result(db_hdl, &res, no_rows)<0) { LM_ERR( "fetching rows (1)\n"); goto error; } LM_DBG("additional %d records found in %.*s\n", RES_ROW_N(res), drr_table->len, drr_table->s); } else { break; } } while(RES_ROW_N(res)>0); dr_dbf->free_result(db_hdl, res); res = 0; LM_DBG("%d total records loaded from table %.*s\n", n, drr_table->len, drr_table->s); return rdata; error: if (res) dr_dbf->free_result(db_hdl, res); if (rdata) free_rt_data( rdata, 1 ); rdata = NULL; return 0; } opensips-2.2.2/modules/drouting/dr_load.h000066400000000000000000000024651300170765700204520ustar00rootroot00000000000000/* * Copyright (C) 2005-2008 Voice Sistem SRL * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2005-02-20 first version (cristian) * 2005-02-27 ported to 0.9.0 (bogdan) */ #ifndef _DR_LOAD_ #define _DR_LOAD_ #include "../../str.h" #include "../../db/db.h" #include "dr_partitions.h" #include "routing.h" rt_data_t* dr_load_routing_info(struct head_db * ,int persistent_state); #endif opensips-2.2.2/modules/drouting/dr_partitions.h000066400000000000000000000052601300170765700217230ustar00rootroot00000000000000/** * dispatcher module fixup functions * * Copyright (C) 2004-2005 FhG Fokus * Copyright (C) 2006-2010 Voice Sistem SRL * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-07-23 initial version (Mihai Tiganus) */ #ifndef DR_DR_PARTITIONS_H #define DR_DR_PARTITIONS_H #include "routing.h" #include "../../sr_module.h" #include "../../str.h" #include "../../dprint.h" #include "../../usr_avp.h" #include "../../db/db.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../rw_locking.h" #include "../../action.h" #include "../../error.h" #include "../../ut.h" #include "../../resolve.h" #include "../../mod_fix.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" #include "../../mi/mi.h" #include "../tm/tm_load.h" int use_partitions; struct head_db { str db_url; str partition; db_func_t db_funcs; db_con_t **db_con; str drd_table; /* drd_table name extracted from database */ str drr_table; /* drr_table name extracted from database */ str drc_table; /* drc_table name extracted from database */ str drg_table; /* drg_table name extracted from database */ time_t time_last_update; int avpID_store_ruri; /* from parse_avp_spec */ int avpID_store_prefix; /* from parse_avp_spec */ int avpID_store_index; /* from parse_avp_spec */ int avpID_store_whitelist; /* from parse_avp_spec */ int avpID_store_group; /* from parse_avp_spec */ int avpID_store_flags; /* from parse_avp_spec */ int gw_priprefix_avp; /* from parse_avp_spec */ int rule_id_avp; /* from parse_avp_spec */ int rule_prefix_avp; /* from parse_avp_spec */ int carrier_id_avp; /* from parse_avp_spec */ int ruri_avp; int gw_id_avp; int gw_sock_avp; int gw_attrs_avp; int rule_attrs_avp; int carrier_attrs_avp; rt_data_t **rdata; rw_lock_t *ref_lock; int ongoing_reload; struct head_db *next; }; struct head_db * get_partition(const str *); #endif opensips-2.2.2/modules/drouting/drouting.c000066400000000000000000004154541300170765700207020ustar00rootroot00000000000000/* * Copyright (C) 2005-2009 Voice Sistem SRL * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2005-02-20 first version (cristian) * 2005-02-27 ported to 0.9.0 (bogdan) */ #include #include #include #include #include "../../evi/evi.h" #include "../../map.h" #include "dr_load.h" #include "prefix_tree.h" #include "dr_bl.h" #include "dr_db_def.h" #include "dr_partitions.h" #include "dr_api.h" #include "dr_api_internal.h" #define DR_PARAM_USE_WEIGTH (1<<0) #define DR_PARAM_RULE_FALLBACK (1<<1) #define DR_PARAM_STRICT_LEN (1<<2) #define DR_PARAM_ONLY_CHECK (1<<3) #define DR_PARAM_INTERNAL_TRIGGERED (1<<30) #define DRD_TABLE_VER 6 #define DRR_TABLE_VER 3 #define DRG_TABLE_VER 2 #define DRC_TABLE_VER 2 #define PART_TABLE_VER 1 #define MAX_LEN_NAME_W_PART 510 /* max len of variable containing avp_spec and partition name */ #define MI_NO_PART_S "Too many arguments (use_partitions is 0 so no parameter"\ " should be supplied to the MI function)" #define MI_NO_PART_LEN (strlen(MI_NO_PART_S)) #define MI_PART_NAME_S "Partition" #define MI_PART_NAME_LEN (strlen(MI_PART_NAME_S)) #define MI_LAST_UPDATE_S "Date" #define MI_LAST_UPDATE_LEN (strlen(MI_LAST_UPDATE_S)) /* probing related stuff */ static unsigned int dr_prob_interval = 30; static str dr_probe_replies = {NULL,0}; struct tm_binds dr_tmb; str dr_probe_method = str_init("OPTIONS"); str dr_probe_from = str_init("sip:prober@localhost"); static int* probing_reply_codes = NULL; static int probing_codes_no = 0; /* reload controll parametere */ static int no_concurrent_reload = 0; /*** DB relatede stuff ***/ /* parameters */ static str db_url = {NULL,0}; static int dr_persistent_state = 1; /* DRG use domain */ static int use_domain = 1; int dr_default_grp = -1; int dr_force_dns = 1; /* internal AVP used to store serial RURIs */ static str ruri_avp_spec = str_init("$avp(___dr_ruri__)"); /* internal AVP used to store GW IDs */ static str gw_id_avp_spec = str_init("$avp(___dr_gw_id__)"); /* internal AVP used to store GW socket */ static str gw_sock_avp_spec = str_init("$avp(___dr_sock__)"); /* internal AVP used to store GW ATTRs */ static str gw_attrs_avp_spec = str_init("$avp(___dr_gw_att__)"); /* AVP used to store GW Pri Prefix */ static str gw_priprefix_avp_spec = { NULL, 0}; /* AVP used to store RULE IDs */ static str rule_id_avp_spec = {NULL, 0}; /* internal AVP used to store RULE ATTRs */ static str rule_attrs_avp_spec = str_init("$avp(___dr_ru_att__)"); /* AVP used to store RULE prefix */ static str rule_prefix_avp_spec = {NULL, 0}; /* AVP used to store CARRIER ID */ static str carrier_id_avp_spec = {NULL, 0}; /* internal AVP used to store CARRIER ATTRs */ static str carrier_attrs_avp_spec = str_init("$avp(___dr_cr_att__)"); /* AVP used to store PARTITION ID when using wildcard operator instead of * partition name */ static str partition_pvar = {NULL, 0}; pv_spec_t partition_spec; /* * global pointers for faster parameter passing between functions * meaning: current script pvar to dump attrs in (NULL to ignore) */ static pv_spec_p rule_attrs_spec; static pv_spec_p gw_attrs_spec; static pv_spec_p carrier_attrs_spec; /* * if the attributes are not used at all in the script, * do not store them in their internal AVPs at all --liviu */ static int populate_rule_attrs; static int populate_gw_attrs; static int populate_carrier_attrs; /* statistic data */ int tree_size = 0; int inode = 0; int unode = 0; static str attrs_empty = str_init(""); /* configuration loader from db specific stuff */ static str db_partitions_table = str_init("dr_partitions"); /* default url */ static str db_partitions_url; //static int use_partitions = 0; // int use_partitions = 0; /* by default don't use db for config */ static struct head_config { str partition; /* partition name extracted from database */ str db_url; str drd_table; /* drd_table name extracted from database */ str drr_table; /* drr_table name extracted from database */ str drc_table; /* drc_table name extracted from database */ str drg_table; /* drg_table name extracted from database */ str gw_priprefix_avp_spec; /* extracted from database - it can be NULL */ str rule_id_avp_spec; /* extracted from database - it can be NULL */ str rule_prefix_avp_spec; /* extracted from database - it can be NULL */ str carrier_id_avp_spec; /* extracted from database - it can be NULL */ str ruri_avp_spec; /* extracted from database - has default value */ str gw_id_avp_spec; /* extracted from database - has default value */ str gw_sock_avp_spec; /* extracted from database - has default value */ str gw_attrs_avp_spec; /* extracted from database - has default value */ str rule_attrs_avp_spec; /* extracted from database - has default value */ str carrier_attrs_avp_spec; /* extracted from database - has default value */ struct head_config *next; }* head_start = NULL,* head_end = NULL; struct head_db * head_db_start = NULL,* head_db_end = NULL; typedef struct param_prob_callback { struct head_db * current_partition; unsigned int _id; }param_prob_callback_t; typedef struct dr_partition { union { struct head_db * part; gparam_p part_name; } v; enum dr_partition_type { DR_PTR_PART, DR_GPARAM_PART, DR_WILDCARD_PART, DR_NO_PART } type; } dr_partition_t; typedef struct dr_part_group { dr_partition_t * dr_part; dr_group_t * group; } dr_part_group_t; static dr_part_group_t * default_part; /* for do_routing, used when use_partitions = 0 */ typedef struct dr_part_old { dr_partition_t *dr_part; gparam_p gw_or_cr; /* gateway or carrier */ } dr_part_old_t; typedef struct dr_part_cr { gparam_p part; gparam_p cr; } dr_part_cr_t; typedef struct dr_part_gw { gparam_p part; gparam_p gw; } dr_part_gw_t; static int get_config_from_db(); static int add_head_config(); static int add_head_db(); static int db_load_head(struct head_db*); /* used for populating head_db with db connections and db funcs */ static void trim_char(char**); static int fixup_dr_disable(void **,int); //static struct head_db * get_partition(const str *); static int _is_dr_gw_w_part(struct sip_msg* , char * , char* , int , struct ip_addr* , unsigned int); static int use_next_gw_w_part( struct sip_msg*, struct head_db *, char *, char *, char *); static int dr_disable(struct sip_msg *req, char * current_partition); static int dr_disable_w_part(struct sip_msg *req, struct head_db *current_partition); static int to_partition(struct sip_msg*, dr_partition_t *, struct head_db **); static inline int init_part_grp(dr_part_group_t **, struct head_db *, dr_group_t*); /* reader-writers lock for reloading the data */ static rw_lock_t *ref_lock = NULL; static int dr_init(void); static int dr_child_init(int rank); static int dr_exit(void); static int fixup_do_routing(void** param, int param_no); static int fixup_next_gw(void** param, int param_no); static int fixup_from_gw(void** param, int param_no); static int fixup_is_gw(void** param, int param_no); static int fixup_route2_carrier( void** param, int param_no); static int fixup_route2_gw( void** param, int param_no); static int do_routing(struct sip_msg* msg,dr_part_group_t*, int sort, gparam_t* wl); static int do_routing_0(struct sip_msg* msg); static int do_routing_1(struct sip_msg* msg, char * , char* id, char* fl, char* wl, char* rule_att, char* gw_att, char* carr_att); static int use_next_gw(struct sip_msg* msg, char* rule_or_part, char* rule_or_gw, char* gw_or_carr, char * carr); static int is_from_gw_0(struct sip_msg* msg); static int is_from_gw_1(struct sip_msg* msg, char * part); static int is_from_gw_2(struct sip_msg* msg, char * part, char* str1); static int is_from_gw_3(struct sip_msg* msg, char *, char*, char* ); static int is_from_gw_4(struct sip_msg*, char*, char*, char*, char*); static int goes_to_gw_0(struct sip_msg* msg); static int goes_to_gw_1(struct sip_msg* msg, char * part, char* f1, char* f2, char* f3); static int dr_is_gw(struct sip_msg* msg, char * part, char* str1, char* str2, char* str3, char* str4); static int route2_carrier(struct sip_msg* msg, char* cr_str, char* gw_att_pv, char* carr_att_pv); static int route2_gw(struct sip_msg* msg, char* gw, char* gw_att_pv); static struct mi_root* dr_reload_cmd(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_dr_gw_status(struct mi_root *cmd, void *param); static struct mi_root* mi_dr_cr_status(struct mi_root *cmd, void *param); static struct mi_root* mi_dr_number_routing(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_dr_reload_status(struct mi_root *cmd_tree, void *param); /* event */ static str dr_event = str_init("E_DROUTING_STATUS"); static event_id_t dr_evi_id; /* * Exported functions */ static cmd_export_t cmds[] = { {"do_routing", (cmd_function)do_routing_0, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"do_routing", (cmd_function)do_routing_1, 1, fixup_do_routing, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"do_routing", (cmd_function)do_routing_1, 2, fixup_do_routing, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"do_routing", (cmd_function)do_routing_1, 3, fixup_do_routing, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"do_routing", (cmd_function)do_routing_1, 4, fixup_do_routing, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"do_routing", (cmd_function)do_routing_1, 5, fixup_do_routing, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"do_routing", (cmd_function)do_routing_1, 6, fixup_do_routing, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"do_routing", (cmd_function)do_routing_1, 7, fixup_do_routing, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"use_next_gw", (cmd_function)use_next_gw, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"use_next_gw", (cmd_function)use_next_gw, 1, fixup_next_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"use_next_gw", (cmd_function)use_next_gw, 2, fixup_next_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"use_next_gw", (cmd_function)use_next_gw, 3, fixup_next_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"use_next_gw", (cmd_function)use_next_gw, 4, fixup_next_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"next_routing", (cmd_function)use_next_gw, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"next_routing", (cmd_function)use_next_gw, 1, fixup_next_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"next_routing", (cmd_function)use_next_gw, 2, fixup_next_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"next_routing", (cmd_function)use_next_gw, 3, fixup_next_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"is_from_gw", (cmd_function)is_from_gw_0, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE}, {"is_from_gw", (cmd_function)is_from_gw_1, 1, fixup_from_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE}, {"is_from_gw", (cmd_function)is_from_gw_2, 2, fixup_from_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE}, {"is_from_gw", (cmd_function)is_from_gw_3, 3, fixup_from_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE}, {"is_from_gw", (cmd_function)is_from_gw_4, 4, fixup_from_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE}, {"goes_to_gw", (cmd_function)goes_to_gw_0, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"goes_to_gw", (cmd_function)goes_to_gw_1, 1, fixup_from_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"goes_to_gw", (cmd_function)goes_to_gw_1, 2, fixup_from_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"goes_to_gw", (cmd_function)goes_to_gw_1, 3, fixup_from_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"goes_to_gw", (cmd_function)goes_to_gw_1, 4, fixup_from_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"dr_is_gw", (cmd_function)dr_is_gw, 1, fixup_is_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"dr_is_gw", (cmd_function)dr_is_gw, 2, fixup_is_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"dr_is_gw", (cmd_function)dr_is_gw, 3, fixup_is_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"dr_is_gw", (cmd_function)dr_is_gw, 4, fixup_is_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"dr_is_gw", (cmd_function)dr_is_gw, 5, fixup_is_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"dr_disable", (cmd_function)dr_disable, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE}, {"dr_disable", (cmd_function)dr_disable, 1, fixup_dr_disable, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE}, {"route_to_carrier",(cmd_function)route2_carrier,1,fixup_route2_carrier, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"route_to_carrier",(cmd_function)route2_carrier,2,fixup_route2_carrier, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"route_to_carrier",(cmd_function)route2_carrier,3,fixup_route2_carrier, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"route_to_gw", (cmd_function)route2_gw, 1,fixup_route2_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"route_to_gw", (cmd_function)route2_gw, 2,fixup_route2_gw, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"load_dr", (cmd_function)load_dr, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"use_partitions", INT_PARAM, &use_partitions }, {"db_partitions_url", STR_PARAM, &db_partitions_url.s }, {"db_partitions_table", STR_PARAM, &db_partitions_table.s }, {"db_url", STR_PARAM, &db_url.s }, {"drd_table", STR_PARAM, &drd_table.s }, {"drr_table", STR_PARAM, &drr_table.s }, {"drg_table", STR_PARAM, &drg_table.s }, {"drc_table", STR_PARAM, &drc_table.s }, {"use_domain", INT_PARAM, &use_domain }, {"drg_user_col", STR_PARAM, &drg_user_col.s }, {"drg_domain_col", STR_PARAM, &drg_domain_col.s }, {"drg_grpid_col", STR_PARAM, &drg_grpid_col.s }, {"ruri_avp", STR_PARAM, &ruri_avp_spec.s }, {"gw_id_avp", STR_PARAM, &gw_id_avp_spec.s }, {"gw_priprefix_avp", STR_PARAM, &gw_priprefix_avp_spec.s }, {"gw_sock_avp", STR_PARAM, &gw_sock_avp_spec.s }, {"rule_id_avp", STR_PARAM, &rule_id_avp_spec.s }, {"rule_prefix_avp", STR_PARAM, &rule_prefix_avp_spec.s }, {"carrier_id_avp", STR_PARAM, &carrier_id_avp_spec.s }, {"force_dns", INT_PARAM, &dr_force_dns }, {"default_group", INT_PARAM, &dr_default_grp }, {"define_blacklist", STR_PARAM|USE_FUNC_PARAM, (void*)set_dr_bl }, {"probing_interval", INT_PARAM, &dr_prob_interval }, {"probing_method", STR_PARAM, &dr_probe_method.s }, {"probing_from", STR_PARAM, &dr_probe_from.s }, {"probing_reply_codes",STR_PARAM, &dr_probe_replies.s }, {"persistent_state", INT_PARAM, &dr_persistent_state }, {"no_concurrent_reload",INT_PARAM, &no_concurrent_reload }, {"partition_id_pvar", STR_PARAM, &partition_pvar.s}, {0, 0, 0} }; /* * Exported MI functions */ #define HLP1 "Params: none ; Forces drouting module to reload data from DB "\ "into memory; A return string is returned only in case of error." #define HLP2 "Params: [ gw_id [ status ]] ; Sets/gets the status of a GW; "\ "If no gw_id is given, all gws will be listed; if a new status is give, "\ "it will be pushed to the given GW." #define HLP3 "Params: [ carrier_id [ status ]] ; Sets/gets the status of a " \ "carrier; If no carrier_id is given, all carrier will be listed; if a " \ "new status is give, it will be pushed to the given carrier." #define HLP4 "Params: [partition] [group_id] number ; List the gateways a "\ "number will match when searching through the rules from a specific group. "\ "The partition parameter must be defined only if use_partitions = 1." #define HLP5 "Params: [partition]; List the time of the last dr_reload"\ " (load from database) for all partitions if no parameter is supplied, or"\ " for a partition given as parameter. If use_partitions is 0, you should"\ " not specify a partition." static mi_export_t mi_cmds[] = { { "dr_reload", HLP1, dr_reload_cmd, 0, 0, 0}, { "dr_gw_status", HLP2, mi_dr_gw_status, 0, 0, 0}, { "dr_carrier_status", HLP3, mi_dr_cr_status, 0, 0, 0}, { "dr_number_routing", HLP4, mi_dr_number_routing, 0, 0, 0}, { "dr_reload_status", HLP5, mi_dr_reload_status, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static module_dependency_t *get_deps_probing_interval(param_export_t *param) { if (*(int *)param->param_pointer <= 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "tm", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "probing_interval", get_deps_probing_interval }, { NULL, NULL }, }, }; struct module_exports exports = { "drouting", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* additional processes */ dr_init, /* Module initialization function */ (response_function) 0, (destroy_function) dr_exit, (child_init_function) dr_child_init /* per-child init function */ }; /** Probing Section **/ static int check_options_rplcode(int code) { int i; for (i =0; i< probing_codes_no; i++) { if(probing_reply_codes[i] == code) return 1; } return 0; } static int dr_disable(struct sip_msg *req, char * param_part_name) { str part_name; struct head_db * current_partition = 0; if( param_part_name!=NULL && fixup_get_svalue(req, (gparam_p)param_part_name, &part_name)==0 ) { if( (current_partition = get_partition(&part_name))!= NULL) { return dr_disable_w_part(req, current_partition); } else { LM_ERR("Given partition name <%.*s> was not found\n", part_name.len, part_name.s); return -1; } } else { if( use_partitions ) { LM_ERR("Partition name is mandatory <%.*s>\n", part_name.len ,part_name.s); return -1; } else { if( head_db_start==NULL ) { LM_ERR(" Error while loading default converation from .cfg" " file\n"); return -1; } return dr_disable_w_part(req, head_db_start); } } return -1;/* unexpected ending */ } static str dr_gwid_str = str_init("gwid"); static str dr_address_str = str_init("address"); static str dr_status_str = str_init("status"); static str dr_inactive_str = str_init("inactive"); static str dr_active_str = str_init("active"); static str dr_disabled_str = str_init("disabled MI"); static str dr_probing_str = str_init("probing"); static void dr_raise_event(pgw_t *gw) { evi_params_p list = NULL; str *txt; if (dr_evi_id == EVI_ERROR || !evi_probe_event(dr_evi_id)) return; list = evi_get_params(); if (!list) { LM_ERR("cannot create event params\n"); return; } if (evi_param_add_str(list, &dr_gwid_str, &gw->id) < 0) { LM_ERR("cannot add gwid\n"); goto error; } if (evi_param_add_str(list, &dr_address_str, &gw->ip_str) < 0) { LM_ERR("cannot add address\n"); goto error; } if (gw->flags&DR_DST_STAT_DSBL_FLAG) { if (gw->flags&DR_DST_STAT_NOEN_FLAG) txt = &dr_disabled_str; else if (gw->flags&DR_DST_PING_DSBL_FLAG) txt = &dr_probing_str; else txt = &dr_inactive_str; } else { txt = &dr_active_str; } if (evi_param_add_str(list, &dr_status_str, txt) < 0) { LM_ERR("cannot add state\n"); goto error; } if (evi_raise_event(dr_evi_id, list)) { LM_ERR("unable to send dr event\n"); } return; error: evi_free_params(list); } static int dr_disable_w_part(struct sip_msg *req, struct head_db *current_partition) { struct usr_avp *avp; int_str id_val; pgw_t *gw; lock_start_read( current_partition->ref_lock ); avp = search_first_avp( AVP_VAL_STR, current_partition->gw_id_avp, &id_val,0); if (avp==NULL) { LM_DBG(" no AVP ID ->nothing to disable\n"); lock_stop_read( current_partition->ref_lock ); return -1; } gw = get_gw_by_id( (*current_partition->rdata)->pgw_tree, &id_val.s ); if (gw!=NULL && (gw->flags&DR_DST_STAT_DSBL_FLAG)==0) { LM_INFO(" partition : %.*s\n", current_partition->partition.len, current_partition->partition.s); gw->flags |= DR_DST_STAT_DSBL_FLAG|DR_DST_STAT_DIRT_FLAG; dr_raise_event(gw); } lock_stop_read( current_partition->ref_lock ); return 1; } static void dr_probing_callback( struct cell *t, int type, struct tmcb_params *ps ) { int code = ps->code; pgw_t *gw; int _id ; struct head_db * current_partition; if (!ps->param || !*ps->param) { LM_CRIT("BUG - reply to a DR probe with no ID (code=%d)\n", ps->code); return; } if( !((param_prob_callback_t*)*ps->param)->current_partition ) { LM_CRIT("BUG - no partition supplied to callback function\n"); return ; } current_partition = ( (param_prob_callback_t*) *ps->param)->current_partition; lock_start_read( current_partition->ref_lock ); _id = ((param_prob_callback_t*)*ps->param)->_id; gw = get_gw_by_internal_id( (*(current_partition->rdata))->pgw_tree, _id); if (gw==NULL) goto end; if ((code == 200) || check_options_rplcode(code)) { /* re-enable to DST (if allowed) */ if ( (gw->flags&DR_DST_STAT_NOEN_FLAG)!=0 || /* permanently disabled */ (gw->flags&DR_DST_STAT_DSBL_FLAG)==0) /* not disabled at all */ goto end; gw->flags &= ~DR_DST_STAT_DSBL_FLAG; gw->flags |= DR_DST_STAT_DIRT_FLAG; dr_raise_event(gw); goto end; } if (code>=400 && (gw->flags&DR_DST_STAT_DSBL_FLAG)==0) { gw->flags |= DR_DST_STAT_DSBL_FLAG|DR_DST_STAT_DIRT_FLAG; dr_raise_event(gw); goto end; } end: lock_stop_read( current_partition->ref_lock ); return; } static void param_prob_callback_free(void *param) { shm_free(param); } static void dr_prob_handler(unsigned int ticks, void* param) { static char buff[1000] = {"sip:"}; /* do probing */ pgw_t *dst; param_prob_callback_t *params; dlg_t *dlg; str uri; void** dest; map_iterator_t map_it; struct head_db *it = head_db_start; while( it!=NULL ) { if (it->rdata==NULL || *(it->rdata)==NULL) return; lock_start_read( it->ref_lock ); /* go through all destinations */ for (map_first( (*(it->rdata))->pgw_tree, &map_it); iterator_is_valid(&map_it); iterator_next(&map_it)) { dest = iterator_val(&map_it); if (dest==NULL) break; dst = (pgw_t*)*dest; /* dst requires probing ? */ if ( dst->flags&DR_DST_STAT_NOEN_FLAG || !( (dst->flags&DR_DST_PING_PERM_FLAG) || /*permanent probing*/ ( dst->flags&DR_DST_PING_DSBL_FLAG && dst->flags&DR_DST_STAT_DSBL_FLAG /*probing on disable*/ ) ) ) { continue; } memcpy(buff + 4, dst->ip_str.s, dst->ip_str.len); uri.s = buff; uri.len = dst->ip_str.len + 4; /* Execute the Dialog using the "request"-Method of the * TM-Module.*/ if (dr_tmb.new_auto_dlg_uac(&dr_probe_from, &uri, dst->sock, &dlg)!=0) { LM_ERR("failed to create new TM dlg\n"); continue; } dlg->state = DLG_CONFIRMED; params = shm_malloc(sizeof(param_prob_callback_t)); if( params==0 ) { LM_ERR("no more shm memory!\n"); return; } params->_id = dst->_id; params->current_partition = it; if (dr_tmb.t_request_within(&dr_probe_method, NULL, NULL, dlg, dr_probing_callback, (void*)params, param_prob_callback_free) < 0) { LM_ERR("unable to execute dialog\n"); } dr_tmb.free_dlg(dlg); } lock_stop_read( it->ref_lock ); it = it->next; } } static void dr_state_flusher(struct head_db* hd) { static db_ps_t cr_ps=NULL, gw_ps=NULL; pgw_t *gw; pcr_t *cr; db_key_t key_cmp; db_val_t val_cmp; db_key_t key_set; db_val_t val_set; void** dest; map_iterator_t it; if(!hd) { LM_ERR(" Bug - no head supplied to dr_state_flusher\n"); } /* is data avaialable? */ if (!hd || !(hd->rdata) || !(*hd->rdata)) return; val_cmp.type = DB_STR; val_cmp.nul = 0; val_set.type = DB_INT; val_set.nul = 0; /* update the gateways */ if ((hd->db_funcs).use_table( (*hd->db_con), &(hd->drd_table)) < 0) { LM_ERR("cannot select table \"%.*s\"\n", hd->drd_table.len, hd->drd_table.s); return; } key_cmp = &gwid_drd_col; key_set = &state_drd_col; /* iterate the gateways */ for (map_first((*hd->rdata)->pgw_tree , &it); iterator_is_valid(&it); iterator_next(&it)) { dest = iterator_val(&it); if (dest==NULL) break; gw = (pgw_t*)*dest; if ( (gw->flags & DR_DST_STAT_DIRT_FLAG)==0 ) /* nothing to do for this gateway */ continue; /* populate the update */ val_cmp.val.str_val = gw->id; val_set.val.int_val = (gw->flags&DR_DST_STAT_DSBL_FLAG) ? ((gw->flags&DR_DST_STAT_NOEN_FLAG)?1:2) : (0); /* update the state of this gateway */ LM_DBG("updating the state of gw <%.*s> to %d\n", gw->id.len, gw->id.s, val_set.val.int_val); CON_PS_REFERENCE(*hd->db_con) = gw_ps; if ( (hd->db_funcs).update(*hd->db_con,&key_cmp,0,&val_cmp,&key_set,&val_set,1,1)<0 ) { LM_ERR("DB update failed\n"); } else { gw->flags &= ~DR_DST_STAT_DIRT_FLAG; } } /* update the carriers */ if ((hd->db_funcs).use_table( *hd->db_con, &(hd->drc_table)) < 0) { LM_ERR("cannot select table \"%.*s\"\n", hd->drc_table.len, hd->drc_table.s); return; } key_cmp = &cid_drc_col; key_set = &state_drc_col; /* iterate the carriers */ for (map_first( (*hd->rdata)->carriers_tree, &it); iterator_is_valid(&it); iterator_next(&it)) { dest = iterator_val(&it); if (dest==NULL) break; cr = (pcr_t*)*dest; if ( (cr->flags & DR_CR_FLAG_DIRTY)==0 ) /* nothing to do for this carrier */ continue; /* populate the update */ val_cmp.val.str_val = cr->id; val_set.val.int_val = (cr->flags&DR_CR_FLAG_IS_OFF) ? 1 : 0; /* update the state of this carrier */ LM_DBG("updating the state of cr <%.*s> to %d\n", cr->id.len, cr->id.s, val_set.val.int_val); CON_PS_REFERENCE(*hd->db_con) = cr_ps; if ( (hd->db_funcs).update(*hd->db_con,&key_cmp,0,&val_cmp,&key_set,&val_set,1,1)<0 ) { LM_ERR("DB update failed\n"); } else { cr->flags &= ~DR_CR_FLAG_DIRTY; } } return; } /* Flushes to DB the state of carriers and gateways (if modified) * Locking is done to protect the data consistency */ static void dr_state_timer(unsigned int ticks, void* param) { struct head_db * it; it = head_db_start; while( it!=NULL ) { lock_start_read( it->ref_lock ); dr_state_flusher(it); lock_stop_read( it->ref_lock ); it = it->next; } } /* * if none is successfully loaded return * -1, else return 0 */ static inline int dr_reload_data_head( struct head_db *hd ) { rt_data_t *new_data; rt_data_t *old_data; pgw_t *gw, *old_gw; pcr_t *cr, *old_cr; time_t rawtime; void **dest; map_iterator_t it; if (no_concurrent_reload) { lock_get( hd->ref_lock->lock ); if (hd->ongoing_reload) { lock_release( hd->ref_lock->lock ); LM_WARN("Reload already in progress, discarding this one\n"); return -2; } hd->ongoing_reload = 1; lock_release( hd->ref_lock->lock ); } new_data = dr_load_routing_info(hd, dr_persistent_state); if ( new_data==0 ) { LM_CRIT("failed to load routing info\n"); goto error; } lock_start_write( hd->ref_lock ); /* no more activ readers -> do the swapping */ old_data = *(hd->rdata); *(hd->rdata) = new_data; /* update the time of the last reload for the current partition */ time(&rawtime); hd->time_last_update = rawtime; lock_stop_write( (hd->ref_lock) ); /* destroy old data */ if (old_data) { /* copy the state of gw/cr from old data */ /* interate new gws and search them into old data */ for (map_first(new_data->pgw_tree, &it); iterator_is_valid(&it); iterator_next(&it)) { dest = iterator_val(&it); if(dest==NULL) break; gw=(pgw_t *)*dest; old_gw = get_gw_by_id( old_data->pgw_tree, &gw->id); if (old_gw) { gw->flags &= ~DR_DST_STAT_MASK; gw->flags |= old_gw->flags&DR_DST_STAT_MASK; } } /* interate new crs and search them into old data */ for (map_first(new_data->carriers_tree, &it); iterator_is_valid(&it); iterator_next(&it)) { dest = iterator_val(&it); if(dest==NULL) break; cr=(pcr_t *)*dest; old_cr = get_carrier_by_id( old_data->carriers_tree, &cr->id); if (old_cr) { cr->flags &= ~DR_CR_FLAG_IS_OFF; cr->flags |= old_cr->flags&DR_CR_FLAG_IS_OFF; } } /* free old data */ free_rt_data( old_data, 1 ); } /* generate new blacklist from the routing info */ populate_dr_bls((*(hd->rdata))->pgw_tree); if (no_concurrent_reload) hd->ongoing_reload = 0; return 0; error: if (no_concurrent_reload) hd->ongoing_reload = 0; return -1; } static inline int dr_reload_data( void ) { struct head_db * it_head_db; int ret_val = 0; for( it_head_db=head_db_start; it_head_db!=NULL; it_head_db=it_head_db->next ) { if( dr_reload_data_head( it_head_db )!=0 ) ret_val = -1; } return ret_val; } #define dr_fix_avp_def_w_default( _pv_spec, _avp_id, _default, _p_name, _name)\ if(_pv_spec.s == NULL) { \ if(use_partitions) {\ _pv_spec.len = _default.len + _p_name.len;\ _pv_spec.s = shm_malloc((_pv_spec.len)*sizeof(char));\ memcpy(_pv_spec.s, _default.s, _default.len-1);\ memcpy(_pv_spec.s + _default.len - 1, _p_name.s, _p_name.len);\ _pv_spec.s[_pv_spec.len-1] = ')';\ LM_DBG("name with partition:%.*s\n",_pv_spec.len, _pv_spec.s);\ }\ else { \ shm_str_dup(&_pv_spec, &_default);\ }\ }\ dr_fix_avp_definition(_pv_spec, _avp_id, _name); #define dr_fix_avp_definition( _pv_spec, _avp_id, _name) \ do { \ if (pv_parse_spec( &_pv_spec, &avp_spec)==0 \ || avp_spec.type!=PVT_AVP) { \ _pv_spec.len = strlen(_pv_spec.s); \ LM_ERR("malformed or non AVP [%.*s] for %s AVP definition\n",\ _pv_spec.len, _pv_spec.s, _name); \ head_db_end->db_url.s = 0;\ goto skip;\ } \ if( pv_get_avp_name(0, &(avp_spec.pvp), &_avp_id, &dummy )!=0) { \ LM_ERR("[%.*s]- invalid AVP definition for %s AVP\n", \ _pv_spec.len, _pv_spec.s, _name); \ head_db_end->db_url.s = 0;\ goto skip;\ } \ } while(0) #define add_partition_to_avp_name( _spec, _p_name, _name_w_part ) \ _name_w_part.len = _spec.len + _p_name.len; \ memcpy(_name_w_part.s, _spec.s, _spec.len);\ memcpy(_name_w_part.s + _spec.len, _p_name.s, _p_name.len); static int cleanup_head_config( struct head_config *hd) { LM_DBG("Cleanup started\n"); if( hd==NULL ) { LM_CRIT(" Cleanup head_config failed. Null pointer supplied\n"); return -1; } if( hd->db_url.s ) { shm_free( hd->db_url.s ); hd->db_url.s = 0; } if( hd->drd_table.s && hd->drd_table.s != drd_table.s) { shm_free( hd->drd_table.s ); } if( hd->drr_table.s && hd->drr_table.s != drr_table.s) { shm_free( hd->drr_table.s ); } if( hd->drc_table.s && hd->drc_table.s != drc_table.s) { shm_free( hd->drc_table.s ); } if( hd->drg_table.s && hd->drg_table.s != drg_table.s) { shm_free( hd->drg_table.s ); } if(hd->gw_priprefix_avp_spec.s) shm_free(hd->gw_priprefix_avp_spec.s); if(hd->rule_id_avp_spec.s) shm_free(hd->rule_id_avp_spec.s); if(hd->rule_prefix_avp_spec.s) shm_free(hd->rule_prefix_avp_spec.s); if(hd->carrier_attrs_avp_spec.s) shm_free(hd->carrier_attrs_avp_spec.s); if(hd->ruri_avp_spec.s) shm_free(hd->ruri_avp_spec.s); if(hd->gw_id_avp_spec.s) shm_free(hd->gw_id_avp_spec.s); if(hd->gw_sock_avp_spec.s) shm_free(hd->gw_sock_avp_spec.s); if(hd->gw_attrs_avp_spec.s) shm_free(hd->gw_attrs_avp_spec.s); if(hd->rule_attrs_avp_spec.s) shm_free(hd->rule_attrs_avp_spec.s); if(hd->carrier_id_avp_spec.s) shm_free(hd->carrier_id_avp_spec.s); return 0; } static int cleanup_head_db( struct head_db *hd) { if(hd) { if( hd->db_con && *(hd->db_con) ) { hd->db_funcs.close(*(hd->db_con)); } if( hd->ref_lock ) { lock_destroy_rw( ref_lock ); } if ( hd->rdata ) { shm_free(hd->rdata); hd->rdata = 0; } if ( hd->partition.s ) { shm_free(hd->partition.s); hd->partition.len = 0; } if( hd->db_url.s ) { shm_free( hd->db_url.s ); hd->db_url.len = 0; } if( hd->drd_table.s && hd->drd_table.s != drd_table.s) { shm_free(hd->drd_table.s); hd->drd_table.s = 0; hd->drd_table.len = 0; } if( hd->drr_table.s && hd->drr_table.s != drr_table.s) { shm_free(hd->drr_table.s); hd->drr_table.s = 0; hd->drr_table.len = 0; } if( hd->drc_table.s && hd->drc_table.s != drc_table.s) { shm_free(hd->drc_table.s); hd->drc_table.s = 0; hd->drc_table.len = 0; } if( hd->drg_table.s && hd->drg_table.s != drg_table.s) { shm_free(hd->drg_table.s); hd->drg_table.s = 0; hd->drg_table.len = 0; } hd->avpID_store_ruri = -1; hd->avpID_store_prefix = -1; hd->avpID_store_index = -1; hd->avpID_store_whitelist = -1; hd->avpID_store_group = -1; hd->avpID_store_flags = -1; hd->gw_priprefix_avp = -1; hd->rule_id_avp = -1; hd->rule_prefix_avp = -1; hd->carrier_id_avp = -1; hd->ruri_avp = -1; hd->gw_id_avp = -1; hd->gw_sock_avp = -1; hd->gw_attrs_avp = -1; hd->rule_attrs_avp = -1; hd->carrier_attrs_avp = -1; } else { LM_CRIT(" No head_db to clean supplied"); return -1; } return 0; } #define head_from_extern_param( _dst, _src, _name)\ do { \ if( (_src).s && ((_src).len=strlen((_src).s))!=0 ) {\ if( shm_str_dup( &(_dst), &(_src))!=0 ) \ LM_ERR(" Fail duplicating extern param (%s) to head\n",_name);\ }\ }while(0) void init_head_w_extern_params(void) { head_from_extern_param( head_start->rule_id_avp_spec, rule_id_avp_spec, "rule_id_avp_spec"); head_from_extern_param( head_start->rule_prefix_avp_spec, rule_prefix_avp_spec, "rule_prefix_avp_spec"); head_from_extern_param( head_start->carrier_id_avp_spec, carrier_id_avp_spec, "carrier_id_avp_spec"); head_from_extern_param( head_start->ruri_avp_spec, ruri_avp_spec, "ruri_avp_spec"); head_from_extern_param( head_start->gw_id_avp_spec, gw_id_avp_spec, "gw_id_avp_spec"); head_from_extern_param( head_start->gw_sock_avp_spec, gw_sock_avp_spec, "gw_sock_avp_spec"); head_from_extern_param( head_start->gw_attrs_avp_spec, gw_attrs_avp_spec, "gw_attrs_avp_spec"); head_from_extern_param( head_start->gw_priprefix_avp_spec, gw_priprefix_avp_spec, "gw_priprefix_avp_spec"); head_from_extern_param( head_start->rule_attrs_avp_spec, rule_attrs_avp_spec, "rule_attrs_avp_spec"); head_from_extern_param( head_start->carrier_attrs_avp_spec, carrier_attrs_avp_spec, "carrier_attrs_avp_spec"); } static int dr_init(void) { pv_spec_t avp_spec; unsigned short dummy; str name, name_w_part; struct head_config * it_head_config = 0; struct head_config * last_cleaned = 0; struct head_db * it_head_db = 0, *to_clean = 0; head_start = NULL; //empty head list head_end = NULL; LM_INFO("Dynamic-Routing - initializing\n"); name_w_part.s = shm_malloc( MAX_LEN_NAME_W_PART /* length of fixed string */); if( name_w_part.s == 0 ) { LM_ERR(" No more shm memory [drouting:name_w_part.s]\n"); goto error; } if( use_partitions == 1 ) { /* loading configurations from db */ if( get_config_from_db() == -1 ) { LM_ERR("Failed to get configuration from db_config\n"); goto error; } if (partition_pvar.s) { partition_pvar.len = strlen(partition_pvar.s); /* just reusing avp_spec; no need to be an AVP to work */ if (pv_parse_spec(&partition_pvar, &partition_spec) == 0) { LM_ERR("malformed PV string: <<%s>>\n", partition_pvar.s); return -1; } if (partition_spec.setf == NULL) { LM_ERR("Partition_id_pvar is not WRITABLE!\n"); return -1; } } } else { init_db_url(db_url, 0); add_head_config(); /* if not empty save to head_config structure */ drd_table.len = strlen(drd_table.s); if (drd_table.s[0]==0) { LM_CRIT("mandatory parameter \"DRD_TABLE\" found empty\n"); goto error; } head_start->drd_table.s = shm_malloc( drd_table.len * sizeof(char) ); if( head_start->drd_table.s == 0 ) { LM_ERR(" no more shm memory [drouting:head_start->drd_table.s]\n"); goto error; } memcpy( head_start->drd_table.s, drd_table.s, drd_table.len ); head_start->drd_table.len = drd_table.len; drr_table.len = strlen(drr_table.s); if (drr_table.s[0]==0) { LM_CRIT("mandatory parameter \"DRR_TABLE\" found empty\n"); goto error; } head_start->drr_table.s = shm_malloc( drr_table.len * sizeof(char) ); if( head_start->drr_table.s == 0 ) { LM_ERR(" no more shm memory [drouting:head_start->drr_table.s]\n"); goto error; } memcpy( head_start->drr_table.s, drr_table.s, drr_table.len); head_start->drr_table.len = drr_table.len; drg_table.len = strlen(drg_table.s); if (drg_table.s[0]==0) { LM_CRIT("mandatory parameter \"DRG_TABLE\" found empty\n"); goto error; } head_start->drg_table.s = shm_malloc( drg_table.len * sizeof(char) ); if( head_start->drg_table.s == 0 ) { LM_ERR(" no more shm memory [drouting:head_start->drg_table.s]\n"); goto error; } memcpy( head_start->drg_table.s, drg_table.s, drg_table.len); head_start->drg_table.len = drg_table.len; drc_table.len = strlen(drc_table.s); if ( drc_table.s[0]==0 ) { LM_CRIT("mandatory parameter \"DRC_TABLE\" found empty\n"); goto error; } head_start->drc_table.s = shm_malloc( drc_table.len * sizeof(char) ); if( head_start->drc_table.s == 0 ) { LM_ERR(" no more shm memory [drouting:head_start->drc_table.s]\n"); goto error; } memcpy( head_start->drc_table.s, drc_table.s, drc_table.len); head_start->drc_table.len = drc_table.len; head_start->db_url.len = db_url.len; head_start->db_url.s = shm_malloc( db_url.len * sizeof(char)); if( head_start->db_url.s == 0 ) { LM_ERR(" no more shm memory [drouting:head_start->db_url.s]\n"); goto error; } memcpy( head_start->db_url.s, db_url.s, db_url.len ); init_head_w_extern_params(); head_start->partition.s = "Default"; head_start->partition.len = strlen("Default\0"); } it_head_config = head_start; drg_user_col.len = strlen(drg_user_col.s); drg_domain_col.len = strlen(drg_domain_col.s); drg_grpid_col.len = strlen(drg_grpid_col.s); while(it_head_config != NULL) { /* check if last head was ok, if not overwrite it */ if( head_db_start==NULL || (head_db_start!=NULL && head_db_end->db_url.s!=NULL) ) { add_head_db(); } if( it_head_config->db_url.s==0 ) continue; if( shm_str_dup( &( head_db_end->db_url ), &(it_head_config->db_url))!=0 ) { LM_CRIT("shm_str_dup failed for db_url"); head_db_end->db_url.s = 0; goto skip; } if( shm_str_dup( &( head_db_end->partition ), &(it_head_config->partition))!=0 ) { LM_CRIT("shm_str_dup failed for db_url"); head_db_end->db_url.s = 0; goto skip; } if(!it_head_config->drd_table.s) { head_db_end->drd_table.s = drd_table.s; head_db_end->drd_table.len = drd_table.len; }else if( shm_str_dup( &( head_db_end->drd_table ), &(it_head_config->drd_table))!=0 ) { LM_CRIT("shm_str_dup failed for db_url"); head_db_end->db_url.s = 0; goto skip; } if(!it_head_config->drr_table.s) { head_db_end->drr_table.s = drr_table.s; head_db_end->drr_table.len = drr_table.len; }else if( shm_str_dup( &( head_db_end->drr_table ), &(it_head_config->drr_table))!=0 ) { LM_CRIT("shm_str_dup failed for db_url"); head_db_end->db_url.s = 0; goto skip; } if(!it_head_config->drc_table.s) { head_db_end->drc_table.s = drc_table.s; head_db_end->drc_table.len = drc_table.len; } else if( shm_str_dup( &( head_db_end->drc_table ), &(it_head_config->drc_table))!=0 ) { LM_CRIT("shm_str_dup failed for db_url"); head_db_end->db_url.s = 0; goto skip; } if(!it_head_config->drg_table.s) { head_db_end->drg_table.s = drg_table.s; head_db_end->drg_table.len = drg_table.len; } else if( shm_str_dup( &( head_db_end->drg_table ), &(it_head_config->drg_table))!=0 ) { LM_CRIT("shm_str_dup failed for db_url"); head_db_end->db_url.s = 0; goto skip; } /* fix specs for internal AVP (used for fallback) */ /* partition name is added to AVP name */ name.s = "_dr_fb_ruri_"; name.len=12; add_partition_to_avp_name( name, it_head_config->partition, name_w_part); if ( parse_avp_spec( &name_w_part, &(head_db_end->avpID_store_ruri))!=0 ) { LM_ERR("failed to init internal AVP for ruri\n"); head_db_end->db_url.s = 0; goto skip; } name.s = "_dr_fb_prefix_"; name.len=14; add_partition_to_avp_name( name, it_head_config->partition, name_w_part); if ( parse_avp_spec( &name_w_part, &(head_db_end->avpID_store_prefix))!=0 ) { LM_ERR("failed to init internal AVP for prefix\n"); head_db_end->db_url.s = 0; goto skip; } name.s = "_dr_fb_index_"; name.len=13; add_partition_to_avp_name( name, it_head_config->partition, name_w_part); if ( parse_avp_spec( &name_w_part, &(head_db_end->avpID_store_index))!=0 ) { LM_ERR("failed to init internal AVP for index\n"); head_db_end->db_url.s = 0; goto skip; } name.s = "_dr_fb_whitelist_"; name.len=17; add_partition_to_avp_name( name, it_head_config->partition, name_w_part); if ( parse_avp_spec( &name_w_part, &(head_db_end->avpID_store_whitelist))!=0 ) { LM_ERR("failed to init internal AVP for whitelist\n"); head_db_end->db_url.s = 0; goto skip; } name.s = "_dr_fb_group_"; name.len=13; add_partition_to_avp_name( name, it_head_config->partition, name_w_part); if ( parse_avp_spec( &name_w_part, &(head_db_end->avpID_store_group))!=0 ) { LM_ERR("failed to init internal AVP for group\n"); head_db_end->db_url.s = 0; goto skip; } name.s = "_dr_fb_flags_"; name.len=13; add_partition_to_avp_name( name, it_head_config->partition, name_w_part); if ( parse_avp_spec( &name_w_part, &(head_db_end->avpID_store_flags))!=0 ) { LM_ERR("failed to init internal AVP for flags\n"); head_db_end->db_url.s = 0; goto skip; } /* fix AVP specs for parameters */ dr_fix_avp_def_w_default( it_head_config->ruri_avp_spec, head_db_end->ruri_avp, ruri_avp_spec, it_head_config->partition, "RURI"); dr_fix_avp_def_w_default( it_head_config->gw_id_avp_spec, head_db_end->gw_id_avp, gw_id_avp_spec, it_head_config->partition, "GW ID"); dr_fix_avp_def_w_default( it_head_config->gw_sock_avp_spec, head_db_end->gw_sock_avp, gw_sock_avp_spec, it_head_config->partition, "GW SOCKET"); dr_fix_avp_def_w_default( it_head_config->gw_attrs_avp_spec, head_db_end->gw_attrs_avp, gw_attrs_avp_spec, it_head_config->partition, "GW ATTRS"); dr_fix_avp_def_w_default( it_head_config->rule_attrs_avp_spec, head_db_end->rule_attrs_avp, rule_attrs_avp_spec, it_head_config->partition, "RULE ATTRS"); dr_fix_avp_def_w_default( it_head_config->carrier_attrs_avp_spec, head_db_end->carrier_attrs_avp, carrier_attrs_avp_spec, it_head_config->partition, "CARRIER ATTRS"); if (it_head_config->gw_priprefix_avp_spec.s ) { dr_fix_avp_definition( it_head_config->gw_priprefix_avp_spec, head_db_end->gw_priprefix_avp, "GW PRI PREFIX"); } if (it_head_config->rule_id_avp_spec.s) { dr_fix_avp_definition( it_head_config->rule_id_avp_spec, head_db_end->rule_id_avp, "RULE ID"); } if (it_head_config->rule_prefix_avp_spec.s) { dr_fix_avp_definition( it_head_config->rule_prefix_avp_spec, head_db_end->rule_prefix_avp, "RULE PREFIX"); } if (it_head_config->carrier_id_avp_spec.s) { dr_fix_avp_definition( it_head_config->carrier_id_avp_spec, head_db_end->carrier_id_avp, "CARRIER ID"); } /* data pointer in shm */ head_db_end->rdata = (rt_data_t**)shm_malloc( sizeof(rt_data_t*) ); if ( head_db_end->rdata==0 ) { LM_CRIT("failed to get shm mem for data ptr\n"); head_db_end->db_url.s = 0; goto skip; } *(head_db_end->rdata) = 0; /* create & init lock */ if ((head_db_end->ref_lock = lock_init_rw()) == NULL) { LM_CRIT("failed to init lock\n"); head_db_end->db_url.s = 0; goto skip; } head_db_end->db_con = pkg_malloc(sizeof(db_con_t **)); (*(head_db_end->db_con)) = 0; /* bind to the SQL module */ if (db_bind_mod( &(head_db_end->db_url), &( head_db_end->db_funcs ))) { LM_CRIT("cannot bind to database module! " "Did you forget to load a database module ? (%.*s)\n", db_url.len, db_url.s); head_db_end->db_url.s = 0; goto skip; } if( (*head_db_end->db_con = head_db_end->db_funcs.init(&head_db_end->db_url)) == 0) { LM_ERR("Cand't load db ulr %.*s", head_db_end->db_url.len, head_db_end->db_url.s); return -1; } if (!DB_CAPABILITY( head_db_end->db_funcs, DB_CAP_QUERY)) { LM_CRIT( "database modules does not " "provide QUERY functions needed by DRouting module\n"); head_db_end->db_url.s = 0; goto skip; } if(db_check_table_version(&head_db_end->db_funcs, *head_db_end->db_con, &head_db_end->drd_table, DRD_TABLE_VER) < 0) { LM_ERR("error during table version check\n", head_db_end->drd_table.len, head_db_end->drd_table.s, head_db_end->partition.len, head_db_end->partition.s); return -1; } if(db_check_table_version(&head_db_end->db_funcs, *head_db_end->db_con, &head_db_end->drr_table, DRR_TABLE_VER) < 0) { LM_ERR("error during table version check\n", head_db_end->drr_table.len, head_db_end->drr_table.s, head_db_end->partition.len, head_db_end->partition.s); return -1; } if(db_check_table_version(&head_db_end->db_funcs, *head_db_end->db_con, &head_db_end->drg_table, DRG_TABLE_VER) < 0) { LM_ERR("error during table version check\n", head_db_end->drg_table.len, head_db_end->drg_table.s, head_db_end->partition.len, head_db_end->partition.s); return -1; } if(db_check_table_version(&head_db_end->db_funcs, *head_db_end->db_con, &head_db_end->drc_table, DRC_TABLE_VER) < 0) { LM_ERR("error during table version check\n", head_db_end->drc_table.len, head_db_end->drc_table.s, head_db_end->partition.len, head_db_end->partition.s); return -1; } (head_db_end->db_funcs).close(*head_db_end->db_con); *head_db_end->db_con = 0; skip: it_head_config = it_head_config->next; if(head_db_end->db_url.s == 0) { cleanup_head_db(head_db_end); memset( head_db_end, 0, sizeof(struct head_db) ); } } if( name_w_part.s ) { shm_free(name_w_part.s); name_w_part.s = 0; } /* free last head if left uninitialized */ if( head_db_end!=NULL && head_db_end->db_url.s==NULL ) { if( head_db_end==head_db_start ) { cleanup_head_db( head_db_start ); memset( head_db_start, 0, sizeof(struct head_db) ); if( head_db_start ) { shm_free( head_db_start ); } head_db_start=head_db_end = 0; return -1; /* no valid head available */ } else { it_head_db = head_db_start; while( it_head_db->next!=head_db_end ) it_head_db = it_head_db->next; to_clean = head_db_end; head_db_end = it_head_db; head_db_end->next = NULL; cleanup_head_db( to_clean ); memset( to_clean, 0, sizeof(struct head_db) ); if( to_clean ) { shm_free( to_clean ); to_clean = 0; } } } if (init_dr_bls(head_db_start)!=0) { LM_ERR("failed to init DR blacklists\n"); return E_CFG; } it_head_config = head_start; while( it_head_config ) { cleanup_head_config( it_head_config ); last_cleaned = it_head_config; it_head_config = it_head_config->next; memset( last_cleaned, 0 , sizeof( struct head_config )); if( last_cleaned ) { shm_free( last_cleaned ); last_cleaned = 0; } } head_start = 0; head_end = 0; if (dr_prob_interval) { /* load TM API */ if (load_tm_api(&dr_tmb)!=0) { LM_ERR("can't load TM API\n"); return -1; } /* probing method */ dr_probe_method.len = strlen(dr_probe_method.s); dr_probe_from.len = strlen(dr_probe_from.s); if (dr_probe_replies.s) dr_probe_replies.len = strlen(dr_probe_replies.s); /* register pinger function */ if (register_timer( "dr-pinger", dr_prob_handler, NULL, dr_prob_interval, TIMER_FLAG_DELAY_ON_DELAY)<0) { LM_ERR("failed to register probing handler\n"); return -1; } if (dr_probe_replies.s) { dr_probe_replies.len = strlen(dr_probe_replies.s); if(parse_reply_codes( &dr_probe_replies, &probing_reply_codes, &probing_codes_no )< 0) { LM_ERR("Bad format for options_reply_code parameter" " - Need a code list separated by commas\n"); return -1; } } } if (dr_persistent_state) { /* register function to flush changes in state */ if (register_timer("dr-flush", dr_state_timer, NULL, 30, TIMER_FLAG_SKIP_ON_DELAY)<0) { LM_ERR("failed to register state flush handler\n"); return -1; } } LM_DBG("All in place in the init. Will return 0\n"); /* init the the default partition for do_routing */ default_part = pkg_malloc(sizeof(dr_part_group_t)); if(default_part == NULL) { LM_ERR("No more pkg memory!\n"); goto error; } memset(default_part, 0, sizeof(dr_part_group_t)); default_part->dr_part = pkg_malloc(sizeof(dr_partition_t)); if(default_part->dr_part == NULL) { LM_ERR("No more pkg memory!\n"); goto error; } memset(default_part->dr_part, 0, sizeof(dr_partition_t)); default_part->dr_part->type = DR_PTR_PART; default_part->dr_part->v.part = head_db_start; dr_evi_id = evi_publish_event(dr_event); if (dr_evi_id == EVI_ERROR) { LM_ERR("cannot register %.*s event\n", dr_event.len, dr_event.s); goto error; } return 0; error: /* clean-up -> only when we used extern_params * from the cfg*/ if( head_db_end==head_db_start) { /* sanity check: should contain only one head */ cleanup_head_db( head_db_end ); if( head_db_end!=0 ) { shm_free( head_db_end ); } head_db_end=head_db_start = 0; cleanup_head_config( head_end ); if( head_end!=0 ) { shm_free( head_end ); head_end = 0; } if (name_w_part.s) { shm_free(name_w_part.s); name_w_part.s = NULL; } } else { LM_ERR(" Something went wrong: Head list should have only " "one head\n"); } return -1; } static int db_load_head(struct head_db *x) { if( *(x->db_con) ) { LM_ERR(" db_con already used\n"); return -1; } if( x->db_url.s && (*(x->db_con) = x->db_funcs.init(&(x->db_url)))==0 ) { LM_ERR("cannot initialize database connection" "(partition:%.*s, db_url:%.*s, len:%d)\n", x->partition.len, x->partition.s, x->db_url.len, x->db_url.s, x->db_url.len); return -1; } if( x->db_con && *(x->db_con) && x->db_funcs.use_table( *(x->db_con), &(x->drg_table)) <0 ) { LM_ERR("cannot select table (partition:%.*s, drg_table:%.*s\n", x->partition.len, x->partition.s, (x->drg_table).len, (x->drg_table).s); return -1; } return 0; } static int dr_child_init(int rank) { /* We need DB connection from: * - attendant - for shutdown, flushingmstate * - timer - may trigger routes with dr group * - workers - execute routes with dr group * - module's proc - ??? */ LM_DBG("Child initialization\n"); if (rank==PROC_TCP_MAIN || rank==PROC_BIN) return 0; struct head_db *head_db_it = head_db_start; while( head_db_it!=NULL ) { db_load_head( head_db_it ); head_db_it = head_db_it->next; LM_DBG("Child iterates\n"); } /* child 1 load the routing info */ if ( (rank==1) && dr_reload_data()!=0 ) { LM_CRIT("failed to load routing data\n"); return -1; } srand(getpid()+time(0)+rank); return 0; } static int dr_exit(void) { struct head_db * it = head_db_start, *to_clean; while( it!=NULL ) { to_clean = it; it = it->next; if (dr_persistent_state && to_clean->db_con && *(to_clean->db_con)) dr_state_flusher(to_clean); /* close DB connection */ if (to_clean->db_con && *(to_clean->db_con)) { (to_clean->db_funcs).close(*(to_clean->db_con)); *(to_clean->db_con) = 0; pkg_free(to_clean->db_con); } /* destroy data */ if ( to_clean->rdata) { if (*(to_clean->rdata)) free_rt_data( *(to_clean->rdata), 1 ); shm_free( to_clean->rdata ); to_clean->rdata = 0; } /* destroy lock */ if (to_clean->ref_lock) { lock_destroy_rw( to_clean->ref_lock ); to_clean->ref_lock = 0; } /* free table names stored in head_db */ if(to_clean->drd_table.s && to_clean->drd_table.s != drd_table.s) { shm_free(to_clean->drd_table.s); } if(to_clean->drr_table.s && to_clean->drr_table.s != drr_table.s) { shm_free(to_clean->drr_table.s); } if(to_clean->drc_table.s && to_clean->drc_table.s != drc_table.s) { shm_free(to_clean->drc_table.s); } if(to_clean->drg_table.s && to_clean->drg_table.s != drg_table.s) { shm_free(to_clean->drg_table.s); } shm_free(to_clean); } /* destroy blacklists */ destroy_dr_bls(); /* destroy all callbacks */ destroy_dr_cbs(); return 0; } static struct mi_root* dr_reload_cmd(struct mi_root *cmd_tree, void *param) { int n; str * part_name; struct head_db * part; struct mi_node * node = NULL; LM_INFO("dr_reload MI command received!\n"); if(cmd_tree!=NULL) node = cmd_tree->node.kids; if(node==NULL || use_partitions==0) { /* no parameter supplied * -> load the data for all the partitions */ if ( (n=dr_reload_data())!=0 ) { LM_CRIT("failed to load routing data\n"); goto error; } } else { part_name = &(node->value); if( (part = get_partition(part_name))==NULL) { LM_CRIT("Partition not found\n"); goto error; } if( dr_reload_data_head(part)<0 ) { LM_CRIT("Failed to load data head\n"); goto error; } } return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: return init_mi_tree( 500, "Failed to reload",16); } static inline int get_group_id(struct sip_uri *uri, struct head_db * current_partition) { db_key_t keys_ret[1]; db_key_t keys_cmp[2]; db_val_t vals_cmp[2]; db_res_t* res = 0; int n; /* user */ keys_cmp[0] = &drg_user_col; vals_cmp[0].type = DB_STR; vals_cmp[0].nul = 0; vals_cmp[0].val.str_val = uri->user; n = 1; if (use_domain) { keys_cmp[1] = &drg_domain_col; vals_cmp[1].type = DB_STR; vals_cmp[1].nul = 0; vals_cmp[1].val.str_val = uri->host; n++; } keys_ret[0] = &drg_grpid_col; res = 0; if( (current_partition->db_funcs).use_table(*(current_partition->db_con), &(current_partition->drg_table))<0 ) { LM_ERR("cannot select table \"%.*s\"\n", (current_partition->drg_table).len, (current_partition->drg_table).s); goto error; } if ( (current_partition->db_funcs).query(*(current_partition->db_con), keys_cmp,0,vals_cmp,keys_ret,n,1,0,&res)<0 ) { LM_ERR("DB query failed\n"); goto error; } if (RES_ROW_N(res) == 0) { if (dr_default_grp!=-1) { (current_partition->db_funcs).free_result (*(current_partition->db_con), res); return dr_default_grp; } LM_ERR("no group for user " "\"%.*s\"@\"%.*s\"\n", uri->user.len, uri->user.s, uri->host.len, uri->host.s); goto error; } if (res->rows[0].values[0].nul || res->rows[0].values[0].type!=DB_INT) { LM_ERR("null or non-integer group_id\n"); goto error; } n = res->rows[0].values[0].val.int_val; (current_partition->db_funcs).free_result(*(current_partition->db_con), res); return n; error: if (res) (current_partition->db_funcs).free_result(*(current_partition->db_con) , res); return -1; } static inline str* build_ruri(struct sip_uri *uri, int strip, str *pri, str *hostport) { static str uri_str; char *p; if (uri->user.len<=strip) { LM_ERR("stripping %d makes " "username <%.*s> null\n",strip,uri->user.len,uri->user.s); return 0; } uri_str.len = 4 /*sip:*/ + uri->user.len - strip +pri->len + (uri->passwd.s?(uri->passwd.len+1):0) + 1/*@*/ + hostport->len + (uri->params.s?(uri->params.len+1):0) + (uri->headers.s?(uri->headers.len+1):0); if ( (uri_str.s=(char*)pkg_malloc( uri_str.len + 1))==0) { LM_ERR("no more pkg mem\n"); return 0; } p = uri_str.s; *(p++)='s'; *(p++)='i'; *(p++)='p'; *(p++)=':'; if (pri->len) { memcpy(p, pri->s, pri->len); p += pri->len; } memcpy(p, uri->user.s+strip, uri->user.len-strip); p += uri->user.len-strip; if (uri->passwd.s && uri->passwd.len) { *(p++)=':'; memcpy(p, uri->passwd.s, uri->passwd.len); p += uri->passwd.len; } *(p++)='@'; memcpy(p, hostport->s, hostport->len); p += hostport->len; if (uri->params.s && uri->params.len) { *(p++)=';'; memcpy(p, uri->params.s, uri->params.len); p += uri->params.len; } if (uri->headers.s && uri->headers.len) { *(p++)='?'; memcpy(p, uri->headers.s, uri->headers.len); p += uri->headers.len; } *p = 0; if (p-uri_str.s!=uri_str.len) { LM_CRIT("difference between allocated(%d)" " and written(%d)\n",uri_str.len,(int)(long)(p-uri_str.s)); return 0; } return &uri_str; } static inline int init_part_grp(dr_part_group_t ** part_w_no_grp, struct head_db * current_partition, dr_group_t * drg) { dr_partition_t * part; *part_w_no_grp = pkg_malloc(sizeof(dr_part_group_t)); if(*part_w_no_grp == NULL) { LM_ERR("No more pkg memory.\n"); return -1; } part = pkg_malloc(sizeof(dr_partition_t)); if(part == NULL) { LM_ERR("No more pkg memory.\n"); return -1; } memset(part, 0, sizeof(dr_partition_t)); part->type = DR_PTR_PART; part->v.part = current_partition; (*part_w_no_grp)->group = drg; (*part_w_no_grp)->dr_part = part; return 0; } static int do_routing_0(struct sip_msg* msg) { rule_attrs_spec = gw_attrs_spec = carrier_attrs_spec = NULL; dr_part_group_t * part_w_no_grp; if(use_partitions == 0) { if(head_db_start == NULL) { LM_ERR("Error while loading configuration\n"); return -1; } if(init_part_grp(&part_w_no_grp, head_db_start, 0) < 0) return -1; return do_routing(msg, part_w_no_grp, (int)0, NULL); } else { LM_ERR("Partition name is mandatory"); return -1; } return -1; } static int do_routing_1(struct sip_msg* msg, char *part_grp, char* grp_flags, char* flags_wlst, char* wlst_rule, char* rule_gw, char* gw_carr, char* carr) { str res = {0,0}; dr_part_group_t * dr_part_group; int flags=0; char *p; char * _flags, * wlst, * rule_att, * gw_att, * carr_att; if (use_partitions == 0) { if(head_db_start == NULL) { LM_CRIT("Can't load configuration.\n"); return -1; } if(part_grp != NULL) { default_part->group = ((dr_part_group_t*)part_grp)->group; } else { default_part->group = NULL; } dr_part_group = default_part; _flags = grp_flags; wlst = flags_wlst; rule_att = wlst_rule; gw_att = rule_gw; carr_att = gw_carr; } else { dr_part_group = (dr_part_group_t*)part_grp; _flags = grp_flags; wlst = flags_wlst; rule_att = wlst_rule; gw_att = rule_gw; carr_att = gw_carr; } if (_flags) { if (fixup_get_svalue(msg, (gparam_p)_flags, &res) != 0) { LM_ERR("failed to extract flags\n"); return -1; } for (p=res.s;ptype == DR_PTR_PART) { current_partition = part->v.part; } else if(part->type == DR_GPARAM_PART) { if(to_partition(msg, part, ¤t_partition) < 0) { return -1; } } return use_next_gw_w_part(msg, current_partition, rule_or_gw, gw_carr, carr); } else { LM_ERR("Partition is mandatory for use_next_gw.\n"); return -1; } } else { /* setup from .cfg file => default partition */ if(head_db_start == NULL) { LM_ERR(" Error while loading default converation from .cfg" " file\n"); return -1; } return use_next_gw_w_part(msg, head_db_start, rule_or_part, rule_or_gw, gw_carr); } return 0; } static int use_next_gw_w_part(struct sip_msg* msg, struct head_db * current_partition, char* rule_att, char* gw_att, char* carr_att) { struct usr_avp *avp, *avp_ru, *avp_sk; unsigned int flags; gparam_t wl_list; dr_group_t grp; int_str val; pv_value_t pv_val; str ruri; dr_part_group_t * part_grp; int ok = 0; pgw_t * dst; struct socket_info *sock; rule_attrs_spec = (pv_spec_p)rule_att; gw_attrs_spec = (pv_spec_p)gw_att; carrier_attrs_spec = (pv_spec_p)carr_att; /* * pop a value from each AVP * (also remove all bogus non-STR top-most values) */ while(1) { if (rule_attrs_spec) { avp = search_first_avp(0, current_partition->rule_attrs_avp, &val, NULL); if (avp) { pv_val.flags = PV_VAL_STR; pv_val.rs = val.s; if (pv_set_value(msg, rule_attrs_spec, 0, &pv_val) != 0) LM_ERR("failed to set value for rule attrs pvar\n"); } } /* remove the old attrs */ if (gw_attrs_spec) { avp = NULL; do { if (avp) destroy_avp(avp); avp = search_first_avp( 0, current_partition->gw_attrs_avp, NULL, NULL); }while (avp && (avp->flags&AVP_VAL_STR)==0 ); if (avp) destroy_avp(avp); avp = search_first_avp(0, current_partition->gw_attrs_avp, &val, NULL); if (avp) { pv_val.flags = PV_VAL_STR; pv_val.rs = val.s; if (pv_set_value(msg, gw_attrs_spec, 0, &pv_val) != 0) LM_ERR("failed to set value for gateway attrs pvar\n"); } } /* remove the old carrier attrs */ if (carrier_attrs_spec) { avp = NULL; do { if (avp) destroy_avp(avp); avp = search_first_avp( 0, current_partition->carrier_attrs_avp, NULL, NULL); }while (avp && (avp->flags&AVP_VAL_STR)==0 ); if (avp) destroy_avp(avp); avp = search_first_avp(0, current_partition->carrier_attrs_avp, &val, NULL); if (avp) { pv_val.flags = PV_VAL_STR; pv_val.rs = val.s; if (pv_set_value(msg, carrier_attrs_spec, 0, &pv_val) != 0) LM_ERR("failed to set value for carrier attrs pvar\n"); } } /* remove the old priprefix */ if (current_partition->gw_priprefix_avp!=-1) { avp = NULL; do { if (avp) destroy_avp(avp); avp = search_first_avp( 0, current_partition->gw_priprefix_avp, NULL, NULL); }while (avp && (avp->flags&AVP_VAL_STR)==0 ); if (avp) destroy_avp(avp); } /* remove the old carrier ID */ if (current_partition->carrier_id_avp!=-1) { avp = NULL; do { if (avp) destroy_avp(avp); avp = search_first_avp( 0, current_partition->carrier_id_avp, NULL, NULL); }while (avp && (avp->flags&AVP_VAL_STR)==0 ); if (avp) destroy_avp(avp); } /* remove old gw ID and search next one */ avp = NULL; do { if (avp) destroy_avp(avp); avp = search_first_avp( 0, current_partition->gw_id_avp, NULL, NULL); }while (avp && (avp->flags&AVP_VAL_STR)==0 ); if (!avp) { LM_WARN("no GWs found at all -> have you done do_routing in script ?? \n"); return -1; } do { if (avp) destroy_avp(avp); avp = search_first_avp( 0, current_partition->gw_id_avp, NULL, NULL); }while (avp && (avp->flags&AVP_VAL_STR)==0 ); /* any GW found ? */ if (!avp) goto rule_fallback; /* search for the first RURI AVP containing a string */ avp_ru = NULL; do { if (avp_ru) destroy_avp(avp_ru); avp_ru = search_first_avp( 0, current_partition->ruri_avp, &val, NULL); }while (avp_ru && (avp_ru->flags&AVP_VAL_STR)==0 ); if (!avp_ru) goto rule_fallback; ruri = val.s; /* search for the first SOCK AVP containing a string */ avp_sk = NULL; do { if (avp_sk) destroy_avp(avp_sk); avp_sk = search_first_avp( 0, current_partition->gw_sock_avp, &val, NULL); }while (avp_sk && (avp_sk->flags&AVP_VAL_STR)==0 ); if (!avp_sk) { /* this shuold not happen, it is a bogus state */ sock = NULL; } else { if (sscanf( val.s.s, "%p", (void**)&sock ) != 1) sock = NULL; destroy_avp(avp_sk); } LM_DBG("new RURI set to <%.*s> via socket <%.*s>\n", val.s.len,val.s.s, sock?sock->name.len:4, sock?sock->name.s:"none"); /* get value for next gw ID from avp */ get_avp_val(avp, &val); /* we have an ID, so we can check the GW state */ lock_start_read( current_partition->ref_lock ); dst = get_gw_by_id( (*current_partition->rdata)->pgw_tree, &val.s); if (dst && (dst->flags & DR_DST_STAT_DSBL_FLAG) == 0) ok = 1; lock_stop_read( current_partition->ref_lock ); if ( ok ) break; /* search for the next available GW*/ destroy_avp(avp_ru); } if (set_ruri( msg, &ruri)==-1) { LM_ERR("failed to rewite RURI\n"); return -1; } if (sock) msg->force_send_socket = sock; destroy_avp(avp_ru); return 1; rule_fallback: LM_DBG("using rule fallback\n"); /* check if a "flags" AVP is there and if fallback allowed */ avp = search_first_avp( 0, current_partition->avpID_store_flags, &val, NULL); if (avp==NULL || !(val.n & DR_PARAM_RULE_FALLBACK) ) return -1; /* fallback allowed, fetch the rest of data from AVPs */ flags = val.n | DR_PARAM_INTERNAL_TRIGGERED; if (!search_first_avp( 0, current_partition->avpID_store_group, &val, NULL)) { LM_ERR("Cannot find group AVP during a fallback\n"); goto fallback_failed; } grp.type = 0; grp.u.grp_id = val.n; if (!search_first_avp( AVP_VAL_STR, current_partition->avpID_store_whitelist, &val, NULL)) { wl_list.type = 0; } else { wl_list.type = GPARAM_TYPE_STR; wl_list.v.sval = val.s; wl_list.v.sval.s[--wl_list.v.sval.len] = 0; } part_grp = pkg_malloc(sizeof(dr_part_group_t)); if(part_grp == NULL) { LM_ERR("No more pkg memory!\n"); return -1; } init_part_grp(&part_grp, current_partition, &grp); if (do_routing( msg, part_grp, flags, wl_list.type?&wl_list:NULL)==1) { return 1; } fallback_failed: /* prevent any more fallback by removing the flags AVP */ destroy_avp(avp); return -1; } #define DR_MAX_GWLIST 64 static int sort_rt_dst(pgw_list_t *pgwl, unsigned short size, int weight, unsigned short *idx) { unsigned short running_sum[DR_MAX_GWLIST]; unsigned int i, first, weight_sum, rand_no; /* populate the index array */ for( i=0 ; i1) { /* calculate the running sum */ for( i=first,weight_sum=0 ; irand_no) break; if (i==size) { LM_CRIT("bug in weight sort\n"); return -1; } } else { /* randomly select index */ // i = (unsigned int)((size-first)*((float)rand()/RAND_MAX)); i = first; } LM_DBG("selecting element %d with weight %d\n", idx[i], pgwl[ idx[i] ].weight); /* "i" is the selected element : swap it with first position and retake alg without first elem */ rand_no = idx[i]; idx[i] = idx[first]; idx[first] = rand_no; first ++; } return 0; } inline static int push_gw_for_usage(struct sip_msg *msg, struct head_db *current_partition, struct sip_uri *uri, pgw_t *gw , str *c_id, str *c_attrs, int idx) { char buf[PTR_STRING_SIZE]; /* a hexa string */ str *ruri; int_str val; if( current_partition==NULL ) { return -1; } /* build uri*/ ruri = build_ruri( uri, gw->strip, &gw->pri, &gw->ip_str); if (ruri==0) { LM_ERR("failed to build new ruri\n"); return -1; } LM_DBG("adding gw [%.*s] as \"%.*s\" in order %d\n", gw->id.len, gw->id.s, ruri->len, ruri->s, idx); /* first GW to be added ? */ if (idx==0) { /* add to RURI */ if (set_ruri( msg, ruri)!= 0 ) { LM_ERR("failed to set new RURI\n"); goto error; } /* set socket to be used */ if (gw->sock) msg->force_send_socket = gw->sock; } else { /* add ruri as AVP */ val.s = *ruri; if (add_avp_last( AVP_VAL_STR, current_partition->ruri_avp, val)!=0 ) { LM_ERR("failed to insert ruri avp\n"); goto error; } /* add GW sock avp */ val.s.len = 1 + snprintf( buf, PTR_STR_SIZE, "%p", gw->sock ); val.s.s = buf; LM_DBG("setting GW sock [%.*s] as avp\n",val.s.len, val.s.s); if (add_avp_last( AVP_VAL_STR, current_partition->gw_sock_avp, val)!=0 ) { LM_ERR("failed to insert sock avp\n"); goto error; } } /* add GW id avp */ val.s = gw->id; LM_DBG("setting GW id [%.*s] as avp\n",val.s.len, val.s.s); if (add_avp_last( AVP_VAL_STR, current_partition->gw_id_avp, val)!=0 ) { LM_ERR("failed to insert ids avp\n"); goto error; } /* add internal GW attrs avp if requested at least once in the script */ if (populate_gw_attrs) { val.s = gw->attrs.s? gw->attrs : attrs_empty; LM_DBG("setting GW attr [%.*s] as avp\n", val.s.len, val.s.s); if (add_avp_last(AVP_VAL_STR, current_partition->gw_attrs_avp, val)!=0){ LM_ERR("failed to insert gw attrs avp\n"); goto error; } } /* add GW priprefix avp */ if (current_partition->gw_priprefix_avp!=-1) { val.s = gw->pri.s? gw->pri : attrs_empty; LM_DBG("setting GW priprefix [%.*s] as avp\n",val.s.len,val.s.s); if (add_avp_last(AVP_VAL_STR, current_partition->gw_priprefix_avp, val)!=0){ LM_ERR("failed to insert priprefix avp\n"); goto error; } } if (current_partition->carrier_id_avp!=-1) { val.s = (c_id && c_id->s)? *c_id : attrs_empty ; LM_DBG("setting CR Id [%.*s] as avp\n",val.s.len,val.s.s); if (add_avp_last(AVP_VAL_STR, current_partition->carrier_id_avp, val)!=0){ LM_ERR("failed to insert attrs avp\n"); goto error; } } /* add internal carrier attrs avp if requested at least once * in the script */ if (populate_carrier_attrs) { val.s = (c_attrs && c_attrs->s)? *c_attrs : attrs_empty; LM_DBG("setting CR attr [%.*s] as avp\n", val.s.len, val.s.s); if (add_avp_last(AVP_VAL_STR, current_partition->carrier_attrs_avp, val)!=0) { LM_ERR("failed to insert carrier attrs avp\n"); goto error; } } pkg_free(ruri->s); return 0; error: pkg_free(ruri->s); return -1; } static inline int is_dst_in_list(void* dst, pgw_list_t *list, unsigned short len) { unsigned short i; if (list==NULL) return 1; for( i=0 ; ipartition.len==name->len && memcmp( it->partition.s, name->s, name->len)==0 ) { return it; } it = it->next; } return NULL; /* partition was not found */ } static int do_routing(struct sip_msg* msg, dr_part_group_t * part_group, int flags, gparam_t* whitelist) { unsigned short dsts_idx[DR_MAX_GWLIST]; unsigned short carrier_idx[DR_MAX_GWLIST]; struct to_body *from; struct sip_uri uri; rt_info_t *rt_info; pv_value_t pv_val; struct usr_avp *avp, *avp_prefix=NULL, *avp_index=NULL; str parsed_whitelist; pgw_list_t *dst, *cdst; pgw_list_t *wl_list; unsigned int prefix_len; unsigned int rule_idx; struct head_db *current_partition=NULL; unsigned short wl_len; dr_group_t * drg; str username; int grp_id; int i, j, n; int_str val; str ruri; str next_carrier_attrs = {NULL, 0}; str next_gw_attrs = {NULL, 0}; int ret, fret; char tmp; char *ruri_buf; gparam_p tmp_gparam = NULL; ret = -1; ruri_buf = NULL; wl_list = NULL; rt_info = NULL; if(use_partitions) { if(part_group == NULL || part_group->dr_part == NULL || part_group->dr_part->type == DR_NO_PART) { LM_ERR("Partition name is mandatory for do_routing\n"); return -1; } if(part_group->dr_part->type == DR_GPARAM_PART) { if ((fret=to_partition(msg, part_group->dr_part, ¤t_partition))<0) { return -1; } else if (fret == 1) { tmp_gparam = part_group->dr_part->v.part_name; part_group->dr_part->type = DR_WILDCARD_PART; } } else if(part_group->dr_part->type == DR_PTR_PART) { current_partition = part_group->dr_part->v.part; } if (part_group->dr_part->type == DR_WILDCARD_PART) { for (current_partition = head_db_start; current_partition; current_partition = current_partition->next) { part_group->dr_part->v.part = current_partition; part_group->dr_part->type = DR_PTR_PART; ret=do_routing( msg, part_group, flags, whitelist); if (ret > 0) { if (partition_pvar.s) { pv_val.rs = current_partition->partition; pv_val.flags = PV_VAL_STR; if (pv_set_value(msg, &partition_spec, 0, &pv_val) != 0) { LM_ERR("cannot print the PV-formatted" " partition string\n"); return -1; } } break; } } /* restore to initial state */ if (tmp_gparam) { part_group->dr_part->type = DR_GPARAM_PART; } else { memset(part_group->dr_part, 0, sizeof(dr_partition_t)); part_group->dr_part->type = DR_WILDCARD_PART; } /* ret must be less than 0 here if nothing found */ return ret; } } else { if(part_group->dr_part->type == DR_PTR_PART) { current_partition = part_group->dr_part->v.part; } else { LM_ERR("Error while loading configuration for do_routing\n"); } } drg = part_group->group; /* allow no GWs if we're only trying to use DR for checking purposes */ if ( *(current_partition->rdata)==0 || ((flags & DR_PARAM_ONLY_CHECK) == 0 && (*(current_partition->rdata))->pgw_tree==0 )) { LM_DBG("empty routing table\n"); goto error1; } /* do some cleanup first (if without the CHECK_ONLY flag) */ if ((flags & DR_PARAM_ONLY_CHECK) == 0) { destroy_avps( 0, current_partition->ruri_avp, 1); destroy_avps( 0, current_partition->gw_id_avp, 1); destroy_avps( 0, current_partition->gw_sock_avp, 1); destroy_avps( 0, current_partition->rule_attrs_avp, 1); destroy_avps( 0, current_partition->gw_attrs_avp, 1); destroy_avps( 0, current_partition->carrier_attrs_avp, 1); if ((current_partition->gw_priprefix_avp)!=-1) destroy_avps( 0, current_partition->gw_priprefix_avp, 1); if ((current_partition->rule_id_avp)!=-1) destroy_avps( 0, current_partition->rule_id_avp, 1); if ((current_partition->rule_prefix_avp)!=-1) destroy_avps( 0, current_partition->rule_prefix_avp, 1); } if ( !(flags & DR_PARAM_INTERNAL_TRIGGERED) ) { /* not internally triggered, so get data from SIP msg */ if(drg==NULL) { /* get the username from FROM_HDR */ if (parse_from_header(msg)!=0) { LM_ERR("unable to parse from hdr\n"); goto error1; } from = (struct to_body*)msg->from->parsed; /* parse uri */ if (parse_uri( from->uri.s, from->uri.len, &uri)!=0) { LM_ERR("unable to parse from uri\n"); goto error1; } grp_id = get_group_id( &uri, current_partition); if (grp_id<0) { LM_ERR("failed to get group id\n"); goto error1; } } else { if(drg->type==0) grp_id = (int)drg->u.grp_id; else if(drg->type==1) { grp_id = 0; /* call get avp here */ if((avp=search_first_avp(0, drg->u.avp_name, &val, 0))==NULL || (avp->flags&AVP_VAL_STR) ) { LM_ERR( "failed to get group id from avp\n"); goto error1; } grp_id = val.n; } else grp_id = 0; } /* get the number/RURI and make a copy of it */ ruri = *GET_RURI(msg); ruri_buf = (char*)pkg_malloc(ruri.len); if (ruri_buf==NULL) { LM_ERR("no more pkg mem (needed %d)\n",ruri.len); goto error1; } memcpy(ruri_buf, ruri.s, ruri.len); ruri.s = ruri_buf; /* parse ruri */ if (parse_uri( ruri.s, ruri.len, &uri)!=0) { LM_ERR("unable to parse RURI\n"); goto error1; } username = uri.user; /* search all rules on dr tree (start from beginning) */ rule_idx = 0; } else { /* resume index on the rule under same prefix */ avp_index = search_first_avp( 0, current_partition->avpID_store_index, &val, 0); if (avp_index==NULL) { LM_ERR("Cannot find index AVP during a fallback\n"); goto error1; } rule_idx = val.n; /* prefix to resume with */ avp_prefix = search_first_avp( AVP_VAL_STR, current_partition->avpID_store_prefix, &val, 0); if (avp_prefix==NULL) { LM_ERR("Cannot find prefix AVP during a fallback\n"); goto error1; } username = val.s; /* still something to look for ? */ if (username.len==0) return -1; /* original RURI to be used when building RURIs for new attempts */ if (search_first_avp( AVP_VAL_STR, current_partition->avpID_store_ruri, &val, 0)==NULL) { LM_ERR("Cannot find ruri AVP during a fallback\n"); goto error1; } if (parse_uri( val.s.s, val.s.len, &uri)!=0) { LM_ERR("unable to parse RURI from AVP\n"); goto error1; } grp_id = (int)drg->u.grp_id; ruri.s = NULL; ruri.len = 0; } LM_DBG("using dr group %d, rule_idx %d, username %.*s\n", grp_id,rule_idx,username.len,username.s); /* ref the data for reading */ lock_start_read( current_partition->ref_lock ); search_again: if (rt_info) { /* we are here because of the "search_again", on a sequential retry based on rule FALL BACK */ /* => force fallback, either on next rule, either on shorter prefix */ username.len = prefix_len -(rule_idx?0:1); LM_DBG("doing internal fallback, prefix_len=%d,rule_idx=%d\n", username.len, rule_idx); if (username.len==0 && rule_idx==0) { /* disable failover as nothing left */ flags = flags & ~DR_PARAM_RULE_FALLBACK; goto error2; } } /* search a prefix */ rt_info = get_prefix( (*(current_partition->rdata))->pt, &username, (unsigned int)grp_id,&prefix_len, &rule_idx); if (flags & DR_PARAM_STRICT_LEN) { if (rt_info==NULL || prefix_len!=username.len) goto error2; } if (rt_info==0) { LM_DBG("no matching for prefix \"%.*s\"\n", username.len, username.s); /* try prefixless rules */ rt_info = check_rt( &(*(current_partition->rdata))->noprefix, (unsigned int)grp_id); if (rt_info==0) { LM_DBG("no prefixless matching for " "grp %d\n", grp_id); goto error2; } prefix_len = 0; } if (rt_info->route_idx>0 && rt_info->route_idxroute_idx].a, msg ); if (ret&ACT_FL_DROP) { /* drop the action */ LM_DBG("script route %s drops routing " "by %d\n", rlist[rt_info->route_idx].name, ret); ret = -1; goto error2; } } /* if only checking the prefix, we are done here */ if (flags & DR_PARAM_ONLY_CHECK) goto no_gws; /* do we have anything left to failover to ? */ if (prefix_len==0 && rule_idx==0) /* disable failover as nothing left */ flags = flags & ~DR_PARAM_RULE_FALLBACK; /* START EVALUATING THE DESTINATIONS FROM LIST ! */ n = 0; if (rt_info->pgwl==NULL) { LM_INFO("no destination for dr group %d, rule_idx %d, username %.*s\n", grp_id,rule_idx,username.len,username.s); if ( flags & DR_PARAM_RULE_FALLBACK ) goto search_again; goto error2; } /* sort the destination elements in the rule */ i = sort_rt_dst(rt_info->pgwl, rt_info->pgwa_len, flags&DR_PARAM_USE_WEIGTH, dsts_idx); if (i!=0) { LM_ERR("failed to sort destinations in rule\n"); goto error2; } /* evaluate and parse the whitelist of GWs/CARRIERs, if provided and if the first time here */ if (whitelist && wl_list==NULL) { if (fixup_get_svalue(msg, whitelist, &parsed_whitelist)!=0) { LM_ERR("failed to evaluate whitelist-> ignoring...\n"); } else { tmp = parsed_whitelist.s[parsed_whitelist.len]; parsed_whitelist.s[parsed_whitelist.len] = 0; if (parse_destination_list( *(current_partition->rdata), parsed_whitelist.s, &wl_list, &wl_len, 1)!=0) { LM_ERR("invalid format in whitelist-> ignoring...\n"); wl_list = NULL; } parsed_whitelist.s[parsed_whitelist.len] = tmp; } } /* iterate through the list, skip the disabled destination */ for ( i=0 ; ipgwa_len ; i++ ) { dst = &rt_info->pgwl[dsts_idx[i]]; /* is the destination carrier or gateway ? */ if (dst->is_carrier) { /* is carrier turned off ? */ if( dst->dst.carrier->flags & DR_CR_FLAG_IS_OFF || !is_dst_in_list( (void*)dst->dst.carrier, wl_list, wl_len) ) continue; /* any gws for this carrier ? */ if( dst->dst.carrier->pgwl==NULL ) continue; /* sort the gws of the carrier */ j = sort_rt_dst(dst->dst.carrier->pgwl, dst->dst.carrier->pgwa_len, dst->dst.carrier->flags&DR_CR_FLAG_WEIGHT, carrier_idx); if (j!=0) { LM_ERR("failed to sort gws for carrier <%.*s>, skipping\n", dst->dst.carrier->id.len, dst->dst.carrier->id.s); continue; } /* iterate through the list of GWs provided by carrier */ for ( j=0 ; jdst.carrier->pgwa_len ; j++ ) { cdst = &dst->dst.carrier->pgwl[carrier_idx[j]]; /* is gateway disabled ? */ if (cdst->dst.gw->flags & DR_DST_STAT_DSBL_FLAG ) { /*ignore it*/ } else { /* add gateway to usage list */ if ( push_gw_for_usage(msg, current_partition, &uri, cdst->dst.gw , &dst->dst.carrier->id, &dst->dst.carrier->attrs, n ) ) { LM_ERR("failed to use gw <%.*s>, skipping\n", cdst->dst.gw->id.len, cdst->dst.gw->id.s); } else { n++; /* only export the top-most carrier/gw * attributes in the script */ if (n == 1) { next_carrier_attrs = dst->dst.carrier->attrs; next_gw_attrs = cdst->dst.gw->attrs; } /* use only first valid GW */ if (dst->dst.carrier->flags&DR_CR_FLAG_FIRST) break; } } } } else { /* is gateway disabled ? */ if (dst->dst.gw->flags & DR_DST_STAT_DSBL_FLAG || !is_dst_in_list( (void*)dst->dst.gw, wl_list, wl_len) ) continue; /* add gateway to usage list */ if ( push_gw_for_usage(msg, current_partition, &uri, dst->dst.gw, NULL, NULL, n) ) { LM_ERR("failed to use gw <%.*s>, skipping\n", dst->dst.gw->id.len, dst->dst.gw->id.s); } else { n++; /* only export the first gw attributes in the script */ if (n == 1) { next_carrier_attrs.s = NULL; next_gw_attrs = dst->dst.gw->attrs; } } } } if( n < 1) { LM_INFO("All the gateways are disabled\n"); if ( flags & DR_PARAM_RULE_FALLBACK ) goto search_again; goto error2; } pv_val.flags = PV_VAL_STR; if (gw_attrs_spec) { pv_val.flags = PV_VAL_STR; pv_val.rs = !next_gw_attrs.s ? attrs_empty : next_gw_attrs; if (pv_set_value(msg, gw_attrs_spec, 0, &pv_val) != 0) { LM_ERR("failed to set value for gateway attrs pvar - do_routing\n"); goto error2; } } if (carrier_attrs_spec) { pv_val.flags = PV_VAL_STR; pv_val.rs = !next_carrier_attrs.s ? attrs_empty : next_carrier_attrs; if (pv_set_value(msg, carrier_attrs_spec, 0, &pv_val) != 0) { LM_ERR("failed to set value for carrier attrs pvar - do_routing\n"); goto error2; } } no_gws: /* add RULE prefix avp */ if (current_partition->rule_prefix_avp!=-1) { val.s.s = username.s ; val.s.len = prefix_len; LM_DBG("setting RULE prefix [%.*s] \n",val.s.len,val.s.s); if (add_avp( AVP_VAL_STR, current_partition->rule_prefix_avp, val)!=0 ) { LM_ERR("failed to insert rule prefix avp\n"); goto error2; } } /* add internal RULE attrs avp if requested at least once in the script */ if (populate_rule_attrs) { val.s = !rt_info->attrs.s ? attrs_empty : rt_info->attrs; LM_DBG("setting RULE attr [%.*s] \n", val.s.len, val.s.s); if (add_avp( AVP_VAL_STR, current_partition->rule_attrs_avp, val) != 0) { LM_ERR("failed to insert rule attrs avp\n"); goto error2; } if (rule_attrs_spec) { pv_val.flags = PV_VAL_STR; pv_val.rs = val.s; if (pv_set_value(msg, rule_attrs_spec, 0, &pv_val) != 0) { LM_ERR("failed to set value for rule attrs pvar\n"); goto error2; } } } /* add RULE id avp */ if (current_partition->rule_id_avp!=-1) { val.n = (int) rt_info->id; LM_DBG("setting RULE id [%d] as avp\n",val.n); if (add_avp( 0, current_partition->rule_id_avp, val)!=0 ) { LM_ERR("failed to insert rule ids avp\n"); goto error2; } } /* we are done reading -> unref the data */ lock_stop_read( current_partition->ref_lock ); if ( flags & DR_PARAM_RULE_FALLBACK ) { if ( !(flags & DR_PARAM_INTERNAL_TRIGGERED) ) { /* first time ? we need to save some date, to be able to do the rule fallback later in "next_gw" */ LM_DBG("saving rule_idx %d, prefix %.*s\n",rule_idx, prefix_len - (rule_idx?0:1), username.s); val.n = rule_idx; if (add_avp( 0 , current_partition->avpID_store_index, val) ) { LM_ERR("failed to insert index avp for fallback\n"); flags = flags & ~DR_PARAM_RULE_FALLBACK; } /* if no rules available on current prefix (index is 0), simply reduce the len of the prefix from start, to lookup another prefix in the DR tree */ val.s.s = username.s ; val.s.len = prefix_len - (rule_idx?0:1); if (add_avp( AVP_VAL_STR, current_partition->avpID_store_prefix, val) ) { LM_ERR("failed to insert prefix avp for fallback\n"); flags = flags & ~DR_PARAM_RULE_FALLBACK; } /* also store current ruri as we will need it */ val.s = ruri; if (add_avp( AVP_VAL_STR, current_partition->avpID_store_ruri, val) ) { LM_ERR("failed to insert ruri avp for fallback\n"); flags = flags & ~DR_PARAM_RULE_FALLBACK; } /* we need to save a some date, to be able to do the rule fallback later in "next_gw" (prefix/index already added) */ if (wl_list) { val.s = parsed_whitelist ; val.s.len++; /* we need extra space to place \0 when using */ if (add_avp( AVP_VAL_STR, current_partition->avpID_store_whitelist, val) ) { LM_ERR("failed to insert whitelist avp for fallback\n"); flags = flags & ~DR_PARAM_RULE_FALLBACK; } } val.n = grp_id ; if (add_avp( 0, current_partition->avpID_store_group, val) ) { LM_ERR("failed to insert group avp for fallback\n"); flags = flags & ~DR_PARAM_RULE_FALLBACK; } val.n = flags ; if (add_avp( 0, current_partition->avpID_store_flags, val) ) { LM_ERR("failed to insert flags avp for fallback\n"); } } else { /* update the fallback coordonats for next resume */ /* using ugly hack by directly accessing the AVP data in order to perform changes - we want to avoid re-creating the AVP -bogdan */ avp_index->data = (void *)(long)rule_idx; if (rule_idx==0) { void *data; /* all rules under current prefix used -> reduce the prefix */ data = (void*)&avp_prefix->data; ((str*)data)->len = prefix_len-1; } LM_DBG("updating to %d, prefix %.*s \n",rule_idx, prefix_len-(rule_idx?1:0),username.s); } } if (ruri_buf) pkg_free(ruri_buf); return 1; error2: /* we are done reading -> unref the data */ lock_stop_read( current_partition->ref_lock ); error1: if (ruri_buf) pkg_free(ruri_buf); return ret; } static int route2_carrier(struct sip_msg* msg, char* part_carrier, char* gw_att_pv, char* carr_att_pv) { unsigned short carrier_idx[DR_MAX_GWLIST]; struct sip_uri uri; pgw_list_t *cdst; pcr_t *cr; pv_value_t pv_val; str ruri, id; str next_carrier_attrs = {NULL, 0}; str next_gw_attrs = {NULL, 0}; int j,n; dr_part_old_t * part_cr; struct head_db * current_partition = 0; char *ruri_buf=NULL; part_cr = (dr_part_old_t*)part_carrier; if(use_partitions) { if(part_cr == NULL) { LM_ERR("Partition is mandatory for route2_carrier.\n"); return -1; } if(part_cr->dr_part->type == DR_PTR_PART) { current_partition = part_cr->dr_part->v.part; } else if(part_cr->dr_part->type == DR_GPARAM_PART) { if(to_partition(msg, part_cr->dr_part, ¤t_partition) < 0) return -1; } } else { current_partition = head_db_start; } if ( (*current_partition->rdata)==0 || (*current_partition->rdata)->pgw_tree==0 ) { LM_DBG("empty routing table\n"); return -1; } /* get the carrier ID */ if (fixup_get_svalue(msg, (gparam_p)part_cr->gw_or_cr, &id) != 0) { LM_ERR("failed to get string value for carrier ID\n"); return -1; } gw_attrs_spec = (pv_spec_p) gw_att_pv; carrier_attrs_spec = (pv_spec_p) carr_att_pv; /* do some cleanup first */ destroy_avps( 0, current_partition->ruri_avp, 1); destroy_avps( 0, current_partition->gw_id_avp, 1); destroy_avps( 0, current_partition->gw_sock_avp, 1); destroy_avps( 0, current_partition->gw_attrs_avp, 1); destroy_avps( 0, current_partition->rule_attrs_avp, 1); destroy_avps( 0, current_partition->carrier_attrs_avp, 1); if (current_partition->gw_priprefix_avp!=-1) destroy_avps( 0, current_partition->gw_priprefix_avp, 1); if (current_partition->rule_id_avp!=-1) destroy_avps( 0, current_partition->rule_id_avp, 1); if (current_partition->rule_prefix_avp!=-1) destroy_avps( 0, current_partition->rule_prefix_avp, 1); /* get the RURI */ ruri = *GET_RURI(msg); ruri_buf = (char*)pkg_malloc(ruri.len); if (ruri_buf==NULL) { LM_ERR("no more pkg mem (needed %d)\n",ruri.len); return -1; } memcpy(ruri_buf, ruri.s, ruri.len); ruri.s = ruri_buf; /* parse ruri */ if (parse_uri( ruri.s, ruri.len, &uri)!=0) { LM_ERR("unable to parse RURI\n"); goto error_free; } /* ref the data for reading */ lock_start_read( current_partition->ref_lock ); cr = get_carrier_by_id( (*current_partition->rdata)->carriers_tree, &id ); if (cr==NULL) { LM_ERR("carrier <%.*s> was not found\n", id.len, id.s ); goto error; } /* is carrier turned off ? */ if( cr->flags & DR_CR_FLAG_IS_OFF ) { LM_NOTICE("routing to disabled carrier <%.*s> failed\n", cr->id.len, cr->id.s); goto error; } /* any GWs for the carrier? */ if (cr->pgwl==NULL) goto no_gws; /* sort the gws of the carrier */ j = sort_rt_dst( cr->pgwl, cr->pgwa_len, cr->flags&DR_CR_FLAG_WEIGHT, carrier_idx); if (j!=0) { LM_ERR("failed to sort gws for carrier <%.*s>, skipping\n", cr->id.len, cr->id.s); goto error; } /* iterate through the list of GWs provided by carrier */ for ( j=0,n=0 ; jpgwa_len ; j++ ) { cdst = &cr->pgwl[carrier_idx[j]]; /* is gateway disabled ? */ if (cdst->dst.gw->flags & DR_DST_STAT_DSBL_FLAG ) { /*ignore it*/ } else { /* add gateway to usage list */ if ( push_gw_for_usage(msg, current_partition, &uri, cdst->dst.gw, &cr->id, &cr->attrs, n ) ) { LM_ERR("failed to use gw <%.*s>, skipping\n", cdst->dst.gw->id.len, cdst->dst.gw->id.s); } else { n++; /* only export the top-most carrier/gw * attributes in the script */ if (n == 1) { next_carrier_attrs = cr->attrs; next_gw_attrs = cdst->dst.gw->attrs; } /* use only first valid GW */ if (cr->flags&DR_CR_FLAG_FIRST) break; } } } if( n < 1) { LM_ERR("All the gateways are disabled\n"); goto error; } pv_val.flags = PV_VAL_STR; if (gw_attrs_spec) { pv_val.flags = PV_VAL_STR; pv_val.rs = !next_gw_attrs.s ? attrs_empty : next_gw_attrs; if (pv_set_value(msg, gw_attrs_spec, 0, &pv_val) != 0) { LM_ERR("failed to set value for gateway attrs pvar\n"); goto error; } } if (carrier_attrs_spec) { pv_val.flags = PV_VAL_STR; pv_val.rs = !next_carrier_attrs.s ? attrs_empty : next_carrier_attrs; if (pv_set_value(msg, carrier_attrs_spec, 0, &pv_val) != 0) { LM_ERR("failed to set value for carrier attrs pvar\n"); goto error; } } no_gws: /* we are done reading -> unref the data */ lock_stop_read( current_partition->ref_lock ); if (ruri_buf) pkg_free(ruri_buf); return 1; error: /* we are done reading -> unref the data */ lock_stop_read( current_partition->ref_lock ); error_free: if (ruri_buf) pkg_free(ruri_buf); return -1; } static int route2_gw(struct sip_msg* msg, char* ch_part_gw, char* gw_att_pv) { struct sip_uri uri; pgw_t *gw; pv_value_t pv_val; str ruri, ids, id; str next_gw_attrs = {NULL, 0}; char *p; int idx; dr_part_old_t * part_gw = (dr_part_old_t*)ch_part_gw; struct head_db * current_partition = 0; char *ruri_buf = NULL; if( part_gw==NULL ) { LM_ERR("No gateway to route to\n"); return -1; } if(use_partitions) { if(part_gw == NULL) { LM_ERR("Partition is mandatory for route2_gw.\n"); return -1; } if(part_gw->dr_part->type == DR_PTR_PART) { current_partition = part_gw->dr_part->v.part; } else if(part_gw->dr_part->type == DR_GPARAM_PART) { if(to_partition(msg, part_gw->dr_part, ¤t_partition) < 0) return -1; } } else { if(head_db_start == NULL) { LM_ERR("Problem loading configuration for route_to_gw\n"); return -1; } current_partition = head_db_start; } if ( (*current_partition->rdata)==0 || (*current_partition->rdata)->pgw_tree==0 ) { LM_DBG("empty routing table\n"); return -1; } gw_attrs_spec = (pv_spec_p)gw_att_pv; /* get the gw ID */ if (fixup_get_svalue(msg, (gparam_p)part_gw->gw_or_cr, &ids) != 0) { LM_ERR("Invalid pseudo variable!\n"); return -1; } str_trim_spaces_lr(ids); if (ids.s[0] == ',' || ids.s[ids.len-1] == ',') { LM_ERR("Empty slot\n"); return -1; } /* get the RURI */ ruri = *GET_RURI(msg); ruri_buf = (char*)pkg_malloc(ruri.len); if (ruri_buf==NULL) { LM_ERR("no more pkg mem (needed %d)\n",ruri.len); return -1; } memcpy(ruri_buf, ruri.s, ruri.len); ruri.s = ruri_buf; /* parse ruri */ if (parse_uri( ruri.s, ruri.len, &uri)!=0) { LM_ERR("unable to parse RURI\n"); goto error_free; } /* ref the data for reading */ lock_start_read( current_partition->ref_lock ); idx = 0; do { id.s = ids.s; p = q_memchr( ids.s , ',' , ids.len); id.len = (p==NULL)?ids.len:(p-ids.s); ids.len -= id.len + (p?1:0); ids.s += id.len + (p?1:0); str_trim_spaces_lr(id); if (id.len<=0) { LM_ERR("empty slot\n"); lock_stop_read( current_partition->ref_lock ); return -1; } else { LM_DBG("found and looking for gw id <%.*s>,len=%d\n",id.len, id.s, id.len); gw = get_gw_by_id( (*current_partition->rdata)->pgw_tree, &id ); if (gw==NULL) { LM_ERR("no GW found with ID <%.*s> -> ignorring\n", id.len, id.s); } else if ( push_gw_for_usage(msg, current_partition, &uri, gw, NULL, NULL, idx ) ) { LM_ERR("failed to use gw <%.*s>, skipping\n", gw->id.len, gw->id.s); } else { idx++; /* only export the top-most gw attributes in the script */ if (idx == 1) next_gw_attrs = gw->attrs; } } } while(ids.len>0); /* we are done reading -> unref the data */ lock_stop_read( current_partition->ref_lock ); if ( idx==0 ) { LM_ERR("no GW added at all\n"); goto error_free; } if (gw_attrs_spec) { pv_val.flags = PV_VAL_STR; pv_val.rs = !next_gw_attrs.s ? attrs_empty : next_gw_attrs; if (pv_set_value(msg, gw_attrs_spec, 0, &pv_val) != 0) { LM_ERR("failed to set value for gateway attrs pvar\n"); goto error_free; } } if (ruri_buf) pkg_free(ruri_buf); return 1; error_free: if (ruri_buf) pkg_free(ruri_buf); return -1; } int fxup_split_param(void ** fst_param, void ** scnd_param) { char * ch_it ; *scnd_param = 0; if(*fst_param == NULL || ((char*)*fst_param)[0] == 0) { /* NULL string */ return -1; } for(ch_it=*fst_param; (*ch_it)!=0 && (*ch_it)!=':'; ch_it++); if(*ch_it == 0) { LM_CRIT("No partition specified. Missing ':'.\n"); return -1; /* partition name was not specified */ } /* partition name exits */ *ch_it = 0; *scnd_param = ch_it+1; /* the second parameter */ return 0; } int fxup_get_partition(void ** part_name, dr_partition_t ** x) { str str_part_name; struct head_db* part; trim_char((char**)part_name); *x = (dr_partition_t*)pkg_malloc( sizeof(dr_partition_t) ); if(*x == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(*x, 0, sizeof(dr_partition_t)); if(part_name == 0 || *part_name == 0 || **(char**)part_name == 0) { (*x)->type = DR_NO_PART; /* NO partition specified */ LM_ERR("No partition\n"); return 0; } if( fixup_sgp((void**)part_name)!=0 ) { LM_CRIT("Failed to get partition name\n"); return -1; } if( ((gparam_p)(*part_name))->type==GPARAM_TYPE_STR ) { /* was defined statically */ str_part_name = (( (gparam_p) (*part_name))->v.sval); str_trim_spaces_lr(str_part_name); if (str_part_name.len == 1 && str_part_name.s[0] == '*') { (*x)->type = DR_WILDCARD_PART; return 0; } if((part = get_partition(&str_part_name)) == NULL) { LM_CRIT("Partition <%.*s> was not found.\n", str_part_name.len, str_part_name.s); return -1; /* partition was not found */ } (*x)->v.part = part; (*x)->type = DR_PTR_PART; } else { /* defined via avp/pv => will be evaluated at runtime*/ (*x)->v.part_name = *part_name; (*x)->type = DR_GPARAM_PART; } return 0; } /* gets partition name from avp, and searches for that partition */ static int to_partition(struct sip_msg* msg, dr_partition_t *part, struct head_db ** current_partition) { str part_name; if(fixup_get_svalue(msg, part->v.part_name, &part_name) < 0) { LM_ERR("Failed to parse avp/pve.\n"); return -1; } str_trim_spaces_lr(part_name); /* check for wildcard operator */ if ( part_name.len == 1 && part_name.s[0] == '*') { return 1; } if((*current_partition = get_partition(&part_name)) == NULL) { LM_ERR("Partition <%.*s> was not found.\n", part_name.len, part_name.s); return -1; } return 0; } /* Returns a gparam_p to the containing partition if * specified. If partition isn't specified return NULL */ gparam_t * fixup_get_partition(void** param) { gparam_t *part_name = 0; char *ch_it,*s = (char*)*param, *separator; if( s==NULL || s[0]==0 ) { return NULL; } if( use_partitions==0 ) /* partition will be omitted */ return NULL; for( ch_it=s; (*ch_it)!=0 && (*ch_it)!=':'; ch_it++); separator = ch_it; if( (*separator)==':' ) { /* partition was specified */ part_name = pkg_malloc(sizeof(gparam_t)); if( part_name==0 ) { LM_ERR("No more pkg memory for part_name\n"); } memset( part_name, 0, sizeof(gparam_t)); while( (*s)==' ' ) s++; /* trim space left-of partition name */ (*ch_it) = 0; ch_it--; while( (*ch_it)==' ' && ch_it!=s) { (*ch_it) = 0; ch_it--; } if( fixup_sgp( (void**)&s )<0 ) /* get partition name */ return NULL; part_name = (gparam_p)s; *param = separator+1; /* go to group */ } return part_name; } static int fixup_dr_disable(void ** param, int param_no) { if(use_partitions) { switch(param_no) { case 1: trim_char((char**)param); return fixup_sgp(param); } } LM_ERR("Too many parameters. (if you don't use partitions)\n"); return -1; } static int fixup_do_routing(void** param, int param_no) { char *s; dr_group_t * drg = 0; dr_part_group_t * part_param; pv_spec_t avp_spec; unsigned short dummy; char * scnd_param; str r; s = (char*)*param; switch (param_no) { /* [partition name':']group ID */ case 1: part_param = pkg_malloc(sizeof(dr_part_group_t)); if(part_param == NULL) { LM_ERR("No more pkg memory.\n"); return -1; } memset(part_param, 0, sizeof(dr_part_group_t)); if(use_partitions == 1) { if(fxup_split_param(param, (void **)&scnd_param) < 0) { return -1; } if(fxup_get_partition(param, &(part_param->dr_part)) < 0) { return -1; } if(part_param->dr_part->type == DR_NO_PART) { LM_ERR("Partition name is mandatory do_routing"); } } else { scnd_param = s; } s = scnd_param; trim_char(&s); if ( s==NULL || s[0]==0 ) { *param = (void*)part_param; return 0; } drg = pkg_malloc(sizeof(dr_group_t)); if(drg == NULL) { LM_ERR("No more pkg memory.\n"); return -1; } memset(drg, 0, sizeof(dr_group_t)); if (s[0]=='$') { /* param is a PV (AVP only supported) */ r.s = s; r.len = strlen(s); if (pv_parse_spec( &r, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %s AVP definition\n", s); return E_CFG; } if( pv_get_avp_name(0, &(avp_spec.pvp), &drg->u.avp_name, &dummy )!=0) { LM_ERR("[%s]- invalid AVP definition\n", s); return E_CFG; } drg->type = 1; /* do not free the param as the AVP spec may point inside this string*/ } else { while(s && *s) { if(*s<'0' || *s>'9') { LM_ERR( "bad number\n"); return E_UNSPEC; } drg->u.grp_id = (drg->u.grp_id)*10+(*s-'0'); s++; } } part_param->group = drg; *param = (void*)part_param; return 0; /* string with flags */ case 2: return fixup_sgp(param); /* white list of GWs/Carriers */ case 3: return fixup_spve(param); /* rule | gateway | carrier attributes output pvars */ case 4: populate_rule_attrs = 1; return fixup_pvar(param); case 5: populate_gw_attrs = 1; return fixup_pvar(param); case 6: populate_carrier_attrs = 1; return fixup_pvar(param); } return -1; } static int fixup_next_gw( void** param, int param_no) { dr_partition_t * part; if( !use_partitions ) { /* partition not needed */ switch (param_no) { /* rule attrs pvar */ case 1: /* first param can be partition name */ populate_rule_attrs = 1; return fixup_pvar(param); /* gateway attrs pvar */ case 2: populate_gw_attrs = 1; return fixup_pvar(param); /* carrier attrs pvar */ case 3: populate_carrier_attrs = 1; return fixup_pvar(param); case 4: LM_ERR("Too many arguments for use_next_gw()\n"); return -1; } } else { /* parition is mandatory => the first param */ switch (param_no) { case 1: part = pkg_malloc(sizeof(dr_partition_t)); if(part == NULL) { LM_CRIT("No more pkg memory!\n"); return -1; } memset(part, 0, sizeof(dr_partition_t)); if(fxup_get_partition(param, &part) < 0) return -1; if(part->type == DR_NO_PART) { LM_ERR("Partition name is mandatory for use_next_gw.\n"); return -1; } *param = part; return 0; case 2: /* first param can be partition name */ populate_rule_attrs = 1; return fixup_pvar(param); /* gateway attrs pvar */ case 3: populate_gw_attrs = 1; return fixup_pvar(param); /* carrier attrs pvar */ case 4: populate_carrier_attrs = 1; return fixup_pvar(param); } } return -1; } static int fixup_from_gw( void** param, int param_no) { dr_partition_t * part; if(use_partitions == 0) { switch (param_no) { /* GW type*/ case 1: return fixup_sint(param); /* GW ops */ case 2: return fixup_spve(param); /* ATTRS pseudo-var */ case 3: return fixup_pvar(param); case 4: LM_ERR("Too many parameters. (if you don't use partitions)\n"); return -1; } } else { switch (param_no) { /* GW type*/ case 1: part = pkg_malloc(sizeof(dr_partition_t)); if(part == NULL) { LM_ERR("No more pkg memory.\n"); return -1; } memset(part, 0, sizeof(dr_partition_t)); if(fxup_get_partition(param, &part) < 0) return -1; *param = part; return 0; case 2: return fixup_sint(param); /* GW ops */ case 3: return fixup_spve(param); /* ATTRS pseudo-var */ case 4: return fixup_pvar(param); } } return -1; } static int fixup_is_gw( void** param, int param_no) { dr_partition_t * part; if(use_partitions == 0) { switch (param_no) { /* SIP URI pseudo-var */ case 1: return fixup_pvar(param); /* GW type*/ case 2: return fixup_sint(param); /* GW ops */ case 3: return fixup_spve(param); /* ATTRS pseudo-var */ case 4: return fixup_pvar(param); case 5: LM_ERR("Too many parameters. (if you don't use partitions)\n"); return -1; } } else { switch (param_no) { case 1: part = pkg_malloc(sizeof(dr_partition_t)); if(part == NULL) { LM_CRIT("No more pkg memory!"); return -1; } memset(part, 0, sizeof(dr_partition_t)); if(fxup_get_partition(param, &part) < 0) return -1; *param = part; return 0; /* SIP URI pseudo-var */ case 2: return fixup_pvar(param); /* GW type*/ case 3: return fixup_sint(param); /* GW ops */ case 4: return fixup_spve(param); /* ATTRS pseudo-var */ case 5: return fixup_pvar(param); } } return -1; } static void trim_char(char ** param) { char *trailing_sp; if(*param!=NULL) { while(**param==' ') (*param)++; trailing_sp = *param; while(*trailing_sp!=0) trailing_sp++; trailing_sp--; while(*trailing_sp==' ') *trailing_sp = 0, trailing_sp--; } } static int fixup_route2_carrier( void** param, int param_no) { dr_part_old_t *part_param; char * scnd_param; int rc; switch (param_no) { /* carrier name string - it has partition */ case 1: part_param = pkg_malloc(sizeof(dr_part_old_t)); if(part_param == NULL) { LM_ERR("No more pkg memory!"); return -1; } memset(part_param, 0, sizeof(dr_part_old_t)); if(use_partitions == 1) { if(fxup_split_param(param, (void**)&scnd_param) < 0) { return -1; } if(fxup_get_partition(param, &(part_param->dr_part)) < 0) { return -1; } if(part_param->dr_part->type == DR_NO_PART) { LM_ERR("Partition name is mandatory for route2_carrier\n"); return -1; } } else { scnd_param = *param; /* only carrier present */ } if(scnd_param == NULL) { LM_CRIT("carrier_id mandatory for function route_to_carrier.\n"); return -1; } trim_char(&scnd_param); if(*scnd_param == 0) { /* carrier_id was formed only from spaces */ LM_CRIT("carrier_id mandatory for function route_to_carrier.\n"); return -1; } rc = fixup_sgp((void**)&scnd_param); part_param->gw_or_cr = (gparam_p)scnd_param; *param = (void*)part_param; return rc; /* gateway attrs pvar */ case 2: populate_gw_attrs = 1; return fixup_pvar(param); /* carrier attrs pvar */ case 3: populate_carrier_attrs = 1; return fixup_pvar(param); } return -1; } static int fixup_route2_gw( void** param, int param_no) { int rc; char *gw = 0; dr_part_old_t * part_param; /* partition and gateway */ switch (param_no) { /* gateway / gateways (csv) */ case 1: part_param = pkg_malloc(sizeof(dr_part_old_t)); if(part_param == NULL) { LM_ERR("No more pkg memory!"); return -1; } memset(part_param, 0, sizeof(dr_part_old_t)); if(use_partitions == 1) { if(fxup_split_param(param, (void**)&gw) < 0) { return -1; } if(fxup_get_partition(param, &(part_param->dr_part))<0) { return -1; } if(part_param->dr_part->type == DR_NO_PART) { LM_ERR("Partition name is mandatory for route2_gw\n"); } } else { gw = *param; } if(gw == NULL) { LM_CRIT("gateway mandatory for function route_to_gw.\n"); return -1; } trim_char((char**)&gw); if(*gw == 0) { LM_CRIT("gateway mandatory for function route_to_gw.\n"); return -1; } rc = fixup_sgp((void**)&gw); part_param->gw_or_cr = (gparam_p)gw; *param = (void*)part_param; return rc; /* gateway attrs pvar */ case 2: populate_gw_attrs = 1; return fixup_pvar(param); } return -1; } static int strip_username(struct sip_msg* msg, int strip) { struct action act; act.type = STRIP_T; act.elem[0].type = NUMBER_ST; act.elem[0].u.number = strip; act.next = 0; if (do_action(&act, msg) < 0) { LM_ERR( "Error in do_action\n"); return -1; } return 0; } static int prefix_username(struct sip_msg* msg, str *pri) { struct action act; act.type = PREFIX_T; act.elem[0].type = STR_ST; act.elem[0].u.s = *pri; act.next = 0; if (do_action(&act, msg) < 0) { LM_ERR( "Error in do_action\n"); return -1; } return 0; } static int gw_matches_ip(pgw_t *pgwa, struct ip_addr *ip, unsigned short port) { unsigned short j; for ( j=0 ; jips_no ; j++) if ( (pgwa->ports[j]==0 || port==0 || pgwa->ports[j]==port) && ip_addr_cmp( &pgwa->ips[j], ip) ) return 1; return 0; } #define DR_IFG_STRIP_FLAG (1<<0) #define DR_IFG_PREFIX_FLAG (1<<1) #define DR_IFG_IDS_FLAG (1<<3) #define DR_IFG_IGNOREPORT_FLAG (1<<4) #define DR_IFG_CARRIERID_FLAG (1<<5) static int _is_dr_gw(struct sip_msg* msg, char * part, char * flags_pv, int type, struct ip_addr *ip, unsigned int port) { int ret=-1; pv_value_t pv_val; struct head_db * it; if(use_partitions) { if(part == NULL || ((dr_partition_t*)part)->type == DR_NO_PART) { LM_ERR("Partition is mandatory!\n"); return -1; } if(((dr_partition_t*)part)->type == DR_PTR_PART) { return _is_dr_gw_w_part(msg, (char*)((dr_partition_t*)part)->v.part, flags_pv, type, ip, port); } else if(((dr_partition_t*)part)->type == DR_GPARAM_PART) { if((ret=to_partition(msg, (dr_partition_t*)part, &it) < 0)) { return -1; } else if (ret == 0) { return _is_dr_gw_w_part(msg, (char*)it,flags_pv, type, ip, port); } } /* if we got here we have the wildcard operator */ for (it = head_db_start; it; it = it->next) { ret = _is_dr_gw_w_part(msg, (char *)it, flags_pv, type, ip, port); if (ret > 0) { if (partition_pvar.s) { pv_val.rs = it->partition; pv_val.flags = PV_VAL_STR; if (pv_set_value(msg, &partition_spec, 0, &pv_val) != 0) { LM_ERR("cannot print the PV-formatted" " partition string\n"); return -1; } } return ret; } } return ret; } else { if( head_db_start == NULL ) { LM_ERR("Error loading config."); return -1; } return _is_dr_gw_w_part(msg, (char*)head_db_start, flags_pv, (int)type, (struct ip_addr *)ip, (unsigned int)port); } return -1; } /* * Checks if a given IP + PORT is a GW; tests the TYPE too * INTERNAL FUNCTION */ static int _is_dr_gw_w_part(struct sip_msg* msg, char * part, char* flags_pv, int type, struct ip_addr *ip, unsigned int port) { pgw_t *pgwa = NULL; pcr_t *pcr = NULL; pv_value_t pv_val; int flags = 0; str flags_s; int_str val; int i; struct head_db *current_partition = (struct head_db *)part; void** dest; map_iterator_t gw_it, cr_it; if(current_partition == NULL || current_partition->rdata==NULL || *current_partition->rdata==NULL || msg==NULL) return -1; if (flags_pv && flags_pv[0]) { if (fixup_get_svalue( msg, (gparam_p)flags_pv, &flags_s)!=0) { LM_ERR("invalid flags parameter"); return -1; } for( i=0 ; i < flags_s.len ; i++ ) { switch (flags_s.s[i]) { case 's': flags |= DR_IFG_STRIP_FLAG; break; case 'p': flags |= DR_IFG_PREFIX_FLAG; break; case 'i': flags |= DR_IFG_IDS_FLAG; break; case 'n': flags |= DR_IFG_IGNOREPORT_FLAG; break; case 'c': flags |= DR_IFG_CARRIERID_FLAG; break; default: LM_WARN("unsupported flag %c \n",flags_s.s[i]); } } } if(current_partition->rdata!=NULL && *current_partition->rdata!=NULL) { for (map_first((*current_partition->rdata)->pgw_tree, &gw_it); iterator_is_valid(&gw_it); iterator_next(&gw_it)) { dest = iterator_val(&gw_it); if (dest==NULL) break; pgwa = (pgw_t*)*dest; if( (type<0 || type==pgwa->type) && gw_matches_ip( pgwa, ip, (flags&DR_IFG_IGNOREPORT_FLAG)?0:port )) { /* strip ? */ if ( (flags&DR_IFG_STRIP_FLAG) && pgwa->strip>0) strip_username(msg, pgwa->strip); /* prefix ? */ if ( (flags&DR_IFG_PREFIX_FLAG) && pgwa->pri.len>0) { /* pri prefix ? */ if (current_partition->gw_priprefix_avp!=-1) { val.s = pgwa->pri.s ? pgwa->pri : attrs_empty ; if (add_avp(AVP_VAL_STR, current_partition->gw_priprefix_avp, val)!=0) LM_ERR("failed to insert GW pri prefix avp\n"); } prefix_username(msg, &pgwa->pri); } /* attrs ? */ if (gw_attrs_spec) { pv_val.flags = PV_VAL_STR; pv_val.rs = pgwa->attrs.s ? pgwa->attrs : attrs_empty; if (pv_set_value(msg, gw_attrs_spec, 0, &pv_val) != 0) LM_ERR("failed to set value for GW attrs pvar\n"); } if ( flags & DR_IFG_IDS_FLAG ) { val.s = pgwa->id; if (add_avp(AVP_VAL_STR, current_partition->gw_id_avp, val)!=0) LM_ERR("failed to insert GW attrs avp\n"); } if ( flags & DR_IFG_CARRIERID_FLAG ) { /* lookup first carrier that contains this gw */ for (map_first((*current_partition->rdata)->carriers_tree, &cr_it); iterator_is_valid(&cr_it); iterator_next(&cr_it)) { dest = iterator_val(&cr_it); if (dest==NULL) break; pcr = (pcr_t*)*dest; for (i=0;ipgwa_len;i++) { if (pcr->pgwl[i].is_carrier == 0 && pcr->pgwl[i].dst.gw == pgwa ) { /* found our carrier */ if (current_partition->carrier_id_avp!=-1) { val.s = pcr->id; if (add_avp_last(AVP_VAL_STR, current_partition->carrier_id_avp,val)!=0){ LM_ERR("failed to add carrier id " "AVP\n"); } } goto end; } } } } end: return 1; } pgwa = pgwa->next; } } return -1; } static int is_from_gw_0(struct sip_msg* msg) { return _is_dr_gw(msg, NULL, NULL, -1, &msg->rcv.src_ip, msg->rcv.src_port); } /* * Checks if a given src IP and PORT is a GW; no TYPE, no FLAGS */ static int is_from_gw_1(struct sip_msg* msg, char * part) { if(use_partitions) { return _is_dr_gw( msg, part, NULL, -1, &msg->rcv.src_ip , msg->rcv.src_port); } else { return _is_dr_gw(msg, NULL, NULL, (!part? -1:(int)(long)part), &msg->rcv.src_ip, msg->rcv.src_port); } } /* * Checks if a given src IP and PORT is a GW; tests the TYPE too, no FLAGS */ static int is_from_gw_2(struct sip_msg* msg, char * part, char* type_s) { if(use_partitions) { return _is_dr_gw(msg, part, NULL, (!type_s ? -1 : (int)(long)type_s), &msg->rcv.src_ip , msg->rcv.src_port); } else { return _is_dr_gw(msg, NULL, type_s, (!part ? -1: (int)(long)part), &msg->rcv.src_ip, msg->rcv.src_port); } } static int is_from_gw_3(struct sip_msg* msg, char * part,char* type_s, char* flags_pv) { if(use_partitions) { return _is_dr_gw(msg, part, flags_pv, (!type_s ? -1:(int)(long)type_s), &msg->rcv.src_ip, msg->rcv.src_port); } else { gw_attrs_spec = (pv_spec_p)flags_pv; return _is_dr_gw(msg, NULL, type_s, (!part ? -1:(int)(long)part), &msg->rcv.src_ip, msg->rcv.src_port); } } /* * Checks if a given src IP and PORT is a GW; tests the TYPE too */ static int is_from_gw_4(struct sip_msg* msg, char * part,char* type_s, char* flags_pv, char* gw_att) { gw_attrs_spec = (pv_spec_p)gw_att; if(use_partitions) { return _is_dr_gw( msg, part, flags_pv, (!type_s ? -1 : (int)(long)type_s), &msg->rcv.src_ip , msg->rcv.src_port); } else { LM_ERR("Too many parameters\n"); return -1; } } /* * Checks if a given SIP URI is a GW; tests the TYPE too * INTERNAL FUNCTION */ static int _is_dr_uri_gw(struct sip_msg* msg, char *part, char* flags_pv, int type, str *uri) { struct sip_uri puri; struct hostent* he; struct ip_addr ip; memset( &puri, 0, sizeof(struct sip_uri)); if (parse_uri(uri->s, uri->len, &puri)!=0) { LM_ERR("invalid sip uri <%.*s>\n", uri->len, uri->s); return -1; } he = sip_resolvehost(&puri.host, &puri.port_no, &puri.proto, (puri.type==SIPS_URI_T), 0); if (he==0) { LM_DBG("resolve_host(%.*s) failure\n", puri.host.len, puri.host.s); return -1; } /* extract the first ip */ memset(&ip,0,sizeof(struct ip_addr)); hostent2ip_addr( &ip, he, 0); return _is_dr_gw( msg, part, flags_pv, type, &ip , puri.port_no); } /* * Checks if RURI is a GW ; tests the TYPE too */ static int goes_to_gw_1(struct sip_msg* msg, char * part, char* _type, char* flags_pv, char* gw_att) { if(use_partitions) { gw_attrs_spec = (pv_spec_p)gw_att; return _is_dr_uri_gw(msg, part, flags_pv, (!_type ? -1 : (int)(long)_type), GET_NEXT_HOP(msg)); } else { gw_attrs_spec = (pv_spec_p)flags_pv; return _is_dr_uri_gw(msg, NULL, _type, (!part ? -1 : (int)(long)part), GET_NEXT_HOP(msg)); } } /* * Checks if RURI is a GW; not TYPE check */ static int goes_to_gw_0(struct sip_msg* msg) { return goes_to_gw_1(msg, NULL, (char *)-1, NULL, NULL); } /* * Checks if a variable (containing a SIP URI) is a GW; tests the TYPE too */ static int dr_is_gw(struct sip_msg* msg, char * part, char* src_pv, char* type_s, char* flags_pv, char* gw_att) { pv_value_t src; if(use_partitions) { gw_attrs_spec = (pv_spec_p)gw_att; if ( pv_get_spec_value(msg, (pv_spec_p)src_pv, &src)!=0 || (src.flags&PV_VAL_STR)==0 || src.rs.len<=0) { LM_ERR("failed to get string value for src\n"); return -1; } return _is_dr_uri_gw(msg, part, flags_pv, !type_s ? -1:(int)(long)type_s, &src.rs); } else { if ( pv_get_spec_value(msg, (pv_spec_p)part, &src)!=0 || (src.flags&PV_VAL_STR)==0 || src.rs.len<=0) { LM_ERR("failed to get string value for src\n"); return -1; } gw_attrs_spec = (pv_spec_p)flags_pv; return _is_dr_uri_gw(msg, NULL, type_s ,!src_pv ? -1:(int)(long)src_pv ,&src.rs); } } static struct mi_root* mi_w_partition(struct mi_node **node, struct head_db ** current_partition) { struct mi_root *rpl_tree; if( use_partitions ) { if( node!=NULL && (*node)!=NULL ) { if( (*current_partition = get_partition(&((*node)->value))) == NULL) { LM_ERR("Partition not found\n"); rpl_tree = init_mi_tree( 404, MI_SSTR("Partition not found\n")); return rpl_tree; } *node = (*node)->next; /* advance to next param */ return NULL; /* everything is ok */ } else { LM_ERR("Partition name mandatory\n"); rpl_tree = init_mi_tree(400, MI_SSTR("Partition mandatory\n")); return rpl_tree; } } else { *current_partition = head_db_start; return NULL; /* everything is ok */ } rpl_tree = init_mi_tree( 400, MI_SSTR("Unexpected outcome while parsing param for opensisctl\n")); return rpl_tree; } static struct mi_root* mi_dr_gw_status(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct mi_node *node; struct mi_attr *attr; unsigned int stat; struct head_db * current_partition=0; pgw_t *gw; str *id; int old_flags; void** dest; map_iterator_t it; node = cmd->node.kids; if( (rpl_tree = mi_w_partition(&node, ¤t_partition))!=NULL ) return rpl_tree; /* something went wrong: bad command format */ lock_start_read( current_partition->ref_lock ); if (current_partition->rdata==NULL || *current_partition->rdata==NULL) { rpl_tree = init_mi_tree( 404, MI_SSTR("No Data available yet")); goto done; } if (node==NULL) { /* no GW specified, list all of them */ rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) goto error; rpl_tree->node.flags |= MI_IS_ARRAY; for (map_first((*current_partition->rdata)->pgw_tree, &it); iterator_is_valid(&it); iterator_next(&it)) { dest = iterator_val(&it); if (dest==NULL) return NULL; gw = (pgw_t*)*dest; node = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, "ID", 2, gw->id.s, gw->id.len); if (node==NULL) goto error; attr = add_mi_attr( node, MI_DUP_VALUE, "IP" , 2, gw->ip_str.s, gw->ip_str.len); if (attr==NULL) goto error; if (gw->flags&DR_DST_STAT_DSBL_FLAG) { if (gw->flags&DR_DST_STAT_NOEN_FLAG) attr = add_mi_attr( node, 0, "State", 5, "Disabled MI", 11); else if (gw->flags&DR_DST_PING_DSBL_FLAG) attr = add_mi_attr( node, 0, "State", 5, "Probing", 7); else attr = add_mi_attr( node, 0, "State", 5, "Inactive", 8); } else { attr = add_mi_attr( node, 0, "State", 5, "Active", 6); } if (attr==NULL) goto error; } goto done; } /* GW ID (param 1) */ id = &node->value; /* search for the Gw */ gw = get_gw_by_id( (*current_partition->rdata)->pgw_tree, id); if (gw==NULL) { rpl_tree = init_mi_tree( 404, MI_SSTR("GW ID not found")); goto done; } /* status (param 2) */ node = node->next; if (node == NULL) { /* no status provided -> return the internal one */ rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) goto error; if (gw->flags&DR_DST_STAT_DSBL_FLAG) { if (gw->flags&DR_DST_STAT_NOEN_FLAG) node = add_mi_node_child( &rpl_tree->node, 0, "State", 5, "Disabled MI", 11); else if (gw->flags&DR_DST_PING_DSBL_FLAG) node = add_mi_node_child( &rpl_tree->node, 0, "State", 5, "Probing", 7); else node = add_mi_node_child( &rpl_tree->node, 0, "State", 5, "Inactive", 8); } else { node = add_mi_node_child( &rpl_tree->node, 0, "State", 5, "Active", 6); } if (node==NULL) goto error; goto done; } /* set the status */ if (node->next) { rpl_tree = init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); goto done; } if (str2int( &node->value, &stat) < 0) { rpl_tree = init_mi_tree( 400, MI_SSTR(MI_BAD_PARM_S)); goto done; } /* set the disable/enable */ old_flags = gw->flags; if (stat) { gw->flags &= ~ (DR_DST_STAT_DSBL_FLAG|DR_DST_STAT_NOEN_FLAG); } else { gw->flags |= DR_DST_STAT_DSBL_FLAG|DR_DST_STAT_NOEN_FLAG; } if (old_flags!=gw->flags) { gw->flags |= DR_DST_STAT_DIRT_FLAG; dr_raise_event(gw); } done: lock_stop_read( current_partition->ref_lock ); return rpl_tree; error: lock_stop_read( current_partition->ref_lock ); if(rpl_tree) free_mi_tree(rpl_tree); return NULL; } static struct mi_root* mi_dr_cr_status(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct mi_node *node; struct mi_attr *attr; unsigned int stat; struct head_db * current_partition = 0; pcr_t *cr; str *id; int old_flags; void** dest; map_iterator_t it; node = cmd->node.kids; if( (rpl_tree = mi_w_partition(&node, ¤t_partition)) !=NULL ) { return rpl_tree; } lock_start_read( current_partition->ref_lock ); if (current_partition->rdata==NULL || *current_partition->rdata==NULL) { rpl_tree = init_mi_tree( 404, MI_SSTR("No Data available yet")); goto done; } if (node==NULL) { /* no carrier specified, list all of them */ rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) goto error; rpl_tree->node.flags |= MI_IS_ARRAY; for (map_first((*current_partition->rdata)->carriers_tree, &it); iterator_is_valid(&it); iterator_next(&it)) { dest = iterator_val(&it); if (dest==NULL) return NULL; cr = (pcr_t*)*dest; node = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, "ID", 2, cr->id.s, cr->id.len); if (node==NULL) goto error; attr = add_mi_attr( node, 0, "Enabled", 7, (cr->flags&DR_CR_FLAG_IS_OFF)?"no ":"yes", 3); if (attr==NULL) goto error; } goto done; } /* GW ID (param 1) */ id = &node->value; /* search for the Carrier */ cr = get_carrier_by_id( (*current_partition->rdata)->carriers_tree, id); if (cr==NULL) { rpl_tree = init_mi_tree( 404, MI_SSTR("Carrier ID not found")); goto done; } /* status (param 2) */ node = node->next; if (node == NULL) { /* no status provided -> return the internal one */ rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) goto error; node = add_mi_node_child( &rpl_tree->node, 0, "Enabled", 7, (cr->flags&DR_CR_FLAG_IS_OFF)?"no ":"yes", 3); if (node==NULL) goto error; goto done; } /* set the status */ if (node->next) { rpl_tree = init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); goto done; } if (str2int( &node->value, &stat) < 0) { rpl_tree = init_mi_tree( 400, MI_SSTR(MI_BAD_PARM_S)); goto done; } /* set the disable/enable */ old_flags = cr->flags; if (stat) { cr->flags &= ~ (DR_CR_FLAG_IS_OFF); } else { cr->flags |= DR_CR_FLAG_IS_OFF; } if (old_flags!=cr->flags) cr->flags |= DR_CR_FLAG_DIRTY; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); done: lock_stop_read( current_partition->ref_lock ); return rpl_tree; error: lock_stop_read( current_partition->ref_lock ); if(rpl_tree) free_mi_tree(rpl_tree); return NULL; } int add_head_db(void) { struct head_db *new; new = ( struct head_db* )shm_malloc(sizeof( struct head_db ) ); if( new == NULL ) { LM_ERR(" no more shm memory(add_head_db)\n"); return -1; } memset( new, 0, sizeof( struct head_db ) ); new->avpID_store_ruri = -1; new->avpID_store_prefix = -1; new->avpID_store_index = -1; new->avpID_store_whitelist = -1; new->avpID_store_group = -1; new->avpID_store_flags = -1; new->gw_priprefix_avp = -1; new->rule_id_avp = -1; new->rule_prefix_avp = -1; new->carrier_id_avp = -1; new->ruri_avp = -1; new->gw_id_avp = -1; new->gw_sock_avp = -1; new->gw_attrs_avp = -1; new->rule_attrs_avp = -1; new->carrier_attrs_avp = -1; if( head_db_start == NULL) { head_db_start = new; head_db_end = new; } else { head_db_end->next = new; head_db_end = new; } return 0; } /* use_partitions: use configurations from database */ int add_head_config(void) { /* expand linked list */ struct head_config *new; new = ( struct head_config* )shm_malloc( sizeof( struct head_config ) ); if( new == NULL ) { LM_ERR("no more shm memory\n"); return -1; } memset(new, 0, sizeof( struct head_config ) ); /* ->next will be null too */ if( head_start == NULL) { head_start = new; head_end = new; } else { head_end->next = new; head_end = new; } return 0; } #define init_head_config_value( from_head, external, default_val)\ if( external.len!=0 ) {\ shm_str_dup( &(from_head), &(external));\ } else {\ from_head = default_val;\ }\ #define set_head_config_value(head_param, db_param)\ if(db_param.len > 0) {\ shm_str_dup(&(head_param), &(db_param));\ }\ static int populate_head_config(struct head_config *current, str attr, int index) { switch(index) { case 0: if(shm_str_dup( &(current->partition), &attr) < 0) { LM_ERR("no more shm memory for partition_name in head_config\n"); } break; case 1: if( shm_str_dup(&(current->db_url), &attr) < 0) { LM_ERR("no more shm memory for db_url in head_config\n"); } break; case 2: init_head_config_value( current->drd_table, attr, drd_table); break; case 3: init_head_config_value( current->drr_table, attr, drr_table); break; case 4: init_head_config_value( current->drg_table, attr, drg_table); break; case 5: init_head_config_value( current->drc_table, attr, drc_table); break; case 6: set_head_config_value( current->ruri_avp_spec, attr); break; case 7: set_head_config_value( current->gw_id_avp_spec, attr); break; case 8: set_head_config_value( current->gw_priprefix_avp_spec, attr); break; case 9: set_head_config_value( current->gw_sock_avp_spec, attr); break; case 10: set_head_config_value( current->rule_id_avp_spec, attr); break; case 11: set_head_config_value( current->rule_prefix_avp_spec, attr); break; case 12: set_head_config_value( current->carrier_id_avp_spec, attr); break; default: LM_DBG("Column from db_config not_known\n"); return -1; } return 0; } static int get_config_from_db(void) { db_func_t db_funcs; db_res_t * query_res; db_con_t * db_con = 0; /* columns needed from db_confgir_url for query */ str partition_col = str_init("partition_name"); str db_url_col = str_init("db_url"); str drd_col = str_init("drd_table"); str drr_col = str_init("drr_table"); str drg_col = str_init("drg_table"); str drc_col = str_init("drc_table"); str ruri_avp_col = str_init("ruri_avp"); str gw_id_avp_col = str_init("gw_id_avp"); str gw_priprefix_avp_col = str_init("gw_priprefix_avp"); str gw_sock_avp_col = str_init("gw_sock_avp"); str rule_id_avp_col = str_init("rule_id_avp"); str rule_prefix_avp_col = str_init("rule_prefix_avp"); str carrier_id_avp_col = str_init("carrier_id_avp"); int n_query_col = 13; db_key_t query_cols[] = {&partition_col, &db_url_col, &drd_col, &drr_col, &drg_col, &drc_col, &ruri_avp_col, &gw_id_avp_col, &gw_priprefix_avp_col, &gw_sock_avp_col, &rule_id_avp_col, &rule_prefix_avp_col, &carrier_id_avp_col}; /* query result processing stuff */ int nr_rows_db_config = 0 ; int nr_cols_db_config = 0 ; db_val_t * value; db_row_t *rows_db_config = NULL; int j; int i; str ans_col; init_db_url(db_partitions_url, 0); db_partitions_url.len = strlen(db_partitions_url.s); db_partitions_table.len = strlen(db_partitions_table.s); if(db_bind_mod( &db_partitions_url, &db_funcs) < 0) { LM_ERR("Unable to bind to database driver (partition definitions) " "\n", db_partitions_url.len, db_partitions_url.s); goto error; } if( (db_con = db_funcs.init(&db_partitions_url)) == 0 ) { LM_ERR("Cannot init connection to partitions table " "\n", db_partitions_url.len, db_partitions_url.s); goto error; } if(db_check_table_version(&db_funcs, db_con, &db_partitions_table, PART_TABLE_VER) < 0) { LM_ERR("error during table version check .\n", db_partitions_table.len, db_partitions_table.s); return -1; } if( db_funcs.use_table( db_con, &db_partitions_table) < 0) { LM_ERR("Cannot use the partitions table " "\n", db_partitions_table.len, db_partitions_table.s, db_partitions_url.len, db_partitions_url.s); goto error; } /* query for populating head_config structure */ if( db_funcs.query( db_con, NULL, NULL, NULL, query_cols, 0, n_query_col, NULL, &query_res) < 0 ) { LM_ERR("Failed to query the table containing the partition definitions " "\n", db_partitions_url.len, db_partitions_url.s, db_partitions_table.len, db_partitions_table.s); goto error; } nr_rows_db_config = RES_ROW_N(query_res); nr_cols_db_config = RES_COL_N(query_res); rows_db_config = RES_ROWS(query_res); for( i=0; inode.kids; struct head_db *partition; str s; int grp_id; unsigned int matched_len; struct mi_node *prefix_node; rt_info_t *route; if (node == NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if (use_partitions) { s = node->value; if((partition = get_partition(&s)) == NULL) { LM_WARN("Partition <%.*s> was not found.\n", s.len, s.s); return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } node = node->next; } else partition = head_db_start; if (node == NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if (node->next == NULL) { grp_id = -1; } else { unsigned int ugrp_id; if (str2int(&node->value, &ugrp_id) != 0) return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); grp_id = ugrp_id; node = node->next; } lock_start_read( partition->ref_lock ); route = find_rule_by_prefix_unsafe((*(partition->rdata))->pt, &(*(partition->rdata))->noprefix, node->value, grp_id, &matched_len); if (route == NULL){ lock_stop_read( partition->ref_lock ); return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } struct mi_root* rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (rpl_tree == NULL){ lock_stop_read( partition->ref_lock ); return 0; } unsigned int i; static const str gw_str = str_init("GATEWAY"); static const str carrier_str = str_init("CARRIER"); static const str matched_str = str_init("Matched Prefix"); str chosen_desc; str chosen_id; if ((prefix_node = add_mi_node_child(&rpl_tree->node, 0, matched_str.s, matched_str.len, node->value.s, matched_len)) == NULL) { LM_ERR("failed to add node\n"); lock_stop_read( partition->ref_lock ); free_mi_tree(rpl_tree); return 0; } prefix_node->flags |= MI_IS_ARRAY; for (i = 0; i < route->pgwa_len; ++i){ if (route->pgwl[i].is_carrier) { chosen_desc = carrier_str; chosen_id = route->pgwl[i].dst.carrier->id; } else { chosen_desc = gw_str; chosen_id = route->pgwl[i].dst.gw->id; } if (add_mi_node_child(prefix_node, 0, chosen_desc.s, chosen_desc.len, chosen_id.s, chosen_id.len) == NULL) { LM_ERR("failed to add node\n"); lock_stop_read( partition->ref_lock ); free_mi_tree(rpl_tree); return 0; } } lock_stop_read( partition->ref_lock ); return rpl_tree; } static struct mi_root* mi_dr_reload_status(struct mi_root *cmd_tree, void *param) { struct mi_node *node = cmd_tree->node.kids; struct mi_root *rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); struct mi_node *ans; struct head_db * partition; str part_name; char * ch_time; if(node != NULL) { if (use_partitions) { part_name = node->value; if((partition = get_partition(&part_name)) == NULL) { LM_WARN("Partition <%.*s> was not found.\n", part_name.len, part_name.s); return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } /* display just for given partition */ lock_start_read(partition->ref_lock); ch_time = ctime(&partition->time_last_update); if((ans = add_mi_node_child(&rpl_tree->node, MI_DUP_VALUE, MI_PART_NAME_S, MI_PART_NAME_LEN, partition->partition.s, partition->partition.len)) == NULL) { LM_ERR("failed to add mi_node\n"); goto error; } if(add_mi_attr(ans, 0, MI_LAST_UPDATE_S, MI_LAST_UPDATE_LEN, ch_time, strlen(ch_time)) == NULL) { LM_ERR("failed to add mi_attr\n"); goto error; } lock_stop_read(partition->ref_lock); } else { return init_mi_tree(400, MI_NO_PART_S, MI_NO_PART_LEN); } } else if(use_partitions){ rpl_tree->node.flags |= MI_IS_ARRAY; /* display for all partitions */ for(partition = head_db_start; partition; partition = partition->next) { lock_start_read(partition->ref_lock); ch_time = ctime(&partition->time_last_update); LM_DBG("partition %.*s was last updated:%s\n", partition->partition.len, partition->partition.s, ch_time); if((ans = add_mi_node_child(&rpl_tree->node, 0, MI_PART_NAME_S, MI_PART_NAME_LEN, partition->partition.s, partition->partition.len)) == NULL) { LM_ERR("failed to add mi_node\n"); goto error; } if(add_mi_attr(ans, 0, MI_LAST_UPDATE_S, MI_LAST_UPDATE_LEN, ch_time, strlen(ch_time)) == NULL) { LM_ERR("failed to add attr to mi_node\n"); goto error; } lock_stop_read(partition->ref_lock); } } else { /* just one partition */ partition = head_db_start; lock_start_read(partition->ref_lock); ch_time = ctime(&partition->time_last_update); if((ans = add_mi_node_child(&rpl_tree->node, 0, MI_LAST_UPDATE_S, MI_LAST_UPDATE_LEN, ch_time, strlen(ch_time))) == NULL) { LM_ERR("failed to add mi_node\n"); goto error; } lock_stop_read(partition->ref_lock); } return rpl_tree; error: lock_stop_read(partition->ref_lock); free_mi_tree(rpl_tree); return 0; } opensips-2.2.2/modules/drouting/parse.h000066400000000000000000000024331300170765700201530ustar00rootroot00000000000000/* * Copyright (C) 2005-2008 Voice Sistem SRL * * This file is part of Open SIP Server. * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2005-07-27 first version (bogdan) */ #ifndef dr_parse_h_ #define dr_parse_h_ #define SEP '|' #define SEP1 ',' #define CARRIER_MARKER '#' #define IS_SPACE(s)\ ((s)==' ' || (s)=='\t' || (s)=='\r' || (s)=='\n') #define EAT_SPACE(s)\ while((s) && IS_SPACE(*(s))) (s)++ #endif opensips-2.2.2/modules/drouting/prefix_tree.c000066400000000000000000000152701300170765700213530ustar00rootroot00000000000000/* * Copyright (C) 2005-2008 Voice Sistem SRL * * This file is part of Open SIP Server. * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2005-02-20 first version (cristian) * 2005-02-27 ported to 0.9.0 (bogdan) */ #include #include #include "../../str.h" #include "../../mem/shm_mem.h" #include "../../time_rec.h" #include "prefix_tree.h" #include "routing.h" extern int inode; extern int unode; static inline int check_time( tmrec_t *time_rec ) { ac_tm_t att; /* shortcut: if there is no dstart, timerec is valid */ if (time_rec->dtstart==0) return 1; memset( &att, 0, sizeof(att)); /* set current time */ if ( ac_tm_set_time( &att, time(0) ) ) return 0; /* does the recv_time match the specified interval? */ if (check_tmrec( time_rec, &att, 0)!=0) return 0; return 1; } static inline rt_info_t* internal_check_rt( ptree_node_t *ptn, unsigned int rgid, unsigned int *rgidx ) { int i,j; int rg_pos=0; rg_entry_t* rg=NULL; rt_info_wrp_t* rtlw=NULL; if((NULL==ptn) || (NULL==ptn->rg)) goto err_exit; rg_pos = ptn->rg_pos; rg=ptn->rg; for(i=0;(i= *rgidx) { if(rtlw->rtl->time_rec == NULL || check_time(rtlw->rtl->time_rec)) goto ok_exit; } rtlw=rtlw->next; } } err_exit: return NULL; ok_exit: /* if rules are still in this node, point to the next index */ *rgidx = (rtlw->next) ? j : 0 ; return rtlw?rtlw->rtl:0; } rt_info_t* check_rt( ptree_node_t *ptn, unsigned int rgid ) { unsigned int rgidx = 0; return internal_check_rt( ptn, rgid, &rgidx); } rt_info_t* get_prefix( ptree_t *ptree, str* prefix, unsigned int rgid, unsigned int *matched_len, unsigned int *rgidx ) { rt_info_t *rt = NULL; char *tmp=NULL; char local=0; int idx=0; if(NULL == ptree) goto err_exit; if(NULL == prefix) goto err_exit; tmp = prefix->s; /* go the tree down to the last digit in the * prefix string or down to a leaf */ while(tmp< (prefix->s+prefix->len)) { if(NULL == tmp) goto err_exit; local=*tmp; if( !IS_DECIMAL_DIGIT(local) ) { /* unknown character in the prefix string */ goto err_exit; } if( tmp == (prefix->s+prefix->len-1) ) { /* last digit in the prefix string */ break; } idx = local -'0'; if( NULL == ptree->ptnode[idx].next) { /* this is a leaf */ break; } ptree = ptree->ptnode[idx].next; tmp++; } /* go in the tree up to the root trying to match the * prefix */ while(ptree !=NULL ) { if(NULL == tmp) goto err_exit; /* is it a real node or an intermediate one */ idx = *tmp-'0'; if(NULL != ptree->ptnode[idx].rg) { /* real node; check the constraints on the routing info*/ if( NULL != (rt = internal_check_rt( &(ptree->ptnode[idx]), rgid, rgidx))) break; } tmp--; ptree = ptree->bp; } if (matched_len) *matched_len = tmp + 1 - prefix->s ; return rt; err_exit: return NULL; } pgw_t* get_gw_by_internal_id( map_t gw_tree, unsigned int id ) { pgw_t* gw; void** dest; map_iterator_t it; for (map_first(gw_tree, &it); iterator_is_valid(&it); iterator_next(&it)) { dest = iterator_val(&it); if (dest==NULL) return NULL; gw = (pgw_t*)*dest; if ( id == gw->_id) return gw; } return NULL; } pgw_t* get_gw_by_id( map_t pgw_tree, str *id ) { pgw_t** ret; return (ret=(pgw_t**)map_find(pgw_tree, *id))?*ret:NULL; } pcr_t* get_carrier_by_id( map_t carriers_tree, str *id ) { pcr_t** ret; return (ret=(pcr_t**)map_find(carriers_tree, *id))?*ret:NULL; } int add_prefix( ptree_t *ptree, str* prefix, rt_info_t *r, unsigned int rg ) { char* tmp=NULL; int res = 0; if(NULL==ptree) { LM_ERR("ptree is null\n"); goto err_exit; } tmp = prefix->s; while(tmp < (prefix->s+prefix->len)) { if(NULL == tmp) { LM_ERR("prefix became null\n"); goto err_exit; } if( !IS_DECIMAL_DIGIT(*tmp) ) { /* unknown character in the prefix string */ LM_ERR("is not decimal digit\n"); goto err_exit; } if( tmp == (prefix->s+prefix->len-1) ) { /* last digit in the prefix string */ LM_DBG("adding info %p, %d at: " "%p (%d)\n", r, rg, &(ptree->ptnode[*tmp-'0']), *tmp-'0'); res = add_rt_info(&(ptree->ptnode[*tmp-'0']), r,rg); if(res < 0 ) { LM_ERR("adding rt info doesn't work\n"); goto err_exit; } unode++; res = 1; goto ok_exit; } /* process the current digit in the prefix */ if(NULL == ptree->ptnode[*tmp - '0'].next) { /* allocate new node */ INIT_PTREE_NODE(ptree, ptree->ptnode[*tmp - '0'].next); inode+=10; #if 0 printf("new tree node: %p (bp: %p)\n", ptree->ptnode[*tmp - '0'].next, ptree->ptnode[*tmp - '0'].next->bp ); #endif } ptree = ptree->ptnode[*tmp-'0'].next; tmp++; } ok_exit: return 0; err_exit: return -1; } int del_tree( ptree_t* t ) { int i,j; if(NULL == t) goto exit; /* delete all the children */ for(i=0; i< PTREE_CHILDREN; i++) { /* shm_free the rg array of rt_info */ if(NULL!=t->ptnode[i].rg) { for(j=0;jptnode[i].rg_pos;j++) { /* if non intermediate delete the routing info */ if(t->ptnode[i].rg[j].rtlw !=NULL) del_rt_list(t->ptnode[i].rg[j].rtlw); } shm_free(t->ptnode[i].rg); } /* if non leaf delete all the children */ if(t->ptnode[i].next != NULL) del_tree(t->ptnode[i].next); } shm_free(t); exit: return 0; } void del_rt_list( rt_info_wrp_t *rwl ) { rt_info_wrp_t* t=rwl; while(rwl!=NULL) { t=rwl; rwl=rwl->next; if ( (--t->rtl->ref_cnt)==0) free_rt_info(t->rtl); shm_free(t); } } void free_rt_info( rt_info_t *rl ) { if(NULL == rl) return; if(NULL!=rl->pgwl) shm_free(rl->pgwl); if(NULL!=rl->time_rec) tmrec_free(rl->time_rec); shm_free(rl); return; } opensips-2.2.2/modules/drouting/prefix_tree.h000066400000000000000000000111151300170765700213520ustar00rootroot00000000000000/* * Copyright (C) 2005-2008 Voice Sistem SRL * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2005-02-20 first version (cristian) * 2005-02-27 ported to 0.9.0 (bogdan) */ #ifndef prefix_tree_h #define prefix_tree_h #include "../../str.h" #include "../../ip_addr.h" #include "../../time_rec.h" #include "../../map.h" #define PTREE_CHILDREN 10 #define IS_DECIMAL_DIGIT(d) \ (((d)>='0') && ((d)<= '9')) extern int tree_size; #define INIT_PTREE_NODE(p, n) \ do {\ (n) = (ptree_t*)shm_malloc(sizeof(ptree_t));\ if(NULL == (n))\ goto err_exit;\ tree_size+=sizeof(ptree_t);\ memset((n), 0, sizeof(ptree_t));\ (n)->bp=(p);\ }while(0); #define DR_DST_PING_DSBL_FLAG (1<<0) #define DR_DST_PING_PERM_FLAG (1<<1) #define DR_DST_STAT_DSBL_FLAG (1<<2) #define DR_DST_STAT_NOEN_FLAG (1<<3) #define DR_DST_STAT_DIRT_FLAG (1<<4) #define DR_DST_STAT_MASK (DR_DST_STAT_DSBL_FLAG|DR_DST_STAT_NOEN_FLAG) #define DR_MAX_IPS 32 /* list of PSTN gw */ typedef struct pgw_ { /* internal numerical ID, not DB related */ unsigned int _id; /* GW ID from DB */ str id; /* type of gateway */ int type; str ip_str; struct socket_info *sock; /* strip / pri and attrs */ str pri; int strip; str attrs; /* address and port */ struct ip_addr ips[DR_MAX_IPS]; unsigned short ports[DR_MAX_IPS]; unsigned short protos[DR_MAX_IPS]; unsigned short ips_no; struct pgw_ *next; int flags; }pgw_t; typedef struct pcr_ pcr_t; /* GW/CARRIER linker as kept in arrays, by rules */ typedef struct pgw_list_ { unsigned int is_carrier; union { pgw_t *gw; pcr_t *carrier; }dst; unsigned int weight; }pgw_list_t; #define DR_CR_FLAG_WEIGHT (1<<0) #define DR_CR_FLAG_FIRST (1<<1) #define DR_CR_FLAG_IS_OFF (1<<2) #define DR_CR_FLAG_DIRTY (1<<3) /* list of carriers */ struct pcr_ { /* carrier ID/name from DB */ str id; /* flags */ unsigned int flags; /* array of pointers into the PSTN gw list */ pgw_list_t *pgwl; /* length of the PSTN gw array */ unsigned short pgwa_len; /* attributes string */ str attrs; /* linker in list */ pcr_t *next; }; /* element containing routing information */ typedef struct rt_info_ { /* id matching the one in db */ unsigned int id; /* priority of the rule */ unsigned int priority; /* timerec says when the rule is on */ tmrec_t *time_rec; /* script route to be executed */ int route_idx; /* opaque string with rule attributes */ str attrs; /* array of pointers into the PSTN gw list */ pgw_list_t *pgwl; /* length of the PSTN gw array */ unsigned short pgwa_len; /* how many lists link this element */ unsigned short ref_cnt; } rt_info_t; typedef struct rt_info_wrp_ { rt_info_t *rtl; struct rt_info_wrp_ *next; } rt_info_wrp_t; typedef struct rg_entry_ { unsigned int rgid; rt_info_wrp_t *rtlw; } rg_entry_t; typedef struct ptree_node_ { unsigned int rg_len; unsigned int rg_pos; rg_entry_t *rg; struct ptree_ *next; } ptree_node_t; typedef struct ptree_ { /* backpointer */ struct ptree_ *bp; ptree_node_t ptnode[PTREE_CHILDREN]; } ptree_t; void print_interim( int, int, ptree_t* ); int del_tree( ptree_t * ); int add_prefix( ptree_t*, /* prefix */ str*, rt_info_t *, unsigned int ); rt_info_t* get_prefix( ptree_t *ptree, str* prefix, unsigned int rgid, unsigned int *rgidx, unsigned int *matched_len ); int add_rt_info( ptree_node_t*, rt_info_t*, unsigned int ); pgw_t* get_gw_by_internal_id( map_t gw_tree, unsigned int id ); pgw_t* get_gw_by_id( map_t pgw_tree, str *id ); pcr_t* get_carrier_by_id( map_t carriers_tree, str *id ); void del_rt_list( rt_info_wrp_t *rl ); void free_rt_info( rt_info_t* ); rt_info_t* check_rt( ptree_node_t *ptn, unsigned int rgid ); #endif opensips-2.2.2/modules/drouting/routing.c000066400000000000000000000353411300170765700205270ustar00rootroot00000000000000/* * Copyright (C) 2005-2008 Voice Sistem SRL * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2005-02-20 first version (cristian) * 2005-02-27 ported to 0.9.0 (bogdan) */ #include #include #include #include #include "../../str.h" #include "../../resolve.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_uri.h" #include "../../time_rec.h" #include "routing.h" #include "prefix_tree.h" #include "parse.h" extern int dr_force_dns; rt_data_t* build_rt_data( void ) { rt_data_t *rdata=NULL; if( NULL==(rdata=shm_malloc(sizeof(rt_data_t)))) { LM_ERR("no more shm mem\n"); goto err_exit; } memset(rdata, 0, sizeof(rt_data_t)); INIT_PTREE_NODE(NULL, rdata->pt); rdata->pgw_tree = map_create( AVLMAP_SHARED ); rdata->carriers_tree = map_create( AVLMAP_SHARED ); if (rdata->pgw_tree == NULL || rdata->carriers_tree == NULL) { LM_ERR("Initializing avl failed!\n"); if (rdata->pgw_tree) map_destroy(rdata->pgw_tree, 0); goto err_exit; } return rdata; err_exit: if (rdata) shm_free(rdata); return 0; } int parse_destination_list(rt_data_t* rd, char *dstlist, pgw_list_t** pgwl_ret, unsigned short *len, int no_resize) { #define PGWL_SIZE 32 pgw_list_t *pgwl=NULL, *p=NULL; unsigned int size, pgwl_size; long int t; char *tmp, *ep; str id; /* temporary list of gw while parsing */ pgwl_size = PGWL_SIZE; pgwl = (pgw_list_t*)pkg_malloc(pgwl_size*sizeof(pgw_list_t)); if (pgwl==NULL) { LM_ERR("no more shm mem\n"); goto error; } memset(pgwl, 0, pgwl_size*sizeof(pgw_list_t)); /* parset the destination list */ tmp = dstlist; size = 0; /* parse the dstlst */ while(tmp && (*tmp!=0)) { /* need a larger array ? */ if(size>=pgwl_size){ p=(pgw_list_t*)pkg_malloc((pgwl_size*2)*sizeof(pgw_list_t)); if (p==NULL) { LM_ERR("not enough shm mem to resize\n"); goto error; } memset( p+pgwl_size, 0, 2*pgwl_size*sizeof(pgw_list_t)); memcpy( p, pgwl, pgwl_size*sizeof(pgw_list_t)); pkg_free(pgwl); pgwl_size*=2; pgwl=p; } /* go over spaces */ EAT_SPACE(tmp); /* carrier id or GW id ? */ if (*tmp==CARRIER_MARKER) { pgwl[size].is_carrier = 1; tmp++; } /* eat the destination ID (alphanumerical) */ id.s = tmp; while( *tmp && (isalpha(*tmp) || isdigit(*tmp) || (*tmp)=='_' || (*tmp)=='-') ) tmp++; if (id.s == tmp) { LM_ERR("bad id '%c' (%d)[%s]\n", *tmp, (int)(tmp-dstlist), dstlist); goto error; } id.len = tmp - id.s ; /* look for the destination */ if (pgwl[size].is_carrier) { pgwl[size].dst.carrier = get_carrier_by_id(rd->carriers_tree, &id); } else { pgwl[size].dst.gw = get_gw_by_id(rd->pgw_tree, &id); } if (pgwl[size].dst.gw==NULL) LM_WARN("destination ID <%.*s> was not found\n",id.len,id.s); /* consume spaces */ EAT_SPACE(tmp); /* any weight? */ if (*tmp=='=') { tmp++; /* expect the weight value (int) */ errno = 0; t = strtol(tmp, &ep, 10); if (ep == tmp) { LM_ERR("bad weight value '%c' (%d)[%s]\n", *ep, (int)(ep-dstlist), dstlist); goto error; } if (errno == ERANGE && (t== LONG_MAX || t== LONG_MIN)) { LM_ERR("weight value out of bounds\n"); goto error; } tmp = ep; pgwl[size].weight = t; /* consume spaces */ EAT_SPACE(tmp); } /* valid record ? */ if (pgwl[size].dst.gw==NULL) { /* reset current record and do not count */ memset( pgwl+size, 0, sizeof(pgw_list_t)); } else { /* count record */ size++; } /* separator */ if ( (*tmp==SEP) || (*tmp==SEP1) ) { tmp++; } else if (*tmp!=0) { LM_ERR("bad char %c (%d) [%s]\n", *tmp, (int)(ep-dstlist), dstlist); goto error; } } if (size==0) { LM_DBG("empty destination list\n"); pkg_free(pgwl); *len = 0; *pgwl_ret = NULL; return 0; } /* done with parsing, build the final array and return */ if (no_resize) { *len = size; *pgwl_ret = pgwl; return 0; } p=(pgw_list_t*)shm_malloc(size*sizeof(pgw_list_t)); if (p==NULL) { LM_ERR("not enough shm mem for final build\n"); goto error; } memcpy( p, pgwl, size*sizeof(pgw_list_t)); pkg_free(pgwl); *len = size; *pgwl_ret = p; return 0; error: if (pgwl) pkg_free(pgwl); *len = 0; *pgwl_ret = NULL; return -1; } int add_carrier(char *id, int flags, char *gwlist, char *attrs, int state, rt_data_t *rd) { pcr_t *cr = NULL; unsigned int i; str key; /* allocate a new carrier structure */ cr = (pcr_t*)shm_malloc(sizeof(pcr_t)+strlen(id)+(attrs?strlen(attrs):0)); if (cr==NULL) { LM_ERR("no more shm mem for a new carrier\n"); goto error; } memset(cr, 0, sizeof(pcr_t)); if (gwlist && gwlist[0]!=0 ) { /* parse the list of gateways */ if (parse_destination_list( rd, gwlist, &cr->pgwl,&cr->pgwa_len,0)!=0){ LM_ERR("failed to parse the destinations\n"); goto error; } /* check that all dest to be GW! */ for( i=0 ; ipgwa_len ; i++ ) { if (cr->pgwl[i].is_carrier) { LM_ERR("invalid carrier <%s> definition as points to other " "carrier (%.*s) in destination list\n",id, cr->pgwl[i].dst.carrier->id.len, cr->pgwl[i].dst.carrier->id.s); goto error; } } } /* copy integer fields */ cr->flags = flags; /* set state */ if (state!=0) /* disabled */ cr->flags |= DR_CR_FLAG_IS_OFF; else /* enabled */ cr->flags &= ~DR_CR_FLAG_IS_OFF; /* copy id */ cr->id.s = (char*)(cr+1); cr->id.len = strlen(id); memcpy(cr->id.s,id,cr->id.len); /* copy attributes */ if (attrs && strlen(attrs)) { cr->attrs.s = cr->id.s + cr->id.len; cr->attrs.len = strlen(attrs); memcpy(cr->attrs.s,attrs,cr->attrs.len); } /* link it */ key.s = id; key.len = strlen(id); LM_INFO("carriers key %.*s\n", key.len, key.s); map_put(rd->carriers_tree, key, cr); return 0; error: if (cr) { shm_free(cr); if (cr->pgwl) shm_free(cr->pgwl); } return -1; } rt_info_t* build_rt_info( int id, int priority, tmrec_t *trec, /* script routing table index */ int route_idx, /* list of destinations indexes */ char* dstlst, char* attrs, rt_data_t* rd ) { rt_info_t* rt = NULL;; rt = (rt_info_t*)shm_malloc(sizeof(rt_info_t)+(attrs?strlen(attrs):0)); if (rt==NULL) { LM_ERR("no more shm mem(1)\n"); goto err_exit; } memset(rt, 0, sizeof(rt_info_t)); rt->id = id; rt->priority = priority; rt->time_rec = trec; rt->route_idx = route_idx; if (attrs && strlen(attrs)) { rt->attrs.s = (char*)(rt+1); rt->attrs.len = strlen(attrs); memcpy(rt->attrs.s,attrs,rt->attrs.len); } if ( dstlst && dstlst[0]!=0 ) { if (parse_destination_list(rd, dstlst, &rt->pgwl,&rt->pgwa_len,0)!=0){ LM_ERR("failed to parse the destinations\n"); goto err_exit; } } return rt; err_exit: if ((NULL != rt) ) { if (NULL!=rt->pgwl) shm_free(rt->pgwl); shm_free(rt); } return NULL; } int add_rt_info( ptree_node_t *pn, rt_info_t* r, unsigned int rgid ) { rg_entry_t *trg=NULL; rt_info_wrp_t *rtl_wrp=NULL; rt_info_wrp_t *rtlw=NULL; int i=0; if((NULL == pn) || (NULL == r)) goto err_exit; if (NULL == (rtl_wrp = (rt_info_wrp_t*)shm_malloc(sizeof(rt_info_wrp_t)))) { LM_ERR("no more shm mem\n"); goto err_exit; } memset( rtl_wrp, 0, sizeof(rt_info_wrp_t)); rtl_wrp->rtl = r; if(NULL==pn->rg) { /* allocate the routing groups array */ pn->rg_len = RG_INIT_LEN; if(NULL == (pn->rg = (rg_entry_t*)shm_malloc( pn->rg_len*sizeof(rg_entry_t)))) { /* recover the old pointer to be able to shm_free mem */ goto err_exit; } memset( pn->rg, 0, pn->rg_len*sizeof(rg_entry_t)); pn->rg_pos=0; } /* search for the rgid up to the rg_pos */ for(i=0; (irg_pos) && (pn->rg[i].rgid!=rgid); i++); if(i==pn->rg_len) { /* realloc & copy the old rg */ trg = pn->rg; if(NULL == (pn->rg = (rg_entry_t*)shm_malloc( (pn->rg_len + RG_INIT_LEN)*sizeof(rg_entry_t)))) { /* recover the old pointer to be able to shm_free mem */ pn->rg = trg; goto err_exit; } memset(pn->rg+pn->rg_len, 0, RG_INIT_LEN*sizeof(rg_entry_t)); memcpy(pn->rg, trg, pn->rg_len*sizeof(rg_entry_t)); pn->rg_len+=RG_INIT_LEN; shm_free( trg ); } /* insert into list */ r->ref_cnt++; if(NULL==pn->rg[i].rtlw){ pn->rg[i].rtlw = rtl_wrp; pn->rg[i].rgid = rgid; pn->rg_pos++; goto ok_exit; } if( r->priority > pn->rg[i].rtlw->rtl->priority) { /* change the head of the list */ rtl_wrp->next = pn->rg[i].rtlw; pn->rg[i].rtlw = rtl_wrp; goto ok_exit; } rtlw = pn->rg[i].rtlw; while( rtlw->next !=NULL) { if(r->priority > rtlw->next->rtl->priority) { rtl_wrp->next = rtlw->next; rtlw->next = rtl_wrp; goto ok_exit; } rtlw = rtlw->next; } /* the smallest priority is linked at the end */ rtl_wrp->next=NULL; rtlw->next=rtl_wrp; ok_exit: return 0; err_exit: if (rtl_wrp) shm_free(rtl_wrp); return -1; } int add_dst( rt_data_t *r, /* id */ char *id, /* ip address */ char* ip, /* strip len */ int strip, /* pri prefix */ char* pri, /* dst type*/ int type, /* dst attrs*/ char* attrs, /* probe_mode */ int probing, /* socket */ struct socket_info *sock, /* state */ int state ) { static unsigned id_counter = 0; pgw_t *pgw=NULL; struct sip_uri uri; int l_ip,l_pri,l_attrs,l_id; #define GWABUF_MAX_SIZE 512 char gwabuf[GWABUF_MAX_SIZE]; union sockaddr_union sau; struct proxy_l *proxy; unsigned int sip_prefix; str gwas; str key; if (NULL==r || NULL==ip) { LM_ERR("invalid parametres\n"); goto err_exit; } l_id = strlen(id); l_ip = strlen(ip); l_pri = pri?strlen(pri):0; l_attrs = attrs?strlen(attrs):0; /* check if GW address starts with 'sip' or 'sips' */ if (l_ip>5) { if ( strncasecmp("sip:", ip, 4)==0) sip_prefix = 4; else if ( strncasecmp("sips:", ip, 5)==0) sip_prefix = 5; else sip_prefix = 0; } else sip_prefix = 0; if( sip_prefix==0 ) { if(l_ip+4>=GWABUF_MAX_SIZE) { LM_ERR("GW address (%d) longer " "than %d\n",l_ip+4,GWABUF_MAX_SIZE); goto err_exit; } memcpy(gwabuf, "sip:", 4); memcpy(gwabuf+4, ip, l_ip); gwas.s = gwabuf; gwas.len = 4+l_ip; } else { gwas.s = ip; gwas.len = l_ip; } /* parse the normalized address as a SIP URI */ memset(&uri, 0, sizeof(struct sip_uri)); if(parse_uri(gwas.s, gwas.len, &uri)!=0) { LM_ERR("invalid uri <%.*s>\n", gwas.len, gwas.s); goto err_exit; } /* update the sip_prefix to skip to domain part */ if (uri.user.len) sip_prefix += uri.host.s - uri.user.s; /* allocate new structure */ pgw = (pgw_t*)shm_malloc(sizeof(pgw_t) + l_id + (l_ip-sip_prefix) + l_pri + l_attrs); if (NULL==pgw) { LM_ERR("no more shm mem (%u)\n", (unsigned int)(sizeof(pgw_t)+l_id+l_ip-sip_prefix+l_pri +l_attrs)); goto err_exit; } memset(pgw,0,sizeof(pgw_t)); /* set probing related flags */ switch(probing) { case 0: break; case 1: pgw->flags |= DR_DST_PING_DSBL_FLAG; break; case 2: pgw->flags |= DR_DST_PING_PERM_FLAG; break; default: goto err_exit; } /* set state related flags */ switch(state) { case 0: break; case 1: pgw->flags |= DR_DST_STAT_DSBL_FLAG|DR_DST_STAT_NOEN_FLAG; break; case 2: pgw->flags |= DR_DST_STAT_DSBL_FLAG; break; default: goto err_exit; } /* set outbound socket */ pgw->sock = sock; pgw->_id = ++id_counter; pgw->id.len= l_id; pgw->id.s = (char*)(pgw+1); memcpy(pgw->id.s, id, l_id); pgw->ip_str.len= l_ip-sip_prefix; pgw->ip_str.s = (char*)(pgw+1)+l_id; memcpy(pgw->ip_str.s, ip+sip_prefix, l_ip-sip_prefix); if (pri) { pgw->pri.len = l_pri; pgw->pri.s = ((char*)(pgw+1))+l_id+l_ip-sip_prefix; memcpy(pgw->pri.s, pri, l_pri); } if (attrs) { pgw->attrs.len = l_attrs; pgw->attrs.s = ((char*)(pgw+1))+l_id+l_ip-sip_prefix+l_pri; memcpy(pgw->attrs.s, attrs, l_attrs); } pgw->strip = strip; pgw->type = type; /* add address in the global list of destinations/GWs */ proxy = mk_proxy(&uri.host,uri.port_no,uri.proto,(uri.type==SIPS_URI_T)); if (proxy==NULL) { if(dr_force_dns) { LM_ERR("cannot resolve <%.*s>\n", uri.host.len, uri.host.s); goto err_exit; } else { LM_DBG("cannot resolve <%.*s> - won't be used" " by is_from_gw()\n", uri.host.len, uri.host.s); goto done; } } hostent2ip_addr( &pgw->ips[0], &proxy->host, proxy->addr_idx); pgw->ports[0] = proxy->port; pgw->protos[0] = proxy->proto; LM_DBG("first gw ip addr [%s]\n", ip_addr2a(&pgw->ips[0])); pgw->ips_no = 1; while (pgw->ips_noips[pgw->ips_no], &sau); pgw->ports[pgw->ips_no] = proxy->port; pgw->protos[pgw->ips_no] = proxy->proto; LM_DBG("additional gw ip addr [%s]\n", ip_addr2a( &pgw->ips[pgw->ips_no] ) ); pgw->ips_no++; } free_proxy(proxy); pkg_free(proxy); done: key.s = id; key.len = strlen(id); LM_INFO("pgw tree %p\n", r->pgw_tree); if (map_put(r->pgw_tree, key, pgw)) { LM_ERR("Duplicate gateway!\n"); return -1; } return 0; err_exit: if(NULL!=pgw) shm_free(pgw); return -1; } void destroy_pgw(void *pgw_p) { shm_free((pgw_t *)pgw_p); } void destroy_pcr(void *pcr_p) { pcr_t* pcr = pcr_p; if (pcr->pgwl) shm_free(pcr->pgwl); shm_free(pcr); } /* FIXME FREE AVL HERE */ void del_pgw_list( map_t pgw_tree ) { map_destroy(pgw_tree, destroy_pgw); } /* FIXME FREE AVL HERE */ void del_carriers_list( map_t carriers_tree ) { map_destroy(carriers_tree, destroy_pcr); } void free_rt_data( rt_data_t* rt_data, int all ) { int j; if(NULL!=rt_data) { /* del GW list */ del_pgw_list(rt_data->pgw_tree); rt_data->pgw_tree = 0 ; /* del prefix tree */ del_tree(rt_data->pt); rt_data->pt = 0 ; /* del prefixless rules */ if(NULL!=rt_data->noprefix.rg) { for(j=0;jnoprefix.rg_pos;j++) { if(rt_data->noprefix.rg[j].rtlw !=NULL) { del_rt_list(rt_data->noprefix.rg[j].rtlw); rt_data->noprefix.rg[j].rtlw = 0; } } shm_free(rt_data->noprefix.rg); rt_data->noprefix.rg = 0; } /* del carriers */ del_carriers_list(rt_data->carriers_tree); rt_data->carriers_tree=0; /* del top level */ if (all) shm_free(rt_data); } } opensips-2.2.2/modules/drouting/routing.h000066400000000000000000000054641300170765700205370ustar00rootroot00000000000000/* * Copyright (C) 2005-2008 Voice Sistem SRL * * This file is part of Open SIP Server (OpenSIPS). * * DROUTING OpenSIPS-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * DROUTING OpenSIPS-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-system.ro * * History: * --------- * 2005-02-20 first version (cristian) * 2005-02-27 ported to 0.9.0 (bogdan) */ #ifndef routing_h #define routing_h #include "../../str.h" #include "../../usr_avp.h" #include "../../time_rec.h" #include "prefix_tree.h" #include "../../map.h" #define RG_HASH_SIZE #define RG_INIT_LEN 4 /* the buckets for the rt_data rg_hash */ typedef struct hb_ { int rgid; ptree_t *pt; struct hb_*next; } hb_t; /* routing data is comprised of: - a list of PSTN gw - a hash over routing groups containing pointers to the coresponding prefix trees */ typedef struct rt_data_ { /* avl of PSTN gw */ map_t pgw_tree; /* avl of carriers */ map_t carriers_tree; /* default routing list for prefixless rules */ ptree_node_t noprefix; /* tree with routing prefixes */ ptree_t *pt; }rt_data_t; typedef struct _dr_group { /* 0 - use grp ; 1 - use AVP */ int type; union { unsigned int grp_id; int avp_name; }u; } dr_group_t; /* init new rt_data structure */ rt_data_t* build_rt_data( void ); int add_carrier( char *id, int flags, char *gwlist, char *attrs, int state, rt_data_t *rd ); /* add a PSTN gw in the list */ int add_dst( rt_data_t*, /* id */ char *, /* ip address */ char*, /* strip len */ int, /* pri prefix */ char*, /* dst type*/ int, /* dst attrs*/ char*, /* probe_mode */ int, /* socket */ struct socket_info*, /* state */ int ); /* build a routing info list element */ rt_info_t* build_rt_info( int id, int priority, tmrec_t* time, /* ser routing table id */ int route_id, /* list of destinations indexes */ char* dstlst, char* attr, rt_data_t* rd ); int parse_destination_list( rt_data_t* rd, char *dstlist, pgw_list_t** pgwl_ret, unsigned short *len, int no_resize ); void del_pgw_list( map_t pgw_tree ); void free_rt_data( rt_data_t*, int ); #endif opensips-2.2.2/modules/emergency/000077500000000000000000000000001300170765700170115ustar00rootroot00000000000000opensips-2.2.2/modules/emergency/Makefile000077500000000000000000000002651300170765700204570ustar00rootroot00000000000000# # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=emergency.so LIBS+=-lcurl include ../../Makefile.modules opensips-2.2.2/modules/emergency/README000066400000000000000000000300301300170765700176650ustar00rootroot00000000000000Emergency Call Module Evandro Villaron Robison Tesini Edited by Evandro Villaron Edited by Robison Tesini Copyright © 2014 Villaron/Tesini Revision History Revision $Rev: 1 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. db_table_routing (string) 1.3.3. db_table_report (string) 1.3.4. db_table_provider (string) 1.3.5. proxy_role (integer) 1.3.6. url_vpc (string) 1.3.7. emergency_codes (string) 1.3.8. timer_interval (interger) 1.3.9. contingency_hostname (string) 1.3.10. emergency_call_server (string) 1.3.11. Exported Functions List of Examples 1.1. Setting the db_url parameter 1.2. Setting the db_table_routing parameter 1.3. Setting the db_table_report parameter 1.4. Setting the db_table_provider parameter 1.5. Setting the proxy_role parameter 1.6. Setting the url_vpc parameter 1.7. Setting the emergency_codes parameter 1.8. Setting the timer_interval parameter 1.9. Setting the contingency_hostname parameter 1.10. Setting the emergency_call_server parameter 1.11. emergency_call() usage 1.12. failure() usage Chapter 1. Admin Guide 1.1. Overview The emergency module provides emergency call treatment for OpenSIPS, following the architecture i2 specification of the American entity NENA. (National Emergency Number Association). The NENA solution routes the emergency call to a closer gateway (ESGW) and this forward the call to a PSAP(call center responsible for answering emergency calls) that serves the area of ​​the caller, so this must consider the handling and transport of caller location information in the SIP protocol. To attend this new need the NENA solution consists of several servers: to determine the location (LIS), to determine the area of emergency treatment depending on location (VPC), validate location stored (VDB), among others. Along with these elements have the SIP Proxy that interface with these servers to route the call. The OpenSIPS can do the functions of these SIP Proxy through this emergency module, may perform the function of a Call Server, Redirect Server and Routing Proxy, depending on the proposed scenario: * scenario I: The VSP(Voip Serve Provide) retains control over the processing of emergency calls. The VSP’s Call Server implements the v2 interface that queries the VPC for routing information, with this information selects the proper ESGW, if normal routing fails routes calls via the PSTN using the contingency number(LRO). * scenario II: The VSP transfers all emergency calls to Routing Proxy provider using the v6 SIP interface. Once done transfer the VSP no longer participates in the call. The Routing Proxy provider implements the v2 interface, queries the VPC for for routing information, and forwards the call. * scenario III: The VSP requests routing information for the Redirect Server operator, but remains part of the call. The Redirect Server obtains the routing information from the VPC. It returns the call to the VSP’s Call Server with routing information in the SIP Contact Header. The Call Server selects the proper ESGW based on this information. The emergency module allows the OpenSIPS play the role of a Call Server, a Proxy or Redirect Server Routing within the scenarios presented depending on how it is configured. 1.2. Scenario I: The VSP that originating the call is the same as handle the call and sends the routing information request to the VPC. The emergency module through emergency_call() command will check if the INVITE received is an emergency call. In this case, the OpenSIPS will get caller location information from specific headers and body in the INVITE. With this information along configuration parameters defined for this module, the opensips implements the v2 interface that queries the VPC for routing information (i.e., ESQK, LRO, and either the ERT or ESGWRI), selects the proper ESGW based on the ESGWRI. When the call ends the OpenSIPS receives BYE request, it warns the VPC for clean your data that is based on the call. The OpenSIPS through failure() command will try to route the calls via the PSTN using a national contingency number(LRO) if normal routing fails. 1.3.Scenario II: The VSP transfers the call to a Routing Server provider The emergency module through emergency_call() command will check if the INVITE received is an emergency call. In this case, it will forward the call to a Routing Proxy that will interface with the VPC and route the call. The OpenSIPS will leave the call, and all the request of this dialog received by the opensips will be forwarded to the Routing Server. 1.4.Scenario III: The VSP requests routing information for the Redirect Server The emergency module through emergency_call() command will check if the INVITE received is an emergency call. In this case, it requests routing information to Redirect Server. The Redirect has interface with the VPC and return to VSP's Call Server response whith routing informations on Contact header. The Call Server uses this information to treat the call. When the emergency call ends, it must notify the Redirect Server that inform to VPC to release the resources. To use this module should informs the mandatory parameters in script and make the correct filling out of the emergency module tables, in accordance with the role chosen within the described scenarios. For more details check the "Emergency calls using OpenSIPS". 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * Dialog - Dialoge module.. * TM - Transaction module.. * RR - Record-Route module.. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libcurl. 1.3. Exported Parameters 1.3.1. db_url (string) The database url must be specified. Default value is “NULLâ€. Example 1.1. Setting the db_url parameter ... modparam("emergency", "db_url", "mysql://opensips:opensipsrw@localhost/o pensipsâ€) ... 1.3.2. db_table_routing (string) The name of the db table storing routing information to emergency calls. Default value is “emergency_routingâ€. Example 1.2. Setting the db_table_routing parameter ... modparam("emergency", "db_table_routing", "emergency_routing") ... 1.3.3. db_table_report (string) The name of the db table that stores the emergency call report. Default value is “emergency_reportâ€. Example 1.3. Setting the db_table_report parameter ... modparam("emergency", "db_table_report", "emergency_report") ... 1.3.4. db_table_provider (string) The name of the db table that stores the nodes information of organization involved in emergency calls. Default value is “emergency_service_providerâ€. Example 1.4. Setting the db_table_provider parameter ... modparam("emergency", "db_table_provider", "emergency_service_provider") ... 1.3.5. proxy_role (integer) This parameter define what role the opensips will take to treat emergency call: 0 – The opensips is the Call Server in scenario I. In this role the opensips implements the V2 interface, directly queries the VPC for ESGWRI/ESQK, selects the proper ESGW given the ESGWRI and routes calls Via the PSTN using the LRO if routing fails. 1 – The opensips is the Call Server in scenario II that sends the INVITE on emergency call to a Routing Proxy provider. The Routing Proxy provider implements the V2 interface. 2 - The opensips is the Routing Proxy in scenario II. In this role the opensips implements the V2 interface, directly queries the VPC for ESGWRI/ESQK, selects the proper ESGW given the ESGWRI and routes calls Via the PSTN using the LRO if routing fails. 3 - The opensips is the Redirect Proxy in scenario III that receives the INVITE on emergency call from Call Server. The Redirect Server obtains the ESGWRI/ESQK from the VPC and sends in the SIP 3xx response to the Call Server. 4 - The opensips is the Call Server in scenario III that sends the INVITE on emergency call to a Redirect Server. The Redirect Server obtains the ESGWRI/ESQK from the VPC. It returns the call to the opensips with the ESGWRI/ESQK in the header contact in the SIP response. The opensips selects the proper ESGW based on the ESGWRI. Default value is “0â€. Example 1.5. Setting the proxy_role parameter ... modparam("emergency", "proxy_role", 0)) ... 1.3.6. url_vpc (string) The VPC url that opensips request the routing information to emergency call. This VPC url has IP:Port format Default value is “empty stringâ€. Example 1.6. Setting the url_vpc parameter ... modparam("emergency", "url_vpc", “192.168.0.103:5060â€) ... 1.3.7. emergency_codes (string) Local emergency number. Opensips uses this number to recognize a emergency call beyond the username default defined by RFC-5031 (urn:service.sos.). Along with the number should be given a brief description about this code. The format is code_number-description. It can register multiple emergency numbers. Default value is “NULLgâ€. Example 1.7. Setting the emergency_codes parameter ... modparam("emergency", "emergency_codes", “911-us emegency codeâ€) ... 1.3.8. timer_interval (interger) Sets the time interval polling to make the copy in memory of the db_table_routing. Default value is “10â€. Example 1.8. Setting the timer_interval parameter ... modparam("emergency","timer_interval",20) ... 1.3.9. contingency_hostname (string) The contingency_hostname is the url of the server que will route the call to the PSTN using the number of contingency. Default value is “NULLâ€. Example 1.9. Setting the contingency_hostname parameter ... modparam("emergency","contingency_hostname",“176.34,29.102:5060â€) ... 1.3.10. emergency_call_server (string) The emergency_call_server is the url of the Routing Proxy/Redirect Server that will handle the emergency call in cenario II. Its is mandatory if Opensips act as Call Server in scenario II (proxy_role = 1 and flag_third_enterprise = 0) or Call Server in scenario III (proxy_role = 2). Default value is “NULLâ€. Example 1.10. Setting the emergency_call_server parameter ... modparam("emergency","emergency_call_server",“124.78.29.123:5060â€) ... 1.3.11. Exported Functions 1.3.11.1. emergency_call() Checks whether the incoming call is an emergency call, case it is treats, and routes the call to the destination determined by VPC. The function returns true if is a emergency call and the treat was Ok. This function can be used from the REQUEST routes. Example 1.11. emergency_call() usage ... # Example of treat of emergency call if (emergency_call()){ xlog("emergency call\n"); t_on_failure("emergency_call"); t_relay(); exit; } ... 1.3.11.2. failure() This function is used when trying to route the emergency call to the destination specified by the VPC and doesn't work, then uses this function to make one last attempt for a contingency number. The function returns true if the contingency treat was OK. This function can be used from the FAILURE routes. Example 1.12. failure() usage ... # Example od treat of contingency in emergency call if (failure()) { if (!t_relay()) { send_reply("500","Internal Error"); }; exit; } ... opensips-2.2.2/modules/emergency/doc/000077500000000000000000000000001300170765700175565ustar00rootroot00000000000000opensips-2.2.2/modules/emergency/doc/emergency.xml000066400000000000000000000023501300170765700222560ustar00rootroot00000000000000 %docentities; ]> Emergency Call Module &osipsname; Evandro Villaron evillaron@gmail.com Robison Tesini rtesini@gmail.com Evandro Villaron Robison Tesini 2014 Villaron/Tesini $Rev: 1 $ $Date$ &admin; &faq; opensips-2.2.2/modules/emergency/doc/emergency_admin.xml000066400000000000000000000342051300170765700234320ustar00rootroot00000000000000 &adminguide;
Overview The emergency module provides emergency call treatment for OpenSIPS, following the architecture i2 specification of the American entity NENA. (National Emergency Number Association). The NENA solution routes the emergency call to a closer gateway (ESGW) and this forward the call to a PSAP(call center responsible for answering emergency calls) that serves the area of ​​the caller, so this must consider the handling and transport of caller location information in the SIP protocol. To attend this new need the NENA solution consists of several servers: to determine the location (LIS), to determine the area of emergency treatment depending on location (VPC), validate location stored (VDB), among others. Along with these elements have the SIP Proxy that interface with these servers to route the call. The OpenSIPS can do the functions of these SIP Proxy through this emergency module, may perform the function of a Call Server, Redirect Server and Routing Proxy, depending on the proposed scenario: scenario I: The VSP(Voip Serve Provide) retains control over the processing of emergency calls. The VSP’s Call Server implements the v2 interface that queries the VPC for routing information, with this information selects the proper ESGW, if normal routing fails routes calls via the PSTN using the contingency number(LRO). scenario II: The VSP transfers all emergency calls to Routing Proxy provider using the v6 SIP interface. Once done transfer the VSP no longer participates in the call. The Routing Proxy provider implements the v2 interface, queries the VPC for for routing information, and forwards the call. scenario III: The VSP requests routing information for the Redirect Server operator, but remains part of the call. The Redirect Server obtains the routing information from the VPC. It returns the call to the VSP’s Call Server with routing information in the SIP Contact Header. The Call Server selects the proper ESGW based on this information. The emergency module allows the OpenSIPS play the role of a Call Server, a Proxy or Redirect Server Routing within the scenarios presented depending on how it is configured. 1.2. Scenario I: The VSP that originating the call is the same as handle the call and sends the routing information request to the VPC. The emergency module through emergency_call() command will check if the INVITE received is an emergency call. In this case, the OpenSIPS will get caller location information from specific headers and body in the INVITE. With this information along configuration parameters defined for this module, the opensips implements the v2 interface that queries the VPC for routing information (i.e., ESQK, LRO, and either the ERT or ESGWRI), selects the proper ESGW based on the ESGWRI. When the call ends the OpenSIPS receives BYE request, it warns the VPC for clean your data that is based on the call. The &osips; through failure() command will try to route the calls via the PSTN using a national contingency number(LRO) if normal routing fails. 1.3.Scenario II: The VSP transfers the call to a Routing Server provider The emergency module through emergency_call() command will check if the INVITE received is an emergency call. In this case, it will forward the call to a Routing Proxy that will interface with the VPC and route the call. The OpenSIPS will leave the call, and all the request of this dialog received by the opensips will be forwarded to the Routing Server. 1.4.Scenario III: The VSP requests routing information for the Redirect Server The emergency module through emergency_call() command will check if the INVITE received is an emergency call. In this case, it requests routing information to Redirect Server. The Redirect has interface with the VPC and return to VSP's Call Server response whith routing informations on Contact header. The Call Server uses this information to treat the call. When the emergency call ends, it must notify the Redirect Server that inform to VPC to release the resources. To use this module should informs the mandatory parameters in script and make the correct filling out of the emergency module tables, in accordance with the role chosen within the described scenarios. For more details check the "Emergency calls using OpenSIPS".
Dependencies
&osips; Modules The following modules must be loaded before this module: Dialog - Dialoge module.. TM - Transaction module.. RR - Record-Route module..
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libcurl.
Exported Parameters
<varname>db_url</varname> (string) The database url must be specified. Default value is NULL. Setting the <varname>db_url</varname> parameter ... modparam("emergency", "db_url", "mysql://opensips:opensipsrw@localhost/opensipsâ€) ...
<varname>db_table_routing</varname> (string) The name of the db table storing routing information to emergency calls. Default value is emergency_routing. Setting the <varname>db_table_routing</varname> parameter ... modparam("emergency", "db_table_routing", "emergency_routing") ...
<varname>db_table_report</varname> (string) The name of the db table that stores the emergency call report. Default value is emergency_report. Setting the <varname>db_table_report</varname> parameter ... modparam("emergency", "db_table_report", "emergency_report") ...
<varname>db_table_provider</varname> (string) The name of the db table that stores the nodes information of organization involved in emergency calls. Default value is emergency_service_provider. Setting the <varname>db_table_provider</varname> parameter ... modparam("emergency", "db_table_provider", "emergency_service_provider") ...
<varname>proxy_role</varname> (integer) This parameter define what role the opensips will take to treat emergency call: 0 – The opensips is the Call Server in scenario I. In this role the opensips implements the V2 interface, directly queries the VPC for ESGWRI/ESQK, selects the proper ESGW given the ESGWRI and routes calls Via the PSTN using the LRO if routing fails. 1 – The opensips is the Call Server in scenario II that sends the INVITE on emergency call to a Routing Proxy provider. The Routing Proxy provider implements the V2 interface. 2 - The opensips is the Routing Proxy in scenario II. In this role the opensips implements the V2 interface, directly queries the VPC for ESGWRI/ESQK, selects the proper ESGW given the ESGWRI and routes calls Via the PSTN using the LRO if routing fails. 3 - The opensips is the Redirect Proxy in scenario III that receives the INVITE on emergency call from Call Server. The Redirect Server obtains the ESGWRI/ESQK from the VPC and sends in the SIP 3xx response to the Call Server. 4 - The opensips is the Call Server in scenario III that sends the INVITE on emergency call to a Redirect Server. The Redirect Server obtains the ESGWRI/ESQK from the VPC. It returns the call to the opensips with the ESGWRI/ESQK in the header contact in the SIP response. The opensips selects the proper ESGW based on the ESGWRI. Default value is 0. Setting the <varname>proxy_role</varname> parameter ... modparam("emergency", "proxy_role", 0)) ...
<varname>url_vpc</varname> (string) The VPC url that opensips request the routing information to emergency call. This VPC url has IP:Port format Default value is empty string. Setting the <varname>url_vpc</varname> parameter ... modparam("emergency", "url_vpc", “192.168.0.103:5060â€) ...
<varname>emergency_codes</varname> (string) Local emergency number. Opensips uses this number to recognize a emergency call beyond the username default defined by RFC-5031 (urn:service.sos.). Along with the number should be given a brief description about this code. The format is code_number-description. It can register multiple emergency numbers. Default value is NULLg. Setting the <varname>emergency_codes</varname> parameter ... modparam("emergency", "emergency_codes", “911-us emegency codeâ€) ...
<varname>timer_interval</varname> (interger) Sets the time interval polling to make the copy in memory of the db_table_routing. Default value is 10. Setting the <varname>timer_interval</varname> parameter ... modparam("emergency","timer_interval",20) ...
<varname>contingency_hostname</varname> (string) The contingency_hostname is the url of the server que will route the call to the PSTN using the number of contingency. Default value is NULL. Setting the <varname>contingency_hostname</varname> parameter ... modparam("emergency","contingency_hostname",“176.34,29.102:5060â€) ...
<varname>emergency_call_server</varname> (string) The emergency_call_server is the url of the Routing Proxy/Redirect Server that will handle the emergency call in cenario II. Its is mandatory if Opensips act as Call Server in scenario II (proxy_role = 1 and flag_third_enterprise = 0) or Call Server in scenario III (proxy_role = 2). Default value is NULL. Setting the <varname>emergency_call_server</varname> parameter ... modparam("emergency","emergency_call_server",“124.78.29.123:5060â€) ...
Exported Functions
<function moreinfo="none">emergency_call() </function> Checks whether the incoming call is an emergency call, case it is treats, and routes the call to the destination determined by VPC. The function returns true if is a emergency call and the treat was Ok. This function can be used from the REQUEST routes. <function moreinfo="none">emergency_call()</function> usage ... # Example of treat of emergency call     if (emergency_call()){         xlog("emergency call\n");         t_on_failure("emergency_call");    t_relay();    exit; } ...
<function moreinfo="none">failure() </function> This function is used when trying to route the emergency call to the destination specified by the VPC and doesn't work, then uses this function to make one last attempt for a contingency number. The function returns true if the contingency treat was OK. This function can be used from the FAILURE routes. <function moreinfo="none">failure()</function> usage ... # Example od treat of contingency in emergency call if (failure()) {         if (!t_relay()) {            send_reply("500","Internal Error");         };         exit; } ...
opensips-2.2.2/modules/emergency/emergency_methods.c000066400000000000000000001526221300170765700226660ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include #include "emergency_methods.h" #define TABLE_ROUTING_VERSION 1 #define TABLE_REPORT_VERSION 1 #define TABLE_PROVIDER_VERSION 1 /* * Module initialization and cleanup */ static int mod_init(void); static int child_init(int); static void mod_destroy(void); struct dlg_binds dlgb; struct rr_binds rr_api; str callid_aux; /* * Exported functions */ static cmd_export_t cmds[] = { {"emergency_call", (cmd_function) emergency_call, 0, 0, 0, REQUEST_ROUTE | BRANCH_ROUTE }, {"failure", (cmd_function) failure, 0, 0, 0, FAILURE_ROUTE | ONREPLY_ROUTE }, { 0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { { "emergency_codes", STR_PARAM | USE_FUNC_PARAM, (void *) &set_codes}, { "timer_interval", INT_PARAM, &timer_interval}, { "db_url", STR_PARAM, &db_url.s}, { "db_table_routing", STR_PARAM, &table_name}, { "db_table_report", STR_PARAM, &table_report}, { "db_table_provider", STR_PARAM, &table_provider}, { "url_vpc", STR_PARAM, &url_vpc}, { "contingency_hostname", STR_PARAM, &contingency_hostname}, { "emergency_call_server", STR_PARAM, &call_server_hostname}, { "proxy_role", INT_PARAM, &proxy_role}, { "callorigin", STR_PARAM, &call_origin}, { "call_htable_size", INT_PARAM, &emetable_size}, { "subs_htable_size", INT_PARAM, &substable_size}, { 0, 0, 0} }; /* * Module parameter variables */ static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* * Module parameter variables */ struct module_exports exports = { "emergency", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ NULL, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ mod_destroy, child_init /* per-child init function */ }; /* * Polling Inicialization Functions */ /* extracts the code and the description of the parameter emergency_code * and stores these values in new_code linked list */ static int set_codes(unsigned int type, void *val) { char *code, *description, *p; int code_len, description_len, len; struct code_number *new_code; code = (char *) val; len = strlen(code); p = memchr(code, CODE_DELIM, len); if (!p) { LM_ERR("Invalid code - delimiter not found\n"); return -1; } /* separates the code and the description using the delimiter CODE_DELIM ("-") */ code_len = p - code; description = ++p; description_len = len - code_len - 1; new_code = pkg_malloc(sizeof (struct code_number)); if (!new_code) { LM_ERR("No more pkg memory\n"); return -1; } LM_DBG(" --- CODE -----> %.*s\n", code_len, code); LM_DBG(" --- DESC -----> %.*s\n", description_len, description); new_code->code.s = code; new_code->code.len = code_len; new_code->description.s = description; new_code->description.len = description_len; if (!codes) codes = new_code; else { new_code->next = codes; codes = new_code; } return 0; } void destroy_codes(struct code_number *codes){ struct code_number *c; while(codes){ c= codes; codes= codes->next; pkg_free(c); } } /* * This function is responsible for : * - load api from modules dialog and rr * - open database connection and test the version of the tables in the database * - initialize polling routing timer * - initialize the module linked lists db_esrn_domain and calls_eme * - initialize lock: ref_lock */ static int mod_init(void) { LM_DBG("Initializing module\n"); // checks for mandatory fields mandatory_parm = shm_malloc(2); mandatory_parm[0] = '1'; mandatory_parm[1] = 0; inicialized = shm_malloc(2); inicialized[0] = '0'; inicialized[1] = 0; LM_DBG(" ---fill parameters not config with blank_space \n"); if (fill_blank_space() == -1) return -1; if ( load_dlg_api( &dlgb ) != 0 ) { LM_ERR("failed to load DLG api\n"); return -1; } if (load_tm_api(&eme_tm)!=0) { LM_ERR( "can't load TM API\n"); return -1; } if (load_rr_api(&rr_api) != 0) { LM_ERR("failed to load rr API\n"); return -1; } empty = shm_malloc(sizeof (char)); memset(empty, '\0', 1); if(call_origin == NULL) call_origin = empty; if (db_url.s) { db_url.len = strlen(db_url.s); LM_DBG("We have db_url = %.*s\n", db_url.len, db_url.s); /* Find a database module */ if (db_bind_mod(&db_url, &db_funcs) < 0) { LM_ERR("Unable to bind to a database driver\n"); return -1; } /* open a test connection */ if ((db_con = db_funcs.init(&db_url)) == 0) { LM_ERR("cannot init connection to DB\n"); return -1; } if (!DB_CAPABILITY(db_funcs, DB_CAP_ALL)) { LM_ERR("database modules does not provide all functions needed by module\n"); return -1; } if (db_check_table_version(&db_funcs, db_con, &table_name, TABLE_ROUTING_VERSION) < 0) { LM_ERR("error during routing table version check.\n"); return -1; } if (db_check_table_version(&db_funcs, db_con, &table_report, TABLE_REPORT_VERSION) < 0) { LM_ERR("error during report table version check.\n"); return -1; } if (db_check_table_version(&db_funcs, db_con, &table_provider, TABLE_PROVIDER_VERSION) < 0) { LM_ERR("error during provider table version check.\n"); return -1; } db_funcs.close(db_con); db_con = 0; } db_table = (str *)shm_malloc(sizeof (str)); if (!db_table) { LM_ERR("no more memory"); return -1; } db_table = &table_report; db_esrn_esgwri = shm_malloc(sizeof (struct esrn_routing *)); if (!db_esrn_esgwri) { LM_ERR("no more memory"); return -1; } *db_esrn_esgwri = NULL; db_service_provider = shm_malloc(sizeof (struct service_provider *)); if (!db_service_provider) { LM_ERR("no more memory"); return -1; } *db_service_provider = NULL; if (register_timer("emer_rout_table", routing_timer, 0, timer_interval, 0) < 0) { LM_ERR("failed to register timer \n"); return -1; } if ((ref_lock = lock_init_rw()) == NULL) { LM_ERR("failed to init lock\n"); return -1; } if(emetable_size< 1) emet_size= 512; else emet_size= 1<< emetable_size; call_htable= new_ehtable(emet_size); if(call_htable== NULL) { LM_ERR(" initializing emergency_call hash table\n"); return -1; } if(substable_size< 1) subst_size= 512; else subst_size= 1<< substable_size; subs_htable= new_shtable(subst_size); if(subs_htable== NULL) { LM_ERR(" initializing emergency_call hash table\n"); return -1; } /* data */ curl_global_init(CURL_GLOBAL_ALL); LM_DBG("EMERGENCY Module initialized!\n"); return 0; } /* this function is responsible for: * - open database connection * - initialize polling routing timer */ static int child_init(int rank) { LM_DBG("Initializing child\n"); if (db_url.s && rank>PROC_MAIN) { /* open a test connection */ if ((db_con = db_funcs.init(&db_url)) == 0) { LM_ERR("cannot init connection to DB\n"); return -1; } if (strcmp(inicialized, "0") == 0){ inicialized[0] = '1'; inicialized[1] = 0; routing_timer(0, 0); } } return 0; } /* * - close database connection * - terminate lock (ref_lock) */ static void mod_destroy(void) { curl_global_cleanup(); if (db_con != NULL && db_funcs.close != 0) db_funcs.close(db_con); if(ref_lock){ lock_destroy_rw( ref_lock ); ref_lock = NULL; } if(call_htable) destroy_ehtable(call_htable, emet_size); if(subs_htable) destroy_shtable(subs_htable, subst_size); shm_free(inicialized); shm_free(db_service_provider); shm_free(db_esrn_esgwri); shm_free(empty); destroy_codes(codes); } /* * - copying data from the routing table to the list db_esrn_domain (performance improvement) */ void routing_timer(unsigned int ticks, void *attr) { if (get_db_routing(table_name, ref_lock ) != 1) LM_ERR("ERROR IN GET ROUTING OF DB \n"); if (get_db_provider(table_provider, ref_lock ) != 1) LM_ERR("ERROR IN GET SERVICE PROVIDER OF DB \n"); libera_esqk(); free_subs(); } /* * - verifying the expiration for packet loss ( timing values are diferent for ACK and BYE) * - if there is an expiration the module sends a POST informing the VPC to exclude the number * the key ESQK is retreived from the list calls_eme */ static void libera_esqk(void) { time_t rawtime; struct tm * timeinfo; int resp = 1; char* response; char* esct_callid; char* xml; struct node* current; NODE *previous = NULL; NODE *free_cell; int i; for(i= 0; i< emet_size; i++){ lock_get(&call_htable[i].lock); previous= call_htable[i].entries; current= previous->next; while (current) { current->esct->timeout --; NODE* next = current->next; LM_DBG("TIMEOUT:%d\n", current->esct->timeout); if (current->esct->timeout <= 0 ){ LM_DBG("time fires\n"); free_cell = current; previous->next = next; LM_DBG("********************************************CALLID FREE%s\n", free_cell->esct->callid); if ((proxy_role == 0) || (proxy_role == 1) ||(proxy_role == 4)){ //sends ESCT only if VPC provided key ESQK if (strlen(free_cell->esct->esqk) > 0){ LM_DBG(" --- SEND ESQK=%s \n \n",free_cell->esct->esqk); //send esctRequest to the VPC time(&rawtime); timeinfo = localtime(&rawtime); strftime(free_cell->esct->datetimestamp, MAX_TIME_SIZE, "%Y-%m-%dT%H:%M:%S%Z", timeinfo); xml = buildXmlFromModel(free_cell->esct); resp = post(url_vpc, xml, &response); if (resp == -1) { LM_ERR(" --- PROBLEM OF THE BYE POST\n \n"); } esct_callid = parse_xml_esct(response); if (esct_callid== NULL) { LM_ERR(" --- esctAck invalid format or without mandatory field \n \n"); } else { if (strcmp(esct_callid, free_cell->esct->callid)){ LM_ERR(" --- callid in esctAck different from asctRequest \n \n"); } LM_DBG(" *** esctACK OK\n"); if(esct_callid) pkg_free(esct_callid); } pkg_free(response); pkg_free(xml); } } shm_free(free_cell->esct->esgwri); shm_free(current); }else{ previous = current; } current = next; } lock_release(&call_htable[i].lock); } } /* * - verifying the expiration for subscribe * - free subscriber cell */ static void free_subs(void) { time_t rawtime; struct sm_subscriber* current; struct sm_subscriber* previous = NULL; struct sm_subscriber* free_cell; struct sm_subscriber* next; int time_C; int i; time(&rawtime); time_C = (int)rawtime; LM_DBG("TIME : %d \n", (int)rawtime ); for(i= 0; i< subst_size; i++){ lock_get(&subs_htable[i].lock); previous= subs_htable[i].entries; current= previous->next; while (current) { next = current->next; LM_DBG("timeout %d\n", current->timeout); if (current->timeout <= time_C ){ LM_DBG("time fires %d\n", current->timeout); free_cell = current; previous->next = next; shm_free(free_cell); }else{ previous = current; } current = next; } lock_release(&subs_htable[i].lock); } } /* * Callback Functions */ /* * - treats the request within the dialog forwarding to the INVITE that first created the dialog * - if the request is a BYE treats the call ending functions */ void indialog_ua(struct dlg_cell* dlg, int type, struct dlg_cb_params * params){ struct sip_msg *msg = params->msg; int dir = params->direction; int resp; UNUSED(resp); LM_DBG(" New sequential request received:%d !! \n",dir); LM_DBG(" New sequential request method:%.*s \n",msg->first_line.u.request.method.len,msg->first_line.u.request.method.s); if (memcmp(msg->first_line.u.request.method.s,"BYE", msg->first_line.u.request.method.len) == 0) { LM_DBG(" --- TREAT BYE ----- \n \n"); resp = bye(msg,dir); LM_DBG(" ---TREATMENT DIALOG BYE:%d", resp); }else{ if (dir == 1){ LM_DBG(" --- TREAT DOWNSTREAM ----- \n \n"); resp = routing_ack(msg); LM_DBG(" ---TREATMENT DIALOG ACK:%d", resp); } } } void reply_in_redirect( struct cell* t, int type, struct tmcb_params *params){ char *contact_esgwri = NULL; char *contact_lro = NULL; struct sip_msg *reply = params->rpl; struct sip_msg *msg_retran = params->req; struct to_body *pfrom = NULL; unsigned int hash_code; int resp = 0; int resp_esqk = 0; if (extract_contact_hdrs(reply, &contact_esgwri, &contact_lro) == -1){ return; } if (msg_retran->from->parsed == NULL){ if ( parse_from_header( reply )<0 ){ LM_ERR("300 response without From header\n"); goto error_01; } } pfrom = get_from(msg_retran); LM_DBG("PFROM_TAG: %.*sxxx \n ", pfrom->tag_value.len, pfrom->tag_value.s ); if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0){ LM_ERR("300 response without from_tag value \n"); goto error_01; } if( msg_retran->callid==NULL || msg_retran->callid->body.s==NULL){ LM_ERR("reply without callid header\n"); goto error_01; } call_cell = pkg_malloc(sizeof (ESCT)); if (call_cell == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return; } call_cell->callid = pkg_malloc(sizeof (char)* reply->callid->body.len + 1); if (call_cell->callid == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return; } memcpy(call_cell->callid, reply->callid->body.s, reply->callid->body.len); call_cell->callid[reply->callid->body.len] = 0; call_cell->eme_dlg_id = pkg_malloc(sizeof (struct dialog_set)); if (call_cell->eme_dlg_id == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return; } call_cell->eme_dlg_id->local_tag = pkg_malloc(sizeof (char)* pfrom->tag_value.len+1); if (call_cell->eme_dlg_id->local_tag == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return; } memcpy(call_cell->eme_dlg_id->local_tag, pfrom->tag_value.s, pfrom->tag_value.len); call_cell->eme_dlg_id->local_tag[pfrom->tag_value.len] = 0; call_cell->eme_dlg_id->call_id = pkg_malloc(sizeof (char)*reply->callid->body.len+1); if (call_cell->eme_dlg_id->call_id == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return; } memcpy(call_cell->eme_dlg_id->call_id, reply->callid->body.s, reply->callid->body.len); call_cell->eme_dlg_id->call_id[reply->callid->body.len] = 0; call_cell->eme_dlg_id->rem_tag = ""; call_cell->esqk = empty; call_cell->esgw = empty; call_cell->lro = empty; call_cell->ert_srid = empty; call_cell->esgwri = empty; call_cell->result = empty; call_cell->datetimestamp = empty; call_cell->ert_npa = 0; call_cell->ert_resn = 0; call_cell->disposition = empty; call_cell->datetimestamp = empty; call_cell->timeout = ACK_TIME; call_cell->source = pkg_malloc(sizeof (NENA)); if (call_cell->source == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return; } call_cell->source->organizationname = empty; call_cell->source->hostname = empty; call_cell->source->nenaid = empty; call_cell->source->contact = empty; call_cell->source->certuri = empty; call_cell->vpc = pkg_malloc(sizeof (NENA)); if (call_cell->vpc == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return; } call_cell->vpc->organizationname = empty; call_cell->vpc->hostname = empty; call_cell->vpc->nenaid = empty; call_cell->vpc->contact = empty; call_cell->vpc->certuri = empty; if (contact_lro){ if(get_lro_in_contact(contact_lro, call_cell) == -1){ return; } } if (contact_esgwri){ resp_esqk = get_esqk_in_contact(contact_esgwri, call_cell); if(resp_esqk == -1){ return; }else{ if(resp_esqk == 1){ resp = get_esgwri_ert_in_contact(contact_esgwri, call_cell); if (resp == -1){ return; }else{ if(resp == 0){ if(call_cell->lro == empty){ LM_ERR("dont exits esgwri/ert or lro to routing\n"); goto end; } } } }else{ LM_DBG("exits lro to routing %d\n", resp); if(call_cell->lro == empty){ LM_ERR("dont exits esgwri/ert or lro to routing\n"); goto end; } } } } hash_code= core_hash(&reply->callid->body, 0, emet_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); if(insert_ehtable(call_htable,hash_code,call_cell)< 0){ LM_ERR("inserting new record in subs_htable\n"); } end: free_call_cell(call_cell); return; error_01: pkg_free(contact_esgwri); pkg_free(contact_lro); return; } /* * Functions that the User can call from the config file */ /* * - verify if the request is an emergency call * - is it is an emergency forward the INVITE to the destiny determined by the VPC */ int emergency_call(struct sip_msg *msg) { struct dlg_cell *dlg; // verify if mandatory parameters were configurated in script if ((proxy_role == 0|| proxy_role == 1|| proxy_role == 4) && (strcmp(mandatory_parm, "1") == 0)){ LM_ERR("source_hostname and sorce_nena_id are mandatory\n"); return -1; } // the emergency call treatment start with INVITE if (memcmp(msg->first_line.u.request.method.s,"INVITE", msg->first_line.u.request.method.len) == 0) { LM_DBG(" --- TREAT INVITE ----- \n \n"); if (is_emergency_call(msg)) { LM_DBG(" --- IT IS AN EMERGECY ----- \n \n"); // It is, forward the INVITE if(send_request_vpc(msg) == 1){ if (dlgb.create_dlg(msg,0)<1) { LM_ERR("failed to create dialog\n"); return -1; } dlg = dlgb.get_dlg(); if (dlg==NULL) { LM_CRIT("BUG: found after create dialog\n"); return -1; } if(dlgb.register_dlgcb(dlg, DLGCB_REQ_WITHIN|DLGCB_TERMINATED, indialog_ua ,0,0)!=0) { LM_ERR("failed to register dialog callback\n"); return -1; } return 1; } } }else{ if (memcmp(msg->first_line.u.request.method.s,"NOTIFY", msg->first_line.u.request.method.len) == 0){ if (proxy_role == 4) { LM_DBG(" --- TREAT NOTIFY ----- \n \n"); if ( !treat_notify(msg)){ LM_ERR ("***** ERROR IN NOTIFY TREATMENT \n"); return -1; } return -1; } } if (memcmp(msg->first_line.u.request.method.s,"SUBSCRIBE", msg->first_line.u.request.method.len) == 0){ if (proxy_role == 3) { if ( !treat_subscribe(msg)){ LM_ERR ("***** ERROR IN SUBSCRIBE TREATMENT \n"); return -1; } return -1; } } } return -1; } /* treat the command FAILURE * - treat contingency forwarding in the case of failure of the original * - Forward the INVITE to a gateway with the contingency number lro from the field R-URI */ static int failure(struct sip_msg *msg) { char* callidHeader; ESCT* info_call; char* new_to; char* cbn_aux; str cbn; char* from_tag; struct to_body *pfrom = NULL; struct node* s; unsigned int hash_code; LM_DBG(" --- FAILURE treatment \n \n"); if (proxy_role == 2) { LM_DBG(" ---role: call server scenario II \n"); return -1; } // get callid of the message if ( parse_headers(msg,HDR_EOH_F, 0) == -1 ){ LM_ERR("error in parsing headers\n"); return -1; } if( msg->callid==NULL || msg->callid->body.s==NULL){ LM_ERR("msg without callid header\n"); return -1; } callidHeader = pkg_malloc(sizeof (char) * msg->callid->body.len + 1); if (callidHeader == NULL) { LM_ERR("no more pkg memory\n"); return -1 ; } memset(callidHeader, '\0', msg->callid->body.len + 1); strncpy(callidHeader, msg->callid->body.s, msg->callid->body.len); LM_DBG(" ---FAILURE treatment callid=%s", callidHeader); if (msg->from->parsed == NULL){ if ( parse_from_header( msg )<0 ){ LM_ERR("subscribe without From header\n"); pkg_free(callidHeader); return -1; } } pfrom = get_from(msg); LM_DBG("PFROM_TAG: %.*sxxx \n ", pfrom->tag_value.len, pfrom->tag_value.s ); if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0){ LM_ERR("INVITE without from_tag value \n"); pkg_free(callidHeader); return -1; } from_tag = pkg_malloc(sizeof (char)* pfrom->tag_value.len + 1); if (from_tag == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(from_tag, 0, pfrom->tag_value.len + 1); strncpy(from_tag, pfrom->tag_value.s, pfrom->tag_value.len); hash_code= core_hash(&msg->callid->body, 0, emet_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); // find the cell with the callid from the list calls_cell s= search_ehtable(call_htable, callidHeader, from_tag, hash_code, 0); if (s == NULL) { LM_ERR(" ---FAILURE treatment did not find the CALLID"); goto error; } info_call = s->esct; if (proxy_role == 3) { if (strstr(info_call->disposition, "processes") != NULL) { LM_DBG(" ---role: proxy routing \n"); cbn_aux = pkg_malloc(sizeof (char)* MAX_URI_SIZE); if (cbn_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1 ; } memset(cbn_aux, 0, MAX_URI_SIZE); found_CBN(msg, &cbn_aux); cbn.s = cbn_aux; cbn.len = strlen(cbn.s); LM_DBG(" --- FOUND CBN%.*s \n \n", cbn.len, cbn.s); if(strlen(info_call->esgwri) > 1){ LM_DBG ("FAILURE REPLY ESGWRI %s \n",info_call->esgwri); if(new_uri_proxy(msg, info_call->esgwri) == -1){ LM_ERR(" ERROR IN NEW_URI_PROXY"); pkg_free(cbn_aux); goto lro; } }else{ if ((strlen(info_call->ert_srid) > 1)&&(info_call->ert_resn != 0)&&(info_call->ert_npa != 0)){ LM_DBG ("CONTEUDO FAILURE REPLY SRID %s \n",info_call->ert_srid); LM_DBG ("CONTEUDO FAILURE REPLY RESN %d \n",info_call->ert_resn); LM_DBG ("CONTEUDO FAILURE REPLY NPA %d \n",info_call->ert_npa); if(routing_by_ert( msg, info_call, 1) == -1){ pkg_free(cbn_aux); goto lro; } }else{ pkg_free(cbn_aux); goto lro; } } if(add_headers(info_call->esqk, msg, cbn)==-1){ goto error; } info_call->timeout = ACK_TIME; memcpy(info_call->disposition, "esgwri", strlen("esgwri")); info_call->disposition[strlen("esgwri")] = 0; goto end; } } lro: LM_DBG("treat lro \n"); // verfifica se o parametro contingency_hostname foi definido no script, caso contrario failure não sera tratado if ( contingency_hostname == NULL) { LM_ERR("contingency_hostname not defined\n"); goto error; } // verifica se a chamada tratada teve o numero de contingencia lro fornecido pelo VPC // caso não tenha, não trata failure if (info_call->lro == empty) { LM_ERR(" ---treat FAILURE not found lro"); goto error; } //verify if there was an attempt to forward the INVITE to the contingency number if (strstr(info_call->disposition, "lro") == NULL) { LM_DBG("EH LRO -- LRO = %s HOST = %s ", info_call->lro, contingency_hostname); int tamanho_new_to = strlen(info_call->lro) + strlen(contingency_hostname) + 17; new_to = shm_malloc(sizeof (char)* tamanho_new_to); sprintf(new_to, "sip:%s@%s;user=phone", info_call->lro, contingency_hostname); if((info_call->esgwri)&&(strlen(info_call->esgwri) > 1)) shm_free (info_call->esgwri); info_call->esgwri = new_to; info_call->esgw = empty; info_call->timeout = ACK_TIME; memcpy(info_call->disposition, "lro", strlen("lro")); info_call->disposition[strlen("lro")] = 0; LM_DBG(" ---NEW DESTIN =%s", new_to); if(new_uri_proxy(msg, new_to) == -1){ LM_ERR(" ---ERRO EM NEW_URI_PROXY"); goto error; } }else{ LM_DBG(" ---FAILURE JA TRANSMITIU LRO"); goto error; } end: if(callidHeader) pkg_free(callidHeader); if(from_tag) pkg_free(from_tag); return 1; error : if(callidHeader) pkg_free(callidHeader); if(from_tag) pkg_free(from_tag); return -1; } /* * Internal functions */ /* verify if the call is an emergency call * - verify if the field uri has a urn standard for emergency call defined by RFC 5031 * - if it does not, then verify if se user field is one of the emengency_code in the database * - if it is a code, the module checks if the host is from the opensips or if there is a field Geolocation_routing = 'yes" */ int is_emergency_call(struct sip_msg *msg) { char *request_uri; // verify if the field uri has a urn standard for emergency call CP_STR_CHAR(msg->first_line.u.request.uri, request_uri); LM_DBG(" --- emergency_call %s\n \n", request_uri); if (memcmp(request_uri, "urn:service:sos", 12) == 0){ LM_DBG(" --- IT IS EMERGENCY ----- \n \n"); pkg_free(request_uri); return 1; } else { // don't have URN standard for emergency call, verify USER field in RURI bind with some code in emergency_code LM_DBG(" --- verifying code \n \n"); pkg_free(request_uri); if ((parse_sip_msg_uri(msg) < 0) || (!msg->parsed_uri.user.s) || (msg->parsed_uri.user.len > MAXNUMBERLEN)) { LM_ERR("cannot parse msg URI\n"); return 0; } struct code_number* codigo = codes; while (codigo != NULL) { LM_DBG(" --- verify CODE %.*s\n \n", codigo->code.len, codigo->code.s); LM_DBG(" --- verify CODE USER %.*s\n \n", msg->parsed_uri.user.len, msg->parsed_uri.user.s); LM_DBG(" --- verify CODE CODE SIZE %d\n \n", codigo->code.len); LM_DBG(" --- verify CODE USER SIZE %d\n \n", msg->parsed_uri.user.len); if (codigo->code.len == msg->parsed_uri.user.len){ if (strncmp(codigo->code.s, msg->parsed_uri.user.s , codigo->code.len) == 0) { LM_DBG(" ---> CODIGO -- OK %.*s\n\n", codigo->code.len, codigo->code.s); if (check_myself(msg)) { LM_DBG(" --- IT IS ONWER HOST \n \n"); return 1; } else { // Host isn't same of opensips, Geolocation_Routing determine if routing the INVITE (RFC 6442) int ret = check_geolocation_header(msg); return ret; } } } codigo = codigo->next; } LM_DBG(" --- IT IS NOT EMERGENCY \n \n"); return 0; } LM_DBG(" --- IT IS NOT EMERGENCY \n \n"); return 0; error: return 0; } /* treatment of an emergency call * - verify the opensips configuration: * - 0 : Call Server from scenario I or Routing Proxy scenario II * - 1 : Call Server from scenario II * - 2 : callserver from scenario III * - 3 : Redirect proxy no cenario III * - checks if the parameters to emergency call treatment were configured * - retreives the location from the INVITE * - includes the ersResponse in a node of the list calls_eme * - source * - vpc * - esgw * - esqk * - callid * - ert_srid * - ert_resn * - ert_npa * - datetimestamp * - lro * - disposition * - result * - timeout * - extracts CBN from INVITE * - */ int send_request_vpc(struct sip_msg *msg) { char* xml; char* pidf_body = NULL; char* response; char* locationHeader; char* callidHeader; PARSED *parsed=NULL; int resp_post =1; char* lie; str cbn; char *cbn_aux; struct to_body *pfrom = NULL; char *from_tag; int resp =1; cbn_aux = pkg_malloc(sizeof (char)* MAX_URI_SIZE); if (cbn_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(cbn_aux, 0, MAX_URI_SIZE); if (found_CBN(msg, &cbn_aux) == -1) return -1; cbn.s = cbn_aux; cbn.len = strlen(cbn.s); LM_DBG(" --- FOUND CBN%.*s \n \n", cbn.len, cbn.s); if (proxy_role == 2) { LM_DBG(" ---role: call server scenario II \n"); if (add_hdr_PAI(msg, cbn) == -1) { LM_ERR("FAILURE IN ADD PAI"); } if (proxy_request(msg,call_server_hostname) == -1) { LM_ERR("ERROR IN ROUTING EMERGENCY REQUEST"); return -1; } return 1; } if (proxy_role == 3) { // Call Server SCENARIO III LM_DBG(" ---role: proxy redirect \n"); //if (add_hdr_PAI(msg, cbn) == -1) { // LM_ERR("FAILURE IN ADD PAI"); //} pkg_free(cbn.s); if (proxy_request(msg,call_server_hostname) == -1) { LM_ERR("ERROR IN ROUTING EMERGENCY REQUEST"); return -1; } eme_tm.register_tmcb(msg,NULL,TMCB_RESPONSE_IN,reply_in_redirect,0,0); return 1; } // proxy if (find_body_pidf(msg, &pidf_body) == -1) { LM_ERR("Failed to get pidf body\n"); return -1; } LM_DBG(" --- INIT get_geolocation_header\n \n"); if (get_geolocation_header(msg, &locationHeader) == -1){ LM_ERR("Failed to get geolocation header\n"); return -1; } LM_DBG(" --- INIT get_callid_header\n \n"); if ( parse_headers(msg,HDR_EOH_F, 0) == -1 ){ LM_ERR("error in parsing headers\n"); return -1; } if( msg->callid==NULL || msg->callid->body.s==NULL){ LM_ERR("msg without callid header\n"); return -1; } CP_STR_CHAR(msg->callid->body, callidHeader); if (msg->from->parsed == NULL){ if ( parse_from_header( msg )<0 ){ LM_ERR("subscribe without From header\n"); return -1; } } pfrom = get_from(msg); LM_DBG("PFROM_TAG: %.*sxxx \n ", pfrom->tag_value.len, pfrom->tag_value.s ); if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0){ LM_ERR("INVITE without from_tag value \n"); return -1; } CP_STR_CHAR(pfrom->tag_value, from_tag); if(pidf_body && strlen(pidf_body)>1) { if(locationHeader && strlen(locationHeader)>1){ int size_lie = strlen(pidf_body) + strlen(locationHeader) + 2; lie = pkg_malloc(sizeof (char)* size_lie); memset(lie, 0, size_lie); sprintf(lie, "%s %s", locationHeader, pidf_body); pkg_free(pidf_body); pkg_free(locationHeader); }else{ lie = pidf_body; } } else{ if(locationHeader && strlen(locationHeader)>1){ lie = locationHeader; }else{ LM_ERR("INVITE whithout location information\n"); return -1; } } xml = formatted_xml(msg, lie, callidHeader, cbn.s); if(xml == NULL){ LM_ERR(" --- PROBLEM IN FORMATTED XML \n \n"); resp = -1; goto end; } // HTTP POST to VPC resp_post = post(url_vpc, xml, &response); pkg_free(xml); if (resp_post == -1) { LM_ERR(" --- PROBLEM IN POST \n \n"); resp = -1; goto end; } parsed = parse_xml(response); pkg_free(response); if (parsed != NULL) {; if(create_call_cell(parsed, msg, callidHeader, cbn, from_tag) == -1){ resp = -1; goto end; } } else { LM_ERR("PARSER ERROR\n"); resp = -1; goto end; } LM_DBG("END EMERGENCY"); resp = 1; end: if(callidHeader) pkg_free(callidHeader); if(from_tag) pkg_free(from_tag); if(lie) pkg_free(lie); return resp; error : return -1; } /* handle data receved in esrResponse * - verify if the message has mandatory fields: * - callid * - result * - vpc_nenaid * - vpc_contact * - put parsed data in calls_eme truct. * - insert calls_eme in call_htable hash with key source ip address */ int create_call_cell(PARSED *parsed,struct sip_msg* msg, char* callidHeader, str cbn, char* from_tag) { unsigned int hash_code; LM_DBG(" ---PARSED "); if ((parsed->callid == empty || parsed->result == empty || parsed->vpc->nenaid == empty || parsed->vpc->contact == empty)) { LM_ERR("MANDATORY FIELDS ARE BLANK \n"); free_parsed(parsed); pkg_free(cbn.s); return -1; } else { // check if the callid send in esrRequest is the same of esrResponse if (strcmp(parsed->callid, callidHeader) != 0) { LM_ERR("CALLID DIFFER %s ## %s \n", parsed->callid, callidHeader); free_parsed(parsed); pkg_free(cbn.s); return -1; } LM_DBG(" --- PARSE OK MANDATORY FIELDS \n \n"); call_cell = pkg_malloc(sizeof (ESCT)); if (call_cell == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } call_cell->vpc = pkg_malloc(sizeof (NENA)); if (call_cell->vpc == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } call_cell->source = pkg_malloc(sizeof (NENA)); if (call_cell->source == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } call_cell->eme_dlg_id = pkg_malloc(sizeof (struct dialog_set)); if (call_cell->eme_dlg_id == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } call_cell->eme_dlg_id->local_tag = pkg_malloc(sizeof (char)*strlen(from_tag)+1); if (call_cell->eme_dlg_id->local_tag == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } strcpy(call_cell->eme_dlg_id->local_tag, from_tag); call_cell->eme_dlg_id->call_id = pkg_malloc(sizeof (char)*strlen(callidHeader)+1); if (call_cell->eme_dlg_id->call_id == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } strcpy(call_cell->eme_dlg_id->call_id , callidHeader); call_cell->eme_dlg_id->rem_tag = ""; LM_DBG("PFROM_TAGII: %s \n ", call_cell->eme_dlg_id->local_tag ); LM_DBG("CALL_IDII: %s \n ", call_cell->eme_dlg_id->call_id ); // get parsed data extract from esrResponse and save in calls_eme struct if(treat_parse_esrResponse(msg, call_cell , parsed, proxy_role) == -1){ return -1; } // treat INVITE routing if (treat_routing(msg, call_cell, callidHeader, cbn) == -1){ return -1; } // insert calls_eme in call_htable hash with key source ip address hash_code= core_hash(&msg->callid->body, 0, emet_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); if(insert_ehtable(call_htable, hash_code,call_cell)< 0){ LM_ERR("inserting new record in subs_htable\n"); } free_call_cell(call_cell); return 1; } } /* treats INVITE routing * - checks the result field to verify if the msg from VPC was seuccessfull * - checks the esgwri code or the data from the emergency area (selectiveRoutingID, routingESN, npa) to translate to esgwri */ int treat_routing(struct sip_msg* msg, struct esct *call_cell, char* callidHeader, str cbn) { static str msg300={"Multiple Choices",sizeof("Multiple Choices")-1}; int result = atoi(call_cell->result); int range = range_result(result); LM_DBG(" --- range %d", range); if (range == 1) { // result NOK without contigency number LM_ERR("INVALID RESULT -- EMERGENCY EXIT%d \n", result); goto error; } // opensips with call server role in scenario I or with routing proxy hole in scenario II if ((proxy_role == 0) || (proxy_role == 1)){ if (range == 2) { // result NOK but the VPC send contingency number to routing the call LM_ERR("INVALID RESULT --CONTINGENCY \n"); if(contingency(msg, call_cell) == -1) goto error; call_cell->ert_npa = 0; call_cell->ert_resn = 0; call_cell->ert_srid = ""; pkg_free(cbn.s); return 1; } // result OK call_cell->disposition = "esgwri"; call_cell->timeout = ACK_TIME; if (call_cell->esgwri != empty && strlen(call_cell->esgwri) > 0) { // VPC send esgwri to routing INVITE if (call_cell->esqk == empty){ LM_ERR(" ---Result 200 but without esqk \n"); goto contingency; } if(new_uri_proxy(msg,call_cell->esgwri) == -1){ LM_ERR(" ---ERROR IN NEW_URI_PROXY"); goto error; } } else { LM_DBG("ert_srid %s \n", call_cell->ert_srid); LM_DBG("ert_resn %d \n", call_cell->ert_resn); if ((call_cell->ert_srid != empty) && (call_cell->ert_resn != 0) && (call_cell->ert_npa != 0)) { if (call_cell->esqk == empty){ LM_ERR(" ---Result 200 but without esqk \n"); goto contingency; } if(routing_by_ert( msg, call_cell, 0) == -1){ goto contingency; } }else{ // VPC not send routing information LM_ERR(" ---Result 200 but without ert or esgwri \n"); goto contingency; } } if(add_headers(call_cell->esqk, msg, cbn)==-1){ free_call_cell(call_cell); return -1; } }else{ // opensips with redirect server role if (proxy_role == 4){ LM_DBG(" ---TRATA REDIRECT\n \n"); if(add_hdr_rpl(call_cell, msg)==-1) goto error; if(!eme_tm.t_reply(msg,300,&msg300)){ LM_DBG("t_reply (300)\n"); goto error; } call_cell->disposition = "redirect"; call_cell->timeout = BYE_TIME; int expires = EXPIRES_SUBSCRIBE; if( !send_subscriber(msg, callidHeader, expires)) goto error; pkg_free(cbn.s); }else{ LM_ERR("proxy_role invalid\n"); goto error; } } return 1; contingency: if(contingency(msg, call_cell) == -1) goto error; pkg_free(cbn.s); return 1; error: pkg_free(cbn.s); free_call_cell(call_cell); return -1; } /* * this function is responsible for getting the forwarding data to the INVITE from tha stucture given by the VPC * Stores : * - selectiveRoutingID * - routingESN * - npa * - retreives the esgwri based on the data * - forward the invite */ int routing_by_ert( struct sip_msg *msg, ESCT *call_cell, int failure) { char *esgwri_db; int size_esgwri = 0; if (emergency_routing(call_cell->ert_srid, call_cell->ert_resn, call_cell->ert_npa, &esgwri_db, ref_lock) != -1) { LM_DBG("DB_ESGWRI %s \n", esgwri_db); if (failure == 1){ shm_free(call_cell->esgwri); size_esgwri = strlen(esgwri_db); call_cell->esgwri= (char*)shm_malloc(size_esgwri + 1); if(call_cell->esgwri== NULL){ LM_ERR("no more pkg memory\n"); return -1; } call_cell->esgwri[size_esgwri] = 0; memcpy(call_cell->esgwri, esgwri_db, size_esgwri); pkg_free(esgwri_db); }else{ call_cell->esgwri = esgwri_db; char *r = strstr(call_cell->esgwri, "@"); r++; int tam_esgw = call_cell->esgwri + strlen(call_cell->esgwri) - r; call_cell->esgw = pkg_malloc(sizeof (char)*tam_esgw + 1); if (call_cell->esgw == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memcpy(call_cell->esgw, r, tam_esgw); call_cell->esgw[tam_esgw] = 0; LM_DBG(" --- ESGW:%s \n", call_cell->esgw); } if(new_uri_proxy(msg, call_cell->esgwri) == -1){ LM_ERR(" ---ERROR IN NEW_URI_PROXY"); return -1; } } else { LM_ERR("NOT FOUND ERT IN DB\n"); return -1; } return 1; } /* * this function treats the forwarding of the message in the case when the VPC returns esrResponse "NOT OK" but with the field LRO not blanck * -Forward the INVITE in a contingency gateway with the altenative numbe lro in the user field of the R-URI */ int contingency(struct sip_msg *msg, ESCT *call_cell){ char *lro; //Treat LRO //checks if the LRO field was forwarded by VPC, otherwise the called will have NOK treatment lro = call_cell-> lro; if (lro == empty) { LM_ERR("no received lro\n"); return -1; } int len_lro = strlen(lro); //checks if contingency_hostname parameter was defined in config script, otherwise the called will have NOK treatment if ( contingency_hostname == NULL) { LM_ERR("contingency_hostname not defined\n"); return -1; } // set R-URI to foward INVITE considering contingency_hostname parameter // and LRO provided by VPC = sip:lro@contingency_hostname;user=phone int tamanho_new_to = len_lro + strlen(contingency_hostname) + 17; call_cell->esgwri = pkg_malloc(sizeof (char)* tamanho_new_to); sprintf(call_cell->esgwri, "sip:%s@%s;user=phone", lro, contingency_hostname); if(new_uri_proxy(msg, call_cell->esgwri) == -1){ LM_ERR(" ---ERRO EM NEW_URI_PROXY"); return -1; } call_cell->disposition = "lro"; call_cell->esgw = empty; call_cell->timeout = ACK_TIME; return 1; } /* treat dialog resquest */ /* ensures the routing of the dialog requests in downstream direction * to same destination routed to INVITE */ int routing_ack(struct sip_msg *msg) { char* callidHeader; char* from_tag; int resp = 1; ESCT* info_call; struct to_body *pfrom = NULL; struct node* s; unsigned int hash_code; LM_DBG(" --- START TREATMENT ACK \n \n"); if (proxy_role == 2) { // Call Server scenario II if (proxy_request(msg,call_server_hostname) == -1) { LM_DBG("ERROR IN ROUTING EMERGENCY REQUEST \n"); return -1; } return -1; } if (proxy_role == 4) { // Redirect Proxy scenario III LM_DBG(" ---role: proxy redirect \n"); return -1; } if ( parse_headers(msg,HDR_EOH_F, 0) == -1 ){ LM_ERR("error in parsing headers\n"); return -1; } if( msg->callid==NULL || msg->callid->body.s==NULL){ LM_ERR("msg without callid header\n"); return -1; } callidHeader = pkg_malloc(sizeof (char) * msg->callid->body.len + 1); if (callidHeader == NULL) { LM_ERR("no more pkg memory\n"); return -1 ; } memset(callidHeader, '\0', msg->callid->body.len + 1); strncpy(callidHeader, msg->callid->body.s, msg->callid->body.len); if (msg->from->parsed == NULL){ if ( parse_from_header( msg )<0 ){ LM_ERR("subscribe without From header\n"); return -1; } } pfrom = get_from(msg); LM_DBG("PFROM_TAG: %.*sxxx \n ", pfrom->tag_value.len, pfrom->tag_value.s ); if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0){ LM_ERR("INVITE without from_tag value \n"); return -1; } from_tag = pkg_malloc(sizeof (char)* pfrom->tag_value.len + 1); if (from_tag == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(from_tag, 0, pfrom->tag_value.len + 1); strncpy(from_tag, pfrom->tag_value.s, pfrom->tag_value.len); LM_DBG("PFROM_TAGIII: %s \n ", from_tag ); hash_code= core_hash(&msg->callid->body, 0, emet_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); LM_DBG(" ---TREATMENT ACK callid=%s \n", callidHeader); s= search_ehtable(call_htable, callidHeader, from_tag, hash_code, 0); if (s == NULL) { LM_DBG(" ---TREATMENT ACK - NOT FIND CALLID \n"); resp = -1; goto end; } info_call = s->esct; if (strlen(info_call->esgwri) > 0) { LM_DBG(" ---Routing ACK %s \n\n", info_call->esgwri); if(new_uri_proxy(msg, info_call->esgwri) == -1){ LM_ERR(" ---ERROR IN NEW_URI_PROXY"); resp = -1; goto end; } } info_call->timeout = BYE_TIME; resp = 1; end : if(callidHeader) pkg_free(callidHeader); if(from_tag) pkg_free(from_tag); return resp; } /* Treat BYE */ /* treat BYE received in emergency calls * - ensure de routing of bye belong dialog received in downstream direction * to same destination routed to INVITE * - signals the VPC the call termination * - free call cell in list linked calls_eme */ int bye(struct sip_msg *msg, int dir) { char* callidHeader; int resp = 1; char* response; char* esct_callid; time_t rawtime; struct tm * timeinfo; NODE* info_call; char* xml; struct sm_subscriber* cell_notif; int time_now; char* from_tag; struct to_body *pfrom = NULL, *pto= NULL; unsigned int hash_code; LM_DBG(" --- BYE \n \n"); time(&rawtime); timeinfo = localtime(&rawtime); time_now = (int)rawtime; if (proxy_role == 2) { // Call Server scenario II if (dir == 1) { LM_DBG(" ---role: proxy routing \n"); if (proxy_request(msg,call_server_hostname) == -1) { LM_ERR("ERROR IN ROUTING EMERGENCY REQUEST"); return -1; } return 1; } return -1; } if (proxy_role == 4) { // Redirect Proxy scenario III LM_DBG(" ---role: proxy redirect \n"); return -1; } // get callid from BYE and put callidHeader var if ( parse_headers(msg,HDR_EOH_F, 0) == -1 ){ LM_ERR("error in parsing headers\n"); return -1; } if( msg->callid==NULL || msg->callid->body.s==NULL){ LM_ERR("msg without callid header\n"); return -1; } callidHeader = pkg_malloc(sizeof (char) * msg->callid->body.len + 1); if (callidHeader == NULL) { LM_ERR("no more pkg memory\n"); return -1 ; } memset(callidHeader, '\0', msg->callid->body.len + 1); strncpy(callidHeader, msg->callid->body.s, msg->callid->body.len); if (proxy_role == 3) { // Redirect proxy scenario III LM_DBG(" ---role: proxy redirect \n"); cell_notif = get_subs_cell(msg, msg->callid->body); if (cell_notif != NULL){ cell_notif->call_dlg_id->status = TERMINATED; cell_notif->timeout = TIMER_N + time_now; send_notifier_within(msg, cell_notif); } } if (dir == 1) { //downstream direction // use from_tag and callid for dialog search if (msg->from->parsed == NULL){ if ( parse_from_header( msg )<0 ){ LM_ERR("subscribe without From header\n"); if(callidHeader) pkg_free(callidHeader); return -1; } } pfrom = get_from(msg); LM_DBG("PFROM_TAG: %.*sxxx \n ", pfrom->tag_value.len, pfrom->tag_value.s ); if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0){ LM_ERR("INVITE without from_tag value \n"); if(callidHeader) pkg_free(callidHeader); return -1; } from_tag = pkg_malloc(sizeof (char)* pfrom->tag_value.len + 1); if (from_tag == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(from_tag, 0, pfrom->tag_value.len + 1); strncpy(from_tag, pfrom->tag_value.s, pfrom->tag_value.len); }else{ // upstream direction // use to_tag and callid for dialog search pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); if(callidHeader) pkg_free(callidHeader); return -1; } if( pto->tag_value.s ==NULL || pto->tag_value.len == 0){ LM_ERR("BYE without tag value \n"); if(callidHeader) pkg_free(callidHeader); return -1; } LM_DBG("PTO: %.*s \n ", pto->uri.len, pto->uri.s ); LM_DBG("PTO_TAG: %.*s \n ", pto->tag_value.len, pto->tag_value.s ); from_tag = pkg_malloc(sizeof (char)* pto->tag_value.len + 1); if (from_tag == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(from_tag, 0, pto->tag_value.len + 1); strncpy(from_tag, pto->tag_value.s, pto->tag_value.len); } hash_code= core_hash(&msg->callid->body, 0, emet_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); // search call hash with hash_code, callidHeader and from/to_tag params LM_DBG(" --- BYE callid=%s \n", callidHeader); info_call= search_ehtable(call_htable, callidHeader, from_tag, hash_code, 1); // report call datas in emergency_table_report if (info_call == NULL) { LM_ERR(" --- BYE DID NOT FIND CALLID \n"); resp = -1; goto end; }else{ if (collect_data(info_call, db_url, *db_table) == 1) { LM_DBG("****** REPORT OK\n"); } else { LM_DBG("****** REPORT NOK\n"); } } if (dir == 1) { // downstream direction // routing BYE to same direction that INVITE if (strlen(info_call->esct->esgwri) > 0) { LM_DBG(" ---Routing BYE %s \n\n", info_call->esct->esgwri); if(new_uri_proxy(msg, info_call->esct->esgwri) == -1){ LM_ERR(" ---ERROR IN NEW_URI_PROXY"); shm_free(info_call->esct->esgwri); shm_free(info_call); resp = -1; goto end; } } } // sends ESCT to VPC signalling end call if ((proxy_role == 0) || (proxy_role == 1)){ // send ESCT only if VPC provided key ESQK if (strlen(info_call->esct->esqk) > 0){ LM_DBG(" --- SEND ESQK =%s\n \n",info_call->esct->esqk); strftime(info_call->esct->datetimestamp, MAX_TIME_SIZE, "%Y-%m-%dT%H:%M:%S%Z", timeinfo); xml = buildXmlFromModel(info_call->esct); LM_DBG(" --- TREAT BYE - XML ESCT %s \n \n", xml); // sends HTTP POST esctRequest to VPC resp = post(url_vpc, xml, &response); if (resp == -1) { LM_ERR(" --- PROBLEM IN POST DO BYE\n \n"); shm_free(info_call->esct->esgwri); shm_free(info_call); pkg_free(xml); resp = -1; goto end; } esct_callid = parse_xml_esct(response); if (esct_callid== NULL) { LM_ERR(" --- esctAck invalid format or without mandatory field \n \n"); } else { if (strcmp(esct_callid, callidHeader)){ LM_ERR(" --- callid in esctAck different from asctRequest \n \n"); } if(esct_callid) pkg_free(esct_callid); } pkg_free(response); pkg_free(xml); } } shm_free(info_call->esct->esgwri); shm_free(info_call); resp = 1; end : if(callidHeader) pkg_free(callidHeader); if(from_tag) pkg_free(from_tag); return resp; } /* * Aux functions */ #define SUCCESS_OR_EXIT(_f) \ do {\ resp = fill_parm_with_BS(&(_f)); \ if (resp < 0) { \ LM_ERR("out of pkg mem\n"); \ return -1; \ } \ } while(0) /* fill with blanck spaces */ int fill_blank_space(void) { int resp = 1; SUCCESS_OR_EXIT(vpc_organization_name); SUCCESS_OR_EXIT(vpc_hostname); SUCCESS_OR_EXIT(vpc_nena_id); SUCCESS_OR_EXIT(vpc_contact); SUCCESS_OR_EXIT(vpc_cert_uri); SUCCESS_OR_EXIT(source_organization_name); SUCCESS_OR_EXIT(source_nena_id); SUCCESS_OR_EXIT(source_cert_uri); SUCCESS_OR_EXIT(vsp_organization_name); if (proxy_role == 0) { SUCCESS_OR_EXIT(vsp_hostname); SUCCESS_OR_EXIT(vsp_nena_id); } SUCCESS_OR_EXIT(vsp_contact); SUCCESS_OR_EXIT(vsp_cert_uri); return resp; } #undef SUCCESS_OR_EXIT /*fill with blanck spaces */ int fill_parm_with_BS(char** var) { if (*var == NULL) { *var = pkg_malloc(sizeof (char) * strlen(BLANK_SPACE)); if (*var == NULL) return -1; strcpy(*var, BLANK_SPACE); return 1; } return 1; } /* verify if the ruri is from the same opensips */ int check_myself(struct sip_msg *msg) { int ret = 0; if ((parse_sip_msg_uri(msg) < 0) || (!msg->parsed_uri.user.s) || (msg->parsed_uri.user.len > MAXNUMBERLEN)) { LM_ERR("cannot parse msg URI\n"); return 0; } LM_DBG(" --- opensips host %.*s \n \n", msg->parsed_uri.host.len, msg->parsed_uri.host.s); ret = check_self_op(EQUAL_OP, &msg->parsed_uri.host, 0); return ret; } /* calculate the size of the xml to allocate memory */ unsigned long get_xml_size(char* lie, char* formated_time, char* callidHeader, char* cbn, char* call_origin) { unsigned long resp = 0; resp += strlen(MODEL); resp += strlen(lie); resp += strlen(callidHeader); resp += strlen(cbn); resp += strlen(formated_time); resp += strlen(vpc_organization_name); resp += strlen(vpc_hostname) + strlen(vpc_nena_id); resp += strlen(vpc_contact) + strlen(vpc_cert_uri); resp += strlen(source_organization_name); resp += strlen(source_hostname) + strlen(source_nena_id); resp += strlen(source_contact) + strlen(source_cert_uri); resp += strlen(vsp_organization_name); resp += strlen(vsp_hostname) + strlen(vsp_nena_id); resp += strlen(vsp_contact) + strlen(vsp_cert_uri); resp += strlen(call_origin); return resp; } /* format the xml to send POST -> esrRequest */ char* formatted_xml(struct sip_msg *msg, char* lie, char* callidHeader, char* cbn) { char* xml; char formated_time[80]; time_t rawtime; struct tm * timeinfo; struct service_provider* source_provider; struct service_provider* vpc_provider; struct service_provider* vsp_provider; source_organization_name = empty; source_hostname = empty; source_nena_id = empty; source_contact = empty; source_cert_uri = empty; vpc_organization_name = empty; vpc_hostname = empty; vpc_nena_id = empty; vpc_contact = empty; vpc_cert_uri = empty; vsp_organization_name = empty; vsp_hostname = empty; vsp_nena_id = empty; vsp_contact = empty; vsp_cert_uri = empty; time(&rawtime); timeinfo = localtime(&rawtime); strftime(formated_time, 80, "%Y-%m-%dT%H:%M:%S%Z", timeinfo); LM_DBG(" --- INIT send_request_vpc\n \n"); LM_DBG(" --- FORMAT XML \n \n"); source_provider = get_provider(msg, 0, ref_lock); if (source_provider != NULL){ CP_STR_CHAR(source_provider->OrganizationName, source_organization_name); CP_STR_CHAR(source_provider->hostId, source_hostname); CP_STR_CHAR(source_provider->nenaId, source_nena_id); CP_STR_CHAR(source_provider->contact, source_contact); CP_STR_CHAR(source_provider->certUri, source_cert_uri); } vpc_provider = get_provider(msg, 1, ref_lock); if (vpc_provider != NULL){ CP_STR_CHAR(vpc_provider->OrganizationName, vpc_organization_name); CP_STR_CHAR(vpc_provider->hostId, vpc_hostname); CP_STR_CHAR(vpc_provider->nenaId, vpc_nena_id); CP_STR_CHAR(vpc_provider->contact, vpc_contact); CP_STR_CHAR(vpc_provider->certUri, vpc_cert_uri); } vsp_provider = get_provider(msg, 2, ref_lock); if (vsp_provider != NULL){ CP_STR_CHAR(vsp_provider->OrganizationName, vsp_organization_name); CP_STR_CHAR(vsp_provider->hostId, vsp_hostname); CP_STR_CHAR(vsp_provider->nenaId, vsp_nena_id); CP_STR_CHAR(vsp_provider->contact, vsp_contact); CP_STR_CHAR(vsp_provider->certUri, vsp_cert_uri); } if (proxy_role == 1 && ((strlen(vsp_hostname) == 0) || (strlen(vsp_nena_id) == 0))){ LM_ERR("vsp_hostname and vsp_nena_id are mandatory when opensips role as routing proxy in scenario II\n"); return NULL; } int size_xml = get_xml_size(lie, formated_time, callidHeader, cbn, call_origin) + 1; LM_DBG(" --- LEN XML %d \n \n", size_xml); xml = pkg_malloc(sizeof (char) * size_xml); memset(xml, 0, size_xml); sprintf(xml, MODEL,\ vpc_organization_name, vpc_hostname, vpc_nena_id, vpc_contact, vpc_cert_uri, \ source_organization_name, source_hostname, source_nena_id, source_contact, source_cert_uri, \ vsp_organization_name, vsp_hostname, vsp_nena_id, vsp_contact, vsp_cert_uri,\ callidHeader, cbn, lie,\ call_origin, formated_time); LM_DBG(" --- INIT xml %s\n \n", xml); FREE_BUF(vpc_organization_name); FREE_BUF(vpc_hostname); FREE_BUF(vpc_nena_id); FREE_BUF(vpc_contact); FREE_BUF(vpc_cert_uri); FREE_BUF(source_organization_name); FREE_BUF(source_hostname); FREE_BUF(source_nena_id); FREE_BUF(source_contact); FREE_BUF(source_cert_uri); FREE_BUF(vsp_organization_name); FREE_BUF(vsp_hostname); FREE_BUF(vsp_nena_id); FREE_BUF(vsp_contact); FREE_BUF(vsp_cert_uri); return xml; error: return NULL; } opensips-2.2.2/modules/emergency/emergency_methods.h000066400000000000000000000120261300170765700226640ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "../../socket_info.h" #include "../../route_struct.h" #include "../../route.c" #include "../rr/api.h" #include "../../ip_addr.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_pai.h" #include "../../parser/parse_ppi.h" #include "../../parser/parse_rpid.h" #include "../../parser/parse_from.h" #include "../../regexp.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "../../rw_locking.h" #include "../../timer.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../forward.h" #include "../../proxy.h" #include "../dialog/dlg_load.h" #include "model.h" #include "notifier_emergency.h" //str str_source; //char *char_dest; #define FREE_BUF(buf)\ if(buf != empty)\ pkg_free(buf); #define MAXNUMBERLEN 31 #define HTTP_HDR_CONTENT_TYPE "Content-Type" #define CONTENT_TYPE_HDR_LEN 12 #define MAX_CONTENT_TYPE_LEN 64 #define CODE_DELIM '-' #define PATTERN_TEL "tel:([+]*[-0-9]+)" #define PATTERN_TEL_LEN (sizeof(PATTERN_TEL)-1) #define ACK_TIME 3 #define BYE_TIME 10 #define EXPIRES_SUBSCRIBE 300 const char *BLANK_SPACE = " "; struct code_number { str code; str description; struct code_number *next; }; /* static char err_buff[CURL_ERROR_SIZE]; static char print_buff[MAX_CONTENT_TYPE_LEN]; */ size_t write_func(char *ptr, size_t size, size_t nmemb, void *userdata); size_t header_func(char *ptr, size_t size, size_t nmemb, void *userdata); /* * Module parameters */ char *emergency_codes; char *vsp_organization_name; char *vsp_hostname; char *vsp_nena_id; char *vsp_contact; char *vsp_cert_uri; char *source_organization_name; char *source_hostname; char *source_nena_id; char *source_contact; char *source_cert_uri; char *vpc_organization_name; char *vpc_hostname; char *vpc_nena_id; char *vpc_contact; char *vpc_cert_uri; char *contingency_hostname; char *call_origin = NULL; char *call_server_hostname = NULL; int proxy_role = 0; int emetable_size = 9; int substable_size = 9; struct code_number *codes = NULL; //struct node **calls_eme = NULL; struct multi_body *mbody; struct esct *call_cell; struct lump *l; int timer_interval=10; str table_name=str_init("emergency_routing"); str table_report=str_init("emergency_report"); str table_provider=str_init("emergency_service_provider"); static rw_lock_t *ref_lock = NULL; str callid_invite; char* inicialized; /* * Function headers */ static int is_emergency_call(struct sip_msg *msg); static int send_request_vpc(struct sip_msg *msg); static int routing_ack(struct sip_msg *msg); static int bye(struct sip_msg *msg, int dir); static int emergency_call(struct sip_msg *msg); static int failure(struct sip_msg *msg); static int set_codes(unsigned int type, void *val); static void libera_esqk(void); static void free_subs(void); void routing_timer(unsigned int ticks,void *attr); int check_myself(struct sip_msg *msg); int contingency(struct sip_msg *msg, ESCT *call_cell); int fill_blank_space(void); int fill_parm_with_BS(char** var); unsigned long get_xml_size(char* lie, char* formated_time, char* callidHeader, char* cbn, char* call_origin); char* formatted_xml(struct sip_msg *msg, char* lie, char* callidHeader, char* cbn); int routing_by_ert( struct sip_msg *msg, ESCT *call_cell, int failure); int treat_routing(struct sip_msg* msg, struct esct *call_cell, char* callidHeader, str cbn); int create_call_cell(PARSED *parsed,struct sip_msg* msg, char* callidHeader, str cbn, char* from_tag); void destroy_codes(struct code_number *codes); opensips-2.2.2/modules/emergency/hash.c000066400000000000000000000347211300170765700201070ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../str.h" #include "hash.h" emetable_t new_ehtable(int hash_size){ emetable_t htable= NULL; int i, j; i = 0; htable= (call_table_t*)shm_malloc(hash_size* sizeof(call_table_t)); if(htable== NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); } memset(htable, 0, hash_size* sizeof(call_table_t)); for(i= 0; i< hash_size; i++) { if(lock_init(&htable[i].lock)== 0) { LM_ERR("initializing lock [%d]\n", i); goto error; } htable[i].entries= (NODE*)shm_malloc(sizeof(NODE)); if(htable[i].entries== NULL) { lock_destroy(&htable[i].lock); LM_ERR("--------------------------------------------------no more shm memory\n"); } memset(htable[i].entries, 0, sizeof(NODE)); htable[i].entries->next= NULL; } return htable; error: if(htable){ for(j=0; j< i; j++){ lock_destroy(&htable[j].lock); shm_free(htable[j].entries); } shm_free(htable); } return NULL; } sbtable_t new_shtable(int hash_size){ sbtable_t htable= NULL; int i, j; i = 0; htable= (subs_table_t*)shm_malloc(hash_size* sizeof(subs_table_t)); if(htable== NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); } memset(htable, 0, hash_size* sizeof(subs_table_t)); for(i= 0; i< hash_size; i++) { if(lock_init(&htable[i].lock)== 0) { LM_ERR("initializing lock [%d]\n", i); goto error; } htable[i].entries= (struct sm_subscriber*)shm_malloc(sizeof(struct sm_subscriber)); if(htable[i].entries== NULL) { lock_destroy(&htable[i].lock); LM_ERR("--------------------------------------------------no more shm memory\n"); } memset(htable[i].entries, 0, sizeof(struct sm_subscriber)); htable[i].entries->next= NULL; } return htable; error: if(htable){ for(j=0; j< i; j++){ lock_destroy(&htable[j].lock); shm_free(htable[j].entries); } shm_free(htable); } return NULL; } void destroy_ehtable(emetable_t htable, int hash_size){ int i; if(htable== NULL) return; for(i= 0; i< hash_size; i++) { lock_destroy(&htable[i].lock); free_call_list(htable[i].entries->next); shm_free(htable[i].entries); } shm_free(htable); htable= NULL; } void destroy_shtable(sbtable_t htable, int hash_size){ int i; if(htable== NULL) return; for(i= 0; i< hash_size; i++) { lock_destroy(&htable[i].lock); free_subs_list(htable[i].entries->next); shm_free(htable[i].entries); } shm_free(htable); htable= NULL; } void free_call_list(NODE* s_array){ NODE* s; while(s_array){ s= s_array; s_array= s_array->next; shm_free(s); } } void free_subs_list(struct sm_subscriber* s_array){ struct sm_subscriber* s; while(s_array){ s= s_array; s_array= s_array->next; shm_free(s); } } int insert_ehtable(emetable_t htable, unsigned int hash_code, ESCT* call_eme){ NODE* new_rec= NULL; new_rec= mem_copy_call_noc(call_eme); if(new_rec== NULL){ LM_ERR("copying in share memory a NODE structure\n"); goto error; } lock_get(&htable[hash_code].lock); new_rec->next= htable[hash_code].entries->next; htable[hash_code].entries->next= new_rec; LM_DBG("******************************END ENTRADA DO HASH %p\n",(void*)new_rec); lock_release(&htable[hash_code].lock); return 0; error: if(new_rec) shm_free(new_rec); return -1; } NODE* mem_copy_call_noc(ESCT* s){ int size; NODE* dest = NULL; NODE* dest_atr; int size_esgwri; int size_esgw; int size_esqk; int size_callid; int size_ert_srid; //int size_datetimestamp; int size_lro; //int size_disposition; int size_result; int size_source_organizationname; int size_source_hostname; int size_source_nenaid; int size_source_contact; int size_source_certuri; int size_vpc_organizationname; int size_vpc_hostname; int size_vpc_nenaid; int size_vpc_contact; int size_vpc_certuri; int size_call_id; int size_local_tag; int size_rem_tag; char *p; size_esgwri = s->esgwri? strlen(s->esgwri)+1:1; size_esgw = s->esgw?strlen(s->esgw)+1:1; size_esqk = s->esqk? strlen(s->esqk)+1:1; size_callid = s->callid? strlen(s->callid)+1:1; size_ert_srid = s->ert_srid? strlen(s->ert_srid)+1:1; //size_datetimestamp = s->datetimestamp? strlen(s->datetimestamp)+1:1; size_lro = s->lro? strlen(s->lro)+1:1; //size_disposition = s->disposition? strlen(s->disposition)+1:1; size_result = s->result? strlen(s->result)+1:1; size_source_organizationname = s->source->organizationname? strlen(s->source->organizationname)+1:1; size_source_hostname = s->source->hostname? strlen(s->source->hostname)+1:1; size_source_nenaid = s->source->nenaid? strlen(s->source->nenaid)+1:1; size_source_contact = s->source->contact? strlen(s->source->contact)+1:1; size_source_certuri = s->source->certuri? strlen(s->source->certuri)+1:1; size_vpc_organizationname = s->vpc->organizationname? strlen(s->vpc->organizationname)+1:1; size_vpc_hostname = s->vpc->hostname? strlen(s->vpc->hostname)+1:1; size_vpc_nenaid = s->vpc->nenaid? strlen(s->vpc->nenaid)+1:1; size_vpc_contact = s->vpc->contact? strlen(s->vpc->contact)+1:1; size_vpc_certuri = s->vpc->certuri? strlen(s->vpc->certuri)+1:1; size_call_id = s->eme_dlg_id->call_id? strlen(s->eme_dlg_id->call_id)+1:1; size_local_tag = s->eme_dlg_id->local_tag? strlen(s->eme_dlg_id->local_tag)+1:1; size_rem_tag = s->eme_dlg_id->rem_tag? strlen(s->eme_dlg_id->rem_tag)+1:1; size= sizeof(NODE)+ sizeof(ESCT)+ (2 * sizeof(NENA)) + sizeof(struct dialog_set) + size_esgw + size_esqk+ size_callid + size_ert_srid + MAX_TIME_SIZE + size_lro + MAX_DISPOSITION_SIZE + size_result + size_call_id + size_local_tag + size_rem_tag + size_source_organizationname + size_source_hostname + size_source_nenaid + size_source_contact + size_source_certuri + size_vpc_organizationname + size_vpc_hostname + size_vpc_nenaid + size_vpc_contact + size_vpc_certuri; p= (char*)shm_malloc(size); if(p== NULL){ //ERR_MEM(SHARE_MEM); goto error; } memset(p, 0, size); dest = (NODE*)p; p = p + sizeof(NODE); dest->esct = (ESCT*)p; p = p + sizeof(ESCT); dest->esct->eme_dlg_id = (struct dialog_set*)p; size= sizeof(struct dialog_set ); CONT_COPY(dest->esct->eme_dlg_id, dest->esct->eme_dlg_id->call_id, s->eme_dlg_id->call_id); CONT_COPY(dest->esct->eme_dlg_id, dest->esct->eme_dlg_id->local_tag, s->eme_dlg_id->local_tag); CONT_COPY(dest->esct->eme_dlg_id, dest->esct->eme_dlg_id->rem_tag, s->eme_dlg_id->rem_tag); p = p + size; dest->esct->source = (NENA*)p; size= sizeof(NENA); CONT_COPY(dest->esct->source, dest->esct->source->organizationname, s->source->organizationname); CONT_COPY(dest->esct->source, dest->esct->source->hostname, s->source->hostname); CONT_COPY(dest->esct->source, dest->esct->source->nenaid, s->source->nenaid); CONT_COPY(dest->esct->source, dest->esct->source->contact, s->source->contact); CONT_COPY(dest->esct->source, dest->esct->source->certuri, s->source->certuri); p = p + size; dest->esct->vpc = (NENA*)p; size= sizeof(NENA); CONT_COPY(dest->esct->vpc, dest->esct->vpc->organizationname, s->vpc->organizationname); CONT_COPY(dest->esct->vpc, dest->esct->vpc->hostname, s->vpc->hostname); CONT_COPY(dest->esct->vpc, dest->esct->vpc->nenaid, s->vpc->nenaid); CONT_COPY(dest->esct->vpc, dest->esct->vpc->contact, s->vpc->contact); CONT_COPY(dest->esct->vpc, dest->esct->vpc->certuri, s->vpc->certuri); p = p + size; dest_atr = (NODE*)p; size = 0; CONT_COPY(dest_atr, dest->esct->esgw, s->esgw); CONT_COPY(dest_atr, dest->esct->esqk, s->esqk); CONT_COPY(dest_atr, dest->esct->callid, s->callid); CONT_COPY(dest_atr, dest->esct->ert_srid, s->ert_srid); if(s->datetimestamp){ dest->esct->datetimestamp= (char*)dest_atr+ size; memcpy(dest->esct->datetimestamp, s->datetimestamp, strlen(s->datetimestamp)); size+= MAX_TIME_SIZE; } CONT_COPY(dest_atr, dest->esct->lro, s->lro); if(s->disposition){ dest->esct->disposition= (char*)dest_atr+ size; memcpy(dest->esct->disposition, s->disposition, strlen(s->disposition)); size+= MAX_DISPOSITION_SIZE; } CONT_COPY(dest_atr, dest->esct->result, s->result); dest->esct->ert_resn= s->ert_resn; dest->esct->ert_npa= s->ert_npa; dest->esct->timeout= s->timeout; dest->esct->esgwri= (char*)shm_malloc(size_esgwri); if(dest->esct->esgwri== NULL){ //ERR_MEM(SHARE_MEM); goto error; } memset(dest->esct->esgwri, 0, size_esgwri); memcpy(dest->esct->esgwri, s->esgwri, size_esgwri - 1); return dest; error: if(dest) shm_free(dest); return NULL; } struct sm_subscriber* insert_shtable(sbtable_t htable, unsigned int hash_code, struct sm_subscriber* subs){ struct sm_subscriber* new_rec= NULL; new_rec= mem_copy_subs_noc(subs); if(new_rec== NULL){ LM_ERR("copying in share memory a sm_subscriber structure\n"); return NULL; } lock_get(&htable[hash_code].lock); new_rec->next= htable[hash_code].entries->next; htable[hash_code].entries->next= new_rec; lock_release(&htable[hash_code].lock); return new_rec; } struct sm_subscriber* mem_copy_subs_noc(struct sm_subscriber* s){ int size; struct sm_subscriber* dest = NULL; struct sm_subscriber* dest_atr; char *p; size= sizeof(struct sm_subscriber) + (2 * sizeof(struct dialog_id)) + s->loc_uri.len + s->rem_uri.len + s->contact.len + s->event.len + s->call_dlg_id->callid.len + s->call_dlg_id->local_tag.len + s->call_dlg_id->rem_tag.len + s->dlg_id->callid.len + s->dlg_id->local_tag.len + s->dlg_id->rem_tag.len; p= (char*)shm_malloc(size); if(p== NULL){ LM_ERR("no more shm\n"); goto error; } memset(p, 0, size); dest = (struct sm_subscriber*)p; p = p + sizeof(struct sm_subscriber); dest->dlg_id = (struct dialog_id*)p; size= sizeof(struct dialog_id); CONT_COPY_STR(dest->dlg_id, dest->dlg_id->callid, s->dlg_id->callid); CONT_COPY_STR(dest->dlg_id, dest->dlg_id->local_tag, s->dlg_id->local_tag); CONT_COPY_STR(dest->dlg_id, dest->dlg_id->rem_tag, s->dlg_id->rem_tag); p = p + size; dest->call_dlg_id = (struct dialog_id*)p; size= sizeof(struct dialog_id); CONT_COPY_STR(dest->call_dlg_id, dest->call_dlg_id->callid, s->call_dlg_id->callid); CONT_COPY_STR(dest->call_dlg_id, dest->call_dlg_id->local_tag, s->call_dlg_id->local_tag); CONT_COPY_STR(dest->call_dlg_id, dest->call_dlg_id->rem_tag, s->call_dlg_id->rem_tag); p = p + size; dest_atr = (struct sm_subscriber*)p; size = 0; CONT_COPY_STR(dest_atr, dest->loc_uri, s->loc_uri); CONT_COPY_STR(dest_atr, dest->rem_uri, s->rem_uri); CONT_COPY_STR(dest_atr, dest->contact, s->contact); CONT_COPY_STR(dest_atr, dest->event, s->event); dest->expires= s->expires; dest->timeout= s->timeout; dest->version= s->version; return dest; error: if(dest) shm_free(dest); return NULL; } NODE* search_ehtable(emetable_t htable, char* callid, char* from_tag, unsigned int hash_code, int delete){ NODE* s; NODE* ps; int size_callid_t; int size_from_tag_t; int size_callid_m; int size_from_tag_m; ps= htable[hash_code].entries; s= ps->next; if (s == NULL){ LM_DBG("Did not find\n"); return NULL; } size_callid_t = strlen(s->esct->eme_dlg_id->call_id); size_from_tag_t = strlen(s->esct->eme_dlg_id->local_tag); size_callid_m = strlen(callid); size_from_tag_m = strlen(from_tag); LM_DBG(" --------------------CALLID M%s\n",callid); LM_DBG(" --------------------FROM TAG M%s\n",from_tag); LM_DBG(" --------------------CALLID T%s\n",s->esct->eme_dlg_id->call_id); LM_DBG(" --------------------FROM TAG T%s\n",s->esct->eme_dlg_id->local_tag); while(s) { if(size_callid_t == size_callid_m && strncmp(s->esct->eme_dlg_id->call_id, callid, size_callid_m)==0 && size_from_tag_t == size_from_tag_m && strncmp(s->esct->eme_dlg_id->local_tag, from_tag, size_from_tag_m)== 0){ LM_DBG(" --------------------found EHTABLE \n"); if(delete){ lock_get(&htable[hash_code].lock); LM_DBG(" --------------------DELETOU\n"); ps->next = s->next; lock_release(&htable[hash_code].lock); } return s; } ps = s; s= s->next; } LM_DBG("Did not find\n"); return NULL; } struct sm_subscriber* search_shtable(sbtable_t htable, str* callid, str* from_tag, unsigned int hash_code, str* method){ struct sm_subscriber* s; struct sm_subscriber* ps; struct dialog_id* dlg_id; ps= htable[hash_code].entries; LM_DBG(" --------------------END HTABLE ENTRIES %p\n", (void*)ps); s= ps->next; if (s == NULL){ LM_DBG("Did not find\n"); return NULL; } LM_DBG("******************************METODO %.*s\n", method->len, method->s); while(s) { if (memcmp(method->s,"BYE", method->len) == 0) { dlg_id = s->call_dlg_id; }else{ dlg_id = s->dlg_id; } LM_DBG(" --------------------CALLID M%.*s\n", callid->len, callid->s); LM_DBG(" --------------------FROM TAG M%.*s\n", from_tag->len, from_tag->s); LM_DBG(" --------------------CALLID T%.*s\n",dlg_id->callid.len,dlg_id->callid.s); LM_DBG(" --------------------FROM TAG T%.*s\n",dlg_id->rem_tag.len,dlg_id->rem_tag.s); if(dlg_id->callid.len == callid->len && strncmp(dlg_id->callid.s, callid->s, callid->len)==0 && dlg_id->rem_tag.len == from_tag->len && strncmp(dlg_id->rem_tag.s, from_tag->s, from_tag->len)== 0){ LM_DBG(" --------------------found SHTABLE \n"); s->prev = ps; return s; } ps = s; s= s->next; } LM_DBG("Did not find\n"); return NULL; } int delete_shtable(sbtable_t htable, unsigned int hash_code, struct sm_subscriber* subs){ struct sm_subscriber* previous; lock_get(&htable[hash_code].lock); previous = subs->prev; previous->next = subs->next; shm_free(subs); lock_release(&htable[hash_code].lock); return 0; } opensips-2.2.2/modules/emergency/hash.h000066400000000000000000000052161300170765700201110ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include "../../lock_ops.h" #include "xml_parser.h" #define PKG_MEM_STR "pkg" #define SHARE_MEM "share" #define CONT_COPY(buf, dest, source)\ if(source){dest= (char*)buf+ size;\ if(source == empty){\ dest = empty;\ }else{\ memcpy(dest, source, strlen(source));\ }\ size+= strlen(source) + 1;\ } #define CONT_COPY_STR(buf, dest, source)\ do{ dest.s= (char*)buf+ size;\ memcpy(dest.s, source.s, source.len);\ dest.len= source.len;\ size+= source.len;\ } while(0) typedef struct call_htable { NODE* entries; gen_lock_t lock; }call_table_t; typedef call_table_t* emetable_t; typedef struct subs_htable { struct sm_subscriber* entries; gen_lock_t lock; }subs_table_t; typedef subs_table_t* sbtable_t; emetable_t new_ehtable(int hash_size); void destroy_ehtable(emetable_t htable, int hash_size); void free_call_list(NODE* s_array); sbtable_t new_shtable(int hash_size); void destroy_shtable(sbtable_t htable, int hash_size); void free_subs_list(struct sm_subscriber* s_array); NODE* mem_copy_call_noc(ESCT* s); int insert_ehtable(emetable_t htable, unsigned int hash_code, ESCT* call_eme); struct sm_subscriber* mem_copy_subs_noc(struct sm_subscriber* s); struct sm_subscriber* insert_shtable(sbtable_t htable, unsigned int hash_code, struct sm_subscriber* call_eme); NODE* search_ehtable(emetable_t htable, char* callid, char* from_tag, unsigned int hash_code, int delete); struct sm_subscriber* search_shtable(sbtable_t htable, str* callid, str* from_tag, unsigned int hash_code, str* method); int delete_shtable(sbtable_t htable, unsigned int hash_code, struct sm_subscriber* subs); opensips-2.2.2/modules/emergency/http_emergency.c000066400000000000000000000521221300170765700221740ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include "http_emergency.h" /* finish the emergency call frees resources: - pull call cell this call from list linked eme_calls - send esct to VPC to release ESQK Key*/ int send_esct(struct sip_msg *msg, str callid_ori, str from_tag){ char* esct_callid; NODE* info_call; char* xml = NULL; time_t rawtime; struct tm * timeinfo; char* response; int resp; char* callidHeader; char* ftag; unsigned int hash_code; str callid; callidHeader = pkg_malloc(callid_ori.len + 1); if(callidHeader == NULL){ LM_ERR("No memory left\n"); return -1; } memset(callidHeader, 0, callid_ori.len + 1); memcpy(callidHeader, callid_ori.s, callid_ori.len); ftag = pkg_malloc(from_tag.len + 1); if(ftag == NULL){ LM_ERR("No memory left\n"); return -1; } memset(ftag, 0, from_tag.len + 1); memcpy(ftag, from_tag.s, from_tag.len); // extract call cell with same callid from list linked eme_calls LM_DBG(" --- BYE callid=%s \n", callidHeader); callid.s = callidHeader, callid.len = strlen(callidHeader); hash_code= core_hash(&callid, 0, emet_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); info_call= search_ehtable(call_htable, callidHeader, ftag, hash_code, 1); if (info_call == NULL) { LM_ERR(" --- BYE DID NOT FIND CALLID \n"); return -1; }else{ if (collect_data(info_call, db_url, *db_table) == 1) { LM_DBG("****** REPORT OK\n"); } else { LM_DBG("****** REPORT NOK\n"); } } if (strlen(info_call->esct->esqk) > 0){ // if VPC provide ESQK then opensips need send esct to free this key LM_DBG(" --- SEND ESQK =%s\n \n",info_call->esct->esqk); time(&rawtime); timeinfo = localtime(&rawtime); strftime(info_call->esct->datetimestamp, MAX_TIME_SIZE, "%Y-%m-%dT%H:%M:%S%Z", timeinfo); LM_DBG(" --- TREAT BYE - XML ESCT %s \n \n", xml); xml = buildXmlFromModel(info_call->esct); // sends HTTP POST esctRequest to VPC resp = post(url_vpc, xml, &response); if (resp == -1) { LM_ERR(" --- PROBLEM IN POST DO BYE\n \n"); shm_free(info_call); pkg_free(xml); return -1; } // verify if esct response came OK esct_callid = parse_xml_esct(response); if (esct_callid== NULL) { LM_ERR(" --- esctAck invalid format or without mandatory field \n \n"); } else { if (strcmp(esct_callid, callidHeader)){ LM_ERR(" --- callid in esctAck different from asctRequest \n \n"); } if(esct_callid) pkg_free(esct_callid); } pkg_free(response); pkg_free(xml); } shm_free(info_call->esct->esgwri); shm_free(info_call); return 1; } /* verify the result field of the VPC */ int range_result(int result) { // OK if (result >= 200 && result <= 203) return 0; // NOT OK USE THE lro if (result >= 400 && result <= 404) return 2; // response NOK, but with lro field if (result >= 500 && result <= 501) return 2; // response NOK without lro field return 1; } /* * get parsed data extract from esrResponse and save in calls_eme struct: * - source * .organizationname * .hostname * .nenaid * .contact * .certuri * - vpc * .organizationname * .hostname * .nenaid * .contact * .certuri * - esqk * - callid * - lro * - result * - datetimestamp */ int treat_parse_esrResponse(struct sip_msg *msg, ESCT *call_cell, PARSED *parsed, int proxy_role){ char *p; int vsp_addr_len; char *vsp_addr = "@vsp.com"; str pattern_lro, replacement_lro; str pt_lro; char *lro_aux; call_cell->esgwri = empty; call_cell->ert_srid = empty; call_cell->ert_npa = 0; call_cell->ert_resn = 0; call_cell->esgw = empty; call_cell->lro = empty; call_cell->disposition = empty; LM_DBG(" --- TREAT PARSE ESRRESPONSE..."); call_cell->source->organizationname = parsed->destination->organizationname; call_cell->source->hostname = parsed->destination->hostname; call_cell->source->nenaid = parsed->destination->nenaid; call_cell->source->contact = parsed->destination->contact; call_cell->source->certuri = parsed->destination->certuri; call_cell->vpc->organizationname = parsed->vpc->organizationname; call_cell->vpc->hostname = parsed->vpc->hostname; call_cell->vpc->nenaid = parsed->vpc->nenaid; call_cell->vpc->contact = parsed->vpc->contact; call_cell->vpc->certuri = parsed->vpc->certuri; call_cell->esqk = parsed->esqk; call_cell->callid = parsed->callid; call_cell->datetimestamp = parsed->datetimestamp; call_cell->result = parsed->result; if (parsed->esgwri != empty && strlen(parsed->esgwri) > 0) { call_cell->esgwri = parsed->esgwri; call_cell->ert_npa = 0; call_cell->ert_resn = 0; call_cell->ert_srid = ""; char *r = strstr(call_cell->esgwri, "@"); r++; int tam_esgw = call_cell->esgwri + strlen(call_cell->esgwri) - r; call_cell->esgw = pkg_malloc(sizeof (char)*tam_esgw + 1); if (call_cell->esgw == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } memcpy(call_cell->esgw, r, tam_esgw); call_cell->esgw[tam_esgw] = 0; LM_DBG(" --- ESGW:%s \n", call_cell->esgw); if (parsed->ert->selectiveRoutingID != empty) pkg_free(parsed->ert->selectiveRoutingID); if (parsed->ert->routingESN != empty) pkg_free(parsed->ert->routingESN); if (parsed->ert->npa != empty) pkg_free(parsed->ert->npa); } else { if ((parsed->ert->selectiveRoutingID != empty) && (parsed->ert->routingESN != empty) && (parsed->ert->npa != empty)) { int npa = atoi(parsed->ert->npa); int resn = atoi(parsed->ert->routingESN); call_cell->ert_npa = npa; call_cell->ert_resn = resn; call_cell->ert_srid = parsed->ert->selectiveRoutingID; if (proxy_role == 4){ // in opensips as redirect role, consider esgwri as joint selectiveRoutingID + routingESN + npa + @vsp_address in contact headers in 300 response // get source ip address that send INVITE vsp_addr = ip_addr2a(&msg->rcv.src_ip); vsp_addr_len = strlen(vsp_addr); int esgw_len = strlen(parsed->ert->selectiveRoutingID) + strlen(parsed->ert->routingESN) + strlen(parsed->ert->npa) + vsp_addr_len + 4; p = pkg_malloc(sizeof (char)*esgw_len); if (p == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } call_cell->esgwri = p; memcpy(p, parsed->ert->selectiveRoutingID, strlen(parsed->ert->selectiveRoutingID)); p += strlen(parsed->ert->selectiveRoutingID); *p = '.'; p++; memcpy(p, parsed->ert->routingESN, strlen(parsed->ert->routingESN)); p += strlen(parsed->ert->routingESN); *p = '.'; p++; memcpy(p, parsed->ert->npa, strlen(parsed->ert->npa)); p += strlen(parsed->ert->npa); *p = '@'; p++; memcpy(p, vsp_addr, vsp_addr_len); p += vsp_addr_len; *p = 0; }else{ call_cell->esgwri = ""; } pkg_free(parsed->ert->routingESN); pkg_free(parsed->ert->npa); } } if (parsed-> lro!= empty ) { // extarct only contigency number in lro LM_DBG( "LRO %s \n", parsed-> lro); int len_lro = strlen(parsed->lro); lro_aux = pkg_malloc(sizeof (char)*len_lro + 1); if (lro_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(lro_aux, 0, len_lro + 1); pt_lro.s = lro_aux; pt_lro.len = len_lro; pattern_lro.s = "(tel:)*[+]*([-0-9]+)"; pattern_lro.len = strlen(pattern_lro.s); replacement_lro.s = "\\2"; replacement_lro.len = strlen(replacement_lro.s); if (reg_replace(pattern_lro.s, replacement_lro.s, parsed->lro, &pt_lro) != 1) { LM_ERR("****** PATTERN LRO NAO OK \n"); pkg_free(lro_aux); goto end; } pt_lro.len = strlen(pt_lro.s); LM_DBG("****** PATTERN LRO OK II %.*s\n",pt_lro.len,pt_lro.s); call_cell->lro = pkg_malloc(sizeof (char)*pt_lro.len+1); if (call_cell->lro == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } memcpy(call_cell->lro, pt_lro.s, pt_lro.len); call_cell->lro[pt_lro.len] = 0; pkg_free(lro_aux); pkg_free(parsed->lro); } end: pkg_free(parsed->ert); pkg_free(parsed->vpc); pkg_free(parsed->destination); pkg_free(parsed); return 1; } /* get lro information from contact header and save this in call cell */ int get_lro_in_contact(char *contact_lro, ESCT *call_cell) { char *contact_lro_aux; str pattern_contact_lro, replacement_contact_lro; str pt_contact_lro; int len_contact_lro = strlen(contact_lro); contact_lro_aux = pkg_malloc(sizeof (char)*len_contact_lro + 1); if (contact_lro_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(contact_lro_aux, 0,len_contact_lro + 1); pt_contact_lro.s = contact_lro_aux; pt_contact_lro.len = len_contact_lro; pattern_contact_lro.s = "sips?:[+]*1?-?([0-9]+)@"; pattern_contact_lro.len = strlen(pattern_contact_lro.s); replacement_contact_lro.s = "\\1"; replacement_contact_lro.len = strlen(replacement_contact_lro.s); if (reg_replace(pattern_contact_lro.s, replacement_contact_lro.s, contact_lro, &pt_contact_lro) != 1) { LM_ERR("****** PATTERN LRO NAO OK \n"); pkg_free(contact_lro_aux); pkg_free(contact_lro); return 1; } pt_contact_lro.len = strlen(pt_contact_lro.s); call_cell->lro = pkg_malloc(sizeof (char)* pt_contact_lro.len + 1); if (call_cell->lro == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } memcpy(call_cell->lro, pt_contact_lro.s, pt_contact_lro.len); call_cell->lro[pt_contact_lro.len] = 0; call_cell->disposition = "none"; LM_DBG ("TRANS REPLY LRO %s \n", call_cell->lro); pkg_free(contact_lro_aux); pkg_free(contact_lro); return 1; } /* get esqk information from contact header and save this in call cell */ int get_esqk_in_contact(char *contact_esgwri, ESCT *call_cell){ char *contact_esqk_aux; str pattern_contact_esqk, replacement_contact_esqk; str pt_contact_esqk; int len_contact_esgwri = strlen(contact_esgwri); contact_esqk_aux = pkg_malloc(sizeof (char)*len_contact_esgwri + 1); if (contact_esqk_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(contact_esqk_aux, 0,len_contact_esgwri + 1); pt_contact_esqk.s = contact_esqk_aux; pt_contact_esqk.len = len_contact_esgwri; pattern_contact_esqk.s = "Asserted-Identity:=<(sips?:)*[+]*1?-?([0-9]+)@"; pattern_contact_esqk.len = strlen(pattern_contact_esqk.s); replacement_contact_esqk.s = "\\2"; replacement_contact_esqk.len = strlen(replacement_contact_esqk.s); if (reg_replace(pattern_contact_esqk.s, replacement_contact_esqk.s, contact_esgwri, &pt_contact_esqk) != 1) { LM_ERR("****** PATTERN ESQK NAO OK \n"); pkg_free(contact_esqk_aux); pkg_free(contact_esgwri); return 0; } pt_contact_esqk.len = strlen(pt_contact_esqk.s); call_cell->esqk = pkg_malloc(sizeof (char)* pt_contact_esqk.len + 1); if (call_cell->esqk == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } memcpy(call_cell->esqk, pt_contact_esqk.s, pt_contact_esqk.len); call_cell->esqk[pt_contact_esqk.len] = 0; LM_DBG ("TRANS REPLY ESQK %s \n", call_cell->esqk); pkg_free(contact_esqk_aux); return 1; } /* get esgwri or ert information from contact header and save this in call cell */ int get_esgwri_ert_in_contact(char *contact_esgwri, ESCT *call_cell){ char *contact_routing_aux; str pattern_contact_routing, replacement_contact_routing; str pt_contact_routing; int len_contact_routing; char *contact_routing; char *pt_aux; char *p_aux; char *srid_aux, *resn_aux, *npa_aux; char *pt_a, *pt_b; str pattern_contact_ert, replacement_contact_ert; char *p = strstr(contact_esgwri, "P-Asserted-Identity"); len_contact_routing = p - contact_esgwri -1; contact_routing = pkg_malloc(sizeof (char)*len_contact_routing); if (contact_routing == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(contact_routing, 0, len_contact_routing); p_aux = contact_esgwri; p_aux++; memcpy(contact_routing, p_aux, len_contact_routing-1 ); pkg_free(contact_esgwri); contact_routing_aux = pkg_malloc(sizeof (char)*len_contact_routing); if (contact_routing_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(contact_routing_aux, 0,len_contact_routing); pt_contact_routing.s = contact_routing_aux; pt_contact_routing.len = len_contact_routing - 1; pattern_contact_routing.s = "^(sips?):[+]*([-0-9]+)@"; pattern_contact_routing.len = strlen(pattern_contact_routing.s); replacement_contact_routing.s = "\\2"; replacement_contact_routing.len = strlen(replacement_contact_routing.s); if (reg_replace(pattern_contact_routing.s, replacement_contact_routing.s, contact_routing, &pt_contact_routing) == 1) { LM_DBG ("TRANS REPLY ESGWRI %s \n",contact_routing); call_cell->esgwri = contact_routing; call_cell->disposition = "processes"; pkg_free(contact_routing_aux); }else{ pattern_contact_ert.s = "^(sips?):([A-Z0-9.]*)@"; pattern_contact_ert.len = strlen(pattern_contact_ert.s); replacement_contact_ert.s = "\\2"; replacement_contact_ert.len = strlen(replacement_contact_ert.s); if (reg_replace(pattern_contact_ert.s, replacement_contact_ert.s, contact_routing, &pt_contact_routing) != 1) { LM_ERR("****** PATTERN ERT NAO OK \n"); pkg_free(contact_routing_aux); pkg_free(contact_routing); return 0; } LM_DBG ("CONTEUDO TRANS REPLY ERT %.*s \n", pt_contact_routing.len, pt_contact_routing.s); pt_aux = pt_contact_routing.s; pt_a = strchr(pt_aux,'.'); int len_srid = pt_a - pt_contact_routing.s; srid_aux = pkg_malloc(sizeof (char)*len_srid + 1); if (srid_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memcpy(srid_aux, pt_aux, len_srid); srid_aux[len_srid] = 0; pt_aux += len_srid + 1; pt_b = strchr(pt_aux,'.'); int len_resn = pt_b - pt_aux ; resn_aux = pkg_malloc(sizeof (char)*len_resn + 1); if (resn_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memcpy(resn_aux, pt_aux, len_resn); resn_aux[len_resn] = 0; pt_aux += len_resn + 1; int len_npa = pt_contact_routing.len - len_srid - len_resn; npa_aux = pkg_malloc(sizeof (char)*len_npa + 1); if (npa_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1; } npa_aux[len_npa] = 0; memcpy(npa_aux, pt_aux, len_npa); LM_DBG ("CONTEUDO TRANS REPLY SRID %s \n",srid_aux); LM_DBG ("CONTEUDO TRANS REPLY RESN %s \n",resn_aux); LM_DBG ("CONTEUDO TRANS REPLY NPA %s \n",npa_aux); int npa = atoi(npa_aux); int resn = atoi(resn_aux); int srid_len = strlen(srid_aux); call_cell->ert_npa = npa; call_cell->ert_resn = resn; call_cell->ert_srid = pkg_malloc(sizeof (char)* srid_len + 1); if (call_cell->ert_srid == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return -1; } strcpy(call_cell->ert_srid, srid_aux); call_cell->ert_srid[srid_len] = 0; call_cell->disposition = "processes"; pkg_free(contact_routing_aux); pkg_free(contact_routing); pkg_free(srid_aux); pkg_free(resn_aux); pkg_free(npa_aux); } return 1; } void free_call_cell(ESCT *info_call){ if(info_call){ if (info_call->source){ if(info_call->source->organizationname){ if (strlen(info_call->source->organizationname)!= 0){ pkg_free (info_call->source->organizationname); LM_DBG(" --- FREE INFO_CALL->SOURCE->ORG"); } } if(info_call->source->hostname){ if (strlen(info_call->source->hostname)!= 0){ pkg_free (info_call->source->hostname); LM_DBG(" --- FREE INFO_CALL->SOURCE->HOST"); } } if(info_call->source->nenaid){ if (strlen(info_call->source->nenaid)!= 0){ pkg_free (info_call->source->nenaid); LM_DBG(" --- FREE INFO_CALL->SOURCE->NENA"); } } if(info_call->source->contact){ if (strlen(info_call->source->contact)!= 0){ pkg_free (info_call->source->contact); LM_DBG(" --- FREE INFO_CALL->SOURCE->CONTACT"); } } if(info_call->source->certuri){ if (strlen(info_call->source->certuri)!= 0){ pkg_free (info_call->source->certuri); LM_DBG(" --- FREE INFO_CALL->SOURCE->CERTURI"); } } pkg_free (info_call->source); } if (info_call->vpc){ if(info_call->vpc->organizationname){ if (strlen(info_call->vpc->organizationname)!= 0){ pkg_free (info_call->vpc->organizationname); LM_DBG(" --- FREE INFO_CALL->VPC->ORG"); } } if(info_call->vpc->hostname){ if (strlen(info_call->vpc->hostname)!= 0){ pkg_free (info_call->vpc->hostname); LM_DBG(" --- FREE INFO_CALL->VPC->HOST"); } } if(info_call->vpc->nenaid){ if (strlen(info_call->vpc->nenaid)!= 0){ pkg_free (info_call->vpc->nenaid); LM_DBG(" --- FREE INFO_CALL->VPC->NENA"); } } if(info_call->vpc->contact){ if (strlen(info_call->vpc->contact)!= 0){ pkg_free (info_call->vpc->contact); LM_DBG(" --- FREE INFO_CALL->VPC->CONTACT"); } } if(info_call->vpc->certuri){ if (strlen(info_call->vpc->certuri)!= 0){ pkg_free (info_call->vpc->certuri); LM_DBG(" --- FREE INFO_CALL->VPC->CERTURI"); } } pkg_free (info_call->vpc); } if (info_call->eme_dlg_id){ if(info_call->eme_dlg_id->call_id){ pkg_free (info_call->eme_dlg_id->call_id); LM_DBG(" --- FREE INFO_CALL->CALLID"); } if(info_call->eme_dlg_id->local_tag){ pkg_free (info_call->eme_dlg_id->local_tag); LM_DBG(" --- FREE INFO_CALL->LOCAL_TAG"); } pkg_free (info_call->eme_dlg_id); } if((info_call->esqk)&&(strlen(info_call->esqk) > 1)){ pkg_free (info_call->esqk); LM_DBG(" --- FREE INFO_CALL->ESQK"); } if(info_call->callid){ pkg_free (info_call->callid); LM_DBG(" --- FREE INFO_CALL->CALLID"); } if((info_call->lro)&&(strlen(info_call->lro) > 1)){ pkg_free (info_call->lro); LM_DBG(" --- FREE INFO_CALL->LRO"); } if((info_call->esgwri)&&(strlen(info_call->esgwri) > 1)){ pkg_free (info_call->esgwri); LM_DBG(" --- FREE INFO_CALL->ESGWRI"); } if((info_call->esgw)&&(strlen(info_call->esgw) > 1)){ pkg_free (info_call->esgw); LM_DBG(" --- FREE INFO_CALL->ESGW"); } if((info_call->ert_srid)&&(strlen(info_call->ert_srid) > 1)){ LM_DBG(" --- FREE INFO_CALL->ERT_SRID"); pkg_free (info_call->ert_srid); } if((info_call->result)&&(strlen(info_call->result) > 1)){ pkg_free (info_call->result); LM_DBG(" --- FREE INFO_CALL->RESULT"); } if((info_call->datetimestamp)&&(strlen(info_call->datetimestamp) > 1)){ pkg_free (info_call->datetimestamp); LM_DBG(" --- FREE INFO_CALL->DATETIMESTAMP"); } pkg_free (info_call); } } /* frees the memory from the struct NENA */ void free_nena(NENA *nena) { if (nena->organizationname && strlen(nena->organizationname)>0){ pkg_free(nena->organizationname); } if (nena->hostname && strlen(nena->hostname)>0){ pkg_free(nena->hostname); } if (nena->nenaid && strlen(nena->nenaid)>0){ pkg_free(nena->nenaid); } if (nena->contact && strlen(nena->contact)>0){ pkg_free(nena->contact); } if (nena->certuri && strlen(nena->certuri)>0){ pkg_free(nena->certuri); } } /*frees memory from the data received from the VPC */ void free_parsed(PARSED *parsed){ if(parsed){ if(parsed->ert->routingESN && strlen(parsed->ert->routingESN)>0){ pkg_free(parsed->ert->routingESN); } if(parsed->ert->selectiveRoutingID && strlen(parsed->ert->selectiveRoutingID)>0){ pkg_free(parsed->ert->selectiveRoutingID); } if(parsed->ert->npa && strlen(parsed->ert->npa)>0){ pkg_free(parsed->ert->npa); } pkg_free(parsed->ert); free_nena(parsed->vpc); pkg_free(parsed->vpc); free_nena(parsed->destination); pkg_free(parsed->destination); if(parsed->result && strlen(parsed->result)>0){ pkg_free(parsed->result); } if(parsed->esgwri && strlen(parsed->esgwri)>0){ pkg_free(parsed->esgwri); } if(parsed->esqk && strlen(parsed->esqk)>0){ pkg_free(parsed->esqk); } if(parsed->lro && strlen(parsed->lro)>0){ pkg_free(parsed->lro); } if(parsed->callid && strlen(parsed->callid)>0){ pkg_free(parsed->callid); } if(parsed->datetimestamp && strlen(parsed->datetimestamp)>0){ pkg_free(parsed->datetimestamp); } pkg_free(parsed); } } opensips-2.2.2/modules/emergency/http_emergency.h000066400000000000000000000052401300170765700222000ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "../../socket_info.h" #include "../../route_struct.h" #include "../../ip_addr.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_pai.h" #include "../../parser/parse_ppi.h" #include "../../parser/parse_rpid.h" #include "../../parser/parse_from.h" #include "../../regexp.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "../../rw_locking.h" #include "../../timer.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../forward.h" #include "../rr/api.h" #include "report_emergency.h" #include "post_curl.h" struct call_htable* call_htable; struct subs_htable* subs_htable; char *url_vpc; str db_url; str *db_table; int emet_size; int subst_size; int send_esct(struct sip_msg *msg, str callid_ori, str from_tag); int treat_parse_esrResponse(struct sip_msg *msg, ESCT *call_cell, PARSED *parsed, int proxy_role); int get_lro_in_contact(char *contact_lro, ESCT *call_cell); int get_esqk_in_contact(char *contact_lro, ESCT *call_cell); int get_esgwri_ert_in_contact(char *contact_esgwri, ESCT *call_cell); void free_call_cell(ESCT *info_call); void free_subs_cell(struct sm_subscriber* subs_cell); void free_nena(NENA *nena); void free_parsed(PARSED *parsed); opensips-2.2.2/modules/emergency/model.h000066400000000000000000000041111300170765700202570ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ const char *MODEL = " \n \ \n \ %s \n \ %s \n \ %s \n \ %s \n \ %s \n \ \n \ \n \ %s \n \ %s \n \ %s \n \ %s \n \ %s \n \ \n \ \n \ %s \n \ %s \n \ %s \n \ %s \n \ %s \n \ \n \ %s \n \ %s \n \ \n \ %s \n \ \n \ %s \n \ %s \n \ 0 \n \ "; opensips-2.2.2/modules/emergency/notifier_emergency.c000066400000000000000000000463621300170765700230450ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include "notifier_emergency.h" /* get data from Subscriber and save in list link with notify information: . dialog id of emergency call . dialog id of subscribe . local uri . remote uri . contact . expires . time to expire subscriber - status . cell next . cell previus */ struct sm_subscriber* build_notify_cell(struct sip_msg *msg, int expires){ char *subs_callid, *subs_fromtag; str callid_event; str fromtag_event; str callid; struct to_body *pto= NULL, *pfrom = NULL; int size_notify_cell; int vsp_addr_len; char *vsp_addr = "@vsp.com"; int vsp_port = 5060; int size_vsp_port = 4; char* str_vsp_port; struct sm_subscriber *notify_cell = NULL; time_t rawtime; int time_now; char *p; unsigned int hash_code; static str msg489={"Bad Event",sizeof("Bad Event")-1}; // get data from SUBSCRIBE request // get callid from Subscribe if( msg->callid==NULL || msg->callid->body.s==NULL){ LM_ERR("subscribe without callid header\n"); return NULL; } callid = msg->callid->body; LM_DBG("CALLID: %.*s \n ", callid.len, callid.s ); //get From header from Subscribe if (msg->from->parsed == NULL){ if ( parse_from_header( msg )<0 ){ LM_ERR("subscribe without From header\n"); return NULL; } } pfrom = get_from(msg); LM_DBG("PFROM: %.*s \n ", pfrom->uri.len, pfrom->uri.s ); LM_DBG("PFROM_TAG: %.*s \n ", pfrom->tag_value.len, pfrom->tag_value.s ); if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0){ LM_ERR("subscribe without from_tag value \n"); return NULL; } if( msg->to==NULL || msg->to->body.s==NULL){ LM_ERR("error in parse TO header\n"); return NULL; } // get To header from Subscribe pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); return NULL; } LM_DBG("PTO: %.*s \n ", pto->uri.len, pto->uri.s ); LM_DBG("PTO_TAG: %.*s \n ", pto->tag_value.len, pto->tag_value.s ); /* get in event header: callid and from_tag */ if(get_event_header(msg, &subs_callid, &subs_fromtag) != 1){ LM_ERR("failed to parse Event header\n"); return NULL; } LM_DBG("SUBS_CALLID: %s\n ", subs_callid); LM_DBG("SUBS_FROMTAG: %s\n ", subs_fromtag); callid_event.s = subs_callid; callid_event.len = strlen(subs_callid); fromtag_event.s = subs_fromtag; fromtag_event.len = strlen(subs_fromtag); hash_code= core_hash(&callid_event, 0, emet_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); // search call hash with hash_code, callidHeader and from/to_tag params if (search_ehtable(call_htable, subs_callid, subs_fromtag, hash_code, 0) == NULL) { LM_ERR(" ---CALLID NOT FOUND IN SHTABLE\n"); if(!eme_tm.t_reply(msg,489,&msg489)){ LM_DBG("t_reply (489)\n"); } pkg_free(callid_event.s); pkg_free(fromtag_event.s); return 0; } LM_DBG("CALLID OK in subs_hash\n"); time(&rawtime); time_now = (int)rawtime; LM_DBG("TIME : %d \n", (int)rawtime ); // get source ip address that send INVITE vsp_addr = ip_addr2a(&msg->rcv.src_ip); vsp_addr_len = strlen(vsp_addr); vsp_port = msg->rcv.src_port; str_vsp_port= int2str(vsp_port, &size_vsp_port); LM_DBG("SRC_PORT : %s \n", str_vsp_port); /* build notifier cell */ size_notify_cell = sizeof(struct sm_subscriber) + (2 * sizeof(struct dialog_id)) + callid.len + pfrom->tag_value.len + pto->tag_value.len + pfrom->uri.len + pto->uri.len + callid_event.len + fromtag_event.len + vsp_addr_len + size_vsp_port + 11 ; notify_cell = pkg_malloc(size_notify_cell + 1); if (!notify_cell) { LM_ERR("no more shm\n"); return NULL; } memset(notify_cell, 0, size_notify_cell + 1); notify_cell->expires = expires; LM_DBG("EXPIRES: %d \n ", notify_cell->expires ); notify_cell->timeout = TIMER_N + time_now; LM_DBG("SUBS_TIMEOUT: %d \n ", notify_cell->timeout ); notify_cell->version = 0; LM_DBG("SUBS_VERSION: %d \n ", notify_cell->version ); notify_cell->dlg_id = (struct dialog_id*)(notify_cell + 1); notify_cell->dlg_id->callid.len = callid.len; notify_cell->dlg_id->callid.s = (char *) (notify_cell->dlg_id + 1); memcpy(notify_cell->dlg_id->callid.s, callid.s, callid.len); LM_DBG("SUBS_CALLID: %.*s \n ", notify_cell->dlg_id->callid.len, notify_cell->dlg_id->callid.s ); notify_cell->dlg_id->rem_tag.len = pfrom->tag_value.len; notify_cell->dlg_id->rem_tag.s = (char *) (notify_cell->dlg_id + 1) + callid.len; memcpy(notify_cell->dlg_id->rem_tag.s, pfrom->tag_value.s, pfrom->tag_value.len); LM_DBG("SUBS_FROM_TAG: %.*s \n ", notify_cell->dlg_id->rem_tag.len, notify_cell->dlg_id->rem_tag.s ); p = (char *)(notify_cell->dlg_id + 1) + callid.len + pfrom->tag_value.len; notify_cell->call_dlg_id = (struct dialog_id*)p; notify_cell->call_dlg_id->callid.len= callid_event.len; notify_cell->call_dlg_id->callid.s = (char *) (notify_cell->call_dlg_id + 1); memcpy(notify_cell->call_dlg_id->callid.s, callid_event.s, callid_event.len); LM_DBG("SUBS_CALLID_event: %.*s \n ", notify_cell->call_dlg_id->callid.len, notify_cell->call_dlg_id->callid.s ); notify_cell->call_dlg_id->rem_tag.len= fromtag_event.len; notify_cell->call_dlg_id->rem_tag.s = (char *) (notify_cell->call_dlg_id + 1) + callid_event.len; memcpy(notify_cell->call_dlg_id->rem_tag.s, fromtag_event.s, fromtag_event.len); LM_DBG("SUBS_FROMTAG_event: %.*s \n ", notify_cell->call_dlg_id->rem_tag.len, notify_cell->call_dlg_id->rem_tag.s ); notify_cell->loc_uri.len = pto->uri.len; notify_cell->loc_uri.s = (char *) (notify_cell->call_dlg_id + 1) + callid_event.len + fromtag_event.len; memcpy(notify_cell->loc_uri.s,pto->uri.s,pto->uri.len); LM_DBG("SUBS_LOC_URI: %.*s \n ", notify_cell->loc_uri.len, notify_cell->loc_uri.s ); notify_cell->rem_uri.len= pfrom->uri.len; notify_cell->rem_uri.s = (char *) (notify_cell->call_dlg_id + 1) + callid_event.len + fromtag_event.len + pto->uri.len; memcpy(notify_cell->rem_uri.s, pfrom->uri.s, pfrom->uri.len); LM_DBG("SUBS_REM_URI: %.*s \n ", notify_cell->rem_uri.len, notify_cell->rem_uri.s ); notify_cell->contact.len = vsp_addr_len + size_vsp_port +11; notify_cell->contact.s = (char *) (notify_cell->call_dlg_id + 1) + pfrom->uri.len + pto->uri.len + callid_event.len + fromtag_event.len; memcpy(notify_cell->contact.s, "sip:teste@", 10); memcpy(notify_cell->contact.s + 10, vsp_addr, vsp_addr_len); memcpy(notify_cell->contact.s + 10 + vsp_addr_len, ":", 1); memcpy(notify_cell->contact.s + 11 + vsp_addr_len, str_vsp_port, size_vsp_port); LM_DBG("SUBS_CONTACT: %.*s \n ", notify_cell->contact.len, notify_cell->contact.s ); notify_cell->dlg_id->status = RESP_WAIT; pkg_free(callid_event.s); pkg_free(fromtag_event.s); return notify_cell; } /* Treat Notify to Subscriber Dialog in scenario III*/ int treat_subscribe(struct sip_msg *msg) { struct cell *t; static str msg200={"OK Subscribe",sizeof("OK Subscribe")-1}; static str msg423={"Interval Too Brief",sizeof("Interval Too Brief")-1}; static str msg481={"Subscription does not exist",sizeof("Subscription does not exist")-1}; static str msg489={"Bad Event",sizeof("Bad Event")-1}; struct sm_subscriber *notify_cell = NULL; struct sm_subscriber *pt_notify = NULL; char *subs_expires; int expires= 0; char *subs_callid, *subs_fromtag; str callid_event; unsigned int hash_code; if(!check_event_header(msg)){ LM_ERR("event header type not allow\n"); if(!eme_tm.t_reply(msg,489,&msg489)){ LM_ERR("t_reply (489)\n"); } return 0; } /* get expires field */ if(!get_expires_header(msg, &subs_expires)){ LM_ERR("body's expires header not found\n"); expires = TIME_DEFAULT_SUBS; }else{ LM_DBG("SUBS_EXPIRES: %s\n ", subs_expires); // if expires body isn't a numerical string, then expires value is zero expires = atoi(subs_expires); pkg_free(subs_expires); if ((expires != 0) & (expires < TIMER_MIN_SUBS)){ /* Reply NOK to Notify*/ if(!eme_tm.t_reply(msg,423,&msg423)){ LM_DBG("t_reply (423)\n"); } return 0; } } if (expires == 0){ if(get_event_header(msg, &subs_callid, &subs_fromtag) == 1){ callid_event.s = subs_callid; callid_event.len = strlen(subs_callid); }else{ LM_ERR("error in Event Header of Subscriber"); return 0; } pt_notify = get_subs_cell(msg, callid_event); if (pt_notify == NULL){ LM_ERR("**** notify cell not found"); if(!eme_tm.t_reply(msg,481,&msg481)){ LM_ERR("t_reply (481)\n"); } return 0; } pt_notify->dlg_id->status = TERMINATED; pt_notify->expires = 0; pkg_free(subs_callid); pkg_free(subs_fromtag); /* Reply OK to Notify*/ if(!eme_tm.t_reply(msg,200,&msg200)){ LM_DBG("t_reply (200)\n"); return 0; } }else{ notify_cell = build_notify_cell(msg, expires); if (notify_cell == NULL){ LM_ERR("**** error in build notify cell"); if(!eme_tm.t_reply(msg,489,&msg489)){ LM_ERR("t_reply (489)\n"); } return 0; } /* Reply OK to Notify*/ if(!eme_tm.t_reply(msg,200,&msg200)){ LM_DBG("t_reply (200)\n"); pkg_free(notify_cell); return 0; } t = eme_tm.t_gett(); LM_DBG(" --- TO TAG %.*s \n", t->uas.local_totag.len, t->uas.local_totag.s); notify_cell->dlg_id->local_tag.s = pkg_malloc(t->uas.local_totag.len + 1); if (!notify_cell->dlg_id->local_tag.s) { LM_ERR("no more shm\n"); return 0; } notify_cell->dlg_id->local_tag.s[t->uas.local_totag.len] = 0; notify_cell->dlg_id->local_tag.len = t->uas.local_totag.len; memcpy(notify_cell->dlg_id->local_tag.s, t->uas.local_totag.s, t->uas.local_totag.len); LM_DBG("SUBS_FROM_TAG: %.*s \n ", notify_cell->dlg_id->local_tag.len, notify_cell->dlg_id->local_tag.s ); hash_code= core_hash(¬ify_cell->call_dlg_id->callid, 0, subst_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); LM_DBG("********************************************CALLID_STR%.*s\n", notify_cell->call_dlg_id->callid.len, notify_cell->call_dlg_id->callid.s); pt_notify = insert_shtable(subs_htable, hash_code, notify_cell); if(pt_notify == NULL){ LM_ERR("inserting new record in subs_htable\n"); return 0; } pkg_free(notify_cell->dlg_id->local_tag.s); pkg_free(notify_cell); } if( !send_notifier_within(msg, pt_notify)){ LM_ERR("send_notifier_within\n"); return 0; } return 1; } /* send notifier within of dialog, this notifier is a request that confirm subscribe */ int send_notifier_within(struct sip_msg* msg, struct sm_subscriber* notify){ dlg_t* dialog =NULL; str met= {"NOTIFY", 6}; int sending; struct sm_subscriber* params_cb; //char* event; str* pt_hdr= NULL; str* pt_body = NULL; dialog = build_dlg(notify); if(dialog== NULL){ LM_DBG(" --- ERROR IN BUILD DIALOG \n"); return -1; } LM_DBG(" --- FINAL \n"); LM_DBG(" --- DIALOG CALLID%.*s \n", dialog->id.call_id.len, dialog->id.call_id.s); LM_DBG(" --- DIALOG REMTAG%.*s \n", dialog->id.rem_tag.len, dialog->id.rem_tag.s); LM_DBG(" --- DIALOG LOCTAG%.*s \n", dialog->id.loc_tag.len, dialog->id.loc_tag.s); LM_DBG(" --- DIALOG REMURI%.*s \n", dialog->rem_uri.len, dialog->rem_uri.s); LM_DBG(" --- DIALOG LOCURI%.*s \n", dialog->loc_uri.len, dialog->loc_uri.s); LM_DBG(" --- DIALOG CONTACT%.*s \n", dialog->rem_target.len, dialog->rem_target.s); params_cb = notify; pt_body = add_body_notifier(notify); pt_hdr = add_hdr_notifier(notify); sending= eme_tm.t_request_within (&met, pt_hdr, pt_body, dialog, notif_cback_func, (void*)params_cb, 0 ); if(sending< 0) LM_ERR("while sending request with t_request_within\n"); if(pt_hdr != NULL){ pkg_free(pt_hdr->s); pkg_free(pt_hdr); } if(pt_body != NULL){ pkg_free(pt_body->s); pkg_free(pt_body); } pkg_free(dialog); return 1; } /* Treat Notify reply callback */ void notif_cback_func(struct cell *t, int cb_type, struct tmcb_params *params){ int code = params->code; struct sm_subscriber* params_notify = (struct sm_subscriber*)(*params->param); unsigned int hash_code; LM_DBG("TREAT NOTIFY REPLY \n"); LM_DBG("CODE: %d \n ", code); // verify if response is OK if (code >= 200 && code < 300){ // response OK(2XX) if (params_notify->expires > 0){ LM_DBG("REPLY OK timeout %d \n", params_notify->timeout); LM_DBG("REPLY OK expires %d \n", params_notify->expires); time_t rawtime; time(&rawtime); int time_now = (int)rawtime; LM_DBG("TIME : %d \n", (int)rawtime ); // update timeout params_notify->timeout = params_notify->expires + time_now; LM_DBG("TIMEOUT_NOTIFY: %d \n ", params_notify->timeout); return; } if (params_notify->dlg_id->status == TERMINATED){ // delete subs_htable hash_code= core_hash(¶ms_notify->call_dlg_id->callid, 0, subst_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); LM_DBG("********************************************CALLID_STR%.*s\n", params_notify->call_dlg_id->callid.len, params_notify->call_dlg_id->callid.s); delete_shtable(subs_htable, hash_code, params_notify); } }else{ // Response NOK LM_ERR("reply to NOTIFY NOK\n"); } return; } /* build new headers(Event, Expires) to SUBSCRIBER request */ str* add_body_notifier(struct sm_subscriber* notifier){ char *aux_body; //char* str_expires= NULL; char* call_status=NULL; int size_status = 0; int size_version = 1; int size_body; str* pt_body= NULL; char* version; if (notifier->dlg_id->status == TERMINATED ){ LM_DBG("finesh notify\n"); return NULL; } if (notifier->call_dlg_id->status == TERMINATED ){ call_status = "terminated"; size_status = 10; //version = "\"2\""; }else{ call_status = "active"; size_status = 6; //version = "\"0\""; } /* convert version in string*/ version = int2str(notifier->version, &size_version); LM_DBG("VERSION -str : %s \n",version ); if(version == NULL || size_version == 0){ LM_ERR("while converting version int to str\n"); return NULL; } notifier->version++; pt_body = (str*) pkg_malloc (sizeof (str)); if (pt_body == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return NULL; } size_body = size_status + size_version + notifier->call_dlg_id->rem_tag.len + notifier->loc_uri.len + notifier->dlg_id->callid.len + notifier->call_dlg_id->callid.len + 197 + 11*CRLF_LEN; aux_body= pkg_malloc(sizeof(char)* size_body + 1); if(aux_body== NULL){ LM_ERR("no more memory\n"); return NULL; } memset(aux_body, 0, size_body+1); pt_body->s = aux_body; pt_body->len = size_body; memcpy(aux_body, "", 21); aux_body+= 21; memcpy(aux_body, CRLF, CRLF_LEN); aux_body += CRLF_LEN; memcpy(aux_body, "call_dlg_id->rem_tag.s, notifier->call_dlg_id->rem_tag.len); aux_body += notifier->call_dlg_id->rem_tag.len; memcpy(aux_body, "\" direction=\"initiator\">", 24); aux_body+= 24; memcpy(aux_body, CRLF, CRLF_LEN); aux_body += CRLF_LEN; memcpy(aux_body, "", 7); aux_body+= 7; memcpy(aux_body, call_status, size_status); aux_body += size_status; memcpy(aux_body, "", 8); aux_body+= 8; memcpy(aux_body, CRLF, CRLF_LEN); aux_body += CRLF_LEN; memcpy(aux_body, "", 9); aux_body+= 9; memcpy(aux_body, CRLF, CRLF_LEN); aux_body += CRLF_LEN; memcpy(aux_body, "", 14); aux_body+= 14; memcpy(aux_body, CRLF, CRLF_LEN); aux_body += CRLF_LEN; LM_DBG(" aux_body:%.*s \n",pt_body->len, pt_body->s); return pt_body; } /* build new headers(Event, Expires) to SUBSCRIBER request */ str* add_hdr_notifier(struct sm_subscriber* notifier){ char *aux_hdr; char* str_expires= NULL; char* status=NULL; int size_status = 0; int size_expires = 1; int size_hdr; str* pt_hdr= NULL; /* convert expires in string*/ str_expires= int2str(notifier->expires, &size_expires); LM_DBG("EXPIRES -str : %s \n",str_expires ); if(str_expires == NULL || size_expires == 0){ LM_ERR("while converting int to str\n"); return NULL; } if (notifier->dlg_id->status == TERMINATED ){ status = "terminated"; size_status = 10; size_expires = 0; size_hdr = size_status + 58 + 3*CRLF_LEN; }else{ status = "active"; size_status = 6; size_hdr = size_expires + size_status + 67 + 3*CRLF_LEN; } pt_hdr = (str*) pkg_malloc (sizeof (str)); if (pt_hdr == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } aux_hdr= pkg_malloc(sizeof(char)* size_hdr + 1); if(aux_hdr== NULL){ LM_ERR("no more memory\n"); return NULL; } memset(aux_hdr, 0, size_hdr+1); pt_hdr->s = aux_hdr; pt_hdr->len = size_hdr; memcpy(aux_hdr, "Event: dialog", 13); aux_hdr+= 13; memcpy(aux_hdr, CRLF, CRLF_LEN); aux_hdr += CRLF_LEN; memcpy(aux_hdr, "Subscription-State: ", 20); aux_hdr+= 20; memcpy(aux_hdr, status, size_status); aux_hdr+= size_status; if ( size_expires != 0){ memcpy(aux_hdr, ";expires=", 9); aux_hdr+= 9; memcpy(aux_hdr, str_expires, size_expires); aux_hdr += size_expires; } memcpy(aux_hdr, CRLF, CRLF_LEN); aux_hdr += CRLF_LEN; memcpy(aux_hdr, "Content-Type: dialog-info", 25); aux_hdr+= 25; memcpy(aux_hdr, CRLF, CRLF_LEN); aux_hdr += CRLF_LEN; LM_DBG("NEW_HDR : %.*s \n", pt_hdr->len, pt_hdr->s); return pt_hdr; } opensips-2.2.2/modules/emergency/notifier_emergency.h000066400000000000000000000051561300170765700230460ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "../../socket_info.h" #include "../../route_struct.h" #include "../../ip_addr.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_pai.h" #include "../../parser/parse_ppi.h" #include "../../parser/parse_rpid.h" #include "../../parser/parse_from.h" #include "../../regexp.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "../../rw_locking.h" #include "../../timer.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../forward.h" #include "../rr/api.h" #include "../tm/tm_load.h" /*load_tm_api*/ #include "subscriber_emergency.h" #define INIT 0 #define RESP_WAIT 1 #define PENDING 2 #define ACTIVE 3 #define TERMINATED 4 #define TIME_DEFAULT_SUBS 3000 #define TIMER_MIN_SUBS 200 int treat_subscribe(struct sip_msg *msg); int send_notifier_within(struct sip_msg* msg, struct sm_subscriber* notify); void notif_cback_func(struct cell *t, int cb_type, struct tmcb_params *params); struct sm_subscriber* build_notify_cell(struct sip_msg *msg, int expires); str* add_hdr_notifier(struct sm_subscriber* notifier); str* add_body_notifier(struct sm_subscriber* notifier); opensips-2.2.2/modules/emergency/post_curl.c000066400000000000000000000065731300170765700212020ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include #include #include "post_curl.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" struct url_data { size_t size; char* data; }; size_t write_data(char *ptr, size_t size, size_t nmemb, void *stream) { struct url_data *data = (struct url_data *)stream; size_t index = data->size; size_t n = (size * nmemb); char* tmp; data->size += (size * nmemb); #ifdef DEBUG fprintf(stderr, "data at %p size=%ld nmemb=%ld\n", ptr, size, nmemb); #endif tmp = realloc(data->data, data->size + 1); /* +1 for '\0' */ if(tmp) { data->data = tmp; } else { if(data->data) { free(data->data); } fprintf(stderr, "Failed to allocate memory.\n"); return 0; } memcpy((data->data + index), ptr, n); data->data[data->size] = '\0'; return size * nmemb; } /* simple FTTP POST using curl lib */ int post(char* url, char* xml, char** response){ CURL *curl; CURLcode res; LM_DBG("INIT CURL"); curl = curl_easy_init(); struct url_data data; data.size = 0; data.data = malloc(1024); /* reasonable size initial buffer */ if(NULL == data.data) { LM_ERR("NO MEMORY"); return -1; } memset(data.data, '\0', 1024); LM_DBG("CURL PASSOU MALLOC"); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, xml); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); long http_code = 0; res = curl_easy_perform(curl); int resp = -1; if(res != CURLE_OK){ LM_DBG("CURL curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); resp = -1; }else{ curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); if(http_code != 0 &&(http_code <200 || http_code >=300)){ LM_DBG("CURL HTTP STATUS %ld", http_code); return -1; } LM_DBG("CURL OK...\n"); *response = pkg_malloc(sizeof(char)*strlen(data.data)); strcpy(*response,data.data); LM_DBG("CURL DEPOIS DO DATA OK...\n"); resp = 1; } /* always cleanup */ curl_easy_cleanup(curl); LM_DBG("CURL DEPOIS DO CLEANUP...\n"); free(data.data); LM_DBG("CURL DEPOIS DO FREE...\n"); return resp; } curl_global_cleanup(); return -1; } opensips-2.2.2/modules/emergency/post_curl.h000066400000000000000000000023321300170765700211740ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ int post(char* url, char* xml,char** response); opensips-2.2.2/modules/emergency/report_emergency.c000066400000000000000000000532331300170765700225340ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include "report_emergency.h" #define NR_KEYS 12 static str id_rep_col=str_init("id"); static str callid_rep_col=str_init("callid"); static str srid_rep_col=str_init("selectiveRoutingID"); static str resn_rep_col=str_init("routingESN"); static str npa_rep_col=str_init("npa"); static str esgwri_rep_col=str_init("esgwri"); static str lro_rep_col=str_init("lro"); static str vpc_name_rep_col=str_init("VPC_organizationName"); static str vpc_host_rep_col=str_init("VPC_hostname"); static str timestamp_rep_col=str_init("VPC_timestamp"); static str result_rep_col=str_init("result"); static str disposition_rep_col=str_init("disposition"); static str id_col=str_init("id"); static str srid_col=str_init("selectiveRoutingID"); static str resn_col=str_init("routingESN"); static str npa_col=str_init("npa"); static str esgwri_col=str_init("esgwri"); static str organizationName_col=str_init("organizationName"); static str hostId_col=str_init("hostId"); static str nenaId_col=str_init("nenaId"); static str contact_col=str_init("contact"); static str certUri_col=str_init("certUri"); static str nodeIP_col=str_init("nodeIP"); static str attribution_col=str_init("attribution"); /* store data in the table emergency_report */ int report(struct emergency_report *report, str db_url, str table_report) { static query_list_t *ins_list = NULL; static db_ps_t emergency_ps = NULL; LM_DBG("Report emergency call in db\n"); db_funcs.use_table(db_con, &table_report); db_key_t db_keys[NR_KEYS]; db_val_t db_vals[NR_KEYS]; if (report == NULL) { LM_DBG("invalid parameter\n"); return -1; } db_keys[0] = &id_rep_col; db_vals[0].type = DB_BIGINT; db_vals[0].val.bigint_val = 0; db_keys[1] = &callid_rep_col; db_vals[1].type = DB_STR; db_vals[1].val.str_val = report->callid; LM_DBG("CALLID_REPORT %.*s \n", report->callid.len, report->callid.s); LM_DBG("CALLID_REPORT_LEN %d \n", report->callid.len); db_keys[2] = &srid_rep_col; db_vals[2].type = DB_STR; db_vals[2].val.str_val = report->ert_srid; LM_DBG("SRID_REPORT %.*s \n", report->ert_srid.len, report->ert_srid.s); LM_DBG("SRID_REPORT_LEN %d \n", report->ert_srid.len); db_keys[3] = &resn_rep_col; db_vals[3].type = DB_BIGINT; db_vals[3].val.bigint_val = report->ert_resn; LM_DBG("RESN_REPORT %d \n", report->ert_resn); db_keys[4] = &npa_rep_col; db_vals[4].type = DB_BIGINT; db_vals[4].val.bigint_val = report->ert_npa; LM_DBG("NPA_REPORT %d \n", report->ert_npa); db_keys[5] = &esgwri_rep_col; db_vals[5].type = DB_STR; db_vals[5].val.str_val = report->esgwri; LM_DBG("ESGWRI_REPORT %.*s \n", report->esgwri.len, report->esgwri.s); LM_DBG("ESGWRI_REPORT_LEN %d \n", report->esgwri.len); db_keys[6] = &lro_rep_col; db_vals[6].type = DB_STR; db_vals[6].val.str_val = report->lro; LM_DBG("LRO_REPORT %.*s \n", report->lro.len, report->lro.s); LM_DBG("LRO_REPORT_LEN %d \n", report->lro.len); db_keys[7] = &vpc_name_rep_col; db_vals[7].type = DB_STR; db_vals[7].val.str_val = report->vpc_name; LM_DBG("VPC_NAME_REPORT %.*s \n", report->vpc_name.len, report->vpc_name.s); LM_DBG("VPC_NAME_REPORT_LEN %d \n", report->vpc_name.len); db_keys[8] = &vpc_host_rep_col; db_vals[8].type = DB_STR; db_vals[8].val.str_val = report->vpc_host; LM_DBG("VPC_HOST_REPORT %.*s \n", report->vpc_host.len, report->vpc_host.s); LM_DBG("VPC_HOST_REPORT_LEN %d \n", report->vpc_host.len); db_keys[9] = ×tamp_rep_col; db_vals[9].type = DB_STR; db_vals[9].val.str_val = report->timestamp; LM_DBG("VPC_TIMESTAMP_REPORT %.*s \n", report->timestamp.len, report->timestamp.s); LM_DBG("VPC_TIMESTAMP_REPORT_LEN %d \n", report->timestamp.len); db_keys[10] = &result_rep_col; db_vals[10].type = DB_STR; db_vals[10].val.str_val = report->result; LM_DBG("RESULT_REPORT %.*s \n", report->result.len, report->result.s); LM_DBG("RESULT_REPORT_LEN %d \n", report->result.len); db_keys[11] = &disposition_rep_col; db_vals[11].type = DB_STR; db_vals[11].val.str_val = report->disposition; LM_DBG("DISPOSITION_REPORT %.*s \n", report->disposition.len, report->disposition.s); LM_DBG("DISPOSITION_REPORT_LEN %d \n", report->disposition.len); // no field can be null int i = 0; for (i = 0; i < NR_KEYS; i++) db_vals[i].nul = 0; LM_DBG("storing info...\n"); if (con_set_inslist(&db_funcs, db_con, &ins_list, db_keys, NR_KEYS) < 0) CON_RESET_INSLIST(db_con); CON_PS_REFERENCE(db_con) = &emergency_ps; if (db_funcs.insert(db_con, db_keys, db_vals, NR_KEYS) < 0) { LM_ERR("failed to insert into database\n"); return -1;; } return 1; } /* collects data to system debug: * - CALLID * - ESGWRI * - ERT-RESN * - ERT-NPA * - ERT-SRID * - LRO * - VPC - NAME * - VPC - HOST * - TIMESTAMP * - RESULT * - DISPOSITION */ int collect_data(struct node *current, str db_url, str table_report) { int callid_len, esgwri_len, srid_len, lro_len, vpc_name_len, vpc_host_len, time_len, result_len, disposition_len; int size_report; struct emergency_report *report_eme; callid_len = strlen(current->esct->callid); esgwri_len = strlen(current->esct->esgwri); srid_len = strlen(current->esct->ert_srid); lro_len = strlen(current->esct->lro); time_len = strlen(current->esct->datetimestamp); result_len = strlen(current->esct->result); disposition_len = strlen(current->esct->disposition); vpc_name_len = strlen(current->esct->vpc->organizationname); vpc_host_len = strlen(current->esct->vpc->hostname); size_report = sizeof (struct emergency_report) +callid_len + esgwri_len + srid_len + lro_len + vpc_name_len + vpc_host_len + time_len + result_len + disposition_len; report_eme = pkg_malloc(size_report); if (report_eme == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(report_eme, 0, size_report); report_eme->callid.len = callid_len; report_eme->callid.s = (char *) (report_eme + 1); memcpy(report_eme->callid.s, current->esct->callid, callid_len); report_eme->ert_srid.len = srid_len; if (srid_len == 0) { report_eme->ert_srid.s = " "; report_eme->ert_srid.len = 1; } else { report_eme->ert_srid.s = (char *) (report_eme + 1) + callid_len; memcpy(report_eme->ert_srid.s, current->esct->ert_srid, srid_len); } report_eme->ert_resn = current->esct->ert_resn; report_eme->ert_npa = current->esct->ert_npa; report_eme->esgwri.len = esgwri_len; if (esgwri_len == 0) { report_eme->esgwri.s = " "; report_eme->esgwri.len = 1; } else { report_eme->esgwri.s = (char *) (report_eme + 1) + callid_len + srid_len; memcpy(report_eme->esgwri.s, current->esct->esgwri, esgwri_len); } report_eme->lro.len = lro_len; if (lro_len == 0) { report_eme->lro.s = " "; report_eme->lro.len = 1; } else { report_eme->lro.s = (char *) (report_eme + 1) + callid_len + srid_len + esgwri_len; memcpy(report_eme->lro.s, current->esct->lro, lro_len); } report_eme->vpc_name.len = vpc_name_len; if (vpc_name_len == 0) { report_eme->vpc_name.s = " "; report_eme->vpc_name.len = 1; } else { report_eme->vpc_name.s = (char *) (report_eme + 1) + callid_len + srid_len + esgwri_len + lro_len; memcpy(report_eme->vpc_name.s, current->esct->vpc->organizationname, vpc_name_len); } report_eme->vpc_host.len = vpc_host_len; if (vpc_host_len == 0) { report_eme->vpc_host.s = " "; report_eme->vpc_host.len = 1; } else { report_eme->vpc_host.s = (char *) (report_eme + 1) + callid_len + srid_len + esgwri_len + lro_len + vpc_name_len; memcpy(report_eme->vpc_host.s, current->esct->vpc->hostname, vpc_host_len); } report_eme->timestamp.len = time_len; if (time_len == 0) { report_eme->timestamp.s = " "; report_eme->timestamp.len = 1; } else { report_eme->timestamp.s = (char *) (report_eme + 1) + callid_len + srid_len + esgwri_len + lro_len + vpc_name_len + vpc_host_len; memcpy(report_eme->timestamp.s, current->esct->datetimestamp, time_len); } report_eme->result.len = result_len; report_eme->result.s = (char *) (report_eme + 1) + callid_len + srid_len + esgwri_len + lro_len + vpc_name_len + vpc_host_len + time_len; memcpy(report_eme->result.s, current->esct->result, result_len); report_eme->disposition.len = disposition_len; report_eme->disposition.s = (char *) (report_eme + 1) + callid_len + srid_len + esgwri_len + lro_len + vpc_name_len + vpc_host_len + time_len + result_len; memcpy(report_eme->disposition.s, current->esct->disposition, disposition_len); LM_DBG(" --- REPORT - CALLID %.*s \n\n", report_eme->callid.len, report_eme->callid.s); LM_DBG(" --- REPORT - ESGWRI %.*s \n\n", report_eme->esgwri.len, report_eme->esgwri.s); LM_DBG(" --- REPORT - ERT-RESN %d \n\n", report_eme->ert_resn); LM_DBG(" --- REPORT - ERT-NPA %d \n\n", report_eme->ert_npa); LM_DBG(" --- REPORT - ERT-SRID %.*s \n\n", report_eme->ert_srid.len, report_eme->ert_srid.s); LM_DBG(" --- REPORT - LRO %.*s \n\n", report_eme->lro.len, report_eme->lro.s); LM_DBG(" --- REPORT - VPC - NAME %.*s \n\n", report_eme->vpc_name.len, report_eme->vpc_name.s); LM_DBG(" --- REPORT - VPC - HOST %.*s \n\n", report_eme->vpc_host.len, report_eme->vpc_host.s); LM_DBG(" --- REPORT - TIMESTAMP %.*s \n\n", report_eme->timestamp.len, report_eme->timestamp.s); LM_DBG(" --- REPORT - RESULT %.*s \n\n", report_eme->result.len, report_eme->result.s); LM_DBG(" --- REPORT - DISPOSITION %.*s \n\n", report_eme->disposition.len, report_eme->disposition.s); LM_DBG(" --- TABLE_REPORT %.*s \n\n", table_report.len, table_report.s); if (report(report_eme, db_url, table_report) != 1) { LM_DBG("****** INSERT NOK\n"); pkg_free(report_eme); return -1; } LM_DBG("****** INSERT OK\n"); pkg_free(report_eme); return 1; } /* retreives esgwrifrom the list db_esrn_esgwri * using srid(selectiveRoutingID), resn(routingESN) and npa. */ int emergency_routing(char *srid, int resn, int npa, char** esgwri, rw_lock_t *ref_lock ) { lock_start_read(ref_lock); struct esrn_routing* esrn_domain = *db_esrn_esgwri; LM_DBG("SRID = %s \n", srid); while (esrn_domain != NULL) { LM_DBG("CMP SRID= %.*s \n", esrn_domain->srid.len, esrn_domain->srid.s); LM_DBG("CMP RESN= %d \n", esrn_domain->resn); LM_DBG("CMP NPA = %d \n", esrn_domain->npa); if (strncmp(esrn_domain->srid.s, srid, esrn_domain->srid.len) == 0) { if ((esrn_domain->resn == resn)&&(esrn_domain->npa == npa)) { char* temp = pkg_malloc(sizeof (char) * esrn_domain->esgwri.len + 1); if (!temp) { LM_ERR("no more memory\n"); lock_stop_read(ref_lock); return -1; } memcpy(temp, esrn_domain->esgwri.s, esrn_domain->esgwri.len); temp[esrn_domain->esgwri.len] = 0; *esgwri = temp; lock_stop_read(ref_lock); return 1; } } esrn_domain = esrn_domain->next; } lock_stop_read(ref_lock); return -1; } /* get provider pointer from emergency service provider table * get 3 types of provider, depend on its attribution: * 0 - source provider * 1 - VPC provider * 2 - vsp provider */ struct service_provider* get_provider(struct sip_msg *msg, int attr, rw_lock_t *ref_lock ) { int vsp_addr_len; char *vsp_addr; lock_start_read(ref_lock); struct service_provider* provider = *db_service_provider; while (provider != NULL) { LM_DBG("***attr:%d\n ", provider->attribution); if (provider->attribution == attr ){ if (provider->attribution == 2){ // search ip source vsp_addr = ip_addr2a(&msg->rcv.src_ip); vsp_addr_len = strlen(vsp_addr); if ( (provider->nodeIP.len == vsp_addr_len) && (strncmp(vsp_addr, provider->nodeIP.s, vsp_addr_len) == 0)) { LM_DBG(" FOUND IP SOURCE\n "); lock_stop_read(ref_lock); return provider; } }else{ lock_stop_read(ref_lock); return provider; } } provider = provider->next; } lock_stop_read(ref_lock); return NULL; } /* select data from emergency_routing and put in memory * coluns keys: srid(selectiveRoutingID), resn(routingESN) and npa. * coluns translate: esgwri */ int get_db_routing(str table_name, rw_lock_t *ref_lock ){ db_key_t query_cols[] = {&id_col, &srid_col, &resn_col, &npa_col, &esgwri_col}; db_res_t * res; db_val_t * values; db_row_t * rows; str esgwri; str SRID; int RESN; int NPA; int nr_rows, i, size; struct esrn_routing *esrn_cell, *old_list, *it, *aux, *new_list; struct esrn_routing *init_esrn = NULL; db_funcs.use_table(db_con, &table_name); /* select value from routing table * the keys of routing table: selectiveRoutingID, routingESN, npa * the result of routing lookup: esgwri */ if (db_funcs.query(db_con, 0, 0, 0, query_cols, 0, 5, 0, &res) != 0) { LM_ERR("Failure to issue query\n"); return -1; } nr_rows = RES_ROW_N(res); rows = RES_ROWS(res); new_list = NULL; LM_DBG("NUMBER OF LINES ROUTING %d \n", nr_rows); for (i = 0; i < nr_rows; i++) { values = ROW_VALUES(rows + i); if (VAL_NULL(values) || (VAL_TYPE(values) != DB_INT)) { LM_ERR("Invalid value returned 1\n"); goto end; } if (VAL_NULL(values + 1) || (VAL_TYPE(values + 1) != DB_STR && VAL_TYPE(values + 1) != DB_STRING)) { LM_ERR("Invalid translated returned 2\n"); goto end; } if (VAL_TYPE(values + 1) == DB_STR) { SRID = VAL_STR(values + 1); } else { SRID.s = (char*) VAL_STRING(values + 1); SRID.len = strlen(SRID.s); } if (VAL_NULL(values + 2) || (VAL_TYPE(values + 2) != DB_INT)) { LM_ERR("Invalid translated returned 3\n"); goto end; } RESN = VAL_INT(values + 2); if (VAL_NULL(values + 3) || (VAL_TYPE(values + 3) != DB_INT)) { LM_ERR("Invalid translated returned 4\n"); goto end; } NPA = VAL_INT(values + 3); if (VAL_NULL(values + 4) || (VAL_TYPE(values + 4) != DB_STR && VAL_TYPE(values + 4) != DB_STRING)) { LM_ERR("Invalid translated returned 5\n"); goto end; } if (VAL_TYPE(values + 4) == DB_STR) { esgwri = VAL_STR(values + 4); } else { esgwri.s = (char*) VAL_STRING(values + 4); esgwri.len = strlen(esgwri.s); } size = sizeof (struct esrn_routing)+SRID.len + esgwri.len; esrn_cell = shm_malloc(size); if (!esrn_cell) { LM_ERR("no more shm\n"); goto end; } memset(esrn_cell, 0, size); esrn_cell->srid.len = SRID.len; esrn_cell->srid.s = (char *) (esrn_cell + 1); memcpy(esrn_cell->srid.s, SRID.s, SRID.len); esrn_cell->resn = RESN; esrn_cell->npa = NPA; esrn_cell->esgwri.len = esgwri.len; esrn_cell->esgwri.s = (char *) (esrn_cell + 1) + SRID.len; memcpy(esrn_cell->esgwri.s, esgwri.s, esgwri.len); LM_DBG("-SRID %.*s \n", SRID.len, SRID.s); LM_DBG("-RESN %d \n", RESN); LM_DBG("-NPA %d \n", NPA); LM_DBG("-esgwri %.*s \n", esgwri.len, esgwri.s); if (new_list != NULL) { new_list->next = esrn_cell; new_list = esrn_cell; } else { new_list = esrn_cell; init_esrn = new_list; } } new_list = init_esrn; lock_start_write(ref_lock); old_list = *db_esrn_esgwri; *db_esrn_esgwri = init_esrn; lock_stop_write(ref_lock); it = old_list; while (it) { aux = it; it = it->next; shm_free(aux); } end: db_funcs.free_result(db_con, res); return 1; } /* select data from emergency_service_prvider for put in xml to VPC * coluns key: attribution and nodeIP. * coluns attributs: OrganizationName, hostId, nenaId, contact, certUri. */ int get_db_provider(str table_name, rw_lock_t *ref_lock ){ db_key_t query_cols[] = {&id_col, &organizationName_col, &hostId_col, &nenaId_col, &contact_col, &certUri_col, &nodeIP_col, &attribution_col}; db_res_t * res; db_val_t * values; db_row_t * rows; str OrganizationName; str hostId; str nenaId; str contact; str certUri; str nodeIP; int attribution; int nr_rows, i, size; struct service_provider *provider_cell, *old_list, *it, *aux, *new_list; struct service_provider *init_provider = NULL; db_funcs.use_table(db_con, &table_name); if (db_funcs.query(db_con, 0, 0, 0, query_cols, 0, 8, 0, &res) != 0) { LM_ERR("Failure to issue query\n"); return -1; } nr_rows = RES_ROW_N(res); rows = RES_ROWS(res); new_list = NULL; LM_DBG("NUMBER OF LINES %d \n", nr_rows); for (i = 0; i < nr_rows; i++) { values = ROW_VALUES(rows + i); if (VAL_NULL(values) || (VAL_TYPE(values) != DB_INT)) { LM_ERR("Invalid value returned 1\n"); goto end; } if (VAL_NULL(values + 1) || (VAL_TYPE(values + 1) != DB_STR && VAL_TYPE(values + 1) != DB_STRING)) { LM_ERR("Invalid translated returned 2\n"); goto end; } if (VAL_TYPE(values + 1) == DB_STR) { OrganizationName = VAL_STR(values + 1); } else { OrganizationName.s = (char*) VAL_STRING(values + 1); OrganizationName.len = strlen(OrganizationName.s); } if (VAL_NULL(values + 2) || (VAL_TYPE(values + 2) != DB_STR && VAL_TYPE(values + 2) != DB_STRING)) { LM_ERR("Invalid translated returned 2\n"); goto end; } if (VAL_TYPE(values + 2) == DB_STR) { hostId = VAL_STR(values + 2); } else { hostId.s = (char*) VAL_STRING(values + 2); hostId.len = strlen(hostId.s); } if (VAL_NULL(values + 3) || (VAL_TYPE(values + 3) != DB_STR && VAL_TYPE(values + 3) != DB_STRING)) { LM_ERR("Invalid translated returned 3\n"); goto end; } if (VAL_TYPE(values + 3) == DB_STR) { nenaId = VAL_STR(values + 3); } else { nenaId.s = (char*) VAL_STRING(values + 3); nenaId.len = strlen(nenaId.s); } if (VAL_NULL(values + 4) || (VAL_TYPE(values + 4) != DB_STR && VAL_TYPE(values + 4) != DB_STRING)) { LM_ERR("Invalid translated returned 4\n"); goto end; } if (VAL_TYPE(values + 4) == DB_STR) { contact = VAL_STR(values + 4); } else { contact.s = (char*) VAL_STRING(values + 4); contact.len = strlen(contact.s); } if (VAL_NULL(values + 5) || (VAL_TYPE(values + 5) != DB_STR && VAL_TYPE(values + 5) != DB_STRING)) { LM_ERR("Invalid translated returned 3\n"); goto end; } if (VAL_TYPE(values + 5) == DB_STR) { certUri = VAL_STR(values + 5); } else { certUri.s = (char*) VAL_STRING(values + 5); certUri.len = strlen(certUri.s); } if (VAL_NULL(values + 6) || (VAL_TYPE(values + 6) != DB_STR && VAL_TYPE(values + 6) != DB_STRING)) { LM_ERR("Invalid translated returned 6\n"); goto end; } if (VAL_TYPE(values + 6) == DB_STR) { nodeIP = VAL_STR(values + 6); } else { nodeIP.s = (char*) VAL_STRING(values + 6); nodeIP.len = strlen(nodeIP.s); } if (VAL_NULL(values + 7) || (VAL_TYPE(values + 7) != DB_INT)) { LM_ERR("Invalid translated returned 7\n"); goto end; } attribution = VAL_INT(values + 7); if (attribution == 0){ if (hostId.len == 0 || contact.len == 0) { mandatory_parm[0] = '1'; mandatory_parm[1] = 0; } else{ mandatory_parm[0] = '0'; mandatory_parm[1] = 0; } } size = sizeof (struct service_provider)+ nodeIP.len + OrganizationName.len + hostId.len + nenaId.len + contact.len + certUri.len; provider_cell = shm_malloc(size); if (!provider_cell) { LM_ERR("no more shm\n"); goto end; } memset(provider_cell, 0, size); provider_cell->nodeIP.len = nodeIP.len; provider_cell->nodeIP.s = (char *) (provider_cell + 1); memcpy(provider_cell->nodeIP.s, nodeIP.s, nodeIP.len); provider_cell->OrganizationName.len = OrganizationName.len; provider_cell->OrganizationName.s = (char *) (provider_cell + 1) + nodeIP.len; memcpy(provider_cell->OrganizationName.s, OrganizationName.s, OrganizationName.len); provider_cell->hostId.len = hostId.len; provider_cell->hostId.s = (char *) (provider_cell + 1) + nodeIP.len + OrganizationName.len; memcpy(provider_cell->hostId.s, hostId.s, hostId.len); provider_cell->nenaId.len = nenaId.len; provider_cell->nenaId.s = (char *) (provider_cell + 1) + nodeIP.len + OrganizationName.len + hostId.len; memcpy(provider_cell->nenaId.s, nenaId.s, nenaId.len); provider_cell->contact.len = contact.len; provider_cell->contact.s = (char *) (provider_cell + 1) + nodeIP.len + OrganizationName.len + hostId.len + nenaId.len; memcpy(provider_cell->contact.s, contact.s, contact.len); provider_cell->certUri.len = certUri.len; provider_cell->certUri.s = (char *) (provider_cell + 1) + nodeIP.len + OrganizationName.len + hostId.len + nenaId.len + contact.len; memcpy(provider_cell->certUri.s, certUri.s, certUri.len); provider_cell->attribution = attribution; if (new_list != NULL) { new_list->next = provider_cell; new_list = provider_cell; } else { new_list = provider_cell; init_provider = new_list; } } new_list = init_provider; lock_start_write(ref_lock); old_list = *db_service_provider; *db_service_provider = init_provider; lock_stop_write(ref_lock); it = old_list; while (it) { aux = it; it = it->next; shm_free(aux); } end: db_funcs.free_result(db_con, res); return 1; } opensips-2.2.2/modules/emergency/report_emergency.h000066400000000000000000000060401300170765700225330ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "../../socket_info.h" #include "../../route_struct.h" #include "../../ip_addr.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_pai.h" #include "../../parser/parse_ppi.h" #include "../../parser/parse_rpid.h" #include "../../parser/parse_from.h" #include "../../regexp.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "../../rw_locking.h" #include "../../timer.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../forward.h" #include "../rr/api.h" #include "sip_emergency.h" db_func_t db_funcs; db_con_t *db_con; struct emergency_report { str callid; str ert_srid; int ert_resn; int ert_npa; str esgwri; str lro; str vpc_host; str vpc_name; str timestamp; str result; str disposition; }; struct esrn_routing { str srid; int resn; int npa; str esgwri; struct esrn_routing *next; }; struct esrn_routing **db_esrn_esgwri; struct service_provider { str nodeIP; str OrganizationName; str hostId; str nenaId; str contact; str certUri; int attribution; struct service_provider *next; }; struct service_provider **db_service_provider; char* mandatory_parm; #define ACK_TIME 3 #define BYE_TIME 10 int report(struct emergency_report *report, str db_url, str table_report); int collect_data(struct node *current, str db_url, str table_report); int emergency_routing(char *srid, int resn, int npa, char** esgwri, rw_lock_t *ref_lock ); int get_db_routing(str table_name, rw_lock_t *ref_lock ); int get_db_provider(str table_name, rw_lock_t *ref_lock ); struct service_provider* get_provider(struct sip_msg *msg, int attr, rw_lock_t *ref_lock ); opensips-2.2.2/modules/emergency/sip_emergency.c000066400000000000000000000663361300170765700220240ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include "sip_emergency.h" const char *GEO_LOCATION_ROUTING = "Geolocation-Routing"; const char *GEO_LOCATION_ROUTING_YES = "yes"; const char *GEO_LOCATION = "Geolocation"; const char *LOCATION_TAG_BEGIN = ""; const char *LOCATION_TAG_END = ""; const char *NEW_LINE = "\n"; const char *CONTENT_TYPE_PIDF = "Content-Type: application/pidf+xml"; const char *PRESENCE_START = "\n" #define PAI_SUFFIX_LEN_II (sizeof(PAI_SUFFIX_II)-1) #define P_ASSERTED_HDR "P-Asserted-Identity: " #define CONTACT_SUFFIX_LEN (sizeof(CONTACT_SUFFIX)-1) #define MAXNUMBERLEN 31 struct lump *l; struct rr_binds rr_api; /* verify if the INVITE has the header Geolocation-Routing with the value "yes" */ int check_geolocation_header(struct sip_msg *msg) { LM_DBG(" --- check_geolocation_header\n\n"); if (parse_headers(msg, HDR_OTHER_F, 0) == -1) { LM_ERR("NO HEADER header\n"); return 0; } LM_DBG(" --- check_geolocation_header --- OK\n\n"); struct hdr_field* atual = msg->headers; while (atual != NULL) { char* name = pkg_malloc(sizeof (char) * atual->name.len); char* body = pkg_malloc(sizeof (char) * atual->body.len); strncpy(name, atual->name.s, atual->name.len); strncpy(body, atual->body.s, atual->body.len); char* geo = strstr(name, GEO_LOCATION_ROUTING); char* val = strstr(body, GEO_LOCATION_ROUTING_YES); if (geo != NULL && val != NULL) { pkg_free(name); pkg_free(body); return 1; } atual = atual->next; pkg_free(name); pkg_free(body); } return 0; } /* * - extracts state and expire values from Subscription_state header from Notify */ int get_subscription_state_header(struct sip_msg *msg, char** subs_state, char** expires) { char *state_aux; char *expires_aux; char *body; str pt_state; str pt_expires; str pattern_state; str replacement_state; str replacement_expires; LM_DBG(" --- get_subscription_state_header\n\n"); if (parse_headers(msg, HDR_OTHER_F, 0) == -1) { LM_ERR("NO HEADER header\n"); return 0; } struct hdr_field* atual = msg->headers; while (atual != NULL) { LM_DBG(" --- HEADERS: %.*s\n\n", atual->name.len, atual->name.s ); if ( strncmp(atual->name.s , SUBSCRIPTION_STATE, atual->name.len) == 0){ body = pkg_malloc(sizeof (char)*atual->body.len + 1); if (body == NULL) { LM_ERR("no more pkg memory\n"); return 0; } memcpy( body, atual->body.s, atual->body.len); body[atual->body.len] = 0; if ( strstr(body , "terminated") != NULL){ state_aux = "terminated"; *subs_state = state_aux; *expires = NULL; return 1; } LM_DBG(" --- Subscription_state body: %.*s\n\n", atual->body.len, atual->body.s); state_aux = pkg_malloc(sizeof (char)*MAXNUMBERLEN); if (state_aux == NULL) { LM_ERR("no more pkg memory\n"); return 0; } memset(state_aux, 0,MAXNUMBERLEN); pt_state.s = state_aux; pt_state.len = MAXNUMBERLEN - 1; pattern_state.s = "^\\s*([a-z]+)\\s*;\\s*expires\\s*=\\s*([0-9]+)"; pattern_state.len = strlen(pattern_state.s); replacement_state.s = "\\1"; replacement_state.len = strlen(replacement_state.s); if (reg_replace(pattern_state.s, replacement_state.s, atual->body.s, &pt_state) == 1) { LM_DBG(" --- REPLACE OK\n\n"); *subs_state = state_aux; expires_aux = pkg_malloc(sizeof (char)*MAXNUMBERLEN); if (expires_aux == NULL) { LM_ERR("no more pkg memory\n"); return 0; } memset(expires_aux, 0,MAXNUMBERLEN); pt_expires.s = expires_aux; pt_expires.len = MAXNUMBERLEN - 1; replacement_expires.s = "\\2"; replacement_expires.len = strlen(replacement_expires.s); if (reg_replace(pattern_state.s, replacement_expires.s, atual->body.s, &pt_expires) == 1) { *expires = expires_aux; return 1; } } LM_DBG(" --- REPLACE NOK\n\n"); return 0; } atual = atual->next; } return 0; } /* * - extracts state and expire values from Subscription_state header from Notify */ int get_expires_header(struct sip_msg *msg, char** expires) { if (msg->expires!=NULL && msg->expires->body.len > 0){ LM_DBG("EXPIRES: %.*s \n", msg->expires->body.len, msg->expires->body.s); *expires = pkg_malloc(sizeof (char) * msg->expires->body.len + 1); if (*expires == NULL) { LM_ERR("NO MEMORY\n"); return 0; } memset(*expires, '\0', msg->expires->body.len + 1); strncpy(*expires, msg->expires->body.s, msg->expires->body.len); return 1; } return 0; } /* * - extracts state and expire values from Subscription_state header from Notify */ int get_event_header(struct sip_msg *msg, char** subs_callid, char** from_tag) { char* callid_aux; char* ftag_aux; str pt_callid; str pt_ftag; str pattern_callid; str replacement_callid; str replacement_ftag; LM_DBG(" --- get_event_header\n\n"); if (parse_headers(msg, HDR_OTHER_F, 0) == -1) { LM_ERR("NO HEADER header\n"); return 0; } if (msg->event != NULL && msg->event->body.len > 0){ LM_DBG(" --- Event body: %.*s\n\n",msg->event->body.len, msg->event->body.s); callid_aux = pkg_malloc(sizeof (char)*MAXNUMBERLEN); if (callid_aux == NULL) { LM_ERR("no more pkg memory\n"); return 0; } memset(callid_aux, 0,MAXNUMBERLEN); pt_callid.s = callid_aux; pt_callid.len = MAXNUMBERLEN - 1; pattern_callid.s = "call-id\\s*=\\s*[\x22]?([\x23-\x7E]+)\\s*[\x22]?\\s*;\\s*from-tag\\s*=\\s*([-a-z0-9]+)"; pattern_callid.len = strlen(pattern_callid.s); replacement_callid.s = "\\1"; replacement_callid.len = strlen(replacement_callid.s); if (reg_replace(pattern_callid.s, replacement_callid.s, msg->event->body.s, &pt_callid) == 1) { LM_DBG(" --- REPLACE OK\n\n"); *subs_callid = callid_aux; ftag_aux = pkg_malloc(sizeof (char)*MAXNUMBERLEN); if (ftag_aux == NULL) { LM_ERR("no more pkg memory\n"); return 0; } memset(ftag_aux, 0,MAXNUMBERLEN); pt_ftag.s = ftag_aux; pt_ftag.len = MAXNUMBERLEN - 1; replacement_ftag.s = "\\2"; replacement_ftag.len = strlen(replacement_ftag.s); if (reg_replace(pattern_callid.s, replacement_ftag.s, msg->event->body.s, &pt_ftag) == 1) { LM_DBG(" --- REPLACE OK II\n\n"); *from_tag = ftag_aux; return 1; } pkg_free(ftag_aux); } pkg_free(callid_aux); LM_DBG(" --- REPLACE NOK\n\n"); } *subs_callid = NULL; *from_tag = NULL; return 0; } /* retreives Geolocation * - extracts the headers Geolocation from the INVITE,this values will be used by the VPC to obtain the location information form the LIS */ int get_geolocation_header(struct sip_msg *msg, char** locationHeader) { char* locationTotalHeader = ""; char* name; char* body; LM_DBG(" --- get_geolocation_header\n\n"); if (parse_headers(msg, HDR_OTHER_F, 0) == -1) { LM_ERR("NO HEADER header\n"); return -1; } LM_DBG(" --- get_geolocation_header --- INICIO %s \n\n", locationTotalHeader); struct hdr_field* atual = msg->headers; while (atual != NULL) { name = pkg_malloc(sizeof (char) * atual->name.len + 1); if (name == NULL) { LM_ERR("NO MEMORY\n"); return -1; } memcpy( name, atual->name.s, atual->name.len); name[atual->name.len] = 0; body = pkg_malloc(sizeof (char) * atual->body.len + 1); if (body == NULL) { LM_ERR("NO MEMORY\n"); return -1; } memcpy( body, atual->body.s, atual->body.len); body[atual->body.len] = 0; char* geo = strstr(name, GEO_LOCATION); char* httpBody = strstr(body, "http"); char* geoRouting = strstr(name, GEO_LOCATION_ROUTING); pkg_free(name); pkg_free(body); if (geo != NULL && httpBody != NULL && geoRouting == NULL) { int TotalHeader_len = strlen(locationTotalHeader); int new_size = atual->body.len + TotalHeader_len + 1; new_size += strlen(LOCATION_TAG_BEGIN) + strlen(LOCATION_TAG_END); new_size += strlen(NEW_LINE); char* aux = pkg_malloc(sizeof (char) * new_size); if (aux == NULL) { LM_ERR("NO MEMORY\n"); return -1; } strcpy(aux, locationTotalHeader); strcat(aux, LOCATION_TAG_BEGIN); strncat(aux, atual->body.s, atual->body.len); strcat(aux, LOCATION_TAG_END); strcat(aux, NEW_LINE); aux[new_size - 1] = 0; if (TotalHeader_len != 0) pkg_free(locationTotalHeader); locationTotalHeader = aux; LM_DBG(" --- get_geolocation_header ATUAL %s \n\n", locationTotalHeader); } atual = atual->next; } *locationHeader = locationTotalHeader; LM_DBG(" --- get_geolocation_header FINAL %s \n\n", *locationHeader); return 1; } /* this function tries to find callback number (CBN) in the given INVITE - first tries to get from the PAI headers , then PPI , then RDID an finally tries the From header */ int found_CBN(struct sip_msg *msg, char** cbn_aux) { str cbn; str pattern, pattern_sip, replacement; int found_cbn; char* header_aux; cbn.s = *cbn_aux; cbn.len = MAX_URI_SIZE; pattern.s = "tel:([+]*[-0-9]+)"; pattern.len = strlen(pattern.s); pattern_sip.s = "sips?:([+]*[-0-9]+)"; pattern_sip.len = strlen(pattern_sip.s); replacement.s = "\\1"; replacement.len = strlen(replacement.s); found_cbn = 0; // First lookup CBN in P-Asserted-Identity header if (parse_pai_header(msg) == 0) { LM_DBG("****** PAI: %.*s\n", msg->pai->body.len, msg->pai->body.s); CP_STR_CHAR(msg->pai->body, header_aux); if (reg_replace(pattern.s, replacement.s, header_aux, &cbn) == 1) { found_cbn = 1; LM_DBG("****** PATTERN OK\n"); LM_DBG("****** REG_REPLACE: %.*s\n", cbn.len, cbn.s); } else { if (reg_replace(pattern_sip.s, replacement.s, header_aux, &cbn) == 1) { found_cbn = 1; LM_DBG("****** PATTERN OK\n"); LM_DBG("****** REG_REPLACE: %.*s\n", cbn.len, cbn.s); } else { memset(cbn.s, 0, MAX_URI_SIZE); LM_ERR("****** PATTERN NAO OK \n"); } } pkg_free(header_aux); } // Second lookup CBN in P-Preferred-Identity header if (found_cbn == 0) { if (parse_ppi_header(msg) == 0) { LM_DBG("****** PPI: %.*s\n", msg->ppi->body.len, msg->ppi->body.s); CP_STR_CHAR(msg->ppi->body, header_aux); if (reg_replace(pattern.s, replacement.s, header_aux, &cbn) == 1) { found_cbn = 1; LM_DBG("****** PATTERN OK\n"); LM_DBG("****** REG_REPLACE: %.*s\n", cbn.len, cbn.s); } else { memset(cbn.s, 0, MAX_URI_SIZE); LM_DBG("****** PATTERN NAO OK \n"); } pkg_free(header_aux); } } // After lookup CBN in Remote-Party_ID header if (found_cbn == 0) { if (parse_rpid_header(msg) == 0) { LM_DBG("****** RPID: %.*s\n", msg->rpid->body.len, msg->rpid->body.s); CP_STR_CHAR(msg->rpid->body, header_aux); if (reg_replace(pattern.s, replacement.s, header_aux, &cbn) == 1) { found_cbn = 1; LM_DBG("****** PATTERN OK\n"); LM_DBG("****** REG_REPLACE: %.*s\n", cbn.len, cbn.s); } else { memset(cbn.s, 0, MAX_URI_SIZE); LM_DBG("****** PATTERN NAO OK \n"); } pkg_free(header_aux); } } // Finally lookup CBN in From header if (found_cbn == 0) { if (parse_from_header(msg) == 0) { LM_DBG("****** FROM: %.*s\n", msg->from->body.len, msg->from->body.s); CP_STR_CHAR(msg->from->body, header_aux); if (reg_replace(pattern.s, replacement.s, header_aux, &cbn) == 1) { found_cbn = 1; LM_DBG("****** PATTERN OK\n"); LM_DBG("****** REG_REPLACE: %.*s\n", cbn.len, cbn.s); } else { if (reg_replace(pattern_sip.s, replacement.s, header_aux, &cbn) == 1) { found_cbn = 1; LM_DBG("****** PATTERN OK\n"); LM_DBG("****** REG_REPLACE: %.*s\n", cbn.len, cbn.s); } else { memset(cbn.s, 0, MAX_URI_SIZE); LM_ERR("****** PATTERN NAO OK \n"); cbn.len = 0; } } pkg_free(header_aux); } else { LM_ERR("****** FROM: ERRO"); return -1; } } return 1; error: return -1; } /* verify if event type is dialog */ int check_event_header(struct sip_msg *msg) { LM_DBG(" --- get_event_header\n\n"); if (parse_headers(msg, HDR_OTHER_F, 0) == -1) { LM_ERR("NO HEADER header\n"); return 0; } if( msg->event==NULL || msg->event->body.s==NULL){ LM_ERR("msg without event header\n"); return 0; } LM_DBG(" -----------EVENT HEADER %.*s \n \n", msg->event->body.len, msg->event->body.s); if(strncmp(msg->event->body.s,EVENT_TYPE,6) == 0) return 1; return 0; } // get ip address of opensips server in port that receive INVITE int get_ip_socket(struct sip_msg *msg, char** saddr){ char *socket; struct socket_info** list; struct socket_info* si; list = get_sock_info_list(msg->rcv.proto); if (list == NULL) { LM_ERR("ERROR in SOCKET\n"); return -1; } si = *list; *saddr = NULL; while (si) { if (si->port_no == msg->rcv.dst_port) { socket = pkg_malloc(si->address_str.len + si->port_no_str.len + 3); if (socket == NULL) { LM_ERR("no more pkg memory\n"); return -1; } *saddr = socket; *socket = '@'; socket++; memcpy(socket, si->address_str.s, si->address_str.len); socket = socket + si->address_str.len; *socket = ':'; socket++; memcpy(socket, si->port_no_str.s, si->port_no_str.len); socket = socket + si->port_no_str.len; *socket = 0; LM_DBG(" --- SERVER = %s \n \n", *saddr); break; } si = si->next; } if (*saddr == NULL) { LM_ERR("failed in found ip listen\n"); return -1; } return 1; } /* Includes the headers to the INVITE * - puts the header PAI with the data: * - esqk@ip_opensips:phone=call_back_number * - adds record_route to the INVIE for the opensips be notified when the call ends */ int add_hdr_rpl(struct esct *call_cell, struct sip_msg *msg) { char *s = "", *p = ""; int len = 0; int rp_addr_len; char *rp_addr = "@rp.com"; static str new_header; struct lump_rpl *hdr_lump; int vsp_addr_len; char *vsp_addr = "@vsp.com"; int q = 0; // get source ip address that send INVITE vsp_addr = ip_addr2a(&msg->rcv.src_ip); vsp_addr_len = strlen(vsp_addr); // get ip address of opensips server in port that receive INVITE if (get_ip_socket(msg, &rp_addr) == -1) return -1; rp_addr_len = strlen(rp_addr); int result = atoi(call_cell->result); int range = range_result(result); LM_DBG(" --- range %d", range); if ( (range == 0) && (call_cell->esgwri != empty && strlen(call_cell->esgwri) > 0) && (call_cell->esqk != empty && strlen(call_cell->esqk) > 0)) { len = CONTACT_HDR_LEN + strlen(call_cell->esqk) + strlen(call_cell->esgwri) + rp_addr_len + CONTACT_SUFFIX_LEN + CONTACT_MIDLE_LEN + 9; s = pkg_malloc(len + 1); if (s == NULL) { LM_ERR("no more pkg memory\n"); pkg_free(rp_addr); return -1; } p = s; memcpy(p, CONTACT_HDR, CONTACT_HDR_LEN); p += CONTACT_HDR_LEN; memcpy(p, call_cell->esgwri, strlen(call_cell->esgwri)); p += strlen(call_cell->esgwri); memcpy(p, CONTACT_MIDLE, CONTACT_MIDLE_LEN); p += CONTACT_MIDLE_LEN; *p = '+'; p++; *p = '1'; p++; *p = '-'; p++; memcpy(p, call_cell->esqk, strlen(call_cell->esqk)); p += strlen(call_cell->esqk); memcpy(p, rp_addr, rp_addr_len); p += rp_addr_len; memcpy(p, CONTACT_SUFFIX, CONTACT_SUFFIX_LEN); p += CONTACT_SUFFIX_LEN; *p = '>'; p++; *p = ';'; p++; *p = 'q'; p++; *p = '='; p++; *p = '1'; p++; *p = '\n'; p++; *p = 0; LM_DBG(" --- NEW HEADER = %s \n \n", s); LM_DBG(" --- NEW HEADER = %d \n \n", len); new_header.s = s; new_header.len = len; hdr_lump = add_lump_rpl( msg, new_header.s, new_header.len, LUMP_RPL_HDR ); if ( !hdr_lump ) { LM_ERR("failed to add hdr lump\n"); pkg_free(s); return -1; } q = 1; pkg_free(s); } pkg_free(rp_addr); if (call_cell->lro != empty){ len = CONTACT_HDR_LEN + strlen(call_cell->lro) + vsp_addr_len + CONTACT_SUFFIX_LEN + 5 + q*4; s = pkg_malloc(sizeof (char)*len + 1); if (s == NULL) { LM_ERR("no more pkg memory\n"); return -1; } p = s; memcpy(p, CONTACT_HDR, CONTACT_HDR_LEN); p += CONTACT_HDR_LEN; *p = '+'; p++; *p = '1'; p++; *p = '-'; p++; memcpy(p, call_cell->lro, strlen(call_cell->lro)); p += strlen(call_cell->lro); *p = '@'; p++; memcpy(p, vsp_addr, vsp_addr_len); p += vsp_addr_len; memcpy(p, CONTACT_SUFFIX, CONTACT_SUFFIX_LEN); p += CONTACT_SUFFIX_LEN; if (q == 1){ *p = ';'; p++; *p = 'q'; p++; *p = '='; p++; *p = '2'; p++; } *p = '\n'; p++; *p = 0; LM_DBG(" --- NEW HEADER = %s \n \n", s); LM_DBG(" --- NEW HEADER = %d \n \n", len); new_header.s = s; new_header.len = len; hdr_lump = add_lump_rpl( msg, new_header.s, new_header.len, LUMP_RPL_HDR ); if ( !hdr_lump ) { LM_ERR("failed to add hdr lump\n"); pkg_free(s); return -1; } pkg_free(s); } return 1; } /* Includes the headers to the INVITE * - puts the header PAI with the data: * - esqk@ip_opensips:phone=call_back_number * - adds record_route to the INVIE for the opensips be notified when the call ends */ int add_headers(char *esqk, struct sip_msg *msg, str cbn) { char *s, *p; int len; int s_addr_len; char *s_addr = "@vsp.com"; LM_DBG(" --- F (CALLBACK) \n \n"); int resp = 1; // get ip address of opensips server in port that receive INVITE if (get_ip_socket(msg, &s_addr) == -1){ pkg_free(cbn.s); return -1; } s_addr_len = strlen(s_addr); // if package has already PAI header that delete this header if (msg->pai) { LM_DBG("PAI: [%.*s]\n", msg->pai->body.len, msg->pai->body.s); LM_DBG("PAI: %d \n", msg->pai->len); l = del_lump( msg, msg->pai->name.s - msg->buf, msg->pai->len, HDR_PAI_T); if (l==NULL) { LM_ERR("failed to add del lump\n"); resp = -1; goto end; } } l = anchor_lump(msg, msg->from->body.s+msg->from->body.len-msg->buf+1,HDR_USERAGENT_T); if (l == NULL) { LM_ERR("failed to create anchor lump\n"); resp = -1; goto end; } len = P_ASSERTED_HDR_LEN + strlen(esqk) + s_addr_len + PAI_SUFFIX_LEN + cbn.len + 2; s = pkg_malloc(sizeof (char)*len + 1); if (s == NULL) { LM_ERR("no more pkg memory\n"); return -1; } LM_DBG(" --- CBN_NUMBER = %.*s \n \n", cbn.len, cbn.s); LM_DBG(" --- CBN_NUMBER_LEN = %d \n \n", cbn.len); p = s; memcpy(p, P_ASSERTED_HDR, P_ASSERTED_HDR_LEN); p += P_ASSERTED_HDR_LEN; *p = '+'; p++; *p = '1'; p++; memcpy(p, esqk, strlen(esqk)); p += strlen(esqk); memcpy(p, s_addr, s_addr_len); p += s_addr_len; memcpy(p, PAI_SUFFIX, PAI_SUFFIX_LEN); p += PAI_SUFFIX_LEN; memcpy(p, cbn.s, cbn.len); p += cbn.len; *p = 0; l = insert_new_lump_after(l, s, len, HDR_PAI_T); if (l == NULL) { LM_ERR("failed to insert new lump\n"); resp = -1; goto end; } //rr_api.record_route(msg, NULL); resp = 1; end: pkg_free(cbn.s); pkg_free(s_addr); return resp; } /* Includes the headers to the INVITE * - puts the header PAI with the data: * - esqk@ip_opensips:phone=call_back_number * - adds record_route to the INVIE for the opensips be notified when the call ends */ int add_hdr_PAI(struct sip_msg *msg, str cbn) { char *s, *p; struct lump *l; int len; int s_addr_len; char *s_addr = "@vsp.com"; LM_DBG(" --- F (CALLBACK) \n \n"); int resp; // obtem o endereço ip do opensips que atende na portaque recebeu o INVITE if (get_ip_socket(msg, &s_addr) == -1){ pkg_free(cbn.s); return -1; } s_addr_len = strlen(s_addr); // if package has already PAI header that delete this header if (msg->pai) { LM_DBG("PAI: [%.*s]\n", msg->pai->body.len, msg->pai->body.s); l = del_lump( msg, msg->pai->name.s - msg->buf, msg->pai->len, HDR_PAI_T); if (l==NULL) { resp = -1; goto end; } } l = anchor_lump(msg, msg->from->body.s+msg->from->body.len-msg->buf+2,HDR_USERAGENT_T); if (l == NULL) { resp = -1; goto end; } len = P_ASSERTED_HDR_LEN + s_addr_len + PAI_SUFFIX_LEN_II + cbn.len; s = pkg_malloc(len + 1); if (s == NULL) { LM_ERR("no more pkg memory\n"); return -1; } LM_DBG(" --- CBN_NUMBER = %.*s \n \n", cbn.len, cbn.s); LM_DBG(" --- CBN_NUMBER_LEN = %d \n \n", cbn.len); p = s; memcpy(p, P_ASSERTED_HDR, P_ASSERTED_HDR_LEN); p += P_ASSERTED_HDR_LEN; memcpy(p, cbn.s, cbn.len); p += cbn.len; memcpy(p, s_addr, s_addr_len); p += s_addr_len; memcpy(p, PAI_SUFFIX_II, PAI_SUFFIX_LEN_II); p += PAI_SUFFIX_LEN_II; *p = 0; l = insert_new_lump_after(l, s, len, HDR_PAI_T); if (l == NULL) { LM_ERR("failed to insert new lump\n"); resp = -1; goto end; } //rr_api.record_route(msg, NULL); resp = 1; end: pkg_free(cbn.s); pkg_free(s_addr); return resp; } /* find the body with the type Content-Type: application/pidf+xml * in the INVITE that has multi-body */ int find_body_pidf(struct sip_msg *msg, char** pidf_body) { struct part* mbody_part; struct multi_body *mbody; char *body_start, *body_end; char *body_aux; int size_body = 0; int cont = 0; UNUSED(cont); LM_DBG(" --- FIND PIDF BODY \n \n"); mbody = get_all_bodies(msg); if (mbody == NULL) { LM_ERR("Failed to get bodies\n"); return -1; } mbody_part = mbody->first; while (mbody_part != NULL) { LM_DBG(" --- PIDF BODY %.*s", mbody_part->body.len, mbody_part->body.s); LM_DBG(" --- PIDF BODY COUNT %d", ++cont); if (strstr(mbody_part->body.s, CONTENT_TYPE_PIDF) != NULL) { body_start = strstr(mbody_part->body.s, PRESENCE_START); body_end = strstr(mbody_part->body.s, PRESENCE_END); size_body = body_end - body_start + 11; body_aux = pkg_malloc(size_body); if (body_aux == NULL) { LM_ERR("no more pkg memory\n"); return -1; } memcpy(body_aux, body_start, size_body - 1); body_aux[size_body - 1] = 0; *pidf_body = body_aux; break; } mbody_part = mbody_part->next; } if (*pidf_body == NULL) { *pidf_body = ""; } LM_DBG(" --- FIND PIDF BODY %s \n \n", *pidf_body); return 1; } /* this function is used to make Opensips play the role of a "Call server"in the scenarios I and II * forward the INVITE to the Routing Proxy(scenarios II) or to Redirect(scenarios III) */ int proxy_request(struct sip_msg *msg,char *call_server_hostname) { char* ack_uri; char *ack_aux; int size_new_uri; LM_DBG(" ---role: proxy routing \n"); if (call_server_hostname == NULL) { LM_ERR("emergency call server not defined\n"); return -1; } if ((parse_sip_msg_uri(msg) < 0) || (!msg->parsed_uri.user.s) || (msg->parsed_uri.user.len > MAXNUMBERLEN)) { LM_ERR("cannot parse msg URI\n"); return -1; } LM_DBG(" ---USER: %.*s \n\n", msg->parsed_uri.user.len, msg->parsed_uri.user.s); int server_host_len = strlen(call_server_hostname); size_new_uri = server_host_len + msg->parsed_uri.user.len + 6; ack_aux = pkg_malloc(size_new_uri); if (ack_aux == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return -1; } memset(ack_aux, 0, size_new_uri); ack_uri = ack_aux; memcpy(ack_aux, "sip:", 4); ack_aux += 4; memcpy(ack_aux, msg->parsed_uri.user.s, msg->parsed_uri.user.len); ack_aux += msg->parsed_uri.user.len; *ack_aux = '@'; ack_aux++; memcpy(ack_aux, call_server_hostname, server_host_len); LM_DBG(" ---NEW_URI: %s \n\n", ack_uri); LM_DBG(" ---NEW_URI -TAM : %d \n\n", size_new_uri); if(new_uri_proxy(msg, ack_uri) == -1){ LM_ERR(" ---ERRO EM NEW_URI_PROXY"); return -1; } pkg_free(ack_aux); return 1; } /* forward request to new_uri */ int new_uri_proxy(struct sip_msg *req_msg, char* new_uri ){ int new_uri_len; LM_DBG("NEW_URI_PROXY %s\n", new_uri); new_uri_len = strlen (new_uri); req_msg->new_uri.s = (char*)pkg_malloc(new_uri_len+1); if (req_msg->new_uri.s==0){ LM_ERR("no more pkg\n"); return -1; } memcpy( req_msg->new_uri.s, new_uri, new_uri_len); req_msg->new_uri.s[new_uri_len]=0; req_msg->new_uri.len = strlen(new_uri); req_msg->parsed_uri_ok = 0; return 1; } /* extract contact headers from reply 300 or 302 */ int extract_contact_hdrs(struct sip_msg *reply, char **contact_esgwri, char **contact_lro) { char* contact_hdr; char* contact_hdr_II; LM_DBG ("TRANS REPLY %.*s \n", reply->first_line.u.reply.reason.len, reply->first_line.u.reply.reason.s); LM_DBG ("TRANS REPLY CODE%d \n", reply->first_line.u.reply.statuscode); // check if is 300/302 reply if ((reply->first_line.u.reply.statuscode != 300)&&(reply->first_line.u.reply.statuscode != 302)){ LM_DBG("NO redirect response\n"); return -1; } if (parse_headers(reply, HDR_EOH_F, 0) == -1) { LM_ERR("NO HEADER header\n"); return -1; } // verify if exist contact headers if (reply->contact==0) { LM_DBG("contact hdr not found in sh_rpl\n"); return -1; } contact_hdr = pkg_malloc(reply->contact->body.len + 1); if (contact_hdr == NULL) { LM_ERR("no more pkg memory\n"); return -1; } contact_hdr[reply->contact->body.len] = 0; memcpy(contact_hdr, reply->contact->body.s, reply->contact->body.len); LM_DBG ("TRANS REPLY %s \n", contact_hdr); // verify if exist another contact header if (reply->contact->sibling != NULL){ contact_hdr_II = pkg_malloc(reply->contact->sibling->body.len + 1); if (contact_hdr_II == NULL) { LM_ERR("no more pkg memory\n"); return -1; } contact_hdr_II[reply->contact->sibling->body.len] = 0; memcpy(contact_hdr_II, reply->contact->sibling->body.s, reply->contact->sibling->body.len); LM_DBG ("TRANS REPLY II %s \n", contact_hdr_II); }else{ contact_hdr_II = NULL; } // match de contact headers with information about esgwri and lro if (strstr(contact_hdr, "P-Asserted-Identity") != NULL){ *contact_esgwri = contact_hdr; if (contact_hdr_II != NULL) *contact_lro = contact_hdr_II; }else{ if (contact_hdr_II != NULL){ if (strstr(contact_hdr_II, "P-Asserted-Identity") != NULL){ *contact_esgwri = contact_hdr_II; *contact_lro = contact_hdr; }else{ pkg_free(contact_hdr); pkg_free(contact_hdr_II); return -1; } }else{ *contact_lro = contact_hdr; } } LM_DBG ("TRANS LRO %s \n", *contact_lro); LM_DBG ("TRANS ESGWRI %s \n", *contact_esgwri); return 1; } opensips-2.2.2/modules/emergency/sip_emergency.h000066400000000000000000000063251300170765700220210ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "../../socket_info.h" #include "../../route_struct.h" #include "../../ip_addr.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_pai.h" #include "../../parser/parse_ppi.h" #include "../../parser/parse_rpid.h" #include "../../parser/parse_from.h" #include "../../regexp.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "../../rw_locking.h" #include "../../timer.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../forward.h" #include "../rr/api.h" #include "hash.h" #define CP_STR_CHAR(str_source, char_dest)\ do{ \ char_dest = (char *)pkg_malloc( str_source.len + 1);\ if (!char_dest) {\ LM_ERR("no more shm\n");\ goto error;\ }\ memcpy(char_dest, str_source.s, str_source.len);\ char_dest[str_source.len] = 0;\ } while(0) int check_geolocation_header(struct sip_msg *msg); int get_geolocation_header(struct sip_msg *msg, char** locationHeader); int found_CBN(struct sip_msg *msg, char** cbn); int get_expires_header(struct sip_msg *msg, char** expires); int check_event_header(struct sip_msg *msg); int add_hdr_rpl(struct esct *call_cell,struct sip_msg *msg); int add_headers(char *esqk, struct sip_msg *msg, str cbn); int add_hdr_PAI(struct sip_msg *msg, str cbn); int find_body_pidf(struct sip_msg *msg, char** pidf_body); int proxy_request(struct sip_msg *msg,char *call_server_hostname); int new_uri_proxy(struct sip_msg *req_msg, char* new_uri ); int get_ip_socket(struct sip_msg *msg, char** saddr); int extract_contact_hdrs(struct sip_msg *reply, char **contact_esgwri, char **contact_lro); int get_subscription_state_header(struct sip_msg *msg, char** subs_state, char** expires); int get_event_header(struct sip_msg *msg, char** subs_state, char** expires); int range_result(int result); opensips-2.2.2/modules/emergency/subscriber_emergency.c000077500000000000000000000745211300170765700233720ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include "subscriber_emergency.h" #define INIT 0 #define NOTIFY_WAIT 1 #define PENDING 2 #define ACTIVE 3 #define TERMINATED 4 /*Create cell to control Subscriber Dialog States This cell save this information: - Dialog Id: .Callid .rem_tag .local_tag - expires - Local_uri - Remote_uri - Notifier_uri - INVITE's Callid - Event body - State */ int create_subscriber_cell(struct sip_msg* reply, struct parms_cb* params_cb){ str* callid = NULL; int expires= 0; struct to_body *pto= NULL, *pfrom = NULL; int size_subs_cell; int vsp_addr_len; char *vsp_addr = "@vsp.com"; time_t rawtime; int time_now; struct sm_subscriber *subs_cell = NULL; char *p; unsigned int hash_code; callid= (str*) pkg_malloc (sizeof (str)); if (callid == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } /*Verify repĺy is OK and get callid and expires from response*/ if ( !extract_reply_headers(reply, callid, expires)){ LM_ERR("fail in extract headers\n"); pkg_free(callid); return 0; } /*get From header fields */ pfrom = get_from(reply); LM_DBG("PFROM: %.*s \n ", pfrom->uri.len, pfrom->uri.s ); if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0){ LM_ERR("reply without tag value \n"); pkg_free(callid); return 0; } /*get To header fields */ pto = get_to(reply); LM_DBG("PTO: %.*s \n ", pto->uri.len, pto->uri.s ); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); pkg_free(callid); return 0; } // get source ip address that send INVITE vsp_addr = ip_addr2a(&reply->rcv.src_ip); vsp_addr_len = strlen(vsp_addr); time(&rawtime); time_now = (int)rawtime; LM_DBG("TIME : %d \n", (int)rawtime ); /* build subscriber cell */ size_subs_cell = sizeof (struct sm_subscriber) + (2 * sizeof(struct dialog_id)) + callid->len + pfrom->tag_value.len + pto->tag_value.len + pfrom->uri.len + pto->uri.len + params_cb->callid_ori.len + params_cb->event.len + params_cb->from_tag.len + vsp_addr_len + 9 ; subs_cell = pkg_malloc(size_subs_cell + 1); if (!subs_cell) { LM_ERR("no more shm\n"); return 0; } memset(subs_cell, 0, size_subs_cell + 1); subs_cell->expires = expires; subs_cell->timeout = TIMER_N + time_now; LM_DBG("SUBS_TIMEOUT: %d \n ", subs_cell->timeout ); subs_cell->version = -1; subs_cell->dlg_id = (struct dialog_id*)(subs_cell + 1); subs_cell->dlg_id->callid.len = callid->len; subs_cell->dlg_id->callid.s = (char *) (subs_cell->dlg_id + 1); memcpy(subs_cell->dlg_id->callid.s, callid->s, callid->len); LM_DBG("SUBS_CALLID: %.*s \n ", subs_cell->dlg_id->callid.len, subs_cell->dlg_id->callid.s ); subs_cell->dlg_id->local_tag.len = pfrom->tag_value.len; subs_cell->dlg_id->local_tag.s = (char *) (subs_cell->dlg_id + 1) + callid->len; memcpy(subs_cell->dlg_id->local_tag.s, pfrom->tag_value.s, pfrom->tag_value.len); LM_DBG("SUBS_FROM_TAG: %.*s \n ", subs_cell->dlg_id->local_tag.len, subs_cell->dlg_id->local_tag.s ); subs_cell->dlg_id->rem_tag.len = pto->tag_value.len; subs_cell->dlg_id->rem_tag.s = (char *) (subs_cell->dlg_id + 1) + callid->len + pfrom->tag_value.len; memcpy(subs_cell->dlg_id->rem_tag.s, pto->tag_value.s, pto->tag_value.len); LM_DBG("SUBS_TO_TAG: %.*s \n ", subs_cell->dlg_id->rem_tag.len, subs_cell->dlg_id->rem_tag.s ); p = (char *)(subs_cell->dlg_id + 1) + callid->len + pfrom->tag_value.len + pto->tag_value.len; subs_cell->call_dlg_id = (struct dialog_id*)p; subs_cell->call_dlg_id->callid.len= params_cb->callid_ori.len; subs_cell->call_dlg_id->callid.s = (char *) (subs_cell->call_dlg_id + 1); memcpy(subs_cell->call_dlg_id->callid.s, params_cb->callid_ori.s, params_cb->callid_ori.len); LM_DBG("SUBS_CALLID_ORI: %.*s \n ", subs_cell->call_dlg_id->callid.len, subs_cell->call_dlg_id->callid.s ); subs_cell->call_dlg_id->local_tag.len= params_cb->from_tag.len; subs_cell->call_dlg_id->local_tag.s = (char *) (subs_cell->call_dlg_id + 1) + params_cb->callid_ori.len; memcpy(subs_cell->call_dlg_id->local_tag.s, params_cb->from_tag.s, params_cb->from_tag.len); LM_DBG("SUBS_FROMTAG_event: %.*s \n ", subs_cell->call_dlg_id->local_tag.len, subs_cell->call_dlg_id->local_tag.s ); subs_cell->loc_uri.len = pfrom->uri.len; subs_cell->loc_uri.s = (char *) (subs_cell->call_dlg_id + 1) + params_cb->callid_ori.len + params_cb->from_tag.len; memcpy(subs_cell->loc_uri.s,pfrom->uri.s,pfrom->uri.len); LM_DBG("SUBS_LOC_URI: %.*s \n ", subs_cell->loc_uri.len, subs_cell->loc_uri.s ); subs_cell->rem_uri.len= pto->uri.len; subs_cell->rem_uri.s = (char *) (subs_cell->call_dlg_id + 1) + params_cb->callid_ori.len + params_cb->from_tag.len + pfrom->uri.len; memcpy(subs_cell->rem_uri.s, pto->uri.s, pto->uri.len); LM_DBG("SUBS_REM_URI: %.*s \n ", subs_cell->rem_uri.len, subs_cell->rem_uri.s ); subs_cell->event.len= params_cb->event.len; subs_cell->event.s = (char *) (subs_cell->call_dlg_id + 1) + params_cb->callid_ori.len + params_cb->from_tag.len + pfrom->uri.len + pto->uri.len; memcpy(subs_cell->event.s, params_cb->event.s, params_cb->event.len); LM_DBG("SUBS_EVENT: %.*s \n ", subs_cell->event.len, subs_cell->event.s ); subs_cell->contact.len = vsp_addr_len + 9; subs_cell->contact.s = (char *) (subs_cell->call_dlg_id + 1) + params_cb->callid_ori.len + params_cb->from_tag.len + pfrom->uri.len + pto->uri.len + params_cb->event.len; memcpy(subs_cell->contact.s, "sip:test@", 9); memcpy(subs_cell->contact.s + 9, vsp_addr, vsp_addr_len); LM_DBG("SUBS_CONTACT: %.*s \n ", subs_cell->contact.len, subs_cell->contact.s ); subs_cell->dlg_id->status = NOTIFY_WAIT; hash_code= core_hash(&subs_cell->dlg_id->callid, 0, subst_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); if(insert_shtable(subs_htable, hash_code,subs_cell) == NULL){ LM_ERR("inserting new record in subs_htable\n"); } pkg_free(subs_cell); pkg_free(callid); return 1; } /* Verify is reply OK and get callid and expires */ int extract_reply_headers(struct sip_msg* reply, str* callid, int expires){ /* get dialog information from reply message: callid, to_tag, from_tag */ if(reply == NULL){ LM_ERR("no reply message\n "); return 0; } if ( parse_headers(reply,HDR_EOH_F, 0) == -1 ){ LM_ERR("error in parsing headers\n"); return 0; } if( reply->callid==NULL || reply->callid->body.s==NULL){ LM_ERR("reply without callid header\n"); return 0; } *callid = reply->callid->body; if (reply->from->parsed == NULL){ if ( parse_from_header( reply )<0 ){ LM_ERR("reply without From header\n"); return 0; } } if( reply->to==NULL || reply->to->body.s==NULL){ LM_ERR("error in parse TO header\n"); return 0; } if(reply->expires == NULL){ LM_ERR("reply without Expires header\n"); return 0; } /* extract the other necessary information for inserting a new record */ if(reply->expires && reply->expires->body.len > 0){ expires = atoi(reply->expires->body.s); LM_DBG("expires= %d\n", expires); } if(expires== 0){ LM_DBG("expires= 0: no not insert\n"); return 0; } return 1; } /* Treat Subscribe reply callback from Notifier */ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *params){ int code = params->code; struct sip_msg *reply = params->rpl; struct parms_cb* params_cb = (struct parms_cb*)(*params->param); LM_DBG("TREAT SUBSCRIBE REPLY \n"); LM_DBG("REPLY: %.*s \n ", reply->first_line.u.reply.version.len, reply->first_line.u.reply.version.s ); LM_DBG("CODE: %d \n ", code); LM_DBG("CALLID_INVITE: %.*s \n ",params_cb->callid_ori.len,params_cb->callid_ori.s); LM_DBG("FROM_TAG_INVITE: %.*s \n ",params_cb->from_tag.len,params_cb->from_tag.s); /* verify if response is OK*/ if (code < 200){ LM_ERR("ignore response \n"); return; } if (code < 300){ /* response OK(2XX): create Subscriber Cell*/ if ( !create_subscriber_cell(reply, params_cb)){ LM_ERR("fail in create subcriber cell \n"); } }else{ /* Response NOK send esct to clear esqk in VPC*/ LM_ERR("reply to SUBSCRIBER NOK - revisa\n"); if(send_esct(reply, params_cb->callid_ori, params_cb->from_tag) == 0){ LM_ERR("error in send to esct\n"); } } shm_free(params_cb->callid_ori.s); shm_free(params_cb->from_tag.s); shm_free(params_cb->event.s); shm_free(params_cb); return; } /* build new headers(Event, Expires) to SUBSCRIBER request */ str* add_hdr_subscriber(int expires, str event){ char *aux_hdr; char* str_expires= NULL; int size_expires = 1; int size_hdr; str* pt_hdr= NULL; /* convert expires in string*/ str_expires= int2str(expires, &size_expires); LM_DBG("EXPIRES -str : %s \n",str_expires ); if(str_expires == NULL || size_expires == 0){ LM_ERR("while converting int to str\n"); return NULL; } pt_hdr = (str*) pkg_malloc (sizeof (str)); if (pt_hdr == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } LM_DBG("EVENT STR %.*s \n", event.len, event.s); size_hdr = size_expires + event.len + 16 + 2*CRLF_LEN; aux_hdr= pkg_malloc(sizeof(char)* size_hdr + 1); if(aux_hdr== NULL){ LM_ERR("no more memory\n"); return NULL; } memset(aux_hdr, 0, size_hdr+1); pt_hdr->s = aux_hdr; pt_hdr->len = size_hdr; memcpy(aux_hdr, "Event: ", 7); aux_hdr+= 7; memcpy(aux_hdr, event.s, event.len); aux_hdr+= event.len; memcpy(aux_hdr, CRLF, CRLF_LEN); aux_hdr += CRLF_LEN; memcpy(aux_hdr, "Expires: ", 9); aux_hdr += 9; memcpy(aux_hdr, str_expires, size_expires); aux_hdr += size_expires; memcpy(aux_hdr, CRLF, CRLF_LEN); aux_hdr += CRLF_LEN; LM_DBG("HDR: %.*s \n", pt_hdr->len, pt_hdr->s); return pt_hdr; } /* Get some fields necessary to pass in function_cb*/ int build_params_cb(struct sip_msg* msg, char* callidHeader, struct parms_cb* params_cb ){ char *dialog_aux; str from_tag; int size_callid; int size_dialog; char *dialog; if (parse_from_header(msg) != 0) { LM_ERR(" REQUEST WITHOUT FROM HEADER\n"); } from_tag = get_from(msg)->tag_value; LM_DBG("FROM_TAG: %.*s\n", from_tag.len, from_tag.s); LM_DBG("CALLID = %s \n", callidHeader); size_callid = strlen(callidHeader); size_dialog= size_callid + from_tag.len + 26; dialog_aux = shm_malloc (sizeof (char)* size_dialog + 1); if (dialog_aux == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } memset(dialog_aux, 0, size_dialog + 1); dialog = dialog_aux; memcpy(dialog_aux, "dialog; call-id=", 16); dialog_aux += 16; memcpy(dialog_aux, callidHeader, size_callid); dialog_aux += size_callid; memcpy(dialog_aux, ";from-tag=", 10); dialog_aux += 10; memcpy(dialog_aux, from_tag.s, from_tag.len); LM_DBG("dialog: %s\n", dialog); char *call_aux = shm_malloc (size_callid + 1); if (call_aux == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } call_aux[size_callid] = 0; memcpy(call_aux, callidHeader, size_callid); char *ftag = shm_malloc (from_tag.len + 1); if (ftag == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } ftag[from_tag.len] = 0; memcpy(ftag, from_tag.s, from_tag.len); params_cb->callid_ori.s = call_aux; params_cb->callid_ori.len = size_callid; params_cb->from_tag.s = ftag; params_cb->from_tag.len = from_tag.len; params_cb->event.s = dialog; params_cb->event.len = size_dialog; return 1; } /* build some Uri to use in SUBSCRIBER request */ int get_uris_to_subscribe(struct sip_msg* msg, str* contact, str* notifier, str* subscriber ){ struct sip_uri *furi; int size_contact; int size_notifier; int size_subscriber; char *contact_aux; char *notifier_aux; char *subscriber_aux; int vsp_addr_len; char *vsp_addr = "@vsp.com"; int rp_addr_len; char *rp_addr = "@rp.com"; /* build contact uri to use in To header */ if ((furi = parse_from_uri(msg)) == NULL) { LM_ERR("****** ERROR PARSE FROM \n"); return 0; } size_contact= furi->user.len + furi->host.len + furi->port.len + 6; contact_aux = pkg_malloc (sizeof (char)* size_contact + 1); if (contact_aux == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } memset(contact_aux, 0, size_contact + 1); contact->s = contact_aux; contact->len = size_contact; memcpy(contact_aux, "sip:", 4); contact_aux += 4; memcpy(contact_aux, furi->user.s, furi->user.len); contact_aux += furi->user.len; *contact_aux = '@'; contact_aux++; memcpy(contact_aux, furi->host.s, furi->host.len); contact_aux += furi->host.len; *contact_aux = ':'; contact_aux++; memcpy(contact_aux, furi->port.s, furi->port.len); LM_DBG("****** contact: %.*s\n", contact->len, contact->s); /* build notifier uri to use in R-URI */ if ((parse_sip_msg_uri(msg) < 0) || (!msg->parsed_uri.user.s) || (msg->parsed_uri.user.len > MAXNUMBERLEN)) { LM_ERR("cannot parse msg URI\n"); pkg_free(contact_aux); return 0; } // get source ip address that send INVITE vsp_addr = ip_addr2a(&msg->rcv.src_ip); vsp_addr_len = strlen(vsp_addr); size_notifier = vsp_addr_len + msg->parsed_uri.user.len + 5; notifier_aux = pkg_malloc(size_notifier + 1); if (notifier_aux == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } memset(notifier_aux, 0, size_notifier + 1); notifier->s = notifier_aux; notifier->len = size_notifier; memcpy(notifier_aux, "sip:", 4); notifier_aux += 4; memcpy(notifier_aux, msg->parsed_uri.user.s, msg->parsed_uri.user.len); notifier_aux += msg->parsed_uri.user.len; *notifier_aux = '@'; notifier_aux++; memcpy(notifier_aux, vsp_addr, vsp_addr_len); LM_DBG("****** notifier: %.*s\n", notifier->len, notifier->s); /* build subscriber uri to use in From header */ // get ip address of opensips server in port that receive INVITE if (get_ip_socket(msg, &rp_addr) == -1){ pkg_free(contact_aux); pkg_free(notifier_aux); return 0; } rp_addr_len = strlen(rp_addr); size_subscriber = rp_addr_len + 21; subscriber_aux = pkg_malloc(size_subscriber + 1); if (subscriber_aux == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } memset(subscriber_aux, 0, size_subscriber + 1); subscriber->s = subscriber_aux; subscriber->len = size_subscriber; memcpy(subscriber_aux, "sip:opensips_redirect", 21); subscriber_aux += 21; memcpy(subscriber_aux, rp_addr, rp_addr_len); LM_DBG("****** subscriber: %.*s\n", subscriber->len, subscriber->s); return 1; } /* analise if reply from subscriber terminated is OK*/ void subs_cback_func_II(struct cell *t, int cb_type, struct tmcb_params *params){ int code = params->code; struct sip_msg *reply = params->rpl; UNUSED(reply); //struct sm_subscriber* params_subs = (struct sm_subscriber*)(*params->param); LM_DBG("TREAT SUBSCRIBE TERMINATED REPLY \n"); LM_DBG("REPLY: %.*s \n ", reply->first_line.u.reply.version.len, reply->first_line.u.reply.version.s ); LM_DBG("CODE: %d \n ", code); if (code < 300){ time_t rawtime; time(&rawtime); //int time_now = (int)rawtime; //params_subs->timeout = TIMER_N + time_now; //LM_DBG("TIMEOUT REPLY SUBSCRIBE: %d\n ", params_subs->timeout); }else{ LM_ERR("reply to subscribe terminated NOK\n "); } return; } /* build dialog struct */ dlg_t* build_dlg(struct sm_subscriber* subscriber){ dlg_t* dialog = NULL; int size; size= sizeof(dlg_t)+ subscriber->dlg_id->callid.len+ subscriber->dlg_id->rem_tag.len+ subscriber->dlg_id->local_tag.len+ subscriber->loc_uri.len+ subscriber->rem_uri.len + subscriber->contact.len;; dialog = (dlg_t*)pkg_malloc(size); if(dialog == NULL){ LM_ERR("No memory left\n"); return NULL; } memset(dialog, 0, size); size= sizeof(dlg_t); dialog->id.call_id.s = (char*)dialog+ size; memcpy(dialog->id.call_id.s, subscriber->dlg_id->callid.s, subscriber->dlg_id->callid.len); dialog->id.call_id.len= subscriber->dlg_id->callid.len; size+= subscriber->dlg_id->callid.len; dialog->id.rem_tag.s = (char*)dialog+ size; memcpy(dialog->id.rem_tag.s, subscriber->dlg_id->rem_tag.s, subscriber->dlg_id->rem_tag.len); dialog->id.rem_tag.len = subscriber->dlg_id->rem_tag.len; size+= subscriber->dlg_id->rem_tag.len; dialog->id.loc_tag.s = (char*)dialog+ size; memcpy(dialog->id.loc_tag.s, subscriber->dlg_id->local_tag.s, subscriber->dlg_id->local_tag.len); dialog->id.loc_tag.len =subscriber->dlg_id->local_tag.len; size+= subscriber->dlg_id->local_tag.len; dialog->loc_uri.s = (char*)dialog+ size; memcpy(dialog->loc_uri.s, subscriber->loc_uri.s, subscriber->loc_uri.len) ; dialog->loc_uri.len = subscriber->loc_uri.len; size+= dialog->loc_uri.len; dialog->rem_uri.s = (char*)dialog+ size; memcpy(dialog->rem_uri.s, subscriber->rem_uri.s, subscriber->rem_uri.len) ; dialog->rem_uri.len = subscriber->rem_uri.len; size+= dialog->rem_uri.len; dialog->rem_target.s = (char*)dialog+ size; memcpy(dialog->rem_target.s, subscriber->contact.s, subscriber->contact.len); dialog->rem_target.len = subscriber->contact.len; size+= dialog->rem_target.len; dialog->loc_seq.is_set = 1; dialog->state= DLG_CONFIRMED ; return dialog; } /* * send SUBSCRIBER to Call Server in scenario III * to receive notify about call status event */ int send_subscriber(struct sip_msg* msg, char* callidHeader, int expires){ str* contact_pt = NULL; str* notifier_pt = NULL; str* subscriber_pt = NULL; str met= {"SUBSCRIBE", 9}; str* pt_hdr= NULL; int sending; struct parms_cb* params_cb; int resp = 0; /*get URI of Notifier, Subscriber and Contact to use in Subscribe request */ contact_pt = (str*) pkg_malloc (sizeof (str)); if (contact_pt == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } notifier_pt = (str*) pkg_malloc (sizeof (str)); if (notifier_pt == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } subscriber_pt = (str*) pkg_malloc (sizeof (str)); if (subscriber_pt == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return 0; } if( !get_uris_to_subscribe(msg, contact_pt, notifier_pt, subscriber_pt)){ LM_ERR("**** fail in build parameters to cb \n"); resp = 0; goto end_02; } /*build struct (INVITE Callid, Event body) for parameter callback */ params_cb = (struct parms_cb*) shm_malloc (sizeof (struct parms_cb)); if (params_cb == NULL) { LM_ERR("--------------------------------------------------no more shm memory\n"); return 0; } if( !build_params_cb(msg, callidHeader, params_cb )){ LM_ERR("**** fail in build parameters to cb \n"); shm_free(params_cb); resp = 0; goto end_01; } /* add new header (Event, Expires) in SUBSCRIBE request */ pt_hdr = add_hdr_subscriber( expires, params_cb->event); LM_DBG("****** PARAMS FROM TAG: %.*s\n", params_cb->from_tag.len, params_cb->from_tag.s); /* send SUBSCRIBER */ sending= eme_tm.t_request (&met, /* Type of the message */ notifier_pt, /* Request-URI*/ contact_pt, /* To */ subscriber_pt, /* From */ pt_hdr, /* Optional headers including CRLF */ 0, /* Message body */ notifier_pt, /* Outbound_proxy */ subs_cback_func, /* Callback function */ (void*)params_cb, /* Callback parameter */ 0 ); resp = 1; if(sending< 0){ LM_ERR("while sending request with t_request\n"); shm_free(params_cb->callid_ori.s); shm_free(params_cb->from_tag.s); shm_free(params_cb->event.s); shm_free(params_cb); resp = 0; } if(pt_hdr != NULL){ pkg_free(pt_hdr->s); pkg_free(pt_hdr); } end_01: pkg_free(notifier_pt->s); pkg_free(contact_pt->s); pkg_free(subscriber_pt->s); end_02: pkg_free(notifier_pt); pkg_free(contact_pt); pkg_free(subscriber_pt); return resp; } /* send subscriber within of dialog, this subscriber close this dialog with Expires header = 0 */ int send_subscriber_within(struct sip_msg* msg, struct sm_subscriber* subs, int expires){ dlg_t* dialog =NULL; str met= {"SUBSCRIBE", 9}; int sending; str* pt_hdr= NULL; struct sm_subscriber* params_cb; dialog = build_dlg(subs); if(dialog== NULL){ LM_DBG(" --- ERROR IN BUILD DIALOG \n"); return -1; } LM_DBG(" --- FINAL \n"); LM_DBG(" --- DIALOG CALLID%.*s \n", dialog->id.call_id.len, dialog->id.call_id.s); LM_DBG(" --- DIALOG REMTAG%.*s \n", dialog->id.rem_tag.len, dialog->id.rem_tag.s); LM_DBG(" --- DIALOG LOCTAG%.*s \n", dialog->id.loc_tag.len, dialog->id.loc_tag.s); LM_DBG(" --- DIALOG REMURI%.*s \n", dialog->rem_uri.len, dialog->rem_uri.s); LM_DBG(" --- DIALOG LOCURI%.*s \n", dialog->loc_uri.len, dialog->loc_uri.s); LM_DBG(" --- DIALOG CONTACT%.*s \n", dialog->rem_target.len, dialog->rem_target.s); /* event = pkg_malloc(sizeof (char)* subs->event.len +1); if (event == NULL) { LM_ERR("no more pkg memory\n"); return -1; } event[subs->event.len] = 0; memcpy(event, subs->event.s, subs->event.len); LM_DBG(" --- EXPIRES = %d \n", expires); LM_DBG(" --- EVENT = %.*s \n", subs->event.len, subs->event.s); */ params_cb = subs; pt_hdr = add_hdr_subscriber(expires, subs->event); sending= eme_tm.t_request_within (&met, pt_hdr, 0, dialog, subs_cback_func_II, (void*)params_cb, 0 ); if(sending< 0) LM_ERR("while sending request with t_request_within\n"); if(pt_hdr != NULL){ pkg_free(pt_hdr->s); pkg_free(pt_hdr); } //pkg_free(event); pkg_free(dialog); return 1; } /* look for subscriber cell using callid and to_tag of Notify*/ struct sm_subscriber* get_subs_cell(struct sip_msg *msg, str callid_event) { str callid; str method; struct to_body *pto= NULL, *pfrom = NULL; struct sm_subscriber* s; unsigned int hash_code; method.s = msg->first_line.u.request.method.s; method.len = msg->first_line.u.request.method.len; if ( parse_headers(msg,HDR_EOH_F, 0) == -1 ){ LM_ERR("error in parsing headers\n"); return NULL; } // get callid from Notify if( msg->callid==NULL || msg->callid->body.s==NULL){ LM_ERR("reply without callid header\n"); return NULL; } callid = msg->callid->body; LM_DBG("CALLID: %.*s \n ", callid.len, callid.s ); if (msg->from->parsed == NULL){ if ( parse_from_header( msg )<0 ){ LM_ERR("reply without From header\n"); return NULL; } } //get From header from Notify pfrom = get_from(msg); LM_DBG("PFROM: %.*s \n ", pfrom->uri.len, pfrom->uri.s ); if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0){ LM_ERR("reply without tag value \n"); return NULL; } if( msg->to==NULL || msg->to->body.s==NULL){ LM_ERR("error in parse TO header\n"); return NULL; } // get To header from Notify pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); return NULL; } if( pto->tag_value.s ==NULL || pto->tag_value.len == 0){ LM_ERR("reply without tag value \n"); } LM_DBG("PTO: %.*s \n ", pto->uri.len, pto->uri.s ); LM_DBG("PTO_TAG: %.*s \n ", pto->tag_value.len, pto->tag_value.s ); LM_DBG("********************************************CALLID_STR%.*s\n", callid_event.len, callid_event.s); hash_code= core_hash(&callid_event, 0, subst_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); s= search_shtable(subs_htable, &callid, &pfrom->tag_value, hash_code, &method); if (s == NULL) { LM_ERR(" ---FAILURE SUB_CELL NOT FOUND IN SHTABLE\n"); } return s; } /* Treat Notify to Subscriber Dialog in scenario III*/ int treat_notify(struct sip_msg *msg) { int resp = 1; struct sm_subscriber* cell_subs; int expires= 0; char *subs_state, *subs_expires; str callid_orig; str from_tag; struct notify_body* notify_body = NULL; time_t rawtime; int time_now; int version; char *version_init, *version_end, *version_aux; int size_version; unsigned int hash_code; str callid_event; static str msg200={"OK Notify",sizeof("OK Notify")-1}; static str msg481={"Subscription does not exist",sizeof("Subscription does not exist")-1}; static str msg489={"Bad Event",sizeof("Bad Event")-1}; static str msg400={"Bad Request",sizeof("Bad Request")-1}; if(!check_event_header(msg)){ LM_ERR("event header type not allow\n"); if(!eme_tm.t_reply(msg,489,&msg489)){ LM_ERR("t_reply (489)\n"); } return 0; } if ( parse_headers(msg,HDR_EOH_F, 0) == -1 ){ LM_ERR("error in parsing headers\n"); return 0; } // get callid from Notify if( msg->callid==NULL || msg->callid->body.s==NULL){ LM_ERR("reply without callid header\n"); return 0; } callid_event = msg->callid->body; LM_DBG("CALLID: %.*s \n ", callid_event.len, callid_event.s ); /* look for cell in list linked subs_pt with same dialog Id*/ cell_subs = get_subs_cell(msg, callid_event); if(cell_subs == NULL){ if(!eme_tm.t_reply(msg,481,&msg481)){ LM_ERR("t_reply (481)\n"); } return 0; } LM_DBG("STATUS: %d \n ", cell_subs->dlg_id->status); LM_DBG("TIMEOUT NOTIFY: %d \n ", cell_subs->timeout); /* get in Subscription_state header: state and expire */ if(!get_subscription_state_header(msg, &subs_state, &subs_expires)){ LM_ERR("invalid body of Subscription_state header\n"); if(!eme_tm.t_reply(msg,400,&msg400)){ LM_ERR("t_reply (400)\n"); } return 0; } LM_DBG("STATE: %s\n ", subs_state); LM_DBG("SUBS_EXPIRES: %s\n ", subs_expires); time(&rawtime); time_now = (int)rawtime; /* analise state value*/ if (strcmp(subs_state, "active") == 0){ cell_subs->dlg_id->status = ACTIVE; cell_subs->expires = atoi(subs_expires); cell_subs->timeout = cell_subs->expires + time_now; }else{ if (strcmp(subs_state, "pending") == 0){ cell_subs->dlg_id->status = PENDING ; cell_subs->expires = atoi(subs_expires); cell_subs->timeout = TIMER_N + time_now; }else{ if(strcmp(subs_state, "terminated") == 0){ /* state is terminated indicate that subcriber dialog finish then pull cell of the list linked and send esct to VPC*/ LM_DBG(" --- CLEAR CELL \n"); callid_orig = cell_subs->call_dlg_id->callid; from_tag = cell_subs->call_dlg_id->local_tag; LM_DBG(" --- CALLID_ORIG %.*s \n", callid_orig.len, callid_orig.s); LM_DBG(" --- FROM_TAG_ORIG %.*s \n", from_tag.len, from_tag.s); if(send_esct(msg, callid_orig, from_tag) == 0){ LM_ERR("error in send to esct\n"); } hash_code= core_hash(&callid_event, 0, subst_size); LM_DBG("********************************************HASH_CODE%d\n", hash_code); delete_shtable(subs_htable, hash_code, cell_subs); /* Reply OK to Notify*/ if(!eme_tm.t_reply(msg,200,&msg200)){ LM_ERR("t_reply (200)\n"); return 0; } return 1; }else{ LM_ERR("INCOMPATIBLE RECEIVED STATUS\n"); if(!eme_tm.t_reply(msg,400,&msg400)){ LM_ERR("t_reply (400)\n"); } return 0; } } } LM_DBG("STATUS: %d \n ", cell_subs->dlg_id->status); LM_DBG(" --- NOTIFY BODY %s", msg->eoh); notify_body = parse_notify(msg->eoh); if( notify_body == NULL){ LM_ERR("invalid body in Notify request\n"); if(!eme_tm.t_reply(msg,400,&msg400)){ LM_ERR("t_reply (400)\n"); } resp = 0; goto end; } version_init = strchr(notify_body->params->version,'\"'); version_init++; version_end = strchr(version_init,'\"'); size_version = version_end - version_init; version_aux = pkg_malloc(size_version + 1); if (version_aux == NULL) { LM_ERR("no more pkg memory\n"); return 0; } memcpy(version_aux, version_init, size_version ); version_aux[size_version] = '\0'; version = atoi(version_aux); pkg_free(version_aux); LM_DBG(" --- STATE %s", notify_body->state); LM_DBG(" --- VERSION %d", version); /* Reply OK to Notify*/ if(!eme_tm.t_reply(msg,200,&msg200)){ LM_DBG("t_reply (200)\n"); free_parsed_notify(notify_body); resp = 0; goto end; } if(cell_subs->version >= version){ LM_ERR(" --- ERRO IN VERSION PARAMETER IN NOTIFY BODY"); free_parsed_notify(notify_body); resp = 0; goto end; }else{ cell_subs->version = version; } /* if Notify body state has terminated value, which indicates that emergency call finish, then send subscribe with expire=0 to terminate the subscriber dialog*/ if(strcmp(notify_body->state, "terminated") == 0){ expires = 0; LM_DBG(" --- STATE %s", notify_body->state); if(send_subscriber_within(msg, cell_subs, expires) == -1){ LM_ERR(" --- Error in send subscriber terminated \n"); } } resp = 1; free_parsed_notify(notify_body); end: pkg_free(subs_state); pkg_free(subs_expires); return resp; } // free notify_body void free_parsed_notify(struct notify_body* notify_body){ if(notify_body){ if(notify_body->target){ if(notify_body->target->dlg_id != empty){ pkg_free(notify_body->target->dlg_id); } if(notify_body->target->callid != empty){ pkg_free(notify_body->target->callid); } if(notify_body->target->local_tag != empty){ pkg_free(notify_body->target->local_tag); } if(notify_body->target->direction != empty){ pkg_free(notify_body->target->direction); } pkg_free(notify_body->target); } if(notify_body->params){ if(notify_body->params->version != empty){ pkg_free(notify_body->params->version); } if(notify_body->params->state != empty){ pkg_free(notify_body->params->state); } if(notify_body->params->entity != empty){ pkg_free(notify_body->params->entity); } pkg_free(notify_body->params); } pkg_free(notify_body); } return; } opensips-2.2.2/modules/emergency/subscriber_emergency.h000066400000000000000000000062061300170765700233670ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "../../socket_info.h" #include "../../route_struct.h" #include "../../ip_addr.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_pai.h" #include "../../parser/parse_ppi.h" #include "../../parser/parse_rpid.h" #include "../../parser/parse_from.h" #include "../../regexp.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "../../rw_locking.h" #include "../../timer.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../forward.h" #include "../rr/api.h" #include "../tm/tm_load.h" /*load_tm_api*/ #include "http_emergency.h" #define TIMER_N 132 // Timer N = 64*T1 T1 = (0,5s) #define MAXNUMBERLEN 31 struct tm_binds eme_tm; struct parms_cb{ str callid_ori; str from_tag; str event; }; int send_subscriber(struct sip_msg* msg, char* callidHeader, int expires); int send_subscriber_within(struct sip_msg* msg, struct sm_subscriber* subs, int expires); int get_uris_to_subscribe(struct sip_msg* msg, str* contact, str* notifier, str* subscriber ); int build_params_cb(struct sip_msg* msg, char* callidHeader, struct parms_cb* params_cb ); str* add_hdr_subscriber(int expires, str event); void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *params); int extract_reply_headers(struct sip_msg* reply, str* callid, int expires); int create_subscriber_cell(struct sip_msg* reply, struct parms_cb* params_cb); int treat_notify(struct sip_msg *msg); struct sm_subscriber* get_subs_cell(struct sip_msg *msg, str callid_event); void subs_cback_func_II(struct cell *t, int cb_type, struct tmcb_params *params); dlg_t* build_dlg(struct sm_subscriber* subscriber); void free_parsed_notify(struct notify_body* notify_body); opensips-2.2.2/modules/emergency/xml_parser.c000066400000000000000000000360731300170765700213420ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include #include #include #include "xml_parser.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" const char *XML_MODEL_ESCT= " \n \ \n \ %s \n \ %s \n \ \n \ \n \ %s \n \ %s \n \ %s \n \ %s \n \ %s \n \ \n \ %s \n \ %s \n \ %s \n \ %s \n \ "; char* copy_str_between_two_pointers(char* str_begin, char* str_end){ char *new_begin; new_begin = strstr (str_begin,">"); new_begin +=1; return copy_str_between_two_pointers_simple(new_begin, str_end); } char* copy_str_between_two_pointers_simple(char* str_begin, char* str_end){ size_t tamanho =0; char *resp; tamanho = str_end - str_begin; if (tamanho == 0) return empty; resp = pkg_malloc(sizeof(char)*(tamanho+1)); if(resp ==NULL) return resp; memcpy ( resp, str_begin, tamanho ); resp[tamanho]='\0'; return resp; } //copy string between initial () and end (<\tag>) tags char* copy_str_between_two_tags(char* tag_begin, char* str_total){ char *ptr1,*ptr2; char *complete_tag_begin, *complete_tag_end; int size_begin, size_end; size_begin = sizeof(char) * (strlen(tag_begin)+ strlen("<") + strlen(">")) + 1; size_end = sizeof(char) * (strlen(tag_begin)+ strlen("")) + 1; complete_tag_begin = pkg_malloc(size_begin); complete_tag_end = pkg_malloc(size_end); memset(complete_tag_begin, 0, size_begin); memset(complete_tag_end, 0, size_end); if(complete_tag_begin == NULL || complete_tag_end == NULL) return empty; strcpy (complete_tag_begin,"<"); strcat (complete_tag_begin,tag_begin); strcat (complete_tag_begin,">"); strcpy (complete_tag_end,""); ptr1 = strstr(str_total,complete_tag_begin); ptr2 = strstr(str_total,complete_tag_end); if(ptr1 != NULL && ptr2 != NULL){ LM_DBG(" --- FOUND TAG %s",str_total); pkg_free(complete_tag_begin); pkg_free(complete_tag_end); return copy_str_between_two_pointers(ptr1,ptr2); }else{ LM_DBG(" --- NOT FOUND TAG %s",str_total); } pkg_free(complete_tag_begin); pkg_free(complete_tag_end); return empty; } //copy string between initial () tags, in this case consider parms in initial tag char* copy_str_between_two_tags_simple(char* tag_begin, char* str_total){ char *ptr1,*ptr2; char *complete_tag_begin, *complete_tag_end; complete_tag_begin = pkg_malloc(sizeof(char) * (strlen(tag_begin)+ strlen("<"))); complete_tag_end = pkg_malloc(sizeof(char) * (strlen(tag_begin)+ strlen(""))); if(complete_tag_begin == NULL || complete_tag_end == NULL) return empty; strcpy (complete_tag_begin,"<"); strcat (complete_tag_begin,tag_begin); strcpy (complete_tag_end,""); ptr1 = strstr(str_total,complete_tag_begin); ptr2 = strstr(str_total,complete_tag_end); if(ptr1 != NULL && ptr2 != NULL){ LM_DBG(" --- FOUND TAG %s",str_total); pkg_free(complete_tag_begin); pkg_free(complete_tag_end); return copy_str_between_two_pointers_simple(ptr1 + strlen(tag_begin) + 1,ptr2); }else{ LM_DBG(" --- NOT FOUND TAG %s",str_total); } pkg_free(complete_tag_begin); pkg_free(complete_tag_end); return empty; } // check main tag in esrResponse int check_str_between_init_tags( char* str_total){ char *ptr1,*ptr2; char *complete_tag_begin, *complete_tag_end; complete_tag_begin = "params = pkg_malloc(sizeof(struct dialog_params)); notify->target = pkg_malloc(sizeof(struct target_info)); if(notify == NULL || notify->params == NULL || notify->target == NULL) return NULL; pt_version = strstr(dialog_body,version); pt_dlg_state = strstr(dialog_body,dlg_state); pt_entity = strstr(dialog_body,entity); pt_end_entity = strstr(dialog_body,">"); if (pt_version == NULL || pt_dlg_state == NULL || pt_entity == NULL || pt_end_entity == NULL) goto error_01; target_info = copy_str_between_two_tags_simple(dialog,dialog_body); if (target_info == empty) goto error_01; notify->state = copy_str_between_two_tags(state,dialog_body); if (notify->state == empty){ pkg_free(target_info); goto error_01; } pt_dialog_id = strstr(target_info,dialog_id); pt_callid = strstr(target_info,callid); pt_local_tag = strstr(target_info,local_tag); pt_direction = strstr(target_info,direction); pt_end_direction = strstr(target_info,">"); if (pt_dialog_id == NULL || pt_callid == NULL || pt_local_tag == NULL || pt_direction == NULL || pt_end_direction == NULL){ pkg_free(target_info); pkg_free(notify->state); goto error_01; } notify->params->version = copy_str_between_two_pointers_simple(pt_version + strlen(version), pt_dlg_state); notify->params->state = copy_str_between_two_pointers_simple(pt_dlg_state + strlen(dlg_state), pt_entity); notify->params->entity = copy_str_between_two_pointers_simple(pt_entity + strlen(entity), pt_end_entity); notify->target->dlg_id = copy_str_between_two_pointers_simple(pt_dialog_id + strlen(dialog_id), pt_callid); notify->target->callid = copy_str_between_two_pointers_simple(pt_callid + strlen(callid), pt_local_tag); notify->target->local_tag = copy_str_between_two_pointers_simple(pt_local_tag + strlen(local_tag), pt_direction); notify->target->direction = copy_str_between_two_pointers_simple(pt_direction + strlen(direction), pt_end_direction); pkg_free(target_info); return notify; error_01: pkg_free(notify->target); pkg_free(notify->params); pkg_free(notify); return NULL; } /* treats the esrResponse xml from VPC * - extract data between tags and put in parsed struct: * - result * - esgwri * - esqk * - lro * - callid * - datetimestamp * - vpc * .organizationname * .hostname * .nenaid * .contact * .certuri * - destination * .organizationname * .hostname * .nenaid * .contact * .certuri * - ert * .selectiveRoutingID * .routingESN * .npa */ PARSED* parse_xml(char* xml){ char* result = "result"; char* esgwri = "esgwri"; char* esqk = "esqk"; char* lro = "lro"; char* callid = "callId"; char* datetimestamp = "datetimestamp"; char* vpc = "vpc"; char* destination = "destination"; char* organizationname = "organizationName"; char* hostname = "hostId"; char* nenaid = "nenaId"; char* contact = "contact"; char* certuri = "certUri"; char* ert = "ert"; char* selectiveRoutingID = "selectiveRoutingID"; char* routingESN = "routingESN"; char* npa = "npa"; char *new_vpc, *new_destination, *new_ert; PARSED *parsed = pkg_malloc(sizeof(PARSED)); parsed->vpc =pkg_malloc(sizeof(NENA)); parsed->destination =pkg_malloc(sizeof(NENA)); parsed->ert =pkg_malloc(sizeof(ERT)); if (check_str_between_init_tags(xml)) return NULL; if(parsed == NULL || parsed->vpc == NULL || parsed->destination == NULL || parsed->ert == NULL) return NULL; parsed->result = copy_str_between_two_tags(result,xml); parsed->esgwri = copy_str_between_two_tags(esgwri,xml); parsed->esqk = copy_str_between_two_tags(esqk,xml); parsed->lro = copy_str_between_two_tags(lro,xml); parsed->callid = copy_str_between_two_tags(callid,xml); parsed->datetimestamp = copy_str_between_two_tags(datetimestamp,xml); new_vpc = copy_str_between_two_tags(vpc,xml); if(new_vpc != empty){ parsed->vpc->organizationname = copy_str_between_two_tags(organizationname,new_vpc); parsed->vpc->hostname = copy_str_between_two_tags(hostname,new_vpc); parsed->vpc->nenaid = copy_str_between_two_tags(nenaid,new_vpc); parsed->vpc->contact = copy_str_between_two_tags(contact,new_vpc); parsed->vpc->certuri = copy_str_between_two_tags(certuri,new_vpc); pkg_free(new_vpc); }else{ parsed->vpc->organizationname = empty; parsed->vpc->hostname = empty; parsed->vpc->nenaid = empty; parsed->vpc->contact = empty; parsed->vpc->certuri = empty; } new_destination = copy_str_between_two_tags(destination,xml); if(new_destination!= empty){ parsed->destination->organizationname = copy_str_between_two_tags(organizationname,new_destination); parsed->destination->hostname = copy_str_between_two_tags(hostname,new_destination); parsed->destination->nenaid = copy_str_between_two_tags(nenaid,new_destination); parsed->destination->contact = copy_str_between_two_tags(contact,new_destination); parsed->destination->certuri = copy_str_between_two_tags(certuri,new_destination); pkg_free(new_destination); }else{ parsed->destination->organizationname = empty; parsed->destination->hostname = empty; parsed->destination->nenaid = empty; parsed->destination->contact = empty; parsed->destination->certuri = empty; } new_ert = copy_str_between_two_tags(ert,xml); if(new_ert != empty){ parsed->ert->selectiveRoutingID = copy_str_between_two_tags(selectiveRoutingID,new_ert); parsed->ert->routingESN = copy_str_between_two_tags(routingESN,new_ert); parsed->ert->npa = copy_str_between_two_tags(npa,new_ert); pkg_free(new_ert); }else{ parsed->ert->selectiveRoutingID = empty; parsed->ert->routingESN = empty; parsed->ert->npa = empty; } return parsed; } int isNotBlank(char *str){ if(str == NULL){ //LM_DBG("STR NULL...\n"); return -1; } if (strcmp(str, "") == 0){ return -1; }else{ return 1; } } // build xml for esctRequest char* buildXmlFromModel(ESCT* esct){ int len_buf = findOutSize(esct); char* resp = pkg_malloc(sizeof(char)* len_buf); if (resp == NULL) { LM_ERR("--------------------------------------------------no more pkg memory\n"); return NULL; } sprintf(resp, XML_MODEL_ESCT ,esct->vpc->organizationname, esct->vpc->hostname, esct->source->organizationname , esct->source->hostname, esct->source->nenaid , esct->source->contact, esct->source->certuri , esct->esgw , esct->esqk , esct->callid , esct->datetimestamp); return resp; } unsigned long findOutSize(ESCT* esct){ unsigned long resp = 0; resp = strlen(XML_MODEL_ESCT); if(esct != NULL){ resp += esct->callid != empty ? strlen(esct->callid) : 0; resp += esct->esgw != empty ? strlen(esct->esgw) : 0; resp += esct->esqk != empty ? strlen(esct->esqk) : 0; resp += esct->datetimestamp != empty ? strlen(esct->datetimestamp) : 0; resp += findOutNenaSize(esct->vpc); resp += findOutNenaSize(esct->source); } return resp; } unsigned long findOutNenaSize(NENA* nena){ unsigned long resp = 0; if(!nena || nena == NULL) return resp; resp += nena->organizationname != empty ? strlen(nena->organizationname) : 0; resp += nena->hostname != empty ? strlen(nena->hostname) : 0; resp += nena->nenaid != empty ? strlen(nena->nenaid) : 0; resp += nena->contact != empty ? strlen(nena->contact) : 0; resp += nena->certuri != empty ? strlen(nena->certuri) : 0; return resp; } opensips-2.2.2/modules/emergency/xml_parser.h000066400000000000000000000101041300170765700213320ustar00rootroot00000000000000/* * emergency module - basic support for emergency calls * * Copyright (C) 2014-2015 Robison Tesini & Evandro Villaron * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2014-10-14 initial version (Villaron/Tesini) * 2015-03-21 implementing subscriber function (Villaron/Tesini) * 2015-04-29 implementing notifier function (Villaron/Tesini) * 2015-05-20 change callcell identity * 2015-06-08 change from list to hash (Villaron/Tesini) * 2015-08-05 code review (Villaron/Tesini) * 2015-09-07 final test cases (Villaron/Tesini) */ #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "../../socket_info.h" #include "../../route_struct.h" #include "../../ip_addr.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_pai.h" #include "../../parser/parse_ppi.h" #include "../../parser/parse_rpid.h" #include "../../parser/parse_from.h" #include "../../regexp.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "../../rw_locking.h" #include "../../timer.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../forward.h" #include "../rr/api.h" #include "../tm/tm_load.h" /*load_tm_api*/ char *empty; #define MAX_TIME_SIZE 80 #define MAX_DISPOSITION_SIZE 20 typedef struct parsed_xml_vpc{ char* organizationname; char* hostname; char* nenaid; char* contact; char* certuri; }NENA; typedef struct parsed_xml_ert{ char* selectiveRoutingID ; char* routingESN; char* npa; }ERT; typedef struct parsed_xml_resp{ char* result; char* esgwri; char* esqk; char* lro; char* callid; char* datetimestamp; NENA *vpc; NENA *destination; ERT *ert; }PARSED; struct dialog_set{ char* call_id; char* local_tag; char* rem_tag; int status; }; typedef struct esct{ struct dialog_set *eme_dlg_id; NENA *source; NENA *vpc; char* esgwri; char* esgw; char* esqk; char* callid; char* ert_srid; int ert_resn; int ert_npa; char* datetimestamp; char* lro; char* disposition; char* result; int timeout; }ESCT; typedef struct node { ESCT *esct; struct node *next; }NODE; struct dialog_params{ char* version; char* state; char* entity; }; struct target_info{ char* dlg_id; char* callid; char* local_tag; char* direction; }; struct notify_body{ struct dialog_params* params; struct target_info* target; char* state; }; struct dialog_id{ str callid; str local_tag; str rem_tag; int status; }; struct sm_subscriber{ struct dialog_id *dlg_id; struct dialog_id *call_dlg_id; str loc_uri; str rem_uri; str contact; str event; int expires; int timeout; int version; struct sm_subscriber *prev; struct sm_subscriber *next; }; char* copy_str_between_two_pointers(char* str_begin, char* str_end); char* copy_str_between_two_pointers_simple(char* str_begin, char* str_end); char* copy_str_between_two_tags(char* tag_begin, char* str_total); int check_str_between_init_tags( char* str_total); int check_ectAck_init_tags( char* str_total); PARSED* parse_xml(char* xml); char* parse_xml_esct(char* xml); int isNotBlank(char *str); unsigned long findOutSize(ESCT* esct); unsigned long findOutNenaSize(NENA* nena); char* buildXmlFromModel(ESCT* esct); struct notify_body* parse_notify(char* xml); char* check_dialog_init_tags( char* str_total); opensips-2.2.2/modules/enum/000077500000000000000000000000001300170765700157775ustar00rootroot00000000000000opensips-2.2.2/modules/enum/Makefile000066400000000000000000000003411300170765700174350ustar00rootroot00000000000000# Makefile v 1.0 2002/12/27 # # Enum module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=enum.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/enum/README000066400000000000000000000316321300170765700166640ustar00rootroot00000000000000Enum Module Juha Heinanen Otmar Lendl Copyright © 2002, 2003 Juha Heinanen Revision History Revision $Revision: 5907 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.3. Exported Parameters 1.3.1. domain_suffix (string) 1.3.2. tel_uri_params (string) 1.3.3. i_enum_suffix (string) 1.3.4. isn_suffix (string) 1.3.5. branchlabel (string) 1.3.6. bl_algorithm (string) 1.4. Exported Functions 1.4.1. enum_query(["suffix"[,"service"]]) 1.4.2. enum_pv_query("pvar"[,"suffix"[,"service"]]) 1.4.3. i_enum_query(["suffix"[,"service"]]) 1.4.4. isn_query(["suffix"[,"service"]]) 1.4.5. is_from_user_enum() List of Examples 1.1. Setting domain_suffix module parameter 1.2. Setting tel_uri_params module parameter 1.3. Setting i_enum_suffix module parameter 1.4. Setting isn_suffix module parameter 1.5. Setting branchlabel module parameter 1.6. Zone file example 1.7. Zone file example 1.8. Setting the bl_algorithm module parameter 1.9. enum_query usage 1.10. enum_pv_query usage 1.11. isn_query usage 1.12. is_from_user_enum usage Chapter 1. Admin Guide 1.1. Overview Enum module implements [i_]enum_query functions that make an enum query based on the user part of the current Request-URI. These functions assume that the user part consists of an international phone number of the form +decimal-digits, where the number of digits is at least 2 and at most 15. Out of this number enum_query forms a domain name, where the digits are in reverse order and separated by dots followed by domain suffix that by default is “e164.arpa.â€. For example, if the user part is +35831234567, the domain name will be “7.6.5.4.3.2.1.3.8.5.3.e164.arpa.â€. i_enum_query operates in a similar fashion. The only difference is that it adds a label (default "i") to branch off from the default, user-ENUM tree to an infrastructure ENUM tree. After forming the domain name, enum_query queries DNS for its NAPTR records. From the possible response enum_query chooses those records, whose flags field has string value "u", and whose services field has string value "e2u+[service:]sip" or "e2u+type[:subtype][+type[:subtype]...]" (case is ignored in both cases), and whose regexp field is of the form !pattern!replacement!. Then enum_query sorts the chosen NAPTR records based on their . After sorting, enum_query replaces the current Request URI by applying regexp of the most preferred NAPTR record its user part and appends to the request new branches by applying regexp of each remaining NAPTR record to the user part of the current Request URI. If a new URI is a tel URI, enum_query appends to it as tel URI parameters the value of tel_uri_params module parameter. Finally, enum_query associates a q value with each new URI based on the of the corresponding NAPTR record. When using enum_query without any parameters, it searches for NAPTRs with service type "e2u+sip" in the default enum tree. When using enum_query with a single parameter, this parameter will be used as enum tree. When using enum_query with two parameters, the functionality depends on the first letter in the second parameter. When the first letter is not a '+' sign, the second parameter will be used to search for NAPTRs with service type "e2u+parameter:sip". When the second parameter starts with a '+' sign, the ENUM lookup also supports compound NAPTRs (e.g. "e2u+voice:sip+video:sip") and searching for multiple service types within one lookup. Multiple service types must be separeted by a '+' sign. Most of the time you want to route based on the RURI. On rare occasions you may wish to route based on something else. The function enum_pv_query mimics the behavior of the enum_query function except the E.164 number in its pseudo variable argument is used for the enum lookup instead of the user part of the RURI. Obviously the user part of the RURI is still used in the NAPTR regexp. Enum query returns 1 if the current Request URI was replaced and -1 if not. In addition to standard ENUM, support for ISN (ITAD Subscriber Numbers) is provided as well. To allow ISN lookups to resolve, a different formatting algorithm is expected by the DNS server. Whereas a ENUM NAPTR record expects a DNS query of the form 9.8.7.6.5.4.3.2.1.suffix, ISN method expects a DNS query of the form 6.5.1212.suffix. That is, a valid ISN number includes a prefix of '56' in the example. The rest of the number is a ITAD (Internet Telephony Administrative Domain) as defined in RFCs 3872 and 2871, and as allocated by the IANA in http://www.iana.org/assignments/trip-parameters. The ITAD is left intact and not refersed as ENUM requires. To learn more about ISN please refer to documents at www.freenum.org. To complete a ISN lookup on the user part of the Request-URI, isn_query() is used instead of enum_query(). Enum module also implements is_from_user_enum function. This function does an enum lookup on the from user and returns true if found, false otherwise. 1.2. Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): * No dependencies. 1.3. Exported Parameters 1.3.1. domain_suffix (string) The domain suffix to be added to the domain name obtained from the digits of an E164 number. Can be overridden by a parameter to enum_query. Default value is “e164.arpa.†Example 1.1. Setting domain_suffix module parameter modparam("enum", "domain_suffix", "e1234.arpa.") 1.3.2. tel_uri_params (string) A string whose contents is appended to each new tel URI in the request as tel URI parameters. Note Currently OpenSIPS does not support tel URIs. This means that at present tel_uri_params is appended as URI parameters to every URI. Default value is ҠExample 1.2. Setting tel_uri_params module parameter modparam("enum", "tel_uri_params", ";npdi") 1.3.3. i_enum_suffix (string) The domain suffix to be used for i_enum_query() lookups. Can be overridden by a parameter to i_enum_query. Default value is “e164.arpa.†Example 1.3. Setting i_enum_suffix module parameter modparam("enum", "i_enum_suffix", "e1234.arpa.") 1.3.4. isn_suffix (string) The domain suffix to be used for isn_query() lookups. Can be overridden by a parameter to isn_query. Default value is “freenum.org.†Example 1.4. Setting isn_suffix module parameter modparam("enum", "isn_suffix", "freenum.org.") 1.3.5. branchlabel (string) This parameter determines which label i_enum_query() will use to branch off to the infrastructure ENUM tree. Default value is “"i"†Example 1.5. Setting branchlabel module parameter modparam("enum", "branchlabel", "i") 1.3.6. bl_algorithm (string) This parameter determines which algorithm i_enum_query() will use to select the position in the DNS tree where the infrastructure tree branches off the user ENUM tree. If set to "cc", i_enum_query() will always inserts the label at the country-code level. Examples: i.1.e164.arpa, i.3.4.e164.arpa, i.2.5.3.e164.arpa If set to "txt", i_enum_query() will look for a TXT record at [branchlabel].[reverse-country-code].[i_enum_suffix] to indicate after how many digits the label should in inserted. Example 1.6. Zone file example i.1.e164.arpa. IN TXT "4" 9.9.9.8.7.6.5.i.4.3.2.1.e164.arpa. IN NAPTR "NAPTR content for +1 234 5 678 999" If set to "ebl", i_enum_query() will look for an EBL (ENUM Branch Label) record at [branchlabel].[reverse-country-code].[i_enum_suffix]. See http://www.ietf.org/internet-drafts/draft-lendl-enum-branch-loc ation-record-00.txt for a description of that record and the meaning of the fields. The RR type for the EBL has not been allocated yet. This version of the code uses 65300. See resolve.h. Example 1.7. Zone file example i.1.e164.arpa. TYPE65300 \# 14 ( 04 ; position 01 69 ; separator 04 65 31 36 34 04 61 72 70 61 00 ; e164.ar pa ; ) 9.9.9.8.7.6.5.i.4.3.2.1.e164.arpa. IN NAPTR "NAPTR content for +1 234 5 678 999" Default value is “cc†Example 1.8. Setting the bl_algorithm module parameter modparam("enum", "bl_algorithm", "txt") 1.4. Exported Functions 1.4.1. enum_query(["suffix"[,"service"]]) The function performs an enum query and rewrites the Request-URI with the result of the query. See Section 1.1, “Overview†for more information. Meaning of the parameters is as follows: * suffix - Suffix to be appended to the domain name. The suffix parameter can have the following types: + string - the suffix is statically assigned + pvar - the suffix is the value of an existing pseudo-variable (as string value) * service - Service string to be used in the service field. + string - the service is statically assigned + pvar - the service is the value of an existing pseudo-variable (as string value) This function can be used from REQUEST_ROUTE. Example 1.9. enum_query usage ... # search for "e2u+sip" in freenum.org enum_query("freenum.org."); ... # search for "e2u+sip" in default tree (configured as parameter) enum_query(); ... # search for "e2u+voice:sip" in e164.arpa enum_query("e164.arpa.","voice"); ... # search for service type "sip" or "voice:sip" or "video:sip" # note the '+' sign in front of the second parameter enum_query("e164.arpa.","+sip+voice:sip+video:sip"); ... # quering for service sip and voice:sip enum_query("e164.arpa."); enum_query("e164.arpa.","voice"); # or use instead enum_query("e164.arpa.","+sip+voice:sip"); ... 1.4.2. enum_pv_query("pvar"[,"suffix"[,"service"]]) The function performs an enum query on E.164 number stored in its pseudo variable argument and rewrites the Request-URI with the result of the query. See Section 1.1, “Overview†for more information. Meaning of the parameters is as follows: * pvar - Pseudo variable that holds an E.164 number on which enum query is performed. * suffix - Suffix to be appended to the domain name. * service - Service string to be used in the service field. This function can be used from REQUEST_ROUTE. Example 1.10. enum_pv_query usage ... # search for "e2u+sip" in freenum.org enum_pv_query("$avp(100)", "freenum.org."); ... # search for "e2u+sip" in default tree (configured as parameter) enum_pv_query("$fU"); ... # search for "e2u+voice:sip" in e164.arpa enum_pv_query("$avp(100)","e164.arpa.","voice"); ... # search for service type "sip" or "voice:sip" or "video:sip" # note the '+' sign in front of the second parameter enum_pv_query("$fU","e164.arpa.","+sip+voice:sip+video:sip"); ... # quering for service sip and voice:sip enum_pv_query("$avp(100)","e164.arpa."); enum_pv_query("$avp(100)","e164.arpa.","voice"); # or use instead enum_pv_query("$avp(100)","e164.arpa.","+sip+voice:sip"); ... 1.4.3. i_enum_query(["suffix"[,"service"]]) The function performs an enum query and rewrites the Request-URI with the result of the query. This the Infrastructure-ENUM version of enum_query(). The only difference to enum_query() is in the calculation of the FQDN where NAPTR records are looked for. See ftp://ftp.rfc-editor.org/in-notes/internet-drafts/draft-haberle r-carrier-enum-01.txt for the rationale behind this function. 1.4.4. isn_query(["suffix"[,"service"]]) The function performs a ISN query and rewrites the Request-URI with the result of the query. See Section 1.1, “Overview†for more information. Meaning of the parameters is as follows: * suffix - Suffix to be appended to the domain name. * service - Service string to be used in the service field. This function can be used from REQUEST_ROUTE. See ftp://www.ietf.org/rfc/rfc3872.txt and ftp://www.ietf.org/rfc/rfc2871.txt for information regarding the ITAD part of the ISN string. Example 1.11. isn_query usage ... # search for "e2u+sip" in freenum.org isn_query("freenum.org."); ... # search for "e2u+sip" in default tree (configured as parameter) isn_query(); ... # search for "e2u+voice:sip" in freenum.org isn_query("freenum.org.","voice"); ... 1.4.5. is_from_user_enum() Checks if the user part of from URI is found in an enum lookup. Returns 1 if yes and -1 if not. This function can be used from REQUEST_ROUTE. Example 1.12. is_from_user_enum usage ... if (is_from_user_enum()) { .... }; ... opensips-2.2.2/modules/enum/doc/000077500000000000000000000000001300170765700165445ustar00rootroot00000000000000opensips-2.2.2/modules/enum/doc/enum.xml000066400000000000000000000017501300170765700202350ustar00rootroot00000000000000 %docentities; ]> Enum Module Juha Heinanen jh@song.fi Otmar Lendl lendl@nic.at 2002 2003 Juha Heinanen $Revision: 5907 $ $Date$ &admin; &faq; opensips-2.2.2/modules/enum/doc/enum_admin.xml000066400000000000000000000416171300170765700214130ustar00rootroot00000000000000 &adminguide;
Overview Enum module implements [i_]enum_query functions that make an enum query based on the user part of the current Request-URI. These functions assume that the user part consists of an international phone number of the form +decimal-digits, where the number of digits is at least 2 and at most 15. Out of this number enum_query forms a domain name, where the digits are in reverse order and separated by dots followed by domain suffix that by default is e164.arpa.. For example, if the user part is +35831234567, the domain name will be 7.6.5.4.3.2.1.3.8.5.3.e164.arpa.. i_enum_query operates in a similar fashion. The only difference is that it adds a label (default "i") to branch off from the default, user-ENUM tree to an infrastructure ENUM tree. After forming the domain name, enum_query queries DNS for its NAPTR records. From the possible response enum_query chooses those records, whose flags field has string value "u", and whose services field has string value "e2u+[service:]sip" or "e2u+type[:subtype][+type[:subtype]...]" (case is ignored in both cases), and whose regexp field is of the form !pattern!replacement!. Then enum_query sorts the chosen NAPTR records based on their <order, preference>. After sorting, enum_query replaces the current Request URI by applying regexp of the most preferred NAPTR record its user part and appends to the request new branches by applying regexp of each remaining NAPTR record to the user part of the current Request URI. If a new URI is a tel URI, enum_query appends to it as tel URI parameters the value of tel_uri_params module parameter. Finally, enum_query associates a q value with each new URI based on the <order, preference> of the corresponding NAPTR record. When using enum_query without any parameters, it searches for NAPTRs with service type "e2u+sip" in the default enum tree. When using enum_query with a single parameter, this parameter will be used as enum tree. When using enum_query with two parameters, the functionality depends on the first letter in the second parameter. When the first letter is not a '+' sign, the second parameter will be used to search for NAPTRs with service type "e2u+parameter:sip". When the second parameter starts with a '+' sign, the ENUM lookup also supports compound NAPTRs (e.g. "e2u+voice:sip+video:sip") and searching for multiple service types within one lookup. Multiple service types must be separeted by a '+' sign. Most of the time you want to route based on the RURI. On rare occasions you may wish to route based on something else. The function enum_pv_query mimics the behavior of the enum_query function except the E.164 number in its pseudo variable argument is used for the enum lookup instead of the user part of the RURI. Obviously the user part of the RURI is still used in the NAPTR regexp. Enum query returns 1 if the current Request URI was replaced and -1 if not. In addition to standard ENUM, support for ISN (ITAD Subscriber Numbers) is provided as well. To allow ISN lookups to resolve, a different formatting algorithm is expected by the DNS server. Whereas a ENUM NAPTR record expects a DNS query of the form 9.8.7.6.5.4.3.2.1.suffix, ISN method expects a DNS query of the form 6.5.1212.suffix. That is, a valid ISN number includes a prefix of '56' in the example. The rest of the number is a ITAD (Internet Telephony Administrative Domain) as defined in RFCs 3872 and 2871, and as allocated by the IANA in http://www.iana.org/assignments/trip-parameters. The ITAD is left intact and not refersed as ENUM requires. To learn more about ISN please refer to documents at www.freenum.org. To complete a ISN lookup on the user part of the Request-URI, isn_query() is used instead of enum_query(). Enum module also implements is_from_user_enum function. This function does an enum lookup on the from user and returns true if found, false otherwise.
Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): No dependencies.
Exported Parameters
<varname>domain_suffix</varname> (string) The domain suffix to be added to the domain name obtained from the digits of an E164 number. Can be overridden by a parameter to enum_query. Default value is e164.arpa. Setting domain_suffix module parameter modparam("enum", "domain_suffix", "e1234.arpa.")
<varname>tel_uri_params</varname> (string) A string whose contents is appended to each new tel URI in the request as tel URI parameters. Currently &osips; does not support tel URIs. This means that at present tel_uri_params is appended as URI parameters to every URI. Default value is Setting tel_uri_params module parameter modparam("enum", "tel_uri_params", ";npdi")
<varname>i_enum_suffix</varname> (string) The domain suffix to be used for i_enum_query() lookups. Can be overridden by a parameter to i_enum_query. Default value is e164.arpa. Setting i_enum_suffix module parameter modparam("enum", "i_enum_suffix", "e1234.arpa.")
<varname>isn_suffix</varname> (string) The domain suffix to be used for isn_query() lookups. Can be overridden by a parameter to isn_query. Default value is freenum.org. Setting isn_suffix module parameter modparam("enum", "isn_suffix", "freenum.org.")
<varname>branchlabel</varname> (string) This parameter determines which label i_enum_query() will use to branch off to the infrastructure ENUM tree. Default value is "i" Setting branchlabel module parameter modparam("enum", "branchlabel", "i")
<varname>bl_algorithm</varname> (string) This parameter determines which algorithm i_enum_query() will use to select the position in the DNS tree where the infrastructure tree branches off the user ENUM tree. If set to "cc", i_enum_query() will always inserts the label at the country-code level. Examples: i.1.e164.arpa, i.3.4.e164.arpa, i.2.5.3.e164.arpa If set to "txt", i_enum_query() will look for a TXT record at [branchlabel].[reverse-country-code].[i_enum_suffix] to indicate after how many digits the label should in inserted. Zone file example i.1.e164.arpa. IN TXT "4" 9.9.9.8.7.6.5.i.4.3.2.1.e164.arpa. IN NAPTR "NAPTR content for +1 234 5678 999" If set to "ebl", i_enum_query() will look for an EBL (ENUM Branch Label) record at [branchlabel].[reverse-country-code].[i_enum_suffix]. See http://www.ietf.org/internet-drafts/draft-lendl-enum-branch-location-record-00.txt for a description of that record and the meaning of the fields. The RR type for the EBL has not been allocated yet. This version of the code uses 65300. See resolve.h. Zone file example i.1.e164.arpa. TYPE65300 \# 14 ( 04 ; position 01 69 ; separator 04 65 31 36 34 04 61 72 70 61 00 ; e164.arpa ; ) 9.9.9.8.7.6.5.i.4.3.2.1.e164.arpa. IN NAPTR "NAPTR content for +1 234 5678 999" Default value is cc Setting the bl_algorithm module parameter modparam("enum", "bl_algorithm", "txt")
Exported Functions
<function moreinfo="none">enum_query(["suffix"[,"service"]])</function> The function performs an enum query and rewrites the Request-URI with the result of the query. See for more information. Meaning of the parameters is as follows: suffix - Suffix to be appended to the domain name. The suffix parameter can have the following types: string - the suffix is statically assigned pvar - the suffix is the value of an existing pseudo-variable (as string value) service - Service string to be used in the service field. string - the service is statically assigned pvar - the service is the value of an existing pseudo-variable (as string value) This function can be used from REQUEST_ROUTE. <function moreinfo="none">enum_query</function> usage ... # search for "e2u+sip" in freenum.org enum_query("freenum.org."); ... # search for "e2u+sip" in default tree (configured as parameter) enum_query(); ... # search for "e2u+voice:sip" in e164.arpa enum_query("e164.arpa.","voice"); ... # search for service type "sip" or "voice:sip" or "video:sip" # note the '+' sign in front of the second parameter enum_query("e164.arpa.","+sip+voice:sip+video:sip"); ... # quering for service sip and voice:sip enum_query("e164.arpa."); enum_query("e164.arpa.","voice"); # or use instead enum_query("e164.arpa.","+sip+voice:sip"); ...
<function moreinfo="none">enum_pv_query("pvar"[,"suffix"[,"service"]])</function> The function performs an enum query on E.164 number stored in its pseudo variable argument and rewrites the Request-URI with the result of the query. See for more information. Meaning of the parameters is as follows: pvar - Pseudo variable that holds an E.164 number on which enum query is performed. suffix - Suffix to be appended to the domain name. service - Service string to be used in the service field. This function can be used from REQUEST_ROUTE. <function moreinfo="none">enum_pv_query</function> usage ... # search for "e2u+sip" in freenum.org enum_pv_query("$avp(100)", "freenum.org."); ... # search for "e2u+sip" in default tree (configured as parameter) enum_pv_query("$fU"); ... # search for "e2u+voice:sip" in e164.arpa enum_pv_query("$avp(100)","e164.arpa.","voice"); ... # search for service type "sip" or "voice:sip" or "video:sip" # note the '+' sign in front of the second parameter enum_pv_query("$fU","e164.arpa.","+sip+voice:sip+video:sip"); ... # quering for service sip and voice:sip enum_pv_query("$avp(100)","e164.arpa."); enum_pv_query("$avp(100)","e164.arpa.","voice"); # or use instead enum_pv_query("$avp(100)","e164.arpa.","+sip+voice:sip"); ...
<function moreinfo="none">i_enum_query(["suffix"[,"service"]])</function> The function performs an enum query and rewrites the Request-URI with the result of the query. This the Infrastructure-ENUM version of enum_query(). The only difference to enum_query() is in the calculation of the FQDN where NAPTR records are looked for. See ftp://ftp.rfc-editor.org/in-notes/internet-drafts/draft-haberler-carrier-enum-01.txt for the rationale behind this function.
<function moreinfo="none">isn_query(["suffix"[,"service"]])</function> The function performs a ISN query and rewrites the Request-URI with the result of the query. See for more information. Meaning of the parameters is as follows: suffix - Suffix to be appended to the domain name. service - Service string to be used in the service field. This function can be used from REQUEST_ROUTE. See ftp://www.ietf.org/rfc/rfc3872.txt and ftp://www.ietf.org/rfc/rfc2871.txt for information regarding the ITAD part of the ISN string. <function moreinfo="none">isn_query</function> usage ... # search for "e2u+sip" in freenum.org isn_query("freenum.org."); ... # search for "e2u+sip" in default tree (configured as parameter) isn_query(); ... # search for "e2u+voice:sip" in freenum.org isn_query("freenum.org.","voice"); ...
<function moreinfo="none">is_from_user_enum()</function> Checks if the user part of from URI is found in an enum lookup. Returns 1 if yes and -1 if not. This function can be used from REQUEST_ROUTE. <function moreinfo="none">is_from_user_enum</function> usage ... if (is_from_user_enum()) { .... }; ...
opensips-2.2.2/modules/enum/enum.c000066400000000000000000000737261300170765700171260ustar00rootroot00000000000000/* * Enum and E164 related functions * * Copyright (C) 2002-2008 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "enum.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../ut.h" #include "../../resolve.h" #include "../../mem/mem.h" #include "../../dset.h" #include "../../qvalue.h" #include "enum_mod.h" #include "../../regexp.h" #include "../../pvar.h" #include "../../mod_fix.h" /* * Input: E.164 number w/o leading + * * Output: number of digits in the country code * 0 on invalid number * * convention: * 3 digits is the default length of a country code. * country codes 1 and 7 are a single digit. * the following country codes are two digits: 20, 27, 30-34, 36, 39, * 40, 41, 43-49, 51-58, 60-66, 81, 82, 84, 86, 90-95, 98. */ static int cclen(const char *number) { char d1,d2; if (!number || (strlen(number) < 3)) return(0); d1 = number[0]; d2 = number[1]; if (!isdigit((int)d2)) return(0); switch(d1) { case '1': case '7': return(1); case '2': if ((d2 == '0') || (d1 == '7')) return(2); break; case '3': if ((d2 >= '0') && (d1 <= '4')) return(2); if ((d2 == '6') || (d1 == '9')) return(2); break; case '4': if (d2 != '2') return(2); break; case '5': if ((d2 >= '1') && (d1 <= '8')) return(2); break; case '6': if (d1 <= '6') return(2); break; case '8': if ((d2 == '1') || (d1 == '2') || (d1 == '4') || (d1 == '6')) return(2); break; case '9': if (d1 <= '5') return(2); if (d2 == '8') return(2); break; default: return(0); } return(3); } /* return the length of the string until c, if not found returns n */ static inline int findchr(char* p, int c, unsigned int size) { int len=0; for(;len 0) { if (*first == '!') { second = (char *)memchr((void *)(first + 1), '!', len - 1); if (second) { len = len - (second - first + 1); if (len > 0) { third = memchr(second + 1, '!', len); if (third) { pattern->len = second - first - 1; pattern->s = first + 1; replacement->len = third - second - 1; replacement->s = second + 1; return 1; } else { LM_ERR("Third ! missing from regexp\n"); return -1; } } else { LM_ERR("Third ! missing from regexp\n"); return -2; } } else { LM_ERR("Second ! missing from regexp\n"); return -3; } } else { LM_ERR("First ! missing from regexp\n"); return -4; } } else { LM_ERR("Regexp missing\n"); return -5; } } /* Checks if NAPTR record has flag u and its services field * e2u+[service:]sip or * e2u+service[+service[+service[+...]]] */ static inline int sip_match( struct naptr_rdata* naptr, str* service) { if (service->len == 0) { return (naptr->flags_len == 1) && ((naptr->flags[0] == 'u') || (naptr->flags[0] == 'U')) && (naptr->services_len == 7) && ((strncasecmp(naptr->services, "e2u+sip", 7) == 0) || (strncasecmp(naptr->services, "sip+e2u", 7) == 0)); } else if (service->s[0] != '+') { return (naptr->flags_len == 1) && ((naptr->flags[0] == 'u') || (naptr->flags[0] == 'U')) && (naptr->services_len == service->len + 8) && (strncasecmp(naptr->services, "e2u+", 4) == 0) && (strncasecmp(naptr->services + 4, service->s, service->len) == 0) && (strncasecmp(naptr->services + 4 + service->len, ":sip", 4) == 0); } else { /* handle compound NAPTRs and multiple services */ str bakservice, baknaptr; /* we bakup the str */ int naptrlen, len; /* length of the extracted service */ /* RFC 3761, NAPTR service field must start with E2U+ */ if (strncasecmp(naptr->services, "e2u+", 4) != 0) { return 0; } baknaptr.s = naptr->services + 4; /* leading 'e2u+' */ baknaptr.len = naptr->services_len - 4; for (;;) { /* iterate over services in NAPTR */ bakservice.s = service->s + 1; /* leading '+' */ bakservice.len = service->len - 1; naptrlen = findchr(baknaptr.s,'+',baknaptr.len); for (;;) { /* iterate over services in enum_query */ len = findchr(bakservice.s,'+',bakservice.len); if ((naptrlen == len ) && !strncasecmp(baknaptr.s, bakservice.s, len)){ return 1; } if ( (bakservice.len -= len+1) > 0) { bakservice.s += len+1; continue; } break; } if ( (baknaptr.len -= naptrlen+1) > 0) { baknaptr.s += naptrlen+1; continue; } break; } /* no matching service found */ return 0; } } /* * Checks if argument is an e164 number starting with + */ static inline int is_e164(str* _user) { int i; char c; if ((_user->len > 2) && (_user->len < 17) && ((_user->s)[0] == '+')) { for (i = 1; i < _user->len; i++) { c = (_user->s)[i]; if ((c < '0') || (c > '9')) return -1; } return 1; } else { return -1; } } /* * Call is_from_user_enum_2 with module parameter suffix and default service. */ int is_from_user_enum_0(struct sip_msg* _msg, char* _str1, char* _str2) { return is_from_user_enum_2(_msg, (char *)(&suffix), (char *)(&service)); } /* * Call is_from_user_enum_2 with given suffix and default service. */ int is_from_user_enum_1(struct sip_msg* _msg, char* _suffix, char* _str2) { return is_from_user_enum_2(_msg, _suffix, (char *)(&service)); } /* * Check if from user is a valid enum based user, and check to make sure * that the src_ip == an srv record that maps to the enum from user. */ int is_from_user_enum_2(struct sip_msg* _msg, char* _suffix, char* _service) { struct ip_addr addr; struct hostent* he; unsigned short zp; unsigned short proto; char *user_s; int user_len, i, j; char name[MAX_DOMAIN_SIZE]; char uri[MAX_URI_SIZE]; struct sip_uri *furi; struct sip_uri luri; struct rdata* head; str* suffix; str* service; struct rdata* l; struct naptr_rdata* naptr; str pattern, replacement, result; char string[17]; if (parse_from_header(_msg) < 0) { LM_ERR("Failed to parse From header\n"); return -1; } if(_msg->from==NULL || get_from(_msg)==NULL) { LM_DBG("No From header\n"); return -1; } if ((furi = parse_from_uri(_msg)) == NULL) { LM_ERR("Failed to parse From URI\n"); return -1; } suffix = (str*)_suffix; service = (str*)_service; if (is_e164(&(furi->user)) == -1) { LM_ERR("From URI user is not an E164 number\n"); return -1; } /* assert: the from user is a valid formatted e164 string */ user_s = furi->user.s; user_len = furi->user.len; j = 0; for (i = user_len - 1; i > 0; i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; } memcpy(name + j, suffix->s, suffix->len + 1); head = get_record(name, T_NAPTR); if (head == 0) { LM_DBG("No NAPTR record found for %s.\n", name); return -3; } /* we have the naptr records, loop and find an srv record with */ /* same ip address as source ip address, if we do then true is returned */ for (l = head; l; l = l->next) { if (l->type != T_NAPTR) continue; /*should never happen*/ naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LM_ERR("Null rdata in DNS response\n"); free_rdata_list(head); return -4; } LM_DBG("ENUM query on %s: order %u, pref %u, flen %u, flags " "'%.*s', slen %u, services '%.*s', rlen %u, " "regexp '%.*s'\n", name, naptr->order, naptr->pref, naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len, (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len, (int)(naptr->regexp_len), ZSW(naptr->regexp)); if (sip_match(naptr, service) != 0) { if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len, &pattern, &replacement) < 0) { free_rdata_list(head); /*clean up*/ LM_ERR("Parsing of NAPTR regexp failed\n"); return -5; } #ifdef LATER if ((pattern.len == 4) && (strncmp(pattern.s, "^.*$", 4) == 0)) { LM_DBG("Resulted in replacement: '%.*s'\n", replacement.len, ZSW(replacement.s)); retval = set_uri(_msg, replacement.s, replacement.len); free_rdata_list(head); /*clean up*/ return retval; } #endif result.s = &(uri[0]); result.len = MAX_URI_SIZE; /* Avoid making copies of pattern and replacement */ pattern.s[pattern.len] = (char)0; replacement.s[replacement.len] = (char)0; /* We have already checked the size of _msg->parsed_uri.user.s */ memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; if (reg_replace(pattern.s, replacement.s, &(string[0]), &result) < 0) { pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; LM_ERR("Regexp replace failed\n"); free_rdata_list(head); /*clean up*/ return -6; } LM_DBG("Resulted in replacement: '%.*s'\n", result.len, ZSW(result.s)); if(parse_uri(result.s, result.len, &luri) < 0) { LM_ERR("Parsing of URI <%.*s> failed\n", result.len, result.s); free_rdata_list(head); /*clean up*/ return -7; } pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; zp = 0; proto = PROTO_NONE; he = sip_resolvehost(&luri.host, &zp, &proto, (luri.type==SIPS_URI_T)?1:0 , 0); if (he == NULL){ LM_ERR("Resolving URI <%.*s> failed\n", result.len, result.s); free_rdata_list(head); /*clean up*/ return -9; } hostent2ip_addr(&addr, he, 0); if(ip_addr_cmp(&addr, &_msg->rcv.src_ip)) { free_rdata_list(head); return(1); } } } free_rdata_list(head); /*clean up*/ LM_DBG("FAIL\n"); /* must not have found the record */ return(-8); } /* * Add parameter to URI. */ int add_uri_param(str *uri, str *param, str *new_uri) { struct sip_uri puri; char *at; if (parse_uri(uri->s, uri->len, &puri) < 0) { return 0; } /* if current uri has no headers, pad param to the end of uri */ if (puri.headers.len == 0) { memcpy(uri->s + uri->len, param->s, param->len); uri->len = uri->len + param->len; new_uri->len = 0; return 1; } /* otherwise take the long path and create new_uri */ at = new_uri->s; switch (puri.type) { case SIP_URI_T: memcpy(at, "sip:", 4); at = at + 4; break; case SIPS_URI_T: memcpy(at, "sips:", 5); at = at + 5; break; case TEL_URI_T: memcpy(at, "tel:", 4); at = at + 4; break; case TELS_URI_T: memcpy(at, "tels:", 5); at = at + 5; break; default: LM_ERR("Unknown URI scheme <%d>\n", puri.type); return 0; } if (puri.user.len) { memcpy(at, puri.user.s, puri.user.len); at = at + puri.user.len; if (puri.passwd.len) { *at = ':'; at = at + 1; memcpy(at, puri.passwd.s, puri.passwd.len); at = at + puri.passwd.len; }; *at = '@'; at = at + 1; } memcpy(at, puri.host.s, puri.host.len); at = at + puri.host.len; if (puri.port.len) { *at = ':'; at = at + 1; memcpy(at, puri.port.s, puri.port.len); at = at + puri.port.len; } if (puri.params.len) { *at = ';'; at = at + 1; memcpy(at, puri.params.s, puri.params.len); at = at + puri.params.len; } memcpy(at, param->s, param->len); at = at + param->len; *at = '?'; at = at + 1; memcpy(at, puri.headers.s, puri.headers.len); at = at + puri.headers.len; new_uri->len = at - new_uri->s; return 1; } /* * Tests if one result record is "greater" that the other. Non-NAPTR records * greater that NAPTR record. An invalid NAPTR record is greater than a * valid one. Valid NAPTR records are compared based on their * (order,preference). */ static inline int naptr_greater(struct rdata* a, struct rdata* b) { struct naptr_rdata *na, *nb; if (a->type != T_NAPTR) return 1; if (b->type != T_NAPTR) return 0; na = (struct naptr_rdata*)a->rdata; if (na == 0) return 1; nb = (struct naptr_rdata*)b->rdata; if (nb == 0) return 0; return (((na->order) << 16) + na->pref) > (((nb->order) << 16) + nb->pref); } /* * Bubble sorts result record list according to naptr (order,preference). */ static inline void naptr_sort(struct rdata** head) { struct rdata *p, *q, *r, *s, *temp, *start; /* r precedes p and s points to the node up to which comparisons are to be made */ s = NULL; start = *head; while ( s != start -> next ) { r = p = start ; q = p -> next ; while ( p != s ) { if ( naptr_greater(p, q) ) { if ( p == start ) { temp = q -> next ; q -> next = p ; p -> next = temp ; start = q ; r = q ; } else { temp = q -> next ; q -> next = p ; p -> next = temp ; r -> next = q ; r = q ; } } else { r = p ; p = p -> next ; } q = p -> next ; if ( q == s ) s = p ; } } *head = start; } /* * Makes enum query on name. On success, rewrites user part and * replaces Request-URI. */ int do_query(struct sip_msg* _msg, char *user, char *name, str *service) { char uri[MAX_URI_SIZE]; char new_uri[MAX_URI_SIZE]; unsigned int priority, curr_prio, first; qvalue_t q; struct rdata* head; struct rdata* l; struct naptr_rdata* naptr; str pattern, replacement, result, new_result; head = get_record(name, T_NAPTR); if (head == 0) { LM_DBG("No NAPTR record found for %s.\n", name); return -1; } naptr_sort(&head); q = MAX_Q - 10; curr_prio = 0; first = 1; for (l = head; l; l = l->next) { if (l->type != T_NAPTR) continue; /*should never happen*/ naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LM_ERR("Null rdata in DNS response\n"); continue; } LM_DBG("ENUM query on %s: order %u, pref %u, flen %u, flags '%.*s', " "slen %u, services '%.*s', rlen %u, regexp '%.*s'\n", name, naptr->order, naptr->pref, naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len, (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len, (int)(naptr->regexp_len), ZSW(naptr->regexp)); if (sip_match(naptr, service) == 0) continue; if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len, &pattern, &replacement) < 0) { LM_ERR("Parsing of NAPTR regexp failed\n"); continue; } result.s = &(uri[0]); result.len = MAX_URI_SIZE; /* Avoid making copies of pattern and replacement */ pattern.s[pattern.len] = (char)0; replacement.s[replacement.len] = (char)0; if (reg_replace(pattern.s, replacement.s, user, &result) < 0) { pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; LM_ERR("Regexp replace failed\n"); continue; } LM_DBG("Resulted in replacement: '%.*s'\n", result.len, ZSW(result.s)); pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; if (param.len > 0) { if (result.len + param.len > MAX_URI_SIZE - 1) { LM_ERR("URI is too long\n"); continue; } new_result.s = &(new_uri[0]); new_result.len = MAX_URI_SIZE; if (add_uri_param(&result, ¶m, &new_result) == 0) { LM_ERR("Parsing of URI <%.*s> failed\n", result.len, result.s); continue; } if (new_result.len > 0) { result = new_result; } } if (first) { if (set_ruri(_msg, &result) == -1) { goto done; } set_ruri_q(_msg, q); first = 0; curr_prio = ((naptr->order) << 16) + naptr->pref; } else { priority = ((naptr->order) << 16) + naptr->pref; if (priority > curr_prio) { q = q - 10; curr_prio = priority; } if (append_branch(_msg, &result, 0, 0, q, 0, 0) == -1) { goto done; } } } done: free_rdata_list(head); return first ? -1 : 1; } /* * Call enum_query_2 with module parameter suffix and default service. */ int enum_query_0(struct sip_msg* _msg, char* _str1, char* _str2) { return enum_query_2(_msg, NULL, NULL); } /* * Call enum_query_2 with given suffix and default service. */ int enum_query_1(struct sip_msg* _msg, char* _suffix, char* _str2) { return enum_query_2(_msg, _suffix, NULL); } /* * See documentation in README file. */ int enum_query_2(struct sip_msg* _msg, char* _suffix, char* _service) { char *user_s; int user_len, i, j; char name[MAX_DOMAIN_SIZE]; char string[17]; gparam_p gp = NULL; pv_value_t value; str __suffix = {0, 0}, __service = {0, 0}; /* Use the suffix module parameter */ if (_suffix == NULL) { __suffix.s = suffix.s; __suffix.len = suffix.len; } else { gp = (gparam_p) _suffix; if (gp->type == GPARAM_TYPE_PVS) { if (pv_get_spec_value(_msg, gp->v.pvs, &value) != 0 || value.flags & PV_VAL_NULL || value.flags & PV_VAL_EMPTY) { LM_ERR("No PV or NULL value specified for suffix\n"); return E_CFG; } if (value.flags & PV_VAL_STR) { __suffix.s = value.rs.s; __suffix.len = value.rs.len; } else { LM_ERR("Unsupported PV value type\n"); return E_CFG; } } else if (gp->type == GPARAM_TYPE_STR) { __suffix.s = gp->v.sval.s; __suffix.len = gp->v.sval.len; } } /* Use the internal service */ if (_service == NULL) { __service.s = service.s; __service.len = service.len; } else { gp = (gparam_p) _service; if (gp->type == GPARAM_TYPE_PVS) { if (pv_get_spec_value(_msg, gp->v.pvs, &value) != 0 || value.flags & PV_VAL_NULL || value.flags & PV_VAL_EMPTY) { LM_ERR("No PV or NULL value specified for suffix\n"); return E_CFG; } if (value.flags & PV_VAL_STR) { __service.s = value.rs.s; __service.len = value.rs.len; } else { LM_ERR("Unsupported PV value type\n"); return E_CFG; } } else if (gp->type == GPARAM_TYPE_STR) { __service.s = gp->v.sval.s; __service.len = gp->v.sval.len; } } if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("Parsing of R-URI failed\n"); return -1; } if (is_e164(&(_msg->parsed_uri.user)) == -1) { LM_ERR("R-URI user is not an E164 number\n"); return -1; } user_s = _msg->parsed_uri.user.s; user_len = _msg->parsed_uri.user.len; memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; j = 0; for (i = user_len - 1; i > 0; i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; } memcpy(name + j, __suffix.s, __suffix.len + 1); return do_query(_msg, string, name, &__service); } /* * Call isn_query_2 with module parameter suffix and default service. */ int isn_query_0(struct sip_msg* _msg, char* _str1, char* _str2) { return isn_query_2(_msg, (char *)(&isnsuffix), (char *)(&service)); } /* * Call isn_query_2 with given suffix and default service. */ int isn_query_1(struct sip_msg* _msg, char* _suffix, char* _str2) { return isn_query_2(_msg, _suffix, (char *)(&service)); } /* * See documentation in README file. */ int isn_query_2(struct sip_msg* _msg, char* _suffix, char* _service) { char *user_s = NULL; int user_len, i, j; char name[MAX_DOMAIN_SIZE] = {0}; char string[17] = {0}; char szItad[17] = {0}; size_t nItlen = 0; str *suffix, *service; suffix = (str*)_suffix; service = (str*)_service; if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("Parsing of R-URI failed\n"); return -1; } user_s = _msg->parsed_uri.user.s; user_len = _msg->parsed_uri.user.len; memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; /* Do primitive test for correct ISN format, */ /* and set szItad to the ISN ITAD (RFC 3872/2871). */ /* Correct ISN format guessed from freenum.org and IANA */ /* doc http://www.iana.org/assignments/trip-parameters/ */ { char *pAster = strchr(string, '*'); if (pAster && (nItlen = strspn(pAster + sizeof(char), "0123456789"))) strncpy(szItad, pAster + sizeof(char), nItlen); else { LM_ERR("R-URI user does not contain a valid ISN\n"); return -1; } } /* Ammend the original ENUM E.164 string logic to process */ /* ISN numbers instead, which include a nonreversed ITAD. */ i = user_len - nItlen - sizeof(char); /* Ex: *1212 */ j = 0; while (i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; } strcat(name + j, szItad); /* Copy the unreversed ITAD, */ name[j + nItlen] = '.'; /* and append a trailing dot. */ memcpy(name + j + nItlen + sizeof(char), suffix->s, suffix->len + 1); return do_query(_msg, string, name, service); } /*********** INFRASTRUCTURE ENUM ***************/ /* * Call enum_query_2 with default suffix and service. */ int i_enum_query_0(struct sip_msg* _msg, char* _suffix, char* _service) { return i_enum_query_2(_msg, (char *)(&i_suffix), (char *)(&service)); } /* * Call enum_query_2 with given suffix and default service. */ int i_enum_query_1(struct sip_msg* _msg, char* _suffix, char* _service) { return i_enum_query_2(_msg, _suffix, (char *)(&service)); } int i_enum_query_2(struct sip_msg* _msg, char* _suffix, char* _service) { char *user_s; int user_len, i, j; char name[MAX_DOMAIN_SIZE]; char apex[MAX_COMPONENT_SIZE + 1]; char separator[MAX_COMPONENT_SIZE + 1]; int sdl = 0; /* subdomain location: infrastructure enum offset */ int cc_len; struct rdata* head; char string[17]; str *suffix, *service; suffix = (str*)_suffix; service = (str*)_service; if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("Parsing of R-URI failed\n"); return -1; } if (is_e164(&(_msg->parsed_uri.user)) == -1) { LM_ERR("R-URI user is not an E164 number\n"); return -1; } user_s = _msg->parsed_uri.user.s; user_len = _msg->parsed_uri.user.len; /* make sure we don't run out of space in strings */ if (( 2*user_len + MAX_COMPONENT_SIZE + MAX_COMPONENT_SIZE + 4) > MAX_DOMAIN_SIZE) { LM_ERR("Strings too long\n"); return -1; } if ( i_branchlabel.len > MAX_COMPONENT_SIZE ) { LM_ERR("i_branchlabel too long\n"); return -1; } if ( suffix->len > MAX_COMPONENT_SIZE ) { LM_ERR("Suffix too long\n"); return -1; } memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; /* Set up parameters as for user-enum */ memcpy(apex, suffix->s , suffix->len); apex[suffix->len] = (char)0; sdl = 0; /* where to insert i-enum separator */ separator[0] = 0; /* don't insert anything */ cc_len = cclen(string + 1); if (!strncasecmp(i_bl_alg.s,"ebl",i_bl_alg.len)) { sdl = cc_len; /* default */ j = 0; memcpy(name, i_branchlabel.s, i_branchlabel.len); j += i_branchlabel.len; name[j++] = '.'; for (i = cc_len ; i > 0; i--) { name[j++] = user_s[i]; name[j++] = '.'; } memcpy(name + j, suffix->s, suffix->len + 1); LM_DBG("Looking for EBL record for %s.\n", name); head = get_record(name, T_EBL); if (head == 0) { LM_DBG("No EBL found for %s. Defaulting to user ENUM.\n",name); } else { struct ebl_rdata* ebl; ebl = (struct ebl_rdata *) head->rdata; LM_DBG("EBL record for %s is %d / %.*s / %.*s.\n", name, ebl->position, (int)ebl->separator_len, ebl->separator,(int)ebl->apex_len, ebl->apex); if ((ebl->apex_len > MAX_COMPONENT_SIZE) || (ebl->separator_len > MAX_COMPONENT_SIZE)) { LM_ERR("EBL strings too long\n"); return -1; } if (ebl->position > 15) { LM_ERR("EBL position too large (%d)\n", ebl->position); return -1; } sdl = ebl->position; memcpy(separator, ebl->separator, ebl->separator_len); separator[ebl->separator_len] = 0; memcpy(apex, ebl->apex, ebl->apex_len); apex[ebl->apex_len] = 0; free_rdata_list(head); } } else if (!strncasecmp(i_bl_alg.s,"txt",i_bl_alg.len)) { sdl = cc_len; /* default */ memcpy(separator, i_branchlabel.s, i_branchlabel.len); separator[i_branchlabel.len] = 0; /* no change to apex */ j = 0; memcpy(name, i_branchlabel.s, i_branchlabel.len); j += i_branchlabel.len; name[j++] = '.'; for (i = cc_len ; i > 0; i--) { name[j++] = user_s[i]; name[j++] = '.'; } memcpy(name + j, suffix->s, suffix->len + 1); head = get_record(name, T_TXT); if (head == 0) { LM_DBG("TXT found for %s. Defaulting to %d\n", name, cc_len); } else { sdl = atoi(((struct txt_rdata*)head->rdata)->txt); LM_DBG("TXT record for %s is %d.\n", name, sdl); if ((sdl < 0) || (sdl > 10)) { LM_ERR("Sdl %d out of bounds. Set back to cc_len.\n", sdl); sdl = cc_len; } free_rdata_list(head); } } else { /* defaults to CC */ sdl = cc_len; memcpy(separator, i_branchlabel.s, i_branchlabel.len); separator[i_branchlabel.len] = 0; /* no change to apex */ } j = 0; sdl++; /* to avoid comparing i to (sdl+1) */ for (i = user_len - 1; i > 0; i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; if (separator[0] && (i == sdl)) { /* insert the I-ENUM separator here? */ strcpy(name + j, separator); /* we've checked string sizes. */ j += strlen(separator); name[j++] = '.'; } } memcpy(name + j, apex, strlen(apex)+1); return do_query(_msg, string, name, service); } /******************* FQUERY *******************/ /* * Call enum_pv_query_3 with pv arg, module parameter suffix, * and default service. */ int enum_pv_query_1(struct sip_msg* _msg, char* _sp) { return enum_pv_query_3(_msg, _sp, (char *)(&suffix), (char *)(&service)); } /* * Call enum_pv_query_3 with pv and suffix args and default service. */ int enum_pv_query_2(struct sip_msg* _msg, char* _sp, char* _suffix) { return enum_pv_query_3(_msg, _sp, _suffix, (char *)(&service)); } /* * See documentation in README file. */ int enum_pv_query_3(struct sip_msg* _msg, char* _sp, char* _suffix, char* _service) { char *user_s; int user_len, i, j, first; char name[MAX_DOMAIN_SIZE]; char uri[MAX_URI_SIZE]; char new_uri[MAX_URI_SIZE]; unsigned int priority, curr_prio; qvalue_t q; char tostring[17]; struct rdata* head; struct rdata* l; struct naptr_rdata* naptr; str pattern, replacement, result, new_result; str *suffix, *service; char string[17]; pv_spec_t *sp; pv_value_t pv_val; sp = (pv_spec_t *)_sp; suffix = (str*)_suffix; service = (str*)_service; /* * Get R-URI user to tostring */ if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("R-URI parsing failed\n"); return -1; } user_s = _msg->parsed_uri.user.s; user_len = _msg->parsed_uri.user.len; memcpy(&(tostring[0]), user_s, user_len); tostring[user_len] = (char)0; /* * Get E.164 number from pseudo variable */ if (sp && (pv_get_spec_value(_msg, sp, &pv_val) == 0)) { if (pv_val.flags & PV_VAL_STR) { if (pv_val.rs.len == 0 || pv_val.rs.s == NULL) { LM_DBG("Missing E.164 number\n"); return -1; } } else { LM_DBG("Pseudo variable value is not string\n"); return -1; } } else { LM_DBG("Cannot get pseudo variable value\n"); return -1; } if (is_e164(&(pv_val.rs)) == -1) { LM_ERR("pseudo variable does not contain an E164 number\n"); return -1; } user_s = pv_val.rs.s; user_len = pv_val.rs.len; memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; j = 0; for (i = user_len - 1; i > 0; i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; } memcpy(name + j, suffix->s, suffix->len + 1); head = get_record(name, T_NAPTR); if (head == 0) { LM_DBG("No NAPTR record found for %s.\n", name); return -1; } naptr_sort(&head); q = MAX_Q - 10; curr_prio = 0; first = 1; for (l = head; l; l = l->next) { if (l->type != T_NAPTR) continue; /*should never happen*/ naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LM_ERR("Null rdata in DNS response\n"); continue; } LM_DBG("ENUM query on %s: order %u, pref %u, flen %u, flags " "'%.*s', slen %u, services '%.*s', rlen %u, " "regexp '%.*s'\n", name, naptr->order, naptr->pref, naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len, (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len, (int)(naptr->regexp_len), ZSW(naptr->regexp)); if (sip_match(naptr, service) == 0) continue; if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len, &pattern, &replacement) < 0) { LM_ERR("Parsing of NAPTR regexp failed\n"); continue; } result.s = &(uri[0]); result.len = MAX_URI_SIZE; /* Avoid making copies of pattern and replacement */ pattern.s[pattern.len] = (char)0; replacement.s[replacement.len] = (char)0; if (reg_replace(pattern.s, replacement.s, &(tostring[0]), &result) < 0) { pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; LM_ERR("Regexp replace failed\n"); continue; } LM_DBG("Resulted in replacement: '%.*s'\n", result.len, ZSW(result.s)); pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; if (param.len > 0) { if (result.len + param.len > MAX_URI_SIZE - 1) { LM_ERR("URI is too long\n"); continue; } new_result.s = &(new_uri[0]); new_result.len = MAX_URI_SIZE; if (add_uri_param(&result, ¶m, &new_result) == 0) { LM_ERR("Parsing of URI <%.*s> failed\n", result.len, result.s); continue; } if (new_result.len > 0) { result = new_result; } } if (first) { if (set_ruri(_msg, &result) == -1) { goto done; } set_ruri_q(_msg, q); first = 0; curr_prio = ((naptr->order) << 16) + naptr->pref; } else { priority = ((naptr->order) << 16) + naptr->pref; if (priority > curr_prio) { q = q - 10; curr_prio = priority; } if (append_branch(_msg, &result, 0, 0, q, 0, 0) == -1) { goto done; } } } done: free_rdata_list(head); return first ? -1 : 1; } opensips-2.2.2/modules/enum/enum.h000066400000000000000000000050341300170765700171160ustar00rootroot00000000000000/* * Header file for Enum and E164 related functions * * Copyright (C) 2002-2008 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ENUM_H #define ENUM_H #include "../../parser/msg_parser.h" #define MAX_DOMAIN_SIZE 256 #define MAX_COMPONENT_SIZE 32 /* separator, apex, ... This simplifies checks */ /* * Check if from user is an e164 number and has a naptr record */ int is_from_user_enum_0(struct sip_msg* _msg, char* _str1, char* _str2); int is_from_user_enum_1(struct sip_msg* _msg, char* _suffix, char* _str2); int is_from_user_enum_2(struct sip_msg* _msg, char* _suffix, char* _service); /* * do source number destination routing. * that is, make the ruri based on the from number * this is like source ip policy routing */ int enum_pv_query_1(struct sip_msg* _msg, char* _sp); int enum_pv_query_2(struct sip_msg* _msg, char* _sp, char* _suffix); int enum_pv_query_3(struct sip_msg* _msg, char* _sp, char* _suffix, char* _service); /* * Make enum query and if query succeeds, replace current uri with the * result of the query */ int enum_query_0(struct sip_msg* _msg, char* _str1, char* _str2); int enum_query_1(struct sip_msg* _msg, char* _suffix, char* _str2); int enum_query_2(struct sip_msg* _msg, char* _suffix, char* _service); /* * Infrastructure ENUM versions. */ int i_enum_query_0(struct sip_msg* _msg, char* _str1, char* _str2); int i_enum_query_1(struct sip_msg* _msg, char* _suffix, char* _str2); int i_enum_query_2(struct sip_msg* _msg, char* _suffix, char* _service); /* * Make ISN query and if query succeeds, replace current uri with the * result of the query */ int isn_query_0(struct sip_msg* _msg, char* _str1, char* _str2); int isn_query_1(struct sip_msg* _msg, char* _suffix, char* _str2); int isn_query_2(struct sip_msg* _msg, char* _suffix, char* _service); #endif /* ENUM_H */ opensips-2.2.2/modules/enum/enum_mod.c000066400000000000000000000110371300170765700177500ustar00rootroot00000000000000/* * Enum module * * Copyright (C) 2002-2008 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) * 2003-12-15: added suffix parameter to enum_query (jh) */ #include #include #include "../../sr_module.h" #include "enum_mod.h" #include "../../error.h" #include "../../mod_fix.h" #include "enum.h" /* * Module initialization function prototype */ static int mod_init(void); /* * Module parameter variables */ char* domain_suffix = "e164.arpa."; char* tel_uri_params = ""; char* branchlabel = "i"; char* i_enum_suffix = "e164.arpa."; char* bl_algorithm = "cc"; char* isn_suffix = "freenum.org."; /* * Internal module variables */ str suffix; str param; str service; str i_suffix; str i_branchlabel; str i_bl_alg; str isnsuffix; /* * Exported functions */ static cmd_export_t cmds[] = { {"enum_query", (cmd_function)enum_query_0, 0, 0, 0, REQUEST_ROUTE}, {"enum_query", (cmd_function)enum_query_1, 1, fixup_sgp_null, fixup_free_str_null, REQUEST_ROUTE}, {"enum_query", (cmd_function)enum_query_2, 2, fixup_sgp_sgp, fixup_free_str_str, REQUEST_ROUTE}, {"enum_pv_query", (cmd_function)enum_pv_query_1, 1, fixup_pvar_null, fixup_free_pvar_null, REQUEST_ROUTE}, {"enum_pv_query", (cmd_function)enum_pv_query_2, 2, fixup_pvar_str, fixup_free_pvar_str, REQUEST_ROUTE}, {"enum_pv_query", (cmd_function)enum_pv_query_3, 3, fixup_pvar_str_str, fixup_free_pvar_str_str, REQUEST_ROUTE}, {"is_from_user_enum", (cmd_function)is_from_user_enum_0, 0, 0, 0, REQUEST_ROUTE}, {"is_from_user_enum", (cmd_function)is_from_user_enum_1, 1, fixup_str_null, fixup_free_str_null, REQUEST_ROUTE}, {"is_from_user_enum", (cmd_function)is_from_user_enum_2, 2, fixup_str_str, fixup_free_str_str, REQUEST_ROUTE}, {"i_enum_query", (cmd_function)i_enum_query_0, 0, 0, 0, REQUEST_ROUTE}, {"i_enum_query", (cmd_function)i_enum_query_1, 1, fixup_str_null, 0, REQUEST_ROUTE}, {"i_enum_query", (cmd_function)i_enum_query_2, 2, fixup_str_str, 0, REQUEST_ROUTE}, {"isn_query", (cmd_function)isn_query_0, 0, 0, 0, REQUEST_ROUTE}, {"isn_query", (cmd_function)isn_query_1, 1, fixup_str_null, fixup_free_str_null, REQUEST_ROUTE}, {"isn_query", (cmd_function)isn_query_2, 2, fixup_str_str, fixup_free_str_str, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"domain_suffix", STR_PARAM, &domain_suffix}, {"tel_uri_params", STR_PARAM, &tel_uri_params}, {"branchlabel", STR_PARAM, &branchlabel}, {"i_enum_suffix", STR_PARAM, &i_enum_suffix}, {"bl_algorithm", STR_PARAM, &bl_algorithm}, {"isn_suffix", STR_PARAM, &isn_suffix}, {0, 0, 0} }; /* * Module parameter variables */ struct module_exports exports = { "enum", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0 /* per-child init function */ }; static int mod_init(void) { LM_DBG("Initializing\n"); suffix.s = domain_suffix; suffix.len = strlen(suffix.s); param.s = tel_uri_params; param.len = strlen(param.s); service.len = 0; i_suffix.s = i_enum_suffix; i_suffix.len = strlen(i_enum_suffix); i_branchlabel.s = branchlabel; i_branchlabel.len = strlen(branchlabel); i_bl_alg.s = bl_algorithm; i_bl_alg.len = strlen(bl_algorithm); isnsuffix.s = isn_suffix; isnsuffix.len = strlen(isn_suffix); return 0; } opensips-2.2.2/modules/enum/enum_mod.h000066400000000000000000000026351300170765700177610ustar00rootroot00000000000000/* * Enum module headers * * Copyright (C) 2002-2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ENUM_MOD_H #define ENUM_MOD_H #include "../../str.h" /* * Internal module variables */ extern str suffix; /* str version of domain_suffix */ extern str param; /* str version of tel_uri_params */ extern str service; /* default (empty) service */ extern str i_suffix; /* suffix for infrastructure ENUM */ extern str i_branchlabel; /* the label branching off the infrastructure tree */ extern str i_bl_alg; /* how to know where to branch off */ extern str isnsuffix; /* str version of isn_suffix */ #endif /* ENUM_MOD_H */ opensips-2.2.2/modules/event_datagram/000077500000000000000000000000001300170765700200145ustar00rootroot00000000000000opensips-2.2.2/modules/event_datagram/Makefile000066400000000000000000000003551300170765700214570ustar00rootroot00000000000000# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=event_datagram.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/event_datagram/README000066400000000000000000000067731300170765700207110ustar00rootroot00000000000000event_datagram Module Razvan Crainea OpenSIPS Solutions Edited by Razvan Crainea Copyright © 2011 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. DATAGRAM events syntax 1.3. DATAGRAM socket syntax 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.6. Exported Functions 1.7. Example 2. Frequently Asked Questions List of Examples 1.1. E_PIKE_BLOCKED event 1.2. UNIX socket 1.3. UDP socket Chapter 1. Admin Guide 1.1. Overview This is a module which provides a UNIX/UDP SOCKET transport layer implementation for the Event Interface. 1.2. DATAGRAM events syntax The raised events will follow the following grammar: * event = event_name (argument '\n')* * event_name = non-quoted_string'\n' * argument = ((arg_name '::')? arg_value)? | (arg_value) * arg_name = not-quoted_string * arg_value = not-quoted_string | '"' string '"' * not-quoted_string = string - {',",\n,\r} The event name can contain any non-quoted string character, but it is recommended to follow the syntax: E_MODULE_NAME_EXTRA_NAME 1.3. DATAGRAM socket syntax There are two types of sockets used by this module, based on the sockets type. An UNIX socket should follow this syntax: ['unix:'] unix_socket_path An UDP socket should follow this syntax: 'udp:' address ':' port 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.5. Exported Parameters No parameter exported by this module. 1.6. Exported Functions No function exported to be used from configuration file. 1.7. Example This is an example of an event raised by the pike module when it decides an ip should be blocked: Example 1.1. E_PIKE_BLOCKED event E_PIKE_BLOCKED ip::192.168.2.11 Example 1.2. UNIX socket unix:/tmp/opensips_event.sock Example 1.3. UDP socket udp:127.0.0.1:8081 Chapter 2. Frequently Asked Questions 2.1. Both UNIX and UDP type of socket can be used to notify the events? Yes, you can use the both types. 2.2. What is the maximum lenght of a datagram event? The maximum length of a datagram event is 65457 bytes. 2.3. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.4. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.5. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/event_datagram/doc/000077500000000000000000000000001300170765700205615ustar00rootroot00000000000000opensips-2.2.2/modules/event_datagram/doc/event_datagram.xml000066400000000000000000000021561300170765700242700ustar00rootroot00000000000000 %docentities; ]> event_datagram Module &osipsname; Razvan Crainea OpenSIPS Solutions
razvancrainea@opensips.org http://www.opensips.org
Razvan Crainea
razvancrainea@opensips.org
2011 &osipssol;
&admin; &faq;
opensips-2.2.2/modules/event_datagram/doc/event_datagram_admin.xml000066400000000000000000000061521300170765700254400ustar00rootroot00000000000000 &adminguide;
Overview This is a module which provides a UNIX/UDP SOCKET transport layer implementation for the Event Interface.
DATAGRAM events syntax The raised events will follow the following grammar: event = event_name (argument '\n')* event_name = non-quoted_string'\n' argument = ((arg_name '::')? arg_value)? | (arg_value) arg_name = not-quoted_string arg_value = not-quoted_string | '"' string '"' not-quoted_string = string - {',",\n,\r} The event name can contain any non-quoted string character, but it is recommended to follow the syntax: E_MODULE_NAME_EXTRA_NAME
DATAGRAM socket syntax There are two types of sockets used by this module, based on the sockets type. An UNIX socket should follow this syntax: ['unix:'] unix_socket_path An UDP socket should follow this syntax: 'udp:' address ':' port
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters No parameter exported by this module.
Exported Functions No function exported to be used from configuration file.
Example This is an example of an event raised by the pike module when it decides an ip should be blocked: E_PIKE_BLOCKED event E_PIKE_BLOCKED ip::192.168.2.11 UNIX socket unix:/tmp/opensips_event.sock UDP socket udp:127.0.0.1:8081
opensips-2.2.2/modules/event_datagram/doc/event_datagram_faq.xml000066400000000000000000000032351300170765700251160ustar00rootroot00000000000000 &faqguide; Both UNIX and UDP type of socket can be used to notify the events? Yes, you can use the both types. What is the maximum lenght of a datagram event? The maximum length of a datagram event is 65457 bytes. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/event_datagram/event_datagram.c000066400000000000000000000270531300170765700231500ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #include "../../sr_module.h" #include "../../evi/evi_transport.h" #include "../../resolve.h" #include "../../ut.h" #include "event_datagram.h" #include #include #include #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #if !defined(PF_LOCAL) #define PF_LOCAL PF_UNIX #endif /* unix and udp sockets */ static struct dgram_socks sockets; /* send buffer */ static char dgram_buffer[DGRAM_BUFFER_SIZE]; static int dgram_buffer_len; /** * module functions */ static int mod_init(void); static void destroy(void); static int child_init(int); /** * exported functions */ static evi_reply_sock* datagram_parse_udp(str socket); static evi_reply_sock* datagram_parse_unix(str socket); static int datagram_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t * params); static int datagram_match(evi_reply_sock *sock1, evi_reply_sock *sock2); static str datagram_print(evi_reply_sock *sock); /** * module exports */ struct module_exports exports= { "event_datagram", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported asyn functions */ 0, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /** * exported functions for core event interface */ static evi_export_t trans_export_udp = { UDP_STR, /* transport module name */ datagram_raise, /* raise function */ datagram_parse_udp, /* parse function */ datagram_match, /* sockets match function */ 0, /* no free function */ datagram_print, /* socket print function */ DGRAM_UDP_FLAG /* flags */ }; static evi_export_t trans_export_unix = { UNIX_STR, /* transport module name */ datagram_raise, /* raise function */ datagram_parse_unix, /* parse function */ datagram_match, /* sockets match function */ 0, /* no free function */ datagram_print, /* socket print function */ DGRAM_UNIX_FLAG /* flags */ }; /** * init module function */ static int mod_init(void) { LM_NOTICE("initializing module ...\n"); if (register_event_mod(&trans_export_udp)) { LM_ERR("cannot register transport functions for UDP\n"); return -1; } if (register_event_mod(&trans_export_unix)) { LM_ERR("cannot register transport functions for UNIX\n"); return -1; } return 0; } /* returns 0 if sockets match */ static int datagram_match(evi_reply_sock *sock1, evi_reply_sock *sock2) { if (!sock1 || !sock2) return 0; /* if the sockets have different types */ if ((sock1->flags & (DGRAM_UDP_FLAG|DGRAM_UNIX_FLAG)) != (sock2->flags & (DGRAM_UDP_FLAG|DGRAM_UNIX_FLAG))) return 0; if (((sock1->flags & EVI_PORT) != (sock2->flags & EVI_PORT)) || ((sock1->flags & EVI_PORT) && (sock1->port != sock2->port))) return 0; if (sock1->flags & EVI_ADDRESS && sock2->flags & EVI_ADDRESS) { if (!memcmp(sock1->address.s, sock2->address.s, sock1->address.len)) { LM_DBG("socket matched %.*s:%hu\n", sock1->address.len, sock1->address.s, sock1->port); return 1; } } return 0; } static evi_reply_sock* datagram_parse(str socket, int is_unix) { evi_reply_sock *sock = NULL; unsigned short port = 0; char *p = NULL, *host = 0; int len = 0; struct hostent *hentity; if (!socket.len || !socket.s) { LM_ERR("no socket specified\n"); return NULL; } len = socket.len; host = socket.s; if (!is_unix) { p = memchr(host, COLON_C, len); if (!p || p == host) { LM_ERR("port not specified <%.*s>\n", len, host); return NULL; } port = str2s(p + 1, host + len - p - 1, 0); if (port == 0) { LM_DBG("malformed port: %.*s\n", (int)(host + len - p - 1), p + 1); return NULL; } LM_DBG("port is %d\n", port); len = p - host; } /* host */ if (!host || len <= 0) { LM_ERR("malformed address %s\n", host); goto error; } sock = shm_malloc(sizeof(evi_reply_sock) + len); if (!sock) { LM_ERR("no more memory for socket\n"); return NULL; } memset(sock, 0, sizeof(evi_reply_sock)); /* only UDP has port */ if (port) { sock->flags = EVI_PORT; sock->port = port; /* also build sockaddr */ *p = 0; hentity = resolvehost(host, 0); if (!hentity) { LM_ERR("cannot resolve host %s\n", host); goto error; } if(hostent2su(&sock->src_addr.udp_addr, hentity, 0, port)){ LM_ERR("failed to resolve %s\n", host); goto error; } /* restore colon */ *p = COLON_C; sock->flags |= EVI_SOCKET | DGRAM_UDP_FLAG; } else { sock->src_addr.unix_addr.sun_family = AF_LOCAL; memcpy(sock->src_addr.unix_addr.sun_path, host, len); sock->src_addr.unix_addr.sun_path[len] = 0; sock->flags |= EVI_SOCKET | DGRAM_UNIX_FLAG; } LM_DBG("address is <%.*s>\n", len, host); sock->address.s = (char *) (sock + 1); sock->address.len = len; memcpy(sock->address.s, host, len); sock->flags |= EVI_ADDRESS; /* needs expire */ sock->flags |= EVI_EXPIRE; return sock; error: if (sock) shm_free(sock); return NULL; } static evi_reply_sock* datagram_parse_udp(str socket) { return datagram_parse(socket, 0); } static evi_reply_sock* datagram_parse_unix(str socket) { return datagram_parse(socket, 1); } #define DO_PRINT(_s, _l) \ do { \ if (datagram_print_s.len + (_l) > datagram_print_len) { \ int new_len = (datagram_print_s.len + (_l)) * 2; \ char *new_s = pkg_realloc(datagram_print_s.s, new_len); \ if (!new_s) { \ LM_ERR("no more pkg mem to realloc\n"); \ goto end; \ } \ datagram_print_s.s = new_s; \ datagram_print_len = new_len; \ } \ memcpy(datagram_print_s.s + datagram_print_s.len, (_s), (_l)); \ datagram_print_s.len += (_l); \ } while (0) static int datagram_print_len = 0; static str datagram_print_s = { 0, 0 }; static str datagram_print(evi_reply_sock *sock) { str aux; datagram_print_s.len = 0; if (!sock) { LM_DBG("Nothing to print"); goto end; } if (sock->flags & EVI_ADDRESS) DO_PRINT(sock->address.s, sock->address.len); if (sock->flags & EVI_PORT) { DO_PRINT(":", 1); aux.s = int2str(sock->port, &aux.len); DO_PRINT(aux.s, aux.len); } end: return datagram_print_s; } #undef DO_PRINT #define DO_COPY(buff, str, len) \ do { \ if ((buff) - dgram_buffer + 1 > DGRAM_BUFFER_SIZE) { \ LM_ERR("buffer too small\n"); \ return -1; \ } \ memcpy((buff), (str), (len)); \ buff += (len); \ } while (0) /* builds parameters list */ static int datagram_build_params(str* ev_name, evi_params_p ev_params) { evi_param_p node; int len; char *buff, *int_s, *p, *end, *old; char quote = QUOTE_C, esc = ESC_C; if (ev_params && ev_params->flags & (DGRAM_UDP_FLAG | DGRAM_UNIX_FLAG)) { LM_DBG("buffer already built\n"); return dgram_buffer_len; } dgram_buffer_len = 0; /* first is event name - cannot be larger than the buffer size */ memcpy(dgram_buffer, ev_name->s, ev_name->len); dgram_buffer[ev_name->len] = PARAM_SEP; buff = dgram_buffer + ev_name->len + 1; dgram_buffer_len = ev_name->len + 1; if (!ev_params) goto end; for (node = ev_params->first; node; node = node->next) { /* parameter name */ if (node->name.len && node->name.s) { DO_COPY(buff, node->name.s, node->name.len); DO_COPY(buff, ATTR_SEP_S, ATTR_SEP_LEN); } if (node->flags & EVI_STR_VAL) { /* it is a string value */ if (node->val.s.len && node->val.s.s) { /* check to see if enclose is needed */ end = node->val.s.s + node->val.s.len; for (p = node->val.s.s; p < end; p++) if (*p == PARAM_SEP) break; if (p == end) { /* copy the whole buffer */ DO_COPY(buff, node->val.s.s, node->val.s.len); } else { DO_COPY(buff, "e, 1); old = node->val.s.s; /* search for '"' to escape */ for (p = node->val.s.s; p < end; p++) if (*p == QUOTE_C) { DO_COPY(buff, old, p - old); DO_COPY(buff, &esc, 1); old = p; } /* copy the rest of the string */ DO_COPY(buff, old, p - old); DO_COPY(buff, "e, 1); } } } else if (node->flags & EVI_INT_VAL) { int_s = int2str(node->val.n, &len); DO_COPY(buff, int_s, len); } else { LM_DBG("unknown parameter type [%x]\n", node->flags); } *buff = PARAM_SEP; buff++; } end: *buff = PARAM_SEP; buff++; /* set buffer len */ dgram_buffer_len = buff - dgram_buffer; if (ev_params) ev_params->flags |= (DGRAM_UDP_FLAG | DGRAM_UNIX_FLAG); return dgram_buffer_len; } #undef DO_COPY static int datagram_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t *params) { int ret; if (!sock || !(sock->flags & EVI_SOCKET)) { LM_ERR("no socket found\n"); return -1; } /* check the socket type */ if (!(sock->flags & (DGRAM_UDP_FLAG | DGRAM_UNIX_FLAG))) { LM_ERR("invalid socket type\n"); return -1; } /* build the params list */ if (datagram_build_params(ev_name, params) < 0) { LM_ERR("error while building parameters list\n"); return -1; } /* send data */ if (sock->flags & DGRAM_UDP_FLAG) { ret = sendto(sockets.udp_sock, dgram_buffer, dgram_buffer_len, 0, &sock->src_addr.udp_addr.s, sizeof(struct sockaddr_in)); } else { ret = sendto(sockets.unix_sock, dgram_buffer, dgram_buffer_len, 0, &sock->src_addr.udp_addr.s, sizeof(struct sockaddr_un)); } if (ret < 0) { LM_ERR("Cannot raise datagram event due to %d:%s\n", errno, strerror(errno)); return -1; } return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module ...\n"); close(sockets.unix_sock); close(sockets.udp_sock); } static int create_socket(int family) { int flags, sock = socket(family, SOCK_DGRAM, 0); if (sock == -1) goto error; /* Turn non-blocking mode on for sending*/ flags = fcntl(sock, F_GETFL); if (flags == -1) { LM_ERR("fcntl failed: %s\n", strerror(errno)); goto close_error; } if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("fcntl: set non-blocking failed: %s\n", strerror(errno)); goto close_error; } return sock; close_error: close(sock); error: return -1; } static int child_init(int rank) { LM_DBG("init_child [%d] pid [%d]\n", rank, getpid()); /* initialize the unix socket */ sockets.unix_sock = create_socket(AF_LOCAL); if (sockets.unix_sock == -1) { LM_ERR("cannot create unix socket: %s\n", strerror(errno)); return -1; } /* initilize the udp socket */ sockets.udp_sock = create_socket(AF_INET); if (sockets.udp_sock == -1) { LM_ERR("cannot create udp socket: %s\n", strerror(errno)); goto error; } return 0; error: close(sockets.unix_sock); return -1; } opensips-2.2.2/modules/event_datagram/event_datagram.h000066400000000000000000000027621300170765700231550ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef _EV_DATAGRAM_H_ #define _EV_DATAGRAM_H_ /* transport protocols name */ #define UDP_NAME "udp" #define UDP_STR { UDP_NAME, sizeof(UDP_NAME) - 1} #define UNIX_NAME "unix" #define UNIX_STR { UNIX_NAME, sizeof(UNIX_NAME) - 1} /* module flag */ #define DGRAM_UDP_FLAG (1 << 30) #define DGRAM_UNIX_FLAG (1 << 29) /* default buffer size */ #define DGRAM_BUFFER_SIZE 16384 /* separation char */ #define COLON_C ':' #define PARAM_SEP '\n' #define QUOTE_C '"' #define ESC_C '\\' #define ATTR_SEP_S "::" #define ATTR_SEP_LEN (sizeof(ATTR_SEP_S) - 1) struct dgram_socks { int udp_sock; int unix_sock; }; #endif opensips-2.2.2/modules/event_flatstore/000077500000000000000000000000001300170765700202375ustar00rootroot00000000000000opensips-2.2.2/modules/event_flatstore/Makefile000066400000000000000000000002621300170765700216770ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=event_flatstore.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/event_flatstore/README000066400000000000000000000066141300170765700211260ustar00rootroot00000000000000event_flatstore Module Ionel Cerghit OpenSIPS Solutions Marius Cristian Eseanu OpenSIPS Solutions Robert-Vladut Patrascu OpenSIPS Solutions Copyright © 2015 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Flatstore socket syntax 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.4. External Libraries or Applications 1.5. Exported Parameters 1.5.1. max_open_sockets (integer) 1.5.2. delimiter (string) 1.5.3. file_permissions (string) 1.6. Exported Functions 1.7. Exported MI Functions 1.7.1. evi_flat_rotate List of Examples 1.1. Set max_open_sockets parameter 1.2. Set delimiter parameter 1.3. Set file_permissions parameter Chapter 1. Admin Guide 1.1. Overview The event_flatstore module provides a logging facility for different events, triggered through the OpenSIPS Event Interface, directly from the OpenSIPS script. The module logs the events along with their parameters in plain text files. 1.2. Flatstore socket syntax flatstore:path_to_file Meanings: * flatstore: - informs the Event Interface that the events sent to this subscriber should be handled by the event_flatstore module. * path_to_file - path to the file where the logged events will be appended to. The file will be created if it does not exist. It must be a valid path and not a directory. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.4. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.5. Exported Parameters 1.5.1. max_open_sockets (integer) Defines the maximum number of simultaneously opened files by the module. If the maximum limit is reached, an error message will be thrown, and further subscriptions will only be possible after at least one of the current subscriptions will expire. Default value is “100â€. Example 1.1. Set max_open_sockets parameter ... modparam("event_flatstore", "max_open_sockets", 200) ... 1.5.2. delimiter (string) Sets the separator between the parameters of the event in the logging file. Default value is “,â€. Example 1.2. Set delimiter parameter ... modparam("event_flatstore", "delimiter", ";") ... 1.5.3. file_permissions (string) Sets the permissions for the newly created logs. It expects a string representation of a octal value. Default value is “644â€. Example 1.3. Set file_permissions parameter ... modparam("event_flatstore", "file_permissions", "664") ... 1.6. Exported Functions No exported functions to be used in the configuration file. 1.7. Exported MI Functions 1.7.1. evi_flat_rotate It makes the processes reopen the file specified as a parameter to the command in order to be compatible with a logrotate command. If the function is not called after the mv command is executed, the module will continue to write in the renamed file. Name: evi_flat_rotate Parameters: path_to_file MI FIFO Command Format: :evi_flat_rotate:_path_to_log_file_ _empty_line_ opensips-2.2.2/modules/event_flatstore/doc/000077500000000000000000000000001300170765700210045ustar00rootroot00000000000000opensips-2.2.2/modules/event_flatstore/doc/event_flatstore.xml000066400000000000000000000030501300170765700247300ustar00rootroot00000000000000 %docentities; ]> event_flatstore Module &osipsname; Ionel Cerghit OpenSIPS Solutions
ionel.cerghit@gmail.com http://www.opensips.org
Marius Cristian Eseanu OpenSIPS Solutions
eseanu.cristian@gmail.com http://www.opensips.org
Robert-Vladut Patrascu OpenSIPS Solutions
rvlad.patrascu@gmail.com http://www.opensips.org
2015 &osipssol;
&admin;
opensips-2.2.2/modules/event_flatstore/doc/event_flatstore_admin.xml000066400000000000000000000103071300170765700261030ustar00rootroot00000000000000 &adminguide;
Overview The event_flatstore module provides a logging facility for different events, triggered through the &osips; Event Interface, directly from the &osips; script. The module logs the events along with their parameters in plain text files.
Flatstore socket syntax flatstore:path_to_file Meanings: flatstore: - informs the Event Interface that the events sent to this subscriber should be handled by the event_flatstore module. path_to_file - path to the file where the logged events will be appended to. The file will be created if it does not exist. It must be a valid path and not a directory.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters
<varname>max_open_sockets</varname> (integer) Defines the maximum number of simultaneously opened files by the module. If the maximum limit is reached, an error message will be thrown, and further subscriptions will only be possible after at least one of the current subscriptions will expire. Default value is 100. Set <varname>max_open_sockets</varname> parameter ... modparam("event_flatstore", "max_open_sockets", 200) ...
<varname>delimiter</varname> (string) Sets the separator between the parameters of the event in the logging file. Default value is ,. Set <varname>delimiter</varname> parameter ... modparam("event_flatstore", "delimiter", ";") ...
<varname>file_permissions</varname> (string) Sets the permissions for the newly created logs. It expects a string representation of a octal value. Default value is 644. Set <varname>file_permissions</varname> parameter ... modparam("event_flatstore", "file_permissions", "664") ...
Exported Functions No exported functions to be used in the configuration file.
Exported MI Functions
<function>evi_flat_rotate</function> It makes the processes reopen the file specified as a parameter to the command in order to be compatible with a logrotate command. If the function is not called after the mv command is executed, the module will continue to write in the renamed file. Name: evi_flat_rotate Parameters: path_to_file MI FIFO Command Format: :evi_flat_rotate:_path_to_log_file_ _empty_line_
opensips-2.2.2/modules/event_flatstore/event_flatstore.c000066400000000000000000000423361300170765700236170ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Project * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2015-06-20 created by Ionel Cerghit, Robert-Vladut Patrascu, Marius Cristian Eseanu */ #include #include #include #include #include #include "event_flatstore.h" #include "../../mem/mem.h" #include "../../locking.h" #include "../../sr_module.h" #include "../../evi/evi_transport.h" #include "../../mem/shm_mem.h" #include "../../locking.h" #include "../../ut.h" static int mod_init(void); static void destroy(void); static int child_init(int rank); static void verify_delete(void); static void flat_free(evi_reply_sock *sock); static str flat_print(evi_reply_sock *sock); static int flat_match(evi_reply_sock *sock1, evi_reply_sock *sock2); static evi_reply_sock* flat_parse(str socket); static struct mi_root* mi_rotate(struct mi_root* root, void *param); static int flat_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t * params); static int *opened_fds; static int *rotate_version; static int buff_convert_len; static int cap_params; static str delimiter; static char *dirc; static int dir_size; static char *buff; static struct iovec *io_param ; static struct flat_socket **list_files; static struct flat_deleted **list_deleted_files; static gen_lock_t *global_lock; static int initial_capacity; static str file_permissions; static mode_t file_permissions_oct; static mi_export_t mi_cmds[] = { { "evi_flat_rotate","rotates the files the module dumps events into",mi_rotate,0,0,0}, {0,0,0,0,0,0} }; static param_export_t mod_params[] = { {"max_open_sockets",INT_PARAM, &initial_capacity}, {"delimiter",STR_PARAM, &delimiter.s}, {"file_permissions", STR_PARAM, &file_permissions.s}, {0,0,0} }; struct module_exports exports= { "event_flatstore", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ mod_params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; static evi_export_t trans_export_flat = { FLAT_STR, /* transport module name */ flat_raise, /* raise function */ flat_parse, /* parse function */ flat_match, /* sockets match function */ flat_free, /* free function */ flat_print, /* print socket */ FLAT_FLAG /* flags */ }; /* initialize function */ static int mod_init(void) { int i; opened_fds = NULL; rotate_version = NULL; buff = NULL; buff_convert_len = 0; io_param = NULL; cap_params = 20; dirc = NULL; LM_NOTICE("initializing module ...\n"); if (register_event_mod(&trans_export_flat)) { LM_ERR("cannot register transport functions for SCRIPTROUTE\n"); return -1; } list_files = shm_malloc(sizeof(struct flat_socket*) + sizeof(struct flat_deleted*)); *list_files = NULL; if (!delimiter.s) { delimiter.s = pkg_malloc(sizeof(char)); delimiter.s[0] = ','; delimiter.len = 1; } else { delimiter.len = strlen(delimiter.s); LM_DBG("The delimiter for separating columns in files was set at %.*s\n", delimiter.len, delimiter.s); } if (initial_capacity <= 0 || initial_capacity > 65535) { LM_WARN("wrong maximum open sockets according to the modparam configuration\n"); initial_capacity = 100; } else LM_DBG("Number of files descriptors was set at %d\n", initial_capacity); if (!file_permissions.s) file_permissions_oct = 0644; else { char *endptr = NULL; file_permissions_oct = strtol(file_permissions.s, &endptr, 8); if (*endptr != '\0') { LM_DBG("file permissions invalid\n"); file_permissions_oct = 0644; } } LM_DBG("file permissions set to: %o\n", file_permissions_oct); if (!list_files) { LM_ERR("no more memory for list pointer\n"); return -1; } list_deleted_files = (struct flat_deleted**)(list_files + 1); global_lock = lock_alloc(); if (global_lock == NULL) { LM_ERR("Failed to allocate lock \n"); return -1; } if (lock_init(global_lock) == NULL) { LM_ERR("Failed to init lock \n"); return -1; } opened_fds = pkg_malloc(initial_capacity * sizeof(int)); rotate_version = pkg_malloc(initial_capacity * sizeof(int)); memset(rotate_version, 0, initial_capacity * sizeof(int)); for(i = 0; i < initial_capacity; i++) opened_fds[i] = -1; return 0; } /* free allocated memory */ static void destroy(void){ struct flat_socket* list_header = *list_files; struct flat_socket* tmp; struct flat_deleted *deleted_header = *list_deleted_files; struct flat_deleted *aux; LM_NOTICE("destroying module ...\n"); /* lock destroy and deallocate */ lock_destroy(global_lock); lock_dealloc(global_lock); /* free file descriptors list from shared memory */ while (list_header != NULL) { tmp = list_header; list_header = list_header->next; shm_free(tmp); } /* free deleted files from shared memory */ while (deleted_header != NULL) { aux = deleted_header; deleted_header = deleted_header->next; shm_free(aux); } shm_free(list_files); } /* it does not do nothing */ static int child_init(int rank){ return 0; } /* compare two str values */ static int str_cmp(str a , str b){ if(a.len == b.len && strncmp(a.s,b.s,a.len)==0) return 1; return 0; } /* search for a file descriptor using the file's path */ static struct flat_socket *search_for_fd(str value){ struct flat_socket *list = *list_files; while (list != NULL) { if (str_cmp(list->path, value)) { /* file descriptor found */ return list; } list = list->next; } /* file descriptor not found */ return NULL; } static struct mi_root* mi_rotate(struct mi_root* root, void *param){ /* sanity checks */ if (!root || !root->node.kids) { LM_ERR("empty root tree\n"); return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); } if (root->node.kids->value.s == NULL || root->node.kids->value.len == 0) { LM_ERR("Missing value\n"); return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); } /* search for a flat_socket structure that contains the file descriptor * we need to rotate */ lock_get(global_lock); struct flat_socket *found_fd = search_for_fd(root->node.kids->value); if (found_fd == NULL) { LM_DBG("Not found path %.*s [lung : %d]\n",root->node.kids->value.len, root->node.kids->value.s, root->node.kids->value.len); lock_release(global_lock); return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM));; } LM_DBG("Found file descriptor and updating rotating version for %s, to %d\n",found_fd->path.s, found_fd->rotate_version + 1); found_fd->rotate_version++; lock_release(global_lock); /* return a mi_root structure with a success return code*/ return init_mi_tree( 200, MI_SSTR(MI_OK)); } static int flat_match (evi_reply_sock *sock1, evi_reply_sock *sock2) { struct flat_socket *fs1; struct flat_socket *fs2; if (sock1 != NULL && sock2 != NULL && sock1->params != NULL && sock2->params != NULL) { fs1 = (struct flat_socket *)sock1->params; fs2 = (struct flat_socket *)sock2->params; /* if the path is equal then the file descriptor structures are equal*/ return str_cmp(fs1->path, fs2->path); } /* not equal */ return 0; } static int insert_in_list(struct flat_socket *entry) { struct flat_socket *head = *list_files, *aux, *parent = NULL; int expected = initial_capacity - 1; lock_get(global_lock); if (head == NULL) { LM_DBG("Its the single entry in list [%s]\n", entry->path.s); entry->file_index_process = 0; *list_files = entry; entry->prev = NULL; entry->next = NULL; lock_release(global_lock); return 0; } if (head->file_index_process < initial_capacity - 1) { LM_DBG("Inserting [%s] at the head of the list, index: [%d]\n",entry->path.s, head->file_index_process + 1); entry->file_index_process = head->file_index_process + 1; entry->prev = NULL; entry->next = head; head->prev = entry; *list_files = entry; lock_release(global_lock); return 0; } for (aux = head; aux != NULL; aux = aux->next, expected--) { if(aux->file_index_process != expected){ LM_DBG("Inserting [%s] in a gap, index: [%d]\n", entry->path.s, expected); entry->file_index_process = expected; entry->prev = aux->prev; entry->next = aux; aux->prev =entry; entry->prev->next = entry; lock_release(global_lock); return 0; } parent = aux; } if (expected >= 0) { LM_DBG("Inserting [%s] at end of list, index: [%d]\n", entry->path.s, expected); entry->file_index_process = expected; entry->prev = parent; entry->next = NULL; parent->next = entry; lock_release(global_lock); return 0; } lock_release(global_lock); LM_ERR("no more free sockets\n"); return -1; } static evi_reply_sock* flat_parse(str socket){ evi_reply_sock *sock; struct flat_socket* entry; struct stat st_buf; struct flat_deleted *head = *list_deleted_files; struct flat_deleted *aux, *tmp; char *dname; if (!socket.s || !socket.len) { LM_ERR("no socket specified\n"); return NULL; } /* if not all processes finished closing the file find the structure and reuse it */ if (head) { if (str_cmp(socket, head->socket->path)) { LM_DBG("Found structure at head of deleted list, reusing it [%s]\n", head->socket->path.s); *list_deleted_files = head->next; entry = head->socket; shm_free(head); return (evi_reply_sock *)((char*)(entry + 1) + socket.len + 1); } else { for (aux = head; aux->next != NULL; aux=aux->next) if (str_cmp(socket, aux->next->socket->path)) { LM_DBG("Found structure inside deleted list, reusing it [%s]\n", aux->next->socket->path.s); tmp = aux->next; aux->next = aux->next->next; entry = tmp->socket; shm_free(tmp); return (evi_reply_sock *)((char*)(entry + 1) + socket.len + 1); } } } entry = shm_malloc(sizeof(struct flat_socket) + socket.len + 1 + sizeof(evi_reply_sock)); if (!entry) { LM_ERR("not enough shared memory\n"); return NULL; } entry->path.s = (char *)(entry + 1); entry->path.len = socket.len; memcpy(entry->path.s, socket.s, socket.len); entry->path.s[socket.len] = '\0'; /* verify if the path is valid (not a directory) and a file can be created */ if (dirc == NULL || dir_size < (socket.len + 1)) { dirc = pkg_realloc(dirc, (socket.len + 1) * sizeof(char)); dir_size = socket.len + 1; } memcpy(dirc, entry->path.s, socket.len + 1); dname = dirname(dirc); if (stat(dname, &st_buf) < 0) { LM_ERR("invalid directory name\n"); shm_free(entry); return NULL; } memset(&st_buf, 0, sizeof(struct stat)); if (stat(entry->path.s, &st_buf) == 0 && S_ISDIR (st_buf.st_mode)) { LM_ERR("path is a directory\n"); shm_free(entry); return NULL; } if (insert_in_list(entry) < 0) { shm_free(entry); return NULL; } entry->rotate_version = 0; entry->counter_open = 0; sock = (evi_reply_sock *)((char*)(entry + 1) + socket.len + 1); memset(sock, 0, sizeof(evi_reply_sock)); sock->address.s = (char *)(entry + 1); sock->address.len = socket.len + 1; sock->params = entry; sock->flags |= EVI_PARAMS; sock->flags |= EVI_ADDRESS; sock->flags |= EVI_EXPIRE; return sock; } /* check if the local 'version' of the file descriptor asociated with entry fs is different from the global version, if it is different reopen the file */ static void rotating(struct flat_socket *fs){ int index = fs->file_index_process; int rc; lock_get(global_lock); if (opened_fds[index] == -1) { opened_fds[index] = open(fs->path.s,O_RDWR | O_APPEND | O_CREAT, file_permissions_oct); if (opened_fds[index] < 0) { LM_ERR("Opening socket error\n"); lock_release(global_lock); return; } rotate_version[index] = fs->rotate_version; fs->counter_open++; LM_DBG("File %s is opened %d time\n", fs->path.s, fs->counter_open); lock_release(global_lock); return; } if (rotate_version[index] != fs->rotate_version && opened_fds[index] != -1) { /* update version */ rotate_version[index] = fs->rotate_version; lock_release(global_lock); /* rotate */ rc = close(opened_fds[index]); if(rc < 0){ LM_ERR("Closing socket error\n"); return; } opened_fds[index] = open(fs->path.s,O_RDWR | O_APPEND | O_CREAT, file_permissions_oct); if (opened_fds[index] < 0) { LM_ERR("Opening socket error\n"); return; } LM_DBG("Rotating file %s\n",fs->path.s); } else lock_release(global_lock); } static int flat_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t *params) { int idx = 0, offset_buff = 0, len, required_length = 0, nwritten; evi_param_p param; struct flat_socket *entry = (struct flat_socket*)(sock ? sock->params: NULL); char endline = '\n'; char *ptr_buff; int nr_params = 0; if (entry) rotating(entry); verify_delete(); if (!sock || !(sock->params)) { LM_ERR("invalid socket specification\n"); return -1; } if (io_param == NULL) io_param = pkg_malloc(cap_params * sizeof(struct iovec)); if (ev_name && ev_name->s) { io_param[idx].iov_base = ev_name->s; io_param[idx].iov_len = ev_name->len; idx++; } if (params) { for (param = params->first; param; param = param->next) { if (param->flags & EVI_INT_VAL) required_length += INT2STR_MAX_LEN; nr_params++; } if (buff == NULL || required_length > buff_convert_len) { buff = pkg_realloc(buff, required_length * sizeof(char) + 1); buff_convert_len = required_length; } memset(buff, 0, buff_convert_len); for (param = params->first; param; param = param->next) { if(idx + 3 > cap_params){ pkg_realloc(io_param, (cap_params + 20) * sizeof(struct iovec)); cap_params += 20; } io_param[idx].iov_base = delimiter.s; io_param[idx].iov_len = delimiter.len; idx++; if (param->flags & EVI_INT_VAL) { ptr_buff = sint2str(param->val.n, &len); memcpy(buff + offset_buff, ptr_buff, len); io_param[idx].iov_base = buff + offset_buff; io_param[idx].iov_len = len; offset_buff += len; idx++; } else if ((param->flags & EVI_STR_VAL) && param->val.s.len && param->val.s.s) { io_param[idx].iov_base = param->val.s.s; io_param[idx].iov_len = param->val.s.len; idx++; } } } io_param[idx].iov_base = &endline; io_param[idx].iov_len = 1; idx++; do { nwritten = writev(opened_fds[entry->file_index_process], io_param, idx); } while (nwritten < 0 && errno == EINTR); if (ev_name && ev_name->s) LM_DBG("raised event: %.*s has %d parameters\n", ev_name->len, ev_name->s, nr_params); if (nwritten < 0){ LM_ERR("cannot write to socket\n"); return -1; } return 0; } static void flat_free(evi_reply_sock *sock) { struct flat_deleted *head = *list_deleted_files; struct flat_deleted *new; if (sock->params == NULL) { LM_ERR("socket not found\n"); } new = shm_malloc(sizeof(struct flat_deleted)); if (!new) { LM_ERR("no more shm mem\n"); return; } new->socket = (struct flat_socket*)sock->params; LM_DBG("File %s is being deleted...\n",new->socket->path.s); new->next = NULL; lock_get(global_lock); if(head != NULL) new->next = head; *list_deleted_files = new; lock_release(global_lock); verify_delete(); } static str flat_print(evi_reply_sock *sock){ struct flat_socket * fs = (struct flat_socket *)sock->params; return fs->path; } static void verify_delete(void) { struct flat_deleted *head = *list_deleted_files; struct flat_deleted *aux, *prev, *tmp; if (head == NULL && opened_fds == NULL) return; lock_get(global_lock); /* close fd if necessary */ aux = head; prev = NULL; while (aux != NULL) { if (opened_fds[aux->socket->file_index_process] != -1) { LM_DBG("File %s is closed locally, open_counter is %d\n", aux->socket->path.s, aux->socket->counter_open - 1); close(opened_fds[aux->socket->file_index_process]); aux->socket->counter_open--; opened_fds[aux->socket->file_index_process] = -1; } /* free file from lists if all other processes closed it */ if (aux->socket->counter_open == 0) { LM_DBG("File %s is deleted globally, count open reached 0\n", aux->socket->path.s); if (aux->socket->prev) aux->socket->prev->next = aux->socket->next; else *list_files = aux->socket->next; if (aux->socket->next) aux->socket->next->prev = aux->socket->prev; shm_free(aux->socket); if (prev != NULL) prev->next = aux->next; else *list_deleted_files = aux->next; tmp = aux; aux = aux->next; shm_free(tmp); } else { prev = aux; aux = aux->next; } } lock_release(global_lock); } opensips-2.2.2/modules/event_flatstore/event_flatstore.h000066400000000000000000000007311300170765700236150ustar00rootroot00000000000000#ifndef _EV_FLAT_H_ #define _EV_FLAT_H_ #include "../../str.h" #define FLAT_NAME "flatstore" #define FLAT_STR { FLAT_NAME, sizeof(FLAT_NAME) - 1} #define FLAT_FLAG (1<<25) struct flat_socket { str path; unsigned int file_index_process; unsigned int counter_open; unsigned int rotate_version; struct flat_socket *next; struct flat_socket *prev; }; struct flat_deleted { struct flat_socket *socket; struct flat_deleted *next; }; #endifopensips-2.2.2/modules/event_rabbitmq/000077500000000000000000000000001300170765700200355ustar00rootroot00000000000000opensips-2.2.2/modules/event_rabbitmq/Makefile000066400000000000000000000010771300170765700215020ustar00rootroot00000000000000# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=event_rabbitmq.so ifeq ($(CROSS_COMPILE),) RABBITMQ_BUILDER = $(shell \ if pkg-config --exists librabbitmq; then \ echo 'pkg-config librabbitmq'; \ fi) endif ifeq ($(RABBITMQ_BUILDER),) DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -lrabbitmq else DEFS += $(shell $(RABBITMQ_BUILDER) --cflags) LIBS += $(shell $(RABBITMQ_BUILDER) --libs) endif include ../../Makefile.modules opensips-2.2.2/modules/event_rabbitmq/README000066400000000000000000000256071300170765700207270ustar00rootroot00000000000000event_rabbitmq Module Razvan Crainea OpenSIPS Solutions Edited by Razvan Crainea Copyright © 2011 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. RabbitMQ events syntax 1.3. RabbitMQ socket syntax 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. heartbeat (integer) 1.5.2. sync_mode (integer) 1.6. Exported Functions 1.7. Example 1.8. Installation and Running 1.8.1. OpenSIPS config file 2. Frequently Asked Questions List of Examples 1.1. Set heartbeat parameter 1.2. Set sync_mode parameter 1.3. E_PIKE_BLOCKED event 1.4. RabbitMQ socket 1.5. OpenSIPS config script - sample event_rabbitmq usage 2.1. Event subscription 2.2. Event subscription Chapter 1. Admin Guide 1.1. Overview RabbitMQ (http://www.rabbitmq.com/) is an open source messaging server. It's purpose is to manage received messages in queues, taking advantage of the flexible AMQP protocol. This module provides the implementation of a RabbitMQ client for the Event Interface. It is used to send AMQP messages to a RabbitMQ server each time the Event Interface triggers an event subscribed for. The AMQP protocol is only used as the transport layer for notifications. The content of a message is presented in the next section. 1.2. RabbitMQ events syntax The raised events will follow the following grammar: * event = event_name (argument '\n')* * event_name = non-quoted_string'\n' * argument = ((arg_name '::')? arg_value)? | (arg_value) * arg_name = not-quoted_string * arg_value = not-quoted_string | '"' string '"' * not-quoted_string = string - {',",\n,\r} The event name can contain any non-quoted string character, but it is recommended to follow the syntax: E_MODULE_NAME_EXTRA_NAME 1.3. RabbitMQ socket syntax 'rabbitmq:' [user[':'password] '@' host [':' port] '/' [exchange '?'] routing_key Meanings: * 'rabbitmq:' - informs the Event Interface that the events sent to this subscriber should be handled by the event_rabbitmq module. * user - username used for RabbitMQ server authentication. The default value is 'guest'. * password - password used for RabbitMQ server authentication. The default value is 'guest'. * host - host name of the RabbitMQ server. * port - port of the RabbitMQ server. The default value is '5672'. * exchange - exchange of the RabbitMQ server. The default value is ''. * routing_key - this is the routing key used by the AMQP protocol and it is used to identify the queue where the event should be sent. NOTE: if the queue does not exist, this module will not try to create it. 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * librabbitmq-dev 1.5. Exported Parameters 1.5.1. heartbeat (integer) Enables heartbeat support for the AMQP communication. If the client does not receive a heartbeat from server within the specified interval, the socket is automatically closed by the rabbitmq-client. This prevents OpenSIPS from blocking while waiting for a response from a dead rabbitmq-server. The value represents the heartbit interval in seconds. Default value is “0 (disabled)â€. Example 1.1. Set heartbeat parameter ... modparam("event_rabbitmq", "heartbeat", 3) ... 1.5.2. sync_mode (integer) Specifies whether an event raise operates synchronous or asynchronous relative to the process where the raise is triggered.In synchronous mode the process waits for the status of the raise from the actual worker process.In asynchronous mode the process continues its operation without receiving any confirmation. Default value is “0 (asynchronous)â€. Example 1.2. Set sync_mode parameter ... modparam("event_rabbitmq", "sync_mode", 1) ... 1.6. Exported Functions No function exported to be used from configuration file. 1.7. Example This is an example of an event raised by the pike module when it decides an ip should be blocked: Example 1.3. E_PIKE_BLOCKED event E_PIKE_BLOCKED ip::192.168.2.11 Example 1.4. RabbitMQ socket rabbitmq:guest:guest@127.0.0.1:5672/pike # same socket can be written as rabbitmq:127.0.0.1/pike 1.8. Installation and Running 1.8.1. OpenSIPS config file This configuration file presents the usage of the event_rabbitmq module. In this scenario, a message is sent to a RabbitMQ server everytime OpenSIPS receives a MESSAGE request. The parameters passed to the server are the R-URI username and the message body. Example 1.5. OpenSIPS config script - sample event_rabbitmq usage ... loadmodule "signaling.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "uri.so" loadmodule "acc.so" loadmodule "event_rabbitmq.so" startup_route { if (!subscribe_event("E_SIP_MESSAGE", "rabbitmq:127.0.0.1/sipmsg ")) { xlog("L_ERR","cannot the RabbitMQ server to the E_SIP_ME SSAGE event\n"); } } route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (has_totag()) { if (loose_route()) { if (is_method("INVITE")) { record_route(); } route(1); } else { if ( is_method("ACK") ) { if ( t_check_trans() ) { t_relay(); exit; } else { exit; } } sl_send_reply("404","Not here"); } exit; } if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); if (loose_route()) { xlog("L_ERR", "Attempt to route with preloaded Route's [$fu/$tu/$ru/$c i]"); if (!is_method("ACK")) sl_send_reply("403","Preload Route denied"); exit; } if (!is_method("REGISTER|MESSAGE")) record_route(); if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(1); } if (is_method("PUBLISH")) { sl_send_reply("503", "Service Unavailable"); exit; } if (is_method("REGISTER")) { if (!save("location")) sl_reply_error(); exit; } if ($rU==NULL) { sl_send_reply("484","Address Incomplete"); exit; } if (is_method("MESSAGE")) { $avp(attrs) = "user"; $avp(vals) = $rU; $avp(attrs) = "msg"; $avp(vals) = $rb; if (!raise_event("E_SIP_MESSAGE", $avp(attrs), $avp(vals ))) xlog("L_ERR", "cannot raise E_SIP_MESSAGE event\ n"); } if (!lookup("location","m")) { switch ($retcode) { case -1: case -3: t_newtran(); t_reply("404", "Not Found"); exit; case -2: sl_send_reply("405", "Method Not Allowed "); exit; } } route(1); } route[1] { if (is_method("INVITE")) { t_on_failure("1"); } if (!t_relay()) { sl_reply_error(); }; exit; } failure_route[1] { if (t_was_cancelled()) { exit; } } ... Chapter 2. Frequently Asked Questions 2.1. What is the maximum lenght of a AMQP message? The maximum length of a datagram event is 16384 bytes. 2.2. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.3. What is the vhost used by the AMQP server? Currently, the only vhost supported is '/'. 2.4. How can I set a vhost in the socket? This version doesn't support a different vhost. 2.5. How can I send an event to my RabbitMQ server? This module acts as a transport module for the OpenSIPS Event Interface. Therefore, this module should follow the Event Interface behavior: The first step is to subscribe the RabbitMQ server to the OpenSIPS Event Interface. This can be done using the subscribe_event core function: Example 2.1. Event subscription startup_route { subscribe_event("E_RABBITMQ_EVENT", "rabbitmq:127.0.0.1/queue"); } The next step is to raise the event from the script, using the raise_event core function: Example 2.2. Event subscription route { ... /* decided that an event should be raised */ raise_event("E_RABBITMQ_EVENT"); ... } NOTE that the event used above is only to exemplify the usage from the script. Any event published through the OpenSIPS Event Interface can be raised using this module. 2.6. Where can I find more information about RabbitMQ? You can find more information about RabbitMQ on their official website ( http://www.rabbitmq.com/). 2.7. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.8. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/event_rabbitmq/doc/000077500000000000000000000000001300170765700206025ustar00rootroot00000000000000opensips-2.2.2/modules/event_rabbitmq/doc/event_rabbitmq.cfg000066400000000000000000000041221300170765700242640ustar00rootroot00000000000000loadmodule "signaling.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "uri.so" loadmodule "acc.so" loadmodule "event_rabbitmq.so" startup_route { if (!subscribe_event("E_SIP_MESSAGE", "rabbitmq:127.0.0.1/sipmsg")) { xlog("L_ERR","cannot the RabbitMQ server to the E_SIP_MESSAGE event\n"); } } route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (has_totag()) { if (loose_route()) { if (is_method("INVITE")) { record_route(); } route(1); } else { if ( is_method("ACK") ) { if ( t_check_trans() ) { t_relay(); exit; } else { exit; } } sl_send_reply("404","Not here"); } exit; } if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); if (loose_route()) { xlog("L_ERR", "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]"); if (!is_method("ACK")) sl_send_reply("403","Preload Route denied"); exit; } if (!is_method("REGISTER|MESSAGE")) record_route(); if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(1); } if (is_method("PUBLISH")) { sl_send_reply("503", "Service Unavailable"); exit; } if (is_method("REGISTER")) { if (!save("location")) sl_reply_error(); exit; } if ($rU==NULL) { sl_send_reply("484","Address Incomplete"); exit; } if (is_method("MESSAGE")) { $avp(attrs) = "user"; $avp(vals) = $rU; $avp(attrs) = "msg"; $avp(vals) = $rb; if (!raise_event("E_SIP_MESSAGE", $avp(attrs), $avp(vals))) xlog("L_ERR", "cannot raise E_SIP_MESSAGE event\n"); } if (!lookup("location","m")) { switch ($retcode) { case -1: case -3: t_newtran(); t_reply("404", "Not Found"); exit; case -2: sl_send_reply("405", "Method Not Allowed"); exit; } } route(1); } route[1] { if (is_method("INVITE")) { t_on_failure("1"); } if (!t_relay()) { sl_reply_error(); }; exit; } failure_route[1] { if (t_was_cancelled()) { exit; } } opensips-2.2.2/modules/event_rabbitmq/doc/event_rabbitmq.xml000066400000000000000000000021471300170765700243320ustar00rootroot00000000000000 %docentities; ]> event_rabbitmq Module &osipsname; Razvan Crainea OpenSIPS Solutions
razvancrainea@opensips.org &osipssol;
Razvan Crainea
razvancrainea@opensips.org
2011 &osipssol;
&admin; &faq;
opensips-2.2.2/modules/event_rabbitmq/doc/event_rabbitmq_admin.xml000066400000000000000000000152031300170765700254770ustar00rootroot00000000000000 &adminguide;
Overview RabbitMQ (http://www.rabbitmq.com/) is an open source messaging server. It's purpose is to manage received messages in queues, taking advantage of the flexible AMQP protocol. This module provides the implementation of a RabbitMQ client for the Event Interface. It is used to send AMQP messages to a RabbitMQ server each time the Event Interface triggers an event subscribed for. The AMQP protocol is only used as the transport layer for notifications. The content of a message is presented in the next section.
RabbitMQ events syntax The raised events will follow the following grammar: event = event_name (argument '\n')* event_name = non-quoted_string'\n' argument = ((arg_name '::')? arg_value)? | (arg_value) arg_name = not-quoted_string arg_value = not-quoted_string | '"' string '"' not-quoted_string = string - {',",\n,\r} The event name can contain any non-quoted string character, but it is recommended to follow the syntax: E_MODULE_NAME_EXTRA_NAME
RabbitMQ socket syntax 'rabbitmq:' [user[':'password] '@' host [':' port] '/' [exchange '?'] routing_key Meanings: 'rabbitmq:' - informs the Event Interface that the events sent to this subscriber should be handled by the event_rabbitmq module. user - username used for RabbitMQ server authentication. The default value is 'guest'. password - password used for RabbitMQ server authentication. The default value is 'guest'. host - host name of the RabbitMQ server. port - port of the RabbitMQ server. The default value is '5672'. exchange - exchange of the RabbitMQ server. The default value is ''. routing_key - this is the routing key used by the AMQP protocol and it is used to identify the queue where the event should be sent. NOTE: if the queue does not exist, this module will not try to create it.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: librabbitmq-dev
Exported Parameters
<varname>heartbeat</varname> (integer) Enables heartbeat support for the AMQP communication. If the client does not receive a heartbeat from server within the specified interval, the socket is automatically closed by the rabbitmq-client. This prevents OpenSIPS from blocking while waiting for a response from a dead rabbitmq-server. The value represents the heartbit interval in seconds. Default value is 0 (disabled). Set <varname>heartbeat</varname> parameter ... modparam("event_rabbitmq", "heartbeat", 3) ...
<varname>sync_mode</varname> (integer) Specifies whether an event raise operates synchronous or asynchronous relative to the process where the raise is triggered.In synchronous mode the process waits for the status of the raise from the actual worker process.In asynchronous mode the process continues its operation without receiving any confirmation. Default value is 0 (asynchronous). Set <varname>sync_mode</varname> parameter ... modparam("event_rabbitmq", "sync_mode", 1) ...
Exported Functions No function exported to be used from configuration file.
Example This is an example of an event raised by the pike module when it decides an ip should be blocked: E_PIKE_BLOCKED event E_PIKE_BLOCKED ip::192.168.2.11 RabbitMQ socket rabbitmq:guest:guest@127.0.0.1:5672/pike # same socket can be written as rabbitmq:127.0.0.1/pike
Installation and Running
&osips; config file This configuration file presents the usage of the event_rabbitmq module. In this scenario, a message is sent to a RabbitMQ server everytime &osips; receives a MESSAGE request. The parameters passed to the server are the R-URI username and the message body. &osips; config script - sample event_rabbitmq usage ... &rabbitmqcfg; ...
opensips-2.2.2/modules/event_rabbitmq/doc/event_rabbitmq_faq.xml000066400000000000000000000066441300170765700251670ustar00rootroot00000000000000 &faqguide; What is the maximum lenght of a AMQP message? The maximum length of a datagram event is 16384 bytes. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. What is the vhost used by the AMQP server? Currently, the only vhost supported is '/'. How can I set a vhost in the socket? This version doesn't support a different vhost. How can I send an event to my RabbitMQ server? This module acts as a transport module for the OpenSIPS Event Interface. Therefore, this module should follow the Event Interface behavior: The first step is to subscribe the RabbitMQ server to the OpenSIPS Event Interface. This can be done using the subscribe_event core function: Event subscription startup_route { subscribe_event("E_RABBITMQ_EVENT", "rabbitmq:127.0.0.1/queue"); } The next step is to raise the event from the script, using the raise_event core function: Event subscription route { ... /* decided that an event should be raised */ raise_event("E_RABBITMQ_EVENT"); ... } NOTE that the event used above is only to exemplify the usage from the script. Any event published through the OpenSIPS Event Interface can be raised using this module. Where can I find more information about RabbitMQ? You can find more information about RabbitMQ on their official website ( http://www.rabbitmq.com/). Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/event_rabbitmq/event_rabbitmq.c000066400000000000000000000345641300170765700232170ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #include "../../sr_module.h" #include "../../evi/evi_transport.h" #include "../../ut.h" #include "event_rabbitmq.h" #include "rabbitmq_send.h" #include /* send buffer */ static char rmq_buffer[RMQ_BUFFER_SIZE]; static int rmq_buffer_len; /** * module functions */ static int mod_init(void); static int child_init(int); static void destroy(void); /** * module parameters */ static unsigned int heartbeat = 0; extern unsigned rmq_sync_mode; /** * exported functions */ static evi_reply_sock* rmq_parse(str socket); static int rmq_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t * params); static int rmq_match(evi_reply_sock *sock1, evi_reply_sock *sock2); static void rmq_free(evi_reply_sock *sock); static str rmq_print(evi_reply_sock *sock); /* sending process */ static proc_export_t procs[] = { {"RabbitMQ sender", 0, 0, rmq_process, 1, 0}, {0,0,0,0,0,0} }; /* module parameters */ static param_export_t mod_params[] = { {"heartbeat", INT_PARAM, &heartbeat}, {"sync_mode", INT_PARAM, &rmq_sync_mode}, {0,0,0} }; /** * module exports */ struct module_exports exports= { "event_rabbitmq", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ mod_params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ procs, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /** * exported functions for core event interface */ static evi_export_t trans_export_rmq = { RMQ_STR, /* transport module name */ rmq_raise, /* raise function */ rmq_parse, /* parse function */ rmq_match, /* sockets match function */ rmq_free, /* free function */ rmq_print, /* print socket */ RMQ_FLAG /* flags */ }; /** * init module function */ static int mod_init(void) { LM_NOTICE("initializing module ......\n"); if (register_event_mod(&trans_export_rmq)) { LM_ERR("cannot register transport functions for RabbitMQ\n"); return -1; } if (rmq_create_pipe() < 0) { LM_ERR("cannot create communication pipe\n"); return -1; } if ( heartbeat <= 0 || heartbeat > 65535) { LM_WARN("heartbeat is disabled according to the modparam configuration\n"); heartbeat = 0; } else { LM_NOTICE("heartbeat is enabled for [%d] seconds\n", heartbeat); } return 0; } static int child_init(int rank) { if (rmq_init_writer() < 0) { LM_ERR("cannot init writing pipe\n"); return -1; } return 0; } /* returns 0 if sockets don't match */ static int rmq_match(evi_reply_sock *sock1, evi_reply_sock *sock2) { rmq_params_t *p1, *p2; /* sock flags */ if (!sock1 || !sock2 || !(sock1->flags & RMQ_FLAG) || !(sock2->flags & RMQ_FLAG) || !(sock1->flags & EVI_PARAMS) || !(sock2->flags & EVI_PARAMS) || !(sock1->flags & EVI_PORT) || !(sock2->flags & EVI_PORT) || !(sock1->flags & EVI_ADDRESS) || !(sock2->flags & EVI_ADDRESS)) return 0; p1 = (rmq_params_t *)sock1->params; p2 = (rmq_params_t *)sock2->params; if (!p1 || !p2 || !(p1->flags & RMQ_PARAM_RKEY) || !(p2->flags & RMQ_PARAM_RKEY)) return 0; if (sock1->port == sock2->port && sock1->address.len == sock2->address.len && p1->routing_key.len == p2->routing_key.len && p1->user.len == p2->user.len && p1->exchange.len == p2->exchange.len && (p1->user.s == p2->user.s || /* trying the static values */ !memcmp(p1->user.s, p2->user.s, p1->user.len)) && !memcmp(sock1->address.s, sock2->address.s, sock1->address.len) && !memcmp(p1->routing_key.s, p2->routing_key.s, p1->routing_key.len) && !memcmp(p1->exchange.s, p2->exchange.s, p1->exchange.len)) { LM_DBG("socket matched: %s@%s:%hu/%s\n", p1->user.s, sock1->address.s, sock2->port, p1->routing_key.s); return 1; } return 0; } static inline int dupl_string(str* dst, const char* begin, const char* end) { str tmp; if (dst->s) shm_free(dst->s); tmp.s = (char *)begin; tmp.len = end - begin; dst->s = shm_malloc(end - begin + 1); if (!dst->s) { LM_ERR("no more shm memory\n"); return -1; } if (un_escape(&tmp, dst) < 0) return -1; /* NULL-terminate the string */ dst->s[dst->len] = 0; dst->len++; return 0; } /* * This is the parsing function * The socket grammar should be: * [user [':' password] '@'] ip [':' port] '/' [ exchange ?] routing_key */ static evi_reply_sock* rmq_parse(str socket) { evi_reply_sock *sock = NULL; rmq_params_t *param; unsigned int len, i; const char* begin; str prev_token; enum state { ST_USER_HOST, /* Username or hostname */ ST_PASS_PORT, /* Password or port part */ ST_HOST, /* Hostname part */ ST_PORT, /* Port part */ ST_ROUTE_OR_EXPORT /* Routing or export key */ } st; if (!socket.len || !socket.s) { LM_ERR("no socket specified\n"); return NULL; } sock = shm_malloc(sizeof(evi_reply_sock) + sizeof(rmq_params_t)); if (!sock) { LM_ERR("no more memory for socket\n"); return NULL; } memset(sock, 0, sizeof(evi_reply_sock) + sizeof(rmq_params_t)); param = (rmq_params_t*)(sock + 1); prev_token.s = 0; prev_token.len = 0; /* Initialize all attributes to 0 */ st = ST_USER_HOST; begin = socket.s; len = socket.len; for(i = 0; i < len; i++) { switch(st) { case ST_USER_HOST: switch(socket.s[i]) { case '@': st = ST_HOST; if (dupl_string(¶m->user, begin, socket.s + i)) goto err; begin = socket.s + i + 1; param->flags |= RMQ_PARAM_USER; break; case ':': st = ST_PASS_PORT; if (dupl_string(&prev_token, begin, socket.s + i) < 0) goto err; begin = socket.s + i + 1; break; case '/': if (dupl_string(&sock->address, begin, socket.s + i) < 0) goto err; sock->flags |= EVI_ADDRESS; st = ST_ROUTE_OR_EXPORT; begin = socket.s + i + 1; } break; case ST_PASS_PORT: switch(socket.s[i]) { case '@': st = ST_HOST; param->user.len = prev_token.len; param->user.s = prev_token.s; param->flags |= RMQ_PARAM_USER; prev_token.s = 0; if (dupl_string(¶m->pass, begin, socket.s + i) < 0) goto err; param->flags |= RMQ_PARAM_PASS; begin = socket.s + i + 1; break; case '/': sock->address.len = prev_token.len; sock->address.s = prev_token.s; prev_token.s = 0; sock->flags |= EVI_ADDRESS; sock->port = str2s(begin, socket.s + i - begin, 0); if (!sock->port) { LM_DBG("malformed port: %.*s\n", (int)(socket.s + i - begin), begin); goto err; } sock->flags |= EVI_PORT; st = ST_ROUTE_OR_EXPORT; begin = socket.s + i + 1; } break; case ST_HOST: switch(socket.s[i]) { case ':': st = ST_PORT; if (dupl_string(&sock->address, begin, socket.s + i) < 0) goto err; sock->flags |= EVI_ADDRESS; begin = socket.s + i + 1; break; case '/': if (dupl_string(&sock->address, begin, socket.s + i) < 0) goto err; sock->flags |= EVI_ADDRESS; st = ST_ROUTE_OR_EXPORT; begin = socket.s + i + 1; } break; case ST_PORT: switch(socket.s[i]) { case '/': sock->port = str2s(begin, socket.s + i - begin, 0); if (!sock->port) { LM_DBG("malformed port: %.*s\n", (int)(socket.s + i - begin), begin); goto err; } sock->flags |= EVI_PORT; st = ST_ROUTE_OR_EXPORT; begin = socket.s + i + 1; } break; case ST_ROUTE_OR_EXPORT: switch(socket.s[i]) { case '?': if (dupl_string(¶m->exchange, begin, socket.s + i) < 0) goto err; param->flags |= RMQ_PARAM_EKEY; if (dupl_string(¶m->routing_key, socket.s + i + 1, socket.s + len) < 0) goto err; param->flags |= RMQ_PARAM_RKEY; goto success; } if(i == len - 1){ if (dupl_string(¶m->routing_key, begin, socket.s + len) < 0) goto err; param->flags |= RMQ_PARAM_RKEY; goto success; } break; } } LM_WARN("not implemented %.*s\n", socket.len, socket.s); goto err; success: if (!(sock->flags & EVI_PORT) || !sock->port) { sock->port = RMQ_DEFAULT_PORT; sock->flags |= EVI_PORT; } if (!(param->flags & RMQ_PARAM_USER) || !param->user.s) { param->user.s = param->pass.s = RMQ_DEFAULT_UP; param->user.len = param->pass.len = RMQ_DEFAULT_UP_LEN; param->flags |= RMQ_PARAM_USER|RMQ_PARAM_PASS; } param->heartbeat = heartbeat; sock->params = param; sock->flags |= EVI_PARAMS | RMQ_FLAG; return sock; err: LM_ERR("error while parsing socket %.*s\n", socket.len, socket.s); if (prev_token.s) shm_free(prev_token.s); rmq_free_param(param); if (sock->address.s) shm_free(sock->address.s); shm_free(sock); return NULL; } #define DO_PRINT(_s, _l) \ do { \ if (rmq_print_s.len + (_l) > rmq_print_len) { \ int new_len = (rmq_print_s.len + (_l)) * 2; \ char *new_s = pkg_realloc(rmq_print_s.s, new_len); \ if (!new_s) { \ LM_ERR("no more pkg mem to realloc\n"); \ goto end; \ } \ rmq_print_s.s = new_s; \ rmq_print_len = new_len; \ } \ memcpy(rmq_print_s.s + rmq_print_s.len, (_s), (_l)); \ rmq_print_s.len += (_l); \ } while (0) static int rmq_print_len = 0; static str rmq_print_s = { 0, 0 }; static str rmq_print(evi_reply_sock *sock) { rmq_params_t * param; rmq_print_s.len = 0; if (!sock) { LM_DBG("Nothing to print"); goto end; } if (!(sock->flags & EVI_PARAMS)) goto end; param = sock->params; if (param->flags & RMQ_PARAM_USER) { DO_PRINT(param->user.s, param->user.len - 1 /* skip 0 */); DO_PRINT("@", 1); } if (sock->flags & EVI_ADDRESS) DO_PRINT(sock->address.s, sock->address.len - 1); DO_PRINT("/", 1); /* needs to be changed if it can print a key without RMQ_PARAM_RKEY */ if (param->flags & RMQ_PARAM_EKEY) { DO_PRINT(param->exchange.s, param->exchange.len - 1); DO_PRINT("?", 1); } if (param->flags & RMQ_PARAM_RKEY) { DO_PRINT(param->routing_key.s, param->routing_key.len - 1); } end: return rmq_print_s; } #undef DO_PRINT #define DO_COPY(buff, str, len) \ do { \ if ((buff) - rmq_buffer + (len) > RMQ_BUFFER_SIZE) { \ LM_ERR("buffer too small\n"); \ goto end; \ } \ memcpy((buff), (str), (len)); \ buff += (len); \ } while (0) /* builds parameters list */ static int rmq_build_params(str* ev_name, evi_params_p ev_params) { evi_param_p node; int len; char *buff, *int_s, *p, *end, *old; char quote = QUOTE_C, esc = ESC_C; if (ev_params && ev_params->flags & RMQ_FLAG) { LM_DBG("buffer already built\n"); return rmq_buffer_len; } rmq_buffer_len = 0; /* first is event name - cannot be larger than the buffer size */ memcpy(rmq_buffer, ev_name->s, ev_name->len); rmq_buffer_len = ev_name->len; buff = rmq_buffer + ev_name->len; if (!ev_params) goto end; for (node = ev_params->first; node; node = node->next) { *buff = PARAM_SEP; buff++; /* parameter name */ if (node->name.len && node->name.s) { DO_COPY(buff, node->name.s, node->name.len); DO_COPY(buff, ATTR_SEP_S, ATTR_SEP_LEN); } if (node->flags & EVI_STR_VAL) { /* it is a string value */ if (node->val.s.len && node->val.s.s) { /* check to see if enclose is needed */ end = node->val.s.s + node->val.s.len; for (p = node->val.s.s; p < end; p++) if (*p == PARAM_SEP) break; if (p == end) { /* copy the whole buffer */ DO_COPY(buff, node->val.s.s, node->val.s.len); } else { DO_COPY(buff, "e, 1); old = node->val.s.s; /* search for '"' to escape */ for (p = node->val.s.s; p < end; p++) if (*p == QUOTE_C) { DO_COPY(buff, old, p - old); DO_COPY(buff, &esc, 1); old = p; } /* copy the rest of the string */ DO_COPY(buff, old, p - old); DO_COPY(buff, "e, 1); } } } else if (node->flags & EVI_INT_VAL) { int_s = int2str(node->val.n, &len); DO_COPY(buff, int_s, len); } else { LM_DBG("unknown parameter type [%x]\n", node->flags); } } end: /* set buffer end */ *buff = 0; rmq_buffer_len = buff - rmq_buffer + 1; if (ev_params) ev_params->flags |= RMQ_FLAG; return rmq_buffer_len; } #undef DO_COPY static int rmq_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t * params) { rmq_send_t *rmqs; int len; if (!sock || !(sock->flags & RMQ_FLAG)) { LM_ERR("invalid socket type\n"); return -1; } /* sanity checks */ if ((sock->flags & (EVI_ADDRESS|EVI_PORT|EVI_PARAMS)) != (EVI_ADDRESS|EVI_PORT|EVI_PARAMS) || !sock->port || !sock->address.len || !sock->address.s) { LM_ERR("socket doesn't have enough details\n"); return -1; } /* check connection */ /* build the params list */ if ((len = rmq_build_params(ev_name, params)) < 0) { LM_ERR("error while building parameters list\n"); return -1; } rmqs = shm_malloc(sizeof(rmq_send_t) + len); if (!rmqs) { LM_ERR("no more shm memory\n"); return -1; } memcpy(rmqs->msg, rmq_buffer, len); rmqs->sock = sock; if (rmq_send(rmqs) < 0) { LM_ERR("cannot send message\n"); return -1; } return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module ...\n"); /* closing sockets */ rmq_destroy_pipe(); } static void rmq_free(evi_reply_sock *sock) { rmq_send_t *rmqs = shm_malloc(sizeof(rmq_send_t) + 1); if (!rmqs) { LM_ERR("no more shm memory\n"); goto destroy; } rmqs->sock = sock; rmqs->msg[0] = 0; if (rmq_send(rmqs) < 0) { LM_ERR("cannot send message\n"); goto destroy; } return; destroy: if (rmqs) shm_free(rmqs); rmq_destroy(sock); } opensips-2.2.2/modules/event_rabbitmq/event_rabbitmq.h000066400000000000000000000040021300170765700232040ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef _EV_RMQ_H_ #define _EV_RMQ_H_ #include #include #if defined AMQP_VERSION && AMQP_VERSION >= 0x00040000 #define AMQP_VERSION_v04 #include #endif /* transport protocols name */ #define RMQ_NAME "rabbitmq" #define RMQ_STR { RMQ_NAME, sizeof(RMQ_NAME) - 1} /* module flag */ #define RMQ_FLAG (1 << 28 ) /* default buffer size */ #define RMQ_BUFFER_SIZE 16384 /* separation char */ #define COLON_C ':' #define PARAM_SEP '\n' #define QUOTE_C '"' #define ESC_C '\\' #define ATTR_SEP_S "::" #define ATTR_SEP_LEN (sizeof(ATTR_SEP_S) - 1) #define RMQ_DEFAULT_UP "guest" #define RMQ_DEFAULT_UP_LEN (sizeof(RMQ_DEFAULT_UP)) #define RMQ_DEFAULT_MAX 131072 #define RMQ_DEFAULT_VHOST "/" #define RMQ_DEFAULT_PORT 5672 #define RMQ_PARAM_RKEY (1 << 1) #define RMQ_PARAM_CONN (1 << 2) #define RMQ_PARAM_CHAN (1 << 3) #define RMQ_PARAM_USER (1 << 4) #define RMQ_PARAM_PASS (1 << 5) #define RMQ_PARAM_EKEY (1 << 6) typedef struct _rmq_params { str routing_key; str exchange; str user; str pass; amqp_connection_state_t conn; int channel; int flags; int heartbeat; } rmq_params_t; #endif opensips-2.2.2/modules/event_rabbitmq/rabbitmq_send.c000066400000000000000000000315371300170765700230240ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #include "../../evi/evi_transport.h" #include "../../mem/shm_mem.h" #include "../../pt.h" #include "rabbitmq_send.h" #include #include #include #define RMQ_SIZE (sizeof(rmq_send_t *)) #define IS_ERR(_err) (errno == _err) #ifdef HAVE_SCHED_YIELD #include #else #include /** Fake sched_yield if no unistd.h include is available */ #define sched_yield() sleep(0) #endif unsigned rmq_sync_mode = 0; static unsigned nr_procs = 0; /* used to communicate with the sending process */ static int rmq_pipe[2]; /* used to communicate the status of the send (success or fail) from the sending process back to the requesting ones */ static int (*rmq_status_pipes)[2]; /* creates communication pipe */ int rmq_create_pipe(void) { int rc; rmq_pipe[0] = rmq_pipe[1] = -1; /* create pipe */ do { rc = pipe(rmq_pipe); } while (rc < 0 && IS_ERR(EINTR)); if (rc < 0) { LM_ERR("cannot create status pipe [%d:%s]\n", errno, strerror(errno)); return -1; } if (rmq_sync_mode && rmq_create_status_pipes() < 0) { LM_ERR("cannot create communication status pipes\n"); return -1; } return 0; } /* creates status pipes */ int rmq_create_status_pipes(void) { int rc, i; nr_procs = count_init_children(0) + 2; /* + 2 timer processes */ rmq_status_pipes = shm_malloc(nr_procs * sizeof(rmq_pipe)); if (!rmq_status_pipes) { LM_ERR("cannot allocate rmq_status_pipes\n"); return -1; } /* create pipes */ for (i = 0; i < nr_procs; i++) { do { rc = pipe(rmq_status_pipes[i]); } while (rc < 0 && IS_ERR(EINTR)); if (rc < 0) { LM_ERR("cannot create status pipe [%d:%s]\n", errno, strerror(errno)); return -1; } } return 0; } void rmq_destroy_pipe(void) { if (rmq_pipe[0] != -1) close(rmq_pipe[0]); if (rmq_pipe[1] != -1) close(rmq_pipe[1]); if (rmq_sync_mode) rmq_destroy_status_pipes(); } void rmq_destroy_status_pipes(void) { int i; for(i = 0; i < nr_procs; i++) { close(rmq_status_pipes[i][0]); close(rmq_status_pipes[i][1]); } shm_free(rmq_status_pipes); } int rmq_send(rmq_send_t* rmqs) { int rc; int retries = RMQ_SEND_RETRY; int send_status; rmqs->process_idx = process_no; do { rc = write(rmq_pipe[1], &rmqs, RMQ_SIZE); } while (rc < 0 && (IS_ERR(EINTR) || retries-- > 0)); if (rc < 0) { LM_ERR("unable to send rmq send struct to worker\n"); shm_free(rmqs); return RMQ_SEND_FAIL; } /* give a change to the writer :) */ sched_yield(); if (rmq_sync_mode) { retries = RMQ_SEND_RETRY; do { rc = read(rmq_status_pipes[process_no][0], &send_status, sizeof(int)); } while (rc < 0 && (IS_ERR(EINTR) || retries-- > 0)); if (rc < 0) { LM_ERR("cannot receive send status\n"); send_status = RMQ_SEND_FAIL; } return send_status; } else return RMQ_SEND_SUCCESS; } static rmq_send_t * rmq_receive(void) { int rc; int retries = RMQ_SEND_RETRY; rmq_send_t * recv; if (rmq_pipe[0] == -1) return NULL; do { rc = read(rmq_pipe[0], &recv, RMQ_SIZE); } while (rc < 0 && (IS_ERR(EINTR) || retries-- > 0)); if (rc < 0) { LM_ERR("cannot receive send param\n"); return NULL; } return recv; } int rmq_init_writer(void) { int flags; if (rmq_pipe[0] != -1) { close(rmq_pipe[0]); rmq_pipe[0] = -1; } if (rmq_sync_mode) close(rmq_status_pipes[process_no][1]); /* Turn non-blocking mode on for sending*/ flags = fcntl(rmq_pipe[1], F_GETFL); if (flags == -1) { LM_ERR("fcntl failed: %s\n", strerror(errno)); goto error; } if (fcntl(rmq_pipe[1], F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("fcntl: set non-blocking failed: %s\n", strerror(errno)); goto error; } return 0; error: close(rmq_pipe[1]); rmq_pipe[1] = -1; return -1; } static void rmq_init_reader(void) { int i, flags; if (rmq_pipe[1] != -1) { close(rmq_pipe[1]); rmq_pipe[1] = -1; } if (rmq_sync_mode) for(i = 0; i < nr_procs; i++) { close(rmq_status_pipes[i][0]); /* Turn non-blocking mode on for sending*/ flags = fcntl(rmq_status_pipes[i][1], F_GETFL); if (flags == -1) { LM_ERR("fcntl failed: %s\n", strerror(errno)); close(rmq_status_pipes[i][1]); return; } if (fcntl(rmq_status_pipes[i][1], F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("fcntl: set non-blocking failed: %s\n", strerror(errno)); close(rmq_status_pipes[i][1]); return; } } } /* function that checks for error */ static int rmq_error(char const *context, amqp_rpc_reply_t x) { amqp_connection_close_t *mconn; amqp_channel_close_t *mchan; switch (x.reply_type) { case AMQP_RESPONSE_NORMAL: return 0; case AMQP_RESPONSE_NONE: LM_ERR("%s: missing RPC reply type!", context); break; case AMQP_RESPONSE_LIBRARY_EXCEPTION: LM_ERR("%s: %s\n", context, "(end-of-stream)"); break; case AMQP_RESPONSE_SERVER_EXCEPTION: switch (x.reply.id) { case AMQP_CONNECTION_CLOSE_METHOD: mconn = (amqp_connection_close_t *)x.reply.decoded; LM_ERR("%s: server connection error %d, message: %.*s", context, mconn->reply_code, (int)mconn->reply_text.len, (char *)mconn->reply_text.bytes); break; case AMQP_CHANNEL_CLOSE_METHOD: mchan = (amqp_channel_close_t *)x.reply.decoded; LM_ERR("%s: server channel error %d, message: %.*s", context, mchan->reply_code, (int)mchan->reply_text.len, (char *)mchan->reply_text.bytes); break; default: LM_ERR("%s: unknown server error, method id 0x%08X", context, x.reply.id); break; } break; } return -1; } void rmq_free_param(rmq_params_t *rmqp) { /* XXX: hack to make clang happy, because it _always_ * warns when comparing two char * (even casted to void *) */ static void *dummy_holder = RMQ_DEFAULT_UP; if ((rmqp->flags & RMQ_PARAM_USER) && rmqp->user.s && rmqp->user.s != dummy_holder) shm_free(rmqp->user.s); if ((rmqp->flags & RMQ_PARAM_PASS) && rmqp->pass.s && rmqp->pass.s != dummy_holder) shm_free(rmqp->pass.s); if ((rmqp->flags & RMQ_PARAM_RKEY) && rmqp->routing_key.s) shm_free(rmqp->routing_key.s); } void rmq_destroy_param(rmq_params_t *rmqp) { if (!rmqp) return; if (rmqp->conn && rmqp->flags & RMQ_PARAM_CONN) { if (rmqp->flags & RMQ_PARAM_CHAN) { rmq_error("closing channel", amqp_channel_close(rmqp->conn, rmqp->channel, AMQP_REPLY_SUCCESS)); } rmq_error("closing connection", amqp_connection_close(rmqp->conn, AMQP_REPLY_SUCCESS)); if (amqp_destroy_connection(rmqp->conn) < 0) LM_ERR("cannot destroy connection\n"); } rmqp->flags &= ~(RMQ_PARAM_CONN|RMQ_PARAM_CHAN); } void rmq_destroy(evi_reply_sock *sock) { if (!sock) return; if ((sock->flags & EVI_ADDRESS) && sock->address.s) shm_free(sock->address.s); if ((sock->flags & EVI_PARAMS) && sock->params) { rmq_free_param((rmq_params_t *)sock->params); rmq_destroy_param((rmq_params_t *)sock->params); } shm_free(sock); } static int rmq_reconnect(evi_reply_sock *sock) { rmq_params_t * rmqp = (rmq_params_t *)sock->params; #if defined AMQP_VERSION_v04 amqp_socket_t *amqp_sock; #endif int socket; if (!rmqp || !(rmqp->flags & RMQ_PARAM_RKEY)) { LM_ERR("not enough socket info\n"); return -1; } if (!(rmqp->flags & RMQ_PARAM_CONN) || !rmqp->conn) { /* init new connection */ if (!(rmqp->conn = amqp_new_connection())) { LM_ERR("cannot create new connection\n"); return -1; } #if defined AMQP_VERSION_v04 amqp_sock = amqp_tcp_socket_new(rmqp->conn); if (!amqp_sock) { LM_ERR("cannot create AMQP socket\n"); goto destroy_rmqp; } socket = amqp_socket_open(amqp_sock, sock->address.s, sock->port); if (socket < 0) { LM_ERR("cannot open AMQP socket\n"); goto destroy_rmqp; } #else socket = amqp_open_socket(sock->address.s, sock->port); if (socket < 0) { LM_ERR("cannot open AMQP socket\n"); goto destroy_rmqp; } amqp_set_sockfd(rmqp->conn, socket); #endif rmqp->flags |= RMQ_PARAM_CONN; if (rmq_error("Logging in", amqp_login( rmqp->conn, RMQ_DEFAULT_VHOST, 0, RMQ_DEFAULT_MAX, rmqp->heartbeat, AMQP_SASL_METHOD_PLAIN, rmqp->flags & RMQ_PARAM_USER ? rmqp->user.s : RMQ_DEFAULT_UP, rmqp->flags & RMQ_PARAM_PASS ? rmqp->pass.s : RMQ_DEFAULT_UP))) goto destroy_rmqp; } if (!(rmqp->flags & RMQ_PARAM_CHAN)) { rmqp->channel = 1; amqp_channel_open(rmqp->conn, rmqp->channel); rmqp->flags |= RMQ_PARAM_CHAN; if (rmq_error("Opening channel", amqp_get_rpc_reply(rmqp->conn))) goto destroy_rmqp; } return 0; destroy_rmqp: rmq_destroy_param(rmqp); return -1; } #ifdef AMQP_VERSION_v04 static inline int amqp_check_status(rmq_params_t *rmqp, int r, int* re_publish) { switch (r) { case AMQP_STATUS_OK: return 0; case AMQP_STATUS_NO_MEMORY: LM_ERR("no more memory\n"); goto no_close; case AMQP_STATUS_TABLE_TOO_BIG: LM_ERR("A table in the properties was too large to fit in a single frame\n"); goto no_close; case AMQP_STATUS_HEARTBEAT_TIMEOUT: LM_ERR("heartbeat timeout\n"); break; case AMQP_STATUS_CONNECTION_CLOSED: LM_ERR("Connection closed\n"); break; /* this should not happened since we do not use ssl */ case AMQP_STATUS_SSL_ERROR: LM_ERR("SSL error\n"); break; case AMQP_STATUS_TCP_ERROR: LM_ERR("TCP error: %s(%d)\n", strerror(errno), errno); break; /* This is happening on rabbitmq server restart */ case AMQP_STATUS_SOCKET_ERROR: LM_WARN("Socket error\n"); if (*re_publish == 0) *re_publish = 1; break; default: LM_ERR("Unknown AMQP error[%d]: %s(%d)\n", r, strerror(errno), errno); break; } /* we close the connection here to be able to re-connect later */ rmq_destroy_param(rmqp); no_close: return r; } #else static inline int amqp_check_status(rmq_params_t *rmqp, int r, int* re_publish) { if (r != 0) { LM_ERR("Unknown AMQP error [%d] while sending\n", r); /* we close the connection here to be able to re-connect later */ rmq_destroy_param(rmqp); return -1; } return 0; } #endif /* sends the buffer */ static int rmq_sendmsg(rmq_send_t *rmqs) { rmq_params_t * rmqp = (rmq_params_t *)rmqs->sock->params; int ret,rtrn; int re_publish = 0; if (!(rmqp->flags & RMQ_PARAM_CONN)) return 0; /* all checks should be already done */ ret = amqp_basic_publish(rmqp->conn, rmqp->channel, rmqp->flags&RMQ_PARAM_EKEY? amqp_cstring_bytes(rmqp->exchange.s) : AMQP_EMPTY_BYTES , amqp_cstring_bytes(rmqp->routing_key.s), 0, 0, 0, amqp_cstring_bytes(rmqs->msg)); rtrn = amqp_check_status(rmqp, ret, &re_publish); if (rtrn != 0 && re_publish != 0) { if (rmq_reconnect(rmqs->sock) < 0) { LM_ERR("cannot reconnect socket\n"); return rtrn; } /* all checks should be already done */ ret = amqp_basic_publish(rmqp->conn, rmqp->channel, rmqp->flags&RMQ_PARAM_EKEY? amqp_cstring_bytes(rmqp->exchange.s) : AMQP_EMPTY_BYTES , amqp_cstring_bytes(rmqp->routing_key.s), 0, 0, 0, amqp_cstring_bytes(rmqs->msg)); rtrn = amqp_check_status(rmqp, ret, &re_publish); } return rtrn; } void rmq_process(int rank) { int retries, rc; int send_status; /* init blocking reader */ rmq_init_reader(); rmq_send_t * rmqs; /* waiting for commands */ for (;;) { rmqs = rmq_receive(); if (!rmqs || !rmqs->sock) { LM_ERR("invalid receive sock info received\n"); goto end; } /* check if we should disconnect it */ if (!rmqs->msg[0]) { rmq_destroy(rmqs->sock); goto end; } /* check if we should reconnect */ if (rmq_reconnect(rmqs->sock) < 0) { LM_ERR("cannot reconnect socket\n"); send_status = RMQ_SEND_FAIL; goto send_status_reply; } /* send msg */ if (rmq_sendmsg(rmqs)) { LM_ERR("cannot send message\n"); send_status = RMQ_SEND_FAIL; } else { send_status = RMQ_SEND_SUCCESS; } send_status_reply: if (rmq_sync_mode) { retries = RMQ_SEND_RETRY; /* check rmqs->process_idx sanity */ if (rmqs->process_idx >= 0 && rmqs->process_idx < nr_procs) { do { rc = write(rmq_status_pipes[rmqs->process_idx][1], &send_status, sizeof(int)); } while (rc < 0 && (IS_ERR(EINTR) || retries-- > 0)); if (rc < 0) LM_ERR("cannot send status back to requesting process\n"); } } end: if (rmqs) shm_free(rmqs); } } opensips-2.2.2/modules/event_rabbitmq/rabbitmq_send.h000066400000000000000000000026421300170765700230240ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef _RMQ_SEND_H_ #define _RMQ_SEND_H_ #include "event_rabbitmq.h" #define RMQ_SEND_RETRY 3 #define RMQ_SEND_SUCCESS 0 #define RMQ_SEND_FAIL -1 typedef struct _rmq_send { evi_reply_sock *sock; int process_idx; char msg[0]; } rmq_send_t; void rmq_process(int rank); int rmq_create_pipe(void); int rmq_create_status_pipes(void); void rmq_destroy_pipe(void); void rmq_destroy_status_pipes(void); int rmq_init_writer(void); int rmq_send(rmq_send_t * rmqs); void rmq_free_param(rmq_params_t *rmqp); void rmq_destroy(evi_reply_sock *sock); #endif opensips-2.2.2/modules/event_route/000077500000000000000000000000001300170765700173725ustar00rootroot00000000000000opensips-2.2.2/modules/event_route/Makefile000066400000000000000000000003521300170765700210320ustar00rootroot00000000000000# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=event_route.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/event_route/README000066400000000000000000000122451300170765700202560ustar00rootroot00000000000000event_route Module Razvan Crainea OpenSIPS Solutions Edited by Razvan Crainea Edited by Ovidiu Sas Copyright © 2012 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. ROUTE events parameters 1.3. EVENT_ROUTE usage 1.4. EVENT_ROUTE socket syntax 1.5. Dependencies 1.5.1. OpenSIPS Modules 1.5.2. External Libraries or Applications 1.6. Exported Parameters 1.7. Exported Functions 1.7.1. fetch_event_params(pvar_list) 2. Frequently Asked Questions List of Examples 1.1. EVENT_ROUTE usage 1.2. fetch_event_params usage Chapter 1. Admin Guide 1.1. Overview This module provides a simple way for handling different events, triggered through the OpenSIPS Event Interface, directly from the OpenSIPS script. For a specific event, a special route (event_route) has to be declared in the script, and should contain the code that handles the event. The route is executed by the module when the corresponding event is raised by the OpenSIPS Event Interface.Since version 1.12, the way an event is handlend (sync/async) should be specified from the configuration script with the desired keyword following the name of the event (event_route[e, sync]). 1.2. ROUTE events parameters In order to retrieve the parameters of an event, the fetch_event_params(pvar_list) function is used. It receives a single parameter, that consists of a list of parameters names (optional) and the pseudo-variable where the values will be stored. The grammar is: [ param_name= ] pvar [; [ param_name= ] pvar ]* Example: fetch_event_params("$avp(first_param)"); fetch_event_params("ip=$avp(pike_ip)"); fetch_event_params("source=$avp(src);destination=$avp(dst)"); If the name of the parameter is not specified, the avp will be populated according to the order of the parameters, as exported by the event. The following code will populate the $avp(first) avp with the first parameter of the event, $avp(second) with the second one and so on. fetch_event_params("$avp(first);$avp(second);$avp(third)"); NOTE: An event may be triggered within a different event, leading to nested processing. This function will retrieve the parameters of the currently processed event. The event name can contain any non-quoted string character, but it is recommended to follow the syntax: E_MODULE_NAME_EXTRA_NAME 1.3. EVENT_ROUTE usage In order to handle the E_PIKE_BLOCKED event, the following snippet can be used: Example 1.1. EVENT_ROUTE usage event_route[E_PIKE_BLOCKED] { fetch_event_params("ip=$avp(pike-ip)"); xlog("IP $avp(pike-ip) has been blocked\n"); } 1.4. EVENT_ROUTE socket syntax As the OpenSIPS Event Interface requires, the event_route module uses a specific socket syntax: 'route:' event_name Example: route:E_PIKE_BLOCKED 1.5. Dependencies 1.5.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.5.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.6. Exported Parameters The module does not export parameters to be used in configuration script. 1.7. Exported Functions 1.7.1. fetch_event_params(pvar_list) Retrieves the parameters of the event. For more information, please read Section 1.2, “ROUTE events parametersâ€. The the pseudo variables list as described in Section 1.2, “ROUTE events parametersâ€. This function can be used from REQUEST_ROUTE and EVENT_ROUTE. Example 1.2. fetch_event_params usage ... fetch_event_params("$avp(first_param)"); # fetch the first parameter of an event fetch_event_params("ip=$avp(pike_ip)"); # fetch the ip parameter fetch_event_params("source=$avp(src);destination=$avp(dst)"); # fetch th e source and destination parameters ... Chapter 2. Frequently Asked Questions 2.1. Can I declare more routes for handling the same event? No, only a single event_route can be used for a particular event. 2.2. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.3. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.4. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/event_route/doc/000077500000000000000000000000001300170765700201375ustar00rootroot00000000000000opensips-2.2.2/modules/event_route/doc/event_route.xml000066400000000000000000000023411300170765700232200ustar00rootroot00000000000000 %docentities; ]> event_route Module &osipsname; Razvan Crainea OpenSIPS Solutions
razvan@opensips.org http://www.opensips.org
Razvan Crainea
razvancrainea@opensips.org
Ovidiu Sas
osas@voipembedded.com
2012 &osipssol;
&admin; &faq;
opensips-2.2.2/modules/event_route/doc/event_route_admin.xml000066400000000000000000000113531300170765700243730ustar00rootroot00000000000000 &adminguide;
Overview This module provides a simple way for handling different events, triggered through the &osips; Event Interface, directly from the &osips; script. For a specific event, a special route (event_route) has to be declared in the script, and should contain the code that handles the event. The route is executed by the module when the corresponding event is raised by the &osips; Event Interface.Since version 1.12, the way an event is handlend (sync/async) should be specified from the configuration script with the desired keyword following the name of the event (event_route[e, sync]).
ROUTE events parameters In order to retrieve the parameters of an event, the fetch_event_params(pvar_list) function is used. It receives a single parameter, that consists of a list of parameters names (optional) and the pseudo-variable where the values will be stored. The grammar is: [ param_name= ] pvar [; [ param_name= ] pvar ]* Example: fetch_event_params("$avp(first_param)"); fetch_event_params("ip=$avp(pike_ip)"); fetch_event_params("source=$avp(src);destination=$avp(dst)"); If the name of the parameter is not specified, the avp will be populated according to the order of the parameters, as exported by the event. The following code will populate the $avp(first) avp with the first parameter of the event, $avp(second) with the second one and so on. fetch_event_params("$avp(first);$avp(second);$avp(third)"); NOTE: An event may be triggered within a different event, leading to nested processing. This function will retrieve the parameters of the currently processed event. The event name can contain any non-quoted string character, but it is recommended to follow the syntax: E_MODULE_NAME_EXTRA_NAME
EVENT_ROUTE usage In order to handle the E_PIKE_BLOCKED event, the following snippet can be used: EVENT_ROUTE usage event_route[E_PIKE_BLOCKED] { fetch_event_params("ip=$avp(pike-ip)"); xlog("IP $avp(pike-ip) has been blocked\n"); }
EVENT_ROUTE socket syntax As the &osips; Event Interface requires, the event_route module uses a specific socket syntax: 'route:' event_name Example: route:E_PIKE_BLOCKED
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters The module does not export parameters to be used in configuration script.
Exported Functions
<function moreinfo="none">fetch_event_params(pvar_list)</function> Retrieves the parameters of the event. For more information, please read . The the pseudo variables list as described in . This function can be used from REQUEST_ROUTE and EVENT_ROUTE. <function>fetch_event_params</function> usage ... fetch_event_params("$avp(first_param)"); # fetch the first parameter of an event fetch_event_params("ip=$avp(pike_ip)"); # fetch the ip parameter fetch_event_params("source=$avp(src);destination=$avp(dst)"); # fetch the source and destination parameters ...
opensips-2.2.2/modules/event_route/doc/event_route_faq.xml000066400000000000000000000027361300170765700240570ustar00rootroot00000000000000 &faqguide; Can I declare more routes for handling the same event? No, only a single event_route can be used for a particular event. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/event_route/event_route.c000066400000000000000000000350261300170765700221030ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2012-12-xx created (razvancrainea) */ #include "../../sr_module.h" #include "../../evi/evi_transport.h" #include "../../evi/evi_modules.h" #include "../../ut.h" #include "event_route.h" #include "route_send.h" #include #include #include /* default PVAR names */ /** * module functions */ static int mod_init(void); static void destroy(void); static int child_init(int rank); static int scriptroute_fetch(struct sip_msg *msg, char *list); static int fixup_scriptroute_fetch(void **param, int param_no); /** * exported functions */ static evi_reply_sock* scriptroute_parse(str socket); static int scriptroute_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t * params); static int scriptroute_match(evi_reply_sock *sock1, evi_reply_sock *sock2); static str scriptroute_print(evi_reply_sock *sock); static inline int get_script_event_route_ID_by_name(char* name, struct script_event_route *sr, int size); #define SR_SOCK_ROUTE(_s) ((int)(unsigned long)(_s->params)) #define EVENT_ROUTE_MODE_SEP '/' #define EVENT_ROUTE_SYNC 0 #define EVENT_ROUTE_ASYNC 1 /** * * module process * */ static proc_export_t procs[] = { {"event-route handler", 0, 0, event_route_handler, 1, 0}, {0,0,0,0,0,0} }; /** * module exported functions */ static cmd_export_t cmds[]={ {"fetch_event_params", (cmd_function)scriptroute_fetch, 1, fixup_scriptroute_fetch, 0, EVENT_ROUTE|REQUEST_ROUTE }, {0,0,0,0,0,0} }; static param_export_t params[] = { {0, 0, 0} }; /** * module exports */ struct module_exports exports= { "event_route", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ procs, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /** * exported functions for core event interface */ static evi_export_t trans_export_scriptroute = { SCRIPTROUTE_NAME_STR, /* transport module name */ scriptroute_raise, /* raise function */ scriptroute_parse, /* parse function */ scriptroute_match, /* sockets match function */ 0, /* no free function */ scriptroute_print, /* socket print function */ SCRIPTROUTE_FLAG /* flags */ }; /** * init module function */ static int mod_init(void) { LM_NOTICE("initializing module ...\n"); if (register_event_mod(&trans_export_scriptroute)) { LM_ERR("cannot register transport functions for SCRIPTROUTE\n"); return -1; } if (create_pipe() < 0) { LM_ERR("cannot create communication pipe\n"); return -1; } return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module ...\n"); /* closing sockets */ destroy_pipe(); } static int child_init(int rank) { char buffer[EV_SCRIPTROUTE_MAX_SOCK]; str sock_name; str event_name; int idx; if (init_writer() < 0) { LM_ERR("cannot init writing pipe\n"); return -1; } /* * Only the first process registers the subscribers * * We do this in child init because here we are sure that all * the events were subscribed */ if (rank != 1) return 0; /* init the socket buffer */ sock_name.s = buffer; memcpy(buffer, SCRIPTROUTE_NAME, sizeof(SCRIPTROUTE_NAME) - 1); buffer[sizeof(SCRIPTROUTE_NAME) - 1] = COLON_C; /* subscribe the route events - idx starts at 1 */ for (idx = 1; event_rlist[idx].a && event_rlist[idx].name; idx++) { /* build the socket */ event_name.s = event_rlist[idx].name; event_name.len = strlen(event_rlist[idx].name); /* first check if the event exists */ if (evi_get_id(&event_name) == EVI_ERROR) { LM_ERR("Event %s not registered\n", event_name.s); return -1; } LM_DBG("Registering event %s\n", event_rlist[idx].name); if (sizeof(SCRIPTROUTE_NAME)+event_name.len > EV_SCRIPTROUTE_MAX_SOCK) { LM_ERR("socket name too big %d (max: %d)\n", (int)(sizeof(SCRIPTROUTE_NAME) + event_name.len), EV_SCRIPTROUTE_MAX_SOCK); return -1; } memcpy(buffer + sizeof(SCRIPTROUTE_NAME), event_name.s, event_name.len); sock_name.len = event_name.len + sizeof(SCRIPTROUTE_NAME); if (sock_name.len + event_rlist[idx].mode+4 /*"sync"*/ +1 /*'/'*/ > EV_SCRIPTROUTE_MAX_SOCK) { LM_ERR("not enough room in socket name buffer\n"); return -1; } sock_name.s[sock_name.len++] = EVENT_ROUTE_MODE_SEP; switch (event_rlist[idx].mode) { case 0: /*sync*/ memcpy(sock_name.s+sock_name.len, "sync", 4); sock_name.len += 4; break; case 1: /*async*/ memcpy(sock_name.s+sock_name.len, "async", 5); sock_name.len += 5; break; default: LM_ERR("invalid route mode value (%d)\n!" "Possibility of memory corruption\n", event_rlist[idx].mode); return -1; } /* register the subscriber - does not expire */ if (evi_event_subscribe(event_name, sock_name, 0, 0) < 0) { LM_ERR("cannot subscribe to event %s\n", event_name.s); return -1; } } return 0; } /* returns 0 if sockets match */ static int scriptroute_match(evi_reply_sock *sock1, evi_reply_sock *sock2) { if (!sock1 || !sock2) return 0; if (!(sock1->flags & EVI_PARAMS) || !(sock2->flags & EVI_PARAMS) || SR_SOCK_ROUTE(sock1) != SR_SOCK_ROUTE(sock2)) return 0; return 1; } static evi_reply_sock* scriptroute_parse(str socket) { #define SET_MSB(value, type) ((type)value << (sizeof(type) * 8 /*BYTE SIZE*/ - 1)) evi_reply_sock *sock = NULL; static char *dummy_buffer = 0, *name; int idx, mode=-1, name_len = 0; char* mode_pos; if (!socket.len || !socket.s) { LM_ERR("no socket specified\n"); return NULL; } mode_pos = q_memrchr(socket.s, EVENT_ROUTE_MODE_SEP, socket.len); if (mode_pos == NULL) mode = 0; /*default 'sync'*/ else mode_pos++; if (mode_pos) { if (!strncmp(mode_pos, "sync", 4)) { mode = 0; } else if (!strncmp(mode_pos, "async", 5)) { mode = 1; } else { LM_ERR("invalid sync/async mode\n"); return NULL; } name_len = socket.len-(mode/*if async add 1*/+4/*sync len*/+1/*'/'*/); name = pkg_realloc(dummy_buffer, name_len + 1); } else { name_len = 0; name = pkg_realloc(dummy_buffer, socket.len+1); } if (!name) { LM_ERR("no more pkg memory\n"); return NULL; } if (mode_pos) { memcpy(name, socket.s, name_len); name[name_len] = '\0'; } else { memcpy(name, socket.s, socket.len); name[socket.len] = '\0'; } dummy_buffer = name; /* try to "resolve" the name of the route */ idx = get_script_event_route_ID_by_name(name,event_rlist,EVENT_RT_NO); if (idx < 0) { LM_ERR("cannot find route %s\n", name); return NULL; } if (mode_pos) sock = shm_malloc(sizeof(evi_reply_sock) + name_len + 1); else sock = shm_malloc(sizeof(evi_reply_sock) + socket.len + 1); if (!sock) { LM_ERR("no more memory for socket\n"); return NULL; } memset(sock, 0, sizeof(evi_reply_sock)); sock->address.s = (char *)(sock + 1); if (mode_pos) { memcpy(sock->address.s, name, name_len + 1); sock->address.len = name_len; } else { memcpy(sock->address.s, name, socket.len + 1); sock->address.len = socket.len; } sock->params = (void *)(unsigned long)idx; sock->params = (void *)((unsigned long)sock->params | SET_MSB(mode, unsigned long)); sock->flags |= EVI_PARAMS; LM_DBG("route is <%.*s> idx %d mode %s\n", sock->address.len, sock->address.s, idx, mode==0?"snyc":"async"); sock->flags |= EVI_ADDRESS; return sock; #undef SET_MSB } static str scriptroute_print(evi_reply_sock *sock) { /* return only the route's name */ return sock->address; } /* static parameters list retrieved by the fetch_event_params */ evi_params_t *parameters = NULL; str *event_name = NULL; // mostly used for debugging static int scriptroute_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t *params) { #define GET_MSB(value, type) ((type)((type)value & (((type)1 << (sizeof(type) * 8 /*BYTE SIZE*/ - 1))))) #define UNSET_MSB(value, type) ((type)value & (~((type)1 << (sizeof(type) * 8 - 1)))) #define SET_MSB(value, type) ((type)value << (sizeof(type) * 8 /*BYTE SIZE*/ - 1)) evi_params_t * backup_params; str * backup_name; route_send_t *buf = NULL; int sync_mode; if (!sock || !(sock->flags & EVI_PARAMS)) { LM_ERR("no socket found\n"); return -1; } /* check the socket type */ if (!(sock->flags & SCRIPTROUTE_FLAG)) { LM_ERR("invalid socket type\n"); return -1; } sync_mode = GET_MSB(sock->params, unsigned long) ? 0 : 1; sock->params = (void*)UNSET_MSB(sock->params, unsigned long); if (sync_mode) { if (exports.procs) exports.procs = 0; /* save the previous parameters */ backup_params = parameters; backup_name = event_name; parameters = params; event_name = ev_name; run_top_route(event_rlist[SR_SOCK_ROUTE(sock)].a, msg); /* restore previous parameters */ parameters = backup_params; event_name = backup_name; } else { if (route_build_buffer(ev_name, sock, params, &buf) < 0) goto reset_msb; buf->a = event_rlist[SR_SOCK_ROUTE(sock)].a; if (route_send(buf) < 0) goto reset_msb; sock->params = (void *)((unsigned long)sock->params | SET_MSB(1, unsigned long)); } return 0; reset_msb: sock->params = (void *)((unsigned long)sock->params | SET_MSB(1, unsigned long)); return -1; #undef GET_MSB #undef UNSET_MSB #undef SET_MSB } struct scriptroute_params { int index; // index of the param str name; // the name of the param pv_spec_t spec; // pvar spec struct scriptroute_params *next; // next element }; static int scriptroute_add_param(struct sip_msg *msg, struct scriptroute_params *param) { int index; evi_param_t *it = parameters->first; pv_value_t val; if (param->index) { /* search the parameter by it's index */ for (index = 1; it && index != param->index; it = it->next, index++); if (!it) { LM_WARN("Parameter %d not found - max %d\n", param->index, index); return 0; } } else { /* specified by name */ for (; it; it = it->next) { if (it->name.s && it->name.len == param->name.len && memcmp(it->name.s, param->name.s, it->name.len) == 0) break; } if (!it) { LM_WARN("Parameter <%.*s> not found for event <%.*s>\n", param->name.len, param->name.s, event_name->len, event_name->s); return 0; } } /* parameter found - populate it */ if (it->flags & EVI_INT_VAL) { val.ri = it->val.n; val.flags = PV_VAL_INT|PV_TYPE_INT; } else { val.rs.len = it->val.s.len; val.rs.s = it->val.s.s; val.flags = PV_VAL_STR; } if (pv_set_value(msg, ¶m->spec, 0, &val) < 0) { LM_WARN("cannot populate parameter\n"); return 0; } return 1; } /** * Functions used to fetch the event's parameters */ static int scriptroute_fetch(struct sip_msg *msg, char *_list) { int nr = 0; struct scriptroute_params *list = (struct scriptroute_params*)_list; if (!list) { LM_ERR("BUG: no parameters specified\n"); return -1; } if (!event_name) { LM_ERR("No event raised in this scope\n"); return -1; } /* check if no parameters were specified */ if (!parameters) { LM_DBG("This event does not have any parameters\n"); return -2; } LM_DBG("Fetching parameters for event %.*s\n", event_name->len, event_name->s); for (; list; list = list->next) nr += scriptroute_add_param(msg, list); LM_DBG("Successfully fetched %d parameters\n", nr); return nr ? nr : -3; } static int fixup_scriptroute_fetch(void **param, int param_no) { char *end, *p, *e; str s, name; int index = 0; struct scriptroute_params *list = NULL; struct scriptroute_params *elem = NULL; struct scriptroute_params *next = NULL; if (param_no != 1) { LM_ERR("BUG: No such parameters %d\n", param_no); return E_BUG; } p = (char*)(*param); end = p + strlen(p); while (p < end) { name.s = 0; s.s = p; while (p < end && *p != ';') p++; // check if equal is found for (e = s.s; e < p && *e != '='; e++); // avoid old gcc versions warning name.len = 0; if (e == p) { s.len = e - s.s; trim_spaces_lr(s); if (s.len <= 0) { LM_WARN("No pvar specified near <%.*s>\n", (int)(p - s.s), s.s); goto next; } index++; name.s = 0; // the pvar is in s } else { name.s = s.s; name.len = e - s.s; trim_spaces_lr(name); if (name.len <= 0) { LM_WARN("No name specified near <%.*s>\n", (int)(p - s.s), s.s); goto next; } s.s = e + 1; s.len = p - s.s; trim_spaces_lr(s); if (s.len <= 0) { LM_WARN("No pvar specified near %.*s\n", (int)(p - s.s), s.s); goto next; } } elem = shm_malloc(sizeof(struct scriptroute_params)); if (!elem) { LM_ERR("no more shm memory\n"); return E_OUT_OF_MEM; } memset(elem, 0, sizeof(struct scriptroute_params)); if (pv_parse_spec(&s, &elem->spec) == NULL) { LM_ERR("cannot parse spec <%.*s>\n", s.len, s.s); shm_free(elem); goto error; } /* if name specified, use it - otherwise param index */ if (name.s) { elem->name = name; LM_DBG("Parameter %.*s will be set in %.*s\n", name.len, name.s, s.len, s.s); } else { elem->index = index; LM_DBG("Parameter %d will be set in %.*s\n", index, s.len, s.s); } /* link it to parameters list */ elem->next = list; list = elem; next: p++; } *param = (void*)list; return 0; error: for (elem = list; elem; elem = next) { next = elem->next; shm_free(elem); } return E_CFG; } static inline int get_script_event_route_ID_by_name(char* name, struct script_event_route *sr, int size) { unsigned int i; for (i=1;i #include #include #define IS_ERR(_err) (errno == _err) extern evi_params_t *parameters; extern str *event_name; /* used to communicate with the sending process */ static int route_pipe[2]; /* creates communication pipe */ int create_pipe(void) { int rc; route_pipe[0] = route_pipe[1] = -1; /* create pipe */ do { rc = pipe(route_pipe); } while (rc < 0 && IS_ERR(EINTR)); if (rc < 0) { LM_ERR("cannot create status pipe [%d:%s]\n", errno, strerror(errno)); return -1; } return 0; } void destroy_pipe(void) { if (route_pipe[0] != -1) close(route_pipe[0]); if (route_pipe[1] != -1) close(route_pipe[1]); } int route_send(route_send_t *route_s) { int rc, retries = ROUTE_SEND_RETRY; do { rc = write(route_pipe[1], &route_s, sizeof(route_send_t *)); if (rc == sizeof(route_send_t *)) break; } while ((rc < 0 && (IS_ERR(EINTR)||IS_ERR(EAGAIN)||IS_ERR(EWOULDBLOCK))) || retries-- > 0); if (rc < 0) { LM_ERR("unable to send route send struct to worker\n"); return -1; } else if (rc != sizeof(route_send_t *)){ LM_ERR("Incomplete write [%d/%zu]\n", rc, sizeof(route_send_t *)); return -1; } /* give a change to the writer :) */ sched_yield(); return 0; } static union tmp_route_send_t { route_send_t *ptr; char buf[sizeof(route_send_t *)]; } recv_buf; static route_send_t * route_receive(void) { int rc; int retries = ROUTE_SEND_RETRY; int len = sizeof(route_send_t*); int bytes_read = 0; if (route_pipe[0] == -1) return NULL; do { rc = read(route_pipe[0], recv_buf.buf + bytes_read, len); if (rc > 0) { bytes_read += rc; len -= rc; } else if (rc < 0 && IS_ERR(EINTR)) { continue; } else if (retries-- <= 0) { break; } } while (len); if (rc < 0) { LM_ERR("cannot receive send param\n"); return NULL; } return recv_buf.ptr; } int init_writer(void) { int flags; if (route_pipe[0] != -1) { close(route_pipe[0]); route_pipe[0] = -1; } /* Turn non-blocking mode on for sending*/ flags = fcntl(route_pipe[1], F_GETFL); if (flags == -1) { LM_ERR("fcntl failed: %s\n", strerror(errno)); goto error; } if (fcntl(route_pipe[1], F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("fcntl: set non-blocking failed: %s\n", strerror(errno)); goto error; } return 0; error: close(route_pipe[1]); route_pipe[1] = -1; return -1; } static void route_init_reader(void) { if (route_pipe[1] != -1) { close(route_pipe[1]); route_pipe[1] = -1; } } int route_build_buffer(str *event_name, evi_reply_sock *sock, evi_params_t *params, route_send_t **msg) { route_send_t *buf; evi_param_p param, buf_param; int len, params_len=0; unsigned int param_no = 0; char *s; len = sizeof(route_send_t) + event_name->len; if (params) { for (param = params->first; param; param = param->next) { if (param->flags & EVI_INT_VAL) { param_no++; params_len += param->name.len; } else if (param->flags & EVI_STR_VAL) { param_no++; params_len += param->name.len + param->val.s.len; } else { LM_ERR("FIXME: handle param=[%p]\n", param); } } } len += sizeof(evi_params_t) + param_no*sizeof(evi_param_t) + params_len; buf = shm_malloc(len); if (!buf) { LM_ERR("oom\n"); return -1; } memset(buf, 0, len); /* First,is event */ buf->event.s = (char*)(buf + 1); buf->event.len = event_name->len; memcpy(buf->event.s, event_name->s, event_name->len); if (params) { buf_param = (evi_param_p)(buf->event.s + buf->event.len); buf->params.first = buf_param; s = (char*)(buf_param + param_no); for (param = params->first; param; param = param->next) { if (param->flags & EVI_INT_VAL) { buf_param->flags = EVI_INT_VAL; memcpy(s, param->name.s, param->name.len); buf_param->name.s = s; buf_param->name.len = param->name.len; s += param->name.len; buf_param->val.n = param->val.n; buf_param->next = buf_param + 1; buf_param++; } else if (param->flags & EVI_STR_VAL) { buf_param->flags = EVI_STR_VAL; memcpy(s, param->name.s, param->name.len); buf_param->name.s = s; buf_param->name.len = param->name.len; s += param->name.len; memcpy(s, param->val.s.s, param->val.s.len); buf_param->val.s.s = s; buf_param->val.s.len = param->val.s.len; s += param->val.s.len; buf_param->next = buf_param + 1; buf_param++; } else { LM_ERR("FIXME: handle param=[%p]\n", param); } } buf_param--; buf_param->next = NULL; buf->params.last = buf_param; } *msg = buf; return 0; } void event_route_handler(int rank) { /* init blocking reader */ route_init_reader(); route_send_t *route_s; struct sip_msg* dummy_req; dummy_req = (struct sip_msg*)pkg_malloc(sizeof(struct sip_msg)); if (dummy_req == NULL) { LM_ERR("oom\n"); return; } memset(dummy_req, 0, sizeof(struct sip_msg)); dummy_req->first_line.type = SIP_REQUEST; dummy_req->first_line.u.request.method.s= "DUMMY"; dummy_req->first_line.u.request.method.len= 5; dummy_req->first_line.u.request.uri.s= "sip:user@domain.com"; dummy_req->first_line.u.request.uri.len= 19; dummy_req->rcv.src_ip.af = AF_INET; dummy_req->rcv.dst_ip.af = AF_INET; /* waiting for commands */ for (;;) { route_s = route_receive(); if (!route_s) { LM_ERR("invalid receive sock info\n"); goto end; } event_name = &route_s->event; parameters = &route_s->params; run_top_route(route_s->a, dummy_req); end: if (route_s) shm_free(route_s); } } opensips-2.2.2/modules/event_route/route_send.h000066400000000000000000000024031300170765700217110ustar00rootroot00000000000000/* * Copyright (C) 2014 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2014-06-27 created (osas) */ #ifndef _ROUTE_SEND_H_ #define _ROUTE_SEND_H_ #define ROUTE_SEND_RETRY 3 typedef struct _route_send { struct action *a; str event; evi_params_t params; } route_send_t; int create_pipe(void); void destroy_pipe(void); int init_writer(void); int route_build_buffer(str *event_name, evi_reply_sock *sock, evi_params_t *params, route_send_t **msg); int route_send(route_send_t *route_s); #endif opensips-2.2.2/modules/event_virtual/000077500000000000000000000000001300170765700177225ustar00rootroot00000000000000opensips-2.2.2/modules/event_virtual/Makefile000066400000000000000000000002571300170765700213660ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=event_virtual.so LIBS= include ../../Makefile.modulesopensips-2.2.2/modules/event_virtual/README000066400000000000000000000055511300170765700206100ustar00rootroot00000000000000event_virtual Module Robert-Vladut Patrascu OpenSIPS Solutions Copyright © 2015 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Virtual socket syntax 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.4. External Libraries or Applications 1.5. Exported Parameters 1.6. Exported Functions 1.7. Example List of Examples 1.1. Virtual socket Chapter 1. Admin Guide 1.1. Overview The event_virtual module provides the possibility to have multiple external applications, using different transport protocols, subscribed to the OpenSIPS Event Interface as a single virtual subscriber, for a specific event. When an event is triggered, the event_virtual module notifies the specified transport modules using one of the following policies: * PARALLEL - all subscribers (applications) are notified at once * FAILOVER - for every event raised, try to notify the subscribers, in the order in which they are given, until the first successful notification * ROUND-ROBIN - for every event raised, notify the subscribers alternatively, in the order in which they are given (for each raised event notify a different subscriber) Only one expire value can be used (for the whole virtual subscription), and not one for each individual subscriber. 1.2. Virtual socket syntax virtual:policy subscriber_1 [[subscriber_2] ...] Meanings: * virtual: - informs the Event Interface that the events sent to this subscriber should be handled by the event_virtual module * policy - subscriber notification policy, can have one of the following values: 'PARALLEL', 'FAILOVER', 'ROUND-ROBIN' (with the behaviour described above) + !! Important: Policies must always be specified as uppercase strings! * subscriber_1 - use the socket syntax for this specific subscriber (eg. "rabbitmq:guest:guest@127.0.0.1:5672/pike") 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: The OpenSIPS event modules which implement the transport protocols used by the subscribers. 1.4. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.5. Exported Parameters No exported parameters to be used in the configuration file. 1.6. Exported Functions No exported functions to be used in the configuration file. 1.7. Example Example 1.1. Virtual socket The sockets of the subscribers may be separated by any number of spaces or tabs: virtual:PARALLEL rabbitmq:guest:guest@127.0.0.1:5672/pike flatst ore:/var/log/opensips_proxy.log opensips-2.2.2/modules/event_virtual/doc/000077500000000000000000000000001300170765700204675ustar00rootroot00000000000000opensips-2.2.2/modules/event_virtual/doc/event_virtual.xml000066400000000000000000000016411300170765700241020ustar00rootroot00000000000000 %docentities; ]> event_virtual Module &osipsname; Robert-Vladut Patrascu OpenSIPS Solutions
rvlad.patrascu@gmail.com http://www.opensips.org
2015 &osipssol;
&admin;
opensips-2.2.2/modules/event_virtual/doc/event_virtual_admin.xml000066400000000000000000000070441300170765700252550ustar00rootroot00000000000000 &adminguide;
Overview The event_virtual module provides the possibility to have multiple external applications, using different transport protocols, subscribed to the &osips; Event Interface as a single virtual subscriber, for a specific event. When an event is triggered, the event_virtual module notifies the specified transport modules using one of the following policies: PARALLEL - all subscribers (applications) are notified at once FAILOVER - for every event raised, try to notify the subscribers, in the order in which they are given, until the first successful notification ROUND-ROBIN - for every event raised, notify the subscribers alternatively, in the order in which they are given (for each raised event notify a different subscriber) Only one expire value can be used (for the whole virtual subscription), and not one for each individual subscriber.
Virtual socket syntax virtual:policy subscriber_1 [[subscriber_2] ...] Meanings: virtual: - informs the Event Interface that the events sent to this subscriber should be handled by the event_virtual module policy - subscriber notification policy, can have one of the following values: 'PARALLEL', 'FAILOVER', 'ROUND-ROBIN' (with the behaviour described above) !! Important: Policies must always be specified as uppercase strings! subscriber_1 - use the socket syntax for this specific subscriber (eg. "rabbitmq:guest:guest@127.0.0.1:5672/pike")
Dependencies
&osips; Modules The following modules must be loaded before this module: The OpenSIPS event modules which implement the transport protocols used by the subscribers.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters No exported parameters to be used in the configuration file.
Exported Functions No exported functions to be used in the configuration file.
Example Virtual socket The sockets of the subscribers may be separated by any number of spaces or tabs: virtual:PARALLEL rabbitmq:guest:guest@127.0.0.1:5672/pike flatstore:/var/log/opensips_proxy.log
opensips-2.2.2/modules/event_virtual/event_virtual.c000066400000000000000000000335071300170765700227650ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Project * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2015-07-xx created (rvlad-patrascu) */ #include "event_virtual.h" #include "../../mem/mem.h" #include "../../locking.h" #include "../../sr_module.h" #include "../../mem/shm_mem.h" #include "../../ut.h" static int mod_init(void); static void destroy(void); static void virtual_free(evi_reply_sock *sock); static str virtual_print(evi_reply_sock *sock); static int virtual_match(evi_reply_sock *sock1, evi_reply_sock *sock2); static evi_reply_sock* virtual_parse(str socket); static int virtual_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t * params); static struct virtual_socket **list_sockets; static gen_lock_t *global_lock; static gen_lock_t *rrobin_lock; struct module_exports exports = { "event_virtual", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ 0, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ destroy, /* destroy function */ 0 /* per-child init function */ }; static evi_export_t trans_export_virtual = { VIRT_STR, /* transport module name */ virtual_raise, /* raise function */ virtual_parse, /* parse function */ virtual_match, /* sockets match function */ virtual_free, /* free function */ virtual_print, /* print socket */ VIRT_FLAG /* flags */ }; /* initialize function */ static int mod_init(void) { LM_NOTICE("initializing module ...\n"); if (register_event_mod(&trans_export_virtual)) { LM_ERR("cannot register transport functions for SCRIPTROUTE\n"); return -1; } list_sockets = shm_malloc(sizeof(struct virtual_socket*)); *list_sockets = NULL; if (!list_sockets) { LM_ERR("no more memory for list_sockets header\n"); return -1; } global_lock = lock_alloc(); rrobin_lock = lock_alloc(); if (!global_lock || !rrobin_lock) { LM_ERR("Failed to allocate locks\n"); return -1; } if (!lock_init(global_lock) || !lock_init(rrobin_lock)) { LM_ERR("Failed to init locks\n"); return -1; } return 0; } /* free allocated memory */ static void destroy(void) { struct virtual_socket* header = *list_sockets; struct virtual_socket* tmp; struct sub_socket *sub_list, *tmp_s; LM_NOTICE("destroying module ...\n"); lock_destroy(global_lock); lock_destroy(rrobin_lock); lock_dealloc(global_lock); lock_dealloc(rrobin_lock); /* free the list of virtual sockets */ while (header) { /* free the list of sockets for this virtual socket */ sub_list = header->list_sockets; while (sub_list) { tmp_s = sub_list; sub_list = sub_list->next; shm_free(tmp_s); } tmp = header; header = header->next; shm_free(tmp); } shm_free(list_sockets); } /* compare two str values */ inline static int str_cmp(str a , str b) { if (a.len == b.len && strncmp(a.s, b.s, a.len) == 0) return 1; return 0; } static int virtual_match (evi_reply_sock *sock1, evi_reply_sock *sock2) { struct virtual_socket *vsock1, *vsock2; struct sub_socket *h_list1, *h_list2; unsigned int found_sock; if (!sock1 || !sock2 || !sock1->params || !sock2->params) return 0; else { vsock1 = sock1->params; vsock2 = sock2->params; } if ((vsock1->type != vsock2->type) || (vsock1->nr_sockets != vsock2->nr_sockets)) return 0; h_list1 = vsock1->list_sockets; if (vsock1->type == FAILOVER_TYPE || vsock1->type == RROBIN_TYPE) { h_list2 = vsock2->list_sockets; for (; h_list1 != NULL && h_list2 != NULL; h_list1 = h_list1->next, h_list2 = h_list2->next) if (!str_cmp(h_list1->sock_str, h_list2->sock_str)) return 0; return 1; } else { /* if the virtual socket type is parallel, match two virtual sockets that have the same actual sockets in any order*/ for (; h_list1 != NULL; h_list1 = h_list1->next) { h_list2 = vsock2->list_sockets; found_sock = 0; for (; h_list2 != NULL; h_list2 = h_list2->next) { if (str_cmp(h_list1->sock_str, h_list2->sock_str)) { found_sock = 1; break; } } if (!found_sock) { return 0; } } return 1; } } /* insert entry in global list of virtual sockets */ static void insert_in_list_sockets(struct virtual_socket *new) { struct virtual_socket *head = *list_sockets; new->next = NULL; new->prev = NULL; lock_get(global_lock); if (head != NULL) { head->prev = new; new->next = head; } *list_sockets = new; lock_release(global_lock); } /* insert an actual socket in the list of sockets of one virtual socket */ /* returns a newly created sub_socket structure*/ static struct sub_socket *insert_sub_socket(struct virtual_socket *vsock) { struct sub_socket *new_entry, *head; new_entry = shm_malloc(sizeof(struct sub_socket)); if (!new_entry) { return NULL; } new_entry->trans_mod = NULL; new_entry->sock = NULL; new_entry->next = NULL; if (!vsock->list_sockets) { vsock->list_sockets = new_entry; return new_entry; } head = vsock->list_sockets; if (!head->next) { head->next = new_entry; return new_entry; } while (head->next->next) { head = head->next; } head->next->next = new_entry; return new_entry; } static evi_reply_sock* virtual_parse(str socket) { evi_reply_sock *ret_sock; struct virtual_socket *new_vsocket; struct sub_socket *socket_entry; char *p1, *p_token = NULL; unsigned int tmp_len, tmp_len_addr = 0, pos; unsigned int token_is_socket; if (!socket.s || !socket.len) { LM_ERR("no socket specified\n"); return NULL; } new_vsocket = shm_malloc(sizeof(struct virtual_socket) + socket.len + sizeof(evi_reply_sock)); if (!new_vsocket) { LM_ERR("no memory for new list_sockets entry\n"); return NULL; } new_vsocket->list_sockets = NULL; new_vsocket->current_sock = NULL; new_vsocket->nr_sockets = 0; ret_sock = (evi_reply_sock *)((char *)(new_vsocket + 1) + socket.len); memset(ret_sock, 0, sizeof(evi_reply_sock)); ret_sock->address.s = (char *)(new_vsocket + 1); ret_sock->address.len = socket.len; ret_sock->params = new_vsocket; ret_sock->flags |= EVI_ADDRESS; ret_sock->flags |= EVI_EXPIRE; /* jump over initial whitespaces */ for (p1 = socket.s, pos = 0; (*p1 == ' ' || *p1 == '\t') && pos < socket.len; p1++, pos++) {} /* parse the virtual socket type ("PARALLEL" etc.) */ if (!memcmp(p1, PARALLEL_STR, PARALLEL_LEN)) { new_vsocket->type = PARALLEL_TYPE; p1 = p1 + PARALLEL_LEN; pos += PARALLEL_LEN; memcpy(ret_sock->address.s, PARALLEL_STR, PARALLEL_LEN); memcpy(ret_sock->address.s + PARALLEL_LEN, " ", 1); tmp_len_addr = PARALLEL_LEN + 1; } else if (!memcmp(p1, FAILOVER_STR, FAILOVER_LEN)) { new_vsocket->type = FAILOVER_TYPE; p1 = p1 + FAILOVER_LEN; pos += FAILOVER_LEN; memcpy(ret_sock->address.s, FAILOVER_STR, FAILOVER_LEN); memcpy(ret_sock->address.s + FAILOVER_LEN, " ", 1); tmp_len_addr = FAILOVER_LEN + 1; } else if (!memcmp(p1, RROBIN_STR, RROBIN_LEN)) { new_vsocket->type = RROBIN_TYPE; p1 = p1 + RROBIN_LEN; pos += RROBIN_LEN; memcpy(ret_sock->address.s, RROBIN_STR, RROBIN_LEN); memcpy(ret_sock->address.s + RROBIN_LEN, " ", 1); tmp_len_addr = RROBIN_LEN + 1; } else { LM_ERR("invalid virtual socket type\n"); shm_free(new_vsocket); return NULL; } tmp_len = 0; token_is_socket = 0; /* parse the actual sockets of this virtual socket */ for (; pos < socket.len; p1++, pos++) { /* jump over whitespaces and parse the last socket before whitespaces*/ if (*p1 == SEP_SPACE || *p1 == SEP_TAB) { if (token_is_socket) { socket_entry = insert_sub_socket(new_vsocket); if(!socket_entry) { LM_ERR("no memory for sub_socket entry\n"); shm_free(new_vsocket); return NULL; } new_vsocket->nr_sockets++; socket_entry->sock_str.len = tmp_len; socket_entry->sock_str.s = shm_malloc(tmp_len); memcpy(socket_entry->sock_str.s, p_token, tmp_len); memcpy(ret_sock->address.s + tmp_len_addr, p_token, tmp_len); tmp_len_addr += tmp_len; memcpy(ret_sock->address.s + tmp_len_addr, " ", 1); tmp_len_addr++; LM_DBG("parsed socket %.*s\n", tmp_len, socket_entry->sock_str.s); token_is_socket = 0; tmp_len = 0; } } else { if (!token_is_socket) p_token = p1; token_is_socket = 1; tmp_len++; } } /* parse the last socket */ if (token_is_socket) { socket_entry = insert_sub_socket(new_vsocket); if(!socket_entry) { LM_ERR("no memory for sub_socket entry\n"); shm_free(new_vsocket); return NULL; } new_vsocket->nr_sockets++; socket_entry->sock_str.len = tmp_len; socket_entry->sock_str.s = shm_malloc(tmp_len); memcpy(socket_entry->sock_str.s, p_token, tmp_len); memcpy(ret_sock->address.s + tmp_len_addr, p_token, tmp_len); tmp_len_addr += tmp_len; LM_DBG("parsed socket %.*s\n", tmp_len, socket_entry->sock_str.s); } ret_sock->address.len = tmp_len_addr; insert_in_list_sockets(new_vsocket); return ret_sock; } /* get the transport module of an actual socket and call the parse function */ static int parse_socket(struct sub_socket *socket) { socket->trans_mod = get_trans_mod(&(socket->sock_str)); if (!socket->trans_mod) { LM_ERR("couldn't find a protocol to support %.*s\n", socket->sock_str.len, socket->sock_str.s); return 0; } socket->sock_str.s += socket->trans_mod->proto.len + 1; socket->sock_str.len -= (socket->trans_mod->proto.len + 1); /* parse socket and get the evi_reply_sock */ socket->sock = socket->trans_mod->parse(socket->sock_str); if (!socket->sock) { return 0; } socket->sock_str.s -= socket->trans_mod->proto.len + 1; socket->sock_str.len += (socket->trans_mod->proto.len + 1); return 1; } static int virtual_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t *params) { struct virtual_socket *vsock; struct sub_socket *h_list; if (!sock || !(sock->params)) { LM_ERR("invalid socket\n"); return -1; } vsock = (struct virtual_socket *)sock->params; h_list = vsock->list_sockets; switch (vsock->type) { /* raise all the sockets at once*/ case PARALLEL_TYPE : { while (h_list) { if (!h_list->trans_mod && !parse_socket(h_list)) { LM_ERR("unable to parse socket %.*s\n", h_list->sock_str.len, h_list->sock_str.s); return -1; } if (h_list->trans_mod->raise(msg, ev_name, h_list->sock, params)) { LM_ERR("unable to raise socket %.*s\n", h_list->sock_str.len, h_list->sock_str.s); return -1; } h_list = h_list->next; } break; } /* try to raise all sockets until first successful raise*/ case FAILOVER_TYPE : { while (h_list) { if (!h_list->trans_mod && !parse_socket(h_list)) { LM_DBG("unable to parse socket %.*s trying next socket\n", h_list->sock_str.len, h_list->sock_str.s); h_list = h_list->next; continue; } if (h_list->trans_mod->raise(msg, ev_name, h_list->sock, params)) { LM_DBG("unable to raise socket %.*s trying next socket\n", h_list->sock_str.len, h_list->sock_str.s); h_list = h_list->next; continue; } break; } if (!h_list) { LM_ERR("unable to raise any socket\n"); return -1; } break; } /* raise the sockets alternatively (in the order they have been parsed) */ case RROBIN_TYPE : { lock_get(rrobin_lock); if (!vsock->current_sock) vsock->current_sock = h_list; if (!vsock->current_sock->trans_mod && !parse_socket(vsock->current_sock)) { LM_ERR("unable to parse socket %.*s\n", vsock->current_sock->sock_str.len, vsock->current_sock->sock_str.s); return -1; } if (vsock->current_sock->trans_mod->raise(msg, ev_name, vsock->current_sock->sock, params)) { LM_ERR("unable to raise socket %.*s\n", vsock->current_sock->sock_str.len, vsock->current_sock->sock_str.s); return -1; } vsock->current_sock = vsock->current_sock->next; lock_release(rrobin_lock); break; } default : { LM_ERR("invalid virtual socket type\n"); return -1; } } return 0; } static void virtual_free(evi_reply_sock *sock) { struct virtual_socket *vsock; struct sub_socket *sub_list, *tmp_s; LM_DBG("freeing socket %.*s\n", sock->address.len ,sock->address.s); lock_get(global_lock); vsock = (struct virtual_socket *)sock->params; if (!vsock) return; /* free the list of sockets for this virtual socket */ sub_list = vsock->list_sockets; while (sub_list) { /* call the free function of the subscriber */ if (sub_list->trans_mod) { sub_list->trans_mod->free(sub_list->sock); } tmp_s = sub_list; sub_list = sub_list->next; shm_free(tmp_s->sock_str.s); shm_free(tmp_s); } /* free the virtual socket from the global list */ if (vsock->next) vsock->next->prev = vsock->prev; if (vsock == *list_sockets) *list_sockets = vsock->next; else vsock->prev->next = vsock->next; if (!vsock->next && !vsock->prev) *list_sockets = NULL; shm_free(vsock); lock_release(global_lock); } static str virtual_print(evi_reply_sock *sock) { return sock->address; }opensips-2.2.2/modules/event_virtual/event_virtual.h000066400000000000000000000015571300170765700227720ustar00rootroot00000000000000#ifndef _EV_VIRTUAL_H_ #define _EV_VIRTUAL_H_ #include "../../str.h" #include "../../evi/evi_transport.h" #define VIRT_NAME "virtual" #define VIRT_STR { VIRT_NAME, sizeof(VIRT_NAME) - 1} #define VIRT_FLAG (1<<24) #define PARALLEL_TYPE 0 #define PARALLEL_STR "PARALLEL" #define PARALLEL_LEN 8 #define FAILOVER_TYPE 1 #define FAILOVER_STR "FAILOVER" #define FAILOVER_LEN 8 #define RROBIN_TYPE 2 #define RROBIN_STR "ROUND-ROBIN" #define RROBIN_LEN 11 #define SEP_SPACE ' ' #define SEP_TAB '\t' struct virtual_socket { unsigned int type; unsigned int nr_sockets; struct sub_socket *current_sock; /* current socket to raise */ struct sub_socket *list_sockets; /* list of actual sockets */ struct virtual_socket *next; struct virtual_socket *prev; }; struct sub_socket { str sock_str; evi_export_t *trans_mod; evi_reply_sock *sock; struct sub_socket *next; }; #endifopensips-2.2.2/modules/event_xmlrpc/000077500000000000000000000000001300170765700175415ustar00rootroot00000000000000opensips-2.2.2/modules/event_xmlrpc/Makefile000066400000000000000000000004201300170765700211750ustar00rootroot00000000000000# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=event_xmlrpc.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/event_xmlrpc/README000066400000000000000000000076641300170765700204360ustar00rootroot00000000000000event_xmlrpc Module Razvan Crainea OpenSIPS Solutions Edited by Razvan Crainea Copyright © 2012 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. XMLRPC socket syntax 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. use_struct_param (integer) 1.4.2. sync_mode (integer) 1.5. Exported Functions 1.6. Example List of Examples 1.1. Set use_struct_param parameter 1.2. Set sync_mode parameter 1.3. E_PIKE_BLOCKED event 1.4. XMLRPC socket Chapter 1. Admin Guide 1.1. Overview This module is an implementation of an XMLRPC client used to notify XMLRPC servers whenever certain notifications are raised by OpenSIPS. It acts as a transport layer for the Event Notification Interface. Basicly, the module executes a remote procedure call when an event is raised from OpenSIPS's script, core or modules using the Event Interface. In order to be notified, an XMLRPC server has to subscribe for a certain event provided by OpenSIPS. This can be done using the generic MI Interface (event_subscribe function) or from OpenSIPS script (subscribe_event core function). 1.2. XMLRPC socket syntax 'xmlrpc:' host ':' port ':' method Meanings: * 'xmlrpc:' - informs the Event Interface that the events sent to this subscriber should be handled by the event_xmlrpc module. * host - host name of the XMLRPC server. * port - port of the XMLRPC server. * method - method called remotely by the XMLRPC client. NOTE: the client does not wait for a response from the XMLRPC server. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * none. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.4. Exported Parameters 1.4.1. use_struct_param (integer) When raising an event, pack the name and value of the parameters in a XMLRPC structure. This provides an easier way for some XMLRPC server implementations to interpret the parameters. Set it to zero to disable or to non-zero to enable it. Default value is “0 (disabled)â€. Example 1.1. Set use_struct_param parameter ... modparam("event_xmlrpc", "use_struct_param", 1) ... 1.4.2. sync_mode (integer) Specifies whether an event raise operates synchronous or asynchronous relative to the process where the raise is triggered.In synchronous mode the process waits for the status of the raise from the actual worker process.In asynchronous mode the process continues its operation without receiving any confirmation. Default value is “0 (asynchronous)â€. Example 1.2. Set sync_mode parameter ... modparam("event_xmlrpc", "sync_mode", 1) ... 1.5. Exported Functions No function exported to be used from configuration file. 1.6. Example This is an example of an event raised by the pike module when it decides an ip should be blocked: Example 1.3. E_PIKE_BLOCKED event POST /RPC2 HTTP/1.1. Host: 127.0.0.1:8081. Connection: close. User-Agent: OpenSIPS XMLRPC Notifier. Content-type: text/xml. Content-length: 240. . e_dummy_h E_MY_EVENT ip 192.168.2.11 Example 1.4. XMLRPC socket # calls the 'block_ip' function xmlrpc:127.0.0.1:8080:block_ip opensips-2.2.2/modules/event_xmlrpc/doc/000077500000000000000000000000001300170765700203065ustar00rootroot00000000000000opensips-2.2.2/modules/event_xmlrpc/doc/event_xmlrpc.xml000066400000000000000000000020661300170765700235420ustar00rootroot00000000000000 %docentities; ]> event_xmlrpc Module &osipsname; Razvan Crainea OpenSIPS Solutions
razvancrainea@opensips.org &osipssol;
Razvan Crainea
razvancrainea@opensips.org
2012 &osipssol;
&admin; &faq;
opensips-2.2.2/modules/event_xmlrpc/doc/event_xmlrpc_admin.xml000066400000000000000000000112711300170765700247100ustar00rootroot00000000000000 &adminguide;
Overview This module is an implementation of an XMLRPC client used to notify XMLRPC servers whenever certain notifications are raised by OpenSIPS. It acts as a transport layer for the Event Notification Interface. Basicly, the module executes a remote procedure call when an event is raised from OpenSIPS's script, core or modules using the Event Interface. In order to be notified, an XMLRPC server has to subscribe for a certain event provided by OpenSIPS. This can be done using the generic MI Interface (event_subscribe function) or from OpenSIPS script (subscribe_event core function).
XMLRPC socket syntax 'xmlrpc:' host ':' port ':' method Meanings: 'xmlrpc:' - informs the Event Interface that the events sent to this subscriber should be handled by the event_xmlrpc module. host - host name of the XMLRPC server. port - port of the XMLRPC server. method - method called remotely by the XMLRPC client. NOTE: the client does not wait for a response from the XMLRPC server.
Dependencies
&osips; Modules The following modules must be loaded before this module: none.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters
<varname>use_struct_param</varname> (integer) When raising an event, pack the name and value of the parameters in a XMLRPC structure. This provides an easier way for some XMLRPC server implementations to interpret the parameters. Set it to zero to disable or to non-zero to enable it. Default value is 0 (disabled). Set <varname>use_struct_param</varname> parameter ... modparam("event_xmlrpc", "use_struct_param", 1) ...
<varname>sync_mode</varname> (integer) Specifies whether an event raise operates synchronous or asynchronous relative to the process where the raise is triggered.In synchronous mode the process waits for the status of the raise from the actual worker process.In asynchronous mode the process continues its operation without receiving any confirmation. Default value is 0 (asynchronous). Set <varname>sync_mode</varname> parameter ... modparam("event_xmlrpc", "sync_mode", 1) ...
Exported Functions No function exported to be used from configuration file.
Example This is an example of an event raised by the pike module when it decides an ip should be blocked: E_PIKE_BLOCKED event e_dummy_h E_MY_EVENT ip 192.168.2.11 ]]> XMLRPC socket # calls the 'block_ip' function xmlrpc:127.0.0.1:8080:block_ip
opensips-2.2.2/modules/event_xmlrpc/event_xmlrpc.c000066400000000000000000000246771300170765700224330ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2012-05-xx created (razvancrainea) */ #include "../../sr_module.h" #include "../../resolve.h" #include "../../evi/evi_transport.h" #include "../../ut.h" #include "event_xmlrpc.h" #include "xmlrpc_send.h" #include #include #include extern unsigned xmlrpc_struct_on; extern unsigned xmlrpc_sync_mode; /** * module functions */ static int mod_init(void); static void destroy(void); static int child_init(int); /** * exported functions */ static evi_reply_sock* xmlrpc_parse(str socket); static int xmlrpc_raise(struct sip_msg *msg, str* ev_name, evi_reply_sock *sock, evi_params_t * params); static int xmlrpc_match(evi_reply_sock *sock1, evi_reply_sock *sock2); static void xmlrpc_free(evi_reply_sock *sock); static str xmlrpc_print(evi_reply_sock *sock); /** * module process */ static proc_export_t procs[] = { {"XML-RPC sender", 0, 0, xmlrpc_process, 1, 0}, {0,0,0,0,0,0} }; /* module parameters */ static param_export_t mod_params[] = { {"use_struct_param", INT_PARAM, &xmlrpc_struct_on}, {"sync_mode", INT_PARAM, &xmlrpc_sync_mode}, {0,0,0} }; /** * module exports */ struct module_exports exports = { "event_xmlrpc", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ mod_params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ procs, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /** * exported functions for core event interface */ static evi_export_t trans_export_xmlrpc = { XMLRPC_STR, /* transport module name */ xmlrpc_raise, /* raise function */ xmlrpc_parse, /* parse function */ xmlrpc_match, /* sockets match function */ xmlrpc_free, /* free function */ xmlrpc_print, /* print function */ XMLRPC_FLAG /* flags */ }; static int child_init(int rank) { if (xmlrpc_init_writer() < 0) { LM_ERR("cannot init writing pipe\n"); return -1; } return 0; } /** * init module function */ static int mod_init(void) { LM_NOTICE("initializing module ...\n"); if (register_event_mod(&trans_export_xmlrpc)) { LM_ERR("cannot register transport functions for XMLRPC\n"); return -1; } if (xmlrpc_create_pipe() < 0) { LM_ERR("cannot create communication pipe\n"); return -1; } if (xmlrpc_init_buffers() < 0) { LM_ERR("cannot initiate buffer\n"); return -1; } return 0; } /* returns 0 if sockets match */ static int xmlrpc_match(evi_reply_sock *sock1, evi_reply_sock *sock2) { str *m1, *m2; unsigned needed_flags = XMLRPC_FLAG|EVI_PORT|EVI_PARAMS|EVI_ADDRESS; if (!sock1 || !sock2) return 0; /* check for similar flags */ if ((sock1->flags & needed_flags) != needed_flags || (sock2->flags & needed_flags) != needed_flags) { return 0; } m1 = (str *)&sock1->params; m2 = (str *)&sock2->params; if (sock1->port != sock2->port || m1->len != m2->len || sock1->address.len != sock2->address.len || memcmp(m1->s, m2->s, m1->len) || memcmp(sock1->address.s, sock2->address.s, sock1->address.len)) { return 0; } return 1; } /* * This is the parsing function * The socket grammar should be: * ip ':' port '/optional/path' ':' method */ static evi_reply_sock* xmlrpc_parse(str socket) { evi_reply_sock *sock = NULL; unsigned short port = 0; char *p = NULL; str host, path=str_init(NULL); struct hostent *hentity; int len; struct xmlrpc_sock_param *params; int http_buf_len=0; if (!socket.len || !socket.s) { LM_ERR("no socket specified\n"); return NULL; } if (!(params=shm_malloc(sizeof(struct xmlrpc_sock_param)))) { LM_ERR("no more pkg mem!\n"); return NULL; } memset(params, 0, sizeof(struct xmlrpc_sock_param)); /* extract host */ host.s = socket.s; p = memchr(socket.s, COLON_C, socket.len); if (!p || p == socket.s) { LM_ERR("port not specified <%.*s>\n", socket.len, socket.s); return NULL; } host.len = p - socket.s; /* used to resolve host */ *p = '\0'; /* skip colon */ socket.s += host.len + 1; socket.len -= host.len + 1; LM_DBG("host is %.*s - remains <%.*s>[%d]\n", host.len, host.s, socket.len, socket.s, socket.len); if (!socket.len || *socket.s == '\0') { LM_ERR("invalid port number\n"); return NULL; } p = memchr(socket.s, COLON_C, socket.len); if (!p || p == socket.s) { LM_ERR("method not specified <%.*s>\n", socket.len, socket.s); return NULL; } port = str2s(socket.s, p - socket.s, 0); if (port == 0) { /* most probably we've got path */ if ((path.s=(q_memchr(socket.s, SLASH_C, p-socket.s)))==NULL) { LM_ERR("malformed port: %.*s\n",(int)(p - socket.s), socket.s); return NULL; } else { port=str2s(socket.s, path.s-socket.s, 0); if (port == 0) { LM_ERR("malformed port: %.*s\n",(int)(p - socket.s), socket.s); return NULL; } path.len = p - path.s; socket.len -= ((path.s+path.len)-socket.s); socket.s = path.s+path.len; } /* will use this later for allocation */ http_buf_len=LENOF(XMLRPC_HTTP_METHOD) + path.len + LENOF(XMLRPC_HTTP_PROTO_HOST); } /* jump over ':' */ socket.len = socket.len - (p - socket.s + 1); socket.s = p + 1; LM_DBG("port is %hu - remains <%.*s>[%d]\n", port, socket.len, socket.s, socket.len); if (socket.len <= 0 || *socket.s == '\0') { LM_ERR("invalid method name\n"); return NULL; } len = sizeof(evi_reply_sock) + host.len + sizeof(struct xmlrpc_sock_param) + socket.len /* this is method */+ http_buf_len; sock = shm_malloc(len); if (!sock) { LM_ERR("no more memory for socket\n"); return NULL; } memset(sock, 0, len); /* only UDP has port */ sock->flags = EVI_PORT; sock->port = port; /* also build sockaddr */ hentity = resolvehost(host.s, 0); if (!hentity) { LM_ERR("cannot resolve host %s\n", host.s); goto error; } if(hostent2su(&sock->src_addr.udp_addr, hentity, 0, port)){ LM_ERR("failed to resolve %s\n", host.s); goto error; } sock->flags |= EVI_SOCKET; /* address */ sock->address.s = (char*)(sock+1); sock->address.len = host.len; memcpy(sock->address.s, host.s, host.len); sock->flags |= EVI_ADDRESS; /* copy parameters: path and method */ params = (struct xmlrpc_sock_param*)(sock->address.s + host.len); params->method.s = (char*)(params+1); memcpy(params->method.s, socket.s, socket.len); params->method.len = socket.len; if (http_buf_len) { /* build only once; not for every message */ params->first_line.s = (char*)(params->method.s+socket.len); memcpy(params->method.s, socket.s, socket.len); params->first_line.len = 0; memcpy(params->first_line.s, XMLRPC_HTTP_METHOD, LENOF(XMLRPC_HTTP_METHOD)); params->first_line.len = LENOF(XMLRPC_HTTP_METHOD); memcpy(params->first_line.s+params->first_line.len, path.s, path.len); params->first_line.len += path.len; memcpy(params->first_line.s+params->first_line.len, XMLRPC_HTTP_PROTO_HOST, LENOF(XMLRPC_HTTP_PROTO_HOST)); params->first_line.len += LENOF(XMLRPC_HTTP_PROTO_HOST); } else { params->first_line.s = XMLRPC_HTTP_CONST; params->first_line.len = LENOF(XMLRPC_HTTP_CONST); } sock->flags |= EVI_PARAMS; /* needs expire */ sock->flags |= EVI_EXPIRE|XMLRPC_FLAG; sock->params= params; return sock; error: if (sock) shm_free(sock); return NULL; } #define DO_PRINT(_s, _l) \ do { \ if (xmlrpc_print_s.len + (_l) > xmlrpc_print_len) { \ int new_len = (xmlrpc_print_s.len + (_l)) * 2; \ char *new_s = pkg_realloc(xmlrpc_print_s.s, new_len); \ if (!new_s) { \ LM_ERR("no more pkg mem to realloc\n"); \ goto end; \ } \ xmlrpc_print_s.s = new_s; \ xmlrpc_print_len = new_len; \ } \ memcpy(xmlrpc_print_s.s + xmlrpc_print_s.len, (_s), (_l)); \ xmlrpc_print_s.len += (_l); \ } while (0) static int xmlrpc_print_len = 0; static str xmlrpc_print_s = { 0, 0 }; static str xmlrpc_print(evi_reply_sock *sock) { str aux; struct xmlrpc_sock_param *params = sock->params; xmlrpc_print_s.len = 0; if (!sock) { LM_DBG("Nothing to print"); goto end; } if (sock->flags & EVI_ADDRESS) DO_PRINT(sock->address.s, sock->address.len); if (sock->flags & EVI_PORT) { DO_PRINT(":", 1); aux.s = int2str(sock->port, &aux.len); DO_PRINT(aux.s, aux.len); } if (sock->flags & EVI_PARAMS) { DO_PRINT(":", 1); DO_PRINT(params->method.s, params->method.len); } end: return xmlrpc_print_s; } #undef DO_PRINT static int xmlrpc_raise(struct sip_msg *dummy_msg, str* ev_name, evi_reply_sock *sock, evi_params_t * params) { xmlrpc_send_t * msg = NULL; if (!sock) { LM_ERR("no socket found\n"); return -1; } /* check the socket type */ if (!(sock->flags & XMLRPC_FLAG)) { LM_ERR("invalid socket type %x\n", sock->flags); return -1; } /* check to see if a socket was specified */ if (!(sock->flags & EVI_SOCKET)) { LM_ERR("not a valid socket\n"); return -1; } if (!(sock->flags & EVI_ADDRESS)) { LM_ERR("cannot find destination address\n"); return -1; } if (!(sock->flags & EVI_PARAMS)) { LM_ERR("no method found\n"); return -1; } if (xmlrpc_build_buffer(ev_name, sock, params, &msg) ) { LM_ERR("cannot create send buffer\n"); return -1; } if (xmlrpc_send(msg) < 0) { LM_ERR("cannot send message\n"); return -1; } return 0; } static void xmlrpc_free(evi_reply_sock *sock) { /* nothing special here */ shm_free(sock); } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module ...\n"); /* closing sockets */ xmlrpc_destroy_pipe(); } opensips-2.2.2/modules/event_xmlrpc/event_xmlrpc.h000066400000000000000000000025651300170765700224300ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2012-05-xx created (razvancrainea) */ #ifndef _EV_XMLRPC_H_ #define _EV_XMLRPC_H_ /* transport protocols name */ #define XMLRPC_NAME "xmlrpc" #define XMLRPC_STR { XMLRPC_NAME, sizeof(XMLRPC_NAME) - 1} /* module flag */ #define XMLRPC_FLAG (1 << 27) #define COLON_C ':' #define SLASH_C '/' struct xmlrpc_sock_param { str method; str first_line; }; #endif #ifdef HAVE_SCHED_YIELD #include #else #include /** Fake sched_yield if no unistd.h include is available */ #define sched_yield() sleep(0) #endif opensips-2.2.2/modules/event_xmlrpc/xmlrpc_send.c000066400000000000000000000373511300170765700222340ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2012-05-xx created (razvancrainea) */ #include "../../evi/evi_transport.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../ut.h" #include "../../pt.h" #include "xmlrpc_send.h" #include "event_xmlrpc.h" #include #include #define IS_ERR(_err) (errno == _err) unsigned xmlrpc_struct_on = 0; unsigned xmlrpc_sync_mode = 0; static char * xmlrpc_body_buf = 0; static struct iovec xmlrpc_iov[XMLRPC_IOVEC_MAX_SIZE]; static unsigned xmlrpc_iov_len = 0; static unsigned xmlrpc_first_line_index = 0; static unsigned xmlrpc_host_index = 0; static unsigned xmlrpc_ct_len_index = 0; static unsigned xmlrpc_met_name_index = 0; static unsigned xmlrpc_ev_name_index = 0; static unsigned xmlrpc_params_index = 0; static unsigned xmlrpc_xmlbody_index = 0; static unsigned nr_procs = 0; int xmlrpc_init_buffers(void) { xmlrpc_body_buf = pkg_malloc(XMLRPC_DEFAULT_BUFFER_SIZE); if (!xmlrpc_body_buf) { LM_ERR("cannot allocate header buffer\n"); return -1; } return 0; } /* used to communicate with the sending process */ static int xmlrpc_pipe[2]; /* used to communicate the status of the send (success or fail) from the sending process back to the requesting ones */ static int (*xmlrpc_status_pipes)[2]; /* more than enought for http first line */ /* creates communication pipe */ int xmlrpc_create_pipe(void) { int rc; xmlrpc_pipe[0] = xmlrpc_pipe[1] = -1; /* create pipe */ do { rc = pipe(xmlrpc_pipe); } while (rc < 0 && IS_ERR(EINTR)); if (rc < 0) { LM_ERR("cannot create status pipe [%d:%s]\n", errno, strerror(errno)); return -1; } if (xmlrpc_sync_mode && xmlrpc_create_status_pipes() < 0) { LM_ERR("cannot create communication status pipes\n"); return -1; } return 0; } /* creates status pipes */ int xmlrpc_create_status_pipes(void) { int rc, i; nr_procs = count_init_children(0) + 2; /* + 2 timer processes */ xmlrpc_status_pipes = shm_malloc(nr_procs * sizeof(xmlrpc_pipe)); if (!xmlrpc_status_pipes) { LM_ERR("cannot allocate xmlrpc_status_pipes\n"); return -1; } /* create pipes */ for (i = 0; i < nr_procs; i++) { do { rc = pipe(xmlrpc_status_pipes[i]); } while (rc < 0 && IS_ERR(EINTR)); if (rc < 0) { LM_ERR("cannot create status pipe [%d:%s]\n", errno, strerror(errno)); return -1; } } return 0; } void xmlrpc_destroy_pipe(void) { if (xmlrpc_pipe[0] != -1) close(xmlrpc_pipe[0]); if (xmlrpc_pipe[1] != -1) close(xmlrpc_pipe[1]); if (xmlrpc_sync_mode) xmlrpc_destroy_status_pipes(); } void xmlrpc_destroy_status_pipes(void) { int i; for(i = 0; i < nr_procs; i++) { close(xmlrpc_status_pipes[i][0]); close(xmlrpc_status_pipes[i][1]); } shm_free(xmlrpc_status_pipes); } int xmlrpc_send(xmlrpc_send_t* xmlrpcs) { int rc, retries = XMLRPC_SEND_RETRY; int send_status; xmlrpcs->process_idx = process_no; do { rc = write(xmlrpc_pipe[1], &xmlrpcs, sizeof(xmlrpc_send_t *)); } while (rc < 0 && (IS_ERR(EINTR) || retries-- > 0)); if (rc < 0) { LM_ERR("unable to send xmlrpc send struct to worker\n"); shm_free(xmlrpcs); return XMLRPC_SEND_FAIL; } /* give a change to the writer :) */ sched_yield(); if (xmlrpc_sync_mode) { retries = XMLRPC_SEND_RETRY; do { rc = read(xmlrpc_status_pipes[process_no][0], &send_status, sizeof(int)); } while (rc < 0 && (IS_ERR(EINTR) || retries-- > 0)); if (rc < 0) { LM_ERR("cannot receive send status\n"); send_status = XMLRPC_SEND_FAIL; } return send_status; } else return XMLRPC_SEND_SUCCESS; } static xmlrpc_send_t * xmlrpc_receive(void) { static xmlrpc_send_t * recv; int rc; int retries = XMLRPC_SEND_RETRY; if (xmlrpc_pipe[0] == -1) return NULL; do { rc = read(xmlrpc_pipe[0], &recv, sizeof(xmlrpc_send_t*)); } while (rc < 0 && (IS_ERR(EINTR) || retries-- > 0)); if (rc < 0) { LM_ERR("cannot receive send param\n"); return NULL; } return recv; } int xmlrpc_init_writer(void) { int flags; if (xmlrpc_pipe[0] != -1) { close(xmlrpc_pipe[0]); xmlrpc_pipe[0] = -1; } if (xmlrpc_sync_mode) close(xmlrpc_status_pipes[process_no][1]); /* Turn non-blocking mode on for sending*/ flags = fcntl(xmlrpc_pipe[1], F_GETFL); if (flags == -1) { LM_ERR("fcntl failed: %s\n", strerror(errno)); goto error; } if (fcntl(xmlrpc_pipe[1], F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("fcntl: set non-blocking failed: %s\n", strerror(errno)); goto error; } return 0; error: close(xmlrpc_pipe[1]); xmlrpc_pipe[1] = -1; return -1; } static void xmlrpc_init_reader(void) { int i, flags; if (xmlrpc_pipe[1] != -1) { close(xmlrpc_pipe[1]); xmlrpc_pipe[1] = -1; } if (xmlrpc_sync_mode) for(i = 0; i < nr_procs; i++) { close(xmlrpc_status_pipes[i][0]); /* Turn non-blocking mode on for sending*/ flags = fcntl(xmlrpc_status_pipes[i][1], F_GETFL); if (flags == -1) { LM_ERR("fcntl failed: %s\n", strerror(errno)); close(xmlrpc_status_pipes[i][1]); return; } if (fcntl(xmlrpc_status_pipes[i][1], F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("fcntl: set non-blocking failed: %s\n", strerror(errno)); close(xmlrpc_status_pipes[i][1]); return; } } } /* sends the buffer */ static int xmlrpc_sendmsg(xmlrpc_send_t *sock) { unsigned long i; int len = 0; int fd, ret = -1; int aux; xmlrpc_iov[xmlrpc_first_line_index].iov_base = sock->first_line.s; xmlrpc_iov[xmlrpc_first_line_index].iov_len = sock->first_line.len; xmlrpc_iov[xmlrpc_host_index].iov_base = sock->host.s; xmlrpc_iov[xmlrpc_host_index].iov_len = sock->host.len; /* event name */ xmlrpc_iov[xmlrpc_ev_name_index].iov_base = sock->event.s; xmlrpc_iov[xmlrpc_ev_name_index].iov_len = sock->event.len; /* method name */ xmlrpc_iov[xmlrpc_met_name_index].iov_base = sock->method.s; xmlrpc_iov[xmlrpc_met_name_index].iov_len = sock->method.len; /* msg body */ xmlrpc_iov[xmlrpc_params_index].iov_base = sock->body.s; xmlrpc_iov[xmlrpc_params_index].iov_len = sock->body.len; /* now compute content length */ for (i = xmlrpc_xmlbody_index; i < xmlrpc_iov_len; i++) len += xmlrpc_iov[i].iov_len; xmlrpc_iov[xmlrpc_ct_len_index].iov_base = int2str(len, &aux); xmlrpc_iov[xmlrpc_ct_len_index].iov_len = aux; /* writing the iov on the network */ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { LM_ERR("cannot create socket\n"); return -1; } if (connect(fd, &sock->addr.s, sizeof(struct sockaddr_in)) < 0) { LM_ERR("cannot connect to %s[%d:%s]\n", inet_ntoa(sock->addr.sin.sin_addr), errno, strerror(errno)); goto close; } do { len = writev(fd, xmlrpc_iov, xmlrpc_iov_len); } while ((len < 0 && (IS_ERR(EINTR)||IS_ERR(EAGAIN)||IS_ERR(EWOULDBLOCK)))); if (len <= 0) { LM_ERR("cannot send xmlrpc command %s[%d]\n", strerror(errno), errno); goto close; } ret = 0; close: shutdown(fd, SHUT_RDWR); close(fd); return ret; } static xmlrpc_send_t * xmlrpc_build_send_t(evi_reply_sock *sock, char *params, int params_len, str *ev_name) { char * p, *aux; struct xmlrpc_sock_param *sock_params=sock->params; int len = sizeof(xmlrpc_send_t) + params_len + sock_params->method.len + ev_name->len + sock->address.len + 6 /* : port */; xmlrpc_send_t *msg = shm_malloc(len); if (!msg) { LM_ERR("no more shm mem\n"); return NULL; } memset(msg, 0, len); /* first is body */ msg->body.s = (char*)(msg + 1); memcpy(msg->body.s, params, params_len); msg->body.len = params_len; /* next is method */ msg->method.s = msg->body.s + params_len; memcpy(msg->method.s, sock_params->method.s, sock_params->method.len); msg->method.len = sock_params->method.len; /* first line */ msg->first_line = sock_params->first_line; /* event */ msg->event.s = msg->method.s + msg->method.len; memcpy(msg->event.s, ev_name->s, ev_name->len); msg->event.len = ev_name->len; /* last is host */ msg->host.s = msg->event.s + msg->event.len; memcpy(msg->host.s, sock->address.s, sock->address.len); msg->host.len = sock->address.len; if (sock->flags & EVI_PARAMS) { p = msg->host.s + sock->address.len; /* if it has port, add it */ *p++ = ':'; aux = int2str(sock->port , &len); memcpy(p, aux, len); msg->host.len += len + 1; } /* finally add the socket info */ memcpy(&msg->addr, &sock->src_addr.udp_addr, sizeof(union sockaddr_union)); return msg; } /* function to build XMLRPC buffer */ int xmlrpc_build_buffer(str *event_name, evi_reply_sock *sock, evi_params_t *params, xmlrpc_send_t ** msg) { int len, b_len; char *b, *p; evi_param_p param; if (params && (params->flags & XMLRPC_FLAG)) { LM_DBG("buffer already built\n"); return 0; } b_len = XMLRPC_DEFAULT_BUFFER_SIZE; b = xmlrpc_body_buf; #define COPY_STR(_s, _l) \ do { \ if ( (_l) > b_len ) { \ LM_ERR("buffer too small...\n");\ return -1; \ } \ memcpy( b, (_s), (_l) ); \ b_len -= (_l); \ b += (_l); \ } while (0) if (params) { for (param = params->first; param; param = param->next) { /* '' */ COPY_STR(START_TAG(XMLRPC_PARAM), LENOF(START_TAG(XMLRPC_PARAM))); if (param->name.len && param->name.s) { if (xmlrpc_struct_on) { COPY_STR(START_TAG(XMLRPC_VALUE), LENOF(START_TAG(XMLRPC_VALUE)) - 1); COPY_STR(START_TAG(XMLRPC_STRUCT), LENOF(START_TAG(XMLRPC_STRUCT)) - 1); COPY_STR(START_TAG(XMLRPC_MEMBER), LENOF(START_TAG(XMLRPC_MEMBER))); } LM_DBG("adding parameter %.*s\n", param->name.len, param->name.s); /* */ COPY_STR(START_TAG(XMLRPC_ATTR), LENOF(START_TAG(XMLRPC_ATTR)) - 1); /* parameter name */ COPY_STR(param->name.s, param->name.len); /* */ COPY_STR(END_TAG(XMLRPC_ATTR), LENOF(END_TAG(XMLRPC_ATTR))); } /* */ COPY_STR(START_TAG(XMLRPC_VALUE), LENOF(START_TAG(XMLRPC_VALUE)) - 1); if (param->flags & EVI_INT_VAL) { /* */ COPY_STR(START_TAG(XMLRPC_INT), LENOF(START_TAG(XMLRPC_INT)) - 1); /* convert int */ p = int2str(param->val.n, &len); if (!p) { LM_ERR("cannot convert int parameter\n"); return -1; } /* integer parameter */ COPY_STR(p, len); /* */ COPY_STR(END_TAG(XMLRPC_INT), LENOF(END_TAG(XMLRPC_INT)) - 1); } else { /* */ COPY_STR(START_TAG(XMLRPC_STRING), LENOF(START_TAG(XMLRPC_STRING)) - 1); /* string parameter */ COPY_STR(param->val.s.s, param->val.s.len); /* */ COPY_STR(END_TAG(XMLRPC_STRING), LENOF(END_TAG(XMLRPC_STRING)) - 1); } COPY_STR(END_TAG(XMLRPC_VALUE), LENOF(END_TAG(XMLRPC_VALUE))); if (param->name.len && param->name.s && xmlrpc_struct_on) { COPY_STR(END_TAG(XMLRPC_MEMBER), LENOF(END_TAG(XMLRPC_MEMBER)) - 1); COPY_STR(END_TAG(XMLRPC_STRUCT), LENOF(END_TAG(XMLRPC_STRUCT)) - 1); COPY_STR(END_TAG(XMLRPC_VALUE), LENOF(END_TAG(XMLRPC_VALUE))); } COPY_STR(END_TAG(XMLRPC_PARAM), LENOF(END_TAG(XMLRPC_PARAM))); } } #undef COPY_STR len = XMLRPC_DEFAULT_BUFFER_SIZE - b_len; *msg = xmlrpc_build_send_t(sock, xmlrpc_body_buf, len, event_name); if (!*msg) { LM_ERR("cannot build send msg\n"); return -1; } if (params) params->flags |= XMLRPC_FLAG; return 0; } /** * Node: if you add any extra headers here, make sure they don't overflow * XMLRPC_IOVEC_MAX_SIZE */ static void xmlrpc_init_send_buf(void) { /*First Line: POST /... HTTP... */ xmlrpc_first_line_index = xmlrpc_iov_len++; /* host */ xmlrpc_host_index = xmlrpc_iov_len++; xmlrpc_iov[xmlrpc_iov_len].iov_base = XMLRPC_HTTP_HEADER; xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(XMLRPC_HTTP_HEADER); xmlrpc_iov_len++; /* content length */ xmlrpc_ct_len_index = xmlrpc_iov_len++; /* delimiter */ xmlrpc_iov[xmlrpc_iov_len].iov_base = "\r\n\r\n"; xmlrpc_iov[xmlrpc_iov_len].iov_len = 4; xmlrpc_iov_len++; /* here goes xml payload */ /* XML version */ xmlrpc_iov[xmlrpc_iov_len].iov_base = XMLRPC_BODY_CONST; xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(XMLRPC_BODY_CONST); xmlrpc_xmlbody_index = xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = START_TAG(XMLRPC_METHOD_CALL); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(START_TAG(XMLRPC_METHOD_CALL)); xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = START_TAG(XMLRPC_METHOD_NAME); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(START_TAG(XMLRPC_METHOD_NAME))-1; xmlrpc_iov_len++; /* method name */ xmlrpc_met_name_index = xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = END_TAG(XMLRPC_METHOD_NAME); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(END_TAG(XMLRPC_METHOD_NAME)); xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = START_TAG(XMLRPC_PARAMS); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(START_TAG(XMLRPC_PARAMS)); xmlrpc_iov_len++; /* event name parameter */ /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = START_TAG(XMLRPC_PARAM); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(START_TAG(XMLRPC_PARAM)); xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = START_TAG(XMLRPC_VALUE); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(START_TAG(XMLRPC_VALUE))-1; xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = START_TAG(XMLRPC_STRING); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(START_TAG(XMLRPC_STRING))-1; xmlrpc_iov_len++; /* events name */ xmlrpc_ev_name_index = xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = END_TAG(XMLRPC_STRING); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(END_TAG(XMLRPC_STRING))-1; xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = END_TAG(XMLRPC_VALUE); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(END_TAG(XMLRPC_VALUE)); xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = END_TAG(XMLRPC_PARAM); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(END_TAG(XMLRPC_PARAM)); xmlrpc_iov_len++; /* parameters */ xmlrpc_params_index = xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = END_TAG(XMLRPC_PARAMS); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(END_TAG(XMLRPC_PARAMS)); xmlrpc_iov_len++; /* */ xmlrpc_iov[xmlrpc_iov_len].iov_base = END_TAG(XMLRPC_METHOD_CALL); xmlrpc_iov[xmlrpc_iov_len].iov_len = LENOF(END_TAG(XMLRPC_METHOD_CALL))-1; xmlrpc_iov_len++; } void xmlrpc_process(int rank) { int retries, rc; int send_status; /* init blocking reader */ xmlrpc_init_reader(); xmlrpc_init_send_buf(); xmlrpc_send_t * xmlrpcs; /* waiting for commands */ for (;;) { xmlrpcs = xmlrpc_receive(); if (!xmlrpcs) { LM_ERR("invalid receive sock info\n"); goto end; } /* send msg */ if (xmlrpc_sendmsg(xmlrpcs)) { LM_ERR("cannot send message\n"); send_status = XMLRPC_SEND_FAIL; } else send_status = XMLRPC_SEND_SUCCESS; if (xmlrpc_sync_mode) { retries = XMLRPC_SEND_RETRY; if (xmlrpcs->process_idx >= 0 && xmlrpcs->process_idx < nr_procs) { do { rc = write(xmlrpc_status_pipes[xmlrpcs->process_idx][1], &send_status, sizeof(int)); } while (rc < 0 && (IS_ERR(EINTR) || retries-- > 0)); if (rc < 0) LM_ERR("cannot send status back to requesting process\n"); } } end: if (xmlrpcs) shm_free(xmlrpcs); } } opensips-2.2.2/modules/event_xmlrpc/xmlrpc_send.h000066400000000000000000000052211300170765700222300ustar00rootroot00000000000000/* * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-05-xx created (razvancrainea) */ #ifndef _XMLRPC_SEND_H_ #define _XMLRPC_SEND_H_ #define XMLRPC_SEND_RETRY 3 #define XMLRPC_USER_AGENT "OpenSIPS XMLRPC Notifier" typedef struct _xmlrpc_send { union sockaddr_union addr; str body; str method; str host; str first_line; str event; int process_idx; } xmlrpc_send_t; void xmlrpc_process(int rank); int xmlrpc_create_pipe(void); int xmlrpc_create_status_pipes(void); void xmlrpc_destroy_pipe(void); void xmlrpc_destroy_status_pipes(void); int xmlrpc_init_writer(void); int xmlrpc_init_buffers(void); int xmlrpc_send(xmlrpc_send_t * xmlrpcs); void xmlrpc_destroy(evi_reply_sock *sock); int xmlrpc_build_buffer(str *, evi_reply_sock*, evi_params_t *, xmlrpc_send_t **); #define XMLRPC_HTTP_MAX_HEADER 512 #define XMLRPC_DEFAULT_BUFFER_SIZE 8192 #define XMLRPC_IOVEC_MAX_SIZE 32 #define XMLRPC_DEFAULT_PORT 8080 #define XMLRPC_SEND_SUCCESS 0 #define XMLRPC_SEND_FAIL -1 /* string macros */ /* computes a macro len */ #define LENOF(m) (sizeof(m) - 1) /* xmlrpc http header */ #define XMLRPC_HTTP_CONST "POST /RPC2 HTTP/1.1\r\nHost: " #define XMLRPC_HTTP_METHOD "POST " #define XMLRPC_HTTP_PROTO_HOST " HTTP/1.1\r\nHost:" #define XMLRPC_HTTP_HEADER \ "\r\nConnection: close\r\n" \ "User-Agent: " XMLRPC_USER_AGENT "\r\n" \ "Content-type: text/xml\r\n" \ "Content-length: " #define XMLRPC_BODY_CONST "\n" #define XMLRPC_METHOD_CALL "methodCall" #define XMLRPC_METHOD_NAME "methodName" #define XMLRPC_STRUCT "struct" #define XMLRPC_MEMBER "member" #define XMLRPC_PARAMS "params" #define XMLRPC_PARAM "param" #define XMLRPC_ATTR "name" #define XMLRPC_STRING "string" #define XMLRPC_VALUE "value" #define XMLRPC_INT "int" #define TAG_O '<' #define TAG_C '>' #define TAG_S '/' #define START_TAG(_s) "<" _s ">\n" #define END_TAG(_s) "\n" #endif opensips-2.2.2/modules/exec/000077500000000000000000000000001300170765700157575ustar00rootroot00000000000000opensips-2.2.2/modules/exec/Makefile000066400000000000000000000003141300170765700174150ustar00rootroot00000000000000# $Id$ # # exec module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=exec.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/exec/README000066400000000000000000000276401300170765700166500ustar00rootroot00000000000000exec Module Jiri Kuthan FhG FOKUS Edited by Jan Janak Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. setvars (integer) 1.3.2. time_to_kill (integer) 1.4. Exported Functions 1.4.1. exec(command, [stdin], [stdout], [stderr], [envavp]) 1.4.2. exec_dset(command) (DEPRECATED) 1.4.3. exec_msg(command) (DEPRECATED) 1.4.4. exec_avp(command[, avplist]) (DEPRECATED) 1.4.5. exec_getenv(environment_variable[, avp]) (DEPRECATED) 1.5. Exported Asyncronous Functions 1.5.1. exec(command[,input[,output[,error[,env]]]]) 1.6. Known Issues List of Examples 1.1. Set “setvars†parameter 1.2. Set “time_to_kill†parameter 1.3. exec usage 1.4. exec_dset usage 1.5. exec_msg usage 1.6. exec_avp usage 1.7. exec_getenv usage 1.8. async exec usage Chapter 1. Admin Guide 1.1. Overview The Exec module enables the execution of external commands from the OpenSIPS script. Any valid shell commands are accepted. The final input string is evaluated and executed using the "/bin/sh" symlink/binary. OpenSIPS may additionally pass a lot more information about the request using environment variables: * SIP_HF_ contains value of each header field in request. If a header field occurred multiple times, values are concatenated and comma-separated. is in capital letters. Ff a header-field name occurred in compact form, is canonical. * SIP_TID is transaction identifier. All request retransmissions or CANCELs/ACKs associated with a previous INVITE result in the same value. * SIP_DID is dialog identifier, which is the same as to-tag. Initially, it is empty. * SIP_SRCIP is source IP address from which request came. * SIP_ORURI is original request URI. * SIP_RURI is current request URI (if unchanged, equal to original). * SIP_USER is userpart of current request URI. * SIP_OUSER is userpart of original request URI. NOTE: Any environment variables which are given to the exec module functions must be specified using the '$$' delimiter (e.g., $$SIP_OUSER), otherwise they will be evaluated as OpenSIPS pseudo-variables, throwing scripting errors. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. setvars (integer) Set to 1 to enable setting all above-mentioned environment variables for all executed commands. WARNING: Before enabling this parameter, make sure your "/bin/sh" is safe from the Shellshock bash vulnerability!!! Default value is 0 (disabled). Example 1.1. Set “setvars†parameter ... modparam("exec", "setvars", 1) ... 1.3.2. time_to_kill (integer) Specifies the longest time a program is allowed to execute. If the time is exceeded, the program is killed (SIGTERM) Default value is 0 (disabled). Example 1.2. Set “time_to_kill†parameter ... modparam("exec", "time_to_kill", 20) ... 1.4. Exported Functions 1.4.1. exec(command, [stdin], [stdout], [stderr], [envavp]) Executes an external command. The input is passed to the standard input of the new process, if specified, and the output is saved in the output variable. The function waits for the external script until it provided all its output (not necessary to actually finish). If no output (standard output or standard error) is required by the function, it will not block at all - it will simply launch the external script and continue the script. Meaning of the parameters is as follows: * command - command to be executed.It can include pseudovariables. * stdin - String to be passed to the standard input of the command. The string can be given as a pseudovariable. * stdout - pseudovariable where to store the output from the standard output of the process. * stderr - pseudovariable where to store the error from the standard error of the process. * envavp - Avp where to store the values for the environment variables to be passed for the command. The names of the environment variables will be "OSIPS_EXEC_#" where # will start from 0. For example if you store 2 values into an avp ("a" and "b") OSIPS_EXEC_0 will contain the first value and OSIPS_EXEC_1 the second value. WARNING: any OpenSIPS pseudo-vars which may contain special bourne shell (sh/bash) characters should be placed inside quotes, e.g. exec("update-stats.sh '$(ct{re.subst,/'//g})'"); WARNING: "stdin"/"stdout"/"stderr" parameters are not designed for large amounts of data, so one should be careful when using them. Because of the basic implementation, filled up pipes could cause a read deadlock. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE, ONREPLY_ROUTE. Example 1.3. exec usage ... $avp(env) = "a"; $avp(env) = "b"; exec("ls -l", , "$var(out)", "$var(err)", "$avp(env)"); xlog("The output is $var(out)\n"); xlog("Received the following error\n$var(err)"); ... $var(input) = "input"; exec("/home/../myscript.sh", "this is my $var(input) for exec\n", , , "$ avp(env)"); ... 1.4.2. exec_dset(command) (DEPRECATED) WARNING - this function is deprecated and it will be remove in the next version - please use the exec() function ( Section 1.4.1, “ exec(command, [stdin], [stdout], [stderr], [envavp]) †). Executes an external command. The current R-URI is appended to the command as its last parameter. The output of the command will rewrite the current R-URI. Multiple lines of output lead to multiple branches. Meaning of the parameters is as follows: * command (string, pvar) - command to be executed. It can include pseudo-variables or '$$' delimited UNIX environment variables WARNING: most OpenSIPS scripting variables should be quoted before being passed to external commands, as in: exec_avp("log-call.sh '$ct'"). This may help avoid some unexpected behaviour (e.g. unwanted extra parameters, errors due to special bash characters, etc.) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.4. exec_dset usage ... exec_dset("ruri-changer.sh"); exec_dset("ruri-changer.sh '$ct'"); ... 1.4.3. exec_msg(command) (DEPRECATED) WARNING - this function is deprecated and it will be remove in the next version - please use the exec() function ( Section 1.4.1, “ exec(command, [stdin], [stdout], [stderr], [envavp]) †). Executes an external command. The current SIP message is passed to it in the standard input, no command-line parameters are added and the output of the command is ignored. See sip-server/modules/exec/etc/exec.cfg in the source tarball for information on usage. Meaning of the parameters is as follows: * command (string) - command to be executed. It can include pseudo-variables or '$$' delimited UNIX environment variables WARNING: most OpenSIPS scripting variables should be quoted before being passed to external commands, as in: exec_avp("log-call.sh '$ct'"). This may help avoid some unexpected behaviour (e.g. unwanted extra parameters, errors due to special bash characters, etc.) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, LOCAL_ROUTE, TIMER_ROUTE, EVENT_ROUTE, ONREPLY_ROUTE. Example 1.5. exec_msg usage ... exec_msg("call-logger.sh '$ct' >> /var/log/call-logger/'$rU'.calls"); ... 1.4.4. exec_avp(command[, avplist]) (DEPRECATED) WARNING - this function is deprecated and it will be remove in the next version - please use the exec() function ( Section 1.4.1, “ exec(command, [stdin], [stdout], [stderr], [envavp]) †). Executes an external command. Each output line of the command is saved in its corresponding AVP from avplist. If avplist is missing or is incomplete, the populated AVPs will be 1, 2, 3... or N, N+1, N+2... Meaning of the parameters is as follows: * command (string) - command to be executed. It can include pseudo-variables or '$$' delimited UNIX environment variables * avplist (string) - comma separated list with AVP names to store the result in WARNING: most OpenSIPS scripting variables should be quoted before being passed to external commands, as in: exec_avp("log-call.sh '$ct'"). This may help avoid some unexpected behaviour (e.g. unwanted extra parameters, errors due to special bash characters, etc.) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE, ONREPLY_ROUTE. Example 1.6. exec_avp usage ... exec_avp("get-subscriber-details.sh '$rU'", "$avp(credit) $avp(contract_ model)"); ... 1.4.5. exec_getenv(environment_variable[, avp]) (DEPRECATED) WARNING - this function is deprecated and it will be remove in the next version - please use the exec() function ( Section 1.4.1, “ exec(command, [stdin], [stdout], [stderr], [envavp]) †). Obtains the value of a UNIX evironment_variable. The value is saved in 'avp'. If 'avp' is missing, output will be stored in $avp(1). If there is no such environment variable no value will be returned. Meaning of the parameters is as follows: * environment_variable (string) - environent variable name. Can also be specified as a pseudo-variable * avp - an AVP to store the result in WARNING: any OpenSIPS pseudo-vars which may contain special bash characters should be placed inside quotes, e.g. exec_getenv("'$ct'"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE, ONREPLY_ROUTE. Example 1.7. exec_getenv usage ... exec_getenv("HOSTNAME"); exec_getenv("HOSTNAME", "$avp(localhost)"); ... 1.5. Exported Asyncronous Functions 1.5.1. exec(command[,input[,output[,error[,env]]]]) Executes an external command. This function does exactly the same as Section 1.4.1, “ exec(command, [stdin], [stdout], [stderr], [envavp]) †(in terms of input, output and processing), but in an asynchronous way. The script execution is suspended until the external script provided all its output. OpenSIPS waits for the external script to close its output stream, not necessarily to terminate (so the script may still be running when OpenSIPS resumes the script execution on "seeing" EOF on the the output stream). NOTE: this function ignore the "error" parameters for now - the asynchronous waiting is done only on the output stream !! This may be fixed in the following versions. To read and understand more on the asynchronous functions, how to use them and what are their advantages, please refer to the OpenSIPS online Manual. Example 1.8. async exec usage { ... async( exec("ruri-changer.sh","$ru","$ru"), resume ); } route[resume] { ... } 1.6. Known Issues When imposing an execution timeout using time_to_kill, make sure your "/bin/sh" is a shell which does not fork when executed, case in which the job itself will not be killed, but rather its parent shell, while the job is silently inherited by "init" and will continue to run. "/bin/dash" is one of these troublesome shell environments. opensips-2.2.2/modules/exec/doc/000077500000000000000000000000001300170765700165245ustar00rootroot00000000000000opensips-2.2.2/modules/exec/doc/exec.xml000066400000000000000000000021751300170765700201770ustar00rootroot00000000000000 %docentities; ]> exec Module &osipsname; Jiri Kuthan FhG FOKUS
jiri@iptel.org
Jan Janak
jan@iptel.org
2003 FhG FOKUS $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/exec/doc/exec_admin.xml000066400000000000000000000344431300170765700213520ustar00rootroot00000000000000 &adminguide;
Overview The Exec module enables the execution of external commands from the &osips; script. Any valid shell commands are accepted. The final input string is evaluated and executed using the "/bin/sh" symlink/binary. &osips; may additionally pass a lot more information about the request using environment variables: SIP_HF_<hf_name> contains value of each header field in request. If a header field occurred multiple times, values are concatenated and comma-separated. <hf_name> is in capital letters. Ff a header-field name occurred in compact form, <hf_name> is canonical. SIP_TID is transaction identifier. All request retransmissions or CANCELs/ACKs associated with a previous INVITE result in the same value. SIP_DID is dialog identifier, which is the same as to-tag. Initially, it is empty. SIP_SRCIP is source &ip; address from which request came. SIP_ORURI is original request &uri;. SIP_RURI is current request &uri; (if unchanged, equal to original). SIP_USER is userpart of current request &uri;. SIP_OUSER is userpart of original request &uri;. NOTE: Any environment variables which are given to the exec module functions must be specified using the '$$' delimiter (e.g., $$SIP_OUSER), otherwise they will be evaluated as &osips; pseudo-variables, throwing scripting errors.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>setvars</varname> (integer) Set to 1 to enable setting all above-mentioned environment variables for all executed commands. WARNING: Before enabling this parameter, make sure your "/bin/sh" is safe from the Shellshock bash vulnerability!!! Default value is 0 (disabled). Set <quote>setvars</quote> parameter ... modparam("exec", "setvars", 1) ...
<varname>time_to_kill</varname> (integer) Specifies the longest time a program is allowed to execute. If the time is exceeded, the program is killed (SIGTERM) Default value is 0 (disabled). Set <quote>time_to_kill</quote> parameter ... modparam("exec", "time_to_kill", 20) ...
Exported Functions
<function moreinfo="none">exec(command, [stdin], [stdout], [stderr], [envavp])</function> Executes an external command. The input is passed to the standard input of the new process, if specified, and the output is saved in the output variable. The function waits for the external script until it provided all its output (not necessary to actually finish). If no output (standard output or standard error) is required by the function, it will not block at all - it will simply launch the external script and continue the script. Meaning of the parameters is as follows: command - command to be executed.It can include pseudovariables. stdin - String to be passed to the standard input of the command. The string can be given as a pseudovariable. stdout - pseudovariable where to store the output from the standard output of the process. stderr - pseudovariable where to store the error from the standard error of the process. envavp - Avp where to store the values for the environment variables to be passed for the command. The names of the environment variables will be "OSIPS_EXEC_#" where # will start from 0. For example if you store 2 values into an avp ("a" and "b") OSIPS_EXEC_0 will contain the first value and OSIPS_EXEC_1 the second value. WARNING: any OpenSIPS pseudo-vars which may contain special bourne shell (sh/bash) characters should be placed inside quotes, e.g. exec("update-stats.sh '$(ct{re.subst,/'//g})'"); WARNING: "stdin"/"stdout"/"stderr" parameters are not designed for large amounts of data, so one should be careful when using them. Because of the basic implementation, filled up pipes could cause a read deadlock. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE, ONREPLY_ROUTE. <function moreinfo="none">exec</function> usage ... $avp(env) = "a"; $avp(env) = "b"; exec("ls -l", , "$var(out)", "$var(err)", "$avp(env)"); xlog("The output is $var(out)\n"); xlog("Received the following error\n$var(err)"); ... $var(input) = "input"; exec("/home/../myscript.sh", "this is my $var(input) for exec\n", , , "$avp(env)"); ...
<function moreinfo="none">exec_dset(command) (DEPRECATED)</function> WARNING - this function is deprecated and it will be remove in the next version - please use the exec() function ( ). Executes an external command. The current &ruri; is appended to the command as its last parameter. The output of the command will rewrite the current R-URI. Multiple lines of output lead to multiple branches. Meaning of the parameters is as follows: command (string, pvar) - command to be executed. It can include pseudo-variables or '$$' delimited UNIX environment variables WARNING: most OpenSIPS scripting variables should be quoted before being passed to external commands, as in: exec_avp("log-call.sh '$ct'"). This may help avoid some unexpected behaviour (e.g. unwanted extra parameters, errors due to special bash characters, etc.) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function moreinfo="none">exec_dset</function> usage ... exec_dset("ruri-changer.sh"); exec_dset("ruri-changer.sh '$ct'"); ...
<function moreinfo="none">exec_msg(command) (DEPRECATED)</function> WARNING - this function is deprecated and it will be remove in the next version - please use the exec() function ( ). Executes an external command. The current SIP message is passed to it in the standard input, no command-line parameters are added and the output of the command is ignored. See sip-server/modules/exec/etc/exec.cfg in the source tarball for information on usage. Meaning of the parameters is as follows: command (string) - command to be executed. It can include pseudo-variables or '$$' delimited UNIX environment variables WARNING: most OpenSIPS scripting variables should be quoted before being passed to external commands, as in: exec_avp("log-call.sh '$ct'"). This may help avoid some unexpected behaviour (e.g. unwanted extra parameters, errors due to special bash characters, etc.) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, LOCAL_ROUTE, TIMER_ROUTE, EVENT_ROUTE, ONREPLY_ROUTE. <function moreinfo="none">exec_msg</function> usage ... exec_msg("call-logger.sh '$ct' >> /var/log/call-logger/'$rU'.calls"); ...
<function moreinfo="none">exec_avp(command[, avplist]) (DEPRECATED)</function> WARNING - this function is deprecated and it will be remove in the next version - please use the exec() function ( ). Executes an external command. Each output line of the command is saved in its corresponding AVP from avplist. If avplist is missing or is incomplete, the populated AVPs will be 1, 2, 3... or N, N+1, N+2... Meaning of the parameters is as follows: command (string) - command to be executed. It can include pseudo-variables or '$$' delimited UNIX environment variables avplist (string) - comma separated list with AVP names to store the result in WARNING: most OpenSIPS scripting variables should be quoted before being passed to external commands, as in: exec_avp("log-call.sh '$ct'"). This may help avoid some unexpected behaviour (e.g. unwanted extra parameters, errors due to special bash characters, etc.) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE, ONREPLY_ROUTE. <function moreinfo="none">exec_avp</function> usage ... exec_avp("get-subscriber-details.sh '$rU'", "$avp(credit) $avp(contract_model)"); ...
<function moreinfo="none">exec_getenv(environment_variable[, avp]) (DEPRECATED)</function> WARNING - this function is deprecated and it will be remove in the next version - please use the exec() function ( ). Obtains the value of a UNIX evironment_variable. The value is saved in 'avp'. If 'avp' is missing, output will be stored in $avp(1). If there is no such environment variable no value will be returned. Meaning of the parameters is as follows: environment_variable (string) - environent variable name. Can also be specified as a pseudo-variable avp - an AVP to store the result in WARNING: any OpenSIPS pseudo-vars which may contain special bash characters should be placed inside quotes, e.g. exec_getenv("'$ct'"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE, ONREPLY_ROUTE. <function moreinfo="none">exec_getenv</function> usage ... exec_getenv("HOSTNAME"); exec_getenv("HOSTNAME", "$avp(localhost)"); ...
Exported Asyncronous Functions
<function moreinfo="none">exec(command[,input[,output[,error[,env]]]])</function> Executes an external command. This function does exactly the same as (in terms of input, output and processing), but in an asynchronous way. The script execution is suspended until the external script provided all its output. OpenSIPS waits for the external script to close its output stream, not necessarily to terminate (so the script may still be running when OpenSIPS resumes the script execution on "seeing" EOF on the the output stream). NOTE: this function ignore the "error" parameters for now - the asynchronous waiting is done only on the output stream !! This may be fixed in the following versions. To read and understand more on the asynchronous functions, how to use them and what are their advantages, please refer to the OpenSIPS online Manual. <function moreinfo="none">async exec</function> usage { ... async( exec("ruri-changer.sh","$ru","$ru"), resume ); } route[resume] { ... }
Known Issues When imposing an execution timeout using , make sure your "/bin/sh" is a shell which does not fork when executed, case in which the job itself will not be killed, but rather its parent shell, while the job is silently inherited by "init" and will continue to run. "/bin/dash" is one of these troublesome shell environments.
opensips-2.2.2/modules/exec/exec.c000066400000000000000000000340311300170765700170500ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * -------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-28 scratchpad removed * 2004-07-21 rewrite uri done via action() (bogdan) */ #include #include #include #include #include #include #include /* #include */ #include #include "../../mem/mem.h" #include "../../error.h" #include "../../config.h" #include "../../parser/msg_parser.h" #include "../../dprint.h" #include "../../dset.h" #include "../../action.h" #include "../../usr_avp.h" #include "../../ut.h" #include "../../trim.h" #include "../../mod_fix.h" #include "exec.h" #include "kill.h" #define SLEEP_INTERVAL 300 int exec_msg(struct sip_msg *msg, char *cmd ) { FILE *pipe; int exit_status; int ret; pid_t pid; ret=-1; /* pessimist: assume error */ pid = __popen3(cmd, &pipe, NULL, NULL); if (pid < 0) { LM_ERR("cannot open pipe: %s\n", cmd); ser_error=E_EXEC; return -1; } LM_DBG("Forked pid %d\n", pid); if (fwrite(msg->buf, 1, msg->len, pipe)!=msg->len || fflush(pipe)) { LM_ERR("failed to write to pipe\n"); ser_error=E_EXEC; goto error01; } schedule_to_kill(pid); wait(&exit_status); /* success */ ret=1; error01: if (ferror(pipe)) { LM_ERR("pipe: %s\n", strerror(errno)); ser_error=E_EXEC; ret=-1; } pclose(pipe); if (WIFEXITED(exit_status)) { /* exited properly .... */ /* return false if script exited with non-zero status */ if (WEXITSTATUS(exit_status)!=0) ret=-1; } else { /* exited erroneously */ LM_ERR("cmd %s failed. exit_status=%d, errno=%d: %s\n", cmd, exit_status, errno, strerror(errno) ); ret=-1; } return ret; } int exec_write_input(FILE** stream, str* input) { if (fwrite(input->s, 1, input->len, *stream) != input->len) { LM_ERR("failed to write to pipe\n"); ser_error=E_EXEC; return -1; } if (ferror(*stream)) { LM_ERR("writing pipe: %s\n", strerror(errno)); ser_error=E_EXEC; return -1; } pclose(*stream); return 0; } int exec_str(struct sip_msg *msg, char *cmd, char *param, int param_len) { int cmd_len; FILE *pipe; char *cmd_line; int ret; int l1; static char uri_line[MAX_URI_SIZE+1]; int uri_cnt; str uri; int exit_status; pid_t pid; /* pessimist: assume error by default */ ret=-1; l1=strlen(cmd); if(param_len>0) cmd_len=l1+param_len+4; else cmd_len=l1+1; cmd_line=pkg_malloc(cmd_len); if (cmd_line==0) { ret=ser_error=E_OUT_OF_MEM; LM_ERR("no pkg mem for command\n"); goto error00; } /* 'command parameter \0' */ memcpy(cmd_line, cmd, l1); if(param_len>0) { cmd_line[l1]=' '; cmd_line[l1+1]='\''; memcpy(cmd_line+l1+2, param, param_len); cmd_line[l1+param_len+2]='\''; cmd_line[l1+param_len+3]=0; } else { cmd_line[l1] = 0; } pid = __popen3(cmd_line, NULL, &pipe, NULL); if (pid < 0) { LM_ERR("failed to run command: %s\n", cmd_line); ser_error=E_EXEC; goto error01; } LM_DBG("Forked pid %d\n", pid); schedule_to_kill(pid); wait(&exit_status); /* read now line by line */ uri_cnt=0; while (fgets(uri_line, MAX_URI_SIZE, pipe)) { uri.s = uri_line; uri.len=strlen(uri.s); trim_trailing(&uri); /* skip empty line */ if (uri.len==0) continue; /* ZT */ uri.s[uri.len]=0; if (uri_cnt==0) { if (set_ruri(msg, &uri)==-1 ) { LM_ERR("failed to set new RURI\n"); ser_error=E_OUT_OF_MEM; goto error02; } } else { if (append_branch(msg, &uri, 0, 0, Q_UNSPECIFIED, 0, 0)==-1) { LM_ERR("append_branch failed; too many or too long URIs?\n"); goto error02; } } uri_cnt++; } if (uri_cnt==0) { LM_ERR("no uri from %s\n", cmd_line ); goto error02; } /* success */ ret=1; error02: if (ferror(pipe)) { LM_ERR("in pipe: %s\n", strerror(errno)); ser_error=E_EXEC; ret=-1; } pclose(pipe); if (WIFEXITED(exit_status)) { /* exited properly .... */ /* return false if script exited with non-zero status */ if (WEXITSTATUS(exit_status)!=0) ret=-1; } else { /* exited erroneously */ LM_ERR("cmd %s failed. exit_status=%d, errno=%d: %s\n", cmd, exit_status, errno, strerror(errno) ); ret=-1; } error01: pkg_free(cmd_line); error00: return ret; } int exec_avp(struct sip_msg *msg, char *cmd, pvname_list_p avpl) { int_str avp_val; int_str avp_name; unsigned short avp_type; FILE *pipe; int ret; char res_line[MAX_URI_SIZE+1]; str res; int exit_status; int i; pvname_list_t* crt; pid_t pid; /* pessimist: assume error by default */ ret=-1; pid = __popen3(cmd, NULL, &pipe, NULL); if (pid < 0) { LM_ERR("failed to run command: %s\n", cmd); ser_error=E_EXEC; return ret; } LM_DBG("Forked pid %d\n", pid); schedule_to_kill(pid); wait(&exit_status); /* read now line by line */ i=0; crt = avpl; while (fgets(res_line, MAX_URI_SIZE, pipe)) { res.s = res_line; res.len=strlen(res.s); trim_trailing(&res); /* skip empty line */ if (res.len==0) continue; /* ZT */ res.s[res.len]=0; avp_type = 0; if(crt==NULL) { avp_name.s.s = int2str(i + 1, &avp_name.s.len); if (!avp_name.s.s) { LM_ERR("cannot convert %d to string\n", i + 1); goto error; } avp_name.n = get_avp_id(&avp_name.s); if (avp_name.n < 0) { LM_ERR("cannot get avp id\n"); goto error; } } else { if(pv_get_avp_name(msg, &(crt->sname.pvp), &avp_name.n, &avp_type)!=0) { LM_ERR("can't get item name [%d]\n",i); goto error; } } avp_type |= AVP_VAL_STR; avp_val.s = res; if(add_avp(avp_type, avp_name.n, avp_val)!=0) { LM_ERR("unable to add avp\n"); goto error; } if(crt) crt = crt->next; i++; } if (i==0) LM_DBG("no result from %s\n", cmd); /* success */ ret=1; error: if (ferror(pipe)) { LM_ERR("pipe: %d/%s\n", errno, strerror(errno)); ser_error=E_EXEC; ret=-1; } pclose(pipe); if (WIFEXITED(exit_status)) { /* exited properly .... */ /* return false if script exited with non-zero status */ if (WEXITSTATUS(exit_status)!=0) ret=-1; } else { /* exited erroneously */ LM_ERR("cmd %s failed. exit_status=%d, errno=%d: %s\n", cmd, exit_status, errno, strerror(errno) ); ret=-1; } return ret; } int exec_getenv(struct sip_msg *msg, char *cmd, pvname_list_p avpl) { int_str avp_val; int_str avp_name; unsigned short avp_type; int ret; str res; pvname_list_t* crt; /* pessimist: assume error by default */ ret=-1; res.s=getenv(cmd); if (res.s==NULL) { goto error; } res.len=strlen(res.s); crt = avpl; avp_type = 0; if(crt==NULL) { avp_name.s.s = int2str(1, &avp_name.s.len); if (!avp_name.s.s) { LM_ERR("cannot convert 1 to string\n"); goto error; } avp_name.n = get_avp_id(&avp_name.s); if (avp_name.n < 0) { LM_ERR("cannot get avp id\n"); goto error; } } else { if(pv_get_avp_name(msg, &(crt->sname.pvp), &avp_name.n, &avp_type)!=0) { LM_ERR("can't get item name\n"); goto error; } } avp_type |= AVP_VAL_STR; avp_val.s = res; if(add_avp(avp_type, avp_name.n, avp_val)!=0) { LM_ERR("unable to add avp\n"); goto error; } /* success */ ret=1; error: return ret; } static int read_and_write2var(struct sip_msg* msg, FILE** strm, gparam_p outvar) { #define MAX_LINE_SIZE 1024 #define MAX_BUF_SIZE 32 * MAX_LINE_SIZE int buflen=0, tmplen; pv_value_t outval; char buf[MAX_BUF_SIZE], tmpbuf[MAX_LINE_SIZE]; while((tmplen=fread(tmpbuf, 1, MAX_LINE_SIZE, *strm))) { if ((buflen + tmplen) >= MAX_BUF_SIZE) { LM_WARN("no more space in output buffer\n"); break; } memcpy(buf+buflen, tmpbuf, tmplen); buflen += tmplen; } outval.flags = PV_VAL_STR; outval.rs.s = buf; outval.rs.len = buflen; if (buflen && pv_set_value(msg, &outvar->v.pve->spec, 0, &outval) < 0) { LM_ERR("cannot set output pv value\n"); return -1; } return 0; #undef MAX_LINE_SIZE #undef MAX_BUF_SIZE } int exec_sync(struct sip_msg* msg, str* command, str* input, gparam_p outvar, gparam_p errvar) { pid_t pid; int ret = -1; FILE *pin = NULL, *pout, *perr; if ((input && input->len && input->s) || outvar || errvar) { pid = __popen3(command->s, (input&&input->len&&input->s) ? &pin : NULL, outvar ? &pout : NULL, errvar ? &perr : NULL); } else { pid = fork(); if (pid == 0) { execl("/bin/sh", "/bin/sh", "-c", command->s, NULL); exit(-1); } else if (pid < 0) { LM_ERR("fork failed\n"); return -1; } } if (input && input->len && pin) { if (fwrite(input->s, 1, input->len, pin) != input->len) { LM_ERR("failed to write to pipe\n"); ser_error=E_EXEC; goto error; } if (ferror(pin)) { ser_error=E_EXEC; goto error; } pclose(pin); } schedule_to_kill(pid); if (outvar) { if (read_and_write2var(msg, &pout, outvar) < 0) { LM_ERR("failed reading stdout from pipe\n"); goto error; } } if (errvar) { if (read_and_write2var(msg, &perr, errvar) < 0) { LM_ERR("failed reading stderr from pipe\n"); goto error; } } ret=1; error: if (outvar && ferror(pout)) { LM_ERR("stdout reading pipe: %s\n", strerror(errno)); ser_error=E_EXEC; ret=-1; } if (errvar && ferror(perr)) { LM_ERR("stderr reading pipe: %s\n", strerror(errno)); ser_error=E_EXEC; ret=-1; } if (outvar) pclose(pout); if (errvar) pclose(perr); return ret; } int start_async_exec(struct sip_msg* msg, str* command, str* input, gparam_p outvar, int *fd) { pid_t pid; FILE *pin = NULL, *pout; int val; if ((input&&input->s&&input->len) || outvar) { pid = __popen3(command->s, (input&&input->s&&input->len) ? &pin : NULL, outvar ? &pout : NULL, NULL); } else { pid = fork(); if (pid == 0) { /* child process*/ execl("/bin/sh", "/bin/sh", "-c", command->s, NULL); exit(-1); } if (pid<0) { /*error of fork*/ LM_ERR("failed to fork (%s)\n",strerror(errno)); goto error; } } if (input && input->len && pin) { if ( (val=fwrite(input->s, 1, input->len, pin)) != input->len) { LM_ERR("failed to write all (%d needed, %d written) to input pipe," " but continuing\n",input->len,val); } if (ferror(pin)) { LM_ERR("failure detected (%s), continuing..\n",strerror(errno)); } pclose(pin); } /* set time to kill on the new process */ schedule_to_kill(pid); if (outvar==NULL) { /* nothing to wait for, no I/O */ return 2; } /* prepare the read FD and make it non-blocking */ if ( (*fd=dup( fileno( pout ) ))<0 ) { LM_ERR("dup failed: (%d) %s\n", errno, strerror(errno)); goto error; } val = fcntl( *fd, F_GETFL); if (val==-1){ LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno)); goto error2; } if (fcntl( *fd , F_SETFL, val|O_NONBLOCK)==-1){ LM_ERR("set non-blocking failed: (%d) %s\n", errno, strerror(errno)); goto error2; } pclose(pout); /* async started with success */ return 1; error2: close(*fd); error: /* async failed */ if (outvar) pclose(pout); return -1; } int resume_async_exec(int fd, struct sip_msg *msg, void *param) { #define MAX_LINE_SIZE 1024 char buf[MAX_LINE_SIZE+1]; exec_async_param *p = (exec_async_param*)param; pv_value_t outval; char *s1, *s2; int n, len; if (p->buf) { memcpy( buf, p->buf, p->buf_len); len = p->buf_len; shm_free(p->buf); p->buf = NULL; } else { len = 0; } do { n=read( fd, buf+len, MAX_LINE_SIZE-len); LM_DBG(" read %d [%.*s] \n",n, n<0?0:n,buf+len); if (n<0) { if (errno==EINTR) continue; if (errno==EAGAIN || errno==EWOULDBLOCK) { /* nothing more to read */ if (len) { /* store what is left */ if ((p->buf=(char*)shm_malloc(len))==NULL) { LM_ERR("failed to allocate buffer\n"); goto error; } memcpy( p->buf, buf, len); p->buf_len = len; LM_DBG(" storing %d [%.*s] \n", p->buf_len, p->buf_len, p->buf); } /* async should continue */ async_status = ASYNC_CONTINUE; return 1; } LM_ERR("read failed with %d (%s)\n",errno, strerror(errno)); /* terminate everything */ goto error; } /* EOF ? */ if (n==0) { if (len) { /* take whatever is left in buffer and push it as var */ outval.flags = PV_VAL_STR; outval.rs.s = buf; outval.rs.len = len; LM_DBG("setting var [%.*s]\n",outval.rs.len,outval.rs.s); if (pv_set_value(msg, &p->outvar->v.pve->spec, 0, &outval) < 0) { LM_ERR("failed to set variable :(, continuing \n"); } } break; } /* successful reading ( n>0 ) */ LM_DBG(" having %d [%.*s] \n", len+n, len+n, buf); if (n+len==MAX_LINE_SIZE) { /* we have full buffer, pack it as a line */ buf[n+len] = '\n'; n++; } /* search for '\n' in the newly read data */ s1 = buf; while ( (buf+len+n-s1>0) && ((s2=q_memchr(s1, '\n', buf+len+n-s1))!=NULL) ) { /* push it as var */ outval.flags = PV_VAL_STR; outval.rs.s = s1; outval.rs.len = s2-s1; LM_DBG("setting var [%.*s]\n",outval.rs.len,outval.rs.s); if (pv_set_value(msg, &p->outvar->v.pve->spec, 0, &outval) < 0) { LM_ERR("failed to set variable :(, continuing \n"); } s1 = s2+1; } /* any data consumed ? */ if ( s1!=buf+len ) { /* yes -> shift the whole buffer to left */ len = buf+len+n-s1; if (len) memmove( buf, s1, len); } else { /* no -> increase the len of the buffer */ len += n; } }while(1); /* done with the async */ shm_free(param); /* make sure our fd is closed by the async engine */ async_status = ASYNC_DONE_CLOSE_FD; return 1; error: shm_free(param); /* stay with default async status ASYNC_DONE */ return -1; #undef MAX_LINE_SIZE } opensips-2.2.2/modules/exec/exec.h000066400000000000000000000036331300170765700170610ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _EXEC_H #define _EXEC_H #include "../../pvar.h" #include "../../locking.h" typedef struct _exec_cmd { char *cmd; str input; int pid; struct _exec_cmd *next; } exec_cmd_t; typedef struct _exec_list { int active_childs; gen_lock_t *lock; exec_cmd_t *first, *last; } exec_list_t, *exec_list_p; typedef struct _exec_async_param { gparam_p outvar; char *buf; int buf_len; } exec_async_param; /* list head */ extern exec_list_p exec_async_list; /* process that waits for asynchronous executions */ void exec_async_proc(int rank); int exec_async(struct sip_msg *msg, char *cmd, str* input ); int exec_str(struct sip_msg *msg, char *cmd, char *param, int param_len); int exec_msg(struct sip_msg *msg, char *cmd ); int exec_avp(struct sip_msg *msg, char *cmd, pvname_list_p avpl); int exec_getenv(struct sip_msg *msg, char *cmd, pvname_list_p avpl); int exec_sync(struct sip_msg* msg, str* command, str* input, gparam_p outvar, gparam_p errvar); int start_async_exec(struct sip_msg* msg, str* command, str* input, gparam_p outvar, int *fd); int resume_async_exec(int fd, struct sip_msg *msg, void *param); #endif opensips-2.2.2/modules/exec/exec_hf.c000066400000000000000000000321331300170765700175260ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * history * ------- * 2003-02-28 scratchpad compatibility abandoned * 2003-01-29 scratchpad removed * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) */ /* functions for creating environment variables out of a request's * header; known compact header field names are translated to * canonical form; multiple header field occurrences are merged * into a single variable * * known limitations: * - compact header field names unknown to parser will not be translated to * canonical form. Thus, environment variables may have either name and * users have to check for both of them. * - symbols in header field names will be translated to underscore * */ #include #include "../../parser/msg_parser.h" #include "../../parser/parse_to.h" #include "../../parser/parse_via.h" #include "../../parser/parse_uri.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../md5utils.h" #include "exec_hf.h" /* should be environment variables set by header fields ? */ unsigned int setvars=0; /* insert a new header field into the structure; */ static int insert_hf( struct hf_wrapper **list, struct hdr_field *hf ) { struct hf_wrapper *w; /* new wrapper */ struct hf_wrapper *i; w=(struct hf_wrapper *)pkg_malloc(sizeof(struct hf_wrapper)); if (!w) { LM_ERR("ran out of pkg mem\n"); return 0; } memset(w, 0, sizeof(struct hf_wrapper)); w->var_type=W_HF;w->u.hf=hf; w->prefix=HF_PREFIX; w->prefix_len=HF_PREFIX_LEN; /* is there another hf of the same type?... */ for(i=*list; i; i=i->next_other) { if (i->var_type==W_HF && i->u.hf->type==hf->type) { /* if it is OTHER, check name too */ if (hf->type==HDR_OTHER_T && (hf->name.len!=i->u.hf->name.len || strncasecmp(i->u.hf->name.s, hf->name.s, hf->name.len)!=0)) continue; /* yes, we found a hf of same type */ w->next_same=i->next_same; w->next_other=i->next_other; i->next_same=w; break; } } /* ... no previous HF of the same type found */ if (i==0) { w->next_other=*list; *list=w; } return 1; } void release_hf_struct( struct hf_wrapper *list ) { struct hf_wrapper *i, *j, *nexts, *nexto; i=list; while(i) { nexto=i->next_other; j=i->next_same; pkg_free(i); /* release list of same type hf */ while(j) { nexts=j->next_same; pkg_free(j); j=nexts; } i=nexto; } } /* if that is some of well-known header fields which have compact * form, return canonical form ... returns 1 and sets params; * 0 is returned otherwise */ static int compacthdr_type2str(hdr_types_t type, char **hname, int *hlen ) { switch(type) { /* HDR_CONTENT_ENCODING: 'e' -- unsupported by parser */ /* HDR_SUBJECT: 's' -- unsupported by parser */ case HDR_VIA_T /* v */ : *hname=VAR_VIA; *hlen=VAR_VIA_LEN; break; case HDR_CONTENTTYPE_T /* c */ : *hname=VAR_CTYPE; *hlen=VAR_CTYPE_LEN; break; case HDR_FROM_T /* f */: *hname=VAR_FROM; *hlen=VAR_FROM_LEN; break; case HDR_CALLID_T /* i */: *hname=VAR_CALLID; *hlen=VAR_CALLID_LEN; break; case HDR_SUPPORTED_T /* k */: *hname=VAR_SUPPORTED; *hlen=VAR_SUPPORTED_LEN; break; case HDR_CONTENTLENGTH_T /* l */: *hname=VAR_CLEN; *hlen=VAR_CLEN_LEN; break; case HDR_CONTACT_T /* m */: *hname=VAR_CONTACT; *hlen=VAR_CONTACT_LEN; break; case HDR_TO_T /* t */: *hname=VAR_TO; *hlen=VAR_TO_LEN; break; case HDR_EVENT_T /* o */: *hname=VAR_EVENT; *hlen=VAR_EVENT_LEN; break; default: return 0; } return 1; } static int canonize_headername(str *orig, char **hname, int *hlen ) { char *c; int i; *hlen=orig->len; *hname=pkg_malloc(*hlen); if (!*hname) { LM_ERR("no pkg mem for hname\n"); return 0; } for (c=orig->s, i=0; i<*hlen; i++, c++) { /* lowercase to uppercase */ if (*c>='a' && *c<='z') *((*hname)+i)=*c-('a'-'A'); /* uppercase and numbers stay "as is" */ else if ((*c>='A' && *c<='Z')||(*c>='0' && *c<='9')) *((*hname)+i)=*c; /* legal symbols will be translated to underscore */ else if (strchr(UNRESERVED_MARK HNV_UNRESERVED, *c) || (*c==ESCAPE)) *((*hname)+i)=HFN_SYMBOL; else { LM_ERR("print_var unexpected char '%c' in hfname %.*s\n", *c, *hlen, orig->s ); *((*hname)+i)=HFN_SYMBOL; } } return 1; } static int print_av_var(struct hf_wrapper *w) { int env_len; char *env; char *c; env_len=w->u.av.attr.len+1/*assignment*/+w->u.av.val.len+1/*ZT*/; env=pkg_malloc(env_len); if (!env) { LM_ERR("no pkg mem\n"); return 0; } c=env; memcpy(c, w->u.av.attr.s, w->u.av.attr.len); c+=w->u.av.attr.len; *c=EV_ASSIGN;c++; memcpy(c, w->u.av.val.s, w->u.av.val.len);c+=w->u.av.val.len; *c=0; /* zero termination */ w->envvar=env; return 1; } /* creates a malloc-ed string with environment variable; returns 1 on success, * 0 on failure */ static int print_hf_var(struct hf_wrapper *w, int offset) { char *hname; int hlen; short canonical; char *envvar; int envvar_len; struct hf_wrapper *wi; char *c; /* make -Wall happy */ hname=0;hlen=0;envvar=0; /* Make sure header names with possible compact forms * will be printed canonically */ canonical=compacthdr_type2str(w->u.hf->type, &hname, &hlen); /* header field has not been made canonical using a table; * do it now by uppercasing header-field name */ if (!canonical) { if (!canonize_headername(&w->u.hf->name, &hname, &hlen)) { LM_ERR("canonize_hn error\n"); return 0; } } /* now we have a header name, let us generate the var */ envvar_len=w->u.hf->body.len; for(wi=w->next_same; wi; wi=wi->next_same) { /* other values, separated */ envvar_len+=1 /* separator */ + wi->u.hf->body.len; } envvar=pkg_malloc(w->prefix_len+hlen+1/*assignment*/+envvar_len+1/*ZT*/); if (!envvar) { LM_ERR("no pkg mem\n"); goto error00; } memcpy(envvar, w->prefix, w->prefix_len); c=envvar+w->prefix_len; memcpy(c, hname, hlen ); c+=hlen; *c=EV_ASSIGN;c++; memcpy(c, w->u.hf->body.s+offset, w->u.hf->body.len ); c+=w->u.hf->body.len; for (wi=w->next_same; wi; wi=wi->next_same) { *c=HF_SEPARATOR;c++; memcpy(c, wi->u.hf->body.s+offset, wi->u.hf->body.len ); c+=wi->u.hf->body.len; } *c=0; /* zero termination */ LM_DBG("%s\n", envvar ); w->envvar=envvar; if (!canonical) pkg_free(hname); return 1; error00: if (!canonical) pkg_free(hname); return 0; } static int print_var(struct hf_wrapper *w, int offset) { switch(w->var_type) { case W_HF: return print_hf_var(w, offset); case W_AV: return print_av_var(w); default: LM_CRIT("unknown type: %d\n", w->var_type ); return 0; } } void release_vars(struct hf_wrapper *list) { while(list) { if (list->envvar) { pkg_free(list->envvar); list->envvar=0; } list=list->next_other; } } /* create ordered HF structure in pkg memory */ static int build_hf_struct(struct sip_msg *msg, struct hf_wrapper **list) { struct hdr_field *h; *list=0; /* create ordered header-field structure */ for (h=msg->headers; h; h=h->next) { if (!insert_hf(list,h)) { LM_ERR("insert_hf failed\n"); goto error00; } } return 1; error00: release_hf_struct(*list); *list=0; return 0; } /* create env vars in malloc memory */ static int create_vars(struct hf_wrapper *list, int offset) { int var_cnt; struct hf_wrapper *w; /* create variables now */ var_cnt=0; for(w=list;w;w=w->next_other) { if (!print_var(w, offset)) { LM_ERR("create_vars failed\n"); return 0; } var_cnt++; } return var_cnt; } environment_t *replace_env(struct hf_wrapper *list) { int var_cnt; char **cp; struct hf_wrapper *w; char **new_env; int i; environment_t *backup_env; backup_env=(environment_t *)pkg_malloc(sizeof(environment_t)); if (!backup_env) { LM_ERR("no pkg mem for backup env\n"); return 0; } /* count length of current env list */ var_cnt=0; for (cp=environ; *cp; cp++) var_cnt++; backup_env->old_cnt=var_cnt; /* count length of our extensions */ for(w=list;w;w=w->next_other) var_cnt++; new_env=pkg_malloc((var_cnt+1)*sizeof(char *)); if (!new_env) { LM_ERR("no pkg mem\n"); return 0; } /* put all var pointers into new environment */ i=0; for (cp=environ; *cp; cp++) { /* replicate old env */ new_env[i]=*cp; i++; } for (w=list;w;w=w->next_other) { /* append new env */ new_env[i]=w->envvar; i++; } new_env[i]=0; /* zero termination */ /* install new environment */ backup_env->env=environ; environ=new_env; /* return previous environment */ return backup_env; } void unset_env(environment_t *backup_env) { char **cur_env, **cur_env0; int i; /* switch-over to backup environment */ cur_env0=cur_env=environ; environ=backup_env->env; i=0; /* release environment */ while(*cur_env) { /* leave previously existing vars alone */ if (i>=backup_env->old_cnt) { pkg_free(*cur_env); } cur_env++; i++; } pkg_free(cur_env0); pkg_free(backup_env); } static int append_var(char *name, char *value, int len, struct hf_wrapper **list) { struct hf_wrapper *w; w=(struct hf_wrapper *)pkg_malloc(sizeof(struct hf_wrapper)); if (!w) { LM_ERR("ran out of pkg mem\n"); return 0; } memset(w, 0, sizeof(struct hf_wrapper)); w->var_type=W_AV; w->u.av.attr.s=name; w->u.av.attr.len=strlen(name); w->u.av.val.s=value; /* NULL strings considered empty, if len unknown, calculate it now */ w->u.av.val.len= value==0?0:(len==0? strlen(value) : len); w->next_other=*list; *list=w; return 1; } static int append_fixed_vars(struct sip_msg *msg, struct hf_wrapper **list) { static char tid[MD5_LEN]; str *uri; struct sip_uri parsed_uri, oparsed_uri; char *val; int val_len; /* source ip */ if (!append_var(EV_SRCIP, ip_addr2a(&msg->rcv.src_ip), 0, list)) { LM_ERR("append_var SRCIP failed \n"); return 0; } /* request URI */ uri=msg->new_uri.s && msg->new_uri.len ? &msg->new_uri : &msg->first_line.u.request.uri; if (!append_var(EV_RURI, uri->s, uri->len, list )) { LM_ERR("append_var URI failed\n"); return 0; } /* userpart of request URI */ if (parse_uri(uri->s, uri->len, &parsed_uri)<0) { LM_WARN("uri not parsed\n"); } else { if (!append_var(EV_USER, parsed_uri.user.s, parsed_uri.user.len, list)) { LM_ERR("append_var USER failed\n"); goto error; } } /* original URI */ if (!append_var(EV_ORURI, msg->first_line.u.request.uri.s, msg->first_line.u.request.uri.len, list)) { LM_ERR("append_var O-URI failed\n"); goto error; } /* userpart of request URI */ if (parse_uri(msg->first_line.u.request.uri.s, msg->first_line.u.request.uri.len, &oparsed_uri)<0) { LM_WARN("orig URI not parsed\n"); } else { if (!append_var(EV_OUSER, oparsed_uri.user.s, oparsed_uri.user.len, list)) { LM_ERR("ppend_var OUSER failed\n"); goto error; } } /* tid, transaction id == via/branch */ if (!char_msg_val(msg, tid)) { LM_WARN("no tid can be determined\n"); val=0; val_len=0; } else { val=tid;val_len=MD5_LEN; } if (!append_var(EV_TID, val,val_len, list)) { LM_ERR("append_var TID failed\n"); goto error; } /* did, dialogue id == To-tag */ if (!(msg->to && get_to(msg) )) { LM_ERR("no to-tag\n"); val=0; val_len=0; } else { val=get_to(msg)->tag_value.s; val_len=get_to(msg)->tag_value.len; } if (!append_var(EV_DID, val, val_len, list)) { LM_ERR("append_var DID failed\n"); goto error; } return 1; error: return 0; } environment_t *set_env(struct sip_msg *msg) { struct hf_wrapper *hf_list; environment_t *backup_env; /* parse all so that we can pass all header fields to script */ if (parse_headers(msg, HDR_EOH_F, 0)==-1) { LM_ERR("parsing failed\n"); return 0; } hf_list=0; /* create a temporary structure with ordered header fields * and create environment variables out of it */ if (!build_hf_struct(msg, &hf_list)) { LM_ERR("build_hf_struct failed\n"); return 0; } if (!append_fixed_vars(msg, &hf_list)) { LM_ERR("append_fixed_vars failed\n"); goto error01; } /* create now the strings for environment variables */ if (!create_vars(hf_list, 0)) { LM_ERR("create_vars failed\n"); goto error00; } /* install the variables in current environment */ backup_env=replace_env(hf_list); if (!backup_env) { LM_ERR("replace_env failed\n"); goto error00; } /* release the ordered HF structure -- we only need the vars now */ release_hf_struct(hf_list); return backup_env; error00: release_vars(hf_list); /* release variables */ error01: release_hf_struct(hf_list); /* release temporary ordered HF struct */ return 0; } opensips-2.2.2/modules/exec/exec_hf.h000066400000000000000000000066661300170765700175470ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _EXEC_HF_H #define _EXEC_HF_H #include "../../parser/msg_parser.h" /* prefix prepended to header field name in env var name */ #define SIP "SIP_" #define HF_PREFIX SIP "HF_" #define HF_PREFIX_LEN (sizeof(HF_PREFIX)-1) /* well known variable names */ #define EV_SRCIP SIP "SRCIP" #define EV_RURI SIP "RURI" #define EV_ORURI SIP "ORUI" #define EV_USER SIP "USER" #define EV_OUSER SIP "OUSER" #define EV_TID SIP "TID" #define EV_DID SIP "DID" /* env var assignment operator */ #define EV_ASSIGN '=' /* header field separator */ #define HF_SEPARATOR ',' /* RFC3261 -- characters legal in header names; a really * _bloated_ thing */ #define UNRESERVED_MARK "-_.!~*'()" #define HNV_UNRESERVED "[]/?:+$" #define ESCAPE '%' /* and this is what all such crazy symbols in header field * name will be replaced with in env vars */ #define HFN_SYMBOL '_' #define VAR_VIA "VIA" #define VAR_VIA_LEN (sizeof(VAR_VIA)-1) #define VAR_CTYPE "CONTENT_TYPE" #define VAR_CTYPE_LEN (sizeof(VAR_CTYPE)-1) #define VAR_FROM "FROM" #define VAR_FROM_LEN (sizeof(VAR_FROM)-1) #define VAR_CALLID "CALLID" #define VAR_CALLID_LEN (sizeof(VAR_CALLID)-1) #define VAR_SUPPORTED "SUPPORTED" #define VAR_SUPPORTED_LEN (sizeof(VAR_SUPPORTED)-1) #define VAR_CLEN "CONTENT_LENGTH" #define VAR_CLEN_LEN (sizeof(VAR_CLEN)-1) #define VAR_CONTACT "CONTACT" #define VAR_CONTACT_LEN (sizeof(VAR_CONTACT)-1) #define VAR_TO "TO" #define VAR_TO_LEN (sizeof(VAR_TO)-1) #define VAR_EVENT "EVENT" #define VAR_EVENT_LEN (sizeof(VAR_EVENT)-1) #if 0 /* _JUST_FOR_INFO_HERE */ struct hdr_field { int type; /* Header field type */ str name; /* Header field name */ str body; /* Header field body */ void* parsed; /* Parsed data structures */ struct hdr_field* next; /* Next header field in the list */ }; #endif typedef struct env { char** env; int old_cnt; } environment_t; struct attrval { str attr; str val; }; enum wrapper_type { W_HF=1, W_AV }; struct hf_wrapper { enum wrapper_type var_type; union { struct hdr_field *hf; struct attrval av; } u; /* next header field of the same type */ struct hf_wrapper *next_same; /* next header field of a different type */ struct hf_wrapper *next_other; /* env var value (zero terminated) */ char *envvar; /* variable name prefix (if any) */ char *prefix; int prefix_len; }; extern unsigned int setvars; extern char **environ; environment_t *set_env(struct sip_msg *msg); void unset_env(environment_t *backup_env); void release_hf_struct ( struct hf_wrapper *list); void release_vars(struct hf_wrapper* list); environment_t *replace_env(struct hf_wrapper *list); #endif opensips-2.2.2/modules/exec/exec_mod.c000066400000000000000000000330761300170765700177170ustar00rootroot00000000000000/* * execution module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) */ #include #include #include "../../sr_module.h" #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../dprint.h" #include "../../mod_fix.h" #include "../../parser/parse_uri.h" #include "../../ut.h" #include #include "exec.h" #include "kill.h" #include "exec_hf.h" unsigned int time_to_kill=0; static int mod_init( void ); inline static int w_exec_dset(struct sip_msg* msg, char* cmd, char* foo); inline static int w_exec_msg(struct sip_msg* msg, char* cmd, char* foo); inline static int w_exec_avp(struct sip_msg* msg, char* cmd, char* avpl); inline static int w_exec_getenv(struct sip_msg* msg, char* cmd, char* avpl); inline static int w_exec(struct sip_msg* msg, char* cmd, char* in, char* out, char* err, char* avp_env); inline static int w_async_exec(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char *cmd, char* out, char* in, char* err, char* avp_env ); static int exec_avp_fixup(void** param, int param_no); static int exec_fixup(void** param, int param_no); inline static void exec_shutdown(void); /* * Exported functions */ static acmd_export_t acmds[] = { {"exec", (acmd_function)w_async_exec, 5, exec_fixup }, {"exec", (acmd_function)w_async_exec, 4, exec_fixup }, {"exec", (acmd_function)w_async_exec, 3, exec_fixup }, {"exec", (acmd_function)w_async_exec, 2, exec_fixup }, {"exec", (acmd_function)w_async_exec, 1, exec_fixup }, {0, 0, 0, 0} }; static cmd_export_t cmds[] = { {"exec", (cmd_function)w_exec, 5, exec_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {"exec", (cmd_function)w_exec, 4, exec_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {"exec", (cmd_function)w_exec, 3, exec_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {"exec", (cmd_function)w_exec, 2, exec_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {"exec", (cmd_function)w_exec, 1, exec_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {"exec_dset", (cmd_function)w_exec_dset, 1, exec_avp_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"exec_msg", (cmd_function)w_exec_msg, 1, exec_avp_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {"exec_avp", (cmd_function)w_exec_avp, 1, exec_avp_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {"exec_avp", (cmd_function)w_exec_avp, 2, exec_avp_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {"exec_getenv", (cmd_function)w_exec_getenv, 2, exec_avp_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE|ONREPLY_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"time_to_kill", INT_PARAM, &time_to_kill}, {"setvars", INT_PARAM, &setvars }, {0, 0, 0} }; #ifdef STATIC_EXEC struct module_exports exec_exports = { #else struct module_exports exports= { #endif "exec", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS,/* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ acmds, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* initialization module */ 0, /* response function */ exec_shutdown, /* destroy function */ 0 /* per-child init function */ }; void exec_shutdown(void) { if (time_to_kill) destroy_kill(); } static int mod_init( void ) { LM_INFO("exec - initializing\n"); if (time_to_kill) initialize_kill(); return 0; } inline static int w_exec_dset(struct sip_msg* msg, char* cmd, char* foo) { str *uri; environment_t *backup; int ret; str command; if(msg==0 || cmd==0) return -1; backup=0; if (setvars) { backup=set_env(msg); if (!backup) { LM_ERR("no env created\n"); return -1; } } if (msg->new_uri.s && msg->new_uri.len) uri=&msg->new_uri; else uri=&msg->first_line.u.request.uri; if(fixup_get_svalue(msg, (gparam_p)cmd, &command)!=0) { LM_ERR("invalid command parameter"); return -1; } LM_DBG("executing [%s]\n", command.s); ret=exec_str(msg, command.s, uri->s, uri->len); if (setvars) { unset_env(backup); } return ret; } inline static int w_exec_msg(struct sip_msg* msg, char* cmd, char* foo) { environment_t *backup; int ret; str command; if(msg==0 || cmd==0) return -1; backup=0; if (setvars) { backup=set_env(msg); if (!backup) { LM_ERR("no env created\n"); return -1; } } if(fixup_get_svalue(msg, (gparam_p)cmd, &command)!=0) { LM_ERR("invalid command parameter"); return -1; } LM_DBG("executing [%s]\n", command.s); ret=exec_msg(msg, command.s); if (setvars) { unset_env(backup); } return ret; } inline static int w_exec_avp(struct sip_msg* msg, char* cmd, char* avpl) { environment_t *backup; int ret; str command; if(msg==0 || cmd==0) return -1; backup=0; if (setvars) { backup=set_env(msg); if (!backup) { LM_ERR("no env created\n"); return -1; } } if(fixup_get_svalue(msg, (gparam_p)cmd, &command)!=0) { LM_ERR("invalid command parameter"); return -1; } LM_DBG("executing [%s]\n", command.s); ret=exec_avp(msg, command.s, (pvname_list_p)avpl); if (setvars) { unset_env(backup); } return ret; } inline static int w_exec_getenv(struct sip_msg* msg, char* cmd, char* avpl) { str command; if(msg==0 || cmd==0) return -1; if(fixup_get_svalue(msg, (gparam_p)cmd, &command)!=0) { LM_ERR("invalid command parameter"); return -1; } LM_DBG("executing getenv [%s]\n", command.s); return exec_getenv(msg, command.s, (pvname_list_p)avpl); } static int exec_avp_fixup(void** param, int param_no) { pvname_list_t *anlist = NULL; str s; s.s = (char*)(*param); if (param_no==1) { LM_WARN("You are using an obosolete function from the EXEC module!" "Please switch to the new exec() function\n"); if(s.s==NULL) { LM_ERR("null format in P%d\n", param_no); return E_UNSPEC; } return fixup_spve_null(param, 1); } else if(param_no==2) { if(s.s==NULL) { LM_ERR("null format in P%d\n", param_no); return E_UNSPEC; } s.len = strlen(s.s); anlist = parse_pvname_list(&s, PVT_AVP); if(anlist==NULL) { LM_ERR("bad format in P%d [%s]\n", param_no, s.s); return E_UNSPEC; } *param = (void*)anlist; return 0; } return 0; } static int exec_fixup(void** param, int param_no) { gparam_p out_var; pv_elem_t* model; str s; if (*param) switch (param_no) { case 1: /* cmd */ return fixup_spve(param); case 2: /* input vars */ s.s = *param; s.len = strlen(s.s); if (pv_parse_format(&s, &model)) { LM_ERR("wrong format [%s] for param no %d!\n", (char*)*param, param_no); pkg_free(s.s); return E_UNSPEC; } *param = (void *)model; return 0; case 3: /* output var */ case 4: /* error var */ if (fixup_spve(param)) { LM_ERR("cannot fix output var\n"); return -1; } out_var = *param; if (out_var->type != GPARAM_TYPE_PVE) { LM_ERR("output var must be a single variable\n"); return -1; } if (out_var->v.pve->spec.setf == NULL) { LM_ERR("output var must be writable\n"); return -1; } return 0; case 5: /* environment avp */ if (fixup_spve(param)) { LM_ERR("cannot fix output var\n"); return -1; } out_var = *param; if (out_var->type != GPARAM_TYPE_PVE) { LM_ERR("env var must be a single variable\n"); return -1; } if (out_var->v.pve->spec.type != PVT_AVP) { LM_ERR("env var must be avp typed\n"); return -1; } return 0; default: LM_ERR("Invalid parameter number %d\n", param_no); return -1; } return 0; } static inline int setenvvar(struct hf_wrapper** hf, int_str* value, int idx) { #define OSIPS_EXEC "OSIPS_EXEC_" int len=0; str sidx; sidx.s = int2str((unsigned long)idx, &sidx.len); (*hf)->envvar=pkg_malloc(strlen(OSIPS_EXEC) + sidx.len + 1/*=*/ + (*value).s.len + 1/*\0*/); if ((*hf)->envvar==0) { LM_ERR("no more pkg mem\n"); return -1; } memcpy((*hf)->envvar, OSIPS_EXEC, strlen(OSIPS_EXEC)); len=strlen(OSIPS_EXEC); memcpy((*hf)->envvar+len, sidx.s, sidx.len); len+=sidx.len; (*hf)->envvar[len++] = '='; memcpy((*hf)->envvar+len, value->s.s, value->s.len); (*hf)->envvar[len+ value->s.len] = '\0'; (*hf)->next_other=(*hf)->next_same=NULL; return 0; #undef OSIPS_EXEC } static struct hf_wrapper* get_avp_values_list(struct sip_msg* msg, pv_param_p avp) { int avp_name, idx=0; unsigned short name_type; int_str value; struct usr_avp* avp_ptr=0; struct hf_wrapper *hf=0, *hf_head; if (pv_get_avp_name(msg, avp, &avp_name, &name_type) < 0) { LM_ERR("cannot get avp name\n"); return 0; } if ((avp_ptr=search_first_avp( name_type, avp_name, &value, 0)) == 0) { LM_ERR("cannot get first avp value\n"); return 0; } hf=pkg_malloc(sizeof(struct hf_wrapper)); if (!hf) goto memerr; setenvvar(&hf, &value, idx++); hf_head=hf; while (search_next_avp( avp_ptr, &value) != 0) { hf->next_other=pkg_malloc(sizeof(struct hf_wrapper)); if (!hf) goto memerr; hf=hf->next_other; setenvvar(&hf, &value, idx++); avp_ptr = avp_ptr->next; if (avp_ptr->id > avp_name) break; } return hf_head; memerr: LM_ERR("no more pkg mem\n"); return 0; } inline static int w_exec(struct sip_msg* msg, char* cmd, char* in, char* out, char* err ,char* avp_env) { str command; str input = {NULL, 0}; int ret; struct hf_wrapper *hf=0; environment_t* backup_env=0; gparam_p outvar = (gparam_p)out; gparam_p errvar = (gparam_p)err; if (msg == 0 || cmd == 0) return -1; /* fetch command */ if(fixup_get_svalue(msg, (gparam_p)cmd, &command)!=0) { LM_ERR("invalid command parameter"); return -1; } /* fetch input */ if (in != NULL) { if (pv_printf_s(msg, (pv_elem_p)in, &input)!=0) return -1; } if (avp_env != NULL) { if ((hf=get_avp_values_list(msg, &((gparam_p)avp_env)->v.pve->spec.pvp)) == 0) return -1; backup_env=replace_env(hf); if (!backup_env) { LM_ERR("replace env failed\n"); release_vars(hf); release_hf_struct(hf); return -1; } release_hf_struct(hf); } ret = exec_sync(msg, &command, &input, outvar, errvar); if (backup_env) unset_env(backup_env); return ret; } inline static int w_async_exec(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char* cmd, char* in, char* out, char* err, char* avp_env) { str command; str input = {NULL, 0}; struct hf_wrapper *hf=0; environment_t* backup_env=0; gparam_p outvar = (gparam_p)out; exec_async_param *param; int ret, fd; if (msg == 0 || cmd == 0) return -1; /* fetch command */ if(fixup_get_svalue(msg, (gparam_p)cmd, &command)!=0) { LM_ERR("invalid command parameter\n"); return -1; } /* fetch input */ if (in != NULL) { if (pv_printf_s(msg, (pv_elem_p)in, &input)!=0) return -1; } if (avp_env != NULL) { if ((hf=get_avp_values_list(msg, &((gparam_p)avp_env)->v.pve->spec.pvp)) == 0) return -1; backup_env=replace_env(hf); if (!backup_env) { LM_ERR("replace env failed\n"); release_vars(hf); release_hf_struct(hf); return -1; } release_hf_struct(hf); } /* better do this alloc now (before starting the async) to avoid * the unplesant situation of having the async started and have a * memory failure -> tricky to recover */ param = (exec_async_param*)shm_malloc(sizeof(exec_async_param)); if(param==NULL) { LM_ERR("failed to allocate new async param\n"); if (backup_env) unset_env(backup_env); return -1; } ret = start_async_exec(msg, &command, in?&input:NULL, outvar, &fd); if (backup_env) unset_env(backup_env); /* populate resume point (if async started) */ if (ret==1) { param->outvar = outvar; /* that ^^^^ is save as "out" is a in private mem, but in all * processes (set before forking) */ param->buf = NULL; *resume_param = (void*)param; *resume_f = resume_async_exec; async_status = fd; } else if (ret==2) { /* no IO done, but success */ shm_free(param); *resume_param = NULL; *resume_f = NULL; async_status = ASYNC_NO_IO; } else { /* error */ shm_free(param); *resume_param = NULL; *resume_f = NULL; async_status = ASYNC_NO_IO; } return ret; } opensips-2.2.2/modules/exec/kill.c000066400000000000000000000134071300170765700170630ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 changed to the new locking scheme: locking.h (andrei) * * * in this file, we implement the ability to send a kill signal to * a child after some time; its a quick ugly hack, for example kill * is sent without any knowledge whether the kid is still alive * * also, it was never compiled without FAST_LOCK -- nothing will * work if you turn it off * * there is also an ugly s/HACK * * and last but not least -- we don't know the child pid (we use popen) * so we cannot close anyway * * */ #include #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../timer.h" #include "../../locking.h" #include "kill.h" static gen_lock_t *kill_lock=NULL; static struct timer_list *kill_list; #define lock() lock_get(kill_lock) #define unlock() lock_release(kill_lock) /* copy and paste from TM -- might consider putting in better in some utils part of core */ static void timer_routine(unsigned int ticks , void * attr) { struct timer_link *tl, *tmp_tl, *end, *ret; int killr; UNUSED(killr); /* check if it worth entering the lock */ if (kill_list->first_tl.next_tl==&kill_list->last_tl || kill_list->first_tl.next_tl->time_out > ticks ) return; lock(); end = &kill_list->last_tl; tl = kill_list->first_tl.next_tl; while( tl!=end && tl->time_out <= ticks ) { tl=tl->next_tl; } /* nothing to delete found */ if (tl->prev_tl==&kill_list->first_tl) { unlock(); return; } /* the detached list begins with current beginning */ ret = kill_list->first_tl.next_tl; /* and we mark the end of the split list */ tl->prev_tl->next_tl = 0; /* the shortened list starts from where we suspended */ kill_list->first_tl.next_tl = tl; tl->prev_tl=&kill_list->first_tl; unlock(); /* process the list now */ while (ret) { tmp_tl=ret->next_tl; ret->next_tl=ret->prev_tl=0; if (ret->time_out>0) { LM_INFO("exec timeout, pid %d -> sending SIGTERM\n", ret->pid); killr=kill(ret->pid, SIGTERM); LM_DBG("child process (%d) kill status: %d\n", ret->pid, killr ); } shm_free(ret); ret=tmp_tl; } } pid_t __popen3(const char* cmd, FILE** strm_w, FILE** strm_r, FILE** strm_e) { #define OPEN_PIPE(strm, fds) \ do { \ if (strm) { \ if (pipe(fds) != 0) { \ LM_ERR("failed to create reading pipe (%d: %s)\n", \ errno, strerror(errno)); \ return -1; \ } \ } \ } while (0); /* * cl - pipe end to be closed * op - pipe end to be redirected * re - fds where to redirect 'op' */ #define CLOSE_AND_REDIRECT(strm, fds, cl, op, re) \ do { \ if (strm) { \ close(fds[cl]); \ dup2(fds[op], re); \ close(fds[op]); \ } \ } while (0); #define CLOSE_END_AND_OPEN_STREAM(strm, way, fds, end2close) \ do { \ if (strm) { \ close(fds[end2close]); \ *strm = fdopen(fds[(1^end2close)], way); \ } \ }while (0); pid_t ret; int r_fds[2], w_fds[2], e_fds[2]; if (strm_r == NULL && strm_w == NULL && strm_e == NULL) { LM_WARN("no descriptor redirect required\n"); } OPEN_PIPE(strm_w, w_fds); OPEN_PIPE(strm_r, r_fds); OPEN_PIPE(strm_e, e_fds); ret=fork(); if (ret==0) { /* write pipe */ CLOSE_AND_REDIRECT(strm_w, w_fds, 1, 0 ,0); /* read pipe */ CLOSE_AND_REDIRECT(strm_r, r_fds, 0, 1, 1); /* error pipe */ CLOSE_AND_REDIRECT(strm_e, e_fds, 0, 1, 2); execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); exit(-1); } CLOSE_END_AND_OPEN_STREAM(strm_w, "w", w_fds, 0); CLOSE_END_AND_OPEN_STREAM(strm_r, "r", r_fds, 1); CLOSE_END_AND_OPEN_STREAM(strm_e, "r", e_fds, 1); return ret; #undef OPEN_PIPE #undef CLOSE_AND_REDIRECT #undef CLOSE_AND_OPEN_STREAM } int schedule_to_kill( int pid ) { struct timer_link *tl; if (time_to_kill <= 0) return 0; tl = shm_malloc(sizeof *tl); if (!tl) { LM_ERR("no shmem\n"); return -1; } memset(tl, 0, sizeof *tl); lock(); tl->pid=pid; tl->time_out=get_ticks()+time_to_kill; tl->prev_tl = kill_list->last_tl.prev_tl; tl->next_tl = &kill_list->last_tl; kill_list->last_tl.prev_tl=tl; tl->prev_tl->next_tl=tl; unlock(); return 0; } int initialize_kill(void) { /* if disabled ... */ if (time_to_kill == 0) return 0; if (register_timer("exec_kill", timer_routine, NULL /* param */, 1 /* period */, TIMER_FLAG_SKIP_ON_DELAY) < 0) { LM_ERR("no exec timer registered\n"); return -1; } kill_list = shm_malloc(sizeof *kill_list); if (!kill_list) { LM_ERR("no more shm!\n"); return -1; } kill_list->first_tl.next_tl = &kill_list->last_tl; kill_list->last_tl.prev_tl = &kill_list->first_tl; kill_list->first_tl.prev_tl = kill_list->last_tl.next_tl = NULL; kill_list->last_tl.time_out = -1; kill_lock = lock_alloc(); if (!kill_lock) { LM_ERR("no shm mem for mutex\n"); return -1; } lock_init(kill_lock); LM_DBG("kill initialized\n"); return 0; } void destroy_kill(void) { /* if disabled ... */ if (time_to_kill==0) return; if (kill_lock) { lock_destroy(kill_lock); lock_dealloc(kill_lock); } } opensips-2.2.2/modules/exec/kill.h000066400000000000000000000027171300170765700170720ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _KILL_H #define _KILL_H struct timer_link { struct timer_link *next_tl; struct timer_link *prev_tl; volatile unsigned int time_out; int pid; }; struct timer_list { struct timer_link first_tl; struct timer_link last_tl; }; extern unsigned int time_to_kill; void destroy_kill(); int initialize_kill(); int schedule_to_kill( int pid ); /** * __popen3 - a wrapper function over execl * * @cmd: the command string to be executed * @strm_w: stream to STDIN of cmd process * @strm_r: stream to STDOUT of cmd process * @strm_w: stream to STDERR of cmd process */ pid_t __popen3(const char *cmd, FILE **strm_w, FILE** strm_r, FILE** strm_e); #endif opensips-2.2.2/modules/fraud_detection/000077500000000000000000000000001300170765700201725ustar00rootroot00000000000000opensips-2.2.2/modules/fraud_detection/Makefile000066400000000000000000000003111300170765700216250ustar00rootroot00000000000000# New module name # #- # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=fraud_detection.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/fraud_detection/README000066400000000000000000000313071300170765700210560ustar00rootroot00000000000000Fraud Detection Module Andrei Daniel Datcu Copyright © 2014 OpenSIPs Foundation Revision History Revision $Revision: 1 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Monitorized Stats 1.1.2. Fraud rules 1.2. Dependencies 1.2.1. OpenSIPS modules 1.2.2. External libraries or applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. table_name (string) 1.3.3. rid_col (string) 1.3.4. pid_col (string) 1.3.5. prefix_col (string) 1.3.6. start_h (string) 1.3.7. end_h (string) 1.3.8. days_col (string) 1.3.9. cpm_thresh_warn_col (string) 1.3.10. cpm_thresh_crit_col (string) 1.3.11. calldur_thresh_warn_col (string) 1.3.12. calldur_thresh_crit_col (string) 1.3.13. totalc_thresh_warn_col (string) 1.3.14. totalc_thresh_crit_col (string) 1.3.15. concalls_thresh_warn_col (string) 1.3.16. concalls_thresh_crit_col (string) 1.3.17. seqcalls_thresh_warn_col (string) 1.3.18. seqcalls_thresh_crit_col (string) 1.4. Exported Functions 1.4.1. check_fraud(user, number, profile_id) 1.5. Exported MI Functions 1.5.1. show_fraud_stats 1.5.2. fraud_reload 1.6. Exported Events 1.6.1. E_FRD_WARNING 1.6.2. E_FRD_CRITICAL List of Examples 1.1. Set the “db_url†parameter 1.2. Set the “table_name†parameter 1.3. Set “rid_col†parameter 1.4. Set “pid_col†parameter 1.5. Set “prefix_col†parameter 1.6. Set “start_h†parameter 1.7. Set “end_h†parameter 1.8. Set “days_col†parameter 1.9. Set “cpm_thresh_warn_col†parameter 1.10. Set “cpm_thresh_crit_col†parameter 1.11. Set “calldur_thresh_warn_col†parameter 1.12. Set “calldur_thresh_crit_col†parameter 1.13. Set “totalc_thresh_warn_col†parameter 1.14. Set “totalc_thresh_crit_col†parameter 1.15. Set “concalls_thresh_warn_col†parameter 1.16. Set “concalls_thresh_crit_col†parameter 1.17. Set “seqcalls_thresh_warn_col†parameter 1.18. Set “seqcalls_thresh_crit_col†parameter Chapter 1. Admin Guide 1.1. Overview This module provides a way to prevent some basic fraud attacks. Alerts are provided through return codes and events. 1.1.1. Monitorized Stats Basically, this module watches the following parameters: * Total calls * Calls per minute * Concurrent calls * Number of sequential calls * Call duration Each of the above parameters is monitored for every user and every called prefix separately. The stats are altered whenever the check_fraud function is called. The function assumes a new call is made, and checks the called number against all the rules from the supplied profile. The rule's prefix is considered to be the called prefix which along with the provided user will be used to monitor values for the 5 parameters. 1.1.2. Fraud rules A rule is a set of two thresholds (warning and critical thresholds) for each of the five parameters (as described above) and is only available for a specified prefix. Further more, a rule will only match between the indicated hours in the indicated days of the week (similarly to a dr rule). A fraud profile is simply a group of fraud rules and is used to only to limit the list of rules to match when calling the check_fraud function. 1.2. Dependencies 1.2.1. OpenSIPS modules The following modules must be loaded before this module: * drouting * dialog 1.2.2. External libraries or applications The following libraries or applications must be installed before running OpenSIPS with this module: * none. 1.3. Exported Parameters 1.3.1. db_url (string) Database where to load the rules from. Default value is “NULLâ€. At least one db_url should be defined for the fraud_detection module to work. Example 1.1. Set the “db_url†parameter ... modparam("fraud_detection", "db_url", "mysql://user:passwb@localhost/dat abase") ... 1.3.2. table_name (string) If you want to load the rules from the database you must set this parameter as the database name. The default value is “fraud_detectionâ€. Example 1.2. Set the “table_name†parameter ... modparam("fraud_detection", "table_name", "my_fraud") ... 1.3.3. rid_col (string) The column's name in the database storing the fraud rule's id. Default value is “ruleidâ€. Example 1.3. Set “rid_col†parameter ... modparam("fraud_detection", "rid_col", "theruleid"") ... 1.3.4. pid_col (string) The column's name in the database storing the fraud profile's id. Please keep in mind that a profile is merely a set of rules. Default value is “profileidâ€. Example 1.4. Set “pid_col†parameter ... modparam("fraud_detection", "pid_col", "profile"") ... 1.3.5. prefix_col (string) The column's name in the database storing the prefix for which the fraud rule will match. Default value is “prefixâ€. Example 1.5. Set “prefix_col†parameter ... modparam("fraud_detection", "prefix_col", "myprefix") ... 1.3.6. start_h (string) The column's name in the database storing the the start time of the interval in which the rule will match. The time needs to be specified as string using the format: “HH:MM†Default value is “start_hourâ€. Example 1.6. Set “start_h†parameter ... modparam("fraud_detection", "start_h", "the_start_time") ... 1.3.7. end_h (string) The column's name in the database storing the the end time of the interval in which the rule will match. The time needs to be specified as string using the format: “HH:MM†Default value is “end_hourâ€. Example 1.7. Set “end_h†parameter ... modparam("fraud_detection", "end_h", "the_end_time") ... 1.3.8. days_col (string) The column's name in the database storing the week days in which the fraud rule's interval is available. The daysoftheweek needs to be specified as a string containing a list of days or intervals. Each day must be specified using the first three letters of its name. A valid string would be: "Fri-Mon, Wed, Thu" Default value is “daysoftheweekâ€. Example 1.8. Set “days_col†parameter ... modparam("fraud_detection", "days_col", "days") ... 1.3.9. cpm_thresh_warn_col (string) The column's name in the database storing the warning threshold value for calls per minute. Default value is “cpm_warningâ€. Example 1.9. Set “cpm_thresh_warn_col†parameter ... modparam("fraud_detection", "cpm_thresh_warn_col", "cpm_warn_thresh") ... 1.3.10. cpm_thresh_crit_col (string) The column's name in the database storing the critical threshold value for calls per minute. Default value is “cpm_criticalâ€. Example 1.10. Set “cpm_thresh_crit_col†parameter ... modparam("fraud_detection", "cpm_thresh_crit_col", "cpm_crit_thresh") ... 1.3.11. calldur_thresh_warn_col (string) The column's name in the database storing the warning threshold value for call duration. Default value is “call_duration_warningâ€. Example 1.11. Set “calldur_thresh_warn_col†parameter ... modparam("fraud_detection", "calldur_thresh_warn_col", "calldur_warn_thr esh") ... 1.3.12. calldur_thresh_crit_col (string) The column's name in the database storing the critical threshold value for call duration. Default value is “call_duration_criticalâ€. Example 1.12. Set “calldur_thresh_crit_col†parameter ... modparam("fraud_detection", "calldur_thresh_crit_col", "calldur_crit_thr esh") ... 1.3.13. totalc_thresh_warn_col (string) The column's name in the database storing the warning threshold value for the number of total calls. Default value is “total_calls_warningâ€. Example 1.13. Set “totalc_thresh_warn_col†parameter ... modparam("fraud_detection", "totalc_thresh_warn_col", "totalc_warn_thres h") ... 1.3.14. totalc_thresh_crit_col (string) The column's name in the database storing the critical threshold value for the number of total calls. Default value is “total_calls_criticalâ€. Example 1.14. Set “totalc_thresh_crit_col†parameter ... modparam("fraud_detection", "totalc_thresh_crit_col", "totalc_crit_thres h") ... 1.3.15. concalls_thresh_warn_col (string) The column's name in the database storing the warning threshold value for the number of concurrent calls. Default value is “concurrent_calls_warningâ€. Example 1.15. Set “concalls_thresh_warn_col†parameter ... modparam("fraud_detection", "concalls_thresh_warn_col", "concalls_warn_t hresh") ... 1.3.16. concalls_thresh_crit_col (string) The column's name in the database storing the critical threshold value for the number of concurrent calls. Default value is “concurrent_calls_criticalâ€. Example 1.16. Set “concalls_thresh_crit_col†parameter ... modparam("fraud_detection", "concalls_thresh_crit_col", "concalls_crit_t hresh") ... 1.3.17. seqcalls_thresh_warn_col (string) The column's name in the database storing the warning threshold value for the number of sequential calls. Default value is “sequential_calls_warningâ€. Example 1.17. Set “seqcalls_thresh_warn_col†parameter ... modparam("fraud_detection", "seqcalls_thresh_warn_col", "seqcalls_warn_t hresh") ... 1.3.18. seqcalls_thresh_crit_col (string) The column's name in the database storing the critical threshold value for the number of sequential calls. Default value is “sequential_calls_criticalâ€. Example 1.18. Set “seqcalls_thresh_crit_col†parameter ... modparam("fraud_detection", "seqcalls_thresh_crit_col", "seqcalls_crit_t hresh") ... 1.4. Exported Functions 1.4.1. check_fraud(user, number, profile_id) This method should be called each time a given user calls a given number. It will try to match a fraud rule within de given fraud profile and update the stats (see above). Furthermore, the stats will be checked against the rule's thresholds. If any of the stats is above it's threhsold value the appropriate event will also be raised (see further details below). Meaning of the parameters is as follows: * user - the user who is making the call. Please keep in mind that the user doesn't have to be registered. This string is only used do keep different stats for different registered users. * number - the number the user is calling to. * profile_id - the fraud profile id (i.e. the subset of fraud rules) in which to try and find a matching fraud rule. The meaning of the return code is as follows: * 2 - no matching fraud rule was found * 1 - a matching rule was found, but there is no parameter above the rule's threshlod, i.e - everything is ok * -1 - there is a parameter above the warning threhsold value. Check the raised event for more info * -2 - there is a parameter above the critical threhsold value. Check the raised event for more info * -3 - something went wrong (internal mechanism failed) This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. 1.5. Exported MI Functions 1.5.1. show_fraud_stats Show the current statistics for a user to a given prefix and fraud profile. Name: show_fraud_stats Parameters: * user * prefix * fraud_profile 1.5.2. fraud_reload Reload the all the fraud rules. Name: fraud_reload Parameters: none 1.6. Exported Events 1.6.1. E_FRD_WARNING This event is raised whenever one of the 5 monitored parameters is above the warning threhsold value Parameters: * param - the name of the parameter. * value - the current value of the parameter. * threshold - the warning threshold value. * user - the user who initiated the call. * called_number - the number that was called. * rule_id - the id of the fraud rule that matched when the call was initiated 1.6.2. E_FRD_CRITICAL This event is raised whenever one of the 5 monitored parameters is above the warning threhsold value Parameters: * param - the name of the parameter. * value - the current value of the parameter. * threshold - the warning threshold value. * user - the user who initiated the call. * called_number - the number that was called. * rule_id - the id of the fraud rule that matched when the call was initiated opensips-2.2.2/modules/fraud_detection/doc/000077500000000000000000000000001300170765700207375ustar00rootroot00000000000000opensips-2.2.2/modules/fraud_detection/doc/fraud_detection.xml000066400000000000000000000015371300170765700246260ustar00rootroot00000000000000 %docentities; ]> Fraud Detection Module &osipsname; Andrei Daniel Datcu datcuandrei@gmail.com 2014 OpenSIPs Foundation $Revision: 1 $ $Date$ &admin; opensips-2.2.2/modules/fraud_detection/doc/fraud_detection_admin.xml000066400000000000000000000423741300170765700260020ustar00rootroot00000000000000 &adminguide;
Overview This module provides a way to prevent some basic fraud attacks. Alerts are provided through return codes and events.
Monitorized Stats Basically, this module watches the following parameters: Total calls Calls per minute Concurrent calls Number of sequential calls Call duration Each of the above parameters is monitored for every user and every called prefix separately. The stats are altered whenever the check_fraud function is called. The function assumes a new call is made, and checks the called number against all the rules from the supplied profile. The rule's prefix is considered to be the called prefix which along with the provided user will be used to monitor values for the 5 parameters.
Fraud rules A rule is a set of two thresholds (warning and critical thresholds) for each of the five parameters (as described above) and is only available for a specified prefix. Further more, a rule will only match between the indicated hours in the indicated days of the week (similarly to a dr rule). A fraud profile is simply a group of fraud rules and is used to only to limit the list of rules to match when calling the check_fraud function.
Dependencies
&osips; modules The following modules must be loaded before this module: drouting dialog
External libraries or applications The following libraries or applications must be installed before running &osips; with this module: none.
Exported Parameters
<varname>db_url</varname> (string) Database where to load the rules from. Default value is NULL. At least one db_url should be defined for the fraud_detection module to work. Set the <quote>db_url</quote> parameter ... modparam("fraud_detection", "db_url", "mysql://user:passwb@localhost/database") ...
<varname>table_name</varname> (string) If you want to load the rules from the database you must set this parameter as the database name. The default value is fraud_detection. Set the <quote>table_name</quote> parameter ... modparam("fraud_detection", "table_name", "my_fraud") ...
<varname>rid_col</varname> (string) The column's name in the database storing the fraud rule's id. Default value is ruleid. Set <quote>rid_col</quote> parameter ... modparam("fraud_detection", "rid_col", "theruleid"") ...
<varname>pid_col</varname> (string) The column's name in the database storing the fraud profile's id. Please keep in mind that a profile is merely a set of rules. Default value is profileid. Set <quote>pid_col</quote> parameter ... modparam("fraud_detection", "pid_col", "profile"") ...
<varname>prefix_col</varname> (string) The column's name in the database storing the prefix for which the fraud rule will match. Default value is prefix. Set <quote>prefix_col</quote> parameter ... modparam("fraud_detection", "prefix_col", "myprefix") ...
<varname>start_h</varname> (string) The column's name in the database storing the the start time of the interval in which the rule will match. The time needs to be specified as string using the format: HH:MM Default value is start_hour. Set <quote>start_h</quote> parameter ... modparam("fraud_detection", "start_h", "the_start_time") ...
<varname>end_h</varname> (string) The column's name in the database storing the the end time of the interval in which the rule will match. The time needs to be specified as string using the format: HH:MM Default value is end_hour. Set <quote>end_h</quote> parameter ... modparam("fraud_detection", "end_h", "the_end_time") ...
<varname>days_col</varname> (string) The column's name in the database storing the week days in which the fraud rule's interval is available. The daysoftheweek needs to be specified as a string containing a list of days or intervals. Each day must be specified using the first three letters of its name. A valid string would be: "Fri-Mon, Wed, Thu" Default value is daysoftheweek. Set <quote>days_col</quote> parameter ... modparam("fraud_detection", "days_col", "days") ...
<varname>cpm_thresh_warn_col</varname> (string) The column's name in the database storing the warning threshold value for calls per minute. Default value is cpm_warning. Set <quote>cpm_thresh_warn_col</quote> parameter ... modparam("fraud_detection", "cpm_thresh_warn_col", "cpm_warn_thresh") ...
<varname>cpm_thresh_crit_col</varname> (string) The column's name in the database storing the critical threshold value for calls per minute. Default value is cpm_critical. Set <quote>cpm_thresh_crit_col</quote> parameter ... modparam("fraud_detection", "cpm_thresh_crit_col", "cpm_crit_thresh") ...
<varname>calldur_thresh_warn_col</varname> (string) The column's name in the database storing the warning threshold value for call duration. Default value is call_duration_warning. Set <quote>calldur_thresh_warn_col</quote> parameter ... modparam("fraud_detection", "calldur_thresh_warn_col", "calldur_warn_thresh") ...
<varname>calldur_thresh_crit_col</varname> (string) The column's name in the database storing the critical threshold value for call duration. Default value is call_duration_critical. Set <quote>calldur_thresh_crit_col</quote> parameter ... modparam("fraud_detection", "calldur_thresh_crit_col", "calldur_crit_thresh") ...
<varname>totalc_thresh_warn_col</varname> (string) The column's name in the database storing the warning threshold value for the number of total calls. Default value is total_calls_warning. Set <quote>totalc_thresh_warn_col</quote> parameter ... modparam("fraud_detection", "totalc_thresh_warn_col", "totalc_warn_thresh") ...
<varname>totalc_thresh_crit_col</varname> (string) The column's name in the database storing the critical threshold value for the number of total calls. Default value is total_calls_critical. Set <quote>totalc_thresh_crit_col</quote> parameter ... modparam("fraud_detection", "totalc_thresh_crit_col", "totalc_crit_thresh") ...
<varname>concalls_thresh_warn_col</varname> (string) The column's name in the database storing the warning threshold value for the number of concurrent calls. Default value is concurrent_calls_warning. Set <quote>concalls_thresh_warn_col</quote> parameter ... modparam("fraud_detection", "concalls_thresh_warn_col", "concalls_warn_thresh") ...
<varname>concalls_thresh_crit_col</varname> (string) The column's name in the database storing the critical threshold value for the number of concurrent calls. Default value is concurrent_calls_critical. Set <quote>concalls_thresh_crit_col</quote> parameter ... modparam("fraud_detection", "concalls_thresh_crit_col", "concalls_crit_thresh") ...
<varname>seqcalls_thresh_warn_col</varname> (string) The column's name in the database storing the warning threshold value for the number of sequential calls. Default value is sequential_calls_warning. Set <quote>seqcalls_thresh_warn_col</quote> parameter ... modparam("fraud_detection", "seqcalls_thresh_warn_col", "seqcalls_warn_thresh") ...
<varname>seqcalls_thresh_crit_col</varname> (string) The column's name in the database storing the critical threshold value for the number of sequential calls. Default value is sequential_calls_critical. Set <quote>seqcalls_thresh_crit_col</quote> parameter ... modparam("fraud_detection", "seqcalls_thresh_crit_col", "seqcalls_crit_thresh") ...
Exported Functions
<function moreinfo="none">check_fraud(user, number, profile_id)</function> This method should be called each time a given user calls a given number. It will try to match a fraud rule within de given fraud profile and update the stats (see above). Furthermore, the stats will be checked against the rule's thresholds. If any of the stats is above it's threhsold value the appropriate event will also be raised (see further details below). Meaning of the parameters is as follows: user - the user who is making the call. Please keep in mind that the user doesn't have to be registered. This string is only used do keep different stats for different registered users. number - the number the user is calling to. profile_id - the fraud profile id (i.e. the subset of fraud rules) in which to try and find a matching fraud rule. The meaning of the return code is as follows: 2 - no matching fraud rule was found 1 - a matching rule was found, but there is no parameter above the rule's threshlod, i.e - everything is ok -1 - there is a parameter above the warning threhsold value. Check the raised event for more info -2 - there is a parameter above the critical threhsold value. Check the raised event for more info -3 - something went wrong (internal mechanism failed) This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE.
Exported MI Functions
<function moreinfo="none">show_fraud_stats</function> Show the current statistics for a user to a given prefix and fraud profile. Name: show_fraud_stats Parameters: user prefix fraud_profile
<function moreinfo="none">fraud_reload</function> Reload the all the fraud rules. Name: fraud_reload Parameters: none
Exported Events
<function moreinfo="none">E_FRD_WARNING</function> This event is raised whenever one of the 5 monitored parameters is above the warning threhsold value Parameters: param - the name of the parameter. value - the current value of the parameter. threshold - the warning threshold value. user - the user who initiated the call. called_number - the number that was called. rule_id - the id of the fraud rule that matched when the call was initiated
<function moreinfo="none">E_FRD_CRITICAL</function> This event is raised whenever one of the 5 monitored parameters is above the warning threhsold value Parameters: param - the name of the parameter. value - the current value of the parameter. threshold - the warning threshold value. user - the user who initiated the call. called_number - the number that was called. rule_id - the id of the fraud rule that matched when the call was initiated
opensips-2.2.2/modules/fraud_detection/fraud_detection.c000066400000000000000000000363131300170765700235030ustar00rootroot00000000000000/** * Fraud Detection Module * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-09-26 initial version (Andrei Datcu) */ #include "../../ut.h" #include "../../db/db.h" #include "../../time_rec.h" #include "../../mod_fix.h" #include "../drouting/dr_api.h" #include "../dialog/dlg_load.h" #include "frd_stats.h" #include "frd_load.h" #include "frd_events.h" extern str db_url; extern str table_name; extern str rid_col; extern str pid_col; extern str prefix_col; extern str start_h_col; extern str end_h_col; extern str days_col; extern str cpm_thresh_warn_col; extern str cpm_thresh_crit_col; extern str calldur_thresh_warn_col; extern str calldur_thresh_crit_col; extern str totalc_thresh_warn_col; extern str totalc_thresh_crit_col; extern str concalls_thresh_warn_col; extern str concalls_thresh_crit_col; extern str seqcalls_thresh_warn_col; extern str seqcalls_thresh_crit_col; #define DEF_PARAM_STR_NAME(pname, strname)\ static str pname ## _name = str_init(strname) DEF_PARAM_STR_NAME(cpm, "calls per minute"); DEF_PARAM_STR_NAME(total_calls, "total calls"); DEF_PARAM_STR_NAME(concurrent_calls, "concurrent calls"); DEF_PARAM_STR_NAME(seq_calls, "sequential calls"); #undef DEF_PARAM_STR_NAME dr_head_p *dr_head; struct dr_binds drb; rw_lock_t *frd_data_lock; gen_lock_t *frd_seq_calls_lock; struct dlg_binds dlgb; static int mod_init(void); static int child_init(int); static void destroy(void); static int check_fraud(struct sip_msg *msg, char *user, char *number, char *pid); static int fixup_check_fraud(void **param, int param_no); static struct mi_root* mi_show_stats(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_reload(struct mi_root *cmd_tree, void *param); static cmd_export_t cmds[]={ {"check_fraud", (cmd_function)check_fraud, 3, fixup_check_fraud, 0, REQUEST_ROUTE | ONREPLY_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[]={ {"db_url", STR_PARAM, &db_url.s}, {"table_name", STR_PARAM, &table_name.s}, {"rid_col", STR_PARAM, &rid_col.s}, {"pid_col", STR_PARAM, &pid_col.s}, {"prefix_col", STR_PARAM, &prefix_col.s}, {"start_h_col", STR_PARAM, &start_h_col.s}, {"end_h_col", STR_PARAM, &end_h_col.s}, {"days_col", STR_PARAM, &days_col.s}, {"cpm_thresh_warn_col", STR_PARAM, &cpm_thresh_warn_col.s}, {"cpm_thresh_crit_col", STR_PARAM, &cpm_thresh_crit_col.s}, {"calldur_thresh_warn_col", STR_PARAM, &calldur_thresh_warn_col.s}, {"calldur_thresh_crit_col", STR_PARAM, &calldur_thresh_crit_col.s}, {"totalc_thresh_warn_col", STR_PARAM, &totalc_thresh_warn_col.s}, {"totalc_thresh_crit_col", STR_PARAM, &totalc_thresh_crit_col.s}, {"concalls_thresh_warn_col", STR_PARAM, &concalls_thresh_warn_col.s}, {"concalls_thresh_crit_col", STR_PARAM, &concalls_thresh_crit_col.s}, {"seqcalls_thresh_warn_col", STR_PARAM, &seqcalls_thresh_warn_col.s}, {"seqcalls_thresh_crit_col", STR_PARAM, &seqcalls_thresh_crit_col.s}, {0,0,0} }; static mi_export_t mi_cmds[] = { //{ "get_maps","return all mappings",mi_get_maps,MI_NO_INPUT_FLAG,0,0}, {"show_fraud_stats", "print current stats for a particular user", mi_show_stats, 0, 0, 0}, {"fraud_reload", "reload fraud profiles from db", mi_reload, 0, 0, 0}, {0,0,0,0,0,0} }; static dep_export_t deps = { { {MOD_TYPE_SQLDB, NULL, DEP_ABORT}, {MOD_TYPE_DEFAULT, "drouting", DEP_ABORT}, {MOD_TYPE_DEFAULT, "dialog", DEP_ABORT}, {MOD_TYPE_NULL, NULL, 0}, }, { {NULL, NULL}, }, }; /** module exports */ struct module_exports exports= { "fraud_detection", /* module name */ MOD_TYPE_DEFAULT, MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function)destroy, /* destroy function */ child_init /* per-child init function */ }; static void set_lengths(void) { table_name.len = strlen(table_name.s); rid_col.len = strlen(rid_col.s); pid_col.len = strlen(pid_col.s); prefix_col.len = strlen(prefix_col.s); start_h_col.len = strlen(start_h_col.s); end_h_col.len = strlen(end_h_col.s); days_col.len = strlen(days_col.s); cpm_thresh_warn_col.len = strlen(cpm_thresh_warn_col.s); cpm_thresh_crit_col.len = strlen(cpm_thresh_crit_col.s); calldur_thresh_warn_col.len = strlen(calldur_thresh_warn_col.s); calldur_thresh_crit_col.len = strlen(calldur_thresh_crit_col.s); totalc_thresh_warn_col.len = strlen(totalc_thresh_warn_col.s); totalc_thresh_crit_col.len = strlen(totalc_thresh_crit_col.s); concalls_thresh_warn_col.len = strlen(concalls_thresh_warn_col.s); concalls_thresh_crit_col.len = strlen(concalls_thresh_crit_col.s); seqcalls_thresh_warn_col.len = strlen(seqcalls_thresh_warn_col.s); seqcalls_thresh_crit_col.len = strlen(seqcalls_thresh_crit_col.s); } static int mod_init(void) { LM_INFO("Initializing module\n"); init_db_url(db_url, 0); if ((frd_data_lock = lock_init_rw()) == NULL) { LM_CRIT("failed to init reader/writer lock\n"); return -1; } if ((frd_seq_calls_lock = lock_alloc()) == NULL) { LM_ERR("cannot alloc seq_calls lock\n"); return -1; } if (lock_init(frd_seq_calls_lock) == NULL) { LM_ERR ("cannot init seq_calls lock\n"); return -1; } if (load_dlg_api(&dlgb) != 0) { LM_ERR("failed to load dialog binds\n"); return -1; } if (frd_event_init() != 0) { LM_ERR("cannot register events\n"); return -1; } if (load_dr_api(&drb) != 0) { LM_ERR("cannot load dr_api\n"); return -1; } dr_head = shm_malloc(sizeof(dr_head_p)); if (dr_head == NULL) { LM_ERR("no more shm memory\n"); return -1; } *dr_head = NULL; set_lengths(); if (init_stats_table() != 0) return -1; /* Check if table version is ok */ frd_init_db(); frd_disconnect_db(); return 0; } static int child_init(int rank) { if (rank == 1) { if (frd_connect_db() != 0 || frd_reload_data() != 0) { LM_ERR ("cannot load data from db\n"); return -1; } frd_disconnect_db(); } return 0; } /* * destroy function */ static void destroy(void) { free_stats_table(); frd_destroy_data(); } static int fixup_check_fraud(void **param, int param_no) { switch (param_no) { case 1: case 2: return fixup_spve(param); case 3: return fixup_igp(param); default: LM_CRIT ("Too many parameters for check_fraud\n"); return -1; } } static int check_fraud(struct sip_msg *msg, char *_user, char *_number, char *_pid) { static const int rc_error = -3, rc_critical_thr = -2, rc_warning_thr = -1, rc_ok_thr = 1, rc_no_rule = 2; str user, number; unsigned int pid; frd_dlg_param *param; static str last_called_prefix; extern unsigned int frd_data_rev; if (dr_head == NULL) { /* No data, probably still loading */ LM_ERR("no data\n"); return rc_error; } /* Get the actual params */ if (fixup_get_svalue(msg, (gparam_p) _user, &user) != 0) { LM_ERR("Cannot get user value\n"); return rc_error; } if (fixup_get_svalue(msg, (gparam_p) _number, &number) != 0) { LM_ERR("Cannot get number value\n"); return rc_error; } if (fixup_get_ivalue(msg, (gparam_p)_pid, (int*)&pid) != 0) { LM_ERR("Cannot get the profile-id value\n"); return rc_error; } /* Find a rule */ unsigned int matched_len; lock_start_read(frd_data_lock); rt_info_t *rule = drb.match_number(*dr_head, pid, &number, &matched_len); if (rule == NULL) { /* No match */ LM_DBG("No rule matched for number=<%.*s>, pid=<%d>\n", number.len, number.s, pid); lock_stop_read(frd_data_lock); return rc_no_rule; } /* We matched a rule */ str prefix = number; prefix.len = matched_len; str shm_user; frd_stats_entry_t *se = get_stats(user, prefix, &shm_user); /* Check if we need to reset the stats */ struct tm now, then; time_t nowt = time(NULL); /* We lock all the stats values */ lock_get(&se->lock); if (gmtime_r(&se->stats.last_matched_time, &then) == NULL || gmtime_r(&nowt, &now) == NULL) { LM_ERR ("Cannot use gmtime function. Will exit\n"); lock_release(&se->lock); return rc_ok_thr; } if (se->stats.last_matched_time == 0 || se->stats.last_matched_rule != rule->id || then.tm_yday != now.tm_yday || then.tm_year != now.tm_year) { se->stats.cpm = 0; se->stats.total_calls = 0; se->stats.concurrent_calls = 0; } /* Update the stats */ lock_get(frd_seq_calls_lock); if (last_called_prefix.len == matched_len && memcmp(last_called_prefix.s, number.s, matched_len) == 0) { /* We have called the same number last time */ ++se->stats.seq_calls; } else { last_called_prefix.s = shm_realloc(last_called_prefix.s, matched_len * sizeof(char)); last_called_prefix.len = matched_len; memcpy(last_called_prefix.s, number.s, matched_len); se->stats.seq_calls = 1; } lock_release(frd_seq_calls_lock); se->stats.last_matched_rule = rule->id; ++se->stats.total_calls; /* Calls per FRD_SECS_PER_WINDOW */ if (nowt - se->stats.last_matched_time >= 2 * FRD_SECS_PER_WINDOW) { /* outside the range of t0 + 2*WINDOW_SIZE; we can't use any of the * data since they are too old */ se->stats.cpm = 0; memset(se->stats.calls_window, 0, sizeof(unsigned short) * FRD_SECS_PER_WINDOW); se->stats.calls_window[nowt % FRD_SECS_PER_WINDOW] = 1; se->stats.last_matched_time = nowt; } else if (nowt - se->stats.last_matched_time >= FRD_SECS_PER_WINDOW) { /* more than t0 + WINDOW_SIZE but less than 2 * WINDOW_SIZE * we can consider calls from t0 + (now - WINDOW_SIZE) * all cals from t0 to t0 + (now - WINDOW_SIZE) shall be invalidated */ unsigned int old_matched_time = se->stats.last_matched_time; se->stats.last_matched_time = nowt - FRD_SECS_PER_WINDOW + 1; /*interval [old_last_matched_time; current_last_matched_time) shall * be invalidated */ unsigned int i = (se->stats.last_matched_time - 1) % FRD_SECS_PER_WINDOW; unsigned int j = (old_matched_time - 1) % FRD_SECS_PER_WINDOW; for (;i != j; i = (i - 1 + FRD_SECS_PER_WINDOW) % FRD_SECS_PER_WINDOW) { se->stats.cpm -= se->stats.calls_window[i]; se->stats.calls_window[i] = 0; } se->stats.calls_window[nowt%FRD_SECS_PER_WINDOW]++; } else { /* less than t0 + WINDOW_SIZE; all we need to do is to increase * the number of calls for nowt */ se->stats.calls_window[nowt%FRD_SECS_PER_WINDOW]++; } ++se->stats.cpm; ++se->stats.concurrent_calls; /* Check the thresholds */ int rc = rc_ok_thr; frd_thresholds_t *thr = (frd_thresholds_t*)rule->attrs.s; #define CHECK_AND_RAISE(pname, type) \ (se->stats.pname >= thr->pname ## _thr.type) { \ raise_ ## type ## _event(&pname ## _name, &se->stats.pname,\ &thr->pname ## _thr.type, &user, &number, &rule->id);\ rc = rc_ ## type ## _thr;\ } if CHECK_AND_RAISE(cpm, critical) else if CHECK_AND_RAISE(total_calls, critical) else if CHECK_AND_RAISE(concurrent_calls, critical) else if CHECK_AND_RAISE(seq_calls, critical) else if CHECK_AND_RAISE(cpm, warning) else if CHECK_AND_RAISE(total_calls, warning) else if CHECK_AND_RAISE(concurrent_calls, warning) else if CHECK_AND_RAISE(seq_calls, warning); #undef CHECK_AND_RAISE lock_release(&se->lock); /* Set dialog callback to check call duration */ struct dlg_cell *dlgc = dlgb.get_dlg(); if (dlgc == NULL) { if (dlgb.create_dlg(msg, 0) < 0) { LM_ERR ("cannot create new_dlg\n"); rc = rc_error; } else if ( (dlgc = dlgb.get_dlg()) == NULL) { LM_ERR("cannot get the new dlg\n"); rc = rc_error; } } param = shm_malloc(sizeof(frd_dlg_param)); if (!param) { LM_ERR("no more shm memory"); } else if (shm_str_dup(¶m->number, &number) == 0) { param->stats = se; param->thr = thr; param->user = shm_user; param->ruleid = rule->id; param->data_rev = frd_data_rev; if (dlgb.register_dlgcb(dlgc, DLGCB_TERMINATED|DLGCB_FAILED|DLGCB_EXPIRED, dialog_terminate_CB, param, NULL) != 0) { LM_ERR("failed to register dialog terminated callback\n"); lock_stop_read(frd_data_lock); shm_free(param->number.s); shm_free(param); return rc_error; } } else { shm_free(param); } lock_stop_read(frd_data_lock); return rc; } static struct mi_root* mi_show_stats(struct mi_root *cmd_tree, void *param) { /* User, number, pid */ struct mi_node *node = cmd_tree->node.kids; str user, prefix; unsigned int pid; if (node == NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); user = node->value; node = node->next; if (node == NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); prefix = node->value; node = node->next; if (node == NULL) return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if (str2int(&node->value, &pid) != 0) { LM_WARN("Wrong value for profile id. Token <%.*s>\n", node->value.len, node->value.s); return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } if (!stats_exist(user, prefix)) { LM_WARN("There is no data for user<%.*s> and prefix=<%.*s>\n", user.len, user.s, prefix.len, prefix.s); return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } struct mi_root* rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (rpl_tree == NULL) return 0; rpl_tree->node.flags |= MI_IS_ARRAY; frd_stats_entry_t *se = get_stats(user, prefix, NULL); lock_get(&se->lock); #define ADD_STAT_CHILD(pname, pval) do {\ int val_len;\ char *cval = int2str(pval, &val_len);\ if (add_mi_node_child(&rpl_tree->node, MI_DUP_VALUE,\ pname ## _name.s, pname ## _name.len, cval, val_len) == 0)\ goto add_error;\ } while (0) ADD_STAT_CHILD(cpm, se->stats.cpm); ADD_STAT_CHILD(total_calls, se->stats.total_calls); ADD_STAT_CHILD(concurrent_calls, se->stats.concurrent_calls); ADD_STAT_CHILD(seq_calls, se->stats.seq_calls); #undef ADD_STAT_CHILD lock_release(&se->lock); return rpl_tree; add_error: lock_release(&se->lock); LM_ERR("failed to add node\n"); free_mi_tree(rpl_tree); return 0; } static struct mi_root* mi_reload(struct mi_root *cmd_tree, void *param) { if (frd_connect_db() != 0 || frd_reload_data() != 0) { LM_ERR ("cannot load data from db\n"); return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN); } else { frd_disconnect_db(); return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } } opensips-2.2.2/modules/fraud_detection/frd_events.c000066400000000000000000000114601300170765700224770ustar00rootroot00000000000000/** * Fraud Detection Module * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-09-26 initial version (Andrei Datcu) */ #include "../../evi/evi_params.h" #include "../../evi/evi_modules.h" #include "../dialog/dlg_load.h" #include "frd_events.h" #include "../../mem/shm_mem.h" /* Events name and ids */ static event_id_t ei_warn_id = EVI_ERROR; static event_id_t ei_crit_id = EVI_ERROR; static str ei_warn_name = str_init("E_FRD_WARNING"); static str ei_crit_name = str_init("E_FRD_CRITICAL"); static evi_params_p event_params; /* Events' parameters name and pointers*/ static str ei_param_name = str_init("param"); static str ei_val_name = str_init("value"); static str ei_thr_name = str_init("threshold"); static str ei_user_name = str_init("user"); static str ei_number_name = str_init("called_number"); static str ei_ruleid_name = str_init("rule_id"); static evi_param_p param_p, val_p, thr_p, user_p, number_p, ruleid_p; /* * Function to init the warning and critical events */ int frd_event_init(void) { /* First publish the events */ ei_warn_id = evi_publish_event(ei_warn_name); if (ei_warn_id == EVI_ERROR) { LM_ERR("cannot register warning event\n"); return -1; } ei_crit_id = evi_publish_event(ei_crit_name); if (ei_crit_id == EVI_ERROR) { LM_ERR("cannot register critical event\n"); return -1; } event_params = pkg_malloc(sizeof(evi_params_t)); if (event_params == NULL) return -1; memset(event_params, 0, sizeof(evi_params_t)); #define CREATE_PARAM(pname) \ pname ## _p = evi_param_create(event_params, &ei_ ## pname ## _name);\ if (! pname ## _p) \ goto create_param_err CREATE_PARAM(param); CREATE_PARAM(val); CREATE_PARAM(thr); CREATE_PARAM(user); CREATE_PARAM(number); CREATE_PARAM(ruleid); #undef CREATE_PARAM return 0; create_param_err: LM_ERR("cannot create event parameter"); return -1; } void frd_event_destroy(void) { evi_free_params(event_params); } /* * Function to be called internally for raising an event */ static void raise_event(event_id_t e, str *param, unsigned int *val, unsigned int *thr, str *user, str *number, unsigned int *ruleid) { #define SET_PARAM(pname, ptype) \ if (evi_param_set_ ##ptype (pname ## _p, pname) < 0) { \ LM_ERR("cannot set " # pname "parameter\n"); \ return; \ } SET_PARAM(param, str); SET_PARAM(val, int); SET_PARAM(thr, int); SET_PARAM(user, str); SET_PARAM(number, str); SET_PARAM(ruleid, int); #undef SET_PARAM if (evi_raise_event(e, event_params) < 0) LM_ERR("cannot raise event\n"); } void raise_warning_event(str *param, unsigned int *val, unsigned int *thr, str *user, str *number, unsigned int *ruleid) { raise_event(ei_warn_id, param, val, thr, user, number, ruleid); } void raise_critical_event(str *param, unsigned int *val, unsigned int *thr, str *user, str *number, unsigned int *ruleid) { raise_event(ei_crit_id, param, val, thr, user, number, ruleid); } /* * dialog API callback - called whenever a dialog is ended * * For confirmed dialogs, it checks the duration against the * thresholds (sent through the params) and raises the appropriate event */ void dialog_terminate_CB(struct dlg_cell *dlgc, int type, struct dlg_cb_params *params) { static str call_dur_name = str_init ("call_duration"); frd_dlg_param *frdparam = (frd_dlg_param*) *(params->param); extern unsigned int frd_data_rev; if (type == DLGCB_TERMINATED && frd_data_rev == frdparam->data_rev) { unsigned int duration = time(NULL) - dlgc->start_ts; if ( duration >= frdparam->thr->call_duration_thr.critical) raise_critical_event(&call_dur_name, &duration, &frdparam->thr->call_duration_thr.critical, &frdparam->user, &frdparam->number, &frdparam->ruleid); else if ( duration >= frdparam->thr->call_duration_thr.warning) raise_warning_event(&call_dur_name, &duration, &frdparam->thr->call_duration_thr.warning, &frdparam->user, &frdparam->number, &frdparam->ruleid); } lock_get(&frdparam->stats->lock); --frdparam->stats->stats.concurrent_calls; lock_release(&frdparam->stats->lock); shm_free(frdparam->number.s); shm_free(frdparam); } opensips-2.2.2/modules/fraud_detection/frd_events.h000066400000000000000000000030541300170765700225040ustar00rootroot00000000000000/** * Fraud Detection Module * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-09-26 initial version (Andrei Datcu) */ #ifndef __FRD_EVENTS_H__ #define __FRD_EVENTS_H__ #include "frd_stats.h" int frd_event_init(void); void frd_event_destroy(void); void raise_warning_event(str *param, unsigned int *val, unsigned int *thr, str *user, str *number, unsigned int *ruleid); void raise_critical_event(str *param, unsigned int *val, unsigned int *thr, str *user, str *number, unsigned int *ruleid); /* Dialog callback */ typedef struct { frd_stats_entry_t *stats; frd_thresholds_t *thr; str user; str number; unsigned int ruleid; unsigned int data_rev; } frd_dlg_param; void dialog_terminate_CB(struct dlg_cell *dlgc, int type, struct dlg_cb_params *params); #endif opensips-2.2.2/modules/fraud_detection/frd_hashmap.c000066400000000000000000000043251300170765700226160ustar00rootroot00000000000000/** * Fraud Detection Module * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-09-26 initial version (Andrei Datcu) */ #include "frd_hashmap.h" #include "../../hash_func.h" #include "../../str.h" #include "../../locking.h" int init_hash_map(hash_map_t *hm) { hm->buckets = shm_malloc(hm->size * sizeof(hash_bucket_t)); if (hm->buckets == NULL) { LM_ERR("No more shm memory\n"); return -1; } unsigned int i; for (i = 0; i < hm->size; ++i) { hm->buckets[i].items = map_create(AVLMAP_SHARED); hm->buckets[i].lock = lock_init_rw(); if (hm->buckets[i].lock == NULL) { LM_ERR("cannot init lock\n"); shm_free(hm->buckets); return -1; } } return 0; } void** get_item (hash_map_t *hm, str key) { unsigned int hash = core_hash(&key, NULL, hm->size); lock_start_read(hm->buckets[hash].lock); void **find_res = map_find(hm->buckets[hash].items, key); lock_stop_read(hm->buckets[hash].lock); if (find_res) { return find_res; } else { lock_start_write(hm->buckets[hash].lock); find_res = map_get(hm->buckets[hash].items, key); lock_stop_write(hm->buckets[hash].lock); return find_res; } } void free_hash_map(hash_map_t* hm, void (*value_destroy_func)(void *)) { unsigned int i; for (i = 0; i < hm->size; ++i) { lock_start_write(hm->buckets[i].lock); map_destroy(hm->buckets[i].items, value_destroy_func); lock_stop_write(hm->buckets[i].lock); lock_destroy_rw(hm->buckets[i].lock); } shm_free(hm->buckets); } opensips-2.2.2/modules/fraud_detection/frd_hashmap.h000066400000000000000000000024551300170765700226250ustar00rootroot00000000000000/** * Fraud Detection Module * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-09-26 initial version (Andrei Datcu) */ #ifndef __FRD_HASHMAP_H__ #define __FRD_HASHMAP_H__ #include "../../map.h" #include "../../rw_locking.h" typedef struct { map_t items; rw_lock_t *lock; } hash_bucket_t; typedef struct { hash_bucket_t *buckets; size_t size; } hash_map_t; int init_hash_map(hash_map_t* hm); void** get_item (hash_map_t *hm, str key); void free_hash_map(hash_map_t* hm, void (*value_destroy_func)(void *)); #endif opensips-2.2.2/modules/fraud_detection/frd_load.c000066400000000000000000000313661300170765700221210ustar00rootroot00000000000000/** * Fraud Detection Module * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-09-26 initial version (Andrei Datcu) */ #include "../../ut.h" #include "../../db/db.h" #include "../drouting/dr_api.h" #include "../../time_rec.h" #include "frd_stats.h" #define FRD_TABLE_VERSION 1 #define FRD_TIME_SEP ':' #define FRD_RID_COL "ruleid" #define FRD_PID_COL "profileid" #define FRD_PREFIX_COL "prefix" #define FRD_START_H_COL "start_hour" #define FRD_END_H_COL "end_hour" #define FRD_DAYS_COL "daysoftheweek" #define FRD_CPM_THRESH_WARN_COL "cpm_warning" #define FRD_CPM_THRESH_CRIT_COL "cpm_critical" #define FRD_CALLDUR_THRESH_WARN_COL "call_duration_warning" #define FRD_CALLDUR_THRESH_CRIT_COL "call_duration_critical" #define FRD_TOTALC_THRESH_WARN_COL "total_calls_warning" #define FRD_TOTALC_THRESH_CRIT_COL "total_calls_critical" #define FRD_CONCALLS_THRESH_WARN_COL "concurrent_calls_warning" #define FRD_CONCALLS_THRESH_CRIT_COL "concurrent_calls_critical" #define FRD_SEQCALLS_THRESH_WARN_COL "sequential_calls_warning" #define FRD_SEQCALLS_THRESH_CRIT_COL "sequential_calls_critical" str db_url; str table_name = str_init("fraud_detection"); str rid_col = str_init(FRD_RID_COL); str pid_col = str_init(FRD_PID_COL); str prefix_col = str_init(FRD_PREFIX_COL); str start_h_col = str_init(FRD_START_H_COL); str end_h_col = str_init(FRD_END_H_COL); str days_col = str_init(FRD_DAYS_COL); str cpm_thresh_warn_col = str_init(FRD_CPM_THRESH_WARN_COL); str cpm_thresh_crit_col = str_init(FRD_CPM_THRESH_CRIT_COL); str calldur_thresh_warn_col = str_init(FRD_CALLDUR_THRESH_WARN_COL); str calldur_thresh_crit_col = str_init(FRD_CALLDUR_THRESH_CRIT_COL); str totalc_thresh_warn_col = str_init(FRD_TOTALC_THRESH_WARN_COL); str totalc_thresh_crit_col = str_init(FRD_TOTALC_THRESH_CRIT_COL); str concalls_thresh_warn_col = str_init(FRD_CONCALLS_THRESH_WARN_COL); str concalls_thresh_crit_col = str_init(FRD_CONCALLS_THRESH_CRIT_COL); str seqcalls_thresh_warn_col = str_init(FRD_SEQCALLS_THRESH_WARN_COL); str seqcalls_thresh_crit_col = str_init(FRD_SEQCALLS_THRESH_CRIT_COL); unsigned int frd_data_rev; static db_con_t *db_handle; static db_func_t dbf; extern dr_head_p *dr_head; extern struct dr_binds drb; extern rw_lock_t *frd_data_lock; /* List of data kept in dr's attr and freed here - pkg */ typedef struct _free_list_t{ tmrec_p trec; frd_thresholds_t *thr; unsigned int n; struct _free_list_t *next; } free_list_t; static free_list_t *free_list; /* * Function that parse time string like %H:%M to a tm struct * tm struct must be allocated and initialized */ static int strtime(const str *time, int *ihrs, int *imin) { char *colon = q_memchr(time->s, FRD_TIME_SEP, time->len); if (colon == NULL) goto parse_error; str hrs = {time->s, colon - time->s}; str min = {colon + 1, time->len - hrs.len - 1}; if (hrs.len == 0 || min.len == 0) goto parse_error; unsigned int uhrs, umin; if (str2int(&hrs, &uhrs) || str2int(&min, &umin)) goto parse_error; if (uhrs > 23 || umin >= 60) goto parse_error; *imin = umin; *ihrs = uhrs; return 0; parse_error: LM_ERR("cannot parse time-value <%.*s>", time->len, time->s); return -1; } static int strcmp_case_insensitive(char *s1, char *s2, int len) { int i; for (i = 0; i < len; ++i) if (tolower(s1[i]) != tolower(s2[i])) return -1; return 0; } static int parse_week_days(const str *week_days, unsigned short *day_set) { static const str str_days[] = { str_init("sun"), str_init("mon"), str_init("tue"), str_init("wed"), str_init("thu"), str_init("fri"), str_init("sat") }; static const char interval_delim = '-'; static const char list_delim = ','; if (week_days->len == 0) return 0; char *p = week_days->s, *np, *dash; int rem_len = week_days->len, token_len, i, j, n = 0; str t1, t2; do { np = q_memchr(p, list_delim, rem_len); token_len = np ? np - p : rem_len; rem_len -= token_len + 1; if (token_len < 3) goto parse_error; /* Now we see if it is an interval */ dash = q_memchr(p, interval_delim, token_len); if (dash){ /* It is an interval */ t1.s = p; t1.len = dash - p; trim_spaces_lr(t1); t2.s = dash + 1; t2.len = token_len - t1.len - 1; trim_spaces_lr(t2); if (t1.len != 3 || t2.len != 3) goto parse_error; for (i = 0; i < 7; ++i) if (strcmp_case_insensitive(str_days[i].s, t1.s, 3) == 0) break; if (i == 7) goto parse_error; for (j = 0; j < 7; ++j) if (strcmp_case_insensitive(str_days[j].s, t2.s, 3) == 0) break; if (j == 7) goto parse_error; /* We increase the size of the days set */ n += (j - i + 7) % 7 + 1; for (; i != j; i = (i + 1) % 7) *day_set |= 1 << i; *day_set |= 1 << j; } else { /* Just one value */ t1.s = p; t1.len = token_len; trim_spaces_lr(t1); if (t1.len != 3) goto parse_error; for (i = 0; i < 7; ++i) if (strcmp_case_insensitive(str_days[i].s, t1.s, 3) == 0) break; if (i == 7) goto parse_error; *day_set |= 1 << i; ++n; } p = np + 1; } while (rem_len > 0); return n; parse_error: LM_ERR("Cannot parse week day list <%.*s>", week_days->len, week_days->s); return -1; } static int create_time_rec(const str *time_start, const str *time_end, const str *week_days, tmrec_p trec) { int end_h, end_m; memset(trec, 0, sizeof(tmrec_t)); if (strtime(time_start, &trec->ts.tm_hour, &trec->ts.tm_min) != 0 || strtime(time_end, &end_h, &end_m) != 0) return -1; trec->duration = (end_h * 3600 + end_m * 60) - (trec->ts.tm_hour * 3600 + trec->ts.tm_min * 60); trec->ts.tm_isdst = -1 /*daylight*/; trec->dtstart = trec->duration; trec->freq = FREQ_DAILY; unsigned short day_set = 0; int n = parse_week_days(week_days, &day_set); if (n == -1) return -1; if (n) { //TODO - byday custom init - no req needed trec->byday = tr_byxxx_new(SHM_ALLOC); if (trec->byday == NULL) return -1; if (tr_byxxx_init(trec->byday, n) < 0) { tr_byxxx_free(trec->byday); return -1; } short i, j = 0; for (i = 0; i < 7; ++i) if (day_set & 1 << i) trec->byday->xxx[j++] = i; } return 0; } static int frd_load_data(dr_head_p drp, free_list_t **fl) { static const size_t col_count = 16; db_res_t *res = NULL; unsigned int no_rows = 0, row_count, i; db_row_t *rows; db_val_t *values; db_key_t query_cols[] = { &rid_col, &pid_col, &prefix_col, &start_h_col, &end_h_col, &days_col, &cpm_thresh_warn_col, &cpm_thresh_crit_col, &calldur_thresh_warn_col, &calldur_thresh_crit_col, &totalc_thresh_warn_col, &totalc_thresh_crit_col, &concalls_thresh_warn_col, &concalls_thresh_crit_col, &seqcalls_thresh_warn_col, &seqcalls_thresh_crit_col }; if (db_handle == NULL) { LM_ERR("Invalid db handler\n"); return -1; } if (dbf.use_table(db_handle, &table_name) != 0) { LM_ERR("Cannot use table\n"); return -1; } if (DB_CAPABILITY(dbf, DB_CAP_FETCH)) { if (dbf.query(db_handle, 0, 0, 0, query_cols, 0, col_count, 0, 0) != 0) { LM_ERR("Error while querying db\n"); goto error; } /* estimate rows */ no_rows = estimate_available_rows(4 + 64 + 5 + 5 + 64 + 5 * 2 * 4, col_count); if (no_rows == 0) no_rows = 10; if (dbf.fetch_result(db_handle, &res, no_rows) != 0) { LM_ERR("Error while fetching rows\n"); goto error; } } else { /* No fetching capability */ if (dbf.query(db_handle, 0, 0, 0, query_cols, 0, col_count, 0, &res) != 0) { LM_ERR("Error while querying db\n"); goto error; } } /* Process the actual data */ unsigned int rid, pid, j; str prefix, start_time, end_time, days; free_list_t *fl_it = NULL; *fl = NULL; do { row_count = RES_ROW_N(res); rows = RES_ROWS(res); fl_it = pkg_malloc(sizeof(free_list_t)); if (fl_it == NULL) { LM_ERR ("no more pkg memory"); dbf.free_result(db_handle, res); return -1; } fl_it ->next = *fl; *fl = fl_it; fl_it->trec = shm_malloc(sizeof(tmrec_t) * row_count); if (fl_it->trec == NULL) goto no_more_shm; fl_it->thr = shm_malloc(sizeof(frd_thresholds_t) * row_count); if (fl_it->thr == NULL) goto no_more_shm; fl_it->n = row_count; for (i = 0; i < row_count; ++i) { values = ROW_VALUES(rows + i); fl_it->trec[i].byday = NULL; /* rule id */ if (VAL_NULL(values)) { LM_ERR("rule id cannot be NULL - skipping rule\n"); continue; } rid = VAL_INT(values); /* profile id */ if (VAL_NULL(values + 1)) { LM_ERR("profile id cannot be NULL - skipping rule\n"); continue; } pid = VAL_INT(values + 1); get_str_from_dbval(prefix_col.s, values + 2, 1, 1, prefix, null_val); get_str_from_dbval(start_h_col.s, values + 3, 1, 1, start_time, null_val); get_str_from_dbval(end_h_col.s, values + 4, 1, 1, end_time, null_val); get_str_from_dbval(days_col.s, values + 5, 1, 1, days, null_val); if (create_time_rec(&start_time, &end_time, &days, fl_it->trec + i) != 0) goto null_val; /* Now load the thresholds */ for (j = 0; j < 2 * 5; ++j) { if (VAL_NULL(values + 6 + j)) goto null_val; memcpy((char*)fl_it->thr + i * sizeof(frd_thresholds_t) + j * sizeof(unsigned int), &VAL_INT(values + 6 + j), sizeof(unsigned int)); } /* Rule OK, time to put it in DR */ if (drb.add_rule(drp, rid, &prefix, pid, 0, fl_it->trec + i, (void*)(&fl_it->thr[i])) != 0) { LM_ERR("Cannot add rule in dr <%u>. Skipping...\n", rid); } null_val: continue; } if (DB_CAPABILITY(dbf, DB_CAP_FETCH)) { /* any more rows to fetch ? */ if(dbf.fetch_result(db_handle, &res, no_rows)<0) { LM_ERR("error while fetching rows\n"); goto error; } /* success in fetching more rows - continue the loop */ } else break; } while (RES_ROW_N(res) > 0); dbf.free_result(db_handle, res); return 0; no_more_shm: LM_ERR ("no more shm memory\n"); dbf.free_result(db_handle, res); error: return -1; } /* This function assumes no one is using the dr_head anymore */ static void frd_destroy_data_unsafe(dr_head_p dr_head, free_list_t *fl) { if (dr_head == NULL && fl == NULL) return; drb.free_head(dr_head); free_list_t *it = fl, *aux; int i; while (it) { for (i = 0; i < it->n; ++i) if (it->trec[i].byday) tr_byxxx_free(it->trec[i].byday); shm_free(it->trec); shm_free(it->thr); aux = it; it = it->next; pkg_free(aux); } } /* Function to be called in mod_destroy * Still unsafe!!! */ void frd_destroy_data(void) { frd_destroy_data_unsafe(*dr_head, free_list); } int frd_reload_data(void) { dr_head_p new_head, old_head; if ((new_head = drb.create_head()) == NULL) { LM_ERR ("cannot create dr_head\n"); return -1; } free_list_t *new_list = NULL, *old_list; if (frd_load_data(new_head, &new_list) != 0) { LM_ERR("cannot load fraud data\n"); return -1; } old_head = *dr_head; old_list = free_list; ++frd_data_rev; lock_start_write(frd_data_lock); *dr_head = new_head; free_list = new_list; lock_stop_write(frd_data_lock); frd_destroy_data_unsafe(old_head, old_list); return 0; } int frd_connect_db(void) { if (db_url.s == NULL || db_url.len == 0) { LM_ERR("invalid db_url\n"); return -1; } if (db_handle != NULL) { LM_CRIT("[BUG] connection already open\n"); return -1; } if ((db_handle = dbf.init(&db_url)) == 0) { LM_ERR("unable to connect to the database\n"); return -1; } return 0; } void frd_disconnect_db(void) { if (db_handle) { dbf.close(db_handle); db_handle = NULL; } } int frd_init_db(void) { int table_version; if (table_name.s == NULL || table_name.len == 0) { LM_ERR("invalid table name\n"); return -1; } if (db_bind_mod(&db_url, &dbf) != 0) { LM_ERR("unable to bind to a database driver\n"); return -1; } if(frd_connect_db() != 0) return -1; table_version = db_table_version(&dbf, db_handle, &table_name); if (table_version < 0) { LM_ERR("failed to query table version\n"); return -1; } else if (table_version != FRD_TABLE_VERSION) { LM_ERR("invalid table version (found %d , required %d)\n", table_version, FRD_TABLE_VERSION ); return -1; } return 0; } opensips-2.2.2/modules/fraud_detection/frd_load.h000066400000000000000000000021251300170765700221150ustar00rootroot00000000000000/** * Fraud Detection Module * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-09-26 initial version (Andrei Datcu) */ #ifndef __FRD_LOAD_H__ #define __FRD_LOAD_H__ int frd_init_db(void); int frd_connect_db(void); void frd_disconnect_db(void); int frd_reload_data(void); void frd_destroy_data(void); #endif opensips-2.2.2/modules/fraud_detection/frd_stats.c000066400000000000000000000067421300170765700223400ustar00rootroot00000000000000/** * Fraud Detection Module * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-09-26 initial version (Andrei Datcu) */ #include #include "frd_stats.h" #include "frd_hashmap.h" #include "../../ut.h" /* Struct used for the first level of the hashmap * the user is kept in shm for two reasons : * a) to keep using core's map * b) to pass it for the dialog_end callback */ typedef struct { hash_map_t numbers_hm; str user; } frd_users_map_item_t; static hash_map_t stats_table; /* * Function to init the stats hash table */ int init_stats_table(void) { stats_table.size = FRD_USER_HASH_SIZE; return init_hash_map(&stats_table); } frd_stats_entry_t* get_stats(str user, str prefix, str *shm_user) { /* First go one level below using the user key */ frd_users_map_item_t **hm = (frd_users_map_item_t **)get_item(&stats_table, user); if (*hm == NULL) { /* First time the user is seen, we must create a hashmap */ *hm = shm_malloc(sizeof(frd_users_map_item_t)); if (*hm == NULL) { LM_ERR("no more shm memory\n"); return NULL; } (*hm)->numbers_hm.size = FRD_PREFIX_HASH_SIZE; if (init_hash_map(&(*hm)->numbers_hm) != 0) { LM_ERR("cannot init hashmap\n"); shm_free(*hm); return NULL; } if (shm_str_dup(&(*hm)->user, &user) != 0) { shm_free(*hm); return NULL; } } if (shm_user) *shm_user = (*hm)->user; frd_stats_entry_t **stats_entry = (frd_stats_entry_t**)get_item(&(*hm)->numbers_hm, prefix); if (*stats_entry == NULL) { /* First time the prefix is seen for this user */ *stats_entry = shm_malloc(sizeof(frd_stats_entry_t)); if (*stats_entry == NULL) { LM_ERR("no more shm memory\n"); return NULL; } /* Now init the auxiliary info for a stats structure */ if (!lock_init(&(*stats_entry)->lock)) { LM_ERR ("cannot init lock\n"); shm_free(*stats_entry); return NULL; } memset(&((*stats_entry)->stats), 0, sizeof(frd_stats_t)); } return *stats_entry; } int stats_exist(str user, str prefix) { /* First go one level below using the user key */ frd_users_map_item_t **hm = (frd_users_map_item_t **)get_item(&stats_table, user); if (*hm == NULL) return 0; frd_stats_entry_t **stats_entry = (frd_stats_entry_t**)get_item(&(*hm)->numbers_hm, prefix); if (*stats_entry == NULL) return 0; return 1; } /* * Functions for freeing the stats hash table */ static void destroy_stats_entry(void *e) { lock_destroy( &((frd_stats_entry_t*)e)->lock ); shm_free(e); } static void destroy_users(void *u) { frd_users_map_item_t *hm = (frd_users_map_item_t*)u; free_hash_map(&hm->numbers_hm, destroy_stats_entry); shm_free(hm->user.s); shm_free(u); } void free_stats_table(void) { free_hash_map(&stats_table, destroy_users); } opensips-2.2.2/modules/fraud_detection/frd_stats.h000066400000000000000000000035571300170765700223460ustar00rootroot00000000000000/** * Fraud Detection Module * * Copyright (C) 2014 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2014-09-26 initial version (Andrei Datcu) */ #ifndef __FRD_STATS_H__ #define __FRD_STATS_H__ #include "../../str.h" #include "../../locking.h" #include "../../rw_locking.h" #define FRD_USER_HASH_SIZE 1000 #define FRD_PREFIX_HASH_SIZE 10 #define FRD_SECS_PER_WINDOW 60 typedef struct { unsigned int cpm; unsigned int total_calls; unsigned int concurrent_calls; unsigned int seq_calls; unsigned int last_matched_rule; time_t last_matched_time; unsigned short calls_window[FRD_SECS_PER_WINDOW]; } frd_stats_t; typedef struct _frd_hash_item { gen_lock_t lock; frd_stats_t stats; } frd_stats_entry_t; int init_stats_table(void); frd_stats_entry_t* get_stats(str user, str prefix, str *shm_user); int stats_exist(str user, str prefix); void free_stats_table(void); typedef struct { unsigned int warning; unsigned int critical; } frd_threshold_t; typedef struct { frd_threshold_t cpm_thr, call_duration_thr, total_calls_thr, concurrent_calls_thr, seq_calls_thr; } frd_thresholds_t; #endif opensips-2.2.2/modules/gflags/000077500000000000000000000000001300170765700162765ustar00rootroot00000000000000opensips-2.2.2/modules/gflags/Makefile000066400000000000000000000003301300170765700177320ustar00rootroot00000000000000# $Id$ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=gflags.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/gflags/README000066400000000000000000000114151300170765700171600ustar00rootroot00000000000000gflags Module Jiri Kuthan Bogdan-Andrei Iancu Edited by Daniel-Constantin Mierla Copyright © 2004 FhG FOKUS Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.3. Exported Parameters 1.3.1. initial (integer) 1.4. Exported Functions 1.4.1. set_gflag(flag) 1.4.2. reset_gflag(flag) 1.4.3. is_gflag(flag) 1.5. MI Commands 1.5.1. set_gflag 1.5.2. reset_gflag 1.5.3. is_gflag 1.5.4. get_gflags List of Examples 1.1. initial parameter usage 1.2. set_gflag() usage 1.3. reset_gflag() usage 1.4. is_gflag() usage 1.5. set_gflag usage 1.6. reset_gflag usage 1.7. is_gflag usage 1.8. get_gflags usage Chapter 1. Admin Guide 1.1. Overview gflags module (global flags) keeps a bitmap of flags in shared memory and may be used to change behaviour of server based on value of the flags. Example: if (is_gflag("1")) { t_relay("udp:10.0.0.1:5060"); } else { t_relay("udp:10.0.0.2:5060"); } The benefit of this module is the value of the switch flags can be manipulated by external applications such as web interface or command line tools. The size of bitmap is 32. The module exports external commands that can be used to change the global flags via Management Interface. The MI commands are: “set_gflagâ€, “reset_gflag†and “is_gflagâ€. 1.2. Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): * none 1.3. Exported Parameters 1.3.1. initial (integer) The initial value of global flags bitmap. Default value is “0â€. Example 1.1. initial parameter usage modparam("gflags", "initial", 15) 1.4. Exported Functions 1.4.1. set_gflag(flag) Set the bit at the position “flag†in global flags. “flag†can have a value in the range of 0..31. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.2. set_gflag() usage ... set_gflag("4"); ... 1.4.2. reset_gflag(flag) Reset the bit at the position “flag†in global flags. “flag†can have a value in the range of 0..31. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.3. reset_gflag() usage ... reset_gflag("4"); ... 1.4.3. is_gflag(flag) Check if bit at the position “flag†in global flags is set. “flag†can have a value in the range of 0..31. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.4. is_gflag() usage ... if(is_gflag("4")) { log("global flag 4 is set\n"); } else { log("global flag 4 is not set\n"); }; ... 1.5. MI Commands Functions that check or change some flags accepts one parameter which is the flag bitmap/mask specifing the corresponding flags. It is not possible to specify directly the flag position that should be changed as in the functions available in the routing script. 1.5.1. set_gflag Set the value of some flags (specified by bitmask) to 1. The parameter value must be a bitmask in decimal or hexa format. The bitmaks has a 32 bit size. Example 1.5. set_gflag usage ... $ opensipsctl fifo set_gflag 1 $ opensipsctl fifo set_gflag 0x3 ... 1.5.2. reset_gflag Reset the value of some flags to 0. The parameter value must be a bitmask in decimal or hexa format. The bitmaks has a 32 bit size. Example 1.6. reset_gflag usage ... $ opensipsctl fifo reset_gflag 1 $ opensipsctl fifo reset_gflag 0x3 ... 1.5.3. is_gflag Returns true if the all the flags from the bitmask are set. The parameter value must be a bitmask in decimal or hexa format. The bitmaks has a 32 bit size. The function returns TRUE if all the flags from the set are set and FALSE if at least one is not set. Example 1.7. is_gflag usage ... $ opensipsctl fifo set_gflag 1024 $ opensipsctl fifo is_gflag 1024 TRUE $ opensipsctl fifo is_gflag 1025 TRUE $ opensipsctl fifo is_gflag 1023 FALSE $ opensipsctl fifo set_gflag 0x10 $ opensipsctl fifo is_gflag 1023 TRUE $ opensipsctl fifo is_gflag 1007 FALSE $ opensipsctl fifo is_gflag 16 TRUE ... 1.5.4. get_gflags Return the bitmap with all flags. The function gets no parameters and returns the bitmap in hexa and decimal format. Example 1.8. get_gflags usage ... $ opensipsctl fifo get_gflags 0x3039 12345 ... opensips-2.2.2/modules/gflags/doc/000077500000000000000000000000001300170765700170435ustar00rootroot00000000000000opensips-2.2.2/modules/gflags/doc/gflags.xml000066400000000000000000000020651300170765700210330ustar00rootroot00000000000000 %docentities; ]> gflags Module Jiri Kuthan jiri@iptel.org Bogdan-Andrei Iancu bogdan@opensips.org Daniel-Constantin Mierla miconda@gmail.com 2004 &fhg; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/gflags/doc/gflags_admin.xml000066400000000000000000000141521300170765700222030ustar00rootroot00000000000000 &adminguide;
Overview gflags module (global flags) keeps a bitmap of flags in shared memory and may be used to change behaviour of server based on value of the flags. Example: if (is_gflag("1")) { t_relay("udp:10.0.0.1:5060"); } else { t_relay("udp:10.0.0.2:5060"); } The benefit of this module is the value of the switch flags can be manipulated by external applications such as web interface or command line tools. The size of bitmap is 32. The module exports external commands that can be used to change the global flags via Management Interface. The MI commands are: set_gflag, reset_gflag and is_gflag.
Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): none
Exported Parameters
<varname>initial</varname> (integer) The initial value of global flags bitmap. Default value is 0. <varname>initial</varname> parameter usage modparam("gflags", "initial", 15)
Exported Functions
<function moreinfo="none">set_gflag(flag)</function> Set the bit at the position flag in global flags. flag can have a value in the range of 0..31. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function moreinfo="none">set_gflag()</function> usage ... set_gflag("4"); ...
<function moreinfo="none">reset_gflag(flag)</function> Reset the bit at the position flag in global flags. flag can have a value in the range of 0..31. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function moreinfo="none">reset_gflag()</function> usage ... reset_gflag("4"); ...
<function moreinfo="none">is_gflag(flag)</function> Check if bit at the position flag in global flags is set. flag can have a value in the range of 0..31. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function moreinfo="none">is_gflag()</function> usage ... if(is_gflag("4")) { log("global flag 4 is set\n"); } else { log("global flag 4 is not set\n"); }; ...
<acronym>MI</acronym> Commands Functions that check or change some flags accepts one parameter which is the flag bitmap/mask specifing the corresponding flags. It is not possible to specify directly the flag position that should be changed as in the functions available in the routing script.
<function moreinfo="none">set_gflag</function> Set the value of some flags (specified by bitmask) to 1. The parameter value must be a bitmask in decimal or hexa format. The bitmaks has a 32 bit size. <function moreinfo="none">set_gflag</function> usage ... $ opensipsctl fifo set_gflag 1 $ opensipsctl fifo set_gflag 0x3 ...
<function moreinfo="none">reset_gflag</function> Reset the value of some flags to 0. The parameter value must be a bitmask in decimal or hexa format. The bitmaks has a 32 bit size. <function moreinfo="none">reset_gflag</function> usage ... $ opensipsctl fifo reset_gflag 1 $ opensipsctl fifo reset_gflag 0x3 ...
<function moreinfo="none">is_gflag</function> Returns true if the all the flags from the bitmask are set. The parameter value must be a bitmask in decimal or hexa format. The bitmaks has a 32 bit size. The function returns TRUE if all the flags from the set are set and FALSE if at least one is not set. <function moreinfo="none">is_gflag</function> usage ... $ opensipsctl fifo set_gflag 1024 $ opensipsctl fifo is_gflag 1024 TRUE $ opensipsctl fifo is_gflag 1025 TRUE $ opensipsctl fifo is_gflag 1023 FALSE $ opensipsctl fifo set_gflag 0x10 $ opensipsctl fifo is_gflag 1023 TRUE $ opensipsctl fifo is_gflag 1007 FALSE $ opensipsctl fifo is_gflag 16 TRUE ...
<function moreinfo="none">get_gflags</function> Return the bitmap with all flags. The function gets no parameters and returns the bitmap in hexa and decimal format. <function moreinfo="none">get_gflags</function> usage ... $ opensipsctl fifo get_gflags 0x3039 12345 ...
opensips-2.2.2/modules/gflags/gflags.c000066400000000000000000000203531300170765700177100ustar00rootroot00000000000000/* * Copyright (C) 2004 FhG * Copyright (C) 2005-2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-09-09 initial module created (jiri) * 2006-05-31 flag range checked ; proper cleanup at module destroy ; * got rid of memory allocation in fixup function ; * optimized fixup function -> compute directly the bitmap ; * allowed functions from BRANCH_ROUTE (bogdan) * * TODO: * ----- * - named flags (takes a protected name list) * * * gflags module: global flags; it keeps a bitmap of flags * in shared memory and may be used to change behaviour * of server based on value of the flags. E.g., * if (is_gflag("1")) { t_relay_to_udp("10.0.0.1","5060"); } * else { t_relay_to_udp("10.0.0.2","5060"); } * The benefit of this module is the value of the switch flags * can be manipulated by external applications such as web interface * or command line tools. * * */ /* flag buffer size for FIFO protocool */ #define MAX_FLAG_LEN 12 /* FIFO action protocol names */ #define FIFO_SET_GFLAG "set_gflag" #define FIFO_IS_GFLAG "is_gflag" #define FIFO_RESET_GFLAG "reset_gflag" #define FIFO_GET_GFLAGS "get_gflags" #include #include "../../sr_module.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" static int set_gflag(struct sip_msg*, char *, char *); static int reset_gflag(struct sip_msg*, char *, char *); static int is_gflag(struct sip_msg*, char *, char *); static struct mi_root* mi_set_gflag(struct mi_root* cmd, void* param ); static struct mi_root* mi_reset_gflag(struct mi_root* cmd, void* param ); static struct mi_root* mi_is_gflag(struct mi_root* cmd, void* param ); static struct mi_root* mi_get_gflags(struct mi_root* cmd, void* param ); static int fixup_gflags( void** param, int param_no); static int mod_init(void); static void mod_destroy(void); static int initial=0; static unsigned int *gflags=0; static cmd_export_t cmds[]={ {"set_gflag", (cmd_function)set_gflag, 1, fixup_gflags, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"reset_gflag", (cmd_function)reset_gflag, 1, fixup_gflags, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"is_gflag", (cmd_function)is_gflag, 1, fixup_gflags, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[]={ {"initial", INT_PARAM, &initial}, {0,0,0} }; static mi_export_t mi_cmds[] = { { FIFO_SET_GFLAG, 0, mi_set_gflag, 0, 0, 0 }, { FIFO_RESET_GFLAG, 0, mi_reset_gflag, 0, 0, 0 }, { FIFO_IS_GFLAG, 0, mi_is_gflag, 0, 0, 0 }, { FIFO_GET_GFLAGS, 0, mi_get_gflags, MI_NO_INPUT_FLAG, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; struct module_exports exports = { "gflags", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ mod_destroy, /* destroy function */ 0 /* per-child init function */ }; /**************************** fixup functions ******************************/ /** * convert char* to int and do bitwise right-shift * char* must be pkg_alloced and will be freed by the function */ static int fixup_gflags( void** param, int param_no) { unsigned int myint; str param_str; /* we only fix the parameter #1 */ if (param_no!=1) return 0; param_str.s=(char*) *param; param_str.len=strlen(param_str.s); if (str2int(¶m_str, &myint )<0) { LM_ERR("bad number <%s>\n", (char *)(*param)); return E_CFG; } if ( myint >= 8*sizeof(*gflags) ) { LM_ERR("flag <%d> out of " "range [0..%lu]\n", myint, ((unsigned long)8*sizeof(*gflags))-1 ); return E_CFG; } /* convert from flag index to flag bitmap */ myint = 1 << myint; /* success -- change to int */ pkg_free(*param); *param=(void *)(long)myint; return 0; } /**************************** module functions ******************************/ static int set_gflag(struct sip_msg *bar, char *flag, char *foo) { (*gflags) |= (unsigned int)(long)flag; return 1; } static int reset_gflag(struct sip_msg *bar, char *flag, char *foo) { (*gflags) &= ~ ((unsigned int)(long)flag); return 1; } static int is_gflag(struct sip_msg *bar, char *flag, char *foo) { return ( (*gflags) & ((unsigned int)(long)flag)) ? 1 : -1; } /************************* MI functions *******************************/ static struct mi_root* mi_set_gflag(struct mi_root* cmd_tree, void* param ) { unsigned int flag; struct mi_node* node; node = cmd_tree->node.kids; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); flag = 0; if( strno2int( &node->value, &flag) <0) goto error; if (!flag) { LM_ERR("incorrect flag\n"); goto error; } (*gflags) |= flag; return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } static struct mi_root* mi_reset_gflag(struct mi_root* cmd_tree, void* param ) { unsigned int flag; struct mi_node* node = NULL; node = cmd_tree->node.kids; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); flag = 0; if( strno2int( &node->value, &flag) <0) goto error; if (!flag) { LM_ERR("incorrect flag\n"); goto error; } (*gflags) &= ~ flag; return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } static struct mi_root* mi_is_gflag(struct mi_root* cmd_tree, void* param ) { unsigned int flag; struct mi_root* rpl_tree = NULL; struct mi_node* node = NULL; node = cmd_tree->node.kids; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); flag = 0; if( strno2int( &node->value, &flag) <0) goto error_param; if (!flag) { LM_ERR("incorrect flag\n"); goto error_param; } rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if(rpl_tree ==0) return 0; if( ((*gflags) & flag)== flag ) node = add_mi_node_child( &rpl_tree->node, 0, 0, 0, "TRUE", 4); else node = add_mi_node_child( &rpl_tree->node, 0, 0, 0, "FALSE", 5); if(node == NULL) { LM_ERR("failed to add node\n"); free_mi_tree(rpl_tree); return 0; } return rpl_tree; error_param: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } static struct mi_root* mi_get_gflags(struct mi_root* cmd_tree, void* param ) { struct mi_root* rpl_tree= NULL; struct mi_node* node= NULL; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN ); if(rpl_tree == NULL) return 0; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "0x%X",(*gflags)); if(node == NULL) goto error; node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%u",(*gflags)); if(node == NULL) goto error; return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } static int mod_init(void) { gflags=(unsigned int *) shm_malloc(sizeof(unsigned int)); if (!gflags) { LM_ERR(" no shmem\n"); return -1; } *gflags=initial; return 0; } static void mod_destroy(void) { if (gflags) shm_free(gflags); } opensips-2.2.2/modules/group/000077500000000000000000000000001300170765700161675ustar00rootroot00000000000000opensips-2.2.2/modules/group/Makefile000066400000000000000000000003261300170765700176300ustar00rootroot00000000000000# $Id$ # # group example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=group.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/group/README000066400000000000000000000207611300170765700170550ustar00rootroot00000000000000group Module Jan Janak FhG FOKUS Edited by Jan Janak Edited by Sergio Gutierrez Edited by Irina-Maria Stanescu Copyright © 2003 FhG FOKUS Copyright © 2009 Voice Sistem SRL Revision History Revision $Revision: 5969 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Strict membership checking 1.1.2. Regular Expression based checking 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. table (string) 1.3.3. user_column (string) 1.3.4. domain_column (string) 1.3.5. group_column (string) 1.3.6. use_domain (integer) 1.3.7. re_table (string) 1.3.8. re_exp_column (string) 1.3.9. re_gid_column (string) 1.3.10. multiple_gid (integer) 1.3.11. aaa_url (string) 1.4. Exported Functions 1.4.1. db_is_user_in(URI, group) 1.4.2. db_get_user_group(URI, AVP) 1.4.3. aaa_is_user_in(URI, group) List of Examples 1.1. Set db_url parameter 1.2. Set table parameter 1.3. Set user_column parameter 1.4. Set domain_column parameter 1.5. Set group_column parameter 1.6. Set use_domain parameter 1.7. Set re_table parameter 1.8. Set reg_exp_column parameter 1.9. Set re_gid_column parameter 1.10. Set multiple_gid parameter 1.11. Set aaa_url parameter 1.12. db_is_user_in usage 1.13. db_get_user_group usage 1.14. aaa_is_user_in usage Chapter 1. Admin Guide 1.1. Overview This module provides functionalities for different methods of group membership checking. 1.1.1. Strict membership checking There is a database table that contains list of users and groups they belong to. The module provides the possibility to check if a specific user belongs to a specific group. There is no DB caching support, each check involving a DB query. 1.1.2. Regular Expression based checking Another database table contains list of regular expressions and group IDs. A matching occurs if the user URI match the regular expression. This type of matching may be used to fetch the group ID(s) the user belongs to (via RE matching) . Due performance reasons (regular expression evaluation), DB cache support is available: the table content is loaded into memory at startup and all regular expressions are compiled. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * A database module, like mysql, postgres or dbtext. * An AAA module, like radius or diameter. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. db_url (string) URL of the database table to be used. Example 1.1. Set db_url parameter ... modparam("group", "db_url", "mysql://username:password@dbhost/opensips") ... 1.3.2. table (string) Name of the table holding strict definitions of groups and their members. Default value is “grpâ€. Example 1.2. Set table parameter ... modparam("group", "table", "grp_table") ... 1.3.3. user_column (string) Name of the “table†column holding usernames. Default value is “usernameâ€. Example 1.3. Set user_column parameter ... modparam("group", "user_column", "user") ... 1.3.4. domain_column (string) Name of the “table†column holding domains. Default value is “domainâ€. Example 1.4. Set domain_column parameter ... modparam("group", "domain_column", "realm") ... 1.3.5. group_column (string) Name of the “table†column holding groups. Default value is “grpâ€. Example 1.5. Set group_column parameter ... modparam("group", "group_column", "grp") ... 1.3.6. use_domain (integer) If enabled (set to non zero value) then domain will be used also used for strict group matching; otherwise only the username part will be used. Default value is 0 (no). Example 1.6. Set use_domain parameter ... modparam("group", "use_domain", 1) ... 1.3.7. re_table (string) Name of the table holding definitions for regular-expression based groups. If no table is defined, the regular-expression support is disabled. Default value is “NULLâ€. Example 1.7. Set re_table parameter ... modparam("group", "re_table", "re_grp") ... 1.3.8. re_exp_column (string) Name of the “re_table†column holding the regular expression used for user matching. Default value is “reg_expâ€. Example 1.8. Set reg_exp_column parameter ... modparam("group", "reg_exp_column", "re") ... 1.3.9. re_gid_column (string) Name of the “re_table†column holding the group IDs. Default value is “group_idâ€. Example 1.9. Set re_gid_column parameter ... modparam("group", "re_gid_column", "grp_id") ... 1.3.10. multiple_gid (integer) If enabled (non zero value) the regular-expression matching will return all group IDs that match the user; otherwise only the first will be returned. Default value is “1â€. Example 1.10. Set multiple_gid parameter ... modparam("group", "multiple_gid", 0) ... 1.3.11. aaa_url (string) This is the url representing the AAA protocol used and the location of the configuration file of this protocol. Example 1.11. Set aaa_url parameter ... modparam("group", "aaa_url", "radius:/etc/radiusclient-ng/radiusclient.c onf") ... 1.4. Exported Functions 1.4.1. db_is_user_in(URI, group) This function is to be used for script group membership. The function returns true if username in the given URI is member of the given group and false if not. Meaning of the parameters is as follows: * URI - URI whose username and optionally domain to be used, this can be one of: + Request-URI - Use Request-URI username and (optionally) domain. + To - Use To username and (optionally) domain. + From - Use From username and (optionally) domain. + Credentials - Use digest credentials username. + $avp(name) - Use the URI from the AVP specified by this pseudo-variable. * group - Name of the group to check. The string may contain a pseudovariable. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.12. db_is_user_in usage ... if (db_is_user_in("Request-URI", "ld")) { ... }; ... $avp(grouptocheck)="offline"; if (db_is_user_in("Credentials", "$avp(grouptocheck)")) { ... }; ... 1.4.2. db_get_user_group(URI, AVP) This function is to be used for regular expression based group membership, using DB support. The function returns true if username in the given URI belongs to at least one group; the group ID(s) are returned as AVPs. Meaning of the parameters is as follows: * URI - URI to be matched against the regular expressions: + Request-URI - Use Request-URI + To - Use To URI. + From - Use From URI + Credentials - Use digest credentials username and realm. + $avp(name) - Use the URI from the AVP specified by this pseudo-variable. * AVP - $avp(name) - the matched group IDs are returned in this AVP. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.13. db_get_user_group usage ... if (db_get_user_group("Request-URI", "$avp(10)")) { xdbg("User $ru belongs to $(avp(10)[*]) group(s)\n"); .... }; ... 1.4.3. aaa_is_user_in(URI, group) This function checks group membership, using AAA support. The function returns true if username in the given URI is member of the given group and false if not. Meaning of the parameters is as follows: * URI - URI whose username and optionally domain to be used, this can be one of: + Request-URI - Use Request-URI username and (optionally) domain. + To - Use To username and (optionally) domain. + From - Use From username and (optionally) domain. + Credentials - Use digest credentials username. * group - Name of the group to check. This function can be used from REQUEST_ROUTE. Example 1.14. aaa_is_user_in usage ... if (aaa_is_user_in("Request-URI", "ld")) { ... }; ... opensips-2.2.2/modules/group/doc/000077500000000000000000000000001300170765700167345ustar00rootroot00000000000000opensips-2.2.2/modules/group/doc/group.xml000066400000000000000000000030171300170765700206130ustar00rootroot00000000000000 %docentities; ]> group Module &osipsname; Jan Janak FhG FOKUS
jan@iptel.org
Jan Janak
jan@iptel.org
Sergio Gutierrez
saguti@gmail.com
Irina-Maria Stanescu
ironmissy@gmail.com
2003 FhG FOKUS 2009 &voicesystem; $Revision: 5969 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/group/doc/group_admin.xml000066400000000000000000000267711300170765700217770ustar00rootroot00000000000000 &adminguide;
Overview This module provides functionalities for different methods of group membership checking.
Strict membership checking There is a database table that contains list of users and groups they belong to. The module provides the possibility to check if a specific user belongs to a specific group. There is no DB caching support, each check involving a DB query.
Regular Expression based checking Another database table contains list of regular expressions and group IDs. A matching occurs if the user URI match the regular expression. This type of matching may be used to fetch the group ID(s) the user belongs to (via RE matching) . Due performance reasons (regular expression evaluation), DB cache support is available: the table content is loaded into memory at startup and all regular expressions are compiled.
Dependencies
&osips; Modules The following modules must be loaded before this module: A database module, like mysql, postgres or dbtext. An AAA module, like radius or diameter.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>db_url</varname> (string) &url; of the database table to be used. Set <varname>db_url</varname> parameter ... modparam("group", "db_url", "mysql://username:password@dbhost/opensips") ...
<varname>table</varname> (string) Name of the table holding strict definitions of groups and their members. Default value is grp. Set <varname>table</varname> parameter ... modparam("group", "table", "grp_table") ...
<varname>user_column</varname> (string) Name of the table column holding usernames. Default value is username. Set <varname>user_column</varname> parameter ... modparam("group", "user_column", "user") ...
<varname>domain_column</varname> (string) Name of the table column holding domains. Default value is domain. Set <varname>domain_column</varname> parameter ... modparam("group", "domain_column", "realm") ...
<varname>group_column</varname> (string) Name of the table column holding groups. Default value is grp. Set <varname>group_column</varname> parameter ... modparam("group", "group_column", "grp") ...
<varname>use_domain</varname> (integer) If enabled (set to non zero value) then domain will be used also used for strict group matching; otherwise only the username part will be used. Default value is 0 (no). Set <varname>use_domain</varname> parameter ... modparam("group", "use_domain", 1) ...
<varname>re_table</varname> (string) Name of the table holding definitions for regular-expression based groups. If no table is defined, the regular-expression support is disabled. Default value is NULL. Set <varname>re_table</varname> parameter ... modparam("group", "re_table", "re_grp") ...
<varname>re_exp_column</varname> (string) Name of the re_table column holding the regular expression used for user matching. Default value is reg_exp. Set <varname>reg_exp_column</varname> parameter ... modparam("group", "reg_exp_column", "re") ...
<varname>re_gid_column</varname> (string) Name of the re_table column holding the group IDs. Default value is group_id. Set <varname>re_gid_column</varname> parameter ... modparam("group", "re_gid_column", "grp_id") ...
<varname>multiple_gid</varname> (integer) If enabled (non zero value) the regular-expression matching will return all group IDs that match the user; otherwise only the first will be returned. Default value is 1. Set <varname>multiple_gid</varname> parameter ... modparam("group", "multiple_gid", 0) ...
<varname>aaa_url</varname> (string) This is the url representing the AAA protocol used and the location of the configuration file of this protocol. Set <varname>aaa_url</varname> parameter ... modparam("group", "aaa_url", "radius:/etc/radiusclient-ng/radiusclient.conf") ...
Exported Functions
<function moreinfo="none">db_is_user_in(URI, group)</function> This function is to be used for script group membership. The function returns true if username in the given &uri; is member of the given group and false if not. Meaning of the parameters is as follows: &uri; - &uri; whose username and optionally domain to be used, this can be one of: Request-URI - Use Request-URI username and (optionally) domain. To - Use To username and (optionally) domain. From - Use From username and (optionally) domain. Credentials - Use digest credentials username. $avp(name) - Use the URI from the AVP specified by this pseudo-variable. group - Name of the group to check. The string may contain a pseudovariable. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. <function>db_is_user_in</function> usage ... if (db_is_user_in("Request-URI", "ld")) { ... }; ... $avp(grouptocheck)="offline"; if (db_is_user_in("Credentials", "$avp(grouptocheck)")) { ... }; ...
<function moreinfo="none">db_get_user_group(URI, AVP)</function> This function is to be used for regular expression based group membership, using DB support. The function returns true if username in the given &uri; belongs to at least one group; the group ID(s) are returned as AVPs. Meaning of the parameters is as follows: &uri; - &uri; to be matched against the regular expressions: Request-URI - Use Request-URI To - Use To URI. From - Use From URI Credentials - Use digest credentials username and realm. $avp(name) - Use the URI from the AVP specified by this pseudo-variable. AVP - $avp(name) - the matched group IDs are returned in this AVP. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. <function>db_get_user_group</function> usage ... if (db_get_user_group("Request-URI", "$avp(10)")) { xdbg("User $ru belongs to $(avp(10)[*]) group(s)\n"); .... }; ...
<function moreinfo="none">aaa_is_user_in(URI, group)</function> This function checks group membership, using AAA support. The function returns true if username in the given &uri; is member of the given group and false if not. Meaning of the parameters is as follows: &uri; - &uri; whose username and optionally domain to be used, this can be one of: Request-URI - Use Request-URI username and (optionally) domain. To - Use To username and (optionally) domain. From - Use From username and (optionally) domain. Credentials - Use digest credentials username. group - Name of the group to check. This function can be used from REQUEST_ROUTE. <function>aaa_is_user_in</function> usage ... if (aaa_is_user_in("Request-URI", "ld")) { ... }; ...
opensips-2.2.2/modules/group/group.c000066400000000000000000000221011300170765700174630ustar00rootroot00000000000000/* * Group membership * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-25 - created by janakj * 2004-06-07 updated to the new DB api, added group_db_{bind,init,close,ver} * (andrei) * 2008-12-26 - pseudovariable argument for group parameter at is_user_in (saguti). * */ #include #include "../../dprint.h" /* Logging */ #include "../../db/db.h" /* Generic database API */ #include "../../parser/digest/digest.h" /* get_authorized_cred */ #include "../../parser/hf.h" /* Header Field types */ #include "../../parser/parse_from.h" /* From parser */ #include "../../parser/parse_uri.h" #include "../../mod_fix.h" #include "group.h" #include "group_mod.h" /* Module parameters */ #include "../../aaa/aaa.h" static unsigned int hf_type( str *str1); int get_username_domain(struct sip_msg *msg, str *hf_s, str *username, str *domain) { struct sip_uri puri; struct sip_uri *turi; struct hdr_field* h; struct auth_body* c = 0; /* Makes gcc happy */ turi = NULL; switch( hf_type(hf_s) ) { case 1: /* Request-URI */ if(parse_sip_msg_uri(msg)<0) { LM_ERR("failed to get Request-URI\n"); return -1; } turi = &msg->parsed_uri; break; case 2: /* To */ if((turi=parse_to_uri(msg))==NULL) { LM_ERR("failed to get To URI\n"); return -1; } break; case 3: /* From */ if((turi=parse_from_uri(msg))==NULL) { LM_ERR("failed to get From URI\n"); return -1; } break; case 4: /* Credentials */ get_authorized_cred( msg->authorization, &h); if (!h) { get_authorized_cred( msg->proxy_auth, &h); if (!h) { LM_ERR("no authorized credentials found " "(error in scripts)\n"); return -1; } } c = (auth_body_t*)(h->parsed); break; default: /* string */ if (parse_uri(hf_s->s, hf_s->len, &puri) < 0) { LM_ERR("failed to parse URI <%.*s>\n",hf_s->len, hf_s->s); return -1; } turi = &puri; break; } if ( c==NULL ) { *username = turi->user; *domain = turi->host; } else { *username = c->digest.username.user; *domain = *(GET_REALM(&c->digest)); } return 0; } /* * Check if username in specified header field is in a table */ int db_is_user_in(struct sip_msg* _msg, char* _hf, char* _grp) { static db_ps_t my_ps = NULL; db_key_t keys[3]; db_val_t vals[3]; db_key_t col[1]; db_res_t* res = NULL; keys[0] = &user_column; keys[1] = &group_column; keys[2] = &domain_column; col[0] = &group_column; str hf_s = { NULL, 0 }; str grp_s = { NULL, 0 }; if(_hf == NULL || fixup_get_svalue(_msg, (gparam_p)_hf, &hf_s) != 0) { LM_ERR("Invalid parameter URI\n"); return -1; } if(_grp == NULL || fixup_get_svalue(_msg, (gparam_p)_grp, &grp_s) != 0) { LM_ERR("Invalid parameter grp\n"); return -1; } if ( get_username_domain( _msg, &hf_s, &(VAL_STR(vals)), &(VAL_STR(vals+2)))!=0) { LM_ERR("failed to get username@domain\n"); return -1; } if (VAL_STR(vals).s==NULL || VAL_STR(vals).len==0 ) { LM_DBG("no username part\n"); return -1; } VAL_TYPE(vals) = VAL_TYPE(vals + 1) = VAL_TYPE(vals + 2) = DB_STR; VAL_NULL(vals) = VAL_NULL(vals + 1) = VAL_NULL(vals + 2) = 0; VAL_STR(vals + 1) = *(&grp_s); group_dbf.use_table(group_dbh, &table); CON_PS_REFERENCE(group_dbh) = &my_ps; if (group_dbf.query(group_dbh, keys, 0, vals, col, (use_domain) ? (3): (2), 1, 0, &res) < 0) { LM_ERR("failed to query database\n"); return -5; } if (RES_ROW_N(res) == 0) { LM_DBG("user is not in group '%.*s'\n", (grp_s.len), ZSW((grp_s.s))); group_dbf.free_result(group_dbh, res); return -6; } else { LM_DBG("user is in group '%.*s'\n", (grp_s.len), ZSW((grp_s.s))); group_dbf.free_result(group_dbh, res); return 1; } } int group_db_init(const str* db_url) { if (group_dbf.init==0){ LM_CRIT("null dbf \n"); goto error; } group_dbh=group_dbf.init(db_url); if (group_dbh==0){ LM_ERR("unable to connect to the database\n"); goto error; } return 0; error: return -1; } int group_db_bind(const str* db_url) { if (db_bind_mod(db_url, &group_dbf)<0){ LM_ERR("unable to bind to the database module\n"); return -1; } if (!DB_CAPABILITY(group_dbf, DB_CAP_QUERY)) { LM_ERR("database module does not implement 'query' function\n"); return -1; } return 0; } void group_db_close(void) { if (group_dbh && group_dbf.close){ group_dbf.close(group_dbh); group_dbh=0; } } /* * * "Request-URI", "To", "From", "Credentials" */ static unsigned int hf_type( str *str1) { if (str1->len==11 && !strncasecmp( str1->s, "Request-URI",11)) { return 1; } else if (str1->len==2 && !strncasecmp( str1->s, "To", 2)) { return 2; } else if (str1->len==4 && !strncasecmp( str1->s, "From", 4)) { return 3; } else if (str1->len==11 && !strncasecmp( str1->s, "Credentials",11)) { return 4; } else { return 0; } } /* * Check from AAA server if a user belongs to a group. User-Name is digest * username or digest username@realm, SIP-Group is group, and Service-Type * is Group-Check. SIP-Group is SER specific attribute and Group-Check is * SER specific service type value. */ int aaa_is_user_in(struct sip_msg* _m, char* _hf, char* _group) { str *grp, user_name, user, domain; dig_cred_t* cred = 0; int hf_type; uint32_t service; aaa_message *send = NULL, *received = NULL; struct hdr_field* h; struct sip_uri *turi; grp = (str*)_group; /* via fixup */ hf_type = (int)(long)_hf; turi = 0; switch(hf_type) { case 1: /* Request-URI */ if(parse_sip_msg_uri(_m)<0) { LM_ERR("failed to get Request-URI\n"); return -1; } turi = &_m->parsed_uri; break; case 2: /* To */ if((turi=parse_to_uri(_m))==NULL) { LM_ERR("failed to get To URI\n"); return -1; } break; case 3: /* From */ if((turi=parse_from_uri(_m))==NULL) { LM_ERR("failed to get From URI\n"); return -1; } break; case 4: /* Credentials */ get_authorized_cred(_m->authorization, &h); if (!h) { get_authorized_cred(_m->proxy_auth, &h); if (!h) { LM_ERR("no authorized" " credentials found (error in scripts)\n"); return -4; } } cred = &((auth_body_t*)(h->parsed))->digest; break; } if (hf_type != 4) { user = turi->user; domain = turi->host; } else { user = cred->username.user; domain = *GET_REALM(cred); } if (user.s == NULL || user.len == 0) { LM_DBG("no username part\n"); return -1; } if (use_domain) { user_name.len = user.len + domain.len + 1; user_name.s = (char*)pkg_malloc(user_name.len); if (!user_name.s) { LM_ERR("no pkg memory left\n"); return -6; } memcpy(user_name.s, user.s, user.len); user_name.s[user.len] = '@'; memcpy(user_name.s + user.len + 1, domain.s, domain.len); } else { user_name = user; } if ((send = proto.create_aaa_message(conn, AAA_AUTH)) == NULL) { LM_ERR("failed to create new aaa message for auth \n"); return -1; } if (proto.avp_add(conn, send, &attrs[A_USER_NAME], user_name.s, user_name.len, 0)) { proto.destroy_aaa_message(conn, send); if (use_domain) pkg_free(user_name.s); return -7; } if (use_domain) pkg_free(user_name.s); if (proto.avp_add(conn, send, &attrs[A_SIP_GROUP], grp->s, grp->len, 0)) { proto.destroy_aaa_message(conn, send); LM_ERR("failed to add Sip-Group attribute\n"); return -8; } service = vals[V_GROUP_CHECK].value; if (proto.avp_add(conn, send, &attrs[A_SERVICE_TYPE], &service, -1, 0)) { proto.destroy_aaa_message(conn, send); LM_ERR("failed to add Service-Type attribute\n"); return -8; } /* Add CALL-ID in Acct-Session-Id Attribute */ if ((parse_headers(_m, HDR_CALLID_F, 0) == -1 || _m->callid == NULL) && _m->callid == NULL) { proto.destroy_aaa_message(conn, send); LM_ERR("msg parsing failed or callid not present"); return -10; } if (proto.avp_add(conn, send, &attrs[A_ACCT_SESSION_ID], _m->callid->body.s, _m->callid->body.len, 0)) { proto.destroy_aaa_message(conn, send); LM_ERR("unable to add CALL-ID attribute\n"); return -11; } if (!proto.send_aaa_request(conn, send, &received)) { LM_DBG("Success\n"); proto.destroy_aaa_message(conn, send); proto.destroy_aaa_message(conn, received); return 1; } else { LM_DBG("Failure\n"); proto.destroy_aaa_message(conn, send); proto.destroy_aaa_message(conn, received); return -12; } } opensips-2.2.2/modules/group/group.h000066400000000000000000000035251300170765700175010ustar00rootroot00000000000000/* * Group membership * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-25 - created by janakj * */ #ifndef GROUP_H #define GROUP_H #include "../../parser/msg_parser.h" #include "../../pvar.h" typedef struct _group_check { int id; pv_spec_t sp; } group_check_t, *group_check_p; /* * extracts username and domain from MSG */ int get_username_domain(struct sip_msg *msg, str *hf_s, str *username, str *domain); /* * Check if username in specified header field is in a table */ int db_is_user_in(struct sip_msg* _msg, char* _hf, char* _grp); /* * Check from AAA if a user belongs to a group. User-Name is digest * username or digest username@realm, SIP-Group is group, and Service-Type * is Group-Check. SIP-Group is SER specific attribute and Group-Check is * SER specific service type value. */ int aaa_is_user_in(struct sip_msg* _msg, char* _hf, char* _group); int group_db_init(const str* db_url); int group_db_bind(const str* db_url); void group_db_close(); #endif /* GROUP_H */ opensips-2.2.2/modules/group/group_mod.c000066400000000000000000000240531300170765700203320ustar00rootroot00000000000000/* * Group membership - module interface * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-25 - created by janakj * 2003-03-11 - New module interface (janakj) * 2003-03-16 - flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free * 2003-04-05 default_uri #define used (jiri) * 2004-06-07 updated to the new DB api: calls to group_db_* (andrei) * 2005-10-06 - added support for regexp-based groups (bogdan) * 2008-12-26 pseudovar argument for group parameter at is_user_in (saguti). * 2009-08-07 - joined with group_radius module to support generic AAA group * requests (Irina Stanescu) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../mem/mem.h" #include "group_mod.h" #include "group.h" #include "re_group.h" #include "../../mod_fix.h" #include "../../aaa/aaa.h" #define TABLE_VERSION 3 #define RE_TABLE_VERSION 2 /* * Module destroy function prototype */ static void destroy(void); /* * Module child-init function prototype */ static int child_init(int rank); /* * Module initialization function prototype */ static int mod_init(void); static int aaa_is_user_fixup(void** param, int param_no); static int db_is_user_fixup(void** param, int param_no); static int db_get_gid_fixup(void** param, int param_no); static int obsolete_fixup_0(void** param, int param_no); static int obsolete_fixup_1(void** param, int param_no); #define TABLE "grp" #define TABLE_LEN (sizeof(TABLE) - 1) #define USER_COL "username" #define USER_COL_LEN (sizeof(USER_COL) - 1) #define DOMAIN_COL "domain" #define DOMAIN_COL_LEN (sizeof(DOMAIN_COL) - 1) #define GROUP_COL "grp" #define GROUP_COL_LEN (sizeof(GROUP_COL) - 1) #define RE_TABLE "re_grp" #define RE_TABLE_LEN (sizeof(TABLE) - 1) #define RE_EXP_COL "reg_exp" #define RE_EXP_COL_LEN (sizeof(USER_COL) - 1) #define RE_GID_COL "group_id" #define RE_GID_COL_LEN (sizeof(DOMAIN_COL) - 1) /* * Module parameter variables */ static str db_url = {NULL, 0}; static str aaa_proto_url = {NULL, 0}; /* Table name where group definitions are stored */ str table = {TABLE, TABLE_LEN}; str user_column = {USER_COL, USER_COL_LEN}; str domain_column = {DOMAIN_COL, DOMAIN_COL_LEN}; str group_column = {GROUP_COL, GROUP_COL_LEN}; int use_domain = 0; /* tabel and columns used for re-based groups */ str re_table = {0, 0}; str re_exp_column = {RE_EXP_COL, RE_EXP_COL_LEN}; str re_gid_column = {RE_GID_COL, RE_GID_COL_LEN}; int multiple_gid = 1; /* DB functions and handlers */ db_func_t group_dbf; db_con_t* group_dbh = 0; aaa_conn *conn = NULL; aaa_prot proto = {NULL, NULL, NULL, NULL, NULL, NULL, NULL}; aaa_map attrs[A_MAX]; aaa_map vals[V_MAX]; /* * Exported functions */ static cmd_export_t cmds[] = { {"is_user_in", (cmd_function)NULL, 2, obsolete_fixup_0, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"get_user_group", (cmd_function)NULL, 2, obsolete_fixup_1, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"aaa_is_user_in", (cmd_function)aaa_is_user_in, 2, aaa_is_user_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"db_is_user_in", (cmd_function)db_is_user_in, 2, db_is_user_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"db_get_user_group", (cmd_function)get_user_group, 2, db_get_gid_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"aaa_url", STR_PARAM, &aaa_proto_url.s}, {"db_url", STR_PARAM, &db_url.s }, {"table", STR_PARAM, &table.s }, {"user_column", STR_PARAM, &user_column.s }, {"domain_column", STR_PARAM, &domain_column.s}, {"group_column", STR_PARAM, &group_column.s }, {"use_domain", INT_PARAM, &use_domain }, {"re_table", STR_PARAM, &re_table.s }, {"re_exp_column", STR_PARAM, &re_exp_column.s}, {"re_gid_column", STR_PARAM, &re_gid_column.s}, {"multiple_gid", INT_PARAM, &multiple_gid }, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_AAA, NULL, DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "db_url", get_deps_sqldb_url }, { NULL, NULL }, }, }; /* * Module interface */ struct module_exports exports = { "group", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; static int child_init(int rank) { if (db_url.s) return group_db_init(&db_url); LM_DBG("db_url is null\n"); return 0; } static int mod_init(void) { LM_DBG("group module - initializing\n"); /* check for a database module */ if (db_url.s) { db_url.len = strlen(db_url.s); table.len = strlen(table.s); user_column.len = strlen(user_column.s); domain_column.len = strlen(domain_column.s); group_column.len = strlen(group_column.s); re_table.len = (re_table.s && re_table.s[0])?strlen(re_table.s):0; re_exp_column.len = strlen(re_exp_column.s); re_gid_column.len = strlen(re_gid_column.s); if (group_db_bind(&db_url)) { LM_ERR("unable to bind database module\n"); return -1; } if (group_db_init(&db_url) < 0 ){ LM_ERR("unable to open database connection\n"); return -1; } /* check version for group table */ if (db_check_table_version(&group_dbf, group_dbh, &table, TABLE_VERSION) < 0) { LM_ERR("error during group table version check.\n"); return -1; } if (re_table.len) { /* check version for group re_group table */ if (db_check_table_version(&group_dbf, group_dbh, &re_table, RE_TABLE_VERSION) < 0) { LM_ERR("error during re_group table version check.\n"); return -1; } if (load_re( &re_table )!=0 ) { LM_ERR("failed to load <%s> table\n", re_table.s); return -1; } } group_db_close(); LM_DBG("group database loaded\n"); } /* check for an aaa module */ if (aaa_proto_url.s) { aaa_proto_url.len = strlen(aaa_proto_url.s); memset(attrs, 0, sizeof(attrs)); memset(vals, 0, sizeof(vals)); attrs[A_SERVICE_TYPE].name = "Service-Type"; attrs[A_USER_NAME].name = "User-Name"; attrs[A_SIP_GROUP].name = "Sip-Group"; attrs[A_ACCT_SESSION_ID].name = "Acct-Session-Id"; vals[V_GROUP_CHECK].name = "Group-Check"; if (aaa_prot_bind(&aaa_proto_url, &proto)) { LM_ERR("unable to bind aaa protocol module\n"); return -1; } if (!(conn = proto.init_prot(&aaa_proto_url))) { LM_ERR("unable to initialize aaa protocol module\n"); return -1; } INIT_AV(proto, conn, attrs, A_MAX, vals, V_MAX, "group", -3, -4); LM_DBG("aaa protocol module loaded\n"); } return 0; } static void destroy(void) { if (!db_url.s) group_db_close(); } static int obsolete_fixup_0(void** param, int param_no) { LM_ERR("You are using is_user_in function that is now obsolete. \ If you want to use it with DB support, use db_is_user_in. \ If you want to use it with AAA support, use aaa_is_user_in.\n"); return E_CFG; } static int obsolete_fixup_1(void** param, int param_no) { LM_ERR("You are get_user_group function that has been renamed\ into db_get_user_group\n"); return E_CFG; } static int db_get_gid_fixup(void** param, int param_no) { pv_spec_t *sp; str name; if (!db_url.s) { LM_ERR("no database url\n"); return E_CFG; } if (param_no == 1) { return fixup_spve_spve(param, param_no); } else if (param_no == 2) { name.s = (char*)*param; name.len = strlen(name.s); sp = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); if (sp == NULL) { LM_ERR("no more pkg memory\n"); return E_UNSPEC; } if(pv_parse_spec(&name, sp)==NULL || sp->type!=PVT_AVP) { LM_ERR("bad AVP spec <%s>\n", name.s); pv_spec_free(sp); return E_UNSPEC; } *param = sp; } return 0; } static int aaa_is_user_fixup(void** param, int param_no) { void* ptr; str* s; if (!aaa_proto_url.s) { LM_ERR("no aaa protocol url\n"); return E_CFG; } if (param_no == 1) { ptr = *param; if (!strcasecmp((char*)*param, "Request-URI")) { *param = (void*)1; } else if (!strcasecmp((char*)*param, "To")) { *param = (void*)2; } else if (!strcasecmp((char*)*param, "From")) { *param = (void*)3; } else if (!strcasecmp((char*)*param, "Credentials")) { *param = (void*)4; } else { LM_ERR("unsupported Header Field identifier\n"); return E_UNSPEC; } pkg_free(ptr); } else if (param_no == 2) { s = (str*)pkg_malloc(sizeof(str)); if (!s) { LM_ERR("no pkg memory left\n"); return E_UNSPEC; } s->s = (char*)*param; s->len = strlen(s->s); *param = (void*)s; } return 0; } static int db_is_user_fixup(void** param, int param_no) { if (db_url.s) { fixup_spve_spve(param, param_no); return 0; } LM_ERR("no database url\n"); return E_CFG; } opensips-2.2.2/modules/group/group_mod.h000066400000000000000000000035201300170765700203330ustar00rootroot00000000000000/* * Group membership * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-25 - created by janakj */ #ifndef GROUP_MOD_H #define GROUP_MOD_H #include "../../db/db.h" #include "../../str.h" #include "../../parser/digest/digest.h" /* auth_body_t */ #include "../../parser/msg_parser.h" /* struct sip_msg */ #include "../../aaa/aaa.h" /* * Module parameters variables */ extern str table; /* 'group' table name */ extern str user_column; /* 'user' column name in group table */ extern str domain_column; /* 'domain' column name in group table */ extern str group_column; /* "group' column name in group table */ extern int use_domain; /* Use domain in is_user_in */ extern str re_table; extern str re_exp_column; extern str re_gid_column; extern int multiple_gid; /* DB functions and handlers */ extern db_func_t group_dbf; extern db_con_t* group_dbh; extern aaa_map attrs[]; extern aaa_map vals[]; extern aaa_conn *conn; extern aaa_prot proto; #endif /* GROUP_MOD_H */ opensips-2.2.2/modules/group/re_group.c000066400000000000000000000104541300170765700201610ustar00rootroot00000000000000/* * Copyright (C) 2005-2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-10-06 - created by bogdan */ #include #include #include "../../mod_fix.h" #include "../../str.h" #include "../../mem/mem.h" #include "../../route_struct.h" #include "group_mod.h" #include "re_group.h" #include "group.h" struct re_grp { regex_t re; int_str gid; struct re_grp *next; }; static struct re_grp *re_list = 0; static int add_re(const char *re, int gid) { struct re_grp *rg; LM_DBG("adding <%s> with %d\n",re, gid); rg = (struct re_grp*)pkg_malloc(sizeof(struct re_grp)); if (rg==0) { LM_ERR("no more pkg mem\n"); goto error; } memset( rg, 0, sizeof(struct re_grp)); if (regcomp(&rg->re, re, REG_EXTENDED|REG_ICASE|REG_NEWLINE) ) { LM_ERR("bad re %s\n", re); pkg_free(rg); goto error; } rg->gid.n = gid; rg->next = re_list; re_list = rg; return 0; error: return -1; } int load_re( str *table ) { db_key_t cols[2]; db_res_t* res = NULL; db_row_t* row; int n; cols[0] = &re_exp_column; cols[1] = &re_gid_column; if (group_dbf.use_table(group_dbh, table) < 0) { LM_ERR("failed to set table <%s>\n", table->s); goto error; } if (group_dbf.query(group_dbh, 0, 0, 0, cols, 0, 2, 0, &res) < 0) { LM_ERR("failed to query database\n"); goto error; } for( n=0 ; nrows[n]; /* validate row */ if (row->values[0].nul || row->values[0].type!=DB_STRING) { LM_ERR("empty or non-string " "value for <%s>(re) column\n",re_exp_column.s); goto error1; } if (row->values[1].nul || row->values[1].type!=DB_INT) { LM_ERR("empty or non-integer " "value for <%s>(gid) column\n",re_gid_column.s); goto error1; } if ( add_re( row->values[0].val.string_val, row->values[1].val.int_val)!=0 ) { LM_ERR("failed to add row\n"); goto error1; } } LM_DBG("%d rules were loaded\n", n); group_dbf.free_result(group_dbh, res); return 0; error1: group_dbf.free_result(group_dbh, res); error: return -1; } int get_user_group(struct sip_msg *req, char *user, char *avp) { static char uri_buf[MAX_URI_SIZE]; str user_str; str username; str domain; pv_spec_t *pvs; pv_value_t val; struct re_grp *rg; regmatch_t pmatch; char *c; unsigned int aux; int n; if(user == NULL || fixup_get_svalue(req, (gparam_p)user, &user_str) != 0){ LM_ERR("Invalid parameter URI\n"); return -1; } if (get_username_domain( req, &user_str, &username, &domain)!=0){ LM_ERR("failed to get username@domain\n"); goto error; } if (username.s==NULL || username.len==0 ) { LM_DBG("no username part\n"); return -1; } if ( 4 + username.len + 1 + domain.len + 1 > MAX_URI_SIZE ) { LM_ERR("URI to large!!\n"); goto error; } aux = htonl(('s'<<24) + ('i'<<16) + ('p'<<8) + ':'); memcpy(uri_buf, &aux, 4); c = uri_buf + 4; memcpy( c, username.s, username.len); c += username.len; *(c++) = '@'; memcpy( c, domain.s, domain.len); c += domain.len; *c = 0; LM_DBG("getting groups for <%s>\n",uri_buf); pvs = (pv_spec_t*)avp; memset(&val, 0, sizeof(pv_value_t)); val.flags = PV_VAL_INT|PV_TYPE_INT; /* check against all re groups */ for( rg=re_list,n=0 ; rg ; rg=rg->next ) { if (regexec( &rg->re, uri_buf, 1, &pmatch, 0)==0) { LM_DBG("user matched to group %d!\n", rg->gid.n); /* match -> add the gid as AVP */ val.ri = rg->gid.n; if(pv_set_value(req, pvs, (int)EQ_T, &val)<0) { LM_ERR("setting PV AVP failed\n"); goto error; } n++; /* continue? */ if (multiple_gid==0) break; } } return n?n:-1; error: return -1; } opensips-2.2.2/modules/group/re_group.h000066400000000000000000000020671300170765700201670ustar00rootroot00000000000000/* * Copyright (C) 2005-2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-10-06 - created by bogdan */ #ifndef RE_GROUP_H #define RE_GROUP_H #include "../../str.h" #include "../../parser/msg_parser.h" int load_re(str *table); int get_user_group(struct sip_msg *req, char *user, char *avp); #endif opensips-2.2.2/modules/h350/000077500000000000000000000000001300170765700155125ustar00rootroot00000000000000opensips-2.2.2/modules/h350/Makefile000066400000000000000000000002751300170765700171560ustar00rootroot00000000000000# $Id$ # # H350 # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=h350.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/h350/README000066400000000000000000000431101300170765700163710ustar00rootroot00000000000000H350 Module Christian Schlatter University of North Carolina Copyright © 2007 University of North Carolina Revision History Revision 0.1 05-17-2007 __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Example H.350 commObject LDAP Entry 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. ldap_session (string) 1.3.2. base_dn (string) 1.3.3. search_scope (string) 1.4. Exported Functions 1.4.1. h350_sipuri_lookup(sip_uri) 1.4.2. h350_auth_lookup(auth_username, "username_avp_spec/pwd_avp_spec") 1.4.3. h350_result_call_preferences(avp_name_prefix) 1.4.4. h350_result_service_level(avp_name_prefix) Resources List of Examples 1.1. Example H.350 commObject storing SIP account data 1.2. ldap_session parameter usage 1.3. base_dn parameter usage 1.4. search_scope parameter usage 1.5. Example Usage 1.6. Example Usage 1.7. Example H.350 callPreferenceURI simple call forwarding rules 1.8. Example Usage 1.9. Example SIPIdentityServiceLevel values and resulting AVPs 1.10. Example Usage Chapter 1. Admin Guide 1.1. Overview The OpenSIPS H350 module enables an OpenSIPS SIP proxy server to access SIP account data stored in an LDAP [RFC4510] directory containing H.350 [H.350] commObjects. ITU-T Recommendation H.350 standardizes LDAP object classes to store Real-Time Communication (RTC) account data. In particular, H.350.4 [H.350.4] defines an object class called sipIdentity that includes attribute specifications for SIP account data like SIP URI, SIP digest username/password, or service level. This allows to store SIP account data in a vendor neutral way and lets different entities, like SIP proxies, provisioning, or billing applications, access the data in a standardized format. The ViDe H.350 Cookbook [vide-h.350-cb] is a good reference for deploying an H.350 directory. Besides general information on H.350, LDAP, and related standards, this document explains how to set up an H.350/LDAP directory and discusses different deployment scenarios. The H350 module uses the OpenSIPS LDAP module to import H.350 attribute values into the OpenSIPS routing script variable space. The module exports functions to parse and store the H.350 attribute values from the OpenSIPS routing script. It allows a script writer to implement H.350 based SIP digest authentication, call forwarding, SIP URI alias to AOR rewriting, and service level parsing. 1.1.1. Example H.350 commObject LDAP Entry The following example shows a typical H.350 commObject LDAP entry storing SIP account data. Example 1.1. Example H.350 commObject storing SIP account data Attribute Name Attribute Value(s) -------------- ----------------- # LDAP URI identifying the owner of this commObject, typically # points to an entry in the enterprise directory commOwner ldap://dir.example.com/dc=example,dc=com??one?(uid=bob) # Unique identifier for this commObject, used for referencing # this object e.g. from the enterprise directory commUniqueId 298217asdjgj213 # Determines if this commObject should be listed on white pages commPrivate false # Valid SIP URIs for this account (can be used to store alias SIP URIs # like DIDs as well) SIPIdentitySIPURI sip:bob@example.com sip:bob@alias.example.com sip:+1919123456@alias.example.com # SIP digest username SIPIdentityUserName bob # SIP digest password SIPIdentityPassword pwd # SIP proxy address SIPIdentityProxyAddress sip.example.com # SIP registrar address SIPIdentityRegistrarAddress sip.example.com # Call preferences: Forward to voicemail on no response # after 20 seconds and on busy callPreferenceURI sip:bob@voicemail.example.com n:20000 sip:bob@voicemail.example.com b # Account service level(s) SIPIdentityServiceLevel long_distance conferencing # H.350 object classes objectClass top commObject SIPIdentity callPreferenceURIObject 1.2. Dependencies 1.2.1. OpenSIPS Modules The module depends on the following modules (the listed modules must be loaded before this module): * LDAP 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * OpenLDAP library (libldap), libldap header files (libldap-dev) are needed for compilation 1.3. Exported Parameters 1.3.1. ldap_session (string) Name of the LDAP session to be used for H.350 queries, as defined in the LDAP module configuration file. Default value: "" Example 1.2. ldap_session parameter usage modparam("h350", "ldap_session", "h350"); 1.3.2. base_dn (string) Base LDAP DN to start LDAP search for H.350 entries. For best performance, this should be set to the direct ancestor of the H.350 objects. Default value: "" Example 1.3. base_dn parameter usage modparam("h350", "base_dn", "ou=h350,dc=example,dc=com"); 1.3.3. search_scope (string) LDAP search scope for H.350 queries, one of "one", "base", or "sub". Default value: "one" Example 1.4. search_scope parameter usage modparam("h350", "search_scope", "sub"); 1.4. Exported Functions 1.4.1. h350_sipuri_lookup(sip_uri) This function performs an LDAP search query for an H.350 commObject with a SIPIdentitySIPURI of sip_uri. The sip_uri parameter first gets escaped according the rules for LDAP filter strings. The result of the LDAP search is stored internally and can be accessed either by one of the h350_result* or one of the ldap_result* functions from the OpenSIPS LDAP module. The function returns -1 (FALSE) for internal errors, and -2 (FALSE) if no H.350 commObject was found with a matching sip_uri. n > 0 (TRUE) is returned if n H.350 commObjects were found. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Function Parameters: sip_uri H.350 SIPIdentitySIPURI to search for in directory. Included OpenSIPS variables do get expanded. Return Values: n > 0 (TRUE): + n H.350 commObjects found. -1 (FALSE): + Internal error occurred. -2 (FALSE): + No H.350 commObject found. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.5. Example Usage # # H.350 lookup for callee # if (!h350_sipuri_lookup("sip:$rU@$rd")) { switch ($retcode) { case -2: xlog("L_INFO", "h350 callee lookup: no entry found in H.350 directory"); exit; case -1: sl_send_reply("500", "Internal server error"); exit; } } # now h350_result* or ldap_result* functions can be used 1.4.2. h350_auth_lookup(auth_username, "username_avp_spec/pwd_avp_spec") This function performs an LDAP search query for SIP digest authentication credentials in an H.350 directory. The H.350 directory is searched for a commObject with SIPIdentityUserName of auth_username. If such a commObject is found, the SIP digest authentication username and password are stored in AVPs username_avp_spec and pwd_avp_spec, respectively. pv_*_authorize functions from AUTH module can then be used to perform SIP digest authentication. The function returns 1 (TRUE) if an H.350 commObject was found, -1 (FALSE) in case of an internal error, and -2 (FALSE) if no matching commObject was found. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Function Parameters: auth_username H.350 SIPIdentityUserName to search for in directory. Included OpenSIPS variables do get expanded. username_avp_spec Specification for authentication username AVP, e.g. $avp(username). pwd_avp_spec Specification for authentication password AVP, e.g. $avp(pwd). Return Values: 1 (TRUE): + H.350 commObject found and SIP digest authentication credentials stored in username_avp_spec and pwd_avp_spec. -1 (FALSE): + Internal error occurred. -2 (FALSE): + No H.350 commObject found. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.6. Example Usage # -- auth params -- modparam("auth", "username_spec", "$avp(auth_user)") modparam("auth", "password_spec", "$avp(auth_pwd)") modparam("auth", "calculate_ha1", 1) # -- h350 params -- modparam("h350", "ldap_session", "h350") modparam("h350", "base_dn", "ou=h350,dc=example,dc=com") modparam("h350", "search_scope", "one") route[1] { # # H.350 based SIP digest authentication # # challenge all requests not including an Auth header if (!(is_present_hf("Authorization") || is_present_hf("Proxy-Authorization"))) { if (is_method("REGISTER")) { www_challenge("example.com", "0"); exit; } proxy_challenge("example.com", "0"); exit; } # get digest password from H.350 using auth username ($au) if (!h350_auth_lookup("$au", "$avp(auth_user)/$avp(auth_pwd)")) { switch ($retcode) { case -2: sl_send_reply("401", "Unauthorized"); exit; case -1: sl_send_reply("500", "Internal server error"); exit; } } # REGISTER requests if (is_method("REGISTER")) { if (!pv_www_authorize("example.com")) { if ($retcode == -5) { sl_send_reply("500", "Internal server error"); exit; } else { www_challenge("example.com", "0"); exit; } } consume_credentials(); xlog("L_INFO", "REGISTER request successfully authenticated"); return(1); } # non-REGISTER requests if (!pv_proxy_authorize("example.com")) { if ($retcode == -5) { sl_send_reply("500", "Internal server error"); exit; } else { proxy_challenge("example.com", "0"); exit; } } consume_credentials(); xlog("L_INFO", "$rm request successfully authenticated"); return(1); } 1.4.3. h350_result_call_preferences(avp_name_prefix) This function parses the callPreferenceURI attribute of an H.350 commObject, which must have been fetched through h350_*_lookup or ldap_search. callPreferenceURI is a multi-valued attribute that stores call preference rules like e.g. forward-on-busy or forward-unconditionally. Directory services architecture for call forwarding and preferences [H.350.6] defines a format for simple call forwarding rules: target_uri type[:argument] In a SIP environment, target_uri is typically the call forwarding rule's target SIP URI, although it could be any type of URI, e.g. an HTTP pointer to a CPL script. Four different values are specified for type: b for "forward on busy", n for "forward on no answer", u for "forward unconditionally", and f for "forward on destination not found". The optional argument is a string indicating the time in milliseconds after which the call forwarding should occur. Example 1.7. Example H.350 callPreferenceURI simple call forwarding rules # Example 1: # forward to sip:voicemail@example.com on no answer after 15 seconds: callPreferenceURI: sip:voicemail@example.com n:15000 # Example 2: # unconditionally forward to sip:alice@example.com: callPreferenceURI: sip:alice@example.com u # Example 3: # forward to sip:bob@example.com and sip:alice@example.com # (forking) on destination not found: callPreferenceURI: sip:bob@example.com f callPreferenceURI: sip:alice@example.com f h350_result_call_preferences stores these call forwarding rules as AVPs according to the following rules: # # AVP storing a forwarding rule's target URI # AVP name = avp_name_prefix + '_' + type AVP value = target_uri # # AVP storing a forwarding rule's argument # AVP name = avp_name_prefix + '_' + type + '_t' AVP value = argument / 1000 Example 1 from above would result in two AVPs: $avp("prefix_n") = "sip:voicemail@example.com" and $avp("prefix_n_t") = 15. Example 2: $avp("prefix_u") = "sip:alice@example.com". Example 3: $avp("prefix_f[1]") = "sip:bob@example.com" and $avp("prefix_f[2]]") = "sip:alice@example.com". These AVPs can then be used to implement the desired behavior in the OpenSIPS routing script. This function returns the number of successfully parsed simple call forwarding rules (TRUE), in case the H.350 callPreferenceURI attribute contained one or multiple values matching the simple call forwarding rule syntax described above. It returns -1 (FALSE) for internal errors, and -2 (FALSE) if none of the rules matched or if no callPreferenceURI attribute was found. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Function Parameters: avp_name_prefix Name prefix for call forwarding rule AVPs, as described above. Return Values: n > 0 (TRUE): + n simple call forwarding rules found. -1 (FALSE): + Internal error occurred. -2 (FALSE): + No simple call forwarding rule found, or callPreferenceURI not present. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.8. Example Usage # # H.350 lookup for callee # ... h350_sipuri_lookup("sip:$rU@$rd") ... # # store H.350 call preferences in AVP # if (!h350_result_call_preferences("callee_pref_") && ($retcode == -1)) { sl_send_reply("500", "Internal server error"); exit; } # $avp(callee_pref_u) == CFU URI(s) # $avp(callee_pref_n) == CFNR URI(s) # $avp(callee_pref_n_t) == CFNR timeout in seconds # $avp(callee_pref_b) == CFB URI(s) # $avp(callee_pref_f) == CFOFFLINE URI(s) # # Example for forward-unconditionally (CFU) # if (is_avp_set("$avp(callee_pref_u)")) { # replace R-URI with CFU URI, g will fetch all CFU URIs # --> request can fork if (avp_pushto("$ru", "$avp(callee_pref_u)/g")) { avp_delete("$avp(callee_pref_u)"); } else { sl_send_reply("500", "Internal server error"); exit; } sl_send_reply("181", "Call is being forwarded"); t_relay(); exit; } 1.4.4. h350_result_service_level(avp_name_prefix) Directory services architecture for SIP [H.350.4] defines a multi-valued LDAP attribute named SIPIdentityServiceLevel, which can be used to store SIP account service level values in an LDAP directory. This function parses the SIPIdentityServiceLevel attribute and stores all service level values as AVPs for later retrieval in the OpenSIPS routing script. The function accesses the H.350 commObject fetched by a call to h350_*_lookup or ldap_search. The resulting AVPs have a name of the form avp_name_prefix + SIPIdentityServiceLevel attribute value, and an integer value of 1. Example 1.9. Example SIPIdentityServiceLevel values and resulting AVPs SIPIdentityServiceLevel: longdistance SIPIdentityServiceLevel: international SIPIdentityServiceLevel: 900 after calling h350_result_service_level("sl_"), the following AVPs will be available in the routing script: $avp("sl_longdistance") = 1 $avp("sl_international") = 1 $avp("sl_900") = 1 This function returns the number of added AVPs (TRUE), -1 (FALSE)for internal errors, and -2 (FALSE)if no SIPIdentityServiceLevel attribute was found. The function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Function Parameters: avp_name_prefix Name prefix for service level AVPs, as described above. Return Values: n > 0 (TRUE): + n AVPs added. -1 (FALSE): + Internal error occurred. -2 (FALSE): + No SIPIdentityServiceLevel attribute found. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.10. Example Usage # # H.350 SIP digest authentication for caller # ... h350_auth_lookup("$au", ...) ... # # store caller's service level as AVP # if (!h350_result_service_level("caller_sl_") && ($retcode == -1)) { sl_send_reply("500", "Internal server error"); exit; } # # make routing decision based on service level AVPs # if (is_avp_set("$avp(caller_sl_international)")) { t_relay(); } else { sl_send_reply("403", "Forbidden"); } exit; Resources [H.350] Directory Services Architecture for Multimedia Conferencing. August 2003. ITU-T. [H.350.4] Directory services architecture for SIP. August 2003. ITU-T. [H.350.6] Directory services architecture for call forwarding and preferences. March 2004. ITU-T. [ViDe-H.350-Cookbook] ViDe H.350 Cookbook. 2005. ViDe. [RFC4510] Lightweight Directory Access Protocol (LDAP): Technical Specification Road Map. June 2006. Internet Engineering Task Force. opensips-2.2.2/modules/h350/doc/000077500000000000000000000000001300170765700162575ustar00rootroot00000000000000opensips-2.2.2/modules/h350/doc/h350.xml000066400000000000000000000023701300170765700174620ustar00rootroot00000000000000 %docentities; ]> H350 Module &osips; Christian Schlatter University of North Carolina cs@unc.edu 2007 University of North Carolina 0.1 05-17-2007 &admin; &faq; &biblio; opensips-2.2.2/modules/h350/doc/h350_admin.xml000066400000000000000000000652501300170765700206400ustar00rootroot00000000000000 &adminguide;
Overview The OpenSIPS H350 module enables an OpenSIPS SIP proxy server to access SIP account data stored in an LDAP directory containing H.350 commObjects. ITU-T Recommendation H.350 standardizes LDAP object classes to store Real-Time Communication (RTC) account data. In particular, H.350.4 defines an object class called sipIdentity that includes attribute specifications for SIP account data like SIP URI, SIP digest username/password, or service level. This allows to store SIP account data in a vendor neutral way and lets different entities, like SIP proxies, provisioning, or billing applications, access the data in a standardized format. The ViDe H.350 Cookbook is a good reference for deploying an H.350 directory. Besides general information on H.350, LDAP, and related standards, this document explains how to set up an H.350/LDAP directory and discusses different deployment scenarios. The H350 module uses the OpenSIPS LDAP module to import H.350 attribute values into the OpenSIPS routing script variable space. The module exports functions to parse and store the H.350 attribute values from the OpenSIPS routing script. It allows a script writer to implement H.350 based SIP digest authentication, call forwarding, SIP URI alias to AOR rewriting, and service level parsing.
Example H.350 commObject LDAP Entry The following example shows a typical H.350 commObject LDAP entry storing SIP account data. Example H.350 commObject storing SIP account data Attribute Name Attribute Value(s) -------------- ----------------- # LDAP URI identifying the owner of this commObject, typically # points to an entry in the enterprise directory commOwner ldap://dir.example.com/dc=example,dc=com??one?(uid=bob) # Unique identifier for this commObject, used for referencing # this object e.g. from the enterprise directory commUniqueId 298217asdjgj213 # Determines if this commObject should be listed on white pages commPrivate false # Valid SIP URIs for this account (can be used to store alias SIP URIs # like DIDs as well) SIPIdentitySIPURI sip:bob@example.com sip:bob@alias.example.com sip:+1919123456@alias.example.com # SIP digest username SIPIdentityUserName bob # SIP digest password SIPIdentityPassword pwd # SIP proxy address SIPIdentityProxyAddress sip.example.com # SIP registrar address SIPIdentityRegistrarAddress sip.example.com # Call preferences: Forward to voicemail on no response # after 20 seconds and on busy callPreferenceURI sip:bob@voicemail.example.com n:20000 sip:bob@voicemail.example.com b # Account service level(s) SIPIdentityServiceLevel long_distance conferencing # H.350 object classes objectClass top commObject SIPIdentity callPreferenceURIObject
Dependencies
OpenSIPS Modules The module depends on the following modules (the listed modules must be loaded before this module): LDAP
External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: OpenLDAP library (libldap), libldap header files (libldap-dev) are needed for compilation
Exported Parameters
ldap_session (string) Name of the LDAP session to be used for H.350 queries, as defined in the LDAP module configuration file. Default value: "" <varname>ldap_session</varname> parameter usage modparam("h350", "ldap_session", "h350");
base_dn (string) Base LDAP DN to start LDAP search for H.350 entries. For best performance, this should be set to the direct ancestor of the H.350 objects. Default value: "" <varname>base_dn</varname> parameter usage modparam("h350", "base_dn", "ou=h350,dc=example,dc=com");
search_scope (string) LDAP search scope for H.350 queries, one of "one", "base", or "sub". Default value: "one" <varname>search_scope</varname> parameter usage modparam("h350", "search_scope", "sub");
Exported Functions
h350_sipuri_lookup(sip_uri) This function performs an LDAP search query for an H.350 commObject with a SIPIdentitySIPURI of sip_uri. The sip_uri parameter first gets escaped according the rules for LDAP filter strings. The result of the LDAP search is stored internally and can be accessed either by one of the h350_result* or one of the ldap_result* functions from the OpenSIPS LDAP module. The function returns -1 (FALSE) for internal errors, and -2 (FALSE) if no H.350 commObject was found with a matching sip_uri. n > 0 (TRUE) is returned if n H.350 commObjects were found. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Function Parameters: sip_uri H.350 SIPIdentitySIPURI to search for in directory. Included OpenSIPS variables do get expanded. Return Values: n > 0 (TRUE): n H.350 commObjects found. -1 (FALSE): Internal error occurred. -2 (FALSE): No H.350 commObject found. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage # # H.350 lookup for callee # if (!h350_sipuri_lookup("sip:$rU@$rd")) { switch ($retcode) { case -2: xlog("L_INFO", "h350 callee lookup: no entry found in H.350 directory"); exit; case -1: sl_send_reply("500", "Internal server error"); exit; } } # now h350_result* or ldap_result* functions can be used
h350_auth_lookup(auth_username, "username_avp_spec/pwd_avp_spec") This function performs an LDAP search query for SIP digest authentication credentials in an H.350 directory. The H.350 directory is searched for a commObject with SIPIdentityUserName of auth_username. If such a commObject is found, the SIP digest authentication username and password are stored in AVPs username_avp_spec and pwd_avp_spec, respectively. pv_*_authorize functions from AUTH module can then be used to perform SIP digest authentication. The function returns 1 (TRUE) if an H.350 commObject was found, -1 (FALSE) in case of an internal error, and -2 (FALSE) if no matching commObject was found. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Function Parameters: auth_username H.350 SIPIdentityUserName to search for in directory. Included OpenSIPS variables do get expanded. username_avp_spec Specification for authentication username AVP, e.g. $avp(username). pwd_avp_spec Specification for authentication password AVP, e.g. $avp(pwd). Return Values: 1 (TRUE): H.350 commObject found and SIP digest authentication credentials stored in username_avp_spec and pwd_avp_spec. -1 (FALSE): Internal error occurred. -2 (FALSE): No H.350 commObject found. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage # -- auth params -- modparam("auth", "username_spec", "$avp(auth_user)") modparam("auth", "password_spec", "$avp(auth_pwd)") modparam("auth", "calculate_ha1", 1) # -- h350 params -- modparam("h350", "ldap_session", "h350") modparam("h350", "base_dn", "ou=h350,dc=example,dc=com") modparam("h350", "search_scope", "one") route[1] { # # H.350 based SIP digest authentication # # challenge all requests not including an Auth header if (!(is_present_hf("Authorization") || is_present_hf("Proxy-Authorization"))) { if (is_method("REGISTER")) { www_challenge("example.com", "0"); exit; } proxy_challenge("example.com", "0"); exit; } # get digest password from H.350 using auth username ($au) if (!h350_auth_lookup("$au", "$avp(auth_user)/$avp(auth_pwd)")) { switch ($retcode) { case -2: sl_send_reply("401", "Unauthorized"); exit; case -1: sl_send_reply("500", "Internal server error"); exit; } } # REGISTER requests if (is_method("REGISTER")) { if (!pv_www_authorize("example.com")) { if ($retcode == -5) { sl_send_reply("500", "Internal server error"); exit; } else { www_challenge("example.com", "0"); exit; } } consume_credentials(); xlog("L_INFO", "REGISTER request successfully authenticated"); return(1); } # non-REGISTER requests if (!pv_proxy_authorize("example.com")) { if ($retcode == -5) { sl_send_reply("500", "Internal server error"); exit; } else { proxy_challenge("example.com", "0"); exit; } } consume_credentials(); xlog("L_INFO", "$rm request successfully authenticated"); return(1); }
h350_result_call_preferences(avp_name_prefix) This function parses the callPreferenceURI attribute of an H.350 commObject, which must have been fetched through h350_*_lookup or ldap_search. callPreferenceURI is a multi-valued attribute that stores call preference rules like e.g. forward-on-busy or forward-unconditionally. Directory services architecture for call forwarding and preferences defines a format for simple call forwarding rules:
target_uri type[:argument]
In a SIP environment, target_uri is typically the call forwarding rule's target SIP URI, although it could be any type of URI, e.g. an HTTP pointer to a CPL script. Four different values are specified for type: b for "forward on busy", n for "forward on no answer", u for "forward unconditionally", and f for "forward on destination not found". The optional argument is a string indicating the time in milliseconds after which the call forwarding should occur. Example H.350 callPreferenceURI simple call forwarding rules # Example 1: # forward to sip:voicemail@example.com on no answer after 15 seconds: callPreferenceURI: sip:voicemail@example.com n:15000 # Example 2: # unconditionally forward to sip:alice@example.com: callPreferenceURI: sip:alice@example.com u # Example 3: # forward to sip:bob@example.com and sip:alice@example.com # (forking) on destination not found: callPreferenceURI: sip:bob@example.com f callPreferenceURI: sip:alice@example.com f h350_result_call_preferences stores these call forwarding rules as AVPs according to the following rules:
# # AVP storing a forwarding rule's target URI # AVP name = avp_name_prefix + '_' + type AVP value = target_uri # # AVP storing a forwarding rule's argument # AVP name = avp_name_prefix + '_' + type + '_t' AVP value = argument / 1000
Example 1 from above would result in two AVPs: $avp("prefix_n") = "sip:voicemail@example.com" and $avp("prefix_n_t") = 15. Example 2: $avp("prefix_u") = "sip:alice@example.com". Example 3: $avp("prefix_f[1]") = "sip:bob@example.com" and $avp("prefix_f[2]]") = "sip:alice@example.com". These AVPs can then be used to implement the desired behavior in the OpenSIPS routing script. This function returns the number of successfully parsed simple call forwarding rules (TRUE), in case the H.350 callPreferenceURI attribute contained one or multiple values matching the simple call forwarding rule syntax described above. It returns -1 (FALSE) for internal errors, and -2 (FALSE) if none of the rules matched or if no callPreferenceURI attribute was found. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Function Parameters: avp_name_prefix Name prefix for call forwarding rule AVPs, as described above. Return Values: n > 0 (TRUE): n simple call forwarding rules found. -1 (FALSE): Internal error occurred. -2 (FALSE): No simple call forwarding rule found, or callPreferenceURI not present. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage # # H.350 lookup for callee # ... h350_sipuri_lookup("sip:$rU@$rd") ... # # store H.350 call preferences in AVP # if (!h350_result_call_preferences("callee_pref_") && ($retcode == -1)) { sl_send_reply("500", "Internal server error"); exit; } # $avp(callee_pref_u) == CFU URI(s) # $avp(callee_pref_n) == CFNR URI(s) # $avp(callee_pref_n_t) == CFNR timeout in seconds # $avp(callee_pref_b) == CFB URI(s) # $avp(callee_pref_f) == CFOFFLINE URI(s) # # Example for forward-unconditionally (CFU) # if (is_avp_set("$avp(callee_pref_u)")) { # replace R-URI with CFU URI, g will fetch all CFU URIs # --> request can fork if (avp_pushto("$ru", "$avp(callee_pref_u)/g")) { avp_delete("$avp(callee_pref_u)"); } else { sl_send_reply("500", "Internal server error"); exit; } sl_send_reply("181", "Call is being forwarded"); t_relay(); exit; }
h350_result_service_level(avp_name_prefix) Directory services architecture for SIP defines a multi-valued LDAP attribute named SIPIdentityServiceLevel, which can be used to store SIP account service level values in an LDAP directory. This function parses the SIPIdentityServiceLevel attribute and stores all service level values as AVPs for later retrieval in the OpenSIPS routing script. The function accesses the H.350 commObject fetched by a call to h350_*_lookup or ldap_search. The resulting AVPs have a name of the form avp_name_prefix + SIPIdentityServiceLevel attribute value, and an integer value of 1. Example SIPIdentityServiceLevel values and resulting AVPs SIPIdentityServiceLevel: longdistance SIPIdentityServiceLevel: international SIPIdentityServiceLevel: 900 after calling h350_result_service_level("sl_"), the following AVPs will be available in the routing script: $avp("sl_longdistance") = 1 $avp("sl_international") = 1 $avp("sl_900") = 1 This function returns the number of added AVPs (TRUE), -1 (FALSE)for internal errors, and -2 (FALSE)if no SIPIdentityServiceLevel attribute was found. The function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Function Parameters: avp_name_prefix Name prefix for service level AVPs, as described above. Return Values: n > 0 (TRUE): n AVPs added. -1 (FALSE): Internal error occurred. -2 (FALSE): No SIPIdentityServiceLevel attribute found. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage # # H.350 SIP digest authentication for caller # ... h350_auth_lookup("$au", ...) ... # # store caller's service level as AVP # if (!h350_result_service_level("caller_sl_") && ($retcode == -1)) { sl_send_reply("500", "Internal server error"); exit; } # # make routing decision based on service level AVPs # if (is_avp_set("$avp(caller_sl_international)")) { t_relay(); } else { sl_send_reply("403", "Forbidden"); } exit;
opensips-2.2.2/modules/h350/doc/h350_biblio.xml000066400000000000000000000037561300170765700210130ustar00rootroot00000000000000 Resources H.350 <ulink url="http://www.itu.int/rec/T-REC-H.350/en">Directory Services Architecture for Multimedia Conferencing</ulink> August 2003 ITU-T H.350.4 <ulink url="http://www.itu.int/rec/T-REC-H.350.4/en">Directory services architecture for SIP</ulink> August 2003 ITU-T H.350.6 <ulink url="http://www.itu.int/rec/T-REC-H.350.6/en">Directory services architecture for call forwarding and preferences</ulink> March 2004 ITU-T ViDe-H.350-Cookbook <ulink url="http://www.vide.net/cookbookh350/">ViDe H.350 Cookbook</ulink> 2005 ViDe RFC4510 <ulink url="http://tools.ietf.org/html/rfc4510">Lightweight Directory Access Protocol (LDAP): Technical Specification Road Map</ulink> June 2006 Internet Engineering Task Force opensips-2.2.2/modules/h350/h350_exp_fn.c000066400000000000000000000325111300170765700176760ustar00rootroot00000000000000/* * OpenSIPS H.350 Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-12: Initial version */ #include #include "ldap.h" #include "h350_mod.h" #include "h350_exp_fn.h" #include "../../pvar.h" #include "../../ut.h" #include "../../mem/mem.h" #define H350_SIPURI_LOOKUP_LDAP_FILTER "(&(objectClass=SIPIdentity)(SIPIdentitySIPURI=%s))" #define H350_AUTH_FILTER_PATTERN "(&(objectClass=SIPIdentity)(SIPIdentityUserName=%s))" static str h350_call_pref_name = str_init("callPreferenceURI"); static str h350_sip_pwd_name = str_init("SIPIdentityPassword"); static str h350_service_level_name = str_init("SIPIdentityServiceLevel"); #define H350_CALL_PREF_REGEX "^([^ ]+) +([a-zA-Z]+)(:([0-9]+))?$" #define SIP_URI_ESCAPED_MAX_LEN 1024 #define AVP_NAME_STR_BUF_LEN 1024 #define DIGEST_USERNAME_BUF_SIZE 2048 static regex_t* call_pref_preg; int h350_sipuri_lookup(struct sip_msg* _msg, pv_elem_t* _sip_uri) { str sip_uri, sip_uri_escaped; int ld_result_count; static char sip_uri_escaped_buf[SIP_URI_ESCAPED_MAX_LEN]; /* * get sip_uri */ if (pv_printf_s(_msg, _sip_uri, &sip_uri) != 0) { LM_ERR("pv_printf_s failed\n"); return E_H350_INTERNAL; } /* * ldap filter escape sip_uri */ sip_uri_escaped.s = sip_uri_escaped_buf; sip_uri_escaped.len = SIP_URI_ESCAPED_MAX_LEN - 1; if (ldap_api.ldap_rfc4515_escape(&sip_uri, &sip_uri_escaped, 0)) { LM_ERR("ldap_rfc4515_escape failed\n"); return E_H350_INTERNAL; } /* * do ldap search */ if (ldap_api.ldap_params_search(&ld_result_count, h350_ldap_session.s, h350_base_dn.s, h350_search_scope_int, NULL, H350_SIPURI_LOOKUP_LDAP_FILTER, sip_uri_escaped.s) != 0) { LM_ERR("ldap search failed\n"); return E_H350_INTERNAL; } if (ld_result_count < 1) { return E_H350_NO_SUCCESS; } return ld_result_count; } int h350_auth_lookup( struct sip_msg* _msg, pv_elem_t* _digest_username, struct h350_auth_lookup_avp_params* _avp_specs) { str digest_username, digest_username_escaped, digest_password; static char digest_username_buf[DIGEST_USERNAME_BUF_SIZE]; struct berval **attr_vals = NULL; int username_avp_name, password_avp_name; int_str avp_val; unsigned short username_avp_type, password_avp_type; int rc, ld_result_count; /* * get digest_username str */ if (_digest_username) { if (pv_printf_s(_msg, _digest_username, &digest_username) != 0) { LM_ERR("pv_printf_s failed\n"); return E_H350_INTERNAL; } } else { LM_ERR("empty digest username\n"); return E_H350_NO_SUCCESS; } /* * get AVP names for username and password */ if (pv_get_avp_name( _msg, &(_avp_specs->username_avp_spec.pvp), &username_avp_name, &username_avp_type) != 0) { LM_ERR("error getting AVP name - pv_get_avp_name failed\n"); return E_H350_INTERNAL; } if (pv_get_avp_name(_msg, &(_avp_specs->password_avp_spec.pvp), &password_avp_name, &password_avp_type) != 0) { LM_ERR("error getting AVP name - pv_get_avp_name failed\n"); return E_H350_INTERNAL; } /* * search for sip digest username in H.350, store digest password */ /* ldap filter escape digest username */ digest_username_escaped.s = digest_username_buf; digest_username_escaped.len = DIGEST_USERNAME_BUF_SIZE - 1; if (ldap_api.ldap_rfc4515_escape( &digest_username, &digest_username_escaped, 0) ) { LM_ERR("ldap_rfc4515_escape() failed\n"); return E_H350_INTERNAL; } /* do ldap search */ if (ldap_api.ldap_params_search(&ld_result_count, h350_ldap_session.s, h350_base_dn.s, h350_search_scope_int, NULL, H350_AUTH_FILTER_PATTERN, digest_username_escaped.s) != 0) { LM_ERR("LDAP search failed\n"); return E_H350_INTERNAL; } if (ld_result_count < 1) { LM_INFO("no H.350 entry found for username [%s]\n", digest_username_escaped.s); return E_H350_NO_SUCCESS; } if (ld_result_count > 1) { LM_WARN("more than one [%d] H.350 entry found for username [%s]\n", ld_result_count, digest_username_escaped.s); } /* get ldap result values */ rc = ldap_api.ldap_result_attr_vals(&h350_sip_pwd_name, &attr_vals); if (rc < 0) { LM_ERR("getting LDAP attribute values failed\n"); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } if ((rc > 0) || (attr_vals == NULL)) { LM_INFO("no values found in LDAP entry for username [%s]\n", digest_username_escaped.s); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } digest_password.s = attr_vals[0]->bv_val; digest_password.len = attr_vals[0]->bv_len; /* * write AVPs */ avp_val.s = digest_username; if (add_avp( username_avp_type | AVP_VAL_STR, username_avp_name, avp_val) < 0) { LM_ERR("failed to create new AVP\n"); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } avp_val.s = digest_password; if (add_avp( password_avp_type | AVP_VAL_STR, password_avp_name, avp_val) < 0) { LM_ERR("failed to create new AVP\n"); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } ldap_api.ldap_value_free_len(attr_vals); return E_H350_SUCCESS; } int h350_call_preferences(struct sip_msg* _msg, pv_elem_t* _avp_name_prefix) { int rc, i, avp_count = 0; struct berval **attr_vals; size_t nmatch = 5; regmatch_t pmatch[5]; int avp_name; int_str avp_val; str avp_val_str, avp_name_str, avp_name_prefix_str, call_pref_timeout_str; int call_pref_timeout; static char call_pref_avp_name[AVP_NAME_STR_BUF_LEN]; /* * get avp_name_prefix_str */ if (pv_printf_s(_msg, _avp_name_prefix, &avp_name_prefix_str) != 0) { LM_ERR("pv_printf_s failed\n"); return E_H350_INTERNAL; } /* * get LDAP attribute values */ if ((rc = ldap_api.ldap_result_attr_vals( &h350_call_pref_name, &attr_vals)) < 0) { LM_ERR("Getting LDAP attribute values failed\n"); return E_H350_INTERNAL; } if (rc > 0) { /* no LDAP values found */ return E_H350_NO_SUCCESS; } /* * loop through call pref values and add AVP(s) */ /* copy avp name prefix into call_pref_avp_name */ if (avp_name_prefix_str.len < AVP_NAME_STR_BUF_LEN) { memcpy(call_pref_avp_name, avp_name_prefix_str.s, avp_name_prefix_str.len); } else { LM_ERR("AVP name prefix too long [%d] (max [%d])", avp_name_prefix_str.len, AVP_NAME_STR_BUF_LEN); return E_H350_INTERNAL; } for (i = 0; attr_vals[i] != NULL; i++) { if ((rc = regexec(call_pref_preg, attr_vals[i]->bv_val, nmatch, pmatch, 0)) != 0) { switch (rc) { case REG_NOMATCH: LM_INFO("no h350 call preference regex match for [%s]\n", attr_vals[i]->bv_val); continue; case REG_ESPACE: LM_ERR("regexec returned REG_ESPACE - out of memory\n"); default: LM_ERR("regexec failed\n"); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } } /* calculate call preference sip uri */ if (avp_name_prefix_str.len + pmatch[2].rm_eo - pmatch[2].rm_so >= AVP_NAME_STR_BUF_LEN) { LM_ERR("AVP name too long for [%s]", attr_vals[i]->bv_val); continue; } avp_val_str.s = attr_vals[i]->bv_val + pmatch[1].rm_so; avp_val_str.len = pmatch[1].rm_eo - pmatch[1].rm_so; avp_val.s = avp_val_str; /* calculate call preference avp name */ memcpy( call_pref_avp_name + avp_name_prefix_str.len, attr_vals[i]->bv_val + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so); avp_name_str.s = call_pref_avp_name; avp_name_str.len = avp_name_prefix_str.len + pmatch[2].rm_eo - pmatch[2].rm_so; avp_name = get_avp_id(&avp_name_str); if (avp_name <= 0) { LM_ERR("cannot get avp id\n"); continue; } /* add avp */ if (add_avp(AVP_NAME_STR | AVP_VAL_STR, avp_name, avp_val) < 0) { LM_ERR("failed to create new AVP\n"); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } avp_count++; /* check for call preference timeout */ if ((pmatch[4].rm_eo - pmatch[4].rm_so) == 0) { continue; } /* calculate call preference timeout avp name */ memcpy( avp_name_str.s + avp_name_str.len, "_t", 2); avp_name_str.len += 2; avp_name = get_avp_id(&avp_name_str); if (avp_name <= 0) { LM_ERR("cannot get avp id\n"); continue; } /* calculate timeout avp value */ call_pref_timeout_str.s = attr_vals[i]->bv_val + pmatch[4].rm_so; call_pref_timeout_str.len = pmatch[4].rm_eo - pmatch[4].rm_so; if (str2sint(&call_pref_timeout_str, &call_pref_timeout) != 0) { LM_ERR("str2sint failed\n"); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } call_pref_timeout = call_pref_timeout / 1000; /* add timeout avp */ avp_val.n = call_pref_timeout; if (add_avp(AVP_NAME_STR, avp_name, avp_val) < 0) { LM_ERR("failed to create new AVP\n"); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } } ldap_api.ldap_value_free_len(attr_vals); if (avp_count > 0) { return avp_count; } else { return E_H350_NO_SUCCESS; } } int h350_service_level(struct sip_msg* _msg, pv_elem_t* _avp_name_prefix) { int i, rc, avp_count = 0; str avp_name_prefix; int_str avp_name; int_str avp_val; struct berval **attr_vals; static char service_level_avp_name[AVP_NAME_STR_BUF_LEN]; /* * get service_level */ if (pv_printf_s(_msg, _avp_name_prefix, &avp_name_prefix) != 0) { LM_ERR("pv_printf_s failed\n"); return E_H350_INTERNAL; } /* * get LDAP attribute values */ if ((rc = ldap_api.ldap_result_attr_vals(&h350_service_level_name, &attr_vals)) < 0) { LM_ERR("Getting LDAP attribute values failed\n"); return E_H350_INTERNAL; } if (rc > 0) { /* no LDAP values found */ return E_H350_NO_SUCCESS; } /* copy avp name prefix into service_level_avp_name */ if (avp_name_prefix.len < AVP_NAME_STR_BUF_LEN) { memcpy(service_level_avp_name, avp_name_prefix.s, avp_name_prefix.len); } else { LM_ERR("AVP name prefix too long [%d] (max [%d])\n", avp_name_prefix.len, AVP_NAME_STR_BUF_LEN); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } /* * loop through service level values and add AVP(s) */ for (i = 0; attr_vals[i] != NULL; i++) { /* get avp name */ if (avp_name_prefix.len + attr_vals[i]->bv_len >= AVP_NAME_STR_BUF_LEN) { LM_ERR("AVP name too long for [%s]\n", attr_vals[i]->bv_val); continue; } memcpy( service_level_avp_name + avp_name_prefix.len, attr_vals[i]->bv_val, attr_vals[i]->bv_len); avp_name.s.s = service_level_avp_name; avp_name.s.len = avp_name_prefix.len + attr_vals[i]->bv_len; avp_name.n = get_avp_id(&avp_name.s); if (avp_name.n <= 0) { LM_ERR("cannot get avp id\n"); continue; } /* avp value = 1 */ avp_val.n = 1; if (add_avp(AVP_NAME_STR, avp_name.n, avp_val) < 0) { LM_ERR("failed to create new AVP\n"); ldap_api.ldap_value_free_len(attr_vals); return E_H350_INTERNAL; } avp_count++; } ldap_api.ldap_value_free_len(attr_vals); if (avp_count > 0) { return avp_count; } else { return E_H350_NO_SUCCESS; } } int h350_exp_fn_init(void) { int rc; if ((call_pref_preg = pkg_malloc(sizeof(regex_t))) == 0) { LM_ERR("allocating memory for regex failed\n"); return -1; } if ((rc = regcomp(call_pref_preg, H350_CALL_PREF_REGEX, REG_EXTENDED)) != 0) { pkg_free(call_pref_preg); LM_ERR("regcomp failed - returned [%d]\n", rc); return -1; } return 0; } opensips-2.2.2/modules/h350/h350_exp_fn.h000066400000000000000000000032461300170765700177060ustar00rootroot00000000000000/* * OpenSIPS H.350 Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-12: Initial version */ #ifndef H350_EXP_FN_H #define H350_EXP_FN_H #include "../../parser/msg_parser.h" #include "../../pvar.h" #include "../../parser/msg_parser.h" #define E_H350_SUCCESS 1 #define E_H350_INTERNAL -1 #define E_H350_NO_SUCCESS -2 struct h350_auth_lookup_avp_params { pv_spec_t username_avp_spec; pv_spec_t password_avp_spec; }; int h350_exp_fn_init(); int h350_sipuri_lookup(struct sip_msg* _msg, pv_elem_t* _sip_uri); int h350_auth_lookup( struct sip_msg* _msg, pv_elem_t* _digest_username, struct h350_auth_lookup_avp_params* _avp_specs); int h350_call_preferences(struct sip_msg* _msg, pv_elem_t* _avp_name_prefix); int h350_service_level(struct sip_msg* _msg, pv_elem_t* _avp_name_prefix); #endif /* H350_EXP_FN_H */ opensips-2.2.2/modules/h350/h350_mod.c000066400000000000000000000176311300170765700172040ustar00rootroot00000000000000/* * OpenSIPS H.350 Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-12: Initial version */ #include "../../sr_module.h" #include "../../ut.h" #include "h350_mod.h" #include "h350_exp_fn.h" /* * Module management function prototypes */ static int mod_init(void); static void destroy(void); static int child_init(int rank); /* * fixup functions */ static int one_str_pv_elem_fixup(void** param, int param_no); static int h350_auth_lookup_fixup(void** param, int param_no); /* * exported functions */ static int w_h350_sipuri_lookup(struct sip_msg* msg, char* sip_uri, char* s2); static int w_h350_auth_lookup(struct sip_msg* msg, char* digest_username, char* avp_specs); static int w_h350_call_preferences(struct sip_msg* msg, char* avp_name_prefix, char* s2); static int w_h350_service_level(struct sip_msg* msg, char* avp_name_prefix, char* s2); /* * Module parameter variables */ str h350_ldap_session = str_init(H350_LDAP_SESSION); str h350_base_dn = str_init(H350_BASE_DN); str h350_search_scope = str_init(H350_SEARCH_SCOPE); int h350_search_scope_int = -1; /* * LDAP API */ ldap_api_t ldap_api; /* * Exported functions */ static cmd_export_t cmds[] = { {"h350_sipuri_lookup", (cmd_function)w_h350_sipuri_lookup, 1, one_str_pv_elem_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE}, {"h350_auth_lookup", (cmd_function)w_h350_auth_lookup, 2, h350_auth_lookup_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE}, {"h350_result_call_preferences", (cmd_function)w_h350_call_preferences, 1, one_str_pv_elem_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE}, {"h350_result_service_level", (cmd_function)w_h350_service_level, 1, one_str_pv_elem_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"ldap_session", STR_PARAM, &h350_ldap_session.s}, {"base_dn", STR_PARAM, &h350_base_dn.s}, {"search_scope", STR_PARAM, &h350_search_scope.s}, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "ldap", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* * Module interface */ struct module_exports exports = { "h350", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; static int child_init(int rank) { /* don't do anything for non-worker process */ if (rank == PROC_MAIN || rank == PROC_TCP_MAIN) { return 0; } h350_search_scope_int = ldap_api.ldap_str2scope(h350_search_scope.s); /* * initialize h350_exp_fn */ if (h350_exp_fn_init() != 0) { LM_ERR("h350_exp_fn_init failed\n"); return -1; } return 0; } static int mod_init(void) { LM_INFO("H350 module - initializing\n"); /* * load the LDAP API */ if (load_ldap_api(&ldap_api) != 0) { LM_ERR("Unable to load LDAP API - this module requires ldap module\n"); return -1; } return 0; /* * check module parameters */ if (ldap_api.ldap_str2scope(h350_search_scope.s) == -1) { LM_ERR("Invalid search_scope [%s]\n", h350_search_scope.s); return -1; } } static void destroy(void) { } /* * EXPORTED functions */ static int w_h350_sipuri_lookup(struct sip_msg* msg, char* sip_uri, char* s2) { return h350_sipuri_lookup(msg, (pv_elem_t*)sip_uri); } static int w_h350_auth_lookup(struct sip_msg* msg, char* digest_username, char* avp_specs) { return h350_auth_lookup( msg, (pv_elem_t*)digest_username, (struct h350_auth_lookup_avp_params*)avp_specs); } static int w_h350_call_preferences(struct sip_msg* msg, char* avp_name_prefix, char* s2) { return h350_call_preferences(msg, (pv_elem_t*)avp_name_prefix); } static int w_h350_service_level(struct sip_msg* msg, char* avp_name_prefix, char* s2) { return h350_service_level(msg, (pv_elem_t*)avp_name_prefix); } /* * FIXUP functions */ static int one_str_pv_elem_fixup(void** param, int param_no) { pv_elem_t *model; str s; if (param_no == 1) { s.s = (char*)*param; if (s.s==0 || s.s[0]==0) { model = 0; } else { s.len = strlen(s.s); if (pv_parse_format(&s,&model)<0) { LM_ERR("pv_parse_format failed\n"); return E_OUT_OF_MEM; } } *param = (void*)model; } return 0; } static int h350_auth_lookup_fixup(void** param, int param_no) { pv_elem_t *model; char *p, *username_avp_spec_str, *pwd_avp_spec_str; str s; struct h350_auth_lookup_avp_params *params; if (param_no == 1) { s.s = (char*)*param; s.len = strlen(s.s); if (s.s==0 || s.s[0]==0) { model = 0; } else { if (pv_parse_format(&s,&model)<0) { LM_ERR("pv_parse_format failed\n"); return E_OUT_OF_MEM; } } *param = (void*)model; } else if (param_no == 2) { /* * parse *param into username_avp_spec_str and pwd_avp_spec_str */ username_avp_spec_str = (char*)*param; if ((pwd_avp_spec_str = strchr(username_avp_spec_str, '/')) == 0) { /* no '/' found in username_avp_spec_str */ LM_ERR("invalid second argument [%s]\n", username_avp_spec_str); return E_UNSPEC; } *(pwd_avp_spec_str++) = 0; /* * parse avp specs into pv_spec_t and store in params */ params = (struct h350_auth_lookup_avp_params*)pkg_malloc (sizeof(struct h350_auth_lookup_avp_params)); if (params == NULL) { LM_ERR("no memory\n"); return E_OUT_OF_MEM; } memset(params, 0, sizeof(struct h350_auth_lookup_avp_params)); s.s = username_avp_spec_str; s.len = strlen(s.s); p = pv_parse_spec(&s, ¶ms->username_avp_spec); if (p == 0) { pkg_free(params); LM_ERR("parse error for [%s]\n", username_avp_spec_str); return E_UNSPEC; } if (params->username_avp_spec.type != PVT_AVP) { pkg_free(params); LM_ERR("invalid AVP specification [%s]\n", username_avp_spec_str); return E_UNSPEC; } s.s = pwd_avp_spec_str; s.len = strlen(s.s); p = pv_parse_spec(&s, ¶ms->password_avp_spec); if (p == 0) { pkg_free(params); LM_ERR("parse error for [%s]\n", pwd_avp_spec_str); return E_UNSPEC; } if (params->password_avp_spec.type != PVT_AVP) { pkg_free(params); LM_ERR("invalid AVP specification [%s]\n", pwd_avp_spec_str); return E_UNSPEC; } *param = (void*)params; } return 0; } opensips-2.2.2/modules/h350/h350_mod.h000066400000000000000000000025571300170765700172120ustar00rootroot00000000000000/* * OpenSIPS H.350 Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-12: Initial version */ #ifndef H350_MOD_H #define H350_MOD_H #include "../../str.h" #include "../ldap/api.h" /* * global pointer to ldap api */ extern ldap_api_t ldap_api; /* * global module parameters */ #define H350_LDAP_SESSION "" #define H350_BASE_DN "" #define H350_SEARCH_SCOPE "one" extern str h350_ldap_session; extern str h350_base_dn; extern str h350_search_scope; extern int h350_search_scope_int; #endif /* H350_MOD_H */ opensips-2.2.2/modules/httpd/000077500000000000000000000000001300170765700161565ustar00rootroot00000000000000opensips-2.2.2/modules/httpd/Makefile000066400000000000000000000012071300170765700176160ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=httpd.so # set CROSS_COMPILE to true if you want to skip # the autodetection # CROSS_COMPILE=true ifeq ($(CROSS_COMPILE),) LIBMICROHTTPD_BUILDER=$(shell \ if pkg-config --exists libmicrohttpd; then \ echo 'pkg-config libmicrohttpd'; \ fi) endif ifneq ($(LIBMICROHTTPD_BUILDER),) DEFS += $(shell $(LIBMICROHTTPD_BUILDER) --cflags) LIBS += $(shell $(LIBMICROHTTPD_BUILDER) --libs) else DEFS +=-I$(LOCALBASE)/include LIBS +=-L$(LOCALBASE)/lib -lmicrohttpd endif DEFS +=-DLIBMICROHTTPD include ../../Makefile.modules opensips-2.2.2/modules/httpd/README000066400000000000000000000114711300170765700170420ustar00rootroot00000000000000httpd Module Ovidiu Sas Edited by Ovidiu Sas Copyright © 2012-2013 VoIP Embedded, Inc. Revision History Revision $Rev: 8580 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. ip(string) 1.3.2. port(integer) 1.3.3. buf_size (integer) 1.3.4. post_buf_size (integer) 1.4. Exported MI Functions 1.4.1. httpd_list_root_path 1.5. Exported Functions 1.6. Known issues 2. Developer Guide 2.1. Available Functions 2.1.1. register_httpdcb (module, root_path, httpd_acces_handler_cb, httpd_flush_data_cb, httpd_init_proc_cb) List of Examples 1.1. Set ip parameter 1.2. Set port parameter 1.3. Set buf_size parameter 1.4. Set post_buf_size parameter Chapter 1. Admin Guide 1.1. Overview This module provides an HTTP transport layer for OpenSIPS. Implementation of httpd module's http server is based on libmicrohttpd library. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libmicrohttpd. 1.3. Exported Parameters 1.3.1. ip(string) The IP address used by the HTTP server to listen for incoming requests. The default value is an empty string. If no IP address is set, then the http server will bind to all available IPs. Example 1.1. Set ip parameter ... modparam("httpd", "ip", "127.0.0.1") ... 1.3.2. port(integer) The port number used by the HTTP server to listen for incoming requests. The default value is 8888. Ports lower than 1024 are not accepted. Example 1.2. Set port parameter ... modparam("httpd", "port", 8000) ... 1.3.3. buf_size (integer) It specifies the maximum length (in bytes) of the buffer used to write in the html response. If the size of the buffer is set to zero, it will be automatically set to a quarter of the size of the pkg memory. The default value is 0. Example 1.3. Set buf_size parameter ... modparam("httpd", "buf_size", 524288) ... 1.3.4. post_buf_size (integer) It specifies the length (in bytes) of the POST HTTP requests processing buffer. For large POST request, the default value might require to be increased. The default value is 1024. The minumal value is 256. Example 1.4. Set post_buf_size parameter ... modparam("httpd", "post_buf_size", 4096) ... 1.4. Exported MI Functions 1.4.1. httpd_list_root_path Lists all the registered http root paths into the httpd module. When a request comes in, if the root parth is in the list, the request will be sent to the module that register it. Name: httpd_list_root_path Parameters: none MI FIFO Command Format: :httpd_list_root_path:_reply_fifo_file_ _empty_line_ 1.5. Exported Functions No function exported to be used from configuration file. 1.6. Known issues Due to the fact that OpenSIPS is a multiprocess application, the microhttpd library is used in "external select" mode. This ensures that the library is not running in multithread mode and the library is entirely controled by OpenSIPS. Due to this particular mode of operations, for now, the entire http response is built in a pre-allocated buffer (see buf_size parameter). Future realeases of this module will address this issue. Running the http daemon as non root on ports below 1024 is forbidden by default in linux (kernel>=2.6.24). To allow the port binding, one can use setcap to give extra privilleges to opensips binary: setcap 'cap_net_bind_service=+ep' /usr/local/sbin/opensips Chapter 2. Developer Guide 2.1. Available Functions 2.1.1. register_httpdcb (module, root_path, httpd_acces_handler_cb, httpd_flush_data_cb, httpd_init_proc_cb) Register a new http root with it's associated callbacks into the httpd module. Meaning of the parameters is as follows: * const char *mod - name of the module that register an http root path to be handled; * str *root_path - the registered root path; * httpd_acces_handler_cb f1 - handler to the callback method to be called on root path match; * httpd_flush_data_cb f2 - handler to the callback method to be called for sending extra data (at a later time); * httpd_init_proc_cb f3 - handler to the callback method to be called during httpd process init; opensips-2.2.2/modules/httpd/doc/000077500000000000000000000000001300170765700167235ustar00rootroot00000000000000opensips-2.2.2/modules/httpd/doc/httpd.xml000066400000000000000000000021541300170765700205720ustar00rootroot00000000000000 %docentities; ]> httpd Module &osipsname; Ovidiu Sas osas@voipembedded.com Ovidiu Sas 2012-2013 VoIP Embedded, Inc. $Rev: 8580 $ $Date$ &admin; &devel; &faq; opensips-2.2.2/modules/httpd/doc/httpd_admin.xml000066400000000000000000000113161300170765700217420ustar00rootroot00000000000000 &adminguide;
Overview This module provides an HTTP transport layer for &osips;. Implementation of httpd module's http server is based on libmicrohttpd library.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libmicrohttpd.
Exported Parameters
<varname>ip</varname>(string) The IP address used by the HTTP server to listen for incoming requests. The default value is an empty string. If no IP address is set, then the http server will bind to all available IPs. Set <varname>ip</varname> parameter ... modparam("httpd", "ip", "127.0.0.1") ...
<varname>port</varname>(integer) The port number used by the HTTP server to listen for incoming requests. The default value is 8888. Ports lower than 1024 are not accepted. Set <varname>port</varname> parameter ... modparam("httpd", "port", 8000) ...
<varname>buf_size</varname> (integer) It specifies the maximum length (in bytes) of the buffer used to write in the html response. If the size of the buffer is set to zero, it will be automatically set to a quarter of the size of the pkg memory. The default value is 0. Set <varname>buf_size</varname> parameter ... modparam("httpd", "buf_size", 524288) ...
<varname>post_buf_size</varname> (integer) It specifies the length (in bytes) of the POST HTTP requests processing buffer. For large POST request, the default value might require to be increased. The default value is 1024. The minumal value is 256. Set <varname>post_buf_size</varname> parameter ... modparam("httpd", "post_buf_size", 4096) ...
Exported MI Functions
<varname>httpd_list_root_path</varname> Lists all the registered http root paths into the httpd module. When a request comes in, if the root parth is in the list, the request will be sent to the module that register it. Name: httpd_list_root_path Parameters: none MI FIFO Command Format: :httpd_list_root_path:_reply_fifo_file_ _empty_line_
Exported Functions No function exported to be used from configuration file.
Known issues Due to the fact that &osips; is a multiprocess application, the microhttpd library is used in "external select" mode. This ensures that the library is not running in multithread mode and the library is entirely controled by &osips;. Due to this particular mode of operations, for now, the entire http response is built in a pre-allocated buffer (see buf_size parameter). Future realeases of this module will address this issue. Running the http daemon as non root on ports below 1024 is forbidden by default in linux (kernel>=2.6.24). To allow the port binding, one can use setcap to give extra privilleges to opensips binary: setcap 'cap_net_bind_service=+ep' /usr/local/sbin/opensips
opensips-2.2.2/modules/httpd/doc/httpd_devel.xml000066400000000000000000000024321300170765700217500ustar00rootroot00000000000000 &develguide;
Available Functions
<function moreinfo="none">register_httpdcb (module, root_path, httpd_acces_handler_cb, httpd_flush_data_cb, httpd_init_proc_cb)</function> Register a new http root with it's associated callbacks into the httpd module. Meaning of the parameters is as follows: const char *mod - name of the module that register an http root path to be handled; str *root_path - the registered root path; httpd_acces_handler_cb f1 - handler to the callback method to be called on root path match; httpd_flush_data_cb f2 - handler to the callback method to be called for sending extra data (at a later time); httpd_init_proc_cb f3 - handler to the callback method to be called during httpd process init;
opensips-2.2.2/modules/httpd/httpd.c000066400000000000000000000136041300170765700174510ustar00rootroot00000000000000/* * Copyright (C) 2011-2012 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2012-01-19 first version (osas) */ #include #include #include #include #include #include #include #include #include #include #include #include "../../globals.h" #include "../../sr_module.h" #include "../../str.h" #include "../../ut.h" #include "../../resolve.h" #include "../../mem/mem.h" #include "httpd_load.h" #include "httpd_proc.h" #define MIN_POST_BUF_SIZE 256 #define DEFAULT_POST_BUF_SIZE 1024 /* module functions */ static int mod_init(); static int destroy(void); static struct mi_root* mi_list_root_path(struct mi_root* cmd, void* param); int port = 8888; str ip = {NULL, 0}; str buffer = {NULL, 0}; int post_buf_size = DEFAULT_POST_BUF_SIZE; struct httpd_cb *httpd_cb_list = NULL; static proc_export_t mi_procs[] = { {"HTTPD", 0, 0, httpd_proc, 1, PROC_FLAG_INITCHILD }, {NULL, 0, 0, NULL, 0, 0} }; /** Module parameters */ static param_export_t params[] = { {"port", INT_PARAM, &port}, {"ip", STR_PARAM, &ip.s}, {"buf_size", INT_PARAM, &buffer.len}, {"post_buf_size", INT_PARAM, &post_buf_size}, {NULL, 0, NULL} }; /** Exported functions */ static cmd_export_t cmds[]= { {"httpd_bind", (cmd_function)httpd_bind, 1, 0, 0, 0}, {NULL, NULL, 0, 0, 0, 0} }; /** MI commands */ static mi_export_t mi_cmds[] = { { "httpd_list_root_path", 0, mi_list_root_path, 0, 0, 0}, { NULL, 0, NULL, 0, 0, 0} }; /** Module exports */ struct module_exports exports = { "httpd", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ NULL, /* exported statistics */ mi_cmds, /* exported MI functions */ NULL, /* exported PV */ mi_procs, /* extra processes */ mod_init, /* module initialization function */ (response_function) NULL, /* response handling function */ (destroy_function) destroy, /* destroy function */ NULL /* per-child init function */ }; static int mod_init(void) { struct ip_addr *_ip; if (ip.s) { ip.len = strlen(ip.s); if ( (_ip=str2ip(&ip)) == NULL ) { LM_ERR("invalid IP [%.*s]\n", ip.len, ip.s); return -1; } } if (post_buf_size < MIN_POST_BUF_SIZE) { LM_ERR("post_buf_size should be bigger then %d\n", MIN_POST_BUF_SIZE); return -1; } if (buffer.len == 0) buffer.len = (pkg_mem_size/4)*3; LM_DBG("buf_size=[%d]\n", buffer.len); return 0; } int destroy(void) { struct httpd_cb *cb = httpd_cb_list; httpd_proc_destroy(); while(cb) { httpd_cb_list = cb->next; shm_free(cb); cb = httpd_cb_list; } return 0; } int httpd_register_httpdcb(const char *module, str *http_root, httpd_acces_handler_cb f1, httpd_flush_data_cb f2, httpd_init_proc_cb f3) { int i; struct httpd_cb *cb; if (!module) { LM_ERR("NULL module name\n"); return -1; } if (!http_root) { LM_ERR("NULL http root path\n"); return -1; } if (!f1) { LM_ERR("NULL acces handler cb\n"); return -1; } if (!f2) { LM_ERR("NULL flush data cb\n"); return -1; } trim_spaces_lr(*http_root); if (!http_root->len) { LM_ERR("invalid http root path from module [%s]\n", module); return -1; } for(i=0;ilen;i++) { if ( !isalnum(http_root->s[i]) && http_root->s[i]!='_') { LM_ERR("bad mi_http_root param [%.*s], char [%c] " "- use only alphanumerical characters\n", http_root->len, http_root->s, http_root->s[i]); return -1; } } cb = (struct httpd_cb*)shm_malloc(sizeof(struct httpd_cb)); if (cb==NULL) { LM_ERR("no more shm mem\n"); return -1; } cb->module = module; cb->http_root = http_root; cb->callback = f1; cb->flush_data_callback = f2; cb->init_proc_callback = f3; cb->next = httpd_cb_list; httpd_cb_list = cb; LM_DBG("got root_path [%s][%.*s]\n", cb->module, cb->http_root->len, cb->http_root->s); return 0; } int httpd_bind(httpd_api_t *api) { if (!api) { LM_ERR("Invalid parameter value\n"); return -1; } api->lookup_arg = httpd_lookup_arg; api->register_httpdcb = httpd_register_httpdcb; return 0; } static struct mi_root* mi_list_root_path(struct mi_root* cmd, void* param) { struct mi_root *rpl_tree; struct mi_node *node, *rpl; struct mi_attr* attr; struct httpd_cb *cb = httpd_cb_list; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; while(cb) { node = add_mi_node_child(rpl, 0, "http_root", 9, cb->http_root->s, cb->http_root->len); if(node == NULL) goto error; attr = add_mi_attr(node, 0, "module", 6, (char*)cb->module, strlen(cb->module)); if(attr == NULL) goto error; cb = cb->next; } return rpl_tree; error: LM_ERR("Unable to create reply\n"); free_mi_tree(rpl_tree); return NULL; } opensips-2.2.2/modules/httpd/httpd_load.h000066400000000000000000000131041300170765700204500ustar00rootroot00000000000000/* * Copyright (C) 2011-2012 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2011-09-20 first version (osas) */ #ifndef HTTPD_HTTPD_LOAD_H #define HTTPD_HTTPD_LOAD_H #define HTTPD_UNKNOWN_CONTENT_LEN -1 enum HTTPD_CONTENT_TYPE { HTTPD_UNKNOWN_CNT_TYPE = -1, HTTPD_STD_CNT_TYPE = 0, HTTPD_TEXT_XML_CNT_TYPE, HTTPD_APPLICATION_JSON_CNT_TYPE }; /** * A client has requested the given url using the given method ("GET", * "PUT", "DELETE", "POST", etc). The callback must call httpd * callbacks to provide content to give back to the client and return * an HTTP status code (i.e. 200 for OK, 404, etc.). * * @param cls argument given together with the function * pointer when the handler was registered * with the httpd module * @param connection abstract connection handler * @param url the requested url after http_root was skipped * @param method the HTTP method used ("GET", "PUT", etc.) * @param version the HTTP version string (i.e. "HTTP/1.1") * @param upload_data the data being uploaded * @param upload_data_size set initially to the size of the * upload_data provided; the method * must update this value to the * number of bytes NOT processed; * @param con_cls pointer that the callback can set to some * address and that will be preserved by * httpd for future calls for this request * @param buffer preallocated buffer for building the page * (the http response) * @param page the page to return. If no page is returned, * then the page will be built later on via a * callback (see httpd_flush_data_cb) * @returns code the HTTP code to be returned to the client */ typedef int (httpd_acces_handler_cb) (void *cls, void *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls, str *buffer, str *page); /** * Callback used by httpd in order to obtain content. The * callback is to copy at most "max" bytes of content into "buf". The * total number of bytes that has been placed into "buf" should be * returned.

* * Note that returning zero will cause httpd to try again, * the next round. Returning 0 for a daemon that runs in internal * select mode is an error (since it would result in busy waiting) and * will cause the program to be aborted (abort()). * * @param cls extra argument to the callback * @param pos position in the datastream to access; * note that if an MHD_Response object is re-used, * it is possible for the same content reader to * be queried multiple times for the same data; * however, if an MHD_Response is not re-used, * libmicrohttpd guarantees that "pos" will be * the sum of all non-negative return values * obtained from the content reader so far. * @return -1 for the end of transmission (or on error); * if a content transfer size was pre-set and the callback * has provided fewer than that amount of data, * httpd will close the connection with the client; * if no content size was specified and this is an * http 1.1 connection using chunked encoding, httpd will * interpret "-1" as the normal end of the transfer * (possibly allowing the client to perform additional * requests using the same TCP connection). */ typedef ssize_t (httpd_flush_data_cb) (void *cls, uint64_t pos, char *buf, size_t max); /** * Callback to be run in order to initialize process specific data */ typedef void (httpd_init_proc_cb) (void); struct httpd_cb { const char *module; str *http_root; httpd_acces_handler_cb *callback; httpd_flush_data_cb *flush_data_callback; httpd_init_proc_cb *init_proc_callback; struct httpd_cb *next; }; void lookup_arg(void *connection, const char *key, void *con_cls, str *val); typedef void (*lookup_arg_f)(void *connection, const char *key, void *con_cls, str *val); int register_httpdcb(const char *mod, str *root_path, httpd_acces_handler_cb f1, httpd_flush_data_cb f2, httpd_init_proc_cb f3); typedef int (*register_httpdcb_f)(const char *mod, str *root_path, httpd_acces_handler_cb f1, httpd_flush_data_cb f2, httpd_init_proc_cb f3); typedef struct httpd_api { lookup_arg_f lookup_arg; register_httpdcb_f register_httpdcb; }httpd_api_t; void httpd_lookup_arg(void *connection, const char *key, void *con_cls, str *val); typedef int(*load_httpd_f)(httpd_api_t *api); int httpd_bind(httpd_api_t *api); static inline int load_httpd_api(httpd_api_t *api) { load_httpd_f load_httpd; /* import the httpd auto-loading functions */ if ( !(load_httpd=(load_httpd_f)find_export("httpd_bind", 1, 0))) return -1; /* let the auto-loading function load all httpd suuff */ if (load_httpd(api)==-1) return -1; return 0; } #endif opensips-2.2.2/modules/httpd/httpd_proc.c000066400000000000000000000476761300170765700205140ustar00rootroot00000000000000/* * Copyright (C) 2011-2012 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2011-09-20 first version (osas) */ #include #include #include #include #include #include #include #include #include #include #include #ifdef LIBMICROHTTPD #include #include #include #endif #include "../../pt.h" #include "../../sr_module.h" #include "../../str.h" #include "../../ut.h" #include "../../sliblist.h" #include "httpd_load.h" extern int port; extern str ip; extern str buffer; extern int post_buf_size; extern struct httpd_cb *httpd_cb_list; static const str MI_HTTP_U_URL = str_init("" "Unable to parse URL!"); static const str MI_HTTP_U_METHOD = str_init("" "Unsupported HTTP request!"); static const str MI_HTTP_U_CNT_TYPE = str_init("" "Unsupported Content-Type!"); /** * Data structure to store inside elents of slinkedl_list list. */ typedef struct str_str { str key; str val; } str_str_t; #ifdef LIBMICROHTTPD struct MHD_Daemon *dmn; struct post_request { struct MHD_PostProcessor *pp; int status; enum HTTPD_CONTENT_TYPE content_type; enum HTTPD_CONTENT_TYPE accept_type; int content_len; slinkedl_list_t *p_list; }; #endif /** * Allocator for the slinkedl_list list. */ void *httpd_alloc(size_t size) { return pkg_malloc(size); } /** * De-allocator for the slinkedl_list list. * * @param ptr The pointer to memory that we want to free up. */ void httpd_free(void *ptr) { pkg_free(ptr); return; } /** * Function to extract data from an element of a slinkedl_list list. * * @param e_data Pointer to the data stored by the current * element being processed (a str_str_t type). * @param data Pointer to the key idetifier. * @param r_data Pointer where the value that we are looking for */ int httpd_get_val(void *e_data, void *data, void *r_data) { str_str_t *kv = (str_str_t*)e_data; str *val = (str*)r_data; if (kv==NULL) { LM_ERR("null data\n"); } else { if (strncmp(kv->key.s, data, kv->key.len)==0) { val->s = kv->val.s; val->len = kv->val.len; LM_DBG("DATA=[%p] [%p][%p] [%.*s]->[%.*s]\n", kv, kv->key.s, kv->val.s, kv->key.len, kv->key.s, kv->val.len, kv->val.s); return 1; } } return 0; } /** * Function to print data stored in slinkedl_list list elemnts. * For debugging purposes only. */ /* int httpd_print_data(void *e_data, void *data, void *r_data) { str_str_t *kv = (str_str_t*)e_data; if (kv==NULL) { LM_ERR("null data\n"); } else { LM_DBG("data=[%p] [%p][%p] [%.*s]->[%.*s]\n", kv, kv->key.s, kv->val.s, kv->key.len, kv->key.s, kv->val.len, kv->val.s); } return 0; } */ /** * Function that retrieves the callback function that should * handle the current request. * * @param url Pointer to the root part of the HTTP URL. * @return The callback function to handle the HTTP request. */ struct httpd_cb *get_httpd_cb(const char *url) { int url_len; int index; struct httpd_cb *cb; str *http_root; if (!url) { LM_ERR("NULL URL\n"); return NULL; } url_len = strlen(url); if (url_len<=0) { LM_ERR("Invalid url length [%d]\n", url_len); return NULL; } if (url[0] != '/') { LM_ERR("URL starting with [%c] instead of'/'\n", *url); return NULL; } cb = httpd_cb_list; while(cb) { index = 1; http_root = cb->http_root; if (url_len - index < http_root->len) goto skip; if (strncmp(http_root->s, &url[index], http_root->len) != 0) goto skip; index += http_root->len; if (url_len - index == 0) return cb; if (url[index] == '/') return cb; skip: cb = cb->next; } return NULL; } #ifdef LIBMICROHTTPD /** * Handle regular POST data. * */ static int post_iterator (void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type, const char *transfer_encoding, const char *value, uint64_t off, size_t size) { int key_len; struct post_request *pr; str_str_t *kv; char *p; LM_DBG("post_iterator: cls=%p, kind=%d key=[%p]->'%s'" " filename='%s' content_type='%s' transfer_encoding='%s'" " value=[%p]->'%s' off=%llu size=%zu\n", cls, kind, key, key, filename, content_type, transfer_encoding, value, value, (long long unsigned int)off, size); pr = (struct post_request*)cls; if (pr==NULL) { LM_CRIT("corrupted data: null cls\n"); return MHD_NO; } if (off!=0) { if (size==0) { /* This is the last call post_iterator call * before destroying the post_processor. */ return MHD_YES; } else { LM_ERR("Trunkated data: post_iterator buffer to small!" " Increase post_buf_size value\n"); pr->status = -1; return MHD_NO; } } if (key) { key_len = strlen(key); if (key_len==0) { LM_ERR("empty key\n"); pr->status = -1; return MHD_NO; } } else { LM_ERR("NULL key\n"); pr->status = -1; return MHD_NO; } if (filename) { LM_ERR("we don't support file uploading\n"); pr->status = -1; return MHD_NO; } if (content_type) { LM_ERR("we don't support content_type\n"); pr->status = -1; return MHD_NO; } if (transfer_encoding) { LM_ERR("we don't support transfer_encoding\n"); pr->status = -1; return MHD_NO; } LM_DBG("[%.*s]->[%.*s]\n", key_len, key, (int)size, value); kv = (str_str_t*)slinkedl_append(pr->p_list, sizeof(str_str_t) + key_len + size); p = (char*)(kv + 1); kv->key.len = key_len; kv->key.s = p; memcpy(p, key, key_len); p += key_len; kv->val.len = size; kv->val.s = p; memcpy(p, value, size); LM_DBG("inserting element pr=[%p] pp=[%p] p_list=[%p]\n", pr, pr->pp, pr->p_list); return MHD_YES; } /** * Lookup for HTTP headers. * * @param cls Pointer to store return data. * @param kind Specifies the source of the key-value pairs that * we are looking for in the HTTP protocol. * @param key The key. * @param value The value. * * @return MHD_YES to continue iterating, * MHD_NO to abort the iteration. */ int getConnectionHeader(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) { struct post_request *pr = (struct post_request*)cls; str content_length; unsigned int len; char *p, bk; if (cls == NULL) { LM_ERR("Unable to store return data\n"); return MHD_NO; } if (kind != MHD_HEADER_KIND) { LM_ERR("Got kind != MHD_HEADER_KIND\n"); return MHD_NO; } if (strcasecmp("Accept", key) == 0) { LM_DBG("Accept=%s\n", value); if (strcasecmp("text/xml", value) == 0) pr->accept_type = HTTPD_TEXT_XML_CNT_TYPE; else if (strcasecmp("application/json", value) == 0) pr->accept_type = HTTPD_APPLICATION_JSON_CNT_TYPE; else pr->accept_type = HTTPD_UNKNOWN_CNT_TYPE; return MHD_YES; } if (strcasecmp("Content-Type", key) == 0) { LM_DBG("Content-Type=%s\n", value); /* extract only the mime */ if ( (p=strchr(value, ';'))!=NULL ) { while( p>value && (*(p-1)==' ' || *(p-1)=='\t') ) p--; bk = *p; *p = 0; } if (strcasecmp("text/xml", value) == 0) pr->content_type = HTTPD_TEXT_XML_CNT_TYPE; else if (strncasecmp("application/json", value, 16) == 0) pr->content_type = HTTPD_APPLICATION_JSON_CNT_TYPE; else pr->content_type = HTTPD_UNKNOWN_CNT_TYPE; if (p) *p = bk; goto done; } if (strcasecmp("Content-Length", key) == 0) { LM_DBG("Content-Length=%s\n", value); content_length.s = (char*)value; content_length.len = strlen(value); if (str2int(&content_length, &len)<0) { LM_ERR("got bogus Content-Length=%s\n", value); pr->content_len = HTTPD_UNKNOWN_CONTENT_LEN; } else pr->content_len = len; goto done; } return MHD_YES; done: if (pr->content_type && pr->content_len) return MHD_NO; else return MHD_YES; } /** * Performs lookup values for given keys. * For GET requests, we will use the libmicrohttpd's * internal API: MHD_lookup_connection_value(). * For POST requests, we will retrieve the value from * the slinkedl_list that was created and populated by * the post_iterator(). * * @param connection Pointer to the MHD_Connection * @param key The key for which we need to retrieve * the value. * @param con_cls This is a pointer to the slinkedl_list * that was passed back and forth via * several callback between the application * and the libmicrohttpd library. * @param val Pointer to the value that we are looking * for. */ void httpd_lookup_arg(void *connection, const char *key, void *con_cls, str *val) { slinkedl_list_t *list = (slinkedl_list_t*)con_cls; if (val) { if (list==NULL) { val->s = (char *)MHD_lookup_connection_value( (struct MHD_Connection *)connection, MHD_GET_ARGUMENT_KIND, key); if (val->s) val->len = strlen(val->s); else val->len = 0; } else { slinkedl_traverse(list, &httpd_get_val, (void *)key, val); } } else { LM_ERR("NULL holder for requested val\n"); } return; } int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { str page = {NULL, 0}; struct MHD_Response *response; int ret; void *async_data = NULL; struct httpd_cb *cb; const char *normalised_url; struct post_request *pr; str_str_t *kv; char *p; int cnt_type = HTTPD_STD_CNT_TYPE; int accept_type = HTTPD_STD_CNT_TYPE; int ret_code = MHD_HTTP_OK; LM_DBG("START *** cls=%p, connection=%p, url=%s, method=%s, " "versio=%s, upload_data[%zu]=%p, *con_cls=%p\n", cls, connection, url, method, version, *upload_data_size, upload_data, *con_cls); pr = *con_cls; if(pr == NULL){ pr = pkg_malloc(sizeof(struct post_request)); if(pr==NULL) { LM_ERR("oom while allocating post_request structure\n"); return MHD_NO; } memset(pr, 0, sizeof(struct post_request)); *con_cls = pr; pr = NULL; } if(strncmp(method, "POST", 4)==0) { if(pr == NULL){ pr = *con_cls; pr->p_list = slinkedl_init(&httpd_alloc, &httpd_free); if (pr->p_list==NULL) { LM_ERR("oom while allocating list\n"); return MHD_NO; } LM_DBG("running MHD_create_post_processor\n"); pr->pp = MHD_create_post_processor(connection, post_buf_size, &post_iterator, pr); if(pr->pp==NULL) { if (*upload_data_size == 0) { /* We need to wait for morte data before * handling the POST request */ return MHD_YES; } LM_DBG("NOT a regular POST :o)\n"); if (pr->content_type==0 && pr->content_len==0) MHD_get_connection_values(connection, MHD_HEADER_KIND, &getConnectionHeader, pr); if (pr->content_type<=0 || pr->content_len<=0) { LM_ERR("got a bogus request\n"); return MHD_NO; } if (*upload_data_size != pr->content_len) { /* For now, we don't support large POST with truncated data */ LM_ERR("got a truncated POST request\n"); return MHD_NO; } LM_DBG("got ContentType [%d] with len [%d]: %.*s\\n", pr->content_type, pr->content_len, (int)*upload_data_size, upload_data); /* Here we save data. */ switch (pr->content_type) { case HTTPD_TEXT_XML_CNT_TYPE: case HTTPD_APPLICATION_JSON_CNT_TYPE: /* Save the entire body as 'body' */ kv = (str_str_t*)slinkedl_append(pr->p_list, sizeof(str_str_t) + 1 + *upload_data_size); p = (char*)(kv + 1); kv->key.len = 1; kv->key.s = p; memcpy(p, "1", 1); p += 1; kv->val.len = *upload_data_size; kv->val.s = p; memcpy(p, upload_data, *upload_data_size); break; default: LM_ERR("Unhandled data for ContentType [%d]\n", pr->content_type); return MHD_NO; } /* Mark the fact that we consumed all data */ *upload_data_size = 0; return MHD_YES; } LM_DBG("pr=[%p] pp=[%p] p_list=[%p]\n", pr, pr->pp, pr->p_list); return MHD_YES; } else { if (pr->pp==NULL) { if (*upload_data_size == 0) { if (pr->content_type==HTTPD_TEXT_XML_CNT_TYPE) cnt_type = HTTPD_TEXT_XML_CNT_TYPE; if (pr->content_type==HTTPD_APPLICATION_JSON_CNT_TYPE) cnt_type = HTTPD_APPLICATION_JSON_CNT_TYPE; *con_cls = pr->p_list; cb = get_httpd_cb(url); if (cb) { normalised_url = &url[cb->http_root->len+1]; LM_DBG("normalised_url=[%s]\n", normalised_url); ret_code = cb->callback(cls, (void*)connection, normalised_url, method, version, upload_data, upload_data_size, con_cls, &buffer, &page); } else { page = MI_HTTP_U_URL; ret_code = MHD_HTTP_BAD_REQUEST; } /* slinkedl_traverse(pr->p_list, &httpd_print_data, NULL, NULL); */ slinkedl_list_destroy(*con_cls); pkg_free(pr); *con_cls = pr = NULL; goto send_response; } LM_DBG("NOT a regular POST :o)\n"); if (pr->content_type==0 && pr->content_len==0) MHD_get_connection_values(connection, MHD_HEADER_KIND, &getConnectionHeader, pr); if (pr->content_type<=0 || pr->content_len<=0) { LM_ERR("got a bogus request\n"); return MHD_NO; } if (*upload_data_size != pr->content_len) { /* For now, we don't support large POST with truncated data */ LM_ERR("got a truncated POST request\n"); return MHD_NO; } LM_DBG("got ContentType [%d] with len [%d]: %.*s\\n", pr->content_type, pr->content_len, (int)*upload_data_size, upload_data); /* Here we save data. */ switch (pr->content_type) { case HTTPD_TEXT_XML_CNT_TYPE: case HTTPD_APPLICATION_JSON_CNT_TYPE: /* Save the entire body as 'body' */ kv = (str_str_t*)slinkedl_append(pr->p_list, sizeof(str_str_t) + 1 + *upload_data_size); p = (char*)(kv + 1); kv->key.len = 1; kv->key.s = p; memcpy(p, "1", 1); p += 1; kv->val.len = *upload_data_size; kv->val.s = p; memcpy(p, upload_data, *upload_data_size); break; default: LM_ERR("Unhandled data for ContentType [%d]\n", pr->content_type); return MHD_NO; } /* Mark the fact that we consumed all data */ *upload_data_size = 0; return MHD_YES; } LM_DBG("running MHD_post_process: " "pp=%p status=%d upload_data_size=%zu\n", pr->pp, pr->status, *upload_data_size); if (pr->status<0) { slinkedl_list_destroy(pr->p_list); pr->p_list = NULL; /* FIXME: * It might be better to reply with an error * instead of resetting the connection via MHD_NO */ return MHD_NO; } ret =MHD_post_process(pr->pp, upload_data, *upload_data_size); LM_DBG("ret=%d upload_data_size=%zu\n", ret, *upload_data_size); if(*upload_data_size != 0) { *upload_data_size = 0; return MHD_YES; } LM_DBG("running MHD_destroy_post_processor: " "pr=[%p] pp=[%p] p_list=[%p]\n", pr, pr->pp, pr->p_list); MHD_destroy_post_processor(pr->pp); LM_DBG("done MHD_destroy_post_processor\n"); /* slinkedl_traverse(pr->p_list, &httpd_print_data, NULL, NULL); */ *con_cls = pr->p_list; cb = get_httpd_cb(url); if (cb) { normalised_url = &url[cb->http_root->len+1]; LM_DBG("normalised_url=[%s]\n", normalised_url); ret_code = cb->callback(cls, (void*)connection, normalised_url, method, version, upload_data, upload_data_size, con_cls, &buffer, &page); } else { page = MI_HTTP_U_URL; ret_code = MHD_HTTP_BAD_REQUEST; } /* slinkedl_traverse(pr->p_list, &httpd_print_data, NULL, NULL); */ slinkedl_list_destroy(*con_cls); pkg_free(pr); *con_cls = pr = NULL; } }else if(strncmp(method, "GET", 3)==0) { pr = *con_cls; MHD_get_connection_values(connection, MHD_HEADER_KIND, &getConnectionHeader, pr); accept_type = pr->accept_type; pkg_free(pr); *con_cls = pr = NULL; LM_DBG("accept_type=[%d]\n", accept_type); cb = get_httpd_cb(url); if (cb) { normalised_url = &url[cb->http_root->len+1]; LM_DBG("normalised_url=[%s]\n", normalised_url); ret_code = cb->callback(cls, (void*)connection, normalised_url, method, version, upload_data, upload_data_size, con_cls, &buffer, &page); } else { page = MI_HTTP_U_URL; ret_code = MHD_HTTP_BAD_REQUEST; } }else{ page = MI_HTTP_U_METHOD; ret_code = MHD_HTTP_METHOD_NOT_ACCEPTABLE; } send_response: if (page.s) { LM_DBG("MHD_create_response_from_data [%p:%d]\n", page.s, page.len); response = MHD_create_response_from_data(page.len, (void*)page.s, 0, 1); } else { LM_DBG("MHD_create_response_from_callback\n"); response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, buffer.len, (MHD_ContentReaderCallback)cb->flush_data_callback, (void*)async_data, NULL); } if (cnt_type==HTTPD_TEXT_XML_CNT_TYPE || accept_type==HTTPD_TEXT_XML_CNT_TYPE) MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/xml; charset=utf-8"); if (cnt_type==HTTPD_APPLICATION_JSON_CNT_TYPE || accept_type==HTTPD_APPLICATION_JSON_CNT_TYPE) MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "application/json"); ret = MHD_queue_response (connection, ret_code, response); MHD_destroy_response (response); return ret; } #endif void httpd_proc(int rank) { #ifdef LIBMICROHTTPD int status; fd_set rs; fd_set ws; fd_set es; int max; #endif struct httpd_cb *cb = httpd_cb_list; /*child's initial settings*/ if (init_mi_child()!=0) { LM_ERR("failed to init the mi child process\n"); return; } /* Allocating http response buffer */ buffer.s = (char*)pkg_malloc(sizeof(char)*buffer.len); if (buffer.s==NULL) { LM_ERR("oom\n"); return; } while(cb) { if (cb->init_proc_callback) cb->init_proc_callback(); cb = cb->next; } #ifdef LIBMICROHTTPD struct timeval tv; struct sockaddr_in saddr_in; memset(&saddr_in, 0, sizeof(saddr_in)); if (ip.s) saddr_in.sin_addr.s_addr = inet_addr(ip.s); else saddr_in.sin_addr.s_addr = INADDR_ANY; saddr_in.sin_family = AF_INET; saddr_in.sin_port = htons(port); LM_DBG("init_child [%d] - [%d] HTTP Server init [%s:%d]\n", rank, getpid(), (ip.s?ip.s:"INADDR_ANY"), port); set_proc_attrs("HTTPD %s:%d", (ip.s?ip.s:"INADDR_ANY"), port); dmn = MHD_start_daemon(MHD_NO_FLAG|MHD_USE_DEBUG, port, NULL, NULL, &(answer_to_connection), NULL, MHD_OPTION_SOCK_ADDR, &saddr_in, MHD_OPTION_END); if (NULL == dmn) { LM_ERR("unable to start http daemon\n"); return; } while(1) { max = 0; FD_ZERO (&rs); FD_ZERO (&ws); FD_ZERO (&es); if (MHD_YES != MHD_get_fdset (dmn, &rs, &ws, &es, &max)) { LM_ERR("unable to get file descriptors\n"); return; } tv.tv_sec = 1; tv.tv_usec = 0; //LM_DBG("select(%d,%p,%p,%p,%p)\n",max+1, &rs, &ws, &es, &tv); status = select(max+1, &rs, &ws, &es, &tv); if (status < 0) { switch(errno){ case EINTR: LM_DBG("error returned by select:" " [%d] [%d][%s]\n", status, errno, strerror(errno)); break; default: LM_WARN("error returned by select:" " [%d] [%d][%s]\n", status, errno, strerror(errno)); return; } } //LM_DBG("select returned %d\n", status); status = MHD_run(dmn); if (status == MHD_NO) { LM_ERR("unable to run http daemon\n"); return; } } #endif LM_DBG("HTTP Server stopped!\n"); } void httpd_proc_destroy(void) { #ifdef LIBMICROHTTPD LM_DBG("destroying module ...\n"); MHD_stop_daemon (dmn); #endif return; } opensips-2.2.2/modules/httpd/httpd_proc.h000066400000000000000000000021071300170765700204750ustar00rootroot00000000000000/* * Copyright (C) 2011 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2011-09-20 first version (osas) */ #ifndef _MI_HTTP_HTTPD_PROC_H #define _MI_HTTP_HTTPD_PROC_H #ifdef LIBMICROHTTPD #include extern struct MHD_Daemon *dmn; #endif void httpd_proc(int rank); void httpd_proc_destroy(void); #endif opensips-2.2.2/modules/identity/000077500000000000000000000000001300170765700166645ustar00rootroot00000000000000opensips-2.2.2/modules/identity/Makefile000066400000000000000000000011761300170765700203310ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=identity.so ifeq ($(CROSS_COMPILE),) SSL_BUILDER=$(shell \ if pkg-config --exists libssl; then \ echo 'pkg-config libssl'; \ fi) endif ifneq ($(SSL_BUILDER),) DEFS += $(shell $(SSL_BUILDER) --cflags) LIBS += $(shell $(SSL_BUILDER) --libs) else DEFS += -I$(LOCALBASE)/ssl/include \ -I$(LOCALBASE)/include LIBS += -L$(LOCALBASE)/lib -L$(LOCALBASE)/ssl/lib \ -L$(LOCALBASE)/lib64 -L$(LOCALBASE)/ssl/lib64 \ -lssl -lcrypto endif include ../../Makefile.modules opensips-2.2.2/modules/identity/README000066400000000000000000000205721300170765700175520ustar00rootroot00000000000000Identity Module Alexander Christ Cologne University of Applied Sciences Edited by Alexander Christ Copyright © 2007 Alexander Christ, Cologne University of Applied Sciences Revision History Revision $Revision: 5901 $ $Date: 2007-03-29$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. privKey (string) 1.3.2. authCert (string) 1.3.3. certUri (string) 1.3.4. verCert (string) 1.3.5. caList (string) 1.3.6. crlList (string) 1.3.7. useCrls (integer) 1.4. Exported Functions 1.4.1. authservice() 1.4.2. verifier() 1.5. Known Limitations List of Examples 1.1. Set privKey parameter 1.2. Set authCert parameter 1.3. Set certUri parameter 1.4. Set verCert parameter 1.5. Set caList parameter 1.6. Set crlList parameter 1.7. Set privKey parameter 1.8. authservice() usage 1.9. verifier() usage Chapter 1. Admin Guide 1.1. Overview This module adds support for SIP Identity (see RFC 4474). 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * openssl (libssl). 1.3. Exported Parameters 1.3.1. privKey (string) Filename of private RSA-key of authentication service. This file must be in PEM format. Example 1.1. Set privKey parameter ... modparam("identity", "privKey", "/etc/openser/privkey.pem") ... 1.3.2. authCert (string) Filename of certificate which belongs to privKey. This file must be in PEM format. Example 1.2. Set authCert parameter ... modparam("identity", "authCert", "/etc/openser/cert.pem") ... 1.3.3. certUri (string) URI from which the certificate of the authentication service can be acquired. This string will be placed in the Identity-Info header. Example 1.3. Set certUri parameter ... modparam("identity", "certUri", "http://www.myserver.com/cert.pem") ... 1.3.4. verCert (string) Path containing certificates for the verifier. Certificates must be in PEM format. The URI in the Identity-Info header field is used to find the corresponding certificate for the request. For this purpose the verifier replaces every character which is not alphanumeric, no “_†and no “.†with a “-â€. A “.†at the beginning of the URI is forbidden. If the URI is “http://www.test.com/cert.pem†the verifier will look for the file “http---www.test.com-cert.pemâ€, for example. It is also possible to store a whole certificate chain in a file. In this case certificates must be in right order, end certificate first. Example 1.4. Set verCert parameter ... modparam("identity", "verCert", "/etc/openser/verCert/") ... 1.3.5. caList (string) File containing all trusted (root) certificates for the verifier. Certificates must be in PEM format. Example 1.5. Set caList parameter ... modparam("identity", "caList", "/etc/openser/caList.pem") ... 1.3.6. crlList (string) File containing certificate revocation lists (crls) for the verifier. Setting this parameter is only necessary if useCrls is set to “1â€. Example 1.6. Set crlList parameter ... modparam("identity", "crlList", "/etc/openser/crls.pem") ... 1.3.7. useCrls (integer) Switch to decide whether to use revocation lists (“1â€) or not (“0â€). Default value is “0â€. Example 1.7. Set privKey parameter ... modparam("identity", "useCrls", 1) ... 1.4. Exported Functions 1.4.1. authservice() This function performs the steps of an authentication service. Before you call this function, you have to ensure that * the server is responsible for this request (from URI matches local SIP domain) * the sender of the request is authorized to claim the identity given in the From header field. This function returns the following values: * -3: Date header field does not match validity period of cert. Identity header has not been added. * -2: message out of time (e.g. message to old), Identity header has not been added. * -1: An error occurred. * 1: everything OK, Identity header has been added. This function can be used from REQUEST_ROUTE. Example 1.8. authservice() usage ... # CANCEL and ACK cannot be challenged if ((method=="CANCEL") || (method=="ACK")) { route(1); # forward exit; } # some clients (e.g. Kphone) do not answer, when a BYE is challenged if (method=="BYE") { route(1); # forward exit; } ### Authentication Service ### # check whether I am authoritative if(!from_uri=~".*@mysipdomain.de") { route(1); # forward exit; } if(!proxy_authorize("mysipdomain.de","subscriber")) { proxy_challenge("mysipdomain.de","0"); exit; } if (!db_check_from()) { sl_send_reply("403", "Use From=ID"); exit; } consume_credentials(); authservice(); switch($retcode) { case -3: xlog("L_DBG" ,"authservice: Date header field does not match val idity period of cert\n"); break; case -2: xlog("L_DBG" ,"authservice: msg out of time (max. +- 10 minutes allowed)\n"); break; case -1: xlog("L_DBG" ,"authservice: ERROR, returnvalue: -1\n"); break; case 1: xlog("L_DBG" ,"authservice: everything OK\n"); break; default: xlog("L_DBG" ,"unknown returnvalue of authservice\n"); } route(1); #forward with ($retcode=1) or without ($retcode!=1) Identity h eader ... 1.4.2. verifier() This function performs the steps of an verifier. The returned code tells you the result of the verification: * -438: Signature does not correspond to the message. 438-response should be send. * -437: Certificate cannot be validated. 437-response should be send. * -436: Certificate is not available. 436-response should be send. * -428: Message does not have an Identity header. 428-response should be send. * -3: Error verifying Date header field. * -2: Authentication service is not authoritative. * -1: An unknown error occurred. * 1: verification OK This function can be used from REQUEST_ROUTE. Example 1.9. verifier() usage ... # we have to define the same exceptions as we did for the authentication service if ((method=="CANCEL") || (method=="ACK")) { route(1); # forward exit; } if (method=="BYE") { route(1); # forward exit; } verifier(); switch($retcode) { case -438: xlog("L_DBG" ,"verifier: returnvalue: -438\n"); sl_send_reply("438", "Invalid Identity Header"); exit; break; case -437: xlog("L_DBG" ,"verifier: returnvalue: -437\n"); sl_send_reply("437", "Unsupported Certificate"); exit; break; case -436: xlog("L_DBG" ,"verifier: returnvalue: -436\n"); sl_send_reply("436", "Bad Identity-Info"); exit; break; case -428: xlog("L_DBG" ,"verifier: returnvalue: -428\n"); sl_send_reply("428", "Use Identity Header"); exit; break; case -3: xlog("L_DBG" ,"verifier: error verifying Date header field\n"); exit; break; case -2: xlog("L_DBG" ,"verifier: authentication service is not authorita tive\n"); exit; break; case -1: xlog("L_DBG" ,"verifier: ERROR, returnvalue: -1\n"); exit; break; case 1: xlog("L_DBG" ,"verifier: verification OK\n"); route(1); # forward exit; break; default: xlog("L_DBG" ,"unknown returnvalue of verifier\n"); exit; } exit; ... 1.5. Known Limitations * Certificates are not downloaded. They have to be stored locally. * Call-IDs of valid requests containing an Identity header are not recorded. Hence the verifier does not provide full replay protection. * Authentication service and verifier use the original request. Changes resulting from message processing in OpenSER script are ignored. opensips-2.2.2/modules/identity/doc/000077500000000000000000000000001300170765700174315ustar00rootroot00000000000000opensips-2.2.2/modules/identity/doc/identity.xml000066400000000000000000000023301300170765700220020ustar00rootroot00000000000000 %docentities; ]> Identity Module &osipsname; Alexander Christ Cologne University of Applied Sciences

Alexander.Christ386_AT_web.de
Alexander Christ
Alexander.Christ386_AT_web.de
2007 Alexander Christ, Cologne University of Applied Sciences $Revision: 5901 $ $Date: 2007-03-29$ &admin; &faq; opensips-2.2.2/modules/identity/doc/identity_admin.xml000066400000000000000000000250521300170765700231600ustar00rootroot00000000000000 &adminguide;
Overview This module adds support for SIP Identity (see RFC 4474).
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: openssl (libssl).
Exported Parameters
<varname>privKey</varname> (string) Filename of private RSA-key of authentication service. This file must be in PEM format. Set <varname>privKey</varname> parameter ... modparam("identity", "privKey", "/etc/openser/privkey.pem") ...
<varname>authCert</varname> (string) Filename of certificate which belongs to privKey. This file must be in PEM format. Set <varname>authCert</varname> parameter ... modparam("identity", "authCert", "/etc/openser/cert.pem") ...
<varname>certUri</varname> (string) URI from which the certificate of the authentication service can be acquired. This string will be placed in the Identity-Info header. Set <varname>certUri</varname> parameter ... modparam("identity", "certUri", "http://www.myserver.com/cert.pem") ...
<varname>verCert</varname> (string) Path containing certificates for the verifier. Certificates must be in PEM format. The URI in the Identity-Info header field is used to find the corresponding certificate for the request. For this purpose the verifier replaces every character which is not alphanumeric, no _ and no . with a -. A . at the beginning of the URI is forbidden. If the URI is http://www.test.com/cert.pem the verifier will look for the file http---www.test.com-cert.pem, for example. It is also possible to store a whole certificate chain in a file. In this case certificates must be in right order, end certificate first. Set <varname>verCert</varname> parameter ... modparam("identity", "verCert", "/etc/openser/verCert/") ...
<varname>caList</varname> (string) File containing all trusted (root) certificates for the verifier. Certificates must be in PEM format. Set <varname>caList</varname> parameter ... modparam("identity", "caList", "/etc/openser/caList.pem") ...
<varname>crlList</varname> (string) File containing certificate revocation lists (crls) for the verifier. Setting this parameter is only necessary if useCrls is set to 1. Set <varname>crlList</varname> parameter ... modparam("identity", "crlList", "/etc/openser/crls.pem") ...
<varname>useCrls</varname> (integer) Switch to decide whether to use revocation lists (1) or not (0). Default value is 0. Set <varname>privKey</varname> parameter ... modparam("identity", "useCrls", 1) ...
Exported Functions
<function moreinfo="none">authservice()</function> This function performs the steps of an authentication service. Before you call this function, you have to ensure that the server is responsible for this request (from URI matches local SIP domain) the sender of the request is authorized to claim the identity given in the From header field. This function returns the following values: -3: Date header field does not match validity period of cert. Identity header has not been added. -2: message out of time (e.g. message to old), Identity header has not been added. -1: An error occurred. 1: everything OK, Identity header has been added. This function can be used from REQUEST_ROUTE. <function>authservice()</function> usage ... # CANCEL and ACK cannot be challenged if ((method=="CANCEL") || (method=="ACK")) { route(1); # forward exit; } # some clients (e.g. Kphone) do not answer, when a BYE is challenged if (method=="BYE") { route(1); # forward exit; } ### Authentication Service ### # check whether I am authoritative if(!from_uri=~".*@mysipdomain.de") { route(1); # forward exit; } if(!proxy_authorize("mysipdomain.de","subscriber")) { proxy_challenge("mysipdomain.de","0"); exit; } if (!db_check_from()) { sl_send_reply("403", "Use From=ID"); exit; } consume_credentials(); authservice(); switch($retcode) { case -3: xlog("L_DBG" ,"authservice: Date header field does not match validity period of cert\n"); break; case -2: xlog("L_DBG" ,"authservice: msg out of time (max. +- 10 minutes allowed)\n"); break; case -1: xlog("L_DBG" ,"authservice: ERROR, returnvalue: -1\n"); break; case 1: xlog("L_DBG" ,"authservice: everything OK\n"); break; default: xlog("L_DBG" ,"unknown returnvalue of authservice\n"); } route(1); #forward with ($retcode=1) or without ($retcode!=1) Identity header ...
<function moreinfo="none">verifier()</function> This function performs the steps of an verifier. The returned code tells you the result of the verification: -438: Signature does not correspond to the message. 438-response should be send. -437: Certificate cannot be validated. 437-response should be send. -436: Certificate is not available. 436-response should be send. -428: Message does not have an Identity header. 428-response should be send. -3: Error verifying Date header field. -2: Authentication service is not authoritative. -1: An unknown error occurred. 1: verification OK This function can be used from REQUEST_ROUTE. <function>verifier()</function> usage ... # we have to define the same exceptions as we did for the authentication service if ((method=="CANCEL") || (method=="ACK")) { route(1); # forward exit; } if (method=="BYE") { route(1); # forward exit; } verifier(); switch($retcode) { case -438: xlog("L_DBG" ,"verifier: returnvalue: -438\n"); sl_send_reply("438", "Invalid Identity Header"); exit; break; case -437: xlog("L_DBG" ,"verifier: returnvalue: -437\n"); sl_send_reply("437", "Unsupported Certificate"); exit; break; case -436: xlog("L_DBG" ,"verifier: returnvalue: -436\n"); sl_send_reply("436", "Bad Identity-Info"); exit; break; case -428: xlog("L_DBG" ,"verifier: returnvalue: -428\n"); sl_send_reply("428", "Use Identity Header"); exit; break; case -3: xlog("L_DBG" ,"verifier: error verifying Date header field\n"); exit; break; case -2: xlog("L_DBG" ,"verifier: authentication service is not authoritative\n"); exit; break; case -1: xlog("L_DBG" ,"verifier: ERROR, returnvalue: -1\n"); exit; break; case 1: xlog("L_DBG" ,"verifier: verification OK\n"); route(1); # forward exit; break; default: xlog("L_DBG" ,"unknown returnvalue of verifier\n"); exit; } exit; ...
Known Limitations Certificates are not downloaded. They have to be stored locally. Call-IDs of valid requests containing an Identity header are not recorded. Hence the verifier does not provide full replay protection. Authentication service and verifier use the original request. Changes resulting from message processing in OpenSER script are ignored.
opensips-2.2.2/modules/identity/identity.c000066400000000000000000001154671300170765700206770ustar00rootroot00000000000000/* * Copyright (C) 2007 Alexander Christ, * Cologne University of Applied Sciences * Copyright (C) 2009 Voice Sistem SRL * * This file is part of openser, a free SIP server. * * openser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * openser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2007-03-29 initial version * 2007-04-06 changes regarding pointer types and signess * 2009-01-19 majore rework on header manipulation (searching, creating and * adding SIP headers) (bogdan) */ /* Some functions are based on examples of [VIE-02]. * You can download these examples at * http://www.opensslbook.com/code.html. * * [VIE-02] Viega, John; Messier, Matt; Chandra Pravir: Network Security * with OpenSSL. * First Edition, Beijing, ... : O'Reilly, 2002 * * The function "static STACK_OF(X509) * load_untrusted(char * certfile)" is * based on the function"static STACK_OF(X509) *load_untrusted(char *certfile)" * of the openssl sources. (apps/verify.c, version 0.9.7e, line 290) * * Some functions are based on or copied from the source of the Textops module. */ /** * make strptime available * use 600 for 'Single UNIX Specification, Version 3' */ #include #define _XOPEN_SOURCE 600 /* glibc2 on linux, bsd */ #define _XOPEN_SOURCE_EXTENDED 1 /* solaris */ /** * _XOPEN_SOURCE creates conflict in swab definition in Solaris */ #ifdef __OS_solaris #undef _XOPEN_SOURCE #endif #include #undef _XOPEN_SOURCE #undef _XOPEN_SOURCE_EXTENDED #include #include #include #include #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../pvar.h" #include "../../data_lump.h" #include "../../mem/mem.h" #include "../../parser/parse_hname2.h" #include "../../parser/contact/contact.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" #include "identity.h" /* parameters */ /* cert of authentication service */ static char * authCert = NULL; /* private key of authentication service */ static char * privKey = NULL; /* uri of Identity-Info header field, for authentication * service NOT for verifier */ static char * certUri = NULL; /* path where the verifier can find the certificates */ static char * verCert = NULL; /* file containing the list of trusted CAs for verifier */ static char * caList = NULL; /* file containing the list of revoked certificates (crl) for verifier */ static char * crlList = NULL; /* switch whether crls should be used (1) or not (0), default: not */ static int useCrls = 0; /* global variables */ /* validity of cert of authentication service: notBefore */ static time_t authCert_notBefore = -1; /* validity of cert of authentication service: notAfter */ static time_t authCert_notAfter = -1; /* private key of authentication service */ static EVP_PKEY * privKey_evp = NULL; /* verCert with a '/' at the end */ static char * verCertWithSlash = NULL; /* needed for certificate verification */ static X509_STORE * store = NULL; /* needed for certificate verification */ static X509_STORE_CTX * verify_ctx = NULL; /** module functions */ static cmd_export_t cmds[]={ {"authservice", (cmd_function)authservice_, 0, 0, 0, REQUEST_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE}, {"verifier", (cmd_function)verifier_, 0, 0, 0, REQUEST_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[]={ {"authCert", STR_PARAM, &authCert}, {"privKey", STR_PARAM, &privKey}, {"certUri", STR_PARAM, &certUri}, {"verCert", STR_PARAM, &verCert}, {"caList", STR_PARAM, &caList}, {"crlList", STR_PARAM, &crlList}, {"useCrls", INT_PARAM, &useCrls}, {0,0,0} }; /** module exports */ struct module_exports exports= { "identity", /* name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* parameters to be exportet */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* local processes */ mod_init, /* module initialization function */ (response_function) 0, /* response function */ mod_destroy, /* destroy function */ 0 /* per-child init function */ }; /** * init module function * return value: -1: error * 0: else */ static int mod_init(void) { LM_INFO("initializing ...\n"); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); if(!caList) { LM_ERR("caList not set\n"); return 0; } if(!privKey) { LM_ERR("modparam privKey not set\n"); return 0; } if(!authCert) { LM_ERR("param authCert not set\n"); return 0; } if(!verCert) { LM_ERR("verCert not set\n"); return 0; } if(useCrls && (!crlList)) { LM_ERR("useCrls=1 and crlList not set\n"); return 0; } if(!setAuthCertPeriod()) { LM_ERR("initialization failed\n"); return -1; } if(!readPrivKey()) { LM_ERR("initialization failed\n"); return -1; } if(!certUri) { LM_ERR("certUri not set\n"); return -1; } if(!initVerCertWithSlash()) { LM_ERR("initialization failed\n"); return -1; } if(!prepareCertValidation()) { LM_ERR("initialization failed\n"); return -1; } return 0; } /** * destroy function */ static void mod_destroy(void) { if(privKey_evp) { EVP_PKEY_free(privKey_evp); } if(store) { X509_STORE_free(store); } if(verify_ctx) { X509_STORE_CTX_free(verify_ctx); } EVP_cleanup(); if((verCert != verCertWithSlash) && verCertWithSlash) { pkg_free(verCertWithSlash); } } /* authentication service return value: -1: error -2: message out of time -3: Date header field does not match validity period of cert 1: success */ static int authservice_(struct sip_msg* msg, char* str1, char* str2) { time_t dateHFValue = -1; int retval = 0; char dateHF[MAX_TIME] = "\0"; long dateDelta = -1; /* parse all headers */ if (parse_headers(msg, HDR_EOH_F, 0)!=0) { LM_ERR("failed to parse headers\n"); return -1; } retval = getDate(dateHF, &dateHFValue, msg); switch(retval) { case 1: /* Date header field exists */ dateDelta = getDateDelta(dateHFValue); if((dateDelta >= MAXDATEDELTA_AUTH) || dateDelta < 0) { return -2; } break; case 0: /* Date header field does not exist */ if(!addDate(dateHF, &dateHFValue, msg)) { LM_ERR("addDate failed\n"); return -1; } break; case -1: LM_ERR("error reading Date header field\n"); return -1; break; } if(!authCertMatchesDate(dateHFValue)) { return -3; } /* is content len hdr found ? if(msg->content_length) { if(!addContentLength(msg)) { LM_ERR("addContentLength failed\n"); return -1; } }*/ if(!addIdentity(dateHF, msg)) { LM_ERR("addIdentity failed\n"); return -1; } if(!addIdentityInfo(msg)) { LM_ERR("addIdentityInfo failed\n"); return -1; } return 1; } /* verifier return value: -438: You should send a 438-reply. -437: You should send a 437-reply. -436: You should send a 436-reply. -428: You should send a 428-reply. -3: Error verifying Date header field. -2: Authentication service is not authoritative. -1: An error occurred. 1: verification OK */ static int verifier_(struct sip_msg* msg, char* str1, char* str2) { char identityHF[MAX_IDENTITY] = "\0"; X509 * cert = NULL; int retval = -1; STACK_OF(X509) * certchain = NULL; /* parse all headers */ if (parse_headers(msg, HDR_EOH_F, 0)!=0) { LM_ERR("failed to parse headers\n"); return -1; } retval = getIdentityHF(identityHF, msg); switch(retval) { case 0: /* Identity header field does not exist */ return -428; case -1: LM_ERR("getIdentityHF failed\n"); return -1; } if(!getCert(&cert, &certchain, msg)) { return -436; } if(!validateCert(cert, certchain)) { X509_free(cert); sk_X509_pop_free(certchain, X509_free); return -437; } sk_X509_pop_free(certchain, X509_free); if(!checkAuthority(cert, msg)) { X509_free(cert); return -2; } if(!checkSign(cert, identityHF, msg)) { X509_free(cert); return -438; } if(!checkDate(cert, msg)) { X509_free(cert); return -3; } X509_free(cert); return 1; } static time_t my_timegm(struct tm *tm) { time_t ret; char *tz; tz = getenv("TZ"); setenv("TZ", "", 1); tzset(); ret = mktime(tm); if (tz) setenv("TZ", tz, 1); else unsetenv("TZ"); tzset(); return ret; } /* reads the Date header field of msg return value: -1: error 0: Date header field does not exist 1: success dateHF must point to an array with at least MAX_TIME bytes */ static int getDate(char * dateHF, time_t * dateHFValue, struct sip_msg * msg) { struct hdr_field * date = NULL; struct tm tmDate; if(!dateHF || !dateHFValue || !msg) { LM_ERR("dateHF, dateHFValue or msg not set\n"); return -1; } date = get_header_by_static_name(msg, "Date"); if (!date) { return 0; } if(date->body.len >= MAX_TIME) { LM_ERR("Date header field to long\n"); return -1; } /* date->body.len < MAX_TIME */ strncpy(dateHF, date->body.s, date->body.len); dateHF[date->body.len] = '\0'; if(!strptime(dateHF, DATE_FORMAT, &tmDate)) { LM_ERR("error while parsing Date header field\n"); return -1; } /* covert struct tm to time_t */ *dateHFValue = my_timegm(&tmDate); if(*dateHFValue == -1) { LM_ERR("error while converting timestamp\n"); return -1; } return 1; } /* adds a Date header field to msg return value: 0: error 1: success */ static int addDate(char * dateHF, time_t * dateHFValue, struct sip_msg * msg) { #define DATE_HDR_S "Date: " #define DATE_HDR_L (sizeof(DATE_HDR_S)-1) char* buf; size_t len = 0; struct tm * bd_time = NULL; if(!dateHF || !dateHFValue || !msg) { LM_ERR("dateHF, dateHFValue or msg not set\n"); return 0; } *dateHFValue = time(0); bd_time = gmtime(dateHFValue); if(!bd_time) { LM_ERR("gmtime failed\n"); return 0; } len=strftime(dateHF, MAX_TIME, DATE_FORMAT, bd_time); if (len>MAX_TIME-1 || len==0) { LM_ERR("unexpected time length\n"); return 0; } buf = (char*)pkg_malloc(MAX_TIME+DATE_HDR_L+CRLF_LEN); if(buf== NULL) { LM_ERR("no more memory\n"); return 0; } memcpy( buf, DATE_HDR_S, DATE_HDR_L); memcpy( buf+DATE_HDR_L, dateHF, len); memcpy( buf+DATE_HDR_L+len, CRLF, CRLF_LEN); if ( id_add_header( msg, buf, DATE_HDR_L+len+CRLF_LEN )!=0) { LM_ERR("failed to add Date header\n"); return 0; } return 1; } /* calculates | now - dateHFValue | return value: result, -1: if an error occurred */ static long getDateDelta(time_t dateHFValue) { time_t now; now = time(0); if(now == -1) { LM_ERR("time() call failed\n"); return -1; } return (labs(now - dateHFValue)); } /* checks whether certificate of authentication service matches dateHFValue return value: 1: dateHFValue matches certificate 0: else */ static int authCertMatchesDate(time_t dateHFValue) { if ( (dateHFValue >= authCert_notBefore) && (dateHFValue <= authCert_notAfter)) { return 1; } return 0; } /* adds a Content-Length header field to msg return value: 1: success 0: else annotation: This function is based on the function search_body_f of the Textops module. static int addContentLength(struct sip_msg * msg) { str body; char * tmp = NULL; char buf[MAX_CONTENT_LENGTH] = "\0"; body.s = get_body(msg); if (body.s == 0) { //body does not exist body.len = 0; } else { //body exists body.len = msg->len -(int)(body.s-msg->buf); } snprintf(buf, MAX_CONTENT_LENGTH, "Content-Length: %i\r\n", body.len); buf[MAX_CONTENT_LENGTH - 1] = '\0'; tmp = buf; //we need a char * for &-operation if(add_header_fixup( (void**) &tmp, 1) != 0) { LOG(L_ERR, "idenity: addContentLength: ERROR: add_header_fixup failed\n"); return 0; } if(append_hf_1(msg, tmp, 0) != 1) { pkg_free(tmp); LOG(L_ERR, "identity: addContentLength: ERROR: append_hf_1 failed\n"); return 0; } pkg_free(tmp); return 1; }*/ /* builds digest string of msg Return value: 1: success 0: else digestString must point to an array with at least MAX_DIGEST bytes */ static int makeDigestString(char * digestString, char * dateHF, struct sip_msg * msg) { struct to_body * from = NULL; struct to_body * to = NULL; struct cseq_body * cseq = NULL; struct hdr_field * date = NULL; contact_t * contact = NULL; unsigned int l; str tmp; if(!digestString || !msg) { LM_ERR("not all parameters set\n"); return 0; } l = 0; /* ###from### */ if(parse_from_header(msg) != 0) { LM_ERR("error parsing from header\n"); return 0; } from = get_from(msg); if(!from) { LM_ERR("error getting from header\n"); return 0; } if (l+from->uri.len+1>MAX_DIGEST) { LM_ERR("buffer to short 1\n"); return 0; } memcpy( digestString+l, from->uri.s, from->uri.len); l += from->uri.len; *(digestString+(l++)) = '|'; /* ###To### */ to = get_to(msg); if(!to) { LM_ERR("error getting to header\n"); return 0; } if (l+to->uri.len+1>MAX_DIGEST) { LM_ERR("buffer to short 2\n"); return 0; } memcpy( digestString+l, to->uri.s, to->uri.len); l += to->uri.len; *(digestString+(l++)) = '|'; /* ###callid### */ if(!msg->callid) { LM_ERR("error getting callid header\n"); return 0; } if (l+msg->callid->body.len+1>MAX_DIGEST) { LM_ERR("buffer to short 3\n"); return 0; } memcpy( digestString+l, msg->callid->body.s, msg->callid->body.len); l += msg->callid->body.len; *(digestString+(l++)) = '|'; /* ###CSeq### */ cseq = (struct cseq_body *)msg->cseq->parsed; if (!cseq) { LM_ERR("error getting cseq header\n"); return 0; } tmp.s = cseq->number.s; tmp.len = cseq->number.len; /* strip leading zeros */ while((*(tmp.s) == '0') && (tmp.len > 1)) { (tmp.s)++; (tmp.len)--; } if (l+tmp.len+cseq->method.len+2>MAX_DIGEST) { LM_ERR("buffer to short 4\n"); return 0; } memcpy( digestString+l, tmp.s, tmp.len); l += tmp.len; *(digestString+(l++)) = ' '; memcpy( digestString+l, cseq->method.s, cseq->method.len); l += cseq->method.len; *(digestString+(l++)) = '|'; /* ###Date### */ if(!dateHF) { /* Date header field is taken from msg: verifier */ date = get_header_by_static_name(msg,"Date"); if (!date) { LM_ERR("error getting date header\n"); return 0; } tmp = date->body; } else { /* Date header field is taken from dateHF: authentication service */ tmp.s = dateHF; tmp.len = strlen(tmp.s); } if (l+tmp.len+1>MAX_DIGEST) { LM_ERR("buffer to short 5\n"); return 0; } memcpy( digestString+l, tmp.s, tmp.len); l += tmp.len; *(digestString+(l++)) = '|'; /* ###Contact### */ if(msg->contact) { if(parse_contact(msg->contact) != 0) { LM_ERR("error parsing contact header\n"); return 0; } /* first contact in list */ contact = ((contact_body_t *)(msg->contact->parsed))->contacts; tmp = contact->uri; } else { tmp.len = 0; tmp.s = 0; } if (l+tmp.len+1>MAX_DIGEST) { LM_ERR("buffer to short 6\n"); return 0; } if (tmp.len) { memcpy( digestString+l, tmp.s, tmp.len); l += tmp.len; } *(digestString+(l++)) = '|'; /* ###body### */ if ( get_body(msg,&tmp)!=0 ) { LM_ERR("failed to inspect body\n"); return 0; } if (tmp.len != 0) { if (l+tmp.len+1>MAX_DIGEST) { LM_ERR("buffer to short 7\n"); return 0; } memcpy( digestString+l, tmp.s, tmp.len); l += tmp.len; *(digestString+(l++)) = 0; } LM_DBG("Digest-String=>%s<\n", digestString); return 1; } /* adds a Identity header field to msg return value: 1: success 0: else */ static int addIdentity(char * dateHF, struct sip_msg * msg) { #define IDENTITY_HDR_S "Identity: \"" #define IDENTITY_HDR_L (sizeof(IDENTITY_HDR_S)-1) EVP_MD_CTX ctx; unsigned int siglen = 0; int b64len = 0; unsigned char * sig = NULL; char digestString[MAX_DIGEST]; str buf; if(!makeDigestString(digestString, dateHF, msg)) { LM_ERR("error making digest string\n"); return 0; } EVP_SignInit(&ctx, EVP_sha1()); EVP_SignUpdate(&ctx, digestString, strlen(digestString)); sig = pkg_malloc(EVP_PKEY_size(privKey_evp)); if(!sig) { EVP_MD_CTX_cleanup(&ctx); LM_ERR("failed allocating memory\n"); return 0; } if(!EVP_SignFinal(&ctx, sig, &siglen, privKey_evp)) { EVP_MD_CTX_cleanup(&ctx); pkg_free(sig); LM_ERR("error calculating signature\n"); return 0; } EVP_MD_CTX_cleanup(&ctx); /* ###Base64-encoding### */ /* annotation: The next few lines are based on example 7-11 of [VIE-02] */ b64len = (((siglen + 2) / 3) * 4) + 1; buf.len = IDENTITY_HDR_L + b64len + 1 + CRLF_LEN; buf.s = pkg_malloc(buf.len); if(!buf.s) { pkg_free(sig); LM_ERR("error allocating memory\n"); return 0; } memcpy( buf.s, IDENTITY_HDR_S, IDENTITY_HDR_L); EVP_EncodeBlock((unsigned char*)(buf.s+IDENTITY_HDR_L), sig, siglen); memcpy( buf.s+IDENTITY_HDR_L+b64len, "\""CRLF, CRLF_LEN+1); pkg_free(sig); if ( id_add_header( msg, buf.s, buf.len )!=0) { pkg_free(buf.s); LM_ERR("failed to add Identity header\n"); return 0; } return 1; } /* adds an Identity-Info header field to msg Return value: 1: success 0: else */ static int addIdentityInfo(struct sip_msg * msg) { #define IDENTITY_INFO_HDR_S "Identity-Info: <" #define IDENTITY_INFO_HDR_L (sizeof(IDENTITY_INFO_HDR_S)-1) #define IDENTITY_INFO_PARAM_S ">;alg=rsa-sha1" #define IDENTITY_INFO_PARAM_L (sizeof(IDENTITY_INFO_PARAM_S)-1) char *buf = NULL; int len = 0; char *p; len = IDENTITY_INFO_HDR_L + strlen(certUri) + IDENTITY_INFO_PARAM_L + CRLF_LEN; buf = (char*)pkg_malloc(len); if (buf==NULL) { LM_ERR("no more pkg mem\n"); return 0; } p = buf; memcpy( p, IDENTITY_INFO_HDR_S, IDENTITY_INFO_HDR_L); p += IDENTITY_INFO_HDR_L; memcpy( p, certUri, strlen(certUri) ); p += strlen(certUri); memcpy( p, IDENTITY_INFO_PARAM_S CRLF, IDENTITY_INFO_PARAM_L+CRLF_LEN); if ( id_add_header( msg, buf, len )!=0) { LM_ERR("failed to add Identity-Info header\n"); return 0; } return 1; } /* reads Identity header field from msg Return value: 1: success 0: Identity header field does not exist 1: else identityHF must point to an array with at least MAX_IDENTITY bytes */ static int getIdentityHF(char * identityHF, struct sip_msg * msg) { struct hdr_field * identity = NULL; if(!identityHF || !msg) { LM_ERR("identityHF or msg not set\n"); return -1; } identity = get_header_by_static_name(msg, "Identity"); if (!identity) { /* Identity header field does not exist */ return 0; } if(((identity->body.len) - 2) >= MAX_IDENTITY) { LM_ERR("identity header to long\n"); return -1; } /* " at the beginning and at the end are cutted */ memcpy( identityHF, identity->body.s+1, identity->body.len-2); identityHF[(identity->body.len) - 2] = '\0'; return 1; } /* checks whether msg contains an Identity-Info header field; if yes, the cert is readed Return value: 1: success; *certp + *certchainp point to data 0: else, *certp + *certchainp empty */ static int getCert(X509 ** certp, STACK_OF(X509) ** certchainp, struct sip_msg * msg) { struct hdr_field * identityInfo = NULL; int uriLen = 0; char * end = NULL; char * begin = NULL; char uri[MAX_IDENTITY_INFO] = "\0"; char filename[MAX_FILENAME] = "\0"; FILE * fp = NULL; char backup; if(!certp || !msg || !certchainp) { LM_ERR("certp, certchainp or msg not set\n"); return 0; } identityInfo = get_header_by_static_name(msg, "Identity-Info"); if (!identityInfo) { /* Identity-Info header field does not exist */ return 0; } if((identityInfo->body.len) >= MAX_IDENTITY_INFO) { LM_ERR("identity-info header to long\n"); return 0; } backup = identityInfo->body.s[identityInfo->body.len]; identityInfo->body.s[identityInfo->body.len] = 0; /* check, whether the algorithm is rsa-sha1 */ if(fnmatch("*;*alg*=*rsa-sha1*", identityInfo->body.s, FNM_CASEFOLD) != 0) { LM_INFO("unknown alg-parameter in Identity-Info header field\n"); identityInfo->body.s[identityInfo->body.len] = backup; return 0; } //begin and end of uri (filename) begin = strchr( identityInfo->body.s, '<'); end = strchr( identityInfo->body.s, '>'); identityInfo->body.s[identityInfo->body.len] = backup; if(!begin || !end) { LM_ERR("unable to get uri from Identity-Info header field\n"); return 0; } uriLen = end - begin - 1; if (uriLen+1>MAX_IDENTITY_INFO) { LM_ERR("identity uri too long\n"); return 0; } memcpy(uri, begin+1, uriLen); uri[uriLen] = '\0'; /* replace forbidden characters */ if(!uri2filename(uri)) { LM_ERR("error while uri2filename\n"); return 0; } /* path */ strncpy(filename, verCertWithSlash, MAX_FILENAME - 1); filename[MAX_FILENAME - 1] = '\0'; /* complete filename */ strncat(filename, uri, (MAX_FILENAME - strlen(filename) - 1)); fp = fopen(filename, "r"); if(!fp) { LM_INFO("unable to open file \"%s\"\n", filename); return 0; } LM_DBG("file \"%s\" opened\n", filename); *certp = PEM_read_X509(fp, NULL, NULL, NULL); if(!(*certp)) { fclose(fp); LM_ERR("unable to read cert from file \"%s\"\n", filename); return 0; } fclose(fp); *certchainp = load_untrusted(filename); if(!(*certchainp)) { X509_free(*certp); LM_ERR("unable to read certchain from file \"%s\"\n", filename); return 0; } return 1; } /* checks whether cert is valid Return value: 1: success, cert is valid 0: else Annotation: This function is based on example 10-7 of [VIE-02]. */ static int validateCert(X509 * cert, STACK_OF(X509) * certchain) { int result = 0; if(!cert || !certchain) { LM_ERR("cert or certchain not set\n"); return 0; } /* X509_STORE_CTX_init did not return an error condition in prior versions */ if(X509_STORE_CTX_init(verify_ctx, store, cert, certchain) != 1) { X509_STORE_CTX_cleanup(verify_ctx); LM_ERR("Error initializing verification context\n"); return 0; } result = X509_verify_cert(verify_ctx); X509_STORE_CTX_cleanup(verify_ctx); if(result != 1) { LM_INFO("Error verifying the certificate\n"); return 0; } return 1; } /* checks whether the signing authentication service is authoritative for the URI in the From header field. Return value: 1: authentication service is authoritative 0: else Annotation: This function is based on example 5-8 of [VIE-02]. */ static int checkAuthority(X509 * cert, struct sip_msg * msg) { struct to_body * from = NULL; struct sip_uri fromUri; char hostname[MAX_HOSTNAME] = "\0"; char tmp[MAX_HOSTNAME] = "\0"; int foundDNSName = 0; int num, i, j; X509_EXTENSION * cext; char * extstr; X509V3_EXT_METHOD * meth; void * ext_str = NULL; const unsigned char * data; STACK_OF(CONF_VALUE) * val; CONF_VALUE * nval; if(!cert || !msg) { LM_ERR("msg or cert not set\n"); return 0; } if(parse_from_header(msg) != 0) { LM_ERR("error parsing from header\n"); return 0; } from = get_from(msg); if(!from) { LM_ERR("error getting from header\n"); return 0; } if(parse_uri(from->uri.s, from->uri.len, &fromUri) != 0) { LM_ERR("error parsing from uri\n"); return 0; } if((fromUri.host.len) >= MAX_HOSTNAME) { LM_ERR("from-hostname to long\n"); return 0; } strncpy(hostname, fromUri.host.s, fromUri.host.len); hostname[fromUri.host.len] = '\0'; /* first, check subjectAltName extensions */ num = X509_get_ext_count(cert); for(i = 0; i < num; i++) { cext = X509_get_ext(cert, i); extstr = (char *) OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(cext))); if(!strcmp(extstr, "subjectAltName")) { if(!(meth = (X509V3_EXT_METHOD*)X509V3_EXT_get(cext))) { LM_ERR("X509V3_EXT_get failed\n"); return 0; } data = cext->value->data; if(meth->it) { ext_str = ASN1_item_d2i(NULL, &data, cext->value->length, ASN1_ITEM_ptr(meth->it)); } else { ext_str = meth->d2i(NULL, &data, cext->value->length); } val = meth->i2v(meth, ext_str, NULL); for (j = 0; j < sk_CONF_VALUE_num(val); j++) { nval = sk_CONF_VALUE_value(val, j); if(!strcmp(nval->name, "DNS")) { /* entry of type dNSName found */ foundDNSName = 1; if(hostNameMatch(hostname, nval->value) == 1) { /* authentication service is authoritative */ return 1; } } } } } /* if no subjectAltName extension is found, Common Name of subject will be checked */ if(foundDNSName == 0) { X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, tmp, MAX_HOSTNAME); tmp[MAX_HOSTNAME - 1] = '\0'; if(hostNameMatch(hostname, tmp) == 1) { /* authentication service is authoritative */ return 1; } } return 0; } /* checks the sinature Return value: 1: signature OK 0: else */ static int checkSign(X509 * cert, char * identityHF, struct sip_msg * msg) { EVP_PKEY * pubkey = NULL; char digestString[MAX_DIGEST] = "\0"; int siglen = -1; unsigned char * sigbuf = NULL; int b64len = 0; EVP_MD_CTX ctx; int result = 0; char *p; unsigned long err; if(!cert || !identityHF || !msg) { LM_ERR("cert or identityHF or msg not set\n"); return 0; } if(!makeDigestString(digestString, NULL, msg)) { LM_ERR("error creating digest string\n"); return 0; } b64len = strlen(identityHF); if(b64len < 4) { LM_ERR("base64 string to short\n"); return 0; } /* data size decreases during base64 decoding */ sigbuf = pkg_malloc(b64len + 1); if(!sigbuf) { LM_ERR("error allocating memory\n"); return 0; } siglen = EVP_DecodeBlock(sigbuf, (unsigned char *)identityHF, b64len); if(siglen <= 1) { pkg_free(sigbuf); LM_ERR("error base64-decoding Identity header field\n"); return 0; } /* EVP_DecodeBlock counts the terminating '=', but this padding character does not belong to the signature.*/ p=strstr(identityHF , "="); siglen-=strspn(p , "="); EVP_VerifyInit(&ctx, EVP_sha1()); EVP_VerifyUpdate(&ctx, digestString, strlen(digestString)); pubkey = X509_get_pubkey(cert); if(!pubkey) { EVP_MD_CTX_cleanup(&ctx); pkg_free(sigbuf); LM_ERR("error reading pubkey from cert\n"); return 0; } result = EVP_VerifyFinal(&ctx, sigbuf, siglen, pubkey); EVP_PKEY_free(pubkey); EVP_MD_CTX_cleanup(&ctx); pkg_free(sigbuf); switch(result) { case -1: LM_ERR("error verifying signature\n"); return 0; case 0: err=ERR_get_error(); LM_ERR("signature not valid Reason: %s\n",ERR_reason_error_string(err)); return 0; case 1: return 1; default: LM_ERR("unknown error verifying signature\n"); return 0; } } /* checks the Date header field: - difference between now and Date header field must be smaller than +-MAXDATEDELTA_VER - Date header field and validity period of cert must match Return value: 1: OK 0: else */ static int checkDate(X509 * cert, struct sip_msg * msg) { char dateHF[MAX_TIME] = "\0"; // dummy for calling getDate time_t timeOfDateHF = -1; time_t certNotBefore = -1; time_t certNotAfter = -1; long dateDelta = -1; if(getDate(dateHF, &timeOfDateHF, msg) != 1) { LM_ERR("error getting date of msg\n"); return 0; } /* now <--> Date header field */ dateDelta = getDateDelta(timeOfDateHF); if(dateDelta == -1) { LM_ERR("error calculating date delta\n"); return 0; } if(dateDelta > MAXDATEDELTA_VER) { LM_INFO("date delta > MAXDATEDELTA_VER\n"); return 0; } /* Date header field <--> certificate */ if(!getCertValidity(cert, &certNotBefore, &certNotAfter)) { LM_ERR("getCertValidity failed\n"); return 0; } if((timeOfDateHF < certNotBefore) || (timeOfDateHF > certNotAfter)) { LM_INFO("date header field and validity period of cert " "do not match\n"); return 0; } return 1; } //#########################helper functions######################### /* parses a date from a certificate return value: date; -1 for an error */ static time_t parseX509Date(ASN1_STRING * dateString) { unsigned char * tmp = NULL; struct tm tmDate; if(!dateString) { LM_ERR("dateString not set\n"); return -1; } if((ASN1_UTCTIME_check(dateString)) && (dateString->length == 13)) { /* UTCTIME string, GMT YYMMDDhhmmssZ */ tmp = dateString->data; tmDate.tm_year = (tmp[0] - '0') * 10 + (tmp[1] - '0'); if(tmDate.tm_year < 50) //see chap. 4.1.2.5.1, rfc 3280 { tmDate.tm_year = tmDate.tm_year + 100; } tmDate.tm_mon = (tmp[2] - '0') * 10 + (tmp[3] - '0') - 1; tmDate.tm_mday = (tmp[4] - '0') * 10 + (tmp[5] - '0'); tmDate.tm_hour = (tmp[6] - '0') * 10 + (tmp[7] - '0'); tmDate.tm_min = (tmp[8] - '0') * 10 + (tmp[9] - '0'); tmDate.tm_sec = (tmp[10] - '0') * 10 + (tmp[11] - '0'); return (my_timegm(&tmDate)); } /* needed for years >= 2050 */ if ((ASN1_GENERALIZEDTIME_check(dateString)) && (dateString->length == 15)) { /* GENERALIZEDTIME string; GMT YYYYMMDDhhmmssZ */ tmp = dateString->data; tmDate.tm_year = (tmp[0] - '0') * 1000 + (tmp[1] - '0') * 100 + (tmp[2] - '0') * 10 + (tmp[3] - '0') - 1900; tmDate.tm_mon = (tmp[4] - '0') * 10 + (tmp[5] - '0') - 1; tmDate.tm_mday = (tmp[6] - '0') * 10 + (tmp[7] - '0'); tmDate.tm_hour = (tmp[8] - '0') * 10 + (tmp[9] - '0'); tmDate.tm_min = (tmp[10] - '0') * 10 + (tmp[11] - '0'); tmDate.tm_sec = (tmp[12] - '0') * 10 + (tmp[13] - '0'); return (my_timegm(&tmDate)); } return -1; } /* sets authCert_notBefore and authCert_notAfter, checks whether cert is valid now return value: 1: success 0: else */ static int setAuthCertPeriod(void) { FILE * fp = NULL; X509 * authCertX509 = NULL;//cert of authentication service time_t now = -1; fp = fopen(authCert, "r"); if(!fp) { LM_ERR("could not open authCert: %s\n", authCert); return 0; } authCertX509 = PEM_read_X509(fp, NULL, NULL, NULL); if(!authCertX509) { fclose(fp); LM_ERR("could not read certificate of authentication service\n"); return 0; } fclose (fp); if(!getCertValidity(authCertX509, &authCert_notBefore, &authCert_notAfter)) { X509_free(authCertX509); LM_ERR("could not get validity of authCert\n"); return 0; } X509_free(authCertX509); now = time(0); if(now == -1) { LM_ERR("time failed\n"); return 0; } if(!authCertMatchesDate(now)) { LM_ERR("authCert is not valid now\n"); return 0; } return 1; } /* reads the validity of cert and sets notBefore and notAfter return value: 1: success 0: else */ static int getCertValidity(X509 * cert, time_t * notBefore, time_t * notAfter) { ASN1_STRING * notBeforeSt = NULL; ASN1_STRING * notAfterSt = NULL; if(!cert || !notBefore || !notAfter) { LM_ERR("some parameters not set\n"); return 0; } notBeforeSt = X509_get_notBefore(cert); notAfterSt = X509_get_notAfter(cert); if(!notBeforeSt || !notAfterSt) { LM_ERR("failed to read cert-values\n"); return 0; } *notBefore = parseX509Date(notBeforeSt); *notAfter = parseX509Date(notAfterSt); if(*notBefore < 0 || *notAfter < 0) { LM_ERR("failed to parse notBefore or notAfter\n"); return 0; } return 1; } /* reads private key of authentication service and stores it in privKey_evp return value: 1: success 0: else */ static int readPrivKey(void) { FILE * fp = NULL; fp = fopen(privKey, "r"); if (!fp) { LM_ERR("could not open privKey: %s\n", privKey); return 0; } privKey_evp = PEM_read_PrivateKey(fp, NULL, NULL, NULL); if(!privKey_evp) { fclose(fp); LM_ERR("could not read privKey\n"); return 0; } fclose(fp); return 1; } /* replaces every forbidden char with a '-'. Only alphanumeric characters, '_' and '.' are allowed. If the first char is a '.', 0 is returned also. Return value: 1: success 0: else */ static int uri2filename(char * name) { if(!name) { return 0; } if(name[0] == '.') { LM_ERR("uri starts with '.'\n"); return 0; } while(*name != '\0') { if(!isalnum(*name) && (*name != '_') && (*name != '.')) { *name = '-'; } name++; } return 1; } /* sets verCertWithSlash, verCert is used, If necessary, a '/' is added at the end. Return value: 1: success 0: else */ static int initVerCertWithSlash(void) { DIR * dirTmp = NULL; int len = 0; len = strlen(verCert); if(verCert[len - 1] != '/') { /* add a '/' */ verCertWithSlash = pkg_malloc(len + 2); if(!verCertWithSlash) { LM_ERR("pkg_malloc failed\n"); return 0; } strcpy(verCertWithSlash, verCert); verCertWithSlash[len] = '/'; verCertWithSlash[len+1] = '\0'; } else { verCertWithSlash = verCert; } /* check whether path exists or not */ dirTmp = opendir(verCertWithSlash); if(!dirTmp) { LM_ERR("unable to open verCert directory\n"); return 0; } closedir(dirTmp); return 1; } /* prepares cert validation for verifier return value: 1: success 0: else annotation: This function is based on example 10-7 of [VIE-02]. */ static int prepareCertValidation(void) { X509_LOOKUP * lookup = NULL; seed_prng(); /* create the cert store and set the verify callback */ store = X509_STORE_new(); if(!store) { LM_ERR("Error creating X509_STORE_CTX object\n"); return 0; } X509_STORE_set_verify_cb_func(store, verify_callback); /* load the CA certificates and CRLs */ if(X509_STORE_load_locations(store, caList, NULL) != 1) { LM_ERR("Error loading the caList\n"); return 0; } if(X509_STORE_set_default_paths(store) != 1) { LM_ERR("Error loading the system-wide CA certificates\n"); return 0; } if(!(lookup = X509_STORE_add_lookup (store, X509_LOOKUP_file ()))) { LM_ERR("Error creating X509_LOOKUP object\n"); return 0; } if(useCrls) { if(X509_load_crl_file(lookup, crlList, X509_FILETYPE_PEM) < 1) { /* changed from !=1 to < 1 return value = number of loaded crls */ LM_ERR("Error reading the crlList file\n"); return 0; } /* enabling verification against CRLs is not possible in prior versions */ /* set the flags of the store so that CRLs are consulted */ X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } /* create a verification context and initialize it */ if(!(verify_ctx = X509_STORE_CTX_new())) { LM_ERR("Error creating X509_STORE_CTX object\n"); return 0; } return 1; } /* logs the reason of an negative verification result return value: ok annotation: This function is based on example 10-7 of [VIE-02]. */ static int verify_callback(int ok, X509_STORE_CTX * stor) { if (!ok) { LM_INFO("certificate validation failed: %s\n", X509_verify_cert_error_string(stor->error)); } return ok; } /* annotation: This function is copied from file ssl/common.c of [VIE-02]. */ void seed_prng(void) { RAND_load_file("/dev/urandom", 1024); } /* loads a certificate chain return value: STACK_OF(X509) *: on success NULL: else annotation: This function is based on the function "static STACK_OF(X509) *load_untrusted(char *certfile)" of the openssl sources. (apps/verify.c, version 0.9.7e, line 290) */ static STACK_OF(X509) * load_untrusted(char * certfile) { STACK_OF(X509_INFO) *sk=NULL; STACK_OF(X509) *stack=NULL, *ret=NULL; BIO *in=NULL; X509_INFO *xi=NULL; stack = sk_X509_new_null(); if(!stack) { LM_ERR("memory allocation failure\n"); goto end; } in=BIO_new_file(certfile, "r"); if(!in) { LM_ERR("error opening the file, %s\n",certfile); goto end; } /* This loads from a file, a stack of x509/crl/pkey sets */ sk=PEM_X509_INFO_read_bio(in,NULL,NULL,NULL); if(!sk) { LM_ERR("error reading the file, %s\n",certfile); goto end; } /* scan over it and pull out the certs */ while(sk_X509_INFO_num(sk)) { xi=sk_X509_INFO_shift(sk); if (xi->x509 != NULL) { sk_X509_push(stack,xi->x509); xi->x509 = NULL; } X509_INFO_free(xi); } if(!sk_X509_num(stack)) { LM_ERR("no certificates in file, %s\n",certfile); sk_X509_free(stack); goto end; } ret = stack; end: BIO_free(in); sk_X509_INFO_free(sk); return(ret); } /* counts how many '.' arg contains return value: number of '.' (>=0) -1: error */ static int pcount(char * arg) { int i = 0; if(!arg) { LM_ERR("arg not set\n"); return -1; } while(*arg != '\0') { if(*arg == '.') { i++; } arg++; } return(i); } /* checks if fromHostname matches certHostname return value: 1: fromHostname matches certHostname 0: else */ static int hostNameMatch(char * fromHostname, char * certHostname) { if(!fromHostname || !certHostname) { LM_ERR("fromHostname or certHostname not set\n"); return 0; } if(strpbrk(certHostname, HOSTNAME_ILLCHAR)) { /* avoid that '[' or '?' are interpreted by fnmatch */ LM_ERR("illegal chars in certHostname\n"); return 0; } if(pcount(certHostname) != pcount(fromHostname)) { /* check, whether number of points is equal rfc2818: "Names may contain the wildcard character * which is considered to match any single domain name component or component fragment. E.g., *.a.com matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com but not bar.com. */ LM_INFO("pcount of certHostname and fromHostname not matched - " "certHostname=[%s] - fromHostname=[%s]\n",certHostname,fromHostname); return 0; } /* FNM_CASEFOLD = case-insensitive */ if(fnmatch(certHostname, fromHostname, FNM_CASEFOLD) != 0) { LM_INFO("certHostname and fromHostname do not match\n"); return 0; } return 1; } static int id_add_header(struct sip_msg* msg, char* s, int len) { struct lump* anchor; anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0); if (!anchor) { LM_ERR("can't get anchor\n"); return -1; } if (!insert_new_lump_before(anchor, s, len, 0)) { LM_ERR("can't insert lump\n"); return -1; } return 0; } opensips-2.2.2/modules/identity/identity.h000066400000000000000000000104371300170765700206730ustar00rootroot00000000000000/* *Copyright (C) 2007 Alexander Christ, * Cologne University of Applied Sciences * * This file is part of openser, a free SIP server. * * openser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * openser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2007-03-29 initial version * 2007-04-06 added GPL copyright, #ifndef ... ; changed MIN macro * */ #ifndef _IDENTITY_H_ #define _IDENTITY_H_ /* Solaris does not define FNM_CASEFOLD for fnmatch() */ #ifdef __OS_solaris #define FNM_CASEFOLD FNM_IGNORECASE #endif #define MAX_TIME 64 //max. length of timestamp #define DATE_FORMAT "%a, %d %b %Y %H:%M:%S GMT" #define MAXDATEDELTA_AUTH 600 //max. allowed | dateHF - now | for authentication service in seconds #define MAXDATEDELTA_VER 3600 //max. allowed | dateHF - now | for verifier in seconds #define MAX_CONTENT_LENGTH 25 //max. length of body of Content-Length header field #define MAX_DIGEST 2048 //max. length of digest string #define MAX_IDENTITY 800 // max. length of Identity header field #define MAX_IDENTITY_INFO 100 //max. length of Idenity-Info header field #define MAX_FILENAME 200 //max. length of string containing a filename #define MAX_HOSTNAME 50 //max. lenght of hostname of From header field #define HOSTNAME_ILLCHAR "?[" //forbidden characters in certHostname #define MIN(a, b) ((a < b) ? a : b) static int mod_init(void); static void mod_destroy(void); static int authservice_(struct sip_msg* msg, char* str1, char* str2); static int verifier_(struct sip_msg* msg, char* str1, char* str2); static int getDate(char * dateHF, time_t * dateHFValue, struct sip_msg * msg); static int addDate(char * dateHF, time_t * dateHFValue, struct sip_msg * msg); static long getDateDelta(time_t dateHFValue); static int authCertMatchesDate(time_t dateHFValue); static int makeDigestString(char * digestString, char * dateHF, struct sip_msg * msg); static int addIdentity(char * dateHF, struct sip_msg * msg); static int addIdentityInfo(struct sip_msg * msg); static int getIdentityHF(char * identityHF, struct sip_msg * msg); static int getCert(X509 ** certp, STACK_OF(X509) ** certchainp, struct sip_msg * msg); static int validateCert(X509 * cert, STACK_OF(X509) * certchain); static int checkAuthority(X509 * cert, struct sip_msg * msg); static int checkSign(X509 * cert, char * identityHF, struct sip_msg * msg); static int checkDate(X509 * cert, struct sip_msg * msg); static int uri2filename(char * name); static time_t parseX509Date(ASN1_STRING * dateString); static int setAuthCertPeriod(); static int getCertValidity(X509 * cert, time_t * notBefore, time_t * notAfter); static int readPrivKey(); static int initVerCertWithSlash(); static int prepareCertValidation(); static int verify_callback(int ok, X509_STORE_CTX * stor); static void seed_prng(void); static STACK_OF(X509) * load_untrusted(char * certfile); static int pcount(char * arg); static int hostNameMatch(char * fromHostname, char * certHostname); static int id_add_header(struct sip_msg* msg, char* s, int len); #endif opensips-2.2.2/modules/identity/openssl-license.txt000066400000000000000000000142061300170765700225330ustar00rootroot00000000000000 LICENSE ISSUES ============== The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact openssl-core@openssl.org. OpenSSL License --------------- /* ==================================================================== * Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ Original SSLeay License ----------------------- /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ opensips-2.2.2/modules/imc/000077500000000000000000000000001300170765700156035ustar00rootroot00000000000000opensips-2.2.2/modules/imc/Makefile000066400000000000000000000003161300170765700172430ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=imc.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/imc/README000066400000000000000000000201161300170765700164630ustar00rootroot00000000000000imc Module Anca-Maria Vamanu Stefan Popescu Daniel-Constantin Mierla Edited by Anca-Maria Vamanu Copyright © 2006-2008 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url (str) 1.3.2. rooms_table (str) 1.3.3. members_table (str) 1.3.4. hash_size (integer) 1.3.5. imc_cmd_start_char (str) 1.3.6. outbound_proxy (str) 1.4. Exported Functions 1.4.1. imc_manager() 1.5. Exported MI Functions 1.5.1. imc_list_rooms 1.5.2. imc_list_members 1.6. Exported Statistics 1.6.1. active_rooms 1.7. IMC Commands 1.8. Installation List of Examples 1.1. Set db_url parameter 1.2. Set rooms_table parameter 1.3. Set members_table parameter 1.4. Set hash_size parameter 1.5. Set imc_cmd_start_char parameter 1.6. Set outbound_proxy parameter 1.7. Usage of imc_manager() function 1.8. List of commands Chapter 1. Admin Guide 1.1. Overview This module offers support for instant message conference. It follows the architecture of IRC channels, you can send commands embedded in MESSAGE body, because there are no SIP UA clients which have GUI for IM conferencing. You have to define an URI corresponding to im conferencing manager, where user can send commands to create a new conference room. Once the conference room is created, users can send commands directly to conferece's URI. To ease the integration in the configuration file, the interpreter of the IMC commands are embeded in the module, from configuration poin of view, there is only one function which has to be executed for both messages and commands. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * mysql. * tm. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. db_url (str) The database url. The default value is “mysql://opensips:opensipsrw@localhost/opensipsâ€. Example 1.1. Set db_url parameter ... modparam("imc", "db_url", "dbdriver://username:password@dbhost/dbname") ... 1.3.2. rooms_table (str) The name of the table storing IMC rooms. The default value is "imc_rooms". Example 1.2. Set rooms_table parameter ... modparam("imc", "rooms_table", "rooms") ... 1.3.3. members_table (str) The name of the table storing IMC members. The default value is "imc_members". Example 1.3. Set members_table parameter ... modparam("imc", "rooms_table", "members") ... 1.3.4. hash_size (integer) The power of 2 to get the size of the hash table used for storing members and rooms. The default value is 4 (resultimg in hash size 16). Example 1.4. Set hash_size parameter ... modparam("imc", "hash_size", 8) ... 1.3.5. imc_cmd_start_char (str) The character which indicates that the body of the message is a command. The default value is "#". Example 1.5. Set imc_cmd_start_char parameter ... modparam("imc", "imc_cmd_start_char", "#") ... 1.3.6. outbound_proxy (str) The SIP address used as next hop when sending the message. Very useful when using OpenSIPS with a domain name not in DNS, or when using a separate OpenSIPS instance for imc processing. If not set, the message will be sent to the address in destination URI. Default value is NULL. Example 1.6. Set outbound_proxy parameter ... modparam("imc", "outbound_proxy", "sip:opensips.org;transport=tcp") ... 1.4. Exported Functions 1.4.1. imc_manager() Handles Message method.It detects if the body of the message is a conference command.If so it executes it, otherwise it sends the message to all the members in the room. This function can be used from REQUEST_ROUTE. Example 1.7. Usage of imc_manager() function ... # the rooms will be named chat-xyz to avoid overlapping # with usernames if(is_method("MESSAGE) && (uri=~ "sip:chat-[0-9]+@" || (uri=~ "sip:chat-manager@") imc_manager(); ... 1.5. Exported MI Functions 1.5.1. imc_list_rooms Lists of the IM Conferencing rooms. Name: imc_list_rooms Parameters: none MI FIFO Command Format: :imc_list_rooms:_reply_fifo_file_ _empty_line_ 1.5.2. imc_list_members Listing of the members in IM Conferencing rooms. Name: imc_list_members Parameters: * _room_ : the room for which you want to list the members MI FIFO Command Format: :imc_list_members:_reply_fifo_file_ _room_ _empty_line_ 1.6. Exported Statistics 1.6.1. active_rooms Number of active IM Conferencing rooms. 1.7. IMC Commands A command is identified by the starting character. A command must be written in one line. By default, the starting character is '#'. You can change it via "imc_cmd_start_char" parameter. Next picture presents the list of commands and their parameters. Example 1.8. List of commands ... 1.create -creates a conference room -takes 2 parameters: 1) the name of the room 2)optional- "private" -if present the created room is private and new members can be added only though invitations -the user is added as the first member and owner of the room -eg: #create chat-000 private 2.join -makes the user member of a room -takes one optional parameter - the address of the room -if not present it will be considered to be the address in the To header of the message -if the room does not exist the command is treated as create -eg:join sip:chat-000@opensips.org, or just, #join, sent to sip:chat-000@opensips.org 3.invite -invites a user to become a member of a room -takes 2 parameters: 1)the complete address of the user 2)the address of the room -if not present it will be considered to be the address in the To header of the message -only certain users have the right to invite other user: the owner and the administrators -eg: #invite sip:john@opensips.org sip:chat-000@opensips.org or #invite john@opensips.org sent to sip:chat-000@opensips.org 4.accept -accepting an invitation -takes one optional parameter - the address of the room - if not present it will be considered to be the address in the To header of the message -eg: #accept sip:john@opensips.org 5.deny -rejects an invitation -the parameter is the same as for accept 6.remove -deletes a member from a room -takes 2 parameters: 1)the complete address of the member 2)the address of the room -if not present it will be considered to be the address in the To header of the message -only certain members have the right to remove other members -eg: #remove sip:john@opensips.org, sent to sip:chat-000@opensips.org 7.exit -leaving a room -takes one optional parameter - the address of the room - if not present it will be considered to be the address in the To header of the message -if the user is the owner of the room, the room will be destroyed 8.destroy -removing a room -the parameter is the same as for exit -only the owner of a room has the right to destroy it 9.list -list members in a room ... 1.8. Installation Before running OpenSIPS with IMC, you have to setup the database tables where the module will store the data. For that, if the tables were not created by the installation script or you choose to install everything by yourself you can use the imc-create.sql SQL script in the database directories in the opensips/scripts folder as template. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. opensips-2.2.2/modules/imc/doc/000077500000000000000000000000001300170765700163505ustar00rootroot00000000000000opensips-2.2.2/modules/imc/doc/imc.cfg000066400000000000000000000037151300170765700176070ustar00rootroot00000000000000# # IMCONF usage example # # # running in debug mode (log level 4, log to stderr, stay in foreground) debug_mode=yes check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 listen=192.168.2.132 # ------------------ module loading ---------------------------------- loadmodule "modules/mysql/mysql.so" loadmodule "modules/textops/textops.so" loadmodule "modules/sl/sl.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/imc/imc.so" loadmodule "modules/tm/tm.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/mi_fifo/mi_fifo.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- usrloc params -- modparam("usrloc", "db_mode", 2) # -- registrar params -- modparam("registrar", "default_expires", 120) # -- registrar params -- modparam("usrloc", "db_mode", 0) # -- imc params -- modparam("imc", "imc_cmd_start_char", "#") modparam("imc|usrloc", "db_url", "mysql://opensips:opensipsrw@192.168.2.132/opensips") # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 15 ) modparam("tm", "wt_timer", 10 ) route{ if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); exit; }; # registrations - if(is_method("REGISTER")) { save("location"); exit; } if($ru=~"sip:q.*@") { # IMC - message xdbg("script: message from [$fu] r-uri [$ru] msg [$rb]\n"); if(is_method("MESSAGE")) { log("MESSAGE received -> processing with imc\n"); sl_send_reply("200", "ok"); imc_manager(); exit; } else { sl_send_reply("404", "not found"); exit; }; } else { xdbg("script: message not for room, from [$fu] r-uri [$ru] msg [$rb]\n"); if(!lookup("location")) { sl_send_reply("404", "not found"); exit; } t_relay(); exit(); }; } opensips-2.2.2/modules/imc/doc/imc.xml000066400000000000000000000022341300170765700176430ustar00rootroot00000000000000 %docentities; ]> imc Module &osipsname; Anca-Maria Vamanu Stefan Popescu Daniel-Constantin Mierla Anca-Maria Vamanu 2006-2008 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/imc/doc/imc_admin.xml000066400000000000000000000230531300170765700210150ustar00rootroot00000000000000 &adminguide;
Overview This module offers support for instant message conference. It follows the architecture of IRC channels, you can send commands embedded in MESSAGE body, because there are no SIP UA clients which have GUI for IM conferencing. You have to define an URI corresponding to im conferencing manager, where user can send commands to create a new conference room. Once the conference room is created, users can send commands directly to conferece's URI. To ease the integration in the configuration file, the interpreter of the IMC commands are embeded in the module, from configuration poin of view, there is only one function which has to be executed for both messages and commands.
Dependencies
&osips; Modules The following modules must be loaded before this module: mysql. tm.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>db_url</varname> (str) The database url. The default value is &defaultdb;. Set <varname>db_url</varname> parameter ... modparam("imc", "db_url", "&exampledb;") ...
<varname>rooms_table</varname> (str) The name of the table storing IMC rooms. The default value is "imc_rooms". Set <varname>rooms_table</varname> parameter ... modparam("imc", "rooms_table", "rooms") ...
<varname>members_table</varname> (str) The name of the table storing IMC members. The default value is "imc_members". Set <varname>members_table</varname> parameter ... modparam("imc", "rooms_table", "members") ...
<varname>hash_size</varname> (integer) The power of 2 to get the size of the hash table used for storing members and rooms. The default value is 4 (resultimg in hash size 16). Set <varname>hash_size</varname> parameter ... modparam("imc", "hash_size", 8) ...
<varname>imc_cmd_start_char</varname> (str) The character which indicates that the body of the message is a command. The default value is "#". Set <varname>imc_cmd_start_char</varname> parameter ... modparam("imc", "imc_cmd_start_char", "#") ...
<varname>outbound_proxy</varname> (str) The SIP address used as next hop when sending the message. Very useful when using OpenSIPS with a domain name not in DNS, or when using a separate OpenSIPS instance for imc processing. If not set, the message will be sent to the address in destination URI. Default value is NULL. Set <varname>outbound_proxy</varname> parameter ... modparam("imc", "outbound_proxy", "sip:opensips.org;transport=tcp") ...
Exported Functions
<function moreinfo="none">imc_manager()</function> Handles Message method.It detects if the body of the message is a conference command.If so it executes it, otherwise it sends the message to all the members in the room. This function can be used from REQUEST_ROUTE. Usage of <varname>imc_manager()</varname> function ... # the rooms will be named chat-xyz to avoid overlapping # with usernames if(is_method("MESSAGE) && (uri=~ "sip:chat-[0-9]+@" || (uri=~ "sip:chat-manager@") imc_manager(); ...
Exported MI Functions
<function moreinfo="none">imc_list_rooms</function> Lists of the IM Conferencing rooms. Name: imc_list_rooms Parameters: none MI FIFO Command Format: :imc_list_rooms:_reply_fifo_file_ _empty_line_
<function moreinfo="none">imc_list_members</function> Listing of the members in IM Conferencing rooms. Name: imc_list_members Parameters: _room_ : the room for which you want to list the members MI FIFO Command Format: :imc_list_members:_reply_fifo_file_ _room_ _empty_line_
Exported Statistics
<function moreinfo="none">active_rooms</function> Number of active IM Conferencing rooms.
IMC Commands A command is identified by the starting character. A command must be written in one line. By default, the starting character is '#'. You can change it via "imc_cmd_start_char" parameter. Next picture presents the list of commands and their parameters. List of commands ... 1.create -creates a conference room -takes 2 parameters: 1) the name of the room 2)optional- "private" -if present the created room is private and new members can be added only though invitations -the user is added as the first member and owner of the room -eg: #create chat-000 private 2.join -makes the user member of a room -takes one optional parameter - the address of the room -if not present it will be considered to be the address in the To header of the message -if the room does not exist the command is treated as create -eg:join sip:chat-000@opensips.org, or just, #join, sent to sip:chat-000@opensips.org 3.invite -invites a user to become a member of a room -takes 2 parameters: 1)the complete address of the user 2)the address of the room -if not present it will be considered to be the address in the To header of the message -only certain users have the right to invite other user: the owner and the administrators -eg: #invite sip:john@opensips.org sip:chat-000@opensips.org or #invite john@opensips.org sent to sip:chat-000@opensips.org 4.accept -accepting an invitation -takes one optional parameter - the address of the room - if not present it will be considered to be the address in the To header of the message -eg: #accept sip:john@opensips.org 5.deny -rejects an invitation -the parameter is the same as for accept 6.remove -deletes a member from a room -takes 2 parameters: 1)the complete address of the member 2)the address of the room -if not present it will be considered to be the address in the To header of the message -only certain members have the right to remove other members -eg: #remove sip:john@opensips.org, sent to sip:chat-000@opensips.org 7.exit -leaving a room -takes one optional parameter - the address of the room - if not present it will be considered to be the address in the To header of the message -if the user is the owner of the room, the room will be destroyed 8.destroy -removing a room -the parameter is the same as for exit -only the owner of a room has the right to destroy it 9.list -list members in a room ...
Installation Before running &osips; with IMC, you have to setup the database tables where the module will store the data. For that, if the tables were not created by the installation script or you choose to install everything by yourself you can use the imc-create.sql SQL script in the database directories in the opensips/scripts folder as template. You can also find the complete database documentation on the project webpage, &osipsdbdocslink;.
opensips-2.2.2/modules/imc/imc.c000066400000000000000000000423551300170765700165300ustar00rootroot00000000000000/* * imc module - instant messaging conferencing implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2006-10-06 first version (anca) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "../../db/db_res.h" #include "../../dprint.h" #include "../../ut.h" #include "../../timer.h" #include "../../str.h" #include "../../mem/shm_mem.h" #include "../../db/db.h" #include "../../parser/parse_from.h" #include "../../parser/parse_content.h" #include "../../parser/contact/parse_contact.h" #include "../../resolve.h" #include "../../mi/mi.h" #include "../tm/tm_load.h" #include "imc_mng.h" #include "imc_cmd.h" /** parameters */ db_con_t *imc_db = NULL; db_func_t imc_dbf; static str db_url = {NULL, 0}; str outbound_proxy = {NULL, 0}; static str rooms_table = str_init("imc_rooms"); static str members_table = str_init("imc_members"); static str imc_col_username = str_init("username"); static str imc_col_domain = str_init("domain"); static str imc_col_flag = str_init("flag"); static str imc_col_room = str_init("room"); static str imc_col_name = str_init("name"); imc_hentry_p _imc_htable = NULL; int imc_hash_size = 4; str imc_cmd_start_str = str_init(IMC_CMD_START_STR); char imc_cmd_start_char; /** module functions */ static int mod_init(void); static int child_init(int); static int imc_manager(struct sip_msg*, char *, char *); static struct mi_root* imc_mi_list_rooms(struct mi_root* cmd, void* param); static struct mi_root* imc_mi_list_members(struct mi_root* cmd, void* param); void destroy(void); /** TM bind */ struct tm_binds tmb; /** TM callback function */ void inv_callback( struct cell *t, int type, struct tmcb_params *ps); static cmd_export_t cmds[]={ {"imc_manager", (cmd_function)imc_manager, 0, 0, 0, REQUEST_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[]={ {"db_url", STR_PARAM, &db_url.s}, {"hash_size", INT_PARAM, &imc_hash_size}, {"imc_cmd_start_char", STR_PARAM, &imc_cmd_start_str.s}, {"rooms_table", STR_PARAM, &rooms_table.s}, {"members_table", STR_PARAM, &members_table.s}, {"outbound_proxy", STR_PARAM, &outbound_proxy.s}, {0,0,0} }; #ifdef STATISTICS #include "../../statistics.h" stat_var* imc_active_rooms; static stat_export_t imc_stats[] = { {"active_rooms" , 0, &imc_active_rooms }, {0,0,0} }; #endif static mi_export_t mi_cmds[] = { { "imc_list_rooms", 0, imc_mi_list_rooms, MI_NO_INPUT_FLAG, 0, 0 }, { "imc_list_members", 0, imc_mi_list_members, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "imc", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported commands */ 0, /* exported async commands */ params, /* exported parameters */ #ifdef STATISTICS imc_stats, #else 0, /* exported statistics */ #endif mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* mod init */ (response_function) 0, /* response handler */ (destroy_function) destroy, /* destroy function */ child_init /* child init */ }; /** * the initiating function */ int add_from_db(void) { imc_member_p member = NULL; int i, j, flag; db_key_t mq_result_cols[4], mquery_cols[2]; db_key_t rq_result_cols[4]; db_val_t mquery_vals[2]; db_res_t *r_res= NULL; db_res_t *m_res= NULL; db_row_t *m_row = NULL, *r_row = NULL; db_val_t *m_row_vals, *r_row_vals = NULL; str name, domain; imc_room_p room = NULL; int er_ret = -1; rq_result_cols[0] = &imc_col_name; rq_result_cols[1] = &imc_col_domain; rq_result_cols[2] = &imc_col_flag; mq_result_cols[0] = &imc_col_username; mq_result_cols[1] = &imc_col_domain; mq_result_cols[2] = &imc_col_flag; mquery_cols[0] = &imc_col_room; mquery_vals[0].type = DB_STR; mquery_vals[0].nul = 0; if(imc_dbf.use_table(imc_db, &rooms_table)< 0) { LM_ERR("use_table failed\n"); return -1; } if(imc_dbf.query(imc_db,0, 0, 0, rq_result_cols,0, 3, 0,&r_res)< 0) { LM_ERR("failed to querry table\n"); return -1; } if(r_res && r_res->n<=0) { LM_INFO("the query returned no result\n"); imc_dbf.free_result(imc_db, r_res); r_res = NULL; return 0; } LM_DBG("found %d rooms\n", r_res->n); for(i =0 ; i< r_res->n ; i++) { /*add rooms*/ r_row = &r_res->rows[i]; r_row_vals = ROW_VALUES(r_row); name.s = r_row_vals[0].val.str_val.s; name.len = strlen(name.s); domain.s = r_row_vals[1].val.str_val.s; domain.len = strlen(domain.s); flag = r_row_vals[2].val.int_val; room = imc_add_room(&name, &domain, flag); if(room == NULL) { LM_ERR("failed to add room\n "); goto error; } /* add members */ if(imc_dbf.use_table(imc_db, &members_table)< 0) { LM_ERR("use_table failed\n "); goto error; } mquery_vals[0].val.str_val= room->uri; if(imc_dbf.query(imc_db, mquery_cols, 0, mquery_vals, mq_result_cols, 1, 3, 0, &m_res)< 0) { LM_ERR("failed to querry table\n"); goto error; } if(m_res && m_res->n <=0) { LM_INFO("the query returned no result\n"); er_ret = 0; goto error; /* each room must have at least one member*/ } for(j =0; j< m_res->n; j++) { m_row = &m_res->rows[j]; m_row_vals = ROW_VALUES(m_row); name.s = m_row_vals[0].val.str_val.s; name.len = strlen(name.s); domain.s = m_row_vals[1].val.str_val.s; domain.len = strlen(domain.s); flag = m_row_vals[2].val.int_val; LM_DBG("adding memeber: [name]=%.*s [domain]=%.*s" " in [room]= %.*s\n",name.len, name.s, domain.len,domain.s, room->uri.len, room->uri.s); member = imc_add_member(room, &name, &domain, flag); if(member == NULL) { LM_ERR("failed to adding member\n "); goto error; } imc_release_room(room); } if(m_res) { imc_dbf.free_result(imc_db, m_res); m_res = NULL; } } if(imc_dbf.use_table(imc_db, &members_table)< 0) { LM_ERR("use table failed\n "); goto error; } if(imc_dbf.delete(imc_db, 0, 0 , 0, 0) < 0) { LM_ERR("failed to delete information from db\n"); goto error; } if(imc_dbf.use_table(imc_db, &rooms_table)< 0) { LM_ERR("use table failed\n "); goto error; } if(imc_dbf.delete(imc_db, 0, 0 , 0, 0) < 0) { LM_ERR("failed to delete information from db\n"); goto error; } if(r_res) { imc_dbf.free_result(imc_db, r_res); r_res = NULL; } if(m_res) { imc_dbf.free_result(imc_db, m_res); m_res = NULL; } return 0; error: if(r_res) { imc_dbf.free_result(imc_db, r_res); r_res = NULL; } if(m_res) { imc_dbf.free_result(imc_db, m_res); m_res = NULL; } if(room) imc_release_room(room); return er_ret; } static int mod_init(void) { LM_INFO("initializing ...\n"); if(imc_hash_size <= 0) { LM_ERR("invalid hash size\n"); return -1; } imc_hash_size = 1 << imc_hash_size; if(imc_htable_init() < 0) { LM_ERR("initializing hash table\n"); return -1; } imc_cmd_start_str.len = strlen(imc_cmd_start_str.s); if(outbound_proxy.s) outbound_proxy.len = strlen(outbound_proxy.s); rooms_table.len = strlen(rooms_table.s); members_table.len = strlen(members_table.s); /* binding to mysql module */ init_db_url( db_url , 0 /*cannot be null*/); LM_DBG("db_url=%s/%d/%p\n", ZSW(db_url.s), db_url.len, db_url.s); if (db_bind_mod(&db_url, &imc_dbf)) { LM_DBG("database module not found\n"); return -1; } imc_db = imc_dbf.init(&db_url); if (!imc_db) { LM_ERR("failed to connect to the database\n"); return -1; } /* read the informations stored in db */ if(add_from_db() <0) { LM_ERR("failed to get information from db\n"); return -1; } /* load TM API */ if (load_tm_api(&tmb)!=0) { LM_ERR("unable to load tm api\n"); return -1; } imc_cmd_start_char = imc_cmd_start_str.s[0]; if(imc_db) imc_dbf.close(imc_db); imc_db = NULL; return 0; } /** * child init */ static int child_init(int rank) { if (imc_dbf.init==0) { LM_ERR("database not bound\n"); return -1; } imc_db = imc_dbf.init(&db_url); if (!imc_db) { LM_ERR("child %d: Error while connecting database\n", rank); return -1; } else { if (imc_dbf.use_table(imc_db, &rooms_table) < 0) { LM_ERR("child %d: Error in use_table '%.*s'\n", rank, rooms_table.len, rooms_table.s); return -1; } if (imc_dbf.use_table(imc_db, &members_table) < 0) { LM_ERR("child %d: Error in use_table '%.*s'\n", rank, members_table.len, members_table.s); return -1; } LM_DBG("child %d: Database connection opened successfully\n", rank); } return 0; } static int imc_manager(struct sip_msg* msg, char *str1, char *str2) { imc_cmd_t cmd; str body; struct sip_uri from_uri, *pto_uri=NULL, *pfrom_uri=NULL; struct to_body *pfrom; if ( get_body( msg, &body)!=0 || body.len==0) { LM_DBG("empty body!\n"); goto error; } if(parse_sip_msg_uri(msg)<0) { LM_ERR("failed to parse r-uri\n"); goto error; } pto_uri=&msg->parsed_uri; if(parse_from_header(msg)<0) { LM_ERR("failed to parse From header\n"); goto error; } pfrom = (struct to_body*)msg->from->parsed; if(parse_uri(pfrom->uri.s, pfrom->uri.len, &from_uri)<0){ LM_ERR("failed to parse From URI\n"); goto error; } pfrom_uri=&from_uri; if(body.s[0]== imc_cmd_start_char) { LM_DBG("found command\n"); if(imc_parse_cmd(body.s, body.len, &cmd)<0) { LM_ERR("failed to parse imc cmd!\n"); goto error; } switch(cmd.type) { case IMC_CMDID_CREATE: if(imc_handle_create(msg, &cmd, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'create'\n"); goto error; } break; case IMC_CMDID_JOIN: if(imc_handle_join(msg, &cmd, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'join'\n"); goto error; } break; case IMC_CMDID_INVITE: if(imc_handle_invite(msg, &cmd, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'invite'\n"); goto error; } break; case IMC_CMDID_ACCEPT: if(imc_handle_accept(msg, &cmd, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'accept'\n"); goto error; } break; case IMC_CMDID_DENY: if(imc_handle_deny(msg, &cmd, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'deny'\n"); goto error; } break; case IMC_CMDID_REMOVE: if(imc_handle_remove(msg, &cmd, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'remove'\n"); goto error; } break; case IMC_CMDID_EXIT: if(imc_handle_exit(msg, &cmd, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'exit'\n"); goto error; } break; case IMC_CMDID_LIST: if(imc_handle_list(msg, &cmd, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'list'\n"); goto error; } break; case IMC_CMDID_DESTROY: if(imc_handle_destroy(msg, &cmd, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'destroy'\n"); goto error; } break; case IMC_CMDID_HELP: if(imc_handle_help(msg, &cmd, &pfrom->uri, (msg->new_uri.s)?&msg->new_uri:&msg->first_line.u.request.uri)<0) { LM_ERR("failed to handle 'help'\n"); goto error; } break; default: if(imc_handle_unknown(msg, &cmd, &pfrom->uri, (msg->new_uri.s)?&msg->new_uri:&msg->first_line.u.request.uri)<0) { LM_ERR("failed to handle 'unknown'\n"); goto error; } } goto done; } if(imc_handle_message(msg, &body, pfrom_uri, pto_uri)<0) { LM_ERR("failed to handle 'message'\n"); goto error; } done: return 1; error: return -1; } /** * destroy module */ void destroy(void) { imc_room_p irp = NULL; imc_member_p member = NULL; int i; db_key_t mq_cols[4]; db_val_t mq_vals[4]; db_key_t rq_cols[4]; db_val_t rq_vals[4]; LM_DBG("destroy module ...\n"); if(imc_db==NULL) goto done; mq_cols[0] = &imc_col_username; mq_vals[0].type = DB_STR; mq_vals[0].nul = 0; mq_cols[1] = &imc_col_domain; mq_vals[1].type = DB_STR; mq_vals[1].nul = 0; mq_cols[2] = &imc_col_flag; mq_vals[2].type = DB_INT; mq_vals[2].nul = 0; mq_cols[3] = &imc_col_room; mq_vals[3].type = DB_STR; mq_vals[3].nul = 0; rq_cols[0] = &imc_col_name; rq_vals[0].type = DB_STR; rq_vals[0].nul = 0; rq_cols[1] = &imc_col_domain; rq_vals[1].type = DB_STR; rq_vals[1].nul = 0; rq_cols[2] = &imc_col_flag; rq_vals[2].type = DB_INT; rq_vals[2].nul = 0; for(i=0; iname; rq_vals[1].val.str_val = irp->domain; rq_vals[2].val.int_val = irp->flags; if(imc_dbf.use_table(imc_db, &rooms_table)< 0) { LM_ERR("use_table failed\n"); return; } if(imc_dbf.insert(imc_db, rq_cols, rq_vals, 3)<0) { LM_ERR("failed to insert into table imc_rooms\n"); return; } LM_DBG("room %d %.*s\n", i, irp->name.len, irp->name.s); member = irp->members; while(member) { mq_vals[0].val.str_val = member->user; mq_vals[1].val.str_val = member->domain; mq_vals[2].val.int_val = member->flags; mq_vals[3].val.str_val = irp->uri; if(imc_dbf.use_table(imc_db, &members_table)< 0) { LM_ERR("use_table failed\n"); return; } if(imc_dbf.insert(imc_db, mq_cols, mq_vals, 4)<0) { LM_ERR("failed to insert into table imc_rooms\n"); return; } member = member->next; } irp = irp->next; } } done: imc_htable_destroy(); } /************************* MI ***********************/ static struct mi_root* imc_mi_list_rooms(struct mi_root* cmd_tree, void* param) { int i, len; struct mi_root* rpl_tree= NULL; struct mi_node* rpl= NULL; struct mi_node* node= NULL; struct mi_attr* attr= NULL; imc_room_p irp = NULL; char* p = NULL; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if(rpl_tree == NULL) return 0; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; for(i=0; iuri.s, irp->uri.len); if(attr == NULL) goto error; p = int2str(irp->nr_of_members, &len); attr= add_mi_attr(node, 0, "MEMBERS", 7,p, len ); if(attr == NULL) goto error; attr= add_mi_attr(node, MI_DUP_VALUE, "OWNER", 5, irp->members->uri.s, irp->members->uri.len); if(attr == NULL) goto error; irp = irp->next; } lock_release(&_imc_htable[i].lock); } return rpl_tree; error: lock_release(&_imc_htable[i].lock); free_mi_tree(rpl_tree); return 0; } static struct mi_root* imc_mi_list_members(struct mi_root* cmd_tree, void* param) { int i, len; struct mi_root* rpl_tree = NULL; struct mi_node* node= NULL; struct mi_node* node_r= NULL; struct mi_attr* attr= NULL; char rnbuf[256]; str room_name; imc_room_p room; struct sip_uri inv_uri, *pinv_uri; imc_member_p imp=NULL; char* p = NULL; node= cmd_tree->node.kids; if(node == NULL|| node->next!=NULL) return 0; /* room name */ room_name.s = rnbuf; room_name.len= node->value.len; memcpy(room_name.s, node->value.s, node->value.len); if(room_name.s == NULL || room_name.len == 0) { LM_ERR(" no room name!\n"); return init_mi_tree( 404, "room name not found", 19); } rnbuf[room_name.len] = '\0'; if(*room_name.s=='\0' || *room_name.s=='.') { LM_INFO("empty room name\n"); return init_mi_tree( 400, "empty param", 11); } /* find room */ parse_uri(room_name.s,room_name.len, &inv_uri); pinv_uri=&inv_uri; room=imc_get_room(&pinv_uri->user, &pinv_uri->host); if(room==NULL) { LM_ERR("no such room!\n"); return init_mi_tree( 404, "no such room", 14); } rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if(rpl_tree == NULL) return 0; node_r = add_mi_node_child( &rpl_tree->node, MI_IS_ARRAY|MI_DUP_VALUE, "ROOM", 4, room_name.s, room_name.len); if(node_r == NULL) goto error; imp = room->members; i=0; while(imp) { i++; node = add_mi_node_child(node_r, MI_DUP_VALUE, "MEMBER",6, imp->uri.s, imp->uri.len); if(node == NULL) goto error; imp = imp->next; } p = int2str(i, &len); attr= add_mi_attr(node_r, MI_DUP_VALUE, "NR_OF_MEMBERS", 13, p, len); if(attr == 0) goto error; imc_release_room(room); return rpl_tree; error: imc_release_room(room); free_mi_tree(rpl_tree); return 0; } opensips-2.2.2/modules/imc/imc.h000066400000000000000000000020611300170765700165230ustar00rootroot00000000000000/* * imc module - instant messaging conferencing implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _IMC_H_ #define _IMC_H_ #include "../tm/tm_load.h" extern str imc_cmd_start_str; extern char imc_cmd_start_char; extern struct tm_binds tmb; extern str outbound_proxy; #endif opensips-2.2.2/modules/imc/imc_cmd.c000066400000000000000000000721671300170765700173570ustar00rootroot00000000000000/* * imc module - instant messaging conferencing implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../parser/parse_uri.h" #include "imc.h" #include "imc_cmd.h" #define IMC_BUF_SIZE 1024 static char imc_body_buf[IMC_BUF_SIZE]; static str imc_msg_type = { "MESSAGE", 7 }; static str imc_hdr_ctype = { "Content-Type: text/plain\r\n", 26}; int imc_send_message(str *src, str *dst, str *headers, str *body); int imc_room_broadcast(imc_room_p room, str *ctype, str *body); void imc_inv_callback( struct cell *t, int type, struct tmcb_params *ps); /** * parse cmd */ int imc_parse_cmd(char *buf, int len, imc_cmd_p cmd) { char *p; int i; if(buf==NULL || len<=0 || cmd==NULL) { LM_ERR("invalid parameters\n"); return -1; } memset(cmd, 0, sizeof(imc_cmd_t)); if(buf[0]!=imc_cmd_start_char) { LM_ERR("invalid command [%.*s]\n", len, buf); return -1; } p = &buf[1]; cmd->name.s = p; while(*p && pname.s == p) { LM_ERR("no command in [%.*s]\n", len, buf); return -1; } cmd->name.len = p - cmd->name.s; /* identify the command */ if(cmd->name.len==(sizeof("create")-1) && !strncasecmp(cmd->name.s, "create", cmd->name.len)) { cmd->type = IMC_CMDID_CREATE; } else if(cmd->name.len==(sizeof("join")-1) && !strncasecmp(cmd->name.s, "join", cmd->name.len)) { cmd->type = IMC_CMDID_JOIN; } else if(cmd->name.len==(sizeof("invite")-1) && !strncasecmp(cmd->name.s, "invite", cmd->name.len)) { cmd->type = IMC_CMDID_INVITE; } else if(cmd->name.len==(sizeof("accept")-1) && !strncasecmp(cmd->name.s, "accept", cmd->name.len)) { cmd->type = IMC_CMDID_ACCEPT; } else if(cmd->name.len==(sizeof("deny")-1) && !strncasecmp(cmd->name.s, "deny", cmd->name.len)) { cmd->type = IMC_CMDID_DENY; } else if(cmd->name.len==(sizeof("remove")-1) && !strncasecmp(cmd->name.s, "remove", cmd->name.len)) { cmd->type = IMC_CMDID_REMOVE; } else if(cmd->name.len==(sizeof("exit")-1) && !strncasecmp(cmd->name.s, "exit", cmd->name.len)) { cmd->type = IMC_CMDID_EXIT; } else if(cmd->name.len==(sizeof("list")-1) && !strncasecmp(cmd->name.s, "list", cmd->name.len)) { cmd->type = IMC_CMDID_LIST; } else if(cmd->name.len==(sizeof("destroy")-1) && !strncasecmp(cmd->name.s, "destroy", cmd->name.len)) { cmd->type = IMC_CMDID_DESTROY; } else if(cmd->name.len==(sizeof("help")-1) && !strncasecmp(cmd->name.s, "help", cmd->name.len)) { cmd->type = IMC_CMDID_HELP; goto done; } else { cmd->type = IMC_CMDID_UNKNOWN; goto done; } if(*p=='\0' || p>=buf+len) goto done; i=0; do { while(p=buf+len || *p=='\0' || *p=='\r' || *p=='\n') goto done; cmd->param[i].s = p; while(pparam[i].len = p - cmd->param[i].s; i++; if(i>=IMC_CMD_MAX_PARAM) break; } while(1); done: LM_ERR("command: [%.*s]\n", cmd->name.len, cmd->name.s); for(i=0; iparam[i].len<=0) break; LM_DBG("parameter %d=[%.*s]\n", i, cmd->param[i].len, cmd->param[i].s); } return 0; } /** * */ int imc_handle_create(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; int flag_room = 0; int flag_member = 0; str body; room = imc_get_room(&cmd->param[0], &dst->host); if(room== NULL) { LM_DBG("new room [%.*s]\n", cmd->param[0].len, cmd->param[0].s); if(cmd->param[1].len==IMC_ROOM_PRIVATE_LEN && !strncasecmp(cmd->param[1].s, IMC_ROOM_PRIVATE, cmd->param[1].len)) { flag_room |= IMC_ROOM_PRIV; LM_DBG("room with private flag on\n"); } room= imc_add_room(&cmd->param[0], &dst->host, flag_room); if(room == NULL) { LM_ERR("failed to add new room\n"); goto error; } LM_DBG("added room uri= %.*s\n", room->uri.len, room->uri.s); flag_member |= IMC_MEMBER_OWNER; /* adding the owner as the forst member*/ member= imc_add_member(room, &src->user, &src->host, flag_member); if(member == NULL) { LM_ERR("failed to add owner [%.*s]\n", src->user.len, src->user.s); goto error; } LM_DBG("added the owner as the first member " "[%.*s]\n",member->uri.len, member->uri.s); /* send info message */ body.s = "*** room was created"; body.len = sizeof("*** room was created")-1; imc_send_message(&room->uri, &member->uri, &imc_hdr_ctype, &body); goto done; } /* room already exists */ LM_DBG("room [%.*s] already created\n", cmd->param[0].len, cmd->param[0].s); if(!(room->flags & IMC_ROOM_PRIV)) { LM_DBG("checking if the user [%.*s] is a member\n", src->user.len, src->user.s); member= imc_get_member(room, &src->user, &src->host); if(member== NULL) { member= imc_add_member(room, &src->user, &src->host, flag_member); if(member == NULL) { LM_ERR("failed to add member [%.*s]\n", src->user.len, src->user.s); goto error; } LM_DBG("added as member [%.*s]\n",member->uri.len, member->uri.s); /* send info message */ body.s = imc_body_buf; body.len = snprintf(body.s, IMC_BUF_SIZE, "*** <%.*s> has joined the room", member->uri.len, member->uri.s); if(body.len>0) imc_room_broadcast(room, &imc_hdr_ctype, &body); } } done: if(room!=NULL) imc_release_room(room); return 0; error: if(room!=NULL) imc_release_room(room); return -1; } /** * */ int imc_handle_join(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; int flag_room = 0; int flag_member = 0; str room_name; str body; room_name = cmd->param[0].s?cmd->param[0]:dst->user; room=imc_get_room(&room_name, &dst->host); if(room== NULL || (room->flags&IMC_ROOM_DELETED)) { LM_DBG("could not find room [%.*s]- adding\n", room_name.len, room_name.s); room= imc_add_room(&room_name, &dst->host, flag_room); if(room == NULL) { LM_ERR("failed to add new room [%.*s]\n", room_name.len, room_name.s); goto error; } LM_DBG("created a new room [%.*s]\n", room->name.len, room->name.s); flag_member |= IMC_MEMBER_OWNER; member= imc_add_member(room, &src->user, &src->host, flag_member); if(member == NULL) { LM_ERR("failed to add new member [%.*s]\n", src->user.len, src->user.s); goto error; } /* send info message */ body.s = "*** room was created"; body.len = sizeof("*** room was created")-1; imc_send_message(&room->uri, &member->uri, &imc_hdr_ctype, &body); goto done; } /* room exists */ LM_DBG("found room [%.*s]\n", room_name.len, room_name.s); member= imc_get_member(room, &src->user, &src->host); if(!(room->flags & IMC_ROOM_PRIV)) { LM_DBG("room [%.*s] is public\n", room_name.len, room_name.s); if(member== NULL) { LM_DBG("adding new member [%.*s]\n", src->user.len, src->user.s); member= imc_add_member(room, &src->user, &src->host, flag_member); if(member == NULL) { LM_ERR("adding new user [%.*s]\n", src->user.len, src->user.s); goto error; } goto build_inform; } else { LM_DBG("member [%.*s] is in room already\n", member->uri.len,member->uri.s ); } } else { if(member==NULL) { LM_ERR("attept to join private room [%.*s] from user [%.*s]\n", room_name.len, room_name.s, src->user.len, src->user.s); goto build_inform; } if(member->flags & IMC_MEMBER_INVITED) member->flags &= ~IMC_MEMBER_INVITED; } build_inform: /* send info message */ body.s = imc_body_buf; body.len = snprintf(body.s, IMC_BUF_SIZE, "*** <%.*s> has joined the room", member->uri.len, member->uri.s); if(body.len>0) imc_room_broadcast(room, &imc_hdr_ctype, &body); done: if(room!=NULL) imc_release_room(room); return 0; error: if(room!=NULL) imc_release_room(room); return -1; } /** * */ int imc_handle_invite(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; int flag_member = 0; int size = 0; int i = 0; int add_domain = 0; int add_sip = 0; str uri = {0, 0}; str body; str room_name; struct sip_uri inv_uri; del_member_t *cback_param = NULL; int result; size = cmd->param[0].len+2 ; add_domain = 1; add_sip = 0; while (iparam[0].s[i]== '@') { add_domain = 0; break; } i++; } if(add_domain) size += dst->host.len; if(cmd->param[0].len<4 || strncasecmp(cmd->param[0].s, "sip:", 4)!=0) { size += 4; add_sip = 1; } uri.s = (char*)pkg_malloc(size *sizeof(char)); if(uri.s == NULL) { LM_ERR("no more pkg memory\n"); goto error; } size= 0; if(add_sip) { strcpy(uri.s, "sip:"); size=4; } memcpy(uri.s+size, cmd->param[0].s, cmd->param[0].len); size += cmd->param[0].len; if(add_domain) { uri.s[size] = '@'; size++; memcpy(uri.s+ size, dst->host.s, dst->host.len); size+= dst->host.len; } uri.len = size; if(parse_uri(uri.s, uri.len, &inv_uri)!=0) { LM_ERR("bad uri [%.*s]!\n", uri.len, uri.s); goto error; } room_name = (cmd->param[1].s)?cmd->param[1]:dst->user; room = imc_get_room(&room_name, &dst->host); if(room== NULL || (room->flags&IMC_ROOM_DELETED)) { LM_ERR("the room does not exist [%.*s]!\n", room_name.len, room_name.s); goto error; } member= imc_get_member(room, &src->user, &src->host); if(member==NULL) { LM_ERR("user [%.*s] is not member of[%.*s]!\n", src->user.len, src->user.s, room_name.len, room_name.s); goto error; } if(!(member->flags & IMC_MEMBER_OWNER) && !(member->flags & IMC_MEMBER_ADMIN)) { LM_ERR("user [%.*s] has no right to invite" " other users!\n", src->user.len, src->user.s); goto error; } member= imc_get_member(room, &inv_uri.user, &inv_uri.host); if(member!=NULL) { LM_ERR("user [%.*s] is already member" " of the room!\n", inv_uri.user.len, inv_uri.user.s); goto error; } flag_member |= IMC_MEMBER_INVITED; member=imc_add_member(room, &inv_uri.user, &inv_uri.host, flag_member); if(member == NULL) { LM_ERR("adding member [%.*s]\n", inv_uri.user.len, inv_uri.user.s); goto error; } body.len = 13 + member->uri.len - 4/* sip: */ + 28; if(body.len>=IMC_BUF_SIZE || member->uri.len>=IMC_BUF_SIZE || room->uri.len>=IMC_BUF_SIZE) { LM_ERR("buffer size overflow\n"); goto error; } body.s = imc_body_buf; memcpy(body.s, "INVITE from: ", 13); memcpy(body.s+13, member->uri.s + 4, member->uri.len - 4); memcpy(body.s+ 9 + member->uri.len, "(Type: '#accept' or '#deny')", 28); body.s[body.len] = '\0'; LM_DBG("to=[%.*s]\nfrom=[%.*s]\nbody=[%.*s]\n", member->uri.len,member->uri.s,room->uri.len, room->uri.s, body.len, body.s); cback_param = (del_member_t*)shm_malloc(sizeof(del_member_t)); if(cback_param==NULL) { LM_ERR("no more shm\n"); goto error; } memset(cback_param, 0, sizeof(del_member_t)); cback_param->room_name = room->name; cback_param->room_domain = room->domain; cback_param->member_name = member->user; cback_param->member_domain = member->domain; cback_param->inv_uri = member->uri; /*?!?! possible race with 'remove user' */ result= tmb.t_request(&imc_msg_type, /* Request Method */ &member->uri, /* Request-URI */ &member->uri, /* To */ &room->uri, /* From */ &imc_hdr_ctype, /* Extra headers */ &body, /* Message body */ (outbound_proxy.s)?&outbound_proxy:NULL,/* outbound proxy*/ imc_inv_callback, /* callback function*/ (void*)(cback_param), /* callback param*/ NULL ); if(result< 0) { LM_ERR("in tm send request\n"); shm_free(cback_param); goto error; } if(uri.s!=NULL) pkg_free(uri.s); imc_release_room(room); return 0; error: if(uri.s!=0) pkg_free(uri.s); if(room!=NULL) imc_release_room(room); return -1; } /** * */ int imc_handle_accept(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; str room_name; str body; /* accepting the invitation */ room_name = cmd->param[0].s?cmd->param[0]:dst->user; room=imc_get_room(&room_name, &dst->host); if(room== NULL || (room->flags&IMC_ROOM_DELETED)) { LM_ERR("room [%.*s] is not created!\n", room_name.len, room_name.s); goto error; } /* if aready invited add as a member */ member=imc_get_member(room, &src->user, &src->host); if(member==NULL || !(member->flags & IMC_MEMBER_INVITED)) { LM_ERR("user [%.*s] not invited in the room!\n", src->user.len, src->user.s); goto error; } member->flags &= ~IMC_MEMBER_INVITED; /* send info message */ body.s = imc_body_buf; body.len = snprintf(body.s, IMC_BUF_SIZE, "*** <%.*s> has joined the room", member->uri.len, member->uri.s); if(body.len>0) imc_room_broadcast(room, &imc_hdr_ctype, &body); imc_release_room(room); return 0; error: if(room!=NULL) imc_release_room(room); return -1; } /** * */ int imc_handle_remove(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; str room_name; str body; str uri = {0, 0}; int size =0; int i = 0; int add_domain = 0; int add_sip = 0; struct sip_uri inv_uri; size= cmd->param[0].len+2; add_domain = 1; while (iparam[0].s[i]== '@') { add_domain =0; break; } i++; } if(add_domain) size += dst->host.len; if(cmd->param[0].len<=4 || strncasecmp(cmd->param[0].s, "sip:", 4)!=0) { size+= 4; add_sip = 1; } uri.s = (char*)pkg_malloc(size*sizeof(char)); if(uri.s == NULL) { LM_ERR("no more pkg memory\n"); goto error; } size= 0; if(add_sip) { strcpy(uri.s, "sip:"); size = 4; } memcpy(uri.s+size, cmd->param[0].s, cmd->param[0].len); size+= cmd->param[0].len; if(add_domain) { uri.s[size] = '@'; size++; memcpy(uri.s+size, dst->host.s, dst->host.len); size+= dst->host.len; } uri.len = size; if(parse_uri(uri.s, uri.len, &inv_uri)<0) { LM_ERR("invalid uri [%.*s]\n", uri.len, uri.s); goto error; } room_name = cmd->param[1].s?cmd->param[1]:dst->user; room= imc_get_room(&room_name, &dst->host); if(room==NULL || (room->flags&IMC_ROOM_DELETED)) { LM_ERR("room [%.*s]does not exist!\n", room_name.len, room_name.s); goto error; } /* verify if the user who sent the request is a member in the room * and has the right to remove other users */ member= imc_get_member(room, &src->user, &src->host); if(member== NULL) { LM_ERR("user [%.*s] is not member of room [%.*s]!\n", src->user.len, src->user.s, room_name.len, room_name.s); goto error; } if(!(member->flags & IMC_MEMBER_OWNER) && !(member->flags & IMC_MEMBER_ADMIN)) { LM_ERR("user [%.*s] has no right to remove other users [%.*s]!\n", src->user.len, src->user.s, uri.len, uri.s); goto error; } /* verify if the user that is to be removed is a member of the room */ member= imc_get_member(room, &inv_uri.user, &inv_uri.host); if(member== NULL) { LM_ERR("user [%.*s] is not member of room [%.*s]!\n", inv_uri.user.len, inv_uri.user.s, room_name.len, room_name.s); goto error; } if(member->flags & IMC_MEMBER_OWNER) { LM_ERR("user [%.*s] is owner of room [%.*s]" " -- cannot be removed!\n", inv_uri.user.len, inv_uri.user.s, room_name.len, room_name.s); goto error; } /* send message to the removed person */ body.s = "You have been removed from this room"; body.len = strlen(body.s); LM_DBG("to: [%.*s]\nfrom: [%.*s]\nbody: [%.*s]\n", member->uri.len, member->uri.s , room->uri.len, room->uri.s, body.len, body.s); imc_send_message(&room->uri, &member->uri, &imc_hdr_ctype, &body); member->flags |= IMC_MEMBER_DELETED; imc_del_member(room, &inv_uri.user, &inv_uri.host); body.s = imc_body_buf; body.len = snprintf(body.s, IMC_BUF_SIZE, "*** <%.*s> has joined the room", member->uri.len, member->uri.s); if(body.len>0) imc_room_broadcast(room, &imc_hdr_ctype, &body); if(uri.s!=0) pkg_free(uri.s); imc_release_room(room); return 0; error: if(uri.s!=0) pkg_free(uri.s); if(room!=NULL) imc_release_room(room); return -1; } /** * */ int imc_handle_deny(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; str room_name; // str body; /* denying an invitation */ room_name = cmd->param[0].s?cmd->param[0]:dst->user; room= imc_get_room(&room_name, &dst->host); if(room== NULL || (room->flags&IMC_ROOM_DELETED)) { LM_ERR("room [%.*s] does not exist!\n", room_name.len, room_name.s); goto error; } /* If the user is an invited member, delete it froim the list */ member= imc_get_member(room, &src->user, &src->host); if(member==NULL || !(member->flags & IMC_MEMBER_INVITED)) { LM_ERR("user [%.*s] was not invited in room [%.*s]!\n", src->user.len, src->user.s, room_name.len, room_name.s); goto error; } #if 0 /* send info message */ body.s = imc_body_buf; body.len = snprintf(body.s, IMC_BUF_SIZE, "The user [%.*s] has denied the invitation", src->user.len, src->user.s); if(body.len>0) imc_send_message(&room->uri, &memeber->uri, &imc_hdr_ctype, &body); #endif LM_ERR("user [%.*s] declined invitation in room [%.*s]!\n", src->user.len, src->user.s, room_name.len, room_name.s); imc_del_member(room, &src->user, &src->host); imc_release_room(room); return 0; error: if(room!=NULL) imc_release_room(room); return -1; } /** * */ int imc_handle_list(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; imc_member_p imp = 0; str room_name; str body; char *p; /* the user wants to leave the room */ room_name = cmd->param[0].s?cmd->param[0]:dst->user; room= imc_get_room(&room_name, &dst->host); if(room== NULL || (room->flags&IMC_ROOM_DELETED)) { LM_ERR("room [%.*s] does not exist!\n", room_name.len, room_name.s); goto error; } /* verify if the user is a member of the room */ member = imc_get_member(room, &src->user, &src->host); if(member == NULL) { LM_ERR("user [%.*s] is not member of room [%.*s]!\n", src->user.len, src->user.s, room_name.len, room_name.s); goto error; } p = imc_body_buf; strncpy(p, "Members:\n", 9); p+=9; imp = room->members; while(imp) { if((imp->flags&IMC_MEMBER_INVITED)||(imp->flags&IMC_MEMBER_DELETED) || (imp->flags&IMC_MEMBER_SKIP)) { imp = imp->next; continue; } if(imp->flags & IMC_MEMBER_OWNER) *p++ = '*'; else if(imp->flags & IMC_MEMBER_ADMIN) *p++ = '~'; strncpy(p, imp->uri.s, imp->uri.len); p += imp->uri.len; *p++ = '\n'; imp = imp->next; } imc_release_room(room); /* write over last '\n' */ *(--p) = 0; body.s = imc_body_buf; body.len = p-body.s; LM_DBG("members = [%.*s]\n", body.len, body.s); imc_send_message(&room->uri, &member->uri, &imc_hdr_ctype, &body); return 0; error: if(room!=NULL) imc_release_room(room); return -1; } /** * */ int imc_handle_exit(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; str room_name; str body; /* the user wants to leave the room */ room_name = cmd->param[0].s?cmd->param[0]:dst->user; room= imc_get_room(&room_name, &dst->host); if(room== NULL || (room->flags&IMC_ROOM_DELETED)) { LM_ERR("room [%.*s] does not exist!\n", room_name.len, room_name.s); goto error; } /* verify if the user is a member of the room */ member= imc_get_member(room, &src->user, &src->host); if(member== NULL) { LM_ERR("user [%.*s] is not member of room [%.*s]!\n", src->user.len, src->user.s, room_name.len, room_name.s); goto error; } if(member->flags & IMC_MEMBER_OWNER) { /*If the user is the owner of the room, the room is distroyed */ room->flags |=IMC_ROOM_DELETED; body.s = imc_body_buf; strcpy(body.s, "The room has been destroyed"); body.len = strlen(body.s); imc_room_broadcast(room, &imc_hdr_ctype, &body); imc_release_room(room); imc_del_room(&room_name, &dst->host); room = NULL; goto done; } else { /* delete user */ member->flags |= IMC_MEMBER_DELETED; imc_del_member(room, &src->user, &src->host); body.s = imc_body_buf; body.len = snprintf(body.s, IMC_BUF_SIZE, "The user [%.*s] has left the room", src->user.len, src->user.s); if(body.len>0) imc_room_broadcast(room, &imc_hdr_ctype, &body); } done: if(room!=NULL) imc_release_room(room); return 0; error: if(room!=NULL) imc_release_room(room); return -1; } /** * */ int imc_handle_destroy(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; str room_name; str body; /* distrugere camera */ room_name = cmd->param[0].s?cmd->param[0]:dst->user; room= imc_get_room(&room_name, &dst->host); if(room== NULL || (room->flags&IMC_ROOM_DELETED)) { LM_ERR("room [%.*s] does not exist!\n", room_name.len, room_name.s); goto error; } /* verify is the user is a member of the room*/ member= imc_get_member(room, &src->user, &src->host); if(member== NULL) { LM_ERR("user [%.*s] is not a member of room [%.*s]!\n", src->user.len, src->user.s, room_name.len, room_name.s); goto error; } if(!(member->flags & IMC_MEMBER_OWNER)) { LM_ERR("user [%.*s] is not owner of room [%.*s] -- cannot destroy it" "!\n", src->user.len, src->user.s, room_name.len, room_name.s); goto error; } room->flags |= IMC_ROOM_DELETED; body.s = imc_body_buf; strcpy(body.s, "The room has been destroyed"); body.len = strlen(body.s); /* braodcast message */ imc_room_broadcast(room, &imc_hdr_ctype, &body); imc_release_room(room); LM_DBG("deleting room\n"); imc_del_room(&room_name, &dst->host); return 0; error: if(room!=NULL) imc_release_room(room); return -1; } /** * */ int imc_handle_help(struct sip_msg* msg, imc_cmd_t *cmd, str *src, str *dst) { str body; body.s = IMC_HELP_MSG; body.len = IMC_HELP_MSG_LEN; LM_DBG("to: [%.*s] from: [%.*s]\n", src->len, src->s, dst->len, dst->s); tmb.t_request(&imc_msg_type, /* Request method */ NULL, /* Request-URI */ src, /* To */ dst, /* From */ &imc_hdr_ctype, /* Headers */ &body, /* Body */ (outbound_proxy.s)?&outbound_proxy:NULL,/* outbound proxy */ NULL, /* callback function */ NULL, /* callback parameter*/ NULL ); return 0; } /** * */ int imc_handle_unknown(struct sip_msg* msg, imc_cmd_t *cmd, str *src, str *dst) { str body; body.s = imc_body_buf; body.len = snprintf(body.s, IMC_BUF_SIZE, "invalid command '%.*s' - send ''%.*shelp' for details", cmd->name.len, cmd->name.s, imc_cmd_start_str.len, imc_cmd_start_str.s); if(body.len<=0) { LM_ERR("unable to print message\n"); return -1; } LM_DBG("to: [%.*s] from: [%.*s]\n", src->len, src->s, dst->len, dst->s); tmb.t_request(&imc_msg_type, /* Request method */ NULL, /* Request-URI */ src, /* To */ dst, /* From */ &imc_hdr_ctype, /* Headers */ &body, /* Body */ (outbound_proxy.s)?&outbound_proxy:NULL,/* outbound proxy */ NULL, /* callback function */ NULL, /* callback parameter*/ NULL ); return 0; } /** * */ int imc_handle_message(struct sip_msg* msg, str *msgbody, struct sip_uri *src, struct sip_uri *dst) { imc_room_p room = 0; imc_member_p member = 0; str body; room = imc_get_room(&dst->user, &dst->host); if(room==NULL || (room->flags&IMC_ROOM_DELETED)) { LM_ERR("room [%.*s] does not exist!\n", dst->user.len, dst->user.s); goto error; } member= imc_get_member(room, &src->user, &src->host); if(member== NULL || (member->flags & IMC_MEMBER_INVITED)) { LM_ERR("user [%.*s] has no rights to send messages in room [%.*s]!\n", src->user.len, src->user.s, dst->user.len, dst->user.s); goto error; } LM_DBG("broadcast to room [%.*s]\n", room->uri.len, room->uri.s); body.s = imc_body_buf; body.len = msgbody->len + member->uri.len /* -4 (sip:) +4 (<>: ) */; if(body.len>=IMC_BUF_SIZE) { LM_ERR("buffer overflow [%.*s]\n", msgbody->len, msgbody->s); goto error; } body.s[0] = '<'; memcpy(body.s + 1, member->uri.s + 4, member->uri.len - 4); memcpy(body.s + 1 + member->uri.len - 4, ">: ", 3); memcpy(body.s + 1 + member->uri.len - 4 +3, msgbody->s, msgbody->len); body.s[body.len] = '\0'; member->flags |= IMC_MEMBER_SKIP; imc_room_broadcast(room, &imc_hdr_ctype, &body); member->flags &= ~IMC_MEMBER_SKIP; imc_release_room(room); return 0; error: if(room!=NULL) imc_release_room(room); return -1; } /* * */ int imc_room_broadcast(imc_room_p room, str *ctype, str *body) { imc_member_p imp; if(room==NULL || body==NULL) return -1; imp = room->members; LM_DBG("nr = %d\n", room->nr_of_members ); while(imp) { LM_DBG("to uri = %.*s\n", imp->uri.len, imp->uri.s); if((imp->flags&IMC_MEMBER_INVITED)||(imp->flags&IMC_MEMBER_DELETED) || (imp->flags&IMC_MEMBER_SKIP)) { imp = imp->next; continue; } /* to-do: callbac to remove user fi delivery fails */ imc_send_message(&room->uri, &imp->uri, ctype, body); imp = imp->next; } return 0; } /* * */ int imc_send_message(str *src, str *dst, str *headers, str *body) { if(src==NULL || dst==NULL || body==NULL) return -1; /* to-do: callbac to remove user fi delivery fails */ tmb.t_request(&imc_msg_type, /* Request method */ NULL, /* Request-URI */ dst, /* To */ src, /* From */ headers, /* Headers */ body, /* Body */ (outbound_proxy.s)?&outbound_proxy:NULL, /* outbound proxy */ NULL, /* callback function */ NULL, /* callback parameter */ NULL ); return 0; } /* * */ void imc_inv_callback( struct cell *t, int type, struct tmcb_params *ps) { str body_final; char from_uri_buf[256]; char to_uri_buf[256]; char body_buf[256]; str from_uri_s, to_uri_s; imc_member_p member= NULL; imc_room_p room = NULL; if(ps->param==NULL || *ps->param==NULL || (del_member_t*)(*ps->param) == NULL) { LM_DBG("member not received\n"); return; } LM_DBG("completed with status %d [member name domain:" "%p/%.*s/%.*s]\n",ps->code, ps->param, ((del_member_t *)(*ps->param))->member_name.len, ((del_member_t *)(*ps->param))->member_name.s, ((del_member_t *)(*ps->param))->member_domain.len, ((del_member_t *)(*ps->param))->member_domain.s); if(ps->code < 300) return; else { room= imc_get_room(&((del_member_t *)(*ps->param))->room_name, &((del_member_t *)(*ps->param))->room_domain ); if(room==NULL) { LM_ERR("the room does not exist!\n"); goto error; } /*verify if the user who sent the request is a member in the room * and has the right to remove other users */ member= imc_get_member(room, &((del_member_t *)(*ps->param))->member_name, &((del_member_t *)(*ps->param))->member_domain); if(member== NULL) { LM_ERR("the user is not a member of the room!\n"); goto error; } imc_del_member(room, &((del_member_t *)(*ps->param))->member_name, &((del_member_t *)(*ps->param))->member_domain); goto build_inform; } build_inform: body_final.s = body_buf; body_final.len = member->uri.len - 4 /* sip: part of URI */ + 20; memcpy(body_final.s, member->uri.s + 4, member->uri.len - 4); memcpy(body_final.s+member->uri.len-4," is not registered. ",21); goto send_message; send_message: from_uri_s.s = from_uri_buf; from_uri_s.len = room->uri.len; strncpy(from_uri_s.s, room->uri.s, room->uri.len); LM_DBG("sending message\n"); to_uri_s.s = to_uri_buf; to_uri_s.len = ((del_member_t *)(*ps->param))->inv_uri.len; strncpy(to_uri_s.s,((del_member_t *)(*ps->param))->inv_uri.s , ((del_member_t *)(*ps->param))->inv_uri.len); LM_DBG("to: %.*s\nfrom: %.*s\nbody: %.*s\n", to_uri_s.len, to_uri_s.s, from_uri_s.len, from_uri_s.s, body_final.len, body_final.s); tmb.t_request(&imc_msg_type, /* Request method*/ NULL, /* Request-URI */ &to_uri_s, /* To */ &from_uri_s, /* From */ NULL, /* Headers */ &body_final, /* Body */ (outbound_proxy.s)?&outbound_proxy:NULL,/* outbound proxy*/ NULL, /* callback function */ NULL, /* callback parameter*/ NULL ); if(room!=NULL) { imc_release_room(room); } if((del_member_t *)(*ps->param)) shm_free(*ps->param); return; error: if(room!=NULL) { imc_release_room(room); } if((del_member_t *)(*ps->param)) shm_free(*ps->param); return; } opensips-2.2.2/modules/imc/imc_cmd.h000066400000000000000000000077001300170765700173530ustar00rootroot00000000000000/* * imc module - instant messaging conferencing implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- */ #ifndef _IMC_CMD_H_ #define _IMC_CMD_H_ #include "../../parser/parse_uri.h" #include "../../str.h" #include "imc_mng.h" #define IMC_CMD_START '#' #define IMC_CMD_START_STR "#" #define IMC_CMDID_CREATE 1 #define IMC_CMDID_INVITE 2 #define IMC_CMDID_JOIN 3 #define IMC_CMDID_EXIT 4 #define IMC_CMDID_ACCEPT 5 #define IMC_CMDID_DENY 6 #define IMC_CMDID_REMOVE 7 #define IMC_CMDID_DESTROY 8 #define IMC_CMDID_HELP 9 #define IMC_CMDID_LIST 10 #define IMC_CMDID_UNKNOWN 11 #define IMC_CMD_CREATE "create" #define IMC_CMD_INVITE "invite" #define IMC_CMD_JOIN "join" #define IMC_CMD_EXIT "exit" #define IMC_CMD_ACCEPT "accept" #define IMC_CMD_DENY "deny" #define IMC_CMD_REMOVE "remove" #define IMC_CMD_DESTROY "destroy" #define IMC_CMD_LIST "list" #define IMC_ROOM_PRIVATE "private" #define IMC_ROOM_PRIVATE_LEN (sizeof(IMC_ROOM_PRIVATE)-1) #define IMC_HELP_MSG "\r\n"IMC_CMD_START_STR IMC_CMD_CREATE" - \ create new connference room\r\n\ "IMC_CMD_START_STR IMC_CMD_JOIN" [] - \ join the conference room\r\n\ "IMC_CMD_START_STR IMC_CMD_INVITE" [] - \ invite a user to join a conference room\r\n\ "IMC_CMD_START_STR IMC_CMD_ACCEPT" - \ accept invitation to join a conference room\r\n\ "IMC_CMD_START_STR IMC_CMD_DENY" - \ deny invitation to join a conference room\r\n\ "IMC_CMD_START_STR IMC_CMD_REMOVE" [] - \ remove an user from the conference room\r\n\ "IMC_CMD_START_STR IMC_CMD_LIST" - \ list members is a conference room\r\n\ "IMC_CMD_START_STR IMC_CMD_EXIT" [] - \ exit from a conference room\r\n\ "IMC_CMD_START_STR IMC_CMD_DESTROY" [] - \ destroy conference room\r\n" #define IMC_HELP_MSG_LEN (sizeof(IMC_HELP_MSG)-1) #define IMC_CMD_MAX_PARAM 5 typedef struct _imc_cmd { str name; int type; str param[IMC_CMD_MAX_PARAM]; } imc_cmd_t, *imc_cmd_p; int imc_parse_cmd(char *buf, int len, imc_cmd_p cmd); int imc_handle_create(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst); int imc_handle_join(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst); int imc_handle_invite(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst); int imc_handle_accept(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst); int imc_handle_deny(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst); int imc_handle_remove(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst); int imc_handle_list(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst); int imc_handle_exit(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst); int imc_handle_destroy(struct sip_msg* msg, imc_cmd_t *cmd, struct sip_uri *src, struct sip_uri *dst); int imc_handle_unknown(struct sip_msg* msg, imc_cmd_t *cmd, str *src, str *dst); int imc_handle_help(struct sip_msg* msg, imc_cmd_t *cmd, str *src, str *dst); int imc_handle_message(struct sip_msg* msg, str *msgbody, struct sip_uri *src, struct sip_uri *dst); #endif opensips-2.2.2/modules/imc/imc_mng.c000066400000000000000000000214541300170765700173660ustar00rootroot00000000000000/* * imc module - instant messaging conferencing implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2006-10-06 first version (anca) */ #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "imc_mng.h" /* imc hash table */ extern imc_hentry_p _imc_htable; extern int imc_hash_size; extern char imc_cmd_start_char; #define imc_get_hentry(_hid, _size) ((_hid)&(_size-1)) /** * hash thable init */ int imc_htable_init(void) { int i; if(imc_hash_size<=0) { LM_ERR("invalid hash table size\n"); return -1; } _imc_htable = (imc_hentry_p)shm_malloc(imc_hash_size*sizeof(imc_hentry_t)); if(_imc_htable == NULL) { LM_ERR("no more shm memory\n"); return -1; } memset(_imc_htable, 0, imc_hash_size*sizeof(imc_hentry_t)); for(i=0; inext; imc_del_room(&irp->name, &irp->domain); irp = irp_temp; } } shm_free(_imc_htable); _imc_htable = NULL; return 0; } /** * add room */ imc_room_p imc_add_room(str* name, str* domain, int flags) { imc_room_p irp = NULL; int size; int hidx; if(name == NULL || name->s==NULL || name->len<=0 || domain == NULL || domain->s==NULL || domain->len<=0) { LM_ERR("invalid parameters\n"); return NULL; } /* struct size + "sip:" + name len + "@" + domain len + '\0' */ size = sizeof(imc_room_t) + (name->len+domain->len+6)*sizeof(char); irp = (imc_room_p)shm_malloc(size); if(irp==NULL) { LM_ERR("no more shm memory left\n"); return NULL; } memset(irp, 0, size); irp->uri.len = 4 /*sip:*/ + name->len + 1 /*@*/ + domain->len; irp->uri.s = (char*)(((char*)irp)+sizeof(imc_room_t)); memcpy(irp->uri.s, "sip:", 4); memcpy(irp->uri.s+4, name->s, name->len); irp->uri.s[4+name->len] = '@'; memcpy(irp->uri.s+5+name->len, domain->s, domain->len); irp->uri.s[irp->uri.len] = '\0'; irp->name.len = name->len; irp->name.s = irp->uri.s+4; irp->domain.len = domain->len; irp->domain.s = irp->uri.s+5+name->len; irp->flags = flags; irp->hashid = core_case_hash(&irp->name, &irp->domain, 0); hidx = imc_get_hentry(irp->hashid, imc_hash_size); lock_get(&_imc_htable[hidx].lock); if(_imc_htable[hidx].rooms!=NULL) { irp->next = _imc_htable[hidx].rooms; _imc_htable[hidx].rooms->prev = irp; _imc_htable[hidx].rooms = irp; } else { _imc_htable[hidx].rooms = irp; } return irp; } /** * release room */ int imc_release_room(imc_room_p room) { unsigned int hidx; if(room==NULL) { LM_ERR("invalid parameters\n"); return -1; } hidx = imc_get_hentry(room->hashid, imc_hash_size); lock_release(&_imc_htable[hidx].lock); return 0; } /** * search room */ imc_room_p imc_get_room(str* name, str* domain) { imc_room_p irp = NULL; unsigned int hashid; int hidx; if(name == NULL || name->s==NULL || name->len<=0 || domain == NULL || domain->s==NULL || domain->len<=0) { LM_ERR("invalid parameters\n"); return NULL; } hashid = core_case_hash(name, domain, 0); hidx = imc_get_hentry(hashid, imc_hash_size); lock_get(&_imc_htable[hidx].lock); irp = _imc_htable[hidx].rooms; while(irp) { if(irp->hashid==hashid && irp->name.len==name->len && irp->domain.len==domain->len && !strncasecmp(irp->name.s, name->s, name->len) && !strncasecmp(irp->domain.s, domain->s, domain->len)) { return irp; } irp = irp->next; } /* no room */ lock_release(&_imc_htable[hidx].lock); return NULL; } /** * delete room */ int imc_del_room(str* name, str* domain) { imc_room_p irp = NULL; imc_member_p imp=NULL, imp_temp=NULL; unsigned int hashid; int hidx; if(name == NULL || name->s==NULL || name->len<=0 || domain == NULL || domain->s==NULL || domain->len<=0) { LM_ERR("invalid parameters\n"); return -1; } hashid = core_case_hash(name, domain, 0); hidx = imc_get_hentry(hashid, imc_hash_size); lock_get(&_imc_htable[hidx].lock); irp = _imc_htable[hidx].rooms; while(irp) { if(irp->hashid==hashid && irp->name.len==name->len && irp->domain.len==domain->len && !strncasecmp(irp->name.s, name->s, name->len) && !strncasecmp(irp->domain.s, domain->s, domain->len)) { if(irp->prev==NULL) _imc_htable[hidx].rooms = irp->next; else irp->prev->next = irp->next; if(irp->next!=NULL) irp->next->prev = irp->prev; /* delete members */ imp = irp->members; while(imp){ imp_temp = imp->next; shm_free(imp); imp = imp_temp; } shm_free(irp); goto done; } irp = irp->next; } done: lock_release(&_imc_htable[hidx].lock); return 0; } /** * add member */ imc_member_p imc_add_member(imc_room_p room, str* user, str* domain, int flags) { imc_member_p imp = NULL; int size; if(room==NULL || user == NULL || user->s==NULL || user->len<=0 || domain == NULL || domain->s==NULL || domain->len<=0) { LM_ERR("invalid parameters\n"); return NULL; } /* struct size + "sip:" + user name len + "@" + domain len + '\0' */ size = sizeof(imc_member_t) + (user->len+domain->len+6)*sizeof(char); imp = (imc_member_p)shm_malloc(size); if(imp== NULL) { LM_ERR("out of shm memory\n"); return NULL; } memset(imp, 0, size); imp->uri.len = 4 /*sip:*/ + user->len + 1 /*@*/ + domain->len; imp->uri.s = (char*)(((char*)imp)+sizeof(imc_member_t)); memcpy(imp->uri.s, "sip:", 4); memcpy(imp->uri.s+4, user->s, user->len); imp->uri.s[4+user->len] = '@'; memcpy(imp->uri.s+5+user->len, domain->s, domain->len); imp->uri.s[imp->uri.len] = '\0'; LM_DBG("[uri]= %.*s\n", imp->uri.len, imp->uri.s); imp->user.len = user->len; imp->user.s = imp->uri.s+4; LM_DBG("[user]= %.*s\n", imp->user.len, imp->user.s); imp->domain.len = domain->len; imp->domain.s = imp->uri.s+5+user->len; imp->flags = flags; imp->hashid = core_case_hash(&imp->user, &imp->domain, 0); room->nr_of_members++; if(room->members==NULL) room->members = imp; else { imp->next = room->members->next; if((room->members)->next!=NULL) ((room->members)->next)->prev = imp; imp->prev = room->members; room->members->next=imp; } return imp; } /** * search memeber */ imc_member_p imc_get_member(imc_room_p room, str* user, str* domain) { imc_member_p imp = NULL; unsigned int hashid; if(room==NULL || user == NULL || user->s==NULL || user->len<=0 || domain == NULL || domain->s==NULL || domain->len<=0) { LM_ERR("invalid parameters\n"); return NULL; } hashid = core_case_hash(user, domain, 0); imp = room->members; while(imp) { if(imp->hashid==hashid && imp->user.len==user->len && imp->domain.len==domain->len && !strncasecmp(imp->user.s, user->s, user->len) && !strncasecmp(imp->domain.s, domain->s, domain->len)) { LM_DBG("found member\n"); return imp; } imp = imp->next; } return NULL; } /** * delete member */ int imc_del_member(imc_room_p room, str* user, str* domain) { imc_member_p imp = NULL; unsigned int hashid; if(room==NULL || user == NULL || user->s==NULL || user->len<=0 || domain == NULL || domain->s==NULL || domain->len<=0) { LM_ERR("invalid parameters\n"); return -1; } hashid = core_case_hash(user, domain, 0); imp = room->members; while(imp) { if(imp->hashid==hashid && imp->user.len==user->len && imp->domain.len==domain->len && !strncasecmp(imp->user.s, user->s, user->len) && !strncasecmp(imp->domain.s, domain->s, domain->len)) { if(imp->prev==NULL) room->members = imp->next; else imp->prev->next = imp->next; if(imp->next!=NULL) imp->next->prev = imp->prev; shm_free(imp); room->nr_of_members--; return 0; } imp = imp->next; } return 0; } opensips-2.2.2/modules/imc/imc_mng.h000066400000000000000000000045421300170765700173720ustar00rootroot00000000000000/* * imc module - instant messaging conferencing implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2006-10-06 first version (anca) */ #ifndef _IMC_MNG_H_ #define _IMC_MNG_H_ #include "../../locking.h" #include "../../str.h" #include "../../parser/parse_from.h" #define IMC_MEMBER_OWNER (1<<0) #define IMC_MEMBER_ADMIN (1<<1) #define IMC_MEMBER_INVITED (1<<2) #define IMC_MEMBER_DELETED (1<<3) #define IMC_MEMBER_SKIP (1<<4) typedef struct _imc_member { unsigned int hashid; str uri; str user; str domain; int flags; struct _imc_member * next; struct _imc_member * prev; } imc_member_t, *imc_member_p; #define IMC_ROOM_PRIV 1<<0 #define IMC_ROOM_DELETED 1<<1 typedef struct del_member { str room_name; str room_domain; str inv_uri; str member_name; str member_domain; }del_member_t; typedef struct _imc_room { unsigned int hashid; str uri; str name; str domain; int flags; int nr_of_members; imc_member_p members; struct _imc_room * next; struct _imc_room * prev; } imc_room_t, *imc_room_p; typedef struct _imc_hentry { imc_room_p rooms; gen_lock_t lock; } imc_hentry_t, *imc_hentry_p; imc_member_p imc_add_member(imc_room_p room, str* user, str* domain, int flags); imc_member_p imc_get_member(imc_room_p room, str* user, str* domain); int imc_del_member(imc_room_p room, str* user, str* domain); imc_room_p imc_add_room(str* name, str* domain, int flags); imc_room_p imc_get_room(str* name, str* domain); int imc_del_room(str* name, str* domain); int imc_release_room(imc_room_p room); int imc_htable_init(); int imc_htable_destroy(); #endif opensips-2.2.2/modules/jabber/000077500000000000000000000000001300170765700162605ustar00rootroot00000000000000opensips-2.2.2/modules/jabber/Makefile000066400000000000000000000005321300170765700177200ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=jabber.so # extra debug messages DEFS+=-DXJ_EXTRA_DEBUG # -DHAVE_IHTTP # expat.h location DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -lexpat include ../../Makefile.modules opensips-2.2.2/modules/jabber/README000066400000000000000000000327651300170765700171550ustar00rootroot00000000000000jabber Module Daniel-Constantin Mierla Edited by Daniel-Constantin Mierla Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. New Features 1.2. Admin's Guide 1.3. Admin Guide 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. db_url (string) 1.5.2. jaddress (string) 1.5.3. jport (integer) 1.5.4. jdomain (string) 1.5.5. aliases (string) 1.5.6. proxy (string) 1.5.7. registrar (string) 1.5.8. workers (integer) 1.5.9. max_jobs (integer) 1.5.10. cache_time (integer) 1.5.11. delay_time (integer) 1.5.12. sleep_time (integer) 1.5.13. check_time (integer) 1.5.14. priority (str) 1.6. Exported Functions 1.6.1. jab_send_message() 1.6.2. jab_join_jconf() 1.6.3. jab_exit_jconf() 1.6.4. jab_go_online() 1.6.5. jab_go_offline() List of Examples 1.1. Set db_url parameter 1.2. Set jaddress parameter 1.3. Set jport parameter 1.4. Set jdomain parameter 1.5. Set jdomain parameter 1.6. Set proxy parameter 1.7. Set registrar parameter 1.8. Set workers parameter 1.9. Set max_jobs parameter 1.10. Set cache_time parameter 1.11. Set delay_time parameter 1.12. Set sleep_time parameter 1.13. Set check_time parameter 1.14. Set priority parameter 1.15. jab_send_message() usage 1.16. jab_join_jconf() usage 1.17. jab_exit_jconf() usage 1.18. jab_go_online() usage 1.19. jab_go_offline() usage Chapter 1. Admin Guide 1.1. Overview This is new version of Jabber module that integrates XODE XML parser for parsing Jabber messages. That introduces a new module dependency: expat library. Expat is a common XML library and is the fastest available for Linux/Unix, the second over all, after msxml library. It is integrated in most of well known Linux distributions. 1.1.1. New Features * Presence support (see doc/xxjab.cfg for a sample cfg file) (January 2003). * SIP to Jabber conference support (December 2003). * Possibility to manage all kinds of Jabber messages (message/presence/iq) (December 2003). * Aliases -- Possibility to set host aliases for addresses (see parameter's desc.) (December 2003). * Send received SIP MESSAGE messages to different IM networks (Jabber, ICQ,MSN, AIM, Yahoo) using a Jabber server (December 2003). * Send incoming Jabber instant messages as SIP MESSAGE messages. * Gateways detection -- Ability to see whether an IM gateway is up or down. 1.2. Admin's Guide Note A more complete guide about SIMPLE2Jabber gateway can be found at http://www.opensips.org/. The part below will be removed soon, only the manual from web will be updated. The Jabber server setup is not a subject of this guide. Check http://www.jabber.org for that. Useful scripts, for creating Jabber Gateway database, or for managing the Jabber accounts form web are located in 'doc' subdirectory of the module. Main steps of using the Jabber gateway: * Create the MySQL database. * Setup the local Jabber server. * Set the module parameter values in cfg file of OpenSIPS, load the dependent modules, set up the routing rules for Jabber gateway. * Run OpenSIPS. The administrator of OpenSIPS/Jabber gateway must inform the users what are the aliases for Jabber/Other IM networks. Other IMs could be AIM, ICQ, MSN, Yahoo, and so on. These aliases depend on the server hostname where runs OpenSIPS and how local Jabber server is setup. Next is presented a use case. Prologue: * OpenSIPS is running on “server.orgâ€. * Local Jabber server is running on “jabsrv.server.orgâ€. * Jabber network alias (first part of “jdomainâ€) is “jabber.server.org†The aliases for other IM networks must be the same as JID set in Jabber configuration file for each IM transport. The JIDs of Jabber transports must start with the name of the network. For AIM, JID must start with “aim.â€, for ICQ with “icq†(that because I use icqv7-t), for MSN with “msn.†and for Yahoo with “yahoo.â€. The gateway needs these to find out what transport is working and which not. For our use case these could be like “aim.server.orgâ€, “icq.server.orgâ€, “msn.server.orgâ€, “yahoo.server.orgâ€. It is indicated to have these aliases in DNS, thus the client application can resolve the DNS name. Otherwise there must be set the outbound proxy to OpenSIPS server. *** Routing rules for Jabber gateway First step is to configure OpenSIPS to recognize messages for Jabber gateway. Look at “doc/xjab.cfg†to see a sample. The idea is to look in messages for destination address and if it contains Jabber alias or other IM alias, that means the message is for Jabber gateway. Next step is to find out what means that message for Jabber gateway. It could be a special message what triggers the gateway to take an action or is a simple message which should be delivered to Jabber network (using the method “jab_send_messageâ€). The special messages are for: * Registering to Jabber server (go online in Jabber network)--here must be called “jab_go_online†method. * Leaving the Jabber network (go offline in Jabber network)--here must be called “jab_go_offline†method. * Joining a Jabber conference room--here must be called “jab_join_jconfâ€. * Leaving a Jabber conference room--here must be called “jab_exit_jconfâ€. The destination address must follow the following patterns: * For Jabber network: “usernamejabber_server@jabber_aliasâ€. * For Jabber conference: “nicknameroomconference_server@jabber_aliasâ€. * For AIM network: “aim_username@aim_aliasâ€. * For ICQ network: “icq_number@icq_aliasâ€. * For MSN network: “msn_usernamemsn_server@msn_aliasâ€. msn_server can be “msn.com†or “hotmail.comâ€. * For YAHOO network: “yahoo_username@yahoo_aliasâ€. Note “jabber_alias†is the first part of “jdomainâ€. 1.3. Admin Guide The user must activate his Jabber account associated with his SIP id. For each other IM network on which he wants to send messages, he must set an account for that IM network. The gateway is not able to create new account in foreign networks, excepting local Jabber server. When you want to send a message to someone in other IM network, you must set the destination of the message according with the pattern corresponding to that IM network (see last part of “Admin guide†chapter). Sending a message to user@jabber.xxx.org which is in Jabber network, the destination must be: userjabber.xxx.org@jabber_alias. For someone who is in Yahoo network the destination must be: user@yahoo_alias Note The OpenSIPS administrator have to set the Jabber transports for each IM network in order to be able to send messages to those networks. The alias of each IM network can be found out from OpenSIPS admin. You cannot send messages from your SIP client to your associated Jabber account--is something like sending messages to yourself. 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * A database module. * pa (Optionally) - Presence Agent. * tm - Transaction Manager. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * Expat library. 1.5. Exported Parameters 1.5.1. db_url (string) SQL URL of database. Default value is “mysql://root@127.0.0.1/sip_jabâ€. Example 1.1. Set db_url parameter ... modparam("jabber", "db_url", "mysql://username:password@host/sip_jab") ... 1.5.2. jaddress (string) IP or hostname of Jabber server -- it must be the same as the value from tag of Jabber server config file. Default value is “127.0.0.1â€. Example 1.2. Set jaddress parameter ... modparam("jabber", "jaddress", "1.2.3.4") ... 1.5.3. jport (integer) Port number of Jabber server. Default value is “5222â€. Example 1.3. Set jport parameter ... modparam("jabber", "jport", 1234) ... 1.5.4. jdomain (string) Format: jabber.sipserver.com=. If the destination is for Jabber network the URI should be like: usernamejabber_server@jdomain or nicknameroomnameconference_server@jdomain must be a un-reserved character. By default this character is * . The destination will be transformed to username@jabber_server or roomname@conference_server/nickname before the message is sent to Jabber server. Default value is none. Example 1.4. Set jdomain parameter ... modparam("jabber", "jdomain", "jabber.sipserver.com=*") ... 1.5.5. aliases (string) Aliases for IM networks. Format: “N;alias1=;...;aliasN=;†Destinations like '*@aliasX' could have other format than those specified for Jabber network. All from user part of the destination address will be changed to if the destination address contains . (Ex: jdomain is 'jabber.x.com=*' and msn_alias is 'msn.x.com=%'. The destination address forM MSN Network, on SIP side, is like 'username*hotmail.com@msn.x.com'. The destination address will be transformed to 'username%hotmail.com@msn.x.com'. 'msn.x.com' must be the same as the JID associated with MSN transport in Jabber configuration file (usually is 'jabberd.xml')) Default value is none. Example 1.5. Set jdomain parameter ... modparam("jabber", "aliases", "1;msn.x.com=%") ... 1.5.6. proxy (string) Outbound proxy address. Format: ip_address:port hostname:port All SIP messages generated by gateway will be sent to that address. If is missing, the message will be delivered to the hostname of the destination address Default value is none. Example 1.6. Set proxy parameter ... modparam("jabber", "proxy", "10.0.0.1:5060 sipserver.com:5060") ... 1.5.7. registrar (string) The address in whose behalf the INFO and ERROR messages are sent. Default value is “jabber_gateway@127.0.0.1â€. Example 1.7. Set registrar parameter ... modparam("jabber", "registrar", "jabber_gateway@127.0.0.1") ... 1.5.8. workers (integer) Number of workers. Default value is 2. Example 1.8. Set workers parameter ... modparam("jabber", "workers", 2) ... 1.5.9. max_jobs (integer) Maximum jobs per worker. Default value is 10. Example 1.9. Set max_jobs parameter ... modparam("jabber", "max_jobs", 10) ... 1.5.10. cache_time (integer) Cache time of a Jabber connection. Default value is 600. Example 1.10. Set cache_time parameter ... modparam("jabber", "cache_time", 600) ... 1.5.11. delay_time (integer) Time to keep a SIP message (in seconds). Default value is 90 seconds. Example 1.11. Set delay_time parameter ... modparam("jabber", "delay_time", 90) ... 1.5.12. sleep_time (integer) Time between expired Jabber connections checking (in seconds). Default value is 20 seconds. Example 1.12. Set sleep_time parameter ... modparam("jabber", "sleep_time", 20) ... 1.5.13. check_time (integer) Time between checking the status of JabberGW workers (in seconds). Default value is 20 seconds. Example 1.13. Set check_time parameter ... modparam("jabber", "check_time", 20) ... 1.5.14. priority (str) Presence priority for Jabber gateway. Default value is “9â€. Example 1.14. Set priority parameter ... modparam("jabber", "priority", "3") ... 1.6. Exported Functions 1.6.1. jab_send_message() Converts SIP MESSAGE message to a Jabber message and sends it to Jabber server. This function can be used from REQUEST_ROUTE. Example 1.15. jab_send_message() usage ... jab_send_message(); ... 1.6.2. jab_join_jconf() Join a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . If the nickname is missing, then the SIP username is used. This function can be used from REQUEST_ROUTE. Example 1.16. jab_join_jconf() usage ... jab_join_jconf(); ... 1.6.3. jab_exit_jconf() Leave a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . This function can be used from REQUEST_ROUTE. Example 1.17. jab_exit_jconf() usage ... jab_exit_jconf(); ... 1.6.4. jab_go_online() Register to the Jabber server with associated Jabber ID of the SIP user. This function can be used from REQUEST_ROUTE. Example 1.18. jab_go_online() usage ... jab_go_online(); ... 1.6.5. jab_go_offline() Log off from Jabber server the associated Jabber ID of the SIP user. This function can be used from REQUEST_ROUTE. Example 1.19. jab_go_offline() usage ... jab_go_offline(); ... opensips-2.2.2/modules/jabber/doc/000077500000000000000000000000001300170765700170255ustar00rootroot00000000000000opensips-2.2.2/modules/jabber/doc/jabber.cfg000066400000000000000000000042611300170765700207360ustar00rootroot00000000000000# # configuration for Jabber module testing # # $ID: daniel $ # # # sample of Jabber module usage # debug=9 # debug level (cmd line: -dddddddddd) #fork=yes # (cmd. line: -D) fork=no log_stderror=yes # (cmd line: -E) #log_stderror=no # (cmd line: -E) children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 listen=10.0.0.1 # for more info: sip_router -h #modules loadmodule "modules/print/print.so" loadmodule "modules/textops/textops.so" loadmodule "modules/tm/tm.so" loadmodule "modules/im/im.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/sl/sl.so" loadmodule "modules/mysql/mysql.so" loadmodule "modules/jabber/jabber.so" modparam("jabber","db_url","mysql://user:***@127.0.0.1/sip_jab") modparam("jabber","jaddress","127.0.0.1") modparam("jabber","jport",5222) modparam("jabber","workers",2) modparam("jabber","max_jobs",10) modparam("jabber","aliases","3;icq.domain.com;msn.domain.com;yahoo.domain.com;") modparam("jabber","jdomain","jabber.domain.com") route{ if ((search("To:.*@icq\.domain\.com")) || (search("To:.*@jabber\.domain\.de")) || (search("To:.*@msn\.domain\.com")) || (search("To:.*@yahoo\.domain\.com"))) { if (method=="MESSAGE") { log("MESSAGE received -> manage it with XJAB\n"); if(search("\n:online")) { if (jab_go_online()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:offline")) { if (jab_go_offline()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:join")) { if (jab_join_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if(search("\n:exit")) { if (jab_exit_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if (jab_send_message()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("503","Service Unavailable"); }; break; }; }; forward(uri:host,uri:port); } opensips-2.2.2/modules/jabber/doc/jabber.gws000066400000000000000000000107071300170765700210010ustar00rootroot00000000000000IM Gateway - capabilities and limitations - v1.0 ================================================ The gateway allows the message passing between SIP users and other IM users (ICQ, AIM, Yahoo ...). IM Gateway uses for this a Jabber server which has the facility of supporting many IM protocols (they are called IM transports). So, in fact, the gateway is between SIP and Jabber. Because the most of the IM protocols are not open source and they are the property of some companies, Jabber does not provide full support for these protocols. From time to time, the owner of the protocol changes the protocol structure, and for a while, that transport is no longer available. There are some guys doing some kind of reverse engineering on these protocols. But not whole protocol structure is discovered, each transport has its own limitations. In additions, many of the IM native clients allow only one user to be online at a moment, and IM servers could easy say that the hosts which have more than one open connection are not using a native client and block those addresses or the accounts. Next, are presented the IM transports that are running on our Jabber server, or they are wanted to be available. The capabilities and limitations are what can be found on the net about transports. AIM transport ------------- It is only one well known version of aim-transport. (web location: http://aim-transport.jabberstudio.org/) AIM-t provides basic interoperability between Jabber and AIM/ICQ network. Currently AIM-t is undergoing massive changes. It will be a standalone component once this is finished. * capabilities - currently supporting basic presence and messaging. - reliable messaging (via server only) - simple user info retrieval (ICQ only) may take some time, just wait... - SMS sending (ICQ) send "SEND-SMS:number:text" to any ICQ user, for example "SEND-SMS:+4917012345:Test" - away message retrieval (AIM only) - offline messages (ICQ only) * limitations - NO file transfer - NO multichat - NO user search - NO incoming SMS - NO import of server buddy lists - NO handling of incoming ICQ auth requests - ICQ messages may get dropped by AOL servers if your contact uses an old ICQv5 client. ICQ transport ------------- Excepting the AIM transport, there exists another distribution for ICQ transport. (web location: http://icqv7-t.sourceforge.net/) * capabilities - messaging (through server only) - presence notification - SMS support * limitations - no file transfer The AIM transport for ICQ. - see AIM transport A new versions of ICQ transport seems to be available now. But now place from where it can be downloaded - I sent a mail asking for a distribution. A short description can be found at http://mailman.jabber.org/pipermail/jdev/2002-July/012343.html IRC transport ------------- (web location: http://download.jabber.org/contrib/irc-transport.tar.gz) This version of IRC transport is a beta version * capabilities - It will connect to irc servers and allow you to talk to them, but it is still in heavy development * limitations - private messaging does not yet function - transport registration/notify will not be supported - groupchat 1.0 and 1.1 will not be supported in favor of the 2.0 iq:conferenec/iq:browse protocol you will be able to log into and create password protected groups now (although this may not work yet) - there still may be some other bugs but they aren't caught yet. - in testing, it has been fairly stable, although some functionality may not be there yet. - requires jabberd version 1.4 several functions used, including MIO, MTQ, and xhash functions were not available in previous jabber server versions MSN transport ------------- (web location: http://msn-transport.jabberstudio.org/) * capabilities - provides basic interoperability between Jabber and MSN (messaging/presence) * limitations - there are memory leaks in MSN-t so is better to be run in a seperate process and restart it from time to time - MSN-t uses TCP port 1863 for incoming connections so you have to open this port if you use a firewall. - the other parts excepting messaging/presence Yahoo transport --------------- (web location: http://yahoo-transport.jabberstudio.org/) As of 2002-07-10 this transport has ceased functioning (probably due to YIM protocol changes). The transport is being worked on - see JDEV mailing list : http://mailman.jabber.org/pipermail/jdev/2002-July/012327.html * capabilities - log into Yahoo Messenger server, and send/recv messages. * limitations opensips-2.2.2/modules/jabber/doc/jabber.sql000066400000000000000000000025621300170765700210000ustar00rootroot00000000000000# # DATABASE definition # DROP DATABASE IF EXISTS sip_jab; CREATE DATABASE sip_jab; USE sip_jab; # jabber users CREATE TABLE jusers( juid INT NOT NULL AUTO_INCREMENT, jab_id VARCHAR(128) NOT NULL, jab_passwd VARCHAR(50), sip_id VARCHAR(128) NOT NULL, type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), KEY(jab_id), KEY(sip_id) ); # icq users CREATE TABLE icq( juid INT NOT NULL, icq_id INT NOT NULL, icq_passwd VARCHAR(50), icq_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(icq_id) ); # msn users CREATE TABLE msn( juid INT NOT NULL, msn_id VARCHAR(128) NOT NULL, msn_passwd VARCHAR(50), msn_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(msn_id) ); # aim users CREATE TABLE aim( juid INT NOT NULL, aim_id VARCHAR(128) NOT NULL, aim_passwd VARCHAR(50), aim_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(aim_id) ); # yahoo users CREATE TABLE yahoo( juid INT NOT NULL, yahoo_id VARCHAR(128) NOT NULL, yahoo_passwd VARCHAR(50), yahoo_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(yahoo_id) ); opensips-2.2.2/modules/jabber/doc/jabber.xml000066400000000000000000000020701300170765700207730ustar00rootroot00000000000000 %docentities; ]> jabber Module &osipsname; Daniel-Constantin Mierla miconda@gmail.com Daniel-Constantin Mierla miconda@gmail.com 2003 FhG FOKUS $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/jabber/doc/jabber_admin.xml000066400000000000000000000452751300170765700221610ustar00rootroot00000000000000 &adminguide;
Overview This is new version of Jabber module that integrates XODE XML parser for parsing Jabber messages. That introduces a new module dependency: expat library. Expat is a common XML library and is the fastest available for Linux/Unix, the second over all, after msxml library. It is integrated in most of well known Linux distributions.
New Features Presence support (see doc/xxjab.cfg for a sample cfg file) (January 2003). SIP to Jabber conference support (December 2003). Possibility to manage all kinds of Jabber messages (message/presence/iq) (December 2003). Aliases -- Possibility to set host aliases for addresses (see parameter's desc.) (December 2003). Send received &sip; MESSAGE messages to different &im; networks (Jabber, ICQ,MSN, AIM, Yahoo) using a Jabber server (December 2003). Send incoming Jabber instant messages as &sip; MESSAGE messages. Gateways detection -- Ability to see whether an &im; gateway is up or down.
Admin's Guide A more complete guide about SIMPLE2Jabber gateway can be found at &osipshomelink;. The part below will be removed soon, only the manual from web will be updated. The Jabber server setup is not a subject of this guide. Check http://www.jabber.org for that. Useful scripts, for creating Jabber Gateway database, or for managing the Jabber accounts form web are located in 'doc' subdirectory of the module. Main steps of using the Jabber gateway: Create the MySQL database. Setup the local Jabber server. Set the module parameter values in cfg file of &osips;, load the dependent modules, set up the routing rules for Jabber gateway. Run &osips;. The administrator of &osips;/Jabber gateway must inform the users what are the aliases for Jabber/Other &im; networks. Other &im;s could be AIM, ICQ, MSN, Yahoo, and so on. These aliases depend on the server hostname where runs &osips; and how local Jabber server is setup. Next is presented a use case. Prologue: &osips; is running on server.org. Local Jabber server is running on jabsrv.server.org. Jabber network alias (first part of jdomain) is jabber.server.org The aliases for other &im; networks must be the same as JID set in Jabber configuration file for each &im; transport. The JIDs of Jabber transports must start with the name of the network. For AIM, JID must start with aim., for ICQ with icq (that because I use icqv7-t), for MSN with msn. and for Yahoo with yahoo.. The gateway needs these to find out what transport is working and which not. For our use case these could be like aim.server.org, icq.server.org, msn.server.org, yahoo.server.org. It is indicated to have these aliases in DNS, thus the client application can resolve the DNS name. Otherwise there must be set the outbound proxy to &osips; server. *** Routing rules for Jabber gateway First step is to configure &osips; to recognize messages for Jabber gateway. Look at doc/xjab.cfg to see a sample. The idea is to look in messages for destination address and if it contains Jabber alias or other &im; alias, that means the message is for Jabber gateway. Next step is to find out what means that message for Jabber gateway. It could be a special message what triggers the gateway to take an action or is a simple message which should be delivered to Jabber network (using the method jab_send_message). The special messages are for: Registering to Jabber server (go online in Jabber network)--here must be called jab_go_online method. Leaving the Jabber network (go offline in Jabber network)--here must be called jab_go_offline method. Joining a Jabber conference room--here must be called jab_join_jconf. Leaving a Jabber conference room--here must be called jab_exit_jconf. The destination address must follow the following patterns: For Jabber network: username<delim>jabber_server@jabber_alias. For Jabber conference: nickname<delim>room<delim>conference_server@jabber_alias. For AIM network: aim_username@aim_alias. For ICQ network: icq_number@icq_alias. For MSN network: msn_username<delim>msn_server@msn_alias. msn_server can be msn.com or hotmail.com. For YAHOO network: yahoo_username@yahoo_alias. jabber_alias is the first part of jdomain.
&adminguide; The user must activate his Jabber account associated with his &sip; id. For each other &im; network on which he wants to send messages, he must set an account for that &im; network. The gateway is not able to create new account in foreign networks, excepting local Jabber server. When you want to send a message to someone in other &im; network, you must set the destination of the message according with the pattern corresponding to that &im; network (see last part of Admin guide chapter). Sending a message to user@jabber.xxx.org which is in Jabber network, the destination must be: user<delim>jabber.xxx.org@jabber_alias. For someone who is in Yahoo network the destination must be: user@yahoo_alias The &osips; administrator have to set the Jabber transports for each &im; network in order to be able to send messages to those networks. The alias of each &im; network can be found out from &osips; admin. You cannot send messages from your &sip; client to your associated Jabber account--is something like sending messages to yourself.
Dependencies
&osips; Modules The following modules must be loaded before this module: A database module. pa (Optionally) - Presence Agent. tm - Transaction Manager.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: Expat library.
Exported Parameters
<varname>db_url</varname> (string) SQL &url; of database. Default value is mysql://root@127.0.0.1/sip_jab. Set <varname>db_url</varname> parameter ... modparam("jabber", "db_url", "mysql://username:password@host/sip_jab") ...
<varname>jaddress</varname> (string) &ip; or hostname of Jabber server -- it must be the same as the value from <host> tag of Jabber server config file. Default value is 127.0.0.1. Set <varname>jaddress</varname> parameter ... modparam("jabber", "jaddress", "1.2.3.4") ...
<varname>jport</varname> (integer) Port number of Jabber server. Default value is 5222. Set <varname>jport</varname> parameter ... modparam("jabber", "jport", 1234) ...
<varname>jdomain</varname> (string) Format: jabber.sipserver.com=<delim>. If the destination is for Jabber network the &uri; should be like: username<delim>jabber_server@jdomain or nickname<delim>roomname<delim>conference_server@jdomain <delim> must be a un-reserved character. By default this character is * . The destination will be transformed to username@jabber_server or roomname@conference_server/nickname before the message is sent to Jabber server. Default value is none. Set <varname>jdomain</varname> parameter ... modparam("jabber", "jdomain", "jabber.sipserver.com=*") ...
<varname>aliases</varname> (string) Aliases for &im; networks. Format: N;alias1=<delim1>;...;aliasN=<delimN>; Destinations like '*@aliasX' could have other format than those specified for Jabber network. All <delim> from user part of the destination address will be changed to <delimX> if the destination address contains <aliasX>. (Ex: jdomain is 'jabber.x.com=*' and msn_alias is 'msn.x.com=%'. The destination address forM MSN Network, on &sip; side, is like 'username*hotmail.com@msn.x.com'. The destination address will be transformed to 'username%hotmail.com@msn.x.com'. 'msn.x.com' must be the same as the JID associated with MSN transport in Jabber configuration file (usually is 'jabberd.xml')) Default value is none. Set <varname>jdomain</varname> parameter ... modparam("jabber", "aliases", "1;msn.x.com=%") ...
<varname>proxy</varname> (string) Outbound proxy address. Format: ip_address:port hostname:port All &sip; messages generated by gateway will be sent to that address. If is missing, the message will be delivered to the hostname of the destination address Default value is none. Set <varname>proxy</varname> parameter ... modparam("jabber", "proxy", "10.0.0.1:5060 sipserver.com:5060") ...
<varname>registrar</varname> (string) The address in whose behalf the INFO and ERROR messages are sent. Default value is jabber_gateway@127.0.0.1. Set <varname>registrar</varname> parameter ... modparam("jabber", "registrar", "jabber_gateway@127.0.0.1") ...
<varname>workers</varname> (integer) Number of workers. Default value is 2. Set <varname>workers</varname> parameter ... modparam("jabber", "workers", 2) ...
<varname>max_jobs</varname> (integer) Maximum jobs per worker. Default value is 10. Set <varname>max_jobs</varname> parameter ... modparam("jabber", "max_jobs", 10) ...
<varname>cache_time</varname> (integer) Cache time of a Jabber connection. Default value is 600. Set <varname>cache_time</varname> parameter ... modparam("jabber", "cache_time", 600) ...
<varname>delay_time</varname> (integer) Time to keep a &sip; message (in seconds). Default value is 90 seconds. Set <varname>delay_time</varname> parameter ... modparam("jabber", "delay_time", 90) ...
<varname>sleep_time</varname> (integer) Time between expired Jabber connections checking (in seconds). Default value is 20 seconds. Set <varname>sleep_time</varname> parameter ... modparam("jabber", "sleep_time", 20) ...
<varname>check_time</varname> (integer) Time between checking the status of JabberGW workers (in seconds). Default value is 20 seconds. Set <varname>check_time</varname> parameter ... modparam("jabber", "check_time", 20) ...
<varname>priority</varname> (str) Presence priority for Jabber gateway. Default value is 9. Set <varname>priority</varname> parameter ... modparam("jabber", "priority", "3") ...
Exported Functions
<function moreinfo="none">jab_send_message()</function> Converts &sip; MESSAGE message to a Jabber message and sends it to Jabber server. This function can be used from REQUEST_ROUTE. <function>jab_send_message()</function> usage ... jab_send_message(); ...
<function moreinfo="none">jab_join_jconf()</function> Join a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . If the nickname is missing, then the &sip; username is used. This function can be used from REQUEST_ROUTE. <function>jab_join_jconf()</function> usage ... jab_join_jconf(); ...
<function moreinfo="none">jab_exit_jconf()</function> Leave a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . This function can be used from REQUEST_ROUTE. <function>jab_exit_jconf()</function> usage ... jab_exit_jconf(); ...
<function moreinfo="none">jab_go_online()</function> Register to the Jabber server with associated Jabber ID of the &sip; user. This function can be used from REQUEST_ROUTE. <function>jab_go_online()</function> usage ... jab_go_online(); ...
<function moreinfo="none">jab_go_offline()</function> Log off from Jabber server the associated Jabber ID of the &sip; user. This function can be used from REQUEST_ROUTE. <function>jab_go_offline()</function> usage ... jab_go_offline(); ...
opensips-2.2.2/modules/jabber/doc/jabberreg.pl000066400000000000000000000072501300170765700213110ustar00rootroot00000000000000#!/usr/bin/perl -w # # Register a new user with Jabber gateway # # The parameters must be only the username part of the sip address # Change the $sip_domain according with your SIP server domain name # use Socket; use DBD::mysql; if(@ARGV == 0) { die ("Syntax: regjab.pl username1 username2 ..."); } $db_driver = "mysql"; $sip_domain = "voip.org"; ##### MySQL connection to JabberGW database $jab_db_nm = "sip_jab"; #JabberGW's database name $jab_db_hn = "127.0.0.1"; #JabberGW's database server address $jab_db_pt = "3306"; #JabberGW's database server port $jab_db_us = "user"; #JabberGW's database username $jab_db_ps = "*********"; #JabberGW's database password $jab_db_dsn = "DBI:$db_driver:database=$jab_db_nm;host=$jab_db_hn;port=$jab_db_pt"; ##### users table $jab_tb_nm = "jusers"; #JabberGW's subscriber table ##### username column $jab_cl_usj = "jab_id"; $jab_cl_psj = "jab_passwd"; $jab_cl_uss = "sip_id"; $jab_server = "localhost"; #Jabber server address $jab_srvid = "jabber.x.com"; #Jabber server id $jab_port = 5222; #Jabber server port $iaddr = inet_aton($jab_server) or die "no host: $jab_server"; $paddr = sockaddr_in($jab_port, $iaddr); $proto = getprotobyname('tcp'); ### Connect to MySQL database $jab_dbh = DBI->connect($jab_db_dsn, $jab_db_us, $jab_db_ps); if(!$jab_dbh ) { print ('ERROR: Jabber\'s MySQL server not responding!'); exit; } foreach $jab_usr (@ARGV) { print "Subscribe SIP user <$jab_usr> to Jabber gateway\n"; #checkif user has already a Jabber ID $sip_uri = "sip:$jab_usr\@$sip_domain"; $jab_pwd = "qw1$jab_usr#"; $jab_sth = $jab_dbh->prepare("SELECT $jab_cl_usj FROM $jab_tb_nm WHERE $jab_cl_uss='$sip_uri'"); if (!$jab_sth) { die($jab_dbh->errstr); } if (!$jab_sth->execute) { die($jab_sth->errstr); } if(!($jab_sth->fetchrow_hashref())) { # create Jabber account print "Register SIP user <$jab_usr> to Jabber server\n"; socket(SOCK, PF_INET,SOCK_STREAM, $proto) or die "socket: $!"; connect(SOCK, $paddr) or die "connect: $!"; $jcid = 0; $err_no = 0; $line = ""; send(SOCK, $line, 0); recv(SOCK, $line, 256, 0); $jid_p = index($line, "id="); if($jid_p > 0) { $jid_p = $jid_p+4; $jid_p1 = index($line, "'", $jid_p); if($jid_p1 > 0) { $jid = substr($line, $jid_p, $jid_p1-$jid_p); print("JID: $jid\n"); } } $jcid =$jcid + 1; $line = ""; send(SOCK, $line, 0); recv(SOCK, $line, 512, 0); # here we should check what Jabber really wants # - I know for our srv, so skip it # $jcid =$jcid + 1; $line = "$jab_usr$jab_pwd"; send(SOCK, $line, 0); recv(SOCK, $line, 512, 0); if(index($line, " id='$jcid'")>0 && index($line, " type='error'")>0) { #error creating Jabber user print("Error: creating Jabber user <$jab_usr>\n$line\n"); $err_no = 1; } $line = ""; send(SOCK, $line, 0); close(SOCK) or die "close: $!"; # add to Jabber database if($err_no == 0) { $rows = $jab_dbh->do("INSERT INTO $jab_tb_nm ($jab_cl_usj, $jab_cl_psj, $jab_cl_uss) VALUES ('$jab_usr', '$jab_pwd', '$sip_uri')"); if($rows == 1) { print("SIP user <$jab_usr> added to Jabber database\n"); } else { print("Error: SIP user <$jab_usr> not added to Jabber database\n"); } } } else { print("SIP user <$jab_usr> is already in Jabber database\n"); } } $jab_sth->finish(); #### Disconnect from the database. $jab_dbh->disconnect(); opensips-2.2.2/modules/jabber/doc/jabberx.cfg000066400000000000000000000102571300170765700211300ustar00rootroot00000000000000# # configuration for Jabber module testing # (sample config file using the module with presence support) # # log_level=4 # debug level (cmd line: -dddddddddd) fork=yes # (cmd. line: -D) log_stderror=yes # (cmd line: -E) children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) listen=10.0.0.179 port=5060 # ------------------ module loading ---------------------------------- #modules loadmodule "modules/print/print.so" loadmodule "modules/textops/textops.so" loadmodule "modules/tm/tm.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/sl/sl.so" loadmodule "modules/mysql/mysql.so" loadmodule "modules/jabber/jabber.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/pa/pa.so" loadmodule "modules/mi_fifo/mi_fifo.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- registrar params -- modparam("registrar", "default_expires", 120) modparam("registrar", "use_domain", 1) # -- usrloc params -- modparam("usrloc", "use_domain", 1) modparam("usrloc", "db_mode", 0) # -- jabber params -- modparam("jabber","db_url","mysql://user:password@127.0.0.1/sip_jab") modparam("jabber","jaddress","jabber.server.com") modparam("jabber","jport",5222) modparam("jabber","workers",2) modparam("jabber","max_jobs",10) modparam("jabber","cache_time",200) modparam("jabber","delay_time",60) modparam("jabber","jdomain","jabber.server.com=*") modparam("jabber","aliases","4;aim.jabber.server.com;icq.jabber.server.com;msn.jabber.server.com=%;yahoo.jabber.server.com;") route{ #if ( !mf_process_maxfwd_header("10") ) #{ # sl_send_reply("483","To Many Hops"); # drop(); #}; if (uri=~"[@:]sip\.server\.com([;:].*)*") { # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { if (t_newtran()) { save("location"); log("REGISTER received -> reply okay\n"); }; if(search("egistration")) { log("XJAB: Going ONline in Jabber network!!!\n"); if(jab_go_online()) { sl_send_reply("200", "Accepted"); } else { sl_send_reply("404","Not found"); }; } else { log("XJAB: Going OFFline in Jabber network!!!\n"); if(jab_go_offline()) { sl_send_reply("200", "Accepted"); } else { sl_send_reply("404","Not found"); }; }; break; }; if (method=="SUBSCRIBE") { if (t_newtran()) { subscribe("registrar"); }; break; }; if(!lookup("location")) { sl_send_reply("404","Not found"); break; }; }; if ((search("To:.*@icq\.jabber\.server\.com")) || (search("To:.*@jabber\.server\.com")) || (search("To:.*@msn\.jabber\.server\.com")) || (search("To:.*@yahoo\.jabber\.server\.com"))) { if (! t_newtran()) { sl_reply_error(); break; }; if (method=="MESSAGE") { log("MESSAGE received -> manage it with XJAB\n"); if(search("\n:on")) { if (jab_go_online()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:off")) { if (jab_go_offline()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:join")) { if (jab_join_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if(search("\n:exit")) { if (jab_exit_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if (jab_send_message()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("503","Service Unavailable"); }; break; }; if (method=="SUBSCRIBE") { subscribe("jabber"); break; }; log("NON_Message request received for JABBER gateway->dropt!\n"); sl_send_reply("202","Accepted"); break; }; if (!t_relay()) { sl_reply_error(); }; #forward(uri:host,uri:port); } opensips-2.2.2/modules/jabber/doc/web/000077500000000000000000000000001300170765700176025ustar00rootroot00000000000000opensips-2.2.2/modules/jabber/doc/web/libjab.php000066400000000000000000000041071300170765700215400ustar00rootroot00000000000000"; fputs ($fd,$stream); return $fd; } function jab_disconnect($fd) { $stream = ""; fputs ($fd,$stream); fclose($fd); } function jab_get_reg($fd, $id, $server) { $str = ""; fputs ($fd,$str); } function jab_set_reg($fd, $id, $server, $username, $password) { $str = "$username$password"; fputs($fd, $str); } function jab_set_regk($fd, $id, $server, $username, $password, $nick, $key) { $str = "$username$password$nick$key"; fputs($fd, $str); } function jab_set_unreg($fd, $id, $server) { $str = ""; fputs($fd, $str); } function jab_get_agents($fd, $id, $server) { $str = ""; fputs($fd, $str); } function jab_get_auth($fd, $id, $user) { $str = "$user"; fputs($fd, $str); } function jab_set_auth($fd, $id, $user, $passwd) { $str = "$userwebjb$passwd"; fputs($fd, $str); } function jab_send_presence($fd, $to, $presence) { $str = ""; fputs($fd, $str); } ?>opensips-2.2.2/modules/jabber/doc/web/subscribe.php000066400000000000000000000331251300170765700223000ustar00rootroot00000000000000"; exit(); } # function dbg_msg($message) { # echo "$message"; } ?> IM Gateway registration
Subscription page for Instant Messaging gateway
You MUST have a SIP account


SIP username:
SIP password:

My Jabber account:      

IM service:
IM nickname:
IM account:
IM password:

     


User info

- for any operation you MUST provide your username and password

AIM Gateway subscription
- choose 'AIM' as 'IM service'
- 'IM nickname' is your display name for AIM network
- 'IM account' is your AIM account name (ex: 'alpha')
- 'IM password' is the password of your AIM account
- click on 'Subscribe'
ICQ Gateway subscription
- choose 'ICQ' as 'IM service'
- 'IM nickname' is your display name for ICQ network
- 'IM account' is your ICQ number (ex: '158251040')
- 'IM password' is the password of your ICQ account
- click on 'Subscribe'
MSN Gateway subscription
- choose 'MSN' as 'IM service'
- 'IM nickname' is your display name for MSN network
- 'IM account' is your MSN account (ex: 'alpha@hotmail.com' or 'alpha@msn.com')
- 'IM password' is the password of your MSN account
- click on 'Subscribe'
Yahoo Gateway subscription
- choose 'Yahoo' as 'IM service'
- 'IM nickname' is your display name for Yahoo network
- 'IM account' is your Yahoo account (ex: 'alpha')
- 'IM password' is the password of your Yahoo account
- click on 'Subscribe'

IM Gateway unsubscription
- choose the 'IM service' from which you want to unsubscribe
- click on 'Unsubscribe'

Instant Messaging Gateway



"; $dblink = mysql_connect($sip_db_srv, $sip_db_usr, $sip_db_pas) or html_die("Could not connect to SIP database server"); mysql_select_db($sip_db_db, $dblink) or html_die("Could not select SIP database"); $query = "SELECT $sip_db_cusr FROM $sip_db_tab WHERE $sip_db_cusr='$sipname' AND $sip_db_cpas='$sippasswd'"; dbg_msg("$query
"); $result = mysql_query($query) or html_die("Invalid SQL query"); if(mysql_num_rows($result) == 0) html_die("Invalid SIP username or password"); mysql_close($dblink); ***************************************** */ # #------------------------------------------------------ # # ----- # check if is already registered to Jabber gateway # ----- $sipuri = "sip:".$sipname."@example.org"; $dblink = mysql_connect($jab_db_srv, $jab_db_usr, $jab_db_pas) or html_die("Could not connect to Jabber database"); mysql_select_db($jab_db_db, $dblink) or html_die("Could not use Jabber database"); # ---- if($action == "Disable") { $query = "UPDATE jusers SET tyep=1 WHERE sip_id='$sipuri'"; $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { mysql_close($dblink); html_die("
Cannot find Jabber ID of '$sipname' in database"); } mysql_close($dblink); html_die("
Your IM account was updated"); } # ---- $query = "SELECT jab_id FROM jusers WHERE sip_id='$sipuri'"; $result = mysql_query($query, $dblink) or html_die("Invalid SQL query"); if(mysql_num_rows($result) == 0) { // no Jabber account - create one $fd = jab_connect($jserver, $jport); if(!$fd) html_die("Could not connect to Jabber server"); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jid1 = stristr($buf_recv, "id='"); $jid1 = substr($jid1, 4); if($jid1) { $jid2 = strstr($jid1, "'"); if($jid2) { $jid = substr($jid1, 0, strlen($jid1)-strlen($jid2)); dbg_msg("JID: $jid
"); } } $jcid = $jcid + 1; jab_get_reg($fd, $jcid, $jserver); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jcid = $jcid + 1; $new_passwd = "#".$sipname."%"; jab_set_reg($fd, $jcid, $jserver, $sipname, $new_passwd); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } if(stristr($buf_recv, " id='$jcid'") && stristr($buf_recv, " type='error'")) { mysql_close($dblink); jab_disconnect($fd); html_die("
Something bizarre with account '$sipname'"); } # ----- # Add user in database # ----- $query = "INSERT INTO jusers (jab_id, jab_passwd, sip_id) VALUES ('$sipname', '$new_passwd', '$sipuri')"; $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { mysql_close($dblink); jab_disconnect($fd); html_die("
Can not insert '$sipname' in database"); } jab_disconnect($fd); } # ----- if($action == "Enable") { $query = "UPDATE jusers SET type=0 WHERE sip_id='$sipuri'"; $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { mysql_close($dblink); html_die("
Cannot find Jabber ID of '$sipname' in database"); } mysql_close($dblink); html_die("
Your IM account was updated"); } # ----- $query="SELECT juid,jab_id,jab_passwd,type FROM jusers WHERE sip_id='$sipuri' and type=0"; $result = mysql_query($query, $dblink) or html_die("Invalid SQL query"); if(mysql_num_rows($result) != 1 || (!($row = mysql_fetch_array($result)))) { mysql_close($dblink); html_die("
You do not have an associated Jabber account or it is disabled!
Press 'Enable' in order to create a new one or to activate an old one.
If error persists, please inform the administrator."); } $juid = $row[0]; $jab_id = $row[1]; $jab_passwd = $row[2]; $jab_type = $row[3]; dbg_msg("Jabber User ID: $juid
"); $fd = jab_connect($jserver, $jport); if(!$fd) html_die("Could not connect to Jabber server"); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jid1 = stristr($buf_recv, "id='"); $jid1 = substr($jid1, 4); if($jid1) { $jid2 = strstr($jid1, "'"); if($jid2) { $jid = substr($jid1, 0, strlen($jid1)-strlen($jid2)); dbg_msg("JID: $jid
"); } } $jcid = $jcid + 1; jab_get_auth($fd, $jcid, $jab_id); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jcid = $jcid + 1; jab_set_auth($fd, $jcid, $jab_id, $jab_passwd); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } if(stristr($buf_recv, " id='$jcid'") && stristr($buf_recv, " type='error'")) { jab_disconnect($fd); html_die("
Wrong username or password at Jabber authentication"); } # ----- # browse agents # ----- $jcid = $jcid + 1; jab_get_agents($fd, $jcid, $jserver); $buf_agents = fread($fd, 4096); while(!$buf_agents) { usleep(100); $buf_agents = fread($fd, 4096); } # dbg_msg("\n"); # ----- $imag1 = stristr($buf_agents, ""); } } # ----- if(isset($imag)) { if($action == "Subscribe" && isset($imname) && $imname != "") { echo "

IM ($imtype) subscription


"; # ----- # unsubscribe the previous IM account (if exists) # ----- $jcid = $jcid + 1; jab_set_unreg($fd, $jcid, $icqag); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } sleep(1); # ----- # subscription # ----- $jcid = $jcid + 1; jab_get_reg($fd, $jcid, $imag); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $imkey1 = stristr($buf_recv, ""); $imkey1 = substr($imkey1, 5); if($imkey1) { $imkey2 = strstr($imkey1, ""); if($imkey2) { $imkey = substr($imkey1, 0, strlen($imkey1)-strlen($imkey2)); dbg_msg("IM key: $imkey
"); } } if(!isset($imkey)) { jab_disconnect($fd); mysql_close($dblink); html_die("
Session key for IM ($imtype) Transport not found"); } $jcid = $jcid + 1; jab_set_regk($fd, $jcid, $imag, $imname, $impasswd, $imnick, $imkey); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } if(stristr($buf_recv, " id='$jcid'") && stristr($buf_recv, " type='error'")) { $err1 = stristr($buf_recv, ""); $err1 = substr($err1, 1); if($err1) { $err2 = strstr($err1, ""); if($err2) $err = substr($err1, 0, strlen($err1)-strlen($err2)); } jab_disconnect($fd); mysql_close($dblink); html_die("
Error registering your IM ($imtype) account: $err"); } jab_send_presence($fd, $imag."/registered", "subscribed"); # ----- # Update database $query = "SELECT ".$imtype."_id FROM ".$imtype." WHERE juid='$juid'"; $result = mysql_query($query, $dblink) or html_die("Invalid SQL query"); if(mysql_num_rows($result) == 0) { # INSERT $query = "INSERT INTO ".$imtype." (juid, ".$imtype."_id, ".$imtype."_passwd, ".$imtype."_nick) VALUES ('$juid', '$imname', '$impasswd', '$imnick')"; dbg_msg("$query
"); $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { echo "
Can not register '$sipname'/'$imname'
"; } else { echo "Your IM ($imtype) account was successfully registered
"; } } else { # UPDATE $query = "UPDATE ".$imtype." SET ".$imtype."_id='$imname', ".$imtype."_passwd='$impasswd', ".$imtype."_nick='$imnick' WHERE juid='$juid'"; dbg_msg("$query
"); $result = mysql_query($query, $dblink); if(!$result) { echo "
Can not update '$sipname'/'$imname'
"; } else { if(mysql_affected_rows() == 1) { echo "Your IM ($imtype) account was successfully updated
"; } else { echo "No modification in your IM ($imtype) account
"; } } } } else { echo "

IM ($imtype) unsubscription


"; # ----- # unsubscribe the IM account # ----- $jcid = $jcid + 1; jab_set_unreg($fd, $jcid, $icqag); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } sleep(1); $query = "DELETE FROM ".$imtype." WHERE juid='$juid'"; dbg_msg("$query
"); $result = mysql_query($query, $dblink); if(!$result) { echo "
Can not remove IM ($imtype) information from database
"; } else { echo "
Unsubscription from IM ($imtype) completed
"; } } } sleep(1); jab_disconnect($fd); mysql_close($dblink); } ?> opensips-2.2.2/modules/jabber/jabber.c000066400000000000000000000554461300170765700176670ustar00rootroot00000000000000/* * XJAB module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --- * * History * ------- * 2003-02-28 connection management with ihttp implemented (dcm) * 2003-02-24 first version of callback functions for ihttp (dcm) * 2003-02-13 lot of comments enclosed in #ifdef XJ_EXTRA_DEBUG (dcm) * 2003-03-11 New module interface (janakj) * 2003-03-16 flags export parameter added (janakj) * 2003-04-06 rank 0 changed to 1 in child_init (janakj) * 2003-06-19 fixed too many Jabber workers bug (mostly on RH9.0) (dcm) * 2003-08-05 adapted to the new parse_content_type_hdr function (bogdan) * 2004-06-07 db API update (andrei) */ #include #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../ut.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../globals.h" #include "../../timer.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../../db/db.h" #include "../tm/tm_load.h" #ifdef HAVE_IHTTP #include "../ihttp/ih_load.h" #endif #include "xjab_load.h" #include "xjab_worker.h" #include "xjab_util.h" /** TM bind */ struct tm_binds tmb; #ifdef HAVE_IHTTP /** iHTTP bind */ struct ih_binds ihb; /** iHTTP callback functions */ int xjab_mod_info(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl); int xjab_connections(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl); #endif /** workers list */ xj_wlist jwl = NULL; /** Structure that represents database connection */ static db_con_t** db_con; static db_func_t jabber_dbf; /** parameters */ static str db_url = {NULL, 0}; static str db_table = str_init("jusers"); char *registrar=NULL; /*"sip:registrar@example.org";*/ int nrw = 2; int max_jobs = 10; char *jaddress = "127.0.0.1"; int jport = 5222; char *jaliases = NULL; char *jdomain = NULL; char *proxy = NULL; char* priority = "9"; int delay_time = 90; int sleep_time = 20; int cache_time = 600; int check_time = 20; int **pipes = NULL; static int mod_init(void); static int child_init(int rank); int xjab_manage_sipmsg(struct sip_msg *msg, int type); void xjab_check_workers(int mpid); static int xj_send_message(struct sip_msg*, char*, char*); static int xj_join_jconf(struct sip_msg*, char*, char*); static int xj_exit_jconf(struct sip_msg*, char*, char*); static int xj_go_online(struct sip_msg*, char*, char*); static int xj_go_offline(struct sip_msg*, char*, char*); void destroy(void); /* * Exported functions */ static cmd_export_t cmds[] = { {"jab_send_message", (cmd_function)xj_send_message, 0, 0, 0, REQUEST_ROUTE}, {"jab_join_jconf", (cmd_function)xj_join_jconf, 0, 0, 0, REQUEST_ROUTE}, {"jab_exit_jconf", (cmd_function)xj_exit_jconf, 0, 0, 0, REQUEST_ROUTE}, {"jab_go_online", (cmd_function)xj_go_online, 0, 0, 0, REQUEST_ROUTE}, {"jab_go_offline", (cmd_function)xj_go_offline, 0, 0, 0, REQUEST_ROUTE}, {"jab_register_watcher", (cmd_function)xj_register_watcher, XJ_NO_SCRIPT_F, 0, 0, 0 }, {"jab_unregister_watcher", (cmd_function)xj_unregister_watcher, XJ_NO_SCRIPT_F, 0, 0, 0 }, {"load_xjab", (cmd_function)load_xjab, XJ_NO_SCRIPT_F, 0, 0, 0 }, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &db_url.s }, {"jaddress", STR_PARAM, &jaddress }, {"aliases", STR_PARAM, &jaliases }, {"proxy", STR_PARAM, &proxy }, {"jdomain", STR_PARAM, &jdomain }, {"registrar", STR_PARAM, ®istrar }, {"priority", STR_PARAM, &priority }, {"jport", INT_PARAM, &jport }, {"workers", INT_PARAM, &nrw }, {"max_jobs", INT_PARAM, &max_jobs }, {"cache_time", INT_PARAM, &cache_time}, {"delay_time", INT_PARAM, &delay_time}, {"sleep_time", INT_PARAM, &sleep_time}, {"check_time", INT_PARAM, &check_time}, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "jabber", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, (destroy_function) destroy, child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { #ifdef HAVE_IHTTP load_ih_f load_ih; #endif int i; init_db_url( db_url , 0 /*cannot be null*/); LM_INFO("initializing ...\n"); if(!jdomain) { LM_ERR("jdomain is NULL\n"); return -1; } /* import mysql functions */ if (db_bind_mod(&db_url, &jabber_dbf)<0) { LM_ERR("database module not found\n"); return -1; } if (!DB_CAPABILITY(jabber_dbf, DB_CAP_QUERY)) { LM_ERR("database module does not implement 'query' function\n"); return -1; } db_con = (db_con_t**)shm_malloc(nrw*sizeof(db_con_t*)); if (db_con == NULL) { LM_ERR("no more shm memory\n"); return -1; } /* load the TM API */ if (load_tm_api(&tmb)!=0) { LM_ERR("can't load TM API\n"); return -1; } #ifdef HAVE_IHTTP /* import the iHTTP auto-loading function */ if ( !(load_ih=(load_ih_f)find_export("load_ih", IH_NO_SCRIPT_F, 0))) { LM_ERR("can't import load_ih\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_ih( &ihb )==-1) return -1; #endif pipes = (int**)pkg_malloc(nrw*sizeof(int*)); if (pipes == NULL) { LM_ERR("no more pkg memory (pipes)\n"); return -1; } for(i=0; i-<%d>\n", i, pipes[i][0], pipes[i][1]); } if((jwl = xj_wlist_init(pipes,nrw,max_jobs,cache_time,sleep_time, delay_time)) == NULL) { LM_ERR("failed to initialize workers list\n"); return -1; } if(xj_wlist_set_aliases(jwl, jaliases, jdomain, proxy) < 0) { LM_ERR("failed to set aliases and outbound proxy\n"); return -1; } LM_DBG("initialized ...\n"); return 0; } /* * Initialize children */ static int child_init(int rank) { int i, j, mpid, cpid; LM_DBG("initializing child <%d>\n", rank); /* Rank 0 is main process now - 1 is the first child (janakj) */ if(rank == 1) { #ifdef HAVE_IHTTP /** register iHTTP callbacks -- go forward in any case*/ ihb.reg_f("xjab", "XMPP Gateway", IH_MENU_YES, xjab_mod_info, NULL); ihb.reg_f("xjabc", "XMPP connections", IH_MENU_YES, xjab_connections, NULL); #endif if((mpid=fork())<0 ) { LM_ERR("cannot launch worker's manager\n"); return -1; } if(mpid == 0) { /** launching the workers */ for(i=0;ito || !msg->from) { LM_ERR("cannot find TO or FROM HEADERS!\n"); goto error; } /* parsing from header */ if ( parse_from_header( msg )<0 || msg->from->parsed==NULL) { LM_DBG("cannot get FROM header\n"); goto error; } from_uri.s = ((struct to_body*)msg->from->parsed)->uri.s; from_uri.len = ((struct to_body*)msg->from->parsed)->uri.len; if(xj_extract_aor(&from_uri, 0)) { LM_DBG("cannot get AoR from FROM header\n"); goto error; } jkey.hash = xj_get_hash(&from_uri, NULL); jkey.id = &from_uri; // get the communication pipe with the worker switch(type) { case XJ_SEND_MESSAGE: case XJ_JOIN_JCONF: case XJ_GO_ONLINE: if((pipe = xj_wlist_get(jwl, &jkey, &p)) < 0) { LM_DBG("cannot find pipe of the worker!\n"); goto error; } break; case XJ_EXIT_JCONF: case XJ_GO_OFFLINE: if((pipe = xj_wlist_check(jwl, &jkey, &p)) < 0) { LM_DBG("no open Jabber session for" " <%.*s>!\n", from_uri.len, from_uri.s); goto error; } break; default: LM_DBG("ERROR:strange SIP msg type!\n"); goto error; } // if is for going ONLINE/OFFLINE we do not need the destination if(type==XJ_GO_ONLINE || type==XJ_GO_OFFLINE) goto prepare_job; // determination of destination // - try to get it from new_uri, r-uri or to hdr, but check it against // jdomain and aliases dst.len = 0; if( msg->new_uri.len > 0) { dst.s = msg->new_uri.s; dst.len = msg->new_uri.len; if(xj_wlist_check_aliases(jwl, &dst)) dst.len = 0; #ifdef XJ_EXTRA_DEBUG else LM_DBG("using NEW URI for destination\n"); #endif } if (dst.len == 0 && msg->first_line.u.request.uri.s != NULL && msg->first_line.u.request.uri.len > 0 ) { dst.s = msg->first_line.u.request.uri.s; dst.len = msg->first_line.u.request.uri.len; if(xj_wlist_check_aliases(jwl, &dst)) dst.len = 0; #ifdef XJ_EXTRA_DEBUG else LM_DBG("using R-URI for destination\n"); #endif } if(dst.len == 0 && msg->to->parsed) { dst.s = ((struct to_body*)msg->to->parsed)->uri.s; dst.len = ((struct to_body*)msg->to->parsed)->uri.len; if(dst.s == NULL || xj_wlist_check_aliases(jwl, &dst)) dst.len = 0; #ifdef XJ_EXTRA_DEBUG else LM_DBG("using TO-URI for destination\n"); #endif } if(dst.len == 0) { LM_DBG("destination not found in SIP message\n"); goto error; } /** skip 'sip:' and parameters in destination address */ if(xj_extract_aor(&dst, 1)) { LM_ERR("cannot get AoR for destination\n"); goto error; } #ifdef XJ_EXTRA_DEBUG LM_DBG("destination after correction [%.*s].\n", dst.len, dst.s); #endif prepare_job: //putting the SIP message parts in share memory to be accessible by workers jsmsg = (xj_sipmsg)shm_malloc(sizeof(t_xj_sipmsg)); memset(jsmsg, 0, sizeof(t_xj_sipmsg)); if(jsmsg == NULL) return -1; switch(type) { case XJ_SEND_MESSAGE: jsmsg->msg.len = body.len; if((jsmsg->msg.s = (char*)shm_malloc(jsmsg->msg.len+1)) == NULL) { shm_free(jsmsg); goto error; } strncpy(jsmsg->msg.s, body.s, jsmsg->msg.len); break; case XJ_GO_ONLINE: case XJ_GO_OFFLINE: dst.len = 0; dst.s = 0; case XJ_JOIN_JCONF: case XJ_EXIT_JCONF: jsmsg->msg.len = 0; jsmsg->msg.s = NULL; break; default: LM_DBG("this SHOULD NOT appear\n"); shm_free(jsmsg); goto error; } if(dst.len>0) { jsmsg->to.len = dst.len; if((jsmsg->to.s = (char*)shm_malloc(jsmsg->to.len+1))==NULL) { if(type == XJ_SEND_MESSAGE) shm_free(jsmsg->msg.s); shm_free(jsmsg); goto error; } strncpy(jsmsg->to.s, dst.s, jsmsg->to.len); } else { jsmsg->to.len = 0; jsmsg->to.s = 0; } jsmsg->jkey = p; jsmsg->type = type; //jsmsg->jkey->hash = jkey.hash; LM_DBG("sending <%p> to worker through <%d>\n", jsmsg, pipe); // sending the SHM pointer of SIP message to the worker fl = write(pipe, &jsmsg, sizeof(jsmsg)); if(fl != sizeof(jsmsg)) { LM_ERR("failed to write to worker pipe!\n"); if(type == XJ_SEND_MESSAGE) shm_free(jsmsg->msg.s); shm_free(jsmsg->to.s); shm_free(jsmsg); goto error; } return 1; error: return -1; } /** * destroy function of module */ void destroy(void) { int i; #ifdef XJ_EXTRA_DEBUG LM_DBG("unloading module ...\n"); #endif if(pipes) { // close the pipes for(i = 0; i < nrw; i++) { if(pipes[i]) { close(pipes[i][0]); close(pipes[i][1]); } pkg_free(pipes[i]); } pkg_free(pipes); } // cleaning MySQL connections if(db_con != NULL) { for(i = 0; ilen, from->s, to->len, to->s); #endif from_uri.s = from->s; from_uri.len = from->len; if(xj_extract_aor(&from_uri, 0)) { LM_ERR("cannot get AoR from FROM header\n"); goto error; } jkey.hash = xj_get_hash(&from_uri, NULL); jkey.id = &from_uri; if((pipe = xj_wlist_get(jwl, &jkey, &jp)) < 0) { LM_DBG("cannot find pipe of the worker!\n"); goto error; } //putting the SIP message parts in share memory to be accessible by workers jsmsg = (xj_sipmsg)shm_malloc(sizeof(t_xj_sipmsg)); memset(jsmsg, 0, sizeof(t_xj_sipmsg)); if(jsmsg == NULL) goto error; jsmsg->msg.len = 0; jsmsg->msg.s = NULL; to_uri.s = to->s; to_uri.len = to->len; /** skip 'sip:' and parameters in destination address */ if(xj_extract_aor(&to_uri, 1)) { LM_ERR("cannot get AoR for destination\n"); goto error; } #ifdef XJ_EXTRA_DEBUG LM_DBG("destination after correction [%.*s].\n", to_uri.len, to_uri.s); #endif jsmsg->to.len = to_uri.len; if((jsmsg->to.s = (char*)shm_malloc(jsmsg->to.len+1)) == NULL) { if(jsmsg->msg.s) shm_free(jsmsg->msg.s); shm_free(jsmsg); goto error; } strncpy(jsmsg->to.s, to_uri.s, jsmsg->to.len); jsmsg->to.s[jsmsg->to.len] = '\0'; jsmsg->jkey = jp; jsmsg->type = XJ_REG_WATCHER; //jsmsg->jkey->hash = jkey.hash; jsmsg->cbf = (pa_callback_f)cbf; jsmsg->p = pp; #ifdef XJ_EXTRA_DEBUG LM_DBG("sending <%p> to worker through <%d>\n", jsmsg, pipe); #endif // sending the SHM pointer of SIP message to the worker fl = write(pipe, &jsmsg, sizeof(jsmsg)); if(fl != sizeof(jsmsg)) { LM_ERR("failed to write to worker pipe!\n"); if(jsmsg->msg.s) shm_free(jsmsg->msg.s); shm_free(jsmsg->to.s); shm_free(jsmsg); goto error; } error: return; } /** * unregister a watcher for a Jabber user' presence */ void xj_unregister_watcher(str *from, str *to, void *cbf, void *pp) { if(!to || !from) return; } /** * check if all SER2Jab workers are still alive * - if not, try to launch new ones */ void xjab_check_workers(int mpid) { int i, n, stat; //LM_DBG("time=%d\n", get_ticks()); if(!jwl || jwl->len <= 0) return; for(i=0; i < jwl->len; i++) { if(jwl->workers[i].pid > 0) { stat = 0; n = waitpid(jwl->workers[i].pid, &stat, WNOHANG); if(n == 0 || n!=jwl->workers[i].pid) continue; LM_ERR("worker[%d][pid=%d] has exited - status=%d err=%d" "errno=%d\n", i, jwl->workers[i].pid, stat, n, errno); xj_wlist_clean_jobs(jwl, i, 1); xj_wlist_set_pid(jwl, -1, i); } #ifdef XJ_EXTRA_DEBUG LM_DBG("create a new worker[%d]\n", i); #endif if ( (stat=fork())<0 ) { #ifdef XJ_EXTRA_DEBUG LM_DBG("cannot launch new worker[%d]\n", i); #endif LM_ERR("worker[%d] lost forever \n", i); return; } if (stat == 0) { if(xj_wlist_set_pid(jwl, getpid(), i) < 0) { LM_ERR("failed to set new worker's pid - w[%d]\n", i); return; } xj_worker_process(jwl,jaddress,jport,priority, i, db_con[i], &jabber_dbf); exit(0); } } } #ifdef HAVE_IHTTP /** * Module's information retrieval - function to use with iHttp module * */ int xjab_mod_info(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl) { if(!_irp || !_bb || !_bl || *_bl <= 0 || !_hb || !_hl || *_hl <= 0) return -1; *_hl = 0; *_hb = 0; strcpy(_bb, "

SER2Jabber Gateway

"); strcat(_bb, "
Module parameters:
"); strcat(_bb, "
-- db table = "); strcat(_bb, db_table); strcat(_bb, "
-- workers = "); strcat(_bb, int2str(nrw, NULL)); strcat(_bb, "
-- max jobs per worker = "); strcat(_bb, int2str(max_jobs, NULL)); strcat(_bb, "
-- jabber server address = "); strcat(_bb, jaddress); strcat(_bb, "
-- jabber server port = "); strcat(_bb, int2str(jport, NULL)); strcat(_bb, "
-- aliases = "); strcat(_bb, (jaliases)?jaliases:"NULL"); strcat(_bb, "
-- jabber domain = "); strcat(_bb, (jdomain)?jdomain:"NULL"); strcat(_bb, "
-- proxy address = "); strcat(_bb, (proxy)?proxy:"NULL"); strcat(_bb, "
-- delay time = "); strcat(_bb, int2str(delay_time, NULL)); strcat(_bb, "
-- sleep time = "); strcat(_bb, int2str(sleep_time, NULL)); strcat(_bb, "
-- cache time = "); strcat(_bb, int2str(cache_time, NULL)); strcat(_bb, "
-- check time = "); strcat(_bb, int2str(check_time, NULL)); *_bl = strlen(_bb); return 0; } /** * SER2Jab connection management - function to use with iHttp module * - be aware of who is able to use the ihttp because he can close any * open connection between SER and Jabber server */ int xjab_connections(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl) { t_xj_jkey jkey, *p; str _u; ih_param_p _ipp = NULL; int idx, i, maxcount; char *cp; if(!_irp || !_bb || !_bl || *_bl <= 0 || !_hb || !_hl || *_hl <= 0) return -1; *_hl = 0; *_hb = 0; idx = -1; strcpy(_bb, "

Active XMPP connections

"); if(_irp->params) { strcat(_bb, "
Close action is alpha release!
"); _ipp = _irp->params; i = 0; while(_ipp) { switch(_ipp->name[0]) { case 'w': idx = 0; cp = _ipp->value; while(*cp && *cp>='0' && *cp<='9') { idx = idx*10 + *cp-'0'; cp++; } i++; break; case 'u': _u.s = _ipp->value; _u.len = strlen(_ipp->value); jkey.id = &_u; i++; break; case 'i': jkey.hash = 0; cp = _ipp->value; while(*cp && *cp>='0' && *cp<='9') { jkey.hash = jkey.hash*10 + *cp-'0'; cp++; } i++; break; } _ipp = _ipp->next; } if(i!=3 || idx < 0 || idx >= jwl->len) { strcat(_bb, "
Bad parameters!\n"); } else { strcat(_bb, "
The connection of ["); strcat(_bb, _u.s); if(xj_wlist_set_flag(jwl, &jkey, XJ_FLAG_CLOSE) < 0) strcat(_bb, "] does not exist!\n"); else strcat(_bb, "] was scheduled for closing!
\n"); } *_bl = strlen(_bb); return 0; } if(jwl!=NULL && jwl->len > 0 && jwl->workers!=NULL) { for(idx=0; idxlen; idx++) { strcat(_bb, "
Worker["); strcat(_bb, int2str(idx, NULL)); strcat(_bb, "]   pid="); strcat(_bb, int2str(jwl->workers[idx].pid, NULL)); strcat(_bb, "   nr of jobs="); strcat(_bb, int2str(jwl->workers[idx].nr, NULL)); if(!jwl->workers[idx].sip_ids) continue; lock_set_get(jwl->sems, idx); maxcount = count234(jwl->workers[idx].sip_ids); for (i = 0; i < maxcount; i++) { p = (xj_jkey)index234(jwl->workers[idx].sip_ids, i); if(p == NULL) continue; strcat(_bb, "
   "); strcat(_bb, int2str(i, NULL)); strcat(_bb, ".   "); strcat(_bb, "close"); strcat(_bb, "   "); strcat(_bb, int2str(p->hash, NULL)); strcat(_bb, "   "); strncat(_bb, p->id->s, p->id->len); } lock_set_release(jwl->sems, idx); } } *_bl = strlen(_bb); return 0; } #endif // HAVE_IHTTP opensips-2.2.2/modules/jabber/mdefines.h000066400000000000000000000026731300170765700202330ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*************************************************************************** mdefines.h - description ------------------- author : Daniel-Constantin MIERLA email : mierla@fokus.fhg.de organization : FhI FOKUS, BERLIN ***************************************************************************/ #ifndef _mdefines_h_ #define _mdefines_h_ #define _M_PRINTF printf #define _M_CALLOC calloc #define _M_REALLOC realloc #define _M_MALLOC pkg_malloc #define _M_FREE pkg_free #define _M_SHM_MALLOC shm_malloc #define _M_SHM_FREE shm_free #endif opensips-2.2.2/modules/jabber/sha.c000066400000000000000000000127071300170765700172060ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Gabber * Copyright (C) 1999-2000 Dave Smith & Julian Missig * */ /* Implements the Secure Hash Algorithm (SHA1) Copyright (C) 1999 Scott G. Miller Released under the terms of the GNU General Public License v2 see file COPYING for details Credits: Robert Klep -- Expansion function fix Thomas "temas" Muldowney : -- shahash() for string fun -- Will add the int32 stuff in a few --- FIXME: This source takes int to be a 32 bit integer. This may vary from system to system. I'd use autoconf if I was familiar with it. Anyone want to help me out? */ //#include #include #include #include #include #ifndef MACOS # include # include #endif #ifndef WIN32 # include # define INT64 long long #else # define snprintf _snprintf # define INT64 __int64 #endif #define switch_endianness(x) (x<<24 & 0xff000000) | \ (x<<8 & 0x00ff0000) | \ (x>>8 & 0x0000ff00) | \ (x>>24 & 0x000000ff) /* Initial hash values */ #define Ai 0x67452301 #define Bi 0xefcdab89 #define Ci 0x98badcfe #define Di 0x10325476 #define Ei 0xc3d2e1f0 /* SHA1 round constants */ #define K1 0x5a827999 #define K2 0x6ed9eba1 #define K3 0x8f1bbcdc #define K4 0xca62c1d6 /* Round functions. Note that f2() is used in both rounds 2 and 4 */ #define f1(B,C,D) ((B & C) | ((~B) & D)) #define f2(B,C,D) (B ^ C ^ D) #define f3(B,C,D) ((B & C) | (B & D) | (C & D)) /* left circular shift functions (rotate left) */ #define rol1(x) ((x<<1) | ((x>>31) & 1)) #define rol5(A) ((A<<5) | ((A>>27) & 0x1f)) #define rol30(B) ((B<<30) | ((B>>2) & 0x3fffffff)) /* Hashes 'data', which should be a pointer to 512 bits of data (sixteen 32 bit ints), into the ongoing 160 bit hash value (five 32 bit ints) 'hash' */ int sha_hash(int *data, int *hash) { int W[80]; unsigned int A=hash[0], B=hash[1], C=hash[2], D=hash[3], E=hash[4]; unsigned int t, x, TEMP; for (t=0; t<16; t++) { #ifndef WORDS_BIGENDIAN W[t]=switch_endianness(data[t]); #else W[t]=data[t]; #endif } /* SHA1 Data expansion */ for (t=16; t<80; t++) { x=W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]; W[t]=rol1(x); } /* SHA1 main loop (t=0 to 79) This is broken down into four subloops in order to use the correct round function and constant */ for (t=0; t<20; t++) { TEMP=rol5(A) + f1(B,C,D) + E + W[t] + K1; E=D; D=C; C=rol30(B); B=A; A=TEMP; } for (; t<40; t++) { TEMP=rol5(A) + f2(B,C,D) + E + W[t] + K2; E=D; D=C; C=rol30(B); B=A; A=TEMP; } for (; t<60; t++) { TEMP=rol5(A) + f3(B,C,D) + E + W[t] + K3; E=D; D=C; C=rol30(B); B=A; A=TEMP; } for (; t<80; t++) { TEMP=rol5(A) + f2(B,C,D) + E + W[t] + K4; E=D; D=C; C=rol30(B); B=A; A=TEMP; } hash[0]+=A; hash[1]+=B; hash[2]+=C; hash[3]+=D; hash[4]+=E; return 0; } /* Takes a pointer to a 160 bit block of data (five 32 bit ints) and initializes it to the start constants of the SHA1 algorithm. This must be called before using hash in the call to sha_hash */ int sha_init(int *hash) { hash[0]=Ai; hash[1]=Bi; hash[2]=Ci; hash[3]=Di; hash[4]=Ei; return 0; } int strprintsha(char *dest, int *hashval) { int x; char *hashstr = dest; for (x=0; x<5; x++) { snprintf(hashstr, 9, "%08x", hashval[x]); hashstr+=8; } /*old way */ //snprintf(hashstr++, 1, "\0"); /*new way - by bogdan*/ *hashstr = 0; return 0; } char *shahash(const char *str) { char read_buffer[65]; //int read_buffer[64]; int c=1, i; INT64 length=0; int strsz; static char final[40]; int *hashval; hashval = (int *)malloc(20); sha_init(hashval); strsz = strlen(str); if(strsz == 0) { memset(read_buffer, 0, 65); read_buffer[0] = 0x80; sha_hash((int *)read_buffer, hashval); } while (strsz>0) { memset(read_buffer, 0, 65); strncpy((char*)read_buffer, str, 64); c = strlen((char *)read_buffer); length+=c; strsz-=c; if (strsz<=0) { length<<=3; read_buffer[c]=(char)0x80; for (i=c+1; i<64; i++) read_buffer[i]=0; if (c>55) { /* we need to do an entire new block */ sha_hash((int *)read_buffer, hashval); for (i=0; i<14; i++) ((int*)read_buffer)[i]=0; } #ifndef WORDS_BIGENDIAN for (i=0; i<8; i++) { read_buffer[56+i]=(char)(length>>(56-(i*8))) & 0xff; } #else memcpy(read_buffer+56, &length, 8); #endif } sha_hash((int *)read_buffer, hashval); str+=64; } strprintsha((char *)final, hashval); free(hashval); return (char *)final; } opensips-2.2.2/modules/jabber/tree234.c000066400000000000000000001204421300170765700176170ustar00rootroot00000000000000/* * tree234.c: reasonably generic counted 2-3-4 tree routines. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include #include #include #include "tree234.h" #include "../../mem/shm_mem.h" //#define smalloc malloc //#define sfree free #define smalloc shm_malloc #define sfree shm_free #define mknew(typ) ( (typ *) smalloc (sizeof (typ)) ) #ifdef TEST #define LOG123(x) (printf x) #else #define LOG123(x) #endif typedef struct node234_Tag node234; struct tree234_Tag { node234 *root; cmpfn234 cmp; }; struct node234_Tag { node234 *parent; node234 *kids[4]; int counts[4]; void *elems[3]; }; /* * Create a 2-3-4 tree. */ tree234 *newtree234(cmpfn234 cmp) { tree234 *ret = mknew(tree234); LOG123(("created tree %p\n", ret)); ret->root = NULL; ret->cmp = cmp; return ret; } /* * Free a 2-3-4 tree (not including freeing the elements). */ static void freenode234(node234 *n) { if (!n) return; freenode234(n->kids[0]); freenode234(n->kids[1]); freenode234(n->kids[2]); freenode234(n->kids[3]); sfree(n); } void freetree234(tree234 *t) { if(t == NULL) return; freenode234(t->root); sfree(t); } /* * Free a 2-3-4 tree (including freeing the elements with 'fn' function). */ static void free2node234(node234 *n, freefn fn ) { if (!n) return; free2node234(n->kids[0], fn); free2node234(n->kids[1], fn); free2node234(n->kids[2], fn); free2node234(n->kids[3], fn); fn(n->elems[0]); fn(n->elems[1]); fn(n->elems[2]); sfree(n); } void free2tree234(tree234 *t, freefn fn) { if(t == NULL) return; free2node234(t->root, fn); sfree(t); } /* * Internal function to count a node. */ static int countnode234(node234 *n) { int count = 0; int i; if (!n) return 0; for (i = 0; i < 4; i++) count += n->counts[i]; for (i = 0; i < 3; i++) if (n->elems[i]) count++; return count; } /* * Count the elements in a tree. */ int count234(tree234 *t) { if (t->root) return countnode234(t->root); else return 0; } /* * Add an element e to a 2-3-4 tree t. Returns e on success, or if * an existing element compares equal, returns that. */ static void *add234_internal(tree234 *t, void *e, int index) { node234 *n, **np, *left, *right; void *orig_e = e; int c, lcount, rcount; LOG123(("adding node %p to tree %p\n", e, t)); if (t->root == NULL) { t->root = mknew(node234); t->root->elems[1] = t->root->elems[2] = NULL; t->root->kids[0] = t->root->kids[1] = NULL; t->root->kids[2] = t->root->kids[3] = NULL; t->root->counts[0] = t->root->counts[1] = 0; t->root->counts[2] = t->root->counts[3] = 0; t->root->parent = NULL; t->root->elems[0] = e; LOG123((" created root %p\n", t->root)); return orig_e; } np = &t->root; n = *np; while (*np) { int childnum; n = *np; LOG123((" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); if (index >= 0) { if (!n->kids[0]) { /* * Leaf node. We want to insert at kid position * equal to the index: * * 0 A 1 B 2 C 3 */ childnum = index; } else { /* * Internal node. We always descend through it (add * always starts at the bottom, never in the * middle). */ do { /* this is a do ... while (0) to allow `break' */ if (index <= n->counts[0]) { childnum = 0; break; } index -= n->counts[0] + 1; if (index <= n->counts[1]) { childnum = 1; break; } index -= n->counts[1] + 1; if (index <= n->counts[2]) { childnum = 2; break; } index -= n->counts[2] + 1; if (index <= n->counts[3]) { childnum = 3; break; } return NULL; /* error: index out of range */ } while (0); } } else { if ((c = t->cmp(e, n->elems[0])) < 0) childnum = 0; else if (c == 0) return n->elems[0]; /* already exists */ else if (n->elems[1] == NULL || (c = t->cmp(e, n->elems[1])) < 0) childnum = 1; else if (c == 0) return n->elems[1]; /* already exists */ else if (n->elems[2] == NULL || (c = t->cmp(e, n->elems[2])) < 0) childnum = 2; else if (c == 0) return n->elems[2]; /* already exists */ else childnum = 3; } np = &n->kids[childnum]; LOG123((" moving to child %d (%p)\n", childnum, *np)); } /* * We need to insert the new element in n at position np. */ left = NULL; lcount = 0; right = NULL; rcount = 0; while (n) { LOG123((" at %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG123((" need to insert %p/%d [%p] %p/%d at position %d\n", left, lcount, e, right, rcount, np - n->kids)); if (n->elems[1] == NULL) { /* * Insert in a 2-node; simple. */ if (np == &n->kids[0]) { LOG123((" inserting on left of 2-node\n")); n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else { /* np == &n->kids[1] */ LOG123((" inserting on right of 2-node\n")); n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; LOG123((" done\n")); break; } else if (n->elems[2] == NULL) { /* * Insert in a 3-node; simple. */ if (np == &n->kids[0]) { LOG123((" inserting on left of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else if (np == &n->kids[1]) { LOG123((" inserting in middle of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } else { /* np == &n->kids[2] */ LOG123((" inserting on right of 3-node\n")); n->kids[3] = right; n->counts[3] = rcount; n->elems[2] = e; n->kids[2] = left; n->counts[2] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; if (n->kids[3]) n->kids[3]->parent = n; LOG123((" done\n")); break; } else { node234 *m = mknew(node234); m->parent = n->parent; LOG123((" splitting a 4-node; created new node %p\n", m)); /* * Insert in a 4-node; split into a 2-node and a * 3-node, and move focus up a level. * * I don't think it matters which way round we put the * 2 and the 3. For simplicity, we'll put the 3 first * always. */ if (np == &n->kids[0]) { m->kids[0] = left; m->counts[0] = lcount; m->elems[0] = e; m->kids[1] = right; m->counts[1] = rcount; m->elems[1] = n->elems[0]; m->kids[2] = n->kids[1]; m->counts[2] = n->counts[1]; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[1]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = left; m->counts[1] = lcount; m->elems[1] = e; m->kids[2] = right; m->counts[2] = rcount; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[2]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = left; m->counts[2] = lcount; /* e = e; */ n->kids[0] = right; n->counts[0] = rcount; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else { /* np == &n->kids[3] */ m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = n->kids[2]; m->counts[2] = n->counts[2]; n->kids[0] = left; n->counts[0] = lcount; n->elems[0] = e; n->kids[1] = right; n->counts[1] = rcount; e = n->elems[2]; } m->kids[3] = n->kids[3] = n->kids[2] = NULL; m->counts[3] = n->counts[3] = n->counts[2] = 0; m->elems[2] = n->elems[2] = n->elems[1] = NULL; if (m->kids[0]) m->kids[0]->parent = m; if (m->kids[1]) m->kids[1]->parent = m; if (m->kids[2]) m->kids[2]->parent = m; if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; LOG123((" left (%p): %p/%d [%p] %p/%d [%p] %p/%d\n", m, m->kids[0], m->counts[0], m->elems[0], m->kids[1], m->counts[1], m->elems[1], m->kids[2], m->counts[2])); LOG123((" right (%p): %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1])); left = m; lcount = countnode234(left); right = n; rcount = countnode234(right); } if (n->parent) np = (n->parent->kids[0] == n ? &n->parent->kids[0] : n->parent->kids[1] == n ? &n->parent->kids[1] : n->parent->kids[2] == n ? &n->parent->kids[2] : &n->parent->kids[3]); n = n->parent; } /* * If we've come out of here by `break', n will still be * non-NULL and all we need to do is go back up the tree * updating counts. If we've come here because n is NULL, we * need to create a new root for the tree because the old one * has just split into two. */ if (n) { while (n->parent) { int count = countnode234(n); int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum] = count; n = n->parent; } } else { LOG123((" root is overloaded, split into two\n")); t->root = mknew(node234); t->root->kids[0] = left; t->root->counts[0] = lcount; t->root->elems[0] = e; t->root->kids[1] = right; t->root->counts[1] = rcount; t->root->elems[1] = NULL; t->root->kids[2] = NULL; t->root->counts[2] = 0; t->root->elems[2] = NULL; t->root->kids[3] = NULL; t->root->counts[3] = 0; t->root->parent = NULL; if (t->root->kids[0]) t->root->kids[0]->parent = t->root; if (t->root->kids[1]) t->root->kids[1]->parent = t->root; LOG123((" new root is %p/%d [%p] %p/%d\n", t->root->kids[0], t->root->counts[0], t->root->elems[0], t->root->kids[1], t->root->counts[1])); } return orig_e; } void *add234(tree234 *t, void *e) { if (!t->cmp) /* tree is unsorted */ return NULL; return add234_internal(t, e, -1); } void *addpos234(tree234 *t, void *e, int index) { if (index < 0 || /* index out of range */ t->cmp) /* tree is sorted */ return NULL; /* return failure */ return add234_internal(t, e, index); /* this checks the upper bound */ } /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. */ void *index234(tree234 *t, int index) { node234 *n; if (!t->root) return NULL; /* tree is empty */ if (index < 0 || index >= countnode234(t->root)) return NULL; /* out of range */ n = t->root; while (n) { if (index < n->counts[0]) n = n->kids[0]; else if (index -= n->counts[0] + 1, index < 0) return n->elems[0]; else if (index < n->counts[1]) n = n->kids[1]; else if (index -= n->counts[1] + 1, index < 0) return n->elems[1]; else if (index < n->counts[2]) n = n->kids[2]; else if (index -= n->counts[2] + 1, index < 0) return n->elems[2]; else n = n->kids[3]; } /* We shouldn't ever get here. I wonder how we did. */ return NULL; } /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. */ void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, int *index) { node234 *n; void *ret; int c; int idx, ecount, kcount, cmpret; if (t->root == NULL) return NULL; if (cmp == NULL) cmp = t->cmp; n = t->root; /* * Attempt to find the element itself. */ idx = 0; ecount = -1; /* * Prepare a fake `cmp' result if e is NULL. */ cmpret = 0; if (e == NULL) { assert(relation == REL234_LT || relation == REL234_GT); if (relation == REL234_LT) cmpret = +1; /* e is a max: always greater */ else if (relation == REL234_GT) cmpret = -1; /* e is a min: always smaller */ } while (1) { for (kcount = 0; kcount < 4; kcount++) { if (kcount >= 3 || n->elems[kcount] == NULL || (c = cmpret ? cmpret : cmp(e, n->elems[kcount])) < 0) { break; } if (n->kids[kcount]) idx += n->counts[kcount]; if (c == 0) { ecount = kcount; break; } idx++; } if (ecount >= 0) break; if (n->kids[kcount]) n = n->kids[kcount]; else break; } if (ecount >= 0) { /* * We have found the element we're looking for. It's * n->elems[ecount], at tree index idx. If our search * relation is EQ, LE or GE we can now go home. */ if (relation != REL234_LT && relation != REL234_GT) { if (index) *index = idx; return n->elems[ecount]; } /* * Otherwise, we'll do an indexed lookup for the previous * or next element. (It would be perfectly possible to * implement these search types in a non-counted tree by * going back up from where we are, but far more fiddly.) */ if (relation == REL234_LT) idx--; else idx++; } else { /* * We've found our way to the bottom of the tree and we * know where we would insert this node if we wanted to: * we'd put it in in place of the (empty) subtree * n->kids[kcount], and it would have index idx * * But the actual element isn't there. So if our search * relation is EQ, we're doomed. */ if (relation == REL234_EQ) return NULL; /* * Otherwise, we must do an index lookup for index idx-1 * (if we're going left - LE or LT) or index idx (if we're * going right - GE or GT). */ if (relation == REL234_LT || relation == REL234_LE) { idx--; } } /* * We know the index of the element we want; just call index234 * to do the rest. This will return NULL if the index is out of * bounds, which is exactly what we want. */ ret = index234(t, idx); if (ret && index) *index = idx; return ret; } void *find234(tree234 *t, void *e, cmpfn234 cmp) { return findrelpos234(t, e, cmp, REL234_EQ, NULL); } void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation) { return findrelpos234(t, e, cmp, relation, NULL); } void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index) { return findrelpos234(t, e, cmp, REL234_EQ, index); } /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. */ static void *delpos234_internal(tree234 *t, int index) { node234 *n; void *retval; int ei = -1; retval = 0; n = t->root; LOG123(("deleting item %d from tree %p\n", index, t)); while (1) { while (n) { int ki; node234 *sub; LOG123((" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d index=%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3], index)); if (index < n->counts[0]) { ki = 0; } else if (index -= n->counts[0]+1, index < 0) { ei = 0; break; } else if (index < n->counts[1]) { ki = 1; } else if (index -= n->counts[1]+1, index < 0) { ei = 1; break; } else if (index < n->counts[2]) { ki = 2; } else if (index -= n->counts[2]+1, index < 0) { ei = 2; break; } else { ki = 3; } /* * Recurse down to subtree ki. If it has only one element, * we have to do some transformation to start with. */ LOG123((" moving to subtree %d\n", ki)); sub = n->kids[ki]; if (!sub->elems[1]) { LOG123((" subtree has only one element!\n", ki)); if (ki > 0 && n->kids[ki-1]->elems[1]) { /* * Case 3a, left-handed variant. Child ki has * only one element, but child ki-1 has two or * more. So we need to move a subtree from ki-1 * to ki. * * . C . . B . * / \ -> / \ * [more] a A b B c d D e [more] a A b c C d D e */ node234 *sib = n->kids[ki-1]; int lastelem = (sib->elems[2] ? 2 : sib->elems[1] ? 1 : 0); sub->kids[2] = sub->kids[1]; sub->counts[2] = sub->counts[1]; sub->elems[1] = sub->elems[0]; sub->kids[1] = sub->kids[0]; sub->counts[1] = sub->counts[0]; sub->elems[0] = n->elems[ki-1]; sub->kids[0] = sib->kids[lastelem+1]; sub->counts[0] = sib->counts[lastelem+1]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->elems[ki-1] = sib->elems[lastelem]; sib->kids[lastelem+1] = NULL; sib->counts[lastelem+1] = 0; sib->elems[lastelem] = NULL; n->counts[ki] = countnode234(sub); LOG123((" case 3a left\n")); LOG123((" index and left subtree count before adjustment: %d, %d\n", index, n->counts[ki-1])); index += n->counts[ki-1]; n->counts[ki-1] = countnode234(sib); index -= n->counts[ki-1]; LOG123((" index and left subtree count after adjustment: %d, %d\n", index, n->counts[ki-1])); } else if (ki < 3 && n->kids[ki+1] && n->kids[ki+1]->elems[1]) { /* * Case 3a, right-handed variant. ki has only * one element but ki+1 has two or more. Move a * subtree from ki+1 to ki. * * . B . . C . * / \ -> / \ * a A b c C d D e [more] a A b B c d D e [more] */ node234 *sib = n->kids[ki+1]; int j; sub->elems[1] = n->elems[ki]; sub->kids[2] = sib->kids[0]; sub->counts[2] = sib->counts[0]; if (sub->kids[2]) sub->kids[2]->parent = sub; n->elems[ki] = sib->elems[0]; sib->kids[0] = sib->kids[1]; sib->counts[0] = sib->counts[1]; for (j = 0; j < 2 && sib->elems[j+1]; j++) { sib->kids[j+1] = sib->kids[j+2]; sib->counts[j+1] = sib->counts[j+2]; sib->elems[j] = sib->elems[j+1]; } sib->kids[j+1] = NULL; sib->counts[j+1] = 0; sib->elems[j] = NULL; n->counts[ki] = countnode234(sub); n->counts[ki+1] = countnode234(sib); LOG123((" case 3a right\n")); } else { /* * Case 3b. ki has only one element, and has no * neighbor with more than one. So pick a * neighbor and merge it with ki, taking an * element down from n to go in the middle. * * . B . . * / \ -> | * a A b c C d a A b B c C d * * (Since at all points we have avoided * descending to a node with only one element, * we can be sure that n is not reduced to * nothingness by this move, _unless_ it was * the very first node, ie the root of the * tree. In that case we remove the now-empty * root and replace it with its single large * child as shown.) */ node234 *sib; int j; if (ki > 0) { ki--; index += n->counts[ki] + 1; } sib = n->kids[ki]; sub = n->kids[ki+1]; sub->kids[3] = sub->kids[1]; sub->counts[3] = sub->counts[1]; sub->elems[2] = sub->elems[0]; sub->kids[2] = sub->kids[0]; sub->counts[2] = sub->counts[0]; sub->elems[1] = n->elems[ki]; sub->kids[1] = sib->kids[1]; sub->counts[1] = sib->counts[1]; if (sub->kids[1]) sub->kids[1]->parent = sub; sub->elems[0] = sib->elems[0]; sub->kids[0] = sib->kids[0]; sub->counts[0] = sib->counts[0]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->counts[ki+1] = countnode234(sub); sfree(sib); /* * That's built the big node in sub. Now we * need to remove the reference to sib in n. */ for (j = ki; j < 3 && n->kids[j+1]; j++) { n->kids[j] = n->kids[j+1]; n->counts[j] = n->counts[j+1]; n->elems[j] = j<2 ? n->elems[j+1] : NULL; } n->kids[j] = NULL; n->counts[j] = 0; if (j < 3) n->elems[j] = NULL; LOG123((" case 3b ki=%d\n", ki)); if (!n->elems[0]) { /* * The root is empty and needs to be * removed. */ LOG123((" shifting root!\n")); t->root = sub; sub->parent = NULL; sfree(n); } } } n = sub; } if (!retval) retval = n->elems[ei]; if (ei==-1) return NULL; /* although this shouldn't happen */ /* * Treat special case: this is the one remaining item in * the tree. n is the tree root (no parent), has one * element (no elems[1]), and has no kids (no kids[0]). */ if (!n->parent && !n->elems[1] && !n->kids[0]) { LOG123((" removed last element in tree\n")); sfree(n); t->root = NULL; return retval; } /* * Now we have the element we want, as n->elems[ei], and we * have also arranged for that element not to be the only * one in its node. So... */ if (!n->kids[0] && n->elems[1]) { /* * Case 1. n is a leaf node with more than one element, * so it's _really easy_. Just delete the thing and * we're done. */ int i; LOG123((" case 1\n")); for (i = ei; i < 2 && n->elems[i+1]; i++) n->elems[i] = n->elems[i+1]; n->elems[i] = NULL; /* * Having done that to the leaf node, we now go back up * the tree fixing the counts. */ while (n->parent) { int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum]--; n = n->parent; } return retval; /* finished! */ } else if (n->kids[ei]->elems[1]) { /* * Case 2a. n is an internal node, and the root of the * subtree to the left of e has more than one element. * So find the predecessor p to e (ie the largest node * in that subtree), place it where e currently is, and * then start the deletion process over again on the * subtree with p as target. */ node234 *m = n->kids[ei]; void *target; LOG123((" case 2a\n")); while (m->kids[0]) { m = (m->kids[3] ? m->kids[3] : m->kids[2] ? m->kids[2] : m->kids[1] ? m->kids[1] : m->kids[0]); } target = (m->elems[2] ? m->elems[2] : m->elems[1] ? m->elems[1] : m->elems[0]); n->elems[ei] = target; index = n->counts[ei]-1; n = n->kids[ei]; } else if (n->kids[ei+1]->elems[1]) { /* * Case 2b, symmetric to 2a but s/left/right/ and * s/predecessor/successor/. (And s/largest/smallest/). */ node234 *m = n->kids[ei+1]; void *target; LOG123((" case 2b\n")); while (m->kids[0]) { m = m->kids[0]; } target = m->elems[0]; n->elems[ei] = target; n = n->kids[ei+1]; index = 0; } else { /* * Case 2c. n is an internal node, and the subtrees to * the left and right of e both have only one element. * So combine the two subnodes into a single big node * with their own elements on the left and right and e * in the middle, then restart the deletion process on * that subtree, with e still as target. */ node234 *a = n->kids[ei], *b = n->kids[ei+1]; int j; LOG123((" case 2c\n")); a->elems[1] = n->elems[ei]; a->kids[2] = b->kids[0]; a->counts[2] = b->counts[0]; if (a->kids[2]) a->kids[2]->parent = a; a->elems[2] = b->elems[0]; a->kids[3] = b->kids[1]; a->counts[3] = b->counts[1]; if (a->kids[3]) a->kids[3]->parent = a; sfree(b); n->counts[ei] = countnode234(a); /* * That's built the big node in a, and destroyed b. Now * remove the reference to b (and e) in n. */ for (j = ei; j < 2 && n->elems[j+1]; j++) { n->elems[j] = n->elems[j+1]; n->kids[j+1] = n->kids[j+2]; n->counts[j+1] = n->counts[j+2]; } n->elems[j] = NULL; n->kids[j+1] = NULL; n->counts[j+1] = 0; /* * It's possible, in this case, that we've just removed * the only element in the root of the tree. If so, * shift the root. */ if (n->elems[0] == NULL) { LOG123((" shifting root!\n")); t->root = a; a->parent = NULL; sfree(n); } /* * Now go round the deletion process again, with n * pointing at the new big node and e still the same. */ n = a; index = a->counts[0] + a->counts[1] + 1; } } } void *delpos234(tree234 *t, int index) { if (index < 0 || index >= countnode234(t->root)) return NULL; return delpos234_internal(t, index); } void *del234(tree234 *t, void *e) { int index; if (!findrelpos234(t, e, NULL, REL234_EQ, &index)) return NULL; /* it wasn't in there anyway */ return delpos234_internal(t, index); /* it's there; delete it. */ } #ifdef TEST /* * Test code for the 2-3-4 tree. This code maintains an alternative * representation of the data in the tree, in an array (using the * obvious and slow insert and delete functions). After each tree * operation, the verify() function is called, which ensures all * the tree properties are preserved: * - node->child->parent always equals node * - tree->root->parent always equals NULL * - number of kids == 0 or number of elements + 1; * - tree has the same depth everywhere * - every node has at least one element * - subtree element counts are accurate * - any NULL kid pointer is accompanied by a zero count * - in a sorted tree: ordering property between elements of a * node and elements of its children is preserved * and also ensures the list represented by the tree is the same * list it should be. (This last check also doubly verifies the * ordering properties, because the `same list it should be' is by * definition correctly ordered. It also ensures all nodes are * distinct, because the enum functions would get caught in a loop * if not.) */ #include #define srealloc realloc /* * Error reporting function. */ void error(char *fmt, ...) { va_list ap; printf("ERROR: "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n"); } /* The array representation of the data. */ void **array; int arraylen, arraysize; cmpfn234 cmp; /* The tree representation of the same data. */ tree234 *tree; typedef struct { int treedepth; int elemcount; } chkctx; int chknode(chkctx *ctx, int level, node234 *node, void *lowbound, void *highbound) { int nkids, nelems; int i; int count; /* Count the non-NULL kids. */ for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++); /* Ensure no kids beyond the first NULL are non-NULL. */ for (i = nkids; i < 4; i++) if (node->kids[i]) { error("node %p: nkids=%d but kids[%d] non-NULL", node, nkids, i); } else if (node->counts[i]) { error("node %p: kids[%d] NULL but count[%d]=%d nonzero", node, i, i, node->counts[i]); } /* Count the non-NULL elements. */ for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++); /* Ensure no elements beyond the first NULL are non-NULL. */ for (i = nelems; i < 3; i++) if (node->elems[i]) { error("node %p: nelems=%d but elems[%d] non-NULL", node, nelems, i); } if (nkids == 0) { /* * If nkids==0, this is a leaf node; verify that the tree * depth is the same everywhere. */ if (ctx->treedepth < 0) ctx->treedepth = level; /* we didn't know the depth yet */ else if (ctx->treedepth != level) error("node %p: leaf at depth %d, previously seen depth %d", node, level, ctx->treedepth); } else { /* * If nkids != 0, then it should be nelems+1, unless nelems * is 0 in which case nkids should also be 0 (and so we * shouldn't be in this condition at all). */ int shouldkids = (nelems ? nelems+1 : 0); if (nkids != shouldkids) { error("node %p: %d elems should mean %d kids but has %d", node, nelems, shouldkids, nkids); } } /* * nelems should be at least 1. */ if (nelems == 0) { error("node %p: no elems", node, nkids); } /* * Add nelems to the running element count of the whole tree. */ ctx->elemcount += nelems; /* * Check ordering property: all elements should be strictly > * lowbound, strictly < highbound, and strictly < each other in * sequence. (lowbound and highbound are NULL at edges of tree * - both NULL at root node - and NULL is considered to be < * everything and > everything. IYSWIM.) */ if (cmp) { for (i = -1; i < nelems; i++) { void *lower = (i == -1 ? lowbound : node->elems[i]); void *higher = (i+1 == nelems ? highbound : node->elems[i+1]); if (lower && higher && cmp(lower, higher) >= 0) { error("node %p: kid comparison [%d=%s,%d=%s] failed", node, i, lower, i+1, higher); } } } /* * Check parent pointers: all non-NULL kids should have a * parent pointer coming back to this node. */ for (i = 0; i < nkids; i++) if (node->kids[i]->parent != node) { error("node %p kid %d: parent ptr is %p not %p", node, i, node->kids[i]->parent, node); } /* * Now (finally!) recurse into subtrees. */ count = nelems; for (i = 0; i < nkids; i++) { void *lower = (i == 0 ? lowbound : node->elems[i-1]); void *higher = (i >= nelems ? highbound : node->elems[i]); int subcount = chknode(ctx, level+1, node->kids[i], lower, higher); if (node->counts[i] != subcount) { error("node %p kid %d: count says %d, subtree really has %d", node, i, node->counts[i], subcount); } count += subcount; } return count; } void verify(void) { chkctx ctx; int i; void *p; ctx.treedepth = -1; /* depth unknown yet */ ctx.elemcount = 0; /* no elements seen yet */ /* * Verify validity of tree properties. */ if (tree->root) { if (tree->root->parent != NULL) error("root->parent is %p should be null", tree->root->parent); chknode(&ctx, 0, tree->root, NULL, NULL); } printf("tree depth: %d\n", ctx.treedepth); /* * Enumerate the tree and ensure it matches up to the array. */ for (i = 0; NULL != (p = index234(tree, i)); i++) { if (i >= arraylen) error("tree contains more than %d elements", arraylen); if (array[i] != p) error("enum at position %d: array says %s, tree says %s", i, array[i], p); } if (ctx.elemcount != i) { error("tree really contains %d elements, enum gave %d", ctx.elemcount, i); } if (i < arraylen) { error("enum gave only %d elements, array has %d", i, arraylen); } i = count234(tree); if (ctx.elemcount != i) { error("tree really contains %d elements, count234 gave %d", ctx.elemcount, i); } } void internal_addtest(void *elem, int index, void *realret) { int i, j; void *retval; if (arraysize < arraylen+1) { arraysize = arraylen+1+256; array = (array == NULL ? smalloc(arraysize*sizeof(*array)) : srealloc(array, arraysize*sizeof(*array))); } i = index; /* now i points to the first element >= elem */ retval = elem; /* expect elem returned (success) */ for (j = arraylen; j > i; j--) array[j] = array[j-1]; array[i] = elem; /* add elem to array */ arraylen++; if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } verify(); } void addtest(void *elem) { int i; void *realret; realret = add234(tree, elem); i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i < arraylen && !cmp(elem, array[i])) { void *retval = array[i]; /* expect that returned not elem */ if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } } else internal_addtest(elem, i, realret); } void addpostest(void *elem, int i) { void *realret; realret = addpos234(tree, elem, i); internal_addtest(elem, i, realret); } void delpostest(int i) { int index = i; void *elem = array[i], *ret; /* i points to the right element */ while (i < arraylen-1) { array[i] = array[i+1]; i++; } arraylen--; /* delete elem from array */ if (tree->cmp) ret = del234(tree, elem); else ret = delpos234(tree, index); if (ret != elem) { error("del returned %p, expected %p", ret, elem); } verify(); } void deltest(void *elem) { int i; i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i >= arraylen || cmp(elem, array[i]) != 0) return; /* don't do it! */ delpostest(i); } /* A sample data set and test utility. Designed for pseudo-randomness, * and yet repeatability. */ /* * This random number generator uses the `portable implementation' * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits; * change it if not. */ int randomnumber(unsigned *seed) { *seed *= 1103515245; *seed += 12345; return ((*seed) / 65536) % 32768; } int mycmp(void *av, void *bv) { char const *a = (char const *)av; char const *b = (char const *)bv; return strcmp(a, b); } #define lenof(x) ( sizeof((x)) / sizeof(*(x)) ) char *strings[] = { "a", "ab", "absque", "coram", "de", "palam", "clam", "cum", "ex", "e", "sine", "tenus", "pro", "prae", "banana", "carrot", "cabbage", "broccoli", "onion", "zebra", "penguin", "blancmange", "pangolin", "whale", "hedgehog", "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux", "murfl", "spoo", "breen", "flarn", "octothorpe", "snail", "tiger", "elephant", "octopus", "warthog", "armadillo", "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin", "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper", "wand", "ring", "amulet" }; #define NSTR lenof(strings) int findtest(void) { const static int rels[] = { REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT }; const static char *const relnames[] = { "EQ", "GE", "LE", "LT", "GT" }; int i, j, rel, index; char *p, *ret, *realret, *realret2; int lo, hi, mid, c; for (i = 0; i < NSTR; i++) { p = strings[i]; for (j = 0; j < sizeof(rels)/sizeof(*rels); j++) { rel = rels[j]; lo = 0; hi = arraylen-1; while (lo <= hi) { mid = (lo + hi) / 2; c = strcmp(p, array[mid]); if (c < 0) hi = mid-1; else if (c > 0) lo = mid+1; else break; } if (c == 0) { if (rel == REL234_LT) ret = (mid > 0 ? array[--mid] : NULL); else if (rel == REL234_GT) ret = (mid < arraylen-1 ? array[++mid] : NULL); else ret = array[mid]; } else { assert(lo == hi+1); if (rel == REL234_LT || rel == REL234_LE) { mid = hi; ret = (hi >= 0 ? array[hi] : NULL); } else if (rel == REL234_GT || rel == REL234_GE) { mid = lo; ret = (lo < arraylen ? array[lo] : NULL); } else ret = NULL; } realret = findrelpos234(tree, p, NULL, rel, &index); if (realret != ret) { error("find(\"%s\",%s) gave %s should be %s", p, relnames[j], realret, ret); } if (realret && index != mid) { error("find(\"%s\",%s) gave %d should be %d", p, relnames[j], index, mid); } if (realret && rel == REL234_EQ) { realret2 = index234(tree, index); if (realret2 != realret) { error("find(\"%s\",%s) gave %s(%d) but %d -> %s", p, relnames[j], realret, index, index, realret2); } } #if 0 printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j], realret, index); #endif } } realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index); if (arraylen && (realret != array[0] || index != 0)) { error("find(NULL,GT) gave %s(%d) should be %s(0)", realret, index, array[0]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,GT) gave %s(%d) should be NULL", realret, index); } realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index); if (arraylen && (realret != array[arraylen-1] || index != arraylen-1)) { error("find(NULL,LT) gave %s(%d) should be %s(0)", realret, index, array[arraylen-1]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,LT) gave %s(%d) should be NULL", realret, index); } } int main(void) { int in[NSTR]; int i, j, k; unsigned seed = 0; for (i = 0; i < NSTR; i++) in[i] = 0; array = NULL; arraylen = arraysize = 0; tree = newtree234(mycmp); cmp = mycmp; verify(); for (i = 0; i < 10000; i++) { j = randomnumber(&seed); j %= NSTR; printf("trial: %d\n", i); if (in[j]) { printf("deleting %s (%d)\n", strings[j], j); deltest(strings[j]); in[j] = 0; } else { printf("adding %s (%d)\n", strings[j], j); addtest(strings[j]); in[j] = 1; } findtest(); } while (arraylen > 0) { j = randomnumber(&seed); j %= arraylen; deltest(array[j]); } freetree234(tree); /* * Now try an unsorted tree. We don't really need to test * delpos234 because we know del234 is based on it, so it's * already been tested in the above sorted-tree code; but for * completeness we'll use it to tear down our unsorted tree * once we've built it. */ tree = newtree234(NULL); cmp = NULL; verify(); for (i = 0; i < 1000; i++) { printf("trial: %d\n", i); j = randomnumber(&seed); j %= NSTR; k = randomnumber(&seed); k %= count234(tree)+1; printf("adding string %s at index %d\n", strings[j], k); addpostest(strings[j], k); } while (count234(tree) > 0) { printf("cleanup: tree size %d\n", count234(tree)); j = randomnumber(&seed); j %= count234(tree); printf("deleting string %s from index %d\n", array[j], j); delpostest(j); } return 0; } #endif opensips-2.2.2/modules/jabber/tree234.h000066400000000000000000000132641300170765700176270ustar00rootroot00000000000000/* * tree234.h: header defining functions in tree234.c. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #ifndef TREE234_H #define TREE234_H /* * This typedef is opaque outside tree234.c itself. */ typedef struct tree234_Tag tree234; typedef int (*cmpfn234)(void *, void *); /** * function for deallocation of a element pointer from a node */ typedef void (*freefn)(void *); /* * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and * lookups by key will fail: you can only look things up by numeric * index, and you have to use addpos234() and delpos234(). */ tree234 *newtree234(cmpfn234 cmp); /* * Free a 2-3-4 tree (not including freeing the elements). */ void freetree234(tree234 *t); /* * Free a 2-3-4 tree (including freeing the elements with 'fn' function). */ void free2tree234(tree234 *t, freefn fn); /* * Add an element e to a sorted 2-3-4 tree t. Returns e on success, * or if an existing element compares equal, returns that. */ void *add234(tree234 *t, void *e); /* * Add an element e to an unsorted 2-3-4 tree t. Returns e on * success, NULL on failure. (Failure should only occur if the * index is out of range or the tree is sorted.) * * Index range can be from 0 to the tree's current element count, * inclusive. */ void *addpos234(tree234 *t, void *e, int index); /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. * * One obvious use for this function is in iterating over the whole * of a tree (sorted or unsorted): * * for (i = 0; (p = index234(tree, i)) != NULL; i++) consume(p); * * or * * int maxcount = count234(tree); * for (i = 0; i < maxcount; i++) { * p = index234(tree, i); * assert(p != NULL); * consume(p); * } */ void *index234(tree234 *t, int index); /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. * * Three of these functions are special cases of findrelpos234. The * non-`pos' variants lack the `index' parameter: if the parameter * is present and non-NULL, it must point to an integer variable * which will be filled with the numeric index of the returned * element. * * The non-`rel' variants lack the `relation' parameter. This * parameter allows you to specify what relation the element you * provide has to the element you're looking for. This parameter * can be: * * REL234_EQ - find only an element that compares equal to e * REL234_LT - find the greatest element that compares < e * REL234_LE - find the greatest element that compares <= e * REL234_GT - find the smallest element that compares > e * REL234_GE - find the smallest element that compares >= e * * Non-`rel' variants assume REL234_EQ. * * If `rel' is REL234_GT or REL234_LT, the `e' parameter may be * NULL. In this case, REL234_GT will return the smallest element * in the tree, and REL234_LT will return the greatest. This gives * an alternative means of iterating over a sorted tree, instead of * using index234: * * // to loop forwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_GT)) != NULL ;) * consume(p); * * // to loop backwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_LT)) != NULL ;) * consume(p); */ enum { REL234_EQ, REL234_LT, REL234_LE, REL234_GT, REL234_GE }; void *find234(tree234 *t, void *e, cmpfn234 cmp); void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation); void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index); void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, int *index); /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. * * delpos234 deletes the element at a particular tree index: it * works on both sorted and unsorted trees. * * del234 deletes the element passed to it, so it only works on * sorted trees. (It's equivalent to using findpos234 to determine * the index of an element, and then passing that index to * delpos234.) * * Both functions return a pointer to the element they delete, for * the user to free or pass on elsewhere or whatever. If the index * is out of range (delpos234) or the element is already not in the * tree (del234) then they return NULL. */ void *del234(tree234 *t, void *e); void *delpos234(tree234 *t, int index); /* * Return the total element count of a tree234. */ int count234(tree234 *t); #endif /* TREE234_H */ opensips-2.2.2/modules/jabber/xjab_base.c000066400000000000000000000064671300170765700203570ustar00rootroot00000000000000/* * eXtended JABber module - Jabber connections pool * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_uri.h" #include "xjab_base.h" #include "mdefines.h" /** * get the hash code - based on Andrei's function * */ int xj_get_hash(str *x, str *y) { char* p; register unsigned v; register unsigned h; if(!x && !y) return 0; h=0; if(x) { for (p=x->s; p<=(x->s+x->len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } v=0; for (;p<(x->s+x->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } if(y) { for (p=y->s; p<=(y->s+y->len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } v=0; for (;p<(y->s+y->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } h=((h)+(h>>11))+((h>>13)+(h>>23)); return (h)?h:1; } /** * function used to compare two xj_jkey elements */ int xj_jkey_cmp(void *x, void *y) { int n; xj_jkey a, b; a = (xj_jkey)x; b = (xj_jkey)y; if(a == NULL || a->id == NULL || a->id->s == NULL) return -1; if(b == NULL || b->id == NULL || b->id->s == NULL) return 1; // LM_DBG("comparing <%.*s> / <%.*s>\n", ((str *)a)->len, // ((str *)a)->s, ((str *)b)->len, ((str *)b)->s); if(a->hash != b->hash) return (a->hash < b->hash)?-1:1; if(a->id->len != b->id->len) return (a->id->len < b->id->len)?-1:1; n=strncmp(a->id->s,b->id->s,a->id->len); if(n!=0) return (n<0)?-1:1; return 0; } /** * free the information from a jkey */ void xj_jkey_free_p(void *p) { if(p == NULL) return; if(((xj_jkey)p)->id != NULL) { if(((xj_jkey)p)->id->s != NULL) _M_SHM_FREE(((xj_jkey)p)->id->s); _M_SHM_FREE(((xj_jkey)p)->id); } _M_SHM_FREE(p); } /** * free a pointer to a t_jab_sipmsg structure * > element where points 'from' MUST be deliberated separated */ void xj_sipmsg_free(xj_sipmsg jsmsg) { if(jsmsg == NULL) return; if(jsmsg->to.s != NULL) _M_SHM_FREE(jsmsg->to.s); // the key is deallocated when the connection is closed // if(jsmsg->jkey->id->s != NULL) // _M_SHM_FREE(jsmsg->from->id->s); if(jsmsg->msg.s != NULL) _M_SHM_FREE(jsmsg->msg.s); _M_SHM_FREE(jsmsg); } int xj_extract_aor(str* u, int t) { struct sip_uri puri; if(!u) return -1; if (parse_uri(u->s, u->len, &puri) < 0) { LM_ERR("failed to parse URI\n"); return -1; } if(t == 1) u->s = puri.user.s; u->len = puri.host.s + puri.host.len - u->s; return 0; } opensips-2.2.2/modules/jabber/xjab_base.h000066400000000000000000000041771300170765700203600ustar00rootroot00000000000000/** * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*** * --- * * History * ------- * 2003-06-05 previously added macro replaced with 'xj_extract_aor', (dcm) * 2003-05-09 added macro for adjusting a SIP address, (dcm) */ #ifndef _XJAB_BASE_H_ #define _XJAB_BASE_H_ #include "../../str.h" #define XJ_NULL 0 #define XJ_SEND_MESSAGE 1 #define XJ_JOIN_JCONF 2 #define XJ_EXIT_JCONF 4 #define XJ_GO_ONLINE 8 #define XJ_GO_OFFLINE 16 #define XJ_REG_WATCHER 32 #define XJ_DEL_WATCHER 64 #define XJ_FLAG_OPEN 0 #define XJ_FLAG_CLOSE 1 typedef void (*pa_callback_f)(str* _user, str* _contact, int _state, void *p); /********** ***/ typedef struct _xj_jkey { int hash; int flag; str *id; } t_xj_jkey, *xj_jkey; /********** ***/ typedef struct _xj_sipmsg { int type; // type of message xj_jkey jkey; // pointer to FROM str to; // destination str msg; // message body pa_callback_f cbf; // callback function void *p; // callback parameter } t_xj_sipmsg, *xj_sipmsg; /********** LOOK AT IMPLEMENTATION OF FUNCTIONS FOR DESCRIPTION ***/ void xj_sipmsg_free(xj_sipmsg); /********** ***/ int xj_jkey_cmp(void*, void*); void xj_jkey_free_p(void*); void xj_jkey_free(xj_jkey); /********** ***/ int xj_get_hash(str*, str*); char *shahash(const char *); int xj_extract_aor(str*, int); #endif opensips-2.2.2/modules/jabber/xjab_dmsg.h000066400000000000000000000054571300170765700204020ustar00rootroot00000000000000/* * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*** error and info messages ***/ #ifndef _XJAB_DMSG_H_ #define _XJAB_DMSG_H_ #define XJ_DMSG_INF_DISCONNECTED "INFO: Connection to Jabber server lost. You have to login to Jabber server again (join again the conferences that you were participating, too)." #define XJ_DMSG_ERR_SENDIM "ERROR: Your message was not sent. Connection to IM network failed." #define XJ_DMSG_ERR_NOTJCONF "ERROR: Your message was not sent. You are not joined in the conference. Please join the room before sending messages." #define XJ_DMSG_INF_JCONFEXIT "INFO: You have just left the conference." #define XJ_DMSG_ERR_JGWFORB "ERROR: Your message was not sent. You do not have permission to use the gateway." #define XJ_DMSG_ERR_NOJSRV "ERROR: Your message was not sent. Cannot connect to Jabber server." #define XJ_DMSG_ERR_JAUTH "ERROR: Your message was not sent. Authentication to Jabber server failed." #define XJ_DMSG_ERR_JGWFULL "ERROR: Your message was not sent. SIP-2-JABBER gateway is full." #define XJ_DMSG_ERR_JOINJCONF "ERROR: Cannot join the conference room." #define XJ_DMSG_ERR_NEWJCONF "ERROR:Cannot create a new conference session." #define XJ_DMSG_ERR_SENDJMSG "ERROR: Your message was not sent. Something wrong during transmitting to Jabber network." #define XJ_DMSG_ERR_STOREJMSG "ERROR: Your message was not sent. Something wrong while trying to transmit it to Jabber network." #define XJ_DMSG_ERR_NOREGIM "ERROR: Your message was not sent. You are not registered with this IM gateway." #define XJ_DMSG_ERR_DISCONNECTED "ERROR: Connection to Jabber server lost. You have to login to Jabber server again (join again the conferences that you were participating, too)." #define XJ_DMSG_INF_JOFFLINE "INFO: Your are now offline in Jabber network. Thank you for using SIP-Jabber gateway." #define XJ_DMSG_ERR_JCONFNICK "ERROR: Your nickname already exists in the conference room. Please choose a new one." #define XJ_DMSG_ERR_JCONFREFUSED "ERROR: Your participation to the conference room was refused." #endif opensips-2.2.2/modules/jabber/xjab_jcon.c000066400000000000000000000400621300170765700203630ustar00rootroot00000000000000/* * eXtended JABber module - functions used for SIP 2 JABBER communication * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../timer.h" #include "../../resolve.h" #include "xjab_jcon.h" #include "xjab_util.h" #include "xode.h" #include "mdefines.h" #define JB_ID_BASE "SJ" #define JB_START_STREAM "" #define JB_START_STREAM_LEN 21 #define JB_CLIENT_OPEN_STREAM "" #define JB_IQ_ROSTER_GET "" #define XJ_MAX_JCONF 12 /** * init a JABBER connection */ xj_jcon xj_jcon_init(char *hostname, int port) { xj_jcon jbc = NULL; if(hostname==NULL || strlen(hostname)<=0) return NULL; jbc = (xj_jcon)_M_MALLOC(sizeof(struct _xj_jcon)); if(jbc == NULL) return NULL; jbc->sock=-1; jbc->port = port; jbc->juid = -1; jbc->seq_nr = 0; jbc->hostname = (char*)_M_MALLOC(strlen(hostname)+1); if(jbc->hostname == NULL) { _M_FREE(jbc); return NULL; } strcpy(jbc->hostname, hostname); jbc->allowed = jbc->ready = XJ_NET_NUL; jbc->jconf = NULL; jbc->nrjconf = 0; jbc->plist = xj_pres_list_init(); if(jbc->plist == NULL) { _M_FREE(jbc->hostname); _M_FREE(jbc); return NULL; } return jbc; } /** * connect to JABBER server */ int xj_jcon_connect(xj_jcon jbc) { struct sockaddr_in address; struct hostent *he; int sock; // open connection to server if((sock = socket(AF_INET, SOCK_STREAM, 0))<0) { LM_DBG("failed to create the socket\n"); return -1; } #ifdef XJ_EXTRA_DEBUG LM_DBG("socket [%d]\n", sock); #endif he=resolvehost(jbc->hostname,0); if(he == NULL) { LM_DBG("failed to get info about Jabber server address\n"); return -1; } // fill the fields of the address memcpy(&address.sin_addr, he->h_addr, he->h_length); address.sin_family=AF_INET; address.sin_port=htons(jbc->port); // try to connect with Jabber server if (connect(sock, (struct sockaddr *)&address, sizeof(address))<0) { LM_DBG("failed to connect with Jabber server\n"); return -1; } jbc->sock = sock; return 0; } /** * set the Jabber internal ID */ void xj_jcon_set_juid(xj_jcon jbc, int _juid) { if(jbc == NULL) return; jbc->juid = _juid; } /** * return the Jabber internal ID */ int xj_jcon_get_juid(xj_jcon jbc) { if(jbc == NULL) return -1; return jbc->juid; } /** * disconnect from JABBER server */ int xj_jcon_disconnect(xj_jcon jbc) { if(jbc == NULL || jbc->sock < 0) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); LM_DBG("socket [%d]\n", jbc->sock); #endif xj_jcon_send_presence(jbc, NULL, "unavailable", NULL, NULL); if(send(jbc->sock, "", 16, 0) < 16) LM_DBG("failed to close the stream\n"); if(close(jbc->sock) == -1) LM_DBG("failed to close the socket\n"); jbc->sock = -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----END-----\n"); #endif return 0; } /** * authentication to the JABBER server */ int xj_jcon_user_auth(xj_jcon jbc, char *username, char *passwd, char *resource) { char msg_buff[4096]; int n, i, err; char *p0, *p1; xode x, y, z; /*** send open stream tag **/ sprintf(msg_buff, JB_CLIENT_OPEN_STREAM, jbc->hostname); if(send(jbc->sock, msg_buff, strlen(msg_buff), 0) != strlen(msg_buff)) goto error; n = recv(jbc->sock, msg_buff, 4096, 0); msg_buff[n] = 0; if(strncasecmp(msg_buff, JB_START_STREAM, JB_START_STREAM_LEN)) goto error; p0 = strstr(msg_buff + JB_START_STREAM_LEN, "id='"); if(p0 == NULL) goto error; p0 += 4; p1 = strchr(p0, '\''); if(p1 == NULL) goto error; jbc->stream_id = (char*)_M_MALLOC(p1-p0+1); strncpy(jbc->stream_id, p0, p1-p0); jbc->stream_id[p1-p0] = 0; sprintf(msg_buff, "%08X", jbc->seq_nr); x = xode_new_tag("iq"); if(!x) return -1; xode_put_attrib(x, "id", msg_buff); xode_put_attrib(x, "type", "get"); y = xode_insert_tag(x, "query"); xode_put_attrib(y, "xmlns", "jabber:iq:auth"); z = xode_insert_tag(y, "username"); xode_insert_cdata(z, username, -1); p0 = xode_to_str(x); n = strlen(p0); i = send(jbc->sock, p0, n, 0); if(i != n) goto errorx; xode_free(x); // receive response // try 10 times i = 10; while(i) { if((n = recv(jbc->sock, msg_buff, 4096, 0)) > 0) { msg_buff[n] = 0; break; } usleep(1000); i--; } if(!i) goto error; x = xode_from_strx(msg_buff, n, &err, &i); p0 = msg_buff; if(err) p0 += i; if(strncasecmp(xode_get_name(x), "iq", 2)) goto errorx; if((x = xode_get_tag(x, "query?xmlns=jabber:iq:auth")) == NULL) goto errorx; y = xode_new_tag("query"); xode_put_attrib(y, "xmlns", "jabber:iq:auth"); z = xode_insert_tag(y, "username"); xode_insert_cdata(z, username, -1); z = xode_insert_tag(y, "resource"); xode_insert_cdata(z, resource, -1); if(xode_get_tag(x, "digest") != NULL) { // digest authentication //sprintf(msg_buff, "%s%s", jbc->stream_id, passwd); strcpy(msg_buff, jbc->stream_id); strcat(msg_buff, passwd); //LM_DBG("[%s:%s]\n", jbc->stream_id, passwd); p1 = shahash(msg_buff); z = xode_insert_tag(y, "digest"); xode_insert_cdata(z, p1, -1); } else { // plaint text authentication z = xode_insert_tag(y, "password"); xode_insert_cdata(z, passwd, -1); } y = xode_wrap(y, "iq"); jbc->seq_nr++; sprintf(msg_buff, "%08X", jbc->seq_nr); xode_put_attrib(y, "id", msg_buff); xode_put_attrib(y, "type", "set"); p1 = xode_to_str(y); n = strlen(p1); i = send(jbc->sock, p1, n, 0); if(i != n) { xode_free(y); goto errorx; } xode_free(x); xode_free(y); // receive response // try 10 times i = 10; while(i) { if((n = recv(jbc->sock, msg_buff, 4096, 0)) > 0) { msg_buff[n] = 0; break; } usleep(1000); i--; } if(!i) goto error; x = xode_from_strx(msg_buff, n, &err, &i); p0 = msg_buff; if(err) p0 += i; if(strncasecmp(xode_get_name(x), "iq", 2) || strncasecmp(xode_get_attrib(x, "type"), "result", 6)) goto errorx; jbc->resource = (char*)_M_MALLOC(strlen(resource)+1); strcpy(jbc->resource, resource); jbc->allowed = XJ_NET_ALL; jbc->ready = XJ_NET_JAB; return 0; errorx: xode_free(x); error: return -1; } /** * receive the list of the roster */ int xj_jcon_get_roster(xj_jcon jbc) { int n = strlen(JB_IQ_ROSTER_GET); if(send(jbc->sock, JB_IQ_ROSTER_GET, n, 0) != n) return -1; return 0; } /** * add a new contact in user's roster */ int xj_jcon_set_roster(xj_jcon jbc, char* jid, char *type) { xode x; char *p; int n; char buff[16]; if(!jbc || !jid) return -1; x = xode_new_tag("item"); if(!x) return -1; xode_put_attrib(x, "jid", jid); if(type != NULL) xode_put_attrib(x, "subscription", type); x = xode_wrap(x, "query"); xode_put_attrib(x, "xmlns", "jabber:iq:roster"); x = xode_wrap(x, "iq"); xode_put_attrib(x, "type", "set"); jbc->seq_nr++; sprintf(buff, "%08X", jbc->seq_nr); xode_put_attrib(x, "id", buff); p = xode_to_str(x); n = strlen(p); if(send(jbc->sock, p, n, 0) != n) { LM_DBG("item not sent\n"); goto error; } xode_free(x); return 0; error: xode_free(x); return -1; } /** * send a message through a JABBER connection * params are pairs (buffer, len) */ int xj_jcon_send_msg(xj_jcon jbc, char *to, int tol, char *msg, int msgl, int type) { char msg_buff[4096], *p; int n; xode x; if(jbc == NULL) return -1; x = xode_new_tag("body"); if(!x) return -1; xode_insert_cdata(x, msg, msgl); x = xode_wrap(x, "message"); strncpy(msg_buff, to, tol); msg_buff[tol] = 0; xode_put_attrib(x, "to", msg_buff); switch(type) { case XJ_JMSG_CHAT: xode_put_attrib(x, "type", "chat"); break; case XJ_JMSG_GROUPCHAT: xode_put_attrib(x, "type", "groupchat"); break; default: xode_put_attrib(x, "type", "normal"); } p = xode_to_str(x); n = strlen(p); #ifdef XJ_EXTRA_DEBUG LM_DBG("jabber msg:\n%s\n", p); #endif if(send(jbc->sock, p, n, 0) != n) { LM_DBG(" message not sent\n"); goto error; } xode_free(x); return 0; error: xode_free(x); return -1; } /** * receive a message from a JABBER connection * NOT USED */ int xj_jcon_recv_msg(xj_jcon jbc, char *from, char *msg) { //char msg_buff[4096]; //sprintf(msg_buff, JB_MSG_NORMAL, to, msg); //send(jbc->sock, msg_buff, strlen(msg_buff), 0); return 0; } /** * send presence * type - "unavailable", "subscribe", "subscribed" .... * status - "online", "away", "unavailable" ... * priority - "0", "1", ... */ int xj_jcon_send_presence(xj_jcon jbc, char *sto, char *type, char *status, char *priority) { char *p; int n; xode x, y; if(jbc == NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); #endif x = xode_new_tag("presence"); if(!x) return -1; if(sto != NULL) xode_put_attrib(x, "to", sto); if(type != NULL) xode_put_attrib(x, "type", type); if(status != NULL) { y = xode_insert_tag(x, "status"); xode_insert_cdata(y, status, strlen(status)); } if(priority != NULL) { y = xode_insert_tag(x, "priority"); xode_insert_cdata(y, priority, strlen(priority)); } p = xode_to_str(x); n = strlen(p); if(send(jbc->sock, p, n, 0) != n) { LM_DBG("presence not sent\n"); goto error; } xode_free(x); #ifdef XJ_EXTRA_DEBUG LM_DBG("presence status was sent\n"); #endif return 0; error: xode_free(x); return -1; } /** * send subscribe for user's presence */ int xj_jcon_send_subscribe(xj_jcon jbc, char *to, char *from, char *type) { char *p; int n; xode x; if(!jbc || !to) return -1; x = xode_new_tag("presence"); if(!x) return -1; xode_put_attrib(x, "to", to); if(from != NULL) xode_put_attrib(x, "from", from); if(type != NULL) xode_put_attrib(x, "type", type); p = xode_to_str(x); n = strlen(p); if(send(jbc->sock, p, n, 0) != n) { LM_DBG("subscribe not sent\n"); goto error; } xode_free(x); return 0; error: xode_free(x); return -1; } /** * free the allocated memory space of a JABBER connection */ int xj_jcon_free(xj_jcon jbc) { xj_jconf jcf; if(jbc == NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); #endif //if(jbc->sock != -1) // jb_disconnect(jbc); if(jbc->hostname != NULL) _M_FREE(jbc->hostname); if(jbc->stream_id != NULL) _M_FREE(jbc->stream_id); if(jbc->resource != NULL) _M_FREE(jbc->resource); #ifdef XJ_EXTRA_DEBUG LM_DBG("%d conferences\n", jbc->nrjconf); #endif while(jbc->nrjconf > 0) { if((jcf=delpos234(jbc->jconf,0))!=NULL) xj_jconf_free(jcf); jbc->nrjconf--; } xj_pres_list_free(jbc->plist); _M_FREE(jbc); #ifdef XJ_EXTRA_DEBUG LM_DBG("-----END-----\n"); #endif return 0; } /** * create a open connection to Jabber * - id : id of the connection * - jbc : pointer to Jabber connection * - cache_time : life time of the connection * - delay_time : time needed to became an active connection * return : pointer to the structure or NULL on error */ int xj_jcon_set_attrs(xj_jcon jbc, xj_jkey jkey, int cache_time, int delay_time) { int t; if(jbc==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; jbc->jkey = jkey; t = get_ticks(); jbc->expire = t + cache_time; jbc->ready = t + delay_time; return 0; } /** * update the life time of the connection * - ojc : pointer to the open connection * - cache_time : number of seconds to keep the connection open * return : 0 on success or <0 on error */ int xj_jcon_update(xj_jcon jbc, int cache_time) { if(jbc == NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("params [%.*s] %d\n", jbc->jkey->id->len, jbc->jkey->id->s, cache_time); #endif jbc->expire = get_ticks() + cache_time; return 0; } int xj_jcon_is_ready(xj_jcon jbc, char *to, int tol, char dl) { char *p; str sto; xj_jconf jcf = NULL; if(!jbc || !to || tol <= 0) return -1; sto.s = to; sto.len = tol; if(!xj_jconf_check_addr(&sto, dl)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("destination=conference\n"); #endif if((jcf=xj_jcon_get_jconf(jbc, &sto, dl))!=NULL) return (jcf->status & XJ_JCONF_READY)?0:3; #ifdef XJ_EXTRA_DEBUG LM_DBG("conference does not exist\n"); #endif return -1; } p = to; while(p < to+tol && *p!='@') p++; if(p>=to+tol) return -1; p++; if(!strncasecmp(p, XJ_AIM_NAME, XJ_AIM_LEN)) return (jbc->ready & XJ_NET_AIM)?0:((jbc->allowed & XJ_NET_AIM)?1:2); if(!strncasecmp(p, XJ_ICQ_NAME, XJ_ICQ_LEN)) return (jbc->ready & XJ_NET_ICQ)?0:((jbc->allowed & XJ_NET_ICQ)?1:2); if(!strncasecmp(p, XJ_MSN_NAME, XJ_MSN_LEN)) return (jbc->ready & XJ_NET_MSN)?0:((jbc->allowed & XJ_NET_MSN)?1:2); if(!strncasecmp(p, XJ_YAH_NAME, XJ_YAH_LEN)) return (jbc->ready & XJ_NET_YAH)?0:((jbc->allowed & XJ_NET_YAH)?1:2); #ifdef XJ_EXTRA_DEBUG LM_DBG("destination=jabber\n"); #endif return 0; } xj_jconf xj_jcon_get_jconf(xj_jcon jbc, str* sid, char dl) { xj_jconf jcf = NULL, p; if(!jbc || !sid || !sid->s || sid->len <= 0) return NULL; #ifdef XJ_EXTRA_DEBUG LM_DBG("looking for conference\n"); #endif if((jcf = xj_jconf_new(sid))==NULL) return NULL; if(xj_jconf_init_sip(jcf, jbc->jkey->id, dl)) goto clean; if(jbc->nrjconf && (p = find234(jbc->jconf, (void*)jcf, NULL)) != NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("conference found\n"); #endif xj_jconf_free(jcf); return p; } if(jbc->nrjconf >= XJ_MAX_JCONF) goto clean; if(jbc->nrjconf==0) if(jbc->jconf==NULL) if((jbc->jconf = newtree234(xj_jconf_cmp)) == NULL) goto clean; if((p = add234(jbc->jconf, (void*)jcf)) != NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("new conference created\n"); #endif jbc->nrjconf++; return p; } clean: LM_DBG("conference not found\n"); xj_jconf_free(jcf); return NULL; } xj_jconf xj_jcon_check_jconf(xj_jcon jbc, char* id) { str sid; xj_jconf jcf = NULL, p = NULL; if(!jbc || !id || !jbc->nrjconf) return NULL; #ifdef XJ_EXTRA_DEBUG LM_DBG("conference not found\n"); #endif sid.s = id; sid.len = strlen(id); if((jcf = xj_jconf_new(&sid))==NULL) return NULL; if(xj_jconf_init_jab(jcf)) goto clean; if((p = find234(jbc->jconf, (void*)jcf, NULL)) != NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("conference found\n"); #endif xj_jconf_free(jcf); return p; } clean: #ifdef XJ_EXTRA_DEBUG LM_DBG("conference not found\n"); #endif xj_jconf_free(jcf); return NULL; } int xj_jcon_jconf_presence(xj_jcon jbc, xj_jconf jcf, char* type, char* status) { char buff[256]; strncpy(buff, jcf->room.s, jcf->room.len + jcf->server.len +1); buff[jcf->room.len + jcf->server.len +1] = '/'; buff[jcf->room.len + jcf->server.len +2] = 0; buff[jcf->room.len] = '@'; strncat(buff, jcf->nick.s, jcf->nick.len); return xj_jcon_send_presence(jbc,buff,type,status,NULL); } int xj_jcon_del_jconf(xj_jcon jbc, str *sid, char dl, int flag) { xj_jconf jcf = NULL, p = NULL; if(!jbc || !sid || !sid->s || sid->len <= 0) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("deleting conference of <%.*s>\n", sid->len, sid->s); #endif if((jcf = xj_jconf_new(sid))==NULL) return -1; if(xj_jconf_init_sip(jcf, jbc->jkey->id, dl)) { xj_jconf_free(jcf); return -1; } p = del234(jbc->jconf, (void*)jcf); if(p != NULL) { if(flag == XJ_JCMD_UNSUBSCRIBE) xj_jcon_jconf_presence(jbc, jcf, "unavailable", NULL); jbc->nrjconf--; xj_jconf_free(p); #ifdef XJ_EXTRA_DEBUG LM_DBG("conference deleted\n"); #endif } xj_jconf_free(jcf); return 0; } /********** *********/ opensips-2.2.2/modules/jabber/xjab_jcon.h000066400000000000000000000062311300170765700203700ustar00rootroot00000000000000/* * eXtended JABber module - headers for functions used for JABBER srv connection * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _XJAB_JCON_H_ #define _XJAB_JCON_H_ #include "../../str.h" #include "xjab_jconf.h" #include "xjab_base.h" #include "tree234.h" #include "xjab_presence.h" #define XJ_NET_NUL 0 #define XJ_NET_ALL 0xFFFFFFFF #define XJ_NET_JAB 1 #define XJ_NET_AIM 2 #define XJ_NET_ICQ 4 #define XJ_NET_MSN 8 #define XJ_NET_YAH 16 #define XJ_AIM_NAME "aim." #define XJ_AIM_LEN 4 #define XJ_ICQ_NAME "icq" #define XJ_ICQ_LEN 3 #define XJ_MSN_NAME "msn." #define XJ_MSN_LEN 4 #define XJ_YAH_NAME "yahoo." #define XJ_YAH_LEN 6 #define XJ_JMSG_NORMAL 1 #define XJ_JMSG_CHAT 2 #define XJ_JMSG_GROUPCHAT 4 #define XJ_JCMD_SUBSCRIBE 1 #define XJ_JCMD_UNSUBSCRIBE 2 typedef struct _xj_jcon { int sock; // communication socket int port; // port of the server int juid; // internal id of the Jabber user int seq_nr; // sequence number char *hostname; // hostname of the Jabber server char *stream_id; // stream id of the session char *resource; // resource ID xj_jkey jkey; // id of connection int expire; // time when the open connection is expired int allowed; // allowed IM networks int ready; // time when the connection is ready for sending messages int nrjconf; // number of open conferences tree234 *jconf; // open conferences xj_pres_list plist; // presence list } t_xj_jcon, *xj_jcon; /** --- **/ xj_jcon xj_jcon_init(char*, int); int xj_jcon_free(xj_jcon); int xj_jcon_connect(xj_jcon); int xj_jcon_disconnect(xj_jcon); void xj_jcon_set_juid(xj_jcon, int); int xj_jcon_get_juid(xj_jcon); int xj_jcon_get_roster(xj_jcon); int xj_jcon_set_roster(xj_jcon, char*, char*); int xj_jcon_user_auth(xj_jcon, char*, char*, char*); int xj_jcon_send_presence(xj_jcon, char*, char*, char*, char*); int xj_jcon_send_subscribe(xj_jcon, char*, char*, char*); int xj_jcon_send_msg(xj_jcon, char*, int, char*, int, int); int xj_jcon_send_sig_msg(xj_jcon, char*, int, char*, int, char*, int); int xj_jcon_is_ready(xj_jcon, char *, int, char); xj_jconf xj_jcon_get_jconf(xj_jcon, str*, char); xj_jconf xj_jcon_check_jconf(xj_jcon, char*); int xj_jcon_del_jconf(xj_jcon, str*, char, int); int xj_jcon_jconf_presence(xj_jcon, xj_jconf, char*, char*); /********** ***/ int xj_jcon_set_attrs(xj_jcon, xj_jkey, int, int); int xj_jcon_update(xj_jcon, int); /********** ***/ #endif opensips-2.2.2/modules/jabber/xjab_jconf.c000066400000000000000000000123221300170765700205270ustar00rootroot00000000000000/* * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "xjab_jconf.h" #include "xjab_base.h" /** * */ xj_jconf xj_jconf_new(str *u) { xj_jconf jcf = NULL; if(!u || !u->s || u->len<=0) return NULL; jcf = (xj_jconf)pkg_malloc(sizeof(t_xj_jconf)); if(jcf == NULL) { LM_DBG("no pkg memory.\n"); return NULL; } jcf->uri.s = (char*)pkg_malloc((u->len+1)*sizeof(char)); if(jcf->uri.s == NULL) { LM_DBG("no pkg memory!\n"); pkg_free(jcf); return NULL; } strncpy(jcf->uri.s, u->s, u->len); jcf->uri.len = u->len; jcf->uri.s[jcf->uri.len] = 0; jcf->jcid = 0; jcf->status = XJ_JCONF_NULL; jcf->room.s = NULL; jcf->room.len = 0; jcf->server.s = NULL; jcf->server.len = 0; jcf->nick.s = NULL; jcf->nick.len = 0; return jcf; } /** * */ int xj_jconf_init_sip(xj_jconf jcf, str *sid, char dl) { char *p, *p0; int n = 0; if(!jcf || !jcf->uri.s || jcf->uri.len <= 0 || !sid || !sid->s || sid->len <= 0) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("parsing uri\n"); #endif p = jcf->uri.s; while(p<(jcf->uri.s + jcf->uri.len) && *p != '@') p++; if(*p != '@') goto bad_format; p0 = p; while(p0 > jcf->uri.s) { p0--; if(*p0 == dl) { switch(n) { case 0: jcf->server.s = p0+1; jcf->server.len = p - jcf->server.s; break; case 1: jcf->room.s = p0+1; jcf->room.len = p - jcf->room.s; break; case 2: jcf->nick.s = p0+1; jcf->nick.len = p - jcf->nick.s; break; } n++; p = p0; } } if(n != 2 || p0 != jcf->uri.s) goto bad_format; if(p0 == jcf->uri.s && *p0 != dl) { jcf->nick.s = p0; jcf->nick.len = p - jcf->nick.s; } else { jcf->nick.s = p = sid->s; while(p < sid->s + sid->len && *p!='@') { if(*p == ':') jcf->nick.s = p+1; p++; } jcf->nick.len = p - jcf->nick.s; } jcf->jcid = xj_get_hash(&jcf->room, &jcf->server); #ifdef XJ_EXTRA_DEBUG LM_DBG("conference id=%d\n", jcf->jcid); #endif return 0; bad_format: LM_ERR("failed to parse uri - bad format\n"); return -2; } /** * */ int xj_jconf_init_jab(xj_jconf jcf) { char *p, *p0; if(!jcf || !jcf->uri.s || jcf->uri.len <= 0) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("parsing uri\n"); #endif p = jcf->uri.s; while(p<(jcf->uri.s + jcf->uri.len) && *p != '@') p++; if(*p != '@' || p==jcf->uri.s) goto bad_format; p0 = p+1; while(p0 < ((jcf->uri.s + jcf->uri.len)) && *p0 != '/') p0++; jcf->server.s = p+1; jcf->server.len = p0 - jcf->server.s; jcf->room.s = jcf->uri.s; jcf->room.len = p - jcf->room.s; if(p0 < jcf->uri.s + jcf->uri.len) { jcf->nick.s = p0+1; jcf->nick.len = jcf->uri.s + jcf->uri.len - jcf->nick.s; } jcf->jcid = xj_get_hash(&jcf->room, &jcf->server); #ifdef XJ_EXTRA_DEBUG LM_DBG("conference id=%d\n", jcf->jcid); #endif return 0; bad_format: LM_ERR("failed to parse uri - bad format\n"); return -2; } /** * */ int xj_jconf_set_status(xj_jconf jcf, int s) { if(!jcf || !jcf->uri.s || jcf->uri.len <= 0) return -1; jcf->status = s; return 0; } /** * */ int xj_jconf_cmp(void *a, void *b) { int n; if(a == NULL) return -1; if(b == NULL) return 1; // LM_DBG("comparing <%.*s> / <%.*s>\n",((str *)a)->len, // ((str *)a)->s, ((str *)b)->len, ((str *)b)->s); if(((xj_jconf)a)->jcid < ((xj_jconf)b)->jcid) return -1; if(((xj_jconf)a)->jcid > ((xj_jconf)b)->jcid) return 1; if(((xj_jconf)a)->room.len < ((xj_jconf)b)->room.len) return -1; if(((xj_jconf)a)->room.len > ((xj_jconf)b)->room.len) return 1; if(((xj_jconf)a)->server.len < ((xj_jconf)b)->server.len) return -1; if(((xj_jconf)a)->server.len > ((xj_jconf)b)->server.len) return 1; n = strncmp(((xj_jconf)a)->room.s, ((xj_jconf)b)->room.s, ((xj_jconf)a)->room.len); if(n<0) return -1; if(n>0) return 1; n = strncmp(((xj_jconf)a)->server.s, ((xj_jconf)b)->server.s, ((xj_jconf)a)->server.len); if(n<0) return -1; if(n>0) return 1; return 0; } /** * */ int xj_jconf_free(xj_jconf jcf) { if(!jcf) return 0; if(jcf->uri.s != NULL) pkg_free(jcf->uri.s); pkg_free(jcf); jcf = NULL; return 0; } /** * */ int xj_jconf_check_addr(str *addr, char dl) { char *p; int i; if(!addr || !addr->s || addr->len <= 0) return -1; p = addr->s; i= 0; while((p < addr->s+addr->len) && *p != '@') { if(*p==dl) i++; p++; } if(i==2 && *p=='@') return 0; return -1; } opensips-2.2.2/modules/jabber/xjab_jconf.h000066400000000000000000000027051300170765700205400ustar00rootroot00000000000000/* * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _XJAB_JCONF_H_ #define _XJAB_JCONF_H_ #include "../../str.h" #define XJ_JCONF_NULL 0 #define XJ_JCONF_READY 1 #define XJ_JCONF_WAITING 2 #define XJ_JCONF_AUTH 4 /********** ***/ typedef struct _xj_jconf { int jcid; int status; str uri; str room; str server; str nick; str passwd; } t_xj_jconf, *xj_jconf; xj_jconf xj_jconf_new(str *u); int xj_jconf_init_sip(xj_jconf jcf, str *sid, char dl); int xj_jconf_init_jab(xj_jconf jcf); int xj_jconf_set_status(xj_jconf jcf, int s); int xj_jconf_cmp(void *a, void *b); int xj_jconf_free(xj_jconf jcf); int xj_jconf_check_addr(str *addr, char dl); #endif opensips-2.2.2/modules/jabber/xjab_load.c000066400000000000000000000023561300170765700203550ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xjab_load.h" int load_xjab(struct xjab_binds *xjb) { if(!( xjb->register_watcher=(pa_register_watcher_f) find_export("jab_register_watcher", XJ_NO_SCRIPT_F, 0)) ) { LM_ERR("'jab_register_watcher' not found!\n"); return -1; } if(!( xjb->unregister_watcher=(pa_unregister_watcher_f) find_export("jab_unregister_watcher", XJ_NO_SCRIPT_F, 0)) ) { LM_ERR("'jab_unregister_watcher' not found!\n"); return -1; } return 1; } opensips-2.2.2/modules/jabber/xjab_load.h000066400000000000000000000025731300170765700203630ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _XJAB_LOAD_H_ #define _XJAB_LOAD_H_ #include "../../sr_module.h" #include "../../str.h" #include "xjab_base.h" #define XJ_NO_SCRIPT_F 1 typedef int (*pa_register_watcher_f)(str*, str *, void*, void*); typedef int (*pa_unregister_watcher_f)(str*, str *, void*, void*); struct xjab_binds { pa_register_watcher_f register_watcher; pa_unregister_watcher_f unregister_watcher; }; typedef int(*load_xjab_f)(struct xjab_binds*); int load_xjab(struct xjab_binds*); void xj_register_watcher(str*, str *, void*, void*); void xj_unregister_watcher(str*, str *, void*, void*); #endif opensips-2.2.2/modules/jabber/xjab_presence.c000066400000000000000000000117031300170765700212360ustar00rootroot00000000000000/* * XJAB module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "xjab_presence.h" /** * create a presence cell */ xj_pres_cell xj_pres_cell_new(void) { xj_pres_cell prc = NULL; prc = (xj_pres_cell)pkg_malloc(sizeof(t_xj_pres_cell)); if(prc == NULL) return NULL; prc->key = 0; prc->userid.s = NULL; prc->userid.len = 0; prc->state = XJ_PS_OFFLINE; prc->status = XJ_PRES_STATUS_NULL; prc->cbf = NULL; prc->cbp = NULL; prc->prev = NULL; prc->next = NULL; return prc; } /** * free the presence cell */ void xj_pres_cell_free(xj_pres_cell prc) { if(!prc) return; if(prc->userid.s) pkg_free(prc->userid.s); pkg_free(prc); prc = NULL; } /** * free all presence cell linked to */ void xj_pres_cell_free_all(xj_pres_cell prc) { xj_pres_cell p, p0; if(!prc) return; p = prc; while(p) { p0 = p->next; xj_pres_cell_free(p); p = p0; } } /** * init a presence cell */ int xj_pres_cell_init(xj_pres_cell prc, str* uid, pa_callback_f f, void* p) { if(!prc || !uid || !uid->s || uid->len<=0) return -1; prc->userid.s = (char*)pkg_malloc(uid->len*sizeof(char)); if(prc->userid.s == NULL) return -1; strncpy(prc->userid.s, uid->s, uid->len); prc->userid.len = uid->len; prc->key = xj_get_hash(uid, NULL); prc->cbf = f; prc->cbp = p; return 0; } /** * update attributes for a presence cell */ int xj_pres_cell_update(xj_pres_cell prc, pa_callback_f f, void *p) { if(!prc) return -1; prc->cbf = f; prc->cbp = p; return 0; } /** * init a presence list */ xj_pres_list xj_pres_list_init(void) { xj_pres_list prl = NULL; prl = (xj_pres_list)pkg_malloc(sizeof(t_xj_pres_list)); if(!prl) return NULL; prl->nr = 0; prl->clist = NULL; return prl; } /** * free the presence list */ void xj_pres_list_free(xj_pres_list prl) { if(!prl) return; xj_pres_cell_free_all(prl->clist); pkg_free(prl); prl = NULL; } /** * add, if does not exist, an user in present list */ xj_pres_cell xj_pres_list_add(xj_pres_list prl, xj_pres_cell prc) { xj_pres_cell p, p0; if(!prc) return NULL; if(!prl) { xj_pres_cell_free(prc); return NULL; } // presence list empty if(prl->clist==NULL) { prl->nr++; prl->clist = prc; return prc; } p0 = p = prl->clist; while(p && p->key <= prc->key) { if(p->key == prc->key && p->userid.len == prc->userid.len && !strncasecmp(p->userid.s, prc->userid.s, p->userid.len)) { // cell already exist // update cbf and cbp p->cbf = prc->cbf; p->cbp = prc->cbp; xj_pres_cell_free(prc); return p; } p0 = p; p = p->next; } // add a the cell in list prc->next = p0->next; prc->prev = p0; if(p0->next) p0->next->prev = prc; p0->next = prc; prl->nr++; return prc; } /** * delete a user from presence list */ int xj_pres_list_del(xj_pres_list prl, str *uid) { xj_pres_cell p; int lkey; if(!prl || !uid || !uid->s || uid->len<=0) return -1; if(prl->nr<=0 || prl->clist==NULL) return 0; lkey = xj_get_hash(uid, NULL); p = prl->clist; while(p && p->key <= lkey) { if(p->key == lkey && p->userid.len == uid->len && !strncasecmp(p->userid.s, uid->s, uid->len)) { prl->nr--; if(p->next) p->next->prev = p->prev; if(p->prev == NULL) prl->clist = p->next; else p->prev->next = p->next; xj_pres_cell_free(p); return 0; } p = p->next; } return 0; } /** * Check if a user is already in presence list */ xj_pres_cell xj_pres_list_check(xj_pres_list prl, str* uid) { xj_pres_cell p; int lkey; if(!prl || !uid || !uid->s || uid->len<=0 || prl->nr<=0 || prl->clist==NULL) return NULL; lkey = xj_get_hash(uid, NULL); p = prl->clist; while(p && p->key <= lkey) { if(p->key == lkey && p->userid.len == uid->len && !strncasecmp(p->userid.s, uid->s, uid->len)) return p; p = p->next; } return NULL; } /** * Notify all users from list */ void xj_pres_list_notifyall(xj_pres_list prl, int s) { xj_pres_cell p; if(!prl || prl->nr<=0 || prl->clist==NULL) return; p = prl->clist; while(p) { if(p->cbf) (*(p->cbf))(&(p->userid),&(p->userid), (s==XJ_PS_CHECK)?p->state:s, p->cbp); p = p->next; } } opensips-2.2.2/modules/jabber/xjab_presence.h000066400000000000000000000036731300170765700212520ustar00rootroot00000000000000/* * XJAB module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _XJAB_PRESENCE_H_ #define _XJAB_PRESENCE_H_ #include "../../str.h" #include "xjab_base.h" #define XJ_PS_CHECK -1 #define XJ_PS_OFFLINE 0 #define XJ_PS_ONLINE 1 #define XJ_PS_TERMINATED 2 #define XJ_PS_REFUSED 3 #define XJ_PRES_STATUS_NULL 0 #define XJ_PRES_STATUS_SUBS 1 #define XJ_PRES_STATUS_WAIT 2 typedef struct _xj_pres_cell { int key; str userid; int state; int status; pa_callback_f cbf; void *cbp; struct _xj_pres_cell *prev; struct _xj_pres_cell *next; } t_xj_pres_cell, *xj_pres_cell; typedef struct _xj_pres_list { int nr; xj_pres_cell clist; } t_xj_pres_list, *xj_pres_list; xj_pres_cell xj_pres_cell_new(void); void xj_pres_cell_free(xj_pres_cell); void xj_pres_cell_free_all(xj_pres_cell); int xj_pres_cell_init(xj_pres_cell, str*, pa_callback_f, void*); int xj_pres_cell_update(xj_pres_cell, pa_callback_f, void*); xj_pres_list xj_pres_list_init(void); void xj_pres_list_free(xj_pres_list); xj_pres_cell xj_pres_list_add(xj_pres_list, xj_pres_cell); int xj_pres_list_del(xj_pres_list, str*); xj_pres_cell xj_pres_list_check(xj_pres_list, str*); void xj_pres_list_notifyall(xj_pres_list,int); #endif opensips-2.2.2/modules/jabber/xjab_util.c000066400000000000000000000140401300170765700204040ustar00rootroot00000000000000/* * eXtended JABber module - Jabber connections pool * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../timer.h" #include "xjab_util.h" #include "xjab_jcon.h" #include "mdefines.h" /** * init a jc_pool structure * - size : maximum number of the open connection to Jabber * - jlen : maximum size of messages queue * return : pointer to the structure or NULL on error */ xj_jcon_pool xj_jcon_pool_init(int size, int jlen, int ch) { xj_jcon_pool jcp = (xj_jcon_pool)_M_MALLOC(sizeof(t_xj_jcon_pool)); if(jcp == NULL) return NULL; jcp->len = size; jcp->ojc = (xj_jcon*)_M_MALLOC(size*sizeof(xj_jcon)); if(jcp->ojc == NULL) { _M_FREE(jcp); return NULL; } memset( jcp->ojc , 0, size*sizeof(xj_jcon) ); jcp->jmqueue.len = jlen; jcp->jmqueue.size = 0; jcp->jmqueue.cache = (ch>0)?ch:90; jcp->jmqueue.expire = (int*)_M_MALLOC(jlen*sizeof(int)); if(jcp->jmqueue.expire == NULL) { _M_FREE(jcp->ojc); _M_FREE(jcp); return NULL; } jcp->jmqueue.jsm=(xj_sipmsg*)_M_MALLOC(jlen*sizeof(xj_sipmsg)); if(jcp->jmqueue.jsm == NULL) { _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp->ojc); _M_FREE(jcp); return NULL; } jcp->jmqueue.ojc = (xj_jcon*)_M_MALLOC(jlen*sizeof(xj_jcon)); if(jcp->jmqueue.ojc == NULL) { _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp->jmqueue.jsm); _M_FREE(jcp->ojc); _M_FREE(jcp); return NULL; } memset( jcp->jmqueue.expire , 0, jlen*sizeof(int) ); memset( jcp->jmqueue.jsm , 0, jlen*sizeof(xj_sipmsg) ); memset( jcp->jmqueue.ojc , 0, jlen*sizeof(xj_jcon) ); return jcp; } /** * add a new element in messages queue * - jcp : pointer to the Jabber connections pool structure * - _jsm : pointer to the message * - _ojc : pointer to the Jabber connection that will be used for this message * return : 0 on success or <0 on error */ int xj_jcon_pool_add_jmsg(xj_jcon_pool jcp, xj_sipmsg _jsm, xj_jcon _ojc) { int i; if(jcp == NULL) return -1; if(jcp->jmqueue.size == jcp->jmqueue.len) return -2; #ifdef XJ_EXTRA_DEBUG LM_DBG("add msg into the pool\n"); #endif for(i = 0; ijmqueue.len; i++) { if(jcp->jmqueue.jsm[i] == NULL || jcp->jmqueue.ojc[i] == NULL) { jcp->jmqueue.size++; jcp->jmqueue.expire[i] = get_ticks() + jcp->jmqueue.cache; jcp->jmqueue.jsm[i] = _jsm; jcp->jmqueue.ojc[i] = _ojc; return 0; } } return -2; } /** * delete first element from messages queue * - jcp : pointer to the Jabber connections pool structure * return : 0 on success or <0 on error */ int xj_jcon_pool_del_jmsg(xj_jcon_pool jcp, int idx) { if(jcp == NULL) return -1; if(jcp->jmqueue.size <= 0) return -2; jcp->jmqueue.size--; jcp->jmqueue.jsm[idx] = NULL; jcp->jmqueue.ojc[idx] = NULL; return 0; } /** * add a new connection in pool * - jcp : pointer to the Jabber connections pool structure * return : 0 on success or <0 on error */ int xj_jcon_pool_add(xj_jcon_pool jcp, xj_jcon jc) { int i = 0; if(jcp == NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("add connection into the pool\n"); #endif while(i < jcp->len && jcp->ojc[i] != NULL) i++; if(i >= jcp->len) return -1; jcp->ojc[i] = jc; return 0; } /** * get the jabber connection associated with 'id' * - jcp : pointer to the Jabber connections pool structure * - id : id of the Jabber connection * return : pointer to the open connection to Jabber structure or NULL on error */ xj_jcon xj_jcon_pool_get(xj_jcon_pool jcp, xj_jkey jkey) { int i = 0; xj_jcon _ojc; if(jcp==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return NULL; #ifdef XJ_EXTRA_DEBUG LM_DBG("looking for the connection of <%.*s>" " into the pool\n", jkey->id->len, jkey->id->s); #endif while(i < jcp->len) { if((jcp->ojc[i]!=NULL) && jcp->ojc[i]->jkey->hash==jkey->hash && (!strncmp(jcp->ojc[i]->jkey->id->s, jkey->id->s, jkey->id->len))) { _ojc = jcp->ojc[i]; //jcp->ojc[i] = NULL; return _ojc; } i++; } return NULL; } /** * remove the connection associated with 'id' from pool * - jcp : pointer to the Jabber connections pool structure * - id : id of the Jabber connection * return : 0 on success or <0 on error */ int xj_jcon_pool_del(xj_jcon_pool jcp, xj_jkey jkey) { int i = 0; if(jcp==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("removing a connection from the pool\n"); #endif while(i < jcp->len) { if((jcp->ojc[i]!=NULL) && jcp->ojc[i]->jkey->hash==jkey->hash && (!strncmp(jcp->ojc[i]->jkey->id->s,jkey->id->s,jkey->id->len))) { xj_jcon_free(jcp->ojc[i]); jcp->ojc[i] = NULL; break; } i++; } return 0; } /** * free a Jabber connections pool structure * - jcp : pointer to the Jabber connections pool structure */ void xj_jcon_pool_free(xj_jcon_pool jcp) { int i; if(jcp == NULL) return; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); #endif if(jcp->ojc != NULL) { for(i=0; ilen; i++) { if(jcp->ojc[i] != NULL) xj_jcon_free(jcp->ojc[i]); } _M_FREE(jcp->ojc); } if(jcp->jmqueue.jsm != NULL) _M_FREE(jcp->jmqueue.jsm); if(jcp->jmqueue.ojc != NULL) _M_FREE(jcp->jmqueue.ojc); if(jcp->jmqueue.expire != NULL) _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp); } opensips-2.2.2/modules/jabber/xjab_util.h000066400000000000000000000040171300170765700204140ustar00rootroot00000000000000/* * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _XJAB_UTIL_H_ #define _XJAB_UTIL_H_ #include "xjab_base.h" #include "xjab_jcon.h" #include "../../str.h" /********** ***/ typedef struct _xj_jmsg_queue { int len; // maximum size of the queue int size; // number of elements in the queue int cache; // cache time (seconds) int *expire; // expire time of the queued message xj_sipmsg *jsm; // pointer to the message xj_jcon *ojc; // pointer to the connection which will be used on sending } t_xj_jmsg_queue, *xj_jmsg_queue; /********** ***/ typedef struct _xj_jcon_pool { int len; // maximum len of the pool xj_jcon *ojc; // the connections to the Jabber t_xj_jmsg_queue jmqueue; // messages queue } t_xj_jcon_pool, *xj_jcon_pool; /********** LOOK AT IMPLEMENTATION OF FUNCTIONS FOR DESCRIPTION ***/ xj_jcon_pool xj_jcon_pool_init(int, int, int); int xj_jcon_pool_add(xj_jcon_pool, xj_jcon); xj_jcon xj_jcon_pool_get(xj_jcon_pool, xj_jkey); int xj_jcon_pool_del(xj_jcon_pool, xj_jkey); void xj_jccon_pool_free(xj_jcon_pool); void xj_jcon_pool_print(xj_jcon_pool); int xj_jcon_pool_add_jmsg(xj_jcon_pool, xj_sipmsg, xj_jcon); int xj_jcon_pool_del_jmsg(xj_jcon_pool, int); /********** ***/ #endif opensips-2.2.2/modules/jabber/xjab_wlist.c000066400000000000000000000347761300170765700206130ustar00rootroot00000000000000/* * eXtended JABber module - worker implementation * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --- * * History * ------- * 2003-02-24 added 'xj_wlist_set_flag' function (dcm) * 2003-03-11 major locking changes - uses locking.h (andrei) * */ #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "xjab_worker.h" #include "mdefines.h" #define XJ_DEF_JDELIM '*' /** * init a workers list * - pipes : communication pipes * - size : size of list - number of workers * - max : maximum number of jobs per worker * return : pointer to workers list or NULL on error */ xj_wlist xj_wlist_init(int **pipes, int size, int max, int cache_time, int sleep_time, int delay_time) { int i; xj_wlist jwl = NULL; if(pipes == NULL || size <= 0 || max <= 0) return NULL; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); #endif jwl = (xj_wlist)_M_SHM_MALLOC(sizeof(t_xj_wlist)); if(jwl == NULL) return NULL; jwl->len = size; jwl->maxj = max; jwl->cachet = cache_time; jwl->delayt = delay_time; jwl->sleept = sleep_time; jwl->aliases = NULL; jwl->sems = NULL; i = 0; /* alloc locks*/ if((jwl->sems = lock_set_alloc(size)) == NULL){ LM_CRIT("failed to alloc lock set\n"); goto clean; }; /* init the locks*/ if (lock_set_init(jwl->sems)==0){ LM_CRIT("failed to initialize the locks\n"); goto clean; }; jwl->workers = (xj_worker)_M_SHM_MALLOC(size*sizeof(t_xj_worker)); if(jwl->workers == NULL){ lock_set_destroy(jwl->sems); goto clean; } for(i = 0; i < size; i++) { jwl->workers[i].nr = 0; jwl->workers[i].pid = 0; jwl->workers[i].wpipe = pipes[i][1]; jwl->workers[i].rpipe = pipes[i][0]; if((jwl->workers[i].sip_ids = newtree234(xj_jkey_cmp)) == NULL){ lock_set_destroy(jwl->sems); goto clean; } } return jwl; clean: LM_DBG("error occurred -> cleaning\n"); if(jwl->sems != NULL) lock_set_dealloc(jwl->sems); if(jwl->workers != NULL) { while(i>=0) { if(jwl->workers[i].sip_ids == NULL) free2tree234(jwl->workers[i].sip_ids, xj_jkey_free_p); i--; } _M_SHM_FREE(jwl->workers); } _M_SHM_FREE(jwl); return NULL; } /** * set the p.id's of the workers * - jwl : pointer to the workers list * - pids : p.id's array * - size : number of pids * return : 0 on success or <0 on error */ int xj_wlist_set_pid(xj_wlist jwl, int pid, int idx) { if(jwl == NULL || pid <= 0 || idx < 0 || idx >= jwl->len) return -1; lock_set_get(jwl->sems, idx); jwl->workers[idx].pid = pid; lock_set_release(jwl->sems, idx); return 0; } /** * free jab_wlist * - jwl : pointer to the workers list */ void xj_wlist_free(xj_wlist jwl) { int i; #ifdef XJ_EXTRA_DEBUG LM_DBG("freeing 'xj_wlist' memory ...\n"); #endif if(jwl == NULL) return; if(jwl->workers != NULL) { for(i=0; ilen; i++) free2tree234(jwl->workers[i].sip_ids, xj_jkey_free_p); _M_SHM_FREE(jwl->workers); } if(jwl->aliases != NULL) { if(jwl->aliases->d) _M_SHM_FREE(jwl->aliases->d); if(jwl->aliases->jdm != NULL) { _M_SHM_FREE(jwl->aliases->jdm->s); _M_SHM_FREE(jwl->aliases->jdm); } if(jwl->aliases->proxy != NULL) { _M_SHM_FREE(jwl->aliases->proxy->s); _M_SHM_FREE(jwl->aliases->proxy); } if(jwl->aliases->size > 0) { for(i=0; ialiases->size; i++) _M_SHM_FREE(jwl->aliases->a[i].s); _M_SHM_FREE(jwl->aliases->a); } _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; } if(jwl->sems != NULL){ lock_set_destroy(jwl->sems); lock_set_dealloc(jwl->sems); } _M_SHM_FREE(jwl); } /** * return communication pipe with the worker that will process the message for * the id 'sid' only if it exists, or -1 if error * - jwl : pointer to the workers list * - sid : id of the entity (connection to Jabber - usually SHOULD be FROM * header of the incoming SIP message) * - p : will point to the SHM location of the 'sid' in jwl */ int xj_wlist_check(xj_wlist jwl, xj_jkey jkey, xj_jkey *p) { int i; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; i = 0; *p = NULL; while(i < jwl->len) { lock_set_get(jwl->sems, i); if(jwl->workers[i].pid <= 0) { lock_set_release(jwl->sems, i); i++; continue; } if((*p = find234(jwl->workers[i].sip_ids, (void*)jkey, NULL)) != NULL) { lock_set_release(jwl->sems, i); #ifdef XJ_EXTRA_DEBUG LM_DBG("entry exists for <%.*s> in the" " pool of <%d> [%d]\n",jkey->id->len, jkey->id->s, jwl->workers[i].pid,i); #endif return jwl->workers[i].wpipe; } lock_set_release(jwl->sems, i); i++; } #ifdef XJ_EXTRA_DEBUG LM_DBG("entry does not exist for <%.*s>\n", jkey->id->len, jkey->id->s); #endif return -1; } /** * return communication pipe with the worker that will process the message for * the id 'sid', or -1 if error * - jwl : pointer to the workers list * - sid : id of the entity (connection to Jabber - usually SHOULD be FROM * header of the incoming SIP message) * - p : will point to the SHM location of the 'sid' in jwl */ int xj_wlist_get(xj_wlist jwl, xj_jkey jkey, xj_jkey *p) { int i = 0, pos = -1, min = 100000; xj_jkey msid = NULL; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; *p = NULL; while(i < jwl->len) { lock_set_get(jwl->sems, i); if(jwl->workers[i].pid <= 0) { lock_set_release(jwl->sems, i); i++; continue; } if((*p = find234(jwl->workers[i].sip_ids, (void*)jkey, NULL))!=NULL) { if(pos >= 0) lock_set_release(jwl->sems, pos); lock_set_release(jwl->sems, i); #ifdef XJ_EXTRA_DEBUG LM_DBG("entry already exists for <%.*s> in the" " pool of <%d> [%d]\n",jkey->id->len, jkey->id->s, jwl->workers[i].pid,i); #endif return jwl->workers[i].wpipe; } if(min > jwl->workers[i].nr) { if(pos >= 0) lock_set_release(jwl->sems, pos); pos = i; min = jwl->workers[i].nr; } else lock_set_release(jwl->sems, i); i++; } if(pos >= 0 && jwl->workers[pos].nr < jwl->maxj) { jwl->workers[pos].nr++; msid = (xj_jkey)_M_SHM_MALLOC(sizeof(t_xj_jkey)); if(msid == NULL) goto error; msid->id = (str*)_M_SHM_MALLOC(sizeof(str)); if(msid->id == NULL) { _M_SHM_FREE(msid); goto error; } msid->id->s = (char*)_M_SHM_MALLOC(jkey->id->len); if(msid->id == NULL) { _M_SHM_FREE(msid->id); _M_SHM_FREE(msid); goto error; } if((*p = add234(jwl->workers[pos].sip_ids, msid)) != NULL) { msid->id->len = jkey->id->len; memcpy(msid->id->s, jkey->id->s, jkey->id->len); msid->hash = jkey->hash; msid->flag = XJ_FLAG_OPEN; lock_set_release(jwl->sems, pos); #ifdef XJ_EXTRA_DEBUG LM_DBG("new entry for <%.*s> in the pool of" " <%d> - [%d]\n", jkey->id->len, jkey->id->s, jwl->workers[pos].pid, pos); #endif return jwl->workers[pos].wpipe; } _M_SHM_FREE(msid->id->s); _M_SHM_FREE(msid->id); _M_SHM_FREE(msid); } error: if(pos >= 0) lock_set_release(jwl->sems, pos); LM_DBG("cannot create a new entry for <%.*s>\n", jkey->id->len, jkey->id->s); return -1; } /** * set the flag of the connection identified by 'jkey' * */ int xj_wlist_set_flag(xj_wlist jwl, xj_jkey jkey, int fl) { int i; xj_jkey p = NULL; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("looking for <%.*s>" " having id=%d\n", jkey->id->len, jkey->id->s, jkey->hash); #endif i = 0; while(i < jwl->len) { lock_set_get(jwl->sems, i); if(jwl->workers[i].pid <= 0) { lock_set_release(jwl->sems, i); i++; continue; } if((p=find234(jwl->workers[i].sip_ids, (void*)jkey, NULL)) != NULL) { p->flag = fl; lock_set_release(jwl->sems, i); #ifdef XJ_EXTRA_DEBUG LM_DBG("the connection for <%.*s>" " marked with flag=%d", jkey->id->len, jkey->id->s, fl); #endif return jwl->workers[i].wpipe; } lock_set_release(jwl->sems, i); i++; } #ifdef XJ_EXTRA_DEBUG LM_DBG("entry does not exist for <%.*s>\n", jkey->id->len, jkey->id->s); #endif return -1; } /** * set IM aliases, jdomain and outbound proxy * * return 0 if OK */ int xj_wlist_set_aliases(xj_wlist jwl, char *als, char *jd, char *pa) { char *p, *p0, *p1; int i, n; if(jwl == NULL) return -1; if(!jd) // || !als || strlen(als)<2) return 0; if((jwl->aliases = (xj_jalias)_M_SHM_MALLOC(sizeof(t_xj_jalias)))==NULL) { LM_DBG("not enough SHMemory.\n"); return -1; } jwl->aliases->jdm = NULL; jwl->aliases->proxy = NULL; jwl->aliases->dlm = XJ_DEF_JDELIM; // default user part delimiter jwl->aliases->size = 0; jwl->aliases->a = NULL; jwl->aliases->d = NULL; // set the jdomain if(jd != NULL && (n=strlen(jd))>2) { p = jd; while(p < jd+n && *p!='=') p++; if(paliases->dlm = *(p+1); n = p - jd; } if((jwl->aliases->jdm = (str*)_M_SHM_MALLOC(sizeof(str)))== NULL) { LM_DBG("not enough SHMemory!?\n"); _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; return -1; } jwl->aliases->jdm->len = n; if((jwl->aliases->jdm->s=(char*)_M_SHM_MALLOC(jwl->aliases->jdm->len)) == NULL) { LM_DBG("not enough SHMemory!?!\n"); _M_SHM_FREE(jwl->aliases->jdm); _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; } strncpy(jwl->aliases->jdm->s, jd, jwl->aliases->jdm->len); #ifdef XJ_EXTRA_DEBUG LM_DBG("jdomain=%.*s delim=%c\n", jwl->aliases->jdm->len, jwl->aliases->jdm->s, jwl->aliases->dlm); #endif } // set the proxy address if(pa && strlen(pa)>0) { if((jwl->aliases->proxy = (str*)_M_SHM_MALLOC(sizeof(str)))==NULL) { LM_DBG(" not enough SHMemory!!\n"); goto clean3; } i = jwl->aliases->proxy->len = strlen(pa); // check if proxy address has sip: prefix if(i < 4 || pa[0]!='s' || pa[1]!='i' || pa[2]!='p' || pa[3]!=':') jwl->aliases->proxy->len += 4; if((jwl->aliases->proxy->s= (char*)_M_SHM_MALLOC(jwl->aliases->proxy->len)) == NULL) { LM_DBG("not enough SHMemory!!!\n"); _M_SHM_FREE(jwl->aliases->proxy); goto clean3; } p0 = jwl->aliases->proxy->s; if(jwl->aliases->proxy->len != i) { strncpy(p0, "sip:", 4); p0 += 4; } strncpy(p0, pa, i); #ifdef XJ_EXTRA_DEBUG LM_DBG("outbound proxy=[%.*s]\n", jwl->aliases->proxy->len, jwl->aliases->proxy->s); #endif } // set the IM aliases if(!als || strlen(als)<2) return 0; if((p = strchr(als, ';')) == NULL) { LM_DBG("bad parameter value\n"); return -1; } if((jwl->aliases->size = atoi(als)) <= 0) { LM_DBG("wrong number of aliases\n"); return 0; } jwl->aliases->d = (char*)_M_SHM_MALLOC(jwl->aliases->size*sizeof(char)); if(jwl->aliases->d == NULL) { LM_DBG("not enough SHMemory..\n"); goto clean2; } memset(jwl->aliases->d, 0, jwl->aliases->size); jwl->aliases->a = (str*)_M_SHM_MALLOC(jwl->aliases->size*sizeof(str)); if(jwl->aliases->a == NULL) { LM_DBG("not enough SHMemory..\n"); goto clean1; } p++; for(i=0; ialiases->size; i++) { if((p0 = strchr(p, ';'))==NULL) { LM_DBG("bad parameter value format\n"); goto clean; } n = p0 - p; p1 = strchr(p, '='); if(p1 && p1aliases->d[i] = *(p1+1); n = p1 - p; } jwl->aliases->a[i].len = n; if((jwl->aliases->a[i].s = (char*)_M_SHM_MALLOC(jwl->aliases->a[i].len)) == NULL) { LM_DBG("not enough SHMemory!\n"); goto clean; } strncpy(jwl->aliases->a[i].s, p, jwl->aliases->a[i].len); #ifdef XJ_EXTRA_DEBUG LM_DBG("alias[%d/%d]=%.*s delim=%c\n", i+1, jwl->aliases->size, jwl->aliases->a[i].len, jwl->aliases->a[i].s, jwl->aliases->d[i]?jwl->aliases->d[i]:'X'); #endif p = p0 + 1; } return 0; clean: while(i>0) { _M_SHM_FREE(jwl->aliases->a[i-1].s); i--; } _M_SHM_FREE(jwl->aliases->a); clean1: if(jwl->aliases->d) _M_SHM_FREE(jwl->aliases->d); clean2: if(jwl->aliases->proxy) { _M_SHM_FREE(jwl->aliases->proxy->s); _M_SHM_FREE(jwl->aliases->proxy); } clean3: if(jwl->aliases->jdm) { _M_SHM_FREE(jwl->aliases->jdm->s); _M_SHM_FREE(jwl->aliases->jdm); } _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; return -1; } /** * check if the addr contains jdomain or an alias * - jwl : pointer to the workers list * - addr: the address to check against jdomain and aliases * returns 0 - if contains or !=0 if not */ int xj_wlist_check_aliases(xj_wlist jwl, str *addr) { char *p, *p0; int ll, i; if(!jwl || !jwl->aliases || !addr || !addr->s || addr->len<=0) return -1; // find '@' p = addr->s; while(p < addr->s + addr->len && *p != '@') p++; if(p >= addr->s + addr->len) return -1; p++; ll = addr->s + addr->len - p; // check parameters p0 = p; while(p0 < p + ll && *p0 != ';') p0++; if(p0 < p + ll) ll = p0 - p; ll = addr->s + addr->len - p; if(jwl->aliases->jdm && jwl->aliases->jdm->len == ll && !strncasecmp(jwl->aliases->jdm->s, p, ll)) return 0; if(jwl->aliases->size <= 0) return 1; for(i = 0; i < jwl->aliases->size; i++) if(jwl->aliases->a[i].len == ll && !strncasecmp(p, jwl->aliases->a[i].s, ll)) return 0; return 1; } /** * delete an entity from working list of a worker * - jwl : pointer to the workers list * - sid : id of the entity (connection to Jabber - usually SHOULD be FROM * header of the incoming SIP message * - _pid : process id of the worker */ void xj_wlist_del(xj_wlist jwl, xj_jkey jkey, int _pid) { int i; void *p; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return; for(i=0; i < jwl->len; i++) if(jwl->workers[i].pid == _pid) break; if(i >= jwl->len) { LM_DBG("%d: key <%.*s> not found in [%d]...\n", _pid, jkey->id->len, jkey->id->s, i); return; } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: trying to delete entry for <%.*s>...\n", _pid, jkey->id->len, jkey->id->s); #endif lock_set_get(jwl->sems, i); p = del234(jwl->workers[i].sip_ids, (void*)jkey); if(p != NULL) { jwl->workers[i].nr--; #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: sip id <%.*s> deleted\n", _pid, jkey->id->len, jkey->id->s); #endif xj_jkey_free_p(p); } lock_set_release(jwl->sems, i); } opensips-2.2.2/modules/jabber/xjab_worker.c000066400000000000000000001060101300170765700207370ustar00rootroot00000000000000/* * eXtended JABber module - worker implementation * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2003-01-20 xj_worker_precess function cleaning - some part of it moved to * xj_worker_check_jcons function, (dcm) * 2003-02-28 send NOTIFYs even the connection is closed by user, (dcm) * 2003-03-11 major locking changes - uses locking.h, (andrei) * 2003-05-07 added new presence status - 'terminated' - when connection * with Jabber server is lost or closed, (dcm) * 2003-05-09 added new presence status - 'refused' - when the presence * subscription request is refused by target, (dcm) * 2003-05-09 new xj_worker_precess function cleaning - some part of it moved * to xj_worker_check_qmsg and xj_worker_check_watcher functions, * (dcm) * 2004-06-07 new DB api => xj_worker_process takes another parameter: dbf * (andrei) */ #include #include #include #include #include #include #include #include #include "../../dprint.h" #include "../../timer.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../tm/tm_load.h" #include "xjab_worker.h" #include "xjab_util.h" #include "xjab_jcon.h" #include "xjab_dmsg.h" #include "xode.h" #include "xjab_presence.h" #include "mdefines.h" #define XJAB_RESOURCE "serXjab" #define XJ_ADDRTR_NUL 0 #define XJ_ADDRTR_S2J 1 #define XJ_ADDRTR_J2S 2 #define XJ_ADDRTR_CON 4 #define XJ_MSG_POOL_SIZE 10 // proxy address #define _PADDR(a) ((a)->aliases->proxy) /** TM bind */ extern struct tm_binds tmb; /** debug info */ int _xj_pid = 0; int main_loop = 1; /** **/ extern char *registrar; static str jab_gw_name = {"jabber_gateway@127.0.0.1", 24}; /** * address correction * alias A~B: flag == 0 => A->B, otherwise B->A */ int xj_address_translation(str *src, str *dst, xj_jalias als, int flag) { char *p, *p0; int i, ll; if(!src || !dst || !src->s || !dst->s ) return -1; if(!als || !als->jdm || !als->jdm->s || als->jdm->len <= 0) goto done; dst->len = 0; #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - checking aliases\n", _xj_pid); #endif p = src->s; while(p<(src->s + src->len) && *p != '@') p++; if(*p != '@') goto done; p++; ll = src->s + src->len - p; #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - domain is [%.*s]\n",_xj_pid,ll,p); #endif /*** checking aliases */ if(als->size > 0) { for(i=0; isize; i++) if(als->a[i].len == ll && !strncasecmp(p, als->a[i].s, als->a[i].len)) { if(als->d[i]) { if(flag & XJ_ADDRTR_S2J) { strncpy(dst->s, src->s, src->len); p0 = dst->s; while(p0 < dst->s + (p-src->s)) { if(*p0 == als->dlm) *p0 = als->d[i]; p0++; } return 0; } if(flag & XJ_ADDRTR_J2S) { strncpy(dst->s, src->s, src->len); p0 = dst->s; while(p0 < dst->s + (p-src->s)) { if(*p0 == als->d[i]) *p0 = als->dlm; p0++; } return 0; } } goto done; } } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - doing address correction\n", _xj_pid); #endif if(flag & XJ_ADDRTR_S2J) { if(als->jdm->len != ll || strncasecmp(p, als->jdm->s, als->jdm->len)) { LM_DBG("%d: - wrong Jabber" " destination <%.*s>!\n", _xj_pid, src->len, src->s); return -1; } if(flag & XJ_ADDRTR_CON) { #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - that is for" " Jabber conference\n", _xj_pid); #endif p0 = p-1; while(p0 > src->s && *p0 != als->dlm) p0--; if(p0 <= src->s) return -1; p0--; while(p0 > src->s && *p0 != als->dlm) p0--; if(*p0 != als->dlm) return -1; dst->len = p - p0 - 2; strncpy(dst->s, p0+1, dst->len); dst->s[dst->len]=0; p = dst->s; while(p < (dst->s + dst->len) && *p!=als->dlm) p++; if(*p==als->dlm) *p = '@'; return 0; } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - that is for Jabber network\n", _xj_pid); #endif dst->len = p - src->s - 1; strncpy(dst->s, src->s, dst->len); dst->s[dst->len]=0; if((p = strchr(dst->s, als->dlm)) != NULL) *p = '@'; else { LM_DBG("%d: - wrong Jabber" " destination <%.*s>!!!\n", _xj_pid, src->len, src->s); return -1; } return 0; } if(flag & XJ_ADDRTR_J2S) { *(p-1) = als->dlm; p0 = src->s + src->len; while(p0 > p) { if(*p0 == '/') { src->len = p0 - src->s; *p0 = 0; } p0--; } strncpy(dst->s, src->s, src->len); dst->s[src->len] = '@'; dst->s[src->len+1] = 0; strncat(dst->s, als->jdm->s, als->jdm->len); dst->len = strlen(dst->s); return 0; } done: dst->s = src->s; dst->len = src->len; return 0; } /** * worker implementation * - jwl : pointer to the workers list * - jaddress : address of the jabber server * - jport : port of the jabber server * - rank : worker's rank * - db_con : connection to database * - priority: jabber's priority * dbf: database module callbacks structure * return : 0 on success or <0 on error */ int xj_worker_process(xj_wlist jwl, char* jaddress, int jport, char* priority, int rank, db_con_t* db_con, db_func_t* dbf) { int pipe, ret, i, pos, maxfd, flag; xj_jcon_pool jcp; struct timeval tmv; fd_set set, mset; xj_sipmsg jsmsg; str sto; xj_jcon jbc = NULL; xj_jconf jcf = NULL; char *p, buff[1024], recv_buff[4096]; int flags, nr, ltime = 0; static str tmp1 = str_init("sip_id"); static str tmp2 = str_init("type"); static str tmp3 = str_init("jab_id"); static str tmp4 = str_init("jab_passwd"); db_key_t keys[] = {&tmp1, &tmp2}; db_val_t vals[2]; db_key_t col[] = {&tmp3, &tmp4}; db_res_t* res = NULL; vals[0].type=DB_STRING; vals[0].nul=0; vals[0].val.string_val=buff; vals[1].type=DB_INT; vals[1].nul=0; vals[1].val.int_val=0; _xj_pid = getpid(); //signal(SIGTERM, xj_sig_handler); //signal(SIGINT, xj_sig_handler); //signal(SIGQUIT, xj_sig_handler); signal(SIGSEGV, xj_sig_handler); if(registrar) { jab_gw_name.s = registrar; jab_gw_name.len = strlen(registrar); if(registrar[0]== 's' && registrar[1]== 'i' && registrar[2]== 'p' && registrar[3]== ':') { jab_gw_name.s += 4; jab_gw_name.len -= 4; } } if(!jwl || !jwl->aliases || !jwl->aliases->jdm || !jaddress || rank >= jwl->len) { LM_DBG("[%d]:%d: exiting - wrong parameters\n", rank, _xj_pid); return -1; } pipe = jwl->workers[rank].rpipe; LM_DBG("[%d]:%d: started - pipe=<%d> : 1st message delay" " <%d>\n", rank, _xj_pid, pipe, jwl->delayt); if((jcp=xj_jcon_pool_init(jwl->maxj,XJ_MSG_POOL_SIZE,jwl->delayt))==NULL) { LM_DBG("cannot allocate the pool\n"); return -1; } maxfd = pipe; tmv.tv_sec = jwl->sleept; tmv.tv_usec = 0; FD_ZERO(&set); FD_SET(pipe, &set); while(main_loop) { mset = set; tmv.tv_sec = (jcp->jmqueue.size == 0)?jwl->sleept:1; #ifdef XJ_EXTRA_DEBUG //LM_DBG("XJAB:xj_worker[%d]:%d: select waiting %ds - queue=%d\n",rank, // _xj_pid, (int)tmv.tv_sec, jcp->jmqueue.size); #endif tmv.tv_usec = 0; ret = select(maxfd+1, &mset, NULL, NULL, &tmv); // check the msg queue xj_worker_check_qmsg(jwl, jcp); if(ret <= 0) goto step_x; #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: something is coming\n", _xj_pid); #endif if(!FD_ISSET(pipe, &mset)) goto step_y; if(read(pipe, &jsmsg, sizeof(jsmsg)) < (int)sizeof(jsmsg)) { LM_DBG("%d: BROKEN PIPE - exiting\n", _xj_pid); break; } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: job <%p> from SER\n", _xj_pid, jsmsg); #endif if(jsmsg == NULL || jsmsg->jkey==NULL || jsmsg->jkey->id==NULL) goto step_w; strncpy(buff, jsmsg->jkey->id->s, jsmsg->jkey->id->len); buff[jsmsg->jkey->id->len] = 0; jbc = xj_jcon_pool_get(jcp, jsmsg->jkey); switch(jsmsg->type) { case XJ_SEND_MESSAGE: if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm) && (!jbc||!xj_jcon_get_jconf(jbc,&jsmsg->to,jwl->aliases->dlm))) { xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOTJCONF, NULL); goto step_w; } break; case XJ_REG_WATCHER: case XJ_JOIN_JCONF: case XJ_GO_ONLINE: break; case XJ_EXIT_JCONF: if(jbc == NULL) goto step_w; // close the conference session here if(jbc->nrjconf <= 0) goto step_w; if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) xj_jcon_del_jconf(jbc, &jsmsg->to, jwl->aliases->dlm, XJ_JCMD_UNSUBSCRIBE); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_INF_JCONFEXIT, NULL); goto step_w; case XJ_GO_OFFLINE: if(jbc != NULL) jbc->expire = ltime = -1; goto step_w; case XJ_DEL_WATCHER: default: goto step_w; } if(jbc != NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: connection already exists" " for <%s> ...\n", _xj_pid, buff); #endif xj_jcon_update(jbc, jwl->cachet); goto step_z; } // NO OPEN CONNECTION FOR THIS SIP ID #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: new connection for <%s>.\n", _xj_pid, buff); #endif if(dbf->query(db_con, keys, 0, vals, col, 2, 2, NULL, &res) != 0 || RES_ROW_N(res) <= 0) { #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: no database result when looking" " for associated Jabber account\n", _xj_pid); #endif xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JGWFORB, NULL); goto step_v; } jbc = xj_jcon_init(jaddress, jport); if(xj_jcon_connect(jbc)) { LM_DBG("%d: Cannot connect" " to the Jabber server ...\n", _xj_pid); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOJSRV, NULL); goto step_v; } #ifdef XJ_EXTRA_DEBUG LM_DBG("auth to jabber as: [%s] / [xxx]\n", (char*)(ROW_VALUES(RES_ROWS(res))[0].val.string_val)); // (char*)(ROW_VALUES(RES_ROWS(res))[1].val.string_val)); #endif if(xj_jcon_user_auth(jbc, (char*)(ROW_VALUES(RES_ROWS(res))[0].val.string_val), (char*)(ROW_VALUES(RES_ROWS(res))[1].val.string_val), XJAB_RESOURCE) < 0) { LM_DBG("athentication to the Jabber server failed ...\n"); xj_jcon_disconnect(jbc); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JAUTH, NULL); xj_jcon_free(jbc); goto step_v; } if(xj_jcon_set_attrs(jbc, jsmsg->jkey, jwl->cachet, jwl->delayt) || xj_jcon_pool_add(jcp, jbc)) { LM_DBG("keeping connection to Jabber server" " failed! Not enough memory ...\n"); xj_jcon_disconnect(jbc); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JGWFULL, NULL); xj_jcon_free(jbc); goto step_v; } /** add socket descriptor to select */ #ifdef XJ_EXTRA_DEBUG LM_DBG("add connection on <%d> \n",jbc->sock); #endif if(jbc->sock > maxfd) maxfd = jbc->sock; FD_SET(jbc->sock, &set); xj_jcon_get_roster(jbc); xj_jcon_send_presence(jbc, NULL, NULL, "Online", priority); /** wait for a while - the worker is tired */ //sleep(3); if ((res != NULL) && (dbf->free_result(db_con,res) < 0)) { LM_DBG("failed to free SQL result - worker terminated\n"); return -1; } else res = NULL; step_z: if(jsmsg->type == XJ_GO_ONLINE) goto step_w; if(jsmsg->type == XJ_REG_WATCHER) { // update or register a presence watcher xj_worker_check_watcher(jwl, jcp, jbc, jsmsg); goto step_w; } flag = 0; if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) { if((jcf = xj_jcon_get_jconf(jbc, &jsmsg->to, jwl->aliases->dlm)) != NULL) { if((jsmsg->type == XJ_JOIN_JCONF) && !(jcf->status & XJ_JCONF_READY || jcf->status & XJ_JCONF_WAITING)) { if(!xj_jcon_jconf_presence(jbc,jcf,NULL,"online")) jcf->status = XJ_JCONF_WAITING; else { // unable to join the conference // --- send back to SIP user a msg xj_send_sip_msgz(_PADDR(jwl),jsmsg->jkey->id,&jsmsg->to, XJ_DMSG_ERR_JOINJCONF, &jbc->jkey->flag); goto step_w; } } flag |= XJ_ADDRTR_CON; } else { // unable to get the conference // --- send back to SIP user a msg xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NEWJCONF, &jbc->jkey->flag); goto step_w; } } if(jsmsg->type != XJ_SEND_MESSAGE) goto step_w; // here will come only XJ_SEND_MESSAGE switch(xj_jcon_is_ready(jbc,jsmsg->to.s,jsmsg->to.len,jwl->aliases->dlm)) { case 0: #ifdef XJ_EXTRA_DEBUG LM_DBG("sending the message to Jabber network ...\n"); #endif /*** address correction ***/ sto.s = buff; sto.len = 0; flag |= XJ_ADDRTR_S2J; if(xj_address_translation(&jsmsg->to, &sto, jwl->aliases, flag) == 0) { if(xj_jcon_send_msg(jbc, sto.s, sto.len, jsmsg->msg.s, jsmsg->msg.len, (flag&XJ_ADDRTR_CON)?XJ_JMSG_GROUPCHAT:XJ_JMSG_CHAT)<0) xj_send_sip_msgz(_PADDR(jwl),jsmsg->jkey->id,&jsmsg->to, XJ_DMSG_ERR_SENDJMSG, &jbc->jkey->flag); } else LM_ERR("sending as Jabber message ...\n"); goto step_w; case 1: #ifdef XJ_EXTRA_DEBUG LM_DBG("scheduling the message.\n"); #endif if(xj_jcon_pool_add_jmsg(jcp, jsmsg, jbc) < 0) { LM_DBG("scheduling the message FAILED." "Message was dropped.\n"); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_STOREJMSG, &jbc->jkey->flag); goto step_w; } else // skip freeing the SIP message - now is in queue goto step_y; case 2: xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOREGIM, &jbc->jkey->flag); goto step_w; case 3: // not joined to Jabber conference xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOTJCONF, &jbc->jkey->flag); goto step_w; default: xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_SENDJMSG, &jbc->jkey->flag); goto step_w; } step_v: // error connecting to Jabber server // cleaning jab_wlist xj_wlist_del(jwl, jsmsg->jkey, _xj_pid); // cleaning db_query if ((res != NULL) && (dbf->free_result(db_con,res) < 0)) { LM_DBG("failed to free the SQL result - worker terminated\n"); return -1; } else res = NULL; step_w: if(jsmsg!=NULL) { xj_sipmsg_free(jsmsg); jsmsg = NULL; } step_y: // check for new message from ... JABBER for(i = 0; i < jcp->len && main_loop; i++) { if(jcp->ojc[i] == NULL) continue; #ifdef XJ_EXTRA_DEBUG LM_DBG("checking socket <%d> ...\n", jcp->ojc[i]->sock); #endif if(!FD_ISSET(jcp->ojc[i]->sock, &mset)) continue; pos = nr = 0; do { p = recv_buff; if(pos != 0) { while(pos < nr) { *p = recv_buff[pos]; pos++; p++; } *p = 0; /** * flush out the socket - set it to nonblocking */ flags = fcntl(jcp->ojc[i]->sock, F_GETFL, 0); if(flags!=-1 && !(flags & O_NONBLOCK)) { /* set NONBLOCK bit to enable non-blocking */ fcntl(jcp->ojc[i]->sock, F_SETFL, flags|O_NONBLOCK); } } if((nr = read(jcp->ojc[i]->sock, p, sizeof(recv_buff)-(p-recv_buff))) == 0 ||(nr < 0 && errno != EAGAIN)) { LM_DBG("connection to jabber lost on socket <%d> ...\n", jcp->ojc[i]->sock); xj_send_sip_msgz(_PADDR(jwl), jcp->ojc[i]->jkey->id, &jab_gw_name,XJ_DMSG_ERR_DISCONNECTED,&jbc->jkey->flag); // make sure that will ckeck expired connections ltime = jcp->ojc[i]->expire = -1; FD_CLR(jcp->ojc[i]->sock, &set); goto step_xx; } #ifdef XJ_EXTRA_DEBUG LM_DBG("received: %dbytes Err:%d/EA:%d\n", nr, errno, EAGAIN); #endif xj_jcon_update(jcp->ojc[i], jwl->cachet); if(nr>0) p[nr] = 0; nr = strlen(recv_buff); pos = 0; #ifdef XJ_EXTRA_DEBUG LM_DBG("JMSG START ----------\n%.*s\n" " JABBER: JMSGL:%d END ----------\n", nr, recv_buff, nr); #endif } while(xj_manage_jab(recv_buff, nr, &pos, jwl->aliases, jcp->ojc[i]) == 9 && main_loop); /** * flush out the socket - set it back to blocking */ flags = fcntl(jcp->ojc[i]->sock, F_GETFL, 0); if(flags!=-1 && (flags & O_NONBLOCK)) { /* reset NONBLOCK bit to enable blocking */ fcntl(jcp->ojc[i]->sock, F_SETFL, flags & ~O_NONBLOCK); } #ifdef XJ_EXTRA_DEBUG LM_DBG("msgs from socket <%d> parsed ...\n", jcp->ojc[i]->sock); #endif } // end FOR(i = 0; i < jcp->len; i++) step_x: if(ret < 0) { LM_DBG("signal received!!!!!!!!\n"); maxfd = pipe; FD_ZERO(&set); FD_SET(pipe, &set); for(i = 0; i < jcp->len; i++) { if(jcp->ojc[i] != NULL) { FD_SET(jcp->ojc[i]->sock, &set); if( jcp->ojc[i]->sock > maxfd ) maxfd = jcp->ojc[i]->sock; } } } step_xx: if(ltime < 0 || ltime + jwl->sleept <= get_ticks()) { ltime = get_ticks(); #ifdef XJ_EXTRA_DEBUG //LM_DBG("XJAB:xj_worker:%d: scanning for expired connection\n", // _xj_pid); #endif xj_worker_check_jcons(jwl, jcp, ltime, &set); } } // END while LM_DBG("cleaning procedure\n"); return 0; } // end xj_worker_process /** * parse incoming message from Jabber server */ int xj_manage_jab(char *buf, int len, int *pos, xj_jalias als, xj_jcon jbc) { int j, err=0; char *p, *to, *from, *msg, *type, *emsg, *ecode, lbuf[4096], fbuf[128]; xj_jconf jcf = NULL; str ts, tf; xode x, y, z; str *sid; xj_pres_cell prc = NULL; if(!jbc) return -1; sid = jbc->jkey->id; x = xode_from_strx(buf, len, &err, &j); #ifdef XJ_EXTRA_DEBUG LM_DBG("xode ret:%d pos:%d\n", err, j); #endif if(err && pos != NULL) *pos= j; if(x == NULL) return -1; lbuf[0] = 0; ecode = NULL; /******************** XMPP 'MESSAGE' HANDLING **********************/ if(!strncasecmp(xode_get_name(x), "message", 7)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("jabber [message] received\n"); #endif if((to = xode_get_attrib(x, "to")) == NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("missing 'to' attribute\n"); #endif err = -1; goto ready; } if((from = xode_get_attrib(x, "from")) == NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("missing 'from' attribute\n"); #endif err = -1; goto ready; } if((y = xode_get_tag(x, "body")) == NULL || (msg = xode_get_data(y)) == NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("missing 'body' of message\n"); #endif err = -1; goto ready; } type = xode_get_attrib(x, "type"); if(type != NULL && !strncasecmp(type, "error", 5)) { if((y = xode_get_tag(x, "error")) == NULL || (emsg = xode_get_data(y)) == NULL) strcpy(lbuf, "{Error sending following message} - "); else { ecode = xode_get_attrib(y, "code"); strcpy(lbuf, "{Error ("); if(ecode != NULL) { strcat(lbuf, ecode); strcat(lbuf, " - "); } strcat(lbuf, emsg); strcat(lbuf, ") when trying to send following message}"); } } // is from a conference?!?! if((jcf=xj_jcon_check_jconf(jbc, from))!=NULL) { if(lbuf[0] == 0) { p = from + strlen(from); while(p>from && *p != '/') p--; if(*p == '/') { if(jcf->nick.len>0 && strlen(p+1) == jcf->nick.len && !strncasecmp(p+1, jcf->nick.s, jcf->nick.len)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("message sent by myself\n"); #endif goto ready; } lbuf[0] = '['; lbuf[1] = 0; strcat(lbuf, p+1); strcat(lbuf, "] "); } } else { jcf->status = XJ_JCONF_NULL; xj_jcon_jconf_presence(jbc,jcf,NULL,"online"); } strcat(lbuf, msg); ts.s = lbuf; ts.len = strlen(lbuf); if(xj_send_sip_msg(als->proxy, sid, &jcf->uri, &ts, &jbc->jkey->flag)<0) LM_ERR("sip message was not sent!\n"); #ifdef XJ_EXTRA_DEBUG else LM_DBG("sip message was sent!\n"); #endif goto ready; } strcat(lbuf, msg); ts.s = from; ts.len = strlen(from); tf.s = fbuf; tf.len = 0; if(xj_address_translation(&ts, &tf, als, XJ_ADDRTR_J2S) == 0) { ts.s = lbuf; ts.len = strlen(lbuf); if(xj_send_sip_msg(als->proxy, sid, &tf, &ts, &jbc->jkey->flag)<0) LM_ERR("sip message was not sent!\n"); #ifdef XJ_EXTRA_DEBUG else LM_DBG("sip message was sent!\n"); #endif } goto ready; } /*------------------- END 'MESSAGE' HANDLING ----------------------*/ /******************** XMPP 'PRESENCE' HANDLING *********************/ if(!strncasecmp(xode_get_name(x), "presence", 8)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("jabber [presence] received\n"); #endif type = xode_get_attrib(x, "type"); from = xode_get_attrib(x, "from"); if(from == NULL) goto ready; ts.s = from; p = from; while(pready |= XJ_NET_AIM; #ifdef XJ_EXTRA_DEBUG LM_DBG("AIM network ready\n"); #endif } else if(!strncasecmp(from, XJ_ICQ_NAME, XJ_ICQ_LEN)) { jbc->ready |= XJ_NET_ICQ; #ifdef XJ_EXTRA_DEBUG LM_DBG("ICQ network ready\n"); #endif } else if(!strncasecmp(from, XJ_MSN_NAME, XJ_MSN_LEN)) { jbc->ready |= XJ_NET_MSN; #ifdef XJ_EXTRA_DEBUG LM_DBG("MSN network ready\n"); #endif } else if(!strncasecmp(from, XJ_YAH_NAME, XJ_YAH_LEN)) { jbc->ready |= XJ_NET_YAH; #ifdef XJ_EXTRA_DEBUG LM_DBG("YAHOO network ready\n"); #endif } } else if((jcf=xj_jcon_check_jconf(jbc, from))!=NULL) { jcf->status = XJ_JCONF_READY; #ifdef XJ_EXTRA_DEBUG LM_DBG(" %s conference ready\n", from); #endif } else { #ifdef XJ_EXTRA_DEBUG LM_DBG("user <%.*s> is online\n",ts.len,ts.s); #endif prc = xj_pres_list_check(jbc->plist, &ts); if(prc) { if(prc->state != XJ_PS_ONLINE) { prc->state = XJ_PS_ONLINE; goto call_pa_cbf; } } else { #ifdef XJ_EXTRA_DEBUG LM_DBG("user state received - creating" " presence cell for [%.*s]\n", ts.len, ts.s); #endif prc = xj_pres_cell_new(); if(prc == NULL) { LM_DBG("cannot create presence" " cell for [%s]\n", from); goto ready; } if(xj_pres_cell_init(prc, &ts, NULL, NULL)<0) { LM_DBG("cannot init presence" " cell for [%s]\n", from); xj_pres_cell_free(prc); goto ready; } prc = xj_pres_list_add(jbc->plist, prc); if(prc) { prc->state = XJ_PS_ONLINE; goto call_pa_cbf; } } } goto ready; } if(strchr(from, '@') == NULL) goto ready; if(!strncasecmp(type, "error", 5)) { if((jcf=xj_jcon_check_jconf(jbc, from))!=NULL) { tf.s = from; tf.len = strlen(from); if((y = xode_get_tag(x, "error")) == NULL) goto ready; if ((p = xode_get_attrib(y, "code")) != NULL && atoi(p) == 409) { xj_send_sip_msgz(als->proxy, sid, &tf, XJ_DMSG_ERR_JCONFNICK, &jbc->jkey->flag); goto ready; } xj_send_sip_msgz(als->proxy,sid,&tf,XJ_DMSG_ERR_JCONFREFUSED, &jbc->jkey->flag); } goto ready; } if(type!=NULL && !strncasecmp(type, "subscribe", 9)) { xj_jcon_send_presence(jbc, from, "subscribed", NULL, NULL); goto ready; } prc = xj_pres_list_check(jbc->plist, &ts); if(!prc) goto ready; if(!strncasecmp(type, "unavailable", 11)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("user <%s> is offline\n", from); #endif if(prc->state != XJ_PS_OFFLINE) { prc->state = XJ_PS_OFFLINE; goto call_pa_cbf; } goto ready; } if(!strncasecmp(type, "unsubscribed", 12)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("user <%s> does not allow to see this" " presence status\n", from); #endif if(prc->state != XJ_PS_REFUSED) { prc->state = XJ_PS_REFUSED; goto call_pa_cbf; } } // ignoring unknown types goto ready; } /*------------------- END XMPP 'PRESENCE' HANDLING ----------------*/ /******************** XMPP 'IQ' HANDLING ***************************/ if(!strncasecmp(xode_get_name(x), "iq", 2)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("jabber [iq] received\n"); #endif if(!strncasecmp(xode_get_attrib(x, "type"), "result", 6)) { if((y = xode_get_tag(x, "query?xmlns=jabber:iq:roster")) == NULL) goto ready; z = xode_get_firstchild(y); while(z) { if(!strncasecmp(xode_get_name(z), "item", 5) && (from = xode_get_attrib(z, "jid")) != NULL) { if(strchr(from, '@') == NULL) { // transports if(!strncasecmp(from, XJ_AIM_NAME, XJ_AIM_LEN)) { jbc->allowed |= XJ_NET_AIM; #ifdef XJ_EXTRA_DEBUG LM_DBG("AIM network available\n"); #endif } else if(!strncasecmp(from, XJ_ICQ_NAME, XJ_ICQ_LEN)) { jbc->allowed |= XJ_NET_ICQ; #ifdef XJ_EXTRA_DEBUG LM_DBG("ICQ network available\n"); #endif } else if(!strncasecmp(from, XJ_MSN_NAME, XJ_MSN_LEN)) { jbc->allowed |= XJ_NET_MSN; #ifdef XJ_EXTRA_DEBUG LM_DBG("MSN network available\n"); #endif } else if(!strncasecmp(from, XJ_YAH_NAME, XJ_YAH_LEN)) { jbc->allowed |= XJ_NET_YAH; #ifdef XJ_EXTRA_DEBUG LM_DBG("YAHOO network available\n"); #endif } goto next_sibling; } } next_sibling: z = xode_get_nextsibling(z); } } goto ready; } /*------------------- END XMPP 'IQ' HANDLING ----------------------*/ call_pa_cbf: if(prc && prc->cbf) { // call the PA callback function tf.s = fbuf; tf.len = 0; if(xj_address_translation(&ts,&tf,als,XJ_ADDRTR_J2S)==0) { #ifdef XJ_EXTRA_DEBUG LM_DBG("calling CBF(%.*s,%d)\n", tf.len, tf.s, prc->state); #endif (*(prc->cbf))(&tf, &tf, prc->state, prc->cbp); } } ready: xode_free(x); return err; } /** * */ void xj_sig_handler(int s) { //signal(SIGTERM, xj_sig_handler); //signal(SIGINT, xj_sig_handler); //signal(SIGQUIT, xj_sig_handler); signal(SIGSEGV, xj_sig_handler); main_loop = 0; LM_DBG("%d: SIGNAL received=%d\n **************", _xj_pid, s); } /***************************** ****************************************/ /** * send a SIP MESSAGE message * - to : destination * - from : origin * - contact : contact header * - msg : body of the message * return : 0 on success or <0 on error */ int xj_send_sip_msg(str *proxy, str *to, str *from, str *msg, int *cbp) { str msg_type = { "MESSAGE", 7}; char buf[512]; str tfrom; str str_hdr; char buf1[1024]; if( !to || !to->s || to->len <= 0 || !from || !from->s || from->len <= 0 || !msg || !msg->s || msg->len <= 0 || (cbp && *cbp!=0) ) return -1; // from correction tfrom.len = 0; strncpy(buf+tfrom.len, "sip:", 4); tfrom.len += 4; strncpy(buf+tfrom.len, from->s, from->len); tfrom.len += from->len; tfrom.s = buf; // building Contact and Content-Type strcpy(buf1,"Content-Type: text/plain"CRLF"Contact: "); str_hdr.len = 24 + CRLF_LEN + 9; strncat(buf1,tfrom.s,tfrom.len); str_hdr.len += tfrom.len; strcat(buf1, CRLF); str_hdr.len += CRLF_LEN; str_hdr.s = buf1; if(cbp) { #ifdef XJ_EXTRA_DEBUG LM_DBG("uac callback parameter [%p==%d]\n", cbp, *cbp); #endif return tmb.t_request(&msg_type, 0, to, &tfrom, &str_hdr, msg, 0, xj_tuac_callback, (void*)cbp, 0); } else return tmb.t_request(&msg_type, 0, to, &tfrom, &str_hdr, msg, 0, 0, 0, 0); } /** * send a SIP MESSAGE message * - to : destination * - from : origin * - contact : contact header * - msg : body of the message, string terminated by zero * return : 0 on success or <0 on error */ int xj_send_sip_msgz(str *proxy, str *to, str *from, char *msg, int *cbp) { str tstr; int n; if(!to || !from || !msg || (cbp && *cbp!=0)) return -1; tstr.s = msg; tstr.len = strlen(msg); if((n = xj_send_sip_msg(proxy, to, from, &tstr, cbp)) < 0) LM_ERR("sip message wasn't sent to [%.*s]...\n", to->len, to->s); #ifdef XJ_EXTRA_DEBUG else LM_DBG("sip message was sent to [%.*s]...\n", to->len, to->s); #endif return n; } /** * send disconnected info to all SIP users associated with worker idx * and clean the entries from wlist */ int xj_wlist_clean_jobs(xj_wlist jwl, int idx, int fl) { xj_jkey p; if(jwl==NULL || idx < 0 || idx >= jwl->len || !jwl->workers[idx].sip_ids) return -1; lock_set_get(jwl->sems, idx); while((p=(xj_jkey)delpos234(jwl->workers[idx].sip_ids, 0))!=NULL) { if(fl) { #ifdef XJ_EXTRA_DEBUG LM_DBG("sending disconnect message" " to <%.*s>\n", p->id->len, p->id->s); #endif xj_send_sip_msgz(_PADDR(jwl), p->id, &jab_gw_name, XJ_DMSG_INF_DISCONNECTED, NULL); } jwl->workers[idx].nr--; xj_jkey_free_p(p); } lock_set_release(jwl->sems, idx); return 0; } /** * callback function for TM */ void xj_tuac_callback( struct cell *t, int type, struct tmcb_params *ps) { #ifdef XJ_EXTRA_DEBUG LM_DBG("completed with status %d\n", ps->code); #endif if(!ps->param) { LM_DBG("parameter not received\n"); return; } #ifdef XJ_EXTRA_DEBUG LM_DBG("parameter [%p : ex-value=%d]\n", ps->param,*((int*)ps->param) ); #endif if(ps->code < 200 || ps->code >= 300) { #ifdef XJ_EXTRA_DEBUG LM_DBG("no 2XX return code - connection set as expired \n"); #endif *((int*)ps->param) = XJ_FLAG_CLOSE; } } /** * check for expired connections */ void xj_worker_check_jcons(xj_wlist jwl, xj_jcon_pool jcp, int ltime, fd_set *pset) { int i; xj_jconf jcf; for(i = 0; i < jcp->len && main_loop; i++) { if(jcp->ojc[i] == NULL) continue; if(jcp->ojc[i]->jkey->flag==XJ_FLAG_OPEN && jcp->ojc[i]->expire > ltime) continue; #ifdef XJ_EXTRA_DEBUG LM_DBG("connection expired for <%.*s> \n", jcp->ojc[i]->jkey->id->len, jcp->ojc[i]->jkey->id->s); #endif xj_send_sip_msgz(_PADDR(jwl), jcp->ojc[i]->jkey->id, &jab_gw_name, XJ_DMSG_INF_JOFFLINE, NULL); #ifdef XJ_EXTRA_DEBUG LM_DBG("connection's close flag =%d\n", jcp->ojc[i]->jkey->flag); #endif // CLEAN JAB_WLIST xj_wlist_del(jwl, jcp->ojc[i]->jkey, _xj_pid); // looking for open conference rooms #ifdef XJ_EXTRA_DEBUG LM_DBG("having %d open conferences\n", jcp->ojc[i]->nrjconf); #endif while(jcp->ojc[i]->nrjconf > 0) { if((jcf=delpos234(jcp->ojc[i]->jconf,0))!=NULL) { // get out of room xj_jcon_jconf_presence(jcp->ojc[i],jcf, "unavailable", NULL); xj_jconf_free(jcf); } jcp->ojc[i]->nrjconf--; } // send offline presence to all subscribers if(jcp->ojc[i]->plist) { #ifdef XJ_EXTRA_DEBUG LM_DBG("sending 'terminated' status to SIP subscriber\n"); #endif xj_pres_list_notifyall(jcp->ojc[i]->plist, XJ_PS_TERMINATED); } FD_CLR(jcp->ojc[i]->sock, pset); xj_jcon_disconnect(jcp->ojc[i]); xj_jcon_free(jcp->ojc[i]); jcp->ojc[i] = NULL; } } /** * check if there are msg to send or delete from queue */ void xj_worker_check_qmsg(xj_wlist jwl, xj_jcon_pool jcp) { int i, flag; str sto; char buff[1024]; if(!jwl || !jcp) return; /** check the msg queue AND if the target connection is ready */ for(i = 0; ijmqueue.size && main_loop; i++) { if(jcp->jmqueue.jsm[i]==NULL || jcp->jmqueue.ojc[i]==NULL) { if(jcp->jmqueue.jsm[i]!=NULL) { xj_sipmsg_free(jcp->jmqueue.jsm[i]); jcp->jmqueue.jsm[i] = NULL; xj_jcon_pool_del_jmsg(jcp, i); } if(jcp->jmqueue.ojc[i]!=NULL) xj_jcon_pool_del_jmsg(jcp, i); continue; } if(jcp->jmqueue.expire[i] < get_ticks()) { #ifdef XJ_EXTRA_DEBUG LM_DBG("message to %.*s is expired\n", jcp->jmqueue.jsm[i]->to.len, jcp->jmqueue.jsm[i]->to.s); #endif xj_send_sip_msgz(_PADDR(jwl), jcp->jmqueue.jsm[i]->jkey->id, &jcp->jmqueue.jsm[i]->to, XJ_DMSG_ERR_SENDIM, &jcp->jmqueue.ojc[i]->jkey->flag); if(jcp->jmqueue.jsm[i]!=NULL) { xj_sipmsg_free(jcp->jmqueue.jsm[i]); jcp->jmqueue.jsm[i] = NULL; } /** delete message from queue */ xj_jcon_pool_del_jmsg(jcp, i); continue; } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: QUEUE: message[%d] from [%.*s]" "/to [%.*s]/body[%.*s] expires at %d\n", get_ticks(), i, jcp->jmqueue.jsm[i]->jkey->id->len, jcp->jmqueue.jsm[i]->jkey->id->s, jcp->jmqueue.jsm[i]->to.len,jcp->jmqueue.jsm[i]->to.s, jcp->jmqueue.jsm[i]->msg.len,jcp->jmqueue.jsm[i]->msg.s, jcp->jmqueue.expire[i]); #endif if(xj_jcon_is_ready(jcp->jmqueue.ojc[i], jcp->jmqueue.jsm[i]->to.s, jcp->jmqueue.jsm[i]->to.len, jwl->aliases->dlm)) continue; /*** address correction ***/ flag = XJ_ADDRTR_S2J; if(!xj_jconf_check_addr(&jcp->jmqueue.jsm[i]->to,jwl->aliases->dlm)) flag |= XJ_ADDRTR_CON; sto.s = buff; sto.len = 0; if(xj_address_translation(&jcp->jmqueue.jsm[i]->to, &sto, jwl->aliases, flag) == 0) { /** send message from queue */ #ifdef XJ_EXTRA_DEBUG LM_DBG("sending the message from" " local queue to Jabber network ...\n"); #endif xj_jcon_send_msg(jcp->jmqueue.ojc[i], sto.s, sto.len, jcp->jmqueue.jsm[i]->msg.s, jcp->jmqueue.jsm[i]->msg.len, (flag&XJ_ADDRTR_CON)?XJ_JMSG_GROUPCHAT:XJ_JMSG_CHAT); } else LM_ERR("sending the message from" " local queue to Jabber network ...\n"); if(jcp->jmqueue.jsm[i]!=NULL) { xj_sipmsg_free(jcp->jmqueue.jsm[i]); jcp->jmqueue.jsm[i] = NULL; } /** delete message from queue */ xj_jcon_pool_del_jmsg(jcp, i); } } /** * update or register a presence watcher */ void xj_worker_check_watcher(xj_wlist jwl, xj_jcon_pool jcp, xj_jcon jbc, xj_sipmsg jsmsg) { str sto; char buff[1024]; xj_pres_cell prc = NULL; if(!jwl || !jcp || !jbc || !jsmsg) return; if(!jsmsg->cbf) { #ifdef XJ_EXTRA_DEBUG LM_DBG("null PA callback function\n"); #endif return; } if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) { // is for a conference - ignore?!?! #ifdef XJ_EXTRA_DEBUG LM_DBG("presence request for a conference.\n"); #endif // set as offline (*(jsmsg->cbf))(&jsmsg->to, &jsmsg->to, XJ_PS_OFFLINE, jsmsg->p); return; } sto.s = buff; sto.len = 0; if(xj_address_translation(&jsmsg->to, &sto, jwl->aliases, XJ_ADDRTR_S2J) == 0) { prc = xj_pres_list_check(jbc->plist, &sto); if(!prc) { #ifdef XJ_EXTRA_DEBUG LM_DBG("new presence cell for %.*s.\n", sto.len, sto.s); #endif prc = xj_pres_cell_new(); if(!prc) { LM_DBG("cannot create a presence cell for %.*s.\n",sto.len, sto.s); return; } if(xj_pres_cell_init(prc, &sto, jsmsg->cbf, jsmsg->p)<0) { LM_DBG("cannot init the presence" " cell for %.*s.\n", sto.len, sto.s); xj_pres_cell_free(prc); return; } if((prc = xj_pres_list_add(jbc->plist, prc))==NULL) { LM_DBG("cannot add the presence" " cell for %.*s.\n", sto.len, sto.s); return; } sto.s[sto.len] = 0; if(!xj_jcon_send_subscribe(jbc, sto.s, NULL, "subscribe")) prc->status = XJ_PRES_STATUS_WAIT; } else { xj_pres_cell_update(prc, jsmsg->cbf, jsmsg->p); #ifdef XJ_EXTRA_DEBUG LM_DBG("calling CBF(%.*s,%d)\n", jsmsg->to.len, jsmsg->to.s, prc->state); #endif // send presence info to SIP subscriber (*(prc->cbf))(&jsmsg->to, &jsmsg->to, prc->state, prc->cbp); } } } /***************************** ****************************************/ opensips-2.2.2/modules/jabber/xjab_worker.h000066400000000000000000000063201300170765700207470ustar00rootroot00000000000000/* * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 major locking changes - uses locking.h (andrei) * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) */ #ifndef _XJAB_WORKER_H_ #define _XJAB_WORKER_H_ #include "../../str.h" #include "../../db/db.h" #include "../../locking.h" #include "../tm/tm_load.h" #include "xjab_util.h" #include "tree234.h" /********** ***/ typedef struct _xj_jalias { int size; // number of aliases str *jdm; // Jabber domain char dlm; // user part delimiter str *proxy; // outbound proxy str *a; // aliases char *d; // user part delimiter for aliases } t_xj_jalias, *xj_jalias; typedef struct _xj_worker { int pid; // process id int wpipe; // communication pipe - write int rpipe; // communication pipe - read int nr; // number of jobs tree234 *sip_ids; // sip ids allocated for the worker } t_xj_worker, *xj_worker; typedef struct _xj_wlist { int len; // length of the list int maxj; // maximum jobs / worker int cachet; int delayt; int sleept; gen_lock_set_t *sems; // semaphores xj_jalias aliases; // added aliases xj_worker workers; // the list of workers } t_xj_wlist, *xj_wlist; /********** LOOK AT IMPLEMENTATION OF FUNCTIONS FOR DESCRIPTION ***/ xj_wlist xj_wlist_init(int **, int, int, int, int, int); int xj_wlist_set_pid(xj_wlist, int, int); int xj_wlist_get(xj_wlist, xj_jkey, xj_jkey*); int xj_wlist_check(xj_wlist, xj_jkey, xj_jkey*); int xj_wlist_set_flag(xj_wlist, xj_jkey, int); void xj_wlist_del(xj_wlist, xj_jkey, int); void xj_wlist_free(xj_wlist); int xj_wlist_set_aliases(xj_wlist, char *, char *, char *); int xj_wlist_check_aliases(xj_wlist, str*); int xj_wlist_clean_jobs(xj_wlist, int, int); int xj_worker_process(xj_wlist, char*, int, char*, int, db_con_t*, db_func_t*); int xj_address_translation(str *src, str *dst, xj_jalias als, int flag); int xj_manage_jab(char *buf, int len, int *pos, xj_jalias als, xj_jcon jbc); void xj_sig_handler(int s); /********** ***/ int xj_send_sip_msg(str *, str *, str *, str *, int *); int xj_send_sip_msgz(str *,str *, str *, char *, int *); void xj_tuac_callback( struct cell *t, int type, struct tmcb_params *ps); void xj_worker_check_jcons(xj_wlist, xj_jcon_pool, int, fd_set*); void xj_worker_check_qmsg(xj_wlist, xj_jcon_pool); void xj_worker_check_watcher(xj_wlist, xj_jcon_pool, xj_jcon, xj_sipmsg); #endif opensips-2.2.2/modules/jabber/xode.c000066400000000000000000000473311300170765700173730ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" static int _xode_strcmp(const char *a, const char *b) { if(a == NULL || b == NULL) return -1; return strcmp(a,b); } /* Internal routines */ static xode _xode_new(xode_pool p, const char* name, unsigned int type) { xode result = NULL; if (type > XODE_TYPE_LAST) return NULL; if (type != XODE_TYPE_CDATA && name == NULL) return NULL; if (p == NULL) { p = xode_pool_heap(1*1024); } /* Allocate & zero memory */ result = (xode)xode_pool_malloc(p, sizeof(_xode)); memset(result, '\0', sizeof(_xode)); /* Initialize fields */ if (type != XODE_TYPE_CDATA) result->name = xode_pool_strdup(p,name); result->type = type; result->p = p; return result; } static xode _xode_appendsibling(xode lastsibling, const char* name, unsigned int type) { xode result; result = _xode_new(xode_get_pool(lastsibling), name, type); if (result != NULL) { /* Setup sibling pointers */ result->prev = lastsibling; lastsibling->next = result; } return result; } static xode _xode_insert(xode parent, const char* name, unsigned int type) { xode result; if(parent == NULL || name == NULL) return NULL; /* If parent->firstchild is NULL, simply create a new node for the first child */ if (parent->firstchild == NULL) { result = _xode_new(parent->p, name, type); parent->firstchild = result; } /* Otherwise, append this to the lastchild */ else { result= _xode_appendsibling(parent->lastchild, name, type); } result->parent = parent; parent->lastchild = result; return result; } static xode _xode_search(xode firstsibling, const char* name, unsigned int type) { xode current; /* Walk the sibling list, looking for a XODE_TYPE_TAG xode with the specified name */ current = firstsibling; while (current != NULL) { if (name != NULL && (current->type == type) && (_xode_strcmp(current->name, name) == 0)) return current; else current = current->next; } return NULL; } static char* _xode_merge(xode_pool p, char* dest, unsigned int destsize, const char* src, unsigned int srcsize) { char* result; result = (char*)xode_pool_malloc(p, destsize + srcsize + 1); memcpy(result, dest, destsize); memcpy(result+destsize, src, srcsize); result[destsize + srcsize] = '\0'; /* WARNING: major ugly hack: since we're throwing the old data away, let's jump in the xode_pool and subtract it from the size, this is for xmlstream's big-node checking */ p->size -= destsize; return result; } static void _xode_hidesibling(xode child) { if(child == NULL) return; if(child->prev != NULL) child->prev->next = child->next; if(child->next != NULL) child->next->prev = child->prev; } static void _xode_tag2str(xode_spool s, xode node, int flag) { xode tmp; if(flag==0 || flag==1) { xode_spooler(s,"<",xode_get_name(node),s); tmp = xode_get_firstattrib(node); while(tmp) { xode_spooler(s," ",xode_get_name(tmp),"='",xode_strescape(xode_get_pool(node),xode_get_data(tmp)),"'",s); tmp = xode_get_nextsibling(tmp); } if(flag==0) xode_spool_add(s,"/>"); else xode_spool_add(s,">"); } else { xode_spooler(s,"",s); } } static xode_spool _xode_tospool(xode node) { xode_spool s; int level=0,dir=0; xode tmp; if(!node || xode_get_type(node) != XODE_TYPE_TAG) return NULL; s = xode_spool_newfrompool(xode_get_pool(node)); if(!s) return(NULL); while(1) { if(dir==0) { if(xode_get_type(node) == XODE_TYPE_TAG) { if(xode_has_children(node)) { _xode_tag2str(s,node,1); node = xode_get_firstchild(node); level++; continue; } else { _xode_tag2str(s,node,0); } } else { xode_spool_add(s,xode_strescape(xode_get_pool(node),xode_get_data(node))); } } tmp = xode_get_nextsibling(node); if(!tmp) { node = xode_get_parent(node); level--; if(level>=0) _xode_tag2str(s,node,2); if(level<1) break; dir = 1; } else { node = tmp; dir = 0; } } return s; } /* External routines */ /* * xode_new_tag -- create a tag node * Automatically creates a memory xode_pool for the node. * * parameters * name -- name of the tag * * returns * a pointer to the tag node * or NULL if it was unsuccessful */ xode xode_new(const char* name) { return _xode_new(NULL, name, XODE_TYPE_TAG); } /* * alias for 'xode_new' */ xode xode_new_tag(const char* name) { return _xode_new(NULL, name, XODE_TYPE_TAG); } /* * xode_new_tag_pool -- create a tag node within given pool * * parameters * p -- previously created memory pool * name -- name of the tag * * returns * a pointer to the tag node * or NULL if it was unsuccessful */ xode xode_new_frompool(xode_pool p, const char* name) { return _xode_new(p, name, XODE_TYPE_TAG); } /* * xode_insert_tag -- append a child tag to a tag * * parameters * parent -- pointer to the parent tag * name -- name of the child tag * * returns * a pointer to the child tag node * or NULL if it was unsuccessful */ xode xode_insert_tag(xode parent, const char* name) { return _xode_insert(parent, name, XODE_TYPE_TAG); } /* * xode_insert_cdata -- append character data to a tag * If last child of the parent is CDATA, merges CDATA nodes. Otherwise * creates a CDATA node, and appends it to the parent's child list. * * parameters * parent -- parent tag * CDATA -- character data * size -- size of CDATA * or -1 for null-terminated CDATA strings * * returns * a pointer to the child CDATA node * or NULL if it was unsuccessful */ xode xode_insert_cdata(xode parent, const char* CDATA, unsigned int size) { xode result; if(CDATA == NULL || parent == NULL) return NULL; if(size == -1) size = strlen(CDATA); if ((parent->lastchild != NULL) && (parent->lastchild->type == XODE_TYPE_CDATA)) { result = parent->lastchild; result->data = _xode_merge(result->p, result->data, result->data_sz, CDATA, size); result->data_sz = result->data_sz + size; } else { result = _xode_insert(parent, "", XODE_TYPE_CDATA); if (result != NULL) { result->data = (char*)xode_pool_malloc(result->p, size + 1); memcpy(result->data, CDATA, size); result->data[size] = '\0'; result->data_sz = size; } } return result; } /* * xode_gettag -- find given tag in an xode tree * * parameters * parent -- pointer to the parent tag * name -- "name" for the child tag of that name * "name/name" for a sub child (recurses) * "?attrib" to match the first tag with that attrib defined * "?attrib=value" to match the first tag with that attrib and value * or any combination: "name/name/?attrib", etc * * results * a pointer to the tag matching search criteria * or NULL if search was unsuccessful */ xode xode_get_tag(xode parent, const char* name) { char *str, *slash, *qmark, *equals; xode step, ret; if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL; if(strstr(name, "/") == NULL && strstr(name,"?") == NULL) return _xode_search(parent->firstchild, name, XODE_TYPE_TAG); /* jer's note: why can't I modify the name directly, why do I have to strdup it? damn c grrr! */ str = strdup(name); slash = strstr(str, "/"); qmark = strstr(str, "?"); equals = strstr(str, "="); if(qmark != NULL && (slash == NULL || qmark < slash)) { /* of type ?attrib */ *qmark = '\0'; qmark++; if(equals != NULL) { *equals = '\0'; equals++; } for(step = parent->firstchild; step != NULL; step = xode_get_nextsibling(step)) { if(xode_get_type(step) != XODE_TYPE_TAG) continue; if(*str != '\0') if(_xode_strcmp(xode_get_name(step),str) != 0) continue; if(xode_get_attrib(step,qmark) == NULL) continue; if(equals != NULL && _xode_strcmp(xode_get_attrib(step,qmark),equals) != 0) continue; break; } free(str); return step; } *slash = '\0'; ++slash; for(step = parent->firstchild; step != NULL; step = xode_get_nextsibling(step)) { if(xode_get_type(step) != XODE_TYPE_TAG) continue; if(_xode_strcmp(xode_get_name(step),str) != 0) continue; ret = xode_get_tag(step, slash); if(ret != NULL) { free(str); return ret; } } free(str); return NULL; } /* return the cdata from any tag */ char *xode_get_tagdata(xode parent, const char *name) { xode tag; tag = xode_get_tag(parent, name); if(tag == NULL) return NULL; return xode_get_data(tag); } void xode_put_attrib(xode owner, const char* name, const char* value) { xode attrib; if(owner == NULL || name == NULL || value == NULL) return; /* If there are no existing attributes, allocate a new one to start the list */ if (owner->firstattrib == NULL) { attrib = _xode_new(owner->p, name, XODE_TYPE_ATTRIB); owner->firstattrib = attrib; owner->lastattrib = attrib; } else { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if(attrib == NULL) { attrib = _xode_appendsibling(owner->lastattrib, name, XODE_TYPE_ATTRIB); owner->lastattrib = attrib; } } /* Update the value of the attribute */ attrib->data_sz = strlen(value); attrib->data = xode_pool_strdup(owner->p, value); } char* xode_get_attrib(xode owner, const char* name) { xode attrib; if (owner != NULL && owner->firstattrib != NULL) { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if (attrib != NULL) return (char*)attrib->data; } return NULL; } void xode_put_vattrib(xode owner, const char* name, void *value) { xode attrib; if (owner != NULL) { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if (attrib == NULL) { xode_put_attrib(owner, name, ""); attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); } if (attrib != NULL) attrib->firstchild = (xode)value; } } void* xode_get_vattrib(xode owner, const char* name) { xode attrib; if (owner != NULL && owner->firstattrib != NULL) { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if (attrib != NULL) return (void*)attrib->firstchild; } return NULL; } xode xode_get_firstattrib(xode parent) { if (parent != NULL) return parent->firstattrib; return NULL; } xode xode_get_firstchild(xode parent) { if (parent != NULL) return parent->firstchild; return NULL; } xode xode_get_lastchild(xode parent) { if (parent != NULL) return parent->lastchild; return NULL; } xode xode_get_nextsibling(xode sibling) { if (sibling != NULL) return sibling->next; return NULL; } xode xode_get_prevsibling(xode sibling) { if (sibling != NULL) return sibling->prev; return NULL; } xode xode_get_parent(xode node) { if (node != NULL) return node->parent; return NULL; } char* xode_get_name(xode node) { if (node != NULL) return node->name; return NULL; } char* xode_get_data(xode node) { xode cur; if(node == NULL) return NULL; if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */ { for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur)) if(xode_get_type(cur) == XODE_TYPE_CDATA) return cur->data; }else{ return node->data; } return NULL; } int xode_get_datasz(xode node) { if( node == NULL ) { return (int)(long)NULL; } else if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */ { xode cur; for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur)) if(xode_get_type(cur) == XODE_TYPE_CDATA) return cur->data_sz; }else{ return node->data_sz; } return (int)(long)NULL; } int xode_get_type(xode node) { if (node != NULL) { return node->type; } return (int)(long)NULL; } int xode_has_children(xode node) { if ((node != NULL) && (node->firstchild != NULL)) return 1; return 0; } int xode_has_attribs(xode node) { if ((node != NULL) && (node->firstattrib != NULL)) return 1; return 0; } xode_pool xode_get_pool(xode node) { if (node != NULL) return node->p; return (xode_pool)NULL; } void xode_hide(xode child) { xode parent; if(child == NULL || child->parent == NULL) return; parent = child->parent; /* first fix up at the child level */ _xode_hidesibling(child); /* next fix up at the parent level */ if(parent->firstchild == child) parent->firstchild = child->next; if(parent->lastchild == child) parent->lastchild = child->prev; } void xode_hide_attrib(xode parent, const char *name) { xode attrib; if(parent == NULL || parent->firstattrib == NULL || name == NULL) return; attrib = _xode_search(parent->firstattrib, name, XODE_TYPE_ATTRIB); if(attrib == NULL) return; /* first fix up at the child level */ _xode_hidesibling(attrib); /* next fix up at the parent level */ if(parent->firstattrib == attrib) parent->firstattrib = attrib->next; if(parent->lastattrib == attrib) parent->lastattrib = attrib->prev; } /* * xode2str -- convert given xode tree into a string * * parameters * node -- pointer to the xode structure * * results * a pointer to the created string * or NULL if it was unsuccessful */ char *xode_to_str(xode node) { return xode_spool_tostr(_xode_tospool(node)); } /* loop through both a and b comparing everything, attribs, cdata, children, etc */ int xode_cmp(xode a, xode b) { int ret = 0; while(1) { if(a == NULL && b == NULL) return 0; if(a == NULL || b == NULL) return -1; if(xode_get_type(a) != xode_get_type(b)) return -1; switch(xode_get_type(a)) { case XODE_TYPE_ATTRIB: ret = _xode_strcmp(xode_get_name(a), xode_get_name(b)); if(ret != 0) return -1; ret = _xode_strcmp(xode_get_data(a), xode_get_data(b)); if(ret != 0) return -1; break; case XODE_TYPE_TAG: ret = _xode_strcmp(xode_get_name(a), xode_get_name(b)); if(ret != 0) return -1; ret = xode_cmp(xode_get_firstattrib(a), xode_get_firstattrib(b)); if(ret != 0) return -1; ret = xode_cmp(xode_get_firstchild(a), xode_get_firstchild(b)); if(ret != 0) return -1; break; case XODE_TYPE_CDATA: ret = _xode_strcmp(xode_get_data(a), xode_get_data(b)); if(ret != 0) return -1; } a = xode_get_nextsibling(a); b = xode_get_nextsibling(b); } } xode xode_insert_tagnode(xode parent, xode node) { xode child; child = xode_insert_tag(parent, xode_get_name(node)); if (xode_has_attribs(node)) xode_insert_node(child, xode_get_firstattrib(node)); if (xode_has_children(node)) xode_insert_node(child, xode_get_firstchild(node)); return child; } /* places copy of node and node's siblings in parent */ void xode_insert_node(xode parent, xode node) { if(node == NULL || parent == NULL) return; while(node != NULL) { switch(xode_get_type(node)) { case XODE_TYPE_ATTRIB: xode_put_attrib(parent, xode_get_name(node), xode_get_data(node)); break; case XODE_TYPE_TAG: xode_insert_tagnode(parent, node); break; case XODE_TYPE_CDATA: xode_insert_cdata(parent, xode_get_data(node), xode_get_datasz(node)); } node = xode_get_nextsibling(node); } } /* produce full duplicate of x with a new xode_pool, x must be a tag! */ xode xode_dup(xode x) { xode x2; if(x == NULL) return NULL; x2 = xode_new(xode_get_name(x)); if (xode_has_attribs(x)) xode_insert_node(x2, xode_get_firstattrib(x)); if (xode_has_children(x)) xode_insert_node(x2, xode_get_firstchild(x)); return x2; } xode xode_dup_frompool(xode_pool p, xode x) { xode x2; if(x == NULL) return NULL; x2 = xode_new_frompool(p, xode_get_name(x)); if (xode_has_attribs(x)) xode_insert_node(x2, xode_get_firstattrib(x)); if (xode_has_children(x)) xode_insert_node(x2, xode_get_firstchild(x)); return x2; } xode xode_wrap(xode x,const char *wrapper) { xode wrap; if(x==NULL||wrapper==NULL) return NULL; wrap=xode_new_frompool(xode_get_pool(x),wrapper); if(wrap==NULL) return NULL; wrap->firstchild=x; wrap->lastchild=x; x->parent=wrap; return wrap; } void xode_free(xode node) { if(node == NULL) return; xode_pool_free(node->p); } void _xode_to_prettystr( xode_spool s, xode x, int deep ) { int i; xode y; if(xode_get_type(x) != XODE_TYPE_TAG) return; for(i=0; i"); xode_spool_add(s,"\n"); if( xode_get_data(x)) { for(i=0; i<=deep; i++) xode_spool_add(s, "\t"); xode_spool_add( s , xode_get_data(x)); } y = xode_get_firstchild(x); while( y ) { _xode_to_prettystr(s , y, deep+1); y = xode_get_nextsibling(y); xode_spool_add(s,"\n"); } for(i=0; i" , s ); return; } char * xode_to_prettystr( xode x ) { xode_spool s; if( !x) return NULL; s = xode_spool_newfrompool( xode_get_pool(x)); _xode_to_prettystr( s , x, 0 ); return xode_spool_tostr(s); } opensips-2.2.2/modules/jabber/xode.h000066400000000000000000000232611300170765700173740ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include #include #include #include #include #include #include #include #include #include "expat.h" #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ /* ** Arrange to use either varargs or stdargs */ #define MAXSHORTSTR 203 /* max short string length */ #define QUAD_T unsigned long long #ifdef __STDC__ #include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap, f) # define VA_END va_end(ap) #else /* __STDC__ */ # include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap) # define VA_END va_end(ap) #endif /* __STDC__ */ #ifndef INCL_LIBXODE_H #define INCL_LIBXODE_H #ifdef __cplusplus extern "C" { #endif #ifndef HAVE_SNPRINTF extern int ap_snprintf(char *, size_t, const char *, ...); #define snprintf ap_snprintf #endif #ifndef HAVE_VSNPRINTF extern int ap_vsnprintf(char *, size_t, const char *, va_list ap); #define vsnprintf ap_vsnprintf #endif /* --------------------------------------------------------- */ /* */ /* Pool-based memory management routines */ /* */ /* --------------------------------------------------------- */ /* xode_pool_cleaner - callback type which is associated with a pool entry; invoked when the pool entry is free'd */ typedef void (*xode_pool_cleaner)(void *arg); /* pheap - singular allocation of memory */ struct xode_pool_heap { void *block; int size, used; }; /* pool - base node for a pool. Maintains a linked list of pool entries (pool_free) */ typedef struct xode_pool_struct { int size; struct xode_pool_free *cleanup; struct xode_pool_heap *heap; } _xode_pool, *xode_pool; /* pool creation routines */ xode_pool xode_pool_heap(int bytes); xode_pool xode_pool_new(void); /* pool wrappers for malloc */ void *xode_pool_malloc (xode_pool p, int size); void *xode_pool_mallocx (xode_pool p, int size, char c); void *xode_pool_malloco (xode_pool p, int size); /* wrapper around strdup, gains mem from pool */ char *xode_pool_strdup (xode_pool p, const char *src); /* calls f(arg) before the pool is freed during cleanup */ void xode_pool_cleanup (xode_pool p, xode_pool_cleaner f, void *arg); /* pool wrapper for free, called on a pool */ void xode_pool_free (xode_pool p); /* returns total bytes allocated in this pool */ int xode_pool_size (xode_pool p); /* --------------------------------------------------------- */ /* */ /* XML escaping utils */ /* */ /* --------------------------------------------------------- */ char *xode_strescape(xode_pool p, char *buf); /* Escape <>&'" chars */ char *xode_strunescape(xode_pool p, char *buf); /* --------------------------------------------------------- */ /* */ /* String pools (spool) functions */ /* */ /* --------------------------------------------------------- */ struct xode_spool_node { char *c; struct xode_spool_node *next; }; typedef struct xode_spool_struct { xode_pool p; int len; struct xode_spool_node *last; struct xode_spool_node *first; } *xode_spool; xode_spool xode_spool_new ( void ); /* create a string pool on a new pool */ xode_spool xode_spool_newfrompool ( xode_pool p ); /* create a string pool from an existing pool */ xode_pool xode_spool_getpool ( const xode_spool s ); /* returns the xode_pool used by this xode_spool */ void xode_spooler ( xode_spool s, ... ); /* append all the char * args to the pool, terminate args with s again */ char *xode_spool_tostr ( xode_spool s ); /* return a big string */ void xode_spool_add ( xode_spool s, char *str ); /* add a single char to the pool */ char *xode_spool_str ( xode_pool p, ... ); /* wrap all the spooler stuff in one function, the happy fun ball! */ int xode_spool_getlen ( const xode_spool s ); /* returns the total length of the string contained in the pool */ void xode_spool_free ( xode_spool s ); /* Free's the pool associated with the xode_spool */ /* --------------------------------------------------------- */ /* */ /* xodes - Document Object Model */ /* */ /* --------------------------------------------------------- */ #define XODE_TYPE_TAG 0 #define XODE_TYPE_ATTRIB 1 #define XODE_TYPE_CDATA 2 #define XODE_TYPE_LAST 2 #define XODE_TYPE_UNDEF -1 /* -------------------------------------------------------------------------- Node structure. Do not use directly! Always use accessors macros and methods! -------------------------------------------------------------------------- */ typedef struct xode_struct { char* name; unsigned short type; char* data; int data_sz; int complete; xode_pool p; struct xode_struct* parent; struct xode_struct* firstchild; struct xode_struct* lastchild; struct xode_struct* prev; struct xode_struct* next; struct xode_struct* firstattrib; struct xode_struct* lastattrib; } _xode, *xode; /* Node creation routines */ xode xode_wrap(xode x,const char* wrapper); xode xode_new(const char* name); xode xode_new_tag(const char* name); xode xode_new_frompool(xode_pool p, const char* name); xode xode_insert_tag(xode parent, const char* name); xode xode_insert_cdata(xode parent, const char* CDATA, unsigned int size); xode xode_insert_tagnode(xode parent, xode node); void xode_insert_node(xode parent, xode node); xode xode_from_str(char *str, int len); xode xode_from_strx(char *str, int len, int *err, int *pos); xode xode_from_file(char *file); xode xode_dup(xode x); /* duplicate x */ xode xode_dup_frompool(xode_pool p, xode x); /* Node Memory Pool */ xode_pool xode_get_pool(xode node); /* Node editing */ void xode_hide(xode child); void xode_hide_attrib(xode parent, const char *name); /* Node deletion routine, also frees the node pool! */ void xode_free(xode node); /* Locates a child tag by name and returns it */ xode xode_get_tag(xode parent, const char* name); char* xode_get_tagdata(xode parent, const char* name); /* Attribute accessors */ void xode_put_attrib(xode owner, const char* name, const char* value); char* xode_get_attrib(xode owner, const char* name); /* Bastard am I, but these are fun for internal use ;-) */ void xode_put_vattrib(xode owner, const char* name, void *value); void* xode_get_vattrib(xode owner, const char* name); /* Node traversal routines */ xode xode_get_firstattrib(xode parent); xode xode_get_firstchild(xode parent); xode xode_get_lastchild(xode parent); xode xode_get_nextsibling(xode sibling); xode xode_get_prevsibling(xode sibling); xode xode_get_parent(xode node); /* Node information routines */ char* xode_get_name(xode node); char* xode_get_data(xode node); int xode_get_datasz(xode node); int xode_get_type(xode node); int xode_has_children(xode node); int xode_has_attribs(xode node); /* Node-to-string translation */ char* xode_to_str(xode node); char* xode_to_prettystr(xode node); /* Puts \t and \n to make a human-easily readable string */ int xode_cmp(xode a, xode b); /* compares a and b for equality */ int xode_to_file(char *file, xode node); /* writes node to file */ /*********************** * XSTREAM Section ***********************/ #define XODE_STREAM_MAXNODE 1000000 #define XODE_STREAM_MAXDEPTH 100 #define XODE_STREAM_ROOT 0 /* root element */ #define XODE_STREAM_NODE 1 /* normal node */ #define XODE_STREAM_CLOSE 2 /* closed root node */ #define XODE_STREAM_ERROR 4 /* parser error */ typedef void (*xode_stream_onNode)(int type, xode x, void *arg); /* xstream event handler */ typedef struct xode_stream_struct { XML_Parser parser; xode node; char *cdata; int cdata_len; xode_pool p; xode_stream_onNode f; void *arg; int status; int depth; } *xode_stream, _xode_stream; xode_stream xode_stream_new(xode_pool p, xode_stream_onNode f, void *arg); /* create a new xstream */ int xode_stream_eat(xode_stream xs, char *buff, int len); /* parse new data for this xstream, returns last XSTREAM_* status */ /* convenience functions */ #ifdef __cplusplus } #endif #endif /* INCL_LIBXODE_H */ opensips-2.2.2/modules/jabber/xode_from.c000066400000000000000000000127771300170765700204240ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" #include #include #include static void _xode_put_expatattribs(xode current, const char **atts) { int i = 0; if (atts == NULL) return; while (atts[i] != '\0') { xode_put_attrib(current, atts[i], atts[i+1]); i += 2; } } static void _xode_expat_startElement(void* userdata, const char* name, const char** atts) { /* get the xmlnode pointed to by the userdata */ xode *x = userdata; xode current = *x; if (current == NULL) { /* allocate a base node */ current = xode_new(name); _xode_put_expatattribs(current, atts); *x = current; } else { *x = xode_insert_tag(current, name); _xode_put_expatattribs(*x, atts); } } static void _xode_expat_endElement(void* userdata, const char* name) { xode *x = userdata; xode current = *x; current->complete = 1; current = xode_get_parent(current); /* if it's NULL we've hit the top folks, otherwise back up a level */ if(current != NULL) *x = current; } static void _xode_expat_charData(void* userdata, const char* s, int len) { xode *x = userdata; xode current = *x; xode_insert_cdata(current, s, len); } xode xode_from_str(char *str, int len) { XML_Parser p; xode *x, node; /* pointer to an xmlnode */ if(NULL == str) return NULL; if(len == -1) len = strlen(str); x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, _xode_expat_startElement, _xode_expat_endElement); XML_SetCharacterDataHandler(p, _xode_expat_charData); if(!XML_Parse(p, str, len, 1)) { /* jdebug(ZONE,"xmlnode_str_error: %s",(char *)XML_ErrorString(XML_GetErrorCode(p)));*/ xode_free(*x); *x = NULL; } node = *x; free(x); XML_ParserFree(p); return node; /* return the xmlnode x points to */ } xode xode_from_strx(char *str, int len, int *err, int *pos) { XML_Parser p; xode *x, node; /* pointer to an xmlnode */ if(NULL == str) return NULL; if(len == -1) len = strlen(str); x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, _xode_expat_startElement, _xode_expat_endElement); XML_SetCharacterDataHandler(p, _xode_expat_charData); XML_Parse(p, str, len, 0); if(err != NULL) *err = XML_GetErrorCode(p); if(pos != NULL) *pos = XML_GetCurrentByteIndex(p); node = *x; free(x); XML_ParserFree(p); return node; /* return the xmlnode x points to */ } xode xode_from_file(char *file) { XML_Parser p; xode *x, node; /* pointer to an xmlnode */ char buf[BUFSIZ]; int done, fd, len; char _file[1000]; if(NULL == file) return NULL; /* perform tilde expansion */ if(*file == '~') { char *env = getenv("HOME"); if(env != NULL) snprintf((char*)_file, 1000, "%s%s", env, file + 1); else snprintf((char*)_file, 1000, "%s", file); } else { snprintf((char*)_file, 1000, "%s", file); } fd = open((char*)&_file,O_RDONLY); if(fd < 0) return NULL; x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, _xode_expat_startElement, _xode_expat_endElement); XML_SetCharacterDataHandler(p, _xode_expat_charData); do{ len = read(fd, buf, BUFSIZ); done = len < BUFSIZ; if(!XML_Parse(p, buf, len, done)) { /* jdebug(ZONE,"xmlnode_file_parseerror: %s",(char *)XML_ErrorString(XML_GetErrorCode(p)));*/ xode_free(*x); *x = NULL; done = 1; } }while(!done); node = *x; XML_ParserFree(p); free(x); close(fd); return node; /* return the xmlnode x points to */ } int xode_to_file(char *file, xode node) { char *doc; int fd, i; char _file[1000]; if(file == NULL || node == NULL) return -1; /* perform tilde expansion */ if(*file == '~') { char *env = getenv("HOME"); if(env != NULL) snprintf((char*)_file, 1000, "%s%s", env, file + 1); else snprintf((char*)_file, 1000, "%s", file); } else { snprintf((char*)_file, 1000, "%s", file); } fd = open((char*)&_file, O_CREAT | O_WRONLY | O_TRUNC, 0600); if(fd < 0) return -1; doc = xode_to_str(node); i = write(fd,doc,strlen(doc)); if(i < 0) return -1; close(fd); return 1; } opensips-2.2.2/modules/jabber/xode_str.c000066400000000000000000000126101300170765700202530ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" xode_pool xode_spool_getpool(const xode_spool s) { if(s == NULL) return NULL; return s->p; } int xode_spool_getlen(const xode_spool s) { if(s == NULL) return 0; return s->len; } void xode_spool_free(xode_spool s) { xode_pool_free(xode_spool_getpool(s)); } xode_spool xode_spool_newfrompool(xode_pool p) { xode_spool s; s = xode_pool_malloc(p, sizeof(struct xode_spool_struct)); s->p = p; s->len = 0; s->last = NULL; s->first = NULL; return s; } xode_spool xode_spool_new(void) { return xode_spool_newfrompool(xode_pool_heap(512)); } void xode_spool_add(xode_spool s, char *str) { struct xode_spool_node *sn; int len; if(str == NULL) return; len = strlen(str); if(len == 0) return; sn = xode_pool_malloc(s->p, sizeof(struct xode_spool_node)); sn->c = xode_pool_strdup(s->p, str); sn->next = NULL; s->len += len; if(s->last != NULL) s->last->next = sn; s->last = sn; if(s->first == NULL) s->first = sn; } void xode_spooler(xode_spool s, ...) { va_list ap; char *arg = NULL; if(s == NULL) return; va_start(ap, s); /* loop till we hit our end flag, the first arg */ while(1) { arg = va_arg(ap,char *); if((void*)arg == (void*)s || arg == NULL) break; else xode_spool_add(s, arg); } va_end(ap); } char *xode_spool_tostr(xode_spool s) { char *ret,*tmp; struct xode_spool_node *next; if(s == NULL || s->len == 0 || s->first == NULL) return NULL; ret = xode_pool_malloc(s->p, s->len + 1); *ret = '\0'; next = s->first; tmp = ret; while(next != NULL) { tmp = strcat(tmp,next->c); next = next->next; } return ret; } /* convenience :) */ char *xode_spool_str(xode_pool p, ...) { va_list ap; xode_spool s; char *arg = NULL; if(p == NULL) return NULL; s = xode_spool_newfrompool(p); va_start(ap, p); /* loop till we hit our end flag, the first arg */ while(1) { arg = va_arg(ap,char *); if((void*)arg == (void*)p) break; else xode_spool_add(s, arg); } va_end(ap); return xode_spool_tostr(s); } char *xode_strunescape(xode_pool p, char *buf) { int i,j=0; char *temp; if (p == NULL || buf == NULL) return(NULL); if (strchr(buf,'&') == NULL) return(buf); temp = xode_pool_malloc(p,strlen(buf)+1); if (temp == NULL) return(NULL); for(i=0;i': newlen+=4; break; } } if(oldlen == newlen) return buf; temp = xode_pool_malloc(p,newlen+1); if (temp==NULL) return(NULL); for(i=j=0;i': memcpy(&temp[j],">",4); j += 4; break; default: temp[j++] = buf[i]; } } temp[j] = '\0'; return temp; } opensips-2.2.2/modules/jabber/xpool.c000066400000000000000000000145341300170765700175740ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ * * 2/27/00:3am, random plans by jer * * ok based on gprof, we really need some innovation here... my thoughs are this: * * most things are strings, so have a string-based true-blue garbage collector * one big global hash containing all the strings created by any pstrdup, returning const char * * a refcount on each string block * when a pool is freed, it moves down the refcount * garbage collector collects pools on the free stack, and runs through the hash for unused strings * j_strcmp can check for == (if they are both from a pstrdup) * * let's see... this would change: * pstrdup: do a hash lookup, success=return, fail=pmalloc & hash put * pool_free: * * * * * */ #include "xode.h" //#include "config.h" #define _xode_pool__malloc malloc #define _xode_pool__free free /* xode_pfree - a linked list node which stores an allocation chunk, plus a callback */ struct xode_pool_free { xode_pool_cleaner f; void *arg; struct xode_pool_heap *heap; struct xode_pool_free *next; }; /* make an empty pool */ xode_pool _xode_pool_new(void) { xode_pool p; while((p = _xode_pool__malloc(sizeof(_xode_pool))) == NULL) sleep(1); p->cleanup = NULL; p->heap = NULL; p->size = 0; return p; } /* free a heap */ void _xode_pool_heapfree(void *arg) { struct xode_pool_heap *h = (struct xode_pool_heap *)arg; _xode_pool__free(h->block); _xode_pool__free(h); } /* mem should always be freed last */ void _xode_pool_cleanup_append(xode_pool p, struct xode_pool_free *pf) { struct xode_pool_free *cur; if(p->cleanup == NULL) { p->cleanup = pf; return; } /* fast forward to end of list */ for(cur = p->cleanup; cur->next != NULL; cur = cur->next); cur->next = pf; } /* create a cleanup tracker */ struct xode_pool_free *_xode_pool_free(xode_pool p, xode_pool_cleaner f, void *arg) { struct xode_pool_free *ret; /* make the storage for the tracker */ while((ret = _xode_pool__malloc(sizeof(struct xode_pool_free))) == NULL) sleep(1); ret->f = f; ret->arg = arg; ret->next = NULL; return ret; } /* create a heap and make sure it get's cleaned up */ struct xode_pool_heap *_xode_pool_heap(xode_pool p, int size) { struct xode_pool_heap *ret; struct xode_pool_free *clean; /* make the return heap */ while((ret = _xode_pool__malloc(sizeof(struct xode_pool_heap))) == NULL) sleep(1); while((ret->block = _xode_pool__malloc(size)) == NULL) sleep(1); ret->size = size; p->size += size; ret->used = 0; /* append to the cleanup list */ clean = _xode_pool_free(p, _xode_pool_heapfree, (void *)ret); clean->heap = ret; /* for future use in finding used mem for pstrdup */ _xode_pool_cleanup_append(p, clean); return ret; } xode_pool _xode_pool_newheap(int bytes) { xode_pool p; p = _xode_pool_new(); p->heap = _xode_pool_heap(p,bytes); return p; } void *xode_pool_malloc(xode_pool p, int size) { void *block; if(p == NULL) { fprintf(stderr,"Memory Leak! xode_pmalloc received NULL pool, unable to track allocation, exiting]\n"); abort(); } /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */ if(p->heap == NULL || size > (p->heap->size / 2)) { while((block = _xode_pool__malloc(size)) == NULL) sleep(1); p->size += size; _xode_pool_cleanup_append(p, _xode_pool_free(p, _xode_pool__free, block)); return block; } /* we have to preserve boundaries, long story :) */ if(size >= 4) while(p->heap->used&7) p->heap->used++; /* if we don't fit in the old heap, replace it */ if(size > (p->heap->size - p->heap->used)) p->heap = _xode_pool_heap(p, p->heap->size); /* the current heap has room */ block = (char *)p->heap->block + p->heap->used; p->heap->used += size; return block; } void *xode_pool_mallocx(xode_pool p, int size, char c) { void* result = xode_pool_malloc(p, size); if (result != NULL) memset(result, c, size); return result; } /* easy safety utility (for creating blank mem for structs, etc) */ void *xode_pool_malloco(xode_pool p, int size) { void *block = xode_pool_malloc(p, size); memset(block, 0, size); return block; } /* XXX efficient: move this to const char * and then loop through the existing heaps to see if src is within a block in this pool */ char *xode_pool_strdup(xode_pool p, const char *src) { char *ret; if(src == NULL) return NULL; ret = xode_pool_malloc(p,strlen(src) + 1); strcpy(ret,src); return ret; } /* when move above, this one would actually return a new block */ char *xode_pool_strdupx(xode_pool p, const char *src) { return xode_pool_strdup(p, src); } int xode_pool_size(xode_pool p) { if(p == NULL) return 0; return p->size; } void xode_pool_free(xode_pool p) { struct xode_pool_free *cur, *stub; if(p == NULL) return; cur = p->cleanup; while(cur != NULL) { (*cur->f)(cur->arg); stub = cur->next; _xode_pool__free(cur); cur = stub; } _xode_pool__free(p); } /* public cleanup utils, insert in a way that they are run FIFO, before mem frees */ void xode_pool_cleanup(xode_pool p, xode_pool_cleaner f, void *arg) { struct xode_pool_free *clean; clean = _xode_pool_free(p, f, arg); clean->next = p->cleanup; p->cleanup = clean; } xode_pool xode_pool_new(void) { return _xode_pool_new(); } xode_pool xode_pool_heap(const int bytes) { return _xode_pool_newheap(bytes); } opensips-2.2.2/modules/jabber/xsnprintf.c000066400000000000000000000645111300170765700204660ustar00rootroot00000000000000/* ==================================================================== * Copyright (c) 1995-1998 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. * * 5. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see . * * This code is based on, and used with the permission of, the * SIO stdio-replacement strx_* functions by Panos Tsirigotis * for xinetd. */ #include "xode.h" #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) #include #include #include #include #include #include #include #ifdef HAVE_GCVT #define ap_ecvt ecvt #define ap_fcvt fcvt #define ap_gcvt gcvt #else /* * cvt.c - IEEE floating point formatting routines for FreeBSD * from GNU libc-4.6.27 */ /* * ap_ecvt converts to decimal * the number of digits is specified by ndigit * decpt is set to the position of the decimal point * sign is set to 0 for positive, 1 for negative */ #define NDIG 80 static char * ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag) { register int r2; double fi, fj; register char *p, *p1; static char buf[NDIG]; if (ndigits >= NDIG - 1) ndigits = NDIG - 2; r2 = 0; *sign = 0; p = &buf[0]; if (arg < 0) { *sign = 1; arg = -arg; } arg = modf(arg, &fi); p1 = &buf[NDIG]; /* * Do integer part */ if (fi != 0) { p1 = &buf[NDIG]; while (fi != 0) { fj = modf(fi / 10, &fi); *--p1 = (int) ((fj + .03) * 10) + '0'; r2++; } while (p1 < &buf[NDIG]) *p++ = *p1++; } else if (arg > 0) { while ((fj = arg * 10) < 1) { arg = fj; r2--; } } p1 = &buf[ndigits]; if (eflag == 0) p1 += r2; *decpt = r2; if (p1 < &buf[0]) { buf[0] = '\0'; return (buf); } while (p <= p1 && p < &buf[NDIG]) { arg *= 10; arg = modf(arg, &fj); *p++ = (int) fj + '0'; } if (p1 >= &buf[NDIG]) { buf[NDIG - 1] = '\0'; return (buf); } p = p1; *p1 += 5; while (*p1 > '9') { *p1 = '0'; if (p1 > buf) ++ * --p1; else { *p1 = '1'; (*decpt)++; if (eflag == 0) { if (p > buf) *p = '0'; p++; } } } *p = '\0'; return (buf); } static char * ap_ecvt(double arg, int ndigits, int *decpt, int *sign) { return (ap_cvt(arg, ndigits, decpt, sign, 1)); } static char * ap_fcvt(double arg, int ndigits, int *decpt, int *sign) { return (ap_cvt(arg, ndigits, decpt, sign, 0)); } /* * ap_gcvt - Floating output conversion to * minimal length string */ static char * ap_gcvt(double number, int ndigit, char *buf) { int sign, decpt; register char *p1, *p2; int i; p1 = ap_ecvt(number, ndigit, &decpt, &sign); p2 = buf; if (sign) *p2++ = '-'; for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--) ndigit--; if ((decpt >= 0 && decpt - ndigit > 4) || (decpt < 0 && decpt < -3)) { /* use E-style */ decpt--; *p2++ = *p1++; *p2++ = '.'; for (i = 1; i < ndigit; i++) *p2++ = *p1++; *p2++ = 'e'; if (decpt < 0) { decpt = -decpt; *p2++ = '-'; } else *p2++ = '+'; if (decpt / 100 > 0) *p2++ = decpt / 100 + '0'; if (decpt / 10 > 0) *p2++ = (decpt % 100) / 10 + '0'; *p2++ = decpt % 10 + '0'; } else { if (decpt <= 0) { if (*p1 != '0') *p2++ = '.'; while (decpt < 0) { decpt++; *p2++ = '0'; } } for (i = 1; i <= ndigit; i++) { *p2++ = *p1++; if (i == decpt) *p2++ = '.'; } if (ndigit < decpt) { while (ndigit++ < decpt) *p2++ = '0'; *p2++ = '.'; } } if (p2[-1] == '.') p2--; *p2 = '\0'; return (buf); } #endif /* HAVE_CVT */ typedef enum { NO = 0, YES = 1 } boolean_e; #define FALSE 0 #define TRUE 1 #define NUL '\0' #define INT_NULL ((int *)0) #define WIDE_INT long typedef WIDE_INT wide_int; typedef unsigned WIDE_INT u_wide_int; typedef int bool_int; #define S_NULL "(null)" #define S_NULL_LEN 6 #define FLOAT_DIGITS 6 #define EXPONENT_LENGTH 10 /* * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions * * XXX: this is a magic number; do not decrease it */ #define NUM_BUF_SIZE 512 /* * Descriptor for buffer area */ struct buf_area { char *buf_end; char *nextb; /* pointer to next byte to read/write */ }; typedef struct buf_area buffy; /* * The INS_CHAR macro inserts a character in the buffer and writes * the buffer back to disk if necessary * It uses the char pointers sp and bep: * sp points to the next available character in the buffer * bep points to the end-of-buffer+1 * While using this macro, note that the nextb pointer is NOT updated. * * NOTE: Evaluation of the c argument should not have any side-effects */ #define INS_CHAR( c, sp, bep, cc ) \ { \ if ( sp < bep ) \ { \ *sp++ = c ; \ cc++ ; \ } \ } #define NUM( c ) ( c - '0' ) #define STR_TO_DEC( str, num ) \ num = NUM( *str++ ) ; \ while ( isdigit((int)*str ) ) \ { \ num *= 10 ; \ num += NUM( *str++ ) ; \ } /* * This macro does zero padding so that the precision * requirement is satisfied. The padding is done by * adding '0's to the left of the string that is going * to be printed. */ #define FIX_PRECISION( adjust, precision, s, s_len ) \ if ( adjust ) \ while ( s_len < precision ) \ { \ *--s = '0' ; \ s_len++ ; \ } /* * Macro that does padding. The padding is done by printing * the character ch. */ #define PAD( width, len, ch ) do \ { \ INS_CHAR( ch, sp, bep, cc ) ; \ width-- ; \ } \ while ( width > len ) /* * Prefix the character ch to the string str * Increase length * Set the has_prefix flag */ #define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES /* * Convert num to its decimal format. * Return value: * - a pointer to a string containing the number (no sign) * - len contains the length of the string * - is_negative is set to TRUE or FALSE depending on the sign * of the number (always set to FALSE if is_unsigned is TRUE) * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char * conv_10(register wide_int num, register bool_int is_unsigned, register bool_int * is_negative, char *buf_end, register int *len) { register char *p = buf_end; register u_wide_int magnitude; if (is_unsigned) { magnitude = (u_wide_int) num; *is_negative = FALSE; } else { *is_negative = (num < 0); /* * On a 2's complement machine, negating the most negative integer * results in a number that cannot be represented as a signed integer. * Here is what we do to obtain the number's magnitude: * a. add 1 to the number * b. negate it (becomes positive) * c. convert it to unsigned * d. add 1 */ if (*is_negative) { wide_int t = num + 1; magnitude = ((u_wide_int) - t) + 1; } else magnitude = (u_wide_int) num; } /* * We use a do-while loop so that we write at least 1 digit */ do { register u_wide_int new_magnitude = magnitude / 10; *--p = magnitude - new_magnitude * 10 + '0'; magnitude = new_magnitude; } while (magnitude); *len = buf_end - p; return (p); } /* * Convert a floating point number to a string formats 'f', 'e' or 'E'. * The result is placed in buf, and len denotes the length of the string * The sign is returned in the is_negative argument (and is not placed * in buf). */ static char * conv_fp(register char format, register double num, boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len) { register char *s = buf; register char *p; int decimal_point; if (format == 'f') p = ap_fcvt(num, precision, &decimal_point, is_negative); else /* either e or E format */ p = ap_ecvt(num, precision + 1, &decimal_point, is_negative); /* * Check for Infinity and NaN */ if (isalpha((int)*p)) { *len = strlen(strcpy(buf, p)); *is_negative = FALSE; return (buf); } if (format == 'f') { if (decimal_point <= 0) { *s++ = '0'; if (precision > 0) { *s++ = '.'; while (decimal_point++ < 0) *s++ = '0'; } else if (add_dp) { *s++ = '.'; } } else { while (decimal_point-- > 0) { *s++ = *p++; } if (precision > 0 || add_dp) { *s++ = '.'; } } } else { *s++ = *p++; if (precision > 0 || add_dp) *s++ = '.'; } /* * copy the rest of p, the NUL is NOT copied */ while (*p) *s++ = *p++; if (format != 'f') { char temp[EXPONENT_LENGTH]; /* for exponent conversion */ int t_len; bool_int exponent_is_negative; *s++ = format; /* either e or E */ decimal_point--; if (decimal_point != 0) { p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len); *s++ = exponent_is_negative ? '-' : '+'; /* * Make sure the exponent has at least 2 digits */ if (t_len == 1) *s++ = '0'; while (t_len--) *s++ = *p++; } else { *s++ = '+'; *s++ = '0'; *s++ = '0'; } } *len = s - buf; return (buf); } /* * Convert num to a base X number where X is a power of 2. nbits determines X. * For example, if nbits is 3, we do base 8 conversion * Return value: * a pointer to a string containing the number * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char * conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len) { register int mask = (1 << nbits) - 1; register char *p = buf_end; static char low_digits[] = "0123456789abcdef"; static char upper_digits[] = "0123456789ABCDEF"; register char *digits = (format == 'X') ? upper_digits : low_digits; do { *--p = digits[num & mask]; num >>= nbits; } while (num); *len = buf_end - p; return (p); } /* * Do format conversion placing the output in buffer */ static int format_converter(register buffy * odp, const char *fmt, va_list ap) { register char *sp; register char *bep; register int cc = 0; register int i; register char *s = NULL; char *q; int s_len; register int min_width = 0; int precision = 0; enum { LEFT, RIGHT } adjust; char pad_char; char prefix_char; double fp_num; wide_int i_num = (wide_int) 0; u_wide_int ui_num; char num_buf[NUM_BUF_SIZE]; char char_buf[2]; /* for printing %% and % */ /* * Flag variables */ boolean_e is_long; boolean_e alternate_form; boolean_e print_sign; boolean_e print_blank; boolean_e adjust_precision; boolean_e adjust_width; bool_int is_negative; s_len = 0; sp = odp->nextb; bep = odp->buf_end; while (*fmt) { if (*fmt != '%') { INS_CHAR(*fmt, sp, bep, cc); } else { /* * Default variable settings */ adjust = RIGHT; alternate_form = print_sign = print_blank = NO; pad_char = ' '; prefix_char = NUL; fmt++; /* * Try to avoid checking for flags, width or precision */ if (isascii((int)*fmt) && !islower((int)*fmt)) { /* * Recognize flags: -, #, BLANK, + */ for (;; fmt++) { if (*fmt == '-') adjust = LEFT; else if (*fmt == '+') print_sign = YES; else if (*fmt == '#') alternate_form = YES; else if (*fmt == ' ') print_blank = YES; else if (*fmt == '0') pad_char = '0'; else break; } /* * Check if a width was specified */ if (isdigit((int)*fmt)) { STR_TO_DEC(fmt, min_width); adjust_width = YES; } else if (*fmt == '*') { min_width = va_arg(ap, int); fmt++; adjust_width = YES; if (min_width < 0) { adjust = LEFT; min_width = -min_width; } } else adjust_width = NO; /* * Check if a precision was specified * * XXX: an unreasonable amount of precision may be specified * resulting in overflow of num_buf. Currently we * ignore this possibility. */ if (*fmt == '.') { adjust_precision = YES; fmt++; if (isdigit((int)*fmt)) { STR_TO_DEC(fmt, precision); } else if (*fmt == '*') { precision = va_arg(ap, int); fmt++; if (precision < 0) precision = 0; } else precision = 0; } else adjust_precision = NO; } else adjust_precision = adjust_width = NO; /* * Modifier check */ if (*fmt == 'l') { is_long = YES; fmt++; } else is_long = NO; /* * Argument extraction and printing. * First we determine the argument type. * Then, we convert the argument to a string. * On exit from the switch, s points to the string that * must be printed, s_len has the length of the string * The precision requirements, if any, are reflected in s_len. * * NOTE: pad_char may be set to '0' because of the 0 flag. * It is reset to ' ' by non-numeric formats */ switch (*fmt) { case 'u': if (is_long) i_num = va_arg(ap, u_wide_int); else i_num = (wide_int) va_arg(ap, unsigned int); /* * The rest also applies to other integer formats, so fall * into that case. */ case 'd': case 'i': /* * Get the arg if we haven't already. */ if ((*fmt) != 'u') { if (is_long) i_num = va_arg(ap, wide_int); else i_num = (wide_int) va_arg(ap, int); }; s = conv_10(i_num, (*fmt) == 'u', &is_negative, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (*fmt != 'u') { if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; } break; case 'o': if (is_long) ui_num = va_arg(ap, u_wide_int); else ui_num = (u_wide_int) va_arg(ap, unsigned int); s = conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && *s != '0') { *--s = '0'; s_len++; } break; case 'x': case 'X': if (is_long) ui_num = (u_wide_int) va_arg(ap, u_wide_int); else ui_num = (u_wide_int) va_arg(ap, unsigned int); s = conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && i_num != 0) { *--s = *fmt; /* 'x' or 'X' */ *--s = '0'; s_len += 2; } break; case 's': s = va_arg(ap, char *); if (s != NULL) { s_len = strlen(s); if (adjust_precision && precision < s_len) s_len = precision; } else { s = S_NULL; s_len = S_NULL_LEN; } pad_char = ' '; break; case 'f': case 'e': case 'E': fp_num = va_arg(ap, double); s = conv_fp(*fmt, fp_num, alternate_form, (adjust_precision == NO) ? FLOAT_DIGITS : precision, &is_negative, &num_buf[1], &s_len); if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; break; case 'g': case 'G': if (adjust_precision == NO) precision = FLOAT_DIGITS; else if (precision == 0) precision = 1; /* * * We use &num_buf[ 1 ], so that we have room for the sign */ s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1]); if (*s == '-') prefix_char = *s++; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; s_len = strlen(s); if (alternate_form && (q = strchr(s, '.')) == NULL) s[s_len++] = '.'; if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) *q = 'E'; break; case 'c': char_buf[0] = (char) (va_arg(ap, int)); s = &char_buf[0]; s_len = 1; pad_char = ' '; break; case '%': char_buf[0] = '%'; s = &char_buf[0]; s_len = 1; pad_char = ' '; break; case 'n': *(va_arg(ap, int *)) = cc; break; /* * Always extract the argument as a "char *" pointer. We * should be using "void *" but there are still machines * that don't understand it. * If the pointer size is equal to the size of an unsigned * integer we convert the pointer to a hex number, otherwise * we print "%p" to indicate that we don't handle "%p". */ case 'p': ui_num = (u_wide_int) va_arg(ap, char *); if (sizeof(char *) <= sizeof(u_wide_int)) s = conv_p2(ui_num, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); else { s = "%p"; s_len = 2; } pad_char = ' '; break; case NUL: /* * The last character of the format string was %. * We ignore it. */ continue; /* * The default case is for unrecognized %'s. * We print % to help the user identify what * option is not understood. * This is also useful in case the user wants to pass * the output of format_converter to another function * that understands some other % (like syslog). * Note that we can't point s inside fmt because the * unknown could be preceded by width etc. */ default: char_buf[0] = '%'; char_buf[1] = *fmt; s = char_buf; s_len = 2; pad_char = ' '; break; } if (prefix_char != NUL) { *--s = prefix_char; s_len++; } if (adjust_width && adjust == RIGHT && min_width > s_len) { if (pad_char == '0' && prefix_char != NUL) { INS_CHAR(*s, sp, bep, cc) s++; s_len--; min_width--; } PAD(min_width, s_len, pad_char); } /* * Print the string s. */ for (i = s_len; i != 0; i--) { INS_CHAR(*s, sp, bep, cc); s++; } if (adjust_width && adjust == LEFT && min_width > s_len) PAD(min_width, s_len, pad_char); } fmt++; } odp->nextb = sp; return (cc); } /* * This is the general purpose conversion function. */ static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) { buffy od; int cc; /* * First initialize the descriptor * Notice that if no length is given, we initialize buf_end to the * highest possible address. */ od.buf_end = len ? &buf[len] : (char *) ~0; od.nextb = buf; /* * Do the conversion */ cc = format_converter(&od, format, ap); if (len == 0 || od.nextb <= od.buf_end) *(od.nextb) = '\0'; if (ccp) *ccp = cc; } int ap_snprintf(char *buf, size_t len, const char *format,...) { int cc; va_list ap; va_start(ap, format); strx_printv(&cc, buf, (len - 1), format, ap); va_end(ap); return (cc); } int ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap) { int cc; strx_printv(&cc, buf, (len - 1), format, ap); return (cc); } #endif /* HAVE_SNPRINTF */ opensips-2.2.2/modules/jabber/xstream.c000066400000000000000000000133021300170765700201060ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" /* xode_stream is a way to have a consistent method of handling incoming XML Stream based events... it doesn't handle the generation of an XML Stream, but provides some facilities to help do that */ static void _xode_put_expatattribs(xode owner, const char** atts) { int i = 0; if (atts == NULL) return; while (atts[i] != '\0') { xode_put_attrib(owner, atts[i], atts[i+1]); i += 2; } } /******* internal expat callbacks *********/ static void _xode_stream_startElement(xode_stream xs, const char* name, const char** atts) { xode_pool p; /* if xode_stream is bad, get outa here */ if(xs->status > XODE_STREAM_NODE) return; if(xs->node == NULL) { p = xode_pool_heap(5*1024); /* 5k, typically 1-2k each plus copy of self and workspace */ xs->node = xode_new_frompool(p,name); _xode_put_expatattribs(xs->node, atts); if(xs->status == XODE_STREAM_ROOT) { xs->status = XODE_STREAM_NODE; /* flag status that we're processing nodes now */ (xs->f)(XODE_STREAM_ROOT, xs->node, xs->arg); /* send the root, f must free all nodes */ xs->node = NULL; } }else{ xs->node = xode_insert_tag(xs->node, name); _xode_put_expatattribs(xs->node, atts); } /* depth check */ xs->depth++; if(xs->depth > XODE_STREAM_MAXDEPTH) xs->status = XODE_STREAM_ERROR; } static void _xode_stream_endElement(xode_stream xs, const char* name) { xode parent; /* if xode_stream is bad, get outa here */ if(xs->status > XODE_STREAM_NODE) return; /* if it's already NULL we've received , tell the app and we're outta here */ if(xs->node == NULL) { xs->status = XODE_STREAM_CLOSE; (xs->f)(XODE_STREAM_CLOSE, NULL, xs->arg); }else{ parent = xode_get_parent(xs->node); /* we are the top-most node, feed to the app who is responsible to delete it */ if(parent == NULL) (xs->f)(XODE_STREAM_NODE, xs->node, xs->arg); xs->node = parent; } xs->depth--; } static void _xode_stream_charData(xode_stream xs, const char *str, int len) { /* if xode_stream is bad, get outa here */ if(xs->status > XODE_STREAM_NODE) return; if(xs->node == NULL) { /* we must be in the root of the stream where CDATA is irrelevant */ return; } xode_insert_cdata(xs->node, str, len); } static void _xode_stream_cleanup(void *arg) { xode_stream xs = (xode_stream)arg; xode_free(xs->node); /* cleanup anything left over */ XML_ParserFree(xs->parser); } /* creates a new xode_stream with given pool, xode_stream will be cleaned up w/ pool */ xode_stream xode_stream_new(xode_pool p, xode_stream_onNode f, void *arg) { xode_stream newx; if(p == NULL || f == NULL) { fprintf(stderr,"Fatal Programming Error: xode_streamnew() was improperly called with NULL.\n"); return NULL; } newx = xode_pool_malloco(p, sizeof(_xode_stream)); newx->p = p; newx->f = f; newx->arg = arg; /* create expat parser and ensure cleanup */ newx->parser = XML_ParserCreate(NULL); XML_SetUserData(newx->parser, (void *)newx); XML_SetElementHandler(newx->parser, (void (*)(void*, const char*, const char**))_xode_stream_startElement, (void (*)(void*, const char*))_xode_stream_endElement); XML_SetCharacterDataHandler(newx->parser, (void (*)(void*, const char*, int))_xode_stream_charData); xode_pool_cleanup(p, _xode_stream_cleanup, (void *)newx); return newx; } /* attempts to parse the buff onto this stream firing events to the handler, returns the last known status */ int xode_stream_eat(xode_stream xs, char *buff, int len) { char *err; xode xerr; static char maxerr[] = "maximum node size reached"; static char deeperr[] = "maximum node depth reached"; if(xs == NULL) { fprintf(stderr,"Fatal Programming Error: xode_streameat() was improperly called with NULL.\n"); return XODE_STREAM_ERROR; } if(len == 0 || buff == NULL) return xs->status; if(len == -1) /* easy for hand-fed eat calls */ len = strlen(buff); if(!XML_Parse(xs->parser, buff, len, 0)) { err = (char *)XML_ErrorString(XML_GetErrorCode(xs->parser)); xs->status = XODE_STREAM_ERROR; }else if(xode_pool_size(xode_get_pool(xs->node)) > XODE_STREAM_MAXNODE || xs->cdata_len > XODE_STREAM_MAXNODE){ err = maxerr; xs->status = XODE_STREAM_ERROR; }else if(xs->status == XODE_STREAM_ERROR){ /* set within expat handlers */ err = deeperr; }else{ err = deeperr; } /* fire parsing error event, make a node containing the error string */ if(xs->status == XODE_STREAM_ERROR) { xerr = xode_new("error"); xode_insert_cdata(xerr,err,-1); (xs->f)(XODE_STREAM_ERROR, xerr, xs->arg); } return xs->status; } opensips-2.2.2/modules/json/000077500000000000000000000000001300170765700160045ustar00rootroot00000000000000opensips-2.2.2/modules/json/Makefile000066400000000000000000000015371300170765700174520ustar00rootroot00000000000000# $Id: Makefile 5862 2009-07-15 10:06:05Z andreidragus $ # # Memcached implementation for memcache API. # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=json.so ifeq ($(CROSS_COMPILE),) JSON_BUILDER = $(shell \ if pkg-config --exists json; then \ echo 'pkg-config json'; \ elif pkg-config --exists json-c; then\ echo 'pkg-config json-c'; \ fi) endif ifeq ($(JSON_BUILDER),) DEFS += -I$(LOCALBASE)/include -I$(SYSBASE)/include LIBS += -L$(LOCALBASE)/lib -ljson else JSON_LIB_VER = $(shell $(JSON_BUILDER) --modversion | sed "s/\.\([0-9]\)\./.0\1./g" | sed "s/\.\([0-9]\)\$$/.0\1/g" | tr -d "." | sed -e "s/^0*//" ) DEFS += $(shell $(JSON_BUILDER) --cflags) -DJSON_LIB_VERSION=$(JSON_LIB_VER) LIBS += $(shell $(JSON_BUILDER) --libs) endif include ../../Makefile.modules opensips-2.2.2/modules/json/README000066400000000000000000000205661300170765700166750ustar00rootroot00000000000000JSON Module Andrei Dragus OpenSIPS Solutions Edited by Andrei Dragus Copyright © 2009 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.4. Exported Variables 1.4.1. Variable lifetime 1.4.2. Accessing the $json(id) variable 1.4.3. Returned values from $json(id) 1.4.4. Operators for the $json(id) variable 1.5. Exported Functions 1.5.1. json_link("$json(dest_id)", "$json(source_id)") List of Examples 1.1. Accessing the $json variable 1.2. Iterating through an array using variables 1.3. Appending integers to arrays 1.4. Deleting the last element in an array 1.5. Adding a string value to a json object 1.6. Initializing an array 1.7. Setting a boolean or null value 1.8. Adding a json to another json 1.9. Creating a reference 1.10. [LOGICAL ERROR] Creating a circular reference Chapter 1. Admin Guide 1.1. Overview This module introduces a new type of variable that provides both serialization and de-serialization from JSON format. The variable provides ways to access objects and arrays to add,replace or delete values from the script. The correct approach is to consider a json object as a hashtable ( you can put (key;value) pairs, and you can delete and get values by key) and a json array as an array ( you can append, delete and replace values). Since the JSON format can have objects inside other objects you can have multiple nested hashtables or arrays and you can access these using paths. 1.2. Dependencies 1.2.1. OpenSIPS Modules This module does not depend on other modules. 1.2.2. External Libraries or Applications * libjson The libjson C library can be downloaded from: http://oss.metaparadigm.com/json-c/ 1.3. Exported Parameters The module does not export any parameters. 1.4. Exported Variables This module exports the $json(id) variable. The json variable provides methods to access fields in json objects and indexes in json arrays. 1.4.1. Variable lifetime The json variables will be available to the process that created them from the moment they were initialized. They will not reset per message or per transaction. If you want to use the on a per message basis you should initialize them each time. 1.4.2. Accessing the $json(id) variable The grammar that describes the id is: id = name(identifier)* identifier = key | index key = /string | /$var index = [integer] | [$var] | [] The "[]" index represents appending to the array. It should only be used when trying to set a value and not when trying to get one. Negative indexes can be used to access an array starting from the end. So "[-1]" signifies the last element. IMPORTANT: The id strictly complies to this grammar. You should be careful when using spaces because they will NOT be ignored. This was done to allow keys that contain spaces. Variables can be used as indexes or keys. Variables that will be used as indexes must contain integer values. Variables that will be used as keys should contain string values. Trying to get a value from a non-existing path (key or value) will return the NULL value and notice messages will be placed in the log describing the value of the json and the path used. Trying to replace or insert a value in a non-existing path will cause an error in setting the value and notice messages will be printed in the log describing the value of the json and the path used Example 1.1. Accessing the $json variable ... $json(obj1/key) = "value"; #replace or insert the (key,value) #pair into the json object; $json(matrix1[1][2]) = 1; #replace the element at index 2 in the elemen t #at index 1 in an array xlog("$json(name/key1[0][-1]/key2)"); # a more complex example ... Example 1.2. Iterating through an array using variables ... $json(ar1) := "[1,2,3,4]"; $var(i) = 0; while( $json(ar1[$var(i)]) ) { #print each value xlog("Found:[$json(ar1[$var(i)])]\n"); #increment each value $json(ar1[$var(i)]) = $json(ar1[$var(i)]) + 1 ; $var(i) = $var(i) + 1; } ... 1.4.3. Returned values from $json(id) If the value specified by the id is an integer it will be returned as an integer value. If the value specified by the id is a string it will be returned as a string. If the value specified by the id is any other type of json ( null, boolean, object, array ) the serialized version of the object will be returned as a string value. Using this and the ":=" operator you can duplicate json objects and put them in other json objects ( for string or integer you may use the "=" operator). If the id does not exist a NULL value will be returned. 1.4.4. Operators for the $json(id) variable There are 2 operators available for this variable. 1.4.4.1. The "=" operator This will cause the value to be taken as is and be added to the json object ( e.g. string value or integer value ). Setting a value to NULL will cause it to be deleted. Example 1.3. Appending integers to arrays ... $json(array1[]) = 1; ... Example 1.4. Deleting the last element in an array ... $json(array1[-1]) = NULL; ... Example 1.5. Adding a string value to a json object ... $json(object1/some_key) = "some_value"; ... 1.4.4.2. The ":=" operator This will cause the value to be taken and interpreted as a json object ( e.g. this operator should be used to parse json inputs ). Example 1.6. Initializing an array ... $json(array1) := "[]"; ... Example 1.7. Setting a boolean or null value ... $json(array1[]) := "null"; $json(array1[]) := "true"; $json(array1[]) := "false"; ... Example 1.8. Adding a json to another json ... $json(array) := "[1,2,3]"; $json(object) := "{}"; $json(object/array) := $json(array) ; ... 1.5. Exported Functions 1.5.1. json_link("$json(dest_id)", "$json(source_id)") This function can be used to link json objects together. This will work simillar to setting a value to an object, the only difference is that the second object is not copied, only a reference is created. Changes to any of the objects will be visible in both of them. You can use this method either to create references so each time you access the field you don't have to go through the full path (for speed efficiency and shorter code), or if you have an object that must be added to many other objects and you don't want to copy it each time (space and speed efficiency). You can think of this object exactly as a reference in an object-oriented language. Modifying fields referenced by the variable will cause modifications in all the objects, BUT modifying the variable itsef will not cause any changes to other objects. WARNING: You should be carefull when using references. If you accidentally create a circular reference and try to get the value from the object you will crash OPENSIPS. Example 1.9. Creating a reference ... $json(b) := "[{},{},{}]"; json_link("$json(stub)","$json(b[0])"); $json(stub/ana) = "are"; #add to the stub $json(stub/ar) := "[]"; $json(stub/ar[]) = 1; $json(stub/ar[]) = 2; $json(stub/ar[]) = 3; $json(b[0]/ar[0]) = NULL; # delete from the original object xlog("\nTest link :\n$json(stub)\n$json(b)\n\n"); /*Output: Test link : { "ana": "are", "ar": [ 2, 3 ] } [ { "ana": "are", "ar": [ 2, 3 ] }, { }, { } ] */ $json(stub) = NULL; #delete the stub, no change will happen to the sourc e xlog("\nTest link :\n$json(stub)\n$json(b)\n\n"); /* Output: Test link : [ { "ana": "are", "ar": [ 2, 3 ] }, { }, { } ] */ ... Example 1.10. [LOGICAL ERROR] Creating a circular reference ... $json(b) := "[1]"; /* NEVER do this, it is meant only to show where problems might occur * / json_link("$json(b[0])","$json(b)"); # replace 1 with a reference to b xlog("\nTest link :\n$json(stub)\n$json(b)\n\n"); /* this will cause OPENSIPS to crash because it will continuously try to get b, then b[0], then b ... */ ... opensips-2.2.2/modules/json/array_del.c000066400000000000000000000024461300170765700201200ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Andrei Dragus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2009-09-04 first version (andreidragus) */ #include #include void array_list_del_idx( struct array_list * arr, int idx) { int i; if( idx >= arr->length) return; arr->free_fn(arr->array[idx]); arr->length--; for( i=idx; ilength; i++ ) arr->array[i] = arr->array[i+1]; }; void json_object_array_del(struct json_object* obj, int idx) { array_list_del_idx(obj->o.c_array, idx); }; opensips-2.2.2/modules/json/doc/000077500000000000000000000000001300170765700165515ustar00rootroot00000000000000opensips-2.2.2/modules/json/doc/json.xml000066400000000000000000000017321300170765700202470ustar00rootroot00000000000000 %docentities; ]> JSON Module &osipsname; Andrei Dragus &osipssolname; Andrei Dragus 2009 &voicesystem; $Revision: 8740 $ $Date$ &admin; opensips-2.2.2/modules/json/doc/json_admin.xml000066400000000000000000000225311300170765700214170ustar00rootroot00000000000000 &adminguide;
Overview This module introduces a new type of variable that provides both serialization and de-serialization from JSON format. The variable provides ways to access objects and arrays to add,replace or delete values from the script. The correct approach is to consider a json object as a hashtable ( you can put (key;value) pairs, and you can delete and get values by key) and a json array as an array ( you can append, delete and replace values). Since the JSON format can have objects inside other objects you can have multiple nested hashtables or arrays and you can access these using paths.
Dependencies
&osips; Modules This module does not depend on other modules.
External Libraries or Applications libjson The libjson C library can be downloaded from: http://oss.metaparadigm.com/json-c/
Exported Parameters The module does not export any parameters.
Exported Variables This module exports the $json(id) variable. The json variable provides methods to access fields in json objects and indexes in json arrays.
Variable lifetime The json variables will be available to the process that created them from the moment they were initialized. They will not reset per message or per transaction. If you want to use the on a per message basis you should initialize them each time.
Accessing the $json(id) variable The grammar that describes the id is: id = name(identifier)* identifier = key | index key = /string | /$var index = [integer] | [$var] | [] The "[]" index represents appending to the array. It should only be used when trying to set a value and not when trying to get one. Negative indexes can be used to access an array starting from the end. So "[-1]" signifies the last element. IMPORTANT: The id strictly complies to this grammar. You should be careful when using spaces because they will NOT be ignored. This was done to allow keys that contain spaces. Variables can be used as indexes or keys. Variables that will be used as indexes must contain integer values. Variables that will be used as keys should contain string values. Trying to get a value from a non-existing path (key or value) will return the NULL value and notice messages will be placed in the log describing the value of the json and the path used. Trying to replace or insert a value in a non-existing path will cause an error in setting the value and notice messages will be printed in the log describing the value of the json and the path used Accessing the $json variable ... $json(obj1/key) = "value"; #replace or insert the (key,value) #pair into the json object; $json(matrix1[1][2]) = 1; #replace the element at index 2 in the element #at index 1 in an array xlog("$json(name/key1[0][-1]/key2)"); # a more complex example ... Iterating through an array using variables ... $json(ar1) := "[1,2,3,4]"; $var(i) = 0; while( $json(ar1[$var(i)]) ) { #print each value xlog("Found:[$json(ar1[$var(i)])]\n"); #increment each value $json(ar1[$var(i)]) = $json(ar1[$var(i)]) + 1 ; $var(i) = $var(i) + 1; } ...
Returned values from $json(id) If the value specified by the id is an integer it will be returned as an integer value. If the value specified by the id is a string it will be returned as a string. If the value specified by the id is any other type of json ( null, boolean, object, array ) the serialized version of the object will be returned as a string value. Using this and the ":=" operator you can duplicate json objects and put them in other json objects ( for string or integer you may use the "=" operator). If the id does not exist a NULL value will be returned.
Operators for the $json(id) variable There are 2 operators available for this variable.
The "=" operator This will cause the value to be taken as is and be added to the json object ( e.g. string value or integer value ). Setting a value to NULL will cause it to be deleted. Appending integers to arrays ... $json(array1[]) = 1; ... Deleting the last element in an array ... $json(array1[-1]) = NULL; ... Adding a string value to a json object ... $json(object1/some_key) = "some_value"; ...
The ":=" operator This will cause the value to be taken and interpreted as a json object ( e.g. this operator should be used to parse json inputs ). Initializing an array ... $json(array1) := "[]"; ... Setting a boolean or null value ... $json(array1[]) := "null"; $json(array1[]) := "true"; $json(array1[]) := "false"; ... Adding a json to another json ... $json(array) := "[1,2,3]"; $json(object) := "{}"; $json(object/array) := $json(array) ; ...
Exported Functions
<function moreinfo="none"> json_link("$json(dest_id)", "$json(source_id)") </function> This function can be used to link json objects together. This will work simillar to setting a value to an object, the only difference is that the second object is not copied, only a reference is created. Changes to any of the objects will be visible in both of them. You can use this method either to create references so each time you access the field you don't have to go through the full path (for speed efficiency and shorter code), or if you have an object that must be added to many other objects and you don't want to copy it each time (space and speed efficiency). You can think of this object exactly as a reference in an object-oriented language. Modifying fields referenced by the variable will cause modifications in all the objects, BUT modifying the variable itsef will not cause any changes to other objects. WARNING: You should be carefull when using references. If you accidentally create a circular reference and try to get the value from the object you will crash OPENSIPS. Creating a reference ... $json(b) := "[{},{},{}]"; json_link("$json(stub)","$json(b[0])"); $json(stub/ana) = "are"; #add to the stub $json(stub/ar) := "[]"; $json(stub/ar[]) = 1; $json(stub/ar[]) = 2; $json(stub/ar[]) = 3; $json(b[0]/ar[0]) = NULL; # delete from the original object xlog("\nTest link :\n$json(stub)\n$json(b)\n\n"); /*Output: Test link : { "ana": "are", "ar": [ 2, 3 ] } [ { "ana": "are", "ar": [ 2, 3 ] }, { }, { } ] */ $json(stub) = NULL; #delete the stub, no change will happen to the source xlog("\nTest link :\n$json(stub)\n$json(b)\n\n"); /* Output: Test link : <null> [ { "ana": "are", "ar": [ 2, 3 ] }, { }, { } ] */ ... [LOGICAL ERROR] Creating a circular reference ... $json(b) := "[1]"; /* NEVER do this, it is meant only to show where problems might occur */ json_link("$json(b[0])","$json(b)"); # replace 1 with a reference to b xlog("\nTest link :\n$json(stub)\n$json(b)\n\n"); /* this will cause OPENSIPS to crash because it will continuously try to get b, then b[0], then b ... */ ...
opensips-2.2.2/modules/json/json.c000066400000000000000000000406601300170765700171270ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Andrei Dragus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2009-09-04 first version (andreidragus) */ #include #include #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../pvar.h" #include "../../mod_fix.h" #include "../../script_cb.h" #include "../../script_var.h" #include "../../mem/mem.h" #include "../../mi/mi.h" #include "../tm/tm_load.h" #include "../rr/api.h" #include #include #define JSON_BUFF_SIZE 4096 enum { TAG_KEY = 1, TAG_IDX = 2, TAG_VAR = 4, TAG_END = 8 }; typedef struct json_object json_t; typedef struct _pv_json { str name; json_t * data; struct _pv_json * next; }pv_json_t; typedef struct _tag_list { int type; str key; int idx; pv_spec_t var; struct _tag_list * next; }json_tag; typedef struct _json_name { str name; json_tag * tags; json_tag ** end; }json_name; pv_json_t * all; char buff[JSON_BUFF_SIZE]; static int mod_init(void); static int child_init(int ); static void mod_destroy(void); void json_object_array_del(struct json_object* , int ); static int fixup_json_bind(void**, int ); static int pv_set_json (struct sip_msg*, pv_param_t*, int , pv_value_t* ); static int pv_get_json (struct sip_msg*, pv_param_t*, pv_value_t* ); static int json_bind(struct sip_msg* , char* , char* ); static void print_tag_list( json_tag *, json_tag *, int); static json_t * get_object(pv_json_t * , pv_param_t* , json_tag **, int ); static int pv_parse_json_name (pv_spec_p , str *); static pv_json_t * get_pv_json (pv_param_t* ); static int pv_add_json ( pv_param_t* , json_t * ); static int expand_tag_list( struct sip_msg*, json_tag *); static cmd_export_t cmds[]={ {"json_link", (cmd_function)json_bind, 2, fixup_json_bind, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {0,0,0,0,0,0} }; static pv_export_t mod_items[] = { { {"json", sizeof("json")-1}, PVT_JSON, pv_get_json, pv_set_json, pv_parse_json_name, 0, 0, 0}, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; struct module_exports exports= { "json", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ 0, /* param exports */ 0, /* exported statistics */ 0, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, child_init /* per-child init function */ }; int json_bind(struct sip_msg* msg, char* s1, char* s2) { pv_spec_t * src, * dest; pv_json_t * var ; json_t * obj; json_name * id ; pv_param_t *pvp; src = (pv_spec_t *) s2; dest = (pv_spec_t *) s1; pvp = &src->pvp; id = (json_name *) pvp->pvn.u.dname; var = get_pv_json(pvp); if( var == NULL ) { LM_ERR("Variable named:%.*s not found\n",id->name.len,id->name.s); return -1; } obj = get_object(var, pvp, NULL, 1); if( obj == NULL ) { LM_NOTICE("Could not find object with that path\n"); return -1; } json_object_get(obj); if( pv_add_json( &dest->pvp, obj ) ) return -1; return 1; }; int fixup_json_bind(void** param, int param_no) { pv_spec_t * var; char * ret; str s; s.s = *param; s.len = strlen(s.s); var = (pv_spec_t *)pkg_malloc(sizeof(pv_spec_t)); if( var == NULL ) { LM_ERR("Out of memory\n"); return -1; } ret = pv_parse_spec(&s,var); if( ret == NULL ) { LM_ERR("Parse error\n"); return -1; } if( var->type != PVT_JSON ) { LM_ERR("Parameter no: %d must be a json variable\n",param_no); return -1; } *param = var; return 0; } struct json_object* json_parse(const char *str,int len,enum json_tokener_error *status) { struct json_tokener* tok; struct json_object* obj; tok = json_tokener_new(); obj = json_tokener_parse_ex(tok, str, len); if( tok-> err == json_tokener_continue ) obj = json_tokener_parse_ex(tok, "", -1); if(tok->err != json_tokener_success) { obj = NULL; if (status) *status = tok->err; } json_tokener_free(tok); return obj; } /* returns the variable designated by pvp */ pv_json_t * get_pv_json (pv_param_t* pvp) { pv_json_t * cur; json_name * id = (json_name *) pvp->pvn.u.dname; cur = all; while( cur ) { if( cur->name.len == id->name.len && !strncmp(cur->name.s,id->name.s,cur->name.len) ) break; cur = cur->next ; } return cur; } json_t * get_object(pv_json_t * var, pv_param_t* pvp , json_tag ** tag, int report_err ) { json_name * id = (json_name *) pvp->pvn.u.dname; json_t * cur_obj, * last_obj = 0; json_tag * cur_tag, * last_tag = 0; int poz; cur_tag = id->tags; cur_obj = var->data; while( cur_tag ) { last_tag = cur_tag; last_obj = cur_obj; if( cur_tag->type & TAG_KEY ) { memcpy( buff, cur_tag->key.s, cur_tag->key.len ); buff[cur_tag->key.len] = 0; if( cur_obj == NULL || !json_object_is_type( cur_obj, json_type_object ) ) goto error; #if JSON_LIB_VERSION < 10 cur_obj = json_object_object_get( cur_obj, buff ); if( cur_obj == NULL && tag == NULL) goto error; #else if (!json_object_object_get_ex( cur_obj,buff, &cur_obj ) && tag == NULL) goto error; #endif } if( cur_tag->type & TAG_IDX ) { if( cur_obj == NULL || !json_object_is_type( cur_obj, json_type_array ) ) goto error; poz = cur_tag->idx; if( cur_tag->type & TAG_END ) { poz = json_object_array_length(cur_obj); } else { if( poz < 0 ) poz += json_object_array_length(cur_obj); } if( poz < 0 ) goto error; cur_obj = json_object_array_get_idx( cur_obj, poz ); if( cur_obj == NULL && tag == NULL) goto error; } cur_tag = cur_tag->next; } if( tag == NULL ) { return cur_obj; } else { *tag = last_tag; return last_obj; } error: if( report_err) { LM_NOTICE("Trying to get a value from a json of incorrect type\n"); if(var->data) LM_NOTICE("Object is:\n%s\n", json_object_to_json_string(var->data)); else LM_NOTICE("Object is null\n"); print_tag_list( id->tags, cur_tag->next, 1); } return NULL; } int pv_get_json (struct sip_msg* msg, pv_param_t* pvp, pv_value_t* val) { pv_json_t * var ; json_t * obj; json_name * id = (json_name *) pvp->pvn.u.dname; UNUSED(id); if( expand_tag_list( msg, ((json_name *)pvp->pvn.u.dname)->tags ) < 0) { LM_ERR("Cannot expand variables in path\n"); return pv_get_null( msg, pvp, val); } var = get_pv_json(pvp); if( var == NULL ) { /* this is not an error - we simply came across a json spec * pointing a json var which was never set/init */ LM_DBG("Variable named:%.*s not found\n",id->name.len,id->name.s); return pv_get_null( msg, pvp, val); } obj = get_object(var, pvp, NULL, 0); memset(val, 0, sizeof(pv_value_t)); if( obj == NULL ) return pv_get_null( msg, pvp, val); if( json_object_is_type(obj, json_type_int) ) { val->rs.s = sint2str(json_object_get_int(obj), &val->rs.len); val->ri = json_object_get_int(obj);; val->flags |= PV_VAL_INT|PV_TYPE_INT|PV_VAL_STR; } else if( json_object_is_type(obj, json_type_string)) { val->flags = PV_VAL_STR; val->rs.s = (char*)json_object_get_string( obj ); #if JSON_LIB_VERSION >= 10 val->rs.len = json_object_get_string_len( obj ); #else val->rs.len = strlen(val->rs.s); #endif } else { val->flags = PV_VAL_STR; val->rs.s = (char*)json_object_to_json_string( obj ); val->rs.len = strlen(val->rs.s); } return 0; } int pv_add_json ( pv_param_t* pvp, json_t * obj ) { json_t *dest; json_name * id; pv_json_t * var; json_tag * tag; int poz; id = (json_name *) pvp->pvn.u.dname; var = get_pv_json(pvp); if( var == NULL ) { if( id->tags ) { LM_ERR("Object is not initialized yet\n"); return -1; } var = (pv_json_t *) pkg_malloc(sizeof(pv_json_t)); if( var == NULL ) { LM_ERR("Out of memory\n"); return -1; } memset(var,0,sizeof(pv_json_t)); var->name = id->name; var->next = all; var->data = obj; all = var; return 0; } if( id ->tags == NULL) { if( var->data ) json_object_put(var->data); var->data = obj; return 0; } dest = get_object(var, pvp, &tag, 1); if( dest == NULL ) { LM_NOTICE("Could not find object with that path\n"); return -1; } if( tag->type & TAG_KEY ) { memcpy(buff,tag->key.s,tag->key.len); buff[tag->key.len] = 0; if( obj == NULL ) json_object_object_del(dest,buff); else json_object_object_add(dest,buff,obj); } if( tag->type & TAG_IDX ) { poz = tag->idx; if( tag->type & TAG_END ) { if( obj == NULL) { LM_ERR("Invalid parameter for deletion\n"); return -1; } json_object_array_add(dest,obj); return 0; } if( poz < 0 ) poz += json_object_array_length(dest); if( poz<0 || poz >= json_object_array_length(dest)) { LM_ERR("Attempting to replace at invalid index in array:%d\n", poz); return -1; } if( obj == NULL) { if( poz >= json_object_array_length(dest)) { LM_ERR("Index out of bounds for deletion\n"); return -1; } json_object_array_del(dest,poz); } else json_object_array_put_idx(dest,poz,obj); } return 0; } int pv_set_json (struct sip_msg* msg, pv_param_t* pvp, int flag , pv_value_t* val) { json_t * obj; enum json_tokener_error parse_status; if( expand_tag_list( msg, ((json_name *)pvp->pvn.u.dname)->tags ) < 0) { LM_ERR("Cannot expand variables in path\n"); return -1; } /* delete value */ if( val == NULL) { return pv_add_json(pvp,NULL); } /* If we want the value to be interpreted prepare the object */ if( flag == COLONEQ_T ) { if( ! (val->flags & PV_VAL_STR) ) { LM_ERR("Trying to interpret a non-string value\n"); return -1; } obj = json_parse( val->rs.s, val->rs.len,&parse_status); if (obj == NULL) { LM_ERR("Error parsing json: %s\n", #if JSON_LIB_VERSION >= 10 json_tokener_error_desc(parse_status) #else json_tokener_errors[(unsigned long)obj] #endif ); pv_add_json(pvp, NULL); return -1; } } else { if( val->flags & PV_VAL_INT ) { obj = json_object_new_int(val->ri); } else { obj = json_object_new_string_len( val->rs.s, val->rs.len); } } return pv_add_json(pvp,obj); } enum { ST_NAME = 0, ST_TEST = 1, ST_KEY = 2, ST_IDX = 3, ST_ERR = 4 }; int next[4][256]; int ignore[4][256]; int inited; int expand_tag_list( struct sip_msg* msg,json_tag * start) { json_tag * cur = start; pv_value_t val; memset(&val,0,sizeof(pv_value_t)); while(cur) { if( cur->type & TAG_VAR ) { if( pv_get_spec_value(msg, &cur->var ,&val) < 0) { LM_ERR("Unable to get value from variable\n"); return -1; } if( cur->type & TAG_IDX ) { if( !(val.flags & PV_VAL_INT) ) { LM_ERR("Non integer value in index\n"); return -1; } cur->idx = val.ri; } if( cur->type & TAG_KEY ) { if( !(val.flags & PV_VAL_STR) ) { LM_ERR("Non string value in key\n"); return -1; } cur->key = val.rs; } } cur = cur->next; } return 0; }; void print_tag_list( json_tag * start, json_tag * end, int err) { json_tag * cur = start; if( !err ) { if( start == NULL ) { LM_DBG("No tags were found\n"); } else { LM_DBG("Tag list:\n"); } while( cur != end ) { if( cur->type & TAG_KEY) LM_DBG("key=[%.*s]\n",cur->key.len,cur->key.s); if( cur->type & TAG_IDX) LM_DBG("idx=[%d]\n",cur->idx); cur = cur->next; } } else { if( start == NULL ) { LM_NOTICE("No tags were found\n"); } else { LM_NOTICE("Tag list:\n"); } while( cur != end ) { if( cur->type & TAG_KEY) LM_NOTICE("key=[%.*s]\n",cur->key.len,cur->key.s); if( cur->type & TAG_IDX) LM_NOTICE("idx=[%d]\n",cur->idx); cur = cur->next; } } } int get_value(int state, json_name * id, char *start, char * cur) { json_tag * node; char * i; int empty; str in; in.s = start; in.len = cur-start; if( state != ST_TEST ) LM_DBG("JSON tag type=%d value=%.*s\n",state,(int)(cur-start),start); switch(state) { case ST_NAME: id->name = in; break; case ST_TEST: break; case ST_KEY: node = (json_tag *) pkg_malloc(sizeof(json_tag)); if( node == NULL ) { LM_ERR("Out of memory\n"); return -1; } memset(node,0,sizeof(json_tag)); node->type = TAG_KEY; *id->end = node; id->end = &node->next; if( in.len > 0 && *start == '$' ) { if( pv_parse_spec(&in, &node->var) < 0 ) { LM_ERR("Unable to parse variable "); return -1; } node->type |= TAG_VAR; return 0; } node->key = in; break; case ST_IDX: node = (json_tag *) pkg_malloc(sizeof(json_tag)); if( node == NULL ) { LM_ERR("Out of memory\n"); return -1; } memset(node,0,sizeof(json_tag)); node->type = TAG_IDX; *id->end = node; id->end = &node->next; empty = 1; for( i=start; itype |= TAG_END; return 0; } if( *i == '$' ) { if( pv_parse_spec(&in, &node->var) < 0 ) { LM_ERR("Unable to parse variable "); return -1; } node->type |= TAG_VAR; return 0; } if( sscanf( start, "%d", &node->idx ) != 1) { LM_ERR("Index value is not an integer:[%.*s]\n", (int)(cur-start), start ); return -1; } break; } return 0; } void init_matrix(void) { int i,j; /* point each state to itself */ for( i=0; i<4; i++) for( j=0; j<256; j++) next[i][j] = i; next[ST_NAME][(unsigned int)'/'] = ST_TEST; next[ST_NAME][(unsigned int)'['] = ST_TEST; for( j=0; j<256; j++) next[ST_TEST][j] = ST_ERR; next[ST_TEST][(unsigned int)'['] = ST_IDX; next[ST_TEST][(unsigned int)'/'] = ST_KEY; next[ST_IDX][(unsigned int)']'] = ST_TEST; next[ST_KEY][(unsigned int)'['] = ST_TEST; next[ST_KEY][(unsigned int)'/'] = ST_TEST; /* set chars that will not be consumed */ for( j=0; j<256; j++) ignore[ST_TEST][j] = 1; ignore[ST_NAME][(unsigned int)'/'] = 1; ignore[ST_TEST][(unsigned int)'/'] = 0; ignore[ST_KEY][(unsigned int)'/'] = 1; ignore[ST_NAME][(unsigned int)'['] = 1; ignore[ST_TEST][(unsigned int)'['] = 0; ignore[ST_KEY][(unsigned int)'['] = 1; } int pv_parse_json_name (pv_spec_p sp, str *in) { json_name * id; char * cur,* start; int state,next_state,prev_state; if( !inited ) init_matrix(); id = (json_name *) pkg_malloc(sizeof(json_name)); if( id == NULL ) { LM_ERR("Out of memory\n"); return -1; } id->tags = NULL; id->end = &id->tags; state = ST_NAME; start = in->s; prev_state = -1; for( cur = in->s; cur < in->s + in->len; cur++) { next_state = next[state][(unsigned int)*cur]; if( next_state == ST_ERR) { LM_ERR("Unexpected char at position: %d in :(%.*s)\n", (int)(cur-in->s),in->len,in->s); return -1; } if( state != prev_state) start = cur; if( state != next_state) if ( get_value(state, id, start, cur) ) return -1; if( ignore[state][(unsigned int)*cur]) { cur --; } prev_state = state; state = next_state; } if( state == ST_IDX) { LM_ERR("Mismatched parenthesis in:(%.*s)\n",in->len,in->s); return -1; } if( get_value(state, id, start, cur) ) return -1; sp->pvp.pvn.u.dname = id ; sp->type = PVT_JSON; sp->getf = pv_get_json; sp->setf = pv_set_json; return 0; } int mod_init(void) { return 0; } int child_init(int rank) { return 0; } void mod_destroy(void) { } opensips-2.2.2/modules/ldap/000077500000000000000000000000001300170765700157535ustar00rootroot00000000000000opensips-2.2.2/modules/ldap/Makefile000066400000000000000000000007231300170765700174150ustar00rootroot00000000000000# $Id$ # # LDAP_H350 # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs #DEFS += -DLDAP_PERF auto_gen= NAME=ldap.so LIBS=-lldap ifeq ($(OS),solaris) DEFS+=-I$(LOCALBASE)/include -I/usr/sfw/include -I/usr/local/include -I/opt/sfw/include -I/opt/csw/include LIBS=-L$(LOCALBASE)/lib -L/usr/sfw/lib -L/usr/local/lib -L/opt/sfw/lib -I/opt/csw/lib -lldap endif include ../../Makefile.modules opensips-2.2.2/modules/ldap/README000066400000000000000000001134051300170765700166370ustar00rootroot00000000000000LDAP Module Christian Schlatter University of North Carolina Copyright © 2007 University of North Carolina Revision History Revision 0.9 05-17-2007 __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Usage Basics 1.1.2. LDAP URLs 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. LDAP Configuration File 1.3.1. Configuration File Syntax 1.3.2. LDAP Session Settings 1.3.3. Configuration File Example 1.4. Exported Parameters 1.4.1. config_file (string) 1.4.2. max_async_connections (int) 1.5. Exported Functions 1.5.1. ldap_search(ldap_url) 1.5.2. ldap_result("ldap_attr_name/avp_spec[/avp_type ]" [, regex_subst]) 1.5.3. ldap_result_check("ldap_attr_name/string_to_ma tch" [, regex_subst]) 1.5.4. ldap_result_next() 1.5.5. ldap_filter_url_encode(string, avp_spec) 1.6. Exported Async Functions 1.6.1. ldap_search(ldap_url) 1.7. Installation & Running 1.7.1. Compiling the Module 2. Developer Guide 2.1. Overview 2.2. API Functions 2.2.1. ldap_params_search 2.2.2. ldap_url_search 2.2.3. ldap_result_attr_vals 2.2.4. ldap_value_free_len 2.2.5. ldap_result_next 2.2.6. ldap_str2scope 2.2.7. ldap_rfc4515_escape 2.2.8. get_ldap_handle 2.2.9. get_last_ldap_result 2.3. Example Usage Resources List of Tables 1.1. RFC 4515 Escaping Rules 1.2. ldap_filter_url_encode() escaping rules List of Examples 1.1. ldap_server_url examples 1.2. ldap_version example 1.3. ldap_bind_dn example 1.4. ldap_bind_password example 1.5. ldap_network_timeout example 1.6. ldap_client_bind_timeout example 1.7. ldap_ca_cert_file example 1.8. ldap_cert_file example 1.9. ldap_key_file example 1.10. ldap_require_certificate example 1.11. Example LDAP Configuration File 1.12. config_file parameter usage 1.13. max_async_connections parameter usage 1.14. Example Usage of ldap_url 1.15. Example Usage 1.16. Example Usage 1.17. Example Usage 1.18. Example Usage 1.19. Example Usage 1.20. Example Usage of ldap_url 1.21. Example Usage 2.1. Example code fragment to load LDAP module API 2.2. Example LDAP module API function call Chapter 1. Admin Guide 1.1. Overview The LDAP module implements an LDAP search interface for OpenSIPS. It exports script functions to perform an LDAP search operation and to store the search results as OpenSIPS AVPs. This allows for using LDAP directory data in the OpenSIPS SIP message routing script. The following features are offered by the LDAP module: * LDAP search function taking an LDAP URL as input both synchronous and asynchronous * LDAP result parsing functions to store LDAP data as AVP * Support for accessing multiple LDAP servers * LDAP SIMPLE authentication * LDAP server failover and automatic reconnect * Configurable LDAP connection and bind timeouts * Module API for LDAP search operations that can be used by other OpenSIPS modules * StartTLS support The module implementation makes use of the open source OpenLDAP library available on most UNIX/Linux platforms. Besides LDAP server failover and automatic reconnect, this module can handle multiple LDAP sessions concurrently allowing to access data stored on different LDAP servers. Each OpenSIPS worker process maintains one LDAP TCP connection per configured LDAP server. This enables parallel execution of LDAP requests and offloads LDAP concurrency control to the LDAP server(s). An LDAP search module API is provided that can be used by other OpenSIPS modules. A module using this API does not have to implement LDAP connection management and configuration, while still having access to the full OpenLDAP API for searching and result handling. Since LDAP server implementations are optimized for fast read access they are a good choice to store SIP provisioning data. Performance tests have shown that this module achieves lower data access times and higher call rates than other database modules like e.g. the OpenSIPS MYSQL module. 1.1.1. Usage Basics First so called LDAP sessions have to be specified in an external configuration file (as described in Section 1.3, “LDAP Configuration Fileâ€). Each LDAP session includes LDAP server access parameters like server hostname or connection timeouts. Normally only a single LDAP session will be used unless there is a need to access more than one LDAP server. The LDAP session name will then be used in the OpenSIPS configuration script to refer to a specific LDAP session. The ldap_search function (Section 1.5.1, “ldap_search(ldap_url)â€) performs an LDAP search operation. It expects an LDAP URL as input which includes the LDAP session name and search parameters. Section 1.1.2, “LDAP URLs†provides a quick overview on LDAP URLs. The result of an LDAP search is stored internally and can be accessed with one of the ldap_result* functions. ldap_result (Section 1.5.2, “ldap_result("ldap_attr_name/avp_spec[/avp_type]" [, regex_subst])â€) stores resulting LDAP attribute value as AVPs. ldap_result_check (Section 1.5.3, “ldap_result_check("ldap_attr_name/string_to_match" [, regex_subst])â€) is a convenience function to compare a string with LDAP attribute values using regular expression matching. Finally, ldap_result_next (Section 1.5.4, “ldap_result_next()â€) allows to handle LDAP search queries that return more than one LDAP entry. All ldap_result* functions do always access the LDAP result set from the last ldap_search call. This should be kept in mind when calling ldap_search more than once in the OpenSIPS configuration script. 1.1.2. LDAP URLs ldap_search expects an LDAP URL as argument. This section describes the format and semantics of an LDAP URL. RFC 4516 [RFC4516] describes the format of an LDAP Uniform Resource Locator (URL). An LDAP URL represents an LDAP search operation in a compact format. The LDAP URL format is defined as follows (slightly modified, refer to section 2 of [RFC4516] for ABNF notation): ldap://[ldap_session_name][/dn?attrs[?scope[?filter]]]] ldap_session_name An LDAP session name as defined in the LDAP configuration file. (RFC 4516 defines this as LDAP hostport parameter) dn Base Distinguished Name (DN) of LDAP search or target of non-search operation, as defined in RFC 4514 [RFC4514] attrs Comma separated list of LDAP attributes to be returned scope Scope for LDAP search, valid values are “baseâ€, “oneâ€, or “sub†filter LDAP search filter definition following rules of RFC 4515 [RFC4515] Note The following table lists characters that have to be escaped in LDAP search filters: Table 1.1. RFC 4515 Escaping Rules * \2a ( \28 ) \29 \ \5c Note Non-URL characters in an LDAP URL have to be escaped using percent-encoding (refer to section 2.1 of RFC 4516). In particular this means that any "?" character in an LDAP URL component must be written as "%3F", since "?" is used as a URL delimiter. The exported function ldap_filter_url_encode (Section 1.5.5, “ldap_filter_url_encode(string, avp_spec)â€) implements RFC 4515/4516 LDAP search filter and URL escaping rules. 1.2. Dependencies 1.2.1. OpenSIPS Modules The module depends on the following modules (the listed modules must be loaded before this module): * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * OpenLDAP library (libldap) v2.1 or greater, libldap header files (libldap-dev) are needed for compilation 1.3. LDAP Configuration File The module reads an external confiuration file at module initialization time that includes LDAP session definitions. 1.3.1. Configuration File Syntax The configuration file follows the Windows INI file syntax, section names are enclosed in square brackets: [Section_Name] Any section can contain zero or more configuration key assignments of the form key = value ; comment Values can be given enclosed with quotes. If no quotes are present, the value is understood as containing all characters between the first and the last non-blank characters. Lines starting with a hash sign and blank lines are treated as comments. Each section describes one LDAP session that can be referred to in the OpenSIPS configuration script. Using the section name as the host part of an LDAP URL tells the module to use the LDAP session specified in the respective section. An example LDAP session specification looks like: [example_ldap] ldap_server_url = "ldap://ldap1.example.com, ldap://ldap2.exa mple.com" ldap_bind_dn = "cn=sip_proxy,ou=accounts,dc=example,dc=com " ldap_bind_password = "pwd" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 ldap_ca_cert_file = "/usr/share/ca-certificates/mycert. pem" ldap_cert_file = "/var/my-certificate/certificate.pe m" ldap_key_file = "/var/my-certificate/key.pem" ldap_require_certificate = "ALLOW" The configuration keys are explained in the following section. This LDAP session can be referred to in the routing script by using an LDAP URL like e.g. ldap://example_ldap/cn=admin,dc=example,dc=com 1.3.2. LDAP Session Settings ldap_server_url (mandatory) LDAP URL including fully qualified domain name or IP address of LDAP server optionally followed by a colon and TCP port to connect: ldap://[:]. Failover LDAP servers can be added, each separated by a comma. In the event of connection errors, the module tries to connect to servers in order of appearance. Default value: none, this is a mandatory setting Example 1.1. ldap_server_url examples ldap_server_url = "ldap://localhost" ldap_server_url = "ldap://ldap.example.com:7777" ldap_server_url = "ldap://ldap1.example.com, ldap://ldap2.example.com:80389" ldap_version (optional) Supported LDAP versions are 2 and 3. Default value: 3 (LDAPv3) Example 1.2. ldap_version example ldap_version = 2 ldap_bind_dn (optional) Authentication user DN used to bind to LDAP server (module currently only supports SIMPLE_AUTH). Empty string enables anonymous LDAP bind. Default value: Ҡ(empty string --> anonymous bind) Example 1.3. ldap_bind_dn example ldap_bind_dn = "cn=root,dc=example,dc=com"; ldap_bind_password (optional) Authentication password used to bind to LDAP server (SIMPLE_AUTH). Empty string enables anonymous bind. Default value: Ҡ(empty string --> anonymous bind) Example 1.4. ldap_bind_password example ldap_bind_password = "secret"; ldap_network_timeout (optional) LDAP TCP connect timeout in milliseconds. Setting this parameter to a low value enables fast failover if ldap_server_url contains more than one LDAP server addresses. Default value: 1000 (one second) Example 1.5. ldap_network_timeout example ldap_network_timeout = 500 ; setting TCP timeout to 500 ms ldap_client_bind_timeout (optional) LDAP bind operation timeout in milliseconds. Default value: 1000 (one second) Example 1.6. ldap_client_bind_timeout example ldap_client_bind_timeout = 1000 ldap_ca_cert_file (optional) LDAP full path of the CA certificate file. No default value. It is mandatory in case you wish to use StartTLS Example 1.7. ldap_ca_cert_file example ldap_ca_cert_file = "/usr/local/CAcert.pem" ldap_cert_file (optional) LDAP full path of the certificate file. No default value. It is mandatory in case you wish to use StartTLS Example 1.8. ldap_cert_file example ldap_cert_file = "/usr/local/mycert.pem" ldap_key_file (optional) LDAP full path of the key file. No default value. It is mandatory in case you wish to use StartTLS Example 1.9. ldap_key_file example ldap_key_file = "/usr/local/mykey.pem" ldap_require_certificate (optional) LDAP peer certificate checking strategy, one of "NEVER", "HARD", "DEMAND", "ALLOW", "TRY". Lower case letters are also accepted. Default value "NEVER". Example 1.10. ldap_require_certificate example ldap_require_certificate = "NEVER" 1.3.3. Configuration File Example The following configuration file example includes two LDAP session definitions that could be used e.g. for accessing H.350 data and do phone number to name mappings. Example 1.11. Example LDAP Configuration File # LDAP session "sipaccounts": # # - using LDAPv3 (default) # - two redundant LDAP servers # [sipaccounts] ldap_server_url = "ldap://h350-1.example.com, ldap://h350-2.example.com" ldap_bind_dn = "cn=sip_proxy,ou=accounts,dc=example,dc=com" ldap_bind_password = "pwd" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 #using StartTLS ldap_ca_cert_file = "/ldap/path/to/ca/certificate.pem" ldap_cert_file = "/ldap/path/to/certificate.pem" ldap_key_file = "/ldap/path/to/key/file.pem" ldap_require_certificate = "NEVER" # LDAP session "campus": # # - using LDAPv2 # - anonymous bind # [campus] ldap_version = 2 ldap_server_url = "ldap://ldap.example.com" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 1.4. Exported Parameters 1.4.1. config_file (string) Full path to LDAP configuration file. Default value: /usr/local/etc/opensips/ldap.cfg Example 1.12. config_file parameter usage modparam("ldap", "config_file", "/etc/opensips/ldap.ini") 1.4.2. max_async_connections (int) Number of maximum asynchronous connections that will be started with the ldap server for executing asynchronous ldap_search calls. The number of connections is per process, so if there are 8 worker processes with 20 max_async_connections, there will be a maximum of 160 connections to the ldap server. Default value: 20 Example 1.13. max_async_connections parameter usage modparam("ldap", "max_async_connections", 50) 1.5. Exported Functions 1.5.1. ldap_search(ldap_url) Performs an LDAP search operation using given LDAP URL and stores result internally for later retrieval by ldap_result* functions. If one ore more LDAP entries are found the function returns the number of found entries which evaluates to TRUE in the OpenSIPS configuration script. It returns -1 (FALSE) in case no LDAP entry was found, and -2 (FALSE) if an internal error like e.g. an LDAP error occurred. Function Parameters: ldap_url An LDAP URL defining the LDAP search operation (refer to Section 1.1.2, “LDAP URLs†for a description of the LDAP URL format). The hostport part must be one of the LDAP session names declared in the LDAP configuration script. OpenSIPS pseudo variables and AVPs included in ldap_url do get substituted with their value. Example 1.14. Example Usage of ldap_url Search with LDAP session named sipaccounts, base ou=sip,dc=example,dc=com, one level deep using search filter (cn=schlatter) and returning all attributes: ldap://sipaccounts/ou=sip,dc=example,dc=com??one?(cn=schlatter) Subtree search with LDAP session named ldap1, base dc=example,dc=com using search filter (cn=$(avp(name))) and returning SIPIdentityUserName and SIPIdentityServiceLevel attributes ldap://ldap_1/dc=example,dc=com? SIPIdentityUserName,SIPIdentityServiceLevel?sub?(cn=$(avp(name))) Return Values: n > 0 (TRUE): + Found n matching LDAP entries -1 (FALSE): + No matching LDAP entries found -2 (FALSE): + LDAP error (e.g. LDAP server unavailable), or + internal error This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.15. Example Usage ... # ldap search if (!ldap_search("ldap://sipaccounts/ou=sip,dc=example,dc=com??one?(cn=$ rU)")) { switch ($retcode) { case -1: # no LDAP entry found sl_send_reply("404", "User Not Found"); exit; case -2: # internal error sl_send_reply("500", "Internal server error"); exit; default: exit; } } xlog("L_INFO", "ldap_search: found [$retcode] entries for (cn=$rU)"); # save telephone number in $avp(tel_number) ldap_result("telephoneNumber/$avp(tel_number)"); ... 1.5.2. ldap_result("ldap_attr_name/avp_spec[/avp_type]" [, regex_subst]) This function converts LDAP attribute values into AVPs for later use in the message routing script. It accesses the LDAP result set fetched by the last ldap_search call. ldap_attr_name specifies the LDAP attribute name who's value should be stored in AVP avp_spec. Multi valued LDAP attributes generate an indexed AVP. The optional regex_subst parameter allows to further define what part of an attribute value should be stored as AVP. An AVP can either be of type string or integer. As default, ldap_result stores LDAP attribute values as AVP of type string. The optional avp_type parameter can be used to explicitly specify the type of the AVP. It can be either str for string, or int for integer. If avp_type is specified as int then ldap_result tries to convert the LDAP attribute values to integer. In this case, the values are only stored as AVP if the conversion to integer is succesfull. Function Parameters: ldap_attr_name The name of the LDAP attribute who's value should be stored, e.g. SIPIdentityServiceLevel or telephonenumber avp_spec Specification of destination AVP, e.g. $avp(service_level) or $avp(12) avp_type Opional specification of destination AVP type, either str or int. If this parameter is not specified then the LDAP attribute values are stored as AVP of type string. regex_subst Regex substitution that gets applied to LDAP attribute value before storing it as AVP, e.g. "/^sip:(.+)$/\1/" to strip off "sip:" from the beginning of an LDAP attribute value. Return Values: n > 0 (TRUE) LDAP attribute ldap_attr_name found in LDAP result set and n LDAP attribute values stored in avp_spec -1 (FALSE) No LDAP attribute ldap_attr_name found in LDAP result set -2 (FALSE) Internal error occurred This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.16. Example Usage ... # ldap_search call ... # save SIPIdentityServiceLevel in $avp(service_level) if (!ldap_result("SIPIdentityServiceLevel/$avp(service_level)")) { switch ($retcode) { case -1: # no SIPIdentityServiceLevel found sl_send_reply("403", "Forbidden"); exit; case -2: # internal error sl_send_reply("500", "Internal server error"); exit; default: exit; } } # save SIP URI domain in $avp(10) ldap_result("SIPIdentitySIPURI/$avp(10)", "/^[^@]+@(.+)$/\1/"); ... 1.5.3. ldap_result_check("ldap_attr_name/string_to_match" [, regex_subst]) This function compares ldap_attr_name's value with string_to_match for equality. It accesses the LDAP result set fetched by the last ldap_search call. The optional regex_subst parameter allows to further define what part of the attribute value should be used for the equality match. If ldap_attr_name is multi valued, each value is checked against string_to_match. If one or more of the values do match the function returns 1 (TRUE). Function Parameters: ldap_attr_name The name of the LDAP attribute who's value should be matched, e.g. SIPIdentitySIPURI string_to_match String to be matched. Included AVPs and pseudo variabels do get expanded. regex_subst Regex substitution that gets applied to LDAP attribute value before comparing it with string_to_match, e.g. "/^[^@]@+(.+)$/\1/" to extract the domain part of a SIP URI Return Values: 1 (TRUE) One or more ldap_attr_name attribute values match string_to_match (after regex_subst is applied) -1 (FALSE) ldap_attr_name attribute not found or attribute value doesn't match string_to_match (after regex_subst is applied) -2 (FALSE) Internal error occurred This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.17. Example Usage ... # ldap_search call ... # check if 'sn' ldap attribute value equals username part of R-URI, # the same could be achieved with ldap_result_check("sn/$rU") if (!ldap_result_check("sn/$ru", "/^sip:([^@]).*$/\1/")) { switch ($retcode) { case -1: # R-URI username doesn't match sn sl_send_reply("401", "Unauthorized"); exit; case -2: # internal error sl_send_reply("500", "Internal server error"); exit; default: exit; } } ... 1.5.4. ldap_result_next() An LDAP search operation can return multiple LDAP entries. This function can be used to cycle through all returned LDAP entries. It returns 1 (TRUE) if there is another LDAP entry present in the LDAP result set and causes ldap_result* functions to work on the next LDAP entry. The function returns -1 (FALSE) if there are no more LDAP entries in the LDAP result set. Return Values: 1 (TRUE) Another LDAP entry is present in the LDAP result set and result pointer is incremented by one -1 (FALSE) No more LDAP entries are available -2 (FALSE) Internal error This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.18. Example Usage ... # ldap_search call ... ldap_result("telephonenumber/$avp(tel1)"); if (ldap_result_next()) { ldap_result("telephonenumber/$avp(tel2)"); } if (ldap_result_next()) { ldap_result("telephonenumber/$avp(tel3)"); } if (ldap_result_next()) { ldap_result("telephonenumber/$avp(tel4)"); } ... 1.5.5. ldap_filter_url_encode(string, avp_spec) This function applies the following escaping rules to string and stores the result in AVP avp_spec: Table 1.2. ldap_filter_url_encode() escaping rules character in string gets replaced with defined in * \2a RFC 4515 ( \28 RFC 4515 ) \29 RFC 4515 \ \5c RFC 4515 ? %3F RFC 4516 The string stored in AVP avp_spec can be safely used in an LDAP URL filter string. Function Parameters: string String to apply RFC 4515 and URL escpaing rules to. AVPs and pseudo variables do get expanded. Example: "cn=$avp(name)" avp_spec Specification of AVP to store resulting RFC 4515 and URL encoded string, e.g. $avp(ldap_search) or $avp(10) Return Values: 1 (TRUE) RFC 4515 and URL encoded filter_component stored as AVP avp_name -1 (FALSE) Internal error This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.19. Example Usage ... if (!ldap_filter_url_encode("cn=$avp(name)", "$avp(name_esc)")) { # RFC 4515/URL encoding failed --> silently discard request exit; } xlog("L_INFO", "encoded LDAP filter component: [$avp(name_esc)]\n"); if (ldap_search( "ldap://h350/ou=commObjects,dc=example,dc=com??sub?($avp(name_esc)) ")) { ... } ... 1.6. Exported Async Functions 1.6.1. ldap_search(ldap_url) Performs an LDAP search operation using given LDAP URL and stores result internally for later retrieval by ldap_result* functions. If one ore more LDAP entries are found the function returns the number of found entries which evaluates to TRUE in the OpenSIPS configuration script. It returns -1 (FALSE) in case no LDAP entry was found, and -2 (FALSE) if an internal error like e.g. an LDAP error occurred. Function Parameters: ldap_url An LDAP URL defining the LDAP search operation (refer to Section 1.1.2, “LDAP URLs†for a description of the LDAP URL format). The hostport part must be one of the LDAP session names declared in the LDAP configuration script. OpenSIPS pseudo variables and AVPs included in ldap_url do get substituted with their value. Example 1.20. Example Usage of ldap_url Search with LDAP session named sipaccounts, base ou=sip,dc=example,dc=com, one level deep using search filter (cn=schlatter) and returning all attributes: ldap://sipaccounts/ou=sip,dc=example,dc=com??one?(cn=schlatter) Subtree search with LDAP session named ldap1, base dc=example,dc=com using search filter (cn=$(avp(name))) and returning SIPIdentityUserName and SIPIdentityServiceLevel attributes ldap://ldap_1/dc=example,dc=com? SIPIdentityUserName,SIPIdentityServiceLevel?sub?(cn=$(avp(name))) Return Values: n > 0 (TRUE): + Found n matching LDAP entries -1 (FALSE): + No matching LDAP entries found -2 (FALSE): + LDAP error (e.g. LDAP server unavailable), or + internal error This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example 1.21. Example Usage ... # ldap search route { async( ldap_search("ldap://sipaccounts/ou=sip,dc=example,dc=com? ?one?(cn=$rU)"), resume); } .... route[resume] { { switch ($rc) { case -1: # no LDAP entry found sl_send_reply("404", "User Not Found"); exit; case -2: # internal error sl_send_reply("500", "Internal server error"); exit; default: exit; } xlog("L_INFO", "ldap_search: found [$retcode] entries for (cn=$rU)") ; # save telephone number in $avp(tel_number) ldap_result("telephoneNumber/$avp(tel_number)"); ... } 1.7. Installation & Running 1.7.1. Compiling the Module OpenLDAP library (libldap) and header files (libldap-dev) v2.1 or greater (this module was tested with v2.1.3 and v2.3.32) are required for compiling the LDAP module. The OpenLDAP source is available at http://www.openldap.org/. The OpenLDAP library is available pre-compiled for most UNIX/Linux flavors. On Debian/Ubuntu, the following packages must be installed: # apt-get install libldap2 libldap2-dev . Chapter 2. Developer Guide 2.1. Overview The LDAP module API can be used by other OpenSIPS modules to implement LDAP search functionality. This frees the module implementer from having to care about LDAP connection management and configuration. In order to use this API, a module has to load the API using the load_ldap_api function which returns a pointer to a ldap_api structure. This structure includes pointers to the API functions described below. The LDAP module source file api.h includes all declarations needed to load the API, it has to be included in the file that loads the API. Loading the API is typically done inside a module's mod_init call as the following example shows: Example 2.1. Example code fragment to load LDAP module API #include "../../sr_module.h" #include "../ldap/api.h" /* * global pointer to ldap api */ extern ldap_api_t ldap_api; ... static int mod_init(void) { /* * load the LDAP API */ if (load_ldap_api(&ldap_api) != 0) { LM_ERR("Unable to load LDAP API - this module requires ldap modu le\n"); return -1; } ... } ... The API functions can then be used like in the following example: Example 2.2. Example LDAP module API function call ... rc = ldap_api.ldap_rfc4515_escape(str1, str2, 0); ... 2.2. API Functions 2.2.1. ldap_params_search Performs an LDAP search using the parameters given as function arguments. typedef int (*ldap_params_search_t)(int* _ld_result_count, char* _lds_name, char* _dn, int _scope, char** _attrs, char* _filter, ...); Function arguments: int* _ld_result_count The function stores the number of returned LDAP entries in _ld_result_count. char* _lds_name LDAP session name as configured in the LDAP module configuration file. char* _dn LDAP search DN. int _scope LDAP search scope, one of LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_BASE, or LDAP_SCOPE_SUBTREE, as defined in OpenLDAP's ldap.h. char** _attrs A null-terminated array of attribute types to return from entries. If empty (NULL), all attribute types are returned. char* _filter LDAP search filter string according to RFC 4515. printf patterns in this string do get replaced with the function arguments' values following the _filter argument. Return Values: -1 Internal error. 0 Success, _ld_result_count includes the number of LDAP entries found. 2.2.2. ldap_url_search Performs an LDAP search using an LDAP URL. typedef int (*ldap_url_search_t)(char* _ldap_url, int* _result_count); Function arguments: char* _ldap_url LDAP URL as described in Section 1.1.2, “LDAP URLsâ€. int* _result_count The function stores the number of returned LDAP entries in _ld_result_count. Return Values: -1 Internal error. 0 Success, _ld_result_count includes the number of LDAP entries found. 2.2.3. ldap_result_attr_vals Retrieve the value(s) of a returned LDAP attribute. The function accesses the LDAP result returned by the last call of ldap_params_search or ldap_url_search. The berval structure is defined in OpenLDAP's ldap.h, which has to be included. This function allocates memory to store the LDAP attribute value(s). This memory has to freed with the function ldap_value_free_len (see next section). typedef int (*ldap_result_attr_vals_t)(str* _attr_name, struct berval ***_vals); typedef struct berval { ber_len_t bv_len; char *bv_val; } BerValue; Function arguments: str* _attr_name str structure holding the LDAP attribute name. struct berval ***_vals A null-terminated array of the attribute's value(s). Return Values: -1 Internal error. 0 Success, _vals includes the attribute's value(s). 1 No attribute value found. 2.2.4. ldap_value_free_len Function used to free memory allocated by ldap_result_attr_vals. The berval structure is defined in OpenLDAP's ldap.h, which has to be included. typedef void (*ldap_value_free_len_t)(struct berval **_vals); typedef struct berval { ber_len_t bv_len; char *bv_val; } BerValue; Function arguments: struct berval **_vals berval array returned by ldap_result_attr_vals. 2.2.5. ldap_result_next Increments the LDAP result pointer. typedef int (*ldap_result_next_t)(); Return Values: -1 No LDAP result found, probably because ldap_params_search or ldap_url_search was not called. 0 Success, LDAP result pointer points now to next result. 1 No more results available. 2.2.6. ldap_str2scope Converts LDAP search scope string into integer value e.g. for ldap_params_search. typedef int (*ldap_str2scope_t)(char* scope_str); Function arguments: char* scope_str LDAP search scope string. One of "one", "onelevel", "base", "sub", or "subtree". Return Values: -1 scope_str not recognized. n >= 0 LDAP search scope integer. 2.2.7. ldap_rfc4515_escape Applies escaping rules described in Section 1.5.5, “ldap_filter_url_encode(string, avp_spec)â€. typedef int (*ldap_rfc4515_escape_t)(str *sin, str *sout, int url_encode ); Function arguments: str *sin str structure holding the string to apply the escaping rules. str *sout str structure holding the escaped string. The length of this string must be at least three times the length of sin plus one. int url_encode Flag that specifies if a '?' character gets escaped with '%3F' or not. If url_encode equals 0, '?' does not get escaped. Return Values: -1 Internal error. 0 Success, sout contains escaped string. 2.2.8. get_ldap_handle Returns the OpenLDAP LDAP handle for a specific LDAP session. This allows a module implementor to use the OpenLDAP API functions directly, instead of using the API functions exported by the OpenSIPS LDAP module. The LDAP structure is defined in OpenLDAP's ldap.h, which has to be included. typedef int (*get_ldap_handle_t)(char* _lds_name, LDAP** _ldap_handle); Function arguments: char* _lds_name LDAP session name as specified in the LDAP module configuration file. LDAP** _ldap_handle OpenLDAP LDAP handle returned by this function. Return Values: -1 Internal error. 0 Success, _ldap_handle contains the OpenLDAP LDAP handle. 2.2.9. get_last_ldap_result Returns the OpenLDAP LDAP handle and OpenLDAP result handle of the last LDAP search operation. These handles can be used as input for OpenLDAP LDAP result API functions. LDAP and LDAPMessage structures are defined in OpenLDAP's ldap.h, which has to be included. typedef void (*get_last_ldap_result_t) (LDAP** _last_ldap_handle, LDAPMessage** _last_ldap_result) ; Function arguments: LDAP** _last_ldap_handle OpenLDAP LDAP handle returned by this function. LDAPMessage** _last_ldap_result OpenLDAP result handle returned by this function. 2.3. Example Usage The following example shows how this API can be used to perform an LDAP search operation. It is assumed that the API is loaded and available through the ldap_api pointer. ... int rc, ld_result_count, scope = 0; char* sip_username = "test"; /* * get LDAP search scope integer */ scope = ldap_api.ldap_str2scope("sub"); if (scope == -1) { LM_ERR("ldap_str2scope failed\n"); return -1; } /* * perform LDAP search */ if (ldap_api.ldap_params_search( &ld_result_count, "campus", "dc=example,dc=com", scope, NULL, "(&(objectClass=SIPIdentity)(SIPIdentityUserName=%s))", sip_username) != 0) { LM_ERR("LDAP search failed\n"); return -1; } /* * check result count */ if (ld_result_count < 1) { LM_ERR("LDAP search returned no entry\n"); return 1; } /* * get password attribute value */ struct berval **attr_vals = NULL; str ldap_pwd_attr_name = str_init("SIPIdentityPassword"); str res_password; rc = ldap_api.ldap_result_attr_vals(&ldap_pwd_attr_name, &attr_vals); if (rc < 0) { LM_ERR("ldap_result_attr_vals failed\n"); ldap_api.ldap_value_free_len(attr_vals); return -1; } if (rc == 1) { LM_INFO("No password attribute value found for [%s]\n", sip_username ); ldap_api.ldap_value_free_len(attr_vals); return 2; } res_password.s = attr_vals[0]->bv_val; res_password.len = attr_vals[0]->bv_len; ldap_api.ldap_value_free_len(attr_vals); LM_INFO("Password for user [%s]: [%s]\n", sip_username, res_password.s); ... return 0; Resources [RFC4510] Lightweight Directory Access Protocol (LDAP): Technical Specification Road Map. June 2006. Internet Engineering Task Force. [RFC4511] Lightweight Directory Access Protocol (LDAP): The Protocol. June 2006. Internet Engineering Task Force. [RFC4514] Lightweight Directory Access Protocol (LDAP): String Representation of Distinguished Names. June 2006. Internet Engineering Task Force. [RFC4515] Lightweight Directory Access Protocol (LDAP): String Representation of Search Filters. June 2006. Internet Engineering Task Force. [RFC4516] Lightweight Directory Access Protocol (LDAP): Uniform Resource Locator. June 2006. Internet Engineering Task Force. [RFC2617] HTTP Authentication: Basic and Digest Access Authentication. June 1999. Internet Engineering Task Force. [RFC3261] SIP: Session Initiation Protocol. June 2002. Internet Engineering Task Force. [H.350] Directory Services Architecture for Multimedia Conferencing. August 2003. ITU-T. [H.350.4] Directory services architecture for SIP. August 2003. ITU-T. opensips-2.2.2/modules/ldap/api.h000066400000000000000000000051011300170765700166720ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #ifndef LDAP_API_H #define LDAP_API_H #include #include "../../str.h" #include "../../sr_module.h" /* * LDAP API function types */ typedef int (*ldap_params_search_t)( int* _ld_result_count, char* _lds_name, char* _dn, int _scope, char** _attrs, char* _filter, ...); typedef int (*ldap_url_search_t)( char* _ldap_url, int* _result_count); typedef int (*ldap_result_attr_vals_t)( str* _attr_name, struct berval ***_vals); typedef void (*ldap_value_free_len_t)(struct berval **_vals); typedef int (*ldap_result_next_t)(); typedef int (*ldap_str2scope_t)(char* scope_str); typedef int (*get_ldap_handle_t)(char* _lds_name, LDAP** _ldap_handle); typedef void (*get_last_ldap_result_t)( LDAP** _last_ldap_handle, LDAPMessage** _last_ldap_result); typedef int (*ldap_rfc4515_escape_t)(str *sin, str *sout, int url_encode); /* * LDAP module API */ typedef struct ldap_api { ldap_params_search_t ldap_params_search; ldap_url_search_t ldap_url_search; ldap_result_attr_vals_t ldap_result_attr_vals; ldap_value_free_len_t ldap_value_free_len; ldap_result_next_t ldap_result_next; ldap_str2scope_t ldap_str2scope; ldap_rfc4515_escape_t ldap_rfc4515_escape; get_ldap_handle_t get_ldap_handle; get_last_ldap_result_t get_last_ldap_result; } ldap_api_t; typedef int (*load_ldap_t)(ldap_api_t *api); int load_ldap(ldap_api_t *api); static inline int load_ldap_api(ldap_api_t *api) { load_ldap_t load_ldap; if (!(load_ldap = (load_ldap_t) find_export("load_ldap", 0, 0))) { LM_ERR("can't import load_ldap\n"); return -1; } if (load_ldap(api) == -1) { return -1; } return 0; } #endif /* LDAP_API_H */ opensips-2.2.2/modules/ldap/doc/000077500000000000000000000000001300170765700165205ustar00rootroot00000000000000opensips-2.2.2/modules/ldap/doc/ldap.xml000066400000000000000000000024621300170765700201660ustar00rootroot00000000000000 %docentities; ]> LDAP Module &osips; Christian Schlatter University of North Carolina cs@unc.edu 2007 University of North Carolina 0.9 05-17-2007 &admin; &devel; &faq; &biblio; opensips-2.2.2/modules/ldap/doc/ldap_admin.xml000066400000000000000000001252671300170765700213470ustar00rootroot00000000000000 &adminguide;
Overview The LDAP module implements an LDAP search interface for OpenSIPS. It exports script functions to perform an LDAP search operation and to store the search results as OpenSIPS AVPs. This allows for using LDAP directory data in the OpenSIPS SIP message routing script. The following features are offered by the LDAP module: LDAP search function taking an LDAP URL as input both synchronous and asynchronous LDAP result parsing functions to store LDAP data as AVP Support for accessing multiple LDAP servers LDAP SIMPLE authentication LDAP server failover and automatic reconnect Configurable LDAP connection and bind timeouts Module API for LDAP search operations that can be used by other OpenSIPS modules StartTLS support The module implementation makes use of the open source OpenLDAP library available on most UNIX/Linux platforms. Besides LDAP server failover and automatic reconnect, this module can handle multiple LDAP sessions concurrently allowing to access data stored on different LDAP servers. Each OpenSIPS worker process maintains one LDAP TCP connection per configured LDAP server. This enables parallel execution of LDAP requests and offloads LDAP concurrency control to the LDAP server(s). An LDAP search module API is provided that can be used by other OpenSIPS modules. A module using this API does not have to implement LDAP connection management and configuration, while still having access to the full OpenLDAP API for searching and result handling. Since LDAP server implementations are optimized for fast read access they are a good choice to store SIP provisioning data. Performance tests have shown that this module achieves lower data access times and higher call rates than other database modules like e.g. the OpenSIPS MYSQL module.
Usage Basics First so called LDAP sessions have to be specified in an external configuration file (as described in ). Each LDAP session includes LDAP server access parameters like server hostname or connection timeouts. Normally only a single LDAP session will be used unless there is a need to access more than one LDAP server. The LDAP session name will then be used in the OpenSIPS configuration script to refer to a specific LDAP session. The ldap_search function () performs an LDAP search operation. It expects an LDAP URL as input which includes the LDAP session name and search parameters. provides a quick overview on LDAP URLs. The result of an LDAP search is stored internally and can be accessed with one of the ldap_result* functions. ldap_result () stores resulting LDAP attribute value as AVPs. ldap_result_check () is a convenience function to compare a string with LDAP attribute values using regular expression matching. Finally, ldap_result_next () allows to handle LDAP search queries that return more than one LDAP entry. All ldap_result* functions do always access the LDAP result set from the last ldap_search call. This should be kept in mind when calling ldap_search more than once in the OpenSIPS configuration script.
LDAP URLs ldap_search expects an LDAP URL as argument. This section describes the format and semantics of an LDAP URL. RFC 4516 describes the format of an LDAP Uniform Resource Locator (URL). An LDAP URL represents an LDAP search operation in a compact format. The LDAP URL format is defined as follows (slightly modified, refer to section 2 of for ABNF notation):
ldap://[ldap_session_name][/dn?attrs[?scope[?filter]]]]
ldap_session_name An LDAP session name as defined in the LDAP configuration file. (RFC 4516 defines this as LDAP hostport parameter) dn Base Distinguished Name (DN) of LDAP search or target of non-search operation, as defined in RFC 4514 attrs Comma separated list of LDAP attributes to be returned scope Scope for LDAP search, valid values are base, one, or sub filter LDAP search filter definition following rules of RFC 4515 The following table lists characters that have to be escaped in LDAP search filters: RFC 4515 Escaping Rules * \2a ( \28 ) \29 \ \5c
Non-URL characters in an LDAP URL have to be escaped using percent-encoding (refer to section 2.1 of RFC 4516). In particular this means that any "?" character in an LDAP URL component must be written as "%3F", since "?" is used as a URL delimiter. The exported function ldap_filter_url_encode () implements RFC 4515/4516 LDAP search filter and URL escaping rules.
Dependencies
OpenSIPS Modules The module depends on the following modules (the listed modules must be loaded before this module): No dependencies on other OpenSIPS modules.
External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: OpenLDAP library (libldap) v2.1 or greater, libldap header files (libldap-dev) are needed for compilation
LDAP Configuration File The module reads an external confiuration file at module initialization time that includes LDAP session definitions.
Configuration File Syntax The configuration file follows the Windows INI file syntax, section names are enclosed in square brackets:[Section_Name]Any section can contain zero or more configuration key assignments of the formkey = value ; commentValues can be given enclosed with quotes. If no quotes are present, the value is understood as containing all characters between the first and the last non-blank characters. Lines starting with a hash sign and blank lines are treated as comments. Each section describes one LDAP session that can be referred to in the OpenSIPS configuration script. Using the section name as the host part of an LDAP URL tells the module to use the LDAP session specified in the respective section. An example LDAP session specification looks like: [example_ldap] ldap_server_url = "ldap://ldap1.example.com, ldap://ldap2.example.com" ldap_bind_dn = "cn=sip_proxy,ou=accounts,dc=example,dc=com" ldap_bind_password = "pwd" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 ldap_ca_cert_file = "/usr/share/ca-certificates/mycert.pem" ldap_cert_file = "/var/my-certificate/certificate.pem" ldap_key_file = "/var/my-certificate/key.pem" ldap_require_certificate = "ALLOW" The configuration keys are explained in the following section. This LDAP session can be referred to in the routing script by using an LDAP URL like e.g.ldap://example_ldap/cn=admin,dc=example,dc=com
LDAP Session Settings ldap_server_url (mandatory) LDAP URL including fully qualified domain name or IP address of LDAP server optionally followed by a colon and TCP port to connect: ldap://<FQDN/IP>[:<port>]. Failover LDAP servers can be added, each separated by a comma. In the event of connection errors, the module tries to connect to servers in order of appearance. Default value: none, this is a mandatory setting <varname>ldap_server_url</varname> examples ldap_server_url = "ldap://localhost" ldap_server_url = "ldap://ldap.example.com:7777" ldap_server_url = "ldap://ldap1.example.com, ldap://ldap2.example.com:80389" ldap_version (optional) Supported LDAP versions are 2 and 3. Default value: 3 (LDAPv3) <varname>ldap_version</varname> example ldap_version = 2 ldap_bind_dn (optional) Authentication user DN used to bind to LDAP server (module currently only supports SIMPLE_AUTH). Empty string enables anonymous LDAP bind. Default value: (empty string --> anonymous bind) <varname>ldap_bind_dn</varname> example ldap_bind_dn = "cn=root,dc=example,dc=com"; ldap_bind_password (optional) Authentication password used to bind to LDAP server (SIMPLE_AUTH). Empty string enables anonymous bind. Default value: (empty string --> anonymous bind) <varname>ldap_bind_password</varname> example ldap_bind_password = "secret"; ldap_network_timeout (optional) LDAP TCP connect timeout in milliseconds. Setting this parameter to a low value enables fast failover if ldap_server_url contains more than one LDAP server addresses. Default value: 1000 (one second) <varname>ldap_network_timeout</varname> example ldap_network_timeout = 500 ; setting TCP timeout to 500 ms ldap_client_bind_timeout (optional) LDAP bind operation timeout in milliseconds. Default value: 1000 (one second) <varname>ldap_client_bind_timeout</varname> example ldap_client_bind_timeout = 1000 ldap_ca_cert_file (optional) LDAP full path of the CA certificate file. No default value. It is mandatory in case you wish to use StartTLS <varname>ldap_ca_cert_file</varname> example ldap_ca_cert_file = "/usr/local/CAcert.pem" ldap_cert_file (optional) LDAP full path of the certificate file. No default value. It is mandatory in case you wish to use StartTLS <varname>ldap_cert_file</varname> example ldap_cert_file = "/usr/local/mycert.pem" ldap_key_file (optional) LDAP full path of the key file. No default value. It is mandatory in case you wish to use StartTLS <varname>ldap_key_file</varname> example ldap_key_file = "/usr/local/mykey.pem" ldap_require_certificate (optional) LDAP peer certificate checking strategy, one of "NEVER", "HARD", "DEMAND", "ALLOW", "TRY". Lower case letters are also accepted. Default value "NEVER". <varname>ldap_require_certificate</varname> example ldap_require_certificate = "NEVER"
Configuration File Example The following configuration file example includes two LDAP session definitions that could be used e.g. for accessing H.350 data and do phone number to name mappings. Example LDAP Configuration File # LDAP session "sipaccounts": # # - using LDAPv3 (default) # - two redundant LDAP servers # [sipaccounts] ldap_server_url = "ldap://h350-1.example.com, ldap://h350-2.example.com" ldap_bind_dn = "cn=sip_proxy,ou=accounts,dc=example,dc=com" ldap_bind_password = "pwd" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 #using StartTLS ldap_ca_cert_file = "/ldap/path/to/ca/certificate.pem" ldap_cert_file = "/ldap/path/to/certificate.pem" ldap_key_file = "/ldap/path/to/key/file.pem" ldap_require_certificate = "NEVER" # LDAP session "campus": # # - using LDAPv2 # - anonymous bind # [campus] ldap_version = 2 ldap_server_url = "ldap://ldap.example.com" ldap_network_timeout = 500 ldap_client_bind_timeout = 500
Exported Parameters
config_file (string) Full path to LDAP configuration file. Default value: /usr/local/etc/opensips/ldap.cfg <varname>config_file</varname> parameter usage modparam("ldap", "config_file", "/etc/opensips/ldap.ini")
max_async_connections (int) Number of maximum asynchronous connections that will be started with the ldap server for executing asynchronous ldap_search calls. The number of connections is per process, so if there are 8 worker processes with 20 max_async_connections, there will be a maximum of 160 connections to the ldap server. Default value: 20 <varname>max_async_connections</varname> parameter usage modparam("ldap", "max_async_connections", 50)
Exported Functions
ldap_search(ldap_url) Performs an LDAP search operation using given LDAP URL and stores result internally for later retrieval by ldap_result* functions. If one ore more LDAP entries are found the function returns the number of found entries which evaluates to TRUE in the OpenSIPS configuration script. It returns -1 (FALSE) in case no LDAP entry was found, and -2 (FALSE) if an internal error like e.g. an LDAP error occurred. Function Parameters: ldap_url An LDAP URL defining the LDAP search operation (refer to for a description of the LDAP URL format). The hostport part must be one of the LDAP session names declared in the LDAP configuration script. OpenSIPS pseudo variables and AVPs included in ldap_url do get substituted with their value. Example Usage of ldap_url Search with LDAP session named sipaccounts, base ou=sip,dc=example,dc=com, one level deep using search filter (cn=schlatter) and returning all attributes: ldap://sipaccounts/ou=sip,dc=example,dc=com??one?(cn=schlatter) Subtree search with LDAP session named ldap1, base dc=example,dc=com using search filter (cn=$(avp(name))) and returning SIPIdentityUserName and SIPIdentityServiceLevel attributes ldap://ldap_1/dc=example,dc=com? SIPIdentityUserName,SIPIdentityServiceLevel?sub?(cn=$(avp(name))) Return Values: n > 0 (TRUE): Found n matching LDAP entries -1 (FALSE): No matching LDAP entries found -2 (FALSE): LDAP error (e.g. LDAP server unavailable), or internal error This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage ... # ldap search if (!ldap_search("ldap://sipaccounts/ou=sip,dc=example,dc=com??one?(cn=$rU)")) { switch ($retcode) { case -1: # no LDAP entry found sl_send_reply("404", "User Not Found"); exit; case -2: # internal error sl_send_reply("500", "Internal server error"); exit; default: exit; } } xlog("L_INFO", "ldap_search: found [$retcode] entries for (cn=$rU)"); # save telephone number in $avp(tel_number) ldap_result("telephoneNumber/$avp(tel_number)"); ...
ldap_result("ldap_attr_name/avp_spec[/avp_type]" [, regex_subst]) This function converts LDAP attribute values into AVPs for later use in the message routing script. It accesses the LDAP result set fetched by the last ldap_search call. ldap_attr_name specifies the LDAP attribute name who's value should be stored in AVP avp_spec. Multi valued LDAP attributes generate an indexed AVP. The optional regex_subst parameter allows to further define what part of an attribute value should be stored as AVP. An AVP can either be of type string or integer. As default, ldap_result stores LDAP attribute values as AVP of type string. The optional avp_type parameter can be used to explicitly specify the type of the AVP. It can be either str for string, or int for integer. If avp_type is specified as int then ldap_result tries to convert the LDAP attribute values to integer. In this case, the values are only stored as AVP if the conversion to integer is succesfull. Function Parameters: ldap_attr_name The name of the LDAP attribute who's value should be stored, e.g. SIPIdentityServiceLevel or telephonenumber avp_spec Specification of destination AVP, e.g. $avp(service_level) or $avp(12) avp_type Opional specification of destination AVP type, either str or int. If this parameter is not specified then the LDAP attribute values are stored as AVP of type string. regex_subst Regex substitution that gets applied to LDAP attribute value before storing it as AVP, e.g. "/^sip:(.+)$/\1/" to strip off "sip:" from the beginning of an LDAP attribute value. Return Values: n > 0 (TRUE) LDAP attribute ldap_attr_name found in LDAP result set and n LDAP attribute values stored in avp_spec -1 (FALSE) No LDAP attribute ldap_attr_name found in LDAP result set -2 (FALSE) Internal error occurred This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage ... # ldap_search call ... # save SIPIdentityServiceLevel in $avp(service_level) if (!ldap_result("SIPIdentityServiceLevel/$avp(service_level)")) { switch ($retcode) { case -1: # no SIPIdentityServiceLevel found sl_send_reply("403", "Forbidden"); exit; case -2: # internal error sl_send_reply("500", "Internal server error"); exit; default: exit; } } # save SIP URI domain in $avp(10) ldap_result("SIPIdentitySIPURI/$avp(10)", "/^[^@]+@(.+)$/\1/"); ...
ldap_result_check("ldap_attr_name/string_to_match" [, regex_subst]) This function compares ldap_attr_name's value with string_to_match for equality. It accesses the LDAP result set fetched by the last ldap_search call. The optional regex_subst parameter allows to further define what part of the attribute value should be used for the equality match. If ldap_attr_name is multi valued, each value is checked against string_to_match. If one or more of the values do match the function returns 1 (TRUE). Function Parameters: ldap_attr_name The name of the LDAP attribute who's value should be matched, e.g. SIPIdentitySIPURI string_to_match String to be matched. Included AVPs and pseudo variabels do get expanded. regex_subst Regex substitution that gets applied to LDAP attribute value before comparing it with string_to_match, e.g. "/^[^@]@+(.+)$/\1/" to extract the domain part of a SIP URI Return Values: 1 (TRUE) One or more ldap_attr_name attribute values match string_to_match (after regex_subst is applied) -1 (FALSE) ldap_attr_name attribute not found or attribute value doesn't match string_to_match (after regex_subst is applied) -2 (FALSE) Internal error occurred This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage ... # ldap_search call ... # check if 'sn' ldap attribute value equals username part of R-URI, # the same could be achieved with ldap_result_check("sn/$rU") if (!ldap_result_check("sn/$ru", "/^sip:([^@]).*$/\1/")) { switch ($retcode) { case -1: # R-URI username doesn't match sn sl_send_reply("401", "Unauthorized"); exit; case -2: # internal error sl_send_reply("500", "Internal server error"); exit; default: exit; } } ...
ldap_result_next() An LDAP search operation can return multiple LDAP entries. This function can be used to cycle through all returned LDAP entries. It returns 1 (TRUE) if there is another LDAP entry present in the LDAP result set and causes ldap_result* functions to work on the next LDAP entry. The function returns -1 (FALSE) if there are no more LDAP entries in the LDAP result set. Return Values: 1 (TRUE) Another LDAP entry is present in the LDAP result set and result pointer is incremented by one -1 (FALSE) No more LDAP entries are available -2 (FALSE) Internal error This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage ... # ldap_search call ... ldap_result("telephonenumber/$avp(tel1)"); if (ldap_result_next()) { ldap_result("telephonenumber/$avp(tel2)"); } if (ldap_result_next()) { ldap_result("telephonenumber/$avp(tel3)"); } if (ldap_result_next()) { ldap_result("telephonenumber/$avp(tel4)"); } ...
ldap_filter_url_encode(string, avp_spec) This function applies the following escaping rules to string and stores the result in AVP avp_spec: ldap_filter_url_encode() escaping rules character in string gets replaced with defined in * \2a RFC 4515 ( \28 RFC 4515 ) \29 RFC 4515 \ \5c RFC 4515 ? %3F RFC 4516
The string stored in AVP avp_spec can be safely used in an LDAP URL filter string. Function Parameters: string String to apply RFC 4515 and URL escpaing rules to. AVPs and pseudo variables do get expanded. Example: "cn=$avp(name)" avp_spec Specification of AVP to store resulting RFC 4515 and URL encoded string, e.g. $avp(ldap_search) or $avp(10) Return Values: 1 (TRUE) RFC 4515 and URL encoded filter_component stored as AVP avp_name -1 (FALSE) Internal error This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage ... if (!ldap_filter_url_encode("cn=$avp(name)", "$avp(name_esc)")) { # RFC 4515/URL encoding failed --> silently discard request exit; } xlog("L_INFO", "encoded LDAP filter component: [$avp(name_esc)]\n"); if (ldap_search( "ldap://h350/ou=commObjects,dc=example,dc=com??sub?($avp(name_esc))")) { ... } ...
Exported Async Functions
ldap_search(ldap_url) Performs an LDAP search operation using given LDAP URL and stores result internally for later retrieval by ldap_result* functions. If one ore more LDAP entries are found the function returns the number of found entries which evaluates to TRUE in the OpenSIPS configuration script. It returns -1 (FALSE) in case no LDAP entry was found, and -2 (FALSE) if an internal error like e.g. an LDAP error occurred. Function Parameters: ldap_url An LDAP URL defining the LDAP search operation (refer to for a description of the LDAP URL format). The hostport part must be one of the LDAP session names declared in the LDAP configuration script. OpenSIPS pseudo variables and AVPs included in ldap_url do get substituted with their value. Example Usage of ldap_url Search with LDAP session named sipaccounts, base ou=sip,dc=example,dc=com, one level deep using search filter (cn=schlatter) and returning all attributes: ldap://sipaccounts/ou=sip,dc=example,dc=com??one?(cn=schlatter) Subtree search with LDAP session named ldap1, base dc=example,dc=com using search filter (cn=$(avp(name))) and returning SIPIdentityUserName and SIPIdentityServiceLevel attributes ldap://ldap_1/dc=example,dc=com? SIPIdentityUserName,SIPIdentityServiceLevel?sub?(cn=$(avp(name))) Return Values: n > 0 (TRUE): Found n matching LDAP entries -1 (FALSE): No matching LDAP entries found -2 (FALSE): LDAP error (e.g. LDAP server unavailable), or internal error This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE. Example Usage ... # ldap search route { async( ldap_search("ldap://sipaccounts/ou=sip,dc=example,dc=com??one?(cn=$rU)"), resume); } .... route[resume] { { switch ($rc) { case -1: # no LDAP entry found sl_send_reply("404", "User Not Found"); exit; case -2: # internal error sl_send_reply("500", "Internal server error"); exit; default: exit; } xlog("L_INFO", "ldap_search: found [$retcode] entries for (cn=$rU)"); # save telephone number in $avp(tel_number) ldap_result("telephoneNumber/$avp(tel_number)"); ... }
Installation & Running
Compiling the Module OpenLDAP library (libldap) and header files (libldap-dev) v2.1 or greater (this module was tested with v2.1.3 and v2.3.32) are required for compiling the LDAP module. The OpenLDAP source is available at http://www.openldap.org/. The OpenLDAP library is available pre-compiled for most UNIX/Linux flavors. On Debian/Ubuntu, the following packages must be installed: # apt-get install libldap2 libldap2-dev.
opensips-2.2.2/modules/ldap/doc/ldap_biblio.xml000066400000000000000000000072661300170765700215150ustar00rootroot00000000000000 Resources RFC4510 <ulink url="http://tools.ietf.org/html/rfc4510">Lightweight Directory Access Protocol (LDAP): Technical Specification Road Map</ulink> June 2006 Internet Engineering Task Force RFC4511 <ulink url="http://tools.ietf.org/html/rfc4511">Lightweight Directory Access Protocol (LDAP): The Protocol</ulink> June 2006 Internet Engineering Task Force RFC4514 <ulink url="http://tools.ietf.org/html/rfc4514">Lightweight Directory Access Protocol (LDAP): String Representation of Distinguished Names</ulink> June 2006 Internet Engineering Task Force RFC4515 <ulink url="http://tools.ietf.org/html/rfc4515">Lightweight Directory Access Protocol (LDAP): String Representation of Search Filters</ulink> June 2006 Internet Engineering Task Force RFC4516 <ulink url="http://tools.ietf.org/html/rfc4516">Lightweight Directory Access Protocol (LDAP): Uniform Resource Locator</ulink> June 2006 Internet Engineering Task Force RFC2617 <ulink url="http://tools.ietf.org/html/rfc2617">HTTP Authentication: Basic and Digest Access Authentication</ulink> June 1999 Internet Engineering Task Force RFC3261 <ulink url="http://tools.ietf.org/html/rfc3261">SIP: Session Initiation Protocol</ulink> June 2002 Internet Engineering Task Force H.350 <ulink url="http://www.itu.int/rec/T-REC-H.350/en">Directory Services Architecture for Multimedia Conferencing</ulink> August 2003 ITU-T H.350.4 <ulink url="http://www.itu.int/rec/T-REC-H.350.4/en">Directory services architecture for SIP</ulink> August 2003 ITU-T opensips-2.2.2/modules/ldap/doc/ldap_devel.xml000066400000000000000000000372371300170765700213550ustar00rootroot00000000000000 &develguide;
Overview The LDAP module API can be used by other OpenSIPS modules to implement LDAP search functionality. This frees the module implementer from having to care about LDAP connection management and configuration. In order to use this API, a module has to load the API using the load_ldap_api function which returns a pointer to a ldap_api structure. This structure includes pointers to the API functions described below. The LDAP module source file api.h includes all declarations needed to load the API, it has to be included in the file that loads the API. Loading the API is typically done inside a module's mod_init call as the following example shows: Example code fragment to load LDAP module API The API functions can then be used like in the following example: Example LDAP module API function call
API Functions
ldap_params_search Performs an LDAP search using the parameters given as function arguments. Function arguments: int* _ld_result_count The function stores the number of returned LDAP entries in _ld_result_count. char* _lds_name LDAP session name as configured in the LDAP module configuration file. char* _dn LDAP search DN. int _scope LDAP search scope, one of LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_BASE, or LDAP_SCOPE_SUBTREE, as defined in OpenLDAP's ldap.h. char** _attrs A null-terminated array of attribute types to return from entries. If empty (NULL), all attribute types are returned. char* _filter LDAP search filter string according to RFC 4515. printf patterns in this string do get replaced with the function arguments' values following the _filter argument. Return Values: -1 Internal error. 0 Success, _ld_result_count includes the number of LDAP entries found.
ldap_url_search Performs an LDAP search using an LDAP URL. Function arguments: char* _ldap_url LDAP URL as described in . int* _result_count The function stores the number of returned LDAP entries in _ld_result_count. Return Values: -1 Internal error. 0 Success, _ld_result_count includes the number of LDAP entries found.
ldap_result_attr_vals Retrieve the value(s) of a returned LDAP attribute. The function accesses the LDAP result returned by the last call of ldap_params_search or ldap_url_search. The berval structure is defined in OpenLDAP's ldap.h, which has to be included. This function allocates memory to store the LDAP attribute value(s). This memory has to freed with the function ldap_value_free_len (see next section). Function arguments: str* _attr_name str structure holding the LDAP attribute name. struct berval ***_vals A null-terminated array of the attribute's value(s). Return Values: -1 Internal error. 0 Success, _vals includes the attribute's value(s). 1 No attribute value found.
ldap_value_free_len Function used to free memory allocated by ldap_result_attr_vals. The berval structure is defined in OpenLDAP's ldap.h, which has to be included. Function arguments: struct berval **_vals berval array returned by ldap_result_attr_vals.
ldap_result_next Increments the LDAP result pointer. Return Values: -1 No LDAP result found, probably because ldap_params_search or ldap_url_search was not called. 0 Success, LDAP result pointer points now to next result. 1 No more results available.
ldap_str2scope Converts LDAP search scope string into integer value e.g. for ldap_params_search. Function arguments: char* scope_str LDAP search scope string. One of "one", "onelevel", "base", "sub", or "subtree". Return Values: -1 scope_str not recognized. n >= 0 LDAP search scope integer.
ldap_rfc4515_escape Applies escaping rules described in . Function arguments: str *sin str structure holding the string to apply the escaping rules. str *sout str structure holding the escaped string. The length of this string must be at least three times the length of sin plus one. int url_encode Flag that specifies if a '?' character gets escaped with '%3F' or not. If url_encode equals 0, '?' does not get escaped. Return Values: -1 Internal error. 0 Success, sout contains escaped string.
get_ldap_handle Returns the OpenLDAP LDAP handle for a specific LDAP session. This allows a module implementor to use the OpenLDAP API functions directly, instead of using the API functions exported by the OpenSIPS LDAP module. The LDAP structure is defined in OpenLDAP's ldap.h, which has to be included. Function arguments: char* _lds_name LDAP session name as specified in the LDAP module configuration file. LDAP** _ldap_handle OpenLDAP LDAP handle returned by this function. Return Values: -1 Internal error. 0 Success, _ldap_handle contains the OpenLDAP LDAP handle.
get_last_ldap_result Returns the OpenLDAP LDAP handle and OpenLDAP result handle of the last LDAP search operation. These handles can be used as input for OpenLDAP LDAP result API functions. LDAP and LDAPMessage structures are defined in OpenLDAP's ldap.h, which has to be included. Function arguments: LDAP** _last_ldap_handle OpenLDAP LDAP handle returned by this function. LDAPMessage** _last_ldap_result OpenLDAP result handle returned by this function.
Example Usage The following example shows how this API can be used to perform an LDAP search operation. It is assumed that the API is loaded and available through the ldap_api pointer. bv_val; res_password.len = attr_vals[0]->bv_len; ldap_api.ldap_value_free_len(attr_vals); LM_INFO("Password for user [%s]: [%s]\n", sip_username, res_password.s); ... return 0; ]]>
opensips-2.2.2/modules/ldap/etc/000077500000000000000000000000001300170765700165265ustar00rootroot00000000000000opensips-2.2.2/modules/ldap/etc/ldap.cfg000066400000000000000000000010721300170765700201270ustar00rootroot00000000000000# # OpenSIPS LDAP module example configuration # # LDAP session "sipaccounts": # # - using LDAPv3 (default) # - two redundant LDAP servers # [sipaccounts] ldap_server_url = "ldap://h350-1.example.com, ldap://h350-2.example.com" ldap_bind_dn = "cn=sip_proxy,ou=accounts,dc=example,dc=com" ldap_bind_password = "pwd" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 # LDAP session "campus": # # - using LDAPv2 # - anonymous bind # [campus] ldap_version = 2 ldap_server_url = "ldap://ldap.example.com" ldap_network_timeout = 500 ldap_client_bind_timeout = 500 opensips-2.2.2/modules/ldap/iniparser.c000066400000000000000000000672541300170765700201310ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ /* Based upon libiniparser, by Nicolas Devillard Hacked into 1 file (m-iniparser) by Freek/2005 Original terms following: -- - Copyright (c) 2000 by Nicolas Devillard (ndevilla AT free DOT fr). Written by Nicolas Devillard. Not derived from licensed software. Permission is granted to anyone to use this software for any purpose on any computer system, and to redistribute it freely, subject to the following restrictions: 1. The author is not responsible for the consequences of use of this software, no matter how awful, even if they arise from defects in it. 2. The origin of this software must not be misrepresented, either by explicit claim or by omission. 3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. This notice may not be removed or altered. */ #include #include #include #include #include "iniparser.h" #ifdef __cplusplus extern "C" { #endif /* strlib.c following */ #define ASCIILINESZ 1024 /*-------------------------------------------------------------------------*/ /** @brief Convert a string to lowercase. @param s String to convert. @return ptr to statically allocated string. This function returns a pointer to a statically allocated string containing a lowercased version of the input string. Do not free or modify the returned string! Since the returned string is statically allocated, it will be modified at each function call (not re-entrant). */ /*--------------------------------------------------------------------------*/ static char * strlwc(char * s) { static char l[ASCIILINESZ+1]; int i ; if (s==NULL) return NULL ; memset(l, 0, ASCIILINESZ+1); i=0 ; while (s[i] && i l) { if (!isspace((int)*(last-1))) break ; last -- ; } *last = (char)0; return l ; } /*-------------------------------------------------------------------------*/ /** @brief Remove blanks at the beginning and the end of a string. @param s String to parse. @return ptr to statically allocated string. This function returns a pointer to a statically allocated string, which is identical to the input string, except that all blank characters at the end and the beg. of the string have been removed. Do not free or modify the returned string! Since the returned string is statically allocated, it will be modified at each function call (not re-entrant). */ /*--------------------------------------------------------------------------*/ /* static char * strstrip(char * s) { static char l[ASCIILINESZ+1]; char * last ; if (s==NULL) return NULL ; while (isspace((int)*s) && *s) s++; memset(l, 0, ASCIILINESZ+1); strcpy(l, s); last = l + strlen(l); while (last > l) { if (!isspace((int)*(last-1))) break ; last -- ; } *last = (char)0; return (char*)l ; } */ /* dictionary.c.c following */ /** Maximum value size for integers and doubles. */ #define MAXVALSZ 1024 /** Minimal allocated number of entries in a dictionary */ #define DICTMINSZ 128 /** Invalid key token */ #define DICT_INVALID_KEY ((char*)-1) /* Doubles the allocated size associated to a pointer 'size' is the current allocated size. */ static void * mem_double(void * ptr, int size) { void *newptr; newptr = calloc(2*size, 1); memcpy(newptr, ptr, size); free(ptr); return newptr ; } /*--------------------------------------------------------------------------- Function codes ---------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /** @brief Compute the hash key for a string. @param key Character string to use for key. @return 1 unsigned int on at least 32 bits. This hash function has been taken from an Article in Dr Dobbs Journal. This is normally a collision-free function, distributing keys evenly. The key is stored anyway in the struct so that collision can be avoided by comparing the key itself in last resort. */ /*--------------------------------------------------------------------------*/ static unsigned dictionary_hash(char * key) { int len ; unsigned hash ; int i ; len = strlen(key); for (hash=0, i=0 ; i>6) ; } hash += (hash <<3); hash ^= (hash >>11); hash += (hash <<15); return hash ; } /*-------------------------------------------------------------------------*/ /** @brief Create a new dictionary object. @param size Optional initial size of the dictionary. @return 1 newly allocated dictionary objet. This function allocates a new dictionary object of given size and returns it. If you do not know in advance (roughly) the number of entries in the dictionary, give size=0. */ /*--------------------------------------------------------------------------*/ static dictionary * dictionary_new(int size) { dictionary *d ; /* If no size was specified, allocate space for DICTMINSZ */ if (sizesize = size ; d->val = (char **)calloc(size, sizeof(char*)); d->key = (char **)calloc(size, sizeof(char*)); d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); return d; } /*-------------------------------------------------------------------------*/ /** @brief Delete a dictionary object @param d dictionary object to deallocate. @return void Deallocate a dictionary object and all memory associated to it. */ /*--------------------------------------------------------------------------*/ static void dictionary_del(dictionary * d) { int i ; if (d==NULL) return ; for (i=0 ; isize ; i++) { if (d->key[i]!=NULL) free(d->key[i]); if (d->val[i]!=NULL) free(d->val[i]); } free(d->val); free(d->key); free(d->hash); free(d); return; } /*-------------------------------------------------------------------------*/ /** @brief Get a value from a dictionary. @param d dictionary object to search. @param key Key to look for in the dictionary. @param def Default value to return if key not found. @return 1 pointer to internally allocated character string. This function locates a key in a dictionary and returns a pointer to its value, or the passed 'def' pointer if no such key can be found in dictionary. The returned character pointer points to data internal to the dictionary object, you should not try to free it or modify it. */ /*--------------------------------------------------------------------------*/ static char * dictionary_get(dictionary * d, char * key, char * def) { unsigned hash ; int i ; hash = dictionary_hash(key); for (i=0 ; isize ; i++) { if (d->key==NULL) continue ; /* Compare hash */ if (hash==d->hash[i]) { /* Compare string, to avoid hash collisions */ if (!strcmp(key, d->key[i])) { return d->val[i] ; } } } return def ; } /*-------------------------------------------------------------------------*/ /** @brief Set a value in a dictionary. @param d dictionary object to modify. @param key Key to modify or add. @param val Value to add. @return void If the given key is found in the dictionary, the associated value is replaced by the provided one. If the key cannot be found in the dictionary, it is added to it. It is Ok to provide a NULL value for val, but NULL values for the dictionary or the key are considered as errors: the function will return immediately in such a case. Notice that if you dictionary_set a variable to NULL, a call to dictionary_get will return a NULL value: the variable will be found, and its value (NULL) is returned. In other words, setting the variable content to NULL is equivalent to deleting the variable from the dictionary. It is not possible (in this implementation) to have a key in the dictionary without value. */ /*--------------------------------------------------------------------------*/ static void dictionary_set(dictionary * d, char * key, char * val) { int i ; unsigned hash ; if (d==NULL || key==NULL) return ; /* Compute hash for this key */ hash = dictionary_hash(key) ; /* Find if value is already in blackboard */ if (d->n>0) { for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; if (hash==d->hash[i]) { /* Same hash value */ if (!strcmp(key, d->key[i])) { /* Same key */ /* Found a value: modify and return */ if (d->val[i]!=NULL) free(d->val[i]); d->val[i] = val ? strdup(val) : NULL ; /* Value has been modified: return */ return ; } } } } /* Add a new value */ /* See if dictionary needs to grow */ if (d->n==d->size) { /* Reached maximum size: reallocate blackboard */ d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ; d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ; d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; /* Double size */ d->size *= 2 ; } /* Insert key in the first empty slot */ for (i=0 ; isize ; i++) { if (d->key[i]==NULL) { /* Add key here */ break ; } } /* Copy key */ d->key[i] = strdup(key); d->val[i] = val ? strdup(val) : NULL ; d->hash[i] = hash; d->n ++ ; return ; } /*-------------------------------------------------------------------------*/ /** @brief Delete a key in a dictionary @param d dictionary object to modify. @param key Key to remove. @return void This function deletes a key in a dictionary. Nothing is done if the key cannot be found. */ /*--------------------------------------------------------------------------*/ static void dictionary_unset(dictionary * d, char * key) { unsigned hash ; int i ; hash = dictionary_hash(key); for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; /* Compare hash */ if (hash==d->hash[i]) { /* Compare string, to avoid hash collisions */ if (!strcmp(key, d->key[i])) { /* Found key */ break ; } } } if (i>=d->size) /* Key not found */ return ; free(d->key[i]); d->key[i] = NULL ; if (d->val[i]!=NULL) { free(d->val[i]); d->val[i] = NULL ; } d->hash[i] = 0 ; d->n -- ; return ; } /*-------------------------------------------------------------------------*/ /** @brief Dump a dictionary to an opened file pointer. @param d Dictionary to dump @param f Opened file pointer. @return void Dumps a dictionary onto an opened file pointer. Key pairs are printed out as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as output file pointers. */ /*--------------------------------------------------------------------------*/ static void dictionary_dump(dictionary *d, FILE *f) { int i; if (d==NULL || f==NULL) return; for (i=0; isize; i++) { if (d->key[i] == NULL) continue ; if (d->val[i] != NULL) { fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); } else { fprintf(f, "[%s]=UNDEF\n", d->key[i]); } } return; } /* iniparser.c.c following */ #define ASCIILINESZ 1024 #define LONGKEYBUFF 2*ASCIILINESZ+1 #define INI_INVALID_KEY ((char*)-1) /* Private: add an entry to the dictionary */ static void iniparser_add_entry( dictionary * d, char * sec, char * key, char * val) { char longkey[LONGKEYBUFF]; /* Make a key as section:keyword */ if (key!=NULL) { snprintf(longkey, LONGKEYBUFF, "%s:%s", sec, key); } else { size_t len = strlen(sec); if (len > LONGKEYBUFF - 1) len = LONGKEYBUFF - 1; strncpy(longkey, sec, len + 1); } /* Add (key,val) to dictionary */ dictionary_set(d, longkey, val); return ; } /*-------------------------------------------------------------------------*/ /** @brief Get number of sections in a dictionary @param d Dictionary to examine @return int Number of sections found in dictionary This function returns the number of sections found in a dictionary. The test to recognize sections is done on the string stored in the dictionary: a section name is given as "section" whereas a key is stored as "section:key", thus the test looks for entries that do not contain a colon. This clearly fails in the case a section name contains a colon, but this should simply be avoided. This function returns -1 in case of error. */ /*--------------------------------------------------------------------------*/ int iniparser_getnsec(dictionary * d) { int i ; int nsec ; if (d==NULL) return -1 ; nsec=0 ; for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; if (strchr(d->key[i], ':')==NULL) { nsec ++ ; } } return nsec ; } /*-------------------------------------------------------------------------*/ /** @brief Get name for section n in a dictionary. @param d Dictionary to examine @param n Section number (from 0 to nsec-1). @return Pointer to char string This function locates the n-th section in a dictionary and returns its name as a pointer to a string statically allocated inside the dictionary. Do not free or modify the returned string! This function returns NULL in case of error. */ /*--------------------------------------------------------------------------*/ char * iniparser_getsecname(dictionary * d, int n) { int i ; int foundsec ; if (d==NULL || n<0) return NULL ; foundsec=0 ; for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; if (strchr(d->key[i], ':')==NULL) { foundsec++ ; if (foundsec>n) break ; } } if (foundsec<=n) { return NULL ; } return d->key[i] ; } /*-------------------------------------------------------------------------*/ /** @brief Dump a dictionary to an opened file pointer. @param d Dictionary to dump. @param f Opened file pointer to dump to. @return void This function prints out the contents of a dictionary, one element by line, onto the provided file pointer. It is OK to specify @c stderr or @c stdout as output files. This function is meant for debugging purposes mostly. */ /*--------------------------------------------------------------------------*/ void iniparser_dump(dictionary * d, FILE * f) { dictionary_dump(d,f); } /*-------------------------------------------------------------------------*/ /** @brief Save a dictionary to a loadable ini file @param d Dictionary to dump @param f Opened file pointer to dump to @return void This function dumps a given dictionary into a loadable ini file. It is Ok to specify @c stderr or @c stdout as output files. */ /*--------------------------------------------------------------------------*/ void iniparser_dump_ini(dictionary * d, FILE * f) { int i, j ; char keym[ASCIILINESZ+1]; int nsec ; char * secname ; int seclen ; if (d==NULL || f==NULL) return ; nsec = iniparser_getnsec(d); if (nsec<1) { /* No section in file: dump all keys as they are */ for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; fprintf(f, "%s = %s\n", d->key[i], d->val[i]); } return ; } for (i=0 ; isize ; j++) { if (d->key[j]==NULL) continue ; if (!strncmp(d->key[j], keym, seclen+1)) { fprintf(f, "%-30s = %s\n", d->key[j]+seclen+1, d->val[j] ? d->val[j] : ""); } } } fprintf(f, "\n"); return ; } /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, return NULL if not found @param d Dictionary to search @param key Key string to look for @return pointer to statically allocated character string, or NULL. This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, NULL is returned. The returned char pointer is pointing to a string allocated in the dictionary, do not free or modify it. This function is only provided for backwards compatibility with previous versions of iniparser. It is recommended to use iniparser_getstring() instead. */ /*--------------------------------------------------------------------------*/ char * iniparser_getstr(dictionary * d, char * key) { return iniparser_getstring(d, key, NULL); } /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key @param d Dictionary to search @param key Key string to look for @param def Default value to return if key not found. @return pointer to statically allocated character string This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the pointer passed as 'def' is returned. The returned char pointer is pointing to a string allocated in the dictionary, do not free or modify it. */ /*--------------------------------------------------------------------------*/ char * iniparser_getstring(dictionary * d, char * key, char * def) { char * lc_key ; char * sval ; if (d==NULL || key==NULL) return def ; lc_key = strdup(strlwc(key)); sval = dictionary_get(d, lc_key, def); free(lc_key); return sval ; } /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to an int @param d Dictionary to search @param key Key string to look for @param notfound Value to return in case of error @return integer This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the notfound value is returned. */ /*--------------------------------------------------------------------------*/ int iniparser_getint(dictionary * d, char * key, int notfound) { char * str ; str = iniparser_getstring(d, key, INI_INVALID_KEY); if (str==INI_INVALID_KEY) return notfound ; return atoi(str); } /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to a double @param d Dictionary to search @param key Key string to look for @param notfound Value to return in case of error @return double This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the notfound value is returned. */ /*--------------------------------------------------------------------------*/ double iniparser_getdouble(dictionary * d, char * key, double notfound) { char * str ; str = iniparser_getstring(d, key, INI_INVALID_KEY); if (str==INI_INVALID_KEY) return notfound ; return atof(str); } /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to a boolean @param d Dictionary to search @param key Key string to look for @param notfound Value to return in case of error @return integer This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the notfound value is returned. A true boolean is found if one of the following is matched: - A string starting with 'y' - A string starting with 'Y' - A string starting with 't' - A string starting with 'T' - A string starting with '1' A false boolean is found if one of the following is matched: - A string starting with 'n' - A string starting with 'N' - A string starting with 'f' - A string starting with 'F' - A string starting with '0' The notfound value returned if no boolean is identified, does not necessarily have to be 0 or 1. */ /*--------------------------------------------------------------------------*/ int iniparser_getboolean(dictionary * d, char * key, int notfound) { char * c ; int ret ; c = iniparser_getstring(d, key, INI_INVALID_KEY); if (c==INI_INVALID_KEY) return notfound ; if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { ret = 1 ; } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { ret = 0 ; } else { ret = notfound ; } return ret; } /*-------------------------------------------------------------------------*/ /** @brief Finds out if a given entry exists in a dictionary @param ini Dictionary to search @param entry Name of the entry to look for @return integer 1 if entry exists, 0 otherwise Finds out if a given entry exists in the dictionary. Since sections are stored as keys with NULL associated values, this is the only way of querying for the presence of sections in a dictionary. */ /*--------------------------------------------------------------------------*/ int iniparser_find_entry( dictionary * ini, char * entry ) { int found=0 ; if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { found = 1 ; } return found ; } /*-------------------------------------------------------------------------*/ /** @brief Set an entry in a dictionary. @param ini Dictionary to modify. @param entry Entry to modify (entry name) @param val New value to associate to the entry. @return int 0 if Ok, -1 otherwise. If the given entry can be found in the dictionary, it is modified to contain the provided value. If it cannot be found, -1 is returned. It is Ok to set val to NULL. */ /*--------------------------------------------------------------------------*/ int iniparser_setstr(dictionary * ini, char * entry, char * val) { dictionary_set(ini, strlwc(entry), val); return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief Delete an entry in a dictionary @param ini Dictionary to modify @param entry Entry to delete (entry name) @return void If the given entry can be found, it is deleted from the dictionary. */ /*--------------------------------------------------------------------------*/ void iniparser_unset(dictionary * ini, char * entry) { dictionary_unset(ini, strlwc(entry)); } /*-------------------------------------------------------------------------*/ /** @brief Parse an ini file and return an allocated dictionary object @param ininame Name of the ini file to read. @return Pointer to newly allocated dictionary This is the parser for ini files. This function is called, providing the name of the file to be read. It returns a dictionary object that should not be accessed directly, but through accessor functions instead. The returned dictionary must be freed using iniparser_free(). */ /*--------------------------------------------------------------------------*/ dictionary * iniparser_new(char *ininame) { dictionary * d ; char lin[ASCIILINESZ+1]; char sec[ASCIILINESZ+1]; char key[ASCIILINESZ+1]; char val[ASCIILINESZ+1]; char * where ; FILE * ini ; int lineno ; if ((ini=fopen(ininame, "r"))==NULL) { return NULL ; } sec[0]=0; /* * Initialize a new dictionary entry */ d = dictionary_new(0); lineno = 0 ; while (fgets(lin, ASCIILINESZ, ini)!=NULL) { lineno++ ; where = strskp(lin); /* Skip leading spaces */ if (*where==';' || *where=='#' || *where==0) continue ; /* Comment lines */ else { if (sscanf(where, "[%[^]]", sec)==1) { /* Valid section name */ strcpy(sec, strlwc(sec)); iniparser_add_entry(d, sec, NULL, NULL); } else if (sscanf (where, "%[^=] = \"%[^\"]\"", key, val) == 2 || sscanf (where, "%[^=] = '%[^\']'", key, val) == 2 || sscanf (where, "%[^=] = %[^;#]", key, val) == 2) { strcpy(key, strlwc(strcrop(key))); /* * sscanf cannot handle "" or '' as empty value, * this is done here */ if (!strcmp(val, "\"\"") || !strcmp(val, "''")) { val[0] = (char)0; } else { strcpy(val, strcrop(val)); } iniparser_add_entry(d, sec, key, val); } } } fclose(ini); return d ; } /*-------------------------------------------------------------------------*/ /** @brief Free all memory associated to an ini dictionary @param d Dictionary to free @return void Free all memory associated to an ini dictionary. It is mandatory to call this function before the dictionary object gets out of the current context. */ /*--------------------------------------------------------------------------*/ void iniparser_free(dictionary * d) { dictionary_del(d); } #ifdef __cplusplus } #endif opensips-2.2.2/modules/ldap/iniparser.h000066400000000000000000000061511300170765700201230ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ /* Based upon libiniparser, by Nicolas Devillard Hacked into 1 file (m-iniparser) by Freek/2005 Original terms following: -- - Copyright (c) 2000 by Nicolas Devillard (ndevilla AT free DOT fr). Written by Nicolas Devillard. Not derived from licensed software. Permission is granted to anyone to use this software for any purpose on any computer system, and to redistribute it freely, subject to the following restrictions: 1. The author is not responsible for the consequences of use of this software, no matter how awful, even if they arise from defects in it. 2. The origin of this software must not be misrepresented, either by explicit claim or by omission. 3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. This notice may not be removed or altered. */ #ifndef _INIPARSER_H_ #define _INIPARSER_H_ #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif typedef struct _dictionary_ { /** Number of entries in dictionary */ int n; /** Storage size */ int size; /** List of string values */ char **val; /** List of string keys */ char **key ; /** List of hash values for keys */ unsigned *hash; } dictionary ; /* generated by genproto */ dictionary * iniparser_new(char *ininame); void iniparser_free(dictionary * d); int iniparser_getnsec(dictionary * d); char * iniparser_getsecname(dictionary * d, int n); void iniparser_dump(dictionary * d, FILE * f); void iniparser_dump_ini(dictionary * d, FILE * f); char * iniparser_getkey(dictionary *d, char *section, char *key); char * iniparser_getstr(dictionary * d, char * key); char * iniparser_getstring(dictionary * d, char * key, char * def); int iniparser_getint(dictionary * d, char * key, int notfound); double iniparser_getdouble(dictionary * d, char * key, double notfound); int iniparser_getboolean(dictionary * d, char * key, int notfound); int iniparser_find_entry(dictionary * ini, char * entry); int iniparser_setstr(dictionary * ini, char * entry, char * val); void iniparser_unset(dictionary * ini, char * entry); #ifdef __cplusplus } #endif #endif opensips-2.2.2/modules/ldap/ld_session.c000066400000000000000000000155601300170765700202700ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #include #include "ld_session.h" #include "ldap_connect.h" #include "../../mem/mem.h" #include "../../sr_module.h" static struct ld_session* ld_sessions = NULL; static char ini_key_name[512]; int add_ld_session(char* _name, dictionary* _d) { struct ld_session* current = ld_sessions; struct ld_session* new_lds = NULL; char *host_name, *bind_dn, *bind_pwd, *bind_cacert, *bind_cert, *bind_key, *bind_req_cert; int client_search_timeout_ms, client_bind_timeout_ms, network_timeout_ms; new_lds = (struct ld_session*)pkg_malloc(sizeof(struct ld_session)); if (new_lds == NULL) { LM_ERR("no memory\n"); return -1; } memset( new_lds, 0, sizeof(struct ld_session)); /* name */ strncpy(new_lds->name, _name, 255); /* handle */ new_lds->conn_pool = NULL; /* host_name */ host_name = iniparser_getstring( _d, get_ini_key_name(_name, CFG_N_LDAP_HOST), CFG_DEF_HOST_NAME); new_lds->host_name = (char*)pkg_malloc(strlen(host_name)+1); if (new_lds->host_name == NULL) { LM_ERR("no memory\n"); return -1; } strcpy(new_lds->host_name, host_name); /* version */ new_lds->version = iniparser_getint( _d, get_ini_key_name(_name, CFG_N_LDAP_VERSION), CFG_DEF_LDAP_VERSION); /* client_search_timeout */ client_search_timeout_ms = iniparser_getint( _d, get_ini_key_name(_name, CFG_N_LDAP_CLIENT_SEARCH_TIMEOUT), CFG_DEF_LDAP_CLIENT_SEARCH_TIMEOUT); if (client_search_timeout_ms < CFG_LDAP_CLIENT_SEARCH_TIMEOUT_MIN) { LM_INFO("[%s = %d ms] is below allowed min" " [%d ms] - [%s] set to [%d ms]\n", CFG_N_LDAP_CLIENT_SEARCH_TIMEOUT, client_search_timeout_ms, CFG_LDAP_CLIENT_SEARCH_TIMEOUT_MIN, CFG_N_LDAP_CLIENT_SEARCH_TIMEOUT, CFG_LDAP_CLIENT_SEARCH_TIMEOUT_MIN); client_search_timeout_ms = CFG_LDAP_CLIENT_SEARCH_TIMEOUT_MIN; } new_lds->client_search_timeout.tv_sec = client_search_timeout_ms / 1000; new_lds->client_search_timeout.tv_usec = (client_search_timeout_ms % 1000) * 1000; /* client_bind_timeout */ client_bind_timeout_ms = iniparser_getint( _d, get_ini_key_name(_name, CFG_N_LDAP_CLIENT_BIND_TIMEOUT), CFG_DEF_LDAP_CLIENT_BIND_TIMEOUT); new_lds->client_bind_timeout.tv_sec = client_bind_timeout_ms / 1000; new_lds->client_bind_timeout.tv_usec = (client_bind_timeout_ms % 1000) * 1000; /* network_timeout */ network_timeout_ms = iniparser_getint( _d, get_ini_key_name(_name, CFG_N_LDAP_NETWORK_TIMEOUT), LDAP_NO_LIMIT); new_lds->network_timeout.tv_sec = network_timeout_ms / 1000; new_lds->network_timeout.tv_usec = (network_timeout_ms % 1000) * 1000; /* bind_dn */ bind_dn = iniparser_getstring( _d, get_ini_key_name(_name, CFG_N_LDAP_BIND_DN), CFG_DEF_LDAP_BIND_DN); new_lds->bind_dn = (char*)pkg_malloc(strlen(bind_dn)+1); if (new_lds->bind_dn == NULL) { LM_ERR("no memory\n"); return -1; } strcpy(new_lds->bind_dn, bind_dn); /* bind_pwd */ bind_pwd = iniparser_getstring( _d, get_ini_key_name(_name, CFG_N_LDAP_BIND_PWD), CFG_DEF_LDAP_BIND_PWD); new_lds->bind_pwd = (char*)pkg_malloc(strlen(bind_pwd)+1); if (new_lds->bind_pwd == NULL) { LM_ERR("no memory\n"); return -1; } memset(new_lds->bind_pwd, 0, strlen(bind_pwd)+1); strcpy(new_lds->bind_pwd, bind_pwd); /* bind_cacert*/ bind_cacert = iniparser_getstring( _d, get_ini_key_name(_name, CFG_N_LDAP_CACERTFILE), CFG_DEF_LDAP_CACERTFILE); new_lds->cacertfile = (char*)pkg_malloc(strlen(bind_cacert)+1); if (new_lds->cacertfile == NULL) { LM_ERR("no memory\n"); return -1; } memset(new_lds->cacertfile, 0, strlen(bind_cacert)+1); strcpy(new_lds->cacertfile, bind_cacert); /* bind_certfile */ bind_cert = iniparser_getstring( _d, get_ini_key_name(_name, CFG_N_LDAP_CERTFILE), CFG_DEF_LDAP_CERTFILE); new_lds->certfile = (char*)pkg_malloc(strlen(bind_cert)+1); if (new_lds->certfile == NULL) { LM_ERR("no memory\n"); return -1; } memset(new_lds->certfile, 0, strlen(bind_cert)+1); strcpy(new_lds->certfile, bind_cert); /* bind_key */ bind_key = iniparser_getstring( _d, get_ini_key_name(_name, CFG_N_LDAP_KEYFILE), CFG_DEF_LDAP_KEYFILE); new_lds->keyfile = (char*)pkg_malloc(strlen(bind_key)+1); if (new_lds->keyfile == NULL) { LM_ERR("no memory\n"); return -1; } memset(new_lds->keyfile, 0, strlen(bind_key)+1); strcpy(new_lds->keyfile, bind_key); /* bind_req_cert */ bind_req_cert = iniparser_getstring( _d, get_ini_key_name(_name, CFG_N_LDAP_REQCERT), CFG_DEF_LDAP_REQCERT); new_lds->req_cert = (char*)pkg_malloc(strlen(bind_req_cert)+1); if (new_lds->req_cert == NULL) { LM_ERR("no memory\n"); return -1; } memset(new_lds->req_cert, 0, strlen(bind_req_cert)+1); strcpy(new_lds->req_cert, bind_req_cert); /* calculate_ha1 */ new_lds->calculate_ha1 = iniparser_getboolean( _d, get_ini_key_name(_name, CFG_N_CALCULATE_HA1), CFG_DEF_CALCULATE_HA1); if (current == NULL) { ld_sessions = new_lds; } else { while (current->next != NULL) { current = current->next; }; current->next = new_lds; } return 0; } struct ld_session* get_ld_session(char* _name) { struct ld_session* current = ld_sessions; if (_name == NULL) { LM_ERR("lds_name == NULL\n"); return NULL; } while (current != NULL) { if (strcmp(current->name, _name) == 0) { return current; } current = current->next; } return NULL; } int free_ld_sessions(void) { struct ld_session* current = ld_sessions; struct ld_session* tmp; while (current != NULL) { tmp = current->next; if (current->conn_s.handle) { ldap_disconnect(current->name, ¤t->conn_s); } if (current->conn_pool != NULL) { ldap_disconnect(current->name, NULL); } if (current->host_name != NULL) { pkg_free(current->host_name); } if (current->bind_dn != NULL) { pkg_free(current->bind_dn); } if (current->bind_pwd != NULL) { pkg_free(current->bind_pwd); } pkg_free(current); current = tmp; } ld_sessions = NULL; return 0; } char* get_ini_key_name(char* _section, char* _key) { sprintf(ini_key_name, "%s:%s", _section, _key); return ini_key_name; } opensips-2.2.2/modules/ldap/ld_session.h000066400000000000000000000063241300170765700202730ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #ifndef LD_SESSION_H #define LD_SESSION_H #include #include "iniparser.h" struct ld_conn { LDAP* handle; char is_used; struct ld_conn* next; }; struct ld_session { char name[256]; /** * pool of connections used for async queries */ struct ld_conn conn_s; /* synchronous connection */ struct ld_conn* conn_pool; /* async connections pool */ unsigned int pool_size; char* host_name; int version; int server_search_timeout; struct timeval client_search_timeout; struct timeval client_bind_timeout; struct timeval network_timeout; char* bind_dn; char* bind_pwd; int calculate_ha1; char* cacertfile; char* certfile; char* keyfile; char* req_cert; struct ld_session* next; }; #define CFG_N_LDAP_HOST "ldap_server_url" #define CFG_N_LDAP_VERSION "ldap_version" #define CFG_N_LDAP_SERVER_SEARCH_TIMEOUT "ldap_server_search_timeout" #define CFG_N_LDAP_CLIENT_SEARCH_TIMEOUT "ldap_client_search_timeout" #define CFG_N_LDAP_CLIENT_BIND_TIMEOUT "ldap_client_bind_timeout" #define CFG_N_LDAP_NETWORK_TIMEOUT "ldap_network_timeout" #define CFG_N_LDAP_BIND_DN "ldap_bind_dn" #define CFG_N_LDAP_BIND_PWD "ldap_bind_password" #define CFG_N_CALCULATE_HA1 "calculate_ha1" #define CFG_N_LDAP_CACERTFILE "ldap_ca_cert_file" #define CFG_N_LDAP_CERTFILE "ldap_cert_file" #define CFG_N_LDAP_KEYFILE "ldap_key_file" #define CFG_N_LDAP_REQCERT "ldap_require_certificate" #define CFG_DEF_HOST_NAME "" #define CFG_DEF_LDAP_SERVER_URL NULL #define CFG_DEF_LDAP_VERSION 3 #define CFG_DEF_LDAP_CLIENT_BIND_TIMEOUT 2000 #define CFG_DEF_LDAP_CLIENT_SEARCH_TIMEOUT 5000 #define CFG_DEF_LDAP_NETWORK_TIMEOUT 1000 #define CFG_DEF_LDAP_BIND_DN "" #define CFG_DEF_LDAP_BIND_PWD "" #define CFG_DEF_CALCULATE_HA1 1 #define CFG_LDAP_CLIENT_SEARCH_TIMEOUT_MIN 2000 #define CFG_DEF_LDAP_CACERTFILE "" #define CFG_DEF_LDAP_CERTFILE "" #define CFG_DEF_LDAP_KEYFILE "" #define CFG_DEF_LDAP_REQCERT "NEVER" extern int add_ld_session(char* _name, dictionary* _d); extern struct ld_session* get_ld_session(char* _name); extern int free_ld_sessions(); extern char* get_ini_key_name(char* _section, char* _key); #endif /* LD_SESSION_H */ opensips-2.2.2/modules/ldap/ldap_api_fn.c000066400000000000000000000366401300170765700203640ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #include #include #include "../../ut.h" #include #include "ldap_api_fn.h" #include "api.h" #include "ldap_connect.h" #include "ldap_escape.h" extern int max_async_connections; static LDAP* last_ldap_handle = NULL; static LDAPMessage* last_ldap_result = NULL; int get_connected_ldap_session( char* _lds_name, struct ld_session** _lds); int lds_search(char* _lds_name, char* _dn, int _scope, char* _filter, char** _attrs, struct timeval* _search_timeout, int* _ld_result_count, int* _ld_error); int lds_search_async( char* _lds_name, char* _dn, int _scope, char* _filter, char** _attrs, struct timeval* _search_timeout, int* _msgidp, struct ld_conn** conn); int load_ldap(ldap_api_t *api) { if (api == NULL) { return -1; } api->ldap_params_search = ldap_params_search; api->ldap_url_search = ldap_url_search; api->ldap_result_attr_vals = ldap_get_attr_vals; api->ldap_value_free_len = ldap_value_free_len; api->ldap_result_next = ldap_inc_result_pointer; api->ldap_str2scope = ldap_str2scope; api->ldap_rfc4515_escape = ldap_rfc4515_escape; api->get_ldap_handle = get_ldap_handle; api->get_last_ldap_result = get_last_ldap_result; return 1; } struct ld_conn* get_ldap_connection(struct ld_session* lds) { struct ld_conn* it; for (it=lds->conn_pool; it; it=it->next) { if (it->handle == NULL) { if (ldap_reconnect(lds->name, it)) { LM_ERR("ldap failed to reconnect!\n"); return NULL; } it->is_used = 1; return it; } if (it->is_used == 0) { it->is_used = 1; return it; } } /* if we're out of connections create a new one * only if we are allowed to; if limit reached return NULL */ if (max_async_connections <= lds->pool_size) { LM_DBG("async connection pool size limit reached!\n"); return NULL; } if (ldap_connect(lds->name, NULL) < 0) { LM_ERR("failed to create new ldap connection!\n"); return NULL; } /* newly created connection will be first in list */ lds->conn_pool->is_used = 1; return lds->conn_pool; } void release_ldap_connection(struct ld_conn* conn) { conn->is_used = 0; } int get_ldap_handle(char* _lds_name, LDAP** _ldap_handle) { int rc; struct ld_session* lds; rc = get_connected_ldap_session(_lds_name, &lds); *_ldap_handle = NULL; if (rc == 0) { *_ldap_handle = lds->conn_s.handle; } return rc; } int get_connected_ldap_session(char* _lds_name, struct ld_session** _lds) { /* * get ld session */ if ((*_lds = get_ld_session(_lds_name)) == NULL) { LM_ERR("[%s]: ldap_session not found\n", _lds_name); return -1; } return 0; } void get_last_ldap_result(LDAP** _last_ldap_handle, LDAPMessage** _last_ldap_result) { *_last_ldap_handle = last_ldap_handle; *_last_ldap_result = last_ldap_result; } int ldap_params_search_async( int* _msgidp, char* _lds_name, char* _dn, int _scope, char** _attrs, struct ld_conn** conn, char* _filter, ...) { int rc; static char filter_str[LDAP_MAX_FILTER_LEN]; va_list filter_vars; /* * check _scope */ switch (_scope) { case LDAP_SCOPE_ONELEVEL: case LDAP_SCOPE_BASE: case LDAP_SCOPE_SUBTREE: break; default: LM_ERR("[%s]: invalid scope argument [%d]\n", _lds_name, _scope); return -1; } /* * vsnprintf */ va_start(filter_vars, _filter); rc = vsnprintf(filter_str, (size_t)LDAP_MAX_FILTER_LEN, _filter, filter_vars); va_end(filter_vars); if (rc >= LDAP_MAX_FILTER_LEN) { LM_ERR( "[%s]: filter string too long (len [%d], max len [%d])\n", _lds_name, rc, LDAP_MAX_FILTER_LEN); return -1; } else if (rc < 0) { LM_ERR("vsnprintf failed\n"); return -1; } /* * ldap search */ if ((rc=lds_search_async(_lds_name, _dn, _scope, filter_str, _attrs, NULL, _msgidp, conn) ) < 0) { LM_ERR("async ldap_search failed!\n"); return -1; } return rc; } int ldap_params_search( int* _ld_result_count, char* _lds_name, char* _dn, int _scope, char** _attrs, char* _filter, ...) { int rc; static char filter_str[LDAP_MAX_FILTER_LEN]; va_list filter_vars; /* * check _scope */ switch (_scope) { case LDAP_SCOPE_ONELEVEL: case LDAP_SCOPE_BASE: case LDAP_SCOPE_SUBTREE: break; default: LM_ERR("[%s]: invalid scope argument [%d]\n", _lds_name, _scope); return -1; } /* * vsnprintf */ va_start(filter_vars, _filter); rc = vsnprintf(filter_str, (size_t)LDAP_MAX_FILTER_LEN, _filter, filter_vars); va_end(filter_vars); if (rc >= LDAP_MAX_FILTER_LEN) { LM_ERR( "[%s]: filter string too long (len [%d], max len [%d])\n", _lds_name, rc, LDAP_MAX_FILTER_LEN); return -1; } else if (rc < 0) { LM_ERR("vsnprintf failed\n"); return -1; } /* * ldap search */ if (lds_search(_lds_name, _dn, _scope, filter_str, _attrs, NULL, _ld_result_count, &rc) != 0) { /* try again if LDAP API ERROR */ if (LDAP_API_ERROR(rc) && (lds_search(_lds_name, _dn, _scope, filter_str, _attrs, NULL, _ld_result_count, &rc) != 0)) { LM_ERR( "[%s]: LDAP search (dn [%s], scope [%d]," " filter [%s]) failed: %s\n", _lds_name, _dn, _scope, filter_str, ldap_err2string(rc)); return -1; } } LM_DBG( "[%s]: [%d] LDAP entries found\n", _lds_name, *_ld_result_count); return 0; } int ldap_url_search_async( char* _ldap_url, int* _msgidp, struct ld_session **ldsp, struct ld_conn** conn, int* ld_result_count) { LDAPURLDesc *ludp; int rc; if (ldap_url_parse(_ldap_url, &ludp) != 0) { LM_ERR("invalid LDAP URL [%s]\n", ZSW(_ldap_url)); if (ludp != NULL) { ldap_free_urldesc(ludp); } return -2; } if (ludp->lud_host == NULL) { LM_ERR( "no ldap session name found in ldap URL [%s]\n", ZSW(_ldap_url)); return -2; } LM_DBG( "LDAP URL parsed into session_name" " [%s], base [%s], scope [%d], filter [%s]\n", ZSW(ludp->lud_host), ZSW(ludp->lud_dn), ludp->lud_scope, ZSW(ludp->lud_filter)); rc = ldap_params_search_async(_msgidp, ludp->lud_host, ludp->lud_dn, ludp->lud_scope, ludp->lud_attrs, conn, ludp->lud_filter); if ((rc == 0 && *_msgidp >= 0) || rc == 1) { if (get_connected_ldap_session(ludp->lud_host, ldsp)) { LM_ERR("[%s]: couldn't get ldap session\n", ludp->lud_host); return -1; } } /* sync mode; get the number of results */ if (rc == 1) { *ld_result_count = ldap_count_entries((*ldsp)->conn_s.handle, last_ldap_result); if (*ld_result_count < 0) { LM_ERR("[%s]: ldap_count_entries for sync call failed\n", (*ldsp)->name); return -1; } } ldap_free_urldesc(ludp); return rc; } int ldap_url_search( char* _ldap_url, int* _ld_result_count) { LDAPURLDesc *ludp; int rc; if (ldap_url_parse(_ldap_url, &ludp) != 0) { LM_ERR("invalid LDAP URL [%s]\n", ZSW(_ldap_url)); if (ludp != NULL) { ldap_free_urldesc(ludp); } return -2; } if (ludp->lud_host == NULL) { LM_ERR( "no ldap session name found in ldap URL [%s]\n", ZSW(_ldap_url)); return -2; } LM_DBG( "LDAP URL parsed into session_name" " [%s], base [%s], scope [%d], filter [%s]\n", ZSW(ludp->lud_host), ZSW(ludp->lud_dn), ludp->lud_scope, ZSW(ludp->lud_filter)); rc = ldap_params_search(_ld_result_count, ludp->lud_host, ludp->lud_dn, ludp->lud_scope, ludp->lud_attrs, ludp->lud_filter); ldap_free_urldesc(ludp); return rc; } int ldap_inc_result_pointer(void) { LDAPMessage *next_result = NULL; /* * check for last_ldap_result */ if (last_ldap_result == NULL) { LM_ERR("last_ldap_result == NULL\n"); return -1; } if (last_ldap_handle == NULL) { LM_ERR("last_ldap_handle == NULL\n"); return -1; } /* * get next LDAP result pointer */ if ((next_result = ldap_next_entry(last_ldap_handle, last_ldap_result)) == NULL) { /* no more LDAP entries */ return 1; } last_ldap_result = next_result; return 0; } int ldap_get_attr_vals(str *_attr_name, struct berval ***_vals) { BerElement *ber; char *a; /* * check for last_ldap_result */ if (last_ldap_result == NULL) { LM_ERR("last_ldap_result == NULL\n"); return -1; } if (last_ldap_handle == NULL) { LM_ERR("last_ldap_handle == NULL\n"); return -1; } /* * search for attribute named _attr_name */ *_vals = NULL; for (a = ldap_first_attribute(last_ldap_handle, last_ldap_result, &ber); a != NULL; a = ldap_next_attribute(last_ldap_handle, last_ldap_result, ber)) { if (strlen(a) == _attr_name->len && strncmp(a, _attr_name->s, _attr_name->len) == 0) { *_vals = ldap_get_values_len( last_ldap_handle, last_ldap_result, a); ldap_memfree(a); break; } ldap_memfree(a); } if (ber != NULL) { ber_free(ber, 0); } if (*_vals != NULL) { return 0; } else { return 1; } } int ldap_str2scope(char* scope_str) { if ( strcasecmp( scope_str, "one" ) == 0 ) { return LDAP_SCOPE_ONELEVEL; } else if ( strcasecmp( scope_str, "onelevel" ) == 0 ) { return LDAP_SCOPE_ONELEVEL; } else if ( strcasecmp( scope_str, "base" ) == 0 ) { return LDAP_SCOPE_BASE; } else if ( strcasecmp( scope_str, "sub" ) == 0 ) { return LDAP_SCOPE_SUBTREE; } else if ( strcasecmp( scope_str, "subtree" ) == 0 ) { return LDAP_SCOPE_SUBTREE; }; return( -1 ); } /* * sets last_ldap_result and last_ldap_handle only if async receive succeeds; * if successfull * 3 return possibilities: * a) message received - @return 0; @_msgidp -1 * b) failed to receive; reactor needed - @return 0; @_msgidp >=0 * c) other type of failure - @return < 0 */ int lds_resume( struct ld_session *lds, int msgidp, struct ld_conn* conn, int *ld_result_count) { int rc; struct timeval zerotime; zerotime.tv_sec = zerotime.tv_usec = 0L; rc = ldap_result(conn->handle, msgidp, LDAP_MSG_ALL, &zerotime, &last_ldap_result); switch (rc) { case -1: /* Leave the connection for other requests if we're not succesfull */ release_ldap_connection(conn); LM_ERR("[%s]: ldap result failed\n", lds->name); return -1; case 0: /* receive did not succeed; reactor needed */ LM_DBG("Timeout exceeded! Async operation not complete!\n"); return 0; default: /* receive successfull */ LM_DBG("Succesfully received response from ldap server!\n"); /* FIXME we release the connection now; calling another async * operation before calling ldap_result will break this mechanism, * since the connection is released and the handle is being kept * in last_ldap_handle global parameter * */ release_ldap_connection(conn); break; } last_ldap_handle = conn->handle; *ld_result_count = ldap_count_entries(conn->handle, last_ldap_result); if (*ld_result_count < 0) { LM_DBG("[%s]: ldap_count_entries failed\n", lds->name); return -1; } return 1; } int lds_search_async( char* _lds_name, char* _dn, int _scope, char* _filter, char** _attrs, struct timeval* _search_timeout, int* _msgidp, struct ld_conn** conn) { int ld_error, rc; struct ld_session* lds; #ifdef LDAP_PERF struct timeval before_search = { 0, 0 }, after_search = { 0, 0 }; #endif /* * get ld_handle */ if (get_connected_ldap_session(_lds_name, &lds) != 0) { LM_ERR("[%s]: couldn't get ldap session\n", _lds_name); return -1; } if ((*conn=get_ldap_connection(lds)) == NULL) { LM_DBG("No more connections available! will do synchronous query\n"); } LM_DBG( "[%s]: performing LDAP search: dn [%s]," " scope [%d], filter [%s], client_timeout [%d] usecs\n", _lds_name, _dn, _scope, _filter, (int)(lds->client_search_timeout.tv_sec * 1000000 + lds->client_search_timeout.tv_usec)); #ifdef LDAP_PERF gettimeofday(&before_search, NULL); #endif /* * perform ldap search */ if (*conn) { ld_error = ldap_search_ext( (*conn)->handle, _dn, _scope, _filter, _attrs, 0, NULL, NULL, &lds->client_search_timeout, 0, _msgidp); rc = 0; } else { /* falling back to sync */ ld_error = ldap_search_ext_s( lds->conn_s.handle, _dn, _scope, _filter, _attrs, 0, NULL, NULL, &lds->client_search_timeout, 0, &last_ldap_result); /* signal that the operation is sync to upper layers */ rc = 1; } #ifdef LDAP_PERF gettimeofday(&after_search, NULL); LM_INFO("[%s]: LDAP search took [%d] usecs\n", _lds_name, (int)((after_search.tv_sec * 1000000 + after_search.tv_usec) - (before_search.tv_sec * 1000000 + before_search.tv_usec))); #endif if (ld_error != LDAP_SUCCESS) { if (LDAP_API_ERROR(ld_error)) { ldap_disconnect(_lds_name, *conn); } LM_ERR( "[%s]: LDAP search (dn [%s], scope [%d]," " filter [%s]) failed: %s\n", _lds_name, _dn, _scope, _filter, ldap_err2string(ld_error)); return -1; } return rc; } /* * sets last_ldap_result and last_ldap_handle */ int lds_search( char* _lds_name, char* _dn, int _scope, char* _filter, char** _attrs, struct timeval* _search_timeout, int* _ld_result_count, int* _ld_error) { struct ld_session* lds; struct ld_conn* conn; #ifdef LDAP_PERF struct timeval before_search = { 0, 0 }, after_search = { 0, 0 }; #endif /* * get ld_handle */ if (get_connected_ldap_session(_lds_name, &lds) != 0) { LM_ERR("[%s]: couldn't get ldap session\n", _lds_name); return -1; } conn = &lds->conn_s; /* * free last_ldap_result */ if (last_ldap_result != NULL) { ldap_msgfree(last_ldap_result); last_ldap_result = NULL; } LM_DBG( "[%s]: performing LDAP search: dn [%s]," " scope [%d], filter [%s], client_timeout [%d] usecs\n", _lds_name, _dn, _scope, _filter, (int)(lds->client_search_timeout.tv_sec * 1000000 + lds->client_search_timeout.tv_usec)); #ifdef LDAP_PERF gettimeofday(&before_search, NULL); #endif /* * perform ldap search */ *_ld_error = ldap_search_ext_s( conn->handle, _dn, _scope, _filter, _attrs, 0, NULL, NULL, &lds->client_search_timeout, 0, &last_ldap_result); #ifdef LDAP_PERF gettimeofday(&after_search, NULL); LM_INFO("[%s]: LDAP search took [%d] usecs\n", _lds_name, (int)((after_search.tv_sec * 1000000 + after_search.tv_usec) - (before_search.tv_sec * 1000000 + before_search.tv_usec))); #endif if (*_ld_error != LDAP_SUCCESS) { if (last_ldap_result != NULL) { ldap_msgfree(last_ldap_result); last_ldap_result = NULL; } if (LDAP_API_ERROR(*_ld_error)) { ldap_disconnect(_lds_name, conn); } LM_DBG( "[%s]: ldap_search_ext_st failed: %s\n", _lds_name, ldap_err2string(*_ld_error)); return -1; } last_ldap_handle = conn->handle; *_ld_result_count = ldap_count_entries(conn->handle, last_ldap_result); if (*_ld_result_count < 0) { LM_DBG("[%s]: ldap_count_entries failed\n", _lds_name); return -1; } return 0; } opensips-2.2.2/modules/ldap/ldap_api_fn.h000066400000000000000000000041161300170765700203620ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #ifndef LDAP_API_FN_H #define LDAP_API_FN_H #include #include "../../str.h" #include "../../sr_module.h" #include "ld_session.h" #define LDAP_MAX_FILTER_LEN 1024 /* * LDAP API functions */ int lds_resume( struct ld_session *lds, int msgidp, struct ld_conn* conn, int *ld_result_count); int ldap_params_search_async( int* _msgidp, char* _lds_name, char* _dn, int _scope, char** _attrs, struct ld_conn** conn, char* _filter, ...); int ldap_params_search( int* _ld_result_count, char* _lds_name, char* _dn, int _scope, char** _attrs, char* _filter, ...); int ldap_url_search_async( char* _ldap_url, int* _msgidp, struct ld_session **ldsp, struct ld_conn** conn, int* ld_result_count); int ldap_url_search( char* _ldap_url, int* _ld_result_count); int ldap_get_attr_vals( str *_attr_name, struct berval ***_vals); int ldap_inc_result_pointer(); int ldap_str2scope(char* scope_str); int get_ldap_handle(char* _lds_name, LDAP** _ldap_handle); void get_last_ldap_result(LDAP** _last_ldap_handle, LDAPMessage** _last_ldap_result); void release_ldap_connection(struct ld_conn* conn); #endif /* LDAP_API_FN_H */ opensips-2.2.2/modules/ldap/ldap_connect.c000066400000000000000000000227721300170765700205620ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #include #include #include #include #include #include "ldap_connect.h" #include "ld_session.h" #include "../../mem/mem.h" #include "../../ut.h" #define W_SET_OPTION(handle, opt, str, name) \ do { \ if (ldap_set_option( handle, opt, str) \ != LDAP_OPT_SUCCESS) { \ LM_ERR("[%s]: could not set " # opt " [%s]\n" \ , name, str); \ return -1; \ } \ } while (0); static inline int ldap_word2upper(char* input) { int index=0; while (input[index] == ' ') input++; while (input[index] != '\0') { if (input[index] >= 'a' && input[index] <= 'z') { input[index++] -= 32; continue; } if (input[index] <= 'A' && input[index] >= 'Z') { LM_ERR("invalid req_cert parameter!" " must contain only letters\n"); return -1; } index++; } return 0; } static inline int get_req_cert_param(char* req_cert) { #define DWORD(s) ( *s + (*(s+1) << 8) + (*(s+2) << 16) + (*(s+3) << 24)) switch (DWORD(req_cert)) { case NEVE: if (*(req_cert+4) != 'R' || *(req_cert+5) != '\0') goto error; return LDAP_OPT_X_TLS_NEVER; case DEMA: if (*(req_cert+4) != 'N' || *(req_cert+5) != 'D' || *(req_cert+6) != '\0') goto error; return LDAP_OPT_X_TLS_DEMAND; case ALLO: if (*(req_cert+4) != 'W' || *(req_cert+5) != '\0') goto error; return LDAP_OPT_X_TLS_ALLOW; case HARD: if (*(req_cert+4) != '\0') goto error; return LDAP_OPT_X_TLS_HARD; case TRY: return LDAP_OPT_X_TLS_TRY; default : goto error; } error: LM_ERR("invalid req_cert parameter [%s]!" "OPTIONS: NEVER|DEMAND|ALLOW|HARD|TRY\n", req_cert); return -1; #undef DWORD } int ldap_connect(char* _ld_name, struct ld_conn* conn) { int rc; int ldap_proto_version; int req_cert_value; char *errmsg; struct ld_session* lds; struct berval ldap_cred; struct berval* ldap_credp; struct ld_conn* ldap_conn; LDAP* handle = NULL; /* struct berval* serv_cred = (struct berval*)pkg_malloc(sizeof(struct berval)); if(!serv_cred){ LM_ERR("Out of mem\n"); return -1; } */ /* * get ld session and session config parameters */ if ((lds = get_ld_session(_ld_name)) == NULL) { LM_ERR("ld_session [%s] not found\n", _ld_name); return -1; } /* * ldap_initialize */ rc = ldap_initialize(&handle, lds->host_name); if (rc != LDAP_SUCCESS) { LM_ERR( "[%s]: ldap_initialize (%s) failed: %s\n", _ld_name, lds->host_name, ldap_err2string(rc)); return -1; } /* * set LDAP OPTIONS */ /* LDAP_OPT_PROTOCOL_VERSION */ switch (lds->version) { case 2: ldap_proto_version = LDAP_VERSION2; break; case 3: ldap_proto_version = LDAP_VERSION3; break; default: LM_ERR( "[%s]: Invalid LDAP protocol version [%d]\n", _ld_name, lds->version); return -1; } if (ldap_set_option(handle, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto_version) != LDAP_OPT_SUCCESS) { LM_ERR( "[%s]: Could not set LDAP_OPT_PROTOCOL_VERSION [%d]\n", _ld_name, ldap_proto_version); return -1; } /* LDAP_OPT_RESTART */ if (ldap_set_option(handle, LDAP_OPT_RESTART, LDAP_OPT_ON) != LDAP_OPT_SUCCESS) { LM_ERR("[%s]: Could not set LDAP_OPT_RESTART to ON\n", _ld_name); return -1; } /* LDAP_OPT_TIMELIMIT */ /* if (lds->server_search_timeout > 0) { if (ldap_set_option(lds->handle, LDAP_OPT_TIMELIMIT, &lds->server_search_timeout) != LDAP_OPT_SUCCESS) { LM_ERR("[%s]: Could not set LDAP_OPT_TIMELIMIT to [%d]\n", _ld_name, lds->server_search_timeout); return -1; } } */ /* LDAP_OPT_NETWORK_TIMEOUT */ if ((lds->network_timeout.tv_sec > 0) || (lds->network_timeout.tv_usec > 0)) { if (ldap_set_option(handle, LDAP_OPT_NETWORK_TIMEOUT, (const void *)&lds->network_timeout) != LDAP_OPT_SUCCESS) { LM_ERR( "[%s]: Could not set" " LDAP_NETWORK_TIMEOUT to [%d.%d]\n", _ld_name, (int)lds->network_timeout.tv_sec, (int)lds->network_timeout.tv_usec); } } /* if timeout == 0 then use default */ if ((lds->client_bind_timeout.tv_sec == 0) && (lds->client_bind_timeout.tv_usec == 0)) { lds->client_bind_timeout.tv_sec = CFG_DEF_LDAP_CLIENT_BIND_TIMEOUT / 1000; lds->client_bind_timeout.tv_usec = (CFG_DEF_LDAP_CLIENT_BIND_TIMEOUT % 1000) * 1000; } rc = ldap_set_option(handle, LDAP_OPT_TIMEOUT, &lds->client_bind_timeout); if(rc != LDAP_SUCCESS){ LM_ERR("[%s]: ldap set option LDAP_OPT_TIMEOUT failed\n", _ld_name); return -1; } /* if no "ldap_bind_password" then anonymous */ ldap_cred.bv_val = lds->bind_pwd; ldap_cred.bv_len = strlen(lds->bind_pwd); if(ldap_cred.bv_len == 0 || ldap_cred.bv_val[0]==0){ ldap_credp = NULL; }else{ ldap_credp = &ldap_cred; } /* configure tls */ if (*lds->cacertfile && *lds->certfile && *lds->keyfile) { W_SET_OPTION(handle, LDAP_OPT_X_TLS_CACERTFILE, lds->cacertfile, _ld_name); W_SET_OPTION(handle, LDAP_OPT_X_TLS_CERTFILE, lds->certfile, _ld_name); W_SET_OPTION(handle, LDAP_OPT_X_TLS_KEYFILE, lds->keyfile, _ld_name); if (ldap_word2upper(lds->req_cert) != 0) return -1; if ((req_cert_value = get_req_cert_param(lds->req_cert)) < 0) return -1; if (ldap_set_option( handle, LDAP_OPT_X_TLS_REQUIRE_CERT, &req_cert_value) != LDAP_OPT_SUCCESS) { LM_ERR("[%s]: could not set LDAP_OPT_X_TLS_REQUIRE_CERT [%s]\n" , _ld_name, lds->req_cert); return -1; } int ret = ldap_start_tls_s(handle, NULL, NULL); switch (ret) { case LDAP_SUCCESS: LM_INFO("Using StartTLS for session [%s]\n", _ld_name); break; case LDAP_CONNECT_ERROR: ldap_get_option(handle, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void *)&errmsg); LM_ERR("ldap_Start_tls_s(): %s\n", errmsg); ldap_memfree(errmsg); ldap_unbind_ext(handle, NULL, NULL); return -1; default: LM_ERR("ldap_start_tls_s(): %s\n", ldap_err2string(ret)); ldap_unbind_ext(handle, NULL,NULL); return -1; } } else if (*lds->cacertfile || *lds->certfile || *lds->keyfile) { LM_WARN("ldap_ca_certfile, ldap_cert_file and ldap_key_file" " must be set in order to use StartTLS. " "No StartTLS configured!\n"); } /* * ldap_sasl_bind (LDAP_SASL_SIMPLE) */ rc = ldap_sasl_bind_s ( handle, lds->bind_dn, LDAP_SASL_SIMPLE, ldap_credp, NULL, NULL, NULL /*&serv_cred */ ); if (rc != LDAP_SUCCESS) { LM_ERR( "[%s]: ldap bind failed: %s\n", _ld_name, ldap_err2string(rc)); return -1; } LM_DBG( "[%s]: LDAP bind successful (ldap_host [%s])\n", _ld_name, lds->host_name); /* it's an already defined connection; just set the new handle */ if (conn) { conn->handle = handle; } else { ldap_conn = pkg_malloc(sizeof(struct ld_conn)); if (ldap_conn == NULL) { LM_ERR("no more pkg mem!\n"); return -1; } memset(ldap_conn, 0, sizeof(struct ld_conn)); ldap_conn->handle = handle; if (lds->conn_pool == NULL) { lds->conn_pool = ldap_conn; } else { ldap_conn->next = lds->conn_pool; lds->conn_pool = ldap_conn; } lds->pool_size++; } return 0; } int ldap_disconnect(char* _ld_name, struct ld_conn* conn) { struct ld_session* lds; struct ld_conn *foo=NULL, *it; /* * get ld session */ /* disconnect all and free */ if (!conn) { if ((lds = get_ld_session(_ld_name)) == NULL) { LM_ERR("ld_session [%s] not found\n", _ld_name); return -1; } if (lds->conn_pool == NULL) { return 0; } for (it=lds->conn_pool; it; foo=it, it=it->next) { ldap_unbind_ext(it->handle, NULL, NULL); if (foo) pkg_free(foo); } /* check for last element in list */ if (foo) pkg_free(foo); lds->conn_pool = NULL; } else { ldap_unbind_ext(conn->handle, NULL, NULL); conn->handle = NULL; conn->is_used = 0; } return 0; } int ldap_reconnect(char* _ld_name, struct ld_conn* conn) { int rc; if (ldap_disconnect(_ld_name, conn) != 0) { LM_ERR("[%s]: disconnect failed\n", _ld_name); return -1; } if ((rc = ldap_connect(_ld_name, conn)) != 0) { LM_ERR("[%s]: reconnect failed\n", _ld_name); } else { LM_DBG("[%s]: LDAP reconnect successful\n", _ld_name); } return rc; } int ldap_get_vendor_version(char** _version) { static char version[128]; LDAPAPIInfo api; int rc; #ifdef LDAP_API_INFO_VERSION api.ldapai_info_version = LDAP_API_INFO_VERSION; #else api.ldapai_info_version = 1; #endif if (ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) != LDAP_SUCCESS) { LM_ERR("ldap_get_option(API_INFO) failed\n"); return -1; } rc = snprintf(version, 128, "%s - %d", api.ldapai_vendor_name, api.ldapai_vendor_version); if ((rc >= 128) || (rc < 0)) { LM_ERR("snprintf failed\n"); return -1; } *_version = version; return 0; } #undef W_SET_OPTION opensips-2.2.2/modules/ldap/ldap_connect.h000066400000000000000000000027671300170765700205710ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #ifndef LDAP_CONNECT_H #define LDAP_CONNECT_H #include "../../str.h" #include "../../dprint.h" #define NEVE 0x4556454E #define DEMA 0x414D4544 #define ALLO 0x4F4C4C41 #define HARD 0x44524148 #define TRY 0x00595254 /* forward declaration for this structure */ struct ld_conn; extern int ldap_connect(char* _ld_name, struct ld_conn* conn); extern int ldap_disconnect(char* _ld_name, struct ld_conn* conn); extern int ldap_reconnect(char* _ld_name, struct ld_conn* conn); extern int ldap_get_vendor_version(char** _version); #endif /* LDAP_CONNECT_H */ opensips-2.2.2/modules/ldap/ldap_escape.c000066400000000000000000000041761300170765700203670ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #include #include #include "ldap_escape.h" static const char hex[] = "0123456789ABCDEF"; /* * escape string following RFC 4515 (LDAP filter syntax) escaping rules: * * * --> \2a * ( --> \28 * ) --> \29 * \ --> \5c * * and percent encode '?' according RFC 4516 (LDAP URL), Section 2.1 * if url_encode equals TRUE * * ? --> %3F * */ int ldap_rfc4515_escape(str *sin, str *sout, int url_encode) { char *src, *dst; if (sin == NULL || sout == NULL || sin->s == NULL || sout->s == NULL || sin->len <= 0 || sout->len < 3*sin->len+1) { return -1; } src = sin->s; dst = sout->s; while (src < (sin->s + sin->len)) { switch (*src) { case '*': *dst++ = '\\'; *dst++ = '2'; *dst = 'a'; break; case '(': *dst++ = '\\'; *dst++ = '2'; *dst = '8'; break; case ')': *dst++ = '\\'; *dst++ = '2'; *dst = '9'; break; case '\\': *dst++ = '\\'; *dst++ = '5'; *dst = 'c'; break; case '?': if (url_encode) { *dst++ = '%'; *dst++ = '3'; *dst = 'F'; } else { *dst = *src; } break; default: *dst = *src; } src++; dst++; } *dst = 0; sout->len = dst - sout->s; return 0; } opensips-2.2.2/modules/ldap/ldap_escape.h000066400000000000000000000021321300170765700203620ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #ifndef _LDAP_ESCAPE_H_ #define _LDAP_ESCAPE_H_ #include "../../str.h" int ldap_rfc4515_escape(str *sin, str *sout, int url_encode); #endif opensips-2.2.2/modules/ldap/ldap_exp_fn.c000066400000000000000000000215701300170765700204030ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #include #include #include #include "../../ut.h" #include "../../str.h" #include "../../pvar.h" #include "../../usr_avp.h" #include "../../mem/mem.h" #include "../../async.h" #include "ldap_exp_fn.h" #include "ldap_connect.h" #include "ldap_api_fn.h" #include "ldap_escape.h" #define STR_BUF_SIZE 1024 #define ESC_BUF_SIZE 65536 static char esc_buf[ESC_BUF_SIZE]; /* * exported functions */ int resume_ldap_search(int fd, struct sip_msg *msg, void *param) { int ld_result_count = 0, rc; struct ldap_async_params *as_params; as_params = (struct ldap_async_params*) param; rc = lds_resume( as_params->lds, as_params->msgid, as_params->conn, &ld_result_count); switch (rc) { case -1: /* error */ pkg_free(as_params); return -1; case 0: /* put back in reactor */ async_status = ASYNC_CONTINUE; return 1; case 1: /* successfull */ pkg_free(as_params); async_status = ASYNC_DONE; break; default: /* invalid rc */ LM_BUG("invalid return code\n"); return -1; } if (ld_result_count < 1) { /* no LDAP entry found */ LM_DBG("no LDAP entry found\n"); return -1; } return ld_result_count; } int ldap_search_impl_async( struct sip_msg* _msg, async_resume_module **resume_f, void **resume_param, pv_elem_t* _ldap_url) { str ldap_url; int msgid; int sockfd; int rc=-1; int ld_result_count; struct ldap_async_params *as_params; struct ld_session *lds; struct ld_conn* conn; /* * do variable substitution for _ldap_url (pv_printf_s) */ if (_ldap_url==NULL) { LM_ERR("empty ldap_url\n"); return -2; } if ( _ldap_url->spec.getf!=NULL) { if (pv_printf_s( _msg, _ldap_url, &ldap_url)!=0 || ldap_url.len<=0) { LM_ERR("pv_printf_s failed\n"); return -2; } } else { ldap_url = _ldap_url->text; } /* * perform LDAP search */ if ((rc=ldap_url_search_async(ldap_url.s, &msgid, &lds, &conn, &ld_result_count)) < 0) { /* LDAP search error */ rc = -2; goto error; } /* operation was completed in sync mode */ if (rc == 1) { async_status = ASYNC_NO_IO; if (ld_result_count == 0) { /* no LDAP entry found */ LM_DBG("no LDAP entry found\n"); return -1; } return ld_result_count; } if (lds == NULL) { LM_ERR("invalid session handle\n"); goto error; } if (ldap_get_option(conn->handle, LDAP_OPT_DESC, &sockfd) != LDAP_SUCCESS) { LM_ERR("failed to get ldap sockbuf\n"); goto error; } as_params = pkg_malloc(sizeof(struct ldap_async_params)); if (as_params == NULL) { LM_ERR("no more pkg mem\n"); goto error; } as_params->msgid = msgid; as_params->lds = lds; as_params->conn = conn; *resume_param = as_params; *resume_f = resume_ldap_search;/* resume function */ async_status = sockfd; return 1; error: release_ldap_connection(conn); return rc; } int ldap_search_impl( struct sip_msg* _msg, pv_elem_t* _ldap_url) { str ldap_url; int ld_result_count = 0; /* * do variable substitution for _ldap_url (pv_printf_s) */ if (_ldap_url==NULL) { LM_ERR("empty ldap_url\n"); return -2; } if ( _ldap_url->spec.getf!=NULL) { if (pv_printf_s( _msg, _ldap_url, &ldap_url)!=0 || ldap_url.len<=0) { LM_ERR("pv_printf_s failed\n"); return -2; } } else { ldap_url = _ldap_url->text; } /* * perform LDAP search */ if (ldap_url_search(ldap_url.s, &ld_result_count) != 0) { /* LDAP search error */ return -2; } if (ld_result_count < 1) { /* no LDAP entry found */ LM_DBG("no LDAP entry found\n"); return -1; } return ld_result_count; } int ldap_write_result( struct sip_msg* _msg, struct ldap_result_params* _lrp, struct subst_expr* _se) { int dst_avp_name; int_str dst_avp_val; unsigned short dst_avp_type; int nmatches, rc, i, added_avp_count = 0; struct berval **attr_vals; str avp_val_str, *subst_result = NULL; int avp_val_int; /* * get dst AVP name (dst_avp_name) */ if (pv_get_avp_name( _msg, &(_lrp->dst_avp_spec.pvp), &dst_avp_name, &dst_avp_type) != 0) { LM_ERR("error getting dst AVP name\n"); return -2; } /* * get LDAP attr values */ if ((rc = ldap_get_attr_vals(&_lrp->ldap_attr_name, &attr_vals)) != 0) { if (rc > 0) { return -1; } else { return -2; } } /* * add AVPs */ for (i = 0; attr_vals[i] != NULL; i++) { if (_se == NULL) { avp_val_str.s = attr_vals[i]->bv_val; avp_val_str.len = attr_vals[i]->bv_len; } else { subst_result = subst_str(attr_vals[i]->bv_val, _msg, _se, &nmatches); if ((subst_result == NULL) || (nmatches < 1)) { continue; } avp_val_str = *subst_result; } if (_lrp->dst_avp_val_type == 1) { /* try to convert ldap value to integer */ if (!str2sint(&avp_val_str, &avp_val_int)) { dst_avp_val.n = avp_val_int; rc = add_avp(dst_avp_type, dst_avp_name, dst_avp_val); } else { continue; } } else { /* save ldap value as string */ dst_avp_val.s = avp_val_str; rc = add_avp(dst_avp_type|AVP_VAL_STR, dst_avp_name, dst_avp_val); } if (subst_result != NULL) { if (subst_result->s != 0) { pkg_free(subst_result->s); } pkg_free(subst_result); subst_result = NULL; } if (rc < 0) { LM_ERR("failed to create new AVP\n"); ldap_value_free_len(attr_vals); return -2; } added_avp_count++; } ldap_value_free_len(attr_vals); if (added_avp_count > 0) { return added_avp_count; } else { return -1; } } int ldap_result_next(void) { int rc; rc = ldap_inc_result_pointer(); switch (rc) { case 1: return -1; case 0: return 1; case -1: default: return -2; } } int ldap_result_check( struct sip_msg* _msg, struct ldap_result_check_params* _lrp, struct subst_expr* _se) { str check_str, *subst_result = NULL; int rc, i, nmatches; char *attr_val; struct berval **attr_vals; /* * do variable substitution for check_str */ if (_lrp->check_str_elem_p) { if (pv_printf_s(_msg, _lrp->check_str_elem_p, &check_str) != 0) { LM_ERR("pv_printf_s failed\n"); return -2; } } else { LM_ERR("empty check string\n"); return -2; } LM_DBG("check_str [%s]\n", check_str.s); /* * get LDAP attr values */ if ((rc = ldap_get_attr_vals(&_lrp->ldap_attr_name, &attr_vals)) != 0) { if (rc > 0) { return -1; } else { return -2; } } /* * loop through attribute values */ for (i = 0; attr_vals[i] != NULL; i++) { if (_se == NULL) { attr_val = attr_vals[i]->bv_val; } else { subst_result = subst_str(attr_vals[i]->bv_val, _msg, _se, &nmatches); if ((subst_result == NULL) || (nmatches < 1)) { continue; } attr_val = subst_result->s; } LM_DBG("attr_val [%s]\n", attr_val); rc = strncmp(check_str.s, attr_val, check_str.len); if (_se != NULL) { pkg_free(subst_result->s); } if (rc == 0) { ldap_value_free_len(attr_vals); return 1; } } ldap_value_free_len(attr_vals); return -1; } int ldap_filter_url_encode( struct sip_msg* _msg, pv_elem_t* _filter_component, pv_spec_t* _dst_avp_spec) { str filter_component_str, esc_str; int dst_avp_name; unsigned short dst_avp_type; /* * variable substitution for _filter_component */ if (_filter_component) { if (pv_printf_s(_msg, _filter_component, &filter_component_str) != 0) { LM_ERR("pv_printf_s failed\n"); return -1; } } else { LM_ERR("empty first argument\n"); return -1; } /* * get dst AVP name (dst_avp_name) */ if (pv_get_avp_name(_msg, &(_dst_avp_spec->pvp), &dst_avp_name, &dst_avp_type) != 0) { LM_ERR("error getting dst AVP name\n"); return -1; } /* * apply LDAP filter escaping rules */ esc_str.s = esc_buf; esc_str.len = ESC_BUF_SIZE; if (ldap_rfc4515_escape(&filter_component_str, &esc_str, 1) != 0) { LM_ERR("ldap_rfc4515_escape() failed\n"); return -1; } /* * add dst AVP */ if (add_avp(dst_avp_type|AVP_VAL_STR, dst_avp_name, (int_str)esc_str) != 0) { LM_ERR("failed to add new AVP\n"); return -1; } return 1; } opensips-2.2.2/modules/ldap/ldap_exp_fn.h000066400000000000000000000041341300170765700204050ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #ifndef LDAP_EXP_FN_H #define LDAP_EXP_FN_H #include "../../str.h" #include "../../pvar.h" #include "../../parser/msg_parser.h" #include "../../re.h" #include "ld_session.h" struct ldap_result_params { str ldap_attr_name; int dst_avp_val_type; /* 0: str, 1: int */ pv_spec_t dst_avp_spec; }; struct ldap_result_check_params { str ldap_attr_name; pv_elem_p check_str_elem_p; }; struct ldap_async_params { int msgid; struct ld_session *lds; struct ld_conn *conn; }; int ldap_search_impl_async( struct sip_msg* _msg, async_resume_module **resume_f, void **resume_param, pv_elem_t* _ldap_url); int ldap_search_impl( struct sip_msg* _msg, pv_elem_t* _ldap_url); int ldap_write_result( struct sip_msg* _msg, struct ldap_result_params* _lrp, struct subst_expr* _se); int ldap_result_next(); int ldap_filter_url_encode( struct sip_msg* _msg, pv_elem_t* _filter_component, pv_spec_t* _dst_avp_spec); int rfc2254_escape( struct sip_msg* _msg, char* _value, char* _avp_name); int ldap_result_check( struct sip_msg* _msg, struct ldap_result_check_params* _lrp, struct subst_expr* _se); #endif /* LDAP_EXP_FN_H */ opensips-2.2.2/modules/ldap/ldap_mod.c000066400000000000000000000321561300170765700177050ustar00rootroot00000000000000/* * OpenSIPS LDAP Module * * Copyright (C) 2007 University of North Carolina * * Original author: Christian Schlatter, cs@unc.edu * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-18: Initial version */ #include #include #include #include "../../sr_module.h" #include "../../ut.h" #include "../../parser/hf.h" #include "../../pvar.h" #include "../../mem/mem.h" #include "ld_session.h" #include "ldap_exp_fn.h" #include "api.h" #include "ldap_connect.h" #include "ldap_api_fn.h" #include "iniparser.h" int max_async_connections=30; /* * Module management function prototypes */ static int mod_init(void); static void destroy(void); static int child_init(int rank); /* * fixup functions */ static int ldap_search_fixup(void** param, int param_no); static int ldap_result_fixup(void** param, int param_no); static int ldap_filter_url_encode_fixup(void** param, int param_no); static int ldap_result_check_fixup(void** param, int param_no); /* * exported functions */ static int w_ldap_search_async(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char* ldap_url, char* param); static int w_ldap_search(struct sip_msg* msg, char* ldap_url, char* param); static int w_ldap_result1(struct sip_msg* msg, char* src, char* param); static int w_ldap_result2(struct sip_msg* msg, char* src, char* subst); static int w_ldap_result_next(struct sip_msg* msg, char* foo, char *bar); static int w_ldap_filter_url_encode(struct sip_msg* msg, char* filter_component, char* dst_avp_name); static int w_ldap_result_check_1(struct sip_msg* msg, char* attr_name_check_str, char* param); static int w_ldap_result_check_2(struct sip_msg* msg, char* attr_name_check_str, char* attr_val_re); /* * Default module parameter values */ #define DEF_LDAP_CONFIG "/usr/local/etc/opensips/ldap.cfg" #define DEF_REQ_CERT "NEVER" /* * Module parameter variables */ str ldap_config = str_init(DEF_LDAP_CONFIG); static dictionary* config_vals = NULL; static acmd_export_t acmds[] = { {"ldap_search", (acmd_function) w_ldap_search_async, 1, ldap_search_fixup}, {0, 0, 0, 0} }; /* * Exported functions */ static cmd_export_t cmds[] = { {"ldap_search", (cmd_function)w_ldap_search, 1, ldap_search_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"ldap_result", (cmd_function)w_ldap_result1, 1, ldap_result_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"ldap_result", (cmd_function)w_ldap_result2, 2, ldap_result_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"ldap_result_next", (cmd_function)w_ldap_result_next, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE| LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"ldap_result_check", (cmd_function)w_ldap_result_check_1, 1, ldap_result_check_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE| BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"ldap_result_check", (cmd_function)w_ldap_result_check_2, 2, ldap_result_check_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE| BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"ldap_filter_url_encode", (cmd_function)w_ldap_filter_url_encode, 2, ldap_filter_url_encode_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE| BRANCH_ROUTE|ONREPLY_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"load_ldap", (cmd_function)load_ldap, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"config_file", STR_PARAM, &ldap_config.s}, {"max_async_connections", INT_PARAM, &max_async_connections}, {0, 0, 0} }; /* * Module interface */ struct module_exports exports = { "ldap", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ acmds, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; static int child_init(int rank) { int i = 0, ld_count = 0; char* ld_name; /* don't do anything for non-worker process */ if (rank==PROC_MAIN || rank==PROC_TCP_MAIN) { return 0; } /* * build ld_sessions and connect all sessions */ ld_count = iniparser_getnsec(config_vals); for (i = 0; i < ld_count; i++) { ld_name = iniparser_getsecname(config_vals, i); if (add_ld_session(ld_name, config_vals) != 0) { LM_ERR("[%s]: add_ld_session failed\n", ld_name); return -1; } /* won't check for null in get_ld_session since it's barely been initialized */ if (ldap_connect(ld_name, &get_ld_session(ld_name)->conn_s) != 0) { LM_ERR("[%s]: failed to connect to LDAP host(s)\n", ld_name); ldap_disconnect(ld_name, NULL); return -1; } } return 0; } static int mod_init(void) { int ld_count = 0, i = 0; char* section_name; char* ldap_version; LM_INFO("LDAP_H350 module - initializing\n"); /* * read config file */ if (strlen(ldap_config.s) == 0) { LM_ERR("config_file is empty - this module param is mandatory\n"); return -2; } if ((config_vals = iniparser_new(ldap_config.s)) == NULL) { LM_ERR("failed to read config_file [%s]\n", ldap_config.s); return -2; } if ((ld_count = iniparser_getnsec(config_vals)) < 1) { LM_ERR("no section found in config_file [%s]\n", ldap_config.s); return -2; } /* check if mandatory settings are present */ for (i = 0; i < ld_count; i++) { section_name = iniparser_getsecname(config_vals, i); if (strlen(section_name) > 255) { LM_ERR( "config_file section name [%s]" " longer than allowed 255 characters", section_name); return -2; } if (!iniparser_find_entry(config_vals, get_ini_key_name(section_name, CFG_N_LDAP_HOST))) { LM_ERR( "mandatory %s not defined in [%s]\n", CFG_N_LDAP_HOST, section_name); return -2; } } /* * print ldap version string */ if (ldap_get_vendor_version(&ldap_version) != 0) { LM_ERR("ldap_get_vendor_version failed\n"); return -2; } return 0; } static void destroy(void) { /* ldap_unbind */ free_ld_sessions(); /* free config file memory */ iniparser_free(config_vals); } /* * EXPORTED functions */ static int w_ldap_search_async(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char* ldap_url, char* param) { return ldap_search_impl_async(msg, resume_f, resume_param, (pv_elem_t*)ldap_url); } static int w_ldap_search(struct sip_msg* msg, char* ldap_url, char* param) { return ldap_search_impl(msg, (pv_elem_t*)ldap_url); } static int w_ldap_result1(struct sip_msg* msg, char* src, char* param) { return ldap_write_result(msg, (struct ldap_result_params*)src, NULL); } static int w_ldap_result2(struct sip_msg* msg, char* src, char* subst) { return ldap_write_result(msg, (struct ldap_result_params*)src, (struct subst_expr*)subst); } static int w_ldap_result_next(struct sip_msg* msg, char* foo, char *bar) { return ldap_result_next(); } static int w_ldap_filter_url_encode(struct sip_msg* msg, char* filter_component, char* dst_avp_name) { return ldap_filter_url_encode(msg, (pv_elem_t*)filter_component, (pv_spec_t*)dst_avp_name); } static int w_ldap_result_check_1(struct sip_msg* msg, char* attr_name_check_str, char* param) { return ldap_result_check(msg, (struct ldap_result_check_params*)attr_name_check_str, NULL); } static int w_ldap_result_check_2(struct sip_msg* msg, char* attr_name_check_str, char* attr_val_re) { return ldap_result_check( msg, (struct ldap_result_check_params*)attr_name_check_str, (struct subst_expr*)attr_val_re); } /* * FIXUP functions */ static int ldap_search_fixup(void** param, int param_no) { pv_elem_t *model; str s; if (param_no == 1) { s.s = (char*)*param; s.len = strlen(s.s); if (s.len==0) { LM_ERR("ldap url is empty string!\n"); return E_CFG; } if ( pv_parse_format(&s,&model) || model==NULL) { LM_ERR("wrong format [%s] for ldap url!\n", s.s); return E_CFG; } *param = (void*)model; } return 0; } static int ldap_result_fixup(void** param, int param_no) { struct ldap_result_params* lp; struct subst_expr* se; str subst; char *arg_str, *dst_avp_str, *dst_avp_val_type_str; char *p; str s; int dst_avp_val_type = 0; if (param_no == 1) { arg_str = (char*)*param; if ((dst_avp_str = strchr(arg_str, '/')) == 0) { /* no / found in arg_str */ LM_ERR("invalid first argument [%s]\n", arg_str); return E_UNSPEC; } *(dst_avp_str++) = 0; if ((dst_avp_val_type_str = strchr(dst_avp_str, '/'))) { *(dst_avp_val_type_str++) = 0; if (!strcmp(dst_avp_val_type_str, "int")) { dst_avp_val_type = 1; } else if (strcmp(dst_avp_val_type_str, "str")) { LM_ERR( "invalid avp_type [%s]\n", dst_avp_val_type_str); return E_UNSPEC; } } lp = (struct ldap_result_params*)pkg_malloc(sizeof(struct ldap_result_params)); if (lp == NULL) { LM_ERR("no memory\n"); return E_OUT_OF_MEM; } memset(lp, 0, sizeof(struct ldap_result_params)); lp->ldap_attr_name.s = arg_str; lp->ldap_attr_name.len = strlen(arg_str); lp->dst_avp_val_type = dst_avp_val_type; s.s = dst_avp_str; s.len = strlen(s.s); p = pv_parse_spec(&s, &lp->dst_avp_spec); if (p == 0) { pkg_free(lp); LM_ERR("parse error for [%s]\n", dst_avp_str); return E_UNSPEC; } if (lp->dst_avp_spec.type != PVT_AVP) { pkg_free(lp); LM_ERR( "bad attribute name [%s]\n", dst_avp_str); return E_UNSPEC; } *param = (void*)lp; } else if (param_no == 2) { subst.s = *param; subst.len = strlen(*param); se = subst_parser(&subst); if (se == 0) { LM_ERR("bad subst re [%s]\n", (char*)*param); return E_BAD_RE; } *param = (void*)se; } return 0; } static int ldap_result_check_fixup(void** param, int param_no) { struct ldap_result_check_params *lp; struct subst_expr *se; str subst; str s; char *arg_str, *check_str; int arg_str_len; if (param_no == 1) { arg_str = (char*)*param; arg_str_len = strlen(arg_str); if ((check_str = strchr(arg_str, '/')) == 0) { /* no / found in arg_str */ LM_ERR( "invalid first argument [%s] (no '/' found)\n", arg_str); return E_UNSPEC; } *(check_str++) = 0; lp = (struct ldap_result_check_params*)pkg_malloc(sizeof(struct ldap_result_check_params)); if (lp == NULL) { LM_ERR("no memory\n"); return E_OUT_OF_MEM; } memset(lp, 0, sizeof(struct ldap_result_check_params)); lp->ldap_attr_name.s = arg_str; lp->ldap_attr_name.len = strlen(arg_str); if (lp->ldap_attr_name.len + 1 == arg_str_len) { /* empty check_str */ lp->check_str_elem_p = 0; } else { s.s = check_str; s.len = strlen(s.s); if (pv_parse_format(&s, &(lp->check_str_elem_p)) < 0) { LM_ERR("pv_parse_format failed\n"); return E_OUT_OF_MEM; } } *param = (void*)lp; } else if (param_no == 2) { subst.s = *param; subst.len = strlen(*param); se = subst_parser(&subst); if (se == 0) { LM_ERR( "bad subst re [%s]\n", (char*)*param); return E_BAD_RE; } *param = (void*)se; } return 0; } static int ldap_filter_url_encode_fixup(void** param, int param_no) { pv_elem_t *elem_p; pv_spec_t *spec_p; str s; if (param_no == 1) { s.s = (char*)*param; if (s.s==0 || s.s[0]==0) { elem_p = 0; } else { s.len = strlen(s.s); if (pv_parse_format(&s, &elem_p) < 0) { LM_ERR("pv_parse_format failed\n"); return E_OUT_OF_MEM; } } *param = (void*)elem_p; } else if (param_no == 2) { spec_p = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); if (spec_p == NULL) { LM_ERR("no memory\n"); return E_OUT_OF_MEM; } s.s = (char*)*param; s.len = strlen(s.s); if (pv_parse_spec(&s, spec_p) == 0) { pkg_free(spec_p); LM_ERR("parse error for [%s]\n", (char*)*param); return E_UNSPEC; } if (spec_p->type != PVT_AVP) { pkg_free(spec_p); LM_ERR("bad attribute name" " [%s]\n", (char*)*param); return E_UNSPEC; } *param = (void*)spec_p; } return 0; } opensips-2.2.2/modules/load_balancer/000077500000000000000000000000001300170765700176015ustar00rootroot00000000000000opensips-2.2.2/modules/load_balancer/Makefile000066400000000000000000000002721300170765700212420ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=load_balancer.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/load_balancer/README000066400000000000000000000444101300170765700204640ustar00rootroot00000000000000Load-Balancer Module Bogdan-Andrei Iancu Edited by Bogdan-Andrei Iancu Copyright © 2009 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How it works 1.3. Probing and Disabling destinations 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. db_url (string) 1.5.2. db_table (string) 1.5.3. probing_interval (integer) 1.5.4. probing_method (string) 1.5.5. probing_from (string) 1.5.6. probing_reply_codes (string) 1.5.7. lb_define_blacklist (string) 1.6. Exported Functions 1.6.1. lb_start(grp,resources[,flags]) 1.6.2. lb_next() 1.6.3. lb_start_or_next(grp,resources[,flags]) 1.6.4. load_balance(grp,resources[,flags]) 1.6.5. lb_reset() 1.6.6. lb_is_started() 1.6.7. lb_disable_dst() 1.6.8. lb_is_destination(ip,port[,group[,active]]) 1.6.9. lb_count_call(ip,port,grp,resources[,undo]) 1.7. Exported statistics 1.8. Exported MI Functions 1.8.1. lb_reload 1.8.2. lb_resize 1.8.3. lb_list 1.8.4. lb_status 1.9. Exported Events 1.9.1. E_LOAD_BALANCER_STATUS 2. Developer Guide 2.1. Available Functions 3. Frequently Asked Questions List of Examples 1.1. Set db_url parameter 1.2. Set db_table parameter 1.3. Set probing_interval parameter 1.4. Set probing_method parameter 1.5. Set probing_from parameter 1.6. Set probing_reply_codes parameter 1.7. Set the lb_define_blacklist parameter 1.8. lb_start usage 1.9. lb_next() usage 1.10. lb_next() usage 1.11. lb_disable_dst() usage 1.12. lb_is_destination usage 1.13. lb_count_call usage 1.14. lb_list usage 1.15. lb_status usage Chapter 1. Admin Guide 1.1. Overview The Load-Balancer module comes to provide traffic routing based on load. Shortly, when OpenSIPS routes calls to a set of destinations, it is able to keep the load status (as number of ongoing calls) of each destination and to choose to route to the less loaded destination (at that moment). OpenSIPS is aware of the capacity of each destination - it is preconfigured with the maximum load accepted by the destinations. To be more precise, when routing, OpenSIPS will consider the less loaded destination not the destination with the smallest number of ongoing calls, but the destination with the largest available slot. Also the module has the capability to do failover (to try a new destination if the selected one done not responde), to keep state of the destinations (to remember the failed destination and avoid using them agai) and to check the health of the destination (by doing probing of the destination and auto re-enabling). 1.2. How it works Please refer to the Load-Balancer tutorial from the OpenSIPS website: http://www.opensips.org/Documentation/Tutorials-LoadBalancing-1 -9. 1.3. Probing and Disabling destinations The module has the capability to monitor the status of the destinations by doing SIP probing (sending SIP requests like OPTIONS). For each destination, you can configure what kind of probing should be done (probe_mode column): * (0) - no probing at all; * (1) - probing only when the destination is in disabled mode (disabling via MI command will competely stop the probing also). The destination will be automatically re-enabled when the probing will succeed next time; * (2) - probing all the time. If disabled, the destination will be automatically re-enabled when the probing will succeed next time; A destination can become disabled in two ways: * script detection - by calling from script the lb_disabled() function after try the destination. In this case, if probing mode for the destination is (1) or (2), the destination will be automatically re-enabled when the probing will succeed. * MI command - by calling the lb_status MI command for disabling (on demand) the destination. If so, the probing and re-enabling of this destination will be completly disabled until you re-enable it again via MI command - this is designed to allow controlled and complete disabling of some destination during maintenance. 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * Dialog - Dialog module * Dialog - TM module (only if probing enabled) * database - one of the DB modules 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.5. Exported Parameters 1.5.1. db_url (string) The URL pointing to the database where the load-balancing rules are stored. Default value is “mysql://opensips:opensipsrw@localhost/opensipsâ€. Example 1.1. Set db_url parameter ... modparam("load_balancer", "db_url", "dbdriver://username:password@dbhost /dbname") ... 1.5.2. db_table (string) The name of the DB table containing the load-balancing rules. Default value is “load_balancerâ€. Example 1.2. Set db_table parameter ... modparam("load_balancer", "db_table", "lb") ... 1.5.3. probing_interval (integer) How often (in seconds) the probing of a destination should be done. If set to 0, the probing will be disabled as functionality (for all destinations) Default value is “30â€. Example 1.3. Set probing_interval parameter ... modparam("load_balancer", "probing_interval", 60) ... 1.5.4. probing_method (string) The SIP method to be used for the probing requests. Default value is “"OPTIONS"â€. Example 1.4. Set probing_method parameter ... modparam("load_balancer", "probing_method", "INFO") ... 1.5.5. probing_from (string) The FROM SIP URI to be advertised in the SIP probing requests. Default value is “"sip:prober@localhost"â€. Example 1.5. Set probing_from parameter ... modparam("load_balancer", "probing_from", "sip:pinger@192.168.2.10") ... 1.5.6. probing_reply_codes (string) A comma separted list of SIP reply codes. The codes defined here will be considered as valid reply codes for probing messages, apart for 200. Default value is “NULLâ€. Example 1.6. Set probing_reply_codes parameter ... modparam("load_balancer", "probing_reply_codes", "501, 403") ... 1.5.7. lb_define_blacklist (string) Defines a blacklist based on a lb group. This list will contain the IPs (no port, all protocols) of the destinations matching the given group. Multiple instances of this param are allowed. Default value is “NULLâ€. Example 1.7. Set the lb_define_blacklist parameter ... modparam("load_balancer", "lb_define_blacklist", "list= 1,4,3") modparam("load_balancer", "lb_define_blacklist", "blist2= 2,10,6") ... 1.6. Exported Functions 1.6.1. lb_start(grp,resources[,flags]) The function starts a new load-balancing session over the available destinations. This translates into finding the less loaded destination that can provide the requested resources and belong to a requested group. Meaning of the parameters is as follows: * grp - group id for the destinations; the destination may be grouped in several groups you can you for differnet scenarios; this can be a number or a variable containing a numerical value. * resources - string containing a semi-colon separated list of resources required by the current call. * flags - various flags to controll the LB algorithm ( or computing the available load on the system): + n - Negative availability - use destinations with negative availability (exceeded capacity); do not ignore resources with negative availability, and thus able to select for load balancing destinations with exceeded capacity. This might be needed in scenarios where we want to limit generic calls volume and always pass important/high-priority calls. + r - Relative value - the relative available load (how many percentages are free) is used in computing the load of each pear/resource; Without this flag, the Absolute value is assumed - the effective available load ( maximum_load - current_load) is used in computing the load of each pear/resource. + s - Pick a random destination if multiple destinations with the same load are found, instead of always picking first matched destination. This could help to offload an excessive load from the first destination and distribute load in situations when failed calls always routed to first destination, since they almost does not affect load counters of destinations. Returns true if a new destination URI is set, pointing to the selected destination. NOTE that the RURI will not be changed by this function. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. Example 1.8. lb_start usage ... if (lb_start("1","trascoding;conference")) { # dst URI points to the new destination xlog("sending call to $du\n"); t_relay(); exit; } ... 1.6.2. lb_next() Function to be used to pull the next available (and less loaded) destination. You need to have an ongoing LB session (started with lb_start()). This function is mainly used for implementing failover for the LB destinations. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.9. lb_next() usage ... if (t_check_status("(408)|(5[0-9][0-9])")) { /* check next available LB destination */ if ( lb_next() ) { t_on_failure("1"); xlog("-----------new dst is $du\n"); t_relay(); exit; } } ... 1.6.3. lb_start_or_next(grp,resources[,flags]) This is just a wrapper function to simplify scripting. If there is no ongoing LB session, it acts as lb_start(); If there is an ongoing LB session, it acts as lb_next(). 1.6.4. load_balance(grp,resources[,flags]) Old name of the lb_start_or_next() function. Take care, this will become obsolete. 1.6.5. lb_reset() Function to stop and flush a current LB session. To be used in failure route, if you want to stop the current LB session (not to try any other destinations from this session) and to start a completly new one. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.10. lb_next() usage ... if (t_check_status("(5[0-9][0-9])")) { /* check next available LB destination */ if ( lb_next() ) { t_on_failure("1"); xlog("-----------new dst is $du\n"); t_relay(); exit; } } else if (t_check_status("(408)")) { lb_reset(); if (lb_start("1","conference")) { t_relay(); exit; } } ... 1.6.6. lb_is_started() Function to check if there is any ongoing LB session. Returns true if so. This function can be used in any type of route. 1.6.7. lb_disable_dst() Marks as disabled the last destination that was used for the current call. The disabling done via this function will prevent the destination to be used for usage from now on. The probing mechanism can re-enable this peer (see the probing section in the beginning) This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.11. lb_disable_dst() usage ... if (t_check_status("(408)|(5[0-9][0-9])")) { lb_disable_dst(); if ( lb_next() ) { t_on_failure("1"); xlog("-----------new dst is $du\n"); t_relay(); } else { t_reply("500","Error"); } } ... 1.6.8. lb_is_destination(ip,port[,group[,active]]) Checks if the given IP and PORT belongs to a destination configured in the load-balancer's list. Returns true if found and active (see the "active" parameter). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Meaning of the parameters is as follows: * ip and port - IP and PORT to be checked (any kind of variables are allowed, but take care as the PORT variables should have an integer value); A value 0 for the port means "any" - will match any port. * group - in what LB group the destination should be looked for; If not specified, the search will be in all groups. * active - if "1", the search will be performed only over "active" (not disabled) destinations. If missing, the search will consider any kind of destinations. Example 1.12. lb_is_destination usage ... if (lb_is_destination("$si","$sp") ) { # request from a LB destination } ... 1.6.9. lb_count_call(ip,port,grp,resources[,undo]) The function counts the current call as load for a given destination with some given resources. Note that this call is not going through the load-balancing logic (there are not routing decision taken for the call); it is simply counted by LB as ongoing call for a destination; Meaning of the parameters is as follows: * ip and port - IP and PORT to identify the destination the call has to be counted for. * grp - group id for the destinations; if no knows, "-1" will mean all groups. * resources - string containing a semi-colon separated list of resources required by the current call. * undo - (optional) if set to a non zero value, it will force the function to un-count - actually it will undo the counting of this call as load in the current LB session; this might be needed if we count call for particular resources and then need to un-count it. Function returns true if the call was properly taken into consideration for estimating the load on the destination. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. Example 1.13. lb_count_call usage ... # count as load also the calls orgininated by lb destinations if (lb_is_destination("$si","$sp") ) { # inbound call from destination lb_count_call("$si","$sp","-1","conference"); } else { # outbound call to destinations if ( !load_balance("1","conference") ) { send_reply("503","unavailable"); exit(); } # dst URI points to the new destination xlog("sending call to $du\n"); t_relay(); exit; } ... 1.7. Exported statistics NONE 1.8. Exported MI Functions 1.8.1. lb_reload Trigers the reload of the load balancing data from the DB. MI FIFO Command Format: :lb_reload:_reply_fifo_file_ _empty_line_ 1.8.2. lb_resize Changes the capacity for a resource of a destination. The function receives as parameters the ID (as per DB) of the destination along with the name of the resource you want to resize. MI FIFO Command Format: :lb_resize:_reply_fifo_file_ 11 /*dstination id*/ voicemail /*resource name*/ 56 /* new resource capacity*/ _empty_line_ 1.8.3. lb_list Lists all the destinations and the maximum and current load for each resource of the destination. MI FIFO Command Format: :lb_list:_reply_fifo_file_ _empty_line_ Example 1.14. lb_list usage $ ./opensipsctl fifo lb_list Destination:: sip:127.0.0.1:5100 id=1 enabled=yes auto-re=on Resource:: pstn max=3 load=0 Resource:: transc max=5 load=1 Resource:: vm max=5 load=2 Destination:: sip:127.0.0.1:5200 id=2 enabled=no auto-re=on Resource:: pstn max=6 load=0 Resource:: trans max=57 load=0 Resource:: vm max=5 load=0 1.8.4. lb_status Gets or sets the status (enabled or disabled) of a destination. The function takes 2 parameters, first mandatory, the id of the destiantion and second, optional, the new status. If no new status is given, the function will return the current status. If a new status is given (0 - disable, 1 - enable), this status will be forced for the destination. MI FIFO Command Format: :lb_status:_reply_fifo_file_ id status (optional) _empty_line_ Example 1.15. lb_status usage $ ./opensipsctl fifo lb_status 2 enable:: no $ ./opensipsctl fifo lb_status 2 1 $ ./opensipsctl fifo lb_status 2 enable:: yes 1.9. Exported Events 1.9.1. E_LOAD_BALANCER_STATUS This event is raised when the module changes the state of a destination, either through MI or probing. Parameters: * group - the group of the destination. * uri - the URI of the destination. * status - disabled if the destination was disabled or enabled if the destination is being used. Chapter 2. Developer Guide 2.1. Available Functions NONE Chapter 3. Frequently Asked Questions 3.1. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 3.2. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 3.3. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/load_balancer/doc/000077500000000000000000000000001300170765700203465ustar00rootroot00000000000000opensips-2.2.2/modules/load_balancer/doc/load_balancer.xml000066400000000000000000000021661300170765700236430ustar00rootroot00000000000000 %docentities; ]> Load-Balancer Module &osipsname; Bogdan-Andrei Iancu
bogdan@opensips.org
Bogdan-Andrei Iancu
bogdan@opensips.org
2009 &voicesystem; $Revision: 8740 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/load_balancer/doc/load_balancer_admin.xml000066400000000000000000000514221300170765700250120ustar00rootroot00000000000000 &adminguide;
Overview The Load-Balancer module comes to provide traffic routing based on load. Shortly, when &osips; routes calls to a set of destinations, it is able to keep the load status (as number of ongoing calls) of each destination and to choose to route to the less loaded destination (at that moment). &osips; is aware of the capacity of each destination - it is preconfigured with the maximum load accepted by the destinations. To be more precise, when routing, &osips; will consider the less loaded destination not the destination with the smallest number of ongoing calls, but the destination with the largest available slot. Also the module has the capability to do failover (to try a new destination if the selected one done not responde), to keep state of the destinations (to remember the failed destination and avoid using them agai) and to check the health of the destination (by doing probing of the destination and auto re-enabling).
How it works Please refer to the Load-Balancer tutorial from the &osips; website: http://www.opensips.org/Documentation/Tutorials-LoadBalancing-1-9.
Probing and Disabling destinations The module has the capability to monitor the status of the destinations by doing SIP probing (sending SIP requests like OPTIONS). For each destination, you can configure what kind of probing should be done (probe_mode column): (0) - no probing at all; (1) - probing only when the destination is in disabled mode (disabling via MI command will competely stop the probing also). The destination will be automatically re-enabled when the probing will succeed next time; (2) - probing all the time. If disabled, the destination will be automatically re-enabled when the probing will succeed next time; A destination can become disabled in two ways: script detection - by calling from script the lb_disabled() function after try the destination. In this case, if probing mode for the destination is (1) or (2), the destination will be automatically re-enabled when the probing will succeed. MI command - by calling the lb_status MI command for disabling (on demand) the destination. If so, the probing and re-enabling of this destination will be completly disabled until you re-enable it again via MI command - this is designed to allow controlled and complete disabling of some destination during maintenance.
Dependencies
&osips; Modules The following modules must be loaded before this module: Dialog - Dialog module Dialog - TM module (only if probing enabled) database - one of the DB modules
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>db_url</varname> (string) The URL pointing to the database where the load-balancing rules are stored. Default value is &defaultdb;. Set <varname>db_url</varname> parameter ... modparam("load_balancer", "db_url", "&exampledb;") ...
<varname>db_table</varname> (string) The name of the DB table containing the load-balancing rules. Default value is load_balancer. Set <varname>db_table</varname> parameter ... modparam("load_balancer", "db_table", "lb") ...
<varname>probing_interval</varname> (integer) How often (in seconds) the probing of a destination should be done. If set to 0, the probing will be disabled as functionality (for all destinations) Default value is 30. Set <varname>probing_interval</varname> parameter ... modparam("load_balancer", "probing_interval", 60) ...
<varname>probing_method</varname> (string) The SIP method to be used for the probing requests. Default value is "OPTIONS". Set <varname>probing_method</varname> parameter ... modparam("load_balancer", "probing_method", "INFO") ...
<varname>probing_from</varname> (string) The FROM SIP URI to be advertised in the SIP probing requests. Default value is "sip:prober@localhost". Set <varname>probing_from</varname> parameter ... modparam("load_balancer", "probing_from", "sip:pinger@192.168.2.10") ...
<varname>probing_reply_codes</varname> (string) A comma separted list of SIP reply codes. The codes defined here will be considered as valid reply codes for probing messages, apart for 200. Default value is NULL. Set <varname>probing_reply_codes</varname> parameter ... modparam("load_balancer", "probing_reply_codes", "501, 403") ...
<varname>lb_define_blacklist</varname> (string) Defines a blacklist based on a lb group. This list will contain the IPs (no port, all protocols) of the destinations matching the given group. Multiple instances of this param are allowed. Default value is NULL. Set the <varname>lb_define_blacklist</varname> parameter ... modparam("load_balancer", "lb_define_blacklist", "list= 1,4,3") modparam("load_balancer", "lb_define_blacklist", "blist2= 2,10,6") ...
Exported Functions
<function moreinfo="none">lb_start(grp,resources[,flags])</function> The function starts a new load-balancing session over the available destinations. This translates into finding the less loaded destination that can provide the requested resources and belong to a requested group. Meaning of the parameters is as follows: grp - group id for the destinations; the destination may be grouped in several groups you can you for differnet scenarios; this can be a number or a variable containing a numerical value. resources - string containing a semi-colon separated list of resources required by the current call. flags - various flags to controll the LB algorithm ( or computing the available load on the system): n - Negative availability - use destinations with negative availability (exceeded capacity); do not ignore resources with negative availability, and thus able to select for load balancing destinations with exceeded capacity. This might be needed in scenarios where we want to limit generic calls volume and always pass important/high-priority calls. r - Relative value - the relative available load (how many percentages are free) is used in computing the load of each pear/resource; Without this flag, the Absolute value is assumed - the effective available load ( maximum_load - current_load) is used in computing the load of each pear/resource. s - Pick a random destination if multiple destinations with the same load are found, instead of always picking first matched destination. This could help to offload an excessive load from the first destination and distribute load in situations when failed calls always routed to first destination, since they almost does not affect load counters of destinations. Returns true if a new destination URI is set, pointing to the selected destination. NOTE that the RURI will not be changed by this function. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. <function>lb_start</function> usage ... if (lb_start("1","trascoding;conference")) { # dst URI points to the new destination xlog("sending call to $du\n"); t_relay(); exit; } ...
<function moreinfo="none">lb_next()</function> Function to be used to pull the next available (and less loaded) destination. You need to have an ongoing LB session (started with lb_start()). This function is mainly used for implementing failover for the LB destinations. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. <function>lb_next()</function> usage ... if (t_check_status("(408)|(5[0-9][0-9])")) { /* check next available LB destination */ if ( lb_next() ) { t_on_failure("1"); xlog("-----------new dst is $du\n"); t_relay(); exit; } } ...
<function moreinfo="none">lb_start_or_next(grp,resources[,flags])</function> This is just a wrapper function to simplify scripting. If there is no ongoing LB session, it acts as lb_start(); If there is an ongoing LB session, it acts as lb_next().
<function moreinfo="none">load_balance(grp,resources[,flags])</function> Old name of the lb_start_or_next() function. Take care, this will become obsolete.
<function moreinfo="none">lb_reset()</function> Function to stop and flush a current LB session. To be used in failure route, if you want to stop the current LB session (not to try any other destinations from this session) and to start a completly new one. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. <function>lb_next()</function> usage ... if (t_check_status("(5[0-9][0-9])")) { /* check next available LB destination */ if ( lb_next() ) { t_on_failure("1"); xlog("-----------new dst is $du\n"); t_relay(); exit; } } else if (t_check_status("(408)")) { lb_reset(); if (lb_start("1","conference")) { t_relay(); exit; } } ...
<function moreinfo="none">lb_is_started()</function> Function to check if there is any ongoing LB session. Returns true if so. This function can be used in any type of route.
<function moreinfo="none">lb_disable_dst()</function> Marks as disabled the last destination that was used for the current call. The disabling done via this function will prevent the destination to be used for usage from now on. The probing mechanism can re-enable this peer (see the probing section in the beginning) This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. <function>lb_disable_dst()</function> usage ... if (t_check_status("(408)|(5[0-9][0-9])")) { lb_disable_dst(); if ( lb_next() ) { t_on_failure("1"); xlog("-----------new dst is $du\n"); t_relay(); } else { t_reply("500","Error"); } } ...
<function moreinfo="none">lb_is_destination(ip,port[,group[,active]])</function> Checks if the given IP and PORT belongs to a destination configured in the load-balancer's list. Returns true if found and active (see the "active" parameter). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Meaning of the parameters is as follows: ip and port - IP and PORT to be checked (any kind of variables are allowed, but take care as the PORT variables should have an integer value); A value 0 for the port means "any" - will match any port. group - in what LB group the destination should be looked for; If not specified, the search will be in all groups. active - if "1", the search will be performed only over "active" (not disabled) destinations. If missing, the search will consider any kind of destinations. <function>lb_is_destination</function> usage ... if (lb_is_destination("$si","$sp") ) { # request from a LB destination } ...
<function moreinfo="none">lb_count_call(ip,port,grp,resources[,undo])</function> The function counts the current call as load for a given destination with some given resources. Note that this call is not going through the load-balancing logic (there are not routing decision taken for the call); it is simply counted by LB as ongoing call for a destination; Meaning of the parameters is as follows: ip and port - IP and PORT to identify the destination the call has to be counted for. grp - group id for the destinations; if no knows, "-1" will mean all groups. resources - string containing a semi-colon separated list of resources required by the current call. undo - (optional) if set to a non zero value, it will force the function to un-count - actually it will undo the counting of this call as load in the current LB session; this might be needed if we count call for particular resources and then need to un-count it. Function returns true if the call was properly taken into consideration for estimating the load on the destination. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. <function>lb_count_call</function> usage ... # count as load also the calls orgininated by lb destinations if (lb_is_destination("$si","$sp") ) { # inbound call from destination lb_count_call("$si","$sp","-1","conference"); } else { # outbound call to destinations if ( !load_balance("1","conference") ) { send_reply("503","unavailable"); exit(); } # dst URI points to the new destination xlog("sending call to $du\n"); t_relay(); exit; } ...
Exported statistics NONE
Exported MI Functions
<varname>lb_reload</varname> Trigers the reload of the load balancing data from the DB. MI FIFO Command Format: :lb_reload:_reply_fifo_file_ _empty_line_
<varname>lb_resize</varname> Changes the capacity for a resource of a destination. The function receives as parameters the ID (as per DB) of the destination along with the name of the resource you want to resize. MI FIFO Command Format: :lb_resize:_reply_fifo_file_ 11 /*dstination id*/ voicemail /*resource name*/ 56 /* new resource capacity*/ _empty_line_
<varname>lb_list</varname> Lists all the destinations and the maximum and current load for each resource of the destination. MI FIFO Command Format: :lb_list:_reply_fifo_file_ _empty_line_ <function>lb_list</function> usage $ ./opensipsctl fifo lb_list Destination:: sip:127.0.0.1:5100 id=1 enabled=yes auto-re=on Resource:: pstn max=3 load=0 Resource:: transc max=5 load=1 Resource:: vm max=5 load=2 Destination:: sip:127.0.0.1:5200 id=2 enabled=no auto-re=on Resource:: pstn max=6 load=0 Resource:: trans max=57 load=0 Resource:: vm max=5 load=0
<varname>lb_status</varname> Gets or sets the status (enabled or disabled) of a destination. The function takes 2 parameters, first mandatory, the id of the destiantion and second, optional, the new status. If no new status is given, the function will return the current status. If a new status is given (0 - disable, 1 - enable), this status will be forced for the destination. MI FIFO Command Format: :lb_status:_reply_fifo_file_ id status (optional) _empty_line_ <function>lb_status</function> usage $ ./opensipsctl fifo lb_status 2 enable:: no $ ./opensipsctl fifo lb_status 2 1 $ ./opensipsctl fifo lb_status 2 enable:: yes
Exported Events
<function moreinfo="none">E_LOAD_BALANCER_STATUS</function> This event is raised when the module changes the state of a destination, either through MI or probing. Parameters: group - the group of the destination. uri - the URI of the destination. status - disabled if the destination was disabled or enabled if the destination is being used.
opensips-2.2.2/modules/load_balancer/doc/load_balancer_devel.xml000066400000000000000000000002511300170765700250130ustar00rootroot00000000000000 &develguide;
Available Functions NONE
opensips-2.2.2/modules/load_balancer/doc/load_balancer_faq.xml000066400000000000000000000023301300170765700244630ustar00rootroot00000000000000 &faqguide; Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/load_balancer/lb_bl.c000066400000000000000000000115311300170765700210200ustar00rootroot00000000000000/** * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2012-09-24 created (liviuchircu) * */ #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../ut.h" #include "../../trim.h" #include "../../ip_addr.h" #include "lb_bl.h" static struct lb_bl *lb_blists = NULL; static char **blacklists = NULL; static unsigned int bl_size = 0; int set_lb_bl(modparam_t type, void *val) { blacklists = pkg_realloc( blacklists, (bl_size+1) * sizeof(*blacklists)); if (blacklists == NULL) { bl_size = 0; LM_ERR("REALLOC failed.\n"); return -1; } blacklists[bl_size] = (char*)val; bl_size++; return 0; } int init_lb_bls(void) { unsigned int i; struct lb_bl *lbbl; str name; str val; char *p; LM_DBG("Initialising lb blacklists\n"); if (blacklists == NULL) return 0; for(i = 0; i < bl_size; i++ ) { LM_DBG("Processing bl definition <%s>\n", blacklists[i]); /* get name */ p = strchr( blacklists[i], '='); if (p==NULL || p==blacklists[i]) { LM_ERR("blacklist definition <%s> has no name", blacklists[i]); return -1; } name.s = blacklists[i]; name.len = p - name.s; trim(&name); if (name.len == 0) { LM_ERR("empty name in blacklist definition <%s>\n", blacklists[i]); return -1; } LM_DBG("found list name <%.*s>\n", name.len, name.s); /* alloc structure */ lbbl = shm_malloc(sizeof(*lbbl)); if (lbbl == NULL) { LM_ERR("no more shme memory\n"); return -1; } memset(lbbl, 0, sizeof(*lbbl)); /* fill in the types */ p++; do { if (lbbl->no_groups == LB_BL_MAX_SETS) { LM_ERR("too many types per rule <%s>\n", blacklists[i]); shm_free(lbbl); return -1; } val.s = p; p = strchr( p, ','); if (p == NULL) { val.len = strlen(val.s); } else { val.len = (int)(long)(p - val.s); p++; } trim(&val); if (val.len == 0) { LM_ERR("invalid types listing in <%s>\n", blacklists[i]); shm_free(lbbl); return -1; } LM_DBG("found type <%.*s>\n", val.len, val.s); if (str2int( &val, &lbbl->groups[lbbl->no_groups])!=0) { LM_ERR("nonnumerical type <%.*s>\n", val.len, val.s); shm_free(lbbl); return -1; } lbbl->no_groups++; } while(p != NULL); pkg_free(blacklists[i]); blacklists[i] = NULL; /* create backlist for it */ lbbl->bl = create_bl_head( 131131, 0/*flags*/, NULL, NULL, &name); if (lbbl->bl == NULL) { LM_ERR("CREATE bl <%.*s> failed.\n", name.len, name.s); shm_free(lbbl); return -1; } /* link it */ lbbl->next = lb_blists; lb_blists = lbbl; } pkg_free(blacklists); blacklists = NULL; return 0; } void destroy_lb_bls(void) { struct lb_bl *lbbl; while ((lbbl = lb_blists)) { lb_blists = lb_blists->next; shm_free(lbbl); } } int populate_lb_bls(struct lb_dst *dest_list) { unsigned int i,j; struct lb_bl *lbbl; struct bl_rule *lbbl_first; struct bl_rule *lbbl_last; struct net *group_net; struct lb_dst *dst; LM_DBG("Updating lb blacklists...\n"); /* each bl list at a time */ for(lbbl = lb_blists; lbbl; lbbl = lbbl->next) { lbbl_first = lbbl_last = NULL; /* each group at a time */ for (i = 0; i < lbbl->no_groups; i++) { LM_DBG("Searching for group [%d]\n", lbbl->groups[i]); /* search in the group list all groups of this type */ for(dst = dest_list; dst; dst = dst->next) { LM_DBG("Checking dest group %d\n", dst->group); if (dst->group == lbbl->groups[i]) { LM_DBG("Group [%d] matches. Adding all IPs\n", dst->group); for ( j=0 ; jips_cnt ; j++ ) { group_net = mk_net_bitlen( &dst->ips[j], dst->ips[j].len*8); if (group_net == NULL) { LM_ERR("BUILD netmask failed.\n"); continue; } /* add this destination to the BL */ add_rule_to_list( &lbbl_first, &lbbl_last, group_net, NULL/*body*/, dst->ports[j], dst->protos[j], 0/*flags*/); pkg_free(group_net); } } } } /* the new content for the BL */ if(lbbl->bl && add_list_to_head(lbbl->bl,lbbl_first,lbbl_last,1,0)!=0){ LM_ERR("UPDATE blacklist failed.\n"); return -1; } } return 0; } opensips-2.2.2/modules/load_balancer/lb_bl.h000066400000000000000000000025501300170765700210260ustar00rootroot00000000000000/** * Copyright (C) 2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2012-09-24 created (liviuchircu) * */ #ifndef _LB_BL_H_ #define _LB_BL_H_ #include "../../blacklists.h" #include "lb_data.h" #include "../../parser/parse_uri.h" #include "../../resolve.h" #define LB_BL_MAX_SETS 32 extern struct lb_data **curr_data; struct lb_bl { unsigned int no_groups; unsigned int groups[LB_BL_MAX_SETS]; struct bl_head *bl; struct lb_bl *next; }; int set_lb_bl(modparam_t type, void *val); int init_lb_bls(void); void destroy_lb_bls(void); int populate_lb_bls(struct lb_dst *dst); #endif /* _LB_BL_H_ */ opensips-2.2.2/modules/load_balancer/lb_data.c000066400000000000000000001005621300170765700213370ustar00rootroot00000000000000/* * load balancer module - complex call load balancing * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-02-01 initial version (bogdan) */ #include #include #include "../../proxy.h" #include "../../parser/parse_uri.h" #include "../../mem/shm_mem.h" #include "../../evi/evi.h" #include "lb_parser.h" #include "lb_data.h" #include "lb_db.h" /* dialog stuff */ extern struct dlg_binds lb_dlg_binds; struct lb_data* load_lb_data(void) { struct lb_data *data; data = (struct lb_data*) shm_malloc( sizeof(struct lb_data) ); if (data==NULL) { LM_ERR("failed to allocate shm mem\n"); return NULL; } memset( data, 0, sizeof(struct lb_data)); if (lb_db_load_data(data)!=0) { LM_ERR("failed to load data from DB\n"); free_lb_data(data); return NULL; } return data; } struct lb_resource *get_resource_by_name(struct lb_data *data, str *name) { struct lb_resource *res; for( res=data->resources ; res ; res=res->next ) { if (name->len==res->name.len && memcmp( name->s, res->name.s, name->len)==0) { LM_DBG("found resource [%.*s]\n",name->len,name->s); return res; } } return NULL; } static struct lb_resource *add_lb_resource(struct lb_data *data, str *name) { #define PROFILE_MAX_NAME 256 char buf[PROFILE_MAX_NAME]; struct lb_resource *new_res; struct lb_resource *res; struct lb_resource *p_res; str profile_name; int o; LM_DBG(" new resource name=<%.*s>\n",name->len,name->s); new_res = (struct lb_resource*)shm_malloc ( sizeof(struct lb_resource) + name->len ); if (new_res==NULL) { LM_ERR("failed to allocate shm mem (%ld)\n", (unsigned long)(sizeof(struct lb_resource) + name->len) ); return NULL; } memset( new_res , 0 , sizeof(struct lb_resource)); new_res->name.s = (char*)(new_res+1); new_res->name.len = name->len; memcpy( new_res->name.s, name->s, name->len ); /* create & init lock */ if ( (new_res->lock=lock_alloc())==0) { LM_CRIT("failed to alloc lock\n"); goto error; } if (lock_init(new_res->lock)==0 ) { LM_CRIT("failed to init lock\n"); goto error; } /* create and get new dialog profile */ profile_name.len = snprintf( buf, PROFILE_MAX_NAME-1, "lbX%.*s", name->len,name->s); profile_name.s = buf; /* first check if the profile already exists */ new_res->profile = lb_dlg_binds.search_profile( &profile_name ); if (new_res->profile==NULL) { /* create a new one */ LM_DBG("adding dialog profile <%.*s>\n", profile_name.len,profile_name.s); if (lb_dlg_binds.add_profiles( buf, 1 /*has value*/ )!=0) { LM_ERR("failed to add dialog profile <%s>\n",buf); goto error; } new_res->profile = lb_dlg_binds.search_profile( &profile_name ); if (new_res->profile==NULL) { LM_CRIT("bug - cannot find just added profile\n"); goto error; } } else { LM_DBG("dialog profile <%.*s> found created\n", profile_name.len,profile_name.s); } /* keep the list alphabetical ordered */ p_res = NULL; res = data->resources; while (res) { o = (name->len < res->name.len)? strncmp(name->s, res->name.s, name->len): strncmp(name->s, res->name.s, res->name.len); if (o>0) /* add between p_res and res */ break; p_res = res; res = res->next; } if (p_res==NULL) { /* add at the beginning */ new_res->next = data->resources; data->resources = new_res; LM_DBG("adding <%.*s> as head\n",name->len,name->s); } else if (res==NULL) { /* add at the end */ p_res->next = new_res; LM_DBG("adding <%.*s> after <%.*s>\n", name->len,name->s,p_res->name.len,p_res->name.s); } else { /* add in the middle */ new_res->next = res; p_res->next = new_res; LM_DBG("adding <%.*s> after <%.*s>\n", name->len,name->s,p_res->name.len,p_res->name.s); } data->res_no ++; return new_res; error: if (new_res->lock) { lock_destroy( new_res->lock ); lock_dealloc( new_res->lock ); new_res->lock = 0; } shm_free(new_res); return NULL; } static int lb_set_resource_bitmask(struct lb_resource *res, unsigned int bit) { #define BITMAP_UNIT 4 unsigned int size; if ( bit >= res->bitmap_size*8*sizeof(unsigned int) ) { size = (bit / (8*sizeof(unsigned int)))+1; size = ((size+BITMAP_UNIT-1) / BITMAP_UNIT ) * BITMAP_UNIT; LM_DBG("realloc the bitmap for bit %u - old size=%u; new size=%u\n", bit, res->bitmap_size, size); res->dst_bitmap = (unsigned int*)shm_realloc( res ->dst_bitmap, size*sizeof(unsigned int)); if (res->dst_bitmap==NULL) { LM_ERR("failed to realloc (shm) bitmap\n"); return -1; } /* set to zero the new allocated bitmap part */ memset( res->dst_bitmap+res->bitmap_size, 0, (size-res->bitmap_size)*sizeof(unsigned int) ); res->bitmap_size = size; } /* set the bit */ size = bit / (8*sizeof(unsigned int)); LM_DBG("setting bit %u in unit %u , pos %d\n", bit, size, bit % ((unsigned int)(8*sizeof(unsigned int)))); res->dst_bitmap[size] |= 1<<( bit % (8*sizeof(unsigned int)) ); return 0; } int add_lb_dsturi( struct lb_data *data, int id, int group, char *uri, char* resource, unsigned int flags) { struct lb_res_str_list *lb_rl; struct lb_res_str *r; struct lb_dst *dst; struct lb_resource *res; struct sip_uri puri; struct proxy_l *proxy; union sockaddr_union sau; int len; int i; LM_DBG("uri=<%s>, grp=%d, res=<%s>\n",uri, group, resource); /* check uri */ len = strlen(uri); if(parse_uri(uri, len, &puri)!=0 ) { LM_ERR("bad uri [%.*s] for destination\n", len, uri); return -1; } /* parse the resources string */ lb_rl = parse_resources_list( resource, 1); if (lb_rl==NULL) { LM_ERR("failed to parse resourse string <%s>\n",resource); return -1; } /*add new destination */ dst = (struct lb_dst*)shm_malloc( sizeof(struct lb_dst) + lb_rl->n*sizeof(struct lb_resource_map) + len + (3+2*sizeof(struct lb_dst*))); if (dst==NULL) { LM_ERR("failed to get shmem\n"); goto error; } memset( dst, 0, sizeof(struct lb_dst)+ lb_rl->n*sizeof(struct lb_resource_map) + len + (3+2*sizeof(struct lb_dst*)) ); dst->rmap = (struct lb_resource_map*)(dst+1); dst->uri.s = (char*)(dst->rmap + lb_rl->n); dst->uri.len = len; memcpy( dst->uri.s , uri, len); dst->profile_id.s = dst->uri.s + len; dst->profile_id.len = snprintf(dst->profile_id.s, 2+2*sizeof(struct lb_dst*), "%X", id); dst->id = id; dst->group = group; dst->rmap_no = lb_rl->n; dst->flags = flags; /* add or update resource list */ for( i=0 ; in ; i++) { r = lb_rl->resources + i; LM_DBG(" setting for uri=<%s> (%d) resource=<%.*s>, val=%d\n", uri, data->dst_no+1, r->name.len, r->name.s, r->val); res = get_resource_by_name( data, &r->name); if (res==NULL) { /* add new resource */ res = add_lb_resource(data, &r->name); if (res==NULL) { LM_ERR("failed to create new resource\n"); goto error; } } /* set the proper bit in the resource */ if (lb_set_resource_bitmask( res, data->dst_no)==-1 ) { LM_ERR("failed to set destination bit\n"); goto error; } /* set the pointer and the max load */ dst->rmap[i].resource = res; dst->rmap[i].max_load = r->val; } /* Do a SIP wise DNS-Lookup for the domain part */ proxy = mk_proxy( &puri.host, puri.port_no, puri.proto, (puri.type==SIPS_URI_T)); if (proxy==NULL) { LM_ERR("could not resolve %.*s\n", puri.host.len, puri.host.s); goto error; } hostent2ip_addr( &dst->ips[0], &proxy->host, proxy->addr_idx); dst->ports[0] = proxy->port; dst->protos[0] = proxy->proto; dst->ips_cnt = 1; LM_DBG("first dst ip addr [%s]:%d\n", ip_addr2a(&dst->ips[0]), dst->ports[0]); /* get the next available IPs from DNS */ while (dst->ips_cntips[dst->ips_cnt], &sau); dst->ports[dst->ips_cnt] = proxy->port; dst->protos[dst->ips_cnt] = proxy->proto; LM_DBG("additional dst ip addr [%s]:%d, proto %d\n", ip_addr2a(&dst->ips[dst->ips_cnt]), dst->ports[dst->ips_cnt], dst->protos[dst->ips_cnt] ); /* one more IP found */ dst->ips_cnt++; } /* free al the helper structures */ free_proxy(proxy); pkg_free(proxy); /* link at the end */ if (data->last_dst==NULL) { data->dsts = data->last_dst = dst; } else { data->last_dst->next = dst; data->last_dst = dst; } data->dst_no++; pkg_free(lb_rl); return 0; error: shm_free(dst); pkg_free(lb_rl); return -1; } void free_lb_data(struct lb_data *data) { struct lb_resource *lbr1, *lbr2; struct lb_dst *lbd1, *lbd2; if (data==NULL) return; /* free resources */ for( lbr1=data->resources ; lbr1 ; ) { lbr2 = lbr1; lbr1 = lbr1->next; if (lbr2->dst_bitmap) shm_free(lbr2->dst_bitmap); if (lbr2->lock) { lock_destroy( lbr2->lock ); lock_dealloc( lbr2->lock ); } shm_free(lbr2); } /* free destinations */ for( lbd1=data->dsts ; lbd1 ; ) { lbd2 = lbd1; lbd1 = lbd1->next; shm_free(lbd2); } shm_free(data); return; } static int get_dst_load(struct lb_resource **res, unsigned int res_no, struct lb_dst *dst, unsigned int flags, int *load) { unsigned int k, l; int av; /* iterate through requested resources */ for( k=0 ; krmap_no ; l++ ) if( res[k] == dst->rmap[l].resource ) break; if( l == dst->rmap_no ) { LM_CRIT("bug - cannot find request resource in dst\n"); return 0; } av = 0; if( flags & LB_FLAGS_RELATIVE ) { if( dst->rmap[l].max_load ) av = 100 - (100 * lb_dlg_binds.get_profile_size(res[k]->profile, &dst->profile_id) / dst->rmap[l].max_load); } else { av = dst->rmap[l].max_load - lb_dlg_binds.get_profile_size(res[k]->profile, &dst->profile_id); } if( (k == 0/*first iteration*/) || (av < *load ) ) *load = av; /* we possibly could have negative avaliability for any resource, because we could use LB_FLAGS_NEGATIVE or manually increment resource with lb_count_call() */ } return (k > 0); /* load initialized */ } int lb_route(struct sip_msg *req, int group, struct lb_res_str_list *rl, unsigned int flags, struct lb_data *data, int reuse) { /* resources for previous iteration */ static struct lb_resource **res_prev = NULL; static unsigned int res_prev_size = 0; /* resources for new iteration */ static struct lb_resource **res_new = NULL; static unsigned int res_new_size = 0; /* probed destinations bitmap */ static unsigned int *dst_bitmap = NULL; static unsigned int bitmap_size = 0; /* selected destinations buffer */ static struct lb_dst **dsts = NULL; static unsigned int dsts_size = 0; /* control vars */ struct lb_resource **res_cur; int res_prev_n, res_new_n, res_cur_n; struct lb_dst **dsts_cur; struct lb_dst *last_dst, *dst; unsigned int dsts_size_cur, dsts_size_max; unsigned int *dst_bitmap_cur; unsigned int bitmap_size_cur; struct dlg_cell *dlg; /* AVP related vars */ struct usr_avp *group_avp; struct usr_avp *flags_avp; struct usr_avp *mask_avp; struct usr_avp *id_avp; struct usr_avp *res_avp; int_str group_val; int_str flags_val; int_str mask_val; int_str id_val; int_str res_val; /* iterators, e.t.c. */ struct lb_dst *it_d; struct lb_resource *it_r; int load, it_l; int i, j, cond; /* init control vars state */ res_cur = NULL; res_cur_n = res_prev_n = res_new_n = 0; last_dst = dst = NULL; dst_bitmap_cur = NULL; /* search and fill new resources references if we should not reuse previous iteration data */ if( !reuse ) { res_new_n = rl->n; /* adjust size of statically allocated buffer */ if( res_new_n > res_new_size ) { res_new = (struct lb_resource **)pkg_realloc (res_new, (res_new_n * sizeof(struct lb_resource *))); if( res_new == NULL ) { res_new_size = 0; LM_ERR("no more pkg mem - resources ptr buffer realloc " "failure\n"); return -1; } res_new_size = res_new_n; } /* fill resource references */ for( it_r=data->resources,i=0 ; it_r ; it_r=it_r->next ) { if( search_resource_str(rl, &it_r->name) ) { res_new[i++] = it_r; LM_DBG("initial call of LB - found requested %d/%d " "resource [%.*s]\n", i, res_new_n, it_r->name.len, it_r->name.s); } } if( i != res_new_n ) { LM_ERR("initial call of LB - unknown resource found in " "input string\n"); return -1; } /* set 'res_new' as current iteration buffer */ res_cur = res_new; res_cur_n = res_new_n; } /* always search for previous iteration data, no matter if we will reuse it or not */ group_avp = search_first_avp(0, group_avp_name, &group_val, NULL); flags_avp = search_first_avp(0, flags_avp_name, &flags_val, NULL); mask_avp = search_first_avp(0, mask_avp_name, &mask_val, NULL); id_avp = search_first_avp(0, id_avp_name, &id_val, NULL); /* sanity checks for fetched AVPs */ if( group_avp && !(is_avp_str_val(group_avp) == 0) ) { destroy_avp(group_avp); group_avp = NULL; } if( flags_avp && !(is_avp_str_val(flags_avp) == 0) ) { destroy_avp(flags_avp); flags_avp = NULL; } if( mask_avp && !(is_avp_str_val(mask_avp) != 0) ) { destroy_avp(mask_avp); mask_avp = NULL; } if( id_avp && !(is_avp_str_val(id_avp) == 0) ) { destroy_avp(id_avp); id_avp = NULL; } /* get previous iteration destination, if any */ if( id_avp ) { for( it_d=data->dsts ; it_d ; it_d=it_d->next ) { if( it_d->id == id_val.n ) { last_dst = it_d; LM_DBG("%s call of LB - found previous dst %d [%.*s]\n", (reuse ? "sequential" : "initial"), last_dst->id, last_dst->profile_id.len, last_dst->profile_id.s); break; } } } /* search and fill previous iteration resources references only if... */ if( /* we should reuse previous resources list */ reuse || /* we have 'last_dst', i.e. previous iteration was successfull and * we need to clean it up */ (last_dst != NULL) ) { do { cond = 0; /* use it here as a 'start loop again' flag */ res_prev_n = 0; res_avp = search_first_avp(0, res_avp_name, &res_val, NULL); for( ; res_avp ; res_avp=search_next_avp(res_avp, &res_val) ) { /* ignore AVPs with invalid type */ if( !(is_avp_str_val(res_avp) != 0) ) continue; if ( (it_r=get_resource_by_name( data, &res_val.s))==NULL ) { LM_WARN("%s call of LB - ignore unknown previous " "resource [%.*s]\n", (reuse?"sequential":"initial"), res_val.s.len, res_val.s.s); continue; } /* fill buffer only if buffer size not exeeded */ if( res_prev_n < res_prev_size ) { res_prev[res_prev_n] = it_r; LM_DBG("%s call of LB - found previous resource [%.*s]\n", (reuse ? "sequential" : "initial"), it_r->name.len, it_r->name.s); } res_prev_n++; } /* adjust size of statically allocated buffer */ if( res_prev_n > res_prev_size ) { /* small hack: if we need to adjust 'res_prev' buffer adjust * it according to 'res_new' size to minimize * future pkg_realloc()'s */ if( !reuse && (res_prev_n < res_new_n) ) res_prev_n = res_new_n; res_prev = (struct lb_resource **)pkg_realloc (res_prev, (res_prev_n * sizeof(struct lb_resource *))); if( res_prev == NULL ) { res_prev_size = 0; LM_ERR("no more pkg mem - previous resources ptr " "buffer realloc failure\n"); return -1; } res_prev_size = res_prev_n; cond = 1; } } while( cond ); } /* reuse previous iteration resources, group and flags */ if( reuse ) { /* set 'res_prev' as current iteration buffer */ res_cur = res_prev; res_cur_n = res_prev_n; if( res_cur_n == 0 ) { LM_ERR("sequential call of LB - cannot find previous resources\n"); return -1; } if( group_avp ) group = group_val.n; else { LM_ERR("sequential call of LB - cannot find previous group\n"); return -1; } if( flags_avp ) flags = flags_val.n; else flags = LB_FLAGS_DEFAULT; LM_DBG("sequential call of LB - found previous group %d and " "flags 0x%x\n", group, flags); } /* sanity check - double check that we have a resource list to work with */ if( (res_cur == NULL) || (res_cur_n == 0) ) { LM_ERR("%s call of LB - no resources list to work with\n", (reuse ? "sequential" : "initial")); return -1; } /* [re-]initialize/reuse destinations mask */ /* sanity check - always calculate current iteration * res_cur[]->bitmap_size */ bitmap_size_cur=(unsigned int)(-1); for( i=0 ; i res_cur[i]->bitmap_size ) bitmap_size_cur = res_cur[i]->bitmap_size; } /* always try to reuse 'mask' buffer from AVP, even if we need * to reinitialize it to avoid un-neded AVP ops */ if(mask_avp && (mask_val.s.len==(bitmap_size_cur*sizeof(unsigned int)))) { dst_bitmap_cur = (unsigned int *)mask_val.s.s; } /* ...or use our static buffer */ if( dst_bitmap_cur == NULL ) { /* adjust size of statically allocated buffer */ if( bitmap_size_cur > bitmap_size ) { dst_bitmap = (unsigned int *)pkg_realloc (dst_bitmap, (bitmap_size_cur * sizeof(unsigned int))); if( dst_bitmap == NULL ) { bitmap_size = 0; LM_ERR("no more pkg mem - dst bitmap buffer realloc failed\n"); return -1; } bitmap_size = bitmap_size_cur; } dst_bitmap_cur = dst_bitmap; } /* reinitalize buffer if... */ if( (dst_bitmap_cur == dst_bitmap) || /* it is our static buffer */ !reuse /* should not reuse previous iteration data */ ) { if( reuse ) { LM_WARN("sequential call of LB - cannot %s previous mask, routing " "will be re-started", (mask_avp ? "reuse" : "find")); } memset(dst_bitmap_cur, 0xff, (bitmap_size_cur * sizeof(unsigned int))); for( i=0 ; idst_bitmap[j]; } } /* init selected destinations buff */ dsts_cur = NULL; dsts_size_max = (flags & LB_FLAGS_RANDOM) ? data->dst_no : 1; if( dsts_size_max > 1 ) { if( dsts_size_max > dsts_size ) { dsts = (struct lb_dst **)pkg_realloc (dsts, (dsts_size_max * sizeof(struct lb_dst *))); if( dsts == NULL ) { dsts_size_max = dsts_size = 0; LM_WARN("no more pkg mem - dsts buffer realloc failed\n"); } else dsts_size = dsts_size_max; } dsts_cur = dsts; } if( dsts_cur == NULL ) { /* fallback to no-buffer / 'select first' scenario */ dsts_cur = &dst; dsts_size_max = 1; } /* be sure the dialog is created */ if ( (dlg=lb_dlg_binds.get_dlg())==NULL ) { if( lb_dlg_binds.create_dlg(req, 0) != 1 ) { LM_ERR("%s call of LB - failed to create dialog\n", (reuse ? "sequential" : "initial")); return -1; } /* get the dialog reference */ dlg = lb_dlg_binds.get_dlg(); } /* we're initialized from here and no errors could abort us */ /* remove the dialog from previous profiles, if any */ if ( (last_dst != NULL) && (res_prev_n > 0) ) { for( i=0 ; iprofile_id, res_prev[i]->profile) != 1 ) LM_ERR("%s call of LB - failed to remove from profile [%.*s]" "->[%.*s]\n", (reuse ? "sequential" : "initial"), res_prev[i]->profile->name.len, res_prev[i]->profile->name.s, last_dst->profile_id.len, last_dst->profile_id.s ); } } /* lock resources */ for( i=0 ; ilock); /* do the load-balancing */ /* select destinations */ cond = 0; /* use it here as a 'first iteration' flag */ load = it_l = 0; dsts_size_cur = 0; for( it_d=data->dsts,i=0,j=0 ; it_d ; it_d=it_d->next ) { if( it_d->group == group ) { if( (dst_bitmap_cur[i] & (1 << j)) && ((it_d->flags & LB_DST_STAT_DSBL_FLAG) == 0) ) { /* valid destination (group & resources & status) */ if( get_dst_load(res_cur, res_cur_n, it_d, flags, &it_l) ) { /* only valid load here */ if( (it_l > 0) || (flags & LB_FLAGS_NEGATIVE) ) { /* only allowed load here */ if( !cond/*first pass*/ || (it_l > load)/*new max*/ ) { cond = 1; /* restart buffer */ dsts_size_cur = 0; } else if( it_l < load ) { /* lower availability -> new iteration */ continue; } /* add destination to to selected destinations buffer, * if we have a room for it */ if( dsts_size_cur < dsts_size_max ) { load = it_l; dsts_cur[dsts_size_cur++] = it_d; LM_DBG("%s call of LB - destination %d <%.*s> " "selected for LB set with free=%d\n", (reuse ? "sequential" : "initial"), it_d->id, it_d->uri.len, it_d->uri.s, it_l ); } } } else { LM_WARN("%s call of LB - skipping destination %d <%.*s> - " "unable to calculate free resources\n", (reuse ? "sequential" : "initial"), it_d->id, it_d->uri.len, it_d->uri.s ); } } else { LM_DBG("%s call of LB - skipping destination %d <%.*s> " "(filtered=%d , disabled=%d)\n", (reuse ? "sequential" : "initial"), it_d->id, it_d->uri.len, it_d->uri.s, ((dst_bitmap_cur[i] & (1 << j)) ? 0 : 1), ((it_d->flags & LB_DST_STAT_DSBL_FLAG) ? 1 : 0) ); } } if( ++j == (8 * sizeof(unsigned int)) ) { i++; j=0; } } /* choose one destination among selected */ if( dsts_size_cur > 0 ) { if( (dsts_size_cur > 1) && (flags & LB_FLAGS_RANDOM) ) { dst = dsts_cur[rand() % dsts_size_cur]; } else { dst = dsts_cur[0]; } } if( dst != NULL ) { LM_DBG("%s call of LB - winning destination %d <%.*s> selected " "for LB set with free=%d\n", (reuse ? "sequential" : "initial"), dst->id, dst->uri.len, dst->uri.s, load ); /* add to the profiles */ for( i=0 ; iprofile_id, res_cur[i]->profile, 0) != 0 ) LM_ERR("%s call of LB - failed to add to profile [%.*s]->" "[%.*s]\n", (reuse ? "sequential" : "initial"), res_cur[i]->profile->name.len, res_cur[i]->profile->name.s, dst->profile_id.len, dst->profile_id.s); } /* set dst as used (not selected) */ for( it_d=data->dsts,i=0,j=0 ; it_d ; it_d=it_d->next ) { if( it_d == dst ) { dst_bitmap_cur[i] &= ~(1 << j); break; } if( ++j == (8 * sizeof(unsigned int)) ) { i++; j=0; } } } else { LM_DBG("%s call of LB - no destination found\n", (reuse ? "sequential" : "initial")); } /* unlock resources */ for( i=0 ; ilock); /* we're done with load-balancing, now save state */ /* save state - group */ if( group_avp == NULL ) { group_val.n = group; if( add_avp(0, group_avp_name, group_val) != 0 ) { LM_ERR("failed to add GROUP AVP\n"); } } else if( group_val.n != group ) { group_avp->data = (void *)(long)group; } /* save state - flags, save only if they are set */ if( flags_avp == NULL ) { if( flags != LB_FLAGS_DEFAULT ) { flags_val.n = flags; if( add_avp(0, flags_avp_name, flags_val) != 0 ) { LM_ERR("failed to add FLAGS AVP\n"); } } } else if( flags_val.n != flags ) { flags_avp->data = (void *)(long)flags; } /* save state - dst_bitmap mask */ if( (mask_avp!=NULL) && (dst_bitmap_cur!=(unsigned int *)mask_val.s.s) ) { destroy_avp(mask_avp); mask_avp = NULL; } if( mask_avp == NULL ) { mask_val.s.s = (char *)dst_bitmap_cur; mask_val.s.len = bitmap_size_cur * sizeof(unsigned int); if( add_avp(AVP_VAL_STR, mask_avp_name, mask_val) != 0 ) { LM_ERR("failed to add MASK AVP\n"); } } /* save state - dst, save only if we have one */ if( id_avp == NULL ) { if( dst != NULL ) { id_val.n = dst->id; if( add_avp(0, id_avp_name, id_val) != 0 ) { LM_ERR("failed to add ID AVP\n"); } } } else { if( dst != NULL ) { id_avp->data = (void *)(long)dst->id; } else { destroy_avp(id_avp); id_avp = NULL; } } /* save state - res */ /* iterate AVPs once and delete old resources */ destroy_avps(0, res_avp_name, 1 /*all*/); /* add new resources */ for( i=0 ; iname; if( add_avp(AVP_VAL_STR, res_avp_name, res_val) != 0 ) LM_ERR("failed to add RES AVP\n"); } /* outcome: set dst uri */ if( (dst != NULL) && (set_dst_uri(req, &dst->uri) != 0) ) { LM_ERR("failed to set duri\n"); return -2; } return dst ? 0 : -2; } int do_lb_start(struct sip_msg *req, int group, struct lb_res_str_list *rl, unsigned int flags, struct lb_data *data) { return lb_route(req, group, rl, flags, data, 0/*no data reusage*/); } int do_lb_next(struct sip_msg *req, struct lb_data *data) { return lb_route(req, -1, NULL, 0, data, 1/*reuse previous data*/); } int do_lb_reset(struct sip_msg *req, struct lb_data *data) { struct usr_avp *id_avp; struct usr_avp *res_avp, *del_res_avp; int_str id_val; int_str res_val; struct dlg_cell *dlg; struct lb_dst *it_d, *last_dst; struct lb_resource *it_r; if ( (dlg=lb_dlg_binds.get_dlg())==NULL ) { LM_ERR("no dialog found for this call, LB not started\n"); return -1; } /* remove any saved AVPs */ destroy_avps(0, group_avp_name, 0); destroy_avps(0, flags_avp_name, 0); destroy_avps(0, mask_avp_name, 0); /* get previous iteration destination, if any */ last_dst = NULL; id_avp = search_first_avp(0, id_avp_name, &id_val, NULL); if( id_avp && (is_avp_str_val(id_avp) == 0) ) { for( it_d=data->dsts ; it_d ; it_d=it_d->next ) { if( it_d->id == id_val.n ) { last_dst = it_d; LM_DBG("reset LB - found previous dst %d [%.*s]\n", last_dst->id, last_dst->profile_id.len, last_dst->profile_id.s); break; } } } destroy_avps(0, id_avp_name, 0); /* any valid previous iteration ? */ if(last_dst == NULL) { /* simply delete all possible resources */ destroy_avps(0, res_avp_name, 1); } else { /* search and clean up previous iteration resources, if any */ res_avp = search_first_avp(0, res_avp_name, &res_val, NULL); while (res_avp) { if ( (it_r=get_resource_by_name( data, &res_val.s))!=NULL ) { if( lb_dlg_binds.unset_profile(dlg, &last_dst->profile_id, it_r->profile) != 1 ) LM_ERR("reset LB - failed to remove from profile [%.*s]->" "[%.*s]\n", res_val.s.len, res_val.s.s, last_dst->profile_id.len, last_dst->profile_id.s ); } else { LM_WARN("reset LB - ignore unknown previous resource " "[%.*s]\n", res_val.s.len, res_val.s.s); } del_res_avp = res_avp; res_avp = search_next_avp(del_res_avp, &res_val); destroy_avp(del_res_avp); } } return 0; } int do_lb_is_started(struct sip_msg *req) { struct usr_avp *group_avp; struct usr_avp *mask_avp; struct usr_avp *res_avp; return ( ((group_avp=search_first_avp(0, group_avp_name, NULL, NULL))!=NULL) && (is_avp_str_val(group_avp) == 0) && ((mask_avp =search_first_avp(0, mask_avp_name, NULL, NULL))!=NULL) && (is_avp_str_val(mask_avp) != 0) && ((res_avp =search_first_avp(0, res_avp_name, NULL, NULL))!=NULL) && (is_avp_str_val(res_avp) != 0) ) ? 1 : -1; } int do_lb_disable_dst(struct sip_msg *req, struct lb_data *data, unsigned int verbose) { struct usr_avp *id_avp; int_str id_val; struct lb_dst *dst; int old_flags; id_avp = search_first_avp( 0, id_avp_name, &id_val, NULL); if( id_avp && (is_avp_str_val(id_avp) == 0) ) { for( dst=data->dsts ; dst ; dst=dst->next ) { if( dst->id == id_val.n ) { old_flags = dst->flags; dst->flags |= LB_DST_STAT_DSBL_FLAG; if( dst->flags != old_flags ) { lb_raise_event(dst); if( verbose ) LM_INFO("manually disable destination %d <%.*s> " "from script\n",dst->id, dst->uri.len, dst->uri.s); } return 0; } } } else LM_DBG("no AVP ID -> nothing to disable\n"); return -1; } /* Checks, if the IP PORT is a LB destination */ int lb_is_dst(struct lb_data *data, struct sip_msg *_m, pv_spec_t *pv_ip, gparam_t *pv_port, int group, int active) { pv_value_t val; struct ip_addr *ip; int port; struct lb_dst *dst; int k; /* get the address to test */ if (pv_get_spec_value( _m, pv_ip, &val)!=0) { LM_ERR("failed to get IP value from PV\n"); return -1; } if ( (val.flags&PV_VAL_STR)==0 ) { LM_ERR("IP PV val is not string\n"); return -1; } if ( (ip=str2ip( &val.rs ))==NULL ) { LM_ERR("IP val is not IP <%.*s>\n",val.rs.len,val.rs.s); return -1; } /* get the port to test */ if (pv_port) { if (fixup_get_ivalue(_m, (gparam_p)pv_port, &port) != 0) { LM_ERR("failed to get PORT value from PV\n"); return -1; } } else { port = 0; } /* and now search !*/ for( dst=data->dsts ; dst ; dst=dst->next) { if ( ((group==-1) || (dst->group==group)) && /*group matches*/ ( !active || (active && (dst->flags&LB_DST_STAT_DSBL_FLAG)==0 ) ) ) { /* check the IPs */ for(k=0 ; kips_cnt ; k++ ) { if ( (dst->ports[k]==0 || port==0 || port==dst->ports[k]) && ip_addr_cmp( ip, &dst->ips[k]) ) { /* found */ return 1; } } } } return -1; } int lb_count_call(struct lb_data *data, struct sip_msg *req,struct ip_addr *ip, int port, int group, struct lb_res_str_list *rl, int dir) { static struct lb_resource **call_res = NULL; static unsigned int call_res_no = 0; struct dlg_cell *dlg; struct lb_resource *res; struct lb_dst *dst; int i,k; /* search for the destination we need to count for */ for( dst=data->dsts ; dst ; dst=dst->next) { if ( (group==-1) || (dst->group==group) ) { /* check the IPs */ for(k=0 ; kips_cnt ; k++ ) { if ( (dst->ports[k]==0 || port==0 || port==dst->ports[k]) && ip_addr_cmp( ip, &dst->ips[k]) ) { /* found */ goto end_search; } } } } end_search: if (dst==NULL) { LM_ERR("no destination to match the given IP and port (%s:%d)\n", ip_addr2a(ip), port); return -1; } /* get references to the resources */ if (rl->n>call_res_no) { call_res = (struct lb_resource**)pkg_realloc (call_res, rl->n*sizeof(struct lb_resorce*)); if (call_res==NULL) { call_res_no = 0; LM_ERR("no more pkg mem - res ptr realloc\n"); return -1; } call_res_no = rl->n; } for( i=0,res=data->resources ; (in)&&res ; res=res->next) { if (search_resource_str( rl, &res->name)) { call_res[i++] = res; LM_DBG("found requested (%d) resource %.*s\n", i-1, res->name.len,res->name.s); } } if (i!=rl->n) { LM_ERR("unknown resource in input string\n"); return -1; } /* create dialog */ if (lb_dlg_binds.create_dlg( req , 0)!=1 ) { LM_ERR("failed to create dialog\n"); return -1; } if ( (dlg=lb_dlg_binds.get_dlg())==NULL ) { LM_CRIT("BUG - no dialog found at this stage :(\n"); return -1; } /* lock the resources */ for( i=0 ; in ; i++) lock_get( call_res[i]->lock ); /* add to the profiles */ for( i=0 ; in ; i++) { if( !dir ) { if (lb_dlg_binds.set_profile( dlg, &dst->profile_id, call_res[i]->profile, 0)!=0) LM_ERR("failed to add to profile\n"); } else { if (lb_dlg_binds.unset_profile( dlg, &dst->profile_id, call_res[i]->profile)!=1) LM_ERR("failed to remove from profile\n"); } } /* unlock the resources*/ for( i=0 ; in ; i++) lock_release( call_res[i]->lock ); return 0; } /* events */ static event_id_t lb_evi_id; static str lb_event = str_init("E_LOAD_BALANCER_STATUS"); static str lb_group_str = str_init("group"); static str lb_uri_str = str_init("uri"); static str lb_state_str = str_init("status"); static str lb_disabled_str = str_init("disabled"); static str lb_enabled_str = str_init("enabled"); int lb_init_event(void) { lb_evi_id = evi_publish_event(lb_event); if (lb_evi_id == EVI_ERROR) { LM_ERR("cannot register %.*s event\n", lb_event.len, lb_event.s); return -1; } return 0; } void lb_raise_event(struct lb_dst *dst) { evi_params_p list = NULL; if (lb_evi_id == EVI_ERROR || !evi_probe_event(lb_evi_id)) return; list = evi_get_params(); if (!list) { LM_ERR("cannot create event params\n"); return; } if (evi_param_add_int(list, &lb_group_str, &dst->group) < 0) { LM_ERR("cannot add destination group\n"); goto error; } if (evi_param_add_str(list, &lb_uri_str, &dst->uri) < 0) { LM_ERR("cannot add destination uri\n"); goto error; } if (evi_param_add_str(list, &lb_state_str, dst->flags&LB_DST_STAT_DSBL_FLAG ? &lb_disabled_str : &lb_enabled_str) < 0) { LM_ERR("cannot add destination state\n"); goto error; } if (evi_raise_event(lb_evi_id, list)) { LM_ERR("unable to send %.*s event\n", lb_event.len, lb_event.s); } return; error: evi_free_params(list); } opensips-2.2.2/modules/load_balancer/lb_data.h000066400000000000000000000067711300170765700213530ustar00rootroot00000000000000/* * load balancer module - complex call load balancing * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-02-01 initial version (bogdan) */ #ifndef LB_LB_DATA_H_ #define LB_LB_DATA_H_ #include "../../mod_fix.h" #include "../../str.h" #include "../../locking.h" #include "../../parser/msg_parser.h" #include "../dialog/dlg_load.h" #include "lb_parser.h" #define LB_FLAGS_RELATIVE (1<<0) /* do relative versus absolute estimation. default is absolute */ #define LB_FLAGS_NEGATIVE (1<<1) /* do not skip negative loads. default to skip */ #define LB_FLAGS_RANDOM (1<<2) /* pick a random destination among all selected dsts with equal load */ #define LB_FLAGS_DEFAULT 0 #define LB_DST_PING_DSBL_FLAG (1<<0) #define LB_DST_PING_PERM_FLAG (1<<1) #define LB_DST_STAT_DSBL_FLAG (1<<2) #define LB_DST_STAT_NOEN_FLAG (1<<3) /* max number of IPs for a destination (DNS loookup) */ #define LB_MAX_IPS 32 struct lb_resource { str name; gen_lock_t *lock; struct dlg_profile_table *profile; unsigned int bitmap_size; unsigned int *dst_bitmap; struct lb_resource *next; }; struct lb_resource_map { struct lb_resource *resource; unsigned int max_load; }; struct lb_dst { unsigned int group; unsigned int id; str uri; str profile_id; unsigned int rmap_no; unsigned int flags; struct lb_resource_map *rmap; struct ip_addr ips[LB_MAX_IPS]; /* IP-Address of the entry */ unsigned short int ports[LB_MAX_IPS]; /* Port of the request URI */ unsigned short int protos[LB_MAX_IPS]; /* Protocol of the request URI */ unsigned short ips_cnt; struct lb_dst *next; }; struct lb_data { unsigned int res_no; struct lb_resource * resources; unsigned int dst_no; struct lb_dst *dsts; struct lb_dst *last_dst; }; struct lb_data* load_lb_data(void); int add_lb_dsturi( struct lb_data *data, int id, int group, char *uri, char* resource, unsigned int flags); void free_lb_data(struct lb_data *data); int do_lb_start(struct sip_msg *req, int group, struct lb_res_str_list *rl, unsigned int flags, struct lb_data *data); int do_lb_next(struct sip_msg *req, struct lb_data *data); int do_lb_reset(struct sip_msg *req, struct lb_data *data); int do_lb_is_started(struct sip_msg *req); int do_lb_disable_dst(struct sip_msg *req, struct lb_data *data, unsigned int verbose); int lb_is_dst(struct lb_data *data, struct sip_msg *_m, pv_spec_t *pv_ip, gparam_t *pv_port, int group, int active); int lb_count_call(struct lb_data *data, struct sip_msg *req, struct ip_addr *ip, int port, int group, struct lb_res_str_list *rl, int dir); int lb_init_event(void); void lb_raise_event(struct lb_dst *dst); /* failover stuff */ extern int group_avp_name; extern int flags_avp_name; extern int mask_avp_name; extern int id_avp_name; extern int res_avp_name; #endif opensips-2.2.2/modules/load_balancer/lb_db.c000066400000000000000000000120131300170765700210040ustar00rootroot00000000000000/* * load balancer module - complex call load balancing * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-02-01 initial version (bogdan) */ #include "../../db/db.h" #include "lb_db.h" #define LB_TABLE_VERSION 2 str lb_id_column = str_init(LB_ID_COL); str lb_grpid_column = str_init(LB_GRP_ID_COL); str lb_dsturi_column = str_init(LB_DST_URI_COL); str lb_resource_column = str_init(LB_RESOURCES_COL); str lb_pmode_column = str_init(LB_PMODE_COL); str lb_table_name = str_init(LB_TABLE_NAME); static db_con_t* lb_db_handle = 0; /* database connection handle */ static db_func_t lb_dbf; #define check_val( _val, _type, _not_null, _is_empty_str) \ do{\ if ((_val)->type!=_type) { \ LM_ERR("bad colum type\n");\ goto error;\ } \ if (_not_null && (_val)->nul) { \ LM_ERR("nul column\n");\ goto error;\ } \ if (_is_empty_str && VAL_STRING(_val)==0) { \ LM_ERR("empty str column\n");\ goto error;\ } \ }while(0) int lb_connect_db(const str *db_url) { if (lb_db_handle) { LM_CRIT("BUG - db connection found already open\n"); return -1; } if ((lb_db_handle = lb_dbf.init(db_url)) == 0) return -1; return 0; } void lb_close_db(void) { if (lb_db_handle==NULL) return; lb_dbf.close(lb_db_handle); lb_db_handle = NULL; } int init_lb_db(const str *db_url, char *table) { /* Find a database module */ if (db_bind_mod(db_url, &lb_dbf) < 0){ LM_ERR("Unable to bind to a database driver\n"); return -1; } if (lb_connect_db(db_url)!=0){ LM_ERR("unable to connect to the database\n"); return -1; } if (table) { lb_table_name.s = table; lb_table_name.len = strlen(table); } if(db_check_table_version(&lb_dbf, lb_db_handle, &lb_table_name, LB_TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); return -1; } return 0; } int lb_db_load_data( struct lb_data *data) { db_key_t columns[5]; db_res_t* res = NULL; db_row_t* row; int i, n; char *resource, *uri; int id, group, pmode; unsigned int flags; int no_rows = 10; lb_dbf.use_table( lb_db_handle, &lb_table_name); columns[0] = &lb_id_column; columns[1] = &lb_grpid_column; columns[2] = &lb_dsturi_column; columns[3] = &lb_resource_column; columns[4] = &lb_pmode_column; if (0/*DB_CAPABILITY(lb_dbf, DB_CAP_FETCH))*/) { if ( lb_dbf.query( lb_db_handle, 0, 0, 0, columns, 0, 5, 0, 0 ) < 0) { LM_ERR("DB query failed\n"); return -1; } no_rows = estimate_available_rows( 4+4+64+256+8, 5/*cols*/); if (no_rows==0) no_rows = 10; if(lb_dbf.fetch_result( lb_db_handle, &res, no_rows)<0) { LM_ERR("Error fetching rows\n"); return -1; } } else { if ( lb_dbf.query( lb_db_handle, 0, 0, 0, columns, 0, 5, 0, &res)<0) { LM_ERR("DB query failed\n"); return -1; } } if (res == NULL || RES_ROW_N(res) == 0) { LM_WARN("table \"%.*s\" empty\n", lb_table_name.len,lb_table_name.s ); return 0; } LM_DBG("%d records found in %.*s\n", RES_ROW_N(res), lb_table_name.len,lb_table_name.s ); n = 0; do { for(i=0; i < RES_ROW_N(res); i++) { row = RES_ROWS(res) + i; flags = 0; /* ID column */ check_val( ROW_VALUES(row), DB_INT, 1, 0); id = VAL_INT(ROW_VALUES(row)); /* GRP_ID column */ check_val( ROW_VALUES(row)+1, DB_INT, 1, 0); group = VAL_INT(ROW_VALUES(row)+1); /* DST_URI column */ check_val( ROW_VALUES(row)+2, DB_STRING, 1, 1); uri = (char*)VAL_STRING(ROW_VALUES(row)+2); /* RESOURCES column */ check_val( ROW_VALUES(row)+3, DB_STRING, 1, 1); resource = (char*)VAL_STRING(ROW_VALUES(row)+3); /* PROBING_MODE column */ check_val( ROW_VALUES(row)+4, DB_INT, 1, 0); pmode = VAL_INT(ROW_VALUES(row)+4); if (pmode==0) { flags |= LB_DST_PING_DSBL_FLAG; } else if (pmode>=2) { flags |= LB_DST_PING_PERM_FLAG; } /* add the destinaton definition in */ if ( add_lb_dsturi( data, id, group, uri, resource, flags)<0 ) { LM_ERR("failed to add destination %d -> skipping\n",n); continue; } n++; } if (DB_CAPABILITY( lb_dbf, DB_CAP_FETCH)) { if(lb_dbf.fetch_result(lb_db_handle, &res, no_rows)<0) { LM_ERR( "fetching rows (1)\n"); return -1; } } else { break; } } while(RES_ROW_N(res)>0); lb_dbf.free_result(lb_db_handle, res); res = 0; return 0; error: if (res) lb_dbf.free_result(lb_db_handle, res); return -1; } opensips-2.2.2/modules/load_balancer/lb_db.h000066400000000000000000000026011300170765700210130ustar00rootroot00000000000000/* * load balancer module - complex call load balancing * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-02-01 initial version (bogdan) */ #ifndef LB_LB_DB_H_ #define LB_LB_DB_H_ #define LB_TABLE_NAME "load_balancer" #define LB_ID_COL "id" #define LB_GRP_ID_COL "group_id" #define LB_DST_URI_COL "dst_uri" #define LB_RESOURCES_COL "resources" #define LB_PMODE_COL "probe_mode" #include "../../str.h" #include "lb_data.h" int init_lb_db(const str *db_url, char *table); int lb_connect_db(const str *db_url); void lb_close_db(void); int lb_db_load_data( struct lb_data *data); #endif opensips-2.2.2/modules/load_balancer/lb_parser.c000066400000000000000000000075201300170765700217220ustar00rootroot00000000000000/* * load balancer module - complex call load balancing * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-02-01 initial version (bogdan) */ #include #include "../../dprint.h" #include "../../ut.h" #include "../../mem/mem.h" #include "lb_parser.h" struct lb_res_str* search_resource_str( struct lb_res_str_list *lb_rl, str*name) { unsigned int i; for( i=0 ; in ; i++) { if (name->len==lb_rl->resources[i].name.len && memcmp(name->s, lb_rl->resources[i].name.s, name->len)==0 ) return &lb_rl->resources[i]; } return NULL; } struct lb_res_str_list *parse_resources_list(char *r_list, int has_val) { struct lb_res_str_list *lb_rl; unsigned int n; unsigned int len; char *p; char *s; char *end; str name; str val; /* validate and count */ n = 0; len = 0; p = r_list; do { /* eat spaces */ for( ; *p && isspace(*p) ; p++); if (!*p) break; /* name and value */ end = strchr(p,';'); if (end) *end = 0; name.s = p; p = strchr(p,'='); if (end) *end = ';'; if (p) { if (!has_val) { LM_ERR("resource must not has value!\n"); goto error; } } else { if (has_val) { LM_ERR("resource must has value!\n"); goto error; } p = end?end:(r_list+strlen(r_list)); } for(; (p-1)!=name.s && isspace(*(p-1)) ; p-- ); if (p==name.s) { LM_ERR("empty resource name around %d\n",(unsigned int)(p-r_list)); goto error; } name.len = p-name.s; /* mark */ n++; len += name.len; /* next */ p = end+1; } while(end && *p); if (n==0) { LM_ERR("empty list of resorces\n"); goto error; } LM_DBG("discovered %d resources\n",n); /* allocate stuff*/ lb_rl = (struct lb_res_str_list *)pkg_malloc (sizeof(struct lb_res_str_list) + n*sizeof(struct lb_res_str) + len); if (lb_rl==NULL) { LM_ERR("no more pkg memory\n"); goto error; } /* init the strucuture */ lb_rl->n = n; lb_rl->resources =(struct lb_res_str*)(lb_rl+1); s = (char*)(lb_rl->resources + n); /* fill in the structures*/ p = r_list; n = 0; do { /* eat spaces */ for( ; *p && isspace(*p) ; p++); if (!*p) break; /* name .... */ end = strchr(p,';'); if (end) *end = 0; name.s = p; val.s = 0; p = strchr(p,'='); if (end) *end = ';'; if (!p) { p = end?end:(r_list+strlen(r_list)); } else { val.s = p+1; } for(; (p-1)!=name.s && isspace(*(p-1)) ; p-- ); name.len = p-name.s; lb_rl->resources[n].name.len = name.len; lb_rl->resources[n].name.s = s; memcpy( s, name.s, name.len ); s += name.len; /* ....and value */ if (has_val) { /* eat spaces */ for( ; *val.s && isspace(*val.s) ; val.s++); if (!*val.s) { LM_ERR("empty val !\n"); goto error1; } val.len = ( end?end:(r_list+strlen(r_list)) ) - val.s; for( ; isspace(val.s[val.len-1]) ; val.len--); if (str2int( &val , &lb_rl->resources[n].val)!=0) { LM_ERR("invalid value [%.*s]\n",val.len,val.s); goto error1; } } else { lb_rl->resources[n].val = 0; } /* next */ n++; p = end+1; } while(end && *p); return lb_rl; error1: pkg_free(lb_rl); error: return NULL; } opensips-2.2.2/modules/load_balancer/lb_parser.h000066400000000000000000000025771300170765700217360ustar00rootroot00000000000000/* * load balancer module - complex call load balancing * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-02-01 initial version (bogdan) */ #ifndef _LB_LB_PARSER_H_ #define _LB_LB_PARSER_H_ #include "../../str.h" struct lb_res_str { str name; unsigned int val; }; struct lb_res_str_list { int n; struct lb_res_str* resources; }; #define RES_TEXT (1<<0) #define RES_ELEM (1<<1) struct lb_res_parse { char type; void *param; }; struct lb_res_str* search_resource_str( struct lb_res_str_list *lb_rl, str *name); struct lb_res_str_list *parse_resources_list(char *r_list, int has_val); #endif opensips-2.2.2/modules/load_balancer/lb_prober.c000066400000000000000000000040451300170765700217160ustar00rootroot00000000000000/* * load balancer module - complex call load balancing * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-07-05 initial version (bogdan) */ #include "../../dprint.h" #include "../tm/tm_load.h" #include "lb_prober.h" extern str lb_probe_method; extern str lb_probe_from; extern struct tm_binds lb_tmb; void set_dst_state_from_rplcode( int id, int code); static void lb_probing_callback( struct cell *t, int type, struct tmcb_params *ps ) { int id; if (!*ps->param) { LM_CRIT("BUG - reply to a LB probe with no ID (code=%d)\n", ps->code); return; } id = (int)(long)(*ps->param); set_dst_state_from_rplcode( id, ps->code); return; } void lb_do_probing(struct lb_data *data) { struct lb_dst *dst; /* go through all destinations */ for( dst = data->dsts ; dst ; dst=dst->next ) { /* dst requires probing ? */ if ( dst->flags&LB_DST_STAT_NOEN_FLAG || !( (dst->flags&LB_DST_PING_PERM_FLAG) || /*permanent probing*/ ( !(dst->flags&LB_DST_PING_DSBL_FLAG) && dst->flags&LB_DST_STAT_DSBL_FLAG /*probing on disable*/ ) ) ) continue; if (lb_tmb.t_request( &lb_probe_method, &dst->uri, &dst->uri, &lb_probe_from, NULL, NULL, NULL, lb_probing_callback, (void*)(long)dst->id, NULL) < 0) { LM_ERR("probing failed\n"); } } } opensips-2.2.2/modules/load_balancer/lb_prober.h000066400000000000000000000020371300170765700217220ustar00rootroot00000000000000/* * load balancer module - complex call load balancing * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-07-05 initial version (bogdan) */ #include "lb_data.h" #ifndef _LB_PROBING_H #define _LB_PROBING_H void lb_do_probing(struct lb_data *data); #endif opensips-2.2.2/modules/load_balancer/load_balancer.c000066400000000000000000000666641300170765700225350ustar00rootroot00000000000000/* * load balancer module - complex call load balancing * * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-02-01 initial version (bogdan) */ #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../timer.h" #include "../../ut.h" #include "../../mod_fix.h" #include "../../rw_locking.h" #include "../../usr_avp.h" #include "../dialog/dlg_load.h" #include "../tm/tm_load.h" #include "lb_parser.h" #include "lb_db.h" #include "lb_data.h" #include "lb_prober.h" #include "lb_bl.h" /* db stuff */ static str db_url = {NULL, 0}; static char *table_name = NULL; /* dialog stuff */ struct dlg_binds lb_dlg_binds; /* reader-writers lock for data reloading */ static rw_lock_t *ref_lock = NULL; struct lb_data **curr_data = NULL; /* probing related stuff */ static unsigned int lb_prob_interval = 30; static unsigned int lb_prob_verbose = 0; static str lb_probe_replies = {NULL,0}; struct tm_binds lb_tmb; str lb_probe_method = str_init("OPTIONS"); str lb_probe_from = str_init("sip:prober@localhost"); static int* probing_reply_codes = NULL; static int probing_codes_no = 0; static int mod_init(void); static int child_init(int rank); static void mod_destroy(void); static int mi_child_init(); /* failover stuff */ static str group_avp_name_s = str_init("lb_grp"); static str flags_avp_name_s = str_init("lb_flg"); static str mask_avp_name_s = str_init("lb_mask"); static str id_avp_name_s = str_init("lb_id"); static str res_avp_name_s = str_init("lb_res"); int group_avp_name; int flags_avp_name; int mask_avp_name; int id_avp_name; int res_avp_name; static struct mi_root* mi_lb_reload(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_lb_resize(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_lb_list(struct mi_root *cmd_tree, void *param); static struct mi_root* mi_lb_status(struct mi_root *cmd_tree, void *param); static int fixup_resources(void** param, int param_no); static int fixup_is_dst(void** param, int param_no); static int fixup_cnt_call(void** param, int param_no); static int w_lb_start(struct sip_msg *req, char *grp, char *rl, char *fl); static int w_lb_next(struct sip_msg *req); static int w_lb_start_or_next(struct sip_msg *req,char *grp,char *rl,char *fl); static int w_lb_reset(struct sip_msg *req); static int w_lb_is_started(struct sip_msg *req); static int w_lb_disable_dst(struct sip_msg *req); static int w_lb_is_dst2(struct sip_msg *msg, char *ip, char *port); static int w_lb_is_dst3(struct sip_msg *msg, char *ip, char *port, char *grp); static int w_lb_is_dst4(struct sip_msg *msg, char *ip, char *port, char *grp, char *active); static int w_lb_count_call(struct sip_msg *req, char *ip, char *port, char *grp, char *rl, char *dir); static void lb_prob_handler(unsigned int ticks, void* param); static cmd_export_t cmds[]={ {"lb_start", (cmd_function)w_lb_start, 2, fixup_resources, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"lb_start", (cmd_function)w_lb_start, 3, fixup_resources, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"load_balance", (cmd_function)w_lb_start_or_next, 2, fixup_resources, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"load_balance", (cmd_function)w_lb_start_or_next, 3, fixup_resources, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"lb_start_or_next",(cmd_function)w_lb_start_or_next, 2, fixup_resources, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"lb_start_or_next",(cmd_function)w_lb_start_or_next, 3, fixup_resources, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"lb_next", (cmd_function)w_lb_next, 0, 0, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"lb_reset", (cmd_function)w_lb_reset, 0, 0, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"lb_is_started", (cmd_function)w_lb_is_started, 0, 0, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"lb_disable_dst", (cmd_function)w_lb_disable_dst, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"lb_is_destination",(cmd_function)w_lb_is_dst2, 2, fixup_is_dst, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"lb_is_destination",(cmd_function)w_lb_is_dst3, 3, fixup_is_dst, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"lb_is_destination",(cmd_function)w_lb_is_dst4, 4, fixup_is_dst, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"lb_count_call", (cmd_function)w_lb_count_call, 4, fixup_cnt_call, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"lb_count_call", (cmd_function)w_lb_count_call, 5, fixup_cnt_call, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {0,0,0,0,0,0} }; static param_export_t mod_params[]={ { "db_url", STR_PARAM, &db_url.s }, { "db_table", STR_PARAM, &table_name }, { "probing_interval", INT_PARAM, &lb_prob_interval }, { "probing_verbose", INT_PARAM, &lb_prob_verbose }, { "probing_method", STR_PARAM, &lb_probe_method.s }, { "probing_from", STR_PARAM, &lb_probe_from.s }, { "probing_reply_codes", STR_PARAM, &lb_probe_replies.s }, { "lb_define_blacklist", STR_PARAM|USE_FUNC_PARAM, (void*)set_lb_bl}, { 0,0,0 } }; static mi_export_t mi_cmds[] = { { "lb_reload", 0, mi_lb_reload, MI_NO_INPUT_FLAG, 0, mi_child_init}, { "lb_resize", 0, mi_lb_resize, 0, 0, 0}, { "lb_list", 0, mi_lb_list, MI_NO_INPUT_FLAG, 0, 0}, { "lb_status", 0, mi_lb_status, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static module_dependency_t *get_deps_probing_interval(param_export_t *param) { if (*(int *)param->param_pointer <= 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "tm", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "dialog", DEP_ABORT }, { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "probing_interval", get_deps_probing_interval }, { NULL, NULL }, }, }; struct module_exports exports= { "load_balancer", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ mod_params, /* param exports */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, child_init /* per-child init function */ }; struct lb_grp_param { int grp_no; pv_spec_t *grp_pv; }; static int fixup_resources(void** param, int param_no) { struct lb_res_str_list *lb_rl; struct lb_grp_param *lbgp; struct lb_res_parse *lbp; pv_elem_t *model=NULL; str s; if (param_no==1) { lbgp = (struct lb_grp_param *)pkg_malloc(sizeof(struct lb_grp_param)); if (lbgp==NULL) { LM_ERR("no more pkg mem\n"); return E_OUT_OF_MEM; } /* try first as number */ s.s = (char*)*param; s.len = strlen(s.s); if (str2int(&s, (unsigned int*)&lbgp->grp_no)==0) { lbgp->grp_pv = NULL; pkg_free(*param); } else { lbgp->grp_pv = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); if (lbgp->grp_pv==NULL) { LM_ERR("no pkg memory left\n"); return E_OUT_OF_MEM; } if (pv_parse_spec(&s, lbgp->grp_pv)==0 || lbgp->grp_pv->type==PVT_NULL) { LM_ERR("%s is not integer nor PV !\n", (char*)*param); return E_UNSPEC; } } *param=(void *)(unsigned long)lbgp; return 0; } else if (param_no==2) { /* parameter is string (semi-colon separated list) * of needed resources */ lbp = (struct lb_res_parse *)pkg_malloc(sizeof(struct lb_res_parse)); if (!lbp) { LM_ERR("no more pkg mem\n"); return E_OUT_OF_MEM; } s.s = (char*)*param; s.len = strlen(s.s); if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] in resource list!\n", s.s); return E_CFG; } /* check if there is any pv in string */ if (!model->spec.getf && !model->next) lbp->type = RES_TEXT; else lbp->type = RES_ELEM; if (lbp->type & RES_TEXT) { lb_rl = parse_resources_list( (char *)(*param), 0); if (lb_rl==NULL) { LM_ERR("invalid parameter %s\n",(char *)(*param)); return E_CFG; } pkg_free(*param); lbp->param = (void*)(unsigned long)lb_rl; } else { lbp->param = (void*)(unsigned long)model; } *param = (void *)(unsigned long)lbp; return 0; } else if (param_no==3) { /* string with flags */ return fixup_sgp(param); } LM_CRIT("error - wrong params count (%d)\n",param_no); return -1; } static int fixup_is_dst(void** param, int param_no) { if (param_no==1) { /* the ip to test */ return fixup_pvar(param); } else if (param_no==2) { /* the port to test */ if (*param==NULL) { return 0; } else if ( *((char*)*param)==0 ) { pkg_free(*param); *param = NULL; return 0; } return fixup_igp(param); } else if (param_no==3) { /* the group to check in */ return fixup_igp(param); } else if (param_no==4) { /* active only check ? */ return fixup_uint(param); } else { LM_CRIT("bug - too many params (%d) in lb_is_dst()\n",param_no); return -1; } } static int fixup_cnt_call(void** param, int param_no) { if (param_no==1) /* IP */ return fixup_is_dst(param, 1); if (param_no==2) /* port */ return fixup_is_dst(param, 2); if (param_no==3) /* group id */ return fixup_resources(param, 1); if (param_no==4) /* resources */ return fixup_resources(param, 2); if (param_no==5) /* count or un-count */ return fixup_uint(param); LM_CRIT("error - wrong params count (%d)\n",param_no); return -1; } static inline int lb_reload_data( void ) { struct lb_data *new_data; struct lb_data *old_data; new_data = load_lb_data(); if ( new_data==0 ) { LM_CRIT("failed to load load-balancing info\n"); return -1; } lock_start_write( ref_lock ); /* no more activ readers -> do the swapping */ old_data = *curr_data; *curr_data = new_data; lock_stop_write( ref_lock ); /* destroy old data */ if (old_data) free_lb_data( old_data ); /* generate new blacklist from the routing info */ populate_lb_bls((*curr_data)->dsts); return 0; } static int mod_init(void) { LM_INFO("Load-Balancer module - initializing\n"); init_db_url( db_url , 0 /*cannot be null*/); /* Load dialog API */ if (load_dlg_api(&lb_dlg_binds) != 0) { LM_ERR("Can't load dialog hooks\n"); return -1; } /* data pointer in shm */ curr_data = (struct lb_data**)shm_malloc( sizeof(struct lb_data*) ); if (curr_data==0) { LM_CRIT("failed to get shm mem for data ptr\n"); return -1; } *curr_data = 0; /* create & init lock */ if ((ref_lock = lock_init_rw()) == NULL) { LM_CRIT("failed to init lock\n"); return -1; } if (init_lb_bls()) { LM_ERR("BL INIT failed\n"); return -1; } /* init and open DB connection */ if (init_lb_db(&db_url, table_name)!=0) { LM_ERR("failed to initialize the DB support\n"); return -1; } /* load data */ if ( lb_reload_data()!=0 ) { LM_CRIT("failed to load load-balancing data\n"); return -1; } /* close DB connection */ lb_close_db(); /* arm a function for probing */ if (lb_prob_interval) { /* load TM API */ if (load_tm_api(&lb_tmb)!=0) { LM_ERR("can't load TM API\n"); return -1; } /* probing method */ lb_probe_method.len = strlen(lb_probe_method.s); lb_probe_from.len = strlen(lb_probe_from.s); if (lb_probe_replies.s) lb_probe_replies.len = strlen(lb_probe_replies.s); /* register pinger function */ if (register_timer( "lb-pinger", lb_prob_handler , NULL, lb_prob_interval, TIMER_FLAG_DELAY_ON_DELAY)<0) { LM_ERR("failed to register probing handler\n"); return -1; } if (lb_probe_replies.s) { lb_probe_replies.len = strlen(lb_probe_replies.s); if(parse_reply_codes( &lb_probe_replies, &probing_reply_codes, &probing_codes_no )< 0) { LM_ERR("Bad format for options_reply_code parameter" " - Need a code list separated by commas\n"); return -1; } } } /* parse avps */ if (parse_avp_spec(&group_avp_name_s, &group_avp_name)) { LM_ERR("cannot parse group avp\n"); return -1; } if (parse_avp_spec(&flags_avp_name_s, &flags_avp_name)) { LM_ERR("cannot parse flags avp\n"); return -1; } if (parse_avp_spec(&mask_avp_name_s, &mask_avp_name)) { LM_ERR("cannot parse mask avp\n"); return -1; } if (parse_avp_spec(&id_avp_name_s, &id_avp_name)) { LM_ERR("cannot parse id avp\n"); return -1; } if (parse_avp_spec(&res_avp_name_s, &res_avp_name)) { LM_ERR("cannot parse resources avp\n"); return -1; } if (lb_init_event() < 0) { LM_ERR("cannot init event\n"); return -1; } return 0; } static int child_init(int rank) { return 0; } static int mi_child_init( void ) { /* init DB connection */ if ( lb_connect_db(&db_url)!=0 ) { LM_CRIT("cannot initialize database connection\n"); return -1; } return 0; } static void mod_destroy(void) { /* destroy data */ if ( curr_data) { if (*curr_data) free_lb_data( *curr_data ); shm_free( curr_data ); curr_data = 0; } /* destroy lock */ if (ref_lock) { lock_destroy_rw( ref_lock ); ref_lock = 0; } /* destroy blacklist structures */ destroy_lb_bls(); } static int w_lb_next(struct sip_msg *req) { int ret; lock_start_read(ref_lock); /* do lb */ ret = do_lb_next(req, *curr_data); lock_stop_read(ref_lock); if( ret < 0 ) return ret; return 1; } static int w_lb_start(struct sip_msg *req, char *grp, char *rl, char *fl) { int ret; int grp_no; struct lb_grp_param *lbgp = (struct lb_grp_param *)grp; pv_value_t val; struct lb_res_str_list *lb_rl; struct lb_res_parse *lbp; pv_elem_t *model; str dest; str flstr = {0,0}; int flags=LB_FLAGS_DEFAULT; char *f; if (lbgp->grp_pv) { if (pv_get_spec_value( req, (pv_spec_p)lbgp->grp_pv, &val)!=0) { LM_ERR("failed to get PV value\n"); return -1; } if ( (val.flags&PV_VAL_INT)==0 ) { LM_ERR("PV vals is not integer\n"); return -1; } grp_no = val.ri; } else { grp_no = lbgp->grp_no; } lbp = (struct lb_res_parse *)rl; if (lbp->type & RES_ELEM) { model = (pv_elem_p)lbp->param; if (pv_printf_s(req, model, &dest) || dest.len <= 0) { LM_ERR("cannot create resource string\n"); return -1; } lb_rl = parse_resources_list(dest.s, 0); if (!lb_rl) { LM_ERR("cannot create resource list\n"); return -1; } } else lb_rl = (struct lb_res_str_list *)lbp->param; if( fl ) { if( fixup_get_svalue(req, (gparam_p)fl, &flstr) != 0 ) { LM_ERR("failed to extract flags\n"); return -1; } for( f=flstr.s ; ftype & RES_ELEM) pkg_free(lb_rl); if (ret<0) return ret; return 1; } static int w_lb_start_or_next(struct sip_msg *req,char *grp,char *rl,char *fl) { return (do_lb_is_started(req) > 0) ? w_lb_next(req) : w_lb_start(req, grp, rl, fl) ; } static int w_lb_reset(struct sip_msg *req) { int ret; lock_start_read(ref_lock); /* do lb */ ret = do_lb_reset(req, *curr_data); lock_stop_read(ref_lock); if( ret < 0 ) return ret; return 1; } static int w_lb_is_started(struct sip_msg *req) { int ret; /* do lb, do not need a lock, since do not use '*curr_data' */ ret = do_lb_is_started(req); if( ret < 0 ) return ret; return 1; } static int w_lb_disable_dst(struct sip_msg *req) { int ret; lock_start_read(ref_lock); /* do lb */ ret = do_lb_disable_dst(req, *curr_data, lb_prob_verbose); lock_stop_read(ref_lock); if( ret < 0 ) return ret; return 1; } static int w_lb_is_dst2(struct sip_msg *msg, char *ip, char *port) { int ret; lock_start_read( ref_lock ); ret = lb_is_dst(*curr_data, msg, (pv_spec_t*)ip, (gparam_t*)port, -1, 0); lock_stop_read( ref_lock ); if (ret<0) return ret; return 1; } static int w_lb_is_dst3(struct sip_msg *msg,char *ip,char *port,char *grp) { return w_lb_is_dst4(msg, ip, port, grp, 0); } static int w_lb_is_dst4(struct sip_msg *msg,char *ip,char *port,char *grp, char *active) { int ret, group; if (fixup_get_ivalue(msg, (gparam_p)grp, &group) != 0) { LM_ERR("Invalid lb group pseudo variable!\n"); return -1; } lock_start_read( ref_lock ); ret = lb_is_dst(*curr_data, msg, (pv_spec_t*)ip, (gparam_t*)port, group, (int)(long)active); lock_stop_read( ref_lock ); if (ret<0) return ret; return 1; } static int w_lb_count_call(struct sip_msg *req, char *ip, char *port, char *grp, char *rl, char *dir) { struct lb_grp_param *lbgp = (struct lb_grp_param *)grp; struct lb_res_str_list *lb_rl; struct lb_res_parse *lbp; struct ip_addr *ipa; pv_value_t val; pv_elem_t *model; int grp_no; int port_no; str dest; int ret; /* get the ip address */ if (pv_get_spec_value( req, (pv_spec_t*)ip, &val)!=0) { LM_ERR("failed to get IP value from PV\n"); return -1; } if ( (val.flags&PV_VAL_STR)==0 ) { LM_ERR("IP PV val is not string\n"); return -1; } if ( (ipa=str2ip( &val.rs ))==NULL ) { LM_ERR("IP val is not IP <%.*s>\n",val.rs.len,val.rs.s); return -1; } /* get the port */ if (port) { if (fixup_get_ivalue( req, (gparam_p)port, &port_no)!=0) { LM_ERR("failed to get PORT value from PV\n"); return -1; } } else { port_no = 0; } /* get the group */ if (lbgp->grp_pv) { if (pv_get_spec_value( req, (pv_spec_p)lbgp->grp_pv, &val)!=0) { LM_ERR("failed to get PV value\n"); return -1; } if ( (val.flags&PV_VAL_INT)==0 ) { LM_ERR("PV vals is not integer\n"); return -1; } grp_no = val.ri; } else { grp_no = lbgp->grp_no; } /* get the resources list */ lbp = (struct lb_res_parse *)rl; if (lbp->type & RES_ELEM) { model = (pv_elem_p)lbp->param; if (pv_printf_s(req, model, &dest) || dest.len <= 0) { LM_ERR("cannot create resource string\n"); return -1; } lb_rl = parse_resources_list(dest.s, 0); if (!lb_rl) { LM_ERR("cannot create resource list\n"); return -1; } } else lb_rl = (struct lb_res_str_list *)lbp->param; lock_start_read( ref_lock ); ret = lb_count_call( *curr_data, req, ipa, port_no, grp_no, lb_rl, (unsigned int)(long)dir); lock_stop_read( ref_lock ); if (lbp->type & RES_ELEM) pkg_free(lb_rl); if (ret<0) return ret; return 1; } /******************** PROBING Stuff ***********************/ static int check_options_rplcode(int code) { int i; for (i =0; i< probing_codes_no; i++) { if(probing_reply_codes[i] == code) return 1; } return 0; } void set_dst_state_from_rplcode( int id, int code) { struct lb_dst *dst; int old_flags; lock_start_read( ref_lock ); for( dst=(*curr_data)->dsts ; dst && dst->id!=id ; dst=dst->next); if (dst==NULL) { lock_stop_read( ref_lock ); return; } if ((code == 200) || check_options_rplcode(code)) { /* re-enable to DST (if allowed) */ if ( dst->flags&LB_DST_STAT_NOEN_FLAG ) { lock_stop_read( ref_lock ); return; } old_flags = dst->flags; dst->flags &= ~LB_DST_STAT_DSBL_FLAG; if (dst->flags != old_flags) { lb_raise_event(dst); if (lb_prob_verbose) LM_INFO("re-enable destination %d <%.*s> after %d reply " "on probe\n", dst->id, dst->uri.len, dst->uri.s, code); } lock_stop_read( ref_lock ); return; } if (code>=400) { old_flags = dst->flags; dst->flags |= LB_DST_STAT_DSBL_FLAG; if (dst->flags != old_flags) { lb_raise_event(dst); if (lb_prob_verbose) LM_INFO("disable destination %d <%.*s> after %d reply " "on probe\n", dst->id, dst->uri.len, dst->uri.s, code); } } lock_stop_read( ref_lock ); } static void lb_prob_handler(unsigned int ticks, void* param) { lock_start_read( ref_lock ); /* do probing */ lb_do_probing(*curr_data); lock_stop_read( ref_lock ); } /******************** MI commands ***********************/ static struct mi_root* mi_lb_reload(struct mi_root *cmd_tree, void *param) { LM_INFO("\"lb_reload\" MI command received!\n"); if ( lb_reload_data()!=0 ) { LM_CRIT("failed to load load balancing data\n"); goto error; } return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: return init_mi_tree( 500, "Failed to reload",16); } /*! \brief * Expects 3 nodes: * destination ID (number) * resource name (string) * size (number) */ static struct mi_root* mi_lb_resize(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct lb_dst *dst; struct mi_node *node; unsigned int id, size; str *name; int n; for( n=0,node = cmd->node.kids; n<3 && node ; n++,node=node->next ); if (n!=3 || node!=0) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); node = cmd->node.kids; /* id (param 1) */ if (str2int( &node->value, &id) < 0) goto bad_syntax; /* resource (param 2) */ node = node->next; name = &node->value; /* id (param 3) */ node = node->next; if (str2int( &node->value, &size) < 0) goto bad_syntax; lock_start_read( ref_lock ); /* get destination */ for( dst=(*curr_data)->dsts ; dst && dst->id!=id ; dst=dst->next); if (dst==NULL) { rpl_tree = init_mi_tree( 404, MI_SSTR("Destination ID not found")); } else { /* get resource */ for( n=0 ; nrmap_no ; n++) if (dst->rmap[n].resource->name.len == name->len && memcmp( dst->rmap[n].resource->name.s, name->s, name->len)==0) break; if (n==dst->rmap_no) { rpl_tree = init_mi_tree( 404, MI_SSTR("Destination has no such resource")); } else { dst->rmap[n].max_load = size; rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK_S)); } } lock_stop_read( ref_lock ); return rpl_tree; bad_syntax: return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM_S)); } /*! \brief * Expects 2 nodes: * destination ID (number) * status (number) */ static struct mi_root* mi_lb_status(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct lb_dst *dst; struct mi_node *node; unsigned int id, stat; unsigned int old_flags; node = cmd->node.kids; if (node==NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* id (param 1) */ if (str2int( &node->value, &id) < 0) return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM_S)); lock_start_read( ref_lock ); /* status (param 2) */ node = node->next; if (node == NULL) { /* return the status -> find the destination */ for(dst=(*curr_data)->dsts; dst && dst->id!=id ;dst=dst->next); if (dst==NULL) { rpl_tree = init_mi_tree( 404, MI_SSTR("Destination ID not found")); } else { rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree!=NULL) { if (dst->flags&LB_DST_STAT_DSBL_FLAG) { node = add_mi_node_child( &rpl_tree->node, 0, "enable", 6, "no", 2); } else { node = add_mi_node_child( &rpl_tree->node, 0, "enable", 6, "yes", 3); } if (node==NULL) {free_mi_tree(rpl_tree); rpl_tree=NULL;} } } } else { /* set the status */ if (node->next) { rpl_tree = init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); } else if (str2int( &node->value, &stat) < 0) { rpl_tree = init_mi_tree( 400, MI_SSTR(MI_BAD_PARM_S)); } else { /* find the destination */ for( dst=(*curr_data)->dsts ; dst && dst->id!=id ; dst=dst->next); if (dst==NULL) { rpl_tree = init_mi_tree( 404, MI_SSTR("Destination ID not found")); } else { /* set the disable/enable */ old_flags = dst->flags; if (stat) { dst->flags &= ~ (LB_DST_STAT_DSBL_FLAG|LB_DST_STAT_NOEN_FLAG); } else { dst->flags |= LB_DST_STAT_DSBL_FLAG|LB_DST_STAT_NOEN_FLAG; } if (old_flags != dst->flags) { lb_raise_event(dst); if( lb_prob_verbose ) LM_INFO("manually %s destination %d <%.*s>\n", (stat ? "enable" : "disable"), dst->id, dst->uri.len, dst->uri.s ); } lock_stop_read( ref_lock ); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } } } lock_stop_read( ref_lock ); return rpl_tree; } static struct mi_root* mi_lb_list(struct mi_root *cmd_tree, void *param) { struct mi_root *rpl_tree; struct mi_node *dst_node; struct mi_node *node, *node1; struct mi_attr *attr; struct lb_dst *dst; char *p; int len; int i; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; rpl_tree->node.flags |= MI_IS_ARRAY; lock_start_read( ref_lock ); /* go through all destination */ for( dst=(*curr_data)->dsts ; dst ; dst=dst->next) { /* add a destination node */ dst_node = add_mi_node_child( &rpl_tree->node, 0, "Destination", 11, dst->uri.s, dst->uri.len); if (dst_node==0) goto error; /* add some attributes to the destination node */ p= int2str((unsigned long)dst->id, &len); attr = add_mi_attr( dst_node, MI_DUP_VALUE, "id", 2, p, len); if (attr==0) goto error; p= int2str((unsigned long)dst->group, &len); attr = add_mi_attr( dst_node, MI_DUP_VALUE, "group", 5, p, len); if (attr==0) goto error; if (dst->flags&LB_DST_STAT_DSBL_FLAG) { attr = add_mi_attr( dst_node, 0, "enabled", 7, "no", 2); } else { attr = add_mi_attr( dst_node, 0, "enabled", 7, "yes", 3); } if (attr==0) goto error; if (dst->flags&LB_DST_STAT_NOEN_FLAG) { attr = add_mi_attr( dst_node, 0, "auto-reenable", 13, "off", 3); } else { attr = add_mi_attr( dst_node, 0, "auto-reenable", 13, "on", 2); } if (attr==0) goto error; node = add_mi_node_child( dst_node, MI_IS_ARRAY, "Resources", 9, NULL, 0); if (node==0) goto error; /* go through all resources */ for( i=0 ; irmap_no ; i++) { /* add a resource node */ node1 = add_mi_node_child( node, 0, "Resource", 8, dst->rmap[i].resource->name.s,dst->rmap[i].resource->name.len); if (node1==0) goto error; /* add some attributes to the destination node */ p= int2str((unsigned long)dst->rmap[i].max_load, &len); attr = add_mi_attr( node1, MI_DUP_VALUE, "max", 3, p, len); if (attr==0) goto error; p= int2str((unsigned long)lb_dlg_binds.get_profile_size (dst->rmap[i].resource->profile, &dst->profile_id), &len); attr = add_mi_attr( node1, MI_DUP_VALUE, "load", 4, p, len); if (attr==0) goto error; } } lock_stop_read( ref_lock ); return rpl_tree; error: lock_stop_read( ref_lock ); free_mi_tree(rpl_tree); return 0; } opensips-2.2.2/modules/lua/000077500000000000000000000000001300170765700156145ustar00rootroot00000000000000opensips-2.2.2/modules/lua/Makefile000066400000000000000000000013531300170765700172560ustar00rootroot00000000000000# $Id: Makefile,v 1.12 2009/01/20 03:00:05 folays Exp $ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=lua.so MYSQLCFG=$(shell which mysql_config) LIB_LUA = $(shell pkg-config --libs lua5.1 2> /dev/null) $(shell pkg-config --libs lua-5.1 2> /dev/null) $(shell pkg-config --libs lua 2>/dev/null) ifeq (,$(findstring -l,$(LIB_LUA))) LIB_LUA = -llua endif DEFS += $(shell $(MYSQLCFG) --include | sed 's/\(-I.*\)\/mysql/\1/g' ) -I/usr/include/lua5.1/ -I$(LOCALBASE)/include/memcached LIBS += $(shell $(MYSQLCFG) --libs) -L/usr/lib/ -L/usr/local/lib/ -lmemcached $(LIB_LUA) include ../../Makefile.modules install_module_custom: echo "installing Lua OpenSIPS packages ..." opensips-2.2.2/modules/lua/README000066400000000000000000000153311300170765700164770ustar00rootroot00000000000000lua Module Arnaud Chong Edited by Eric Gouyer Eric Gouyer Edited by Arnaud Chong Copyright © 2006-2011 __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Installing the module 1.3. Using the module 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. luafilename (string) 1.5.2. lua_auto_reload (int) 1.5.3. warn_missing_free_fixup (int) 1.5.4. lua_allocator (string) 1.5.5. lua_user_debug (int) 1.6. Exported Functions 1.6.1. lua_exec(func, [param]) 1.6.2. lua_meminfo() 2. OpenSIPS Lua API 2.1. Available functions 2.1.1. xdbg(message) 2.1.2. xlog([level],message) 2.1.3. setUserDebug 2.1.4. WarnMissingFreeFixup 2.1.5. getpid 2.1.6. getmem 2.1.7. getmeminfo 2.1.8. gethostname 2.1.9. getType(msg) 2.1.10. getURI_User(msg) 2.1.11. getExpires(msg) 2.1.12. getHeader(header) 2.1.13. getContact(msg) 2.1.14. moduleFunc(msg, function, args1, args2) 2.1.15. getStatus(msg) 2.1.16. getMethod(msg) 2.1.17. getSrcIp(msg) 2.1.18. getDstIp(msg) 2.1.19. AVP_get(msg, name) 2.1.20. AVP_set(msg, name, value) 2.1.21. AVP_destroy(msg, name) 2.1.22. pseudoVar(msg, variable) 2.1.23. add_lump_rpl List of Examples 1.1. Set luafilename parameter 1.2. lua_exec() usage Chapter 1. Admin Guide 1.1. Overview The time needed when writing a new OpenSIPS module unfortunately is quite high, while the options provided by the configuration file are limited to the features implemented in the modules. With this Lua module, you can easily implement your own OpenSIPS extensions in Lua. 1.2. Installing the module This Lua module is loaded in opensips.cfg (just like all the other modules) with loadmodule("/path/to/lua.so");. For the Lua module to compile, you need a recent version of Lua (tested with 5.1) linked dynamically. The default version of your favorite Linux distribution should work fine. 1.3. Using the module With the Lua module, you can access to lua function on the OpenSIPS side. You need to define a file to load and call a function from it. Write a function "mongo_alias" and then write in your opensips.cfg ... if (lua_exec("mongo_alias")) { ... } ... On the Lua side, you have access to opensips functions and variables (AVP, pseudoVar, ...). Read the documentation below for further informations. 1.4. Dependencies 1.4.1. OpenSIPS Modules None ;-) 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * Lua 5.1.x or later * memcached This module has been developed and tested with Lua 5.1.?, but should work with any 5.1.x release. Earlier versions do not work. On current Debian systems, at least the following packages should be installed: * lua5.1 * liblua5.1-0-dev * libmemcached-dev * libmysqlclient-dev It was reported that other Debian-style distributions (such as Ubuntu) need the same packages. On OpenBSD systems, at least the following packages should be installed: * Lua 1.5. Exported Parameters 1.5.1. luafilename (string) This is the file name of your script. This may be set once only, but it may include an arbitary number of functions and "use" as many Lua module as necessary. The default value is "/etc/opensips/opensips.lua" Example 1.1. Set luafilename parameter ... modparam("lua", "luafilename", "/etc/opensips/opensips.lua") ... 1.5.2. lua_auto_reload (int) Define this value to 1 if you want to reload automatically the lua script. Disabled by default. 1.5.3. warn_missing_free_fixup (int) When you call a function via moduleFunc() you could have a memleak. Enable this warns you when you're doing it. Enabled by default. 1.5.4. lua_allocator (string) Change the default memory allocator for the lua module. Possible values are : * opensips (default) * malloc 1.5.5. lua_user_debug (int) Disable by default 1.6. Exported Functions 1.6.1. lua_exec(func, [param]) Calls a Lua function with passing it the current SIP message. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE and BRANCH_ROUTE. Example 1.2. lua_exec() usage ... if (lua_exec("mongo_alias")) { ... } ... 1.6.2. lua_meminfo() Logs informations about memory. Chapter 2. OpenSIPS Lua API 2.1. Available functions This module provides access to a limited number of OpenSIPS core functions. 2.1.1. xdbg(message) An alias for xlog(DBG, message) 2.1.2. xlog([level],message) Logs the message with OpenSIPS's logging facility. The logging level is one of the following: * ALERT * CRIT * ERR * WARN * NOTICE * INFO * DBG 2.1.3. setUserDebug Enable or disable the xlog feature. Disabled, the xlog function has no effect. 2.1.4. WarnMissingFreeFixup Dynamically change the variable warn_missing_free_fixup. 2.1.5. getpid Returns the current pid. 2.1.6. getmem Returns a table with the size of allocated memory and the fragmentation. 2.1.7. getmeminfo Returns a table with memory infos. 2.1.8. gethostname Returns the value of the current hostname. 2.1.9. getType(msg) Returns "SIP_REQUEST" or "SIP_REPLY". 2.1.10. getURI_User(msg) Returns the user of the To URI. 2.1.11. getExpires(msg) Returns the expires header of the current message. 2.1.12. getHeader(header) Returns the value of the specified header. 2.1.13. getContact(msg) Returns a table with the contact header. 2.1.14. moduleFunc(msg, function, args1, args2) You can pass arguments to this function. 2.1.15. getStatus(msg) Returns the current status if the SIP message is a SIP_REPLY. 2.1.16. getMethod(msg) Returns the current method. 2.1.17. getSrcIp(msg) Returns the IP address of the source. 2.1.18. getDstIp(msg) Returns the IP address of the destination. 2.1.19. AVP_get(msg, name) Returns an AVP variable. 2.1.20. AVP_set(msg, name, value) Defines an AVP variable. 2.1.21. AVP_destroy(msg, name) Destroys an AVP variable. 2.1.22. pseudoVar(msg, variable) Returns a pseudoVar. 2.1.23. add_lump_rpl Add header to the reply. opensips-2.2.2/modules/lua/crc32.c000066400000000000000000000116641300170765700167040ustar00rootroot00000000000000/* $OpenBSD: crc32.c,v 1.9.12.1 2006/10/06 03:19:32 brad Exp $ */ /* * Copyright (c) 2003 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "crc32.h" static const u_int32_t crc32tab[] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; u_int32_t ssh_crc32(const u_char *buf, u_int32_t size) { u_int32_t i, crc; crc = 0; for (i = 0; i < size; i++) crc = crc32tab[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); return crc; } opensips-2.2.2/modules/lua/crc32.h000066400000000000000000000027111300170765700167020ustar00rootroot00000000000000/* $OpenBSD: crc32.h,v 1.14.12.1 2006/10/06 03:19:32 brad Exp $ */ /* * Copyright (c) 2003 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SSH_CRC32_H #define SSH_CRC32_H u_int32_t ssh_crc32(const u_char *, u_int32_t); #endif opensips-2.2.2/modules/lua/doc/000077500000000000000000000000001300170765700163615ustar00rootroot00000000000000opensips-2.2.2/modules/lua/doc/lua.xml000066400000000000000000000023161300170765700176660ustar00rootroot00000000000000 %docentities; ]> lua Module &osipsname; Arnaud Chong
shine@achamo.net
Eric Gouyer
folays@folays.net
Eric Gouyer
folays@folays.net
Arnaud Chong
shine@achamo.net
2006-2011
&admin; &api;
opensips-2.2.2/modules/lua/doc/lua_admin.xml000066400000000000000000000120541300170765700210360ustar00rootroot00000000000000 &adminguide;
Overview The time needed when writing a new OpenSIPS module unfortunately is quite high, while the options provided by the configuration file are limited to the features implemented in the modules. With this Lua module, you can easily implement your own OpenSIPS extensions in Lua.
Installing the module This Lua module is loaded in opensips.cfg (just like all the other modules) with loadmodule("/path/to/lua.so");. For the Lua module to compile, you need a recent version of Lua (tested with 5.1) linked dynamically. The default version of your favorite Linux distribution should work fine.
Using the module With the Lua module, you can access to lua function on the OpenSIPS side. You need to define a file to load and call a function from it. Write a function "mongo_alias" and then write in your opensips.cfg ... if (lua_exec("mongo_alias")) { ... } ... On the Lua side, you have access to opensips functions and variables (AVP, pseudoVar, ...). Read the documentation below for further informations.
Dependencies
OpenSIPS Modules None ;-)
External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: Lua 5.1.x or later memcached This module has been developed and tested with Lua 5.1.?, but should work with any 5.1.x release. Earlier versions do not work. On current Debian systems, at least the following packages should be installed: lua5.1 liblua5.1-0-dev libmemcached-dev libmysqlclient-dev It was reported that other Debian-style distributions (such as Ubuntu) need the same packages. On OpenBSD systems, at least the following packages should be installed: Lua
Exported Parameters
luafilename (string) This is the file name of your script. This may be set once only, but it may include an arbitary number of functions and "use" as many Lua module as necessary. The default value is "/etc/opensips/opensips.lua" Set luafilename parameter ... modparam("lua", "luafilename", "/etc/opensips/opensips.lua") ...
lua_auto_reload (int) Define this value to 1 if you want to reload automatically the lua script. Disabled by default.
warn_missing_free_fixup (int) When you call a function via moduleFunc() you could have a memleak. Enable this warns you when you're doing it. Enabled by default.
lua_allocator (string) Change the default memory allocator for the lua module. Possible values are : opensips (default) malloc
lua_user_debug (int) Disable by default
Exported Functions
lua_exec(func, [param]) Calls a Lua function with passing it the current SIP message. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE and BRANCH_ROUTE. lua_exec() usage ... if (lua_exec("mongo_alias")) { ... } ...
lua_meminfo() Logs informations about memory.
opensips-2.2.2/modules/lua/doc/lua_api.xml000066400000000000000000000075031300170765700205220ustar00rootroot00000000000000 OpenSIPS Lua API
Available functions This module provides access to a limited number of OpenSIPS core functions.
xdbg(message) An alias for xlog(DBG, message)
xlog([level],message) Logs the message with OpenSIPS's logging facility. The logging level is one of the following: ALERT CRIT ERR WARN NOTICE INFO DBG
setUserDebug Enable or disable the xlog feature. Disabled, the xlog function has no effect.
WarnMissingFreeFixup Dynamically change the variable warn_missing_free_fixup.
getpid Returns the current pid.
getmem Returns a table with the size of allocated memory and the fragmentation.
getmeminfo Returns a table with memory infos.
gethostname Returns the value of the current hostname.
getType(msg) Returns "SIP_REQUEST" or "SIP_REPLY".
getURI_User(msg) Returns the user of the To URI.
getExpires(msg) Returns the expires header of the current message.
getHeader(header) Returns the value of the specified header.
getContact(msg) Returns a table with the contact header.
moduleFunc(msg, function, args1, args2) You can pass arguments to this function.
getStatus(msg) Returns the current status if the SIP message is a SIP_REPLY.
getMethod(msg) Returns the current method.
getSrcIp(msg) Returns the IP address of the source.
getDstIp(msg) Returns the IP address of the destination.
AVP_get(msg, name) Returns an AVP variable.
AVP_set(msg, name, value) Defines an AVP variable.
AVP_destroy(msg, name) Destroys an AVP variable.
pseudoVar(msg, variable) Returns a pseudoVar.
add_lump_rpl Add header to the reply.
opensips-2.2.2/modules/lua/sipapi.c000066400000000000000000000605331300170765700172540ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../usr_avp.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_expires.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/contact/contact.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_rr.h" #include "../../socket_info.h" #include "../../forward.h" #include "../../route_struct.h" #include "../../sr_module.h" #include "../../action.h" #include "../../pvar.h" #include "../../script_var.h" #include "../../data_lump_rpl.h" #include "siplua.h" #include "sipluafunc.h" #include "sipapi.h" static void siplua_moduleFunc_free(const char *func, cmd_export_t *exp_func_struct, action_elem_t *elems, int nargs); /* * Warning, should pcall this to prevent exit(1) if being out-of-memory. */ struct sipapi_object *sipapi_create_object(lua_State *L) { struct sipapi_object *o; o = lua_newuserdata(L, sizeof(*o)); memset(o, '\0', sizeof(*o)); luaL_newmetatable(L, "siplua.api"); lua_setmetatable(L, -2); o->ref = luaL_ref(L, LUA_REGISTRYINDEX); return o; } void sipapi_delete_object(struct sipapi_object *o) { } void sipapi_set_object(struct sipapi_object *o, struct sip_msg *msg) { o->msg = msg; } int sipapi_get_object_ref(struct sipapi_object *o) { return o->ref; } static int sipapi_getExpires(struct sip_msg *msg) { exp_body_t *_p_expires; if (parse_headers(msg, ~0, 0) < 0) return -1; if (!msg->expires || parse_expires(msg->expires) < 0) return -1; if (!msg->expires->parsed) return -1; /* siplua_log(L_DBG, "Step 0"); */ if (msg->expires) { /* siplua_log(L_DBG, "Step 1 %p", msg->expires->parsed); */ _p_expires = msg->expires->parsed; /* siplua_log(L_DBG, "Step 2 %p", _p_expires); */ if (_p_expires->valid) { /* siplua_log(L_DBG, "Step 3"); */ return _p_expires->val; } else return -1; } else return -1; } static int l_siplua_getType(lua_State *L) { struct sipapi_object *o; o = luaL_checkudata(L, 1, "siplua.api"); if (o->msg) { switch (o->msg->first_line.type) { case SIP_REQUEST: lua_pushstring(L, "SIP_REQUEST"); break; case SIP_REPLY: lua_pushstring(L, "SIP_REPLY"); break; default: lua_pushnil(L); } } else lua_pushnil(L); return 1; } static int l_siplua_getURI_User(lua_State *L) { struct sipapi_object *o; struct sip_uri *myuri; o = luaL_checkudata(L, 1, "siplua.api"); myuri = parse_to_uri(o->msg); if (!myuri) { /* siplua_log(L_WARN, "parse_to_uri returned NULL results"); */ lua_pushnil(L); } else { /* siplua_log(L_DBG, "parse_to_uri returned non-empty results"); */ lua_pushlstring(L, myuri->user.s, myuri->user.len); } return 1; } static int l_siplua_getExpires(lua_State *L) { struct sipapi_object *o; int expires; o = luaL_checkudata(L, 1, "siplua.api"); /* siplua_log(L_DBG, "BEFORE"); */ expires = sipapi_getExpires(o->msg); /* siplua_log(L_DBG, "AFTER"); */ if (expires != -1) lua_pushinteger(L, expires); else lua_pushnil(L); return 1; } static int l_siplua_getHeader(lua_State *L) { struct sipapi_object *o; const char *str; size_t len; struct hdr_field *hf; o = luaL_checkudata(L, 1, "siplua.api"); str = luaL_checklstring(L, 2, &len); if (parse_headers(o->msg, ~0, 0) < 0) return luaL_error(L, "failed to parse headers"); for (hf = o->msg->headers; hf; hf = hf->next) { if (len == hf->name.len) { if (strncasecmp(str, hf->name.s, len) == 0) { /* Found the right header. */ lua_pushlstring(L, hf->body.s, hf->body.len); return 1; } } } lua_pushnil(L); return 1; } static int l_siplua_getContact(lua_State *L) { struct sipapi_object *o; struct hdr_field *_p; contact_t *_c; int n = 1; int found_hf_no_star = 0; int found_hf_star = 0; int expires; o = luaL_checkudata(L, 1, "siplua.api"); if (!o->msg->contact) { lua_pushnil(L); return 1; } lua_newtable(L); _p = o->msg->contact; for (_p = o->msg->contact; _p; _p = _p->next) { /* siplua_log(L_DBG, "l_siplua_getContact _p/%p", _p); */ if (_p->type == HDR_CONTACT_T) { if (parse_contact(_p) < 0) { return luaL_error(L, "failed to parse Contact body"); } if (((contact_body_t *)_p->parsed)->star) { lua_pushinteger(L, n++); lua_newtable(L); lua_pushstring(L, "star"); lua_pushboolean(L, 1); lua_rawset(L, -3); lua_pushstring(L, "name"); lua_pushstring(L, "*"); lua_rawset(L, -3); lua_pushstring(L, "uri"); lua_pushstring(L, "*"); lua_rawset(L, -3); lua_rawset(L, -3); found_hf_star = 1; } for (_c = ((contact_body_t *)_p->parsed)->contacts; _c; _c = _c->next) { /* siplua_log(L_DBG, "l_siplua_getContact _c/%p", _c); */ if (!_c) break; lua_pushinteger(L, n++); lua_newtable(L); lua_pushstring(L, "name"); lua_pushlstring(L, _c->name.s, _c->name.len); lua_rawset(L, -3); lua_pushstring(L, "uri"); lua_pushlstring(L, _c->uri.s, _c->uri.len); lua_rawset(L, -3); /* siplua_log(L_DBG, "contact q/%p expires/%p", _c->q, _c->expires); */ if (_c->q) { lua_pushstring(L, "q"); lua_pushlstring(L, _c->q->body.s, _c->q->body.len); lua_pushnumber(L, lua_tonumber(L, -1)); lua_remove(L, -2); lua_rawset(L, -3); } if (_c->expires) { lua_pushstring(L, "expires"); lua_pushlstring(L, _c->expires->body.s, _c->expires->body.len); lua_pushnumber(L, lua_tonumber(L, -1)); lua_remove(L, -2); lua_rawset(L, -3); } lua_rawset(L, -3); found_hf_no_star = 1; } } } if (found_hf_star) { if (found_hf_no_star) { lua_remove(L, -1); lua_pushnil(L); siplua_log(L_DBG, "l_siplua_getContact Found Contact HF with both star and no star."); } else { /* siplua_log(L_DBG, "BEFORE"); */ expires = sipapi_getExpires(o->msg); /* siplua_log(L_DBG, "AFTER"); */ if (expires != 0 && expires != -1) { lua_remove(L, -1); lua_pushnil(L); siplua_log(L_DBG, "l_siplua_getContact Found Contact HF star with unvalid expires."); } } } /* siplua_log(L_DBG, "l_siplua_getContact returned."); */ return 1; } static int l_siplua_getRoute(lua_State *L) { struct sipapi_object *o; rr_t *rt; str uri; struct sip_uri puri; int n = 1; o = luaL_checkudata(L, 1, "siplua.api"); if (parse_headers(o->msg, HDR_ROUTE_F, 0) == -1) return luaL_error(L, "failed to parse headers"); if (!o->msg->route) { lua_pushnil(L); return 1; } if (parse_rr(o->msg->route) < 0) return luaL_error(L, "failed to parse route HF"); lua_newtable(L); for (rt = (rr_t *)o->msg->route->parsed; rt; rt = rt->next ) { uri = rt->nameaddr.uri; lua_pushinteger(L, n++); lua_newtable(L); lua_pushliteral(L, "uri"); lua_pushlstring(L, uri.s, uri.len); lua_rawset(L, -3); if (parse_uri(uri.s, uri.len, &puri) < 0) { if (n == 1) return luaL_error(L, "failed to parse the first route URI"); continue; } lua_pushliteral(L, "user"); lua_pushlstring(L, puri.user.s, puri.user.len); lua_rawset(L, -3); lua_pushliteral(L, "host"); lua_pushlstring(L, puri.host.s, puri.host.len); lua_rawset(L, -3); lua_pushliteral(L, "port"); lua_pushinteger(L, puri.port_no); lua_rawset(L, -3); lua_pushliteral(L, "params"); lua_pushlstring(L, puri.params.s, puri.params.len); lua_rawset(L, -3); lua_pushliteral(L, "lr"); lua_pushlstring(L, puri.lr.s, puri.lr.len); lua_rawset(L, -3); lua_pushliteral(L, "lr_val"); lua_pushlstring(L, puri.lr_val.s, puri.lr_val.len); lua_rawset(L, -3); lua_pushliteral(L, "r2"); lua_pushlstring(L, puri.r2.s, puri.r2.len); lua_rawset(L, -3); lua_pushliteral(L, "r2_val"); lua_pushlstring(L, puri.r2_val.s, puri.r2_val.len); lua_rawset(L, -3); lua_pushliteral(L, "is_myself"); if (check_self(&puri.host, puri.port_no ? puri.port_no : SIP_PORT, 0) >= 0) lua_pushboolean(L, 1); else lua_pushboolean(L, 0); lua_rawset(L, -3); lua_rawset(L, -3); } return 1; } /* does not check user. See ENABLE_USER_CHECK in modules/rr/loose.c:is_myself() */ /* look into the aliases */ static int l_siplua_isMyself(lua_State *L) { struct sip_uri puri; size_t len; int ret; memset(&puri, '\0', sizeof(puri)); puri.host.s = (char *)luaL_checklstring(L, 1, &len); puri.host.len = len; puri.port_no = luaL_checkinteger(L, 2); set_sip_defaults(puri.port_no, puri.proto); ret = check_self(&puri.host, puri.port_no, puri.proto); if (!ret) lua_pushnil(L); else lua_pushboolean(L, 1); return 1; } /* Similar to isMyself, but without taking a look into the aliases */ static int l_siplua_grepSockInfo(lua_State *L) { struct sip_uri puri; size_t len; struct socket_info *si; memset(&puri, '\0', sizeof(puri)); puri.host.s = (char *)luaL_checklstring(L, 1, &len); puri.host.len = len; puri.port_no = luaL_checkinteger(L, 2); set_sip_defaults(puri.port_no, puri.proto); si = grep_sock_info(&puri.host, puri.port_no, puri.proto); if (!si) lua_pushnil(L); else { lua_newtable(L); lua_pushliteral(L, "name"); lua_pushlstring(L, si->name.s, si->name.len); lua_rawset(L, -3); lua_pushliteral(L, "port"); lua_pushinteger(L, si->port_no); lua_rawset(L, -3); if (si->adv_name_str.s) { lua_pushliteral(L, "adv_name"); lua_pushlstring(L, si->adv_name_str.s, si->adv_name_str.len); lua_rawset(L, -3); } if (si->adv_port) { lua_pushliteral(L, "adv_port"); lua_pushinteger(L, si->adv_port); lua_rawset(L, -3); } } return 1; } /* * FIXME: Don't work and returns false IPs. Cheers! * PS: Probably not in network host order. */ static int l_siplua_getSrcIp(lua_State *L) { struct sipapi_object *o; struct sockaddr sa; char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; o = luaL_checkudata(L, 1, "siplua.api"); sa.sa_family = o->msg->rcv.src_ip.af; memcpy(sa.sa_data, &o->msg->rcv.src_ip.u, o->msg->rcv.src_ip.len); if (getnameinfo(&sa, sizeof(sa), hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) { siplua_log(L_DBG, "could not get numeric hostname"); lua_pushnil(L); } else { siplua_log(L_DBG, "host=%s, serv=%s\n", hbuf, sbuf); lua_pushstring(L, hbuf); } return 1; } /* * FIXME: Don't work and returns false IPs. Cheers! * PS: Probably not in network host order. */ static int l_siplua_getDstIp(lua_State *L) { struct sipapi_object *o; struct sockaddr sa; char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; o = luaL_checkudata(L, 1, "siplua.api"); sa.sa_family = o->msg->rcv.dst_ip.af; memcpy(sa.sa_data, &o->msg->rcv.dst_ip.u, o->msg->rcv.dst_ip.len); if (getnameinfo(&sa, sizeof(sa), hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) { siplua_log(L_DBG, "could not get numeric hostname"); lua_pushnil(L); } else { siplua_log(L_DBG, "host=%s, serv=%s\n", hbuf, sbuf); lua_pushstring(L, hbuf); } return 1; } static int l_siplua_getStatus(lua_State *L) { struct sipapi_object *o; o = luaL_checkudata(L, 1, "siplua.api"); if (o->msg->first_line.type != SIP_REPLY) lua_pushnil(L); else lua_pushlstring(L, o->msg->first_line.u.reply.status.s, o->msg->first_line.u.reply.status.len); return 1; } static int l_siplua_getMethod(lua_State *L) { struct sipapi_object *o; o = luaL_checkudata(L, 1, "siplua.api"); if (o->msg->first_line.type != SIP_REQUEST) lua_pushnil(L); else lua_pushlstring(L, o->msg->first_line.u.request.method.s, o->msg->first_line.u.request.method.len); return 1; } static int l_siplua_AVP_set(lua_State *L) { int name; int_str val; str s; int retval; int flags = 0; luaL_checkany(L, 1); luaL_checkany(L, 2); s.s = (char *) lua_tostring(L, 1); s.len = strlen(s.s); name = get_avp_id(&s); if (lua_type(L, 2) == LUA_TNUMBER) val.n = luaL_checkinteger(L, 2); else { val.s.s = (char *)luaL_checkstring(L, 2); val.s.len = strlen(val.s.s); flags |= AVP_VAL_STR; } retval = add_avp(flags, name, val); if (!retval) lua_pushboolean(L, 1); else lua_pushnil(L); return 1; } static int l_siplua_AVP_get(lua_State *L) { struct usr_avp *first_avp; int name; str s; int_str val; int flags = 0; luaL_checkany(L, 1); s.s = (char *) lua_tostring(L, 1); s.len = strlen(s.s); name = get_avp_id(&s); first_avp = search_first_avp(flags, name, &val, NULL); if (first_avp != NULL) { if (is_avp_str_val(first_avp)) lua_pushlstring(L, val.s.s, val.s.len); else lua_pushinteger(L, val.n); } else lua_pushnil(L); return 1; } static int l_siplua_AVP_destroy(lua_State *L) { struct usr_avp *first_avp; int name; str s; int_str val; int flags = 0; luaL_checkany(L, 1); s.s = (char *) lua_tostring(L, 1); s.len = strlen(s.s); name = get_avp_id(&s); first_avp = search_first_avp(flags, name, &val, NULL); if (first_avp != NULL) { destroy_avp(first_avp); lua_pushboolean(L, 1); } else lua_pushnil(L); return 1; } static int l_siplua_pseudoVar(lua_State *L) { struct sipapi_object *o; const char *name; str s; pv_elem_t *model; int buf_size = 4096; char *out; o = luaL_checkudata(L, 1, "siplua.api"); name = luaL_checkstring(L, 2); s.s = (char *)name; s.len = strlen(name); if (pv_parse_format(&s, &model) < 0) { lua_pushnil(L); return 1; } out = pkg_malloc(buf_size); if (!out) { pv_elem_free_all(model); return luaL_error(L, "Not enough memory"); } if (pv_printf(o->msg, model, out, &buf_size) < 0) lua_pushnil(L); else lua_pushstring(L, out); pkg_free(out); pv_elem_free_all(model); return 1; } static int l_siplua_pseudoVarSet(lua_State *L) { struct sipapi_object *o; const char *name; str s; pv_spec_t dspec; pv_value_t val; int retval; o = luaL_checkudata(L, 1, "siplua.api"); name = luaL_checkstring(L, 2); s.s = (char *)name; s.len = strlen(name); if (!pv_parse_spec(&s, &dspec)) return luaL_error(L, "error in parsing pvar `%s'", name); if (!pv_is_w(&dspec)) return luaL_error(L, "read only PV in left expression"); luaL_checkany(L, 3); if (lua_type(L, 3) == LUA_TNIL) { val.flags = PV_VAL_NULL; } else if (lua_type(L, 3) == LUA_TNUMBER) { val.ri = luaL_checkinteger(L, 3); val.flags = PV_VAL_INT; } else { val.rs.s = (char *)luaL_checkstring(L, 3); val.rs.len = strlen(val.rs.s); val.flags = PV_VAL_STR; } /* siplua_log(L_ALERT, "dspec.setf(, , EQ_T, %.*s)", val.rs.len, val.rs.s); */ retval = pv_set_value(o->msg, &dspec, EQ_T, &val); if (retval >= 0) lua_pushboolean(L, 1); else lua_pushnil(L); return 1; } static int l_siplua_scriptVarGet(lua_State *L) { const char *name; str s; script_var_t *it; name = luaL_checkstring(L, 1); if (*name == '$') ++name; s.s = (char *)name; s.len = strlen(name); it = get_var_by_name(&s); if (!it) lua_pushnil(L); else { switch (it->v.flags) { case 0: lua_pushinteger(L, it->v.value.n); break; case VAR_VAL_STR: lua_pushlstring(L, it->v.value.s.s, it->v.value.s.len); break; } } return 1; } static int l_siplua_scriptVarSet(lua_State *L) { const char *name; str s; int_str val; int flags = 0; script_var_t *it; name = luaL_checkstring(L, 1); if (*name == '$') ++name; s.s = (char *)name; s.len = strlen(name); switch (lua_type(L, 2)) { case LUA_TNIL: /* no way currently exists to drop a script variable */ /* well, set_var_value(it, NULL, 0) API exists but won't do anything useful */ val.n = 0; break; case LUA_TNUMBER: val.n = luaL_checkinteger(L, 2); break; case LUA_TSTRING: flags = VAR_VAL_STR; val.s.s = (char *)luaL_checkstring(L, 2); val.s.len = strlen(val.s.s); break; default: return luaL_error(L, "scriptVarSet %s type value not supported", lua_typename(L, lua_type(L, 2))); } it = get_var_by_name(&s); if (!it) it = add_var(&s); if (!it) return luaL_error(L, "add_var of script variable `%s' failed", name); if (set_var_value(it, &val, flags)) lua_pushboolean(L, 1); else lua_pushboolean(L, 0); return 1; } static int l_siplua_add_lump_rpl(lua_State *L) { struct sipapi_object *o; const char *name; size_t len; o = luaL_checkudata(L, 1, "siplua.api"); name = luaL_checklstring(L, 2, &len); add_lump_rpl(o->msg, (char *)name, len, LUMP_RPL_HDR); return 1; } /* * It could be useful to keep all fixup'ed parameters into a hash table, so that it would * permit to call function modules missing free_fixup, as long as parameters don't change. */ static int siplua_unsafemodfnc = 1; static int l_siplua_moduleFunc(lua_State *L) { struct sipapi_object *o; const char *func; int n, nargs; cmd_export_t *exp_func_struct; action_elem_t elems[MAX_ACTION_ELEMS]; const char *s, *msg; char *str; int i; struct action *act; int retval; o = luaL_checkudata(L, 1, "siplua.api"); func = luaL_checkstring(L, 2); n = lua_gettop(L); nargs = n - 2; if (n - 1 > MAX_ACTION_ELEMS) return luaL_error(L, "function '%s' called with too many arguments [%d > %d]", func, nargs, MAX_ACTION_ELEMS - 1); exp_func_struct = find_cmd_export_t((char *)func, nargs, 0); if (!exp_func_struct) { return luaL_error(L, "function '%s' called, but not available."); } elems[0].type = CMD_ST; elems[0].u.data = exp_func_struct; memset(&elems[1], '\0', nargs * sizeof(action_elem_t)); for (i = 0; i < nargs; ++i) { s = lua_tostring(L, 3 + i); if (!s) { siplua_moduleFunc_free(func, exp_func_struct, elems, nargs); msg = lua_pushfstring(L, "%s expected, got %s", lua_typename(L, LUA_TSTRING), luaL_typename(L, 3 + i)); return luaL_argerror(L, 3 + i, msg); } str = pkg_malloc(strlen(s) + 1); if (!str) { siplua_moduleFunc_free(func, exp_func_struct, elems, nargs); return luaL_error(L, "Not enough memory"); } strcpy(str, s); /* We should maybe try STR_ST and elems[].u.str.{s,len} */ elems[i + 1].type = STRING_ST; elems[i + 1].u.data = str; /* elems[].u.string */ } act = mk_action(MODULE_T, n - 2 + 1, elems, 0, "lua"); if (!act) { siplua_moduleFunc_free(func, exp_func_struct, elems, nargs); return luaL_error(L, "action structure could not be created. Error."); } /* siplua_log(L_DBG, "fixup/%p free_fixup/%p", */ /* exp_func_struct->fixup, */ /* exp_func_struct->free_fixup); */ if (exp_func_struct->fixup) { if (!siplua_unsafemodfnc && !exp_func_struct->free_fixup) { siplua_moduleFunc_free(func, exp_func_struct, act->elem, nargs); return luaL_error(L, "Module function '%s' is unsafe. Call is refused.\n", func); } if (nargs == 0) { retval = exp_func_struct->fixup(0, 0); if (retval < 0) { siplua_moduleFunc_free(func, exp_func_struct, act->elem, nargs); return luaL_error(L, "Error in fixup (0)\n"); } } for (i = 0; i < nargs; ++i) { retval = exp_func_struct->fixup(&act->elem[i + 1].u.data, i + 1); if (retval < 0) { siplua_moduleFunc_free(func, exp_func_struct, act->elem, nargs); return luaL_error(L, "Error in fixup (%d)\n", i + 1); } act->elem[i + 1].type = MODFIXUP_ST; } } retval = do_action(act, o->msg); siplua_moduleFunc_free(func, exp_func_struct, act->elem, nargs); pkg_free(act); lua_pushinteger(L, retval); return 1; } static void siplua_moduleFunc_free(const char *func, cmd_export_t *exp_func_struct, action_elem_t *elems, int nargs) { int i; for (i = 0; i < nargs; ++i) { if (!elems[i + 1].u.data) continue; switch (elems[i + 1].type) { case STRING_ST: pkg_free(elems[i + 1].u.data); break; case MODFIXUP_ST: if (!exp_func_struct->free_fixup) { if (warn_missing_free_fixup) siplua_log(L_DBG, "moduleFunction (%s): A fixup function was called without corresponding free_fixup. " "This currently *COULD* creates a memory leak.\n", func); } else exp_func_struct->free_fixup(&elems[i + 1].u.data, i + 1); break; } } } static const struct luaL_reg siplua_api_mylib [] = { {"getType", l_siplua_getType}, {"getURI_User", l_siplua_getURI_User}, {"getExpires", l_siplua_getExpires}, {"getHeader", l_siplua_getHeader}, {"getContact", l_siplua_getContact}, {"getRoute", l_siplua_getRoute}, {"isMyself", l_siplua_isMyself}, {"grepSockInfo", l_siplua_grepSockInfo}, {"moduleFunc", l_siplua_moduleFunc}, {"getStatus", l_siplua_getStatus}, {"getMethod", l_siplua_getMethod}, {"getSrcIp", l_siplua_getSrcIp}, {"getDstIp", l_siplua_getDstIp}, {"AVP_get", l_siplua_AVP_get}, {"AVP_set", l_siplua_AVP_set}, {"AVP_destroy", l_siplua_AVP_destroy}, {"pseudoVar", l_siplua_pseudoVar}, {"pseudoVarSet", l_siplua_pseudoVarSet}, {"scriptVarGet", l_siplua_scriptVarGet}, {"scriptVarSet", l_siplua_scriptVarSet}, {"add_lump_rpl", l_siplua_add_lump_rpl}, {NULL, NULL} /* sentinel */ }; void siplua_register_api_cclosures(lua_State *L) { lua_pushvalue(L, LUA_GLOBALSINDEX); luaL_openlib(L, NULL, siplua_api_mylib, 0); lua_remove(L, -1); } /* SV *getStringFromURI(SV *self, enum xs_uri_members what) { */ /* struct sip_uri *myuri = sv2uri(self); */ /* str *ret = NULL; */ /* if (!myuri) { */ /* LM_ERR("Invalid URI reference\n"); */ /* ret = NULL; */ /* } else { */ /* switch (what) { */ /* case XS_URI_USER: ret = &(myuri->user); */ /* break; */ /* case XS_URI_HOST: ret = &(myuri->host); */ /* break; */ /* case XS_URI_PASSWD: ret = &(myuri->passwd); */ /* break; */ /* case XS_URI_PORT: ret = &(myuri->port); */ /* break; */ /* case XS_URI_PARAMS: ret = &(myuri->params); */ /* break; */ /* case XS_URI_HEADERS: ret = &(myuri->headers); */ /* break; */ /* case XS_URI_TRANSPORT: ret = &(myuri->transport); */ /* break; */ /* case XS_URI_TTL: ret = &(myuri->ttl); */ /* break; */ /* case XS_URI_USER_PARAM: ret = &(myuri->user_param); */ /* break; */ /* case XS_URI_MADDR: ret = &(myuri->maddr); */ /* break; */ /* case XS_URI_METHOD: ret = &(myuri->method); */ /* break; */ /* case XS_URI_LR: ret = &(myuri->lr); */ /* break; */ /* case XS_URI_R2: ret = &(myuri->r2); */ /* break; */ /* case XS_URI_TRANSPORT_VAL: ret = &(myuri->transport_val); */ /* break; */ /* case XS_URI_TTL_VAL: ret = &(myuri->ttl_val); */ /* break; */ /* case XS_URI_USER_PARAM_VAL: ret = &(myuri->user_param_val); */ /* break; */ /* case XS_URI_MADDR_VAL: ret = &(myuri->maddr_val); */ /* break; */ /* case XS_URI_METHOD_VAL: ret = &(myuri->method_val); */ /* break; */ /* case XS_URI_LR_VAL: ret = &(myuri->lr_val); */ /* break; */ /* case XS_URI_R2_VAL: ret = &(myuri->r2_val); */ /* break; */ /* default: LM_INFO("Unknown URI element" */ /* " requested: %d\n", what); */ /* break; */ /* } */ /* } */ /* if ((ret) && (ret->len)) { */ /* return sv_2mortal(newSVpv(ret->s, ret->len)); */ /* } else { */ /* return &PL_sv_undef; */ /* } */ /* } */ opensips-2.2.2/modules/lua/sipapi.h000066400000000000000000000025011300170765700172500ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct sip_msg; #include #ifndef SIPAPI_H_ # define SIPAPI_H_ void siplua_register_api_cclosures(lua_State *L); struct sipapi_object { int finalized; int ref; struct sip_msg *msg; }; struct sipapi_object *sipapi_create_object(lua_State *L); void sipapi_delete_object(struct sipapi_object *o); void sipapi_set_object(struct sipapi_object *o, struct sip_msg *msg); int sipapi_get_object_ref(struct sipapi_object *o); #endif /* !SIPAPI_H_ */ opensips-2.2.2/modules/lua/sipdatetime.c000066400000000000000000000113261300170765700202730ustar00rootroot00000000000000/* * Copyright (c) 2006, 2007, 2008, 2009 * Eric Gouyer * Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define _XOPEN_SOURCE #include #include #define __USE_MISC /* for timegm() */ #include #include #include struct sipdatetime { int finalized; time_t v; }; static int l_sipdatetime_now(lua_State *L) { struct sipdatetime *o; o = lua_newuserdata(L, sizeof(*o)); memset(o, '\0', sizeof(*o)); luaL_getmetatable(L, "siplua.datetime"); lua_setmetatable(L, -2); o->v = time(NULL); /* UTC */ return 1; } static int l_sipdatetime_duplicate(lua_State *L) { struct sipdatetime *o; struct sipdatetime *o2; o = luaL_checkudata(L, 1, "siplua.datetime"); if (o->finalized) { lua_pushnil(L); return 1; } o2 = lua_newuserdata(L, sizeof(*o2)); memset(o2, '\0', sizeof(*o2)); luaL_getmetatable(L, "siplua.datetime"); lua_setmetatable(L, -2); o2->v = o->v; return 1; } static int sipdatetime_strftime(struct sipdatetime *o, lua_State *L, const char *format) { struct tm tm; char buf[256]; int ret; gmtime_r(&o->v, &tm); /* UTC */ ret = strftime(buf, sizeof(buf), format, &tm); if (!ret || ret >= sizeof(buf)) { lua_pushnil(L); return 1; } lua_pushlstring(L, buf, ret); return 1; } static int l_sipdatetime_strftime(lua_State *L) { struct sipdatetime *o; const char *str; o = luaL_checkudata(L, 1, "siplua.datetime"); str = luaL_checkstring(L, 2); if (o->finalized) { lua_pushnil(L); return 1; } return sipdatetime_strftime(o, L, str); } static int l_sipdatetime_str(lua_State *L) { struct sipdatetime *o; o = luaL_checkudata(L, 1, "siplua.datetime"); if (o->finalized) { lua_pushnil(L); return 1; } return sipdatetime_strftime(o, L, "%Y-%m-%d %H:%M:%S"); } static int l_sipdatetime_add(lua_State *L) { struct sipdatetime *o; int n; o = luaL_checkudata(L, 1, "siplua.datetime"); n = luaL_checkinteger(L, 2); if (o->finalized) { lua_pushnil(L); return 1; } o->v += n; lua_pushvalue(L, 1); return 1; } static int l_sipdatetime_compare(lua_State *L) { struct sipdatetime *o; struct sipdatetime *o2; int cmp; o = luaL_checkudata(L, 1, "siplua.datetime"); o2 = luaL_checkudata(L, 2, "siplua.datetime"); if (o->finalized || o2->finalized) { lua_pushnil(L); return 1; } cmp = difftime(o->v, o2->v); lua_pushinteger(L, cmp); return 1; } static int l_sipdatetime_parse_str(lua_State *L) { const char *str; struct tm tm; char *ret; struct sipdatetime *o; str = luaL_checkstring(L, 1); ret = strptime(str, "%Y-%m-%d %H:%M:%S", &tm); if (!ret || *ret != '\0') { lua_pushnil(L); return 1; } o = lua_newuserdata(L, sizeof(*o)); memset(o, '\0', sizeof(*o)); luaL_getmetatable(L, "siplua.datetime"); lua_setmetatable(L, -2); o->v = timegm(&tm); /* UTC */ return 1; } static int l_sipdatetime___gc(lua_State *L) { return 0; } static int l_sipdatetime___index(lua_State *L) { luaL_checkudata(L, 1, "siplua.datetime"); lua_getmetatable(L, 1); luaL_checkstring(L, 2); lua_pushvalue(L, 2); lua_rawget(L, -2); lua_remove(L, -2); return 1; } static const struct luaL_reg siplua_datetime_mylib [] = { {"duplicate", l_sipdatetime_duplicate}, {"strftime", l_sipdatetime_strftime}, {"str", l_sipdatetime_str}, {"add", l_sipdatetime_add}, {"compare", l_sipdatetime_compare}, {"parse_str", l_sipdatetime_parse_str}, {"__gc", l_sipdatetime___gc}, {"__index", l_sipdatetime___index}, {NULL, NULL} /* sentinel */ }; void siplua_register_datetime_cclosures(lua_State *L) { luaL_newmetatable(L, "siplua.datetime"); luaL_openlib(L, NULL, siplua_datetime_mylib, 0); lua_remove(L, -1); lua_pushcclosure(L, l_sipdatetime_now, 0); lua_setglobal(L, "datetime_now"); lua_pushcclosure(L, l_sipdatetime_parse_str, 0); lua_setglobal(L, "datetime_parse_str"); } opensips-2.2.2/modules/lua/sipdatetime.h000066400000000000000000000020231300170765700202720ustar00rootroot00000000000000/* * Copyright (c) 2006, 2007, 2008, 2009 * Eric Gouyer * Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef SIPDATETIME_H_ # define SIPDATETIME_H_ void siplua_register_datetime_cclosures(lua_State *L); #endif /* !SIPDATETIME_H_ */ opensips-2.2.2/modules/lua/siplua.c000066400000000000000000000074321300170765700172630ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "../../sr_module.h" #include "../sl/sl_api.h" #include "siplua.h" #include "sipluafunc.h" #include "sipluami.h" #include "sipwatch.h" #include "sipstate.h" char *luafilename = ""; int lua_user_debug = 1; int warn_missing_free_fixup = 1; char *lua_allocator = "opensips"; int lua_auto_reload = 0; static void destroy(void); static int child_init(int rank); static int mod_init(void); struct sl_binds slb; /* * Exported functions */ static cmd_export_t cmds[] = { { "lua_exec", (cmd_function)siplua_exec1, 1, NULL, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, { "lua_exec", (cmd_function)siplua_exec2, 2, NULL, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, { "lua_meminfo", (cmd_function)siplua_meminfo, 0, NULL, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, { 0, 0, 0, 0, 0, 0 } }; /* * Exported parameters */ static param_export_t params[] = { { "luafilename", STR_PARAM, &luafilename}, { "lua_user_debug", INT_PARAM, &lua_user_debug}, { "warn_missing_free_fixup", INT_PARAM, &warn_missing_free_fixup}, { "lua_allocator", STR_PARAM, &lua_allocator}, { "lua_auto_reload", INT_PARAM, &lua_auto_reload}, { 0, 0, 0 } }; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { { "lua_reload", 0,siplua_mi_reload, 0, 0, 0 }, { "bla" , 0,siplua_mi_bla, 0, 0, 0 }, { "watch", 0,siplua_mi_watch, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 } }; /* * Module interface */ struct module_exports exports = { "lua", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, RTLD_NOW | RTLD_GLOBAL, NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; static int child_init(int rank) { siplua_log(L_INFO, "child_init"); if (sipstate_open(lua_allocator)) { siplua_log(L_ERR, "failed to initialize siplua's Lua state"); return -1; } if (sipstate_load(luafilename)) { siplua_log(L_ERR, "failed to load siplua's file %s", luafilename); sipstate_close(); return -1; } return 0; } /* * mod_init * Called by openser at init time */ static int mod_init(void) { int ret = 0; siplua_log(L_INFO, "mod_init"); /* load the SL API */ if (load_sl_api(&slb)!=0) { siplua_log(L_CRIT, "can't load SL API\n"); return -1; } if (sipwatch_create_object()) { siplua_log(L_CRIT, "failed to initialized siplua's watch object"); return -1; } return ret; } /* * destroy * called by openser at exit time */ static void destroy(void) { siplua_log(L_INFO, "destroy"); } opensips-2.2.2/modules/lua/siplua.h000066400000000000000000000020551300170765700172640ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef SIPLUA_H_ # define SIPLUA_H_ extern int lua_user_debug; extern int warn_missing_free_fixup; extern int lua_auto_reload; extern struct sl_binds slb; #endif /* !SIPLUA_H_ */ opensips-2.2.2/modules/lua/sipluafunc.c000066400000000000000000000076111300170765700201360ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define _GNU_SOURCE #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" /* #include "../../data_lump.h" */ /* #include "../../parser/parse_param.h" */ /* #include "../../parser/msg_parser.h" */ /* #include "../../dprint.h" */ /* #include "../../action.h" */ /* #include "../../config.h" */ #include "../../parser/parse_uri.h" #include "../sl/sl_api.h" #include "siplua.h" #include "sipluafunc.h" #include "sipapi.h" #include "sipstate.h" #ifndef LM_GEN1 # define LM_GEN1 LOG /* 1.3.x backward compatibility */ #endif /* !LM_GEN1 */ void siplua_log(int lev, const char *format, ...) { va_list ap; char *ret; int priority; int rc; if (!format) return; if (!(is_printable(lev) | lua_user_debug)) return; va_start(ap, format); rc = vasprintf(&ret, format, ap); va_end(ap); if (rc < 0) return; LM_GEN1(lev, "siplua: %s", ret); if (lua_user_debug) { switch (lev) { case L_ALERT: priority = LOG_ALERT; break; case L_CRIT: priority = LOG_CRIT; break; case L_ERR: priority = LOG_ERR; break; case L_WARN: priority = LOG_WARNING; break; case L_NOTICE: priority = LOG_NOTICE; break; case L_INFO: priority = LOG_INFO; break; case L_DBG: priority = LOG_DEBUG; break; default: /* should not happen, no execution path permits it */ priority = LOG_ERR; } syslog(LOG_USER | priority, "siplua: %s", ret); } free(ret); } void siplua_notice(int local, const char *format, ...) { va_list ap; if (!(local >= 0 && local <= 7)) return; va_start(ap, format); vsyslog((LOG_LOCAL0 + local) | LOG_NOTICE, format, ap); va_end(ap); } static int siplua_exec(struct sip_msg* _msg, const char *fnc, const char *mystr) { str reason; if ((_msg->first_line).type != SIP_INVALID) parse_headers(_msg, ~0, 0); switch ((_msg->first_line).type) { case SIP_REQUEST: if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("failed to parse Request-URI\n"); reason.s = "Bad Request-URI"; reason.len = sizeof("Bad Request-URI")-1; if (slb.reply(_msg, 400, &reason) == -1) { LM_ERR("failed to send reply\n"); } return -1; } break; case SIP_REPLY: break; default: LM_ERR("invalid firstline"); return -1; } return sipstate_call(_msg, fnc, mystr); } int siplua_exec1(struct sip_msg* _msg, char *fnc, char *str) { /* siplua_log(L_DBG, "exec1(,,%s)", str); */ int ret; ret = siplua_exec(_msg, fnc, NULL); return (ret>=0)?1:-1; } int siplua_exec2(struct sip_msg* _msg, char *fnc, char *str) { int ret; /* siplua_log(L_DBG, "exec2(,,%s)", str); */ ret = siplua_exec(_msg, fnc, str); /* siplua_log(L_DBG, "exec2 RETURN %d", n); */ return (ret>=0)?1:-1; } int siplua_meminfo(struct sip_msg *msg) { struct mem_info info; shm_info(&info); siplua_log(L_INFO, "free/%d used/%d real_used/%d max_used/%d min_frag/%d total_frags/%d", info.free, info.used, info.real_used, info.max_used, info.min_frag, info.total_frags); return -1; } opensips-2.2.2/modules/lua/sipluafunc.h000066400000000000000000000025641300170765700201450ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "../../parser/msg_parser.h" #ifndef SIPLUAFUNC_H_ # define SIPLUAFUNC_H_ /* * Run a lua function with a reference to the current SIP message. * An optional string may be passed to lua_exec. */ int siplua_exec1(struct sip_msg* _msg, char *fnc, char *str); int siplua_exec2(struct sip_msg* _msg, char *fnc, char *str); int siplua_meminfo(struct sip_msg* _msg); void siplua_log(int lev, const char *format, ...); void siplua_notice(int local, const char *format, ...); #endif /* !SIPLUAFUNC_H_ */ opensips-2.2.2/modules/lua/sipluami.c000066400000000000000000000051471300170765700176120ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define _GNU_SOURCE #include #include #include #include #include #include "../../mi/mi.h" #include "sipwatch.h" #include "sipluami.h" #define SIPLUAMI_USAGE "usage: watch [add | delete | show] [extension]" struct mi_root *siplua_mi_reload(struct mi_root *cmd_tree, void *param) { struct mi_root *answer; answer = init_mi_tree(200, "xOK", 3); addf_mi_node_child(&answer->node, 0, "pid", 3, "%d", (int)getpid()); return answer; } struct mi_root *siplua_mi_bla(struct mi_root *cmd_tree, void *param) { return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } struct mi_root *siplua_mi_watch(struct mi_root *cmd_tree, void *param) { struct mi_root *answer; struct mi_node *node; str action; node = cmd_tree->node.kids; if (!node) return init_mi_tree(200, SIPLUAMI_USAGE, sizeof(SIPLUAMI_USAGE) - 1); action = node->value; node = node->next; if (action.len == 3 && !strncmp("add", action.s, action.len)) { if (!node) return init_mi_tree(200, "usage: missing extension", 24); sipwatch_add(node->value.s, node->value.len); } if (action.len == 6 && !strncmp("delete", action.s, action.len)) { if (!node) return init_mi_tree(200, "usage: missing extension", 24); sipwatch_delete(node->value.s, node->value.len); } if (action.len == 4 && !strncmp("show", action.s, action.len)) { int i; answer = init_mi_tree(200, "xOK", 3); answer->node.flags |= MI_IS_ARRAY; sipwatch_lock(); for (i = 0; i < siplua_watch->nb; ++i) addf_mi_node_child(&answer->node, 0, "extension", 9, "%s", siplua_watch->ext[i].str); sipwatch_unlock(); return answer; } answer = init_mi_tree(200, "xOK", 3); return answer; } opensips-2.2.2/modules/lua/sipluami.h000066400000000000000000000022541300170765700176130ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "../../mi/mi.h" #ifndef SIPLUAMI_H_ # define SIPLUAMI_H_ struct mi_root *siplua_mi_reload(struct mi_root *cmd_tree, void *param); struct mi_root *siplua_mi_bla(struct mi_root *cmd_tree, void *param); struct mi_root *siplua_mi_watch(struct mi_root *cmd_tree, void *param); #endif /* !SIPLUAMI_H_ */ opensips-2.2.2/modules/lua/sipmemcache.c000066400000000000000000000177741300170765700202560ustar00rootroot00000000000000/* * Copyright (c) 2007, 2008, 2009 * Eric Gouyer * Copyright (c) 2007, 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../ut.h" #include "sipluafunc.h" struct sipmemcache { int finalized; memcached_st memc; const char **keys; size_t *keyslen; }; static int l_sipmemcache_new(lua_State *L) { struct sipmemcache *o; o = lua_newuserdata(L, sizeof(*o)); memset(o, '\0', sizeof(*o)); luaL_getmetatable(L, "siplua.memcache"); lua_setmetatable(L, -2); if (!memcached_create(&o->memc) || memcached_behavior_set(&o->memc, MEMCACHED_BEHAVIOR_NO_BLOCK,1) != MEMCACHED_SUCCESS ) { lua_remove(L, -1); lua_pushnil(L); } return 1; } static int l_sipmemcache_server_add(lua_State *L) { memcached_server_st *servers = NULL; memcached_return rc; struct sipmemcache *o; const char *host; const char *port; in_port_t iport; str s; o = luaL_checkudata(L, 1, "siplua.memcache"); host = luaL_checkstring(L, 2); port = luaL_checkstring(L, 3); if (o->finalized) { lua_pushnil(L); } else { s.len = strlen(port); s.s = (char *)port; if (str2int(&s, (unsigned int *)&iport) < 0) lua_pushboolean(L, 0); else lua_pushboolean(L, 1); servers = memcached_server_list_append(servers, host, iport, &rc); if (rc != MEMCACHED_SUCCESS) { LM_ERR("cannot add server: %s\n", memcached_strerror(&o->memc, rc)); lua_pushboolean(L, 0); } else lua_pushboolean(L, 1); rc = memcached_server_push(&o->memc, servers); if (rc != MEMCACHED_SUCCESS) { LM_ERR("cannot push server: %s\n", memcached_strerror(&o->memc, rc)); lua_pushboolean(L, 0); } else lua_pushboolean(L, 1); } return 1; } static int sipmemcache_storage_cmds(lua_State *L, memcached_return (*f)(memcached_st *ptr, const char *key, size_t key_length, const char *value, size_t value_length, time_t expiration, uint32_t flags)) { struct sipmemcache *o; const char *key, *val; size_t keyslen, bytes; time_t expire; o = luaL_checkudata(L, 1, "siplua.memcache"); key = luaL_checklstring(L, 2, &keyslen); val = luaL_checklstring(L, 3, &bytes); expire = luaL_optinteger(L, 4, 3600); if (o->finalized) { lua_pushnil(L); } else { if (f(&o->memc, key, keyslen, val, bytes, expire, 0) == MEMCACHED_SUCCESS) lua_pushinteger(L, 0); else lua_pushinteger(L, -1); } return 1; } static int l_sipmemcache_add(lua_State *L) { return sipmemcache_storage_cmds(L, memcached_add); } static int l_sipmemcache_replace(lua_State *L) { return sipmemcache_storage_cmds(L, memcached_replace); } static int l_sipmemcache_set(lua_State *L) { return sipmemcache_storage_cmds(L, memcached_set); } static int sipmemcache_atomic_opts(lua_State *L, u_int32_t (*f)(memcached_st *st, const char *key, size_t key_len, const uint32_t offset, uint64_t *value)) { struct sipmemcache *o; const char *key; size_t keyslen; uint64_t res; int nb; o = luaL_checkudata(L, 1, "siplua.memcache"); key = luaL_checklstring(L, 2, &keyslen); nb = luaL_checkinteger(L, 3); if (o->finalized) { lua_pushnil(L); } else { if (f(&o->memc, key, keyslen, nb, &res) == MEMCACHED_SUCCESS) lua_pushinteger(L, (int)res); } return 1; } static int l_sipmemcache_incr(lua_State *L) { return sipmemcache_atomic_opts(L, memcached_increment); } static int l_sipmemcache_decr(lua_State *L) { return sipmemcache_atomic_opts(L, memcached_decrement); } static int l_sipmemcache_delete(lua_State *L) { struct sipmemcache *o; const char *key; size_t keyslen; memcached_return ret; o = luaL_checkudata(L, 1, "siplua.memcache"); key = luaL_checklstring(L, 2, &keyslen); if (o->finalized) { lua_pushnil(L); } else { ret = memcached_delete(&o->memc, (char *)key, keyslen, 0); lua_pushinteger(L, ret); } return 1; } static int l_sipmemcache_get(lua_State *L) { struct sipmemcache *o; const char *key; size_t keyslen; char *blah; size_t retlen; memcached_return_t rc; o = luaL_checkudata(L, 1, "siplua.memcache"); key = luaL_checklstring(L, 2, &keyslen); if (o->finalized) { lua_pushnil(L); } else { blah = memcached_get(&o->memc, (char *)key, keyslen, &retlen, 0, &rc); if (rc == MEMCACHED_SUCCESS && blah) { lua_pushlstring(L, blah, retlen); free(blah); } else lua_pushnil(L); } return 1; } static void sipmemcache_close(lua_State *L) { struct sipmemcache *o; o = luaL_checkudata(L, 1, "siplua.memcache"); if (!o->finalized) { if (o->keys) { pkg_free(o->keys); o->keys = NULL; } if (o->keyslen) { pkg_free(o->keyslen); o->keyslen = NULL; } memcached_quit(&o->memc); o->finalized = 1; } } int l_sipmemcache_close(lua_State *L) { sipmemcache_close(L); return 0; } int l_sipmemcache___gc(lua_State *L) { sipmemcache_close(L); return 0; } int l_sipmemcache___index(lua_State *L) { luaL_checkudata(L, 1, "siplua.memcache"); lua_getmetatable(L, 1); luaL_checkstring(L, 2); lua_pushvalue(L, 2); lua_rawget(L, -2); lua_remove(L, -2); return 1; } int l_sipmemcache_multi_get(lua_State *L) { struct sipmemcache *o; int i, n; memcached_return rc; memcached_result_st res; o = luaL_checkudata(L, 1, "siplua.memcache"); if (o->finalized) { lua_pushnil(L); return 1; } n = lua_gettop(L); lua_newtable(L); if (n < 2) return 1; o->keys = pkg_malloc((n - 1) * sizeof(char *)); o->keyslen = pkg_malloc((n - 1) * sizeof(size_t)); for (i = 0; i < n - 1; ++i) { o->keys[i] = luaL_checklstring(L, i + 2, &o->keyslen[i]); /* o->res[i]->size = 1024; */ /* o->res[i]->val = malloc(o->res[i]->size); */ /* mc_res_free_on_delete(o->res[i], 1); */ } if (memcached_mget(&o->memc, o->keys, o->keyslen, n) == MEMCACHED_SUCCESS) { for (i = 0; i < n - 1; ++i) { if (memcached_fetch_result(&o->memc, &res, &rc)) { lua_pushvalue(L, i + 2); lua_pushlstring(L, memcached_result_value(&res), memcached_result_length(&res)); lua_rawset(L, -3); } /* mc_res_free(o->req, o->res[i]); */ } } pkg_free(o->keys); o->keys = NULL; pkg_free(o->keyslen); o->keyslen = NULL; return 1; } static const struct luaL_reg siplua_memcache_mylib [] = { {"server_add", l_sipmemcache_server_add}, {"add", l_sipmemcache_add}, {"replace", l_sipmemcache_replace}, {"set", l_sipmemcache_set}, {"get", l_sipmemcache_get}, {"delete", l_sipmemcache_delete}, {"incr", l_sipmemcache_incr}, {"decr", l_sipmemcache_decr}, {"close", l_sipmemcache_close}, {"multi_get", l_sipmemcache_multi_get}, {NULL, NULL} /* sentinel */ }; void siplua_register_memcache_cclosures(lua_State *L) { luaL_newmetatable(L, "siplua.memcache"); luaL_openlib(L, NULL, siplua_memcache_mylib, 0); lua_pushstring(L, "__gc"); lua_pushcclosure(L, l_sipmemcache___gc, 0); lua_rawset(L, -3); lua_pushstring(L, "__index"); lua_pushcclosure(L, l_sipmemcache___index, 0); lua_rawset(L, -3); lua_remove(L, -1); lua_pushcclosure(L, l_sipmemcache_new, 0); lua_setglobal(L, "mc_new"); } opensips-2.2.2/modules/lua/sipmemcache.h000066400000000000000000000020071300170765700202420ustar00rootroot00000000000000/* * Copyright (c) 2007, 2008, 2009 * Eric Gouyer * Copyright (c) 2007, 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef SIPMEMCACHE_H_ # define SIPMEMCACHE_H_ void siplua_register_memcache_cclosures(lua_State *L); #endif /* !SIPMEMCACHE_H_ */ opensips-2.2.2/modules/lua/sipmysql.c000066400000000000000000000534761300170765700176600ustar00rootroot00000000000000/* * Copyright (c) 2006, 2007, 2008, 2009 * Eric Gouyer * Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "../../mem/mem.h" #include "sipluafunc.h" #define SIPMYSQL_FETCH_NUM (1 << 0) #define SIPMYSQL_FETCH_ASSOC (1 << 1) #define SIPMYSQL_FETCH_BOTH (SIPMYSQL_FETCH_NUM | SIPMYSQL_FETCH_ASSOC) struct sipmysql { int finalized; MYSQL *my; int num_fields; int num_rows; MYSQL_RES *result; MYSQL_FIELD *fields; MYSQL_ROW row; int ref; /* luaL_ref() */ }; struct sipmysql_stmt { int finalized; MYSQL_STMT *stmt; int param_count; int num_fields; MYSQL_BIND *bind; MYSQL_BIND *result; my_bool *is_null; /* param `is null' array */ unsigned long *length; /* param lengths */ unsigned long *real_length; /* result non-truncated lengths */ MYSQL_RES *meta_result; MYSQL_FIELD *fields; int result_set_exist; int num_rows; }; static int l_sipmysql_connect(lua_State *L) { struct sipmysql *o; void *ptr; const char *host, *user, *password, *db; my_bool reconnect = 1; host = luaL_checkstring(L, 1); user = luaL_checkstring(L, 2); password = luaL_checkstring(L, 3); db = luaL_checkstring(L, 4); o = lua_newuserdata(L, sizeof(*o)); memset(o, '\0', sizeof(*o)); luaL_getmetatable(L, "siplua.mysql"); lua_setmetatable(L, -2); o->ref = LUA_NOREF; mysql_library_init(0, NULL, NULL); o->my = mysql_init(NULL); mysql_options(o->my, MYSQL_OPT_RECONNECT, &reconnect); ptr = mysql_real_connect(o->my, host, user, password, db, 0, NULL, 0); if (!ptr) { lua_remove(L, -1); lua_pushnil(L); } /* printf("ptr: %p\n", ptr); */ lua_newtable(L); lua_newtable(L); lua_pushliteral(L, "__mode"); lua_pushliteral(L, "k"); lua_rawset(L, -3); lua_setmetatable(L, -2); o->ref = luaL_ref(L, LUA_REGISTRYINDEX); return 1; } static void sipmysql_free_result(struct sipmysql *o) { if (!o->finalized && o->result) { o->fields = NULL; mysql_free_result(o->result); o->result = NULL; } } static void sipmysql_close(lua_State *L) { struct sipmysql *o; o = luaL_checkudata(L, 1, "siplua.mysql"); if (!o->finalized && o->my) { if (o->ref != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, o->ref); lua_pushnil(L); while (lua_next(L, -2) != 0) { if (luaL_callmeta(L, -2, "close")) lua_pop(L, 1); lua_pop(L, 1); } lua_pop(L, 1); luaL_unref(L, LUA_REGISTRYINDEX, o->ref); o->ref = LUA_NOREF; } sipmysql_free_result(o); mysql_close(o->my); o->my = NULL; mysql_library_end(); /* could crash other opened mysql libraries? */ o->finalized = 1; } } static int l_sipmysql_close(lua_State *L) { sipmysql_close(L); return 0; } static int l_sipmysql_query(lua_State *L) { struct sipmysql *o; const char *str; int ret; o = luaL_checkudata(L, 1, "siplua.mysql"); if (o->finalized) { lua_pushnil(L); } else { sipmysql_free_result(o); str = luaL_checkstring(L, 2); ret = mysql_query(o->my, str); if (ret) { lua_pushnil(L); } else { o->result = mysql_store_result(o->my); if (o->result) { o->num_fields = mysql_num_fields(o->result); o->num_rows = mysql_num_rows(o->result); /* siplua_log(L_DBG, "mysql query return a table of %d fields and %d rows", */ /* o->num_fields, o->num_rows); */ lua_pushboolean(L, 1); } else { if (mysql_field_count(o->my) == 0) { o->num_fields = 0; o->num_rows = mysql_affected_rows(o->my); lua_pushboolean(L, 1); } else { lua_pushnil(L); } } } } return 1; } static int l_sipmysql_affected_rows(lua_State *L) { struct sipmysql *o; o = luaL_checkudata(L, 1, "siplua.mysql"); if (o->finalized) { lua_pushnil(L); } else { lua_pushinteger(L, o->num_rows); } return 1; } static int sipmysql_fetch(lua_State *L, int result_type) { struct sipmysql *o; int n; o = luaL_checkudata(L, 1, "siplua.mysql"); if (o->finalized || !o->result) { lua_pushnil(L); } else { if (result_type & SIPMYSQL_FETCH_ASSOC && !o->fields) o->fields = mysql_fetch_fields(o->result); n = lua_gettop(L); if (n < 2) lua_newtable(L); else { luaL_checktype(L, 2, LUA_TTABLE); lua_pushvalue(L, -1); } o->row = mysql_fetch_row(o->result); if (!o->row) /* we should check for a server error here */ { lua_remove(L, -1); lua_pushnil(L); } else { unsigned long *lengths; int i; lengths = mysql_fetch_lengths(o->result); for (i = 0; i < o->num_fields; ++i) { if (result_type & SIPMYSQL_FETCH_NUM) { lua_pushinteger(L, i + 1); lua_pushlstring(L, o->row[i], lengths[i]); lua_rawset(L, -3); } if (result_type & SIPMYSQL_FETCH_ASSOC) { lua_pushstring(L, o->fields[i].name); lua_pushlstring(L, o->row[i], lengths[i]); lua_rawset(L, -3); } } } } return 1; } int l_sipmysql_fetch_row(lua_State *L) { return sipmysql_fetch(L, SIPMYSQL_FETCH_NUM); } int l_sipmysql_fetch_assoc(lua_State *L) { return sipmysql_fetch(L, SIPMYSQL_FETCH_ASSOC); } int l_sipmysql_fetch_array(lua_State *L) { return sipmysql_fetch(L, SIPMYSQL_FETCH_BOTH); } int l_sipmysql_free_result(lua_State *L) { struct sipmysql *o; o = luaL_checkudata(L, 1, "siplua.mysql"); sipmysql_free_result(o); return 0; } static int l_sipmysql_escape(lua_State *L) { struct sipmysql *o; const char *str; size_t len; char *to; o = luaL_checkudata(L, 1, "siplua.mysql"); str = luaL_checklstring(L, 2, &len); to = pkg_malloc(2 * len + 1); if (!to) { siplua_log(L_CRIT, "malloc of %d bytes failed", 2 * len + 1); lua_pushnil(L); return 1; } len = mysql_real_escape_string(o->my, to, str, len); lua_pushlstring(L, to, len); pkg_free(to); return 1; } static int l_sipmysql_insert_id(lua_State *L) { struct sipmysql *o; long long id; o = luaL_checkudata(L, 1, "siplua.mysql"); id = mysql_insert_id(o->my); lua_pushinteger(L, id); return 1; } static int l_sipmysql_prepare(lua_State *L) { struct sipmysql *o; struct sipmysql_stmt *o_stmt; const char *str; size_t len; int ret; int i; /* my_bool arg; */ o = luaL_checkudata(L, 1, "siplua.mysql"); if (o->finalized) { lua_pushnil(L); } else { str = luaL_checklstring(L, 2, &len); o_stmt = lua_newuserdata(L, sizeof(*o_stmt)); memset(o_stmt, '\0', sizeof(*o_stmt)); luaL_getmetatable(L, "siplua.mysql_stmt"); lua_setmetatable(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, o->ref); lua_pushvalue(L, -2); lua_pushboolean(L, 1); lua_rawset(L, -3); lua_pop(L, 1); o_stmt->stmt = mysql_stmt_init(o->my); /* should be free'ed with mysql_stmt_close(MYSQL_STMT *) */ /* arg = 1; */ /* mysql_stmt_attr_set(MYSQL_STMT, STMT_ATTR_UPDATE_MAX_LENGTH, &arg); */ ret = mysql_stmt_prepare(o_stmt->stmt, str, len); if (ret) { lua_remove(L, -1); lua_pushnil(L); lua_pushstring(L, mysql_stmt_error(o_stmt->stmt)); return 2; } else { o_stmt->param_count = mysql_stmt_param_count(o_stmt->stmt); if (o_stmt->param_count) { o_stmt->bind = pkg_malloc(o_stmt->param_count * sizeof(MYSQL_BIND)); if (!o_stmt->bind) { siplua_log(L_CRIT, "malloc of %d bytes failed", o_stmt->param_count * sizeof(MYSQL_BIND)); lua_remove(L, -1); lua_pushnil(L); return 1; } memset(o_stmt->bind, '\0', o_stmt->param_count * sizeof(MYSQL_BIND)); o_stmt->is_null = pkg_malloc(o_stmt->param_count * sizeof(my_bool)); if (!o_stmt->is_null) { siplua_log(L_CRIT, "malloc of %d bytes failed", o_stmt->param_count * sizeof(my_bool)); lua_remove(L, -1); lua_pushnil(L); return 1; } memset(o_stmt->is_null, '\0', o_stmt->param_count * sizeof(my_bool)); o_stmt->length = pkg_malloc(o_stmt->param_count * sizeof(unsigned long)); if (!o_stmt->length) { siplua_log(L_CRIT, "malloc of %d bytes failed", o_stmt->param_count * sizeof(unsigned long)); lua_remove(L, -1); lua_pushnil(L); return 1; } memset(o_stmt->length, '\0', o_stmt->param_count * sizeof(unsigned long)); for (i = 0; i < o_stmt->param_count; ++i) { /* *(o_stmt->bind[i].is_null = &o_stmt->is_null[i]) = 1; */ o_stmt->bind[i].is_null = &o_stmt->is_null[i]; *o_stmt->bind[i].is_null = 1; o_stmt->bind[i].buffer_type = MYSQL_TYPE_NULL; o_stmt->bind[i].length = &o_stmt->length[i]; } } o_stmt->num_fields = mysql_stmt_field_count(o_stmt->stmt); if (o_stmt->num_fields) { o_stmt->result = pkg_malloc(o_stmt->num_fields * sizeof(MYSQL_BIND)); if (!o_stmt->result) { siplua_log(L_CRIT, "malloc of %d bytes failed", o_stmt->num_fields * sizeof(MYSQL_BIND)); lua_remove(L, -1); lua_pushnil(L); return 1; } memset(o_stmt->result, '\0', o_stmt->num_fields * sizeof(MYSQL_BIND)); o_stmt->real_length = pkg_malloc(o_stmt->num_fields * sizeof(unsigned long)); if (!o_stmt->real_length) { siplua_log(L_CRIT, "malloc of %d bytes failed", o_stmt->num_fields * sizeof(unsigned long)); lua_remove(L, -1); lua_pushnil(L); return 1; } memset(o_stmt->real_length, '\0', o_stmt->num_fields * sizeof(unsigned long)); for (i = 0; i < o_stmt->num_fields; ++i) o_stmt->result[i].length = &o_stmt->real_length[i]; } o_stmt->meta_result = mysql_stmt_result_metadata(o_stmt->stmt); if (o_stmt->meta_result) { o_stmt->fields = mysql_fetch_fields(o_stmt->meta_result); } } } return 1; } /* * XXX result_set_exist is set even when there is no meta_result. * http://dev.mysql.com/doc/refman/5.1/en/mysql-stmt-store-result.html * * [...] * It is unnecessary to call mysql_stmt_store_result() after executing * an SQL statement that does not produce a result set, but if you do, * it does not harm or cause any notable performance problem. * [...] * * It seems this does no harm either to call stmt_free_result(). Just useless. * * On the other hand, it's still desirable to clear o->num_rows. */ static void sipmysql_stmt_free_result(struct sipmysql_stmt *o) { int ret; if (!o->finalized && o->result_set_exist) { o->num_rows = 0; ret = mysql_stmt_free_result(o->stmt); if (ret) siplua_log(L_CRIT, "mysql_stmt_free_result failed: [%d] %s", mysql_stmt_errno(o->stmt), mysql_stmt_error(o->stmt)); o->result_set_exist = 0; } } static void sipmysql_stmt_close(lua_State *L) { struct sipmysql_stmt *o; int i; o = luaL_checkudata(L, 1, "siplua.mysql_stmt"); if (!o->finalized && o->stmt) { sipmysql_stmt_free_result(o); for (i = 0; i < o->param_count; ++i) { if (o->bind[i].buffer) { pkg_free(o->bind[i].buffer); o->bind[i].buffer = NULL; *o->bind[i].length = 0; } } for (i = 0; i < o->num_fields; ++i) { if (o->result[i].buffer) { pkg_free(o->result[i].buffer); o->result[i].buffer = NULL; o->result[i].buffer_length = 0; } } if (o->meta_result) mysql_free_result(o->meta_result); o->meta_result = NULL; if (o->real_length) { pkg_free(o->real_length); o->real_length = NULL; } if (o->result) { pkg_free(o->result); o->result = NULL; o->num_fields = 0; } if (o->length) { pkg_free(o->length); o->length = NULL; } if (o->is_null) { pkg_free(o->is_null); o->is_null = NULL; } if (o->bind) { pkg_free(o->bind); o->bind = NULL; o->param_count = 0; } mysql_stmt_close(o->stmt); o->stmt = NULL; o->finalized = 1; } } static int l_sipmysql_stmt_close(lua_State *L) { sipmysql_stmt_close(L); return 0; } /* * n : position of placeholder starting at 0 * index : position onto the Lua stack */ static int sipmysql_stmt_bind(struct sipmysql_stmt *o, lua_State *L, int n, int index) { if (!(n >= 0 && n < o->param_count)) return luaL_error(L, "invalid bind parameter #%d", n); luaL_checkany(L, index); if (!*o->bind[n].is_null) { if (o->bind[n].buffer_type != MYSQL_TYPE_NULL) { if (o->bind[n].buffer) { pkg_free(o->bind[n].buffer); o->bind[n].buffer = NULL; *o->bind[n].length = 0; } o->bind[n].buffer_type = MYSQL_TYPE_NULL; } *o->bind[n].is_null = 1; } switch (lua_type(L, index)) { case LUA_TNIL: lua_pushboolean(L, 1); break; case LUA_TNUMBER: case LUA_TBOOLEAN: { long number; number = luaL_checklong(L, index); *o->bind[n].is_null = 0; o->bind[n].buffer_type = MYSQL_TYPE_LONG; o->bind[n].buffer = pkg_malloc(sizeof(number)); if (!o->bind[n].buffer) { siplua_log(L_CRIT, "malloc of %d bytes failed", sizeof(number)); lua_pushnil(L); return 1; } memcpy(o->bind[n].buffer, &number, sizeof(number)); lua_pushboolean(L, 1); } break; case LUA_TSTRING: { const char *str; size_t len; str = luaL_checklstring(L, index, &len); *o->bind[n].is_null = 0; o->bind[n].buffer_type = MYSQL_TYPE_STRING; o->bind[n].buffer = pkg_malloc(len); if (!o->bind[n].buffer) { siplua_log(L_CRIT, "malloc of %d bytes failed", len); lua_pushnil(L); return 1; } memcpy(o->bind[n].buffer, str, len); *o->bind[n].length = len; lua_pushboolean(L, 1); } break; default: /* light & full userdata */ siplua_log(L_CRIT, "invalid bind parameter #%d, Lua type %s not yet handled", n, lua_typename(L, lua_type(L, index))); lua_pushnil(L); } return 1; } static int l_sipmysql_stmt_bind(lua_State *L) { struct sipmysql_stmt *o; int n; o = luaL_checkudata(L, 1, "siplua.mysql_stmt"); if (o->finalized || !o->bind) { lua_pushnil(L); return 1; } n = luaL_checkinteger(L, 2); return sipmysql_stmt_bind(o, L, n - 1, 3); } static int l_sipmysql_stmt_bind_all(lua_State *L) { struct sipmysql_stmt *o; int n; int i; int ret; o = luaL_checkudata(L, 1, "siplua.mysql_stmt"); if (o->finalized || !o->bind) { lua_pushnil(L); return 1; } n = lua_gettop(L) - 1; if (n != o->param_count) return luaL_error(L, "invalid number of bind parameter #%d (expected %d)", n, o->param_count); for (i = 0; i < o->param_count; ++i) { ret = sipmysql_stmt_bind(o, L, i, i + 2); if (!(lua_isboolean(L, -1) && lua_toboolean(L, -1))) return ret; lua_pop(L, ret); } lua_pushboolean(L, 1); return 1; } static int l_sipmysql_stmt_execute(lua_State *L) { struct sipmysql_stmt *o; int ret; o = luaL_checkudata(L, 1, "siplua.mysql_stmt"); if (o->finalized) { lua_pushnil(L); } else { sipmysql_stmt_free_result(o); if (o->param_count) mysql_stmt_bind_param(o->stmt, o->bind); /* try to execute it only once */ ret = mysql_stmt_execute(o->stmt); if (ret) { lua_pushnil(L); } else { /* XXX: result_set_exist might be set only if meta_result is true, see stmt_free_result */ o->result_set_exist = 1; if (o->meta_result) { ret = mysql_stmt_bind_result(o->stmt, o->result); /* try to execute it only once */ if (ret) /* failure */ { siplua_log(L_CRIT, "mysql_stmt_bind_result failed: [%d] %s", mysql_stmt_errno(o->stmt), mysql_stmt_error(o->stmt)); sipmysql_stmt_free_result(o); lua_pushnil(L); } else { ret = mysql_stmt_store_result(o->stmt); if (ret) /* failure */ { /* unlike without prepared statement, store_result will succeed even with a result set of 0 rows. We thus do not need to check mysql_field_count */ siplua_log(L_CRIT, "mysql_stmt_store_result failed: [%d] %s ", mysql_stmt_errno(o->stmt), mysql_stmt_error(o->stmt)); sipmysql_stmt_free_result(o); lua_pushnil(L); } else { o->num_rows = mysql_stmt_affected_rows(o->stmt); } } } else { /* XXX: check the API, we should maybe use mysql_stmt_num_rows() */ o->num_rows = mysql_stmt_affected_rows(o->stmt); } lua_pushboolean(L, 1); } } return 1; } static int l_sipmysql_stmt_affected_rows(lua_State *L) { struct sipmysql_stmt *o; o = luaL_checkudata(L, 1, "siplua.mysql_stmt"); if (o->finalized) { lua_pushnil(L); } else { lua_pushinteger(L, o->num_rows); } return 1; } static int sipmysql_stmt_fetch(lua_State *L, int result_type) { struct sipmysql_stmt *o; int n; int ret; int i; o = luaL_checkudata(L, 1, "siplua.mysql_stmt"); if (o->finalized || !o->meta_result || !o->result_set_exist) { lua_pushnil(L); } else { n = lua_gettop(L); if (n < 2) lua_newtable(L); else { luaL_checktype(L, 2, LUA_TTABLE); lua_pushvalue(L, -1); } ret = mysql_stmt_fetch(o->stmt); if (ret == 1) /* an error occurred */ { siplua_log(L_CRIT, "mysql_stmt_fetch failed: [%d] %s", mysql_stmt_errno(o->stmt), mysql_stmt_error(o->stmt)); lua_remove(L, -1); lua_pushnil(L); return 1; } else if (ret == MYSQL_NO_DATA) { lua_remove(L, -1); lua_pushnil(L); return 1; } else if (ret == MYSQL_DATA_TRUNCATED) { char *buf; for (i = 0; i < o->num_fields; ++i) { if (o->real_length[i] <= o->result[i].buffer_length) continue; buf = pkg_realloc(o->result[i].buffer, o->real_length[i]); if (!buf) { siplua_log(L_CRIT, "realloc of %d bytes failed", o->real_length[i]); lua_remove(L, -1); lua_pushnil(L); return 1; } o->result[i].buffer_type = MYSQL_TYPE_STRING; o->result[i].buffer = buf; o->result[i].buffer_length = *o->result[i].length; ret = mysql_stmt_fetch_column(o->stmt, &o->result[i], i, 0); if (ret) { siplua_log(L_CRIT, "mysql_stmt_fetch_column failed: [%d] %s", mysql_stmt_errno(o->stmt), mysql_stmt_error(o->stmt)); lua_remove(L, -1); lua_pushnil(L); return 1; } } ret = mysql_stmt_bind_result(o->stmt, o->result); } /* ret == 0 */ for (i = 0; i < o->num_fields; ++i) { if (result_type & SIPMYSQL_FETCH_NUM) { lua_pushinteger(L, i + 1); lua_pushlstring(L, o->result[i].buffer, o->real_length[i]); lua_rawset(L, -3); } if (result_type & SIPMYSQL_FETCH_ASSOC) { lua_pushstring(L, o->fields[i].name); lua_pushlstring(L, o->result[i].buffer, o->real_length[i]); lua_rawset(L, -3); } } } return 1; } static int l_sipmysql_stmt_fetch_row(lua_State *L) { return sipmysql_stmt_fetch(L, SIPMYSQL_FETCH_NUM); } static int l_sipmysql_stmt_fetch_assoc(lua_State *L) { return sipmysql_stmt_fetch(L, SIPMYSQL_FETCH_ASSOC); } static int l_sipmysql_stmt_fetch_array(lua_State *L) { return sipmysql_stmt_fetch(L, SIPMYSQL_FETCH_BOTH); } static int l_sipmysql_stmt_free_result(lua_State *L) { struct sipmysql_stmt *o; o = luaL_checkudata(L, 1, "siplua.mysql_stmt"); sipmysql_stmt_free_result(o); return 0; } int l_sipmysql___gc(lua_State *L) { sipmysql_close(L); return 0; } int l_sipmysql_stmt___gc(lua_State *L) { sipmysql_stmt_close(L); return 0; } int l_sipmysql___index(lua_State *L) { luaL_checkudata(L, 1, "siplua.mysql"); lua_getmetatable(L, 1); luaL_checkstring(L, 2); lua_pushvalue(L, 2); lua_rawget(L, -2); lua_remove(L, -2); return 1; } int l_sipmysql_stmt___index(lua_State *L) { luaL_checkudata(L, 1, "siplua.mysql_stmt"); lua_getmetatable(L, 1); luaL_checkstring(L, 2); lua_pushvalue(L, 2); lua_rawget(L, -2); lua_remove(L, -2); return 1; } static const struct luaL_reg siplua_mysql_mylib [] = { {"close", l_sipmysql_close}, {"query", l_sipmysql_query}, {"fetch_row", l_sipmysql_fetch_row}, {"fetch_assoc", l_sipmysql_fetch_assoc}, {"fetch_array", l_sipmysql_fetch_array}, {"free_result", l_sipmysql_free_result}, {"escape", l_sipmysql_escape}, {"insert_id", l_sipmysql_insert_id}, {"affected_rows", l_sipmysql_affected_rows}, {"prepare", l_sipmysql_prepare}, {"__gc", l_sipmysql___gc}, {"__index", l_sipmysql___index}, {NULL, NULL} /* sentinel */ }; static const struct luaL_reg siplua_mysql_stmt_mylib [] = { {"close", l_sipmysql_stmt_close}, {"bind", l_sipmysql_stmt_bind}, {"bind_all", l_sipmysql_stmt_bind_all}, {"execute", l_sipmysql_stmt_execute}, {"fetch_row", l_sipmysql_stmt_fetch_row}, {"fetch_assoc", l_sipmysql_stmt_fetch_assoc}, {"fetch_array", l_sipmysql_stmt_fetch_array}, {"free_result", l_sipmysql_stmt_free_result}, {"affected_rows", l_sipmysql_stmt_affected_rows}, {"__gc", l_sipmysql_stmt___gc}, {"__index", l_sipmysql_stmt___index}, {NULL, NULL} /* sentinel */ }; void siplua_register_mysql_cclosures(lua_State *L) { luaL_newmetatable(L, "siplua.mysql"); luaL_openlib(L, NULL, siplua_mysql_mylib, 0); lua_remove(L, -1); luaL_newmetatable(L, "siplua.mysql_stmt"); luaL_openlib(L, NULL, siplua_mysql_stmt_mylib, 0); lua_remove(L, -1); lua_pushcclosure(L, l_sipmysql_connect, 0); lua_setglobal(L, "mysql_connect"); } opensips-2.2.2/modules/lua/sipmysql.h000066400000000000000000000020071300170765700176450ustar00rootroot00000000000000/* * Copyright (c) 2006, 2007, 2008, 2009 * Eric Gouyer * Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef SIPMYSQL_H_ # define SIPMYSQL_H_ void siplua_register_mysql_cclosures(lua_State *L); #endif /* !SIPMYSQL_H_ */ opensips-2.2.2/modules/lua/sipstate.c000066400000000000000000000242451300170765700176230ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "siplua.h" #include "sipluafunc.h" #include "sipapi.h" #include "sipmysql.h" #include "sipmemcache.h" #include "sipwatch.h" #include "sipdatetime.h" #include "sipstate.h" static const luaL_Reg siplua_libs[] = { {"", luaopen_base}, {"string", luaopen_string}, {"os", luaopen_os}, {"table", luaopen_table}, {"math", luaopen_math}, {"io", luaopen_io}, {"package", luaopen_package}, {NULL, NULL} }; static const char *sipstate_filename; static int sipstate_time; static lua_State *siplua_L; static struct sipapi_object *siplua_msg; static size_t total_size; static int total_frags; static void *siplua_lua_Alloc(void *ud, void *ptr, size_t osize, size_t nsize) { char *p; total_size += nsize - osize; if (nsize == 0) { if (osize != 0 && ptr) { pkg_free(ptr); --total_frags; } return NULL; } else { if (osize == 0 || !ptr) { p = pkg_malloc(nsize); ++total_frags; } else { p = pkg_realloc(ptr, nsize); } if (!p) LM_ERR("cannot allocate pkg memory\n"); return p; } } static void *siplua_lua_Alloc2(void *ud, void *ptr, size_t osize, size_t nsize) { (void)ud; (void)osize; /* not used */ total_size += nsize - osize; if (nsize == 0) { if (ptr) --total_frags; } else { if (!ptr) ++total_frags; } if (nsize == 0) { free(ptr); return NULL; } else return realloc(ptr, nsize); } static int l_sipstate_xlog(lua_State *L) { const char *level; const char *str; int lev = L_ERR; size_t len; int n; n = lua_gettop(L); if (n < 2) str = luaL_checklstring(L, 1, &len); else { level = luaL_checkstring(L, 1); if (strlen(level) < 3) return luaL_error(L, "wrong log level %s", level); switch (level[2]) { case 'A': lev = L_ALERT; break; case 'C': lev = L_CRIT; break; case 'E': lev = L_ERR; break; case 'W': lev = L_WARN; break; case 'N': lev = L_NOTICE; break; case 'I': lev = L_INFO; break; case 'D': lev = L_DBG; break; default: return luaL_error(L, "unknown log level %s", level); } str = luaL_checklstring(L, 2, &len); } siplua_log(lev, "%.*s", (int)len, str); return 0; } static int l_sipstate_xdbg(lua_State *L) { const char *str; size_t len; str = luaL_checklstring(L, 1, &len); siplua_log(L_DBG, "%.*s", (int)len, str); return 0; } /* I was tired of not seing output when i wrongly used print() instead of xlog() */ static int l_sipstate_print(lua_State *L) { const char *str; size_t len; int top; int i; top = lua_gettop(L); for (i = 0; i < top; ++i) { str = luaL_checklstring(L, i + 1, &len); siplua_log(L_ALERT, "%.*s", (int)len, str); } return 0; } static int l_sipstate_notice(lua_State *L) { int nargs; int local = 0; const char *str; size_t len; nargs = lua_gettop(L); if (!(nargs >= 1 && nargs <= 2)) return luaL_error(L, "wrong number of arguments ([local], str)"); if (nargs >= 2) local = luaL_checkinteger(L, 1); str = luaL_checklstring(L, nargs, &len); siplua_notice(local, "%.*s", (int)len, str); return 0; } static int l_sipstate_setUserDebug(lua_State *L) { int n; n = luaL_checkinteger(L, 1); lua_user_debug = n; return 0; } static int l_sipstate_WarnMissingFreeFixup(lua_State *L) { int n; n = luaL_checkinteger(L, 1); warn_missing_free_fixup = n; return 0; } static int l_sipstate_getpid(lua_State *L) { int pid; pid = getpid(); lua_pushinteger(L, pid); return 1; } static int l_sipstate_getmem(lua_State *L) { lua_newtable(L); lua_pushstring(L, "total_size"); lua_pushinteger(L, total_size); lua_rawset(L, -3); lua_pushstring(L, "total_frags"); lua_pushinteger(L, total_frags); lua_rawset(L, -3); return 1; } static int sipstate_getmeminfo(lua_State *L, struct mem_info *info) { lua_newtable(L); lua_pushstring(L, "total_size"); lua_pushinteger(L, info->total_size); lua_rawset(L, -3); lua_pushstring(L, "free"); lua_pushinteger(L, info->free); lua_rawset(L, -3); lua_pushstring(L, "used"); lua_pushinteger(L, info->used); lua_rawset(L, -3); lua_pushstring(L, "real_used"); lua_pushinteger(L, info->real_used); lua_rawset(L, -3); lua_pushstring(L, "max_used"); lua_pushinteger(L, info->max_used); lua_rawset(L, -3); lua_pushstring(L, "min_frag"); lua_pushinteger(L, info->min_frag); lua_rawset(L, -3); lua_pushstring(L, "total_frags"); lua_pushinteger(L, info->total_frags); lua_rawset(L, -3); return 1; } static int l_sipstate_getpkginfo(lua_State *L) { struct mem_info info; MY_MEMINFO(mem_block, &info); return sipstate_getmeminfo(L, &info); } static int l_sipstate_getshminfo(lua_State *L) { struct mem_info info; shm_info(&info); return sipstate_getmeminfo(L, &info); } static int l_sipstate_gethostname(lua_State *L) { char name[MAXHOSTNAMELEN]; int ret; ret = gethostname(name, sizeof(name)); if (!ret) lua_pushstring(L, name); else lua_pushnil(L); return 1; } static int l_sipstate_filemtime(lua_State *L) { const char *str; struct stat sb; int ret; str = luaL_checkstring(L, 1); ret = stat(str, &sb); if (!ret) lua_pushinteger(L, sb.st_mtime); else lua_pushnil(L); return 1; } static int l_sipstate_setCoreDebug(lua_State *L) { int n; n = luaL_checkinteger(L, 1); set_proc_log_level(n); return 0; } static const struct luaL_reg siplua_state_mylib [] = { {"xlog", l_sipstate_xlog}, {"xdbg", l_sipstate_xdbg}, {"print", l_sipstate_print}, {"notice", l_sipstate_notice}, {"setUserDebug", l_sipstate_setUserDebug}, {"WarnMissingFreeFixup", l_sipstate_WarnMissingFreeFixup}, {"getpid", l_sipstate_getpid}, {"getmem", l_sipstate_getmem}, {"getmeminfo", l_sipstate_getpkginfo}, {"getpkginfo", l_sipstate_getpkginfo}, {"getshminfo", l_sipstate_getshminfo}, {"gethostname", l_sipstate_gethostname}, {"filemtime", l_sipstate_filemtime}, {"setCoreDebug", l_sipstate_setCoreDebug}, {NULL, NULL} /* sentinel */ }; static void siplua_register_state_cclosures(lua_State *L) { lua_pushvalue(L, LUA_GLOBALSINDEX); luaL_openlib(L, NULL, siplua_state_mylib, 0); lua_remove(L, -1); } int sipstate_open(char *allocator) { lua_State *L; const luaL_Reg *lib = siplua_libs; const char *errmsg; if (!strcmp(allocator, "opensips")) L = lua_newstate(siplua_lua_Alloc, NULL); else if (!strcmp(allocator, "malloc")) L = lua_newstate(siplua_lua_Alloc2, NULL); else { siplua_log(L_ERR, "Unknown Lua memory allocator"); return -1; } if (!(siplua_L = L)) { siplua_log(L_ERR, "Failed to open Lua state"); return -1; } else siplua_log(L_DBG, "Lua state opened"); for (; lib->func; lib++) { lua_pushcfunction(L, lib->func); lua_pushstring(L, lib->name); if (lua_pcall(L, 1, 0, 0)) { errmsg = lua_tostring(L, -1); siplua_log(L_ERR, "Error loading library `%s': %s", lib->name, errmsg); lua_remove(L, -1); sipstate_close(); return -1; } } siplua_register_state_cclosures(L); siplua_register_api_cclosures(L); siplua_register_mysql_cclosures(L); siplua_register_memcache_cclosures(L); siplua_register_watch_cclosures(L); siplua_register_datetime_cclosures(L); siplua_msg = sipapi_create_object(L); return 0; } void sipstate_close(void) { sipapi_delete_object(siplua_msg); lua_close(siplua_L); siplua_L = 0; } int sipstate_load(const char *filename) { lua_State *L = siplua_L; struct stat sb; int ret; const char *errmsg; if (!filename) filename = sipstate_filename; if (!filename) { siplua_log(L_ERR, "siplua Lua filename is NULL"); return -1; } ret = stat(filename, &sb); if (!ret && sipstate_filename && sb.st_mtime == sipstate_time) return 0; if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) { errmsg = lua_tostring(L, -1); siplua_log(L_ERR, "siplua error loading file %s: %s", filename, errmsg); lua_remove(L, -1); return -1; } else { siplua_log(L_INFO, "siplua file %s successfully reloaded", filename); sipstate_filename = filename; sipstate_time = sb.st_mtime; return 0; } } int sipstate_call(struct sip_msg *msg, const char *fnc, const char *mystr) { lua_State *L = siplua_L; int ref; const char *errmsg; int n; if (lua_auto_reload) sipstate_load(NULL); if (!fnc) return -1; lua_getglobal(L, fnc); if (lua_isnil(L, -1)) { siplua_log(L_ERR, "siplua Lua function %s is nil", fnc); lua_remove(L, -1); return -1; } sipapi_set_object(siplua_msg, msg); ref = sipapi_get_object_ref(siplua_msg); lua_rawgeti(L, LUA_REGISTRYINDEX, ref); if (mystr) lua_pushstring(L, mystr); if (lua_pcall(siplua_L, (mystr ? 2 : 1), 1, 0)) { errmsg = lua_tostring(L, -1); siplua_log(L_ERR, "siplua error running function %s: %s", fnc, errmsg); lua_remove(L, -1); n = -1; } else { n = lua_tointeger(L, -1); lua_remove(L, -1); /* siplua_log(L_DBG , "siplua Lua function %s returned %d\n", fnc, n); */ } return n; } opensips-2.2.2/modules/lua/sipstate.h000066400000000000000000000022001300170765700176130ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009 * Eric Gouyer * Copyright (c) 2008, 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct sip_msg; #ifndef SIPSTATE_H_ # define SIPSTATE_H_ int sipstate_open(char *allocator); void sipstate_close(void); int sipstate_load(const char *filename); int sipstate_call(struct sip_msg *msg, const char *fnc, const char *mystr); #endif /* !SIPSTATE_H_ */ opensips-2.2.2/modules/lua/sipwatch.c000066400000000000000000000100301300170765700175740ustar00rootroot00000000000000/* * Copyright (c) 2009 * Eric Gouyer * Copyright (c) 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "../../mem/shm_mem.h" #include "../../locking.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "crc32.h" #include "sipapi.h" #include "sipwatch.h" struct siplua_watch *siplua_watch; int sipwatch_create_object(void) { siplua_watch = shm_malloc(sizeof(*siplua_watch)); if (!siplua_watch) return -1; memset(siplua_watch, '\0', sizeof(*siplua_watch)); if (!lock_init(&siplua_watch->lock)) return -1; return 0; } void sipwatch_lock(void) { lock_get(&siplua_watch->lock); } void sipwatch_unlock(void) { lock_release(&siplua_watch->lock); } /* XXX: realloc() is not checked! */ void sipwatch_add(const char *str, int len) { char *ext; lock_get(&siplua_watch->lock); ext = shm_malloc(len + 1); if (ext) { memcpy(ext, str, len); ext[len] = '\0'; siplua_watch->ext = shm_realloc(siplua_watch->ext, (siplua_watch->nb + 1) * sizeof(struct siplua_watch_ext)); siplua_watch->ext[siplua_watch->nb].str = ext; siplua_watch->ext[siplua_watch->nb].crc = ssh_crc32((unsigned char *)str, len); ++siplua_watch->nb; } lock_release(&siplua_watch->lock); } void sipwatch_delete(const char *str, int len) { int i; u_int32_t crc; crc = ssh_crc32((unsigned char *)str, len); lock_get(&siplua_watch->lock); for (i = 0; i < siplua_watch->nb; ++i) { if (siplua_watch->ext[i].crc == crc) { memmove(&siplua_watch->ext[i], &siplua_watch->ext[i + 1], siplua_watch->nb - i - 1); siplua_watch->ext = shm_realloc(siplua_watch->ext, (siplua_watch->nb - 1) * sizeof(struct siplua_watch_ext)); --siplua_watch->nb; --i; } } lock_release(&siplua_watch->lock); } static int sipwatch_getFlagFromExtension(const char *str, int len) { int i; u_int32_t crc; int flag = 0; crc = ssh_crc32((unsigned char *)str, len); lock_get(&siplua_watch->lock); for (i = 0; i < siplua_watch->nb; ++i) { if (siplua_watch->ext[i].crc == crc) { flag = 1; break; } } lock_release(&siplua_watch->lock); return flag; } static int l_sipwatch_getFlag(lua_State *L) { struct sipapi_object *o; struct sip_uri *myuri; o = luaL_checkudata(L, 1, "siplua.api"); myuri = parse_from_uri(o->msg); if (myuri) { if (sipwatch_getFlagFromExtension(myuri->user.s, myuri->user.len)) { lua_pushboolean(L, 1); return 1; } } myuri = parse_to_uri(o->msg); if (myuri) { if (sipwatch_getFlagFromExtension(myuri->user.s, myuri->user.len)) { lua_pushboolean(L, 1); return 1; } } lua_pushnil(L); return 1; } static int l_sipwatch_getFlagFromExtension(lua_State *L) { const char *str; size_t len; str = luaL_checklstring(L, 1, &len); lua_pushboolean(L, sipwatch_getFlagFromExtension(str, len)); return 1; } static const struct luaL_reg siplua_watch_mylib [] = { {"watch_getFlag", l_sipwatch_getFlag}, {"watch_getFlagFromExtension", l_sipwatch_getFlagFromExtension}, {NULL, NULL} /* sentinel */ }; void siplua_register_watch_cclosures(lua_State *L) { lua_pushvalue(L, LUA_GLOBALSINDEX); luaL_openlib(L, NULL, siplua_watch_mylib, 0); lua_remove(L, -1); } opensips-2.2.2/modules/lua/sipwatch.h000066400000000000000000000026621300170765700176150ustar00rootroot00000000000000/* * Copyright (c) 2009 * Eric Gouyer * Copyright (c) 2009, 2010, 2011 * Arnaud Chong * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "../../locking.h" #ifndef SIPWATCH_H_ # define SIPWATCH_H_ struct siplua_watch_ext; struct siplua_watch { gen_lock_t lock; struct siplua_watch_ext *ext; int nb; }; struct siplua_watch_ext { char *str; u_int32_t crc; }; extern struct siplua_watch *siplua_watch; int sipwatch_create_object(void); void siplua_register_watch_cclosures(lua_State *L); void sipwatch_lock(void); void sipwatch_unlock(void); void sipwatch_add(const char *s, int len); void sipwatch_delete(const char *s, int len); #endif /* !SIPWATCH_H_ */ opensips-2.2.2/modules/mangler/000077500000000000000000000000001300170765700164605ustar00rootroot00000000000000opensips-2.2.2/modules/mangler/Makefile000066400000000000000000000003441300170765700201210ustar00rootroot00000000000000# Makefile v 1.0 2002/12/27 # # Enum module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=mangler.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/mangler/README000066400000000000000000000126011300170765700173400ustar00rootroot00000000000000mangler Module Gabriel Vasile FhG FOKUS Edited by Gabriel Vasile Copyright © 2003 Fill in here Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. contact_flds_separator (string) 1.4. Exported Functions 1.4.1. sdp_mangle_ip(pattern, newip) 1.4.2. sdp_mangle_port(offset) 1.4.3. encode_contact(encoding_prefix, public_ip) 1.4.4. decode_contact() 1.4.5. decode_contact_header() List of Examples 1.1. Set db_url parameter 1.2. sdp_mangle_ip usage 1.3. sdp_mangle_port usage 1.4. encode_contact usage 1.5. decode_contact usage 1.6. decode_contact_header usage Chapter 1. Admin Guide 1.1. Overview This is a module to help with SDP mangling. Note: This module is obselete and will be removed for the 1.5.0 release. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. contact_flds_separator (string) First char of this parameter is used as separator for encoding/decoding Contact header. Warning First char of this field must be set to a value which is not used inside username,password or other fields of contact. Otherwise it is possible for the decoding step to fail/produce wrong results. Default value is “*â€. Example 1.1. Set db_url parameter ... modparam("mangler", "contact_flds_separator", "-") ... then an encoded uri might look sip:user-password-ip-port-protocol@PublicIP 1.4. Exported Functions 1.4.1. sdp_mangle_ip(pattern, newip) Changes IP addresses inside SDP package in lines describing connections like c=IN IP4 Currently in only changes IP4 addresses since IP6 probably will not need to traverse NAT :) The function returns negative on error, or number of replacements + 1. Meaning of the parameters is as follows: * pattern - A pair ip/mask used to match IP's located inside SDP package in lines c=IN IP4 ip. This lines will only be mangled if located IP is in the network described by this pattern. Examples of valid patterns are “10.0.0.0/255.0.0.0†or “10.0.0.0/8†etc. * newip - A string representing the new IP to be put inside SDP package if old IP address matches pattern. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. Example 1.2. sdp_mangle_ip usage ... sdp_mangle_ip("10.0.0.0/8","193.175.135.38"); ... 1.4.2. sdp_mangle_port(offset) Changes ports inside SDP package in lines describing media like m=audio 13451. The function returns negative on error, or number of replacements + 1. Meaning of the parameters is as follows: * offset - A string representing an integer which will be added/subtracted from the located port. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. Example 1.3. sdp_mangle_port usage ... sdp_mangle_port("-12000"); ... 1.4.3. encode_contact(encoding_prefix, public_ip) This function will encode uri-s inside Contact header in the following manner sip:username:password@ip:port;transport=protocol goes sip:enc_pref*username*ip*port*protocol@public_ip * is the default separator. The function returns negative on error, 1 on success. Meaning of the parameters is as follows: * encoding_prefix - Something to allow us to determine that a contact is encoded publicip--a routable IP, most probably you should put your external IP of your NAT box. public_ip - The public IP which will be used in the encoded contact, as described by the example above. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. Example 1.4. encode_contact usage ... if (src_ip == 10.0.0.0/8) encode_contact("enc_prefix","193.175.135.38"); ... 1.4.4. decode_contact() This function will decode the URI in first line in packets which come with encoded URI in the following manner sip:enc_pref*username*ip*port*protocol@public_ip goes to sip:username:password@ip:port;transport=protocol It uses the default set parameter for contact encoding separator. The function returns negative on error, 1 on success. Meaning of the parameters is as follows: This function can be used from REQUEST_ROUTE. Example 1.5. decode_contact usage ... if (uri =~ "^enc*") { decode_contact(); } ... 1.4.5. decode_contact_header() This function will decode URIs inside Contact header in the following manner sip:enc_pref*username*ip*port*protocol@public_ip goes to sip:username:password@ip:port;transport=protocol. It uses the default set parameter for contact encoding separator. The function returns negative on error, 1 on success. Meaning of the parameters is as follows: This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. Example 1.6. decode_contact_header usage ... if (uri =~ "^enc*") { decode_contact_header(); } ... opensips-2.2.2/modules/mangler/common.h000066400000000000000000000017631300170765700201300ustar00rootroot00000000000000/* * mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ #ifndef COMMON_H #define COMMON_H #define STRICT_CHECK 1 //#define DEBUG 1 //#define DEMO 1 /* for mangler.c */ #endif opensips-2.2.2/modules/mangler/contact_ops.c000066400000000000000000000416271300170765700211520ustar00rootroot00000000000000/* * mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ #include "contact_ops.h" #include "utils.h" #include "common.h" #include "../../mem/mem.h" #include "../../data_lump.h" #include "../../parser/hf.h" #include "../../parser/parse_uri.h" #include "../../parser/contact/parse_contact.h" #include "../../ut.h" #include #include //#define DEBUG int encode_contact (struct sip_msg *msg, char *encoding_prefix,char *public_ip) { contact_body_t *cb; contact_t *c; str uri; str newUri; int res; char separator; /* * I have a list of contacts in contact->parsed which is of type * contact_body_t inside i have a contact->parsed->contact which is the * head of the list of contacts inside it is a * str uri; * struct contact *next; * I just have to visit each uri and encode each uri according to a scheme */ if ((msg->contact == NULL)&&((parse_headers(msg,HDR_CONTACT_F,0) == -1) || (msg->contact == NULL) )) { LM_ERR("no Contact header present\n"); return -1; } separator = DEFAULT_SEPARATOR[0]; if (contact_flds_separator != NULL) if (strlen(contact_flds_separator)>=1) separator = contact_flds_separator[0]; if (msg->contact->parsed == NULL) parse_contact (msg->contact); if (msg->contact->parsed != NULL) { cb = (contact_body_t *) msg->contact->parsed; c = cb->contacts; /* we visit each contact */ if (c != NULL) { uri = c->uri; if ((uri.s < msg->buf) || (uri.s > (msg->buf + msg->len))) { LM_ERR("you can't encode a contact that was aleady " "changed !!!\n"); return -1; } res = encode_uri(uri, encoding_prefix,public_ip,separator,&newUri); if (res != 0) { LM_ERR("failed encoding contact.Code %d\n", res); #ifdef STRICT_CHECK return res; #endif } else if (patch (msg, uri.s, uri.len, newUri.s, newUri.len) < 0) { LM_ERR("lumping failed in mangling port \n"); return -2; } /* encoding next contacts too?*/ #ifdef ENCODE_ALL_CONTACTS while (c->next != NULL) { c = c->next; uri = c->uri; res = encode_uri (uri, encoding_prefix,public_ip,separator,&newUri); if (res != 0) { LM_ERR("failed encode_uri.Code %d\n",res); #ifdef STRICT_CHECK return res; #endif } else if (patch (msg, uri.s, uri.len, newUri.s, newUri.len)< 0) { LM_ERR("lumping failed in mangling port \n"); return -3; } } /* while */ #endif /* ENCODE_ALL_CONTACTS */ } /* if c != NULL */ } /* end if */ else /* after parsing still NULL */ { LM_ERR("unable to parse Contact header\n"); return -4; } return 1; } int decode_contact (struct sip_msg *msg,char *unused1,char *unused2) { str uri; str newUri; char separator; int res; uri.s = 0; #ifdef DEBUG fprintf (stdout,"---START--------DECODE CONTACT-----------------\n"); fprintf (stdout,"%.*s\n",50,msg->buf); fprintf (stdout, "INITIAL.s=[%.*s]\n", uri.len, uri.s); #endif separator = DEFAULT_SEPARATOR[0]; if (contact_flds_separator != NULL) if (strlen(contact_flds_separator)>=1) separator = contact_flds_separator[0]; if ((msg->new_uri.s == NULL) || (msg->new_uri.len == 0)) { uri = msg->first_line.u.request.uri; if (uri.s == NULL) return -1; } res = decode_uri (uri, separator, &newUri); #ifdef DEBUG if (res == 0) fprintf (stdout, "newuri.s=[%.*s]\n", newUri.len, newUri.s); #endif if (res != 0) { LM_ERR("failed decoding contact.Code %d\n", res); #ifdef STRICT_CHECK return res; #endif } else /* we do not modify the original first line */ if ((msg->new_uri.s == NULL) || (msg->new_uri.len == 0)) msg->new_uri = newUri; else { pkg_free(msg->new_uri.s); msg->new_uri = newUri; } /* if (patch (msg, uri.s, uri.len, newUri.s, newUri.len) < 0) { LM_ERR("lumping failed in mangling port \n"); return -2; } */ return 1; } int decode_contact_header (struct sip_msg *msg,char *unused1,char *unused2) { contact_body_t *cb; contact_t *c; str uri; str newUri; char separator; int res; #ifdef DEBUG fprintf (stdout,"---START--------DECODE CONTACT HEADER-----------------\n"); #endif if ((msg->contact == NULL)&&((parse_headers(msg,HDR_CONTACT_F,0) == -1) || (msg->contact== NULL) )) { LM_ERR("no Contact header present\n"); return -1; } separator = DEFAULT_SEPARATOR[0]; if (contact_flds_separator != NULL) if (strlen(contact_flds_separator)>=1) separator = contact_flds_separator[0]; #ifdef DEBUG fprintf (stdout,"Using separator %c\n",separator); str* ruri; ruri = GET_RURI(msg); fprintf (stdout,"[len = %d]New uri is->%*.s\n",ruri->len,ruri->len,ruri->s); ruri = &msg->first_line.u.request.uri; fprintf (stdout, "INITIAL.s=[%.*s]\n", ruri->len, ruri->s); #endif if (msg->contact->parsed == NULL) parse_contact (msg->contact); if (msg->contact->parsed != NULL) { cb = (contact_body_t *) msg->contact->parsed; c = cb->contacts; // we visit each contact if (c != NULL) { uri = c->uri; res = decode_uri (uri, separator, &newUri); #ifdef DEBUG fprintf (stdout, "newuri.s=[%.*s]\n", newUri.len, newUri.s); #endif if (res != 0) { LM_ERR("failed decoding contact.Code %d\n", res); #ifdef STRICT_CHECK return res; #endif } else if (patch (msg, uri.s, uri.len, newUri.s, newUri.len) < 0) { LM_ERR("lumping failed in mangling port \n"); return -2; } #ifdef DECODE_ALL_CONTACTS while (c->next != NULL) { c = c->next; uri = c->uri; res = decode_uri (uri, separator, &newUri); if (res != 0) { LM_ERR("failed decoding contact.Code %d\n",res); #ifdef STRICT_CHECK return res; #endif } else if (patch (msg, uri.s, uri.len, newUri.s, newUri.len) < 0) { LM_ERR("lumping failed in mangling port \n"); return -3; } } // end while #endif } // if c!= NULL } // end if else // after parsing still NULL { LM_ERR("unable to parse Contact header\n"); return -4; } #ifdef DEBUG fprintf (stdout,"---END--------DECODE CONTACT HEADER-----------------\n");fflush(stdout); #endif return 1; } int encode2format (str uri, struct uri_format *format) { int foo; char *string, *pos, *start, *end; struct sip_uri sipUri; if (uri.s == NULL) return -1; string = uri.s; pos = q_memchr (string, '<', uri.len); if (pos != NULL) /* we are only interested of chars inside <> */ { start = q_memchr (string, ':', uri.len); if (start == NULL) return -2; if (start - pos < 4) return -3; start = start - 3; end = strchr (start, '>'); if (end == NULL) return -4; /* must be a match to < */ } else /* we do not have <> */ { start = q_memchr (string, ':', uri.len); if (start == NULL) return -5; if (start - pos < 3) return -6; start = start - 3; end = string + uri.len; } memset(format,0,sizeof(struct uri_format)); format->first = start - string + 4; /*sip: */ format->second = end - string; /* --------------------------testing ------------------------------- */ /* sip:gva@pass@10.0.0.1;;transport=udp>;expires=2 INCORECT BEHAVIOR OF parse_uri,myfunction works good */ foo = parse_uri (start, end - start, &sipUri); if (foo != 0) { LM_ERR("parse_uri failed on [%.*s].Code %d \n",uri.len,uri.s,foo); #ifdef DEBUG fprintf (stdout, "PARSING uri with parse uri not ok %d\n", foo); #endif return foo-10; } format->username = sipUri.user; format->password = sipUri.passwd; format->ip = sipUri.host; format->port = sipUri.port; format->protocol = sipUri.transport_val; #ifdef DEBUG fprintf (stdout, "transport=[%.*s] transportval=[%.*s]\n", sipUri.transport.len,sipUri.transport.s,sipUri.transport_val.len,sipUri.transport_val.s); fprintf(stdout,"First %d,second %d\n",format->first,format->second); #endif return 0; } int encode_uri (str uri, char *encoding_prefix, char *public_ip,char separator, str * result) { struct uri_format format; char *pos; int foo,res; result->s = NULL; result->len = 0; if (uri.len <= 1) return -1; /* no contact or an invalid one */ if (public_ip == NULL) { LM_ERR("invalid NULL value for public_ip parameter\n"); return -2; } #ifdef DEBUG fprintf (stdout, "Primit cerere de encodare a [%.*s] cu %s-%s\n", uri.len,uri.s, encoding_prefix, public_ip); #endif fflush (stdout); foo = encode2format (uri, &format); if (foo < 0) { LM_ERR("unable to encode Contact URI [%.*s].Return code %d\n",uri.len,uri.s,foo); return foo - 20; } #ifdef DEBUG fprintf(stdout,"user=%.*s ip=%.*s port=%.*s protocol=%.*s\n",format.username.len,format.username.s,format.ip.len,format.ip.s, format.port.len,format.port.s,format.protocol.len,format.protocol.s); #endif /* a complete uri would be sip:username@ip:port;transport=protocol goes to * sip:enc_pref*username*ip*port*protocol@public_ip */ foo = 1; /*strlen(separator); */ result->len = format.first + uri.len - format.second + //ar trebui sa sterg 1 strlen (encoding_prefix) + foo + format.username.len + foo + format.password.len + foo + format.ip.len + foo + format.port.len + foo + format.protocol.len + 1 + strlen (public_ip); /* adding one comes from @ */ result->s = pkg_malloc (result->len); pos = result->s; if (pos == NULL) { #ifdef DEBUG fprintf (stdout, "Unable to alloc result [%d] end=%d\n",result->len, format.second); #endif LM_ERR("unable to alloc pkg memory\n"); return -3; } #ifdef DEBUG fprintf (stdout, "[pass=%d][Allocated %d bytes][first=%d][lengthsec=%d]\nAdding [%d] ->%.*s\n",format.password.len,result->len,format.first,uri.len-format.second,format.first, format.first,uri.s);fflush (stdout); #endif res = snprintf(pos,result->len,"%.*s%s%c%.*s%c%.*s%c%.*s%c%.*s%c%.*s@",format.first,uri.s,encoding_prefix,separator, format.username.len,format.username.s,separator,format.password.len,format.password.s, separator,format.ip.len,format.ip.s,separator,format.port.len,format.port.s,separator,format.protocol.len,format.protocol.s); if ((res < 0 )||(res>result->len)) { LM_ERR("unable to construct new uri.\n"); if (result->s != NULL) pkg_free(result->s); return -4; } #ifdef DEBUG fprintf(stdout,"res= %d\npos=%s\n",res,pos); #endif pos = pos + res ;/* overwriting the \0 from snprintf */ memcpy (pos, public_ip, strlen (public_ip)); pos = pos + strlen (public_ip); memcpy (pos, uri.s + format.second, uri.len - format.second); #ifdef DEBUG fprintf (stdout, "Adding2 [%d] ->%.*s\n", uri.len - format.second,uri.len - format.second, uri.s + format.second); fprintf (stdout, "NEW NEW uri is->[%.*s]\n", result->len, result->s); #endif /* Because called parse_uri format contains pointers to the inside of msg,must not deallocate */ return 0; } int decode2format (str uri, char separator, struct uri_format *format) { char *start, *end, *pos,*lastpos; str tmp; enum {EX_PREFIX=0,EX_USER,EX_PASS,EX_IP,EX_PORT,EX_PROT,EX_FINAL} state; //memset (format, 0, sizeof ((struct uri_format))); if (uri.s == NULL) { LM_ERR("invalid parameter uri.It is NULL\n"); return -1; } /* sip:enc_pref*username*password*ip*port*protocol@public_ip */ start = q_memchr (uri.s, ':', uri.len); if (start == NULL) { LM_ERR("invalid SIP uri.Missing :\n"); return -2; } /* invalid uri */ start = start + 1; /* jumping over sip: ATENTIE LA BUFFER OVERFLOW DACA E DOAR sip: */ format->first = start - uri.s; /* start */ end = q_memchr(start,'@',uri.len-(start-uri.s)); if (end == NULL) { LM_ERR("invalid SIP uri.Missing @\n"); return -3;/* no host address found */ } #ifdef DEBUG fprintf (stdout, "Decoding %.*s\n",end-start,start); #endif state = EX_PREFIX; lastpos = start; for (pos = start;pos0) tmp.s = lastpos; else tmp.s = NULL; switch (state) { case EX_PREFIX: state = EX_USER;break; case EX_USER:format->username = tmp;state = EX_PASS;break; case EX_PASS:format->password = tmp;state = EX_IP;break; case EX_IP:format->ip = tmp;state = EX_PORT;break; case EX_PORT:format->port = tmp;state = EX_PROT;break; default: { /* this should not happen, we should find @ not separator */ return -4; break; } } lastpos = pos+1; } else if (((*pos) == '>')||(*pos == ';')) { /* invalid chars inside username part */ return -5; } } /* we must be in state EX_PROT and protocol is between lastpos and end@ */ if (state != EX_PROT) return -6; format->protocol.len = end - lastpos; if (format->protocol.len>0) format->protocol.s = lastpos; else format->protocol.s = NULL; /* I should check perhaps that after @ there is something */ #ifdef DEBUG fprintf (stdout, "username=%.*s\n", format->username.len,format->username.s); fprintf (stdout, "password=%.*s\n", format->password.len,format->password.s); fprintf (stdout, "ip=%.*s\n", format->ip.len, format->ip.s); fprintf (stdout, "port=%.*s\n", format->port.len,format->port.s); fprintf (stdout, "protocol=%.*s\n", format->protocol.len,format->protocol.s); #endif /* looking for the end of public ip */ start = end;/*we are now at @ */ for(pos = start;pos')) { /* found end */ format->second = pos - uri.s; return 0; } } /* if we are here we did not find > or ; */ format->second = uri.len; return 0; } int decode_uri (str uri, char separator, str * result) { char *pos; struct uri_format format; int foo; result->s = NULL; result->len = 0; if ((uri.len <= 0) || (uri.s == NULL)) { LM_ERR("invalid value for uri\n"); return -1; } foo = decode2format (uri, separator, &format); if (foo < 0) { LM_ERR("failed to decode Contact uri .Error code %d\n",foo); return foo - 20; } /* sanity check */ if (format.ip.len <= 0) { LM_ERR("unable to decode host address \n"); return -2;/* should I quit or ignore ? */ } if ((format.password.len > 0) && (format.username.len <= 0)) { LM_ERR("password decoded but no username available\n"); return -3; } /* a complete uri would be sip:username:password@ip:port;transport=protocol goes to * sip:enc_pref#username#password#ip#port#protocol@public_ip */ result->len = format.first + (uri.len - format.second); /* not NULL terminated */ if (format.username.len > 0) result->len += format.username.len + 1; //: or @ if (format.password.len > 0) result->len += format.password.len + 1; //@ /* if (format.ip.len > 0) */ result->len += format.ip.len; if (format.port.len > 0) result->len += 1 + format.port.len; //: if (format.protocol.len > 0) result->len += 1 + 10 + format.protocol.len; //;transport= #ifdef DEBUG fprintf (stdout, "Result size is %d.Original Uri size is %d\n",result->len, uri.len); #endif /* adding one comes from * */ result->s = pkg_malloc (result->len); if (result->s == NULL) { LM_ERR("unable to allocate pkg memory\n"); return -4; } pos = result->s; #ifdef DEBUG fprintf (stdout, "Adding [%d] ->%.*s\n", format.first, format.first,uri.s);fflush (stdout); #endif memcpy (pos, uri.s, format.first); /* till sip: */ pos = pos + format.first; if (format.username.len > 0) { memcpy (pos, format.username.s, format.username.len); pos = pos + format.username.len; if (format.password.len > 0) memcpy (pos, ":", 1); else memcpy (pos, "@", 1); pos = pos + 1; } if (format.password.len > 0) { memcpy (pos, format.password.s, format.password.len); pos = pos + format.password.len; memcpy (pos, "@", 1); pos = pos + 1; } /* if (format.ip.len > 0) */ memcpy (pos, format.ip.s, format.ip.len); pos = pos + format.ip.len; if (format.port.len > 0) { memcpy (pos, ":", 1); pos = pos + 1; memcpy (pos, format.port.s, format.port.len); pos = pos + format.port.len; } if (format.protocol.len > 0) { memcpy (pos, ";transport=", 11); pos = pos + 11; memcpy (pos, format.protocol.s, format.protocol.len); pos = pos + format.protocol.len; } #ifdef DEBUG fprintf (stdout, "Adding2 [%d] ->%.*s\n", uri.len - format.second,uri.len - format.second, uri.s + format.second);fflush (stdout); #endif memcpy (pos, uri.s + format.second, uri.len - format.second); /* till end: */ #ifdef DEBUG fprintf (stdout, "New decoded uri is->[%.*s]\n", result->len,result->s); #endif return 0; } opensips-2.2.2/modules/mangler/contact_ops.h000066400000000000000000000036151300170765700211520ustar00rootroot00000000000000/* * mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ /* TODO :decode2format unpleasant */ #ifndef CONTACT_OPS_H #define CONTACT_OPS_H /* if you want to parse all contacts not just de first one */ #include "../../parser/msg_parser.h" /* struct sip_msg */ #include "common.h" #define ENCODE_ALL_CONTACTS 1 #define DECODE_ALL_CONTACTS 1 #define DEFAULT_SEPARATOR "*" extern char *contact_flds_separator; struct uri_format { str username; str password; str ip; str port; str protocol; int first; int second; }; typedef struct uri_format contact_fields_t; int encode_contact (struct sip_msg *msg, char *encoding_prefix,char *public_ip); int decode_contact (struct sip_msg *msg, char *unused1,char *unused2); int decode_contact_header (struct sip_msg *msg, char *unused1,char *unused2); int encode2format (str uri, struct uri_format *format); int decode2format (str uri, char separator, struct uri_format *format); int encode_uri (str uri, char *encoding_prefix, char *public_ip,char separator, str * result); int decode_uri (str uri, char separator, str * result); #endif opensips-2.2.2/modules/mangler/doc/000077500000000000000000000000001300170765700172255ustar00rootroot00000000000000opensips-2.2.2/modules/mangler/doc/mangler.xml000066400000000000000000000022431300170765700213750ustar00rootroot00000000000000 %docentities; ]> mangler Module &osipsname; Gabriel Vasile &fhg;
vasile@fokus.fraunhofer.de
Gabriel Vasile
vasile@fokus.fraunhofer.de
2003 Fill in here $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/mangler/doc/mangler_admin.xml000066400000000000000000000154161300170765700225530ustar00rootroot00000000000000 &adminguide;
Overview This is a module to help with &sdp; mangling. Note: This module is obselete and will be removed for the 1.5.0 release.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>contact_flds_separator</varname> (string) First char of this parameter is used as separator for encoding/decoding Contact header. First char of this field must be set to a value which is not used inside username,password or other fields of contact. Otherwise it is possible for the decoding step to fail/produce wrong results. Default value is *. Set <varname>db_url</varname> parameter ... modparam("mangler", "contact_flds_separator", "-") ... then an encoded uri might look sip:user-password-ip-port-protocol@PublicIP
Exported Functions
<function moreinfo="none">sdp_mangle_ip(pattern, newip)</function> Changes &ip; addresses inside &sdp; package in lines describing connections like c=IN IP4 Currently in only changes IP4 addresses since IP6 probably will not need to traverse NAT :) The function returns negative on error, or number of replacements + 1. Meaning of the parameters is as follows: pattern - A pair ip/mask used to match &ip;'s located inside &sdp; package in lines c=IN IP4 ip. This lines will only be mangled if located &ip; is in the network described by this pattern. Examples of valid patterns are 10.0.0.0/255.0.0.0 or 10.0.0.0/8 etc. newip - A string representing the new &ip; to be put inside &sdp; package if old &ip; address matches pattern. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. <function>sdp_mangle_ip</function> usage ... sdp_mangle_ip("10.0.0.0/8","193.175.135.38"); ...
<function moreinfo="none">sdp_mangle_port(offset)</function> Changes ports inside &sdp; package in lines describing media like m=audio 13451. The function returns negative on error, or number of replacements + 1. Meaning of the parameters is as follows: offset - A string representing an integer which will be added/subtracted from the located port. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. <function>sdp_mangle_port</function> usage ... sdp_mangle_port("-12000"); ...
<function moreinfo="none">encode_contact(encoding_prefix, public_ip)</function> This function will encode uri-s inside Contact header in the following manner sip:username:password@ip:port;transport=protocol goes sip:enc_pref*username*ip*port*protocol@public_ip * is the default separator. The function returns negative on error, 1 on success. Meaning of the parameters is as follows: encoding_prefix - Something to allow us to determine that a contact is encoded publicip--a routable &ip;, most probably you should put your external &ip; of your &nat; box. public_ip - The public IP which will be used in the encoded contact, as described by the example above. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. <function>encode_contact</function> usage ... if (src_ip == 10.0.0.0/8) encode_contact("enc_prefix","193.175.135.38"); ...
<function moreinfo="none">decode_contact()</function> This function will decode the &uri; in first line in packets which come with encoded &uri; in the following manner sip:enc_pref*username*ip*port*protocol@public_ip goes to sip:username:password@ip:port;transport=protocol It uses the default set parameter for contact encoding separator. The function returns negative on error, 1 on success. Meaning of the parameters is as follows: This function can be used from REQUEST_ROUTE. <function>decode_contact</function> usage ... if (uri =~ "^enc*") { decode_contact(); } ...
<function moreinfo="none">decode_contact_header()</function> This function will decode &uri;s inside Contact header in the following manner sip:enc_pref*username*ip*port*protocol@public_ip goes to sip:username:password@ip:port;transport=protocol. It uses the default set parameter for contact encoding separator. The function returns negative on error, 1 on success. Meaning of the parameters is as follows: This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. <function>decode_contact_header</function> usage ... if (uri =~ "^enc*") { decode_contact_header(); } ...
opensips-2.2.2/modules/mangler/ip_helper.c000066400000000000000000000127631300170765700206040ustar00rootroot00000000000000/* * Sdp mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ #include "ip_helper.h" #include #include #include #include #include #include /* given an ip and a mask it provides network address */ unsigned int net_address (unsigned int ip, unsigned int mask) { return (ip & mask); } /* returns 1 if ip belongs to address/net or 0 if not */ int same_net (unsigned int ip, unsigned int address, unsigned int mask) { return ((address & mask) == (ip & mask)); } /* Small help func takes ip-address-integer and returns string representation */ void ip2str (unsigned int address, char **rr) { int i; char *hlp, hlp2[5]; /* initial era hlp2[18] */ unsigned char *addrp = (unsigned char *) &address; hlp = (char *) malloc (18); hlp[0] = '\0'; for (i = 0; i < 3; i++) { sprintf (hlp2, "%i.", addrp[i]); hlp = strcat (hlp, (char *) hlp2); } sprintf (hlp2, "%i", addrp[3]); hlp = strcat (hlp, hlp2); *rr = hlp; } /* Small help func takes ip-address-string, determines its validity and write the integer representation at address. Returns 1 if successful converted, 0 if the dotted isn't valid. If you want to parse IP/netmask pairs, call parse_ip_netmask first - it will remove the netmask, then use this func */ int parse_ip_address (char *c, unsigned int *address) //inet_aton { int quat, i, j, digit_bol; char buf[20]; char *p, *q, *r; unsigned char *addrp; if (c == NULL) return 0; if (strlen (c) >= 16) return 0; quat = 0; digit_bol = 1; buf[0] = '\0'; /* cool dirty hack to address the bytes of the int easily */ addrp = (unsigned char *) address; /* make a copy of the dotted string, because we modify it */ strncpy (buf, c, 20); p = buf; /* search three times for a dot in the string */ for (i = 0; i < 3; i++) { if ((q = strchr (p, '.')) == NULL) return 0; else { *q = '\0'; /* cut off at the dot */ if (strlen (p)) /* is the distance between dots greater 0 */ { r = p; for (j = 0; j < strlen (p); j++, r++) /* are all char of the * byte digits */ digit_bol = digit_bol && isdigit ((unsigned char)*r); if (digit_bol) { quat = atoi (p); if (quat > 255) /* is it a byte or greater */ return 0; else addrp[i] = (unsigned char) quat; } else return 0; } else return 0; } p = q + 1; } /* for */ /* and the last byte */ if (strlen (p)) { r = p; for (j = 0; j < strlen (p); j++, r++) digit_bol = digit_bol && isdigit ((unsigned char)*r); if (digit_bol) { quat = atoi (p); if (quat > 255) { return 0; } else addrp[3] = (unsigned char) quat; return 1; } else { return 0; } } else { return 0; } } /* return 1 if ALL str is a positive number or 0. no + signs allowed*/ int is_positive_number (char *str) { int i; if (str == NULL) return 0; for (i = 0; i < strlen (str); i++) { if (isdigit ((unsigned char)str[i]) == 0) return 0; } return 1; } /* return 0 in case of error */ unsigned int make_mask (unsigned int length) { unsigned int res; if ((length < 8) || (length > 30)) return -1; /* invalid value for mask */ /* fill it with 1 */ res = 0xFFFFFFFF; /* shift it to right with 32-length positions */ res = htonl (res << (32 - length)); return res; } /* Small help func takes ip-address-string, determines if a valid netmask is specified and inserts the netmask into mask. Cuts of the netmask of the string, if it founds a netmask !!! Returns 0 if no netmask found, -1 if netmask isn't valid, and 1 if successful. According to this function a mask is in form of 255.255.192.0 so an ip/mask looks like 10.0.0.0/255.255.192.0 we will extend it to 10.0.0.0/18 which will be also valid */ int parse_ip_netmask (char *c, char **ip, unsigned int *mask) { char *p, *q; unsigned int netmask; if (c == NULL) { return -10; } p = c; if ((q = strchr (p, '/')) == NULL) { *mask = 0xFFFFFFFF; return 0; /* no mask */ } else { *ip = (char *) malloc (q - p + 1); if ((*ip) == NULL) return -2; memcpy (*ip, p, q - p); (*ip)[q - p] = 0; // wrong (*q) = 0; /* cut of the netmask */ q++; /* * two possibilities /16 or /255.255.192.0 */ if (is_positive_number (q) == 1) { /* we have a /16 mask */ if ((netmask = make_mask (atoi (q))) == 0) { *mask = 0; /* error in value of /43 or something like */ return -1; } else { *mask = netmask; return 1; } } else /* we may have a 255.255.192.0 mask */ if (parse_ip_address (q, &netmask) == 1) /* and parse the netmask */ { *mask = netmask; return 1; } else { *mask = 0; return -1; } } } opensips-2.2.2/modules/mangler/ip_helper.h000066400000000000000000000024651300170765700206070ustar00rootroot00000000000000/* * Sdp mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ #ifndef IP_HELPER_H #define IP_HELPER_H unsigned int net_address (unsigned int address, unsigned int mask); int same_net (unsigned int ip, unsigned int address, unsigned int mask); void ip2str (unsigned int address, char **rr); int is_positive_number (char *str); int parse_ip_address (char *c, unsigned int *address); int parse_ip_netmask (char *c, char **ip, unsigned int *mask); unsigned int make_mask (unsigned int length); #endif opensips-2.2.2/modules/mangler/mangler.c000066400000000000000000000113341300170765700202530ustar00rootroot00000000000000/* * mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ #include #include #include #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../ut.h" #include "../../error.h" #include "mangler.h" #include "sdp_mangler.h" #include "contact_ops.h" #include "utils.h" #include "common.h" #ifdef DEMO #include "../tm/t_hooks.h" #include "../tm/tm_load.h" #include "../tm/h_table.h" struct tm_binds tmb; #endif /* * Module destroy function prototype */ static void destroy (void); /* * Module initialization function prototype */ static int mod_init (void); char *contact_flds_separator = DEFAULT_SEPARATOR; static param_export_t params[] = { {"contact_flds_separator",STR_PARAM,&contact_flds_separator}, {0, 0, 0} }; /* perhaps I should add pre-compiled expressions */ /* * Exported functions */ static cmd_export_t cmds[] = { {"sdp_mangle_ip", (cmd_function)sdp_mangle_ip, 2,0, 0, REQUEST_ROUTE|ONREPLY_ROUTE}, {"sdp_mangle_port", (cmd_function)sdp_mangle_port, 1,0, 0, REQUEST_ROUTE|ONREPLY_ROUTE}, {"encode_contact", (cmd_function)encode_contact,2,0, 0, REQUEST_ROUTE|ONREPLY_ROUTE}, {"decode_contact", (cmd_function)decode_contact,0,0, 0, REQUEST_ROUTE}, {"decode_contact_header", (cmd_function)decode_contact_header,0,0,0,REQUEST_ROUTE|ONREPLY_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Module interface */ struct module_exports exports = { "mangler", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ 0 /* child initialization function */ }; #ifdef DEMO /* MANGLING EXAMPLE */ /* ================================================================= */ static void func_invite(struct cell *t,struct sip_msg *msg,int code,void *param) { int i; //callback function if (!check_transaction_quadruple(msg)) { //we do not have a correct message from/callid/cseq/to return; } i = encode_contact(msg,"enc_prefix","193.175.135.38"); fprintf(stdout,"decode/encode = returned %d\n",i);fflush(stdout); if (t->is_invite) { if (msg->buf != NULL) { fprintf(stdout,"INVITE:received \n%s\n",msg->buf);fflush(stdout); i = sdp_mangle_port(msg,"1000",NULL); fprintf(stdout,"sdp_mangle_port returned %d\n",i);fflush(stdout); i = sdp_mangle_ip(msg,"10.0.0.0/16","123.124.125.126"); fprintf(stdout,"sdp_mangle_ip returned %d\n",i);fflush(stdout); } else fprintf(stdout,"INVITE:received NULL\n");fflush(stdout); } else { fprintf(stdout,"NOT INVITE(REGISTER?) received \n%s\n",msg->buf);fflush(stdout); //i = decode_contact(msg,NULL,NULL); //fprintf(stdout,"decode/encode = returned %d\n",i);fflush(stdout); } fflush(stdout); } #endif int prepare (void) { /* using pre-compiled expressions to speed things up*/ compile_expressions(PORT_REGEX,IP_REGEX); #ifdef DEMO fprintf(stdout,"===============NEW RUN================\n"); /* load the TM API */ if (load_tm_api(&tmb)!=0) { LM_ERR("can't load TM API\n"); return -1; } //register callbacks if (tmb.register_tmcb(TMCB_REQUEST_OUT,func_invite,0) <= 0) return -1; #endif return 0; } static int mod_init (void) { ipExpression = NULL; portExpression = NULL; prepare (); /* * Might consider to compile at load time some regex to avoid compilation * every time I use this functions */ return 0; } static void destroy (void) { /*free some compiled regex expressions */ free_compiled_expressions(); #ifdef DEMO fprintf(stdout,"Freeing pre-compiled expressions\n"); #endif return; } opensips-2.2.2/modules/mangler/mangler.cfg000066400000000000000000000042371300170765700205740ustar00rootroot00000000000000# # simple quick-start config script # # WARNING: replace PUBLIC_IP with the public ip of the nat box # (e.g.: 213.34.65.56 ) # ----------- global configuration parameters ------------------------ #debug_mode=yes check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 mhomed=yes # ------------------ module loading ---------------------------------- loadmodule "modules/sl/sl.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/textops/textops.so" loadmodule "modules/mangler/mangler.so" loadmodule "modules/mi_fifo/mi_fifo.so" # ----------------- setting module-specific parameters --------------- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # seting separator for encoded contact modparam("mangler","contact_flds_separator","*") # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if ( msg:len > max_len ) { sl_send_reply("513", "Message too big"); exit; }; record_route(); /* replace PUBLIC_IP with the public ip of the nat box (e.g.: 213.34.65.56 )*/ /* outgoing connection */ if (src_ip == 10.0.0.0/8 || src_ip == 192.168.0.0/16 ){ /* we mangle the contact address and ports in SDP part of the * message */ if (method == "INVITE"){ sdp_mangle_ip("0.0.0.0/0","PUBLIC_IP"); /* diferent mangling based on what phone we use */ if (src_ip==10.0.0.1) sdp_mangle_port("+1000"); if (src_ip==10.0.0.2) sdp_mangle_port("-1000"); }; if (search("Contact: .*@(10\.|111\.)")){ /* we seem to have a private address on a Contact which is not * valid */ encode_contact("enc_prefix","PUBLIC_IP"); }; /* #if ser is behind a NAT and NAT box has port forwarding #on port 5060 set to our ser private address ... record_route(PUBLIC_IP); */ }; /* we received something for a local phone */ if (uri=~"enc_prefix*"){ decode_contact(); }; # forward to current uri now forward(); break; } opensips-2.2.2/modules/mangler/mangler.h000066400000000000000000000020671300170765700202630ustar00rootroot00000000000000/* * Sdp mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ #ifndef MANGLER_MOD_H #define MANGLER_MOD_H #include "../../str.h" #include "../../parser/msg_parser.h" /* struct sip_msg */ #include "sdp_mangler.h" #include "contact_ops.h" #endif opensips-2.2.2/modules/mangler/sdp_mangler.c000066400000000000000000000302511300170765700211200ustar00rootroot00000000000000/* * mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ #include #include #include #include #include #include "sdp_mangler.h" #include "ip_helper.h" #include "utils.h" #include "common.h" #include "../../mem/mem.h" #include "../../data_lump.h" #include "../../parser/hf.h" #include "../../parser/parse_content.h" #include "../../parser/parse_uri.h" #include "../../parser/contact/parse_contact.h" #include "../../ut.h" #include "../../parser/msg_parser.h" /* struct sip_msg */ //#define DEBUG 1 int sdp_mangle_port (struct sip_msg *msg, char *offset, char *unused) { int old_content_length, new_content_length, oldlen, err, oldPort, newPort, diff, offsetValue,len,off,ret,need_to_deallocate; struct lump *l; regmatch_t pmatch; regex_t *re; char *s, *pos,*begin,*key; char buf[6]; str body; key = PORT_REGEX; /* * Checking if msg has a payload */ if (msg == NULL) { LM_ERR("received NULL msg\n"); return -1; } if ( get_body(msg,&body)!=0 || body.len==0) { LM_ERR(" failed to get a body \n"); return -2; } old_content_length = get_content_length(msg); if (offset == NULL) return -14; if (sscanf (offset, "%d", &offsetValue) != 1) { LM_ERR("invalid value for offset \n"); return -13; } //offsetValue = (int)offset; #ifdef DEBUG fprintf (stdout,"---START--------MANGLE PORT-----------------\n"); fprintf(stdout,"===============OFFSET = %d\n",offsetValue); #endif if ((offsetValue < MIN_OFFSET_VALUE) || (offsetValue > MAX_OFFSET_VALUE)) { LM_ERR("invalid value %d for offset \n",offsetValue); return -3; } begin = body.s; ret = -1; /* try to use pre-compiled expressions */ need_to_deallocate = 0; if (portExpression != NULL) { re = portExpression; #ifdef DEBUG fprintf(stdout,"Using PRECOMPILED expression for port ...\n"); #endif } else /* we are not using pre-compiled expressions */ { re = pkg_malloc(sizeof(regex_t)); if (re == NULL) { LM_ERR("unable to allocate re\n"); return -4; } need_to_deallocate = 1; if ((regcomp (re, key, REG_EXTENDED)) != 0) { LM_ERR("unable to compile %s \n",key); return -5; } #ifdef DEBUG fprintf(stdout,"Using ALLOCATED expression for port ...\n"); #endif } diff = 0; while ((begin < msg->buf + msg->len) && (regexec (re, begin, 1, &pmatch, 0) == 0)) { off = begin - msg->buf; if (pmatch.rm_so == -1) { LM_ERR("offset unknown\n"); return -6; } #ifdef STRICT_CHECK pmatch.rm_eo --; /* return with one space */ #endif /* for BSD and Solaris we avoid memrchr pos = (char *) memrchr (begin + pmatch.rm_so, ' ',pmatch.rm_eo - pmatch.rm_so); */ pos = begin+pmatch.rm_eo; #ifdef DEBUG printf("begin=%c pos=%c rm_so=%d rm_eo=%d\n",*begin,*pos,pmatch.rm_so,pmatch.rm_eo); #endif do pos--; while (*pos != ' '); /* we should find ' ' because we matched m=audio port */ pos++; /* jumping over space */ oldlen = (pmatch.rm_eo - pmatch.rm_so) - (pos - (begin + pmatch.rm_so)); /* port length */ /* convert port to int */ oldPort = str2s (pos, oldlen, &err); #ifdef DEBUG printf("port to convert [%.*s] to int\n",oldlen,pos); #endif if (err) { LM_ERR("failed to convert [%.*s] to int\n",oldlen,pos); #ifdef STRICT_CHECK return -7; #else goto continue1; #endif } if ((oldPort < MIN_ORIGINAL_PORT) || (oldPort > MAX_ORIGINAL_PORT)) /* we silently fail,we ignore this match or return -11 */ { #ifdef DEBUG printf("WARNING: sdp_mangle_port: Silent fail for not matching old port %d\n",oldPort); #endif LM_WARN("silent fail for not matching old port %d\n",oldPort); #ifdef STRICT_CHECK return -8; #else goto continue1; #endif } if ((offset[0] != '+')&&(offset[0] != '-')) newPort = offsetValue;//fix value else newPort = oldPort + offsetValue; /* new port is between 1 and 65536, or so should be */ if ((newPort < MIN_MANGLED_PORT) || (newPort > MAX_MANGLED_PORT)) /* we silently fail,we ignore this match */ { #ifdef DEBUG printf("WARNING: sdp_mangle_port: Silent fail for not matching new port %d\n",newPort); #endif LM_WARN("silent fail for not matching new port %d\n",newPort); #ifdef STRICT_CHECK return -9; #else goto continue1; #endif } #ifdef DEBUG fprintf(stdout,"Extracted port is %d and mangling to %d\n",oldPort,newPort); #endif /* len = 1; while ((newPort = (newPort / 10)) != 0) len++; newPort = oldPort + offsetValue; */ if (newPort >= 10000) len = 5; else if (newPort >= 1000) len = 4; else if (newPort >= 100) len = 3; else if (newPort >= 10) len = 2; else len = 1; /* replaced five div's + 1 add with most probably 1 comparison or 2 */ /* deleting old port */ if ((l = del_lump (msg, pmatch.rm_so + off + (pos -(begin + pmatch.rm_so)),oldlen, 0)) == 0) { LM_ERR("del_lump failed\n"); return -10; } s = pkg_malloc (len); if (s == 0) { LM_ERR("no more pkg memory\n"); return -11; } snprintf (buf, len + 1, "%u", newPort); /* converting to string */ memcpy (s, buf, len); if (insert_new_lump_after (l, s, len, 0) == 0) { LM_ERR("could not insert new lump\n"); pkg_free (s); return -12; } diff = diff + len /*new length */ - oldlen; /* new cycle */ ret++; #ifndef STRICT_CHECK continue1: #endif begin = begin + pmatch.rm_eo; } /* while */ if (need_to_deallocate) { regfree (re); pkg_free(re); #ifdef DEBUG fprintf(stdout,"Deallocating expression for port ...\n"); #endif } if (diff != 0) { new_content_length = old_content_length + diff; patch_content_length (msg, new_content_length); } #ifdef DEBUG fprintf (stdout,"---END--------MANGLE PORT-----------------\n"); #endif return ret+2; } int sdp_mangle_ip (struct sip_msg *msg, char *oldip, char *newip) { int i, old_content_length, new_content_length; int diff, oldlen, len, off, ret, need_to_deallocate; unsigned int mask, address, locatedIp; struct lump *l; regmatch_t pmatch; regex_t *re; char *s, *pos, *begin, *key; char buffer[16]; /* 123.456.789.123\0 */ str body; #ifdef DEBUG fprintf (stdout,"---START--------MANGLE IP-----------------\n"); #endif key = IP_REGEX; /* * Checking if msg has a payload */ if (msg == NULL) { LM_ERR("msg null\n"); return -1; } if ( get_body(msg,&body)!=0 || body.len==0) { LM_ERR(" failed to get a body \n"); return -2; } old_content_length = get_content_length(msg); /* checking oldip */ if (oldip == NULL) { LM_ERR("received NULL for oldip\n"); return -3; } /* checking newip */ if (newip == NULL) { LM_ERR("received NULL for newip\n"); return -4; } i = parse_ip_netmask (oldip, &pos, &mask); if (i == -1) { /* invalid value for the netmask specified in oldip */ LM_ERR("invalid value for the netmask specified in oldip\n"); return -5; } else { i = parse_ip_address (pos, &address); if (pos != NULL) free (pos); if (i == 0) { LM_ERR("invalid value for the ip specified in oldip\n"); return -6; /* parse error in ip */ } } /* now we have in address/netmask binary values */ begin = body.s; ret = -1; len = strlen (newip); /* try to use pre-compiled expressions */ need_to_deallocate = 0; if (ipExpression != NULL) { re = ipExpression; #ifdef DEBUG fprintf(stdout,"Using PRECOMPILED expression for ip ...\n"); #endif } else /* we are not using pre-compiled expressions */ { re = pkg_malloc(sizeof(regex_t)); if (re == NULL) { LM_ERR("unable to allocate re in pkg mem\n"); return -7; } need_to_deallocate = 1; if ((regcomp (re, key, REG_EXTENDED)) != 0) { LM_ERR("unable to compile %s \n",key); return -8; } #ifdef DEBUG fprintf(stdout,"Using ALLOCATED expression for ip ...\n"); #endif } diff = 0; while ((begin < msg->buf + msg->len) && (regexec (re, begin, 1, &pmatch, 0) == 0)) { off = begin - msg->buf; if (pmatch.rm_so == -1) { LM_ERR("offset unknown\n"); return -9; } #ifdef STRICT_CHECK pmatch.rm_eo --; /* return with one space,\n,\r */ #endif /* for BSD and Solaris we avoid memrchr pos = (char *) memrchr (begin + pmatch.rm_so, ' ',pmatch.rm_eo - pmatch.rm_so); */ pos = begin+pmatch.rm_eo; do pos--; while (*pos != ' '); /* we should find ' ' because we matched c=IN IP4 ip */ pos++; /* jumping over space */ oldlen = (pmatch.rm_eo - pmatch.rm_so) - (pos - (begin + pmatch.rm_so)); /* ip length */ if (oldlen > 15) { LM_WARN("silent fail because oldlen > 15\n"); #ifdef STRICT_CHECK return -10; #else goto continue2; /* silent fail return -10; invalid ip format ,probably like 1000.3.12341.2 */ #endif } buffer[0] = '\0'; strncat ((char *) buffer, pos, oldlen); buffer[oldlen] = '\0'; i = parse_ip_address (buffer, &locatedIp); if (i == 0) { LM_WARN("silent fail on parsing matched address \n"); #ifdef STRICT_CHECK return -11; #else goto continue2; #endif } if (same_net (locatedIp, address, mask) == 0) { LM_WARN("silent fail because matched address is not in network\n"); #ifdef DEBUG fprintf(stdout,"Extracted ip is %s and not mangling \n",buffer); #endif goto continue2; /* not in the same net, skipping */ } #ifdef DEBUG fprintf(stdout,"Extracted ip is %s and mangling to %s\n",buffer,newip); #endif /* replacing ip */ /* deleting old ip */ if ((l = del_lump (msg,pmatch.rm_so + off + (pos - (begin + pmatch.rm_so)),oldlen, 0)) == 0) { LM_ERR("del_lump failed\n"); return -12; } s = pkg_malloc (len); if (s == 0) { LM_ERR("no more pkg mem\n"); return -13; } memcpy (s, newip, len); if (insert_new_lump_after (l, s, len, 0) == 0) { LM_ERR("could not insert new lump\n"); pkg_free (s); return -14; } diff = diff + len /*new length */ - oldlen; /* new cycle */ ret++; continue2: begin = begin + pmatch.rm_eo; } /* while */ if (need_to_deallocate) { regfree (re); /* if I am going to use pre-compiled expressions to be removed */ pkg_free(re); #ifdef DEBUG fprintf(stdout,"Deallocating expression for ip ...\n"); #endif } if (diff != 0) { new_content_length = old_content_length + diff; patch_content_length (msg, new_content_length); } #ifdef DEBUG fprintf (stdout,"---END--------MANGLE IP-----------------\n"); #endif return ret+2; } int compile_expressions(char *port,char *ip) { portExpression = NULL; portExpression = pkg_malloc(sizeof(regex_t)); if (portExpression != NULL) { if ((regcomp (portExpression,port, REG_EXTENDED)) != 0) { LM_ERR("unable to compile portExpression [%s]\n",port); pkg_free(portExpression); portExpression = NULL; } } else { LM_ERR("unable to alloc portExpression in pkg mem\n"); } ipExpression = NULL; ipExpression = pkg_malloc(sizeof(regex_t)); if (ipExpression != NULL) { if ((regcomp (ipExpression,ip, REG_EXTENDED)) != 0) { LM_ERR("unable to compile ipExpression [%s]\n",ip); pkg_free(ipExpression); ipExpression = NULL; } } else { LM_ERR("unable to alloc ipExpression in pkg mem\n"); } return 0; } int free_compiled_expressions(void) { if (portExpression != NULL) { regfree(portExpression); pkg_free(portExpression); portExpression = NULL; } if (ipExpression != NULL) { regfree(ipExpression); pkg_free(ipExpression); ipExpression = NULL; } return 0; } opensips-2.2.2/modules/mangler/sdp_mangler.h000066400000000000000000000053351300170765700211320ustar00rootroot00000000000000/* * mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ /* TO DO: precompiled expressions */ #ifndef SDP_MANGLER_H #define SDP_MANGLER_H #include "../../parser/msg_parser.h" /* struct sip_msg */ #include "common.h" #include /* With STRICT_CHECK off: If you define a port like 41231311 and BEST_EFFORT is defined it will consider a port the first 5 digits Similarly an ip like 12.31.12.313131132131 will be mangled with only 3 digits from the last group */ #ifdef STRICT_CHECK #define PORT_REGEX "(m=[a-z]+ [0-9]{1,5} )" #define IP_REGEX "(c=IN IP4 [0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}( |\n|\r))" #endif #ifndef STRICT_CHECK #define PORT_REGEX "m=[a-z]+ [0-9]{1,5}" #define IP_REGEX "(c=IN IP4 [0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})" #endif #define MIN_ORIGINAL_PORT 1 #define MAX_ORIGINAL_PORT 65535 #define MIN_MANGLED_PORT 1 #define MAX_MANGLED_PORT 65535 #define MIN_OFFSET_VALUE -65535 #define MAX_OFFSET_VALUE 65535 regex_t *portExpression; regex_t *ipExpression; /* replaces all appearances of a port in lines like m=audio port with a new value for port which is oldvalue+offset @param msg a pointer to a sip message @param offset value of an offset.Must be a numeric format like "-12345" @param unused unused parameter @return negative in case of error or number of replacements - 1 */ int sdp_mangle_port (struct sip_msg *msg, char *offset, char *unused); /* may replace all appearances of an ip in lines like c=IN IP4 ip with a new value for ip if the found ip matches the filter @param msg a pointer to a sip message @param oldip an filter for ip's in form ip/mask.Example of oldip "10.0.0.0/255.0.0.0" or "10.0.0.0/8" @param newip the ip replacing old ip @return negative in case of error or number of replacements - 1 */ int sdp_mangle_ip (struct sip_msg *msg, char *oldip, char *newip); int compile_expressions(char *port,char *ip); int free_compiled_expressions(); #endif opensips-2.2.2/modules/mangler/utils.c000066400000000000000000000052701300170765700177700ustar00rootroot00000000000000/* * mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ #include "utils.h" #include "../../parser/msg_parser.h" /* struct sip_msg */ #include "../../mem/mem.h" #include "../../data_lump.h" #include int patch (struct sip_msg *msg, char *oldstr, unsigned int oldlen, char *newstr, unsigned int newlen) { int off; struct lump *anchor; if (oldstr == NULL) return -1; if (newstr == NULL) return -2; off = oldstr - msg->buf; if (off < 0) return -3; if ((anchor = del_lump (msg, off, oldlen, 0)) == 0) { LM_ERR("lumping with del_lump\n"); return -4; } if ((insert_new_lump_after (anchor, newstr, newlen, 0)) == 0) { LM_ERR("lumping with insert_new_lump_after\n"); return -5; } return 0; } /* TESTED */ int patch_content_length (struct sip_msg *msg, unsigned int newValue) { struct hdr_field *contentLength; char *s, pos[11]; int len; contentLength = msg->content_length; if (contentLength == NULL) /* maybe not yet parsed */ { if (parse_headers (msg, HDR_CONTENTLENGTH_F, 0) == -1) { LM_ERR("parse headers on Content-Length failed\n"); return -1; } contentLength = msg->content_length; if (contentLength == NULL) { LM_ERR("failed to parse headers on Content-Length " "succeeded but msg->content_length is still NULL\n"); return -2; } } /* perhaps dangerous because buffer is static ? */ //pos = int2str(newValue,&len); len = snprintf ((char *) pos, 10, "%u", newValue); s = pkg_malloc (len); if (s == NULL) { LM_ERR("unable to allocate %d bytes in pkg mem\n", len); return -3; } memcpy (s, pos, len); /* perhaps we made it and no one called int2str,might use sprintf */ if (patch (msg, contentLength->body.s, contentLength->body.len, s, len) < 0) { pkg_free (s); LM_ERR("lumping failed\n"); return -4; } LM_DBG ("succeeded in altering Content-Length to new value %u\n",newValue); return 0; } opensips-2.2.2/modules/mangler/utils.h000066400000000000000000000033471300170765700200000ustar00rootroot00000000000000/* * Sdp mangler module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-07 first version. */ #ifndef UTILS_H #define UTILS_H #include "../../parser/msg_parser.h" /* struct sip_msg */ /* replace a part of a sip message identified by (start address,length) with a new part @param msg a pointer to a sip message @param oldstr the start address of the part to be modified @param oldlen the length of the part being modified @param newstr the start address of the part to be added @param oldlen the length of the part being added @return 0 in case of success, negative on error */ int patch (struct sip_msg *msg, char *oldstr, unsigned int oldlen, char *newstr, unsigned int newlen); /* modify the Content-Length header of a sip message @param msg a pointer to a sip message @param newValue the new value of Content-Length @return 0 in case of success, negative on error */ int patch_content_length (struct sip_msg *msg, unsigned int newValue); #endif opensips-2.2.2/modules/mathops/000077500000000000000000000000001300170765700165065ustar00rootroot00000000000000opensips-2.2.2/modules/mathops/Makefile000066400000000000000000000003521300170765700201460ustar00rootroot00000000000000# Makefile v 1.0 2002/12/27 # # Mathops module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=mathops.so LIBS=-lm include ../../Makefile.modules opensips-2.2.2/modules/mathops/README000066400000000000000000000251751300170765700174000ustar00rootroot00000000000000mathops Module Liviu Chircu OpenSIPS Solutions Edited by Liviu Chircu Edited by Stephane Alnet Copyright © 2013 www.opensips-solutions.com Revision History Revision $Revision$ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. decimal_digits (integer) 1.4. Exported Functions 1.4.1. math_eval(expression, result_pvar) 1.4.2. math_rpn(expression, result_pvar) 1.4.3. math_trunc(number, result_pvar) 1.4.4. math_floor(number, result_pvar) 1.4.5. math_ceil(number, result_pvar) 1.4.6. math_round(number, result_pvar[, decimals]) 1.4.7. math_round_sf(number, result_pvar, figures) List of Examples 1.1. Setting the decimal_digits module parameter 1.2. math_eval usage 1.3. math_rpn usage 1.4. math_trunc usage 1.5. math_floor usage 1.6. math_ceil usage 1.7. math_round usage 1.8. math_round_sf usage Chapter 1. Admin Guide 1.1. Overview The mathops module provides a series of functions which enable various floating point operations at OpenSIPS script level. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules.. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. decimal_digits (integer) The precision of the results returned by all the module functions. The higher the “decimal_digits†value, the more decimal digits the results will have. Default value is “6â€. Example 1.1. Setting the decimal_digits module parameter modparam("mathops", "decimal_digits", 10) 1.4. Exported Functions 1.4.1. math_eval(expression, result_pvar) The function evaluates a given expression and writes the result in the output pseudo-variable. The expression may contain any number of pseudo variables. Evaluation uses tinyexpr (see https://github.com/codeplea/tinyexpr). Currently allowed syntax for specifying an expression: * Nested parentheses * addition (+), subtraction/negation (-), multiplication (*), division (/), exponentiation (^) and modulus (%) with the normal operator precedence (the one exception being that exponentiation is evaluated left-to-right) * C math functions: abs (calls to fabs), acos, asin, atan, ceil, cos, cosh, exp, floor, ln (calls to log), log (calls to log10), sin, sinh, sqrt, tan, tanh Meaning of the parameters is as follows: * expression - String containing a mathematical expression. It can also include pseudo variables. The expression parameter can only be given as a string. * result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. Example 1.2. math_eval usage ... # Compute some random math expression $avp(1) = "3.141592"; $avp(2) = "2.71828"; $avp(3) = "123.45678"; if (math_eval("$avp(1) * ($avp(3) - ($avp(1) - $avp(2))) / $avp(3)", "$a vp(result)")) { xlog("Result of expression: $avp(result)\n"); } else { xlog("Math eval failed!\n"); } ... 1.4.2. math_rpn(expression, result_pvar) The function evaluates a given RPN expression and writes the result in the output pseudo-variable. The expression may contain any number of pseudo variables. The expression is specified in Reverse Polish Notation. Values are pushed onto a stack, while operations are executed on that stack. The following operations are supported: * binary operators: + - / * mod pow * unary functions: neg exp ln log10 abs sqrt cbrt floor ceil round nearbyint trunc neg will change the sign of the top of the stack ln is natural logarithm; abs is absolute value; other functions are standard C functions * constants: e pi * stack manipulations commands: drop dup swap Meaning of the parameters is as follows: * expression - String containing a RPN expression. It can also include pseudo variables. The expression parameter can only be given as a string. * result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. Example 1.3. math_rpn usage $avp(1) = "3"; if (math_rpn("1 $avp(1) swap swap dup drop / exp ln 1 swap /", "$avp(res ult)")) { xlog("Result of expression: $avp(result)\n"); } else { xlog("RPN eval failed!\n"); } /* This example RPN script will push 1 then 3 onto the stack, then do a couple no-ops (exchange the two values twice, duplicate one of them then drop the dupl icate), compute the division of 1 by 3, then do another no-op (exponentiation th en logarithm), and finally compute 1 divided by the result, giving 3 as the result. */ 1.4.3. math_trunc(number, result_pvar) Truncation of a number towards zero. This means that trunc(3.7) = 3.0 and trunc(-2.9) = -2.0. Meaning of the parameters is as follows: * number - Number to be truncated. The number parameter can have the following types: + string - statically given + pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) * result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. Example 1.4. math_trunc usage ... # Truncate a random number $avp(1) = "3.141492"; if (math_trunc("$avp(1)", "$avp(result)")) { xlog("Truncate result: $avp(result)\n"); } else { xlog("Truncate failed!\n"); } ... 1.4.4. math_floor(number, result_pvar) Truncates a number, always towards -infinity. This means that floor(3.7) = 3.0 and floor(-2.9) = -3.0 Meaning of the parameters is as follows: * number - Number to be truncated. The number parameter can have the following types: + string - statically given + pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) * result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. Example 1.5. math_floor usage ... # Truncate a random number $avp(1) = "3.141492"; if (math_floor("$avp(1)", "$avp(result)")) { xlog("Floor result: $avp(result)\n"); } else { xlog("Floor operation failed!\n"); } ... 1.4.5. math_ceil(number, result_pvar) Truncates a number, always towards +infinity. This means that ceil(3.2) = 4.0 and ceil(-2.9) = -2.0 Meaning of the parameters is as follows: * number - Number to be truncated. The number parameter can have the following types: + string - statically given + pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) * result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. Example 1.6. math_ceil usage ... # Truncate a random number $avp(1) = "3.141492"; if (math_ceil("$avp(1)", "$avp(result)")) { xlog("Ceil result: $avp(result)\n"); } else { xlog("Ceil operation failed!\n"); } ... 1.4.6. math_round(number, result_pvar[, decimals]) The round function returns the nearest integer, and tie-breaking is done away from zero. Examples: round(1.2) = 1.0, round(0.5) = 1.0, round(-0.5) = -1.0 By default, the function returns an integer. An additional parameter controls the number of decimal digits of the initial number which will be kept. The rounding will then be done using the remaining decimal digits, and the result will be a float value, represented as a string. Meaning of the parameters is as follows: * number - Number to be rounded. The number parameter can have the following types: + string - statically given + pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) * result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. * decimals - (pvar / integer as a string) which further improves the precision of the rounding. This function can be used from any route. Example 1.7. math_round usage ... # Rounding PI $avp(1) = "3.141492"; if (math_round("$avp(1)", "$avp(result)")) { # result should be: 3 xlog("Round result: $avp(result)\n"); } else { xlog("Round operation failed!\n"); } ... if (math_round("$avp(1)", "$avp(result)", "4")) { # result should be: "3.1415" xlog("Round result: $avp(result)\n"); } else { xlog("Round operation failed!\n"); } ... 1.4.7. math_round_sf(number, result_pvar, figures) To give a simple explanation, rounding to N significant figures is done by first obtaining the number resulted from keeping N significant figures (0 padded if necessary), then adjusting it if the N+1'th digit is greater or equal to 5. Some examples: * round_sf(17892.987, 1) = 20000 round_sf(17892.987, 2) = 18000 round_sf(17892.987, 3) = 17900 round_sf(17892.987, 4) = 17890 round_sf(17892.987, 5) = 17893 round_sf(17892.987, 6) = 17893.0 round_sf(17892.987, 7) = 17892.99 Meaning of the parameters is as follows: * number - Number to be rounded. The number parameter can have the following types: + string - statically given + pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) * result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. * figures - (pvar / integer as a string) which further improves the precision of the rounding. This function can be used from any route. Example 1.8. math_round_sf usage ... # Rounding PI $avp(1) = "3.141492"; if (math_round_sf("$avp(1)", "$avp(result)", "4")) { # result should be: "3.141" xlog("Round result: $avp(result)\n"); } else { xlog("Round operation failed!\n"); } ... opensips-2.2.2/modules/mathops/doc/000077500000000000000000000000001300170765700172535ustar00rootroot00000000000000opensips-2.2.2/modules/mathops/doc/mathops.xml000066400000000000000000000022271300170765700214530ustar00rootroot00000000000000 %docentities; ]> mathops Module Liviu Chircu OpenSIPS Solutions liviu@opensips.org Liviu Chircu
liviu@opensips.org
Stephane Alnet
stephane@shimaore.net
2013 &osipssol; $Revision$ $Date$
&admin;
opensips-2.2.2/modules/mathops/doc/mathops_admin.xml000066400000000000000000000341711300170765700226260ustar00rootroot00000000000000 &adminguide;
Overview The mathops module provides a series of functions which enable various floating point operations at &osips; script level.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules..
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>decimal_digits</varname> (integer) The precision of the results returned by all the module functions. The higher the decimal_digits value, the more decimal digits the results will have. Default value is 6. Setting the decimal_digits module parameter modparam("mathops", "decimal_digits", 10)
Exported Functions
<function moreinfo="none">math_eval(expression, result_pvar)</function> The function evaluates a given expression and writes the result in the output pseudo-variable. The expression may contain any number of pseudo variables. Evaluation uses tinyexpr (see https://github.com/codeplea/tinyexpr). Currently allowed syntax for specifying an expression: Nested parentheses addition (+), subtraction/negation (-), multiplication (*), division (/), exponentiation (^) and modulus (%) with the normal operator precedence (the one exception being that exponentiation is evaluated left-to-right) C math functions: abs (calls to fabs), acos, asin, atan, ceil, cos, cosh, exp, floor, ln (calls to log), log (calls to log10), sin, sinh, sqrt, tan, tanh Meaning of the parameters is as follows: expression - String containing a mathematical expression. It can also include pseudo variables. The expression parameter can only be given as a string. result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. <function moreinfo="none">math_eval</function> usage ... # Compute some random math expression $avp(1) = "3.141592"; $avp(2) = "2.71828"; $avp(3) = "123.45678"; if (math_eval("$avp(1) * ($avp(3) - ($avp(1) - $avp(2))) / $avp(3)", "$avp(result)")) { xlog("Result of expression: $avp(result)\n"); } else { xlog("Math eval failed!\n"); } ...
<function moreinfo="none">math_rpn(expression, result_pvar)</function> The function evaluates a given RPN expression and writes the result in the output pseudo-variable. The expression may contain any number of pseudo variables. The expression is specified in Reverse Polish Notation. Values are pushed onto a stack, while operations are executed on that stack. The following operations are supported: binary operators: + - / * mod pow unary functions: neg exp ln log10 abs sqrt cbrt floor ceil round nearbyint trunc neg will change the sign of the top of the stack ln is natural logarithm; abs is absolute value; other functions are standard C functions constants: e pi stack manipulations commands: drop dup swap Meaning of the parameters is as follows: expression - String containing a RPN expression. It can also include pseudo variables. The expression parameter can only be given as a string. result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. <function moreinfo="none">math_rpn</function> usage $avp(1) = "3"; if (math_rpn("1 $avp(1) swap swap dup drop / exp ln 1 swap /", "$avp(result)")) { xlog("Result of expression: $avp(result)\n"); } else { xlog("RPN eval failed!\n"); } /* This example RPN script will push 1 then 3 onto the stack, then do a couple no-ops (exchange the two values twice, duplicate one of them then drop the duplicate), compute the division of 1 by 3, then do another no-op (exponentiation then logarithm), and finally compute 1 divided by the result, giving 3 as the result. */
<function moreinfo="none">math_trunc(number, result_pvar)</function> Truncation of a number towards zero. This means that trunc(3.7) = 3.0 and trunc(-2.9) = -2.0. Meaning of the parameters is as follows: number - Number to be truncated. The number parameter can have the following types: string - statically given pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. <function moreinfo="none">math_trunc</function> usage ... # Truncate a random number $avp(1) = "3.141492"; if (math_trunc("$avp(1)", "$avp(result)")) { xlog("Truncate result: $avp(result)\n"); } else { xlog("Truncate failed!\n"); } ...
<function moreinfo="none">math_floor(number, result_pvar)</function> Truncates a number, always towards -infinity. This means that floor(3.7) = 3.0 and floor(-2.9) = -3.0 Meaning of the parameters is as follows: number - Number to be truncated. The number parameter can have the following types: string - statically given pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. <function moreinfo="none">math_floor</function> usage ... # Truncate a random number $avp(1) = "3.141492"; if (math_floor("$avp(1)", "$avp(result)")) { xlog("Floor result: $avp(result)\n"); } else { xlog("Floor operation failed!\n"); } ...
<function moreinfo="none">math_ceil(number, result_pvar)</function> Truncates a number, always towards +infinity. This means that ceil(3.2) = 4.0 and ceil(-2.9) = -2.0 Meaning of the parameters is as follows: number - Number to be truncated. The number parameter can have the following types: string - statically given pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. This function can be used from any route. <function moreinfo="none">math_ceil</function> usage ... # Truncate a random number $avp(1) = "3.141492"; if (math_ceil("$avp(1)", "$avp(result)")) { xlog("Ceil result: $avp(result)\n"); } else { xlog("Ceil operation failed!\n"); } ...
<function moreinfo="none">math_round(number, result_pvar[, decimals])</function> The round function returns the nearest integer, and tie-breaking is done away from zero. Examples: round(1.2) = 1.0, round(0.5) = 1.0, round(-0.5) = -1.0 By default, the function returns an integer. An additional parameter controls the number of decimal digits of the initial number which will be kept. The rounding will then be done using the remaining decimal digits, and the result will be a float value, represented as a string. Meaning of the parameters is as follows: number - Number to be rounded. The number parameter can have the following types: string - statically given pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. decimals - (pvar / integer as a string) which further improves the precision of the rounding. This function can be used from any route. <function moreinfo="none">math_round</function> usage ... # Rounding PI $avp(1) = "3.141492"; if (math_round("$avp(1)", "$avp(result)")) { # result should be: 3 xlog("Round result: $avp(result)\n"); } else { xlog("Round operation failed!\n"); } ... if (math_round("$avp(1)", "$avp(result)", "4")) { # result should be: "3.1415" xlog("Round result: $avp(result)\n"); } else { xlog("Round operation failed!\n"); } ...
<function moreinfo="none">math_round_sf(number, result_pvar, figures)</function> To give a simple explanation, rounding to N significant figures is done by first obtaining the number resulted from keeping N significant figures (0 padded if necessary), then adjusting it if the N+1'th digit is greater or equal to 5. Some examples: round_sf(17892.987, 1) = 20000 round_sf(17892.987, 2) = 18000 round_sf(17892.987, 3) = 17900 round_sf(17892.987, 4) = 17890 round_sf(17892.987, 5) = 17893 round_sf(17892.987, 6) = 17893.0 round_sf(17892.987, 7) = 17892.99 Meaning of the parameters is as follows: number - Number to be rounded. The number parameter can have the following types: string - statically given pvar - value of an existing pseudo-variable (as string value - it makes no sense to truncate integers) result_pvar - pseudo-variable which will hold the result of the evaluation. Specified as a quoted string. figures - (pvar / integer as a string) which further improves the precision of the rounding. This function can be used from any route. <function moreinfo="none">math_round_sf</function> usage ... # Rounding PI $avp(1) = "3.141492"; if (math_round_sf("$avp(1)", "$avp(result)", "4")) { # result should be: "3.141" xlog("Round result: $avp(result)\n"); } else { xlog("Round operation failed!\n"); } ...
opensips-2.2.2/modules/mathops/math_funcs.c000066400000000000000000000243551300170765700210120ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-02-13: Created (Liviu) */ #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #define _ADDED_XOPEN #define _GNU_SOURCE #endif #include #include #ifdef _ADDED_XOPEN #undef _ADDED_XOPEN #undef _XOPEN_SOURCE #undef _GNU_SOURCE #endif #include #include #include "../../pvar.h" #include "../../dprint.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../trim.h" #include "tinyexpr.h" #include "math_funcs.h" static char print_buffer[MATHOP_REAL_DIGITS + MATHOP_DECIMAL_DIGITS]; static token stack[MAX_STACK_SIZE]; static token output[MAX_STACK_SIZE]; int top = 0; int pos = 0; static int push_number(double value) { if(top >= MAX_STACK_SIZE) { LM_ERR("RPN Stack Full\n"); return -1; } LM_DBG("push %f\n",value); stack[top].type = MATHOP_NUMBER; stack[top].value = value; top++; return 0; } static int pop_number(double *value) { if(top <= 0) { LM_ERR("RPN Stack Empty\n"); return -1; } top--; if(stack[top].type != MATHOP_NUMBER) { LM_ERR("RPN Stack Top is not a number\n"); return -1; } *value = stack[top].value; LM_DBG("pop = %f\n",*value); return 0; } static int rpn_eval(const token* t) { double o1, o2; switch (t->type) { case MATHOP_NUMBER: return push_number(t->value); case MATHOP_ADD: return pop_number(&o2) || pop_number(&o1) || push_number(o1 + o2); case MATHOP_SUB: return pop_number(&o2) || pop_number(&o1) || push_number(o1 - o2); case MATHOP_MUL: return pop_number(&o2) || pop_number(&o1) || push_number(o1 * o2); case MATHOP_DIV: return pop_number(&o2) || pop_number(&o1) || push_number(o1 / o2); case MATHOP_NEG: return pop_number(&o1) || push_number(-o1); case MATHOP_DROP: return pop_number(&o1); case MATHOP_DUP: if(pop_number(&o1)) return -1; return push_number(o1) || push_number(o1); case MATHOP_SWAP: return pop_number(&o2) || pop_number(&o1) || push_number(o2) || push_number(o1); case MATHOP_MOD: return pop_number(&o2) || pop_number(&o1) || push_number(fmod(o1,o2)); case MATHOP_POW: return pop_number(&o2) || pop_number(&o1) || push_number(pow(o1,o2)); case MATHOP_EXP: return pop_number(&o1) || push_number(exp(o1)); case MATHOP_LOG10: return pop_number(&o1) || push_number(log10(o1)); case MATHOP_LN: return pop_number(&o1) || push_number(log(o1)); case MATHOP_ABS: return pop_number(&o1) || push_number(fabs(o1)); case MATHOP_SQRT: return pop_number(&o1) || push_number(sqrt(o1)); case MATHOP_CBRT: return pop_number(&o1) || push_number(cbrt(o1)); case MATHOP_FLOOR: return pop_number(&o1) || push_number(floor(o1)); case MATHOP_CEIL: return pop_number(&o1) || push_number(ceil(o1)); case MATHOP_ROUND: return pop_number(&o1) || push_number(round(o1)); case MATHOP_NEARBYINT: return pop_number(&o1) || push_number(nearbyint(o1)); case MATHOP_TRUNC: return pop_number(&o1) || push_number(trunc(o1)); case MATHOP_E: return push_number(M_E); case MATHOP_PI: return push_number(M_PI); default: LM_WARN("Invalid RPN token type\n"); return -1; } } #define inc_and_trim(s) \ do { \ s.s++; \ s.len--; \ trim_leading(&s); \ } while (0) static inline void parse_word(str* _s, str* word) { trim_leading(_s); word->len = 0; word->s = _s->s; for(; _s->len > 0; _s->len--, _s->s++) { switch(*(_s->s)) { case ' ': case '\t': case '\r': case '\n': return; default: word->len++; break; } } } struct mathop_entry { str s; int op; }; const struct mathop_entry word_to_mathop[] = { {.s = { .len = 1, .s = "+" }, .op = MATHOP_ADD}, {.s = { .len = 1, .s = "-" }, .op = MATHOP_SUB}, {.s = { .len = 1, .s = "*" }, .op = MATHOP_MUL}, {.s = { .len = 1, .s = "/" }, .op = MATHOP_DIV}, {.s = { .len = 4, .s = "drop" }, .op = MATHOP_DROP}, {.s = { .len = 3, .s = "dup" }, .op = MATHOP_DUP}, {.s = { .len = 4, .s = "swap" }, .op = MATHOP_SWAP}, {.s = { .len = 3, .s = "mod" }, .op = MATHOP_MOD}, {.s = { .len = 3, .s = "pow" }, .op = MATHOP_POW}, {.s = { .len = 3, .s = "exp" }, .op = MATHOP_EXP}, {.s = { .len = 2, .s = "ln" }, .op = MATHOP_LN}, {.s = { .len = 3, .s = "log10" }, .op = MATHOP_LOG10}, {.s = { .len = 3, .s = "abs" }, .op = MATHOP_ABS}, {.s = { .len = 3, .s = "neg" }, .op = MATHOP_NEG}, {.s = { .len = 4, .s = "sqrt" }, .op = MATHOP_SQRT}, {.s = { .len = 4, .s = "cbrt" }, .op = MATHOP_CBRT}, {.s = { .len = 5, .s = "floor" }, .op = MATHOP_FLOOR}, {.s = { .len = 4, .s = "ceil" }, .op = MATHOP_CEIL}, {.s = { .len = 5, .s = "round" }, .op = MATHOP_ROUND}, {.s = { .len = 9, .s = "nearbyint" }, .op = MATHOP_NEARBYINT}, {.s = { .len = 5, .s = "trunc" }, .op = MATHOP_TRUNC}, {.s = { .len = 1, .s = "e" }, .op = MATHOP_E}, {.s = { .len = 2, .s = "pi" }, .op = MATHOP_PI}, {.s = { .len = 0, .s = NULL}, .op = -1} }; static int get_rpn_op(str *_s) { str word; const struct mathop_entry* j; trim_leading(_s); parse_word(_s,&word); if(word.len == 0) { return -1; } for( j = word_to_mathop; j->s.len > 0; j++ ) { if(j->s.len == word.len && !strncmp(j->s.s,word.s,j->s.len)) { return j->op; } } LM_WARN("Parse expr error: Invalid operator! <%.*s>\n", word.len, word.s); return -1; } static int parse_rpn(str *exp) { double d; char *p; int op; str s; p = exp->s; s.s = exp->s; s.len = exp->len; while (s.len) { if (*s.s >= '0' && *s.s <= '9') { errno = 0; d = strtod(s.s, &p); s.len -= p - s.s; s.s = p; if (errno == ERANGE) { LM_WARN("Overflow in parsing a numeric value!\n"); return -1; } output[pos].type = MATHOP_NUMBER; output[pos].value = d; pos++; } else { op = get_rpn_op(&s); if (op < 0) { return -1; } output[pos].type = op; pos++; } trim_leading(&s); } return 0; } /** * The function assumes that the 'output' buffer is properly written and * the 'pos' variable holds the size of the buffer */ static int evaluate_rpn_output(double *result) { int i; for (i = 0; i < pos; i++) { if(rpn_eval(output+i) < 0) { return -1; } } if (top != 1) { LM_ERR("Parse expr error: stack has %d elements\n",top); return -1; } return pop_number(result); } /** * Computes the result of a given RPN expression */ int evaluate_rpn(struct sip_msg *msg, str *exp, pv_spec_p result_var) { double result; pv_value_t pv_val; trim(exp); /* reset stack and output markers */ top = 0; pos = 0; if (parse_rpn(exp) != 0) { LM_ERR("Failed to parse RPN!\n"); return -1; } if (evaluate_rpn_output(&result) != 0) { LM_ERR("Mismatched tokens in expression: <%.*s>\n", exp->len, exp->s); return -1; } sprintf(print_buffer, "%.*lf", decimal_digits, result); pv_val.flags = PV_VAL_STR; pv_val.rs.s = print_buffer; pv_val.rs.len = strlen(print_buffer); if (pv_set_value(msg, result_var, 0, &pv_val) != 0) { LM_ERR("SET output value failed.\n"); return -1; } return 1; } /** * Computes the result of a given math expression */ int evaluate_exp(struct sip_msg *msg, str *exp, pv_spec_p result_var) { double result; int error; pv_value_t pv_val; trim(exp); result = te_interp(exp->s, &error); if (isnan(result)) { LM_ERR("Failed to run math expression: <%.*s>\n", exp->len, exp->s); return -1; } sprintf(print_buffer, "%.*lf", decimal_digits, result); pv_val.flags = PV_VAL_STR; pv_val.rs.s = print_buffer; pv_val.rs.len = strlen(print_buffer); if (pv_set_value(msg, result_var, 0, &pv_val) != 0) { LM_ERR("SET output value failed.\n"); return -1; } return 1; } /** * Basic rounding to nearest integer functions: floor, ceil, trunc */ int basic_round_op(struct sip_msg *msg, str *n, pv_spec_p result_var, double (*math_op)(double)) { double d; pv_value_t pv_val; errno = 0; d = strtod(n->s, NULL); if (errno == ERANGE) { LM_WARN("Overflow in parsing a numeric value!\n"); } pv_val.flags = PV_VAL_INT|PV_TYPE_INT; pv_val.ri = (int)math_op(d); if (pv_set_value(msg, result_var, 0, &pv_val) != 0) { LM_ERR("SET output value failed.\n"); return -1; } return 1; } /** * Rounds a number away from zero [ to the specified number of decimal digits ] */ int round_dp_op(struct sip_msg *msg, str *n, pv_spec_p result_var, int digits) { double d; pv_value_t pv_val; errno = 0; d = strtod(n->s, NULL); if (errno == ERANGE) { LM_WARN("Overflow in parsing a numeric value!\n"); } if (digits == 0) { pv_val.flags = PV_TYPE_INT|PV_VAL_INT; pv_val.ri = (int)round(d); } else { sprintf(print_buffer, "%.*lf", digits, d); pv_val.flags = PV_VAL_STR; pv_val.rs.s = print_buffer; pv_val.rs.len = strlen(print_buffer); } if (pv_set_value(msg, result_var, 0, &pv_val) != 0) { LM_ERR("SET output value failed.\n"); return -1; } return 1; } /** * Rounds a number to the given number of significant digits */ int round_sf_op(struct sip_msg *msg, str *n, pv_spec_p result_var, int digits) { double d, factor; pv_value_t pv_val; d = strtod(n->s, NULL); factor = pow(10.0, digits - ceil(log10(fabs(d)))); d = round(d * factor) / factor; sprintf(print_buffer, "%.*f", decimal_digits, d); pv_val.flags = PV_VAL_STR; pv_val.rs.s = print_buffer; pv_val.rs.len = strlen(print_buffer); if (pv_set_value(msg, result_var, 0, &pv_val) != 0) { LM_ERR("SET output value failed.\n"); return -1; } return 1; } opensips-2.2.2/modules/mathops/math_funcs.h000066400000000000000000000041551300170765700210130ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-02-13: Created (Liviu) */ #ifndef __MATHOPS_H__ #define __MATHOPS_H__ #include #include "tinyexpr.h" #define MAX_STACK_SIZE 100 #define MATHOP_PLUS '+' #define MATHOP_MINUS '-' #define MATHOP_MULT '*' #define MATHOP_SLASH '/' #define MATHOP_L_PAREN '(' #define MATHOP_R_PAREN ')' #define MATHOP_REAL_DIGITS 128 #define MATHOP_DECIMAL_DIGITS 128 extern int decimal_digits; enum { MATHOP_NUMBER = 0, MATHOP_LPAREN, MATHOP_ADD, MATHOP_SUB, MATHOP_MUL, MATHOP_DIV, MATHOP_DROP, MATHOP_DUP, MATHOP_SWAP, MATHOP_MOD, MATHOP_NEG, MATHOP_POW, MATHOP_EXP, MATHOP_LN, MATHOP_LOG10, MATHOP_ABS, MATHOP_SQRT, MATHOP_CBRT, MATHOP_FLOOR, MATHOP_CEIL, MATHOP_ROUND, MATHOP_NEARBYINT, MATHOP_TRUNC, MATHOP_E, MATHOP_PI }; typedef struct _token { int type; double value; } token; /** * Exported function headers */ int basic_round_op(struct sip_msg *msg, str *n, pv_spec_p result_var, double (*math_op)(double)); int round_dp_op(struct sip_msg *msg, str *n, pv_spec_p result_var, int digits); int round_sf_op(struct sip_msg *msg, str *n, pv_spec_p result_var, int digits); int evaluate_rpn(struct sip_msg *msg, str *exp, pv_spec_p result_var); int evaluate_exp(struct sip_msg *msg, str *exp, pv_spec_p result_var); #endif /* __MATHOPS_H__ */ opensips-2.2.2/modules/mathops/mathops.c000066400000000000000000000213671300170765700203360ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-02-13: Created (Liviu) */ #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #define _ADDED_XOPEN #define _GNU_SOURCE #endif #include #include #ifdef _ADDED_XOPEN #undef _ADDED_XOPEN #undef _XOPEN_SOURCE #undef _GNU_SOURCE #endif #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "math_funcs.h" /** * Module initialization function prototype */ static int mod_init(void); /** * Module parameter variables */ int decimal_digits = 6; /* default number of decimal digits written into pvs */ /** * Fixup functions */ static int fixup_evaluate_exp(void **param, int param_no); static int fixup_binary_op(void **param, int param_no); static int fixup_round_op(void **param, int param_no); /** * Function headers */ static int w_evaluate_exp(struct sip_msg *msg, char *exp, char *result); static int w_evaluate_rpn(struct sip_msg *msg, char *exp, char *result); static int w_basic_round_op(struct sip_msg *msg, char *number, char *result, double (*math_op)(double)); static int w_floor_op(struct sip_msg *msg, char *number, char *result); static int w_ceil_op(struct sip_msg *msg, char *number, char *result); static int w_trunc_op(struct sip_msg *msg, char *number, char *result); static int w_round_dp_op(struct sip_msg *msg, char *number, char *result, char *digits); static int w_round_sf_op(struct sip_msg *msg, char *number, char *result, char *digits); /** * Exported functions */ static cmd_export_t cmds[] = { {"math_eval",(cmd_function)w_evaluate_exp, 2, fixup_evaluate_exp, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, {"math_rpn",(cmd_function)w_evaluate_rpn, 2, fixup_evaluate_exp, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, {"math_floor",(cmd_function)w_floor_op, 2, fixup_binary_op, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, {"math_ceil",(cmd_function)w_ceil_op, 2, fixup_binary_op, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, {"math_trunc",(cmd_function)w_trunc_op, 2, fixup_binary_op, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, {"math_round",(cmd_function)w_round_dp_op, 2, fixup_binary_op, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, {"math_round",(cmd_function)w_round_dp_op, 3, fixup_round_op, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, {"math_round_sf",(cmd_function)w_round_sf_op, 3, fixup_round_op, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, {0, 0, 0, 0, 0, 0} }; /** * Exported parameters */ static param_export_t params[] = { {"decimal_digits", INT_PARAM, &decimal_digits}, {0, 0, 0} }; /** * Module parameter variables */ struct module_exports exports = { "mathops", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0 /* per-child init function */ }; static int mod_init(void) { LM_DBG("Initializing...\n"); LM_INFO("Module initialized!\n"); return 0; } /**************************** Fixup functions ********************************/ static int fixup_binary_op(void **param, int param_no) { pv_spec_p sp; str s; switch (param_no) { case 1: return fixup_sgp(param); case 2: if (!(sp = pkg_malloc(sizeof(*sp)))) { LM_ERR("No more pkg memory!\n"); return -1; } memset(sp, 0, sizeof(*sp)); s.s = (char *)*param; s.len = strlen(s.s); if (!pv_parse_spec(&s, sp)) { LM_ERR("Parameter 2 only accepts pvars! Given: <%.*s>\n", s.len, s.s); return -1; } *param = (void *)sp; return 0; default: LM_ERR("Invalid parameter number: %d\n", param_no); return E_UNSPEC; } } static int fixup_round_op(void **param, int param_no) { switch (param_no) { case 1: case 2: return fixup_binary_op(param, param_no); case 3: return fixup_igp(param); default: LM_ERR("Invalid parameter number: %d\n", param_no); return E_UNSPEC; } } static int fixup_evaluate_exp(void **param, int param_no) { pv_elem_p ep; pv_spec_p sp; str s; if (param_no != 1 && param_no != 2) { LM_ERR("Invalid parameter number: %d\n", param_no); return E_UNSPEC; } if (param_no == 1) { s.s = (char*)(*param); s.len = strlen(s.s); if (pv_parse_format(&s, &ep) < 0) { LM_ERR("wrong format[%.*s]\n", s.len, s.s); return E_UNSPEC; } *param = (void *)ep; return 0; } else { if (!(sp = pkg_malloc(sizeof(*sp)))) { LM_ERR("No more pkg memory!\n"); return -1; } memset(sp, 0, sizeof(*sp)); s.s = (char *)*param; s.len = strlen(s.s); if (!pv_parse_spec(&s, sp)) { LM_ERR("Parameter 2 only accepts pvars! Given: <%.*s>\n", s.len, s.s); return -1; } *param = (void *)sp; return 0; } } /**************************** Module functions *******************************/ static int w_evaluate_exp(struct sip_msg *msg, char *exp, char *result) { pv_elem_p exp_fmt = (pv_elem_p)exp; str s; if (pv_printf_s(msg, exp_fmt, &s) != 0) { LM_ERR("Failed to print the pv format string!\n"); return -1; } LM_DBG("Evaluating expression: %.*s\n", s.len, s.s); return evaluate_exp(msg, &s, (pv_spec_p)result); } static int w_evaluate_rpn(struct sip_msg *msg, char *exp, char *result) { pv_elem_p exp_fmt = (pv_elem_p)exp; str s; if (pv_printf_s(msg, exp_fmt, &s) != 0) { LM_ERR("Failed to print the pv format string!\n"); return -1; } LM_DBG("Evaluating expression: %.*s\n", s.len, s.s); return evaluate_rpn(msg, &s, (pv_spec_p)result); } static int w_floor_op(struct sip_msg *msg, char *number, char *result) { return w_basic_round_op(msg, number, result, floor); } static int w_ceil_op(struct sip_msg *msg, char *number, char *result) { return w_basic_round_op(msg, number, result, ceil); } static int w_trunc_op(struct sip_msg *msg, char *number, char *result) { return w_basic_round_op(msg, number, result, trunc); } static int w_basic_round_op(struct sip_msg *msg, char *number, char *result, double (*round_func)(double)) { str n; if (fixup_get_svalue(msg, (gparam_p)number, &n) != 0) { LM_ERR("Invalid number pseudo variable!\n"); return -1; } return basic_round_op(msg, &n, (pv_spec_p)result, round_func); } static int w_round_dp_op(struct sip_msg *msg, char *number, char *result, char *digits) { int d; str n; if (fixup_get_svalue(msg, (gparam_p)number, &n) != 0) { LM_ERR("Invalid number pseudo variable!\n"); return -1; } if (!digits) return round_dp_op(msg, &n, (pv_spec_p)result, 0); if (fixup_get_ivalue(msg, (gparam_p)digits, &d) != 0) { LM_ERR("Invalid digits pseudo variable!\n"); return -1; } return round_dp_op(msg, &n, (pv_spec_p)result, d); } static int w_round_sf_op(struct sip_msg *msg, char *number, char *result, char *digits) { int d; str n; if (fixup_get_svalue(msg, (gparam_p)number, &n) != 0) { LM_ERR("Invalid number pseudo variable!\n"); return -1; } if (!digits) return round_dp_op(msg, &n, (pv_spec_p)result, 0); if (fixup_get_ivalue(msg, (gparam_p)digits, &d) != 0) { LM_ERR("Invalid digits pseudo variable!\n"); return -1; } return round_sf_op(msg, &n, (pv_spec_p)result, d); } opensips-2.2.2/modules/mathops/tinyexpr - LICENSE.md000066400000000000000000000016711300170765700220770ustar00rootroot00000000000000TINYEXPR - Tiny recursive descent parser and evaluation engine in C Copyright (c) 2015, 2016 Lewis Van Winkle http://CodePlea.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgement in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. opensips-2.2.2/modules/mathops/tinyexpr.c000066400000000000000000000244541300170765700205450ustar00rootroot00000000000000/* * TINYEXPR - Tiny recursive descent parser and evaluation engine in C * * Copyright (c) 2015, 2016 Lewis Van Winkle * * http://CodePlea.com * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgement in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "tinyexpr.h" #include #include #include #include enum {TOK_NULL, TOK_END, TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_ADD, TOK_SUB, TOK_MUL, TOK_DIV, TOK_FUNCTION1, TOK_FUNCTION2, TOK_VARIABLE, TOK_ERROR}; typedef struct { const char *start; const char *next; int type; union {double value; te_fun1 f1; te_fun2 f2; const double *var;}; const te_variable *lookup; int lookup_len; } state; static te_expr *new_expr(te_expr *l, te_expr *r) { te_expr *ret = malloc(sizeof(te_expr)); ret->left = l; ret->right = r; ret->bound = 0; return ret; } void te_free(te_expr *n) { if (!n) return; if (n->left) te_free(n->left); if (n->right) te_free(n->right); free(n); } typedef struct { const char *name; te_fun1 f1; } builtin; static const builtin functions[] = { /* must be in alphabetical order */ {"abs", fabs}, {"acos", acos}, {"asin", asin}, {"atan", atan}, {"ceil", ceil}, {"cos", cos}, {"cosh", cosh}, {"exp", exp}, {"floor", floor}, {"ln", log}, {"log", log10}, {"sin", sin}, {"sinh", sinh}, {"sqrt", sqrt}, {"tan", tan}, {"tanh", tanh}, {0} }; static const builtin *find_function(const char *name, int len) { int imin = 0; int imax = sizeof(functions) / sizeof(builtin) - 2; /*Binary search.*/ while (imax >= imin) { const int i = (imin + ((imax-imin)/2)); int c = strncmp(name, functions[i].name, len); if (!c) c = '\0' - functions[i].name[len]; if (c == 0) { return functions + i; } else if (c > 0) { imin = i + 1; } else { imax = i - 1; } } return 0; } static const double *find_var(const state *s, const char *name, int len) { int i; if (!s->lookup) return 0; for (i = 0; i < s->lookup_len; ++i) { if (strncmp(name, s->lookup[i].name, len) == 0 && s->lookup[i].name[len] == '\0') { return s->lookup[i].value; } } return 0; } static double add(double a, double b) {return a + b;} static double sub(double a, double b) {return a - b;} static double mul(double a, double b) {return a * b;} static double divide(double a, double b) {return a / b;} static double negate(double a) {return -a;} void next_token(state *s) { s->type = TOK_NULL; if (!*s->next){ s->type = TOK_END; return; } do { /* Try reading a number. */ if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { s->value = strtod(s->next, (char**)&s->next); s->type = TOK_NUMBER; } else { /* Look for a variable or builtin function call. */ if (s->next[0] >= 'a' && s->next[0] <= 'z') { const char *start; start = s->next; while (s->next[0] >= 'a' && s->next[0] <= 'z') s->next++; const double *var = find_var(s, start, s->next - start); if (var) { s->type = TOK_VARIABLE; s->var = var; } else { if (s->next - start > 15) { s->type = TOK_ERROR; } else { s->type = TOK_FUNCTION1; const builtin *f = find_function(start, s->next - start); if (!f) { s->type = TOK_ERROR; } else { s->f1 = f->f1; } } } } else { /* Look for an operator or special character. */ switch (s->next++[0]) { case '+': s->type = TOK_FUNCTION2; s->f2 = add; break; case '-': s->type = TOK_FUNCTION2; s->f2 = sub; break; case '*': s->type = TOK_FUNCTION2; s->f2 = mul; break; case '/': s->type = TOK_FUNCTION2; s->f2 = divide; break; case '^': s->type = TOK_FUNCTION2; s->f2 = pow; break; case '%': s->type = TOK_FUNCTION2; s->f2 = fmod; break; case '(': s->type = TOK_OPEN; break; case ')': s->type = TOK_CLOSE; break; case ' ': case '\t': case '\n': case '\r': break; default: s->type = TOK_ERROR; break; } } } } while (s->type == TOK_NULL); } static te_expr *expr(state *s); static te_expr *power(state *s); static te_expr *base(state *s) { /* = | | | "(" ")" */ te_expr *ret; switch (s->type) { case TOK_NUMBER: ret = new_expr(0, 0); ret->value = s->value; next_token(s); break; case TOK_VARIABLE: ret = new_expr(0, 0); ret->bound = s->var; next_token(s); break; case TOK_FUNCTION1: ret = new_expr(0, 0); ret->f1 = s->f1; next_token(s); ret->left = power(s); break; case TOK_OPEN: next_token(s); ret = expr(s); if (s->type != TOK_CLOSE) { s->type = TOK_ERROR; } else { next_token(s); } break; default: ret = new_expr(0, 0); s->type = TOK_ERROR; ret->value = 0.0/0.0; break; } return ret; } static te_expr *power(state *s) { /* = {("-" | "+")} */ int sign = 1; while (s->type == TOK_FUNCTION2 && (s->f2 == add || s->f2 == sub)) { if (s->f2 == sub) sign = -sign; next_token(s); } te_expr *ret; if (sign == 1) { ret = base(s); } else { ret = new_expr(base(s), 0); ret->f1 = negate; } return ret; } static te_expr *factor(state *s) { /* = {"^" } */ te_expr *ret = power(s); while (s->type == TOK_FUNCTION2 && (s->f2 == pow)) { te_fun2 t = s->f2; next_token(s); ret = new_expr(ret, power(s)); ret->f2 = t; } return ret; } static te_expr *term(state *s) { /* = {("*" | "/" | "%") } */ te_expr *ret = factor(s); while (s->type == TOK_FUNCTION2 && (s->f2 == mul || s->f2 == divide || s->f2 == fmod)) { te_fun2 t = s->f2; next_token(s); ret = new_expr(ret, factor(s)); ret->f2 = t; } return ret; } static te_expr *expr(state *s) { /* = {("+" | "-") } */ te_expr *ret = term(s); while (s->type == TOK_FUNCTION2 && (s->f2 == add || s->f2 == sub)) { te_fun2 t = s->f2; next_token(s); ret = new_expr(ret, term(s)); ret->f2 = t; } return ret; } double te_eval(const te_expr *n) { double ret; if (n->bound) { ret = *n->bound; } else if (n->left == 0 && n->right == 0) { ret = n->value; } else if (n->left && n->right == 0) { ret = n->f1(te_eval(n->left)); } else { ret = n->f2(te_eval(n->left), te_eval(n->right)); } return ret; } static void optimize(te_expr *n) { /* Evaluates as much as possible. */ if (n->bound) return; if (n->left) optimize(n->left); if (n->right) optimize(n->right); if (n->left && n->right) { if (n->left->left == 0 && n->left->right == 0 && n->right->left == 0 && n->right->right == 0 && n->right->bound == 0 && n->left->bound == 0) { const double r = n->f2(n->left->value, n->right->value); free(n->left); free(n->right); n->left = 0; n->right = 0; n->value = r; } } else if (n->left && !n->right) { if (n->left->left == 0 && n->left->right == 0 && n->left->bound == 0) { const double r = n->f1(n->left->value); free(n->left); n->left = 0; n->value = r; } } } te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error) { state s; s.start = s.next = expression; s.lookup = variables; s.lookup_len = var_count; next_token(&s); te_expr *root = expr(&s); if (s.type != TOK_END) { te_free(root); if (error) { *error = (s.next - s.start); if (*error == 0) *error = 1; } return 0; } else { optimize(root); if (error) *error = 0; return root; } } double te_interp(const char *expression, int *error) { te_expr *n = te_compile(expression, 0, 0, error); double ret; if (n) { ret = te_eval(n); te_free(n); } else { ret = 0.0/0.0; } return ret; } static void pn (const te_expr *n, int depth) { printf("%*s", depth, ""); if (n->bound) { printf("bound %p\n", n->bound); } else if (n->left == 0 && n->right == 0) { printf("%f\n", n->value); } else if (n->left && n->right == 0) { printf("f1 %p\n", n->left); pn(n->left, depth+1); } else { printf("f2 %p %p\n", n->left, n->right); pn(n->left, depth+1); pn(n->right, depth+1); } } void te_print(const te_expr *n) { pn(n, 0); } opensips-2.2.2/modules/mathops/tinyexpr.h000066400000000000000000000040021300170765700205350ustar00rootroot00000000000000/* * TINYEXPR - Tiny recursive descent parser and evaluation engine in C * * Copyright (c) 2015, 2016 Lewis Van Winkle * * http://CodePlea.com * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgement in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef __TINYEXPR_H__ #define __TINYEXPR_H__ #ifdef __cplusplus extern "C" { #endif typedef double (*te_fun1)(double); typedef double (*te_fun2)(double, double); typedef struct te_expr { struct te_expr *left, *right; union {double value; te_fun1 f1; te_fun2 f2;}; const double *bound; } te_expr; typedef struct { const char *name; const double *value; } te_variable; /* Parses the input expression, evaluates it, and frees it. */ /* Returns NaN on error. */ double te_interp(const char *expression, int *error); /* Parses the input expression and binds variables. */ /* Returns NULL on error. */ te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error); /* Evaluates the expression. */ double te_eval(const te_expr *n); /* Prints debugging information on the syntax tree. */ void te_print(const te_expr *n); /* Frees the expression. */ /* This is safe to call on NULL pointers. */ void te_free(te_expr *n); #ifdef __cplusplus } #endif #endif /*__TINYEXPR_H__*/ opensips-2.2.2/modules/maxfwd/000077500000000000000000000000001300170765700163215ustar00rootroot00000000000000opensips-2.2.2/modules/maxfwd/Makefile000066400000000000000000000003221300170765700177560ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=maxfwd.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/maxfwd/README000066400000000000000000000077651300170765700172200ustar00rootroot00000000000000maxfwd Module Bogdan-Andrei Iancu Edited by Bogdan-Andrei Iancu Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. max_limit (integer) 1.4. Exported Functions 1.4.1. mf_process_maxfwd_header(max_value) 1.4.2. is_maxfwd_lt(max_value) List of Examples 1.1. Set max_limit parameter 1.2. mx_process_maxfwd_header usage 1.3. is_maxfwd_lt usage Chapter 1. Admin Guide 1.1. Overview The module implements all the operations regarding MaX-Forward header field, like adding it (if not present) or decrementing and checking the value of the existent one. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. max_limit (integer) Set an upper limit for the max-forward value in the outgoing requests. If the header is present, the decremented value is not allowed to exceed this max_limits - if it does, the header value will by decreased to “max_limitâ€. Note: This check is done when calling the mf_process_maxfwd_header() header. The range of values stretches from 1 to 256, which is the maximum MAX-FORWARDS value allowed by RFC 3261. Default value is “256â€. Example 1.1. Set max_limit parameter ... modparam("maxfwd", "max_limit", 32) ... 1.4. Exported Functions 1.4.1. mf_process_maxfwd_header(max_value) If no Max-Forward header is present in the received request, a header will be added having the original value equal with “max_valueâ€. If a Max-Forward header is already present, its value will be decremented (if not 0). Retuning codes: * 2 (true) - header was not found and a new header was successfully added. * 1 (true) - header was found and its value was successfully decremented (had a non-0 value). * -1 (false) - the header was found and its value is 0 (cannot be decremented). * -2 (false) - error during processing. The return code may be extensivly tested via script variable “retcode†(or “$?â€). Meaning of the parameters is as follows: * max_value - Value to be added if there is no Max-Forwards header field in the message. This function can be used from REQUEST_ROUTE. Example 1.2. mx_process_maxfwd_header usage ... # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10") && $retcode==-1) { sl_send_reply("483","Too Many Hops"); exit; }; ... 1.4.2. is_maxfwd_lt(max_value) Checks if the Max-Forward header value is less then the “max_value†parameter value. It considers also the value of the new inserted header (if locally added). Retuning codes: * 1 (true) - header was found or set and its value is stricly less than “max_valueâ€. * -1 (false) - the header was found or set and its value is greater or equal to “max_valueâ€. * -2 (false) - header was not found or not set. * -3 (false) - error during processing. The return code may be extensivly tested via script variable “retcode†(or “$?â€). Meaning of the parameters is as follows: * max_value - value to check the Max-Forward.value against (as less than). Example 1.3. is_maxfwd_lt usage ... # next hope is a gateway, so make no sens to # forward if MF is 0 (after decrement) if ( is_maxfwd_lt("1") ) { sl_send_reply("483","Too Many Hops"); exit; }; ... opensips-2.2.2/modules/maxfwd/doc/000077500000000000000000000000001300170765700170665ustar00rootroot00000000000000opensips-2.2.2/modules/maxfwd/doc/maxfwd.xml000066400000000000000000000020551300170765700211000ustar00rootroot00000000000000 %docentities; ]> maxfwd Module &osipsname; Bogdan-Andrei Iancu
bogdan@opensips.org
Bogdan-Andrei Iancu
bogdan@opensips.org
2003 &fhg; $Revision: 8740 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/maxfwd/doc/maxfwd_admin.xml000066400000000000000000000124251300170765700222520ustar00rootroot00000000000000 &adminguide;
Overview The module implements all the operations regarding MaX-Forward header field, like adding it (if not present) or decrementing and checking the value of the existent one.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>max_limit</varname> (integer) Set an upper limit for the max-forward value in the outgoing requests. If the header is present, the decremented value is not allowed to exceed this max_limits - if it does, the header value will by decreased to max_limit. Note: This check is done when calling the mf_process_maxfwd_header() header. The range of values stretches from 1 to 256, which is the maximum MAX-FORWARDS value allowed by RFC 3261. Default value is 256. Set <varname>max_limit</varname> parameter ... modparam("maxfwd", "max_limit", 32) ...
Exported Functions
<function moreinfo="none">mf_process_maxfwd_header(max_value)</function> If no Max-Forward header is present in the received request, a header will be added having the original value equal with max_value. If a Max-Forward header is already present, its value will be decremented (if not 0). Retuning codes: 2 (true) - header was not found and a new header was successfully added. 1 (true) - header was found and its value was successfully decremented (had a non-0 value). -1 (false) - the header was found and its value is 0 (cannot be decremented). -2 (false) - error during processing. The return code may be extensivly tested via script variable retcode (or $?). Meaning of the parameters is as follows: max_value - Value to be added if there is no Max-Forwards header field in the message. This function can be used from REQUEST_ROUTE. <function>mx_process_maxfwd_header</function> usage ... # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10") && $retcode==-1) { sl_send_reply("483","Too Many Hops"); exit; }; ...
<function moreinfo="none">is_maxfwd_lt(max_value)</function> Checks if the Max-Forward header value is less then the max_value parameter value. It considers also the value of the new inserted header (if locally added). Retuning codes: 1 (true) - header was found or set and its value is stricly less than max_value. -1 (false) - the header was found or set and its value is greater or equal to max_value. -2 (false) - header was not found or not set. -3 (false) - error during processing. The return code may be extensivly tested via script variable retcode (or $?). Meaning of the parameters is as follows: max_value - value to check the Max-Forward.value against (as less than). <function>is_maxfwd_lt</function> usage ... # next hope is a gateway, so make no sens to # forward if MF is 0 (after decrement) if ( is_maxfwd_lt("1") ) { sl_send_reply("483","Too Many Hops"); exit; }; ...
opensips-2.2.2/modules/maxfwd/maxfwd.c000066400000000000000000000111111300170765700177460ustar00rootroot00000000000000/* * MAXFWD module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module interface (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) * 2004-08-15 max value of max-fwd header is configurable via max_limit * module param (bogdan) * 2005-09-15 max_limit param cannot be disabled anymore (according to RFC) * (bogdan) * 2005-11-03 is_maxfwd_lt() function added; MF value saved in * msg->maxforwards->parsed (bogdan) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../mem/mem.h" #include "mf_funcs.h" #define MAXFWD_UPPER_LIMIT 256 static int max_limit = MAXFWD_UPPER_LIMIT; static int fixup_maxfwd_header(void** param, int param_no); static int w_process_maxfwd_header(struct sip_msg* msg,char* str,char* str2); static int is_maxfwd_lt(struct sip_msg *msg, char *slimit, char *foo); static int mod_init(void); static cmd_export_t cmds[]={ {"mf_process_maxfwd_header", (cmd_function)w_process_maxfwd_header, 1, fixup_maxfwd_header, 0, REQUEST_ROUTE}, {"is_maxfwd_lt", (cmd_function)is_maxfwd_lt, 1, fixup_maxfwd_header, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[]={ {"max_limit", INT_PARAM, &max_limit}, {0,0,0} }; #ifdef STATIC_MAXFWD struct module_exports maxfwd_exports = { #else struct module_exports exports= { #endif "maxfwd", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, 0, params, 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, (response_function) 0, (destroy_function) 0, 0 /* per-child init function */ }; static int mod_init(void) { LM_INFO("initializing...\n"); if ( max_limit<1 || max_limit>MAXFWD_UPPER_LIMIT ) { LM_ERR("invalid max limit (%d) [1,%d]\n", max_limit,MAXFWD_UPPER_LIMIT); return E_CFG; } return 0; } static int fixup_maxfwd_header(void** param, int param_no) { unsigned long code; int err; if (param_no==1){ code=str2s(*param, strlen(*param), &err); if (err==0){ if (code<1 || code>MAXFWD_UPPER_LIMIT){ LM_ERR("invalid MAXFWD number <%ld> [1,%d]\n", code,MAXFWD_UPPER_LIMIT); return E_UNSPEC; } if (code>max_limit) { LM_ERR("default value <%ld> bigger than max limit(%d)\n", code, max_limit); return E_UNSPEC; } pkg_free(*param); *param=(void*)code; return 0; }else{ LM_ERR("bad number <%s>\n",(char*)(*param)); return E_UNSPEC; } } return 0; } static int w_process_maxfwd_header(struct sip_msg* msg, char* str1,char* str2) { int val; str mf_value; val=is_maxfwd_present(msg, &mf_value); switch (val) { /* header not found */ case -1: if (add_maxfwd_header( msg, (unsigned int)(unsigned long)str1)!=0) goto error; return 2; /* error */ case -2: goto error; /* found */ case 0: return -1; default: if (val>max_limit){ LM_DBG("value %d decreased to %d\n", val, max_limit); val = max_limit+1; } if ( decrement_maxfwd(msg, val, &mf_value)!=0 ) { LM_ERR("decrement failed!\n"); goto error; } } return 1; error: return -2; } static int is_maxfwd_lt(struct sip_msg *msg, char *slimit, char *foo) { str mf_value; int limit; int val; limit = (int)(long)slimit; val = is_maxfwd_present( msg, &mf_value); LM_DBG("value = %d \n",val); if ( val<0 ) { /* error or not found */ return val-1; } else if ( val>=limit ) { /* greater or equal than/to limit */ return -1; } return 1; } opensips-2.2.2/modules/maxfwd/mf_funcs.c000066400000000000000000000076451300170765700203010ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ---------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2002-01-28 scratchpad removed (jiri) * 2004-08-15 max value of max-fwd header is configurable (bogdan) * 2005-11-03 MF value saved in msg->maxforwards->parsed (bogdan) */ #include #include #include "mf_funcs.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../data_lump.h" #define MF_HDR "Max-Forwards: " #define MF_HDR_LEN (sizeof(MF_HDR) - 1) /* do a tricky thing and keep the parsed value of MAXFWD hdr incremented * by one in order to make difference between 0 (not set) * and 0 (zero value) - bogdan */ #define IS_MAXWD_STORED(_msg_) \ ((_msg_)->maxforwards->parsed) #define STORE_MAXWD_VAL(_msg_,_val_) \ (_msg_)->maxforwards->parsed = ((void*)(long)((_val_)+1)) #define FETCH_MAXWD_VAL(_msg_) \ (((int)(long)(_msg_)->maxforwards->parsed)-1) /* looks for the MAX FORWARDS header returns the its value, -1 if is not present or -2 for error */ int is_maxfwd_present( struct sip_msg* msg , str *foo) { int x, err; /* lookup into the message for MAX FORWARDS header*/ if ( !msg->maxforwards ) { if ( parse_headers( msg , HDR_MAXFORWARDS_F, 0 )==-1 ){ LM_ERR("parsing MAX_FORWARD header failed!\n"); return -2; } if (!msg->maxforwards) { LM_DBG("max_forwards header not found!\n"); return -1; } } else if (IS_MAXWD_STORED(msg)) { trim_len( foo->len , foo->s , msg->maxforwards->body ); return FETCH_MAXWD_VAL(msg); } /* if header is present, trim to get only the string containing numbers */ trim_len( foo->len , foo->s , msg->maxforwards->body ); /* convert from string to number */ x = str2s( foo->s,foo->len,&err); if (err){ LM_ERR("unable to parse the max forwards number\n"); return -2; } /* store the parsed values */ STORE_MAXWD_VAL(msg, x); LM_DBG("value = %d \n",x); return x; } int decrement_maxfwd( struct sip_msg* msg , int x, str *s) { int i; /* decrement the value */ x--; /* update the stored value */ STORE_MAXWD_VAL(msg, x); /* rewriting the max-fwd value in the message (buf and orig) */ for(i = s->len - 1; i >= 0; i--) { s->s[i] = (x % 10) + '0'; x /= 10; if (x==0) { i = i - 1; break; } } while(i >= 0) s->s[i--] = ' '; return 0; } int add_maxfwd_header( struct sip_msg* msg , unsigned int val ) { unsigned int len; char *buf; struct lump* anchor; /* constructing the header */ len = MF_HDR_LEN /*"MAX-FORWARDS: "*/+ CRLF_LEN + 3/*val max on 3 digits*/; buf = (char*)pkg_malloc( len ); if (!buf) { LM_ERR("add_maxfwd_header: no more pkg memory\n"); goto error; } memcpy( buf , MF_HDR, MF_HDR_LEN ); len = MF_HDR_LEN ; len += btostr( buf+len , val ); memcpy( buf+len , CRLF , CRLF_LEN ); len +=CRLF_LEN; /*inserts the header at the beginning of the message*/ anchor = anchor_lump(msg, msg->headers->name.s - msg->buf, 0); if (anchor == 0) { LM_ERR("add_maxfwd_header: failed to get anchor\n"); goto error1; } if (insert_new_lump_before(anchor, buf, len, 0) == 0) { LM_ERR("add_maxfwd_header: failed to insert MAX-FORWARDS lump\n"); goto error1; } return 0; error1: pkg_free( buf ); error: return -1; } opensips-2.2.2/modules/maxfwd/mf_funcs.h000066400000000000000000000022271300170765700202750ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MF_FUNCS_H #define _MF_FUNCS_H #include "../../parser/msg_parser.h" #include "../../dprint.h" #include "../../config.h" #include "../../str.h" int decrement_maxfwd( struct sip_msg* msg, int nr_val, str *str_val ); int add_maxfwd_header( struct sip_msg* msg , unsigned int val ); int is_maxfwd_present( struct sip_msg* msg, str *mf_value ); #endif opensips-2.2.2/modules/mediaproxy/000077500000000000000000000000001300170765700172145ustar00rootroot00000000000000opensips-2.2.2/modules/mediaproxy/Makefile000066400000000000000000000003311300170765700206510ustar00rootroot00000000000000# $Id$ # # mediaproxy module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=mediaproxy.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/mediaproxy/README000066400000000000000000000300711300170765700200750ustar00rootroot00000000000000Mediaproxy Module Dan Pascu Edited by Dan Pascu Copyright © 2004 Dan Pascu Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Principle of operation 1.3. Features 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported parameters 1.5.1. disable (int) 1.5.2. mediaproxy_socket (string) 1.5.3. mediaproxy_timeout (int) 1.5.4. signaling_ip_avp (string) 1.5.5. media_relay_avp (string) 1.5.6. ice_candidate (string) 1.5.7. ice_candidate_avp (string) 1.6. Exported Functions 1.6.1. engage_media_proxy() 1.6.2. use_media_proxy() 1.6.3. end_media_session() List of Examples 1.1. Setting the disable parameter 1.2. Setting the mediaproxy_socket parameter 1.3. Setting the mediaproxy_timeout parameter 1.4. Setting the signaling_ip_avp parameter 1.5. Setting the media_relay_avp parameter 1.6. Setting the ice_candidate parameter 1.7. Setting the ice_candidate_avp parameter 1.8. Using the engage_media_proxy function 1.9. Using the use_media_proxy function 1.10. Using the end_media_session function Chapter 1. Admin Guide 1.1. Overview Mediaproxy is an OpenSIPS module that is designed to allow automatic NAT traversal for the majority of existing SIP clients. This means that there will be no need to configure anything in particular on the NAT box to allow these clients to work behind NAT when using the mediaproxy module. 1.2. Principle of operation This NAT traversal solution operates by placing a media relay in the middle between 2 SIP user-agents. It mangles the SDP messages for both of them in a way that will make the parties talk with the relay while they think they talk directly with each other. Mediaproxy consists of 2 components: * The OpenSIPS mediaproxy module * An external application called MediaProxy which employs a dispatcher and multiple distributed media relays. This is available from http://ag-projects.com/MediaProxy.html (version 2.0.0 or newer is required by this module). The mediaproxy dispatcher runs on the same machine as OpenSIPS and its purpose is to select a media relay for a call. The media relay may run on the same machine as the dispatcher or on multiple remote hosts and its purpose is to forward the streams between the calling parties. To find out more about the architecture of MediaProxy please read the documentation that comes with it. To be able to act as a relay between the 2 user agents, the machine(s) running the module/proxy server must have a public IP address. OpenSIPS will ask the media relay to allocate as many ports as there are media streams in the SDP offer and answer. The media relay will send back to OpenSIPS the IP address and port(s) for them. Then OpenSIPS will replace the original contact IP and RTP ports from the SDP messages with the ones provided by the media relay. By doing this, both user agents will try to contact the media relay instead of communicating directly with each other. Once the user agents contact the media relay, it will record the addresses they came from and will know where to forward packets received from the other endpoint. This is needed because the address/port the NAT box will allocate for the media streams is not known before they actually leave the NAT box. However the address of the media relay is always known (being a public IP) so the 2 endpoints know where to connect. After they do so, the relay learns their addresses and can forward packets between them. The SIP clients that will work transparently behind NAT when using mediaproxy, are the so-called symmetric clients. The symmetric clients have the particularity that use the same port to send and receive data. This must be true for both signaling and media for a client to work transparently with mediaproxy without any configuration on the NAT box. 1.3. Features * make symmetric clients work behind NAT transparently, with no configuration needed on the client's NAT box. * have the ability to distribute RTP traffic on multiple media relays running on multiple hosts. 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * dialog module - if engage_media_proxy is used (see below the description of engage_media_proxy). 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.5. Exported parameters 1.5.1. disable (int) Boolean flag that specifies if mediaproxy should be disabled. This is useful when you want to use the same OpenSIPS configuration in two different context, one using mediaproxy, the other not. In the case mediaproxy is disabled, calls to its functions will have no effect, allowing you to use the same configuration without changes. Default value is “0â€. Example 1.1. Setting the disable parameter ... modparam("mediaproxy", "disable", 1) ... 1.5.2. mediaproxy_socket (string) It is the path to the filesystem socket where the mediaproxy dispatcher listens for commands from the module. Default value is “/var/run/mediaproxy/dispatcher.sockâ€. Example 1.2. Setting the mediaproxy_socket parameter ... modparam("mediaproxy", "mediaproxy_socket", "/var/run/mediaproxy/dispatc her.sock") ... 1.5.3. mediaproxy_timeout (int) How much time (in milliseconds) to wait for an answer from the mediaproxy dispatcher. Default value is “500â€. Example 1.3. Setting the mediaproxy_timeout parameter ... modparam("mediaproxy", "mediaproxy_timeout", 500) ... 1.5.4. signaling_ip_avp (string) Specification of the AVP which holds the IP address from where the SIP signaling originated. If this AVP is set it will be used to get the signaling IP address, else the source IP address from where the SIP message was received will be used. This AVP is meant to be used in cases where there are more than one proxy in the call setup path and the proxy that actually starts mediaproxy doesn't receive the SIP messages directly from the UA and it cannot determine the NAT IP address from where the signaling originated. In such a case attaching a SIP header at the first proxy and then copying that header's value into the signaling_ip_avp on the proxy that starts mediaproxy will allow it to get the correct NAT IP address from where the SIP signaling originated. Default value is “$avp(signaling_ip)â€. Example 1.4. Setting the signaling_ip_avp parameter ... modparam("mediaproxy", "signaling_ip_avp", "$avp(nat_ip)") ... 1.5.5. media_relay_avp (string) Specification of the AVP which holds an optional application defined media relay IP address of a particular media relay that is preferred to be used for the current call. If an IP address is written to this AVP before calling use_media_proxy(), it will be preferred by the dispatcher over the normal selection algorithm. Default value is “$avp(media_relay)â€. Example 1.5. Setting the media_relay_avp parameter ... modparam("mediaproxy", "media_relay_avp", "$avp(media_relay)") ... 1.5.6. ice_candidate (string) Indicates the type of ICE candidate that will be added to the SDP. It can take 3 values: 'none', 'low-priority' or 'high-priority'. If 'none' is selected no candidate will be adeed to the SDP. If 'low-priority' is selected then a low priority candidate will be added and if 'high-priority' is selected a high priority one. Default value is “noneâ€. Example 1.6. Setting the ice_candidate parameter ... modparam("mediaproxy", "ice_candidate", "low-priority") ... 1.5.7. ice_candidate_avp (string) Specification of the AVP which holds the ICE candidate that will be inserted in the SDP. The value specified in this AVP will override the value in ice_candidate module parameter. Note that if use_media_proxy() and end_media_session() functions are being used, the AVP will not be available in the reply route unless you set onreply_avp_mode from the tm module to '1', and if the AVP is not set, the default value will be used. Default value is “$avp(ice_candidate)â€. Example 1.7. Setting the ice_candidate_avp parameter ... modparam("mediaproxy", "ice_candidate_avp", "$avp(ice_candidate)") ... 1.6. Exported Functions 1.6.1. engage_media_proxy() Trigger the use of MediaProxy for all the dialog requests and replies that have an SDP body. This needs to be called only once for the first INVITE in a dialog. After that it will use the dialog module to trace the dialog and automatically call use_media_proxy() on every request and reply that belongs to the dialog and has an SDP body. When the dialog ends it will also call automatically end_media_session(). All of these are called internally on dialog callbacks, so for this function to work, the dialog module must be loaded and configured. This function is an advanced mechanism to use a media relay without having to manually call a function on each message that belongs to the dialog. However this method is less flexible, because once things were set in motion by calling this function on the first INVITE, it cannot be stopped, not even by calling end_media_session(). It will only stop when the dialog ends. Until then it will modify the SDP content of every in-dialog message to make it use a media relay. If one needs more control over the process, like starting to use mediaproxy only later in the failure route, or stopping to use mediaproxy in the failure route, then the use_media_proxy and end_media_session functions should be used, and manually called as appropriate. Using this function should NOT be mixed with either of use_media_proxy() or end_media_session(). This function can be used from REQUEST_ROUTE. Example 1.8. Using the engage_media_proxy function ... if (method==INVITE && !has_totag()) { # We can also use a specific media relay if we need to #$avp(media_relay) = "1.2.3.4"; engage_media_proxy(); } ... 1.6.2. use_media_proxy() Will make a call to the dispatcher and replace the IPs and ports in the SDP body with the ones returned by the media relay for each supported media stream in the SDP body. This will force the media streams to be routed through the media relay. If a mix of supported and unsupported streams are present in the SDP, only the supported streams will be modified, while the unsupported streams will be left alone. This function should NOT be mixed with engage_media_proxy(). This function has the following return codes: * +1 - successfully modified message (true value) * -1 - error in processing message (false value) * -2 - missing SDP body, nothing to process (false value) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.9. Using the use_media_proxy function ... if (method==INVITE) { # We can also use a specific media relay if we need to #$avp(media_relay) = "1.2.3.4"; use_media_proxy(); } ... 1.6.3. end_media_session() Will call on the dispatcher to inform the media relay to end the media session. This is done when a call ends, to instruct the media relay to release the resources allocated to that call as well as to save logging information about the media session. Called on BYE, CANCEL or failures. This function should NOT be mixed with engage_media_proxy(). This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.10. Using the end_media_session function ... if (method==BYE) { end_media_session(); } ... opensips-2.2.2/modules/mediaproxy/doc/000077500000000000000000000000001300170765700177615ustar00rootroot00000000000000opensips-2.2.2/modules/mediaproxy/doc/mediaproxy.xml000066400000000000000000000017441300170765700226720ustar00rootroot00000000000000 %docentities; ]> Mediaproxy Module Dan Pascu dan@ag-projects.com Dan Pascu dan@ag-projects.com 2004 Dan Pascu $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/mediaproxy/doc/mediaproxy_admin.xml000066400000000000000000000370741300170765700240470ustar00rootroot00000000000000 &adminguide;
Overview Mediaproxy is an &osips; module that is designed to allow automatic NAT traversal for the majority of existing SIP clients. This means that there will be no need to configure anything in particular on the NAT box to allow these clients to work behind NAT when using the mediaproxy module.
Principle of operation This NAT traversal solution operates by placing a media relay in the middle between 2 SIP user-agents. It mangles the SDP messages for both of them in a way that will make the parties talk with the relay while they think they talk directly with each other. Mediaproxy consists of 2 components: The &osips; mediaproxy module An external application called MediaProxy which employs a dispatcher and multiple distributed media relays. This is available from http://ag-projects.com/MediaProxy.html (version 2.0.0 or newer is required by this module). The mediaproxy dispatcher runs on the same machine as &osips; and its purpose is to select a media relay for a call. The media relay may run on the same machine as the dispatcher or on multiple remote hosts and its purpose is to forward the streams between the calling parties. To find out more about the architecture of MediaProxy please read the documentation that comes with it. To be able to act as a relay between the 2 user agents, the machine(s) running the module/proxy server must have a public IP address. &osips; will ask the media relay to allocate as many ports as there are media streams in the SDP offer and answer. The media relay will send back to &osips; the IP address and port(s) for them. Then &osips; will replace the original contact IP and RTP ports from the SDP messages with the ones provided by the media relay. By doing this, both user agents will try to contact the media relay instead of communicating directly with each other. Once the user agents contact the media relay, it will record the addresses they came from and will know where to forward packets received from the other endpoint. This is needed because the address/port the NAT box will allocate for the media streams is not known before they actually leave the NAT box. However the address of the media relay is always known (being a public IP) so the 2 endpoints know where to connect. After they do so, the relay learns their addresses and can forward packets between them. The SIP clients that will work transparently behind NAT when using mediaproxy, are the so-called symmetric clients. The symmetric clients have the particularity that use the same port to send and receive data. This must be true for both signaling and media for a client to work transparently with mediaproxy without any configuration on the NAT box.
Features make symmetric clients work behind NAT transparently, with no configuration needed on the client's NAT box. have the ability to distribute RTP traffic on multiple media relays running on multiple hosts.
Dependencies
&osips; Modules The following modules must be loaded before this module: dialog module - if engage_media_proxy is used (see below the description of engage_media_proxy).
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported parameters
<varname>disable</varname> (int) Boolean flag that specifies if mediaproxy should be disabled. This is useful when you want to use the same &osips; configuration in two different context, one using mediaproxy, the other not. In the case mediaproxy is disabled, calls to its functions will have no effect, allowing you to use the same configuration without changes. Default value is 0. Setting the <varname>disable</varname> parameter ... modparam("mediaproxy", "disable", 1) ...
<varname>mediaproxy_socket</varname> (string) It is the path to the filesystem socket where the mediaproxy dispatcher listens for commands from the module. Default value is /var/run/mediaproxy/dispatcher.sock. Setting the <varname>mediaproxy_socket</varname> parameter ... modparam("mediaproxy", "mediaproxy_socket", "/var/run/mediaproxy/dispatcher.sock") ...
<varname>mediaproxy_timeout</varname> (int) How much time (in milliseconds) to wait for an answer from the mediaproxy dispatcher. Default value is 500. Setting the <varname>mediaproxy_timeout</varname> parameter ... modparam("mediaproxy", "mediaproxy_timeout", 500) ...
<varname>signaling_ip_avp</varname> (string) Specification of the AVP which holds the IP address from where the SIP signaling originated. If this AVP is set it will be used to get the signaling IP address, else the source IP address from where the SIP message was received will be used. This AVP is meant to be used in cases where there are more than one proxy in the call setup path and the proxy that actually starts mediaproxy doesn't receive the SIP messages directly from the UA and it cannot determine the NAT IP address from where the signaling originated. In such a case attaching a SIP header at the first proxy and then copying that header's value into the signaling_ip_avp on the proxy that starts mediaproxy will allow it to get the correct NAT IP address from where the SIP signaling originated. Default value is $avp(signaling_ip). Setting the <varname>signaling_ip_avp</varname> parameter ... modparam("mediaproxy", "signaling_ip_avp", "$avp(nat_ip)") ...
<varname>media_relay_avp</varname> (string) Specification of the AVP which holds an optional application defined media relay IP address of a particular media relay that is preferred to be used for the current call. If an IP address is written to this AVP before calling use_media_proxy(), it will be preferred by the dispatcher over the normal selection algorithm. Default value is $avp(media_relay). Setting the <varname>media_relay_avp</varname> parameter ... modparam("mediaproxy", "media_relay_avp", "$avp(media_relay)") ...
<varname>ice_candidate</varname> (string) Indicates the type of ICE candidate that will be added to the SDP. It can take 3 values: 'none', 'low-priority' or 'high-priority'. If 'none' is selected no candidate will be adeed to the SDP. If 'low-priority' is selected then a low priority candidate will be added and if 'high-priority' is selected a high priority one. Default value is none. Setting the <varname>ice_candidate</varname> parameter ... modparam("mediaproxy", "ice_candidate", "low-priority") ...
<varname>ice_candidate_avp</varname> (string) Specification of the AVP which holds the ICE candidate that will be inserted in the SDP. The value specified in this AVP will override the value in ice_candidate module parameter. Note that if use_media_proxy() and end_media_session() functions are being used, the AVP will not be available in the reply route unless you set onreply_avp_mode from the tm module to '1', and if the AVP is not set, the default value will be used. Default value is $avp(ice_candidate). Setting the <varname>ice_candidate_avp</varname> parameter ... modparam("mediaproxy", "ice_candidate_avp", "$avp(ice_candidate)") ...
Exported Functions
<function moreinfo="none">engage_media_proxy()</function> Trigger the use of MediaProxy for all the dialog requests and replies that have an SDP body. This needs to be called only once for the first INVITE in a dialog. After that it will use the dialog module to trace the dialog and automatically call use_media_proxy() on every request and reply that belongs to the dialog and has an SDP body. When the dialog ends it will also call automatically end_media_session(). All of these are called internally on dialog callbacks, so for this function to work, the dialog module must be loaded and configured. This function is an advanced mechanism to use a media relay without having to manually call a function on each message that belongs to the dialog. However this method is less flexible, because once things were set in motion by calling this function on the first INVITE, it cannot be stopped, not even by calling end_media_session(). It will only stop when the dialog ends. Until then it will modify the SDP content of every in-dialog message to make it use a media relay. If one needs more control over the process, like starting to use mediaproxy only later in the failure route, or stopping to use mediaproxy in the failure route, then the use_media_proxy and end_media_session functions should be used, and manually called as appropriate. Using this function should NOT be mixed with either of use_media_proxy() or end_media_session(). This function can be used from REQUEST_ROUTE. Using the <function>engage_media_proxy</function> function ... if (method==INVITE && !has_totag()) { # We can also use a specific media relay if we need to #$avp(media_relay) = "1.2.3.4"; engage_media_proxy(); } ...
<function moreinfo="none">use_media_proxy()</function> Will make a call to the dispatcher and replace the IPs and ports in the SDP body with the ones returned by the media relay for each supported media stream in the SDP body. This will force the media streams to be routed through the media relay. If a mix of supported and unsupported streams are present in the SDP, only the supported streams will be modified, while the unsupported streams will be left alone. This function should NOT be mixed with engage_media_proxy(). This function has the following return codes: +1 - successfully modified message (true value) -1 - error in processing message (false value) -2 - missing SDP body, nothing to process (false value) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Using the <function>use_media_proxy</function> function ... if (method==INVITE) { # We can also use a specific media relay if we need to #$avp(media_relay) = "1.2.3.4"; use_media_proxy(); } ...
<function moreinfo="none">end_media_session()</function> Will call on the dispatcher to inform the media relay to end the media session. This is done when a call ends, to instruct the media relay to release the resources allocated to that call as well as to save logging information about the media session. Called on BYE, CANCEL or failures. This function should NOT be mixed with engage_media_proxy(). This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Using the <function>end_media_session</function> function ... if (method==BYE) { end_media_session(); } ...
opensips-2.2.2/modules/mediaproxy/mediaproxy.c000066400000000000000000001624271300170765700215550ustar00rootroot00000000000000/* * Copyright (C) 2004-2008 Dan Pascu * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../str.h" #include "../../pvar.h" #include "../../error.h" #include "../../data_lump.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../trim.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../msg_translator.h" #include "../dialog/dlg_load.h" #include "../dialog/dlg_hash.h" #include "../tm/tm_load.h" #include "../tm/t_msgbuilder.h" #if defined(__GNUC__) && !defined(__STRICT_ANSI__) # define INLINE inline #else # define INLINE #endif #define SIGNALING_IP_AVP_SPEC "$avp(mp_signaling_ip)" #define MEDIA_RELAY_AVP_SPEC "$avp(mp_media_relay)" #define ICE_CANDIDATE_AVP_SPEC "$avp(mp_ice_candidate)" #define NO_CANDIDATE -1 // Although `AF_LOCAL' is mandated by POSIX.1g, `AF_UNIX' is portable to // more systems. `AF_UNIX' was the traditional name stemming from BSD, so // even most POSIX systems support it. It is also the name of choice in // the Unix98 specification. So if there's no AF_LOCAL fallback to AF_UNIX #ifndef AF_LOCAL # define AF_LOCAL AF_UNIX #endif // Solaris does not have the MSG_NOSIGNAL flag for the send(2) syscall #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 #endif #define isnulladdr(adr) ((adr).len==7 && memcmp("0.0.0.0", (adr).s, 7)==0) #define isnullport(port) ((port).len==1 && (port).s[0]=='0') #define STR_MATCH(str, buf) ((str).len==strlen(buf) && memcmp(buf, (str).s, (str).len)==0) #define STR_IMATCH(str, buf) ((str).len==strlen(buf) && strncasecmp(buf, (str).s, (str).len)==0) #define STR_HAS_PREFIX(str, prefix) ((str).len>=(prefix).len && memcmp((prefix).s, (str).s, (prefix).len)==0) #define STR_HAS_IPREFIX(str, prefix) ((str).len>=(prefix).len && strncasecmp((prefix).s, (str).s, (prefix).len)==0) typedef int Bool; #define True 1 #define False 0 typedef Bool (*NatTestFunction)(struct sip_msg *msg); typedef enum { TNone=0, TSupported, TUnsupported } TransportType; #define RETRY_INTERVAL 10 #define BUFFER_SIZE 8192 typedef struct MediaproxySocket { char *name; // name int sock; // socket int timeout; // how many milliseconds to wait for an answer time_t last_failure; // time of the last failure char data[BUFFER_SIZE]; // buffer for the answer data } MediaproxySocket; typedef struct { const char *name; uint32_t address; uint32_t mask; } NetInfo; typedef struct { str type; // stream type (`audio', `video', `image', ...) str ip; str port; str rtcp_ip; // pointer to the rtcp IP if explicitly specified by stream str rtcp_port; // pointer to the rtcp port if explicitly specified by stream str direction; Bool local_ip; // true if the IP is locally defined inside this media stream Bool has_ice; Bool has_rtcp_ice; TransportType transport; char *start_line; char *next_line; char *first_ice_candidate; } StreamInfo; #define MAX_STREAMS 32 typedef struct SessionInfo { str ip; str ip_line; // pointer to the whole session level ip line str direction; str separator; StreamInfo streams[MAX_STREAMS]; unsigned int stream_count; unsigned int supported_count; } SessionInfo; typedef struct AVP_Param { str spec; int name; unsigned short type; } AVP_Param; typedef struct ice_candidate_data { unsigned int priority; Bool skip_next_reply; } ice_candidate_data; // Function prototypes // static int EngageMediaProxy(struct sip_msg *msg); static int UseMediaProxy(struct sip_msg *msg); static int EndMediaSession(struct sip_msg *msg); static int mod_init(void); static int child_init(int rank); // Module global variables and state // static int mediaproxy_disabled = False; static str ice_candidate = str_init("none"); static MediaproxySocket mediaproxy_socket = { "/var/run/mediaproxy/dispatcher.sock", // name -1, // sock 500, // timeout in 500 milliseconds if there is no answer 0, // time of the last failure "" // data }; struct tm_binds tm_api; struct dlg_binds dlg_api; Bool have_dlg_api = False; // The AVP where the caller signaling IP is stored (if defined) static AVP_Param signaling_ip_avp = {str_init(SIGNALING_IP_AVP_SPEC), -1, 0}; // The AVP where the application-defined media relay IP is stored static AVP_Param media_relay_avp = {str_init(MEDIA_RELAY_AVP_SPEC), -1, 0}; // The AVP where the ICE candidate priority is stored (if defined) static AVP_Param ice_candidate_avp = {str_init(ICE_CANDIDATE_AVP_SPEC), -1, 0}; static cmd_export_t commands[] = { {"engage_media_proxy", (cmd_function)EngageMediaProxy, 0, 0, 0, REQUEST_ROUTE}, {"use_media_proxy", (cmd_function)UseMediaProxy, 0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE}, {"end_media_session", (cmd_function)EndMediaSession, 0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t parameters[] = { {"disable", INT_PARAM, &mediaproxy_disabled}, {"mediaproxy_socket", STR_PARAM, &(mediaproxy_socket.name)}, {"mediaproxy_timeout", INT_PARAM, &(mediaproxy_socket.timeout)}, {"signaling_ip_avp", STR_PARAM, &(signaling_ip_avp.spec.s)}, {"media_relay_avp", STR_PARAM, &(media_relay_avp.spec.s)}, {"ice_candidate", STR_PARAM, &(ice_candidate.s)}, {"ice_candidate_avp", STR_PARAM, &(ice_candidate_avp.spec.s)}, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_SILENT }, { MOD_TYPE_DEFAULT, "dialog", DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "mediaproxy", // module name MOD_TYPE_DEFAULT,// class of this module MODULE_VERSION, // module name DEFAULT_DLFLAGS, // dlopen flags &deps, // OpenSIPS module dependencies commands, // exported functions NULL, // exported async functions parameters, // exported parameters NULL, // exported statistics NULL, // exported MI functions NULL, // exported pseudo-variables NULL, // extra processes mod_init, // module init function (before fork. kids will inherit) NULL, // reply processing function NULL, // destroy function child_init // child init function }; // String processing functions // // strfind() finds the start of the first occurrence of the substring needle // of length nlen in the memory area haystack of length len. static void* strfind(const void *haystack, size_t len, const void *needle, size_t nlen) { char *sp; // Sanity check if(!(haystack && needle && nlen && len>=nlen)) return NULL; for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) { if (*sp == *(char*)needle && memcmp(sp, needle, nlen)==0) { return sp; } } return NULL; } // strcasefind() finds the start of the first occurrence of the substring // needle of length nlen in the memory area haystack of length len by doing // a case insensitive search static void* strcasefind(const char *haystack, size_t len, const char *needle, size_t nlen) { char *sp; // Sanity check if(!(haystack && needle && nlen && len>=nlen)) return NULL; for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) { if (tolower(*sp) == tolower(*(char*)needle) && strncasecmp(sp, needle, nlen)==0) { return sp; } } return NULL; } // returns a pointer to first CR or LF char found or the end of string static char* findendline(char *string, int len) { char *ptr = string; while(ptr - string < len && *ptr != '\n' && *ptr != '\r') ptr++; return ptr; } static int strtoint(str *data) { long int result; char c; // hack to avoid copying the string c = data->s[data->len]; data->s[data->len] = 0; result = strtol(data->s, NULL, 10); data->s[data->len] = c; return (int)result; } // find a line in str `block' that starts with `start'. static char* find_line_starting_with(str *block, char *start, int ignoreCase) { char *ptr, *bend; str zone; int tlen; bend = block->s + block->len; tlen = strlen(start); ptr = NULL; for (zone = *block; zone.len > 0; zone.len = bend - zone.s) { if (ignoreCase) ptr = strcasefind(zone.s, zone.len, start, tlen); else ptr = strfind(zone.s, zone.len, start, tlen); if (!ptr || ptr==block->s || ptr[-1]=='\n' || ptr[-1]=='\r') break; zone.s = ptr + tlen; } return ptr; } // count all lines in str `block' that starts with `start'. static unsigned int count_lines_starting_with(str *block, char *start, int ignoreCase) { char *ptr, *bend; str zone; int tlen; unsigned count; bend = block->s + block->len; tlen = strlen(start); count = 0; for (zone = *block; zone.len > 0; zone.len = bend - zone.s) { if (ignoreCase) ptr = strcasefind(zone.s, zone.len, start, tlen); else ptr = strfind(zone.s, zone.len, start, tlen); if (!ptr) break; if (ptr==block->s || ptr[-1]=='\n' || ptr[-1]=='\r') count++; zone.s = ptr + tlen; } return count; } // get up to `limit' whitespace separated tokens from `char *string' static int get_tokens(char *string, str *tokens, int limit) { int i, len, size; char *ptr; if (!string) { return 0; } len = strlen(string); for (ptr=string, i=0; i0; i++) { size = strspn(ptr, " \t\n\r"); ptr += size; len -= size; if (len <= 0) break; size = strcspn(ptr, " \t\n\r"); if (size==0) break; tokens[i].s = ptr; tokens[i].len = size; ptr += size; len -= size; } return i; } // get up to `limit' whitespace separated tokens from `str *string' static int get_str_tokens(str *string, str *tokens, int limit) { int count; char c; if (!string || !string->s) { return 0; } c = string->s[string->len]; string->s[string->len] = 0; count = get_tokens(string->s, tokens, limit); string->s[string->len] = c; return count; } // Functions to extract the info we need from the SIP/SDP message // static Bool get_callid(struct sip_msg* msg, str *cid) { if (msg->callid == NULL) { if (parse_headers(msg, HDR_CALLID_F, 0) == -1) { LM_ERR("cannot parse Call-ID header\n"); return False; } if (msg->callid == NULL) { LM_ERR("missing Call-ID header\n"); return False; } } *cid = msg->callid->body; trim(cid); return True; } static Bool get_cseq_number(struct sip_msg *msg, str *cseq) { struct cell *trans = tm_api.t_gett(); if (msg->first_line.type == SIP_REPLY && trans != NULL && trans != T_UNDEFINED && trans->uas.request!=NULL ) { *cseq = get_cseq(trans->uas.request)->number; } else { if (msg->cseq == NULL) { if (parse_headers(msg, HDR_CSEQ_F, 0)==-1) { LM_ERR("cannot parse CSeq header\n"); return False; } if (msg->cseq == NULL) { LM_ERR("missing CSeq header\n"); return False; } } *cseq = get_cseq(msg)->number; } return True; } static str get_from_uri(struct sip_msg *msg) { static str unknown = str_init("unknown"); str uri; char *ptr; if (parse_from_header(msg) < 0) { LM_ERR("cannot parse the From header\n"); return unknown; } uri = get_from(msg)->uri; if (uri.len == 0) return unknown; if (strncasecmp(uri.s, "sip:", 4)==0) { uri.s += 4; uri.len -= 4; } if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) { uri.len = ptr - uri.s; } return uri; } static str get_to_uri(struct sip_msg *msg) { static str unknown = str_init("unknown"); str uri; char *ptr; if (parse_headers(msg, HDR_TO_F, 0) == -1) { LM_ERR("failed to parse To header\n"); return unknown; } if (!msg->to) { LM_ERR("missing To header\n"); return unknown; } uri = get_to(msg)->uri; if (uri.len == 0) return unknown; if (strncasecmp(uri.s, "sip:", 4)==0) { uri.s += 4; uri.len -= 4; } if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) { uri.len = ptr - uri.s; } return uri; } static str get_from_tag(struct sip_msg *msg) { static str undefined = str_init(""); str tag; if (parse_from_header(msg) < 0) { LM_ERR("cannot parse the From header\n"); return undefined; } tag = get_from(msg)->tag_value; if (tag.len == 0) return undefined; return tag; } static str get_to_tag(struct sip_msg *msg) { static str undefined = str_init(""); str tag; if (msg->first_line.type==SIP_REPLY && msg->REPLY_STATUS<200) { // Ignore the To tag for provisional replies return undefined; } if (parse_headers(msg, HDR_TO_F, 0) == -1) { LM_ERR("failed to parse To header\n"); return undefined; } if (!msg->to) { LM_ERR("missing To header\n"); return undefined; } tag = get_to(msg)->tag_value; if (tag.len == 0) return undefined; return tag; } static str get_user_agent(struct sip_msg* msg) { static str unknown = str_init("unknown agent"); str block, server; char *ptr; if (parse_headers(msg, HDR_USERAGENT_F, 0)==0 && msg->user_agent && msg->user_agent->body.s && msg->user_agent->body.len>0) { return msg->user_agent->body; } // If we can't find user-agent, look after the `Server' header // This is a temporary hack. Normally it should be extracted by opensips. block.s = msg->buf; block.len = msg->len; ptr = find_line_starting_with(&block, "Server:", True); if (!ptr) return unknown; server.s = ptr + 7; server.len = findendline(server.s, block.s+block.len-server.s) - server.s; trim(&server); if (server.len == 0) return unknown; return server; } // Get caller signaling IP static str get_signaling_ip(struct sip_msg* msg) { int_str value; if (!search_first_avp(signaling_ip_avp.type | AVP_VAL_STR, signaling_ip_avp.name, &value, NULL) || value.s.s==NULL || value.s.len==0) { value.s.s = ip_addr2a(&msg->rcv.src_ip); value.s.len = strlen(value.s.s); } return value.s; } // Get the application-defined media_relay if defined static str get_media_relay(struct sip_msg* msg) { static str undefined = str_init(""); int_str value; if (!search_first_avp(media_relay_avp.type | AVP_VAL_STR, media_relay_avp.name, &value, NULL) || value.s.s==NULL || value.s.len==0) { return undefined; } return value.s; } // Functions to manipulate the SDP message body // static Bool check_content_type(struct sip_msg *msg) { str type; if (!msg->content_type) { LM_WARN("the Content-Type header is missing! Assume the content type is text/plain\n"); return True; } type = msg->content_type->body; trim(&type); if (strncasecmp(type.s, "application/sdp", 15) != 0) return False; if (!(isspace((int)type.s[15]) || type.s[15] == ';' || type.s[15] == 0)) { LM_ERR("invalid character after Content-Type: `%c'\n", type.s[15]); return False; } return True; } // Get the SDP message from SIP message and check it's Content-Type // Return values: // 1 - success // -1 - error in getting body or invalid content type // -2 - empty message static int get_sdp_message(struct sip_msg *msg, str *sdp) { if (get_body(msg, sdp)!=0 || sdp->len==0) return -2; if (!check_content_type(msg)) return -1; return 1; } // Return a str containing the line separator used in the SDP body static str get_sdp_line_separator(str *sdp) { char *ptr, *end_ptr, *sdp_end; str separator; sdp_end = sdp->s + sdp->len; ptr = find_line_starting_with(sdp, "v=", False); end_ptr = findendline(ptr, sdp_end-ptr); separator.s = ptr = end_ptr; while ((*ptr=='\n' || *ptr=='\r') && ptr 2) separator.len = 2; // safety check return separator; } // will return the direction attribute defined in the given block. // if missing, default is used if provided, else `sendrecv' is used. static str get_direction_attribute(str *block, str *default_direction) { str direction, zone, line; char *ptr; for (zone=*block;;) { ptr = find_line_starting_with(&zone, "a=", False); if (!ptr) { if (default_direction) return *default_direction; direction.s = "sendrecv"; direction.len = 8; return direction; } line.s = ptr + 2; line.len = findendline(line.s, zone.s + zone.len - line.s) - line.s; if (line.len==8) { if (strncasecmp(line.s, "sendrecv", 8)==0 || strncasecmp(line.s, "sendonly", 8)==0 || strncasecmp(line.s, "recvonly", 8)==0 || strncasecmp(line.s, "inactive", 8)==0) { return line; } } zone.s = line.s + line.len; zone.len = block->s + block->len - zone.s; } } // will return the rtcp port of the stream in the given block // if defined by the stream, otherwise will return {NULL, 0}. static str get_rtcp_port_attribute(str *block) { str zone, rtcp_port, undefined = {NULL, 0}; char *ptr; int count; ptr = find_line_starting_with(block, "a=rtcp:", False); if (!ptr) return undefined; zone.s = ptr + 7; zone.len = findendline(zone.s, block->s + block->len - zone.s) - zone.s; count = get_str_tokens(&zone, &rtcp_port, 1); if (count != 1) { LM_ERR("invalid `a=rtcp' line in SDP body\n"); return undefined; } return rtcp_port; } // will return the rtcp IP of the stream in the given block // if defined by the stream, otherwise will return {NULL, 0}. static str get_rtcp_ip_attribute(str *block) { str zone, tokens[4], undefined = {NULL, 0}; char *ptr; int count; ptr = find_line_starting_with(block, "a=rtcp:", False); if (!ptr) return undefined; zone.s = ptr + 7; zone.len = findendline(zone.s, block->s + block->len - zone.s) - zone.s; count = get_str_tokens(&zone, tokens, 4); if (count != 4) { return undefined; } return tokens[3]; } // will return true if the given block has both // a=ice-pwd and a=ice-ufrag attributes. static Bool has_ice_attributes(str *block) { char *ptr; ptr = find_line_starting_with(block, "a=ice-pwd:", False); if (ptr) { ptr = find_line_starting_with(block, "a=ice-ufrag:", False); if (ptr) { return True; } } return False; } // will return true if the given SDP has both // a=ice-pwd and a=ice-ufrag attributes at the // session level. static Bool has_session_ice_attributes(str *sdp) { str block; char *ptr; // session level ICE attributes can be found from the beginning up to the first media block ptr = find_line_starting_with(sdp, "m=", False); if (ptr) { block.s = sdp->s; block.len = ptr - block.s; } else { block = *sdp; } return has_ice_attributes(&block); } // will return true if the given block contains // a a=candidate attribute. This should be called // for a stream, as a=candidate attribute is not // allowed at the session level static Bool has_ice_candidates(str *block) { char *ptr; ptr = find_line_starting_with(block, "a=candidate:", False); if (ptr) { return True; } return False; } // will return true if given block contains an ICE // candidate with the given component ID static Bool has_ice_candidate_component(str *block, int id) { char *ptr, *block_end; int i, components, count; str chunk, zone, tokens[2]; block_end = block->s + block->len; components = count_lines_starting_with(block, "a=candidate:", False); for (i=0, chunk=*block; icseq) { LM_ERR("missing CSeq header\n"); return -1; } cseq = reply->cseq->parsed; return cseq->method_id; } static Bool supported_transport(str transport) { // supported transports: RTP/AVP, RTP/AVPF, RTP/SAVP, RTP/SAVPF, udp, udptl str prefixes[] = {str_init("RTP"), str_init("udp"), {NULL, 0}}; int i; for (i=0; prefixes[i].s != NULL; i++) { if (STR_HAS_IPREFIX(transport, prefixes[i])) { return True; } } return False; } static int get_session_info(str *sdp, SessionInfo *session) { str tokens[3], ip, ip_line, block, zone; char *ptr, *sdp_end; int i, count, result; count = count_lines_starting_with(sdp, "v=", False); if (count != 1) { LM_ERR("cannot handle more than 1 media session in SDP\n"); return -1; } count = count_lines_starting_with(sdp, "m=", False); if (count > MAX_STREAMS) { LM_ERR("cannot handle more than %d media streams in SDP\n", MAX_STREAMS); return -1; } memset(session, 0, sizeof(SessionInfo)); if (count == 0) return 0; if (!get_sdp_session_ip(sdp, &ip, &ip_line)) { LM_ERR("failed to parse the SDP message\n"); return -1; } ptr = memchr(ip.s, '/', ip.len); if (ptr) { LM_ERR("unsupported multicast IP specification in SDP: %.*s\n", ip.len, ip.s); return -1; } session->ip = ip; session->ip_line = ip_line; session->direction = get_session_direction(sdp); session->separator = get_sdp_line_separator(sdp); session->stream_count = count; sdp_end = sdp->s + sdp->len; for (i=0, block=*sdp; istreams[i].ip = session->ip; session->streams[i].local_ip = 0; } else { if (session->streams[i].transport == TSupported) { ptr = memchr(ip.s, '/', ip.len); if (ptr) { LM_ERR("unsupported multicast IP specification in stream nr %d: %.*s\n", i+1, ip.len, ip.s); return -1; } } session->streams[i].ip = ip; session->streams[i].local_ip = 1; } session->streams[i].rtcp_ip = get_rtcp_ip_attribute(&block); session->streams[i].rtcp_port = get_rtcp_port_attribute(&block); session->streams[i].direction = get_direction_attribute(&block, &session->direction); session->streams[i].has_ice = ((has_ice_attributes(&block) || has_session_ice_attributes(sdp)) && has_ice_candidates(&block)); session->streams[i].has_rtcp_ice = has_ice_candidate_component(&block, 2); session->streams[i].first_ice_candidate = find_line_starting_with(&block, "a=candidate:", False); } return session->stream_count; } static Bool insert_element(struct sip_msg *msg, char *position, char *element) { struct lump *anchor; char *buf; int len; len = strlen(element); buf = pkg_malloc(len); if (!buf) { LM_ERR("out of memory\n"); return False; } anchor = anchor_lump(msg, position - msg->buf, 0); if (!anchor) { LM_ERR("failed to get anchor for new element\n"); pkg_free(buf); return False; } memcpy(buf, element, len); if (insert_new_lump_after(anchor, buf, len, 0)==0) { LM_ERR("failed to insert new element\n"); pkg_free(buf); return False; } return True; } static Bool replace_element(struct sip_msg *msg, str *old_element, str *new_element) { struct lump *anchor; char *buf; if (new_element->len==old_element->len && memcmp(new_element->s, old_element->s, new_element->len)==0) { return True; } buf = pkg_malloc(new_element->len); if (!buf) { LM_ERR("out of memory\n"); return False; } anchor = del_lump(msg, old_element->s - msg->buf, old_element->len, 0); if (!anchor) { LM_ERR("failed to delete old element\n"); pkg_free(buf); return False; } memcpy(buf, new_element->s, new_element->len); if (insert_new_lump_after(anchor, buf, new_element->len, 0)==0) { LM_ERR("failed to insert new element\n"); pkg_free(buf); return False; } return True; } static Bool remove_element(struct sip_msg *msg, str *element) { if (!del_lump(msg, element->s - msg->buf, element->len, 0)) { LM_ERR("failed to delete old element\n"); return False; } return True; } // Functions dealing with the external mediaproxy helper // static Bool mediaproxy_connect(void) { struct sockaddr_un addr; if (mediaproxy_socket.sock >= 0) return True; if (mediaproxy_socket.last_failure + RETRY_INTERVAL > time(NULL)) return False; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; strncpy(addr.sun_path, mediaproxy_socket.name, sizeof(addr.sun_path) - 1); #ifdef HAVE_SOCKADDR_SA_LEN addr.sun_len = strlen(addr.sun_path); #endif mediaproxy_socket.sock = socket(AF_LOCAL, SOCK_STREAM, 0); if (mediaproxy_socket.sock < 0) { LM_ERR("can't create socket\n"); mediaproxy_socket.last_failure = time(NULL); return False; } if (connect(mediaproxy_socket.sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { LM_ERR("failed to connect to %s: %s\n", mediaproxy_socket.name, strerror(errno)); close(mediaproxy_socket.sock); mediaproxy_socket.sock = -1; mediaproxy_socket.last_failure = time(NULL); return False; } return True; } static void mediaproxy_disconnect(void) { if (mediaproxy_socket.sock < 0) return; close(mediaproxy_socket.sock); mediaproxy_socket.sock = -1; mediaproxy_socket.last_failure = time(NULL); } static char* send_command(char *command) { int cmd_len, bytes, tries, sent, received, count; struct timeval timeout; fd_set rset; if (!mediaproxy_connect()) return NULL; cmd_len = strlen(command); for (sent=0, tries=0; sentfirst_line.type == SIP_REQUEST) { type = "request"; } else if (msg->first_line.type == SIP_REPLY) { if (ice_data != NULL && ice_data->skip_next_reply) { // we don't process replies to ICE negotiation end requests // (those containing a=remote-candidates) ice_data->skip_next_reply = False; return -1; } type = "reply"; } else { return -1; } if (!get_callid(msg, &callid)) { LM_ERR("failed to get Call-ID\n"); return -1; } if (!get_cseq_number(msg, &cseq)) { LM_ERR("failed to get CSeq\n"); return -1; } status = get_sdp_message(msg, &sdp); // status = -1 is error, -2 is missing SDP body if (status == -1 || (status == -2 && msg->first_line.type == SIP_REQUEST)) { return status; } else if (status == -2 && !(msg->REPLY_STATUS == 200 && get_method_from_reply(msg) == METHOD_INVITE)) { return -2; } have_sdp = (status == 1); if (have_sdp) { if (msg->first_line.type == SIP_REQUEST && find_line_starting_with(&sdp, "a=remote-candidates", False)) { // we don't process requests with a=remote-candidates, this indicates the end of an ICE // negotiation and we must not mangle the SDP. if (ice_data != NULL) { ice_data->skip_next_reply = True; } return -1; } status = get_session_info(&sdp, &session); if (status < 0) { LM_ERR("can't extract media streams from the SDP message\n"); return -1; } if (session.supported_count == 0) return 1; // there are no supported media streams. we have nothing to do. len = sprintf(media_str, "%s", "media: "); for (i=0, str_buf.len=sizeof(media_str)-len-2, str_buf.s=media_str+len; i str_buf.len) { LM_ERR("media stream description is longer than %lu bytes\n", (unsigned long)sizeof(media_str)); return -1; } len = sprintf(str_buf.s, "%.*s:%.*s:%.*s:%.*s:%s,", stream.type.len, stream.type.s, stream.ip.len, stream.ip.s, stream.port.len, stream.port.s, stream.direction.len, stream.direction.s, stream.has_ice?"ice=yes":"ice=no"); str_buf.s += len; str_buf.len -= len; } *(str_buf.s-1) = 0; // remove the last comma sprintf(str_buf.s-1, "%s", "\r\n"); } else { media_str[0] = 0; } from_uri = get_from_uri(msg); to_uri = get_to_uri(msg); from_tag = get_from_tag(msg); to_tag = get_to_tag(msg); user_agent = get_user_agent(msg); signaling_ip = get_signaling_ip(msg); media_relay = get_media_relay(msg); len = snprintf(request, sizeof(request), "update\r\n" "type: %s\r\n" "dialog_id: %s\r\n" "call_id: %.*s\r\n" "cseq: %.*s\r\n" "from_uri: %.*s\r\n" "to_uri: %.*s\r\n" "from_tag: %.*s\r\n" "to_tag: %.*s\r\n" "user_agent: %.*s\r\n" "signaling_ip: %.*s\r\n" "media_relay: %.*s\r\n" "%s" "\r\n", type, dialog_id, callid.len, callid.s, cseq.len, cseq.s, from_uri.len, from_uri.s, to_uri.len, to_uri.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s, user_agent.len, user_agent.s, signaling_ip.len, signaling_ip.s, media_relay.len, media_relay.s, media_str); if (len >= sizeof(request)) { LM_ERR("mediaproxy request is longer than %lu bytes\n", (unsigned long)sizeof(request)); return -1; } result = send_command(request); if (result == NULL) return -1; if (!have_sdp) { // we updated the dispatcher, we can't do anything else as // there is no SDP return 1; } len = get_tokens(result, tokens, sizeof(tokens)/sizeof(str)); if (len == 0) { LM_ERR("empty response from mediaproxy\n"); return -1; } else if (len==1 && STR_MATCH(tokens[0], "error")) { LM_ERR("mediaproxy returned error\n"); return -1; } else if (lenfirst_line.type == SIP_REQUEST) { LM_ERR("insufficient ports returned from mediaproxy: got %d, " "expected %d\n", len-1, session.supported_count); return -1; } else { LM_WARN("broken client. Called UA added extra media stream(s) " "in the OK reply\n"); } } removed_session_ip = False; // only replace the session ip if there are no streams with unsupported // transports otherwise we insert an ip line in the supported streams // and remove the session level ip if (session.ip.s && !isnulladdr(session.ip)) { if (session.stream_count == session.supported_count) { if (!replace_element(msg, &session.ip, &tokens[0])) { LM_ERR("failed to replace session-level media IP in the SDP body\n"); return -1; } } else { if (!remove_element(msg, &session.ip_line)) { LM_ERR("failed to remove session-level media IP in the SDP body\n"); return -1; } removed_session_ip = True; } } for (i=0, j=1; i= len) { break; } if (!isnullport(stream.port)) { if (!replace_element(msg, &stream.port, &tokens[j])) { LM_ERR("failed to replace port in media stream number %d\n", i+1); return -1; } } if (stream.rtcp_port.len>0 && !isnullport(stream.rtcp_port)) { str rtcp_port; port = strtoint(&tokens[j]); rtcp_port.s = int2str(port+1, &rtcp_port.len); if (!replace_element(msg, &stream.rtcp_port, &rtcp_port)) { LM_ERR("failed to replace RTCP port in media stream number %d\n", i+1); return -1; } } if (stream.rtcp_ip.len > 0) { if (!replace_element(msg, &stream.rtcp_ip, &tokens[0])) { LM_ERR("failed to replace RTCP IP in media stream number %d\n", i+1); return -1; } } if (stream.local_ip && !isnulladdr(stream.ip)) { if (!replace_element(msg, &stream.ip, &tokens[0])) { LM_ERR("failed to replace IP address in media stream number %d\n", i+1); return -1; } } else if (!stream.local_ip && removed_session_ip) { strcpy(buf, "c=IN IP4 "); strncat(buf, tokens[0].s, tokens[0].len); strncat(buf, session.separator.s, session.separator.len); if (!insert_element(msg, stream.next_line, buf)) { LM_ERR("failed to insert IP address in media stream number %d\n", i+1); return -1; } } if (ice_data == NULL) { priority_str = get_ice_candidate(); } else if (ice_data->priority == NO_CANDIDATE) { priority_str.s = "none"; } else { // we don't need the string value, we'll use the number priority_str.s = ""; } priority_str.len = strlen(priority_str.s); if (stream.has_ice && stream.first_ice_candidate && !STR_IMATCH(priority_str, "none")) { // add some pseudo-random string to the foundation struct in_addr hexip; inet_aton(tokens[0].s, &hexip); unsigned int priority = (ice_data == NULL)?get_ice_candidate_priority(priority_str):ice_data->priority; port = strtoint(&tokens[j]); candidate.s = buf; candidate.len = sprintf(candidate.s, "a=candidate:R%x 1 UDP %u %.*s %i typ relay raddr %.*s rport %i%.*s", hexip.s_addr, priority, tokens[0].len, tokens[0].s, port, tokens[0].len, tokens[0].s, port, session.separator.len, session.separator.s); if (!insert_element(msg, stream.first_ice_candidate, candidate.s)) { LM_ERR("failed to insert ICE candidate in media stream number %d\n", i+1); return -1; } if (stream.has_rtcp_ice) { candidate.s = buf; candidate.len = sprintf(candidate.s, "a=candidate:R%x 2 UDP %u %.*s %i typ relay raddr %.*s rport %i%.*s", hexip.s_addr, priority-1, tokens[0].len, tokens[0].s, port+1, tokens[0].len, tokens[0].s, port+1, session.separator.len, session.separator.s); if (!insert_element(msg, stream.first_ice_candidate, candidate.s)) { LM_ERR("failed to insert ICE candidate in media stream number %d\n", i+1); return -1; } } } j++; } return 1; } static int end_media_session(str callid, str from_tag, str to_tag) { char request[2048], *result; int len; len = snprintf(request, sizeof(request), "remove\r\n" "call_id: %.*s\r\n" "from_tag: %.*s\r\n" "to_tag: %.*s\r\n" "\r\n", callid.len, callid.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s); if (len >= sizeof(request)) { LM_ERR("mediaproxy request is longer than %lu bytes\n", (unsigned long)sizeof(request)); return -1; } result = send_command(request); return result==NULL ? -1 : 1; } // Dialog callbacks and helpers // typedef enum { MPInactive = 0, MPActive } MediaProxyState; static INLINE char* get_dialog_id(struct dlg_cell *dlg) { static char buffer[64]; snprintf(buffer, sizeof(buffer), "%d:%d", dlg->h_entry, dlg->h_id); return buffer; } static void __free_dialog_data(void *data) { shm_free((ice_candidate_data*)data); } static void __dialog_requests(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { use_media_proxy(_params->msg, get_dialog_id(dlg), (ice_candidate_data*)*_params->param); } static void __dialog_replies(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { struct sip_msg *reply = _params->msg; if (reply == FAKED_REPLY) return; if (reply->REPLY_STATUS>100 && reply->REPLY_STATUS<300) { use_media_proxy(reply, get_dialog_id(dlg), (ice_candidate_data*)*_params->param); } } static void __dialog_ended(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { if ((int)(long)*_params->param == MPActive) { end_media_session(dlg->callid, dlg->legs[DLG_CALLER_LEG].tag, dlg->legs[DLG_FIRST_CALLEE_LEG].tag); *_params->param = (void *)MPInactive; } } // TM callbacks // static void __tm_request_in(struct cell *trans, int type, struct tmcb_params *param) { struct sip_msg *request = param->req; struct dlg_cell *dlg; ice_candidate_data *ice_data; if ((request->msg_flags & FL_USE_MEDIA_PROXY) == 0) return; if (dlg_api.create_dlg(request, 0) < 0) { LM_ERR("could not create new dialog\n"); return; } dlg = dlg_api.get_dlg(); if (!dlg) { LM_CRIT("error getting dialog\n"); return; } ice_data = (ice_candidate_data*)shm_malloc(sizeof(ice_candidate_data)); if (!ice_data) { LM_ERR("failed to allocate shm memory for ice_candidate_data\n"); return; } ice_data->priority = get_ice_candidate_priority(get_ice_candidate()); ice_data->skip_next_reply = False; if (dlg_api.register_dlgcb(dlg, DLGCB_REQ_WITHIN, __dialog_requests, (void*)ice_data, __free_dialog_data) != 0) LM_ERR("cannot register callback for in-dialog requests\n"); if (dlg_api.register_dlgcb(dlg, DLGCB_RESPONSE_FWDED | DLGCB_RESPONSE_WITHIN, __dialog_replies, (void*)ice_data, NULL) != 0) LM_ERR("cannot register callback for dialog and in-dialog replies\n"); if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED | DLGCB_DESTROY, __dialog_ended, (void*)MPActive, NULL) != 0) LM_ERR("cannot register callback for dialog termination\n"); use_media_proxy(request, get_dialog_id(dlg), ice_data); } // // The public functions that are exported by this module // static int EngageMediaProxy(struct sip_msg *msg) { str totag; if (mediaproxy_disabled) return -1; if (!have_dlg_api) { LM_ERR("engage_media_proxy requires the TM and dialog modules to be loaded\n"); return -1; } if (msg->first_line.type!=SIP_REQUEST || msg->REQ_METHOD!=METHOD_INVITE) { LM_ERR("engage_media_proxy should only be called for initial INVITE requests\n"); return -1; } if (parse_headers(msg, HDR_TO_F, 0) == -1) { LM_ERR("failed to parse To header\n"); return -1; } if (!msg->to) { LM_ERR("missing To header\n"); return -1; } totag = get_to(msg)->tag_value; if (totag.s && totag.len>0) { LM_ERR("engage_media_proxy should only be called for initial INVITE requests\n"); return -1; } msg->msg_flags |= FL_USE_MEDIA_PROXY; return 1; } static int UseMediaProxy(struct sip_msg *msg) { if (mediaproxy_disabled) return -1; return use_media_proxy(msg, "", NULL); } static int EndMediaSession(struct sip_msg *msg) { str callid, from_tag, to_tag; if (mediaproxy_disabled) return -1; if (!get_callid(msg, &callid)) { LM_ERR("failed to get Call-ID\n"); return -1; } from_tag = get_from_tag(msg); to_tag = get_to_tag(msg); return end_media_session(callid, from_tag, to_tag); } // // Module management: initialization/destroy/function-parameter-fixing/... // static int mod_init(void) { pv_spec_t avp_spec; // initialize the signaling_ip_avp structure if (signaling_ip_avp.spec.s==NULL || *(signaling_ip_avp.spec.s)==0) { LM_WARN("missing/empty signaling_ip_avp parameter. will use default.\n"); signaling_ip_avp.spec.s = SIGNALING_IP_AVP_SPEC; } signaling_ip_avp.spec.len = strlen(signaling_ip_avp.spec.s); if (pv_parse_spec(&(signaling_ip_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_CRIT("invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &(signaling_ip_avp.name), &(signaling_ip_avp.type))!=0) { LM_CRIT("invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s); return -1; } // initialize the media_relay_avp structure if (media_relay_avp.spec.s==NULL || *(media_relay_avp.spec.s)==0) { LM_WARN("missing/empty media_relay_avp parameter. will use default.\n"); media_relay_avp.spec.s = MEDIA_RELAY_AVP_SPEC; } media_relay_avp.spec.len = strlen(media_relay_avp.spec.s); if (pv_parse_spec(&(media_relay_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_CRIT("invalid AVP specification for media_relay_avp: `%s'\n", media_relay_avp.spec.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &(media_relay_avp.name), &(media_relay_avp.type))!=0) { LM_CRIT("invalid AVP specification for media_relay_avp: `%s'\n", media_relay_avp.spec.s); return -1; } // initialize the ice_candidate_avp structure if (ice_candidate_avp.spec.s==NULL || *(ice_candidate_avp.spec.s)==0) { LM_WARN("missing/empty ice_candidate_avp parameter. will use default.\n"); ice_candidate_avp.spec.s = ICE_CANDIDATE_AVP_SPEC; } ice_candidate_avp.spec.len = strlen(ice_candidate_avp.spec.s); if (pv_parse_spec(&(ice_candidate_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_CRIT("invalid AVP specification for ice_candidate_avp: `%s'\n", ice_candidate_avp.spec.s); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &(ice_candidate_avp.name), &(ice_candidate_avp.type))!=0) { LM_CRIT("invalid AVP specification for ice_candidate_avp: `%s'\n", ice_candidate_avp.spec.s); return -1; } // initialize ice_candidate module parameter ice_candidate.len = strlen(ice_candidate.s); if (!STR_IMATCH(ice_candidate, "none") && !STR_IMATCH(ice_candidate, "low-priority") && !STR_IMATCH(ice_candidate, "high-priority")) { LM_CRIT("invalid value specified for ice_candidate: `%s'\n", ice_candidate.s); return -1; } // bind to the TM and dialog APIs if (load_tm_api(&tm_api)==0 && load_dlg_api(&dlg_api)==0) { have_dlg_api = True; // register callback for incoming requests if (tm_api.register_tmcb(0, 0, TMCB_REQUEST_IN, __tm_request_in, 0, 0) <= 0) { LM_CRIT("cannot register TM callback for incoming INVITE request\n"); return -1; } } else { LM_NOTICE("engage_media_proxy() will not work because the TM/dialog modules are not loaded\n"); } return 0; } static int child_init(int rank) { // initialize the connection to mediaproxy if needed if (!mediaproxy_disabled) mediaproxy_connect(); return 0; } opensips-2.2.2/modules/mi_datagram/000077500000000000000000000000001300170765700173005ustar00rootroot00000000000000opensips-2.2.2/modules/mi_datagram/Makefile000066400000000000000000000003261300170765700207410ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=mi_datagram.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/mi_datagram/README000066400000000000000000000136611300170765700201670ustar00rootroot00000000000000mi_datagram Module Andreea-Ancuta Onofrei Edited by Andreea-Ancuta Onofrei Copyright © 2007 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. DATAGRAM command syntax 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. socket_name (string) 1.4.2. children_count (string) 1.4.3. unix_socket_mode (integer) 1.4.4. unix_socket_group (integer) unix_socket_group (string) 1.4.5. unix_socket_user (integer) unix_socket_group (string) 1.4.6. socket_timeout (integer) 1.4.7. reply_indent (string) 1.5. Exported Functions 1.6. Example 2. Frequently Asked Questions List of Examples 1.1. Set socket_name parameter 1.2. Set children_count parameter 1.3. Set unix_socket_mode parameter 1.4. Set unix_socket_group parameter 1.5. Set unix_socket_user parameter 1.6. Set socket_timeout parameter 1.7. Set reply_indent parameter 1.8. DATAGRAM request Chapter 1. Admin Guide 1.1. Overview This is a module which provides a UNIX/UDP SOCKET transport layer implementation for the Management Interface. 1.2. DATAGRAM command syntax The external commands issued via DATAGRAM interface must follow the following syntax: * request = first_line (argument '\n')* * first_line = ':'command_name':''\n' * argument = (arg_name '::' (arg_value)? ) | (arg_value) * arg_name = not-quoted_string * arg_value = not-quoted_string | '"' string '"' * not-quoted_string = string - {',",\n,\r} 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.4. Exported Parameters 1.4.1. socket_name (string) The name of a UNIX SOCKET or an IP address. The UNIX datagram or UDP socket will be created using this parameter in order to read the external commands. Both IPv4 and IPv6 are supported. Default value is NONE. Example 1.1. Set socket_name parameter ... modparam("mi_datagram", "socket_name", "/tmp/opensips.sock") ... modparam("mi_datagram", "socket_name", "udp:192.168.2.133:8080") ... 1.4.2. children_count (string) The number of child processes to be created. Each child process will be a datagram server. Default value is 1. Example 1.2. Set children_count parameter ... modparam("mi_datagram", "children_count", 3) ... 1.4.3. unix_socket_mode (integer) Permission to be used for creating the listening UNIX datagram socket. Not necessary for a UDP socket. It follows the UNIX conventions. Default value is 0660 (rw-rw----). Example 1.3. Set unix_socket_mode parameter ... modparam("mi_datagram", "unix_socket_mode", 0600) ... 1.4.4. unix_socket_group (integer) unix_socket_group (string) Group to be used for creating the listening UNIX socket. Default value is the inherited one. Example 1.4. Set unix_socket_group parameter ... modparam("mi_datagram", "unix_socket_group", 0) modparam("mi_datagram", "unix_socket_group", "root") ... 1.4.5. unix_socket_user (integer) unix_socket_group (string) User to be used for creating the listening UNIX socket. Default value is the inherited one. Example 1.5. Set unix_socket_user parameter ... modparam("mi_datagram", "unix_socket_user", 0) modparam("mi_datagram", "unix_socket_user", "root") ... 1.4.6. socket_timeout (integer) The reply will expire after trying to sent it for socket_timeout milliseconds. Default value is 2000. Example 1.6. Set socket_timeout parameter ... modparam("mi_datagram", "socket_timeout", 2000) ... 1.4.7. reply_indent (string) Strings to be used for line indentation. As the MI data structure is tree oriendeted, the depth level will printed as indentation. Default value is “"\t" (TAB)â€. Example 1.7. Set reply_indent parameter ... modparam("mi_datagram", "reply_indent", " ") ... 1.5. Exported Functions No function exported to be used from configuration file. 1.6. Example This is an example showing the DATAGRAM format for the “get_statistics dialog: tm:†MI commad: response. Example 1.8. DATAGRAM request :get_statistics:\n dialog:\n tm:\n Chapter 2. Frequently Asked Questions 2.1. Both UNIX and UDP type of socket can be created simultaneusly? This version supports only one kind of socket at a time. If there are more than one value set for socket_name the last one will take effect. 2.2. Is there a limit in the datagram request's size? The maximum length of a datagram request or reply is 65457 bytes. 2.3. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.4. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.5. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/mi_datagram/datagram_fnc.c000066400000000000000000000333721300170765700220620ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2007-06-25 first version (ancuta) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../resolve.h" #include "mi_datagram.h" #include "datagram_fnc.h" #include "mi_datagram_parser.h" #include "mi_datagram_writer.h" /* solaris doesn't have SUN_LEN */ #ifndef SUN_LEN #define SUN_LEN(sa) ( strlen((sa)->sun_path) + \ (size_t)(((struct sockaddr_un*)0)->sun_path) ) #endif /* AF_LOCAL is not defined on solaris */ #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif int flags; static char *mi_buf = 0; typedef union { struct sockaddr_un un; struct sockaddr_in in; } reply_addr_t; typedef struct{ reply_addr_t address; int address_len; int tx_sock; }my_socket_address; static reply_addr_t reply_addr; static unsigned int reply_addr_len; /* Timeout for sending replies in milliseconds */ extern int mi_socket_timeout; static unsigned int mi_socket_domain; static int mi_sock_check(int fd, char* fname); int mi_init_datagram_server(sockaddr_dtgram *addr, unsigned int socket_domain, rx_tx_sockets * socks, int mode, int uid, int gid ) { char * socket_name; /* create sockets rx and tx ... */ /***********************************/ mi_socket_domain = socket_domain; /**********************************/ socks->rx_sock = socket(socket_domain, SOCK_DGRAM, 0); if (socks->rx_sock == -1) { LM_ERR("cannot create RX socket: %s\n", strerror(errno)); return -1; } switch(socket_domain) { case AF_LOCAL: LM_DBG("we have a unix socket: %s\n", addr->unix_addr.sun_path); socket_name = addr->unix_addr.sun_path; if(bind(socks->rx_sock,(struct sockaddr*)&addr->unix_addr, SUN_LEN(&addr->unix_addr))< 0) { LM_ERR("bind: %s\n", strerror(errno)); goto err_rx; } if(mi_sock_check(socks->rx_sock, socket_name)!=0) goto err_rx; /* change permissions */ if (mode){ if (chmod(socket_name, mode)<0){ LM_ERR("failed to change the permissions for %s to %04o:" "%s[%d]\n",socket_name, mode, strerror(errno), errno); goto err_rx; } } /* change ownership */ if ((uid!=-1) || (gid!=-1)){ if (chown(socket_name, uid, gid)<0){ LM_ERR("failed to change the owner/group for %s to %d.%d;" "%s[%d]\n",socket_name, uid, gid, strerror(errno), errno); goto err_rx; } } /* create TX socket */ socks->tx_sock = socket( socket_domain, SOCK_DGRAM, 0); if (socks->tx_sock == -1) { LM_ERR("cannot create socket: %s\n", strerror(errno)); goto err_rx; }; /* Turn non-blocking mode on for tx*/ flags = fcntl(socks->tx_sock, F_GETFL); if (flags == -1) { LM_ERR("fcntl failed: %s\n", strerror(errno)); goto err_both; } if (fcntl(socks->tx_sock, F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("fcntl: set non-blocking failed: %s\n",strerror(errno)); goto err_both; } break; case AF_INET: if (bind(socks->rx_sock, &addr->udp_addr.s, sockaddru_len(addr->udp_addr))< 0) { LM_ERR("bind: %s\n", strerror(errno)); goto err_rx; } socks->tx_sock = socks->rx_sock; break; case AF_INET6: if(bind(socks->rx_sock, (struct sockaddr*)&addr->udp_addr.sin6, sizeof(addr->udp_addr)) < 0) { LM_ERR("bind: %s\n", strerror(errno)); goto err_rx; } socks->tx_sock = socks->rx_sock; break; default: LM_ERR("domain not supported\n"); goto err_rx; } return 0; err_both: close(socks->tx_sock); err_rx: close(socks->rx_sock); return -1; } int mi_init_datagram_buffer(void){ mi_buf = pkg_malloc(DATAGRAM_SOCK_BUF_SIZE); if ( mi_buf==NULL) { LM_ERR("no more pkg memory\n"); return -1; } return 0; } /* reply socket security checks: * checks if fd is a socket, is not hardlinked and it's not a softlink * opened file descriptor + file name (for soft link check) * returns 0 if ok, <0 if not */ int mi_sock_check(int fd, char* fname) { struct stat fst; struct stat lst; if (fstat(fd, &fst)<0){ LM_ERR("fstat failed: %s\n", strerror(errno)); return -1; } /* check if socket */ if (!S_ISSOCK(fst.st_mode)){ LM_ERR("%s is not a sock\n", fname); return -1; } /* check if hard-linked */ if (fst.st_nlink>1){ LM_ERR("security: sock_check: %s is hard-linked %d times\n", fname, (unsigned)fst.st_nlink); return -1; } /* lstat to check for soft links */ if (lstat(fname, &lst)<0){ LM_ERR("lstat failed: %s\n", strerror(errno)); return -1; } if (S_ISLNK(lst.st_mode)){ LM_ERR("security: sock_check: %s is a soft link\n", fname); return -1; } /* if this is not a symbolic link, check to see if the inode didn't * change to avoid possible sym.link, rm sym.link & replace w/ sock race */ /*LM_DBG("for %s lst.st_dev %fl fst.st_dev %i lst.st_ino %i fst.st_ino" "%i\n", fname, lst.st_dev, fst.st_dev, lst.st_ino, fst.st_ino);*/ /*if ((lst.st_dev!=fst.st_dev)||(lst.st_ino!=fst.st_ino)){ LM_ERR("security: sock_check: " "socket %s inode/dev number differ: %d %d \n", fname, (int)fst.st_ino, (int)lst.st_ino); }*/ /* success */ return 0; } /* this function sends the reply over the reply socket */ static int mi_send_dgram(int fd, char* buf, unsigned int len, const struct sockaddr* to, int tolen, int timeout) { int n; size_t total_len; total_len = strlen(buf); /*LM_DBG("response is %s \n tolen is %i " "and len is %i\n",buf, tolen,len);*/ if(total_len == 0 || tolen ==0) return -1; if (total_len>DATAGRAM_SOCK_BUF_SIZE) { LM_DBG("datagram too big, " "truncking, datagram_size is %i\n",DATAGRAM_SOCK_BUF_SIZE); len = DATAGRAM_SOCK_BUF_SIZE; } /*LM_DBG("destination address length is %i\n", tolen);*/ n=sendto(fd, buf, len, 0, to, tolen); return n; } /*function that verifyes that the function from the datagram's first * line is correct and exists*/ static int identify_command(datagram_stream * dtgram, struct mi_cmd * *f) { char *command,*p; /* default offset for the command: 0 */ p= dtgram->start; if (!p){ LM_ERR("null pointer\n"); return -1; } /*if no command*/ if ( dtgram->len ==0 ){ LM_DBG("command empty case1\n"); goto error; } if (*p != MI_CMD_SEPARATOR){ LM_ERR("command must begin with: %c\n", MI_CMD_SEPARATOR); goto error; } command = p+1; LM_DBG("the command starts here: %s\n", command); p=strchr(command, MI_CMD_SEPARATOR ); if (!p ){ LM_ERR("empty command \n"); goto error; } if(*(p+1)!='\n'){ LM_ERR("the request's first line is invalid :no newline after " "the second %c\n",MI_CMD_SEPARATOR); goto error; } /* make command zero-terminated */ *p=0; LM_DBG("the command is %s\n",command); /*search for the appropiate command*/ *f=lookup_mi_cmd( command, p-command); if(!*f) goto error; /*the current offset has changed*/ LM_DBG("dtgram->len is %i\n", dtgram->len); dtgram->current = p+2 ; dtgram->len -=p+2 - dtgram->start; LM_DBG("dtgram->len is %i\n",dtgram->len); return 0; error: return -1; } /*************************** async functions ******************************/ static inline void free_async_handler( struct mi_handler *hdl ) { if (hdl) shm_free(hdl); } static void datagram_close_async(struct mi_root *mi_rpl,struct mi_handler *hdl, int done) { datagram_stream dtgram; int ret; my_socket_address *p; p = (my_socket_address *)hdl->param; if ( mi_rpl!=0 || done ) { if (mi_rpl!=0) { /*allocate the response datagram*/ dtgram.start = pkg_malloc(DATAGRAM_SOCK_BUF_SIZE); if(!dtgram.start){ LM_ERR("no more pkg memory\n"); goto err; } /*build the response*/ if(mi_datagram_write_tree(&dtgram , mi_rpl) != 0){ LM_ERR("failed to build the response\n"); goto err1; } LM_DBG("the response is %s", dtgram.start); /*send the response*/ ret = mi_send_dgram(p->tx_sock, dtgram.start, dtgram.current - dtgram.start, (struct sockaddr *)&p->address, p->address_len, mi_socket_timeout); if (ret>0){ LM_DBG("the response: %s has been sent in %i octets\n", dtgram.start, ret); }else{ LM_ERR("failed to send the response, ret is %i\n",ret); } free_mi_tree( mi_rpl ); pkg_free(dtgram.start); } else { mi_send_dgram(p->tx_sock, MI_COMMAND_FAILED, MI_COMMAND_FAILED_LEN, (struct sockaddr*)&reply_addr, reply_addr_len, mi_socket_timeout); } } if (done) free_async_handler( hdl ); return; err1: pkg_free(dtgram.start); err: return; } static inline struct mi_handler* build_async_handler(unsigned int sock_domain, reply_addr_t *reply_addr, unsigned int reply_addr_len, int tx_sock) { struct mi_handler *hdl; void * p; my_socket_address * repl_address; hdl = (struct mi_handler*)shm_malloc( sizeof(struct mi_handler) + sizeof(my_socket_address)); if (hdl==0) { LM_ERR("no more shm mem\n"); return 0; } p = (void *)((hdl) + 1); repl_address = p; memcpy( &repl_address->address, reply_addr, sizeof(reply_addr_t)); repl_address->address_len = reply_addr_len; repl_address->tx_sock = tx_sock; hdl->handler_f = datagram_close_async; hdl->param = (void*)repl_address; return hdl; } void mi_datagram_server(int rx_sock, int tx_sock) { struct mi_root *mi_cmd; struct mi_root *mi_rpl; struct mi_handler *hdl; struct mi_cmd * f; datagram_stream dtgram; int ret, len; ret = 0; f = 0; while(1){/*read the datagram*/ memset(mi_buf, 0, DATAGRAM_SOCK_BUF_SIZE); reply_addr_len = sizeof(reply_addr); /* get the client's address */ ret = recvfrom(rx_sock, mi_buf, DATAGRAM_SOCK_BUF_SIZE, 0, (struct sockaddr*)&reply_addr, &reply_addr_len); if (ret == -1) { LM_ERR("recvfrom: (%d) %s\n", errno, strerror(errno)); if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == ECONNREFUSED)) { LM_DBG("got %d (%s), going on\n", errno, strerror(errno)); continue; } LM_DBG("error in recvfrom\n"); continue; } if(ret == 0) continue; LM_DBG("received %.*s\n", ret, mi_buf); if(ret> DATAGRAM_SOCK_BUF_SIZE){ LM_ERR("buffer overflow\n"); continue; } LM_DBG("mi_buf is %.*s and we have received %i bytes\n", ret, mi_buf, ret); /*mi_buff is not null terminated */ dtgram.start = mi_buf; dtgram.len = ret; dtgram.current = dtgram.start; ret = identify_command(&dtgram, &f); /*analyze the command--from the first line*/ if(ret != 0) { LM_ERR("command not available\n"); mi_send_dgram(tx_sock, MI_COMMAND_NOT_AVAILABLE, MI_COMMAND_AVAILABLE_LEN, (struct sockaddr* )&reply_addr, reply_addr_len, mi_socket_timeout); continue; } LM_DBG("we have a valid command \n"); /* if asyncron cmd, build the async handler */ if (f->flags&MI_ASYNC_RPL_FLAG) { hdl = build_async_handler(mi_socket_domain, &reply_addr, reply_addr_len, tx_sock); if (hdl==0) { LM_ERR("failed to build async handler\n"); mi_send_dgram(tx_sock, MI_INTERNAL_ERROR, MI_INTERNAL_ERROR_LEN,(struct sockaddr* )&reply_addr, reply_addr_len, mi_socket_timeout); continue; } } else{ hdl = 0; } LM_DBG("after identifing the command, the received datagram is %s\n", dtgram.current); /*if no params required*/ if (f->flags&MI_NO_INPUT_FLAG) { LM_DBG("the command has no params\n"); mi_cmd = 0; } else { LM_DBG("parsing the command's params\n"); mi_cmd = mi_datagram_parse_tree(&dtgram); if (mi_cmd==NULL){ LM_ERR("failed to parse the MI tree\n"); mi_send_dgram(tx_sock, MI_PARSE_ERROR, MI_PARSE_ERROR_LEN, (struct sockaddr* )&reply_addr, reply_addr_len, mi_socket_timeout); free_async_handler(hdl); continue; } mi_cmd->async_hdl = hdl; } LM_DBG("done parsing the mi tree\n"); if ( (mi_rpl=run_mi_cmd(f, mi_cmd, (mi_flush_f *)mi_datagram_flush_tree, &dtgram))==0 ) { /*error while running the command*/ LM_ERR("failed to process the command\n"); mi_send_dgram(tx_sock, MI_COMMAND_FAILED, MI_COMMAND_FAILED_LEN, (struct sockaddr* )&reply_addr, reply_addr_len, mi_socket_timeout); goto failure; } /*the command exited well*/ LM_DBG("command process (%s)succeeded\n",f->name.s); if (mi_rpl!=MI_ROOT_ASYNC_RPL) { if(mi_datagram_write_tree(&dtgram , mi_rpl) != 0){ LM_ERR("failed to build the response \n"); goto failure; } len = dtgram.current - dtgram.start; ret = mi_send_dgram(tx_sock, dtgram.start,len, (struct sockaddr* )&reply_addr, reply_addr_len, mi_socket_timeout); if (ret>0){ LM_DBG("the response: %s has been sent in %i octets\n", dtgram.start, ret); }else{ LM_ERR("failed to send the response: %s (%d)\n", strerror(errno), errno); } free_mi_tree( mi_rpl ); free_async_handler(hdl); if (mi_cmd) free_mi_tree( mi_cmd ); }else { if (mi_cmd) free_mi_tree( mi_cmd ); } continue; failure: free_async_handler(hdl); /* destroy request tree */ if (mi_cmd) free_mi_tree( mi_cmd ); /* destroy the reply tree */ if (mi_rpl) free_mi_tree(mi_rpl); continue; } } opensips-2.2.2/modules/mi_datagram/datagram_fnc.h000066400000000000000000000041231300170765700220570ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2007-06-25 first version (ancuta) */ #ifndef _DATAGRAM_FNC_H #define _DATAGRAM_FNC_H #include #include #include #include "../../ip_addr.h" #include "../../str.h" #include "../../dprint.h" #include "../../ut.h" #include "../../mi/mi.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "mi_datagram.h" #define MI_COMMAND_FAILED "500 command failed\n" #define MI_COMMAND_FAILED_LEN (sizeof(MI_COMMAND_FAILED)-1) #define MI_COMMAND_NOT_AVAILABLE "500 command not available\n" #define MI_COMMAND_AVAILABLE_LEN (sizeof(MI_COMMAND_NOT_AVAILABLE)-1) #define MI_PARSE_ERROR "400 parse error in command\n" #define MI_PARSE_ERROR_LEN (sizeof(MI_PARSE_ERROR)-1) #define MI_INTERNAL_ERROR "500 Internal server error\n" #define MI_INTERNAL_ERROR_LEN (sizeof(MI_INTERNAL_ERROR)-1) typedef struct datagram_str{ char * start, * current; int len; }datagram_stream; typedef struct rx_tx{ int rx_sock, tx_sock; }rx_tx_sockets; int mi_init_datagram_server(sockaddr_dtgram * address, unsigned int domain, rx_tx_sockets * socks,int mode, int uid, int gid ); int mi_init_datagram_buffer(); void mi_datagram_server(int rx_sock, int tx_sock); #endif /*_DATAGRAM_FNC_H*/ opensips-2.2.2/modules/mi_datagram/doc/000077500000000000000000000000001300170765700200455ustar00rootroot00000000000000opensips-2.2.2/modules/mi_datagram/doc/mi_datagram.xml000066400000000000000000000016771300170765700230470ustar00rootroot00000000000000 %docentities; ]> mi_datagram Module &osipsname; Andreea-Ancuta Onofrei Andreea-Ancuta Onofrei 2007 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/mi_datagram/doc/mi_datagram_admin.xml000066400000000000000000000137301300170765700242100ustar00rootroot00000000000000 &adminguide;
Overview This is a module which provides a UNIX/UDP SOCKET transport layer implementation for the Management Interface.
DATAGRAM command syntax The external commands issued via DATAGRAM interface must follow the following syntax: request = first_line (argument '\n')* first_line = ':'command_name':''\n' argument = (arg_name '::' (arg_value)? ) | (arg_value) arg_name = not-quoted_string arg_value = not-quoted_string | '"' string '"' not-quoted_string = string - {',",\n,\r}
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters
<varname>socket_name</varname> (string) The name of a UNIX SOCKET or an IP address. The UNIX datagram or UDP socket will be created using this parameter in order to read the external commands. Both IPv4 and IPv6 are supported. Default value is NONE. Set <varname>socket_name</varname> parameter ... modparam("mi_datagram", "socket_name", "/tmp/opensips.sock") ... modparam("mi_datagram", "socket_name", "udp:192.168.2.133:8080") ...
<varname>children_count</varname> (string) The number of child processes to be created. Each child process will be a datagram server. Default value is 1. Set <varname>children_count</varname> parameter ... modparam("mi_datagram", "children_count", 3) ...
<varname>unix_socket_mode</varname> (integer) Permission to be used for creating the listening UNIX datagram socket. Not necessary for a UDP socket. It follows the UNIX conventions. Default value is 0660 (rw-rw----). Set <varname>unix_socket_mode</varname> parameter ... modparam("mi_datagram", "unix_socket_mode", 0600) ...
<varname>unix_socket_group</varname> (integer) <varname>unix_socket_group</varname> (string) Group to be used for creating the listening UNIX socket. Default value is the inherited one. Set <varname>unix_socket_group</varname> parameter ... modparam("mi_datagram", "unix_socket_group", 0) modparam("mi_datagram", "unix_socket_group", "root") ...
<varname>unix_socket_user</varname> (integer) <varname>unix_socket_group</varname> (string) User to be used for creating the listening UNIX socket. Default value is the inherited one. Set <varname>unix_socket_user</varname> parameter ... modparam("mi_datagram", "unix_socket_user", 0) modparam("mi_datagram", "unix_socket_user", "root") ...
<varname>socket_timeout</varname> (integer) The reply will expire after trying to sent it for socket_timeout milliseconds. Default value is 2000. Set <varname>socket_timeout</varname> parameter ... modparam("mi_datagram", "socket_timeout", 2000) ...
<varname>reply_indent</varname> (string) Strings to be used for line indentation. As the MI data structure is tree oriendeted, the depth level will printed as indentation. Default value is "\t" (TAB). Set <varname>reply_indent</varname> parameter ... modparam("mi_datagram", "reply_indent", " ") ...
Exported Functions No function exported to be used from configuration file.
Example This is an example showing the DATAGRAM format for the get_statistics dialog: tm: MI commad: response. DATAGRAM request :get_statistics:\n dialog:\n tm:\n
opensips-2.2.2/modules/mi_datagram/doc/mi_datagram_faq.xml000066400000000000000000000034121300170765700236630ustar00rootroot00000000000000 &faqguide; Both UNIX and UDP type of socket can be created simultaneusly? This version supports only one kind of socket at a time. If there are more than one value set for socket_name the last one will take effect. Is there a limit in the datagram request's size? The maximum length of a datagram request or reply is 65457 bytes. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/mi_datagram/mi_datagram.c000066400000000000000000000211711300170765700217130ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2007-06-25 first version (ancuta) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../resolve.h" #include "../../dprint.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "../../ip_addr.h" #include "mi_datagram.h" #include "datagram_fnc.h" #include "mi_datagram_parser.h" #include "mi_datagram_writer.h" /* AF_LOCAL is not defined on solaris */ #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #if !defined(PF_LOCAL) #define PF_LOCAL PF_UNIX #endif #define MAX_CTIME_LEN 128 #define MAX_NB_PORT 65535 static int mi_mod_init(void); static int mi_child_init(int rank); static int mi_destroy(void); static int pre_datagram_process(void); static int post_datagram_process(void); static void datagram_process(int rank); /* local variables */ static int mi_socket_domain = AF_LOCAL; static sockaddr_dtgram mi_dtgram_addr; /* socket definition parameter */ static char *mi_socket = 0; int mi_socket_timeout = 2000; static rx_tx_sockets sockets; /* unixsock specific parameters */ static int mi_unix_socket_uid = -1; static char *mi_unix_socket_uid_s = 0; static int mi_unix_socket_gid = -1; static char *mi_unix_socket_gid_s = 0; static int mi_unix_socket_mode = S_IRUSR| S_IWUSR| S_IRGRP| S_IWGRP; /* mi specific parameters */ static char *mi_reply_indent = DEFAULT_MI_REPLY_IDENT; static proc_export_t mi_procs[] = { {"MI Datagram", pre_datagram_process, post_datagram_process, datagram_process, MI_CHILD_NO, PROC_FLAG_INITCHILD }, {0,0,0,0,0,0} }; static param_export_t mi_params[] = { {"children_count", INT_PARAM, &mi_procs[0].no }, {"socket_name", STR_PARAM, &mi_socket }, {"socket_timeout", INT_PARAM, &mi_socket_timeout }, {"unix_socket_mode", INT_PARAM, &mi_unix_socket_mode }, {"unix_socket_group", STR_PARAM, &mi_unix_socket_gid_s }, {"unix_socket_group", INT_PARAM, &mi_unix_socket_gid }, {"unix_socket_user", STR_PARAM, &mi_unix_socket_uid_s }, {"unix_socket_user", INT_PARAM, &mi_unix_socket_uid }, {"reply_indent", STR_PARAM, &mi_reply_indent }, {0,0,0} }; struct module_exports exports = { "mi_datagram", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ mi_params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ mi_procs, /* extra processes */ mi_mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) mi_destroy, /* destroy function */ mi_child_init /* per-child init function */ }; static int mi_mod_init(void) { unsigned int port_no; int n; struct stat filestat; struct hostent * host; char *p, *host_s; str port_str; /* checking the mi_socket module param */ LM_DBG("testing socket existence...\n"); if( mi_socket==NULL || *mi_socket == 0) { LM_ERR("no DATAGRAM_ socket configured\n"); return -1; } LM_DBG("the socket's name/address is %s\n", mi_socket); memset( &mi_dtgram_addr, 0, sizeof(mi_dtgram_addr) ); if(strncmp(mi_socket, "udp:",4) == 0) { /*for an UDP socket*/ LM_DBG("we have an udp socket\n"); /*separate proto and host */ p = mi_socket+4; if( (*(p)) == '\0') { LM_ERR("malformed ip address\n"); return -1; } host_s=p; LM_DBG("the remaining address after separating the protocol is %s\n",p); if( (p = strrchr(p+1, ':')) == 0 ) { LM_ERR("no port specified\n"); return -1; } /*the address contains a port number*/ *p = '\0'; p++; port_str.s = p; port_str.len = strlen(p); LM_DBG("the port string is %s\n", p); if(str2int(&port_str, &port_no) != 0 ){ LM_ERR("there is not a valid number port\n"); return -1; } *p = '\0'; if (port_no<1024 || port_no>MAX_NB_PORT) { LM_ERR("invalid port number; must be in [1024,%d]\n",MAX_NB_PORT); return -1; } if(! (host = resolvehost(host_s, 0)) ){ LM_ERR("failed to resolve %s\n", host_s); return -1; } LM_DBG("the ip is %s\n",host_s); if(hostent2su( &(mi_dtgram_addr.udp_addr), host, 0, port_no ) !=0){ LM_ERR("failed to resolve %s\n", mi_socket); return -1; } mi_socket_domain = host->h_addrtype; } else { /*in case of a Unix socket*/ LM_DBG("we have an UNIX socket\n"); n=stat(mi_socket, &filestat); if( n==0){ LM_INFO("the socket %s already exists, trying to delete it...\n", mi_socket); if (unlink(mi_socket)<0){ LM_ERR("cannot delete old socket: %s\n", strerror(errno)); return -1; } }else if (n<0 && errno!=ENOENT){ LM_ERR("socket stat failed:%s\n", strerror(errno)); return -1; } /* check mi_unix_socket_mode */ if(!mi_unix_socket_mode){ LM_WARN("cannot specify mi_unix_socket_mode = 0, " "forcing it to rw-------\n"); mi_unix_socket_mode = S_IRUSR| S_IWUSR; } if (mi_unix_socket_uid_s){ if (user2uid(&mi_unix_socket_uid, &mi_unix_socket_gid, mi_unix_socket_uid_s)<0){ LM_ERR("bad user name %s\n", mi_unix_socket_uid_s); return -1; } } if (mi_unix_socket_gid_s){ if (group2gid(&mi_unix_socket_gid, mi_unix_socket_gid_s)<0){ LM_ERR("bad group name %s\n", mi_unix_socket_gid_s); return -1; } } /*create the unix socket address*/ mi_dtgram_addr.unix_addr.sun_family = AF_LOCAL; memcpy( mi_dtgram_addr.unix_addr.sun_path, mi_socket, strlen(mi_socket)); } return 0; } static int mi_child_init(int rank) { if ( rank>PROC_MAIN ) { if(mi_datagram_writer_init( DATAGRAM_SOCK_BUF_SIZE , mi_reply_indent )!= 0){ LM_CRIT("failed to initiate mi_datagram_writer\n"); return -1; } } return 0; } static int pre_datagram_process(void) { int res; /*create the sockets*/ res = mi_init_datagram_server(&mi_dtgram_addr, mi_socket_domain, &sockets, mi_unix_socket_mode, mi_unix_socket_uid, mi_unix_socket_gid); if ( res ) { LM_CRIT("function mi_init_datagram_server returned with error!!!\n"); return -1; } return 0; } static void datagram_process(int rank) { LM_INFO("a new child %d/%d\n", rank, getpid()); /*child's initial settings*/ if ( init_mi_child()!=0) { LM_CRIT("failed to init the mi process\n"); exit(-1); } if (mi_init_datagram_buffer()!=0){ LM_ERR("failed to allocate datagram buffer\n"); exit(-1); } if (mi_datagram_writer_init( DATAGRAM_SOCK_BUF_SIZE , mi_reply_indent )!= 0){ LM_CRIT("failed to initiate mi_datagram_writer\n"); exit(-1); } mi_datagram_server(sockets.rx_sock, sockets.tx_sock); exit(-1); } static int post_datagram_process(void) { /* close the RX sockets, but try to keep the TX socket open (for AYSNC) */ if (sockets.rx_sock!=sockets.tx_sock) close(sockets.rx_sock); return 0; } static int mi_destroy(void) { int n; struct stat filestat; /* destroying the socket descriptors */ if(mi_socket_domain == AF_UNIX){ n=stat(mi_socket, &filestat); if (n==0){ if (unlink(mi_socket)<0){ LM_ERR("cannot delete the socket (%s): %s\n", mi_socket, strerror(errno)); goto error; } } else if (n<0 && errno!=ENOENT) { LM_ERR("socket stat failed: %s\n", strerror(errno)); goto error; } } return 0; error: return -1; } opensips-2.2.2/modules/mi_datagram/mi_datagram.h000066400000000000000000000027161300170765700217240ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2007-06-25 first version (ancuta) */ #ifndef _MI_DATAGRAM_H_ #define _MI_DATAGRAM_H_ #include #define DEFAULT_MI_REPLY_IDENT "\t" #define MI_CMD_SEPARATOR ':' /* the 2-chars separator between name and value */ #define MI_ATTR_VAL_SEP1 ':' #define MI_ATTR_VAL_SEP2 ':' /* maximum size for the socket reply name */ #define MAX_MI_FILENAME 128 /* size of buffer used by parser to read and build the MI tree */ #define MI_CHILD_NO 1 #include #include "../../ip_addr.h" typedef union{ union sockaddr_union udp_addr; struct sockaddr_un unix_addr; }sockaddr_dtgram; #endif /* _MI_DATAGRAM */ opensips-2.2.2/modules/mi_datagram/mi_datagram_parser.c000066400000000000000000000157741300170765700233030ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2007-06-25 first version (ancuta) */ #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../mi/tree.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "datagram_fnc.h" #include "mi_datagram.h" #include "mi_datagram_parser.h" /*static unsigned int mi_parse_buffer_len = 0; int mi_datagram_parser_init( unsigned int size ) { mi_parse_buffer_len = size; return 0; }*/ /* returns -1 = error * 0 = ok * 1 = end of input */ /*example: mi_parse_node(datagram, &buf, &name, &value)*/ static inline int mi_datagram_parse_node(datagram_stream * data, str *name, str *value) { char *p, *pmax; char *start, *start1; char *mark_nsp; int newline_found = 0; LM_DBG("the remaining datagram to be parsed is %s and %i in length \n", data->current,data->len); p =data->current; start1 = start = p; if(data->len > DATAGRAM_SOCK_BUF_SIZE) { LM_ERR("overflow while parsing the received datagram\n"); goto parse_err; } pmax = start + data->len ; /* remove leading spaces */ for( ; starts = value->s = 0; name->len = value->len = 0; mark_nsp = 0; /* start parsing */ if (*start!='"') { LM_DBG("the string is not just a quoted string\n"); /* look for the atribute name */ p = mark_nsp = start; while ( p!=pmax && (( *p!=MI_ATTR_VAL_SEP1) || p+1==pmax ||*(p+1)!=MI_ATTR_VAL_SEP2) ) { if (!isspace((int)*p)) { if (*p=='"') { LM_DBG("found \" before attr_separator\n"); goto parse_err; } mark_nsp = p; } if(*p=='\n' && p!=(pmax -1)){ LM_DBG("found newline before attr_separator--we have just the " "attribute's value\n"); mark_nsp++; pmax = ++p; break; }else if (p == (pmax-1)){ mark_nsp++; pmax = ++p; LM_DBG("just a value, no new line"); break; } p++; } if (p!=pmax) { /* we have found the separator */ LM_DBG("we've found the attr_separator\n"); if (p==start) { /* empty attr name */ LM_DBG("empty attr_name\n"); } else { name->s = start; name->len = mark_nsp - start+1; LM_DBG("attr name <%.*s> found\n",name->len, name->s); } p += 2; /* for separator */ /* consume the trailing spaces */ for( ; p!=pmax && isspace((int)*p) ; p++) { if(*p=='\n') { LM_DBG("empty value\n"); /* empty value.....we are done */ goto done; } } /*LM_DBG("p is %s case2\n",p);*/ if(p==pmax && *p=='\n') { LM_DBG("empty value\n"); /* empty value.....we are done */ goto done; } /*LM_DBG("p is %s case1\n",p);*/ /* value (only if not quoted ) */ if (*p!='"') { LM_DBG("not quoted value, p is %c \n", *p); for( start=p ; p!=pmax ; p++ ) { if (!isspace((int)*p)) { if (*p=='"'){ goto parse_err; } mark_nsp = p; LM_DBG("nsp is %p ,p is %p, pmax is %p and *p is %c\n", mark_nsp, p, pmax,*p); } if(*p=='\n'){/*end of the node*/ pmax = p; break; } } value->s = start; value->len = mark_nsp - start+1; LM_DBG("*start is %c and start is %p\n",*start, start); LM_DBG("attr value <%s> found\n"/*,value->len*/, value->s); goto done; } /* quoted value....continue */ } else { /* we have an empty name ... and we read a non-quoted value */ value->s = start; value->len = mark_nsp - start; LM_DBG("empty name, attr not quoted value <%.*s> found\n", value->len, value->s); goto done; } } else { p = start; /*send the value only: as a quoted string*/ } /*we have a quoted value*/ LM_DBG("we have a quoted value, %s\n", p); start = p+1; /*skip the first "*/ value->s = start; p = start; /* parse the buffer and look for " */ while (plen = p - value->s; LM_DBG("attr value <%.*s> found\n",value->len, value->s); /* is the line ending propely (only spaces) ? */ p++; for(; p!=pmax && isspace((int)*p) ; p++) { if(*p=='\n') { /*case : ""quoted string" \n on a line */ LM_DBG("line ended properly case1\n"); pmax = p; break; } } if (p!=pmax )/*didn't find the second " on the current line*/ { LM_ERR("didn't find newline case1 \n"); goto parse_err; } newline_found = 1; /* found! */ goto done; } }else { p++; } } if(p== pmax && !newline_found) { LM_ERR("didn't find newline case2\n"); goto parse_err; } done: /*set the current datagram's offset */ LM_DBG("1 data->len is %i\n",data->len); data->len -= p-start1; LM_DBG("2 data->len is %i\n",data->len); data->current = p; return 0; parse_err: LM_ERR("parse error around %c\n",*p); return -1; } /*parsing the datagram buffer*/ struct mi_root * mi_datagram_parse_tree(datagram_stream * datagram) { struct mi_root *root; struct mi_node *node; str name; str value; int ret; root = init_mi_tree(0,0,0); if (!root) { LM_ERR("the MI tree cannot be initialized!\n"); goto error; } if(!datagram || datagram->current[0] == '\0') { LM_DBG("no data in the datagram\n"); return root; } node = &root->node; name.s = value.s = 0; name.len = value.len = 0; /* every tree for a command ends with a \n that is alone on its line */ while ((ret=mi_datagram_parse_node(datagram, &name, &value))>=0 ) { if(ret == 1) return root; LM_DBG("adding node <%.*s> ; val <%.*s>\n", name.len,name.s, value.len,value.s); if(!add_mi_node_child(node,0,name.s,name.len,value.s,value.len)){ LM_ERR("cannot add the child node to the tree\n"); goto error; } LM_DBG("the remaining datagram has %i bytes\n",datagram->len); /*end condition*/ if(datagram->len == 0) { LM_DBG("found end of input\n"); return root; } } LM_ERR("parse error!\n"); error: if (root) free_mi_tree(root); return 0; } opensips-2.2.2/modules/mi_datagram/mi_datagram_parser.h000066400000000000000000000022121300170765700232670ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2007-06-25 first version (ancuta) */ #ifndef _MI_DATAGRAM_PARSER_H_ #define _MI_DATAGRAM_PARSER_H_ #include #define DATAGRAM_SOCK_BUF_SIZE 65457 int mi_datagram_parser_init( unsigned int size ); struct mi_root * mi_datagram_parse_tree(datagram_stream * data); #endif /* _MI_DATAGRAM_PARSER_H_ */ opensips-2.2.2/modules/mi_datagram/mi_datagram_writer.c000066400000000000000000000160611300170765700233110ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2007-06-25 first version (ancuta) */ #include #include #include "../../str.h" #include "../../ut.h" #include "../../dprint.h" #include "../../mi/tree.h" #include "../../mem/mem.h" #include "mi_datagram.h" #include "datagram_fnc.h" #include "mi_datagram_parser.h" static unsigned int mi_write_buffer_len = 0; static str mi_datagram_indent; int mi_datagram_writer_init( unsigned int size , char *indent) { mi_write_buffer_len = size; if (indent==NULL || indent[0]==0 ) { mi_datagram_indent.s = 0; mi_datagram_indent.len = 0; } else { mi_datagram_indent.s = indent; mi_datagram_indent.len = strlen(indent); } return 0; } static inline int mi_datagram_write_node(datagram_stream * dtgram, struct mi_node *node, int level) { struct mi_attr *attr; char *start, *end, *p; start = p = dtgram->current; end = dtgram->start + dtgram->len; LM_DBG("writing the name <%.*s> and value <%.*s> \n", node->name.len, node->name.s, node->value.len, node->value.s); /* write indents */ if (mi_datagram_indent.s) { if (p + level*mi_datagram_indent.len>end) { LM_DBG("a too long line\n"); return -1; } for( ; level>0 ; level-- ) { memcpy( p, mi_datagram_indent.s, mi_datagram_indent.len); p += mi_datagram_indent.len; } } /* name and value */ if (node->name.s!=NULL) { if (p+node->name.len+3>end) { LM_DBG("too long name\n"); return -1; } memcpy(p,node->name.s,node->name.len); p += node->name.len; *(p++) = MI_ATTR_VAL_SEP1; *(p++) = MI_ATTR_VAL_SEP2; *(p++) = ' '; } /*LM_DBG("after adding the " "name, the datagram is %s\n ", dtgram->datagram.s);*/ if (node->value.s!=NULL) { if (p+node->value.len>end) { LM_DBG("too long value\n"); return -1; } memcpy(p,node->value.s,node->value.len); p += node->value.len; } /* LM_DBG("after adding the " "value, the datagram is %s\n ", dtgram->datagram.s);*/ /* attributes */ for( attr=node->attributes ; attr!=NULL ; attr=attr->next ) { if (attr->name.s!=NULL) { if (p+attr->name.len+2>end) { LM_DBG("too long attr name\n"); return -1; } *(p++) = ' '; memcpy(p,attr->name.s,attr->name.len); p += attr->name.len; *(p++) = '='; } if (attr->value.s!=NULL) { if (p+attr->value.len>end) { LM_DBG("too long attr value\n"); return -1; } memcpy(p,attr->value.s,attr->value.len); p += attr->value.len; } } /* LM_DBG("after adding the " "attributes, the datagram is %s\n ", dtgram->datagram.s);*/ if (p+1>end) { LM_DBG("overflow before returning\n"); return -1; } *(p++) = '\n'; dtgram->len -= p-start; dtgram->current = p; return 0; } static int datagram_recur_write_tree(datagram_stream *dtgram, struct mi_node *tree, int level) { for( ; tree ; tree=tree->next ) { if (!(tree->flags & MI_WRITTEN)) { if (mi_datagram_write_node( dtgram, tree, level)!=0) { LM_ERR("failed to write -line too long!!!\n"); return -1; } } if (tree->kids) { if (datagram_recur_write_tree(dtgram, tree->kids, level+1)<0) return -1; } } return 0; } int mi_datagram_write_tree(datagram_stream * dtgram, struct mi_root *tree) { str code; if (!(tree->node.flags & MI_WRITTEN)) { /* no write so far, so init theoutput datagram */ dtgram->current = dtgram->start; dtgram->len = mi_write_buffer_len; /* write the root node */ code.s = int2str((unsigned long)tree->code, &code.len); if (code.len+tree->reason.len+1>dtgram->len) { LM_ERR("failed to write - reason too long!\n"); return -1; } memcpy( dtgram->start, code.s, code.len); dtgram->current += code.len; *(dtgram->current) = ' '; dtgram->current++; if (tree->reason.len) { memcpy(dtgram->current, tree->reason.s, tree->reason.len); dtgram->current += tree->reason.len; } *(dtgram->current) = '\n'; dtgram->current++; dtgram->len -= code.len + 1 + tree->reason.len+1; } if (datagram_recur_write_tree(dtgram, tree->node.kids, 0)!=0) return -1; if (dtgram->len<=0) { LM_ERR("failed to write - EOC does not fit in!!!\n"); return -1; } *(dtgram->current) = '\n'; dtgram->len--; *(dtgram->current) = '\0'; return 0; } static int mi_datagram_recur_flush_tree(datagram_stream * dtgram, struct mi_node *tree, int level) { struct mi_node *kid, *tmp; int ret; for(kid = tree->kids ; kid ; ){ /* write the current kid */ if (!(kid->flags & MI_WRITTEN)){ if (mi_datagram_write_node( dtgram, kid, level)<0) { LM_ERR("failed to write -line too long!!!\n"); return -1; } /* we are sure that this node has been written * => avoid writing it again */ kid->flags |= MI_WRITTEN; } /* write the current kid's children */ if ((ret = mi_datagram_recur_flush_tree(dtgram, kid, level+1))<0) return -1; else if (ret > 0) return ret; if (!(kid->flags & MI_NOT_COMPLETED)){ tmp = kid; kid = kid->next; tree->kids = kid; if(!tmp->kids){ /* this node does not have any kids */ free_mi_node(tmp); } } else{ /* the node will have more kids => to keep the tree shape, do not * flush any other node for now */ return 1; } } return 0; } int mi_datagram_flush_tree(datagram_stream * dtgram, struct mi_root *tree) { str code; if (!(tree->node.flags & MI_WRITTEN)) { /* first writing, so init the tree */ dtgram->current = dtgram->start; dtgram->len = mi_write_buffer_len; /* write the root node */ code.s = int2str((unsigned long)tree->code, &code.len); if (code.len+tree->reason.len+1>dtgram->len) { LM_ERR("failed to write - reason too long!\n"); return -1; } memcpy( dtgram->start, code.s, code.len); dtgram->current += code.len; *(dtgram->current) = ' '; dtgram->current++; if (tree->reason.len) { memcpy(dtgram->current, tree->reason.s, tree->reason.len); dtgram->current += tree->reason.len; } *(dtgram->current) = '\n'; dtgram->current++; dtgram->len -= code.len + 1 + tree->reason.len+1; /* we are sure that this node has been written * => avoid writing it again */ tree->node.flags |= MI_WRITTEN; } if (mi_datagram_recur_flush_tree(dtgram, &tree->node, 0)<0) return -1; if (dtgram->len<=0) { LM_ERR("failed to write - EOC does not fit in!\n"); return -1; } *(dtgram->current) = '\n'; dtgram->len--; *(dtgram->current) = '\0'; return 0; } opensips-2.2.2/modules/mi_datagram/mi_datagram_writer.h000066400000000000000000000023121300170765700233100ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2007-06-25 first version (ancuta) */ #ifndef _MI_DATAGRAM_WRITER_H_ #define _MI_DATAGRAM_WRITER_H_ #include int mi_datagram_writer_init( unsigned int size, char *ident); int mi_datagram_write_tree( datagram_stream * dtgram, struct mi_root *tree); int mi_datagram_flush_tree( datagram_stream * dtgram, struct mi_root *tree); #endif /* _MI_DATAGRAM_WRITER_H_ */ opensips-2.2.2/modules/mi_fifo/000077500000000000000000000000001300170765700164435ustar00rootroot00000000000000opensips-2.2.2/modules/mi_fifo/Makefile000066400000000000000000000003221300170765700201000ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=mi_fifo.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/mi_fifo/README000066400000000000000000000101211300170765700173160ustar00rootroot00000000000000mi_fifo Module Bogdan-Andrei Iancu OpenSIPS Solutions Edited by Bogdan-Andrei Iancu Copyright © 2006 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. FIFO command syntax 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. fifo_name (string) 1.4.2. fifo_mode (integer) 1.4.3. fifo_group (integer) fifo_group (string) 1.4.4. fifo_user (integer) fifo_group (string) 1.4.5. reply_dir (string) 1.4.6. reply_indent (string) 1.5. Exported Functions 1.6. Example List of Examples 1.1. Set fifo_name parameter 1.2. Set fifo_mode parameter 1.3. Set fifo_group parameter 1.4. Set fifo_user parameter 1.5. Set reply_dir parameter 1.6. Set reply_indent parameter 1.7. FIFO request Chapter 1. Admin Guide 1.1. Overview This is a module which provides a FIFO transport layer implementation for Management Interface. It receives the command over a FIFO file and returns the output through the reply_fifo specified. The module checks every 30 seconds if the FIFO file exists, and if it was deleted, it recreates it. If one wants to force the fifo file recreation, it should send a SIGHUP signal to the MI process PID. 1.2. FIFO command syntax The external commands issued via FIFO interface must follow the following syntax: * request = first_line argument* * first_line = ':'command_name':'reply_fifo'\n' * argument = (arg_name '::' (arg_value)? ) | (arg_value) * arg_name = not-quoted_string * arg_value = not-quoted_string | '"' string '"' * not-quoted_string = string - {',",\n,\r} 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.4. Exported Parameters 1.4.1. fifo_name (string) The name of the FIFO file to be created for listening and reading external commands. Default value is NONE. Example 1.1. Set fifo_name parameter ... modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") ... 1.4.2. fifo_mode (integer) Permission to be used for creating the listening FIFO file. It follows the UNIX conventions. Default value is 0660 (rw-rw----). Example 1.2. Set fifo_mode parameter ... modparam("mi_fifo", "fifo_mode", 0600) ... 1.4.3. fifo_group (integer) fifo_group (string) Group to be used for creating the listening FIFO file. Default value is the inherited one. Example 1.3. Set fifo_group parameter ... modparam("mi_fifo", "fifo_group", 0) modparam("mi_fifo", "fifo_group", "root") ... 1.4.4. fifo_user (integer) fifo_group (string) User to be used for creating the listening FIFO file. Default value is the inherited one. Example 1.4. Set fifo_user parameter ... modparam("mi_fifo", "fifo_user", 0) modparam("mi_fifo", "fifo_user", "root") ... 1.4.5. reply_dir (string) Directory to be used for creating the reply FIFO files. Default value is “/tmp/†Example 1.5. Set reply_dir parameter ... modparam("mi_fifo", "reply_dir", "/home/opensips/tmp/") ... 1.4.6. reply_indent (string) Strings to be used for line indentation. As the MI data structure is tree oriendeted, the depth level will printed as indentation. Default value is “"\t" (TAB)â€. Example 1.6. Set reply_indent parameter ... modparam("mi_fifo", "reply_indent", " ") ... 1.5. Exported Functions No function exported to be used from configuration file. 1.6. Example This is an example showing the FIFO format for the “get_statistics dialog: tm:†MI commad: response. Example 1.7. FIFO request :get_statistics:reply_fifo\n dialog:\n tm:\n \n opensips-2.2.2/modules/mi_fifo/doc/000077500000000000000000000000001300170765700172105ustar00rootroot00000000000000opensips-2.2.2/modules/mi_fifo/doc/mi_fifo.xml000066400000000000000000000017701300170765700213470ustar00rootroot00000000000000 %docentities; ]> mi_fifo Module &osipsname; Bogdan-Andrei Iancu &osipssolname; Bogdan-Andrei Iancu 2006 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/mi_fifo/doc/mi_fifo_admin.xml000066400000000000000000000127161300170765700225210ustar00rootroot00000000000000 &adminguide;
Overview This is a module which provides a FIFO transport layer implementation for Management Interface. It receives the command over a FIFO file and returns the output through the reply_fifo specified. The module checks every 30 seconds if the FIFO file exists, and if it was deleted, it recreates it. If one wants to force the fifo file recreation, it should send a SIGHUP signal to the MI process PID.
FIFO command syntax The external commands issued via FIFO interface must follow the following syntax: request = first_line argument* first_line = ':'command_name':'reply_fifo'\n' argument = (arg_name '::' (arg_value)? ) | (arg_value) arg_name = not-quoted_string arg_value = not-quoted_string | '"' string '"' not-quoted_string = string - {',",\n,\r}
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters
<varname>fifo_name</varname> (string) The name of the FIFO file to be created for listening and reading external commands. Default value is NONE. Set <varname>fifo_name</varname> parameter ... modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") ...
<varname>fifo_mode</varname> (integer) Permission to be used for creating the listening FIFO file. It follows the UNIX conventions. Default value is 0660 (rw-rw----). Set <varname>fifo_mode</varname> parameter ... modparam("mi_fifo", "fifo_mode", 0600) ...
<varname>fifo_group</varname> (integer) <varname>fifo_group</varname> (string) Group to be used for creating the listening FIFO file. Default value is the inherited one. Set <varname>fifo_group</varname> parameter ... modparam("mi_fifo", "fifo_group", 0) modparam("mi_fifo", "fifo_group", "root") ...
<varname>fifo_user</varname> (integer) <varname>fifo_group</varname> (string) User to be used for creating the listening FIFO file. Default value is the inherited one. Set <varname>fifo_user</varname> parameter ... modparam("mi_fifo", "fifo_user", 0) modparam("mi_fifo", "fifo_user", "root") ...
<varname>reply_dir</varname> (string) Directory to be used for creating the reply FIFO files. Default value is /tmp/ Set <varname>reply_dir</varname> parameter ... modparam("mi_fifo", "reply_dir", "/home/opensips/tmp/") ...
<varname>reply_indent</varname> (string) Strings to be used for line indentation. As the MI data structure is tree oriendeted, the depth level will printed as indentation. Default value is "\t" (TAB). Set <varname>reply_indent</varname> parameter ... modparam("mi_fifo", "reply_indent", " ") ...
Exported Functions No function exported to be used from configuration file.
Example This is an example showing the FIFO format for the get_statistics dialog: tm: MI commad: response. FIFO request :get_statistics:reply_fifo\n dialog:\n tm:\n \n
opensips-2.2.2/modules/mi_fifo/fifo_fnc.c000066400000000000000000000352721300170765700203710ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-25 first version (bogdan) */ #include #include #include #include #include #include #include #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../mi/mi.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "mi_fifo.h" #include "fifo_fnc.h" #include "mi_parser.h" #include "mi_writer.h" static char *mi_buf = 0; static char *reply_fifo_s = 0; static int reply_fifo_len = 0; static char *mi_fifo_name = NULL; static int mi_fifo_mode; static int mi_fifo_uid; static int mi_fifo_gid; static int volatile mi_reload_fifo = 0; FILE* mi_create_fifo(void) { static int mi_fifo_read = 0; static int mi_fifo_write = 0; FILE *fifo_stream = 0; long opt; /* create FIFO ... */ if ((mkfifo(mi_fifo_name, mi_fifo_mode)<0)) { LM_ERR("can't create FIFO: %s (mode=%d)\n", strerror(errno), mi_fifo_mode); return 0; } LM_DBG("FIFO created @ %s\n", mi_fifo_name ); if ((chmod(mi_fifo_name, mi_fifo_mode)<0)) { LM_ERR("can't chmod FIFO: %s (mode=%d)\n", strerror(errno), mi_fifo_mode); return 0; } if ((mi_fifo_uid!=-1) || (mi_fifo_gid!=-1)){ if (chown(mi_fifo_name, mi_fifo_uid, mi_fifo_gid)<0){ LM_ERR("failed to change the owner/group for %s to %d.%d; %s[%d]\n", mi_fifo_name, mi_fifo_uid, mi_fifo_gid, strerror(errno), errno); return 0; } } LM_DBG("fifo %s opened, mode=%o\n", mi_fifo_name, mi_fifo_mode ); /* open it non-blocking or else wait here until someone * opens it for writing */ mi_fifo_read=open(mi_fifo_name, O_RDONLY|O_NONBLOCK, 0); if (mi_fifo_read<0) { LM_ERR("mi_fifo_read did not open: %s\n", strerror(errno)); return 0; } fifo_stream = fdopen(mi_fifo_read, "r"); if (fifo_stream==NULL) { LM_ERR("fdopen failed: %s\n", strerror(errno)); return 0; } /* make sure the read fifo will not close */ mi_fifo_write=open(mi_fifo_name, O_WRONLY|O_NONBLOCK, 0); if (mi_fifo_write<0) { fclose(fifo_stream); close(mi_fifo_read); LM_ERR("fifo_write did not open: %s\n", strerror(errno)); return 0; } /* set read fifo blocking mode */ if ((opt=fcntl(mi_fifo_read, F_GETFL))==-1){ fclose(fifo_stream); close(mi_fifo_read); close(mi_fifo_write); LM_ERR("fcntl(F_GETFL) failed: %s [%d]\n", strerror(errno), errno); return 0; } if (fcntl(mi_fifo_read, F_SETFL, opt & (~O_NONBLOCK))==-1){ fclose(fifo_stream); close(mi_fifo_read); close(mi_fifo_write); LM_ERR("cntl(F_SETFL) failed: %s [%d]\n", strerror(errno), errno); return 0; } return fifo_stream; } static void mi_sig_hup(int signo) { mi_reload_fifo = 1; } FILE* mi_init_fifo_server(char *fifo_name, int fifo_mode, int fifo_uid, int fifo_gid, char* fifo_reply_dir) { FILE *fifo_stream; /* allocate all static buffers */ mi_buf = pkg_malloc(MAX_MI_FIFO_BUFFER); reply_fifo_s = pkg_malloc(MAX_MI_FILENAME); if ( mi_buf==NULL|| reply_fifo_s==NULL) { LM_ERR("no more private memory\n"); return 0; } mi_fifo_name = fifo_name; mi_fifo_mode = fifo_mode; mi_fifo_uid = fifo_uid; mi_fifo_gid = fifo_gid; fifo_stream = mi_create_fifo(); if (!fifo_stream) { LM_ERR("cannot create fifo\n"); return 0; } /* init fifo reply dir buffer */ reply_fifo_len = strlen(fifo_reply_dir); memcpy( reply_fifo_s, fifo_reply_dir, reply_fifo_len); if (signal(SIGHUP, mi_sig_hup) == SIG_ERR ) { LM_ERR("cannot install SIGHUP signal\n"); pkg_free(reply_fifo_s); return 0; } return fifo_stream; } /* reply fifo security checks: * checks if fd is a fifo, is not hardlinked and it's not a softlink * opened file descriptor + file name (for soft link check) * returns 0 if ok, <0 if not */ static int mi_fifo_check(int fd, char* fname) { struct stat fst; struct stat lst; if (fstat(fd, &fst)<0){ LM_ERR("fstat failed: %s\n", strerror(errno)); return -1; } /* check if fifo */ if (!S_ISFIFO(fst.st_mode)){ LM_ERR("%s is not a fifo\n", fname); return -1; } /* check if hard-linked */ if (fst.st_nlink>1){ LM_ERR("security: fifo_check: %s is hard-linked %d times\n", fname, (unsigned)fst.st_nlink); return -1; } /* lstat to check for soft links */ if (lstat(fname, &lst)<0){ LM_ERR("lstat failed: %s\n", strerror(errno)); return -1; } if (S_ISLNK(lst.st_mode)){ LM_ERR("security: fifo_check: %s is a soft link\n", fname); return -1; } /* if this is not a symbolic link, check to see if the inode didn't * change to avoid possible sym.link, rm sym.link & replace w/ fifo race */ if ((lst.st_dev!=fst.st_dev)||(lst.st_ino!=fst.st_ino)){ LM_ERR("security: fifo_check: inode/dev number differ: %d %d (%s)\n", (int)fst.st_ino, (int)lst.st_ino, fname); return -1; } /* success */ return 0; } static inline FILE* get_fifo_stream(FILE *old_stream) { int fd, n; struct stat fst; if (mi_reload_fifo == 0) { fd = fileno(old_stream); if (!mi_fifo_check(fd, mi_fifo_name)) return old_stream; LM_INFO("invalid FIFO file: creating a new one (%s)\n", mi_fifo_name); } else { LM_INFO("Forcefully replacing FIFO file (%s)\n", mi_fifo_name); } /* here we are either forced to reload or the check did not pass */ n = stat(mi_fifo_name, &fst); if (n == 0) { if (unlink(mi_fifo_name) < 0) { LM_ERR("cannot delete fifo file %s\n", mi_fifo_name); return NULL; } LM_INFO("deleted FIFO file (%s)\n", mi_fifo_name); } else if (n < 0 && errno != ENOENT) { LM_ERR("stat failed: %s\n", strerror(errno)); return NULL; } mi_reload_fifo = 0; return mi_create_fifo(); } static FILE *mi_open_reply_pipe( char *pipe_name ) { int fifofd; FILE *file_handle; int flags; int retries=FIFO_REPLY_RETRIES; if (!pipe_name || *pipe_name==0) { LM_DBG("no file to write to about missing cmd\n"); return 0; } tryagain: /* open non-blocking to make sure that a broken client will not * block the FIFO server forever */ fifofd=open( pipe_name, O_WRONLY | O_NONBLOCK ); if (fifofd==-1) { /* retry several times if client is not yet ready for getting feedback via a reply pipe */ if (errno==ENXIO) { /* give up on the client - we can't afford server blocking */ if (retries==0) { LM_ERR("no client at %s\n",pipe_name ); return 0; } /* don't be noisy on the very first try */ if (retries!=FIFO_REPLY_RETRIES) LM_DBG("retry countdown: %d\n", retries ); sleep_us( FIFO_REPLY_WAIT ); retries--; goto tryagain; } /* some other opening error */ LM_ERR("open error (%s): %s\n", pipe_name, strerror(errno)); return 0; } /* security checks: is this really a fifo?, is * it hardlinked? is it a soft link? */ if (mi_fifo_check(fifofd, pipe_name)<0) goto error; /* we want server blocking for big writes */ if ( (flags=fcntl(fifofd, F_GETFL, 0))<0) { LM_ERR("pipe (%s): getfl failed: %s\n", pipe_name, strerror(errno)); goto error; } flags&=~O_NONBLOCK; if (fcntl(fifofd, F_SETFL, flags)<0) { LM_ERR("pipe (%s): setfl cntl failed: %s\n", pipe_name, strerror(errno)); goto error; } /* create an I/O stream */ file_handle=fdopen( fifofd, "w"); if (file_handle==NULL) { LM_ERR("open error (%s): %s\n", pipe_name, strerror(errno)); goto error; } return file_handle; error: close(fifofd); return 0; } static FILE *mi_init_read(FILE *stream, int *fd, fd_set *fds) { FILE *new_stream = get_fifo_stream(stream); if (!new_stream) return NULL; *fd = fileno(new_stream); FD_ZERO(fds); FD_SET(*fd, fds); return new_stream; } int mi_read_line( char *b, int max, FILE **stream, int *read_len) { int ret = 0; int done, i, fd; struct timeval tv; fd_set fds, init_fds; FILE *new_stream; /* first check if we need to update our fifo file */ if (!(new_stream = mi_init_read(*stream, &fd, &init_fds))) return -1; done = 0; for (i = 0; !done && i < max; i++) { fds = init_fds; tv.tv_sec = FIFO_CHECK_WAIT; tv.tv_usec = 0; retry: ret = select(fd + 1, &fds, NULL, NULL, &tv); if (ret < 0) { if (errno == EAGAIN) goto retry; /* interrupted by signal or ... */ if (errno == EINTR) { if (!(new_stream = mi_init_read(new_stream, &fd, &init_fds))) return -1; } else { kill(0, SIGTERM); } } else if (ret == 0) { if (!(new_stream = mi_init_read(new_stream, &fd, &init_fds))) return -1; --i; continue; } ret = read(fd, &b[i], 1); if (ret < 0) return ret; else if (ret == 0 || b[i] == '\n') done = 1; } if (!done) { LM_ERR("request line too long\n"); return -1; } *read_len = i; *stream = new_stream; return 0; } static inline char *get_reply_filename( char * file, int len ) { if ( strchr(file,'.') || strchr(file,'/') || strchr(file, '\\') ) { LM_ERR("forbidden filename: %s\n", file); return 0; } if (reply_fifo_len + len + 1 > MAX_MI_FILENAME) { LM_ERR("reply fifoname too long %d\n",reply_fifo_len + len); return 0; } memcpy( reply_fifo_s+reply_fifo_len, file, len ); reply_fifo_s[reply_fifo_len+len]=0; return reply_fifo_s; } static inline void free_async_handler( struct mi_handler *hdl ) { if (hdl) shm_free(hdl); } static void fifo_close_async( struct mi_root *mi_rpl, struct mi_handler *hdl, int done) { FILE *reply_stream; char *name; name = (char*)hdl->param; if ( mi_rpl!=0 || done ) { /*open fifo reply*/ reply_stream = mi_open_reply_pipe( name ); if (reply_stream==NULL) { LM_ERR("cannot open reply pipe %s\n", name ); return; } if (mi_rpl!=0) { mi_write_tree( reply_stream, mi_rpl); free_mi_tree( mi_rpl ); } else { mi_fifo_reply( reply_stream, "500 command failed\n"); } fclose(reply_stream); } if (done) free_async_handler( hdl ); return; } static inline struct mi_handler* build_async_handler( char *name, int len) { struct mi_handler *hdl; char *p; hdl = (struct mi_handler*)shm_malloc( sizeof(struct mi_handler) + len + 1); if (hdl==0) { LM_ERR("no more shared memory\n"); return 0; } p = (char*)(hdl) + sizeof(struct mi_handler); memcpy( p, name, len+1 ); hdl->handler_f = fifo_close_async; hdl->param = (void*)p; return hdl; } #define mi_do_consume() \ do { \ LM_DBG("entered consume\n"); \ /* consume the rest of the fifo request */ \ do { \ mi_read_line(mi_buf,MAX_MI_FIFO_BUFFER,&fifo_stream,&line_len); \ } while(line_len>1); \ LM_DBG("**** done consume\n"); \ } while(0) #define mi_open_reply(_name,_file,_err) \ do { \ _file = mi_open_reply_pipe( _name ); \ if (_file==NULL) { \ LM_ERR("cannot open reply pipe %s\n", _name); \ goto _err; \ } \ } while(0) void mi_fifo_server(FILE *fifo_stream) { struct mi_root *mi_cmd; struct mi_root *mi_rpl; struct mi_handler *hdl; int line_len; char *file_sep, *command, *file; struct mi_cmd *f; FILE *reply_stream; while(1) { reply_stream = NULL; /* commands must look this way '::[filename]' */ if (mi_read_line(mi_buf,MAX_MI_FIFO_BUFFER,&fifo_stream, &line_len)) { LM_ERR("failed to read command\n"); continue; } /* trim from right */ while(line_len) { if(mi_buf[line_len-1]=='\n' || mi_buf[line_len-1]=='\r' || mi_buf[line_len-1]==' ' || mi_buf[line_len-1]=='\t' ) { line_len--; mi_buf[line_len]=0; } else break; } if (line_len==0) { LM_DBG("command empty\n"); continue; } if (line_len<3) { LM_ERR("command must have at least 3 chars\n"); continue; } if (*mi_buf!=MI_CMD_SEPARATOR) { LM_ERR("command must begin with %c: %.*s\n", MI_CMD_SEPARATOR, line_len, mi_buf ); goto consume1; } command = mi_buf+1; file_sep=strchr(command, MI_CMD_SEPARATOR ); if (file_sep==NULL) { LM_ERR("file separator missing\n"); goto consume1; } if (file_sep==command) { LM_ERR("empty command\n"); goto consume1; } if (*(file_sep+1)==0) { file = NULL; } else { file = file_sep+1; file = get_reply_filename(file, mi_buf+line_len-file); if (file==NULL) { LM_ERR("trimming filename\n"); goto consume1; } } /* make command zero-terminated */ *file_sep=0; f=lookup_mi_cmd( command, strlen(command) ); if (f==0) { LM_ERR("command %s is not available\n", command); mi_open_reply( file, reply_stream, consume1); mi_fifo_reply( reply_stream, "500 command '%s' not available\n", command); goto consume2; } /* if asyncron cmd, build the async handler */ if (f->flags&MI_ASYNC_RPL_FLAG) { hdl = build_async_handler( file, strlen(file) ); if (hdl==0) { LM_ERR("failed to build async handler\n"); mi_open_reply( file, reply_stream, consume1); mi_fifo_reply( reply_stream, "500 Internal server error\n"); goto consume2; } } else { hdl = 0; mi_open_reply( file, reply_stream, consume1); } if (f->flags&MI_NO_INPUT_FLAG) { mi_cmd = 0; mi_do_consume(); } else { mi_cmd = mi_parse_tree(fifo_stream); if (mi_cmd==NULL){ LM_ERR("error parsing MI tree\n"); if (!reply_stream) mi_open_reply( file, reply_stream, consume3); mi_fifo_reply( reply_stream, "400 parse error in " "command '%s'\n", command); goto consume3; } mi_cmd->async_hdl = hdl; } LM_DBG("done parsing the mi tree\n"); if ( (mi_rpl=run_mi_cmd(f, mi_cmd, (mi_flush_f *)mi_flush_tree, reply_stream))==0 ) { if (!reply_stream) mi_open_reply( file, reply_stream, failure); mi_fifo_reply(reply_stream, "500 command '%s' failed\n", command); LM_ERR("command (%s) processing failed\n", command ); } else if (mi_rpl!=MI_ROOT_ASYNC_RPL) { if (!reply_stream) mi_open_reply( file, reply_stream, failure); mi_write_tree( reply_stream, mi_rpl); free_mi_tree( mi_rpl ); } else { if (mi_cmd) free_mi_tree( mi_cmd ); continue; } free_async_handler(hdl); /* close reply fifo */ fclose(reply_stream); /* destroy request tree */ if (mi_cmd) free_mi_tree( mi_cmd ); continue; failure: free_async_handler(hdl); /* destroy request tree */ if (mi_cmd) free_mi_tree( mi_cmd ); /* destroy the reply tree */ if (mi_rpl) free_mi_tree(mi_rpl); continue; consume3: free_async_handler(hdl); if (reply_stream) consume2: fclose(reply_stream); consume1: mi_do_consume(); } } opensips-2.2.2/modules/mi_fifo/fifo_fnc.h000066400000000000000000000033421300170765700203670ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of a module for opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-09-25: first version (bogdan) */ #ifndef _FIFO_FNC_H_ #define _FIFO_FNC_H_ #include #include #include /* how patient is opensips with FIFO clients not awaiting a reply? default = 4 x 80ms = 0.32 sec */ #define FIFO_REPLY_RETRIES 4 #define FIFO_REPLY_WAIT 80000 FILE* mi_init_fifo_server(char *fifo_name, int mode, int uid, int gid, char* fifo_reply_dir); void mi_fifo_server(FILE *fifostream); int mi_read_line( char *b, int max, FILE **stream, int *read); static inline int mi_fifo_reply( FILE *stream, char *reply_fmt, ... ) { int r; va_list ap; retry: va_start(ap, reply_fmt); r = vfprintf( stream, reply_fmt, ap); va_end(ap); if (r<=0) { if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK)) { goto retry; } LM_ERR("fifo_error: write error: %s\n", strerror(errno)); return -1; } return 0; } #endif /* _FIFO_FNC_H */ opensips-2.2.2/modules/mi_fifo/mi_fifo.c000066400000000000000000000140741300170765700202250ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-25 first version (bogdan) */ #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "mi_fifo.h" #include "mi_parser.h" #include "mi_writer.h" #include "fifo_fnc.h" static int mi_mod_init(void); static int mi_child_init(int rank); static void fifo_process(int rank); static int mi_destroy(void); /* FIFO server vars */ /* FIFO name */ static char *mi_fifo = 0; /* dir where reply fifos are allowed */ static char *mi_fifo_reply_dir = DEFAULT_MI_REPLY_DIR; static char *mi_reply_indent = DEFAULT_MI_REPLY_IDENT; static int mi_fifo_uid = -1; static char *mi_fifo_uid_s = 0; static int mi_fifo_gid = -1; static char *mi_fifo_gid_s = 0; static int mi_fifo_mode = S_IRUSR| S_IWUSR| S_IRGRP| S_IWGRP; /* rw-rw---- */ static int read_buf_size = MAX_MI_FIFO_READ; static param_export_t mi_params[] = { {"fifo_name", STR_PARAM, &mi_fifo}, {"fifo_mode", INT_PARAM, &mi_fifo_mode}, {"fifo_group", STR_PARAM, &mi_fifo_gid_s}, {"fifo_group", INT_PARAM, &mi_fifo_gid}, {"fifo_user", STR_PARAM, &mi_fifo_uid_s}, {"fifo_user", INT_PARAM, &mi_fifo_uid}, {"reply_dir", STR_PARAM, &mi_fifo_reply_dir}, {"reply_indent", STR_PARAM, &mi_reply_indent}, {0,0,0} }; static proc_export_t mi_procs[] = { {"MI FIFO", 0, 0, fifo_process, 1 , PROC_FLAG_INITCHILD }, {0,0,0,0,0,0} }; struct module_exports exports = { "mi_fifo", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ mi_params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ mi_procs, /* extra processes */ mi_mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) mi_destroy, /* destroy function */ mi_child_init /* per-child init function */ }; static int mi_mod_init(void) { int n; struct stat filestat; /* checking the mi_fifo module param */ if (mi_fifo==NULL || *mi_fifo == 0) { LM_ERR("no fifo configured\n"); return -1; } LM_DBG("testing fifo existence ...\n"); n=stat(mi_fifo, &filestat); if (n==0){ /* FIFO exist, delete it (safer) */ if (unlink(mi_fifo)<0){ LM_ERR("cannot delete old fifo (%s): %s\n", mi_fifo, strerror(errno)); return -1; } }else if (n<0 && errno!=ENOENT){ LM_ERR("FIFO stat failed: %s\n", strerror(errno)); return -1; } /* checking the mi_fifo_reply_dir param */ if(!mi_fifo_reply_dir || *mi_fifo_reply_dir == 0){ LM_ERR("mi_fifo_reply_dir parameter is empty\n"); return -1; } n = stat(mi_fifo_reply_dir, &filestat); if(n < 0){ LM_ERR("directory stat failed: %s\n", strerror(errno)); return -1; } if(S_ISDIR(filestat.st_mode) == 0){ LM_ERR("mi_fifo_reply_dir parameter is not a directory\n"); return -1; } /* check mi_fifo_mode */ if(!mi_fifo_mode){ LM_WARN("cannot specify mi_fifo_mode = 0, forcing it to rw-------\n"); mi_fifo_mode = S_IRUSR| S_IWUSR; } if (mi_fifo_uid_s){ if (user2uid(&mi_fifo_uid, &mi_fifo_gid, mi_fifo_uid_s)<0){ LM_ERR("bad user name %s\n", mi_fifo_uid_s); return -1; } } if (mi_fifo_gid_s){ if (group2gid(&mi_fifo_gid, mi_fifo_gid_s)<0){ LM_ERR("bad group name %s\n", mi_fifo_gid_s); return -1; } } return 0; } static int mi_child_init(int rank) { if (rank>PROC_MAIN ) { if ( mi_writer_init(read_buf_size, mi_reply_indent)!=0 ) { LM_CRIT("failed to init the reply writer\n"); return -1; } } return 0; } static void fifo_process(int rank) { FILE *fifo_stream; LM_DBG("new process with pid = %d created\n",getpid()); fifo_stream = mi_init_fifo_server( mi_fifo, mi_fifo_mode, mi_fifo_uid, mi_fifo_gid, mi_fifo_reply_dir); if ( fifo_stream==NULL ) { LM_CRIT("The function mi_init_fifo_server returned with error!!!\n"); exit(-1); } if( init_mi_child()!=0) { LM_CRIT("faild to init the mi process\n"); exit(-1); } if ( mi_parser_init(read_buf_size)!=0 ) { LM_CRIT("failed to init the command parser\n"); exit(-1); } if ( mi_writer_init(read_buf_size, mi_reply_indent)!=0 ) { LM_CRIT("failed to init the reply writer\n"); exit(-1); } mi_fifo_server( fifo_stream ); LM_CRIT("the function mi_fifo_server returned with error!!!\n"); exit(-1); } static int mi_destroy(void) { int n; struct stat filestat; /* destroying the fifo file */ n=stat(mi_fifo, &filestat); if (n==0){ /* FIFO exist, delete it (safer) */ if (unlink(mi_fifo)<0){ LM_ERR("cannot delete the fifo (%s): %s\n", mi_fifo, strerror(errno)); goto error; } } else if (n<0 && errno!=ENOENT) { LM_ERR("FIFO stat failed: %s\n", strerror(errno)); goto error; } return 0; error: return -1; } opensips-2.2.2/modules/mi_fifo/mi_fifo.h000066400000000000000000000031301300170765700202210ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-25 first version (bogdan) */ #ifndef _MI_FIFO_H_ #define _MI_FIFO_H_ #define DEFAULT_MI_REPLY_DIR "/tmp/" #define DEFAULT_MI_REPLY_IDENT "\t" #define MI_CMD_SEPARATOR ':' /* the 2-chars separator between name and value */ #define MI_ATTR_VAL_SEP1 ':' #define MI_ATTR_VAL_SEP2 ':' /* maximum length of a FIFO line */ #define MAX_MI_FIFO_BUFFER 1024 /* how patient is ser with FIFO clients not awaiting a reply? 4 x 80ms = 0.32 sec */ #define FIFO_REPLY_RETRIES 4 /* maximum size for the composed fifo reply name */ #define MAX_MI_FILENAME 128 /* size of buffer used by parser to read and build the MI tree */ #define MAX_MI_FIFO_READ 8192 /* we are waiting for a while between fifo file checks */ #define FIFO_CHECK_WAIT 30 #endif /* _MI_FIFO */ opensips-2.2.2/modules/mi_fifo/mi_parser.c000066400000000000000000000123731300170765700205760ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-25 first version (bogdan) */ #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../mi/tree.h" #include "../../mem/mem.h" #include "fifo_fnc.h" #include "mi_fifo.h" #include "mi_parser.h" static char *mi_parse_buffer = 0; static unsigned int mi_parse_buffer_len = 0; int mi_parser_init( unsigned int size ) { mi_parse_buffer_len = size; mi_parse_buffer = pkg_malloc(size); if(!mi_parse_buffer){ LM_ERR("pkg_malloc cannot allocate any more memory!\n"); return -1; } return 0; } /* returns -1 = error * 0 = ok * 1 = end of stream */ static inline int mi_parse_node( FILE *stream, str *buf, str *name, str *value) { char *p, *pmax; char *start; char *mark_nsp; int line_len; /* read one line */ do { if (mi_read_line( buf->s, buf->len, &stream, &line_len)<0) { LM_ERR("failed to read from fifo\n"); return -1; } if (line_len == 1){ LM_DBG("end of input tree\n"); return 1; } start = buf->s; pmax = buf->s + line_len - 1; /* remove leading spaces */ for( ; starts = value->s = 0; name->len = value->len = 0; mark_nsp = 0; /* start parsing */ if (*start!='"') { /* look for the atribute name */ p = mark_nsp = start; while ( p!=pmax && ( (p[0]!=MI_ATTR_VAL_SEP1) || (p+1==pmax) || p[1]!=MI_ATTR_VAL_SEP2) ) { if (!isspace((int)*p)) { if (*p=='"') goto parse_err; mark_nsp = p; } p++; } if (p!=pmax) { /* we have found the separator */ if (p==start) { /* empty attr name */ } else { name->s = start; name->len = mark_nsp - start + 1; } p += 2; /* for separator */ LM_DBG("attr name <%.*s> found\n", name->len, name->s); /* consume the trailing spaces */ for( ; p!=pmax && isspace((int)*p) ; p++); if (p==pmax) { /* empty value.....we are done */ goto done; } /* value (only if not quoted ) */ if (*p!='"') { for( start=p ; p!=pmax ; p++ ) { if (!isspace((int)*p)) { if (*p=='"') goto parse_err; mark_nsp = p; } } value->s = start; value->len = mark_nsp + 1 - start; goto done; } /* quoted value....continue */ } else { /* we have an empty name ... and we read a non-quoted value */ value->s = start; value->len = mark_nsp + 1 - start; goto done; } } else { p = start; } start = p+1; value->s = start; do { p = start; /* parse the buffer and look for " */ while (plen = p - value->s; /* is the line ending propely (only spaces) ? */ for( p++ ; p!=pmax && isspace((int)*p) ; p++); if (p!=pmax) goto parse_err; /* found! */ goto done; } } else { p++; } } /* adjust input buffer */ p++; buf->len -= p - buf->s; buf->s = p; /*read one more line */ if (mi_read_line( buf->s, buf->len, &stream, &line_len)<0) { LM_ERR("failed to re-read from fifo\n"); return -1; } if (line_len == 1) { LM_DBG("end of input tree\n"); return -2; } start = buf->s; pmax = buf->s + line_len - 1; } while(1); done: buf->len -= p - buf->s; buf->s = p; return 0; parse_err: LM_ERR("parse error around %c\n",*p); return -1; } struct mi_root * mi_parse_tree(FILE *stream) { struct mi_root *root; struct mi_node *node; str name; str value; str buf; int ret; buf.s = mi_parse_buffer; buf.len= mi_parse_buffer_len; root = init_mi_tree(0,0,0); if (!root) { LM_ERR("the MI tree cannot be initialized!\n"); goto error; } node = &root->node; name.s = value.s = 0; name.len = value.len = 0; /* every tree for a command ends with a \n that is alone on its line */ while ( (ret=mi_parse_node(stream, &buf, &name, &value))>=0 ) { if (ret==1) return root; LM_DBG("adding node <%.*s> ; val <%.*s>\n", name.len,name.s, value.len,value.s); if(!add_mi_node_child(node,0,name.s,name.len,value.s,value.len)){ LM_ERR("cannot add the child node to the tree\n"); goto error; } } LM_ERR("Parse error!\n"); if (ret==-1) { /* consume the rest of the fifo request */ do { mi_read_line(mi_parse_buffer,mi_parse_buffer_len,&stream,&ret); }while(ret>1); } error: if (root) free_mi_tree(root); return 0; } opensips-2.2.2/modules/mi_fifo/mi_parser.h000066400000000000000000000020551300170765700205770ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-25 first version (bogdan) */ #ifndef _MI_PARSER_H_ #define _MI_PARSER_H_ #include int mi_parser_init( unsigned int size ); struct mi_root *mi_parse_tree(FILE *stream); #endif /* _MI_PARSER_H_ */ opensips-2.2.2/modules/mi_fifo/mi_writer.c000066400000000000000000000153621300170765700206170ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-25 first version (bogdan) */ #include #include #include "../../str.h" #include "../../ut.h" #include "../../dprint.h" #include "../../mi/tree.h" #include "../../mem/mem.h" #include "mi_fifo.h" #include "fifo_fnc.h" #include "mi_parser.h" static char *mi_write_buffer = 0; static unsigned int mi_write_buffer_len = 0; static str mi_fifo_indent; int mi_writer_init( unsigned int size , char *indent) { mi_write_buffer_len = size; mi_write_buffer = pkg_malloc(size); if(!mi_write_buffer){ LM_ERR("pkg_malloc cannot allocate any more memory!\n"); return -1; } if (indent==NULL || indent[0]==0 ) { mi_fifo_indent.s = 0; mi_fifo_indent.len = 0; } else { mi_fifo_indent.s = indent; mi_fifo_indent.len = strlen(indent); } return 0; } void mi_writer_destroy(void) { pkg_free(mi_write_buffer); } static inline int mi_write_node(str *buf, struct mi_node *node, int level) { struct mi_attr *attr; char *end; char *p; p = buf->s; end = buf->s + buf->len; /* write indents */ if (mi_fifo_indent.s) { if (p + level*mi_fifo_indent.len>end) return -1; for( ; level>0 ; level-- ) { memcpy( p, mi_fifo_indent.s, mi_fifo_indent.len); p += mi_fifo_indent.len; } } /* name and value */ if (node->name.s!=NULL) { if (p+node->name.len+3>end) return -1; memcpy(p,node->name.s,node->name.len); p += node->name.len; *(p++) = MI_ATTR_VAL_SEP1; *(p++) = MI_ATTR_VAL_SEP2; *(p++) = ' '; } if (node->value.s!=NULL) { if (p+node->value.len>end) return -1; memcpy(p,node->value.s,node->value.len); p += node->value.len; } /* attributes */ for( attr=node->attributes ; attr!=NULL ; attr=attr->next ) { if (attr->name.s!=NULL) { if (p+attr->name.len+2>end) return -1; *(p++) = ' '; memcpy(p,attr->name.s,attr->name.len); p += attr->name.len; *(p++) = '='; } if (attr->value.s!=NULL) { if (p+attr->value.len>end) return -1; memcpy(p,attr->value.s,attr->value.len); p += attr->value.len; } } if (p+1>end) return -1; *(p++) = '\n'; buf->len -= p-buf->s; buf->s = p; return 0; } static int recur_write_tree(FILE *stream, struct mi_node *tree, str *buf, int level) { for( ; tree ; tree=tree->next ) { if (!(tree->flags & MI_WRITTEN)) { if (mi_write_node( buf, tree, level)!=0) { /* buffer is full -> write it and reset buffer */ if (mi_fifo_reply( stream,"%.*s", buf->s-mi_write_buffer, mi_write_buffer)!=0) return -1; buf->s = mi_write_buffer; buf->len = mi_write_buffer_len; if (mi_write_node( buf, tree, level)!=0) { LM_ERR("failed to write - line too long!\n"); return -1; } } } if (tree->kids) { if (recur_write_tree(stream, tree->kids, buf, level+1)<0) return -1; } } return 0; } int mi_write_tree(FILE *stream, struct mi_root *tree) { str buf; str code; buf.s = mi_write_buffer; buf.len = mi_write_buffer_len; if (!(tree->node.flags & MI_WRITTEN)) { /* write the root node */ code.s = int2str((unsigned long)tree->code, &code.len); if (code.len+tree->reason.len+1>buf.len) { LM_ERR("failed to write - reason too long!\n"); return -1; } memcpy( buf.s, code.s, code.len); buf.s += code.len; *(buf.s++) = ' '; if (tree->reason.len) { memcpy( buf.s, tree->reason.s, tree->reason.len); buf.s += tree->reason.len; } *(buf.s++) = '\n'; buf.len -= code.len + 1 + tree->reason.len+1; } if (recur_write_tree(stream, tree->node.kids, &buf, 0)!=0) return -1; if (buf.len<=0) { LM_ERR("failed to write - EOC does not fit in!\n"); return -1; } *(buf.s++)='\n'; buf.len--; if (mi_fifo_reply(stream,"%.*s",buf.s-mi_write_buffer,mi_write_buffer)!=0) return -1; return 0; } static int recur_flush_tree(FILE *stream, struct mi_node *tree, str *buf, int level) { struct mi_node *kid, *tmp; int ret; for(kid = tree->kids ; kid ; ){ /* write the current kid */ if (!(kid->flags & MI_WRITTEN)){ if (mi_write_node( buf, kid, level)!=0) { /* buffer is full -> write it and reset buffer */ if (mi_fifo_reply( stream,"%.*s", buf->s-mi_write_buffer, mi_write_buffer)!=0) return -1; buf->s = mi_write_buffer; buf->len = mi_write_buffer_len; if (mi_write_node( buf, kid, level)!=0) { LM_ERR("failed to write - line too long!\n"); return -1; } } /* we are sure that this node has been written * => avoid writing it again */ kid->flags |= MI_WRITTEN; } /* write the current kid's children */ if ((ret = recur_flush_tree(stream, kid, buf, level+1))<0) return -1; else if (ret > 0) return ret; if (!(kid->flags & MI_NOT_COMPLETED)){ tmp = kid; kid = kid->next; tree->kids = kid; if(!tmp->kids){ /* this node does not have any kids */ free_mi_node(tmp); } } else{ /* the node will have more kids => to keep the tree shape, do not * flush any other node for now */ return 1; } } return 0; } int mi_flush_tree(FILE *stream, struct mi_root *tree) { str buf; str code; buf.s = mi_write_buffer; buf.len = mi_write_buffer_len; if (!(tree->node.flags & MI_WRITTEN)) { /* write the root node */ code.s = int2str((unsigned long)tree->code, &code.len); if (code.len+tree->reason.len+1>buf.len) { LM_ERR("failed to write - reason too long!\n"); return -1; } memcpy( buf.s, code.s, code.len); buf.s += code.len; *(buf.s++) = ' '; if (tree->reason.len) { memcpy( buf.s, tree->reason.s, tree->reason.len); buf.s += tree->reason.len; } *(buf.s++) = '\n'; buf.len -= code.len + 1 + tree->reason.len+1; /* we are sure that this node has been written * => avoid writing it again */ tree->node.flags |= MI_WRITTEN; } if (recur_flush_tree(stream, &tree->node, &buf, 0)<0) return -1; if (buf.len<=0) { LM_ERR("failed to write - EOC does not fit in!\n"); return -1; } *(buf.s++)='\n'; buf.len--; if (mi_fifo_reply(stream,"%.*s",buf.s-mi_write_buffer,mi_write_buffer)!=0) return -1; return 0; } opensips-2.2.2/modules/mi_fifo/mi_writer.h000066400000000000000000000022311300170765700206130ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-09-25 first version (bogdan) */ #ifndef _MI_WRITER_H_ #define _MI_WRITER_H_ #include int mi_writer_init( unsigned int size, char *ident); void mi_writer_destroy(void); int mi_write_tree( FILE *stream, struct mi_root *tree); int mi_flush_tree(FILE *stream, struct mi_root *tree); #endif /* _MI_WRITER_H_ */ opensips-2.2.2/modules/mi_http/000077500000000000000000000000001300170765700164775ustar00rootroot00000000000000opensips-2.2.2/modules/mi_http/Makefile000066400000000000000000000002531300170765700201370ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=mi_http.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/mi_http/README000066400000000000000000000037721300170765700173700ustar00rootroot00000000000000mi_http Module Ovidiu Sas Edited by Ovidiu Sas Copyright © 2011-2013 VoIP Embedded, Inc. Revision History Revision $Rev: 8688 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. To-do 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.4. Exported Parameters 1.4.1. mi_http_root(string) 1.4.2. mi_http_method(integer) 1.5. Exported Functions 1.6. Known issues List of Examples 1.1. Set mi_http_root parameter 1.2. Set mi_http_method parameter Chapter 1. Admin Guide 1.1. Overview This module provides an HTTP transport layer implementation for OpenSIPS's Management Interface. 1.2. To-do Features to be added in the future: * possibility to authenticate connections. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * httpd module. 1.4. Exported Parameters 1.4.1. mi_http_root(string) Specifies the root path for mi http requests. The link to the mi web interface must be constructed using the following patern: http://[opensips_IP]:[opensips_mi_port]/[mi_http_root] The default value is "mi". Example 1.1. Set mi_http_root parameter ... modparam("mi_http", "mi_http_root", "opensips_mi") ... 1.4.2. mi_http_method(integer) Specifies the HTTP request method to be used: * 0 - use GET HTTP request * 1 - use POST HTTP request The default value is 0. Example 1.2. Set mi_http_method parameter ... modparam("mi_http", "mi_http_method", 1) ... 1.5. Exported Functions No function exported to be used from configuration file. 1.6. Known issues Commands with large responses (like ul_dump) will fail if the configured size of the httpd buffer is to small (or if there isn't enough pkg memory configured). Future realeases of the httpd and mi_httpd modules will address this issue. opensips-2.2.2/modules/mi_http/doc/000077500000000000000000000000001300170765700172445ustar00rootroot00000000000000opensips-2.2.2/modules/mi_http/doc/mi_http.xml000066400000000000000000000020731300170765700214340ustar00rootroot00000000000000 %docentities; ]> mi_http Module &osipsname; Ovidiu Sas osas@voipembedded.com Ovidiu Sas 2011-2013 VoIP Embedded, Inc. $Rev: 8688 $ $Date$ &admin; &faq; opensips-2.2.2/modules/mi_http/doc/mi_http_admin.xml000066400000000000000000000046351300170765700226120ustar00rootroot00000000000000 &adminguide;
Overview This module provides an HTTP transport layer implementation for &osips;'s Management Interface.
To-do Features to be added in the future: possibility to authenticate connections.
Dependencies
&osips; Modules The following modules must be loaded before this module: httpd module.
Exported Parameters
<varname>mi_http_root</varname>(string) Specifies the root path for mi http requests. The link to the mi web interface must be constructed using the following patern: http://[opensips_IP]:[opensips_mi_port]/[mi_http_root] The default value is "mi". Set <varname>mi_http_root</varname> parameter ... modparam("mi_http", "mi_http_root", "opensips_mi") ...
<varname>mi_http_method</varname>(integer) Specifies the HTTP request method to be used: 0 - use GET HTTP request 1 - use POST HTTP request The default value is 0. Set <varname>mi_http_method</varname> parameter ... modparam("mi_http", "mi_http_method", 1) ...
Exported Functions No function exported to be used from configuration file.
Known issues Commands with large responses (like ul_dump) will fail if the configured size of the httpd buffer is to small (or if there isn't enough pkg memory configured). Future realeases of the httpd and mi_httpd modules will address this issue.
opensips-2.2.2/modules/mi_http/http_fnc.c000066400000000000000000000730461300170765700204620ustar00rootroot00000000000000/* * Copyright (C) 2011-2013 VoIP Embedded, Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2011-09-20 first version (osas) */ #include "../../str.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "../../config.h" #include "../../globals.h" #include "../../locking.h" #include "http_fnc.h" extern str http_root; extern int http_method; str upSinceCTime; http_mi_cmd_t* http_mi_cmds; int http_mi_cmds_size; mi_http_html_page_data_t html_page_data; gen_lock_t* mi_http_lock; #define MI_HTTP_COPY(p,str) \ do{ \ if ((int)((p)-buf)+(str).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str).s, (str).len); (p) += (str).len; \ }while(0) #define MI_HTTP_COPY_2(p,str1,str2) \ do{ \ if ((int)((p)-buf)+(str1).len+(str2).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str1).s, (str1).len); (p) += (str1).len; \ memcpy((p), (str2).s, (str2).len); (p) += (str2).len; \ }while(0) #define MI_HTTP_COPY_3(p,str1,str2,str3) \ do{ \ if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str1).s, (str1).len); (p) += (str1).len; \ memcpy((p), (str2).s, (str2).len); (p) += (str2).len; \ memcpy((p), (str3).s, (str3).len); (p) += (str3).len; \ }while(0) #define MI_HTTP_COPY_4(p,str1,str2,str3,str4) \ do{ \ if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len+(str4).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str1).s, (str1).len); (p) += (str1).len; \ memcpy((p), (str2).s, (str2).len); (p) += (str2).len; \ memcpy((p), (str3).s, (str3).len); (p) += (str3).len; \ memcpy((p), (str4).s, (str4).len); (p) += (str4).len; \ }while(0) #define MI_HTTP_COPY_5(p,s1,s2,s3,s4,s5) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ }while(0) #define MI_HTTP_COPY_6(p,s1,s2,s3,s4,s5,s6) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ }while(0) #define MI_HTTP_COPY_7(p,s1,s2,s3,s4,s5,s6,s7) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ }while(0) #define MI_HTTP_COPY_10(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \ memcpy((p), (s10).s, (s10).len); (p) += (s10).len; \ }while(0) #define MI_HTTP_COPY_11(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len+(s11).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \ memcpy((p), (s10).s, (s10).len); (p) += (s10).len; \ memcpy((p), (s11).s, (s11).len); (p) += (s11).len; \ }while(0) #define MI_HTTP_COPY_12(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len+(s11).len+(s12).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \ memcpy((p), (s10).s, (s10).len); (p) += (s10).len; \ memcpy((p), (s11).s, (s11).len); (p) += (s11).len; \ memcpy((p), (s12).s, (s12).len); (p) += (s12).len; \ }while(0) #define MI_HTTP_ESC_COPY(p,str,temp_holder,temp_counter) \ do{ \ (temp_holder).s = (str).s; \ (temp_holder).len = 0; \ for((temp_counter)=0;(temp_counter)<(str).len;(temp_counter)++) { \ switch((str).s[(temp_counter)]) { \ case '<': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_HTTP_COPY_2(p, (temp_holder), MI_HTTP_ESC_LT); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '>': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_HTTP_COPY_2(p, (temp_holder), MI_HTTP_ESC_GT); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '&': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_HTTP_COPY_2(p, (temp_holder), MI_HTTP_ESC_AMP); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '"': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_HTTP_COPY_2(p, (temp_holder), MI_HTTP_ESC_QUOT); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '\'': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_HTTP_COPY_2(p, (temp_holder), MI_HTTP_ESC_SQUOT); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ } \ } \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_HTTP_COPY(p, (temp_holder)); \ }while(0) static const str MI_HTTP_METHOD[] = { str_init("GET"), str_init("POST") }; static const str MI_HTTP_Response_Head_1 = str_init(""\ "OpenSIPS Management Interface"\ ""\ ""\ ""); static const str MI_HTTP_Response_Head_2 = str_init(\ ""\ "\n"\ ""); static const str MI_HTTP_Response_Title_Table_1 = str_init(\ ""\ ""\ "
"\ "

OpenSIPS MI HTTP Interface

"\ "

"); static const str MI_HTTP_Response_Title_Table_2 = str_init(" is running since "); static const str MI_HTTP_Response_Title_Table_3 = str_init("


\n
\n"); static const str MI_HTTP_Response_Menu_Table_1 = str_init("\n"); static const str MI_HTTP_Response_Menu_Table_2 = str_init("
"); static const str MI_HTTP_Response_Menu_Table_4 = str_init("\n"); static const str MI_HTTP_Response_Menu_Table_4b = str_init("\n"); static const str MI_HTTP_Response_Menu_Table_5 = str_init("
\n"); static const str MI_HTTP_Response_Menu_Cmd_Table_1 = str_init("\n"); static const str MI_HTTP_Response_Menu_Cmd_tr_1 = str_init("\n"); static const str MI_HTTP_Response_Menu_Cmd_td_1a = str_init(" \n"); static const str MI_HTTP_Response_Menu_Cmd_td_1b = str_init(" \n"); static const str MI_HTTP_Response_Menu_Cmd_td_4c = str_init(" \n"); static const str MI_HTTP_Response_Menu_Cmd_td_4d = str_init("\n"); static const str MI_HTTP_Response_Menu_Cmd_tr_2 = str_init("\n"); static const str MI_HTTP_Response_Menu_Cmd_Table_2 = str_init("
"); static const str MI_HTTP_Response_Menu_Cmd_td_4a = str_init(""); static const str MI_HTTP_Response_Menu_Cmd_td_1c = str_init(" "); static const str MI_HTTP_Response_Menu_Cmd_td_3c = str_init("\">"); static const str MI_HTTP_Response_Menu_Cmd_td_4b = str_init("
\n"); static const str MI_HTTP_NBSP = str_init(" "); static const str MI_HTTP_SLASH = str_init("/"); static const str MI_HTTP_SEMICOLON = str_init(" : "); static const str MI_HTTP_NODE_INDENT = str_init("\t"); static const str MI_HTTP_NODE_SEPARATOR = str_init(":: "); static const str MI_HTTP_ATTR_SEPARATOR = str_init(" "); static const str MI_HTTP_ATTR_VAL_SEPARATOR = str_init("="); static const str MI_HTTP_BREAK = str_init("
"); static const str MI_HTTP_CODE_1 = str_init("
");
static const str MI_HTTP_CODE_2 = str_init("
"); static const str MI_HTTP_Post_1 = str_init("\n"\ "
\n"\ " \n"\ " \n"\ "
\n"); static const str MI_HTTP_Response_Foot = str_init(\ "\n
\n
"\ ""\ "OpenSIPS web site
"\ "Copyright © 2011-2015 VoIP Embedded, Inc."\ ". All rights reserved."\ "
"); #define MI_HTTP_ROWSPAN 5 static const str MI_HTTP_CMD_ROWSPAN = str_init("5"); static const str MI_HTTP_ESC_LT = str_init("<"); /* < */ static const str MI_HTTP_ESC_GT = str_init(">"); /* > */ static const str MI_HTTP_ESC_AMP = str_init("&"); /* & */ static const str MI_HTTP_ESC_QUOT = str_init("""); /* " */ static const str MI_HTTP_ESC_SQUOT = str_init("'"); /* ' */ int mi_http_init_async_lock(void) { mi_http_lock = lock_alloc(); if (mi_http_lock==NULL) { LM_ERR("failed to create lock\n"); return -1; } if (lock_init(mi_http_lock)==NULL) { LM_ERR("failed to init lock\n"); return -1; } return 0; } void mi_http_destroy_async_lock(void) { if (mi_http_lock) { lock_destroy(mi_http_lock); lock_dealloc(mi_http_lock); } } int mi_http_parse_url(const char* url, int* mod, int* cmd) { int url_len = strlen(url); int index = 0; int i; int mod_len, cmd_len; if (url_len<0) { LM_ERR("Invalid url length [%d]\n", url_len); return -1; } if (url_len==0) return 0; if (url[0] != '/') { LM_ERR("URL starting with [%c] instead of'/'\n", *url); return -1; } index++; /* Looking for "mod" */ if (index>=url_len) return 0; for(i=index;i=url_len) return 0; /* skip over '/' */ index++; /* Looking for "cmd" */ if (index>=url_len) return 0; for(i=index;i=url_len) return 0; /* skip over '/' */ index++; if (url_len - index>0) { LM_DBG("got extra [%s]\n", &url[index]); } return 0; } static int mi_http_recur_flush_tree(char** pointer, char *buf, int max_page_len, struct mi_node *tree, int level); int mi_http_flush_content(str *page, int max_page_len, int mod, int cmd, struct mi_root* tree); int mi_http_flush_tree(void* param, struct mi_root *tree) { if (param==NULL) { LM_CRIT("null param\n"); return 0; } mi_http_html_page_data_t* html_p_data = (mi_http_html_page_data_t*)param; mi_http_flush_content(&html_p_data->page, html_p_data->buffer.len, html_p_data->mod, html_p_data->cmd, tree); return 0; } struct mi_root* mi_http_parse_tree(str* buf) { struct mi_root *root; struct mi_node *node; str name = {NULL, 0}; str value = {NULL, 0}; char *start, *pmax; root = init_mi_tree(0,0,0); if (!root) { LM_ERR("the MI tree cannot be initialized!\n"); return NULL; } if (buf->len == 0) return root; node = &root->node; start = buf->s; pmax = buf->s + buf->len; LM_DBG("original: [%.*s]\n",(int)(pmax-start),start); while (start<=pmax) { /* remove leading spaces */ //for(;startparam [%p], and done [%u]\n", mi_rpl, hdl, hdl->param, done); if (!done) { /* we do not pass provisional stuff (yet) */ if (mi_rpl) free_mi_tree( mi_rpl ); return; } async_resp_data = (mi_http_async_resp_data_t*)(hdl+1); lock = async_resp_data->lock; if (mi_rpl==NULL || (shm_rpl=clone_mi_tree( mi_rpl, 1))==NULL) { LM_WARN("Unable to process async reply [%p]\n", mi_rpl); /* mark it as invalid */ shm_rpl = MI_HTTP_ASYNC_FAILED; } if (mi_rpl) free_mi_tree(mi_rpl); lock_get(lock); if (hdl->param==NULL) { hdl->param = shm_rpl; x = 0; } else { x = 1; } LM_DBG("shm_rpl [%p], hdl [%p], hdl->param [%p]\n", shm_rpl, hdl, hdl->param); lock_release(lock); if (x) { if (shm_rpl!=MI_HTTP_ASYNC_FAILED) free_shm_mi_tree(shm_rpl); shm_free(hdl); } return; } static inline struct mi_handler* mi_http_build_async_handler(int mod, int cmd) { struct mi_handler *hdl; mi_http_async_resp_data_t *async_resp_data; unsigned int len; len = sizeof(struct mi_handler)+sizeof(mi_http_async_resp_data_t); hdl = (struct mi_handler*)shm_malloc(len); if (hdl==NULL) { LM_ERR("oom\n"); return NULL; } memset(hdl, 0, len); async_resp_data = (mi_http_async_resp_data_t*)(hdl+1); hdl->handler_f = mi_http_close_async; hdl->param = NULL; async_resp_data->mod = mod; async_resp_data->cmd = cmd; async_resp_data->lock = mi_http_lock; LM_DBG("hdl [%p], hdl->param [%p], mi_http_lock=[%p]\n", hdl, hdl->param, async_resp_data->lock); return hdl; } struct mi_root* mi_http_run_mi_cmd(int mod, int cmd, const str* arg, str *page, str *buffer, struct mi_handler **async_hdl) { struct mi_cmd *f; struct mi_root *mi_cmd = NULL; struct mi_root *mi_rpl = NULL; struct mi_handler *hdl = NULL; str miCmd; str buf; if (mod<0 && cmd<0) { LM_ERR("Incorect params: mod=[%d], cmd=[%d]\n", mod, cmd); goto error; } miCmd = http_mi_cmds[mod].cmds[cmd].name; f = lookup_mi_cmd(miCmd.s, miCmd.len); if (f == NULL) { LM_ERR("unable to find mi command [%.*s]\n", miCmd.len, miCmd.s); goto error; } if (f->flags&MI_ASYNC_RPL_FLAG) { /* We need to build an async handler */ hdl = mi_http_build_async_handler(mod, cmd); if (hdl==NULL) { LM_ERR("failed to build async handler\n"); goto error; } } else { hdl = NULL; } if (f->flags&MI_NO_INPUT_FLAG) { mi_cmd = NULL; } else { if (arg->s) { buf.s = arg->s; buf.len = arg->len; LM_DBG("start parsing [%d][%s]\n", buf.len, buf.s); mi_cmd = mi_http_parse_tree(&buf); if (mi_cmd==NULL) goto error; mi_cmd->async_hdl = hdl; } else { mi_cmd = NULL; } } html_page_data.page.s = buffer->s; html_page_data.page.len = 0; html_page_data.buffer.s = buffer->s; html_page_data.buffer.len = buffer->len; html_page_data.mod = mod; html_page_data.cmd = cmd; mi_rpl = run_mi_cmd(f, mi_cmd, (mi_flush_f *)mi_http_flush_tree, &html_page_data); if (mi_rpl == NULL) { LM_ERR("failed to process the command\n"); goto error; } else { *page = html_page_data.page; } LM_DBG("got mi_rpl=[%p]\n",mi_rpl); *async_hdl = hdl; if (mi_cmd) free_mi_tree(mi_cmd); return mi_rpl; error: if (mi_cmd) free_mi_tree(mi_cmd); if (hdl) shm_free(hdl); *async_hdl = NULL; return NULL; } int init_upSinceCTime(void) { char* p; /* Build a cache value of initial startup time */ p = ctime(&startup_time); upSinceCTime.len = strlen(p)-1; upSinceCTime.s = (char*)pkg_malloc(upSinceCTime.len); if (upSinceCTime.s==NULL) { LM_ERR("oom\n"); return -1; } memcpy(upSinceCTime.s, p, upSinceCTime.len); return 0; } int mi_http_init_cmds(void) { int size, i; struct mi_cmd* cmds; http_mi_cmd_t *mi_cmd; /* Build a cache of all mi commands */ get_mi_cmds(&cmds, &size); if (size<=0) { LM_ERR("Unable to get mi comands\n"); return -1; } http_mi_cmds = (http_mi_cmd_t*)pkg_malloc(sizeof(http_mi_cmd_t)); if (http_mi_cmds==NULL) { LM_ERR("oom\n"); return -1; } http_mi_cmds->cmds = &cmds[0]; http_mi_cmds->size = 0; http_mi_cmds_size = 1; mi_cmd = http_mi_cmds; for(i=0;icmds->module.s == cmds[i].module.s) { mi_cmd->size++; } else { mi_cmd = (http_mi_cmd_t*)pkg_realloc(http_mi_cmds, (http_mi_cmds_size+1)*sizeof(http_mi_cmd_t)); if (mi_cmd==NULL) { LM_ERR("oom\n"); return -1; } http_mi_cmds = mi_cmd; mi_cmd = &http_mi_cmds[http_mi_cmds_size]; http_mi_cmds_size++; mi_cmd->cmds = &cmds[i]; mi_cmd->size = 0; mi_cmd->size++; } } /* Build a cache value of initial startup time */ return init_upSinceCTime(); } static inline int mi_http_write_node(char** pointer, char* buf, int max_page_len, struct mi_node *node, int level) { struct mi_attr *attr; int temp_counter; str temp_holder; /* name and value */ if (node->name.s!=NULL) { for(;level>0;level--) { MI_HTTP_COPY(*pointer,MI_HTTP_NODE_INDENT); } MI_HTTP_COPY(*pointer,node->name); } if (node->value.s!=NULL) { MI_HTTP_COPY(*pointer,MI_HTTP_NODE_SEPARATOR); MI_HTTP_ESC_COPY(*pointer, node->value, temp_holder, temp_counter); } /* attributes */ for(attr=node->attributes;attr!=NULL;attr=attr->next) { if (attr->name.s!=NULL) { MI_HTTP_COPY_3(*pointer, MI_HTTP_ATTR_SEPARATOR, attr->name, MI_HTTP_ATTR_VAL_SEPARATOR); if(attr->value.len) { MI_HTTP_ESC_COPY(*pointer, attr->value, temp_holder, temp_counter); } } } MI_HTTP_COPY(*pointer,MI_HTTP_BREAK); return 0; error: LM_ERR("buffer 2 small: *pointer=[%p] buf=[%p] max_page_len=[%d]\n", *pointer, buf, max_page_len); return -1; } static int mi_http_recur_flush_tree(char** pointer, char *buf, int max_page_len, struct mi_node *tree, int level) { struct mi_node *kid, *tmp; int ret; for(kid = tree->kids ; kid ; ){ if (!(kid->flags & MI_WRITTEN)) { if (mi_http_write_node(pointer, buf, max_page_len, kid, level)!=0) return -1; kid->flags |= MI_WRITTEN; } if ((ret = mi_http_recur_flush_tree(pointer, buf, max_page_len, tree->kids, level+1))<0){ return -1; } else if (ret > 0) { return ret; } if (!(kid->flags & MI_NOT_COMPLETED)){ tmp = kid; kid = kid->next; tree->kids = kid; if(!tmp->kids){ /* this node does not have any kids */ free_mi_node(tmp); } } else { /* the node will have more kids => * to keep the tree shape, * do not flush any other node for now */ return 1; } } return 0; } static int mi_http_recur_write_tree(char** pointer, char *buf, int max_page_len, struct mi_node *tree, int level) { for( ; tree ; tree=tree->next ) { if (!(tree->flags & MI_WRITTEN)) { if (mi_http_write_node(pointer, buf, max_page_len, tree, level)!=0){ return -1; } } if (tree->kids) { if (mi_http_recur_write_tree(pointer, buf, max_page_len, tree->kids, level+1)<0){ return -1; } } } return 0; } int mi_http_build_header(str *page, int max_page_len, int mod, int cmd, struct mi_root *tree, int flush) { int i, j; char *p, *buf; str code; if (page->s == NULL) { LM_ERR("Please provide a valid page\n"); return -1; } p = buf = page->s; MI_HTTP_COPY_3(p,MI_HTTP_Response_Head_1, MI_HTTP_Response_Head_2, MI_HTTP_Response_Title_Table_1); if ((int)((p)-buf)+SERVER_HDR_LEN-8>max_page_len) goto error; memcpy(p, SERVER_HDR+8, SERVER_HDR_LEN-8); p += SERVER_HDR_LEN-8; MI_HTTP_COPY_3(p,MI_HTTP_Response_Title_Table_2, upSinceCTime, MI_HTTP_Response_Title_Table_3); /* Building module menu */ MI_HTTP_COPY(p,MI_HTTP_Response_Menu_Table_1); for(i=0;inode.flags & MI_WRITTEN)) { code.s = int2str((unsigned long)tree->code, &code.len); MI_HTTP_COPY_11(p,code, MI_HTTP_SEMICOLON, tree->reason, MI_HTTP_Response_Menu_Cmd_td_4d, MI_HTTP_Response_Menu_Cmd_tr_2, MI_HTTP_Response_Menu_Cmd_tr_1, MI_HTTP_Response_Menu_Cmd_td_1d, MI_HTTP_NBSP, MI_HTTP_Response_Menu_Cmd_td_4d, MI_HTTP_Response_Menu_Cmd_td_1d, MI_HTTP_CODE_1); tree->node.flags |= MI_WRITTEN; } if (flush) { if (mi_http_recur_flush_tree(&p, buf, max_page_len, &tree->node, 0)<0) return -1; } else { if (mi_http_recur_write_tree(&p, buf, max_page_len, tree->node.kids, 0)<0) return -1; } } else if (mod>=0) { /* Building command menu */ /* Build the list of comands for the selected module */ MI_HTTP_COPY_4(p,MI_HTTP_Response_Menu_Cmd_Table_1, MI_HTTP_Response_Menu_Cmd_tr_1, MI_HTTP_Response_Menu_Cmd_td_1a, MI_HTTP_SLASH); if (http_root.len) { MI_HTTP_COPY_2(p,http_root,MI_HTTP_SLASH); } MI_HTTP_COPY_6(p,http_mi_cmds[mod].cmds[0].module, MI_HTTP_SLASH, http_mi_cmds[mod].cmds[0].name, MI_HTTP_Response_Menu_Cmd_td_3a, http_mi_cmds[mod].cmds[0].name, MI_HTTP_Response_Menu_Cmd_td_4a); if (cmd>=0) { MI_HTTP_COPY_3(p,MI_HTTP_Response_Menu_Cmd_td_1b, http_mi_cmds[mod].cmds[cmd].name, MI_HTTP_Response_Menu_Cmd_td_4b); } MI_HTTP_COPY(p,MI_HTTP_Response_Menu_Cmd_tr_2); for(j=1;j=0){ if (j==1) { MI_HTTP_COPY_7(p, MI_HTTP_Response_Menu_Cmd_td_1c, MI_HTTP_CMD_ROWSPAN, MI_HTTP_Response_Menu_Cmd_td_3c, MI_HTTP_Post_1, MI_HTTP_METHOD[http_method], MI_HTTP_Post_2, MI_HTTP_Response_Menu_Cmd_td_4c); } else if (j>MI_HTTP_ROWSPAN) { MI_HTTP_COPY_3(p, MI_HTTP_Response_Menu_Cmd_td_1d, MI_HTTP_NBSP, MI_HTTP_Response_Menu_Cmd_td_4d); } } MI_HTTP_COPY(p,MI_HTTP_Response_Menu_Cmd_tr_2); } if (cmd>=0){ if (j==1) { MI_HTTP_COPY_12(p,MI_HTTP_Response_Menu_Cmd_tr_1, MI_HTTP_Response_Menu_Cmd_td_1d, MI_HTTP_NBSP, MI_HTTP_Response_Menu_Cmd_td_4d, MI_HTTP_Response_Menu_Cmd_td_1c, MI_HTTP_CMD_ROWSPAN, MI_HTTP_Response_Menu_Cmd_td_3c, MI_HTTP_Post_1, MI_HTTP_METHOD[http_method], MI_HTTP_Post_2, MI_HTTP_Response_Menu_Cmd_td_4c, MI_HTTP_Response_Menu_Cmd_tr_2); j++; } for(;j<=MI_HTTP_ROWSPAN;j++) { MI_HTTP_COPY_5(p,MI_HTTP_Response_Menu_Cmd_tr_1, MI_HTTP_Response_Menu_Cmd_td_1d, MI_HTTP_NBSP, MI_HTTP_Response_Menu_Cmd_td_4d, MI_HTTP_Response_Menu_Cmd_tr_2); } } MI_HTTP_COPY_2(p,MI_HTTP_Response_Menu_Cmd_Table_2, MI_HTTP_Response_Foot); } else { MI_HTTP_COPY(p,MI_HTTP_Response_Foot); } page->len = p - page->s; return 0; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } int mi_http_build_content(str *page, int max_page_len, int mod, int cmd, struct mi_root* tree) { char *p, *buf; if (page->len==0) { if (0!=mi_http_build_header(page, max_page_len, mod, cmd, tree, 0)) return -1; } else { buf = page->s; p = page->s + page->len; if (tree) { /* Build mi reply */ if (mi_http_recur_write_tree(&p, buf, max_page_len, tree->node.kids, 0)<0) return -1; page->len = p - page->s; } } return 0; } int mi_http_build_page(str *page, int max_page_len, int mod, int cmd, struct mi_root *tree) { char *p, *buf; if (0!=mi_http_build_content(page, max_page_len, mod, cmd, tree)) return -1; buf = page->s; p = page->s + page->len; if (tree) { /* Build foot reply */ MI_HTTP_COPY_5(p,MI_HTTP_CODE_2, MI_HTTP_Response_Menu_Cmd_td_4d, MI_HTTP_Response_Menu_Cmd_tr_2, MI_HTTP_Response_Menu_Cmd_Table_2, MI_HTTP_Response_Foot); page->len = p - page->s; } return 0; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } int mi_http_flush_content(str *page, int max_page_len, int mod, int cmd, struct mi_root* tree) { char *p, *buf; if (page->len==0) if (0!=mi_http_build_header(page, max_page_len, mod, cmd, tree, 1)) return -1; buf = page->s; p = page->s + page->len; if (tree) { /* Build mi reply */ if (mi_http_recur_flush_tree(&p, buf, max_page_len, &tree->node, 0)<0) return -1; page->len = p - page->s; } return 0; } opensips-2.2.2/modules/mi_http/http_fnc.h000066400000000000000000000033301300170765700204540ustar00rootroot00000000000000/* * Copyright (C) 2011 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2011-09-20 first version (osas) */ #ifndef _MI_HTTP_HTTP_FNC_H #define _MI_HTTP_HTTP_FNC_H #define MI_HTTP_ASYNC_FAILED ((void*)-2) #define MI_HTTP_ASYNC_EXPIRED ((void*)-3) typedef struct http_mi_cmd_ { struct mi_cmd* cmds; int size; }http_mi_cmd_t; typedef struct mi_http_html_page_data_ { str page; str buffer; int mod; int cmd; }mi_http_html_page_data_t; typedef struct mi_http_async_resp_data_ { int mod; int cmd; gen_lock_t* lock; struct mi_root* tree; }mi_http_async_resp_data_t; int mi_http_init_async_lock(void); void mi_http_destroy_async_lock(void); int mi_http_init_cmds(void); int mi_http_parse_url(const char* url, int* mod, int* cmd); struct mi_root* mi_http_run_mi_cmd(int mod, int cmd, const str* arg, str *page, str *buffer, struct mi_handler **async_hdl); int mi_http_build_page(str* page, int max_page_len, int mod, int cmd, struct mi_root* tree); #endif opensips-2.2.2/modules/mi_http/mi_http.c000066400000000000000000000211701300170765700203100ustar00rootroot00000000000000/* * Copyright (C) 2011-2013 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2011-09-20 first version (osas) */ #include #include "../../globals.h" #include "../../sr_module.h" #include "../../str.h" #include "../../ut.h" #include "../../resolve.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../httpd/httpd_load.h" #include "http_fnc.h" /* module functions */ static int mod_init(); static int destroy(void); int mi_http_answer_to_connection (void *cls, void *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls, str *buffer, str *page); static ssize_t mi_http_flush_data(void *cls, uint64_t pos, char *buf, size_t max); str http_root = str_init("mi"); int http_method = 0; httpd_api_t httpd_api; static const str MI_HTTP_U_ERROR = str_init("" "Internal server error!"); static const str MI_HTTP_U_URL = str_init("" "Unable to parse URL!"); static const str MI_HTTP_U_METHOD = str_init("" "Unexpected method (only GET is accepted)!"); /* module parameters */ static param_export_t mi_params[] = { {"mi_http_root", STR_PARAM, &http_root.s}, {"mi_http_method", INT_PARAM, &http_method}, {0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "httpd", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* module exports */ struct module_exports exports = { "mi_http", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ mi_params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported PV */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ NULL /* per-child init function */ }; void proc_init(void) { /* Build a cache of all mi commands */ if (0!=mi_http_init_cmds()) exit(-1); /* Build async lock */ if (mi_http_init_async_lock() != 0) exit(-1); return; } static int mod_init(void) { http_root.len = strlen(http_root.s); if (http_method<0 || http_method>1) { LM_ERR("mi_http_method can be between [0,1]\n"); return -1; } /* Load httpd api */ if(load_httpd_api(&httpd_api)<0) { LM_ERR("Failed to load httpd api\n"); return -1; } /* Load httpd hooks */ httpd_api.register_httpdcb(exports.name, &http_root, &mi_http_answer_to_connection, &mi_http_flush_data, &proc_init); return 0; } int destroy(void) { mi_http_destroy_async_lock(); return 0; } static ssize_t mi_http_flush_data(void *cls, uint64_t pos, char *buf, size_t max) { struct mi_handler *hdl = (struct mi_handler*)cls; gen_lock_t *lock; mi_http_async_resp_data_t *async_resp_data; str page = {NULL, 0}; if (hdl==NULL) { LM_ERR("Unexpected NULL mi handler!\n"); return -1; } LM_DBG("hdl=[%p], hdl->param=[%p], pos=[%d], buf=[%p], max=[%d]\n", hdl, hdl->param, (int)pos, buf, (int)max); if (pos){ LM_DBG("freeing hdl=[%p]: hdl->param=[%p], " " pos=[%d], buf=[%p], max=[%d]\n", hdl, hdl->param, (int)pos, buf, (int)max); shm_free(hdl); return -1; } async_resp_data = (mi_http_async_resp_data_t*)((char*)hdl+sizeof(struct mi_handler)); lock = async_resp_data->lock; lock_get(lock); if (hdl->param) { if (*(struct mi_root**)hdl->param) { page.s = buf; LM_DBG("tree=[%p]\n", *(struct mi_root**)hdl->param); if (mi_http_build_page(&page, max, async_resp_data->mod, async_resp_data->cmd, *(struct mi_root**)hdl->param)!=0){ LM_ERR("Unable to build response\n"); shm_free(*(void**)hdl->param); *(void**)hdl->param = NULL; lock_release(lock); memcpy(buf, MI_HTTP_U_ERROR.s, MI_HTTP_U_ERROR.len); return MI_HTTP_U_ERROR.len; } else { shm_free(*(void**)hdl->param); *(void**)hdl->param = NULL; lock_release(lock); return page.len; } } else { LM_DBG("data not ready yet\n"); lock_release(lock); return 0; } } else { lock_release(lock); LM_ERR("Invalid async reply\n"); memcpy(buf, MI_HTTP_U_ERROR.s, MI_HTTP_U_ERROR.len); return MI_HTTP_U_ERROR.len; } lock_release(lock); LM_CRIT("done?\n"); shm_free(hdl); return -1; } #define MI_HTTP_OK 200 #define MI_HTTP_NOT_ACCEPTABLE 406 #define MI_HTTP_INTERNAL_ERROR 500 #define MI_HTTP_MAX_WAIT 2*60*4 static inline struct mi_root* mi_http_wait_async_reply(struct mi_handler *hdl) { mi_http_async_resp_data_t *async_resp_data = (mi_http_async_resp_data_t*)(hdl+1); struct mi_root *mi_rpl; int i; int x; for( i=0 ; iparam) break; sleep_us(1000*500); } if (i==MI_HTTP_MAX_WAIT) { /* no more waiting ....*/ lock_get(async_resp_data->lock); if (hdl->param==NULL) { hdl->param = MI_HTTP_ASYNC_EXPIRED; x = 0; } else { x = 1; } lock_release(async_resp_data->lock); if (x==0) { LM_INFO("exiting before receiving reply\n"); return NULL; } } mi_rpl = (struct mi_root *)hdl->param; if (mi_rpl==MI_HTTP_ASYNC_FAILED) mi_rpl = NULL; /* free the async handler*/ shm_free(hdl); return mi_rpl; } int mi_http_answer_to_connection (void *cls, void *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls, str *buffer, str *page) { int mod = -1; int cmd = -1; str arg = {NULL, 0}; struct mi_root *tree = NULL; struct mi_handler *async_hdl; int ret_code = MI_HTTP_OK; int is_shm = 0; LM_DBG("START *** cls=%p, connection=%p, url=%s, method=%s, " "versio=%s, upload_data[%d]=%p, *con_cls=%p\n", cls, connection, url, method, version, (int)*upload_data_size, upload_data, *con_cls); if (strncmp(method, "GET", 3)==0 || strncmp(method, "POST", 4)==0) { if(0 == mi_http_parse_url(url, &mod, &cmd)) { httpd_api.lookup_arg(connection, "arg", *con_cls, &arg); if (mod>=0 && cmd>=0 && arg.s) { LM_DBG("arg [%p]->[%.*s]\n", arg.s, arg.len, arg.s); tree = mi_http_run_mi_cmd(mod, cmd, &arg, page, buffer, &async_hdl); if (tree == MI_ROOT_ASYNC_RPL) { LM_DBG("got an async reply\n"); tree = mi_http_wait_async_reply(async_hdl); async_hdl = NULL; is_shm = 1; } if (tree == NULL) { LM_ERR("no reply\n"); *page = MI_HTTP_U_ERROR; ret_code = MI_HTTP_INTERNAL_ERROR; } else { LM_DBG("building on page [%p:%d]\n", page->s, page->len); if(0!=mi_http_build_page(page,buffer->len,mod,cmd,tree)){ LM_ERR("unable to build response" "for cmd [%d] w/ args [%.*s]\n", cmd, arg.len, arg.s); *page = MI_HTTP_U_ERROR; ret_code = MI_HTTP_INTERNAL_ERROR; } else { ret_code = tree->code; } } } else { page->s = buffer->s; if(0 != mi_http_build_page(page, buffer->len, mod, cmd, tree)) { LM_ERR("unable to build response\n"); *page = MI_HTTP_U_ERROR; ret_code = MI_HTTP_INTERNAL_ERROR; } } if (tree) { is_shm?free_shm_mi_tree(tree):free_mi_tree(tree); tree = NULL; } } else { LM_ERR("unable to parse URL [%s]\n", url); *page = MI_HTTP_U_URL; ret_code = MI_HTTP_NOT_ACCEPTABLE; } } else { LM_ERR("unexpected method [%s]\n", method); *page = MI_HTTP_U_METHOD; ret_code = MI_HTTP_INTERNAL_ERROR; } return ret_code; } opensips-2.2.2/modules/mi_json/000077500000000000000000000000001300170765700164715ustar00rootroot00000000000000opensips-2.2.2/modules/mi_json/Makefile000066400000000000000000000002521300170765700201300ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=mi_json.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/mi_json/README000066400000000000000000000055121300170765700173540ustar00rootroot00000000000000mi_json Module Stephane Alnet Edited by Stephane Alnet Copyright © 2013 shimaore.net Revision History Revision $Rev$ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. External Libraries or Applications 1.2.2. OpenSIPS Modules 1.3. Exported Parameters 1.3.1. mi_json_root(string) 1.4. Exported Functions 1.5. Known issues 1.6. Examples List of Examples 1.1. Set mi_json_root parameter 1.2. JSON request 1.3. JSON request Chapter 1. Admin Guide 1.1. Overview This module implements a JSON server that handles GET requests and generates JSON responses. 1.2. Dependencies 1.2.1. External Libraries or Applications None 1.2.2. OpenSIPS Modules The following modules must be loaded before this module: * httpd module. 1.3. Exported Parameters 1.3.1. mi_json_root(string) Specifies the root path for JSON requests: http://[opensips_IP]:[opensips_httpd_port]/[mi_json_root] The default value is "json". Example 1.1. Set mi_json_root parameter ... modparam("mi_json", "mi_json_root", "opensips_mi_json") ... 1.4. Exported Functions No function exported to be used from configuration file. 1.5. Known issues Commands with large responses (like ul_dump) will fail if the configured size of the httpd buffer is to small (or if there isn't enough pkg memory configured). Future realeases of the httpd and mi_json modules will address this issue. 1.6. Examples This is an example showing the JSON format for the “get_statistics net: uri:†MI command. Notice how the paramaters are comma-separated then URI-encoded. Example 1.2. JSON request GET /json/get_statistics?params=net%3A%2Curi%3A HTTP/1.1 Accept: application/json Host: example.net HTTP/1.1 200 OK Content-Length: 49 Content-Type: application/json Date: Fri, 01 Nov 2013 12:00:00 GMT ["net:waiting_udp = 0", "net:waiting_tcp = 0", "uri:positive checks = 0" , "uri:negative_checks = 0"] Here is another example showing the JSON format for the “ps†MI command. Example 1.3. JSON request GET /json/ps HTTP/1.1 Accept: application/json Host: example.net HTTP/1.1 200 OK Content-Length: 428 Content-Type: application/json Date: Fri, 01 Nov 2013 12:00:00 GMT [{"name":"Process", "value":null, "attributes":{"ID": "0", "PID": "7400" , "Type": "stand-alone SIP receiver udp:127.0.0.1:5060"}}, {"name":"Proc ess", "value":null, "attributes":{"ID": "1", "PID": "7402", "Type": "HTT PD INADDR_ANY:8888"}}, {"name":"Process", "value":null, "attributes":{"I D": "2", "PID": "7403", "Type": "time_keeper"}}, {"name":"Process", "val ue":null, "attributes":{"ID": "3", "PID": "7404", "Type": "timer"}}] opensips-2.2.2/modules/mi_json/doc/000077500000000000000000000000001300170765700172365ustar00rootroot00000000000000opensips-2.2.2/modules/mi_json/doc/mi_json.xml000066400000000000000000000020471300170765700214210ustar00rootroot00000000000000 %docentities; ]> mi_json Module &osipsname; Stephane Alnet stephane@shimaore.net Stephane Alnet 2013 shimaore.net $Rev$ $Date$ &admin; &faq; opensips-2.2.2/modules/mi_json/doc/mi_json_admin.xml000066400000000000000000000061471300170765700225760ustar00rootroot00000000000000 &adminguide;
Overview This module implements a JSON server that handles GET requests and generates JSON responses.
Dependencies
External Libraries or Applications None
&osips; Modules The following modules must be loaded before this module: httpd module.
Exported Parameters
<varname>mi_json_root</varname>(string) Specifies the root path for JSON requests: http://[opensips_IP]:[opensips_httpd_port]/[mi_json_root] The default value is "json". Set <varname>mi_json_root</varname> parameter ... modparam("mi_json", "mi_json_root", "opensips_mi_json") ...
Exported Functions No function exported to be used from configuration file.
Known issues Commands with large responses (like ul_dump) will fail if the configured size of the httpd buffer is to small (or if there isn't enough pkg memory configured). Future realeases of the httpd and mi_json modules will address this issue.
Examples This is an example showing the JSON format for the get_statistics net: uri: MI command. Notice how the paramaters are comma-separated then URI-encoded. JSON request Here is another example showing the JSON format for the ps MI command. JSON request
opensips-2.2.2/modules/mi_json/http_fnc.c000066400000000000000000000427251300170765700204540ustar00rootroot00000000000000/* * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2013-10-31 first version (shimaore) */ #include "../../str.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "../../config.h" #include "../../globals.h" #include "../../locking.h" #include "http_fnc.h" extern str http_root; mi_json_page_data_t html_page_data; gen_lock_t* mi_json_lock; struct page_buf { char *current; char *buf; int max_page_len; short status; }; static inline void MI_JSON_COPY(struct page_buf* pb, const str s) { if ( pb->status ) { return; } if ( s.s == NULL || s.len == 0 ) { return; } if ( (int)(pb->current - pb->buf) + s.len > pb->max_page_len) { pb->status = -1; } else { memcpy(pb->current, s.s, s.len); pb->current += s.len; } } static const str MI_JSON_ESC = str_init("\\"); static inline void MI_JSON_ESC_COPY(struct page_buf* pb, const str s) { str temp_holder; int temp_counter; if( pb->status ) { return; } if( s.s == NULL || s.len == 0 ) { return; } temp_holder.s = s.s; temp_holder.len = 0; for(temp_counter=0;temp_counterpage, html_p_data->buffer.len, tree); return 0; } static void mi_json_close_async(struct mi_root *mi_rpl, struct mi_handler *hdl, int done) { struct mi_root *shm_rpl = NULL; gen_lock_t* lock; mi_json_async_resp_data_t *async_resp_data; int x; if (hdl==NULL) { LM_CRIT("null mi handler\n"); return; } LM_DBG("mi_root [%p], hdl [%p], hdl->param [%p] and done [%u]\n", mi_rpl, hdl, hdl->param, done); if (!done) { /* we do not pass provisional stuff (yet) */ if (mi_rpl) free_mi_tree( mi_rpl ); return; } async_resp_data = (mi_json_async_resp_data_t*)(hdl+1); lock = async_resp_data->lock; if (mi_rpl==NULL || (shm_rpl=clone_mi_tree( mi_rpl, 1))==NULL) { LM_WARN("Unable to process async reply [%p]\n", mi_rpl); /* mark it as invalid */ shm_rpl = MI_JSON_ASYNC_FAILED; } if (mi_rpl) free_mi_tree(mi_rpl); lock_get(lock); if (hdl->param==NULL) { hdl->param = shm_rpl; x = 0; } else { x = 1; } LM_DBG("shm_rpl [%p], hdl [%p], hdl->param [%p]\n", shm_rpl, hdl, hdl->param); lock_release(lock); if (x) { if (shm_rpl!=MI_JSON_ASYNC_FAILED) free_shm_mi_tree(shm_rpl); shm_free(hdl); } return; } static inline struct mi_handler* mi_json_build_async_handler(void) { struct mi_handler *hdl; mi_json_async_resp_data_t *async_resp_data; unsigned int len; len = sizeof(struct mi_handler)+sizeof(mi_json_async_resp_data_t); hdl = (struct mi_handler*)shm_malloc(len); if (hdl==NULL) { LM_ERR("oom\n"); return NULL; } memset(hdl, 0, len); async_resp_data = (mi_json_async_resp_data_t*)(hdl+1); hdl->handler_f = mi_json_close_async; hdl->param = NULL; async_resp_data->lock = mi_json_lock; LM_DBG("hdl [%p], hdl->param [%p], mi_json_lock=[%p]\n", hdl, hdl->param, async_resp_data->lock); return hdl; } struct mi_root* mi_json_run_mi_cmd(struct mi_cmd *f, const str* miCmd, const str* params, str *page, str *buffer, struct mi_handler **async_hdl) { struct mi_node *node; struct mi_root *mi_cmd = NULL; struct mi_root *mi_rpl = NULL; struct mi_handler *hdl = NULL; str val; int i, j; LM_DBG("got command=%.*s\n", miCmd->len, miCmd->s); if (f->flags&MI_ASYNC_RPL_FLAG) { LM_DBG("command=%.*s is async\n", miCmd->len, miCmd->s); /* We need to build an async handler */ hdl = mi_json_build_async_handler(); if (hdl==NULL) { LM_ERR("failed to build async handler\n"); goto error; } } else { hdl = NULL; } if (f->flags&MI_NO_INPUT_FLAG) { LM_DBG("command=%.*s requires no parameters\n", miCmd->len, miCmd->s); mi_cmd = NULL; } else { LM_DBG("command=%.*s accepts parameters\n", miCmd->len, miCmd->s); if (params->s) { mi_cmd = init_mi_tree(0,0,0); if (mi_cmd==NULL) { LM_ERR("the MI tree cannot be initialized!\n"); goto error; } i = 0; j = 0; for( i = 0; i < params->len; i++ ) { if (params->s[i] == ',') { val.s = params->s + j; val.len = i-j; LM_DBG("got string param [%.*s]\n", val.len, val.s); node = &mi_cmd->node; if(!add_mi_node_child(node,0,NULL,0,val.s,val.len)){ LM_ERR("cannot add the child node to the tree\n"); free_mi_tree(mi_cmd); goto error; } j = i+1; } } if( j < params->len ) { val.s = params->s + j; val.len = params->len-j; LM_DBG("got string param [%.*s]\n", val.len, val.s); node = &mi_cmd->node; if(!add_mi_node_child(node,0,NULL,0,val.s,val.len)){ LM_ERR("cannot add the child node to the tree\n"); free_mi_tree(mi_cmd); goto error; } } mi_cmd->async_hdl = hdl; } else { LM_DBG("but no parameters were found\n"); mi_cmd = init_mi_tree(0,0,0); if (mi_cmd==NULL) { LM_ERR("the MI tree cannot be initialized!\n"); goto error; } } } html_page_data.page.s = buffer->s; html_page_data.page.len = 0; html_page_data.buffer.s = buffer->s; html_page_data.buffer.len = buffer->len; /* FIXME: find a proper way for handling flushing */ mi_rpl = run_mi_cmd(f, mi_cmd, NULL, &html_page_data); if (mi_rpl == NULL) { LM_ERR("failed to process the command\n"); goto error; } else { *page = html_page_data.page; } LM_DBG("got mi_rpl=[%p]\n",mi_rpl); *async_hdl = hdl; if (mi_cmd) free_mi_tree(mi_cmd); return mi_rpl; error: if (mi_cmd) free_mi_tree(mi_cmd); if (hdl) shm_free(hdl); *async_hdl = NULL; return NULL; } static inline int mi_json_write_node_array(struct page_buf* pb, struct mi_node *node) { LM_DBG("start\n"); MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, node->value); MI_JSON_COPY(pb, MI_JSON_SQUOT); node->flags |= MI_WRITTEN; return pb->status; } static inline int mi_json_write_node_hash(struct page_buf* pb, struct mi_node *node) { LM_DBG("start\n"); MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, node->name); MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_COPY(pb, MI_JSON_COLON); if (node->value.s!=NULL) { MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, node->value); MI_JSON_COPY(pb, MI_JSON_SQUOT); } else { MI_JSON_COPY(pb, MI_JSON_NULL); } node->flags |= MI_WRITTEN; return pb->status; } static inline int mi_json_write_node(struct page_buf* pb, struct mi_node *node) { struct mi_attr *attr; LM_DBG("start\n"); /* name */ MI_JSON_COPY(pb, MI_JSON_KEY_NAME); if (node->name.s!=NULL) { MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, node->name); MI_JSON_COPY(pb, MI_JSON_SQUOT); } else { MI_JSON_COPY(pb, MI_JSON_NULL); } MI_JSON_COPY(pb, MI_JSON_COMMA); /* value */ MI_JSON_COPY(pb, MI_JSON_KEY_VALUE); if (node->value.s!=NULL) { MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, node->value); MI_JSON_COPY(pb, MI_JSON_SQUOT); } else { MI_JSON_COPY(pb, MI_JSON_NULL); } MI_JSON_COPY(pb, MI_JSON_COMMA); /* attributes */ MI_JSON_COPY(pb, MI_JSON_KEY_ATTRIBUTES); MI_JSON_COPY(pb, MI_JSON_OBJECT_START); for(attr=node->attributes;attr!=NULL;attr=attr->next) { if (attr->name.s!=NULL) { /* attribute name */ MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, attr->name); MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_COPY(pb, MI_JSON_COLON); /* attribute value */ if (attr->value.s!=NULL) { MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, attr->value); MI_JSON_COPY(pb, MI_JSON_SQUOT); } else { MI_JSON_COPY(pb, MI_JSON_NULL); } } if (attr->next!=NULL) { MI_JSON_COPY(pb, MI_JSON_COMMA); } } MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP); return pb->status; } /* sync case */ #if 0 static int mi_json_recur_write_tree(struct page_buf* pb, struct mi_node *tree, unsigned int flags) { int names = 0; int values = 0; int attributes = 0; int kids = 0; struct mi_node* t; LM_DBG("start\n"); for( t = tree; t ; t=t->next ) { if(t->name.s) { names++; } if(t->value.s) { values++; } if(t->attributes) { attributes++; } if(t->kids) { kids++; } } if(names == 0 && values > 0 && attributes == 0 && kids == 0) { LM_DBG("Treat as an array\n"); /* Treat as an array */ MI_JSON_COPY(pb, MI_JSON_ARRAY_START); for( t = tree; t; t=t->next ) { mi_json_write_node_array(pb,t); t->flags |= MI_WRITTEN; if(t->next) { MI_JSON_COPY(pb, MI_JSON_COMMA); } } MI_JSON_COPY(pb, MI_JSON_ARRAY_STOP); LM_DBG("done\n"); return 0; } if(names >= values && attributes == 0 && kids == 0) { LM_DBG("Treat as a hash\n"); /* Treat as a hash */ MI_JSON_COPY(pb, MI_JSON_OBJECT_START); for( t = tree; t; t=t->next ) { LM_DBG("t = %p\n",t); mi_json_write_node_hash(pb,t); t->flags |= MI_WRITTEN; if(t->next) { MI_JSON_COPY(pb, MI_JSON_COMMA); } } MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP); LM_DBG("done\n"); return 0; } if(names == 0 && values == 0 && attributes == 0 && kids > 0) { LM_DBG("Treat as an array of objects\n"); /* Treat as an array of objects */ MI_JSON_COPY(pb, MI_JSON_ARRAY_START); for( t = tree; t; t=t->next ) { mi_json_recur_write_tree(pb,t->kids, t->flags); t->flags |= MI_WRITTEN; if(t->next) { MI_JSON_COPY(pb, MI_JSON_COMMA); } } MI_JSON_COPY(pb, MI_JSON_ARRAY_STOP); LM_DBG("done\n"); return 0; } /* Otherwise */ LM_DBG("Treat as a complex array of hashes\n"); /* Treat as a complex array of hashes */ MI_JSON_COPY(pb, MI_JSON_ARRAY_START); for( t = tree; t; t=t->next ) { MI_JSON_COPY(pb, MI_JSON_OBJECT_START); mi_json_write_node(pb,t); if (t->kids) { MI_JSON_COPY(pb, MI_JSON_COMMA); MI_JSON_COPY(pb, MI_JSON_KEY_CHILDREN); mi_json_recur_write_tree(pb, t->kids, t->flags); } MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP); if(t->next) { MI_JSON_COPY(pb, MI_JSON_COMMA); } } MI_JSON_COPY(pb, MI_JSON_ARRAY_STOP); LM_DBG("done\n"); return pb->status; } #endif static void mi_json_recur_write_node(struct page_buf* pb, struct mi_node *node, int dump_name) { struct mi_attr *attr; int first = 1; /* if we only have name and value, then dump it like hash */ if (dump_name && node->name.s && node->value.s && !node->attributes && !node->kids) { mi_json_write_node_hash(pb, node); return; } if (dump_name && node->name.s) { MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, node->name); MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_COPY(pb, MI_JSON_COLON); MI_JSON_COPY(pb, MI_JSON_OBJECT_START); } /* value */ if (node->value.s) { MI_JSON_COPY(pb, MI_JSON_KEY_VALUE); MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, node->value); MI_JSON_COPY(pb, MI_JSON_SQUOT); first = 0; } /* attributes */ if (node->attributes) { if (!first) MI_JSON_COPY(pb, MI_JSON_COMMA); MI_JSON_COPY(pb, MI_JSON_KEY_ATTRIBUTES); MI_JSON_COPY(pb, MI_JSON_OBJECT_START); for(attr=node->attributes;attr!=NULL;attr=attr->next) { if (attr->name.s!=NULL) { /* attribute name */ MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, attr->name); MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_COPY(pb, MI_JSON_COLON); /* attribute value */ if (attr->value.s!=NULL) { MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_ESC_COPY(pb, attr->value); MI_JSON_COPY(pb, MI_JSON_SQUOT); } else { MI_JSON_COPY(pb, MI_JSON_NULL); } } if (attr->next!=NULL) { MI_JSON_COPY(pb, MI_JSON_COMMA); } } MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP); first = 0; } /* kids */ if (node->kids) { if (!first) MI_JSON_COPY(pb, MI_JSON_COMMA); MI_JSON_COPY(pb, MI_JSON_KEY_CHILDREN); mi_json_recur_write_tree(pb, node->kids, node->flags); } if (dump_name && node->name.s) { MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP); } } static int mi_json_recur_write_tree(struct page_buf* pb, struct mi_node *tree, unsigned int flags) { struct mi_node* t; if (!tree) return pb->status; if (flags & MI_IS_ARRAY) { LM_DBG("Treat as an array\n"); MI_JSON_COPY(pb, MI_JSON_OBJECT_START); MI_JSON_COPY(pb, MI_JSON_SQUOT); if (tree->name.s) { MI_JSON_ESC_COPY(pb, tree->name); } MI_JSON_COPY(pb, MI_JSON_SQUOT); MI_JSON_COPY(pb, MI_JSON_COLON); MI_JSON_COPY(pb, MI_JSON_ARRAY_START); for( t = tree; t; t=t->next ) { MI_JSON_COPY(pb, MI_JSON_OBJECT_START); mi_json_recur_write_node(pb,t,0); MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP); t->flags |= MI_WRITTEN; if(t->next) { MI_JSON_COPY(pb, MI_JSON_COMMA); } } MI_JSON_COPY(pb, MI_JSON_ARRAY_STOP); MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP); } else { LM_DBG("Treat as a hash\n"); MI_JSON_COPY(pb, MI_JSON_OBJECT_START); for( t = tree; t; t=t->next ) { mi_json_recur_write_node(pb,t,1); t->flags |= MI_WRITTEN; if(t->next) { MI_JSON_COPY(pb, MI_JSON_COMMA); } } MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP); } LM_DBG("done\n"); return pb->status; } int mi_json_build_content(str *page, int max_page_len, struct mi_root* tree) { struct page_buf pb; LM_DBG("start\n"); pb.buf = page->s; pb.current = page->s + page->len; pb.max_page_len = max_page_len; pb.status = 0; if (tree) { /* Build mi reply */ mi_json_recur_write_tree(&pb, tree->node.kids, tree->node.flags); page->len = pb.current - page->s; } LM_DBG("done\n"); return pb.status; } int mi_json_build_page(str *page, int max_page_len, struct mi_root *tree) { LM_DBG("start\n"); return mi_json_build_content(page, max_page_len, tree); } /* async case */ static int mi_json_recur_flush_tree(struct page_buf* pb, struct mi_node *tree) { struct mi_node *kid; LM_DBG("start\n"); for(kid = tree->kids ; kid ; ){ if (kid->flags & MI_NOT_COMPLETED) { return 1; } } mi_json_recur_write_tree(pb,tree,0); LM_DBG("done\n"); return pb->status; } int mi_json_flush_content(str *page, int max_page_len, struct mi_root* tree) { struct page_buf pb; LM_DBG("start\n"); pb.buf = page->s; pb.current = page->s + page->len; pb.max_page_len = max_page_len; pb.status = 0; if (tree) { /* Build mi reply */ mi_json_recur_flush_tree(&pb, &tree->node); page->len = pb.current - page->s; } LM_DBG("done\n"); return pb.status; } opensips-2.2.2/modules/mi_json/http_fnc.h000066400000000000000000000027151300170765700204540ustar00rootroot00000000000000/* * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2013-03-04 first version (osas) */ #ifndef _MI_JSON_HTTP_FNC_H #define _MI_JSON_HTTP_FNC_H #define MI_JSON_ASYNC_FAILED ((void*)-2) #define MI_JSON_ASYNC_EXPIRED ((void*)-3) typedef struct mi_json_html_page_data_ { str page; str buffer; }mi_json_page_data_t; typedef struct mi_json_async_resp_data_ { gen_lock_t* lock; }mi_json_async_resp_data_t; int mi_json_init_async_lock(void); void mi_json_destroy_async_lock(void); struct mi_root* mi_json_run_mi_cmd(struct mi_cmd *f, const str* command, const str* params, str *page, str *buffer, struct mi_handler **async_hdl); int mi_json_build_page(str* page, int max_page_len, struct mi_root* tree); #endif opensips-2.2.2/modules/mi_json/mi_json.c000066400000000000000000000213021300170765700202710ustar00rootroot00000000000000/* * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2013-03-04 first version (osas) */ #include #include "../../globals.h" #include "../../sr_module.h" #include "../../str.h" #include "../../ut.h" #include "../../resolve.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../httpd/httpd_load.h" #include "http_fnc.h" #define MI_JSON_ERROR_BUF_MAX_LEN 512 /* module functions */ static int mod_init(); static int destroy(void); int mi_json_answer_to_connection (void *cls, void *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls, str *buffer, str *page); static ssize_t mi_json_flush_data(void *cls, uint64_t pos, char *buf, size_t max); str http_root = str_init("json"); httpd_api_t httpd_api; static const str MI_HTTP_U_ERROR = str_init("Internal server error"); static const str MI_HTTP_U_METHOD = str_init("Unexpected method"); static const str MI_HTTP_U_NOT_FOUND = str_init("Command not found"); static char err_buf[MI_JSON_ERROR_BUF_MAX_LEN]; static const char* MI_JSON_COMMAND_ERROR_S = "{\"error\": {\"code\": %u, \"message\": \"%.*s\"}}"; /* module parameters */ static param_export_t mi_params[] = { {"mi_json_root", STR_PARAM, &http_root.s}, {0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "httpd", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* module exports */ struct module_exports exports = { "mi_json", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ NULL, /* exported functions */ NULL, /* exported async functions */ mi_params, /* exported parameters */ NULL, /* exported statistics */ NULL, /* exported MI functions */ NULL, /* exported PV */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ NULL /* per-child init function */ }; void proc_init(void) { /* Build async lock */ if (mi_json_init_async_lock() != 0) exit(-1); return; } static int mod_init(void) { http_root.len = strlen(http_root.s); /* Load httpd api */ if(load_httpd_api(&httpd_api)<0) { LM_ERR("Failed to load httpd api\n"); return -1; } /* Load httpd hooks */ httpd_api.register_httpdcb(exports.name, &http_root, &mi_json_answer_to_connection, &mi_json_flush_data, &proc_init); return 0; } int destroy(void) { mi_json_destroy_async_lock(); return 0; } static ssize_t mi_json_flush_data(void *cls, uint64_t pos, char *buf, size_t max) { struct mi_handler *hdl = (struct mi_handler*)cls; gen_lock_t *lock; mi_json_async_resp_data_t *async_resp_data; str page = {NULL, 0}; if (hdl==NULL) { LM_ERR("Unexpected NULL mi handler!\n"); return -1; } LM_DBG("hdl=[%p], hdl->param=[%p], pos=[%d], buf=[%p], max=[%d]\n", hdl, hdl->param, (int)pos, buf, (int)max); if (pos){ LM_DBG("freeing hdl=[%p]: hdl->param=[%p], " " pos=[%d], buf=[%p], max=[%d]\n", hdl, hdl->param, (int)pos, buf, (int)max); shm_free(hdl); return -1; } async_resp_data = (mi_json_async_resp_data_t*)((char*)hdl+sizeof(struct mi_handler)); lock = async_resp_data->lock; lock_get(lock); if (hdl->param) { if (*(struct mi_root**)hdl->param) { page.s = buf; LM_DBG("tree=[%p]\n", *(struct mi_root**)hdl->param); if (mi_json_build_page(&page, max, *(struct mi_root**)hdl->param)!=0){ LM_ERR("Unable to build response\n"); shm_free(*(void**)hdl->param); *(void**)hdl->param = NULL; lock_release(lock); memcpy(buf, MI_HTTP_U_ERROR.s, MI_HTTP_U_ERROR.len); return MI_HTTP_U_ERROR.len; } else { shm_free(*(void**)hdl->param); *(void**)hdl->param = NULL; lock_release(lock); return page.len; } } else { LM_DBG("data not ready yet\n"); lock_release(lock); return 0; } } else { lock_release(lock); LM_ERR("Invalid async reply\n"); memcpy(buf, MI_HTTP_U_ERROR.s, MI_HTTP_U_ERROR.len); return MI_HTTP_U_ERROR.len; } lock_release(lock); LM_CRIT("done?\n"); shm_free(hdl); return -1; } #define MI_JSON_MAX_WAIT 2*60*4 static inline struct mi_root* mi_json_wait_async_reply(struct mi_handler *hdl) { mi_json_async_resp_data_t *async_resp_data = (mi_json_async_resp_data_t*)(hdl+1); struct mi_root *mi_rpl; int i; int x; for( i=0 ; iparam) break; sleep_us(1000*500); } if (i==MI_JSON_MAX_WAIT) { /* no more waiting ....*/ lock_get(async_resp_data->lock); if (hdl->param==NULL) { hdl->param = MI_JSON_ASYNC_EXPIRED; x = 0; } else { x = 1; } lock_release(async_resp_data->lock); if (x==0) { LM_INFO("exiting before receiving reply\n"); return NULL; } } mi_rpl = (struct mi_root *)hdl->param; if (mi_rpl==MI_JSON_ASYNC_FAILED) mi_rpl = NULL; /* free the async handler*/ shm_free(hdl); return mi_rpl; } #define MI_JSON_OK 200 #define MI_JSON_NOT_ACCEPTABLE 406 #define MI_JSON_INTERNAL_ERROR 500 int mi_json_answer_to_connection (void *cls, void *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls, str *buffer, str *page) { str command = {NULL, 0}; str params = {NULL, 0}; struct mi_cmd *f = NULL; struct mi_root *tree = NULL; struct mi_handler *async_hdl; int ret_code = MI_JSON_OK; int is_shm = 0; page->s = err_buf; LM_DBG("START *** cls=%p, connection=%p, url=%s, method=%s, " "versio=%s, upload_data[%d]=%p, *con_cls=%p\n", cls, connection, url, method, version, (int)*upload_data_size, upload_data, *con_cls); if (strncmp(method, "GET", 3)==0) { if(url && url[0] == '/' && url[1] != '\0') { command.s = (char*)url+1; command.len = strlen(command.s); } httpd_api.lookup_arg(connection, "params", *con_cls, ¶ms); if (command.s) { f = lookup_mi_cmd(command.s, command.len); if (f == NULL) { LM_ERR("unable to find mi command [%.*s]\n", command.len, command.s); page->len = snprintf(page->s, MI_JSON_ERROR_BUF_MAX_LEN, MI_JSON_COMMAND_ERROR_S, MI_JSON_INTERNAL_ERROR, MI_HTTP_U_NOT_FOUND.len, MI_HTTP_U_NOT_FOUND.s); } else { tree = mi_json_run_mi_cmd(f, &command,¶ms, page, buffer, &async_hdl); if (tree == MI_ROOT_ASYNC_RPL) { LM_DBG("got an async reply\n"); tree = mi_json_wait_async_reply(async_hdl); async_hdl = NULL; is_shm = 1; } if (tree == NULL) { LM_ERR("no reply\n"); page->len = snprintf(page->s, MI_JSON_ERROR_BUF_MAX_LEN, MI_JSON_COMMAND_ERROR_S, MI_JSON_INTERNAL_ERROR, MI_HTTP_U_ERROR.len, MI_HTTP_U_ERROR.s); } else { LM_DBG("building on page [%p:%d]\n", page->s, page->len); if(0!=mi_json_build_page(page, buffer->len, tree)){ LM_ERR("unable to build response\n"); page->len = snprintf(page->s, MI_JSON_ERROR_BUF_MAX_LEN, MI_JSON_COMMAND_ERROR_S, MI_JSON_INTERNAL_ERROR, MI_HTTP_U_ERROR.len, MI_HTTP_U_ERROR.s); } else { if (tree->code >= 400) { page->s = err_buf; page->len = snprintf(page->s, MI_JSON_ERROR_BUF_MAX_LEN, MI_JSON_COMMAND_ERROR_S, tree->code, tree->reason.len, tree->reason.s); } } } } } else { LM_ERR("unable to build response for empty request\n"); page->len = snprintf(page->s, MI_JSON_ERROR_BUF_MAX_LEN, MI_JSON_COMMAND_ERROR_S, MI_JSON_INTERNAL_ERROR, MI_HTTP_U_ERROR.len, MI_HTTP_U_ERROR.s); } if (tree) { is_shm?free_shm_mi_tree(tree):free_mi_tree(tree); tree = NULL; } } else { LM_ERR("unexpected method [%s]\n", method); page->len = snprintf(page->s, MI_JSON_ERROR_BUF_MAX_LEN, MI_JSON_COMMAND_ERROR_S, MI_JSON_NOT_ACCEPTABLE, MI_HTTP_U_METHOD.len, MI_HTTP_U_METHOD.s); } return ret_code; } opensips-2.2.2/modules/mi_xmlrpc_ng/000077500000000000000000000000001300170765700175115ustar00rootroot00000000000000opensips-2.2.2/modules/mi_xmlrpc_ng/Makefile000066400000000000000000000010051300170765700211450ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=mi_xmlrpc_ng.so LIBS= ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/mi_xmlrpc_ng/README000066400000000000000000000073441300170765700204010ustar00rootroot00000000000000mi_xmlrpc_ng Module Ovidiu Sas Edited by Ovidiu Sas Copyright © 2013 VoIP Embedded, Inc. Revision History Revision $Rev$ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. External Libraries or Applications 1.2.2. OpenSIPS Modules 1.3. Exported Parameters 1.3.1. http_root(string) 1.3.2. format_version(integer) 1.4. Exported Functions 1.5. Known issues 1.6. Example List of Examples 1.1. Set http_root parameter 1.2. Set format_version parameter 1.3. XMLRPC request Chapter 1. Admin Guide 1.1. Overview This module implements a xmlrpc server that handles xmlrpc requests and generates xmlrpc responses. When a xmlrpc message is received a default method is executed. At first, it looks up the MI command. If found it parses the called procedure's parameters into a MI tree and the command is executed. A MI reply tree is returned that is formatted back in xmlrpc. The response is built in two ways - like a string that contains the MI tree nodes information (name, values and attributes) or like an array whose elements are consisted of each MI tree node stored information. 1.2. Dependencies 1.2.1. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml2 1.2.2. OpenSIPS Modules The following modules must be loaded before this module: * httpd module. 1.3. Exported Parameters 1.3.1. http_root(string) Specifies the root path for xmlrpc requests: http://[opensips_IP]:[opensips_httpd_port]/[http_root] The default value is "RPC2". Example 1.1. Set http_root parameter ... modparam("mi_xmlrpc_ng", "http_root", "opensips_mi_xmlrpc") ... 1.3.2. format_version(integer) Specifies which formatting to be used for the output generated by the module. The value of 2 will generate a response broken in many tags which doesn't require string parsing, only xml parsing, this is the newer version. The other accepted value is 1 which will generate a long simple sting response, this is the older vesion. The default value is 2. Example 1.2. Set format_version parameter ... modparam("mi_xmlrpc_ng", "format_version", 1) ... 1.4. Exported Functions No function exported to be used from configuration file. 1.5. Known issues Commands with large responses (like ul_dump) will fail if the configured size of the httpd buffer is to small (or if there isn't enough pkg memory configured). Future realeases of the httpd and mi_xmlrpc_ng modules will address this issue. 1.6. Example This is an example showing the xmlrpc format for the “get_statistics net: uri:†MI commad: response. Example 1.3. XMLRPC request POST /xmlrpc HTTP/1.0 Host: my.host.com User-Agent: My xmlrpc UA Content-Type: text/xml Content-Length: 216 get_statistics net: uri: HTTP/1.0 200 OK Content-Length: 236 Content-Type: text/xml; charset=utf-8 Date: Mon, 8 Mar 2013 12:00:00 GMT :: net:waiting_udp = 0 :: net:waiting_tcp = 0 :: uri:positive checks = 0 :: uri:negative_checks = 0 opensips-2.2.2/modules/mi_xmlrpc_ng/doc/000077500000000000000000000000001300170765700202565ustar00rootroot00000000000000opensips-2.2.2/modules/mi_xmlrpc_ng/doc/mi_xmlrpc_ng.xml000066400000000000000000000020711300170765700234560ustar00rootroot00000000000000 %docentities; ]> mi_xmlrpc_ng Module &osipsname; Ovidiu Sas osas@voipembedded.com Ovidiu Sas 2013 VoIP Embedded, Inc. $Rev$ $Date$ &admin; &faq; opensips-2.2.2/modules/mi_xmlrpc_ng/doc/mi_xmlrpc_ng_admin.xml000066400000000000000000000100041300170765700246210ustar00rootroot00000000000000 &adminguide;
Overview This module implements a xmlrpc server that handles xmlrpc requests and generates xmlrpc responses. When a xmlrpc message is received a default method is executed. At first, it looks up the MI command. If found it parses the called procedure's parameters into a MI tree and the command is executed. A MI reply tree is returned that is formatted back in xmlrpc. The response is built in two ways - like a string that contains the MI tree nodes information (name, values and attributes) or like an array whose elements are consisted of each MI tree node stored information.
Dependencies
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml2
&osips; Modules The following modules must be loaded before this module: httpd module.
Exported Parameters
<varname>http_root</varname>(string) Specifies the root path for xmlrpc requests: http://[opensips_IP]:[opensips_httpd_port]/[http_root] The default value is "RPC2". Set <varname>http_root</varname> parameter ... modparam("mi_xmlrpc_ng", "http_root", "opensips_mi_xmlrpc") ...
<varname>format_version</varname>(integer) Specifies which formatting to be used for the output generated by the module. The value of 2 will generate a response broken in many tags which doesn't require string parsing, only xml parsing, this is the newer version. The other accepted value is 1 which will generate a long simple sting response, this is the older vesion. The default value is 2. Set <varname>format_version</varname> parameter ... modparam("mi_xmlrpc_ng", "format_version", 1) ...
Exported Functions No function exported to be used from configuration file.
Known issues Commands with large responses (like ul_dump) will fail if the configured size of the httpd buffer is to small (or if there isn't enough pkg memory configured). Future realeases of the httpd and mi_xmlrpc_ng modules will address this issue.
Example This is an example showing the xmlrpc format for the get_statistics net: uri: MI commad: response. XMLRPC request get_statistics net: uri: HTTP/1.0 200 OK Content-Length: 236 Content-Type: text/xml; charset=utf-8 Date: Mon, 8 Mar 2013 12:00:00 GMT :: net:waiting_udp = 0 :: net:waiting_tcp = 0 :: uri:positive checks = 0 :: uri:negative_checks = 0 ]]>
opensips-2.2.2/modules/mi_xmlrpc_ng/http_fnc.c000066400000000000000000000777251300170765700215040ustar00rootroot00000000000000/* * Copyright (C) 2013 VoIP Embedded, Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2013-03-04 first version (osas) */ #include #include "../../str.h" #include "../../ut.h" #include "../../strcommon.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "../../config.h" #include "../../globals.h" #include "../../locking.h" #include "../../strcommon.h" #include "http_fnc.h" #define MI_XMLRPC_HTTP_XML_METHOD_CALL_NODE "methodCall" #define MI_XMLRPC_HTTP_XML_METHOD_NAME_NODE "methodName" #define MI_XMLRPC_HTTP_XML_PARAMS_NODE "params" #define MI_XMLRPC_HTTP_XML_PARAM_NODE "param" #define MI_XMLRPC_HTTP_XML_VALUE_NODE "value" #define MI_XMLRPC_HTTP_XML_STRING_NODE "string" extern str http_root; extern int version; mi_xmlrpc_http_page_data_t html_page_data; gen_lock_t* mi_xmlrpc_http_lock; #define MI_XMLRPC_HTTP_COPY(p,str) \ do{ \ if ((int)((p)-buf)+(str).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str).s, (str).len); (p) += (str).len; \ }while(0) #define MI_XMLRPC_HTTP_COPY_2(p,str1,str2) \ do{ \ if ((int)((p)-buf)+(str1).len+(str2).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str1).s, (str1).len); (p) += (str1).len; \ memcpy((p), (str2).s, (str2).len); (p) += (str2).len; \ }while(0) #define MI_XMLRPC_HTTP_COPY_3(p,str1,str2,str3) \ do{ \ if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str1).s, (str1).len); (p) += (str1).len; \ memcpy((p), (str2).s, (str2).len); (p) += (str2).len; \ memcpy((p), (str3).s, (str3).len); (p) += (str3).len; \ }while(0) #define MI_XMLRPC_HTTP_COPY_4(p,str1,str2,str3,str4) \ do{ \ if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len+(str4).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str1).s, (str1).len); (p) += (str1).len; \ memcpy((p), (str2).s, (str2).len); (p) += (str2).len; \ memcpy((p), (str3).s, (str3).len); (p) += (str3).len; \ memcpy((p), (str4).s, (str4).len); (p) += (str4).len; \ }while(0) #define MI_XMLRPC_HTTP_COPY_5(p,s1,s2,s3,s4,s5) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ }while(0) #define MI_XMLRPC_HTTP_COPY_6(p,s1,s2,s3,s4,s5,s6) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ }while(0) #define MI_XMLRPC_HTTP_COPY_7(p,s1,s2,s3,s4,s5,s6,s7) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ }while(0) #define MI_XMLRPC_HTTP_COPY_10(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \ memcpy((p), (s10).s, (s10).len); (p) += (s10).len; \ }while(0) #define MI_XMLRPC_HTTP_COPY_11(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len+(s11).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \ memcpy((p), (s10).s, (s10).len); (p) += (s10).len; \ memcpy((p), (s11).s, (s11).len); (p) += (s11).len; \ }while(0) #define MI_XMLRPC_HTTP_COPY_12(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len+(s11).len+(s12).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \ memcpy((p), (s10).s, (s10).len); (p) += (s10).len; \ memcpy((p), (s11).s, (s11).len); (p) += (s11).len; \ memcpy((p), (s12).s, (s12).len); (p) += (s12).len; \ }while(0) #define MI_XMLRPC_HTTP_ESC_COPY(p,str,temp_holder,temp_counter) \ do{ \ (temp_holder).s = (str).s; \ (temp_holder).len = 0; \ for((temp_counter)=0;(temp_counter)<(str).len;(temp_counter)++) { \ switch((str).s[(temp_counter)]) { \ case '<': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_XMLRPC_HTTP_COPY_2(p, (temp_holder), MI_XMLRPC_HTTP_ESC_LT); \ (temp_holder).s += (temp_holder).len + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '>': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_XMLRPC_HTTP_COPY_2(p, (temp_holder), MI_XMLRPC_HTTP_ESC_GT); \ (temp_holder).s += (temp_holder).len + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '&': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_XMLRPC_HTTP_COPY_2(p, (temp_holder), MI_XMLRPC_HTTP_ESC_AMP); \ (temp_holder).s += (temp_holder).len + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '"': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_XMLRPC_HTTP_COPY_2(p, (temp_holder), MI_XMLRPC_HTTP_ESC_QUOT); \ (temp_holder).s += (temp_holder).len + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '\'': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_XMLRPC_HTTP_COPY_2(p, (temp_holder), MI_XMLRPC_HTTP_ESC_SQUOT); \ (temp_holder).s += (temp_holder).len + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ } \ } \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ MI_XMLRPC_HTTP_COPY(p, (temp_holder)); \ }while(0) static int mi_xmlrpc_http_recur_write_tree(char** pointer, char *buf, int max_page_len, struct mi_node *tree, int level, unsigned int flags, int flush, struct mi_node *parent, int object_flags); static int mi_xmlrpc_http_recur_write_node(char** pointer, char* buf, int max_page_len, struct mi_node *node, int level, int dump_name, int flush); static int mi_xmlrpc_http_recur_write_tree_old(char** pointer, char *buf, int max_page_len, struct mi_node *tree, int level); static int mi_xmlrpc_http_recur_flush_tree(char** pointer, char *buf, int max_page_len, struct mi_node *tree, int level); static int mi_xmlrpc_http_build_content_old(str *page, int max_page_len, struct mi_root* tree); static int mi_xmlrpc_http_write_node_old(char** pointer, char* buf, int max_page_len, struct mi_node *node, int level); static const str MI_XMLRPC_HTTP_CR = str_init("\n"); static const str MI_XMLRPC_HTTP_SLASH = str_init("/"); static const str MI_XMLRPC_HTTP_SEMICOLON = str_init(" : "); static const str MI_XMLRPC_HTTP_NODE_INDENT = str_init(" "); static const str MI_XMLRPC_HTTP_NODE_SEPARATOR = str_init(":: "); static const str MI_XMLRPC_HTTP_ATTR_SEPARATOR = str_init(" "); static const str MI_XMLRPC_HTTP_ATTR_VAL_SEPARATOR = str_init("="); static const str MI_XMLRPC_HTTP_XML_START = str_init(MI_XMLRPC_XML_START); static const str MI_XMLRPC_HTTP_XML_STOP = str_init(MI_XMLRPC_XML_STOP); static const str MI_XMLRPC_HTTP_XML_START_VER2 = str_init(MI_XMLRPC_XML_START_VER2); static const str MI_XMLRPC_HTTP_XML_STOP_VER2 = str_init(MI_XMLRPC_XML_STOP_VER2); static const str MI_XMLRPC_HTTP_ESC_LT = str_init("<"); /* < */ static const str MI_XMLRPC_HTTP_ESC_GT = str_init(">"); /* > */ static const str MI_XMLRPC_HTTP_ESC_AMP = str_init("&"); /* & */ static const str MI_XMLRPC_HTTP_ESC_QUOT = str_init("""); /* " */ static const str MI_XMLRPC_HTTP_ESC_SQUOT = str_init("'"); /* ' */ static const str MI_XMLRPC_HTTP_STRUCT_START = str_init(""); static const str MI_XMLRPC_HTTP_STRUCT_END = str_init(""); static const str MI_XMLRPC_HTTP_MEMBER_START = str_init(""); static const str MI_XMLRPC_HTTP_MEMBER_END = str_init(""); static const str MI_XMLRPC_HTTP_NAME_START = str_init(""); static const str MI_XMLRPC_HTTP_NAME_END = str_init(""); static const str MI_XMLRPC_HTTP_VALUE_START = str_init(""); static const str MI_XMLRPC_HTTP_VALUE_END = str_init(""); static const str MI_XMLRPC_HTTP_VALUE_DEFAULT = str_init("value"); static const str MI_XMLRPC_HTTP_NAME_DEFAULT = str_init("name"); static const str MI_XMLRPC_HTTP_KIDS_DEFAULT = str_init("kids"); static const str MI_XMLRPC_HTTP_ATTRIBUTES_DEFAULT = str_init("attributes"); static const str MI_XMLRPC_HTTP_ARRAY_START = str_init(""); static const str MI_XMLRPC_HTTP_ARRAY_END = str_init(""); static const str MI_XMLRPC_HTTP_DATA_START = str_init(""); static const str MI_XMLRPC_HTTP_DATA_END = str_init(""); int mi_xmlrpc_http_init_async_lock(void) { mi_xmlrpc_http_lock = lock_alloc(); if (mi_xmlrpc_http_lock==NULL) { LM_ERR("failed to create lock\n"); return -1; } if (lock_init(mi_xmlrpc_http_lock)==NULL) { LM_ERR("failed to init lock\n"); return -1; } return 0; } /* xmlAttrPtr mi_xmlNodeGetAttrByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = node->properties; while (attr) { if(xmlStrcasecmp(attr->name, (const xmlChar*)name)==0) return attr; attr = attr->next; } return NULL; } */ /* xmlNodePtr mi_xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = ph_xmlNodeGetAttrByName(node, name); if (attr) return (char*)xmlNodeGetContent(attr->children); else return NULL; } */ xmlNodePtr mi_xmlNodeGetNodeByName(xmlNodePtr node, const char *name) { xmlNodePtr cur = node; while (cur) { if(xmlStrcasecmp(cur->name, (const xmlChar*)name)==0) return cur; cur = cur->next; } return NULL; } /* char *ph_xmlNodeGetNodeContentByName(xmlNodePtr node, const char *name) { xmlNodePtr node1 = mi_xmlNodeGetNodeByName(node, name); if (node1) return (char*)xmlNodeGetContent(node1); else return NULL; } */ void mi_xmlrpc_http_destroy_async_lock(void) { if (mi_xmlrpc_http_lock) { lock_destroy(mi_xmlrpc_http_lock); lock_dealloc(mi_xmlrpc_http_lock); } } int mi_xmlrpc_http_flush_content(str *page, int max_page_len, struct mi_root* tree); int mi_xmlrpc_http_flush_content_old(str *page, int max_page_len, struct mi_root* tree); int mi_xmlrpc_http_flush_tree(void* param, struct mi_root *tree) { if (param==NULL) { LM_CRIT("null param\n"); return 0; } mi_xmlrpc_http_page_data_t* html_p_data = (mi_xmlrpc_http_page_data_t*)param; switch(version) { case MI_XMLRPC_FORMATED_OUTPUT: mi_xmlrpc_http_flush_content(&html_p_data->page, html_p_data->buffer.len, tree); break; case MI_XMLRPC_UNFORMATED_OUTPUT: mi_xmlrpc_http_flush_content_old(&html_p_data->page, html_p_data->buffer.len, tree); break; default: LM_ERR("Version param not set acordingly"); return -1; } return 0; } static void mi_xmlrpc_http_close_async(struct mi_root *mi_rpl, struct mi_handler *hdl, int done) { struct mi_root *shm_rpl = NULL; gen_lock_t* lock; mi_xmlrpc_http_async_resp_data_t *async_resp_data; int x; if (hdl==NULL) { LM_CRIT("null mi handler\n"); return; } LM_DBG("mi_root [%p], hdl [%p], hdl->param [%p] and done [%u]\n", mi_rpl, hdl, hdl->param, done); if (!done) { /* we do not pass provisional stuff (yet) */ if (mi_rpl) free_mi_tree( mi_rpl ); return; } async_resp_data = (mi_xmlrpc_http_async_resp_data_t*)(hdl+1); lock = async_resp_data->lock; if (mi_rpl==NULL || (shm_rpl=clone_mi_tree( mi_rpl, 1))==NULL) { LM_WARN("Unable to process async reply [%p]\n", mi_rpl); /* mark it as invalid */ shm_rpl = MI_XMLRPC_ASYNC_FAILED; } if (mi_rpl) free_mi_tree(mi_rpl); lock_get(lock); if (hdl->param==NULL) { hdl->param = shm_rpl; x = 0; } else { x = 1; } LM_DBG("shm_rpl [%p], hdl [%p], hdl->param [%p]\n", shm_rpl, hdl, hdl->param); lock_release(lock); if (x) { if (shm_rpl!=MI_XMLRPC_ASYNC_FAILED) free_shm_mi_tree(shm_rpl); shm_free(hdl); } return; } static inline struct mi_handler* mi_xmlrpc_http_build_async_handler(void) { struct mi_handler *hdl; mi_xmlrpc_http_async_resp_data_t *async_resp_data; unsigned int len; len = sizeof(struct mi_handler)+sizeof(mi_xmlrpc_http_async_resp_data_t); hdl = (struct mi_handler*)shm_malloc(len); if (hdl==NULL) { LM_ERR("oom\n"); return NULL; } memset(hdl, 0, len); async_resp_data = (mi_xmlrpc_http_async_resp_data_t*)(hdl+1); hdl->handler_f = mi_xmlrpc_http_close_async; hdl->param = NULL; async_resp_data->lock = mi_xmlrpc_http_lock; LM_DBG("hdl [%p], hdl->param [%p], mi_xmlrpc_http_lock=[%p]\n", hdl, hdl->param, async_resp_data->lock); return hdl; } struct mi_root* mi_xmlrpc_http_run_mi_cmd(const str* arg, str *page, str *buffer, struct mi_handler **async_hdl) { static str esc_buf = {NULL, 0}; struct mi_cmd *f; struct mi_node *node; struct mi_root *mi_cmd = NULL; struct mi_root *mi_rpl = NULL; struct mi_handler *hdl = NULL; str miCmd; xmlDocPtr doc; xmlNodePtr methodCall_node; xmlNodePtr methodName_node; xmlNodePtr params_node; xmlNodePtr param_node; xmlNodePtr value_node; xmlNodePtr string_node; str val, esc_val = {NULL, 0}; //LM_DBG("arg [%p]->[%.*s]\n", arg->s, arg->len, arg->s); doc = xmlParseMemory(arg->s, arg->len); if(doc==NULL){ LM_ERR("Failed to parse xml document: [%s]\n", arg->s); return NULL; } methodCall_node = mi_xmlNodeGetNodeByName(doc->children, MI_XMLRPC_HTTP_XML_METHOD_CALL_NODE); if (methodCall_node==NULL) { LM_ERR("missing node %s\n", MI_XMLRPC_HTTP_XML_METHOD_CALL_NODE); goto xml_error; } methodName_node = mi_xmlNodeGetNodeByName(methodCall_node->children, MI_XMLRPC_HTTP_XML_METHOD_NAME_NODE); if (methodName_node==NULL) { LM_ERR("missing node %s\n", MI_XMLRPC_HTTP_XML_METHOD_NAME_NODE); goto xml_error; } miCmd.s = (char*)xmlNodeGetContent(methodName_node); if (miCmd.s==NULL) { LM_ERR("missing content for node %s\n", MI_XMLRPC_HTTP_XML_METHOD_NAME_NODE); goto xml_error; } miCmd.len = strlen(miCmd.s); LM_DBG("got methodName=%.*s\n", miCmd.len, miCmd.s); f = lookup_mi_cmd(miCmd.s, miCmd.len); if (f == NULL) { LM_ERR("unable to find mi command [%.*s]\n", miCmd.len, miCmd.s); goto xml_error; } if (f->flags&MI_ASYNC_RPL_FLAG) { /* We need to build an async handler */ hdl = mi_xmlrpc_http_build_async_handler(); if (hdl==NULL) { LM_ERR("failed to build async handler\n"); goto xml_error; } } else { hdl = NULL; } if (f->flags&MI_NO_INPUT_FLAG) { mi_cmd = NULL; } else { if (arg->s) { mi_cmd = init_mi_tree(0,0,0); if (mi_cmd==NULL) { LM_ERR("the MI tree cannot be initialized!\n"); goto xml_error; } params_node = mi_xmlNodeGetNodeByName(methodCall_node->children, MI_XMLRPC_HTTP_XML_PARAMS_NODE); if (params_node!=NULL) { for(param_node=params_node->children; param_node;param_node=param_node->next){ if (xmlStrcasecmp(param_node->name, (const xmlChar*)MI_XMLRPC_HTTP_XML_PARAM_NODE) == 0) { value_node = mi_xmlNodeGetNodeByName(param_node->children, MI_XMLRPC_HTTP_XML_VALUE_NODE); if (value_node==NULL) { LM_ERR("missing node %s\n", MI_XMLRPC_HTTP_XML_VALUE_NODE); goto xml_error; } string_node = mi_xmlNodeGetNodeByName(value_node->children, MI_XMLRPC_HTTP_XML_STRING_NODE); if (string_node==NULL) { LM_ERR("missing node %s\n", MI_XMLRPC_HTTP_XML_STRING_NODE); goto xml_error; } val.s = (char*)xmlNodeGetContent(string_node); if(val.s==NULL){ LM_ERR("No content for node [%s]\n", string_node->name); goto xml_error; } val.len = strlen(val.s); if(val.len==0){ LM_ERR("Empty content for node [%s]\n", string_node->name); goto xml_error; } LM_DBG("got string param [%.*s]\n", val.len, val.s); if (val.len > esc_buf.len) { esc_buf.s = shm_realloc(esc_buf.s, val.len); if (!esc_buf.s) { esc_buf.len = 0; free_mi_tree(mi_cmd); goto xml_error; } esc_buf.len = val.len; } esc_val.s = esc_buf.s; esc_val.len = unescape_xml(esc_val.s, val.s, val.len); LM_DBG("got escaped string param [%.*s]\n", esc_val.len, esc_val.s); node = &mi_cmd->node; if(!add_mi_node_child(node,MI_DUP_VALUE,NULL,0,esc_val.s,esc_val.len)){ LM_ERR("cannot add the child node to the tree\n"); free_mi_tree(mi_cmd); goto xml_error; } } } } mi_cmd->async_hdl = hdl; } else { mi_cmd = NULL; } } html_page_data.page.s = buffer->s; html_page_data.page.len = 0; html_page_data.buffer.s = buffer->s; html_page_data.buffer.len = buffer->len; mi_rpl = run_mi_cmd(f, mi_cmd, (mi_flush_f *)mi_xmlrpc_http_flush_tree, &html_page_data); if (mi_rpl == NULL) { LM_ERR("failed to process the command\n"); goto xml_error; } else { *page = html_page_data.page; } LM_DBG("got mi_rpl=[%p]\n",mi_rpl); *async_hdl = hdl; if (mi_cmd) free_mi_tree(mi_cmd); if(doc)xmlFree(doc);doc=NULL; return mi_rpl; xml_error: if (mi_cmd) free_mi_tree(mi_cmd); if (hdl) shm_free(hdl); *async_hdl = NULL; if(doc)xmlFree(doc);doc=NULL; return NULL; } static int mi_xmlrpc_http_recur_write_node(char** pointer, char* buf, int max_page_len, struct mi_node *node, int level, int dump_name, int flush) { struct mi_attr *attr; str temp_holder; int temp_counter; if(dump_name){ MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_START); if (node->name.s!=NULL) MI_XMLRPC_HTTP_ESC_COPY(*pointer, node->name, temp_holder, temp_counter); else MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_DEFAULT); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_END); } MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_START); if(!node->kids && !node->attributes){ if (node->value.s!=NULL) MI_XMLRPC_HTTP_ESC_COPY(*pointer, node->value, temp_holder, temp_counter); else MI_XMLRPC_HTTP_ESC_COPY(*pointer, node->value, temp_holder, temp_counter); } else { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_STRUCT_START); if (node->value.s!=NULL){ MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_DEFAULT); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_START); MI_XMLRPC_HTTP_ESC_COPY(*pointer, node->value, temp_holder, temp_counter); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_END); } if (node->attributes != NULL){ MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_ATTRIBUTES_DEFAULT); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_STRUCT_START); for(attr = node->attributes; attr != NULL; attr = attr->next) { if (attr->name.s!=NULL) { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_START); MI_XMLRPC_HTTP_ESC_COPY(*pointer, attr->name,temp_holder,temp_counter); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_START); if (attr->value.s!=NULL) MI_XMLRPC_HTTP_ESC_COPY(*pointer, attr->value,temp_holder,temp_counter); else MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_DEFAULT); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_END); } } MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_STRUCT_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_END); } if (node->kids != NULL) { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_KIDS_DEFAULT); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_START); mi_xmlrpc_http_recur_write_tree(pointer, buf, max_page_len, node->kids, level + 3, node->flags, flush, node, MI_XMLRPC_FULL_OBJECT); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_END); } MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_STRUCT_END); } MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_END); return 0; error: LM_ERR("buffer 2 small: *pointer=[%p] buf=[%p] max_page_len=[%d]\n", *pointer, buf, max_page_len); return -1; } void flush_node (struct mi_node *parent, struct mi_node *prev) { struct mi_node *freed; if(!prev){ freed = parent->kids; parent->kids = freed->next; } else { freed = prev->next; prev->next = prev->next->next; } if(!freed->kids) free_mi_node(freed); } static int mi_xmlrpc_http_recur_write_tree(char** pointer, char *buf, int max_page_len, struct mi_node *tree, int level, unsigned int flags, int flush, struct mi_node *parent, int object_flags) { struct mi_node *t, *prev, *next; str temp_holder; int temp_counter; if (flags & MI_IS_ARRAY) { LM_DBG("Treated as an array\n"); if(object_flags & MI_XMLRPC_START_OBJECT){ MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_STRUCT_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_START); if (tree && tree->name.s) MI_XMLRPC_HTTP_ESC_COPY(*pointer, tree->name, temp_holder, temp_counter); else MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_DEFAULT); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NAME_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_ARRAY_START); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_DATA_START); } prev = NULL; t = tree; while(t) { mi_xmlrpc_http_recur_write_node(pointer, buf, max_page_len,t, level + 4, 0, flush); t->flags |= MI_WRITTEN; if(flush && !(t->flags & MI_NOT_COMPLETED)){ next = t->next; flush_node(parent, prev); t = next; } else { prev = t; t = t->next; } } if (object_flags & MI_XMLRPC_END_OBJECT) { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_DATA_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_ARRAY_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_VALUE_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_END); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_STRUCT_END); } } else { LM_DBG("Treated as an hash\n"); if (object_flags & MI_XMLRPC_START_OBJECT) { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_STRUCT_START); } prev = NULL; t = tree; while(t) { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_START); mi_xmlrpc_http_recur_write_node(pointer, buf, max_page_len,t, level + 2, 1, flush); MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_MEMBER_END); t->flags |= MI_WRITTEN; if(flush && !(t->flags & MI_NOT_COMPLETED)){ next = t->next; flush_node(parent, prev); t = next; } else{ prev = t; t = t->next; } } if (object_flags & MI_XMLRPC_END_OBJECT) { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_STRUCT_END); } } return 0; error: LM_ERR("buffer 2 small\n"); return -1; } int mi_xmlrpc_http_build_content(str *page, int max_page_len, struct mi_root* tree) { char *p, *buf; if (page->len==0) { p = buf = page->s; MI_XMLRPC_HTTP_COPY(p, MI_XMLRPC_HTTP_XML_START_VER2); if (mi_xmlrpc_http_recur_write_tree(&p, buf, max_page_len, tree->node.kids, 0, tree->node.flags, 0, NULL,MI_XMLRPC_FULL_OBJECT)<0) return -1; MI_XMLRPC_HTTP_COPY(p, MI_XMLRPC_HTTP_XML_STOP_VER2); page->len = p - page->s; } else { buf = page->s; p = page->s + page->len; if (tree) { /* Build mi reply */ if (mi_xmlrpc_http_recur_write_tree(&p, buf, max_page_len, tree->node.kids, 0, tree->node.flags, 0, NULL, MI_XMLRPC_END_OBJECT) < 0) return -1; MI_XMLRPC_HTTP_COPY(p, MI_XMLRPC_HTTP_XML_STOP_VER2); page->len = p - page->s; } } //LM_DBG("mesaj :\n %.*s\n", page->len, page->s); return 0; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } int mi_xmlrpc_http_build_page(str *page, int max_page_len, struct mi_root *tree) { switch(version) { case MI_XMLRPC_FORMATED_OUTPUT: if (0!=mi_xmlrpc_http_build_content(page, max_page_len, tree)) return -1; break; case MI_XMLRPC_UNFORMATED_OUTPUT: if (0!=mi_xmlrpc_http_build_content_old(page, max_page_len, tree)) return -1; break; default: LM_ERR("Version param not set acordingly"); return -1; } return 0; } int mi_xmlrpc_http_flush_content(str *page, int max_page_len, struct mi_root* tree) { char *p, *buf; if (page->len==0){ p = buf = page->s; MI_XMLRPC_HTTP_COPY(p, MI_XMLRPC_HTTP_XML_START_VER2); if (mi_xmlrpc_http_recur_write_tree(&p, buf, max_page_len, tree->node.kids, 0, tree->node.flags, 1, &tree->node,MI_XMLRPC_START_OBJECT)<0) return -1; page->len = p - page->s; return 0; } else { buf = page->s; p = page->s + page->len; if (tree) { /* Build mi reply */ if (mi_xmlrpc_http_recur_write_tree(&p, buf, max_page_len, tree->node.kids, 0, tree->node.flags, 1, &tree->node, 0)<0) return -1; page->len = p - page->s; } } return 0; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } /* old implementations for less formatted ouput */ int mi_xmlrpc_http_build_header(str *page, int max_page_len, struct mi_root *tree, int flush) { char *p, *buf; if (page->s == NULL) { LM_ERR("Please provide a valid page\n"); return -1; } p = buf = page->s; if (tree) { LM_DBG("return code: %d\n", tree->code); if (!(tree->node.flags & MI_WRITTEN)) { MI_XMLRPC_HTTP_COPY(p, MI_XMLRPC_HTTP_XML_START); tree->node.flags |= MI_WRITTEN; } if (flush) { if (mi_xmlrpc_http_recur_flush_tree(&p, buf, max_page_len, &tree->node, 0)<0) return -1; } else { if (mi_xmlrpc_http_recur_write_tree_old(&p, buf, max_page_len, tree->node.kids, 0)<0) return -1; } MI_XMLRPC_HTTP_COPY(p, MI_XMLRPC_HTTP_XML_STOP); } page->len = p - page->s; return 0; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } static int mi_xmlrpc_http_build_content_old(str *page, int max_page_len, struct mi_root* tree) { char *p, *buf; if (page->len==0) { if (0!=mi_xmlrpc_http_build_header(page, max_page_len, tree, 0)) return -1; } else { buf = page->s; p = page->s + page->len; if (tree) { /* Build mi reply */ if (mi_xmlrpc_http_recur_write_tree_old(&p, buf, max_page_len, tree->node.kids, 0)<0) return -1; page->len = p - page->s; } } return 0; } int mi_xmlrpc_http_flush_content_old(str *page, int max_page_len, struct mi_root* tree) { char *p, *buf; if (page->len==0) if (0!=mi_xmlrpc_http_build_header(page, max_page_len, tree, 1)) return -1; buf = page->s; p = page->s + page->len; if (tree) { /* Build mi reply */ if (mi_xmlrpc_http_recur_flush_tree(&p, buf, max_page_len, &tree->node, 0)<0) return -1; page->len = p - page->s; } return 0; } static int mi_xmlrpc_http_recur_flush_tree(char** pointer, char *buf, int max_page_len, struct mi_node *tree, int level) { struct mi_node *kid, *tmp; int ret; LM_DBG("flushing tree"); for(kid = tree->kids ; kid ; ){ if (!(kid->flags & MI_WRITTEN)) { if (mi_xmlrpc_http_write_node_old(pointer, buf, max_page_len, kid, level)!=0) return -1; kid->flags |= MI_WRITTEN; } if ((ret = mi_xmlrpc_http_recur_flush_tree(pointer, buf, max_page_len, tree->kids, level+1))<0){ return -1; } else if (ret > 0) { return ret; } if (!(kid->flags & MI_NOT_COMPLETED)){ tmp = kid; kid = kid->next; tree->kids = kid; if(!tmp->kids){ free_mi_node(tmp); } } else { return 1; } } return 0; } static int mi_xmlrpc_http_write_node_old(char** pointer, char* buf, int max_page_len, struct mi_node *node, int level) { struct mi_attr *attr; str temp_holder; int temp_counter; int insert_node_separator; /* name and value */ if (node->name.s!=NULL) { for(;level>0;level--) { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NODE_INDENT); } MI_XMLRPC_HTTP_COPY(*pointer, node->name); insert_node_separator = 1; } else { insert_node_separator = 0; } if (node->value.s!=NULL) { if (insert_node_separator) { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NODE_SEPARATOR); insert_node_separator = 0; } MI_XMLRPC_HTTP_ESC_COPY(*pointer, node->value, temp_holder, temp_counter); } /* attributes */ for(attr=node->attributes;attr!=NULL;attr=attr->next) { if (insert_node_separator) { MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_NODE_SEPARATOR); insert_node_separator = 0; } if (attr->name.s!=NULL) { MI_XMLRPC_HTTP_COPY_3(*pointer, MI_XMLRPC_HTTP_ATTR_SEPARATOR, attr->name, MI_XMLRPC_HTTP_ATTR_VAL_SEPARATOR); MI_XMLRPC_HTTP_ESC_COPY(*pointer, attr->value, temp_holder, temp_counter); } } MI_XMLRPC_HTTP_COPY(*pointer, MI_XMLRPC_HTTP_CR); return 0; error: LM_ERR("buffer 2 small: *pointer=[%p] buf=[%p] max_page_len=[%d]\n", *pointer, buf, max_page_len); return -1; } static int mi_xmlrpc_http_recur_write_tree_old(char** pointer, char *buf, int max_page_len, struct mi_node *tree, int level) { for( ; tree ; tree=tree->next ) { if (!(tree->flags & MI_WRITTEN)) { if (mi_xmlrpc_http_write_node_old(pointer, buf, max_page_len, tree, level)!=0){ return -1; } } if (tree->kids) { if (mi_xmlrpc_http_recur_write_tree_old(pointer, buf, max_page_len, tree->kids, level+1)<0){ return -1; } } } return 0; } opensips-2.2.2/modules/mi_xmlrpc_ng/http_fnc.h000066400000000000000000000065071300170765700214770ustar00rootroot00000000000000/* * Copyright (C) 2013 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2013-03-04 first version (osas) */ #ifndef _MI_XMLRPC_HTTP_HTTP_FNC_H #define _MI_XMLRPC_HTTP_HTTP_FNC_H #define MI_XMLRPC_XML_START "\r\n\r\n" \ "\r\n" #define MI_XMLRPC_XML_STOP "" \ "\r\n\r\n\r\n" #define MI_XMLRPC_XML_START_VER2 "\r\n\r\n" \ "\r\n" #define MI_XMLRPC_XML_STOP_VER2 "\r\n\r\n" #define MI_XMLRPC_XML_FAULT_START "\r\n\r\n" \ "\r\n\r\n" #define MI_XMLRPC_XML_FAULT_END "\r\n\r\n\r\n" \ "\r\n" #define MI_XMLRPC_XML_FAULT_MESSAGE_START "\r\nfaultString\r\n" \ "" #define MI_XMLRPC_XML_FAULT_MESSAGE_END "\r\n\r\n" #define MI_XMLRPC_XML_FAULT_CODE_START "\r\nfaultCode\r\n" \ "" #define MI_XMLRPC_XML_FAULT_CODE_END "\r\n\r\n" #define INIT_XMLRPC_FAULT(code, message) MI_XMLRPC_XML_FAULT_START \ MI_XMLRPC_XML_FAULT_CODE_START \ code \ MI_XMLRPC_XML_FAULT_CODE_END \ MI_XMLRPC_XML_FAULT_MESSAGE_START \ message \ MI_XMLRPC_XML_FAULT_MESSAGE_END \ MI_XMLRPC_XML_FAULT_END #define XMLRPC_FAULT_FORMAT MI_XMLRPC_XML_FAULT_START \ MI_XMLRPC_XML_FAULT_CODE_START \ "%u" \ MI_XMLRPC_XML_FAULT_CODE_END \ MI_XMLRPC_XML_FAULT_MESSAGE_START \ "%.*s" \ MI_XMLRPC_XML_FAULT_MESSAGE_END \ MI_XMLRPC_XML_FAULT_END #define MI_XMLRPC_START_OBJECT (1<<0) #define MI_XMLRPC_END_OBJECT (1<<1) #define MI_XMLRPC_FULL_OBJECT 3 #define MI_XMLRPC_FORMATED_OUTPUT 2 #define MI_XMLRPC_UNFORMATED_OUTPUT 1 #define MI_XMLRPC_ASYNC_FAILED ((void*)-2) #define MI_XMLRPC_ASYNC_EXPIRED ((void*)-3) typedef struct mi_xmlrpc_http_html_page_data_ { str page; str buffer; }mi_xmlrpc_http_page_data_t; typedef struct mi_xmlrpc_http_async_resp_data_ { gen_lock_t* lock; }mi_xmlrpc_http_async_resp_data_t; int mi_xmlrpc_http_init_async_lock(void); void mi_xmlrpc_http_destroy_async_lock(void); struct mi_root* mi_xmlrpc_http_run_mi_cmd(const str* arg, str *page, str *buffer, struct mi_handler **async_hdl); int mi_xmlrpc_http_build_page(str* page, int max_page_len, struct mi_root* tree); #endif opensips-2.2.2/modules/mi_xmlrpc_ng/mi_xmlrpc_http.c000066400000000000000000000204521300170765700227110ustar00rootroot00000000000000/* * Copyright (C) 2013 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2013-03-04 first version (osas) */ #include #include "../../globals.h" #include "../../sr_module.h" #include "../../str.h" #include "../../ut.h" #include "../../resolve.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../httpd/httpd_load.h" #include "http_fnc.h" /* module functions */ static int mod_init(); static int destroy(void); int mi_xmlrpc_http_answer_to_connection (void *cls, void *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls, str *buffer, str *page); static ssize_t mi_xmlrpc_http_flush_data(void *cls, uint64_t pos, char *buf, size_t max); str http_root = str_init("RPC2"); int version = 2; httpd_api_t httpd_api; #define MI_XMLRPC_NOT_ACCEPTABLE_STR "406" #define MI_XMLRPC_INTERNAL_ERROR_STR "500" static const str MI_XMLRPC_U_ERROR = str_init( INIT_XMLRPC_FAULT(MI_XMLRPC_INTERNAL_ERROR_STR, "Internal server error!" )); static const str MI_XMLRPC_U_METHOD = str_init( INIT_XMLRPC_FAULT(MI_XMLRPC_NOT_ACCEPTABLE_STR, "Unexpected method (only POST is accepted)!")); #define MI_XML_ERROR_BUF_MAX_LEN 1024 static char err_buf[MI_XML_ERROR_BUF_MAX_LEN]; #define MI_XMLRPC_PRINT_FAULT(page, code, message) \ do { \ page->len = snprintf(page->s, MI_XML_ERROR_BUF_MAX_LEN, \ XMLRPC_FAULT_FORMAT, \ code, message.len, message.s); \ } while(0); /* module parameters */ static param_export_t mi_params[] = { {"http_root", STR_PARAM, &http_root.s}, {"format_version", INT_PARAM, &version}, {0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "httpd", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* module exports */ struct module_exports exports = { "mi_xmlrpc_ng", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ NULL, /* exported functions */ NULL, /* exported async functions */ mi_params, /* exported parameters */ NULL, /* exported statistics */ NULL, /* exported MI functions */ NULL, /* exported PV */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ NULL /* per-child init function */ }; void proc_init(void) { /* Build async lock */ if (mi_xmlrpc_http_init_async_lock() != 0) exit(-1); return; } static int mod_init(void) { http_root.len = strlen(http_root.s); /* Load httpd api */ if(load_httpd_api(&httpd_api)<0) { LM_ERR("Failed to load httpd api\n"); return -1; } /* Load httpd hooks */ httpd_api.register_httpdcb(exports.name, &http_root, &mi_xmlrpc_http_answer_to_connection, &mi_xmlrpc_http_flush_data, &proc_init); return 0; } int destroy(void) { mi_xmlrpc_http_destroy_async_lock(); return 0; } static ssize_t mi_xmlrpc_http_flush_data(void *cls, uint64_t pos, char *buf, size_t max) { struct mi_handler *hdl = (struct mi_handler*)cls; gen_lock_t *lock; mi_xmlrpc_http_async_resp_data_t *async_resp_data; str page = {NULL, 0}; if (hdl==NULL) { LM_ERR("Unexpected NULL mi handler!\n"); return -1; } LM_DBG("hdl=[%p], hdl->param=[%p], pos=[%d], buf=[%p], max=[%d]\n", hdl, hdl->param, (int)pos, buf, (int)max); if (pos){ LM_DBG("freeing hdl=[%p]: hdl->param=[%p], " " pos=[%d], buf=[%p], max=[%d]\n", hdl, hdl->param, (int)pos, buf, (int)max); shm_free(hdl); return -1; } async_resp_data = (mi_xmlrpc_http_async_resp_data_t*)((char*)hdl+sizeof(struct mi_handler)); lock = async_resp_data->lock; lock_get(lock); if (hdl->param) { if (*(struct mi_root**)hdl->param) { page.s = buf; LM_DBG("tree=[%p]\n", *(struct mi_root**)hdl->param); if (mi_xmlrpc_http_build_page(&page, max, *(struct mi_root**)hdl->param)!=0){ LM_ERR("Unable to build response\n"); shm_free(*(void**)hdl->param); *(void**)hdl->param = NULL; lock_release(lock); memcpy(buf, MI_XMLRPC_U_ERROR.s, MI_XMLRPC_U_ERROR.len); return MI_XMLRPC_U_ERROR.len; } else { shm_free(*(void**)hdl->param); *(void**)hdl->param = NULL; lock_release(lock); return page.len; } } else { LM_DBG("data not ready yet\n"); lock_release(lock); return 0; } } else { lock_release(lock); LM_ERR("Invalid async reply\n"); memcpy(buf, MI_XMLRPC_U_ERROR.s, MI_XMLRPC_U_ERROR.len); return MI_XMLRPC_U_ERROR.len; } lock_release(lock); LM_CRIT("done?\n"); shm_free(hdl); return -1; } #define MI_XMLRPC_MAX_WAIT 2*60*4 static inline struct mi_root* mi_xmlrpc_wait_async_reply(struct mi_handler *hdl) { mi_xmlrpc_http_async_resp_data_t *async_resp_data = (mi_xmlrpc_http_async_resp_data_t*)(hdl+1); struct mi_root *mi_rpl; int i; int x; for( i=0 ; iparam) break; sleep_us(1000*500); } if (i==MI_XMLRPC_MAX_WAIT) { /* no more waiting ....*/ lock_get(async_resp_data->lock); if (hdl->param==NULL) { hdl->param = MI_XMLRPC_ASYNC_EXPIRED; x = 0; } else { x = 1; } lock_release(async_resp_data->lock); if (x==0) { LM_INFO("exiting before receiving reply\n"); return NULL; } } mi_rpl = (struct mi_root *)hdl->param; if (mi_rpl==MI_XMLRPC_ASYNC_FAILED) mi_rpl = NULL; /* free the async handler*/ shm_free(hdl); return mi_rpl; } #define MI_XMLRPC_OK 200 #define MI_XMLRPC_NOT_ACCEPTABLE 406 #define MI_XMLRPC_INTERNAL_ERROR 500 int mi_xmlrpc_http_answer_to_connection (void *cls, void *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls, str *buffer, str *page) { str arg = {NULL, 0}; struct mi_root *tree = NULL; struct mi_handler *async_hdl; int ret_code = MI_XMLRPC_OK; int is_shm = 0; page->s = err_buf; LM_DBG("START *** cls=%p, connection=%p, url=%s, method=%s, " "versio=%s, upload_data[%d]=%p, *con_cls=%p\n", cls, connection, url, method, version, (int)*upload_data_size, upload_data, *con_cls); if (strncmp(method, "POST", 4)==0) { httpd_api.lookup_arg(connection, "1", *con_cls, &arg); if (arg.s) { tree = mi_xmlrpc_http_run_mi_cmd(&arg, page, buffer, &async_hdl); if (tree == MI_ROOT_ASYNC_RPL) { LM_DBG("got an async reply\n"); tree = mi_xmlrpc_wait_async_reply(async_hdl); async_hdl = NULL; is_shm = 1; } if (tree == NULL) { LM_ERR("no reply\n"); *page = MI_XMLRPC_U_ERROR; } else { LM_DBG("building on page [%p:%d]\n", page->s, page->len); if(0!=mi_xmlrpc_http_build_page(page, buffer->len, tree)){ LM_ERR("unable to build response\n"); *page = MI_XMLRPC_U_ERROR; } else { if (tree->code >= 400) { MI_XMLRPC_PRINT_FAULT(page, tree->code, tree->reason); } } } } else { page->s = buffer->s; LM_ERR("unable to build response for empty request\n"); *page = MI_XMLRPC_U_ERROR; } if (tree) { is_shm?free_shm_mi_tree(tree):free_mi_tree(tree); tree = NULL; } } else { LM_ERR("unexpected method [%s]\n", method); *page = MI_XMLRPC_U_METHOD; } return ret_code; } opensips-2.2.2/modules/mmgeoip/000077500000000000000000000000001300170765700164705ustar00rootroot00000000000000opensips-2.2.2/modules/mmgeoip/Makefile000066400000000000000000000017741300170765700201410ustar00rootroot00000000000000# # # WARNING: do not run this directly, it should be run by the master Makefile # # # This file is part of openser, a free SIP server. # # openser is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # openser is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # History: # -------- # include ../../Makefile.defs auto_gen= NAME=mmgeoip.so #LIBS+=-lGeoIP LIBS=-L$(LOCALBASE)/lib -lGeoIP DEFS+=-I$(LOCALBASE)/include include ../../Makefile.modules opensips-2.2.2/modules/mmgeoip/README000066400000000000000000000071321300170765700173530ustar00rootroot00000000000000mmgeoip Module Kobi Eshun SightSpeed, Inc. Edited by Kobi Eshun Copyright © 2008 SightSpeed, Inc. Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. mmgeoip_city_db_path (string) 1.3.2. cache_type (string) 1.4. Exported Functions 1.4.1. mmg_lookup([fields,]src,dst) 1.5. Known Issues List of Examples 1.1. Set “mmgeoip_city_db_path†parameter 1.2. Set “cache_type†parameter 1.3. mmg_lookup usage Chapter 1. Admin Guide 1.1. Overview This module is a lightweight wrapper for the MaxMind GeoIP API. It adds IP address-to-location lookup capability to OpenSIPS scripts. Lookups are executed against the freely-available GeoLite City database; and the non-free GeoIP City database is drop-in compatible. All lookup fields provided by the API are accessible by the script. Visit the MaxMind website for more information on the location databases. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libGeoIP. 1.3. Exported Parameters 1.3.1. mmgeoip_city_db_path (string) Path to either a GeoLite or GeoIP City database file. Mandatory parameter. Example 1.1. Set “mmgeoip_city_db_path†parameter ... modparam("mmgeoip", "mmgeoip_city_db_path", "/usr/share/GeoIP/GeoLiteCity.dat") ... 1.3.2. cache_type (string) Databse memory caching options. The following options are available: * STANDARD - Read database from file system; least memory used; * MMAP_CACHE - Load database into mmap allocated memory; WARNING: this option will cause a segmentation fault if database file is changed at runtime! * MEM_CACHE_CHECK - Load database into memory; this mode checks for database updates; if database was modified, the file will be reloaded after 60 seconds; it will be slower than MMAP_CACHE but it will allow reloads; Default value for this parameter is MMAP_CACHE. Example 1.2. Set “cache_type†parameter ... modparam("mmgeoip", "cache_type","MEM_CACHE_CHECK") ... 1.4. Exported Functions 1.4.1. mmg_lookup([fields,]src,dst) Looks up specified fields associated with IP address specified by the src. The resulting data are loaded in reverse order into the dst AVP. src can be a pseudo-variable or AVP; and dst must be an AVP. fields defaults to "lon:lat," and is a colon-delimited list of these elements: * lat Latitude * lon Longitude * cont Continent * cc Country Code * reg Region * city City * pc Postal Code * dma DMA Code * ac Area Code * TZ Time Zone This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE,ERROR_ROUTE, and LOCAL_ROUTE. Example 1.3. mmg_lookup usage ... if(mmg_lookup("lon:lat","$si","$avp(lat_lon)")) { xlog("L_INFO","Source IP latitude:$(avp(lat_lon)[0])\n"); xlog("L_INFO","Source IP longitude:$(avp(lat_lon)[1])\n"); }; ... 1.5. Known Issues It is not currently possible to load an updated location database without first stalling the server. opensips-2.2.2/modules/mmgeoip/doc/000077500000000000000000000000001300170765700172355ustar00rootroot00000000000000opensips-2.2.2/modules/mmgeoip/doc/mmgeoip.xml000066400000000000000000000022001300170765700214060ustar00rootroot00000000000000 %docentities; ]> mmgeoip Module &osipsname; Kobi Eshun SightSpeed, Inc.
noreply@sightspeed.com
Kobi Eshun
noreply@sightspeed.com
2008 SightSpeed, Inc. $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/mmgeoip/doc/mmgeoip_admin.xml000066400000000000000000000122061300170765700225650ustar00rootroot00000000000000 &adminguide;
Overview This module is a lightweight wrapper for the MaxMind GeoIP API. It adds IP address-to-location lookup capability to &osips; scripts. Lookups are executed against the freely-available GeoLite City database; and the non-free GeoIP City database is drop-in compatible. All lookup fields provided by the API are accessible by the script. Visit the MaxMind website for more information on the location databases.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libGeoIP.
Exported Parameters
<varname>mmgeoip_city_db_path</varname> (string) Path to either a GeoLite or GeoIP City database file. Mandatory parameter. Set <quote>mmgeoip_city_db_path</quote> parameter ... modparam("mmgeoip", "mmgeoip_city_db_path", "/usr/share/GeoIP/GeoLiteCity.dat") ...
<varname>cache_type</varname> (string) Databse memory caching options. The following options are available: STANDARD - Read database from file system; least memory used; MMAP_CACHE - Load database into mmap allocated memory; WARNING: this option will cause a segmentation fault if database file is changed at runtime! MEM_CACHE_CHECK - Load database into memory; this mode checks for database updates; if database was modified, the file will be reloaded after 60 seconds; it will be slower than MMAP_CACHE but it will allow reloads; Default value for this parameter is MMAP_CACHE. Set <quote>cache_type</quote> parameter ... modparam("mmgeoip", "cache_type","MEM_CACHE_CHECK") ...
Exported Functions
<function moreinfo="none">mmg_lookup([fields,]src,dst)</function> Looks up specified fields associated with IP address specified by the src. The resulting data are loaded in reverse order into the dst AVP. src can be a pseudo-variable or AVP; and dst must be an AVP. fields defaults to "lon:lat," and is a colon-delimited list of these elements: lat Latitude lon Longitude cont Continent cc Country Code reg Region city City pc Postal Code dma DMA Code ac Area Code TZ Time Zone This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE,ERROR_ROUTE, and LOCAL_ROUTE. <function moreinfo="none">mmg_lookup</function> usage ... if(mmg_lookup("lon:lat","$si","$avp(lat_lon)")) { xlog("L_INFO","Source IP latitude:$(avp(lat_lon)[0])\n"); xlog("L_INFO","Source IP longitude:$(avp(lat_lon)[1])\n"); }; ...
Known Issues It is not currently possible to load an updated location database without first stalling the server.
opensips-2.2.2/modules/mmgeoip/mmgeoip.c000066400000000000000000000216051300170765700202750ustar00rootroot00000000000000/* * This file is part of openser, a free SIP server. * * openser is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * openser is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 080511 -- Initial revision, KE * * XXX -- todo: Add command variant to pull source/dest IP from * current SIP message. * */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../str.h" #include "../../usr_avp.h" #include "../../mod_fix.h" #include "../../ut.h" #include "GeoIP.h" #include "GeoIPCity.h" #define MMG_OP_DELIMS ":|,/ " static str MMG_city_db_path = {NULL, 0}; static GeoIP *MMG_gi = NULL; static int geoip_cache_option = GEOIP_MMAP_CACHE; int parse_mem_option( unsigned int type, void *val) { str opt_s; static const str opt_STANDARD = str_init("STANDARD"); static const str opt_MMAP = str_init("MMAP_CACHE"); static const str opt_MEM_CHECK = str_init("MEM_CACHE_CHECK"); opt_s.s = (char *) val; opt_s.len = strlen(opt_s.s); if (opt_s.len == opt_STANDARD.len && !strncasecmp(opt_s.s, opt_STANDARD.s, opt_s.len)) { geoip_cache_option = GEOIP_STANDARD; } else if (opt_s.len == opt_MMAP.len && !strncasecmp(opt_s.s, opt_MMAP.s, opt_s.len)) { geoip_cache_option = GEOIP_MMAP_CACHE; } else if (opt_s.len == opt_MEM_CHECK.len && !strncasecmp(opt_s.s, opt_MEM_CHECK.s, opt_s.len)) { geoip_cache_option = GEOIP_MEMORY_CACHE|GEOIP_CHECK_CACHE; } else { LM_ERR("Invalid cache option!\n"); return -1; } return 0; } static int mod_init(void) { LM_INFO("MM GeoIP module - initializing\n"); if (!MMG_city_db_path.s) { LM_ERR("mandatory parameter 'city_db_path' not set.\n"); return -1; } MMG_city_db_path.len=strlen(MMG_city_db_path.s); if(0==(MMG_gi = GeoIP_open(MMG_city_db_path.s, geoip_cache_option))){ LM_ERR("Unable to open City DB at path '%.*s'.\n", MMG_city_db_path.len,MMG_city_db_path.s); return -1; } LM_INFO("MM GeoIP module - city_db_path:'%s'\n", MMG_city_db_path.s); return 0; } static void mod_destroy(void) { if(MMG_gi)GeoIP_delete(MMG_gi); return; } static int fixup_lookup3(void **param, int param_no) { str s; s.s=(char *)(*param); s.len=strlen(s.s); if(!s.len) { LM_ERR("fixup_lookup3:Parameter %d is empty.\n", param_no); return E_CFG; } if(1==param_no || 2==param_no) { /* Expecting input pseudo vars --> pv_elem_t */ pv_elem_t *model=0; if(pv_parse_format(&s,&model) || !model) { LM_ERR("Bad format for input PV: '%s'.", s.s); return E_CFG; } *param=(void*)model; return 0; } else if(3==param_no) { /* Expecting AVP spec --> pv_spec_t */ pv_spec_t *spec; int ret=fixup_pvar(param); if(ret<0) return ret; spec=(pv_spec_t*)(*param); if(spec->type!=PVT_AVP) { LM_ERR("AVP required for return value!\n"); return E_CFG; } return 0; } else { LM_ERR("Invalid parameter number: %d.\n", param_no); return E_CFG; } return 0; } static int fixup_lookup2(void **param, int param_no) { if(1==param_no) return fixup_lookup3(param,2); if(2==param_no) return fixup_lookup3(param,3); LM_ERR("Invalid parameter number: %d.\n", param_no); return E_CFG; } #include static int mmg_lookup_cmd(struct sip_msg *msg, char *_fields_pv, char *_ipaddr_pv, char *_dst_spec) { pv_elem_t *fields_pv=(pv_elem_t*)_fields_pv, *ipaddr_pv=(pv_elem_t*)_ipaddr_pv; pv_spec_t *dst_spec=(pv_spec_t*)_dst_spec; GeoIPRecord *gir=0; str field_str, ipaddr_str; char rslt_buf[256], ipaddr_buf[256], field_buf[256]; char *token=0, *saveptr=0; int dst_name=-1; int_str rslt=(int_str)0; unsigned short dstType=0; /* Sanity checks */ if(!(ipaddr_pv && fields_pv && dst_spec)) { LM_ERR("Missing argument(s).\n"); return -1; } if(dst_spec->type != PVT_AVP) { LM_ERR("Invalid destination spec -- expected AVP.\n"); return -1; } if(pv_get_avp_name(msg, &(dst_spec->pvp), &dst_name, &dstType)!=0) { LM_ERR("Internal error getting AVP name.\n"); return -1; } /* Expand input args: lookup field list and IP address.*/ *ipaddr_buf=0; ipaddr_str.s=ipaddr_buf; ipaddr_str.len=sizeof ipaddr_buf; if(pv_printf(msg, ipaddr_pv, ipaddr_buf, &ipaddr_str.len) || ipaddr_str.len==0) { LM_ERR("Internal error parsing IP address.\n"); return -1; } *field_buf=0; field_str.s=field_buf; field_str.len=sizeof field_buf; if(pv_printf(msg, fields_pv, field_buf, &field_str.len) || field_str.len==0) { LM_ERR("Internal error parsing lookup fields.\n"); return -1; } /* Attempt lookup */ if(!(gir=GeoIP_record_by_name (MMG_gi,ipaddr_buf))){ LM_DBG("'%s'--> 'Unknown'.\n", *ipaddr_buf?ipaddr_buf:"Undefined"); return -1; } /* Construct return data. Know fields are: */ /* lat - latitude */ /* lon - longitude */ /* cont - continent */ /* cc - country code */ /* reg - region */ /* city - city */ /* pc - postal code */ /* dma - dma code */ /* ac - area code */ /* tz - timezone */ #define MMG_FAIL_EXIT if(gir) GeoIPRecord_delete(gir); return -1 LM_DBG("ipaddr:'%.*s'; fields:'%.*s'.\n", ipaddr_str.len, ipaddr_str.s, field_str.len, field_str.s); *rslt_buf=0; rslt.s.s=rslt_buf; token=strtok_r(field_buf,MMG_OP_DELIMS,&saveptr); while (token) { if(!strcmp(token,"lat")) { rslt.s.len=snprintf(rslt_buf,sizeof rslt_buf,"%f",gir->latitude); } else if(!strcmp(token,"lon")) { rslt.s.len=snprintf(rslt_buf,sizeof rslt_buf,"%f",gir->longitude); } else if(!strcmp(token,"cont")) { rslt.s.len=snprintf(rslt_buf,sizeof rslt_buf,"%s",gir->continent_code); } else if(!strcmp(token,"cc")) { rslt.s.len=snprintf(rslt_buf,sizeof rslt_buf,"%s",gir->country_code); } else if(!strcmp(token,"reg")) { rslt.s.len=snprintf(rslt_buf,sizeof rslt_buf,"%s",gir->region); } else if(!strcmp(token,"city")) { rslt.s.len=snprintf(rslt_buf,sizeof rslt_buf,"%s",gir->city); } else if(!strcmp(token,"pc")) { rslt.s.len=snprintf(rslt_buf,sizeof rslt_buf,"%s",gir->postal_code); } else if(!strcmp(token,"dma")) { rslt.s.len=snprintf(rslt_buf,sizeof rslt_buf,"%d",gir->dma_code); } else if(!strcmp(token,"ac")) { rslt.s.len=snprintf(rslt_buf,sizeof rslt_buf,"%d",gir->area_code); } else if(!strcmp(token,"rbc")) { rslt.s.len=snprintf( rslt_buf,sizeof rslt_buf,"%s",GeoIP_region_name_by_code(gir->country_code, gir->region)); } else if(!strcmp(token,"tz")) { rslt.s.len=snprintf( rslt_buf,sizeof rslt_buf,"%s",GeoIP_time_zone_by_country_and_region(gir->country_code, gir->region)); } else { LM_ERR("unknown field:'%s'\n",token); MMG_FAIL_EXIT; } if(rslt.s.len<0 || rslt.s.len>sizeof rslt_buf || add_avp(dstType|AVP_VAL_STR,dst_name,rslt)==-1 ) { LM_ERR("Internal error processing field/IP '%s/%s'.\n", token,ipaddr_buf); MMG_FAIL_EXIT; } LM_DBG("field %s[%s] %.*s\n",ipaddr_buf,token,rslt.s.len,rslt.s.s); token=strtok_r(0,MMG_OP_DELIMS,&saveptr); } GeoIPRecord_delete(gir); return 1; } static int w_lookup_cmd2(struct sip_msg *m, char *ipaddr, char *dst) { return mmg_lookup_cmd(m,"lon:lat",ipaddr,dst); } /* * wire module pieces together. */ static param_export_t mod_params[]={ {"mmgeoip_city_db_path", STR_PARAM, &MMG_city_db_path.s}, {"cache_type", STR_PARAM|USE_FUNC_PARAM, parse_mem_option}, { 0,0,0 } }; static cmd_export_t cmds[] = { {"mmg_lookup", (cmd_function)w_lookup_cmd2, 2, fixup_lookup2, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|ERROR_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"mmg_lookup", (cmd_function)mmg_lookup_cmd, 3, fixup_lookup3, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|ERROR_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {0, 0, 0, 0, 0, 0} }; struct module_exports exports= { "mmgeoip", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module's name */ DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ mod_params, /* param exports */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, /* Destroy function */ 0 /* per-child init function */ }; opensips-2.2.2/modules/msilo/000077500000000000000000000000001300170765700161565ustar00rootroot00000000000000opensips-2.2.2/modules/msilo/Makefile000066400000000000000000000003201300170765700176110ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=msilo.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/msilo/README000066400000000000000000000451731300170765700170500ustar00rootroot00000000000000MSILO Module Daniel-Constantin Mierla Edited by Daniel-Constantin Mierla Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS modules 1.2.2. External libraries or applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. db_table (string) 1.3.3. from_address (string) 1.3.4. contact_hdr (string) 1.3.5. offline_message (string) 1.3.6. content_type_hdr (string) 1.3.7. reminder (string) 1.3.8. outbound_proxy (string) 1.3.9. expire_time (int) 1.3.10. check_time (int) 1.3.11. send_time (int) 1.3.12. clean_period (int) 1.3.13. use_contact (int) 1.3.14. sc_mid (string) 1.3.15. sc_from (string) 1.3.16. sc_to (string) 1.3.17. sc_uri_user (string) 1.3.18. sc_uri_host (string) 1.3.19. sc_body (string) 1.3.20. sc_ctype (string) 1.3.21. sc_exp_time (string) 1.3.22. sc_inc_time (string) 1.3.23. sc_snd_time (string) 1.3.24. snd_time_avp (str) 1.3.25. add_date (int) 1.3.26. max_messages (int) 1.4. Exported Functions 1.4.1. m_store([owner]) 1.4.2. m_dump([owner, [maxmsg]]) 1.5. Exported Statistics 1.5.1. stored_messages 1.5.2. dumped_messages 1.5.3. failed_messages 1.5.4. dumped_reminders 1.5.5. failed_reminders 1.6. Installation and Running 1.6.1. OpenSIPS config file List of Examples 1.1. Set the “db_url†parameter 1.2. Set the “db_table†parameter 1.3. Set the “from_address†parameter 1.4. Set the “contact_hdr†parameter 1.5. Set the “offline_message†parameter 1.6. Set the “content_type_hdr†parameter 1.7. Set the “reminder†parameter 1.8. Set the “outbound_proxy†parameter 1.9. Set the “expire_time†parameter 1.10. Set the “check_time†parameter 1.11. Set the “send_time†parameter 1.12. Set the “clean_period†parameter 1.13. Set the “use_contact†parameter 1.14. Set the “sc_mid†parameter 1.15. Set the “sc_from†parameter 1.16. Set the “sc_to†parameter 1.17. Set the “sc_uri_user†parameter 1.18. Set the “sc_uri_host†parameter 1.19. Set the “sc_body†parameter 1.20. Set the “sc_ctype†parameter 1.21. Set the “sc_exp_time†parameter 1.22. Set the “sc_inc_time†parameter 1.23. Set the “sc_snd_time†parameter 1.24. Set the “snd_time_avp†parameter 1.25. Set the “add_date†parameter 1.26. Set the “max_messages†parameter 1.27. m_store usage 1.28. m_dump usage 1.29. OpenSIPS config script - sample msilo usage Chapter 1. Admin Guide 1.1. Overview This modules provides offline message storage for the Open SIP Server. It stores received messages for an offline user and sends them when the user becomes online. For each message, the modules stores “Request-URI†(“R-URIâ€) only if it is a complete address of record (“username@hostnameâ€), URI from “To†header, URI from “From†header, incoming time, expiration time, content type and body of the message. If “R-URI†is not an address of record (it might be the contact address for current SIP session) the URI from “To†header will be used as R-URI. When the expiration time passed, the message is discarded from database. Expiration time is computed based on incoming time and one of the module's parameters. Every time when a user registers with OpenSIPS, the module is looking in database for offline messages intended for that user. All of them will be sent to contact address provided in REGISTER request. It may happen the SIP user to be registered but his SIP User Agent to have no support for MESSAGE request. In this case it should be used the “failure_route†to store the undelivered requests. Another functionality provided by the modules is to send messages at a certain time -- the reminder functionality. Using config logic, a received message can be stored and delivered at a time specified while storing with the 'snd_time_avp'. 1.2. Dependencies 1.2.1. OpenSIPS modules The following modules must be loaded before this module: * database module - mysql, dbtext or other module that implements the “db†interface and provides support for storing/receiving data to/from a database system. * TM--transaction module--is used to send SIP requests. 1.2.2. External libraries or applications The following libraries or applications must be installed before running OpenSIPS with this module: * none. 1.3. Exported Parameters 1.3.1. db_url (string) Database URL. Default value is “mysql://opensips:opensipsrw@localhost/opensipsâ€. Example 1.1. Set the “db_url†parameter ... modparam("msilo", "db_url", "mysql://user:passwd@host.com/dbname") ... 1.3.2. db_table (string) The name of table where to store the messages. Default value is “siloâ€. Example 1.2. Set the “db_table†parameter ... modparam("msilo", "db_table", "silo") ... 1.3.3. from_address (string) The SIP address used to inform users that destination of their message is not online and the message will be delivered next time when that user goes online. If the parameter is not set, the module will not send any notification. It can contain pseudo-variables. Default value is “NULLâ€. Example 1.3. Set the “from_address†parameter ... modparam("msilo", "from_address", "sip:registrar@example.org") modparam("msilo", "from_address", "sip:$rU@example.org") ... 1.3.4. contact_hdr (string) The value of the Contact header (including header name and ending \r\n) to be added in notification messages. It can contain pseudo-variables. Default value is “NULLâ€. Example 1.4. Set the “contact_hdr†parameter ... modparam("msilo", "contact_hdr", "Contact: \r\n") ... 1.3.5. offline_message (string) The body of the notification message. It can contain pseudo-variables. Default value is “NULLâ€. Example 1.5. Set the “offline_message†parameter ... modparam("msilo", "offline_message", "*** User $rU is offline!") modparam("msilo", "offline_message", "I am offline!") ... 1.3.6. content_type_hdr (string) The value of the Content-Type header (including header name and ending \r\n) to be added in notification messages. It must reflect what the 'offline_message' contains. It can contain pseudo-variables. Default value is “NULLâ€. Example 1.6. Set the “content_type_hdr†parameter ... modparam("msilo", "content_type_hdr", "Content-Type: text/plain\r\n") modparam("msilo", "content_type_hdr", "Content-Type: text/html\r\n") ... 1.3.7. reminder (string) The SIP address used to send reminder messages. If this value is not set, the reminder feature is disabled. Default value is “NULLâ€. Example 1.7. Set the “reminder†parameter ... modparam("msilo", "reminder", "sip:registrar@example.org") ... 1.3.8. outbound_proxy (string) The SIP address used as next hop when sending the message. Very useful when using OpenSIPS with a domain name not in DNS, or when using a separate OpenSIPS instance for msilo processing. If not set, the message will be sent to the address in destination URI. Default value is “NULLâ€. Example 1.8. Set the “outbound_proxy†parameter ... modparam("msilo", "outbound_proxy", "sip:opensips.org;transport=tcp") ... 1.3.9. expire_time (int) Expire time of stored messages - seconds. When this time passed, the message is silently discarded from database. Default value is “259200 (72 hours = 3 days)â€. Example 1.9. Set the “expire_time†parameter ... modparam("msilo", "expire_time", 36000) ... 1.3.10. check_time (int) Timer interval to check if dumped messages are sent OK - seconds. The module keeps each request send by itself for a new online user and if the reply is 2xx then the message is deleted from database. Default value is “30â€. Example 1.10. Set the “check_time†parameter ... modparam("msilo", "check_time", 10) ... 1.3.11. send_time (int) Timer interval in seconds to check if there are reminder messages. The module takes all reminder messages that must be sent at that moment or before that moment. If the value is 0, the reminder feature is disabled. Default value is “0â€. Example 1.11. Set the “send_time†parameter ... modparam("msilo", "send_time", 60) ... 1.3.12. clean_period (int) Number of “check_time†cycles when to check if there are expired messages in database. Default value is “5â€. Example 1.12. Set the “clean_period†parameter ... modparam("msilo", "clean_period", 3) ... 1.3.13. use_contact (int) Turns on/off the usage of the Contact address to send notification back to sender whose message is stored by MSILO. Default value is “1 (0 = off, 1 = on)â€. Example 1.13. Set the “use_contact†parameter ... modparam("msilo", "use_contact", 0) ... 1.3.14. sc_mid (string) The name of the column in silo table, storing message id. Default value is “midâ€. Example 1.14. Set the “sc_mid†parameter ... modparam("msilo", "sc_mid", "other_mid") ... 1.3.15. sc_from (string) The name of the column in silo table, storing the source address. Default value is “src_addrâ€. Example 1.15. Set the “sc_from†parameter ... modparam("msilo", "sc_from", "source_address") ... 1.3.16. sc_to (string) The name of the column in silo table, storing the destination address. Default value is “dst_addrâ€. Example 1.16. Set the “sc_to†parameter ... modparam("msilo", "sc_to", "destination_address") ... 1.3.17. sc_uri_user (string) The name of the column in silo table, storing the user name. Default value is “usernameâ€. Example 1.17. Set the “sc_uri_user†parameter ... modparam("msilo", "sc_uri_user", "user") ... 1.3.18. sc_uri_host (string) The name of the column in silo table, storing the domain. Default value is “domainâ€. Example 1.18. Set the “sc_uri_host†parameter ... modparam("msilo", "sc_uri_host", "domain") ... 1.3.19. sc_body (string) The name of the column storing the message body in silo table. Default value is “bodyâ€. Example 1.19. Set the “sc_body†parameter ... modparam("msilo", "sc_body", "message_body") ... 1.3.20. sc_ctype (string) The name of the column in silo table, storing content type. Default value is “ctypeâ€. Example 1.20. Set the “sc_ctype†parameter ... modparam("msilo", "sc_ctype", "content_type") ... 1.3.21. sc_exp_time (string) The name of the column in silo table, storing the expire time of the message. Default value is “exp_timeâ€. Example 1.21. Set the “sc_exp_time†parameter ... modparam("msilo", "sc_exp_time", "expire_time") ... 1.3.22. sc_inc_time (string) The name of the column in silo table, storing the incoming time of the message. Default value is “inc_timeâ€. Example 1.22. Set the “sc_inc_time†parameter ... modparam("msilo", "sc_inc_time", "incoming_time") ... 1.3.23. sc_snd_time (string) The name of the column in silo table, storing the send time for the reminder. Default value is “snd_timeâ€. Example 1.23. Set the “sc_snd_time†parameter ... modparam("msilo", "sc_snd_time", "send_reminder_time") ... 1.3.24. snd_time_avp (str) The name of an AVP which may contain the time when to sent the received message as reminder.The AVP is used ony by m_store(). If the parameter is not set, the module does not look for this AVP. If the value is set to a valid AVP name, then the module expects in the AVP to be a time value in format YYYYMMDDHHMMSS (e.g., 20060101201500). Default value is “nullâ€. Example 1.24. Set the “snd_time_avp†parameter ... modparam("msilo", "snd_time_avp", "$avp(snd_time)") ... 1.3.25. add_date (int) Wheter to add as prefix the date when the message was stored. Default value is “1†(1==on/0==off). Example 1.25. Set the “add_date†parameter ... modparam("msilo", "add_date", 0) ... 1.3.26. max_messages (int) Maximum number of stored message for an AoR. Value 0 equals to no limit. Default value is 0. Example 1.26. Set the “max_messages†parameter ... modparam("msilo", "max_messages", 0) ... 1.4. Exported Functions 1.4.1. m_store([owner]) The method stores certain parts of the current SIP request (it should be called when the request type is MESSAGE and the destination user is offline or his UA does not support MESSAGE requests). If the user is registered with a UA which does not support MESSAGE requests you should not use mode=“0†if you have changed the request uri with the contact address of user's UA. Meaning of the parameters is as follows: * owner - is a string that must contain a SIP URI in whose inbox the message will be stored. It can have any pseudo variable. If "owner" is missing, the SIP address is taken from R-URI. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.27. m_store usage ... m_store(); m_store("$tu"); ... 1.4.2. m_dump([owner, [maxmsg]]) The method sends stored messages for the SIP user that is going to register to his actual contact address. The method should be called when a REGISTER request is received and the “Expire†header has a value greater than zero. Meaning of the parameters is as follows: * owner - is a string that must contain a SIP URI whose inbox will be dumped. It can have any pseudo variable. If "owner" is missing, the SIP address is taken from To URI. maxmsg - is a maximum number of messages to be dumped. This function can be used from REQUEST_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE Example 1.28. m_dump usage ... m_dump(); m_dump("$fu"); m_dump("$fu", 10); ... 1.5. Exported Statistics 1.5.1. stored_messages The number of messages stored by msilo. 1.5.2. dumped_messages The number of dumped messages. 1.5.3. failed_messages The number of failed dumped messages. 1.5.4. dumped_reminders The number of dumped reminder messages. 1.5.5. failed_reminders The number of failed reminder messages. 1.6. Installation and Running 1.6.1. OpenSIPS config file Next picture displays a sample usage of msilo. Example 1.29. OpenSIPS config script - sample msilo usage ... # # MSILO usage example # # # running in debug mode (log level 4, log to stderr, stay in foreground) debug_mode=yes check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 listen=10.0.0.2 # listen address # ------------------ module loading ---------------------------------- mpath="/usr/local/lib/opensips/modules/" loadmodule "textops.so" loadmodule "sl.so" loadmodule "mysql.so" loadmodule "maxfwd.so" loadmodule "msilo.so" loadmodule "tm.so" loadmodule "registrar.so" loadmodule "usrloc.so" # ----------------- setting module-specific parameters --------------- # -- registrar params -- modparam("registrar", "default_expires", 120) # -- registrar params -- modparam("usrloc", "db_mode", 0) # -- msilo params -- modparam("msilo","db_url","mysql://opensips:opensipsrw@localhost/opensip s") modparam("msilo","from_address","sip:registrar@opensips.org") modparam("msilo","contact_hdr","Contact: registrar@192.168.1.2:5060;msil o=yes\r\n") modparam("msilo","content_type_hdr","Content-Type: text/plain\r\n") modparam("msilo","offline_message","*** User $rU is offline!") # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 15 ) modparam("tm", "wt_timer", 10 ) route{ if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); exit; }; if (uri==myself) { { # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { save("location"); log("REGISTER received -> dumping messages with MSILO\n"); # MSILO - dumping user's offline messages if (m_dump()) { log("MSILO: offline messages dumped - if they were\n"); }else{ log("MSILO: no offline messages dumped\n"); }; exit; }; # domestic SIP destinations are handled using our USRLOC DB if(!lookup("location")) { if (! t_newtran()) { sl_reply_error(); exit; }; # we do not care about anything else but MESSAGEs if (!method=="MESSAGE") { if (!t_reply("404", "Not found")) { sl_reply_error(); }; exit; }; log("MESSAGE received -> storing using MSILO\n"); # MSILO - storing as offline message if (m_store("$ru")) { log("MSILO: offline message stored\n"); if (!t_reply("202", "Accepted")) { sl_reply_error(); }; }else{ log("MSILO: offline message NOT stored\n"); if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; exit; }; # if the downstream UA does not support MESSAGE requests # go to failure_route[1] t_on_failure("1"); t_relay(); exit; }; # forward anything else t_relay(); } failure_route[1] { # forwarding failed -- check if the request was a MESSAGE if (!method=="MESSAGE") { exit; }; log(1,"MSILO:the downstream UA doesn't support MESSAGEs\n"); # we have changed the R-URI with the contact address, ignore it now if (m_store("$ou")) { log("MSILO: offline message stored\n"); t_reply("202", "Accepted"); }else{ log("MSILO: offline message NOT stored\n"); t_reply("503", "Service Unavailable"); }; } ... opensips-2.2.2/modules/msilo/doc/000077500000000000000000000000001300170765700167235ustar00rootroot00000000000000opensips-2.2.2/modules/msilo/doc/msilo.cfg000066400000000000000000000072451300170765700205370ustar00rootroot00000000000000# # MSILO usage example # # # running in debug mode (log level 4, log to stderr, stay in foreground) debug_mode=yes check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 listen=10.0.0.2 # listen address # ------------------ module loading ---------------------------------- mpath="/usr/local/lib/opensips/modules/" loadmodule "textops.so" loadmodule "sl.so" loadmodule "mysql.so" loadmodule "maxfwd.so" loadmodule "msilo.so" loadmodule "tm.so" loadmodule "registrar.so" loadmodule "usrloc.so" # ----------------- setting module-specific parameters --------------- # -- registrar params -- modparam("registrar", "default_expires", 120) # -- registrar params -- modparam("usrloc", "db_mode", 0) # -- msilo params -- modparam("msilo","db_url","mysql://opensips:opensipsrw@localhost/opensips") modparam("msilo","from_address","sip:registrar@opensips.org") modparam("msilo","contact_hdr","Contact: registrar@192.168.1.2:5060;msilo=yes\r\n") modparam("msilo","content_type_hdr","Content-Type: text/plain\r\n") modparam("msilo","offline_message","*** User $rU is offline!") # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 15 ) modparam("tm", "wt_timer", 10 ) route{ if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); exit; }; if (uri==myself) { { # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { save("location"); log("REGISTER received -> dumping messages with MSILO\n"); # MSILO - dumping user's offline messages if (m_dump()) { log("MSILO: offline messages dumped - if they were\n"); }else{ log("MSILO: no offline messages dumped\n"); }; exit; }; # domestic SIP destinations are handled using our USRLOC DB if(!lookup("location")) { if (! t_newtran()) { sl_reply_error(); exit; }; # we do not care about anything else but MESSAGEs if (!method=="MESSAGE") { if (!t_reply("404", "Not found")) { sl_reply_error(); }; exit; }; log("MESSAGE received -> storing using MSILO\n"); # MSILO - storing as offline message if (m_store("$ru")) { log("MSILO: offline message stored\n"); if (!t_reply("202", "Accepted")) { sl_reply_error(); }; }else{ log("MSILO: offline message NOT stored\n"); if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; exit; }; # if the downstream UA does not support MESSAGE requests # go to failure_route[1] t_on_failure("1"); t_relay(); exit; }; # forward anything else t_relay(); } failure_route[1] { # forwarding failed -- check if the request was a MESSAGE if (!method=="MESSAGE") { exit; }; log(1,"MSILO:the downstream UA doesn't support MESSAGEs\n"); # we have changed the R-URI with the contact address, ignore it now if (m_store("$ou")) { log("MSILO: offline message stored\n"); t_reply("202", "Accepted"); }else{ log("MSILO: offline message NOT stored\n"); t_reply("503", "Service Unavailable"); }; } opensips-2.2.2/modules/msilo/doc/msilo.xml000066400000000000000000000021141300170765700205660ustar00rootroot00000000000000 %docentities; ]> MSILO Module &osipsname; Daniel-Constantin Mierla miconda@gmail.com Daniel-Constantin Mierla miconda@gmail.com 2003 &fhg; $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/msilo/doc/msilo_admin.xml000066400000000000000000000454621300170765700217530ustar00rootroot00000000000000 &adminguide;
Overview This modules provides offline message storage for the &osipsname;. It stores received messages for an offline user and sends them when the user becomes online. For each message, the modules stores Request-URI (R-URI) only if it is a complete address of record (username@hostname), &uri; from To header, &uri; from From header, incoming time, expiration time, content type and body of the message. If R-URI is not an address of record (it might be the contact address for current &sip; session) the &uri; from To header will be used as R-URI. When the expiration time passed, the message is discarded from database. Expiration time is computed based on incoming time and one of the module's parameters. Every time when a user registers with &osips;, the module is looking in database for offline messages intended for that user. All of them will be sent to contact address provided in REGISTER request. It may happen the &sip; user to be registered but his &sip; User Agent to have no support for MESSAGE request. In this case it should be used the failure_route to store the undelivered requests. Another functionality provided by the modules is to send messages at a certain time -- the reminder functionality. Using config logic, a received message can be stored and delivered at a time specified while storing with the 'snd_time_avp'.
Dependencies
&osips; modules The following modules must be loaded before this module: database module - mysql, dbtext or other module that implements the db interface and provides support for storing/receiving data to/from a database system. TM--transaction module--is used to send &sip; requests.
External libraries or applications The following libraries or applications must be installed before running &osips; with this module: none.
Exported Parameters
<varname>db_url</varname> (string) Database &url;. Default value is &defaultdb;. Set the <quote>db_url</quote> parameter ... modparam("msilo", "db_url", "mysql://user:passwd@host.com/dbname") ...
<varname>db_table</varname> (string) The name of table where to store the messages. Default value is silo. Set the <quote>db_table</quote> parameter ... modparam("msilo", "db_table", "silo") ...
<varname>from_address</varname> (string) The &sip; address used to inform users that destination of their message is not online and the message will be delivered next time when that user goes online. If the parameter is not set, the module will not send any notification. It can contain pseudo-variables. Default value is NULL. Set the <quote>from_address</quote> parameter ... modparam("msilo", "from_address", "sip:registrar@example.org") modparam("msilo", "from_address", "sip:$rU@example.org") ...
<varname>contact_hdr</varname> (string) The value of the Contact header (including header name and ending \r\n) to be added in notification messages. It can contain pseudo-variables. Default value is NULL. Set the <quote>contact_hdr</quote> parameter ... modparam("msilo", "contact_hdr", "Contact: <sip:null@example.com>\r\n") ...
<varname>offline_message</varname> (string) The body of the notification message. It can contain pseudo-variables. Default value is NULL. Set the <quote>offline_message</quote> parameter ... modparam("msilo", "offline_message", "*** User $rU is offline!") modparam("msilo", "offline_message", "<em>I am offline!</em>") ...
<varname>content_type_hdr</varname> (string) The value of the Content-Type header (including header name and ending \r\n) to be added in notification messages. It must reflect what the 'offline_message' contains. It can contain pseudo-variables. Default value is NULL. Set the <quote>content_type_hdr</quote> parameter ... modparam("msilo", "content_type_hdr", "Content-Type: text/plain\r\n") modparam("msilo", "content_type_hdr", "Content-Type: text/html\r\n") ...
<varname>reminder</varname> (string) The &sip; address used to send reminder messages. If this value is not set, the reminder feature is disabled. Default value is NULL. Set the <quote>reminder</quote> parameter ... modparam("msilo", "reminder", "sip:registrar@example.org") ...
<varname>outbound_proxy</varname> (string) The &sip; address used as next hop when sending the message. Very useful when using OpenSIPS with a domain name not in DNS, or when using a separate OpenSIPS instance for msilo processing. If not set, the message will be sent to the address in destination URI. Default value is NULL. Set the <quote>outbound_proxy</quote> parameter ... modparam("msilo", "outbound_proxy", "sip:opensips.org;transport=tcp") ...
<varname>expire_time</varname> (int) Expire time of stored messages - seconds. When this time passed, the message is silently discarded from database. Default value is 259200 (72 hours = 3 days). Set the <quote>expire_time</quote> parameter ... modparam("msilo", "expire_time", 36000) ...
<varname>check_time</varname> (int) Timer interval to check if dumped messages are sent OK - seconds. The module keeps each request send by itself for a new online user and if the reply is 2xx then the message is deleted from database. Default value is 30. Set the <quote>check_time</quote> parameter ... modparam("msilo", "check_time", 10) ...
<varname>send_time</varname> (int) Timer interval in seconds to check if there are reminder messages. The module takes all reminder messages that must be sent at that moment or before that moment. If the value is 0, the reminder feature is disabled. Default value is 0. Set the <quote>send_time</quote> parameter ... modparam("msilo", "send_time", 60) ...
<varname>clean_period</varname> (int) Number of check_time cycles when to check if there are expired messages in database. Default value is 5. Set the <quote>clean_period</quote> parameter ... modparam("msilo", "clean_period", 3) ...
<varname>use_contact</varname> (int) Turns on/off the usage of the Contact address to send notification back to sender whose message is stored by MSILO. Default value is 1 (0 = off, 1 = on). Set the <quote>use_contact</quote> parameter ... modparam("msilo", "use_contact", 0) ...
<varname>sc_mid</varname> (string) The name of the column in silo table, storing message id. Default value is mid. Set the <quote>sc_mid</quote> parameter ... modparam("msilo", "sc_mid", "other_mid") ...
<varname>sc_from</varname> (string) The name of the column in silo table, storing the source address. Default value is src_addr. Set the <quote>sc_from</quote> parameter ... modparam("msilo", "sc_from", "source_address") ...
<varname>sc_to</varname> (string) The name of the column in silo table, storing the destination address. Default value is dst_addr. Set the <quote>sc_to</quote> parameter ... modparam("msilo", "sc_to", "destination_address") ...
<varname>sc_uri_user</varname> (string) The name of the column in silo table, storing the user name. Default value is username. Set the <quote>sc_uri_user</quote> parameter ... modparam("msilo", "sc_uri_user", "user") ...
<varname>sc_uri_host</varname> (string) The name of the column in silo table, storing the domain. Default value is domain. Set the <quote>sc_uri_host</quote> parameter ... modparam("msilo", "sc_uri_host", "domain") ...
<varname>sc_body</varname> (string) The name of the column storing the message body in silo table. Default value is body. Set the <quote>sc_body</quote> parameter ... modparam("msilo", "sc_body", "message_body") ...
<varname>sc_ctype</varname> (string) The name of the column in silo table, storing content type. Default value is ctype. Set the <quote>sc_ctype</quote> parameter ... modparam("msilo", "sc_ctype", "content_type") ...
<varname>sc_exp_time</varname> (string) The name of the column in silo table, storing the expire time of the message. Default value is exp_time. Set the <quote>sc_exp_time</quote> parameter ... modparam("msilo", "sc_exp_time", "expire_time") ...
<varname>sc_inc_time</varname> (string) The name of the column in silo table, storing the incoming time of the message. Default value is inc_time. Set the <quote>sc_inc_time</quote> parameter ... modparam("msilo", "sc_inc_time", "incoming_time") ...
<varname>sc_snd_time</varname> (string) The name of the column in silo table, storing the send time for the reminder. Default value is snd_time. Set the <quote>sc_snd_time</quote> parameter ... modparam("msilo", "sc_snd_time", "send_reminder_time") ...
<varname>snd_time_avp</varname> (str) The name of an AVP which may contain the time when to sent the received message as reminder.The AVP is used ony by m_store(). If the parameter is not set, the module does not look for this AVP. If the value is set to a valid AVP name, then the module expects in the AVP to be a time value in format YYYYMMDDHHMMSS (e.g., 20060101201500). Default value is null. Set the <quote>snd_time_avp</quote> parameter ... modparam("msilo", "snd_time_avp", "$avp(snd_time)") ...
<varname>add_date</varname> (int) Wheter to add as prefix the date when the message was stored. Default value is 1 (1==on/0==off). Set the <quote>add_date</quote> parameter ... modparam("msilo", "add_date", 0) ...
<varname>max_messages</varname> (int) Maximum number of stored message for an AoR. Value 0 equals to no limit. Default value is 0. Set the <quote>max_messages</quote> parameter ... modparam("msilo", "max_messages", 0) ...
Exported Functions
<function moreinfo="none">m_store([owner])</function> The method stores certain parts of the current &sip; request (it should be called when the request type is MESSAGE and the destination user is offline or his UA does not support MESSAGE requests). If the user is registered with a UA which does not support MESSAGE requests you should not use mode=0 if you have changed the request uri with the contact address of user's &ua;. Meaning of the parameters is as follows: owner - is a string that must contain a SIP URI in whose inbox the message will be stored. It can have any pseudo variable. If "owner" is missing, the SIP address is taken from R-URI. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>m_store</function> usage ... m_store(); m_store("$tu"); ...
<function moreinfo="none">m_dump([owner, [maxmsg]])</function> The method sends stored messages for the &sip; user that is going to register to his actual contact address. The method should be called when a REGISTER request is received and the Expire header has a value greater than zero. Meaning of the parameters is as follows: owner - is a string that must contain a SIP URI whose inbox will be dumped. It can have any pseudo variable. If "owner" is missing, the SIP address is taken from To URI. maxmsg - is a maximum number of messages to be dumped. This function can be used from REQUEST_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE <function>m_dump</function> usage ... m_dump(); m_dump("$fu"); m_dump("$fu", 10); ...
Exported Statistics
stored_messages The number of messages stored by msilo.
dumped_messages The number of dumped messages.
failed_messages The number of failed dumped messages.
dumped_reminders The number of dumped reminder messages.
failed_reminders The number of failed reminder messages.
Installation and Running
&osips; config file Next picture displays a sample usage of msilo. &osips; config script - sample msilo usage ... &msilocfg; ...
opensips-2.2.2/modules/msilo/ms_msg_list.c000066400000000000000000000121301300170765700206370ustar00rootroot00000000000000/** * MSILO module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 major locking changes: not it uses locking.h (andrei) */ #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "ms_msg_list.h" /** * create a new element */ msg_list_el msg_list_el_new(void) { msg_list_el mle = NULL; mle = (msg_list_el)shm_malloc(sizeof(t_msg_list_el)); if(mle == NULL) return NULL; mle->next = NULL; mle->prev = NULL; mle->msgid = 0; mle->flag = MS_MSG_NULL; return mle; } /** * free an element */ void msg_list_el_free(msg_list_el mle) { if(mle) shm_free(mle); } /** * free a list of elements */ void msg_list_el_free_all(msg_list_el mle) { msg_list_el p0, p1; if(!mle) return; p0 = mle; while(p0) { p1 = p0; p0 = p0->next; msg_list_el_free(p1); } } /** * init a list */ msg_list msg_list_init(void) { msg_list ml = NULL; ml = (msg_list)shm_malloc(sizeof(t_msg_list)); if(ml == NULL) return NULL; /* init locks */ if (lock_init(&ml->sem_sent)==0){ LM_CRIT("could not initialize a lock\n"); goto clean; }; if (lock_init(&ml->sem_done)==0){ LM_CRIT("could not initialize a lock\n"); lock_destroy(&ml->sem_sent); goto clean; }; ml->nrsent = 0; ml->nrdone = 0; ml->lsent = NULL; ml->ldone = NULL; return ml; clean: shm_free(ml); return NULL; } /** * free a list */ void msg_list_free(msg_list ml) { msg_list_el p0, p1; if(!ml) return; lock_destroy(&ml->sem_sent); lock_destroy(&ml->sem_done); if(ml->nrsent>0 && ml->lsent) { // free sent list p0 = ml->lsent; ml->lsent = NULL; ml->nrsent = 0; while(p0) { p1 = p0->next; msg_list_el_free(p0); p0 = p1; } } if(ml->nrdone>0 && ml->ldone) { // free done list p0 = ml->ldone; ml->ldone = NULL; ml->nrdone = 0; while(p0) { p1 = p0->next; msg_list_el_free(p0); p0 = p1; } } shm_free(ml); } /** * check if a message is in list */ int msg_list_check_msg(msg_list ml, int mid) { msg_list_el p0, p1; if(!ml || mid==0) goto errorx; LM_DBG("checking msgid=%d\n", mid); lock_get(&ml->sem_sent); p0 = p1 = ml->lsent; while(p0) { if(p0->msgid==mid) goto exist; p1 = p0; p0 = p0->next; } p0 = msg_list_el_new(); if(!p0) { LM_ERR("failed to create new msg elem.\n"); goto error; } p0->msgid = mid; p0->flag |= MS_MSG_SENT; if(p1) { p1->next = p0; p0->prev = p1; goto done; } ml->lsent = p0; done: ml->nrsent++; lock_release(&ml->sem_sent); LM_DBG("msg added to sent list.\n"); return MSG_LIST_OK; exist: lock_release(&ml->sem_sent); LM_DBG("msg already in sent list.\n"); return MSG_LIST_EXIST; error: lock_release(&ml->sem_sent); errorx: return MSG_LIST_ERR; } /** * set flag for message with mid */ int msg_list_set_flag(msg_list ml, int mid, int fl) { msg_list_el p0; if(ml==0 || mid==0) { LM_ERR("bad param %p / %d\n", ml, fl); goto errorx; } lock_get(&ml->sem_sent); p0 = ml->lsent; while(p0) { if(p0->msgid==mid) { p0->flag |= fl; LM_DBG("mid:%d fl:%d\n", p0->msgid, fl); goto done; } p0 = p0->next; } done: lock_release(&ml->sem_sent); return MSG_LIST_OK; errorx: return MSG_LIST_ERR; } /** * check if the messages from list were sent */ int msg_list_check(msg_list ml) { msg_list_el p0,p1; if(!ml) goto errorx; lock_get(&ml->sem_sent); if(ml->nrsent<=0) goto done; lock_get(&ml->sem_done); p0 = ml->lsent; while(p0) { if(p0->flag & MS_MSG_DONE || p0->flag & MS_MSG_ERRO) { LM_DBG("mid:%d got reply\n", p0->msgid); if(p0->prev) (p0->prev)->next = p0->next; else ml->lsent = p0->next; if(p0->next) (p0->next)->prev = p0->prev; ml->nrsent--; if(!ml->nrsent) ml->lsent = NULL; p1 = p0->next; if(ml->ldone) (ml->ldone)->prev = p0; p0->next = ml->ldone; p0->prev = NULL; ml->ldone = p0; ml->nrdone++; if (!p1) break; else { p0 = p1; continue; } } p0 = p0->next; } lock_release(&ml->sem_done); done: lock_release(&ml->sem_sent); return MSG_LIST_OK; errorx: return MSG_LIST_ERR; } /** * reset a list * return old list */ msg_list_el msg_list_reset(msg_list ml) { msg_list_el p0; if(!ml) return NULL; lock_get(&ml->sem_done); p0 = ml->ldone; ml->ldone = NULL; ml->nrdone = 0; lock_release(&ml->sem_done); return p0; } opensips-2.2.2/modules/msilo/ms_msg_list.h000066400000000000000000000035141300170765700206520ustar00rootroot00000000000000/** * MSILO module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 major locking changes, now it uses locking.h (andrei) */ #ifndef _MS_MSG_LIST_H_ #define _MS_MSG_LIST_H_ #include "../../locking.h" #define MS_MSG_NULL 0 #define MS_MSG_SENT 1 #define MS_MSG_DONE 4 #define MS_MSG_ERRO 8 #define MS_MSG_TSND 16 #define MS_SEM_SENT 0 #define MS_SEM_DONE 1 #define MSG_LIST_OK 0 #define MSG_LIST_ERR -1 #define MSG_LIST_EXIST 1 typedef struct _msg_list_el { int msgid; int flag; struct _msg_list_el * prev; struct _msg_list_el * next; } t_msg_list_el, *msg_list_el; typedef struct _msg_list { int nrsent; int nrdone; msg_list_el lsent; msg_list_el ldone; gen_lock_t sem_sent; gen_lock_t sem_done; } t_msg_list, *msg_list; msg_list_el msg_list_el_new(); void msg_list_el_free(msg_list_el); void msg_list_el_free_all(msg_list_el); msg_list msg_list_init(); void msg_list_free(msg_list); int msg_list_check_msg(msg_list, int); int msg_list_set_flag(msg_list, int, int); int msg_list_check(msg_list); msg_list_el msg_list_reset(msg_list); #endif opensips-2.2.2/modules/msilo/msfuncs.c000066400000000000000000000215331300170765700200040ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "msfuncs.h" #include #include #include #include #include #include "../../dprint.h" #include "../../config.h" #include "../../ut.h" #include "../../forward.h" #include "../../resolve.h" #include "../../globals.h" #include "../../pt.h" #define CONTACT_PREFIX "Contact: <" #define CONTACT_SUFFIX ">;msilo=yes"CRLF #define CONTACT_PREFIX_LEN (sizeof(CONTACT_PREFIX)-1) #define CONTACT_SUFFIX_LEN (sizeof(CONTACT_SUFFIX)-1) extern int ms_add_date; /** * apostrophes escaping * - src: source buffer * - slen: length of source buffer * - dst: destination buffer * - dlen: max length of destination buffer * return: destination length => OK; -1 => error */ int m_apo_escape(char* src, int slen, char* dst, int dlen) { int i, j; if(!src || !dst || dlen <= 0) return -1; if(slen == -1) slen = strlen(src); for(i=j=0; i=dlen) return -2; memcpy(&dst[j], "\\'", 2); j += 2; break; default: if(j+1>=dlen) return -2; dst[j] = src[i]; j++; } } dst[j] = '\0'; return j; } /** * Build a RFC 3261 compliant Date string from a time_t value * - date: input of time_t to build the string from * - buf: pointer to string for output * - bufLen: length of buf param * * return: >0 length of data copied to buf ; <0 error occurred */ int timetToSipDateStr(time_t date, char* buf, int bufLen) { struct tm *gmt; char* dayArray[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; char* monthArray[12] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; int len = 0; gmt = gmtime(&date); /* In RFC 3261 the format is always GMT and in the string form like * "Wkday, Day Month Year HOUR:MIN:SEC GMT" * "Mon, 19 Feb 2007 18:42:27 GMT" */ len = snprintf(buf,bufLen,"Date: %s, %02d %s %d %02d:%02d:%02d GMT\r\n", dayArray[gmt->tm_wday], gmt->tm_mday, monthArray[gmt->tm_mon], 1900 + gmt->tm_year, gmt->tm_hour, gmt->tm_min, gmt->tm_sec ); /* snprintf returns number of chars it should have printed, so you * need to bounds check against input*/ return (len > bufLen) ? bufLen : len; } /** * extract the value of Content-Type header * - src: pointer to C-T content * - len: length of src * - ctype: parsed C-T * - flag: what to parse - bit mask of CT_TYPE, CT_CHARSET, CT_MSGR * * return: 0 OK ; -1 error */ int m_extract_content_type(char* src, int len, content_type_t* ctype, int flag) { char *p, *end; int f = 0; if( !src || len <=0 ) goto error; p = src; end = p + len; while((p < end) && (f != flag)) { while((p < end) && (*p==' ' || *p=='\t')) p++; if(p >= end) goto done; if((flag & CT_TYPE) && !(f & CT_TYPE)) { ctype->type.s = p; while(p < end && *p!=' ' && *p!='\t' && *p!='\0' && *p!=';' && *p!='\r' && *p!='\n') p++; LM_DBG("content-type found\n"); f |= CT_TYPE; ctype->type.len = p - ctype->type.s; if(f == flag) { return 0; } else { p++; continue; } } else { if((flag & CT_CHARSET) && !(f & CT_CHARSET)) { return -1; } else { if((flag & CT_MSGR) && !(f & CT_MSGR)) { return -1; } else { return 0; } } } } done: if(f==flag) return 0; else return -1; error: LM_DBG("error\n"); return -1; } /** build MESSAGE headers * * Add Content-Type, Contact and Date headers if they exist * expects - max buf len of the resulted body in body->len * - body->s MUST be allocated * return: 0 OK ; -1 error * */ int m_build_headers(str *buf, str ctype, str contact, time_t date) { char *p; char strDate[48]; int lenDate = 0; if(!buf || !buf->s || buf->len <= 0 || ctype.len < 0 || contact.len < 0 || buf->len <= ctype.len+contact.len+14 /*Content-Type: */ +CRLF_LEN+CONTACT_PREFIX_LEN+CONTACT_SUFFIX_LEN) goto error; p = buf->s; if(date > 0) { lenDate = timetToSipDateStr(date,strDate,48); strncpy(p, strDate, lenDate); p += lenDate; } if(ctype.len > 0) { strncpy(p, "Content-Type: ", 14); p += 14; strncpy(p, ctype.s, ctype.len); p += ctype.len; strncpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; } if(contact.len > 0) { strncpy(p, CONTACT_PREFIX, CONTACT_PREFIX_LEN); p += CONTACT_PREFIX_LEN; strncpy(p, contact.s, contact.len); p += contact.len; strncpy(p, CONTACT_SUFFIX, CONTACT_SUFFIX_LEN); p += CONTACT_SUFFIX_LEN; } buf->len = p - buf->s; return 0; error: return -1; } /** build MESSAGE body --- add incoming time and 'from' * * expects - max buf len of the resulted body in body->len * - body->s MUST be allocated * return: 0 OK ; -1 error * */ int m_build_body(str *body, time_t date, str msg, time_t sdate) { char *p; if(!body || !(body->s) || body->len <= 0 || msg.len <= 0 || date < 0 || msg.len < 0 || (46+msg.len > body->len) ) goto error; p = body->s; if(ms_add_date!=0) { if(sdate!=0) { strncpy(p, "[Reminder message - ", 20); p += 20; strncpy(p, ctime(&sdate), 24); p += 24; *p++ = ']'; } else { strncpy(p, "[Offline message - ", 19); p += 19; strncpy(p, ctime(&date), 24); p += 24; *p++ = ']'; } *p++ = ' '; } memcpy(p, msg.s, msg.len); p += msg.len; body->len = p - body->s; return 0; error: return -1; } /* return time stamp of YYYYMMDDHHMMSS */ int ms_extract_time(str *time_str, int *time_val) { struct tm stm; int i; if(time_str==NULL || time_str->s==NULL || time_str->len<=0 || time_val==NULL) { LM_ERR("bad parameters\n"); return -1; } memset(&stm, 0, sizeof(struct tm)); for(i=0; ilen; i++) { if(time_str->s[i]<'0' || time_str->s[i]>'9') { LM_ERR("bad time [%.*s]\n", time_str->len, time_str->s); return -1; } switch(i) { case 0: if(time_str->s[i]<'2') { LM_ERR("bad year in time [%.*s]\n", time_str->len, time_str->s); return -1; } stm.tm_year += 1000*(time_str->s[i]-'0') - 1900; break; case 1: stm.tm_year += 100*(time_str->s[i]-'0'); break; case 2: stm.tm_year += 10*(time_str->s[i]-'0'); break; case 3: stm.tm_year += (time_str->s[i]-'0'); break; case 4: if(time_str->s[i]>'1') { LM_ERR("bad month in time[%.*s]\n", time_str->len, time_str->s); return -1; } stm.tm_mon += 10*(time_str->s[i]-'0') - 1; break; case 5: if((time_str->s[i-1]=='0' && time_str->s[i]=='0') || (time_str->s[i-1]=='1' && time_str->s[i]>'2')) { LM_ERR("bad month in time[%.*s]\n", time_str->len, time_str->s); return -1; } stm.tm_mon += (time_str->s[i]-'0'); break; case 6: if(time_str->s[i]>'3') { LM_ERR("bad day in time [%.*s]\n", time_str->len, time_str->s); return -1; } stm.tm_mday += 10*(time_str->s[i]-'0'); break; case 7: if((time_str->s[i-1]=='0' && time_str->s[i]=='0') || (time_str->s[i-1]=='3' && time_str->s[i]>'1')) { LM_ERR("bad day in time [%.*s]\n", time_str->len, time_str->s); return -1; } stm.tm_mday += (time_str->s[i]-'0'); break; case 8: if(time_str->s[i]>'2') { LM_ERR("bad hour in time [%.*s]\n", time_str->len, time_str->s); return -1; } stm.tm_hour += 10*(time_str->s[i]-'0'); break; case 9: if(time_str->s[i-1]=='2' && time_str->s[i]>'3') { LM_ERR("bad hour in time [%.*s]\n", time_str->len, time_str->s); return -1; } stm.tm_hour += (time_str->s[i]-'0'); break; case 10: if(time_str->s[i]>'5') { LM_ERR("bad min in time [%.*s]\n", time_str->len, time_str->s); return -1; } stm.tm_min += 10*(time_str->s[i]-'0'); break; case 11: stm.tm_min += (time_str->s[i]-'0'); break; case 12: if(time_str->s[i]>'5') { LM_ERR("bad sec in time [%.*s]\n", time_str->len, time_str->s); return -1; } stm.tm_sec += 10*(time_str->s[i]-'0'); break; case 13: stm.tm_sec += (time_str->s[i]-'0'); break; default: LM_ERR("time spec too long [%.*s]\n", time_str->len, time_str->s); return -1; } } *time_val = (int)mktime(&stm); return 0; } opensips-2.2.2/modules/msilo/msfuncs.h000066400000000000000000000030401300170765700200020ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MSFUNCS_H_ #define _MSFUNCS_H_ #include #include "../../str.h" #define CT_TYPE 1 #define CT_CHARSET 2 #define CT_MSGR 4 #ifdef MSILO_TAG #undef MSILO_TAG #endif #define MSILO_TAG "msilo-HI4U-Ah0X-bZ98-" typedef struct _content_type { str type; str charset; str msgr; } content_type_t; /** apostrophes escape - useful for MySQL strings */ int m_apo_escape(char*, int, char*, int); /** extract content-type value */ int m_extract_content_type(char*, int, content_type_t*, int); /** build MESSAGE headers */ int m_build_headers(str *buf, str ctype, str contact, time_t date); /** build MESSAGE body */ int m_build_body(str *body, time_t date, str msg, time_t sdate); int ms_extract_time(str *time_str, int *time_val); #endif opensips-2.2.2/modules/msilo/msilo.c000066400000000000000000001070731300170765700174550ustar00rootroot00000000000000/* * MSILO module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * * 2003-01-23: switched from t_uac to t_uac_dlg (dcm) * 2003-02-28: protocolization of t_uac_dlg completed (jiri) * 2003-03-11: updated to the new module interface (andrei) * removed non-constant initializers to some strs (andrei) * 2003-03-16: flags parameter added (janakj) * 2003-04-05: default_uri #define used (jiri) * 2003-04-06: db_init removed from mod_init, will be called from child_init * now (janakj) * 2003-04-07: m_dump takes a parameter which sets the way the outgoing URI * is computed (dcm) * 2003-08-05 adapted to the new parse_content_type_hdr function (bogdan) * 2004-06-07 updated to the new DB api (andrei) * 2006-09-10 m_dump now checks if registering UA supports MESSAGE method (jh) * 2006-10-05 added max_messages module variable (jh) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../ut.h" #include "../../timer.h" #include "../../mem/shm_mem.h" #include "../../db/db.h" #include "../../parser/parse_from.h" #include "../../parser/parse_content.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_allow.h" #include "../../parser/parse_methods.h" #include "../../resolve.h" #include "../../usr_avp.h" #include "../../mod_fix.h" #include "../tm/tm_load.h" #include "ms_msg_list.h" #include "msfuncs.h" #define MAX_DEL_KEYS 1 #define NR_KEYS 10 static str sc_mid = str_init("id"); /* 0 */ static str sc_from = str_init("src_addr"); /* 1 */ static str sc_to = str_init("dst_addr"); /* 2 */ static str sc_uri_user = str_init("username"); /* 3 */ static str sc_uri_host = str_init("domain"); /* 4 */ static str sc_body = str_init("body"); /* 5 */ static str sc_ctype = str_init("ctype"); /* 6 */ static str sc_exp_time = str_init("exp_time"); /* 7 */ static str sc_inc_time = str_init("inc_time"); /* 8 */ static str sc_snd_time = str_init("snd_time"); /* 9 */ #define SET_STR_VAL(_str, _res, _r, _c) \ if (RES_ROWS(_res)[_r].values[_c].nul == 0) \ { \ switch(RES_ROWS(_res)[_r].values[_c].type) \ { \ case DB_STRING: \ (_str).s=(char*)RES_ROWS(_res)[_r].values[_c].val.string_val; \ (_str).len=strlen((_str).s); \ break; \ case DB_STR: \ (_str).len=RES_ROWS(_res)[_r].values[_c].val.str_val.len; \ (_str).s=(char*)RES_ROWS(_res)[_r].values[_c].val.str_val.s; \ break; \ case DB_BLOB: \ (_str).len=RES_ROWS(_res)[_r].values[_c].val.blob_val.len; \ (_str).s=(char*)RES_ROWS(_res)[_r].values[_c].val.blob_val.s; \ break; \ default: \ (_str).len=0; \ (_str).s=NULL; \ } \ } #define S_TABLE_VERSION 6 /** database connection */ static db_con_t *db_con = NULL; static db_func_t msilo_dbf; /** precessed msg list - used for dumping the messages */ msg_list ml = NULL; /** TM bind */ struct tm_binds tmb; /** parameters */ static str ms_db_url = {NULL, 0}; static str ms_db_table = str_init("silo"); str ms_reminder = {NULL, 0}; str ms_outbound_proxy = {NULL, 0}; char* ms_from = NULL; /*"sip:registrar@example.org";*/ char* ms_contact = NULL; /*"Contact: \r\n";*/ char* ms_content_type = NULL; /*"Content-Type: text/plain\r\n";*/ char* ms_offline_message = NULL; /*"I'm offline."*/ void** ms_from_sp = NULL; void** ms_contact_sp = NULL; void** ms_content_type_sp = NULL; void** ms_offline_message_sp = NULL; int ms_expire_time = 259200; int ms_check_time = 60; int ms_send_time = 0; int ms_clean_period = 10; int ms_use_contact = 1; int ms_add_date = 1; int ms_max_messages = 0; static str ms_snd_time_avp_param = {NULL, 0}; int ms_snd_time_avp_name = -1; unsigned short ms_snd_time_avp_type; str msg_type = str_init("MESSAGE"); /** module functions */ static int mod_init(void); static int child_init(int); static int m_store(struct sip_msg*, char*, char*); static int m_dump(struct sip_msg*, char*, char*); void destroy(void); void m_clean_silo(unsigned int ticks, void *); void m_send_ontimer(unsigned int ticks, void *); int ms_reset_stime(int mid); int check_message_support(struct sip_msg* msg); /** TM callback function */ static void m_tm_callback( struct cell *t, int type, struct tmcb_params *ps); /* commands wrappers and fixups */ static int fixup_m_dump(void** param, int param_no); static cmd_export_t cmds[]={ {"m_store", (cmd_function)m_store, 0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"m_store", (cmd_function)m_store, 1, fixup_spve_null, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"m_dump", (cmd_function)m_dump, 0, 0, 0, REQUEST_ROUTE | STARTUP_ROUTE | TIMER_ROUTE | EVENT_ROUTE}, {"m_dump", (cmd_function)m_dump, 1, fixup_spve_null, 0, REQUEST_ROUTE | STARTUP_ROUTE | TIMER_ROUTE | EVENT_ROUTE}, {"m_dump", (cmd_function)m_dump, 2, fixup_m_dump, 0, REQUEST_ROUTE | STARTUP_ROUTE | TIMER_ROUTE | EVENT_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[]={ { "db_url", STR_PARAM, &ms_db_url.s }, { "db_table", STR_PARAM, &ms_db_table.s }, { "from_address", STR_PARAM, &ms_from }, { "contact_hdr", STR_PARAM, &ms_contact }, { "content_type_hdr", STR_PARAM, &ms_content_type }, { "offline_message", STR_PARAM, &ms_offline_message }, { "reminder", STR_PARAM, &ms_reminder.s }, { "outbound_proxy", STR_PARAM, &ms_outbound_proxy.s }, { "expire_time", INT_PARAM, &ms_expire_time }, { "check_time", INT_PARAM, &ms_check_time }, { "send_time", INT_PARAM, &ms_send_time }, { "clean_period", INT_PARAM, &ms_clean_period }, { "use_contact", INT_PARAM, &ms_use_contact }, { "sc_mid", STR_PARAM, &sc_mid.s }, { "sc_from", STR_PARAM, &sc_from.s }, { "sc_to", STR_PARAM, &sc_to.s }, { "sc_uri_user", STR_PARAM, &sc_uri_user.s }, { "sc_uri_host", STR_PARAM, &sc_uri_host.s }, { "sc_body", STR_PARAM, &sc_body.s }, { "sc_ctype", STR_PARAM, &sc_ctype.s }, { "sc_exp_time", STR_PARAM, &sc_exp_time.s }, { "sc_inc_time", STR_PARAM, &sc_inc_time.s }, { "sc_snd_time", STR_PARAM, &sc_snd_time.s }, { "snd_time_avp", STR_PARAM, &ms_snd_time_avp_param.s }, { "add_date", INT_PARAM, &ms_add_date }, { "max_messages", INT_PARAM, &ms_max_messages }, { 0,0,0 } }; #ifdef STATISTICS #include "../../statistics.h" stat_var* ms_stored_msgs; stat_var* ms_dumped_msgs; stat_var* ms_failed_msgs; stat_var* ms_dumped_rmds; stat_var* ms_failed_rmds; static stat_export_t msilo_stats[] = { {"stored_messages" , 0, &ms_stored_msgs }, {"dumped_messages" , 0, &ms_dumped_msgs }, {"failed_messages" , 0, &ms_failed_msgs }, {"dumped_reminders" , 0, &ms_dumped_rmds }, {"failed_reminders" , 0, &ms_failed_rmds }, {0,0,0} }; #endif static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "msilo", /* module id */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* module's exported functions */ 0, /* module's exported async functions */ params, /* module's exported parameters */ #ifdef STATISTICS msilo_stats, #else 0, /* exported statistics */ #endif 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handler */ (destroy_function) destroy, /* module destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { pv_spec_t avp_spec; init_db_url( ms_db_url , 0 /*cannot be null*/); ms_db_table.len = strlen (ms_db_table.s); sc_mid.len = strlen(sc_mid.s); sc_from.len = strlen(sc_from.s); sc_to.len = strlen(sc_to.s); sc_uri_user.len = strlen(sc_uri_user.s); sc_uri_host.len = strlen(sc_uri_host.s); sc_body.len = strlen(sc_body.s); sc_ctype.len = strlen(sc_ctype.s); sc_exp_time.len = strlen(sc_exp_time.s); sc_inc_time.len = strlen(sc_inc_time.s); sc_snd_time.len = strlen(sc_snd_time.s); if (ms_snd_time_avp_param.s) ms_snd_time_avp_param.len = strlen(ms_snd_time_avp_param.s); LM_DBG("initializing ...\n"); /* binding to mysql module */ if (db_bind_mod(&ms_db_url, &msilo_dbf)) { LM_DBG("database module not found\n"); return -1; } if (!DB_CAPABILITY(msilo_dbf, DB_CAP_ALL)) { LM_ERR("database module does not implement " "all functions needed by the module\n"); return -1; } if (ms_snd_time_avp_param.s && ms_snd_time_avp_param.len > 0) { if (pv_parse_spec(&ms_snd_time_avp_param, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %.*s AVP definition\n", ms_snd_time_avp_param.len, ms_snd_time_avp_param.s); return -1; } if(pv_get_avp_name(0, &(avp_spec.pvp), &ms_snd_time_avp_name, &ms_snd_time_avp_type)!=0) { LM_ERR("[%.*s]- invalid AVP definition\n", ms_snd_time_avp_param.len, ms_snd_time_avp_param.s); return -1; } } db_con = msilo_dbf.init(&ms_db_url); if (!db_con) { LM_ERR("failed to connect to the database\n"); return -1; } if(db_check_table_version(&msilo_dbf, db_con, &ms_db_table, S_TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); return -1; } if(db_con) msilo_dbf.close(db_con); db_con = NULL; /* load the TM API */ if (load_tm_api(&tmb)!=0) { LM_ERR("can't load TM API\n"); return -1; } if(ms_from!=NULL) { ms_from_sp = (void**)pkg_malloc(sizeof(void*)); if(ms_from_sp==NULL) { LM_ERR("no more pkg\n"); return -1; } *ms_from_sp = (void*)ms_from; if(fixup_spve_null(ms_from_sp, 1)!=0) { LM_ERR("bad contact parameter\n"); return -1; } } if(ms_contact!=NULL) { ms_contact_sp = (void**)pkg_malloc(sizeof(void*)); if(ms_contact_sp==NULL) { LM_ERR("no more pkg\n"); return -1; } *ms_contact_sp = (void*)ms_contact; if(fixup_spve_null(ms_contact_sp, 1)!=0) { LM_ERR("bad contact parameter\n"); return -1; } } if(ms_content_type!=NULL) { ms_content_type_sp = (void**)pkg_malloc(sizeof(void*)); if(ms_content_type_sp==NULL) { LM_ERR("no more pkg\n"); return -1; } *ms_content_type_sp = (void*)ms_content_type; if(fixup_spve_null(ms_content_type_sp, 1)!=0) { LM_ERR("bad content_type parameter\n"); return -1; } } if(ms_offline_message!=NULL) { ms_offline_message_sp = (void**)pkg_malloc(sizeof(void*)); if(ms_offline_message_sp==NULL) { LM_ERR("no more pkg\n"); return -1; } *ms_offline_message_sp = (void*)ms_offline_message; if(fixup_spve_null(ms_offline_message_sp, 1)!=0) { LM_ERR("bad offline_message parameter\n"); return -1; } } if(ms_offline_message!=NULL && ms_content_type==NULL) { LM_ERR("content_type parameter must be set\n"); return -1; } ml = msg_list_init(); if(ml==NULL) { LM_ERR("can't initialize msg list\n"); return -1; } if(ms_check_time<0) { LM_ERR("bad check time value\n"); return -1; } register_timer( "msilo-clean", m_clean_silo, 0, ms_check_time, TIMER_FLAG_SKIP_ON_DELAY); if(ms_send_time>0 && ms_reminder.s!=NULL) register_timer( "msilo-reminder", m_send_ontimer, 0, ms_send_time,TIMER_FLAG_DELAY_ON_DELAY); if(ms_reminder.s!=NULL) ms_reminder.len = strlen(ms_reminder.s); if(ms_outbound_proxy.s!=NULL) ms_outbound_proxy.len = strlen(ms_outbound_proxy.s); return 0; } /** * Initialize children */ static int child_init(int rank) { LM_DBG("rank #%d / pid <%d>\n", rank, getpid()); if (msilo_dbf.init==0) { LM_CRIT("database not bound\n"); return -1; } db_con = msilo_dbf.init(&ms_db_url); if (!db_con) { LM_ERR("child %d: failed to connect database\n", rank); return -1; } else { if (msilo_dbf.use_table(db_con, &ms_db_table) < 0) { LM_ERR("child %d: failed in use_table\n", rank); return -1; } LM_DBG("#%d database connection opened successfully\n", rank); } return 0; } /** * store message * mode = "0" -- look for outgoing URI starting with new_uri * = "1" -- look for outgoing URI starting with r-uri * = "2" -- look for outgoing URI only at to header */ static int m_store(struct sip_msg* msg, char* owner, char* s2) { str body, str_hdr, ctaddr; struct to_body *pto, *pfrom; struct sip_uri puri; str duri, owner_s; db_key_t db_keys[NR_KEYS-1]; db_val_t db_vals[NR_KEYS-1]; db_key_t db_cols[1]; db_res_t* res = NULL; int nr_keys = 0, val, lexpire; #define MS_BUF1_SIZE 1024 static char ms_buf1[MS_BUF1_SIZE]; str notify_from; str notify_body; str notify_ctype; str notify_contact; int_str avp_value; struct usr_avp *avp; /* get message body - after that whole SIP MESSAGE is parsed */ if ( get_body( msg, &body)!=0 ) { LM_ERR("cannot extract body from msg\n"); goto error; } /* missing body is not an error here as we can have * requests with external bodies (refered from content-type hdr) */ /* get TO URI */ if(!msg->to || !msg->to->body.s) { LM_ERR("cannot find 'to' header!\n"); goto error; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); goto error; } /* get the owner */ memset(&puri, 0, sizeof(struct sip_uri)); if(owner) { if(fixup_get_svalue(msg, (gparam_p)owner, &owner_s)!=0) { LM_ERR("invalid owner uri parameter"); return -1; } if(parse_uri(owner_s.s, owner_s.len, &puri)!=0) { LM_ERR("bad owner SIP address!\n"); goto error; } else { LM_DBG("using user id [%.*s]\n", owner_s.len, owner_s.s); } } else { /* get it from R-URI */ if(msg->new_uri.len <= 0) { if(msg->first_line.u.request.uri.len <= 0) { LM_ERR("bad dst URI!\n"); goto error; } duri = msg->first_line.u.request.uri; } else { duri = msg->new_uri; } LM_DBG("NEW R-URI found - check if is AoR!\n"); if(parse_uri(duri.s, duri.len, &puri)!=0) { LM_ERR("bad dst R-URI!!\n"); goto error; } } if(puri.user.len<=0) { LM_ERR("no username for owner\n"); goto error; } db_keys[nr_keys] = &sc_uri_user; db_vals[nr_keys].type = DB_STR; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.str_val.s = puri.user.s; db_vals[nr_keys].val.str_val.len = puri.user.len; nr_keys++; db_keys[nr_keys] = &sc_uri_host; db_vals[nr_keys].type = DB_STR; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.str_val.s = puri.host.s; db_vals[nr_keys].val.str_val.len = puri.host.len; nr_keys++; if (msilo_dbf.use_table(db_con, &ms_db_table) < 0) { LM_ERR("failed to use_table\n"); goto error; } if (ms_max_messages > 0) { db_cols[0] = &sc_inc_time; if (msilo_dbf.query(db_con, db_keys, 0, db_vals, db_cols, 2, 1, 0, &res) < 0 ) { LM_ERR("failed to query the database\n"); return -1; } if (RES_ROW_N(res) >= ms_max_messages) { LM_ERR("too many messages for AoR '%.*s@%.*s'\n", puri.user.len, puri.user.s, puri.host.len, puri.host.s); msilo_dbf.free_result(db_con, res); return -1; } msilo_dbf.free_result(db_con, res); } /* Set To key */ db_keys[nr_keys] = &sc_to; db_vals[nr_keys].type = DB_STR; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.str_val.s = pto->uri.s; db_vals[nr_keys].val.str_val.len = pto->uri.len; nr_keys++; /* check FROM URI */ if(!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); goto error; } if(msg->from->parsed == NULL) { LM_DBG("'From' header not parsed\n"); /* parsing from header */ if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); goto error; } } pfrom = (struct to_body*)msg->from->parsed; LM_DBG("'From' header: <%.*s>\n", pfrom->uri.len, pfrom->uri.s); db_keys[nr_keys] = &sc_from; db_vals[nr_keys].type = DB_STR; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.str_val.s = pfrom->uri.s; db_vals[nr_keys].val.str_val.len = pfrom->uri.len; nr_keys++; /* add the message's body in SQL query */ db_keys[nr_keys] = &sc_body; /* insert NULL value is body was found empty */ db_vals[nr_keys].type = DB_BLOB; db_vals[nr_keys].nul = body.len?0:1; db_vals[nr_keys].val.blob_val.s = body.s; db_vals[nr_keys].val.blob_val.len = body.len; nr_keys++; /* add 'content-type' header (already found) */ if (msg->content_type==0) { LM_ERR("missing Content-Type header\n"); goto error; } db_keys[nr_keys] = &sc_ctype; db_vals[nr_keys].type = DB_STR; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.str_val = msg->content_type->body; nr_keys++; /* check 'expires' -- no more parsing - already done by get_body() */ lexpire = ms_expire_time; if(msg->expires && msg->expires->body.len > 0) { LM_DBG("'expires' found\n"); val = atoi(msg->expires->body.s); if(val > 0) lexpire = (ms_expire_time<=val)?ms_expire_time:val; } /* current time */ val = (int)time(NULL); /* add expiration time */ db_keys[nr_keys] = &sc_exp_time; db_vals[nr_keys].type = DB_INT; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.int_val = val+lexpire; nr_keys++; /* add incoming time */ db_keys[nr_keys] = &sc_inc_time; db_vals[nr_keys].type = DB_INT; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.int_val = val; nr_keys++; /* add sending time */ db_keys[nr_keys] = &sc_snd_time; db_vals[nr_keys].type = DB_INT; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.int_val = 0; if(ms_snd_time_avp_name >= 0) { avp = NULL; avp=search_first_avp(ms_snd_time_avp_type, ms_snd_time_avp_name, &avp_value, 0); if(avp!=NULL && is_avp_str_val(avp)) { if(ms_extract_time(&avp_value.s, &db_vals[nr_keys].val.int_val)!=0) db_vals[nr_keys].val.int_val = 0; } } nr_keys++; if(msilo_dbf.insert(db_con, db_keys, db_vals, nr_keys) < 0) { LM_ERR("failed to store message\n"); goto error; } LM_DBG("message stored. T:<%.*s> F:<%.*s>\n", pto->uri.len, pto->uri.s, pfrom->uri.len, pfrom->uri.s); #ifdef STATISTICS update_stat(ms_stored_msgs, 1); #endif if(ms_from==NULL || ms_offline_message == NULL) goto done; LM_DBG("sending info message.\n"); if(fixup_get_svalue(msg, (gparam_p)*ms_from_sp, ¬ify_from)!=0 || notify_from.len<=0) { LM_WARN("cannot get notification From address\n"); goto done; } if(fixup_get_svalue(msg, (gparam_p)*ms_offline_message_sp, ¬ify_body)!=0 || notify_body.len<=0) { LM_WARN("cannot get notification body\n"); goto done; } if(fixup_get_svalue(msg, (gparam_p)*ms_content_type_sp, ¬ify_ctype)!=0 || notify_ctype.len<=0) { LM_WARN("cannot get notification content type\n"); goto done; } if(ms_contact!=NULL && fixup_get_svalue(msg, (gparam_p)*ms_contact_sp, ¬ify_contact)==0 && notify_contact.len>0) { if(notify_contact.len+notify_ctype.len>=MS_BUF1_SIZE) { LM_WARN("insufficient buffer to build notification headers\n"); goto done; } memcpy(ms_buf1, notify_contact.s, notify_contact.len); memcpy(ms_buf1+notify_contact.len, notify_ctype.s, notify_ctype.len); str_hdr.s = ms_buf1; str_hdr.len = notify_contact.len + notify_ctype.len; } else { str_hdr = notify_ctype; } /* look for Contact header -- must be parsed by now*/ ctaddr.s = NULL; if(ms_use_contact && msg->contact!=NULL && msg->contact->body.s!=NULL && msg->contact->body.len > 0) { LM_DBG("contact header found\n"); if((msg->contact->parsed!=NULL && ((contact_body_t*)(msg->contact->parsed))->contacts!=NULL) || (parse_contact(msg->contact)==0 && msg->contact->parsed!=NULL && ((contact_body_t*)(msg->contact->parsed))->contacts!=NULL)) { LM_DBG("using contact header for info msg\n"); ctaddr.s = ((contact_body_t*)(msg->contact->parsed))->contacts->uri.s; ctaddr.len = ((contact_body_t*)(msg->contact->parsed))->contacts->uri.len; if(!ctaddr.s || ctaddr.len < 6 || strncasecmp(ctaddr.s, "sip:", 4) || ctaddr.s[4]==' ') ctaddr.s = NULL; else LM_DBG("feedback contact [%.*s]\n", ctaddr.len,ctaddr.s); } } tmb.t_request(&msg_type, /* Type of the message */ (ctaddr.s)?&ctaddr:&pfrom->uri, /* Request-URI */ &pfrom->uri, /* To */ ¬ify_from, /* From */ &str_hdr, /* Optional headers including CRLF */ ¬ify_body, /* Message body */ (ms_outbound_proxy.s)?&ms_outbound_proxy:0, /* outbound uri */ NULL, /* Callback function */ NULL, /* Callback parameter */ NULL ); done: return 1; error: return -1; } /** * dump message */ static int m_dump(struct sip_msg* msg, char* owner, char* maxmsg) { struct to_body *pto = NULL; db_key_t db_keys[3]; db_key_t ob_key; db_op_t db_ops[3]; db_val_t db_vals[3]; db_key_t db_cols[6]; db_res_t* db_res = NULL; int i, db_no_cols = 6, db_no_keys = 3, mid, n; int sent_cnt = 0; int maxmsg_i; static char hdr_buf[1024]; static char body_buf[1024]; struct sip_uri puri; str owner_s; str str_vals[4], hdr_str , body_str; time_t rtime; /* init */ ob_key = &sc_mid; db_keys[0]=&sc_uri_user; db_keys[1]=&sc_uri_host; db_keys[2]=&sc_snd_time; db_ops[0]=OP_EQ; db_ops[1]=OP_EQ; db_ops[2]=OP_EQ; db_cols[0]=&sc_mid; db_cols[1]=&sc_from; db_cols[2]=&sc_to; db_cols[3]=&sc_body; db_cols[4]=&sc_ctype; db_cols[5]=&sc_inc_time; hdr_str.s=hdr_buf; hdr_str.len=1024; body_str.s=body_buf; body_str.len=1024; maxmsg_i = (int)(long)maxmsg; /* get the owner */ memset(&puri, 0, sizeof(struct sip_uri)); if(owner) { if(fixup_get_svalue(msg, (gparam_p)owner, &owner_s)!=0) { LM_ERR("invalid owner uri parameter"); return -1; } if(parse_uri(owner_s.s, owner_s.len, &puri)!=0) { LM_ERR("bad owner SIP address!\n"); goto error; } else { LM_DBG("using user id [%.*s]\n", owner_s.len, owner_s.s); } } else { /* get it from To URI */ /* check for TO header */ if(msg->to==NULL && (parse_headers(msg, HDR_TO_F, 0)==-1 || msg->to==NULL || msg->to->body.s==NULL)) { LM_ERR("cannot find TO HEADER!\n"); goto error; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); goto error; } if(parse_uri(pto->uri.s, pto->uri.len, &puri)!=0) { LM_ERR("bad owner To URI!\n"); goto error; } } if(puri.user.len<=0 || puri.user.s==NULL || puri.host.len<=0 || puri.host.s==NULL) { LM_ERR("bad owner URI!\n"); goto error; } if (msg->REQ_METHOD == METHOD_REGISTER) { /** * check if has expires=0 (REGISTER) */ if(parse_headers(msg, HDR_EXPIRES_F, 0) >= 0) { /* check 'expires' > 0 */ if(msg->expires && msg->expires->body.len > 0) { i = atoi(msg->expires->body.s); if(i <= 0) { /* user goes offline */ LM_DBG("user <%.*s@%.*s> goes offline - expires=%d\n", puri.user.len, puri.user.s, puri.host.len, puri.host.s, i); goto error; } else LM_DBG("user <%.*s@%.*s> online - expires=%d\n", puri.user.len, puri.user.s, puri.host.len, puri.host.s, i); } } else { LM_ERR("failed to parse 'expires'\n"); goto error; } if (check_message_support(msg)!=0) { LM_DBG("MESSAGE method not supported\n"); return -1; } } db_vals[0].type = DB_STR; db_vals[0].nul = 0; db_vals[0].val.str_val.s = puri.user.s; db_vals[0].val.str_val.len = puri.user.len; db_vals[1].type = DB_STR; db_vals[1].nul = 0; db_vals[1].val.str_val.s = puri.host.s; db_vals[1].val.str_val.len = puri.host.len; db_vals[2].type = DB_INT; db_vals[2].nul = 0; db_vals[2].val.int_val = 0; if (msilo_dbf.use_table(db_con, &ms_db_table) < 0) { LM_ERR("failed to use_table\n"); goto error; } if((msilo_dbf.query(db_con,db_keys,db_ops,db_vals,db_cols,db_no_keys, db_no_cols, ob_key, &db_res)!=0) || (RES_ROW_N(db_res) <= 0)) { LM_DBG("no stored message for <%.*s@%.*s>!\n", puri.user.len, puri.user.s, puri.host.len, puri.host.s); goto done; } LM_DBG("dumping [%d] messages for <%.*s@%.*s>!!!\n", RES_ROW_N(db_res), puri.user.len, puri.user.s, puri.host.len, puri.host.s); for(i = 0; i < RES_ROW_N(db_res); i++) { mid = RES_ROWS(db_res)[i].values[0].val.int_val; if(msg_list_check_msg(ml, mid)) { LM_DBG("message[%d] mid=%d already sent.\n", i, mid); continue; } memset(str_vals, 0, 4*sizeof(str)); SET_STR_VAL(str_vals[0], db_res, i, 1); /* from */ SET_STR_VAL(str_vals[1], db_res, i, 2); /* to */ SET_STR_VAL(str_vals[2], db_res, i, 3); /* body */ SET_STR_VAL(str_vals[3], db_res, i, 4); /* ctype */ rtime = (time_t)RES_ROWS(db_res)[i].values[5/*inc time*/].val.int_val; hdr_str.len = 1024; if(m_build_headers(&hdr_str, str_vals[3] /*ctype*/, str_vals[0]/*from*/, rtime /*Date*/) < 0) { LM_ERR("headers building failed [%d]\n", mid); if (msilo_dbf.free_result(db_con, db_res) < 0) LM_ERR("failed to free the query result\n"); msg_list_set_flag(ml, mid, MS_MSG_ERRO); goto error; } LM_DBG("msg [%d-%d] for: %.*s@%.*s\n", i+1, mid, puri.user.len, puri.user.s, puri.host.len, puri.host.s); /** sending using TM function: t_uac */ body_str.len = 1024; n = m_build_body(&body_str, rtime, str_vals[2/*body*/], 0); if(n<0) LM_DBG("sending simple body\n"); else LM_DBG("sending composed body\n"); tmb.t_request(&msg_type, /* Type of the message */ &str_vals[1], /* Request-URI (To) */ &str_vals[1], /* To */ &str_vals[0], /* From */ &hdr_str, /* Optional headers including CRLF */ (n<0)?(RES_ROWS(db_res)[i].values[3].nul?NULL:&str_vals[2]):&body_str, /* Message body */ (ms_outbound_proxy.s)?&ms_outbound_proxy:0, /* outbound uri */ m_tm_callback, /* Callback function */ (void*)(long)mid, /* Callback parameter */ NULL ); if (maxmsg_i > 0) { LM_DBG("Maximum number of dumped messages: %d\n", maxmsg_i); sent_cnt++; if (sent_cnt >= maxmsg_i) break; } } done: /** * Free the result because we don't need it * anymore */ if (db_res!=NULL && msilo_dbf.free_result(db_con, db_res) < 0) LM_ERR("failed to free result of query\n"); return 1; error: return -1; } /** * - cleaning up the messages that got reply * - delete expired messages from database */ void m_clean_silo(unsigned int ticks, void *param) { msg_list_el mle = NULL, p; db_key_t db_keys[MAX_DEL_KEYS]; db_val_t db_vals[MAX_DEL_KEYS]; db_op_t db_ops[1] = { OP_LEQ }; int n; LM_DBG("cleaning stored messages - %d\n", ticks); msg_list_check(ml); mle = p = msg_list_reset(ml); n = 0; while(p) { if(p->flag & MS_MSG_DONE) { #ifdef STATISTICS if(p->flag & MS_MSG_TSND) update_stat(ms_dumped_msgs, 1); else update_stat(ms_dumped_rmds, 1); #endif db_keys[n] = &sc_mid; db_vals[n].type = DB_INT; db_vals[n].nul = 0; db_vals[n].val.int_val = p->msgid; LM_DBG("cleaning sent message [%d]\n", p->msgid); n++; if(n==MAX_DEL_KEYS) { if (msilo_dbf.delete(db_con, db_keys, NULL, db_vals, n) < 0) LM_ERR("failed to clean %d messages.\n",n); n = 0; } } if((p->flag & MS_MSG_ERRO) && (p->flag & MS_MSG_TSND)) { /* set snd time to 0 */ ms_reset_stime(p->msgid); #ifdef STATISTICS update_stat(ms_failed_rmds, 1); #endif } #ifdef STATISTICS if((p->flag & MS_MSG_ERRO) && !(p->flag & MS_MSG_TSND)) update_stat(ms_failed_msgs, 1); #endif p = p->next; } if(n>0) { if (msilo_dbf.delete(db_con, db_keys, NULL, db_vals, n) < 0) LM_ERR("failed to clean %d messages\n", n); n = 0; } msg_list_el_free_all(mle); /* cleaning expired messages */ if(ticks%(ms_check_time*ms_clean_period)param==NULL || *ps->param==0) { LM_DBG("message id not received\n"); goto done; } LM_DBG("completed with status %d [mid: %ld/%d]\n", ps->code, (long)ps->param, *((int*)ps->param)); if(!db_con) { LM_ERR("db_con is NULL\n"); goto done; } if(ps->code >= 300) { LM_DBG("message <%d> was not sent successfully\n", *((int*)ps->param)); msg_list_set_flag(ml, *((int*)ps->param), MS_MSG_ERRO); goto done; } LM_DBG("message <%d> was sent successfully\n", *((int*)ps->param)); msg_list_set_flag(ml, *((int*)ps->param), MS_MSG_DONE); done: return; } void m_send_ontimer(unsigned int ticks, void *param) { db_key_t db_keys[2]; db_op_t db_ops[2]; db_val_t db_vals[2]; db_key_t db_cols[6]; db_res_t* db_res = NULL; int i, db_no_cols = 6, db_no_keys = 2, mid, n; static char hdr_buf[1024]; static char uri_buf[1024]; static char body_buf[1024]; str puri; time_t ttime; str str_vals[4], hdr_str , body_str; time_t stime; if(ms_reminder.s==NULL) { LM_WARN("reminder address null\n"); return; } /* init */ db_keys[0]=&sc_snd_time; db_keys[1]=&sc_snd_time; db_ops[0]=OP_NEQ; db_ops[1]=OP_LEQ; db_cols[0]=&sc_mid; db_cols[1]=&sc_uri_user; db_cols[2]=&sc_uri_host; db_cols[3]=&sc_body; db_cols[4]=&sc_ctype; db_cols[5]=&sc_snd_time; LM_DBG("------------ start ------------\n"); hdr_str.s=hdr_buf; hdr_str.len=1024; body_str.s=body_buf; body_str.len=1024; db_vals[0].type = DB_INT; db_vals[0].nul = 0; db_vals[0].val.int_val = 0; db_vals[1].type = DB_INT; db_vals[1].nul = 0; ttime = time(NULL); db_vals[1].val.int_val = (int)ttime; if (msilo_dbf.use_table(db_con, &ms_db_table) < 0) { LM_ERR("failed to use_table\n"); return; } if((msilo_dbf.query(db_con,db_keys,db_ops,db_vals,db_cols,db_no_keys, db_no_cols, NULL,&db_res)!=0) || (RES_ROW_N(db_res) <= 0)) { LM_DBG("no message for <%.*s>!\n", 24, ctime((const time_t*)&ttime)); goto done; } LM_DBG("dumping [%d] messages for <%.*s>!!!\n", RES_ROW_N(db_res), 24, ctime((const time_t*)&ttime)); for(i = 0; i < RES_ROW_N(db_res); i++) { mid = RES_ROWS(db_res)[i].values[0].val.int_val; if(msg_list_check_msg(ml, mid)) { LM_DBG("message[%d] mid=%d already sent.\n", i, mid); continue; } memset(str_vals, 0, 4*sizeof(str)); SET_STR_VAL(str_vals[0], db_res, i, 1); /* user */ SET_STR_VAL(str_vals[1], db_res, i, 2); /* host */ SET_STR_VAL(str_vals[2], db_res, i, 3); /* body */ SET_STR_VAL(str_vals[3], db_res, i, 4); /* ctype */ hdr_str.len = 1024; if(m_build_headers(&hdr_str, str_vals[3] /*ctype*/, ms_reminder/*from*/,0/*Date*/) < 0) { LM_ERR("headers building failed [%d]\n", mid); if (msilo_dbf.free_result(db_con, db_res) < 0) LM_DBG("failed to free result of query\n"); msg_list_set_flag(ml, mid, MS_MSG_ERRO); return; } puri.s = uri_buf; puri.len = 4 + str_vals[0].len + 1 + str_vals[1].len; memcpy(puri.s, "sip:", 4); memcpy(puri.s+4, str_vals[0].s, str_vals[0].len); puri.s[4+str_vals[0].len] = '@'; memcpy(puri.s+4+str_vals[0].len+1, str_vals[1].s, str_vals[1].len); LM_DBG("msg [%d-%d] for: %.*s\n", i+1, mid, puri.len, puri.s); /** sending using TM function: t_uac */ body_str.len = 1024; stime = (time_t)RES_ROWS(db_res)[i].values[5/*snd time*/].val.int_val; n = m_build_body(&body_str, 0, str_vals[2/*body*/], stime); if(n<0) LM_DBG("sending simple body\n"); else LM_DBG("sending composed body\n"); msg_list_set_flag(ml, mid, MS_MSG_TSND); tmb.t_request(&msg_type, /* Type of the message */ &puri, /* Request-URI */ &puri, /* To */ &ms_reminder, /* From */ &hdr_str, /* Optional headers including CRLF */ (n<0)?&str_vals[2]:&body_str, /* Message body */ (ms_outbound_proxy.s)?&ms_outbound_proxy:0, /* outbound uri */ m_tm_callback, /* Callback function */ (void*)(long)mid, /* Callback parameter */ NULL ); } done: /** * Free the result because we don't need it anymore */ if (db_res!=NULL && msilo_dbf.free_result(db_con, db_res) < 0) LM_DBG("failed to free result of query\n"); return; } int ms_reset_stime(int mid) { db_key_t db_keys[1]; db_op_t db_ops[1]; db_val_t db_vals[1]; db_key_t db_cols[1]; db_val_t db_cvals[1]; db_keys[0]=&sc_mid; db_ops[0]=OP_EQ; db_vals[0].type = DB_INT; db_vals[0].nul = 0; db_vals[0].val.int_val = mid; db_cols[0]=&sc_snd_time; db_cvals[0].type = DB_INT; db_cvals[0].nul = 0; db_cvals[0].val.int_val = 0; LM_DBG("updating send time for [%d]!\n", mid); if (msilo_dbf.use_table(db_con, &ms_db_table) < 0) { LM_ERR("failed to use_table\n"); return -1; } if(msilo_dbf.update(db_con,db_keys,db_ops,db_vals,db_cols,db_cvals,1,1)!=0) { LM_ERR("failed to make update for [%d]!\n", mid); return -1; } return 0; } static int fixup_m_dump(void** param, int param_no) { if (param_no==1) { return fixup_spve(param); } else if (param_no==2) { return fixup_uint(param); } return 0; } /* * Check if REGISTER request has contacts that support MESSAGE method or * if MESSAGE method is listed in Allow header and contact does not have * methods parameter. */ int check_message_support(struct sip_msg* msg) { contact_t* c; unsigned int allow_message = 0; unsigned int allow_hdr = 0; str *methods_body; unsigned int methods; /* Parse all headers in order to see all Allow headers */ if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse headers\n"); return -1; } if (parse_allow(msg) == 0) { allow_hdr = 1; allow_message = get_allow_methods(msg) & METHOD_MESSAGE; } LM_DBG("Allow message: %u\n", allow_message); if (!msg->contact) { LM_DBG("no Contact found\n"); return -1; } if (parse_contact(msg->contact) < 0) { LM_ERR("failed to parse Contact HF\n"); return -1; } if (((contact_body_t*)msg->contact->parsed)->star) { LM_DBG("* Contact found\n"); return -1; } if (contact_iterator(&c, msg, 0) < 0) return -1; /* * Check contacts for MESSAGE method in methods parameter list * If contact does not have methods parameter, use Allow header methods, * if any. Stop if MESSAGE method is found. */ while(c) { if (c->methods) { methods_body = &(c->methods->body); if (parse_methods(methods_body, &methods) < 0) { LM_ERR("failed to parse contact methods\n"); return -1; } if (methods & METHOD_MESSAGE) { LM_DBG("MESSAGE contact found\n"); return 0; } } else { if (allow_message) { LM_DBG("MESSAGE found in Allow Header\n"); return 0; } } if (contact_iterator(&c, msg, c) < 0) { LM_DBG("MESSAGE contact not found\n"); return -1; } } /* no Allow header and no methods in Contact => dump MESSAGEs */ if(allow_hdr==0) return 0; return -1; } opensips-2.2.2/modules/nat_traversal/000077500000000000000000000000001300170765700177005ustar00rootroot00000000000000opensips-2.2.2/modules/nat_traversal/Makefile000066400000000000000000000003371300170765700213430ustar00rootroot00000000000000# $Id$ # # nat_traversal module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=nat_traversal.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/nat_traversal/README000066400000000000000000001056631300170765700205730ustar00rootroot00000000000000NAT Traversal Module Dan Pascu Edited by Dan Pascu Copyright © 2008 Dan Pascu Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Keepalive functionality 1.2.1. Overview 1.2.2. Background 1.2.3. Implementation 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported parameters 1.4.1. keepalive_interval (integer) 1.4.2. keepalive_method (string) 1.4.3. keepalive_from (string) 1.4.4. keepalive_extra_headers (string) 1.4.5. keepalive_state_file (string) 1.5. Exported functions 1.5.1. client_nat_test(type) 1.5.2. fix_contact() 1.5.3. nat_keepalive() 1.6. Exported statistics 1.6.1. keepalive_endpoints 1.6.2. registered_endpoints 1.6.3. subscribed_endpoints 1.6.4. dialog_endpoints 1.7. Exported pseudo-variables 1.7.1. $keepalive.socket(nat_endpoint) 1.7.2. $source_uri 1.7.3. $nat_traversal.track_dialog 1.8. Keepalive use cases 1.8.1. Single proxy environments 1.8.2. Registration in multi-proxy environments 1.8.3. Subscription in multi-proxy environments 1.8.4. Outgoing INVITEs in multi-proxy environments List of Examples 1.1. Setting the keepalive_interval parameter 1.2. Setting the keepalive_method parameter 1.3. Setting the keepalive_from parameter 1.4. Setting the keepalive_extra_headers parameter 1.5. Setting the keepalive_state_file parameter 1.6. Using the client_nat_test function 1.7. Using the fix_contact function 1.8. Using the nat_keepalive function 1.9. Using $keepalive.socket in multi-proxy environments 1.10. Using $source_uri to set the received AVP on registrars 1.11. Using $source_uri in multi-proxy environments Chapter 1. Admin Guide 1.1. Overview The nat_traversal module provides support for handling far-end NAT traversal for SIP signaling. The module includes functionality to detect user agents behind NAT, to modify SIP headers to allow user agents to work transparently behind NAT and to send keepalive messages to user agents behind NAT in order to preserve their visibility in the network. The module can handle user agents behind multiple cascaded NAT boxes as easily as user agents behind a single level of NAT. The module is designed to work in complex environments where multiple SIP proxies may be involved in handling registration and routing and where the incoming and outgoing paths may not necessarily be the same, or where the routing path may even change between consecutive dialogs. 1.2. Keepalive functionality 1.2.1. Overview The nat_traversal module implements a very sophisticated keepalive mechanism, that is able to handle the most complex environments and use cases, including distributed environments with multiple proxies. Unlike existing keepalive solutions that only send keepalive messages to user agents that have registered (during their registration), the nat_traversal module can keepalive an user agent based on multiple conditions, making it not only more flexible and more efficient, but also able to work in environments and with use cases where a simple keepalive implementation based on keeping alive registrations alone cannot work. The keepalive mechanism works by sending a SIP request to a user agent behind NAT to make that user agent send back a reply. The purpose is to have packets sent from inside the NAT to the proxy often enough to prevent the NAT box from timing out the connection. Many NAT boxes do not consider packets that travel from the outside to the inside of the NAT to reset the connection expiration timer, thus to keepalive a user agent we need to trigger an answer from it. 1.2.2. Background One of the major limitations of an implementation that only sends keepalive messages to registered user agents, is that it creates an artificial association between the concept of network visibility with the concept of user registration. The registration process only creates network visibility for incoming INVITE requests, in other words for incoming calls. However, there are other cases where a user agent needs to preserve its network visibility when behind NAT, that have nothing to do with receiving incoming calls. One of them is the ability of the user agent to keep receiving NOTIFY requests for a presence subscription it has made. Another situation is where the user agent should be able to receive all messages within a dialog it has initiated, even if it is not registered. In the first case, a presence agent is required to register to be able to receive notifications for its subscriptions and it has to keep the registration active the whole time. In the second case a user agent that wants to make an outgoing call has to register and keep the registration active during the call, otherwise it may not be able to receive future in-dialog messages, including the BYE that closes the dialog. Not only we have this forced association shown above, that requires a user agent to register to be able to do anything, but a simple keepalive implementation based on sending keepalive messages only to registered user agents, will also fail to work in common cases, exactly because of this artificial association. For example lets assume that we have an user agent that is registered. If during an outgoing call initiated by this user agent, the agent stops registering, then it will not be able to receive further in-dialog messages after the NAT binding expires. The same is true for a presence agent, receiving notifications for its subscriptions. In environments with multiple proxies handling the same domains, the problem gets even more acute. In this case the incoming and outgoing paths for a call may be completely different: the user agent may register using one proxy as an entry point to the network, but may make an outgoing call using a different proxy as the network entry point. Even more a registration may use a different proxy as the entry point to the network with each renewal of the registration, making it volatile and unreliable for anything else except incoming calls. A keepalive implementation that only sends keepalive messages to registered user agents will not be able to guarantee the delivery of in-dialog messages for outgoing calls even if it requires the user agent to register before making a call. In this case, even if we assume that the user agent would pick the same proxy for an outgoing call as the one it has used for the last registration, at the next registration it may pick another one (as returned by DNS), and will dissociate the incoming and outgoing paths rendering the outgoing path unusable (assuming the outgoing call takes longer than the registration period). All this leads to the conclusion that a keepalive implementation based solely on sending keepalive messages to registered user agents can only work in single proxy environments and then only work reliably if it requires the user agent to register before doing anything else, even though some actions would not require a user agent to register. 1.2.3. Implementation To avoid the above mentioned issues, this implementation introduces the concept of network visibility for a given condition. This way we can keepalive a user agent for multiple independent conditions, thus avoiding all the problems presented above. The conditions for which the module will send keepalive messages are: * Registration - for user agents that have registered to preserve their visibility for incoming calls. This is the result of triggering keepalive for a REGISTER request. * Subscription - for presence agents that have subscribed to some events to preserve their visibility for receiving back notifications. This is the result of triggering keepalive for a SUBSCRIBE request. * Dialogs - for user agents that have initiated an outgoing call to preserve their visibility for receiving further in-dialog messages. This is the result of triggering keepalive for an outgoing INVITE request. A user agent's NAT entry point may be kept alive for one or multiple of the conditions listed above. Even when a NAT endpoint is kept alive for more than one condition, only one keepalive message is sent to that NAT endpoint. The presence of multiple conditions for a NAT endpoint, only guarantees that the network visibility for a user agent based on a certain condition will be available while that condition is true, independently of the other conditions. When all the conditions to keepalive a NAT endpoint will disappear, that endpoint will be removed from the list with the NAT endpoints that need to be kept alive. The user interface for the keepalive functionality is very simple. It consists of a single function called nat_keepalive() that needs to be called only once for the requests that trigger the need for network visibility. These requests are: REGISTER, SUBSCRIBE and outgoing INVITEs. After such a request arrives it makes the user agent visible for the purpose of receiving back other messages. Thus, after a REGISTER the user agent may receive back incoming calls, after a SUBSCRIBE it may receive back notifications and after an outgoing INVITE it may receive back further in-dialog messages including the BYE that ends the dialog. The nat_keepalive() function needs to be called on the proxy that directly receives the request from the user agent, if it determines that the user agent making the request is behind NAT. The function needs to be called before the request gets either a stateless reply or it is relayed with t_relay(). Calling the nat_keepalive() function has no effect if the request gets no stateless reply or it is not relayed. For environments with multiple proxies, where the proxy that acts as an entry point to the network for a given request is not the one that actually handles the request, then the nat_keepalive() function needs to be called on the proxy that is the entry point and after that the request must be sent to the proxy that actually handles the request using t_relay(). This is needed because the keepalive functionality detects from the stateless replies or the TM relayed replies if the NAT endpoint needs to be kept alive for the condition triggered by the request for which the nat_keepalive() function was called. For example assume a network where a proxy P1 receives a REGISTER from an user agent behind NAT. P1 will determine that the user agent is behind NAT so it needs keepalive functionality, but another proxy called P2 is actually handling the subscriber registrations. In this case P1 has to call nat_keepalive() even though it doesn't yet know the answer P2 will give to the REGISTER request (which may even be a negative reply) or if P2 will restrict the proposed expiration time in any way. Thus P1 calls nat_keepalive() after which it calls t_relay(). When the reply from P2 arrives, a callback is triggered which will determine if the request did get a positive reply, and if so it will extract the registration expiration time and enable the keepalive functionality for that endpoint for the registration condition for the time given by the registration expiration. For single proxy environments, or if P1 is the same as P2, then t_relay() is not called, instead save_location() is called if the registration is accepted. Then the same process described above happens only this time triggered by a stateless reply callback. In both cases, calling nat_keepalive() when the REGISTER is received has no other effect that to trigger some callbacks that will determine from the reply if the caller endpoint should be kept alive or not. Below is described how nat_keepalive() should be called and what it does for each of the requests that need keepalive functionality (the function should only be called if it is determined that the user agent that generated the request is behind NAT): * REGISTER - called before save_location() or t_relay() (depending on whether the proxy that received the REGISTER is also handling registration for that subscriber or not). It will determine from either the stateless reply generated by save_location() or the TM relayed reply if the registration was successful and what is its expiration time. If the registration was successful it will mark the given NAT endpoint for keepalive for the registration condition using the detected expiration time. If the REGISTER request is discarded after nat_keepalive() was called or if it intercepts a negative reply it will have no effect and the registration condition will not be activated for that endpoint. * SUBSCRIBE - called before handle_subscribe() or t_relay() (depending on whether the proxy that received the SUBSCRIBE is also handling subscriptions for that subscriber or not). It will determine from either the stateless reply generated by handle_subscribe() or the TM relayed reply if the subscription was successful and what is its expiration time. If the subscription was successful it will mark the given NAT endpoint for keepalive for the subscription condition using the detected expiration time. If the SUBSCRIBE request is discarded after nat_keepalive() was called or if it intercepts a negative reply it will have no effect and the subscription condition will not be activated for that endpoint. It should be called for every SUBSCRIBE received, not only the ones that start a subscription (do not have a to tag), because it needs to update (extend) the expiration time for the subscription. * INVITE - called before t_relay() for the first INVITE in a dialog. It will automatically trigger dialog tracing for that dialog and will use the dialog callbacks to detect changes in the dialog state. It will add a keepalive entry with the dialog condition for the caller NAT endpoint as soon as the dialog is created (this happens when t_relay() is called). It will then keep that condition for the given endpoint until the dialog is destroyed (either terminated, failed or expired). If the INVITE request cannot be relayed after nat_keepalive() was called it will have no effect and the dialog condition will not be activated for that endpoint. In addition an INVITE that starts a dialog will automatically trigger keepalive functionality for the destination endpoints if they are behind NAT. This is done by detecting if any of the destination endpoints already has a keepalive entry for the register condition. If so, a dialog condition will be added to that entry thus preserving that endpoint visibility even if the registration expires during the dialog or is moved to another proxy. During the call setup stage, multiple entries for the callee may be added with the dialog condition if parallel forking is used, however only the destination endpoints behind NAT will have the extra dialog condition set. Later when the dialog is confirmed, only the endpoint that answered the call will keep the dialog condition activated (if present), while all the endpoints from the unanswered branches will have it removed. This is done automatically without any need to call any function. Considering the elements presented in this section, we can say that the nat_traversal module provides a flexible and efficient keepalive functionality that is very easy to use. Because only the border proxies send keepalive messages, the network traffic is minimized. For the same reason, message processing in the proxies is also minimized, as border proxies generate keepalive messages themselves and send them statelessly, instead of having to relay messages generated by the registrars. Network traffic is also minimized by only sending a single keepalive message for an endpoint no matter for how many reasons the endpoint is kept alive. Keepalive messages are also distributed over the keepalive interval to avoid overloading the proxy by generating too many messages at a time. The nat_traversal module keeps its internal state about endpoints that need keepalive, state that is build while messages are processed by the proxy and thus it doesn't need to transfer any information from the usrloc module, which should also improve its efficiency. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * sl module - if keepalive is enabled. * tm module - if keepalive is enabled. * dialog module - if keepalive is enabled and keeping alive INVITE dialogs is needed. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.4. Exported parameters 1.4.1. keepalive_interval (integer) The time interval (in seconds) required to send a keepalive message to all the endpoints that need being kept alive. During this interval, each endpoint will receive exactly one keepalive message. A negative value or zero will disable the keepalive functionality. Default value is “60â€. Example 1.1. Setting the keepalive_interval parameter ... modparam("nat_traversal", "keepalive_interval", 90) ... 1.4.2. keepalive_method (string) What SIP method to use to send keepalive messages. Typical methods used for this purpose are NOTIFY and OPTIONS. NOTIFY generates smaller replies from user agents, but they are almost entirely negative replies. Apparently almost none of the user agents understand that the purpose of the NOTIFY with a “keep-alive†event is to keep NAT open, even though many user agents send such NOTIFY requests themselves. However this does not affect the result at all, since the purpose is to trigger a response from the user agent behind NAT, positive or negative replies having little relevance as they are discarded anyway. The OPTIONS method on the other hand has a much higher rate of positive replies, but at the same time those positive replies are much bigger, mostly because the OPTIONS method is used to inform about the user agent capabilities and thus it includes a lot of extra headers to indicate those capabilities. Many user agents also include a SDP body with a bogus media session, probably to indicate media capabilities. All of this makes that positive replies to OPTIONS requests are 2 to 3 times bigger than negative replies or replies to NOTIFY requests. For this reason the default value for the used method is NOTIFY. Default value is “NOTIFYâ€. Example 1.2. Setting the keepalive_method parameter ... modparam("nat_traversal", "keepalive_method", "OPTIONS") ... 1.4.3. keepalive_from (string) Indicates what SIP URI to use in the From header of the keepalive requests. If not specified it will use sip:keepalive@proxy_ip, where proxy_ip is the IP address of the outgoing interface used to send the keepalive message, which is the same interface on which the request that triggered keepalive functionality arrived. Default value is “sip:keepalive@proxy_ip†with proxy_ip being the actual IP of the outgoing interface. Example 1.3. Setting the keepalive_from parameter ... modparam("nat_traversal", "keepalive_from", "sip:keepalive@my-domain.com ") ... 1.4.4. keepalive_extra_headers (string) Specifies extra headers that should be added to the keepalive messages that are sent by the proxy. The header specification must also include the CRLF (\r\n) line separator. Multiple headers can be specified by concatenating them and each of them must include the \r\n separator. Default value is undefined (send no extra headers). Example 1.4. Setting the keepalive_extra_headers parameter ... modparam("nat_traversal", "keepalive_extra_headers", "User-Agent: OpenSI PS\r\nX-MyHeader: some_value\r\n") ... 1.4.5. keepalive_state_file (string) Specifies a filename where information about the NAT endpoints and the conditions for which they are being kept alive is saved when OpenSIPS exits. The information in this file is then used when OpenSIPS starts to restore its internal state and continue to send keepalive messages to the NAT endpoints that have not expired in the meantime. This is useful when restarting OpenSIPS to avoid losing keepalive state information about the NAT endpoints. The internal keepalive state is guaranteed to be saved in this file on exit, even when OpenSIPS crashes. The value of this parameter can be either a relative path, in which case it will store it in the OpenSIPS working directory, or an absolute path. Default value is undefined “keepalive_stateâ€. Example 1.5. Setting the keepalive_state_file parameter ... modparam("nat_traversal", "keepalive_state_file", "/var/run/opensips/kee palive_state") ... 1.5. Exported functions 1.5.1. client_nat_test(type) Check if the client is behind NAT. What tests are performed is specified by the type parameter which is an integer given by the sum of the numbers corresponsing to the tests that one wishes to perform. The numbers corresponding to individual tests are shown below: * 1 - tests if client has a private IP address (as defined by RFC1918) in the Contact field of the SIP message. * 2 - tests if client has contacted OpenSIPS from an address that is different from the one in the Via field. Both the IP and port are compared by this test. * 4 - tests if client has a private IP address (as defined by RFC1918) in the top Via field of the SIP message. * 8 - tests if client has contacted OpenSIPS from an address that is different from the one in the Contact field. Only IP is compared by this test. For example calling client_nat_test("3") will perform test 1 and test 2 and return true if at least one succeeds, otherwise false. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.6. Using the client_nat_test function ... if (client_nat_test("3")) { ..... } ... 1.5.2. fix_contact() Will replace the IP and port in the Contact header with the IP and port the SIP message was received from. Usually called after a successful call to client_nat_test(type) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE. Example 1.7. Using the fix_contact function ... if (client_nat_test("3")) { fix_contact(); } ... 1.5.3. nat_keepalive() Trigger keepalive functionality for the source address of the request. When called it only sets some internal flags, which will trigger later the addition of the endpoint to the keepalive list if a positive reply is generated/received (for REGISTER and SUBSCRIBE) or when the dialog is started/replied (for INVITEs). For this reason, it can be called early or late in the script. The only condition is to call it before replying to the request or before sending it to another proxy. If the request needs to be sent to another proxy, t_relay() must be used to be able to intercept replies via TM or dialog callbacks. If stateless forwarding is used, the keepalive functionality will not work. Also for outgoing INVITEs, record_route() should also be used to make sure the proxy that keeps the caller endpoint alive stays in the path. For multi-proxy setups, this function should always be called on the border proxies (the ones that received the request directly from the user agent). For more details about this function, see the Implementation subsection from the Keepalive functionality section. This function can be used from REQUEST_ROUTE. Example 1.8. Using the nat_keepalive function ... if ((method=="REGISTER" || method=="SUBSCRIBE" || (method=="INVITE" && !has_totag())) && client_nat_test("3")) { nat_keepalive(); } ... 1.6. Exported statistics 1.6.1. keepalive_endpoints Indicates the total number of NAT endpoints that are being kept alive. 1.6.2. registered_endpoints Indicates how many of the NAT endpoints are kept alive for registrations. 1.6.3. subscribed_endpoints Indicates how many of the NAT endpoints are kept alive for subscriptions. 1.6.4. dialog_endpoints Indicates how many of the NAT endpoints are kept alive for taking part in an INVITE dialog. 1.7. Exported pseudo-variables 1.7.1. $keepalive.socket(nat_endpoint) Returns the local socket used to send messages to the given NAT endpoint URI. The socket has the form proto:ip:port. The NAT endpoint URI is in the form: sip:ip:port[;transport=xxx] with transport missing if UDP. If the requested NAT endpoint URI is present in the internal keepalive table for any condition, it will return its associated local socket, else it will return null. The nat_endpoint can be a string or another pseudo-variable. This can be useful to restore the sending socket when relaying messages to a given user agent in multi-proxy environments. Consider an example where 2 proxies are involved, P1 and P2. A user agent registers by sending a REGISTER request to P1. P1 will call nat_keepalive() but because it determines that P2 should actually handle the user registration will forward the request to P2. Now assume P2 receives an incoming INVITE for this user. It will determine that the registration came through P1 and will forward the request to P1. P2 should also include the NAT endpoint URI where this request is to be relayed. This information should have been provided by P1 when it relayed the REGISTER request to P2. The means to do this is out of the scope of this example, but one can either use the path extension or custom headers to do this. When P1 receives the INVITE it will use the NAT endpoint URI it has received along with the request to determine the socket to send out the request, which should be the same as the one where the registration request was originally received. In the example below lets assume that P2 provided the original NAT endpoint address in a custom header called X-NAT-URI and that it also provides a custom header called X-Scope to indicate that the message is sent to P1 for being relayed back to the user agent by P1 which has the NAT open with it. Example 1.9. Using $keepalive.socket in multi-proxy environments ... # This code runs on P1 which has received an INVITE from P2 to forward # it to the user agent behind NAT (because P1 has the NAT open with it). if (method=="INVITE" && $hdr(X-Scope)=="nat-relay") { $du = $hdr(X-NAT-URI); $fs = $keepalive.socket($du); t_relay(); exit; } ... 1.7.2. $source_uri Returns the URI specification from where a request was received in the form sip:ip:port[;transport=xxx] with transport missing if UDP. This pseudo-variable can be used to set the received AVP for the registrar module to indicate that a user agent is behind NAT. This is meant as a more flexible replacement for the fix_nated_register() function, because it allows one to modify the source uri by appending some extra parameters before saving it to the received AVP. Another use for this pseudo-variable is in multi-proxy environments to indicate the NAT endpoint URI to the next proxy (if needed). Consider the previous example with two proxies P1 and P2. P1 receives the REGISTER request from a user agent and forwards it to P2 which does the actual registration. P1 needs to indicate the NAT endpoint URI to P2, so that P2 can include it later for incoming INVITE requests to this user agent. Example 1.10. Using $source_uri to set the received AVP on registrars ... modparam("registrar", "received_avp", "$avp(received_uri)") modparam("registrar", "tcp_persistent_flag", 10) ... # This code runs on the registrar, assuming it has received the # REGISTER request directly from the user agent. if (method=="REGISTER") { if (client_nat_test("3")) { if (proto==UDP) { nat_keepalive(); } else { # Keep TCP/TLS connections open until the registration # expires, by setting the tcp_persistent_flag setflag(10); } force_rport(); $avp(received_uri) = $source_uri; # or we could add some extra parameters to it if needed # $avp(received_uri) = $source_uri + ";relayed=false" } if (!www_authorize("", "subscriber")) { www_challenge("", "0"); return; } else if (!db_check_to()) { sl_send_reply("403", "Username!=To not allowed ($au!=$tU)"); return; } if (!save("location")) { sl_reply_error(); } exit; } ... Example 1.11. Using $source_uri in multi-proxy environments ... # This code runs on P1 which received the REGISTER request and has to # forward it to the registrar P2. if (method=="REGISTER") { if (client_nat_test("3")) { force_rport(); nat_keepalive(); append_hf("X-NAT-URI: $source_uri\r\n"); } $du = "sip:P2_ip:P2_port"; t_relay(); exit; } ... 1.7.3. $nat_traversal.track_dialog Returns a boolean value (0 or 1) indicating if dialog tracking will be enabled by the nat_traversal module. The nat_traversal module will always track the dialog (by calling create_dialog internally) unless told otherwise. This is an advanced setting which is only meant to be used by multi-proxy setups where a proxy doesn't want to keep track of a dialog, that is, if it won't stay in the signaling path. By setting this pv to 0 the nat_traversal module will not attempt to create the dialog. 1.8. Keepalive use cases 1.8.1. Single proxy environments In this case the usage is straight forward. The nat_keepalive() function needs to be called before save_location() for REGISTER requests, before handle_subscribe() for SUBSCRIBE requests and before t_relay() for the first INVITE of a dialog. 1.8.2. Registration in multi-proxy environments If the proxy receiving the REGISTER request is the same as the proxy handling it, then the case is reduced to the single proxy case. For this example, lets assume they are different. We have a user agent UA1 for which the registration is handled by the proxy P1. However UA1 sends the REGISTER to P0 which in turn forwards it to P1 like this: UA1 --> P0 --> P1. In this case P0 calls nat_keepalive(), adds the NAT endpoint URI to the request (for example using a custom header) and forwards the request to P1. P1 will save the user in the user location together with the NAT endpoint URI. When an incoming INVITE request arrives on P1 for UA1, P1, will lookup the location and determine that it has to relay it to P0 because P0 has the NAT open with UA1. P1 will include the original NAT endpoint URI in the request and an indication that the only role P0 has in this transaction is to relay it to UA1. P0 will receive this request and determine that is has to act as a relay for it. It will extract the NAT endpoint URI, then based on it the corresponding local socket using $keepalive.socket(endpoint_uri). It will then set both $du and $fs to the values it has found, call record_route() to stay in the path and call t_relay() to send it to UA1. Handling other type of requests (like for example SUBSCRIBE or MESSAGE) that arrive on P1 for UA1 is done the same way as with the first INVITE, on both P1 and P0. 1.8.3. Subscription in multi-proxy environments If the proxy receiving the SUBSCRIBE request is the same as the proxy handling it, then the case is reduced to the single proxy case. For this example, lets assume they are different. We have a user agent UA1 for which subscriptions are handled by the proxy P1. However UA1 sends the SUBSCRIBE to P0 which in turn forwards it to P1 like this: UA1 --> P0 --> P1. In this case P0 calls nat_keepalive(), then calls record_route() to stay in the path and forwards the request to P1 using t_relay(). Further SUBSCRIBE and NOTIFY requests will follow the record route and use P0 as a NAT entry point to have access to UA1. Further in-dialog SUBSCRIBE requests should also call record_route(). 1.8.4. Outgoing INVITEs in multi-proxy environments If the proxy receiving the INVITE request is the same as the proxy handling it, then the case is reduced to the single proxy case. For this example, lets assume they are different. We have a user agent UA1 which is handled by the proxy P1 and UA2 which is handled by P2. UA2 has registered with P2 going through P3, while UA1 calls UA2 by sending the first INVITE to P0. The call flow for the first INVITE looks like this: UA1 --> P0 --> P1 --> P2 --> P3 --> UA2. In this case P0 calls nat_keepalive(), then calls record_route() to stay in the path and forwards the request to P1. P1 authenticates UA1 then forwards the request to P2, which is the home proxy for UA2. P1 doesn't have to use record_route to stay in the path, but it can do that if needed for other purposes. P2 will lookup UA2 and find out that it is reachable through P3. It will take the original NAT endpoint URI that is has saved in the user location when UA2 has registered and include it in the message along with an indication that P3 only has to relay the message to UA2. If P2 does accounting or starts a media relay, it should also call record_route() to stay in the path. Then it forwards the request to P3 using t_relay(). P3 will detect that it only has to relay the request to UA2 because it has the NAT open with it. It will extract the NAT endpoint URI from the message and the local sending socket using $keepalive.socket(endpoint_uri) and will set both $du and $fs. After that it will call record_route() to stay in the path, and forward the request to UA2 using t_relay(). Further in-dialog requests will follow the recorded route and use P0 and P3 as access points to UA1 respectively UA2. All the proxies that have used record_route() during the first INVITE should also call record_route() during further in-dialog requests to keep staying in the path. opensips-2.2.2/modules/nat_traversal/doc/000077500000000000000000000000001300170765700204455ustar00rootroot00000000000000opensips-2.2.2/modules/nat_traversal/doc/nat_traversal.xml000066400000000000000000000016761300170765700240460ustar00rootroot00000000000000 %docentities; ]> NAT Traversal Module Dan Pascu dan@ag-projects.com Dan Pascu dan@ag-projects.com 2008 Dan Pascu $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/nat_traversal/doc/nat_traversal_admin.xml000066400000000000000000001220361300170765700252100ustar00rootroot00000000000000 &adminguide;
Overview The nat_traversal module provides support for handling far-end NAT traversal for SIP signaling. The module includes functionality to detect user agents behind NAT, to modify SIP headers to allow user agents to work transparently behind NAT and to send keepalive messages to user agents behind NAT in order to preserve their visibility in the network. The module can handle user agents behind multiple cascaded NAT boxes as easily as user agents behind a single level of NAT. The module is designed to work in complex environments where multiple SIP proxies may be involved in handling registration and routing and where the incoming and outgoing paths may not necessarily be the same, or where the routing path may even change between consecutive dialogs.
Keepalive functionality
Overview The nat_traversal module implements a very sophisticated keepalive mechanism, that is able to handle the most complex environments and use cases, including distributed environments with multiple proxies. Unlike existing keepalive solutions that only send keepalive messages to user agents that have registered (during their registration), the nat_traversal module can keepalive an user agent based on multiple conditions, making it not only more flexible and more efficient, but also able to work in environments and with use cases where a simple keepalive implementation based on keeping alive registrations alone cannot work. The keepalive mechanism works by sending a SIP request to a user agent behind NAT to make that user agent send back a reply. The purpose is to have packets sent from inside the NAT to the proxy often enough to prevent the NAT box from timing out the connection. Many NAT boxes do not consider packets that travel from the outside to the inside of the NAT to reset the connection expiration timer, thus to keepalive a user agent we need to trigger an answer from it.
Background One of the major limitations of an implementation that only sends keepalive messages to registered user agents, is that it creates an artificial association between the concept of network visibility with the concept of user registration. The registration process only creates network visibility for incoming INVITE requests, in other words for incoming calls. However, there are other cases where a user agent needs to preserve its network visibility when behind NAT, that have nothing to do with receiving incoming calls. One of them is the ability of the user agent to keep receiving NOTIFY requests for a presence subscription it has made. Another situation is where the user agent should be able to receive all messages within a dialog it has initiated, even if it is not registered. In the first case, a presence agent is required to register to be able to receive notifications for its subscriptions and it has to keep the registration active the whole time. In the second case a user agent that wants to make an outgoing call has to register and keep the registration active during the call, otherwise it may not be able to receive future in-dialog messages, including the BYE that closes the dialog. Not only we have this forced association shown above, that requires a user agent to register to be able to do anything, but a simple keepalive implementation based on sending keepalive messages only to registered user agents, will also fail to work in common cases, exactly because of this artificial association. For example lets assume that we have an user agent that is registered. If during an outgoing call initiated by this user agent, the agent stops registering, then it will not be able to receive further in-dialog messages after the NAT binding expires. The same is true for a presence agent, receiving notifications for its subscriptions. In environments with multiple proxies handling the same domains, the problem gets even more acute. In this case the incoming and outgoing paths for a call may be completely different: the user agent may register using one proxy as an entry point to the network, but may make an outgoing call using a different proxy as the network entry point. Even more a registration may use a different proxy as the entry point to the network with each renewal of the registration, making it volatile and unreliable for anything else except incoming calls. A keepalive implementation that only sends keepalive messages to registered user agents will not be able to guarantee the delivery of in-dialog messages for outgoing calls even if it requires the user agent to register before making a call. In this case, even if we assume that the user agent would pick the same proxy for an outgoing call as the one it has used for the last registration, at the next registration it may pick another one (as returned by DNS), and will dissociate the incoming and outgoing paths rendering the outgoing path unusable (assuming the outgoing call takes longer than the registration period). All this leads to the conclusion that a keepalive implementation based solely on sending keepalive messages to registered user agents can only work in single proxy environments and then only work reliably if it requires the user agent to register before doing anything else, even though some actions would not require a user agent to register.
Implementation To avoid the above mentioned issues, this implementation introduces the concept of network visibility for a given condition. This way we can keepalive a user agent for multiple independent conditions, thus avoiding all the problems presented above. The conditions for which the module will send keepalive messages are: Registration - for user agents that have registered to preserve their visibility for incoming calls. This is the result of triggering keepalive for a REGISTER request. Subscription - for presence agents that have subscribed to some events to preserve their visibility for receiving back notifications. This is the result of triggering keepalive for a SUBSCRIBE request. Dialogs - for user agents that have initiated an outgoing call to preserve their visibility for receiving further in-dialog messages. This is the result of triggering keepalive for an outgoing INVITE request. A user agent's NAT entry point may be kept alive for one or multiple of the conditions listed above. Even when a NAT endpoint is kept alive for more than one condition, only one keepalive message is sent to that NAT endpoint. The presence of multiple conditions for a NAT endpoint, only guarantees that the network visibility for a user agent based on a certain condition will be available while that condition is true, independently of the other conditions. When all the conditions to keepalive a NAT endpoint will disappear, that endpoint will be removed from the list with the NAT endpoints that need to be kept alive. The user interface for the keepalive functionality is very simple. It consists of a single function called nat_keepalive() that needs to be called only once for the requests that trigger the need for network visibility. These requests are: REGISTER, SUBSCRIBE and outgoing INVITEs. After such a request arrives it makes the user agent visible for the purpose of receiving back other messages. Thus, after a REGISTER the user agent may receive back incoming calls, after a SUBSCRIBE it may receive back notifications and after an outgoing INVITE it may receive back further in-dialog messages including the BYE that ends the dialog. The nat_keepalive() function needs to be called on the proxy that directly receives the request from the user agent, if it determines that the user agent making the request is behind NAT. The function needs to be called before the request gets either a stateless reply or it is relayed with t_relay(). Calling the nat_keepalive() function has no effect if the request gets no stateless reply or it is not relayed. For environments with multiple proxies, where the proxy that acts as an entry point to the network for a given request is not the one that actually handles the request, then the nat_keepalive() function needs to be called on the proxy that is the entry point and after that the request must be sent to the proxy that actually handles the request using t_relay(). This is needed because the keepalive functionality detects from the stateless replies or the TM relayed replies if the NAT endpoint needs to be kept alive for the condition triggered by the request for which the nat_keepalive() function was called. For example assume a network where a proxy P1 receives a REGISTER from an user agent behind NAT. P1 will determine that the user agent is behind NAT so it needs keepalive functionality, but another proxy called P2 is actually handling the subscriber registrations. In this case P1 has to call nat_keepalive() even though it doesn't yet know the answer P2 will give to the REGISTER request (which may even be a negative reply) or if P2 will restrict the proposed expiration time in any way. Thus P1 calls nat_keepalive() after which it calls t_relay(). When the reply from P2 arrives, a callback is triggered which will determine if the request did get a positive reply, and if so it will extract the registration expiration time and enable the keepalive functionality for that endpoint for the registration condition for the time given by the registration expiration. For single proxy environments, or if P1 is the same as P2, then t_relay() is not called, instead save_location() is called if the registration is accepted. Then the same process described above happens only this time triggered by a stateless reply callback. In both cases, calling nat_keepalive() when the REGISTER is received has no other effect that to trigger some callbacks that will determine from the reply if the caller endpoint should be kept alive or not. Below is described how nat_keepalive() should be called and what it does for each of the requests that need keepalive functionality (the function should only be called if it is determined that the user agent that generated the request is behind NAT): REGISTER - called before save_location() or t_relay() (depending on whether the proxy that received the REGISTER is also handling registration for that subscriber or not). It will determine from either the stateless reply generated by save_location() or the TM relayed reply if the registration was successful and what is its expiration time. If the registration was successful it will mark the given NAT endpoint for keepalive for the registration condition using the detected expiration time. If the REGISTER request is discarded after nat_keepalive() was called or if it intercepts a negative reply it will have no effect and the registration condition will not be activated for that endpoint. SUBSCRIBE - called before handle_subscribe() or t_relay() (depending on whether the proxy that received the SUBSCRIBE is also handling subscriptions for that subscriber or not). It will determine from either the stateless reply generated by handle_subscribe() or the TM relayed reply if the subscription was successful and what is its expiration time. If the subscription was successful it will mark the given NAT endpoint for keepalive for the subscription condition using the detected expiration time. If the SUBSCRIBE request is discarded after nat_keepalive() was called or if it intercepts a negative reply it will have no effect and the subscription condition will not be activated for that endpoint. It should be called for every SUBSCRIBE received, not only the ones that start a subscription (do not have a to tag), because it needs to update (extend) the expiration time for the subscription. INVITE - called before t_relay() for the first INVITE in a dialog. It will automatically trigger dialog tracing for that dialog and will use the dialog callbacks to detect changes in the dialog state. It will add a keepalive entry with the dialog condition for the caller NAT endpoint as soon as the dialog is created (this happens when t_relay() is called). It will then keep that condition for the given endpoint until the dialog is destroyed (either terminated, failed or expired). If the INVITE request cannot be relayed after nat_keepalive() was called it will have no effect and the dialog condition will not be activated for that endpoint. In addition an INVITE that starts a dialog will automatically trigger keepalive functionality for the destination endpoints if they are behind NAT. This is done by detecting if any of the destination endpoints already has a keepalive entry for the register condition. If so, a dialog condition will be added to that entry thus preserving that endpoint visibility even if the registration expires during the dialog or is moved to another proxy. During the call setup stage, multiple entries for the callee may be added with the dialog condition if parallel forking is used, however only the destination endpoints behind NAT will have the extra dialog condition set. Later when the dialog is confirmed, only the endpoint that answered the call will keep the dialog condition activated (if present), while all the endpoints from the unanswered branches will have it removed. This is done automatically without any need to call any function. Considering the elements presented in this section, we can say that the nat_traversal module provides a flexible and efficient keepalive functionality that is very easy to use. Because only the border proxies send keepalive messages, the network traffic is minimized. For the same reason, message processing in the proxies is also minimized, as border proxies generate keepalive messages themselves and send them statelessly, instead of having to relay messages generated by the registrars. Network traffic is also minimized by only sending a single keepalive message for an endpoint no matter for how many reasons the endpoint is kept alive. Keepalive messages are also distributed over the keepalive interval to avoid overloading the proxy by generating too many messages at a time. The nat_traversal module keeps its internal state about endpoints that need keepalive, state that is build while messages are processed by the proxy and thus it doesn't need to transfer any information from the usrloc module, which should also improve its efficiency.
Dependencies
&osips; Modules The following modules must be loaded before this module: sl module - if keepalive is enabled. tm module - if keepalive is enabled. dialog module - if keepalive is enabled and keeping alive INVITE dialogs is needed.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported parameters
<varname>keepalive_interval</varname> (integer) The time interval (in seconds) required to send a keepalive message to all the endpoints that need being kept alive. During this interval, each endpoint will receive exactly one keepalive message. A negative value or zero will disable the keepalive functionality. Default value is 60. Setting the <varname>keepalive_interval</varname> parameter ... modparam("nat_traversal", "keepalive_interval", 90) ...
<varname>keepalive_method</varname> (string) What SIP method to use to send keepalive messages. Typical methods used for this purpose are NOTIFY and OPTIONS. NOTIFY generates smaller replies from user agents, but they are almost entirely negative replies. Apparently almost none of the user agents understand that the purpose of the NOTIFY with a keep-alive event is to keep NAT open, even though many user agents send such NOTIFY requests themselves. However this does not affect the result at all, since the purpose is to trigger a response from the user agent behind NAT, positive or negative replies having little relevance as they are discarded anyway. The OPTIONS method on the other hand has a much higher rate of positive replies, but at the same time those positive replies are much bigger, mostly because the OPTIONS method is used to inform about the user agent capabilities and thus it includes a lot of extra headers to indicate those capabilities. Many user agents also include a SDP body with a bogus media session, probably to indicate media capabilities. All of this makes that positive replies to OPTIONS requests are 2 to 3 times bigger than negative replies or replies to NOTIFY requests. For this reason the default value for the used method is NOTIFY. Default value is NOTIFY. Setting the <varname>keepalive_method</varname> parameter ... modparam("nat_traversal", "keepalive_method", "OPTIONS") ...
<varname>keepalive_from</varname> (string) Indicates what SIP URI to use in the From header of the keepalive requests. If not specified it will use sip:keepalive@proxy_ip, where proxy_ip is the IP address of the outgoing interface used to send the keepalive message, which is the same interface on which the request that triggered keepalive functionality arrived. Default value is sip:keepalive@proxy_ip with proxy_ip being the actual IP of the outgoing interface. Setting the <varname>keepalive_from</varname> parameter ... modparam("nat_traversal", "keepalive_from", "sip:keepalive@my-domain.com") ...
<varname>keepalive_extra_headers</varname> (string) Specifies extra headers that should be added to the keepalive messages that are sent by the proxy. The header specification must also include the CRLF (\r\n) line separator. Multiple headers can be specified by concatenating them and each of them must include the \r\n separator. Default value is undefined (send no extra headers). Setting the <varname>keepalive_extra_headers</varname> parameter ... modparam("nat_traversal", "keepalive_extra_headers", "User-Agent: OpenSIPS\r\nX-MyHeader: some_value\r\n") ...
<varname>keepalive_state_file</varname> (string) Specifies a filename where information about the NAT endpoints and the conditions for which they are being kept alive is saved when &osips; exits. The information in this file is then used when &osips; starts to restore its internal state and continue to send keepalive messages to the NAT endpoints that have not expired in the meantime. This is useful when restarting &osips; to avoid losing keepalive state information about the NAT endpoints. The internal keepalive state is guaranteed to be saved in this file on exit, even when &osips; crashes. The value of this parameter can be either a relative path, in which case it will store it in the &osips; working directory, or an absolute path. Default value is undefined keepalive_state. Setting the <varname>keepalive_state_file</varname> parameter ... modparam("nat_traversal", "keepalive_state_file", "/var/run/opensips/keepalive_state") ...
Exported functions
<function moreinfo="none">client_nat_test(type)</function> Check if the client is behind NAT. What tests are performed is specified by the type parameter which is an integer given by the sum of the numbers corresponsing to the tests that one wishes to perform. The numbers corresponding to individual tests are shown below: 1 - tests if client has a private IP address (as defined by RFC1918) in the Contact field of the SIP message. 2 - tests if client has contacted &osips; from an address that is different from the one in the Via field. Both the IP and port are compared by this test. 4 - tests if client has a private IP address (as defined by RFC1918) in the top Via field of the SIP message. 8 - tests if client has contacted &osips; from an address that is different from the one in the Contact field. Only IP is compared by this test. For example calling client_nat_test("3") will perform test 1 and test 2 and return true if at least one succeeds, otherwise false. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Using the <function>client_nat_test</function> function ... if (client_nat_test("3")) { ..... } ...
<function moreinfo="none">fix_contact()</function> Will replace the IP and port in the Contact header with the IP and port the SIP message was received from. Usually called after a successful call to client_nat_test(type) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE. Using the <function>fix_contact</function> function ... if (client_nat_test("3")) { fix_contact(); } ...
<function moreinfo="none">nat_keepalive()</function> Trigger keepalive functionality for the source address of the request. When called it only sets some internal flags, which will trigger later the addition of the endpoint to the keepalive list if a positive reply is generated/received (for REGISTER and SUBSCRIBE) or when the dialog is started/replied (for INVITEs). For this reason, it can be called early or late in the script. The only condition is to call it before replying to the request or before sending it to another proxy. If the request needs to be sent to another proxy, t_relay() must be used to be able to intercept replies via TM or dialog callbacks. If stateless forwarding is used, the keepalive functionality will not work. Also for outgoing INVITEs, record_route() should also be used to make sure the proxy that keeps the caller endpoint alive stays in the path. For multi-proxy setups, this function should always be called on the border proxies (the ones that received the request directly from the user agent). For more details about this function, see the Implementation subsection from the Keepalive functionality section. This function can be used from REQUEST_ROUTE. Using the <function>nat_keepalive</function> function ... if ((method=="REGISTER" || method=="SUBSCRIBE" || (method=="INVITE" && !has_totag())) && client_nat_test("3")) { nat_keepalive(); } ...
Exported statistics
<varname>keepalive_endpoints</varname> Indicates the total number of NAT endpoints that are being kept alive.
<varname>registered_endpoints</varname> Indicates how many of the NAT endpoints are kept alive for registrations.
<varname>subscribed_endpoints</varname> Indicates how many of the NAT endpoints are kept alive for subscriptions.
<varname>dialog_endpoints</varname> Indicates how many of the NAT endpoints are kept alive for taking part in an INVITE dialog.
Exported pseudo-variables
<varname>$keepalive.socket(nat_endpoint)</varname> Returns the local socket used to send messages to the given NAT endpoint URI. The socket has the form proto:ip:port. The NAT endpoint URI is in the form: sip:ip:port[;transport=xxx] with transport missing if UDP. If the requested NAT endpoint URI is present in the internal keepalive table for any condition, it will return its associated local socket, else it will return null. The nat_endpoint can be a string or another pseudo-variable. This can be useful to restore the sending socket when relaying messages to a given user agent in multi-proxy environments. Consider an example where 2 proxies are involved, P1 and P2. A user agent registers by sending a REGISTER request to P1. P1 will call nat_keepalive() but because it determines that P2 should actually handle the user registration will forward the request to P2. Now assume P2 receives an incoming INVITE for this user. It will determine that the registration came through P1 and will forward the request to P1. P2 should also include the NAT endpoint URI where this request is to be relayed. This information should have been provided by P1 when it relayed the REGISTER request to P2. The means to do this is out of the scope of this example, but one can either use the path extension or custom headers to do this. When P1 receives the INVITE it will use the NAT endpoint URI it has received along with the request to determine the socket to send out the request, which should be the same as the one where the registration request was originally received. In the example below lets assume that P2 provided the original NAT endpoint address in a custom header called X-NAT-URI and that it also provides a custom header called X-Scope to indicate that the message is sent to P1 for being relayed back to the user agent by P1 which has the NAT open with it. Using <varname>$keepalive.socket</varname> in multi-proxy environments ... # This code runs on P1 which has received an INVITE from P2 to forward # it to the user agent behind NAT (because P1 has the NAT open with it). if (method=="INVITE" && $hdr(X-Scope)=="nat-relay") { $du = $hdr(X-NAT-URI); $fs = $keepalive.socket($du); t_relay(); exit; } ...
<varname>$source_uri</varname> Returns the URI specification from where a request was received in the form sip:ip:port[;transport=xxx] with transport missing if UDP. This pseudo-variable can be used to set the received AVP for the registrar module to indicate that a user agent is behind NAT. This is meant as a more flexible replacement for the fix_nated_register() function, because it allows one to modify the source uri by appending some extra parameters before saving it to the received AVP. Another use for this pseudo-variable is in multi-proxy environments to indicate the NAT endpoint URI to the next proxy (if needed). Consider the previous example with two proxies P1 and P2. P1 receives the REGISTER request from a user agent and forwards it to P2 which does the actual registration. P1 needs to indicate the NAT endpoint URI to P2, so that P2 can include it later for incoming INVITE requests to this user agent. Using <varname>$source_uri</varname> to set the received AVP on registrars ... modparam("registrar", "received_avp", "$avp(received_uri)") modparam("registrar", "tcp_persistent_flag", 10) ... # This code runs on the registrar, assuming it has received the # REGISTER request directly from the user agent. if (method=="REGISTER") { if (client_nat_test("3")) { if (proto==UDP) { nat_keepalive(); } else { # Keep TCP/TLS connections open until the registration # expires, by setting the tcp_persistent_flag setflag(10); } force_rport(); $avp(received_uri) = $source_uri; # or we could add some extra parameters to it if needed # $avp(received_uri) = $source_uri + ";relayed=false" } if (!www_authorize("", "subscriber")) { www_challenge("", "0"); return; } else if (!db_check_to()) { sl_send_reply("403", "Username!=To not allowed ($au!=$tU)"); return; } if (!save("location")) { sl_reply_error(); } exit; } ... Using <varname>$source_uri</varname> in multi-proxy environments ... # This code runs on P1 which received the REGISTER request and has to # forward it to the registrar P2. if (method=="REGISTER") { if (client_nat_test("3")) { force_rport(); nat_keepalive(); append_hf("X-NAT-URI: $source_uri\r\n"); } $du = "sip:P2_ip:P2_port"; t_relay(); exit; } ...
<varname>$nat_traversal.track_dialog</varname> Returns a boolean value (0 or 1) indicating if dialog tracking will be enabled by the nat_traversal module. The nat_traversal module will always track the dialog (by calling create_dialog internally) unless told otherwise. This is an advanced setting which is only meant to be used by multi-proxy setups where a proxy doesn't want to keep track of a dialog, that is, if it won't stay in the signaling path. By setting this pv to 0 the nat_traversal module will not attempt to create the dialog.
Keepalive use cases
Single proxy environments In this case the usage is straight forward. The nat_keepalive() function needs to be called before save_location() for REGISTER requests, before handle_subscribe() for SUBSCRIBE requests and before t_relay() for the first INVITE of a dialog.
Registration in multi-proxy environments If the proxy receiving the REGISTER request is the same as the proxy handling it, then the case is reduced to the single proxy case. For this example, lets assume they are different. We have a user agent UA1 for which the registration is handled by the proxy P1. However UA1 sends the REGISTER to P0 which in turn forwards it to P1 like this: UA1 --> P0 --> P1. In this case P0 calls nat_keepalive(), adds the NAT endpoint URI to the request (for example using a custom header) and forwards the request to P1. P1 will save the user in the user location together with the NAT endpoint URI. When an incoming INVITE request arrives on P1 for UA1, P1, will lookup the location and determine that it has to relay it to P0 because P0 has the NAT open with UA1. P1 will include the original NAT endpoint URI in the request and an indication that the only role P0 has in this transaction is to relay it to UA1. P0 will receive this request and determine that is has to act as a relay for it. It will extract the NAT endpoint URI, then based on it the corresponding local socket using $keepalive.socket(endpoint_uri). It will then set both $du and $fs to the values it has found, call record_route() to stay in the path and call t_relay() to send it to UA1. Handling other type of requests (like for example SUBSCRIBE or MESSAGE) that arrive on P1 for UA1 is done the same way as with the first INVITE, on both P1 and P0.
Subscription in multi-proxy environments If the proxy receiving the SUBSCRIBE request is the same as the proxy handling it, then the case is reduced to the single proxy case. For this example, lets assume they are different. We have a user agent UA1 for which subscriptions are handled by the proxy P1. However UA1 sends the SUBSCRIBE to P0 which in turn forwards it to P1 like this: UA1 --> P0 --> P1. In this case P0 calls nat_keepalive(), then calls record_route() to stay in the path and forwards the request to P1 using t_relay(). Further SUBSCRIBE and NOTIFY requests will follow the record route and use P0 as a NAT entry point to have access to UA1. Further in-dialog SUBSCRIBE requests should also call record_route().
Outgoing INVITEs in multi-proxy environments If the proxy receiving the INVITE request is the same as the proxy handling it, then the case is reduced to the single proxy case. For this example, lets assume they are different. We have a user agent UA1 which is handled by the proxy P1 and UA2 which is handled by P2. UA2 has registered with P2 going through P3, while UA1 calls UA2 by sending the first INVITE to P0. The call flow for the first INVITE looks like this: UA1 --> P0 --> P1 --> P2 --> P3 --> UA2. In this case P0 calls nat_keepalive(), then calls record_route() to stay in the path and forwards the request to P1. P1 authenticates UA1 then forwards the request to P2, which is the home proxy for UA2. P1 doesn't have to use record_route to stay in the path, but it can do that if needed for other purposes. P2 will lookup UA2 and find out that it is reachable through P3. It will take the original NAT endpoint URI that is has saved in the user location when UA2 has registered and include it in the message along with an indication that P3 only has to relay the message to UA2. If P2 does accounting or starts a media relay, it should also call record_route() to stay in the path. Then it forwards the request to P3 using t_relay(). P3 will detect that it only has to relay the request to UA2 because it has the NAT open with it. It will extract the NAT endpoint URI from the message and the local sending socket using $keepalive.socket(endpoint_uri) and will set both $du and $fs. After that it will call record_route() to stay in the path, and forward the request to UA2 using t_relay(). Further in-dialog requests will follow the recorded route and use P0 and P3 as access points to UA1 respectively UA2. All the proxies that have used record_route() during the first INVITE should also call record_route() during further in-dialog requests to keep staying in the path.
opensips-2.2.2/modules/nat_traversal/nat_traversal.c000066400000000000000000001617361300170765700227270ustar00rootroot00000000000000/* * Copyright (C) 2007-2009 Dan Pascu * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../lock_ops.h" #include "../../dprint.h" #include "../../str.h" #include "../../pvar.h" #include "../../error.h" #include "../../timer.h" #include "../../resolve.h" #include "../../data_lump.h" #include "../../mod_fix.h" #include "../../script_cb.h" #include "../../sl_cb.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_expires.h" #include "../../parser/contact/parse_contact.h" #include "../dialog/dlg_load.h" #include "../tm/tm_load.h" #if defined(__GNUC__) && !defined(__STRICT_ANSI__) # define INLINE inline #else # define INLINE #endif #define HASH_SIZE 512 #define max(a, b) ((a)>(b) ? (a) : (b)) #define min(a, b) ((a)<(b) ? (a) : (b)) #define STR_MATCH(str, buf) ((str).len==strlen(buf) && memcmp(buf, (str).s, (str).len)==0) #define STR_IMATCH(str, buf) ((str).len==strlen(buf) && strncasecmp(buf, (str).s, (str).len)==0) #define STR_MATCH_STR(str, str2) ((str).len==(str2).len && memcmp((str).s, (str2).s, (str).len)==0) #define STR_IMATCH_STR(str, str2) ((str).len==(str2).len && strncasecmp((str).s, (str2).s, (str).len)==0) #define STR_HAS_PREFIX(str, prefix) ((str).len>(prefix).len && memcmp((prefix).s, (str).s, (prefix).len)==0) #define STR_HAS_IPREFIX(str, prefix) ((str).len>(prefix).len && strncasecmp((prefix).s, (str).s, (prefix).len)==0) typedef int Bool; #define True 1 #define False 0 typedef Bool (*NatTestFunction)(struct sip_msg *msg); typedef enum { NTNone=0, NTPrivateContact=1, NTSourceAddress=2, NTPrivateVia=4, NTSourceContact=8 } NatTestType; typedef struct { NatTestType test; NatTestFunction proc; } NatTest; typedef struct { const char *name; uint32_t address; uint32_t mask; } NetInfo; typedef struct SIP_Dialog { struct dlg_cell *dlg; time_t expire; struct SIP_Dialog *next; } SIP_Dialog; typedef struct NAT_Contact { char *uri; struct socket_info *socket; time_t registration_expire; time_t subscription_expire; SIP_Dialog *dialogs; struct NAT_Contact *next; } NAT_Contact; typedef struct HashSlot { NAT_Contact *head; // pointer to the head of the linked list stored in this slot gen_lock_t lock; } HashSlot; typedef struct HashTable { HashSlot *slots; unsigned size; // table size (number of slots) } HashTable; #define URI_LIST_INITIAL_SIZE 8 #define URI_LIST_RESIZE_INCREMENT 8 typedef struct Dialog_Param { char *caller_uri; char *callee_uri; time_t expire; Bool confirmed; gen_lock_t lock; struct { char **uri; int count; int size; } callee_candidates; } Dialog_Param; // Module parameters // typedef struct Keepalive_Params { // user specified char *method; char *from; char *extra_headers; // internally generated char callid_prefix[20]; unsigned callid_counter; unsigned from_tag; char *event_header; // this will be set if method is NOTIFY } Keepalive_Params; // Function prototypes // static int NAT_Keepalive(struct sip_msg *msg); static int FixContact(struct sip_msg *msg); static int ClientNatTest(struct sip_msg *msg, unsigned int tests); static Bool test_private_contact(struct sip_msg *msg); static Bool test_source_address(struct sip_msg *msg); static Bool test_private_via(struct sip_msg *msg); static Bool test_source_contact(struct sip_msg *msg); static INLINE char* shm_strdup(char *source); static int mod_init(void); static int child_init(int rank); static void mod_destroy(void); static int preprocess_request(struct sip_msg *msg, void *param); static int reply_filter(struct sip_msg *reply); static int pv_parse_nat_contact_name(pv_spec_p sp, str *in); static int pv_get_keepalive_socket(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int pv_get_source_uri(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int pv_get_track_dialog(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int pv_set_track_dialog(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); // Module global variables and state // static HashTable *nat_table = NULL; static Bool keepalive_disabled = False; static unsigned int keepalive_interval = 60; static char *keepalive_state_file = "keepalive_state"; static Keepalive_Params keepalive_params = {"NOTIFY", NULL, "", "", 0, 0, ""}; struct tm_binds tm_api; struct dlg_binds dlg_api; Bool have_dlg_api = False; static unsigned dialog_default_timeout = 12*3600; // 12 hours stat_var *keepalive_endpoints = 0; stat_var *registered_endpoints = 0; stat_var *subscribed_endpoints = 0; stat_var *dialog_endpoints = 0; static NetInfo rfc1918nets[] = { {"10.0.0.0", 0x0a000000UL, 0xff000000UL}, {"172.16.0.0", 0xac100000UL, 0xfff00000UL}, {"192.168.0.0", 0xc0a80000UL, 0xffff0000UL}, {NULL, 0UL, 0UL} }; static NatTest NAT_Tests[] = { {NTPrivateContact, test_private_contact}, {NTSourceAddress, test_source_address}, {NTPrivateVia, test_private_via}, {NTSourceContact, test_source_contact}, {NTNone, NULL} }; static cmd_export_t commands[] = { {"nat_keepalive", (cmd_function)NAT_Keepalive, 0, NULL, 0, REQUEST_ROUTE}, {"fix_contact", (cmd_function)FixContact, 0, NULL, 0, REQUEST_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE |LOCAL_ROUTE}, {"client_nat_test", (cmd_function)ClientNatTest, 1, fixup_uint_null, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t parameters[] = { {"keepalive_interval", INT_PARAM, &keepalive_interval}, {"keepalive_method", STR_PARAM, &keepalive_params.method}, {"keepalive_from", STR_PARAM, &keepalive_params.from}, {"keepalive_extra_headers", STR_PARAM, &keepalive_params.extra_headers}, {"keepalive_state_file", STR_PARAM, &keepalive_state_file}, {0, 0, 0} }; static pv_export_t pvars[] = { {str_init("keepalive.socket"), 1000, pv_get_keepalive_socket, NULL, pv_parse_nat_contact_name, NULL, NULL, 0}, {str_init("source_uri"), 1000, pv_get_source_uri, NULL, NULL, NULL, NULL, 0}, {str_init("nat_traversal.track_dialog"), 1000, pv_get_track_dialog, pv_set_track_dialog, NULL, NULL, NULL, 0}, {{0, 0}, 0, 0, 0, 0, 0, 0, 0} }; #ifdef STATISTICS static stat_export_t statistics[] = { {"keepalive_endpoints", STAT_NO_RESET, &keepalive_endpoints}, {"registered_endpoints", STAT_NO_RESET, ®istered_endpoints}, {"subscribed_endpoints", STAT_NO_RESET, &subscribed_endpoints}, {"dialog_endpoints", STAT_NO_RESET, &dialog_endpoints}, {0, 0, 0} }; #endif static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "sl", DEP_ABORT }, { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_DEFAULT, "dialog", DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "nat_traversal", // module name MOD_TYPE_DEFAULT,// class of this module MODULE_VERSION, // module version DEFAULT_DLFLAGS, // dlopen flags &deps, // OpenSIPS module dependencies commands, // exported functions 0, // exported async functions parameters, // exported parameters NULL, // exported statistics (initialized early in mod_init) NULL, // exported MI functions pvars, // exported pseudo-variables NULL, // extra processes mod_init, // module init function (before fork. kids will inherit) reply_filter, // reply processing function mod_destroy, // destroy function child_init // child init function }; // SIP_Dialog structure handling functions // static SIP_Dialog* SIP_Dialog_new(struct dlg_cell *dlg, time_t expire) { SIP_Dialog *dialog; dialog = (SIP_Dialog*)shm_malloc(sizeof(SIP_Dialog)); if (!dialog) { LM_ERR("out of memory while creating new SIP_Dialog structure\n"); return NULL; } dialog->dlg = dlg; dialog->expire = expire; dialog->next = NULL; // we assume expire is always strictly positive on new dialogs update_stat(dialog_endpoints, 1); return dialog; } static void SIP_Dialog_del(SIP_Dialog *dialog) { if (!dialog) return; if (dialog->expire > 0) update_stat(dialog_endpoints, -1); shm_free(dialog); } // Purge expired dialogs from the linked list pointed by dialog // static SIP_Dialog* SIP_Dialog_purge_expired(SIP_Dialog *dialog, time_t now) { SIP_Dialog *next; if (dialog==NULL) return NULL; dialog->next = SIP_Dialog_purge_expired(dialog->next, now); if (now > dialog->expire) { next = dialog->next; SIP_Dialog_del(dialog); return next; } return dialog; } static INLINE void SIP_Dialog_end(SIP_Dialog *dialog) { if (dialog->expire > 0) { dialog->expire = 0; update_stat(dialog_endpoints, -1); } } // Helpers to handle registration and subscription timeouts for NAT_Contacts // static INLINE void SIP_Registration_update(NAT_Contact *contact, time_t expire) { if (expire > contact->registration_expire) { if (contact->registration_expire == 0) update_stat(registered_endpoints, 1); contact->registration_expire = expire; } } static INLINE void SIP_Registration_expire(NAT_Contact *contact, time_t now) { if (contact->registration_expire && now > contact->registration_expire) { update_stat(registered_endpoints, -1); contact->registration_expire = 0; } } static INLINE void SIP_Subscription_update(NAT_Contact *contact, time_t expire) { if (expire > contact->subscription_expire) { if (contact->subscription_expire == 0) update_stat(subscribed_endpoints, 1); contact->subscription_expire = expire; } } static INLINE void SIP_Subscription_expire(NAT_Contact *contact, time_t now) { if (contact->subscription_expire && now > contact->subscription_expire) { update_stat(subscribed_endpoints, -1); contact->subscription_expire = 0; } } // NAT_Contact structure handling functions // static NAT_Contact* NAT_Contact_new(char *uri, struct socket_info *socket) { NAT_Contact *contact; contact = (NAT_Contact*)shm_malloc(sizeof(NAT_Contact)); if (!contact) { LM_ERR("out of memory while creating new NAT_Contact structure\n"); return NULL; } memset(contact, 0, sizeof(NAT_Contact)); contact->uri = shm_strdup(uri); if (!contact->uri) { LM_ERR("out of memory while creating new NAT_Contact structure\n"); shm_free(contact); return NULL; } contact->socket = socket; update_stat(keepalive_endpoints, 1); return contact; } static void NAT_Contact_del(NAT_Contact *contact) { SIP_Dialog *dialog, *next; if (!contact) return; dialog = contact->dialogs; while (dialog) { next = dialog->next; SIP_Dialog_del(dialog); dialog = next; } if (contact->registration_expire > 0) update_stat(registered_endpoints, -1); if (contact->subscription_expire > 0) update_stat(subscribed_endpoints, -1); update_stat(keepalive_endpoints, -1); shm_free(contact->uri); shm_free(contact); } static Bool NAT_Contact_match(NAT_Contact *contact, const char *uri) { return strcmp(contact->uri, uri)==0; } static SIP_Dialog* NAT_Contact_get_dialog(NAT_Contact *contact, struct dlg_cell *dlg) { SIP_Dialog *dialog; dialog = contact->dialogs; while (dialog) { if (dialog->dlg == dlg) break; dialog = dialog->next; } return dialog; } static NAT_Contact* NAT_Contact_purge_expired(NAT_Contact *contact, time_t now) { NAT_Contact *next; if (contact==NULL) return NULL; contact->next = NAT_Contact_purge_expired(contact->next, now); SIP_Registration_expire(contact, now); SIP_Subscription_expire(contact, now); contact->dialogs = SIP_Dialog_purge_expired(contact->dialogs, now); if (!contact->registration_expire && !contact->subscription_expire && !contact->dialogs) { next = contact->next; NAT_Contact_del(contact); return next; } return contact; } // HashTable structure manipulation // #define HASH(table, key) (hash_string(key) % (table)->size) static INLINE unsigned hash_string(const char *key) { register unsigned ret = 0; register unsigned ctr = 0; while (*key) { ret ^= *(char*)key++ << ctr; ctr = (ctr + 1) % sizeof (char *); } return ret; } static HashTable* HashTable_new(void) { HashTable *table; int i, j; table = shm_malloc(sizeof(HashTable)); if (!table) { LM_ERR("cannot allocate shared memory for hash table\n"); return NULL; } memset(table, 0, sizeof(HashTable)); table->size = HASH_SIZE; table->slots = shm_malloc(sizeof(HashSlot)*table->size); if (!table->slots) { LM_ERR("cannot allocate shared memory for hash table\n"); shm_free(table); return NULL; } memset(table->slots, 0, sizeof(HashSlot)*table->size); for (i=0; isize; i++) { if (!lock_init(&table->slots[i].lock)) { LM_ERR("cannot initialize hash table locks\n"); for (j=0; jslots[j].lock); shm_free(table->slots); shm_free(table); return NULL; } } return table; } static void HashTable_del(HashTable *table) { NAT_Contact *contact, *next; int i; for (i=0; i < table->size; i++) { lock_get(&table->slots[i].lock); contact = table->slots[i].head; while (contact) { next = contact->next; NAT_Contact_del(contact); contact = next; } table->slots[i].head = NULL; lock_release(&table->slots[i].lock); } shm_free(table->slots); shm_free(table); } // This function assumes that the caller has locked the slot already // static NAT_Contact* HashTable_search(HashTable *table, char *uri, unsigned slot) { NAT_Contact *contact; contact = table->slots[slot].head; while (contact) { if (NAT_Contact_match(contact, uri)) break; contact = contact->next; } return contact; } // Dialog_Param structure handling functions // static Dialog_Param* Dialog_Param_new(void) { Dialog_Param *param; param = shm_malloc(sizeof(Dialog_Param)); if (!param) { LM_ERR("cannot allocate shared memory for dialog callback param\n"); return NULL; } memset(param, 0, sizeof(Dialog_Param)); param->callee_candidates.uri = shm_malloc(sizeof(char*) * URI_LIST_INITIAL_SIZE); if (!param->callee_candidates.uri) { LM_ERR("cannot allocate shared memory for callee_candidates uri list\n"); shm_free(param); return NULL; } memset(param->callee_candidates.uri, 0, sizeof(char*) * URI_LIST_INITIAL_SIZE); param->callee_candidates.size = URI_LIST_INITIAL_SIZE; param->expire = time(NULL) + dialog_default_timeout; if (!lock_init(¶m->lock)) { LM_ERR("cannot initialize dialog param structure lock\n"); shm_free(param->callee_candidates.uri); shm_free(param); return NULL; } return param; } static void Dialog_Param_del(Dialog_Param *param) { int i; if (!param) return; lock_destroy(¶m->lock); if (param->caller_uri) shm_free(param->caller_uri); if (param->callee_uri) shm_free(param->callee_uri); for (i=0; icallee_candidates.count; i++) shm_free(param->callee_candidates.uri[i]); shm_free(param->callee_candidates.uri); shm_free(param); } // This function assumes the caller has locked the Dialog_Param while operating on it // static Bool Dialog_Param_has_candidate(Dialog_Param *param, char *candidate) { int i; for (i=0; icallee_candidates.count; i++) { if (strcmp(candidate, param->callee_candidates.uri[i])==0) { return True; } } return False; } // This function assumes the caller has locked the Dialog_Param while operating on it // static Bool Dialog_Param_add_candidate(Dialog_Param *param, char *candidate) { char **new_uri, *new_candidate; int new_size; if (param->callee_candidates.count == param->callee_candidates.size) { new_size = param->callee_candidates.size + URI_LIST_RESIZE_INCREMENT; LM_DBG("growing callee_candidates list size from %d to %d entries\n", param->callee_candidates.size, new_size); new_uri = shm_realloc(param->callee_candidates.uri, sizeof(char*) * new_size); if (!new_uri) { LM_ERR("failed to grow callee_candidates uri list\n"); return False; } param->callee_candidates.uri = new_uri; param->callee_candidates.size = new_size; } new_candidate = shm_strdup(candidate); if (!new_candidate) { LM_ERR("cannot allocate shared memory for new candidate uri\n"); return False; } param->callee_candidates.uri[param->callee_candidates.count] = new_candidate; param->callee_candidates.count++; return True; } // Miscellaneous helper functions // static INLINE char* shm_strdup(char *source) { char *copy; if (!source) return NULL; copy = (char*)shm_malloc(strlen(source) + 1); if (!copy) return NULL; strcpy(copy, source); return copy; } static Bool get_contact_uri(struct sip_msg* msg, struct sip_uri *uri, contact_t **_c) { if ((parse_headers(msg, HDR_CONTACT_F, 0) == -1) || !msg->contact) return False; if (!msg->contact->parsed && parse_contact(msg->contact) < 0) { LM_ERR("cannot parse the Contact header\n"); return False; } *_c = ((contact_body_t*)msg->contact->parsed)->contacts; if (*_c == NULL) { return False; } if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) { LM_ERR("cannot parse the Contact URI\n"); return False; } return True; } #define is_private_address(x) (rfc1918address(x)==1 ? 1 : 0) // Test if IP in `address' belongs to a RFC1918 network static INLINE int rfc1918address(str *address) { struct ip_addr *ip; uint32_t netaddr; int i; ip = str2ip(address); if (ip == NULL) return -1; // invalid address to test netaddr = ntohl(ip->u.addr32[0]); for (i=0; rfc1918nets[i].name!=NULL; i++) { if ((netaddr & rfc1918nets[i].mask)==rfc1918nets[i].address) { return 1; } } return 0; } // Test if address of signaling is different from address in 1st Via field static Bool test_source_address(struct sip_msg *msg) { Bool different_ip, different_port; int via1_port; different_ip = received_test(msg); via1_port = (msg->via1->port ? msg->via1->port : SIP_PORT); different_port = (msg->rcv.src_port != via1_port); return (different_ip || different_port); } // Test if Contact field contains a private IP address as defined in RFC1918 static Bool test_private_contact(struct sip_msg *msg) { struct sip_uri uri; contact_t* contact; if (!get_contact_uri(msg, &uri, &contact)) return False; return is_private_address(&(uri.host)); } // Test if top Via field contains a private IP address as defined in RFC1918 static Bool test_private_via(struct sip_msg *msg) { return is_private_address(&(msg->via1->host)); } // Test if Contact field contains the same IP as the received one static Bool test_source_contact(struct sip_msg *msg) { struct sip_uri uri; contact_t* contact; if (!get_contact_uri(msg, &uri, &contact)) return False; return check_ip_address(&msg->rcv.src_ip, &uri.host, uri.port_no, uri.proto, received_dns); } // return the Expires header value (converted to an UNIX timestamp if > 0) static int get_expires(struct sip_msg *msg) { exp_body_t *expires; if (parse_headers(msg, HDR_EXPIRES_F, 0) < 0) { LM_ERR("failed to parse the Expires header\n"); return 0; } if (!msg->expires) return 0; if (parse_expires(msg->expires) < 0) { LM_ERR("failed to parse the Expires header body\n"); return 0; } expires = (exp_body_t*)msg->expires->parsed; return ((expires->valid && expires->val) ? expires->val + time(NULL) : 0); } // return the highest expire value from all registered contacts in the request static time_t get_register_expire(struct sip_msg *request, struct sip_msg *reply) { struct hdr_field contact_hdr, *hdr, *r_hdr; contact_body_t *contact_body, *r_contact_body; contact_t *contact, *r_contact; param_t *expires_param; time_t now, expire=0; unsigned exp; Bool matched; if (!request->contact) return 0; if (parse_headers(reply, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse headers for REGISTER reply\n"); return 0; } if (!reply->contact) return 0; now = time(NULL); // request may be R/O (if we are called from the TM callback), // thus we copy the hdr_field structures before parsing them for (hdr=request->contact; hdr; hdr=hdr->sibling) { if (!hdr->parsed) { memcpy(&contact_hdr, hdr, sizeof(struct hdr_field)); if (parse_contact(&contact_hdr) < 0) { LM_ERR("failed to parse the Contact header body\n"); continue; } contact_body = (contact_body_t*)contact_hdr.parsed; } else { contact_body = (contact_body_t*)hdr->parsed; } if (contact_body->star) { if (!hdr->parsed) clean_hdr_field(&contact_hdr); return 0; } for (contact=contact_body->contacts; contact; contact=contact->next) { for (r_hdr=reply->contact, matched=False; r_hdr && !matched; r_hdr=r_hdr->sibling) { if (!r_hdr->parsed && parse_contact(r_hdr) < 0) { LM_ERR("failed to parse the Contact header body in reply\n"); continue; } r_contact_body = (contact_body_t*)r_hdr->parsed; for (r_contact=r_contact_body->contacts; r_contact; r_contact=r_contact->next) { if (STR_MATCH_STR(contact->uri, r_contact->uri)) { expires_param = r_contact->expires; if (expires_param && expires_param->body.len && str2int(&expires_param->body, &exp) == 0) expire = max(expire, exp); matched = True; break; } } } } if (!hdr->parsed) { clean_hdr_field(&contact_hdr); } } LM_DBG("maximum expire for all contacts: %u\n", (unsigned)expire); return (expire ? expire + now : 0); } static char* get_source_uri(struct sip_msg *msg) { static char uri[64]; snprintf(uri, 64, "sip:%s:%d", ip_addr2a(&msg->rcv.src_ip), msg->rcv.src_port); return uri; } static void keepalive_registration(struct sip_msg *request, time_t expire) { NAT_Contact *contact; unsigned h; char *uri; uri = get_source_uri(request); h = HASH(nat_table, uri); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, uri, h); if (contact) { SIP_Registration_update(contact, expire); } else { contact = NAT_Contact_new(uri, request->rcv.bind_address); if (contact) { SIP_Registration_update(contact, expire); contact->next = nat_table->slots[h].head; nat_table->slots[h].head = contact; } else { LM_ERR("cannot allocate shared memory for new NAT contact\n"); } } lock_release(&nat_table->slots[h].lock); } static void keepalive_subscription(struct sip_msg *request, time_t expire) { NAT_Contact *contact; unsigned h; char *uri; uri = get_source_uri(request); h = HASH(nat_table, uri); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, uri, h); if (contact) { SIP_Subscription_update(contact, expire); } else { contact = NAT_Contact_new(uri, request->rcv.bind_address); if (contact) { SIP_Subscription_update(contact, expire); contact->next = nat_table->slots[h].head; nat_table->slots[h].head = contact; } else { LM_ERR("cannot allocate shared memory for new NAT contact\n"); } } lock_release(&nat_table->slots[h].lock); } static void __dialog_early(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { Dialog_Param *param = (Dialog_Param*)*_params->param; NAT_Contact *contact; SIP_Dialog *dialog; unsigned h; char *uri; lock_get(¶m->lock); if (param->confirmed) { // this 1xx is late; dialog already confirmed by 200 OK; ignore it lock_release(¶m->lock); return; } if(_params->msg == FAKED_REPLY) { LM_ERR("FAKED reply - exit\n"); lock_release(¶m->lock); return; } uri = get_source_uri(_params->msg); if (!Dialog_Param_has_candidate(param, uri)) { if (!Dialog_Param_add_candidate(param, uri)) { LM_ERR("cannot add callee candidate uri to the list\n"); } else { h = HASH(nat_table, uri); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, uri, h); if (contact) { dialog = NAT_Contact_get_dialog(contact, dlg); if (!dialog) { dialog = SIP_Dialog_new(dlg, param->expire); if (dialog) { dialog->next = contact->dialogs; contact->dialogs = dialog; } else { LM_ERR("cannot allocate shared memory for new SIP dialog\n"); } } } lock_release(&nat_table->slots[h].lock); } } lock_release(¶m->lock); } static void __dialog_confirmed(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { Dialog_Param *param = (Dialog_Param*)*_params->param; NAT_Contact *contact; SIP_Dialog *dialog; char *callee_uri, *uri; unsigned h; int i; lock_get(¶m->lock); param->confirmed = True; if(_params->msg == FAKED_REPLY) { LM_ERR("FAKED reply - exit\n"); lock_release(¶m->lock); return; } callee_uri = get_source_uri(_params->msg); // remove all keepalives on unanswered branches for (i=0; icallee_candidates.count; i++) { uri = param->callee_candidates.uri[i]; if (strcmp(uri, callee_uri) != 0) { // this is an unanswered branch h = HASH(nat_table, uri); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, uri, h); if (contact) { dialog = NAT_Contact_get_dialog(contact, dlg); if (dialog) { SIP_Dialog_end(dialog); } } lock_release(&nat_table->slots[h].lock); } shm_free(param->callee_candidates.uri[i]); param->callee_candidates.uri[i] = NULL; } param->callee_candidates.count = 0; // add dialog keepalive for answered branch, if needed and not already there h = HASH(nat_table, callee_uri); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, callee_uri, h); if (contact) { dialog = NAT_Contact_get_dialog(contact, dlg); if (!dialog) { dialog = SIP_Dialog_new(dlg, param->expire); if (dialog) { dialog->next = contact->dialogs; contact->dialogs = dialog; } else { LM_ERR("cannot allocate shared memory for new SIP dialog\n"); } } // free old uri in case this callback is called more than once (shouldn't normally happen) if (param->callee_uri) shm_free(param->callee_uri); param->callee_uri = shm_strdup(callee_uri); if (!param->callee_uri) { LM_ERR("cannot allocate shared memory for callee_uri in dialog param\n"); } } lock_release(&nat_table->slots[h].lock); lock_release(¶m->lock); } static void __dialog_destroy(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { Dialog_Param *param = (Dialog_Param*)*_params->param; NAT_Contact *contact; SIP_Dialog *dialog; unsigned h; int i; if (!param) return; // If nat_table is NULL, it's because it was already removed during // shutdown by mod_destroy. However we can still receive dialog destroy // notifications when the dialog module removes dialogs on shutdown. if (!nat_table) { Dialog_Param_del(param); *_params->param = NULL; return; } if (param->caller_uri) { h = HASH(nat_table, param->caller_uri); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, param->caller_uri, h); if (contact) { dialog = NAT_Contact_get_dialog(contact, dlg); if (dialog) { SIP_Dialog_end(dialog); } } lock_release(&nat_table->slots[h].lock); } if (param->callee_uri) { h = HASH(nat_table, param->callee_uri); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, param->callee_uri, h); if (contact) { dialog = NAT_Contact_get_dialog(contact, dlg); if (dialog) { SIP_Dialog_end(dialog); } } lock_release(&nat_table->slots[h].lock); } lock_get(¶m->lock); // remove all keepalives on unanswered branches. this is neded because // we may transit from early to ended without going through confirmed for (i=0; icallee_candidates.count; i++) { h = HASH(nat_table, param->callee_candidates.uri[i]); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, param->callee_candidates.uri[i], h); if (contact) { dialog = NAT_Contact_get_dialog(contact, dlg); if (dialog) { SIP_Dialog_end(dialog); } } lock_release(&nat_table->slots[h].lock); shm_free(param->callee_candidates.uri[i]); param->callee_candidates.uri[i] = NULL; } param->callee_candidates.count = 0; lock_release(¶m->lock); Dialog_Param_del(param); *_params->param = NULL; } // callback to handle all SL generated replies // static void __sl_reply_out(struct sip_msg* request, str *buffer, int rpl_code, union sockaddr_union *dst, struct socket_info *sock, int proto) { struct sip_msg reply; time_t expire; if (request->REQ_METHOD == METHOD_INVITE) return; if (rpl_code >= 200 && rpl_code < 300) { memset(&reply, 0, sizeof(struct sip_msg)); reply.buf = buffer->s; reply.len = buffer->len; if (parse_msg(buffer->s, buffer->len, &reply) != 0) { LM_ERR("cannot parse outgoing SL reply for keepalive information\n"); return; } switch (request->REQ_METHOD) { case METHOD_SUBSCRIBE: expire = get_expires(&reply); if (expire > 0) keepalive_subscription(request, expire); break; case METHOD_REGISTER: expire = get_register_expire(request, &reply); if (expire > 0) keepalive_registration(request, expire); break; default: LM_ERR("called with keepalive flag set for unsupported method\n"); break; } free_sip_msg(&reply); } } // callback to handle incoming replies for the request's transaction // static void __tm_reply_in(struct cell *trans, int type, struct tmcb_params *param) { time_t expire; if (param->req==NULL || param->rpl==NULL) return; if (param->code >= 200 && param->code < 300) { switch (param->req->REQ_METHOD) { case METHOD_SUBSCRIBE: expire = get_expires(param->rpl); if (expire > 0) keepalive_subscription(param->req, expire); break; case METHOD_REGISTER: expire = get_register_expire(param->req, param->rpl); if (expire > 0) keepalive_registration(param->req, expire); break; } } } // callback to handle outgoing replies for the request's transaction // static void __tm_reply_out(struct cell *trans, int type, struct tmcb_params *param) { struct sip_msg reply; time_t expire; // only handle locally generated replies here (FAKED_REPLY) as the // in-transit replies are processed in __tm_reply_in() if (param->req==NULL || param->rpl!=FAKED_REPLY) return; if (param->code >= 200 && param->code < 300) { memset(&reply, 0, sizeof(struct sip_msg)); reply.buf = ((str*)param->extra1)->s; reply.len = ((str*)param->extra1)->len; if (parse_msg(reply.buf, reply.len, &reply) != 0) { LM_ERR("cannot parse outgoing TM reply for keepalive information\n"); return; } switch (param->req->REQ_METHOD) { case METHOD_SUBSCRIBE: expire = get_expires(&reply); if (expire > 0) keepalive_subscription(param->req, expire); break; case METHOD_REGISTER: expire = get_register_expire(param->req, &reply); if (expire > 0) keepalive_registration(param->req, expire); break; } free_sip_msg(&reply); } } // callback to handle incoming requests // static void __tm_request_in(struct cell *trans, int type, struct tmcb_params *t_param) { struct sip_msg *request = t_param->req; struct dlg_cell *dlg; unsigned h; char *uri; Dialog_Param *param; NAT_Contact *contact; SIP_Dialog *dialog; if (request->REQ_METHOD != METHOD_INVITE) return; if ((request->msg_flags & FL_NAT_TRACK_DIALOG) == 0 && (request->msg_flags & FL_DO_KEEPALIVE) == 0) return; if (dlg_api.create_dlg(request, 0) < 0) { LM_ERR("could not create new dialog\n"); return; } dlg = dlg_api.get_dlg(); if (!dlg) { LM_CRIT("error getting dialog\n"); return; } param = Dialog_Param_new(); if (!param) { LM_ERR("cannot create dialog callback param\n"); return; } if (dlg_api.register_dlgcb(dlg, DLGCB_DESTROY, __dialog_destroy, param, NULL) != 0) { LM_ERR("cannot register callback for dialog destruction\n"); Dialog_Param_del(param); return; } if (dlg_api.register_dlgcb(dlg, DLGCB_EARLY, __dialog_early, param, NULL) != 0) LM_ERR("cannot register callback for dialog early replies\n"); if (dlg_api.register_dlgcb(dlg, DLGCB_CONFIRMED, __dialog_confirmed, param, NULL) != 0) LM_ERR("cannot register callback for dialog confirmation\n"); if ((request->msg_flags & FL_DO_KEEPALIVE) == 0) return; uri = get_source_uri(request); param->caller_uri = shm_strdup(uri); if (!param->caller_uri) { LM_ERR("cannot allocate shared memory for caller_uri in dialog param\n"); return; } h = HASH(nat_table, uri); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, uri, h); if (contact) { dialog = SIP_Dialog_new(dlg, param->expire); if (dialog) { dialog->next = contact->dialogs; contact->dialogs = dialog; } else { LM_ERR("cannot allocate shared memory for new SIP dialog\n"); } } else { contact = NAT_Contact_new(uri, request->rcv.bind_address); if (contact) { contact->dialogs = SIP_Dialog_new(dlg, param->expire); if (contact->dialogs) { contact->next = nat_table->slots[h].head; nat_table->slots[h].head = contact; } else { LM_ERR("cannot allocate shared memory for new SIP dialog\n"); NAT_Contact_del(contact); } } else { LM_ERR("cannot allocate shared memory for new NAT contact\n"); } } lock_release(&nat_table->slots[h].lock); } // Keepalive NAT for an UA while it has registered contacts or active dialogs // static int NAT_Keepalive(struct sip_msg *msg) { str totag; if (keepalive_disabled) return -1; // keepalive is only supported for UDP dialogs if (msg->rcv.proto!=PROTO_UDP) return -1; switch (msg->REQ_METHOD) { case METHOD_REGISTER: // make the expires & contact headers available later in the TM cloned msg if (parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse headers in REGISTER request\n"); return -1; } // fallthrough case METHOD_SUBSCRIBE: msg->msg_flags |= FL_DO_KEEPALIVE; if (tm_api.register_tmcb(msg, 0, TMCB_RESPONSE_IN, __tm_reply_in, 0, 0) <= 0) { LM_ERR("cannot register TM callback for incoming replies\n"); return -1; } if (tm_api.register_tmcb(msg, 0, TMCB_RESPONSE_OUT, __tm_reply_out, 0, 0) <= 0) { LM_ERR("cannot register TM callback for outgoing replies\n"); return -1; } return 1; case METHOD_INVITE: if (!have_dlg_api) { LM_ERR("cannot keep alive dialog without the dialog module being loaded\n"); return -1; } if (parse_headers(msg, HDR_TO_F, 0) == -1) { LM_ERR("failed to parse To header\n"); return -1; } if (!msg->to) { LM_ERR("missing To header\n"); return -1; } totag = get_to(msg)->tag_value; if (totag.s && totag.len>0) { LM_ERR("nat_keepalive should only be called for initial INVITE requests\n"); return -1; } msg->msg_flags |= FL_DO_KEEPALIVE; return 1; default: LM_ERR("unsupported method for keepalive\n"); return -1; } } // Replace IP:Port in Contact field with the source address of the packet. static int FixContact(struct sip_msg *msg) { str before_host, after, newip; unsigned short port, newport; contact_t* contact; struct lump* anchor; struct sip_uri uri; int len, offset; char *buf; if (!get_contact_uri(msg, &uri, &contact)) return -1; newip.s = ip_addr2a(&msg->rcv.src_ip); newip.len = strlen(newip.s); newport = msg->rcv.src_port; port = uri.port_no ? uri.port_no : 5060; // Don't do anything if the address is the same, just return success. if (STR_MATCH_STR(uri.host, newip) && port==newport) return 1; if (uri.port.len == 0) uri.port.s = uri.host.s + uri.host.len; before_host.s = contact->uri.s; before_host.len = uri.host.s - contact->uri.s; after.s = uri.port.s + uri.port.len; after.len = contact->uri.s + contact->uri.len - after.s; len = before_host.len + newip.len + after.len + 22; // first try to alloc mem. if we fail we don't want to have the lump // deleted and not replaced. at least this way we keep the original. buf = pkg_malloc(len); if (buf == NULL) { LM_ERR("out of memory\n"); return -1; } offset = contact->uri.s - msg->buf; anchor = del_lump(msg, offset, contact->uri.len, HDR_CONTACT_T); if (!anchor) { pkg_free(buf); return -1; } if(msg->rcv.src_ip.af == AF_INET6) { len = sprintf(buf, "%.*s[%s]:%d%.*s", before_host.len, before_host.s, newip.s, newport, after.len, after.s); } else { len = sprintf(buf, "%.*s%s:%d%.*s", before_host.len, before_host.s, newip.s, newport, after.len, after.s); } if (insert_new_lump_after(anchor, buf, len, HDR_CONTACT_T) == 0) { pkg_free(buf); return -1; } contact->uri.s = buf; contact->uri.len = len; return 1; } static int ClientNatTest(struct sip_msg *msg, unsigned int tests) { int i; for (i=0; NAT_Tests[i].test!=NTNone; i++) { if ((tests & NAT_Tests[i].test)!=0 && NAT_Tests[i].proc(msg)) { return 1; } } return -1; // all failed } #define FROM_PREFIX "sip:keepalive@" static void send_keepalive(NAT_Contact *contact) { #define MAX_BRANCHID 9999999 #define MIN_BRANCHID 1000000 char buffer[8192], *from_uri, *ptr; static char from[64] = FROM_PREFIX; static char *from_ip = from + sizeof(FROM_PREFIX) - 1; static struct socket_info *last_socket = NULL; struct hostent* hostent; union sockaddr_union to; int nat_port, len, tolen; str nat_ip; if (keepalive_params.from == NULL) { if (contact->socket != last_socket) { memcpy(from_ip, contact->socket->address_str.s, contact->socket->address_str.len); from_ip[contact->socket->address_str.len] = 0; last_socket = contact->socket; } from_uri = from; } else { from_uri = keepalive_params.from; } len = snprintf(buffer, sizeof(buffer), "%s %s SIP/2.0\r\n" "Via: SIP/2.0/UDP %.*s:%d;branch=z9hG4bK%ld\r\n" "From: %s;tag=%x\r\n" "To: %s\r\n" "Call-ID: %s-%x-%x@%.*s\r\n" "CSeq: 1 %s\r\n" "%s%s" "Content-Length: 0\r\n\r\n", keepalive_params.method, contact->uri, contact->socket->address_str.len, contact->socket->address_str.s, contact->socket->port_no, (long)(rand()/(float)RAND_MAX * (MAX_BRANCHID-MIN_BRANCHID) + MIN_BRANCHID), from_uri, keepalive_params.from_tag++, contact->uri, keepalive_params.callid_prefix, keepalive_params.callid_counter++, get_ticks(), contact->socket->address_str.len, contact->socket->address_str.s, keepalive_params.method, keepalive_params.event_header, keepalive_params.extra_headers); if (len >= sizeof(buffer)) { LM_ERR("keepalive message is longer than %lu bytes\n", (unsigned long)sizeof(buffer)); return; } //nat_ip.s = strchr(contact->uri, ':') + 1; nat_ip.s = &contact->uri[4]; // skip over "sip:" ptr = strchr(nat_ip.s, ':'); nat_ip.len = ptr - nat_ip.s; nat_port = strtol(ptr+1, NULL, 10); hostent = sip_resolvehost(&nat_ip, NULL, NULL, False, NULL); hostent2su(&to, hostent, 0, nat_port); tolen=sockaddru_len(to); again: if (sendto(contact->socket->socket, buffer, len, 0, &to.s, tolen)==-1) { if (errno==EINTR) goto again; LM_ERR("sendto() failed with %s(%d)\n", strerror(errno),errno); } } static void keepalive_timer(unsigned int ticks, void *counter) { // we do not need to worry on accessing the counter without lock from // all the processes where this timer routine gets executed, as the // the routine is registered with TIMER_FLAG_DELAY_ON_DELAY and the // timer core will take care and avoid overlapping between the executions // of this routine (only one execution at the time) unsigned iteration = *(unsigned*)(unsigned long*)counter; NAT_Contact *contact; HashSlot *slot; time_t now; int i; now = time(NULL); for (i=0; isize; i++) { if ((i % keepalive_interval) != iteration) continue; slot = &nat_table->slots[i]; lock_get(&slot->lock); slot->head = NAT_Contact_purge_expired(slot->head, now); contact = slot->head; lock_release(&slot->lock); while (contact) { send_keepalive(contact); contact = contact->next; } } *(unsigned*)(unsigned long*)counter = (iteration+1) % keepalive_interval; } // Functions to save and restore the keepalive NAT table. They should only be // called from mod_init/mod_destroy as they access shm memory without locking // #define STATE_FILE_HEADER "# Automatically generated file from internal keepalive state. Do NOT modify!\n" static void save_keepalive_state(void) { NAT_Contact *contact; FILE *f; int i; if (!keepalive_state_file) return; f = fopen(keepalive_state_file, "w"); if (!f) { LM_ERR("failed to open keepalive state file for writing: %s\n", strerror(errno)); return; } fprintf(f, STATE_FILE_HEADER); for (i=0; isize; i++) { contact = nat_table->slots[i].head; while (contact) { fprintf(f, "%s %.*s %ld %ld\n", contact->uri, contact->socket->sock_str.len, contact->socket->sock_str.s, (long)contact->registration_expire, (long)contact->subscription_expire); contact = contact->next; } } if (ferror(f)) LM_ERR("couldn't write keepalive state file: %s\n", strerror(errno)); fclose(f); } static void restore_keepalive_state(void) { char uri[64], socket[64]; time_t rtime, stime, now; NAT_Contact *contact; struct socket_info *sock; int port, proto, res; unsigned h; str host; FILE *f; if (!keepalive_state_file) return; f = fopen(keepalive_state_file, "r"); if (!f) { if (errno != ENOENT) LM_ERR("failed to open keepalive state file for reading: %s\n", strerror(errno)); return; } now = time(NULL); res = fscanf(f, STATE_FILE_HEADER); // skip header while (True) { res = fscanf(f, "%63s %63s %ld %ld", uri, socket, &rtime, &stime); if (res == EOF) { if (ferror(f)) LM_ERR("error while reading keepalive state file: %s\n", strerror(errno)); break; } else if (res != 4) { LM_ERR("invalid/corrupted keepalive state file. ignoring remaining entries.\n"); break; } else { if (now > rtime && now > stime) continue; // expired entry if (parse_phostport(socket, strlen(socket), &host.s, &host.len, &port, &proto) < 0) continue; set_sip_defaults( port, proto); sock = grep_sock_info(&host, (unsigned short)port, (unsigned short)proto); if (!sock) continue; // socket no longer available since last time. ignore. h = HASH(nat_table, uri); contact = NAT_Contact_new(uri, sock); if (contact) { SIP_Registration_update(contact, rtime); SIP_Subscription_update(contact, stime); contact->next = nat_table->slots[h].head; nat_table->slots[h].head = contact; } else { LM_ERR("cannot allocate shared memory for new NAT contact\n"); break; } } } fclose(f); } // Module management: initialization/destroy/function-parameter-fixing/... // static int mod_init(void) { int *param; if (keepalive_interval <= 0) { LM_NOTICE("keepalive functionality is disabled from the configuration\n"); keepalive_disabled = True; return 0; } // get SL module callback registration function if (register_slcb(SLCB_REPLY_OUT, FL_DO_KEEPALIVE, __sl_reply_out) != 0) { LM_ERR("cannot register callback for stateless outgoing replies\n"); return -1; } // bind to the TM API if (load_tm_api(&tm_api)!=0) { LM_ERR("cannot load the TM module API\n"); return -1; } // bind to the dialog API if (load_dlg_api(&dlg_api)==0) { have_dlg_api = True; param = find_param_export("dialog", "default_timeout", INT_PARAM); if (!param) { LM_ERR("cannot find default_timeout parameter in the dialog module\n"); return -1; } dialog_default_timeout = *param; // register callback for incoming requests if (tm_api.register_tmcb(0, 0, TMCB_REQUEST_IN, __tm_request_in, 0, 0) <=0) { LM_ERR("cannot register TM callback for incoming INVITE request\n"); return -1; } // register a pre-script callback to automatically enable dialog tracing if (register_script_cb(preprocess_request, PRE_SCRIPT_CB|REQ_TYPE_CB, 0)!=0) { LM_ERR("could not register request preprocessing callback\n"); return -1; } } else { LM_NOTICE("keeping alive dialogs is disabled because the dialog module is not loaded\n"); } // initialize the keepalive message parameters if (keepalive_params.from!=NULL && *(keepalive_params.from)==0) { LM_WARN("ignoring empty keepalive_from parameter\n"); keepalive_params.from = NULL; } if (strcasecmp(keepalive_params.method, "NOTIFY")==0) keepalive_params.event_header = "Event: keep-alive\r\n"; snprintf(keepalive_params.callid_prefix, 20, "%x", rand()); keepalive_params.callid_counter = rand(); keepalive_params.from_tag = rand(); // we need the statistics initialized before restoring the keepalive state if (register_module_stats(exports.name, statistics) < 0) { LM_ERR("failed to initialize module statistics\n"); return -1; } // create hash table to hold NAT contacts nat_table = HashTable_new(); if (!nat_table) { LM_ERR("cannot create hash table to store NAT endpoints\n"); return -1; } restore_keepalive_state(); // check keepalive interval and add keepalive timer process if (keepalive_interval < 10) { LM_WARN("keepalive_interval should be at least 10 seconds\n"); LM_NOTICE("using 10 seconds for keepalive_interval\n"); keepalive_interval = 10; } // allocate a shm variable to keep the counter used by the keepalive // timer routine - it must be shared as the routine get executed // in different processes if (NULL==(param=(int*) shm_malloc(sizeof(int)))) { LM_ERR("cannot allocate shm memory for keepalive counter\n"); return -1; } *param = 0; if (register_timer( "nt-pinger", keepalive_timer, (void*)(long)param, 1, TIMER_FLAG_DELAY_ON_DELAY)<0) { LM_ERR("failed to register keepalive timer\n"); return -1; } return 0; } static int child_init(int rank) { return 0; } static void mod_destroy(void) { if (nat_table) { save_keepalive_state(); HashTable_del(nat_table); nat_table = NULL; } } // Preprocess a request before it is processed in the main script route // // Here we enable dialog tracing to be able to automatically extend an // existing registration keepalive to a destination, for the duration of // the dialog, even if the dialog source is not kept alive by explicitly // calling nat_keepalive(). This is needed to still be able to forward // messages to the callee, even if the registration keepalive expires // during the dialog and it is not renewed. // static int preprocess_request(struct sip_msg *msg, void *_param) { str totag; if (msg->REQ_METHOD != METHOD_INVITE) return SCB_RUN_ALL; if (parse_headers(msg, HDR_TO_F, 0) == -1) { LM_ERR("failed to parse To header\n"); return SCB_RUN_ALL; } if (!msg->to) { LM_ERR("missing To header\n"); return SCB_RUN_ALL; } totag = get_to(msg)->tag_value; if (totag.s==0 || totag.len==0) { msg->msg_flags |= FL_NAT_TRACK_DIALOG; } return SCB_RUN_ALL; } // Filter out replies to keepalive messages // static int reply_filter(struct sip_msg *reply) { struct cseq_body *cseq; static str prefix = {NULL, 0}; str call_id; parse_headers(reply, HDR_VIA2_F, 0); if (reply->via2) return 1; // check if the method from CSeq header matches our method if (!reply->cseq && parse_headers(reply, HDR_CSEQ_F, 0) < 0) { LM_ERR("failed to parse the CSeq header\n"); return -1; } if (!reply->cseq) { LM_ERR("missing CSeq header\n"); return -1; } cseq = reply->cseq->parsed; if (!STR_MATCH(cseq->method, keepalive_params.method)) return 1; // check if callid_prefix matches if (!reply->callid && parse_headers(reply, HDR_CALLID_F, 0) < 0) { LM_ERR("failed to parse the Call-ID header\n"); return -1; } if (!reply->callid) { LM_ERR("missing Call-ID header\n"); return -1; } call_id = reply->callid->body; if (prefix.s == NULL) { prefix.s = keepalive_params.callid_prefix; prefix.len = strlen(prefix.s); } if (!STR_HAS_PREFIX(call_id, prefix) || call_id.s[prefix.len]!='-') return 1; return 0; } // Pseudo variable management // static int pv_parse_nat_contact_name(pv_spec_p sp, str *in) { char *p; char *s; pv_spec_p nsp = 0; if(in==NULL || in->s==NULL || sp==NULL) return -1; p = in->s; if (*p==PV_MARKER) { nsp = (pv_spec_p)pkg_malloc(sizeof(pv_spec_t)); if (nsp==NULL) { LM_ERR("cannot allocate private memory\n"); return -1; } s = pv_parse_spec(in, nsp); if (s==NULL) { LM_ERR("invalid name [%.*s]\n", in->len, in->s); pv_spec_free(nsp); return -1; } sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void*)nsp; return 0; } sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = AVP_NAME_STR; sp->pvp.pvn.u.isname.name.s = *in; return 0; } static int pv_get_keepalive_socket(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char uri[128]; NAT_Contact *contact; pv_value_t tv; unsigned h; if (msg==NULL || param==NULL || res==NULL) return -1; if (pv_get_spec_name(msg, param, &tv)!=0 || (!(tv.flags&PV_VAL_STR))) { LM_ERR("invalid NAT contact uri\n"); return -1; } if (tv.rs.len > sizeof(uri)-1) { LM_ERR("NAT contact uri too long\n"); return -1; } strncpy(uri, tv.rs.s, tv.rs.len); uri[tv.rs.len] = 0; h = HASH(nat_table, uri); lock_get(&nat_table->slots[h].lock); contact = HashTable_search(nat_table, uri, h); if (!contact) { lock_release(&nat_table->slots[h].lock); return pv_get_null(msg, param, res); } res->rs = contact->socket->sock_str; res->flags = PV_VAL_STR; lock_release(&nat_table->slots[h].lock); return 0; } static int pv_get_source_uri(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char uri[128]; if (msg==NULL || res==NULL) return -1; snprintf(uri, 64, "sip:%s:%d", ip_addr2a(&msg->rcv.src_ip), msg->rcv.src_port); switch (msg->rcv.proto) { case PROTO_TCP: strcat(uri, ";transport=tcp"); break; case PROTO_TLS: strcat(uri, ";transport=tls"); break; case PROTO_SCTP: strcat(uri, ";transport=sctp"); break; } res->rs.s = uri; res->rs.len = strlen(uri); res->flags = PV_VAL_STR; return 0; } static int pv_get_track_dialog(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if (msg==NULL || res==NULL) return -1; if ((msg->msg_flags & FL_NAT_TRACK_DIALOG) == 0) { res->ri = 0; } else { res->ri = 1; } res->flags = PV_VAL_INT; return 0; } static int pv_set_track_dialog(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) { if (val==NULL) { msg->msg_flags &= ~FL_NAT_TRACK_DIALOG; return 0; } if (!(val->flags&PV_VAL_INT)){ LM_ERR("assigning non-int value to track_dialog flag\n"); return -1; } if (val->ri == 0) { msg->msg_flags &= ~FL_NAT_TRACK_DIALOG; } else { msg->msg_flags |= FL_NAT_TRACK_DIALOG; } return 0; } opensips-2.2.2/modules/nathelper/000077500000000000000000000000001300170765700170155ustar00rootroot00000000000000opensips-2.2.2/modules/nathelper/Makefile000066400000000000000000000003331300170765700204540ustar00rootroot00000000000000# $Id$ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=nathelper.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/nathelper/README000066400000000000000000000377621300170765700177140ustar00rootroot00000000000000nathelper Module Maxim Sobolev Sippy Software, Inc. Edited by Maxim Sobolev Edited by Bogdan-Andrei Iancu Copyright © 2003-2008 Sippy Software, Inc. Copyright © 2005 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. NAT pinging types 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. natping_interval (integer) 1.4.2. ping_nated_only (integer) 1.4.3. natping_partitions (integer) 1.4.4. natping_socket (string) 1.4.5. received_avp (str) 1.4.6. force_socket (string) 1.4.7. sipping_bflag (string/integer) 1.4.8. remove_on_timeout_bflag (string/integer) 1.4.9. sipping_from (string) 1.4.10. sipping_method (string) 1.4.11. nortpproxy_str (string) 1.4.12. natping_tcp (integer) 1.4.13. oldip_skip (string) 1.4.14. ping_threshold (int) 1.4.15. max_pings_lost (int) 1.5. Exported Functions 1.5.1. fix_nated_contact([uri_params]) 1.5.2. fix_nated_sdp(flags [, ip_address]) 1.5.3. add_rcv_param([flag]), 1.5.4. fix_nated_register() 1.5.5. nat_uac_test(flags) 1.5.6. MI Commands 2. Frequently Asked Questions List of Examples 1.1. Set natping_interval parameter 1.2. Set ping_nated_only parameter 1.3. Set natping_partitions parameter 1.4. Set natping_socket parameter 1.5. Set received_avp parameter 1.6. Set force_socket parameter 1.7. Set sipping_bflag parameter 1.8. Set remove_on_timeout_bflag parameter 1.9. Set sipping_from parameter 1.10. Set sipping_method parameter 1.11. Set nortpproxy_str parameter 1.12. Set natping_interval parameter 1.13. Set natping_interval parameter 1.14. Set ping_threshold parameter 1.15. Set ping_threshold parameter 1.16. fix_nated_contact usage 1.17. fix_nated_sdp usage 1.18. add_rcv_paramer usage 1.19. fix_nated_register usage 1.20. nh_enable_ping usage Chapter 1. Admin Guide 1.1. Overview This is a module to help with NAT traversal. In particular, it helps symmetric UAs that don't advertise they are symmetric and are not able to determine their public address. fix_nated_contact rewrites Contact header field with request's source address:port pair. fix_nated_sdp adds the active direction indication to SDP (flag 0x01) and updates source IP address too (flag 0x02). Since version 2.2, stateful ping(only SIP Pings) for nathelper is available. This allows you to remove contacts from usrloc location table when max_pings_lost pings are not responded to, each ping having a response timeout of ping_threshold seconds. In order to have this functionality, contacts must have remove_on_timeout_bflag flag set when inserted into the location table. Works with multipart messages that contain an SDP part, but not with multi-layered multipart messages. 1.2. NAT pinging types Currently, the nathelper module supports two types of NAT pings: * UDP package - 4 bytes (zero filled) UDP packages are sent to the contact address. + Advantages: low bandwitdh traffic, easy to generate by OpenSIPS; + Disadvantages: unidirectional traffic through NAT (inbound - from outside to inside); As many NATs do update the bind timeout only on outbound traffic, the bind may expire and closed. * SIP request - a stateless SIP request is sent to the contact address. + Advantages: bidirectional traffic through NAT, since each PING request from OpenSIPS (inbound traffic) will force the SIP client to generate a SIP reply (outbound traffic) - the NAT bind will be surely kept open. Since version 2.2, one can also choose to remove contacts from the location table if a certain threshold is detected. + Disadvantages: higher bandwitdh traffic, more expensive (as time) to generate by OpenSIPS; 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * usrloc module - only if the NATed contacts are to be pinged. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.4. Exported Parameters 1.4.1. natping_interval (integer) Period of time in seconds between sending the NAT pings to all currently registered UAs to keep their NAT bindings alive. Value of 0 disables this functionality. Note Enabling the NAT pinging functionality will force the module to bind itself to USRLOC module. Default value is 0. Example 1.1. Set natping_interval parameter ... modparam("nathelper", "natping_interval", 10) ... 1.4.2. ping_nated_only (integer) If this variable is set then only contacts that have “behind_NAT†flag in user location database set will get ping. Default value is 0. Example 1.2. Set ping_nated_only parameter ... modparam("nathelper", "ping_nated_only", 1) ... 1.4.3. natping_partitions (integer) How many partitions/chunks to be used for sending the pingings. One partition means sending all pingings together. Two partitions means to send half pings and second half at a time. Default value is 1. Example 1.3. Set natping_partitions parameter ... modparam("nathelper", "natping_partitions", 4) ... 1.4.4. natping_socket (string) Spoof the natping's source-ip to this address. Works only for IPv4. Default value is NULL. Example 1.4. Set natping_socket parameter ... modparam("nathelper", "natping_socket", "192.168.1.1:5006") ... 1.4.5. received_avp (str) The name of the Attribute-Value-Pair (AVP) used to store the URI containing the received IP, port, and protocol. The URI is created by fix_nated_register function of nathelper module and the attribute is then used by the registrar to store the received parameters. Do not forget to change the value of corresponding parameter in registrar module if you change the value of this parameter. Note You must set this parameter if you use "fix_nated_register". In such case you must set the parameter with same name of "registrar" module to same value. Default value is "NULL" (disabled). Example 1.5. Set received_avp parameter ... modparam("nathelper", "received_avp", "$avp(received)") ... 1.4.6. force_socket (string) Socket to be forced when sending pings. It makes sense only for UDP communication. If no one specified, the OS will choose. Default value is “NULLâ€. Example 1.6. Set force_socket parameter ... modparam("nathelper", "force_socket", "localhost:33333") ... 1.4.7. sipping_bflag (string/integer) What branch flag should be used by the module to identify NATed contacts for which it should perform NAT ping via a SIP request instead if dummy UDP package. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is "NULL" (disabled). Example 1.7. Set sipping_bflag parameter ... modparam("nathelper", "sipping_bflag", "SIP_PING_FLAG") ... 1.4.8. remove_on_timeout_bflag (string/integer) What flag to be used in order to activate usrloc contact removal when ping_threshold is exceeded. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is "NULL" (disabled). Example 1.8. Set remove_on_timeout_bflag parameter ... modparam("nathelper", "remove_on_timeout_bflag", "RM_ONTO_FLAG") ... 1.4.9. sipping_from (string) The parameter sets the SIP URI to be used in generating the SIP requests for NAT ping purposes. To enable the SIP request pinging feature, you have to set this parameter. The SIP request pinging will be used only for requests marked so. Default value is “NULLâ€. Example 1.9. Set sipping_from parameter ... modparam("nathelper", "sipping_from", "sip:pinger@siphub.net") ... 1.4.10. sipping_method (string) The parameter sets the SIP method to be used in generating the SIP requests for NAT ping purposes. Default value is “OPTIONSâ€. Example 1.10. Set sipping_method parameter ... modparam("nathelper", "sipping_method", "INFO") ... 1.4.11. nortpproxy_str (string) The parameter sets the SDP attribute used by nathelper to mark the packet SDP informations have already been mangled. If empty string, no marker will be added or checked. Note The string must be a complete SDP line, including the EOH (\r\n). Default value is “a=nortpproxy:yes\r\nâ€. Example 1.11. Set nortpproxy_str parameter ... modparam("nathelper", "nortpproxy_str", "a=sdpmangled:yes\r\n") ... 1.4.12. natping_tcp (integer) If the flag is set, TCP/TLS clients will also be pinged with SIP OPTIONS messages. Default value is 0 (not set). Example 1.12. Set natping_interval parameter ... modparam("nathelper", "natping_tcp", 1) ... 1.4.13. oldip_skip (string) Parameter which specifies whether old media ip and old origin ip shall be put in the sdp body. The parameter has two values : 'o' ("a=oldoip" field shall be skipped) and 'c' ("a=oldcip" field shall be skipped). Default value is 0 (not set). Example 1.13. Set natping_interval parameter ... modparam("nathelper", "oldip_skip", "oc") ... 1.4.14. ping_threshold (int) If a contact does not respond in ping_threshold seconds since the ping has been sent, the contact shall be removed after max_pings_lost unresponded pings. Default value is 3 (seconds). Example 1.14. Set ping_threshold parameter ... modparam("nathelper", "ping_threshold", 10) ... 1.4.15. max_pings_lost (int) Number of unresponded pings after which the contact shall be removed from the location table. Default value is 3 (pings). Example 1.15. Set ping_threshold parameter ... modparam("nathelper", "max_pings_lost", 5) ... 1.5. Exported Functions 1.5.1. fix_nated_contact([uri_params]) Rewrites the URI Contact HF to contain request's source address:port. If a list of URI parameter is provided, it will be added to the modified contact; IMPORTANT NOTE: Changes made by this function shall not be seen in the async resume route. So make sure you call it in all the resume routes where you need the contact fixed. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE. Example 1.16. fix_nated_contact usage ... if (search("User-Agent: Cisco ATA.*") { fix_nated_contact(";ata=cisco"); } else { fix_nated_contact(); } ... 1.5.2. fix_nated_sdp(flags [, ip_address]) Alters the SDP information in orer to facilitate NAT traversal. What changes to be performed may be controled via the “flags†parameter. Since version 1.12 the name of the old ip fields are "a=oldoip" for old origin ip and "a=oldcip" for old meda ip. Meaning of the parameters is as follows: * flags - the value may be a bitwise OR of the following flags: + 0x01 - adds “a=direction:active†SDP line; + 0x02 - rewrite media IP address (c=) with source address of the message or the provided IP address (the provide IP address take precedence over the source address). + 0x04 - adds “a=nortpproxy:yes†SDP line; + 0x08 - rewrite IP from origin description (o=) with source address of the message or the provided IP address (the provide IP address take precedence over the source address). * ip_address - IP to be used for rewriting SDP. If not specified, the received signalling IP will be used. The parameter allows pseudo-variables usage. NOTE: For the IP to be used, you need to use 0x02 or 0x08 flags, otherwise it will have no effect. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.17. fix_nated_sdp usage ... if (search("User-Agent: Cisco ATA.*") {fix_nated_sdp("3");}; ... 1.5.3. add_rcv_param([flag]), Add received parameter to Contact header fields or Contact URI. The parameter will contain URI created from the source IP, port, and protocol of the packet containing the SIP message. The parameter can be then processed by another registrar, this is useful, for example, when replicating register messages using t_replicate function to another registrar. Meaning of the parameters is as follows: * flag - flags to indicate if the parameter should be added to Contact URI or Contact header. If the flag is non-zero, the parameter will be added to the Contact URI. If not used or equal to zero, the parameter will go to the Contact header. This function can be used from REQUEST_ROUTE. Example 1.18. add_rcv_paramer usage ... add_rcv_param(); # add the parameter to the Contact header .... add_rcv_param("1"); # add the parameter to the Contact URI ... 1.5.4. fix_nated_register() The function creates a URI consisting of the source IP, port, and protocol and stores the URI in an Attribute-Value-Pair. The URI will be appended as "received" parameter to Contact in 200 OK and registrar will store it in the user location database. This function can be used from REQUEST_ROUTE. Example 1.19. fix_nated_register usage ... fix_nated_register(); ... 1.5.5. nat_uac_test(flags) Tries to guess if client's request originated behind a nat. The parameter determines what heuristics is used. Meaning of the flags is as follows: * 1 - Contact header field is searched for occurrence of RFC1918 / RFC6598 addresses. * 2 - the "received" test is used: address in Via is compared against source IP address of signaling * 4 - Top Most VIA is searched for occurrence of RFC1918 / RFC6598 addresses * 8 - SDP is searched for occurrence of RFC1918 / RFC6598 addresses * 16 - test if the source port is different from the port in Via * 32 - address in Contact is compared against source IP address of signaling * 64 - Port in Contact is compared against source port of signaling All flags can be bitwise combined, the test returns true if any of the tests identified a NAT. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. 1.5.6. MI Commands 1.5.6.1. nh_enable_ping Enables natping if parameter value greater than 0. Disables natping if parameter value is 0. With no parameter, it returns the current natping status. The function may takean optional parameter (for set operations) - a number in decimal format. Example 1.20. nh_enable_ping usage ... $ opensipsctl fifo nh_enable_ping Status:: 1 $ $ opensipsctl fifo nh_enable_ping 0 $ $ opensipsctl fifo nh_enable_ping Status:: 0 $ ... Chapter 2. Frequently Asked Questions 2.1. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.2. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.3. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/nathelper/doc/000077500000000000000000000000001300170765700175625ustar00rootroot00000000000000opensips-2.2.2/modules/nathelper/doc/nathelper.xml000066400000000000000000000025421300170765700222710ustar00rootroot00000000000000 %docentities; ]> nathelper Module &osipsname; Maxim Sobolev Sippy Software, Inc.
sobomax@sippysoft.com
Maxim Sobolev
sobomax@sippysoft.com
Bogdan-Andrei Iancu
bogdan@opensips.org
2003-2008 Sippy Software, Inc. 2005 &voicesystem; $Revision: 8740 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/nathelper/doc/nathelper_admin.xml000066400000000000000000000465471300170765700234560ustar00rootroot00000000000000 &adminguide;
Overview This is a module to help with &nat; traversal. In particular, it helps symmetric &ua;s that don't advertise they are symmetric and are not able to determine their public address. fix_nated_contact rewrites Contact header field with request's source address:port pair. fix_nated_sdp adds the active direction indication to &sdp; (flag 0x01) and updates source &ip; address too (flag 0x02). Since version 2.2, stateful ping(only SIP Pings) for nathelper is available. This allows you to remove contacts from usrloc location table when max_pings_lost pings are not responded to, each ping having a response timeout of ping_threshold seconds. In order to have this functionality, contacts must have remove_on_timeout_bflag flag set when inserted into the location table. Works with multipart messages that contain an SDP part, but not with multi-layered multipart messages.
NAT pinging types Currently, the nathelper module supports two types of NAT pings: UDP package - 4 bytes (zero filled) UDP packages are sent to the contact address. Advantages: low bandwitdh traffic, easy to generate by &osips;; Disadvantages: unidirectional traffic through NAT (inbound - from outside to inside); As many NATs do update the bind timeout only on outbound traffic, the bind may expire and closed. SIP request - a stateless SIP request is sent to the contact address. Advantages: bidirectional traffic through NAT, since each PING request from &osips; (inbound traffic) will force the SIP client to generate a SIP reply (outbound traffic) - the NAT bind will be surely kept open. Since version 2.2, one can also choose to remove contacts from the location table if a certain threshold is detected. Disadvantages: higher bandwitdh traffic, more expensive (as time) to generate by &osips;;
Dependencies
&osips; Modules The following modules must be loaded before this module: usrloc module - only if the NATed contacts are to be pinged.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>natping_interval</varname> (integer) Period of time in seconds between sending the NAT pings to all currently registered &ua;s to keep their &nat; bindings alive. Value of 0 disables this functionality. Enabling the NAT pinging functionality will force the module to bind itself to USRLOC module. Default value is 0. Set <varname>natping_interval</varname> parameter ... modparam("nathelper", "natping_interval", 10) ...
<varname>ping_nated_only</varname> (integer) If this variable is set then only contacts that have behind_NAT flag in user location database set will get ping. Default value is 0. Set <varname>ping_nated_only</varname> parameter ... modparam("nathelper", "ping_nated_only", 1) ...
<varname>natping_partitions</varname> (integer) How many partitions/chunks to be used for sending the pingings. One partition means sending all pingings together. Two partitions means to send half pings and second half at a time. Default value is 1. Set <varname>natping_partitions</varname> parameter ... modparam("nathelper", "natping_partitions", 4) ...
<varname>natping_socket</varname> (string) Spoof the natping's source-ip to this address. Works only for IPv4. Default value is NULL. Set <varname>natping_socket</varname> parameter ... modparam("nathelper", "natping_socket", "192.168.1.1:5006") ...
<varname>received_avp</varname> (str) The name of the Attribute-Value-Pair (AVP) used to store the URI containing the received IP, port, and protocol. The URI is created by fix_nated_register function of nathelper module and the attribute is then used by the registrar to store the received parameters. Do not forget to change the value of corresponding parameter in registrar module if you change the value of this parameter. You must set this parameter if you use "fix_nated_register". In such case you must set the parameter with same name of "registrar" module to same value. Default value is "NULL" (disabled). Set <varname>received_avp</varname> parameter ... modparam("nathelper", "received_avp", "$avp(received)") ...
<varname>force_socket</varname> (string) Socket to be forced when sending pings. It makes sense only for UDP communication. If no one specified, the OS will choose. Default value is NULL. Set <varname>force_socket</varname> parameter ... modparam("nathelper", "force_socket", "localhost:33333") ...
<varname>sipping_bflag</varname> (string/integer) What branch flag should be used by the module to identify NATed contacts for which it should perform NAT ping via a SIP request instead if dummy UDP package. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is "NULL" (disabled). Set <varname>sipping_bflag</varname> parameter ... modparam("nathelper", "sipping_bflag", "SIP_PING_FLAG") ...
<varname>remove_on_timeout_bflag</varname> (string/integer) What flag to be used in order to activate usrloc contact removal when ping_threshold is exceeded. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is "NULL" (disabled). Set <varname>remove_on_timeout_bflag</varname> parameter ... modparam("nathelper", "remove_on_timeout_bflag", "RM_ONTO_FLAG") ...
<varname>sipping_from</varname> (string) The parameter sets the SIP URI to be used in generating the SIP requests for NAT ping purposes. To enable the SIP request pinging feature, you have to set this parameter. The SIP request pinging will be used only for requests marked so. Default value is NULL. Set <varname>sipping_from</varname> parameter ... modparam("nathelper", "sipping_from", "sip:pinger@siphub.net") ...
<varname>sipping_method</varname> (string) The parameter sets the SIP method to be used in generating the SIP requests for NAT ping purposes. Default value is OPTIONS. Set <varname>sipping_method</varname> parameter ... modparam("nathelper", "sipping_method", "INFO") ...
<varname>nortpproxy_str</varname> (string) The parameter sets the SDP attribute used by nathelper to mark the packet SDP informations have already been mangled. If empty string, no marker will be added or checked. The string must be a complete SDP line, including the EOH (\r\n). Default value is a=nortpproxy:yes\r\n. Set <varname>nortpproxy_str</varname> parameter ... modparam("nathelper", "nortpproxy_str", "a=sdpmangled:yes\r\n") ...
<varname>natping_tcp</varname> (integer) If the flag is set, TCP/TLS clients will also be pinged with SIP OPTIONS messages. Default value is 0 (not set). Set <varname>natping_interval</varname> parameter ... modparam("nathelper", "natping_tcp", 1) ...
<varname>oldip_skip</varname> (string) Parameter which specifies whether old media ip and old origin ip shall be put in the sdp body. The parameter has two values : 'o' ("a=oldoip" field shall be skipped) and 'c' ("a=oldcip" field shall be skipped). Default value is 0 (not set). Set <varname>natping_interval</varname> parameter ... modparam("nathelper", "oldip_skip", "oc") ...
<varname>ping_threshold</varname> (int) If a contact does not respond in ping_threshold seconds since the ping has been sent, the contact shall be removed after max_pings_lost unresponded pings. Default value is 3 (seconds). Set <varname>ping_threshold</varname> parameter ... modparam("nathelper", "ping_threshold", 10) ...
<varname>max_pings_lost</varname> (int) Number of unresponded pings after which the contact shall be removed from the location table. Default value is 3 (pings). Set <varname>ping_threshold</varname> parameter ... modparam("nathelper", "max_pings_lost", 5) ...
Exported Functions
<function moreinfo="none">fix_nated_contact([uri_params])</function> Rewrites the URI Contact HF to contain request's source address:port. If a list of URI parameter is provided, it will be added to the modified contact; IMPORTANT NOTE: Changes made by this function shall not be seen in the async resume route. So make sure you call it in all the resume routes where you need the contact fixed. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE. <function>fix_nated_contact</function> usage ... if (search("User-Agent: Cisco ATA.*") { fix_nated_contact(";ata=cisco"); } else { fix_nated_contact(); } ...
<function moreinfo="none">fix_nated_sdp(flags [, ip_address])</function> Alters the SDP information in orer to facilitate NAT traversal. What changes to be performed may be controled via the flags parameter. Since version 1.12 the name of the old ip fields are "a=oldoip" for old origin ip and "a=oldcip" for old meda ip. Meaning of the parameters is as follows: flags - the value may be a bitwise OR of the following flags: 0x01 - adds a=direction:active SDP line; 0x02 - rewrite media &ip; address (c=) with source address of the message or the provided IP address (the provide IP address take precedence over the source address). 0x04 - adds a=nortpproxy:yes SDP line; 0x08 - rewrite IP from origin description (o=) with source address of the message or the provided IP address (the provide IP address take precedence over the source address). ip_address - IP to be used for rewriting SDP. If not specified, the received signalling IP will be used. The parameter allows pseudo-variables usage. NOTE: For the IP to be used, you need to use 0x02 or 0x08 flags, otherwise it will have no effect. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>fix_nated_sdp</function> usage ... if (search("User-Agent: Cisco ATA.*") {fix_nated_sdp("3");}; ...
<function moreinfo="none">add_rcv_param([flag])</function>, Add received parameter to Contact header fields or Contact URI. The parameter will contain URI created from the source IP, port, and protocol of the packet containing the SIP message. The parameter can be then processed by another registrar, this is useful, for example, when replicating register messages using t_replicate function to another registrar. Meaning of the parameters is as follows: flag - flags to indicate if the parameter should be added to Contact URI or Contact header. If the flag is non-zero, the parameter will be added to the Contact URI. If not used or equal to zero, the parameter will go to the Contact header. This function can be used from REQUEST_ROUTE. <function>add_rcv_paramer</function> usage ... add_rcv_param(); # add the parameter to the Contact header .... add_rcv_param("1"); # add the parameter to the Contact URI ...
<function moreinfo="none">fix_nated_register()</function> The function creates a URI consisting of the source IP, port, and protocol and stores the URI in an Attribute-Value-Pair. The URI will be appended as "received" parameter to Contact in 200 OK and registrar will store it in the user location database. This function can be used from REQUEST_ROUTE. <function>fix_nated_register</function> usage ... fix_nated_register(); ...
<function>nat_uac_test(flags)</function> Tries to guess if client's request originated behind a nat. The parameter determines what heuristics is used. Meaning of the flags is as follows: 1 - Contact header field is searched for occurrence of RFC1918 / RFC6598 addresses. 2 - the "received" test is used: address in Via is compared against source IP address of signaling 4 - Top Most VIA is searched for occurrence of RFC1918 / RFC6598 addresses 8 - SDP is searched for occurrence of RFC1918 / RFC6598 addresses 16 - test if the source port is different from the port in Via 32 - address in Contact is compared against source IP address of signaling 64 - Port in Contact is compared against source port of signaling All flags can be bitwise combined, the test returns true if any of the tests identified a NAT. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE.
<acronym>MI</acronym> Commands
<function moreinfo="none">nh_enable_ping</function> Enables natping if parameter value greater than 0. Disables natping if parameter value is 0. With no parameter, it returns the current natping status. The function may takean optional parameter (for set operations) - a number in decimal format. <function moreinfo="none">nh_enable_ping</function> usage ... $ opensipsctl fifo nh_enable_ping Status:: 1 $ $ opensipsctl fifo nh_enable_ping 0 $ $ opensipsctl fifo nh_enable_ping Status:: 0 $ ...
opensips-2.2.2/modules/nathelper/doc/nathelper_faq.xml000066400000000000000000000024241300170765700231170ustar00rootroot00000000000000 &faqguide; Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/nathelper/examples/000077500000000000000000000000001300170765700206335ustar00rootroot00000000000000opensips-2.2.2/modules/nathelper/examples/4to6.cfg000066400000000000000000000054311300170765700221130ustar00rootroot00000000000000# $Id$ # # Simple application level gateway config script. # # Assumes that SER/rtpproxy run on a machine, which connected to # both IPv4 and IPv6 networks. # # Correspondingly, this machine has two IP addresses: one IPv4 # and the second one IPv6 # # For example: # # 192.168.0.1 - "internal" IPv4 address # 2002:1234:5678::1 - "external" IPv6 address # # rtpproxy started with `-l 192.168.0.1 -6 /2002:1234:5678::1' option. # ser started with `-l 192.168.0.1 -l [2002:1234:5678::1] option. # # ------------------ module loading ---------------------------------- loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" loadmodule "/usr/local/lib/ser/modules/nathelper.so" # ----------------- setting module-specific parameters --------------- # -- nathelper params -- modparam("nathelper", "natping_interval", 15) # ------------------ main fun below ---------------------------------- route { # initial sanity checks -- messages with # max_forwars == 0, or excessively long requests, # or those that don't addressed to us if (!mf_process_maxfwd_header("10")) { sl_send_reply("483", "Too Many Hops"); break; }; if (msg:len > max_len) { sl_send_reply("513", "Message too big"); break; }; if (!(uri == myself) && method == "INVITE") { sl_send_reply("403", "Call cannot be served here"); break; }; if (method == "REGISTER") { if (af == inet) { save("location-inet4"); } else if (af == inet6) { save("location-inet6"); } else { sl_send_reply("403", "Call cannot be served here"); }; break; }; if (method == "INVITE") { if (lookup("location-inet4")) { # Comment out three lines below if you want # RTP for IPv4->IPv4 calls to go directly # between UAs if (af == inet) if (rtpproxy_offer("FAII")) t_on_reply("1"); if (af == inet6) if (rtpproxy_offer("FAEI")) t_on_reply("1"); } else if (lookup("location-inet6")) { if (af == inet) if (rtpproxy_offer("FAIE")) t_on_reply("1"); # Comment out three lines below if you want # RTP for IPv6->IPv6 calls to go directly # between UAs if (af == inet6) if (rtpproxy_offer("FAEE")) t_on_reply("1"); } else { sl_send_reply("403", "Call cannot be served here"); break; }; } if (method == "BYE" || method == "CANCEL") unforce_rtp_proxy(); # Do strict routing if pre-loaded route headers present if (loose_route()) { t_relay(); break; }; if (method == "INVITE") record_route(); if (!t_relay()) sl_reply_error(); } onreply_route[1] { if (!(status=~"183" || status=~"200")) break; rtpproxy_answer("FA"); } opensips-2.2.2/modules/nathelper/examples/alg.cfg000066400000000000000000000051001300170765700220530ustar00rootroot00000000000000# $Id$ # # Simple application level gateway config script. # # Assumes that SER/rtpproxy run on a machine, which connected to # two non-routable letworks: 192.168.0.0/24 and 192.168.1.1/24. # # Correspondingly, this machine has two IP addresses: 192.168.0.1 # and 192.168.1.1. # # 192.168.0.0/24 - "internal" network # 192.168.1.0/24 - "external" network # # rtpproxy started with `-l 192.168.0.1/192.168.1.1' option. # # ------------------ module loading ---------------------------------- loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" loadmodule "/usr/local/lib/ser/modules/nathelper.so" # ----------------- setting module-specific parameters --------------- # -- nathelper params -- modparam("nathelper", "natping_interval", 15) # ------------------ main fun below ---------------------------------- route { # initial sanity checks -- messages with # max_forwars == 0, or excessively long requests, # or those that don't addressed to us if (!mf_process_maxfwd_header("10")) { sl_send_reply("483", "Too Many Hops"); break; }; if (msg:len > max_len) { sl_send_reply("513", "Message too big"); break; }; if (!(uri == myself) && method == "INVITE") { sl_send_reply("403", "Call cannot be served here"); break; }; if (method == "REGISTER") { if (dst_ip == 192.168.0.1) { save("location-internal"); } else if (dst_ip == 192.168.1.1) { save("location-external"); } else { sl_send_reply("403", "Call cannot be served here"); }; break; }; if (method == "INVITE") { if (lookup("location-internal")) { if (dst_ip == 192.168.0.1) if (rtpproxy_offer("FAII")) t_on_reply("1"); if (dst_ip == 192.168.1.1) if (rtpproxy_offer("FAEI")) t_on_reply("1"); } else if (lookup("location-external")) { if (dst_ip == 192.168.0.1) if (rtpproxy_offer("FAIE")) t_on_reply("1"); if (dst_ip == 192.168.1.1) if (rtpproxy_offer("FAEE")) t_on_reply("1"); } else { sl_send_reply("403", "Call cannot be served here"); break; }; } if (method == "BYE" || method == "CANCEL") unforce_rtp_proxy(); # Do strict routing if pre-loaded route headers present if (loose_route()) { t_relay(); break; }; if (method == "INVITE") record_route(); if (!t_relay()) sl_reply_error(); } onreply_route[1] { if (!(status=~"183" || status=~"200")) break; rtpproxy_answer("FA"); } opensips-2.2.2/modules/nathelper/nathelper.c000066400000000000000000001231021300170765700211420ustar00rootroot00000000000000/* * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-10-09 nat_uac_test introduced (jiri) * * 2003-11-06 nat_uac_test permitted from onreply_route (jiri) * * 2004-01-28 nat_uac_test extended to allow testing SDP body (sobomax) * * nat_uac_test extended to allow testing top Via (sobomax) * * 2005-02-25 Force for pinging the socket returned by USRLOC (bogdan) * * * 2005-07-11 SIP ping support added (bogdan) * * * 2006-03-08 fix_nated_sdp() may take one more param to force a specific IP; * force_rtp_proxy() accepts a new flag 's' to swap creation/ * confirmation between requests/replies; * add_rcv_param() may take as parameter a flag telling if the * parameter should go to the contact URI or contact header; * (bogdan) * nh_enable_ping used to enable or disable natping * 2007-09-11 Separate timer process and support for multiple timer processes * (bogdan) * 2010-09-23 Remove force-rtp-proxy function */ #include #include #ifndef __USE_BSD #define __USE_BSD #endif #include #include #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif #include #include #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../forward.h" #include "../../parser/parse_uri.h" #include "../../parser/sdp/sdp_helpr_funcs.h" #include "../../timer.h" #include "../../msg_translator.h" #include "../../socket_info.h" #include "../../mod_fix.h" #include "../registrar/sip_msg.h" #include "../usrloc/usrloc.h" #include "../usrloc/ul_mod.h" #include "sip_pinger.h" #include "../../parser/parse_content.h" #include "../../sr_module.h" #include "nh_table.h" /* NAT UAC test constants */ #define NAT_UAC_TEST_C_1918 0x01 #define NAT_UAC_TEST_V_RCVD 0x02 #define NAT_UAC_TEST_V_1918 0x04 #define NAT_UAC_TEST_S_1918 0x08 #define NAT_UAC_TEST_RPORT 0x10 #define NAT_UAC_TEST_C_RCVD 0x20 #define NAT_UAC_TEST_C_RPORT 0x40 #define MI_SET_NATPING_STATE "nh_enable_ping" #define MI_DEFAULT_NATPING_STATE 1 #define MI_PING_DISABLED "NATping disabled from script" #define MI_PING_DISABLED_LEN (sizeof(MI_PING_DISABLED)-1) #define SKIP_OLDORIGIP (1<<0) #define SKIP_OLDMEDIAIP (1<<1) #define REMOVE_ON_TIMEOUT (sipping_flag && rm_on_to_flag) static int nat_uac_test_f(struct sip_msg* msg, char* str1, char* str2); static int fix_nated_contact_f(struct sip_msg *, char *, char *); static int fix_nated_sdp_f(struct sip_msg *, char *, char *); static int fix_nated_register_f(struct sip_msg *, char *, char *); static int fixup_fix_nated_register(void** param, int param_no); static int fixup_fix_sdp(void** param, int param_no); static int add_rcv_param_f(struct sip_msg *, char *, char *); static int get_oldip_fields_value(modparam_t type, void* val); static void nh_timer(unsigned int, void *); static void ping_checker_timer(unsigned int ticks, void *timer_idx); static int mod_init(void); static void mod_destroy(void); /*mi commands*/ static struct mi_root* mi_enable_natping(struct mi_root* cmd_tree, void* param ); //static usrloc_api_t ul; static int cblen = 0; static str nortpproxy_str = str_init("a=nortpproxy:yes"); static int natping_interval = 0; struct socket_info* force_socket = 0; /* */ int ping_checker_interval = 1; int ping_threshold = 3; int max_pings_lost=3; static struct { const char *cnetaddr; uint32_t netaddr; uint32_t mask; } nets_1918[] = { {"10.0.0.0", 0, 0xffffffffu << 24}, /* RFC 1918 */ {"172.16.0.0", 0, 0xffffffffu << 20}, /* RFC 1918 */ {"192.168.0.0", 0, 0xffffffffu << 16}, /* RFC 1918 */ {"100.64.0.0", 0, 0xffffffffu << 22}, /* RFC 6598 */ {NULL, 0, 0} }; /* * Extract URI from the Contact header field - iterates through all contacts */ int get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c, struct hdr_field **_hdr) { if (*_hdr==NULL) { if ((parse_headers(_m, HDR_EOH_F, 0) == -1) || !_m->contact) return -1; if (!_m->contact->parsed && parse_contact(_m->contact) < 0) { LM_ERR("failed to parse Contact body\n"); return -1; } *_hdr = _m->contact; *_c = ((contact_body_t*)_m->contact->parsed)->contacts; } else { *_c = (*_c)->next; } while (*_c==NULL) { *_hdr = (*_hdr)->sibling; if (*_hdr==NULL) /* no more contact headers */ return -1; if (!(*_hdr)->parsed && parse_contact(*_hdr) < 0) { LM_ERR("failed to parse Contact body\n"); return -1; } *_c = ((contact_body_t*)(*_hdr)->parsed)->contacts; } if (*_c == NULL) /* no more contacts found */ return -1; /* contact found -> parse it */ if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri)<0 || uri->host.len<=0) { LM_ERR("failed to parse Contact URI\n"); return -1; } return 0; } /* * If this parameter is set then the natpinger will ping only contacts * that have the NAT flag set in user location database */ static int ping_nated_only = 0; static const char sbuf[4] = {0, 0, 0, 0}; static char *force_socket_str = 0; static int sipping_flag = -1; static char *sipping_flag_str = 0; static int rm_on_to_flag = -1; static char *rm_on_to_flag_str = 0; static int natping_tcp = 0; static int natping_partitions = 1; static char* rcv_avp_param = NULL; static unsigned short rcv_avp_type = 0; static int rcv_avp_name = -1; static char *natping_socket = 0; static int raw_sock = -1; static unsigned int raw_ip = 0; static unsigned short raw_port = 0; int skip_oldip=0; /*0-> disabled, 1 ->enabled*/ unsigned int *natping_state=0; static cmd_export_t cmds[] = { {"fix_nated_contact", (cmd_function)fix_nated_contact_f, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"fix_nated_contact", (cmd_function)fix_nated_contact_f, 1, fixup_str_null, 0, REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"fix_nated_sdp", (cmd_function)fix_nated_sdp_f, 1, fixup_fix_sdp, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"fix_nated_sdp", (cmd_function)fix_nated_sdp_f, 2, fixup_fix_sdp, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"nat_uac_test", (cmd_function)nat_uac_test_f, 1, fixup_uint_null, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"fix_nated_register", (cmd_function)fix_nated_register_f, 0, fixup_fix_nated_register, 0, REQUEST_ROUTE }, {"add_rcv_param", (cmd_function)add_rcv_param_f, 0, 0, 0, REQUEST_ROUTE }, {"add_rcv_param", (cmd_function)add_rcv_param_f, 1, fixup_uint_null, 0, REQUEST_ROUTE }, {0, 0, 0, 0, 0, 0} }; static param_export_t params[] = { {"natping_interval", INT_PARAM, &natping_interval }, {"ping_nated_only", INT_PARAM, &ping_nated_only }, {"nortpproxy_str", STR_PARAM, &nortpproxy_str.s }, {"received_avp", STR_PARAM, &rcv_avp_param }, {"force_socket", STR_PARAM, &force_socket_str }, {"sipping_from", STR_PARAM, &sipping_from.s }, {"sipping_method", STR_PARAM, &sipping_method.s }, {"sipping_bflag", STR_PARAM, &sipping_flag_str }, {"sipping_bflag", INT_PARAM, &sipping_flag }, {"remove_on_timeout_bflag", STR_PARAM, &rm_on_to_flag_str }, {"remove_on_timeout_bflag", INT_PARAM, &rm_on_to_flag }, {"natping_tcp", INT_PARAM, &natping_tcp }, {"natping_partitions", INT_PARAM, &natping_partitions }, {"natping_socket", STR_PARAM, &natping_socket }, {"oldip_skip", STR_PARAM|USE_FUNC_PARAM, (void*)get_oldip_fields_value}, {"ping_threshold", INT_PARAM, &ping_threshold }, {"max_pings_lost", INT_PARAM, &max_pings_lost }, {0, 0, 0} }; static mi_export_t mi_cmds[] = { {MI_SET_NATPING_STATE, 0, mi_enable_natping, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static module_dependency_t *get_deps_natping_interval(param_export_t *param) { if (*(int *)param->param_pointer <= 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "usrloc", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "natping_interval", get_deps_natping_interval }, { NULL, NULL }, }, }; struct module_exports exports = { "nathelper", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, NULL, params, 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, 0, /* reply processing */ mod_destroy, /* destroy function */ 0 }; static int get_oldip_fields_value(modparam_t type, void* val) { char* flags = (char*)val; while (*flags != '\0') { switch (*flags) { case ' ': break; case 'c': skip_oldip |= SKIP_OLDMEDIAIP; break; case 'o': skip_oldip |= SKIP_OLDORIGIP; break; default: LM_ERR("invalid old ip's fields to skip flag\n"); return -1; } flags++; } return 0; } static int fixup_fix_sdp(void** param, int param_no) { pv_elem_t *model; str s; if (param_no==1) { /* flags */ return fixup_uint_null( param, param_no); } /* new IP */ model=NULL; s.s = (char*)(*param); s.len = strlen(s.s); if(pv_parse_format(&s,&model)<0) { LM_ERR("wrong format[%s]!\n", (char*)(*param)); return E_UNSPEC; } if (model==NULL) { LM_ERR("empty parameter!\n"); return E_UNSPEC; } *param = (void*)model; return 0; } static int fixup_fix_nated_register(void** param, int param_no) { if (rcv_avp_name < 0) { LM_ERR("you must set 'received_avp' parameter. Must be same value as" " parameter 'received_avp' of registrar module\n"); return -1; } return 0; } static struct mi_root* mi_enable_natping(struct mi_root* cmd_tree, void* param ) { unsigned int value; struct mi_node* node; struct mi_root* root; char *s; int len; if (natping_state==NULL) return init_mi_tree( 400, MI_PING_DISABLED, MI_PING_DISABLED_LEN); node = cmd_tree->node.kids; if(node == NULL) { root = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (root==NULL) return NULL; node = &root->node; s = int2str(*natping_state, &len); if (!add_mi_node_child(node, MI_DUP_VALUE, MI_SSTR("Status"), s, len)){ LM_ERR("cannot add the child node to the tree\n"); goto error; } return root; } value = 0; if( strno2int( &node->value, &value) <0) goto error; (*natping_state) = value?1:0; return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } static int init_raw_socket(void) { int on = 1; raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (raw_sock ==-1) { LM_ERR("cannot create raw socket\n"); return -1; } if (setsockopt(raw_sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1) { LM_ERR("cannot set socket options\n"); return -1; } return raw_sock; } static int get_natping_socket(char *socket, unsigned int *ip, unsigned short *port) { struct hostent* he; str host; int lport; int lproto; if (parse_phostport( socket, strlen(socket), &host.s, &host.len, &lport, &lproto)!=0){ LM_CRIT("invalid natping_socket parameter <%s>\n",natping_socket); return -1; } if (lproto!=PROTO_UDP && lproto!=PROTO_NONE) { LM_CRIT("natping_socket can be only UDP <%s>\n",natping_socket); return 0; } lproto = PROTO_UDP; *port = lport?(unsigned short)lport:SIP_PORT; he = sip_resolvehost( &host, port, (unsigned short*)(void*)&lproto, 0, 0); if (he==0) { LM_ERR("could not resolve hostname:\"%.*s\"\n", host.len, host.s); return -1; } if (he->h_addrtype != AF_INET) { LM_ERR("only ipv4 addresses allowed in natping_socket\n"); return -1; } memcpy( ip, he->h_addr_list[0], he->h_length); return 0; } static int mod_init(void) { int i; bind_usrloc_t bind_usrloc; str socket_str; struct in_addr addr; pv_spec_t avp_spec; str s; if (rcv_avp_param && *rcv_avp_param) { s.s = rcv_avp_param; s.len = strlen(s.s); if (pv_parse_spec(&s, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %s AVP definition\n", rcv_avp_param); return -1; } if(pv_get_avp_name(0, &avp_spec.pvp, &rcv_avp_name, &rcv_avp_type)!=0) { LM_ERR("[%s]- invalid AVP definition\n", rcv_avp_param); return -1; } } else { rcv_avp_name = -1; rcv_avp_type = 0; } if (nortpproxy_str.s==NULL || nortpproxy_str.s[0]==0) { nortpproxy_str.len = 0; nortpproxy_str.s = NULL; } else { nortpproxy_str.len = strlen(nortpproxy_str.s); while (nortpproxy_str.len > 0 && (nortpproxy_str.s[nortpproxy_str.len - 1] == '\r' || nortpproxy_str.s[nortpproxy_str.len - 1] == '\n')) nortpproxy_str.len--; if (nortpproxy_str.len == 0) nortpproxy_str.s = NULL; } /* enable all the pinging stuff only if pinging interval is set */ if (natping_interval > 0) { bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (!bind_usrloc) { LM_ERR("can't find usrloc module\n"); return -1; } if (bind_usrloc(&ul) < 0) { return -1; } if (force_socket_str) { socket_str.s=force_socket_str; socket_str.len=strlen(socket_str.s); force_socket=grep_sock_info(&socket_str,0,0); } /* create raw socket? */ if (natping_socket && natping_socket[0]) { if (get_natping_socket( natping_socket, &raw_ip, &raw_port)!=0) return -1; if (init_raw_socket() < 0) return -1; } natping_state =(unsigned int *) shm_malloc(sizeof(unsigned int)); if (!natping_state) { LM_ERR("no shmem left\n"); return -1; } *natping_state = MI_DEFAULT_NATPING_STATE; if (ping_nated_only && ul.nat_flag==0) { LM_ERR("bad config - ping_nated_only enabled, but no nat bflag" " set in usrloc module\n"); return -1; } if (natping_partitions>8) { LM_ERR("too many natping processes (%d) max=8\n", natping_partitions); return -1; } fix_flag_name(sipping_flag_str, sipping_flag); sipping_flag=get_flag_id_by_name(FLAG_TYPE_BRANCH, sipping_flag_str); sipping_flag = (sipping_flag==-1)?0:(1< natping_interval) { LM_WARN("Maximum ping threshold must be smaller than " "the interval between two pings! Setting threshold " "to half the ping interval!\n"); ping_threshold = natping_interval/2; } } for( i=0 ; is; cp < sx->s + sx->len; cp++) if (*cp != '0' && *cp != ':') return 0; return 1; } return (sx->len == 7 && memcmp("0.0.0.0", sx->s, 7) == 0); } /* * Replaces ip:port pair in the Contact: field with the source address * of the packet. */ static int fix_nated_contact_f(struct sip_msg* msg, char* str1, char* str2) { int len, len1; char *cp, *buf, temp, *p; contact_t *c; struct hdr_field *hdr; struct lump *anchor; struct sip_uri uri; str hostport, left, left2; int is_enclosed; str *params = (str*)str1; if (params && params->len==0) params = 0; for ( c=NULL,hdr=NULL ; get_contact_uri(msg, &uri, &c, &hdr)==0 ; ) { /* if uri string points outside the original msg buffer, it means the URI was already changed, and we cannot do it again */ if( c->uri.s < msg->buf || c->uri.s > msg->buf+msg->len ) { LM_ERR("SCRIPT BUG - second attempt to change URI Contact \n"); return -1; } hostport = uri.host; if (uri.port.len > 0) hostport.len = uri.port.s + uri.port.len - uri.host.s; left.s = hostport.s + hostport.len; left.len = c->uri.s+c->uri.len - left.s; if (uri.maddr.len) { left2.s = uri.maddr_val.s + uri.maddr_val.len; left2.len = left.s + left.len - left2.s; left.len=uri.maddr.s-1-left.s; } else { left2.s = ""; left2.len = 0; } is_enclosed = 0; p = hostport.s + hostport.len; /*start searching after ip:port */ cp = (c->name.s?c->name.s:c->uri.s) + c->len; /* where to end */ for( ; p') {is_enclosed=1;hostport.len=p-uri.host.s;break;} //LM_DBG("--removing %d |%.*s|\n",hostport.s+hostport.len-c->uri.s, // hostport.s+hostport.len-c->uri.s, c->uri.s); anchor = del_lump(msg, c->uri.s-msg->buf /* offset */, hostport.s+hostport.len-c->uri.s /* len */, HDR_CONTACT_T); if (anchor == 0) return -1; cp = ip_addr2a(&msg->rcv.src_ip); len = (hostport.s-c->uri.s) + strlen(cp) + 6 /* :port */ + 2 /* just in case if IPv6 */ + (params?params->len+(is_enclosed?0:2):0) + 1 + left.len + left2.len; buf = pkg_malloc(len); if (buf == NULL) { LM_ERR("out of pkg memory\n"); return -1; } temp = hostport.s[0]; hostport.s[0] = '\0'; if (params==NULL) { if (msg->rcv.src_ip.af==AF_INET6) len1 = snprintf(buf, len, "%s[%s]:%d%.*s%.*s", c->uri.s, cp, msg->rcv.src_port,left.len,left.s,left2.len,left2.s); else len1 = snprintf(buf, len, "%s%s:%d%.*s%.*s", c->uri.s, cp, msg->rcv.src_port,left.len,left.s,left2.len,left2.s); } else if (!is_enclosed) { if (msg->rcv.src_ip.af==AF_INET6) len1 = snprintf(buf, len, "<%s[%s]:%d%.*s>", c->uri.s, cp, msg->rcv.src_port,params->len,params->s); else len1 = snprintf(buf, len, "<%s%s:%d%.*s>", c->uri.s, cp, msg->rcv.src_port,params->len,params->s); } else { if (msg->rcv.src_ip.af==AF_INET6) len1 = snprintf(buf, len, "%s[%s]:%d%.*s%.*s%.*s", c->uri.s, cp, msg->rcv.src_port,params->len,params->s, left.len,left.s,left2.len,left2.s); else len1 = snprintf(buf, len, "%s%s:%d%.*s%.*s%.*s", c->uri.s, cp, msg->rcv.src_port,params->len,params->s, left.len,left.s,left2.len,left2.s); } if (len1 < len) len = len1; hostport.s[0] = temp; //LM_DBG("lump--- |%.*s|\n",len,buf); if (insert_new_lump_after(anchor, buf, len, HDR_CONTACT_T) == 0) { pkg_free(buf); return -1; } if (params==NULL || is_enclosed) { c->uri.s = buf; c->uri.len = len1; } else { c->uri.s = buf + 1; c->uri.len = len - 2; } //LM_DBG("new uri is--- |%.*s|\n",c->uri.len,c->uri.s); } return 1; } /* * Test if IP address pointed to by saddr belongs to RFC1918 / RFC6598 networks */ static inline int is1918addr(str *saddr) { struct in_addr addr; uint32_t netaddr; int i, rval; char backup; rval = -1; backup = saddr->s[saddr->len]; saddr->s[saddr->len] = '\0'; if (inet_aton(saddr->s, &addr) != 1) goto theend; netaddr = ntohl(addr.s_addr); for (i = 0; nets_1918[i].cnetaddr != NULL; i++) { if ((netaddr & nets_1918[i].mask) == nets_1918[i].netaddr) { rval = 1; goto theend; } } rval = 0; theend: saddr->s[saddr->len] = backup; return rval; } /* * test for occurrence of RFC1918 / RFC6598 IP address in Contact HF */ static int contact_1918(struct sip_msg* msg) { struct sip_uri uri; struct hdr_field *hdr; contact_t* c; for( hdr=NULL,c=NULL ; get_contact_uri(msg, &uri, &c, &hdr)==0 ; ) if ( is1918addr(&(uri.host)) == 1) return 1; return 0; } /* * test for occurrence of RFC1918 / RFC6598 IP address in SDP */ static int sdp_1918(struct sip_msg* msg) { str body, ip; int pf; struct multi_body * bodies; struct part *p; int ret = 0; bodies = get_all_bodies(msg); if( bodies == NULL) { LM_DBG("Unable to get bodies from message\n"); return 0; } p = bodies->first; while(p) { body = p->body; trim_r(body); if( p->content_type != ((TYPE_APPLICATION << 16) + SUBTYPE_SDP) || body.len == 0) { p=p->next; continue; } if (extract_mediaip(&body, &ip, &pf, "c=") == -1) { LM_ERR("can't extract media IP from the SDP\n"); return 0; } if (pf != AF_INET || isnulladdr(&ip, pf)) return 0; ret |= (is1918addr(&ip) == 1) ? 1 : 0; p= p->next; } return ret; } /* * test for occurrence of RFC1918 / RFC6598 IP address in top Via */ static int via_1918(struct sip_msg* msg) { return (is1918addr(&(msg->via1->host)) == 1) ? 1 : 0; } /* * test for Contact IP against received IP */ static int contact_rcv(struct sip_msg* msg) { struct sip_uri uri; contact_t* c; struct hdr_field *hdr; for( hdr=NULL,c=NULL ; get_contact_uri(msg, &uri, &c, &hdr)==0 ; ) if ( check_ip_address(&msg->rcv.src_ip, &uri.host, uri.port_no, uri.proto, received_dns)!=0 ) return 1; return 0; } /* * test for Contact port against received port */ static int contact_rport(struct sip_msg* msg) { struct sip_uri uri; contact_t* c; struct hdr_field *hdr; for( hdr=NULL,c=NULL ; get_contact_uri(msg, &uri, &c, &hdr)==0 ; ) { if ( msg->rcv.src_port != get_uri_port( &uri, NULL) ) return 1; } return 0; } static int nat_uac_test_f(struct sip_msg* msg, char* str1, char* str2) { int tests; tests = (int)(long)str1; /* return true if any of the NAT-UAC tests holds */ /* test if the source port is different from the port in Via */ if ((tests & NAT_UAC_TEST_RPORT) && (msg->rcv.src_port!=(msg->via1->port?msg->via1->port:SIP_PORT)) ){ return 1; } /* * test if source address of signaling is different from * address advertised in Via */ if ((tests & NAT_UAC_TEST_V_RCVD) && received_test(msg)) return 1; /* * test for occurrences of RFC1918 / RFC6598 addresses in Contact * header field */ if ((tests & NAT_UAC_TEST_C_1918) && (contact_1918(msg)>0)) return 1; /* * test for occurrences of RFC1918 / RFC6598 addresses in SDP body */ if ((tests & NAT_UAC_TEST_S_1918) && sdp_1918(msg)) return 1; /* * test for occurrences of RFC1918 / RFC6598 addresses top Via */ if ((tests & NAT_UAC_TEST_V_1918) && via_1918(msg)) return 1; /* * test if source address of signaling is different from * address advertised in Contact */ if ((tests & NAT_UAC_TEST_C_RCVD) && contact_rcv(msg)) return 1; /* * test if source port of signaling is different from * port advertised in Contact */ if ((tests & NAT_UAC_TEST_C_RPORT) && contact_rport(msg)) return 1; /* no test succeeded */ return -1; } #define ADD_ADIRECTION 0x01 #define FIX_MEDIP 0x02 #define ADD_ANORTPPROXY 0x04 #define FIX_ORGIP 0x08 #define ADIRECTION "a=direction:active" #define ADIRECTION_LEN (sizeof(ADIRECTION) - 1) #define AOLDMEDIP "a=oldcip:" #define AOLDMEDIP_LEN (sizeof(AOLDMEDIP) - 1) #define AOLDMEDIP6 "a=oldcip6:" #define AOLDMEDIP6_LEN (sizeof(AOLDMEDIP6) - 1) #define AOLDORIGIP "a=oldoip:" #define AOLDORIGIP_LEN (sizeof(AOLDORIGIP) - 1) #define AOLDORIGIP6 "a=oldoip6:" #define AOLDORIGIP6_LEN (sizeof(AOLDORIGIP6) - 1) static int alter_mediaip(struct sip_msg *msg, str *body, str *oldip, int oldpf, str *newip, int newpf, int preserve, char type) { char *buf; int offset; struct lump* anchor; str omip, nip, oip; /* check that updating mediaip is really necessary */ if (oldpf == newpf && isnulladdr(oldip, oldpf)) return 0; if (newip->len == oldip->len && memcmp(newip->s, oldip->s, newip->len) == 0) return 0; if (preserve != 0) { anchor = anchor_lump(msg, body->s + body->len - msg->buf, 0); if (anchor == NULL) { LM_ERR("anchor_lump failed\n"); return -1; } if (oldpf == AF_INET6) { switch (type) { case 'c': omip.s = AOLDMEDIP6; omip.len = AOLDMEDIP6_LEN; break; case 'o': omip.s = AOLDORIGIP6; omip.len = AOLDORIGIP6_LEN; break; default: LM_ERR("IPv6: invalid field\n"); return -1; } } else { switch (type) { case 'c': omip.s = AOLDMEDIP; omip.len = AOLDMEDIP_LEN; break; case 'o': omip.s = AOLDORIGIP; omip.len = AOLDORIGIP_LEN; break; default: LM_ERR("IPv4: invalid field\n"); return -1; } } buf = pkg_malloc(omip.len + oldip->len + CRLF_LEN); if (buf == NULL) { LM_ERR("out of pkg memory\n"); return -1; } memcpy(buf, CRLF, CRLF_LEN); memcpy(buf + CRLF_LEN, omip.s, omip.len); memcpy(buf + CRLF_LEN + omip.len, oldip->s, oldip->len); if (insert_new_lump_after(anchor, buf, omip.len + oldip->len + CRLF_LEN, 0) == NULL) { LM_ERR("insert_new_lump_after failed\n"); pkg_free(buf); return -1; } } if (oldpf == newpf) { nip.len = newip->len; nip.s = pkg_malloc(nip.len); if (nip.s == NULL) { LM_ERR("out of pkg memory\n"); return -1; } memcpy(nip.s, newip->s, newip->len); } else { nip.len = newip->len + 2; nip.s = pkg_malloc(nip.len); if (nip.s == NULL) { LM_ERR("out of pkg memory\n"); return -1; } memcpy(nip.s + 2, newip->s, newip->len); nip.s[0] = (newpf == AF_INET6) ? '6' : '4'; nip.s[1] = ' '; } oip = *oldip; if (oldpf != newpf) { do { oip.s--; oip.len++; } while (*oip.s != '6' && *oip.s != '4'); } offset = oip.s - msg->buf; anchor = del_lump(msg, offset, oip.len, 0); if (anchor == NULL) { LM_ERR("del_lump failed\n"); pkg_free(nip.s); return -1; } if (insert_new_lump_after(anchor, nip.s, nip.len, 0) == 0) { LM_ERR("insert_new_lump_after failed\n"); pkg_free(nip.s); return -1; } return 0; } static inline int get_field_flag(char value) { switch (value) { case 'c': return SKIP_OLDMEDIAIP; case 'o': return SKIP_OLDORIGIP; default : return -1; } return -1; } static inline int replace_sdp_ip(struct sip_msg* msg, str *org_body, char *line, str *ip) { str body1, oldip, newip; str body = *org_body; unsigned hasreplaced = 0; int pf, pf1 = 0; str body2; char *bodylimit = body.s + body.len; /* Iterate all lines and replace ips in them. */ if (!ip) { newip.s = ip_addr2a(&msg->rcv.src_ip); newip.len = strlen(newip.s); } else { newip = *ip; } body1 = body; for(;;) { if (extract_mediaip(&body1, &oldip, &pf,line) == -1) break; if (pf != AF_INET) { LM_ERR("not an IPv4 address in '%s' SDP\n",line); return -1; } if (!pf1) pf1 = pf; else if (pf != pf1) { LM_ERR("mismatching address families in '%s' SDP\n",line); return -1; } body2.s = oldip.s + oldip.len; body2.len = bodylimit - body2.s; if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf, !(get_field_flag(line[0])&skip_oldip), line[0]) == -1) { /*if flag set do not set oldmediaip field*/ LM_ERR("can't alter '%s' IP\n",line); return -1; } hasreplaced = 1; body1 = body2; } if (!hasreplaced) { LM_ERR("can't extract '%s' IP from the SDP\n",line); return -1; } return 0; } static int fix_nated_sdp_f(struct sip_msg* msg, char* str1, char* str2) { str body; str ip; int level; char *buf; struct lump* anchor; struct multi_body * bodies; struct part * p; level = (int)(long)str1; if (str2 && pv_printf_s( msg, (pv_elem_p)str2, &ip)!=0) return -1; bodies = get_all_bodies(msg); if( bodies == NULL) { LM_ERR("Unable to get bodies from message\n"); return -1; } p = bodies->first; while(p) { body = p->body; trim_r(body); if( p->content_type != ((TYPE_APPLICATION << 16) + SUBTYPE_SDP) || body.len == 0) { p=p->next; continue; } if (level & (ADD_ADIRECTION | ADD_ANORTPPROXY)) { msg->msg_flags |= FL_FORCE_ACTIVE; anchor = anchor_lump(msg, body.s + body.len - msg->buf, 0); if (anchor == NULL) { LM_ERR("anchor_lump failed\n"); return -1; } if (level & ADD_ADIRECTION) { buf = pkg_malloc((ADIRECTION_LEN + CRLF_LEN) * sizeof(char)); if (buf == NULL) { LM_ERR("out of pkg memory\n"); return -1; } memcpy(buf, CRLF, CRLF_LEN); memcpy(buf + CRLF_LEN, ADIRECTION, ADIRECTION_LEN); if (insert_new_lump_after(anchor, buf, ADIRECTION_LEN + CRLF_LEN, 0) == NULL) { LM_ERR("insert_new_lump_after failed 1\n"); pkg_free(buf); return -1; } } if ((level & ADD_ANORTPPROXY) && nortpproxy_str.len) { buf = pkg_malloc((nortpproxy_str.len + CRLF_LEN) * sizeof(char)); if (buf == NULL) { LM_ERR("out of pkg memory\n"); return -1; } memcpy(buf, CRLF, CRLF_LEN); memcpy(buf + CRLF_LEN, nortpproxy_str.s, nortpproxy_str.len); if (insert_new_lump_after(anchor, buf, nortpproxy_str.len + CRLF_LEN, 0) == NULL) { LM_ERR("insert_new_lump_after failed 2\n"); pkg_free(buf); return -1; } } } if (level & FIX_ORGIP) { /* Iterate all o= and replace ips in them. */ if (replace_sdp_ip(msg, &body, "o=", str2?&ip:0)==-1) return -1; } if (level & FIX_MEDIP) { /* Iterate all c= and replace ips in them. */ if (replace_sdp_ip(msg, &body, "c=", str2?&ip:0)==-1) return -1; } p= p->next; } return 1; } static u_short raw_checksum(unsigned char *buffer, int len) { u_long sum = 0; while (len > 1) { sum += *buffer << 8; buffer++; sum += *buffer; buffer++; len -= 2; } if (len) { sum += *buffer << 8; } sum = (sum >> 16) + (sum & 0xffff); sum = (sum >> 16) + (sum); return (u_short) ~sum; } static int send_raw(const char *buf, int buf_len, union sockaddr_union *to, const unsigned int s_ip, const unsigned int s_port) { struct ip *ip; struct udphdr *udp; unsigned char packet[50]; int len = sizeof(struct ip) + sizeof(struct udphdr) + buf_len; if (len > sizeof(packet)) { LM_ERR("payload too big\n"); return -1; } ip = (struct ip*) packet; udp = (struct udphdr *) (packet + sizeof(struct ip)); memcpy(packet + sizeof(struct ip) + sizeof(struct udphdr), buf, buf_len); ip->ip_v = 4; ip->ip_hl = sizeof(struct ip) / 4; // no options ip->ip_tos = 0; ip->ip_len = htons(len); ip->ip_id = 23; ip->ip_off = 0; ip->ip_ttl = 69; ip->ip_p = 17; ip->ip_src.s_addr = s_ip; ip->ip_dst.s_addr = to->sin.sin_addr.s_addr; ip->ip_sum = raw_checksum((unsigned char *) ip, sizeof(struct ip)); udp->uh_sport = htons(s_port); udp->uh_dport = to->sin.sin_port; udp->uh_ulen = htons((unsigned short) sizeof(struct udphdr) + buf_len); udp->uh_sum = 0; return sendto(raw_sock, packet, len, 0, (struct sockaddr *) to, sizeof(struct sockaddr_in)); } static void nh_timer(unsigned int ticks, void *timer_idx) { int rval; void *buf = NULL; void *cp; str c; str opt; str path; union sockaddr_union to; struct hostent *he; struct socket_info* send_sock; unsigned int flags; struct proxy_l next_hop; uint64_t contact_id=0; udomain_t *d; if ((*natping_state) == 0) goto done; if (cblen > 0) { buf = pkg_malloc(cblen); if (buf == NULL) { LM_ERR("out of pkg memory\n"); goto done; } } for ( d=ul.get_next_udomain(NULL); d; d=ul.get_next_udomain(d)) { rval = ul.get_domain_ucontacts(d, buf, cblen, (ping_nated_only?ul.nat_flag:0), ((unsigned int)(unsigned long)timer_idx)*natping_interval+ (ticks%natping_interval), natping_partitions*natping_interval, REMOVE_ON_TIMEOUT?1:0); if (rval<0) { LM_ERR("failed to fetch contacts\n"); goto done; } if (rval > 0) { if (buf != NULL) pkg_free(buf); cblen += rval + 128 /*some extra*/; buf = pkg_malloc(cblen); if (buf == NULL) { LM_ERR("out of pkg memory\n"); goto done; } rval = ul.get_domain_ucontacts(d, buf, cblen, (ping_nated_only?ul.nat_flag:0), ((unsigned int)(unsigned long)timer_idx)*natping_interval+ (ticks%natping_interval), natping_partitions*natping_interval, REMOVE_ON_TIMEOUT?1:0); if (rval != 0) { goto done; } } if (buf == NULL) goto done; tcp_no_new_conn = 1; cp = buf; while (1) { memcpy(&(c.len), cp, sizeof(c.len)); if (c.len == 0) break; c.s = (char*)cp + sizeof(c.len); cp = (char*)cp + sizeof(c.len) + c.len; memcpy(&path.len, cp, sizeof(path.len)); path.s = path.len ? ((char*)cp + sizeof(path.len)) : NULL; cp = (char*)cp + sizeof(path.len) + path.len; memcpy(&send_sock, cp, sizeof(send_sock)); cp = (char*)cp + sizeof(send_sock); memcpy(&flags, cp, sizeof(flags)); cp = (char*)cp + sizeof(flags); memcpy(&next_hop, cp, sizeof(next_hop)); cp = (char*)cp + sizeof(next_hop); if (REMOVE_ON_TIMEOUT) { memcpy(&contact_id, cp, sizeof(contact_id)); cp = (char*)cp + sizeof(contact_id); } if (next_hop.proto != PROTO_NONE && next_hop.proto != PROTO_UDP && (natping_tcp == 0 || (next_hop.proto != PROTO_TCP && next_hop.proto != PROTO_TLS && next_hop.proto != PROTO_WSS && next_hop.proto != PROTO_WS))) continue; LM_DBG("resolving next hop: '%.*s'\n", next_hop.name.len, next_hop.name.s); he = sip_resolvehost(&next_hop.name, &next_hop.port, &next_hop.proto, 0, NULL); if (!he) { LM_ERR("failed to resolve next hop: '%.*s'\n", next_hop.name.len, next_hop.name.s); continue; } hostent2su(&to, he, 0, next_hop.port); if (!send_sock) { send_sock = force_socket ? force_socket : get_send_socket(0, &to, next_hop.proto); if (!send_sock) { LM_ERR("can't get sending socket\n"); continue; } } if ((flags & sipping_flag) && (opt.s = build_sipping(d, &c, send_sock, &path, &opt.len, contact_id ,flags&rm_on_to_flag))) { if (msg_send(send_sock, next_hop.proto, &to, 0, opt.s, opt.len, NULL) < 0) { LM_ERR("sip msg_send failed\n"); } } else if (raw_ip && next_hop.proto == PROTO_UDP) { if (send_raw((char*)sbuf, sizeof(sbuf), &to, raw_ip, raw_port)<0) { LM_ERR("send_raw failed\n"); } } else { if (msg_send(send_sock, next_hop.proto, &to, 0, (char *)sbuf, sizeof(sbuf), NULL) < 0) { LM_ERR("sip msg_send failed!\n"); } } } } tcp_no_new_conn = 0; done: if (buf) pkg_free(buf); } /* * Create received SIP uri that will be either * passed to registrar in an AVP or apended * to Contact header field as a parameter */ static int create_rcv_uri(str* uri, struct sip_msg* m) { static char buf[MAX_URI_SIZE]; char* p; str ip, port; int len; str proto; if (!uri || !m) { LM_ERR("invalid parameter value\n"); return -1; } ip.s = ip_addr2a(&m->rcv.src_ip); ip.len = strlen(ip.s); port.s = int2str(m->rcv.src_port, &port.len); /* TODO: make this dynamic based on protos */ switch(m->rcv.proto) { case PROTO_NONE: case PROTO_UDP: proto.s = 0; /* Do not add transport parameter, UDP is default */ proto.len = 0; break; case PROTO_TCP: proto.s = "TCP"; proto.len = 3; break; case PROTO_TLS: proto.s = "TLS"; proto.len = 3; break; case PROTO_SCTP: proto.s = "SCTP"; proto.len = 4; break; case PROTO_WS: case PROTO_WSS: proto.s = "WS"; proto.len = 2; break; default: LM_ERR("unknown transport protocol\n"); return -1; } len = 4 + ip.len + 2*(m->rcv.src_ip.af==AF_INET6)+ 1 + port.len; if (proto.s) { len += TRANSPORT_PARAM_LEN; len += proto.len; } if (len > MAX_URI_SIZE) { LM_ERR("buffer too small\n"); return -1; } p = buf; memcpy(p, "sip:", 4); p += 4; if (m->rcv.src_ip.af==AF_INET6) *p++ = '['; memcpy(p, ip.s, ip.len); p += ip.len; if (m->rcv.src_ip.af==AF_INET6) *p++ = ']'; *p++ = ':'; memcpy(p, port.s, port.len); p += port.len; if (proto.s) { memcpy(p, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); p += TRANSPORT_PARAM_LEN; memcpy(p, proto.s, proto.len); p += proto.len; } uri->s = buf; uri->len = len; return 0; } /* * Add received parameter to Contacts for further * forwarding of the REGISTER requuest */ static int add_rcv_param_f(struct sip_msg* msg, char* str1, char* str2) { contact_t* c; struct lump* anchor; char* param; str uri; int hdr_param; hdr_param = str1?0:1; if (create_rcv_uri(&uri, msg) < 0) { return -1; } if (contact_iterator(&c, msg, 0) < 0) { return -1; } while(c) { param = (char*)pkg_malloc(RECEIVED_LEN + 2 + uri.len); if (!param) { LM_ERR("no pkg memory left\n"); return -1; } memcpy(param, RECEIVED, RECEIVED_LEN); param[RECEIVED_LEN] = '\"'; memcpy(param + RECEIVED_LEN + 1, uri.s, uri.len); param[RECEIVED_LEN + 1 + uri.len] = '\"'; if (hdr_param) { /* add the param as header param */ anchor = anchor_lump(msg, c->name.s + c->len - msg->buf, 0); } else { /* add the param as uri param */ anchor = anchor_lump(msg, c->uri.s + c->uri.len - msg->buf, 0); } if (anchor == NULL) { LM_ERR("anchor_lump failed\n"); return -1; } if (insert_new_lump_after(anchor, param, RECEIVED_LEN + 1 + uri.len + 1, 0) == 0) { LM_ERR("insert_new_lump_after failed\n"); pkg_free(param); return -1; } if (contact_iterator(&c, msg, c) < 0) { return -1; } } return 1; } /* * Create an AVP to be used by registrar with the source IP and port * of the REGISTER */ static int fix_nated_register_f(struct sip_msg* msg, char* str1, char* str2) { str uri; int_str val; if(rcv_avp_name < 0) return 1; if (create_rcv_uri(&uri, msg) < 0) { return -1; } val.s = uri; if (add_avp(AVP_VAL_STR|rcv_avp_type, rcv_avp_name, val) < 0) { LM_ERR("failed to create AVP\n"); return -1; } return 1; } /* * timer which checks entries that responded or * did not responded to pings */ static void ping_checker_timer(unsigned int ticks, void *timer_idx) { time_t ctime; uint64_t _contact_id; udomain_t *_d; struct nh_table *table; struct ping_cell *cell, *first, *last, *prev; table = get_htable(); ctime=now; /* detect cells for which threshold has been exceeded */ /* something very fishy here */ lock_get(&table->timer_list.mutex); first = last = table->timer_list.first; if (table->timer_list.first == NULL || table->timer_list.last == NULL) { /* nothing to do here - empty list */ goto out_release_lock; } /* only valid elements */ if (table->timer_list.first == table->timer_list.last && ((ctime-last->timestamp) < ping_threshold)) { goto out_release_lock; } /* at least one invalid element * */ prev=NULL; while (last != LIST_END_CELL && ((ctime-last->timestamp)>ping_threshold)) { prev = last; last = last->tnext; } if (prev != NULL) { /* have at least 1 element to remove */ if (last == LIST_END_CELL) { /* all the list contains expired elements */ table->timer_list.first = table->timer_list.last = NULL; } else { /* still have non expired elements * move list start to first valid one */ table->timer_list.first = last; } last = prev; last->tnext = LIST_END_CELL; } else { /* nothing to remove - timer list remains the same */ goto out_release_lock;; } lock_release(&table->timer_list.mutex); /* * getting here means we have at least one element in the list to remove */ cell = first; do { if (cell->timestamp == 0) { /* ping confirmed and unlinked from hash; only free the cell */ prev = cell; cell = cell->tnext; shm_free(prev); continue; } /* we need lock since we don't know whether we will remove this * cell from the list or not */ lock_hash(cell->hash_id); /* for these cells threshold has been exceeded */ LM_DBG("cell with cid %llu has %d unresponded pings\n", (long long unsigned int)cell->contact_id, cell->not_responded); cell->not_responded++; if (cell->not_responded >= max_pings_lost) { LM_DBG("cell with cid %llu exceeded max pings threshold! removing...\n", (long long unsigned int)cell->contact_id); _contact_id = cell->contact_id; _d = cell->d; remove_given_cell(cell, &table->entries[cell->hash_id]); /* we put the lock on cell which now moved into prev */ unlock_hash(cell->hash_id); prev = cell; cell = cell->tnext; shm_free(prev); if (ul.delete_ucontact_from_id && ul.delete_ucontact_from_id(_d, _contact_id, 0) < 0) { /* we keep going since it might work for other contacts */ LM_ERR("failed to remove ucontact from db\n"); } } else { prev = cell; cell = cell->tnext; /* allow cell to be reintroduced in timer list */ prev->tnext = FREE_CELL; /* we put the lock on cell which now moved into prev */ unlock_hash(prev->hash_id); } } while (cell != LIST_END_CELL); out_release_lock: lock_release(&table->timer_list.mutex); } opensips-2.2.2/modules/nathelper/nathelper.cfg000066400000000000000000000030341300170765700214600ustar00rootroot00000000000000# # example script showing use of nathelper module # (incomplete for sake of brevity) # # ----------- global configuration parameters ------------------------ # debugging mode debug_mode=yes # ------------------ module loading ---------------------------------- loadmodule "modules/nathelper/nathelper.so" loadmodule "modules/textops/textops.so" loadmodule "modules/tm/tm.so" loadmodule "modules/rr/rr.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/sl/sl.so" # ------------------ request-routing script -------------------------- # main routing logic route{ # compulsory processing of Route header fields and adding RR loose_route(); # ATA's are symmetric but don't advertise it -- force use of rport if (search("User-Agent: Cisco ATA.*")) { setflag(1); # remember this is ATA force_rport(); fix_nated_contact(); }; /* registration (uses rewritten contacts) */ if (method=="REGISTER") { save("foo.bar.com"); break; }; if (method=="INVITE") { record_route(); if (isflagset(1)) { # ATA ? fix_nated_sdp("3"); }; /* set up reply processing */ t_on_reply("1"); }; if (method == "INVITE" || method == "CANCEL") { if (!lookup("foo.bar.com")) { sl_send_reply("404", "Not Found"); break; }; }; /* set up reply processing and forward statefuly */ t_relay(); } # all incoming replies for t_onrepli-ed transactions enter here onreply_route[1] { if (status=~"2[0-9][0-9]" && search("Server: Cisco ATA.*")) fix_nated_contact(); fix_nated_sdp("3"); } opensips-2.2.2/modules/nathelper/nathelper_rtpp.cfg000066400000000000000000000031221300170765700225230ustar00rootroot00000000000000# # example script showing use of nathelper module with RTP proxy # (incomplete for sake of brevity) # # ----------- global configuration parameters ------------------------ # debugging mode debug_mode=yes # ------------------ module loading ---------------------------------- loadmodule "modules/rtpproxy/rtpproxy.so" loadmodule "modules/nathelper/nathelper.so" loadmodule "modules/textops/textops.so" loadmodule "modules/tm/tm.so" loadmodule "modules/rr/rr.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/sl/sl.so" # ------------------ request-routing script -------------------------- # main routing logic route{ # compulsory processing of Route header fields and adding RR loose_route(); # ATA's are symmetric but don't advertise it -- force use of rport if (search("User-Agent: Cisco ATA.*")) { setflag(1); # remember this is ATA force_rport(); fix_nated_contact(); }; /* registration (uses rewritten contacts) */ if (method=="REGISTER") { save("foo.bar.com"); break; }; if (method=="INVITE") { record_route(); if (isflagset(1)) { # ATA ? force_rtp_proxy(); }; /* set up reply processing */ t_on_reply("1"); }; if (method == "INVITE" || method == "CANCEL") { if (!lookup("foo.bar.com")) { sl_send_reply("404", "Not Found"); break; }; }; /* set up reply processing and forward statefuly */ t_relay(); } # all incoming replies for t_onrepli-ed transactions enter here onreply_route[1] { if (status=~"2[0-9][0-9]" && search("Server: Cisco ATA.*")) fix_nated_contact(); force_rtp_proxy(); } opensips-2.2.2/modules/nathelper/nh_locks.h000066400000000000000000000020231300170765700207630ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #ifndef _NH_LOCKS_H #define _NH_LOCKS_H #include "../../locking.h" #define lock(__S__) lock_get(__S__) #define unlock(__S__) lock_release(__S__) #endif opensips-2.2.2/modules/nathelper/nh_table.c000066400000000000000000000066111300170765700207410ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #include "nh_table.h" #include "nh_locks.h" #include "../usrloc/usrloc.h" static struct nh_table* n_table=0; extern int ping_checker_interval; extern int ping_threshold; extern usrloc_api_t ul; void lock_hash(int i) { lock(&n_table->entries[i].mutex); } void unlock_hash(int i) { unlock(&n_table->entries[i].mutex); } struct nh_table* init_hash_table(void) { int i; n_table = shm_malloc(sizeof(struct nh_table)); if (n_table==NULL) { LM_ERR("no more shared memory\n"); goto error; } memset(n_table, 0, sizeof(struct nh_table)); for ( i=0; i < NH_TABLE_ENTRIES; i++) { lock_init(&n_table->entries[i].mutex); n_table->entries[i].next_via_label = rand(); n_table->entries[i].first = n_table->entries[i].last = 0; } lock_init(&n_table->timer_list.mutex); return n_table; error: return 0; } void free_hash_table(void) { int i; for (i=0; i < NH_TABLE_ENTRIES; i++) lock_destroy(&n_table->entries[i].mutex); lock_destroy(&n_table->timer_list.mutex); shm_free(n_table); } struct nh_table* get_htable(void) { return n_table; } struct ping_cell *build_p_cell(int hash_id, udomain_t* d, uint64_t contact_id) { struct ping_cell *cell; cell = shm_malloc(sizeof(struct ping_cell)); if (0 == cell) { LM_ERR("no more memory\n"); return 0; } memset(cell, 0, sizeof(struct ping_cell)); cell->hash_id = hash_id; cell->timestamp = now; cell->d = d; cell->contact_id = contact_id; return cell; } void insert_into_hash( struct ping_cell* p_cell) { struct nh_entry* entry; struct ping_cell* cell; entry = &n_table->entries[p_cell->hash_id]; cell = entry->first; if (!cell) { entry->first = entry->last = p_cell; return; } p_cell->next = cell; cell->prev = p_cell; entry->first = p_cell; } struct ping_cell *get_cell(int hash_id, uint64_t contact_id) { struct nh_entry *entry; struct ping_cell* cell=NULL; entry = &n_table->entries[hash_id]; cell = entry->first; for (cell=entry->first; cell; cell = cell->next) { if (cell->contact_id == contact_id) return cell; } return NULL; } /* * needs to be called under lock * only removes cell from hash * also frees the cell */ void remove_given_cell(struct ping_cell *cell, struct nh_entry *entry) { if (cell == entry->first && cell == entry->last) { entry->first = entry->last = 0; } else if (cell == entry->first) { entry->first = cell->next; cell->next->prev = 0; } else if (cell == entry->last) { entry->last = cell->prev; cell->prev->next = 0; } else { cell->prev->next = cell->next; cell->next->prev = cell->prev; } } opensips-2.2.2/modules/nathelper/nh_table.h000066400000000000000000000044631300170765700207510ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History * ------- * 2015-02-18 initial version (Ionut Ionita) */ #ifndef _NH_TABLE_H #define _NH_TABLE_H #include #include #include "../../parser/msg_parser.h" #include "../../locking.h" #include "../usrloc/udomain.h" /* size of hash table */ #define NH_TABLE_ENTRIES (1<<16) /* current time */ #define now (time(NULL)) struct ping_cell { int hash_id; udomain_t* d; uint64_t contact_id; unsigned int timestamp; /* !< timestamp when ping was sent */ char not_responded; /* !< number of pings not responded to */ /* hash table links */ struct ping_cell* next; struct ping_cell* prev; /* timer list link */ struct ping_cell* tnext; }; struct nh_entry { struct ping_cell* first; struct ping_cell* last; unsigned int next_via_label; /* !< label to make the via unique */ gen_lock_t mutex; }; /* timer list */ typedef struct nh_tlist { struct ping_cell *first; struct ping_cell *last; gen_lock_t mutex; } nh_tlist; struct nh_table { /* the queue and its mutex */ nh_tlist timer_list; /* the hash table */ struct nh_entry entries[NH_TABLE_ENTRIES]; }; void lock_hash(int i); void unlock_hash(int i); struct nh_table* init_hash_table(void); struct nh_table* get_htable(void); void free_hash_table(); struct ping_cell *build_p_cell(int hash_id, udomain_t* d, uint64_t contact_id); void insert_into_hash( struct ping_cell* p_cell); struct ping_cell *get_cell(int hash_id, uint64_t contact_id); void remove_given_cell(struct ping_cell *cell, struct nh_entry *entry); #endif opensips-2.2.2/modules/nathelper/sip_pinger.h000066400000000000000000000231031300170765700213240ustar00rootroot00000000000000/* * Copyright (C) 2005 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2005-07-11 created (bogdan) */ #ifndef NATHELPER_OPTIONS_H_ #define NATHELPER_OPTIONS_H_ #include #include #include "../../parser/parse_rr.h" #include "../../str.h" #include "../../ut.h" #include "../../ip_addr.h" #include "nh_table.h" /* size of buffer used for building SIP PING req */ #define MAX_SIPPING_SIZE 65536 /* maximum number of hops */ #define MAX_FORWARD "70" /* branch magic */ #define BMAGIC "z9hG4bK" #define BMAGIC_LEN (sizeof(BMAGIC) - 1) #define BSTART ";branch=" #define LIST_END_CELL ((struct ping_cell*)-1) /* this cell is the end of the list */ #define FREE_CELL NULL /* this cell is not in the timer list */ /* helping macros for building SIP PING ping request */ #define append_str( _p, _s) \ do {\ memcpy(_p,(_s).s,(_s).len);\ _p += (_s).len;\ }while(0) #define append_fix( _p, _s) \ do {\ memcpy(_p, _s, sizeof(_s)-1);\ _p += sizeof(_s)-1;\ }while(0) /* info used to generate SIP ping requests */ static int sipping_fromtag = 0; static char sipping_callid_buf[8]; static int sipping_callid_cnt = 0; static str sipping_callid = {0,0}; static str sipping_from = {0,0}; static str sipping_method = {"OPTIONS",7}; static int remove_on_timeout=0; static void init_sip_ping(int rto) { int len; char *p; /* FROM tag - some random number */ sipping_fromtag = rand(); /* callid fix part - hexa string */ len = 8; p = sipping_callid_buf; int2reverse_hex( &p, &len, rand() ); sipping_callid.s = sipping_callid_buf; sipping_callid.len = 8-len; /* callid counter part */ sipping_callid_cnt = rand(); remove_on_timeout=(rto>0?1:0); } static int parse_branch(str branch) { int hash_id, cid_len; char *end; int64_t ret; uint64_t contact_id=0; struct ping_cell *p_cell; if (branch.len < BMAGIC_LEN || memcmp(branch.s, BMAGIC, BMAGIC_LEN)) { LM_ERR("invalid branch\n"); return -1; } branch.s += BMAGIC_LEN; branch.len -= BMAGIC_LEN; end = q_memchr(branch.s, '.', branch.len); if (0 == end) return 1; hash_id = reverse_hex2int(branch.s, end-branch.s); branch.len -= (end-branch.s + 1); branch.s = end+1; if (0 == end) return 1; end = q_memchr(branch.s, '.', branch.len); cid_len = end-branch.s; ret = reverse_hex2int64(branch.s, cid_len, 1/* request unsafe parsing */); /* we don't parse the label since we don't need it */ if (ret == -1) { LM_ERR("received invalid contact id\n"); return -1; } contact_id = (uint64_t)ret; lock_hash(hash_id); if ((p_cell=get_cell(hash_id, contact_id))==NULL) { LM_WARN("received ping response for a removed contact" " with contact id %llu\n", (long long unsigned int)contact_id); unlock_hash(hash_id); return 0; } /* when we receive answer to a ping we consider all pings sent * confirmed, because what we want to know is that the contact * is alive; only remove the cell from the hash; will be * completely removed when the timer will be up */ p_cell->not_responded = 0; /* mark for removal */ p_cell->timestamp = 0; remove_given_cell(p_cell, &get_htable()->entries[p_cell->hash_id]); unlock_hash(hash_id); return 0; } static int sipping_rpl_filter(struct sip_msg *rpl) { struct cseq_body* cseq_b; /* first check number of vias -> must be only one */ if (parse_headers( rpl, HDR_VIA2_F, 0 )==-1 || (rpl->via2!=0)) goto skip; /* check the method -> we need CSeq header */ if ( (!rpl->cseq && parse_headers(rpl,HDR_CSEQ_F,0)!=0) || rpl->cseq==0 ) { LM_ERR("failed to parse CSeq\n"); goto error; } cseq_b = (struct cseq_body*)rpl->cseq->parsed; if (cseq_b->method.len!=sipping_method.len || strncmp(cseq_b->method.s,sipping_method.s,sipping_method.len)!=0) goto skip; /* check constant part of callid */ if ( (!rpl->callid && parse_headers(rpl,HDR_CALLID_F,0)!=0) || rpl->callid==0 ) { LM_ERR("failed to parse Call-ID\n"); goto error; } if ( rpl->callid->body.len<=sipping_callid.len+1 || strncmp(rpl->callid->body.s,sipping_callid.s,sipping_callid.len)!=0 || rpl->callid->body.s[sipping_callid.len]!='-') goto skip; LM_DBG("reply for SIP natping filtered\n"); /* it's a reply to a SIP NAT ping -> absorb it and stop any * further processing of it */ if (remove_on_timeout && parse_branch(rpl->via1->branch->value)) goto skip; return 0; skip: return 1; error: return -1; } /* */ static inline int build_branch(char *branch, int *size, str *curi, udomain_t *d, uint64_t contact_id, int rm_on_to) { int hash_id, ret, label; time_t timestamp; struct ping_cell *p_cell; struct nh_table *htable; /* we want all contact pings from a contact in one bucket*/ hash_id = core_hash(curi, 0, 0) & (NH_TABLE_ENTRIES-1); if (rm_on_to) { /* get the time before the lock - we may wait a little bit * on this lock */ timestamp=now; lock_hash(hash_id); if ((p_cell=get_cell(hash_id, contact_id))==NULL) { if (0 == (p_cell = build_p_cell(hash_id, d, contact_id))) { unlock_hash(hash_id); goto out_memfault; } insert_into_hash(p_cell); } p_cell->timestamp = timestamp; unlock_hash(hash_id); htable = get_htable(); /* put the cell in timer list */ lock_get(&htable->timer_list.mutex); if (p_cell->tnext == FREE_CELL) { if (!htable->timer_list.first) { htable->timer_list.first = htable->timer_list.last = p_cell; } else { htable->timer_list.last->tnext = p_cell; htable->timer_list.last = p_cell; } /* this cell will be the end of the list */ p_cell->tnext = LIST_END_CELL; } /* we get the label that assures us that the via is unique */ label = htable->entries[hash_id].next_via_label++; lock_release(&htable->timer_list.mutex); } else { label = sipping_callid_cnt; } memcpy( branch, BMAGIC, BMAGIC_LEN); branch += BMAGIC_LEN; ret=int2reverse_hex(&branch, size, hash_id); if (ret < 0) goto out_nospace; *branch = '.'; branch++; ret=int64_2reverse_hex(&branch, size, contact_id); if (ret < 0) goto out_nospace; *branch = '.'; branch++; ret=int2reverse_hex(&branch, size, label); if (ret < 0) goto out_nospace; *branch = '\0'; return 0; out_memfault: LM_ERR("no more shared memory\n"); return -1; out_nospace: LM_ERR("not enough space in send buffer\n"); return -1; } /* build the buffer of a SIP ping request */ static inline char* build_sipping(udomain_t *d, str *curi, struct socket_info* s,str *path, int *len_p, uint64_t contact_id, int rm_on_to) { #define s_len(_s) (sizeof(_s)-1) static char buf[MAX_SIPPING_SIZE]; char *p, proto_str[PROTO_NAME_MAX_SIZE]; str address, port; str st; int len; int bsize = 100; str sbranch; char branch[100]; char *bbuild = branch; memcpy(bbuild, BSTART, sizeof(BSTART) - 1); bbuild += sizeof(BSTART) - 1; bsize -= (bbuild - branch); build_branch( bbuild, &bsize, curi, d, contact_id, rm_on_to); sbranch.s = branch; sbranch.len = strlen(branch); p = proto2str(s->proto, proto_str); *(p++) = ' '; st.s = proto_str; st.len = p - proto_str; if (s->adv_name_str.len) address = s->adv_name_str; else if (default_global_address.len) address = default_global_address; else address = s->address_str; if (s->adv_port_str.len) port = s->adv_port_str; else if (default_global_port.len) port = default_global_port; else port = s->port_no_str; /* quick proto uppercase */ *((int *)st.s) &= ~((1 << 21) | (1 << 13) | (1 << 5)); if ( sipping_method.len + 1 + curi->len + s_len(" SIP/2.0"CRLF) + s_len("Via: SIP/2.0/") + st.len + address.len + 1 + port.len + strlen(branch) + (path->len ? (s_len(CRLF"Route: ") + path->len) : 0) + s_len(CRLF"From: ") + sipping_from.len + s_len(";tag=") + 8 + s_len(CRLF"To: ") + curi->len + s_len(CRLF"Call-ID: ") + sipping_callid.len + 1 + 8 + 1 + 8 + 1 + address.len + s_len(CRLF"CSeq: 1 ") + sipping_method.len + s_len(CRLF"Max-Forwards: "MAX_FORWARD) + s_len(CRLF"Content-Length: 0" CRLF CRLF) > MAX_SIPPING_SIZE ) { LM_ERR("len exceeds %d\n",MAX_SIPPING_SIZE); return 0; } p = buf; append_str( p, sipping_method); *(p++) = ' '; append_str( p, *curi); append_fix( p, " SIP/2.0"CRLF"Via: SIP/2.0/"); append_str( p, st); append_str( p, address); *(p++) = ':'; append_str( p, port); append_str( p, sbranch); if (path->len) { append_fix( p, CRLF"Route: "); append_str( p, *path); } append_fix( p, CRLF"From: "); append_str( p, sipping_from); append_fix( p, ";tag="); len = 8; int2reverse_hex( &p, &len, sipping_fromtag++ ); append_fix( p, CRLF"To: "); append_str( p, *curi); append_fix( p, CRLF"Call-ID: "); append_str( p, sipping_callid); *(p++) = '-'; len = 8; int2reverse_hex( &p, &len, sipping_callid_cnt++ ); *(p++) = '-'; len = 8; int2reverse_hex( &p, &len, get_ticks() ); *(p++) = '@'; append_str( p, address); append_fix( p, CRLF"CSeq: 1 "); append_str( p, sipping_method); append_fix( p, CRLF"Max-Forwards: "MAX_FORWARD); append_fix( p, CRLF"Content-Length: 0" CRLF CRLF); *len_p = p - buf; return buf; } #endif opensips-2.2.2/modules/options/000077500000000000000000000000001300170765700165265ustar00rootroot00000000000000opensips-2.2.2/modules/options/Makefile000066400000000000000000000003221300170765700201630ustar00rootroot00000000000000# $Id$ # # opt_rpl module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=options.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/options/README000066400000000000000000000107601300170765700174120ustar00rootroot00000000000000Options Module Nils Ohlmeier FhG Fokus Edited by Nils Ohlmeier Copyright © 2003 FhG Fokus Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. accept (string) 1.3.2. accept_encoding (string) 1.3.3. accept_language (string) 1.3.4. support (string) 1.4. Exported Functions 1.4.1. options_reply() List of Examples 1.1. Set accept parameter 1.2. Set accept_encoding parameter 1.3. Set accept_language parameter 1.4. Set support parameter 1.5. options_reply usage Chapter 1. Admin Guide 1.1. Overview This module provides a function to answer OPTIONS requests which are directed to the server itself. This means an OPTIONS request which has the address of the server in the request URI, and no username in the URI. The request will be answered with a 200 OK which the capabilities of the server. To answer OPTIONS request directed to your server is the easiest way for is-alive-tests on the SIP (application) layer from remote (similar to ICMP echo requests, also known as “pingâ€, on the network layer). 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * sl -- Stateless replies. * signaling -- Stateless replies. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. accept (string) This parameter is the content of the Accept header field. If “â€, the header is not added in the reply. Note: it is not clearly written in RFC3261 if a proxy should accept any content (the default “*/*â€) because it does not care about content. Or if it does not accept any content, which is “â€. Default value is “*/*â€. Example 1.1. Set accept parameter ... modparam("options", "accept", "application/*") ... 1.3.2. accept_encoding (string) This parameter is the content of the Accept-Encoding header field. If “â€, the header is not added in the reply. Please do not change the default value because OpenSIPS does not support any encodings yet. Default value is “â€. Example 1.2. Set accept_encoding parameter ... modparam("options", "accept_encoding", "gzip") ... 1.3.3. accept_language (string) This parameter is the content of the Accept-Language header field. If “â€, the header is not added in the reply. You can set any language code which you prefer for error descriptions from other devices, but presumably there are not much devices around which support other languages then the default English. Default value is “enâ€. Example 1.3. Set accept_language parameter ... modparam("options", "accept_language", "de") ... 1.3.4. support (string) This parameter is the content of the Support header field. If “â€, the header is not added in the reply. Please do not change the default value, because OpenSIPS currently does not support any of the SIP extensions registered at the IANA. Default value is “â€. Example 1.4. Set support parameter ... modparam("options", "support", "100rel") ... 1.4. Exported Functions 1.4.1. options_reply() This function checks if the request method is OPTIONS and if the request URI does not contain an username. If both is true the request will be answered stateless with “200 OK†and the capabilities from the modules parameters. It sends “500 Server Internal Error†for some errors and returns false if it is called for a wrong request. The check for the request method and the missing username is optional because it is also done by the function itself. But you should not call this function outside the myself check because in this case the function could answer OPTIONS requests which are sent to you as outbound proxy but with an other destination then your proxy (this check is currently missing in the function). This function can be used from REQUEST_ROUTE. Example 1.5. options_reply usage ... if (uri==myself) { if ((method==OPTIONS) && (! uri=~"sip:.*[@]+.*")) { options_reply(); } } ... opensips-2.2.2/modules/options/doc/000077500000000000000000000000001300170765700172735ustar00rootroot00000000000000opensips-2.2.2/modules/options/doc/options.xml000066400000000000000000000022121300170765700215050ustar00rootroot00000000000000 %docentities; ]> Options Module &osipsname; Nils Ohlmeier FhG Fokus
nils@iptel.org
Nils Ohlmeier
nils@iptel.org
2003 FhG Fokus $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/options/doc/options_admin.xml000066400000000000000000000126601300170765700226650ustar00rootroot00000000000000 &adminguide;
Overview This module provides a function to answer OPTIONS requests which are directed to the server itself. This means an OPTIONS request which has the address of the server in the request URI, and no username in the URI. The request will be answered with a 200 OK which the capabilities of the server. To answer OPTIONS request directed to your server is the easiest way for is-alive-tests on the SIP (application) layer from remote (similar to ICMP echo requests, also known as ping, on the network layer).
Dependencies
&osips; Modules The following modules must be loaded before this module: sl -- Stateless replies. signaling -- Stateless replies.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>accept</varname> (string) This parameter is the content of the Accept header field. If , the header is not added in the reply. Note: it is not clearly written in RFC3261 if a proxy should accept any content (the default */*) because it does not care about content. Or if it does not accept any content, which is . Default value is */*. Set <varname>accept</varname> parameter ... modparam("options", "accept", "application/*") ...
<varname>accept_encoding</varname> (string) This parameter is the content of the Accept-Encoding header field. If , the header is not added in the reply. Please do not change the default value because &osips; does not support any encodings yet. Default value is . Set <varname>accept_encoding</varname> parameter ... modparam("options", "accept_encoding", "gzip") ...
<varname>accept_language</varname> (string) This parameter is the content of the Accept-Language header field. If , the header is not added in the reply. You can set any language code which you prefer for error descriptions from other devices, but presumably there are not much devices around which support other languages then the default English. Default value is en. Set <varname>accept_language</varname> parameter ... modparam("options", "accept_language", "de") ...
<varname>support</varname> (string) This parameter is the content of the Support header field. If , the header is not added in the reply. Please do not change the default value, because &osips; currently does not support any of the SIP extensions registered at the IANA. Default value is . Set <varname>support</varname> parameter ... modparam("options", "support", "100rel") ...
Exported Functions
<function moreinfo="none">options_reply()</function> This function checks if the request method is OPTIONS and if the request URI does not contain an username. If both is true the request will be answered stateless with 200 OK and the capabilities from the modules parameters. It sends 500 Server Internal Error for some errors and returns false if it is called for a wrong request. The check for the request method and the missing username is optional because it is also done by the function itself. But you should not call this function outside the myself check because in this case the function could answer OPTIONS requests which are sent to you as outbound proxy but with an other destination then your proxy (this check is currently missing in the function). This function can be used from REQUEST_ROUTE. <function>options_reply</function> usage ... if (uri==myself) { if ((method==OPTIONS) && (! uri=~"sip:.*[@]+.*")) { options_reply(); } } ...
opensips-2.2.2/modules/options/mod_options.c000066400000000000000000000144441300170765700212330ustar00rootroot00000000000000/* * Options Reply Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) */ #ifdef EXTRA_DEBUG #include /* required by abort() */ #endif #include "../../sr_module.h" #include "mod_options.h" #include "../../str.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../data_lump_rpl.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../signaling/signaling.h" static str options_reply_hdrs; static str acpt_s = str_init(ACPT_DEF); static str acpt_enc_s = str_init(ACPT_ENC_DEF); static str acpt_lan_s = str_init(ACPT_LAN_DEF); static str supt_s = str_init(SUPT_DEF); /** SIGNALING binds */ struct sig_binds sigb; static str opt_200_rpl = str_init("OK"); static str opt_500_rpl = str_init("Server internal error"); static int mod_init(void); static int opt_reply(struct sip_msg* _msg, char* _foo, char* _bar); /* * Exported functions */ static cmd_export_t cmds[] = { {"options_reply", (cmd_function)opt_reply, 0, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"accept", STR_PARAM, &acpt_s.s}, {"accept_encoding", STR_PARAM, &acpt_enc_s.s}, {"accept_language", STR_PARAM, &acpt_lan_s.s}, {"support", STR_PARAM, &supt_s.s}, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* * Module description */ struct module_exports exports = { "options", /* Module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ NULL, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* Initialization function */ 0, /* Response function */ 0, /* Destroy function */ 0 /* Child init function */ }; /* * initialize module */ static int mod_init(void) { int len, offset; LM_INFO("initializing...\n"); /* load SIGNALING API */ if(load_sig_api(&sigb)< 0) { LM_ERR("can't load signaling functions\n"); return -1; } acpt_s.len = strlen(acpt_s.s); acpt_enc_s.len = strlen(acpt_enc_s.s); acpt_lan_s.len = strlen(acpt_lan_s.s); supt_s.len = strlen(supt_s.s); len = (acpt_s.len>0 ? ACPT_STR_LEN+acpt_s.len+HF_SEP_STR_LEN : 0)+ (acpt_enc_s.len>0 ? ACPT_ENC_STR_LEN+acpt_enc_s.len+HF_SEP_STR_LEN : 0)+ (acpt_lan_s.len>0 ? ACPT_LAN_STR_LEN+acpt_lan_s.len+HF_SEP_STR_LEN : 0)+ (supt_s.len>0 ? SUPT_STR_LEN+supt_s.len+HF_SEP_STR_LEN : 0); options_reply_hdrs.s = pkg_malloc(len); if (!options_reply_hdrs.s) { LM_ERR("No more pkg memory\n"); return -1; } offset = 0; /* create the header fields */ if (acpt_s.len > 0) { memcpy(options_reply_hdrs.s, ACPT_STR, ACPT_STR_LEN); offset = ACPT_STR_LEN; memcpy(options_reply_hdrs.s + offset, acpt_s.s, acpt_s.len); offset += acpt_s.len; memcpy(options_reply_hdrs.s + offset, HF_SEP_STR, HF_SEP_STR_LEN); offset += HF_SEP_STR_LEN; } if (acpt_enc_s.len > 0) { memcpy(options_reply_hdrs.s + offset, ACPT_ENC_STR, ACPT_ENC_STR_LEN); offset += ACPT_ENC_STR_LEN; memcpy(options_reply_hdrs.s + offset, acpt_enc_s.s, acpt_enc_s.len); offset += acpt_enc_s.len; memcpy(options_reply_hdrs.s + offset, HF_SEP_STR, HF_SEP_STR_LEN); offset += HF_SEP_STR_LEN; } if (acpt_lan_s.len > 0) { memcpy(options_reply_hdrs.s + offset, ACPT_LAN_STR, ACPT_LAN_STR_LEN); offset += ACPT_LAN_STR_LEN; memcpy(options_reply_hdrs.s + offset, acpt_lan_s.s, acpt_lan_s.len); offset += acpt_lan_s.len; memcpy(options_reply_hdrs.s + offset, HF_SEP_STR, HF_SEP_STR_LEN); offset += HF_SEP_STR_LEN; } if (supt_s.len > 0) { memcpy(options_reply_hdrs.s + offset, SUPT_STR, SUPT_STR_LEN); offset += SUPT_STR_LEN; memcpy(options_reply_hdrs.s + offset, supt_s.s, supt_s.len); offset += supt_s.len; memcpy(options_reply_hdrs.s + offset, HF_SEP_STR, HF_SEP_STR_LEN); offset += HF_SEP_STR_LEN; } #ifdef EXTRA_DEBUG if (offset != len) { LM_CRIT("headerlength (%i) != offset (%i)\n", len, offset); abort(); } #endif options_reply_hdrs.len = len; return 0; } static int opt_reply(struct sip_msg* _msg, char* _foo, char* _bar) { /* check if it is called for an OPTIONS request */ if (_msg->REQ_METHOD!=METHOD_OPTIONS) { LM_ERR("called for non-OPTIONS request\n"); return -1; } if(_msg->parsed_uri_ok==0 && parse_sip_msg_uri(_msg)<0) { LM_ERR("ERROR while parsing the R-URI\n"); return -1; } /* FIXME: should we additionally check if ruri == server addresses ?! */ if (_msg->parsed_uri.user.len != 0) { LM_ERR("ruri contains username\n"); return -1; } if (!options_reply_hdrs.s || options_reply_hdrs.len < 0) { LM_CRIT("headers not yet initialized\n"); goto error; } if (add_lump_rpl( _msg, options_reply_hdrs.s, options_reply_hdrs.len, LUMP_RPL_HDR|LUMP_RPL_NODUP|LUMP_RPL_NOFREE)!=0) { if (sigb.reply(_msg, 200, &opt_200_rpl, NULL) == -1) { LM_ERR("failed to send 200 via send_reply\n"); return -1; } else return 0; } LM_ERR("add_lump_rpl failed\n"); error: if (sigb.reply(_msg, 500, &opt_500_rpl, NULL) == -1) { LM_ERR("failed to send 500 via send_reply\n"); return -1; } else return 0; } opensips-2.2.2/modules/options/mod_options.h000066400000000000000000000026131300170765700212330ustar00rootroot00000000000000/* * Options Reply Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef OPT_RPL_H #define OPT_RPL_H #define ACPT_STR "Accept: " #define ACPT_STR_LEN 8 #define ACPT_ENC_STR "Accept-Encoding: " #define ACPT_ENC_STR_LEN 17 #define ACPT_LAN_STR "Accept-Language: " #define ACPT_LAN_STR_LEN 17 #define SUPT_STR "Supported: " #define SUPT_STR_LEN 11 #define HF_SEP_STR "\r\n" #define HF_SEP_STR_LEN 2 /* * I think RFC3261 is not precise if a proxy should accept any * or no body (because it is not the endpoint of the media) */ #define ACPT_DEF "*/*" #define ACPT_ENC_DEF "" #define ACPT_LAN_DEF "en" #define SUPT_DEF "" #endif /* OPT_RPL_H */ opensips-2.2.2/modules/osp/000077500000000000000000000000001300170765700156345ustar00rootroot00000000000000opensips-2.2.2/modules/osp/Makefile000066400000000000000000000016621300170765700173010ustar00rootroot00000000000000 # osp module makefile # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=osp.so ifeq ($(CROSS_COMPILE),) SSL_BUILDER=$(shell \ if pkg-config --exists libssl; then \ echo 'pkg-config libssl'; \ fi) endif ifneq ($(SSL_BUILDER),) DEFS += $(shell $(SSL_BUILDER) --cflags) LIBS += $(shell $(SSL_BUILDER) --libs) else DEFS += -I$(LOCALBASE)/ssl/include \ -I$(LOCALBASE)/include LIBS += -L$(LOCALBASE)/lib -L$(LOCALBASE)/ssl/lib \ -L$(LOCALBASE)/lib64 -L$(LOCALBASE)/ssl/lib64 \ -lssl -lcrypto endif DEFS+=-D_POSIX_THREADS -I$(LOCALBASE)/include LIBS+=-L$(LOCALBASE)/lib -losptk -lpthread -lm include ../../Makefile.modules install_module_custom: echo "OSP module overwrites the default configuration file" sed \ -e "s#/usr/local/lib/opensips#$(modules_prefix)/$(lib_dir)#g" \ < etc/sample-osp-opensips.cfg \ > $(cfg_prefix)/$(cfg_dir)/opensips.cfg opensips-2.2.2/modules/osp/README000066400000000000000000001001061300170765700165120ustar00rootroot00000000000000OSP Module for Secure, Multi-Lateral Peering Ulrich Abend FhG FOKUS Edited by Di-Shi Sun TransNexus, Inc. Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.3. Exported Parameters 1.3.1. work_mode 1.3.2. service_type 1.3.3. sp1_uri, sp2_uri, ..., sp16_uri 1.3.4. sp1_weight, sp2_weight, ..., sp16_weight 1.3.5. device_ip 1.3.6. use_security_features 1.3.7. token_format 1.3.8. private_key, local_certificate, ca_certificates 1.3.9. enable_crypto_hardware_support 1.3.10. ssl_lifetime 1.3.11. persistence 1.3.12. retry_delay 1.3.13. retry_limit 1.3.14. timeout 1.3.15. support_nonsip_protocol 1.3.16. max_destinations 1.3.17. report_networkid 1.3.18. validate_call_id 1.3.19. use_number_portability 1.3.20. append_userphone 1.3.21. networkid_location 1.3.22. networkid_parameter 1.3.23. switchid_location 1.3.24. switchid_parameter 1.3.25. parameterstring_location 1.3.26. parameterstring_value 1.3.27. source_device_avp 1.3.28. source_networkid_avp 1.3.29. source_switchid_avp 1.3.30. custom_info_avp 1.3.31. cnam_avp 1.3.32. extraheaders_value 1.3.33. source_media_avp, destination_media_avp 1.3.34. request_date_avp 1.3.35. sdp_fingerprint_avp 1.3.36. identity_signature_avp, identity_algorithm_avp, identity_information_avp, identity_type_avp, identity_canon_avp 1.4. Exported Functions 1.4.1. checkospheader() 1.4.2. validateospheader() 1.4.3. getlocaladdress() 1.4.4. setrequestdate() 1.4.5. requestosprouting() 1.4.6. checkosproute() 1.4.7. prepareosproute() 1.4.8. prepareospresponse() 1.4.9. prepareallosproutes() 1.4.10. checkcallingtranslation() 1.4.11. reportospusage() 1.4.12. processsubscribe() 2. Developer Guide List of Examples 1.1. Instructing the module to work in direct mode 1.2. Instructing the module to provide normal voice service 1.3. Setting the OSP servers 1.4. Setting the OSP server weights 1.5. Setting the device IP address 1.6. Instructing the module not to use OSP security features 1.7. Setting the token format 1.8. Set authorization files 1.9. Setting the hardware support 1.10. Setting the ssl lifetime 1.11. Setting the persistence 1.12. Setting the retry delay 1.13. Setting the retry limit 1.14. Setting the timeout 1.15. Setting support non-SIP destination devices 1.16. Setting the number of destination 1.17. Setting report network ID flag 1.18. Instructing the module to validate call id 1.19. Instructing the module to use number portability parameters in Request URI 1.20. Append user=phone parameter 1.21. Append networkid location 1.22. Networkid parameter name 1.23. Append switchid location 1.24. Networkid parameter name 1.25. Append parameter string location 1.26. Parameter string value 1.27. Setting the source device IP AVP 1.28. Setting the source network ID AVP 1.29. Setting the source switch ID AVP 1.30. Setting the custom info AVP 1.31. Setting the CNAM AVP 1.32. Setting the NOTIFY extra headers 1.33. Setting the media address AVPs 1.34. Setting the request date AVP 1.35. Setting the SDP finger print AVP 1.36. Setting the Identity related AVPs 1.37. checkospheader usage 1.38. validateospheader usage 1.39. getlocaladress usage 1.40. setrequestdate usage 1.41. requestosprouting usage 1.42. checkosproute usage 1.43. prepareosproute usage 1.44. prepareospresponse usage 1.45. prepareallosproutes usage 1.46. checkcallingtranslation usage 1.47. reportospusage usage 1.48. processsubscribe usage Chapter 1. Admin Guide 1.1. Overview The OSP module enables OpenSIPS to support secure, multi-lateral peering using the OSP standard defined by ETSI (TS 101 321 V4.1.1). This module will enable your OpenSIPS to: * Send a peering authorization request to a peering server. * Validate a digitally signed peering authorization token received in a SIP INVITE message. * Report usage information to a peering server. 1.2. Dependencies The OSP module depends on the following modules which must be loaded before the OSP module. * auth -- Authentication Framework module * avpops -- AVP operation module * maxfwd -- Max-Forward processor module * mi_fifo -- FIFO support for Management Interface * options -- OPTIONS server replier module * proto_udp -- UDP protocol module - implements UDP-plain transport for SIP * registrar -- SIP Registrar implementation module * rr -- Record-Route and Route module * signaling -- SIP signaling module * sipmsgops -- SIP operations module * sl -- Stateless replier module * tm -- Transaction (stateful) module * uac -- UAC functionalies (FROM mangling and UAC auth) * uac_auth -- UAC Authentication functionality * usrloc -- User location implementation module * OSP Toolkit -- The OSP Toolkit, available from http://sourceforge.net/projects/osp-toolkit, must be built before building OpenSIPS with the OSP module. For instructions on building OpenSIPS with the OSP Toolkit, see http://www.http://transnexus.com/wp-content/uploads/OSP-Rou ting-and-CDR-Collection-Server-with-OpenSIPS-1.7.2.pdf. For OpenSIPS 2.1.0, OSP Toolkit 4.5.0 or later versions should be used. 1.3. Exported Parameters 1.3.1. work_mode The work_mode (integer) parameter instructs the OSP module what mode it should work in. If this value is set to 0, the OSP module works in direct mode. If this value is set to 1, the OSP module works in indirect mode. The default value is 0. Example 1.1. Instructing the module to work in direct mode modparam("osp","work_mode",0) 1.3.2. service_type The service_type (integer) parameter instructs the OSP module what services it should provide. If this value is set to 0, the OSP module provides normal voice service. If this value is set to 1, the OSP module provides ported number query service. If this value is set to 2, the OSP module provides CNAM query service. The default value is 0. Example 1.2. Instructing the module to provide normal voice service modparam("osp","service_type",0) 1.3.3. sp1_uri, sp2_uri, ..., sp16_uri These sp_uri (string) parameters define peering servers to be used for requesting peering authorization and routing information. At least one peering server must be configured. Others are required only if there are more than one peering servers. Each peering server address takes the form of a standard URL, and consists of up to four components: * An optional indication of the protocol to be used for communicating with the peering server. Both HTTP and HTTP secured with SSL/TLS are supported and are indicated by "http://" and "https://" respectively. If the protocol is not explicitly indicated, the OpenSIPS defaults to HTTP secured with SSL. * The Internet domain name for the peering server. An IP address may also be used, provided it is enclosed in square brackets such as [172.16.1.1]. * An optional TCP port number for communicating with the peering server. If the port number is omitted, the OpenSIPS defaults to port 5045 (for HTTP) or port 1443 (for HTTP secured with SSL). The uniform resource identifier for requests to the peering server. This component is not optional and must be included. Example 1.3. Setting the OSP servers modparam("osp","sp1_uri","http://osptestserver.transnexus.com:5045/osp") modparam("osp","sp2_uri","https://[1.2.3.4]:1443/osp") 1.3.4. sp1_weight, sp2_weight, ..., sp16_weight These sp_weight (integer) parameters are used for load balancing peering requests to peering servers. These parameters are most effective when configured as factors of 1000. For example, if sp1_uri should manage twice the traffic load of sp2_uri, then set sp1_weight to 2000 and sp2_weight to 1000. Shared load balancing between peering servers is recommended. However, peering servers can be configured as primary and backup by assigning a sp_weight of 0 to the primary server and a non-zero sp_weight to the back-up server. The default values for sp1_weight and sp2_weight are 1000. Example 1.4. Setting the OSP server weights modparam("osp","sp1_weight",1000) 1.3.5. device_ip The device_ip (string) is a recommended parameter that explicitly defines the IP address of OpenSIPS in a peering request message (as SourceAlternate type=transport). The dotted-decimal IP address must be in brackets as shown in the example below. Example 1.5. Setting the device IP address modparam("osp","device_ip","[127.0.0.1]:5060") 1.3.6. use_security_features The use_security_features (integer) parameter instructs the OSP module how to use the OSP security features. If this value is set to 1, the OSP module uses the OSP security features. If this value is set to 0, the OSP module will not use the OSP security features. The default value is 0. Example 1.6. Instructing the module not to use OSP security features modparam("osp","use_security_features",0) 1.3.7. token_format When OpenSIPS receives a SIP INVITE with a peering token, the OSP module will validate the token to determine whether or not the call has been authorized by a peering server. Peering tokens may, or may not, be digitally signed. The token_format (integer) parameter defines if OpenSIPS will validate signed or unsigned tokens or both. The values for token format are defined below. The default value is 2. If use_security_features parameter is set to 0, signed tokens cannot be validated. 0 - Validate only signed tokens. Calls with valid signed tokens are allowed. 1 - Validate only unsigned tokens. Calls with valid unsigned tokens are allowed. 2 - Validate both signed and unsigned tokens are allowed. Calls with valid tokens are allowed. Example 1.7. Setting the token format modparam("osp","token_format",2) 1.3.8. private_key, local_certificate, ca_certificates These parameters identify files are used for validating peering authorization tokens and establishing a secure channel between OpenSIPS and a peering server using SSL. The files are generated using the 'Enroll' utility from the OSP Toolkit. By default, the proxy will look for pkey.pem, localcert.pem, and cacart_0.pem in the default configuration directory. The default config directory is set at compile time using CFG_DIR and defaults to /usr/local/etc/opensips/. The files may be copied to the expected file location or the parameters below may be changed. If use_security_features parameter is set to 0, these parameters will be ignored. Example 1.8. Set authorization files If the default CFG_DIR value was used at compile time, the files will be loaded from: modparam("osp","private_key","/usr/local/etc/opensips/pkey.pem") modparam("osp","local_certificate","/usr/local/etc/opensips/localcert.pe m") modparam("osp","ca_certificates","/usr/local/etc/opensips/cacert.pem") 1.3.9. enable_crypto_hardware_support The enable_crypto_hardware_support (integer) parameter is used to set the cryptographic hardware acceleration engine in the openssl library. The default value is 0 (no crypto hardware is present). If crypto hardware is used, the value should be set to 1. Example 1.9. Setting the hardware support modparam("osp","enable_crypto_hardware_support",0) 1.3.10. ssl_lifetime The ssl_lifetime (integer) parameter defines the lifetime, in seconds, of a single SSL session key. Once this time limit is exceeded, the OSP module will negotiate a new session key. Communication exchanges in progress will not be interrupted when this time limit expires. This is an optional field with default value is 200 seconds. Example 1.10. Setting the ssl lifetime modparam("osp","ssl_lifetime",200) 1.3.11. persistence The persistence (integer) parameter defines the time, in seconds, that an HTTP connection should be maintained after the completion of a communication exchange. The OSP module will maintain the connection for this time period in anticipation of future communication exchanges to the same peering server. Example 1.11. Setting the persistence modparam("osp","persistence",1000) 1.3.12. retry_delay The retry_delay (integer) parameter defines the time, in seconds, between retrying connection attempts to an OSP peering server. After exhausting all peering servers the OSP module will delay for this amount of time before resuming connection attempts. This is an optional field with default value is 1 second. Example 1.12. Setting the retry delay modparam("osp","retry_delay",1) 1.3.13. retry_limit The retry_limit (integer) parameter defines the maximum number of retries for connection attempts to a peering server. If no connection is established after this many retry attempts to all peering servers, the OSP module will cease connection attempts and return appropriate error codes. This number does not count the initial connection attempt, so that a retry_limit of 1 will result in a total of two connection attempts to every peering server. The default value is 2. Example 1.13. Setting the retry limit modparam("osp","retry_limit",2) 1.3.14. timeout The timeout (integer) parameter defines the maximum time in milliseconds, to wait for a response from a peering server. If no response is received within this time, the current connection is aborted and the OSP module attempts to contact the next peering server. The default value is 10 seconds. Example 1.14. Setting the timeout modparam("osp","timeout",10) 1.3.15. support_nonsip_protocol The support_nonsip_protocol (integer) parameter is used to tell the OSP module if non-SIP signaling protocol destination devices are supported. The default value is 0. Example 1.15. Setting support non-SIP destination devices modparam("osp","support_nonsip_protocol",0) 1.3.16. max_destinations The max_destinations (integer) parameter defines the maximum number of destinations that OpenSIPS requests the peering server to return in a peering response. The OSP module supports up to 12 destinations. The default value is 12. Example 1.16. Setting the number of destination modparam("osp","max_destinations",12) 1.3.17. report_networkid The report_networkid (integer) parameter is used to tell the OSP module if to report network ID in completed call CDRs. If it is set to 0, ths OSP module does not report any network ID. If it is set to 1, the OSP module reports source network ID. If it is set to 2, the OSP module reports destination network ID. If it is set to 3, the OSP module report both source and destination network IDs. The default value is 3. Example 1.17. Setting report network ID flag modparam("osp","report_networkid",3) 1.3.18. validate_call_id The validate_call_id (integer) parameter instructs the OSP module to validate call id in the peering token. If this value is set to 1, the OSP module validates that the call id in the SIP INVITE message matches the call id in the peering token. If they do not match the INVITE is rejected. If this value is set to 0, the OSP module will not validate the call id in the peering token. The default value is 1. Example 1.18. Instructing the module to validate call id modparam("osp","validate_call_id",1) 1.3.19. use_number_portability The use_number_portability (integer) parameter instructs the OSP module how to use the number portability parameters in the Request URI of the SIP INVITE message. If this value is set to 1, the OSP module uses the number portability parameters in the Request URI when these parameters exist. If this value is set to 0, the OSP module will not use the number portability parameters. The default value is 1. Example 1.19. Instructing the module to use number portability parameters in Request URI modparam("osp","use_number_portablity",1) 1.3.20. append_userphone The append_userphone (integer) parameter instructs the OSP module if to append "user=phone" parameter in URI. If this value is set to 0, the OSP module does not append "user=phone" parameter. If this value is set to 1, the OSP module will append "user=phone" parameter. The default value is 0 Example 1.20. Append user=phone parameter modparam("osp","append_userphone",0) 1.3.21. networkid_location The networkid_location (integer) parameter instructs the OSP module where the destination network ID should be appended. The default value is 2 0 - network ID is not appended. 1 - network ID is appended as userinfo parameter. 2 - network ID is appended as URI parameter. Example 1.21. Append networkid location modparam("osp","networkid_location",2) 1.3.22. networkid_parameter The networkid_parameter (string) parameter instructs the OSP module to use which parameter name in outbound destination URIs to append destination network ID. The default value is "networkid" Example 1.22. Networkid parameter name modparam("osp","networkid_param","networkid") 1.3.23. switchid_location The switchid_location (integer) parameter instructs the OSP module where the destination switch ID should be appended. The default value is 2 0 - switch ID is not appended. 1 - switch ID is appended as userinfo parameter. 2 - switch ID is appended as URI parameter. Example 1.23. Append switchid location modparam("osp","switchid_location",2) 1.3.24. switchid_parameter The switchid_parameter (string) parameter instructs the OSP module to use which parameter name in outbound destination URIs to append destination switch ID. The default value is "switchid" Example 1.24. Networkid parameter name modparam("osp","switchid_param","switchid") 1.3.25. parameterstring_location The parameterstring_location (integer) parameter instructs the OSP module where the parameter string should be appended. The default value is 0 0 - parameter string is not appended. 1 - parameter string is appended as userinfo parameter. 2 - parameter string is appended as URI parameter. Example 1.25. Append parameter string location modparam("osp","parameterstring_location",0) 1.3.26. parameterstring_value The parameterstring_value (string) parameter instructs the OSP module to append the parameter string in outbound URIs. The default value is "" Example 1.26. Parameter string value modparam("osp","parameterstring_value","") 1.3.27. source_device_avp The source_device_avp (string) parameter instructs the OSP module to use the defined AVP to pass the source device IP value in the indirect work mode. The default value is "$avp(_osp_source_device_)". Then the source device IP can be set by "$avp(_osp_source_device_) = pseudo-variables". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Example 1.27. Setting the source device IP AVP modparam("osp","source_device_avp","$avp(srcdev)") 1.3.28. source_networkid_avp The source_networkid_avp (string) parameter instructs the OSP module to use the defined AVP to pass the source network ID value. The default value is "$avp(_osp_source_networkid_)". Then the source network ID can be set by "$avp(_osp_source_networkid_) = pseudo-variables". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Example 1.28. Setting the source network ID AVP modparam("osp","source_networkid_avp","$avp(snid)") 1.3.29. source_switchid_avp The source_switchid_avp (string) parameter instructs the OSP module to use the defined AVP to pass the source switch ID value. The default value is "$avp(_osp_source_switchid_)". Then the source switch ID can be set by "$avp(_osp_source_switchid_) = pseudo-variables". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Example 1.29. Setting the source switch ID AVP modparam("osp","source_switchid_avp","$avp(swid)") 1.3.30. custom_info_avp The custom_info_avp (string) parameter instructs the OSP module to use the defined AVP to pass the custom information values. The default value is "$avp(_osp_custom_info_)". Then the custom information can be set by "$avp(_osp_custom_info_) = pseudo-variables". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Example 1.30. Setting the custom info AVP modparam("osp","custom_info_avp","$avp(cinfo)") 1.3.31. cnam_avp The cnam_avp (string) parameter instructs the OSP module to use the defined AVP to pass the CNAM values. The default value is "$avp(_osp_cnam_)". Then the CNAM can be used by "$avp(_osp_cnam_)". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Example 1.31. Setting the CNAM AVP modparam("osp","cnam_avp","$avp(cnam)") 1.3.32. extraheaders_value The extraheaders_value (string) parameter instructs the OSP module to append the defined SIP headers in outbound SIP NOTIFY messages. The default value is empty. Example 1.32. Setting the NOTIFY extra headers modparam("osp", "extraheaders_value", "Source: N") 1.3.33. source_media_avp, destination_media_avp These parameters are used to tell the OSP module which AVPs are used to store media addresses. The default values are "$avp(_osp_source_media_address_)" and "$avp(_osp_destination_media_address_)". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Example 1.33. Setting the media address AVPs modparam("osp", "source_media_avp", "$avp(srcmedia)") modparam("osp", "destination_media_avp", "$avp(destmedia)") 1.3.34. request_date_avp The request_date_avp (string) parameter instructs the OSP module to use the defined AVP to pass the SIP request Date header values. The default value is "$avp(_osp_request_date_)". Then the request date can be used by "$avp(_osp_request_date_)". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Example 1.34. Setting the request date AVP modparam("osp","request_date_avp","$avp(reqdate)") 1.3.35. sdp_fingerprint_avp The sdp_fingerprint_avp (string) parameter instructs the OSP module to use the defined AVP to pass the SDP fing print attribute values. The default value is "$avp(_osp_sdp_fingerprint_)". Then the SDP finger print attributes can be used by "$avp(_osp_sdp_fingerprint_)". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Example 1.35. Setting the SDP finger print AVP modparam("osp","sdp_fingerprint_avp","$avp(sdpfp)") 1.3.36. identity_signature_avp, identity_algorithm_avp, identity_information_avp, identity_type_avp, identity_canon_avp These parameters instruct the OSP module to use the defined AVPs to pass the Identity related values. The default values are "$avp(_osp_identity_signature_)", "$avp(_osp_identity_algorithm_)", "$avp(_osp_identity_information_)", "$avp(_osp_identity_type_)", "$avp(_osp_identity_canon_)". Then the indentity related values can be used by these AVPs. All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Example 1.36. Setting the Identity related AVPs modparam("osp","identity_signature_avp","$avp(idsign)") modparam("osp","identity_algorithm_avp","$avp(idalg)") modparam("osp","identity_information_avp","$avp(idinfo)") modparam("osp","identity_type_avp","$avp(idtype)") modparam("osp","identity_canon_avp","$avp(idcanon)") 1.4. Exported Functions 1.4.1. checkospheader() This function checks for the existence of the OSP-Auth-Token header field. This function can be used from REQUEST_ROUTE. Example 1.37. checkospheader usage ... if (checkospheader()) { log(1,"OSP header field found.\n"); } else { log(1,"no OSP header field present\n"); }; ... 1.4.2. validateospheader() This function validates an OSP-Token specified in the OSP-Auth-Tokenheader field of the SIP message. If a peering token is present, it will be validated locally. If no OSP header is found or the header token is invalid or expired, -1 is returned; on successful validation 1 is returned. This function can be used from REQUEST_ROUTE. Example 1.38. validateospheader usage ... if (validateospheader()) { log(1,"valid OSP header found\n"); } else { log(1,"OSP header not found, invalid or expired\n"); }; ... 1.4.3. getlocaladdress() This function gets the receiving IP address of SIP response and stores it as proxy egress address. This function can be used from ONREPLY_ROUTE. Example 1.39. getlocaladress usage ... if (getlocaladdress()) { log(1,"Obtain proxy local egress address\n"); } else { log(1,"Failed to get proxy local egress address\n"); }; ... 1.4.4. setrequestdate() This function gets the receiving IP address of SIP response and stores it as proxy egress address. This function can be used from REQUEST_ROUTE. Example 1.40. setrequestdate usage ... if (setrequest()) { log(1,"Set request date\n"); } else { log(1,"Failed to set request date\n"); }; ... 1.4.5. requestosprouting() This function launches a query to the peering server requesting the IP address of one or more destination peers serving the called party. If destination peers are available, the peering server will return the IP address and a peering authorization token for each destination peer. The OSP-Auth-Token Header field is inserted into the SIP message and the SIP uri is rewritten to the IP address of destination peer provided by the peering server. The address of the called party must be a valid E164 number, otherwise this function returns -1. If the transaction was accepted by the peering server, the uri is being rewritten and 1 returned, on errors (peering servers are not available, authentication failed or there is no route to destination or the route is blocked) -1 is returned. This function can be used from REQUEST_ROUTE. Example 1.41. requestosprouting usage ... if (requestosprouting()) { log(1,"successfully queried OSP server, now relaying call\n"); } else { log(1,"Authorization request was rejected from OSP server\n"); }; ... 1.4.6. checkosproute() This function is used to check if there is any route for the call. This function can be used from REQUEST_ROUTE. Example 1.42. checkosproute usage ... if (checkosproute()) { log(1,"There is at least one route for the call\n"); } else { log(1,"There is not any route for the call\n"); }; ... 1.4.7. prepareosproute() This function tries to prepare the INVITE to be forwarded using the destination in the list returned by the peering server. If the calling number is translated, a RPID value for the RPID AVP will be set. If the route could not be prepared, the function returns 'FALSE' back to the script, which can then decide how to handle the failure. Note, if checkosproute has been called and returns 'TRUE' before calling prepareosproute, prepareosproute should not return 'FALSE' because checkosproute has confirmed that there is at least one route. This function can be used from BRANCH_ROUTE. Example 1.43. prepareosproute usage ... if (prepareosproute()) { log(1,"successfully prepared the route, now relaying call\n"); } else { log(1,"could not prepare the route, there is not route\n"); }; ... 1.4.8. prepareospresponse() This function tries to prepare all the routes in the list returned by the peering server into SIP 300 Redirect or SIP 380 Alternative Service message. The message is then replied to the source. If unsuccessful in preparing the routes a SIP 500 is sent back and a trace message is logged. This function can be used from REQUEST_ROUTE. Example 1.44. prepareospresponse usage ... if (prepareospresponse()) { log(1,"Response is prepared.\n"); } else { log(1,"Could not prepare the response.\n"); }; ... 1.4.9. prepareallosproutes() This function tries to prepare all the routes in the list returned by the peering server. The message is then forked off to the destinations. If unsuccessful in preparing the routes a SIP 500 is sent back and a trace message is logged. This function can be used from REQUEST_ROUTE. Example 1.45. prepareallosproutes usage ... if (prepareallosproutes()) { log(1,"Routes are prepared, now forking the call\n"); } else { log(1,"Could not prepare the routes. No destination available\n"); }; ... 1.4.10. checkcallingtranslation() This function is used to check if the calling number is translated. Before calling checkcallingtranslation, prepareosproute should be called. If the calling number does been translated, the original Remote-Party-ID, if it exists, should be removed from the INVITE message. And a new Remote-Party-ID header should be added (a RPID value for the RPID AVP has been set by prepareosproute). If the calling number is not translated, nothing should be done. This function can be used from BRANCH_ROUTE. Example 1.46. checkcallingtranslation usage ... if (checkcallingtranslation()) { # Remove the Remote_Party-ID from the received message # Otherwise it will be forwarded on to the next hop remove_hf("Remote-Party-ID"); # Append a new Remote_Party append_rpid_hf(); } ... 1.4.11. reportospusage() This function should be called after receiving a BYE message. If the message contains an OSP cookie, the function will forward originating and/or terminating duration usage information to a peering server. The function returns TRUE if the BYE includes an OSP cookie. The actual usage message will be send on a different thread and will not delay BYE processing. The function should be called before relaying the message. Meaning of the parameter is as follows: * "0" - Source device releases the call. * "1" - Destination device releases the call. This function can be used from REQUEST_ROUTE. Example 1.47. reportospusage usage ... if (is_direction("downstream")) { log(1,"This BYE message is from SOURCE\n"); if (!reportospusage("0")) { log(1,"This BYE message does not include OSP usage information\n"); } } else { log(1,"This BYE message is from DESTINATION\n"); if (!reportospusage("1")) { log(1,"This BYE message does not include OSP usage information\n"); } } ... 1.4.12. processsubscribe() This function should be called after receiving a SUBSCRIBE for CNAM message and there is a cached CNAM record for this message. This function generates a NOTIFY message including the cached CNAM record, then sends the NOTIFY message to the device sending the SUBSCRIBE message. Meaning of the parameter is as follows: * cachedcnamrecord - Cached CNAM record. This function can be used from REQUEST_ROUTE. Example 1.48. processsubscribe usage ... if (method == "SUBSCRIBE") { if (($var(sevent) == "calling-name") && (uri == myself)) { if ($var(cnamrecord) != NULL) { processsubscribe("$(var(cnamrecord){s.b64decode})"); } else { t_relay("1.2.3.4", "0x02"); } } else { t_relay(); } } ... Chapter 2. Developer Guide The functions of the OSP modules are not used by other OpenSIPS modules. opensips-2.2.2/modules/osp/RELEASE-NOTES.txt000066400000000000000000000150621300170765700203470ustar00rootroot00000000000000 2005 July 23 Implemented routing and authorization validation. The module can: o Request authorization and routing information from an OSP server o Use provided routes to: o Redirect the originating UI to all routes OR o Try all routes simultaneously using parallel forking OR o Try routes in the order they were received using sequential forking o Add and retrieve P-OSP-Auth-Token header o Perform local validation of the P-OSP-Auth-Token 2005 September 12 o Bug 4863 - the module will remove OSP Auth token from the incoming Invite after validating the token and before forwarding Invite to the next hop. o Bug 4891 - added a new flag 'validate_call_id' for disabling call id validation in OSP tokens. The value is optional, call id validation can be turned off by setting the value to 0 (No). It may be used for interoperability issues with some OSP clients sending invalid call ids in Authorization Requests. o Bug 4986 - changed return value for 'prepareallosproutes' from 0 - stop routing logic to 1 - true o Bug 5039 - changed return value for 'preparenextosproute' when there is no more routes from 0 - stop routing logic to -1 - false o Bug 5039 - changed return value for 'preparenextosproute' when there is o Bug 4893 - sequential routing (in failure_route[1]) now checks for code 487 (Canceled) before trying the next OSP route. o Bug 4892 - removed trailing binary chars from some log messages. o Bug 4987 - fixed a compile time error on Solaris o Bug 4946 - added README file 2005 September 26 o Bug 5094 - don't route the call if OSP token validation fails. o Bug 5109 - send "100 Trying" message before requesting OSP routes. o Bug 5111 - fixed typos in error messages. o Bug 5153 - removed trailing binary chars from P-OSP-Auth-Token header value. 2005 October 3 o Report OSP usage indications after the call set-up and tear down transactions complete. 2005 October 11 o Report call set-up usage indication after receiving either 200 or 202. o Report termination cause code for duration usage indications as 10,016 instead of 1,016. o Improved error checking and logging. o Bug 5366 - removed a memory leak in usage reporting logic. 2005 November 4 o Bug 5110 and 5531 - Updated the sample configuration files to not fail-over on 486 - user busy and 408 - user not available. o Bug 5535 - The module will report call set-up usage details after sending any final response code to UAC. 2005 November 7 o Copied files from cvs.berlios.de:/cvsroot/osp-module 2006 August 3 o Using BRAND_ROUTE approach to add route specific header to avoid sending an OSP token to a non-OSP destination. For bug 1524046 on OpenSER tracker. o Using called number in Request-Line for token validation and auth request. For bug 1524070 on OpenSER tracker. o Using RPID approach for calling number translation. For bug 1524079 on OpenSER tracker. o Sending DeviceInfo in "[x.x.x.x]" format in OSP AuthReq messages. For bug 1524085 on OpenSER tracker. o Using fromtag in RR to report who releases the call first. For bug 1527940 on OpenSER tracker. 2007 April 20 o Report destination count in source-stop usage indications, requires osptoolkit version 3.4.0 2009 March 4 o Updated for OSP Toolkit 3.5.0 2009 March 30 o Fixed the parsing called number issue caused by the number portability parameters in user part of RURI. 2009 April 7 o Fixed the issue caused by without certificate files. 2009 August 28 o Added user=phone parameter support. o Added number portability parameters support. o Added Diversion header support. 2009 October 06 o Fixed un-initialized variable issue. 2009 October 07 o Updated OSP sample configuration file. 2009 November 18 o Fixed a running error message by adding null string check. 2009 November 19 o Added IP port support for inbound/outbound IP addresses. 2009 November 24 o Removed an unused variable. 2009 December 01 o Added custom info support. 2009 December 03 o Replaced two deprecated functions of OSP Toolkit. o Removed useless white char. 2009 December 14 o Added buffer size check. o Added proxy_type for number portability. 2010 January 18 o Changed configuration option proxy_type to service_type. o Added operator name support in AuthReq and AuthRsp. 2010 March 05 o Added destination network ID in outbound URI. 2010 May 13 o Fixed bug of removing '+' in numbers. 2010 May 24 o Added indirect work mode. o Changed append network ID default value. o Updated sample configuration file. o Updated doc. 2010 May 26 11:35:25 +0800 (Wed, 26 May 2010) | 2 lines o Added DeviceInfo= in AuthReq if there is not P-Source-Device in indirect work mode. 2010 July 07 o Changed OSP TCP port from 1080 to 5045. 2010 August 06 o Removed loading xlog module from sample configuration. 2010 November 29 o Added support to append destination network ID by different ways. 2011 January 17 o Added unsupport char check in From/To user part. o Fixed number portability parameter parsing issue caused by user=phone. 2011 February 10 o Fixed NULL string issue on Solaris. o Added install_module_custom in Makefile. 2011 October 12 Increased max number of destinations. Sync with OSP Toolkit 4.0.0. Removed From/To header XML escape char check. Changed structure element names. Changed calling number implementation. Added support for network ID as Contact header parameter. Added reporting protocol. Fixed the last device is not a SIP device issue. Added role info types. Added support for non-SIP signaling protocol. Added support for called number digit filtering. Added support for calling number invalid. Fixed network ID reporting issue. Fixed t_relay check issue in sample configuration file. 2015 June 4 Updated to support more OSP server response codes. Fixed the bug of source network ID in OSP cookie. Updated to support 603 Decline status code. Different timeout values for receiving 18x and not receiving 18x. Hardcoded not to report redirect CDRs. Support CDRProxy Support FromDisplayName Support User-Agent Support TotalSetupAttempts for completed calls. 2015 September 1 Updated to support media addresses, local address and provider post dial delay. Updated to support CNAM. 2015 September 15 Updated to support CNAM diversion use case. 2016 February 26 Updated to support STIR. 2016 September 13 Added SUBSCRIBE/NOTIFY CNAM support. Formatted sample configuration file. Updated for OSP Toolkit 4.12.0 opensips-2.2.2/modules/osp/cnam.c000066400000000000000000000254571300170765700167330ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2016-06-01 CNAM related functions. */ #include "../../mod_fix.h" #include "../../parser/parse_from.h" #include "../../parser/parse_event.h" #include "../../parser/parse_expires.h" #include "../../parser/contact/parse_contact.h" #include "../../data_lump_rpl.h" #include "../signaling/signaling.h" #include "../tm/tm_load.h" #include "../presence/utils_func.h" #include "osp_mod.h" #include "cnam.h" #include "signaling.h" #include "tm.h" extern char* _osp_extraheaders_value; /* * Parse SUBSCRIBE * param msg SIP message * return 0 success, others failure */ static int ospParseSubscribe( struct sip_msg* msg) { int ret = -1; if (msg == NULL) { LM_ERR("wrong message\n"); } else if (parse_headers(msg,HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse message\n"); } else if (parse_sip_msg_uri(msg) < 0) { LM_ERR("failed to parse ruri\n"); } else if (parse_from_uri(msg) < 0) { LM_ERR("failed to parse from\n"); } else if (parse_to_uri(msg) < 0) { LM_ERR("failed to parse to\n"); } else if ((msg->contact == NULL) || (msg->contact->body.len <= 0) || (parse_contact(msg->contact) < 0)) { LM_ERR("failed to parse contact\n"); } else if ((msg->event == NULL) || (msg->event->body.len <= 0) || (parse_event(msg->event) < 0)) { LM_ERR("failed to parse event\n"); } else if ((msg->expires != NULL) && (msg->expires->body.len > 0) && (parse_expires(msg->expires) < 0)) { LM_ERR("failed to parse expires\n"); } else { ret = 0; } return ret; } /* * Send 200 OK reply * param msg SIP message * param expire Expires value * param tag To tag * return 0 success, others failure */ static int ospReply200(struct sip_msg* msg, int expire, str* contact, str* tag) { int length; char buffer[OSP_STRBUF_SIZE]; char headers[OSP_HEADERBUF_SIZE]; str ok = { "OK", 2 }; int ret = -1; buffer[0] = '\0'; if (msg->rcv.proto != PROTO_UDP) { memcpy(buffer, ";transport=", 11); if (proto2str(msg->rcv.proto, buffer + 11) == 0) { buffer[0] = '\0'; } } length = snprintf(headers, sizeof(headers), "Expires: %ud\r\nContact: <%.*s%s>\r\n", expire, contact->len, contact->s, buffer); if (add_lump_rpl(msg, headers, length, LUMP_RPL_HDR) == 0) { LM_ERR("failed to add lump_rl\n"); } else if (osp_sigb.reply(msg, 200, &ok, tag)< 0) { LM_ERR("failed to send reply\n"); } else { ret = 0; } return ret; } /* * Create dialog structure * param msg SIP message * return dialog */ static dlg_t* ospCreateDialog( struct sip_msg* msg, str* tag) { struct to_body* from; struct to_body* to; contact_body_t* contact; str record_route = { 0, 0 }; dlg_t* dialog = NULL; if ((dialog = (dlg_t*)pkg_malloc(sizeof(dlg_t))) != NULL) { /* Retrieve message info */ from = msg->from->parsed; to = msg->to->parsed; contact = msg->contact->parsed; /* Init dialog structure */ memset(dialog, 0, sizeof(dlg_t)); if (uandd_to_uri(from->parsed_uri.user, from->parsed_uri.host, &dialog->rem_uri) < 0) { LM_ERR("failed to construct from uri\n"); pkg_free(dialog); dialog = NULL; } else if (uandd_to_uri(to->parsed_uri.user, to->parsed_uri.host, &dialog->loc_uri) < 0) { LM_ERR("failed to construct to uri\n"); pkg_free(dialog->rem_uri.s); pkg_free(dialog); dialog = NULL; } else { /* Set From */ dialog->id.rem_tag = from->tag_value; /* Set To*/ if ((to->tag_value.s == NULL) || (to->tag_value.len == 0)) { dialog->id.loc_tag = *tag; } else { dialog->id.loc_tag = to->tag_value; } /* Set display names */ if(osp_tmb.dlg_add_extra(dialog, &(from->display), &(to->display)) < 0) { LM_WARN("failed to add display names\n"); } /* Set Call-ID */ dialog->id.call_id = msg->callid->body; /* Set Contact */ if ((contact->contacts->uri.len == 0) || (contact->contacts->uri.s == NULL)) { dialog->rem_target = dialog->rem_uri; } else { dialog->rem_target = contact->contacts->uri; } /* Set CSeq */ dialog->loc_seq.value = 0; dialog->loc_seq.is_set = 1; /* Set Route */ if (msg->record_route != NULL) { if (print_rr_body(msg->record_route, &record_route, 0, 0) == 0) { if (parse_rr_body(record_route.s, record_route.len, &dialog->route_set) < 0) { LM_ERR("failed to parse record route\n"); } } } /* Set dialog status */ dialog->state= DLG_CONFIRMED ; /* Set send address */ dialog->send_sock = msg->rcv.bind_address; } } else { LM_ERR("failed to allocate memory\n"); } return dialog; } /* * Free dialog structure * param dialog Dialog structure */ static void ospFreeDialog( dlg_t* dialog) { if (dialog != NULL) { if (dialog->loc_uri.s != NULL) { pkg_free(dialog->loc_uri.s); } if (dialog->rem_uri.s != NULL) { pkg_free(dialog->rem_uri.s); } pkg_free(dialog); } } /* * Generate NOTIFY additional headers * param msg SIP message * param expire Expires value * param contact Contact * param headers Extra headers * return 0 success, others failure */ static int ospExtraHeaders( struct sip_msg* msg, int expire, str* contact, str* headers) { int length; param_t* param; event_t* event; str eventid = { NULL, 0 }; char probuf[OSP_STRBUF_SIZE]; char statebuf[OSP_STRBUF_SIZE]; int ret = -1; event = msg->event->parsed; param = event->params; while (param) { if ((param->name.len == 2) && (strncasecmp(param->name.s, "id", 2) == 0)) { eventid = param->body; break; } param= param->next; } probuf[0] = '\0'; if (msg->rcv.proto != PROTO_UDP) { memcpy(probuf, ";transport=", 11); if (proto2str(msg->rcv.proto, probuf+ 11) == 0) { probuf[0] = '\0'; } } if (expire > 0) { snprintf(statebuf, sizeof(statebuf), "active;expires=%ud", expire); } else { snprintf(statebuf, sizeof(statebuf), "terminated;reason=timeout"); } if ((headers->s != NULL) && (headers->len > 0)) { length = snprintf(headers->s, headers->len, "Expires: %ud\r\nEvent: %.*s;id=%.*s\r\nContact: <%.*s%s>\r\nSubscription-State: %s\r\n%s\r\nContent-Type: %s\r\n", expire, event->text.len, event->text.s, eventid.len, eventid.s, contact->len, contact->s, probuf, statebuf, _osp_extraheaders_value, "application/calling-name-info"); headers->len = length; ret = 0; } else { LM_WARN("wrong parameter\n"); } return ret; } /* * Callback function for NOTIFY * param t * param type * param ps */ static void ospNotifyCallback( struct cell* t, int type, struct tmcb_params *ps) { LM_DBG("NOTIFY finished with code '%d'\n", ps->code); return; } /* * Process SUBSCRIBE * param msg SIP message * param cnamrecord Cached CNAM record * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospProcessSubscribe( struct sip_msg* msg, char* cnamrecord, char* ignore2) { str record; int expire; str contact = { NULL, 0 }; str tag = { NULL, 0 }; str notify = { "NOTIFY", 6 }; dlg_t *dialog; char buffer[OSP_HEADERBUF_SIZE]; str headers = { buffer, sizeof(buffer) }; int result = MODULE_RETURNCODE_FALSE; if ((cnamrecord != NULL) && (fixup_get_svalue(msg, (gparam_p)cnamrecord, &record) == 0) && (record.len > 0)) { LM_DBG("cnam record '%.*s'\n", record.len, record.s); /* Parse SUBSCRIBE */ if (ospParseSubscribe(msg) == 0) { expire = 0; if (msg->expires != NULL) { expire = ((exp_body_t*)msg->expires->parsed)->val; } if (get_local_contact(msg->rcv.bind_address, &contact) < 0) { LM_WARN("failed to get contact\n"); } /* Send 200 OK reply */ if (ospReply200(msg, expire, &contact, &tag) == 0) { /* Create dialog */ if ((dialog = ospCreateDialog(msg, &tag)) != NULL) { /* Generate extra headers */ if (ospExtraHeaders(msg, expire, &contact, &headers) == 0) { /* Send NOTIFY */ if (osp_tmb.t_request_within(¬ify, &headers, &record, dialog, ospNotifyCallback, NULL, NULL) < 0) { LM_ERR("failed to send notify\n"); } else { result = MODULE_RETURNCODE_TRUE; } } else { LM_ERR("failed to generate extre headers\n"); } } else { LM_ERR("failed to create dialog\n"); } ospFreeDialog(dialog); } else { LM_ERR("failed to send ok\n"); } } else { LM_ERR("failed to parse subscribe\n"); } } else { LM_ERR("invalid CNAM record\n"); } return result; } opensips-2.2.2/modules/osp/cnam.h000066400000000000000000000026371300170765700167330ustar00rootroot00000000000000 /* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_CNAMFUNC_H_ #define _OSP_MOD_CNAMFUNC_H_ #include "../../parser/msg_parser.h" int ospProcessSubscribe(struct sip_msg* msg, char* cnamrecord, char* ignore2); #endif /* _OSP_MOD_CNAMFUNC_H_ */ opensips-2.2.2/modules/osp/destination.c000066400000000000000000000415571300170765700203350ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../usr_avp.h" #include "destination.h" #include "usage.h" extern int _osp_inbound_avpid; extern int _osp_origdest_avpid; extern int _osp_termdest_avpid; extern int _osp_calling_avpid; extern int _osp_destmedia_avpid; extern unsigned short _osp_destmedia_avptype; /* Name of AVP of OSP */ static str OSP_INBOUND_NAME = {"_osp_inbound_", 13}; static str OSP_ORIGDEST_NAME = {"_osp_orig_dests_", 16}; static str OSP_TERMDEST_NAME = {"_osp_term_dests_", 16}; static str OSP_CALLING_NAME = {"_osp_calling_translated_", 24}; static int ospSaveDestination(osp_dest* dest, int avpid); static void ospRecordCode(int code, osp_dest* dest); static int ospIsToReportUsage(int code); /* * Parses avps used by this module * return 0 success, -1 failure */ int ospParseAvps(void) { if (parse_avp_spec(&OSP_INBOUND_NAME, &_osp_inbound_avpid)) { LM_ERR("cannot get INBOUND AVP id\n"); return -1; } if (parse_avp_spec(&OSP_ORIGDEST_NAME, &_osp_origdest_avpid)) { LM_ERR("cannot get ORIGDEST AVP id\n"); return -1; } if (parse_avp_spec(&OSP_TERMDEST_NAME, &_osp_termdest_avpid)) { LM_ERR("cannot get TERMDEST AVP id\n"); return -1; } if (parse_avp_spec(&OSP_CALLING_NAME, &_osp_calling_avpid)) { LM_ERR("cannot get CALLING AVP id\n"); return -1; } return 0; } /* * Initialize inbound info structure * param inbound Inbound info data structure */ void ospInitInboundInfo( osp_inbound* inbound) { memset(inbound, 0, sizeof(osp_inbound)); } /* * Save inbound info as an AVP * avpid - osp_inbound_avpid * value - osp_inbound wrapped in a string * param inbound Inbound info structure * return 0 success, -1 failure */ int ospSaveInboundInfo( osp_inbound* inbound) { str wrapper; int result = -1; wrapper.s = (char*)inbound; wrapper.len = sizeof(osp_inbound); /* * add_avp will make a private copy of both the avpid and value in shared * memory which will be released by TM at the end of the transaction */ if (add_avp(AVP_VAL_STR, _osp_inbound_avpid, (int_str)wrapper) == 0) { LM_DBG("inbound info saved\n"); result = 0; } else { LM_ERR("failed to save inbound info\n"); } return result; } /* * Retrieved the inbound info from an AVP * avpid - osp_inbound_avpid * value - osp_inbound wrapped in a string * return NULL on failure */ osp_inbound* ospGetInboundInfo(void) { int_str inboundval; osp_inbound* inbound = NULL; if (search_first_avp(AVP_VAL_STR, _osp_inbound_avpid, &inboundval, 0) != NULL) { /* OSP inbound info is wrapped in a string */ inbound = (osp_inbound*)inboundval.s.s; LM_DBG("inbound info found\n"); } return inbound; } /* * Initialize destination structure * param dest Destination data structure * return initialized destination sturcture */ osp_dest* ospInitDestination( osp_dest* dest) { memset(dest, 0, sizeof(osp_dest)); dest->callidsize = sizeof(dest->callid); dest->tokensize = sizeof(dest->token); LM_DBG("callidsize '%d' tokensize '%d'\n", dest->callidsize, dest->tokensize); return dest; } /* * Save destination as an AVP * avpid - osp_origdest_avpid / osp_termdest_avpid * value - osp_dest wrapped in a string * param dest Destination structure * param avpid ID of AVP * return 0 success, -1 failure */ static int ospSaveDestination( osp_dest* dest, int avpid) { str wrapper; int result = -1; wrapper.s = (char*)dest; wrapper.len = sizeof(osp_dest); /* * add_avp will make a private copy of both the avpid and value in shared * memory which will be released by TM at the end of the transaction */ if (add_avp(AVP_VAL_STR, avpid, (int_str)wrapper) == 0) { LM_DBG("destination saved\n"); result = 0; } else { LM_ERR("failed to save destination\n"); } return result; } /* * Save originate destination * param dest Originate destination structure * return 0 success, -1 failure */ int ospSaveOrigDestination( osp_dest* dest) { return ospSaveDestination(dest, _osp_origdest_avpid); } /* * Save terminate destination * param dest Terminate destination structure * return 0 success, -1 failure */ int ospSaveTermDestination( osp_dest* dest) { return ospSaveDestination(dest, _osp_termdest_avpid); } /* * Check if there is an unused and supported originate destination from an AVP * avpid - osp_origdest_avpid * value - osp_dest wrapped in a string * search unused (used==0) & supported (support==1) * return 0 success, -1 failure */ int ospCheckOrigDestination(void) { struct usr_avp* destavp = NULL; int_str destval; osp_dest* dest = NULL; int result = -1; for (destavp = search_first_avp(AVP_VAL_STR, _osp_origdest_avpid, NULL, 0); destavp != NULL; destavp = search_next_avp(destavp, NULL)) { get_avp_val(destavp, &destval); /* OSP destintaion is wrapped in a string */ dest = (osp_dest*)destval.s.s; if (dest->used == 0) { if (dest->supported == 1) { LM_DBG("orig dest exist\n"); result = 0; break; } else { /* Make it looks like used */ dest->used = 1; /* 111 means wrong protocol */ dest->lastcode = 111; LM_DBG("destination does not been supported\n"); } } else { LM_DBG("destination has already been used\n"); } } if (result == -1) { LM_DBG("there is not unused destination\n"); ospReportOrigSetupUsage(); } return result; } /* * Retrieved an unused and supported originate destination from an AVP * avpid - osp_origdest_avpid * value - osp_dest wrapped in a string * There can be 0, 1 or more originate destinations. * Find the 1st unused destination (used==0) & supported (support==1), * return it, and mark it as used (used==1). * return NULL on failure */ osp_dest* ospGetNextOrigDestination(void) { struct usr_avp* destavp = NULL; int_str destval; osp_dest* dest = NULL; osp_dest* result = NULL; for (destavp = search_first_avp(AVP_VAL_STR, _osp_origdest_avpid, NULL, 0); destavp != NULL; destavp = search_next_avp(destavp, NULL)) { get_avp_val(destavp, &destval); /* OSP destintaion is wrapped in a string */ dest = (osp_dest*)destval.s.s; if (dest->used == 0) { if (dest->supported == 1) { LM_DBG("orig dest found\n"); dest->used = 1; result = dest; break; } else { /* Make it looks like used */ dest->used = 1; /* 111 means wrong protocol */ dest->lastcode = 111; LM_DBG("destination does not been supported\n"); } } else { LM_DBG("destination has already been used\n"); } } if (result == NULL) { LM_DBG("there is not unused destination\n"); } return result; } /* * Retrieved the last used originate destination from an AVP * avpid - osp_origdest_avpid * value - osp_dest wrapped in a string * There can be 0, 1 or more destinations. * Find the last used destination (used==1) & supported (support==1), * and return it. * In normal condition, this one is the current destination. But it may * be wrong for loop condition. * return NULL on failure */ osp_dest* ospGetLastOrigDestination(void) { struct usr_avp* destavp = NULL; int_str destval; osp_dest* dest = NULL; osp_dest* lastdest = NULL; for (destavp = search_first_avp(AVP_VAL_STR, _osp_origdest_avpid, NULL, 0); destavp != NULL; destavp = search_next_avp(destavp, NULL)) { get_avp_val(destavp, &destval); /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; if (dest->used == 1) { if (dest->supported == 1) { lastdest = dest; LM_DBG("curent destination '%s'\n", lastdest->host); } } else { break; } } return lastdest; } /* * Retrieved the terminate destination from an AVP * avpid - osp_termdest_avpid * value - osp_dest wrapped in a string * There can be 0 or 1 term destinations. Find and return it. * return NULL on failure (no terminate destination) */ osp_dest* ospGetTermDestination(void) { int_str destval; osp_dest* dest = NULL; if (search_first_avp(AVP_VAL_STR, _osp_termdest_avpid, &destval, 0) != NULL) { /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; LM_DBG("term dest found\n"); } return dest; } /* * Record destination status * param code Destination status * param dest Destination */ static void ospRecordCode( int code, osp_dest* dest) { struct usr_avp* destmediaavp = NULL; int_str destmediaval; LM_DBG("code '%d'\n", code); dest->lastcode = code; switch (code) { case 100: if (!dest->time100) { dest->time100 = time(NULL); } else { LM_DBG("100 already recorded\n"); } break; case 180: case 181: case 182: case 183: if (!dest->time180) { dest->time180 = time(NULL); if (!dest->endtime) { dest->endtime = time(NULL); } else { LM_DBG("180, 181, 182 or 183 end allready recorded\n"); } } else { LM_DBG("180, 181, 182 or 183 allready recorded\n"); } break; case 200: case 202: if (!dest->time200) { dest->time200 = time(NULL); if ((_osp_destmedia_avpid >= 0) && ((destmediaavp = search_first_avp(_osp_destmedia_avptype, _osp_destmedia_avpid, &destmediaval, 0)) != NULL) && (destmediaavp->flags & AVP_VAL_STR) && (destmediaval.s.s && destmediaval.s.len)) { snprintf(dest->destmedia, sizeof(dest->destmedia), "%.*s", destmediaval.s.len, destmediaval.s.s); } else { dest->destmedia[0] = '\0'; } } else { LM_DBG("200 or 202 allready recorded\n"); } break; case 408: case 487: if (!dest->endtime) { dest->endtime = time(NULL); } else { LM_DBG("408 or 487 end allready recorded\n"); } break; default: /* It may overwrite existing end time, it is the expected behavior */ if ((code >= 400) && (code <= 699)) { dest->endtime = time(NULL); } } } /* * Check destination status for reporting usage * param code Destination status * return 1 should report, 0 should not report */ static int ospIsToReportUsage( int code) { int istime = 0; LM_DBG("code '%d'\n", code); if (code >= 200) { istime = 1; } return istime; } /* * Report call setup usage for both client and server side * param clientcode Client status * param servercode Server status */ void ospRecordEvent( int clientcode, int servercode) { osp_dest* dest; LM_DBG("client status '%d'\n", clientcode); if ((clientcode != 0) && (dest = ospGetLastOrigDestination())) { ospRecordCode(clientcode, dest); if (ospIsToReportUsage(servercode) == 1) { ospReportOrigSetupUsage(); } } LM_DBG("server status '%d'\n", servercode); if ((servercode != 0) && (dest = ospGetTermDestination())) { ospRecordCode(servercode, dest); if (ospIsToReportUsage(servercode) == 1) { ospReportTermSetupUsage(); } } } /* * Dump destination information * param dest Destination */ void ospDumpDestination( osp_dest* dest) { LM_DBG("dest->host..........'%s'\n", dest->host); LM_DBG("dest->used..........'%d'\n", dest->used); LM_DBG("dest->lastcode......'%d'\n", dest->lastcode); LM_DBG("dest->time100.......'%d'\n", (unsigned int)dest->time100); LM_DBG("dest->time180.......'%d'\n", (unsigned int)dest->time180); LM_DBG("dest->time200.......'%d'\n", (unsigned int)dest->time200); } /* * Dump all destination information */ void ospDumpAllDestination(void) { struct usr_avp* destavp = NULL; int_str destval; osp_dest* dest = NULL; int count = 0; for (destavp = search_first_avp(AVP_VAL_STR, _osp_origdest_avpid, NULL, 0); destavp != NULL; destavp = search_next_avp(destavp, NULL)) { get_avp_val(destavp, &destval); /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; LM_DBG("....originate '%d'....\n", count++); ospDumpDestination(dest); } if (count == 0) { LM_DBG("there is not originate destination AVP\n"); } if (search_first_avp(AVP_VAL_STR, _osp_termdest_avpid, &destval, 0) != NULL) { /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; LM_DBG("....terminate....\n"); ospDumpDestination(dest); } else { LM_DBG("there is not terminate destination AVP\n"); } } /* * Convert "address:port" to "[x.x.x.x]:port" or "hostname:port" format * param src Source address string * param dest Destination address string * param bufsize Size of dest buffer */ void ospConvertToOutAddress( const char* src, char* dest, int bufsize) { struct in_addr inp; char buffer[OSP_STRBUF_SIZE]; char* port; int size; if ((src != NULL) && (*src != '\0')) { size = sizeof(buffer); strncpy(buffer, src, size); buffer[size - 1] = '\0'; if((port = strchr(buffer, ':')) != NULL) { *port = '\0'; port++; } if (inet_pton(AF_INET, buffer, &inp) == 1) { if (port != NULL) { snprintf(dest, bufsize, "[%s]:%s", buffer, port); } else { snprintf(dest, bufsize, "[%s]", buffer); } } else { strncpy(dest, src, bufsize); dest[bufsize - 1] = '\0'; } } else { *dest = '\0'; } } /* * Convert "[x.x.x.x]:port" or "hostname:prot" to "address:port" format * param src Source address string * param dest Destination address string * param bufsize Size of dest buffer */ void ospConvertToInAddress( const char* src, char* dest, int bufsize) { char buffer[OSP_STRBUF_SIZE]; char* end; char* port; int size; if ((src != NULL) && (*src != '\0')) { size = sizeof(buffer); strncpy(buffer, src, size); buffer[size - 1] = '\0'; if (buffer[0] == '[') { if((port = strchr(buffer + 1, ':')) != NULL) { *port = '\0'; port++; } if ((end = strchr(buffer + 1, ']')) != NULL) { *end = '\0'; } if (port != NULL) { snprintf(dest, bufsize, "%s:%s", buffer + 1, port); } else { strncpy(dest, buffer + 1, bufsize); dest[bufsize - 1] = '\0'; } } else { strncpy(dest, src, bufsize); dest[bufsize - 1] = '\0'; } } else { *dest = '\0'; } } opensips-2.2.2/modules/osp/destination.h000066400000000000000000000073361300170765700203370ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_DESTINATION_H_ #define _OSP_MOD_DESTINATION_H_ #include #include #include "osp_mod.h" typedef struct _osp_inbound { char ingressaddr[OSP_STRBUF_SIZE]; char srcdev[OSP_STRBUF_SIZE]; char source[OSP_STRBUF_SIZE]; char snid[OSP_STRBUF_SIZE]; char swid[OSP_STRBUF_SIZE]; char calling[OSP_STRBUF_SIZE]; char called[OSP_STRBUF_SIZE]; char fromdisplay[OSP_STRBUF_SIZE]; char fromuri[OSP_STRBUF_SIZE]; char touri[OSP_STRBUF_SIZE]; char paiuser[OSP_STRBUF_SIZE]; char rpiduser[OSP_STRBUF_SIZE]; char pciuser[OSP_STRBUF_SIZE]; char divuser[OSP_STRBUF_SIZE]; char divhost[OSP_STRBUF_SIZE]; char pcvicid[OSP_STRBUF_SIZE]; char srcmedia[OSP_STRBUF_SIZE]; time_t date; time_t authtime; } osp_inbound; typedef struct _osp_dest { int type; unsigned int destcount; int supported; int used; int reported; unsigned long long transid; char validafter[OSP_STRBUF_SIZE]; char validuntil[OSP_STRBUF_SIZE]; char callid[OSP_STRBUF_SIZE]; unsigned int callidsize; char calling[OSP_STRBUF_SIZE]; char called[OSP_STRBUF_SIZE]; char host[OSP_STRBUF_SIZE]; char destdev[OSP_STRBUF_SIZE]; char dnid[OSP_STRBUF_SIZE]; char swid[OSP_STRBUF_SIZE]; char nprn[OSP_STRBUF_SIZE]; char npcic[OSP_STRBUF_SIZE]; int npdi; char opname[OSPC_OPNAME_NUMBER][OSP_STRBUF_SIZE]; char cnam[OSP_STRBUF_SIZE]; char destmedia[OSP_STRBUF_SIZE]; char egress[OSP_STRBUF_SIZE]; unsigned char token[OSP_TOKENBUF_SIZE]; unsigned int tokensize; unsigned int timelimit; OSPE_PROTOCOL_NAME protocol; OSPE_SERVICE srvtype; int lastcode; time_t starttime; time_t endtime; time_t time100; time_t time180; time_t time200; } osp_dest; int ospParseAvps(void); void ospInitInboundInfo(osp_inbound* inbound); int ospSaveInboundInfo(osp_inbound* inbound); osp_inbound* ospGetInboundInfo(void); osp_dest* ospInitDestination(osp_dest* dest); int ospSaveOrigDestination(osp_dest* dest); int ospSaveTermDestination(osp_dest* dest); int ospCheckOrigDestination(void); osp_dest* ospGetNextOrigDestination(void); osp_dest* ospGetLastOrigDestination(void); osp_dest* ospGetTermDestination(void); void ospRecordEvent(int clientcode, int servercode); void ospDumpDestination(osp_dest* dest); void ospDumpAllDestination(void); void ospConvertToOutAddress(const char* src, char* dest, int bufsize); void ospConvertToInAddress(const char* src, char* dest, int bufsize); #endif /* _OSP_MOD_DESTINATION_H_ */ opensips-2.2.2/modules/osp/doc/000077500000000000000000000000001300170765700164015ustar00rootroot00000000000000opensips-2.2.2/modules/osp/doc/osp.xml000066400000000000000000000025661300170765700177350ustar00rootroot00000000000000 %docentities; ]> OSP Module for Secure, Multi-Lateral Peering &osipsname; Ulrich Abend &fhg;
ullstar@iptel.org
Di-Shi Sun TransNexus, Inc.
support@transnexus.com
2003 &fhg; $Revision: 5901 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/osp/doc/osp_admin.xml000066400000000000000000001145461300170765700211070ustar00rootroot00000000000000 &adminguide;
Overview The OSP module enables OpenSIPS to support secure, multi-lateral peering using the OSP standard defined by ETSI (TS 101 321 V4.1.1). This module will enable your OpenSIPS to: Send a peering authorization request to a peering server. Validate a digitally signed peering authorization token received in a SIP INVITE message. Report usage information to a peering server.
Dependencies The OSP module depends on the following modules which must be loaded before the OSP module. auth -- Authentication Framework module avpops -- AVP operation module maxfwd -- Max-Forward processor module mi_fifo -- FIFO support for Management Interface options -- OPTIONS server replier module proto_udp -- UDP protocol module - implements UDP-plain transport for SIP registrar -- SIP Registrar implementation module rr -- Record-Route and Route module signaling -- SIP signaling module sipmsgops -- SIP operations module sl -- Stateless replier module tm -- Transaction (stateful) module uac -- UAC functionalies (FROM mangling and UAC auth) uac_auth -- UAC Authentication functionality usrloc -- User location implementation module OSP Toolkit -- The OSP Toolkit, available from http://sourceforge.net/projects/osp-toolkit, must be built before building OpenSIPS with the OSP module. For instructions on building OpenSIPS with the OSP Toolkit, see http://www.http://transnexus.com/wp-content/uploads/OSP-Routing-and-CDR-Collection-Server-with-OpenSIPS-1.7.2.pdf. For OpenSIPS 2.1.0, OSP Toolkit 4.5.0 or later versions should be used.
Exported Parameters
<varname>work_mode</varname> The work_mode (integer) parameter instructs the OSP module what mode it should work in. If this value is set to 0, the OSP module works in direct mode. If this value is set to 1, the OSP module works in indirect mode. The default value is 0. Instructing the module to work in direct mode modparam("osp","work_mode",0)
<varname>service_type</varname> The service_type (integer) parameter instructs the OSP module what services it should provide. If this value is set to 0, the OSP module provides normal voice service. If this value is set to 1, the OSP module provides ported number query service. If this value is set to 2, the OSP module provides CNAM query service. The default value is 0. Instructing the module to provide normal voice service modparam("osp","service_type",0)
<varname>sp1_uri</varname>, <varname>sp2_uri</varname>, ..., <varname>sp16_uri</varname> These sp_uri (string) parameters define peering servers to be used for requesting peering authorization and routing information. At least one peering server must be configured. Others are required only if there are more than one peering servers. Each peering server address takes the form of a standard URL, and consists of up to four components: An optional indication of the protocol to be used for communicating with the peering server. Both HTTP and HTTP secured with SSL/TLS are supported and are indicated by "http://" and "https://" respectively. If the protocol is not explicitly indicated, the OpenSIPS defaults to HTTP secured with SSL. The Internet domain name for the peering server. An IP address may also be used, provided it is enclosed in square brackets such as [172.16.1.1]. An optional TCP port number for communicating with the peering server. If the port number is omitted, the OpenSIPS defaults to port 5045 (for HTTP) or port 1443 (for HTTP secured with SSL). The uniform resource identifier for requests to the peering server. This component is not optional and must be included. Setting the OSP servers modparam("osp","sp1_uri","http://osptestserver.transnexus.com:5045/osp") modparam("osp","sp2_uri","https://[1.2.3.4]:1443/osp")
<varname>sp1_weight</varname>, <varname>sp2_weight</varname>, ..., <varname>sp16_weight</varname> These sp_weight (integer) parameters are used for load balancing peering requests to peering servers. These parameters are most effective when configured as factors of 1000. For example, if sp1_uri should manage twice the traffic load of sp2_uri, then set sp1_weight to 2000 and sp2_weight to 1000. Shared load balancing between peering servers is recommended. However, peering servers can be configured as primary and backup by assigning a sp_weight of 0 to the primary server and a non-zero sp_weight to the back-up server. The default values for sp1_weight and sp2_weight are 1000. Setting the OSP server weights modparam("osp","sp1_weight",1000)
<varname>device_ip</varname> The device_ip (string) is a recommended parameter that explicitly defines the IP address of OpenSIPS in a peering request message (as SourceAlternate type=transport). The dotted-decimal IP address must be in brackets as shown in the example below. Setting the device IP address modparam("osp","device_ip","[127.0.0.1]:5060")
<varname>use_security_features</varname> The use_security_features (integer) parameter instructs the OSP module how to use the OSP security features. If this value is set to 1, the OSP module uses the OSP security features. If this value is set to 0, the OSP module will not use the OSP security features. The default value is 0. Instructing the module not to use OSP security features modparam("osp","use_security_features",0)
<varname>token_format</varname> When OpenSIPS receives a SIP INVITE with a peering token, the OSP module will validate the token to determine whether or not the call has been authorized by a peering server. Peering tokens may, or may not, be digitally signed. The token_format (integer) parameter defines if OpenSIPS will validate signed or unsigned tokens or both. The values for token format are defined below. The default value is 2. If use_security_features parameter is set to 0, signed tokens cannot be validated. 0 - Validate only signed tokens. Calls with valid signed tokens are allowed. 1 - Validate only unsigned tokens. Calls with valid unsigned tokens are allowed. 2 - Validate both signed and unsigned tokens are allowed. Calls with valid tokens are allowed. Setting the token format modparam("osp","token_format",2)
<varname>private_key</varname>, <varname>local_certificate</varname>, <varname>ca_certificates</varname> These parameters identify files are used for validating peering authorization tokens and establishing a secure channel between OpenSIPS and a peering server using SSL. The files are generated using the 'Enroll' utility from the OSP Toolkit. By default, the proxy will look for pkey.pem, localcert.pem, and cacart_0.pem in the default configuration directory. The default config directory is set at compile time using CFG_DIR and defaults to /usr/local/etc/opensips/. The files may be copied to the expected file location or the parameters below may be changed. If use_security_features parameter is set to 0, these parameters will be ignored. Set authorization files If the default CFG_DIR value was used at compile time, the files will be loaded from: modparam("osp","private_key","/usr/local/etc/opensips/pkey.pem") modparam("osp","local_certificate","/usr/local/etc/opensips/localcert.pem") modparam("osp","ca_certificates","/usr/local/etc/opensips/cacert.pem")
<varname>enable_crypto_hardware_support</varname> The enable_crypto_hardware_support (integer) parameter is used to set the cryptographic hardware acceleration engine in the openssl library. The default value is 0 (no crypto hardware is present). If crypto hardware is used, the value should be set to 1. Setting the hardware support modparam("osp","enable_crypto_hardware_support",0)
<varname>ssl_lifetime</varname> The ssl_lifetime (integer) parameter defines the lifetime, in seconds, of a single SSL session key. Once this time limit is exceeded, the OSP module will negotiate a new session key. Communication exchanges in progress will not be interrupted when this time limit expires. This is an optional field with default value is 200 seconds. Setting the ssl lifetime modparam("osp","ssl_lifetime",200)
<varname>persistence</varname> The persistence (integer) parameter defines the time, in seconds, that an HTTP connection should be maintained after the completion of a communication exchange. The OSP module will maintain the connection for this time period in anticipation of future communication exchanges to the same peering server. Setting the persistence modparam("osp","persistence",1000)
<varname>retry_delay</varname> The retry_delay (integer) parameter defines the time, in seconds, between retrying connection attempts to an OSP peering server. After exhausting all peering servers the OSP module will delay for this amount of time before resuming connection attempts. This is an optional field with default value is 1 second. Setting the retry delay modparam("osp","retry_delay",1)
<varname>retry_limit</varname> The retry_limit (integer) parameter defines the maximum number of retries for connection attempts to a peering server. If no connection is established after this many retry attempts to all peering servers, the OSP module will cease connection attempts and return appropriate error codes. This number does not count the initial connection attempt, so that a retry_limit of 1 will result in a total of two connection attempts to every peering server. The default value is 2. Setting the retry limit modparam("osp","retry_limit",2)
<varname>timeout</varname> The timeout (integer) parameter defines the maximum time in milliseconds, to wait for a response from a peering server. If no response is received within this time, the current connection is aborted and the OSP module attempts to contact the next peering server. The default value is 10 seconds. Setting the timeout modparam("osp","timeout",10)
<varname>support_nonsip_protocol</varname> The support_nonsip_protocol (integer) parameter is used to tell the OSP module if non-SIP signaling protocol destination devices are supported. The default value is 0. Setting support non-SIP destination devices modparam("osp","support_nonsip_protocol",0)
<varname>max_destinations</varname> The max_destinations (integer) parameter defines the maximum number of destinations that OpenSIPS requests the peering server to return in a peering response. The OSP module supports up to 12 destinations. The default value is 12. Setting the number of destination modparam("osp","max_destinations",12)
<varname>report_networkid</varname> The report_networkid (integer) parameter is used to tell the OSP module if to report network ID in completed call CDRs. If it is set to 0, ths OSP module does not report any network ID. If it is set to 1, the OSP module reports source network ID. If it is set to 2, the OSP module reports destination network ID. If it is set to 3, the OSP module report both source and destination network IDs. The default value is 3. Setting report network ID flag modparam("osp","report_networkid",3)
<varname>validate_call_id</varname> The validate_call_id (integer) parameter instructs the OSP module to validate call id in the peering token. If this value is set to 1, the OSP module validates that the call id in the SIP INVITE message matches the call id in the peering token. If they do not match the INVITE is rejected. If this value is set to 0, the OSP module will not validate the call id in the peering token. The default value is 1. Instructing the module to validate call id modparam("osp","validate_call_id",1)
<varname>use_number_portability</varname> The use_number_portability (integer) parameter instructs the OSP module how to use the number portability parameters in the Request URI of the SIP INVITE message. If this value is set to 1, the OSP module uses the number portability parameters in the Request URI when these parameters exist. If this value is set to 0, the OSP module will not use the number portability parameters. The default value is 1. Instructing the module to use number portability parameters in Request URI modparam("osp","use_number_portablity",1)
<varname>append_userphone</varname> The append_userphone (integer) parameter instructs the OSP module if to append "user=phone" parameter in URI. If this value is set to 0, the OSP module does not append "user=phone" parameter. If this value is set to 1, the OSP module will append "user=phone" parameter. The default value is 0 Append user=phone parameter modparam("osp","append_userphone",0)
<varname>networkid_location</varname> The networkid_location (integer) parameter instructs the OSP module where the destination network ID should be appended. The default value is 2 0 - network ID is not appended. 1 - network ID is appended as userinfo parameter. 2 - network ID is appended as URI parameter. Append networkid location modparam("osp","networkid_location",2)
<varname>networkid_parameter</varname> The networkid_parameter (string) parameter instructs the OSP module to use which parameter name in outbound destination URIs to append destination network ID. The default value is "networkid" Networkid parameter name modparam("osp","networkid_param","networkid")
<varname>switchid_location</varname> The switchid_location (integer) parameter instructs the OSP module where the destination switch ID should be appended. The default value is 2 0 - switch ID is not appended. 1 - switch ID is appended as userinfo parameter. 2 - switch ID is appended as URI parameter. Append switchid location modparam("osp","switchid_location",2)
<varname>switchid_parameter</varname> The switchid_parameter (string) parameter instructs the OSP module to use which parameter name in outbound destination URIs to append destination switch ID. The default value is "switchid" Networkid parameter name modparam("osp","switchid_param","switchid")
<varname>parameterstring_location</varname> The parameterstring_location (integer) parameter instructs the OSP module where the parameter string should be appended. The default value is 0 0 - parameter string is not appended. 1 - parameter string is appended as userinfo parameter. 2 - parameter string is appended as URI parameter. Append parameter string location modparam("osp","parameterstring_location",0)
<varname>parameterstring_value</varname> The parameterstring_value (string) parameter instructs the OSP module to append the parameter string in outbound URIs. The default value is "" Parameter string value modparam("osp","parameterstring_value","")
<varname>source_device_avp</varname> The source_device_avp (string) parameter instructs the OSP module to use the defined AVP to pass the source device IP value in the indirect work mode. The default value is "$avp(_osp_source_device_)". Then the source device IP can be set by "$avp(_osp_source_device_) = pseudo-variables". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Setting the source device IP AVP modparam("osp","source_device_avp","$avp(srcdev)")
<varname>source_networkid_avp</varname> The source_networkid_avp (string) parameter instructs the OSP module to use the defined AVP to pass the source network ID value. The default value is "$avp(_osp_source_networkid_)". Then the source network ID can be set by "$avp(_osp_source_networkid_) = pseudo-variables". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Setting the source network ID AVP modparam("osp","source_networkid_avp","$avp(snid)")
<varname>source_switchid_avp</varname> The source_switchid_avp (string) parameter instructs the OSP module to use the defined AVP to pass the source switch ID value. The default value is "$avp(_osp_source_switchid_)". Then the source switch ID can be set by "$avp(_osp_source_switchid_) = pseudo-variables". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Setting the source switch ID AVP modparam("osp","source_switchid_avp","$avp(swid)")
<varname>custom_info_avp</varname> The custom_info_avp (string) parameter instructs the OSP module to use the defined AVP to pass the custom information values. The default value is "$avp(_osp_custom_info_)". Then the custom information can be set by "$avp(_osp_custom_info_) = pseudo-variables". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Setting the custom info AVP modparam("osp","custom_info_avp","$avp(cinfo)")
<varname>cnam_avp</varname> The cnam_avp (string) parameter instructs the OSP module to use the defined AVP to pass the CNAM values. The default value is "$avp(_osp_cnam_)". Then the CNAM can be used by "$avp(_osp_cnam_)". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Setting the CNAM AVP modparam("osp","cnam_avp","$avp(cnam)")
<varname>extraheaders_value</varname> The extraheaders_value (string) parameter instructs the OSP module to append the defined SIP headers in outbound SIP NOTIFY messages. The default value is empty. Setting the NOTIFY extra headers modparam("osp", "extraheaders_value", "Source: N")
<varname>source_media_avp, destination_media_avp</varname> These parameters are used to tell the OSP module which AVPs are used to store media addresses. The default values are "$avp(_osp_source_media_address_)" and "$avp(_osp_destination_media_address_)". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Setting the media address AVPs modparam("osp", "source_media_avp", "$avp(srcmedia)") modparam("osp", "destination_media_avp", "$avp(destmedia)")
<varname>request_date_avp</varname> The request_date_avp (string) parameter instructs the OSP module to use the defined AVP to pass the SIP request Date header values. The default value is "$avp(_osp_request_date_)". Then the request date can be used by "$avp(_osp_request_date_)". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Setting the request date AVP modparam("osp","request_date_avp","$avp(reqdate)")
<varname>sdp_fingerprint_avp</varname> The sdp_fingerprint_avp (string) parameter instructs the OSP module to use the defined AVP to pass the SDP fing print attribute values. The default value is "$avp(_osp_sdp_fingerprint_)". Then the SDP finger print attributes can be used by "$avp(_osp_sdp_fingerprint_)". All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Setting the SDP finger print AVP modparam("osp","sdp_fingerprint_avp","$avp(sdpfp)")
<varname>identity_signature_avp</varname>, <varname>identity_algorithm_avp</varname>, <varname>identity_information_avp</varname>, <varname>identity_type_avp</varname>, <varname>identity_canon_avp</varname> These parameters instruct the OSP module to use the defined AVPs to pass the Identity related values. The default values are "$avp(_osp_identity_signature_)", "$avp(_osp_identity_algorithm_)", "$avp(_osp_identity_information_)", "$avp(_osp_identity_type_)", "$avp(_osp_identity_canon_)". Then the indentity related values can be used by these AVPs. All pseudo variables are described in http://www.opensips.org/Resources/DocsCoreVar. Setting the Identity related AVPs modparam("osp","identity_signature_avp","$avp(idsign)") modparam("osp","identity_algorithm_avp","$avp(idalg)") modparam("osp","identity_information_avp","$avp(idinfo)") modparam("osp","identity_type_avp","$avp(idtype)") modparam("osp","identity_canon_avp","$avp(idcanon)")
Exported Functions
<function moreinfo="none">checkospheader()</function> This function checks for the existence of the OSP-Auth-Token header field. This function can be used from REQUEST_ROUTE. checkospheader usage ... if (checkospheader()) { log(1,"OSP header field found.\n"); } else { log(1,"no OSP header field present\n"); }; ...
<function moreinfo="none">validateospheader()</function> This function validates an OSP-Token specified in the OSP-Auth-Tokenheader field of the SIP message. If a peering token is present, it will be validated locally. If no OSP header is found or the header token is invalid or expired, -1 is returned; on successful validation 1 is returned. This function can be used from REQUEST_ROUTE. validateospheader usage ... if (validateospheader()) { log(1,"valid OSP header found\n"); } else { log(1,"OSP header not found, invalid or expired\n"); }; ...
<function moreinfo="none">getlocaladdress()</function> This function gets the receiving IP address of SIP response and stores it as proxy egress address. This function can be used from ONREPLY_ROUTE. getlocaladress usage ... if (getlocaladdress()) { log(1,"Obtain proxy local egress address\n"); } else { log(1,"Failed to get proxy local egress address\n"); }; ...
<function moreinfo="none">setrequestdate()</function> This function gets the receiving IP address of SIP response and stores it as proxy egress address. This function can be used from REQUEST_ROUTE. setrequestdate usage ... if (setrequest()) { log(1,"Set request date\n"); } else { log(1,"Failed to set request date\n"); }; ...
<function moreinfo="none">requestosprouting()</function> This function launches a query to the peering server requesting the IP address of one or more destination peers serving the called party. If destination peers are available, the peering server will return the IP address and a peering authorization token for each destination peer. The OSP-Auth-Token Header field is inserted into the SIP message and the SIP uri is rewritten to the IP address of destination peer provided by the peering server. The address of the called party must be a valid E164 number, otherwise this function returns -1. If the transaction was accepted by the peering server, the uri is being rewritten and 1 returned, on errors (peering servers are not available, authentication failed or there is no route to destination or the route is blocked) -1 is returned. This function can be used from REQUEST_ROUTE. requestosprouting usage ... if (requestosprouting()) { log(1,"successfully queried OSP server, now relaying call\n"); } else { log(1,"Authorization request was rejected from OSP server\n"); }; ...
<function moreinfo="none">checkosproute()</function> This function is used to check if there is any route for the call. This function can be used from REQUEST_ROUTE. checkosproute usage ... if (checkosproute()) { log(1,"There is at least one route for the call\n"); } else { log(1,"There is not any route for the call\n"); }; ...
<function moreinfo="none">prepareosproute()</function> This function tries to prepare the INVITE to be forwarded using the destination in the list returned by the peering server. If the calling number is translated, a RPID value for the RPID AVP will be set. If the route could not be prepared, the function returns 'FALSE' back to the script, which can then decide how to handle the failure. Note, if checkosproute has been called and returns 'TRUE' before calling prepareosproute, prepareosproute should not return 'FALSE' because checkosproute has confirmed that there is at least one route. This function can be used from BRANCH_ROUTE. prepareosproute usage ... if (prepareosproute()) { log(1,"successfully prepared the route, now relaying call\n"); } else { log(1,"could not prepare the route, there is not route\n"); }; ...
<function moreinfo="none">prepareospresponse()</function> This function tries to prepare all the routes in the list returned by the peering server into SIP 300 Redirect or SIP 380 Alternative Service message. The message is then replied to the source. If unsuccessful in preparing the routes a SIP 500 is sent back and a trace message is logged. This function can be used from REQUEST_ROUTE. prepareospresponse usage ... if (prepareospresponse()) { log(1,"Response is prepared.\n"); } else { log(1,"Could not prepare the response.\n"); }; ...
<function moreinfo="none">prepareallosproutes()</function> This function tries to prepare all the routes in the list returned by the peering server. The message is then forked off to the destinations. If unsuccessful in preparing the routes a SIP 500 is sent back and a trace message is logged. This function can be used from REQUEST_ROUTE. prepareallosproutes usage ... if (prepareallosproutes()) { log(1,"Routes are prepared, now forking the call\n"); } else { log(1,"Could not prepare the routes. No destination available\n"); }; ...
<function moreinfo="none">checkcallingtranslation()</function> This function is used to check if the calling number is translated. Before calling checkcallingtranslation, prepareosproute should be called. If the calling number does been translated, the original Remote-Party-ID, if it exists, should be removed from the INVITE message. And a new Remote-Party-ID header should be added (a RPID value for the RPID AVP has been set by prepareosproute). If the calling number is not translated, nothing should be done. This function can be used from BRANCH_ROUTE. checkcallingtranslation usage ... if (checkcallingtranslation()) { # Remove the Remote_Party-ID from the received message # Otherwise it will be forwarded on to the next hop remove_hf("Remote-Party-ID"); # Append a new Remote_Party append_rpid_hf(); } ...
<function moreinfo="none">reportospusage()</function> This function should be called after receiving a BYE message. If the message contains an OSP cookie, the function will forward originating and/or terminating duration usage information to a peering server. The function returns TRUE if the BYE includes an OSP cookie. The actual usage message will be send on a different thread and will not delay BYE processing. The function should be called before relaying the message. Meaning of the parameter is as follows: "0" - Source device releases the call. "1" - Destination device releases the call. This function can be used from REQUEST_ROUTE. reportospusage usage ... if (is_direction("downstream")) { log(1,"This BYE message is from SOURCE\n"); if (!reportospusage("0")) { log(1,"This BYE message does not include OSP usage information\n"); } } else { log(1,"This BYE message is from DESTINATION\n"); if (!reportospusage("1")) { log(1,"This BYE message does not include OSP usage information\n"); } } ...
<function moreinfo="none">processsubscribe()</function> This function should be called after receiving a SUBSCRIBE for CNAM message and there is a cached CNAM record for this message. This function generates a NOTIFY message including the cached CNAM record, then sends the NOTIFY message to the device sending the SUBSCRIBE message. Meaning of the parameter is as follows: cachedcnamrecord - Cached CNAM record. This function can be used from REQUEST_ROUTE. processsubscribe usage ... if (method == "SUBSCRIBE") { if (($var(sevent) == "calling-name") && (uri == myself)) { if ($var(cnamrecord) != NULL) { processsubscribe("$(var(cnamrecord){s.b64decode})"); } else { t_relay("1.2.3.4", "0x02"); } } else { t_relay(); } } ...
opensips-2.2.2/modules/osp/doc/osp_devel.xml000066400000000000000000000002641300170765700211050ustar00rootroot00000000000000 &develguide; The functions of the OSP modules are not used by other OpenSIPS modules. opensips-2.2.2/modules/osp/etc/000077500000000000000000000000001300170765700164075ustar00rootroot00000000000000opensips-2.2.2/modules/osp/etc/cacert_0.pem000066400000000000000000000010321300170765700205660ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBYDCCAQoCAQEwDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNl cnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTAyMDIw NDE4MjU1MloXDTEyMDIwMzE4MjU1MlowOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZl ci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMFwwDQYJKoZIhvcN AQEBBQADSwAwSAJBAPGeGwV41EIhX0jEDFLRXQhDEr50OUQPq+f55VwQd0TQNts0 6BP29+UiNdRW3c3IRHdZcJdC1Cg68ME9cgeq0h8CAwEAATANBgkqhkiG9w0BAQQF AANBAGkzBSj1EnnmUxbaiG1N4xjIuLAWydun7o3bFk2tV8dBIhnuh445obYyk1En Q27kI7eACCILBZqi2MHDOIMnoN0= -----END CERTIFICATE----- opensips-2.2.2/modules/osp/etc/localcert.pem000066400000000000000000000010711300170765700210610ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBeTCCASMCEHqkOHVRRWr+1COq3CR/xsowDQYJKoZIhvcNAQEEBQAwOzElMCMG A1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQ U2VydmVyMB4XDTA1MDYyMzAwMjkxOFoXDTA2MDYyNDAwMjkxOFowRTELMAkGA1UE BhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdp ZGdpdHMgUHR5IEx0ZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCvLeZfj1G0wuJb 8JTcVeZaZftncEmRluOnaME3ustd918lRMDYokZmtmDZN8SrP0yd1pfVqZ7NkmBA CbBX0k7pAgMBAAEwDQYJKoZIhvcNAQEEBQADQQDnV8QNFVVJx/+7IselU0wsepqM urivXZzuxOmTEmTVDzCJx1xhA8jd3vGAj7XDIYiPub1PV23eY5a2ARJuw5w9 -----END CERTIFICATE-----opensips-2.2.2/modules/osp/etc/pkey.pem000066400000000000000000000007551300170765700200710ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAK8t5l+PUbTC4lvwlNxV5lpl+2dwSZGW46dowTe6y133XyVEwNii Rma2YNk3xKs/TJ3Wl9Wpns2SYEAJsFfSTukCAwEAAQJAPz13vCm2GmZ8Zyp74usT xLCqSJZNyMRLHQWBM0g44Iuy4wE3vpi7Wq+xYuSOH2mu4OddnxswCP4QhaXVQavT AQIhAOBVCKXtppEw9UaOBL4vW0Ed/6EA/1D8hDW6St0h7EXJAiEAx+iRmZKhJD6V T84dtX5ZYNVk3j3dAcIOovpzUj9a0CECIEduTCapmZQ5xqAEsLXuVlxRtQgLTUD4 ZxDElPn8x0MhAiBE2HlcND0+qDbvtwJQQOUzDgqg5xk3w8capboVdzAlQQIhAMC+ lDL7+gDYkNAft5Mu+NObJmQs4Cr+DkDFsKqoxqrm -----END RSA PRIVATE KEY----- opensips-2.2.2/modules/osp/etc/sample-osp-opensips.cfg000066400000000000000000001152371300170765700230170ustar00rootroot00000000000000 log_level=3 # debug level (cmd line: -dddddddddd) log_stderror=no # (cmd line: -E) sip_warning=no /* Uncomment these line to enter debugging mode */ #debug_mode=yes check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) disable_dns_blacklist=yes listen=udp:127.0.0.1:5060 children=64 # --------- module loading ------------------------------------------- mpath="/usr/local/lib/opensips/modules" loadmodule "sl.so" loadmodule "tm.so" loadmodule "maxfwd.so" loadmodule "rr.so" loadmodule "sipmsgops.so" loadmodule "usrloc.so" loadmodule "signaling.so" loadmodule "registrar.so" loadmodule "auth.so" loadmodule "mi_fifo.so" loadmodule "options.so" loadmodule "avpops.so" loadmodule "proto_udp.so" # Load OSP module loadmodule "osp.so" # For SIP From header modification # loadmodule "uac_auth.so" # loadmodule "uac.so" # For CNAM SUBSCRIBE/NOTIFY # loadmodule "cachedb_local.so" # --------- setting module-specific parameters ----------------------- # # PEERING PARAMETERS # ==================================================================== # This section contains configuration parameters that enables OpenSIPS # to communicate with one or more OSP servers for routing and CDR # collection. The parameter sp1_uri must be configured. Additional # detail on OSP Module parameters and functions is provided in the # "OSP Module for Secure, Multi-Lateral Peering" document located at # http://www.opensips.org/html/docs/modules/devel/osp.html # # # Configure Work Mode # ==================================================================== # This parameter is used to tell the OSP module the work mode it # should work in. The default value is 0. # # 0 - Direct # 1 - Indirect # modparam("osp", "work_mode", 1) # # Configure Service Type # ==================================================================== # This parameter is used to tell the OSP module the service type it # should provide. The default value is 0. # # 0 - Normal voice service # 1 - Ported number query service # 2 - CNAM query service # # modparam("osp", "service_type", 0) # # Configure OSP Servers # ==================================================================== # OpenSIPS can be configured to query up to 16 OSP servers for routing # information, number portability correction and peering authorization # tokens using the sp1_uri to sp16_uri parameters. Each spX_uri is # the configuration for an OSP server. A configuration for sp1_uri # is required. Configuring other OSP servers, using sp2_uri, ..., # sp16_uri parameters, is optional. The OSP server address should be # configured as a standard URL beginning with either http:// or # https:// followed by the domain name of the OSP server or the IP # address enclosed in brackets. The domain name or IP address should # be followed by the peering server TCP port number and uniform # resource identifier. Below are example configurations. # modparam("osp", "sp1_uri", "http://osptestserver.transnexus.com:5045/osp") # modparam("osp", "sp2_uri", "https://[1.2.3.4]:1443/osp") # # OpenSIPS IP Address # ==================================================================== # device_ip is a recommended parameter that explicitly defines the IP # address of OpenSIPS in an OSP AuthorizationRequest message (as # SourceAlternate type=transport). The IP address must be in brackets # as shown in the example below. # # modparam("osp", "device_ip", "[127.0.0.1]:5060") # # Use OSP Security Features # ==================================================================== # This parameter is used to tell the OSP module if the OSP security # features should be used. The default value is 0. # # 0 - OSP module will not use the OSP security features. # 1 - OSP module will use the OSP security features. # # modparam("osp", "use_security_features", 0) # # Peering Token Validation # ==================================================================== # When OpenSIPS receives a SIP INVITE with an OSP peering token, the # OSP Module will validate the token to determine whether or not the # call has been authorized by a trusted OSP server. OSP peering # tokens may, or may not, be digitally signed. This parameter defines # if OpenSIPS will validate signed or unsigned tokens or both. The # values for "token format" are defined below. The default value # is 2. If the use_security_features parameter is set to 0, signed # tokens cannot be validated. # # 0 - Validate only signed tokens. Calls with valid signed tokens are # allowed. # 1 - Validate only unsigned tokens. Calls with valid unsigned tokens # are allowed. # 2 - Validate both signed and unsigned tokens are allowed. Calls # with valid tokens are allowed. # # modparam("osp", "token_format", 2) # # Crypto files from Peering Server Enrollment # ==================================================================== # These parameters identify crypto files used for validating peering # authorization tokens and establishing a secure channel between # OpenSIPS and an OSP server using SSL. The files may be generated # using the 'Enroll' utility from the OSP Toolkit. By default, the # proxy will look for pkey.pem, localcert.pem, and cacart_0.pem in the # default configuration directory. # # The default config directory is set at compile time using CFG_DIR # and defaults to /usr/local/etc/opensips/. The files may be copied # to the expected file location or the parameters below may be # changed. If the use_security_features parameter is set to 0, these # parameters will be ignored. # # If the default CFG_DIR value was used at compile time, the files # will be loaded from: # modparam("osp", "private_key", "/usr/local/etc/opensips/pkey.pem") # modparam("osp", "local_certificate", "/usr/local/etc/opensips/localcert.pem") # modparam("osp", "ca_certificates", "/usr/local/etc/opensips/cacert_0.pem") # # Support non-SIP signaling protocols # ==================================================================== # This parameter is used to tell the OSP module if non-SIP signaling # protocols are supported. The default value is 0. # # modparam("osp", "support_nonsip_protocol", 0) # # Max number of destiantions # ==================================================================== # This parameter is used to tell OSP servers max how many destinations # should be returned. It is up to 12. The default value is 12. # # modparam("osp", "max_destinations", 12) # # Report Network ID in CDRs # ==================================================================== # This parameter is used to tell the OSP module if to report # network IDs in completed call CDRs. The default value is 3. # # 0 - Do not report any network ID. # 1 - Report source network ID. # 2 - Report destination network ID. # 3 - Report both source and destination network ID. # # modparam("osp", "report_networkid", 3) # # Use Number Portability parameters # ==================================================================== # This parameter is used to tell the OSP module if the number # portability parameters should be obtained from RURI. The default # value is 1. # # 0 - OSP module will not use the number portability parameters. # 1 - OSP module will use the number portability parameters in RURI in # Authorization Request to OSP server if these parameters exist in # the INVITE received from the UA. # # modparam("osp", "use_number_portability", 1) # # Append "user=phone" Parameter # ==================================================================== # This parameter is used to tell the OSP module if the "user=phone" # parameter, which indicates that the user portion of the URI should # be treated as a tel URI, should be appended. The default value is # 0. # # modparam("osp", "append_userphone", 0) # # Destination Network ID Location # ==================================================================== # This parameter is used to tell the OSP module where the destination # network ID should be appended. The default value is 2. # # 0 - network ID is not appended. # 1 - network ID is appended as userinfo parameter. # 2 - network ID is appended as URI parameter. # # modparam("osp", "networkid_location", 2) # # Destination Network ID Parameter Name # ==================================================================== # This parameter is used to tell the OSP module the parameter name # that is used to carry destination network ID. The default value is # "networkid". # # modparam("osp", "networkid_parameter", "networkid") # # Destination Switch ID Location # ==================================================================== # This parameter is used to tell the OSP module where the destination # switch ID should be appended. The default value is 2. # # 0 - switch ID is not appended. # 1 - switch ID is appended as userinfo parameter. # 2 - switch ID is appended as URI parameter. # # modparam("osp", "switchid_location", 2) # # Destination Switch ID Parameter Name # ==================================================================== # This parameter is used to tell the OSP module the parameter name # that is used to carry destination switch ID. The default value is # "switchid". # # modparam("osp", "switchid_parameter", "switchid") # # Parameter String Location # ==================================================================== # This parameter is used to tell the OSP module where the parameter # string should be appended in the outbound URI. The default value is # 0. # # 0 - parameter string is not appended. # 1 - parameter string is appended as userinfo parameter. # 2 - parameter string is appended as URI parameter. # # modparam("osp", "parameterstring_location", 0) # # Parameter String Value # ==================================================================== # This parameter is used to tell the OSP module the parameter string # that is appended in outbound URI. The default value is empty. # # modparam("osp", "parameterstring_value", "") # # Source Device IP AVP # ==================================================================== # This parameter is used to tell the OSP module which AVP is used to # store source device IP in the indirect work mode. The default # value is "$avp(_osp_source_device_)". # # modparam("osp", "source_device_avp", "$avp(_osp_source_device_)") modparam("osp", "source_device_avp", "$avp(srcdev)") # # Source Network ID AVP # ==================================================================== # This parameter is used to tell the OSP module which AVP is used to # store source network ID. The default value is # "$avp(_osp_source_networkid_)". # # modparam("osp", "source_networkid_avp", "$avp(_osp_source_networkid_)") modparam("osp", "source_networkid_avp", "$avp(snid)") # # Source Switch ID AVP # ==================================================================== # This parameter is used to tell the OSP module which AVP is used to # store source switch ID. The default value is # "$avp(_osp_source_switchid_)". # # modparam("osp", "source_switchid_avp", "$avp(_osp_source_switchid_)") modparam("osp", "source_switchid_avp", "$avp(swid)") # # Custom Information AVP # ==================================================================== # This parameter is used to tell the OSP module which AVP is used to # store custom information. The default value is # "$avp(_osp_custom_info_)". # # modparam("osp", "custom_info_avp", "$avp(_osp_custom_info_)") modparam("osp", "custom_info_avp", "$avp(cinfo)") # # CNAM AVP # ==================================================================== # This parameter is used to tell the OSP module which AVP is used to # store CNAM. The default value is "$avp(_osp_cnam_)". # # modparam("osp", "cnam_avp", "$avp(_osp_cnam_)") modparam("osp", "cnam_avp", "$avp(cnam)") # # NOTIFY extra headers # ==================================================================== # This parameter is used to tell the OSP module the extra SIP headers # that are appended in NOTIFY. The default value is empty. # # modparam("osp", "extraheaders_value", "Source: N") # # Media Address AVPs # ==================================================================== # These parameters are used to tell the OSP module which AVPs are used # to store media addresses. The default values are # "$avp(_osp_source_media_address_)" and # "$avp(_osp_destination_media_address_)". # # modparam("osp", "source_media_avp", "$avp(_osp_source_media_address_)") # modparam("osp", "destination_media_avp", "$avp(_osp_destination_media_address_)") modparam("osp", "source_media_avp", "$avp(srcmedia)") modparam("osp", "destination_media_avp", "$avp(destmedia)") # # Request Date header AVP # ==================================================================== # This parameter is used to tell the OSP module which AVP is used to # store request Date header. The default value is # "$avp(_osp_request_date_)". # # modparam("osp", "request_date_avp", "$avp(_osp_request_date_)") modparam("osp", "request_date_avp", "$avp(reqdate)") # # SDP finger print attributes AVP # ==================================================================== # This parameter is used to tell the OSP module which AVP is used to # store SDP finger print attributes. The default value is # "$avp(_osp_sdp_fingerprint_)". # # modparam("osp", "sdp_fingerprint_avp", "$avp(_osp_sdp_fingerprint_)") modparam("osp", "sdp_fingerprint_avp", "$avp(sdpfp)") # # Identity header AVPs # ==================================================================== # These parameters are used to tell the OSP module which AVPs are used # to store Identity header parameters. The default values are # "$avp(_osp_identity_signature_)", "$avp(_osp_identity_algorithm_)", # "$avp(_osp_identity_information_)", # "$avp(_osp_identity_type_)" and # "$avp(_osp_identity_canon_)" # # modparam("osp", "identity_signature_avp", "$avp(_osp_identity_signature_)") # modparam("osp", "identity_algorithm_avp", "$avp(_osp_identity_algorithm_)") # modparam("osp", "identity_information_avp", "$avp(_osp_identity_information_)") # modparam("osp", "identity_type_avp", "$avp(_osp_identity_type_)") # modparam("osp", "identity_canon_avp", "$avp(_osp_identity_canon_)") modparam("osp", "identity_signature_avp", "$avp(idsign)") modparam("osp", "identity_algorithm_avp", "$avp(idalg)") modparam("osp", "identity_information_avp", "$avp(idinfo)") modparam("osp", "identity_type_avp", "$avp(idtype)") modparam("osp", "identity_canon_avp", "$avp(idcanon)") # # -- mi_fifo params -- # # The name of the FIFO file to be created for listening and reading # external commands. # modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- usrloc params -- # # Disable the usrloc module to utilize database for persistent # contact storage. # modparam("usrloc", "db_mode", 0) # -- rr params -- # # Enable append_fromtag, request's from-tag is appended to # Record-Route. This is useful for understanding whether subsequent # requests (such as BYE) come from caller (route's from-tag == BYE's # from-tag) or callee (route's from-tag == BYE's to-tag). # modparam("rr", "append_fromtag", 1) # -- tm params -- # # Timer which hits if there is no response (including provisional # messages such as 100, 180, 183, ..., etc.) for a request such as # INVITE, RE-INVITE or BYE after fr_timeout seconds. # # For the INVITE use case, if the proxy does not receive a response # to an INVITE before this timer expires, the proxy will retry the # call and send an INVITE to the next VoIP destination in the routing # list. # # For the RE-INVITE and BYE use cases, if the proxy does not receive # a response before this timer expires, the proxy will send a SIP 408 # Timeout message to the source of the request. # modparam("tm", "fr_timeout", 2) # # Timer which hits if no final reply for an INVITE arrives after a # provisional message was received (in seconds). For example, user is # not picking up the phone. # modparam("tm", "fr_inv_timeout", 5) modparam("tm", "onreply_avp_mode", 1) modparam("tm", "restart_fr_on_each_reply", 1) # -- cachedb_local params -- # # The size of the hash table. This parameter will be used as the power of 2 when computing table size. # Default value is “9 (512)â€. # # modparam("cachedb_local", "cache_table_size", 21) # --------- request routing logic ------------------------------------ # main routing logic route{ xlog("L_INFO", "----ROUTE: Route IN - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); # initial sanity checks if (!mf_process_maxfwd_header("10")) { xlog("L_WARN", "----ROUTE: Too many hops, $rm from '$fu' to '$tu'\n"); sl_send_reply("483", "Too Many Hops"); return; }; if (msg:len >= max_len) { xlog("L_WARN", "----ROUTE: Message too big, $rm from '$fu' to '$tu'\n"); sl_send_reply("513", "Message Too Big"); return; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; this # is useful if upstream and downstream entities # use different transport protocol record_route(); # loose-route processing if (loose_route()) { if (method == "INVITE") { log(2, "----ROUTE: Relay re-INVITE\n"); # send it out now; use stateful forwarding as it works # reliably even for UDP2TCP if (!t_relay("0x02")) { sl_reply_error(); } return; } else if (method == "ACK") { log(2, "----ROUTE: Relay ACK\n"); # send it out now; use stateful forwarding as it works # reliably even for UDP2TCP if (!t_relay("0x02")) { sl_reply_error(); } return; } } else { if (method == "BYE") { xlog("L_WARN", "----ROUTE: Processing BYE without route header - F=$fu T=$tu IP=$si ID=$ci\n"); if (t_check_trans()) { log(2, "----ROUTE: Duplicated BYE\n"); } else { # NOTE - don't t_relay before reporting usage log(2, "----ROUTE: BYE from UNKNOWN\n"); if (!reportospusage("2")) { xlog("L_WARN", "----ROUTE: failed to report usage, from '$fu' to '$tu'\n"); } sl_send_reply("400", "Bad Request - no route header in BYE message"); } return; } } if (method == "INVITE") { log(2, "----ROUTE: Processing INVITE\n"); # Stop retransmission sl_send_reply("100", "Trying"); if (t_check_trans()) { log(2, "----ROUTE: Duplicated INVITE\n"); return; } # Authentication log(3, "OSP authorization validation logic\n"); # This function looks for OSP peering token in the message. # It will fail if the token is not present if (checkospheader()) { log(3, "With OSP token, validate it\n"); # The function validates OSP tokens. It will fail if the # token is not valid or has expired validateospheader(); switch ($retcode) { case 1: # Authorization is valid. The proxy can now use # its own database of registered users for # routing information. # The proxy could also issue another OSP peering # authorization and routing request by calling # route(1) function. log(3, "OSP authorization valid\n"); # Remove the OSP peering token from the received # message otherwise it will be forwarded on to # the next hop remove_hf("P-OSP-Auth-Token"); break; case -2: log(2, "----ROUTE: Internal server error\n"); sl_send_reply("500", "Internal Server Error"); return; default: log(2, "----ROUTE: OSP authorization invalid\n"); sl_send_reply("401", "Unauthorized"); return; }; } else { log(3, "Without OSP token, apply different authentication strategy\n"); log(3, "Go ahead, everyone is welcomed\n"); # Implement authentication strategy here or simply add the # statements below to block all invites without OSP # peering tokens # sl_send_reply("401", "Unauthorized"); # return; } log(2, "----ROUTE: Authentication passed\n"); # Routing # if (lookup("location")) { # log(2, "----ROUTE: Registered user, forward the message\n"); # append_hf("P-hint: usrloc\r\n"); # t_relay(); # } else { # log(2, "----ROUTE: Unregistered user, use OSP to get routing\n"); # route(2); # } log(2, "----ROUTE: Use OSP to get routing\n"); route(2); } else if (method == "ACK") { log(2, "----ROUTE: Processing ACK\n"); if (t_check_trans()) { log(2, "----ROUTE: Relay E2E ACK\n"); t_relay(); } else { log(2, "----ROUTE: Not to relay ACK"); } } else if (method == "BYE") { log(2, "----ROUTE: Processing BYE\n"); xlog("L_INFO", "----ROUTE: R=$hdr(Route)\n"); if (t_check_trans()) { log(2, "----ROUTE: Duplicated BYE\n"); return; } # NOTE - don't t_relay before reporting usage if (is_direction("downstream")) { log(2, "----ROUTE: BYE from SOURCE\n"); if (!reportospusage("0")) { xlog("L_WARN", "----ROUTE: failed to report usage, from '$fu' to '$tu'\n"); } } else { log(2, "----ROUTE: BYE from DESTINATION\n"); if (!reportospusage("1")) { xlog("L_WARN", "----ROUTE: failed to report usage, from '$fu' to '$tu'\n"); } } t_relay(); } else if (method == "CANCEL") { log(2, "----ROUTE: Processing CANCEL\n"); if (t_check_trans()) { t_relay(); } else { xlog("L_WARN", "----ROUTE: CANCEL without matching transaction, from '$fu' to '$tu'\n"); } } else if ((method == "OPTIONS") && (uri == myself)) { log(2, "----ROUTE: Processing OPTIONS\n"); $var(username) = "metaswitch"; if (($rU == $var(username)) && ($tU == $var(username)) && ($fU == $var(username))) { xlog("L_INFO", "----ROUTE: OPTIONS with username '$var(username)'\n"); sl_send_reply("200", "OPTIONS OK"); } else { options_reply(); } } else if (method == "PRACK") { log(2, "----ROUTE: Processing PRACK\n"); t_relay(); } else if (method == "INFO") { log(2, "----ROUTE: Processing INFO\n"); t_relay(); } else if (method == "UPDATE") { log(2, "----ROUTE: Processing UPDATE\n"); t_relay(); # } else if (method == "REGISTER") { # log(2, "----ROUTE: Processing REGISTER\n"); # # # Stop retransmission # sl_send_reply("100", "Trying"); # # if (uri == myself) { # log(2, "----ROUTE: Registered\n"); # save("location"); # } else { # log(2, "----ROUTE: Register from outside domain rejected\n"); # sl_send_reply("488", "Unknown Domain"); # } # } else if (method == "SUBSCRIBE") { # log(2, "----ROUTE: Processing SUBSCRIBE\n"); # # # Retrive Event header # $var(SEVENTREG) = "/^([^;]*).*/\1/s"; # $var(sevent) = $(hdr(Event){re.subst,$var(SEVENTREG)}); # if (($var(sevent) == "calling-name") && (uri == myself)) { # # Retrive calling number # $var(CLGPARTYREG) = "/.*Calling-party: *([^\r\n]*).*/\1/s"; # $var(clgparty) = $(rb{re.subst,$var(CLGPARTYREG)}); # $var(sclg) = $(var(clgparty){uri.user}); # # # Search local cache # $var(cnamrecord) = NULL; # cache_fetch("local", "CNAM_$var(sclg)", $var(cnamrecord)); # if ($retcode && ($var(cnamrecord) != NULL)) { # xlog("L_INFO", "----ROUTE: Find cached CNAM record for '$var(sclg)' '$var(cnamrecord)'\n"); # # # Process SUBSCRIBE # processsubscribe("$(var(cnamrecord){s.b64decode})"); # } else { # xlog("L_INFO", "----ROUTE: Failed to find cached CNAM record for '$var(sclg)'\n"); # # # Relay SUBSCRIBE to XXX and YYY # if (!t_relay("XXX", "0x02")) { # log(2, "----ROUTE: t_relay SUBSCRIBE to XXX failed.\n"); # if (!t_relay("YYY", "0x02")) { # log(2, "----ROUTE: t_relay SUBSCRIBE to YYY failed.\n"); # sl_reply_error(); # } # } # } # } else { # log(2, "----ROUTE: t_relay SUBSCRIBE.\n"); # t_relay(); # } # } else if (method == "NOTIFY") { # log(2, "----ROUTE: Processing NOTIFY\n"); # # # Retrive Event header # $var(NEVENTREG) = "/^([^;]*).*/\1/s"; # $var(nevent) = $(hdr(Event){re.subst,$var(NEVENTREG)}); # if ($var(nevent) == "calling-name") { # # Retrieve calling number # $var(CLGNAMEREG) = "/.*Calling-Name: *([^\r\n]*).*/\1/s"; # $var(clgname) = $(rb{re.subst,$var(CLGNAMEREG)}); # $var(nclg) = $(var(clgname){nameaddr.uri}{uri.user}); # # # Cache CNAM resultes # $var(cnamentry) = $(rb{s.b64encode}); # xlog("L_INFO", "----ROUTE: Cache CNAM for '$var(nclg)' '$var(cnamentry)'\n"); # cache_store("local", "CNAM_$var(nclg)", "$var(cnamentry)", 86400); # } # # t_relay(); } else { xlog("L_WARN", "----ROUTE: Unsupported message, $rm from '$fu' to '$tu'\n"); sl_send_reply("500", "Unsupported Message"); } log(3, "----ROUTE: Route OUT\n"); } # OSP Authorization and Routing route[2] { log(3, "OSP authorization and routing logic\n"); # Is request to a phone number? # A phone number consists of digits (0 through 9) # and can begin with + # if (uri =~ "sip:[+,0-9][0-9]*@") { # Requesting OSP peering routing and authorization # The request may fail if: # o OSP peering servers are not available # o Authentication failed # o There is no route to destination or the route is blocked log(3, "Requesting OSP authorization and routing\n"); # Get actual source device IP if (is_present_hf("P-Source-Device")) { $avp(srcdev) = $hdr(P-Source-Device); } # Get source network ID # OpenSIPS can include a specific header parameter as the # source NetworkId in an OSP AuthorizationRequest to an OSP # server. Below are two example configurations for this # feature. # # Uncomment the line below to use the otg parameter value in # the From URI ($fu) as the source NetworkId ($avp(snid)). # if ($(fu{param.exist, otg})) { # $avp(snid) = $(fu{param.value, otg}); # } # # Uncomment the line below to use the TGID parameter value in # the From header as the source NetworkId ($avp(snid)). # $var(PARAMREG) = "/^[^>]*>;//"; # $var(fparam) = $(hdr(From){re.subst,$var(PARAMREG)}); # if ($(var(fparam){param.exist, TGID})) { # $avp(snid) = $(var(fparam){param.value, TGID}); # } # if ($(var(fparam){param.exist, SWID})) { # $avp(swid) = $(var(fparam){param.value, SWID}); # } # # Uncomment the line below to use P-Call-Owner SIP header # value as the source NetworkId ($avp(snid)). # if (is_present_hf("P-Call-Owner")) { # $avp(snid) = $hdr(P-Call-Owner); # } # # Uncomment the line below to use P-Network-ID SIP header # value as the source NetworkId ($avp(snid)). if (is_present_hf("P-Network-ID")) { $avp(snid) = $hdr(P-Network-ID); } # Get custom info if (is_present_hf("P-Custom-Info")) { $avp(cinfo) = $hdr(P-Custom-Info); } # Get request Date header if (is_present_hf("Date")) { $avp(reqdate) = $hdr(Date); } else { setrequestdate(); if (is_avp_set("$avp(reqdate)")) { append_hf("Date: $avp(reqdate)\r\n"); } } # Get Identity header if (is_present_hf("Identity")) { $var(SIGNREG) = "/^\"(.*)\"$/\1/"; $avp(idsign) = $(hdr(Identity){s.select,0,;}{re.subst,$var(SIGNREG)}); $var(IDREG) = "/^[^;]*;//"; $var(idparam) = $(hdr(Identity){re.subst,$var(IDREG)}); $avp(idalg) = $(var(idparam){param.value,alg}); $avp(idinfo) = $(var(idparam){param.value,info}); $avp(idtype) = $(var(idparam){param.value,type}); $avp(idcanon) = $(var(idparam){param.value,canon}); } # Get source media address if ($(rb{sdp.line,c}) != NULL) { $avp(srcmedia) = $(rb{sdp.line,c}{s.substr,9,0}{s.select,0,/}); } # Get SDP finger print attributes $var(FPHEADER) = "a=fingerprint:"; $var(FPREG) = "/^a=fingerprint: *//"; $var(SPACEREG) = "/ / /g"; $var(i) = 0; $var(aline) = $(rb{sdp.line,a,$var(i)}); while ($(var(aline){s.len}) != 0) { $var(aheader) = $(var(aline){s.substr,0,$(var(FPHEADER){s.len})}); if ($var(aheader) == $var(FPHEADER)) { $avp(sdpfp) = $(var(aline){re.subst,$var(FPREG)}{re.subst,$var(SPACEREG)}); } $var(i) = $var(i) + 1; $var(aline) = $(rb{sdp.line,a,$var(i)}); } requestosprouting(); switch ($retcode) { # To change the OpenSIPS response for specific use cases, # edit the 'sl_send-reply...' lines in the cases above. # For example, to change the SIP response for a blocked # route from 403 to 404 change the line # sl_send_reply("403", "Forbidden - Call is blocked"); # to # sl_send_reply("404", "Forbidden - Call is blocked"); case 1: log(3, "Response received\n"); if (is_avp_set("$avp(idsign)") && is_avp_set("$avp(idalg)") && is_avp_set("$avp(idinfo)")) { $var(id) = "Identity: \"" + $avp(idsign) + "\";alg=" + $avp(idalg) + ";info=" + $avp(idinfo); if (is_avp_set("$avp(idtype)")) { $var(id) = $var(id) + ";type=" + $avp(idtype); } if (is_avp_set("$avp(idcanon)")) { $var(id) = $var(id) + ";canon=" + $avp(idcanon); } append_hf("$var(id)\r\n"); } if (checkosproute()) { # Now we have 3 options. # o route(3) - sends a redirect for all available # routes, or a CNAM response # o route(4) - fork off to all available routes # o route(5) in conjunction with failure_route and # branch_route - sequentially tries all routes # route(3); # route(4); route(5); } else { xlog("L_WARN", "----ROUTE: No supported destination for call ID '$ci'.\n"); sl_send_reply("404", "Route Not Found - No supported route"); } break; case -4000: # HTTP 400, missing OSP elements or bad message case -4001: # AuthRsp 400, not used xlog("L_WARN", "----ROUTE: Call to '$tU' from source device '$si' is a bad request.\n"); sl_send_reply("400", "Bad Request"); break; case -4010: # HTTP 401, invalid wire device case -4011: # AuthRsp 401, invalid transport or source device xlog("L_WARN", "----ROUTE: Call to '$tU' from source device '$si' is unauthorized on OSP Server.\n"); sl_send_reply("401", "Unauthorized"); break; case -4030: # Route blocked xlog("L_WARN", "----ROUTE: Call to '$tU' from source device '$si' is blocked on OSP Server.\n"); sl_send_reply("403", "Forbidden - Call is blocked"); break; case -4040: # Route not found xlog("L_WARN", "----ROUTE: No route on OSP server for call to '$tU' from source device '$si'.\n"); sl_send_reply("404", "Route Not Found"); break; case -4050: # Source may not originate xlog("L_WARN", "----ROUTE: Method not allowed on OSP server for call to '$tU' from source device '$si'.\n"); sl_send_reply("405", "Method Not Allowed"); break; case -4280: # Invalid calling number xlog("L_WARN", "----ROUTE: Calling number invalid for call to '$tU' from source device '$si'.\n"); sl_send_reply("400", "Bad Request - Calling number invalid"); break; case -4800: # OSPrey server not start case -4801: # OSPrey server timeout case -4802: # OSPrey server not ready xlog("L_WARN", "----ROUTE: Server not ready for call to '$tU' from source device '$si'.\n"); sl_send_reply("480", "Temporarily Unavailable - Server not ready"); break; case -4840: # Invalid called number xlog("L_WARN", "----ROUTE: Address incomplete for call to '$tU' from source device '$si'.\n"); sl_send_reply("484", "Address Incomplete"); break; case -5030: # Routing feature off, license exceeded or inbound device throttle xlog("L_WARN", "----ROUTE: Service not available for call to '$tU' from source device '$si'.\n"); sl_send_reply("503", "Service Not Available"); break; case -6030: # Fraud detected xlog("L_WARN", "----ROUTE: Call to '$tU' from source device '$si' is declined on OSP Server.\n"); sl_send_reply("603", "Decline"); break; case -2: # Internal error log(2, "----ROUTE: Internal server error\n"); sl_send_reply("500", "Internal Server Error"); break; default: # Unspecified server error log(2, "----ROUTE: OSP Authorization failed\n"); sl_send_reply("503", "Service Not Available"); } # } else { # log(3, "Wrong phone number\n"); # sl_send_reply("401", "Not Phone Number"); # } } # The SIP 300 message indicates multiple destinations are returned # for the call. The user agent can try one or more the destinations # to complete the call. # The SIP 301 message indicates that the called party has moved # permanently and cannot be found at the Request-URI address. A 301 # response would not be a expected response for an OSP query. # The SIP 302 message indicates the user agent SHOULD retry the request # at the new address(es) given in the redirect Contact header field. # # To change the SIP response for the route[3] use case, edit the # sl_send_reply... configuration. For example, to change the 300 # response to 302, change from # sl_send_reply("300", "Redirect"); # to # sl_send_reply("302", "Redirect"); route[3] { log(3, "Prepare response\n"); prepareospresponse(); switch ($retcode) { case 300: sl_send_reply("300", "Redirect"); break; case 380: if (is_avp_set("$avp(cnam)")) { append_to_reply("P-Asserted-Identity: $avp(cnam) <$fu>\r\n"); } else { append_to_reply("P-Asserted-Identity: $fu\r\n"); } sl_send_reply("380", "CNAM Lookup"); break; default: log(3, "Failed to prepare response\n"); sl_send_reply("500", "Internal Server Error"); } } route[4] { log(3, "Prepare all routes and fork-off\n"); if (prepareallosproutes()) { t_relay(); } else { log(3, "Failed to prepare all routes\n"); sl_send_reply("500", "Internal Server Error"); } } route[5] { log(3, "Try the 1st route\n"); t_on_branch("1"); t_on_reply("1"); t_on_failure("1"); # Reset fr_inv_timeout $T_fr_inv_timeout = null; # For SIP From header modification # route(7); if (!t_relay("0x02")) { xlog("L_WARN", "----ROUTE: t_relay failed.\n"); route(6); } } route[6] { log(3, "Try the next route\n"); if (checkosproute()) { t_on_branch("1"); t_on_reply("1"); t_on_failure("1"); # Reset fr_inv_timeout $T_fr_inv_timeout = null; if (!t_relay("0x02")) { xlog("L_WARN", "----ROUTE: t_relay failed.\n"); route(6); } } else { xlog("L_WARN", "----ROUTE: All destinations attempted for call ID '$ci'. Call cannot be completed.\n"); t_reply("503", "Service Not Available - Call cannot be completed"); } } # For SIP From header modification #route[7] { # log(3, "Remove otg parameter\n"); # # if ($(fu{param.exist, otg})) { # $avp(from) = $fu; # avp_subst("$avp(from)", "/(.*);otg=([^;]*)(.*)/\1\3/"); # xlog("L_INFO", "----ROUTE: New From URI = $avp(from)\n"); # uac_replace_from("$avp(from)"); # } else { # log(3, "Without otg parameter\n"); # } #} onreply_route[1] { # Get local egress address getlocaladdress(); if (t_check_status("180") || t_check_status("183")) { log(3, "180/183 received\n"); # Reset fr_inv_timeout $T_fr_inv_timeout = 60; } else if (t_check_status("200")) { # Get destination media address if ($(rb{sdp.line,c}) != NULL) { $avp(destmedia) = $(rb{sdp.line,c}{s.substr,9,0}{s.select,0,/}); } } } failure_route[1] { if (t_check_status("487")) { log(3, "Call canceled (status 487)\n"); return; } if (t_check_status("486")) { log(3, "User busy (status 486)\n"); return; } if (t_check_status("408")) { if (!t_local_replied("last")) { log(3, "User unavailable (status 408)\n"); return; } } route(6); } branch_route[1] { log(3, "Prepare route specific OSP information\n"); if (prepareosproute()) { # Only add/change Remote-Party-ID if calling number is translated if (checkcallingtranslation()) { log(3, "Calling number translated, add a new RPID header\n"); # Remove the Remote_Party-ID from the received message # Otherwise it will be forwarded on to the next hop remove_hf("Remote-Party-ID"); # Append a new Remote_Party append_rpid_hf(); } } else { drop(); } } opensips-2.2.2/modules/osp/globals.c000066400000000000000000000110421300170765700174210ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../../usr_avp.h" #include "osp_mod.h" int _osp_inbound_avpid = OSP_DEF_AVP; int _osp_origdest_avpid = OSP_DEF_AVP; int _osp_termdest_avpid = OSP_DEF_AVP; int _osp_calling_avpid = OSP_DEF_AVP; int _osp_work_mode = OSP_DEF_MODE; int _osp_service_type = OSP_DEF_SERVICE; unsigned int _osp_sp_number; char* _osp_sp_uris[OSP_DEF_SPS]; unsigned long _osp_sp_weights[OSP_DEF_SPS] = { OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT }; char* _osp_device_ip = NULL; char _osp_in_device[OSP_STRBUF_SIZE]; char _osp_out_device[OSP_STRBUF_SIZE]; int _osp_use_security = OSP_DEF_USESEC; char* _osp_private_key = NULL; char* _osp_local_certificate = NULL; char* _osp_ca_certificate = NULL; int _osp_crypto_hw = OSP_DEF_HW; int _osp_validate_callid = OSP_DEF_CALLID; int _osp_token_format = OSP_DEF_TOKEN; int _osp_ssl_lifetime = OSP_DEF_SSLLIFE; int _osp_persistence = OSP_DEF_PERSISTENCE; int _osp_retry_delay = OSP_DEF_DELAY; int _osp_retry_limit = OSP_DEF_RETRY; int _osp_timeout = OSP_DEF_TIMEOUT; int _osp_non_sip = OSP_DEF_NONSIP; int _osp_max_dests = OSP_DEF_DESTS; int _osp_report_nid = OSP_DEF_REPORTNID; int _osp_use_np = OSP_DEF_USENP; int _osp_append_userphone = OSP_DEF_USERPHONE; int _osp_dnid_location = OSP_DEF_DNIDLOC; char* _osp_dnid_param = OSP_DEF_DNIDPARAM; int _osp_swid_location = OSP_DEF_SWIDLOC; char* _osp_swid_param = OSP_DEF_SWIDPARAM; int _osp_paramstr_location = OSP_DEF_PARAMSTRLOC; char* _osp_paramstr_value = OSP_DEF_PARAMSTRVAL; char _osp_PRIVATE_KEY[OSP_STRBUF_SIZE]; char _osp_LOCAL_CERTIFICATE[OSP_STRBUF_SIZE]; char _osp_CA_CERTIFICATE[OSP_STRBUF_SIZE]; char* _osp_srcdev_avp = OSP_DEF_SNIDAVP; int_str _osp_srcdev_avpid; unsigned short _osp_srcdev_avptype; char* _osp_snid_avp = OSP_DEF_SNIDAVP; int_str _osp_snid_avpid; unsigned short _osp_snid_avptype; char* _osp_swid_avp = OSP_DEF_SWIDAVP; int_str _osp_swid_avpid; unsigned short _osp_swid_avptype; char* _osp_cinfo_avp = OSP_DEF_CINFOAVP; int_str _osp_cinfo_avpid; unsigned short _osp_cinfo_avptype; char* _osp_cnam_avp = OSP_DEF_CNAMAVP; int_str _osp_cnam_avpid; unsigned short _osp_cnam_avptype; char* _osp_extraheaders_value = OSP_DEF_EXTHEADERVAL; char* _osp_srcmedia_avp = OSP_DEF_SRCMEDIAAVP; int_str _osp_srcmedia_avpid; unsigned short _osp_srcmedia_avptype; char* _osp_destmedia_avp = OSP_DEF_DESTMEDIAAVP; int_str _osp_destmedia_avpid; unsigned short _osp_destmedia_avptype; char* _osp_reqdate_avp = OSP_DEF_REQDATEAVP; int_str _osp_reqdate_avpid; unsigned short _osp_reqdate_avptype; char* _osp_sdpfp_avp = OSP_DEF_SDPFPAVP; int_str _osp_sdpfp_avpid; unsigned short _osp_sdpfp_avptype; char* _osp_idsign_avp = OSP_DEF_IDSIGNAVP; int_str _osp_idsign_avpid; unsigned short _osp_idsign_avptype; char* _osp_idalg_avp = OSP_DEF_IDALGAVP; int_str _osp_idalg_avpid; unsigned short _osp_idalg_avptype; char* _osp_idinfo_avp = OSP_DEF_IDINFOAVP; int_str _osp_idinfo_avpid; unsigned short _osp_idinfo_avptype; char* _osp_idtype_avp = OSP_DEF_IDTYPEAVP; int_str _osp_idtype_avpid; unsigned short _osp_idtype_avptype; char* _osp_idcanon_avp = OSP_DEF_IDCANONAVP; int_str _osp_idcanon_avpid; unsigned short _osp_idcanon_avptype; OSPTPROVHANDLE _osp_provider = -1; opensips-2.2.2/modules/osp/orig_transaction.c000066400000000000000000001123461300170765700213540ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "../../dset.h" #include "../../usr_avp.h" #include "../../mem/mem.h" #include "../auth/api.h" #include "orig_transaction.h" #include "destination.h" #include "osptoolkit.h" #include "sipheader.h" #include "timeapi.h" #include "usage.h" #ifndef timersub #define timersub(a, b, result) \ { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ --(result)->tv_sec; \ (result)->tv_usec += 1000000; \ } \ } #endif extern int _osp_calling_avpid; extern int _osp_service_type; extern char _osp_in_device[]; extern char _osp_out_device[]; extern int _osp_non_sip; extern int _osp_max_dests; extern int _osp_snid_avpid; extern unsigned short _osp_snid_avptype; extern int _osp_swid_avpid; extern unsigned short _osp_swid_avptype; extern int _osp_cinfo_avpid; extern unsigned short _osp_cinfo_avptype; extern int _osp_cnam_avpid; extern unsigned short _osp_cnam_avptype; extern int _osp_srcmedia_avpid; extern unsigned short _osp_srcmedia_avptype; extern int _osp_reqdate_avpid; extern unsigned short _osp_reqdate_avptype; extern int _osp_sdpfp_avpid; extern unsigned short _osp_sdpfp_avptype; extern int _osp_idsign_avpid; extern unsigned short _osp_idsign_avptype; extern int _osp_idalg_avpid; extern unsigned short _osp_idalg_avptype; extern int _osp_idinfo_avpid; extern unsigned short _osp_idinfo_avptype; extern int _osp_idtype_avpid; extern unsigned short _osp_idtype_avptype; extern int _osp_idcanon_avpid; extern unsigned short _osp_idcanon_avptype; extern OSPTPROVHANDLE _osp_provider; extern auth_api_t osp_auth; const int OSP_FIRST_ROUTE = 1; const int OSP_NEXT_ROUTE = 0; const int OSP_MAIN_ROUTE = 1; const int OSP_BRANCH_ROUTE = 0; static int ospSetIdentity(OSPTTRANHANDLE trans); static int ospReportIdentity(OSPTTRANHANDLE trans); static int ospLoadRoutes(OSPTTRANHANDLE trans, int destcount, osp_inbound* inbound); static int ospPrepareDestination(struct sip_msg* msg, int isfirst, int route, int response, int* rsptype); static int ospSetCalling(struct sip_msg* msg, osp_inbound* inbound, osp_dest* dest); /* * Set Identity header * param trans Transaction handle * return 0 success, -1 failure */ static int ospSetIdentity( OSPTTRANHANDLE trans) { char encoded[OSP_SIGNBUF_SIZE]; unsigned encodedsize = sizeof(encoded); unsigned char sign[OSP_SIGNBUF_SIZE]; unsigned signsize = sizeof(sign); char alg[OSP_ALGBUF_SIZE]; char info[OSP_STRBUF_SIZE]; char type[OSP_STRBUF_SIZE]; unsigned char canon[OSP_STRBUF_SIZE]; unsigned canonsize = sizeof(canon); str value; int result = -1; if (OSPPTransactionGetIdentity(trans, &signsize, sign, sizeof(alg), alg, sizeof(info), info, sizeof(type), type, &canonsize, canon) == OSPC_ERR_NO_ERROR) { if (signsize != 0) { if (OSPPBase64Encode(sign, signsize, (unsigned char*)encoded, &encodedsize) == OSPC_ERR_NO_ERROR) { value.s = encoded; value.len = encodedsize; add_avp(_osp_idsign_avptype | AVP_VAL_STR, _osp_idsign_avpid, (int_str)value); } } if (alg[0] != '\0') { value.s = alg; value.len = strlen(alg); add_avp(_osp_idalg_avptype | AVP_VAL_STR, _osp_idalg_avpid, (int_str)value); } if (info[0] != '\0') { value.s = info; value.len = strlen(info); add_avp(_osp_idinfo_avptype | AVP_VAL_STR, _osp_idinfo_avpid, (int_str)value); } if (type[0] != '\0') { value.s = type; value.len = strlen(type); add_avp(_osp_idtype_avptype | AVP_VAL_STR, _osp_idtype_avpid, (int_str)value); } if (canonsize != 0) { if (OSPPBase64Encode(canon, canonsize, (unsigned char*)encoded, &encodedsize) == OSPC_ERR_NO_ERROR) { value.s = encoded; value.len = encodedsize; add_avp(_osp_idcanon_avptype | AVP_VAL_STR, _osp_idcanon_avpid, (int_str)value); } } result = 0; } return result; } /* * Report Identity header * param trans Transaction handle * return 0 success, -1 failure */ static int ospReportIdentity( OSPTTRANHANDLE trans) { char encoded[OSP_SIGNBUF_SIZE]; unsigned signsize; unsigned char sign[OSP_SIGNBUF_SIZE]; char alg[OSP_ALGBUF_SIZE]; char info[OSP_STRBUF_SIZE]; char type[OSP_STRBUF_SIZE]; unsigned canonsize; unsigned char canon[OSP_STRBUF_SIZE]; int result = 0; if (ospGetAVP(_osp_idsign_avpid, _osp_idsign_avptype, encoded, sizeof(encoded)) == 0) { signsize = sizeof(sign); if (OSPPBase64Decode(encoded, strlen(encoded), sign, &signsize) != OSPC_ERR_NO_ERROR) { signsize = 0; } } else { signsize = 0; } if (ospGetAVP(_osp_idalg_avpid, _osp_idalg_avptype, alg, sizeof(alg)) != 0) { alg[0] = '\0'; } if (ospGetAVP(_osp_idinfo_avpid, _osp_idinfo_avptype, info, sizeof(info)) != 0) { info[0] = '\0'; } if (ospGetAVP(_osp_idtype_avpid, _osp_idtype_avptype, type, sizeof(type)) != 0) { type[0] = '\0'; } if (ospGetAVP(_osp_idcanon_avpid, _osp_idcanon_avptype, encoded, sizeof(encoded)) == 0) { canonsize = sizeof(canon); if (OSPPBase64Decode(encoded, strlen(encoded), canon, &canonsize) != OSPC_ERR_NO_ERROR) { canonsize = 0; } } else { canonsize = 0; } if (OSPPTransactionSetIdentity(trans, signsize, sign, alg, info, type, canonsize, canon) != OSPC_ERR_NO_ERROR) { result = -1; } return result; } /* * Get routes from AuthRsp * param transaction Transaction handle * param destcount Expected destination count * param inbound Inbound info * return 0 success, -1 failure */ static int ospLoadRoutes( OSPTTRANHANDLE trans, int destcount, osp_inbound* inbound) { int count; int errcode; osp_dest* dest; osp_dest dests[OSP_DEF_DESTS]; char host[OSP_STRBUF_SIZE]; char destdev[OSP_STRBUF_SIZE]; OSPE_OPERATOR_NAME type; OSPE_DEST_OSPENABLED enabled; int result = 0; ospSetIdentity(trans); for (count = 0; count < destcount; count++) { /* This is necessary because we will save destinations in reverse order */ dest = ospInitDestination(&dests[count]); if (dest == NULL) { result = -1; break; } dest->destcount = count + 1; if (count == 0) { errcode = OSPPTransactionGetFirstDestination( trans, sizeof(dest->validafter), dest->validafter, dest->validuntil, &dest->timelimit, &dest->callidsize, (void*)dest->callid, sizeof(dest->called), dest->called, sizeof(dest->calling), dest->calling, sizeof(host), host, sizeof(destdev), destdev, &dest->tokensize, dest->token); } else { errcode = OSPPTransactionGetNextDestination( trans, 0, sizeof(dest->validafter), dest->validafter, dest->validuntil, &dest->timelimit, &dest->callidsize, (void*)dest->callid, sizeof(dest->called), dest->called, sizeof(dest->calling), dest->calling, sizeof(host), host, sizeof(destdev), destdev, &dest->tokensize, dest->token); } if (errcode != OSPC_ERR_NO_ERROR) { LM_ERR("failed to load routes (%d) expected '%d' current '%d'\n", errcode, destcount, count); result = -1; break; } ospConvertToInAddress(host, dest->host, sizeof(dest->host)); errcode = OSPPTransactionGetNumberPortabilityParameters(trans, sizeof(dest->nprn), dest->nprn, sizeof(dest->npcic), dest->npcic, &dest->npdi); if (errcode != OSPC_ERR_NO_ERROR) { LM_DBG("cannot get number portability parameters (%d)\n", errcode); dest->nprn[0] = '\0'; dest->npcic[0] = '\0'; dest->npdi = 0; } for (type = OSPC_OPNAME_START; type < OSPC_OPNAME_NUMBER; type++) { errcode = OSPPTransactionGetOperatorName(trans, type, sizeof(dest->opname[type]), dest->opname[type]); if (errcode != OSPC_ERR_NO_ERROR) { LM_DBG("cannot get operator name '%d' (%d)\n", type, errcode); dest->opname[type][0] = '\0'; } } errcode = OSPPTransactionGetDestProtocol(trans, &dest->protocol); if (errcode != OSPC_ERR_NO_ERROR) { /* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */ LM_DBG("cannot get dest protocol (%d)\n", errcode); dest->protocol = OSPC_PROTNAME_SIP; } switch (dest->protocol) { case OSPC_PROTNAME_Q931: case OSPC_PROTNAME_LRQ: case OSPC_PROTNAME_IAX: case OSPC_PROTNAME_T37: case OSPC_PROTNAME_T38: case OSPC_PROTNAME_SKYPE: case OSPC_PROTNAME_SMPP: case OSPC_PROTNAME_XMPP: if (_osp_non_sip) { dest->supported = 1; } else { dest->supported = 0; } break; case OSPC_PROTNAME_SIP: case OSPC_PROTNAME_UNDEFINED: case OSPC_PROTNAME_UNKNOWN: default: dest->supported = 1; break; } errcode = OSPPTransactionIsDestOSPEnabled(trans, &enabled); if (errcode != OSPC_ERR_NO_ERROR) { /* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */ LM_DBG("cannot get dest OSP version (%d)\n", errcode); } else if (enabled == OSPC_DOSP_FALSE) { /* Destination device does not support OSP. Do not send token to it */ dest->token[0] = '\0'; dest->tokensize = 0; } errcode = OSPPTransactionGetDestinationNetworkId(trans, sizeof(dest->dnid), dest->dnid); if (errcode != OSPC_ERR_NO_ERROR) { /* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */ LM_DBG("cannot get dest network ID (%d)\n", errcode); dest->dnid[0] = '\0'; } errcode = OSPPTransactionGetDestSwitchId(trans, sizeof(dest->swid), dest->swid); if (errcode != OSPC_ERR_NO_ERROR) { /* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */ LM_DBG("cannot get dest switch ID (%d)\n", errcode); dest->swid[0] = '\0'; } errcode = OSPPTransactionGetCNAM(trans, sizeof(dest->cnam), dest->cnam); if (errcode != OSPC_ERR_NO_ERROR) { LM_DBG("cannot get CNAM (%d)\n", errcode); dest->cnam[0] = '\0'; } OSPPTransactionGetServiceType(trans, &dest->srvtype); dest->type = OSPC_ROLE_SOURCE; dest->transid = ospGetTransactionId(trans); LM_INFO("get destination '%d': " "validafter '%s' " "validuntil '%s' " "timelimit '%d' seconds " "callid '%.*s' " "calling '%s' " "called '%s' " "host '%s' " "nprn '%s' " "npcic '%s' " "npdi '%d' " /* "spid '%s' " "ocn '%s' " "spn '%s' " "altspn '%s' " "mcc '%s' " "mnc '%s' " */ "cnam '%s' " "service '%d' " "protocol '%d' " "supported '%d' " "networkid '%s' " "switchid '%s' " "tokensize '%d'\n", count, dest->validafter, dest->validuntil, dest->timelimit, dest->callidsize, dest->callid, dest->calling, dest->called, host, dest->nprn, dest->npcic, dest->npdi, /* dest->opname[OSPC_OPNAME_SPID], dest->opname[OSPC_OPNAME_OCN], dest->opname[OSPC_OPNAME_SPN], dest->opname[OSPC_OPNAME_ALTSPN], dest->opname[OSPC_OPNAME_MCC], dest->opname[OSPC_OPNAME_MNC], */ dest->cnam, dest->srvtype, dest->protocol, dest->supported, dest->dnid, dest->swid, dest->tokensize); } /* * Save destination in reverse order, * when we start searching avps the destinations * will be in order */ if (result == 0) { if (ospSaveInboundInfo(inbound) == -1) { ospRecordEvent(0, 500); result = -1; } else { for(count = destcount -1; count >= 0; count--) { if (ospSaveOrigDestination(&dests[count]) == -1) { LM_ERR("failed to save originate destination\n"); /* Report terminate CDR */ ospRecordEvent(0, 500); result = -1; break; } } } } return result; } /* * Request OSP authorization and routeing * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure, others error */ int ospRequestRouting( struct sip_msg* msg, char* ignore1, char* ignore2) { int i, errcode; char rn[OSP_STRBUF_SIZE]; char cic[OSP_STRBUF_SIZE]; int npdi; OSPE_OPERATOR_NAME type; char opname[OSPC_OPNAME_NUMBER][OSP_STRBUF_SIZE]; osp_inbound inbound; char sourcebuf[OSP_STRBUF_SIZE]; char srcdevbuf[OSP_STRBUF_SIZE]; char divhostbuf[OSP_STRBUF_SIZE]; char useragent[OSP_STRBUF_SIZE]; struct usr_avp* avp = NULL; int_str avpval; unsigned int cinfonum = 0; char cinfo[OSP_DEF_CINFONUM][OSP_STRBUF_SIZE]; char cinfostr[OSP_STRBUF_SIZE]; unsigned int callidnumber = 1; OSPT_CALL_ID* callids[callidnumber]; unsigned int logsize = 0; char* detaillog = NULL; char tohost[OSP_STRBUF_SIZE]; char tohostbuf[OSP_STRBUF_SIZE]; const char* preferred[2] = { NULL }; unsigned int destcount; struct timeval ts, te, td; char datebuf[OSP_STRBUF_SIZE]; unsigned int sdpfpnum = 0; char sdpfp[OSP_DEF_SDPFPNUM][OSP_STRBUF_SIZE]; char* sdpfpstr[OSP_DEF_SDPFPNUM]; OSPTTRANHANDLE trans = -1; int result = MODULE_RETURNCODE_FALSE; ospInitInboundInfo(&inbound); if ((errcode = OSPPTransactionNew(_osp_provider, &trans)) != OSPC_ERR_NO_ERROR) { LM_ERR("failed to create new OSP transaction (%d)\n", errcode); } else if (ospGetCallId(msg, &(callids[0])) != 0) { LM_ERR("failed to extract call id\n"); } else if (ospGetFromUser(msg, inbound.calling, sizeof(inbound.calling)) != 0) { LM_ERR("failed to extract calling number\n"); } else if ((ospGetUriUser(msg, inbound.called, sizeof(inbound.called)) != 0) && (ospGetToUser(msg, inbound.called, sizeof(inbound.called)) != 0)) { LM_ERR("failed to extract called number\n"); } else if (ospGetSource(msg, inbound.source, sizeof(inbound.source)) != 0) { LM_ERR("failed to extract source address\n"); } else if (ospGetSrcDev(msg, inbound.srcdev, sizeof(inbound.srcdev)) != 0) { LM_ERR("failed to extract source deivce address\n"); } else { inbound.authtime = time(NULL); if(msg->rcv.bind_address && msg->rcv.bind_address->address_str.s) { ospCopyStrToBuffer(&msg->rcv.bind_address->address_str, inbound.ingressaddr, sizeof(inbound.ingressaddr)); } ospConvertToOutAddress(inbound.source, sourcebuf, sizeof(sourcebuf)); ospConvertToOutAddress(inbound.srcdev, srcdevbuf, sizeof(srcdevbuf)); switch (_osp_service_type) { case 1: case 2: OSPPTransactionSetServiceType(trans, (_osp_service_type == 1) ? OSPC_SERVICE_NPQUERY : OSPC_SERVICE_CNAMQUERY); ospGetToHost(msg, tohost, sizeof(tohost)); ospConvertToOutAddress(tohost, tohostbuf, sizeof(tohostbuf)); preferred[0] = tohostbuf; destcount = 1; break; case 0: default: OSPPTransactionSetServiceType(trans, OSPC_SERVICE_VOICE); destcount = _osp_max_dests; break; } if (ospGetNpParam(msg, rn, sizeof(rn), cic, sizeof(cic), &npdi) == 0) { OSPPTransactionSetNumberPortability(trans, rn, cic, npdi); } for (type = OSPC_OPNAME_START; type < OSPC_OPNAME_NUMBER; type++) { if (ospGetOperatorName(msg, type, opname[type], sizeof(opname[type])) == 0) { OSPPTransactionSetOperatorName(trans, type, opname[type]); } } if (ospGetFromDisplay(msg, inbound.fromdisplay, sizeof(inbound.fromdisplay)) == 0) { OSPPTransactionSetSIPHeader(trans, OSPC_SIPHEADER_FROM, OSPC_NFORMAT_DISPLAYNAME, inbound.fromdisplay); } if (ospGetFromUri(msg, inbound.fromuri, sizeof(inbound.fromuri)) == 0) { OSPPTransactionSetSIPHeader(trans, OSPC_SIPHEADER_FROM, OSPC_NFORMAT_URL, inbound.fromuri); } if (ospGetToUri(msg, inbound.touri, sizeof(inbound.touri)) == 0) { OSPPTransactionSetSIPHeader(trans, OSPC_SIPHEADER_TO, OSPC_NFORMAT_URL, inbound.touri); } if (ospGetRpidUser(msg, inbound.rpiduser, sizeof(inbound.rpiduser)) == 0) { OSPPTransactionSetSIPHeader(trans, OSPC_SIPHEADER_RPID, OSPC_NFORMAT_E164, inbound.rpiduser); } if (ospGetPaiUser(msg, inbound.paiuser, sizeof(inbound.paiuser)) == 0) { OSPPTransactionSetSIPHeader(trans, OSPC_SIPHEADER_PAI, OSPC_NFORMAT_E164, inbound.paiuser); } if (ospGetPciUser(msg, inbound.pciuser, sizeof(inbound.pciuser)) == 0) { OSPPTransactionSetSIPHeader(trans, OSPC_SIPHEADER_PCI, OSPC_NFORMAT_E164, inbound.pciuser); } if (ospGetDiversion(msg, inbound.divuser, sizeof(inbound.divuser), inbound.divhost, sizeof(inbound.divhost)) == 0) { ospConvertToOutAddress(inbound.divhost, divhostbuf, sizeof(divhostbuf)); } else { divhostbuf[0] = '\0'; } OSPPTransactionSetDiversion(trans, inbound.divuser, divhostbuf); if (ospGetPcvIcid(msg, inbound.pcvicid, sizeof(inbound.pcvicid)) == 0) { OSPPTransactionSetChargingVector(trans, inbound.pcvicid, NULL, NULL, NULL); } if (ospGetUserAgent(msg, useragent, sizeof(useragent)) == 0) { OSPPTransactionSetUserAgent(trans, useragent); } OSPPTransactionSetProtocol(trans, OSPC_PROTTYPE_SOURCE, OSPC_PROTNAME_SIP); if (ospGetAVP(_osp_snid_avpid, _osp_snid_avptype, inbound.snid, sizeof(inbound.snid)) == 0) { OSPPTransactionSetNetworkIds(trans, inbound.snid, ""); } else { inbound.snid[0] = '\0'; } if (ospGetAVP(_osp_swid_avpid, _osp_swid_avptype, inbound.swid, sizeof(inbound.swid)) == 0) { OSPPTransactionSetSrcSwitchId(trans, inbound.swid); } else { inbound.swid[0] = '\0'; } if (_osp_cinfo_avpid >= 0) { for (i = 0, avp = search_first_avp(_osp_cinfo_avptype, _osp_cinfo_avpid, NULL, 0); ((i < OSP_DEF_CINFONUM) && (avp != NULL)); i++, avp = search_next_avp(avp, NULL)) { get_avp_val(avp, &avpval); if ((avp->flags & AVP_VAL_STR) && (avpval.s.s && avpval.s.len)) { snprintf(cinfo[i], sizeof(cinfo[i]), "%.*s", avpval.s.len, avpval.s.s); } else { cinfo[i][0] = '\0'; } } cinfonum = i; cinfostr[0] = '\0'; for (i = 0; i < cinfonum; i++) { if (cinfo[cinfonum - i - 1][0] != '\0') { OSPPTransactionSetCustomInfo(trans, i, cinfo[cinfonum - i - 1]); snprintf(cinfostr + strlen(cinfostr), sizeof(cinfostr) - strlen(cinfostr), "custom_info%d '%s' ", i + 1, cinfo[cinfonum - i - 1]); } } } if (ospGetAVP(_osp_srcmedia_avpid, _osp_srcmedia_avptype, inbound.srcmedia, sizeof(inbound.srcmedia)) == 0) { OSPPTransactionSetSrcAudioAddr(trans, inbound.srcmedia); } else { inbound.srcmedia[0] = '\0'; } inbound.date = 0; if (ospGetAVP(_osp_reqdate_avpid, _osp_reqdate_avptype, datebuf, sizeof(datebuf)) == 0) { if (ospStrToTime(datebuf, &inbound.date) == 0) { OSPPTransactionSetRequestDate(trans, inbound.date); } } if (_osp_sdpfp_avpid >= 0) { for (i = 0, avp = search_first_avp(_osp_sdpfp_avptype, _osp_sdpfp_avpid, NULL, 0); ((i < OSP_DEF_SDPFPNUM) && (avp != NULL)); i++, avp = search_next_avp(avp, NULL)) { get_avp_val(avp, &avpval); if ((avp->flags & AVP_VAL_STR) && (avpval.s.s && avpval.s.len)) { snprintf(sdpfp[i], sizeof(sdpfp[i]), "%.*s", avpval.s.len, avpval.s.s); } else { sdpfp[i][0] = '\0'; } } sdpfpnum = i; for (i = 0; i < sdpfpnum; i++) { sdpfpstr[i] = sdpfp[sdpfpnum - i - 1]; } OSPPTransactionSetFingerPrint(trans, sdpfpnum, (const char**)sdpfpstr); } ospReportIdentity(trans); LM_INFO("request auth and routing for: " "service '%d' " "source '%s' " "srcdev '%s' " "snid '%s' " "swid '%s' " "calling '%s' " "called '%s' " "preferred '%s' " "nprn '%s' " "npcic '%s' " "npdi '%d' " /* "spid '%s' " "ocn '%s' " "spn '%s' " "altspn '%s' " "mcc '%s' " "mnc '%s' " */ "fromdisplay '%s' " "paiuser '%s' " "rpiduser '%s' " "pciuser '%s' " "divuser '%s' " "divhost '%s' " "pcvicid '%s' " "srcmedia '%s' " "callid '%.*s' " "destcount '%d' " "%s\n", _osp_service_type, sourcebuf, srcdevbuf, inbound.snid, inbound.swid, inbound.calling, inbound.called, (preferred[0] == NULL) ? "" : preferred[0], rn, cic, npdi, /* opname[OSPC_OPNAME_SPID], opname[OSPC_OPNAME_OCN], opname[OSPC_OPNAME_SPN], opname[OSPC_OPNAME_ALTSPN], opname[OSPC_OPNAME_MCC], opname[OSPC_OPNAME_MNC], */ inbound.fromdisplay, inbound.paiuser, inbound.rpiduser, inbound.pciuser, inbound.divuser, divhostbuf, inbound.pcvicid, inbound.srcmedia, callids[0]->Length, callids[0]->Value, destcount, cinfostr); gettimeofday(&ts, NULL); /* try to request authorization */ errcode = OSPPTransactionRequestAuthorisation( trans, /* transaction handle */ sourcebuf, /* from the configuration file */ srcdevbuf, /* source device of call, protocol specific, in OSP format */ inbound.calling, /* calling number in nodotted e164 notation */ OSPC_NFORMAT_E164, /* calling number format */ inbound.called, /* called number */ OSPC_NFORMAT_E164, /* called number format */ "", /* optional username string, used if no number */ callidnumber, /* number of call ids, here always 1 */ callids, /* sized-1 array of call ids */ preferred, /* preferred destinations */ &destcount, /* max destinations, after call dest_count */ &logsize, /* size allocated for detaillog (next param) 0=no log */ detaillog); /* memory location for detaillog to be stored */ gettimeofday(&te, NULL); timersub(&te, &ts, &td); LM_INFO("authreq cost = %lu.%06lu for call-id '%.*s'\n", td.tv_sec, td.tv_usec, callids[0]->Length, callids[0]->Value); if ((errcode == OSPC_ERR_NO_ERROR) && (ospLoadRoutes(trans, destcount, &inbound) == 0)) { LM_INFO("there are '%d' OSP routes, call_id '%.*s'\n", destcount, callids[0]->Length, callids[0]->Value); result = MODULE_RETURNCODE_TRUE; } else { LM_ERR("failed to request auth and routing (%d), call_id '%.*s'\n", errcode, callids[0]->Length, callids[0]->Value); switch (errcode) { case OSPC_ERR_HTTP_BAD_REQUEST: result = -4000; break; case OSPC_ERR_TRAN_BAD_REQUEST: result = -4001; break; case OSPC_ERR_HTTP_UNAUTHORIZED: result = -4010; break; case OSPC_ERR_TRAN_UNAUTHORIZED: result = -4011; break; case OSPC_ERR_TRAN_ROUTE_BLOCKED: result = -4030; break; case OSPC_ERR_TRAN_ROUTE_NOT_FOUND: result = -4040; break; case OSPC_ERR_TRAN_MAY_NOT_ORIGINATE: result = -4050; break; case OSPC_ERR_TRAN_CALLING_INVALID: result = -4280; break; case OSPC_ERR_SOCK_CONNECT_FAILED: result = -4800; break; case OSPC_ERR_SOCK_SELECT_FAILED: result = -4801; break; case OSPC_ERR_HTTP_SERVER_NOT_READY: result = -4802; break; case OSPC_ERR_TRAN_CALLED_FILTERING: result = -4840; break; case OSPC_ERR_HTTP_SERVICE_UNAVAILABLE: result = -5030; break; case OSPC_ERR_TRAN_DECLINE: result = -6030; break; case OSPC_ERR_NO_ERROR: /* AuthRsp ok but ospLoadRoutes fails */ result = MODULE_RETURNCODE_ERROR; break; default: result = MODULE_RETURNCODE_FALSE; break; } } } if (callids[0] != NULL) { OSPPCallIdDelete(&(callids[0])); } if (trans != -1) { OSPPTransactionDelete(trans); } return result; } /* * Check if there is a route * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospCheckRoute( struct sip_msg* msg, char* ignore1, char* ignore2) { if (ospCheckOrigDestination() == 0) { return MODULE_RETURNCODE_TRUE; } else { return MODULE_RETURNCODE_FALSE; } } /* * Set calling number if translated * param msg SIP message * param inbound Inbound info * param dest Destination structure * return 0 success, 1 calling number same or not support calling number translation, -1 failure */ static int ospSetCalling( struct sip_msg* msg, osp_inbound* inbound, osp_dest* dest) { str rpid; int_str val; char buffer[OSP_STRBUF_SIZE]; int result; if (strcmp(inbound->calling, dest->calling) == 0) { LM_DBG("calling number does not been translated\n"); result = 1; } else if (osp_auth.rpid_avp < 0) { LM_WARN("rpid_avp is not found, cannot set rpid avp\n"); result = -1; } else { snprintf(buffer, sizeof(buffer), "\"%s\" ", dest->calling, dest->calling, inbound->source); rpid.s = buffer; rpid.len = strlen(buffer); add_avp(osp_auth.rpid_avp_type | AVP_VAL_STR, osp_auth.rpid_avp, (int_str)rpid); result = 0; } if (result == 0) { val.n = 1; } else { val.n = 0; } add_avp(0, _osp_calling_avpid, val); return result; } /* * Check if the calling number is translated. * This function checks the avp set by ospPrepareDestination. * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE calling number translated MODULE_RETURNCODE_FALSE without transaltion */ int ospCheckCalling( struct sip_msg* msg, char* ignore1, char* ignore2) { int_str callingval; int result = MODULE_RETURNCODE_FALSE; if (search_first_avp(0, _osp_calling_avpid, &callingval, 0) != NULL) { if (callingval.n == 0) { LM_DBG("the calling number does not been translated\n"); } else { LM_DBG("the calling number is translated\n"); result = MODULE_RETURNCODE_TRUE; } } else { LM_ERR("there is not calling translation avp\n"); } return result; } /* * Build SIP message for destination * param msg SIP message * param isfirst Is first destination * param route Main or branch route block * param response Is for response * param rsptype SIP response type * return MODULE_RETURNCODE_TRUE success MODULE_RETURNCODE_FALSE failure */ static int ospPrepareDestination( struct sip_msg* msg, int isfirst, int route, int response, int* rsptype) { str cnam; char buffer[OSP_HEADERBUF_SIZE]; str newuri = { buffer, sizeof(buffer) }; osp_inbound* inbound = ospGetInboundInfo(); osp_dest* dest = ospGetNextOrigDestination(); int result = MODULE_RETURNCODE_TRUE; if (inbound != NULL) { if (dest != NULL) { if (response) { /* SIP 300 or 380 response */ if (route == OSP_MAIN_ROUTE) { if (dest->srvtype == OSPC_SERVICE_CNAMQUERY) { LM_INFO("prepare CNAM for call_id '%.*s' transaction_id '%llu'\n", dest->callidsize, dest->callid, dest->transid); if (dest->cnam[0] != '\0') { cnam.s = dest->cnam; cnam.len = strlen(dest->cnam); add_avp(_osp_cnam_avptype | AVP_VAL_STR, _osp_cnam_avpid, (int_str)cnam); } dest->lastcode = 380; *rsptype = 380; } else { /* For default service, voice service or ported number query service */ ospRebuildDestUri(&newuri, dest); LM_INFO("prepare route to URI '%.*s' for call_id '%.*s' transaction_id '%llu'\n", newuri.len, newuri.s, dest->callidsize, dest->callid, dest->transid); if (isfirst == OSP_FIRST_ROUTE) { set_ruri(msg, &newuri); } else { append_branch(msg, &newuri, NULL, NULL, Q_UNSPECIFIED, 0, NULL); } /* Do not add route specific OSP information */ dest->lastcode = 300; *rsptype = 300; } } else { LM_ERR("unsupported route block type\n"); result = MODULE_RETURNCODE_FALSE; } } else { /* Single destination or all destinations */ ospRebuildDestUri(&newuri, dest); LM_INFO("prepare route to URI '%.*s' for call_id '%.*s' transaction_id '%llu'\n", newuri.len, newuri.s, dest->callidsize, dest->callid, dest->transid); if (route == OSP_MAIN_ROUTE) { if (isfirst == OSP_FIRST_ROUTE) { set_ruri(msg, &newuri); } else { append_branch(msg, &newuri, NULL, NULL, Q_UNSPECIFIED, 0, NULL); } /* Do not add route specific OSP information */ } else if (route == OSP_BRANCH_ROUTE) { /* For branch route, add route specific OSP information */ /* Update the Request-Line */ set_ruri(msg, &newuri); /* Add OSP token header */ ospAddOspToken(msg, dest->token, dest->tokensize); /* Add branch-specific OSP Cookie */ ospRecordOrigTransaction(msg, inbound, dest); /* Handle calling number translation */ ospSetCalling(msg, inbound, dest); /* Set call attempt start time */ dest->starttime = time(NULL); } else { LM_ERR("unsupported route block type\n"); result = MODULE_RETURNCODE_FALSE; } } } else { LM_DBG("there is no more routes\n"); if (!response) { ospReportOrigSetupUsage(); } result = MODULE_RETURNCODE_FALSE; } } else { LM_ERR("internal error\n"); result = MODULE_RETURNCODE_FALSE; } return result; } /* * Prepare OSP route * This function only works in branch route block. * This function is only for OpenSIPS. * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospPrepareRoute( struct sip_msg* msg, char* ignore1, char* ignore2) { int tmp = 0; int result = MODULE_RETURNCODE_TRUE; /* The first parameter will be ignored */ result = ospPrepareDestination(msg, OSP_FIRST_ROUTE, OSP_BRANCH_ROUTE, 0, &tmp); return result; } /* * Prepare response * This function does not work in branch route block. * param msg SIP message * param ignore1 * param ignore2 * return 300 or 380 success, MODULE_RETURNCODE_FALSE failure */ int ospPrepareResponse( struct sip_msg* msg, char* ignore1, char* ignore2) { int tmp; int rsptype = 0; int result = MODULE_RETURNCODE_TRUE; for(result = ospPrepareDestination(msg, OSP_FIRST_ROUTE, OSP_MAIN_ROUTE, 1, &rsptype); result == MODULE_RETURNCODE_TRUE; result = ospPrepareDestination(msg, OSP_NEXT_ROUTE, OSP_MAIN_ROUTE, 1, &tmp)) { } return rsptype; } /* * Prepare all OSP routes * This function does not work in branch route block. * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospPrepareAllRoutes( struct sip_msg* msg, char* ignore1, char* ignore2) { int tmp = 0; int result = MODULE_RETURNCODE_TRUE; for(result = ospPrepareDestination(msg, OSP_FIRST_ROUTE, OSP_MAIN_ROUTE, 0, &tmp); result == MODULE_RETURNCODE_TRUE; result = ospPrepareDestination(msg, OSP_NEXT_ROUTE, OSP_MAIN_ROUTE, 0, &tmp)) { } return MODULE_RETURNCODE_TRUE; } opensips-2.2.2/modules/osp/orig_transaction.h000066400000000000000000000032731300170765700213570ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_ORIG_TRANSACTION_H_ #define _OSP_MOD_ORIG_TRANSACTION_H_ #include "../../parser/msg_parser.h" #include "osp_mod.h" int ospRequestRouting(struct sip_msg*, char*, char*); int ospCheckRoute(struct sip_msg*, char*, char*); int ospPrepareRoute(struct sip_msg*, char*, char*); int ospPrepareResponse(struct sip_msg*, char*, char*); int ospPrepareAllRoutes(struct sip_msg*, char*, char*); int ospCheckCalling(struct sip_msg*, char*, char*); #endif /* _OSP_MOD_ORIG_TRANSACTION_H_ */ opensips-2.2.2/modules/osp/osp_mod.c000066400000000000000000000542611300170765700174500ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2006-03-13 RR functions are loaded via API function (bogdan) */ #include #include "../../mod_fix.h" #include "../../sr_module.h" #include "../rr/api.h" #include "../auth/api.h" #include "osp_mod.h" #include "destination.h" #include "orig_transaction.h" #include "term_transaction.h" #include "usage.h" #include "signaling.h" #include "tm.h" #include "provider.h" #include "sipheader.h" #include "cnam.h" extern int _osp_work_mode; extern int _osp_service_type; extern unsigned int _osp_sp_number; extern char* _osp_sp_uris[]; extern unsigned long _osp_sp_weights[]; extern char* _osp_device_ip; extern char _osp_in_device[OSP_STRBUF_SIZE]; extern char _osp_out_device[OSP_STRBUF_SIZE]; extern int _osp_use_security; extern char* _osp_private_key; extern char* _osp_local_certificate; extern char* _osp_ca_certificate; extern int _osp_crypto_hw; extern int _osp_validate_callid; extern int _osp_token_format; extern int _osp_ssl_lifetime; extern int _osp_persistence; extern int _osp_retry_delay; extern int _osp_retry_limit; extern int _osp_timeout; extern int _osp_non_sip; extern int _osp_max_dests; extern int _osp_report_nid; extern int _osp_use_np; extern int _osp_append_userphone; extern int _osp_dnid_location; extern char* _osp_dnid_param; extern int _osp_swid_location; extern char* _osp_swid_param; extern int _osp_paramstr_location; extern char* _osp_paramstr_value; extern char _osp_PRIVATE_KEY[]; extern char _osp_LOCAL_CERTIFICATE[]; extern char _osp_CA_CERTIFICATE[]; extern char* _osp_srcdev_avp; extern int _osp_srcdev_avpid; extern unsigned short _osp_srcdev_avptype; extern char* _osp_snid_avp; extern int _osp_snid_avpid; extern unsigned short _osp_snid_avptype; extern char* _osp_swid_avp; extern int _osp_swid_avpid; extern unsigned short _osp_swid_avptype; extern char* _osp_cinfo_avp; extern int _osp_cinfo_avpid; extern unsigned short _osp_cinfo_avptype; extern char* _osp_cnam_avp; extern int _osp_cnam_avpid; extern unsigned short _osp_cnam_avptype; extern char* _osp_extraheaders_value; extern char* _osp_srcmedia_avp; extern int _osp_srcmedia_avpid; extern unsigned short _osp_srcmedia_avptype; extern char* _osp_destmedia_avp; extern int _osp_destmedia_avpid; extern unsigned short _osp_destmedia_avptype; extern char* _osp_reqdate_avp; extern int _osp_reqdate_avpid; extern unsigned short _osp_reqdate_avptype; extern char* _osp_sdpfp_avp; extern int _osp_sdpfp_avpid; extern unsigned short _osp_sdpfp_avptype; extern char* _osp_idsign_avp; extern int _osp_idsign_avpid; extern unsigned short _osp_idsign_avptype; extern char* _osp_idalg_avp; extern int _osp_idalg_avpid; extern unsigned short _osp_idalg_avptype; extern char* _osp_idinfo_avp; extern int _osp_idinfo_avpid; extern unsigned short _osp_idinfo_avptype; extern char* _osp_idtype_avp; extern int _osp_idtype_avpid; extern unsigned short _osp_idtype_avptype; extern char* _osp_idcanon_avp; extern int _osp_idcanon_avpid; extern unsigned short _osp_idcanon_avptype; extern OSPTPROVHANDLE _osp_provider; struct rr_binds osp_rr; auth_api_t osp_auth; int osp_index[OSP_DEF_SPS]; static int ospInitMod(void); static void ospDestMod(void); static int ospInitChild(int); static int ospVerifyParameters(void); static void ospDumpParameters(void); static cmd_export_t cmds[]={ { "checkospheader", (cmd_function)ospCheckHeader, 0, 0, 0, REQUEST_ROUTE }, { "validateospheader", (cmd_function)ospValidateHeader, 0, 0, 0, REQUEST_ROUTE }, { "getlocaladdress", (cmd_function)ospGetLocalAddress, 0, 0, 0, ONREPLY_ROUTE }, { "setrequestdate", (cmd_function)ospSetRequestDate, 0, 0, 0, REQUEST_ROUTE }, { "requestosprouting", (cmd_function)ospRequestRouting, 0, 0, 0, REQUEST_ROUTE }, { "checkosproute", (cmd_function)ospCheckRoute, 0, 0, 0, REQUEST_ROUTE }, { "prepareosproute", (cmd_function)ospPrepareRoute, 0, 0, 0, BRANCH_ROUTE }, { "prepareospresponse", (cmd_function)ospPrepareResponse, 0, 0, 0, REQUEST_ROUTE }, { "prepareallosproutes", (cmd_function)ospPrepareAllRoutes, 0, 0, 0, REQUEST_ROUTE }, { "checkcallingtranslation", (cmd_function)ospCheckCalling, 0, 0, 0, BRANCH_ROUTE }, { "reportospusage", (cmd_function)ospReportUsage, 1, fixup_spve_null, 0, REQUEST_ROUTE }, { "processsubscribe", (cmd_function)ospProcessSubscribe, 1, fixup_spve_null, 0, REQUEST_ROUTE }, { 0, 0, 0, 0, 0, 0 } }; static param_export_t params[]={ { "work_mode", INT_PARAM, &_osp_work_mode }, { "service_type", INT_PARAM, &_osp_service_type }, { "sp1_uri", STR_PARAM, &_osp_sp_uris[0] }, { "sp2_uri", STR_PARAM, &_osp_sp_uris[1] }, { "sp3_uri", STR_PARAM, &_osp_sp_uris[2] }, { "sp4_uri", STR_PARAM, &_osp_sp_uris[3] }, { "sp5_uri", STR_PARAM, &_osp_sp_uris[4] }, { "sp6_uri", STR_PARAM, &_osp_sp_uris[5] }, { "sp7_uri", STR_PARAM, &_osp_sp_uris[6] }, { "sp8_uri", STR_PARAM, &_osp_sp_uris[7] }, { "sp9_uri", STR_PARAM, &_osp_sp_uris[8] }, { "sp10_uri", STR_PARAM, &_osp_sp_uris[9] }, { "sp11_uri", STR_PARAM, &_osp_sp_uris[10] }, { "sp12_uri", STR_PARAM, &_osp_sp_uris[11] }, { "sp13_uri", STR_PARAM, &_osp_sp_uris[12] }, { "sp14_uri", STR_PARAM, &_osp_sp_uris[13] }, { "sp15_uri", STR_PARAM, &_osp_sp_uris[14] }, { "sp16_uri", STR_PARAM, &_osp_sp_uris[15] }, { "sp1_weight", INT_PARAM, &_osp_sp_weights[0] }, { "sp2_weight", INT_PARAM, &_osp_sp_weights[1] }, { "sp3_weight", INT_PARAM, &_osp_sp_weights[2] }, { "sp4_weight", INT_PARAM, &_osp_sp_weights[3] }, { "sp5_weight", INT_PARAM, &_osp_sp_weights[4] }, { "sp6_weight", INT_PARAM, &_osp_sp_weights[5] }, { "sp7_weight", INT_PARAM, &_osp_sp_weights[6] }, { "sp8_weight", INT_PARAM, &_osp_sp_weights[7] }, { "sp9_weight", INT_PARAM, &_osp_sp_weights[8] }, { "sp10_weight", INT_PARAM, &_osp_sp_weights[9] }, { "sp11_weight", INT_PARAM, &_osp_sp_weights[10] }, { "sp12_weight", INT_PARAM, &_osp_sp_weights[11] }, { "sp13_weight", INT_PARAM, &_osp_sp_weights[12] }, { "sp14_weight", INT_PARAM, &_osp_sp_weights[13] }, { "sp15_weight", INT_PARAM, &_osp_sp_weights[14] }, { "sp16_weight", INT_PARAM, &_osp_sp_weights[15] }, { "device_ip", STR_PARAM, &_osp_device_ip }, { "use_security_features", INT_PARAM, &_osp_use_security }, { "private_key", STR_PARAM, &_osp_private_key }, { "local_certificate", STR_PARAM, &_osp_local_certificate }, { "ca_certificates", STR_PARAM, &_osp_ca_certificate }, { "enable_crypto_hardware_support", INT_PARAM, &_osp_crypto_hw }, { "validate_callid", INT_PARAM, &_osp_validate_callid }, { "token_format", INT_PARAM, &_osp_token_format }, { "ssl_lifetime", INT_PARAM, &_osp_ssl_lifetime }, { "persistence", INT_PARAM, &_osp_persistence }, { "retry_delay", INT_PARAM, &_osp_retry_delay }, { "retry_limit", INT_PARAM, &_osp_retry_limit }, { "timeout", INT_PARAM, &_osp_timeout }, { "support_nonsip_protocol", INT_PARAM, &_osp_non_sip }, { "max_destinations", INT_PARAM, &_osp_max_dests }, { "report_networkid", INT_PARAM, &_osp_report_nid }, { "use_number_portability", INT_PARAM, &_osp_use_np }, { "append_userphone", INT_PARAM, &_osp_append_userphone }, { "networkid_location", INT_PARAM, &_osp_dnid_location}, { "networkid_parameter", STR_PARAM, &_osp_dnid_param }, { "switchid_location", INT_PARAM, &_osp_swid_location}, { "switchid_parameter", STR_PARAM, &_osp_swid_param }, { "parameterstring_location", INT_PARAM, &_osp_paramstr_location}, { "parameterstring_value", STR_PARAM, &_osp_paramstr_value }, { "source_device_avp", STR_PARAM, &_osp_srcdev_avp }, { "source_networkid_avp", STR_PARAM, &_osp_snid_avp }, { "source_switchid_avp", STR_PARAM, &_osp_swid_avp }, { "custom_info_avp", STR_PARAM, &_osp_cinfo_avp }, { "cnam_avp", STR_PARAM, &_osp_cnam_avp }, { "extraheaders_value", STR_PARAM, &_osp_extraheaders_value }, { "source_media_avp", STR_PARAM, &_osp_srcmedia_avp }, { "destination_media_avp", STR_PARAM, &_osp_destmedia_avp }, { "request_date_avp", STR_PARAM, &_osp_reqdate_avp }, { "sdp_fingerprint_avp", STR_PARAM, &_osp_sdpfp_avp }, { "identity_signature_avp", STR_PARAM, &_osp_idsign_avp }, { "identity_algorithm_avp", STR_PARAM, &_osp_idalg_avp }, { "identity_information_avp", STR_PARAM, &_osp_idinfo_avp }, { "identity_type_avp", STR_PARAM, &_osp_idtype_avp }, { "identity_canon_avp", STR_PARAM, &_osp_idcanon_avp }, { 0,0,0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "auth", DEP_ABORT }, { MOD_TYPE_DEFAULT, "rr", DEP_ABORT }, { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "osp", MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported params */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ ospInitMod, /* module initialization function */ 0, /* response function*/ ospDestMod, /* destroy function */ ospInitChild, /* per-child init function */ }; static int ospCheckAVP(char* avpname, int *avpid, unsigned short *avptype); /* * Initialize OSP module * return 0 success, -1 failure */ static int ospInitMod(void) { bind_auth_t bind_auth; LM_INFO("initializing...\n"); if (ospVerifyParameters() != 0) { /* At least one parameter incorrect -> error */ return -1; } /* Load the RR API */ if (load_rr_api(&osp_rr) != 0) { LM_WARN("failed to load the RR API. Check if you load the rr module\n"); LM_WARN("add_rr_param is required for reporting duration for OSP transactions\n"); memset(&osp_rr, 0, sizeof(osp_rr)); } /* Load the AUTH API */ bind_auth = (bind_auth_t)find_export("bind_auth", 0, 0); if ((bind_auth == NULL) || (bind_auth(&osp_auth) != 0)) { LM_WARN("failed to load the AUTH API. Check if you load the auth module.\n"); LM_WARN("rpid_avp & rpid_avp_type is required for calling number translation\n"); memset(&osp_auth, 0, sizeof(osp_auth)); } if (ospInitSig() != 0) { return -1; } if (ospInitTm() != 0) { return -1; } if(ospParseAvps() != 0) { return -1; } /* everything is fine, initialization done */ return 0; } /* * Destrroy OSP module */ static void ospDestMod(void) { } /* * Initializeild process of OSP module * param rank * return 0 success, -1 failure */ static int ospInitChild( int rank) { int code = -1; code = ospSetupProvider(); LM_DBG("provider '%d' (%d)\n", _osp_provider, code); return 0; } /* * Check AVP definition * param avpname AVP name * param avpid AVP ID * param avptype AVP type * return 0 success, -1 failure */ static int ospCheckAVP( char* avpname, int *avpid, unsigned short *avptype) { str avp_str; pv_spec_t avp_spec; if (avpname && *avpname) { avp_str.s = avpname; avp_str.len = strlen(avpname); if (pv_parse_spec(&avp_str, &avp_spec) == NULL || avp_spec.type != PVT_AVP || pv_get_avp_name(0, &(avp_spec.pvp), avpid, avptype) != 0) { LM_WARN("'%s' invalid AVP definition\n", avpname); *avpid = OSP_DEF_AVP; *avptype = 0; } } else { *avpid = OSP_DEF_AVP; *avptype = 0; } return 0; } /* * Verify parameters for OSP module * return 0 success, -1 failure */ static int ospVerifyParameters(void) { int i; char hostname[OSP_STRBUF_SIZE]; int result = 0; if ((_osp_work_mode < 0) || (_osp_work_mode > 1)) { _osp_work_mode = OSP_DEF_MODE; LM_WARN("work mode is out of range, reset to %d\n", OSP_DEF_MODE); } if ((_osp_service_type < 0) || (_osp_service_type > 2)) { _osp_service_type = OSP_DEF_SERVICE; LM_WARN("service type is out of range, reset to %d\n", OSP_DEF_SERVICE); } /* If use_security_features is 0, ignroe the certificate files */ if (_osp_use_security != 0) { /* Default location for the cert files is in the compile time variable CFG_DIR */ if (_osp_private_key == NULL) { sprintf(_osp_PRIVATE_KEY, "%spkey.pem", CFG_DIR); _osp_private_key = _osp_PRIVATE_KEY; } if (_osp_local_certificate == NULL) { sprintf(_osp_LOCAL_CERTIFICATE, "%slocalcert.pem", CFG_DIR); _osp_local_certificate = _osp_LOCAL_CERTIFICATE; } if (_osp_ca_certificate == NULL) { sprintf(_osp_CA_CERTIFICATE, "%scacert_0.pem", CFG_DIR); _osp_ca_certificate = _osp_CA_CERTIFICATE; } } if (_osp_device_ip == NULL) { gethostname(hostname, sizeof(hostname)); _osp_device_ip = hostname; } ospConvertToOutAddress(_osp_device_ip, _osp_out_device, sizeof(_osp_out_device)); ospConvertToInAddress(_osp_device_ip, _osp_in_device, sizeof(_osp_in_device)); if (_osp_max_dests > OSP_DEF_DESTS || _osp_max_dests < 1) { _osp_max_dests = OSP_DEF_DESTS; LM_WARN("max_destinations is out of range, reset to %d\n", OSP_DEF_DESTS); } if (_osp_report_nid < 0 || _osp_report_nid > 3) { _osp_report_nid = OSP_DEF_REPORTNID; LM_WARN("report_networkid is out of range, reset to %d\n", OSP_DEF_REPORTNID); } if (_osp_token_format < 0 || _osp_token_format > 2) { _osp_token_format = OSP_DEF_TOKEN; LM_WARN("token_format is out of range, reset to %d\n", OSP_DEF_TOKEN); } _osp_sp_number = 0; for (i = 0; i < OSP_DEF_SPS; i++) { if (_osp_sp_uris[i] != NULL) { if (_osp_sp_number != i) { _osp_sp_uris[_osp_sp_number] = _osp_sp_uris[i]; _osp_sp_weights[_osp_sp_number] = _osp_sp_weights[i]; _osp_sp_uris[i] = NULL; _osp_sp_weights[i] = OSP_DEF_WEIGHT; } osp_index[_osp_sp_number] = i + 1; _osp_sp_number++; } } if (_osp_sp_number == 0) { LM_ERR("at least one service point uri must be configured\n"); result = -1; } if ((_osp_dnid_location < 0) || (_osp_dnid_location > 2)) { _osp_dnid_location = OSP_DEF_DNIDLOC; LM_WARN("networkid_location is out of range, reset to %d\n", OSP_DEF_DNIDLOC); } if (!(_osp_dnid_param && *_osp_dnid_param)) { _osp_dnid_param = OSP_DEF_DNIDPARAM; } if ((_osp_swid_location < 0) || (_osp_swid_location > 2)) { _osp_swid_location = OSP_DEF_SWIDLOC; LM_WARN("switchid_location is out of range, reset to %d\n", OSP_DEF_SWIDLOC); } if (!(_osp_swid_param && *_osp_swid_param)) { _osp_swid_param = OSP_DEF_SWIDPARAM; } if ((_osp_paramstr_location < 0) || (_osp_paramstr_location > 2)) { _osp_paramstr_location = OSP_DEF_PARAMSTRLOC; LM_WARN("parameterstring_location is out of range, reset to %d\n", OSP_DEF_PARAMSTRLOC); } if (!(_osp_paramstr_value && *_osp_paramstr_value)) { _osp_paramstr_value = OSP_DEF_PARAMSTRVAL; } ospCheckAVP(_osp_srcdev_avp, &_osp_srcdev_avpid, &_osp_srcdev_avptype); ospCheckAVP(_osp_snid_avp, &_osp_snid_avpid, &_osp_snid_avptype); ospCheckAVP(_osp_swid_avp, &_osp_swid_avpid, &_osp_swid_avptype); ospCheckAVP(_osp_cinfo_avp, &_osp_cinfo_avpid, &_osp_cinfo_avptype); ospCheckAVP(_osp_cnam_avp, &_osp_cnam_avpid, &_osp_cnam_avptype); if (!(_osp_extraheaders_value && *_osp_extraheaders_value)) { _osp_extraheaders_value = OSP_DEF_EXTHEADERVAL; } ospCheckAVP(_osp_srcmedia_avp, &_osp_srcmedia_avpid, &_osp_srcmedia_avptype); ospCheckAVP(_osp_destmedia_avp, &_osp_destmedia_avpid, &_osp_destmedia_avptype); ospCheckAVP(_osp_reqdate_avp, &_osp_reqdate_avpid, &_osp_reqdate_avptype); ospCheckAVP(_osp_sdpfp_avp, &_osp_sdpfp_avpid, &_osp_sdpfp_avptype); ospCheckAVP(_osp_idsign_avp, &_osp_idsign_avpid, &_osp_idsign_avptype); ospCheckAVP(_osp_idalg_avp, &_osp_idalg_avpid, &_osp_idalg_avptype); ospCheckAVP(_osp_idinfo_avp, &_osp_idinfo_avpid, &_osp_idinfo_avptype); ospCheckAVP(_osp_idtype_avp, &_osp_idtype_avpid, &_osp_idtype_avptype); ospCheckAVP(_osp_idcanon_avp, &_osp_idcanon_avpid, &_osp_idcanon_avptype); ospDumpParameters(); return result; } /* * Dump OSP module configuration */ static void ospDumpParameters(void) { int i; LM_INFO("module configuration: "); LM_INFO(" work mode '%d'", _osp_work_mode); LM_INFO(" service type '%d'", _osp_service_type); LM_INFO(" number of service points '%d'", _osp_sp_number); for (i = 0; i < _osp_sp_number; i++) { LM_INFO(" sp%d_uri '%s' sp%d_weight '%ld' ", osp_index[i], _osp_sp_uris[i], osp_index[i], _osp_sp_weights[i]); } LM_INFO(" device_ip '%s' ", _osp_in_device); LM_INFO(" use_security_features '%d' ", _osp_use_security); if (_osp_use_security != 0) { LM_INFO(" private_key '%s' ", _osp_private_key); LM_INFO(" local_certificate '%s' ", _osp_local_certificate); LM_INFO(" ca_certificates '%s' ", _osp_ca_certificate); } LM_INFO(" enable_crypto_hardware_support '%d' ", _osp_crypto_hw); LM_INFO(" token_format '%d' ", _osp_token_format); LM_INFO(" ssl_lifetime '%d' ", _osp_ssl_lifetime); LM_INFO(" persistence '%d' ", _osp_persistence); LM_INFO(" retry_delay '%d' ", _osp_retry_delay); LM_INFO(" retry_limit '%d' ", _osp_retry_limit); LM_INFO(" timeout '%d' ", _osp_timeout); LM_INFO(" validate_call_id '%d' ", _osp_validate_callid); LM_INFO(" use_number_portability '%d' ", _osp_use_np); LM_INFO(" append_userphone '%d' ", _osp_append_userphone); LM_INFO(" networkid_location '%d' ", _osp_dnid_location); LM_INFO(" networkid_parameter '%s' ", _osp_dnid_param); LM_INFO(" switchid_location '%d' ", _osp_swid_location); LM_INFO(" switchid_parameter '%s' ", _osp_swid_param); LM_INFO(" parameterstring_location '%d' ", _osp_paramstr_location); LM_INFO(" parameterstring_value '%s' ", _osp_paramstr_value); LM_INFO(" max_destinations '%d'\n", _osp_max_dests); LM_INFO(" report_networkid '%d'\n", _osp_report_nid); LM_INFO(" support_nonsip_protocol '%d'\n", _osp_non_sip); LM_INFO(" source device IP AVP ID '%d'\n", _osp_srcdev_avpid); LM_INFO(" source network ID AVP ID '%d'\n", _osp_snid_avpid); LM_INFO(" source switch ID AVP ID '%d'\n", _osp_swid_avpid); LM_INFO(" custom info AVP ID '%d'\n", _osp_cinfo_avpid); LM_INFO(" cnam AVP ID '%d'\n", _osp_cnam_avpid); LM_INFO(" extraheaders_value '%s' ", _osp_extraheaders_value); LM_INFO(" meida AVP ID '%d/%d'\n", _osp_srcmedia_avpid, _osp_destmedia_avpid); LM_INFO(" request date AVP ID '%d'\n", _osp_reqdate_avpid); LM_INFO(" sdp fingerprint AVP ID '%d'\n", _osp_sdpfp_avpid); LM_INFO(" identity AVP ID '%d/%d/%d/%d/%d'\n", _osp_idsign_avpid, _osp_idalg_avpid, _osp_idinfo_avpid, _osp_idtype_avpid, _osp_idcanon_avpid); } opensips-2.2.2/modules/osp/osp_mod.h000066400000000000000000000076151300170765700174560ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_H_ #define _OSP_MOD_H_ #define MODULE_RETURNCODE_TRUE 1 #define MODULE_RETURNCODE_STOPROUTE 0 #define MODULE_RETURNCODE_FALSE -1 #define MODULE_RETURNCODE_ERROR -2 #define OSP_DEF_AVP -1 #define OSP_DEF_MODE 0 #define OSP_DEF_SERVICE 0 #define OSP_DEF_SPS 16 #define OSP_DEF_WEIGHT 1000 #define OSP_DEF_HW 0 #define OSP_DEF_USESEC 0 #define OSP_DEF_CALLID 1 /* Validate call ids, set to 0 to disable */ #define OSP_DEF_TOKEN 2 #define OSP_DEF_SSLLIFE 300 #define OSP_DEF_PERSISTENCE 60 #define OSP_DEF_DELAY 0 #define OSP_DEF_RETRY 2 #define OSP_DEF_TIMEOUT (60 * 1000) #define OSP_DEF_DESTS 12 #define OSP_DEF_REPORTNID 3 #define OSP_DEF_NONSIP 0 #define OSP_DEF_USENP 1 #define OSP_DEF_USERPHONE 0 #define OSP_DEF_DNIDLOC 2 #define OSP_DEF_DNIDPARAM "networkid" #define OSP_DEF_SWIDLOC 2 #define OSP_DEF_SWIDPARAM "switchid" #define OSP_DEF_PARAMSTRLOC 0 #define OSP_DEF_PARAMSTRVAL "" #define OSP_DEF_SRCIPAVP "$avp(_osp_source_device_)" #define OSP_DEF_SNIDAVP "$avp(_osp_source_networkid_)" #define OSP_DEF_SWIDAVP "$avp(_osp_source_switchid_)" #define OSP_DEF_CINFONUM 8 #define OSP_DEF_CINFOAVP "$avp(_osp_custom_info_)" #define OSP_DEF_CNAMAVP "$avp(_osp_cnam_)" #define OSP_DEF_EXTHEADERVAL "" #define OSP_DEF_SRCMEDIAAVP "$avp(_osp_source_media_address_)" #define OSP_DEF_DESTMEDIAAVP "$avp(_osp_destination_media_address_)" #define OSP_DEF_REQDATEAVP "$avp(_osp_request_date_)" #define OSP_DEF_SDPFPNUM 4 #define OSP_DEF_SDPFPAVP "$avp(_osp_sdp_fingerprint_)" #define OSP_DEF_IDSIGNAVP "$avp(_osp_identity_signature_)" #define OSP_DEF_IDALGAVP "$avp(_osp_identity_algorithm_)" #define OSP_DEF_IDINFOAVP "$avp(_osp_identity_information_)" #define OSP_DEF_IDTYPEAVP "$avp(_osp_identity_type_)" #define OSP_DEF_IDCANONAVP "$avp(_osp_identity_canon_)" #define OSP_STRBUF_SIZE 256 #define OSP_KEYBUF_SIZE 1024 #define OSP_TOKENBUF_SIZE 2048 #define OSP_HEADERBUF_SIZE 3072 #define OSP_ALGBUF_SIZE 16 #define OSP_SIGNBUF_SIZE 4096 #endif /* _OSP_MOD_H_ */ opensips-2.2.2/modules/osp/osptoolkit.c000066400000000000000000000131221300170765700202060ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "../../dprint.h" #include "osptoolkit.h" static OSPTTHREADRETURN ospReportUsageWork(void* usagearg); typedef struct _osp_usage { OSPTTRANHANDLE transaction; /* Transaction handle */ unsigned cause; /* Release code */ unsigned duration; /* Length of call */ time_t start; /* Call start time */ time_t end; /* Call end time */ time_t alert; /* Call alert time */ time_t connect; /* Call connect time */ unsigned haspdd; /* Is PDD Info present */ unsigned pdd; /* Post Dial Delay, in seconds */ OSPE_RELEASE release; /* EP that released the call */ } osp_usage; /* * Get OSP transaction ID from transaction handle * param transaction OSP transaction headle * return OSP transaction ID */ unsigned long long ospGetTransactionId( OSPTTRANHANDLE transaction) { OSPTTRANS* context = NULL; unsigned long long id = 0; int errorcode = OSPC_ERR_NO_ERROR; context = OSPPTransactionGetContext(transaction, &errorcode); if (errorcode == OSPC_ERR_NO_ERROR) { id = (unsigned long long)context->TransactionID; } else { LM_ERR("failed to extract transaction_id from transaction handle %d (%d)\n", transaction, errorcode); } return id; } /* * Create a thread to report OSP usage * param ospvTransaction OSP transaction handle * param ospvReleaseCode Call release reason * param ospvDurating Call duration * param ospvStartTime Call start time * param ospvEndTime Call end time * param ospvAlertTime Call alert time * param ospvConnectTime Call connected time * param ospvIsPDDInfoPresent If post dial delay information avaliable * param ospvPostDialDelay Post dial delay information, in seconds * param ospvReleaseSource Which side release the call */ void ospReportUsageWrapper( OSPTTRANHANDLE ospvTransaction, unsigned ospvReleaseCode, unsigned ospvDuration, time_t ospvStartTime, time_t ospvEndTime, time_t ospvAlertTime, time_t ospvConnectTime, unsigned ospvIsPDDInfoPresent, unsigned ospvPostDialDelay, OSPE_RELEASE ospvReleaseSource) { osp_usage* usage; OSPTTHREADID threadid; OSPTTHRATTR threadattr; int errorcode; LM_DBG("schedule usage report for '%llu'\n", ospGetTransactionId(ospvTransaction)); usage = (osp_usage*)malloc(sizeof(osp_usage)); usage->transaction = ospvTransaction; usage->cause = ospvReleaseCode; usage->duration = ospvDuration; usage->start = ospvStartTime; usage->end = ospvEndTime; usage->alert = ospvAlertTime; usage->connect = ospvConnectTime; usage->haspdd = ospvIsPDDInfoPresent; usage->pdd = ospvPostDialDelay; usage->release = ospvReleaseSource; OSPM_THRATTR_INIT(threadattr, errorcode); OSPM_SETDETACHED_STATE(threadattr, errorcode); OSPM_CREATE_THREAD(threadid, &threadattr, ospReportUsageWork, usage, errorcode); OSPM_THRATTR_DESTROY(threadattr); } /* * Report OSP usage thread function * param usagearg OSP usage information * return */ static OSPTTHREADRETURN ospReportUsageWork( void* usagearg) { int i; const int MAX_RETRIES = 5; osp_usage* usage; int errorcode; usage = (osp_usage*)usagearg; OSPPTransactionRecordFailure( usage->transaction, usage->cause); #if 0 OSPPTransactionSetTermCause( usage->transaction, OSPC_TCAUSE_SIP, usage->cause, NULL); #endif for (i = 1; i <= MAX_RETRIES; i++) { errorcode = OSPPTransactionReportUsage( usage->transaction, usage->duration, usage->start, usage->end, usage->alert, usage->connect, usage->haspdd, usage->pdd * 1000, usage->release, NULL, -1, -1, -1, -1, NULL, NULL); if (errorcode == OSPC_ERR_NO_ERROR) { LM_DBG("reporte usage for '%llu'\n", ospGetTransactionId(usage->transaction)); break; } else { LM_ERR("failed to report usage for '%llu' (%d) attempt '%d' of '%d'\n", ospGetTransactionId(usage->transaction), errorcode, i, MAX_RETRIES); } } OSPPTransactionDelete(usage->transaction); free(usage); OSPTTHREADRETURN_NULL(); } opensips-2.2.2/modules/osp/osptoolkit.h000066400000000000000000000046041300170765700202200ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_OSPTOOLKIT_H_ #define _OSP_MOD_OSPTOOLKIT_H_ #include /* * This module implements help functions for the OSP toolkit. * Some of the functions maybe impemented by the toolkit in the future. */ /* * Returns the OSP transaction id generated by the server. The id is * returned in the Authorization Response and also packaged in the token. */ unsigned long long ospGetTransactionId(OSPTTRANHANDLE transaction); void ospReportUsageWrapper( OSPTTRANHANDLE ospvTransaction, /* In - Transaction handle */ unsigned ospvReleaseCode, /* In - Release code */ unsigned ospvDuration, /* In - Length of call */ time_t ospvStartTime, /* In - Call start time */ time_t ospvEndTime, /* In - Call end time */ time_t ospvAlertTime, /* In - Call alert time */ time_t ospvConnectTime, /* In - Call connect time */ unsigned ospvIsPDDInfoPresent, /* In - Is PDD Info present */ unsigned ospvPostDialDelay, /* In - Post Dial Delay, in seconds */ OSPE_RELEASE ospvReleaseSource /* In - EP that released the call */ ); #endif /* _OSP_MOD_OSPTOOLKIT_H_ */ opensips-2.2.2/modules/osp/provider.c000066400000000000000000000166141300170765700176420ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "../../dprint.h" #include "osp_mod.h" #include "provider.h" extern unsigned int _osp_sp_number; extern char* _osp_sp_uris[]; extern unsigned long _osp_sp_weights[]; extern int _osp_use_security; extern char* _osp_private_key; extern char* _osp_local_certificate; extern char* _osp_ca_certificate; extern int _osp_ssl_lifetime; extern int _osp_persistence; extern int _osp_retry_delay; extern int _osp_retry_limit; extern int _osp_timeout; extern int _osp_crypto_hw; extern OSPTPROVHANDLE _osp_provider; const char* B64PKey = "MIIBOgIBAAJBAK8t5l+PUbTC4lvwlNxV5lpl+2dwSZGW46dowTe6y133XyVEwNiiRma2YNk3xKs/TJ3Wl9Wpns2SYEAJsFfSTukCAwEAAQJAPz13vCm2GmZ8Zyp74usTxLCqSJZNyMRLHQWBM0g44Iuy4wE3vpi7Wq+xYuSOH2mu4OddnxswCP4QhaXVQavTAQIhAOBVCKXtppEw9UaOBL4vW0Ed/6EA/1D8hDW6St0h7EXJAiEAx+iRmZKhJD6VT84dtX5ZYNVk3j3dAcIOovpzUj9a0CECIEduTCapmZQ5xqAEsLXuVlxRtQgLTUD4ZxDElPn8x0MhAiBE2HlcND0+qDbvtwJQQOUzDgqg5xk3w8capboVdzAlQQIhAMC+lDL7+gDYkNAft5Mu+NObJmQs4Cr+DkDFsKqoxqrm"; const char* B64LCert = "MIIBeTCCASMCEHqkOHVRRWr+1COq3CR/xsowDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTA1MDYyMzAwMjkxOFoXDTA2MDYyNDAwMjkxOFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCvLeZfj1G0wuJb8JTcVeZaZftncEmRluOnaME3ustd918lRMDYokZmtmDZN8SrP0yd1pfVqZ7NkmBACbBX0k7pAgMBAAEwDQYJKoZIhvcNAQEEBQADQQDnV8QNFVVJx/+7IselU0wsepqMurivXZzuxOmTEmTVDzCJx1xhA8jd3vGAj7XDIYiPub1PV23eY5a2ARJuw5w9"; const char* B64CACert = "MIIBYDCCAQoCAQEwDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTAyMDIwNDE4MjU1MloXDTEyMDIwMzE4MjU1MlowOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPGeGwV41EIhX0jEDFLRXQhDEr50OUQPq+f55VwQd0TQNts06BP29+UiNdRW3c3IRHdZcJdC1Cg68ME9cgeq0h8CAwEAATANBgkqhkiG9w0BAQQFAANBAGkzBSj1EnnmUxbaiG1N4xjIuLAWydun7o3bFk2tV8dBIhnuh445obYyk1EnQ27kI7eACCILBZqi2MHDOIMnoN0="; /* * Create a new OSP provider object per process * return 0 success, others failure */ int ospSetupProvider(void) { OSPTPRIVATEKEY privatekey = { NULL, 0 }; OSPT_CERT localcert = { NULL, 0 }; OSPT_CERT cacert = { NULL, 0 }; OSPT_CERT* cacerts[1]; unsigned char privatekeydata[OSP_KEYBUF_SIZE]; unsigned char localcertdata[OSP_KEYBUF_SIZE]; unsigned char cacertdata[OSP_KEYBUF_SIZE]; int errorcode, result = -1; cacerts[0] = &cacert; if ((errorcode = OSPPInit(_osp_crypto_hw)) != OSPC_ERR_NO_ERROR) { LM_ERR("failed to initalize OSP (%d)\n", errorcode); } else { if (_osp_use_security == 0) { privatekey.PrivateKeyData = privatekeydata; privatekey.PrivateKeyLength = sizeof(privatekeydata); localcert.CertData = localcertdata; localcert.CertDataLength = sizeof(localcertdata); cacert.CertData = cacertdata; cacert.CertDataLength = sizeof(cacertdata); if ((errorcode = OSPPBase64Decode(B64PKey, strlen(B64PKey), privatekey.PrivateKeyData, &privatekey.PrivateKeyLength)) != OSPC_ERR_NO_ERROR) { LM_ERR("failed to decode private key (%d)\n", errorcode); } else if ((errorcode = OSPPBase64Decode(B64LCert, strlen(B64LCert), localcert.CertData, &localcert.CertDataLength)) != OSPC_ERR_NO_ERROR) { LM_ERR("failed to decode local cert (%d)\n", errorcode); } else if ((errorcode = OSPPBase64Decode(B64CACert, strlen(B64CACert), cacert.CertData, &cacert.CertDataLength)) != OSPC_ERR_NO_ERROR) { LM_ERR("failed to decode cacert (%d)\n", errorcode); } } else { if ((errorcode = OSPPUtilLoadPEMPrivateKey((unsigned char*)_osp_private_key, &privatekey)) != OSPC_ERR_NO_ERROR) { LM_ERR("failed to load private key (%d) from '%s'\n", errorcode, _osp_private_key); } else if ((errorcode = OSPPUtilLoadPEMCert((unsigned char*)_osp_local_certificate, &localcert)) != OSPC_ERR_NO_ERROR) { LM_ERR("failed to load local certificate (%d) from '%s'\n", errorcode, _osp_local_certificate); } else if ((errorcode = OSPPUtilLoadPEMCert((unsigned char*)_osp_ca_certificate, &cacert)) != OSPC_ERR_NO_ERROR) { LM_ERR("failed to load CA certificate (%d) from '%s'\n", errorcode, _osp_ca_certificate); } } if (errorcode == OSPC_ERR_NO_ERROR) { errorcode = OSPPProviderNew( _osp_sp_number, (const char**)_osp_sp_uris, _osp_sp_weights, "http://localhost:1234", &privatekey, &localcert, 1, (const OSPT_CERT**)cacerts, 1, _osp_ssl_lifetime, _osp_sp_number, _osp_persistence, _osp_retry_delay, _osp_retry_limit, _osp_timeout, "", "", &_osp_provider); if (errorcode != OSPC_ERR_NO_ERROR) { LM_ERR("failed to create provider (%d)\n", errorcode); } else { LM_DBG("created new (per process) provider '%d'\n", _osp_provider); result = 0; } } /* * Free space allocated while loading crypto information from PEM-encoded files. * There are some problems to free the memory, do not free them */ #if 0 if (_osp_use_security != 0) { if (privatekey.PrivateKeyData != NULL) { free(privatekey.PrivateKeyData); } if (localcert.CertData != NULL) { free(localcert.CertData); } if (cacert.CertData != NULL) { free(localcert.CertData); } } #endif } return result; } /* * Erase OSP provider object * return 0 success, others failure */ int ospDeleteProvider(void) { int errorcode; if ((errorcode = OSPPProviderDelete(_osp_provider, 0)) != OSPC_ERR_NO_ERROR) { LM_ERR("failed to erase provider '%d' (%d)\n", _osp_provider, errorcode); } return errorcode; } opensips-2.2.2/modules/osp/provider.h000066400000000000000000000025421300170765700176420ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_PROVIDER_H_ #define _OSP_MOD_PROVIDER_H_ int ospSetupProvider(void); int ospDeleteProvider(void); #endif /* _OSP_MOD_PROVIDER_H_ */ opensips-2.2.2/modules/osp/signaling.c000066400000000000000000000032111300170765700177500ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2016-06-06 SIGNALING functions are loaded via API function */ #include "../signaling/signaling.h" #include "signaling.h" struct sig_binds osp_sigb; /* * Load SIGNALING API * return 0 success, -1 failure */ int ospInitSig(void) { /* load SIGNALING API */ if (load_sig_api(&osp_sigb) < 0) { LM_ERR("failed to load signaling functions\n"); return -1; } else { return 0; } } opensips-2.2.2/modules/osp/signaling.h000066400000000000000000000025451300170765700177660ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_SIGNALING_H_ #define _OSP_MOD_SIGNALING_H_ extern struct sig_binds osp_sigb; int ospInitSig(void); #endif /* _OSP_MOD_SIGNALING_H_ */ opensips-2.2.2/modules/osp/sipheader.c000066400000000000000000001325051300170765700177520ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../../forward.h" #include "../../parser/parse_from.h" #include "../../parser/parse_diversion.h" #include "../../parser/parse_rpid.h" #include "../../parser/parse_pai.h" #include "../../parser/parse_rr.h" #include "../../parser/parse_uri.h" #include "../../data_lump.h" #include "../../mem/mem.h" #include "osp_mod.h" #include "destination.h" #include "timeapi.h" #include "sipheader.h" extern int _osp_work_mode; extern char _osp_in_device[]; extern int _osp_use_np; extern int _osp_append_userphone; extern int _osp_dnid_location; extern char* _osp_dnid_param; extern int _osp_swid_location; extern char* _osp_swid_param; extern int _osp_paramstr_location; extern char* _osp_paramstr_value; extern int _osp_srcdev_avpid; extern unsigned short _osp_srcdev_avptype; extern int _osp_reqdate_avpid; extern unsigned short _osp_reqdate_avptype; static void ospSkipUserParam(char* userinfo); static int ospAppendHeader(struct sip_msg* msg, str* header); /* * Get AVP value * param avpid AVP ID * param avptype AVP type * param avpstr AVP string * param bufsize AVP string buffer size * return 0 success, -1 failure */ int ospGetAVP( int avpid, unsigned short avptype, char* avpstr, int bufsize) { struct usr_avp* avp = NULL; int_str avpval; int result = -1; if ((avpid >= 0) && ((avp = search_first_avp(avptype, avpid, &avpval, 0)) != NULL) && (avp->flags & AVP_VAL_STR) && (avpval.s.s && avpval.s.len)) { snprintf(avpstr, bufsize, "%.*s", avpval.s.len, avpval.s.s); destroy_avp(avp); result = 0; } return result; } /* * Copy str to buffer and check overflow * param source Str * param buffer Buffer * param bufsize Size of buffer */ void ospCopyStrToBuffer( str* source, char* buffer, int bufsize) { int copybytes; if (source->len >= bufsize) { LM_ERR("buffer for copying '%.*s' is too small, will copy the first '%d' bytes\n", source->len, source->s, bufsize - 1); copybytes = bufsize; } else { copybytes = source->len + 1; } strncpy(buffer, source->s, copybytes); buffer[copybytes - 1] = '\0'; } /* * Get local egress address * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure MODULE_RETURNCODE_ERROR error */ int ospGetLocalAddress( struct sip_msg* msg, char* ignore1, char* ignore2) { osp_dest* dest; if(msg->rcv.bind_address && msg->rcv.bind_address->address_str.s) { if ((dest = ospGetLastOrigDestination())) { ospCopyStrToBuffer(&msg->rcv.bind_address->address_str, dest->egress, sizeof(dest->egress)); } } return MODULE_RETURNCODE_TRUE; } /* * Get display name from From header * param msg SIP message * param fromdisplay Display name of From header * param bufsize Size of fromdisplay buffer * return 0 success, -1 failure */ int ospGetFromDisplay( struct sip_msg* msg, char* fromdisplay, int bufsize) { struct to_body* from; int result = -1; if ((fromdisplay != NULL) && (bufsize > 0)) { fromdisplay[0] = '\0'; if (msg->from != NULL) { if (parse_from_header(msg) == 0) { from = get_from(msg); if ((from->display.s != NULL) && (from->display.len > 0)) { ospCopyStrToBuffer(&from->display, fromdisplay, bufsize); result = 0; } } else { LM_ERR("failed to parse From header\n"); } } else { LM_ERR("failed to find From header\n"); } } else { LM_ERR("bad parameters to parse display name from From header\n"); } return result; } /* * Remove user parameters from userinfo * param userinfo User info */ static void ospSkipUserParam( char* userinfo) { char* delim = NULL; if ((delim = strchr(userinfo, ';')) != NULL) { *delim = '\0'; } if ((delim = strchr(userinfo, ':')) != NULL) { *delim = '\0'; } } /* * Get user part from From header * param msg SIP message * param fromuser User part of From header * param bufsize Size of fromuser buffer * return 0 success, -1 failure */ int ospGetFromUser( struct sip_msg* msg, char* fromuser, int bufsize) { struct to_body* from; struct sip_uri uri; int result = -1; if ((fromuser != NULL) && (bufsize > 0)) { fromuser[0] = '\0'; if (msg->from != NULL) { if (parse_from_header(msg) == 0) { from = get_from(msg); if (parse_uri(from->uri.s, from->uri.len, &uri) == 0) { ospCopyStrToBuffer(&uri.user, fromuser, bufsize); ospSkipUserParam(fromuser); result = 0; } else { LM_ERR("failed to parse From uri\n"); } } else { LM_ERR("failed to parse From header\n"); } } else { LM_ERR("failed to find From header\n"); } } else { LM_ERR("bad parameters to parse user part from From header\n"); } return result; } /* * Get URI of From header * param msg SIP message * param fromuri URI of From header * param bufsize Size of fromuri buffer * return 0 success, -1 failure */ int ospGetFromUri( struct sip_msg* msg, char* fromuri, int bufsize) { struct to_body* from; int result = -1; if ((fromuri != NULL) && (bufsize > 0)) { fromuri[0] = '\0'; if (msg->from != NULL) { if (parse_from_header(msg) == 0) { from = get_from(msg); ospCopyStrToBuffer(&from->uri, fromuri, bufsize); result = 0; } else { LM_ERR("failed to parse From header\n"); } } else { LM_ERR("failed to find From header\n"); } } else { LM_ERR("bad parameters to parse URI of From header\n"); } return result; } /* * Get display name from To header * param msg SIP message * param todisplay Display name of To header * param bufsize Size of todisplay buffer * return 0 success, -1 failure */ int ospGetToDisplay( struct sip_msg* msg, char* todisplay, int bufsize) { struct to_body* to; int result = -1; if ((todisplay != NULL) && (bufsize > 0)) { todisplay[0] = '\0'; if (msg->to != NULL) { if (parse_headers(msg, HDR_TO_F, 0) == 0) { to = get_to(msg); if ((to->display.s != NULL) && (to->display.len > 0)) { ospCopyStrToBuffer(&to->display, todisplay, bufsize); result = 0; } } else { LM_ERR("failed to parse To header\n"); } } else { LM_ERR("failed to find To header\n"); } } else { LM_ERR("bad parameters to parse display name from To header\n"); } return result; } /* * Get user part from To header * param msg SIP message * param touser User part of To header * param bufsize Size of touser buffer * return 0 success, -1 failure */ int ospGetToUser( struct sip_msg* msg, char* touser, int bufsize) { struct to_body* to; struct sip_uri uri; int result = -1; if ((touser != NULL) && (bufsize > 0)) { touser[0] = '\0'; if (msg->to != NULL) { if (parse_headers(msg, HDR_TO_F, 0) == 0) { to = get_to(msg); if (parse_uri(to->uri.s, to->uri.len, &uri) == 0) { ospCopyStrToBuffer(&uri.user, touser, bufsize); ospSkipUserParam(touser); result = 0; } else { LM_ERR("failed to parse To uri\n"); } } else { LM_ERR("failed to parse To header\n"); } } else { LM_ERR("failed to find To header\n"); } } else { LM_ERR("bad parameters to parse user part from To header\n"); } return result; } /* * Get host info from To header * param msg SIP message * param tohost Host part of To header * param bufsize Size of tohost buffer * return 0 success, -1 failure */ int ospGetToHost( struct sip_msg* msg, char* tohost, int bufsize) { struct to_body* to; struct sip_uri uri; int result = -1; if ((tohost != NULL) && (bufsize > 0)) { tohost[0] = '\0'; if (msg->to != NULL) { if (parse_headers(msg, HDR_TO_F, 0) == 0) { to = get_to(msg); if (parse_uri(to->uri.s, to->uri.len, &uri) == 0) { if (uri.port_no != 0) { snprintf(tohost, bufsize, "%.*s:%d", uri.host.len, uri.host.s, uri.port_no); } else { ospCopyStrToBuffer(&uri.host, tohost, bufsize); } result = 0; } else { LM_ERR("failed to parse To uri\n"); } } else { LM_ERR("failed to parse To header\n"); } } else { LM_ERR("failed to find To header\n"); } } else { LM_ERR("bad parameters to parse hsto info from To header\n"); } return result; } /* * Get URI of To header * param msg SIP message * param touri URI of To header * param bufsize Size of touri buffer * return 0 success, -1 failure */ int ospGetToUri( struct sip_msg* msg, char* touri, int bufsize) { struct to_body* to; int result = -1; if ((touri != NULL) && (bufsize > 0)) { touri[0] = '\0'; if (msg->to != NULL) { if (parse_headers(msg, HDR_TO_F, 0) == 0) { to = get_to(msg); ospCopyStrToBuffer(&to->uri, touri, bufsize); result = 0; } else { LM_ERR("failed to parse To header\n"); } } else { LM_ERR("failed to find To header\n"); } } else { LM_ERR("bad parameters to parse URI of To header\n"); } return result; } /* * Get user part from P-Asserted-Identity header * param msg SIP message * param user User part of P-Asserted-Identity header * param bufsize Size of fromuser buffer * return 0 success, 1 without PAI, -1 failure */ int ospGetPaiUser( struct sip_msg* msg, char* paiuser, int bufsize) { struct to_body* pai; struct sip_uri uri; int result = -1; if ((paiuser != NULL) && (bufsize > 0)) { paiuser[0] = '\0'; if (msg->pai != NULL) { if (parse_pai_header(msg) == 0) { pai = get_pai(msg); if (parse_uri(pai->uri.s, pai->uri.len, &uri) == 0) { ospCopyStrToBuffer(&uri.user, paiuser, bufsize); ospSkipUserParam(paiuser); result = 0; } else { LM_ERR("failed to parse PAI uri\n"); } } else { LM_ERR("failed to parse PAI uri\n"); } } else { LM_DBG("without PAI header\n"); result = 1; } } else { LM_ERR("bad parameters to parse user part from PAI\n"); } return result; } /* * Get user part from Remote-Party-ID header * param msg SIP message * param user User part of Remote-Party-ID header * param bufsize Size of fromuser buffer * return 0 success, 1 without RPID, -1 failure */ int ospGetRpidUser( struct sip_msg* msg, char* rpiduser, int bufsize) { struct to_body* rpid; struct sip_uri uri; int result = -1; if ((rpiduser != NULL) && (bufsize > 0)) { rpiduser[0] = '\0'; if (msg->rpid != NULL) { if (parse_rpid_header(msg) == 0) { rpid = get_rpid(msg); if (parse_uri(rpid->uri.s, rpid->uri.len, &uri) == 0) { ospCopyStrToBuffer(&uri.user, rpiduser, bufsize); ospSkipUserParam(rpiduser); result = 0; } else { LM_ERR("failed to parse RPID uri\n"); } } else { LM_ERR("failed to parse RPID uri\n"); } } else { LM_DBG("without RPID header\n"); result = 1; } } else { LM_ERR("bad parameters to parse user part from RPID\n"); } return result; } /* * Get user part from P-Charge-Info header * param msg SIP message * param user User part of P-Charge-Info header * param bufsize Size of fromuser buffer * return 0 success, 1 without P-Charge-Info, -1 failure */ int ospGetPciUser( struct sip_msg* msg, char* pciuser, int bufsize) { static const char* header = "P-Charge-Info"; struct to_body body; struct to_body* pci = NULL; struct hdr_field* hf; struct sip_uri uri; int result = -1; if ((pciuser != NULL) && (bufsize > 0)) { pciuser[0] = '\0'; if (parse_headers(msg, HDR_EOH_F, 0) == 0) { for (hf = msg->headers; hf; hf = hf->next) { if ((hf->type == HDR_OTHER_T) && (hf->name.len == strlen(header)) && (strncasecmp(hf->name.s, header, hf->name.len) == 0)) { if (!(pci = hf->parsed)) { pci = &body; parse_to(hf->body.s, hf->body.s + hf->body.len + 1, pci); } if (pci->error != PARSE_ERROR) { if (parse_uri(pci->uri.s, pci->uri.len, &uri) == 0) { ospCopyStrToBuffer(&uri.user, pciuser, bufsize); ospSkipUserParam(pciuser); result = 0; } else { LM_ERR("failed to parse P-Charge-Info uri\n"); } if (pci == &body) { free_to_params(pci); } } else { LM_ERR("bad P-Charge-Info header\n"); } break; } } if (!hf) { LM_DBG("without P-Charge-Info header\n"); result = 1; } } else { LM_ERR("failed to parse message\n"); } } else { LM_ERR("bad parameters to parse user part from PAI\n"); } return result; } /* * Get number and domain from Diversion header * param msg SIP message * param user User part of Diversion header * param userbufsize Size of user buffer * param host Host part of Diversion header * param hostbufsize Size of host buffer * return 0 success, 1 without Diversion, -1 failure */ int ospGetDiversion( struct sip_msg* msg, char* user, int userbufsize, char* host, int hostbufsize) { struct to_body* diversion; struct sip_uri uri; int result = -1; if (((user != NULL) && (userbufsize > 0)) && ((host != NULL) && (hostbufsize > 0))){ user[0] = '\0'; host[0] = '\0'; if (msg->diversion != NULL) { if (parse_diversion_header(msg) == 0) { diversion = get_diversion(msg); if (parse_uri(diversion->uri.s, diversion->uri.len, &uri) == 0) { ospCopyStrToBuffer(&uri.user, user, userbufsize); ospSkipUserParam(user); if (uri.port_no != 0) { snprintf(host, hostbufsize, "%.*s:%d", uri.host.len, uri.host.s, uri.port_no); } else { ospCopyStrToBuffer(&uri.host, host, hostbufsize); } result = 0; } else { LM_ERR("failed to parse Diversion uri\n"); } } else { LM_ERR("failed to parse Diversion header\n"); } } else { LM_DBG("without Diversion header\n"); result = 1; } } else { LM_ERR("bad paraneters to parse number from Diversion\n"); } return result; } /* * Get all parameters of P-Charging-Vector header * param msg SIP message * param pcvicid ICID value of P-Charging-Vector header * param bufsize Size of ICID value buffer * return 0 success, 1 without P-Charging-Vector, -1 failure */ int ospGetPcvIcid( struct sip_msg* msg, char* pcvicid, int bufsize) { static const char* header = "P-Charging-Vector"; struct hdr_field* hf; param_hooks_t phooks; param_t* params = NULL; param_t* pit; int result = -1; if ((pcvicid != NULL) && (bufsize > 0)) { pcvicid[0] = '\0'; if (parse_headers(msg, HDR_EOH_F, 0) == 0) { for (hf = msg->headers; hf; hf = hf->next) { if ((hf->type == HDR_OTHER_T) && (hf->name.len == strlen(header)) && (strncasecmp(hf->name.s, header, hf->name.len) == 0)) { parse_params(&(hf->body), CLASS_ANY, &phooks, ¶ms); for (pit = params; pit; pit = pit->next) { if ((pit->name.len == OSP_ICID_SIZE) && (strncasecmp(pit->name.s, OSP_ICID_NAME, OSP_ICID_SIZE) == 0) && (pcvicid[0] == '\0')) { ospCopyStrToBuffer(&pit->body, pcvicid, bufsize); result = 0; } } break; } } if (!hf) { LM_DBG("without P-Charging-Vector header\n"); result = 1; } } else { LM_ERR("failed to parse message\n"); } } else { LM_ERR("bad parameters to parse PCV\n"); } return result; } /* * Get user part from Request-Line header * param msg SIP message * param uriuser User part of To header * param bufsize Size of touser buffer * return 0 success, -1 failure */ int ospGetUriUser( struct sip_msg* msg, char* uriuser, int bufsize) { int result = -1; if ((uriuser != NULL) && (bufsize > 0)) { uriuser[0] = '\0'; if (parse_sip_msg_uri(msg) >= 0) { ospCopyStrToBuffer(&msg->parsed_uri.user, uriuser, bufsize); ospSkipUserParam(uriuser); result = 0; } else { LM_ERR("failed to parse Request-Line URI\n"); } } else { LM_ERR("bad parameters to parse user part from RURI\n"); } return result; } /* * Append header to SIP message * param msg SIP message * param header Header to be appended * return 0 success, -1 failure */ static int ospAppendHeader( struct sip_msg* msg, str* header) { char* s; struct lump* anchor; if((msg == 0) || (header == 0) || (header->s == 0) || (header->len <= 0)) { LM_ERR("bad parameters for appending header\n"); return -1; } if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse message\n"); return -1; } anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0); if (anchor == 0) { LM_ERR("failed to get anchor\n"); return -1; } s = (char*)pkg_malloc(header->len); if (s == 0) { LM_ERR("no pkg memory\n"); return -1; } memcpy(s, header->s, header->len); if (insert_new_lump_before(anchor, s, header->len, 0) == 0) { LM_ERR("failed to insert lump\n"); pkg_free(s); return -1; } return 0; } /* * Add OSP token header to SIP message * param msg SIP message * param token OSP authorization token * param tokensize Size of OSP authorization token * return 0 success, -1 failure */ int ospAddOspToken( struct sip_msg* msg, unsigned char* token, unsigned int tokensize) { str headerval; char buffer[OSP_HEADERBUF_SIZE]; unsigned char encodedtoken[OSP_TOKENBUF_SIZE]; unsigned int encodedtokensize = sizeof(encodedtoken); int errorcode, result = -1; if ((token == NULL) || (tokensize == 0)) { LM_DBG("destination is not OSP device\n"); result = 0; } else { if ((errorcode = OSPPBase64Encode(token, tokensize, encodedtoken, &encodedtokensize)) == OSPC_ERR_NO_ERROR) { snprintf(buffer, sizeof(buffer), "%s: %.*s\r\n", OSP_TOKENHEADER_NAME, encodedtokensize, encodedtoken); headerval.s = buffer; headerval.len = strlen(buffer); LM_DBG("setting osp token header field '%s'\n", buffer); if (ospAppendHeader(msg, &headerval) == 0) { result = 0; } else { LM_ERR("failed to append osp header\n"); } } else { LM_ERR("failed to base64 encode token (%d)\n", errorcode); } } return result; } /* * Get OSP token from SIP message * param msg SIP message * param token OSP authorization token * param tokensize Size of OSP authorization token * return 0 success, -1 failure */ int ospGetOspToken( struct sip_msg* msg, unsigned char* token, unsigned int* tokensize) { struct hdr_field* hf; int errorcode; int result = -1; if (parse_headers(msg, HDR_EOH_F, 0) == 0) { hf = get_header_by_name(msg, OSP_TOKENHEADER_NAME, OSP_TOKENHEADER_SIZE); if (hf) { if ((errorcode = OSPPBase64Decode(hf->body.s, hf->body.len, token, tokensize)) == OSPC_ERR_NO_ERROR) { result = 0; } else { LM_ERR("failed to base64 decode token (%d)\n", errorcode); LM_ERR("header '%.*s' length %d\n", hf->body.len, hf->body.s, hf->body.len); } } } else { LM_ERR("failed to parse all headers\n"); } return result; } /* * Get first VIA header and use the IP or host name * param msg SIP message * param viaaddr Via header IP address * param bufsize Size of viaaddr * return 0 success, -1 failure */ int ospGetViaAddress( struct sip_msg* msg, char* viaaddr, int bufsize) { struct hdr_field* hf; struct via_body* via; int result = -1; if ((viaaddr != NULL) && (bufsize > 0)) { /* * No need to call parse_headers, called already and VIA is parsed * anyway by default */ for (hf = msg->headers; hf; hf = hf->next) { if (hf->type == HDR_VIA_T) { /* found first VIA */ via = (struct via_body*)hf->parsed; if (via->port != 0) { snprintf(viaaddr, bufsize, "%.*s:%d", via->host.len, via->host.s, via->port); } else { ospCopyStrToBuffer(&via->host, viaaddr, bufsize); } LM_DBG("via address '%s'\n", viaaddr); result = 0; break; } } } else { LM_ERR("bad parameters to parse host from VIA header\n"); } return result; } /* * Get source device IP address * param msg SIP message * param srcdev Source device address * param bufsize Size of srcdev * return 0 success, -1 failure */ int ospGetSrcDev( struct sip_msg* msg, char* srcdev, int bufsize) { int result = -1; if ((srcdev != NULL) && (bufsize > 0)) { switch (_osp_work_mode) { case 1: if (ospGetAVP(_osp_srcdev_avpid, _osp_srcdev_avptype, srcdev, bufsize) == 0) { result = 0; } else { result = ospGetViaAddress(msg, srcdev, bufsize); } break; case 0: default: result = ospGetViaAddress(msg, srcdev, bufsize); break; } } else { LM_ERR("bad parameters to parse source device\n"); } return result; } /* * Get source IP address * param msg SIP message * param source Source IP address * param bufsize Size of source * return 0 success, -1 failure */ int ospGetSource( struct sip_msg* msg, char* source, int bufsize) { int result = -1; if ((source != NULL) && (bufsize > 0)) { switch (_osp_work_mode) { case 1: result = ospGetViaAddress(msg, source, bufsize); break; case 0: default: strncpy(source, _osp_in_device, bufsize); source[bufsize - 1] = '\0'; result = 0; break; } } else { LM_ERR("bad parameters to parse source\n"); } return result; } /* * Get Call-ID header from SIP message * param msg SIP message * param callid Call ID * return 0 success, -1 failure */ int ospGetCallId( struct sip_msg* msg, OSPT_CALL_ID** callid) { struct hdr_field* hf; int result = -1; hf = (struct hdr_field*)msg->callid; if (hf != NULL) { *callid = OSPPCallIdNew(hf->body.len, (unsigned char*)hf->body.s); if (*callid) { result = 0; } else { LM_ERR("failed to allocate OSPCALLID object for '%.*s'\n", hf->body.len, hf->body.s); } } else { LM_ERR("failed to find Call-ID header\n"); } return result; } /* * Get route parameters from the 1st Route or Request-Line * param msg SIP message * param routeparameters Route parameters * param bufsize Size of routeparameters * return 0 success, -1 failure */ int ospGetRouteParam( struct sip_msg* msg, char* routeparameters, int bufsize) { struct hdr_field* hf; rr_t* rt; struct sip_uri uri; int result = -1; if ((routeparameters != NULL) && (bufsize > 0)) { LM_DBG("parsed uri host '%.*s' port '%d' vars '%.*s'\n", msg->parsed_uri.host.len, msg->parsed_uri.host.s, msg->parsed_uri.port_no, msg->parsed_uri.params.len, msg->parsed_uri.params.s); if (!(hf = msg->route)) { LM_DBG("there is no Route headers\n"); } else if (!(rt = (rr_t*)hf->parsed)) { LM_ERR("route headers are not parsed\n"); } else if (parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &uri) != 0) { LM_ERR("failed to parse the Route uri '%.*s'\n", rt->nameaddr.uri.len, rt->nameaddr.uri.s); } else if (check_self(&uri.host, uri.port_no ? uri.port_no : SIP_PORT, PROTO_NONE) != 1) { LM_DBG("the Route uri is NOT mine\n"); LM_DBG("host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no); LM_DBG("params '%.*s'\n", uri.params.len, uri.params.s); } else { LM_DBG("the Route uri IS mine - '%.*s'\n", uri.params.len, uri.params.s); LM_DBG("host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no); ospCopyStrToBuffer(&uri.params, routeparameters, bufsize); result = 0; } if ((result == -1) && (msg->parsed_uri.params.len > 0)) { LM_DBG("using route parameters from Request-Line uri\n"); ospCopyStrToBuffer(&msg->parsed_uri.params, routeparameters, bufsize); routeparameters[msg->parsed_uri.params.len] = '\0'; result = 0; } } else { LM_ERR("bad parameters to parse parameters from Route header\n"); } return result; } /* * Rebuild URI * param newuri URI to be built, newuri.len includes buffer size * param dest Destination data structure * return 0 success, -1 failure */ int ospRebuildDestUri( str* newuri, osp_dest* dest) { static const str TRANS = { ";transport=tcp", 14 }; static const str USERPHONE = { ";user=phone", 11 }; char* buffer; int calledsize; int hostsize; int uriparamsize; int userparamsize; int dnidsize; int swidsize; int paramstrsize; int count; calledsize = strlen(dest->called); hostsize = strlen(dest->host); /* ";rn=" + nprn */ userparamsize = dest->nprn[0] ? 4 + strlen(dest->nprn) : 0; /* ";cic=" + npcic */ userparamsize += dest->npcic[0] ? 5 + strlen(dest->npcic) : 0; /* ";npdi" */ userparamsize += dest->npdi ? 5 : 0; /* ";spid=" */ userparamsize += dest->opname[OSPC_OPNAME_SPID][0] ? 6 + strlen(dest->opname[OSPC_OPNAME_SPID]) : 0; /* ";ocn=" */ userparamsize += dest->opname[OSPC_OPNAME_OCN][0] ? 5 + strlen(dest->opname[OSPC_OPNAME_OCN]) : 0; /* ";spn=" */ userparamsize += dest->opname[OSPC_OPNAME_SPN][0] ? 5 + strlen(dest->opname[OSPC_OPNAME_SPN]) : 0; /* ";altspn=" */ userparamsize += dest->opname[OSPC_OPNAME_ALTSPN][0] ? 8 + strlen(dest->opname[OSPC_OPNAME_ALTSPN]) : 0; /* ";mcc=" */ userparamsize += dest->opname[OSPC_OPNAME_MCC][0] ? 5 + strlen(dest->opname[OSPC_OPNAME_MCC]) : 0; /* ";mnc=" */ userparamsize += dest->opname[OSPC_OPNAME_MNC][0] ? 5 + strlen(dest->opname[OSPC_OPNAME_MNC]) : 0; /* ";user=phone" */ uriparamsize = _osp_append_userphone ? USERPHONE.len : 0; /* destination network ID parameter */ dnidsize = (_osp_dnid_location && dest->dnid[0]) ? 1 + strlen(_osp_dnid_param) + 1 + strlen(dest->dnid) : 0; /* destination switch ID parameter */ swidsize = (_osp_swid_location && dest->swid[0]) ? 1 + strlen(_osp_swid_param) + 1 + strlen(dest->swid) : 0; /* parameter string */ paramstrsize = (_osp_paramstr_location && _osp_paramstr_value[0]) ? 1 + strlen(_osp_paramstr_value) : 0; LM_DBG("'%s' (%d) '%s' (%d) '%s' '%s' '%d' '%s' '%s' '%s' '%s' '%s' '%s' (%d) '%s' '%s' (%d)\n", dest->called, calledsize, dest->host, hostsize, dest->nprn, dest->npcic, dest->npdi, dest->opname[OSPC_OPNAME_SPID], dest->opname[OSPC_OPNAME_OCN], dest->opname[OSPC_OPNAME_SPN], dest->opname[OSPC_OPNAME_ALTSPN], dest->opname[OSPC_OPNAME_MCC], dest->opname[OSPC_OPNAME_MNC], userparamsize, dest->dnid, dest->swid, uriparamsize); /* "sip:" + called + NP + "@" + host + ";user=phone" + ";_osp_dnid_param=" + dnid + " + ";_osp_swid_param=" + swid + " SIP/2.0" etc. */ /* OpenSIPS will add "<>" for the Contact headers of SIP 3xx messages */ if (newuri->len < (4 + calledsize + userparamsize + 1 + hostsize + uriparamsize + dnidsize + swidsize + paramstrsize + 1 + 7 + TRANS.len)) { LM_ERR("new uri buffer is too small\n"); newuri->len = 0; return -1; } buffer = newuri->s; *buffer++ = 's'; *buffer++ = 'i'; *buffer++ = 'p'; *buffer++ = ':'; memcpy(buffer, dest->called, calledsize); buffer += calledsize; if (dest->nprn[0]) { count = sprintf(buffer, ";rn=%s", dest->nprn); buffer += count; } if (dest->npcic[0]) { count = sprintf(buffer, ";cic=%s", dest->npcic); buffer += count; } if (dest->npdi) { sprintf(buffer, ";npdi"); buffer += 5; } if (dest->opname[OSPC_OPNAME_SPID][0]) { count = sprintf(buffer, ";spid=%s", dest->opname[OSPC_OPNAME_SPID]); buffer += count; } if (dest->opname[OSPC_OPNAME_OCN][0]) { count = sprintf(buffer, ";ocn=%s", dest->opname[OSPC_OPNAME_OCN]); buffer += count; } if (dest->opname[OSPC_OPNAME_SPN][0]) { count = sprintf(buffer, ";spn=%s", dest->opname[OSPC_OPNAME_SPN]); buffer += count; } if (dest->opname[OSPC_OPNAME_ALTSPN][0]) { count = sprintf(buffer, ";altspn=%s", dest->opname[OSPC_OPNAME_ALTSPN]); buffer += count; } if (dest->opname[OSPC_OPNAME_MCC][0]) { count = sprintf(buffer, ";mcc=%s", dest->opname[OSPC_OPNAME_MCC]); buffer += count; } if (dest->opname[OSPC_OPNAME_MNC][0]) { count = sprintf(buffer, ";mnc=%s", dest->opname[OSPC_OPNAME_MNC]); buffer += count; } if ((_osp_dnid_location == 1) && (dest->dnid[0] != '\0')) { count = sprintf(buffer, ";%s=%s", _osp_dnid_param, dest->dnid); buffer += count; } if ((_osp_swid_location == 1) && (dest->swid[0] != '\0')) { count = sprintf(buffer, ";%s=%s", _osp_swid_param, dest->swid); buffer += count; } if ((_osp_paramstr_location == 1) && (_osp_paramstr_value[0] != '\0')) { count = sprintf(buffer, ";%s", _osp_paramstr_value); buffer += count; } *buffer++ = '@'; strncpy(buffer, dest->host, newuri->len - (buffer - newuri->s)); buffer += hostsize; if (_osp_append_userphone != 0) { memcpy(buffer, USERPHONE.s, USERPHONE.len); buffer += USERPHONE.len; } if ((_osp_dnid_location == 2) && (dest->dnid[0] != '\0')) { count = sprintf(buffer, ";%s=%s", _osp_dnid_param, dest->dnid); buffer += count; } if ((_osp_swid_location == 2) && (dest->swid[0] != '\0')) { count = sprintf(buffer, ";%s=%s", _osp_swid_param, dest->swid); buffer += count; } if ((_osp_paramstr_location == 2) && (_osp_paramstr_value[0] != '\0')) { count = sprintf(buffer, ";%s", _osp_paramstr_value); buffer += count; } /* *buffer++ = ' '; *buffer++ = 'S'; *buffer++ = 'I'; *buffer++ = 'P'; *buffer++ = '/'; *buffer++ = '2'; *buffer++ = '.'; *buffer++ = '0'; memcpy(buffer, TRANS.s, TRANS.len); buffer += TRANS.len; *buffer = '\0'; */ newuri->len = buffer - newuri->s; LM_DBG("new uri '%.*s'\n", newuri->len, newuri->s); return 0; } /* * Get next hop using the first Route not generated by this proxy or URI from the Request-Line * param msg SIP message * param nexthop Next hop IP * param bufsize Size of nexthop * return 0 success, -1 failure */ int ospGetNextHop( struct sip_msg* msg, char* nexthop, int bufsize) { struct hdr_field* hf; struct sip_uri uri; rr_t* rt; int found = 0; int result = -1; if ((nexthop != NULL) && (bufsize > 0)) { result = 0; for (hf = msg->headers; hf; hf = hf->next) { if (hf->type == HDR_ROUTE_T) { for (rt = (rr_t*)hf->parsed; rt; rt = rt->next) { if (parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &uri) == 0) { LM_DBG("host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no); if (check_self(&uri.host, uri.port_no ? uri.port_no : SIP_PORT, PROTO_NONE) != 1) { LM_DBG("it is NOT me, FOUND!\n"); if (uri.port_no != 0) { snprintf(nexthop, bufsize, "%.*s:%d", uri.host.len, uri.host.s, uri.port_no); } else { ospCopyStrToBuffer(&uri.host, nexthop, bufsize); } found = 1; break; } else { LM_DBG("it IS me, keep looking\n"); } } else { LM_ERR("failed to parse route uri '%.*s'\n", rt->nameaddr.uri.len, rt->nameaddr.uri.s); } } if (found == 1) { break; } } } if (!found) { LM_DBG("using the Request-Line instead host '%.*s' port '%d'\n", msg->parsed_uri.host.len, msg->parsed_uri.host.s, msg->parsed_uri.port_no); if (msg->parsed_uri.port_no != 0) { snprintf(nexthop, bufsize, "%.*s:%d", msg->parsed_uri.host.len, msg->parsed_uri.host.s, msg->parsed_uri.port_no); } else { ospCopyStrToBuffer(&msg->parsed_uri.host, nexthop, bufsize); } found = 1; } } else { LM_ERR("bad parameters to get next hop\n"); } return result; } /* * Get number portability parameter from Request-Line * param msg SIP message * param rn Routing number * param rnbufsize Size of rn buffer * param cic Carrier identification code * param cicbufsize Size of cic buffer * param npdi NP database dip indicator * return 0 success, 1 not use NP or without NP parameters, -1 failure */ int ospGetNpParam( struct sip_msg* msg, char* rn, int rnbufsize, char* cic, int cicbufsize, int* npdi) { str sv; param_hooks_t phooks; param_t* params = NULL; param_t* pit; int result = -1; if (((rn != NULL) && (rnbufsize > 0)) && ((cic != NULL) && (cicbufsize > 0)) && (npdi != NULL)) { rn[0] = '\0'; cic[0] = '\0'; *npdi = 0; if (_osp_use_np != 0) { if (parse_sip_msg_uri(msg) >= 0) { switch (msg->parsed_uri.type) { case TEL_URI_T: case TELS_URI_T: sv = msg->parsed_uri.params; break; case ERROR_URI_T: case SIP_URI_T: case SIPS_URI_T: default: sv = msg->parsed_uri.user; break; } parse_params(&sv, CLASS_ANY, &phooks, ¶ms); for (pit = params; pit; pit = pit->next) { if ((pit->name.len == OSP_RN_SIZE) && (strncasecmp(pit->name.s, OSP_RN_NAME, OSP_RN_SIZE) == 0) && (rn[0] == '\0')) { ospCopyStrToBuffer(&pit->body, rn, rnbufsize); } else if ((pit->name.len == OSP_CIC_SIZE) && (strncasecmp(pit->name.s, OSP_CIC_NAME, OSP_CIC_SIZE) == 0) && (cic[0] == '\0')) { ospCopyStrToBuffer(&pit->body, cic, cicbufsize); } else if ((pit->name.len == OSP_NPDI_SIZE) && (strncasecmp(pit->name.s, OSP_NPDI_NAME, OSP_NPDI_SIZE) == 0)) { *npdi = 1; } } if (params != NULL) { free_params(params); } if ((rn[0] != '\0') || (cic[0] != '\0') || (*npdi != 0)) { result = 0; } else { LM_DBG("without number portability parameters\n"); result = 1; } } else { LM_ERR("failed to parse Request-Line URI\n"); } } else { LM_DBG("do not use number portability\n"); result = 1; } } else { LM_ERR("bad parameters to parse number portability parameters\n"); } return result; } /* * Get operator name from Request-Line * param msg SIP message * param type Operator name type * param name Operator name buffer * param namebufsize Size of name buffer * return 0 success, 1 not use NP or without operator name, -1 failure */ int ospGetOperatorName( struct sip_msg* msg, OSPE_OPERATOR_NAME type, char* name, int namebufsize) { str sv; param_hooks_t phooks; param_t* params = NULL; param_t* pit; int result = -1; if (((name != NULL) && (namebufsize > 0))) { name[0] = '\0'; if (_osp_use_np != 0) { if (parse_sip_msg_uri(msg) >= 0) { switch (msg->parsed_uri.type) { case TEL_URI_T: case TELS_URI_T: sv = msg->parsed_uri.params; break; case ERROR_URI_T: case SIP_URI_T: case SIPS_URI_T: default: sv = msg->parsed_uri.user; break; } parse_params(&sv, CLASS_ANY, &phooks, ¶ms); for (pit = params; pit; pit = pit->next) { switch (type) { case OSPC_OPNAME_SPID: if ((pit->name.len == OSP_SPID_SIZE) && (strncasecmp(pit->name.s, OSP_SPID_NAME, OSP_SPID_SIZE) == 0) && (name[0] == '\0')) { ospCopyStrToBuffer(&pit->body, name, namebufsize); } break; case OSPC_OPNAME_OCN: if ((pit->name.len == OSP_OCN_SIZE) && (strncasecmp(pit->name.s, OSP_OCN_NAME, OSP_OCN_SIZE) == 0) && (name[0] == '\0')) { ospCopyStrToBuffer(&pit->body, name, namebufsize); } break; case OSPC_OPNAME_SPN: if ((pit->name.len == OSP_SPN_SIZE) && (strncasecmp(pit->name.s, OSP_SPN_NAME, OSP_SPN_SIZE) == 0) && (name[0] == '\0')) { ospCopyStrToBuffer(&pit->body, name, namebufsize); } break; case OSPC_OPNAME_ALTSPN: if ((pit->name.len == OSP_ALTSPN_SIZE) && (strncasecmp(pit->name.s, OSP_ALTSPN_NAME, OSP_ALTSPN_SIZE) == 0) && (name[0] == '\0')) { ospCopyStrToBuffer(&pit->body, name, namebufsize); } break; case OSPC_OPNAME_MCC: if ((pit->name.len == OSP_MCC_SIZE) && (strncasecmp(pit->name.s, OSP_MCC_NAME, OSP_MCC_SIZE) == 0) && (name[0] == '\0')) { ospCopyStrToBuffer(&pit->body, name, namebufsize); } break; case OSPC_OPNAME_MNC: if ((pit->name.len == OSP_MNC_SIZE) && (strncasecmp(pit->name.s, OSP_MNC_NAME, OSP_MNC_SIZE) == 0) && (name[0] == '\0')) { ospCopyStrToBuffer(&pit->body, name, namebufsize); } break; default: break; } } if (params != NULL) { free_params(params); } if (name[0] != '\0') { result = 0; } else { LM_DBG("without operator name\n"); result = 1; } } else { LM_ERR("failed to parse Request-Line URI\n"); } } else { LM_DBG("do not use number portability\n"); result = 1; } } else { LM_ERR("bad parameters to parse operator name\n"); } return result; } /* * Get User-Agent header * param msg SIP message * param useragent User-Agent buffer * param bufsize Size of useragent * return 0 success, -1 failure */ int ospGetUserAgent( struct sip_msg* msg, char* useragent, int bufsize) { int result = -1; if ((useragent != NULL) && (bufsize > 0)) { useragent[0] = '\0'; if (parse_headers(msg, HDR_USERAGENT_F, 0) == 0) { if ((msg->user_agent != NULL) && (msg->user_agent->body.s != NULL) && (msg->user_agent->body.len > 0)) { ospCopyStrToBuffer(&msg->user_agent->body, useragent, bufsize); } result = 0; } else { LM_ERR("failed to parse User-Agent header\n"); } } else { LM_ERR("bad paraneters to parse User-Agent header\n"); } return result; } /* * Set request Date header AVP * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure MODULE_RETURNCODE_ERROR error */ int ospSetRequestDate( struct sip_msg* msg, char* ignore1, char* ignore2) { char datebuf[OSP_STRBUF_SIZE]; str date; time_t datetime = time(NULL); int result = MODULE_RETURNCODE_TRUE; if (ospTimeToStr(datetime, datebuf, sizeof(datebuf)) == 0) { date.s = datebuf; date.len = strlen(datebuf); add_avp(_osp_reqdate_avptype | AVP_VAL_STR, _osp_reqdate_avpid, (int_str)date); } else { result = MODULE_RETURNCODE_FALSE; } return result; } opensips-2.2.2/modules/osp/sipheader.h000066400000000000000000000103731300170765700177550ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_SIPHEADER_H_ #define _OSP_MOD_SIPHEADER_H_ #include #include "../../parser/msg_parser.h" #define OSP_TOKENHEADER_NAME "P-OSP-Auth-Token" #define OSP_TOKENHEADER_SIZE 16 #define OSP_DATEHEADER_NAME "Date" #define OSP_DATEHEADER_SIZE 4 #define OSP_DATEHEADER_FORMAT "%a, %d %b %Y %H:%M:%S GMT" #define OSP_RN_NAME "rn" #define OSP_RN_SIZE 2 #define OSP_CIC_NAME "cic" #define OSP_CIC_SIZE 3 #define OSP_NPDI_NAME "npdi" #define OSP_NPDI_SIZE 4 #define OSP_SPID_NAME "spid" #define OSP_SPID_SIZE 4 #define OSP_OCN_NAME "ocn" #define OSP_OCN_SIZE 3 #define OSP_SPN_NAME "spn" #define OSP_SPN_SIZE 3 #define OSP_ALTSPN_NAME "altspn" #define OSP_ALTSPN_SIZE 6 #define OSP_MCC_NAME "mcc" #define OSP_MCC_SIZE 3 #define OSP_MNC_NAME "mnc" #define OSP_MNC_SIZE 3 #define OSP_ICID_NAME "icid-value" #define OSP_ICID_SIZE 10 int ospGetAVP(int avpid, unsigned short avptype, char* avpstr, int bufsize); void ospCopyStrToBuffer(str* source, char* buffer, int bufsize); int ospGetLocalAddress(struct sip_msg*, char*, char*); int ospGetFromDisplay(struct sip_msg* msg, char* fromdisplay, int bufsize); int ospGetFromUser(struct sip_msg* msg, char* fromuser, int bufsize); int ospGetFromUri(struct sip_msg* msg, char* fromuri, int bufsize); int ospGetToDisplay(struct sip_msg* msg, char* todisplay, int bufsize); int ospGetToUser(struct sip_msg* msg, char* touser, int bufsize); int ospGetToHost(struct sip_msg* msg, char* tohost, int bufsize); int ospGetToUri(struct sip_msg* msg, char* touri, int bufsize); int ospGetPaiUser(struct sip_msg* msg, char* paiuser, int bufsize); int ospGetRpidUser(struct sip_msg* msg, char* rpiduser, int bufsize); int ospGetPciUser(struct sip_msg* msg, char* paiuser, int bufsize); int ospGetDiversion(struct sip_msg* msg, char* user, int userbufsize, char* host, int hostbufsize); int ospGetPcvIcid(struct sip_msg* msg, char* pcvicid, int bufsize); int ospGetUriUser(struct sip_msg* msg, char* uriuser, int bufsize); int ospAddOspToken(struct sip_msg* msg, unsigned char* token, unsigned int tokensize); int ospGetOspToken(struct sip_msg* msg, unsigned char* token, unsigned int* tokensize); int ospGetViaAddress(struct sip_msg* msg, char* srcaddr, int bufsize); int ospGetSrcDev(struct sip_msg* msg, char* srcaddr, int bufsize); int ospGetSource(struct sip_msg* msg, char* srcaddr, int bufsize); int ospGetCallId(struct sip_msg* msg, OSPT_CALL_ID** callid); int ospGetRouteParam(struct sip_msg* msg, char* routeparams, int bufsize); int ospRebuildDestUri(str* newuri, osp_dest* dest); int ospGetNextHop(struct sip_msg* msg, char* nexthop, int bufsize); int ospGetNpParam(struct sip_msg* msg, char* rn, int rnbufsize, char* cic, int cicbufsize, int* npdi); int ospGetOperatorName(struct sip_msg* msg, OSPE_OPERATOR_NAME type, char* name, int namebufsize); int ospGetUserAgent(struct sip_msg* msg, char* useragent, int bufsize); int ospSetRequestDate(struct sip_msg* msg, char*, char*); #endif /* _OSP_MOD_SIPHEADER_H_ */ opensips-2.2.2/modules/osp/term_transaction.c000066400000000000000000000150051300170765700213550ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "osp_mod.h" #include "term_transaction.h" #include "destination.h" #include "sipheader.h" #include "osptoolkit.h" #include "usage.h" extern char _osp_in_device[]; extern int _osp_token_format; extern int _osp_validate_callid; extern OSPTPROVHANDLE _osp_provider; /* * Get OSP token * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospCheckHeader( struct sip_msg* msg, char* ignore1, char* ignore2) { unsigned char buffer[OSP_TOKENBUF_SIZE]; unsigned int bufsize = sizeof(buffer); if (ospGetOspToken(msg, buffer, &bufsize) != 0) { return MODULE_RETURNCODE_FALSE; } else { return MODULE_RETURNCODE_TRUE; } } /* * Validate OSP token * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure MODULE_RETURNCODE_ERROR error */ int ospValidateHeader( struct sip_msg* msg, char* ignore1, char* ignore2) { int errorcode; OSPTTRANHANDLE transaction = -1; unsigned int authorized = 0; unsigned int timelimit = 0; void* detaillog = NULL; unsigned int logsize = 0; unsigned char* callidval = (unsigned char*)""; OSPT_CALL_ID* callid = NULL; unsigned callidsize = 0; unsigned char token[OSP_TOKENBUF_SIZE]; unsigned int tokensize = sizeof(token); osp_inbound inbound; osp_dest dest; int result = MODULE_RETURNCODE_FALSE; ospInitInboundInfo(&inbound); ospInitDestination(&dest); if ((errorcode = OSPPTransactionNew(_osp_provider, &transaction) != OSPC_ERR_NO_ERROR)) { LM_ERR("failed to create a new OSP transaction handle (%d)\n", errorcode); } else if (ospGetFromUser(msg, dest.calling, sizeof(dest.calling)) != 0) { LM_ERR("failed to extract calling number\n"); } else if ((ospGetUriUser(msg, dest.called, sizeof(dest.called)) != 0) && (ospGetToUser(msg, dest.called, sizeof(dest.called)) != 0)) { LM_ERR("failed to extract called number\n"); } else if (ospGetCallId(msg, &callid) != 0) { LM_ERR("failed to extract call id\n"); } else if (ospGetViaAddress(msg, inbound.source, sizeof(inbound.source)) != 0) { LM_ERR("failed to extract source device address\n"); } else if (ospGetOspToken(msg, token, &tokensize) != 0) { LM_ERR("failed to extract OSP authorization token\n"); } else { LM_INFO("validate token for: " "transaction_handle '%d' " "e164_source '%s' " "e164_dest '%s' " "validate_call_id '%s' " "call_id '%.*s'\n", transaction, dest.calling, dest.called, _osp_validate_callid == 0 ? "No" : "Yes", callid->Length, callid->Value); if (_osp_validate_callid != 0) { callidsize = callid->Length; callidval = callid->Value; } errorcode = OSPPTransactionValidateAuthorisation( transaction, "", "", "", "", dest.calling, OSPC_NFORMAT_E164, dest.called, OSPC_NFORMAT_E164, callidsize, callidval, tokensize, token, &authorized, &timelimit, &logsize, detaillog, _osp_token_format); if ((errorcode == OSPC_ERR_NO_ERROR) && (authorized == 1)) { if (callid->Length > sizeof(dest.callid) - 1) { dest.callidsize = sizeof(dest.callid) - 1; } else { dest.callidsize = callid->Length; } memcpy(dest.callid, callid->Value, dest.callidsize); dest.callid[dest.callidsize] = 0; dest.transid = ospGetTransactionId(transaction); dest.type = OSPC_ROLE_DESTINATION; inbound.authtime = time(NULL); strncpy(dest.host, _osp_in_device, sizeof(dest.host)); dest.host[sizeof(dest.host) - 1] = '\0'; if (ospSaveTermDestination(&dest) == -1) { LM_ERR("failed to save terminate destination\n"); ospRecordEvent(0, 500); result = MODULE_RETURNCODE_ERROR; } else { LM_DBG("call is authorized for %d seconds, call_id '%.*s' transaction_id '%llu'", timelimit, dest.callidsize, dest.callid, dest.transid); ospRecordTermTransaction(msg, &inbound, &dest); result = MODULE_RETURNCODE_TRUE; } } else { LM_ERR("token is invalid (%d)\n", errorcode); /* * Update terminating status code to 401 and report terminating setup usage. * We may need to make 401 configurable, just in case a user decides to reply with * a different code. Other options - trigger call setup usage reporting from the cpl * (after replying with an error code), or maybe use a different tm callback. */ ospRecordEvent(0, 401); result = MODULE_RETURNCODE_FALSE; } } if (transaction != -1) { OSPPTransactionDelete(transaction); } if (callid != NULL) { OSPPCallIdDelete(&callid); } return result; } opensips-2.2.2/modules/osp/term_transaction.h000066400000000000000000000027201300170765700213620ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_TERM_TRANSACTION_H_ #define _OSP_MOD_TERM_TRANSACTION_H_ #include "../../parser/msg_parser.h" int ospCheckHeader(struct sip_msg*, char*, char*); int ospValidateHeader(struct sip_msg*, char*, char*); #endif /* _OSP_MOD_TERM_TRANSACTION_H_ */ opensips-2.2.2/modules/osp/timeapi.c000066400000000000000000000060411300170765700174310ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2016-01-25 Time related functions. */ #define _XOPEN_SOURCE 600 /* glibc2 on linux, bsd */ #define _XOPEN_SOURCE_EXTENDED 1 /* solaris */ /** * * _XOPEN_SOURCE creates conflict in swab definition in Solaris * */ #ifdef __OS_solaris #undef _XOPEN_SOURCE #endif #include #undef _XOPEN_SOURCE #undef _XOPEN_SOURCE_EXTENDED #include #include #include "../../dprint.h" #include "timeapi.h" #define OSP_DATEHEADER_FORMAT "%a, %d %b %Y %H:%M:%S GMT" /* * Converter time string to Unix time * param timestr Time string * param timeval Unix time value * return 0 success, -1 failure */ int ospStrToTime( const char* timestr, time_t* timeval) { struct tm tmdate; char* tz; int result = -1; if (timestr != NULL && (timeval != NULL)) { *timeval = 0; if(strptime(timestr, OSP_DATEHEADER_FORMAT, &tmdate)) { tz = getenv("TZ"); setenv("TZ", "", 1); tzset(); *timeval = mktime(&tmdate); if (tz) { setenv("TZ", tz, 1); } else { unsetenv("TZ"); } tzset(); result = 0; } else { LM_ERR("failed to parse time string\n"); } } else { LM_ERR("wromg arguments\n"); } return result; } /* * Converter Unix time to time string * param timeval Unix time value * param timestr Time string * return 0 success, -1 failure */ int ospTimeToStr( time_t timeval, char* timestr, int bufsize) { struct tm* tmdate; int result = 0; tmdate = gmtime(&timeval); if ((tmdate == NULL) || (strftime(timestr, bufsize, OSP_DATEHEADER_FORMAT, tmdate) == 0)) { LM_ERR("failed to convert time to string\n"); result = -1; } return result; } opensips-2.2.2/modules/osp/timeapi.h000066400000000000000000000026371300170765700174450ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_TIMEFUNC_H_ #define _OSP_MOD_TIMEFUNC_H_ int ospStrToTime(const char* timestr, time_t* timeval); int ospTimeToStr(time_t timeval, char* timestr, int bufsize); #endif /* _OSP_MOD_TIMEFUNC_H_ */ opensips-2.2.2/modules/osp/tm.c000066400000000000000000000074251300170765700164300ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2006-03-13 TM functions are loaded via API function (bogdan) */ #include "../tm/tm_load.h" #include "tm.h" #include "destination.h" struct tm_binds osp_tmb; static void ospOnReq(struct cell* t, int type, struct tmcb_params* ps); static void ospTmcbFunc(struct cell* t, int type, struct tmcb_params* ps); /* * Load TM API * return 0 success, -1 failure */ int ospInitTm(void) { if (load_tm_api(&osp_tmb) != 0) { LM_ERR("failed to load TM API\n"); LM_ERR("TM is required for reporting call setup usage\n"); return -1; } /* Register callbacks, listen for all incoming requests */ if (osp_tmb.register_tmcb(0, 0, TMCB_REQUEST_IN, ospOnReq, 0, 0) <= 0) { LM_ERR("failed to register TMCB_REQUEST_IN callback\n"); LM_ERR("TM callbacks are required for reporting call set up usage\n"); return -1; } return 0; } /* * Register OSP callback function * param t * param type * param ps */ static void ospOnReq( struct cell* t, int type, struct tmcb_params* ps) { int tmcb_types; /* install addaitional handlers */ tmcb_types = // TMCB_REQUEST_FWDED | // TMCB_RESPONSE_FWDED | TMCB_ON_FAILURE | // TMCB_LOCAL_COMPLETED | /* report on completed transactions */ TMCB_RESPONSE_OUT | /* report on missed calls */ TMCB_ON_FAILURE | /* get incoming replies ready for processing */ // TMCB_RESPONSE_IN | 0; if (osp_tmb.register_tmcb(0, t, tmcb_types, ospTmcbFunc, 0, 0) <= 0) { LM_ERR("failed to register TM callbacks\n"); LM_ERR("TM callbacks are required for reporting call setup usage\n"); return; } } /* * OSP callback function * param t * param type * param ps */ static void ospTmcbFunc( struct cell* t, int type, struct tmcb_params* ps) { if (type & TMCB_RESPONSE_OUT) { LM_DBG("RESPONSE_OUT\n"); } else if (type & TMCB_ON_FAILURE) { LM_DBG("FAILURE_RO\n"); } else if (type & TMCB_RESPONSE_IN) { LM_DBG("RESPONSE_IN\n"); } else if (type & TMCB_REQUEST_FWDED) { LM_DBG("REQUEST_FWDED\n"); } else if (type & TMCB_RESPONSE_FWDED) { LM_DBG("RESPONSE_FWDED\n"); } else if (type & TMCB_ON_FAILURE) { LM_DBG("FAILURE\n"); } else if (type & TMCB_LOCAL_COMPLETED) { LM_DBG("COMPLETED\n"); } else { LM_DBG("something else '%d'\n", type); } if (t) { ospRecordEvent(t->uac[t->first_branch].last_received, t->uas.status); } else { LM_DBG("cell is empty\n"); } } opensips-2.2.2/modules/osp/tm.h000066400000000000000000000027761300170765700164410ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_TM_H_ #define _OSP_MOD_TM_H_ extern struct tm_binds osp_tmb; /* * Register for tm events and use them to record and report information * about the call set up transaction - return codes, call start, alert and * connect times, etc. */ int ospInitTm(void); #endif /* _OSP_MOD_TM_H_ */ opensips-2.2.2/modules/osp/usage.c000066400000000000000000000641751300170765700171210ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2006-03-13 RR functions are loaded via API function (bogdan) */ #include #include "../rr/api.h" #include "../../usr_avp.h" #include "../../mod_fix.h" #include "destination.h" #include "usage.h" #include "osptoolkit.h" #include "sipheader.h" #define OSP_ORIG_COOKIE "osp-o" #define OSP_TERM_COOKIE "osp-t" #define OSP_RELEASE_ORIG 0 #define OSP_RELEASE_TERM 1 /* The up case tags for the destinations may corrupt OSP cookies */ #define OSP_COOKIE_TRANSID 't' #define OSP_COOKIE_TRANSIDUP 'T' #define OSP_COOKIE_SRCIP 'i' #define OSP_COOKIE_SRCIPUP 'I' #define OSP_COOKIE_AUTHTIME 'a' #define OSP_COOKIE_AUTHTIMEUP 'A' #define OSP_COOKIE_DCOUNT 'c' #define OSP_COOKIE_DCOUNTUP 'C' #define OSP_COOKIE_SNID 's' #define OSP_COOKIE_SNIDUP 'S' #define OSP_COOKIE_DNID 'd' #define OSP_COOKIE_DNIDUP 'D' /* Flags for OSP cookies */ #define OSP_COOKIEHAS_TRANSID (1 << 0) #define OSP_COOKIEHAS_SRCIP (1 << 1) #define OSP_COOKIEHAS_AUTHTIME (1 << 2) #define OSP_COOKIEHAS_DSTCOUNT (1 << 3) #define OSP_COOKIEHAS_ORIGALL (OSP_COOKIEHAS_TRANSID | OSP_COOKIEHAS_SRCIP | OSP_COOKIEHAS_AUTHTIME | OSP_COOKIEHAS_DSTCOUNT) #define OSP_COOKIEHAS_TERMALL (OSP_COOKIEHAS_TRANSID | OSP_COOKIEHAS_SRCIP | OSP_COOKIEHAS_AUTHTIME) /* Flags for reporting network ID */ #define OSP_REPORT_SNID (1<<0) #define OSP_REPORT_DNID (1<<1) #define OSP_OPENSIPS "opensips" extern int _osp_report_nid; extern int _osp_origdest_avpid; extern char _osp_in_device[]; extern char _osp_out_device[]; extern OSPTPROVHANDLE _osp_provider; extern str OSP_ORIGDEST_NAME; extern struct rr_binds osp_rr; static void ospRecordTransaction(struct sip_msg* msg, osp_inbound* inbound, osp_dest* dest, int isorig); static int ospBuildUsageFromDestination(OSPTTRANHANDLE transaction, osp_inbound* inbound, osp_dest* dest, int lastcode); static int ospReportUsageFromDestination(OSPTTRANHANDLE transaction, osp_inbound* inbound, osp_dest* dest); static int ospReportUsageFromCookie(struct sip_msg* msg, char* cooky, OSPT_CALL_ID* callid, int release, OSPE_ROLE type); /* * Create OSP cookie and insert it into Record-Route header * param msg SIP message * param tansid Transaction ID * param inbound Inbound info * param dest Destination * param isorig Originate / Terminate */ static void ospRecordTransaction( struct sip_msg* msg, osp_inbound* inbound, osp_dest* dest, int isorig) { str cookie; char buffer1[OSP_STRBUF_SIZE]; char buffer2[OSP_STRBUF_SIZE]; if (osp_rr.add_rr_param == 0) { LM_WARN("add_rr_param function is not found, cannot record information about the OSP transaction\n"); return; } cookie.s = buffer1; if (isorig == 1) { cookie.len = snprintf( buffer1, sizeof(buffer1), ";%s=%c%llu_%c%s_%c%d_%c%d", OSP_ORIG_COOKIE, OSP_COOKIE_TRANSID, dest->transid, OSP_COOKIE_SRCIP, inbound->srcdev, OSP_COOKIE_AUTHTIME, (unsigned int)inbound->authtime, OSP_COOKIE_DCOUNT, dest->destcount); } else { cookie.len = snprintf( buffer1, sizeof(buffer1), ";%s=%c%llu_%c%s_%c%d", OSP_TERM_COOKIE, OSP_COOKIE_TRANSID, dest->transid, OSP_COOKIE_SRCIP, inbound->source, OSP_COOKIE_AUTHTIME, (unsigned int)inbound->authtime); } if ((_osp_report_nid & OSP_REPORT_SNID) && inbound->snid[0]) { cookie.len = snprintf( buffer2, sizeof(buffer2), "%s_%c%s", buffer1, OSP_COOKIE_SNID, inbound->snid); strncpy(buffer1, buffer2, sizeof(buffer1)); buffer1[sizeof(buffer1) - 1] = '\0'; } if ((_osp_report_nid & OSP_REPORT_DNID) && dest->dnid[0]) { cookie.len = snprintf( buffer2, sizeof(buffer2), "%s_%c%s", buffer1, OSP_COOKIE_DNID, dest->dnid); strncpy(buffer1, buffer2, sizeof(buffer1)); buffer1[sizeof(buffer1) - 1] = '\0'; } if (cookie.len < 0) { LM_ERR("failed to create OSP cookie\n"); return; } LM_DBG("adding RR parameter '%s'\n", buffer1); osp_rr.add_rr_param(msg, &cookie); } /* * Create OSP originate cookie and insert it into Record-Route header * param msg SIP message * param inbound Inbound info * param dest Destination */ void ospRecordOrigTransaction( struct sip_msg* msg, osp_inbound* inbound, osp_dest* dest) { int isorig = 1; ospRecordTransaction(msg, inbound, dest, isorig); } /* * Create OSP terminate cookie and insert it into Record-Route header * param msg SIP message * param inbound Inbound info * param dest Destination */ void ospRecordTermTransaction( struct sip_msg* msg, osp_inbound* inbound, osp_dest* dest) { int isorig = 0; ospRecordTransaction(msg, inbound, dest, isorig); } /* * Report OSP usage from OSP cookie * param msg SIP message * param cookie OSP cookie (buffer owned by ospReportUsage, can be modified) * param callid Call ID * param release Who releases the call first. 0 orig, 1 term * param type Usage type * return */ static int ospReportUsageFromCookie( struct sip_msg* msg, char* cookie, OSPT_CALL_ID* callid, OSPE_RELEASE release, OSPE_ROLE type) { char* tmp; char* token; char tag; char* value; unsigned long long transid = 0; time_t authtime = 0; unsigned destcount = 0; time_t duration = 0; time_t endtime = time(NULL); int cookieflags = 0; unsigned releasecode; char firstvia[OSP_STRBUF_SIZE]; char fromdisplay[OSP_STRBUF_SIZE]; char fromuser[OSP_STRBUF_SIZE]; char todisplay[OSP_STRBUF_SIZE]; char touser[OSP_STRBUF_SIZE]; char paiuser[OSP_STRBUF_SIZE]; char rpiduser[OSP_STRBUF_SIZE]; char pciuser[OSP_STRBUF_SIZE]; char divuser[OSP_STRBUF_SIZE]; char divhost[OSP_STRBUF_SIZE]; char pcvicid[OSP_STRBUF_SIZE]; char nexthop[OSP_STRBUF_SIZE]; char* snid = NULL; char* dnid = NULL; char* display; char* calling; char* called; char* originator = NULL; char* terminator; char source[OSP_STRBUF_SIZE]; char dest[OSP_STRBUF_SIZE]; char srcdev[OSP_STRBUF_SIZE]; char receive[OSP_STRBUF_SIZE]; char* ingress = NULL; char* egress = NULL; OSPTTRANHANDLE transaction = -1; int errorcode; LM_DBG("cookie '%s' type '%d'\n", cookie == NULL ? "NULL" : cookie, type); if (cookie != NULL) { for (token = strtok_r(cookie, "_", &tmp); token; token = strtok_r(NULL, "_", &tmp)) { tag = *token; value= token + 1; switch (tag) { case OSP_COOKIE_TRANSID: case OSP_COOKIE_TRANSIDUP: transid = atoll(value); cookieflags |= OSP_COOKIEHAS_TRANSID; break; case OSP_COOKIE_AUTHTIME: case OSP_COOKIE_AUTHTIMEUP: authtime = atoi(value); duration = endtime - authtime; cookieflags |= OSP_COOKIEHAS_AUTHTIME; break; case OSP_COOKIE_SRCIP: case OSP_COOKIE_SRCIPUP: originator = value; cookieflags |= OSP_COOKIEHAS_SRCIP; break; case OSP_COOKIE_DCOUNT: case OSP_COOKIE_DCOUNTUP: destcount = (unsigned)atoi(value); cookieflags |= OSP_COOKIEHAS_DSTCOUNT; break; case OSP_COOKIE_SNID: case OSP_COOKIE_SNIDUP: snid = value; break; case OSP_COOKIE_DNID: case OSP_COOKIE_DNIDUP: dnid = value; break; default: LM_ERR("unexpected tag '%c' / value '%s'\n", tag, value); break; } } } switch (type) { case OSPC_ROLE_DESTINATION: if (cookieflags == OSP_COOKIEHAS_TERMALL) { releasecode = 10016; } else { releasecode = 9016; } break; case OSPC_ROLE_SOURCE: case OSPC_ROLE_OTHER: case OSPC_ROLE_UNDEFINED: default: if (cookieflags == OSP_COOKIEHAS_ORIGALL) { releasecode = 10016; } else { releasecode = 9016; } break; } if (releasecode == 9016) { transid = 0; originator = NULL; authtime = 0; duration = 0; destcount = 0; } if(msg->rcv.bind_address && msg->rcv.bind_address->address_str.s) { ospCopyStrToBuffer(&msg->rcv.bind_address->address_str, receive, sizeof(receive)); } ospGetViaAddress(msg, firstvia, sizeof(firstvia)); ospGetFromDisplay(msg, fromdisplay, sizeof(fromdisplay)); ospGetFromUser(msg, fromuser, sizeof(fromuser)); ospGetToDisplay(msg, todisplay, sizeof(todisplay)); ospGetToUser(msg, touser, sizeof(touser)); ospGetPaiUser(msg, paiuser, sizeof(paiuser)); ospGetRpidUser(msg, rpiduser, sizeof(rpiduser)); ospGetPciUser(msg, pciuser, sizeof(pciuser)); ospGetDiversion(msg, divuser, sizeof(divuser), divhost, sizeof(divhost)); ospGetPcvIcid(msg, pcvicid, sizeof(pcvicid)); ospGetNextHop(msg, nexthop, sizeof(nexthop)); LM_DBG("first via '%s' from '%s' to '%s' next hop '%s'\n", firstvia, fromuser, touser, nexthop); if (release == OSPC_RELEASE_DESTINATION) { LM_DBG("term '%s' released the call, call_id '%.*s' transaction_id '%llu'\n", firstvia, callid->Length, callid->Value, transid); if (originator == NULL) { originator = nexthop; } display = todisplay; calling = touser; called = fromuser; terminator = firstvia; egress = receive; } else { if (release == OSPC_RELEASE_SOURCE) { LM_DBG("orig '%s' released the call, call_id '%.*s' transaction_id '%llu'\n", firstvia, callid->Length, callid->Value, transid); } else { LM_DBG("unknown '%s' released the call, call_id '%.*s' transaction_id '%llu'\n", firstvia, callid->Length, callid->Value, transid); } if (originator == NULL) { originator = firstvia; } display = fromdisplay; calling = fromuser; called = touser; terminator = nexthop; ingress = receive; } errorcode = OSPPTransactionNew(_osp_provider, &transaction); LM_DBG("created transaction handle '%d' (%d)\n", transaction, errorcode); switch (type) { case OSPC_ROLE_DESTINATION: srcdev[0] = '\0'; ospConvertToOutAddress(originator, source, sizeof(source)); strncpy(dest, _osp_out_device, sizeof(dest)); dest[sizeof(dest) - 1] = '\0'; break; case OSPC_ROLE_SOURCE: case OSPC_ROLE_OTHER: case OSPC_ROLE_UNDEFINED: default: ospConvertToOutAddress(originator, srcdev, sizeof(srcdev)); strncpy(source, _osp_out_device, sizeof(source)); source[sizeof(source) - 1] = '\0'; ospConvertToOutAddress(terminator, dest, sizeof(dest)); break; } /* RoleInfo must be set before BuildUsageFromScratch */ OSPPTransactionSetRoleInfo(transaction, OSPC_RSTATE_STOP, OSPC_RFORMAT_OSP, OSPC_RVENDOR_OPENSIPS); errorcode = OSPPTransactionBuildUsageFromScratch( transaction, transid, type, source, dest, srcdev, "", calling, OSPC_NFORMAT_E164, called, OSPC_NFORMAT_E164, callid->Length, callid->Value, 0, NULL, NULL); LM_DBG("built usage handle '%d' (%d)\n", transaction, errorcode); if ((errorcode == OSPC_ERR_NO_ERROR) && (destcount > 0)) { errorcode = OSPPTransactionSetDestinationCount( transaction, destcount); errorcode = OSPPTransactionSetTotalSetupAttempts( transaction, destcount); } if (errorcode == OSPC_ERR_NO_ERROR) { OSPPTransactionSetProtocol(transaction, OSPC_PROTTYPE_SOURCE, OSPC_PROTNAME_SIP); OSPPTransactionSetProtocol(transaction, OSPC_PROTTYPE_DESTINATION, OSPC_PROTNAME_SIP); OSPPTransactionSetSrcNetworkId(transaction, snid); OSPPTransactionSetDestNetworkId(transaction, dnid); OSPPTransactionSetSIPHeader(transaction, OSPC_SIPHEADER_FROM, OSPC_NFORMAT_DISPLAYNAME, display); OSPPTransactionSetSIPHeader(transaction, OSPC_SIPHEADER_RPID, OSPC_NFORMAT_E164, rpiduser); OSPPTransactionSetSIPHeader(transaction, OSPC_SIPHEADER_PAI, OSPC_NFORMAT_E164, paiuser); OSPPTransactionSetSIPHeader(transaction, OSPC_SIPHEADER_PCI, OSPC_NFORMAT_E164, pciuser); OSPPTransactionSetDiversion(transaction, divuser, divhost); OSPPTransactionSetChargingVector(transaction, pcvicid, NULL, NULL, NULL); OSPPTransactionSetProxyIngressAddr(transaction, ingress); OSPPTransactionSetProxyEgressAddr(transaction, egress); OSPPTransactionSetCDRProxy(transaction, _osp_in_device, OSP_OPENSIPS, NULL); ospReportUsageWrapper( transaction, releasecode, duration, authtime, endtime, 0, 0, 0, 0, release); } return errorcode; } /* * Report OSP usage * param msg SIP message * param whorelease Who releases the call first, 0 orig, 1 term * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospReportUsage( struct sip_msg* msg, char* whorelease, char* ignore2) { str relstr; OSPE_RELEASE release; char* tmp; char* token; char parameters[OSP_HEADERBUF_SIZE]; OSPT_CALL_ID* callid = NULL; int result = MODULE_RETURNCODE_FALSE; ospGetCallId(msg, &callid); if (callid != NULL) { /* Who releases the call first, 0 orig, 1 term */ if (fixup_get_svalue(msg, (gparam_p)whorelease, &relstr) == 0) { if ((relstr.len <= 0) || (sscanf(relstr.s, "%d", &release) != 1) || ((release != OSPC_RELEASE_SOURCE) && (release != OSPC_RELEASE_DESTINATION))) { release = OSPC_RELEASE_UNKNOWN; } LM_DBG("who releases the call first '%d'\n", release); if (ospGetRouteParam(msg, parameters, sizeof(parameters)) == 0) { for (token = strtok_r(parameters, ";", &tmp); token; token = strtok_r(NULL, ";", &tmp)) { if ((strncmp(token, OSP_ORIG_COOKIE, strlen(OSP_ORIG_COOKIE)) == 0) && (token[strlen(OSP_ORIG_COOKIE)] == '=')) { LM_INFO("report orig duration for call_id '%.*s'\n", callid->Length, callid->Value); ospReportUsageFromCookie(msg, token + strlen(OSP_ORIG_COOKIE) + 1, callid, release, OSPC_ROLE_SOURCE); result = MODULE_RETURNCODE_TRUE; } else if ((strncmp(token, OSP_TERM_COOKIE, strlen(OSP_TERM_COOKIE)) == 0) && (token[strlen(OSP_TERM_COOKIE)] == '=')) { LM_INFO("report term duration for call_id '%.*s'\n", callid->Length, callid->Value); ospReportUsageFromCookie(msg, token + strlen(OSP_TERM_COOKIE) + 1, callid, release, OSPC_ROLE_DESTINATION); result = MODULE_RETURNCODE_TRUE; } else { LM_DBG("ignoring parameter '%s'\n", token); } } } if (result == MODULE_RETURNCODE_FALSE) { LM_DBG("without orig or term OSP information\n"); LM_INFO("report other duration for call_id '%.*s'\n", callid->Length, callid->Value); ospReportUsageFromCookie(msg, NULL, callid, release, OSPC_ROLE_SOURCE); result = MODULE_RETURNCODE_TRUE; } } OSPPCallIdDelete(&callid); } if (result == MODULE_RETURNCODE_FALSE) { LM_ERR("failed to report usage\n"); } return result; } /* * Build OSP usage from destination * param transaction OSP transaction handle * param inbound Inbound info * param dest Destination * param lastcode Destination status * return 0 success, others failure */ static int ospBuildUsageFromDestination( OSPTTRANHANDLE transaction, osp_inbound* inbound, osp_dest* dest, int lastcode) { int errorcode; char srcdev[OSP_STRBUF_SIZE]; char source[OSP_STRBUF_SIZE]; char host[OSP_STRBUF_SIZE]; char destdev[OSP_STRBUF_SIZE]; ospConvertToOutAddress(inbound->srcdev, srcdev, sizeof(srcdev)); ospConvertToOutAddress(inbound->source, source, sizeof(source)); ospConvertToOutAddress(dest->host, host, sizeof(host)); ospConvertToOutAddress(dest->destdev, destdev, sizeof(destdev)); /* Must be called before BuildUsageFromScratch */ OSPPTransactionSetSrcSwitchId(transaction, inbound->swid); errorcode = OSPPTransactionBuildUsageFromScratch( transaction, dest->transid, dest->type, source, host, srcdev, destdev, dest->calling, OSPC_NFORMAT_E164, inbound->called, /* Report original called number */ OSPC_NFORMAT_E164, dest->callidsize, dest->callid, lastcode, NULL, NULL); OSPPTransactionSetSrcNetworkId(transaction, inbound->snid); OSPPTransactionSetDestNetworkId(transaction, dest->dnid); OSPPTransactionSetDestSwitchId(transaction, dest->swid); OSPPTransactionSetDestAudioAddr(transaction, dest->destmedia); OSPPTransactionSetProxyEgressAddr(transaction, dest->egress); if (dest->starttime && dest->endtime && (dest->starttime <= dest->endtime)) { OSPPTransactionSetProviderPDD(transaction, (dest->endtime - dest->starttime) * 1000); } return errorcode; } /* * Report OSP usage from destination * param transaction OSP transaction handle * param inbound Inbound info * param dest Destination * return 0 success */ static int ospReportUsageFromDestination( OSPTTRANHANDLE transaction, osp_inbound* inbound, osp_dest* dest) { OSPPTransactionSetSIPHeader(transaction, OSPC_SIPHEADER_FROM, OSPC_NFORMAT_DISPLAYNAME, inbound->fromdisplay); OSPPTransactionSetSIPHeader(transaction, OSPC_SIPHEADER_FROM, OSPC_NFORMAT_DISPLAYNAME, inbound->fromdisplay); OSPPTransactionSetSIPHeader(transaction, OSPC_SIPHEADER_RPID, OSPC_NFORMAT_E164, inbound->rpiduser); OSPPTransactionSetSIPHeader(transaction, OSPC_SIPHEADER_PAI, OSPC_NFORMAT_E164, inbound->paiuser); OSPPTransactionSetSIPHeader(transaction, OSPC_SIPHEADER_PCI, OSPC_NFORMAT_E164, inbound->pciuser); OSPPTransactionSetDiversion(transaction, inbound->divuser, inbound->divhost); OSPPTransactionSetChargingVector(transaction, inbound->pcvicid, NULL, NULL, NULL); OSPPTransactionSetSrcAudioAddr(transaction, inbound->srcmedia); OSPPTransactionSetProxyIngressAddr(transaction, inbound->ingressaddr); OSPPTransactionSetCDRProxy(transaction, _osp_in_device, OSP_OPENSIPS, NULL); ospReportUsageWrapper( transaction, /* In - Transaction handle */ dest->lastcode, /* In - Release Code */ 0, /* In - Length of call */ inbound->authtime, /* In - Call start time */ 0, /* In - Call end time */ dest->time180, /* In - Call alert time */ dest->time200, /* In - Call connect time */ dest->time180 ? 1 : 0, /* In - Is PDD Info present */ dest->time180 ? dest->time180 - inbound->authtime : 0, /* In - Post Dial Delay */ ((dest->lastcode == 200) || (dest->lastcode == 300)) ? OSPC_RELEASE_UNKNOWN : OSPC_RELEASE_INTERNAL); return 0; } /* * Report originate call setup usage */ void ospReportOrigSetupUsage(void) { osp_inbound* inbound = ospGetInboundInfo(); struct usr_avp* destavp = NULL; int_str destval; osp_dest* dest = NULL; osp_dest* lastused = NULL; OSPTTRANHANDLE trans = -1; int lastcode = 0; OSPE_ROLE_STATE rstate; int errcode; errcode = OSPPTransactionNew(_osp_provider, &trans); if (errcode != OSPC_ERR_NO_ERROR) { return; } if (inbound == NULL) { return; } for (destavp = search_first_avp(AVP_VAL_STR, _osp_origdest_avpid, NULL, 0); destavp != NULL; destavp = search_next_avp(destavp, NULL)) { get_avp_val(destavp, &destval); /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; if (dest->used == 1) { LM_DBG("iterating through used destination\n"); if (dest->reported == 1) { LM_DBG("orig setup already reported\n"); break; } else { dest->reported = 1; ospDumpDestination(dest); lastused = dest; if (dest->lastcode == 200) { rstate = OSPC_RSTATE_START; } else if (dest->lastcode == 300) { rstate = OSPC_RSTATE_REDIRECT; } else { rstate = OSPC_RSTATE_STOP; } /* RoleInfo must be set before BuildUsageFromScratch */ OSPPTransactionSetRoleInfo(trans, rstate, OSPC_RFORMAT_OSP, OSPC_RVENDOR_OPENSIPS); ospBuildUsageFromDestination(trans, inbound, dest, lastcode); OSPPTransactionSetProtocol(trans, OSPC_PROTTYPE_SOURCE, OSPC_PROTNAME_SIP); OSPPTransactionSetProtocol(trans, OSPC_PROTTYPE_DESTINATION, dest->protocol); lastcode = dest->lastcode; } } else { LM_DBG("destination has not been used, breaking out\n"); break; } } if (lastused) { LM_INFO("report orig setup for call_id '%.*s' transaction_id '%llu'\n", lastused->callidsize, lastused->callid, lastused->transid); ospReportUsageFromDestination(trans, inbound, lastused); } else { /* If a Toolkit transaction handle was created, but we did not find * any destinations to report, we need to release the handle. Otherwise, * the ospReportUsageFromDestination will release it. */ OSPPTransactionDelete(trans); } } /* * Report terminate call setup usage */ void ospReportTermSetupUsage(void) { osp_inbound* inbound = ospGetInboundInfo(); osp_dest* dest = ospGetTermDestination(); OSPTTRANHANDLE trans = -1; OSPE_ROLE_STATE rstate; int errorcode; if (inbound == NULL) { if (dest == NULL) { if (dest->reported == 0) { dest->reported = 1; LM_INFO("report term setup for call_id '%.*s' transaction_id '%llu'\n", dest->callidsize, dest->callid, dest->transid); errorcode = OSPPTransactionNew(_osp_provider, &trans); if (errorcode == OSPC_ERR_NO_ERROR) { if (dest->lastcode == 200) { rstate = OSPC_RSTATE_START; } else if (dest->lastcode == 300) { rstate = OSPC_RSTATE_REDIRECT; } else { rstate = OSPC_RSTATE_STOP; } /* RoleInfo must be set before BuildUsageFromScratch */ OSPPTransactionSetRoleInfo(trans, rstate, OSPC_RFORMAT_OSP, OSPC_RVENDOR_OPENSIPS); ospBuildUsageFromDestination(trans, inbound, dest, 0); OSPPTransactionSetProtocol(trans, OSPC_PROTTYPE_DESTINATION, OSPC_PROTNAME_SIP); ospReportUsageFromDestination(trans, inbound, dest); } } else { LM_DBG("term setup already reported\n"); } } else { LM_ERR("without term setup to report\n"); } } else { LM_ERR("internal error\n"); } } opensips-2.2.2/modules/osp/usage.h000066400000000000000000000045701300170765700171170ustar00rootroot00000000000000/* * opensips osp module. * * This module enables opensips to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OSP_MOD_USAGE_H_ #define _OSP_MOD_USAGE_H_ #include #include "../../parser/msg_parser.h" /* This module reports originating and terminating call set up and duration usage * for OSP transactions. * * Call set-up usage is reported based on the osp_dest structures stored as AVPs. * It includes OSP transaction id, response codes, start time, alert time, * connect time, etc. * * Duration usage is reported based on the OSP cooky recorded into the route set * (using add_rr_param) after requesting routing/authorization on the originating * side, and validating authorization on the terminating side. It include * OSP transaction id, duration, stop time, etc. * * Actual conversation duration maybe calculated using connect time (from the call * set up usage) and stop time (from the duration usage). */ void ospRecordOrigTransaction(struct sip_msg* msg, osp_inbound* inbound, osp_dest* dest); void ospRecordTermTransaction(struct sip_msg* msg, osp_inbound* inbound, osp_dest* dest); void ospReportOrigSetupUsage(void); void ospReportTermSetupUsage(void); int ospReportUsage(struct sip_msg* msg, char* whorelease, char* ignore2); #endif /* _OSP_MOD_USAGE_H_ */ opensips-2.2.2/modules/path/000077500000000000000000000000001300170765700157675ustar00rootroot00000000000000opensips-2.2.2/modules/path/Makefile000066400000000000000000000003141300170765700174250ustar00rootroot00000000000000# $Id$ # # path module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=path.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/path/README000066400000000000000000000140251300170765700166510ustar00rootroot00000000000000path Module Andreas Granig Inode GmbH Edited by Andreas Granig Copyright © 2006 Inode GmbH Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Path insertion for registrations 1.1.2. Outbound routing to NAT'ed UACs 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. use_received (int) 1.3.2. enable_double_path (integer) 1.4. Exported Functions 1.4.1. add_path() 1.4.2. add_path(user) 1.4.3. add_path_received() 1.4.4. add_path_received(user) List of Examples 1.1. Set use_received parameter 1.2. Set enable_double_path parameter 1.3. add_path usage 1.4. add_path(user) usage 1.5. add_path_received() usage 1.6. add_path_received(user) usage Chapter 1. Admin Guide 1.1. Overview This module is designed to be used at intermediate sip proxies like loadbalancers in front of registrars and proxies. It provides functions for inserting a Path header including a parameter for passing forward the received-URI of a registration to the next hop. It also provides a mechanism for evaluating this parameter in subsequent requests and to set the destination URI according to it. 1.1.1. Path insertion for registrations For registrations in a scenario like “[UAC] -> [P1] -> [REG]â€, the "path" module can be used at the intermediate proxy P1 to insert a Path header into the message before forwarding it to the registrar REG. Two functions can be used to achieve this: * add_path(...) adds a Path header in the form of “Path: †to the message using the address of the outgoing interface. A port is only added if it's not the default port 5060. If a username is passed to the function, it is also included in the Path URI, like “Path: â€. * add_path_received(...) also add a Path header in the same form as above, but also adds a parameter indicating the received-URI of the message, like “Path: â€. This is especially useful if the proxy does NAT detection and wants to pass the NAT'ed address to the registrar. If the function is called with a username, it's included in the Path URI too. 1.1.2. Outbound routing to NAT'ed UACs If the NAT'ed address of an UAC is passed to the registrar, the registrar routes back subsequent requests using the Path header of the registration as Route header of the current request. If the intermediate proxy had inserted a Path header including the “received†parameter during the registration, this parameter will show up in the Route header of the new request as well, allowing the intermediate proxy to route to this address instead of the one propagated in the Route URI for tunneling through NAT. This behaviour can be activated by setting the module parameter “use_receivedâ€. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * The "rr" module is needed for outbound routing according to the “received†parameter. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. use_received (int) If set to 1, the “received†parameter of the first Route URI is evaluated and used as destination-URI if present. Default value is 0. Example 1.1. Set use_received parameter ... modparam("path", "use_received", 1) ... 1.3.2. enable_double_path (integer) There are some situations when the server needs to insert two Path header fields instead of one. For example when using two disconnected networks or doing cross-protocol forwarding from UDP->TCP. This parameter enables inserting of 2 Paths. Default value is 1 (yes). Example 1.2. Set enable_double_path parameter ... modparam("path", "enable_double_path", 0) ... 1.4. Exported Functions 1.4.1. add_path() This function is used to insert a Path header in the form “Path: â€, where “1.2.3.4†is the address of the outgoing interface. This function can be used from REQUEST_ROUTE. Example 1.3. add_path usage ... if (!add_path()) { sl_send_reply("503", "Internal Path Error"); ... }; ... 1.4.2. add_path(user) This function adds a Path header in the form “Path: â€. Meaning of the parameters is as follows: * user - The username to be inserted as user part. This function can be used from REQUEST_ROUTE. Example 1.4. add_path(user) usage ... if (!add_path("loadbalancer")) { sl_send_reply("503", "Internal Path Error"); ... }; ... 1.4.3. add_path_received() This function adds a Path header in the form “Path: â€, setting it's own outgoing address as domain-part, and the address the request has been received from as received-parameter. This function can be used from REQUEST_ROUTE. Example 1.5. add_path_received() usage ... if (!add_path_received()) { sl_send_reply("503", "Internal Path Error"); ... }; ... 1.4.4. add_path_received(user) This function adds a Path header in the form “Path: â€, setting 'user' as username part of address, it's own outgoing address as domain-part, and the address the request has been received from as received-parameter. This function can be used from REQUEST_ROUTE. Example 1.6. add_path_received(user) usage ... if (!add_path_received("inbound")) { sl_send_reply("503", "Internal Path Error"); ... }; ... opensips-2.2.2/modules/path/doc/000077500000000000000000000000001300170765700165345ustar00rootroot00000000000000opensips-2.2.2/modules/path/doc/path.xml000066400000000000000000000022361300170765700202150ustar00rootroot00000000000000 %docentities; ]> path Module &osipsname; Andreas Granig Inode GmbH
andreas.granig@inode.info
Andreas Granig
andreas.granig@inode.info
2006 Inode GmbH $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/path/doc/path_admin.xml000066400000000000000000000164211300170765700213660ustar00rootroot00000000000000 &adminguide;
Overview This module is designed to be used at intermediate sip proxies like loadbalancers in front of registrars and proxies. It provides functions for inserting a Path header including a parameter for passing forward the received-&uri; of a registration to the next hop. It also provides a mechanism for evaluating this parameter in subsequent requests and to set the destination &uri; according to it.
Path insertion for registrations For registrations in a scenario like [UAC] -> [P1] -> [REG], the "path" module can be used at the intermediate proxy P1 to insert a Path header into the message before forwarding it to the registrar REG. Two functions can be used to achieve this: add_path(...) adds a Path header in the form of Path: <sip:1.2.3.4;lr> to the message using the address of the outgoing interface. A port is only added if it's not the default port 5060. If a username is passed to the function, it is also included in the Path &uri;, like Path: <sip:username@1.2.3.4;lr>. add_path_received(...) also add a Path header in the same form as above, but also adds a parameter indicating the received-&uri; of the message, like Path: <sip:1.2.3.4;received=sip:2.3.4.5:1234;lr>. This is especially useful if the proxy does NAT detection and wants to pass the NAT'ed address to the registrar. If the function is called with a username, it's included in the Path &uri; too.
Outbound routing to NAT'ed UACs If the NAT'ed address of an UAC is passed to the registrar, the registrar routes back subsequent requests using the Path header of the registration as Route header of the current request. If the intermediate proxy had inserted a Path header including the received parameter during the registration, this parameter will show up in the Route header of the new request as well, allowing the intermediate proxy to route to this address instead of the one propagated in the Route &uri; for tunneling through NAT. This behaviour can be activated by setting the module parameter use_received.
Dependencies
&osips; Modules The following modules must be loaded before this module: The "rr" module is needed for outbound routing according to the received parameter.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>use_received</varname> (int) If set to 1, the received parameter of the first Route &uri; is evaluated and used as destination-&uri; if present. Default value is 0. Set <varname>use_received</varname> parameter ... modparam("path", "use_received", 1) ...
<varname>enable_double_path</varname> (integer) There are some situations when the server needs to insert two Path header fields instead of one. For example when using two disconnected networks or doing cross-protocol forwarding from UDP->TCP. This parameter enables inserting of 2 Paths. Default value is 1 (yes). Set <varname>enable_double_path</varname> parameter ... modparam("path", "enable_double_path", 0) ...
Exported Functions
<function moreinfo="none">add_path()</function> This function is used to insert a Path header in the form Path: <sip:1.2.3.4;lr>, where 1.2.3.4 is the address of the outgoing interface. This function can be used from REQUEST_ROUTE. <function>add_path</function> usage ... if (!add_path()) { sl_send_reply("503", "Internal Path Error"); ... }; ...
<function moreinfo="none">add_path(user)</function> This function adds a Path header in the form Path: <sip:user@1.2.3.4;lr>. Meaning of the parameters is as follows: user - The username to be inserted as user part. This function can be used from REQUEST_ROUTE. <function>add_path(user)</function> usage ... if (!add_path("loadbalancer")) { sl_send_reply("503", "Internal Path Error"); ... }; ...
<function moreinfo="none">add_path_received()</function> This function adds a Path header in the form Path: <sip:1.2.3.4;received=sip:2.3.4.5:1234;lr>, setting it's own outgoing address as domain-part, and the address the request has been received from as received-parameter. This function can be used from REQUEST_ROUTE. <function>add_path_received()</function> usage ... if (!add_path_received()) { sl_send_reply("503", "Internal Path Error"); ... }; ...
<function moreinfo="none">add_path_received(user)</function> This function adds a Path header in the form Path: <sip:user@1.2.3.4;received=sip:2.3.4.5:1234;lr>, setting 'user' as username part of address, it's own outgoing address as domain-part, and the address the request has been received from as received-parameter. This function can be used from REQUEST_ROUTE. <function>add_path_received(user)</function> usage ... if (!add_path_received("inbound")) { sl_send_reply("503", "Internal Path Error"); ... }; ...
opensips-2.2.2/modules/path/path.c000066400000000000000000000203041300170765700170660ustar00rootroot00000000000000/* * Path handling for intermediate proxies. * * Copyright (C) 2006 Inode GmbH (Andreas Granig ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include "../../mem/mem.h" #include "../../data_lump.h" #include "../../parser/parse_param.h" #include "path.h" #include "path_mod.h" #define PATH_PREFIX "Path: \r\n" #define PATH_CRLF_LEN (sizeof(PATH_CRLF)-1) #define PATH_R2 ";r2=on" #define PATH_R2_LEN (sizeof(PATH_R2)-1) #define INBOUND 1 /* Insert inbound Path */ #define OUTBOUND 0 /* Insert outbound Path */ static int build_path(struct sip_msg* _m, struct lump* l, struct lump* l2, str* user, int recv, int _inbound) { char *prefix, *suffix, *crlf, *r2; int prefix_len, suffix_len; str rcv_addr = {0, 0}; char *src_ip; prefix = suffix = crlf = r2 = 0; prefix_len = PATH_PREFIX_LEN + (user->len ? (user->len+1) : 0); prefix = pkg_malloc(prefix_len); if (!prefix) { LM_ERR("no pkg memory left for prefix\n"); goto out1; } memcpy(prefix, PATH_PREFIX, PATH_PREFIX_LEN); if (user->len) { memcpy(prefix + PATH_PREFIX_LEN, user->s, user->len); memcpy(prefix + prefix_len - 1, "@", 1); } suffix_len = PATH_LR_PARAM_LEN + (recv ? PATH_RC_PARAM_LEN : 0); suffix = pkg_malloc(suffix_len); if (!suffix) { LM_ERR("no pkg memory left for suffix\n"); goto out1; } memcpy(suffix, PATH_LR_PARAM, PATH_LR_PARAM_LEN); if(recv) memcpy(suffix+PATH_LR_PARAM_LEN, PATH_RC_PARAM, PATH_RC_PARAM_LEN); crlf = pkg_malloc(PATH_CRLF_LEN); if (!crlf) { LM_ERR("no pkg memory left for crlf\n"); goto out1; } memcpy(crlf, PATH_CRLF, PATH_CRLF_LEN); r2 = pkg_malloc(PATH_R2_LEN); if (!r2) { LM_ERR("no pkg memory left for r2\n"); goto out1; } memcpy(r2, PATH_R2, PATH_R2_LEN); l = insert_new_lump_after(l, prefix, prefix_len, 0); if (!l) goto out1; l = insert_subst_lump_after(l, _inbound?SUBST_RCV_ALL:SUBST_SND_ALL, 0); if (!l) goto out2; if (enable_double_path) { if (!(l = insert_cond_lump_after(l, COND_IF_DIFF_REALMS, 0))) goto out2; if (!(l = insert_new_lump_after(l, r2, PATH_R2_LEN, 0))) goto out2; r2 = 0; } else { pkg_free(r2); r2 = 0; } l2 = insert_new_lump_before(l2, suffix, suffix_len, 0); if (!l) goto out3; if (recv) { /* TODO: agranig: optimize this one! */ src_ip = ip_addr2a(&_m->rcv.src_ip); rcv_addr.s = pkg_malloc(4 + IP_ADDR_MAX_STR_SIZE + 7 + PATH_TRANS_PARAM_LEN + 4); /* sip::[;transport=xxxx]\0 */ if(!rcv_addr.s) { LM_ERR("no pkg memory left for receive-address\n"); goto out4; } rcv_addr.len = snprintf(rcv_addr.s, 4 + IP_ADDR_MAX_STR_SIZE + 6, "sip:%s:%u", src_ip, _m->rcv.src_port); switch (_m->rcv.proto) { case PROTO_TCP: memcpy(rcv_addr.s+rcv_addr.len, PATH_TRANS_PARAM "tcp",PATH_TRANS_PARAM_LEN+3); rcv_addr.len += PATH_TRANS_PARAM_LEN + 3; break; case PROTO_TLS: memcpy(rcv_addr.s+rcv_addr.len, PATH_TRANS_PARAM "tls",PATH_TRANS_PARAM_LEN+3); rcv_addr.len += PATH_TRANS_PARAM_LEN + 3; break; case PROTO_SCTP: memcpy(rcv_addr.s+rcv_addr.len, PATH_TRANS_PARAM "sctp",PATH_TRANS_PARAM_LEN+4); rcv_addr.len += PATH_TRANS_PARAM_LEN + 4; break; } l2 = insert_new_lump_before(l2, rcv_addr.s, rcv_addr.len, 0); if (!l2) goto out4; } l2 = insert_new_lump_before(l2, crlf, CRLF_LEN+1, 0); if (!l2) goto out5; return 1; out1: if (prefix) pkg_free(prefix); out2: if (r2) pkg_free(r2); out3: if (suffix) pkg_free(suffix); out4: if (rcv_addr.s) pkg_free(rcv_addr.s); out5: if (crlf) pkg_free(crlf); LM_ERR("failed to insert prefix lump\n"); return -1; } static int prepend_path(struct sip_msg* _m, str *user, int recv) { struct lump* l, *l2; struct hdr_field *hf; if (parse_headers(_m, HDR_PATH_F, 0) < 0) { LM_ERR("failed to parse message for Path header\n"); return -1; } for (hf = _m->headers; hf; hf = hf->next) { if (hf->type == HDR_PATH_T) { break; } } if (hf) { /* path found, add ours in front of that */ l = anchor_lump(_m, hf->name.s - _m->buf, 0); l2 = anchor_lump(_m, hf->name.s - _m->buf, 0); } else { /* no path, append to message */ l = anchor_lump(_m, _m->unparsed - _m->buf, 0); l2 = anchor_lump(_m, _m->unparsed - _m->buf, 0); } if (!l || !l2) { LM_ERR("failed to get anchor\n"); return -2; } if (build_path(_m, l, l2, user, recv, OUTBOUND) < 0) { LM_ERR("failed to insert outbound Path"); return -3; } if (enable_double_path) { if (hf) { /* path found, add ours in front of that */ l = anchor_lump(_m, hf->name.s - _m->buf, 0); l2 = anchor_lump(_m, hf->name.s - _m->buf, 0); } else { /* no path, append to message */ l = anchor_lump(_m, _m->unparsed - _m->buf, 0); l2 = anchor_lump(_m, _m->unparsed - _m->buf, 0); } if (!l || !l2) { LM_ERR("failed to get anchor\n"); return -4; } l = insert_cond_lump_after(l, COND_IF_DIFF_REALMS, 0); l2 = insert_cond_lump_before(l2, COND_IF_DIFF_REALMS, 0); if (!l || !l2) { LM_ERR("failed to insert conditional lump\n"); return -5; } if (build_path(_m, l, l2, user, 0, INBOUND) < 0) { LM_ERR("failed to insert inbound Path"); return -6; } } return 1; } /* * Prepend own uri to Path header */ int add_path(struct sip_msg* _msg, char* _a, char* _b) { str user = {0,0}; return prepend_path(_msg, &user, 0); } /* * Prepend own uri to Path header and take care of given * user. */ int add_path_usr(struct sip_msg* _msg, char* _usr, char* _b) { return prepend_path(_msg, (str*)_usr, 0); } /* * Prepend own uri to Path header and append received address as * "received"-param to that uri. */ int add_path_received(struct sip_msg* _msg, char* _a, char* _b) { str user = {0,0}; return prepend_path(_msg, &user, 1); } /* * Prepend own uri to Path header and append received address as * "received"-param to that uri and take care of given user. */ int add_path_received_usr(struct sip_msg* _msg, char* _usr, char* _b) { return prepend_path(_msg, (str*)_usr, 1); } /* * rr callback */ void path_rr_callback(struct sip_msg *_m, str *r_param, void *cb_param) { param_hooks_t hooks; param_t *params; param_t *first_param; str received = {0, 0}; str transport = {0, 0}; str dst_uri = {0, 0}; if (parse_params(r_param, CLASS_ANY, &hooks, ¶ms) != 0) { LM_ERR("failed to parse route parameters\n"); return; } first_param = params; while(params) { if ( params->name.len == 9 && !strncasecmp(params->name.s, "transport", params->name.len) ) transport = params->body; if ( params->name.len == 8 && !strncasecmp(params->name.s,"received", params->name.len) ) received = params->body; params = params->next; } if (received.len > 0) { if (transport.len > 0) { dst_uri.len = received.len + PATH_TRANS_PARAM_LEN + 1 + transport.len; dst_uri.s = pkg_malloc(dst_uri.len); if(!dst_uri.s) { LM_ERR("no pkg memory left for receive-address\n"); goto out1; } dst_uri.len = snprintf(dst_uri.s, dst_uri.len, "%.*s" PATH_TRANS_PARAM "%.*s", received.len, received.s, transport.len, transport.s); } else { dst_uri = received; } if (set_dst_uri(_m, &dst_uri) != 0) LM_ERR("failed to set dst-uri\n"); if (transport.len > 0) pkg_free(dst_uri.s); } out1: free_params(first_param); return; } opensips-2.2.2/modules/path/path.h000066400000000000000000000032771300170765700171050ustar00rootroot00000000000000/* * Path handling for intermediate proxies. * * Copyright (C) 2006 Inode GmbH (Andreas Granig ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef MOD_PATH_H #define MOD_PATH_H #include "../../parser/msg_parser.h" /* * Prepend own uri to Path header */ int add_path(struct sip_msg* _msg, char* _a, char* _b); /* * Prepend own uri to Path header and take care of given * user. */ int add_path_usr(struct sip_msg* _msg, char* _a, char* _b); /* * Prepend own uri to Path header and append received address as * "received"-param to that uri. */ int add_path_received(struct sip_msg* _msg, char* _a, char* _b); /* * Prepend own uri to Path header and append received address as * "received"-param to that uri and take care of given user. */ int add_path_received_usr(struct sip_msg* _msg, char* _a, char* _b); /* * rr callback for setting dst-uri */ void path_rr_callback(struct sip_msg *_m, str *r_param, void *cb_param); #endif /* MOD_PATH_H */ opensips-2.2.2/modules/path/path_mod.c000066400000000000000000000072161300170765700177340ustar00rootroot00000000000000/* * Path handling for intermediate proxies * * Copyright (C) 2006 Inode GmbH (Andreas Granig ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../mod_fix.h" #include "../rr/api.h" #include "path.h" #include "path_mod.h" int enable_double_path = 1; /* Enable using of 2 Path by default */ /* If received-param of current Route uri should be used * as dst-uri. */ int use_received = 0; /* * Module destroy function prototype */ static void destroy(void); /* * Module child-init function prototype */ static int child_init(int rank); /* * Module initialization function prototype */ static int mod_init(void); /* * rr callback API */ struct rr_binds path_rrb; /* * Exported functions */ static cmd_export_t cmds[] = { { "add_path", (cmd_function)add_path, 0, 0, 0, REQUEST_ROUTE }, { "add_path", (cmd_function)add_path_usr, 1, fixup_str_null, 0, REQUEST_ROUTE }, { "add_path_received", (cmd_function)add_path_received, 0, 0, 0, REQUEST_ROUTE }, { "add_path_received", (cmd_function)add_path_received_usr, 1, fixup_str_null, 0, REQUEST_ROUTE }, { 0, 0, 0, 0, 0, 0 } }; /* * Exported parameters */ static param_export_t params[] = { {"use_received", INT_PARAM, &use_received }, {"enable_double_path", INT_PARAM, &enable_double_path }, { 0, 0, 0 } }; static module_dependency_t *get_deps_use_received(param_export_t *param) { if (! *(int *)param->param_pointer) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "rr", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "use_received", get_deps_use_received }, { NULL, NULL }, }, }; /* * Module interface */ struct module_exports exports = { "path", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; static int child_init(int rank) { return 0; } static int mod_init(void) { LM_INFO("initializing...\n"); if (use_received) { if (load_rr_api(&path_rrb) != 0) { LM_ERR("failed to load rr-API\n"); return -1; } if (path_rrb.register_rrcb(path_rr_callback, 0, 0) != 0) { LM_ERR("failed to register rr callback\n"); return -1; } } return 0; } static void destroy(void) { } opensips-2.2.2/modules/path/path_mod.h000066400000000000000000000017761300170765700177460ustar00rootroot00000000000000/* * Path handling for intermediate proxies * * Copyright (C) 2006 Inode GmbH (Andreas Granig ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PATH_MOD_H #define PATH_MOD_H extern int use_received; extern int enable_double_path; #endif /* PATH_MOD_H */ opensips-2.2.2/modules/peering/000077500000000000000000000000001300170765700164645ustar00rootroot00000000000000opensips-2.2.2/modules/peering/Makefile000066400000000000000000000003771300170765700201330ustar00rootroot00000000000000# # Peering module Makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=peering.so DEFS+= -DWITH_RADIUS_SUPPORT -I$(LOCALBASE)/include LIBS= include ../../Makefile.modules opensips-2.2.2/modules/peering/README000066400000000000000000000141451300170765700173510ustar00rootroot00000000000000Peering Module Juha Heinanen Edited by Juha Heinanen Edited by Irina-Maria Stanescu Copyright © 2008 Juha Heinanen Revision History Revision $Revision: 5952 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.3. Exported Parameters 1.3.1. aaa_url (string) 1.3.2. verify_destination_service_type (integer) 1.3.3. verify_source_service_type (integer) 1.4. Exported Functions 1.4.1. verify_destination() 1.4.2. verify_source() List of Examples 1.1. Set aaa_url parameter 1.2. verify_destination_service_type parameter usage 1.3. verify_source_service_type parameter usage 1.4. verify_destination() usage 1.5. verify_source() usage Chapter 1. Admin Guide 1.1. Overview Peering module allows SIP providers (operators or organizations) to verify from a broker if source or destination of a SIP request is a trusted peer. In order to participate in the trust community provided by a broker, each SIP provider registers with the broker the domains (host parts of SIP URIs) that they serve. When a SIP proxy of a provider needs to send a SIP request to a non-local domain, it can find out from the broker using verify_destination() function if the non-local domain is served by a trusted peer. If so, the provider receives from the broker a hash of the SIP request and a timestamp that it includes in the request to the non-local domain. When a SIP proxy of the non-local domain receives the SIP request, it, in turn, can verify from the broker using verify_source() function if the request came from a trusted peer. Verification functions communicate with the broker using an AAA protocol. Comments and suggestions for improvements are welcome. 1.2. Dependencies 1.2.1. OpenSIPS Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): * an AAA implementing module 1.3. Exported Parameters 1.3.1. aaa_url (string) This is the url representing the AAA protocol used and the location of the configuration file of this protocol. If the parameter is set to empty string, the AAA accounting support will be disabled (even if compiled). Default value is “NULLâ€. Example 1.1. Set aaa_url parameter ... modparam("peering", "aaa_url", "radius:/etc/radiusclient-ng/radiusclient .conf") ... 1.3.2. verify_destination_service_type (integer) This is the value of the Service-Type AAA attribute to be used, when sender of SIP Request verifies request's destination using verify_destination() function. Default value is dictionary value of “Sip-Verify-Destination†Service-Type. Example 1.2. verify_destination_service_type parameter usage ... modparam("peering", "verify_destination_service_type", 21) ... 1.3.3. verify_source_service_type (integer) This is the value of the Service-Type AAA attribute to be used, when receiver of SIP Request verifies request's source using verify_source() function. Default value is dictionary value of “Sip-Verify-Source†Service-Type. Example 1.3. verify_source_service_type parameter usage ... modparam("peering", "verify_source_service_type", 22) ... 1.4. Exported Functions 1.4.1. verify_destination() Function verify_destination() queries from broker's AAA server if domain (host part) of Request URI is served by a trusted peer. AAA request contains the following attributes/values: * User-Name - Request-URI host * SIP-URI-User - Request-URI user * SIP-From-Tag - From tag * SIP-Call-Id - Call id * Service-Type - verify_destination_service_type Function returns value 1 if domain of Request URI is served by a trusted peer and -1 otherwise. In case of positive result, AAA server returns a set of SIP-AVP reply attributes. Value of each SIP-AVP is of form: [#]name(:|#)value Value of each SIP-AVP reply attribute is mapped to an OpenSIPS AVP. Prefix # in front of name or value indicates a string name or string value, respectively. One of the SIP-AVP reply attributes contains a string that the source peer must include "as is" in a P-Request-Hash header when it sends the SIP request to the destination peer. The string value may, for example, be of form hash@timestamp, where hash contains a hash calculated by the broker based on the attributes of the query and some local information and timestamp is the time when the calculation was done. AVP names used in reply attributes are assigned by the broker. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.4. verify_destination() usage ... if (verify_destination()) { append_hf("P-Request-Hash: $avp(prh)\r\n"); } ... 1.4.2. verify_source() Function verify_source() queries from broker's AAA server if SIP request was received from a trusted peer. AAA request contains the following attributes/values: * User-Name - Request-URI host * SIP-URI-User - Request-URI user * SIP-From-Tag - From tag * SIP-Call-Id - Call id * SIP-Request-Hash - body of P-Request-Hash header * Service-Type - verify_source_service_type Function returns value 1 if SIP request was received from a trusted peer and -1 otherwise. In case of positive result, AAA server may return a set of SIP-AVP reply attributes. Value of each SIP-AVP is of form: [#]name(:|#)value Value of each SIP-AVP reply attribute is mapped to an OpenSIPS AVP. Prefix # in front of name or value indicates a string name or string value, respectively. AVP names used in reply attributes are assigned by the broker. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.5. verify_source() usage ... if (is_present_hf("P-Request-Hash")) { if (verify_source()) { xlog("L_INFO", "Request came from trusted peer\n") } } ... opensips-2.2.2/modules/peering/doc/000077500000000000000000000000001300170765700172315ustar00rootroot00000000000000opensips-2.2.2/modules/peering/doc/peering.xml000066400000000000000000000021431300170765700214040ustar00rootroot00000000000000 %docentities; ]> Peering Module Juha Heinanen jh@tutpro.com Juha Heinanen jh@tutpro.com Irina-Maria Stanescu ironmissy@gmail.com 2008 Juha Heinanen $Revision: 5952 $ $Date$ &admin; &faq; opensips-2.2.2/modules/peering/doc/peering_admin.xml000066400000000000000000000164121300170765700225600ustar00rootroot00000000000000 &adminguide;
Overview Peering module allows SIP providers (operators or organizations) to verify from a broker if source or destination of a SIP request is a trusted peer. In order to participate in the trust community provided by a broker, each SIP provider registers with the broker the domains (host parts of SIP URIs) that they serve. When a SIP proxy of a provider needs to send a SIP request to a non-local domain, it can find out from the broker using verify_destination() function if the non-local domain is served by a trusted peer. If so, the provider receives from the broker a hash of the SIP request and a timestamp that it includes in the request to the non-local domain. When a SIP proxy of the non-local domain receives the SIP request, it, in turn, can verify from the broker using verify_source() function if the request came from a trusted peer. Verification functions communicate with the broker using an AAA protocol. Comments and suggestions for improvements are welcome.
Dependencies
&osips; Modules The module depends on the following modules (in the other words the listed modules must be loaded before this module): an AAA implementing module
Exported Parameters
<varname>aaa_url</varname> (string) This is the url representing the AAA protocol used and the location of the configuration file of this protocol. If the parameter is set to empty string, the AAA accounting support will be disabled (even if compiled). Default value is NULL. Set <varname>aaa_url</varname> parameter ... modparam("peering", "aaa_url", "radius:/etc/radiusclient-ng/radiusclient.conf") ...
<varname>verify_destination_service_type</varname> (integer) This is the value of the Service-Type AAA attribute to be used, when sender of SIP Request verifies request's destination using verify_destination() function. Default value is dictionary value of Sip-Verify-Destination Service-Type. <varname>verify_destination_service_type</varname> parameter usage ... modparam("peering", "verify_destination_service_type", 21) ...
<varname>verify_source_service_type</varname> (integer) This is the value of the Service-Type AAA attribute to be used, when receiver of SIP Request verifies request's source using verify_source() function. Default value is dictionary value of Sip-Verify-Source Service-Type. <varname>verify_source_service_type</varname> parameter usage ... modparam("peering", "verify_source_service_type", 22) ...
Exported Functions
<function moreinfo="none">verify_destination()</function> Function verify_destination() queries from broker's AAA server if domain (host part) of Request URI is served by a trusted peer. AAA request contains the following attributes/values: User-Name - Request-URI host SIP-URI-User - Request-URI user SIP-From-Tag - From tag SIP-Call-Id - Call id Service-Type - verify_destination_service_type Function returns value 1 if domain of Request URI is served by a trusted peer and -1 otherwise. In case of positive result, AAA server returns a set of SIP-AVP reply attributes. Value of each SIP-AVP is of form: [#]name(:|#)value Value of each SIP-AVP reply attribute is mapped to an OpenSIPS AVP. Prefix # in front of name or value indicates a string name or string value, respectively. One of the SIP-AVP reply attributes contains a string that the source peer must include "as is" in a P-Request-Hash header when it sends the SIP request to the destination peer. The string value may, for example, be of form hash@timestamp, where hash contains a hash calculated by the broker based on the attributes of the query and some local information and timestamp is the time when the calculation was done. AVP names used in reply attributes are assigned by the broker. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. <function moreinfo="none">verify_destination()</function> usage ... if (verify_destination()) { append_hf("P-Request-Hash: $avp(prh)\r\n"); } ...
<function moreinfo="none">verify_source()</function> Function verify_source() queries from broker's AAA server if SIP request was received from a trusted peer. AAA request contains the following attributes/values: User-Name - Request-URI host SIP-URI-User - Request-URI user SIP-From-Tag - From tag SIP-Call-Id - Call id SIP-Request-Hash - body of P-Request-Hash header Service-Type - verify_source_service_type Function returns value 1 if SIP request was received from a trusted peer and -1 otherwise. In case of positive result, AAA server may return a set of SIP-AVP reply attributes. Value of each SIP-AVP is of form: [#]name(:|#)value Value of each SIP-AVP reply attribute is mapped to an OpenSIPS AVP. Prefix # in front of name or value indicates a string name or string value, respectively. AVP names used in reply attributes are assigned by the broker. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. <function moreinfo="none">verify_source()</function> usage ... if (is_present_hf("P-Request-Hash")) { if (verify_source()) { xlog("L_INFO", "Request came from trusted peer\n") } } ...
opensips-2.2.2/modules/peering/peering.c000066400000000000000000000101361300170765700202620ustar00rootroot00000000000000/* * Peering module * * Copyright (C) 2008 Juha Heinanen * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../config.h" #include "../../aaa/aaa.h" #include "verify.h" aaa_map attrs[A_MAX]; aaa_map vals[V_MAX]; aaa_conn *conn; aaa_prot proto; static int mod_init(void); /* Module initialization function */ /* * Module parameter variables */ static char* aaa_proto_url = NULL; int verify_destination_service_type = -1; int verify_source_service_type = -1; /* * Exported functions */ static cmd_export_t cmds[] = { {"verify_destination", (cmd_function)verify_destination, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"verify_source", (cmd_function)verify_source, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"aaa_url", STR_PARAM, &aaa_proto_url}, {"verify_destination_service_type", INT_PARAM, &verify_destination_service_type}, {"verify_source_service_type", INT_PARAM, &verify_source_service_type}, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_AAA, NULL, DEP_WARN }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* * Module interface */ struct module_exports exports = { "peering", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0 /* child initialization function */ }; /* * Module initialization function */ static int mod_init(void) { str prot_url; LM_DBG("initializing\n"); memset(attrs, 0, sizeof(attrs)); memset(vals, 0, sizeof(vals)); attrs[A_USER_NAME].name = "User-Name"; attrs[A_SIP_URI_USER].name = "SIP-URI-User"; attrs[A_SIP_FROM_TAG].name = "SIP-From-Tag"; attrs[A_SIP_CALL_ID].name = "SIP-Call-Id"; attrs[A_SIP_REQUEST_HASH].name = "SIP-Request-Hash"; attrs[A_SIP_AVP].name = "SIP-AVP"; attrs[A_SERVICE_TYPE].name = "Service-Type"; vals[V_SIP_VERIFY_DESTINATION].name = "Sip-Verify-Destination"; vals[V_SIP_VERIFY_SOURCE].name = "Sip-Verify-Source"; prot_url.s = aaa_proto_url; prot_url.len = strlen(aaa_proto_url); if(aaa_prot_bind(&prot_url, &proto)) { LM_ERR("aaa protocol bind failure\n"); return -1; } if (!(conn = proto.init_prot(&prot_url))) { LM_ERR("aaa protocol initialization failure\n"); return -2; } INIT_AV(proto, conn, attrs, A_MAX, vals, V_MAX, "peering", -3, -4); if (verify_destination_service_type != -1) vals[V_SIP_VERIFY_DESTINATION].value = verify_destination_service_type; if (verify_source_service_type != -1) vals[V_SIP_VERIFY_SOURCE].value = verify_source_service_type; return 0; } opensips-2.2.2/modules/peering/peering.h000066400000000000000000000021141300170765700202640ustar00rootroot00000000000000/* * Radius based peering module .h file * * Copyright (C) 2008 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PEERING_H_ #define _PEERING_H_ extern aaa_map attrs[]; extern aaa_map vals[]; extern aaa_conn *conn; extern aaa_prot proto; extern int verify_destination_service_type; extern int verify_source_service_type; #endif /* _PEERING_H_ */ opensips-2.2.2/modules/peering/verify.c000066400000000000000000000150361300170765700201410ustar00rootroot00000000000000/* * Verification functions * * Copyright (C) 2008 Juha Heinanen * Copyright (C) 2009 Irina-Maria Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. * */ #include "../../str.h" #include "../../dprint.h" #include "../../aaa/aaa.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "peering.h" /* * Send Radius request to verify destination and generate AVPs from * reply items of positive response. */ int verify_destination(struct sip_msg* _msg, char* s1, char* s2) { aaa_message *send = NULL, *received = NULL; uint32_t service; /* Add Request-URI host A_USER_NAME and user as A_SIP_URI_USER */ if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("error while parsing Request-URI\n"); return -1; } if ((send = proto.create_aaa_message(conn, AAA_AUTH)) == NULL) { LM_ERR("failed to create new aaa message for auth\n"); return -1; } if (proto.avp_add(conn, send, &attrs[A_USER_NAME], _msg->parsed_uri.host.s, _msg->parsed_uri.host.len, 0)) { LM_ERR("error adding PW_USER_NAME\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_SIP_URI_USER], _msg->parsed_uri.user.s, _msg->parsed_uri.user.len, 0)) { LM_ERR("error adding PW_SIP_URI_USER\n"); goto err; } /* Add From Tag */ if (parse_from_header(_msg) < 0) { LM_ERR("error while parsing From header field\n"); goto err; } if ((_msg->from==NULL) || (get_from(_msg) == NULL) || (get_from(_msg)->tag_value.s == NULL) || (get_from(_msg)->tag_value.len <= 0)) { LM_ERR("error while accessing From header tag\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_SIP_FROM_TAG], get_from(_msg)->tag_value.s, get_from(_msg)->tag_value.len, 0)) { LM_ERR("error adding PW_SIP_FROM_TAG\n"); goto err; } /* Add Call-Id */ if ((parse_headers(_msg, HDR_CALLID_F, 0) == -1) || (_msg->callid == NULL) || (_msg->callid->body.s == NULL) || (_msg->callid->body.len <= 0)) { LM_ERR("error while accessing Call-Id\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_SIP_CALL_ID], _msg->callid->body.s, _msg->callid->body.len, 0)) { LM_ERR("error adding PW_SIP_CALL_ID\n"); goto err; } /* Add Service-Type */ service = vals[V_SIP_VERIFY_DESTINATION].value; if (proto.avp_add(conn, send, &attrs[A_SERVICE_TYPE], &service, -1, 0)) { LM_ERR("error adding PW_SERVICE_TYPE\n"); goto err; } /* Send Request and generate AVPs of positive reply */ if (!proto.send_aaa_request(conn, send, &received)) { LM_DBG("success\n"); proto.destroy_aaa_message(conn, send); proto.destroy_aaa_message(conn, received); return 1; } LM_DBG("failure\n"); err: if (send) proto.destroy_aaa_message(conn, send); if (received) proto.destroy_aaa_message(conn, received); return -1; } /* * Send Radius request to verify source. */ int verify_source(struct sip_msg* _msg, char* s1, char* s2) { aaa_message *send = NULL, *received = NULL; struct hdr_field *hf; uint32_t service; /* Add Request-URI host A_USER_NAME and user as A_SIP_URI_USER */ if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("error while parsing Request-URI\n"); return -1; } if ((send = proto.create_aaa_message(conn, AAA_AUTH)) == NULL) { LM_ERR("failed to create new aaa message for auth\n"); return -1; } if (proto.avp_add(conn, send, &attrs[A_USER_NAME], _msg->parsed_uri.host.s, _msg->parsed_uri.host.len, 0)) { LM_ERR("error adding PW_USER_NAME\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_SIP_URI_USER], _msg->parsed_uri.user.s, _msg->parsed_uri.user.len, 0)) { LM_ERR("error adding PW_SIP_URI_USER\n"); goto err; } /* Add From Tag */ if (parse_from_header(_msg) < 0) { LM_ERR("error while parsing From header field\n"); goto err; } if (_msg->from == NULL || get_from(_msg) == NULL || get_from(_msg)->tag_value.s == NULL || get_from(_msg)->tag_value.len <= 0) { LM_ERR("error while accessing From header tag\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_SIP_FROM_TAG], get_from(_msg)->tag_value.s, get_from(_msg)->tag_value.len, 0)) { LM_ERR("error adding PW_SIP_FROM_TAG\n"); goto err; } /* Add Call-Id */ if (parse_headers(_msg, HDR_CALLID_F, 0) == -1 || _msg->callid == NULL || _msg->callid->body.s == NULL || _msg->callid->body.len <= 0) { LM_ERR("error while accessing Call-Id\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_SIP_CALL_ID], _msg->callid->body.s, _msg->callid->body.len, 0)) { LM_ERR("error adding PW_SIP_CALL_ID\n"); goto err; } /* Add P-Request-Hash header body */ parse_headers(_msg, HDR_EOH_F, 0); hf = get_header_by_static_name( _msg, "P-Request-Hash"); if (!hf) { LM_ERR("no P-Request-Hash header field\n"); goto err; } if (hf->body.s == NULL || hf->body.len <= 0) { LM_ERR("error while accessing P-Request-Hash body\n"); goto err; } if (proto.avp_add(conn, send, &attrs[A_SIP_REQUEST_HASH], hf->body.s, hf->body.len, 0)) { LM_ERR("error adding PW_SIP_REQUEST_HASH\n"); goto err; } /* Add Service-Type */ service = vals[V_SIP_VERIFY_SOURCE].value; if (proto.avp_add(conn, send, &attrs[A_SERVICE_TYPE], &service, -1, 0)) { LM_ERR("error adding PW_SERVICE_TYPE\n"); goto err; } /* Send Request and generate AVPs of positive reply */ if (!proto.send_aaa_request(conn, send, &received)) { LM_DBG("success\n"); proto.destroy_aaa_message(conn, send); proto.destroy_aaa_message(conn, received); return 1; } LM_DBG("failure\n"); err: if (send) proto.destroy_aaa_message(conn, send); if (received) proto.destroy_aaa_message(conn, received); return -1; } opensips-2.2.2/modules/peering/verify.h000066400000000000000000000021571300170765700201460ustar00rootroot00000000000000/* * Verification functions .h file * * Copyright (C) 2008 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. * */ #ifndef _PEERING_VERIFY_H_ #define _PEERING_VERIFY_H_ #include "../../parser/msg_parser.h" #include "../../aaa/aaa.h" int verify_destination(struct sip_msg* _msg, char* s1, char* s2); int verify_source(struct sip_msg* _msg, char* s1, char* s2); #endif /* _PEERING_VERIFY_H_ */ opensips-2.2.2/modules/perl/000077500000000000000000000000001300170765700157755ustar00rootroot00000000000000opensips-2.2.2/modules/perl/.gitignore000066400000000000000000000000151300170765700177610ustar00rootroot00000000000000opensipsxs.c opensips-2.2.2/modules/perl/Makefile000066400000000000000000000045221300170765700174400ustar00rootroot00000000000000# $Id$ # # perl module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=perl.so LIBS= # define it (or export this) to the dir where xsubpp is on your system (if # not on the default path. #PERLBINDIR= ifeq ($(CC_NAME), gcc) DEFS+=-Wno-unused -Wno-redundant-decls endif ifeq ($(PERLLDOPTS),) LIBS+=$(shell perl -MExtUtils::Embed -e ldopts) else LIBS+=$(PERLLDOPTS) endif ifeq ($(PERLCCOPTS),) DEFS+=$(shell perl -MExtUtils::Embed -e ccopts) else DEFS+=$(PERLCCOPTS) endif include ../../Makefile.modules PODFILES=opensipsxs.xs PODFILES+=$(shell find lib/perl -name *.pm) ifeq ($(TYPEMAP),) ifeq ($(PERLLIBPATH),) PERLLIBPATH=$(shell perl -MConfig -e 'print $$Config{installprivlib}') endif TYPEMAP=$(PERLLIBPATH)/ExtUtils/typemap endif perl.d: opensipsxs.o perl.o: perl.c opensipsxs.o opensipsxs.o: opensipsxs.c opensipsxs.d $(Q)$(CC) $(MOD_CFLAGS) $(DEFS) -c -o opensipsxs.o opensipsxs.c opensipsxs.c: opensipsxs.xs typemap $(Q)$(PERLBINDIR)xsubpp -typemap $(TYPEMAP) -typemap typemap opensipsxs.xs > opensipsxs.c clean: clean-special .PHONY: clean-special clean-special: -rm -f opensipsxs.c opensipsxs.o opensipsxs.d .PHONY: perlpod perlpod: doc/perl_pod.sgml doc/perl_pod.sgml: opensipsxs.xs cat $(PODFILES) | pod2docbook --doctype=chapter --title='OpenSIPS Perl API' --no-header - doc/perl_pod.sgml install_module_custom: echo "installing Perl OpenSIPS packages ..." mkdir -p $(modules_prefix)/$(lib_dir)/perl $(INSTALL_CFG) lib/perl/*.pm $(modules_prefix)/$(lib_dir)/perl mkdir -p $(modules_prefix)/$(lib_dir)/perl/OpenSIPS $(INSTALL_CFG) lib/perl/OpenSIPS/*.pm \ $(modules_prefix)/$(lib_dir)/perl/OpenSIPS mkdir -p $(modules_prefix)/$(lib_dir)/perl/OpenSIPS/LDAPUtils $(INSTALL_CFG) lib/perl/OpenSIPS/LDAPUtils/*.pm \ $(modules_prefix)/$(lib_dir)/perl/OpenSIPS/LDAPUtils mkdir -p $(modules_prefix)/$(lib_dir)/perl/OpenSIPS/Utils $(INSTALL_CFG) lib/perl/OpenSIPS/Utils/*.pm \ $(modules_prefix)/$(lib_dir)/perl/OpenSIPS/Utils mkdir -p $(modules_prefix)/$(lib_dir)/perl/OpenSIPS/VDB $(INSTALL_CFG) lib/perl/OpenSIPS/VDB/*.pm \ $(modules_prefix)/$(lib_dir)/perl/OpenSIPS/VDB mkdir -p $(modules_prefix)/$(lib_dir)/perl/OpenSIPS/VDB/Adapter $(INSTALL_CFG) lib/perl/OpenSIPS/VDB/Adapter/*.pm \ $(modules_prefix)/$(lib_dir)/perl/OpenSIPS/VDB/Adapter opensips-2.2.2/modules/perl/README000066400000000000000000001126661300170765700166710ustar00rootroot00000000000000perl Module Bastian Friedrich Collax GmbH Edited by Bastian Friedrich Copyright © 2007 Collax GmbH Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Installing the module 1.3. Using the module 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. filename (string) 1.5.2. modpath (string) 1.6. Exported Functions 1.6.1. perl_exec_simple(func, [param]) 1.6.2. perl_exec(func, [param]) 2. OpenSIPS Perl API 2.1. OpenSIPS 2.1.1. log(level,message) 2.2. OpenSIPS::Message 2.2.1. getType() 2.2.2. getStatus() 2.2.3. getReason() 2.2.4. getVersion() 2.2.5. getRURI() 2.2.6. getMethod() 2.2.7. getFullHeader() 2.2.8. getBody() 2.2.9. getMessage() 2.2.10. getHeader(name) 2.2.11. getHeaderNames() 2.2.12. moduleFunction(func,string1,string2) 2.2.13. log(level,message) (deprecated type) 2.2.14. rewrite_ruri(newruri) 2.2.15. setFlag(flag) 2.2.16. resetFlag(flag) 2.2.17. isFlagSet(flag) 2.2.18. pseudoVar(string) 2.2.19. append_branch(branch,qval) 2.2.20. serialize_branches(clean_before) 2.2.21. next_branches() 2.2.22. getParsedRURI() 2.3. OpenSIPS::URI 2.3.1. user() 2.3.2. host() 2.3.3. passwd() 2.3.4. port() 2.3.5. params() 2.3.6. headers() 2.3.7. transport() 2.3.8. ttl() 2.3.9. user_param() 2.3.10. maddr() 2.3.11. method() 2.3.12. lr() 2.3.13. r2() 2.3.14. transport_val() 2.3.15. ttl_val() 2.3.16. user_param_val() 2.3.17. maddr_val() 2.3.18. method_val() 2.3.19. lr_val() 2.3.20. r2_val() 2.4. OpenSIPS::AVP 2.4.1. add(name,val) 2.4.2. get(name) 2.4.3. destroy(name) 2.5. OpenSIPS::Utils::PhoneNumbers 2.5.1. new(publicAccessPrefix,internationalPrefix,lon gDistancePrefix,countryCode,areaCode,pbxCode ) 2.5.2. canonicalForm( number [, context] ) 2.5.3. dialNumber( number [, context] ) 2.6. OpenSIPS::LDAPUtils::LDAPConf 2.6.1. Constructor new() 2.6.2. Method base() 2.6.3. Method host() 2.6.4. Method port() 2.6.5. Method uri() 2.6.6. Method rootbindpw() 2.6.7. Method rootbinddn() 2.6.8. Method binddn() 2.6.9. Method bindpw() 2.7. OpenSIPS::LDAPUtils::LDAPConnection 2.7.1. Constructor new( [config, [authenticated]] ) 2.7.2. Function/Method search( conf, filter, base, [requested_attributes ...]) 2.8. OpenSIPS::VDB 2.9. OpenSIPS::Constants 2.10. OpenSIPS::VDB::Adapter::Speeddial 2.11. OpenSIPS::VDB::Adapter::Alias 2.11.1. query(conds,retkeys,order) 2.12. OpenSIPS::VDB::Adapter::AccountingSIPtrace 2.13. OpenSIPS::VDB::Adapter::Describe 2.14. OpenSIPS::VDB::Adapter::Auth 2.15. OpenSIPS::VDB::ReqCond 2.15.1. new(key,op,type,name) 2.15.2. op() 2.16. OpenSIPS::VDB::Pair 2.16.1. new(key,type,name) 2.16.2. key() 2.17. OpenSIPS::VDB::VTab 2.17.1. new() 2.17.2. call(op,[args]) 2.18. OpenSIPS::VDB::Value 2.18.1. stringification 2.18.2. new(type,data) 2.18.3. type() 2.18.4. data() 2.19. OpenSIPS::VDB::Column 2.19.1. Stringification 2.19.2. new(type,name) 2.19.3. type( ) 2.19.4. name() 2.19.5. OpenSIPS::VDB::Result 2.19.6. new(coldefs,[row, row, ...]) 2.19.7. coldefs() 2.19.8. rows() 3. Perl samples 3.1. sample directory 3.1.1. Script descriptions 4. Frequently Asked Questions List of Examples 1.1. Set filename parameter 1.2. Set modpath parameter 1.3. perl_exec_simple() usage 1.4. perl_exec() usage Chapter 1. Admin Guide 1.1. Overview The time needed when writing a new OpenSIPS module unfortunately is quite high, while the options provided by the configuration file are limited to the features implemented in the modules. With this Perl module, you can easily implement your own OpenSIPS extensions in Perl. This allows for simple access to the full world of CPAN modules. SIP URI rewriting could be implemented based on regular expressions; accessing arbitrary data backends, e.g. LDAP or Berkeley DB files, is now extremely simple. 1.2. Installing the module This Perl module is loaded in opensips.cfg (just like all the other modules) with loadmodule("/path/to/perl.so");. For the Perl module to compile, you need a reasonably recent version of perl (tested with 5.8.8) linked dynamically. It is strongly advised to use a threaded version. The default binary packages from your favorite Linux distribution should work fine. Cross compilation is supported by the Makefile. You need to set the environment variables PERLLDOPTS, PERLCCOPTS and TYPEMAP to values similar to the output of PERLLDOPTS: perl -MExtUtils::Embed -e ldopts PERLCCOPTS: perl -MExtUtils::Embed -e ccopts TYPEMAP: echo "`perl -MConfig -e 'print $Config{installprivlib}'`/Ext Utils/typemap" The exact position of your (precompiled!) perl libraries depends on the setup of your environment. 1.3. Using the module The Perl module has two interfaces: The perl side, and the OpenSIPS side. Once a Perl function is defined and loaded via the module parameters (see below), it may be called in OpenSIPS's configuration at an arbitary point. E.g., you could write a function "ldap_alias" in Perl, and then execute ... if (perl_exec("ldap_alias")) { ... } ... just as you would have done with the current alias_db module. The functions you can use are listed in the "Exported Functions" section below. On the Perl side, there are a number of functions that let you read and modify the current SIP message, such as the RURI or the message flags. An introduction to the Perl interface and the full reference documentation can be found below. 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * The "sl" module is needed for sending replies uppon fatal errors. All other modules can be accessed from the Perl module, though. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * Perl 5.8.x or later Additionally, a number of perl modules should be installed. The OpenSIPS::LDAPUtils package relies on Net::LDAP to be installed. One of the sample scripts needs IPC::Shareable This module has been developed and tested with Perl 5.8.8, but should work with any 5.8.x release. Compilation is possible with 5.6.x, but its behavior is unsupported. Earlier versions do not work. On current Debian systems, at least the following packages should be installed: * perl * perl-base * perl-modules * libperl5.8 * libperl-dev * libnet-ldap-perl * libipc-shareable-perl It was reported that other Debian-style distributions (such as Ubuntu) need the same packages. On SuSE systems, at least the following packages should be installed: * perl * perl-ldap * IPC::Shareable perl module from CPAN Although SuSE delivers a lot of perl modules, others may have to be fetched from CPAN. Consider using the program “cpan2rpm†- which, in turn, is available on CPAN. It creates RPM files from CPAN. 1.5. Exported Parameters 1.5.1. filename (string) This is the file name of your script. This may be set once only, but it may include an arbitary number of functions and “use†as many Perl module as necessary. May not be empty! Example 1.1. Set filename parameter ... modparam("perl", "filename", "/home/john/opensips/myperl.pl") ... 1.5.2. modpath (string) The path to the Perl modules included (OpenSIPS.pm et.al). It is not absolutely crucial to set this path, as you may install the Modules in Perl's standard path, or update the “%INC†variable from within your script. Using this module parameter is the standard behavior, though. Example 1.2. Set modpath parameter ... modparam("perl", "modpath", "/usr/local/lib/opensips/perl/") ... 1.6. Exported Functions 1.6.1. perl_exec_simple(func, [param]) Calls a perl function without passing it the current SIP message. May be used for very simple simple requests that do not have to fiddle with the message themselves, but rather return information values about the environment. The first parameter is the function to be called. An arbitrary string may optionally be passed as a parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE and BRANCH_ROUTE. Example 1.3. perl_exec_simple() usage ... if (method=="INVITE") { perl_exec_simple("dosomething", "on invite messages"); }; ... 1.6.2. perl_exec(func, [param]) Calls a perl function with passing it the current SIP message. The SIP message is reflected by a Perl module that gives you access to the information in the current SIP message (OpenSIPS::Message). The first parameter is the function to be called. An arbitrary string may be passed as a parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE and BRANCH_ROUTE. Example 1.4. perl_exec() usage ... if (perl_exec("ldapalias")) { ... }; ... Chapter 2. OpenSIPS Perl API 2.1. OpenSIPS This module provides access to a limited number of OpenSIPS core functions. As the most interesting functions deal with SIP messages, they are located in the OpenSIPS::Message class below. 2.1.1. log(level,message) Logs the message with OpenSIPS's logging facility. The logging level is one of the following: * L_ALERT * L_CRIT * L_ERR * L_WARN * L_NOTICE * L_INFO * L_DBG Please note that this method is NOT automatically exported, as it collides with the perl function log (which calculates the logarithm). Either explicitly import the function (via use OpenSIPS qw ( log );), or call it with its full name: OpenSIPS::log(L_INFO, "foobar"); 2.2. OpenSIPS::Message This package provides access functions for an OpenSIPS sip_msg structure and its sub-components. Through its means it is possible to fully configure alternative routing decisions. 2.2.1. getType() Returns one of the constants SIP_REQUEST, SIP_REPLY, SIP_INVALID stating the type of the current message. 2.2.2. getStatus() Returns the status code of the current Reply message. This function is invalid in Request context! 2.2.3. getReason() Returns the reason of the current Reply message. This function is invalid in Request context! 2.2.4. getVersion() Returns the version string of the current SIP message. 2.2.5. getRURI() This function returns the recipient URI of the present SIP message: my $ruri = $m->getRURI(); getRURI returns a string. See “getParsedRURI()†below how to receive a parsed structure. This function is valid in request messages only. 2.2.6. getMethod() Returns the current method, such as INVITE, REGISTER, ACK and so on. my $method = $m->getMethod(); This function is valid in request messages only. 2.2.7. getFullHeader() Returns the full message header as present in the current message. You might use this header to further work with it with your favorite MIME package. my $hdr = $m->getFullHeader(); 2.2.8. getBody() Returns the message body. 2.2.9. getMessage() Returns the whole message including headers and body. 2.2.10. getHeader(name) Returns the body of the first message header with this name. print $m->getHeader("To"); "John" 2.2.11. getHeaderNames() Returns an array of all header names. Duplicates possible! 2.2.12. moduleFunction(func,string1,string2) Search for an arbitrary function in module exports and call it with the parameters self, string1, string2. string1 and/or string2 may be omitted. As this function provides access to the functions that are exported to the OpenSIPS configuration file, it is autoloaded for unknown functions. Instead of writing $m->moduleFunction("sl_send_reply", "500", "Internal Error"); $m->moduleFunction("xlog", "L_INFO", "foo"); you may as well write $m->sl_send_reply("500", "Internal Error"); $m->xlog("L_INFO", "foo"); WARNING In OpenSIPS 1.2, only a limited subset of module functions is available. This restriction will be removed in a later version. Here is a list of functions that are expected to be working (not claiming completeness): * alias_db_lookup * consume_credentials * is_rpid_user_e164 * append_rpid_hf * bind_auth * avp_print * cpl_process_register * cpl_process_register_norpl * load_dlg * ds_next_dst * ds_next_domain * ds_mark_dst * ds_mark_dst * is_from_local * is_uri_host_local * dp_can_connect * dp_apply_policy * enum_query (without parameters) * enum_fquery (without parameters) * is_from_user_enum (without parameters) * i_enum_query (without parameters) * imc_manager * jab_* (all functions from the jabber module) * sdp_mangle_ip * sdp_mangle_port * encode_contact * decode_contact * decode_contact_header * fix_contact * use_media_proxy * end_media_session * m_store * m_dump * fix_nated_contact * unforce_rtp_proxy * force_rtp_proxy * fix_nated_register * add_rcv_param * options_reply * checkospheader * validateospheader * requestosprouting * checkosproute * prepareosproute * prepareallosproutes * checkcallingtranslation * reportospusage * mangle_pidf * mangle_message_cpim * add_path (without parameters) * add_path_received (without parameters) * prefix2domain * allow_routing (without parameters) * allow_trusted * pike_check_req * handle_publish * handle_subscribe * stored_pres_info * bind_pua * send_publish * send_subscribe * pua_set_publish * loose_route * record_route * load_rr * sip_trace * sl_reply_error * sms_send_msg * sd_lookup * sstCheckMin * append_time * has_body (without parameters) * is_peer_verified * t_newtran * t_release * t_relay (without parameters) * t_flush_flags * t_check_trans * t_was_cancelled * uac_restore_from * uac_auth * has_totag * tel2sip * check_to * check_from * radius_does_uri_exist * ul_* (All functions exported by the usrloc module for user access) * xmpp_send_message 2.2.13. log(level,message) (deprecated type) Logs the message with OpenSIPS's logging facility. The logging level is one of the following: * L_ALERT * L_CRIT * L_ERR * L_WARN * L_NOTICE * L_INFO * L_DBG The logging function should be accessed via the OpenSIPS module variant. This one, located in OpenSIPS::Message, is deprecated. 2.2.14. rewrite_ruri(newruri) Sets a new destination (recipient) URI. Useful for rerouting the current message/call. if ($m->getRURI() =~ m/\@somedomain.net/) { $m->rewrite_ruri("sip:dispatcher\@organization.net"); } 2.2.15. setFlag(flag) Sets a message flag. The constants as known from the C API may be used, when Constants.pm is included. 2.2.16. resetFlag(flag) Resets a message flag. 2.2.17. isFlagSet(flag) Returns whether a message flag is set or not. 2.2.18. pseudoVar(string) Returns a new string where all pseudo variables are substituted by their values. Can be used to receive the values of single variables, too. Please remember that you need to escape the '$' sign in perl strings! 2.2.19. append_branch(branch,qval) Append a branch to current message. 2.2.20. serialize_branches(clean_before) Serialize branches. 2.2.21. next_branches() Next branches. 2.2.22. getParsedRURI() Returns the current destination URI as an OpenSIPS::URI object. 2.3. OpenSIPS::URI This package provides functions for access to sip_uri structures. 2.3.1. user() Returns the user part of this URI. 2.3.2. host() Returns the host part of this URI. 2.3.3. passwd() Returns the passwd part of this URI. 2.3.4. port() Returns the port part of this URI. 2.3.5. params() Returns the params part of this URI. 2.3.6. headers() Returns the headers part of this URI. 2.3.7. transport() Returns the transport part of this URI. 2.3.8. ttl() Returns the ttl part of this URI. 2.3.9. user_param() Returns the user_param part of this URI. 2.3.10. maddr() Returns the maddr part of this URI. 2.3.11. method() Returns the method part of this URI. 2.3.12. lr() Returns the lr part of this URI. 2.3.13. r2() Returns the r2 part of this URI. 2.3.14. transport_val() Returns the transport_val part of this URI. 2.3.15. ttl_val() Returns the ttl_val part of this URI. 2.3.16. user_param_val() Returns the user_param_val part of this URI. 2.3.17. maddr_val() Returns the maddr_val part of this URI. 2.3.18. method_val() Returns the method_val part of this URI. 2.3.19. lr_val() Returns the lr_val part of this URI. 2.3.20. r2_val() Returns the r2_val part of this URI. 2.4. OpenSIPS::AVP This package provides access functions for OpenSIPS's AVPs. These variables can be created, evaluated, modified and removed through this package. Please note that these functions do NOT support the notation used in the configuration file, but directly work on strings or numbers. See documentation of add method below. 2.4.1. add(name,val) Add an AVP. Add an OpenSIPS AVP to its environment. name and val may both be integers or strings; this function will try to guess what is correct. Please note that OpenSIPS::AVP::add("10", "10") is something different than OpenSIPS::AVP::add(10, 10) due to this evaluation: The first will create _string_ AVPs with the name 10, while the latter will create a numerical AVP. You can modify/overwrite AVPs with this function. 2.4.2. get(name) get an OpenSIPS AVP: my $numavp = OpenSIPS::AVP::get(5); my $stravp = OpenSIPS::AVP::get("foo"); 2.4.3. destroy(name) Destroy an AVP. OpenSIPS::AVP::destroy(5); OpenSIPS::AVP::destroy("foo"); 2.5. OpenSIPS::Utils::PhoneNumbers OpenSIPS::Utils::PhoneNumbers - Functions for canonical forms of phone numbers. use OpenSIPS::Utils::PhoneNumbers; my $phonenumbers = new OpenSIPS::Utils::PhoneNumbers( publicAccessPrefix => "0", internationalPrefix => "+", longDistancePrefix => "0", areaCode => "761", pbxCode => "456842", countryCode => "49" ); $canonical = $phonenumbers->canonicalForm("07612034567"); $number = $phonenumbers->dialNumber("+497612034567"); A telphone number starting with a plus sign and containing all dial prefixes is in canonical form. This is usally not the number to dial at any location, so the dialing number depends on the context of the user/system. The idea to canonicalize numbers were taken from hylafax. Example: +497614514829 is the canonical form of my phone number, 829 is the number to dial at Pyramid, 4514829 is the dialing number from Freiburg are and so on. To canonicalize any number, we strip off any dial prefix we find and then add the prefixes for the location. So, when the user enters the number 04514829 in context pyramid, we remove the publicAccessPrefix (at Pyramid this is 0) and the pbxPrefix (4514 here). The result is 829. Then we add all the general dial prefixes - 49 (country) 761 (area) 4514 (pbx) and 829, the number itself => +497614514829 To get the dialing number from a canonical phone number, we substract all general prefixes until we have something As said before, the interpretation of a phone number depends on the context of the location. For the functions in this package, the context is created through the new operator. The following fields should be set: 'longDistancePrefix' 'areaCode' 'pbxCode' 'internationalPrefix' 'publicAccessPrefix' 'countryCode' This module exports the following functions when useed: 2.5.1. new(publicAccessPrefix,internationalPrefix,longDistancePrefix, countryCode,areaCode,pbxCode) The new operator returns an object of this type and sets its locational context according to the passed parameters. See OpenSIPS::Utils::PhoneNumbers above. 2.5.2. canonicalForm( number [, context] ) Convert a phone number (given as first argument) into its canonical form. When no context is passed in as the second argument, the default context from the systems configuration file is used. 2.5.3. dialNumber( number [, context] ) Convert a canonical phone number (given in the first argument) into a number to to dial. WHen no context is given in the second argument, a default context from the systems configuration is used. 2.6. OpenSIPS::LDAPUtils::LDAPConf OpenSIPS::LDAPUtils::LDAPConf - Read openldap config from standard config files. use OpenSIPS::LDAPUtils::LDAPConf; my $conf = new OpenSIPS::LDAPUtils::LDAPConf(); This module may be used to retrieve the global LDAP configuration as used by other LDAP software, such as nsswitch.ldap and pam-ldap. The configuration is usualy stored in /etc/openldap/ldap.conf When used from an account with sufficient privilegs (e.g. root), the ldap manager passwort is also retrieved. 2.6.1. Constructor new() Returns a new, initialized OpenSIPS::LDAPUtils::LDAPConf object. 2.6.2. Method base() Returns the servers base-dn to use when doing queries. 2.6.3. Method host() Returns the ldap host to contact. 2.6.4. Method port() Returns the ldap servers port. 2.6.5. Method uri() Returns an uri to contact the ldap server. When there is no ldap_uri in the configuration file, an ldap: uri is constucted from host and port. 2.6.6. Method rootbindpw() Returns the ldap "root" password. Note that the rootbindpw is only available when the current account has sufficient privilegs to access /etc/openldap/ldap.secret. 2.6.7. Method rootbinddn() Returns the DN to use for "root"-access to the ldap server. 2.6.8. Method binddn() Returns the DN to use for authentication to the ldap server. When no bind dn has been specified in the configuration file, returns the rootbinddn. 2.6.9. Method bindpw() Returns the password to use for authentication to the ldap server. When no bind password has been specified, returns the rootbindpw if any. 2.7. OpenSIPS::LDAPUtils::LDAPConnection OpenSIPS::LDAPUtils::LDAPConnection - Perl module to perform simple LDAP queries. OO-Style interface: use OpenSIPS::LDAPUtils::LDAPConnection; my $ldap = new OpenSIPS::LDAPUtils::LDAPConnection; my @rows = $ldap-search("uid=andi","ou=people,ou=coreworks,ou=de"); Procedural interface: use OpenSIPS::LDAPUtils::LDAPConnection; my @rows = $ldap->search( new OpenSIPS::LDAPUtils::LDAPConfig(), "uid=andi","ou=people,ou=co reworks,ou=de"); This perl module offers a somewhat simplified interface to the Net::LDAP functionality. It is intended for cases where just a few attributes should be retrieved without the overhead of the full featured Net::LDAP. 2.7.1. Constructor new( [config, [authenticated]] ) Set up a new LDAP connection. The first argument, when given, should be a hash reference pointing to to the connection parameters, possibly an OpenSIPS::LDAPUtils::LDAPConfig object. This argument may be undef in which case a new (default) OpenSIPS::LDAPUtils::LDAPConfig object is used. When the optional second argument is a true value, the connection will be authenticated. Otherwise an anonymous bind is done. On success, a new LDAPConnection object is returned, otherwise the result is undef. 2.7.2. Function/Method search( conf, filter, base, [requested_attributes ...]) perform an ldap search, return the dn of the first matching directory entry, unless a specific attribute has been requested, in wich case the values(s) fot this attribute are returned. When the first argument (conf) is a OpenSIPS::LDAPUtils::LDAPConnection, it will be used to perform the queries. You can pass the first argument implicitly by using the "method" syntax. Otherwise the conf argument should be a reference to a hash containing the connection setup parameters as contained in a OpenSIPS::LDAPUtils::LDAPConf object. In this mode, the OpenSIPS::LDAPUtils::LDAPConnection from previous queries will be reused. 2.7.2.1. Arguments: conf configuration object, used to find host,port,suffix and use_ldap_checks filter ldap search filter, eg '(mail=some@domain)' base search base for this query. If undef use default suffix, concat base with default suffix if the last char is a ',' requested_attributes retrieve the given attributes instead of the dn from the ldap directory. 2.7.2.2. Result: Without any specific requested_attributes, return the dn of all matching entries in the LDAP directory. When some requested_attributes are given, return an array with those attibutes. When multiple entries match the query, the attribute lists are concatenated. 2.8. OpenSIPS::VDB This package is an (abstract) base class for all virtual databases. Derived packages can be configured to be used by OpenSIPS as a database. The base class itself should NOT be used in this context, as it does not provide any functionality. 2.9. OpenSIPS::Constants This package provides a number of constants taken from enums and defines of OpenSIPS header files. Unfortunately, there is no mechanism for updating the constants automatically, so check the values if you are in doubt. 2.10. OpenSIPS::VDB::Adapter::Speeddial This adapter can be used with the speeddial module. 2.11. OpenSIPS::VDB::Adapter::Alias This package is intended for usage with the alias_db module. The query VTab has to take two arguments and return an array of two arguments (user name/domain). 2.11.1. query(conds,retkeys,order) Queries the vtab with the given arguments for request conditions, keys to return and sort order column name. 2.12. OpenSIPS::VDB::Adapter::AccountingSIPtrace This package is an Adapter for the acc and siptrace modules, featuring only an insert operation. 2.13. OpenSIPS::VDB::Adapter::Describe This package is intended for debug usage. It will print information about requested functions and operations of a client module. Use this module to request schema information when creating new adapters. 2.14. OpenSIPS::VDB::Adapter::Auth This adapter is intended for usage with the auth_db module. The VTab should take a username as an argument and return a (plain text!) password. 2.15. OpenSIPS::VDB::ReqCond This package represents a request condition for database access, consisting of a column name, an operator (=, <, >, ...), a data type and a value. This package inherits from OpenSIPS::VDB::Pair and thus includes its methods. 2.15.1. new(key,op,type,name) Constructs a new Column object. 2.15.2. op() Returns or sets the current operator. 2.16. OpenSIPS::VDB::Pair This package represents database key/value pairs, consisting of a key, a value type, and the value. This package inherits from OpenSIPS::VDB::Value and thus has the same methods. 2.16.1. new(key,type,name) Constructs a new Column object. 2.16.2. key() Returns or sets the current key. 2.17. OpenSIPS::VDB::VTab This package handles virtual tables and is used by the OpenSIPS::VDB class to store information about valid tables. The package is not inteded for end user access. 2.17.1. new() Constructs a new VTab object 2.17.2. call(op,[args]) Invokes an operation on the table (insert, update, ...) with the given arguments. 2.18. OpenSIPS::VDB::Value This package represents a database value. Additional to the data itself, information about its type is stored. 2.18.1. stringification When accessing a OpenSIPS::VDB::Value object as a string, it simply returns its data regardless of its type. =cut use strict; package OpenSIPS::VDB::Value; use overload '""' => \&stringify; sub stringify { shift->{data} } use OpenSIPS; use OpenSIPS::Constants; our @ISA = qw ( OpenSIPS::Utils::Debug ); 2.18.2. new(type,data) Constructs a new Value object. Its data type and the data are passed as parameters. 2.18.3. type() Returns or sets the current data type. Please consider using the constants from OpenSIPS::Constants 2.18.4. data() Returns or sets the current data. 2.19. OpenSIPS::VDB::Column This package represents database column definition, consisting of a column name and its data type. 2.19.1. Stringification When accessing a OpenSIPS::VDB::Column object as a string, it simply returns its column name regardless of its type. =cut package OpenSIPS::VDB::Column; use overload '""' => \&stringify; sub stringify { shift->{name} } use OpenSIPS; use OpenSIPS::Constants; our @ISA = qw ( OpenSIPS::Utils::Debug ); 2.19.2. new(type,name) Constructs a new Column object. Its type and the name are passed as parameters. 2.19.3. type( ) Returns or sets the current type. Please consider using the constants from OpenSIPS::Constants 2.19.4. name() Returns or sets the current column name. 2.19.5. OpenSIPS::VDB::Result This class represents a VDB result set. It contains a column definition, plus an array of rows. Rows themselves are simply references to arrays of scalars. 2.19.6. new(coldefs,[row, row, ...]) The constructor creates a new Result object. Its first parameter is a reference to an array of OpenSIPS::VDB::Column objects. Additional parameters may be passed to provide initial rows, which are references to arrays of scalars. 2.19.7. coldefs() Returns or sets the column definition of the object. 2.19.8. rows() Returns or sets the rows of the object. Chapter 3. Perl samples Revision History Revision $Revision: 5901 $ $Date$ 3.1. sample directory There are a number of example scripts in the “samples/â€. They are documented well. Read them, it will explain a lot to you :) If you want to use any of these scripts directly in your implementation, you can use Perl's “require†mechanism to import them (just remember that you need to use quotes when require'ing .pl files). 3.1.1. Script descriptions The included sample scripts are described below: 3.1.1.1. branches.pl The minimal function in branches.pl demonstrates that you can access the "append_branch" function from within perl, just as you would have done from your normal configuration file. You'll find documentation on the concepts of branching in the OpenSIPS documentation. 3.1.1.2. firstline.pl Message's first_line structure may be evaluated. Message can be either of SIP_REQUEST or SIP_REPLY. Depending on that, different information can be received. This script demonstrates these functions. 3.1.1.3. flags.pl The perl module provides access to OpenSIPS's flagging mechanism. The flag names available for OpenSIPS modules are made available through the OpenSIPS::Constants package, so you can flag messages as "green", "magenta" etc. The first function, setflag, demonstrates how the "green" flag is set. In the second function, readflag, the "green" and "magenta" flags are evaluated. 3.1.1.4. functions.pl This sample script demonstrates different things related to calling functions from within perl, and the different types of functions you can offer for OpenSIPS access. “exportedfuncs†simply demonstrates that you can use the moduleFunction method to call functions offered by other modules. The results are equivalent to calling these functions from your config file. In the demonstrated case, telephone calls with a destination number beginning with 555... are rejected with an internal server error. Other destination addresses are passed to the alias_db module. Please note that the moduleFunction method is not fully available in OpenSIPS 1.2. See the method's documentation for details. “paramfunc†shows that you can pass arbitrary strings to perl functions. Do with them whatever you want :) “autotest†demonstrates that unknown functions in OpenSIPS::Message objects are automatically transformed into calls to module functions. The “diefuncâ€s show that dying perl scripts - by "manual" dying, or because of script errors - are handled by the OpenSIPS package. The error message is logged through OpenSIPS's logging mechanism. Please note that this only works correctly if you do NOT overwrite the default die handler. Oh, yes, that works for warnings, too. 3.1.1.5. headers.pl Header extraction is among the most crucial functionalities while processing SIP messages. This sample script demonstrates access to header names and values within two sample functions. “headernames†extracts all header names and logs their names. “someheaders†logs the contents of the two headers, “To†and “WWW-Contactâ€. As you can see, headers that occur more than once are retrieved as an array, which may be accessed by Perl's array accessing methods. 3.1.1.6. logging.pl For debugging purposes, you probably want to write messages to the syslog. The “logdemo†shows three ways to access the OpenSIPS log function: it is available through the OpenSIPS class as well as through the OpenSIPS::Message class. Remember that you can use exported functions from other modules. You may thus as well use the “xlog†module and it's xlog function. The L_INFO, L_DBG, L_ERR, L_CRIT... constants are available through the OpenSIPS::Constants package. 3.1.1.7. messagedump.pl This script demonstrates how to access the whole message header of the current message. Please note that modifications on the message made by earlier function calls in your configuration script may NOT be reflected in this dump. 3.1.1.8. persistence.pl When processing SIP messages, you may want to use persistent data across multiple calls to your Perl functions. Your first option is to use global variables in your script. Unfortunately, these globals are not visible from the mulitple instances of OpenSIPS. You may want to use a mechanism such as the IPC::Shareable shared memory access package to correct this. 3.1.1.9. phonenumbers.pl The OpenSIPS::Utils::PhoneNumbers package provides two methods for the transformation of local to canonical telephone numbers, and vice versa. This script demonstrates it's use. 3.1.1.10. pseudovars.pl This script demonstrates the Perl module's “pseudoVar†method. It may be used to retrieve the values of current pseudo variables. You might notice that there is no particular function for setting pseudo variables; you may use the exported functions from the avpops module, though. Chapter 4. Frequently Asked Questions 4.1. Are there known bugs in the Perl module? The Perl module does have a few shortcomings that may be regarded as bugs. * Missing module functions. Not all functions of other modules are available for Perl access. The reason for this is a design property of OpenSIPS. Making available more functions is work in progress. * Perl and threads. Perl itself is, when compiled with the correct parameters, thread safe; unfortunately, not all Perl modules are. The DBI modules, especially (but not restricted to) DBI::ODBC are known NOT to be thread safe. Using DBI::ODBC -- and possibly other non-thread-safe Perl extensions -- may result in erroneous behavior of OpenSIPS, including (but not restricted to) server crashes and wrong routing. 4.2. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 4.3. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 4.4. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/perl/doc/000077500000000000000000000000001300170765700165425ustar00rootroot00000000000000opensips-2.2.2/modules/perl/doc/perl.xml000066400000000000000000000024051300170765700202270ustar00rootroot00000000000000 %docentities; ]> perl Module &osipsname; Bastian Friedrich Collax GmbH
bastian.friedrich@collax.com
Bastian Friedrich
bastian.friedrich@collax.com
2007 Collax GmbH $Revision: 5901 $ $Date$
&admin; &pod; &samples; &faq;
opensips-2.2.2/modules/perl/doc/perl_admin.xml000066400000000000000000000173661300170765700214130ustar00rootroot00000000000000 &adminguide;
Overview The time needed when writing a new OpenSIPS module unfortunately is quite high, while the options provided by the configuration file are limited to the features implemented in the modules. With this Perl module, you can easily implement your own OpenSIPS extensions in Perl. This allows for simple access to the full world of CPAN modules. SIP URI rewriting could be implemented based on regular expressions; accessing arbitrary data backends, e.g. LDAP or Berkeley DB files, is now extremely simple.
Installing the module This Perl module is loaded in opensips.cfg (just like all the other modules) with loadmodule("/path/to/perl.so");. For the Perl module to compile, you need a reasonably recent version of perl (tested with 5.8.8) linked dynamically. It is strongly advised to use a threaded version. The default binary packages from your favorite Linux distribution should work fine. Cross compilation is supported by the Makefile. You need to set the environment variables PERLLDOPTS, PERLCCOPTS and TYPEMAP to values similar to the output of PERLLDOPTS: perl -MExtUtils::Embed -e ldopts PERLCCOPTS: perl -MExtUtils::Embed -e ccopts TYPEMAP: echo "`perl -MConfig -e 'print $Config{installprivlib}'`/ExtUtils/typemap" The exact position of your (precompiled!) perl libraries depends on the setup of your environment.
Using the module The Perl module has two interfaces: The perl side, and the OpenSIPS side. Once a Perl function is defined and loaded via the module parameters (see below), it may be called in OpenSIPS's configuration at an arbitary point. E.g., you could write a function "ldap_alias" in Perl, and then execute ... if (perl_exec("ldap_alias")) { ... } ... just as you would have done with the current alias_db module. The functions you can use are listed in the "Exported Functions" section below. On the Perl side, there are a number of functions that let you read and modify the current SIP message, such as the RURI or the message flags. An introduction to the Perl interface and the full reference documentation can be found below.
Dependencies
&osips; Modules The following modules must be loaded before this module: The "sl" module is needed for sending replies uppon fatal errors. All other modules can be accessed from the Perl module, though.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: Perl 5.8.x or later Additionally, a number of perl modules should be installed. The OpenSIPS::LDAPUtils package relies on Net::LDAP to be installed. One of the sample scripts needs IPC::Shareable This module has been developed and tested with Perl 5.8.8, but should work with any 5.8.x release. Compilation is possible with 5.6.x, but its behavior is unsupported. Earlier versions do not work. On current Debian systems, at least the following packages should be installed: perl perl-base perl-modules libperl5.8 libperl-dev libnet-ldap-perl libipc-shareable-perl It was reported that other Debian-style distributions (such as Ubuntu) need the same packages. On SuSE systems, at least the following packages should be installed: perl perl-ldap IPC::Shareable perl module from CPAN Although SuSE delivers a lot of perl modules, others may have to be fetched from CPAN. Consider using the program cpan2rpm - which, in turn, is available on CPAN. It creates RPM files from CPAN.
Exported Parameters
<varname>filename</varname> (string) This is the file name of your script. This may be set once only, but it may include an arbitary number of functions and use as many Perl module as necessary. May not be empty! Set <varname>filename</varname> parameter ... modparam("perl", "filename", "/home/john/opensips/myperl.pl") ...
<varname>modpath</varname> (string) The path to the Perl modules included (OpenSIPS.pm et.al). It is not absolutely crucial to set this path, as you may install the Modules in Perl's standard path, or update the %INC variable from within your script. Using this module parameter is the standard behavior, though. Set <varname>modpath</varname> parameter ... modparam("perl", "modpath", "/usr/local/lib/opensips/perl/") ...
Exported Functions
<function moreinfo="none">perl_exec_simple(func, [param])</function> Calls a perl function without passing it the current SIP message. May be used for very simple simple requests that do not have to fiddle with the message themselves, but rather return information values about the environment. The first parameter is the function to be called. An arbitrary string may optionally be passed as a parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE and BRANCH_ROUTE. <function>perl_exec_simple()</function> usage ... if (method=="INVITE") { perl_exec_simple("dosomething", "on invite messages"); }; ...
<function moreinfo="none">perl_exec(func, [param])</function> Calls a perl function with passing it the current SIP message. The SIP message is reflected by a Perl module that gives you access to the information in the current SIP message (OpenSIPS::Message). The first parameter is the function to be called. An arbitrary string may be passed as a parameter. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE and BRANCH_ROUTE. <function>perl_exec()</function> usage ... if (perl_exec("ldapalias")) { ... }; ...
opensips-2.2.2/modules/perl/doc/perl_faq.xml000066400000000000000000000044351300170765700210630ustar00rootroot00000000000000 &faqguide; Are there known bugs in the Perl module? The Perl module does have a few shortcomings that may be regarded as bugs. Missing module functions. Not all functions of other modules are available for Perl access. The reason for this is a design property of OpenSIPS. Making available more functions is work in progress. Perl and threads. Perl itself is, when compiled with the correct parameters, thread safe; unfortunately, not all Perl modules are. The DBI modules, especially (but not restricted to) DBI::ODBC are known NOT to be thread safe. Using DBI::ODBC -- and possibly other non-thread-safe Perl extensions -- may result in erroneous behavior of OpenSIPS, including (but not restricted to) server crashes and wrong routing. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/perl/doc/perl_pod.xml000066400000000000000000001053471300170765700211020ustar00rootroot00000000000000 OpenSIPS Perl API
OpenSIPS This module provides access to a limited number of OpenSIPS core functions. As the most interesting functions deal with SIP messages, they are located in the OpenSIPS::Message class below.
log(level,message) Logs the message with OpenSIPS's logging facility. The logging level is one of the following: Please note that this method is NOT automatically exported, as it collides with the perl function log (which calculates the logarithm). Either explicitly import the function (via ), or call it with its full name:
OpenSIPS::Message This package provides access functions for an OpenSIPS structure and its sub-components. Through its means it is possible to fully configure alternative routing decisions.
getType() Returns one of the constants SIP_REQUEST, SIP_REPLY, SIP_INVALID stating the type of the current message.
getStatus() Returns the status code of the current Reply message. This function is invalid in Request context!
getReason() Returns the reason of the current Reply message. This function is invalid in Request context!
getVersion() Returns the version string of the current SIP message.
getRURI() This function returns the recipient URI of the present SIP message: getRURI();]]> getRURI returns a string. See getParsedRURI() below how to receive a parsed structure. This function is valid in request messages only.
getMethod() Returns the current method, such as , , and so on. getMethod();]]> This function is valid in request messages only.
getFullHeader() Returns the full message header as present in the current message. You might use this header to further work with it with your favorite MIME package. getFullHeader();]]>
getBody() Returns the message body.
getMessage() Returns the whole message including headers and body.
getHeader(name) Returns the body of the first message header with this name. getHeader("To");]]> ]]>
getHeaderNames() Returns an array of all header names. Duplicates possible!
moduleFunction(func,string1,string2) Search for an arbitrary function in module exports and call it with the parameters self, string1, string2. and/or may be omitted. As this function provides access to the functions that are exported to the OpenSIPS configuration file, it is autoloaded for unknown functions. Instead of writing moduleFunction("sl_send_reply", "500", "Internal Error"); $m->moduleFunction("xlog", "L_INFO", "foo");]]> you may as well write sl_send_reply("500", "Internal Error"); $m->xlog("L_INFO", "foo");]]> WARNING In OpenSIPS 1.2, only a limited subset of module functions is available. This restriction will be removed in a later version. Here is a list of functions that are expected to be working (not claiming completeness):
log(level,message) (deprecated type) Logs the message with OpenSIPS's logging facility. The logging level is one of the following: The logging function should be accessed via the OpenSIPS module variant. This one, located in OpenSIPS::Message, is deprecated.
rewrite_ruri(newruri) Sets a new destination (recipient) URI. Useful for rerouting the current message/call. getRURI() =~ m/\@somedomain.net/) { $m->rewrite_ruri("sip:dispatcher\@organization.net"); }]]>
setFlag(flag) Sets a message flag. The constants as known from the C API may be used, when Constants.pm is included.
resetFlag(flag) Resets a message flag.
isFlagSet(flag) Returns whether a message flag is set or not.
pseudoVar(string) Returns a new string where all pseudo variables are substituted by their values. Can be used to receive the values of single variables, too. Please remember that you need to escape the '$' sign in perl strings!
append_branch(branch,qval) Append a branch to current message.
serialize_branches(clean_before) Serialize branches.
next_branches() Next branches.
getParsedRURI() Returns the current destination URI as an OpenSIPS::URI object.
OpenSIPS::URI This package provides functions for access to sip_uri structures.
user() Returns the user part of this URI.
host() Returns the host part of this URI.
passwd() Returns the passwd part of this URI.
port() Returns the port part of this URI.
params() Returns the params part of this URI.
headers() Returns the headers part of this URI.
transport() Returns the transport part of this URI.
ttl() Returns the ttl part of this URI.
user_param() Returns the user_param part of this URI.
maddr() Returns the maddr part of this URI.
method() Returns the method part of this URI.
lr() Returns the lr part of this URI.
r2() Returns the r2 part of this URI.
transport_val() Returns the transport_val part of this URI.
ttl_val() Returns the ttl_val part of this URI.
user_param_val() Returns the user_param_val part of this URI.
maddr_val() Returns the maddr_val part of this URI.
method_val() Returns the method_val part of this URI.
lr_val() Returns the lr_val part of this URI.
r2_val() Returns the r2_val part of this URI.
OpenSIPS::AVP This package provides access functions for OpenSIPS's AVPs. These variables can be created, evaluated, modified and removed through this package. Please note that these functions do NOT support the notation used in the configuration file, but directly work on strings or numbers. See documentation of add method below.
add(name,val) Add an AVP. Add an OpenSIPS AVP to its environment. name and val may both be integers or strings; this function will try to guess what is correct. Please note that is something different than due to this evaluation: The first will create _string_ AVPs with the name 10, while the latter will create a numerical AVP. You can modify/overwrite AVPs with this function.
get(name) get an OpenSIPS AVP:
destroy(name) Destroy an AVP.
OpenSIPS::Utils::PhoneNumbers OpenSIPS::Utils::PhoneNumbers - Functions for canonical forms of phone numbers. "0", internationalPrefix => "+", longDistancePrefix => "0", areaCode => "761", pbxCode => "456842", countryCode => "49" ); $canonical = $phonenumbers->canonicalForm("07612034567"); $number = $phonenumbers->dialNumber("+497612034567");]]> A telphone number starting with a plus sign and containing all dial prefixes is in canonical form. This is usally not the number to dial at any location, so the dialing number depends on the context of the user/system. The idea to canonicalize numbers were taken from hylafax. Example: +497614514829 is the canonical form of my phone number, 829 is the number to dial at Pyramid, 4514829 is the dialing number from Freiburg are and so on. To canonicalize any number, we strip off any dial prefix we find and then add the prefixes for the location. So, when the user enters the number 04514829 in context pyramid, we remove the publicAccessPrefix (at Pyramid this is 0) and the pbxPrefix (4514 here). The result is 829. Then we add all the general dial prefixes - 49 (country) 761 (area) 4514 (pbx) and 829, the number itself => +497614514829 To get the dialing number from a canonical phone number, we substract all general prefixes until we have something As said before, the interpretation of a phone number depends on the context of the location. For the functions in this package, the context is created through the operator. The following fields should be set: This module exports the following functions when ed:
new(publicAccessPrefix,internationalPrefix,longDistancePrefix,countryCode,areaCode,pbxCode) The new operator returns an object of this type and sets its locational context according to the passed parameters. See OpenSIPS::Utils::PhoneNumbers above.
canonicalForm( number [, context] ) Convert a phone number (given as first argument) into its canonical form. When no context is passed in as the second argument, the default context from the systems configuration file is used.
dialNumber( number [, context] ) Convert a canonical phone number (given in the first argument) into a number to to dial. WHen no context is given in the second argument, a default context from the systems configuration is used.
OpenSIPS::LDAPUtils::LDAPConf OpenSIPS::LDAPUtils::LDAPConf - Read openldap config from standard config files. This module may be used to retrieve the global LDAP configuration as used by other LDAP software, such as and . The configuration is usualy stored in When used from an account with sufficient privilegs (e.g. root), the ldap manager passwort is also retrieved.
Constructor new() Returns a new, initialized object.
Method base() Returns the servers base-dn to use when doing queries.
Method host() Returns the ldap host to contact.
Method port() Returns the ldap servers port.
Method uri() Returns an uri to contact the ldap server. When there is no ldap_uri in the configuration file, an uri is constucted from host and port.
Method rootbindpw() Returns the ldap "root" password. Note that the is only available when the current account has sufficient privilegs to access .
Method rootbinddn() Returns the DN to use for "root"-access to the ldap server.
Method binddn() Returns the DN to use for authentication to the ldap server. When no bind dn has been specified in the configuration file, returns the .
Method bindpw() Returns the password to use for authentication to the ldap server. When no bind password has been specified, returns the if any.
OpenSIPS::LDAPUtils::LDAPConnection OpenSIPS::LDAPUtils::LDAPConnection - Perl module to perform simple LDAP queries. OO-Style interface: Procedural interface: search( new OpenSIPS::LDAPUtils::LDAPConfig(), "uid=andi","ou=people,ou=coreworks,ou=de");]]> This perl module offers a somewhat simplified interface to the functionality. It is intended for cases where just a few attributes should be retrieved without the overhead of the full featured .
Constructor new( [config, [authenticated]] ) Set up a new LDAP connection. The first argument, when given, should be a hash reference pointing to to the connection parameters, possibly an object. This argument may be in which case a new (default) object is used. When the optional second argument is a true value, the connection will be authenticated. Otherwise an anonymous bind is done. On success, a new object is returned, otherwise the result is .
Function/Method search( conf, filter, base, [requested_attributes ...]) perform an ldap search, return the dn of the first matching directory entry, unless a specific attribute has been requested, in wich case the values(s) fot this attribute are returned. When the first argument (conf) is a , it will be used to perform the queries. You can pass the first argument implicitly by using the "method" syntax. Otherwise the argument should be a reference to a hash containing the connection setup parameters as contained in a object. In this mode, the from previous queries will be reused.
Arguments: conf configuration object, used to find host,port,suffix and use_ldap_checks filter ldap search filter, eg '(mail=some@domain)' base search base for this query. If undef use default suffix, concat base with default suffix if the last char is a ',' requested_attributes retrieve the given attributes instead of the dn from the ldap directory.
Result: Without any specific , return the dn of all matching entries in the LDAP directory. When some are given, return an array with those attibutes. When multiple entries match the query, the attribute lists are concatenated.
OpenSIPS::VDB This package is an (abstract) base class for all virtual databases. Derived packages can be configured to be used by OpenSIPS as a database. The base class itself should NOT be used in this context, as it does not provide any functionality.
OpenSIPS::Constants This package provides a number of constants taken from enums and defines of OpenSIPS header files. Unfortunately, there is no mechanism for updating the constants automatically, so check the values if you are in doubt.
OpenSIPS::VDB::Adapter::Speeddial This adapter can be used with the speeddial module.
OpenSIPS::VDB::Adapter::Alias This package is intended for usage with the alias_db module. The query VTab has to take two arguments and return an array of two arguments (user name/domain).
query(conds,retkeys,order) Queries the vtab with the given arguments for request conditions, keys to return and sort order column name.
OpenSIPS::VDB::Adapter::AccountingSIPtrace This package is an Adapter for the acc and siptrace modules, featuring only an insert operation.
OpenSIPS::VDB::Adapter::Describe This package is intended for debug usage. It will print information about requested functions and operations of a client module. Use this module to request schema information when creating new adapters.
OpenSIPS::VDB::Adapter::Auth This adapter is intended for usage with the auth_db module. The VTab should take a username as an argument and return a (plain text!) password.
OpenSIPS::VDB::ReqCond This package represents a request condition for database access, consisting of a column name, an operator (=, <, >, ...), a data type and a value. This package inherits from OpenSIPS::VDB::Pair and thus includes its methods.
new(key,op,type,name) Constructs a new Column object.
op() Returns or sets the current operator.
OpenSIPS::VDB::Pair This package represents database key/value pairs, consisting of a key, a value type, and the value. This package inherits from OpenSIPS::VDB::Value and thus has the same methods.
new(key,type,name) Constructs a new Column object.
key() Returns or sets the current key.
OpenSIPS::VDB::VTab This package handles virtual tables and is used by the OpenSIPS::VDB class to store information about valid tables. The package is not inteded for end user access.
new()
call(op,[args]) Invokes an operation on the table (insert, update, ...) with the given arguments.
OpenSIPS::VDB::Value This package represents a database value. Additional to the data itself, information about its type is stored.
stringification When accessing a OpenSIPS::VDB::Value object as a string, it simply returns its data regardless of its type. =cut use strict; package OpenSIPS::VDB::Value; use overload '""' => \&stringify; sub stringify { shift->{data} } use OpenSIPS; use OpenSIPS::Constants; our @ISA = qw ( OpenSIPS::Utils::Debug );
new(type,data) Constructs a new Value object. Its data type and the data are passed as parameters.
type() Returns or sets the current data type. Please consider using the constants from OpenSIPS::Constants
data() Returns or sets the current data.
OpenSIPS::VDB::Column This package represents database column definition, consisting of a column name and its data type.
Stringification When accessing a OpenSIPS::VDB::Column object as a string, it simply returns its column name regardless of its type. =cut package OpenSIPS::VDB::Column; use overload '""' => \&stringify; sub stringify { shift->{name} } use OpenSIPS; use OpenSIPS::Constants; our @ISA = qw ( OpenSIPS::Utils::Debug );
new(type,name) Constructs a new Column object. Its type and the name are passed as parameters.
type( ) Returns or sets the current type. Please consider using the constants from OpenSIPS::Constants
name() Returns or sets the current column name.
OpenSIPS::VDB::Result This class represents a VDB result set. It contains a column definition, plus an array of rows. Rows themselves are simply references to arrays of scalars.
new(coldefs,[row, row, ...]) The constructor creates a new Result object. Its first parameter is a reference to an array of OpenSIPS::VDB::Column objects. Additional parameters may be passed to provide initial rows, which are references to arrays of scalars.
coldefs()
rows()
opensips-2.2.2/modules/perl/doc/perl_samples.xml000066400000000000000000000146171300170765700217630ustar00rootroot00000000000000 $Revision: 5901 $ $Date$ Perl samples
sample directory There are a number of example scripts in the samples/. They are documented well. Read them, it will explain a lot to you :) If you want to use any of these scripts directly in your implementation, you can use Perl's require mechanism to import them (just remember that you need to use quotes when require'ing .pl files).
Script descriptions The included sample scripts are described below:
branches.pl The minimal function in branches.pl demonstrates that you can access the "append_branch" function from within perl, just as you would have done from your normal configuration file. You'll find documentation on the concepts of branching in the OpenSIPS documentation.
firstline.pl Message's first_line structure may be evaluated. Message can be either of SIP_REQUEST or SIP_REPLY. Depending on that, different information can be received. This script demonstrates these functions.
flags.pl The perl module provides access to OpenSIPS's flagging mechanism. The flag names available for OpenSIPS modules are made available through the OpenSIPS::Constants package, so you can flag messages as "green", "magenta" etc. The first function, setflag, demonstrates how the "green" flag is set. In the second function, readflag, the "green" and "magenta" flags are evaluated.
functions.pl This sample script demonstrates different things related to calling functions from within perl, and the different types of functions you can offer for OpenSIPS access. exportedfuncs simply demonstrates that you can use the moduleFunction method to call functions offered by other modules. The results are equivalent to calling these functions from your config file. In the demonstrated case, telephone calls with a destination number beginning with 555... are rejected with an internal server error. Other destination addresses are passed to the alias_db module. Please note that the moduleFunction method is not fully available in OpenSIPS 1.2. See the method's documentation for details. paramfunc shows that you can pass arbitrary strings to perl functions. Do with them whatever you want :) autotest demonstrates that unknown functions in OpenSIPS::Message objects are automatically transformed into calls to module functions. The diefuncs show that dying perl scripts - by "manual" dying, or because of script errors - are handled by the OpenSIPS package. The error message is logged through OpenSIPS's logging mechanism. Please note that this only works correctly if you do NOT overwrite the default die handler. Oh, yes, that works for warnings, too.
headers.pl Header extraction is among the most crucial functionalities while processing SIP messages. This sample script demonstrates access to header names and values within two sample functions. headernames extracts all header names and logs their names. someheaders logs the contents of the two headers, To and WWW-Contact. As you can see, headers that occur more than once are retrieved as an array, which may be accessed by Perl's array accessing methods.
logging.pl For debugging purposes, you probably want to write messages to the syslog. The logdemo shows three ways to access the OpenSIPS log function: it is available through the OpenSIPS class as well as through the OpenSIPS::Message class. Remember that you can use exported functions from other modules. You may thus as well use the xlog module and it's xlog function. The L_INFO, L_DBG, L_ERR, L_CRIT... constants are available through the OpenSIPS::Constants package.
messagedump.pl This script demonstrates how to access the whole message header of the current message. Please note that modifications on the message made by earlier function calls in your configuration script may NOT be reflected in this dump.
persistence.pl When processing SIP messages, you may want to use persistent data across multiple calls to your Perl functions. Your first option is to use global variables in your script. Unfortunately, these globals are not visible from the mulitple instances of OpenSIPS. You may want to use a mechanism such as the IPC::Shareable shared memory access package to correct this.
phonenumbers.pl The OpenSIPS::Utils::PhoneNumbers package provides two methods for the transformation of local to canonical telephone numbers, and vice versa. This script demonstrates it's use.
pseudovars.pl This script demonstrates the Perl module's pseudoVar method. It may be used to retrieve the values of current pseudo variables. You might notice that there is no particular function for setting pseudo variables; you may use the exported functions from the avpops module, though.
opensips-2.2.2/modules/perl/doc/samples/000077500000000000000000000000001300170765700202065ustar00rootroot00000000000000opensips-2.2.2/modules/perl/doc/samples/branches.pl000066400000000000000000000001261300170765700223270ustar00rootroot00000000000000sub appbranch { my $m = shift; $m->append_branch("sip:new\@address"); return 1; } opensips-2.2.2/modules/perl/doc/samples/firstline.pl000066400000000000000000000006651300170765700225510ustar00rootroot00000000000000use OpenSIPS; use OpenSIPS::Constants; sub firstline { my $m = shift; my $v = $m->getVersion(); my $t = $m->getType(); OpenSIPS::log(L_INFO, "type is $t; version is $v\n"); if ($t == SIP_REQUEST) { OpenSIPS::log(L_INFO, "A request. Method is ".$m->getMethod()." to RURI ".$m->getRURI()."\n"); } else { OpenSIPS::log(L_INFO, "A reply. status is ".$m->getStatus()." with reason ".$m->getReason()."\n"); } return 1; } 1; opensips-2.2.2/modules/perl/doc/samples/flags.pl000066400000000000000000000006721300170765700216440ustar00rootroot00000000000000use OpenSIPS qw ( log ); use OpenSIPS::Constants; sub setflag{ my $m = shift; $m->setFlag(FL_GREEN); log(L_INFO, "Setting flag GREEN..."); return 1; } sub readflag { my $m = shift; if ($m->isFlagSet(FL_GREEN)) { log(L_INFO, "flag GREEN set"); } else { log(L_INFO, "flag GREEN not set"); } if ($m->isFlagSet(FL_MAGENTA)) { log(L_INFO, "flag MAGENTA set"); } else { log(L_INFO, "flag MAGENTA not set"); } return 1; } opensips-2.2.2/modules/perl/doc/samples/functions.pl000066400000000000000000000027071300170765700225610ustar00rootroot00000000000000use OpenSIPS qw ( log ); use OpenSIPS::Constants; #This function demonstrates how to call functions that are exported by other modules. sub exportedfuncs { my $m = shift; my $res = -1; if (($m->getMethod() eq "INVITE") || ($m->getMethod eq "CANCEL")) { $m->moduleFunction("xlog", "L_INFO", "x foobar"); if ($m->getRURI() =~ m/sip:555[0-9]+@/) { $m->moduleFunction("sl_send_reply", "500", "Error: 555 not available"); } else { $res = $m->moduleFunction("alias_db_lookup", "dbaliases"); } } return $res; } # This demonstrates that a parameter may be passed to a function sub paramfunc { my $m = shift; my $param = shift; log(L_INFO, "This function was called with a parameter: $param\n"); $param =~ s/l/L/g; log(L_INFO, "We can fiddle with it: $param\n"); return 1; } # The following function shows that you can use exported functions just as if they were "real". # This is achieved through Perl's autoloading mechanisms. sub autotest { my $m = shift; $m->xlog("L_ERR", "This logging is done via perl's autoload mechanism and the xlog function"); return 1; } # The following two functions demonstrate that the OpenSIPS perl module handles # dieing interpreters correctly. OpenSIPS itself will not crash. sub diefunc1 { my $m = shift; warn("I'll die in a moment..."); log(L_INFO, "About to die..."); die("Here I die!"); return 1; } sub diefunc2 { my $m = shift; NoSuchClass::NoSuchMethod(noSuchParameter); return 1; } opensips-2.2.2/modules/perl/doc/samples/headers.pl000066400000000000000000000010721300170765700221560ustar00rootroot00000000000000use OpenSIPS qw ( log ); use OpenSIPS::Constants; # Header demos sub headernames { my $m = shift; my @h = $m->getHeaderNames(); foreach (@h) { my $f = $_; log(L_INFO, "header $f\n"); } return 1; } sub someheaders { my $m = shift; foreach ( qw ( To WWW-Contact )) { my $srch = $_; my @h = $m->getHeader($srch); foreach (@h) { my $f = $_; log(L_INFO, "$srch retrieved from array is $f\n"); } log(L_INFO, "$srch as array is @h\n"); my $scalarto = $m->getHeader($srch); log(L_INFO, "$srch as scalar is $scalarto\n"); } return 1; } opensips-2.2.2/modules/perl/doc/samples/logging.pl000066400000000000000000000007251300170765700221750ustar00rootroot00000000000000use OpenSIPS qw ( log ); use OpenSIPS::Constants; # Demonstrating the three ways of internal logging. # Additionally, you have the xlog module... sub logdemo { my $m = shift; log(L_INFO, "Logging without OpenSIPS:: - import the symbol manually! See use statement...\n"); OpenSIPS::log(L_INFO, "This is the other preferred way: Include the package name"); $m->log(L_INFO, "The Message object has its own logging function. Rather don't use it ;)"); return 1; } opensips-2.2.2/modules/perl/doc/samples/messagedump.pl000066400000000000000000000006301300170765700230540ustar00rootroot00000000000000use OpenSIPS qw ( log ); use OpenSIPS::Constants; sub messagedump { my $m = shift; my $method = $m->getMethod(); log(L_INFO, "Method is $method\n"); my $uri = $m->getRURI(); log(L_INFO, "RURI is $uri\n"); open F,">>/tmp/opensips-perl-messagedump"; print F "=========================== New header ===========================\n"; my $fh = $m->getFullHeader(); print F $fh; close F; return 1; } opensips-2.2.2/modules/perl/doc/samples/persistence.pl000066400000000000000000000031761300170765700230760ustar00rootroot00000000000000use OpenSIPS qw ( log ); use OpenSIPS::Constants; use IPC::Shareable; my $foo = 0; my %lastcalltimes; my %lastcallids; # This function shows that normal persistent variables are _not_ valid between multiple instances of the OpenSIPS. # With the default setup of 4 children, the value logged is only incremented every 4th time. sub limited { use vars qw ($foo); log(L_INFO, "My persistent variable is $foo\n"); $foo++; return 1; } # Only one call per hour to each recipient # By using the IPC::Shareable perl module, real persistence between multiple instances can be achieved # Consider using the locking mechanisms available in IPC::Shareable when using in real world environments. sub onceperhour { my $m = shift; tie %lastcalltimes, IPC::Shareable, { key => "lstt", create => 1, destroy => 1 } or die "Error with IPC::Shareable"; tie %lastcallids, IPC::Shareable, { key => "lsti", create => 1, destroy => 1 } or die "Error with IPC::Shareable"; if ($m->getMethod() eq "INVITE") { my $dst = $m->getRURI(); my $currentid = $m->getHeader("Call-ID"); log(L_INFO, "invite to $dst"); my $lasttime = %lastcalltimes->{$dst}; log(L_INFO, "lasttime is $lasttime"); if ($lasttime) { # Value set. log(L_INFO, "I was already called at $lasttime"); if ((time() - $lasttime) < 3600) { if ($currentid eq %lastcallids->{$dst}) { log(L_INFO, "I know this call already. Doing nothing."); } else { log(L_INFO, "replying, return 1"); $m->sl_send_reply("488", "You already called this hour"); return 1; } } } %lastcalltimes->{$dst} = time(); %lastcallids->{$dst} = $currentid; } return -1; } opensips-2.2.2/modules/perl/doc/samples/phonenumbers.pl000066400000000000000000000023531300170765700232530ustar00rootroot00000000000000use OpenSIPS::Constants; use OpenSIPS::Utils::PhoneNumbers; # Demonstrate how the PhoneNumbers class works. sub canonical { my $m = shift; if ($m->getMethod() eq "INVITE") { my $p = new OpenSIPS::Utils::PhoneNumbers(publicAccessPrefix => "0", internationalPrefix => "+", longDistancePrefix => "0", countryCode => "49", areaCode => "761", pbxCode => "456842"); my $u = $m->getRURI(); if ($u =~ m/(.*)sip:([+0-9]+)\@(.*)/) { my $c = $p->canonicalForm($2); OpenSIPS::log(L_INFO, "canonical number in '$u' is '$c'\n"); } else { OpenSIPS::log(L_INFO, "Not a POTS number.\n"); } } return 1; } sub dialnr { my $m = shift; if ($m->getMethod() eq "INVITE") { my $p = new OpenSIPS::Utils::PhoneNumbers(publicAccessPrefix => "0", internationalPrefix => "+", longDistancePrefix => "0", countryCode => "49", areaCode => "761", pbxCode => "456842"); my $u = $m->getRURI(); if ($u =~ m/(.*)sip:([+0-9]+)\@(.*)/) { my $c = $p->dialNumber($2); OpenSIPS::log(L_INFO, "dial number in '$u' is '$c'\n"); } else { OpenSIPS::log(L_INFO, "Not a POTS number.\n"); } } return 1; } opensips-2.2.2/modules/perl/doc/samples/pseudovars.pl000066400000000000000000000003731300170765700227410ustar00rootroot00000000000000use OpenSIPS::Constants; sub pseudo { my $m = shift; my $varstring = "User: \$rU - UA: \$ua"; my $v = $m->pseudoVar($varstring); OpenSIPS::log(L_INFO, "pseudovar substitution demo; original: '$varstring' - substituted: '$v'\n"); return 1; } opensips-2.2.2/modules/perl/lib/000077500000000000000000000000001300170765700165435ustar00rootroot00000000000000opensips-2.2.2/modules/perl/lib/perl/000077500000000000000000000000001300170765700175055ustar00rootroot00000000000000opensips-2.2.2/modules/perl/lib/perl/OpenSIPS.pm000066400000000000000000000025051300170765700214450ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # package OpenSIPS; require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT = qw ( t ); @EXPORT_OK = qw ( log ); use OpenSIPS::Message; use OpenSIPS::Constants; use OpenSIPS::Utils::Debug; bootstrap OpenSIPS; BEGIN { $SIG{'__DIE__'} = sub { OpenSIPS::Message::log(undef, L_ERR, "perl error: $_[0]\n"); }; $SIG{'__WARN__'} = sub { OpenSIPS::Message::log(undef, L_ERR, "perl warning: $_[0]\n"); }; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/000077500000000000000000000000001300170765700211055ustar00rootroot00000000000000opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/Constants.pm000066400000000000000000000116151300170765700234230ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::Constants This package provides a number of constants taken from enums and defines of OpenSIPS header files. Unfortunately, there is no mechanism for updating the constants automatically, so check the values if you are in doubt. =cut package OpenSIPS::Constants; use Exporter; # Constants from route_struct.h our @ISA = qw(Exporter); our @EXPORT; BEGIN { sub const { my $c = shift; my $v = shift; eval "use constant $c => $v"; push(@EXPORT, $c); } ########################################################### ## Unfortunately, there are no "enum"s in Perl. The following blocks reflect ## some C headers from OpenSIPS. ## UPDATE THIS FILE WHEN THESE HEADER FILES CHANGE! ##################### # From parse_fline.h const( SIP_REQUEST => 1); const( SIP_REPLY => 2); const( SIP_INVALID => 0); ##################### # From route_struct.h const( EXP_T => 1); const( ELEM_T => 2); const( AND_OP => 1); const( OR_OP => 2); const( NOT_OP => 3); const( EQUAL_OP => 10); const( MATCH_OP => 11); const( GT_OP => 12); const( LT_OP => 13); const( GTE_OP => 14); const( LTE_OP => 15); const( DIFF_OP => 16); const( NO_OP => 17); const( METHOD_O => 1); const( URI_O => 2); const( FROM_URI_O => 3); const( TO_URI_O => 4); const( SRCIP_O => 5); const( SRCPORT_O => 6); const( DSTIP_O => 7); const( DSTPORT_O => 9); const( PROTO_O => 9); const( AF_O => 10); const( MSGLEN_O => 11); const( DEFAULT_O => 12); const( ACTION_O => 13); const( NUMBER_O => 14); const( RETCODE_O => 15); const( FORWARD_T => 1); const( SEND_T => 2); const( DROP_T => 3); const( LOG_T => 4); const( ERROR_T => 5); const( ROUTE_T => 6); const( EXEC_T => 7); const( SET_HOST_T => 8); const( SET_HOSTPORT_T => 9); const( SET_USER_T => 10); const( SET_USERPASS_T => 11); const( SET_PORT_T => 12); const( SET_URI_T => 13); const( IF_T => 14); const( MODULE_T => 15); const( SETFLAG_T => 16); const( RESETFLAG_T => 17); const( ISFLAGSET_T => 18); const( LEN_GT_T => 19); const( PREFIX_T => 20); const( STRIP_T => 21); const( STRIP_TAIL_T => 22); const( APPEND_BRANCH_T => 23); const( REVERT_URI_T => 24); const( FORCE_RPORT_T => 25); const( FORCE_LOCAL_RPORT_T => 26); const( SET_ADV_ADDR_T => 27); const( SET_ADV_PORT_T => 28); const( FORCE_TCP_ALIAS_T => 29); const( FORCE_SEND_SOCKET_T => 30); const( SERIALIZE_BRANCHES_T => 31); const( NEXT_BRANCHES_T => 32); const( RETURN_T => 33); const( EXIT_T => 34); const( SWITCH_T => 35); const( CASE_T => 36); const( DEFAULT_T => 37); const( SBREAK_T => 38); const( SET_DSTURI_T => 39); const( RESET_DSTURI_T => 40); const( ISDSTURISET_T => 41); const( NOSUBTYPE => 0); const( STRING_ST => 1); const( NET_ST => 2); const( NUMBER_ST => 3); const( IP_ST => 4); const( RE_ST => 5); const( PROXY_ST => 6); const( EXPR_ST => 7); const( ACTIONS_ST => 8); const( CMD_ST => 9); const( MODFIXUP_ST => 10); const( MYSELF_ST => 11); const( STR_ST => 12); const( SOCKID_ST => 13); const( SOCKETINFO_ST => 14); ##################### # non-enum constants from dprint.h: # Logging levels const( L_ALERT => -3); const( L_CRIT => -2); const( L_ERR => -1); const( L_WARN => 1); const( L_NOTICE => 2); const( L_INFO => 3); const( L_DBG => 4); ##################### # From flags.h: # Flags for setflag et.al const( FL_WHITE => 1); const( FL_YELLOW => 2); const( FL_GREEN => 3); const( FL_RED => 4); const( FL_BLUE => 5); const( FL_MAGENTA => 6); const( FL_BROWN => 7); const( FL_BLACK => 8); const( FL_ACC => 9); const( FL_MAX => 10); ##################### # From db/db_val.h: # Value types for virtual database classes const( DB_INT => 0 ); const( DB_BIGINT => 1 ); const( DB_DOUBLE => 2 ); const( DB_STRING => 3 ); const( DB_STR => 4 ); const( DB_DATETIME => 5 ); const( DB_BLOB => 6 ); const( DB_BITMAP => 7 ); ##################### # For VDB call types const( VDB_CALLTYPE_FUNCTION_SINGLE => 0 ); const( VDB_CALLTYPE_FUNCTION_MULTI => 0 ); const( VDB_CALLTYPE_METHOD => 1 ); } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/LDAPUtils/000077500000000000000000000000001300170765700226465ustar00rootroot00000000000000opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/LDAPUtils/LDAPConf.pm000066400000000000000000000101501300170765700245270ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # This file was kindly donated by Collax GmbH =head1 OpenSIPS::LDAPUtils::LDAPConf OpenSIPS::LDAPUtils::LDAPConf - Read openldap config from standard config files. use OpenSIPS::LDAPUtils::LDAPConf; my $conf = new OpenSIPS::LDAPUtils::LDAPConf(); This module may be used to retrieve the global LDAP configuration as used by other LDAP software, such as C and C. The configuration is usualy stored in C When used from an account with sufficient privilegs (e.g. root), the ldap manager passwort is also retrieved. =cut package OpenSIPS::LDAPUtils::LDAPConf; my $def_ldap_path = "/etc/openldap" ; my $def_conf = "ldap.conf"; my $def_secret = "ldap.secret"; =head2 Constructor new() Returns a new, initialized C object. =cut sub new { my $class = shift; my $self = _init( $def_ldap_path . "/" . $def_conf , $def_ldap_path . "/" . $def_secret); if( ! $self ) { return undef; } ## can happen during customizing bless $self , $class; return $self; } sub _init { my $conf_file = shift; my $secret_file = shift; my $result = {}; if( open(LDAPCONF,"<$conf_file") ) { while() { chomp; s/#.*$//; if( m/^\s*$/ ) { next; } my ($var,$val) = split(" ",$_,2); $result->{lc($var)} = $val; } close(LDAPCONF); } else { return undef; } if( -r $secret_file ) { if( open(SECRET,"<$secret_file") ) { my $secret = ; chomp $secret; $result->{'rootbindpw'} = $secret; close(SECRET); } } return $result; } =head2 Method base() Returns the servers base-dn to use when doing queries. =cut sub base { return $_[0]->{'base'}; } =head2 Method host() Returns the ldap host to contact. =cut sub host { return $_[0]->{'host'}; } =head2 Method port() Returns the ldap servers port. =cut sub port { return $_[0]->{'port'}; } =head2 Method uri() Returns an uri to contact the ldap server. When there is no ldap_uri in the configuration file, an C uri is constucted from host and port. =cut sub uri { my $self = shift; if( $self->{'ldap_uri'} ) { return $self->{'ldap_uri'}; } return "ldap://" . $self->host . ":" . $self->port ; } =head2 Method rootbindpw() Returns the ldap "root" password. Note that the C is only available when the current account has sufficient privilegs to access C. =cut sub rootbindpw { return $_[0]->{'rootbindpw'}; } =head2 Method rootbinddn() Returns the DN to use for "root"-access to the ldap server. =cut sub rootbinddn { return $_[0]->{'rootbinddn'}; } =head2 Method binddn() Returns the DN to use for authentication to the ldap server. When no bind dn has been specified in the configuration file, returns the C. =cut sub binddn { my $self = shift; if( $self->{'binddn'} ) { return $self->{'binddn'}; } return $self->rootbinddn; } =head2 Method bindpw() Returns the password to use for authentication to the ldap server. When no bind password has been specified, returns the C if any. =cut sub bindpw { my $self = shift; if( $self->{'bindpw'} ) { return $self->{'bindpw'}; } return $self->rootbindpw; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/LDAPUtils/LDAPConnection.pm000066400000000000000000000143411300170765700257470ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # This file was kindly donated by Collax GmbH =head1 OpenSIPS::LDAPUtils::LDAPConnection OpenSIPS::LDAPUtils::LDAPConnection - Perl module to perform simple LDAP queries. OO-Style interface: use OpenSIPS::LDAPUtils::LDAPConnection; my $ldap = new OpenSIPS::LDAPUtils::LDAPConnection; my @rows = $ldap-search("uid=andi","ou=people,ou=coreworks,ou=de"); Procedural interface: use OpenSIPS::LDAPUtils::LDAPConnection; my @rows = $ldap->search( new OpenSIPS::LDAPUtils::LDAPConfig(), "uid=andi","ou=people,ou=coreworks,ou=de"); This perl module offers a somewhat simplified interface to the C functionality. It is intended for cases where just a few attributes should be retrieved without the overhead of the full featured C. =cut package OpenSIPS::LDAPUtils::LDAPConnection; use OpenSIPS::LDAPUtils::LDAPConf; use Net::LDAP; use Authen::SASL; my $ldap_singleton = undef; =head2 Constructor new( [config, [authenticated]] ) Set up a new LDAP connection. The first argument, when given, should be a hash reference pointing to to the connection parameters, possibly an C object. This argument may be C in which case a new (default) C object is used. When the optional second argument is a true value, the connection will be authenticated. Otherwise an anonymous bind is done. On success, a new C object is returned, otherwise the result is C. =cut sub new { my $class = shift; my $conf = shift; my $doauth = shift; if( ! defined( $conf ) ) { $conf = new OpenSIPS::LDAPUtils::LDAPConf(); } #print STDERR "new ldap checks\n"; my $self = { conf => $conf, }; bless $self, $class; # # connect to ldap server and do an anonymous bind # $self->{'ldap'} = Net::LDAP->new( $self->conf->uri() ); # return undef if ldap connection failed if( ! $self->ldap ) { print STDERR "Could not connect to LDAP server.\n"; return undef; } if( ! $doauth ) { $self->ldap->bind ; # an anonymous bind } else { if( $self->conf->uri && ( $self->conf->uri =~ m/^ldapi:/ ) ) { my $sasl = new Authen::SASL( mechanism => 'EXTERNAL ANONYMOUS'); $self->ldap->bind($self->conf->binddn, sasl => $sasl ); } else { # only simple binds for now. $self->ldap->bind($self->conf->binddn, password => $self->conf->bindpw); } } return $self; }; sub DESTROY { my $self = shift; if( $self->ldap ) { $self->ldap->unbind(); delete $self->{'ldap'}; } } sub conf { return $_[0]->{'conf'}; } sub ldap { return $_[0]->{'ldap'}; } =head2 Function/Method search( conf, filter, base, [requested_attributes ...]) perform an ldap search, return the dn of the first matching directory entry, unless a specific attribute has been requested, in wich case the values(s) fot this attribute are returned. When the first argument (conf) is a C, it will be used to perform the queries. You can pass the first argument implicitly by using the "method" syntax. Otherwise the C argument should be a reference to a hash containing the connection setup parameters as contained in a C object. In this mode, the C from previous queries will be reused. =head3 Arguments: =over 12 =item conf configuration object, used to find host,port,suffix and use_ldap_checks =item filter ldap search filter, eg '(mail=some@domain)' =item base search base for this query. If undef use default suffix, concat base with default suffix if the last char is a ',' =item requested_attributes retrieve the given attributes instead of the dn from the ldap directory. =back =head3 Result: Without any specific C, return the dn of all matching entries in the LDAP directory. When some C are given, return an array with those attibutes. When multiple entries match the query, the attribute lists are concatenated. =cut sub search { my $conf = shift; my $ldap = undef; if( $conf->isa("OpenSIPS::LDAPUtils::LDAPConnection") ) { $ldap = $conf; } else { if( ! $ldap_singleton ) { $ldap_singleton = new OpenSIPS::LDAPUtils::LDAPConnection($conf); } return undef unless $ldap_singleton; $ldap = $ldap_singleton; } my $filter = shift; my $base = shift; my @requested_attr = @_ ; if( ! $base ) { $base = $ldap->conf->base; } elsif ( substr($base,-1) eq ',' ) { $base .= $ldap->conf->base; } my @search_extra = (); if( @requested_attr ) { push @search_extra , 'attrs' => [ @requested_attr ]; } my $mesg = $ldap->ldap->search ( base => $base, filter => $filter, #sizelimit => 100 , @search_extra ); if( $mesg->is_error ) { warn $mesg->error; return undef }; my @result=(); my $max_entries=$mesg->count; my $i; for($i = 0 ; $i < $max_entries ; $i++) { my $entry = $mesg->entry($i); foreach my $attr (@requested_attr) { push @result, $entry->get_value($attr); } } return @result; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/Message.pm000066400000000000000000000030511300170765700230260ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # package OpenSIPS::Message; require Exporter; require DynaLoader; our @ISA = qw(Exporter DynaLoader); our @EXPORT = qw ( t ); bootstrap OpenSIPS; sub AUTOLOAD{ use vars qw($AUTOLOAD); my $a = $AUTOLOAD; $a =~ s/^OpenSIPS::Message:://; my $l = scalar @_; if ($l == 0) { croak("Usage: $a(self, param1 = undef, param2 = undef)"); } elsif ($l == 1) { return OpenSIPS::Message::moduleFunction(@_[0], $a); } elsif ($l == 2) { return OpenSIPS::Message::moduleFunction(@_[0], $a, @_[1]); } elsif ($l == 3) { return OpenSIPS::Message::moduleFunction(@_[0], $a, @_[1], @_[2]); } else { croak("Usage: $a(self, param1 = undef, param2 = undef)"); } } sub DESTROY {} 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/Utils/000077500000000000000000000000001300170765700222055ustar00rootroot00000000000000opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/Utils/Debug.pm000066400000000000000000000021311300170765700235660ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2007 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # package OpenSIPS::Utils::Debug; use OpenSIPS qw ( log ); use OpenSIPS::Constants; #sub DESTROY { # my $class = shift; # OpenSIPS::log(OpenSIPS::Constants::L_CRIT, "perl/vdb:Debug:Dying object: $class\n"); #} 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/Utils/PhoneNumbers.pm000066400000000000000000000137131300170765700251550ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # This file was kindly donated by Collax GmbH =head1 OpenSIPS::Utils::PhoneNumbers OpenSIPS::Utils::PhoneNumbers - Functions for canonical forms of phone numbers. use OpenSIPS::Utils::PhoneNumbers; my $phonenumbers = new OpenSIPS::Utils::PhoneNumbers( publicAccessPrefix => "0", internationalPrefix => "+", longDistancePrefix => "0", areaCode => "761", pbxCode => "456842", countryCode => "49" ); $canonical = $phonenumbers->canonicalForm("07612034567"); $number = $phonenumbers->dialNumber("+497612034567"); A telphone number starting with a plus sign and containing all dial prefixes is in canonical form. This is usally not the number to dial at any location, so the dialing number depends on the context of the user/system. The idea to canonicalize numbers were taken from hylafax. Example: +497614514829 is the canonical form of my phone number, 829 is the number to dial at Pyramid, 4514829 is the dialing number from Freiburg are and so on. To canonicalize any number, we strip off any dial prefix we find and then add the prefixes for the location. So, when the user enters the number 04514829 in context pyramid, we remove the publicAccessPrefix (at Pyramid this is 0) and the pbxPrefix (4514 here). The result is 829. Then we add all the general dial prefixes - 49 (country) 761 (area) 4514 (pbx) and 829, the number itself => +497614514829 To get the dialing number from a canonical phone number, we substract all general prefixes until we have something As said before, the interpretation of a phone number depends on the context of the location. For the functions in this package, the context is created through the C operator. The following fields should be set: 'longDistancePrefix' 'areaCode' 'pbxCode' 'internationalPrefix' 'publicAccessPrefix' 'countryCode' This module exports the following functions when Ced: =cut package OpenSIPS::Utils::PhoneNumbers; use Exporter; our @ISA = qw(Exporter); our @EXPORT = qw( canonicalForm dialNumber ); =head2 new(publicAccessPrefix,internationalPrefix,longDistancePrefix,countryCode,areaCode,pbxCode) The new operator returns an object of this type and sets its locational context according to the passed parameters. See L above. =cut sub new { my $class = shift; my %setup = @_; my $self = { PAPrefix => %setup->{'publicAccessPrefix'}, IDPrefix => %setup->{'internationalPrefix'}, LDPrefix => %setup->{'longDistancePrefix'}, Country => %setup->{'countryCode'}, Area => %setup->{'areaCode'}, PBX => %setup->{'pbxCode'} }; bless $self, $class; return $self; } =head2 canonicalForm( number [, context] ) Convert a phone number (given as first argument) into its canonical form. When no context is passed in as the second argument, the default context from the systems configuration file is used. =cut sub canonicalForm { my $self = shift; my $number = shift; $number =~ s/[^+0-9]*//g; # strip white space etc. if( $number =~ m/^[^+]/ ) { $number =~ s/^$self->{'PAPrefix'}$self->{'IDPrefix'}/+/; # replace int. dialing code if( $number =~ m/^[^+]/ ) { $number =~ s/^$self->{'PAPrefix'}$->{'LDPrefix'}/+$self->{'Country'}/; # long distance number if( $number =~ m/^[^+]/ ) { $number =~ s/^$self->{'PAPrefix'}/+$self->{'Country'}$self->{'Area'}/; # local number if( $number =~ m/^[^+]/ ) { $number =~ s/^(.*)/+$self->{'Country'}$self->{'Area'}$self->{PBX}$1/; # else cononicalize } } } } return $number; } =head2 dialNumber( number [, context] ) Convert a canonical phone number (given in the first argument) into a number to to dial. WHen no context is given in the second argument, a default context from the systems configuration is used. =cut sub dialNumber { my $self = shift; my $number = shift; $number =~ s/[^+0-9]+//g; # strip syntactical sugar. $number =~ s/^$self->{'PAPrefix'}$self->{'IDPrefix'}$self->{'Country'}$self->{'Area'}$self->{'PBX'}//; # inhouse phone call $number =~ s/^$self->{'PAPrefix'}$self->{'IDPrefix'}$self->{'Country'}$self->{'Area'}/$self->{'PAPrefix'}/; # local phone call $number =~ s/^$self->{'PAPrefix'}$self->{'LDPrefix'}$self->{'Area'}$self->{'PBX'}//; # inhouse phone call $number =~ s/^$self->{'PAPrefix'}$self->{'LDPrefix'}$self->{'Area'}/$self->{'PAPrefix'}/; # local phone call $number =~ s/^$self->{'PAPrefix'}$self->{'IDPrefix'}$self->{'Country'}/$self->{'PAPrefix'}$self->{'LDPrefix'}/; # long distance call $number =~ s/^$self->{'PAPrefix'}$self->{'PBX'}//; # inhouse call $number =~ s/^[+]$self->{'Country'}$self->{'Area'}$self->{'PBX'}//; # inhouse phone call $number =~ s/^[+]$self->{'Country'}$self->{'Area'}/$self->{'PAPrefix'}/; # local phone call $number =~ s/^[+]$self->{'Country'}/$self->{'PAPrefix'}$self->{'LDPrefix'}/; # long distance call $number =~ s/^[+]/$self->{'PAPrefix'}$self->{'IDPrefix'}/; # international call return $number; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB.pm000066400000000000000000000064451300170765700220670ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB This package is an (abstract) base class for all virtual databases. Derived packages can be configured to be used by OpenSIPS as a database. The base class itself should NOT be used in this context, as it does not provide any functionality. =cut package OpenSIPS::VDB; use OpenSIPS; use OpenSIPS::Constants; use OpenSIPS::VDB::Column; use OpenSIPS::VDB::Pair; use OpenSIPS::VDB::ReqCond; use OpenSIPS::VDB::Result; use OpenSIPS::VDB::Value; use OpenSIPS::VDB::VTab; our @ISA = qw ( OpenSIPS::Utils::Debug ); sub new { my $class = shift; my $self = { tablename => undef, vtabs => {} }; bless $self, $class; return $self; } sub use_table { my $self = shift; my $v = shift; $self->{tablename} = $v; if ($v eq "version") { # "version" table is handled individually. Don't initialize VTabs. return 0; } if ($self->{vtabs}->{$v}) { return 0; } if ($v =~ m/^((.*)::)?([\w_][\d\w_]*)$/) { my $pkg; if (!$2) { $pkg = "main"; } else { $pkg = $2; } OpenSIPS::log(L_DBG, "perlvdb:VDB: Setting VTab: v is $v (pkg is $pkg, func/method is $3)\n"); if ($pkg->can($3)) { $self->{vtabs}->{$v} = new OpenSIPS::VDB::VTab( func => $pkg . "::" . $3); } elsif ($v->can("init")) { $v->init(); $self->{vtabs}->{$v} = new OpenSIPS::VDB::VTab( obj => $v ); } elsif ($v->can("new")) { my $obj = $v->new(); $self->{vtabs}->{$v} = new OpenSIPS::VDB::VTab( obj => $obj ); } else { OpenSIPS::log(L_ERR, "perlvdb:VDB: Invalid virtual table.\n"); return -1; } } else { OpenSIPS::log(L_ERR, "perlvdb:VDB: Invalid virtual table.\n"); return -1; } } sub _insert { my $self = shift; return $self->insert(@_); } sub _replace { my $self = shift; return $self->replace(@_); } sub _delete { my $self = shift; return $self->delete(@_); } sub _update { my $self = shift; return $self->update(@_); } sub _query { my $self = shift; return $self->query(@_); } sub insert { OpenSIPS::log(L_INFO, "perlvdb:Insert not implemented in base class.\n"); return -1; } sub replace { OpenSIPS::log(L_INFO, "perlvdb:Replace not implemented in base class.\n"); return -1; } sub delete { OpenSIPS::log(L_INFO, "perlvdb:Delete not implemented in base class.\n"); return -1; } sub update { OpenSIPS::log(L_INFO, "perlvdb:Update not implemented in base class.\n"); return -1; } sub query { OpenSIPS::log(L_INFO, "perlvdb:Query not implemented in base class.\n"); return -1; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/000077500000000000000000000000001300170765700215205ustar00rootroot00000000000000opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Adapter/000077500000000000000000000000001300170765700231005ustar00rootroot00000000000000opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Adapter/AccountingSIPtrace.pm000066400000000000000000000025551300170765700271320ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2007 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::Adapter::AccountingSIPtrace This package is an Adapter for the acc and siptrace modules, featuring only an insert operation. =cut package OpenSIPS::VDB::Adapter::AccountingSIPtrace; use OpenSIPS::VDB; use OpenSIPS::VDB::VTab; use OpenSIPS; use OpenSIPS::Constants; use Data::Dumper; our @ISA = qw ( OpenSIPS::VDB ); sub insert { my $self = shift; my $vals = shift; my $vtab = $self->{vtabs}->{$self->{tablename}}; return $vtab->call("insert", $vals); } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Adapter/Alias.pm000066400000000000000000000050251300170765700244710ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2007 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::Adapter::Alias This package is intended for usage with the alias_db module. The query VTab has to take two arguments and return an array of two arguments (user name/domain). =cut package OpenSIPS::VDB::Adapter::Alias; use OpenSIPS::Constants; use OpenSIPS qw ( log ); use OpenSIPS::VDB; use OpenSIPS::VDB::Column; use OpenSIPS::VDB::Result; use Data::Dumper; our @ISA = qw ( OpenSIPS::VDB ); =head2 query(conds,retkeys,order) Queries the vtab with the given arguments for request conditions, keys to return and sort order column name. =cut sub query { my $self = shift; my $conds = shift; my $retkeys = shift; # Unused value here. my $order = shift; # Unused value here. my @cols; my $alias_username = undef; my $alias_domain = undef; my $username; my $domain; for my $c (@$conds) { if (($c->key() eq "alias_username") && ($c->op() eq "=")) { $alias_username = $c->data(); } if (($c->key() eq "alias_domain") && ($c->op() eq "=")) { $alias_domain = $c->data(); } } my $vtab = $self->{vtabs}->{$self->{tablename}}; $newaddr = $vtab->call("query", $alias_username, $alias_domain); my $result; push @cols, new OpenSIPS::VDB::Column(DB_STRING, "username"); push @cols, new OpenSIPS::VDB::Column(DB_STRING, "domain"); if ($newaddr) { my @row; my $resval1 = new OpenSIPS::VDB::Value(DB_STRING, $newaddr->{username} ); push @row, $resval1; my $resval2 = new OpenSIPS::VDB::Value(DB_STRING, $newaddr->{domain} ); push @row, $resval2; $result = new OpenSIPS::VDB::Result(\@cols, (bless \@row, "OpenSIPS::Utils::Debug")); } else { $result = new OpenSIPS::VDB::Result(\@cols); } return $result; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Adapter/Auth.pm000066400000000000000000000044661300170765700243510ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2007 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::Adapter::Auth This adapter is intended for usage with the auth_db module. The VTab should take a username as an argument and return a (plain text!) password. =cut package OpenSIPS::VDB::Adapter::Auth; use OpenSIPS::Constants; use OpenSIPS qw ( log ); use OpenSIPS::VDB; use OpenSIPS::VDB::Column; use OpenSIPS::VDB::Result; use OpenSIPS::VDB::Adapter::TableVersions; use Data::Dumper; our @ISA = qw ( OpenSIPS::VDB ); sub query { my $self = shift; my $conds = shift; my $retkeys = shift; my $order = shift; # Unused value here. my @cols; my $username = undef; my $password = undef; if ($self->{tablename} eq "version") { return OpenSIPS::VDB::Adapter::TableVersions::version(@$conds[0]->data()); } if ((scalar @$conds != 1) || (scalar @$retkeys != 2)) { log(L_ERR, "perlvdb:Auth: Broken column count requested. Unknown behavior. Desperately exiting.\n"); return undef; } for my $c (@$conds) { $username = $c->data(); } for my $k (@$retkeys) { push @cols, new OpenSIPS::VDB::Column(DB_STRING, $k); } my $vtab = $self->{vtabs}->{$self->{tablename}}; $password = $vtab->call("query", $username); my $result; if ($password) { my @row; push @row, new OpenSIPS::VDB::Value(DB_STRING, $password); push @row, undef; $result = new OpenSIPS::VDB::Result(\@cols, (bless \@row, "OpenSIPS::Utils::Debug")); } else { $result = new OpenSIPS::VDB::Result(\@cols); } return $result; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Adapter/Describe.pm000066400000000000000000000064471300170765700251710ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2007 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::Adapter::Describe This package is intended for debug usage. It will print information about requested functions and operations of a client module. Use this module to request schema information when creating new adapters. =cut package OpenSIPS::VDB::Adapter::Describe; use OpenSIPS::Constants; use OpenSIPS qw ( log ); use OpenSIPS::VDB; use OpenSIPS::VDB::Column; use OpenSIPS::VDB::Result; use Data::Dumper; our @ISA = qw ( OpenSIPS::VDB ); sub use_table { my $self = shift; my $t = shift; log(L_INFO, "About to use table $t\n"); return 0; } sub insert { my $self = shift; my $pairs = shift; my $pairs_descr; for my $p (@$pairs) { $pairs_descr .= " (key = '".$p->key()."' type = '".$p->type()."' value = '".$p->data()."')"; } log(L_INFO, "perlvdb:Describe: An insert was found with the following data: $pairs_descr\n"); return 0; } sub replace { my $self = shift; return $self->insert(@_); } sub update { my $self = shift; my $conds = shift; my $pairs = shift; my $cond_descr; my $pairs_descr; for my $c (@$conds) { $cond_descr .= " (key = '".$c->key()."' op = '".$c->op()."' data = '".$c->data()."')"; } log(L_INFO, "perlvdb:Describe: An update was found with the following conditions: $cond_descr\n"); for my $p (@$pairs) { $pairs_descr .= " (key = '".$p->key()."' type = '".$p->type()."' value = '".$p->data()."')"; } log(L_INFO, "perlvdb:Describe: The following data are to be updated: $pairs_descr\n"); return 0; } sub delete { my $self = shift; my $conds = shift; my $cond_descr; for my $c (@$conds) { $cond_descr .= " (key = '".$c->key()."' op = '".$c->op()."' data = '".$c->data()."')"; } log(L_INFO, "perlvdb:Describe: A delete was found with the following conditions: $cond_descr\n"); return 0; } sub query { my $self = shift; my $conds = shift; my $retkeys = shift; my $order = shift; my $cond_descr; my $keys_descr; my @cols; for my $c (@$conds) { $cond_descr .= " (key = '".$c->key()."' op = '".$c->op()."' data = '".$c->data()."')"; } log(L_INFO, "perlvdb:Describe: A query was found with the following conditions: $cond_descr\n"); for my $k (@$retkeys) { $keys_descr .= " ($k)"; push @cols, new OpenSIPS::VDB::Column(DB_STRING, $k); } log(L_INFO, "perlvdb:Describe: The following keys were requested: $keys_descr\n"); log(L_INFO, "perlvdb:Describe: Order requested: '$order'\n"); return new OpenSIPS::VDB::Result(\@cols); } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Adapter/Speeddial.pm000066400000000000000000000041111300170765700253250ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2007 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::Adapter::Speeddial This adapter can be used with the speeddial module. =cut package OpenSIPS::VDB::Adapter::Speeddial; use OpenSIPS::Constants; use OpenSIPS qw ( log ); use OpenSIPS::VDB; use OpenSIPS::VDB::Column; use OpenSIPS::VDB::Result; our @ISA = qw ( OpenSIPS::VDB ); sub query { my $self = shift; my $conds = shift; my $retkeys = shift; # Unused value here. my $order = shift; # Unused value here. my @cols; my $requested_username; my $requested_sd_username; for my $c (@$conds) { if (($c->key() eq "username") && ($c->op() eq "=")) { $requested_username = $c->data(); } if (($c->key() eq "sd_username") && ($c->op() eq "=")) { $requested_sd_username = $c->data(); } } my $vtab = $self->{vtabs}->{$self->{tablename}}; $newaddr = $vtab->call("query", $requested_username, $requested_sd_username); my $result; push @cols, new OpenSIPS::VDB::Column(DB_STRING, "uid_name"); if ($newaddr) { my $resval = new OpenSIPS::VDB::Value(DB_STRING, $newaddr ); push my @row, $resval; $result = new OpenSIPS::VDB::Result(\@cols, (bless \@row, "OpenSIPS::Utils::Debug")); } else { $result = new OpenSIPS::VDB::Result(\@cols); } return $result; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Adapter/TableVersions.pm000066400000000000000000000025511300170765700262210ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2007 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # package OpenSIPS::VDB::Adapter::TableVersions; use OpenSIPS; use OpenSIPS::Constants; sub version { my $table = shift; # FIXME We need to split table name again - possibly, this is a function only, so this one will not work my $v = $table->version(); my @cols; my @row; push @cols, new OpenSIPS::VDB::Column(DB_INT, "table_version"); push @row, new OpenSIPS::VDB::Value(DB_INT, $v); return new OpenSIPS::VDB::Result(\@cols, (bless \@row, OpenSIPS::Utils::Debug)); } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Column.pm000066400000000000000000000037341300170765700233220ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::Column This package represents database column definition, consisting of a column name and its data type. =head2 Stringification When accessing a OpenSIPS::VDB::Column object as a string, it simply returns its column name regardless of its type. =cut package OpenSIPS::VDB::Column; use overload '""' => \&stringify; sub stringify { shift->{name} } use OpenSIPS; use OpenSIPS::Constants; our @ISA = qw ( OpenSIPS::Utils::Debug ); =head2 new(type,name) Constructs a new Column object. Its type and the name are passed as parameters. =cut sub new { my $class = shift; my $type = shift; my $name = shift; my $self = { type => $type, name => $name, }; bless $self, $class; return $self; } =head2 type( ) Returns or sets the current type. Please consider using the constants from OpenSIPS::Constants =cut sub type { my $self = shift; if (@_) { $self->{type} = shift; } return $self->{type}; } =head2 name() Returns or sets the current column name. =cut sub name { my $self = shift; if (@_) { $self->{name} = shift; } return $self->{name}; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Pair.pm000066400000000000000000000031711300170765700227530ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::Pair This package represents database key/value pairs, consisting of a key, a value type, and the value. This package inherits from OpenSIPS::VDB::Value and thus has the same methods. =cut package OpenSIPS::VDB::Pair; use OpenSIPS::VDB::Value; our @ISA = qw ( OpenSIPS::VDB::Value OpenSIPS::Utils::Debug ); =head2 new(key,type,name) Constructs a new Column object. =cut sub new { my $class = shift; my $key = shift; my $type = shift; my $data = shift; my $self = new OpenSIPS::VDB::Value($type, $data); bless $self, $class; $self->{key} = $key; return $self; } =head2 key() Returns or sets the current key. =cut sub key { my $self = shift; if (@_) { $self->{key} = shift; } return $self->{key}; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/ReqCond.pm000066400000000000000000000033351300170765700234150ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::ReqCond This package represents a request condition for database access, consisting of a column name, an operator (=, <, >, ...), a data type and a value. This package inherits from OpenSIPS::VDB::Pair and thus includes its methods. =cut package OpenSIPS::VDB::ReqCond; use OpenSIPS::VDB::Value; use OpenSIPS::VDB::Pair; our @ISA = qw ( OpenSIPS::VDB::Pair OpenSIPS::Utils::Debug ); =head2 new(key,op,type,name) Constructs a new Column object. =cut sub new { my $class = shift; my $key = shift; my $op = shift; my $type = shift; my $data = shift; my $self = new OpenSIPS::VDB::Pair($key, $type, $data); bless $self, $class; $self->{op} = $op; return $self; } =head2 op() Returns or sets the current operator. =cut sub op { my $self = shift; if (@_) { $self->{op} = shift; } return $self->{op}; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Result.pm000066400000000000000000000037771300170765700233520ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # use strict; =head2 OpenSIPS::VDB::Result This class represents a VDB result set. It contains a column definition, plus an array of rows. Rows themselves are simply references to arrays of scalars. =cut package OpenSIPS::VDB::Result; use OpenSIPS; use OpenSIPS::VDB::Column; our @ISA = qw ( OpenSIPS::Utils::Debug ); =head2 new(coldefs,[row, row, ...]) The constructor creates a new Result object. Its first parameter is a reference to an array of OpenSIPS::VDB::Column objects. Additional parameters may be passed to provide initial rows, which are references to arrays of scalars. =cut sub new { my $class = shift; my $coldefs = shift; my $self = {}; bless $self, $class; $self->{coldefs} = $coldefs; while (@_) { push @{$self->{rows}}, shift; } return $self; } =head2 coldefs() Returns or sets the column definition of the object. =cut sub coldefs { my $self = shift; if (@_) { $self->{coldefs} = shift; } return $self->{coldefs}; } =head2 rows() Returns or sets the rows of the object. =cut sub rows { my $self = shift; while (@_) { push @{$self->{rows}}, shift; } return $self->{rows}; } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/VTab.pm000066400000000000000000000031341300170765700227130ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::VTab This package handles virtual tables and is used by the OpenSIPS::VDB class to store information about valid tables. The package is not inteded for end user access. =cut package OpenSIPS::VDB::VTab; use OpenSIPS; our @ISA = qw ( OpenSIPS::Utils::Debug ); =head2 new() Constructs a new VTab object =cut sub new { my $class = shift; return bless { @_ }, $class; } =head2 call(op,[args]) Invokes an operation on the table (insert, update, ...) with the given arguments. =cut sub call { my $self = shift; my $operation = shift; my @args = @_; if( my $obj = $self->{obj} ) { return $obj->$operation(@args); } else { no strict; return &{$self->{func}}($operation, @args); } } 1; opensips-2.2.2/modules/perl/lib/perl/OpenSIPS/VDB/Value.pm000066400000000000000000000037521300170765700231410ustar00rootroot00000000000000# # $Id$ # # Perl module for OpenSIPS # # Copyright (C) 2006 Collax GmbH # (Bastian Friedrich ) # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # =head1 OpenSIPS::VDB::Value This package represents a database value. Additional to the data itself, information about its type is stored. =head2 stringification When accessing a OpenSIPS::VDB::Value object as a string, it simply returns its data regardless of its type. =cut use strict; package OpenSIPS::VDB::Value; use overload '""' => \&stringify; sub stringify { shift->{data} } use OpenSIPS; use OpenSIPS::Constants; our @ISA = qw ( OpenSIPS::Utils::Debug ); =head2 new(type,data) Constructs a new Value object. Its data type and the data are passed as parameters. =cut sub new { my $class = shift; my $type = shift; my $data = shift; my $self = { type => $type, data => $data, }; bless $self, $class; return $self; } =head2 type() Returns or sets the current data type. Please consider using the constants from OpenSIPS::Constants =cut sub type { my $self = shift; if (@_) { $self->{type} = shift; } return $self->{type}; } =head2 data() Returns or sets the current data. =cut sub data { my $self = shift; if (@_) { $self->{data} = shift; } return $self->{data}; } 1; opensips-2.2.2/modules/perl/opensipsxs.xs000066400000000000000000000776471300170765700206110ustar00rootroot00000000000000/* * Perl module for OpenSIPS * * Copyright (C) 2006 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #undef load_module /* perl.h defines union semun */ #ifdef USE_SYSV_SEM # undef _SEM_SEMUN_UNDEFINED #endif #include "../../sr_module.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../usr_avp.h" #include "../../action.h" #include "../../flags.h" #include "../../pvar.h" #include "../../mem/mem.h" #include "../../route_struct.h" #include "../../serialize.h" #include "../../qvalue.h" #include "../../dprint.h" extern int unsafemodfnc; enum xs_uri_members { XS_URI_USER = 0, XS_URI_PASSWD, XS_URI_HOST, XS_URI_PORT, XS_URI_PARAMS, XS_URI_HEADERS, XS_URI_TRANSPORT, XS_URI_TTL, XS_URI_USER_PARAM, XS_URI_MADDR, XS_URI_METHOD, XS_URI_LR, XS_URI_R2, XS_URI_TRANSPORT_VAL, XS_URI_TTL_VAL, XS_URI_USER_PARAM_VAL, XS_URI_MADDR_VAL, XS_URI_METHOD_VAL, XS_URI_LR_VAL, XS_URI_R2_VAL /* These members are no strings: unsigned short port_no; unsigned short proto; / * from transport * / uri_type type; / * uri scheme */ }; /* * Return the sip_msg struct referred to by perl reference sv */ struct sip_msg * sv2msg(SV *sv) { struct sip_msg* m; if (SvROK(sv)) { sv = SvRV(sv); if (SvIOK(sv)) { m = INT2PTR(struct sip_msg*, SvIV(sv)); return m; } } return NULL; /* In case of error above... */ } struct sip_uri * sv2uri(SV *sv) { struct sip_uri* u; if (SvROK(sv)) { sv = SvRV(sv); if (SvIOK(sv)) { u = INT2PTR(struct sip_uri*, SvIV(sv)); return u; } } return NULL; /* In case of error above... */ } struct action * sv2action(SV *sv) { struct action* a; if (SvROK(sv)) { sv = SvRV(sv); if (SvIOK(sv)) { a = INT2PTR(struct action*, SvIV(sv)); return a; } } return NULL; /* In case of error above... */ } /* * We have a private function for two reasons: * a) Return SIP_INVALID even if type was sth different * b) easy access */ inline static int getType(struct sip_msg *msg) { int t = SIP_INVALID; if (!msg) return SIP_INVALID; switch ((msg->first_line).type) { case SIP_REQUEST: t = SIP_REQUEST; break; case SIP_REPLY: t = SIP_REPLY; break; } return t; } SV *getStringFromURI(SV *self, enum xs_uri_members what) { struct sip_uri *myuri = sv2uri(self); str *ret = NULL; if (!myuri) { LM_ERR("Invalid URI reference\n"); ret = NULL; } else { switch (what) { case XS_URI_USER: ret = &(myuri->user); break; case XS_URI_HOST: ret = &(myuri->host); break; case XS_URI_PASSWD: ret = &(myuri->passwd); break; case XS_URI_PORT: ret = &(myuri->port); break; case XS_URI_PARAMS: ret = &(myuri->params); break; case XS_URI_HEADERS: ret = &(myuri->headers); break; case XS_URI_TRANSPORT: ret = &(myuri->transport); break; case XS_URI_TTL: ret = &(myuri->ttl); break; case XS_URI_USER_PARAM: ret = &(myuri->user_param); break; case XS_URI_MADDR: ret = &(myuri->maddr); break; case XS_URI_METHOD: ret = &(myuri->method); break; case XS_URI_LR: ret = &(myuri->lr); break; case XS_URI_R2: ret = &(myuri->r2); break; case XS_URI_TRANSPORT_VAL: ret = &(myuri->transport_val); break; case XS_URI_TTL_VAL: ret = &(myuri->ttl_val); break; case XS_URI_USER_PARAM_VAL: ret = &(myuri->user_param_val); break; case XS_URI_MADDR_VAL: ret = &(myuri->maddr_val); break; case XS_URI_METHOD_VAL: ret = &(myuri->method_val); break; case XS_URI_LR_VAL: ret = &(myuri->lr_val); break; case XS_URI_R2_VAL: ret = &(myuri->r2_val); break; default: LM_INFO("Unknown URI element" " requested: %d\n", what); break; } } if ((ret) && (ret->len)) { return sv_2mortal(newSVpv(ret->s, ret->len)); } else { return &PL_sv_undef; } } /* * Calls an exported function. Parameters are copied and fixup'd. * * Return codes: * -1 - Function not available (or other error). * 1 - Function was called. Its return value is returned via the retval * parameter. */ int moduleFunc(struct sip_msg *m, char *func, char *param1, char *param2, int *retval) { cmd_export_t *exp_func_struct; struct action *act; char *argv[2]; int argc = 0; action_elem_t elems[MAX_ACTION_ELEMS]; if (!func) { LM_ERR("moduleFunc called with null function name. Error."); return -1; } if ((!param1) && param2) { LM_ERR("moduleFunc called with parameter 1 UNSET and" " parameter 2 SET. Error."); return -1; } if (param1) { argv[0] = (char *)pkg_malloc(strlen(param1)+1); strcpy(argv[0], param1); argc++; } else { argv[0] = NULL; } if (param2) { argv[1] = (char *)pkg_malloc(strlen(param2)+1); strcpy(argv[1], param2); argc++; } else { argv[1] = NULL; } exp_func_struct = find_cmd_export_t(func, argc, 0); if (!exp_func_struct) { LM_ERR("function '%s' called, but not available.", func); *retval = -1; if (argv[0]) pkg_free(argv[0]); if (argv[1]) pkg_free(argv[1]); return -1; } elems[0].type = CMD_ST; elems[0].u.data = exp_func_struct; elems[1].type = STRING_ST; elems[1].u.data = argv[0]; elems[2].type = STRING_ST; elems[2].u.data = argv[1]; act = mk_action( MODULE_T, 3, elems, 0, "perl"); if (!act) { LM_ERR("action structure could not be created. Error."); if (argv[0]) pkg_free(argv[0]); if (argv[1]) pkg_free(argv[1]); return -1; } if (exp_func_struct->fixup) { if (!unsafemodfnc) { LM_ERR("Module function '%s' is unsafe. Call is refused.\n", func); if (argv[0]) pkg_free(argv[0]); if (argv[1]) pkg_free(argv[1]); *retval = -1; return -1; } if (argc>=2) { *retval = exp_func_struct->fixup(&(act->elem[2].u.data), 2); if (*retval < 0) { LM_ERR("Error in fixup (2)\n"); return -1; } act->elem[2].type = MODFIXUP_ST; } if (argc>=1) { *retval = exp_func_struct->fixup(&(act->elem[1].u.data), 1); if (*retval < 0) { LM_ERR("Error in fixup (1)\n"); return -1; } act->elem[1].type = MODFIXUP_ST; } if (argc==0) { *retval = exp_func_struct->fixup(0, 0); if (*retval < 0) { LM_ERR("Error in fixup (0)\n"); return -1; } } } *retval = do_action(act, m); if ((act->elem[2].type == MODFIXUP_ST) && (act->elem[2].u.data)) { /* pkg_free(act->elem[2].u.data); */ LM_WARN("moduleFunction: A fixup function was called. " "This currently creates a memory leak.\n"); } if ((act->elem[1].type == MODFIXUP_ST) && (act->elem[1].u.data)) { /* pkg_free(act->elem[1].u.data); */ LM_WARN("moduleFunction: A fixup function was called. " "This currently creates a memory leak.\n"); } if (argv[0]) pkg_free(argv[0]); if (argv[1]) pkg_free(argv[1]); pkg_free(act); return 1; } /** * Rewrite Request-URI */ static inline int rewrite_ruri(struct sip_msg* _m, char* _s) { struct action act; act.type = SET_URI_T; act.elem[0].type = STR_ST; act.elem[0].u.s.s = _s; act.elem[0].u.s.len = strlen(_s); act.next = 0; if (do_action(&act, _m) < 0) { LM_ERR("rewrite_ruri: Error in do_action\n"); return -1; } return 0; } /** * Compile a string with pseudo variables substituted by their values. * A string buffer is allocated. Deallocate afterwards! */ char *pv_sprintf(struct sip_msg *m, char *fmt) { int buf_size = 4096; pv_elem_t *model; str s; char *out = (char *)pkg_malloc(buf_size); char *ret = NULL; if (!out) { LM_ERR("pv_sprintf: Memory exhausted!\n"); return NULL; } s.s = fmt; s.len = strlen(s.s); if(pv_parse_format(&s, &model) < 0) { LM_ERR("pv_sprintf: ERROR: wrong format[%s]!\n", fmt); return NULL; } if(pv_printf(m, model, out, &buf_size) < 0) { ret = NULL; } else { ret = strdup(out); } pv_elem_free_all(model); pkg_free(out); return ret; } /** * Convert an SV to an int_str struct. Needed in AVP package. * - val: SV to convert. * - is: pointer to resulting int_str * - flags: pointer to flags to set * - strflag: flag mask to be or-applied for string match */ static inline int sv2int_str(SV *val, int_str *is, unsigned short *flags, unsigned short strflag) { char *s; STRLEN len; if (!SvOK(val)) { LM_ERR("AVP:sv2int_str: Invalid value " "(not a scalar).\n"); return 0; } if (SvIOK(val)) { /* numerical name */ is->n = SvIV(val); *flags = 0; return 1; } else if (SvPOK(val)) { s = SvPV(val, len); is->s.len = len; is->s.s = s; (*flags) |= strflag; return 1; } else { LM_ERR("AVP:sv2int_str: Invalid value " "(neither string nor integer).\n"); return 0; } } /* ************************************************************************ */ /* Object methods begin here */ =head1 OpenSIPS This module provides access to a limited number of OpenSIPS core functions. As the most interesting functions deal with SIP messages, they are located in the OpenSIPS::Message class below. =cut MODULE = OpenSIPS PACKAGE = OpenSIPS =head2 log(level,message) Logs the message with OpenSIPS's logging facility. The logging level is one of the following: * L_ALERT * L_CRIT * L_ERR * L_WARN * L_NOTICE * L_INFO * L_DBG Please note that this method is I automatically exported, as it collides with the perl function log (which calculates the logarithm). Either explicitly import the function (via C), or call it with its full name: OpenSIPS::log(L_INFO, "foobar"); =cut void log(level, log) int level char *log PREINIT: INIT: CODE: switch (level) { case L_ALERT: LM_ALERT("%s", log); break; case L_CRIT: LM_CRIT("%s", log); break; case L_ERR: LM_ERR("%s", log); break; case L_WARN: LM_WARN("%s", log); break; case L_NOTICE: LM_NOTICE("%s", log); break; case L_INFO: LM_INFO("%s", log); break; default: LM_DBG("%s", log); break; } OUTPUT: MODULE = OpenSIPS PACKAGE = OpenSIPS::Message PROTOTYPES: ENABLE =head1 OpenSIPS::Message This package provides access functions for an OpenSIPS C structure and its sub-components. Through its means it is possible to fully configure alternative routing decisions. =cut =head2 getType() Returns one of the constants SIP_REQUEST, SIP_REPLY, SIP_INVALID stating the type of the current message. =cut int getType(self) SV *self PREINIT: struct sip_msg *msg = sv2msg(self); INIT: CODE: RETVAL = getType(msg); OUTPUT: RETVAL =head2 getStatus() Returns the status code of the current Reply message. This function is invalid in Request context! =cut SV * getStatus(self) SV *self PREINIT: struct sip_msg *msg = sv2msg(self); str *ret; INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = &PL_sv_undef; } else { if (getType(msg) != SIP_REPLY) { LM_ERR("getStatus: Status not available in" " non-reply messages."); ST(0) = &PL_sv_undef; } else { ret = &((msg->first_line).u.reply.status); ST(0) = sv_2mortal(newSVpv(ret->s, ret->len)); } } =head2 getReason() Returns the reason of the current Reply message. This function is invalid in Request context! =cut SV * getReason(self) SV *self PREINIT: struct sip_msg *msg = sv2msg(self); str *ret; INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = &PL_sv_undef; } else { if (getType(msg) != SIP_REPLY) { LM_ERR("getReason: Reason not available in" " non-reply messages."); ST(0) = &PL_sv_undef; } else { ret = &((msg->first_line).u.reply.reason); ST(0) = sv_2mortal(newSVpv(ret->s, ret->len)); } } =head2 getVersion() Returns the version string of the current SIP message. =cut SV * getVersion(self) SV *self PREINIT: struct sip_msg *msg = sv2msg(self); str *ret; INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = &PL_sv_undef; } else { if (getType(msg) == SIP_REQUEST) { ret = &((msg->first_line).u.request.version); } else { /* SIP_REPLY */ ret = &((msg->first_line).u.reply.version); } ST(0) = sv_2mortal(newSVpv(ret->s, ret->len)); } =head2 getRURI() This function returns the recipient URI of the present SIP message: C<< my $ruri = $m->getRURI(); >> getRURI returns a string. See L below how to receive a parsed structure. This function is valid in request messages only. =cut SV * getRURI(self) SV *self PREINIT: struct sip_msg *msg = sv2msg(self); str *ret; INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = &PL_sv_undef; } else { if (getType(msg) != SIP_REQUEST) { LM_ERR("Not a request message - " "no RURI available.\n"); ST(0) = &PL_sv_undef; } else { ret = &((msg->first_line).u.request.uri); ST(0) = sv_2mortal(newSVpv(ret->s, ret->len)); } } =head2 getMethod() Returns the current method, such as C, C, C and so on. C<< my $method = $m->getMethod(); >> This function is valid in request messages only. =cut char * getMethod(self) SV *self PREINIT: struct sip_msg *msg = sv2msg(self); str *ret; INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = &PL_sv_undef; } else { if (getType(msg) != SIP_REQUEST) { LM_ERR("Not a request message - " "no method available.\n"); ST(0) = &PL_sv_undef; } else { ret = &((msg->first_line).u.request.method); ST(0) = sv_2mortal(newSVpv(ret->s, ret->len)); } } =head2 getFullHeader() Returns the full message header as present in the current message. You might use this header to further work with it with your favorite MIME package. C<< my $hdr = $m->getFullHeader(); >> =cut SV * getFullHeader(self) SV *self PREINIT: struct sip_msg *msg = sv2msg(self); char *firsttoken; long headerlen; INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = &PL_sv_undef; } else { if (getType(msg) == SIP_INVALID) { LM_ERR("getFullHeader: Invalid message type.\n"); ST(0) = &PL_sv_undef; } else { parse_headers(msg, ~0, 0); if (getType(msg) == SIP_REQUEST) { firsttoken = (msg->first_line).u.request.method.s; } else { /* SIP_REPLY */ firsttoken = (msg->first_line).u.reply.version.s; } if (msg->eoh == NULL) headerlen = 0; else headerlen = ((long)(msg->eoh)) -((long)(firsttoken)); if (headerlen > 0) { ST(0) = sv_2mortal(newSVpv(firsttoken, headerlen)); } else { ST(0) = &PL_sv_undef; } } } =head2 getBody() Returns the message body. =cut SV * getBody(self) SV *self PREINIT: struct sip_msg *msg = sv2msg(self); str body; INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = &PL_sv_undef; } else { body.s = NULL; get_body(msg,&body); ST(0) = sv_2mortal(newSVpv(body.s, 0)); } =head2 getMessage() Returns the whole message including headers and body. =cut SV * getMessage(self) SV *self PREINIT: struct sip_msg *msg = sv2msg(self); INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = &PL_sv_undef; } else { ST(0) = sv_2mortal(newSVpv(msg->buf, 0)); } =head2 getHeader(name) Returns the body of the first message header with this name. C<< print $m->getHeader("To"); >> B >>> =cut SV * getHeader(self, name) SV *self; char *name; PREINIT: struct sip_msg *msg = sv2msg(self); str *body = NULL; struct hdr_field *hf; int found = 0; int namelen = strlen(name); INIT: PPCODE: LM_DBG("searching '%s'\n", name); if (!msg) { LM_ERR("Invalid message reference\n"); } else { parse_headers(msg, ~0, 0); for (hf = msg->headers; hf; hf = hf->next) { if (namelen == hf->name.len) { if (strncmp(name, hf->name.s, namelen) == 0) { /* Found the right header. */ found = 1; body = &(hf->body); XPUSHs(sv_2mortal(newSVpv(body->s, body->len))); } } } } if (!found) { XPUSHs(&PL_sv_undef); } =head2 getHeaderNames() Returns an array of all header names. Duplicates possible! =cut AV * getHeaderNames(self) SV *self; PREINIT: struct sip_msg *msg = sv2msg(self); struct hdr_field *hf = NULL; int found = 0; PPCODE: if (!msg) { LM_ERR("Invalid message reference\n"); } else { parse_headers(msg, ~0, 0); for (hf = msg->headers; hf; hf = hf->next) { found = 1; XPUSHs(sv_2mortal(newSVpv(hf->name.s, hf->name.len))); } } if (!found) { XPUSHs(&PL_sv_undef); } =head2 moduleFunction(func,string1,string2) Search for an arbitrary function in module exports and call it with the parameters self, string1, string2. C and/or C may be omitted. As this function provides access to the functions that are exported to the OpenSIPS configuration file, it is autoloaded for unknown functions. Instead of writing $m->moduleFunction("sl_send_reply", "500", "Internal Error"); $m->moduleFunction("xlog", "L_INFO", "foo"); you may as well write $m->sl_send_reply("500", "Internal Error"); $m->xlog("L_INFO", "foo"); WARNING In OpenSIPS 1.2, only a limited subset of module functions is available. This restriction will be removed in a later version. Here is a list of functions that are expected to be working (not claiming completeness): * alias_db_lookup * consume_credentials * is_rpid_user_e164 * append_rpid_hf * bind_auth * avp_print * cpl_process_register * cpl_process_register_norpl * load_dlg * ds_next_dst * ds_next_domain * ds_mark_dst * ds_mark_dst * is_from_local * is_uri_host_local * dp_can_connect * dp_apply_policy * enum_query (without parameters) * enum_fquery (without parameters) * is_from_user_enum (without parameters) * i_enum_query (without parameters) * imc_manager * jab_* (all functions from the jabber module) * sdp_mangle_ip * sdp_mangle_port * encode_contact * decode_contact * decode_contact_header * fix_contact * use_media_proxy * end_media_session * m_store * m_dump * fix_nated_contact * unforce_rtp_proxy * force_rtp_proxy * fix_nated_register * add_rcv_param * options_reply * checkospheader * validateospheader * requestosprouting * checkosproute * prepareosproute * prepareallosproutes * checkcallingtranslation * reportospusage * mangle_pidf * mangle_message_cpim * add_path (without parameters) * add_path_received (without parameters) * prefix2domain * allow_routing (without parameters) * allow_trusted * pike_check_req * handle_publish * handle_subscribe * stored_pres_info * bind_pua * send_publish * send_subscribe * pua_set_publish * loose_route * record_route * load_rr * sip_trace * sl_reply_error * sms_send_msg * sd_lookup * sstCheckMin * append_time * has_body (without parameters) * is_peer_verified * t_newtran * t_release * t_relay (without parameters) * t_flush_flags * t_check_trans * t_was_cancelled * uac_restore_from * uac_auth * has_totag * tel2sip * check_to * check_from * radius_does_uri_exist * ul_* (All functions exported by the usrloc module for user access) * xmpp_send_message =cut int moduleFunction (self, func, string1 = NULL, string2 = NULL) SV *self; char *func; char *string1; char *string2; PREINIT: struct sip_msg *msg = sv2msg(self); int retval; /* Return value of called function */ int ret; /* Return value of moduleFunc - < 0 for "non existing function" and other errors */ INIT: CODE: LM_DBG("Calling exported func '%s', Param1 is '%s'," " Param2 is '%s'\n", func, string1, string2); ret = moduleFunc(msg, func, string1, string2, &retval); if (ret < 0) { LM_ERR("calling module function '%s' failed." " Missing loadmodule?\n", func); retval = -1; } RETVAL = retval; OUTPUT: RETVAL =head2 log(level,message) (deprecated type) Logs the message with OpenSIPS's logging facility. The logging level is one of the following: * L_ALERT * L_CRIT * L_ERR * L_WARN * L_NOTICE * L_INFO * L_DBG The logging function should be accessed via the OpenSIPS module variant. This one, located in OpenSIPS::Message, is deprecated. =cut void log(self, level, log) SV *self int level char *log PREINIT: INIT: CODE: switch (level) { case L_ALERT: LM_ALERT("%s", log); break; case L_CRIT: LM_CRIT("%s", log); break; case L_ERR: LM_ERR("%s", log); break; case L_WARN: LM_WARN("%s", log); break; case L_NOTICE: LM_NOTICE("%s", log); break; case L_INFO: LM_INFO("%s", log); break; default: LM_DBG("%s", log); break; } =head2 rewrite_ruri(newruri) Sets a new destination (recipient) URI. Useful for rerouting the current message/call. if ($m->getRURI() =~ m/\@somedomain.net/) { $m->rewrite_ruri("sip:dispatcher\@organization.net"); } =cut int rewrite_ruri(self, newruri) SV *self; char *newruri; PREINIT: struct sip_msg *msg = sv2msg(self); INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); RETVAL = -1; } else { if (getType(msg) != SIP_REQUEST) { LM_ERR("Not a Request. RURI rewrite unavailable.\n"); RETVAL = -1; } else { LM_DBG("New R-URI is [%s]\n", newruri); RETVAL = rewrite_ruri(msg, newruri); } } OUTPUT: RETVAL =head2 setFlag(flag) Sets a message flag. The constants as known from the C API may be used, when Constants.pm is included. =cut int setFlag(self, flag) SV *self; unsigned int flag; PREINIT: struct sip_msg *msg = sv2msg(self); INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); RETVAL = -1; } else { RETVAL = setflag(msg, flag); } OUTPUT: RETVAL =head2 resetFlag(flag) Resets a message flag. =cut int resetFlag(self, flag) SV *self; unsigned int flag; PREINIT: struct sip_msg *msg = sv2msg(self); INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); RETVAL = -1; } else { RETVAL = resetflag(msg, flag); } OUTPUT: RETVAL =head2 isFlagSet(flag) Returns whether a message flag is set or not. =cut int isFlagSet(self, flag) SV *self; unsigned int flag; PREINIT: struct sip_msg *msg = sv2msg(self); INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); RETVAL = -1; } else { RETVAL = isflagset(msg, flag) == 1 ? 1 : 0; } OUTPUT: RETVAL =head2 pseudoVar(string) Returns a new string where all pseudo variables are substituted by their values. Can be used to receive the values of single variables, too. B =cut SV * pseudoVar(self, varstring) SV *self; char *varstring; PREINIT: struct sip_msg *msg = sv2msg(self); char *ret; CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = &PL_sv_undef; } else { ret = pv_sprintf(msg, varstring); if (ret) { ST(0) = sv_2mortal(newSVpv(ret, strlen(ret))); free(ret); } else { ST(0) = &PL_sv_undef; } } =head2 append_branch(branch,qval) Append a branch to current message. =cut int append_branch(self, branch = NULL, qval = NULL) SV *self; char *branch; char *qval; PREINIT: struct sip_msg *msg = sv2msg(self); action_elem_t elems[MAX_ACTION_ELEMS]; qvalue_t q; int err = 0; struct action *act = NULL; INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); RETVAL = -1; } else { if (qval) { if (str2q(&q, qval, strlen(qval)) < 0) { LM_ERR("append_branch: Bad q value."); } else { /* branch and qval set */ elems[0].type = STR_ST; elems[0].u.data = branch; elems[1].type = NUMBER_ST; elems[1].u.data = (void *)(long)q; act = mk_action(APPEND_BRANCH_T, 2, elems, 0, "perl"); } } else { if (branch) { /* branch set, qval unset */ elems[0].type = STR_ST; elems[0].u.data = branch; elems[1].type = NUMBER_ST; elems[1].u.data = (void *)Q_UNSPECIFIED; act = mk_action(APPEND_BRANCH_T, 2, elems, 0, "perl"); } else { /* neither branch nor qval set */ elems[0].type = STR_ST; elems[0].u.data = NULL; elems[1].type = NUMBER_ST; elems[1].u.data = (void *)Q_UNSPECIFIED; act = mk_action(APPEND_BRANCH_T, 2, elems, 0, "perl"); } } if (act) { RETVAL = do_action(act, msg); } else { RETVAL = -1; } } OUTPUT: RETVAL =head2 serialize_branches(clean_before) Serialize branches. =cut int serialize_branches(self, clean_before) SV *self; int clean_before; PREINIT: struct sip_msg *msg = sv2msg(self); CODE: if (!msg) { LM_ERR("Invalid message reference\n"); RETVAL = -1; } else { RETVAL = serialize_branches(msg, clean_before); } OUTPUT: RETVAL =head2 next_branches() Next branches. =cut int next_branches(self) SV *self; PREINIT: struct sip_msg *msg = sv2msg(self); CODE: if (!msg) { LM_ERR("Invalid message reference\n"); RETVAL = -1; } else { RETVAL = next_branches(msg); } OUTPUT: RETVAL =head2 getParsedRURI() Returns the current destination URI as an OpenSIPS::URI object. =cut SV * getParsedRURI(self) SV *self; PREINIT: struct sip_msg *msg = sv2msg(self); struct sip_uri *uri; SV *ret; INIT: CODE: if (!msg) { LM_ERR("Invalid message reference\n"); ST(0) = NULL; } else { parse_sip_msg_uri(msg); parse_headers(msg, ~0, 0); uri = &(msg->parsed_uri); ret = sv_newmortal(); sv_setref_pv(ret, "OpenSIPS::URI", (void *)uri); SvREADONLY_on(SvRV(ret)); ST(0) = ret; } MODULE = OpenSIPS PACKAGE = OpenSIPS::URI =head1 OpenSIPS::URI This package provides functions for access to sip_uri structures. =cut =head2 user() Returns the user part of this URI. =cut SV * user(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_USER); =head2 host() Returns the host part of this URI. =cut SV * host(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_HOST); =head2 passwd() Returns the passwd part of this URI. =cut SV * passwd(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_PASSWD); =head2 port() Returns the port part of this URI. =cut SV * port(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_PORT); =head2 params() Returns the params part of this URI. =cut SV * params(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_PARAMS); =head2 headers() Returns the headers part of this URI. =cut SV * headers(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_HEADERS); =head2 transport() Returns the transport part of this URI. =cut SV * transport(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_TRANSPORT); =head2 ttl() Returns the ttl part of this URI. =cut SV * ttl(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_TTL); =head2 user_param() Returns the user_param part of this URI. =cut SV * user_param(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_USER_PARAM); =head2 maddr() Returns the maddr part of this URI. =cut SV * maddr(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_MADDR); =head2 method() Returns the method part of this URI. =cut SV * method(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_METHOD); =head2 lr() Returns the lr part of this URI. =cut SV * lr(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_LR); =head2 r2() Returns the r2 part of this URI. =cut SV * r2(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_R2); =head2 transport_val() Returns the transport_val part of this URI. =cut SV * transport_val(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_TRANSPORT_VAL); =head2 ttl_val() Returns the ttl_val part of this URI. =cut SV * ttl_val(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_TTL_VAL); =head2 user_param_val() Returns the user_param_val part of this URI. =cut SV * user_param_val(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_USER_PARAM_VAL); =head2 maddr_val() Returns the maddr_val part of this URI. =cut SV * maddr_val(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_MADDR_VAL); =head2 method_val() Returns the method_val part of this URI. =cut SV * method_val(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_METHOD_VAL); =head2 lr_val() Returns the lr_val part of this URI. =cut SV * lr_val(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_LR_VAL); =head2 r2_val() Returns the r2_val part of this URI. =cut SV * r2_val(self) SV *self; CODE: ST(0) = getStringFromURI(self, XS_URI_R2_VAL); =head1 OpenSIPS::AVP This package provides access functions for OpenSIPS's AVPs. These variables can be created, evaluated, modified and removed through this package. Please note that these functions do NOT support the notation used in the configuration file, but directly work on strings or numbers. See documentation of add method below. =cut MODULE = OpenSIPS PACKAGE = OpenSIPS::AVP =head2 add(name,val) Add an AVP. Add an OpenSIPS AVP to its environment. name and val may both be integers or strings; this function will try to guess what is correct. Please note that OpenSIPS::AVP::add("10", "10") is something different than OpenSIPS::AVP::add(10, 10) due to this evaluation: The first will create _string_ AVPs with the name 10, while the latter will create a numerical AVP. You can modify/overwrite AVPs with this function. =cut int add(p_name, p_val) SV *p_name; SV *p_val; PREINIT: int_str name; int_str val; unsigned short flags = 0; char *s; STRLEN len; CODE: RETVAL = 0; if (SvOK(p_name) && SvOK(p_val)) { if (!sv2int_str(p_name, &name, &flags, AVP_NAME_STR)) { RETVAL = -1; } else if (!sv2int_str(p_val, &val, &flags, AVP_VAL_STR)) { RETVAL = -1; } if (RETVAL == 0) { if (flags & AVP_NAME_STR) { name.n = get_avp_id(&name.s); } RETVAL = add_avp(flags, name.n, val); } } OUTPUT: RETVAL =head2 get(name) get an OpenSIPS AVP: my $numavp = OpenSIPS::AVP::get(5); my $stravp = OpenSIPS::AVP::get("foo"); =cut int get(p_name) SV *p_name; PREINIT: struct usr_avp *first_avp; int_str name; int_str val; unsigned short flags = 0; SV *ret = &PL_sv_undef; int err = 0; char *s; STRLEN len; CODE: if (SvOK(p_name)) { if (!sv2int_str(p_name, &name, &flags, AVP_NAME_STR)) { LM_ERR("AVP:get: Invalid name."); err = 1; } } else { LM_ERR("AVP:get: Invalid name."); err = 1; } if (err == 0) { if (flags & AVP_NAME_STR) { name.n = get_avp_id(&name.s); } first_avp = search_first_avp(flags, name.n, &val, NULL); if (first_avp != NULL) { /* found correct AVP */ if (is_avp_str_val(first_avp)) { ret = sv_2mortal(newSVpv(val.s.s, val.s.len)); } else { ret = sv_2mortal(newSViv(val.n)); } } else { /* Empty AVP requested. */ } } ST(0) = ret; =head2 destroy(name) Destroy an AVP. OpenSIPS::AVP::destroy(5); OpenSIPS::AVP::destroy("foo"); =cut int destroy(p_name) SV *p_name; PREINIT: struct usr_avp *first_avp; int_str name; int_str val; unsigned short flags = 0; SV *ret = &PL_sv_undef; char *s; STRLEN len; CODE: RETVAL = 1; if (SvOK(p_name)) { if (!sv2int_str(p_name, &name, &flags, AVP_NAME_STR)) { RETVAL = 0; LM_ERR("AVP:destroy: Invalid name."); } } else { RETVAL = 0; LM_ERR("VP:destroy: Invalid name."); } if (RETVAL == 1) { if (flags & AVP_NAME_STR) { name.n = get_avp_id(&name.s); } first_avp = search_first_avp(flags, name.n, &val, NULL); if (first_avp != NULL) { /* found correct AVP */ destroy_avp(first_avp); } else { RETVAL = 0; /* Empty AVP requested. */ } } OUTPUT: RETVAL opensips-2.2.2/modules/perl/perl.c000066400000000000000000000173361300170765700171150ustar00rootroot00000000000000/* * Perl module for OpenSIPS * * Copyright (C) 2006 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #define DEFAULTMODULE "OpenSIPS" #include #include #include #include #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../mi/mi.h" #include "../rr/api.h" #include "../sl/sl_api.h" /* lock_ops.h defines union semun, perl does not need to redefine it */ #ifdef USE_SYSV_SEM # define HAS_UNION_SEMUN #endif #include "perlfunc.h" #include "perl.h" /* #include "perlxsi.h" function is in here... */ /* Full path to the script including executed functions */ char *filename = NULL; /* Path to an arbitrary directory where the OpenSIPS Perl modules are * installed */ char *modpath = NULL; /* Allow unsafe module functions - functions with fixups. This will create * memory leaks, the variable thus is not documented! */ int unsafemodfnc = 0; /* Reference to the running Perl interpreter instance */ PerlInterpreter *my_perl = NULL; /** SIGNALING binds */ struct sig_binds sigb; /* * Module destroy function prototype */ static void destroy(void); /* * Module child-init function prototype */ static int child_init(int rank); /* * Module initialization function prototype */ static int mod_init(void); /* * Reload perl interpreter - reload perl script. Forward declaration. */ struct mi_root* perl_mi_reload(struct mi_root *cmd_tree, void *param); /* * Exported functions */ static cmd_export_t cmds[] = { { "perl_exec_simple", (cmd_function)perl_exec_simple1, 1, perl_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, { "perl_exec_simple", (cmd_function)perl_exec_simple2, 2, perl_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, { "perl_exec", (cmd_function)perl_exec1, 1, perl_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, { "perl_exec", (cmd_function)perl_exec2, 2, perl_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, { 0, 0, 0, 0, 0, 0 } }; /* * Exported parameters */ static param_export_t params[] = { {"filename", STR_PARAM, &filename}, {"modpath", STR_PARAM, &modpath}, {"unsafemodfnc", INT_PARAM, &unsafemodfnc}, { 0, 0, 0 } }; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { /* FIXME This does not yet work... { "perl_reload", perl_mi_reload, MI_NO_INPUT_FLAG, 0, 0 },*/ { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* * Module info */ #ifndef RTLD_NOW /* for openbsd */ #define RTLD_NOW DL_LAZY #endif #ifndef RTLD_GLOBAL /* Unsupported! */ #define RTLD_GLOBAL 0 #endif /* * Module interface */ struct module_exports exports = { "perl", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, RTLD_NOW | RTLD_GLOBAL, &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; static int child_init(int rank) { return 0; } EXTERN_C void boot_DynaLoader (pTHX_ CV* cv); EXTERN_C void boot_OpenSIPS(pTHX_ CV* cv); /* * This is output by perl -MExtUtils::Embed -e xsinit * and complemented by the OpenSIPS bootstrapping */ EXTERN_C void xs_init(pTHX) { char *file = __FILE__; dXSUB_SYS; newXS("OpenSIPS::bootstrap", boot_OpenSIPS, file); newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); } /* * Initialize the perl interpreter. * This might later be used to reinit the module. */ PerlInterpreter *parser_init(void) { int argc = 0; char *argv[9]; PerlInterpreter *new_perl = NULL; int modpathset = 0; new_perl = perl_alloc(); if (!new_perl) { LM_ERR("could not allocate perl.\n"); return NULL; } perl_construct(new_perl); argv[0] = ""; argc++; /* First param _needs_ to be empty */ /* Possible Include path extension by modparam */ if (modpath && (strlen(modpath) > 0)) { modpathset = argc; LM_INFO("setting lib path: '%s'\n", modpath); argv[argc] = pkg_malloc(strlen(modpath)+20); sprintf(argv[argc], "-I%s", modpath); argc++; } argv[argc] = "-M"DEFAULTMODULE; argc++; /* Always "use" Opensips.pm */ argv[argc] = filename; /* The script itself */ argc++; if (perl_parse(new_perl, xs_init, argc, argv, NULL)) { LM_ERR("failed to load perl file \"%s\".\n", argv[argc-1]); if (modpathset) pkg_free(argv[modpathset]); return NULL; } else { LM_INFO("successfully loaded perl file \"%s\"\n", argv[argc-1]); } if (modpathset) pkg_free(argv[modpathset]); perl_run(new_perl); return new_perl; } /* * */ int unload_perl(PerlInterpreter *p) { perl_destruct(p); perl_free(p); return 0; } /* * reload function. * Reinitializes the interpreter. Works, but execution for _all_ * children is difficult. */ int perl_reload(struct sip_msg *m, char *a, char *b) { PerlInterpreter *new_perl; new_perl = parser_init(); if (new_perl) { unload_perl(my_perl); my_perl = new_perl; #ifdef PERL_EXIT_DESTRUCT_END PL_exit_flags |= PERL_EXIT_DESTRUCT_END; #else #warning Perl 5.8.x should be used. Please upgrade. #warning This binary will be unsupported. PL_exit_flags |= PERL_EXIT_EXPECTED; #endif return 1; } else { return 0; } } /* * Reinit through fifo. * Currently does not seem to work :(( */ struct mi_root* perl_mi_reload(struct mi_root *cmd_tree, void *param) { if (perl_reload(NULL, NULL, NULL)) { return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } else { return init_mi_tree( 500, "Perl reload failed", 18); } } /* * mod_init * Called by opensips at init time */ static int mod_init(void) { int ret = 0; static int argc = 1; static char *argv_name = "opensips"; static char **argv = { &argv_name }; LM_INFO("initializing...\n"); if (!filename) { LM_ERR("insufficient module parameters. Module not loaded.\n"); return -1; } /** * We will need reply() from signaling * module for sending replies */ /* load SIGNALING API */ if(load_sig_api(&sigb)< 0) { LM_ERR("can't load signaling functions\n"); return -1; } PERL_SYS_INIT3(&argc, &argv, &environ); if ((my_perl = parser_init())) { ret = 0; #ifdef PERL_EXIT_DESTRUCT_END PL_exit_flags |= PERL_EXIT_DESTRUCT_END; #else PL_exit_flags |= PERL_EXIT_EXPECTED; #endif } else { ret = -1; } return ret; } /* * destroy * called by opensips at exit time */ static void destroy(void) { if(my_perl==NULL) return; unload_perl(my_perl); PERL_SYS_TERM(); my_perl = NULL; } opensips-2.2.2/modules/perl/perl.h000066400000000000000000000025411300170765700171120ustar00rootroot00000000000000/* * Perl module for OpenSIPS * * Copyright (C) 2006 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PERL_MOD_H #define PERL_MOD_H #include "../signaling/signaling.h" /* lock_ops.h defines union semun, perl does not need to redefine it */ #ifdef USE_SYSV_SEM # define HAS_UNION_SEMUN #endif #include #include extern char *filename; extern char *modpath; extern PerlInterpreter *my_perl; extern struct sig_binds sigb; #define PERLCLASS_MESSAGE "OpenSIPS::Message" #define PERLCLASS_URI "OpenSIPS::URI" #endif /* PERL_MOD_H */ opensips-2.2.2/modules/perl/perlfunc.c000066400000000000000000000127321300170765700177640ustar00rootroot00000000000000/* * Perl module for OpenSIPS * * Copyright (C) 2006 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include "../../mem/mem.h" #include "../../data_lump.h" #include "../../parser/parse_param.h" #include "../../parser/msg_parser.h" #include "../../dprint.h" #include "../../action.h" #include "../../config.h" #include "../../parser/parse_uri.h" #include "../../mod_fix.h" #include "perlfunc.h" #include "perl.h" int perl_fixup(void** param, int param_no) { int ret=E_UNSPEC; pv_elem_t* model; if (param == NULL || (param_no < 1 || param_no > 2)) { LM_ERR("invalid number of parameters\n"); return -1; } if (param_no == 1) { /* simple fixup */ return fixup_spve(param); } else { /* can have more vars for each param */ str s = {*param, strlen(*param)}; ret = pv_parse_format(&s, &model); if (ret) { LM_ERR("wrong format [%s] for param no %d!\n", (char*)*param, param_no); pkg_free(s.s); return E_UNSPEC; } *param = (void *)model; ret = 0; } return ret; } /* * Check for existence of a function. */ int perl_checkfnc(char *fnc) { if (get_cv(fnc, 0)) { return 1; } else { return 0; } } /* * parse function and parameters * returns p(arsed)fnc name and p(arsed)prm parameters * requiers output strs to be allocated */ int perl_parse_params(struct sip_msg *msg, char *fnc, char *prm, str *pfnc, str *pprm) { if (!pfnc && !pprm) { LM_ERR("null output parameters given!\n"); return -1; } if (msg == 0 || fnc == 0) { LM_ERR("null input parameters given!\n"); return -1; } if (fixup_get_svalue(msg, (gparam_p)fnc, pfnc) != 0) { LM_ERR("invalid function name given\n"); return -1; } if (prm && pprm && pv_printf_s(msg, (pv_elem_p)prm, pprm)!=0) { LM_ERR("invalid function paramters given!\n"); return -1; } return 0; } /* * Run function without parameters */ int perl_exec_simple(char* fnc, char* args[], int flags) { if (perl_checkfnc(fnc)) { LM_DBG("running perl function \"%s\"", fnc); call_argv(fnc, flags, args); } else { LM_ERR("unknown function '%s' called.\n", fnc); return -1; } return 1; } int perl_exec_simple1(struct sip_msg* _msg, char* fnc, char* str2) { char *args[] = { NULL }; str pfnc; if (perl_parse_params(_msg, fnc, NULL, &pfnc, NULL)) { LM_ERR("failed to parse params\n"); return -1; } return perl_exec_simple(pfnc.s, args, G_DISCARD | G_NOARGS | G_EVAL); } int perl_exec_simple2(struct sip_msg* _msg, char* fnc, char* param) { str pfnc, pparam; if (perl_parse_params(_msg, fnc, param, &pfnc, &pparam)) { LM_ERR("failed to parse params\n"); return -1; } char *args[] = { pparam.s, NULL }; return perl_exec_simple(pfnc.s, args, G_DISCARD | G_EVAL); } /* * Run function, with current SIP message as a parameter */ int perl_exec1(struct sip_msg* _msg, char* fnc, char *foobar) { return perl_exec2(_msg, fnc, NULL); } int perl_exec2(struct sip_msg* _msg, char* fnc, char* mystr) { int retval; SV *m; str reason; str pfnc, pparam; if (perl_parse_params(_msg, fnc, mystr, &pfnc, mystr?&pparam:NULL)) { LM_ERR("failed to parse params\n"); return -1; } fnc = pfnc.s; mystr = mystr ? pparam.s : NULL; dSP; if (!perl_checkfnc(fnc)) { LM_ERR("unknown perl function called.\n"); reason.s = "Internal error"; reason.len = sizeof("Internal error")-1; if (sigb.reply(_msg, 500, &reason, NULL) == -1) { LM_ERR("failed to send reply\n"); } return -1; } switch ((_msg->first_line).type) { case SIP_REQUEST: if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("failed to parse Request-URI\n"); reason.s = "Bad Request-URI"; reason.len = sizeof("Bad Request-URI")-1; if (sigb.reply(_msg, 400, &reason, NULL) == -1) { LM_ERR("failed to send reply\n"); } return -1; } break; case SIP_REPLY: break; default: LM_ERR("invalid firstline"); return -1; } ENTER; /* everything created after here */ SAVETMPS; /* ...is a temporary variable. */ PUSHMARK(SP); /* remember the stack pointer */ m = sv_newmortal(); /* create a mortal SV to be killed on FREETMPS */ sv_setref_pv(m, "OpenSIPS::Message", (void *)_msg); /* bless the message with a class */ SvREADONLY_on(SvRV(m)); /* set the content of m to be readonly */ XPUSHs(m); /* Our reference to the stack... */ if (mystr) XPUSHs(sv_2mortal(newSVpv(mystr, strlen(mystr)))); /* Our string to the stack... */ PUTBACK; /* make local stack pointer global */ call_pv(fnc, G_EVAL|G_SCALAR); /* call the function */ SPAGAIN; /* refresh stack pointer */ /* pop the return value from stack */ retval = POPi; PUTBACK; FREETMPS; /* free that return value */ LEAVE; /* ...and the XPUSHed "mortal" args.*/ return retval; } opensips-2.2.2/modules/perl/perlfunc.h000066400000000000000000000030331300170765700177630ustar00rootroot00000000000000/* * Perl module for OpenSIPS * * Copyright (C) 2006 Collax GmbH * (Bastian Friedrich ) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PERL_FUNC_H #define PERL_FUNC_H #include "../../parser/msg_parser.h" /* fixup for perl_exec and perl_exec_simple */ int perl_fixup(void** param, int param_no); /* * Run a perl function without a sip message parameter. */ int perl_exec_simple1(struct sip_msg* _msg, char* fnc, char* str2); int perl_exec_simple2(struct sip_msg* _msg, char* fnc, char* str2); /* * Run function with a reference to the current SIP message. * An optional string may be passed to perl_exec_string. */ int perl_exec1(struct sip_msg* _msg, char* fnc, char *foobar); int perl_exec2(struct sip_msg* _msg, char* fnc, char* mystr); #endif /* PERL_FUNC_H */ opensips-2.2.2/modules/perl/typemap000066400000000000000000000000001300170765700173650ustar00rootroot00000000000000opensips-2.2.2/modules/permissions/000077500000000000000000000000001300170765700174065ustar00rootroot00000000000000opensips-2.2.2/modules/permissions/Makefile000066400000000000000000000003301300170765700210420ustar00rootroot00000000000000# $Id$ # # PERMISSIONS module makefile # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=permissions.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/permissions/README000066400000000000000000000602221300170765700202700ustar00rootroot00000000000000permissions Module Miklos Tirpak Edited by Miklos Tirpak Edited by Bogdan-Andrei Iancu Edited by Juha Heinanen Edited by Irina-Maria Stanescu Copyright © 2003 Miklos Tirpak Copyright © 2006-2008 Juha Heinanen Copyright © 2009 Irina-Maria Stanescu Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Call Routing 1.1.2. Registration Permissions 1.1.3. URI Permissions 1.1.4. Address Permissions 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. default_allow_file (string) 1.3.2. default_deny_file (string) 1.3.3. check_all_branches (integer) 1.3.4. allow_suffix (string) 1.3.5. deny_suffix (string) 1.3.6. db_url (string) 1.3.7. address_table (string) 1.3.8. partition (string) 1.3.9. grp_col (string) 1.3.10. ip_col (string) 1.3.11. mask_col (string) 1.3.12. port_col (string) 1.3.13. proto_col (string) 1.3.14. pattern_col (string) 1.3.15. info_col (string) 1.4. Exported Functions 1.4.1. allow_routing() 1.4.2. allow_routing(basename) 1.4.3. allow_routing(allow_file,deny_file) 1.4.4. allow_register(basename) 1.4.5. allow_register(allow_file, deny_file) 1.4.6. allow_uri(basename, pvar) 1.4.7. check_address([partition:]group_id, ip, port, proto [, context_info [, pattern]]) 1.4.8. check_source_address(group_id [, context_info [, pattern]]) 1.4.9. get_source_group(pvar) 1.5. Exported MI Functions 1.5.1. address_reload 1.5.2. address_dump 1.5.3. subnet_dump 1.5.4. allow_uri List of Examples 1.1. Set default_allow_file parameter 1.2. Set default_deny_file parameter 1.3. Set check_all_branches parameter 1.4. Set allow_suffix parameter 1.5. Set deny_suffix parameter 1.6. Set db_url parameter 1.7. Set address_table parameter 1.8. Set partition parameter 1.9. Set grp_col parameter 1.10. Set ip_col parameter 1.11. Set mask_col parameter 1.12. Set port_col parameter 1.13. Set proto_col parameter 1.14. Set pattern_col parameter 1.15. Set info_col parameter 1.16. allow_routing usage 1.17. allow_routing(basename) usage 1.18. allow_routing(allow_file, deny_file) usage 1.19. allow_register(basename) usage 1.20. allow_register(allow_file, deny_file) usage 1.21. allow_uri(basename, pvar) usage 1.22. check_address() usage 1.23. check_source_address() usage 1.24. get_source_group() usage Chapter 1. Admin Guide 1.1. Overview 1.1.1. Call Routing The module can be used to determine if a call has appropriate permission to be established. Permission rules are stored in plaintext configuration files similar to hosts.allow and hosts.deny files used by tcpd. When allow_routing function is called it tries to find a rule that matches selected fields of the message. OpenSIPS is a forking proxy and therefore a single message can be sent to different destinations simultaneously. When checking permissions all the destinations must be checked and if one of them fails, the forwarding will fail. The matching algorithm is as follows, first match wins: * Create a set of pairs of form (From, R-URI of branch 1), (From, R-URI of branch 2), etc. * Routing will be allowed when all pairs match an entry in the allow file. * Otherwise routing will be denied when one of pairs matches an entry in the deny file. * Otherwise, routing will be allowed. A non-existing permission control file is treated as if it were an empty file. Thus, permission control can be turned off by providing no permission control files. From header field and Request-URIs are always compared with regular expressions! For the syntax see the sample file: config/permissions.allow. 1.1.2. Registration Permissions In addition to call routing it is also possible to check REGISTER messages and decide--based on the configuration files--whether the message should be allowed and the registration accepted or not. Main purpose of the function is to prevent registration of "prohibited" IP addresses. One example, when a malicious user registers a contact containing IP address of a PSTN gateway, he might be able to bypass authorization checks performed by the SIP proxy. That is undesirable and therefore attempts to register IP address of a PSTN gateway should be rejected. Files config/register.allow and config/register.deny contain an example configuration. Function for registration checking is called allow_register and the algorithm is very similar to the algorithm described in Section 1.1.1, “Call Routingâ€. The only difference is in the way how pairs are created. Instead of From header field the function uses To header field because To header field in REGISTER messages contains the URI of the person being registered. Instead of the Request-URI of branches the function uses Contact header field. Thus, pairs used in matching will look like this: (To, Contact 1), (To, Contact 2), (To, Contact 3), and so on.. The algorithm of matching is same as described in Section 1.1.1, “Call Routingâ€. 1.1.3. URI Permissions The module can be used to determine if request is allowed to the destination specified by an URI stored in a pvar. Permission rules are stored in plaintext configuration files similar to hosts.allow and hosts.deny used by tcpd. When allow_uri function is called, it tries to find a rule that matches selected fields of the message. The matching algorithm is as follows, first match wins: * Create a pair . * Request will be allowed when the pair matches an entry in the allow file. * Otherwise request will be denied when the pair matches an entry in the deny file. * Otherwise, request will be allowed. A non-existing permission control file is treated as if it were an empty file. Thus, permission control can be turned off by providing no permission control files. From URI and URI stored in pvar are always compared with regular expressions! For the syntax see the sample file: config/permissions.allow. 1.1.4. Address Permissions The module can be used to determine if an address (IP address and port) matches any of the IP subnets stored in cached OpenSIPS database table. Port 0 in cached database table matches any port. Group ID, IP address, port and transport protocol values to be matched can be either taken from the request (check_source_address) or given as pvar arguments or directly as strings(check_address). Addresses stored in cached database table can be grouped together into one or more groups specified by a group identifier (unsigned integer). Group identifier is given as argument to check_address and check_source_address. Otherwise the request is rejected. The address database table is specified by module parameters. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. default_allow_file (string) Default allow file used by functions without parameters. If you don't specify full pathname then the directory in which is the main config file is located will be used. Default value is “permissions.allowâ€. Example 1.1. Set default_allow_file parameter ... modparam("permissions", "default_allow_file", "/etc/permissions.allow") ... 1.3.2. default_deny_file (string) Default file containing deny rules. The file is used by functions without parameters. If you don't specify full pathname then the directory in which the main config file is located will be used. Default value is “permissions.denyâ€. Example 1.2. Set default_deny_file parameter ... modparam("permissions", "default_deny_file", "/etc/permissions.deny") ... 1.3.3. check_all_branches (integer) If set then allow_routing functions will check Request-URI of all branches (default). If disabled then only Request-URI of the first branch will be checked. Warning Do not disable this parameter unless you really know what you are doing. Default value is 1. Example 1.3. Set check_all_branches parameter ... modparam("permissions", "check_all_branches", 0) ... 1.3.4. allow_suffix (string) Suffix to be appended to basename to create filename of the allow file when version with one parameter of either allow_routing or allow_register is used. Note Including leading dot. Default value is “.allowâ€. Example 1.4. Set allow_suffix parameter ... modparam("permissions", "allow_suffix", ".allow") ... 1.3.5. deny_suffix (string) Suffix to be appended to basename to create filename of the deny file when version with one parameter of either allow_routing or allow_register is used. Note Including leading dot. Default value is “.denyâ€. Example 1.5. Set deny_suffix parameter ... modparam("permissions", "deny_suffix", ".deny") ... 1.3.6. db_url (string) This is URL of the database to be used. Since version 2.2, this url represents the db_url for the “default†partition. Default value is “NULLâ€. Example 1.6. Set db_url parameter ... modparam("permissions", "db_url", "dbdriver://username:password@dbhost/d bname") ... 1.3.7. address_table (string) Name of database table containing matching rules used by allow_register function. Since version 2.2, this table name represents the table name for the “default†partition. Default value is “addressâ€. Example 1.7. Set address_table parameter ... modparam("permissions", "address_table", "pbx") ... 1.3.8. partition (string) Create a partition containg an url and a table name. Partitions allow you to use different databases or different tables from the same database. It is IMPORTANT that you split the paramteres by ";". Also, it is very IMPORTANT to put ";" after the last parameter, elseway it will not be taken into consideration. The db_url for a partition is mandatory. The table name, instead, has a default value 'address'. Example 1.8. Set partition parameter ... modparam("permissions", "partition", "my_part: db_url=“some_urlâ€; table_ name=“some_tableâ€;") ... 1.3.9. grp_col (string) Name of address table column containing group identifier of the address. Default value is “grpâ€. Example 1.9. Set grp_col parameter ... modparam("permissions", "grp_col", "group_id") ... 1.3.10. ip_col (string) Name of address table column containing IP address part of the address. Default value is “ipâ€. Example 1.10. Set ip_col parameter ... modparam("permissions", "ip_col", "ipess") ... 1.3.11. mask_col (string) Name of address table column containing network mask of the address. Possible values are 0-32. Default value is “maskâ€. Example 1.11. Set mask_col parameter ... modparam("permissions", "mask_col", "subnet_length") ... 1.3.12. port_col (string) Name of address table column containing port part of the address. Default value is “portâ€. Example 1.12. Set port_col parameter ... modparam("permissions", "port_col", "prt") ... 1.3.13. proto_col (string) Name of address table column containing transport protocol that is matched against transport protocol of received request. Possible values that can be stored in proto_col are “anyâ€, “udpâ€, “tcpâ€, “tlsâ€, “sctpâ€, and “noneâ€. Value “any†matches always and value “none†never. Default value is “protoâ€. Example 1.13. Set proto_col parameter ... modparam("permissions", "proto_col", "transport") ... 1.3.14. pattern_col (string) Name of address table column containing regular expression that is matched against the arguments received by check_address or check_source_address. Default value is “patternâ€. Example 1.14. Set pattern_col parameter ... modparam("permissions", "pattern_col", "regexp") ... 1.3.15. info_col (string) Name of address table column containing a string that is added as value to a pvar given as argument to check_address or check_source_address in case the function succedes. Default value is “context_infoâ€. Example 1.15. Set info_col parameter ... modparam("permissions", "info_col", "info_col") ... 1.4. Exported Functions 1.4.1. allow_routing() Returns true if all pairs constructed as described in Section 1.1.1, “Call Routing†have appropriate permissions according to the configuration files. This function uses default configuration files specified in default_allow_file and default_deny_file. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.16. allow_routing usage ... if (allow_routing()) { t_relay(); }; ... 1.4.2. allow_routing(basename) Returns true if all pairs constructed as described in Section 1.1.1, “Call Routing†have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.17. allow_routing(basename) usage ... if (allow_routing("basename")) { t_relay(); }; ... 1.4.3. allow_routing(allow_file,deny_file) Returns true if all pairs constructed as described in Section 1.1.1, “Call Routing†have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. * deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.18. allow_routing(allow_file, deny_file) usage ... if (allow_routing("rules.allow", "rules.deny")) { t_relay(); }; ... 1.4.4. allow_register(basename) The function returns true if all pairs constructed as described in Section 1.1.2, “Registration Permissions†have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.19. allow_register(basename) usage ... if (method=="REGISTER") { if (allow_register("register")) { save("location"); exit; } else { sl_send_reply("403", "Forbidden"); }; }; ... 1.4.5. allow_register(allow_file, deny_file) The function returns true if all pairs constructed as described in Section 1.1.2, “Registration Permissions†have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. * deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.20. allow_register(allow_file, deny_file) usage ... if (method=="REGISTER") { if (allow_register("register.allow", "register.deny")) { save("location"); exit; } else { sl_send_reply("403", "Forbidden"); }; }; ... 1.4.6. allow_uri(basename, pvar) Returns true if the pair constructed as described in Section 1.1.3, “URI Permissions†have appropriate permissions according to the configuration files specified by the parameter. Meaning of the parameter is as follows: * basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. * pvar - Any pseudo-variable defined in OpenSIPS. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.21. allow_uri(basename, pvar) usage ... if (allow_uri("basename", "$rt")) { // Check Refer-To URI t_relay(); }; if (allow_uri("basename", "$avp(uri)") { // Check URI stored in $avp(ur i) t_relay(); }; ... 1.4.7. check_address([partition:]group_id, ip, port, proto [, context_info [, pattern]]) Returns 1 if group id, IP address, port and protocol given as arguments match an IP subnet found in cached address table, as described in Section 1.1.4, “Address Permissions†. The function takes 4 mandatory arguments and 2 optional ones. This function can be useful to check if a request can be allowed without authentication. Meaning of the parameter is as follows: * group_id This argument represents the group id to be matched. It can be an integer string or a string pvar. If the group_id argument is "0", the query can match any group in the cached address table. Since version 2.2, you can also specify the partition(either string or pv). If no partition specified, the “default†one will be used. * ip This argument represents the ip address to be matched. It can be an given directly as string or as a string pvar. This argument cannot be null/empty. * port This argument represents the port to be matched. It can be an given as an integer string or as a string pvar. Cached address table entry containing port value 0 matches any port. Also, a "0" value for the argument can match any port in the address table. * proto This argument represents the protocol used for transport; it can be an given as string or as string pvar. Transport protocol is either "ANY" or any valid transport protocol value: "UDP, "TCP", "TLS", and "SCTP". * context_info This argument represents the pvar in wich the context_info field from the cached address table will be stored in case of match. This argument is optional. * pattern This argument is a string to be matched with the regular expression pattern field from the cached address table. This argument is optional. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.22. check_address() usage ... // Checks if the tuple IP address/port (given as strings) and source pro tocol // (given as pvar), belongs to group 4, verifies if pattern string "text test" // matches the regular expression field in the database table and stores the // context information in $avp(ctx) if (check_address("4","192.168.2.135","5700","$proto","$avp(ctx)", "text test")) { t_relay(); xlog("$avp(ctx)\n"); } if (check_address("my_part:4","192.168.2.135","5700","$proto","$avp(ctx) ", "texttest")) { t_relay(); xlog("$avp(ctx)\n"); } ... // Checks if the tuple IP address/port/protocol of the source message is in group 4 if (check_address("4","$si","$sp","$proto")) { t_relay(); } ... // Checks if the tuple IP address/port/protocol stored in AVPs s:ip/s:po rt/s:proto // is in group 4 and stores context information in $avp(ctx) $avp(ip) = "192.168.2.135"; $avp(port) = 5061; $avp(proto) = "any"; $avp(partition)="my_part"; if (check_address("$avp(partition):4","$avp(ip)","$avp(port)","$avp(prot o)","$avp(ctx))) { t_relay(); xlog("$avp(ctx)\n"); } ... // Checks if the tuple IP address/port (given as strings) and source pro tocol // (given as pvar) is in group 4, verifies if pattern string "texttest" matches // the regular expression field in the database table, without storing a ny // context information if (check_address("4","$si","5700","$proto","", "texttest")) { t_relay(); } ... 1.4.8. check_source_address(group_id [, context_info [, pattern]]) Equivalent to check_address(group_id, "$si", "$sp", "$proto", context_info, pattern). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.23. check_source_address() usage ... // Check if source address/port/proto is in group 4 and stores // context information in $avp(ctx) if (check_source_address("$avp(partition):4","$avp(ctx)")) { xlog("$avp(ctx)\n"); }else { sl_send_reply("403", "Forbidden"); } ... 1.4.9. get_source_group(pvar) Checks if an entry with the source ip/port/protocol is found in cached address or subnet table in any group. If yes, returns that group in the pvar parameter. If not returns -1. Port value 0 in cached address and subnet table matches any port. Since version 2.2, you can also specify the partition(either string or pv). If no partition specified, the “default†one will be used. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.24. get_source_group() usage ... if ( get_source_group("$var(group)") ) { # do something with $var(group) xlog("group is $var(group)\n"); }; ... 1.5. Exported MI Functions 1.5.1. address_reload Causes permissions module to re-read the contents of the address database table into cache memory. In cache memory the entries are for performance reasons stored in two different tables: address table and subnet table depending on the value of the mask field (32 or smaller). Parameters: * partition - the name of the partition to be reloaded. If none specified all the partitions shall be reloaded. 1.5.2. address_dump Causes permissions module to dump contents of the address table from cache memory. Parameters: * partition - the name of the partition to be dumped. If none specified all the partitions shall be dumped. 1.5.3. subnet_dump Causes permissions module to dump contents of cache memory subnet table. Parameters: * partition - the name of the partition to be dumped. If none specified all the partitions shall be dumped. 1.5.4. allow_uri Tests if (URI, Contact) pair is allowed according to allow/deny files. The files must already have been loaded by OpenSIPS. Parameters: * basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. * URI - URI to be tested * Contact - Contact to be tested opensips-2.2.2/modules/permissions/address.c000066400000000000000000000470361300170765700212110ustar00rootroot00000000000000/* * check_address related functions * * Copyright (C) 2003 Juha Heinanen * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-06-07 updated to the new DB api, moved reload_address_table (andrei) * 2009-09-10 major refactoring (irina) */ #include #include #include #include #include "../../config.h" #include "../../db/db.h" #include "../../ip_addr.h" #include "../../socket_info.h" #include "../../mem/shm_mem.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../mod_fix.h" #include "../../resolve.h" #include "permissions.h" #include "hash.h" #include "address.h" #include "partitions.h" #define TABLE_VERSION 5 str def_part = str_init("default"); static inline int proto_char2int(str *proto) { int ret_proto; if (proto->len==0 || (proto->len==3 && !strcasecmp(proto->s, "any"))) return PROTO_NONE; if (parse_proto((unsigned char*)proto->s, proto->len, &ret_proto) < 0) return -1; return ret_proto; } /* * Reload address table to new hash table and when done, make new hash table * current one. */ int reload_address_table(struct pm_part_struct *part_struct) { db_key_t cols[8]; db_res_t* res = NULL; db_row_t* row; db_val_t* val; struct address_list **new_hash_table; struct subnet *new_subnet_table; int i, mask, proto, group, port, id; struct ip_addr *ip_addr; struct net *subnet; str str_pattern = {NULL,0}, str_info={NULL,0}; str str_src_ip, str_proto; UNUSED(id); cols[0] = &ip_col; cols[1] = &grp_col; cols[2] = &mask_col; cols[3] = &port_col; cols[4] = &proto_col; cols[5] = &pattern_col; cols[6] = &info_col; cols[7] = &id_col; if (part_struct->perm_dbf.use_table(part_struct->db_handle, &part_struct->table) < 0) { LM_ERR("failed to use address table\n"); return -1; } if (part_struct->perm_dbf.query(part_struct->db_handle, NULL, 0, NULL, cols, 0, 8, 0, &res) < 0) { LM_ERR("failed to query database\n"); return -1; } /* Choose new hash table and free its old contents */ if (*part_struct->hash_table == part_struct->hash_table_1) { empty_hash(part_struct->hash_table_2); new_hash_table = part_struct->hash_table_2; } else { empty_hash(part_struct->hash_table_1); new_hash_table = part_struct->hash_table_1; } /* Choose new subnet table */ if (*part_struct->subnet_table == part_struct->subnet_table_1) { empty_subnet_table(part_struct->subnet_table_2); new_subnet_table = part_struct->subnet_table_2; } else { empty_subnet_table(part_struct->subnet_table_1); new_subnet_table = part_struct->subnet_table_1; } row = RES_ROWS(res); LM_DBG("number of rows in address table: %d\n", RES_ROW_N(res)); if (RES_COL_N(res) != 8) { LM_ERR("too many columns\n"); goto error; } for (i = 0; i < RES_ROW_N(res); i++) { val = ROW_VALUES(row + i); if ((VAL_TYPE(val)!=DB_STRING && VAL_TYPE(val)!=DB_STR) || VAL_NULL(val)) { LM_ERR("invalid IP column type on row %d, skipping..\n", i); continue; } if (VAL_TYPE(val + 1) != DB_INT || VAL_NULL(val + 1) || VAL_INT(val + 1) < 0) { LM_ERR("invalid group column type on row %d, skipping..\n", i); continue; } if (VAL_TYPE(val + 2) != DB_INT || VAL_NULL(val + 2) || VAL_INT(val + 2) < 0 || VAL_INT(val + 2) > 32) { LM_ERR("invalid mask column type on row %d, skipping..\n", i); continue; } if (VAL_TYPE(val + 3) != DB_INT || VAL_NULL(val + 3)) { LM_ERR("invalid port column type on row %d, skipping..\n", i); continue; } if ((VAL_TYPE(val + 4) != DB_STRING && VAL_TYPE(val + 4) != DB_STR) || VAL_NULL(val + 4)) { LM_ERR("invalid protocol column type on row %d, skipping..\n", i); continue; } if (VAL_TYPE(val + 5) != DB_STRING && VAL_TYPE(val + 5) != DB_STR) { LM_ERR("invalid pattern column type on row %d, skipping..\n", i); continue; } if (VAL_TYPE(val + 6) != DB_STRING && VAL_TYPE(val + 6) != DB_STR) { LM_ERR("invalid info column type on row %d, skipping..\n", i); goto error; } id = (unsigned int) VAL_INT(val + 7); /* IP string */ if (VAL_TYPE(val)==DB_STRING) { str_src_ip.s = (char*)VAL_STRING(val); str_src_ip.len = strlen(str_src_ip.s); } else { str_src_ip = VAL_STR(val); } if (str_src_ip.len==0) { LM_DBG("empty ip field in address table, ignoring entry" " number %d\n", i); continue; } ip_addr = str2ip(&str_src_ip); if (!ip_addr) { LM_DBG("invalid ip field in address table, ignoring entry " "with id %d\n", id); continue; } /* proto string */ if (VAL_TYPE(val+4)==DB_STRING) { str_proto.s = (char*)VAL_STRING(val+4); str_proto.len = strlen(str_proto.s); } else { str_proto = VAL_STR(val+4); } if (str_proto.len==4 && !strncasecmp(str_proto.s, "none",4)) { LM_DBG("protocol field is \"none\" in address table, ignoring" " entry with id %d\n", id); continue; } proto = proto_char2int(&str_proto); if (proto == -1) { LM_DBG("unknown protocol field in address table, ignoring" " entry with id %d\n", id); continue; } /* pattern string */ if (!VAL_NULL(val + 5)) { if (VAL_TYPE(val+5)==DB_STRING) { str_pattern.s = (char*)VAL_STRING(val+5); str_pattern.len = strlen(str_pattern.s); } else { str_pattern = VAL_STR(val+5); } } else { str_pattern.len = 0; str_pattern.s = 0; } /* info string */ if (!VAL_NULL(val + 6)) { if (VAL_TYPE(val+6)==DB_STRING) { str_info.s = (char*)VAL_STRING(val+6); str_info.len = strlen(str_info.s); } else { str_info = VAL_STR(val+6); } } else { str_info.len = 0; str_info.s = 0; } group = (unsigned int) VAL_INT(val + 1); port = (unsigned int) VAL_INT(val + 3); mask = (unsigned int) VAL_INT(val + 2); if (mask == 32) { if (hash_insert(new_hash_table, ip_addr, group, port, proto, &str_pattern, &str_info) == -1) { LM_ERR("hash table insert error\n"); goto error; } LM_DBG("Tuple <%.*s, %u, %u, %u, %.*s, %.*s> inserted into " "address hash table\n", str_src_ip.len, str_src_ip.s, group, port, proto, str_pattern.len, str_pattern.s, str_info.len,str_info.s); } else { subnet = mk_net_bitlen(ip_addr, mask); if (subnet_table_insert(new_subnet_table, group, subnet, port, proto, &str_pattern, &str_info) == -1) { LM_ERR("subnet table problem\n"); if (subnet) { pkg_free(subnet); } goto error; } LM_DBG("Tuple <%.*s, %u, %u, %u> inserted into subnet table\n", str_src_ip.len, str_src_ip.s, group, mask, port); /* subnet in pkg; needs to be freed since was copied to shm */ if (subnet) { pkg_free(subnet); } } } part_struct->perm_dbf.free_result(part_struct->db_handle, res); *part_struct->hash_table = new_hash_table; *part_struct->subnet_table = new_subnet_table; LM_DBG("address table reloaded successfully.\n"); return 1; error: part_struct->perm_dbf.free_result(part_struct->db_handle, res); return -1; } /* * Initialize data structures */ int init_address(struct pm_partition *partition) { struct pm_part_struct *part_struct; /* Check if hash table needs to be loaded from address table */ if (!partition->url.s) { LM_INFO("db_url parameter of permissions module not set, " "disabling allow_address\n"); return 0; } part_struct = pkg_malloc(sizeof (struct pm_part_struct)); if (part_struct == NULL) { LM_ERR("no more pkg mem\n"); return -1; } memset(part_struct, 0, sizeof(struct pm_part_struct)); part_struct->name = partition->name; part_struct->url = partition->url; part_struct->table = partition->table; if (db_bind_mod(&partition->url, &part_struct->perm_dbf) < 0) { LM_ERR("load a database support module\n"); return -1; } if (!DB_CAPABILITY(part_struct->perm_dbf, DB_CAP_QUERY)) { LM_ERR("database module does not implement 'query' function\n"); return -1; } part_struct->hash_table_1 = part_struct->hash_table_2 = 0; part_struct->hash_table = 0; part_struct->db_handle = part_struct->perm_dbf.init(&partition->url); if (!part_struct->db_handle) { LM_ERR("unable to connect database\n"); return -1; } if (db_check_table_version(&part_struct->perm_dbf, part_struct->db_handle, &partition->table, TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); part_struct->perm_dbf.close(part_struct->db_handle); return -1; } part_struct->hash_table_1 = hash_create(); if (!part_struct->hash_table_1) return -1; part_struct->hash_table_2 = hash_create(); if (!part_struct->hash_table_2) goto error; part_struct->hash_table = (struct address_list ***)shm_malloc (sizeof(struct address_list **)); if (!part_struct->hash_table) goto error; *part_struct->hash_table = part_struct->hash_table_1; part_struct->subnet_table_1 = new_subnet_table(); if (!part_struct->subnet_table_1) goto error; part_struct->subnet_table_2 = new_subnet_table(); if (!part_struct->subnet_table_2) goto error; part_struct->subnet_table = (struct subnet **)shm_malloc(sizeof(struct subnet *)); if (!part_struct->subnet_table) goto error; *part_struct->subnet_table = part_struct->subnet_table_1; if (reload_address_table(part_struct) == -1) { LM_CRIT("reload of address table failed\n"); goto error; } part_struct->perm_dbf.close(part_struct->db_handle); part_struct->db_handle = 0; add_part_struct(part_struct); return 0; error: if (part_struct->hash_table_1) { hash_destroy(part_struct->hash_table_1); part_struct->hash_table_1 = 0; } if (part_struct->hash_table_2) { hash_destroy(part_struct->hash_table_2); part_struct->hash_table_2 = 0; } if (part_struct->hash_table) { shm_free(part_struct->hash_table); part_struct->hash_table = 0; } if (part_struct->subnet_table_1) { free_subnet_table(part_struct->subnet_table_1); part_struct->subnet_table_1 = 0; } if (part_struct->subnet_table_2) { free_subnet_table(part_struct->subnet_table_2); part_struct->subnet_table_2 = 0; } if (part_struct->subnet_table) { shm_free(part_struct->subnet_table); part_struct->subnet_table = 0; } part_struct->perm_dbf.close(part_struct->db_handle); part_struct->db_handle = 0; pkg_free(part_struct); return -1; } /* * Open database connection if necessary */ int mi_init_address(void) { struct pm_part_struct *it; for (it=get_part_structs(); it; it=it->next) { if (it->db_handle) continue; it->db_handle = it->perm_dbf.init(&it->url); if (!it->db_handle) { LM_ERR("unable to connect database\n"); return -1; } } return 0; } /* * Close connections and release memory */ void clean_address(struct pm_part_struct *part_struct) { if (part_struct->hash_table_1) hash_destroy(part_struct->hash_table_1); if (part_struct->hash_table_2) hash_destroy(part_struct->hash_table_2); if (part_struct->hash_table) shm_free(part_struct->hash_table); } /* * */ int check_addr_6(struct sip_msg* msg, char* grp_sgp, char* ip_sp, char* port_sp, char* proto_sp, char* info, char* pattern) { unsigned int port; int group, proto, hash_ret, subnet_ret, ret = 1; struct ip_addr *ip; str str_ip, str_proto, str_port, pattern_s, str_part_group; struct pm_part_struct *part_struct; struct part_var *pvar, *pvar_new; memset(&str_ip, 0, sizeof(str)); memset(&str_proto, 0, sizeof(str)); if (grp_sgp) { pvar = (struct part_var *) grp_sgp; if (pvar->type == TYPE_PV) { if (fixup_get_svalue(msg, pvar->u.gp, &str_part_group)) { LM_ERR("cannot get group value\n"); return -1; } pvar_new = pkg_malloc(sizeof(struct part_var)); if (pvar_new == NULL) { LM_ERR("no more pkg mem\n"); return -1; } if (check_addr_param1( &str_part_group, pvar_new)) { LM_ERR("failed to parse [%s]!", str_part_group.s); return -1; } group = pvar_new->u.parsed_part.v.ival; if (pvar_new->u.parsed_part.partition.s) part_struct = get_part_struct(&pvar_new->u.parsed_part.partition); else part_struct = get_part_struct(&def_part); pkg_free(pvar_new); } else { group = pvar->u.parsed_part.v.ival; if (pvar->u.parsed_part.partition.s) part_struct = get_part_struct(&pvar->u.parsed_part.partition); else part_struct = get_part_struct(&def_part); } if (group < 0) { LM_ERR("invalid group value\n"); return -1; } } else { group = 0; part_struct = get_part_struct(&def_part); } if (part_struct == NULL) { LM_ERR("no db_url defined or no (such) partition!\n"); return -1; } if (ip_sp) { if (fixup_get_svalue(msg, (gparam_p)ip_sp, &str_ip)) { LM_ERR("cannot get str_ip string\n"); return -1; } } else { LM_ERR("source ip not provided!\n"); return -1; } if (str_ip.len <= 0 || !str_ip.s) { LM_ERR("source ip is not set!\n"); return -1; } ip = str2ip(&str_ip); if (!ip) { LM_ERR("invalid ip set <%.*s>!\n", str_ip.len, str_ip.s); return -1; } if (proto_sp) { if (fixup_get_svalue(msg, (gparam_p) proto_sp, &str_proto)) { LM_ERR("cannot get str_proto string\n"); return -1; } } if (str_proto.len <= 0 || !str_proto.s) { str_proto.s = "any"; str_proto.len = strlen(str_proto.s); } if ((proto = proto_char2int(&str_proto)) < 0) { LM_ERR("unknown protocol %.*s\n", str_proto.len, str_proto.s); return -1; } if (port_sp) { if (fixup_get_svalue(msg, (gparam_p)port_sp, &str_port)) { LM_ERR("cannot get port value\n"); return -1; } if (str2int(&str_port, &port) < 0) { LM_ERR("invalid port value\n"); return -1; } } else port = 0; if (pattern) { if (fixup_get_svalue(msg, (gparam_p)pattern, &pattern_s) < 0) { LM_ERR("cannot get pattern value\n"); return -1; } pattern = pkg_malloc(pattern_s.len + 1); if (!pattern) { LM_ERR("no more pkg mem\n"); return -1; } memcpy(pattern, pattern_s.s, pattern_s.len); pattern[pattern_s.len] = 0; } LM_DBG("Looking for : <%d, %.*s, %.*s, %d, %s>\n", group, str_ip.len, str_ip.s, str_proto.len, str_proto.s, port, ZSW(pattern) ); hash_ret = hash_match(msg, *part_struct->hash_table, group, ip, port, proto, pattern, info); if (hash_ret < 0) { subnet_ret = match_subnet_table(msg, *part_struct->subnet_table, group, ip, port, proto, pattern, info); ret = (hash_ret > subnet_ret) ? hash_ret : subnet_ret; } if (pattern) pkg_free(pattern); return ret; } int check_addr_4(struct sip_msg *msg, char *grp, char *src_ip_sp, char *port_sp, char *proto_sp) { return check_addr_6(msg, grp, src_ip_sp, port_sp, proto_sp, NULL, NULL); } int check_addr_5(struct sip_msg *msg, char *grp, char *src_ip_sp, char *port_sp, char *proto_sp, char *info) { return check_addr_6(msg, grp, src_ip_sp, port_sp, proto_sp, info, NULL); } int check_src_addr_3(struct sip_msg *msg, char *grp, char *info, char* pattern) { int group, hash_ret, subnet_ret, ret = 1; struct in_addr in; str str_ip, str_part_group; struct ip_addr *ip; str pattern_s; struct pm_part_struct *part_struct; struct part_var *pvar, *pvar_new; if (grp) { pvar = (struct part_var *) grp; if (pvar->type == TYPE_PV) { if (fixup_get_svalue(msg, pvar->u.gp, &str_part_group)) { LM_ERR("cannot get group value\n"); return -1; } pvar_new = pkg_malloc(sizeof(struct part_var)); if (pvar_new == NULL) { LM_ERR("no more pkg mem\n"); return -1; } if (check_addr_param1( &str_part_group, pvar_new)) { LM_ERR("failed to parse [%s]!", str_part_group.s); return -1; } group = pvar_new->u.parsed_part.v.ival; if (pvar_new->u.parsed_part.partition.s) part_struct = get_part_struct(&pvar_new->u.parsed_part.partition); else part_struct = get_part_struct(&def_part); pkg_free(pvar_new); } else { group = pvar->u.parsed_part.v.ival; if (pvar->u.parsed_part.partition.s) part_struct = get_part_struct(&pvar->u.parsed_part.partition); else part_struct = get_part_struct(&def_part); } if (group < 0) { LM_ERR("invalid group value\n"); return -1; } } else { group = 0; part_struct = get_part_struct(&def_part); } if (part_struct == NULL) { LM_ERR("no db_url defined or no (such) partition!\n"); return -1; } in.s_addr = msg->rcv.src_ip.u.addr32[0]; str_ip.s = inet_ntoa(in); if (!str_ip.s) { LM_ERR("error at inet_ntoa\n"); return -1; } str_ip.len = strlen(str_ip.s); ip = str2ip(&str_ip); LM_DBG("Looking for : <%d, %.*s, %d, %d> in tables\n", group, str_ip.len, str_ip.s, msg->rcv.src_port, msg->rcv.proto); if (pattern) { if (fixup_get_svalue(msg, (gparam_p)pattern, &pattern_s) < 0) { LM_ERR("cannot get pattern value\n"); return -1; } pattern = pkg_malloc(pattern_s.len + 1); if (!pattern) { LM_ERR("no more pkg mem\n"); return -1; } memcpy(pattern, pattern_s.s, pattern_s.len); pattern[pattern_s.len] = 0; } hash_ret = hash_match(msg, *part_struct->hash_table, group, ip, msg->rcv.src_port, msg->rcv.proto, pattern, info); if (hash_ret < 0) { subnet_ret = match_subnet_table(msg, *part_struct->subnet_table, group, ip, msg->rcv.src_port, msg->rcv.proto, pattern, info); ret = (hash_ret > subnet_ret) ? hash_ret : subnet_ret; } if (pattern) pkg_free(pattern); return ret; } int check_src_addr_2(struct sip_msg* msg, char* grp, char* info) { return check_src_addr_3(msg, grp, info, NULL); } int check_src_addr_1(struct sip_msg* msg, char* grp) { return check_src_addr_3(msg, grp, NULL, NULL); } int get_source_group(struct sip_msg* msg, char *arg) { int group = -1; struct in_addr in; struct ip_addr *ip; str str_ip, partition; pv_value_t pvt; struct part_pvar *ppv; struct pm_part_struct *ps; ppv = (struct part_pvar *)arg; if (ppv->part) { if (fixup_get_svalue(msg, ppv->part, &partition)) { LM_ERR("cannot get partition value\n"); return -1; } str_trim_spaces_lr(partition); ps = get_part_struct(&partition); if (ps == NULL) { LM_ERR("no such partition (%.*s)\n", partition.len, partition.s); return -1; } } else { ps = get_part_struct(&def_part); if (ps == NULL) { LM_ERR("no default partition\n"); return -1; } } LM_DBG("Looking for <%x, %u> in address table\n", msg->rcv.src_ip.u.addr32[0], msg->rcv.src_port); in.s_addr = msg->rcv.src_ip.u.addr32[0]; str_ip.s = inet_ntoa(in); str_ip.len = str_ip.s ? strlen(str_ip.s) : 0; ip = str2ip(&str_ip); group = find_group_in_hash_table(*ps->hash_table, ip, msg->rcv.src_port); if (group == -1) { LM_DBG("Looking for <%x, %u> in subnet table\n", msg->rcv.src_ip.u.addr32[0], msg->rcv.src_port); group = find_group_in_subnet_table(*ps->subnet_table, ip, msg->rcv.src_port); if (group == -1) { LM_DBG("IP <%.*s:%u> not found in any group\n", str_ip.len, str_ip.s, msg->rcv.src_port); return -1; } } LM_DBG("Found <%d>\n", group); pvt.flags = PV_VAL_INT|PV_TYPE_INT; pvt.rs.s = NULL; pvt.rs.len = 0; pvt.ri = group; if (pv_set_value(msg, ppv->sp, (int)EQ_T, &pvt) < 0) { LM_ERR("setting of pvar failed\n"); return -1; } return 1; } opensips-2.2.2/modules/permissions/address.h000066400000000000000000000043041300170765700212050ustar00rootroot00000000000000/* * Header file for address.c implementing allow_address function * * Copyright (C) 2003-2008 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TRUSTED_H #define TRUSTED_H #include "../../parser/msg_parser.h" #include "partitions.h" /* * Initialize data structures */ int init_address(struct pm_partition*); /* * Open database connections if necessary */ int init_child_address(int rank); /* * Open database connections if necessary */ int mi_init_address(void); /* * Reload address table to new hash table and when done, make new hash table * current one. */ int reload_address_table(struct pm_part_struct*); /* * Close connections and release memory */ void clean_address(struct pm_part_struct*); int get_source_group(struct sip_msg *msg, char *arg); /* Checks based on avp's received as parameter */ int check_addr_4(struct sip_msg *msg, char *grp, char *src_ip, char *port, char *proto); int check_addr_5(struct sip_msg* msg, char *grp, char *src_ip, char *port, char *proto, char *info); int check_addr_6(struct sip_msg* msg, char *grp, char *src_ip, char *port, char *proto, char *info, char *pattern); /* Checks based on data from the message */ int check_src_addr_1(struct sip_msg *msg, char *grp); int check_src_addr_2(struct sip_msg *msg, char *grp, char *info); int check_src_addr_3(struct sip_msg *msg, char *grp, char *info, char *pattern); #endif /* TRUSTED_H */ opensips-2.2.2/modules/permissions/config/000077500000000000000000000000001300170765700206535ustar00rootroot00000000000000opensips-2.2.2/modules/permissions/config/permissions.allow000066400000000000000000000007751300170765700242770ustar00rootroot00000000000000# SIP Express Router permissions module config file # # Syntax: # from_list [EXCEPT from_list] : req_uri_list [EXCEPT req_uri_list] # # from_list and req_uri_list are comma separated expressions # Expressions are treated as case insensitive POSIX Extended Regular Expressions. # Keyword ALL matches any expression. # # Examples: # ALL : "^sip:361[0-9]*@abc\.com$" EXCEPT "^sip:361[0-9]*3@abc\.com$", "^sip:361[0-9]*4@abc\.com$" # # "^sip:3677[0-9]*@abc\.com$" : "^sip:361[0-9]*@abc\.com$" # # ALL : ALL opensips-2.2.2/modules/permissions/config/permissions.deny000066400000000000000000000007741300170765700241170ustar00rootroot00000000000000# SIP Express Router permissions module config file # # Syntax: # from_list [EXCEPT from_list] : req_uri_list [EXCEPT req_uri_list] # # from_list and req_uri_list are comma separated expressions # Expressions are treated as case insensitive POSIX Extended Regular Expressions. # Keyword ALL matches any expression. # # Examples: # ALL : "^sip:361[0-9]*@abc\.com$" EXCEPT "^sip:361[0-9]*3@abc\.com$", "^sip:361[0-9]*4@abc\.com$" # # "^sip:3677[0-9]*@abc\.com$" : "^sip:361[0-9]*@abc\.com$" # # All : ALL opensips-2.2.2/modules/permissions/config/register.allow000066400000000000000000000010151300170765700235340ustar00rootroot00000000000000# # $Id$ # # Files register.allow and register.deny could be used # by allow_register function to prevent registration of # certain IP addresses. This (allow) file is empty because # we only want to restrict IPs that can be registered and # therefore we use register.deny file only. # # If you have a privileged user that should be able to # register anything than you might want to uncomment the # following line. WARNING: Do not uncomment unless you # really know what are you doing ! # # "^sip:username@foo\.bar$" : ALL # opensips-2.2.2/modules/permissions/config/register.deny000066400000000000000000000020571300170765700233640ustar00rootroot00000000000000# # $Id$ # # Suppose that we have a PSTN gateway with IP address 1.2.3.4 # We should prevent REGISTER messages that contain that IP # address in Contact header field because that can cause serious # security hole (a malicious user might be able to register such # a contact and bypass security checks performed by the SIP proxy). # # The following line prevents registering Contacts with IP 1.2.3.4 ALL : "^sip:.*1\.2\.3\.4$" # This can be bypassed by the insertion of one or more '0' in the # IP address. To prevent this the following line could be used. # #ALL : "^sip:.*0*1\.0*2\.0*3\.0*4" # Don't forget to list also all hostnames that can be used to # reach the PSTN gateway. Please keep in mind that this don't # provide a perfect security, as everybody can register arbitrary # domains that point to any IP. To prevent this the usage of hostnames # and domains in the Contact header field should be avoided. # A better solution to this problem is the usage of the dst_blacklist # feature in the opensips core. All "critical" IPs should be added to # this list. opensips-2.2.2/modules/permissions/doc/000077500000000000000000000000001300170765700201535ustar00rootroot00000000000000opensips-2.2.2/modules/permissions/doc/permissions.xml000066400000000000000000000032311300170765700232470ustar00rootroot00000000000000 %docentities; ]> permissions Module &osipsname; Miklos Tirpak
mtirpak@sztaki.hu
Miklos Tirpak
mtirpak@sztaki.hu
Bogdan-Andrei Iancu
bogdan@opensips.org
Juha Heinanen
jh@tutpro.com
Irina-Maria Stanescu
ironmissy@gmail.com
2003 Miklos Tirpak 2006-2008 Juha Heinanen 2009 Irina-Maria Stanescu $Revision: 8740 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/permissions/doc/permissions_admin.xml000066400000000000000000000751771300170765700244410ustar00rootroot00000000000000 &adminguide;
Overview
Call Routing The module can be used to determine if a call has appropriate permission to be established. Permission rules are stored in plaintext configuration files similar to hosts.allow and hosts.deny files used by tcpd. When allow_routing function is called it tries to find a rule that matches selected fields of the message. &osips; is a forking proxy and therefore a single message can be sent to different destinations simultaneously. When checking permissions all the destinations must be checked and if one of them fails, the forwarding will fail. The matching algorithm is as follows, first match wins: Create a set of pairs of form (From, R-URI of branch 1), (From, R-URI of branch 2), etc. Routing will be allowed when all pairs match an entry in the allow file. Otherwise routing will be denied when one of pairs matches an entry in the deny file. Otherwise, routing will be allowed. A non-existing permission control file is treated as if it were an empty file. Thus, permission control can be turned off by providing no permission control files. From header field and Request-URIs are always compared with regular expressions! For the syntax see the sample file: config/permissions.allow.
Registration Permissions In addition to call routing it is also possible to check REGISTER messages and decide--based on the configuration files--whether the message should be allowed and the registration accepted or not. Main purpose of the function is to prevent registration of "prohibited" IP addresses. One example, when a malicious user registers a contact containing IP address of a PSTN gateway, he might be able to bypass authorization checks performed by the SIP proxy. That is undesirable and therefore attempts to register IP address of a PSTN gateway should be rejected. Files config/register.allow and config/register.deny contain an example configuration. Function for registration checking is called allow_register and the algorithm is very similar to the algorithm described in . The only difference is in the way how pairs are created. Instead of From header field the function uses To header field because To header field in REGISTER messages contains the URI of the person being registered. Instead of the Request-URI of branches the function uses Contact header field. Thus, pairs used in matching will look like this: (To, Contact 1), (To, Contact 2), (To, Contact 3), and so on.. The algorithm of matching is same as described in .
URI Permissions The module can be used to determine if request is allowed to the destination specified by an URI stored in a pvar. Permission rules are stored in plaintext configuration files similar to hosts.allow and hosts.deny used by tcpd. When allow_uri function is called, it tries to find a rule that matches selected fields of the message. The matching algorithm is as follows, first match wins: Create a pair <From URI, URI stored in pvar>. Request will be allowed when the pair matches an entry in the allow file. Otherwise request will be denied when the pair matches an entry in the deny file. Otherwise, request will be allowed. A non-existing permission control file is treated as if it were an empty file. Thus, permission control can be turned off by providing no permission control files. From URI and URI stored in pvar are always compared with regular expressions! For the syntax see the sample file: config/permissions.allow.
Address Permissions The module can be used to determine if an address (IP address and port) matches any of the IP subnets stored in cached &osips; database table. Port 0 in cached database table matches any port. Group ID, IP address, port and transport protocol values to be matched can be either taken from the request (check_source_address) or given as pvar arguments or directly as strings(check_address). Addresses stored in cached database table can be grouped together into one or more groups specified by a group identifier (unsigned integer). Group identifier is given as argument to check_address and check_source_address. Otherwise the request is rejected. The address database table is specified by module parameters.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>default_allow_file</varname> (string) Default allow file used by functions without parameters. If you don't specify full pathname then the directory in which is the main config file is located will be used. Default value is permissions.allow. Set <varname>default_allow_file</varname> parameter ... modparam("permissions", "default_allow_file", "/etc/permissions.allow") ...
<varname>default_deny_file</varname> (string) Default file containing deny rules. The file is used by functions without parameters. If you don't specify full pathname then the directory in which the main config file is located will be used. Default value is permissions.deny. Set <varname>default_deny_file</varname> parameter ... modparam("permissions", "default_deny_file", "/etc/permissions.deny") ...
<varname>check_all_branches</varname> (integer) If set then allow_routing functions will check Request-URI of all branches (default). If disabled then only Request-URI of the first branch will be checked. Do not disable this parameter unless you really know what you are doing. Default value is 1. Set <varname>check_all_branches</varname> parameter ... modparam("permissions", "check_all_branches", 0) ...
<varname>allow_suffix</varname> (string) Suffix to be appended to basename to create filename of the allow file when version with one parameter of either allow_routing or allow_register is used. Including leading dot. Default value is .allow. Set <varname>allow_suffix</varname> parameter ... modparam("permissions", "allow_suffix", ".allow") ...
<varname>deny_suffix</varname> (string) Suffix to be appended to basename to create filename of the deny file when version with one parameter of either allow_routing or allow_register is used. Including leading dot. Default value is .deny. Set <varname>deny_suffix</varname> parameter ... modparam("permissions", "deny_suffix", ".deny") ...
<varname>db_url</varname> (string) This is URL of the database to be used. Since version 2.2, this url represents the db_url for the default partition. Default value is NULL. Set <varname>db_url</varname> parameter ... modparam("permissions", "db_url", "&exampledb;") ...
<varname>address_table</varname> (string) Name of database table containing matching rules used by allow_register function. Since version 2.2, this table name represents the table name for the default partition. Default value is address. Set <varname>address_table</varname> parameter ... modparam("permissions", "address_table", "pbx") ...
<varname>partition</varname> (string) Create a partition containg an url and a table name. Partitions allow you to use different databases or different tables from the same database. It is IMPORTANT that you split the paramteres by ";". Also, it is very IMPORTANT to put ";" after the last parameter, elseway it will not be taken into consideration. The db_url for a partition is mandatory. The table name, instead, has a default value 'address'. Set <varname>partition</varname> parameter ... modparam("permissions", "partition", "my_part: db_url=some_url; table_name=some_table;") ...
<varname>grp_col</varname> (string) Name of address table column containing group identifier of the address. Default value is grp. Set <varname>grp_col</varname> parameter ... modparam("permissions", "grp_col", "group_id") ...
<varname>ip_col</varname> (string) Name of address table column containing IP address part of the address. Default value is ip. Set <varname>ip_col</varname> parameter ... modparam("permissions", "ip_col", "ipess") ...
<varname>mask_col</varname> (string) Name of address table column containing network mask of the address. Possible values are 0-32. Default value is mask. Set <varname>mask_col</varname> parameter ... modparam("permissions", "mask_col", "subnet_length") ...
<varname>port_col</varname> (string) Name of address table column containing port part of the address. Default value is port. Set <varname>port_col</varname> parameter ... modparam("permissions", "port_col", "prt") ...
<varname>proto_col</varname> (string) Name of address table column containing transport protocol that is matched against transport protocol of received request. Possible values that can be stored in proto_col are any, udp, tcp, tls, sctp, and none. Value any matches always and value none never. Default value is proto. Set <varname>proto_col</varname> parameter ... modparam("permissions", "proto_col", "transport") ...
<varname>pattern_col</varname> (string) Name of address table column containing regular expression that is matched against the arguments received by check_address or check_source_address. Default value is pattern. Set <varname>pattern_col</varname> parameter ... modparam("permissions", "pattern_col", "regexp") ...
<varname>info_col</varname> (string) Name of address table column containing a string that is added as value to a pvar given as argument to check_address or check_source_address in case the function succedes. Default value is context_info. Set <varname>info_col</varname> parameter ... modparam("permissions", "info_col", "info_col") ...
Exported Functions
<function moreinfo="none">allow_routing()</function> Returns true if all pairs constructed as described in have appropriate permissions according to the configuration files. This function uses default configuration files specified in default_allow_file and default_deny_file. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>allow_routing</function> usage ... if (allow_routing()) { t_relay(); }; ...
<function moreinfo="none">allow_routing(basename)</function> Returns true if all pairs constructed as described in have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>allow_routing(basename)</function> usage ... if (allow_routing("basename")) { t_relay(); }; ...
<function moreinfo="none">allow_routing(allow_file,deny_file)</function> Returns true if all pairs constructed as described in have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>allow_routing(allow_file, deny_file)</function> usage ... if (allow_routing("rules.allow", "rules.deny")) { t_relay(); }; ...
<function moreinfo="none">allow_register(basename)</function> The function returns true if all pairs constructed as described in have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>allow_register(basename)</function> usage ... if (method=="REGISTER") { if (allow_register("register")) { save("location"); exit; } else { sl_send_reply("403", "Forbidden"); }; }; ...
<function moreinfo="none">allow_register(allow_file, deny_file)</function> The function returns true if all pairs constructed as described in have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>allow_register(allow_file, deny_file)</function> usage ... if (method=="REGISTER") { if (allow_register("register.allow", "register.deny")) { save("location"); exit; } else { sl_send_reply("403", "Forbidden"); }; }; ...
<function moreinfo="none">allow_uri(basename, pvar)</function> Returns true if the pair constructed as described in have appropriate permissions according to the configuration files specified by the parameter. Meaning of the parameter is as follows: basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. pvar - Any pseudo-variable defined in OpenSIPS. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>allow_uri(basename, pvar)</function> usage ... if (allow_uri("basename", "$rt")) { // Check Refer-To URI t_relay(); }; if (allow_uri("basename", "$avp(uri)") { // Check URI stored in $avp(uri) t_relay(); }; ...
<function moreinfo="none">check_address([partition:]group_id, ip, port, proto [, context_info [, pattern]])</function> Returns 1 if group id, IP address, port and protocol given as arguments match an IP subnet found in cached address table, as described in . The function takes 4 mandatory arguments and 2 optional ones. This function can be useful to check if a request can be allowed without authentication. Meaning of the parameter is as follows: group_id This argument represents the group id to be matched. It can be an integer string or a string pvar. If the group_id argument is "0", the query can match any group in the cached address table. Since version 2.2, you can also specify the partition(either string or pv). If no partition specified, the default one will be used. ip This argument represents the ip address to be matched. It can be an given directly as string or as a string pvar. This argument cannot be null/empty. port This argument represents the port to be matched. It can be an given as an integer string or as a string pvar. Cached address table entry containing port value 0 matches any port. Also, a "0" value for the argument can match any port in the address table. proto This argument represents the protocol used for transport; it can be an given as string or as string pvar. Transport protocol is either "ANY" or any valid transport protocol value: "UDP, "TCP", "TLS", and "SCTP". context_info This argument represents the pvar in wich the context_info field from the cached address table will be stored in case of match. This argument is optional. pattern This argument is a string to be matched with the regular expression pattern field from the cached address table. This argument is optional. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>check_address() </function> usage ... // Checks if the tuple IP address/port (given as strings) and source protocol // (given as pvar), belongs to group 4, verifies if pattern string "texttest" // matches the regular expression field in the database table and stores the // context information in $avp(ctx) if (check_address("4","192.168.2.135","5700","$proto","$avp(ctx)", "texttest")) { t_relay(); xlog("$avp(ctx)\n"); } if (check_address("my_part:4","192.168.2.135","5700","$proto","$avp(ctx)", "texttest")) { t_relay(); xlog("$avp(ctx)\n"); } ... // Checks if the tuple IP address/port/protocol of the source message is in group 4 if (check_address("4","$si","$sp","$proto")) { t_relay(); } ... // Checks if the tuple IP address/port/protocol stored in AVPs s:ip/s:port/s:proto // is in group 4 and stores context information in $avp(ctx) $avp(ip) = "192.168.2.135"; $avp(port) = 5061; $avp(proto) = "any"; $avp(partition)="my_part"; if (check_address("$avp(partition):4","$avp(ip)","$avp(port)","$avp(proto)","$avp(ctx))) { t_relay(); xlog("$avp(ctx)\n"); } ... // Checks if the tuple IP address/port (given as strings) and source protocol // (given as pvar) is in group 4, verifies if pattern string "texttest" matches // the regular expression field in the database table, without storing any // context information if (check_address("4","$si","5700","$proto","", "texttest")) { t_relay(); } ...
<function moreinfo="none">check_source_address(group_id [, context_info [, pattern]])</function> Equivalent to check_address(group_id, "$si", "$sp", "$proto", context_info, pattern). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>check_source_address()</function> usage ... // Check if source address/port/proto is in group 4 and stores // context information in $avp(ctx) if (check_source_address("$avp(partition):4","$avp(ctx)")) { xlog("$avp(ctx)\n"); }else { sl_send_reply("403", "Forbidden"); } ...
<function moreinfo="none">get_source_group(pvar)</function> Checks if an entry with the source ip/port/protocol is found in cached address or subnet table in any group. If yes, returns that group in the pvar parameter. If not returns -1. Port value 0 in cached address and subnet table matches any port. Since version 2.2, you can also specify the partition(either string or pv). If no partition specified, the default one will be used. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>get_source_group()</function> usage ... if ( get_source_group("$var(group)") ) { # do something with $var(group) xlog("group is $var(group)\n"); }; ...
Exported MI Functions
<function moreinfo="none">address_reload</function> Causes permissions module to re-read the contents of the address database table into cache memory. In cache memory the entries are for performance reasons stored in two different tables: address table and subnet table depending on the value of the mask field (32 or smaller). Parameters: partition - the name of the partition to be reloaded. If none specified all the partitions shall be reloaded.
<function moreinfo="none">address_dump</function> Causes permissions module to dump contents of the address table from cache memory. Parameters: partition - the name of the partition to be dumped. If none specified all the partitions shall be dumped.
<function moreinfo="none">subnet_dump</function> Causes permissions module to dump contents of cache memory subnet table. Parameters: partition - the name of the partition to be dumped. If none specified all the partitions shall be dumped.
<function moreinfo="none">allow_uri</function> Tests if (URI, Contact) pair is allowed according to allow/deny files. The files must already have been loaded by OpenSIPS. Parameters: basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. URI - URI to be tested Contact - Contact to be tested
opensips-2.2.2/modules/permissions/hash.c000066400000000000000000000311561300170765700205030ustar00rootroot00000000000000/* * Hash table functions * * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hash.h" #include "../../mem/shm_mem.h" #include "../../hash_func.h" #include "../../ip_addr.h" #include "../../ut.h" #include "../../pvar.h" #include "../../route_struct.h" #include "../../resolve.h" #include #include #include //#include #define perm_hash(_s) core_hash( &(_s), 0, PERM_HASH_SIZE) struct address_list** hash_create(void) { struct address_list** ptr; /* Initializing hash tables and hash table variable */ ptr = (struct address_list **)shm_malloc (sizeof(struct address_list*) * PERM_HASH_SIZE); if (!ptr) { LM_ERR("no shm memory for hash table\n"); return 0; } memset(ptr, 0, sizeof(struct address_list*) * PERM_HASH_SIZE); return ptr; } void hash_destroy(struct address_list** table) { if (!table) { LM_ERR("trying to destroy an empty hash table\n"); return; } empty_hash(table); shm_free(table); } int hash_insert(struct address_list** table, struct ip_addr *ip, unsigned int grp, unsigned int port, int proto, str* pattern, str* info) { struct address_list *node; unsigned int hash_val; str str_ip; node = (struct address_list*) shm_malloc (sizeof(struct address_list)); if (!node) { LM_ERR("no shm memory left\n"); return -1; } node->proto = proto; node->ip = (struct ip_addr *) shm_malloc (sizeof(struct ip_addr)); if (!node->ip) { LM_ERR("cannot allocate shm memory for ip_addr struct\n"); shm_free(node); return -1; } memcpy(node->ip, ip, sizeof(struct ip_addr)); if (pattern->len) { node->pattern = (char *) shm_malloc(pattern->len + 1); if (!node->pattern) { LM_ERR("cannot allocate shm memory for pattern string\n"); shm_free(node->ip); shm_free(node); return -1; } memcpy(node->pattern, pattern->s, pattern->len); node->pattern[pattern->len] = 0; } else { node->pattern = NULL; } if (info->len) { node->info = (char *) shm_malloc(info->len + 1); if (!node->info) { LM_CRIT("cannot allocate shm memory for context info string\n"); shm_free(node->ip); if (node->pattern) shm_free(node->pattern); shm_free(node); return -1; } memcpy(node->info, info->s, info->len); node->info[info->len] = '\0'; } else { node->info = NULL; } node->grp = grp; node->port = port; str_ip.len = ip->len; str_ip.s = (char*)ip->u.addr; hash_val = perm_hash(str_ip); node->next = table[hash_val]; table[hash_val] = node; return 1; } int hash_match(struct sip_msg *msg, struct address_list** table, unsigned int grp, struct ip_addr *ip, unsigned int port, int proto, char *pattern, char *info) { struct address_list *node; str str_ip; pv_spec_t *pvs; pv_value_t pvt; int i, match_res; if (grp != GROUP_ANY) { for (i = 0; i < PERM_HASH_SIZE; i++) { for (node = table[i]; node; node = node->next) { if (node->grp == grp) { goto grp_found; } } } /* group not found */ if (!node || i == PERM_HASH_SIZE) { LM_DBG("specified group %u does not exist in hash table\n", grp); return -2; } } grp_found: str_ip.len = ip->len; str_ip.s = (char*)ip->u.addr; for (node = table[perm_hash(str_ip)]; node; node = node->next) { /* LM_DBG("Comparing (%s %s) , (%d %d) , (%d %d) , (%d %d)\n", ip_addr2a(node->ip), ip_addr2a(ip), node->proto, proto, node->port , port, node->grp , grp); */ if ((node->grp == GROUP_ANY || node->grp == grp || grp == GROUP_ANY) && (node->proto == PROTO_NONE || node->proto == proto || proto == PROTO_NONE ) && (node->port == PORT_ANY || node->port == port || port == PORT_ANY) && ip_addr_cmp(ip, node->ip)) { if (!node->pattern || !pattern) { LM_DBG("no pattern to match\n"); goto found; } match_res = fnmatch(node->pattern, pattern, FNM_PERIOD); if (!match_res) { LM_DBG("pattern match\n"); goto found; } if (match_res != FNM_NOMATCH) { LM_ERR("fnmatch failed\n"); return -1; } } } LM_DBG("no match in the hash table\n"); return -1; found: if (info) { pvs = (pv_spec_t *)info; memset(&pvt, 0, sizeof(pv_value_t)); pvt.flags = PV_VAL_STR; pvt.rs.s = node->info; pvt.rs.len = node->info ? strlen(node->info) : 0; if (pv_set_value(msg, pvs, (int)EQ_T, &pvt) < 0) { LM_ERR("setting of avp failed\n"); return -1; } } LM_DBG("match found in the hash table\n"); return 1; } /* * Check if an ip_addr/port entry exists in hash table in any group. * Returns first group in which ip_addr/port is found. * Port 0 in hash table matches any port. */ int find_group_in_hash_table(struct address_list** table, struct ip_addr *ip, unsigned int port) { struct address_list *node; str str_ip; if (ip == NULL){ return -1; } str_ip.len = ip->len; str_ip.s = (char*) ip->u.addr; for (node = table[perm_hash(str_ip)]; node; node = node->next) { if ( (node->port == 0 || node->port == port) && ip_addr_cmp(ip, node->ip) ) return node->grp; } return -1; } int hash_mi_print(struct address_list **table, struct mi_node* rpl, struct pm_part_struct *pm) { int i; struct address_list *node; if (addf_mi_node_child(rpl, 0, 0, 0, "%.*s\n", pm->name.len, pm->name.s) == 0) return -1; for (i = 0; i < PERM_HASH_SIZE; i++) for (node = table[i]; node; node=node->next) if (addf_mi_node_child(rpl, 0, 0, 0, "\t%4d <%s,%u, %u, %d, %s, %s>", i, ip_addr2a(node->ip), node->grp, node->port, node->proto, node->pattern?node->pattern:"NULL", node->info?node->info:"NULL") == 0) return -1; return 0; } void empty_hash(struct address_list** table) { int i; struct address_list *node = NULL, *next = NULL; for (i = 0; i < PERM_HASH_SIZE; i++) { for (node = table[i]; node; node = next) { next = node->next; if (node->ip) shm_free(node->ip); if (node->pattern) shm_free(node->pattern); if (node->info) shm_free(node->info); shm_free(node); } table[i] = 0; } } /* * Create and initialize a subnet table */ struct subnet* new_subnet_table(void) { struct subnet* ptr; /* subnet record [PERM_MAX_SUBNETS] contains in its grp field the number of subnet records in the subnet table */ ptr = (struct subnet *)shm_malloc (sizeof(struct subnet) * (PERM_MAX_SUBNETS + 1)); if (!ptr) { LM_ERR("no shm memory for subnet table\n"); return 0; } ptr[PERM_MAX_SUBNETS].grp = 0; return ptr; } /* * Add into subnet table so that table is * kept in increasing ordered according to grp. */ int subnet_table_insert(struct subnet* table, unsigned int grp, struct net *subnet, unsigned int port, int proto, str* pattern, str *info) { int i; unsigned int count; count = table[PERM_MAX_SUBNETS].grp; if (count == PERM_MAX_SUBNETS) { LM_CRIT("subnet table is full\n"); return 0; } i = count - 1; while (i >= 0 && table[i].grp > grp) { table[i + 1] = table[i]; i--; } table[i + 1].grp = grp; table[i + 1].port = port; table[i + 1].proto = proto; if (subnet) { table[i + 1].subnet = (struct net*) shm_malloc(sizeof(struct net)); if (!table[i + 1].subnet) { LM_ERR("cannot allocate shm memory for table subnet\n"); return -1; } memcpy(table[i + 1].subnet, subnet, sizeof(struct net)); } else table[i + 1].subnet = NULL; if (info->len) { table[i + 1].info = (char*) shm_malloc(info->len + 1); if (!table[i + 1].info) { LM_ERR("cannot allocate shm memory for table info\n"); return -1; } memcpy(table[i + 1].info, info->s, info->len); table[i + 1].info[info->len] = 0; } else table[i + 1].info = NULL; if (pattern->len) { table[i + 1].pattern = (char*) shm_malloc(pattern->len + 1); if (!table[i + 1].pattern) { LM_ERR("cannot allocate shm memory for table pattern\n"); return -1; } memcpy(table[i + 1].pattern, pattern->s, pattern->len); table[i + 1].pattern[ pattern->len ] = 0; } else table[i + 1].pattern = NULL; table[PERM_MAX_SUBNETS].grp = count + 1; return 1; } /* * Check if an entry exists in subnet table that matches given group, ip_addr, * and port. Port 0 in subnet table matches any port. */ int match_subnet_table(struct sip_msg *msg, struct subnet* table, unsigned int grp, struct ip_addr *ip, unsigned int port, int proto, char *pattern, char *info) { unsigned int count, i; pv_value_t pvt; pv_spec_t *pvs; int match_res, found_group = 0; count = table[PERM_MAX_SUBNETS].grp; if (count == 0) { LM_DBG("subnet table is empty\n"); return -2; } if (grp != GROUP_ANY) { for (i = 0; i < count; i++) { if (table[i].grp == grp) { found_group = 1; break; } else if (table[i].grp > grp) { break; } } if (!found_group) { LM_DBG("specified group %u does not exist in hash table\n", grp); return -2; } } i = 0; do { if ((table[i].grp == grp || table[i].grp == GROUP_ANY || grp == GROUP_ANY) && (table[i].port == port || table[i].port == PORT_ANY || port == PORT_ANY) && (table[i].proto == proto || table[i].proto == PROTO_NONE || proto == PROTO_NONE)) { match_res = matchnet(ip, table[i].subnet); if (match_res != 1) { i++; continue; } if (table[i].pattern && pattern) { match_res = fnmatch(table[i].pattern, pattern, FNM_PERIOD); if (match_res) { i++; continue; } } if (info) { pvs = (pv_spec_t *)info; memset(&pvt, 0, sizeof(pv_value_t)); pvt.flags = PV_VAL_STR; pvt.rs.s = table[i].info; pvt.rs.len = table[i].info ? strlen(table[i].info) : 0; if (pv_set_value(msg, pvs, (int)EQ_T, &pvt) < 0) { LM_ERR("setting of avp failed\n"); return -1; } } LM_DBG("match found in the subnet table\n"); return 1; } if (table[i].grp > grp && grp != GROUP_ANY) break; i++; } while (i < count); LM_DBG("no match in the subnet table\n"); return -1; } /* * Print subnets stored in subnet table */ int subnet_table_mi_print(struct subnet* table, struct mi_node* rpl, struct pm_part_struct *pm) { unsigned int count, i; char *ip, *mask; static char ip_buff[IP_ADDR_MAX_STR_SIZE]; count = table[PERM_MAX_SUBNETS].grp; if (addf_mi_node_child(rpl, 0, 0, 0, "%.*s\n", pm->name.len, pm->name.s) == 0) return -1; for (i = 0; i < count; i++) { ip = ip_addr2a(&table[i].subnet->ip); if (!ip) { LM_ERR("cannot print ip address\n"); continue; } strcpy(ip_buff, ip); mask = ip_addr2a(&table[i].subnet->mask); if (!mask) { LM_ERR("cannot print mask address\n"); continue; } if (addf_mi_node_child(rpl, 0, 0, 0, "\t%4d <%u, %s, %s, %u>", i, table[i].grp, ip_buff, mask, table[i].port) == 0) return -1; } return 0; } /* * Check if an entry exists in subnet table that matches given ip_addr, * and port. Port 0 in subnet table matches any port. Return group of * first match or -1 if no match is found. */ int find_group_in_subnet_table(struct subnet* table, struct ip_addr *ip, unsigned int port) { unsigned int count, i, match_res; count = table[PERM_MAX_SUBNETS].grp; i = 0; while (i < count) { if (table[i].port == port || table[i].port == 0) { match_res = matchnet(ip, table[i].subnet); if (match_res == 1) return table[i].grp; } i++; } return -1; } /* * Empty contents of subnet table */ void empty_subnet_table(struct subnet *table) { int count, i; if (!table) return; count = table[PERM_MAX_SUBNETS].grp; for (i = 0; i < count; i++) { if (table[i].info) shm_free(table[i].info); if (table[i].subnet) shm_free(table[i].subnet); } table[PERM_MAX_SUBNETS].grp = 0; } /* * Release memory allocated for a subnet table */ void free_subnet_table(struct subnet* table) { empty_subnet_table(table); if (table) shm_free(table); } opensips-2.2.2/modules/permissions/hash.h000066400000000000000000000107751300170765700205140ustar00rootroot00000000000000/* * Hash table functions header file * * Copyright (C) 2009 Irina Stanescu * Copyright (C) 2009 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PERM_HASH_H #define PERM_HASH_H #include #include "../../ip_addr.h" #include "../../str.h" #include "../../mi/mi.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../usr_avp.h" #include "partitions.h" #define PERM_HASH_SIZE 128 #define GROUP_ANY 0 #define MASK_ANY 32 #define PORT_ANY 0 /* * Structure stored in address hash table */ struct address_list { struct ip_addr *ip; /* IP */ unsigned int grp; /* Group for the specified IP */ unsigned int port; /* Port */ int proto; /* Protocol -- UDP, TCP, TLS, or SCTP */ char *pattern; /* Pattern matching From header field */ char *info; /* Extra information */ struct address_list *next; /* Next element in the list */ }; /* * Create and initialize a hash table */ struct address_list** hash_create(void); /* * Destroy a hash table and release memory */ void hash_destroy(struct address_list** table); /* * Add into hash table */ int hash_insert(struct address_list** table, struct ip_addr *ip, unsigned int grp, unsigned int port, int proto, str* pattern, str* info); /* * Check if an entry exists in hash table that has given group, ip, * port, protocol value and pattern that matches to From URI. */ int hash_match(struct sip_msg *msg, struct address_list** table, unsigned int grp, struct ip_addr *ip, unsigned int port, int proto, char *pattern, char* info); /* * Print entries stored in hash table */ //void hash_print(struct address_list** hash_table, FILE* reply_file); int hash_mi_print(struct address_list **table, struct mi_node* rpl, struct pm_part_struct *pm); /* * Empty hash table */ void empty_hash(struct address_list** table); int find_group_in_hash_table(struct address_list** table, struct ip_addr *ip, unsigned int port); #define PERM_MAX_SUBNETS 128 /* * Structure used to store a subnet */ struct subnet { unsigned int grp; /* address group, subnet count in last record */ struct net *subnet; /* IP subnet + mask */ int proto; /* Protocol -- UDP, TCP, TLS, or SCTP */ char *pattern; /* Pattern matching From header field */ unsigned int port; /* port or 0 */ char *info; /* extra information */ }; /* * Create a subnet table */ struct subnet* new_subnet_table(void); /* * Check if an entry exists in subnet table that matches given group, ip_addr, * and port. Port 0 in subnet table matches any port. */ int match_subnet_table(struct sip_msg *msg, struct subnet* table, unsigned int group, struct ip_addr *ip, unsigned int port, int proto, char *pattern, char* info); /* * Checks if an entry exists in subnet table that matches given ip_addr, * and port. Port 0 in subnet table matches any port. Returns group of * the first match or -1 if no match is found. */ int find_group_in_subnet_table(struct subnet* table, struct ip_addr *ip, unsigned int port); /* * Empty contents of subnet table */ void empty_subnet_table(struct subnet *table); /* * Release memory allocated for a subnet table */ void free_subnet_table(struct subnet* table); /* * Add into subnet table so that table is * kept ordered according to subnet, port, grp. */ int subnet_table_insert(struct subnet* table, unsigned int grp, struct net *subnet, unsigned int port, int proto, str* pattern, str *info); /* * Print subnets stored in subnet table */ /*void subnet_table_print(struct subnet* table, FILE* reply_file);*/ int subnet_table_mi_print(struct subnet* table, struct mi_node* rpl, struct pm_part_struct *pm); #endif /* PERM_HASH_H */ opensips-2.2.2/modules/permissions/mi.c000066400000000000000000000136711300170765700201670ustar00rootroot00000000000000/* * Permissions MI functions * * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-10-16 created (juhe) */ #include "../../dprint.h" #include "address.h" #include "hash.h" #include "mi.h" #include "permissions.h" /* * MI function to reload address table */ struct mi_root* mi_address_reload(struct mi_root *cmd_tree, void *param) { struct mi_node *node = NULL; struct pm_part_struct *it, *ps; char errbuf[100] = "failed to reload partition "; int errlen = strlen(errbuf); if (cmd_tree) node = cmd_tree->node.kids; if (node == NULL) { /* reload all partitions */ for (it=get_part_structs(); it; it = it->next) { if (it->hash_table == NULL) continue; sprintf(errbuf + errlen, " %.*s!", it->name.len, it->name.s); LM_DBG("trying to reload address table for %.*s\n", it->name.len, it->name.s); if (reload_address_table(it) != 1) return init_mi_tree( 400, MI_SSTR(errbuf)); } return init_mi_tree( 200, MI_SSTR(MI_OK)); } else { /* reload requested partition */ ps = get_part_struct(&node->value); if (ps == NULL) goto err; if (ps->hash_table == NULL) return init_mi_tree( 200, MI_SSTR(MI_OK)); LM_INFO("trying to reload address table for %.*s\n", ps->name.len, ps->name.s); if (reload_address_table(ps) == 1) return init_mi_tree( 200, MI_SSTR(MI_OK)); } err: return init_mi_tree( 400, MI_SSTR("Trusted table reload failed")); } /* * MI function to print address entries from current hash table */ struct mi_root* mi_address_dump(struct mi_root *cmd_tree, void *param) { struct mi_root* rpl_tree; struct mi_node *node = NULL; struct pm_part_struct *it, *ps; if (cmd_tree) node = cmd_tree->node.kids; rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (node == NULL) { /* dump all partitions */ for (it=get_part_structs(); it; it = it->next) { if (it->hash_table == NULL) continue; if(hash_mi_print(*it->hash_table, &rpl_tree->node, it)< 0) { LM_ERR("failed to add a node\n"); free_mi_tree(rpl_tree); return 0; } } } else { /* dump only requested partition */ ps = get_part_struct(&node->value); if (ps == NULL) return init_mi_tree(404, MI_SSTR("No such partition")); if (ps->hash_table == NULL) return init_mi_tree( 200, MI_SSTR(MI_OK)); if(hash_mi_print(*ps->hash_table, &rpl_tree->node, ps)< 0) { LM_ERR("failed to add a node\n"); free_mi_tree(rpl_tree); return 0; } } return rpl_tree; } #define MAX_FILE_LEN 128 /* * MI function to make allow_uri query. */ struct mi_root* mi_allow_uri(struct mi_root *cmd, void *param) { struct mi_node *node; str *basenamep, *urip, *contactp; char basename[MAX_FILE_LEN + 1]; char uri[MAX_URI_SIZE + 1], contact[MAX_URI_SIZE + 1]; unsigned int allow_suffix_len; node = cmd->node.kids; if (node == NULL || node->next == NULL || node->next->next == NULL || node->next->next->next != NULL) return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM)); /* look for base name */ basenamep = &node->value; if (basenamep == NULL) return init_mi_tree(404, MI_SSTR("Basename is NULL")); allow_suffix_len = strlen(allow_suffix); if (basenamep->len + allow_suffix_len + 1 > MAX_FILE_LEN) return init_mi_tree(404, MI_SSTR("Basename is too long")); memcpy(basename, basenamep->s, basenamep->len); memcpy(basename + basenamep->len, allow_suffix, allow_suffix_len); basename[basenamep->len + allow_suffix_len] = 0; /* look for uri */ urip = &node->next->value; if (urip == NULL) return init_mi_tree(404, MI_SSTR("URI is NULL")); if (urip->len > MAX_URI_SIZE) return init_mi_tree(404, MI_SSTR("URI is too long")); memcpy(uri, urip->s, urip->len); uri[urip->len] = 0; /* look for contact */ contactp = &node->next->next->value; if (contactp == NULL) return init_mi_tree(404, MI_SSTR("Contact is NULL")); if (contactp->len > MAX_URI_SIZE) return init_mi_tree(404, MI_SSTR("Contact is too long")); memcpy(contact, contactp->s, contactp->len); contact[contactp->len] = 0; if (allow_test(basename, uri, contact) == 1) { return init_mi_tree(200, MI_SSTR(MI_OK)); } else { return init_mi_tree(403, MI_SSTR("Forbidden")); } } /* * MI function to print subnets from current subnet table */ struct mi_root* mi_subnet_dump(struct mi_root *cmd_tree, void *param) { struct mi_root* rpl_tree; struct mi_node *node = NULL; struct pm_part_struct *it, *ps; if (cmd_tree) node = cmd_tree->node.kids; rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (node == NULL) { /* dump all subnets */ for (it=get_part_structs(); it; it = it->next) { if (it->subnet_table == NULL) continue; if (subnet_table_mi_print(*it->subnet_table, &rpl_tree->node, it) < 0) { LM_ERR("failed to add a node\n"); free_mi_tree(rpl_tree); return 0; } } } else { ps = get_part_struct(&node->value); if (ps == NULL) return init_mi_tree(404, MI_SSTR("No such partition")); if (ps->subnet_table == NULL) return init_mi_tree( 200, MI_SSTR(MI_OK)); /* dump requested subnet*/ if (subnet_table_mi_print(*ps->subnet_table, &rpl_tree->node, ps) < 0) { LM_ERR("failed to add a node\n"); free_mi_tree(rpl_tree); return 0; } } return rpl_tree; } opensips-2.2.2/modules/permissions/mi.h000066400000000000000000000027611300170765700201720ustar00rootroot00000000000000/* * Header file for permissions MI functions * * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PERMISSIONS_MI_H_ #define _PERMISSIONS_MI_H_ #include "../../mi/mi.h" #define MI_ADDRESS_RELOAD "address_reload" #define MI_ADDRESS_DUMP "address_dump" #define MI_SUBNET_DUMP "subnet_dump" #define MI_ALLOW_URI "allow_uri" struct mi_root* mi_address_reload(struct mi_root *cmd, void *param); struct mi_root* mi_address_dump(struct mi_root *cmd, void *param); //struct mi_root* mi_address_reload(struct mi_root *cmd, void *param); //struct mi_root* mi_address_dump(struct mi_root *cmd, void *param); struct mi_root* mi_subnet_dump(struct mi_root *cmd_tree, void *param); struct mi_root* mi_allow_uri(struct mi_root *cmd, void *param); #endif opensips-2.2.2/modules/permissions/parse_config.c000066400000000000000000000135531300170765700222200ustar00rootroot00000000000000/* * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include "../../sr_module.h" #include "rule.h" #include "parse_config.h" /* * parse a comma separated expression list like a, b, c * return 0 on success, -1 on error * parsed expressions are returned in **e */ static int parse_expression_list(char *str, expression **e) { int start=0, i=-1, j=-1, apost=0; char str2[EXPRESSION_LENGTH]; expression *e1=NULL, *e2; if (!str || !e) return -1; *e = NULL; do { i++; switch(str[i]) { case '"': apost = !apost; break; case ',': if (apost) break; case '\0': /* word found */ while ((str[start] == ' ') || (str[start] == '\t')) start++; if (str[start] == '"') start++; j = i-1; while ((0 < j) && ((str[j] == ' ') || (str[j] == '\t'))) j--; if ((0 < j) && (str[j] == '"')) j--; if (start<=j) { /* valid word */ if (j-start+1+1>EXPRESSION_LENGTH) { LM_ERR("expression too long " "<%.*s>(%d)\n",j-start+1,str+start,j-start+1); goto error; } strncpy(str2, str+start, j-start+1); str2[j-start+1] = '\0'; e2 = new_expression(str2); if (!e2) /* memory error */ goto error; if (e1) { /* it is not the first */ e1->next = e2; e1 = e2; } else { /* it is the first */ *e = e1 = e2; } } else { /* parsing error */ goto error; } /* for the next word */ start = i+1; } } while (str[i] != '\0'); return 0; error: if (*e) { free_expression(*e); *e = NULL; } return -1; } /* * parse a complex expression list like a, b, c EXCEPT d, e * return 0 on success, -1 on error * parsed expressions are returned in **e, and exceptions are returned in **e_exceptions */ static int parse_expression(char *str, expression **e, expression **e_exceptions) { char *except, str2[LINE_LENGTH+1]; int i,j; if (!str || !e || !e_exceptions) return -1; except = strstr(str, " EXCEPT "); if (except) { /* exception found */ strncpy(str2, str, except-str); str2[except-str] = '\0'; /* except+8 points to the exception */ if (parse_expression_list(except+8, e_exceptions)) { /* error */ *e = *e_exceptions = NULL; return -1; } } else { /* no exception */ strcpy(str2, str); *e_exceptions = NULL; } for( i=0; isspace((int)str2[i]) ; i++); for( j=strlen(str2)-1 ; isspace((int)str2[j]) ; str2[j--]=0); if (strcmp("ALL", str2+i) == 0) { *e = NULL; } else { if (parse_expression_list(str2+i, e)) { /* error */ if (*e_exceptions) free_expression(*e_exceptions); *e = *e_exceptions = NULL; return -1; } } return 0; } /* * parse one line of the config file * return the rule according to line */ static rule *parse_config_line(char *line) { rule *rule1; expression *left, *left_exceptions, *right, *right_exceptions; int i=-1, exit=0, apost=0, colon=-1, eval=0; static char str1[LINE_LENGTH], str2[LINE_LENGTH+1]; if (!line) return 0; rule1 = 0; left = left_exceptions = right = right_exceptions = 0; while (!exit) { i++; switch(line[i]) { case '"': apost = !apost; eval = 1; break; case ':': if (!apost) colon = i; eval = 1; break; case '#': if (apost) break; case '\0': case '\n': exit = 1; break; case ' ': break; case '\t': break; default: eval = 1; } } if (eval) { if ((0left = left; rule1->left_exceptions = left_exceptions; rule1->right = right; rule1->right_exceptions = right_exceptions; return rule1; } else { /* error */ LM_ERR("failed to parse line: %s\n", line); } } return 0; error: if (left) free_expression(left); if (left_exceptions) free_expression(left_exceptions); if (right) free_expression(right); if (right_exceptions) free_expression(right_exceptions); return 0; } /* * parse a config file * return a list of rules */ rule *parse_config_file(char *filename) { FILE *file; char line[LINE_LENGTH+1]; rule *start_rule = NULL, *rule1 = NULL, *rule2 = NULL; file = fopen(filename,"r"); if (!file) { LM_INFO("file not found: %s\n", filename); return NULL; } while (fgets(line, LINE_LENGTH, file)) { rule2 = parse_config_line(line); if (rule2) { if (rule1) { /* it is not the first rule */ rule1->next = rule2; } else { /* it is the first rule */ start_rule = rule2; } rule1 = rule2; } } fclose(file); return start_rule; /* returns the linked list */ } opensips-2.2.2/modules/permissions/parse_config.h000066400000000000000000000017451300170765700222250ustar00rootroot00000000000000/* * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PARSE_CONFIG_H #define PARSE_CONFIG_H 1 #include "rule.h" rule *parse_config_file(char *filename); #endif /* PARSE_CONFIG_H */ opensips-2.2.2/modules/permissions/partitions.c000066400000000000000000000147031300170765700217530ustar00rootroot00000000000000/* * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "partitions.h" #include "../../str.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../db/db.h" #define ERR 1 str part_db_url = {"db_url", sizeof("db_url") - 1}; str part_table_name = {"table_name", sizeof("table_name") - 1}; /* since default partition will probably be used the most * it deserves a pointer for its own*/ struct pm_partition *partitions=NULL, *default_partition=NULL; struct pm_part_struct *part_structs=NULL; static void *alloc_partitions(void) { partitions = pkg_malloc(sizeof(struct pm_partition)); if (partitions) memset(partitions, 0, sizeof(struct pm_partition)); return partitions; } static void *alloc_default_partition(void) { default_partition = pkg_malloc(sizeof(struct pm_partition)); if (default_partition) { memset(default_partition, 0, sizeof(struct pm_partition)); default_partition->name.s = "default"; default_partition->name.len = sizeof("default") - 1; default_partition->next = partitions; partitions = default_partition; } return default_partition; } struct pm_partition *get_partitions(void) { return partitions; } struct pm_partition *get_partition(str *part_name) { struct pm_partition *it; for (it=get_partitions(); it; it=it->next) { if (!str_strcmp(part_name, &it->name)) break; } return it; } struct pm_partition *partition_set(void) { return partitions; } /* * set default partition url */ int set_default_db_url(modparam_t type, void *val) { str db_str; db_str.s = (char *)val; db_str.len = strlen(db_str.s); str_trim_spaces_lr(db_str); if (default_partition == NULL) if (alloc_default_partition() == NULL) goto out_nomem; default_partition->url.s = (char *)val; init_db_url( default_partition->url, 1 /* can be null */); return 0; out_nomem: LM_ERR("no more memory!\n"); return -1; } /* * set default partition table */ int set_default_table(modparam_t type, void *val) { str db_table; db_table.s = (char *)val; db_table.len = strlen(db_table.s); str_trim_spaces_lr(db_table); if (default_partition == NULL) if (alloc_default_partition() == NULL) goto out_nomem; default_partition->table = db_table; return 0; out_nomem: LM_ERR("no more memory!\n"); return -1; } /* * parse a partition parameter of type * : attr1=val1; attr2=val2; */ int parse_partition(modparam_t t, void *val) { str type, value, token; char *tok_end; struct pm_partition *el, *it; str decl = {(char*)val, strlen((char *)val)}; if (get_partitions() == NULL) { if (alloc_partitions() == NULL) goto out_memfault; el=get_partitions(); } else { el=pkg_malloc(sizeof(struct pm_partition)); if (el == NULL) goto out_memfault; memset(el, 0, sizeof(struct pm_partition)); for (it=get_partitions(); it->next; it=it->next); it->next = el; } tok_end = q_memchr(decl.s, ':', decl.len); if (tok_end == NULL) goto out_invdef; value.s = decl.s; value.len = tok_end - decl.s; str_trim_spaces_lr(value); el->name = value; decl.len = decl.len - (++tok_end - decl.s); decl.s = tok_end; while (decl.len > 0 && decl.s) { tok_end = q_memchr(decl.s, ';', decl.len); if (tok_end == NULL) break; token.s = decl.s; token.len = tok_end - token.s; tok_end = q_memchr(token.s, '=', token.len); if (tok_end == NULL) break; type.s = token.s; type.len = tok_end - type.s; value.s = tok_end + 1; value.len = (token.s + token.len) - value.s; decl.s += token.len + 1; decl.len -= (token.len + 1); str_trim_spaces_lr(type); str_trim_spaces_lr(value); if (!str_strcmp( &type, &part_db_url)) el->url = value; else if (!str_strcmp( &type, &part_table_name)) el->table = value; else goto out_invdef; } if (el->url.s == NULL) { LM_ERR("you should define an URL for this partition %.*s\n", el->name.len, el->name.s); return -1; } return 0; out_invdef: LM_ERR("invalid partition definition!\n"); return -ERR; out_memfault: LM_ERR("no more memory\n"); return -ERR; } int check_addr_param1(str *s, struct part_var *pv) { char *end; unsigned int gid; int ret; str spart, sval; ret=0; spart.s = s->s; end = q_memchr(s->s, ':', s->len); ret = -1; if (end == NULL) { ret = str2int(s, &gid); pv->u.parsed_part.partition.s = NULL; if (0 == ret) pv->u.parsed_part.v.ival = gid; else { pv->u.parsed_part.v.sval.s = s->s; pv->u.parsed_part.v.sval.len = s->len; } } else { spart.len = end - spart.s; sval.s = end + 1; sval.len = (s->s + s->len) - sval.s; str_trim_spaces_lr(sval); str_trim_spaces_lr(spart); pv->u.parsed_part.partition = spart; ret = str2int(&sval, &gid); if (0 == ret) pv->u.parsed_part.v.ival = gid; else pv->u.parsed_part.v.sval = sval; } return 0; } /* part struct API */ void add_part_struct(struct pm_part_struct *part_struct) { struct pm_part_struct *it; if (part_structs == NULL) { part_structs = part_struct; } else { for (it=part_structs; it->next; it = it->next); it->next = part_struct; } } void remove_part_struct(struct pm_part_struct *part_struct) { struct pm_part_struct *before, *el; if (!part_structs) LM_BUG("no part structs; what are you asking for?\n"); before = el = part_structs; while (el) { if (part_struct == el) { if (el->next) before->next = el->next; pkg_free(el); } if (before != el) before = before->next; el = el->next; } } struct pm_part_struct *get_part_structs(void) { return part_structs; } struct pm_part_struct *get_part_struct(str *name) { struct pm_part_struct *it; for (it=part_structs; it; it = it->next) { if (str_strcmp(name, &it->name) == 0) return it; } return NULL; } opensips-2.2.2/modules/permissions/partitions.h000066400000000000000000000045121300170765700217550ustar00rootroot00000000000000/* * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PM_PARTITIONS_H #define PM_PARTITIONS_H 1 #include "../../sr_module.h" #include "../../mod_fix.h" #include "../../db/db.h" #define TYPE_PV 0 #define TYPE_PARSED 1 struct pm_partition { str name; str url; str table; struct pm_partition *next; }; struct pm_part_struct { str url; str name; str table; struct address_list ***hash_table; /* Pointer to current hash table pointer */ struct address_list **hash_table_1; /* Pointer to hash table 1 */ struct address_list **hash_table_2; /* Pointer to hash table 2 */ struct subnet **subnet_table; /* Ptr to current subnet table */ struct subnet *subnet_table_1; /* Ptr to subnet table 1 */ struct subnet *subnet_table_2; /* Ptr to subnet table 2 */ db_con_t* db_handle; db_func_t perm_dbf; struct pm_part_struct *next; }; struct part_var { int type; union { struct parsed_part { str partition; union { int ival; str sval; } v; } parsed_part; gparam_p gp; } u; }; struct part_pvar { pv_spec_t *sp; gparam_p part; }; int parse_partition(modparam_t, void*); int set_default_table(modparam_t, void *val); int set_default_db_url(modparam_t, void *val); struct pm_partition *partition_set(void); int check_addr_param1(str *s, struct part_var *pv); struct pm_part_struct *get_part_structs(void); struct pm_part_struct *get_part_struct(str *name); struct pm_partition *get_partitions(void); void add_part_struct(struct pm_part_struct *part_struct); #endif opensips-2.2.2/modules/permissions/permissions.c000066400000000000000000000660021300170765700221310ustar00rootroot00000000000000/* * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * Copyright (C) 2003 iptel.org * Copyright (C) 2003-2007 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "../../sr_module.h" #include "permissions.h" #include "parse_config.h" #include "partitions.h" #include "address.h" #include "hash.h" #include "mi.h" #include "../../mem/mem.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_refer_to.h" #include "../../parser/contact/parse_contact.h" #include "../../str.h" #include "../../dset.h" #include "../../globals.h" #include "../../mod_fix.h" #include "../../ut.h" static rule_file_t allow[MAX_RULE_FILES]; /* Parsed allow files */ static rule_file_t deny[MAX_RULE_FILES]; /* Parsed deny files */ static int rules_num; /* Number of parsed allow/deny files */ /* Module parameter variables */ static char* default_allow_file = DEFAULT_ALLOW_FILE; static char* default_deny_file = DEFAULT_DENY_FILE; char* allow_suffix = ".allow"; static char* deny_suffix = ".deny"; /* for allow_address and allow_address function */ str db_url = {NULL, 0}; /* Don't connect to the database by default */ /* for allow_address function */ str address_table = str_init("address"); /* Name of address table */ str ip_col = str_init("ip"); /* Name of ip column */ str proto_col = str_init("proto"); /* Name of protocol column */ str pattern_col = str_init("pattern"); /* Name of pattern column */ str info_col = str_init("context_info"); /* Name of context info column */ str grp_col = str_init("grp"); /* Name of address group column */ str mask_col = str_init("mask"); /* Name of mask column */ str port_col = str_init("port"); /* Name of port column */ str id_col = str_init("id"); /* Name of id column */ /* * By default we check all branches */ static int check_all_branches = 1; /* * Convert the name of the files into table index */ static int load_fixup(void** param, int param_no); /* * Convert the name of the file into table index, this * function takes just one name, appends .allow and .deny * to and and the rest is same as in load_fixup */ static int single_fixup(void** param, int param_no); /* * Parse pseudo variable parameter */ static int double_fixup(void** param, int param_no); static int check_addr_fixup(void** param, int param_no); static int check_src_addr_fixup(void** param, int param_no); static int get_src_grp_fixup(void** param, int param_no); static int allow_routing_0(struct sip_msg* msg, char* str1, char* str2); static int allow_routing_1(struct sip_msg* msg, char* basename, char* str2); static int allow_routing_2(struct sip_msg* msg, char* allow_file, char* deny_file); static int allow_register_1(struct sip_msg* msg, char* basename, char* s); static int allow_register_2(struct sip_msg* msg, char* allow_file, char* deny_file); static int allow_uri(struct sip_msg* msg, char* basename, char* uri); static int mod_init(void); static void mod_exit(void); static int child_init(int rank); static int mi_address_child_init(); /* Exported functions */ static cmd_export_t cmds[] = { {"check_address" , (cmd_function) check_addr_4, 4, check_addr_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"check_address" , (cmd_function) check_addr_5, 5, check_addr_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"check_address" , (cmd_function) check_addr_6, 6, check_addr_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"check_source_address" ,(cmd_function)check_src_addr_1, 1, check_src_addr_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"check_source_address" , (cmd_function) check_src_addr_2, 2, check_src_addr_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, {"check_source_address" , (cmd_function) check_src_addr_3, 3, check_src_addr_fixup, 0, REQUEST_ROUTE| FAILURE_ROUTE|LOCAL_ROUTE}, {"get_source_group", (cmd_function) get_source_group, 1, get_src_grp_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE | LOCAL_ROUTE }, {"allow_routing", (cmd_function)allow_routing_0, 0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE | LOCAL_ROUTE}, {"allow_routing", (cmd_function)allow_routing_1, 1, single_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE | LOCAL_ROUTE}, {"allow_routing", (cmd_function)allow_routing_2, 2, load_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE | LOCAL_ROUTE}, {"allow_register", (cmd_function)allow_register_1, 1, single_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"allow_register", (cmd_function)allow_register_2, 2, load_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"allow_uri", (cmd_function)allow_uri, 2, double_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* Exported parameters */ static param_export_t params[] = { {"default_allow_file", STR_PARAM, &default_allow_file}, {"default_deny_file", STR_PARAM, &default_deny_file }, {"check_all_branches", INT_PARAM, &check_all_branches}, {"allow_suffix", STR_PARAM, &allow_suffix }, {"deny_suffix", STR_PARAM, &deny_suffix }, {"partition", STR_PARAM|USE_FUNC_PARAM, (void *)parse_partition }, {"db_url", STR_PARAM|USE_FUNC_PARAM, (void *)set_default_db_url }, {"address_table", STR_PARAM|USE_FUNC_PARAM, (void *)set_default_table }, {"ip_col", STR_PARAM, &ip_col.s }, {"proto_col", STR_PARAM, &proto_col.s }, {"pattern_col", STR_PARAM, &pattern_col.s }, {"info_col", STR_PARAM, &info_col.s }, {"grp_col", STR_PARAM, &grp_col.s }, {"mask_col", STR_PARAM, &mask_col.s }, {"port_col", STR_PARAM, &port_col.s }, {0, 0, 0} }; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { { MI_ADDRESS_RELOAD, 0, mi_address_reload, 0, 0, mi_address_child_init }, { MI_ADDRESS_DUMP, 0, mi_address_dump, 0, 0, 0 }, { MI_SUBNET_DUMP, 0, mi_subnet_dump, 0, 0, 0 }, { MI_ALLOW_URI, 0, mi_allow_uri, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; /* Module interface */ struct module_exports exports = { "permissions", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ mod_exit, /* destroy function */ child_init /* child initialization function */ }; static int get_src_grp_fixup(void** param, int param_no) { int ret; str s; struct part_var *pv; struct part_pvar *ppv; if (get_part_structs() == NULL) { LM_ERR("get_source_group() needs at least default partition!\n"); return E_UNSPEC; } if(param_no==1) { pv = pkg_malloc(sizeof(struct part_var)); if (pv == NULL) { LM_ERR("no more pkg mem\n"); return -1; } s.s = *param; s.len = strlen(s.s); if (check_addr_param1(&s, pv)) return -1; ppv = pkg_malloc(sizeof(struct part_pvar)); if (ppv == NULL) { LM_ERR("no more pkg mem\n"); return -1; } ppv->sp = (pv_spec_t *)pv->u.parsed_part.v.sval.s; ret=fixup_pvar((void **)&ppv->sp); if (ret) return E_UNSPEC; if (pv->u.parsed_part.partition.s) { pv->u.parsed_part.partition.s[pv->u.parsed_part.partition.len] = '\0'; if (fixup_sgp((void **)&pv->u.parsed_part.partition.s)) return E_UNSPEC; ppv->part = (gparam_p)pv->u.parsed_part.partition.s; } else { ppv->part = NULL; } *param = ppv; pkg_free(pv); return 0; } return E_UNSPEC; } /* * Extract path (the beginning of the string * up to the last / character * Returns length of the path */ static int get_path(char* pathname) { char* c; if (!pathname) return 0; c = strrchr(pathname, '/'); if (!c) return 0; return c - pathname + 1; } /* * Prepend path if necessary */ static char* get_pathname(char* name) { char* buffer; int path_len, name_len; if (!name) return 0; name_len = strlen(name); if (strchr(name, '/')) { buffer = (char*)pkg_malloc(name_len + 1); if (!buffer) goto err; strcpy(buffer, name); return buffer; } else { path_len = get_path(cfg_file); buffer = (char*)pkg_malloc(path_len + name_len + 1); if (!buffer) goto err; memcpy(buffer, cfg_file, path_len); memcpy(buffer + path_len, name, name_len); buffer[path_len + name_len] = '\0'; return buffer; } err: LM_ERR("no pkg memory left\n"); return 0; } /* * If the file pathname has been parsed already then the * function returns its index in the tables, otherwise it * returns -1 to indicate that the file needs to be read * and parsed yet */ static int find_index(rule_file_t* array, char* pathname) { int i; for(i = 0; i < rules_num; i++) { if (!strcmp(pathname, array[i].filename)) return i; } return -1; } /* * Return URI without all the bells and whistles, that means only * sip:username@domain, resulting buffer is statically allocated and * zero terminated */ static char* get_plain_uri(const str* uri) { static char buffer[EXPRESSION_LENGTH + 1]; struct sip_uri puri; int len; if (!uri) return 0; if (parse_uri(uri->s, uri->len, &puri) < 0) { LM_ERR("failed to parse URI\n"); return 0; } if (puri.user.len) { len = puri.user.len + puri.host.len + 5; } else { len = puri.host.len + 4; } if (len > EXPRESSION_LENGTH) { LM_ERR("Request-URI is too long: %d chars\n", len); return 0; } strcpy(buffer, "sip:"); if (puri.user.len) { memcpy(buffer + 4, puri.user.s, puri.user.len); buffer[puri.user.len + 4] = '@'; memcpy(buffer + puri.user.len + 5, puri.host.s, puri.host.len); } else { memcpy(buffer + 4, puri.host.s, puri.host.len); } buffer[len] = '\0'; return buffer; } /* * determines the permission of the call * return values: * -1: deny * 1: allow */ static int check_routing(struct sip_msg* msg, int idx) { struct hdr_field *from; int len, q; static char from_str[EXPRESSION_LENGTH+1]; static char ruri_str[EXPRESSION_LENGTH+1]; char* uri_str; str branch; int br_idx; /* turn off control, allow any routing */ if ((!allow[idx].rules) && (!deny[idx].rules)) { LM_DBG("no rules => allow any routing\n"); return 1; } /* looking for FROM HF */ if ((!msg->from) && (parse_headers(msg, HDR_FROM_F, 0) == -1)) { LM_ERR("failed to parse message\n"); return -1; } if (!msg->from) { LM_ERR("FROM header field not found\n"); return -1; } /* we must call parse_from_header explicitly */ if ((!(msg->from)->parsed) && (parse_from_header(msg) < 0)) { LM_ERR("failed to parse From body\n"); return -1; } from = msg->from; len = ((struct to_body*)from->parsed)->uri.len; if (len > EXPRESSION_LENGTH) { LM_ERR("From header field is too long: %d chars\n", len); return -1; } strncpy(from_str, ((struct to_body*)from->parsed)->uri.s, len); from_str[len] = '\0'; /* looking for request URI */ if (parse_sip_msg_uri(msg) < 0) { LM_ERR("uri parsing failed\n"); return -1; } len = msg->parsed_uri.user.len + msg->parsed_uri.host.len + 5; if (len > EXPRESSION_LENGTH) { LM_ERR("Request URI is too long: %d chars\n", len); return -1; } strcpy(ruri_str, "sip:"); memcpy(ruri_str + 4, msg->parsed_uri.user.s, msg->parsed_uri.user.len); ruri_str[msg->parsed_uri.user.len + 4] = '@'; memcpy(ruri_str + msg->parsed_uri.user.len + 5, msg->parsed_uri.host.s, msg->parsed_uri.host.len); ruri_str[len] = '\0'; LM_DBG("looking for From: %s Request-URI: %s\n", from_str, ruri_str); /* rule exists in allow file */ if (search_rule(allow[idx].rules, from_str, ruri_str)) { if (check_all_branches) goto check_branches; LM_DBG("allow rule found => routing is allowed\n"); return 1; } /* rule exists in deny file */ if (search_rule(deny[idx].rules, from_str, ruri_str)) { LM_DBG("deny rule found => routing is denied\n"); return -1; } if (!check_all_branches) { LM_DBG("neither allow nor deny rule found => routing is allowed\n"); return 1; } check_branches: for( br_idx=0 ; (branch.s=get_branch(br_idx,&branch.len,&q,0,0,0,0))!=0 ; br_idx++ ) { uri_str = get_plain_uri(&branch); if (!uri_str) { LM_ERR("failed to extract plain URI\n"); return -1; } LM_DBG("looking for From: %s Branch: %s\n", from_str, uri_str); if (search_rule(allow[idx].rules, from_str, uri_str)) { continue; } if (search_rule(deny[idx].rules, from_str, uri_str)) { LM_DBG("deny rule found for one of branches => routing" "is denied\n"); return -1; } } LM_DBG("check of branches passed => routing is allowed\n"); return 1; } /* * Convert the name of the files into table index */ static int load_fixup(void** param, int param_no) { char* pathname; int idx; rule_file_t* table; if (param_no == 1) { table = allow; } else { table = deny; } pathname = get_pathname(*param); idx = find_index(table, pathname); if (idx == -1) { /* Not opened yet, open the file and parse it */ table[rules_num].filename = pathname; table[rules_num].rules = parse_config_file(pathname); if (table[rules_num].rules) { LM_DBG("file (%s) parsed\n", pathname); } else { LM_INFO("file (%s) not found => empty rule set\n", pathname); } *param = (void*)(long)rules_num; if (param_no == 2) rules_num++; } else { /* File already parsed, re-use it */ LM_DBG("file (%s) already loaded, re-using\n", pathname); pkg_free(pathname); *param = (void*)(long)idx; } return 0; } /* * Convert the name of the file into table index */ static int single_fixup(void** param, int param_no) { char* buffer; void* tmp; int param_len, ret, suffix_len; if (param_no != 1) return 0; param_len = strlen((char*)*param); if (strlen(allow_suffix) > strlen(deny_suffix)) { suffix_len = strlen(allow_suffix); } else { suffix_len = strlen(deny_suffix); } buffer = pkg_malloc(param_len + suffix_len + 1); if (!buffer) { LM_ERR("no pkg memory left\n"); return -1; } strcpy(buffer, (char*)*param); strcat(buffer, allow_suffix); tmp = buffer; ret = load_fixup(&tmp, 1); strcpy(buffer + param_len, deny_suffix); tmp = buffer; ret |= load_fixup(&tmp, 2); *param = tmp; pkg_free(buffer); return ret; } /* * Convert the name of the file into table index and pvar into parsed pseudo * variable specification */ static int double_fixup(void** param, int param_no) { char* buffer; void* tmp; int param_len, ret, suffix_len; pv_spec_t *sp; str s; if (param_no == 1) { /* basename */ param_len = strlen((char*)*param); if (strlen(allow_suffix) > strlen(deny_suffix)) { suffix_len = strlen(allow_suffix); } else { suffix_len = strlen(deny_suffix); } buffer = pkg_malloc(param_len + suffix_len + 1); if (!buffer) { LM_ERR("no pkg memory left\n"); return -1; } strcpy(buffer, (char*)*param); strcat(buffer, allow_suffix); tmp = buffer; ret = load_fixup(&tmp, 1); strcpy(buffer + param_len, deny_suffix); tmp = buffer; ret |= load_fixup(&tmp, 2); *param = tmp; pkg_free(buffer); return 0; } else if (param_no == 2) { /* pseudo variable */ sp = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); if (sp == 0) { LM_ERR("no pkg memory left\n"); return -1; } s.s = (char*)*param; s.len = strlen(s.s); if (pv_parse_spec(&s, sp) == 0) { LM_ERR("parsing of pseudo variable %s failed!\n", (char*)*param); pkg_free(sp); return -1; } if (sp->type == PVT_NULL) { LM_ERR("bad pseudo variable\n"); pkg_free(sp); return -1; } *param = (void*)sp; return 0; } *param = (void *)0; return 0; } /* * module initialization function */ static int mod_init(void) { struct pm_partition *el, *prev_el=NULL; LM_DBG("initializing...\n"); ip_col.len = strlen(ip_col.s); proto_col.len = strlen(proto_col.s); pattern_col.len = strlen(pattern_col.s); info_col.len = strlen(info_col.s); grp_col.len = strlen(grp_col.s); mask_col.len = strlen(mask_col.s); port_col.len = strlen(port_col.s); allow[0].filename = get_pathname(default_allow_file); allow[0].rules = parse_config_file(allow[0].filename); if (allow[0].rules) { LM_DBG("default allow file (%s) parsed\n", allow[0].filename); } else { LM_INFO("default allow file (%s) not found => empty rule set\n", allow[0].filename); } deny[0].filename = get_pathname(default_deny_file); deny[0].rules = parse_config_file(deny[0].filename); if (deny[0].rules) { LM_DBG("default deny file (%s) parsed\n", deny[0].filename); } else { LM_INFO("default deny file (%s) not found => empty rule set\n", deny[0].filename); } el = get_partitions(); while (el) { /* initialize table name if not done from script */ if (el->table.s == NULL) { el->table.s = "address"; el->table.len = strlen(el->table.s); } if (init_address(el) != 0) { LM_ERR("failed to initialize the allow_address function\n"); return -1; } prev_el = el; el = el->next; pkg_free(prev_el); } rules_num = 1; return 0; } static int child_init(int rank) { return 0; } static int mi_address_child_init(void) { return mi_init_address(); } /* static int mi_addr_child_init(void) { return mi_init_addresses(); } */ /* * destroy function */ static void mod_exit(void) { int i; struct pm_part_struct *it; for(i = 0; i < rules_num; i++) { free_rule(allow[i].rules); pkg_free(allow[i].filename); free_rule(deny[i].rules); pkg_free(deny[i].filename); } for (it=get_part_structs(); it; it=it->next) clean_address(it); // clean_addresses(); } /* * Uses default rule files from the module parameters */ int allow_routing_0(struct sip_msg* msg, char* str1, char* str2) { return check_routing(msg, 0); } int allow_routing_1(struct sip_msg* msg, char* basename, char* s) { return check_routing(msg, (int)(long)basename); } /* * Accepts allow and deny files as parameters */ int allow_routing_2(struct sip_msg* msg, char* allow_file, char* deny_file) { /* Index converted by load_lookup */ return check_routing(msg, (int)(long)allow_file); } /* * Test of REGISTER messages. Creates To-Contact pairs and compares them * against rules in allow and deny files passed as parameters. The function * iterates over all Contacts and creates a pair with To for each contact * found. That allows to restrict what IPs may be used in registrations, for * example */ static int check_register(struct sip_msg* msg, int idx) { int len; static char to_str[EXPRESSION_LENGTH + 1]; char* contact_str; contact_t* c; /* turn off control, allow any routing */ if ((!allow[idx].rules) && (!deny[idx].rules)) { LM_DBG("no rules => allow any registration\n"); return 1; } /* * Note: We do not parse the whole header field here although the message can * contain multiple Contact header fields. We try contacts one by one and if one * of them causes reject then we don't look at others, this could improve performance * a little bit in some situations */ if (parse_headers(msg, HDR_TO_F | HDR_CONTACT_F, 0) == -1) { LM_ERR("failed to parse headers\n"); return -1; } if (!msg->to) { LM_ERR("To or Contact not found\n"); return -1; } if (!msg->contact) { /* REGISTER messages that contain no Contact header field * are allowed. Such messages do not modify the contents of * the user location database anyway and thus are not harmful */ LM_DBG("no Contact found, allowing\n"); return 1; } /* Check if the REGISTER message contains start Contact and if * so then allow it */ if (parse_contact(msg->contact) < 0) { LM_ERR("failed to parse Contact HF\n"); return -1; } if (((contact_body_t*)msg->contact->parsed)->star) { LM_DBG("* Contact found, allowing\n"); return 1; } len = ((struct to_body*)msg->to->parsed)->uri.len; if (len > EXPRESSION_LENGTH) { LM_ERR("To header field is too long: %d chars\n", len); return -1; } strncpy(to_str, ((struct to_body*)msg->to->parsed)->uri.s, len); to_str[len] = '\0'; if (contact_iterator(&c, msg, 0) < 0) { return -1; } while(c) { contact_str = get_plain_uri(&c->uri); if (!contact_str) { LM_ERR("can't extract plain Contact URI\n"); return -1; } LM_DBG("looking for To: %s Contact: %s\n", to_str, contact_str); /* rule exists in allow file */ if (search_rule(allow[idx].rules, to_str, contact_str)) { if (check_all_branches) goto skip_deny; } /* rule exists in deny file */ if (search_rule(deny[idx].rules, to_str, contact_str)) { LM_DBG("deny rule found => Register denied\n"); return -1; } skip_deny: if (contact_iterator(&c, msg, c) < 0) { return -1; } } LM_DBG("no contact denied => Allowed\n"); return 1; } int allow_register_1(struct sip_msg* msg, char* basename, char* s) { return check_register(msg, (int)(long)basename); } int allow_register_2(struct sip_msg* msg, char* allow_file, char* deny_file) { return check_register(msg, (int)(long)allow_file); } /* * determines the permission to an uri * return values: * -1: deny * 1: allow */ static int allow_uri(struct sip_msg* msg, char* _idx, char* _sp) { struct hdr_field *from; int idx, len; static char from_str[EXPRESSION_LENGTH+1]; static char uri_str[EXPRESSION_LENGTH+1]; pv_spec_t *sp; pv_value_t pv_val; idx = (int)(long)_idx; sp = (pv_spec_t *)_sp; /* turn off control, allow any uri */ if ((!allow[idx].rules) && (!deny[idx].rules)) { LM_DBG("no rules => allow any uri\n"); return 1; } /* looking for FROM HF */ if ((!msg->from) && (parse_headers(msg, HDR_FROM_F, 0) == -1)) { LM_ERR("failed to parse message\n"); return -1; } if (!msg->from) { LM_ERR("FROM header field not found\n"); return -1; } /* we must call parse_from_header explicitly */ if ((!(msg->from)->parsed) && (parse_from_header(msg) < 0)) { LM_ERR("failed to parse From body\n"); return -1; } from = msg->from; len = ((struct to_body*)from->parsed)->uri.len; if (len > EXPRESSION_LENGTH) { LM_ERR("From header field is too long: %d chars\n", len); return -1; } strncpy(from_str, ((struct to_body*)from->parsed)->uri.s, len); from_str[len] = '\0'; if (sp && (pv_get_spec_value(msg, sp, &pv_val) == 0)) { if (pv_val.flags & PV_VAL_STR) { if (pv_val.rs.len > EXPRESSION_LENGTH) { LM_ERR("pseudo variable value is too " "long: %d chars\n", pv_val.rs.len); return -1; } strncpy(uri_str, pv_val.rs.s, pv_val.rs.len); uri_str[pv_val.rs.len] = '\0'; } else { LM_ERR("pseudo variable value is not string\n"); return -1; } } else { LM_ERR("cannot get pseudo variable value\n"); return -1; } LM_DBG("looking for From: %s URI: %s\n", from_str, uri_str); /* rule exists in allow file */ if (search_rule(allow[idx].rules, from_str, uri_str)) { LM_DBG("allow rule found => URI is allowed\n"); return 1; } /* rule exists in deny file */ if (search_rule(deny[idx].rules, from_str, uri_str)) { LM_DBG("deny rule found => URI is denied\n"); return -1; } LM_DBG("neither allow nor deny rule found => URI is allowed\n"); return 1; } /* * Test URI against Contact. */ int allow_test(char *file, char *uri, char *contact) { char *pathname; int idx; pathname = get_pathname(file); if (!pathname) { LM_ERR("Cannot get pathname of <%s>\n", file); return 0; } idx = find_index(allow, pathname); if (idx == -1) { LM_ERR("File <%s> has not been loaded\n", pathname); pkg_free(pathname); return 0; } pkg_free(pathname); /* turn off control, allow any routing */ if ((!allow[idx].rules) && (!deny[idx].rules)) { LM_DBG("No rules => Allowed\n"); return 1; } LM_DBG("Looking for URI: %s, Contact: %s\n", uri, contact); /* rule exists in allow file */ if (search_rule(allow[idx].rules, uri, contact)) { LM_DBG("Allow rule found => Allowed\n"); return 1; } /* rule exists in deny file */ if (search_rule(deny[idx].rules, uri, contact)) { LM_DBG("Deny rule found => Denied\n"); return 0; } LM_DBG("Neither allow or deny rule found => Allowed\n"); return 1; } static int check_addr_fixup(void** param, int param_no) { int ret; gparam_p gp; struct part_var *pv; if (get_part_structs() == NULL) { LM_ERR("check_source_address needs db_url to be set!\n"); return E_UNSPEC; } /* grp ip port proto info pattern*/ switch (param_no) { case 1: ret = fixup_spve(param); if (0 == ret) { gp = *param; pv = pkg_malloc(sizeof(struct part_var)); if (pv == NULL) { LM_ERR("no more pkg mem\n"); return -1; } if (gp->type == GPARAM_TYPE_STR) { pv->type = TYPE_PARSED; ret = check_addr_param1(&gp->v.sval, pv); } else { pv->type = TYPE_PV; pv->u.gp = gp; } *param = pv; } return ret; case 2: case 3: case 4: return fixup_spve(param); case 5: if (*param && strlen((char*)*param)) return fixup_pvar(param); *param = NULL; return 0; case 6: if (*param && strlen((char*)*param)) return fixup_spve(param); *param = NULL; return 0; } return E_UNSPEC; } static int check_src_addr_fixup(void** param, int param_no) { int ret; gparam_p gp; struct part_var *pv; if (get_part_structs() == NULL) { LM_ERR("check_source_address needs db_url to be set!\n"); return E_UNSPEC; } /* grp info pattern */ switch (param_no) { case 1: ret = fixup_spve(param); if (0 == ret) { gp = *param; pv = pkg_malloc(sizeof(struct part_var)); if (pv == NULL) { LM_ERR("no more pkg mem\n"); return -1; } if (gp->type == GPARAM_TYPE_STR) { pv->type = TYPE_PARSED; ret = check_addr_param1(&gp->v.sval, pv); } else { pv->type = TYPE_PV; pv->u.gp = gp; } *param = pv; } return ret; case 2: if (*param && strlen((char*)*param)) return fixup_pvar(param); *param = NULL; return 0; case 3: if (*param && strlen((char*)*param)) return fixup_spve(param); *param = NULL; return 0; } return E_UNSPEC; } opensips-2.2.2/modules/permissions/permissions.h000066400000000000000000000043021300170765700221310ustar00rootroot00000000000000/* * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-09-03 replaced /usr/local/et/ser/ with CFG_DIR (andrei) */ #ifndef PERMISSIONS_H #define PERMISSIONS_H 1 #include "../../sr_module.h" #include "../../db/db.h" #include "../../pvar.h" #include "rule.h" #define DEFAULT_ALLOW_FILE "permissions.allow" #define DEFAULT_DENY_FILE "permissions.deny" typedef struct rule_file { rule* rules; /* Parsed rule set */ char* filename; /* The name of the file */ } rule_file_t; /* * Maximum number if allow/deny file pairs that can be opened * at any time */ #define MAX_RULE_FILES 64 extern str db_url; /* Database URL */ extern str address_table; /* Name of address table */ extern str ip_col; /* Name of IP address column */ extern str proto_col; /* Name of protocol column */ extern str pattern_col; /* Name of pattern column */ extern str info_col; /* Name of context_info column */ extern str grp_col; /* Name of address group column */ extern str mask_col; /* Name of mask column */ extern str port_col; /* Name of port column */ extern str id_col; /* Name of id column */ typedef struct int_or_pvar { unsigned int i; pv_spec_t *pvar; /* zero if int */ } int_or_pvar_t; #define DISABLE_CACHE 0 #define ENABLE_CACHE 1 char *allow_suffix; int allow_test(char *file, char *uri, char *contact); #endif opensips-2.2.2/modules/permissions/rule.c000066400000000000000000000075261300170765700205330ustar00rootroot00000000000000/* * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include "../../mem/mem.h" #include "../../sr_module.h" #include "../../mem/mem.h" #include "rule.h" /* * allocate memory for a new rule */ rule *new_rule(void) { rule *r; r = (rule *)pkg_malloc(sizeof(rule)); if (!r) { LM_ERR("not enough pkg memory\n"); return 0; } memset(r, 0, sizeof(rule)); return r; } /* * free memory allocated by a rule */ void free_rule(rule *r) { if (!r) return; if (r->left) free_expression(r->left); if (r->left_exceptions) free_expression(r->left_exceptions); if (r->right) free_expression(r->right); if (r->right_exceptions) free_expression(r->right_exceptions); if (r->next) free_rule(r->next); pkg_free(r); } /* * list rules */ void print_rule(rule *r) { if (!r) return; printf("\nNEW RULE:\n"); printf("\n\tLEFT: "); if (r->left) print_expression(r->left); else printf("ALL"); if (r->left_exceptions) { printf("\n\tLEFT EXCEPTIONS: "); print_expression(r->left_exceptions); } printf("\n\tRIGHT: "); if (r->right) print_expression(r->right); else printf("ALL"); if (r->right_exceptions) { printf("\n\tRIGHT EXCEPTIONS: "); print_expression(r->right_exceptions); } printf("\n"); if (r->next) print_rule(r->next); } /* * look for a proper rule matching with left:right */ int search_rule(rule *r, char *left, char *right) { rule *r1; r1 = r; while (r1) { if (( (!r1->left) || (search_expression(r1->left, left)) ) && (!search_expression(r1->left_exceptions, left)) && ( (!r1->right) || (search_expression(r1->right, right)) ) && (!search_expression(r1->right_exceptions, right))) return 1; r1 = r1->next; } return 0; } /* * allocate memory for a new expression * str is saved in vale, and compiled to POSIX regexp (reg_value) */ expression *new_expression(char *str) { expression *e; if (!str) return 0; e = (expression *)pkg_malloc(sizeof(expression)); if (!e) { LM_ERR("not enough pkg memory\n"); return 0; } if (strlen(str) > EXPRESSION_LENGTH){ LM_ERR("expression too long\n"); pkg_free(e); return 0; } strcpy(e->value, str); e->reg_value = (regex_t*)pkg_malloc(sizeof(regex_t)); if (!e->reg_value) { LM_ERR("not enough pkg memory\n"); pkg_free(e); return 0; } if (regcomp(e->reg_value, str, REG_EXTENDED|REG_NOSUB|REG_ICASE) ) { LM_ERR("bad regular expression: %s\n", str); pkg_free(e->reg_value); pkg_free(e); return NULL; } e->next = 0; return e; } /* * free memory allocated by an expression */ void free_expression(expression *e) { if (!e) return; if (e->next) free_expression(e->next); regfree(e->reg_value); pkg_free(e); } /* * list expressions */ void print_expression(expression *e) { if (!e) return; printf("%s, ", e->value); if (e->next) print_expression(e->next); } /* * look for matching expression */ int search_expression(expression *e, char *value) { expression *e1; e1 = e; while (e1) { if (regexec(e1->reg_value, value, 0, 0, 0) == 0) return 1; e1 = e1->next; } return 0; } opensips-2.2.2/modules/permissions/rule.h000066400000000000000000000037761300170765700205430ustar00rootroot00000000000000/* * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef RULE_H #define RULE_H 1 #include #define EXPRESSION_LENGTH 256 /* maximum length of an expression */ #define LINE_LENGTH 500 /* maximum length of lines in the config file */ struct rule_struct; typedef struct rule_struct rule; struct expression_struct; typedef struct expression_struct expression; rule *new_rule(void); void free_rule(rule *r); void print_rule(rule *r); int search_rule(rule *r, char *left, char *right); expression *new_expression(char *str); void free_expression(expression *e); void print_expression(expression *e); int search_expression(expression *e, char *value); /* * stores an expression * value represents the string, and reg_value is the compiled string to POSIX regular expression */ struct expression_struct { char value[EXPRESSION_LENGTH+1]; regex_t *reg_value; struct expression_struct *next; }; /* * stores 4 lists of expressions in the following way: * * a, b, c EXCEPT d, e : f, g EXCEPT h * left = a, b, c * left_exceptions = d, e * right = f, g * right_exceptions = h */ struct rule_struct { expression *left, *left_exceptions, *right, *right_exceptions; struct rule_struct *next; }; #endif /* RULE_H */ opensips-2.2.2/modules/pi_http/000077500000000000000000000000001300170765700165025ustar00rootroot00000000000000opensips-2.2.2/modules/pi_http/Makefile000066400000000000000000000016101300170765700201400ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pi_http.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules install_module_custom: echo "installing pi_http framework samples ..." mkdir -p $(data_prefix)/$(data_dir)/pi_http for FILE in $(wildcard ../../scripts/pi_http/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/pi_http/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/pi_http/`basename "$$FILE"` ; \ fi ; \ done opensips-2.2.2/modules/pi_http/README000066400000000000000000000163471300170765700173750ustar00rootroot00000000000000pi_http Module Ovidiu Sas Edited by Ovidiu Sas Copyright © 2012-2013 VoIP Embedded, Inc. Revision History Revision $Rev: 8688 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Usage 1.3. Framework 1.3.1. Database connection definition block 1.3.2. Table definition block 1.3.3. Command definition block 1.4. To-do 1.5. Dependencies 1.5.1. OpenSIPS Modules 1.6. External Libraries or Applications 1.7. Exported Parameters 1.7.1. pi_http_root(string) 1.7.2. framework(string) 1.7.3. pi_http_method(integrer) 1.8. Exported MI Functions 1.8.1. pi_reload_tbls_and_cmds List of Examples 1.1. Set pi_http_root parameter 1.2. Set framework parameter 1.3. Set pi_http_method parameter Chapter 1. Admin Guide 1.1. Overview This module provides an HTTP provisioning interface for OpenSIPS. It is using the OpenSIPS's internal database API to provide a simple way of manipulating records inside OpenSIPS's tables. The module offers: * ability to connect to multiple/different databases through OpenSIPS's db API; (all OpenSIPS's databases are supported); * ability to perform data input validation through OpenSIPS API; * ability to reconfigure the interface layout on the fly by reloading the config from the xml framework via mi command interface. Note: when provisioning tables using db_text, any change made to a db_text table will not be reflected on the actual text file. In order to force a write out to the disk of the cached tables, the db_text mi command dbt_dump must be used. 1.2. Usage The layout of the provisionning interface is controlled via an external xml file (see the framework parameter). An example of a framework xml file is provided inside the examples directory of the pi_http module. A simple framework file can be generated by the opensipsdbctl command: opensipsdbctl pframework create The generated framework will be saved inside OpenSIPS's config directory as pi_framework_sample. The list of configurable tables will be based on the STANDARD_MODULES and EXTRA_MODULES vars from opensipsctlrc file. 1.3. Framework The xml framework file is organized in three distinctive blocks: * database connection definition block * table definition block * command definition block 1.3.1. Database connection definition block Each connection to a particular database must be defined here with a unique database connection id. The connection parameters are defined following the db_url param pattern for all OpenSIPS modules that are using a database. Supported databases: * berkeley * flatstore * http * mysql * oracle * postgres * text * unixodbc * virtual 1.3.2. Table definition block Each table managed through the OpenSIPS provisioning interface must be defined here with a unique table id. For each table, the database connection id must be specified. Each table must list all columns that will be managed by the OpenSIPS provisioning interface. Each column must have a unique field name and a type. Each column may have a validation tag for validating input data. Supported column types: * DB_INT * DB_BIGINT * DB_DOUBLE * DB_STRING * DB_STR * DB_DATETIME + Note: input field must be provided in 'YEAR-MM-DD HH:MM:SS' format. * DB_BLOB * DB_BITMAP Supported validation methods: * IPV4 - represents an IPv4 address * URI - represents a SIP URI * URI_IPV4HOST - represents a SIP URI with an IPV4 as a host * P_HOST_PORT - represents [proto:]host[:port] * P_IPV4_PORT - represents [proto:]IPv4[:port] 1.3.3. Command definition block Multiple provisioning commands can be grouped together. Each group can have multiple commands. Each command definition in a group must have the table id of the table that is operating on along with the command type to be performed. The command type can have up to three type of column parameters: * clause columns * query columns * order by columns Each column parameter must define the name(s) of the column(s) (must match a field name in the description table identified by the table id). A column can accept a list of imposed values. Each imposed value will have an id that will be displayed on the web interface and the actual value that will be used for db operations. Clause columns must define operators. Here's the list of supported operators: '<', '>', '=', '<=', '>=', '!='. Supported database command types: * DB_QUERY - performs an SQL query and supports three type of columns: + clause: 0 or more columns + query: 1 column + order: 0 or 1 column * DB_INSERT - performs an SQL insert and supports one type of column: + query: 1 or more columns * DB_DELETE - performs an SQL delete and supports one type of column: + clause: 1 or more columns * DB_UPDATE - performs an SQL update and supports two type of columns: + clause: 0 or more columns + query: 1 or more columns * DB_REPLACE - performs an SQL replace and supports one type of column: + query: 1 or more columns Please note that some databases have a restricted set of database command types. 1.4. To-do Features to be added in the future: * full subscriber provisionning with automatic ha1/ha1b fields. 1.5. Dependencies 1.5.1. OpenSIPS Modules The following modules must be loaded before this module: * httpd module. 1.6. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml2 1.7. Exported Parameters 1.7.1. pi_http_root(string) Specifies the root path for pi HTTP requests. The link to the OpenSIPS provisioning web interface must be constructed using the following patern: http://[opensips_IP]:[opensips_mi_port]/[pi_http_root] The default value is "pi". Example 1.1. Set pi_http_root parameter ... modparam("pi_http", "pi_http_root", "opensips_pi") ... 1.7.2. framework(string) Specifies the full path for xml framework descriptor. There's no default value. This parameter is mandatory. Example 1.2. Set framework parameter ... modparam("pi_http", "framework", "/usr/local/etc/opensips/pi_framework.x ml") ... 1.7.3. pi_http_method(integrer) Specifies the HTTP request method to be used: * 0 - use GET HTTP request * 1 - use POST HTTP request The default value is 0. Example 1.3. Set pi_http_method parameter ... modparam("pi_http", "pi_http_method", 1) ... 1.8. Exported MI Functions 1.8.1. pi_reload_tbls_and_cmds Reloads the layout of the provisioning interface from the framework file. Name: pi_reload_tbls_and_cmds Parameters: none MI FIFO Command Format: :pi_reload_tbls_and_cmds:_reply_fifo_file_ _empty_line_ opensips-2.2.2/modules/pi_http/doc/000077500000000000000000000000001300170765700172475ustar00rootroot00000000000000opensips-2.2.2/modules/pi_http/doc/pi_http.xml000066400000000000000000000020731300170765700214420ustar00rootroot00000000000000 %docentities; ]> pi_http Module &osipsname; Ovidiu Sas osas@voipembedded.com Ovidiu Sas 2012-2013 VoIP Embedded, Inc. $Rev: 8688 $ $Date$ &admin; &faq; opensips-2.2.2/modules/pi_http/doc/pi_http_admin.xml000066400000000000000000000237461300170765700226240ustar00rootroot00000000000000 &adminguide;
Overview This module provides an HTTP provisioning interface for &osips;. It is using the &osips;'s internal database API to provide a simple way of manipulating records inside &osips;'s tables. The module offers: ability to connect to multiple/different databases through &osips;'s db API; (all &osips;'s databases are supported); ability to perform data input validation through &osips; API; ability to reconfigure the interface layout on the fly by reloading the config from the xml framework via mi command interface. Note: when provisioning tables using db_text, any change made to a db_text table will not be reflected on the actual text file. In order to force a write out to the disk of the cached tables, the db_text mi command dbt_dump must be used.
Usage The layout of the provisionning interface is controlled via an external xml file (see the framework parameter). An example of a framework xml file is provided inside the examples directory of the pi_http module. A simple framework file can be generated by the opensipsdbctl command: opensipsdbctl pframework create The generated framework will be saved inside &osips;'s config directory as pi_framework_sample. The list of configurable tables will be based on the STANDARD_MODULES and EXTRA_MODULES vars from opensipsctlrc file.
Framework The xml framework file is organized in three distinctive blocks: database connection definition block table definition block command definition block
Database connection definition block Each connection to a particular database must be defined here with a unique database connection id. The connection parameters are defined following the db_url param pattern for all &osips; modules that are using a database. Supported databases: berkeley flatstore http mysql oracle postgres text unixodbc virtual
Table definition block Each table managed through the &osips; provisioning interface must be defined here with a unique table id. For each table, the database connection id must be specified. Each table must list all columns that will be managed by the &osips; provisioning interface. Each column must have a unique field name and a type. Each column may have a validation tag for validating input data. Supported column types: DB_INT DB_BIGINT DB_DOUBLE DB_STRING DB_STR DB_DATETIME Note: input field must be provided in 'YEAR-MM-DD HH:MM:SS' format. DB_BLOB DB_BITMAP Supported validation methods: IPV4 - represents an IPv4 address URI - represents a SIP URI URI_IPV4HOST - represents a SIP URI with an IPV4 as a host P_HOST_PORT - represents [proto:]host[:port] P_IPV4_PORT - represents [proto:]IPv4[:port]
Command definition block Multiple provisioning commands can be grouped together. Each group can have multiple commands. Each command definition in a group must have the table id of the table that is operating on along with the command type to be performed. The command type can have up to three type of column parameters: clause columns query columns order by columns Each column parameter must define the name(s) of the column(s) (must match a field name in the description table identified by the table id). A column can accept a list of imposed values. Each imposed value will have an id that will be displayed on the web interface and the actual value that will be used for db operations. Clause columns must define operators. Here's the list of supported operators: '<', '>', '=', '<=', '>=', '!='. Supported database command types: DB_QUERY - performs an SQL query and supports three type of columns: clause: 0 or more columns query: 1 column order: 0 or 1 column DB_INSERT - performs an SQL insert and supports one type of column: query: 1 or more columns DB_DELETE - performs an SQL delete and supports one type of column: clause: 1 or more columns DB_UPDATE - performs an SQL update and supports two type of columns: clause: 0 or more columns query: 1 or more columns DB_REPLACE - performs an SQL replace and supports one type of column: query: 1 or more columns Please note that some databases have a restricted set of database command types.
To-do Features to be added in the future: full subscriber provisionning with automatic ha1/ha1b fields.
Dependencies
&osips; Modules The following modules must be loaded before this module: httpd module.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml2
Exported Parameters
<varname>pi_http_root</varname>(string) Specifies the root path for pi HTTP requests. The link to the &osips; provisioning web interface must be constructed using the following patern: http://[opensips_IP]:[opensips_mi_port]/[pi_http_root] The default value is "pi". Set <varname>pi_http_root</varname> parameter ... modparam("pi_http", "pi_http_root", "opensips_pi") ...
<varname>framework</varname>(string) Specifies the full path for xml framework descriptor. There's no default value. This parameter is mandatory. Set <varname>framework</varname> parameter ... modparam("pi_http", "framework", "/usr/local/etc/opensips/pi_framework.xml") ...
<varname>pi_http_method</varname>(integrer) Specifies the HTTP request method to be used: 0 - use GET HTTP request 1 - use POST HTTP request The default value is 0. Set <varname>pi_http_method</varname> parameter ... modparam("pi_http", "pi_http_method", 1) ...
Exported MI Functions
<varname>pi_reload_tbls_and_cmds</varname> Reloads the layout of the provisioning interface from the framework file. Name: pi_reload_tbls_and_cmds Parameters: none MI FIFO Command Format: :pi_reload_tbls_and_cmds:_reply_fifo_file_ _empty_line_
opensips-2.2.2/modules/pi_http/http_db_handler.c000066400000000000000000000052741300170765700217770ustar00rootroot00000000000000/* * http_db_handler module * * Copyright (C) 2012 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2012-03-19 initial version (Ovidiu Sas) */ #include #include #include "../../db/db.h" #include "http_fnc.h" int connect_http_db(ph_framework_t *framework_data, int index) { ph_db_url_t *ph_db_urls = framework_data->ph_db_urls; if (*ph_db_urls[index].http_db_handle) { LM_CRIT("BUG - db connection found already open\n"); return -1; } if ((*ph_db_urls[index].http_db_handle = ph_db_urls[index].http_dbf.init(&ph_db_urls[index].db_url)) == NULL) { return -1; } return 0; } int use_table(ph_db_table_t *db_table) { ph_db_url_t *db_url; if(db_table==NULL){ LM_ERR("null db_table handler\n"); return -1; } if(db_table->db_url==NULL){ LM_ERR("null db_url for table [%s]\n", db_table->name.s); return -1; } db_url = db_table->db_url; if(*db_url->http_db_handle==NULL){ LM_ERR("null db handle for table [%s]\n", db_table->name.s); return -1; } db_table->db_url->http_dbf.use_table(*db_table->db_url->http_db_handle, &db_table->name); return 0; } int init_http_db(ph_framework_t *framework_data, int index) { ph_db_url_t *ph_db_urls = framework_data->ph_db_urls; if (db_bind_mod(&ph_db_urls[index].db_url, &ph_db_urls[index].http_dbf) < 0) { LM_ERR("Unable to bind to a database driver\n"); return -1; } if (connect_http_db(framework_data, index)!=0){ LM_ERR("unable to connect to the database\n"); return -1; } ph_db_urls[index].http_dbf.close(*ph_db_urls[index].http_db_handle); *ph_db_urls[index].http_db_handle = NULL; return 0; } void destroy_http_db(ph_framework_t *framework_data) { int i; ph_db_url_t *ph_db_urls = framework_data->ph_db_urls; /* close the DB connections */ for(i=0;iph_db_urls_size;i++){ if (*ph_db_urls[i].http_db_handle) { ph_db_urls[i].http_dbf.close(*ph_db_urls[i].http_db_handle); *ph_db_urls[i].http_db_handle = NULL; } } } opensips-2.2.2/modules/pi_http/http_db_handler.h000066400000000000000000000022731300170765700220000ustar00rootroot00000000000000/* * http_db_handler module * * Copyright (C) 2012 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2012-03-19 initial version (Ovidiu Sas) */ #ifndef PI_HTTP_HTTP_DB_HANDLER #define PI_HTTP_HTTP_DB_HANDLER int init_http_db(ph_framework_t *framework_data, int index); int use_table(ph_db_table_t *db_table); int connect_http_db(ph_framework_t *framework_data, int index); void destroy_http_db(ph_framework_t *framework_data); #endif opensips-2.2.2/modules/pi_http/http_fnc.c000066400000000000000000002760001300170765700204600ustar00rootroot00000000000000/* * Copyright (C) 2011-2013 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2011-09-20 first version (osas) */ #include #include "../../str.h" #include "../../ut.h" #include "../../db/db_ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../config.h" #include "../../globals.h" #include "../../socket_info.h" #include "../../resolve.h" #include "../../parser/parse_uri.h" #include "../httpd/httpd_load.h" #include "http_fnc.h" #include "http_db_handler.h" #define PI_HTTP_XML_FRAMEWORK_NODE "framework" #define PI_HTTP_XML_DB_URL_NODE "db_url" #define PI_HTTP_XML_DB_TABLE_NODE "db_table" #define PI_HTTP_XML_TABLE_NAME_NODE "table_name" #define PI_HTTP_XML_DB_URL_ID_NODE "db_url_id" #define PI_HTTP_XML_MOD_NODE "mod" #define PI_HTTP_XML_MOD_NAME_NODE "mod_name" #define PI_HTTP_XML_CMD_NODE "cmd" #define PI_HTTP_XML_DB_TABLE_ID_NODE "db_table_id" #define PI_HTTP_XML_CMD_NAME_NODE "cmd_name" #define PI_HTTP_XML_CMD_TYPE_NODE "cmd_type" #define PI_HTTP_XML_CLAUSE_COLS_NODE "clause_cols" #define PI_HTTP_XML_QUERY_COLS_NODE "query_cols" #define PI_HTTP_XML_ORDER_BY_COLS_NODE "order_by_cols" #define PI_HTTP_XML_COLUMN_NODE "column" #define PI_HTTP_XML_COL_NODE "col" #define PI_HTTP_XML_FIELD_NODE "field" #define PI_HTTP_XML_LINK_CMD_NODE "link_cmd" #define PI_HTTP_XML_TYPE_NODE "type" #define PI_HTTP_XML_OPERATOR_NODE "operator" #define PI_HTTP_XML_VALUE_NODE "value" #define PI_HTTP_XML_VALIDATE_NODE "validate" #define PI_HTTP_XML_ID_ATTR "id" extern str http_root; extern int http_method; extern httpd_api_t httpd_api; ph_framework_t *ph_framework_data = NULL; ph_html_page_data_t html_page_data; #define PI_HTTP_DB_UNDEF 0 #define PI_HTTP_DB_QUERY 1 #define PI_HTTP_DB_INSERT 2 #define PI_HTTP_DB_DELETE 3 #define PI_HTTP_DB_UPDATE 4 #define PI_HTTP_DB_REPLACE 5 #define PI_HTTP_COPY(p,str) \ do{ \ if ((int)((p)-buf)+(str).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str).s, (str).len); (p) += (str).len; \ }while(0) #define PI_HTTP_COPY_2(p,str1,str2) \ do{ \ if ((int)((p)-buf)+(str1).len+(str2).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str1).s, (str1).len); (p) += (str1).len; \ memcpy((p), (str2).s, (str2).len); (p) += (str2).len; \ }while(0) #define PI_HTTP_COPY_3(p,str1,str2,str3) \ do{ \ if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str1).s, (str1).len); (p) += (str1).len; \ memcpy((p), (str2).s, (str2).len); (p) += (str2).len; \ memcpy((p), (str3).s, (str3).len); (p) += (str3).len; \ }while(0) #define PI_HTTP_COPY_4(p,str1,str2,str3,str4) \ do{ \ if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len+(str4).len>max_page_len) { \ goto error; \ } \ memcpy((p), (str1).s, (str1).len); (p) += (str1).len; \ memcpy((p), (str2).s, (str2).len); (p) += (str2).len; \ memcpy((p), (str3).s, (str3).len); (p) += (str3).len; \ memcpy((p), (str4).s, (str4).len); (p) += (str4).len; \ }while(0) #define PI_HTTP_COPY_5(p,s1,s2,s3,s4,s5) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ }while(0) #define PI_HTTP_COPY_6(p,s1,s2,s3,s4,s5,s6) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ }while(0) #define PI_HTTP_COPY_8(p,s1,s2,s3,s4,s5,s6,s7,s8) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ }while(0) #define PI_HTTP_COPY_9(p,s1,s2,s3,s4,s5,s6,s7,s8,s9) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \ }while(0) #define PI_HTTP_COPY_10(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \ memcpy((p), (s10).s, (s10).len); (p) += (s10).len; \ }while(0) #define PI_HTTP_COPY_11(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11) \ do{ \ if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len+(s11).len>max_page_len) { \ goto error; \ } \ memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \ memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \ memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \ memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \ memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \ memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \ memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \ memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \ memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \ memcpy((p), (s10).s, (s10).len); (p) += (s10).len; \ memcpy((p), (s11).s, (s11).len); (p) += (s11).len; \ }while(0) #define PI_HTTP_COMPLETE_REPLY(page,buffer,mod,cmd,fmt,args...) \ do{ \ _len = snprintf((page)->s + (page)->len, \ (buffer)->len - (page)->len, \ fmt, ##args); \ if(_len<0) \ goto error; \ else \ (page)->len += _len; \ p = page->s + page->len; \ PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Cmd_td_4d); \ page->len = p - page->s; \ if(ph_build_reply_footer((page), (buffer)->len)<0) \ goto error; \ }while(0) #define PI_HTTP_BUILD_REPLY(page,buffer,mod,cmd,fmt,args...) \ do{ \ if(ph_build_reply((page),(buffer)->len,(mod),(cmd))<0) \ goto error; \ _len = snprintf((page)->s + (page)->len, \ (buffer)->len - (page)->len, \ fmt, ##args); \ if(_len<0) \ goto error; \ else \ (page)->len += _len; \ p = page->s + page->len; \ PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Cmd_td_4d); \ page->len = p - page->s; \ if(ph_build_reply_footer((page), (buffer)->len)<0) \ goto error; \ }while(0) /* */ #define PI_HTTP_ESC_COPY(p,str,temp_holder,temp_counter) \ do{ \ (temp_holder).s = (str).s; \ (temp_holder).len = 0; \ for((temp_counter)=0;(temp_counter)<(str).len;(temp_counter)++) { \ switch((str).s[(temp_counter)]) { \ case '<': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ PI_HTTP_COPY_2(p, (temp_holder), PI_HTTP_ESC_LT); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '>': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ PI_HTTP_COPY_2(p, (temp_holder), PI_HTTP_ESC_GT); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '&': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ PI_HTTP_COPY_2(p, (temp_holder), PI_HTTP_ESC_AMP); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '"': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ PI_HTTP_COPY_2(p, (temp_holder), PI_HTTP_ESC_QUOT); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ case '\'': \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ PI_HTTP_COPY_2(p, (temp_holder), PI_HTTP_ESC_SQUOT); \ (temp_holder).s = (str).s + (temp_counter) + 1; \ (temp_holder).len = (temp_counter) + 1; \ break; \ } \ } \ (temp_holder).len = (temp_counter) - (temp_holder).len; \ PI_HTTP_COPY(p, (temp_holder)); \ }while(0) static const str PI_HTTP_METHOD[] = { str_init("GET"), str_init("POST") }; static const str PI_HTTP_Response_Head_1 = str_init(""\ "OpenSIPS Provisionning Interface"\ ""\ ""\ ""); static const str PI_HTTP_Response_Head_2 = str_init(\ ""\ "\n"\ ""); static const str PI_HTTP_Response_Title_Table_1 = str_init(\ ""\ ""\ "
"\ "

OpenSIPS Provisionning Interface

"); static const str PI_HTTP_Response_Title_Table_3 = str_init("
\n
\n"); static const str PI_HTTP_Response_Menu_Table_1 = str_init("\n"); static const str PI_HTTP_Response_Menu_Table_2 = str_init("
"); static const str PI_HTTP_Response_Menu_Table_4 = str_init("\n"); static const str PI_HTTP_Response_Menu_Table_4b = str_init("\n"); static const str PI_HTTP_Response_Menu_Table_5 = str_init("
\n"); static const str PI_HTTP_Response_Menu_Cmd_Table_1a = str_init("\n"); static const str PI_HTTP_Response_Menu_Cmd_Table_1b = str_init("
\n"); static const str PI_HTTP_Response_Menu_Cmd_tr_1 = str_init("\n"); static const str PI_HTTP_Response_Menu_Cmd_td_1a = str_init(" idupdatesdomainprefixdomainadd pdt DB_INSERT sdomainprefixdomainupdate pdt DB_UPDATE id= sdomainprefixdomaindelete pdt DB_DELETE id= opensips-2.2.2/obsolete_modules/pdt/scripts/pi_http/pdt-table000066400000000000000000000005571300170765700244620ustar00rootroot00000000000000 pdt mysql idDB_INT sdomainDB_STR prefixDB_STR domainDB_STR opensips-2.2.2/obsolete_modules/pdt/scripts/postgres/000077500000000000000000000000001300170765700230535ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/pdt/scripts/postgres/pdt-create.sql000066400000000000000000000005441300170765700256270ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('pdt','2'); CREATE TABLE pdt ( id SERIAL PRIMARY KEY NOT NULL, sdomain VARCHAR(128) NOT NULL, prefix VARCHAR(32) NOT NULL, domain VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT pdt_sdomain_prefix_idx UNIQUE (sdomain, prefix) ); ALTER SEQUENCE pdt_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/opensips.8000066400000000000000000000102321300170765700153120ustar00rootroot00000000000000.\" $Id$ .\" Process with .\" groff -man -Tascii opensips.8 .\" .TH "OpenSIPS" "8" "08.06.2006" "OpenSIPS" "VoIP SIP Server" .SH "NAME" opensips \- flexible and roboust SIP (RFC3261, RFC3263) server for Voice over IP (VoIP) .SH "SYNOPSIS" .B opensips [ .B \-hcCrRvdDEVT ] [ .BI \-f " config\-file" ] [ .BI \-l " address" ] [ .BI \-n " processes\-no" ] [ .BI \-N " tcp processes\-no" ] [ .BI \-b " max_rcv_buf_size" ] [ .BI \-m " shared_mem_size" ] [ .BI \-w " working\-dir" ] [ .BI \-W " method" ] [ .BI \-t " chroot\-dir" ] [ .BI \-u " uid" ] [ .BI \-g " gid" ] [ .BI \-P " pid\-file" ] [ .BI \-i " fifo\-path" ] [ .BI \-x " socket\-path" ] .SH "DESCRIPTION" .B OpenSIPS .BR is a SIP\-based VoIP server running on most Unix\-like operating systems. It supports UDP, TCP and TLS transport layers. Among its features: .IP * SIP Proxy Server, SIP Registrar Server, SIP Redirect Server, SIP Location Server, SIP Application Server .IP * Call Processing Language (RFC3880) .IP * ENUM support .IP * server side NAT traversal .IP * load balancing .IP * AAA with database (MySQL, Postgres, Unixodbc) or RADIUS backend .IP * audio, video, instant messaging and pressence .SH "OPTIONS" .TP 12 .B \-h Displays a short usage description, including all available options. .TP .BI \-c Checks the config file and displays the aliases and listen interface list. .TP .BI \-C Similar to \-c but in addition checks the flags of exported functions from included route blocks. .TP .BI \-r Uses dns to check if it is necessary to add a "received=" field to a via. .TP .BI \-R Same as .B \-r but uses reverse dns. .TP .BI \-v Turns on via host checking when forwarding replies. .TP .BI \-d Turns on debugging, multiple .B \-d increase the debug level. .TP .BI \-D Runs opensips in the foreground (it doesn't fork into daemon mode). .TP .BI \-E Sends all the log messages to stderr. .TP .BI \-T Disables TCP support. .TP .BI \-V Displays the version number. .TP .BI \-f " config\-file" Reads the configuration from .B " config\-file" (default .I /etc/opensips/opensips.cfg ). .TP .BI \-l " address" Listens on the specified address/interface. Multiple .B \-l mean listening on multiple addresses. The address format is [proto:]address[:port], where proto = udp|tcp and address = host|ip_address|interface_name. Example: \-l localhost, \-l udp:127.0.0.1:5080, \-l eth0:5062. The default behaviour is to listen on all the ipv4 interfaces. .TP .BI \-n " processes\-no" Specifies the number of children processes forked per interface (default 8). .TP .BI \-N " tcp processes\-no" Specifies the number of children processes forked to handle tcp incoming connections (by default is equal to .BI \-n ). .TP .BI \-b " max_rcv_buf_size" Maximum receive buffer size which will not be exceeded by the auto\-probing procedure even if the OS allows. .TP .BI \-m " shared_mem_size" Size of the shared memory which will be allocated (in Megabytes). .TP .BI \-w " working\-dir" Specifies the working directory. In the very improbable event that .B opensips will crash, the core file will be generated here. .TP .BI \-W " method" Specify poll method. .TP .BI \-t " chroot\-dir" Forces .B opensips to chroot after reading the config file. .TP .BI \-u " uid" Changes the user id under which .B opensips runs. .TP .BI \-g " gid" Changes the group id under which .B opensips runs. .TP .BI \-P " pid\-file" Creates a file containing the pid of the main .B opensips process. .TP .BI \-i " fifo\-path" Creates a fifo, useful for monitoring .B opensips status. .TP .BI \-x " socket\-path" Creates a unix socket, useful for monitoring .B opensips status ( same as .BI \-i " fifo\-path" but using instead unix sockets). .SH "FILES" .PD 0 .B /usr/sbin/opensips .br .B /etc/opensips/opensips.cfg .br .B /usr/lib/opensips/modules/* .PD .SH "AUTHORS" see .B /usr/share/doc/opensips/AUTHORS .SH "SEE ALSO" .BR opensips.cfg(5) .PP Full documentation on opensips is available at .I http://www.opensips.org/. .PP Project tracker and GIT .I https://github.com/OpenSIPS/opensips. .PP Mailing lists: .nf users@lists.opensips.org \- opensips user community .nf devel@lists.opensips.org \- opensips development, new features and unstable version opensips-2.2.2/opensips.cfg.5000066400000000000000000000021161300170765700160470ustar00rootroot00000000000000.\" $Id$ .TH opensips.cfg 5 08.06.2006 opensips "Open SIP Server" .\" Process with .\" groff -man -Tascii opensips.cfg.5 .\" .SH NAME opensips.cfg \- opensips configuration file .SH SYNOPSIS .I /etc/opensips/opensips.cfg .SH DESCRIPTION .B opensips reads the configuration data from .I /etc/opensips/opensips.cfg (or the file specified with .B \-f on the command line). The file contains global configuration parameters, module loading commands and the script that will be executed for each received request. Lines starting with .B # or enclosed in .B /* ... */ are interpreted as comments. .PP This manual page is incomplete. For further information please read the .I opensips documentation (http://www.opensips.org/). .SH FILES .PD 0 .I /etc/opensips/opensips.cfg .br .SH AUTHORS see .B /usr/share/doc/opensips/AUTHORS .SH SEE ALSO .BR opensips(8) .PP Full documentation on opensips is available at .I http://www.opensips.org/. .PP Mailing lists: .nf users@lists.opensips.org - opensips user community .nf devel@lists.opensips.org - opensips development, new features and unstable version opensips-2.2.2/opensipsdbctl.8000066400000000000000000000047771300170765700163440ustar00rootroot00000000000000.\" $Id: opensipsdbctl.8 5891 2009-11-22 12:53:09Z alerios $ .TH opensipsdbctl 8 22.11.2009 opensips "OpenSIPS" .\" Process with .\" groff -man -Tascii opensipsdbctl.8 .\" .SH NAME opensipsdbctl \- control tool for maintaining OpenSIPS databases .SH SYNOPSIS .B opensipsdbctl .B opensipsdbctl .BI command [ .BI parameters ] .SH DESCRIPTION .B opensipsdbctl is a control tool for maintaining .B OpenSIPS SIP server databases. It can be used to create, migrate, copy, delete, dump and restore the persistence databases for OpenSIPS, on the supported Engines. .SH FILES .PD 0 .I /etc/opensips/opensipsdbctlrc .br .I ~/.opensipsdbctlrc .br .SH COMMANDS .TP 16 .I OpenSIPS databases management: .TP 12 .B db create creates a new database .TP .B db presence adds the presence related tables .TP .B db extra adds the extra tables .TP .B db migrate migrates DB from 1.5 to 1.6 .TP .B db drop !entirely deletes tables! .TP .B db reinit !entirely deletes and than re-creates tables! .TP .B db backup dumps current database to file .TP .B db restore restores tables from a file .TP .B db copy creates a new db from an existing one .TP 16 .I OpenSIPS Berkeley DB tables management: .TP 12 .B bdb | db_berkeley list lists the underlying db files in DB_PATH .TP .B bdb | db_berkeley cat db_dump the underlying db file to STDOUT .TP .B bdb | db_berkeley swap installs db.new by db -> db.old; db.new -> db .TP .B bdb | db_berkeley append appends data to an existing db;output DB_PATH/db.new .TP .B bdb | db_berkeley newappend appends data to a new instance of db; output DB_PATH/db.new .TP .B bdb | db_berkeley export exports table data to plain-txt files in dump_dir .TP .B bdb | db_berkeley import imports plain-txt table data and creates new db tables in db_path .SH AUTHORS see .B /usr/share/doc/opensips/AUTHORS .PP This manual page was written by Alejandro Rios P. , based on opensipsctl manpage by Bogdan-Andrei Iancu , for the Debian project (and may be used by others). .SH SEE ALSO .BR opensips(8), opensips.cfg(5), opensipsctl(8), osipsconsole(8) .PP Full documentation on opensips is available at .I http://www.opensips.org/. .PP Mailing lists: .nf users@lists.opensips.org - opensips user community .nf devel@lists.opensips.org - opensips development, new features and unstable version opensips-2.2.2/osipsconfig.8000066400000000000000000000027501300170765700160030ustar00rootroot00000000000000.TH osipsconfig 8 18.08.2015 opensips-menuconfig "OpenSIPS" .\" Process with .\" groff -man -Tascii osipsconfig.8 .\" .SH NAME osipsconfig \- utility for configuring OpenSIPS .SH SYNOPSIS .B osipsconfig .B osipsconfig [ .BI --local ] .SH DESCRIPTION .B osipsconfig is a ncurses utility for configuring .B OpenSIPS SIP server. It is a graphical tool that allows in an easy way to configure, compile and generate OpenSIPS configuration files - all by simply running make menuconfig. .SH FILES .PD 0 .I /etc/menuconfig/configs/opensips_loadbalancer_def.m4 .br .I /etc/menuconfig/configs/opensips_loadbalancer.m4 .br .I /etc/menuconfig/configs/opensips_residential_def.m4 .br .I /etc/menuconfig/configs/opensips_residential.m4 .br .I /etc/menuconfig/configs/opensips_trunking_def.m4 .br .I /etc/menuconfig/configs/opensips_trunking.m4 .br .SH COMMANDS .TP 12 .B --local Indicates that menuconfig should be run in the sources' root. .SH AUTHORS see .B /usr/share/doc/opensips/AUTHORS .PP This manual page was written by Razvan Crainea based on osipsconsole manpage by Alejandro Rios P. , for the Debian project (and may be used by others). .SH SEE ALSO .BR opensips(8), opensips.cfg(5), opensipsctl(8), osipsconsole(8) .PP Full documentation on opensips is available at .I http://www.opensips.org/. .PP Mailing lists: .nf users@lists.opensips.org - opensips user community .nf devel@lists.opensips.org - opensips development, new features and unstable version opensips-2.2.2/osipsconsole.8000066400000000000000000000217051300170765700162010ustar00rootroot00000000000000.\" $Id: osipsconsole.8 5891 2009-11-22 12:53:09Z alerios $ .TH osipsconsole 8 22.11.2009 opensips-console "OpenSIPS" .\" Process with .\" groff -man -Tascii osipsconsole.8 .\" .SH NAME osipsconsole \- utility for provisioning OpenSIPS .SH SYNOPSIS .B osipsconsole .B osipsconsole .BI command [ .BI parameters ] .SH DESCRIPTION .B osipsconsole is a console like utility for provisioning .B OpenSIPS SIP server. It can be used to manage users, domains, aliases and other server options. If command arguments are passed it will work in non-interactive mode, and behave as old opensipsctl and opensipsdbctl tools. If no arguments are passed, it behaves as a console - you will get a prompt, and can run the available commands, with embedded help and command history -. .SH FILES .PD 0 .I /etc/opensips/osipsconsolerc .br .I ~/.osipsconsolerc .br .SH COMMANDS .TP 12 .B help Displays a short usage description, including all available options. .TP 16 .I Command 'online' - dump online users from memory .TP 12 .B online display online users .TP 16 .I Command 'monitor' - show internal status .TP 12 .B monitor show server's internal status .TP 16 .I Command 'ping' - ping a SIP URI (OPTIONS) .TP 12 .B ping ping with SIP OPTIONS .TP 16 .I Command 'ul|alias' - manage user location or aliases .TP 12 .B ul show [] show in-RAM online users .TP .B ul show --brief show in-RAM online users in short format .TP .B ul rm [] delete user's usrloc entries .TP .B ul add introduce a permanent usrloc entry .TP .B ul add introduce a temporary usrloc entry .TP 16 .I Command 'start|stop|restart' .TP 12 .B restart restart OpenSIPS .TP .B start start OpenSIPS .TP .B stop stop OpenSIPS .TP 16 .I Command 'tls' .TP 12 .B tls rootCA [] creates new rootCA .TP .B tls userCERT [] creates user certificate default is /usr/local/etc/opensips/tls .TP 16 .I Command 'acl' - manage access control lists (acl) .TP 12 .B acl show [] show user membership .TP .B acl grant grant user membership (*) .TP .B acl revoke [] revoke user membership(s) (*) .TP 16 .I Command 'cr' - manage carrierroute tables .TP 12 .B cr show show tables .TP .B cr reload reload tables .TP .B cr dump show in memory tables .TP .B cr addrt add a tree .TP .B cr rmrt rm a tree .TP cr addcarrier [] [] [] [] [] [] [] add a carrier .TP .B cr rmcarrier rm a carrier .TP 16 .I Command 'rpid' - manage Remote-Party-ID (RPID) .TP 12 .B rpid add add rpid for a user (*) .TP .B rpid rm set rpid to NULL for a user (*) .TP .B rpid show show rpid of a user .TP 16 .I Command 'add|passwd|rm' - manage subscribers .TP 12 .B add add a new subscriber (*) .TP .B passwd change user's password (*) .TP .B rm delete a user (*) .TP 16 .I Command 'add|dump|reload|rm|show' - manage address .TP 12 .B address show show db content .TP .B address dump show cache content .TP .B address reload reload db table into cache .TP .B address add [] [] add a new entry .B address rm remove all entries for the given grp ip mask and port .TP 16 .I Command 'dispatcher' - manage dispatcher .TP 12 .B dispatcher show show dispatcher gateways .TP .B dispatcher reload reload dispatcher gateways .TP .B dispatcher dump show in memory dispatcher gateways .TP .B dispatcher addgw add gateway .TP .B dispatcher rmgw delete gateway .TP 16 .I Command 'db' - database operations .TP 12 .B db exec execute SQL query .TP .B db roexec execute read-only SQL query .TP .B db run execute SQL query from $id variable .TP .B db rorun execute read-only SQL query from $id variable .TP .B db show
"); static const str PI_HTTP_NODE_INDENT = str_init("\t"); static const str PI_HTTP_NODE_SEPARATOR = str_init(":: "); static const str PI_HTTP_ATTR_SEPARATOR = str_init(" "); static const str PI_HTTP_ATTR_VAL_SEPARATOR = str_init("="); static const str PI_HTTP_BREAK = str_init("
"); static const str PI_HTTP_CODE_1 = str_init("
");
static const str PI_HTTP_CODE_2 = str_init("
"); static const str PI_HTTP_Post_Form_1a = str_init("\n"\ "
\n" " \n"); static const str PI_HTTP_Post_Input = str_init(\ " "); static const str PI_HTTP_Post_Clause_Input = str_init("
Clause:"); static const str PI_HTTP_Post_Values_Input = str_init("
Values:"); static const str PI_HTTP_Post_Query_Input = str_init("\n"); static const str PI_HTTP_Post_Input_1 = str_init(\ " \n"); static const str PI_HTTP_Post_Input_Hidden_1 = str_init(\ " \n"); static const str PI_HTTP_Post_Input_3 = str_init("\"/>\n"); static const str PI_HTTP_Post_Input_4 = str_init(\ "
"); static const str PI_HTTP_Post_Input_Text = str_init(\ "
\n"); static const str PI_HTTP_Post_Form_2 = str_init(\ "
\n"\ "
\n"); static const str PI_HTTP_Response_Foot = str_init(\ "\n\n
"\ ""\ "OpenSIPS web site
"\ "Copyright © 2012-2015 VoIP Embedded, Inc."\ ". All rights reserved."\ "
"); #define PI_HTTP_ROWSPAN 20 static const str PI_HTTP_CMD_ROWSPAN = str_init("20"); static const str PI_HTTP_ESC_LT = str_init("<"); /* < */ static const str PI_HTTP_ESC_GT = str_init(">"); /* > */ static const str PI_HTTP_ESC_AMP = str_init("&"); /* & */ static const str PI_HTTP_ESC_QUOT = str_init("""); /* " */ static const str PI_HTTP_ESC_SQUOT = str_init("'"); /* ' */ static const str PI_HTTP_HREF_1 = str_init("=url_len) return 0; for(i=index;iph_modules_size && (mod_len!=ph_modules[i].module.len || strncmp(&url[index], ph_modules[i].module.s,mod_len)!=0);i++); if (i==ph_framework_data->ph_modules_size) { LM_ERR("Invalid mod [%.*s] in url [%s]\n", mod_len, &url[index], url); return -1; } *mod = i; LM_DBG("got mod [%d][%.*s]\n", *mod, mod_len, &url[index]); index += mod_len; LM_DBG("index=%d url_len=%d\n", index, url_len); if (index>=url_len) return 0; /* skip over '/' */ index++; /* Looking for "cmd" */ if (index>=url_len) return 0; for(i=index;i=url_len) return 0; /* skip over '/' */ index++; if (url_len - index>0) { LM_DBG("got extra [%s]\n", &url[index]); } return 0; } int ph_build_form_imput(char **p, char *buf, str *page, int max_page_len, int mod, int cmd, str *clause, db_val_t *values) { unsigned long i, j; char c; str op, arg; str val_str; str temp_holder; int temp_counter; ph_cmd_t *command; ph_mod_t *ph_modules; ph_modules = ph_framework_data->ph_modules; command = &ph_modules[mod].cmds[cmd]; if(command->c_keys_size && (command->type==DB_CAP_QUERY || command->type==DB_CAP_DELETE || command->type==DB_CAP_UPDATE)){ PI_HTTP_COPY_3(*p,PI_HTTP_Post_Input, PI_HTTP_Post_Clause_Input, PI_HTTP_Post_Query_Input); for(i=0;ic_keys_size;i++){ /* FIXME: we should escape c_ops */ op.s = (char*)command->c_ops[i]; op.len = strlen(op.s); arg.s = int2str(i, &arg.len); switch(command->c_vals[i].vals_size){ case 0: PI_HTTP_COPY(*p, PI_HTTP_Post_Input_1); PI_HTTP_COPY(*p, *command->c_keys[i]); PI_HTTP_COPY(*p, PI_HTTP_ATTR_SEPARATOR); PI_HTTP_COPY(*p, op); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Text); PI_HTTP_COPY(*p, arg); if (i==0 && clause) { PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Hidden_2); PI_HTTP_COPY(*p, *clause); } PI_HTTP_COPY(*p, PI_HTTP_Post_Input_3); break; case 1: PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Hidden_1); PI_HTTP_COPY(*p, arg); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Hidden_2); PI_HTTP_COPY(*p, command->c_vals[i].vals[0]); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Hidden_3); break; default: PI_HTTP_COPY(*p, PI_HTTP_Post_Input_1); PI_HTTP_COPY(*p, *command->c_keys[i]); PI_HTTP_COPY(*p, PI_HTTP_ATTR_SEPARATOR); PI_HTTP_COPY(*p, op); LM_DBG("Here we need to enforce select\n"); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Select_1); PI_HTTP_COPY(*p, arg); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Select_2); for(j=0;jc_vals[i].vals_size;j++){ PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Option_1); PI_HTTP_COPY(*p, command->c_vals[i].vals[j]); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Option_2); PI_HTTP_COPY(*p, command->c_vals[i].ids[j]); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Option_3); } PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Select_3); } } PI_HTTP_COPY(*p, PI_HTTP_Post_Input_4); } if(command->q_keys_size && (command->type==DB_CAP_INSERT || command->type==DB_CAP_UPDATE || command->type==DB_CAP_REPLACE)){ PI_HTTP_COPY_3(*p,PI_HTTP_Post_Input, PI_HTTP_Post_Values_Input, PI_HTTP_Post_Query_Input); arg.s = &c; arg.len = 1; for(i=0,c='a';iq_keys_size;i++,c++){ if(c=='z'){ LM_ERR("To many q_keys\n"); return -1; } switch(command->q_vals[i].vals_size){ case 0: PI_HTTP_COPY(*p, PI_HTTP_Post_Input_1); PI_HTTP_COPY(*p, *command->q_keys[i]); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Text); PI_HTTP_COPY(*p, arg); if (values) { PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Hidden_2); switch(command->q_types[i]){ case DB_STR: case DB_STRING: case DB_BLOB: if(values[i].val.str_val.s==NULL){ val_str.s = NULL; val_str.len = 0; } else { val_str.s = values[i].val.str_val.s; val_str.len = strlen(val_str.s); } LM_DBG("...got %.*s[0]=>" "[%.*s][%.*s]\n", command->q_keys[i]->len, command->q_keys[i]->s, values[i].val.str_val.len, values[i].val.str_val.s, val_str.len, val_str.s); if (val_str.len) { PI_HTTP_ESC_COPY(*p, val_str, temp_holder, temp_counter); } break; case DB_INT: val_str.s = *p; val_str.len = max_page_len - page->len; if(db_int2str(values[i].val.int_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert int [%d]\n", values[i].val.int_val); goto error; } *p += val_str.len; page->len += val_str.len; LM_DBG(" got %.*s[0]=>" "[%d][%.*s]\n", command->q_keys[i]->len, command->q_keys[i]->s, values[i].val.int_val, val_str.len, val_str.s); break; case DB_BITMAP: val_str.s = *p; val_str.len = max_page_len - page->len; if(db_int2str(values[i].val.bitmap_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert bitmap [%d]\n", values[i].val.bitmap_val); goto error; } *p += val_str.len; page->len += val_str.len; LM_DBG(" got %.*s[0]=>" "[%d][%.*s]\n", command->q_keys[i]->len, command->q_keys[i]->s, values[i].val.bitmap_val, val_str.len, val_str.s); break; case DB_BIGINT: val_str.s = *p; val_str.len = max_page_len - page->len; if(db_bigint2str(values[i].val.bigint_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert bigint [%-lld]\n", values[i].val.bigint_val); goto error; } *p += val_str.len; page->len += val_str.len; LM_DBG(" got %.*s[0]=>" "[%-lld][%.*s]\n", command->q_keys[i]->len, command->q_keys[i]->s, values[i].val.bigint_val, val_str.len, val_str.s); break; case DB_DOUBLE: val_str.s = *p; val_str.len = max_page_len - page->len; if(db_double2str(values[i].val.double_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert double [%-10.2f]\n", values[i].val.double_val); goto error; } *p += val_str.len; page->len += val_str.len; LM_DBG(" got %.*s[0]=>" "[%-10.2f][%.*s]\n", command->q_keys[i]->len, command->q_keys[i]->s, values[i].val.double_val, val_str.len, val_str.s); break; case DB_DATETIME: val_str.s = *p; val_str.len = max_page_len - page->len; if (db_time2str_nq(values[i].val.time_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert time [%ld]\n", (unsigned long int)values[i].val.time_val); goto error; } *p += val_str.len; page->len += val_str.len; LM_DBG(" got %.*s[0]=>" "[%ld][%.*s]\n", command->q_keys[i]->len, command->q_keys[i]->s, (unsigned long int)values[i].val.time_val, val_str.len, val_str.s); break; default: LM_ERR("unexpected type [%d] " "for [%.*s]\n", command->q_types[i], command->q_keys[i]->len, command->q_keys[i]->s); } } PI_HTTP_COPY(*p, PI_HTTP_Post_Input_3); break; case 1: PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Hidden_1); PI_HTTP_COPY(*p, arg); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Hidden_2); PI_HTTP_COPY(*p, command->q_vals[i].vals[0]); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Hidden_3); break; default: PI_HTTP_COPY(*p, PI_HTTP_Post_Input_1); PI_HTTP_COPY(*p, *command->q_keys[i]); LM_DBG("Here we need to enforce select\n"); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Select_1); PI_HTTP_COPY(*p, arg); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Select_2); for(j=0;jq_vals[i].vals_size;j++){ PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Option_1); PI_HTTP_COPY(*p, command->q_vals[i].vals[j]); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Option_2); PI_HTTP_COPY(*p, command->q_vals[i].ids[j]); PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Option_3); } PI_HTTP_COPY(*p, PI_HTTP_Post_Input_Select_3); } } PI_HTTP_COPY(*p, PI_HTTP_Post_Input_4); } return 0; error: LM_ERR("buffer 2 small: *p=[%p] buf=[%p] max_page_len=[%d]\n", *p, buf, max_page_len); return -1; } int ph_build_header(str *page, int max_page_len, int mod, int cmd) { int i; char *p, *buf; ph_mod_t *ph_modules; ph_modules = ph_framework_data->ph_modules; if (page->s == NULL) { LM_ERR("Please provide a valid page\n"); return -1; } p = buf = page->s; PI_HTTP_COPY_4(p,PI_HTTP_Response_Head_1, PI_HTTP_Response_Head_2, PI_HTTP_Response_Title_Table_1, PI_HTTP_Response_Title_Table_3); /* Building module menu */ PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Table_1); for(i=0;iph_modules_size;i++) { if(i!=mod) { PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Table_2); } else { PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Table_2b); } PI_HTTP_COPY(p,PI_HTTP_SLASH); if (http_root.len) { PI_HTTP_COPY_2(p,http_root,PI_HTTP_SLASH); } PI_HTTP_COPY_3(p,ph_modules[i].module, PI_HTTP_Response_Menu_Table_3, ph_modules[i].module); if(i!=mod) { PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Table_4); } else { PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Table_4b); } } PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Table_5); page->len = p - page->s; return 0; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } int ph_build_reply(str *page, int max_page_len, int mod, int cmd) { char *p, *buf; ph_mod_t *ph_modules; ph_modules = ph_framework_data->ph_modules; buf = page->s; p = page->s + page->len; /* Print comand name */ PI_HTTP_COPY_4(p,PI_HTTP_Response_Menu_Cmd_Table_1b, PI_HTTP_Response_Menu_Cmd_tr_1, PI_HTTP_Response_Menu_Cmd_td_1a, PI_HTTP_SLASH); if (http_root.len) { PI_HTTP_COPY_2(p,http_root, PI_HTTP_SLASH); } PI_HTTP_COPY_6(p,ph_modules[mod].module, PI_HTTP_SLASH, ph_modules[mod].cmds[cmd].name, PI_HTTP_SQUOT_GT, ph_modules[mod].cmds[cmd].name, PI_HTTP_Response_Menu_Cmd_td_4a); /* Print cmd name */ PI_HTTP_COPY_9(p,PI_HTTP_Response_Menu_Cmd_td_1e, ph_modules[mod].cmds[cmd].name, PI_HTTP_Response_Menu_Cmd_td_4d, PI_HTTP_Response_Menu_Cmd_tr_2, PI_HTTP_Response_Menu_Cmd_tr_1, PI_HTTP_Response_Menu_Cmd_td_1f, PI_HTTP_NBSP, PI_HTTP_Response_Menu_Cmd_td_4d, PI_HTTP_Response_Menu_Cmd_td_1d); page->len = p - page->s; return 0; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } int ph_build_reply_footer(str *page, int max_page_len) { char *p, *buf; /* Here we print the footer */ buf = page->s; p = page->s + page->len; PI_HTTP_COPY_3(p,PI_HTTP_Response_Menu_Cmd_tr_2, PI_HTTP_Response_Menu_Cmd_Table_2, PI_HTTP_Response_Foot); page->len = p - page->s; return 0; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } int ph_build_content(str *page, int max_page_len, int mod, int cmd, str *clause, db_val_t *values) { char *p, *buf; int j; ph_mod_t *ph_modules; ph_modules = ph_framework_data->ph_modules; buf = page->s; p = page->s + page->len; if (mod>=0) { /* Building command menu */ /* Build the list of comands for the selected module */ PI_HTTP_COPY_4(p,PI_HTTP_Response_Menu_Cmd_Table_1a, PI_HTTP_Response_Menu_Cmd_tr_1, PI_HTTP_Response_Menu_Cmd_td_1a, PI_HTTP_SLASH); if (http_root.len) { PI_HTTP_COPY_2(p,http_root,PI_HTTP_SLASH); } PI_HTTP_COPY_6(p,ph_modules[mod].module, PI_HTTP_SLASH, ph_modules[mod].cmds[0].name, PI_HTTP_SQUOT_GT, ph_modules[mod].cmds[0].name, PI_HTTP_Response_Menu_Cmd_td_4a); if (cmd>=0) { PI_HTTP_COPY_3(p,PI_HTTP_Response_Menu_Cmd_td_1b, ph_modules[mod].cmds[cmd].name, PI_HTTP_Response_Menu_Cmd_td_4b); } PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Cmd_tr_2); for(j=1;j=0){ if (j==1) { PI_HTTP_COPY_6(p, PI_HTTP_Response_Menu_Cmd_td_1c, PI_HTTP_CMD_ROWSPAN, PI_HTTP_Response_Menu_Cmd_td_3c, PI_HTTP_Post_Form_1a, PI_HTTP_METHOD[http_method], PI_HTTP_Post_Form_1b); if(ph_build_form_imput(&p, buf, page, max_page_len, mod, cmd, clause, values)!=0) return -1; PI_HTTP_COPY_2(p, PI_HTTP_Post_Form_2, PI_HTTP_Response_Menu_Cmd_td_4c); } else if (j>PI_HTTP_ROWSPAN) { PI_HTTP_COPY_3(p, PI_HTTP_Response_Menu_Cmd_td_1d, PI_HTTP_NBSP, PI_HTTP_Response_Menu_Cmd_td_4d); } } PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Cmd_tr_2); } if (cmd>=0){ if (j==1) { PI_HTTP_COPY_10(p,PI_HTTP_Response_Menu_Cmd_tr_1, PI_HTTP_Response_Menu_Cmd_td_1d, PI_HTTP_NBSP, PI_HTTP_Response_Menu_Cmd_td_4d, PI_HTTP_Response_Menu_Cmd_td_1c, PI_HTTP_CMD_ROWSPAN, PI_HTTP_Response_Menu_Cmd_td_3c, PI_HTTP_Post_Form_1a, PI_HTTP_METHOD[http_method], PI_HTTP_Post_Form_1b); if(ph_build_form_imput(&p, buf, page, max_page_len, mod, cmd, clause, values)!=0) return -1; PI_HTTP_COPY_3(p, PI_HTTP_Post_Form_2, PI_HTTP_Response_Menu_Cmd_td_4c, PI_HTTP_Response_Menu_Cmd_tr_2); j++; } for(;j<=PI_HTTP_ROWSPAN;j++) { PI_HTTP_COPY_5(p,PI_HTTP_Response_Menu_Cmd_tr_1, PI_HTTP_Response_Menu_Cmd_td_1d, PI_HTTP_NBSP, PI_HTTP_Response_Menu_Cmd_td_4d, PI_HTTP_Response_Menu_Cmd_tr_2); } } PI_HTTP_COPY_2(p,PI_HTTP_Response_Menu_Cmd_Table_2, PI_HTTP_Response_Foot); } else { PI_HTTP_COPY(p,PI_HTTP_Response_Foot); } page->len = p - page->s; return 0; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } int getVal(db_val_t *val, db_type_t val_type, db_key_t key, ph_db_table_t *table, str *arg, str *page, str *buffer, int mod, int cmd) { char *p = page->s + page->len; char *buf = page->s; int _len, i; int max_page_len = buffer->len; ph_val_flags flags; str host; int port, proto; struct sip_uri uri; char c; for(i=0;i<=table->cols_size;i++){ if(table->cols[i].type==val_type && table->cols[i].field.len==key->len && strncmp(table->cols[i].field.s,key->s,key->len)==0){ if(table->cols[i].validation==0) continue; flags = table->cols[i].validation; LM_DBG("[%.*s] has flags [%u]\n", key->len, key->s, flags); if(flags&PH_FLAG_P_HOST_PORT){ flags&= ~ PH_FLAG_P_HOST_PORT; if (parse_phostport(arg->s, arg->len, &host.s, &host.len, &port, &proto)!=0){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Invalid [proto:]host[:port] for" "%.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } LM_DBG("[%.*s]->[%d][%.*s][%d]\n", arg->len, arg->s, proto, host.len, host.s, port); continue; } LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags); if(flags&PH_FLAG_P_IPV4_PORT){ flags&= ~ PH_FLAG_P_IPV4_PORT; if (parse_phostport(arg->s, arg->len, &host.s, &host.len, &port, &proto)!=0){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Invalid [proto:]IPv4[:port] for" " %.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } LM_DBG("[%.*s]->[%d][%.*s][%d]\n", arg->len, arg->s, proto, host.len, host.s, port); if (str2ip(&host)==NULL) { PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Invalid IPv4 in [proto:]IPv4[:port]" " %.*s [%.*s][%.*s].", key->len, key->s, host.len, host.s, arg->len, arg->s); goto done; } continue; } LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags); if(flags&PH_FLAG_IPV4){ flags&= ~ PH_FLAG_IPV4; if (str2ip(arg)==NULL) { PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Invalid IPv4 for %.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } continue; } LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags); if(flags&PH_FLAG_URI){ flags&= ~ PH_FLAG_URI; if (parse_uri(arg->s, arg->len, &uri)<0){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Invalid SIP URI for %.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } continue; } LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags); if(flags&PH_FLAG_URI_IPV4HOST){ flags&= ~ PH_FLAG_URI_IPV4HOST; if (parse_uri(arg->s, arg->len, &uri)<0){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Invalid SIP URI for %.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } if (str2ip(&uri.host)==NULL) { PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Invalid IPv4 host in SIP URI for" " %.*s [%.*s][%.*s].", key->len, key->s, uri.host.len, uri.host.s, arg->len, arg->s); goto done; } continue; } LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags); if(flags){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Unknown validation [%d] for %s.", table->cols[i].validation, key->s); goto done; } } } switch(val_type){ case DB_STR: case DB_STRING: case DB_BLOB: if(arg->len){ val->val.str_val.s = arg->s; val->val.str_val.len = arg->len; } break; case DB_INT: c = arg->s[arg->len]; arg->s[arg->len] = '\0'; if(db_str2int(arg->s,&val->val.int_val)<0){ arg->s[arg->len] = c; PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Bogus field %.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } arg->s[arg->len] = c; break; case DB_BITMAP: c = arg->s[arg->len]; arg->s[arg->len] = '\0'; if(db_str2int(arg->s,(int*)&val->val.bitmap_val)<0){ arg->s[arg->len] = c; PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Bogus field %.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } arg->s[arg->len] = c; break; case DB_BIGINT: c = arg->s[arg->len]; arg->s[arg->len] = '\0'; if(db_str2bigint(arg->s,&val->val.bigint_val)<0){ arg->s[arg->len] = c; PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Bogus field %.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } arg->s[arg->len] = c; break; case DB_DOUBLE: c = arg->s[arg->len]; arg->s[arg->len] = '\0'; if(db_str2double(arg->s,&val->val.double_val)<0){ arg->s[arg->len] = c; PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Bogus field %.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } arg->s[arg->len] = c; break; case DB_DATETIME: c = arg->s[arg->len]; arg->s[arg->len] = '\0'; if(db_str2time(arg->s,&val->val.time_val)<0){ arg->s[arg->len] = c; PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Bogus field %.*s [%.*s].", key->len, key->s, arg->len, arg->s); goto done; } arg->s[arg->len] = c; break; default: PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Unexpected type [%d] for field [%.*s]\n", val_type, key->len, key->s); goto done; } page->len = p - page->s; return 0; done: page->len = p - page->s; return 1; error: LM_ERR("buffer 2 small\n"); page->len = p - page->s; return -1; } int ph_run_pi_cmd(int mod, int cmd, void *connection, void *con_cls, str *page, str *buffer) { char *p; char *buf; int ret; str s_arg; str l_arg; str temp_holder; int temp_counter; int i; int j; int max_page_len; ph_cmd_t *command; int _len; int link_on; char tmp; char c[2]; db_val_t *c_vals = NULL; db_val_t *q_vals = NULL; db_val_t *val; str val_str = {NULL, 0}; int nr_rows; ph_db_url_t *db_url = NULL; db_res_t *res = NULL; db_val_t *values = NULL; db_row_t *rows; ph_mod_t *ph_modules; ph_modules = ph_framework_data->ph_modules; html_page_data.page.s = buffer->s; html_page_data.page.len = 0; html_page_data.buffer.s = buffer->s; html_page_data.buffer.len = buffer->len; html_page_data.mod = mod; html_page_data.cmd = cmd; max_page_len = buffer->len; if (0!=ph_build_header(page, buffer->len, mod, cmd)) return -1; buf = page->s; p = page->s + page->len; if (cmd<0) return ph_build_content(page, buffer->len, mod, cmd, NULL, NULL); httpd_api.lookup_arg(connection, "cmd", con_cls, &l_arg); if(l_arg.s==NULL) return ph_build_content(page, buffer->len, mod, cmd, NULL, NULL); LM_DBG("got arg cmd=[%.*s]\n", l_arg.len, l_arg.s); command = &ph_modules[mod].cmds[cmd]; if (l_arg.len==3 && strncmp(l_arg.s, "pre", 3)==0) { /* We prebuild values only for update */ if(command->type!=DB_CAP_UPDATE) { LM_ERR("command [%.*s] is not DB_CAP_UPDATE type\n", command->name.len, command->name.s); return ph_build_content(page, buffer->len, mod, cmd, NULL, NULL); } /* We prebuild values only for single clause update command */ if(command->c_keys_size!=1) { LM_ERR("command [%.*s] has [%d] clause keys\n", command->name.len, command->name.s, command->c_keys_size); return ph_build_content(page, buffer->len, mod, cmd, NULL, NULL); } LM_DBG("[%.*s] with clause key [%.*s]\n", command->name.len, command->name.s, command->c_keys[0]->len, command->c_keys[0]->s); tmp = command->c_keys[0]->s[command->c_keys[0]->len]; command->c_keys[0]->s[command->c_keys[0]->len] = '\0'; LM_DBG("httpd_api.lookup_arg[%s]\n", command->c_keys[0]->s); httpd_api.lookup_arg(connection, command->c_keys[0]->s, con_cls, &l_arg); if(l_arg.s==NULL) { LM_ERR("missing clause key [%.*s] in args\n", command->c_keys[0]->len, command->c_keys[0]->s); command->c_keys[0]->s[command->c_keys[0]->len] = tmp; return ph_build_content(page, buffer->len, mod, cmd, NULL, NULL); } command->c_keys[0]->s[command->c_keys[0]->len] = tmp; LM_DBG("got clause [%.*s] with value [%.*s]\n", command->c_keys[0]->len, command->c_keys[0]->s, l_arg.len, l_arg.s); c_vals = (db_val_t*)pkg_malloc(command->c_keys_size*sizeof(db_val_t)); if(c_vals==NULL){ LM_ERR("oom\n"); return -1; } memset(c_vals, 0, command->c_keys_size*sizeof(db_val_t)); val = &c_vals[0]; val->type = command->c_types[0]; ret = getVal(val, command->c_types[0], command->c_keys[0], command->db_table, &l_arg, page, buffer, mod, cmd); if(ret<0) goto error; else if(ret>0) goto finish_page; /* Let's run the query to get the values for the record to update*/ db_url = command->db_table->db_url; if(use_table(command->db_table)<0){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Error on table [%.*s].", command->db_table->name.len, command->db_table->name.s); goto finish_page; } if(db_url->http_dbf.query(*db_url->http_db_handle, command->c_keys, command->c_ops, c_vals, command->q_keys, command->c_keys_size, command->q_keys_size, command->o_keys?*command->o_keys:0, &res) < 0){ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Error while querying database."); goto finish_page; } nr_rows = RES_ROW_N(res); switch (nr_rows) { case 0: LM_ERR("no record on clause key [%.*s]\n", command->c_keys[0]->len, command->c_keys[0]->s); if(c_vals) pkg_free(c_vals); c_vals = NULL; goto finish_page; case 1: LM_DBG("got [%d] rows for key [%.*s]\n", nr_rows, command->c_keys[0]->len, command->c_keys[0]->s); break; default: LM_ERR("to many records [%d] on clause key [%.*s]\n", nr_rows, command->c_keys[0]->len, command->c_keys[0]->s); goto finish_page; } rows = RES_ROWS(res); values = ROW_VALUES(rows); ret = ph_build_content(page, buffer->len, mod, cmd, &l_arg, values); db_url->http_dbf.free_result(*db_url->http_db_handle, res); //res = NULL; return ret; } else if(l_arg.len==2 && strncmp(l_arg.s, "on", 2)==0) { /* allocate c_vals array */ if(command->c_keys_size && command->c_keys_size){ c_vals = (db_val_t*)pkg_malloc(command->c_keys_size*sizeof(db_val_t)); if(c_vals==NULL){ LM_ERR("oom\n"); return -1; } memset(c_vals, 0, command->c_keys_size*sizeof(db_val_t)); for(i=0;ic_keys_size;i++){ s_arg.s = int2str(i, &s_arg.len); httpd_api.lookup_arg(connection, s_arg.s, con_cls, &l_arg); if(l_arg.s==NULL){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "No argument for clause field #%d: %.*s.", i, command->c_keys[i]->len, command->c_keys[i]->s); goto done; } s_arg.len = l_arg.len; if(s_arg.len==0){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Empty argument for clause field #%d: %.*s.", i,command->c_keys[i]->len, command->c_keys[i]->s); goto done; } s_arg.s = l_arg.s; val = &c_vals[i]; val->type = command->c_types[i]; ret = getVal(val, command->c_types[i], command->c_keys[i], command->db_table, &s_arg, page, buffer, mod, cmd); if(ret<0) goto error; else if(ret>0) goto done; } } } if(command->q_keys_size && command->q_keys_size && command->type!=DB_CAP_QUERY){ q_vals = (db_val_t*)pkg_malloc(command->q_keys_size*sizeof(db_val_t)); if(q_vals==NULL){ LM_ERR("oom\n"); return -1; } memset(q_vals, 0, command->q_keys_size*sizeof(db_val_t)); c[1] = '\0'; for(i=0,c[0]='a';iq_keys_size;i++,(c[0])++){ if(c[0]=='z'){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Too many query values."); goto done; } LM_DBG("looking for arg [%s]\n", c); httpd_api.lookup_arg(connection, c, con_cls, &l_arg); if(l_arg.s==NULL){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "No argument for query field #%d: %.*s.", i, command->q_keys[i]->len, command->q_keys[i]->s); goto done; } s_arg.len = l_arg.len; if(s_arg.len==0 && (command->q_types[i]!=DB_STR && command->q_types[i]!=DB_STRING && command->q_types[i]!=DB_BLOB)){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Empty argument for query field #%d: %.*s.", i, command->q_keys[i]->len, command->q_keys[i]->s); goto done; } s_arg.s = l_arg.s; val = &q_vals[i]; val->type = command->q_types[i]; ret = getVal(val, command->q_types[i], command->q_keys[i], command->db_table, &s_arg, page, buffer, mod, cmd); if(ret<0) goto error; else if(ret>0) goto done; } } db_url = command->db_table->db_url; if(use_table(command->db_table)<0){ PI_HTTP_BUILD_REPLY(page, buffer, mod, cmd, "Error on table [%.*s].", command->db_table->name.len, command->db_table->name.s); goto done; } if(ph_build_reply(page, buffer->len, mod, cmd)<0) goto error; p = page->s + page->len; switch (command->type) { case DB_CAP_QUERY: for(j=0;jq_keys_size;j++){ if(j)PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Cmd_td_1d); PI_HTTP_COPY_2(p,*(command->q_keys[j]), PI_HTTP_Response_Menu_Cmd_td_4d); } if (DB_CAPABILITY(db_url->http_dbf, DB_CAP_FETCH)){ if(db_url->http_dbf.query(*db_url->http_db_handle, command->c_keys, command->c_ops, c_vals, command->q_keys, command->c_keys_size, command->q_keys_size, command->o_keys?*command->o_keys:0, 0) < 0){ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Error while querying (fetch) database."); goto done; } if(db_url->http_dbf.fetch_result(*db_url->http_db_handle, &res, 100)<0){ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Fetching rows failed."); goto done; } }else{ if(db_url->http_dbf.query(*db_url->http_db_handle, command->c_keys, command->c_ops, c_vals, command->q_keys, command->c_keys_size, command->q_keys_size, command->o_keys?*command->o_keys:0, &res) < 0){ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Error while querying database."); goto done; } } nr_rows = RES_ROW_N(res); do{ LM_DBG("loading [%i] records from db\n", nr_rows); rows = RES_ROWS(res); for(i=0;iq_keys_size;j++){ PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Cmd_td_1d); /* BEGIN */ link_on = 0; if(command->link_cmd && command->link_cmd[j].s) { link_on = 1; PI_HTTP_COPY(p,PI_HTTP_HREF_1); if (http_root.len) { PI_HTTP_COPY_2(p,http_root, PI_HTTP_SLASH); } PI_HTTP_COPY_2(p,ph_modules[mod].module, PI_HTTP_SLASH); PI_HTTP_COPY(p,command->link_cmd[j]); /* this is the command */ PI_HTTP_COPY_3(p,PI_HTTP_HREF_2, *command->q_keys[j], PI_HTTP_ATTR_VAL_SEPARATOR); } /* END */ switch(command->q_types[j]){ case DB_STR: case DB_STRING: case DB_BLOB: if(values[j].val.str_val.s==NULL){ val_str.s = NULL; val_str.len = 0; } else { val_str.s = values[j].val.str_val.s; val_str.len = strlen(val_str.s); } LM_DBG("...got %.*s[%d]=>" "[%.*s][%.*s]\n", command->q_keys[j]->len, command->q_keys[j]->s, i, values[j].val.str_val.len, values[j].val.str_val.s, val_str.len, val_str.s); if (val_str.len) { if(link_on) { PI_HTTP_ESC_COPY(p, val_str, temp_holder, temp_counter); PI_HTTP_COPY(p,PI_HTTP_SQUOT_GT); } PI_HTTP_ESC_COPY(p, val_str, temp_holder, temp_counter); } else { if(link_on) { PI_HTTP_COPY(p, PI_HTTP_NBSP); PI_HTTP_COPY(p,PI_HTTP_SQUOT_GT); } PI_HTTP_COPY(p, PI_HTTP_NBSP); } break; case DB_INT: val_str.s = p; val_str.len = max_page_len - page->len; if(db_int2str(values[j].val.int_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert int [%d]\n", values[j].val.int_val); goto error; } p += val_str.len; page->len += val_str.len; if(link_on) PI_HTTP_COPY_2(p,PI_HTTP_SQUOT_GT,val_str); LM_DBG(" got %.*s[%d]=>" "[%d][%.*s]\n", command->q_keys[j]->len, command->q_keys[j]->s, i, values[j].val.int_val, val_str.len, val_str.s); break; case DB_BITMAP: val_str.s = p; val_str.len = max_page_len - page->len; if(db_int2str(values[j].val.bitmap_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert bitmap [%d]\n", values[j].val.bitmap_val); goto error; } p += val_str.len; page->len += val_str.len; if(link_on) PI_HTTP_COPY_2(p,PI_HTTP_SQUOT_GT,val_str); LM_DBG(" got %.*s[%d]=>" "[%d][%.*s]\n", command->q_keys[j]->len, command->q_keys[j]->s, i, values[j].val.bitmap_val, val_str.len, val_str.s); break; case DB_BIGINT: val_str.s = p; val_str.len = max_page_len - page->len; if(db_bigint2str(values[j].val.bigint_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert bigint [%-lld]\n", values[j].val.bigint_val); goto error; } p += val_str.len; page->len += val_str.len; if(link_on) PI_HTTP_COPY_2(p,PI_HTTP_SQUOT_GT,val_str); LM_DBG(" got %.*s[%d]=>" "[%-lld][%.*s]\n", command->q_keys[j]->len, command->q_keys[j]->s, i, values[j].val.bigint_val, val_str.len, val_str.s); break; case DB_DOUBLE: val_str.s = p; val_str.len = max_page_len - page->len; if(db_double2str(values[j].val.double_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert double [%-10.2f]\n", values[j].val.double_val); goto error; } p += val_str.len; page->len += val_str.len; if(link_on) PI_HTTP_COPY_2(p,PI_HTTP_SQUOT_GT,val_str); LM_DBG(" got %.*s[%d]=>" "[%-10.2f][%.*s]\n", command->q_keys[j]->len, command->q_keys[j]->s, i, values[j].val.double_val, val_str.len, val_str.s); break; case DB_DATETIME: val_str.s = p; val_str.len = max_page_len - page->len; if (db_time2str_nq(values[j].val.time_val, val_str.s, &val_str.len)!=0){ LM_ERR("Unable to convert time [%ld]\n", (unsigned long int)values[j].val.time_val); goto error; } p += val_str.len; page->len += val_str.len; if(link_on) PI_HTTP_COPY_2(p,PI_HTTP_SQUOT_GT,val_str); LM_DBG(" got %.*s[%d]=>" "[%ld][%.*s]\n", command->q_keys[j]->len, command->q_keys[j]->s, i, (unsigned long int)values[j].val.time_val, val_str.len, val_str.s); break; default: LM_ERR("unexpected type [%d] " "for [%.*s]\n", command->q_types[j], command->q_keys[j]->len, command->q_keys[j]->s); } if(link_on) PI_HTTP_COPY(p,PI_HTTP_HREF_3); PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Cmd_td_4d); } PI_HTTP_COPY(p,PI_HTTP_Response_Menu_Cmd_tr_2); } /* any more data to be fetched ?*/ if (DB_CAPABILITY(db_url->http_dbf, DB_CAP_FETCH)){ if(db_url->http_dbf.fetch_result(*db_url->http_db_handle, &res, 100)<0){ LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(res); }else{ nr_rows = 0; } }while (nr_rows>0); db_url->http_dbf.free_result(*db_url->http_db_handle, res); res=NULL; goto finish_page; break; case DB_CAP_INSERT: if((db_url->http_dbf.insert(*db_url->http_db_handle, command->q_keys, q_vals, command->q_keys_size))!=0){ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Unable to add record to db."); }else{ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Record successfully added to db."); } goto done; break; case DB_CAP_DELETE: if((db_url->http_dbf.delete(*db_url->http_db_handle, command->c_keys, command->c_ops, c_vals, command->c_keys_size))!=0) { PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Unable to delete record."); }else{ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Record successfully deleted from db."); } goto done; break; case DB_CAP_UPDATE: if((db_url->http_dbf.update(*db_url->http_db_handle, command->c_keys, command->c_ops, c_vals, command->q_keys, q_vals, command->c_keys_size, command->q_keys_size))!=0){ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Unable to update record."); }else{ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Record successfully updated."); } goto done; break; case DB_CAP_REPLACE: if((db_url->http_dbf.replace(*db_url->http_db_handle, command->q_keys, q_vals, command->q_keys_size))!=0){ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Unable to replace record."); }else{ PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Record successfully replaced."); } break; default: PI_HTTP_COMPLETE_REPLY(page, buffer, mod, cmd, "Corrupt data for mod=[%d] and cmd=[%d]\n", mod, cmd); goto done; } LM_ERR("You shoudn't end up here\n"); error: if (db_url && res) db_url->http_dbf.free_result(*db_url->http_db_handle, res); if(c_vals) pkg_free(c_vals); if(q_vals) pkg_free(q_vals); return -1; finish_page: if(c_vals) pkg_free(c_vals); if(q_vals) pkg_free(q_vals); page->len = p - page->s; return ph_build_reply_footer(page, buffer->len); done: if(c_vals) pkg_free(c_vals); if(q_vals) pkg_free(q_vals); return 0; } opensips-2.2.2/modules/pi_http/http_fnc.h000066400000000000000000000057421300170765700204700ustar00rootroot00000000000000/* * Copyright (C) 2011 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2011-09-20 first version (osas) */ #ifndef _PI_HTTP_HTTP_FNC_H #define _PI_HTTP_HTTP_FNC_H #include "../../db/db.h" /**< no validation required */ #define PH_FLAG_NONE 0 /**< validate as socket: [proto:]host[:port] */ #define PH_FLAG_P_HOST_PORT (1<<0) /**< validate as socket: [proto:]IPv4[:port] */ #define PH_FLAG_P_IPV4_PORT (1<<1) /**< validate as IPv4 */ #define PH_FLAG_IPV4 (1<<2) /**< validate as SIP URI */ #define PH_FLAG_URI (1<<3) /**< validate as SIP URI w/ IPv4 host */ #define PH_FLAG_URI_IPV4HOST (1<<4) typedef short int ph_val_flags; typedef struct ph_db_url_ { str id; str db_url; db_con_t **http_db_handle; db_func_t http_dbf; }ph_db_url_t; typedef struct ph_table_col_ { str field; db_type_t type; ph_val_flags validation; }ph_table_col_t; typedef struct ph_db_table_ { str id; str name; ph_db_url_t *db_url; ph_table_col_t *cols; int cols_size; }ph_db_table_t; typedef struct ph_vals_ { str *ids; /* String to display for the given value */ str *vals; /* pre=populated value for a specific field */ int vals_size; }ph_vals_t; typedef struct ph_cmd_ { str name; unsigned int type; ph_db_table_t *db_table; db_op_t *c_ops; db_key_t *c_keys; db_type_t *c_types; ph_vals_t *c_vals; /* array of prepopulated values */ int c_keys_size; db_key_t *q_keys; db_type_t *q_types; ph_vals_t *q_vals; /* array of prepopulated values */ str *link_cmd; /* cmd to be executed for query links */ int q_keys_size; db_key_t *o_keys; int o_keys_size; }ph_cmd_t; typedef struct ph_mod_ { str module; ph_cmd_t *cmds; int cmds_size; }ph_mod_t; typedef struct ph_framework_ { ph_db_url_t *ph_db_urls; int ph_db_urls_size; ph_db_table_t *ph_db_tables; int ph_db_tables_size; ph_mod_t *ph_modules; int ph_modules_size; }ph_framework_t; typedef struct ph_html_page_data_ { str page; str buffer; int mod; int cmd; }ph_html_page_data_t; int ph_init_async_lock(void); void ph_destroy_async_lock(void); int ph_init_cmds(ph_framework_t **framework_data, const char* filename); int ph_parse_url(const char* url, int* mod, int* cmd); int ph_run_pi_cmd(int mod, int cmd, void *connection, void *con_cls, str *page, str *buffer); #endif opensips-2.2.2/modules/pi_http/pi_http.c000066400000000000000000000162161300170765700203230ustar00rootroot00000000000000/* * Copyright (C) 2011 VoIP Embedded Inc. * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2012-03-17 first version (osas) */ #include #include "../../globals.h" #include "../../sr_module.h" #include "../../str.h" #include "../../ut.h" #include "../../resolve.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../httpd/httpd_load.h" #include "http_fnc.h" #include "http_db_handler.h" extern ph_framework_t *ph_framework_data; /* module functions */ static int mod_init(); static int child_init(); static int destroy(void); int ph_answer_to_connection (void *cls, void *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls, str *buffer, str *page); static ssize_t ph_flush_data(void *cls, uint64_t pos, char *buf, size_t max); static struct mi_root *mi_framework_reload(struct mi_root* cmd, void* param); str http_root = str_init("pi"); int http_method = 0; str filename = {NULL, 0}; httpd_api_t httpd_api; gen_lock_t* ph_lock; static const str PI_HTTP_U_ERROR = str_init("" "Internal server error!"); static const str PI_HTTP_U_URL = str_init("" "Unable to parse URL!"); static const str PI_HTTP_U_METHOD = str_init("" "Unexpected method (only GET is accepted)!"); /* module parameters */ static param_export_t params[] = { {"pi_http_root", STR_PARAM, &http_root.s}, {"pi_http_method", INT_PARAM, &http_method}, {"framework", STR_PARAM, &filename.s}, {0,0,0} }; /** MI commands */ static mi_export_t mi_cmds[] = { { "pi_reload_tbls_and_cmds", 0, mi_framework_reload, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "httpd", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* module exports */ struct module_exports exports = { "pi_http", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported PV */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ (child_init_function)child_init /* per-child init function */ }; void proc_init(void) { return; } int ph_init_async_lock(void) { ph_lock = lock_alloc(); if (ph_lock==NULL) { LM_ERR("failed to create lock\n"); return -1; } if (lock_init(ph_lock)==NULL) { LM_ERR("failed to init lock\n"); return -1; } return 0; } void ph_destroy_async_lock(void) { if (ph_lock) { lock_destroy(ph_lock); lock_dealloc(ph_lock); } } static int mod_init(void) { int i; if (filename.s==NULL) { LM_ERR("invalid framework\n"); return -1; } filename.len = strlen(filename.s); http_root.len = strlen(http_root.s); if (http_method<0 || http_method>1) { LM_ERR("pi_http_method can be between [0,1]\n"); return -1; } /* Load httpd api */ if(load_httpd_api(&httpd_api)<0) { LM_ERR("Failed to load httpd api\n"); return -1; } /* Load httpd hooks */ httpd_api.register_httpdcb(exports.name, &http_root, &ph_answer_to_connection, &ph_flush_data, &proc_init); /* Build a cache of all provisionning commands */ if (0!=ph_init_cmds(&ph_framework_data, filename.s)) return -1; /* init db connections */ for(i=0;iph_db_urls_size;i++){ ph_framework_data->ph_db_urls[i].http_db_handle = pkg_malloc(sizeof(db_con_t *)); *ph_framework_data->ph_db_urls[i].http_db_handle = 0; LM_DBG("initializing db[%d] [%s]\n", i, ph_framework_data->ph_db_urls[i].db_url.s); if (init_http_db(ph_framework_data, i)!=0) { LM_ERR("failed to initialize the DB support\n"); return -1; } } /* Build async lock */ if (ph_init_async_lock() != 0) exit(-1); return 0; } static int child_init(int rank) { int i; LM_DBG("Child initialization\n"); if (rank==PROC_TCP_MAIN || rank==PROC_BIN) return 0; for(i=0;iph_db_urls_size;i++){ LM_DBG("connecting to db[%d] [%s]\n", i, ph_framework_data->ph_db_urls[i].db_url.s); if (connect_http_db(ph_framework_data, i)) { LM_ERR("failed to connect to database\n"); return -1; } } return 0; } int destroy(void) { destroy_http_db(ph_framework_data); ph_destroy_async_lock(); return 0; } static ssize_t ph_flush_data(void *cls, uint64_t pos, char *buf, size_t max) { /* Not used for now */ return -1; } int ph_answer_to_connection (void *cls, void *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls, str *buffer, str *page) { int mod = -1; int cmd = -1; LM_DBG("START *** cls=%p, connection=%p, url=%s, method=%s, " "versio=%s, upload_data[%d]=%p, *con_cls=%p\n", cls, connection, url, method, version, (int)*upload_data_size, upload_data, *con_cls); if ((strncmp(method, "GET", 3)==0) || (strncmp(method, "POST", 4)==0)) { lock_get(ph_lock); if(0 == ph_parse_url(url, &mod, &cmd)) { page->s = buffer->s; if(0!=ph_run_pi_cmd(mod, cmd, connection, *con_cls, page, buffer)){ LM_ERR("unable to build response for cmd [%d]\n", cmd); *page = PI_HTTP_U_ERROR; } } else { LM_ERR("unable to parse URL [%s]\n", url); *page = PI_HTTP_U_URL; } lock_release(ph_lock); } else { LM_ERR("unexpected method [%s]\n", method); *page = PI_HTTP_U_METHOD; } return 200; } static struct mi_root *mi_framework_reload(struct mi_root* cmd, void* param) { struct mi_root *rpl_tree; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; lock_get(ph_lock); if (0!=ph_init_cmds(&ph_framework_data, filename.s)) goto error; lock_release(ph_lock); return rpl_tree; error: lock_release(ph_lock); free_mi_tree(rpl_tree); return NULL; } opensips-2.2.2/modules/pike/000077500000000000000000000000001300170765700157635ustar00rootroot00000000000000opensips-2.2.2/modules/pike/Makefile000066400000000000000000000003201300170765700174160ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pike.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/pike/README000066400000000000000000000215251300170765700166500ustar00rootroot00000000000000pike Module Bogdan-Andrei Iancu Edited by Bogdan-Andrei Iancu Copyright © 2003 FhG FOKUS Copyright © 2005-2009 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How to use 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. sampling_time_unit (integer) 1.4.2. reqs_density_per_unit (integer) 1.4.3. remove_latency (integer) 1.4.4. check_route (integer) 1.4.5. pike_log_level (integer) 1.5. Exported Functions 1.5.1. pike_check_req() 1.6. Exported MI Functions 1.6.1. pike_list 1.6.2. pike_rm 1.7. Exported Events 1.7.1. E_PIKE_BLOCKED 2. Developer Guide List of Examples 1.1. Set sampling_time_unit parameter 1.2. Set reqs_density_per_unit parameter 1.3. Set remove_latency parameter 1.4. Set check_route parameter 1.5. Set pike_log_level parameter 1.6. pike_check_req usage 2.1. Tree of IP addresses Chapter 1. Admin Guide 1.1. Overview The module provides a simple mechanism for DOS protection - DOS based on floods at network level. The module keeps trace of all (or selected ones) IPs of incoming SIP traffic (as source IP) and blocks the ones that exceeded some limit. Works simultaneous for IPv4 and IPv6 addresses. The module does not implement any actions on blocking - it just simply reports that there is a high traffic from an IP; what to do, is the administator decision (via scripting). 1.2. How to use There are 2 ways of using this module (as detecting flood attacks and as taking the right action to limit the impact on the system): * manual - from routing script you can force the check of the source IP of an incoming requests, using "pike_check_req" function. Note that this checking works only for SIP requests and you can decide (based on scripting logic) what source IPs to be monitored and what action to be taken when a flood is detected. * automatic - the module will install internal hooks to catch all incoming requests and replies (even if not well formed from SIP point of view) - more or less the module will monitor all incoming packages (from the network) on the SIP sockets. Each time the source IP of a package needs to be analyse (to see if trusted or not), the module will run a script route - see "check_route" module parameter -, where, based on custom logic, you can decide if that IP needs to be monitored for flooding or not. As action, when flood is detected, the module will automatically drop the packages. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.4. Exported Parameters 1.4.1. sampling_time_unit (integer) Time period used for sampling (or the sampling accuracy ;-) ). The smaller the better, but slower. If you want to detect peaks, use a small one. To limit the access (like total number of requests on a long period of time) to a proxy resource (a gateway for ex), use a bigger value of this parameter. IMPORTANT: a too small value may lead to performance penalties due timer process overloading. Default value is 2. Example 1.1. Set sampling_time_unit parameter ... modparam("pike", "sampling_time_unit", 10) ... 1.4.2. reqs_density_per_unit (integer) How many requests should be allowed per sampling_time_unit before blocking all the incoming request from that IP. Practically, the blocking limit is between ( let's have x=reqs_density_per_unit) x and 3*x for IPv4 addresses and between x and 8*x for ipv6 addresses. Default value is 30. Example 1.2. Set reqs_density_per_unit parameter ... modparam("pike", "reqs_density_per_unit", 30) ... 1.4.3. remove_latency (integer) For how long the IP address will be kept in memory after the last request from that IP address. It's a sort of timeout value. Default value is 120. Example 1.3. Set remove_latency parameter ... modparam("pike", "remove_latency", 130) ... 1.4.4. check_route (integer) The name of the script route to be triggers (in automatic way) when a package is received from the network. If you do a "drop" in this route, it will indicate to the module that the source IP of the package does not need to be monitored. Otherwise, the source IP will be automatically monitered. By defining this parameter, the automatic checking mode is enabled. Default value is NONE (no auto mode). Example 1.4. Set check_route parameter ... modparam("pike", "check_route", "pike") ... route[pike]{ if (src_ip==111.222.111.222) /*trusted, do not check it*/ drop; /* all other IPs are checked*/ } .... 1.4.5. pike_log_level (integer) Log level to be used by module to auto report the blocking (only first time) and unblocking of IPs detected as source of floods. Default value is 1 (L_WARN). Example 1.5. Set pike_log_level parameter ... modparam("pike", "pike_log_level", -1) ... 1.5. Exported Functions 1.5.1. pike_check_req() Process the source IP of the current request and returns false if the IP was exceeding the blocking limit. Return codes: * 1 (true) - IP is not to be blocked or internal error occurred. Warning IMPORTANT: in case of internal error, the function returns true to avoid reporting the current processed IP as blocked. * -1 (false) - IP is source of flooding, being previously detected * -2 (false) - IP is detected as a new source of flooding - first time detection This function can be used from REQUEST_ROUTE. Example 1.6. pike_check_req usage ... if (!pike_check_req()) { exit; }; ... 1.6. Exported MI Functions 1.6.1. pike_list Lists the nodes in the pike tree. Name: pike_list Parameters: * IP - IP address currently blocked. MI FIFO Command Format: :pike_list:_reply_fifo_file_ _empty_line_ 1.6.2. pike_rm Remove a node from the pike tree by IP address. Name: pike_rm Parameters: none MI FIFO Command Format: :pike_list:_reply_fifo_file_ _empty_line_ 1.7. Exported Events 1.7.1. E_PIKE_BLOCKED This event is raised when the pike module decides that an IP should be blocked. Parameters: * ip - the IP address that has been blocked. Chapter 2. Developer Guide One single tree (for both IPv4 and IPv6) is used. Each node contains a byte, the IP addresses stretching from root to the leafs. Example 2.1. Tree of IP addresses / 193 - 175 - 132 - 164 tree root / \ 142 \ 195 - 37 - 78 - 163 \ 79 - 134 To detect the whole address, step by step, from the root to the leafs, the nodes corresponding to each byte of the ip address are expanded. In order to be expended a node has to be hit for a given number of times (possible by different addresses; in the previous example, the node “37†was expended by the 195.37.78.163 and 195.37.79.134 hits). For 193.175.132.164 with x= reqs_density_per_unit: * After first req hits -> the “193†node is built. * After x more hits, the “175†node is build; the hits of “193†node are split between itself and its child--both of them gone have x/2. * And so on for node “132†and “164â€. * Once “164†build the entire address can be found in the tree. “164†becomes a leaf. After it will be hit as a leaf for x times, it will become “RED†(further request from this address will be blocked). So, to build and block this address were needed 3*x hits. Now, if reqs start coming from 193.175.132.142, the first 3 bytes are already in the tree (they are shared with the previous address), so I will need only x hits (to build node “142†and to make it “REDâ€) to make this address also to be blocked. This is the reason for the variable number of hits necessary to block an IP. The maximum number of hits to turn an address red are (n is the address's number of bytes): 1 (first byte) + x (second byte) + (x / 2) * (n - 2) (for the rest of the bytes) + (n - 1) (to turn the node to red). So, for IPv4 (n = 4) will be 3x and for IPv6 (n = 16) will be 9x. The minimum number of hits to turn an address red is x. opensips-2.2.2/modules/pike/doc/000077500000000000000000000000001300170765700165305ustar00rootroot00000000000000opensips-2.2.2/modules/pike/doc/pike.xml000066400000000000000000000022571300170765700202100ustar00rootroot00000000000000 %docentities; ]> pike Module &osipsname; Bogdan-Andrei Iancu
bogdan@opensips.org
Bogdan-Andrei Iancu
bogdan@opensips.org
2003 &fhg; 2005-2009 &voicesystem; $Revision: 8740 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/pike/doc/pike_admin.xml000066400000000000000000000212631300170765700213560ustar00rootroot00000000000000 &adminguide;
Overview The module provides a simple mechanism for DOS protection - DOS based on floods at network level. The module keeps trace of all (or selected ones) IPs of incoming SIP traffic (as source IP) and blocks the ones that exceeded some limit. Works simultaneous for IPv4 and IPv6 addresses. The module does not implement any actions on blocking - it just simply reports that there is a high traffic from an IP; what to do, is the administator decision (via scripting).
How to use There are 2 ways of using this module (as detecting flood attacks and as taking the right action to limit the impact on the system): manual - from routing script you can force the check of the source IP of an incoming requests, using "pike_check_req" function. Note that this checking works only for SIP requests and you can decide (based on scripting logic) what source IPs to be monitored and what action to be taken when a flood is detected. automatic - the module will install internal hooks to catch all incoming requests and replies (even if not well formed from SIP point of view) - more or less the module will monitor all incoming packages (from the network) on the SIP sockets. Each time the source IP of a package needs to be analyse (to see if trusted or not), the module will run a script route - see "check_route" module parameter -, where, based on custom logic, you can decide if that IP needs to be monitored for flooding or not. As action, when flood is detected, the module will automatically drop the packages.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>sampling_time_unit</varname> (integer) Time period used for sampling (or the sampling accuracy ;-) ). The smaller the better, but slower. If you want to detect peaks, use a small one. To limit the access (like total number of requests on a long period of time) to a proxy resource (a gateway for ex), use a bigger value of this parameter. IMPORTANT: a too small value may lead to performance penalties due timer process overloading. Default value is 2. Set <varname>sampling_time_unit</varname> parameter ... modparam("pike", "sampling_time_unit", 10) ...
<varname>reqs_density_per_unit</varname> (integer) How many requests should be allowed per sampling_time_unit before blocking all the incoming request from that IP. Practically, the blocking limit is between ( let's have x=reqs_density_per_unit) x and 3*x for IPv4 addresses and between x and 8*x for ipv6 addresses. Default value is 30. Set <varname>reqs_density_per_unit</varname> parameter ... modparam("pike", "reqs_density_per_unit", 30) ...
<varname>remove_latency</varname> (integer) For how long the IP address will be kept in memory after the last request from that IP address. It's a sort of timeout value. Default value is 120. Set <varname>remove_latency</varname> parameter ... modparam("pike", "remove_latency", 130) ...
<varname>check_route</varname> (integer) The name of the script route to be triggers (in automatic way) when a package is received from the network. If you do a "drop" in this route, it will indicate to the module that the source IP of the package does not need to be monitored. Otherwise, the source IP will be automatically monitered. By defining this parameter, the automatic checking mode is enabled. Default value is NONE (no auto mode). Set <varname>check_route</varname> parameter ... modparam("pike", "check_route", "pike") ... route[pike]{ if (src_ip==111.222.111.222) /*trusted, do not check it*/ drop; /* all other IPs are checked*/ } ....
<varname>pike_log_level</varname> (integer) Log level to be used by module to auto report the blocking (only first time) and unblocking of IPs detected as source of floods. Default value is 1 (L_WARN). Set <varname>pike_log_level</varname> parameter ... modparam("pike", "pike_log_level", -1) ...
Exported Functions
<function moreinfo="none">pike_check_req()</function> Process the source IP of the current request and returns false if the IP was exceeding the blocking limit. Return codes: 1 (true) - IP is not to be blocked or internal error occurred. IMPORTANT: in case of internal error, the function returns true to avoid reporting the current processed IP as blocked. -1 (false) - IP is source of flooding, being previously detected -2 (false) - IP is detected as a new source of flooding - first time detection This function can be used from REQUEST_ROUTE. <function>pike_check_req</function> usage ... if (!pike_check_req()) { exit; }; ...
Exported MI Functions
<function moreinfo="none">pike_list</function> Lists the nodes in the pike tree. Name: pike_list Parameters: IP - IP address currently blocked. MI FIFO Command Format: :pike_list:_reply_fifo_file_ _empty_line_
<function moreinfo="none">pike_rm</function> Remove a node from the pike tree by IP address. Name: pike_rm Parameters: none MI FIFO Command Format: :pike_list:_reply_fifo_file_ _empty_line_
Exported Events
<function moreinfo="none">E_PIKE_BLOCKED</function> This event is raised when the pike module decides that an IP should be blocked. Parameters: ip - the IP address that has been blocked.
opensips-2.2.2/modules/pike/doc/pike_devel.xml000066400000000000000000000050031300170765700213570ustar00rootroot00000000000000 &develguide; One single tree (for both IPv4 and IPv6) is used. Each node contains a byte, the &ip; addresses stretching from root to the leafs. Tree of &ip; addresses / 193 - 175 - 132 - 164 tree root / \ 142 \ 195 - 37 - 78 - 163 \ 79 - 134 To detect the whole address, step by step, from the root to the leafs, the nodes corresponding to each byte of the ip address are expanded. In order to be expended a node has to be hit for a given number of times (possible by different addresses; in the previous example, the node 37 was expended by the 195.37.78.163 and 195.37.79.134 hits). For 193.175.132.164 with x= reqs_density_per_unit: After first req hits -> the 193 node is built. After x more hits, the 175 node is build; the hits of 193 node are split between itself and its child--both of them gone have x/2. And so on for node 132 and 164. Once 164 build the entire address can be found in the tree. 164 becomes a leaf. After it will be hit as a leaf for x times, it will become RED (further request from this address will be blocked). So, to build and block this address were needed 3*x hits. Now, if reqs start coming from 193.175.132.142, the first 3 bytes are already in the tree (they are shared with the previous address), so I will need only x hits (to build node 142 and to make it RED) to make this address also to be blocked. This is the reason for the variable number of hits necessary to block an &ip;. The maximum number of hits to turn an address red are (n is the address's number of bytes): 1 (first byte) + x (second byte) + (x / 2) * (n - 2) (for the rest of the bytes) + (n - 1) (to turn the node to red). So, for IPv4 (n = 4) will be 3x and for IPv6 (n = 16) will be 9x. The minimum number of hits to turn an address red is x. opensips-2.2.2/modules/pike/ip_tree.c000066400000000000000000000207761300170765700175720ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-11-05: adaptiv init lock (bogdan) * 2008-04-17 the leaf nodes memorize (via flags) if they are in RED state * (detected) or not -> better logging and MI (bogdan) */ #include #include #include #include #include #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "ip_tree.h" static struct ip_tree* root = 0; static inline struct ip_node* prv_get_tree_branch(unsigned char b) { return root->entries[b].node; } /* locks a tree branch */ static inline void prv_lock_tree_branch(unsigned char b) { lock_set_get( root->entry_lock_set, root->entries[b].lock_idx); } /* unlocks a tree branch */ static inline void prv_unlock_tree_branch(unsigned char b) { lock_set_release( root->entry_lock_set, root->entries[b].lock_idx); } /* wrapper functions */ struct ip_node* get_tree_branch(unsigned char b) { return prv_get_tree_branch(b); } void lock_tree_branch(unsigned char b) { prv_lock_tree_branch(b); } void unlock_tree_branch(unsigned char b) { prv_unlock_tree_branch(b); } /* size must be a power of 2 */ static gen_lock_set_t* init_lock_set(int *size) { gen_lock_set_t *lset; lset=0; /* kill warnings */ for( ; *size ; *size=((*size)>>1) ) { LM_INFO("probing %d set size\n", *size); /* create a lock set */ lset = lock_set_alloc( *size ); if (lset==0) { LM_INFO("cannot get %d locks\n", *size); continue; } /* init lock set */ if (lock_set_init(lset)==0) { LM_INFO("cannot init %d locks\n", *size); lock_set_dealloc( lset ); lset = 0; continue; } /* alloc and init succesfull */ break; } if (*size==0) { LM_ERR("cannot get a lock set\n"); return 0; } return lset; } /* Builds and Inits a new IP tree */ int init_ip_tree(int maximum_hits) { int size; int i; /* create the root */ root = (struct ip_tree*)shm_malloc(sizeof(struct ip_tree)); if (root==0) { LM_ERR("shm malloc failed\n"); goto error; } memset( root, 0, sizeof(struct ip_tree)); /* init lock set */ size = MAX_IP_BRANCHES; root->entry_lock_set = init_lock_set( &size ); if (root->entry_lock_set==0) { LM_ERR("failed to create locks\n"); goto error; } /* assign to each branch a lock */ for(i=0;ientries[i].node = 0; root->entries[i].lock_idx = i % size; } root->max_hits = maximum_hits; return 0; error: if (root) shm_free(root); return -1; } /* destroy an ip_node and all nodes under it; the nodes must be first removed * from any other lists/timers */ static inline void destroy_ip_node(struct ip_node *node) { struct ip_node *foo, *bar; foo = node->kids; while (foo){ bar = foo; foo = foo->next; destroy_ip_node(bar); } shm_free(node); } /* destroy and free the IP tree */ void destroy_ip_tree(void) { int i; if (root==0) return; /* destroy and free the lock set */ if (root->entry_lock_set) { lock_set_destroy(root->entry_lock_set); lock_set_dealloc(root->entry_lock_set); } /* destroy all the nodes */ for(i=0;ientries[i].node) destroy_ip_node(root->entries[i].node); shm_free( root ); root = 0; return; } /* builds a new ip_node corresponding to a byte value */ static inline struct ip_node *new_ip_node(unsigned char byte) { struct ip_node *new_node; new_node = (struct ip_node*)shm_malloc(sizeof(struct ip_node)); if (!new_node) { LM_ERR("no more shm mem\n"); return 0; } memset( new_node, 0, sizeof(struct ip_node)); new_node->byte = byte; return new_node; } /* splits from the current node (dad) a new child */ struct ip_node *split_node(struct ip_node* dad, unsigned char byte) { struct ip_node *new_node; /* create a new node */ if ( (new_node=new_ip_node(byte))==0 ) return 0; /* the child node inherits a part of his father hits */ if (dad->hits[CURR_POS]>=1) new_node->hits[CURR_POS] = (dad->hits[CURR_POS])-1; if (dad->leaf_hits[CURR_POS]>=1) new_node->leaf_hits[PREV_POS] = (dad->leaf_hits[PREV_POS])-1; /* link the child into father's kids list -> insert it at the beginning, * is much faster */ if (dad->kids) { dad->kids->prev = new_node; new_node->next = dad->kids; } dad->kids = new_node; new_node->branch = dad->branch; new_node->prev = dad; return new_node; } #define is_hot_non_leaf(_node) \ ( (_node)->hits[PREV_POS]>=root->max_hits>>2 ||\ (_node)->hits[CURR_POS]>=root->max_hits>>2 ||\ (((_node)->hits[PREV_POS]+(_node)->hits[CURR_POS])>>1)>=\ root->max_hits>>2 ) #define is_hot_leaf(_node) \ ( (_node)->leaf_hits[PREV_POS]>=root->max_hits ||\ (_node)->leaf_hits[CURR_POS]>=root->max_hits ||\ (((_node)->leaf_hits[PREV_POS]+(_node)->leaf_hits[CURR_POS])>>1)>=\ root->max_hits ) #define is_warm_leaf(_node) \ ( (_node)->hits[CURR_POS]>=root->max_hits>>2 ) #define MAX_TYPE_VAL(_x) \ (( (1<<(8*sizeof(_x)-1))-1 )|( (1<<(8*sizeof(_x)-1)) )) int is_node_hot_leaf(struct ip_node *node) { return is_hot_leaf(node); } /* mark with one more hit the given IP address - */ struct ip_node* mark_node(unsigned char *ip,int ip_len, struct ip_node **father,unsigned char *flag) { struct ip_node *node; struct ip_node *kid; int byte_pos; kid = root->entries[ ip[0] ].node; node = 0; byte_pos = 0; LM_DBG("search on branch %d (top=%p)\n", ip[0],kid); /* search into the ip tree the longest prefix matching the given IP */ while (kid && byte_posbyte!=(unsigned char)ip[byte_pos]) { kid = kid->next; } if (kid) { node = kid; kid = kid->kids; byte_pos++; } } LM_DBG("only first %d were matched!\n",byte_pos); *flag = 0; *father = 0; /* what have we found? */ if (byte_pos==ip_len) { /* we found the entire address */ node->flags |= NODE_IPLEAF_FLAG; /* increment it, but be careful not to overflow the value */ if(node->leaf_hits[CURR_POS]leaf_hits[CURR_POS])-1) node->leaf_hits[CURR_POS]++; /* becoming red node? */ if ( (node->flags&NODE_ISRED_FLAG)==0 ) { if (is_hot_leaf(node) ) { *flag |= RED_NODE|NEWRED_NODE; node->flags |= NODE_ISRED_FLAG; } } else { *flag |= RED_NODE; } } else if (byte_pos==0) { /* we hit an empty branch in the IP tree */ assert(node==0); /* add a new node containing the start byte of the IP address */ if ( (node=new_ip_node(ip[0]))==0) return 0; node->hits[CURR_POS] = 1; node->branch = ip[0]; *flag = NEW_NODE ; /* set this node as root of the branch starting with first byte of IP*/ root->entries[ ip[0] ].node = node; } else{ /* only a non-empty prefix of the IP was found */ if ( node->hits[CURR_POS]hits[CURR_POS])-1 ) node->hits[CURR_POS]++; if ( is_hot_non_leaf(node) ) { /* we have to split the node */ *flag = NEW_NODE ; LM_DBG("splitting node %p [%d]\n",node,node->byte); *father = node; node = split_node(node,ip[byte_pos]); } else { /* to reduce memory usage, force to expire non-leaf nodes if they * have just a few hits -> basically, don't update the timer for * them the nr of hits is small */ if ( !is_warm_leaf(node) ) *flag = NO_UPDATE; } } return node; } /* remove and destroy a IP node along with its subtree */ void remove_node(struct ip_node *node) { LM_DBG("destroying node %p\n",node); /* is it a branch root node? (these nodes have no prev (father)) */ if (node->prev==0) { assert(root->entries[node->byte].node==node); root->entries[node->byte].node = 0; } else { /* unlink it from kids list */ if (node->prev->kids==node) /* it's the head of the list! */ node->prev->kids = node->next; else /* it's somewhere in the list */ node->prev->next = node->next; if (node->next) node->next->prev = node->prev; } /* destroy the node */ node->next = node->prev = 0; destroy_ip_node(node); } opensips-2.2.2/modules/pike/ip_tree.h000066400000000000000000000050551300170765700175700ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) * 2004-11-05 adaptiv init lock (bogdan) * 2005-06-02 flags added to ip_node structure (bogdan) * 2008-04-17 the leaf nodes memorize (via flags) if they are in RED state * (detected) or not -> better logging and MI (bogdan) */ #ifndef _IP_TREE_H #define _IP_TREE_H #include #include "../../locking.h" #include "timer.h" #define NEW_NODE (1<<0) #define RED_NODE (1<<1) #define NEWRED_NODE (1<<2) #define NO_UPDATE (1<<3) #define MAX_IP_BRANCHES 256 #define PREV_POS 0 #define CURR_POS 1 #define NODE_EXPIRED_FLAG (1<<0) #define NODE_INTIMER_FLAG (1<<1) #define NODE_IPLEAF_FLAG (1<<2) #define NODE_ISRED_FLAG (1<<3) struct ip_node { unsigned int expires; unsigned short leaf_hits[2]; unsigned short hits[2]; unsigned char byte; unsigned char branch; volatile unsigned short flags; struct list_link timer_ll; struct ip_node *prev; struct ip_node *next; struct ip_node *kids; }; struct ip_tree { struct entry { struct ip_node *node; int lock_idx; } entries[MAX_IP_BRANCHES]; unsigned short max_hits; gen_lock_set_t *entry_lock_set; }; #define ll2ipnode(ptr) \ ((struct ip_node*)((char *)(ptr)-\ (unsigned long)(&((struct ip_node*)0)->timer_ll))) int init_ip_tree(int); void destroy_ip_tree(); struct ip_node* mark_node( unsigned char *ip, int ip_len, struct ip_node **father, unsigned char *flag); void remove_node(struct ip_node *node); int is_node_hot_leaf(struct ip_node *node); void lock_tree_branch(unsigned char b); void unlock_tree_branch(unsigned char b); struct ip_node* get_tree_branch(unsigned char b); #endif opensips-2.2.2/modules/pike/pike.c000066400000000000000000000121321300170765700170560ustar00rootroot00000000000000/* * PIKE module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-11 converted to the new locking interface: locking.h -- * major changes (andrei) * 2003-03-16 flags export parameter added (janakj) * 2008-04-17 new parameter to control the module's log regarding the * blocking/unblocking of IPs (bogdan) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../dprint.h" #include "../../script_cb.h" #include "../../ut.h" #include "../../mem/shm_mem.h" #include "../../evi/evi_modules.h" #include "../../timer.h" #include "../../locking.h" #include "ip_tree.h" #include "timer.h" #include "pike_mi.h" #include "pike_funcs.h" static int pike_init(void); static int pike_exit(void); /* parameters */ static int time_unit = 2; static int max_reqs = 30; static char *pike_route_s = NULL; int timeout = 120; int pike_log_level = L_WARN; /* global variables */ gen_lock_t* timer_lock=0; struct list_link* timer = 0; /* event id */ static str pike_block_event = str_init("E_PIKE_BLOCKED"); event_id_t pike_event_id = EVI_ERROR; static cmd_export_t cmds[]={ {"pike_check_req", (cmd_function)pike_check_req, 0, 0, 0, REQUEST_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[]={ {"sampling_time_unit", INT_PARAM, &time_unit}, {"reqs_density_per_unit", INT_PARAM, &max_reqs}, {"remove_latency", INT_PARAM, &timeout}, {"pike_log_level", INT_PARAM, &pike_log_level}, {"check_route", STR_PARAM, &pike_route_s}, {0,0,0} }; static mi_export_t mi_cmds [] = { {MI_PIKE_LIST, "lists the nodes in the pike tree", mi_pike_list, MI_NO_INPUT_FLAG, 0, 0 }, {MI_PIKE_RM, "remove a node from the tree", mi_pike_rm, 0, 0, 0 }, {0,0,0,0,0,0} }; struct module_exports exports= { "pike", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, 0, params, 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ pike_init, /* module initialization function */ (response_function) 0, (destroy_function) pike_exit, /* module exit function */ 0 /* per-child init function */ }; static int pike_init(void) { int rt; LM_INFO("initializing...\n"); /* alloc the timer lock */ timer_lock=lock_alloc(); if (timer_lock==0) { LM_ERR(" alloc locks failed!\n"); goto error1; } /* init the lock */ if (lock_init(timer_lock)==0){ LM_ERR(" init lock failed\n"); goto error1; } /* init the IP tree */ if ( init_ip_tree(max_reqs)!=0 ) { LM_ERR(" ip_tree creation failed!\n"); goto error2; } /* init timer list */ timer = (struct list_link*)shm_malloc(sizeof(struct list_link)); if (timer==0) { LM_ERR(" cannot alloc shm mem for timer!\n"); goto error3; } timer->next = timer->prev = timer; /* registering timing functions */ register_timer( "pike-clean", clean_routine , 0, 1 , TIMER_FLAG_DELAY_ON_DELAY); register_timer( "pike-swap", swap_routine , 0, time_unit, TIMER_FLAG_DELAY_ON_DELAY ); if (pike_route_s && *pike_route_s) { rt = get_script_route_ID_by_name( pike_route_s, rlist, RT_NO); if (rt<1) { LM_ERR("route <%s> does not exist\n",pike_route_s); return -1; } /* register the script callback to get all requests and replies */ if (register_script_cb( run_pike_route , PARSE_ERR_CB|REQ_TYPE_CB|RPL_TYPE_CB|PRE_SCRIPT_CB, (void*)(long)rt )!=0 ) { LM_ERR("failed to register script callbacks\n"); goto error3; } } if((pike_event_id = evi_publish_event(pike_block_event)) == EVI_ERROR) LM_ERR("cannot register pike flood start event\n"); return 0; error3: destroy_ip_tree(); error2: lock_destroy(timer_lock); error1: if (timer_lock) lock_dealloc(timer_lock); timer_lock = 0; return -1; } static int pike_exit(void) { LM_INFO("destroying...\n"); /* destroy semaphore */ if (timer_lock) { lock_destroy(timer_lock); lock_dealloc(timer_lock); } /* empty the timer list head */ if (timer) { shm_free(timer); timer = 0; } /* destroy the IP tree */ destroy_ip_tree(); return 0; } opensips-2.2.2/modules/pike/pike_funcs.c000066400000000000000000000236071300170765700202650ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 converted to the new locking interface: locking.h -- * major changes (andrei) * 2005-05-02 flags field added to node structure -better sync between timer * and worker processes; some races eliminated (bogdan) * 2008-04-17 new parameter to control the module's log regarding the * blocking/unblocking of IPs (bogdan) * 2008-04-17 the leaf nodes memorize (via flags) if they are in RED state * (detected) or not -> better logging and MI (bogdan) */ #include #include #include #include #include "../../mem/shm_mem.h" #include "../../evi/evi_modules.h" #include "../../locking.h" #include "../../timer.h" #include "../../ip_addr.h" #include "../../resolve.h" #include "../../action.h" #include "../../route.h" #include "../../script_cb.h" #include "ip_tree.h" #include "pike_funcs.h" #include "timer.h" extern gen_lock_t* timer_lock; extern struct list_link* timer; extern int timeout; extern int pike_log_level; extern int pike_start_level; extern int pike_stop_level; extern event_id_t pike_event_id; static inline void pike_raise_event(char *ip) { evi_params_p list; str ip_str; static str parameter_str = { "ip", 2 }; if (pike_event_id == EVI_ERROR) { LM_ERR("event not registered %d\n", pike_event_id); return; } if (evi_probe_event(pike_event_id)) { if (!(list = evi_get_params())) return; ip_str.s = ip; ip_str.len = strlen(ip); if (evi_param_add_str(list, ¶meter_str, &ip_str)) { LM_ERR("unable to add socket parameter\n"); evi_free_params(list); return; } if (evi_raise_event(pike_event_id, list)) { LM_ERR("unable to send event %d\n", pike_event_id); } } else { LM_DBG("no event sent\n"); } } int pike_check_req(struct sip_msg *msg) { struct ip_node *node; struct ip_node *father; unsigned char flags; struct ip_addr* ip; #ifdef _test /* get the ip address from second via */ if (parse_headers(msg, HDR_VIA1_F, 0)!=0 ) return -1; if (msg->via1==0 ) return -1; /* convert from string to ip_addr */ ip = str2ip( &msg->via1->host ); if (ip==0) return -1; #else ip = &(msg->rcv.src_ip); #endif /* first lock the proper tree branch and mark the IP with one more hit*/ lock_tree_branch( ip->u.addr[0] ); node = mark_node( ip->u.addr, ip->len, &father, &flags); if (node==0) { unlock_tree_branch( ip->u.addr[0] ); /* even if this is an error case, we return true in script to avoid * considering the IP as marked (bogdan) */ return 1; } LM_DBG("src IP [%s],node=%p; hits=[%d,%d],[%d,%d] node_flags=%d" " func_flags=%d\n", ip_addr2a( ip ), node, node->hits[PREV_POS],node->hits[CURR_POS], node->leaf_hits[PREV_POS],node->leaf_hits[CURR_POS], node->flags, flags); /* update the timer */ lock_get(timer_lock); if ( flags&NEW_NODE ) { /* put this node into the timer list and remove its father only if this has one kid and is not a LEAF_NODE*/ node->expires = get_ticks() + timeout; append_to_timer( timer, &(node->timer_ll) ); node->flags |= NODE_INTIMER_FLAG; if (father) { LM_DBG("father %p: flags=%d kids->next=%p\n", father,father->flags,father->kids->next); if (!(father->flags&NODE_IPLEAF_FLAG) && !father->kids->next){ /* debug */ assert( has_timer_set(&(father->timer_ll)) && (father->flags&(NODE_EXPIRED_FLAG|NODE_INTIMER_FLAG)) ); /* if the node is maked as expired by timer, let the timer * to finish and remove the node */ if ( !(father->flags&NODE_EXPIRED_FLAG) ) { remove_from_timer( timer, &(father->timer_ll) ); father->flags &= ~NODE_INTIMER_FLAG; } else { father->flags &= ~NODE_EXPIRED_FLAG; } } } } else { /* update the timer -> in timer can be only nodes * as IP-leaf(complete address) or tree-leaf */ if (node->flags&NODE_IPLEAF_FLAG || node->kids==0) { /* tree leafs which are not potential red nodes are not update in * order to make them to expire */ /* debug */ assert( has_timer_set(&(node->timer_ll)) && (node->flags&(NODE_EXPIRED_FLAG|NODE_INTIMER_FLAG)) ); /* if node exprired, ignore the current hit and let is * expire in timer process */ if ( !(flags&NO_UPDATE) && !(node->flags&NODE_EXPIRED_FLAG) ) { node->expires = get_ticks() + timeout; update_in_timer( timer, &(node->timer_ll) ); } } else { /* debug */ assert( !has_timer_set(&(node->timer_ll)) && !(node->flags&(NODE_INTIMER_FLAG|NODE_EXPIRED_FLAG)) ); /* debug */ assert( !(node->flags&NODE_IPLEAF_FLAG) && node->kids ); } } /*print_timer_list( timer );*/ /* debug*/ lock_release(timer_lock); unlock_tree_branch( ip->u.addr[0] ); /*print_tree( 0 );*/ /* debug */ if (flags&RED_NODE) { if (flags&NEWRED_NODE) { LM_GEN1( pike_log_level, "PIKE - BLOCKing ip %s, node=%p\n",ip_addr2a(ip),node); pike_raise_event(ip_addr2a(ip)); return -2; } return -1; } return 1; } int run_pike_route( struct sip_msg *msg, void *rt ) { /* the check was dropped */ if ( run_top_route( rlist[(int)(long)rt].a, msg)&ACT_FL_DROP) return SCB_RUN_ALL; /* run the check */ if (pike_check_req(msg)<0) return SCB_DROP_MSG; return SCB_RUN_ALL; } void clean_routine(unsigned int ticks , void *param) { static unsigned char mask[32]; /* 256 positions mask */ struct list_link head; struct list_link *ll; struct ip_node *dad; struct ip_node *node; int i; /* LM_DBG("entering (%d)\n",ticks); */ /* before locking check first if the list is not empty and if can * be at least one element removed */ if ( is_list_empty( timer )) return; /* quick exit */ /* get the expired elements */ lock_get( timer_lock ); /* check again for empty list */ if (is_list_empty(timer) || (ll2ipnode(timer->next)->expires>ticks )){ lock_release( timer_lock ); return; } check_and_split_timer( timer, ticks, &head, mask); /*print_timer_list(timer);*/ /* debug */ lock_release( timer_lock ); /*print_tree( 0 );*/ /*debug*/ /* got something back? */ if ( is_list_empty(&head) ) return; /* process what we got -> don't forget to lock the tree!! */ for(i=0;i skip it */ if ( ((mask[i>>3])&(1<<(i&0x07)))==0 ) continue; lock_tree_branch( i ); for( ll=head.next ; ll!=&head ; ) { node = ll2ipnode( ll ); ll = ll->next; /* skip nodes from a different branch */ if (node->branch!=i) continue; /* unlink the node -> the list will get shorter and it will be * faster for the next branches to process it */ ll->prev->prev->next = ll; ll->prev = ll->prev->prev; node->expires = 0; node->timer_ll.prev = node->timer_ll.next = 0; if ( node->flags&NODE_EXPIRED_FLAG ) node->flags &= ~NODE_EXPIRED_FLAG; else continue; /* process the node */ LM_DBG("clean node %p (kids=%p; hits=[%d,%d];leaf=[%d,%d])\n", node,node->kids, node->hits[PREV_POS],node->hits[CURR_POS], node->leaf_hits[PREV_POS],node->leaf_hits[CURR_POS]); /* if it's a node, leaf for an ipv4 address inside an * ipv6 address -> just remove it from timer it will be deleted * only when all its kids will be deleted also */ if (node->kids) { assert( node->flags&NODE_IPLEAF_FLAG ); node->flags &= ~NODE_IPLEAF_FLAG; node->leaf_hits[CURR_POS] = 0; } else { /* if the node has no prev, means its a top branch node -> just * removed and destroy it */ if (node->prev!=0) { /* if this is the last kid, we have to put the father * into timer list */ if (node->prev->kids==node && node->next==0) { /* this is the last kid node */ dad = node->prev; /* put it in the list only if it's not an IP leaf * (in this case, it's already there) */ if ( !(dad->flags&NODE_IPLEAF_FLAG) ) { lock_get(timer_lock); dad->expires = get_ticks() + timeout; assert( !has_timer_set(&(dad->timer_ll)) ); append_to_timer( timer, &(dad->timer_ll)); dad->flags |= NODE_INTIMER_FLAG; lock_release(timer_lock); } else { assert( has_timer_set(&(dad->timer_ll)) ); } } } LM_DBG("rmv node %p[%d] \n", node,node->byte); /* del the node */ remove_node( node); } } /* for all expired elements */ unlock_tree_branch( i ); } /* for all branches */ } static inline void refresh_node( struct ip_node *node) { for( ; node ; node=node->next ) { node->hits[PREV_POS] = node->hits[CURR_POS]; node->hits[CURR_POS] = 0; node->leaf_hits[PREV_POS] = node->leaf_hits[CURR_POS]; node->leaf_hits[CURR_POS] = 0; if ( node->flags&NODE_ISRED_FLAG && !is_node_hot_leaf(node) ) { node->flags &= ~(NODE_ISRED_FLAG); LM_GEN1( pike_log_level,"PIKE - UNBLOCKing node %p\n",node); } if (node->kids) refresh_node( node->kids ); } } void swap_routine( unsigned int ticks, void *param) { struct ip_node *node; int i; /* LM_DBG("entering \n"); */ for(i=0;i #include "../../resolve.h" #include "ip_tree.h" #include "pike_mi.h" #define IPv6_LEN 16 #define IPv4_LEN 4 #define MAX_IP_LEN IPv6_LEN static struct ip_node *ip_stack[MAX_IP_LEN]; extern int pike_log_level; static inline void print_ip_stack( int level, struct mi_node *node) { if (level==IPv6_LEN) { /* IPv6 */ addf_mi_node_child( node, 0, 0, 0, "%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x", ip_stack[0]->byte, ip_stack[1]->byte, ip_stack[2]->byte, ip_stack[3]->byte, ip_stack[4]->byte, ip_stack[5]->byte, ip_stack[6]->byte, ip_stack[7]->byte, ip_stack[8]->byte, ip_stack[9]->byte, ip_stack[10]->byte, ip_stack[11]->byte, ip_stack[12]->byte, ip_stack[13]->byte, ip_stack[14]->byte, ip_stack[15]->byte ); } else if (level==IPv4_LEN) { /* IPv4 */ addf_mi_node_child( node, 0, 0, 0, "%d.%d.%d.%d", ip_stack[0]->byte, ip_stack[1]->byte, ip_stack[2]->byte, ip_stack[3]->byte ); } else { LM_CRIT("leaf node at depth %d!!!\n", level); return; } } static void print_red_ips( struct ip_node *ip, int level, struct mi_node *node) { struct ip_node *foo; if (level==MAX_IP_LEN) { LM_CRIT("tree deeper than %d!!!\n", MAX_IP_LEN); return; } ip_stack[level] = ip; /* is the node marked red? */ if ( ip->flags&NODE_ISRED_FLAG) print_ip_stack(level+1,node); /* go through all kids */ foo = ip->kids; while(foo){ print_red_ips( foo, level+1, node); foo = foo->next; } } struct mi_root* mi_pike_rm(struct mi_root *cmd, void *param) { struct mi_node *mn; struct ip_node *node; struct ip_node *kid; struct ip_addr *ip; int byte_pos; mn = cmd->node.kids; if (mn==NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); ip = str2ip(&mn->value); if (ip==0) return init_mi_tree( 500, "Bad IP", 6); node = 0; byte_pos = 0; kid = get_tree_branch((unsigned char)ip->u.addr[byte_pos]); /* pilfered from ip_tree.c:mark_node(..) */ while (kid && byte_pos < ip->len) { while (kid && kid->byte!=(unsigned char)ip->u.addr[byte_pos]) { kid = kid->next; } if (kid) { node = kid; kid = kid->kids; byte_pos++; } } /* If all octets weren't matched, 404 */ if (byte_pos!=ip->len) { return init_mi_tree( 404, "Match not found", 15); } /* If the node exists, check to see if it's really blocked */ if (!(node->flags&NODE_ISRED_FLAG)) { return init_mi_tree( 400, "IP not blocked", 14); } /* reset the node block flag and counters */ node->flags &= ~(NODE_ISRED_FLAG); node->hits[PREV_POS] = 0; node->hits[CURR_POS] = 0; node->leaf_hits[PREV_POS] = 0; node->leaf_hits[CURR_POS] = 0; LM_GEN1(pike_log_level, "PIKE - UNBLOCKing ip %s, node=%p\n",ip_addr2a(ip),node); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } /* Syntax of "pike_list" : no nodes */ struct mi_root* mi_pike_list(struct mi_root* cmd_tree, void* param) { struct mi_root* rpl_tree; struct ip_node *ip; int i; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; rpl_tree->node.flags |= MI_IS_ARRAY; for( i=0 ; inode ); unlock_tree_branch(i); } return rpl_tree; } opensips-2.2.2/modules/pike/pike_mi.h000066400000000000000000000022561300170765700175560ustar00rootroot00000000000000/* * Header file for PIKE MI functions * * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-12-05 created (bogdan) */ #ifndef _PIKE_MI_H_ #define _PIKE_MI_H_ #include "../../mi/mi.h" #define MI_PIKE_LIST "pike_list" #define MI_PIKE_RM "pike_rm" struct mi_root* mi_pike_list(struct mi_root* cmd_tree, void* param); struct mi_root* mi_pike_rm(struct mi_root *cmd, void *param); #endif opensips-2.2.2/modules/pike/timer.c000066400000000000000000000053771300170765700172630ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-05-02 flags field added to node structure -better sync between timer * and worker processes; some races eliminated (bogdan) */ #include #include "../../dprint.h" #include "timer.h" #include "ip_tree.h" void append_to_timer(struct list_link *head, struct list_link *new_ll ) { LM_DBG("%p in %p(%p,%p)\n", new_ll, head,head->prev,head->next); assert( !has_timer_set(new_ll) ); new_ll->prev = head->prev; head->prev->next = new_ll; head->prev = new_ll; new_ll->next = head; } void remove_from_timer(struct list_link *head, struct list_link *ll) { LM_DBG("%p from %p(%p,%p)\n", ll, head,head->prev,head->next); assert( has_timer_set(ll) ); ll->next->prev = ll->prev; ll->prev->next = ll->next; ll->next = ll->prev = 0; } /* "head" list MUST not be empty */ void check_and_split_timer(struct list_link *head, unsigned int time, struct list_link *split, unsigned char *mask) { struct list_link *ll; struct ip_node *node; unsigned char b; int i; /* reset the mask */ for(i=0;i<32;mask[i++]=0); ll = head->next; while( ll!=head && (node=ll2ipnode(ll))->expires<=time) { LM_DBG("splitting %p(%p,%p)node=%p\n", ll,ll->prev,ll->next, node); /* mark the node as expired and un-mark it as being in timer list */ node->flags |= NODE_EXPIRED_FLAG; node->flags &= ~NODE_INTIMER_FLAG; b = node->branch; ll=ll->next; /*LM_DBG("b=%d; [%d,%d]\n", b,b>>3,1<<(b&0x07));*/ mask[b>>3] |= (1<<(b&0x07)); } if (ll==head->next) { /* nothing to return */ split->next = split->prev = split; } else { /* the detached list begins with current beginning */ split->next = head->next; split->next->prev = split; /* and we mark the end of the split list */ split->prev = ll->prev; split->prev->next = split; /* the shortened list starts from where we suspended */ head->next = ll; ll->prev = head; } LM_DBG("succ. to split (h=%p)(p=%p,n=%p)\n", head,head->prev,head->next); return; } opensips-2.2.2/modules/pike/timer.h000066400000000000000000000027121300170765700172560ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * */ #ifndef _PIKE_TIMER_H #define _PIKE_TIMER_H struct list_link { struct list_link *next; struct list_link *prev; }; #define has_timer_set(_ll) \ ((_ll)->prev || (_ll)->next) #define is_list_empty(_head) \ ((_head)->next == (_head)) #define update_in_timer( _head, _ll) \ do { \ remove_from_timer( _head, _ll);\ append_to_timer( _head, _ll); \ }while(0) void append_to_timer(struct list_link *head, struct list_link *ll ); void remove_from_timer(struct list_link *head, struct list_link *ll); void check_and_split_timer(struct list_link *head, unsigned int time, struct list_link *split, unsigned char *mask); #endif opensips-2.2.2/modules/presence/000077500000000000000000000000001300170765700166375ustar00rootroot00000000000000opensips-2.2.2/modules/presence/Makefile000066400000000000000000000005371300170765700203040ustar00rootroot00000000000000# $Id$ # # Presence Agent # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=presence.so DEFS+=-I$(SYSBASE)/include/libxml2 -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS=-L$(SYSBASE)/include/lib -L$(LOCALBASE)/lib -lxml2 include ../../Makefile.modules opensips-2.2.2/modules/presence/README000066400000000000000000000630401300170765700175220ustar00rootroot00000000000000Presence Module Anca-Maria Vamanu Edited by Ovidiu Sas Edited by Anca-Maria Vamanu Edited by Saul Ibarra Corretge Copyright © 2006 Voice Sistem SRL Revision History Revision $Revision: 9371 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url(str) 1.3.2. presentity_table(str) 1.3.3. active_watchers_table(str) 1.3.4. watchers_table(str) 1.3.5. clean_period (int) 1.3.6. db_update_period (int) 1.3.7. expires_offset (int) 1.3.8. max_expires_subscribe (int) 1.3.9. max_expires_publish (int) 1.3.10. server_address (str) 1.3.11. fallback2db (int) 1.3.12. subs_htable_size (int) 1.3.13. pres_htable_size (int) 1.3.14. enable_sphere_check (int) 1.3.15. waiting_subs_daysno (int) 1.3.16. mix_dialog_presence (int) 1.3.17. bla_presentity_spec (str) 1.3.18. bla_fix_remote_target (int) 1.3.19. notify_offline_body (int) 1.3.20. end_sub_on_timeout (int) 1.4. Exported Functions 1.4.1. handle_publish(char* sender_uri) 1.4.2. handle_subscribe(char* force_active) 1.5. Exported MI Functions 1.5.1. refreshWatchers 1.5.2. cleanup 1.5.3. pres_phtable_list 1.5.4. subs_phtable_list 1.6. Exported Events 1.6.1. E_PRESENCE_PUBLISH 1.7. Installation 2. Developer Guide 2.1. bind_presence(presence_api_t* api) 2.2. add_event 2.3. get_rules_doc 2.4. get_auth_status 2.5. apply_auth_nbody 2.6. agg_nbody 2.7. free_body 2.8. aux_body_processing 2.9. aux_free_body 2.10. evs_publ_handl 2.11. evs_subs_handl 2.12. contains_event 2.13. get_event_list 2.14. update_watchers_status 2.15. get_sphere 2.16. contains_presence List of Examples 1.1. Set db_url parameter 1.2. Set presentity_table parameter 1.3. Set active_watchers_table parameter 1.4. Set watchers_table parameter 1.5. Set clean_period parameter 1.6. Set db_update_period parameter 1.7. Set expires_offset parameter 1.8. Set max_expires_subscribe parameter 1.9. Set max_expires_publish parameter 1.10. Set server_address parameter 1.11. Set fallback2db parameter 1.12. Set subs_htable_size parameter 1.13. Set pres_htable_size parameter 1.14. Set enable_sphere_check parameter 1.15. Set waiting_subs_daysno parameter 1.16. Set mix_dialog_presence parameter 1.17. Set bla_presentity_spec parameter 1.18. Set bla_fix_remote_target parameter 1.19. Set notify_offline_body parameter 1.20. Set end_sub_on_timeout parameter 1.21. handle_publish usage 1.22. handle_subscribe usage 2.1. presence_api_t structure Chapter 1. Admin Guide 1.1. Overview The modules handles PUBLISH and SUBSCRIBE messages and generates NOTIFY messages in a general, event independent way. It allows registering events to it from other OpenSIPS modules. Events that can currently be added to it are: presence, presence.winfo, dialog;sla from presence_xml module and message-summary from presence_mwi module. The modules uses database storage. It has later been improved with memory caching operations to improve performance. The Subscribe dialog information are stored in memory and are periodically updated in database, while for Publish only the presence or absence of stored info for a certain resource is maintained in memory to avoid unnecessary, costly db operations. It is possible to configure a fallback to database mode(by setting module parameter "fallback2db"). In this mode, in case a searched record is not found in cache, the search is continued in database. This is useful for an architecture in which processing and memory load might be divided on more machines using the same database. The module can also work only with the functionality of a library, with no message processing and generation, but used only for the exported functions. This mode of operation is enabled if the db_url parameter is not set to any value. The server follows the specifications in: RFC3265, RFC3856, RFC3857, RFC3858. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * a database module. * signaling. 1.2.2. External Libraries or Applications * libxml-dev. 1.3. Exported Parameters 1.3.1. db_url(str) The database url. If set, the module is a fully operational presence server. Otherwise, it is used as a 'library', for its exported functions. Default value is “NULLâ€. Example 1.1. Set db_url parameter ... modparam("presence", "db_url", "mysql://opensips:opensipsrw@192.168.2.132/opensips") ... 1.3.2. presentity_table(str) The name of the db table where Publish information are stored. Default value is “presentityâ€. Example 1.2. Set presentity_table parameter ... modparam("presence", "presentity_table", "presentity") ... 1.3.3. active_watchers_table(str) The name of the db table where active subscription information are stored. Default value is “active_watchersâ€. Example 1.3. Set active_watchers_table parameter ... modparam("presence", "active_watchers_table", "active_watchers") ... 1.3.4. watchers_table(str) The name of the db table where subscription states are stored. Default value is “watchersâ€. Example 1.4. Set watchers_table parameter ... modparam("presence", "watchers_table", "watchers") ... 1.3.5. clean_period (int) The period at which to clean the expired subscription dialogs. Default value is “100â€. A zero or negative value disables this activity. Example 1.5. Set clean_period parameter ... modparam("presence", "clean_period", 100) ... 1.3.6. db_update_period (int) The period at which to synchronize cached subscriber info with the database. Default value is “100â€. A zero or negative value disables synchronization. Example 1.6. Set db_update_period parameter ... modparam("presence", "db_update_period", 100) ... 1.3.7. expires_offset (int) The extra time to store a subscription/publication. Default value is “0â€. Example 1.7. Set expires_offset parameter ... modparam("presence", "expires_offset", 10) ... 1.3.8. max_expires_subscribe (int) The the maximum admissible expires value for SUBSCRIBE messages. Default value is “3600â€. Example 1.8. Set max_expires_subscribe parameter ... modparam("presence", "max_expires_subscribe", 3600) ... 1.3.9. max_expires_publish (int) The the maximum admissible expires value for PUBLISH messages. Default value is “3600â€. Example 1.9. Set max_expires_publish parameter ... modparam("presence", "max_expires_publish", 3600) ... 1.3.10. server_address (str) The presence server address which will become the value of Contact header filed for 200OK replies to Subscribe and Publish and in Notify messages. Example 1.10. Set server_address parameter ... modparam("presence", "server_address", "sip:10.10.10.10:5060") ... 1.3.11. fallback2db (int) Setting this parameter enables a fallback to db mode of operation. In this mode, in case a searched record is not found in cache, the search is continued in database. Useful for an architecture in which processing and memory load might be divided on more machines using the same database. Example 1.11. Set fallback2db parameter ... modparam("presence", "fallback2db", 1) ... 1.3.12. subs_htable_size (int) The size of the hash table to store subscription dialogs. This parameter will be used as the power of 2 when computing table size. Default value is “9 (512)â€. Example 1.12. Set subs_htable_size parameter ... modparam("presence", "subs_htable_size", 11) ... 1.3.13. pres_htable_size (int) The size of the hash table to store publish records. This parameter will be used as the power of 2 when computing table size. Default value is “9 (512)â€. Example 1.13. Set pres_htable_size parameter ... modparam("presence", "pres_htable_size", 11) ... 1.3.14. enable_sphere_check (int) This parameter is a flag that should be set if permission rules include sphere checking. The sphere information is expected to be present in the RPID body published by the presentity. The flag is introduced as this check requires extra processing that should be avoided if this feature is not supported by the clients. Default value is “0 â€. Example 1.14. Set enable_sphere_check parameter ... modparam("presence", "enable_sphere_check", 1) ... 1.3.15. waiting_subs_daysno (int) The number of days to keep the record of a subscription in server database if the subscription is in pending or waiting state (no authorization policy was defined for it or the target user did not register sice the subscription and was not informed about it). Default value is “3†days. Maximum accepted value is 30 days. Example 1.15. Set waiting_subs_daysno parameter ... modparam("presence", "waiting_subs_daysno", 2) ... 1.3.16. mix_dialog_presence (int) This module parameter enables a very nice feature in the presence server - generating presence information from dialogs state. If this parameter is set, the presence server will tell you if a buddy is in a call even if his phone did not send a presence Publish with this information. You will need to load the dialoginfo modules, presence_dialoginfo, pua_dialoginfo, dialog and pua. Default value is “0â€. Example 1.16. Set mix_dialog_presence parameter ... modparam("presence", "mix_dialog_presence", 1) ... 1.3.17. bla_presentity_spec (str) By default the presentity uri for BLA subscribes (event=dialog;sla) is computed from contact username + from domain. In some cases though, this way of computing the presentity might not be right ( for example if you have a SBC in front that masquerades the contact). So we added this parameter that allows defining a custom uri to be used as presentity uri for BLA subscribes. You should set this parameter to the name of a pseudovariable and then set this pseudovariable to the desired URI before calling the function handle_subscribe. Default value is “NULLâ€. Example 1.17. Set bla_presentity_spec parameter ... modparam("presence", "bla_presentity_spec", "$var(bla_pres)") ... 1.3.18. bla_fix_remote_target (int) Polycom has a bug in the bla implementation. It inserts the remote IP contact in the Notify body and when a phone picks up a call put on hold by another phone in the same BLA group, it sends an Invite directly to the remote IP. OpenSIPS BLA server tries to prevent this by replacing the IP contact with the domain, when this is possible. In some cases(configurations) however this is not desirable, so this parameter was introduced to disable this behaviour when needed. Default value is “1â€. Example 1.18. Set bla_fix_remote_target parameter ... modparam("presence", "bla_fix_remote_target", 0) ... 1.3.19. notify_offline_body (int) If this parameter is set, when no published info is found for a user, the presence server will generate a dummy body with status 'closed' and use it when sending Notify, instead of notifying with no body. Default value is “0â€. Example 1.19. Set notify_offline_body parameter ... modparam("presence", "notify_offline_body", 1) ... 1.3.20. end_sub_on_timeout (int) If a presence subscription should be automatically terminated (destroyed) when receiving a SIP timeout (408) for a sent NOTIFY requests. Default value is “1†(enabled). Example 1.20. Set end_sub_on_timeout parameter ... modparam("presence", "end_sub_on_timeout", 0) ... 1.4. Exported Functions 1.4.1. handle_publish(char* sender_uri) The function handles PUBLISH requests. It stores and updates published information in database and calls functions to send NOTIFY messages when changes in the published information occur. It takes one argument -> sender_uri. The parameter was added for enabling BLA implementation. If present, Notification of a change in published state is not sent to the respective uri even though a subscription exists. It should be taken from the Sender header. It was left at the decision of the administrator whether or not to transmit the content of this header as parameter for handle_publish, to prevent security problems. This function can be used from REQUEST_ROUTE. Return code: * 1 - if success. * -1 - if error. The module sends an appropriate stateless reply in all cases. Example 1.21. handle_publish usage ... if(is_method("PUBLISH")) { if($hdr(Sender)!= NULL) handle_publish("$hdr(Sender)"); else handle_publish(); } ... 1.4.2. handle_subscribe(char* force_active) This function is to be used for handling SUBSCRIBE requests. It stores or updates the watcher/subscriber information in database. Additionally, in response to initial SUBSCRIBE requests (creating a new subscription session), the function also sends back the NOTIFY (with the presence information) to the wathcer/subscriber. The function may take on parameter that controlls what is the default policy (of the presentity) on accepting new subscriptions (accept or reject) - of course, this parameter makes sense only when using a presence configuration with privacy rules enabled (force_active parameter in presence_xml module is not set). There are scenarios where the presentity (the party you subscribe to) can not upload an XCAP document with it's privacy rules (to controll which watchers are allowed to subscribe to it). In such case, from script level, you can force the presence server to consider the current subscription allowed (with Subscription-Status:active) by calling handle_subscribe() function with the string parameter "1". Ex: if(uri =~ "kphone@opensips.org") handle_subscribe("1"); This function can be used from REQUEST_ROUTE. Return code: * 1 - if success. * -1 - if error. The module sends an appropriate stateless reply in all cases. Example 1.22. handle_subscribe usage ... if(method=="SUBSCRIBE") handle_subscribe(); ... 1.5. Exported MI Functions 1.5.1. refreshWatchers Triggers sending Notify messages to watchers if a change in watchers authorization or in published state occurred. Name: refreshWatchers Parameters: * presentity_uri : the uri of the user who made the change and whose watchers should be informed * event : the event package * refresh type : it distinguishes between the two different types of events that can trigger a refresh: + a change in watchers authentication: refresh type= 0 ; + a statical update in published state (either through direct update in db table or by modifying the pidf manipulation document, if pidf_manipulation parameter is set): refresh type!= 0. MI FIFO Command Format: :refreshWatchers:fifo_reply sip:11@192.168.2.132 presence 1 _empty_line_ 1.5.2. cleanup Manually triggers the cleanup functions for watchers and presentity tables. Useful if you have set clean_period to zero or less. Name: cleanup Parameters:none.emphasis> MI FIFO Command Format: :cleanup:fifo_reply _empty_line_ 1.5.3. pres_phtable_list Lists all the presentity records. Name: pres_phtable_list Parameters:none. MI FIFO Command Format: :pres_phtable_list:fifo_reply _empty_line_ 1.5.4. subs_phtable_list Lists all the subscription records. Name: subs_phtable_list Parameters:none. MI FIFO Command Format: :subs_phtable_list:fifo_reply _empty_line_ 1.6. Exported Events 1.6.1. E_PRESENCE_PUBLISH This event is raised when the presence module receives a PUBLISH message. Parameters: * user - the AOR of the user * domain - the domain * event - the type of the event published * expires - the expire value of the publish * etag - the entity tag * body - the body of the PUBLISH request 1.7. Installation The module requires 3 table in OpenSIPS database: presentity, active_watchers and watchers tables. The SQL syntax to create them can be found in presence-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. Chapter 2. Developer Guide The module provides the following functions that can be used in other OpenSIPS modules. 2.1. bind_presence(presence_api_t* api) This function binds the presence modules and fills the structure with the exported functions that represent functions adding events in presence module and functions specific for Subscribe processing. Example 2.1. presence_api_t structure ... typedef struct presence_api { add_event_t add_event; contains_event_t contains_event; search_event_t search_event; get_event_list_t get_event_list; update_watchers_t update_watchers_status; /* subs hash table handling functions */ new_shtable_t new_shtable; destroy_shtable_t destroy_shtable; insert_shtable_t insert_shtable; search_shtable_t search_shtable; delete_shtable_t delete_shtable; update_shtable_t update_shtable; /* function to duplicate a subs structure*/ mem_copy_subs_t mem_copy_subs; /* function used for update in database*/ update_db_subs_t update_db_subs; /* function to extract dialog information from a SUBSCRIBE message */ extract_sdialog_info_t extract_sdialog_info; /* function to request sphere defition for a presentity */ pres_get_sphere_t get_sphere; pres_contains_presence_t contains_presence; }presence_api_t; ... 2.2. add_event Field type: ... typedef int (*add_event_t)(pres_ev_t* event); ... This function receives as a parameter a structure with event specific information and adds it to presence event list. The structure received as a parameter: ... typedef struct pres_ev { str name; event_t* evp; str content_type; int default_expires; int type; int etag_not_new; /* * 0 - the standard mechanism (allocating new etag for each Publish) * 1 - allocating an etag only for an initial Publish */ int req_auth; get_rules_doc_t* get_rules_doc; apply_auth_t* apply_auth_nbody; is_allowed_t* get_auth_status; /* an agg_body_t function should be registered * if the event permits having multiple published * states and requires an aggregation of the information * otherwise, this field should be NULL and the last * published state is taken when constructing Notify msg */ agg_nbody_t* agg_nbody; publ_handling_t * evs_publ_handl; subs_handling_t * evs_subs_handl; free_body_t* free_body; /* sometimes it is necessary that a module make changes for a bo dy for each * active watcher (e.g. setting the "version" parameter in an XM L document. * If a module registers the aux_body_processing callback, it ge ts called for * each watcher. It either gets the body received by the PUBLISH , or the body * generated by the agg_nbody function. * The module can deceide if it makes a copy of the original bod y, which is then * manipulated, or if it works directly in the original body. If the module makes a * copy of the original body, it also has to register the aux_fr ee_body() to * free this "per watcher" body. */ aux_body_processing_t* aux_body_processing; free_body_t* aux_free_body; struct pres_ev* wipeer; struct pres_ev* next; }pres_ev_t; ... 2.3. get_rules_doc Filed type: ... typedef int (get_rules_doc_t)(str* user, str* domain, str** rules_doc); ... This function returns the authorization rules document that will be used in obtaining the status of the subscription and processing the notified body. A reference to the document should be put in the auth_rules_doc of the subs_t structure given as a parameter to the functions described bellow. 2.4. get_auth_status This filed is a function to be called for a subscription request to return the state for that subscription according to authorization rules. In the auth_rules_doc field of the subs_t structure received as a parameter should contain the rules document of the presentity in case, if it exists. It is called only if the req_auth field is not 0. Filed type: ... typedef int (is_allowed_t)(struct subscription* subs); ... 2.5. apply_auth_nbody This parameter should be a function to be called for an event that requires authorization, when constructing final body. The authorization document is taken from the auth_rules_doc field of the subs_t structure given as a parameter. It is called only if the req_auth field is not 0. Filed type: ... typedef int (apply_auth_t)(str* , struct subscription*, str** ); ... 2.6. agg_nbody If present, this field marks that the events requires aggregation of states. This function receives a body array and should return the final body. If not present, it is considered that the event does not require aggregation and the most recent published information is used when constructing Notifies. Filed type: ... typedef str* (agg_nbody_t)(str* pres_user, str* pres_domain, str** body_array, int n, int off_index); .. 2.7. free_body This field must be field in if subsequent processing is performed on the info from database before being inserted in Notify message body(if agg_nbody or apply_auth_nbody fields are filled in). It should match the allocation function used when processing the body. Filed type: ... typedef void(free_body_t)(char* body); .. 2.8. aux_body_processing This field must be set if the module needs to manipulate the NOTIFY body for each watcher. E.g. if the XML body includes a 'version' parameter which will be increased for each NOTIFY, on a "per watcher" basis. The module can either allocate a new buffer for the new body an return it (aux_free_body function must be set too) or it manipualtes the original body directly and returns NULL. Filed type: ... typedef str* (aux_body_processing_t)(struct subscription *subs, str* bod y); .. 2.9. aux_free_body This field must be set if the module registers the aux_body_processing function and allocates memory for the new modified body. Then, this function will be used to free the pointer returned by the aux_body_processing function. If the module does use the aux_body_processing, but does not allocate new memory, but manipulates directly the original body buffer, then the aux_body_processing must return NULL and this field should not be set. Filed type: ... typedef void(free_body_t)(char* body); .. 2.10. evs_publ_handl This function is called when handling Publish requests. Most contain body correctness check. ... typedef int (publ_handling_t)(struct sip_msg*); .. 2.11. evs_subs_handl It is not compulsory. Should contain event specific handling for Subscription requests. Filed type: ... typedef int (subs_handling_t)(struct sip_msg*); .. 2.12. contains_event Field type: .. typedef pres_ev_t* (*contains_event_t)(str* name, event_t* parsed_event); ... The function parses the event name received as a parameter and searches the result in the list. It returns the found event or NULL, if not found. If the second argument is an allocated event_t* structure it fills it with the result of the parsing. 2.13. get_event_list Field type: ... typedef int (*get_event_list_t) (str** ev_list); ... This function returns a string representation of the events registered in presence module.( used for Allowed-Events header). 2.14. update_watchers_status Field type: ... typedef int (*update_watchers_t)(str pres_uri, pres_ev_t* ev, str* rules_doc); ... This function is an external command that can be used to announce a change in authorization rules for a presentity. It updates the stored status and sends a Notify to the watchers whose status has changes. (used by presence_xml module when notified through an MI command of a change in an xcap document). 2.15. get_sphere Field type: ... typedef char* (*pres_get_sphere_t)(str* pres_uri); ... This function searches for a sphere definition in the published information if this has type RPID. If not found returns NULL. (the return value is allocated in private memory and should be freed) 2.16. contains_presence Field type: ... typedef int (*pres_contains_presence_t)(str* pres_uri); ... This function searches is a presence uri has published any presence information. It return 1 if a record is found, -1 otherwise. opensips-2.2.2/modules/presence/bind_presence.c000066400000000000000000000036421300170765700216100ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-17 initial version (Anca Vamanu) */ #include #include #include "../../dprint.h" #include "../../sr_module.h" #include "presentity.h" #include "presence.h" #include "bind_presence.h" int bind_presence(presence_api_t* api) { if (!api) { LM_ERR("Invalid parameter value\n"); return -1; } api->add_event = add_event; api->contains_event= contains_event; api->search_event= search_event; api->get_event_list= get_event_list; api->update_watchers_status= update_watchers_status; api->terminate_watchers= terminate_watchers; api->update_presentity = internal_update_presentity; api->new_shtable= new_shtable; api->destroy_shtable= destroy_shtable; api->insert_shtable= insert_shtable; api->search_shtable= search_shtable; api->delete_shtable= delete_shtable; api->update_shtable= update_shtable; api->mem_copy_subs= mem_copy_subs; api->update_db_subs= update_db_subs; api->extract_sdialog_info= extract_sdialog_info; api->get_sphere= get_sphere; api->contains_presence = contains_presence; return 0; } opensips-2.2.2/modules/presence/bind_presence.h000066400000000000000000000040471300170765700216150ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-17 initial version (Anca Vamanu) */ #ifndef _PRES_BIND_H_ #define _PRES_BIND_H_ #include "event_list.h" #include "hash.h" #include "presentity.h" typedef int (*update_watchers_t)(str pres_uri, pres_ev_t* ev, str* rules_doc); typedef int (*update_presentity_t)(presentity_t* presentity); typedef int (*terminate_watchers_t)(str *pres_uri, pres_ev_t* ev); typedef struct presence_api { add_event_t add_event; contains_event_t contains_event; search_event_t search_event; get_event_list_t get_event_list; update_watchers_t update_watchers_status; terminate_watchers_t terminate_watchers; update_presentity_t update_presentity; /* subs hash table functions */ new_shtable_t new_shtable; destroy_shtable_t destroy_shtable; insert_shtable_t insert_shtable; search_shtable_t search_shtable; delete_shtable_t delete_shtable; update_shtable_t update_shtable; mem_copy_subs_t mem_copy_subs; update_db_subs_t update_db_subs; extract_sdialog_info_t extract_sdialog_info; pres_get_sphere_t get_sphere; pres_contains_presence_t contains_presence; } presence_api_t; int bind_presence(presence_api_t* api); typedef int (*bind_presence_t)(presence_api_t* api); #endif opensips-2.2.2/modules/presence/doc/000077500000000000000000000000001300170765700174045ustar00rootroot00000000000000opensips-2.2.2/modules/presence/doc/presence.xml000066400000000000000000000022251300170765700217330ustar00rootroot00000000000000 %docentities; ]> Presence Module &osipsname; Anca-Maria Vamanu Ovidiu Sas Anca-Maria Vamanu Saul Ibarra Corretge 2006 &voicesystem; $Revision: 9371 $ $Date$ &admin; &devel; opensips-2.2.2/modules/presence/doc/presence_admin.xml000066400000000000000000000524151300170765700231110ustar00rootroot00000000000000 &adminguide;
Overview The modules handles PUBLISH and SUBSCRIBE messages and generates NOTIFY messages in a general, event independent way. It allows registering events to it from other &osips; modules. Events that can currently be added to it are: presence, presence.winfo, dialog;sla from presence_xml module and message-summary from presence_mwi module. The modules uses database storage. It has later been improved with memory caching operations to improve performance. The Subscribe dialog information are stored in memory and are periodically updated in database, while for Publish only the presence or absence of stored info for a certain resource is maintained in memory to avoid unnecessary, costly db operations. It is possible to configure a fallback to database mode(by setting module parameter "fallback2db"). In this mode, in case a searched record is not found in cache, the search is continued in database. This is useful for an architecture in which processing and memory load might be divided on more machines using the same database. The module can also work only with the functionality of a library, with no message processing and generation, but used only for the exported functions. This mode of operation is enabled if the db_url parameter is not set to any value. The server follows the specifications in: RFC3265, RFC3856, RFC3857, RFC3858.
Dependencies
&osips; Modules The following modules must be loaded before this module: a database module. signaling.
External Libraries or Applications libxml-dev.
Exported Parameters
<varname>db_url</varname>(str) The database url. If set, the module is a fully operational presence server. Otherwise, it is used as a 'library', for its exported functions. Default value is NULL. Set <varname>db_url</varname> parameter ... modparam("presence", "db_url", "mysql://opensips:opensipsrw@192.168.2.132/opensips") ...
<varname>presentity_table</varname>(str) The name of the db table where Publish information are stored. Default value is presentity. Set <varname>presentity_table</varname> parameter ... modparam("presence", "presentity_table", "presentity") ...
<varname>active_watchers_table</varname>(str) The name of the db table where active subscription information are stored. Default value is active_watchers. Set <varname>active_watchers_table</varname> parameter ... modparam("presence", "active_watchers_table", "active_watchers") ...
<varname>watchers_table</varname>(str) The name of the db table where subscription states are stored. Default value is watchers. Set <varname>watchers_table</varname> parameter ... modparam("presence", "watchers_table", "watchers") ...
<varname>clean_period</varname> (int) The period at which to clean the expired subscription dialogs. Default value is 100. A zero or negative value disables this activity. Set <varname>clean_period</varname> parameter ... modparam("presence", "clean_period", 100) ...
<varname>db_update_period</varname> (int) The period at which to synchronize cached subscriber info with the database. Default value is 100. A zero or negative value disables synchronization. Set <varname>db_update_period</varname> parameter ... modparam("presence", "db_update_period", 100) ...
<varname>expires_offset</varname> (int) The extra time to store a subscription/publication. Default value is 0. Set <varname>expires_offset</varname> parameter ... modparam("presence", "expires_offset", 10) ...
<varname>max_expires_subscribe</varname> (int) The the maximum admissible expires value for SUBSCRIBE messages. Default value is 3600. Set <varname>max_expires_subscribe</varname> parameter ... modparam("presence", "max_expires_subscribe", 3600) ...
<varname>max_expires_publish</varname> (int) The the maximum admissible expires value for PUBLISH messages. Default value is 3600. Set <varname>max_expires_publish</varname> parameter ... modparam("presence", "max_expires_publish", 3600) ...
<varname>server_address</varname> (str) The presence server address which will become the value of Contact header filed for 200OK replies to Subscribe and Publish and in Notify messages. Set <varname>server_address</varname> parameter ... modparam("presence", "server_address", "sip:10.10.10.10:5060") ...
<varname>fallback2db</varname> (int) Setting this parameter enables a fallback to db mode of operation. In this mode, in case a searched record is not found in cache, the search is continued in database. Useful for an architecture in which processing and memory load might be divided on more machines using the same database. Set <varname>fallback2db</varname> parameter ... modparam("presence", "fallback2db", 1) ...
<varname>subs_htable_size</varname> (int) The size of the hash table to store subscription dialogs. This parameter will be used as the power of 2 when computing table size. Default value is 9 (512). Set <varname>subs_htable_size</varname> parameter ... modparam("presence", "subs_htable_size", 11) ...
<varname>pres_htable_size</varname> (int) The size of the hash table to store publish records. This parameter will be used as the power of 2 when computing table size. Default value is 9 (512). Set <varname>pres_htable_size</varname> parameter ... modparam("presence", "pres_htable_size", 11) ...
<varname>enable_sphere_check</varname> (int) This parameter is a flag that should be set if permission rules include sphere checking. The sphere information is expected to be present in the RPID body published by the presentity. The flag is introduced as this check requires extra processing that should be avoided if this feature is not supported by the clients. Default value is 0 . Set <varname>enable_sphere_check</varname> parameter ... modparam("presence", "enable_sphere_check", 1) ...
<varname>waiting_subs_daysno</varname> (int) The number of days to keep the record of a subscription in server database if the subscription is in pending or waiting state (no authorization policy was defined for it or the target user did not register sice the subscription and was not informed about it). Default value is 3 days. Maximum accepted value is 30 days. Set <varname>waiting_subs_daysno</varname> parameter ... modparam("presence", "waiting_subs_daysno", 2) ...
<varname>mix_dialog_presence</varname> (int) This module parameter enables a very nice feature in the presence server - generating presence information from dialogs state. If this parameter is set, the presence server will tell you if a buddy is in a call even if his phone did not send a presence Publish with this information. You will need to load the dialoginfo modules, presence_dialoginfo, pua_dialoginfo, dialog and pua. Default value is 0. Set <varname>mix_dialog_presence</varname> parameter ... modparam("presence", "mix_dialog_presence", 1) ...
<varname>bla_presentity_spec</varname> (str) By default the presentity uri for BLA subscribes (event=dialog;sla) is computed from contact username + from domain. In some cases though, this way of computing the presentity might not be right ( for example if you have a SBC in front that masquerades the contact). So we added this parameter that allows defining a custom uri to be used as presentity uri for BLA subscribes. You should set this parameter to the name of a pseudovariable and then set this pseudovariable to the desired URI before calling the function handle_subscribe. Default value is NULL. Set <varname>bla_presentity_spec</varname> parameter ... modparam("presence", "bla_presentity_spec", "$var(bla_pres)") ...
<varname>bla_fix_remote_target</varname> (int) Polycom has a bug in the bla implementation. It inserts the remote IP contact in the Notify body and when a phone picks up a call put on hold by another phone in the same BLA group, it sends an Invite directly to the remote IP. OpenSIPS BLA server tries to prevent this by replacing the IP contact with the domain, when this is possible. In some cases(configurations) however this is not desirable, so this parameter was introduced to disable this behaviour when needed. Default value is 1. Set <varname>bla_fix_remote_target</varname> parameter ... modparam("presence", "bla_fix_remote_target", 0) ...
<varname>notify_offline_body</varname> (int) If this parameter is set, when no published info is found for a user, the presence server will generate a dummy body with status 'closed' and use it when sending Notify, instead of notifying with no body. Default value is 0. Set <varname>notify_offline_body</varname> parameter ... modparam("presence", "notify_offline_body", 1) ...
<varname>end_sub_on_timeout</varname> (int) If a presence subscription should be automatically terminated (destroyed) when receiving a SIP timeout (408) for a sent NOTIFY requests. Default value is 1 (enabled). Set <varname>end_sub_on_timeout</varname> parameter ... modparam("presence", "end_sub_on_timeout", 0) ...
Exported Functions
<function moreinfo="none">handle_publish(char* sender_uri)</function> The function handles PUBLISH requests. It stores and updates published information in database and calls functions to send NOTIFY messages when changes in the published information occur. It takes one argument -> sender_uri. The parameter was added for enabling BLA implementation. If present, Notification of a change in published state is not sent to the respective uri even though a subscription exists. It should be taken from the Sender header. It was left at the decision of the administrator whether or not to transmit the content of this header as parameter for handle_publish, to prevent security problems. This function can be used from REQUEST_ROUTE. Return code: 1 - if success. -1 - if error. The module sends an appropriate stateless reply in all cases. <function>handle_publish</function> usage ... if(is_method("PUBLISH")) { if($hdr(Sender)!= NULL) handle_publish("$hdr(Sender)"); else handle_publish(); } ...
<function moreinfo="none">handle_subscribe(char* force_active)</function> This function is to be used for handling SUBSCRIBE requests. It stores or updates the watcher/subscriber information in database. Additionally, in response to initial SUBSCRIBE requests (creating a new subscription session), the function also sends back the NOTIFY (with the presence information) to the wathcer/subscriber. The function may take on parameter that controlls what is the default policy (of the presentity) on accepting new subscriptions (accept or reject) - of course, this parameter makes sense only when using a presence configuration with privacy rules enabled (force_active parameter in presence_xml module is not set). There are scenarios where the presentity (the party you subscribe to) can not upload an XCAP document with it's privacy rules (to controll which watchers are allowed to subscribe to it). In such case, from script level, you can force the presence server to consider the current subscription allowed (with Subscription-Status:active) by calling handle_subscribe() function with the string parameter "1". Ex: if(uri =~ "kphone@opensips.org") handle_subscribe("1"); This function can be used from REQUEST_ROUTE. Return code: 1 - if success. -1 - if error. The module sends an appropriate stateless reply in all cases. <function>handle_subscribe</function> usage ... if(method=="SUBSCRIBE") handle_subscribe(); ...
Exported MI Functions
<function moreinfo="none">refreshWatchers</function> Triggers sending Notify messages to watchers if a change in watchers authorization or in published state occurred. Name: refreshWatchers Parameters: presentity_uri : the uri of the user who made the change and whose watchers should be informed event : the event package refresh type : it distinguishes between the two different types of events that can trigger a refresh: a change in watchers authentication: refresh type= 0 ; a statical update in published state (either through direct update in db table or by modifying the pidf manipulation document, if pidf_manipulation parameter is set): refresh type!= 0. MI FIFO Command Format: :refreshWatchers:fifo_reply sip:11@192.168.2.132 presence 1 _empty_line_
<function moreinfo="none">cleanup</function> Manually triggers the cleanup functions for watchers and presentity tables. Useful if you have set clean_period to zero or less. Name: cleanup Parameters:none.emphasis> MI FIFO Command Format: :cleanup:fifo_reply _empty_line_
<function moreinfo="none">pres_phtable_list</function> Lists all the presentity records. Name: pres_phtable_list Parameters:none. MI FIFO Command Format: :pres_phtable_list:fifo_reply _empty_line_
<function moreinfo="none">subs_phtable_list</function> Lists all the subscription records. Name: subs_phtable_list Parameters:none. MI FIFO Command Format: :subs_phtable_list:fifo_reply _empty_line_
Exported Events
<function moreinfo="none">E_PRESENCE_PUBLISH</function> This event is raised when the presence module receives a PUBLISH message. Parameters: user - the AOR of the user domain - the domain event - the type of the event published expires - the expire value of the publish etag - the entity tag body - the body of the PUBLISH request
Installation The module requires 3 table in OpenSIPS database: presentity, active_watchers and watchers tables. The SQL syntax to create them can be found in presence-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, &osipsdbdocslink;.
opensips-2.2.2/modules/presence/doc/presence_devel.xml000066400000000000000000000257251300170765700231240ustar00rootroot00000000000000 &develguide; The module provides the following functions that can be used in other &osips; modules.
<function moreinfo="none">bind_presence(presence_api_t* api)</function> This function binds the presence modules and fills the structure with the exported functions that represent functions adding events in presence module and functions specific for Subscribe processing. <function>presence_api_t</function> structure ... typedef struct presence_api { add_event_t add_event; contains_event_t contains_event; search_event_t search_event; get_event_list_t get_event_list; update_watchers_t update_watchers_status; /* subs hash table handling functions */ new_shtable_t new_shtable; destroy_shtable_t destroy_shtable; insert_shtable_t insert_shtable; search_shtable_t search_shtable; delete_shtable_t delete_shtable; update_shtable_t update_shtable; /* function to duplicate a subs structure*/ mem_copy_subs_t mem_copy_subs; /* function used for update in database*/ update_db_subs_t update_db_subs; /* function to extract dialog information from a SUBSCRIBE message */ extract_sdialog_info_t extract_sdialog_info; /* function to request sphere defition for a presentity */ pres_get_sphere_t get_sphere; pres_contains_presence_t contains_presence; }presence_api_t; ...
<function moreinfo="none">add_event</function> Field type: ... typedef int (*add_event_t)(pres_ev_t* event); ... This function receives as a parameter a structure with event specific information and adds it to presence event list. The structure received as a parameter: ... typedef struct pres_ev { str name; event_t* evp; str content_type; int default_expires; int type; int etag_not_new; /* * 0 - the standard mechanism (allocating new etag for each Publish) * 1 - allocating an etag only for an initial Publish */ int req_auth; get_rules_doc_t* get_rules_doc; apply_auth_t* apply_auth_nbody; is_allowed_t* get_auth_status; /* an agg_body_t function should be registered * if the event permits having multiple published * states and requires an aggregation of the information * otherwise, this field should be NULL and the last * published state is taken when constructing Notify msg */ agg_nbody_t* agg_nbody; publ_handling_t * evs_publ_handl; subs_handling_t * evs_subs_handl; free_body_t* free_body; /* sometimes it is necessary that a module make changes for a body for each * active watcher (e.g. setting the "version" parameter in an XML document. * If a module registers the aux_body_processing callback, it gets called for * each watcher. It either gets the body received by the PUBLISH, or the body * generated by the agg_nbody function. * The module can deceide if it makes a copy of the original body, which is then * manipulated, or if it works directly in the original body. If the module makes a * copy of the original body, it also has to register the aux_free_body() to * free this "per watcher" body. */ aux_body_processing_t* aux_body_processing; free_body_t* aux_free_body; struct pres_ev* wipeer; struct pres_ev* next; }pres_ev_t; ...
<function moreinfo="none">get_rules_doc</function> Filed type: ... typedef int (get_rules_doc_t)(str* user, str* domain, str** rules_doc); ... This function returns the authorization rules document that will be used in obtaining the status of the subscription and processing the notified body. A reference to the document should be put in the auth_rules_doc of the subs_t structure given as a parameter to the functions described bellow.
<function moreinfo="none">get_auth_status</function> This filed is a function to be called for a subscription request to return the state for that subscription according to authorization rules. In the auth_rules_doc field of the subs_t structure received as a parameter should contain the rules document of the presentity in case, if it exists. It is called only if the req_auth field is not 0. Filed type: ... typedef int (is_allowed_t)(struct subscription* subs); ...
<function moreinfo="none">apply_auth_nbody</function> This parameter should be a function to be called for an event that requires authorization, when constructing final body. The authorization document is taken from the auth_rules_doc field of the subs_t structure given as a parameter. It is called only if the req_auth field is not 0. Filed type: ... typedef int (apply_auth_t)(str* , struct subscription*, str** ); ...
<function moreinfo="none">agg_nbody</function> If present, this field marks that the events requires aggregation of states. This function receives a body array and should return the final body. If not present, it is considered that the event does not require aggregation and the most recent published information is used when constructing Notifies. Filed type: ... typedef str* (agg_nbody_t)(str* pres_user, str* pres_domain, str** body_array, int n, int off_index); ..
<function moreinfo="none">free_body</function> This field must be field in if subsequent processing is performed on the info from database before being inserted in Notify message body(if agg_nbody or apply_auth_nbody fields are filled in). It should match the allocation function used when processing the body. Filed type: ... typedef void(free_body_t)(char* body); ..
<function moreinfo="none">aux_body_processing</function> This field must be set if the module needs to manipulate the NOTIFY body for each watcher. E.g. if the XML body includes a 'version' parameter which will be increased for each NOTIFY, on a "per watcher" basis. The module can either allocate a new buffer for the new body an return it (aux_free_body function must be set too) or it manipualtes the original body directly and returns NULL. Filed type: ... typedef str* (aux_body_processing_t)(struct subscription *subs, str* body); ..
<function moreinfo="none">aux_free_body</function> This field must be set if the module registers the aux_body_processing function and allocates memory for the new modified body. Then, this function will be used to free the pointer returned by the aux_body_processing function. If the module does use the aux_body_processing, but does not allocate new memory, but manipulates directly the original body buffer, then the aux_body_processing must return NULL and this field should not be set. Filed type: ... typedef void(free_body_t)(char* body); ..
<function moreinfo="none">evs_publ_handl</function> This function is called when handling Publish requests. Most contain body correctness check. ... typedef int (publ_handling_t)(struct sip_msg*); ..
<function moreinfo="none">evs_subs_handl</function> It is not compulsory. Should contain event specific handling for Subscription requests. Filed type: ... typedef int (subs_handling_t)(struct sip_msg*); ..
<function moreinfo="none">contains_event</function> Field type: .. typedef pres_ev_t* (*contains_event_t)(str* name, event_t* parsed_event); ... The function parses the event name received as a parameter and searches the result in the list. It returns the found event or NULL, if not found. If the second argument is an allocated event_t* structure it fills it with the result of the parsing.
<function moreinfo="none">get_event_list</function> Field type: ... typedef int (*get_event_list_t) (str** ev_list); ... This function returns a string representation of the events registered in presence module.( used for Allowed-Events header).
<function moreinfo="none">update_watchers_status</function> Field type: ... typedef int (*update_watchers_t)(str pres_uri, pres_ev_t* ev, str* rules_doc); ... This function is an external command that can be used to announce a change in authorization rules for a presentity. It updates the stored status and sends a Notify to the watchers whose status has changes. (used by presence_xml module when notified through an MI command of a change in an xcap document).
<function moreinfo="none">get_sphere</function> Field type: ... typedef char* (*pres_get_sphere_t)(str* pres_uri); ... This function searches for a sphere definition in the published information if this has type RPID. If not found returns NULL. (the return value is allocated in private memory and should be freed)
<function moreinfo="none">contains_presence</function> Field type: ... typedef int (*pres_contains_presence_t)(str* pres_uri); ... This function searches is a presence uri has published any presence information. It return 1 if a record is found, -1 otherwise.
opensips-2.2.2/modules/presence/event_list.c000066400000000000000000000214721300170765700211650ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-04 initial version (Anca Vamanu) */ #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../parser/parse_event.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "event_list.h" #include "hash.h" #define MAX_EVNAME_SIZE 20 int search_event_params(event_t* ev, event_t* searched_ev); event_t* shm_copy_event(event_t* e) { event_t* ev= NULL; param_t* p1, *p2; int size; ev= (event_t*)shm_malloc(sizeof(event_t)); if(ev== NULL) { ERR_MEM(SHARE_MEM); } memset(ev, 0, sizeof(event_t)); ev->text.s= (char*)shm_malloc(e->text.len); if(ev->text.s== NULL) { ERR_MEM(SHARE_MEM); } memcpy(ev->text.s, e->text.s, e->text.len); ev->text.len= e->text.len; p1= e->params; while(p1) { size= sizeof(param_t)+ p1->name.len+ p1->body.len; p2= (param_t*)shm_malloc(size); if(p2== NULL) { ERR_MEM(SHARE_MEM); } memset(p2, 0, size); size= sizeof(param_t); CONT_COPY(p2, p2->name, p1->name); if(p1->body.s && p1->body.len) CONT_COPY(p2, p2->body, p1->body); p2->next= ev->params; ev->params= p2; p1= p1->next; } ev->parsed= e->parsed; return ev; error: shm_free_event(ev); return NULL; } void shm_free_event(event_t* ev) { if(ev== NULL) return; if(ev->text.s) shm_free(ev->text.s); free_event_params(ev->params, SHM_MEM_TYPE); shm_free(ev); } int add_event(pres_ev_t* event) { pres_ev_t* ev= NULL; event_t parsed_event; str wipeer_name; char* sep; char buf[50]; int not_in_list= 0; if(EvList == NULL) { LM_ERR("'presence' modules must be loaded before this module\n"); return -1; } memset(&parsed_event, 0, sizeof(event_t)); if(event->name.s== NULL || event->name.len== 0) { LM_ERR("NULL event name\n"); return -1; } if(event->content_type.s== NULL || event->content_type.len== 0) { if (event->mandatory_body) { LM_ERR("NULL content_type param\n"); return -1; } } ev= contains_event(&event->name, &parsed_event); if(ev== NULL) { not_in_list= 1; ev= (pres_ev_t*)shm_malloc(sizeof(pres_ev_t)); if(ev== NULL) { free_event_params(parsed_event.params, PKG_MEM_TYPE); ERR_MEM(SHARE_MEM); } memset(ev, 0, sizeof(pres_ev_t)); ev->name.s= (char*)shm_malloc(event->name.len); if(ev->name.s== NULL) { free_event_params(parsed_event.params, PKG_MEM_TYPE); ERR_MEM(SHARE_MEM); } memcpy(ev->name.s, event->name.s, event->name.len); ev->name.len= event->name.len; ev->evp= shm_copy_event(&parsed_event); if(ev->evp== NULL) { LM_ERR("copying event_t structure\n"); free_event_params(parsed_event.params, PKG_MEM_TYPE); goto error; } free_event_params(parsed_event.params, PKG_MEM_TYPE); } else { free_event_params(parsed_event.params, PKG_MEM_TYPE); if(ev->content_type.s) { LM_DBG("Event already registered\n"); return 0; } } ev->content_type.s = (char*)shm_malloc(event->content_type.len); if(ev->content_type.s== NULL) { ERR_MEM(SHARE_MEM); } ev->content_type.len= event->content_type.len; memcpy(ev->content_type.s, event->content_type.s, event->content_type.len); sep= strchr(event->name.s, '.'); if(sep != NULL && strncasecmp(sep+1, "winfo", 5)== 0) { ev->type= WINFO_TYPE; wipeer_name.s= event->name.s; wipeer_name.len= sep - event->name.s; ev->wipeer= contains_event(&wipeer_name, NULL); } else { ev->type= PUBL_TYPE; wipeer_name.s= buf; memcpy(wipeer_name.s, event->name.s, event->name.len); wipeer_name.len= event->name.len; memcpy(wipeer_name.s+ wipeer_name.len, ".winfo", 6); wipeer_name.len+= 6; ev->wipeer= contains_event(&wipeer_name, NULL); } if(ev->wipeer) ev->wipeer->wipeer= ev; if(event->req_auth && ( event->get_auth_status==0 ||event->get_rules_doc== 0)) { LM_ERR("bad event structure\n"); goto error; } ev->extra_hdrs= event->extra_hdrs; ev->req_auth= event->req_auth; ev->agg_nbody= event->agg_nbody; ev->apply_auth_nbody= event->apply_auth_nbody; ev->get_auth_status= event->get_auth_status; ev->get_rules_doc= event->get_rules_doc; ev->evs_publ_handl= event->evs_publ_handl; ev->evs_subs_handl= event->evs_subs_handl; ev->mandatory_body = event->mandatory_body; ev->mandatory_timeout_notification = event->mandatory_timeout_notification; ev->etag_not_new= event->etag_not_new; ev->aux_body_processing= event->aux_body_processing; ev->aux_free_body= event->aux_free_body; ev->free_body= event->free_body; ev->build_empty_pres_info= event->build_empty_pres_info; ev->default_expires= event->default_expires; if(not_in_list) { ev->next= EvList->events; EvList->events= ev; } EvList->ev_count++; LM_DBG("successfully added event: %.*s - len= %d\n",ev->name.len, ev->name.s, ev->name.len); /* if event 'presence' set the pointer */ if(ev->evp->parsed == EVENT_PRESENCE) *pres_event_p = ev; /* if event 'dialog' set the pointer */ if(ev->evp->parsed == EVENT_DIALOG) *dialog_event_p = ev; return 0; error: if(ev && not_in_list) { free_pres_event(ev); } return -1; } void free_pres_event(pres_ev_t* ev) { if(ev== NULL) return; if(ev->name.s) shm_free(ev->name.s); if(ev->content_type.s) shm_free(ev->content_type.s); shm_free_event(ev->evp); shm_free(ev); } evlist_t* init_evlist(void) { evlist_t* list= NULL; list= (evlist_t*)shm_malloc(sizeof(evlist_t)); if(list== NULL) { LM_ERR("no more share memory\n"); return NULL; } list->ev_count= 0; list->events= NULL; return list; } pres_ev_t* contains_event(str* sname, event_t* parsed_event) { event_t event; pres_ev_t* e; memset(&event, 0, sizeof(event_t)); if(event_parser(sname->s, sname->len, &event)< 0) { LM_ERR("parsing event\n"); return NULL; } if(parsed_event) *parsed_event= event; else { free_event_params(event.params, PKG_MEM_TYPE); } e= search_event(&event); return e; } void free_event_params(param_t* params, int mem_type) { param_t* t1, *t2; t2= t1= params; while(t1) { t2= t1->next; if(mem_type == SHM_MEM_TYPE) shm_free(t1); else pkg_free(t1); t1= t2; } } pres_ev_t* search_event(event_t* event) { pres_ev_t* pres_ev; pres_ev= EvList->events; LM_DBG("start event= [%.*s]\n", event->text.len, event->text.s); while(pres_ev) { if(pres_ev->evp->parsed== event->parsed) { if(event->params== NULL && pres_ev->evp->params== NULL) { return pres_ev; } /* search all parameters in event in ev */ /* if(search_event_params(event, pres_ev->evp)< 0) goto cont; */ /* search all parameters in ev in event */ if(search_event_params(pres_ev->evp, event)< 0) goto cont; return pres_ev; } cont: pres_ev= pres_ev->next; } return NULL; } int search_event_params(event_t* ev, event_t* searched_ev) { param_t* ps, *p; int found; ps= ev->params; while(ps) { p= searched_ev->params; found= 0; while(p) { if(p->name.len== ps->name.len && strncmp(p->name.s,ps->name.s, ps->name.len)== 0) if((p->body.s== 0 && ps->body.s== 0) || (p->body.len== ps->body.len && strncmp(p->body.s,ps->body.s,ps->body.len)== 0)) { found= 1; break; } p= p->next; } if(found== 0) return -1; ps= ps->next; } return 1; } int get_event_list(str** ev_list) { pres_ev_t* ev= EvList->events; int i; str* list; *ev_list= NULL; if(EvList->ev_count== 0) return 0; list= (str*)pkg_malloc(sizeof(str)); if(list== NULL) { LM_ERR("No more memory\n"); return -1; } memset(list, 0, sizeof(str)); list->s= (char*)pkg_malloc(EvList->ev_count* MAX_EVNAME_SIZE); if(list->s== NULL) { LM_ERR("No more memory\n"); pkg_free(list); return -1; } list->s[0]= '\0'; for(i= 0; i< EvList->ev_count; i++) { if(i> 0) { memcpy(list->s+ list->len, ", ", 2); list->len+= 2; } memcpy(list->s+ list->len, ev->name.s, ev->name.len ); list->len+= ev->name.len ; ev= ev->next; } *ev_list= list; return 0; } void destroy_evlist(void) { pres_ev_t* e1, *e2; if (EvList) { e1= EvList->events; while(e1) { e2= e1->next; free_pres_event(e1); e1= e2; } shm_free(EvList); } } opensips-2.2.2/modules/presence/event_list.h000066400000000000000000000134331300170765700211700ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-05 initial version (Anca Vamanu) */ #ifndef _PRES_EV_LST_H #define _PRES_EV_LST_H #include "../../parser/msg_parser.h" #include "../../parser/parse_event.h" #include "../../str.h" #include "subscribe.h" #define WINFO_TYPE 1<< 0 #define PUBL_TYPE 1<< 1 struct subscription; typedef int (apply_auth_t)(str* , struct subscription*, str** ); typedef int (publ_handling_t)(struct sip_msg*, int* sent_event); typedef int (subs_handling_t)(struct sip_msg*,struct subscription *,int*, str*); typedef str* (empty_pres_info_t)(str* pres_uri, str* extra_hdrs); typedef str* (agg_nbody_t)(str* pres_user, str* pres_domain, str** body_array, int n, int off_index); /* params for agg_body_t * body_array= an array with all the bodies stored for that resource * n= the number of bodies * off_index= the index of the registration(etag) for which a Publish * with Expires: 0 has just been received * */ typedef str* (aux_body_processing_t)(struct subscription *subs, str* body); /* params for agg_body_t * subs= a subscription structure to manipulate the body for a certain watcher * body= the original body * * return value: 0: means that there was no manipulation or the manipulation was * done directly in the original body * pointer: a pointer to str for the "per watcher" body. gets freed by aux_free_body() * */ typedef int (is_allowed_t)(struct subscription* subs); typedef int (get_rules_doc_t)(str* user, str* domain, str** rules_doc); /* return code rules for is_allowed_t * < 0 if error occurred * =0 if no change in status(if no xcap document exists) * >0 if change in status * */ /* event specific body free function */ typedef void(free_body_t)(char* body); struct pres_ev { str name; event_t* evp; str content_type; str* extra_hdrs; int default_expires; int type; /* Flag that sets the requirements for body: * * 0 - body is not mandatory * 1 - body is mandatory */ int mandatory_body; /* Flag that sets the requirements for timeout notification: * * 0 - NOTIFY with reason "timeout" is not mandatory * 1 - NOTIFY with reason "timeout" is mandatory */ int mandatory_timeout_notification; /* * 0 - the standard mechanism (allocating new etag for each Publish) * 1 - allocating an etag only for an initial Publish * */ int etag_not_new; /* fileds that deal with authorization rules*/ /* * req_auth -> flag 0 - if not require * is_watcher_allowed - get subscription state from xcap rules * apply_auth_nbody - alter the body according to authorization rules */ int req_auth; get_rules_doc_t* get_rules_doc; apply_auth_t* apply_auth_nbody; is_allowed_t* get_auth_status; /* an agg_body_t function should be registered if the event permits having * multiple published states and requires an aggregation of the information * otherwise, this field should be NULL and the last published state is taken * when constructing Notify msg * */ agg_nbody_t* agg_nbody; publ_handling_t * evs_publ_handl; subs_handling_t * evs_subs_handl; /* for some phones and specific events, we need to provide some dummy presence * information - like a dummy body for dialog event when no information is * available in the presentity table */ empty_pres_info_t * build_empty_pres_info; free_body_t* free_body; /* sometimes it is necessary that a module make changes for a body for each * active watcher (e.g. setting the "version" parameter in an XML document. * If a module registers the aux_body_processing callback, it gets called for * each watcher. It either gets the body received by the PUBLISH, or the body * generated by the agg_nbody function. * The module can deceide if it makes a copy of the original body, which is then * manipulated, or if it works directly in the original body. If the module makes a * copy of the original body, it also has to register the aux_free_body() to * free this "per watcher" body. */ aux_body_processing_t* aux_body_processing; free_body_t* aux_free_body; struct pres_ev* wipeer; struct pres_ev* next; }; typedef struct pres_ev pres_ev_t; /* pointer holders for 'dialog' and 'presence_event', * they need fast access */ extern pres_ev_t** pres_event_p; extern pres_ev_t** dialog_event_p; typedef struct evlist { int ev_count; pres_ev_t* events; }evlist_t; evlist_t* init_evlist(void); int add_event(pres_ev_t* event); typedef int (*add_event_t)(pres_ev_t* event); void free_event_params(param_t* params, int mem_type); pres_ev_t* contains_event(str* name, event_t* parsed_event); typedef pres_ev_t* (*contains_event_t)(str* name, event_t* parsed_event); int get_event_list(str** ev_list); typedef int (*get_event_list_t) (str** ev_list); void destroy_evlist(void); extern evlist_t* EvList; pres_ev_t* search_event(event_t* event); typedef pres_ev_t* (*search_event_t)(event_t* event); event_t* shm_copy_event(event_t* e); void shm_free_event(event_t* ev); void free_pres_event(pres_ev_t* ev); #endif opensips-2.2.2/modules/presence/hash.c000066400000000000000000000346721300170765700177420ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-08-20 initial version (Anca Vamanu) */ #include #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../str.h" #include "../pua/hash.h" #include "presence.h" #include "hash.h" #include "notify.h" shtable_t new_shtable(int hash_size) { shtable_t htable= NULL; int i, j; i = 0; htable= (subs_entry_t*)shm_malloc(hash_size* sizeof(subs_entry_t)); if(htable== NULL) { ERR_MEM(SHARE_MEM); } memset(htable, 0, hash_size* sizeof(subs_entry_t)); for(i= 0; i< hash_size; i++) { if(lock_init(&htable[i].lock)== 0) { LM_ERR("initializing lock [%d]\n", i); goto error; } htable[i].entries= (subs_t*)shm_malloc(sizeof(subs_t)); if(htable[i].entries== NULL) { lock_destroy(&htable[i].lock); ERR_MEM(SHARE_MEM); } memset(htable[i].entries, 0, sizeof(subs_t)); htable[i].entries->next= NULL; } return htable; error: if(htable) { for(j=0; j< i; j++) { lock_destroy(&htable[j].lock); shm_free(htable[j].entries); } shm_free(htable); } return NULL; } void destroy_shtable(shtable_t htable, int hash_size) { int i; if(htable== NULL) return; for(i= 0; i< hash_size; i++) { lock_destroy(&htable[i].lock); free_subs_list(htable[i].entries->next, SHM_MEM_TYPE, 1); shm_free(htable[i].entries); } shm_free(htable); htable= NULL; } subs_t* search_shtable(shtable_t htable,str callid,str to_tag, str from_tag,unsigned int hash_code) { subs_t* s; s= htable[hash_code].entries->next; while(s) { if(s->callid.len==callid.len && strncmp(s->callid.s, callid.s, callid.len)==0 && s->to_tag.len== to_tag.len && strncmp(s->to_tag.s, to_tag.s, to_tag.len)==0 && s->from_tag.len== from_tag.len && strncmp(s->from_tag.s, from_tag.s, from_tag.len)== 0) return s; s= s->next; } return NULL; } subs_t* mem_copy_subs(subs_t* s, int mem_type) { int size; subs_t* dest; size= sizeof(subs_t)+ s->pres_uri.len+ s->to_user.len + s->to_domain.len+ s->from_user.len+ s->from_domain.len+ s->callid.len + s->to_tag.len+ s->from_tag.len+s->event_id.len + s->local_contact.len+ s->contact.len+ s->record_route.len+ + s->reason.len+ 1; if(mem_type == PKG_MEM_TYPE) dest= (subs_t*)pkg_malloc(size); else dest= (subs_t*)shm_malloc(size); if(dest== NULL) { ERR_MEM((mem_type==PKG_MEM_TYPE)?PKG_MEM_STR:SHARE_MEM); } memset(dest, 0, size); size= sizeof(subs_t); CONT_COPY(dest, dest->pres_uri, s->pres_uri); CONT_COPY(dest, dest->to_user, s->to_user); CONT_COPY(dest, dest->to_domain, s->to_domain); CONT_COPY(dest, dest->from_user, s->from_user); CONT_COPY(dest, dest->from_domain, s->from_domain); CONT_COPY(dest, dest->to_tag, s->to_tag); CONT_COPY(dest, dest->from_tag, s->from_tag); CONT_COPY(dest, dest->callid, s->callid); CONT_COPY(dest, dest->local_contact, s->local_contact); CONT_COPY(dest, dest->contact, s->contact); CONT_COPY(dest, dest->record_route, s->record_route); if(s->event_id.s) CONT_COPY(dest, dest->event_id, s->event_id); if(s->reason.s) CONT_COPY(dest, dest->reason, s->reason); dest->event= s->event; dest->local_cseq= s->local_cseq; dest->remote_cseq= s->remote_cseq; dest->status= s->status; dest->version= s->version; dest->expires= s->expires; dest->db_flag= s->db_flag; dest->sockinfo= s->sockinfo; return dest; error: if(dest) { if(mem_type == PKG_MEM_TYPE) pkg_free(dest); else shm_free(dest); } return NULL; } subs_t* mem_copy_subs_noc(subs_t* s) { int size; subs_t* dest; size= sizeof(subs_t)+ s->pres_uri.len+ s->to_user.len + s->to_domain.len+ s->from_user.len+ s->from_domain.len+ s->callid.len + s->to_tag.len+ s->from_tag.len+s->event_id.len + s->local_contact.len + s->record_route.len+ + s->reason.len+ 1; dest= (subs_t*)shm_malloc(size); if(dest== NULL) { ERR_MEM(SHARE_MEM); } memset(dest, 0, size); size= sizeof(subs_t); CONT_COPY(dest, dest->pres_uri, s->pres_uri); CONT_COPY(dest, dest->to_user, s->to_user); CONT_COPY(dest, dest->to_domain, s->to_domain); CONT_COPY(dest, dest->from_user, s->from_user); CONT_COPY(dest, dest->from_domain, s->from_domain); CONT_COPY(dest, dest->to_tag, s->to_tag); CONT_COPY(dest, dest->from_tag, s->from_tag); CONT_COPY(dest, dest->callid, s->callid); CONT_COPY(dest, dest->local_contact, s->local_contact); CONT_COPY(dest, dest->record_route, s->record_route); if(s->event_id.s) CONT_COPY(dest, dest->event_id, s->event_id); if(s->reason.s) CONT_COPY(dest, dest->reason, s->reason); dest->event= s->event; dest->local_cseq= s->local_cseq; dest->remote_cseq= s->remote_cseq; dest->status= s->status; dest->version= s->version; dest->expires= s->expires; dest->db_flag= s->db_flag; dest->sockinfo = s->sockinfo; dest->contact.s= (char*)shm_malloc(s->contact.len); if(dest->contact.s== NULL) { ERR_MEM(SHARE_MEM); } memcpy(dest->contact.s, s->contact.s, s->contact.len); dest->contact.len= s->contact.len; return dest; error: if(dest) shm_free(dest); return NULL; } int insert_shtable(shtable_t htable,unsigned int hash_code, subs_t* subs) { subs_t* new_rec= NULL; new_rec= mem_copy_subs_noc(subs); if(new_rec== NULL) { LM_ERR("copying in share memory a subs_t structure\n"); goto error; } new_rec->expires+= (int)time(NULL); if(fallback2db) new_rec->db_flag= NO_UPDATEDB_FLAG; else new_rec->db_flag= INSERTDB_FLAG; lock_get(&htable[hash_code].lock); new_rec->next= htable[hash_code].entries->next; htable[hash_code].entries->next= new_rec; lock_release(&htable[hash_code].lock); return 0; error: if(new_rec) shm_free(new_rec); return -1; } void free_subs(subs_t* s) { if(s->contact.s) shm_free(s->contact.s); shm_free(s); } int delete_shtable(shtable_t htable,unsigned int hash_code,str to_tag) { subs_t* s= NULL, *ps= NULL; int found= -1; lock_get(&htable[hash_code].lock); ps= htable[hash_code].entries; s= ps->next; while(s) { if(s->to_tag.len== to_tag.len && strncmp(s->to_tag.s, to_tag.s, to_tag.len)== 0) { found= s->local_cseq; ps->next= s->next; free_subs(s); break; } ps= s; s= s->next; } lock_release(&htable[hash_code].lock); return found; } void free_subs_list(subs_t* s_array, int mem_type, int ic) { subs_t* s; while(s_array) { s= s_array; s_array= s_array->next; if(mem_type == PKG_MEM_TYPE) { if(ic) pkg_free(s->contact.s); pkg_free(s); } else { if(ic) shm_free(s->contact.s); shm_free(s); } } } int update_shtable(shtable_t htable,unsigned int hash_code, subs_t* subs, int type) { subs_t* s; lock_get(&htable[hash_code].lock); s= search_shtable(htable,subs->callid, subs->to_tag, subs->from_tag, hash_code); if(s== NULL) { LM_DBG("record not found in hash table\n"); lock_release(&htable[hash_code].lock); return -1; } if(type & REMOTE_TYPE) { s->expires= subs->expires+ (int)time(NULL); s->remote_cseq= subs->remote_cseq; } else { subs->local_cseq= s->local_cseq++; subs->version= s->version++; } if(strncmp(s->contact.s, subs->contact.s, subs->contact.len)) { shm_free(s->contact.s); s->contact.s= (char*)shm_malloc(subs->contact.len); if(s->contact.s== NULL) { lock_release(&htable[hash_code].lock); LM_ERR("no more shared memory\n"); return -1; } memcpy(s->contact.s, subs->contact.s, subs->contact.len); s->contact.len= subs->contact.len; } s->status= subs->status; s->event= subs->event; subs->db_flag= s->db_flag; if(s->db_flag == NO_UPDATEDB_FLAG) s->db_flag= UPDATEDB_FLAG; if(fallback2db && type == LOCAL_TYPE) s->db_flag = NO_UPDATEDB_FLAG; lock_release(&htable[hash_code].lock); return 0; } phtable_t* new_phtable(void) { phtable_t* htable= NULL; int i, j; i = 0; htable= (phtable_t*)shm_malloc(phtable_size* sizeof(phtable_t)); if(htable== NULL) { ERR_MEM(SHARE_MEM); } memset(htable, 0, phtable_size* sizeof(phtable_t)); for(i= 0; i< phtable_size; i++) { if(lock_init(&htable[i].lock)== 0) { LM_ERR("initializing lock [%d]\n", i); goto error; } htable[i].entries= (pres_entry_t*)shm_malloc(sizeof(pres_entry_t)); if(htable[i].entries== NULL) { ERR_MEM(SHARE_MEM); } memset(htable[i].entries, 0, sizeof(pres_entry_t)); htable[i].entries->next= NULL; } return htable; error: if(htable) { for(j=0; j< i; j++) { if(htable[i].entries) shm_free(htable[i].entries); else break; lock_destroy(&htable[i].lock); } shm_free(htable); } return NULL; } void destroy_phtable(void) { int i; pres_entry_t* p, *prev_p; if(pres_htable== NULL) return; for(i= 0; i< phtable_size; i++) { lock_destroy(&pres_htable[i].lock); p= pres_htable[i].entries; while(p) { prev_p= p; p= p->next; if(prev_p->sphere) shm_free(prev_p->sphere); shm_free(prev_p); } } shm_free(pres_htable); } /* entry must be locked before calling this function */ pres_entry_t* search_phtable(str* pres_uri,int event, unsigned int hash_code) { pres_entry_t* p; LM_DBG("pres_uri= %.*s, event=%d\n", pres_uri->len, pres_uri->s, event); p= pres_htable[hash_code].entries->next; while(p) { if(p->event== event && p->pres_uri.len== pres_uri->len && strncmp(p->pres_uri.s, pres_uri->s, pres_uri->len)== 0 ) { return p; } p= p->next; } return NULL; } pres_entry_t* search_phtable_etag(str* pres_uri, int event, str* etag, unsigned int hash_code) { pres_entry_t* p; LM_DBG("pres_uri= %.*s, event=%d, etag= %.*s\n", pres_uri->len, pres_uri->s, event, etag->len, etag->s); p= pres_htable[hash_code].entries->next; while(p) { LM_DBG("found etag = %.*s\n", p->etag_len, p->etag); if( p->event== event && p->pres_uri.len== pres_uri->len && strncmp(p->pres_uri.s, pres_uri->s, pres_uri->len)== 0 && p->etag_len == etag->len && strncmp(p->etag, etag->s, etag->len)== 0 ) { return p; } p= p->next; } return NULL; } void update_pres_etag(pres_entry_t* p, str* etag) { LM_DBG("etag = %.*s\n", etag->len, etag->s); memcpy(p->etag, etag->s, etag->len); p->etag_len = etag->len; p->etag_count++; } pres_entry_t* insert_phtable(str* pres_uri, int event, str* etag, char* sphere, int init_turn) { unsigned int hash_code; pres_entry_t* p= NULL; int size; size= sizeof(pres_entry_t)+ pres_uri->len; p= (pres_entry_t*)shm_malloc(size); if(p== NULL) { ERR_MEM(SHARE_MEM); } memset(p, 0, size); size= sizeof(pres_entry_t); p->pres_uri.s= (char*)p+ size; memcpy(p->pres_uri.s, pres_uri->s, pres_uri->len); p->pres_uri.len= pres_uri->len; if(sphere) { p->sphere= (char*)shm_malloc(strlen(sphere)+ 1); if(p->sphere== NULL) { ERR_MEM(SHARE_MEM); } strcpy(p->sphere, sphere); } p->event= event; update_pres_etag(p, etag); hash_code= core_hash(pres_uri, NULL, phtable_size); lock_get(&pres_htable[hash_code].lock); p->next= pres_htable[hash_code].entries->next; pres_htable[hash_code].entries->next= p; p->last_turn = init_turn; lock_release(&pres_htable[hash_code].lock); return p; error: if(p) shm_free(p); return NULL; } int delete_phtable_query(str *pres_uri, int event, str* etag) { pres_entry_t* p; unsigned int hash_code; hash_code = core_hash(pres_uri, 0, phtable_size); lock_get(&pres_htable[hash_code].lock); p = search_phtable_etag(pres_uri, event, etag, hash_code); if(p == NULL) { LM_ERR("Record not found [%.*s]\n", etag->len, etag->s); lock_release(&pres_htable[hash_code].lock); return -1; } delete_phtable(p, hash_code); lock_release(&pres_htable[hash_code].lock); return 0; } void next_turn_phtable(pres_entry_t* p_p, unsigned int hash_code) { pres_entry_t* p; lock_get(&pres_htable[hash_code].lock); for ( p=pres_htable[hash_code].entries->next ; p ; p=p->next ) { if(p==p_p) { p->current_turn++; LM_DBG("new current turn is %d for <%.*s>\n",p->current_turn, p_p->pres_uri.len, p_p->pres_uri.s); break; } } lock_release(&pres_htable[hash_code].lock); return; } int delete_phtable(pres_entry_t* p, unsigned int hash_code) { pres_entry_t* prev_p= NULL; LM_DBG("Count = 0, delete\n"); /* delete record */ prev_p= pres_htable[hash_code].entries; while(prev_p->next) { if(prev_p->next== p) break; prev_p= prev_p->next; } if(prev_p->next== NULL) { LM_ERR("record not found\n"); return -1; } prev_p->next= p->next; if(p->sphere) shm_free(p->sphere); shm_free(p); return 0; } int update_phtable(presentity_t* presentity, str pres_uri, str body) { char* sphere= NULL; unsigned int hash_code; pres_entry_t* p; int ret= 0; str* xcap_doc= NULL; /* get new sphere */ sphere= extract_sphere(body); if(sphere==NULL) { LM_DBG("no sphere defined in new body\n"); return 0; } /* search for record in hash table */ hash_code= core_hash(&pres_uri, NULL, phtable_size); lock_get(&pres_htable[hash_code].lock); p= search_phtable(&pres_uri, presentity->event->evp->parsed, hash_code); if(p== NULL) { lock_release(&pres_htable[hash_code].lock); goto done; } if(p->sphere) { if(strcmp(p->sphere, sphere)!= 0) { /* new sphere definition */ shm_free(p->sphere); } else { /* no change in sphere definition */ lock_release(&pres_htable[hash_code].lock); pkg_free(sphere); return 0; } } p->sphere= (char*)shm_malloc(strlen(sphere)+ 1); if(p->sphere== NULL) { lock_release(&pres_htable[hash_code].lock); ret= -1; goto done; } strcpy(p->sphere, sphere); lock_release(&pres_htable[hash_code].lock); /* call for watchers status update */ if(presentity->event->get_rules_doc(&presentity->user, &presentity->domain, &xcap_doc)< 0) { LM_ERR("failed to retrieve xcap document\n"); ret= -1; goto done; } update_watchers_status(pres_uri, presentity->event, xcap_doc); done: if(xcap_doc) { if(xcap_doc->s) pkg_free(xcap_doc->s); pkg_free(xcap_doc); } if(sphere) pkg_free(sphere); return ret; } opensips-2.2.2/modules/presence/hash.h000066400000000000000000000104011300170765700177270ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-08-20 initial version (Anca Vamanu) */ #ifndef PS_HASH_H #define PS_HASH_H #include "../../lock_ops.h" #include "../../str.h" //#include "presentity.h" struct presentity; #define REMOTE_TYPE 1<<1 #define LOCAL_TYPE 1<<2 #define PKG_MEM_STR "pkg" #define SHARE_MEM "share" #define ETAG_LEN 128 #define ERR_MEM(mem_type) \ do { LM_ERR("No more %s memory\n",mem_type);\ goto error;\ } while(0) #define CONT_COPY_P_x(buf, dest, source)\ do{ dest= (str*)((char*)buf+ size);\ size+= sizeof(str);\ dest->s = (char*)buf + size;\ memcpy(dest->s, source->s, source->len);\ dest->len= source->len;\ size+= source->len;\ } while(0) #define CONT_COPY(buf, dest, source)\ do{ dest.s= (char*)buf+ size;\ memcpy(dest.s, source.s, source.len);\ dest.len= source.len;\ size+= source.len;\ } while(0) #define PKG_MEM_TYPE 0 #define SHM_MEM_TYPE 1 /* subscribe hash entry */ struct subscription; typedef struct subs_entry { struct subscription* entries; gen_lock_t lock; }subs_entry_t; typedef subs_entry_t* shtable_t; shtable_t new_shtable(int hash_size); struct subscription* search_shtable(shtable_t htable, str callid,str to_tag,str from_tag, unsigned int hash_code); int insert_shtable(shtable_t htable, unsigned int hash_code, struct subscription* subs); int delete_shtable(shtable_t htable, unsigned int hash_code, str to_tag); int update_shtable(shtable_t htable, unsigned int hash_code, struct subscription* subs, int type); struct subscription* mem_copy_subs(struct subscription* s, int mem_type); void free_subs_list(struct subscription* s_array, int mem_type, int ic); void destroy_shtable(shtable_t htable, int hash_size); /* subs htable functions type definitions */ typedef shtable_t (*new_shtable_t)(int hash_size); typedef struct subscription* (*search_shtable_t)(shtable_t htable, str callid,str to_tag, str from_tag, unsigned int hash_code); typedef int (*insert_shtable_t)(shtable_t htable, unsigned int hash_code, struct subscription* subs); typedef int (*delete_shtable_t)(shtable_t htable, unsigned int hash_code, str to_tag); typedef int (*update_shtable_t)(shtable_t htable, unsigned int hash_code, struct subscription* subs, int type); typedef void (*destroy_shtable_t)(shtable_t htable, int hash_size); typedef struct subscription* (*mem_copy_subs_t)(struct subscription* s, int mem_type); void free_subs(struct subscription* s); /* presentity hash table */ typedef struct pres_entry { str pres_uri; int event; int etag_count; char* sphere; char etag[ETAG_LEN]; int etag_len; /* ordering */ unsigned int current_turn; unsigned int last_turn; struct pres_entry* next; }pres_entry_t; typedef struct pres_htable { pres_entry_t* entries; gen_lock_t lock; }phtable_t; phtable_t* new_phtable(void); pres_entry_t* search_phtable(str* pres_uri, int event, unsigned int hash_code); pres_entry_t* search_phtable_etag(str* pres_uri, int event, str* etag, unsigned int hash_code); void update_pres_etag(pres_entry_t* p, str* etag); pres_entry_t* insert_phtable(str* pres_uri, int event, str* etag, char* sphere, int init_turn); int update_phtable(struct presentity* presentity, str pres_uri, str body); void next_turn_phtable(pres_entry_t* p_p, unsigned int hash_code); int delete_phtable(pres_entry_t* p, unsigned int hash_code); int delete_phtable_query(str *pres_uri, int event, str* etag); void destroy_phtable(void); #endif opensips-2.2.2/modules/presence/notify.c000066400000000000000000001543221300170765700203220ustar00rootroot00000000000000/* * presence module- presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) * 2010-10-19 support for extra headers (osas) */ #include #include #include #include #include "../../trim.h" #include "../../ut.h" #include "../../globals.h" #include "../../str.h" #include "../../db/db.h" #include "../../db/db_val.h" #include "../../socket_info.h" #include "../tm/tm_load.h" #include "../pua/hash.h" #include "presentity.h" #include "presence.h" #include "notify.h" #include "utils_func.h" #define MAX_FORWARD 70 c_back_param* shm_dup_cbparam(subs_t*); void free_cbparam(c_back_param* cb_param); void p_tm_callback( struct cell *t, int type, struct tmcb_params *ps); int add_waiting_watchers(watcher_t* watchers, str pres_uri, str event); str str_to_user_col = str_init("to_user"); str str_username_col = str_init("username"); str str_domain_col = str_init("domain"); str str_body_col = str_init("body"); str str_extra_hdrs_col = str_init("extra_hdrs"); str str_to_domain_col = str_init("to_domain"); str str_watcher_username_col = str_init("watcher_username"); str str_watcher_domain_col = str_init("watcher_domain"); str str_event_id_col = str_init("event_id"); str str_event_col = str_init("event"); str str_etag_col = str_init("etag"); str str_from_tag_col = str_init("from_tag"); str str_to_tag_col = str_init("to_tag"); str str_callid_col = str_init("callid"); str str_local_cseq_col = str_init("local_cseq"); str str_remote_cseq_col = str_init("remote_cseq"); str str_record_route_col = str_init("record_route"); str str_contact_col = str_init("contact"); str str_expires_col = str_init("expires"); str str_status_col = str_init("status"); str str_reason_col = str_init("reason"); str str_socket_info_col = str_init("socket_info"); str str_local_contact_col = str_init("local_contact"); str str_version_col = str_init("version"); str str_presentity_uri_col = str_init("presentity_uri"); str str_inserted_time_col = str_init("inserted_time"); str str_received_time_col = str_init("received_time"); str str_id_col = str_init("id"); str str_sender_col = str_init("sender"); char* get_status_str(int status_flag) { switch(status_flag) { case ACTIVE_STATUS: return "active"; case PENDING_STATUS: return "pending"; case TERMINATED_STATUS: return "terminated"; case WAITING_STATUS: return "waiting"; } return NULL; } static void pkg_free_w(char* s) { pkg_free(s); } inline void printf_subs(subs_t* subs) { LM_DBG("\n\t[pres_uri]= %.*s\n\t[to_user]= %.*s\t[to_domain]= %.*s" "\n\t[w_user]= %.*s\t[w_domain]= %.*s\n\t[event]= %.*s\n\t[status]= %s" "\n\t[expires]= %u\n\t[callid]= %.*s\t[local_cseq]=%d" "\n\t[to_tag]= %.*s\t[from_tag]= %.*s""\n\t[contact]= %.*s" "\t[record_route]= %.*s\n",subs->pres_uri.len,subs->pres_uri.s, subs->to_user.len,subs->to_user.s,subs->to_domain.len, subs->to_domain.s,subs->from_user.len,subs->from_user.s, subs->from_domain.len,subs->from_domain.s,subs->event->name.len, subs->event->name.s,get_status_str(subs->status),subs->expires, subs->callid.len,subs->callid.s,subs->local_cseq,subs->to_tag.len, subs->to_tag.s,subs->from_tag.len, subs->from_tag.s,subs->contact.len, subs->contact.s,subs->record_route.len,subs->record_route.s); } int build_str_hdr(subs_t* subs, int is_body, str* hdr, str* extra_hdrs) { int len = 0; int extra_len = 0; int lexpire_len; char* lexpire_s; char* p; str status; int lexpire; if(hdr == NULL) { LM_ERR("NULL pointer\n"); return -1; } lexpire = subs->expires - expires_offset; if(lexpire < 0 ) lexpire = 0; lexpire_s = int2str(lexpire, &lexpire_len); status.s= get_status_str(subs->status); if(status.s== NULL) { LM_ERR("bad status flag= %d\n", subs->status); pkg_free(hdr->s); return -1; } status.len = strlen(status.s); len = 7 /*Event: */ + subs->event->name.len +4 /*;id=*/+ subs->event_id.len+ CRLF_LEN + 10 /*Contact: <*/ + subs->local_contact.len + 1/*>*/ + ((subs->sockinfo && subs->sockinfo->proto!=PROTO_UDP)? 15/*";transport=xxxx"*/:0) + CRLF_LEN + 20 /*Subscription-State: */ + status.len + ((subs->status== TERMINATED_STATUS)?(10/*;reason=*/+ subs->reason.len):9/*expires=*/ + lexpire_len) + CRLF_LEN + (is_body? (14 /*Content-Type: */+subs->event->content_type.len + CRLF_LEN):0); if(extra_hdrs && extra_hdrs->s && extra_hdrs->len) extra_len = extra_hdrs->len; hdr->s = (char*)pkg_malloc(extra_len + len); if(hdr->s== NULL) { LM_ERR("while allocating memory\n"); return -1; } p = hdr->s; if (extra_len) { memcpy(p, extra_hdrs->s, extra_len); p+= extra_hdrs->len; } memcpy(p ,"Event: ", 7); p+= 7; memcpy(p, subs->event->name.s, subs->event->name.len); p+= subs->event->name.len; if(subs->event_id.len && subs->event_id.s) { memcpy(p, ";id=", 4); p += 4; memcpy(p, subs->event_id.s, subs->event_id.len); p += subs->event_id.len; } memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; memcpy(p ,"Contact: <", 10); p += 10; memcpy(p, subs->local_contact.s, subs->local_contact.len); p += subs->local_contact.len; if (subs->sockinfo && subs->sockinfo->proto!=PROTO_UDP) { memcpy(p,";transport=",11); p += 11; p = proto2str(subs->sockinfo->proto, p); if (p == NULL) { LM_ERR("invalid proto\n"); pkg_free(hdr->s); return -1; } } *(p++) = '>'; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; memcpy(p, "Subscription-State: ", 20); p+= 20; memcpy(p, status.s, status.len); p += status.len; if(subs->status== TERMINATED_STATUS) { LM_DBG("state = terminated\n"); memcpy(p,";reason=", 8); p+= 8; memcpy(p, subs->reason.s ,subs->reason.len ); p+= subs->reason.len; memcpy(p, CRLF, CRLF_LEN); p+= CRLF_LEN; } else { memcpy(p,";expires=", 9); p += 9; memcpy(p, lexpire_s ,lexpire_len ); p += lexpire_len; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; } if(is_body && subs->event->content_type.s && subs->event->content_type.len) { memcpy(p,"Content-Type: ", 14); p += 14; memcpy(p, subs->event->content_type.s , subs->event->content_type.len); p += subs->event->content_type.len; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; } hdr->len = p - hdr->s; return 0; } int get_wi_subs_db(subs_t* subs, watcher_t* watchers) { // static db_ps_t my_ps = NULL; db_key_t query_cols[6]; db_op_t query_ops[6]; db_val_t query_vals[6]; db_key_t result_cols[6]; db_res_t *result = NULL; db_row_t *row = NULL ; db_val_t *row_vals = NULL; int n_result_cols = 0; int n_query_cols = 0; int i; int status_col, expires_col, from_user_col, from_domain_col, callid_col; subs_t s; query_cols[n_query_cols] = &str_presentity_uri_col; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= subs->pres_uri; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->event->wipeer->name; n_query_cols++; result_cols[status_col=n_result_cols++] = &str_status_col; result_cols[expires_col=n_result_cols++] = &str_expires_col; result_cols[from_user_col=n_result_cols++] = &str_watcher_username_col; result_cols[from_domain_col=n_result_cols++] = &str_watcher_domain_col; result_cols[callid_col=n_result_cols++] = &str_callid_col; if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) { LM_ERR("in use_table\n"); goto error; } // CON_PS_REFERENCE(pa_db) = &my_ps; if (pa_dbf.query (pa_db, query_cols, query_ops, query_vals, result_cols, n_query_cols, n_result_cols, 0, &result) < 0) { LM_ERR("querying active_watchers db table\n"); goto error; } if(result== NULL ) { goto error; } if(result->n <= 0) { LM_DBG("The query in db table for active subscription" " returned no result\n"); pa_dbf.free_result(pa_db, result); return 0; } for(i=0; in; i++) { row = &result->rows[i]; row_vals = ROW_VALUES(row); s.from_user.s= (char*)row_vals[from_user_col].val.string_val; s.from_user.len= strlen(s.from_user.s); s.from_domain.s= (char*)row_vals[from_domain_col].val.string_val; s.from_domain.len= strlen(s.from_domain.s); s.callid.s= (char*)row_vals[callid_col].val.string_val; s.callid.len= strlen(s.callid.s); s.event =subs->event->wipeer; s.status= row_vals[status_col].val.int_val; if(add_watcher_list(&s, watchers) <0) { LM_ERR("failed to add watcher to list\n"); goto error; } } pa_dbf.free_result(pa_db, result); return 0; error: if(result) pa_dbf.free_result(pa_db, result); return -1; } str* get_wi_notify_body(subs_t* subs, subs_t* watcher_subs) { str* notify_body = NULL; char* version_str; watcher_t *watchers = NULL; int len = 0; unsigned int hash_code; subs_t* s= NULL; int state = FULL_STATE_FLAG; hash_code = 0; version_str = int2str(subs->version, &len); if(version_str ==NULL) { LM_ERR("converting int to str\n "); goto error; } watchers= (watcher_t*)pkg_malloc(sizeof(watcher_t)); if(watchers== NULL) { ERR_MEM(PKG_MEM_STR); } memset(watchers, 0, sizeof(watcher_t)); if(watcher_subs != NULL) { if(add_watcher_list(watcher_subs, watchers)< 0) { LM_ERR("failed to add watcher to list\n"); goto error; } state = PARTIAL_STATE_FLAG; goto done; } if(fallback2db) { if(get_wi_subs_db(subs, watchers)< 0) { LM_ERR("getting watchers from database\n"); goto error; } } hash_code= core_hash(&subs->pres_uri, &subs->event->wipeer->name, shtable_size); lock_get(&subs_htable[hash_code].lock); s= subs_htable[hash_code].entries; while(s->next) { s= s->next; if(s->expires< (int)time(NULL)) { LM_DBG("expired record\n"); continue; } if(fallback2db && s->db_flag!= INSERTDB_FLAG) { LM_DBG("record already found in database\n"); continue; } if(s->event== subs->event->wipeer && s->pres_uri.len== subs->pres_uri.len && strncmp(s->pres_uri.s, subs->pres_uri.s,subs->pres_uri.len)== 0) { if(add_watcher_list(s, watchers)< 0) { LM_ERR("failed to add watcher to list\n"); lock_release(&subs_htable[hash_code].lock); goto error; } } } if( add_waiting_watchers(watchers, subs->pres_uri, subs->event->wipeer->name)< 0 ) { LM_ERR("failed to add waiting watchers\n"); goto error; } done: notify_body = create_winfo_xml(watchers,version_str,subs->pres_uri, subs->event->wipeer->name, state ); if(watcher_subs == NULL) lock_release(&subs_htable[hash_code].lock); if(notify_body== NULL) { LM_ERR("in function create_winfo_xml\n"); goto error; } free_watcher_list(watchers); return notify_body; error: if(notify_body) { if(notify_body->s) xmlFree(notify_body->s); pkg_free(notify_body); } free_watcher_list(watchers); return NULL; } void free_watcher_list(watcher_t* watchers) { watcher_t* w; while(watchers) { w= watchers; if(w->uri.s !=NULL) pkg_free(w->uri.s); if(w->id.s !=NULL) pkg_free(w->id.s); watchers= watchers->next; pkg_free(w); } } int add_watcher_list(subs_t* s, watcher_t* watchers) { watcher_t* w; w= (watcher_t*)pkg_malloc(sizeof(watcher_t)); if(w== NULL) { LM_ERR("No more private memory\n"); return -1; } w->status= s->status; if(uandd_to_uri(s->from_user, s->from_domain, &w->uri)<0) { LM_ERR("failed to create uri\n"); goto error; } w->id.s = (char*)pkg_malloc(s->callid.len+ 1); if(w->id.s == NULL) { LM_ERR("no more memory\n"); goto error; } memcpy(w->id.s, s->callid.s, s->callid.len); w->id.len = s->callid.len; w->id.s[w->id.len] = '\0'; w->next= watchers->next; watchers->next= w; return 0; error: if(w) { if(w->uri.s) pkg_free(w->uri.s); pkg_free(w); } return -1; } int watcher_found_in_list(watcher_t * watchers, str wuri) { watcher_t * w; w = watchers->next; while(w) { if(w->uri.len == wuri.len && strncmp(w->uri.s, wuri.s, wuri.len)== 0) return 1; w= w->next; } return 0; } int add_waiting_watchers(watcher_t* watchers, str pres_uri, str event) { // static db_ps_t my_ps = NULL; watcher_t * w; db_key_t query_cols[3]; db_val_t query_vals[3]; db_key_t result_cols[2]; db_res_t *result = NULL; db_row_t *row= NULL ; db_val_t *row_vals; int n_result_cols = 0; int n_query_cols = 0; int wuser_col, wdomain_col; str wuser, wdomain, wuri; int i; /* select from watchers table the users that have subscribed to the presentity * and have status pending */ query_cols[n_query_cols] = &str_presentity_uri_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = pres_uri; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = event; n_query_cols++; query_cols[n_query_cols] = &str_status_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val = PENDING_STATUS; n_query_cols++; result_cols[wuser_col=n_result_cols++] = &str_watcher_username_col; result_cols[wdomain_col=n_result_cols++] = &str_watcher_domain_col; if (pa_dbf.use_table(pa_db, &watchers_table) < 0) { LM_ERR("sql use table 'watchers_table' failed\n"); return -1; } // CON_PS_REFERENCE(pa_db) = &my_ps; if (pa_dbf.query (pa_db, query_cols, 0, query_vals, result_cols, n_query_cols, n_result_cols, 0, &result) < 0) { LM_ERR("failed to query %.*s table\n", watchers_table.len, watchers_table.s); if(result) pa_dbf.free_result(pa_db, result); return -1; } if(result== NULL) { LM_ERR("mysql query failed - null result\n"); return -1; } if (result->n<=0 ) { LM_DBG("The query returned no result\n"); pa_dbf.free_result(pa_db, result); return 0; } for(i=0; i< result->n; i++) { row = &result->rows[i]; row_vals = ROW_VALUES(row); wuser.s = (char*)row_vals[wuser_col].val.string_val; wuser.len = strlen(wuser.s); wdomain.s = (char*)row_vals[wdomain_col].val.string_val; wdomain.len = strlen(wdomain.s); if(uandd_to_uri(wuser, wdomain, &wuri)<0) { LM_ERR("creating uri from username and domain\n"); goto error; } if(watcher_found_in_list(watchers, wuri)) { pkg_free(wuri.s); continue; } w= (watcher_t*)pkg_malloc(sizeof(watcher_t)); if(w== NULL) { ERR_MEM(PKG_MEM_STR); } memset(w, 0, sizeof(watcher_t)); w->status= WAITING_STATUS; w->uri = wuri; w->id.s = (char*)pkg_malloc(w->uri.len*2 +1); if(w->id.s== NULL) { pkg_free(w->uri.s); pkg_free(w); ERR_MEM(PKG_MEM_STR); } to64frombits((unsigned char *)w->id.s, (const unsigned char*)w->uri.s, w->uri.len); w->id.len = strlen(w->id.s); w->next= watchers->next; watchers->next= w; } pa_dbf.free_result(pa_db, result); return 0; error: if(result) pa_dbf.free_result(pa_db, result); return -1; } str* build_empty_bla_body(str pres_uri) { xmlDocPtr doc; xmlNodePtr node; xmlAttrPtr attr; str* body= NULL; char* text; int len; char* entity= NULL; doc = xmlNewDoc(BAD_CAST "1.0"); if(doc== NULL) { LM_ERR("failed to construct xml document\n"); return NULL; } node = xmlNewNode(NULL, BAD_CAST "dialog-info"); if(node== NULL) { LM_ERR("failed to initialize node\n"); goto error; } xmlDocSetRootElement(doc, node); attr = xmlNewProp(node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:dialog-info"); if(attr== NULL) { LM_ERR("failed to initialize node attribute\n"); goto error; } attr = xmlNewProp(node, BAD_CAST "version", BAD_CAST "1"); if(attr== NULL) { LM_ERR("failed to initialize node attribute\n"); goto error; } attr = xmlNewProp(node, BAD_CAST "state", BAD_CAST "full"); if(attr== NULL) { LM_ERR("failed to initialize node attribute\n"); goto error; } entity = (char*)pkg_malloc(pres_uri.len+1); if(entity== NULL) { LM_ERR("no more memory\n"); goto error; } memcpy(entity, pres_uri.s, pres_uri.len); entity[pres_uri.len]= '\0'; attr = xmlNewProp(node, BAD_CAST "entity", BAD_CAST entity); if(attr== NULL) { LM_ERR("failed to initialize node attribute\n"); pkg_free(entity); goto error; } body = (str*) pkg_malloc(sizeof(str)); if(body== NULL) { LM_ERR("no more private memory"); pkg_free(entity); goto error; } xmlDocDumpMemory(doc,(xmlChar**)(void*)&text, &len); body->s = (char*) pkg_malloc(len); if(body->s == NULL) { LM_ERR("no more private memory"); pkg_free(body); pkg_free(entity); goto error; } memcpy(body->s, text, len); body->len= len; pkg_free(entity); xmlFreeDoc(doc); xmlFree(text); return body; error: xmlFreeDoc(doc); return NULL; } static inline db_res_t* pres_search_db(struct sip_uri* uri,str* ev_name, int* body_col, int* extra_hdrs_col, int* expires_col, int* etag_col) { /* static db_ps_t my_ps = NULL; */ db_key_t query_cols[5]; db_val_t query_vals[5]; db_key_t result_cols[5]; db_res_t *result = NULL; int n_result_cols = 0; int n_query_cols = 0; int i; static str query_str = str_init("received_time"); query_cols[n_query_cols] = &str_domain_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = uri->host; n_query_cols++; query_cols[n_query_cols] = &str_username_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = uri->user; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= *ev_name; n_query_cols++; result_cols[n_result_cols] = &str_body_col; *body_col=n_result_cols++; result_cols[n_result_cols] = &str_extra_hdrs_col; *extra_hdrs_col=n_result_cols++; result_cols[n_result_cols] = &str_expires_col; *expires_col=n_result_cols++; result_cols[n_result_cols] = &str_etag_col; *etag_col=n_result_cols++; if (pa_dbf.use_table(pa_db, &presentity_table) < 0) { LM_ERR("in use_table\n"); return NULL; } for(i= 0; i< n_query_cols; i++) { LM_DBG("qval [%i] = %.*s\n",i, query_vals[i].val.str_val.len, query_vals[i].val.str_val.s); } /* CON_PS_REFERENCE(pa_db) = &my_ps; */ if (pa_dbf.query (pa_db, query_cols, 0, query_vals, result_cols, n_query_cols, n_result_cols, &query_str, &result) < 0) { LM_ERR("failed to query %.*s table\n", presentity_table.len, presentity_table.s); if(result) pa_dbf.free_result(pa_db, result); return NULL; } return result; } str* get_presence_from_dialog(str* pres_uri, struct sip_uri* uri, unsigned int hash_code) { db_res_t *result = NULL; int body_col, extra_hdrs_col, expires_col, etag_col; str body; str* dialog_body; db_row_t *row= NULL ; db_val_t *row_vals; int i; int ringing_index = -1; int ringing_state = 0; int dlg_state; int state; pres_entry_t* p; lock_get(&pres_htable[hash_code].lock); p= search_phtable(pres_uri, (*dialog_event_p)->evp->parsed, hash_code); lock_release(&pres_htable[hash_code].lock); if(p == NULL) { LM_DBG("No record exists in hashtable, pres_uri=[%.*s] event=[dialog]\n", pres_uri->len, pres_uri->s); if(!fallback2db) return NULL; } result = pres_search_db(uri, &((*dialog_event_p)->name), &body_col, &extra_hdrs_col, &expires_col, &etag_col); if(result== NULL) return NULL; if (result->n<=0 ) { LM_DBG("The query returned no result, pres_uri=[%.*s] event=[dialog]\n", pres_uri->len, pres_uri->s); pa_dbf.free_result(pa_db, result); return NULL; } /* if there are more records - go through them until you find one with a dialog */ for(i = result->n -1; i>=0 ; i--) { row = &result->rows[i]; row_vals = ROW_VALUES(row); body.s = (char*)row_vals[body_col].val.string_val; if(body.s == NULL) { LM_ERR("NULL notify body record\n"); goto error; } body.len= strlen(body.s); if(get_dialog_state(body, &dlg_state) < 0) { LM_ERR("get dialog state failed\n"); goto error; } LM_DBG("dlg_state = %d = DLG_CONFIRMED= %d\n", dlg_state, DLG_CONFIRMED); if(dlg_state == DLG_CONFIRMED) break; if(dlg_state == DLG_DESTROYED) continue; if(ringing_index < 0) { ringing_index = i; ringing_state = dlg_state; } } pa_dbf.free_result(pa_db, result); LM_DBG("i = %d, ringing_inde = %d\n", i, ringing_index); if(i >= 0) /* if a confirmed index was found */ { state = dlg_state; } else { if(ringing_index > 0) /* if a ringind dialog was found */ state = ringing_state; else return FAKED_BODY; } dialog_body = xml_dialog_gen_presence(pres_uri, state); return dialog_body; error: if(result) pa_dbf.free_result(pa_db, result); return NULL; } str* get_p_notify_body(str pres_uri, pres_ev_t* event, str* etag, str* publ_body, str* contact, str* dbody, str* extra_hdrs, free_body_t** free_fct, int from_publish) { int body_col, extra_hdrs_col, expires_col, etag_col= 0; db_res_t *result = NULL; db_row_t *row= NULL ; db_val_t *row_vals; str** body_array= NULL; str* notify_body= NULL; int i, n= 0, len; int build_off_n= -1; str etags; str* body; int size= 0; struct sip_uri uri; unsigned int hash_code; int body_cnt = 0; str* dialog_body= NULL, *local_dialog_body = NULL; int init_i = 0; pres_entry_t* p; if(parse_uri(pres_uri.s, pres_uri.len, &uri)< 0) { LM_ERR("while parsing uri\n"); return NULL; } hash_code= core_hash(&pres_uri, NULL, phtable_size); if(mix_dialog_presence && event->evp->parsed == EVENT_PRESENCE) { if(!dbody || dbody==FAKED_BODY) { if(*dialog_event_p == NULL) { LM_ERR("You required mixing dialog and presence but you have" " not loaded 'dialog' event module (presence_dialoginfo)\n"); return NULL; } if(!event->agg_nbody) { LM_ERR("Conflict - If you want to mix dialog info with presence" " you have to keep the default 'presence' event configuration" " with aggregation enabled\n"); return NULL; } /* search also for 'dialog' event publications */ local_dialog_body = get_presence_from_dialog(&pres_uri, &uri, hash_code); dialog_body = (local_dialog_body==FAKED_BODY)?NULL:local_dialog_body; } else dialog_body = (dbody==FAKED_BODY)?NULL:dbody; if(dialog_body) { LM_DBG("Presence from 'dialog': %.*s\n", dialog_body->len, dialog_body->s); body_array = &dialog_body; /* only for the case no other presence info is found */ body_cnt = 1; } } /* search in hash table if any record exists */ lock_get(&pres_htable[hash_code].lock); p= search_phtable(&pres_uri, event->evp->parsed, hash_code); lock_release(&pres_htable[hash_code].lock); if( !etag && p== NULL) { LM_DBG("No record exists in hash_table\n"); if(!fallback2db) { /* for pidf manipulation && dialog-presence mixing */ if(event->agg_nbody) { /* broken - it will not work with pidf manipulation */ if(body_cnt == 0 && (mix_dialog_presence||notify_offline_body) && event->evp->parsed == EVENT_PRESENCE) { local_dialog_body = build_offline_presence(&pres_uri); if(local_dialog_body == NULL) { LM_ERR("Failed to build offline presence body\n"); goto error; } dialog_body= local_dialog_body; body_array = &dialog_body; /* only for the case no other presence info is found */ body_cnt = 1; } notify_body = event->agg_nbody(&uri.user, &uri.host, body_array, body_cnt, -1); if(notify_body) goto done; } if (event->build_empty_pres_info) { notify_body = event->build_empty_pres_info(&pres_uri, extra_hdrs); if(notify_body == NULL && event->mandatory_body) { LM_ERR("Failed to construct body\n"); return NULL; } if(notify_body) LM_DBG("Built empty pres info %p\n", notify_body->s); goto done; } return NULL; } } result = pres_search_db(&uri,&event->name,&body_col,&extra_hdrs_col,&expires_col,&etag_col); if(result== NULL) return NULL; if (result->n<=0 ) { LM_DBG("The query returned no result: [username]='%.*s'" " [domain]='%.*s' [event]='%.*s'\n",uri.user.len, uri.user.s, uri.host.len, uri.host.s, event->name.len, event->name.s); pa_dbf.free_result(pa_db, result); result= NULL; if(event->agg_nbody) { /* broken - it will not work with pidf manipulation */ if(body_cnt == 0 && (mix_dialog_presence||notify_offline_body) && event->evp->parsed == EVENT_PRESENCE) { local_dialog_body = build_offline_presence(&pres_uri); if(local_dialog_body == NULL) { LM_ERR("Failed to build offline presence body\n"); goto error; } dialog_body= local_dialog_body; body_array = &dialog_body; /* only for the case no other presence info is found */ body_cnt = 1; } notify_body = event->agg_nbody(&uri.user, &uri.host, body_array, body_cnt, -1); if(notify_body) { goto done; } } LM_DBG("build empty pres info\n"); if(event->build_empty_pres_info) { notify_body = event->build_empty_pres_info(&pres_uri, extra_hdrs); if(notify_body == NULL && event->mandatory_body) { LM_ERR("Failed to construct body\n"); return NULL; } goto done; } return NULL; } else { n= result->n; if(event->agg_nbody== NULL ) { LM_DBG("Event does not require aggregation\n"); row = &result->rows[n-1]; row_vals = ROW_VALUES(row); if(row_vals[extra_hdrs_col].val.string_val!= NULL) { len = strlen(row_vals[extra_hdrs_col].val.string_val); if (len > 0 && extra_hdrs && !extra_hdrs->s) { extra_hdrs->s = (char*)pkg_malloc(len); if (extra_hdrs->s == NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(extra_hdrs->s, row_vals[extra_hdrs_col].val.string_val, len); extra_hdrs->len = len; } } if(row_vals[body_col].val.string_val== NULL) { if (event->mandatory_body) LM_ERR("NULL notify body record\n"); goto error; } len= strlen(row_vals[body_col].val.string_val); if(len== 0) { if (event->mandatory_body) LM_ERR("Empty notify body record\n"); goto error; } notify_body= (str*)pkg_malloc(sizeof(str)); if(notify_body== NULL) { ERR_MEM(PKG_MEM_STR); } memset(notify_body, 0, sizeof(str)); notify_body->s= (char*)pkg_malloc(len); if(notify_body->s== NULL) { pkg_free(notify_body); ERR_MEM(PKG_MEM_STR); } memcpy(notify_body->s, row_vals[body_col].val.string_val, len); notify_body->len= len; pa_dbf.free_result(pa_db, result); *free_fct = (free_body_t*)pkg_free_w; return notify_body; } body_array =(str**)pkg_malloc( (n+3) *sizeof(str*)); if(body_array == NULL) { ERR_MEM(PKG_MEM_STR); } memset(body_array, 0, (n+3) *sizeof(str*)); init_i = body_cnt; if(etag!= NULL) { LM_DBG("searched etag = %.*s len= %d\n", etag->len, etag->s, etag->len); LM_DBG("etag not NULL\n"); for(i= 0; i< n; i++) { row = &result->rows[i]; row_vals = ROW_VALUES(row); etags.s = (char*)row_vals[etag_col].val.string_val; etags.len = strlen(etags.s); if(row_vals[extra_hdrs_col].val.string_val!= NULL) { len = strlen(row_vals[extra_hdrs_col].val.string_val); if (len > 0 && extra_hdrs && !extra_hdrs->s) { extra_hdrs->s = (char*)pkg_malloc(len); if (extra_hdrs->s == NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(extra_hdrs->s, row_vals[extra_hdrs_col].val.string_val, len); extra_hdrs->len = len; } } LM_DBG("etag = %.*s len= %d\n", etags.len, etags.s, etags.len); if( (etags.len == etag->len) && (strncmp(etags.s, etag->s,etags.len)==0 ) ) { LM_DBG("found etag\n"); build_off_n= body_cnt; } len= strlen((char*)row_vals[body_col].val.string_val); if(len== 0) { if (event->mandatory_body) LM_ERR("Empty notify body record\n"); goto error; } size= sizeof(str)+ len; body= (str*)pkg_malloc(size); if(body== NULL) { ERR_MEM(PKG_MEM_STR); } memset(body, 0, size); size= sizeof(str); body->s= (char*)body+ size; memcpy(body->s, (char*)row_vals[body_col].val.string_val, len); body->len= len; body_array[body_cnt++]= body; } } else { for(i=0; i< n; i++) { row = &result->rows[i]; row_vals = ROW_VALUES(row); if(row_vals[extra_hdrs_col].val.string_val!= NULL) { len = strlen(row_vals[extra_hdrs_col].val.string_val); if (len > 0 && extra_hdrs && !extra_hdrs->s) { extra_hdrs->s = (char*)pkg_malloc(len); if (extra_hdrs->s == NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(extra_hdrs->s, row_vals[extra_hdrs_col].val.string_val, len); extra_hdrs->len = len; } } len= strlen((char*)row_vals[body_col].val.string_val); if(len== 0) { if (event->mandatory_body) LM_ERR("Empty notify body record\n"); goto error; } size= sizeof(str)+ len; body= (str*)pkg_malloc(size); if(body== NULL) { ERR_MEM(PKG_MEM_STR); } memset(body, 0, size); size= sizeof(str); body->s= (char*)body+ size; memcpy(body->s, row_vals[body_col].val.string_val, len); body->len= len; body_array[body_cnt++]= body; } } pa_dbf.free_result(pa_db, result); result= NULL; /* put the dialog info extracted body if present */ if(dialog_body) { body_array[body_cnt++] = dialog_body; } /* if the Publish with expires=0 has body -> use this one */ if(etag && publ_body && build_off_n>=0) { pkg_free(body_array[build_off_n]); body_array[build_off_n] = publ_body; build_off_n = -1; } /* RFC 4235 states that a NOTIFY generated after an initial * or refreshed SUBSCRIBE request must contain a full-state notification. * In other cases, only the modified state should be notified using * a partial state notification. */ if (event->evp->parsed == EVENT_DIALOG && from_publish && publ_body) { /* Presence dialoginfo knows that special n value of -2 means we publish * a partial state. Calling the agg_nbody method is however required because * it builds the full NOTIFY body as described in the RFC (it adds the version * field to the body, defines if state is partial or full, ...). */ notify_body = event->agg_nbody(&uri.user, &uri.host, &publ_body, -2, build_off_n); if(notify_body) { goto done; } } notify_body = event->agg_nbody(&uri.user, &uri.host, body_array, body_cnt, build_off_n); if(notify_body == NULL) { LM_ERR("Failed to aggregate notify body\n"); goto error; } } done: if(body_array!=NULL && body_array!=&dialog_body) { for(i= init_i; i< init_i + n; i++) { if( body_array[i] && body_array[i] != publ_body ) pkg_free(body_array[i]); } pkg_free(body_array); } if(local_dialog_body && local_dialog_body!=FAKED_BODY && local_dialog_body->s) { xmlFree(local_dialog_body->s); pkg_free(local_dialog_body); } *free_fct = event->free_body; return notify_body; error: if(result!=NULL) pa_dbf.free_result(pa_db, result); if(local_dialog_body && local_dialog_body!=FAKED_BODY && local_dialog_body->s) { xmlFree(local_dialog_body->s); pkg_free(local_dialog_body); } if(body_array!=NULL && body_array!=&dialog_body) { for(i= init_i; i< init_i + n; i++) { if( body_array[i] && body_array[i] != publ_body ) pkg_free(body_array[i]); else break; } pkg_free(body_array); } return NULL; } int free_tm_dlg(dlg_t *td) { if(td) { if(td->loc_uri.s) pkg_free(td->loc_uri.s); if(td->rem_uri.s) pkg_free(td->rem_uri.s); if(td->route_set) free_rr(&td->route_set); pkg_free(td); } return 0; } dlg_t* build_dlg_t(subs_t* subs) { dlg_t* td =NULL; int found_contact = 1; td = (dlg_t*)pkg_malloc(sizeof(dlg_t)); if(td == NULL) { ERR_MEM(PKG_MEM_STR); } memset(td, 0, sizeof(dlg_t)); td->loc_seq.value = subs->local_cseq; td->loc_seq.is_set = 1; td->id.call_id = subs->callid; td->id.rem_tag = subs->from_tag; td->id.loc_tag =subs->to_tag; uandd_to_uri(subs->to_user, subs->to_domain, &td->loc_uri); if(td->loc_uri.s== NULL) { LM_ERR("while creating uri\n"); goto error; } if(subs->contact.len ==0 || subs->contact.s == NULL ) { found_contact = 0; } else { LM_DBG("CONTACT = %.*s\n", subs->contact.len , subs->contact.s); td->rem_target = subs->contact; } uandd_to_uri(subs->from_user, subs->from_domain, &td->rem_uri); if(td->rem_uri.s ==NULL) { LM_ERR("while creating uri\n"); goto error; } if(found_contact == 0) { td->rem_target = td->rem_uri; } if(subs->record_route.s && subs->record_route.len) { if(parse_rr_body(subs->record_route.s, subs->record_route.len, &td->route_set)< 0) { LM_ERR("in function parse_rr_body\n"); goto error; } } td->state= DLG_CONFIRMED ; td->send_sock = subs->sockinfo; return td; error: free_tm_dlg(td); return NULL; } int get_subs_db(str* pres_uri, pres_ev_t* event, str* sender, subs_t** s_array, int* n) { // static db_ps_t my_ps = NULL; db_key_t query_cols[7]; db_op_t query_ops[7]; db_val_t query_vals[7]; db_key_t result_cols[19]; int n_result_cols = 0, n_query_cols = 0; db_row_t *row ; db_val_t *row_vals ; db_res_t *result = NULL; int from_user_col, from_domain_col, from_tag_col; int to_user_col, to_domain_col, to_tag_col; int expires_col= 0,callid_col, cseq_col, i, reason_col; int version_col= 0, record_route_col = 0, contact_col = 0; int sockinfo_col= 0, local_contact_col= 0, event_id_col = 0; subs_t s, *s_new; int inc= 0; str sockinfo_str; int port, proto; str host; if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) { LM_ERR("in use_table\n"); return -1; } LM_DBG("querying database table = active_watchers\n"); query_cols[n_query_cols] = &str_presentity_uri_col; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = *pres_uri; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = event->name; n_query_cols++; query_cols[n_query_cols] = &str_status_col; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val = ACTIVE_STATUS; n_query_cols++; query_cols[n_query_cols] = &str_contact_col; query_ops[n_query_cols] = OP_NEQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; if(sender) { LM_DBG("Do not send Notify to:[uri]= %.*s\n",sender->len,sender->s); query_vals[n_query_cols].val.str_val = *sender; } else { query_vals[n_query_cols].val.str_val.s = ""; query_vals[n_query_cols].val.str_val.len = 0; } n_query_cols++; result_cols[to_user_col=n_result_cols++] = &str_to_user_col; result_cols[to_domain_col=n_result_cols++] = &str_to_domain_col; result_cols[from_user_col=n_result_cols++] = &str_watcher_username_col; result_cols[from_domain_col=n_result_cols++] = &str_watcher_domain_col; result_cols[event_id_col=n_result_cols++] = &str_event_id_col; result_cols[from_tag_col=n_result_cols++] = &str_from_tag_col; result_cols[to_tag_col=n_result_cols++] = &str_to_tag_col; result_cols[callid_col=n_result_cols++] = &str_callid_col; result_cols[cseq_col=n_result_cols++] = &str_local_cseq_col; result_cols[record_route_col=n_result_cols++] = &str_record_route_col; result_cols[contact_col=n_result_cols++] = &str_contact_col; result_cols[expires_col=n_result_cols++] = &str_expires_col; result_cols[reason_col=n_result_cols++] = &str_reason_col; result_cols[sockinfo_col=n_result_cols++] = &str_socket_info_col; result_cols[local_contact_col=n_result_cols++]= &str_local_contact_col; result_cols[version_col=n_result_cols++] = &str_version_col; //CON_PS_REFERENCE(pa_db) = &my_ps; if (pa_dbf.query(pa_db, query_cols, query_ops, query_vals,result_cols, n_query_cols, n_result_cols, 0, &result) < 0) { LM_ERR("while querying database\n"); if(result) { pa_dbf.free_result(pa_db, result); } return -1; } if(result== NULL) return -1; if(result->n <=0 ) { LM_DBG("The query for subscribtion for [uri]= %.*s for [event]= %.*s" " returned no result\n",pres_uri->len, pres_uri->s, event->name.len, event->name.s); pa_dbf.free_result(pa_db, result); return 0; } LM_DBG("found %d dialogs\n", result->n); for(i=0; in; i++) { row = &result->rows[i]; row_vals = ROW_VALUES(row); // if(row_vals[expires_col].val.int_val< (int)time(NULL)) // continue; if(row_vals[reason_col].val.string_val) { if(strlen(row_vals[reason_col].val.string_val) != 0) continue; } // s.reason.len= strlen(s.reason.s); memset(&s, 0, sizeof(subs_t)); s.status= ACTIVE_STATUS; s.pres_uri= *pres_uri; s.to_user.s= (char*)row_vals[to_user_col].val.string_val; s.to_user.len= strlen(s.to_user.s); s.to_domain.s= (char*)row_vals[to_domain_col].val.string_val; s.to_domain.len= strlen(s.to_domain.s); s.from_user.s= (char*)row_vals[from_user_col].val.string_val; s.from_user.len= strlen(s.from_user.s); s.from_domain.s= (char*)row_vals[from_domain_col].val.string_val; s.from_domain.len= strlen(s.from_domain.s); s.event_id.s=(char*)row_vals[event_id_col].val.string_val; s.event_id.len= (s.event_id.s)?strlen(s.event_id.s):0; s.to_tag.s= (char*)row_vals[to_tag_col].val.string_val; s.to_tag.len= strlen(s.to_tag.s); s.from_tag.s= (char*)row_vals[from_tag_col].val.string_val; s.from_tag.len= strlen(s.from_tag.s); s.callid.s= (char*)row_vals[callid_col].val.string_val; s.callid.len= strlen(s.callid.s); s.record_route.s= (char*)row_vals[record_route_col].val.string_val; s.record_route.len= (s.record_route.s)?strlen(s.record_route.s):0; s.contact.s= (char*)row_vals[contact_col].val.string_val; s.contact.len= strlen(s.contact.s); sockinfo_str.s = (char*)row_vals[sockinfo_col].val.string_val; if (sockinfo_str.s) { sockinfo_str.len = strlen(sockinfo_str.s); if (parse_phostport (sockinfo_str.s, sockinfo_str.len,&host.s, &host.len, &port, &proto )< 0) { LM_ERR("bad format for stored sockinfo string\n"); goto error; } s.sockinfo = grep_sock_info(&host, (unsigned short) port, (unsigned short) proto); } s.local_contact.s = (char*)row_vals[local_contact_col].val.string_val; s.local_contact.len = s.local_contact.s?strlen(s.local_contact.s):0; s.event= event; s.local_cseq = row_vals[cseq_col].val.int_val; if(row_vals[expires_col].val.int_val < (int)time(NULL)) s.expires = 0; else s.expires = row_vals[expires_col].val.int_val -(int)time(NULL); s.version = row_vals[version_col].val.int_val; s_new= mem_copy_subs(&s, PKG_MEM_TYPE); if(s_new== NULL) { LM_ERR("while copying subs_t structure\n"); goto error; } s_new->next= (*s_array); (*s_array)= s_new; printf_subs(s_new); inc++; } pa_dbf.free_result(pa_db, result); *n= inc; return 0; error: if(result) pa_dbf.free_result(pa_db, result); return -1; } int update_in_list(subs_t* s, subs_t* s_array, int new_rec_no, int n) { int i= 0; subs_t* ls; ls= s_array; while(i< new_rec_no) { i++; ls= ls->next; } for(i = 0; i< n; i++) { if(ls== NULL) { LM_ERR("wrong records count\n"); return -1; } printf_subs(ls); if(ls->callid.len== s->callid.len && strncmp(ls->callid.s, s->callid.s, s->callid.len)== 0 && ls->to_tag.len== s->to_tag.len && strncmp(ls->to_tag.s, s->to_tag.s, s->to_tag.len)== 0 && ls->from_tag.len== s->from_tag.len && strncmp(ls->from_tag.s, s->from_tag.s, s->from_tag.len)== 0 ) { ls->local_cseq= s->local_cseq; ls->expires= s->expires- (int)time(NULL); ls->version= s->version; ls->status= s->status; return 1; } ls= ls->next; } return -1; } subs_t* get_subs_dialog(str* pres_uri, pres_ev_t* event, str* sender) { unsigned int hash_code; subs_t* s= NULL, *s_new; subs_t* s_array= NULL; int n= 0, i= 0; /* if fallback2db -> should take all dialogs from db * and the only those dialogs from cache with db_flag= INSERTDB_FLAG */ if(fallback2db) { if(get_subs_db(pres_uri, event, sender, &s_array, &n)< 0) { LM_ERR("getting dialogs from database\n"); goto error; } } else { /* get records from hash table */ hash_code= core_hash(pres_uri, &event->name, shtable_size); lock_get(&subs_htable[hash_code].lock); s= subs_htable[hash_code].entries; while(s->next) { s= s->next; printf_subs(s); if(s->expires< (int)time(NULL)) { LM_DBG("expired subs\n"); continue; } if((!(s->status== ACTIVE_STATUS && s->reason.len== 0 && s->event== event && s->pres_uri.len== pres_uri->len && strncmp(s->pres_uri.s, pres_uri->s, pres_uri->len)== 0)) || (sender && sender->len== s->contact.len && strncmp(sender->s, s->contact.s, sender->len)== 0)) continue; s_new= mem_copy_subs(s, PKG_MEM_TYPE); if(s_new== NULL) { LM_ERR("copying subs_t structure\n"); lock_release(&subs_htable[hash_code].lock); goto error; } s_new->expires-= (int)time(NULL); s_new->next= s_array; s_array= s_new; i++; } lock_release(&subs_htable[hash_code].lock); n = i; } LM_DBG("found %d dialogs\n",n); return s_array; error: free_subs_list(s_array, PKG_MEM_TYPE, 0); return NULL; } int publ_notify(presentity_t* p, str pres_uri, str* body, str* offline_etag, str* rules_doc, str* dialog_body, int from_publish) { str *notify_body = NULL; str notify_extra_hdrs = {NULL, 0}; subs_t* subs_array= NULL, *s= NULL; int ret_code= -1; free_body_t* free_fct = 0; subs_array= get_subs_dialog(&pres_uri, p->event , p->sender); if(subs_array == NULL) { LM_DBG("Could not find subs_dialog\n"); ret_code= 0; goto done; } /* if the event does not require aggregation - we have the final body */ if(p->event->agg_nbody) { notify_body = get_p_notify_body(pres_uri, p->event , offline_etag, body, NULL, dialog_body, p->extra_hdrs?p->extra_hdrs:¬ify_extra_hdrs, &free_fct, from_publish); } s= subs_array; while(s) { s->auth_rules_doc= rules_doc; LM_INFO("notify\n"); if(notify(s, NULL, notify_body?notify_body:body, 0, p->extra_hdrs?p->extra_hdrs:¬ify_extra_hdrs, from_publish)< 0 ) { LM_ERR("Could not send notify for %.*s\n", p->event->name.len, p->event->name.s); } s= s->next; } ret_code= 0; done: free_subs_list(subs_array, PKG_MEM_TYPE, 0); if (notify_extra_hdrs.s) pkg_free(notify_extra_hdrs.s); if(notify_body!=NULL) { if(notify_body->s) { if( free_fct) free_fct(notify_body->s); else p->event->free_body(notify_body->s); } pkg_free(notify_body); } return ret_code; } int query_db_notify(str* pres_uri, pres_ev_t* event, subs_t* watcher_subs) { subs_t* subs_array = NULL, *s= NULL; str notify_extra_hdrs = {NULL, 0}; str* notify_body = NULL; int ret_code= -1; free_body_t* free_fct = 0; subs_array= get_subs_dialog(pres_uri, event , NULL); if(subs_array == NULL) { LM_DBG("Could not get subscription dialog\n"); ret_code= 1; goto done; } if(event->type & PUBL_TYPE) { notify_body = get_p_notify_body(*pres_uri, event, 0, 0, 0, 0, ¬ify_extra_hdrs, &free_fct, 0); } s= subs_array; while(s) { LM_INFO("notify\n"); if(notify(s, watcher_subs, notify_body, 0, NULL, 0)< 0 ) { LM_ERR("Could not send notify for [event]=%.*s\n", event->name.len, event->name.s); } s= s->next; } ret_code= 1; done: free_subs_list(subs_array, PKG_MEM_TYPE, 0); if (notify_extra_hdrs.s) pkg_free(notify_extra_hdrs.s); if(notify_body!=NULL) { if(notify_body->s) { if(event->type & WINFO_TYPE) pkg_free(notify_body->s); else if (free_fct) free_fct(notify_body->s); else event->free_body(notify_body->s); } pkg_free(notify_body); } return ret_code; } int send_notify_request(subs_t* subs, subs_t * watcher_subs, str* n_body,int force_null_body, str* extra_hdrs, int from_publish) { dlg_t* td = NULL; str met = {"NOTIFY", 6}; str str_hdr = {NULL, 0}; str notify_extra_hdrs = {NULL, 0}; str* notify_body = NULL; int result= 0; c_back_param *cb_param= NULL; str* final_body= NULL; str* aux_body = 0; free_body_t* free_fct = 0; LM_DBG("enter: have_body=%d force_null=%d dialog info:\n", (n_body!=0&&n_body->s!=0)?1:0, force_null_body); printf_subs(subs); /* getting the status of the subscription */ if(force_null_body) { goto jump_over_body; } if(n_body!= 0 && n_body->s!= 0 && subs->status== ACTIVE_STATUS) { if( subs->event->req_auth) { if(subs->auth_rules_doc && subs->event->apply_auth_nbody ) { if(subs->event->apply_auth_nbody(n_body, subs, ¬ify_body)< 0) { LM_ERR("in function apply_auth_nbody\n"); goto error; } } if(notify_body== NULL) notify_body= n_body; } else notify_body= n_body; } else { if(subs->status== TERMINATED_STATUS || subs->status== PENDING_STATUS) { LM_DBG("state terminated or pending- notify body NULL\n"); notify_body = NULL; } else { if(subs->event->type & WINFO_TYPE) { notify_body = get_wi_notify_body(subs, watcher_subs); if(notify_body == NULL) { LM_DBG("Could not get notify_body\n"); goto error; } } else { if (from_publish && n_body!= 0 && n_body->s!= 0) notify_body = n_body; else notify_body = get_p_notify_body(subs->pres_uri, subs->event, 0, 0, (subs->contact.s)?&subs->contact:NULL, NULL, extra_hdrs?extra_hdrs:¬ify_extra_hdrs, &free_fct, from_publish); if(notify_body == NULL || notify_body->s== NULL) { LM_DBG("Could not get the notify_body\n"); } else /* apply authorization rules if exists */ { if(subs->event->req_auth) { if(subs->auth_rules_doc && subs->event->apply_auth_nbody && subs->event->apply_auth_nbody(notify_body,subs,&final_body)<0) { LM_ERR("in function apply_auth\n"); goto error; } if(final_body) { free_fct(notify_body->s); pkg_free(notify_body); notify_body= final_body; } } LM_DBG("built notify_body %p\n", notify_body->s); } } } } jump_over_body: if(subs->expires<= 0) { subs->expires= 0; subs->status= TERMINATED_STATUS; subs->reason.s= "timeout"; subs->reason.len= 7; } /* build extra headers */ if( build_str_hdr( subs, notify_body?1:0, &str_hdr, extra_hdrs?extra_hdrs:¬ify_extra_hdrs)< 0 ) { LM_ERR("while building headers\n"); goto error; } LM_DBG("headers:\n%.*s\n", str_hdr.len, str_hdr.s); /* construct the dlg_t structure */ td = build_dlg_t(subs); if(td ==NULL) { LM_ERR("while building dlg_t structure\n"); goto error; } cb_param = shm_dup_cbparam(subs); if(cb_param == NULL) { LM_ERR("failed to duplicate cb_param in share memory\n"); goto error; } if (notify_body && subs->event->aux_body_processing) aux_body = subs->event->aux_body_processing(subs, notify_body); /* don't open new TCP connections if connection is down */ tcp_no_new_conn = 1; result = tmb.t_request_within (&met, /* method*/ &str_hdr, /* extra headers*/ aux_body?aux_body:notify_body, /* body*/ td, /* dialog structure*/ p_tm_callback, /* callback function*/ (void*)cb_param, /* callback parameter*/ NULL); tcp_no_new_conn = 0; if(aux_body) { if(aux_body->s) subs->event->aux_free_body(aux_body->s); pkg_free(aux_body); } if(result< 0) { LM_ERR("tmb.t_request_within failed\n"); free_cbparam(cb_param); goto error; } LM_INFO("NOTIFY %.*s via %.*s on behalf of %.*s for event %.*s, to_tag=%.*s, cseq=%d\n", td->rem_uri.len, td->rem_uri.s, td->hooks.next_hop->len, td->hooks.next_hop->s, td->loc_uri.len, td->loc_uri.s, subs->event->name.len, subs->event->name.s, td->id.loc_tag.len, td->id.loc_tag.s, td->loc_seq.value); free_tm_dlg(td); pkg_free(str_hdr.s); if (notify_extra_hdrs.s) pkg_free(notify_extra_hdrs.s); if((int)(long)n_body!= (int)(long)notify_body) { if(notify_body!=NULL) { if(notify_body->s!=NULL) { if(subs->event->type& WINFO_TYPE ) xmlFree(notify_body->s); else if(free_fct) free_fct(notify_body->s); else subs->event->free_body(notify_body->s); } pkg_free(notify_body); } } return 0; error: if(td) free_tm_dlg(td); if(str_hdr.s) pkg_free(str_hdr.s); if (notify_extra_hdrs.s) pkg_free(notify_extra_hdrs.s); if((int)(long)n_body!= (int)(long)notify_body) { if(notify_body!=NULL) { if(notify_body->s!=NULL) { if(subs->event->type& WINFO_TYPE) xmlFree(notify_body->s); else if(subs->event->apply_auth_nbody== NULL && subs->event->agg_nbody== NULL) pkg_free(notify_body->s); else subs->event->free_body(notify_body->s); } pkg_free(notify_body); } } return -1; } int notify(subs_t* subs, subs_t * watcher_subs, str* n_body, int force_null_body, str* extra_hdrs, int from_publish) { /* update first in hash table and the send Notify */ if(subs->expires!= 0 && subs->status != TERMINATED_STATUS) { unsigned int hash_code; hash_code= core_hash(&subs->pres_uri, &subs->event->name, shtable_size); if(update_shtable(subs_htable, hash_code, subs, LOCAL_TYPE)< 0) { LM_DBG("record not found in subs htable\n"); } if(fallback2db) { if(update_subs_db(subs, LOCAL_TYPE)< 0) { LM_ERR("updating subscription in database\n"); return -1; } } } if(subs->reason.s && subs->status== ACTIVE_STATUS && subs->reason.len== 12 && strncmp(subs->reason.s, "polite-block", 12)== 0) { force_null_body = 1; } if(send_notify_request(subs, watcher_subs, n_body, force_null_body, extra_hdrs, from_publish)< 0) { LM_ERR("sending Notify not successful\n"); return -1; } return 0; } void p_tm_callback( struct cell *t, int type, struct tmcb_params *ps) { c_back_param* cb; if(ps->param==NULL || *ps->param==NULL || ((c_back_param*)(*ps->param))->pres_uri.s == NULL || ((c_back_param*)(*ps->param))->ev_name.s== NULL || ((c_back_param*)(*ps->param))->to_tag.s== NULL) { LM_DBG("message id not received\n"); if(ps->param!=NULL && *ps->param !=NULL ) free_cbparam((c_back_param*)(*ps->param)); return; } cb = (c_back_param*)(*ps->param); if(ps->code == 200) { LM_DBG("completed with status [%d] and to_tag [%.*s]\n", ps->code, cb?cb->to_tag.len:0, cb?cb->to_tag.s:""); } else { LM_WARN("completed with status [%d] and to_tag [%.*s], cseq [%.*s]\n", ps->code, cb?cb->to_tag.len:0, cb?cb->to_tag.s:"", t->cseq_n.len, t->cseq_n.s); } if(ps->code == 481 || (end_sub_on_timeout && (ps->code==408)) ) { unsigned int hash_code; hash_code= core_hash(&cb->pres_uri, &cb->ev_name, shtable_size); delete_shtable(subs_htable, hash_code, cb->to_tag); delete_db_subs(cb->pres_uri, cb->ev_name, cb->to_tag); } if(cb != NULL) free_cbparam(cb); return ; } void free_cbparam(c_back_param* cb_param) { if(cb_param!= NULL) shm_free(cb_param); } c_back_param* shm_dup_cbparam(subs_t* subs) { int size; c_back_param* cb_param = NULL; size = sizeof(c_back_param)+ subs->pres_uri.len+ subs->event->name.len+ subs->to_tag.len; cb_param= (c_back_param*)shm_malloc(size); if(cb_param== NULL) { LM_ERR("no more shared memory"); return 0; } memset(cb_param, 0, size); size= sizeof(c_back_param); CONT_COPY(cb_param, cb_param->pres_uri, subs->pres_uri); CONT_COPY(cb_param, cb_param->ev_name, subs->event->name); CONT_COPY(cb_param, cb_param->to_tag, subs->to_tag); return cb_param; } str* create_winfo_xml(watcher_t* watchers, char* version, str resource, str event, int STATE_FLAG ) { xmlDocPtr doc = NULL; xmlNodePtr root_node = NULL, node = NULL; xmlNodePtr w_list_node = NULL; char content[200]; str *body= NULL; char* buffer= NULL; watcher_t* w; LIBXML_TEST_VERSION; doc = xmlNewDoc(BAD_CAST "1.0"); root_node = xmlNewNode(NULL, BAD_CAST "watcherinfo"); xmlDocSetRootElement(doc, root_node); xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:watcherinfo"); xmlNewProp(root_node, BAD_CAST "version", BAD_CAST version ); if(STATE_FLAG & FULL_STATE_FLAG) { if( xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "full") == NULL) { LM_ERR("while adding new attribute\n"); goto error; } } else { if( xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "partial")==NULL) { LM_ERR("while adding new attribute\n"); goto error; } } w_list_node =xmlNewChild(root_node, NULL, BAD_CAST "watcher-list",NULL); if( w_list_node == NULL) { LM_ERR("while adding child\n"); goto error; } buffer= (char*)pkg_malloc(resource.len+ 1); if(buffer== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(buffer, resource.s, resource.len); buffer[resource.len]= '\0'; xmlNewProp(w_list_node, BAD_CAST "resource", BAD_CAST buffer); pkg_free(buffer); buffer= (char*)pkg_malloc(event.len+1); if(buffer== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(buffer, event.s, event.len); buffer[event.len]= '\0'; xmlNewProp(w_list_node, BAD_CAST "package", BAD_CAST buffer); pkg_free(buffer); w= watchers->next; while(w) { strncpy( content,w->uri.s, w->uri.len); content[ w->uri.len ]='\0'; node = xmlNewChild(w_list_node, NULL, BAD_CAST "watcher", BAD_CAST content) ; if( node ==NULL) { LM_ERR("while adding child\n"); goto error; } if(xmlNewProp(node, BAD_CAST "id", BAD_CAST w->id.s)== NULL) { LM_ERR("while adding new attribute\n"); goto error; } if(xmlNewProp(node, BAD_CAST "event", BAD_CAST "subscribe")== NULL) { LM_ERR("while adding new attribute\n"); goto error; } if(xmlNewProp(node, BAD_CAST "status", BAD_CAST get_status_str(w->status))== NULL) { LM_ERR("while adding new attribute\n"); goto error; } w= w->next; } body = (str*)pkg_malloc(sizeof(str)); if(body == NULL) { ERR_MEM(PKG_MEM_STR); } memset(body, 0, sizeof(str)); xmlDocDumpMemory(doc,(xmlChar**)(void*)&body->s, &body->len); xmlFreeDoc(doc); xmlCleanupParser(); xmlMemoryDump(); return body; error: if(doc) xmlFreeDoc(doc); return NULL; } opensips-2.2.2/modules/presence/notify.h000066400000000000000000000064361300170765700203310ustar00rootroot00000000000000/* * presence module -presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) * 2010-10-19 support for extra headers (osas) */ #include "../../str.h" #include "../tm/dlg.h" #include "subscribe.h" #include "presentity.h" #ifndef NOTIFY_H #define NOTIFY_H #define FULL_STATE_FLAG (1<<0) #define PARTIAL_STATE_FLAG (1<<1) #define PRES_LEN 8 #define PWINFO_LEN 14 #define BLA_LEN 10 #define FAKED_BODY ((str *) -1) typedef struct watcher { str uri; str id; int status; str display_name; str expiration; str duration_subscribed; struct watcher* next; }watcher_t; typedef struct wid_cback { str pres_uri; str ev_name; str to_tag; /* to identify the exact record */ }c_back_param; extern str str_to_user_col; extern str str_username_col; extern str str_domain_col; extern str str_body_col; extern str str_extra_hdrs_col; extern str str_to_domain_col; extern str str_watcher_username_col; extern str str_watcher_domain_col; extern str str_event_id_col; extern str str_event_col; extern str str_etag_col; extern str str_from_tag_col; extern str str_to_tag_col; extern str str_callid_col; extern str str_local_cseq_col; extern str str_remote_cseq_col; extern str str_record_route_col; extern str str_contact_col; extern str str_expires_col; extern str str_status_col; extern str str_reason_col; extern str str_socket_info_col; extern str str_local_contact_col; extern str str_version_col; extern str str_presentity_uri_col; extern str str_inserted_time_col; extern str str_received_time_col; extern str str_id_col; extern str str_sender_col; void PRINT_DLG(FILE* out, dlg_t* _d); void printf_subs(subs_t* subs); int query_db_notify(str* pres_uri,pres_ev_t* event, subs_t* watcher_subs ); int publ_notify(presentity_t* p, str pres_uri, str* body, str* offline_etag, str* rules_doc, str* dialog_publish, int from_publish); int notify(subs_t* subs, subs_t* watcher_subs, str* n_body,int force_null_body, str* extra_hdrs, int from_publish); int send_notify_request(subs_t* subs, subs_t * watcher_subs, str* n_body,int force_null_body, str* extra_hdrs, int from_publish); char* get_status_str(int flag); void free_watcher_list(watcher_t* w); int add_watcher_list(subs_t* s, watcher_t* watchers); subs_t* get_subs_dialog(str* pres_uri, pres_ev_t* event, str* sender); str* create_winfo_xml(watcher_t* watchers, char* version, str resource, str event, int STATE_FLAG ); str* xml_dialog2presence(str* pres_uri, str* body); str* build_offline_presence(str* pres_uri); #endif opensips-2.2.2/modules/presence/presence.c000066400000000000000000001041031300170765700206060ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) * 2010-10-19 support for extra headers (osas) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../parser/parse_to.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../usr_avp.h" #include "../tm/tm_load.h" #include "../signaling/signaling.h" #include "../../pt.h" #include "../../mi/mi.h" #include "../pua/hash.h" #include "publish.h" #include "subscribe.h" #include "event_list.h" #include "bind_presence.h" #include "notify.h" #include "../../evi/evi_modules.h" #define S_TABLE_VERSION 4 #define P_TABLE_VERSION 5 #define ACTWATCH_TABLE_VERSION 11 char *log_buf = NULL; static int clean_period=100; static int watchers_clean_period=3600; static int db_update_period=100; /* database connection */ db_con_t *pa_db = NULL; db_func_t pa_dbf; str presentity_table= str_init("presentity"); str active_watchers_table = str_init("active_watchers"); str watchers_table= str_init("watchers"); int library_mode= 0; str server_address= {0, 0}; evlist_t* EvList= NULL; /* TM bind */ struct tm_binds tmb; /* SIGNALING bind */ struct sig_binds sigb; /** module functions */ static int mod_init(void); static int child_init(int); static void destroy(void); int stored_pres_info(struct sip_msg* msg, char* pres_uri, char* s); static int fixup_presence(void** param, int param_no); static int fixup_subscribe(void** param, int param_no); static struct mi_root* mi_refreshWatchers(struct mi_root* cmd, void* param); static struct mi_root* mi_cleanup(struct mi_root* cmd, void* param); static struct mi_root* mi_list_phtable(struct mi_root* cmd, void* param); static struct mi_root* mi_list_shtable(struct mi_root* cmd, void* param); static int update_pw_dialogs(subs_t* subs, unsigned int hash_code, subs_t** subs_array); int update_watchers_status(str pres_uri, pres_ev_t* ev, str* rules_doc); int refresh_send_winfo_notify(watcher_t* watcher, str pres_uri, struct pres_ev* ev); int counter =0; int pid = 0; char prefix='a'; str db_url = {0, 0}; int expires_offset = 0; int max_expires_subscribe= 3600; int max_expires_publish= 3600; int shtable_size= 9; shtable_t subs_htable= NULL; int fallback2db= 0; int sphere_enable= 0; int mix_dialog_presence= 0; int notify_offline_body= 0; /* if subscription should be automatically ended on SIP timeout 408 */ int end_sub_on_timeout= 1; /* holder for the pointer to presence event */ pres_ev_t** pres_event_p= NULL; pres_ev_t** dialog_event_p= NULL; int phtable_size= 9; phtable_t* pres_htable = NULL; unsigned int waiting_subs_daysno = 0; unsigned long waiting_subs_time = 3*24*3600; str bla_presentity_spec_param = {0, 0}; pv_spec_t bla_presentity_spec; int fix_remote_target=1; /* event id */ static str presence_publish_event = str_init("E_PRESENCE_PUBLISH"); event_id_t presence_event_id = EVI_ERROR; static cmd_export_t cmds[]= { {"handle_publish", (cmd_function)handle_publish, 0,fixup_presence,0, REQUEST_ROUTE}, {"handle_publish", (cmd_function)handle_publish, 1,fixup_presence, 0, REQUEST_ROUTE}, {"handle_subscribe",(cmd_function)handle_subscribe,0,fixup_subscribe,0, REQUEST_ROUTE}, {"handle_subscribe",(cmd_function)handle_subscribe,1,fixup_subscribe,0, REQUEST_ROUTE}, {"bind_presence", (cmd_function)bind_presence, 1, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static param_export_t params[]={ { "db_url", STR_PARAM, &db_url.s}, { "presentity_table", STR_PARAM, &presentity_table.s}, { "active_watchers_table", STR_PARAM, &active_watchers_table.s}, { "watchers_table", STR_PARAM, &watchers_table.s}, { "clean_period", INT_PARAM, &clean_period }, { "db_update_period", INT_PARAM, &db_update_period }, { "expires_offset", INT_PARAM, &expires_offset }, { "max_expires_subscribe", INT_PARAM, &max_expires_subscribe }, { "max_expires_publish", INT_PARAM, &max_expires_publish }, { "server_address", STR_PARAM, &server_address.s}, { "subs_htable_size", INT_PARAM, &shtable_size}, { "pres_htable_size", INT_PARAM, &phtable_size}, { "fallback2db", INT_PARAM, &fallback2db}, { "enable_sphere_check", INT_PARAM, &sphere_enable}, { "waiting_subs_daysno", INT_PARAM, &waiting_subs_daysno}, { "mix_dialog_presence", INT_PARAM, &mix_dialog_presence}, { "bla_presentity_spec", STR_PARAM, &bla_presentity_spec_param}, { "bla_fix_remote_target", INT_PARAM, &fix_remote_target}, { "notify_offline_body", INT_PARAM, ¬ify_offline_body}, { "end_sub_on_timeout", INT_PARAM, &end_sub_on_timeout}, {0,0,0} }; static mi_export_t mi_cmds[] = { { "refreshWatchers", 0, mi_refreshWatchers, 0, 0, 0}, { "cleanup", 0, mi_cleanup, 0, 0, 0}, { "pres_phtable_list", 0, mi_list_phtable, 0, 0, 0}, { "subs_phtable_list", 0, mi_list_shtable, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "db_url", get_deps_sqldb_url }, { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "presence", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { /* register event E_PRESENCE_NOTIFY */ if( (presence_event_id=evi_publish_event(presence_publish_event)) == EVI_ERROR ) LM_ERR("Cannot register E_PRESENCE_PUBLISH event\n"); else LM_NOTICE("E_PRESENCE_PUBLISH event registered\n"); db_url.len = db_url.s ? strlen(db_url.s) : 0; LM_DBG("db_url=%s/%d/%p\n", ZSW(db_url.s), db_url.len,db_url.s); presentity_table.len = strlen(presentity_table.s); active_watchers_table.len = strlen(active_watchers_table.s); watchers_table.len = strlen(watchers_table.s); LM_NOTICE("initializing module ...\n"); EvList= init_evlist(); if(!EvList) { LM_ERR("initializing event list\n"); return -1; } pres_event_p = (pres_ev_t**)shm_malloc(sizeof(pres_ev_t*)); dialog_event_p = (pres_ev_t**)shm_malloc(sizeof(pres_ev_t*)); if(pres_event_p == NULL || dialog_event_p == NULL) { LM_ERR("No more shared memory\n"); return -1; } *dialog_event_p = *pres_event_p = NULL; if(db_url.s== NULL) { library_mode= 1; LM_DBG("presence module used for library purpose only\n"); /* disable all MI commands (loading MI cmds is done after init) */ exports.mi_cmds = NULL; return 0; } if(expires_offset<0) expires_offset = 0; if(max_expires_subscribe<= 0) max_expires_subscribe = 3600; if(max_expires_publish<= 0) max_expires_publish = 3600; if(server_address.s== NULL) LM_DBG("server_address parameter not set in configuration file\n"); if(server_address.s) server_address.len= strlen(server_address.s); else server_address.len= 0; /* load SIGNALING API */ if(load_sig_api(&sigb)< 0) { LM_ERR("can't load signaling functions\n"); return -1; } /* load all TM stuff */ if(load_tm_api(&tmb)==-1) { LM_ERR("can't load tm functions\n"); return -1; } if(db_url.s== NULL) { LM_ERR("database url not set!\n"); return -1; } /* binding to database module */ if (db_bind_mod(&db_url, &pa_dbf)) { LM_ERR("Database module not found\n"); return -1; } if (!DB_CAPABILITY(pa_dbf, DB_CAP_ALL)) { LM_ERR("Database module does not implement all functions" " needed by presence module\n"); return -1; } pa_db = pa_dbf.init(&db_url); if (!pa_db) { LM_ERR("connecting to database failed\n"); return -1; } /*verify table versions */ if((db_check_table_version(&pa_dbf, pa_db, &presentity_table, P_TABLE_VERSION) < 0) || (db_check_table_version(&pa_dbf, pa_db, &active_watchers_table, ACTWATCH_TABLE_VERSION) < 0) || (db_check_table_version(&pa_dbf, pa_db, &watchers_table, S_TABLE_VERSION) < 0)) { LM_ERR("error during table version check\n"); return -1; } if(shtable_size< 1) shtable_size= 512; else shtable_size= 1<< shtable_size; subs_htable= new_shtable(shtable_size); if(subs_htable== NULL) { LM_ERR(" initializing subscribe hash table\n"); return -1; } if(restore_db_subs()< 0) { LM_ERR("restoring subscribe info from database\n"); return -1; } if(phtable_size< 1) phtable_size= 256; else phtable_size= 1<< phtable_size; pres_htable= new_phtable(); if(pres_htable== NULL) { LM_ERR("initializing presentity hash table\n"); return -1; } if(pres_htable_restore()< 0) { LM_ERR("filling in presentity hash table from database\n"); return -1; } if(clean_period>0) { register_timer("presence-pclean", msg_presentity_clean, 0, clean_period, TIMER_FLAG_DELAY_ON_DELAY); register_timer("presence-wclean", msg_watchers_clean, 0, watchers_clean_period, TIMER_FLAG_DELAY_ON_DELAY); } if(db_update_period>0) register_timer("presence-dbupdate", timer_db_update, 0, db_update_period, TIMER_FLAG_SKIP_ON_DELAY); if (pa_dbf.use_table(pa_db, &watchers_table) < 0) { LM_ERR("unsuccessful use table sql operation\n"); return -1; } if(pa_dbf.delete(pa_db, 0,0,0,0)< 0) { LM_ERR("deleting all records from database table\n"); return -1; } if(pa_db) pa_dbf.close(pa_db); pa_db = NULL; if(waiting_subs_daysno > 30) { LM_INFO("Too greater value for waiting_subs_daysno parameter." " 30 days, the maximum accepted value will be used instead\n"); waiting_subs_daysno = 30; } if(waiting_subs_daysno > 0) waiting_subs_time = waiting_subs_daysno*24*3600; if(bla_presentity_spec_param.s) { bla_presentity_spec_param.len = strlen(bla_presentity_spec_param.s); if(pv_parse_spec(&bla_presentity_spec_param, &bla_presentity_spec)==NULL) { LM_ERR("failed to parse bla_presentity spec\n"); return -2; } switch(bla_presentity_spec.type) { case PVT_NONE: case PVT_EMPTY: case PVT_NULL: case PVT_MARKER: case PVT_COLOR: LM_ERR("invalid bla_presentity spec\n"); return -3; default: ; } } return 0; } /** * Initialize children */ static int child_init(int rank) { LM_NOTICE("init_child [%d] pid [%d]\n", rank, getpid()); pid = my_pid(); if(library_mode) return 0; if (pa_dbf.init==0) { LM_CRIT("child_init: database not bound\n"); return -1; } pa_db = pa_dbf.init(&db_url); if (!pa_db) { LM_ERR("child %d: unsuccessful connecting to database\n", rank); return -1; } LM_DBG("child %d: Database connection opened successfully\n", rank); return 0; } /* * destroy function */ static void destroy(void) { LM_NOTICE("destroy module ...\n"); if(subs_htable && pa_db) timer_db_update(0, 0); if(subs_htable) destroy_shtable(subs_htable, shtable_size); if(pres_htable) destroy_phtable(); if(pa_db && pa_dbf.close) pa_dbf.close(pa_db); if(pres_event_p) shm_free(pres_event_p); if(dialog_event_p) shm_free(dialog_event_p); destroy_evlist(); } static int fixup_presence(void** param, int param_no) { pv_elem_t *model; str s; if(library_mode) { LM_ERR("Bad config - you can not call 'handle_publish' function" " (db_url not set)\n"); return -1; } if(param_no== 0) return 0; if(*param) { s.s = (char*)(*param); s.len = strlen(s.s); if(pv_parse_format(&s, &model)<0) { LM_ERR( "wrong format[%s]\n",(char*)(*param)); return E_UNSPEC; } *param = (void*)model; return 0; } LM_ERR( "null format\n"); return E_UNSPEC; } static int fixup_subscribe(void** param, int param_no) { if(library_mode) { LM_ERR("Bad config - you can not call 'handle_subscribe' function" " (db_url not set)\n"); return -1; } return 0; } /* * mi cmd: refreshWatchers * * * // can be: = 0 -> watchers autentification type or * != 0 -> publish type // * * */ static struct mi_root* mi_refreshWatchers(struct mi_root* cmd, void* param) { struct mi_node* node= NULL; str pres_uri, event; struct sip_uri uri; pres_ev_t* ev; str* rules_doc= NULL; int result; unsigned int refresh_type; LM_DBG("start\n"); node = cmd->node.kids; if(node == NULL) return init_mi_tree(404, "No parameters", 13); /* Get presentity URI */ pres_uri = node->value; if(pres_uri.s == NULL || pres_uri.len== 0) { LM_ERR( "empty uri\n"); return init_mi_tree(404, "Empty presentity URI", 20); } node = node->next; if(node == NULL) return 0; event= node->value; if(event.s== NULL || event.len== 0) { LM_ERR( "empty event parameter\n"); return init_mi_tree(400, "Empty event parameter", 21); } LM_DBG("event '%.*s'\n", event.len, event.s); node = node->next; if(node == NULL) return 0; if(node->value.s== NULL || node->value.len== 0) { LM_ERR( "empty event parameter\n"); return init_mi_tree(400, "Empty event parameter", 21); } if(str2int(&node->value, &refresh_type)< 0) { LM_ERR("converting string to int\n"); goto error; } if(node->next!= NULL) { LM_ERR( "Too many parameters\n"); return init_mi_tree(400, "Too many parameters", 19); } ev= contains_event(&event, NULL); if(ev== NULL) { LM_ERR( "wrong event parameter\n"); return 0; } if(refresh_type== 0) /* if a request to refresh watchers authorization*/ { if(ev->get_rules_doc== NULL) { LM_ERR("wrong request for a refresh watchers authorization status" "for an event that does not require authorization\n"); goto error; } if(parse_uri(pres_uri.s, pres_uri.len, &uri)< 0) { LM_ERR( "parsing uri\n"); goto error; } result= ev->get_rules_doc(&uri.user,&uri.host,&rules_doc); if(result< 0 || rules_doc==NULL || rules_doc->s== NULL) { LM_ERR( "no rules doc found for the user\n"); goto error; } if(update_watchers_status(pres_uri, ev, rules_doc)< 0) { LM_ERR("failed to update watchers\n"); goto error; } pkg_free(rules_doc->s); pkg_free(rules_doc); rules_doc = NULL; } else /* if a request to refresh Notified info */ { if(query_db_notify(&pres_uri, ev, NULL)< 0) { LM_ERR("sending Notify requests\n"); goto error; } } return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: if(rules_doc) { if(rules_doc->s) pkg_free(rules_doc->s); pkg_free(rules_doc); } return 0; } /* * mi cmd: cleanup * * */ static struct mi_root* mi_cleanup(struct mi_root* cmd, void* param) { LM_DBG("mi_cleanup:start\n"); (void)msg_watchers_clean(0,0); (void)msg_presentity_clean(0,0); return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } static inline int mi_print_phtable_record(struct mi_node *rpl, pres_entry_t* pres) { struct mi_node* node; struct mi_attr* attr; char* p; int len; node = add_mi_node_child(rpl, 0, "pres_uri", 8, pres->pres_uri.s, pres->pres_uri.len); if(node == NULL) goto error; p= int2str((unsigned long)pres->event, &len); attr = add_mi_attr(node, MI_DUP_VALUE, "event", 5, p, len); if(attr == NULL) goto error; p= int2str((unsigned long)pres->etag_count, &len); attr = add_mi_attr(node, MI_DUP_VALUE, "etag_count", 10, p, len); if(attr == NULL) goto error; if (pres->sphere) { attr = add_mi_attr(node, MI_DUP_VALUE, "sphere", 6, pres->sphere, strlen(pres->sphere)); if(attr == NULL) goto error; } attr = add_mi_attr(node, MI_DUP_VALUE, "etag", 4, pres->etag, pres->etag_len); if(attr == NULL) goto error; return 0; error: LM_ERR("failed to add node\n"); return -1; } static struct mi_root* mi_list_phtable(struct mi_root* cmd, void* param) { struct mi_root *rpl_tree; struct mi_node* rpl; pres_entry_t* p; unsigned int i; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; for(i= 0; inext; while(p) { if(mi_print_phtable_record(rpl, p)<0) goto error; p= p->next;; } lock_release(&pres_htable[i].lock); } return rpl_tree; error: lock_release(&pres_htable[i].lock); LM_ERR("Unable to create reply\n"); free_mi_tree(rpl_tree); return NULL; } static inline int mi_print_shtable_record(struct mi_node *rpl, subs_t* s) { struct mi_node *node, *node1; struct mi_attr *attr; time_t _ts; char date_buf[MI_DATE_BUF_LEN]; int date_buf_len; char *p; int len; node = add_mi_node_child(rpl, 0, "pres_uri", 8, s->pres_uri.s, s->pres_uri.len); if (node==NULL) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "event", 5, s->event->name.s, s->event->name.len); if (attr==NULL) goto error; /* attr = add_mi_attr(node, MI_DUP_VALUE, "event_id", 8, s->event_id.s, s->event_id.len); if (attr==NULL) goto error; */ if (attr==NULL) goto error; _ts = (time_t)s->expires; date_buf_len = strftime(date_buf, MI_DATE_BUF_LEN - 1, "%Y-%m-%d %H:%M:%S", localtime(&_ts)); if (date_buf_len != 0) { attr = add_mi_attr(node, MI_DUP_VALUE, "expires", 7, date_buf, date_buf_len); } else { p= int2str((unsigned long)s->expires, &len); attr = add_mi_attr(node, MI_DUP_VALUE, "expires", 7, p, len); } if (attr==NULL) goto error; p= int2str((unsigned long)s->db_flag, &len); attr = add_mi_attr(node, MI_DUP_VALUE, "db_flag", 7, p, len); if (attr==NULL) goto error; p= int2str((unsigned long)s->version, &len); attr = add_mi_attr(node, MI_DUP_VALUE, "version", 7, p, len); if (attr==NULL) goto error; node1 = add_mi_node_child(node, 0, "to_user", 7, s->to_user.s, s->to_user.len); if (node1==NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "to_domain", 9, s->to_domain.s, s->to_domain.len); if (attr==NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "to_tag", 6, s->to_tag.s, s->to_tag.len); if (attr==NULL) goto error; node1 = add_mi_node_child(node, 0, "from_user", 9, s->from_user.s, s->from_user.len); if (node1==NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "from_domain", 11, s->from_domain.s, s->from_domain.len); if (attr==NULL) goto error; attr = add_mi_attr(node1, MI_DUP_VALUE, "from_tag", 8, s->from_tag.s, s->from_tag.len); if (attr==NULL) goto error; node1 = add_mi_node_child(node, 0, "callid", 6, s->callid.s, s->callid.len); if (node1==NULL) goto error; p= int2str((unsigned long)s->local_cseq, &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "local_cseq", 10, p, len); if (attr==NULL) goto error; p= int2str((unsigned long)s->remote_cseq, &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "remote_cseq", 11, p, len); if (attr==NULL) goto error; return 0; error: LM_ERR("failed to add node\n"); return -1; } static struct mi_root* mi_list_shtable(struct mi_root* cmd, void* param) { struct mi_root *rpl_tree; struct mi_node* rpl; subs_t *s; unsigned int i,j; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==NULL) return NULL; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; for(i=0,j=0; i< shtable_size; i++) { lock_get(&subs_htable[i].lock); s = subs_htable[i].entries->next; while(s) { if(mi_print_shtable_record(rpl, s)<0) goto error; s= s->next; j++; } lock_release(&subs_htable[i].lock); if ((j % 50) == 0) flush_mi_tree(rpl_tree); } return rpl_tree; error: lock_release(&subs_htable[i].lock); LM_ERR("Unable to create reply\n"); free_mi_tree(rpl_tree); return NULL; } int pres_update_status(subs_t subs, str reason, db_key_t* query_cols, db_val_t* query_vals, int n_query_cols, subs_t** subs_array) { static db_ps_t my_del_ps = NULL; static db_ps_t my_upd_ps = NULL; db_key_t update_cols[5]; db_val_t update_vals[5]; int n_update_cols= 0; int u_status_col, u_reason_col, q_wuser_col, q_wdomain_col; int status; query_cols[q_wuser_col=n_query_cols]= &str_watcher_username_col; query_vals[n_query_cols].nul= 0; query_vals[n_query_cols].type= DB_STR; n_query_cols++; query_cols[q_wdomain_col=n_query_cols]= &str_watcher_domain_col; query_vals[n_query_cols].nul= 0; query_vals[n_query_cols].type= DB_STR; n_query_cols++; update_cols[u_status_col= n_update_cols]= &str_status_col; update_vals[u_status_col].nul= 0; update_vals[u_status_col].type= DB_INT; n_update_cols++; update_cols[u_reason_col= n_update_cols]= &str_reason_col; update_vals[u_reason_col].nul= 0; update_vals[u_reason_col].type= DB_STR; n_update_cols++; status= subs.status; if(subs.event->get_auth_status(&subs)< 0) { LM_ERR( "getting status from rules document\n"); return -1; } LM_DBG("subs.status= %d\n", subs.status); if(get_status_str(subs.status)== NULL) { LM_ERR("wrong status: %d\n", subs.status); return -1; } if(subs.status!= status || reason.len!= subs.reason.len || (reason.s && subs.reason.s && strncmp(reason.s, subs.reason.s, reason.len))) { /* update in watchers_table */ query_vals[q_wuser_col].val.str_val= subs.from_user; query_vals[q_wdomain_col].val.str_val= subs.from_domain; update_vals[u_status_col].val.int_val= subs.status; update_vals[u_reason_col].val.str_val= subs.reason; if (pa_dbf.use_table(pa_db, &watchers_table) < 0) { LM_ERR( "in use_table\n"); return -1; } /* if status is terminated and reason="deactivated", * delete the record from table */ if(subs.status == TERMINATED_STATUS && subs.reason.len==11 && strncmp(subs.reason.s, "deactivated", 11)==0) { CON_PS_REFERENCE(pa_db) = &my_del_ps; if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols)<0) { LM_ERR( "in sql delete\n"); return -1; } } else { CON_PS_REFERENCE(pa_db) = &my_upd_ps; if(pa_dbf.update(pa_db, query_cols, 0, query_vals, update_cols, update_vals, n_query_cols, n_update_cols)< 0) { LM_ERR( "in sql update\n"); return -1; } } /* save in the list all affected dialogs */ /* if status switches to terminated -> delete dialog */ if(update_pw_dialogs(&subs, subs.db_flag, subs_array)< 0) { LM_ERR( "extracting dialogs from [watcher]=%.*s@%.*s to" " [presentity]=%.*s\n", subs.from_user.len, subs.from_user.s, subs.from_domain.len, subs.from_domain.s, subs.pres_uri.len, subs.pres_uri.s); return -1; } } return 0; } int pres_db_delete_status(subs_t* s) { static db_ps_t my_ps = NULL; int n_query_cols= 0; db_key_t query_cols[5]; db_val_t query_vals[5]; if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) { LM_ERR("sql use table failed\n"); return -1; } query_cols[n_query_cols]= &str_event_col; query_vals[n_query_cols].nul= 0; query_vals[n_query_cols].type= DB_STR; query_vals[n_query_cols].val.str_val= s->event->name ; n_query_cols++; query_cols[n_query_cols]= &str_presentity_uri_col; query_vals[n_query_cols].nul= 0; query_vals[n_query_cols].type= DB_STR; query_vals[n_query_cols].val.str_val= s->pres_uri; n_query_cols++; query_cols[n_query_cols]= &str_watcher_username_col; query_vals[n_query_cols].nul= 0; query_vals[n_query_cols].type= DB_STR; query_vals[n_query_cols].val.str_val= s->from_user; n_query_cols++; query_cols[n_query_cols]= &str_watcher_domain_col; query_vals[n_query_cols].nul= 0; query_vals[n_query_cols].type= DB_STR; query_vals[n_query_cols].val.str_val= s->from_domain; n_query_cols++; CON_PS_REFERENCE(pa_db) = &my_ps; if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols)< 0) { LM_ERR("sql delete failed\n"); return -1; } return 0; } int terminate_watchers(str *pres_uri, pres_ev_t* ev) { subs_t *all_s; subs_t *s; subs_t *tmp; /* get all watchers for the presentity */ all_s = get_subs_dialog( pres_uri, ev, NULL); if ( all_s==NULL ) { LM_DBG("No subscription dialogs found for <%.*s>\n", pres_uri->len, pres_uri->s); return 0; } /* set expire on 0 for all watchers */ for( s=all_s ; s ; ) { s->expires = 0; tmp = s; s = s->next; /* update subscription */ update_subscription( NULL, tmp, 0); } free_subs_list( all_s, PKG_MEM_TYPE, 0); return 0; } int update_watchers_status(str pres_uri, pres_ev_t* ev, str* rules_doc) { // static db_ps_t my_ps = NULL; subs_t subs; db_key_t query_cols[6], result_cols[5]; db_val_t query_vals[6]; int n_result_cols= 0, n_query_cols = 0; db_res_t* result= NULL; db_row_t *row; db_val_t *row_vals ; int i; str w_user, w_domain, reason= {0, 0}; unsigned int status; int status_col, w_user_col, w_domain_col, reason_col; subs_t* subs_array= NULL,* s; unsigned int hash_code; int err_ret= -1; int n= 0; watcher_t *watchers = NULL; typedef struct ws { int status; str reason; str w_user; str w_domain; }ws_t; ws_t* ws_list= NULL; LM_DBG("start\n"); if(ev->content_type.s== NULL) { ev= contains_event(&ev->name, NULL); if(ev== NULL) { LM_ERR("wrong event parameter\n"); return 0; } } subs.pres_uri= pres_uri; subs.event= ev; subs.auth_rules_doc= rules_doc; /* update in watchers_table */ query_cols[n_query_cols]= &str_presentity_uri_col; query_vals[n_query_cols].nul= 0; query_vals[n_query_cols].type= DB_STR; query_vals[n_query_cols].val.str_val= pres_uri; n_query_cols++; query_cols[n_query_cols]= &str_event_col; query_vals[n_query_cols].nul= 0; query_vals[n_query_cols].type= DB_STR; query_vals[n_query_cols].val.str_val= ev->name; n_query_cols++; result_cols[status_col= n_result_cols++]= &str_status_col; result_cols[reason_col= n_result_cols++]= &str_reason_col; result_cols[w_user_col= n_result_cols++]= &str_watcher_username_col; result_cols[w_domain_col= n_result_cols++]= &str_watcher_domain_col; if (pa_dbf.use_table(pa_db, &watchers_table) < 0) { LM_ERR( "in use_table\n"); goto done; } // CON_PS_REFERENCE(pa_db) = &my_ps; if(pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols,n_query_cols, n_result_cols, 0, &result)< 0) { LM_ERR( "in sql query\n"); goto done; } if(result== NULL) return 0; if(result->n<= 0) { err_ret= 0; goto done; } LM_DBG("found %d record-uri in watchers_table\n", result->n); hash_code= core_hash(&pres_uri, &ev->name, shtable_size); subs.db_flag= hash_code; /*must do a copy as sphere_check requires database queries */ if(sphere_enable) { n= result->n; ws_list= (ws_t*)pkg_malloc(n * sizeof(ws_t)); if(ws_list== NULL) { LM_ERR("No more private memory\n"); goto done; } memset(ws_list, 0, n * sizeof(ws_t)); for(i= 0; i< result->n ; i++) { row= &result->rows[i]; row_vals = ROW_VALUES(row); status= row_vals[status_col].val.int_val; reason.s= (char*)row_vals[reason_col].val.string_val; reason.len= reason.s?strlen(reason.s):0; w_user.s= (char*)row_vals[w_user_col].val.string_val; w_user.len= strlen(w_user.s); w_domain.s= (char*)row_vals[w_domain_col].val.string_val; w_domain.len= strlen(w_domain.s); if(reason.len) { ws_list[i].reason.s = (char*)pkg_malloc(reason.len); if(ws_list[i].reason.s== NULL) { LM_ERR("No more private memory\n"); goto done; } memcpy(ws_list[i].reason.s, reason.s, reason.len); ws_list[i].reason.len= reason.len; } else ws_list[i].reason.s= NULL; ws_list[i].w_user.s = (char*)pkg_malloc(w_user.len); if(ws_list[i].w_user.s== NULL) { LM_ERR("No more private memory\n"); goto done; } memcpy(ws_list[i].w_user.s, w_user.s, w_user.len); ws_list[i].w_user.len= w_user.len; ws_list[i].w_domain.s = (char*)pkg_malloc(w_domain.len); if(ws_list[i].w_domain.s== NULL) { LM_ERR("No more private memory\n"); goto done; } memcpy(ws_list[i].w_domain.s, w_domain.s, w_domain.len); ws_list[i].w_domain.len= w_domain.len; ws_list[i].status= status; } pa_dbf.free_result(pa_db, result); result= NULL; for(i=0; i< n; i++) { subs.from_user = ws_list[i].w_user; subs.from_domain = ws_list[i].w_domain; subs.status = ws_list[i].status; memset(&subs.reason, 0, sizeof(str)); if( pres_update_status(subs, reason, query_cols, query_vals, n_query_cols, &subs_array)< 0) { LM_ERR("failed to update watcher status\n"); goto done; } } for(i=0; i< n; i++) { pkg_free(ws_list[i].w_user.s); pkg_free(ws_list[i].w_domain.s); if(ws_list[i].reason.s) pkg_free(ws_list[i].reason.s); } ws_list= NULL; goto send_notify; } for(i = 0; i< result->n; i++) { row= &result->rows[i]; row_vals = ROW_VALUES(row); status= row_vals[status_col].val.int_val; reason.s= (char*)row_vals[reason_col].val.string_val; reason.len= reason.s?strlen(reason.s):0; w_user.s= (char*)row_vals[w_user_col].val.string_val; w_user.len= strlen(w_user.s); w_domain.s= (char*)row_vals[w_domain_col].val.string_val; w_domain.len= strlen(w_domain.s); subs.from_user= w_user; subs.from_domain= w_domain; subs.status= status; memset(&subs.reason, 0, sizeof(str)); if( pres_update_status(subs,reason, query_cols, query_vals, n_query_cols, &subs_array)< 0) { LM_ERR("failed to update watcher status\n"); goto done; } } pa_dbf.free_result(pa_db, result); result= NULL; send_notify: s= subs_array; watchers= (watcher_t*)pkg_malloc(sizeof(watcher_t)); if(watchers== NULL) { LM_ERR("no more pkg memory\n"); goto done; } memset(watchers, 0, sizeof(watcher_t)); while(s) { if(notify(s, NULL, NULL, 0, NULL, 0)< 0) { LM_ERR( "sending Notify request\n"); goto done; } /* delete from database also */ if(s->status== TERMINATED_STATUS) { if(pres_db_delete_status(s)<0) { err_ret= -1; LM_ERR("failed to delete terminated dialog from database\n"); goto done; } } if(add_watcher_list(s, watchers)< 0) { LM_ERR("failed to add watcher to list\n"); continue; } s= s->next; } if( refresh_send_winfo_notify(watchers, pres_uri, ev->wipeer) < 0) { LM_ERR("failed to send Notify for winfo\n"); goto done; } free_watcher_list(watchers); free_subs_list(subs_array, PKG_MEM_TYPE, 0); return 0; done: if(result) pa_dbf.free_result(pa_db, result); free_subs_list(subs_array, PKG_MEM_TYPE, 0); if(ws_list) { for(i= 0; i< n; i++) { if(ws_list[i].w_user.s) pkg_free(ws_list[i].w_user.s); else break; if(ws_list[i].w_domain.s) pkg_free(ws_list[i].w_domain.s); if(ws_list[i].reason.s) pkg_free(ws_list[i].reason.s); } } free_watcher_list(watchers); return err_ret; } int refresh_send_winfo_notify(watcher_t* watchers, str pres_uri, struct pres_ev* ev) { subs_t* subs_array= NULL, *s; str* winfo_nbody= NULL; char version[12]; /* send Notify for watcher info */ if(watchers->next== NULL) return 0; subs_array= get_subs_dialog(&pres_uri, ev, NULL); if(subs_array == NULL) { LM_DBG("Could not get subscription dialog\n"); return 0; } s= subs_array; while(s) { /* extract notify body */ sprintf(version, "%d", s->version); winfo_nbody = create_winfo_xml(watchers, version, pres_uri, ev->wipeer->name, PARTIAL_STATE_FLAG); if(winfo_nbody== NULL) { LM_ERR("failed to create winfo Notify body\n"); goto error; } if(notify(s, NULL, winfo_nbody, 0, NULL, 0)< 0 ) { LM_ERR("Could not send notify for [event]=%.*s\n", s->event->name.len, s->event->name.s); goto error; } s = s->next; } xmlFree(winfo_nbody->s); pkg_free(winfo_nbody); return 0; error: if(winfo_nbody) { if(winfo_nbody->s) xmlFree(winfo_nbody->s); pkg_free(winfo_nbody); } return -1; } static int update_pw_dialogs(subs_t* subs, unsigned int hash_code, subs_t** subs_array) { subs_t* s, *ps, *cs; int i= 0; LM_DBG("start\n"); lock_get(&subs_htable[hash_code].lock); ps= subs_htable[hash_code].entries; while(ps && ps->next) { s= ps->next; if(s->event== subs->event && s->pres_uri.len== subs->pres_uri.len && s->from_user.len== subs->from_user.len && s->from_domain.len==subs->from_domain.len && strncmp(s->pres_uri.s, subs->pres_uri.s, subs->pres_uri.len)== 0 && strncmp(s->from_user.s, subs->from_user.s, s->from_user.len)== 0 && strncmp(s->from_domain.s,subs->from_domain.s,s->from_domain.len)==0) { i++; s->status= subs->status; s->reason= subs->reason; s->db_flag= UPDATEDB_FLAG; cs= mem_copy_subs(s, PKG_MEM_TYPE); if(cs== NULL) { LM_ERR( "copying subs_t structure\n"); lock_release(&subs_htable[hash_code].lock); return -1; } cs->expires-= (int)time(NULL); cs->next= (*subs_array); (*subs_array)= cs; if(subs->status== TERMINATED_STATUS) { ps->next= s->next; shm_free(s->contact.s); shm_free(s); LM_DBG(" deleted terminated dialog from hash table\n"); /* delete from database also */ if( delete_db_subs(cs->pres_uri, cs->event->name, cs->to_tag)< 0) { LM_ERR("deleting subscription record from database\n"); lock_release(&subs_htable[hash_code].lock); pkg_free(cs); return -1; } } else ps= s; printf_subs(cs); } else ps= s; } LM_DBG("found %d matching dialogs\n", i); lock_release(&subs_htable[hash_code].lock); return 0; } opensips-2.2.2/modules/presence/presence.h000066400000000000000000000042271300170765700206210ustar00rootroot00000000000000/* * presence - presence server implementation * * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-10-09 first version (Anca Vamanu) */ #ifndef PRESENCE_MOD_H #define PRESENCE_MOD_H #include "../../parser/msg_parser.h" #include "../tm/tm_load.h" #include "../signaling/signaling.h" #include "../../db/db.h" #include "../../parser/parse_from.h" #include "event_list.h" #include "hash.h" /* TM bind */ extern struct tm_binds tmb; extern struct sig_binds sigb; /* DB module bind */ extern db_func_t pa_dbf; extern db_con_t* pa_db; /* PRESENCE database */ extern str db_url; extern str presentity_table; extern str active_watchers_table; extern str watchers_table; extern int counter; extern int pid; extern char *to_tag_pref; extern int expires_offset; extern str server_address; extern int max_expires_publish; extern int max_expires_subscribe; extern int fallback2db; extern int sphere_enable; extern int shtable_size; extern shtable_t subs_htable; extern int mix_dialog_presence; extern int notify_offline_body; extern int end_sub_on_timeout; extern int phtable_size; extern phtable_t* pres_htable; extern long waiting_subs_time; int update_watchers_status(str pres_uri, pres_ev_t* ev, str* rules_doc); int terminate_watchers(str *pres_uri, pres_ev_t* ev); extern str bla_presentity_spec_param; extern pv_spec_t bla_presentity_spec; extern int fix_remote_target; #endif /* PA_MOD_H */ opensips-2.2.2/modules/presence/presentity.c000066400000000000000000001115121300170765700212120ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) * 2010-10-19 support for extra headers (osas) */ #include #include #include #include #include "../../db/db.h" #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../str.h" #include "../../receive.h" #include "../../usr_avp.h" #include "../alias_db/alias_db.h" #include "../../data_lump_rpl.h" #include "presentity.h" #include "presence.h" #include "notify.h" #include "publish.h" #include "hash.h" #include "utils_func.h" #define DLG_STATES_NO 4 char* dialog_states[]= { "trying", "early", "confirmed", "terminated"}; char* presence_notes[]={ "Proceeding", "Proceeding", "On the phone", ""}; unsigned char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name); xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns); static str pu_200_rpl = str_init("OK"); static str pu_412_rpl = str_init("Conditional request failed"); static char etag_buf[ETAG_LEN]; int generate_ETag(int publ_count, str* etag) { etag->s = etag_buf; memset(etag_buf, 0, ETAG_LEN); etag->len = sprintf (etag_buf, "%c.%d.%d.%d.%d", prefix, (int)startup_time, pid, counter, publ_count); if( etag->len <0 ) { LM_ERR("unsuccessfull sprintf\n "); return -1; } if(etag->len > ETAG_LEN) { LM_ERR("buffer size overflown\n"); return -1; } LM_DBG("etag= %.*s\n",etag->len, etag->s); return 0; } int publ_send200ok(struct sip_msg *msg, int lexpire, str etag) { char buf[128]; int buf_len= 128, size; str hdr_append= {0, 0}, hdr_append2= {0, 0} ; LM_DBG("send 200OK reply, etag= %.*s\n", etag.len, etag.s); hdr_append.s = buf; hdr_append.s[0]='\0'; hdr_append.len = sprintf(hdr_append.s, "Expires: %d\r\n",((lexpire< expires_offset)?0: (lexpire - expires_offset))); if(hdr_append.len < 0) { LM_ERR("unsuccessful sprintf\n"); goto error; } if(hdr_append.len >= buf_len) { LM_ERR("buffer size overflown\n"); goto error; } hdr_append.s[hdr_append.len]= '\0'; if (add_lump_rpl( msg, hdr_append.s, hdr_append.len, LUMP_RPL_HDR)==0 ) { LM_ERR("unable to add lump_rl\n"); goto error; } size= 20 + etag.len; hdr_append2.s = (char *)pkg_malloc(size); if(hdr_append2.s == NULL) { ERR_MEM(PKG_MEM_STR); } hdr_append2.s[0]='\0'; hdr_append2.len = sprintf(hdr_append2.s, "SIP-ETag: %.*s\r\n", etag.len, etag.s); if(hdr_append2.len < 0) { LM_ERR("unsuccessful sprintf\n "); goto error; } if(hdr_append2.len+1 > size) { LM_ERR("buffer size overflown\n"); goto error; } hdr_append2.s[hdr_append2.len]= '\0'; if (add_lump_rpl(msg, hdr_append2.s, hdr_append2.len, LUMP_RPL_HDR)==0 ) { LM_ERR("unable to add lump_rl\n"); goto error; } if( sigb.reply( msg, 200, &pu_200_rpl, 0)== -1) { LM_ERR("sending reply\n"); goto error; } pkg_free(hdr_append2.s); return 0; error: if(hdr_append2.s) pkg_free(hdr_append2.s); return -1; } xmlAttrPtr xmlNodeGetAttrByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = node->properties; while (attr) { if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0) return attr; attr = attr->next; } return NULL; } unsigned char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = xmlNodeGetAttrByName(node, name); if (attr) return xmlNodeGetContent(attr->children); else return NULL; } xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name) { xmlNodePtr cur = node->children; while (cur) { if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) return cur; cur = cur->next; } return NULL; } #define bla_extract_dlginfo(node, callid, fromtag, totag) \ do {\ callid = xmlNodeGetAttrContentByName(node, "call-id");\ dir = xmlNodeGetAttrContentByName(node, "direction");\ if(dir == NULL) {\ LM_ERR("Dialog direction not specified\n");\ goto error;\ }\ if(xmlStrcasecmp(dir, (unsigned char*)"initiator") == 0) {\ fromtag = xmlNodeGetAttrContentByName(node, "local-tag");\ totag = xmlNodeGetAttrContentByName(node, "remote-tag");\ } else {\ totag = xmlNodeGetAttrContentByName(node, "local-tag");\ fromtag = xmlNodeGetAttrContentByName(node, "remote-tag");\ }\ xmlFree(dir);\ dir = NULL;\ }while(0) int bla_same_dialog(unsigned char* n_callid, unsigned char* n_fromtag, unsigned char* n_totag, unsigned char* o_callid, unsigned char* o_fromtag, unsigned char* o_totag) { if(n_callid && o_callid && xmlStrcasecmp(n_callid, o_callid)) return 0; if(n_fromtag && o_fromtag && xmlStrcasecmp(n_fromtag, o_fromtag)) return 0; if(n_totag && o_totag && xmlStrcasecmp(n_totag, o_totag)) return 0; return 1; } int dialog_fix_remote_target(str *body, str *fixed_body) { xmlDocPtr doc = NULL; xmlNodePtr n_dlg_node; xmlNodePtr remote_node; xmlNodePtr identity_node; xmlNodePtr node; xmlErrorPtr xml_error; unsigned char* attr; if (!fixed_body) { LM_ERR("invalid NULL fixed_body pointer\n"); goto error; } doc = xmlParseMemory(body->s, body->len); if (!doc) { xml_error = xmlGetLastError(); LM_ERR("Failed to parse xml dialog body: %s\n", xml_error ? xml_error->message : "unknown error"); goto error; } n_dlg_node = xmlNodeGetChildByName(doc->children, "dialog"); for(; n_dlg_node; n_dlg_node=n_dlg_node->next) { if(xmlStrcasecmp(n_dlg_node->name, (unsigned char*)"dialog")!= 0) continue; /* change the remote target - don't let it pass contact on the other side */ remote_node = xmlNodeGetChildByName(n_dlg_node, "remote"); if(remote_node) { node = xmlNodeGetChildByName(remote_node, "target"); if(node) { xmlUnlinkNode(node); xmlFreeNode(node); /* add another target node */ identity_node = xmlNodeGetChildByName(remote_node, "identity"); if(identity_node == NULL) { LM_ERR("No remote identity node found\n"); goto error; } attr = xmlNodeGetContent(identity_node); if(attr == NULL) { LM_ERR("No identity node content\n"); goto error; } node = xmlNewChild(remote_node, 0, (unsigned char*)"target", 0); if(node == NULL) { LM_ERR("Failed to add new node target\n"); xmlFree(attr); goto error; } xmlNewProp(node, BAD_CAST "uri", attr); xmlFree(attr); } } } xmlDocDumpMemory(doc, (xmlChar **)(void *)&fixed_body->s, &fixed_body->len); xmlFreeDoc(doc); return 0; error: if (doc) xmlFreeDoc(doc); return -1; } int get_dialog_state(str body, int *dialog_state) { xmlDocPtr doc; xmlNodePtr node; unsigned char* state = NULL; int i; doc = xmlParseMemory(body.s, body.len); if(doc== NULL) { LM_ERR("failed to parse xml document\n"); return -1; } node = doc->children; node = xmlNodeGetChildByName(node, "dialog"); if(node == NULL) { *dialog_state = DLG_DESTROYED; xmlFreeDoc(doc); return 0; } node = xmlNodeGetChildByName(node, "state"); if(node == NULL) { LM_ERR("Malformed document - no state found\n"); goto error; } state = xmlNodeGetContent(node); if(state == NULL) { LM_ERR("Malformed document - null state\n"); goto error; } LM_DBG("state = %s\n", state); for(i = 0; i< DLG_STATES_NO; i++) { if(xmlStrcasecmp(state, BAD_CAST dialog_states[i])==0) { break; } } xmlFree(state); xmlFreeDoc(doc); xmlCleanupParser(); xmlMemoryDump(); if(i == DLG_STATES_NO) { LM_ERR("Wrong dialog state\n"); return -1; } *dialog_state = i; return 0; error: xmlFreeDoc(doc); return -1; } int check_if_dialog(str body, int *is_dialog) { xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(body.s, body.len); if(doc== NULL) { LM_ERR("failed to parse xml document\n"); return -1; } node = doc->children; node = xmlNodeGetChildByName(node, "dialog"); if(node == NULL) *is_dialog = 0; else *is_dialog = 1; xmlFreeDoc(doc); return 0; } int update_presentity(struct sip_msg* msg, presentity_t* presentity, int* sent_reply) { // static db_ps_t my_ps_insert = NULL, my_ps_update_no_body = NULL, // my_ps_update_body = NULL; // static db_ps_t my_ps_delete = NULL, my_ps_query = NULL; db_key_t query_cols[13], update_keys[8], result_cols[1]; db_op_t query_ops[13]; db_val_t query_vals[13], update_vals[8]; int n_query_cols = 0; int n_update_cols = 0; str etag= {NULL, 0}; str notify_body = {NULL, 0}; str cur_etag= {NULL, 0}; str* rules_doc= NULL; str pres_uri= {NULL, 0}; pres_entry_t* p= NULL; unsigned int hash_code; unsigned int turn; str body = presentity->body; str *extra_hdrs = presentity->extra_hdrs; db_res_t *result= NULL; *sent_reply= 0; if(presentity->event->req_auth) { /* get rules_document */ if(presentity->event->get_rules_doc(&presentity->user, &presentity->domain, &rules_doc)) { LM_ERR("getting rules doc\n"); goto error; } } if(uandd_to_uri(presentity->user, presentity->domain, &pres_uri)< 0) { LM_ERR("constructing uri from user and domain\n"); goto error; } query_cols[n_query_cols] = &str_domain_col; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = presentity->domain; n_query_cols++; query_cols[n_query_cols] = &str_username_col; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = presentity->user; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = presentity->event->name; n_query_cols++; query_cols[n_query_cols] = &str_etag_col; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = presentity->etag; n_query_cols++; result_cols[0] = &str_etag_col; hash_code= core_hash(&pres_uri, NULL, phtable_size); if(presentity->etag_new) { if (msg && publ_send200ok(msg,presentity->expires,presentity->etag)<0) { LM_ERR("sending 200OK\n"); goto error; } *sent_reply= 1; /* insert new record in hash_table */ p = insert_phtable(&pres_uri, presentity->event->evp->parsed, &presentity->etag, presentity->sphere, 1); if (p==NULL) { LM_ERR("inserting record in hash table\n"); goto error; } /* insert new record into database */ query_cols[n_query_cols] = &str_expires_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val = presentity->expires+ (int)time(NULL); n_query_cols++; query_cols[n_query_cols] = &str_sender_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; if( presentity->sender) { query_vals[n_query_cols].val.str_val = *presentity->sender; } else { query_vals[n_query_cols].val.str_val.s = ""; query_vals[n_query_cols].val.str_val.len = 0; } n_query_cols++; query_cols[n_query_cols] = &str_body_col; query_vals[n_query_cols].type = DB_BLOB; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = body; n_query_cols++; query_cols[n_query_cols] = &str_received_time_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val = presentity->received_time; n_query_cols++; if(extra_hdrs) { query_cols[n_query_cols] = &str_extra_hdrs_col; query_vals[n_query_cols].type = DB_BLOB; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = *extra_hdrs; n_query_cols++; } if (pa_dbf.use_table(pa_db, &presentity_table) < 0) { LM_ERR("unsuccessful use_table\n"); goto error; } LM_DBG("inserting %d cols into table\n",n_query_cols); //CON_PS_REFERENCE(pa_db) = &my_ps_insert; if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols) < 0) { LM_ERR("inserting new record in database\n"); goto error; } goto send_notify; } else { lock_get(&pres_htable[hash_code].lock); p = search_phtable_etag(&pres_uri, presentity->event->evp->parsed, &presentity->etag, hash_code); if (p) { turn = p->last_turn++; LM_DBG("pres <%.*s> my turn is %d, current turn is %d\n",pres_uri.len, pres_uri.s, turn, p->current_turn); /* wait to get our turn as order of handling pubishs (need to wait the ongoing published to terminate before starting */ while (p && turn!=p->current_turn) { lock_release(&pres_htable[hash_code].lock); sleep_us(100); lock_get(&pres_htable[hash_code].lock); p = search_phtable_etag(&pres_uri, presentity->event->evp->parsed, &presentity->etag, hash_code); } } else { lock_release(&pres_htable[hash_code].lock); /* search also in db */ if (pa_dbf.use_table(pa_db, &presentity_table) < 0) { LM_ERR("unsuccessful sql use table\n"); goto error; } if (pa_dbf.query (pa_db, query_cols, query_ops, query_vals, result_cols, n_query_cols, 1, 0, &result) < 0) { LM_ERR("unsuccessful sql query\n"); goto error; } if(result== NULL) goto error; if (result->n <= 0) { pa_dbf.free_result(pa_db, result); LM_ERR("No E_Tag match [%.*s]\n", presentity->etag.len, presentity->etag.s); if (msg && sigb.reply(msg, 412, &pu_412_rpl, 0)==-1 ) { LM_ERR("sending '412 Conditional request failed' reply\n"); goto error; } *sent_reply= 1; goto done; } pa_dbf.free_result(pa_db, result); LM_INFO("*** found in db but not in htable [%.*s]\n", presentity->etag.len, presentity->etag.s); } /* record found */ if(presentity->expires == 0) { /* delete from hash table */ if(p && delete_phtable(p, hash_code)< 0) { LM_ERR("deleting record from hash table failed\n"); } /* presentity removed, pointer no longer valid */ p = NULL; lock_release(&pres_htable[hash_code].lock); if(msg && publ_send200ok(msg,presentity->expires,presentity->etag)<0) { LM_ERR("sending 200OK reply\n"); goto error; } *sent_reply= 1; if(publ_notify(presentity, pres_uri, body.s ? &body : 0, &presentity->etag, rules_doc, NULL, 1) < 0) { LM_ERR("while sending notify\n"); goto error; } if (pa_dbf.use_table(pa_db, &presentity_table) < 0) { LM_ERR("unsuccessful sql use table\n"); goto error; } //CON_PS_REFERENCE(pa_db) = &my_ps_delete; if(pa_dbf.delete(pa_db,query_cols,0,query_vals,n_query_cols)<0) { LM_ERR("unsuccessful sql delete operation"); goto error; } LM_DBG("Expires=0, deleted from db %.*s\n", presentity->user.len,presentity->user.s); /* Send another NOTIFY, this time rely on whatever is on the DB, * so in case there are no documents an empty * NOTIFY will be sent to the watchers */ if(publ_notify(presentity, pres_uri, NULL, NULL, rules_doc, NULL, 1) < 0) { LM_ERR("while sending notify\n"); goto error; } goto send_mxd_notify; } if(presentity->event->etag_not_new== 0) { if(generate_ETag(p?p->etag_count:0, &etag) < 0) { LM_ERR("while generating etag\n"); lock_release(&pres_htable[hash_code].lock); goto error; } cur_etag= etag; if(p) { update_pres_etag(p, &etag); lock_release(&pres_htable[hash_code].lock); } else { lock_release(&pres_htable[hash_code].lock); p = insert_phtable(&pres_uri, presentity->event->evp->parsed, &presentity->etag, presentity->sphere, 1); if ( p==NULL ) { LM_ERR("inserting record in hash table\n"); goto error; } } } else { lock_release(&pres_htable[hash_code].lock); cur_etag= presentity->etag; } n_update_cols= 0; update_keys[n_update_cols] = &str_etag_col; update_vals[n_update_cols].type = DB_STR; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.str_val = cur_etag; n_update_cols++; update_keys[n_update_cols] = &str_expires_col; update_vals[n_update_cols].type = DB_INT; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.int_val= presentity->expires + (int)time(NULL); n_update_cols++; update_keys[n_update_cols] = &str_received_time_col; update_vals[n_update_cols].type = DB_INT; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.int_val= presentity->received_time; n_update_cols++; update_keys[n_update_cols] = &str_sender_col; update_vals[n_update_cols].type = DB_STR; update_vals[n_update_cols].nul = 0; if( presentity->sender) { update_vals[n_update_cols].val.str_val = *presentity->sender; } else { update_vals[n_update_cols].val.str_val.s = ""; update_vals[n_update_cols].val.str_val.len = 0; } n_update_cols++; if(extra_hdrs) { update_keys[n_update_cols] = &str_extra_hdrs_col; update_vals[n_update_cols].type = DB_BLOB; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.str_val = *extra_hdrs; n_update_cols++; } if(body.s) { if (fix_remote_target) { if (dialog_fix_remote_target(&body, ¬ify_body)== 0) { body.s = notify_body.s; body.len = notify_body.len; } else { LM_ERR("Failed to fix remote target\n"); } } update_keys[n_update_cols] = &str_body_col; update_vals[n_update_cols].type = DB_BLOB; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.str_val = body; n_update_cols++; /* updated stored sphere */ if(sphere_enable && presentity->event->evp->parsed== EVENT_PRESENCE) { if(update_phtable(presentity, pres_uri, body)< 0) { LM_ERR("failed to update sphere for presentity\n"); goto error; } } //CON_PS_REFERENCE(pa_db) = &my_ps_update_body; } else { //CON_PS_REFERENCE(pa_db) = &my_ps_update_no_body; } if (pa_dbf.use_table(pa_db, &presentity_table) < 0) { LM_ERR("unsuccessful sql use table\n"); goto error; } if( pa_dbf.update( pa_db,query_cols, query_ops, query_vals, update_keys, update_vals, n_query_cols, n_update_cols )<0) { LM_ERR("updating published info in database\n"); goto error; } /* send 200OK */ if (msg && publ_send200ok(msg, presentity->expires, cur_etag)<0 ) { LM_ERR("sending 200OK reply\n"); goto error; } *sent_reply= 1; if(!body.s && !extra_hdrs) goto done; } send_notify: if (publ_notify(presentity, pres_uri, body.s?&body:0, NULL, rules_doc, NULL, 1)<0) { LM_ERR("while sending Notify requests to watchers\n"); goto error; } send_mxd_notify: /* if event dialog -> send Notify for presence also */ if(mix_dialog_presence && *pres_event_p && presentity->event->evp->parsed == EVENT_DIALOG) { str* dialog_body= NULL; LM_DBG("Publish for event dialog - try to send Notify for presence\n"); dialog_body = xml_dialog2presence(&pres_uri, &body); if(dialog_body) { /* send Notify for presence */ presentity->event = *pres_event_p; if (publ_notify(presentity, pres_uri, 0, NULL, 0, dialog_body, 1)<0) { LM_ERR("while sending Notify requests to watchers\n"); if(dialog_body && dialog_body!=FAKED_BODY) { xmlFree(dialog_body->s); pkg_free(dialog_body); } goto error; } if(dialog_body && dialog_body!=FAKED_BODY) { xmlFree(dialog_body->s); pkg_free(dialog_body); } } } done: /* allow next publish to be handled */ if (p) next_turn_phtable( p, hash_code); if (notify_body.s) xmlFree(notify_body.s); if(rules_doc) { if(rules_doc->s) pkg_free(rules_doc->s); pkg_free(rules_doc); } if(pres_uri.s) pkg_free(pres_uri.s); return 0; error: /* allow next publish to be handled */ if (p) next_turn_phtable( p, hash_code); if(result) pa_dbf.free_result(pa_db, result); if (notify_body.s) xmlFree(notify_body.s); if(rules_doc) { if(rules_doc->s) pkg_free(rules_doc->s); pkg_free(rules_doc); } if(pres_uri.s) pkg_free(pres_uri.s); return -1; } /* This is just a wrappter over "update_presentity" to be able to export the * function via the internal API - basically provides the "publishing" support * without actually having the PUBLISH SIP request, but directly a presentity */ int internal_update_presentity(presentity_t* presentity) { int dummy; return update_presentity( NULL, presentity, &dummy); } int pres_htable_restore(void) { /* query all records from presentity table and insert records * in presentity table */ db_key_t result_cols[6]; db_res_t *result= NULL; db_row_t *rows= NULL ; db_val_t *row_vals; int i; str user, domain, ev_str, uri, body; int n_result_cols= 0; int user_col, domain_col, event_col, expires_col, body_col = 0, etag_col; int event; event_t ev; char* sphere= NULL; int nr_rows; str etag; int no_rows = 10; result_cols[user_col= n_result_cols++]= &str_username_col; result_cols[domain_col= n_result_cols++]= &str_domain_col; result_cols[event_col= n_result_cols++]= &str_event_col; result_cols[expires_col= n_result_cols++]= &str_expires_col; result_cols[etag_col= n_result_cols++]= &str_etag_col; if(sphere_enable) result_cols[body_col= n_result_cols++]= &str_body_col; if (pa_dbf.use_table(pa_db, &presentity_table) < 0) { LM_ERR("unsuccessful use table sql operation\n"); goto error; } /* select the whole tabel and all the columns */ if (DB_CAPABILITY(pa_dbf, DB_CAP_FETCH)) { if(pa_dbf.query(pa_db,0,0,0,result_cols, 0, n_result_cols, result_cols[user_col], 0) < 0) { LM_ERR("Error while querying (fetch) database\n"); return -1; } no_rows = estimate_available_rows( 32+32+32+4+64, n_result_cols); if (no_rows==0) no_rows = 10; if(pa_dbf.fetch_result(pa_db,&result, no_rows)<0) { LM_ERR("fetching rows failed\n"); return -1; } } else { if (pa_dbf.query (pa_db, 0, 0, 0,result_cols,0, n_result_cols, result_cols[user_col], &result) < 0) { LM_ERR("querying presentity\n"); goto error; } } nr_rows = RES_ROW_N(result); do { LM_DBG("loading information from database for %i records\n", nr_rows); rows = RES_ROWS(result); /* for every row */ for(i=0; i skipping\n", str_username_col.len, str_username_col.s, str_domain_col.len, str_domain_col.s); continue; } if (VAL_NULL(row_vals+2) || VAL_NULL(row_vals+3)) { LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n", str_event_col.len, str_event_col.s, str_domain_col.len, str_domain_col.s); continue; } if(row_vals[expires_col].val.int_val< (int)time(NULL)) continue; sphere= NULL; user.s= (char*)row_vals[user_col].val.string_val; user.len= strlen(user.s); domain.s= (char*)row_vals[domain_col].val.string_val; domain.len= strlen(domain.s); ev_str.s= (char*)row_vals[event_col].val.string_val; ev_str.len= strlen(ev_str.s); etag.s= (char*)row_vals[etag_col].val.string_val; etag.len= strlen(etag.s); if(event_parser(ev_str.s, ev_str.len, &ev)< 0) { LM_ERR("parsing event\n"); free_event_params(ev.params, PKG_MEM_TYPE); goto error; } event= ev.parsed; free_event_params(ev.params, PKG_MEM_TYPE); if(uandd_to_uri(user, domain, &uri)< 0) { LM_ERR("constructing uri\n"); goto error; } /* insert in hash_table*/ if(sphere_enable && event== EVENT_PRESENCE ) { body.s= (char*)row_vals[body_col].val.string_val; body.len= strlen(body.s); sphere= extract_sphere(body); } if(insert_phtable(&uri, event, &etag, sphere, 0)== NULL) { LM_ERR("inserting record in presentity hash table"); pkg_free(uri.s); if(sphere) pkg_free(sphere); goto error; } if(sphere) pkg_free(sphere); pkg_free(uri.s); } /* any more data to be fetched ?*/ if (DB_CAPABILITY(pa_dbf, DB_CAP_FETCH)) { if (pa_dbf.fetch_result( pa_db, &result, no_rows) < 0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(result); } else nr_rows = 0; }while (nr_rows>0); pa_dbf.free_result(pa_db, result); return 0; error: if(result) pa_dbf.free_result(pa_db, result); return -1; } char* extract_sphere(str body) { /* check for a rpid sphere element */ xmlDocPtr doc= NULL; xmlNodePtr node; char* cont, *sphere= NULL; doc= xmlParseMemory(body.s, body.len); if(doc== NULL) { LM_ERR("failed to parse xml body\n"); return NULL; } node= xmlNodeGetNodeByName(doc->children, "sphere", "rpid"); if(node== NULL) node= xmlNodeGetNodeByName(doc->children, "sphere", "r"); if(node) { LM_DBG("found sphere definition\n"); cont= (char*)xmlNodeGetContent(node); if(cont== NULL) { LM_ERR("failed to extract sphere node content\n"); goto error; } sphere= (char*)pkg_malloc(strlen(cont)+ 1); if(sphere== NULL) { xmlFree(cont); ERR_MEM(PKG_MEM_STR); } strcpy(sphere, cont); xmlFree(cont); } else LM_DBG("didn't find sphere definition\n"); error: xmlFreeDoc(doc); return sphere; } xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns) { xmlNodePtr cur = node; while (cur) { xmlNodePtr match = NULL; if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) { if (!ns || (cur->ns && xmlStrcasecmp(cur->ns->prefix, (unsigned char*)ns) == 0)) return cur; } match = xmlNodeGetNodeByName(cur->children, name, ns); if (match) return match; cur = cur->next; } return NULL; } char* get_sphere(str* pres_uri) { // static db_ps_t my_ps = NULL; unsigned int hash_code; char* sphere= NULL; pres_entry_t* p; db_key_t query_cols[6]; db_val_t query_vals[6]; db_key_t result_cols[6]; db_res_t *result = NULL; db_row_t *row= NULL ; db_val_t *row_vals; int n_result_cols = 0; int n_query_cols = 0; struct sip_uri uri; str body; static str query_str = str_init("received_time"); if(!sphere_enable) return NULL; /* search in hash table*/ hash_code= core_hash(pres_uri, NULL, phtable_size); lock_get(&pres_htable[hash_code].lock); p= search_phtable(pres_uri, EVENT_PRESENCE, hash_code); if(p) { if(p->sphere) { sphere= (char*)pkg_malloc(strlen(p->sphere)); if(sphere== NULL) { lock_release(&pres_htable[hash_code].lock); ERR_MEM(PKG_MEM_STR); } strcpy(sphere, p->sphere); } lock_release(&pres_htable[hash_code].lock); return sphere; } lock_release(&pres_htable[hash_code].lock); /* if record not found and fallback2db query database*/ if(!fallback2db) { return NULL; } if(parse_uri(pres_uri->s, pres_uri->len, &uri)< 0) { LM_ERR("failed to parse presentity uri\n"); goto error; } query_cols[n_query_cols] = &str_domain_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = uri.host; n_query_cols++; query_cols[n_query_cols] = &str_username_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = uri.user; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val.s= "presence"; query_vals[n_query_cols].val.str_val.len= 8; n_query_cols++; result_cols[n_result_cols++] = &str_body_col; result_cols[n_result_cols++] = &str_extra_hdrs_col; if (pa_dbf.use_table(pa_db, &presentity_table) < 0) { LM_ERR("in use_table\n"); return NULL; } // CON_PS_REFERENCE(pa_db) = &my_ps; if (pa_dbf.query (pa_db, query_cols, 0, query_vals, result_cols, n_query_cols, n_result_cols, &query_str , &result) < 0) { LM_ERR("failed to query %.*s table\n", presentity_table.len, presentity_table.s); if(result) pa_dbf.free_result(pa_db, result); return NULL; } if(result== NULL) return NULL; if (result->n<=0 ) { LM_DBG("no published record found in database\n"); pa_dbf.free_result(pa_db, result); return NULL; } row = &result->rows[result->n-1]; row_vals = ROW_VALUES(row); if(row_vals[0].val.string_val== NULL) { LM_ERR("NULL notify body record\n"); goto error; } body.s= (char*)row_vals[0].val.string_val; body.len= strlen(body.s); if(body.len== 0) { LM_ERR("Empty notify body record\n"); goto error; } sphere= extract_sphere(body); pa_dbf.free_result(pa_db, result); return sphere; error: if(result) pa_dbf.free_result(pa_db, result); return NULL; } int contains_presence(str* pres_uri) { unsigned int hash_code; db_key_t query_cols[6]; db_val_t query_vals[6]; db_key_t result_cols[6]; db_res_t *result = NULL; int n_result_cols = 0; int n_query_cols = 0; struct sip_uri uri; static str query_str = str_init("received_time"); int ret = -1; hash_code= core_hash(pres_uri, NULL, phtable_size); lock_get(&pres_htable[hash_code].lock); if ( search_phtable(pres_uri, EVENT_PRESENCE, hash_code)!=NULL ) { ret = 1; } lock_release(&pres_htable[hash_code].lock); if ( ret== -1 && fallback2db ) { if(parse_uri(pres_uri->s, pres_uri->len, &uri)< 0) { LM_ERR("failed to parse presentity uri\n"); goto done; } query_cols[n_query_cols] = &str_domain_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = uri.host; n_query_cols++; query_cols[n_query_cols] = &str_username_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = uri.user; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val.s= "presence"; query_vals[n_query_cols].val.str_val.len= 8; n_query_cols++; result_cols[n_result_cols++] = &str_body_col; result_cols[n_result_cols++] = &str_extra_hdrs_col; pa_dbf.use_table(pa_db, &presentity_table); if (pa_dbf.query (pa_db, query_cols, 0, query_vals, result_cols, n_query_cols, n_result_cols, &query_str, &result)<0) { LM_ERR("failed to query %.*s table\n", presentity_table.len, presentity_table.s); goto done; } if(result== NULL) goto done; if (result->n<=0 ) { LM_DBG("no published record found in database\n"); goto done; } ret = 1; } done: if(result) pa_dbf.free_result(pa_db, result); return ret; } str* xml_dialog_gen_presence(str* pres_uri, int dlg_state) { char* pres_note; xmlDocPtr pres_doc; xmlNodePtr node, root_node; xmlNodePtr tuple_node, person_node; str* dialog_body = NULL; char* entity; LM_DBG("dlg_state = %d\n", dlg_state); pres_note = presence_notes[dlg_state]; /* if state is terminated, do not add anything */ if(pres_note && strlen(pres_note) == 0) { LM_DBG("NULL pres note\n"); return FAKED_BODY; } pres_doc= xmlNewDoc(BAD_CAST "1.0"); if(pres_doc== NULL) { LM_ERR("allocating new xml doc\n"); goto error; } root_node = xmlNewNode(NULL, BAD_CAST "presence"); if(root_node== NULL) { LM_ERR("Failed to create xml node\n"); goto error; } xmlDocSetRootElement(pres_doc, root_node); xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:pidf"); xmlNewProp(root_node, BAD_CAST "xmlns:dm", BAD_CAST "urn:ietf:params:xml:ns:pidf:data-model"); xmlNewProp(root_node, BAD_CAST "xmlns:rpid", BAD_CAST "urn:ietf:params:xml:ns:pidf:rpid" ); xmlNewProp(root_node, BAD_CAST "xmlns:c", BAD_CAST "urn:ietf:params:xml:ns:pidf:cipid"); entity= (char*)pkg_malloc(pres_uri->len + 1); if(entity == NULL) { LM_ERR("No more memory\n"); goto error; } memcpy(entity, pres_uri->s, pres_uri->len); entity[pres_uri->len] = '\0'; xmlNewProp(root_node, BAD_CAST "entity", BAD_CAST entity); pkg_free(entity); tuple_node =xmlNewChild(root_node, NULL, BAD_CAST "tuple", NULL) ; if(tuple_node == NULL) { LM_ERR("while adding child\n"); goto error; } xmlNewProp(tuple_node, BAD_CAST "id", BAD_CAST "tuple_mixingid"); node = xmlNewChild(tuple_node, NULL, BAD_CAST "status", NULL) ; if(node == NULL) { LM_ERR("while adding child\n"); goto error; } node = xmlNewChild(node, NULL, BAD_CAST "basic", BAD_CAST "open") ; if(node ==NULL) { LM_ERR("while adding child\n"); goto error; } if(pres_note && strlen(pres_note)) { node = xmlNewChild(root_node, NULL, BAD_CAST "note", BAD_CAST pres_note) ; if(node ==NULL) { LM_ERR("while adding child\n"); goto error; } /* put also the person node - to get status indication */ person_node = xmlNewChild(root_node, 0, BAD_CAST "dm:person", NULL) ; if(person_node == NULL) { LM_ERR("while adding child\n"); goto error; } /* now put the id for tuple and person */ xmlNewProp(person_node, BAD_CAST "id", BAD_CAST "pers_mixingid"); node = xmlNewChild(person_node, 0, BAD_CAST "rpid:activities", NULL) ; if(node == NULL) { LM_ERR("Failed to add person activities node\n"); goto error; } if(xmlNewChild(node, 0, BAD_CAST "rpid:on-the-phone", NULL) == NULL) { LM_ERR("Failed to add activities child\n"); goto error; } if(xmlNewChild(person_node, 0, BAD_CAST "dm:note", BAD_CAST pres_note) == NULL) { LM_ERR("Failed to add activities child\n"); goto error; } } dialog_body = (str*)pkg_malloc(sizeof(str)); if(dialog_body == NULL) { LM_ERR("No more memory\n"); goto error; } xmlDocDumpMemory(pres_doc,(xmlChar**)(void*)&dialog_body->s, &dialog_body->len); LM_DBG("Generated dialog body: %.*s\n", dialog_body->len, dialog_body->s); error: if(pres_doc) xmlFreeDoc(pres_doc); xmlCleanupParser(); xmlMemoryDump(); return dialog_body; } str* xml_dialog2presence(str* pres_uri, str* body) { xmlDocPtr dlg_doc = NULL; xmlNodePtr node, dialog_node; unsigned char* state; int i; if(body->len == 0) return NULL; dlg_doc = xmlParseMemory(body->s, body->len); if(dlg_doc == NULL) { LM_ERR("Wrong formatted xml document\n"); return NULL; } dialog_node = xmlNodeGetNodeByName(dlg_doc->children, "dialog", 0); if(!dialog_node) { goto done; } node = xmlNodeGetNodeByName(dialog_node, "state", 0); if(!node) goto done; state = xmlNodeGetContent(node); if(!state) goto done; for(i = 0; i< DLG_STATES_NO; i++) { if(xmlStrcasecmp(state, BAD_CAST dialog_states[i])==0) { break; } } xmlFree(state); xmlFreeDoc(dlg_doc); xmlCleanupParser(); xmlMemoryDump(); if(i == DLG_STATES_NO) { LM_ERR("Unknown dialog state\n"); return 0; } return xml_dialog_gen_presence(pres_uri, i); done: xmlFreeDoc(dlg_doc); return 0; } str* build_offline_presence(str* pres_uri) { xmlDocPtr pres_doc = NULL; xmlNodePtr root_node, tuple_node, node; char* entity; str* body = NULL; pres_doc= xmlNewDoc(BAD_CAST "1.0"); if(pres_doc== NULL) { LM_ERR("allocating new xml doc\n"); goto error; } root_node = xmlNewNode(NULL, BAD_CAST "presence"); if(root_node== NULL) { LM_ERR("Failed to create xml node\n"); goto error; } xmlDocSetRootElement(pres_doc, root_node); xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:pidf"); xmlNewProp(root_node, BAD_CAST "xmlns:dm", BAD_CAST "urn:ietf:params:xml:ns:pidf:data-model"); xmlNewProp(root_node, BAD_CAST "xmlns:rpid", BAD_CAST "urn:ietf:params:xml:ns:pidf:rpid" ); xmlNewProp(root_node, BAD_CAST "xmlns:c", BAD_CAST "urn:ietf:params:xml:ns:pidf:cipid"); entity= (char*)pkg_malloc(pres_uri->len + 1); if(entity == NULL) { LM_ERR("No more memory\n"); goto error; } memcpy(entity, pres_uri->s, pres_uri->len); entity[pres_uri->len] = '\0'; xmlNewProp(root_node, BAD_CAST "entity", BAD_CAST entity); pkg_free(entity); tuple_node =xmlNewChild(root_node, NULL, BAD_CAST "tuple", NULL) ; if(tuple_node == NULL) { LM_ERR("while adding child\n"); goto error; } xmlNewProp(tuple_node, BAD_CAST "id", BAD_CAST "tuple_mixingid"); node = xmlNewChild(tuple_node, NULL, BAD_CAST "status", NULL) ; if(node == NULL) { LM_ERR("while adding child\n"); goto error; } node = xmlNewChild(node, NULL, BAD_CAST "basic", BAD_CAST "closed") ; if(node ==NULL) { LM_ERR("while adding child\n"); goto error; } body = (str*)pkg_malloc(sizeof(str)); if(body == NULL) { LM_ERR("No more memory\n"); goto error; } xmlDocDumpMemory(pres_doc,(xmlChar**)(void*)&body->s, &body->len); LM_DBG("Generated dialog body: %.*s\n", body->len, body->s); error: if(pres_doc) xmlFreeDoc(pres_doc); xmlCleanupParser(); xmlMemoryDump(); return body; } opensips-2.2.2/modules/presence/presentity.h000066400000000000000000000040201300170765700212120ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) * 2010-10-19 support for extra headers (osas) */ #ifndef PRESENTITY_H #define PRESENTITY_H #include "../../str.h" #include "../../parser/msg_parser.h" #include "event_list.h" //#include "presence.h" extern char prefix; typedef struct presentity { int presid; str user; str domain; pres_ev_t* event; int etag_count; str etag; str* sender; time_t expires; time_t received_time; str* extra_hdrs; short etag_new; char* sphere; str body; } presentity_t; int internal_update_presentity(presentity_t* presentity); /* update presentity in database */ int update_presentity(struct sip_msg* msg, presentity_t* presentity, int* sent_reply); /* free memory */ void free_presentity(presentity_t* p); int generate_ETag(int etag_count, str* etag); int pres_htable_restore(void); char* extract_sphere(str body); char* get_sphere(str* pres_uri); typedef char* (*pres_get_sphere_t)(str* pres_uri); int contains_presence(str* pres_uri); typedef int (*pres_contains_presence_t)(str* pres_uri); int get_dialog_state(str body, int *dialog_state); str* xml_dialog_gen_presence(str* pres_uri, int dlg_state); #endif opensips-2.2.2/modules/presence/publish.c000066400000000000000000000367501300170765700204640ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) * 2010-10-19 support for extra headers (osas) */ #include #include "../../ut.h" #include "../../str.h" #include "../../parser/parse_to.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_event.h" #include "../../parser/parse_content.h" #include "../../lock_ops.h" #include "../../hash_func.h" #include "../../db/db.h" #include "../../evi/evi_modules.h" #include "presence.h" #include "notify.h" #include "utils_func.h" #include "publish.h" #include "presentity.h" static str pu_400_rpl = str_init("Bad request"); static str pu_500_rpl = str_init("Server Internal Error"); static str pu_489_rpl = str_init("Bad Event"); struct p_modif { presentity_t* p; str uri; }; #define MAX_NO_OF_EXTRA_HDRS 4 /* event declaration */ extern event_id_t presence_event_id; static inline void presence_raise_event(presentity_t* presentity) { evi_params_p list; static str parameter_user_str = { "user", 4 }; static str parameter_domain_str = { "domain", 6 }; static str parameter_eventname_str = { "event", 5 }; static str parameter_expires_str = { "expires", 7 }; static str parameter_etag_str = { "etag", 4 }; static str parameter_body_str = { "body", 4 }; if (presence_event_id == EVI_ERROR) { LM_ERR("event not registered %d\n", presence_event_id); return; } if (evi_probe_event(presence_event_id)) { if (!(list = evi_get_params())) return; //if (evi_param_add_str(list, ¶meter_user_str, &user)) { if (evi_param_add_str(list, ¶meter_user_str, &presentity->user)) { LM_ERR("unable to add user parameter\n"); evi_free_params(list); return; } if (evi_param_add_str(list, ¶meter_domain_str, &presentity->domain)) { LM_ERR("unable to add domain parameter\n"); evi_free_params(list); return; } if (evi_param_add_str(list, ¶meter_eventname_str, &presentity->event->name)) { LM_ERR("unable to add event parameter\n"); evi_free_params(list); return; } if (evi_param_add_int(list, ¶meter_expires_str, &presentity->expires)) { LM_ERR("unable to add expires parameter\n"); evi_free_params(list); return; } if (evi_param_add_str(list, ¶meter_etag_str, &presentity->etag)) { LM_ERR("unable to add etag parameter\n"); evi_free_params(list); return; } if (evi_param_add_str(list, ¶meter_body_str, &presentity->body)) { LM_ERR("unable to add body parameter\n"); evi_free_params(list); return; } if (evi_raise_event(presence_event_id, list)) { LM_ERR("unable to send event %d\n", presence_event_id); } } else { LM_DBG("no event sent\n"); } } static inline void build_extra_hdrs(struct sip_msg* msg, const str* map, str* extra_hdrs) { struct hdr_field *hf; str xtra_hdr_list[MAX_NO_OF_EXTRA_HDRS]; char *p; int is_present, len=0, i=0; memset(xtra_hdr_list, 0, MAX_NO_OF_EXTRA_HDRS* sizeof(str)); for (; map->s; map++) { if (i>=MAX_NO_OF_EXTRA_HDRS) { LM_WARN("maximum no of extra headers reached: " "increase MAX_NO_OF_EXTRA_HDRS and recompile\n"); break; } is_present = 0; for (hf=msg->headers; hf; hf=hf->next) { if (hf->name.len!=map->len) continue; if (strncasecmp(hf->name.s,map->s,map->len)!=0) continue; is_present = 1; break; } if (is_present) { /* insert the header into the list */ LM_DBG("found '%.*s'\n", hf->len, hf->name.s); xtra_hdr_list[i].s = hf->name.s; xtra_hdr_list[i].len = hf->len; len+= hf->len; i++; } } /* Concatenate found feaders */ if (len) { p= (char*)pkg_malloc(len); if (p==NULL) { LM_ERR("oom: dropping extra hdrs\n"); return; } extra_hdrs->s = p; extra_hdrs->len = len; for (i=0;in<= 0) { pa_dbf.free_result(pa_db, result); return; } LM_DBG("found n= %d expires messages\n",result->n); n= result->n; p= (struct p_modif*)pkg_malloc(n* sizeof(struct p_modif)); if(p== NULL) { ERR_MEM(PKG_MEM_STR); } memset(p, 0, n* sizeof(struct p_modif)); for(i = 0; i< n; i++) { row = &result->rows[i]; row_vals = ROW_VALUES(row); user.s= (char*)row_vals[user_col].val.string_val; user.len= strlen(user.s); domain.s= (char*)row_vals[domain_col].val.string_val; domain.len= strlen(domain.s); etag.s= (char*)row_vals[etag_col].val.string_val; etag.len= strlen(etag.s); event.s= (char*)row_vals[event_col].val.string_val; event.len= strlen(event.s); size= sizeof(presentity_t) + user.len+ domain.len+ etag.len; pres= (presentity_t*)pkg_malloc(size); if(pres== NULL) { ERR_MEM(PKG_MEM_STR); } memset(pres, 0, size); size= sizeof(presentity_t); pres->user.s= (char*)pres+ size; memcpy(pres->user.s, user.s, user.len); pres->user.len= user.len; size+= user.len; pres->domain.s= (char*)pres+ size; memcpy(pres->domain.s, domain.s, domain.len); pres->domain.len= domain.len; size+= domain.len; pres->etag.s= (char*)pres+ size; memcpy(pres->etag.s, etag.s, etag.len); pres->etag.len= etag.len; size+= etag.len; pres->event= contains_event(&event, &ev); if(pres->event== NULL) { LM_DBG("event not found\n"); p[i].p = 0; goto no_notify; } p[i].p= pres; no_notify: if(uandd_to_uri(user, domain, &p[i].uri)< 0) { LM_ERR("constructing uri\n"); free_event_params(ev.params, PKG_MEM_TYPE); goto error; } free_event_params(ev.params, PKG_MEM_TYPE); } pa_dbf.free_result(pa_db, result); result= NULL; for(i= 0; iuser.len,p[i].p->user.s, p[i].p->domain.len, p[i].p->domain.s); rules_doc= NULL; if(p[i].p->event->get_rules_doc && p[i].p->event->get_rules_doc(&p[i].p->user, &p[i].p->domain, &rules_doc)< 0) { LM_ERR("getting rules doc\n"); goto error; } if(publ_notify( p[i].p, p[i].uri, NULL, &p[i].p->etag, rules_doc, NULL, 0)< 0) { LM_ERR("sending Notify request\n"); goto error; } if(rules_doc) { if(rules_doc->s) pkg_free(rules_doc->s); pkg_free(rules_doc); } rules_doc= NULL; /* delete from hash table */ if(delete_phtable_query(&p[i].uri, ev.parsed, &p[i].p->etag)< 0) { LM_ERR("deleting from pres hash table\n"); } } error: if(result) pa_dbf.free_result(pa_db, result); if (pa_dbf.use_table(pa_db, &presentity_table) < 0) { LM_ERR("in use_table\n"); goto clean; } CON_PS_REFERENCE(pa_db) = &my_ps_delete; if (pa_dbf.delete(pa_db, db_keys, db_ops, db_vals, 1) < 0) LM_ERR("cleaning expired messages\n"); clean: if(p) { for(i= 0; i< n; i++) { if(p[i].p) pkg_free(p[i].p); if(p[i].uri.s) pkg_free(p[i].uri.s); else break; } pkg_free(p); } if(rules_doc) { if(rules_doc->s) pkg_free(rules_doc->s); pkg_free(rules_doc); } } /** * PUBLISH request handling * returns: * 0: success * -1: error * - sends a reply in all cases (success or error). * TODO replace -1 return code in error case with 0 ( exit from the script) **/ int handle_publish(struct sip_msg* msg, char* sender_uri, char* str2) { struct sip_uri puri; str body; int lexpire; presentity_t presentity; struct hdr_field* hdr; int etag_new = 0; str etag={NULL, 0}; str extra_hdrs={NULL, 0}; str* sender= NULL; static char buf[256]; int buf_len= 255; pres_ev_t* event= NULL; str pres_user; str pres_domain; int reply_code; str reply_str; int sent_reply= 0; char* sphere= NULL; reply_code= 400; reply_str= pu_400_rpl; counter++; if ( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("parsing headers\n"); goto error; } memset(&body, 0, sizeof(str)); /* inspecting the Event header field */ if(msg->event && msg->event->body.len > 0) { if (!msg->event->parsed && (parse_event(msg->event) < 0)) { LM_ERR("cannot parse Event header\n"); goto error; } if(((event_t*)msg->event->parsed)->parsed == EVENT_OTHER) { LM_ERR("unrecognized value [%.*s] in Event header\n", msg->event->body.len, msg->event->body.s); goto unsupported_event; } } else { LM_ERR("Missing Event header\n"); goto unsupported_event; } /* search event in the list */ event= search_event((event_t*)msg->event->parsed); if(event== NULL) { LM_ERR("un-registered support for known event [%.*s]\n", msg->event->body.len, msg->event->body.s); goto unsupported_event; } /* examine the expire header field */ if(msg->expires && msg->expires->body.len > 0) { if (!msg->expires->parsed && (parse_expires(msg->expires) < 0)) { LM_ERR("cannot parse Expires header\n"); goto error; } lexpire = ((exp_body_t*)msg->expires->parsed)->val; LM_DBG("Expires header found, value= %d\n", lexpire); } else { LM_DBG("'expires' not found; default=%d\n", event->default_expires); lexpire = event->default_expires; } if(lexpire > max_expires_publish) lexpire = max_expires_publish; /* get pres_uri from Request-URI*/ if( parse_sip_msg_uri(msg)< 0) { LM_ERR("parsing Request URI\n"); goto error; } pres_user= msg->parsed_uri.user; pres_domain= msg->parsed_uri.host; /* examine the SIP-If-Match header field */ hdr = get_header_by_static_name( msg, "SIP-If-Match"); if( hdr==NULL ) { LM_DBG("SIP-If-Match header not found\n"); if(generate_ETag(0, &etag) < 0) { LM_ERR("when generating etag\n"); reply_code = 500; reply_str= pu_500_rpl; goto error; } etag_new=1; LM_DBG("new etag= %.*s\n", etag.len, etag.s); } else { LM_DBG("SIP-If-Match header found\n"); etag = hdr->body; LM_DBG("existing etag= %.*s\n", etag.len, etag.s); } if (!msg->content_length) { LM_ERR("no Content-Length header found!\n"); goto error; } /* process the body */ if ( get_content_length(msg) == 0 ) { body.s = NULL; if (etag_new) { if (event->mandatory_body) { LM_ERR("No E-Tag and no body found\n"); goto error; } } } else { if ( event->content_type.len ) { if (get_body(msg,&body)!=0 || body.len==0) { LM_ERR("cannot extract body\n"); goto error; } if(sphere_enable && event->evp->parsed == EVENT_PRESENCE && get_content_type(msg)== SUBTYPE_PIDFXML) { sphere= extract_sphere(body); } } } memset(&puri, 0, sizeof(struct sip_uri)); if(sender_uri) { sender=(str*)pkg_malloc(sizeof(str)); if(sender== NULL) { ERR_MEM(PKG_MEM_STR); } if(pv_printf(msg, (pv_elem_t*)sender_uri, buf, &buf_len)<0) { LM_ERR("cannot print the format\n"); reply_code= 500; reply_str= pu_500_rpl; goto error; } if(parse_uri(buf, buf_len, &puri)!=0) { LM_ERR("bad sender SIP address!\n"); goto error; } else { LM_DBG("using user id [%.*s]\n",buf_len,buf); } sender->s= buf; sender->len= buf_len; } /* call event specific handling function*/ if(event->evs_publ_handl) { if(event->evs_publ_handl(msg, &sent_reply)< 0) { LM_ERR("in event specific publish handling\n"); reply_code = 500; reply_str = pu_500_rpl; goto error; } } /* build extra headers list */ if (event->extra_hdrs) build_extra_hdrs(msg, event->extra_hdrs, &extra_hdrs); /* now we have all the necessary values */ /* fill in the filds of the structure */ memset(&presentity, 0, sizeof(presentity_t)); presentity.domain = pres_domain; presentity.user = pres_user; presentity.etag = etag; if(sender) presentity.sender= sender; presentity.event= event; presentity.expires = lexpire; presentity.received_time= (int)time(NULL); if(extra_hdrs.s) presentity.extra_hdrs = &extra_hdrs; presentity.etag_new = etag_new; presentity.sphere = sphere; presentity.body = body; /* send event E_PRESENCE_PUBLISH*/ presence_raise_event(&presentity); /* querry the database and update or insert */ if(update_presentity(msg, &presentity, &sent_reply) <0) { LM_ERR("when updating presentity\n"); reply_code = 500; reply_str = pu_500_rpl; goto error; } if(sender) pkg_free(sender); if(sphere) pkg_free(sphere); if(extra_hdrs.s) pkg_free(extra_hdrs.s); return 1; unsupported_event: LM_ERR("Missing or unsupported event header field value\n"); if(msg->event && msg->event->body.s && msg->event->body.len>0) LM_ERR("\tevent=[%.*s]\n", msg->event->body.len, msg->event->body.s); reply_code= BAD_EVENT_CODE; reply_str= pu_489_rpl; error: if(sent_reply== 0) { if(send_error_reply(msg, reply_code, reply_str)< 0) { LM_ERR("failed to send error reply\n"); } } if(sender) pkg_free(sender); if(sphere) pkg_free(sphere); if(extra_hdrs.s) pkg_free(extra_hdrs.s); return -1; } opensips-2.2.2/modules/presence/publish.h000066400000000000000000000025641300170765700204650ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) */ #ifndef PUBLISH_H #define PUBLISH_H #include #include #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../parser/msg_parser.h" #include "presentity.h" #include "../alias_db/alias_db.h" void msg_presentity_clean(unsigned int ticks,void *param); int handle_publish(struct sip_msg* msg, char* str1 ,char* str2); #endif opensips-2.2.2/modules/presence/subscribe.c000066400000000000000000001622101300170765700207660ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP serves. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) * 2010-10-19 support for extra headers (osas) */ #include "../../ut.h" #include "../../usr_avp.h" #include "../../data_lump_rpl.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_event.h" #include "../../parser/contact/parse_contact.h" #include "presence.h" #include "subscribe.h" #include "utils_func.h" #include "notify.h" #include "../pua/hash.h" int get_stored_info(struct sip_msg* msg, subs_t* subs, int* error_ret, str* reply_str); int get_database_info(struct sip_msg* msg, subs_t* subs, int* error_ret, str* reply_str); int get_db_subs_auth(subs_t* subs, int* found); int insert_db_subs_auth(subs_t* subs); int insert_subs_db(subs_t* s); static str su_200_rpl = str_init("OK"); static str pu_481_rpl = str_init("Subscription does not exist"); static str pu_400_rpl = str_init("Bad request"); static str pu_500_rpl = str_init("Server Internal Error"); static str pu_489_rpl = str_init("Bad Event"); int send_2XX_reply(struct sip_msg * msg, int reply_code, int lexpire, str *rtag, str* local_contact) { char * hdr_append=NULL; int lexpire_len; char *lexpire_s; int len; char *p; if(lexpire < 0 ) lexpire = 0; lexpire_s = int2str((unsigned long)lexpire, &lexpire_len); len = 9 /*"Expires: "*/ + lexpire_len + CRLF_LEN + 10 /*"Contact: <"*/ + local_contact->len + 1 /*">"*/ + ((msg->rcv.proto!=PROTO_UDP)?15/*";transport=xxxx"*/:0) + CRLF_LEN; hdr_append = (char *)pkg_malloc( len ); if(hdr_append == NULL) { ERR_MEM(PKG_MEM_STR); } p = hdr_append; /* expires header */ memcpy(p, "Expires: ", 9); p += 9; memcpy(p,lexpire_s,lexpire_len); p += lexpire_len; memcpy(p,CRLF,CRLF_LEN); p += CRLF_LEN; /* contact header */ memcpy(p,"Contact: <", 10); p += 10; memcpy(p,local_contact->s,local_contact->len); p += local_contact->len; if (msg->rcv.proto!=PROTO_UDP) { memcpy(p,";transport=",11); p += 11; p = proto2str(msg->rcv.proto, p); if (p==NULL) { LM_ERR("invalid proto\n"); goto error; } } *(p++) = '>'; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; if (add_lump_rpl( msg, hdr_append, p-hdr_append, LUMP_RPL_HDR)==0 ) { LM_ERR("unable to add lump_rl\n"); goto error; } if( sigb.reply( msg, reply_code, &su_200_rpl, rtag)== -1) { LM_ERR("sending reply\n"); goto error; } pkg_free(hdr_append); return 0; error: if (hdr_append) pkg_free(hdr_append); return -1; } int delete_db_subs(str pres_uri, str ev_stored_name, str to_tag) { static db_ps_t my_ps = NULL; db_key_t query_cols[5]; db_val_t query_vals[5]; int n_query_cols= 0; query_cols[n_query_cols] = &str_presentity_uri_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = pres_uri; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = ev_stored_name; n_query_cols++; query_cols[n_query_cols] = &str_to_tag_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = to_tag; n_query_cols++; if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) { LM_ERR("in use table sql operation\n"); return -1; } CON_PS_REFERENCE(pa_db) = &my_ps; LM_DBG("delete subs \n"); if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols)< 0 ) { LM_ERR("sql delete failed\n"); return -1; } return 0; } int update_subs_db(subs_t* subs, int type) { static db_ps_t my_ps_remote = NULL, my_ps_local = NULL; db_key_t query_cols[22], update_keys[8]; db_val_t query_vals[22], update_vals[8]; int n_update_cols= 0; int n_query_cols = 0; if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) { LM_ERR("in use table sql operation\n"); return -1; } query_cols[n_query_cols] = &str_presentity_uri_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->pres_uri; n_query_cols++; query_cols[n_query_cols] = &str_watcher_username_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->from_user; n_query_cols++; query_cols[n_query_cols] = &str_watcher_domain_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->from_domain; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->event->name; n_query_cols++; query_cols[n_query_cols] = &str_event_id_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; if(subs->event_id.s) { query_vals[n_query_cols].val.str_val = subs->event_id; } else { query_vals[n_query_cols].val.str_val.s = ""; query_vals[n_query_cols].val.str_val.len = 0; } n_query_cols++; query_cols[n_query_cols] = &str_callid_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->callid; n_query_cols++; query_cols[n_query_cols] = &str_to_tag_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->to_tag; n_query_cols++; query_cols[n_query_cols] = &str_from_tag_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->from_tag; n_query_cols++; if(type & REMOTE_TYPE) { update_keys[n_update_cols] = &str_expires_col; update_vals[n_update_cols].type = DB_INT; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.int_val = subs->expires + (int)time(NULL); n_update_cols++; update_keys[n_update_cols] = &str_remote_cseq_col; update_vals[n_update_cols].type = DB_INT; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.int_val = subs->remote_cseq; n_update_cols++; update_keys[n_update_cols] = &str_contact_col; update_vals[n_update_cols].type = DB_STR; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.str_val = subs->contact; n_update_cols++; CON_PS_REFERENCE(pa_db) = &my_ps_remote; } else { update_keys[n_update_cols] = &str_local_cseq_col; update_vals[n_update_cols].type = DB_INT; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.int_val = subs->local_cseq+ 1; n_update_cols++; update_keys[n_update_cols] = &str_version_col; update_vals[n_update_cols].type = DB_INT; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.int_val = subs->version+ 1; n_update_cols++; CON_PS_REFERENCE(pa_db) = &my_ps_local; } update_keys[n_update_cols] = &str_status_col; update_vals[n_update_cols].type = DB_INT; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.int_val = subs->status; n_update_cols++; update_keys[n_update_cols] = &str_reason_col; update_vals[n_update_cols].type = DB_STR; update_vals[n_update_cols].nul = 0; update_vals[n_update_cols].val.str_val = subs->reason; n_update_cols++; if(pa_dbf.update( pa_db,query_cols, 0, query_vals, update_keys, update_vals, n_query_cols,n_update_cols)<0) { LM_ERR("updating presence information\n"); return -1; } return 0; } int subs_process_insert_status(subs_t* subs) { struct sip_uri uri; /*default 'pending' status */ subs->status= PENDING_STATUS; subs->reason.s= NULL; subs->reason.len= 0; if(parse_uri(subs->pres_uri.s, subs->pres_uri.len, &uri)< 0) { LM_ERR("parsing uri\n"); goto error; } if(subs->event->get_rules_doc(&uri.user, &uri.host, &subs->auth_rules_doc)< 0) { LM_ERR("getting rules doc\n"); goto error; } if(subs->event->get_auth_status(subs)< 0) { LM_ERR("in event specific function is_watcher_allowed\n"); goto error; } if(get_status_str(subs->status)== NULL) { LM_ERR("wrong status= %d\n", subs->status); goto error; } if(insert_db_subs_auth(subs)< 0) { LM_ERR("while inserting record in watchers table\n"); goto error; } return 0; error: return -1; } int update_subscription(struct sip_msg* msg, subs_t* subs, int init_req) { unsigned int hash_code; int reply_code= 200; if(subs->event->type & PUBL_TYPE) reply_code=(subs->status==PENDING_STATUS)?202:200; hash_code= core_hash(&subs->pres_uri, &subs->event->name, shtable_size); if(init_req ==0) /*if a SUBSCRIBE within a dialog */ { if(subs->expires == 0) { LM_DBG("expires=0, deleting subscription from [%.*s@%.*s] to [%.*s]\n", subs->from_user.len, subs->from_user.s, subs->from_domain.len, subs->from_domain.s, subs->pres_uri.len, subs->pres_uri.s); if(delete_db_subs(subs->pres_uri,subs->event->name,subs->to_tag)<0) { LM_ERR("deleting subscription record from database\n"); goto error; } /* delete record from hash table also */ subs->local_cseq= delete_shtable(subs_htable,hash_code, subs->to_tag); if( msg && send_2XX_reply(msg, reply_code, subs->expires, 0, &subs->local_contact) <0) { LM_ERR("sending %d OK\n", reply_code); goto error; } goto send_notify; } subs->expires+= expires_offset; if(update_shtable(subs_htable, hash_code, subs, REMOTE_TYPE)< 0) { LM_DBG("updating subscription record in hash table failed\n"); if(!fallback2db) goto error; } if(fallback2db) { if(update_subs_db(subs, REMOTE_TYPE)< 0) { LM_ERR("updating subscription in database table\n"); goto error; } } if(msg && send_2XX_reply(msg, reply_code, subs->expires, 0, &subs->local_contact)<0) { LM_ERR("sending 2XX reply\n"); goto error; } } else { if(msg && send_2XX_reply(msg, reply_code, subs->expires, &subs->to_tag, &subs->local_contact)<0) { LM_ERR("sending 2XX reply\n"); goto error; } if(subs->expires!= 0) { subs->expires += expires_offset; if(insert_shtable(subs_htable,hash_code,subs)< 0) { LM_ERR("inserting new record in subs_htable\n"); goto error; } if(fallback2db) { if(insert_subs_db(subs) < 0) { LM_ERR("failed to insert subscription in database\n"); goto error; } } } /*otherwise there is a subscription outside a dialog with expires= 0 * no update in database, but should try to send Notify */ } /* send Notifies */ send_notify: if((subs->event->type & PUBL_TYPE) && subs->event->wipeer) { LM_DBG("send Notify with winfo\n"); if(query_db_notify(&subs->pres_uri, subs->event->wipeer, (subs->expires==0)?NULL:subs)< 0) { LM_ERR("Could not send notify winfo\n"); goto error; } } LM_INFO("notify\n"); if(notify(subs, NULL, NULL, 0 , NULL, 0)< 0) { LM_ERR("Failed to send notify request\n"); goto error; } return 0; error: return -1; } void msg_watchers_clean(unsigned int ticks,void *param) { db_key_t db_keys[3]; db_val_t db_vals[3]; db_op_t db_ops[3] ; LM_DBG("cleaning pending subscriptions\n"); db_keys[0] = &str_inserted_time_col; db_ops[0] = OP_LT; db_vals[0].type = DB_INT; db_vals[0].nul = 0; db_vals[0].val.int_val = (int)time(NULL)- waiting_subs_time; db_keys[1] = &str_status_col; db_ops [1] = OP_EQ; db_vals[1].type = DB_INT; db_vals[1].nul = 0; db_vals[1].val.int_val = PENDING_STATUS; if (pa_dbf.use_table(pa_db, &watchers_table) < 0) { LM_ERR("unsuccessful use_table sql operation\n"); return; } if (pa_dbf.delete(pa_db, db_keys, db_ops, db_vals, 2) < 0) LM_ERR("cleaning pending subscriptions\n"); } /* * Function called from the script to process a SUBSCRIBE request * returns: * 1 : success * -1: error * - sends a reply in all cases (success or error). * TODO replace -1 return code in error case with 0 ( exit from the script) * */ int handle_subscribe(struct sip_msg* msg, char* force_active_param, char* str2) { int init_req = 0; subs_t subs; pres_ev_t* event= NULL; event_t* parsed_event= NULL; param_t* ev_param= NULL; int found; str reason= {0, 0}; int reply_code; str reply_str; int ret; /* ??? rename to avoid collisions with other symbols */ counter++; memset(&subs, 0, sizeof(subs_t)); reply_code= 400; reply_str= pu_400_rpl; if( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("parsing headers\n"); goto error; } /* inspecting the Event header field */ if(msg->event && msg->event->body.len > 0) { if (!msg->event->parsed && (parse_event(msg->event) < 0)) { LM_ERR("bad Event header\n"); goto error; } if(((event_t*)msg->event->parsed)->parsed == EVENT_OTHER) { LM_ERR("unrecognized value [%.*s] in Event header\n", msg->event->body.len, msg->event->body.s); goto bad_event; } } else { LM_ERR("Missing Event header\n"); goto bad_event; } /* search event in the list */ parsed_event= (event_t*)msg->event->parsed; event= search_event(parsed_event); if(event== NULL) { LM_ERR("un-registered support for known event [%.*s]\n", parsed_event->text.len, parsed_event->text.s); goto bad_event; } subs.event= event; /* extract the id if any*/ ev_param= parsed_event->params; while(ev_param) { if(ev_param->name.len== 2 && strncasecmp(ev_param->name.s, "id", 2)== 0) { subs.event_id= ev_param->body; break; } ev_param= ev_param->next; } ret = extract_sdialog_info(&subs, msg, max_expires_subscribe, &init_req, server_address); if(ret< 0) { LM_ERR("failed to extract dialog information\n"); if(ret== -2) { reply_code= 500; reply_str= pu_500_rpl; } goto error; } /* from now one most of the possible error are due to fail in internal processing */ reply_code= 500; reply_str= pu_500_rpl; /* getting presentity uri from Request-URI if initial subscribe - or else from database*/ if(init_req) { if(parsed_event->parsed!= EVENT_DIALOG_SLA) { if( parse_sip_msg_uri(msg)< 0) { LM_ERR("failed to parse R-URI\n"); reply_code= 400; reply_str= pu_400_rpl; goto error; } if(uandd_to_uri(msg->parsed_uri.user, msg->parsed_uri.host, &subs.pres_uri)< 0) { LM_ERR("failed to construct uri from user and domain\n"); goto error; } } } else { if(get_stored_info(msg, &subs, &reply_code, &reply_str )< 0) { LM_ERR("getting stored info\n"); goto error; } reason= subs.reason; } /* call event specific subscription handling */ if(event->evs_subs_handl) { if(event->evs_subs_handl(msg, &subs, &reply_code, &reply_str)< 0) { LM_ERR("in event specific subscription handling\n"); goto error; } } /* if dialog initiation Subscribe - get subscription state */ if(init_req) { if(!event->req_auth ||(force_active_param && force_active_param[0] == '1')) subs.status = ACTIVE_STATUS; else { /* query in watchers_table - if negative reply - server error */ if(get_db_subs_auth(&subs, &found) < 0) { LM_ERR("getting subscription status from watchers table\n"); goto error; } if(found== 0) { if( subs_process_insert_status(&subs) < 0) { LM_ERR("Failed to extract and insert authorization status\n"); goto error; } } else { reason= subs.reason; } } } /* check if correct status */ if(get_status_str(subs.status)== NULL) { LM_ERR("wrong status\n"); goto error; } LM_DBG("subscription status= %s - %s\n", get_status_str(subs.status), found==0?"inserted":"found in watcher table"); if(update_subscription(msg, &subs, init_req) <0) { LM_ERR("in update_subscription\n"); goto error_free; } if(subs.auth_rules_doc) { pkg_free(subs.auth_rules_doc->s); pkg_free(subs.auth_rules_doc); } if(reason.s) pkg_free(reason.s); if(subs.pres_uri.s) pkg_free(subs.pres_uri.s); if(subs.record_route.s) pkg_free(subs.record_route.s); return 1; bad_event: LM_INFO("Missing or unsupported event header field value\n"); if(parsed_event && parsed_event->text.s) LM_INFO("\tevent= %.*s\n",parsed_event->text.len,parsed_event->text.s); reply_code= BAD_EVENT_CODE; reply_str= pu_489_rpl; error: if(send_error_reply(msg, reply_code, reply_str)< 0) { LM_ERR("failed to send reply on error case\n"); } error_free: if(subs.pres_uri.s) pkg_free(subs.pres_uri.s); if(subs.auth_rules_doc) { if(subs.auth_rules_doc->s) pkg_free(subs.auth_rules_doc->s); pkg_free(subs.auth_rules_doc); } if(reason.s) pkg_free(reason.s); if(subs.record_route.s) pkg_free(subs.record_route.s); return -1; } int extract_sdialog_info(subs_t* subs,struct sip_msg* msg, int mexp, int* init_req, str local_address) { str rec_route= {0, 0}; int rt = 0; contact_body_t *b; struct to_body *pto, *pfrom = NULL; int lexpire; struct sip_uri uri; int err_ret = -1; /* examine the expire header field */ if(msg->expires && msg->expires->body.len > 0) { if (!msg->expires->parsed && (parse_expires(msg->expires) < 0)) { LM_ERR("cannot parse Expires header\n"); goto error; } lexpire = ((exp_body_t*)msg->expires->parsed)->val; LM_DBG("'Expires' header found, value= %d\n", lexpire); } else { LM_DBG("'expires' not found; default=%d\n",subs->event->default_expires); lexpire = subs->event->default_expires; } if(lexpire > mexp) lexpire = mexp; subs->expires = lexpire; if ( (!msg->to && parse_headers(msg, HDR_TO_F, 0)<0) || !msg->to ) { LM_ERR("bad request or missing TO hdr\n"); goto error; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); goto error; } if( pto->parsed_uri.user.s && pto->parsed_uri.host.s && pto->parsed_uri.user.len && pto->parsed_uri.host.len) { subs->to_user = pto->parsed_uri.user; subs->to_domain = pto->parsed_uri.host; } else { if(parse_uri(pto->uri.s, pto->uri.len, &uri)< 0) { LM_ERR("while parsing uri\n"); goto error; } subs->to_user = uri.user; subs->to_domain = uri.host; } /* examine the from header */ if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); goto error; } if (msg->from->parsed == NULL) { LM_DBG("'From' header not parsed\n"); /* parsing from header */ if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); goto error; } } pfrom = (struct to_body*)msg->from->parsed; if( pfrom->parsed_uri.user.s && pfrom->parsed_uri.host.s && pfrom->parsed_uri.user.len && pfrom->parsed_uri.host.len) { subs->from_user = pfrom->parsed_uri.user; subs->from_domain = pfrom->parsed_uri.host; } else { if(parse_uri(pfrom->uri.s, pfrom->uri.len, &uri)< 0) { LM_ERR("while parsing uri\n"); goto error; } subs->from_user = uri.user; subs->from_domain = uri.host; } /*check if the message is an initial request */ if (pto->tag_value.s==NULL || pto->tag_value.len==0 ) { LM_DBG("initial request\n"); *init_req = 1; } else { subs->to_tag = pto->tag_value; *init_req = 0; } if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); goto error; } subs->callid = msg->callid->body; if( msg->cseq==NULL || msg->cseq->body.s==NULL) { LM_ERR("cannot parse cseq header\n"); goto error; } if (str2int( &(get_cseq(msg)->number), &subs->remote_cseq)!=0 ) { LM_ERR("cannot parse cseq number\n"); goto error; } if( msg->contact==NULL || msg->contact->body.s==NULL) { LM_ERR("cannot parse contact header\n"); goto error; } if( parse_contact(msg->contact) <0 ) { LM_ERR(" cannot parse contact" " header\n"); goto error; } b= (contact_body_t* )msg->contact->parsed; if(b == NULL) { LM_ERR("cannot parse contact header\n"); goto error; } subs->contact = b->contacts->uri; LM_DBG("subs->contact= %.*s - len = %d\n",subs->contact.len, subs->contact.s, subs->contact.len); if(subs->event->evp->parsed== EVENT_DIALOG_SLA) { pv_value_t tok; /* if pseudovaraible set use that value */ if(bla_presentity_spec_param.s) /* if parameter defined */ { memset(&tok, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, &bla_presentity_spec, &tok) < 0) /* if value set */ { LM_ERR("Failed to get bla_presentity value\n"); goto error; } if(!(tok.flags&PV_VAL_STR)) { LM_ERR("Wrong value in bla_presentity pvar\n"); goto error; } if(parse_uri(tok.rs.s, tok.rs.len, &uri)< 0) { LM_ERR("Not a valid value, must be a uri [%.*s]\n", tok.rs.len, tok.rs.s); goto error; } if(uandd_to_uri(uri.user, uri.host, &subs->pres_uri)< 0) { LM_ERR("failed to construct uri\n"); goto error; } } else { /* user_contact@from_domain */ if(parse_uri(subs->contact.s, subs->contact.len, &uri)< 0) { LM_ERR("failed to parse contact uri\n"); goto error; } if(uandd_to_uri(uri.user, subs->from_domain, &subs->pres_uri)< 0) { LM_ERR("failed to construct uri\n"); goto error; } } } /*process record route and add it to a string*/ if(*init_req && msg->record_route!=NULL) { rt = print_rr_body(msg->record_route, &rec_route, 0, 0); if(rt != 0) { LM_ERR("processing the record route [%d]\n", rt); rec_route.s=NULL; rec_route.len=0; // goto error; } } subs->record_route = rec_route; subs->sockinfo= msg->rcv.bind_address; if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); goto error; } subs->from_tag = pfrom->tag_value; subs->version = 0; if(!local_address.s || !local_address.len) { if(get_local_contact(msg->rcv.bind_address, &subs->local_contact) < 0) { LM_ERR("in function get_local_contact\n"); err_ret = -2; goto error; } } else subs->local_contact= local_address; return 0; error: return err_ret; /* * -1 - bad message * -2 - internal error * */ } /* * function that queries 'active_watchers' table for stored subscription dialog * - sets reply_code and reply_str in error case if different than server error * */ int get_stored_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* reply_str) { str pres_uri= {0, 0}, reason={0, 0}; subs_t* s; int i; unsigned int hash_code; /* first try to_user== pres_user and to_domain== pres_domain */ if(subs->pres_uri.s == NULL) { uandd_to_uri(subs->to_user, subs->to_domain, &pres_uri); if(pres_uri.s== NULL) { LM_ERR("creating uri from user and domain\n"); return -1; } } else pres_uri = subs->pres_uri; hash_code= core_hash(&pres_uri, &subs->event->name, shtable_size); lock_get(&subs_htable[hash_code].lock); i= hash_code; s= search_shtable(subs_htable, subs->callid, subs->to_tag, subs->from_tag, hash_code); if(s) { goto found_rec; } lock_release(&subs_htable[hash_code].lock); if(subs->pres_uri.s) goto not_found; pkg_free(pres_uri.s); pres_uri.s= NULL; LM_DBG("record not found using R-URI search iteratively\n"); /* take one row at a time */ for(i= 0; i< shtable_size; i++) { lock_get(&subs_htable[i].lock); s= search_shtable(subs_htable, subs->callid,subs->to_tag,subs->from_tag, i); if(s) { if(s->event->evp->parsed != EVENT_DIALOG_SLA) { pres_uri.s= (char*)pkg_malloc(s->pres_uri.len); if(pres_uri.s== NULL) { lock_release(&subs_htable[i].lock); ERR_MEM(PKG_MEM_STR); } memcpy(pres_uri.s, s->pres_uri.s, s->pres_uri.len); pres_uri.len= s->pres_uri.len; } goto found_rec; } lock_release(&subs_htable[i].lock); } if(fallback2db) { return get_database_info(msg, subs, reply_code, reply_str); } not_found: LM_ERR("record not found in hash_table\n"); *reply_code= 481; *reply_str= pu_481_rpl; return -1; found_rec: LM_DBG("Record found in hash_table\n"); if(s->event->evp->parsed!= EVENT_DIALOG_SLA) subs->pres_uri= pres_uri; subs->version = s->version; subs->status= s->status; if(s->reason.s && s->reason.len) { reason.s= (char*)pkg_malloc(s->reason.len); if(reason.s== NULL) { lock_release(&subs_htable[i].lock); ERR_MEM(PKG_MEM_STR); } memcpy(reason.s, s->reason.s, s->reason.len); reason.len= s->reason.len; subs->reason= reason; } if(s->record_route.s && s->record_route.len) { subs->record_route.s= (char*)pkg_malloc (s->record_route.len); if(subs->record_route.s== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(subs->record_route.s, s->record_route.s, s->record_route.len); subs->record_route.len= s->record_route.len; } subs->local_cseq= s->local_cseq; if(subs->remote_cseq<= s->remote_cseq) { LM_ERR("wrong sequence number;received: %d - stored: %d\n", subs->remote_cseq, s->remote_cseq); *reply_code= 400; *reply_str= pu_400_rpl; lock_release(&subs_htable[i].lock); goto error; } lock_release(&subs_htable[i].lock); return 0; error: if(subs->reason.s) pkg_free(subs->reason.s); subs->reason.s= NULL; if(subs->record_route.s) pkg_free(subs->record_route.s); subs->record_route.s= NULL; return -1; } int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* reply_str) { static db_ps_t my_ps = NULL; db_key_t query_cols[10]; db_val_t query_vals[10]; db_key_t result_cols[9]; db_res_t *result= NULL; db_row_t *row ; db_val_t *row_vals ; int n_query_cols = 0; int n_result_cols = 0; int remote_cseq_col= 0, local_cseq_col= 0, status_col, reason_col; int record_route_col, version_col; int pres_uri_col; unsigned int remote_cseq; str pres_uri, record_route; str reason; query_cols[n_query_cols] = &str_to_user_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->to_user; n_query_cols++; query_cols[n_query_cols] = &str_to_domain_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->to_domain; n_query_cols++; query_cols[n_query_cols] = &str_watcher_username_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->from_user; n_query_cols++; query_cols[n_query_cols] = &str_watcher_domain_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->from_domain; n_query_cols++; query_cols[n_query_cols] = &str_event_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->event->name; n_query_cols++; query_cols[n_query_cols] = &str_event_id_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; if( subs->event_id.s != NULL) { query_vals[n_query_cols].val.str_val.s = subs->event_id.s; query_vals[n_query_cols].val.str_val.len = subs->event_id.len; } else { query_vals[n_query_cols].val.str_val.s = ""; query_vals[n_query_cols].val.str_val.len = 0; } n_query_cols++; query_cols[n_query_cols] = &str_callid_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->callid; n_query_cols++; query_cols[n_query_cols] = &str_to_tag_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->to_tag; n_query_cols++; query_cols[n_query_cols] = &str_from_tag_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = subs->from_tag; n_query_cols++; result_cols[pres_uri_col=n_result_cols++] = &str_presentity_uri_col; result_cols[remote_cseq_col=n_result_cols++] = &str_remote_cseq_col; result_cols[local_cseq_col=n_result_cols++] = &str_local_cseq_col; result_cols[status_col=n_result_cols++] = &str_status_col; result_cols[reason_col=n_result_cols++] = &str_reason_col; result_cols[record_route_col=n_result_cols++] = &str_record_route_col; result_cols[version_col=n_result_cols++] = &str_version_col; if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) { LM_ERR("unsuccessful use_table sql operation\n"); return -1; } CON_PS_REFERENCE(pa_db) = &my_ps; if (pa_dbf.query (pa_db, query_cols, 0, query_vals, result_cols, n_query_cols, n_result_cols, 0, &result) < 0) { LM_ERR("querying subscription dialog\n"); if(result) pa_dbf.free_result(pa_db, result); return -1; } if(result== NULL) return -1; if(result && result->n <=0) { LM_ERR("No matching subscription dialog found in database\n"); pa_dbf.free_result(pa_db, result); *reply_code= 481; *reply_str= pu_481_rpl; return -1; } row = &result->rows[0]; row_vals = ROW_VALUES(row); remote_cseq= row_vals[remote_cseq_col].val.int_val; if(subs->remote_cseq<= remote_cseq) { LM_ERR("wrong sequence number received: %d - stored: %d\n", subs->remote_cseq, remote_cseq); *reply_code= 400; *reply_str= pu_400_rpl; pa_dbf.free_result(pa_db, result); return -1; } subs->status= row_vals[status_col].val.int_val; reason.s= (char*)row_vals[reason_col].val.string_val; if(reason.s) { reason.len= strlen(reason.s); subs->reason.s= (char*)pkg_malloc(reason.len); if(subs->reason.s== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(subs->reason.s, reason.s, reason.len); subs->reason.len= reason.len; } subs->local_cseq= row_vals[local_cseq_col].val.int_val; subs->version= row_vals[version_col].val.int_val; if(subs->event->evp->parsed!= EVENT_DIALOG_SLA) { pres_uri.s= (char*)row_vals[pres_uri_col].val.string_val; pres_uri.len= strlen(pres_uri.s); subs->pres_uri.s= (char*)pkg_malloc(pres_uri.len); if(subs->pres_uri.s== NULL) { if(subs->reason.s) pkg_free(subs->reason.s); ERR_MEM(PKG_MEM_STR); } memcpy(subs->pres_uri.s, pres_uri.s, pres_uri.len); subs->pres_uri.len= pres_uri.len; } record_route.s= (char*)row_vals[record_route_col].val.string_val; if(record_route.s) { record_route.len= strlen(record_route.s); subs->record_route.s= (char*)pkg_malloc(record_route.len); if(subs->record_route.s== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(subs->record_route.s, record_route.s, record_route.len); subs->record_route.len= record_route.len; } pa_dbf.free_result(pa_db, result); result= NULL; return 0; error: if(result) pa_dbf.free_result(pa_db, result); return -1; } int handle_expired_subs(subs_t* s) { if (s->event->mandatory_timeout_notification) { /* send Notify with state=terminated;reason=timeout */ s->status= TERMINATED_STATUS; s->reason.s= "timeout"; s->reason.len= 7; s->expires= 0; LM_INFO("notify\n"); if(send_notify_request(s, NULL, NULL, 1, NULL, 0)< 0) { LM_ERR("send Notify not successful\n"); return -1; } } return 0; } void timer_db_update(unsigned int ticks,void *param) { int no_lock=0; if(ticks== 0 && param == NULL) no_lock= 1; if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0) { LM_ERR("sql use table failed\n"); return; } update_db_subs(pa_db, pa_dbf, subs_htable, shtable_size, no_lock, handle_expired_subs); } void update_db_subs(db_con_t *db,db_func_t dbf, shtable_t hash_table, int htable_size, int no_lock, handle_expired_func_t handle_expired_func) { static db_ps_t my_ps_delete = NULL; static db_ps_t my_ps_update = NULL, my_ps_insert = NULL; db_key_t query_cols[22], update_cols[8]; db_val_t query_vals[22], update_vals[8]; db_op_t update_ops[1]; subs_t* del_s; int pres_uri_col, to_user_col, to_domain_col, from_user_col, from_domain_col, callid_col, totag_col, fromtag_col, event_col,status_col, event_id_col, local_cseq_col, remote_cseq_col, expires_col, record_route_col, contact_col, local_contact_col, version_col,socket_info_col,reason_col; int u_expires_col, u_local_cseq_col, u_remote_cseq_col, u_version_col, u_reason_col, u_status_col, u_contact_col; int i; subs_t* s= NULL, *prev_s= NULL; int n_query_cols= 0, n_update_cols= 0; int n_query_update; query_cols[pres_uri_col= n_query_cols] =&str_presentity_uri_col; query_vals[pres_uri_col].type = DB_STR; query_vals[pres_uri_col].nul = 0; n_query_cols++; query_cols[callid_col= n_query_cols] =&str_callid_col; query_vals[callid_col].type = DB_STR; query_vals[callid_col].nul = 0; n_query_cols++; query_cols[totag_col= n_query_cols] =&str_to_tag_col; query_vals[totag_col].type = DB_STR; query_vals[totag_col].nul = 0; n_query_cols++; query_cols[fromtag_col= n_query_cols] =&str_from_tag_col; query_vals[fromtag_col].type = DB_STR; query_vals[fromtag_col].nul = 0; n_query_cols++; n_query_update= n_query_cols; query_cols[to_user_col= n_query_cols] =&str_to_user_col; query_vals[to_user_col].type = DB_STR; query_vals[to_user_col].nul = 0; n_query_cols++; query_cols[to_domain_col= n_query_cols] =&str_to_domain_col; query_vals[to_domain_col].type = DB_STR; query_vals[to_domain_col].nul = 0; n_query_cols++; query_cols[from_user_col= n_query_cols] =&str_watcher_username_col; query_vals[from_user_col].type = DB_STR; query_vals[from_user_col].nul = 0; n_query_cols++; query_cols[from_domain_col= n_query_cols] =&str_watcher_domain_col; query_vals[from_domain_col].type = DB_STR; query_vals[from_domain_col].nul = 0; n_query_cols++; query_cols[event_col= n_query_cols] =&str_event_col; query_vals[event_col].type = DB_STR; query_vals[event_col].nul = 0; n_query_cols++; query_cols[event_id_col= n_query_cols] =&str_event_id_col; query_vals[event_id_col].type = DB_STR; query_vals[event_id_col].nul = 0; n_query_cols++; query_cols[local_cseq_col= n_query_cols]=&str_local_cseq_col; query_vals[local_cseq_col].type = DB_INT; query_vals[local_cseq_col].nul = 0; n_query_cols++; query_cols[remote_cseq_col= n_query_cols]=&str_remote_cseq_col; query_vals[remote_cseq_col].type = DB_INT; query_vals[remote_cseq_col].nul = 0; n_query_cols++; query_cols[expires_col= n_query_cols] =&str_expires_col; query_vals[expires_col].type = DB_INT; query_vals[expires_col].nul = 0; n_query_cols++; query_cols[status_col= n_query_cols] =&str_status_col; query_vals[status_col].type = DB_INT; query_vals[status_col].nul = 0; n_query_cols++; query_cols[reason_col= n_query_cols] =&str_reason_col; query_vals[reason_col].type = DB_STR; query_vals[reason_col].nul = 0; n_query_cols++; query_cols[record_route_col= n_query_cols] =&str_record_route_col; query_vals[record_route_col].type = DB_STR; query_vals[record_route_col].nul = 0; n_query_cols++; query_cols[contact_col= n_query_cols] =&str_contact_col; query_vals[contact_col].type = DB_STR; query_vals[contact_col].nul = 0; n_query_cols++; query_cols[local_contact_col= n_query_cols] =&str_local_contact_col; query_vals[local_contact_col].type = DB_STR; query_vals[local_contact_col].nul = 0; n_query_cols++; query_cols[socket_info_col= n_query_cols] =&str_socket_info_col; query_vals[socket_info_col].type = DB_STR; query_vals[socket_info_col].nul = 0; n_query_cols++; query_cols[version_col= n_query_cols]=&str_version_col; query_vals[version_col].type = DB_INT; query_vals[version_col].nul = 0; n_query_cols++; /* cols and values used for update */ update_cols[u_expires_col= n_update_cols]= &str_expires_col; update_vals[u_expires_col].type = DB_INT; update_vals[u_expires_col].nul = 0; n_update_cols++; update_cols[u_status_col= n_update_cols]= &str_status_col; update_vals[u_status_col].type = DB_INT; update_vals[u_status_col].nul = 0; n_update_cols++; update_cols[u_reason_col= n_update_cols]= &str_reason_col; update_vals[u_reason_col].type = DB_STR; update_vals[u_reason_col].nul = 0; n_update_cols++; update_cols[u_remote_cseq_col= n_update_cols]= &str_remote_cseq_col; update_vals[u_remote_cseq_col].type = DB_INT; update_vals[u_remote_cseq_col].nul = 0; n_update_cols++; update_cols[u_local_cseq_col= n_update_cols]= &str_local_cseq_col; update_vals[u_local_cseq_col].type = DB_INT; update_vals[u_local_cseq_col].nul = 0; n_update_cols++; update_cols[u_contact_col= n_update_cols]= &str_contact_col; update_vals[u_contact_col].type = DB_STR; update_vals[u_contact_col].nul = 0; n_update_cols++; update_cols[u_version_col= n_update_cols]= &str_version_col; update_vals[u_version_col].type = DB_INT; update_vals[u_version_col].nul = 0; n_update_cols++; if (db==NULL){ LM_ERR("null database connection\n"); return; } for(i=0; inext; while(s) { printf_subs(s); if(s->expires < (int)time(NULL)) { LM_DBG("Found expired record\n"); del_s= s; s= s->next; prev_s->next= s; if(!no_lock) { lock_release(&hash_table[i].lock); if(handle_expired_func(del_s)< 0) { LM_ERR("in function handle_expired_record\n"); return; } } free_subs(del_s); if(!no_lock) lock_get(&hash_table[i].lock); continue; } switch(s->db_flag) { case NO_UPDATEDB_FLAG: { LM_DBG("NO_UPDATEDB_FLAG\n"); break; } case UPDATEDB_FLAG: { LM_DBG("UPDATEDB_FLAG\n"); query_vals[pres_uri_col].val.str_val= s->pres_uri; query_vals[callid_col].val.str_val= s->callid; query_vals[totag_col].val.str_val= s->to_tag; query_vals[fromtag_col].val.str_val= s->from_tag; update_vals[u_expires_col].val.int_val= s->expires; update_vals[u_local_cseq_col].val.int_val= s->local_cseq; update_vals[u_remote_cseq_col].val.int_val= s->remote_cseq; update_vals[u_version_col].val.int_val= s->version; update_vals[u_status_col].val.int_val= s->status; update_vals[u_reason_col].val.str_val= s->reason; update_vals[u_contact_col].val.str_val= s->contact; CON_PS_REFERENCE(db) = &my_ps_update; if(dbf.update(db, query_cols, 0, query_vals, update_cols, update_vals, n_query_update, n_update_cols)< 0) { LM_ERR("updating in database\n"); if(!no_lock) lock_release(&hash_table[i].lock); return ; } break; } case INSERTDB_FLAG: { LM_DBG("INSERTDB_FLAG\n"); query_vals[pres_uri_col].val.str_val= s->pres_uri; query_vals[callid_col].val.str_val= s->callid; query_vals[totag_col].val.str_val= s->to_tag; query_vals[fromtag_col].val.str_val= s->from_tag; query_vals[to_user_col].val.str_val = s->to_user; query_vals[to_domain_col].val.str_val = s->to_domain; query_vals[from_user_col].val.str_val = s->from_user; query_vals[from_domain_col].val.str_val = s->from_domain; query_vals[event_col].val.str_val = s->event->name; query_vals[event_id_col].val.str_val = s->event_id; query_vals[local_cseq_col].val.int_val= s->local_cseq; query_vals[remote_cseq_col].val.int_val= s->remote_cseq; query_vals[expires_col].val.int_val = s->expires; query_vals[record_route_col].val.str_val = s->record_route; query_vals[contact_col].val.str_val = s->contact; query_vals[local_contact_col].val.str_val = s->local_contact; query_vals[version_col].val.int_val= s->version; query_vals[status_col].val.int_val= s->status; query_vals[reason_col].val.str_val= s->reason; if(s->sockinfo) query_vals[socket_info_col].val.str_val= s->sockinfo->sock_str; else { query_vals[socket_info_col].val.str_val.s = 0; query_vals[socket_info_col].val.str_val.len = 0; } CON_PS_REFERENCE(db) = &my_ps_insert; if(dbf.insert(db,query_cols,query_vals,n_query_cols )<0) { LM_ERR("unsuccessful sql insert\n"); if(!no_lock) lock_release(&hash_table[i].lock); return ; } break; } } s->db_flag= NO_UPDATEDB_FLAG; prev_s= s; s= s->next; } if(!no_lock) lock_release(&hash_table[i].lock); } /* now that all records were updated, delete whatever was still left as expired */ LM_DBG("delete expired\n"); update_vals[0].val.int_val = (int)time(NULL); update_ops[0] = OP_LT; CON_PS_REFERENCE(db) = &my_ps_delete; if (dbf.use_table(db, &active_watchers_table) < 0) { LM_ERR("deleting expired information from database\n"); } else { if (dbf.delete(db, update_cols, update_ops, update_vals, 1) < 0) LM_ERR("deleting expired information from database\n"); } return; } int insert_subs_db(subs_t* s) { static db_ps_t my_ps = NULL; db_key_t query_cols[22]; db_val_t query_vals[22]; int n_query_cols= 0; query_cols[n_query_cols] =&str_presentity_uri_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= s->pres_uri; n_query_cols++; query_cols[n_query_cols] =&str_callid_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= s->callid; n_query_cols++; query_cols[n_query_cols] =&str_to_tag_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= s->to_tag; n_query_cols++; query_cols[n_query_cols] =&str_from_tag_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= s->from_tag; n_query_cols++; query_cols[n_query_cols] =&str_to_user_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = s->to_user; n_query_cols++; query_cols[n_query_cols] =&str_to_domain_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = s->to_domain; n_query_cols++; query_cols[n_query_cols] =&str_watcher_username_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = s->from_user; n_query_cols++; query_cols[n_query_cols] =&str_watcher_domain_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = s->from_domain; n_query_cols++; query_cols[n_query_cols] =&str_event_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = s->event->name; n_query_cols++; query_cols[n_query_cols] =&str_event_id_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = s->event_id; n_query_cols++; query_cols[n_query_cols]=&str_local_cseq_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val= s->local_cseq; n_query_cols++; query_cols[n_query_cols]=&str_remote_cseq_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val= s->remote_cseq; n_query_cols++; query_cols[n_query_cols] =&str_expires_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val = s->expires + (int)time(NULL); n_query_cols++; query_cols[n_query_cols] =&str_status_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val= s->status; n_query_cols++; query_cols[n_query_cols] =&str_reason_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= s->reason; n_query_cols++; query_cols[n_query_cols] =&str_record_route_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = s->record_route; n_query_cols++; query_cols[n_query_cols] =&str_contact_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = s->contact; n_query_cols++; query_cols[n_query_cols] =&str_local_contact_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = s->local_contact; n_query_cols++; query_cols[n_query_cols]=&str_version_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val= s->version; n_query_cols++; query_cols[n_query_cols] =&str_socket_info_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; if(s->sockinfo) query_vals[n_query_cols].val.str_val= s->sockinfo->sock_str; else { query_vals[n_query_cols].val.str_val.s = 0; query_vals[n_query_cols].val.str_val.len = 0; } n_query_cols++; if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0) { LM_ERR("in use table\n"); return -1; } CON_PS_REFERENCE(pa_db) = &my_ps; if(pa_dbf.insert(pa_db,query_cols,query_vals,n_query_cols )<0) { LM_ERR("unsuccessful sql insert\n"); return -1; } return 0; } int restore_db_subs(void) { db_key_t result_cols[22]; db_res_t *result= NULL; db_row_t *rows = NULL; db_val_t *row_vals= NULL; int i; int n_result_cols= 0; int pres_uri_col, expires_col, from_user_col, from_domain_col,to_user_col; int callid_col,totag_col,fromtag_col,to_domain_col,sockinfo_col,reason_col; int event_col,contact_col,record_route_col, event_id_col, status_col; int remote_cseq_col, local_cseq_col, local_contact_col, version_col; subs_t s; str ev_sname, sockinfo_str; pres_ev_t* event= NULL; event_t parsed_event; unsigned int expires; unsigned int hash_code; int port, proto; str host; int nr_rows; int no_rows = 10; result_cols[pres_uri_col=n_result_cols++] =&str_presentity_uri_col; result_cols[expires_col=n_result_cols++]=&str_expires_col; result_cols[event_col=n_result_cols++] =&str_event_col; result_cols[event_id_col=n_result_cols++]=&str_event_id_col; result_cols[to_user_col=n_result_cols++] =&str_to_user_col; result_cols[to_domain_col=n_result_cols++] =&str_to_domain_col; result_cols[from_user_col=n_result_cols++] =&str_watcher_username_col; result_cols[from_domain_col=n_result_cols++]=&str_watcher_domain_col; result_cols[callid_col=n_result_cols++] =&str_callid_col; result_cols[totag_col=n_result_cols++] =&str_to_tag_col; result_cols[fromtag_col=n_result_cols++]=&str_from_tag_col; result_cols[local_cseq_col= n_result_cols++] =&str_local_cseq_col; result_cols[remote_cseq_col= n_result_cols++] =&str_remote_cseq_col; result_cols[record_route_col= n_result_cols++] =&str_record_route_col; result_cols[sockinfo_col= n_result_cols++] =&str_socket_info_col; result_cols[contact_col= n_result_cols++] =&str_contact_col; result_cols[local_contact_col= n_result_cols++] =&str_local_contact_col; result_cols[version_col= n_result_cols++] =&str_version_col; result_cols[status_col= n_result_cols++] =&str_status_col; result_cols[reason_col= n_result_cols++] =&str_reason_col; if(!pa_db) { LM_ERR("null database connection\n"); return -1; } if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0) { LM_ERR("in use table\n"); return -1; } /* select the whole tabel and all the columns */ if (DB_CAPABILITY(pa_dbf, DB_CAP_FETCH)) { if(pa_dbf.query(pa_db,0,0,0,result_cols, 0, n_result_cols, 0, 0) < 0) { LM_ERR("Error while querying (fetch) database\n"); return -1; } no_rows = estimate_available_rows( 64+4+32+4+64+64+64+64+128 +32+32+8+8+256+32+64+64+8+8+8, n_result_cols); if (no_rows==0) no_rows = 10; if(pa_dbf.fetch_result(pa_db,&result, no_rows)<0) { LM_ERR("fetching rows failed\n"); goto error; } } else { if (pa_dbf.query (pa_db, 0, 0, 0,result_cols,0, n_result_cols, 0, &result) < 0) { LM_ERR("querying presentity\n"); goto error; } } nr_rows = RES_ROW_N(result); do { LM_DBG("loading information from database %i records\n", nr_rows); rows = RES_ROWS(result); /* for every row */ for(i=0; iname.s= (char*)shm_malloc(ev_sname.len); if(event->name.s== NULL) { free_event_params(parsed_event.params, PKG_MEM_TYPE); ERR_MEM("shm"); } memcpy(event->name.s,ev_sname.s, ev_sname.len); event->name.len= ev_sname.len; event->evp= shm_copy_event(&parsed_event); if(event->evp== NULL) { LM_ERR("ERROR copying event_t structure\n"); free_event_params(parsed_event.params, PKG_MEM_TYPE); goto error; } event->next= EvList->events; EvList->events= event; } free_event_params(parsed_event.params, PKG_MEM_TYPE); s.event= event; s.event_id.s=(char*)row_vals[event_id_col].val.string_val; if(s.event_id.s) s.event_id.len= strlen(s.event_id.s); s.remote_cseq= row_vals[remote_cseq_col].val.int_val; s.local_cseq= row_vals[local_cseq_col].val.int_val; s.version= row_vals[version_col].val.int_val; s.expires= expires- (int)time(NULL); s.status = row_vals[status_col].val.int_val; if(!event->req_auth) s.status = ACTIVE_STATUS; else { if(subs_process_insert_status(&s)< 0) { LM_ERR("Failed to extract and insert status\n"); goto error; } } s.reason.s= (char*)row_vals[reason_col].val.string_val; if(s.reason.s) s.reason.len= strlen(s.reason.s); s.contact.s=(char*)row_vals[contact_col].val.string_val; s.contact.len= strlen(s.contact.s); s.local_contact.s=(char*)row_vals[local_contact_col].val.string_val; s.local_contact.len= strlen(s.local_contact.s); s.record_route.s=(char*)row_vals[record_route_col].val.string_val; if(s.record_route.s) s.record_route.len= strlen(s.record_route.s); sockinfo_str.s = (char*)row_vals[sockinfo_col].val.string_val; if (sockinfo_str.s) { sockinfo_str.len = strlen(sockinfo_str.s); if (parse_phostport (sockinfo_str.s, sockinfo_str.len, &host.s, &host.len, &port, &proto )< 0) { LM_ERR("bad format for stored sockinfo string\n"); goto error; } s.sockinfo = grep_sock_info(&host, (unsigned short) port, (unsigned short) proto); } hash_code= core_hash(&s.pres_uri, &s.event->name, shtable_size); if(insert_shtable(subs_htable, hash_code, &s)< 0) { LM_ERR("adding new record in hash table\n"); goto error; } } /* any more data to be fetched ?*/ if (DB_CAPABILITY(pa_dbf, DB_CAP_FETCH)) { if (pa_dbf.fetch_result( pa_db, &result, no_rows ) < 0) { LM_ERR("fetching more rows failed\n"); goto error; } nr_rows = RES_ROW_N(result); } else { nr_rows = 0; } }while (nr_rows>0); pa_dbf.free_result(pa_db, result); if(!fallback2db) { /* delete all records */ if(pa_dbf.delete(pa_db, 0,0,0,0)< 0) { LM_ERR("deleting all records from database table\n"); return -1; } } return 0; error: if(result) pa_dbf.free_result(pa_db, result); return -1; } int refresh_watcher(str* pres_uri, str* watcher_uri, str* event, int status, str* reason) { unsigned int hash_code; subs_t* s, *s_copy; pres_ev_t* ev; struct sip_uri uri; str user, domain; /* refresh status in subs_htable and send notify */ ev= contains_event(event, NULL); if(ev== NULL) { LM_ERR("while searching event in list\n"); return -1; } if(parse_uri(watcher_uri->s, watcher_uri->len, &uri)< 0) { LM_ERR("parsing uri\n"); return -1; } user= uri.user; domain= uri.host; hash_code= core_hash(pres_uri, event, shtable_size); lock_get(&subs_htable[hash_code].lock); s= subs_htable[hash_code].entries->next; while(s) { if(s->event== ev && s->pres_uri.len== pres_uri->len && strncmp(s->pres_uri.s, pres_uri->s, pres_uri->len)== 0 && s->from_user.len==user.len && strncmp(s->from_user.s,user.s, user.len)==0 && s->from_domain.len== domain.len && strncmp(s->from_domain.s, domain.s, domain.len)== 0) { s->status= status; if(reason) s->reason= *reason; s_copy= mem_copy_subs(s, PKG_MEM_TYPE); if(s_copy== NULL) { LM_ERR("copying subs_t\n"); lock_release(&subs_htable[hash_code].lock); return -1; } lock_release(&subs_htable[hash_code].lock); if(notify(s_copy, NULL, NULL, 0, NULL, 0)< 0) { LM_ERR("in notify function\n"); pkg_free(s_copy); return -1; } pkg_free(s_copy); lock_get(&subs_htable[hash_code].lock); } s= s->next; } return 0; } /* * function that queries 'watchers' table from subscription status * */ int get_db_subs_auth(subs_t* subs, int* found) { static db_ps_t my_ps = NULL; db_key_t db_keys[5]; db_val_t db_vals[5]; int n_query_cols= 0; db_key_t result_cols[3]; db_res_t *result = NULL; db_row_t *row ; db_val_t *row_vals ; db_keys[n_query_cols] =&str_presentity_uri_col; db_vals[n_query_cols].type = DB_STR; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.str_val= subs->pres_uri; n_query_cols++; db_keys[n_query_cols] =&str_watcher_username_col; db_vals[n_query_cols].type = DB_STR; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.str_val = subs->from_user; n_query_cols++; db_keys[n_query_cols] =&str_watcher_domain_col; db_vals[n_query_cols].type = DB_STR; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.str_val = subs->from_domain; n_query_cols++; db_keys[n_query_cols] =&str_event_col; db_vals[n_query_cols].type = DB_STR; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.str_val = subs->event->name; n_query_cols++; result_cols[0] = &str_status_col; result_cols[1] = &str_reason_col; if(pa_dbf.use_table(pa_db, &watchers_table)< 0) { LM_ERR("in use table\n"); return -1; } CON_PS_REFERENCE(pa_db) = &my_ps; if(pa_dbf.query(pa_db, db_keys, 0, db_vals, result_cols, n_query_cols, 2, 0, &result )< 0) { LM_ERR("while querying watchers table\n"); if(result) pa_dbf.free_result(pa_db, result); return -1; } if(result== NULL) return -1; if(result->n<= 0) { *found= 0; pa_dbf.free_result(pa_db, result); return 0; } *found= 1; row = &result->rows[0]; row_vals = ROW_VALUES(row); subs->status= row_vals[0].val.int_val; if(row_vals[1].val.string_val) { subs->reason.len= strlen(row_vals[1].val.string_val); if(subs->reason.len== 0) subs->reason.s= NULL; else { subs->reason.s= (char*)pkg_malloc(subs->reason.len); if(subs->reason.s== NULL) { pa_dbf.free_result(pa_db, result); ERR_MEM(PKG_MEM_STR); } memcpy(subs->reason.s, row_vals[1].val.string_val, subs->reason.len); } } pa_dbf.free_result(pa_db, result); return 0; error: if (result) pa_dbf.free_result(pa_db, result); return -1; } int insert_db_subs_auth(subs_t* subs) { static db_ps_t my_ps = NULL; db_key_t db_keys[10]; db_val_t db_vals[10]; int n_query_cols= 0; db_keys[n_query_cols] =&str_presentity_uri_col; db_vals[n_query_cols].type = DB_STR; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.str_val= subs->pres_uri; n_query_cols++; db_keys[n_query_cols] =&str_watcher_username_col; db_vals[n_query_cols].type = DB_STR; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.str_val = subs->from_user; n_query_cols++; db_keys[n_query_cols] =&str_watcher_domain_col; db_vals[n_query_cols].type = DB_STR; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.str_val = subs->from_domain; n_query_cols++; db_keys[n_query_cols] =&str_event_col; db_vals[n_query_cols].type = DB_STR; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.str_val = subs->event->name; n_query_cols++; db_keys[n_query_cols] =&str_status_col; db_vals[n_query_cols].type = DB_INT; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.int_val = subs->status; n_query_cols++; db_keys[n_query_cols] = &str_inserted_time_col; db_vals[n_query_cols].type = DB_INT; db_vals[n_query_cols].nul = 0; db_vals[n_query_cols].val.int_val= (int)time(NULL); n_query_cols++; db_keys[n_query_cols] =&str_reason_col; db_vals[n_query_cols].type = DB_STR; db_vals[n_query_cols].nul = 0; if(subs->reason.s && subs->reason.len) { db_vals[n_query_cols].val.str_val = subs->reason; } else { db_vals[n_query_cols].val.str_val.s = ""; db_vals[n_query_cols].val.str_val.len = 0; } n_query_cols++; if (pa_dbf.use_table(pa_db, &watchers_table) < 0) { LM_ERR("in use_table\n"); return -1; } CON_PS_REFERENCE(pa_db) = &my_ps; if(pa_dbf.insert(pa_db, db_keys, db_vals, n_query_cols )< 0) { LM_ERR("in sql insert\n"); return -1; } return 0; } opensips-2.2.2/modules/presence/subscribe.h000066400000000000000000000055531300170765700210010ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) */ #ifndef SUBSCRIBE_H #define SUBSCRIBE_H //#include "presence.h" #include "../../str.h" #include "../../db/db.h" struct pres_ev; #include "event_list.h" #include "hash.h" #define ACTIVE_STATUS 1 #define PENDING_STATUS 2 #define TERMINATED_STATUS 3 #define WAITING_STATUS 4 struct subscription { str pres_uri; str to_user; str to_domain; str from_user; str from_domain; struct pres_ev* event; str event_id; str to_tag; str from_tag; str callid; struct socket_info* sockinfo; unsigned int remote_cseq; unsigned int local_cseq; str contact; str local_contact; str record_route; unsigned int expires; unsigned int status; str reason; int version; int db_flag; str* auth_rules_doc; int internal_update_flag; struct subscription* next; }; typedef struct subscription subs_t; void msg_active_watchers_clean(unsigned int ticks,void *param); void msg_watchers_clean(unsigned int ticks,void *param); int handle_subscribe(struct sip_msg*, char*, char*); int delete_db_subs(str pres_uri, str ev_stored_name, str to_tag); void timer_db_update(unsigned int ticks,void *param); int update_subs_db(subs_t* subs, int type); int refresh_watcher(str* pres_uri, str* watcher_uri, str* event, int status, str* reason); typedef int (*refresh_watcher_t)(str*, str* , str* ,int , str* ); int restore_db_subs(void); typedef int (*handle_expired_func_t)(subs_t* ); void update_db_subs(db_con_t *db,db_func_t dbf, shtable_t hash_table, int htable_size, int no_lock, handle_expired_func_t handle_expired_func); typedef void (*update_db_subs_t)(db_con_t * ,db_func_t ,shtable_t ,int ,int , handle_expired_func_t); int extract_sdialog_info(subs_t* subs,struct sip_msg* msg, int max_expire, int* to_tag_gen, str local_address); typedef int (*extract_sdialog_info_t)(subs_t* subs, struct sip_msg* msg, int max_expire, int* to_tag_gen, str local_address); int update_subscription(struct sip_msg* msg, subs_t* subs, int init_req); #endif opensips-2.2.2/modules/presence/utils_func.c000066400000000000000000000072761300170765700211720ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) */ #include #include #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../ut.h" #include "../../data_lump_rpl.h" #include "utils_func.h" #include "event_list.h" #include "presence.h" static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; #define BAD -1 static const char base64val[] = { BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD, 62, BAD,BAD,BAD, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,BAD,BAD, BAD,BAD,BAD,BAD, BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,BAD, BAD,BAD,BAD,BAD, BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,BAD, BAD,BAD,BAD,BAD }; #define DECODE64(c) (isascii(c) ? base64val[c] : BAD) void to64frombits(unsigned char *out, const unsigned char *in, int inlen) { for (; inlen >= 3; inlen -= 3) { *out++ = base64digits[in[0] >> 2]; *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; *out++ = base64digits[in[2] & 0x3f]; in += 3; } if (inlen > 0) { unsigned char fragment; *out++ = base64digits[in[0] >> 2]; fragment = (in[0] << 4) & 0x30; if (inlen > 1) fragment |= in[1] >> 4; *out++ = base64digits[fragment]; *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c]; *out++ = '='; } *out = '\0'; } int a_to_i (char *s,int len) { int n = 0, i= 0; while( ievents; hdr_append.s = buffer; hdr_append.s[0]='\0'; hdr_append.len = sprintf(hdr_append.s, "Allow-Events: "); if(hdr_append.len < 0) { LM_ERR("unsuccessful sprintf\n"); return -1; } for(i= 0; i< EvList->ev_count; i++) { if(i> 0) { memcpy(hdr_append.s+ hdr_append.len, ", ", 2); hdr_append.len+= 2; } memcpy(hdr_append.s+ hdr_append.len, ev->name.s, ev->name.len ); hdr_append.len+= ev->name.len ; ev= ev->next; } memcpy(hdr_append.s+ hdr_append.len, CRLF, CRLF_LEN); hdr_append.len+= CRLF_LEN; hdr_append.s[hdr_append.len]= '\0'; if (add_lump_rpl( msg, hdr_append.s, hdr_append.len, LUMP_RPL_HDR)==0 ) { LM_ERR("unable to add lump_rl\n"); return -1; } } if (sigb.reply(msg, reply_code, &reply_str, 0) == -1) { LM_ERR("sending %d %.*s reply\n", reply_code, reply_str.len, reply_str.s); return -1; } return 0; } opensips-2.2.2/modules/presence/utils_func.h000066400000000000000000000063171300170765700211720ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (Anca Vamanu) */ #ifndef UTILS_FUNC_H #define UTILS_FUNC_H #include #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../str.h" #include "../../parser/msg_parser.h" #define LCONTACT_BUF_SIZE 1024 #define BAD_EVENT_CODE 489 static inline int uandd_to_uri(str user, str domain, str *out) { int size; if(out==0) return -1; size = user.len + domain.len+7; out->s = (char*)pkg_malloc(size); if(out->s == NULL) { LM_ERR("no more memory\n"); return -1; } strcpy(out->s,"sip:"); out->len = 4; if( user.len != 0) { memcpy(out->s+out->len, user.s, user.len); out->len += user.len; out->s[out->len++] = '@'; } memcpy(out->s + out->len, domain.s, domain.len); out->len += domain.len; out->s[out->len] = '\0'; return 0; } /* Build an contact URI but without the "transport" param - this is to be * added when a send is done, depending on the used interface. */ static inline int get_local_contact(struct socket_info *sock, str* contact) { static char buf[LCONTACT_BUF_SIZE]; contact->s = buf; contact->len= 0; memset(buf, 0, LCONTACT_BUF_SIZE); /* write "sip:ip" */ memcpy(contact->s+contact->len, "sip:", 4); contact->len+= 4; /* if advertised address is set for this interface, use this one */ if (sock->adv_name_str.s) { memcpy(contact->s+contact->len, sock->adv_name_str.s, sock->adv_name_str.len); contact->len += sock->adv_name_str.len; } else { memcpy(contact->s+contact->len, sock->address_str.s, sock->address_str.len); contact->len += sock->address_str.len; } if(contact->len> LCONTACT_BUF_SIZE - 21) { LM_ERR("buffer overflow\n"); return -1; } /* write ":port" if port defined */ if (sock->adv_name_str.s) { if(sock->adv_port_str.s) { *(contact->s+(contact->len++)) = ':'; memcpy(contact->s+contact->len, sock->adv_port_str.s, sock->adv_port_str.len); contact->len += sock->adv_port_str.len; } } else if (sock->port_no_str.len) { *(contact->s+(contact->len++)) = ':'; memcpy(contact->s+contact->len, sock->port_no_str.s, sock->port_no_str.len); contact->len += sock->port_no_str.len; } return 0; } int a_to_i (char *s,int len); void to64frombits(unsigned char *out, const unsigned char *in, int inlen); int send_error_reply(struct sip_msg* msg, int reply_code, str reply_str); #endif opensips-2.2.2/modules/presence_callinfo/000077500000000000000000000000001300170765700205065ustar00rootroot00000000000000opensips-2.2.2/modules/presence_callinfo/Makefile000066400000000000000000000003241300170765700221450ustar00rootroot00000000000000# # Presence_MWI Makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=presence_callinfo.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/presence_callinfo/README000066400000000000000000000254171300170765700213770ustar00rootroot00000000000000Presence_CallInfo Module Ovidiu Sas Bogdan-Andrei Iancu Edited by Ovidiu Sas Edited by Bogdan-Andrei Iancu Copyright © 2010-2013 VoIP Embedded, Inc. , www.opensips-solutions.com Revision History Revision $Revision: 7754 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Usage modes 1.2.1. External publishing 1.2.2. Internal publishing 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. call_info_timeout_notification (int) 1.4.2. line_seize_timeout_notification (int) 1.4.3. disable_dialog_support_for_sca (int) 1.4.4. line_hash_size (int) 1.5. Exported Functions 1.5.1. sca_set_calling_line([line]) 1.5.2. sca_set_called_line([line]) List of Examples 1.1. Set call_info_timeout_notification parameter 1.2. Set line_seize_timeout_notification parameter 1.3. Set disable_dialog_support_for_sca parameter 1.4. Set line_hash_size parameter 1.5. sca_set_calling_line() usage 1.6. sca_set_called_line() usage Chapter 1. Admin Guide 1.1. Overview This module provides OpenSIPS support for shared call appearances (SCA) as defined by BroadWorks SIP Access Side Extensions Interface specifications. The SCA mechanism is a fundamental building block for a variety of enhanced telephony services. Features like attendant console, line extensions, and key system emulation cannot be delivered without some mechanism for sharing call appearances across access devices. Although SIP (RFC 3261) by itself offers no inherent semantics for supporting SCA features, when coupled with an appropriate instantiation of the “SIP Specific Event Notification†framework (RFC 3265), these services can be deployed quite easily in a distributed network. A shared line is an address of record managed by central controlling element, such as an application server. The application server allows multiple endpoints to register locations against the address of record. The application server is responsible for policing who can register and who cannot register against the shared line. The module enables the handling of "call-info" and "line-seize" events inside the presence module. It is used with the general event handling module: presence and it constructs and adds "Call-Info" headers to notification events. 1.2. Usage modes The module can be used in two ways (depending on who is doing the publishing of the "call-info" data: * external publishing - the "call-info" data is received from a third party via SIP PUBLISH requests. In this mode, the modules simply distributes the SCA info, it is not producing any of it - a third-party application must publish "call-info" events to the presence server. * internal publishing - the "call-info" data is internally generated by the module, based on the information received from the dialog module - what calls are using what line/index, what is the state of the call, etc. There is no SIP PUBLISH in this case and there is no need for a third-party - the module is self-sufficient and stand alone as functionality. The used mode can be controlled via the module parameter "disable_dialog_support_for_sca" - see below in the parameter's section. 1.2.1. External publishing The module does not currently implement any authorization rules. It assumes that publish requests are only issued by a third-party application and subscribe requests only by subscriber to call-info and line-seize events. Authorization can thus be easily done by OpenSIPS configuration file before calling handle_publish() and handle_subscribe() functions. To get better understanding on how the module works please take a look at the follwing figure: caller proxy & callee watcher publisher alice@example presence bob@example watcher@example server | | | | | | |<-----SUBSCRIBE bob----| | | |------200 OK---------->| | | |------NOTIFY---------->| | | |<-----200 OK-----------| | | | | | | |--INV bob--->| | | | | |--INV bob->| | | | |<-100------| | | | |<-----PUBLISH(alerting)---------------| | |------200 OK------------------------->| | |------NOTIFY---------->| | | |<-----200 OK-----------| | | | | | | | |<-180 ring-| | | |<--180 ring--| | | | | | | | | | | | | | | |<-200 OK---| | | |<--200 OK----| | | | | |<-----PUBLISH(active)-----------------| | |------200 OK------------------------->| | |------NOTIFY---------->| | | |<-----200 OK-----------| | | | | | | * The watcher subscribes the "Event: dialog" of Bob. * Alice calls Bob. * The publisher is publishing the "alerting" state for Bob. * PUBLISH is received and handled by presence module. Presence module updates the "presentity". Presence module checks for active watchers of the presentity. The active watcher is notified via a NOTIFY SIP request. * Bob answers the call. * The publisher is publishing the "active" state for Bob. * PUBLISH is received and handled by presence module. Presence module updates the "presentity". Presence module checks for active watchers of the presentity. The active watcher is notified via a NOTIFY SIP request. 1.2.2. Internal publishing In this mode, the module requires the "dialog" module to be loaded into OpenSIPS. All the publishing will be automatically done (the modules will exchange data directly via C API). From presence perspective, the OpenSIPS script must be configured to handle the SUBSCRIBE requests only (there is no need for PUBLISH handling as there is no SIP publishing in this mode). So be sure to use the "handle_subscribe()" function (from presence module) in the script. To trigger the internal publishing (from the dialog module) for a certain call, use the "sca_set_calling_line()" or "sca_set_called_line()" functions from the script when handling a new call. These functions will do all the work (creating dialog, setting the internal publishing, etc) - you just need to use them and eventually specify the name of the line (if other then the one from the SIP INVITE) - see the below documentation. LIMITATIONS : in this mode, the module does not really check if the line exists or not (like defined) - it blindly trust the traffic; also there is no check on how many indexes are for each line. Such information (lines and indexes) are not provisioned into the module, but the module will dynamically accept and handle any line and index based on the SIP traffic. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * presence. * dialog. 1.3.2. External Libraries or Applications None. 1.4. Exported Parameters 1.4.1. call_info_timeout_notification (int) Enables or disables call_info event timeout notifications. Default value is “1†(enabled). Example 1.1. Set call_info_timeout_notification parameter ... modparam("presence_callinfo", "call_info_timeout_notification", 0) ... 1.4.2. line_seize_timeout_notification (int) Enables or disables line_seize event timeout notifications. Default value is “0†(disabled). Example 1.2. Set line_seize_timeout_notification parameter ... modparam("presence_callinfo", "line_seize_timeout_notification", 1) ... 1.4.3. disable_dialog_support_for_sca (int) Disables the internal publishing of the "call-info" events (generated by the dialog module). The publishing is expected to be done via SIP PUBLISH from a third-party. See the wroking mode described in the beginning of this document. Default value is “0†(not disabled). Example 1.3. Set disable_dialog_support_for_sca parameter ... modparam("presence_callinfo", "disable_dialog_support_for_sca", 1) ... 1.4.4. line_hash_size (int) Allows you to controll the size of the internal hash table used for storing the information about the lines and indexes (in the internal publishing mode). The value must be a power of 2. You may consider increasing the value if using a large set of lines (>1000). Default value is “64â€. Example 1.4. Set line_hash_size parameter ... modparam("presence_callinfo", "line_hash_size", 128) ... 1.5. Exported Functions 1.5.1. sca_set_calling_line([line]) The function (to be used only in internal publishing mode) is setting for the current new call (initinal INVITE) the outbound line - the line used for calling out. If no parameter is provided, the name of the line is taken from the SIP FROM header of the INVITE. You can override that by providing the name of the line a parameter - be careful as the value must be a SIP URI ! Variables are accepted. This function can be used from REQUEST_ROUTE. Example 1.5. sca_set_calling_line() usage ... if (is_method("INVITE") and !has_totag()) { sca_set_calling_line(); } ... 1.5.2. sca_set_called_line([line]) The function (to be used only in internal publishing mode) is setting for the current new call (initinal INVITE) the inbound line - the line the call was received on. If no parameter is provided, the name of the line is taken from the SIP RURI of the INVITE. You can override that by providing the name of the line a parameter - be careful as the value must be a SIP URI ! Variables are accepted. This function can be used from REQUEST_ROUTE. Example 1.6. sca_set_called_line() usage ... if (is_method("INVITE") and !has_totag()) { sca_set_called_line(); } ... opensips-2.2.2/modules/presence_callinfo/add_events.c000066400000000000000000000301051300170765700227650ustar00rootroot00000000000000/* * Add "call-info" event to presence module * * Copyright (C) 2010 Ovidiu Sas * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-03-11 initial version (osas) * 2010-07-13 added support for SCA Broadsoft with dialog module (bogdan) */ #include #include #include #include "../../timer.h" #include "../../ut.h" #include "../../parser/parse_call_info.h" #include "../presence/event_list.h" #include "presence_callinfo.h" #include "sca_hash.h" #include "add_events.h" extern int call_info_timeout_notification; extern int line_seize_timeout_notification; static str extra_hdrs[] = { str_init("Call-Info"), {NULL,0}, }; static pres_ev_t *callinfo_event = NULL; static pres_ev_t *seize_event = NULL; static str dummy_ci_hdr1 = str_init("Call-Info: <"); static str dummy_ci_hdr2 = str_init(">;appearance-index=*;appearance-state=idle\r\n"); /* * event specific publish handling - check if body format is ok */ static int callinfo_hdr_checker(struct sip_msg* msg, int* sent_reply) { if (parse_headers(msg,HDR_EOH_F, 0) == -1) { LM_ERR("parsing headers\n"); return -1; } if (!msg->call_info) { LM_ERR("No 'Call-Info' header\n"); return -1; } if (0 != parse_call_info_header(msg)) { LM_ERR("Unable to parse Call-Info\n"); return -1; } return 1; } /* * event specific extra headers builder - for empty notifications */ str* build_callinfo_dummy_header(str* pres_uri, str* extra_hdrs) { if (extra_hdrs->s == NULL) { extra_hdrs->s = (char*)pkg_malloc( dummy_ci_hdr1.len + pres_uri->len + dummy_ci_hdr2.len); if (extra_hdrs->s == NULL) { LM_ERR("oom: no dummy header\n"); return NULL; } memcpy(extra_hdrs->s, dummy_ci_hdr1.s, dummy_ci_hdr1.len); extra_hdrs->len = dummy_ci_hdr1.len; memcpy(extra_hdrs->s+extra_hdrs->len, pres_uri->s, pres_uri->len); extra_hdrs->len += pres_uri->len; memcpy(extra_hdrs->s+extra_hdrs->len, dummy_ci_hdr2.s, dummy_ci_hdr2.len); extra_hdrs->len += dummy_ci_hdr2.len; } return NULL; } /* assumes the Call-INFO hdr is parsed ! */ unsigned int get_appearance_index(struct sip_msg *msg) { struct to_param *top; unsigned int idx; top = get_call_info(msg)->call_info_body.param_lst; for ( ; top ; top=top->next) { if ( (top->name.len==CI_hdr_AI_param_len) && (memcmp(CI_hdr_AI_param_s,top->name.s,CI_hdr_AI_param_len)==0) ) { /* found */ if ( str2int( &top->value, &idx)<0 ) { LM_ERR("appearance-index <%.*s> param is not numerical\n", top->value.len, top->value.s); return 0; } return idx; } } LM_ERR("Call-INFO hdr <%.*s> does not contain 'appearance-index' parameter\n", msg->call_info->body.len,msg->call_info->body.s); return 0; } /* * Line must be locked, returned unlocked ! */ int terminate_line_sieze(struct sca_line *sca) { /* do we have a valid seize on the line ? */ if (sca->seize_state==0 || sca->seize_expiresseize_state = 0; sca->seize_expires = 0; unlock_sca_line(sca); return pres.terminate_watchers( &sca->line, seize_event); } /* Function to be called under lock - extracts and saved in local buffers * the sca info that is needed for by "do_callinfo_publish" (as we need to * call this function without locking). * You need to take care and free the "user" string (only that one) !!! */ int extract_publish_data_from_line(struct sca_line *sca, str *user, str *host, str *etag, int *new) { char *buf; buf = (char*)pkg_malloc( sca->user.len + sca->domain.len + MD5_LEN ); if (buf==NULL) { LM_ERR("no more pkg mem\n"); return -1; } user->s = buf ; user->len = sca->user.len; memcpy( user->s, sca->user.s, user->len); buf += user->len; host->s = buf ; host->len = sca->domain.len; memcpy( host->s, sca->domain.s, host->len); buf += host->len; etag->s = buf; etag->len = MD5_LEN; if (sca->etag.len==0) { MD5StringArray( sca->etag.s, &sca->line, 1); sca->etag.len = MD5_LEN; *new = 1; } else { *new = 0; } memcpy( etag->s, sca->etag.s, etag->len); return 0; } /* send the pusblish for the line - expects to get the line locked, * returnes the line unlocked */ int do_callinfo_publish(struct sca_line *sca) { str user, host, etag, ci_hdr; presentity_t presentity; int new_etag; /* generate the new call-info line */ ci_hdr.s = sca_print_line_status( sca, &ci_hdr.len ); if (ci_hdr.s==NULL || extract_publish_data_from_line(sca, &user, &host, &etag, &new_etag)<0){ unlock_sca_line(sca); LM_ERR("failed to extract Call-INFO data for publishing\n"); } else { unlock_sca_line(sca); /* do publish for callinfo */ memset(&presentity, 0, sizeof(presentity_t)); presentity.domain = host; presentity.user = user; presentity.etag = etag; presentity.event = callinfo_event; presentity.expires = callinfo_event->default_expires; presentity.received_time= (int)time(NULL); presentity.extra_hdrs = &ci_hdr; presentity.etag_new = new_etag; if ( pres.update_presentity( &presentity )<0 ) LM_ERR("failed to update presentity\n"); /* release memory from "extract_publish_data_from_line" */ pkg_free(user.s); } /* release memory from "sca_print_line_status" */ if (ci_hdr.s) pkg_free(ci_hdr.s); return 0; } /* * event specific SUBSCRIBE handling - check if body format is ok */ int lineseize_subs_handl(struct sip_msg* msg, struct subscription *subs, int *reply_code, str *reply_reason) { str *line; struct sca_line *sca; unsigned int idx; int is_initial; int new_state; /* search for the Call-INFO hdr */ if ( parse_call_info_header( msg )!=0 ) { LM_ERR("missing or bogus Call-Info header in SUBSCRIBE lineseize\n"); *reply_code = 400; reply_reason->s = "Bad request"; reply_reason->len = sizeof("Bad request")-1; return -1; } is_initial = (subs->to_tag.len==0)?1:0; idx = get_appearance_index(msg); if (idx==0) { LM_ERR("failed to extract index from Call-Info hdr\n"); *reply_code = 400; reply_reason->s = "Bad request"; reply_reason->len = sizeof("Bad request")-1; return -1; } /* get the name of the line -> the subscribed presentity */ line = &subs->pres_uri; /* search for the line in the SCA hash */ LM_DBG("searching for SCA <%.*s>, initial=%d\n", line->len,line->s,is_initial); if (subs->expires==0) { /* if un-subscribe, search without auto create */ sca = get_sca_line(line, 0); } else { /* search with auto create (only if initial) */ sca = get_sca_line(line, is_initial ); } if (sca==NULL) { LM_DBG("SCA not found, expires=%d\n",subs->expires); if (subs->expires==0) { /* an unsubscribe from an inexisting list, let presence deal with it, we do not really care */ return 0; } else { /* for sure this is an internal error, default reply of presence dore */ return -1; } } LM_DBG("SCA found (%p), seizing (%d,%d), subs expires %d\n", sca, sca->seize_state,sca->seize_expires, subs->expires); new_state = 0; /* SCA found, careful now, it is locked !! */ if (!is_initial) { /* sequential FIXME - some double check here? */ if (subs->expires==0) { /* terminate the subscription */ LM_DBG("seizing terminated by un-subscribe\n"); sca->seize_state = 0; sca->seize_expires = 0; new_state = SCA_STATE_IDLE; } else { LM_DBG("seizing changed by re-subscribe\n"); sca->seize_expires = get_ticks() + subs->expires; } } else { /* new SUBSCRIBE */ if (sca->seize_state!=0) { /* already in seizing from a different subscrine */ if (sca->seize_expires < get_ticks()) { /* old seizing still valid -> reject it */ *reply_code = 480; reply_reason->s = "Temporarily Unavailable"; reply_reason->len = sizeof("Temporarily Unavailable")-1; unlock_sca_line(sca); return -1; } } /* FIXME - check the seized idx is not already in a call */ /* do the seizing */ sca->seize_state = idx; sca->seize_expires = get_ticks() + subs->expires; new_state = SCA_STATE_SEIZED; } if (!new_state) { unlock_sca_line(sca); return 0; } /* push new state for the index and do the publishing */ /* STILL LOCKED HERE !! */ /* everything ok, change the state of the line and notify */ set_sca_index_state( sca, idx, new_state); /* do publish for callinfo */ do_callinfo_publish( sca ); return 0; } /* * event specific extra headers builder - for empty notifications */ str* build_lineseize_notify_hdrs(str* pres_uri, str* extra_hdrs) { struct sca_line *sca; unsigned int idx; int l; char *p; char *q; if (extra_hdrs->s!= NULL) return NULL; /* search for the SCA */ sca = get_sca_line(pres_uri, 0); if (sca==NULL) { LM_CRIT("BUG? notify to line-seize but SCA (%.*s) not found\n", pres_uri->len, pres_uri->s); return NULL; } /* watch it!!!! SCA is locked now */ idx = sca->seize_state; unlock_sca_line(sca); if (idx==0) return NULL; /* build the header */ extra_hdrs->s = (char*)pkg_malloc( CI_hdr_name_len + 1 /*<*/ + pres_uri->len + 2 /*>;*/ + CI_hdr_AI_param_len + 1 /*=*/ + 5 /*idx*/ + 2 /*CRLF*/); if (extra_hdrs->s == NULL) { LM_ERR("no more pkg mem for the Call-Info hdr in Notify\n"); return NULL; } p = extra_hdrs->s; memcpy( p, CI_hdr_name_s "<", CI_hdr_name_len+1); p += CI_hdr_name_len + 1; memcpy( p, pres_uri->s, pres_uri->len); p += pres_uri->len; memcpy( p, ">;" CI_hdr_AI_param_s "=", 3+CI_hdr_AI_param_len); p += 3 + CI_hdr_AI_param_len; q = int2str( (unsigned long)idx, &l ); LM_DBG("index is <%.*s>\n",l,q); memcpy( p , q, l); p += l; memcpy( p, CRLF, CRLF_LEN); p += CRLF_LEN; extra_hdrs->len = p - extra_hdrs->s; LM_DBG("hdr is <%.*s>\n",extra_hdrs->len,extra_hdrs->s); return NULL; } int callinfo_add_events(void) { pres_ev_t event; event_t ev; /* constructing call-info event */ memset(&event, 0, sizeof(pres_ev_t)); event.name.s = "call-info"; event.name.len = 9; event.extra_hdrs = extra_hdrs; event.etag_not_new = 1; event.default_expires= 3600; event.mandatory_timeout_notification = call_info_timeout_notification; event.type = PUBL_TYPE; event.evs_publ_handl = callinfo_hdr_checker; /* register the dummy Call-Info header builder */ event.build_empty_pres_info = build_callinfo_dummy_header; if (pres.add_event(&event) < 0) { LM_ERR("failed to add event \"call-info\"\n"); return -1; } /* now search it back as we need the internal event structure */ ev.parsed = EVENT_CALL_INFO; ev.text = event.name; callinfo_event = pres.search_event( &ev ); if (callinfo_event==NULL) { LM_CRIT("BUG: failed to get back the registered CALL INFO event!\n"); return -1; } /* constructing line-seize-info event */ memset(&event, 0, sizeof(pres_ev_t)); event.name.s = "line-seize"; event.name.len = 10; event.default_expires= 15; event.mandatory_timeout_notification = line_seize_timeout_notification; event.type = PUBL_TYPE; if (no_dialog_support) { /* with no dialog, just check the Call-Info hdrs */ event.evs_publ_handl = callinfo_hdr_checker; } else { /* with dialog, handle the subscribes */ event.evs_subs_handl = lineseize_subs_handl; /* register the Call-Info builder for NOTIFIES */ event.build_empty_pres_info = build_lineseize_notify_hdrs; } if (pres.add_event(&event) < 0) { LM_ERR("failed to add event \"line-seize\"\n"); return -1; } /* now search it back as we need the internal event structure */ ev.parsed = EVENT_LINE_SEIZE; ev.text = event.name; seize_event = pres.search_event( &ev ); if (seize_event==NULL) { LM_CRIT("BUG: failed to get back the registered CALL INFO event!\n"); return -1; } return 0; } opensips-2.2.2/modules/presence_callinfo/add_events.h000066400000000000000000000031741300170765700230000ustar00rootroot00000000000000/* * presence_callinfo module - add_event header file * * Copyright (C) 2010 Ovidiu Sas * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-03-11 initial version (osas) * 2010-07-13 added support for SCA Broadsoft with dialog module (bogdan) */ #ifndef _CALLINFO_ADD_EV_H_ #define _CALLINFO_ADD_EV_H_ #include "../../parser/msg_parser.h" #define CI_hdr_name_s "Call-Info: " #define CI_hdr_name_len (sizeof(CI_hdr_name_s)-1) #define CI_hdr_AI_param_s "appearance-index" #define CI_hdr_AI_param_len (sizeof(CI_hdr_AI_param_s)-1) #define CI_hdr_AS_param_s "appearance-state" #define CI_hdr_AS_param_len (sizeof(CI_hdr_AS_param_s)-1) #include "sca_hash.h" int callinfo_add_events(); unsigned int get_appearance_index(struct sip_msg *msg); int do_callinfo_publish(struct sca_line *sca); int terminate_line_sieze(struct sca_line *sca); #endif opensips-2.2.2/modules/presence_callinfo/doc/000077500000000000000000000000001300170765700212535ustar00rootroot00000000000000opensips-2.2.2/modules/presence_callinfo/doc/presence_callinfo.xml000066400000000000000000000026311300170765700254520ustar00rootroot00000000000000 %docentities; ]> Presence_CallInfo Module &osipsname; Ovidiu Sas osas@voipembedded.com Bogdan-Andrei Iancu bogdan@opensips.org Ovidiu Sas osas@voipembedded.com Bogdan-Andrei Iancu bogdan@opensips.org 2010-2013 VoIP Embedded, Inc. &osipssol; $Revision: 7754 $ $Date$ &admin; &faq; opensips-2.2.2/modules/presence_callinfo/doc/presence_callinfo_admin.xml000066400000000000000000000277231300170765700266330ustar00rootroot00000000000000 &adminguide;
Overview This module provides OpenSIPS support for shared call appearances (SCA) as defined by BroadWorks SIP Access Side Extensions Interface specifications. The SCA mechanism is a fundamental building block for a variety of enhanced telephony services. Features like attendant console, line extensions, and key system emulation cannot be delivered without some mechanism for sharing call appearances across access devices. Although SIP (RFC 3261) by itself offers no inherent semantics for supporting SCA features, when coupled with an appropriate instantiation of the “SIP Specific Event Notification†framework (RFC 3265), these services can be deployed quite easily in a distributed network. A shared line is an address of record managed by central controlling element, such as an application server. The application server allows multiple endpoints to register locations against the address of record. The application server is responsible for policing who can register and who cannot register against the shared line. The module enables the handling of "call-info" and "line-seize" events inside the presence module. It is used with the general event handling module: presence and it constructs and adds "Call-Info" headers to notification events.
Usage modes The module can be used in two ways (depending on who is doing the publishing of the "call-info" data: external publishing - the "call-info" data is received from a third party via SIP PUBLISH requests. In this mode, the modules simply distributes the SCA info, it is not producing any of it - a third-party application must publish "call-info" events to the presence server. internal publishing - the "call-info" data is internally generated by the module, based on the information received from the dialog module - what calls are using what line/index, what is the state of the call, etc. There is no SIP PUBLISH in this case and there is no need for a third-party - the module is self-sufficient and stand alone as functionality. The used mode can be controlled via the module parameter "disable_dialog_support_for_sca" - see below in the parameter's section.
External publishing The module does not currently implement any authorization rules. It assumes that publish requests are only issued by a third-party application and subscribe requests only by subscriber to call-info and line-seize events. Authorization can thus be easily done by &osips; configuration file before calling handle_publish() and handle_subscribe() functions. To get better understanding on how the module works please take a look at the follwing figure: | | | |------NOTIFY---------->| | | |<-----200 OK-----------| | | | | | | |--INV bob--->| | | | | |--INV bob->| | | | |<-100------| | | | |<-----PUBLISH(alerting)---------------| | |------200 OK------------------------->| | |------NOTIFY---------->| | | |<-----200 OK-----------| | | | | | | | |<-180 ring-| | | |<--180 ring--| | | | | | | | | | | | | | | |<-200 OK---| | | |<--200 OK----| | | | | |<-----PUBLISH(active)-----------------| | |------200 OK------------------------->| | |------NOTIFY---------->| | | |<-----200 OK-----------| | | | | | | ]]> The watcher subscribes the "Event: dialog" of Bob. Alice calls Bob. The publisher is publishing the "alerting" state for Bob. PUBLISH is received and handled by presence module. Presence module updates the "presentity". Presence module checks for active watchers of the presentity. The active watcher is notified via a NOTIFY SIP request. Bob answers the call. The publisher is publishing the "active" state for Bob. PUBLISH is received and handled by presence module. Presence module updates the "presentity". Presence module checks for active watchers of the presentity. The active watcher is notified via a NOTIFY SIP request.
Internal publishing In this mode, the module requires the "dialog" module to be loaded into OpenSIPS. All the publishing will be automatically done (the modules will exchange data directly via C API). From presence perspective, the OpenSIPS script must be configured to handle the SUBSCRIBE requests only (there is no need for PUBLISH handling as there is no SIP publishing in this mode). So be sure to use the "handle_subscribe()" function (from presence module) in the script. To trigger the internal publishing (from the dialog module) for a certain call, use the "sca_set_calling_line()" or "sca_set_called_line()" functions from the script when handling a new call. These functions will do all the work (creating dialog, setting the internal publishing, etc) - you just need to use them and eventually specify the name of the line (if other then the one from the SIP INVITE) - see the below documentation. LIMITATIONS : in this mode, the module does not really check if the line exists or not (like defined) - it blindly trust the traffic; also there is no check on how many indexes are for each line. Such information (lines and indexes) are not provisioned into the module, but the module will dynamically accept and handle any line and index based on the SIP traffic.
Dependencies
&osips; Modules The following modules must be loaded before this module: presence. dialog.
External Libraries or Applications None.
Exported Parameters
<varname>call_info_timeout_notification</varname> (int) Enables or disables call_info event timeout notifications. Default value is 1 (enabled). Set <varname>call_info_timeout_notification</varname> parameter ... modparam("presence_callinfo", "call_info_timeout_notification", 0) ...
<varname>line_seize_timeout_notification</varname> (int) Enables or disables line_seize event timeout notifications. Default value is 0 (disabled). Set <varname>line_seize_timeout_notification</varname> parameter ... modparam("presence_callinfo", "line_seize_timeout_notification", 1) ...
<varname>disable_dialog_support_for_sca</varname> (int) Disables the internal publishing of the "call-info" events (generated by the dialog module). The publishing is expected to be done via SIP PUBLISH from a third-party. See the wroking mode described in the beginning of this document. Default value is 0 (not disabled). Set <varname>disable_dialog_support_for_sca</varname> parameter ... modparam("presence_callinfo", "disable_dialog_support_for_sca", 1) ...
<varname>line_hash_size</varname> (int) Allows you to controll the size of the internal hash table used for storing the information about the lines and indexes (in the internal publishing mode). The value must be a power of 2. You may consider increasing the value if using a large set of lines (>1000). Default value is 64. Set <varname>line_hash_size</varname> parameter ... modparam("presence_callinfo", "line_hash_size", 128) ...
Exported Functions
<function moreinfo="none">sca_set_calling_line([line])</function> The function (to be used only in internal publishing mode) is setting for the current new call (initinal INVITE) the outbound line - the line used for calling out. If no parameter is provided, the name of the line is taken from the SIP FROM header of the INVITE. You can override that by providing the name of the line a parameter - be careful as the value must be a SIP URI ! Variables are accepted. This function can be used from REQUEST_ROUTE. <function>sca_set_calling_line()</function> usage ... if (is_method("INVITE") and !has_totag()) { sca_set_calling_line(); } ...
<function moreinfo="none">sca_set_called_line([line])</function> The function (to be used only in internal publishing mode) is setting for the current new call (initinal INVITE) the inbound line - the line the call was received on. If no parameter is provided, the name of the line is taken from the SIP RURI of the INVITE. You can override that by providing the name of the line a parameter - be careful as the value must be a SIP URI ! Variables are accepted. This function can be used from REQUEST_ROUTE. <function>sca_set_called_line()</function> usage ... if (is_method("INVITE") and !has_totag()) { sca_set_called_line(); } ...
opensips-2.2.2/modules/presence_callinfo/presence_callinfo.c000066400000000000000000000151751300170765700243360ustar00rootroot00000000000000/* * presence_callinfo module - Presence Handling of call-info events * * Copyright (C) 2010 Ovidiu Sas * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-03-11 initial version (osas) * 2010-07-13 added support for SCA Broadsoft with dialog module (bogdan) */ #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../str.h" #include "../../mod_fix.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../mem/mem.h" #include "../presence/bind_presence.h" #include "add_events.h" #include "sca_hash.h" #include "sca_dialog.h" int call_info_timeout_notification = 1; int line_seize_timeout_notification = 0; int no_dialog_support = 0; static int hash_size = 64; /* external API's */ presence_api_t pres; /* module functions */ static int mod_init(void); static int child_init(int); static void destroy(void); int sca_set_calling_line(struct sip_msg *msg, char *line_var); int sca_set_called_line(struct sip_msg *msg, char *line_var); /* module exported commands */ static cmd_export_t cmds[] = { {"sca_set_calling_line", (cmd_function)sca_set_calling_line, 0, NULL, NULL, REQUEST_ROUTE }, {"sca_set_calling_line", (cmd_function)sca_set_calling_line, 1, fixup_pvar_null, fixup_free_pvar_null, REQUEST_ROUTE }, {"sca_set_called_line", (cmd_function)sca_set_called_line, 0, NULL, NULL, REQUEST_ROUTE }, {"sca_set_called_line", (cmd_function)sca_set_called_line, 1, fixup_pvar_null, fixup_free_pvar_null, REQUEST_ROUTE }, {0, 0, 0, 0, 0, 0} }; /* module exported paramaters */ static param_export_t params[] = { {"line_hash_size", INT_PARAM, &hash_size}, {"disable_dialog_support_for_sca", INT_PARAM, &no_dialog_support}, {"call_info_timeout_notification", INT_PARAM, &call_info_timeout_notification}, {"line_seize_timeout_notification", INT_PARAM, &line_seize_timeout_notification}, {0, 0, 0} }; static module_dependency_t *get_deps_dialog_support(param_export_t *param) { int no_dialog_support = *(int *)param->param_pointer; if (no_dialog_support) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "dialog", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "presence", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "disable_dialog_support_for_sca", get_deps_dialog_support }, { NULL, NULL }, }, }; /* module exports */ struct module_exports exports= { "presence_callinfo", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /* * init module function */ static int mod_init(void) { bind_presence_t bind_presence; LM_INFO("initializing...\n"); /* bind to presence module */ bind_presence= (bind_presence_t)find_export("bind_presence", 1,0); if (!bind_presence) { LM_ERR("can't bind presence\n"); return -1; } if (bind_presence(&pres) < 0) { LM_ERR("can't bind pua\n"); return -1; } if (pres.add_event == NULL) { LM_ERR("could not import add_event\n"); return -1; } if(callinfo_add_events() < 0) { LM_ERR("failed to add call-info events\n"); return -1; } if (no_dialog_support==0) { /* bind to the dialog API */ if (init_dialog_support()<0 ) { LM_ERR("failed to enable the dialog support\n"); return -1; } /* init internal hash table to keep the SCA/lines status */ if ( init_sca_hash(hash_size) < 0 ) { LM_ERR("failed to init hash table for SCA lines\n"); return -1; } } return 0; } static int child_init(int rank) { return 0; } static void destroy(void) { LM_DBG("destroying module ...\n"); if (no_dialog_support==0) destroy_sca_hash(); return; } int sca_set_calling_line(struct sip_msg *msg, char *line_var) { pv_value_t value; str line; if (no_dialog_support) { LM_ERR("dialog support is disabled, cannot use this function\n"); return -1; } if (msg->REQ_METHOD != METHOD_INVITE) return 1; /* get the name of line first */ if (line_var) { /* take it from param */ if ( pv_get_spec_value( msg, (pv_spec_p)line_var, &value) < 0 ) { LM_ERR("failed to evaluate parameter\n"); return -1; } if ( (value.flags&PV_VAL_STR)==0 ) { LM_ERR("line value is not a string (flags are %d)\n",value.flags); return -1; } line = value.rs; } else { /* take it from FROM msg */ if (parse_from_header(msg) < 0 ) { LM_ERR("failed to extract FROM URI\n"); return -1; } line = get_from(msg)->uri; } return sca_set_line(msg, &line, 1/*calling*/); } int sca_set_called_line(struct sip_msg *msg, char *line_var) { pv_value_t value; str line; if (no_dialog_support) { LM_ERR("dialog support is disabled, cannot use this function\n"); return -1; } if (msg->REQ_METHOD != METHOD_INVITE) return 1; /* get the name of line first */ if (line_var) { /* take it from param */ if ( pv_get_spec_value( msg, (pv_spec_p)line_var, &value) < 0 ) { LM_ERR("failed to evaluate parameter\n"); return -1; } if ( (value.flags&PV_VAL_STR)==0 ) { LM_ERR("line value is not a string (flags are %d)\n",value.flags); return -1; } line = value.rs; } else { /* take it from RURI msg */ line = *GET_RURI(msg); } return sca_set_line(msg, &line, 0/*called*/); } opensips-2.2.2/modules/presence_callinfo/presence_callinfo.h000066400000000000000000000023661300170765700243410ustar00rootroot00000000000000/* * presence_callinfo module - presence_callinfo header file * * Copyright (C) 2010 Ovidiu Sas * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-03-11 initial version (osas) * 2010-07-13 added support for SCA Broadsoft with dialog module (bogdan) */ #ifndef _PRES_CALLINFO_H_ #define _PRES_CALLINFO_H_ #include "../presence/bind_presence.h" /* presence API */ extern presence_api_t pres; /* if dialog support is on or off */ extern int no_dialog_support; #endif opensips-2.2.2/modules/presence_callinfo/sca_dialog.c000066400000000000000000000115571300170765700227500ustar00rootroot00000000000000/* * Add "call-info" event to presence module * * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-07-13 added support for SCA Broadsoft with dialog module (bogdan) */ #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_call_info.h" #include "../dialog/dlg_load.h" #include "sca_dialog.h" #include "add_events.h" static struct dlg_binds dlgf; static str calling_line_Dvar = {"PCI_calling_line",16}; static str called_line_Dvar = {"PCI_called_line", 15}; int init_dialog_support(void) { if (load_dlg_api(&dlgf)!=0) { LM_ERR("failed to find dialog API - is dialog module loaded?\n"); return -1; } return 0; } static void sca_dialog_callback(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { str calling_line = {NULL,0}; str called_line = {NULL,0}; struct sca_line *line=NULL; int idx; int state; /* search the lines */ if ( dlgf.fetch_dlg_value(dlg, &calling_line_Dvar, &calling_line, 1)==0 || calling_line.s!=NULL) { LM_DBG("calling line <%.*s> found \n",calling_line.len,calling_line.s); /* search without auto create */ line = get_sca_line( &calling_line, 0); } else if ( dlgf.fetch_dlg_value(dlg, &called_line_Dvar, &called_line, 1)==0 || called_line.s!=NULL) { LM_DBG("called line <%.*s> found \n",called_line.len,called_line.s); /* search without auto create */ line = get_sca_line( &called_line, 0); } if (line==NULL) { LM_ERR("could not found the line in dialog callback :( \n"); return; } /* careful now, the line is LOCKED !! */ /* get the index and the new state */ idx = (int)(long)(*(_params->param)); switch (type) { case DLGCB_FAILED: case DLGCB_TERMINATED: case DLGCB_EXPIRED: state = SCA_STATE_IDLE; break; case DLGCB_EARLY: state = calling_line.len?SCA_STATE_PROGRESSING:SCA_STATE_ALERTING; break; case DLGCB_CONFIRMED: state = SCA_STATE_ACTIVE; break; default: LM_CRIT("BUG: unsupported callback type %d \n",type); unlock_sca_line(line); return; } /* everything ok, change the state of the line and notify */ set_sca_index_state( line, idx, state); do_callinfo_publish( line ); /* now the line is unlocked */ return; } int sca_set_line(struct sip_msg *msg, str *line_s, int calling) { struct dlg_cell *dlg; unsigned int idx; struct sca_line *line; /* extract the index from the call-info line */ if ( parse_call_info_header( msg )!=0 ) { LM_ERR("missing or bogus Call-Info header in INVITE\n"); return -1; } idx = get_appearance_index(msg); if (idx==0) { LM_ERR("failed to extract line index from Call-Info hdr\n"); return -1; } LM_DBG("looking for line <%.*s>, idx %d, calling %d \n", line_s->len, line_s->s, idx, calling); /* search for the line (with no creation) */ line = get_sca_line( line_s, 0); if (line==NULL) { LM_ERR("used line <%.*s> not found in hash. Using without seizing?\n", line_s->len, line_s->s); return -1; } /* NOTE: the line is now locked !!!!! */ /* check if the index is seized */ if (calling) { if (line->seize_state!=idx) { LM_ERR("line not seized or seized for other index " "(idx=%d,seize=%d)\n",idx,line->seize_state); goto error; } } /* create and bind to the dialog */ if (dlgf.create_dlg(msg,0)< 0) { LM_ERR("failed to create dialog\n"); goto error; } dlg = dlgf.get_dlg(); LM_DBG("INVITE dialog created: using line <%.*s>\n", line_s->len, line_s->s); /* store the line variable into dialog */ if (calling) { if(dlgf.store_dlg_value(dlg, &calling_line_Dvar, line_s)< 0) { LM_ERR("Failed to store calling line\n"); goto error; } } else { if(dlgf.store_dlg_value(dlg, &called_line_Dvar, line_s)< 0) { LM_ERR("Failed to store called line\n"); goto error; } } /* register callbacks */ if (dlgf.register_dlgcb( dlg, DLGCB_FAILED| DLGCB_CONFIRMED | DLGCB_TERMINATED | DLGCB_EXPIRED | DLGCB_EARLY , sca_dialog_callback, (void*)(long)idx, 0) != 0) { LM_ERR("cannot register callbacks for dialog\n"); goto error; } /* STILL LOCKED HERE !! */ terminate_line_sieze(line); /* lock released by above function */ return 1; error: unlock_sca_line(line); return -1; } opensips-2.2.2/modules/presence_callinfo/sca_dialog.h000066400000000000000000000023331300170765700227450ustar00rootroot00000000000000/* * Add "call-info" event to presence module * * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-07-13 added support for SCA Broadsoft with dialog module (bogdan) */ #ifndef _H_PRESENCE_CALL_INFO_SCA_DIALOG #define _H_PRESENCE_CALL_INFO_SCA_DIALOG #include "../../str.h" #include "../../locking.h" #include "../../parser/msg_parser.h" int init_dialog_support(void); int sca_set_line(struct sip_msg *msg, str *line, int calling); #endif opensips-2.2.2/modules/presence_callinfo/sca_hash.c000066400000000000000000000204411300170765700224240ustar00rootroot00000000000000/* * Add "call-info" event to presence module * * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-07-13 added support for SCA Broadsoft with dialog module (bogdan) */ #include "../../dprint.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../hash_func.h" #include "../../parser/parse_uri.h" #include "sca_hash.h" #include "add_events.h" static struct sca_hash *sca_table = NULL; #define sca_lock(_entry) \ lock_set_get( sca_table->locks, sca_table->entries[_entry].lock_idx) #define sca_unlock(_entry) \ lock_set_release( sca_table->locks, sca_table->entries[_entry].lock_idx) #define sca_hash(_line) core_hash(_line, 0, sca_table->size) int init_sca_hash(int size) { int n; /* check/ajust the size of the hash table */ for( n=0 ; n<(8*sizeof(n)) ; n++) { if (size==(1< rounding from %d to %d\n", size, 1<<(n-1)); size = 1<<(n-1); break; } } /* allocate the hash table + entries */ sca_table = (struct sca_hash*)shm_malloc ( sizeof(struct sca_hash) + size*sizeof(struct sca_entry)); if (sca_table==0) { LM_ERR("no more shm mem for SCA hash table\n"); goto error0; } memset( sca_table, 0, sizeof(struct sca_hash) + size*sizeof(struct sca_entry) ); sca_table->size = size; sca_table->entries = (struct sca_entry*)(sca_table+1); /* calculate how many locks we can get */ n = (size=MIN_SCA_LOCKS ; n-- ) { sca_table->locks = lock_set_alloc(n); if (sca_table->locks==0) continue; if (lock_set_init(sca_table->locks)==0) { lock_set_dealloc(sca_table->locks); sca_table->locks = 0; continue; } sca_table->locks_no = n; break; } if (sca_table->locks==0) { LM_ERR("unable to allocted at least %d locks for the hash table\n", MIN_SCA_LOCKS); goto error1; } /* distribute the locks over all entries */ for( n=0 ; nentries[n].lock_idx = n % sca_table->locks_no; return 0; error1: shm_free( sca_table ); error0: return -1; } /* * Creates new SCA structure, adds into hash table * Assumes hash entry is locked !!!! */ static struct sca_line* create_sca_line(str *line, unsigned int hash) { struct sca_line *scal; struct sip_uri puri; /* parse the URI line */ if ( parse_uri( line->s, line->len, &puri)<0 ) { LM_ERR("failed to parse URI line <%.*s> \n", line->len, line->s); return NULL; } scal = (struct sca_line *)shm_malloc( sizeof(struct sca_line) + line->len + MD5_LEN ); if (scal==NULL) { LM_ERR("no more shm - failed to allocate new SCA structure\n"); return NULL; } memset( scal, 0, sizeof(struct sca_line)); scal->hash = hash; /* name of the line */ scal->line.s = (char*)(scal+1); scal->line.len = line->len; memcpy( scal->line.s, line->s, line->len); /* user anf host, just as pointers */ scal->user.s = scal->line.s + (puri.user.s - line->s); scal->user.len = puri.user.len; scal->domain.s = scal->line.s + (puri.host.s - line->s); scal->domain.len = puri.host.len; /* etag space */ scal->etag.s = scal->line.s + scal->line.len; scal->etag.len = 0; /* insert into hash */ if (sca_table->entries[hash].first!=NULL) { scal->next = sca_table->entries[hash].first; scal->next->prev = scal; } sca_table->entries[hash].first = scal; return scal; } /* * Searches for an SCA by name ; if found, it will returned with the lock taken !! */ struct sca_line* get_sca_line(str *line, int create) { unsigned int hash; struct sca_line *scal; hash = sca_hash( line ); sca_lock(hash); /* search */ for( scal=sca_table->entries[hash].first ; scal ; scal=scal->next ) { if ( (scal->line.len==line->len) && (memcmp(scal->line.s, line->s , line->len)==0) ) return scal; } /* not found */ if (create==0) { sca_unlock(hash); return NULL; } /* create */ scal = create_sca_line(line, hash); if (scal==NULL) { LM_ERR("failed to create new SCA record\n"); sca_unlock(hash); return NULL; } return scal; } /* * sets a new state for an index - it assumes the line is locked */ int set_sca_index_state(struct sca_line *line, unsigned int idx, unsigned int state) { struct sca_idx *scai; struct sca_idx *prev; /* search for the index */ for( scai=line->indexes,prev=NULL ; scai ; prev=scai,scai=scai->next) if (scai->idx>=idx) break; /* if not found, add it to the right position */ if (scai==NULL || scai->idx!=idx) { scai = (struct sca_idx*)shm_malloc(sizeof(struct sca_idx)); if (scai==NULL) { LM_ERR("not enough shm mem for a new sca index\n"); return -1; } scai->idx = idx; /* insert it after prev */ if (prev==NULL) { scai->next = line->indexes; line->indexes = scai; } else { scai->next = prev->next; prev->next = scai; } } /* set the state */ scai->state = state; return 0; } char * sca_print_line_status(struct sca_line *line, int *l) { unsigned int len; struct sca_idx *scai; char *buf; char *p, *q; int n; len = CI_hdr_name_len + 1/*<*/ + line->line.len + 1 /*>*/ + 1/*;*/ + CI_hdr_AI_param_len + 2/* =* */ + 1/*;*/ + CI_hdr_AS_param_len + 15/* =idle */ + CRLF_LEN; for( scai=line->indexes ; scai ; scai=scai->next ) { if (scai->state!=SCA_STATE_IDLE) len += 1/*;*/ + CI_hdr_AI_param_len +1 + 3 /* =idx */ + 1/*;*/ + CI_hdr_AS_param_len + 1 + 3 /* =state */; } buf = (char *)pkg_malloc(len); if (buf==NULL) { LM_ERR("no more mem (needed %d)\n",len); return NULL; } p = buf; memcpy( p, CI_hdr_name_s "<", CI_hdr_name_len+1); p += CI_hdr_name_len+1; memcpy( p, line->line.s, line->line.len); p += line->line.len; *(p++) = '>'; for( scai=line->indexes ; scai ; scai=scai->next ) { if (scai->state==SCA_STATE_IDLE) continue; memcpy( p, ";"CI_hdr_AI_param_s "=", CI_hdr_AI_param_len+2 ); p += CI_hdr_AI_param_len+2 ; q = int2str(scai->idx, &n); memcpy( p , q, n); p += n; memcpy( p, ";"CI_hdr_AS_param_s "=", CI_hdr_AS_param_len+2 ); p += CI_hdr_AS_param_len+2 ; switch (scai->state) { case SCA_STATE_SEIZED: memcpy( p, "seized", 6); p += 6 ; break; case SCA_STATE_PROGRESSING: memcpy( p, "progressing", 11); p += 11 ; break; case SCA_STATE_ALERTING: memcpy( p, "alerting", 8); p += 8 ; break; case SCA_STATE_ACTIVE: memcpy( p, "active", 6); p += 6 ; break; default: LM_ERR("unsupported state %d for index %d line %.*s\n", scai->state, scai->idx, line->line.len, line->line.s); pkg_free(buf); return NULL; } } /* add the idle state */ memcpy( p, ";"CI_hdr_AI_param_s "=*;" CI_hdr_AS_param_s "=idle" CRLF, 1+CI_hdr_AI_param_len+3+CI_hdr_AS_param_len+5+CRLF_LEN ); p += 1+CI_hdr_AI_param_len+3+CI_hdr_AS_param_len+5+CRLF_LEN ; *l = (int)(p-buf); if (p-buf>len) LM_ERR("BUG: allocated %d, wrote, %d\n",len,(int)(p-buf)); LM_DBG("hdr is <%.*s>",*l,buf); return buf; } void unlock_sca_line(struct sca_line *scal) { sca_unlock(scal->hash); } void free_sca_line(struct sca_line *scal) { struct sca_idx *idx,*tmp; /* free indexes */ for( idx=scal->indexes ; idx ; ) { tmp = idx; idx = idx->next; shm_free(tmp); } /* free main structure */ shm_free(scal); } void destroy_sca_hash(void) { struct sca_line *sline, *l_sline; unsigned int i; if (sca_table==NULL) return; if (sca_table->locks) { lock_set_destroy(sca_table->locks); lock_set_dealloc(sca_table->locks); } for( i=0 ; isize; i++ ) { sline = sca_table->entries[i].first; while (sline) { l_sline = sline; sline = sline->next; free_sca_line(l_sline); } } shm_free(sca_table); sca_table = NULL; } opensips-2.2.2/modules/presence_callinfo/sca_hash.h000066400000000000000000000042731300170765700224360ustar00rootroot00000000000000/* * Add "call-info" event to presence module * * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-07-13 added support for SCA Broadsoft with dialog module (bogdan) */ #ifndef _H_PRESENCE_CALL_INFO_SCA_HASH #define _H_PRESENCE_CALL_INFO_SCA_HASH #include "../../str.h" #include "../../locking.h" #define MAX_SCA_LOCKS 512 #define MIN_SCA_LOCKS 1 #define SCA_STATE_IDLE 1 #define SCA_STATE_SEIZED 2 #define SCA_STATE_PROGRESSING 3 #define SCA_STATE_ALERTING 4 #define SCA_STATE_ACTIVE 5 struct sca_idx { unsigned int idx; unsigned int state; struct sca_idx *next; }; struct sca_line { /* name of the line */ str line; str user; str domain; str etag; /* seizing info */ unsigned int seize_state; unsigned int seize_expires; /* index info */ struct sca_idx *indexes; /* linking in the sca_hash */ unsigned int hash; struct sca_line *prev; struct sca_line *next; }; struct sca_entry { struct sca_line *first; unsigned int lock_idx; }; struct sca_hash { unsigned int size; struct sca_entry *entries; unsigned int locks_no; gen_lock_set_t *locks; }; int init_sca_hash(int size); struct sca_line* get_sca_line(str *line, int create); void unlock_sca_line(struct sca_line *scal); int set_sca_index_state(struct sca_line *line, unsigned int idx, unsigned int state); char * sca_print_line_status(struct sca_line *line, int *l); void destroy_sca_hash(); #endif opensips-2.2.2/modules/presence_dialoginfo/000077500000000000000000000000001300170765700210325ustar00rootroot00000000000000opensips-2.2.2/modules/presence_dialoginfo/Makefile000066400000000000000000000010451300170765700224720ustar00rootroot00000000000000# # Presence_MWI Makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=presence_dialoginfo.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/presence_dialoginfo/README000066400000000000000000000175311300170765700217210ustar00rootroot00000000000000presence_dialoginfo Module Juha Heinanen Klaus Darilion Edited by Juha Heinanen Edited by Klaus Darilion Copyright © 2007 Juha Heinanen Copyright © 2008 Klaus Darilion, IPCom (Module implementation was partly sponsored by Silver Server (www.sil.at)) Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. force_single_dialog (int) 1.4. Exported Functions List of Examples 1.1. Set parameter Chapter 1. Admin Guide 1.1. Overview The module enables the handling of "Event: dialog" (as defined in RFC 4235) inside of the presence module. This can be used distribute the dialog-info status to the subscribed watchers. The module does not currently implement any authorization rules. It assumes that publish requests are only issued by an authorized application and subscribe requests only by authorized users. Authorization can thus be easily done in OpenSIPS configuration file before calling handle_publish() and handle_subscribe() functions. Note: This module only activates the processing of the "dialog" in the presence module. To send dialog-info to watchers you also need a source which PUBLISH the dialog info to the presence module. For example you can use the pua_dialoginfo module or any external component. This approach allows to have the presence server and the dialog-info aware publisher (e.g. the main proxy) on different OpenSIPS instances. This module by default does body aggregation. That means, if the presence module received PUBLISH from multiple presentities (e.g. if the entity has multiple dialogs the pua_dialoginfo will send multiple PUBLISH), the module will parse all the received (and still valid, depending on the Expires header in the PUBLISH request) XML documents and generate a single XML document with multiple "dialog" elements. This is perfectly valid, but unfortunately not supported by all SIP phones, e.g. Linksys SPA962 crashes when it receives dialog-info with multiple dialog elements. In this case use the force_single_dialog module parameter. To get better understanding how all the module works together please take a look at the follwing figure: Main Proxy and Presence Server on the same Instance caller proxy & callee watcher alice@example presence bob@example watcher@example server | | | | | |<-------SUBSCRIBE bob-------| | |--------200 OK------------->| | |--------NOTIFY------------->| | |<-------200 OK--------------| | | | | |--INV bob--->| | | | |--INV bob-->| | | |<-100-------| | | | | | | |<-180 ring--| | |<--180 ring--| | | | |-- | | | | \ | | | | PUBLISH bob| | | | / | | | |<- | | | | | | | |-- | | | | \ | | | | 200 ok | | | | / | | | |<- | | | |--------NOTIFY------------->| | |<-------200 OK--------------| | | | | * The watcher subscribes the "Event: dialog" of Bob. * Alice calls Bob. * Bob replies with ringing, the dialog in the dialog module transits to "early". The callback in pua_dialoginfo is executed. The pua_dialoginfo module creates the XML document and uses the pua module to send the PUBLISH. (pua module itself uses tm module to send the PUBLISH stateful) * PUBLISH is received and handled by presence module. Presence module updates the "presentity". Presence module checks for active watchers of the presentity. It gives all the XML dcouments to presence_dialoginfo module to aggregate them into a single XML document. Then it sends the NOTIFY with the aggregated XML document to all active watchers. The presence server can also be separated from the main proxy by using a separate OpenSIPS instance as shown in the following figure. (Either set the outbound_proxy parameter of pua module or make sure to route the "looped" PUBLISH requests from the main proxy to the presence server). Main Proxy and Presence Server use a separate Instance caller proxy & presence callee watcher alice@example server server bob@example watcher@example | | | | | | |<--------------------SUBSCRIBE bob-------| | |-SUBSC bob->| | | | |<-200 ok----| | | | |---------------------200 OK------------->| | | .... NOTIFY ... 200 OK ... | | | | | | | | | | | |--INV bob--->| | | | | |--INV bob------------------>| | | |<-100-----------------------| | | | | | | | |<-180 ring------------------| | |<--180 ring--| | | | | |--PUBL bob->| | | | |<-200 ok----| | | | | |--------NOTIFY------------->| | | |<-------200 OK--------------| | | | | | Known issues: * The "version" attribute is increased for every NOTIFY, even if the XML document has not changed. This is of course valid, but not very smart. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * presence. 1.2.2. External Libraries or Applications None. 1.3. Exported Parameters 1.3.1. force_single_dialog (int) By default the module aggregates all available dialog info into a single dialog-info document containing multiple "dialog" elements. If the phone does not support this, you can activate this parameter. If this parameter is set, only the dialog element with the currently most interesting dialog state will be put into the dialog-info document. Thus, the dialog-info element will contain only a single "dialog" element. The algorithm chooses the state based onf the following order of priority (least important first): terminated, trying, proceeding, confirmed, early. Note: I consider the "early" state more intersting than confirmed as often you might want to pickup a call if the originall callee is already busy in a call. Default value is “0â€. Example 1.1. Set parameter ... modparam("presence_dialoginfo", "force_single_dialog", 1) ... 1.4. Exported Functions None to be used in configuration file. opensips-2.2.2/modules/presence_dialoginfo/add_events.c000066400000000000000000000041711300170765700233150ustar00rootroot00000000000000/* * Add "dialog" event to presence module * * Copyright (C) 2007 Juha Heinanen * Copyright (C) 2008 Klaus Darilion, IPCom * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-08-25 initial version (kd) */ #include #include #include #include "../../parser/parse_content.h" #include "../presence/event_list.h" #include "../presence/presentity.h" #include "presence_dialoginfo.h" #include "notify_body.h" int dlginfo_add_events(void) { pres_ev_t event; /* constructing message-summary event */ memset(&event, 0, sizeof(pres_ev_t)); event.name.s = "dialog"; event.name.len = 6; event.content_type.s = "application/dialog-info+xml"; event.content_type.len = 27; event.default_expires= 3600; event.mandatory_body = 1; event.mandatory_timeout_notification = 1; event.type = PUBL_TYPE; event.req_auth = 0; event.evs_publ_handl = 0; /* aggregate XML body and free() fuction */ event.agg_nbody = dlginfo_agg_nbody; event.free_body = free_xml_body; /* register the dummy dialoginfo body builder */ event.build_empty_pres_info = build_empty_dialoginfo; /* modify XML body for each watcher to set the correct "version" */ event.aux_body_processing = dlginfo_body_setversion; if (pres_add_event(&event) < 0) { LM_ERR("failed to add event \"dialog\"\n"); return -1; } return 0; } opensips-2.2.2/modules/presence_dialoginfo/add_events.h000066400000000000000000000020551300170765700233210ustar00rootroot00000000000000/* * presence_dialoginfo module - add_event header file * * Copyright (C) 2007 Juha Heinanen * Copyright (C) 2008 Klaus Darilion, IPCom * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-08-25 initial version (kd) */ #ifndef _DLGINFO_ADD_EV_H_ #define _DLGINFO_ADD_EV_H_ int dlginfo_add_events(void); #endif opensips-2.2.2/modules/presence_dialoginfo/doc/000077500000000000000000000000001300170765700215775ustar00rootroot00000000000000opensips-2.2.2/modules/presence_dialoginfo/doc/presence_dialoginfo.xml000066400000000000000000000030321300170765700263160ustar00rootroot00000000000000 %docentities; ]> presence_dialoginfo Module &osips; Juha Heinanen jh@tutpro.com Klaus Darilion klaus.darilion@mailinglists@pernau.at Juha Heinanen jh@tutpro.com Klaus Darilion klaus.darilion@mailinglists@pernau.at 2007 Juha Heinanen 2008 Klaus Darilion, IPCom (Module implementation was partly sponsored by Silver Server (www.sil.at)) $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/presence_dialoginfo/doc/presence_dialoginfo_admin.xml000066400000000000000000000204151300170765700274720ustar00rootroot00000000000000 &adminguide;
Overview The module enables the handling of "Event: dialog" (as defined in RFC 4235) inside of the presence module. This can be used distribute the dialog-info status to the subscribed watchers. The module does not currently implement any authorization rules. It assumes that publish requests are only issued by an authorized application and subscribe requests only by authorized users. Authorization can thus be easily done in &osips; configuration file before calling handle_publish() and handle_subscribe() functions. Note: This module only activates the processing of the "dialog" in the presence module. To send dialog-info to watchers you also need a source which PUBLISH the dialog info to the presence module. For example you can use the pua_dialoginfo module or any external component. This approach allows to have the presence server and the dialog-info aware publisher (e.g. the main proxy) on different &osips; instances. This module by default does body aggregation. That means, if the presence module received PUBLISH from multiple presentities (e.g. if the entity has multiple dialogs the pua_dialoginfo will send multiple PUBLISH), the module will parse all the received (and still valid, depending on the Expires header in the PUBLISH request) XML documents and generate a single XML document with multiple "dialog" elements. This is perfectly valid, but unfortunately not supported by all SIP phones, e.g. Linksys SPA962 crashes when it receives dialog-info with multiple dialog elements. In this case use the force_single_dialog module parameter. To get better understanding how all the module works together please take a look at the follwing figure: | | |--------NOTIFY------------->| | |<-------200 OK--------------| | | | | |--INV bob--->| | | | |--INV bob-->| | | |<-100-------| | | | | | | |<-180 ring--| | |<--180 ring--| | | | |-- | | | | \ | | | | PUBLISH bob| | | | / | | | |<- | | | | | | | |-- | | | | \ | | | | 200 ok | | | | / | | | |<- | | | |--------NOTIFY------------->| | |<-------200 OK--------------| | | | | ]]> The watcher subscribes the "Event: dialog" of Bob. Alice calls Bob. Bob replies with ringing, the dialog in the dialog module transits to "early". The callback in pua_dialoginfo is executed. The pua_dialoginfo module creates the XML document and uses the pua module to send the PUBLISH. (pua module itself uses tm module to send the PUBLISH stateful) PUBLISH is received and handled by presence module. Presence module updates the "presentity". Presence module checks for active watchers of the presentity. It gives all the XML dcouments to presence_dialoginfo module to aggregate them into a single XML document. Then it sends the NOTIFY with the aggregated XML document to all active watchers. The presence server can also be separated from the main proxy by using a separate &osips; instance as shown in the following figure. (Either set the outbound_proxy parameter of pua module or make sure to route the "looped" PUBLISH requests from the main proxy to the presence server). | | | | |<-200 ok----| | | | |---------------------200 OK------------->| | | .... NOTIFY ... 200 OK ... | | | | | | | | | | | |--INV bob--->| | | | | |--INV bob------------------>| | | |<-100-----------------------| | | | | | | | |<-180 ring------------------| | |<--180 ring--| | | | | |--PUBL bob->| | | | |<-200 ok----| | | | | |--------NOTIFY------------->| | | |<-------200 OK--------------| | | | | | ]]> Known issues: The "version" attribute is increased for every NOTIFY, even if the XML document has not changed. This is of course valid, but not very smart.
Dependencies
&osips; Modules The following modules must be loaded before this module: presence.
External Libraries or Applications None.
Exported Parameters
<varname>force_single_dialog</varname> (int) By default the module aggregates all available dialog info into a single dialog-info document containing multiple "dialog" elements. If the phone does not support this, you can activate this parameter. If this parameter is set, only the dialog element with the currently most interesting dialog state will be put into the dialog-info document. Thus, the dialog-info element will contain only a single "dialog" element. The algorithm chooses the state based onf the following order of priority (least important first): terminated, trying, proceeding, confirmed, early. Note: I consider the "early" state more intersting than confirmed as often you might want to pickup a call if the originall callee is already busy in a call. Default value is 0. Set <varname></varname> parameter ... modparam("presence_dialoginfo", "force_single_dialog", 1) ...
Exported Functions None to be used in configuration file.
opensips-2.2.2/modules/presence_dialoginfo/notify_body.c000066400000000000000000000321631300170765700235300ustar00rootroot00000000000000/* * presence_dialoginfo module - * * Copyright (C) 2006 Voice Sistem S.R.L. * Copyright (C) 2008 Klaus Darilion, IPCom * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-08-25 initial version (kd) */ #define MAX_INT_LEN 11 /* 2^32: 10 chars + 1 char sign */ #include #include #include #include "../../mem/mem.h" #include "../presence/utils_func.h" #include "../presence/hash.h" #include "../presence/event_list.h" #include "../presence/presence.h" #include "../presence/presentity.h" #include "presence_dialoginfo.h" #include "notify_body.h" #include "pidf.h" str* agregate_xmls(str* pres_user, str* pres_domain, str** body_array, int n, int partial); str* build_dialoginfo(str* pres_user, str* pres_domain); extern int force_single_dialog; static str* _build_empty_dialoginfo(const char* pres_uri_char, str* extra_hdrs); #define VERSION_HOLDER "00000000000" void free_xml_body(char* body) { if(body) xmlFree(body); } /* Joins user and domain into "sip:USER@DOMAIN". * dst must fit at least MAX_URI_SIZE+1 characters! */ static inline int sipuri_cat(char* dst, const str* user, const str* domain) { if ((4 + user->len + 1 + domain->len) > MAX_URI_SIZE) { LM_ERR("entity URI too long, maximum=%d\n", MAX_URI_SIZE); return -1; } memcpy(dst, "sip:", 4); memcpy(dst + 4, user->s, user->len); dst[user->len + 4] = '@'; memcpy(dst + user->len + 5, domain->s, domain->len); dst[user->len + 5 + domain->len] = '\0'; return 0; } str* dlginfo_agg_nbody(str* pres_user, str* pres_domain, str** body_array, int n, int off_index) { str* n_body= NULL; char pres_uri_char[MAX_URI_SIZE+1]; if (sipuri_cat(pres_uri_char, pres_user, pres_domain) != 0) return NULL; LM_DBG("[pres_uri] %s (%d), [n]=%d\n", pres_uri_char, pres_user->len + 5 + pres_domain->len, n); if(body_array == NULL) return _build_empty_dialoginfo(pres_uri_char, NULL); if (n == -2) n_body= agregate_xmls(pres_user, pres_domain, body_array, 1, 1); else n_body= agregate_xmls(pres_user, pres_domain, body_array, n, 0); LM_DBG("[n_body]=%p\n", n_body); if(n_body) { LM_DBG("[*n_body]=%.*s\n", n_body->len, n_body->s); } if(n_body== NULL && n!= 0) { LM_ERR("while aggregating body\n"); } xmlCleanupParser(); xmlMemoryDump(); if (n_body== NULL) n_body = _build_empty_dialoginfo(pres_uri_char, NULL); return n_body; } str* agregate_xmls(str* pres_user, str* pres_domain, str** body_array, int n, int partial) { int i, j= 0; xmlDocPtr doc = NULL; xmlNodePtr root_node = NULL; xmlNsPtr namespace = NULL; xmlNodePtr p_root= NULL; xmlDocPtr* xml_array ; xmlNodePtr node = NULL; char *state; int winner_priority = -1, priority ; xmlNodePtr winner_dialog_node = NULL ; str *body= NULL; char buf[MAX_URI_SIZE+1]; LM_DBG("[pres_user]=%.*s [pres_domain]= %.*s, [n]=%d\n", pres_user->len, pres_user->s, pres_domain->len, pres_domain->s, n); xml_array = (xmlDocPtr*)pkg_malloc( n*sizeof(xmlDocPtr)); if(xml_array== NULL) { LM_ERR("while allocating memory"); return NULL; } memset(xml_array, 0, n*sizeof(xmlDocPtr)) ; /* parse all the XML documents */ for(i=0; is, body_array[i]->len ); /* LM_DBG("parsing XML body: [n]=%d, [i]=%d, [j]=%d xml_array[j]=%p\n", n, i, j, xml_array[j] ); */ if( xml_array[j]== NULL) { LM_ERR("while parsing xml body message\n"); goto error; } j++; } if(j== 0) /* no body */ { if(xml_array) pkg_free(xml_array); return NULL; } /* n: number of bodies in total */ /* j: number of useful bodies; created XML structures */ /* i: loop counter */ /* LM_DBG("number of bodies in total [n]=%d, number of useful bodies [j]=%d\n", n, j ); */ /* create the new NOTIFY body */ if (sipuri_cat(buf, pres_user, pres_domain) != 0) goto error; doc = xmlNewDoc(BAD_CAST "1.0"); if(doc==0) return NULL; root_node = xmlNewNode(NULL, BAD_CAST "dialog-info"); if(root_node==0) goto error; xmlDocSetRootElement(doc, root_node); namespace = xmlNewNs(root_node, BAD_CAST "urn:ietf:params:xml:ns:dialog-info", NULL); if (!namespace) { LM_ERR("creating namespace failed\n"); } xmlSetNs(root_node, namespace); /* The version must be increased for each new document and is a 32bit int. As the version is different for each watcher, we can not set here the correct value. Thus, we just put here a placeholder which will be replaced by the correct value in the aux_body_processing callback. Thus we have CPU intensive XML aggregation only once and can use quick search&replace in the per-watcher aux_body_processing callback. We use 11 chracters as an signed int (although RFC says unsigned int we use signed int as presence module stores "version" in DB as signed int) has max. 10 characters + 1 character for the sign */ xmlNewProp(root_node, BAD_CAST "version", BAD_CAST VERSION_HOLDER); xmlNewProp(root_node, BAD_CAST "entity", BAD_CAST buf); if (!partial) xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "full" ); else xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "partial" ); /* loop over all bodies and create the aggregated body */ for(i=0; ichildren) { for (node = p_root->children; node; node = node->next) { if (node->type == XML_ELEMENT_NODE) { LM_DBG("node type: Element, name: %s\n", node->name); /* we do not copy the node, but unlink it and then add it ot the new node * this destroys the original document but we do not need it anyway. * using "copy" instead of "unlink" would also copy the namespace which * would then be declared redundant (libxml unfortunately can not remove * namespaces) */ if (!force_single_dialog || (j==1)) { xmlUnlinkNode(node); if(xmlAddChild(root_node, node)== NULL) { LM_ERR("while adding child\n"); goto error; } } else { /* try to put only the most important into the XML document * order of importance: terminated->trying->proceeding->confirmed->early */ state = xmlNodeGetNodeContentByName(node, "state", NULL); if (state) { LM_DBG("state element content = %s\n", state); priority = get_dialog_state_priority(state); if (priority > winner_priority) { winner_priority = priority; LM_DBG("new winner priority = %s (%d)\n", state, winner_priority); winner_dialog_node = node; } xmlFree(state); } } } } } } if (force_single_dialog && (j!=1)) { xmlUnlinkNode(winner_dialog_node); if(xmlAddChild(root_node, winner_dialog_node)== NULL) { LM_ERR("while adding winner-child\n"); goto error; } } body = (str*)pkg_malloc(sizeof(str)); if(body == NULL) { ERR_MEM(PKG_MEM_STR); } xmlDocDumpMemory(doc,(xmlChar**)(void*)&body->s, &body->len); for(i=0; ilen < 41) { LM_ERR("body string too short!\n"); return NULL; } version_start = strstr(body->s + 34, "version="); if (!version_start) { LM_ERR("version string not found!\n"); return NULL; } version_start += 9; version_len = snprintf(version, MAX_INT_LEN + 2,"%d\"", subs->version); if (version_len >= MAX_INT_LEN + 2) { LM_ERR("failed to convert 'version' to string\n"); return NULL; } /* Replace the placeholder 00000000000 with the version. * Put the padding behind the "" */ LM_DBG("replace version with \"%s\n",version); memcpy(version_start, version, version_len); memset(version_start + version_len, ' ', MAX_INT_LEN + 2 - version_len); return NULL; } str* build_dialoginfo(str* pres_user, str* pres_domain) { xmlDocPtr doc = NULL; xmlNodePtr root_node = NULL; xmlNodePtr dialog_node = NULL; xmlNodePtr state_node = NULL; str *body= NULL; str pres_uri; char buf[MAX_URI_SIZE+1]; if (sipuri_cat(buf, pres_user, pres_domain) != 0) return NULL; pres_uri.s = buf; pres_uri.len = 4 + pres_user->len + 1 + pres_domain->len; LM_DBG("[pres_uri] %.*s\n", pres_uri.len, pres_uri.s); if (pres_contains_presence(&pres_uri) < 0) { LM_DBG("No record exists in hash_table\n"); goto error; } /* create the Publish body */ doc = xmlNewDoc(BAD_CAST "1.0"); if(doc==0) goto error; root_node = xmlNewNode(NULL, BAD_CAST "dialog-info"); if(root_node==0) goto error; xmlDocSetRootElement(doc, root_node); xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:dialog-info"); /* we set the version to 0 but it should be set to the correct value in the pua module */ xmlNewProp(root_node, BAD_CAST "version", BAD_CAST VERSION_HOLDER); xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "partial" ); xmlNewProp(root_node, BAD_CAST "entity", BAD_CAST buf); /* dialog tag */ dialog_node =xmlNewChild(root_node, NULL, BAD_CAST "dialog", NULL) ; if( dialog_node ==NULL) { LM_ERR("while adding child [dialog]\n"); goto error; } /* reuse buf for user-part only */ memcpy(buf, pres_user->s, pres_user->len); buf[pres_user->len] = '\0'; xmlNewProp(dialog_node, BAD_CAST "id", BAD_CAST buf); /* state tag */ state_node = xmlNewChild(dialog_node, NULL, BAD_CAST "state", BAD_CAST "terminated"); if( state_node ==NULL) { LM_ERR("while adding child [state]\n"); goto error; } /* create the body */ body = (str*)pkg_malloc(sizeof(str)); if(body == NULL) { LM_ERR("while allocating memory\n"); goto error; } memset(body, 0, sizeof(str)); xmlDocDumpMemory(doc,(unsigned char**)(void*)&body->s,&body->len); LM_DBG("new_body:\n%.*s\n",body->len, body->s); /*free the document */ xmlFreeDoc(doc); xmlCleanupParser(); return body; error: if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } if(doc) xmlFreeDoc(doc); return NULL; } static str* _build_empty_dialoginfo(const char* pres_uri_char, str* extra_hdrs) { str* nbody= 0; xmlDocPtr doc = NULL; xmlNodePtr node; nbody= (str*) pkg_malloc(sizeof(str)); if(nbody== NULL) { LM_ERR("No more memory\n"); return 0; } doc = xmlNewDoc(BAD_CAST "1.0"); if(doc == NULL) { LM_ERR("Failed to create new xml document\n"); goto error; } node = xmlNewNode(0, BAD_CAST "dialog-info"); if(node == NULL) { LM_ERR("Failed to create new xml node\n"); goto error; } xmlDocSetRootElement(doc, node); xmlNewProp(node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:dialog-info"); xmlNewProp(node, BAD_CAST "version", BAD_CAST VERSION_HOLDER); xmlNewProp(node, BAD_CAST "state", BAD_CAST "full"); xmlNewProp(node, BAD_CAST "entity", BAD_CAST pres_uri_char); xmlDocDumpMemory(doc,(xmlChar**)(void*)&nbody->s, &nbody->len); xmlFreeDoc(doc); xmlCleanupParser(); xmlMemoryDump(); return nbody; error: if(doc) xmlFreeDoc(doc); if(nbody) pkg_free(nbody); return 0; } str* build_empty_dialoginfo(str* pres_uri, str* extra_hdrs) { char* pres_uri_char; str* ret; pres_uri_char = (char*)pkg_malloc(pres_uri->len + 1); if(pres_uri_char == NULL) { LM_ERR("No more memory\n"); return NULL; } memcpy(pres_uri_char, pres_uri->s, pres_uri->len); pres_uri_char[pres_uri->len] = '\0'; /* do the call with a null-terminated pres_uri */ ret = _build_empty_dialoginfo(pres_uri_char, extra_hdrs); pkg_free(pres_uri_char); return ret; } opensips-2.2.2/modules/presence_dialoginfo/notify_body.h000066400000000000000000000024201300170765700235260ustar00rootroot00000000000000/* * presence_dialoginfo module * * Copyright (C) 2006 Voice Sistem S.R.L. * Copyright (C) 2008 Klaus Darilion, IPCom * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-08-25 initial version (kd) */ #ifndef _NBODY_H_ #define _NBODY_H_ str* dlginfo_agg_nbody(str* pres_user, str* pres_domain, str** body_array, int n, int off_index); str* dlginfo_body_setversion(subs_t *subs, str* body); void free_xml_body(char* body); int get_dialog_state_priority(char *state); str* build_empty_dialoginfo(str* pres_uri, str* extra_hdrs); #endif opensips-2.2.2/modules/presence_dialoginfo/pidf.c000066400000000000000000000077221300170765700221300ustar00rootroot00000000000000/* * presence_dialoginfo module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-14 initial version (anca) */ /** * make strptime available * use 600 for 'Single UNIX Specification, Version 3' * _XOPEN_SOURCE creates conflict in header definitions in Solaris */ #ifndef __OS_solaris #define _XOPEN_SOURCE 600 /* glibc2 on linux, bsd */ #else #define _XOPEN_SOURCE_EXTENDED 1 /* solaris */ #endif #include #undef _XOPEN_SOURCE #undef _XOPEN_SOURCE_EXTENDED #include #include #include #include "../../dprint.h" #include "../../sr_module.h" #include "pidf.h" xmlAttrPtr xmlNodeGetAttrByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = node->properties; while (attr) { if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0) return attr; attr = attr->next; } return NULL; } char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = xmlNodeGetAttrByName(node, name); if (attr) return (char*)xmlNodeGetContent(attr->children); else return NULL; } xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name) { xmlNodePtr cur = node->children; while (cur) { if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) return cur; cur = cur->next; } return NULL; } xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns) { xmlNodePtr cur = node; while (cur) { xmlNodePtr match = NULL; if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) { if (!ns || (cur->ns && xmlStrcasecmp(cur->ns->prefix, (unsigned char*)ns) == 0)) return cur; } match = xmlNodeGetNodeByName(cur->children, name, ns); if (match) return match; cur = cur->next; } return NULL; } char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns) { xmlNodePtr node = xmlNodeGetNodeByName(root, name, ns); if (node) return (char*)xmlNodeGetContent(node->children); else return NULL; } xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns) { xmlNodePtr cur = doc->children; return xmlNodeGetNodeByName(cur, name, ns); } char *xmlDocGetNodeContentByName(xmlDocPtr doc, const char *name, const char *ns) { xmlNodePtr node = xmlDocGetNodeByName(doc, name, ns); if (node) return (char*)xmlNodeGetContent(node->children); else return NULL; } time_t xml_parse_dateTime(char* xml_time_str) { struct tm tm; char * p; int h, m; char h1, h2, m1, m2; int sign= 1; signed int timezone_diff= 0; p= strptime(xml_time_str, "%F", &tm); if(p== NULL) { printf("error: failed to parse time\n"); return 0; } p++; p= strptime(p, "%T", &tm); if(p== NULL) { printf("error: failed to parse time\n"); return 0; } if(*p== '\0') goto done; if(*p== '.') { p++; /* read the fractionar part of the seconds*/ while(*p!= '\0' && *p>= '0' && *p<= '9') { p++; } } if(*p== '\0') goto done; /* read time zone */ if(*p== 'Z') { goto done; } if(*p== '+') sign= -1; p++; sscanf(p, "%c%c:%c%c", &h1, &h2, &m1, &m2); h= (h1- '0')*10+ h2- '0'; m= (m1- '0')*10+ m2- '0'; timezone_diff= sign* ((m+ h* 60)* 60); done: return (mktime(&tm) + timezone_diff); } opensips-2.2.2/modules/presence_dialoginfo/pidf.h000066400000000000000000000026631300170765700221340ustar00rootroot00000000000000/* * presence_dialoginfo module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (anca) */ #ifndef PIDF_H #define PIDF_H #include "../../str.h" #include xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns); xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns); xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name); char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns); char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name); time_t xml_parse_dateTime(char* xml_time_str); #endif opensips-2.2.2/modules/presence_dialoginfo/presence_dialoginfo.c000066400000000000000000000066621300170765700252070ustar00rootroot00000000000000/* * presence_dialoginfo module - Presence Handling of dialog events * * Copyright (C) 2007 Juha Heinanen * Copyright (C) 2008 Klaus Darilion, IPCom * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-08-25 initial version (kd) */ #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../str.h" #include "../../parser/msg_parser.h" #include "../../mem/mem.h" #include "../presence/bind_presence.h" #include "add_events.h" #include "presence_dialoginfo.h" /* module functions */ static int mod_init(void); /* module variables */ add_event_t pres_add_event; pres_contains_presence_t pres_contains_presence; /* module parameters */ int force_single_dialog = 0; /* module exported commands */ static cmd_export_t cmds[] = { {0, 0, 0, 0, 0, 0} }; /* module exported paramaters */ static param_export_t params[] = { { "force_single_dialog", INT_PARAM, &force_single_dialog }, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "presence", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* module exports */ struct module_exports exports= { "presence_dialoginfo", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ 0, /* destroy function */ 0 /* per-child init function */ }; /* * init module function */ static int mod_init(void) { presence_api_t pres; bind_presence_t bind_presence; bind_presence= (bind_presence_t)find_export("bind_presence", 1,0); if (!bind_presence) { LM_ERR("can't bind presence\n"); return -1; } if (bind_presence(&pres) < 0) { LM_ERR("can't bind pua\n"); return -1; } pres_add_event = pres.add_event; if (pres_add_event == NULL) { LM_ERR("could not import add_event\n"); return -1; } pres_contains_presence = pres.contains_presence; if ( pres_contains_presence == NULL ) { LM_ERR("could not import contains_presence\n"); return -1; } if(dlginfo_add_events() < 0) { LM_ERR("failed to add dialog-info events\n"); return -1; } return 0; } opensips-2.2.2/modules/presence_dialoginfo/presence_dialoginfo.h000066400000000000000000000021571300170765700252070ustar00rootroot00000000000000/* * presence_dialoginfo module - presence_dialoginfo header file * * Copyright (C) 2007 Juha Heinanen * Copyright (C) 2008 Klaus Darilion IPCom * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-08-25 initial version (kd) */ #ifndef _PRES_DLGINFO_H_ #define _PRES_DLGINFO_H_ extern add_event_t pres_add_event; extern pres_contains_presence_t pres_contains_presence; #endif opensips-2.2.2/modules/presence_mwi/000077500000000000000000000000001300170765700175135ustar00rootroot00000000000000opensips-2.2.2/modules/presence_mwi/Makefile000066400000000000000000000003171300170765700211540ustar00rootroot00000000000000# # Presence_MWI Makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=presence_mwi.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/presence_mwi/README000066400000000000000000000032221300170765700203720ustar00rootroot00000000000000Presence_MWI Module Juha Heinanen Edited by Juha Heinanen Copyright © 2007 Juha Heinanen Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.4. Exported Functions Chapter 1. Admin Guide 1.1. Overview The module does specific handling for notify-subscribe message-summary (message waiting indication) events as specified in RFC 3842. It is used with the general event handling module, presence. It constructs and adds message-summary event to it. The module does not currently implement any authorization rules. It assumes that publish requests are only issued by a voicemail application and subscribe requests only by the owner of voicemail box. Authorization can thus be easily done by OpenSIPS configuration file before calling handle_publish() and handle_subscribe() functions. The module implements a simple check of content type application/simple-message-summary: Content must start with Messages-Waiting status line followed by zero or more lines that consist of tabs and printable ASCII characters. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * presence. 1.2.2. External Libraries or Applications None. 1.3. Exported Parameters None. 1.4. Exported Functions None to be used in configuration file. opensips-2.2.2/modules/presence_mwi/add_events.c000066400000000000000000000067271300170765700220070ustar00rootroot00000000000000/* * Add "message-summary" event to presence module * * Copyright (C) 2007 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-05-1 initial version (jih) */ #include #include #include #include "../../parser/parse_content.h" #include "../presence/event_list.h" #include "presence_mwi.h" /* utility function that skips spaces and tabs */ inline char *eat_sp_tab(char *at, char *over) { while((at < over) && ((*at == ' ') || (*at == '\t'))) at++; return at; } /* utility function that skips printable ascii chars */ inline char *eat_printable(char *at, char *over) { while ((at < over) && ((*at == '\t') || ((*at >= 32) && (*at <= 126)))) at++; return at; } /* * event specific publish handling - check if body format is ok */ int mwi_publ_handl(struct sip_msg* msg, int *sent_reply) { return 1; str body; char *at, *over; if ( get_body(msg,&body)!=0 ) { LM_ERR("cannot extract body from msg\n"); return -1; } if (body.len == 0) return 1; at = body.s; over = body.s + body.len; /* check msg-status-line */ if (body.len <= 16){ goto err; } if (strncasecmp(body.s, "Messages-Waiting", 16) != 0){ goto err; } at = at + 16; at = eat_sp_tab(at, over); if ((at >= over) || (*at != ':')){ goto err; } at++; if ((at >= over) || ((*at != ' ') && (*at != '\t'))){ goto err; } at++; at = eat_sp_tab(at, over); if (at + 3 >= over){ goto err; } if (strncasecmp(at, "yes", 3) == 0) at = at + 3; else { if (strncasecmp(at, "no", 2) == 0) at = at + 2; else goto err; } if ((at + 1 >= over) || (*at != '\r') || (*(at + 1) != '\n')) goto err; at = at + 2; /* check that remaining body consists of lines that only contain printable ascii chars */ while (at < over) { at = eat_printable(at, over); if ((at + 1 >= over) || (*at != '\r') || (*(at + 1) != '\n')) goto err; at = at + 2; } return 1; err: LM_ERR("check of body <%.*s> failed at character number %d\n", body.len, body.s, (int)(at - body.s + 1)); return -1; } int mwi_add_events(void) { pres_ev_t event; /* constructing message-summary event */ memset(&event, 0, sizeof(pres_ev_t)); event.name.s = "message-summary"; event.name.len = 15; event.content_type.s = "application/simple-message-summary"; event.content_type.len = 34; event.mandatory_body = 1; event.mandatory_timeout_notification = 1; event.default_expires= 3600; event.type = PUBL_TYPE; event.req_auth = 0; event.evs_publ_handl = mwi_publ_handl; if (pres_add_event(&event) < 0) { LM_ERR("failed to add event \"message-summary\"\n"); return -1; } return 0; } opensips-2.2.2/modules/presence_mwi/add_events.h000066400000000000000000000017521300170765700220050ustar00rootroot00000000000000/* * presence_mwi module - add_event header file * * Copyright (C) 2007 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-05-1 initial version (jih) */ #ifndef _MWI_ADD_EV_H_ #define _MWI_ADD_EV_H_ int mwi_add_events(); #endif opensips-2.2.2/modules/presence_mwi/doc/000077500000000000000000000000001300170765700202605ustar00rootroot00000000000000opensips-2.2.2/modules/presence_mwi/doc/presence_mwi.xml000066400000000000000000000021231300170765700234600ustar00rootroot00000000000000 %docentities; ]> Presence_MWI Module &osipsname; Juha Heinanen jh@tutpro.com Juha Heinanen jh@tutpro.com 2007 Juha Heinanen $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/presence_mwi/doc/presence_mwi_admin.xml000066400000000000000000000034341300170765700246360ustar00rootroot00000000000000 &adminguide;
Overview The module does specific handling for notify-subscribe message-summary (message waiting indication) events as specified in RFC 3842. It is used with the general event handling module, presence. It constructs and adds message-summary event to it. The module does not currently implement any authorization rules. It assumes that publish requests are only issued by a voicemail application and subscribe requests only by the owner of voicemail box. Authorization can thus be easily done by &osips; configuration file before calling handle_publish() and handle_subscribe() functions. The module implements a simple check of content type application/simple-message-summary: Content must start with Messages-Waiting status line followed by zero or more lines that consist of tabs and printable ASCII characters.
Dependencies
&osips; Modules The following modules must be loaded before this module: presence.
External Libraries or Applications None.
Exported Parameters None.
Exported Functions None to be used in configuration file.
opensips-2.2.2/modules/presence_mwi/presence_mwi.c000066400000000000000000000064711300170765700223470ustar00rootroot00000000000000/* * presence_mwi module - Presence Handling of message-summary events * * Copyright (C) 2007 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-05-1 initial version (jih) */ #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../str.h" #include "../../parser/msg_parser.h" #include "../../mem/mem.h" #include "../presence/bind_presence.h" #include "add_events.h" #include "presence_mwi.h" /* module functions */ static int mod_init(void); static int child_init(int); static void destroy(void); /* module variables */ add_event_t pres_add_event; /* module exported commands */ static cmd_export_t cmds[] = { {0, 0, 0, 0, 0, 0} }; /* module exported paramaters */ static param_export_t params[] = { {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "presence", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* module exports */ struct module_exports exports= { "presence_mwi", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /* * init module function */ static int mod_init(void) { presence_api_t pres; LM_INFO("initializing...\n"); bind_presence_t bind_presence; bind_presence= (bind_presence_t)find_export("bind_presence", 1,0); if (!bind_presence) { LM_ERR("can't bind presence\n"); return -1; } if (bind_presence(&pres) < 0) { LM_ERR("can't bind pua\n"); return -1; } pres_add_event = pres.add_event; if (pres_add_event == NULL) { LM_ERR("could not import add_event\n"); return -1; } if(mwi_add_events() < 0) { LM_ERR("failed to add mwi events\n"); return -1; } return 0; } static int child_init(int rank) { LM_DBG("[%d] pid [%d]\n", rank, getpid()); return 0; } static void destroy(void) { LM_DBG("destroying module ...\n"); return; } opensips-2.2.2/modules/presence_mwi/presence_mwi.h000066400000000000000000000017661300170765700223560ustar00rootroot00000000000000/* * presence_mwi module - presence_mwi header file * * Copyright (C) 2007 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-05-1 initial version (jih) */ #ifndef _PRES_MWI_H_ #define _PRES_MWI_H_ extern add_event_t pres_add_event; #endif opensips-2.2.2/modules/presence_xcapdiff/000077500000000000000000000000001300170765700205035ustar00rootroot00000000000000opensips-2.2.2/modules/presence_xcapdiff/Makefile000066400000000000000000000002561300170765700221460ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=presence_xcapdiff.so include ../../Makefile.modules opensips-2.2.2/modules/presence_xcapdiff/README000066400000000000000000000041771300170765700213740ustar00rootroot00000000000000Presence_XCAPDiff Module Denis Bilenko Edited by Denis Bilenko Copyright © 2008 AG Projects Revision History Revision $Revision$ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications Chapter 1. Admin Guide 1.1. Overview The presence_xcapdiff is an OpenSIPS module that adds support for the "xcap-diff" event to presence and pua. At the moment, the module just registers the event but doesn't do any event-specific processing. The module will automatically determine if the presence and/or pua modules are present and if so it will register the xcap-diff event with them. This allows the module to automatically offer presence or pua related functionality simply based on the presence of the aforementioned modules in the OpenSIPS configuration, without any need for manual configuration. Registering the event with pua, allows the XCAP server to publish the xcap-event when some modification of a document happens. Registering the event with presence allows clients to subscribe to the event. The module is intended to be used with the OpenXCAP server (www.openxcap.org), although it doesn't contain any OpenXCAP-specific code and should be usable with any XCAP server. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * presence module - to enable clients to subscribe to the xcap-diff event package. * pua module - to be able to publish the xcap-diff event when some modification of a document happens. * pua_mi module - to enable pua to publish the xcap-diff event using the MI interface. This is needed if this module is intended to be used in conjunction with OpenXCAP. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. opensips-2.2.2/modules/presence_xcapdiff/doc/000077500000000000000000000000001300170765700212505ustar00rootroot00000000000000opensips-2.2.2/modules/presence_xcapdiff/doc/presence_xcapdiff.xml000066400000000000000000000017711300170765700254500ustar00rootroot00000000000000 %docentities; ]> Presence_XCAPDiff Module Denis Bilenko denis@ag-projects.com Denis Bilenko denis@ag-projects.com 2008 AG Projects $Revision$ $Date$ &admin; &faq; opensips-2.2.2/modules/presence_xcapdiff/doc/presence_xcapdiff_admin.xml000066400000000000000000000051541300170765700266170ustar00rootroot00000000000000 &adminguide;
Overview The presence_xcapdiff is an &osips; module that adds support for the "xcap-diff" event to presence and pua. At the moment, the module just registers the event but doesn't do any event-specific processing. The module will automatically determine if the presence and/or pua modules are present and if so it will register the xcap-diff event with them. This allows the module to automatically offer presence or pua related functionality simply based on the presence of the aforementioned modules in the &osips; configuration, without any need for manual configuration. Registering the event with pua, allows the XCAP server to publish the xcap-event when some modification of a document happens. Registering the event with presence allows clients to subscribe to the event. The module is intended to be used with the OpenXCAP server (www.openxcap.org), although it doesn't contain any OpenXCAP-specific code and should be usable with any XCAP server.
Dependencies
&osips; Modules The following modules must be loaded before this module: presence module - to enable clients to subscribe to the xcap-diff event package. pua module - to be able to publish the xcap-diff event when some modification of a document happens. pua_mi module - to enable pua to publish the xcap-diff event using the MI interface. This is needed if this module is intended to be used in conjunction with OpenXCAP.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
opensips-2.2.2/modules/presence_xcapdiff/presence_xcapdiff.c000066400000000000000000000074101300170765700243210ustar00rootroot00000000000000/* * Copyright (C) 2008 AG Projects * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "../../sr_module.h" #include "../presence/bind_presence.h" #include "../pua/pua_bind.h" str s_event_name = str_init("xcap-diff"); str s_content_type = str_init("application/xcap-diff+xml"); static int mod_init(void); static cmd_export_t cmds[] = { {0, 0, 0, 0, 0, 0} }; static param_export_t params[] = { {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "presence", DEP_SILENT }, { MOD_TYPE_DEFAULT, "pua", DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "presence_xcapdiff", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ 0, /* destroy function */ 0 /* per-child init function */ }; static int mod_init(void) { bind_presence_t bind_presence; presence_api_t pres; bind_pua_t bind_pua; pres_ev_t event; pua_api_t pua; LM_INFO("initializing...\n"); bind_presence = (bind_presence_t)find_export("bind_presence", 1, 0); if (bind_presence) { if (bind_presence(&pres) < 0) { LM_ERR("could not bind to the presence module API\n"); return -1; } memset(&event, 0, sizeof(pres_ev_t)); event.name = s_event_name; event.content_type = s_content_type; event.default_expires = 3600; event.type = PUBL_TYPE; event.req_auth = 0; if (pres.add_event(&event) < 0) { LM_ERR("could not add xcap-diff event to presence\n"); return -1; } } else { LM_NOTICE("subscribing to the xcap-diff event is not available as the presence module is not loaded\n"); } bind_pua = (bind_pua_t)find_export("bind_pua", 1, 0); if (bind_pua) { if (bind_pua(&pua) < 0) { LM_ERR("could not bind to the pua module API\n"); return -1; } if(pua.add_event(XCAPDIFF_EVENT, s_event_name.s, s_content_type.s, 0) < 0) { LM_ERR("could not add xcap-diff event to pua\n"); return -1; } } else { LM_NOTICE("publishing of xcap-diff events is not available as the pua module is not loaded\n"); } return 0; } opensips-2.2.2/modules/presence_xml/000077500000000000000000000000001300170765700175175ustar00rootroot00000000000000opensips-2.2.2/modules/presence_xml/Makefile000066400000000000000000000005431300170765700211610ustar00rootroot00000000000000# $Id$ # # Presence_XML # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=presence_xml.so DEFS+=-I$(SYSBASE)/include/libxml2 -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS=-L$(SYSBASE)/include/lib -L$(LOCALBASE)/lib -lxml2 include ../../Makefile.modules opensips-2.2.2/modules/presence_xml/README000066400000000000000000000125441300170765700204050ustar00rootroot00000000000000Presence_XML Module Anca-Maria Vamanu Edited by Anca-Maria Vamanu Edited by Saul Ibarra Corretge Copyright © 2007 Voice Sistem SRL Revision History Revision $Revision: 9037 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. force_active (int) 1.3.2. pidf_manipulation (int) 1.3.3. xcap_server (str) 1.3.4. pres_rules_auid (str) 1.3.5. pres_rules_filename (str) 1.3.6. generate_offline_body (str) 1.4. Exported Functions 1.5. Installation 2. Developer Guide List of Examples 1.1. Set force_active parameter 1.2. Set pidf_manipulation parameter 1.3. Set xcap_server parameter 1.4. Set pres_rules_auid parameter 1.5. Set pres_rules_filename parameter 1.6. Set generate_offline_body parameter Chapter 1. Admin Guide 1.1. Overview The module does specific handling for notify-subscribe events using xml bodies. It is used with the general event handling module, presence. It constructs and adds 3 events to it: presence, presence.winfo, dialog;sla. This module takes the xcap permission rule documents from xcap_table. The presence permission rules are interpreted according to the specifications in RFC 4745 and RFC 5025. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * a database module. * presence. * signaling. * xcap. * xcap_client. Only compulsory if not using an integrated xcap server (if 'integrated_xcap_server' parameter is not set). 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml-dev. 1.3. Exported Parameters 1.3.1. force_active (int) This parameter is used for permissions when handling Subscribe messages. If set to 1, subscription state is considered active and the presentity is not queried for permissions(should be set to 1 if not using an xcap server). Otherwise,the xcap server is queried and the subscription states is according to user defined permission rules. If no rules are defined for a certain watcher, the subscriptions remains in pending state and the Notify sent will have no body. Note: When switching from one value to another, the watchers table must be emptied. Default value is “0â€. Example 1.1. Set force_active parameter ... modparam("presence_xml", "force_active", 1) ... 1.3.2. pidf_manipulation (int) Setting this parameter to 1 enables the features described in RFC 4827. It gives the possibility to have a permanent state notified to the users even in the case in which the phone is not online. The presence document is taken from the xcap server and aggregated together with the other presence information, if any exist, for each Notify that is sent to the watchers. It is also possible to have information notified even if not issuing any Publish (useful for services such as email, SMS, MMS). Default value is “0â€. Example 1.2. Set pidf_manipulation parameter ... modparam("presence_xml", "pidf_manipulation", 1) ... 1.3.3. xcap_server (str) The address of the xcap servers used for storage. This parameter is compulsory if the integrated_xcap_server parameter is not set. It can be set more that once, to construct an address list of trusted XCAP servers. Example 1.3. Set xcap_server parameter ... modparam("presence_xml", "xcap_server", "xcap_server.example.org") modparam("presence_xml", "xcap_server", "xcap_server.ag.org") ... 1.3.4. pres_rules_auid (str) This parameter should be configured if you are using the non integrated xcap mode and you need to use another pres-rules auid than the default 'pres-rules'. Example 1.4. Set pres_rules_auid parameter ... modparam("presence_xml", "pres_rules_auid", "org.openmobilealliance.pres -rules") ... 1.3.5. pres_rules_filename (str) This parameter should be configured if you are using the non integrated xcap mode and you need to configure another filename than the default 'index'. Example 1.5. Set pres_rules_filename parameter ... modparam("presence_xml", "pres_rules_filename", "pres-rules") ... 1.3.6. generate_offline_body (str) This parameter should be set to 0 if you want to prevent OpenSIPS from automatically generating a PIDF body when a publication expires or is explicitly terminated (a PUBLISH request is received with Expires: 0). Example 1.6. Set generate_offline_body parameter ... modparam("presence_xml", "generate_offline_body", 0) ... 1.4. Exported Functions None to be used in configuration file. 1.5. Installation The module requires 1 table in OpenSIPS database: xcap. The SQL syntax to create it can be found in presence-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. Chapter 2. Developer Guide The module exports no function to be used in other OpenSIPS modules. opensips-2.2.2/modules/presence_xml/add_events.c000066400000000000000000000116431300170765700220040ustar00rootroot00000000000000/* * presence_xml module - * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-17 initial version (anca) */ /* * add 3 events: presence, presence.winfo, dialog;sla * */ #include #include #include #include #include "../../parser/parse_content.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "xcap_auth.h" #include "notify_body.h" #include "add_events.h" #include "presence_xml.h" #include "pidf.h" static str pu_415_rpl = str_init("Unsupported media type"); /* * in event specific publish handling - only check is good body format */ int xml_publ_handl(struct sip_msg* msg, int* sent_reply) { str body= {0, 0}; xmlDocPtr doc= NULL; *sent_reply= 0; if ( get_body(msg,&body)!=0 ) { LM_ERR("cannot extract body from msg\n"); return -1; } if (body.len == 0) return 1; doc= xmlParseMemory( body.s, body.len ); if(doc== NULL) { LM_ERR("bad body format\n"); if( xml_sigb.reply( msg, 415, &pu_415_rpl, 0)== -1) { LM_ERR("while sending '415 Unsupported media type' reply\n"); } *sent_reply = 1; goto error; } xmlFreeDoc(doc); xmlCleanupParser(); xmlMemoryDump(); return 1; error: xmlFreeDoc(doc); xmlCleanupParser(); xmlMemoryDump(); return -1; } str* bla_set_version(subs_t* subs, str* body) { xmlNodePtr node= NULL; xmlDocPtr doc= NULL; char* version; str* new_body= NULL; int len; doc= xmlParseMemory(body->s, body->len ); if(doc== NULL) { LM_ERR("while parsing xml memory\n"); goto error; } /* change version */ node= xmlDocGetNodeByName(doc, "dialog-info", NULL); if(node == NULL) { LM_ERR("while extracting dialog-info node\n"); goto error; } version= int2str(subs->version, &len); version[len]= '\0'; LM_DBG("set version %.*s %d\n", subs->callid.len, subs->callid.s, subs->version); if( xmlSetProp(node, (const xmlChar *)"version",(const xmlChar*)version)== NULL) { LM_ERR("while setting version attribute\n"); goto error; } new_body= (str*)pkg_malloc(sizeof(str)); if(new_body== NULL) { LM_ERR("NO more memory left\n"); goto error; } memset(new_body, 0, sizeof(str)); xmlDocDumpMemory(doc, (xmlChar**)(void*)&new_body->s, &new_body->len); xmlFreeDoc(doc); xmlMemoryDump(); xmlCleanupParser(); return new_body; error: if(doc) xmlFreeDoc(doc); xmlMemoryDump(); xmlCleanupParser(); return 0; } int xml_add_events(void) { pres_ev_t event; /* constructing presence event */ memset(&event, 0, sizeof(pres_ev_t)); event.name.s= "presence"; event.name.len= 8; event.content_type.s= "application/pidf+xml"; event.content_type.len= 20; event.mandatory_body = 1; event.mandatory_timeout_notification = 1; event.type= PUBL_TYPE; event.req_auth= 1; event.apply_auth_nbody= pres_apply_auth; event.get_auth_status= pres_watcher_allowed; event.agg_nbody= presence_agg_nbody; event.evs_publ_handl= xml_publ_handl; event.free_body= free_xml_body; event.default_expires= 3600; event.get_rules_doc= pres_get_rules_doc; if(pres_add_event(&event)< 0) { LM_ERR("while adding event presence\n"); return -1; } /* constructing presence.winfo event */ memset(&event, 0, sizeof(pres_ev_t)); event.name.s= "presence.winfo"; event.name.len= 14; event.content_type.s= "application/watcherinfo+xml"; event.content_type.len= 27; event.mandatory_body = 1; event.mandatory_timeout_notification = 1; event.type= WINFO_TYPE; event.free_body= free_xml_body; event.default_expires= 3600; if(pres_add_event(&event)< 0) { LM_ERR("while adding event presence.winfo\n"); return -1; } /* constructing bla event */ memset(&event, 0, sizeof(pres_ev_t)); event.name.s= "dialog;sla"; event.name.len= 10; event.mandatory_body = 1; event.mandatory_timeout_notification = 1; // event.etag_not_new= 1; event.evs_publ_handl= xml_publ_handl; event.agg_nbody= dialog_agg_nbody; event.content_type.s= "application/dialog-info+xml"; event.content_type.len= 27; event.type= PUBL_TYPE; event.free_body= free_xml_body; event.aux_body_processing = bla_set_version; event.aux_free_body = free_xml_body; event.default_expires= 3600; if(pres_add_event(&event)< 0) { LM_ERR("while adding event dialog;sla\n"); return -1; } return 0; } opensips-2.2.2/modules/presence_xml/add_events.h000066400000000000000000000017401300170765700220060ustar00rootroot00000000000000/* * presence_xml module - * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-18 initial version (anca) */ #ifndef _XML_ADD_EV_H_ #define _XML_ADD_EV_H_ int xml_add_events(void); #endif opensips-2.2.2/modules/presence_xml/doc/000077500000000000000000000000001300170765700202645ustar00rootroot00000000000000opensips-2.2.2/modules/presence_xml/doc/presence_xml.xml000066400000000000000000000022101300170765700234650ustar00rootroot00000000000000 %docentities; ]> Presence_XML Module &osipsname; Anca-Maria Vamanu Anca-Maria Vamanu Saul Ibarra Corretge 2007 &voicesystem; $Revision: 9037 $ $Date$ &admin; &devel; &faq; opensips-2.2.2/modules/presence_xml/doc/presence_xml_admin.xml000066400000000000000000000141701300170765700246450ustar00rootroot00000000000000 &adminguide;
Overview The module does specific handling for notify-subscribe events using xml bodies. It is used with the general event handling module, presence. It constructs and adds 3 events to it: presence, presence.winfo, dialog;sla. This module takes the xcap permission rule documents from xcap_table. The presence permission rules are interpreted according to the specifications in RFC 4745 and RFC 5025.
Dependencies
&osips; Modules The following modules must be loaded before this module: a database module. presence. signaling. xcap. xcap_client. Only compulsory if not using an integrated xcap server (if 'integrated_xcap_server' parameter is not set).
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml-dev.
Exported Parameters
<varname>force_active</varname> (int) This parameter is used for permissions when handling Subscribe messages. If set to 1, subscription state is considered active and the presentity is not queried for permissions(should be set to 1 if not using an xcap server). Otherwise,the xcap server is queried and the subscription states is according to user defined permission rules. If no rules are defined for a certain watcher, the subscriptions remains in pending state and the Notify sent will have no body. Note: When switching from one value to another, the watchers table must be emptied. Default value is 0. Set <varname>force_active</varname> parameter ... modparam("presence_xml", "force_active", 1) ...
<varname>pidf_manipulation</varname> (int) Setting this parameter to 1 enables the features described in RFC 4827. It gives the possibility to have a permanent state notified to the users even in the case in which the phone is not online. The presence document is taken from the xcap server and aggregated together with the other presence information, if any exist, for each Notify that is sent to the watchers. It is also possible to have information notified even if not issuing any Publish (useful for services such as email, SMS, MMS). Default value is 0. Set <varname>pidf_manipulation</varname> parameter ... modparam("presence_xml", "pidf_manipulation", 1) ...
<varname>xcap_server</varname> (str) The address of the xcap servers used for storage. This parameter is compulsory if the integrated_xcap_server parameter is not set. It can be set more that once, to construct an address list of trusted XCAP servers. Set <varname>xcap_server</varname> parameter ... modparam("presence_xml", "xcap_server", "xcap_server.example.org") modparam("presence_xml", "xcap_server", "xcap_server.ag.org") ...
<varname>pres_rules_auid</varname> (str) This parameter should be configured if you are using the non integrated xcap mode and you need to use another pres-rules auid than the default 'pres-rules'. Set <varname>pres_rules_auid</varname> parameter ... modparam("presence_xml", "pres_rules_auid", "org.openmobilealliance.pres-rules") ...
<varname>pres_rules_filename</varname> (str) This parameter should be configured if you are using the non integrated xcap mode and you need to configure another filename than the default 'index'. Set <varname>pres_rules_filename</varname> parameter ... modparam("presence_xml", "pres_rules_filename", "pres-rules") ...
<varname>generate_offline_body</varname> (str) This parameter should be set to 0 if you want to prevent OpenSIPS from automatically generating a PIDF body when a publication expires or is explicitly terminated (a PUBLISH request is received with Expires: 0). Set <varname>generate_offline_body</varname> parameter ... modparam("presence_xml", "generate_offline_body", 0) ...
Exported Functions None to be used in configuration file.
Installation The module requires 1 table in OpenSIPS database: xcap. The SQL syntax to create it can be found in presence-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, &osipsdbdocs;.
opensips-2.2.2/modules/presence_xml/doc/presence_xml_devel.xml000066400000000000000000000002651300170765700246540ustar00rootroot00000000000000 &develguide; The module exports no function to be used in other &osips; modules. opensips-2.2.2/modules/presence_xml/notify_body.c000066400000000000000000000756441300170765700222300ustar00rootroot00000000000000/* * presence_xml module - * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-11 initial version (anca) */ #include #include #include #include "../../mem/mem.h" #include "../presence/utils_func.h" #include "../presence/hash.h" #include "xcap_auth.h" #include "pidf.h" #include "notify_body.h" #include "presence_xml.h" str* get_final_notify_body( subs_t *subs, str* notify_body, xmlNodePtr rule_node); enum { OFFB_STATUS_OK = 0, OFFB_STATUS_NO_DIALOG, OFFB_STATUS_ERROR, }; #define GET_LAST_XML_ERROR(e, msg) \ (e) = xmlGetLastError(); (msg) = (e) ? (e)->message : "unknown error" struct xml_node_s { xmlNodePtr node; struct xml_node_s *next; }; typedef struct xml_node_s xml_node_t; static inline int check_duplicated_id(const char *id, xml_node_t *list) { int found = 0; char *curr_id; xml_node_t *curr; curr = list; while (curr) { curr_id = xmlNodeGetAttrContentByName(curr->node, "id"); if(curr_id == NULL) continue; if(xmlStrcasecmp(BAD_CAST id, BAD_CAST curr_id )== 0) { found = 1; xmlFree(curr_id); break; } xmlFree(curr_id); curr = curr->next; } return found; } int dialog_offline_body(str* body, str** offline_body) { xmlDocPtr doc= NULL; xmlNodePtr node; xmlErrorPtr xml_error; str* new_body = NULL; char *err_msg; int rc = OFFB_STATUS_ERROR; if (!offline_body) { LM_ERR("invalid parameter"); return OFFB_STATUS_ERROR; } *offline_body = NULL; doc= xmlParseMemory(body->s, body->len); if(doc== NULL) { GET_LAST_XML_ERROR(xml_error, err_msg); LM_ERR("xml memory parsing failed: %s\n", err_msg); goto done; } node= xmlDocGetNodeByName(doc, "dialog", NULL); if(node== NULL) { LM_DBG("no dialog nodes found"); rc = OFFB_STATUS_NO_DIALOG; goto done; } node= xmlNodeGetChildByName(node, "state"); if(node== NULL) { LM_ERR("while extracting state node\n"); goto done; } xmlNodeSetContent(node, (const unsigned char*)"terminated"); new_body = (str*)pkg_malloc(sizeof(str)); if(new_body == NULL) { LM_ERR("No more pkg memory"); goto done; } memset(new_body, 0, sizeof(str)); xmlDocDumpMemory(doc,(xmlChar**)(void*)&new_body->s, &new_body->len); *offline_body = new_body; rc = OFFB_STATUS_OK; done: if (doc) xmlFreeDoc(doc); return rc; } int presence_offline_body(str* body, str** offline_body) { xmlDocPtr doc= NULL; xmlDocPtr new_doc= NULL; xmlNodePtr node, tuple_node= NULL, status_node; xmlNodePtr root_node, add_node, pres_node; xmlErrorPtr xml_error; str* new_body; char *err_msg; int rc = OFFB_STATUS_ERROR; doc= xmlParseMemory(body->s, body->len); if(doc== NULL) { GET_LAST_XML_ERROR(xml_error, err_msg); LM_ERR("xml memory parsing failed: %s\n", err_msg); goto done; } node= xmlDocGetNodeByName(doc, "basic", NULL); if(node== NULL) { LM_ERR("while extracting basic node\n"); goto done; } xmlNodeSetContent(node, (const unsigned char*)"closed"); tuple_node= xmlDocGetNodeByName(doc, "tuple", NULL); if(tuple_node== NULL) { LM_ERR("while extracting tuple node\n"); goto done; } status_node= xmlDocGetNodeByName(doc, "status", NULL); if(status_node== NULL) { LM_ERR("while extracting tuple node\n"); goto done; } pres_node= xmlDocGetNodeByName(doc, "presence", NULL); if(node== NULL) { LM_ERR("while extracting presence node\n"); goto done; } new_doc = xmlNewDoc(BAD_CAST "1.0"); if(new_doc==0) { GET_LAST_XML_ERROR(xml_error, err_msg); LM_ERR("failed to create new XML document: %s\n", err_msg); goto done; } root_node= xmlCopyNode(pres_node, 2); if(root_node== NULL) { GET_LAST_XML_ERROR(xml_error, err_msg); LM_ERR("failed to copy root node: %s\n", err_msg); goto done; } xmlDocSetRootElement(new_doc, root_node); tuple_node= xmlCopyNode(tuple_node, 2); if(tuple_node== NULL) { GET_LAST_XML_ERROR(xml_error, err_msg); LM_ERR("failed to copy tuple node: %s\n", err_msg); goto done; } xmlAddChild(root_node, tuple_node); add_node= xmlCopyNode(status_node, 1); if(add_node== NULL) { GET_LAST_XML_ERROR(xml_error, err_msg); LM_ERR("failed to copy status node: %s\n", err_msg); goto done; } xmlAddChild(tuple_node, add_node); new_body = (str*)pkg_malloc(sizeof(str)); if(new_body == NULL) { LM_ERR("No more pkg memory"); goto done; } memset(new_body, 0, sizeof(str)); xmlDocDumpMemory(new_doc,(xmlChar**)(void*)&new_body->s, &new_body->len); *offline_body = new_body; rc = OFFB_STATUS_OK; done: if(doc) xmlFreeDoc(doc); if(new_doc) xmlFreeDoc(new_doc); return rc; } void free_xml_body(char* body) { if(body== NULL) return; xmlFree(body); body= NULL; } str* agregate_dialog_xmls(str* pres_user, str* pres_domain, str** body_array, int n) { char* root_name = "dialog-info"; char* elem_name = "dialog"; int i, j= 0, append ; xmlNodePtr p_root= NULL, new_p_root= NULL ; xmlDocPtr* xml_array ; xmlNodePtr node = NULL; xmlNodePtr add_node = NULL ; str *body= NULL; char* id= NULL, *elem_id = NULL; xml_array = (xmlDocPtr*)pkg_malloc( (n+2)*sizeof(xmlDocPtr)); if(xml_array== NULL) { LM_ERR("while alocating memory"); return NULL; } memset(xml_array, 0, (n+2)*sizeof(xmlDocPtr)) ; for(i=0; is, body_array[i]->len ); LM_DBG("i = [%d] - body: %.*s\n", i, body_array[i]->len, body_array[i]->s); if( xml_array[j]== NULL) { LM_ERR("while parsing xml body message\n"); goto error; } j++; } if(j== 0) /* no body */ { if(xml_array) pkg_free(xml_array); return NULL; } j--; p_root = xmlDocGetNodeByName( xml_array[j], root_name, NULL); if(p_root ==NULL) { LM_ERR("while geting the xml_tree root\n"); goto error; } for(i= j-1; i>=0; i--) { LM_DBG("i = %d\n", i); new_p_root= xmlDocGetNodeByName( xml_array[i], root_name, NULL); if(new_p_root ==NULL) { LM_ERR("while geting the xml_tree root\n"); goto error; } node= xmlNodeGetChildByName(new_p_root, elem_name); if(node== NULL) { LM_DBG("no %s node found\n", elem_name); append = 1; goto append_label; } elem_id= xmlNodeGetAttrContentByName(node, "id"); if(elem_id== NULL) { LM_ERR("while extracting %s id\n", elem_name); goto error; } append= 1; for (node = p_root->children; node!=NULL; node = node->next) { if( xmlStrcasecmp(node->name,(unsigned char*)"text")==0) continue; if( xmlStrcasecmp(node->name,(unsigned char*)elem_name)==0) { id = xmlNodeGetAttrContentByName(node, "id"); if(id== NULL) { LM_ERR("while extracting %s id\n", elem_name); goto error; } if(xmlStrcasecmp((unsigned char*)elem_id, (unsigned char*)id )== 0) { append = 0; xmlFree(id); break; } xmlFree(id); } } xmlFree(elem_id); elem_id= NULL; append_label: if(append) { LM_DBG("in if\n"); for(node= new_p_root->children; node; node= node->next) { LM_DBG("adding node [%s]\n", node->name); add_node= xmlCopyNode(node, 1); if(add_node== NULL) { LM_ERR("while copying node [%s]\n", node->name); goto error; } if(xmlAddChild(p_root, add_node)== NULL) { LM_ERR("while adding child\n"); goto error; } } } } body = (str*)pkg_malloc(sizeof(str)); if(body == NULL) { ERR_MEM(PKG_MEM_STR); } xmlDocDumpMemory(xml_array[j],(xmlChar**)(void*)&body->s, &body->len); LM_DBG("body = %.*s\n", body->len, body->s); for(i=0; i<=j; i++) { if(xml_array[i]!=NULL) xmlFreeDoc( xml_array[i]); } if(xml_array!=NULL) pkg_free(xml_array); return body; error: if(xml_array!=NULL) { for(i=0; i<=j; i++) { if(xml_array[i]!=NULL) xmlFreeDoc( xml_array[i]); } pkg_free(xml_array); } if(elem_id) xmlFree(elem_id); if(body) pkg_free(body); return NULL; } #define ADD_NODE(node, list_head, list_tail) \ do { \ LM_DBG("adding node [%s]\n", node->name); \ add_node = xmlCopyNode(node, 1); \ if(add_node == NULL) \ { \ LM_ERR("while copying node [%s]\n", node->name); \ goto error; \ } \ tmp_node = (xml_node_t *)pkg_malloc(sizeof(xml_node_t)); \ if(tmp_node == NULL) \ { \ ERR_MEM(PKG_MEM_STR); \ } \ tmp_node->node = add_node; \ tmp_node->next = NULL; \ if (!list_head) \ { \ list_head = tmp_node; \ list_tail = tmp_node; \ } \ else \ { \ list_tail->next = tmp_node; \ list_tail = tmp_node; \ } \ } while(0) \ str* agregate_presence_xmls(str* pres_user, str* pres_domain, str** body_array, int n) { static char* root_name = "presence"; static char* tuple_name = "tuple"; static char* note_name = "note"; static char* person_name = "person"; static char* device_name = "device"; int i, j = 0, len; char* id = NULL; char buf[MAX_URI_SIZE+1]; str *body= NULL; str* pidf_doc= NULL; str pres_uri = {0,0}; xmlDocPtr* xml_array; xmlDocPtr new_doc = NULL; xmlDocPtr pidf_manip_doc = NULL; xmlNodePtr new_doc_root = NULL; xmlNodePtr current_doc_root = NULL; xmlNodePtr node = NULL; xmlNodePtr add_node = NULL; xml_node_t *tmp_node = NULL; xml_node_t *tmp_node2 = NULL; xml_node_t *tuples_head = NULL; xml_node_t *tuples_tail = NULL; xml_node_t *notes_head = NULL; xml_node_t *notes_tail = NULL; xml_node_t *persons_head = NULL; xml_node_t *persons_tail = NULL; xml_node_t *devices_head = NULL; xml_node_t *devices_tail = NULL; xml_node_t *others_head = NULL; xml_node_t *others_tail = NULL; xml_array = (xmlDocPtr*)pkg_malloc( (n+2)*sizeof(xmlDocPtr)); if(xml_array == NULL) { LM_ERR("while alocating memory"); return NULL; } memset(xml_array, 0, (n+2)*sizeof(xmlDocPtr)) ; if ((4 + pres_user->len + 1 + pres_domain->len + 1) > MAX_URI_SIZE) { LM_ERR("entity URI too long, maximum=%d\n", MAX_URI_SIZE); return NULL; } memcpy(buf, "sip:", 4); len = 4; memcpy(buf+len, pres_user->s, pres_user->len); len += pres_user->len; buf[len] = '@'; len += 1; memcpy(buf+len, pres_domain->s, pres_domain->len); len += pres_domain->len; buf[len]= '\0'; pres_uri.s = buf; pres_uri.len = len; LM_DBG("[pres_uri] %.*s\n", pres_uri.len, pres_uri.s); /* if pidf_manipulation usage is configured and there are no other bodies */ if(pidf_manipulation && body_array == NULL) { if(p_get_xcap_doc(pres_user, pres_domain, PIDF_MANIPULATION, &pidf_doc) < 0) { LM_ERR("while getting xcap tree for doc_type PIDF_MANIPULATION\n"); goto error; } if(pidf_doc == NULL) { LM_DBG("No PIDF_MANIPULATION doc for [user]= %.*s [domain]= %.*s\n", pres_user->len, pres_user->s, pres_domain->len, pres_domain->s); } else { pidf_manip_doc = xmlParseMemory(pidf_doc->s, pidf_doc->len); pkg_free(pidf_doc->s); pkg_free(pidf_doc); if(pidf_manip_doc == NULL) { LM_ERR("parsing xml memory\n"); goto error; } else { xml_array[0]= pidf_manip_doc; j++; } } } for(i = 0; i < n; i++) { if(body_array[i] == NULL ) continue; xml_array[j] = NULL; xml_array[j] = xmlParseMemory( body_array[i]->s, body_array[i]->len ); LM_DBG("i = [%d] - body: %.*s\n", i, body_array[i]->len, body_array[i]->s); if(xml_array[j] == NULL) { LM_ERR("while parsing xml body message\n"); goto error; } j++; } if(j == 0) /* no body */ { if(xml_array) pkg_free(xml_array); return NULL; } j--; for(i = j; i >= 0; i--) { LM_DBG("i = %d\n", i); current_doc_root = xmlDocGetRootElement(xml_array[i]); if(current_doc_root == NULL) { LM_ERR("while geting the xml_tree root\n"); continue; } if(!(xmlStrcasecmp(current_doc_root->name, (unsigned char*)root_name)==0 && xmlStrcasecmp(current_doc_root->ns->href, BAD_CAST "urn:ietf:params:xml:ns:pidf")==0)) { LM_ERR("invalid root element\n"); continue; } for (node = current_doc_root->children; node; node = node->next) { if (node->type != XML_ELEMENT_NODE) continue; /* Handle tuple elements */ if(xmlStrcasecmp(node->name, (unsigned char*)tuple_name)==0 && xmlStrcasecmp(node->ns->href, BAD_CAST "urn:ietf:params:xml:ns:pidf")==0) { id = xmlNodeGetAttrContentByName(node, "id"); if(id == NULL) { LM_ERR("while extracting %s id\n", node->name); goto error; } /* xs:ID needs to be unique in the whole document */ if (check_duplicated_id(id, tuples_head) || check_duplicated_id(id, persons_head) || check_duplicated_id(id, devices_head)) { xmlFree(id); continue; } xmlFree(id); ADD_NODE(node, tuples_head, tuples_tail); continue; } /* Handle note elements */ if(xmlStrcasecmp(node->name, (unsigned char*)note_name)==0 && xmlStrcasecmp(node->ns->href, BAD_CAST "urn:ietf:params:xml:ns:pidf")==0) { ADD_NODE(node, notes_head, notes_tail); continue; } /* Handle person elements */ if(xmlStrcasecmp(node->name, (unsigned char*)person_name)==0 && xmlStrcasecmp(node->ns->href, BAD_CAST "urn:ietf:params:xml:ns:pidf:data-model")==0) { id = xmlNodeGetAttrContentByName(node, "id"); if(id == NULL) { LM_ERR("while extracting %s id\n", node->name); goto error; } /* xs:ID needs to be unique in the whole document */ if (check_duplicated_id(id, persons_head) || check_duplicated_id(id, tuples_head) || check_duplicated_id(id, devices_head)) { xmlFree(id); continue; } xmlFree(id); ADD_NODE(node, persons_head, persons_tail); continue; } /* Handle device elements */ if(xmlStrcasecmp(node->name, (unsigned char*)device_name)==0 && xmlStrcasecmp(node->ns->href, BAD_CAST "urn:ietf:params:xml:ns:pidf:data-model")==0) { id = xmlNodeGetAttrContentByName(node, "id"); if(id == NULL) { LM_ERR("while extracting %s id\n", node->name); goto error; } /* xs:ID needs to be unique in the whole document */ if (check_duplicated_id(id, devices_head) || check_duplicated_id(id, tuples_head) || check_duplicated_id(id, persons_head)) { xmlFree(id); continue; } xmlFree(id); ADD_NODE(node, devices_head, devices_tail); continue; } /* Handle other elements */ ADD_NODE(node, others_head, others_tail); } } /* We built all lists, we can now build the new document */ new_doc = xmlNewDoc(BAD_CAST "1.0"); if(new_doc == NULL) { LM_ERR("allocating new xml doc\n"); goto error; } new_doc_root = xmlNewNode(NULL, BAD_CAST "presence"); if(new_doc_root == NULL) { LM_ERR("Failed to create xml node\n"); goto error; } xmlNewProp(new_doc_root, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:pidf"); xmlDocSetRootElement(new_doc, new_doc_root); xmlNewProp(new_doc_root, BAD_CAST "entity", BAD_CAST pres_uri.s); /* Add tuple elements */ tmp_node = tuples_head; while(tmp_node) { xmlAddChild(new_doc_root, tmp_node->node); tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } /* Add note elements */ tmp_node = notes_head; while(tmp_node) { xmlAddChild(new_doc_root, tmp_node->node); tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } /* Add person elements */ tmp_node = persons_head; while(tmp_node) { xmlAddChild(new_doc_root, tmp_node->node); tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } /* Add devices elements */ tmp_node = devices_head; while(tmp_node) { xmlAddChild(new_doc_root, tmp_node->node); tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } /* Add other elements */ tmp_node = others_head; while(tmp_node) { xmlAddChild(new_doc_root, tmp_node->node); tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } tmp_node = tmp_node2 = tuples_head = tuples_tail = notes_head = notes_tail = persons_head = persons_tail = devices_head = devices_tail = others_head = others_tail = NULL; body = (str *)pkg_malloc(sizeof(str)); if(body == NULL) { ERR_MEM(PKG_MEM_STR); } xmlDocDumpMemory(new_doc, (xmlChar**)(void*)&body->s, &body->len); LM_DBG("body = %.*s\n", body->len, body->s); for(i = 0; i <= j; i++) { if(xml_array[i] != NULL) xmlFreeDoc(xml_array[i]); } if(xml_array != NULL) pkg_free(xml_array); xmlFreeDoc(new_doc); return body; error: if (new_doc) xmlFreeDoc(new_doc); tmp_node = tuples_head; while(tmp_node) { tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } tmp_node = notes_head; while(tmp_node) { tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } tmp_node = persons_head; while(tmp_node) { tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } tmp_node = devices_head; while(tmp_node) { tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } tmp_node = others_head; while(tmp_node) { tmp_node2 = tmp_node; tmp_node = tmp_node->next; pkg_free(tmp_node2); } if(xml_array != NULL) { for(i = 0; i <= j; i++) { if(xml_array[i] != NULL) xmlFreeDoc(xml_array[i]); } pkg_free(xml_array); } if(body) pkg_free(body); return NULL; } #undef ADD_NODE str* dialog_agg_nbody(str* pres_user, str* pres_domain, str** body_array, int n, int off_index) { str* n_body = NULL; str* body = NULL; int status = OFFB_STATUS_OK; if(body_array == NULL) return NULL; if(off_index >= 0 && generate_offline_body) { body = body_array[off_index]; status = dialog_offline_body(body, &n_body); if (status != OFFB_STATUS_OK && status != OFFB_STATUS_NO_DIALOG) { LM_ERR("constructing offline body failed\n"); return NULL; } body_array[off_index] = n_body; } LM_DBG("[user]=%.*s [domain]= %.*s\n", pres_user->len, pres_user->s, pres_domain->len, pres_domain->s); n_body = agregate_dialog_xmls(pres_user, pres_domain, body_array, n); if(n_body == NULL && n != 0 && generate_offline_body != 0) { LM_ERR("while aggregating body\n"); } if(off_index >= 0 && generate_offline_body && status == OFFB_STATUS_OK) { xmlFree(body_array[off_index]->s); pkg_free(body_array[off_index]); body_array[off_index] = body; } return n_body; } str* presence_agg_nbody(str* pres_user, str* pres_domain, str** body_array, int n, int off_index) { str* n_body = NULL; str* body = NULL; int status = OFFB_STATUS_OK; if(body_array == NULL && !pidf_manipulation) return NULL; if(off_index >= 0 && generate_offline_body) { body = body_array[off_index]; status = presence_offline_body(body, &n_body); if (status != OFFB_STATUS_OK) { LM_ERR("constructing offline body failed\n"); return NULL; } body_array[off_index] = n_body; } LM_DBG("[user]=%.*s [domain]= %.*s\n", pres_user->len, pres_user->s, pres_domain->len, pres_domain->s); n_body = agregate_presence_xmls(pres_user, pres_domain, body_array, n); if(n_body == NULL && n != 0 && generate_offline_body != 0) { LM_ERR("while aggregating body\n"); } if(off_index >= 0 && generate_offline_body && status == OFFB_STATUS_OK) { xmlFree(body_array[off_index]->s); pkg_free(body_array[off_index]); body_array[off_index] = body; } return n_body; } int pres_apply_auth(str* notify_body, subs_t* subs, str** final_nbody) { xmlDocPtr doc= NULL; xmlNodePtr node= NULL; str* n_body= NULL; *final_nbody= NULL; if(force_active) return 0; if(subs->auth_rules_doc== NULL) { LM_ERR("NULL rules doc\n"); return -1; } doc= xmlParseMemory(subs->auth_rules_doc->s, subs->auth_rules_doc->len); if(doc== NULL) { LM_ERR("parsing xml doc\n"); return -1; } node= get_rule_node(subs, doc); if(node== NULL) { LM_DBG("The subscriber didn't match the conditions\n"); xmlFreeDoc(doc); return 0; } n_body= get_final_notify_body(subs, notify_body, node); if(n_body== NULL) { LM_ERR("in function get_final_notify_body\n"); xmlFreeDoc(doc); return -1; } xmlFreeDoc(doc); *final_nbody= n_body; return 1; } str* get_final_notify_body( subs_t *subs, str* notify_body, xmlNodePtr rule_node) { xmlNodePtr transf_node = NULL, node = NULL, dont_provide = NULL; xmlNodePtr doc_root = NULL, doc_node = NULL, provide_node = NULL; xmlNodePtr all_node = NULL; xmlDocPtr doc= NULL; char name[15]; char service_uri_scheme[10]; int i= 0, found = 0; str* new_body = NULL; char* class_cont = NULL, *occurence_ID= NULL, *service_uri= NULL; char* deviceID = NULL; char* content = NULL; char all_name[20]; strcpy(all_name, "all-"); new_body = (str*)pkg_malloc(sizeof(str)); if(new_body == NULL) { LM_ERR("while allocating memory\n"); return NULL; } memset(new_body, 0, sizeof(str)); doc = xmlParseMemory(notify_body->s, notify_body->len); if(doc== NULL) { LM_ERR("while parsing the xml body message\n[%.*s]\n", notify_body->len, notify_body->s); goto error; } doc_root = xmlDocGetNodeByName(doc,"presence", NULL); if(doc_root == NULL) { LM_ERR("while extracting the presence node\n"); goto error; } transf_node = xmlNodeGetChildByName(rule_node, "transformations"); if(transf_node == NULL) { LM_DBG("No transformations node found\n"); goto done; } for(node = transf_node->children; node; node = node->next ) { if(xmlStrcasecmp(node->name, (unsigned char*)"text")== 0) continue; LM_DBG("transf_node->name:%s\n",node->name); strcpy((char*)name ,(char*)(node->name + 8)); strcpy(all_name+4, name); if(xmlStrcasecmp((unsigned char*)name,(unsigned char*)"services") == 0) strcpy(name, "tuple"); if(strncmp((char*)name,"person", 6) == 0) name[6] = '\0'; doc_node = xmlNodeGetNodeByName(doc_root, name, NULL); if(doc_node == NULL) continue; LM_DBG("searched doc_node->name:%s\n",name); content = (char*)xmlNodeGetContent(node); if(content) { LM_DBG("content = %s\n", content); if(xmlStrcasecmp((unsigned char*)content, (unsigned char*) "FALSE") == 0) { LM_DBG("found content false\n"); while( doc_node ) { xmlUnlinkNode(doc_node); xmlFreeNode(doc_node); doc_node = xmlNodeGetChildByName(doc_root, name); } xmlFree(content); continue; } if(xmlStrcasecmp((unsigned char*)content, (unsigned char*) "TRUE") == 0) { LM_DBG("found content true\n"); xmlFree(content); continue; } xmlFree(content); } while (doc_node ) { if (xmlStrcasecmp(doc_node->name,(unsigned char*)"text")==0) { doc_node = doc_node->next; continue; } if (xmlStrcasecmp(doc_node->name,(unsigned char*)name)!=0) { break; } all_node = xmlNodeGetChildByName(node, all_name) ; if( all_node ) { LM_DBG("must provide all\n"); doc_node = doc_node->next; continue; } found = 0; class_cont = xmlNodeGetNodeContentByName(doc_node, "class", NULL); if(class_cont == NULL) LM_DBG("no class tag found\n"); else LM_DBG("found class = %s\n", class_cont); occurence_ID = xmlNodeGetAttrContentByName(doc_node, "id"); if(occurence_ID == NULL) LM_DBG("no id found\n"); else LM_DBG("found id = %s\n", occurence_ID); deviceID = xmlNodeGetNodeContentByName(doc_node, "deviceID", NULL); if(deviceID== NULL) LM_DBG("no deviceID found\n"); else LM_DBG("found deviceID = %s\n", deviceID); service_uri = xmlNodeGetNodeContentByName(doc_node, "contact", NULL); if(service_uri == NULL) LM_DBG("no service_uri found\n"); else LM_DBG("found service_uri = %s\n", service_uri); i = 0; if(service_uri!= NULL) { while(service_uri[i]!= ':') { service_uri_scheme[i] = service_uri[i]; i++; } service_uri_scheme[i] = '\0'; LM_DBG("service_uri_scheme: %s\n", service_uri_scheme); } provide_node = node->children; while ( provide_node!= NULL ) { if(xmlStrcasecmp(provide_node->name,(unsigned char*) "text")==0) { provide_node = provide_node->next; continue; } if(xmlStrcasecmp(provide_node->name,(unsigned char*)"class")== 0 && class_cont ) { content = (char*)xmlNodeGetContent(provide_node); if(content&& xmlStrcasecmp((unsigned char*)content, (unsigned char*)class_cont) == 0) { found = 1; LM_DBG("found class= %s", class_cont); xmlFree(content); break; } if(content) xmlFree(content); } if(xmlStrcasecmp(provide_node->name, (unsigned char*) "deviceID")==0&&deviceID ) { content = (char*)xmlNodeGetContent(provide_node); if(content && xmlStrcasecmp ((unsigned char*)content, (unsigned char*)deviceID) == 0) { found = 1; LM_DBG("found deviceID= %s", deviceID); xmlFree(content); break; } if(content) xmlFree(content); } if(xmlStrcasecmp(provide_node->name, (unsigned char*)"occurence-id")== 0&& occurence_ID) { content = (char*)xmlNodeGetContent(provide_node); if(content && xmlStrcasecmp ((unsigned char*)content, (unsigned char*)occurence_ID) == 0) { found = 1; LM_DBG("found occurenceID= %s\n", occurence_ID); xmlFree(content); break; } if(content) xmlFree(content); } if(xmlStrcasecmp(provide_node->name, (unsigned char*)"service-uri")== 0 && service_uri) { content = (char*)xmlNodeGetContent(provide_node); if(content&& xmlStrcasecmp ((unsigned char*)content, (unsigned char*)service_uri) == 0) { found = 1; LM_DBG("found service_uri= %s", service_uri); xmlFree(content); break; } if(content) xmlFree(content); } if(xmlStrcasecmp(provide_node->name, (unsigned char*)"service-uri-scheme")==0&& i) { content = (char*)xmlNodeGetContent(provide_node); LM_DBG("service_uri_scheme=%s\n",content); if(content && xmlStrcasecmp((unsigned char*)content, (unsigned char*)service_uri_scheme) == 0) { found = 1; LM_DBG("found service_uri_scheme= %s", service_uri_scheme); xmlFree(content); break; } if(content) xmlFree(content); } provide_node = provide_node->next; } if(found == 0) { LM_DBG("delete node: %s\n", doc_node->name); dont_provide = doc_node; doc_node = doc_node->next; xmlUnlinkNode(dont_provide); xmlFreeNode(dont_provide); } else doc_node = doc_node->next; } } done: xmlDocDumpMemory(doc,(xmlChar**)(void*)&new_body->s, &new_body->len); LM_DBG("body = \n%.*s\n", new_body->len, new_body->s); xmlFreeDoc(doc); xmlFree(class_cont); xmlFree(occurence_ID); xmlFree(deviceID); xmlFree(service_uri); return new_body; error: if(doc) xmlFreeDoc(doc); if(new_body) { if(new_body->s) xmlFree(new_body->s); pkg_free(new_body); } if(class_cont) xmlFree(class_cont); if(occurence_ID) xmlFree(occurence_ID); if(deviceID) xmlFree(deviceID); if(service_uri) xmlFree(service_uri); return NULL; } opensips-2.2.2/modules/presence_xml/notify_body.h000066400000000000000000000023541300170765700222210ustar00rootroot00000000000000/* * presence_xml module - * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-11 initial version (anca) */ #ifndef _NBODY_H_ #define _NBODY_H_ str* dialog_agg_nbody(str* pres_user, str* pres_domain, str** body_array, int n, int off_index); str* presence_agg_nbody(str* pres_user, str* pres_domain, str** body_array, int n, int off_index); int pres_apply_auth(str* notify_body, subs_t* subs, str** final_nbody); void free_xml_body(char* body); #endif opensips-2.2.2/modules/presence_xml/pidf.c000066400000000000000000000072311300170765700206100ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-14 initial version (anca) */ #define _XOPEN_SOURCE #include #include #include #include #include "../../dprint.h" #include "../../sr_module.h" #include "pidf.h" xmlAttrPtr xmlNodeGetAttrByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = node->properties; while (attr) { if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0) return attr; attr = attr->next; } return NULL; } char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = xmlNodeGetAttrByName(node, name); if (attr) return (char*)xmlNodeGetContent(attr->children); else return NULL; } xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name) { xmlNodePtr cur = node->children; while (cur) { if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) return cur; cur = cur->next; } return NULL; } xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns) { xmlNodePtr cur = node; while (cur) { xmlNodePtr match = NULL; if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) { if (!ns || (cur->ns && xmlStrcasecmp(cur->ns->prefix, (unsigned char*)ns) == 0)) return cur; } match = xmlNodeGetNodeByName(cur->children, name, ns); if (match) return match; cur = cur->next; } return NULL; } char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns) { xmlNodePtr node = xmlNodeGetNodeByName(root, name, ns); if (node) return (char*)xmlNodeGetContent(node->children); else return NULL; } xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns) { xmlNodePtr cur = doc->children; return xmlNodeGetNodeByName(cur, name, ns); } char *xmlDocGetNodeContentByName(xmlDocPtr doc, const char *name, const char *ns) { xmlNodePtr node = xmlDocGetNodeByName(doc, name, ns); if (node) return (char*)xmlNodeGetContent(node->children); else return NULL; } time_t xml_parse_dateTime(char* xml_time_str) { struct tm tm; char * p; int h, m; char h1, h2, m1, m2; int sign= 1; signed int timezone_diff= 0; p= strptime(xml_time_str, "%F", &tm); if(p== NULL) { printf("error: failed to parse time\n"); return 0; } p++; p= strptime(p, "%T", &tm); if(p== NULL) { printf("error: failed to parse time\n"); return 0; } if(*p== '\0') goto done; if(*p== '.') { p++; /* read the fractionar part of the seconds*/ while(*p!= '\0' && *p>= '0' && *p<= '9') { p++; } } if(*p== '\0') goto done; /* read time zone */ if(*p== 'Z') { goto done; } if(*p== '+') sign= -1; p++; sscanf(p, "%c%c:%c%c", &h1, &h2, &m1, &m2); h= (h1- '0')*10+ h2- '0'; m= (m1- '0')*10+ m2- '0'; timezone_diff= sign* ((m+ h* 60)* 60); done: return (mktime(&tm) + timezone_diff); } opensips-2.2.2/modules/presence_xml/pidf.h000066400000000000000000000027141300170765700206160ustar00rootroot00000000000000/* * presence module - presence server implementation * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-08-15 initial version (anca) */ #ifndef PIDF_H #define PIDF_H #include "../../str.h" #include xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns); xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns); xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name); char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns); char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name); time_t xml_parse_dateTime(char* xml_time_str); #endif opensips-2.2.2/modules/presence_xml/presence_xml.c000066400000000000000000000257351300170765700223630ustar00rootroot00000000000000/* * presence_xml module - Presence Handling XML bodies module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-12 initial version (anca) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../str.h" #include "../../ut.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../mem/mem.h" #include "../presence/bind_presence.h" #include "../presence/hash.h" #include "../presence/notify.h" #include "../signaling/signaling.h" #include "pidf.h" #include "add_events.h" #include "presence_xml.h" #define IETF_PRES_RULES_AUID "pres-rules" #define IETF_PRES_RULES_AUID_LEN sizeof(IETF_PRES_RULES_AUID)-1 #define OMA_PRES_RULES_AUID "org.openmobilealliance.pres-rules" #define OMA_PRES_RULES_AUID_LEN sizeof(OMA_PRES_RULES_AUID)-1 /** module functions */ static int mod_init(void); static int child_init(int); static void destroy(void); static int pxml_add_xcap_server( modparam_t type, void* val); static int shm_copy_xcap_list(void); static void free_xs_list(xcap_serv_t* xs_list, int mem_type); static int xcap_doc_updated(int doc_type, str xid, char* doc); /** module variables ***/ add_event_t pres_add_event; update_watchers_t pres_update_watchers; pres_get_sphere_t pres_get_sphere; int force_active= 0; int pidf_manipulation= 0; xcap_serv_t* xs_list= NULL; str pres_rules_auid = {0, 0}; str pres_rules_filename = {0, 0}; int generate_offline_body = 1; int pres_rules_doc_id = PRES_RULES; /* xcap API */ str db_url = {NULL, 0}; str xcap_table = {NULL, 0}; int integrated_xcap_server = 0; parse_xcap_uri_t xcapParseUri; normalize_sip_uri_t normalizeSipUri; get_xcap_doc_t xcapDbGetDoc; /* SIGNALING bind */ struct sig_binds xml_sigb; /* database connection */ db_con_t *pxml_db = NULL; db_func_t pxml_dbf; /* functions imported from xcap_client module */ xcapGetNewDoc_t xcap_GetNewDoc; static param_export_t params[]={ { "force_active", INT_PARAM, &force_active }, { "pidf_manipulation", INT_PARAM, &pidf_manipulation}, { "xcap_server", STR_PARAM|USE_FUNC_PARAM,(void*)pxml_add_xcap_server}, { "pres_rules_auid", STR_PARAM, &pres_rules_auid.s}, { "pres_rules_filename", STR_PARAM, &pres_rules_filename.s}, { "generate_offline_body", INT_PARAM, &generate_offline_body}, { 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "xcap", DEP_ABORT }, { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_DEFAULT, "presence", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "presence_xml", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; static int verify_db(void) { /* binding to mysql module */ if (db_bind_mod(&db_url, &pxml_dbf)) { LM_ERR("Database module not found\n"); return -1; } if (!DB_CAPABILITY(pxml_dbf, DB_CAP_ALL)) { LM_ERR("Database module does not implement all functions" " needed by the module\n"); return -1; } pxml_db = pxml_dbf.init(&db_url); if (!pxml_db) { LM_ERR("while connecting to database\n"); return -1; } /* pxml_db is free'd by caller later, not sure if safe to do now */ return 0; } /** * init module function */ static int mod_init(void) { bind_presence_t bind_presence; presence_api_t pres; bind_xcap_t bind_xcap; xcap_api_t xcap_api; /* load XCAP API */ bind_xcap = (bind_xcap_t)find_export("bind_xcap", 1, 0); if (!bind_xcap) { LM_ERR("Can't bind xcap\n"); return -1; } if (bind_xcap(&xcap_api) < 0) { LM_ERR("Can't bind xcap\n"); return -1; } integrated_xcap_server = xcap_api.integrated_server; db_url = xcap_api.db_url; xcap_table = xcap_api.xcap_table; normalizeSipUri = xcap_api.normalize_sip_uri; xcapParseUri = xcap_api.parse_xcap_uri; xcapDbGetDoc = xcap_api.get_xcap_doc; if(force_active==0) { if ( verify_db() < 0 ) return -1; } /* load SL API */ if(load_sig_api(&xml_sigb)==-1) { LM_ERR("can't load signaling functions\n"); return -1; } bind_presence= (bind_presence_t)find_export("bind_presence", 1,0); if (!bind_presence) { LM_ERR("Can't bind presence\n"); return -1; } if (bind_presence(&pres) < 0) { LM_ERR("Can't bind module pua\n"); return -1; } pres_get_sphere= pres.get_sphere; pres_add_event= pres.add_event; pres_update_watchers= pres.update_watchers_status; if (pres_add_event == NULL || pres_update_watchers== NULL) { LM_ERR("Can't import add_event\n"); return -1; } if(xml_add_events()< 0) { LM_ERR("adding xml events\n"); return -1; } if(pres_rules_auid.s) { pres_rules_auid.len = strlen(pres_rules_auid.s); if (pres_rules_auid.len == IETF_PRES_RULES_AUID_LEN && strncmp(pres_rules_auid.s, IETF_PRES_RULES_AUID, IETF_PRES_RULES_AUID_LEN) == 0) { LM_INFO("using IETF mode for pres-rules\n"); pres_rules_doc_id = PRES_RULES; } if (pres_rules_auid.len == OMA_PRES_RULES_AUID_LEN && strncmp(pres_rules_auid.s, OMA_PRES_RULES_AUID, OMA_PRES_RULES_AUID_LEN) == 0) { LM_INFO("using OMA mode for pres-rules\n"); pres_rules_doc_id = OMA_PRES_RULES; } else { LM_ERR("unrecognized AUID for pres-rules: %.*s\n", pres_rules_auid.len, pres_rules_auid.s); return -1; } } if(force_active== 0 && !integrated_xcap_server ) { xcap_client_api_t xcap_client_api; bind_xcap_client_t bind_xcap_client; /* bind xcap */ bind_xcap_client = (bind_xcap_client_t)find_export("bind_xcap_client", 1, 0); if (!bind_xcap_client) { LM_ERR("Can't bind xcap_client\n"); return -1; } if (bind_xcap_client(&xcap_client_api) < 0) { LM_ERR("Can't bind xcap_client_api\n"); return -1; } xcap_GetNewDoc= xcap_client_api.getNewDoc; if(xcap_GetNewDoc== NULL) { LM_ERR("can't import getNewDoc from xcap_client module\n"); return -1; } if(xcap_client_api.register_xcb(pres_rules_doc_id, xcap_doc_updated) < 0) { LM_ERR("registering xcap callback function\n"); return -1; } if(pres_rules_filename.s) pres_rules_filename.len = strlen(pres_rules_filename.s); } if(shm_copy_xcap_list()< 0) { LM_ERR("copying xcap server list in share memory\n"); return -1; } if(pxml_db) pxml_dbf.close(pxml_db); pxml_db = NULL; return 0; } static int child_init(int rank) { LM_DBG("[%d] pid [%d]\n", rank, getpid()); if(force_active==0) { if (pxml_dbf.init==0) { LM_CRIT("database not bound\n"); return -1; } pxml_db = pxml_dbf.init(&db_url); if (pxml_db== NULL) { LM_ERR("child %d: ERROR while connecting database\n",rank); return -1; } LM_DBG("child %d: Database connection opened successfully\n",rank); } return 0; } static void destroy(void) { LM_DBG("start\n"); if(pxml_db && pxml_dbf.close) pxml_dbf.close(pxml_db); free_xs_list(xs_list, SHM_MEM_TYPE); return ; } static int pxml_add_xcap_server( modparam_t type, void* val) { xcap_serv_t* xs; int size; char* serv_addr= (char*)val; char* sep= NULL; unsigned int port= 80; str serv_addr_str; serv_addr_str.s= serv_addr; serv_addr_str.len= strlen(serv_addr); sep= strchr(serv_addr, ':'); if(sep) { char* sep2= NULL; str port_str; sep2= strchr(sep+ 1, ':'); if(sep2) sep= sep2; port_str.s= sep+ 1; port_str.len= serv_addr_str.len- (port_str.s- serv_addr); if(str2int(&port_str, &port)< 0) { LM_ERR("while converting string to int\n"); goto error; } if(port> 65535) { LM_ERR("wrong port number\n"); goto error; } *sep = '\0'; serv_addr_str.len= sep- serv_addr; } size= sizeof(xcap_serv_t)+ (serv_addr_str.len+ 1)* sizeof(char); xs= (xcap_serv_t*)pkg_malloc(size); if(xs== NULL) { ERR_MEM(PKG_MEM_STR); } memset(xs, 0, size); size= sizeof(xcap_serv_t); xs->addr= (char*)xs+ size; strcpy(xs->addr, serv_addr); xs->port= port; /* check for duplicates */ xs->next= xs_list; xs_list= xs; return 0; error: free_xs_list(xs_list, PKG_MEM_TYPE); return -1; } static int shm_copy_xcap_list(void) { xcap_serv_t* xs, *shm_xs, *prev_xs; int size; xs= xs_list; if(xs== NULL) { if(force_active== 0 && !integrated_xcap_server) { LM_ERR("no xcap_server parameter set\n"); return -1; } return 0; } xs_list= NULL; size= sizeof(xcap_serv_t); while(xs) { size+= (strlen(xs->addr)+ 1)* sizeof(char); shm_xs= (xcap_serv_t*)shm_malloc(size); if(shm_xs== NULL) { ERR_MEM(SHARE_MEM); } memset(shm_xs, 0, size); size= sizeof(xcap_serv_t); shm_xs->addr= (char*)shm_xs+ size; strcpy(shm_xs->addr, xs->addr); shm_xs->port= xs->port; shm_xs->next= xs_list; xs_list= shm_xs; prev_xs= xs; xs= xs->next; pkg_free(prev_xs); } return 0; error: free_xs_list(xs_list, SHM_MEM_TYPE); return -1; } static void free_xs_list(xcap_serv_t* xsl, int mem_type) { xcap_serv_t* xs, *prev_xs; xs= xsl; while(xs) { prev_xs= xs; xs= xs->next; if(mem_type == SHM_MEM_TYPE) shm_free(prev_xs); else pkg_free(prev_xs); } xsl= NULL; } static int xcap_doc_updated(int doc_type, str xid, char* doc) { pres_ev_t ev; str rules_doc; /* call updating watchers */ ev.name.s= "presence"; ev.name.len= PRES_LEN; rules_doc.s= doc; rules_doc.len= strlen(doc); if(pres_update_watchers(xid, &ev, &rules_doc)< 0) { LM_ERR("updating watchers in presence\n"); return -1; } return 0; } opensips-2.2.2/modules/presence_xml/presence_xml.h000066400000000000000000000036021300170765700223550ustar00rootroot00000000000000/* * presence_xml module - Presence Handling XML bodies module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-18 initial version (anca) */ #ifndef _PRES_XML_H_ #define _PRES_XML_H_ #include "../../db/db.h" #include "../signaling/signaling.h" #include "../presence/event_list.h" #include "../presence/presence.h" #include "../presence/presentity.h" #include "../xcap/api.h" #include "../xcap_client/xcap_functions.h" typedef struct xcap_serv { char* addr; unsigned int port; struct xcap_serv* next; }xcap_serv_t; extern add_event_t pres_add_event; extern db_con_t *pxml_db; extern db_func_t pxml_dbf; extern int force_active; extern int pidf_manipulation; extern xcap_serv_t* xs_list; extern xcapGetNewDoc_t xcap_GetNewDoc; extern pres_get_sphere_t pres_get_sphere; extern struct sig_binds xml_sigb; extern str pres_rules_auid; extern str pres_rules_filename; extern int generate_offline_body; extern int pres_rules_doc_id; extern str xcap_table; extern int integrated_xcap_server; extern normalize_sip_uri_t normalizeSipUri; extern parse_xcap_uri_t xcapParseUri; extern get_xcap_doc_t xcapDbGetDoc; #endif opensips-2.2.2/modules/presence_xml/xcap_auth.c000066400000000000000000001066031300170765700216450ustar00rootroot00000000000000/* * presence_xml module - * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-11 initial version (Anca Vamanu) */ #include #include #include #include #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../parser/parse_uri.h" #include "../presence/utils_func.h" #include "../presence/hash.h" #include "presence_xml.h" #include "xcap_auth.h" #include "pidf.h" str str_username_col = str_init("username"); str str_domain_col = str_init("domain"); str str_doc_type_col = str_init("doc_type"); str str_doc_col = str_init("doc"); str str_doc_uri_col = str_init("doc_uri"); static void ietf_get_rules(subs_t* subs, xmlDocPtr xcap_tree, xcap_rule_t **rules) { str w_uri= {0, 0}; char* id = NULL, *domain = NULL, *time_cont= NULL; int apply_rule = -1; xmlNodePtr ruleset_node = NULL, node1= NULL, node2= NULL; xmlNodePtr cond_node = NULL, except_node = NULL; xmlNodePtr identity_node = NULL, sphere_node = NULL; xmlNodePtr iden_child; xmlNodePtr validity_node, time_node; time_t t_init, t_fin, t; int valid= 0; xcap_rule_t *rule = NULL; *rules = NULL; uandd_to_uri(subs->from_user, subs->from_domain, &w_uri); if(w_uri.s == NULL) { LM_ERR("while creating uri\n"); return; } ruleset_node = xmlDocGetNodeByName(xcap_tree, "ruleset", NULL); if(ruleset_node == NULL) { LM_DBG("ruleset_node NULL\n"); goto error; } for(node1 = ruleset_node->children ; node1; node1 = node1->next) { if(xmlStrcasecmp(node1->name, (unsigned char*)"text")==0 ) continue; /* process conditions */ LM_DBG("node1->name= %s\n", node1->name); cond_node = xmlNodeGetChildByName(node1, "conditions"); if(cond_node == NULL) { LM_DBG("cond node NULL\n"); goto error; } LM_DBG("cond_node->name= %s\n", cond_node->name); validity_node = xmlNodeGetChildByName(cond_node, "validity"); if(validity_node !=NULL) { LM_DBG("found validity tag\n"); t= time(NULL); /* search all from-until pair */ for(time_node= validity_node->children; time_node; time_node= time_node->next) { if(xmlStrcasecmp(time_node->name, (unsigned char*)"from")!= 0) { continue; } time_cont= (char*)xmlNodeGetContent(time_node); t_init= xml_parse_dateTime(time_cont); xmlFree(time_cont); if(t_init< 0) { LM_ERR("failed to parse xml dateTime\n"); goto error; } if(t< t_init) { LM_DBG("the lower time limit is not respected\n"); continue; } time_node= time_node->next; while(1) { if(time_node== NULL) { LM_ERR("bad formatted xml doc:until child not found in" " validity pair\n"); goto error; } if( xmlStrcasecmp(time_node->name, (unsigned char*)"until")== 0) break; time_node= time_node->next; } time_cont= (char*)xmlNodeGetContent(time_node); t_fin= xml_parse_dateTime(time_cont); xmlFree(time_cont); if(t_fin< 0) { LM_ERR("failed to parse xml dateTime\n"); goto error; } if(t <= t_fin) { LM_DBG("the rule is active at this time\n"); valid= 1; } } if(!valid) { LM_DBG("the rule is not active at this time\n"); continue; } } sphere_node = xmlNodeGetChildByName(cond_node, "sphere"); if(sphere_node!= NULL) { /* check to see if matches presentity current sphere */ /* ask presence for sphere information */ char* sphere= pres_get_sphere(&subs->pres_uri); if(sphere) { char* attr= (char*)xmlNodeGetContent(sphere_node); if(xmlStrcasecmp((unsigned char*)attr, (unsigned char*)sphere)!= 0) { LM_DBG("sphere condition not respected\n"); pkg_free(sphere); xmlFree(attr); continue; } pkg_free(sphere); xmlFree(attr); } else { LM_DBG("Noo sphere definition found\n"); continue; } /* if the user has not define a sphere * consider the condition false*/ } identity_node = xmlNodeGetChildByName(cond_node, "identity"); if(identity_node == NULL) { LM_ERR("didn't find identity tag\n"); goto error; } iden_child= xmlNodeGetChildByName(identity_node, "one"); if(iden_child) { for(node2 = identity_node->children; node2; node2 = node2->next) { if(xmlStrcasecmp(node2->name, (unsigned char*)"one")!= 0) continue; id = xmlNodeGetAttrContentByName(node2, "id"); if(id== NULL) { LM_ERR("while extracting attribute\n"); goto error; } if((strlen(id)== w_uri.len && (strncmp(id, w_uri.s, w_uri.len)==0))) { apply_rule = 1; xmlFree(id); break; } xmlFree(id); } } /* search for many node*/ iden_child= xmlNodeGetChildByName(identity_node, "many"); if(iden_child) { domain = NULL; for(node2 = identity_node->children; node2; node2 = node2->next) { if(xmlStrcasecmp(node2->name, (unsigned char*)"many")!= 0) continue; domain = xmlNodeGetAttrContentByName(node2, "domain"); if(domain == NULL) { LM_DBG("No domain attribute to many\n"); } else { LM_DBG("\n", domain); if((strlen(domain)!= subs->from_domain.len && strncmp(domain, subs->from_domain.s, subs->from_domain.len) )) { xmlFree(domain); continue; } } xmlFree(domain); apply_rule = 1; if(node2->children == NULL) /* there is no exception */ break; for(except_node = node2->children; except_node; except_node= except_node->next) { if(xmlStrcasecmp(except_node->name, (unsigned char*)"except")) continue; id = xmlNodeGetAttrContentByName(except_node, "id"); if(id!=NULL) { if((strlen(id)- 1== w_uri.len && (strncmp(id, w_uri.s, w_uri.len)==0))) { xmlFree(id); apply_rule = 0; break; } xmlFree(id); } else { domain = NULL; domain = xmlNodeGetAttrContentByName(except_node, "domain"); if(domain!=NULL) { LM_DBG("Found except domain= %s\n- strlen(domain)= %d\n", domain, (int)strlen(domain)); if(strlen(domain)==subs->from_domain.len && (strncmp(domain,subs->from_domain.s , subs->from_domain.len)==0)) { LM_DBG("except domain match\n"); xmlFree(domain); apply_rule = 0; break; } xmlFree(domain); } } } if(apply_rule== 1) /* if a match was found no need to keep searching*/ break; } } if(apply_rule ==1) break; } LM_DBG("apply_rule= %d\n", apply_rule); if(w_uri.s!=NULL) pkg_free(w_uri.s); if( !apply_rule || !node1) return; rule = (xcap_rule_t *)pkg_malloc(sizeof(*rule)); if (rule == NULL) { LM_ERR("cannot allocate pkg_mem\n"); return; } /* TODO: in IETF mode only the first matching rule is returned */ rule->node = node1; rule->next = NULL; *rules = rule; return; error: if(w_uri.s) pkg_free(w_uri.s); } /* OMA mode auth handling */ static inline int oma_match_identity_condition(xmlNodePtr condition, subs_t *subs, str *w_uri) { int r = 0, many_match = 0; char *domain = NULL; str uri; str *normalized_uri; xmlNodePtr node = NULL, except_node = NULL; for(node = condition->children; node; node = node->next) { if(xmlStrcasecmp(node->name, (unsigned char*)"one") == 0) { uri.s = xmlNodeGetAttrContentByName(node, "id"); if(uri.s == NULL) { LM_ERR("when extracting entry attribute\n"); continue; } uri.len = strlen(uri.s); normalized_uri = normalizeSipUri(&uri); if (normalized_uri->s == NULL || normalized_uri->len == 0) { LM_ERR("normalizing URI\n"); xmlFree(uri.s); continue; } xmlFree(uri.s); if (normalized_uri->len == w_uri->len && strncmp(normalized_uri->s, w_uri->s, w_uri->len) == 0) { r = 1; break; } } else if(xmlStrcasecmp(node->name, (unsigned char*)"many") == 0) { domain = xmlNodeGetAttrContentByName(node, "domain"); if(domain == NULL) { LM_DBG("No domain attribute in identity many\n"); } else { LM_DBG("\n", domain); if(!(strlen(domain) == subs->from_domain.len && strncmp(domain, subs->from_domain.s, subs->from_domain.len) == 0)) { xmlFree(domain); continue; } xmlFree(domain); } many_match = 1; for(except_node = node->children; except_node; except_node= except_node->next) { if(xmlStrcasecmp(except_node->name, (unsigned char*)"except")) continue; uri.s = xmlNodeGetAttrContentByName(except_node, "id"); if(uri.s != NULL) { uri.len = strlen(uri.s); normalized_uri = normalizeSipUri(&uri); if (normalized_uri->s == NULL || normalized_uri->len == 0) { LM_ERR("normalizing URI\n"); xmlFree(uri.s); continue; } xmlFree(uri.s); if (normalized_uri->len == w_uri->len && strncmp(normalized_uri->s, w_uri->s, w_uri->len) == 0) { many_match = 0; break; } } else { domain = NULL; domain = xmlNodeGetAttrContentByName(except_node, "domain"); if(domain != NULL) { LM_DBG("Found except domain= %s\n- strlen(domain)= %d\n", domain, (int)strlen(domain)); if(strlen(domain)==subs->from_domain.len && (strncmp(domain,subs->from_domain.s , subs->from_domain.len)==0)) { LM_DBG("except domain match\n"); xmlFree(domain); many_match = 0; break; } xmlFree(domain); } } } if(many_match) { r = 1; break; } } } return r; } #define MAX_PATH_LEN 127 static inline int get_resource_list(str *username, str *domain, str *filename, str *selector, xmlNodePtr *rl_node, xmlDocPtr *xmldoc) { static char path_buf[MAX_PATH_LEN+1]; int checked = 0; str path; str *doc = NULL; str *etag = NULL; xmlXPathContextPtr xpathCtx = NULL; xmlXPathObjectPtr xpathObj = NULL; if (filename==NULL || username==NULL || domain==NULL) { LM_ERR("invalid parameters\n"); return -1; } if (xcapDbGetDoc(username, domain, RESOURCE_LISTS, filename, NULL, &doc, &etag) < 0) { LM_DBG("No rl document found\n"); return -1; } LM_DBG("rl document:\n%.*s\n", doc->len, doc->s); path.s = path_buf; path.len = 0; if (selector->s) { while (checked < selector->len && path.len <= MAX_PATH_LEN) { if (selector->s[checked] == '/') { memcpy(path.s+path.len, "/xmlns:", 7); path.len += 7; } else { path.s[path.len++] = selector->s[checked]; } checked++; } path.s[path.len] = '\0'; LM_DBG("path: %.*s", path.len, path.s); } *xmldoc = xmlParseMemory(doc->s, doc->len); if (*xmldoc == NULL) { LM_ERR("while parsing XML memory\n"); goto error; } if(path.len == 0) { LM_ERR("no path specified\n"); goto error; } /* TODO: move this to xcap module? */ xpathCtx = xmlXPathNewContext(*xmldoc); if (xpathCtx == NULL) { LM_ERR("unable to create new XPath context"); goto error; } if (xmlXPathRegisterNs(xpathCtx, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:resource-lists") != 0) { LM_ERR("unable to register xmlns\n"); goto error; } xpathObj = xmlXPathEvalExpression(BAD_CAST path.s, xpathCtx); if (xpathObj == NULL) { LM_ERR("unable to evaluate path\n"); goto error; } if (xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr <= 0) { LM_ERR("no nodes found\n"); goto error; } if (xpathObj->nodesetval->nodeTab[0] != NULL && xpathObj->nodesetval->nodeTab[0]->type != XML_ELEMENT_NODE) { LM_ERR("no nodes of the correct type found\n"); goto error; } *rl_node = xpathObj->nodesetval->nodeTab[0]; xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); pkg_free(doc->s); pkg_free(doc); pkg_free(etag->s); pkg_free(etag); return 0; error: if (doc != NULL) { if (doc->s != NULL) pkg_free(doc->s); pkg_free(doc); } if (etag != NULL) { if (etag->s != NULL) pkg_free(etag->s); pkg_free(etag); } if (xpathObj) xmlXPathFreeObject(xpathObj); if (xpathCtx) xmlXPathFreeContext(xpathCtx); if (*xmldoc) xmlFreeDoc(*xmldoc); return -1; } static inline int oma_match_external_list_condition(xmlNodePtr condition, subs_t *subs, str *w_uri) { int found = 0; str anchor, uri; str *normalized_uri; struct sip_uri sip_uri; xcap_uri_t anchor_uri; xmlNodePtr entry_node = NULL; xmlNodePtr rl_node = NULL, rl_entry = NULL; xmlDocPtr rl_doc = NULL; if(!integrated_xcap_server) { LM_ERR(" is not supported in non integrated mode\n"); return 0; } if(parse_uri(subs->pres_uri.s, subs->pres_uri.len, &sip_uri) < 0) { LM_ERR("failed to parse uri\n"); return 0; } for(entry_node = condition->children; entry_node; entry_node = entry_node->next) { if(xmlStrcasecmp(entry_node->name, (unsigned char*)"entry") != 0) continue; rl_node = NULL; rl_doc = NULL; anchor.s = NULL; uri.s = NULL; anchor.s = xmlNodeGetAttrContentByName(entry_node, "anc"); if(anchor.s == NULL) { LM_ERR("cannot get external-list entry anchor\n"); continue; } anchor.len = strlen(anchor.s); if(xcapParseUri(&anchor, &anchor_uri) != 0) { LM_ERR("unable to parse URI for external-list entry anchor\n"); xmlFree(anchor.s); continue; } xmlFree(anchor.s); /* TODO: validate XUI? */ if(get_resource_list(&sip_uri.user, &sip_uri.host, &anchor_uri.filename, &anchor_uri.selector, &rl_node, &rl_doc) < 0) { LM_ERR("error getting resource-list list pointed by external list anchor\n"); continue; } for(rl_entry = rl_node->children; rl_entry; rl_entry = rl_entry->next) { if(xmlStrcasecmp(rl_entry->name, (unsigned char*)"entry") != 0) continue; uri.s = xmlNodeGetAttrContentByName(rl_entry, "uri"); if(uri.s == NULL) { LM_ERR("when extracting entry uri attribute\n"); continue; } uri.len = strlen(uri.s); normalized_uri = normalizeSipUri(&uri); if (normalized_uri->s == NULL || normalized_uri->len == 0) { LM_ERR("normalizing URI\n"); xmlFree(uri.s); continue; } xmlFree(uri.s); if (normalized_uri->len == w_uri->len && strncmp(normalized_uri->s, w_uri->s, w_uri->len) == 0) { found = 1; break; } } xmlFreeDoc(rl_doc); if (found) break; } return found; } static inline int oma_match_anonymous_condition(xmlNodePtr condition, subs_t *subs, str *w_uri) { if (strncmp(subs->from_user.s , "anonymous", subs->from_user.len)==0 && strncmp(subs->from_domain.s , "anonymous.invalid", subs->from_domain.len)==0) return 1; return 0; } static inline void free_rules(xcap_rule_t *rules) { xcap_rule_t *ptr = NULL, *current = NULL; ptr = rules; while (ptr) { current = ptr; ptr = ptr->next; pkg_free(current); } } static void oma_get_rules(subs_t* subs, xmlDocPtr xcap_tree, xcap_rule_t **rules) { int apply_rule = 0, current_node_type = -1; str w_uri = {0, 0}; xmlNodePtr ruleset_node = NULL, cond_node = NULL; xmlNodePtr node1 = NULL, node2 = NULL, current_node = NULL; xcap_rule_t *tmp_rule = NULL; xcap_rule_t *identity_rules = NULL, *external_rules = NULL, *anonymous_rules = NULL, *other_identity_rules = NULL; xcap_rule_t *identity_tail = NULL, *external_tail = NULL, *anonymous_tail = NULL, *other_identity_tail = NULL; *rules = NULL; uandd_to_uri(subs->from_user, subs->from_domain, &w_uri); if(w_uri.s == NULL) { LM_ERR("while creating uri\n"); return; } ruleset_node = xmlDocGetNodeByName(xcap_tree, "ruleset", NULL); if(ruleset_node == NULL) { LM_ERR("ruleset_node not found\n"); pkg_free(w_uri.s); return; } for(node1 = ruleset_node->children; node1; node1 = node1->next) { if(xmlStrcasecmp(node1->name, (unsigned char*)"text")==0) continue; cond_node = xmlNodeGetChildByName(node1, "conditions"); if(cond_node == NULL) { LM_WARN("condition node not found\n"); continue; } apply_rule = 0; current_node = node1; current_node_type = -1; for(node2 = cond_node->children; node2; node2 = node2->next) { if(xmlStrcasecmp(node2->name, (unsigned char*)"identity") == 0) { current_node_type = IDENTITY_RULE; apply_rule = oma_match_identity_condition(node2, subs, &w_uri); break; } else if(xmlStrcasecmp(node2->name, (unsigned char*)"external-list") == 0) { current_node_type = EXTERNAL_LIST_RULE; apply_rule = oma_match_external_list_condition(node2, subs, &w_uri); break; } else if(xmlStrcasecmp(node2->name, (unsigned char*)"anonymous-request") == 0) { current_node_type = ANONYMOUS_REQUEST_RULE; apply_rule = oma_match_anonymous_condition(node2, subs, &w_uri); break; } else if(xmlStrcasecmp(node2->name, (unsigned char*)"other-identity") == 0) { current_node_type = OTHER_IDENTITY_RULE; apply_rule = 1; break; } else { /* unknown condition */ continue; } } /* finished scanning all conditions for a given rule */ if (apply_rule) { tmp_rule = (xcap_rule_t *)pkg_malloc(sizeof(*tmp_rule)); if (tmp_rule == NULL) { LM_ERR("pkg mem\n"); goto error; } tmp_rule->node = current_node; tmp_rule->next = NULL; switch (current_node_type) { case IDENTITY_RULE: if(identity_rules == NULL) identity_rules = tmp_rule; else identity_tail->next = tmp_rule; identity_tail = tmp_rule; break; case EXTERNAL_LIST_RULE: if(external_rules == NULL) external_rules = tmp_rule; else external_tail->next = tmp_rule; external_tail = tmp_rule; break; case ANONYMOUS_REQUEST_RULE: if(anonymous_rules == NULL) anonymous_rules = tmp_rule; else anonymous_tail->next = tmp_rule; anonymous_tail = tmp_rule; break; case OTHER_IDENTITY_RULE: if(other_identity_rules == NULL) other_identity_rules = tmp_rule; else other_identity_tail->next = tmp_rule; other_identity_tail = tmp_rule; break; default: /* this will never happen */ break; } } } if (anonymous_rules) { *rules = anonymous_rules; free_rules(identity_rules); free_rules(external_rules); free_rules(other_identity_rules); } else if (identity_rules) { *rules = identity_rules; free_rules(external_rules); free_rules(anonymous_rules); free_rules(other_identity_rules); } else if (external_rules) { *rules = external_rules; free_rules(identity_rules); free_rules(anonymous_rules); free_rules(other_identity_rules); } else if (other_identity_rules) { *rules = other_identity_rules; free_rules(identity_rules); free_rules(external_rules); free_rules(anonymous_rules); } else { *rules = NULL; LM_DBG("no matching rules found\n"); } pkg_free(w_uri.s); return; error: if (w_uri.s) pkg_free(w_uri.s); free_rules(identity_rules); free_rules(external_rules); free_rules(anonymous_rules); free_rules(other_identity_rules); } static inline int get_action_value(char *action) { if(strncmp(action, "block",5 )==0) return SH_ACTION_BLOCK; if(strncmp(action, "confirm",7 )==0) return SH_ACTION_CONFIRM; if(strncmp(action, "polite-block",12 )==0) return SH_ACTION_POLITE_BLOCK; if(strncmp(action, "allow",5 )==0) return SH_ACTION_ALLOW; return -1; } xmlNodePtr get_rule_node(subs_t* subs, xmlDocPtr xcap_tree) { int action_value = -1, max_action_value = -1; char* sub_handling = NULL; xmlNodePtr node = NULL, actions_node = NULL, sub_handling_node = NULL; xcap_rule_t *rules = NULL, *rule_ptr = NULL; if (pres_rules_doc_id == OMA_PRES_RULES) oma_get_rules(subs, xcap_tree, &rules); else ietf_get_rules(subs, xcap_tree, &rules); if (rules == NULL) return NULL; for (rule_ptr = rules; rule_ptr; rule_ptr = rule_ptr->next) { actions_node = xmlNodeGetChildByName(rule_ptr->node, "actions"); if(actions_node == NULL) { LM_DBG("actions_node NULL\n"); continue; } sub_handling_node = xmlNodeGetChildByName(actions_node, "sub-handling"); if(sub_handling_node == NULL) { LM_DBG("sub_handling_node NULL\n"); xmlFree(sub_handling); continue; } sub_handling = (char*)xmlNodeGetContent(sub_handling_node); if(sub_handling == NULL) { LM_ERR("Couldn't get sub-handling content\n"); continue; } LM_DBG("sub_handling_node->content= %s\n", sub_handling); action_value = get_action_value((char*)sub_handling); if (action_value > max_action_value) { max_action_value = action_value; node = rule_ptr->node; } xmlFree(sub_handling); } free_rules(rules); return node; } static char* subs_strstatus(subs_t* subs) { static char buf[64]; int len = 0; switch(subs->status) { case ACTIVE_STATUS: memcpy(buf, "active", 6); len += 6; break; case PENDING_STATUS: memcpy(buf, "pending", 7); len += 7; break; case TERMINATED_STATUS: memcpy(buf, "terminated", 10); len += 10; break; case WAITING_STATUS: memcpy(buf, "waiting", 7); len += 7; break; default: memcpy(buf, "unknown", 7); len += 7; break; } if (subs->reason.s != NULL) { sprintf(buf+len, " (%.*s)", subs->reason.len, subs->reason.s); len += subs->reason.len+3; } buf[len] = '\0'; return buf; } int pres_watcher_allowed(subs_t* subs) { xmlDocPtr xcap_tree = NULL; xmlNodePtr node = NULL, actions_node = NULL, sub_handling_node = NULL; char* sub_handling = NULL; int action_value = -1, ret = 0; str watcher = {0, 0}; uandd_to_uri(subs->from_user, subs->from_domain, &watcher); if(watcher.s == NULL) { LM_ERR("while creating uri\n"); return -1; } /* if force_active set status to active*/ if(force_active) { subs->status = ACTIVE_STATUS; subs->reason.s = NULL; subs->reason.len = 0; ret = 0; goto done; } if(subs->auth_rules_doc == NULL) { subs->status = PENDING_STATUS; subs->reason.s = NULL; subs->reason.len = 0; ret = 0; goto done; } xcap_tree = xmlParseMemory(subs->auth_rules_doc->s, subs->auth_rules_doc->len); if(xcap_tree == NULL) { LM_ERR("parsing xml memory\n"); ret = -1; goto done; } node = get_rule_node(subs, xcap_tree); if(node == NULL) { /* if no rule node was found and the previous state was active -> set the * state to terminated with reason deactivated */ if(subs->status != PENDING_STATUS) { subs->status = TERMINATED_STATUS; subs->reason.s = "deactivated"; subs->reason.len = 11; } ret = 0; goto done; } /* If node is not NULL then there should be a actions element and a sub-handling element * for sure, get_rule_node makes sure of that */ actions_node = xmlNodeGetChildByName(node, "actions"); if (actions_node == NULL) { ret = -1; goto done; } sub_handling_node = xmlNodeGetChildByName(actions_node, "sub-handling"); if (sub_handling_node == NULL) { ret = -1; goto done; } sub_handling = (char*)xmlNodeGetContent(sub_handling_node); if (sub_handling == NULL) { ret = -1; goto done; } action_value = get_action_value(sub_handling); switch (action_value) { case SH_ACTION_BLOCK: subs->status = TERMINATED_STATUS; subs->reason.s = "rejected"; subs->reason.len = 8; break; case SH_ACTION_CONFIRM: subs->status = PENDING_STATUS; subs->reason.s = NULL; subs->reason.len = 0; break; case SH_ACTION_POLITE_BLOCK: subs->status = ACTIVE_STATUS; subs->reason.s = "polite-block"; subs->reason.len = 12; break; case SH_ACTION_ALLOW: subs->status = ACTIVE_STATUS; subs->reason.s = NULL; subs->reason.len = 0; break; default: LM_ERR("unknown subscription handling action\n"); subs->status = PENDING_STATUS; subs->reason.s = NULL; subs->reason.len = 0; break; } LM_INFO("Subscription from %.*s to %.*s is %s\n", watcher.len, watcher.s, subs->pres_uri.len, subs->pres_uri.s, subs_strstatus(subs)); done: if (watcher.s) pkg_free(watcher.s); if (sub_handling) xmlFree(sub_handling); xmlFreeDoc(xcap_tree); return ret; } int pres_get_rules_doc(str* user, str* domain, str** rules_doc) { if(force_active) { *rules_doc = NULL; return 0; } return p_get_xcap_doc(user, domain, pres_rules_doc_id, rules_doc); } static int http_get_xcap_doc(str* user, str* domain, int type, str** doc) { str body; str *doc_tmp; xcap_doc_sel_t doc_sel; xcap_serv_t* xs; xcap_get_req_t req; *doc = NULL; if (type != PRES_RULES && type != OMA_PRES_RULES) { LM_ERR("only pres-rules documents can be fetched though HTTP for now\n"); goto error; } memset(&req, 0, sizeof(xcap_get_req_t)); if(uandd_to_uri(*user, *domain, &doc_sel.xid) < 0) { LM_ERR("constructing uri\n"); goto error; } if(pres_rules_auid.s && pres_rules_auid.len) { doc_sel.auid = pres_rules_auid; } else { doc_sel.auid.s = "pres-rules"; doc_sel.auid.len = strlen(doc_sel.auid.s); } doc_sel.doc_type = pres_rules_doc_id; doc_sel.type = USERS_TYPE; if(pres_rules_filename.s && pres_rules_filename.len) { doc_sel.filename = pres_rules_filename; } else { doc_sel.filename.s = "index"; doc_sel.filename.len = strlen(doc_sel.filename.s); } /* need the whole document so the node selector is NULL */ /* don't know which is the authoritative server for the user * so send request to all in the list */ req.doc_sel = doc_sel; xs = xs_list; while (xs) { req.xcap_root = xs->addr; req.port = xs->port; if(xcap_GetNewDoc(req, *user, *domain, &body) < 0) { LM_ERR("while fetching data from xcap server\n"); pkg_free(doc_sel.xid.s); goto error; } if(body.s) { /* if document found, stop searching */ break; } xs = xs->next; } pkg_free(doc_sel.xid.s); if (body.s == NULL) goto error; doc_tmp = pkg_malloc(sizeof(*doc_tmp)); if(doc_tmp == NULL) { LM_ERR("No more pkg memory\n"); goto error; } doc_tmp->s = pkg_malloc(body.len); if(doc_tmp->s == NULL) { pkg_free(doc_tmp); LM_ERR("No more pkg memory\n"); goto error; } memcpy(doc_tmp->s, body.s, body.len); doc_tmp->len = body.len; pkg_free(body.s); *doc = doc_tmp; return 0; error: if (body.s) pkg_free(body.s); return -1; } int p_get_xcap_doc(str* user, str* domain, int type, str** doc) { str *etag = NULL; if (xcapDbGetDoc(user, domain, type, NULL, NULL, doc, &etag) < 0) { LM_ERR("whie fetching XCAP document from DB\n"); return -1; } if (*doc == NULL) { if(integrated_xcap_server) return 0; if (http_get_xcap_doc(user, domain, type, doc) < 0) return 0; } pkg_free(etag->s); pkg_free(etag); return 0; } opensips-2.2.2/modules/presence_xml/xcap_auth.h000066400000000000000000000032041300170765700216430ustar00rootroot00000000000000/* * presence_xml module - * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-04-12 initial version (anca) */ #ifndef _AUTH_XML_H_ #define _AUTH_XML_H_ #include #include "../../str.h" #include "../presence/subscribe.h" /* sub-handling priorities */ enum { SH_ACTION_BLOCK = 0, SH_ACTION_CONFIRM = 10, SH_ACTION_POLITE_BLOCK = 20, SH_ACTION_ALLOW = 30 }; /* OMA rule types*/ enum { IDENTITY_RULE = 0, EXTERNAL_LIST_RULE, ANONYMOUS_REQUEST_RULE, OTHER_IDENTITY_RULE, }; typedef struct xcap_rule_s { xmlNodePtr node; struct xcap_rule_s *next; } xcap_rule_t; int pres_watcher_allowed(subs_t* subs); xmlNodePtr get_rule_node(subs_t* subs, xmlDocPtr xcap_tree ); int pres_get_rules_doc(str* user, str* domain, str** rules_doc); int p_get_xcap_doc(str* user, str* domain, int type, str** doc); #endif opensips-2.2.2/modules/proto_bin/000077500000000000000000000000001300170765700170265ustar00rootroot00000000000000opensips-2.2.2/modules/proto_bin/Makefile000066400000000000000000000001351300170765700204650ustar00rootroot00000000000000include ../../Makefile.defs auto_gen= NAME=proto_bin.so LIBS= include ../../Makefile.modulesopensips-2.2.2/modules/proto_bin/README000066400000000000000000000115161300170765700177120ustar00rootroot00000000000000proto_bin Module Ionel Cerghit OpenSIPS Solutions Copyright © 2015 OpenSIPS Project __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. bin_port (integer) 1.3.2. bin_port_send_timeout (integer) 1.3.3. bin_max_msg_chunks (integer) 1.3.4. bin_async (integer) 1.3.5. bin_async_max_postponed_chunks (integer) 1.3.6. bin_async_local_connect_timeout (integer) 1.3.7. bin_async_local_write_timeout (integer) List of Examples 1.1. Set bin_port parameter 1.2. Set bin_send_timeout parameter 1.3. Set bin_max_msg_chunks parameter 1.4. Set bin_async parameter 1.5. Set bin_async_max_postponed_chunks parameter 1.6. Set bin_async_local_connect_timeout parameter 1.7. Set bin_async_local_write_timeout parameter Chapter 1. Admin Guide 1.1. Overview The proto_bin module is a transport module which implements Binary Interface TCP-based communication. It does not handle TCP connections management, but only offers higher-level primitives to read and write BIN messages over TCP. It calls registered callback functions for every complete message received. Once loaded, you will be able to define BIN listeners in your configuration file by adding their IP and, optionally, a listening port, similar to this example: ... listen = bin:127.0.0.1 # change the listening IP listen = bin:127.0.0.1:5080 # change the listening IP and port ... 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * None. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. bin_port (integer) The default port to be used by all TCP listeners. Default value is 5555. Example 1.1. Set bin_port parameter ... modparam("proto_bin", "bin_port", 6666) ... 1.3.2. bin_port_send_timeout (integer) Time in milliseconds after a TCP connection will be closed if it is not available for blocking writing in this interval (and OpenSIPS wants to send something on it). Default value is 100 ms. Example 1.2. Set bin_send_timeout parameter ... modparam("proto_bin", "bin_send_timeout", 200) ... 1.3.3. bin_max_msg_chunks (integer) The maximum number of chunks in which a BIN message is expected to arrive via TCP. If a received packet is more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending very fragmented traffic in order to decrease server performance). Default value is 32. Example 1.3. Set bin_max_msg_chunks parameter ... modparam("proto_bin", "bin_max_msg_chunks", 8) ... 1.3.4. bin_async (integer) Specifies whether the TCP connect and write operations should be done in an asynchronous mode (non-blocking connect and write) or not. If disabled, OpenSIPS will block and wait for TCP operations like connect and write. Default value is 1 (enabled). Example 1.4. Set bin_async parameter ... modparam("proto_bin", "bin_async", 0) ... 1.3.5. bin_async_max_postponed_chunks (integer) If bin_async is enabled, this specifies the maximum number of BIN messages that can be stashed for later/async writing. If the connection pending writes exceed this number, the connection will be marked as broken and dropped. Default value is 32. Example 1.5. Set bin_async_max_postponed_chunks parameter ... modparam("proto_bin", "bin_async_max_postponed_chunks", 16) ... 1.3.6. bin_async_local_connect_timeout (integer) If bin_async is enabled, this specifies the number of milliseconds that a connect will be tried in blocking mode (optimization). If the connect operation lasts more than this, the connect will go to async mode and will be passed to TCP MAIN for polling. Default value is 100 ms. Example 1.6. Set bin_async_local_connect_timeout parameter ... modparam("proto_bin", "bin_async_local_connect_timeout", 200) ... 1.3.7. bin_async_local_write_timeout (integer) If bin_async is enabled, this specifies the number of milliseconds that a write op will be tried in blocking mode (optimization). If the write operation lasts more than this, the write will go to async mode and will be passed to bin MAIN for polling. Default value is 10 ms. Example 1.7. Set bin_async_local_write_timeout parameter ... modparam("proto_bin", "tcp_async_local_write_timeout", 100) ... opensips-2.2.2/modules/proto_bin/doc/000077500000000000000000000000001300170765700175735ustar00rootroot00000000000000opensips-2.2.2/modules/proto_bin/doc/proto_bin.xml000066400000000000000000000016241300170765700223130ustar00rootroot00000000000000 %docentities; ]> proto_bin Module &osipsname; Ionel Cerghit OpenSIPS Solutions
ionel.cerghit@gmail.com http://www.opensips.org
2015 &osips; Project
&admin;
opensips-2.2.2/modules/proto_bin/doc/proto_bin_admin.xml000066400000000000000000000136201300170765700234620ustar00rootroot00000000000000 &adminguide;
Overview The proto_bin module is a transport module which implements Binary Interface TCP-based communication. It does not handle TCP connections management, but only offers higher-level primitives to read and write BIN messages over TCP. It calls registered callback functions for every complete message received.
Once loaded, you will be able to define BIN listeners in your configuration file by adding their IP and, optionally, a listening port, similar to this example: ... listen = bin:127.0.0.1 # change the listening IP listen = bin:127.0.0.1:5080 # change the listening IP and port ...
Dependencies
&osips; Modules The following modules must be loaded before this module: None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>bin_port</varname> (integer) The default port to be used by all TCP listeners. Default value is 5555. Set <varname>bin_port</varname> parameter ... modparam("proto_bin", "bin_port", 6666) ...
<varname>bin_port_send_timeout</varname> (integer) Time in milliseconds after a TCP connection will be closed if it is not available for blocking writing in this interval (and &osips; wants to send something on it). Default value is 100 ms. Set <varname>bin_send_timeout</varname> parameter ... modparam("proto_bin", "bin_send_timeout", 200) ...
<varname>bin_max_msg_chunks</varname> (integer) The maximum number of chunks in which a BIN message is expected to arrive via TCP. If a received packet is more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending very fragmented traffic in order to decrease server performance). Default value is 32. Set <varname>bin_max_msg_chunks</varname> parameter ... modparam("proto_bin", "bin_max_msg_chunks", 8) ...
<varname>bin_async</varname> (integer) Specifies whether the TCP connect and write operations should be done in an asynchronous mode (non-blocking connect and write) or not. If disabled, OpenSIPS will block and wait for TCP operations like connect and write. Default value is 1 (enabled). Set <varname>bin_async</varname> parameter ... modparam("proto_bin", "bin_async", 0) ...
<varname>bin_async_max_postponed_chunks</varname> (integer) If bin_async is enabled, this specifies the maximum number of BIN messages that can be stashed for later/async writing. If the connection pending writes exceed this number, the connection will be marked as broken and dropped. Default value is 32. Set <varname>bin_async_max_postponed_chunks</varname> parameter ... modparam("proto_bin", "bin_async_max_postponed_chunks", 16) ...
<varname>bin_async_local_connect_timeout</varname> (integer) If bin_async is enabled, this specifies the number of milliseconds that a connect will be tried in blocking mode (optimization). If the connect operation lasts more than this, the connect will go to async mode and will be passed to TCP MAIN for polling. Default value is 100 ms. Set <varname>bin_async_local_connect_timeout</varname> parameter ... modparam("proto_bin", "bin_async_local_connect_timeout", 200) ...
<varname>bin_async_local_write_timeout</varname> (integer) If bin_async is enabled, this specifies the number of milliseconds that a write op will be tried in blocking mode (optimization). If the write operation lasts more than this, the write will go to async mode and will be passed to bin MAIN for polling. Default value is 10 ms. Set <varname>bin_async_local_write_timeout</varname> parameter ... modparam("proto_bin", "tcp_async_local_write_timeout", 100) ...
opensips-2.2.2/modules/proto_bin/proto_bin.c000066400000000000000000000561161300170765700211760ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-01-09 first version (razvanc) */ #include #include #include #include #include "../../timer.h" #include "../../sr_module.h" #include "../../net/api_proto.h" #include "../../net/api_proto_net.h" #include "../../net/net_tcp.h" #include "../../socket_info.h" #include "../../tsend.h" #include "../../net/proto_tcp/tcp_common_defs.h" #include "../../pt.h" #include "../../bin_interface.h" #include "proto_bin.h" #include "../../ut.h" static int mod_init(void); static int proto_bin_init(struct proto_info *pi); static int proto_bin_init_listener(struct socket_info *si); static int proto_bin_send(struct socket_info* send_sock, char* buf, unsigned int len, union sockaddr_union* to, int id); static int bin_read_req(struct tcp_connection* con, int* bytes_read); static int bin_write_async_req(struct tcp_connection* con,int fd); static int bin_conn_init(struct tcp_connection* c); static void bin_conn_clean(struct tcp_connection* c); static int bin_port = 5555; static int bin_send_timeout = 100; static struct tcp_req bin_current_req; static int bin_max_msg_chunks = 32; static int bin_async = 0; static int bin_async_max_postponed_chunks = 32; static int bin_async_local_connect_timeout = 100; static int bin_async_local_write_timeout = 10; struct bin_send_chunk { char *buf; /* buffer that needs to be sent out */ char *pos; /* the position that we should be writing next */ int len; /* length of the buffer */ int ticks; /* time at which this chunk was initially attempted to be written */ }; struct bin_data { /* the chunks that need to be written on this * connection when it will become writable */ struct bin_send_chunk **async_chunks; /* the total number of chunks pending to be written */ int async_chunks_no; /* the oldest chunk in our write list */ int oldest_chunk; }; static cmd_export_t cmds[] = { {"proto_init", (cmd_function)proto_bin_init, 0, 0, 0, 0}, {0,0,0,0,0,0} }; static param_export_t params[] = { { "bin_port", INT_PARAM, &bin_port }, { "bin_send_timeout", INT_PARAM, &bin_send_timeout }, { "bin_max_msg_chunks", INT_PARAM, &bin_max_msg_chunks }, { "bin_async", INT_PARAM, &bin_async }, { "bin_async_max_postponed_chunks", INT_PARAM, &bin_async_max_postponed_chunks }, { "bin_async_local_connect_timeout", INT_PARAM, &bin_async_local_connect_timeout}, { "bin_async_local_write_timeout", INT_PARAM, &bin_async_local_write_timeout }, {0, 0, 0} }; struct module_exports exports = { PROTO_PREFIX "bin", /* module name*/ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* per-child init function */ }; static int proto_bin_init(struct proto_info *pi) { pi->id = PROTO_BIN; pi->name = "bin"; pi->default_port = bin_port; pi->tran.init_listener = proto_bin_init_listener; pi->tran.send = proto_bin_send; pi->tran.dst_attr = tcp_conn_fcntl; pi->net.flags = PROTO_NET_USE_TCP; pi->net.read = (proto_net_read_f)bin_read_req; pi->net.write = (proto_net_write_f)bin_write_async_req; if (bin_async != 0) { pi->net.conn_init = bin_conn_init; pi->net.conn_clean = bin_conn_clean; } return 0; } static int mod_init(void) { LM_INFO("initializing BIN protocol\n"); return 0; } static int bin_conn_init(struct tcp_connection* c) { struct bin_data *d; /* allocate the tcp_data and the array of chunks as a single mem chunk */ d = (struct bin_data*)shm_malloc( sizeof(struct bin_data) + sizeof(struct bin_send_chunk *) * bin_async_max_postponed_chunks ); if (d == NULL) { LM_ERR("failed to create tcp chunks in shm mem\n"); return -1; } d->async_chunks = (struct bin_send_chunk **)(d+1); d->async_chunks_no = 0; d->oldest_chunk = 0; c->proto_data = (void*)d; return 0; } static void bin_conn_clean(struct tcp_connection* c) { struct bin_data *d = (struct bin_data*)c->proto_data; int r; for (r = 0; r < d->async_chunks_no; r++) { shm_free(d->async_chunks[r]); } shm_free(d); c->proto_data = NULL; } static int proto_bin_init_listener(struct socket_info *si) { /* we do not do anything particular, so * transparently use the generic listener init from net TCP layer */ return tcp_init_listener(si); } static int add_write_chunk(struct tcp_connection *con,char *buf,int len, int lock) { struct bin_send_chunk *c; struct bin_data *d = (struct bin_data*)con->proto_data; c = shm_malloc(sizeof(struct bin_send_chunk) + len); if (!c) { LM_ERR("No more SHM\n"); return -1; } c->len = len; c->ticks = get_ticks(); c->buf = (char *)(c+1); memcpy(c->buf,buf,len); c->pos = c->buf; if (lock) lock_get(&con->write_lock); if (d->async_chunks_no == bin_async_max_postponed_chunks) { LM_ERR("We have reached the limit of max async postponed chunks\n"); if (lock) lock_release(&con->write_lock); shm_free(c); return -2; } d->async_chunks[d->async_chunks_no++] = c; if (d->async_chunks_no == 1) d->oldest_chunk = c->ticks; if (lock) lock_release(&con->write_lock); return 0; } static int async_tsend_stream(struct tcp_connection *c, int fd, char* buf, unsigned int len, int timeout) { int written; int n; struct pollfd pf; pf.fd=fd; pf.events=POLLOUT; written=0; again: n=send(fd, buf, len,0); if (n<0){ if (errno==EINTR) goto again; else if (errno!=EAGAIN && errno!=EWOULDBLOCK) { LM_ERR("Failed first TCP async send : (%d) %s\n", errno, strerror(errno)); return -1; } else goto poll_loop; } written+=n; if (n < len) { /* partial write */ buf += n; len -= n; } else { /* succesful write from the first try */ LM_DBG("Async succesful write from first try on %p\n",c); return len; } poll_loop: n = poll(&pf,1,timeout); if (n<0) { if (errno==EINTR) goto poll_loop; LM_ERR("Polling while trying to async send failed %s [%d]\n", strerror(errno), errno); return -1; } else if (n == 0) { LM_DBG("timeout -> do an async write (add it to conn)\n"); /* timeout - let's just pass to main */ if (add_write_chunk(c,buf,len,0) < 0) { LM_ERR("Failed to add write chunk to connection \n"); return -1; } else { /* we have succesfully added async write chunk * tell MAIN to poll out for us */ LM_DBG("Data still pending for write on conn %p\n",c); return 0; } } if (pf.events&POLLOUT) goto again; /* some other events triggered by poll - treat as errors */ return -1; } static struct tcp_connection* bin_sync_connect(struct socket_info* send_sock, union sockaddr_union* server, int *fd) { int s; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (tcp_init_sock_opt(s)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(s, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } if (tcp_connect_blocking(s, &server->s, sockaddru_len(*server))<0){ LM_ERR("tcp_blocking_connect failed\n"); goto error; } con = tcp_conn_create(s, server, send_sock, S_CONN_OK); if (con==NULL){ LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } *fd = s; return con; /*FIXME: set sock idx! */ error: /* close the opened socket */ if (s!=-1) close(s); return 0; } static int tcpconn_async_connect(struct socket_info* send_sock, union sockaddr_union* server, char *buf, unsigned len, struct tcp_connection** c, int *ret_fd) { int fd, n; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; struct pollfd pf; unsigned int elapsed,to; int err; unsigned int err_len; int poll_err; char *ip; unsigned short port; struct timeval begin; /* create the socket */ fd = socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (fd == -1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); return -1; } if (tcp_init_sock_opt(fd)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(fd, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } /* attempt to do connect and see if we do block or not */ poll_err = 0; elapsed = 0; to = bin_async_local_connect_timeout*1000; if (gettimeofday(&(begin), NULL)) { LM_ERR("Failed to get TCP connect start time\n"); goto error; } again: n = connect(fd, &server->s, sockaddru_len(*server)); if (n == -1) { if (errno == EINTR){ elapsed=get_time_diff(&begin); if (elapsed < to) goto again; else { LM_DBG("Local connect attempt failed \n"); goto async_connect; } } if (errno != EINPROGRESS && errno!=EALREADY) { get_su_info(&server->s, ip, port); LM_ERR("[server=%s:%d] (%d) %s\n",ip, port, errno,strerror(errno)); goto error; } } else goto local_connect; /* let's poll for a little */ pf.fd = fd; pf.events = POLLOUT; while(1){ elapsed = get_time_diff(&begin); if (elapsed < to) to -= elapsed; else { LM_DBG("Polling is overdue \n"); goto async_connect; } n = poll(&pf, 1, to/1000); if (n < 0){ if (errno == EINTR) continue; get_su_info(&server->s, ip, port); LM_ERR("poll/select failed:[server=%s:%d] (%d) %s\n", ip, port, errno, strerror(errno)); goto error; } else if (n==0) /* timeout */ continue; if (pf.revents & (POLLERR|POLLHUP|POLLNVAL)){ LM_ERR("poll error: flags %x\n", pf.revents); poll_err=1; } err_len=sizeof(err); getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &err_len); if ((err==0) && (poll_err==0)) goto local_connect; if (err!=EINPROGRESS && err!=EALREADY){ get_su_info(&server->s, ip, port); LM_ERR("failed to retrieve SO_ERROR [server=%s:%d] (%d) %s\n", ip, port, err, strerror(err)); goto error; } } async_connect: LM_DBG("Create connection for async connect\n"); /* create a new dummy connection */ con = tcp_conn_create(fd, server, send_sock, S_CONN_CONNECTING); if (con==NULL) { LM_ERR("tcp_conn_create failed\n"); goto error; } /* attach the write buffer to it */ lock_get(&con->write_lock); if (add_write_chunk(con,buf,len,0) < 0) { LM_ERR("Failed to add the initial write chunk\n"); /* FIXME - seems no more SHM now ... * continue the async connect process ? */ } lock_release(&con->write_lock); /* report an async, in progress connect */ *c = con; return 0; local_connect: con = tcp_conn_create(fd, server, send_sock, S_CONN_OK); if (con==NULL) { LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } *c = con; *ret_fd = fd; /* report a local connect */ return 1; error: close(fd); *c = NULL; return -1; } inline static int _bin_write_on_socket(struct tcp_connection *c, int fd, char *buf, int len){ int n; lock_get(&c->write_lock); if (bin_async) { n=async_tsend_stream(c,fd,buf,len, bin_async_local_write_timeout); } else { n = tsend_stream(fd, buf, len, bin_send_timeout); } lock_release(&c->write_lock); return n; } static int proto_bin_send(struct socket_info* send_sock, char* buf, unsigned int len, union sockaddr_union* to, int id) { struct tcp_connection *c; struct ip_addr ip; int port; int fd, n; port=0; if (to){ su2ip_addr(&ip, to); port=su_getport(to); n = tcp_conn_get(id, &ip, port, PROTO_BIN, &c, &fd); }else if (id){ n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd); }else{ LM_CRIT("tcp_send called with null id & to\n"); return -1; } if (n<0) { /* error during conn get, return with error too */ LM_ERR("failed to aquire connection\n"); return -1; } /* was connection found ?? */ if (c==0) { if (tcp_no_new_conn) { return -1; } if (!to) { LM_ERR("Unknown destination - cannot open new tcp connection\n"); return -1; } LM_DBG("no open tcp connection found, opening new one, async = %d\n",bin_async); /* create tcp connection */ if (bin_async) { n = tcpconn_async_connect(send_sock, to, buf, len, &c, &fd); if ( n<0 ) { LM_ERR("async TCP connect failed\n"); return -1; } /* connect succeeded, we have a connection */ if (n==0) { /* connect is still in progress, break the sending * flow now (the actual write will be done when * connect will be completed */ LM_DBG("Succesfully started async connection \n"); tcp_conn_release(c, 0); return len; } /* our first connect attempt succeeded - go ahead as normal */ } else if ((c=bin_sync_connect(send_sock, to, &fd))==0) { LM_ERR("connect failed\n"); return -1; } goto send_it; } /* now we have a connection, let's see what we can do with it */ /* BE CAREFUL now as we need to release the conn before exiting !!! */ if (fd==-1) { /* connection is not writable because of its state - can we append * data to it for later writting (async writting)? */ if (c->state==S_CONN_CONNECTING) { /* the connection is currently in the process of getting * connected - let's append our send chunk as well - just in * case we ever manage to get through */ LM_DBG("We have acquired a TCP connection which is still " "pending to connect - delaying write \n"); n = add_write_chunk(c,buf,len,1); if (n < 0) { LM_ERR("Failed to add another write chunk to %p\n",c); /* we failed due to internal errors - put the * connection back */ tcp_conn_release(c, 0); return -1; } /* we succesfully added our write chunk - success */ tcp_conn_release(c, 0); return len; } else { /* return error, nothing to do about it */ tcp_conn_release(c, 0); return -1; } } send_it: LM_DBG("sending via fd %d...\n",fd); n = _bin_write_on_socket(c, fd, buf, len); tcp_conn_set_lifetime( c, tcp_con_lifetime); LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd); /* LM_DBG("buf=\n%.*s\n", (int)len, buf); */ if (n<0){ LM_ERR("failed to send\n"); c->state=S_CONN_BAD; if (c->proc_id != process_no) close(fd); tcp_conn_release(c, 0); return -1; } /* only close the FD if not already in the context of our process either we just connected, or main sent us the FD */ if (c->proc_id != process_no) close(fd); tcp_conn_release(c, (nfd; bytes_free=TCP_BUF_SIZE- (int)(r->pos - r->buf); if (bytes_free==0){ LM_ERR("buffer overrun, dropping\n"); r->error=TCP_REQ_OVERRUN; return -1; } again: bytes_read=read(fd, r->pos, bytes_free); if(bytes_read==-1){ if (errno == EWOULDBLOCK || errno == EAGAIN){ return 0; /* nothing has been read */ }else if (errno == EINTR) goto again; else{ LM_ERR("error reading: %s\n",strerror(errno)); r->error=TCP_READ_ERROR; return -1; } }else if (bytes_read==0){ c->state=S_CONN_EOF; LM_DBG("EOF on %p, FD %d\n", c, fd); } #ifdef EXTRA_DEBUG LM_DBG("read %d bytes:\n%.*s\n", bytes_read, bytes_read, r->pos); #endif r->pos+=bytes_read; return bytes_read; } static int bin_handle_req(struct tcp_req *req, struct tcp_connection *con, int _max_msg_chunks) { long size; if (req->complete){ /* update the timeout - we succesfully read the request */ tcp_conn_set_lifetime( con, tcp_con_lifetime); con->timeout = con->lifetime; LM_DBG("completely received a message\n"); /* rcv.bind_address should always be !=0 */ /* just for debugging use sendipv4 as receiving socket FIXME*/ con->rcv.proto_reserved1=con->id; /* copy the id */ /* prepare for next request */ size=req->pos - req->parsed; if (!size) { /* did not read any more things - we can release * the connection */ LM_DBG("Nothing more to read on TCP conn %p, currently in state %d \n", con,con->state); if (req != &bin_current_req) { /* we have the buffer in the connection tied buff - * detach it , release the conn and free it afterwards */ con->con_req = NULL; } } else { LM_DBG("We still have things on the pipe - " "keeping connection \n"); } /* give the message to the registered functions */ call_callbacks(req->buf, &con->rcv); if (!size && req != &bin_current_req) { /* if we no longer need this tcp_req * we can free it now */ pkg_free(req); } if (size) memmove(req->buf, req->parsed, size); init_tcp_req(req, size); con->msg_attempts = 0; /* if we still have some unparsed bytes, try to parse them too*/ if (size) return 1; } else { /* request not complete - check the if the thresholds are exceeded */ if (con->msg_attempts==0) /* if first iteration, set a short timeout for reading * a whole SIP message */ con->timeout = get_ticks() + tcp_max_msg_time; con->msg_attempts ++; if (con->msg_attempts == _max_msg_chunks) { LM_ERR("Made %u read attempts but message is not complete yet - " "closing connection \n",con->msg_attempts); goto error; } if (req == &bin_current_req) { /* let's duplicate this - most likely another conn will come in */ LM_DBG("We didn't manage to read a full request\n"); con->con_req = pkg_malloc(sizeof(struct tcp_req)); if (con->con_req == NULL) { LM_ERR("No more mem for dynamic con request buffer\n"); goto error; } if (req->pos != req->buf) { /* we have read some bytes */ memcpy(con->con_req->buf,req->buf,req->pos-req->buf); con->con_req->pos = con->con_req->buf + (req->pos-req->buf); } else { con->con_req->pos = con->con_req->buf; } if (req->parsed != req->buf) con->con_req->parsed =con->con_req->buf+(req->parsed-req->buf); else con->con_req->parsed = con->con_req->buf; con->con_req->complete=req->complete; con->con_req->content_len=req->content_len; con->con_req->error = req->error; } } /* everything ok */ return 0; error: /* report error */ return -1; } static void bin_parse_headers(struct tcp_req *req){ unsigned short *px; if(req->content_len == 0 && req->pos - req->buf < HEADER_SIZE){ req->parsed = req->pos; return; } px = (unsigned short*)(req->buf + MARKER_SIZE); req->content_len = (*px); if(req->pos - req->buf == req->content_len){ LM_DBG("received a COMPLETE message\n"); req->complete = 1; req->parsed = req->buf + req->content_len; } else if(req->pos - req->buf > req->content_len){ LM_DBG("received MORE then a message\n"); req->complete = 1; req->parsed = req->buf + req->content_len; } else { LM_DBG("received only PART of a message\n"); req->parsed = req->pos; } } static int bin_read_req(struct tcp_connection* con, int* bytes_read){ int bytes; int total_bytes; struct tcp_req *req; bytes = -1; total_bytes = 0; if (con->con_req) { req = con->con_req; LM_DBG("Using the per connection buff \n"); } else { LM_DBG("Using the global ( per process ) buff \n"); init_tcp_req(&bin_current_req, 0); req = &bin_current_req; } again: if(req->error == TCP_REQ_OK){ /* if we still have some unparsed part, parse it first, * don't do the read*/ if (req->parsed < req->pos){ bytes=0; } else { bytes=tcp_read(con,req); if (bytes < 0) { LM_ERR("failed to read \n"); goto error; } } bin_parse_headers(req); total_bytes+=bytes; /* eof check: * is EOF if eof on fd and req. not complete yet, * if req. is complete we might have a second unparsed * request after it, so postpone release_with_eof */ if ((con->state==S_CONN_EOF) && (req->complete==0)) { LM_DBG("EOF received\n"); goto done; } } if (req->error!=TCP_REQ_OK){ LM_ERR("bad request, state=%d, error=%d " "buf:\n%.*s\nparsed:\n%.*s\n", req->state, req->error, (int)(req->pos-req->buf), req->buf, (int)(req->parsed-req->start), req->start); LM_DBG("- received from: port %d\n", con->rcv.src_port); print_ip("- received from: ip ",&con->rcv.src_ip, "\n"); goto error; } switch (bin_handle_req(req, con, bin_max_msg_chunks) ) { case 1: goto again; case -1: goto error; } LM_DBG("tcp_read_req end\n"); done: if (bytes_read) *bytes_read=total_bytes; /* connection will be released */ return 0; error: /* connection will be released as ERROR */ return -1; } static int bin_write_async_req(struct tcp_connection* con,int fd) { int n,left; struct bin_send_chunk *chunk; struct bin_data *d = (struct bin_data*)con->proto_data; if (d->async_chunks_no == 0) { LM_DBG("The connection has been triggered " " for a write event - but we have no pending write chunks\n"); return 0; } next_chunk: chunk=d->async_chunks[0]; again: left = (int)((chunk->buf+chunk->len)-chunk->pos); LM_DBG("Trying to send %d bytes from chunk %p in conn %p - %d %d \n", left,chunk,con,chunk->ticks,get_ticks()); n = send(fd, chunk->pos, left, 0); if (n<0) { if (errno == EINTR) goto again; else if (errno == EAGAIN || errno == EWOULDBLOCK) { LM_DBG("Can't finish to write chunk %p on conn %p\n", chunk,con); /* report back we have more writting to be done */ return 1; } else { LM_ERR("Error occured while sending async chunk %d (%s)\n", errno,strerror(errno)); /* report the conn as broken */ return -1; } } if (n < left) { /* partial write */ chunk->pos+=n; goto again; } else { /* written a full chunk - move to the next one, if any */ shm_free(chunk); d->async_chunks_no--; if (d->async_chunks_no == 0) { LM_DBG("We have finished writing all our async chunks in %p\n",con); d->oldest_chunk=0; /* report back everything ok */ return 0; } else { LM_DBG("We still have %d chunks pending on %p\n", d->async_chunks_no,con); memmove(&d->async_chunks[0],&d->async_chunks[1], d->async_chunks_no * sizeof(struct bin_send_chunk*)); d->oldest_chunk = d->async_chunks[0]->ticks; goto next_chunk; } } } opensips-2.2.2/modules/proto_bin/proto_bin.h000066400000000000000000000001101300170765700211620ustar00rootroot00000000000000#ifndef _proto_bin__ #define _proto_bin__ #define MARKER_SIZE 4 #endifopensips-2.2.2/modules/proto_hep/000077500000000000000000000000001300170765700170325ustar00rootroot00000000000000opensips-2.2.2/modules/proto_hep/Makefile000066400000000000000000000001361300170765700204720ustar00rootroot00000000000000include ../../Makefile.defs auto_gen= NAME=proto_hep.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/proto_hep/README000066400000000000000000000153671300170765700177260ustar00rootroot00000000000000proto_hep Module Ionut-Razvan Ionita OpenSIPS Solutions Copyright © 2015 OpenSIPS Project __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. hep_port (integer) 1.3.2. hep_send_timeout (integer) 1.3.3. hep_max_msg_chunks (integer) 1.3.4. hep_async (integer) 1.3.5. hep_async_max_postponed_chunks (integer) 1.3.6. hep_capture_id (integer) 1.3.7. hep_async_local_connect_timeout (integer) 1.3.8. hep_async_local_write_timeout (integer) 2. Developer Guide 2.1. Available Functions 2.1.1. pack_hep(from, to, proto, payload, plen, retbuf, retlen) 2.1.2. register_hep_cb(cb) 2.1.3. hep_version List of Examples 1.1. Set hep_port parameter 1.2. Set hep_send_timeout parameter 1.3. Set hep_max_msg_chunks parameter 1.4. Set hep_async parameter 1.5. Set hep_async_max_postponed_chunks parameter 1.6. Set hep_capture_id parameter 1.7. Set hep_async_local_connect_timeout parameter 1.8. Set hep_async_local_write_timeout parameter Chapter 1. Admin Guide 1.1. Overview The proto_hep module is a transport module which implements hepV1 and hepV2 UDP-based communication and hepV3 TCP-based communication. It also offers an API with which you can register callbacks which are called after the HEP header is parsed and also can pack sip messages to HEP messages.The unpacking part is done internally. Once loaded, you will be able to define HEP listeners in your configuration file by adding their IP and, optionally, a listening port. You can define both TCP and UDP listeners. On UDP you will be able to receive HEP v1, v2 and v3 packets, on TCP only HEPv3. ... #HEPv3 listener listen = hep_tcp:127.0.0.1:6061 # change the listening I P #HEPv1, v2, v3 listener listen = hep_udp:127.0.0.1:6061 # change the listening I P ... 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * None. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. hep_port (integer) The default port to be used by all TCP/UDP listeners. Default value is 5656. Example 1.1. Set hep_port parameter ... modparam("proto_hep", "hep_port", 6666) ... 1.3.2. hep_send_timeout (integer) Time in milliseconds after a TCP connection will be closed if it is not available for blocking writing in this interval (and OpenSIPS wants to send something on it). Default value is 100 ms. Example 1.2. Set hep_send_timeout parameter ... modparam("proto_hep", "hep_send_timeout", 200) ... 1.3.3. hep_max_msg_chunks (integer) The maximum number of chunks in which a HEP message is expected to arrive via TCP. If a received packet is more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending very fragmented traffic in order to decrease server performance). Default value is 32. Example 1.3. Set hep_max_msg_chunks parameter ... modparam("proto_hep", "hep_max_msg_chunks", 8) ... 1.3.4. hep_async (integer) Specifies whether the TCP connect and write operations should be done in an asynchronous mode (non-blocking connect and write) or not. If disabled, OpenSIPS will block and wait for TCP operations like connect and write. Default value is 1 (enabled). Example 1.4. Set hep_async parameter ... modparam("proto_bin", "hep_async", 0) ... 1.3.5. hep_async_max_postponed_chunks (integer) If hep_async is enabled, this specifies the maximum number of HEP messages that can be stashed for later/async writing. If the connection pending writes exceed this number, the connection will be marked as broken and dropped. Default value is 32. Example 1.5. Set hep_async_max_postponed_chunks parameter ... modparam("proto_hep", "hep_async_max_postponed_chunks", 16) ... 1.3.6. hep_capture_id (integer) The parameter indicate the capture agent ID for HEPv2/v3 protocol. Limitation: 16-bit integer. Default value is "1". Example 1.6. Set hep_capture_id parameter ... modparam("proto_hep", "hep_capture_id", 234) ... 1.3.7. hep_async_local_connect_timeout (integer) If hep_async is enabled, this specifies the number of milliseconds that a connect will be tried in blocking mode (optimization). If the connect operation lasts more than this, the connect will go to async mode and will be passed to TCP MAIN for polling. Default value is 100 ms. Example 1.7. Set hep_async_local_connect_timeout parameter ... modparam("proto_hep", "hep_async_local_connect_timeout", 200) ... 1.3.8. hep_async_local_write_timeout (integer) If hep_async is enabled, this specifies the number of milliseconds that a write op will be tried in blocking mode (optimization). If the write operation lasts more than this, the write will go to async mode and will be passed to bin MAIN for polling. Default value is 10 ms. Example 1.8. Set hep_async_local_write_timeout parameter ... modparam("proto_hep", "tcp_async_local_write_timeout", 100) ... Chapter 2. Developer Guide 2.1. Available Functions 2.1.1. pack_hep(from, to, proto, payload, plen, retbuf, retlen) The function packs connection details and sip message into HEP message. It's your job to free both the old and the new buffer. Meaning of the parameters is as follows: * sockaddr_union *from - sockaddr_union describing sending socket * sockaddr_union *to - sockaddr_union describing receiving socket * int proto - protocol used in hep header; * char *payload SIP payload buffer * int plen SIP payload buffer length * char **retbuf HEP message buffer * int *retlen HEP message buffer length 2.1.2. register_hep_cb(cb) The function register callbacks to be called whenever a HEP message is received. The callbacks parameters are struct hep_desc*(see hep.h for details) a structure that holds all details about the hep header and the receive_info* structure. The callback can return HEP_SCRIPT_SKIP which stops the HEP message from being passed thrrough scripts. Meaning of the parameters is as follows: * hep_cb_t cb HEP callback 2.1.3. hep_version Current version of hep used. opensips-2.2.2/modules/proto_hep/doc/000077500000000000000000000000001300170765700175775ustar00rootroot00000000000000opensips-2.2.2/modules/proto_hep/doc/proto_hep.xml000066400000000000000000000017211300170765700223210ustar00rootroot00000000000000 %docentities; ]> proto_hep Module &osipsname; Ionut-Razvan Ionita OpenSIPS Solutions
ionutionita@opensips.org http://www.opensips.org
2015 &osips; Project
&admin; &devel;
opensips-2.2.2/modules/proto_hep/doc/proto_hep_admin.xml000066400000000000000000000150271300170765700234750ustar00rootroot00000000000000 &adminguide;
Overview The proto_hep module is a transport module which implements hepV1 and hepV2 UDP-based communication and hepV3 TCP-based communication. It also offers an API with which you can register callbacks which are called after the HEP header is parsed and also can pack sip messages to HEP messages.The unpacking part is done internally.
Once loaded, you will be able to define HEP listeners in your configuration file by adding their IP and, optionally, a listening port. You can define both TCP and UDP listeners. On UDP you will be able to receive HEP v1, v2 and v3 packets, on TCP only HEPv3. ... #HEPv3 listener listen = hep_tcp:127.0.0.1:6061 # change the listening IP #HEPv1, v2, v3 listener listen = hep_udp:127.0.0.1:6061 # change the listening IP ...
Dependencies
&osips; Modules The following modules must be loaded before this module: None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>hep_port</varname> (integer) The default port to be used by all TCP/UDP listeners. Default value is 5656. Set <varname>hep_port</varname> parameter ... modparam("proto_hep", "hep_port", 6666) ...
<varname>hep_send_timeout</varname> (integer) Time in milliseconds after a TCP connection will be closed if it is not available for blocking writing in this interval (and &osips; wants to send something on it). Default value is 100 ms. Set <varname>hep_send_timeout</varname> parameter ... modparam("proto_hep", "hep_send_timeout", 200) ...
<varname>hep_max_msg_chunks</varname> (integer) The maximum number of chunks in which a HEP message is expected to arrive via TCP. If a received packet is more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending very fragmented traffic in order to decrease server performance). Default value is 32. Set <varname>hep_max_msg_chunks</varname> parameter ... modparam("proto_hep", "hep_max_msg_chunks", 8) ...
<varname>hep_async</varname> (integer) Specifies whether the TCP connect and write operations should be done in an asynchronous mode (non-blocking connect and write) or not. If disabled, OpenSIPS will block and wait for TCP operations like connect and write. Default value is 1 (enabled). Set <varname>hep_async</varname> parameter ... modparam("proto_bin", "hep_async", 0) ...
<varname>hep_async_max_postponed_chunks</varname> (integer) If hep_async is enabled, this specifies the maximum number of HEP messages that can be stashed for later/async writing. If the connection pending writes exceed this number, the connection will be marked as broken and dropped. Default value is 32. Set <varname>hep_async_max_postponed_chunks</varname> parameter ... modparam("proto_hep", "hep_async_max_postponed_chunks", 16) ...
<varname>hep_capture_id</varname> (integer) The parameter indicate the capture agent ID for HEPv2/v3 protocol. Limitation: 16-bit integer. Default value is "1". Set <varname>hep_capture_id</varname> parameter ... modparam("proto_hep", "hep_capture_id", 234) ...
<varname>hep_async_local_connect_timeout</varname> (integer) If hep_async is enabled, this specifies the number of milliseconds that a connect will be tried in blocking mode (optimization). If the connect operation lasts more than this, the connect will go to async mode and will be passed to TCP MAIN for polling. Default value is 100 ms. Set <varname>hep_async_local_connect_timeout</varname> parameter ... modparam("proto_hep", "hep_async_local_connect_timeout", 200) ...
<varname>hep_async_local_write_timeout</varname> (integer) If hep_async is enabled, this specifies the number of milliseconds that a write op will be tried in blocking mode (optimization). If the write operation lasts more than this, the write will go to async mode and will be passed to bin MAIN for polling. Default value is 10 ms. Set <varname>hep_async_local_write_timeout</varname> parameter ... modparam("proto_hep", "tcp_async_local_write_timeout", 100) ...
opensips-2.2.2/modules/proto_hep/doc/proto_hep_devel.xml000066400000000000000000000041351300170765700235020ustar00rootroot00000000000000 &develguide;
Available Functions
<function moreinfo="none">pack_hep(from, to, proto, payload, plen, retbuf, retlen) </function> The function packs connection details and sip message into HEP message. It's your job to free both the old and the new buffer. Meaning of the parameters is as follows: sockaddr_union *from - sockaddr_union describing sending socket sockaddr_union *to - sockaddr_union describing receiving socket int proto - protocol used in hep header; char *payload SIP payload buffer int plen SIP payload buffer length char **retbuf HEP message buffer int *retlen HEP message buffer length
<function moreinfo="none">register_hep_cb(cb) </function> The function register callbacks to be called whenever a HEP message is received. The callbacks parameters are struct hep_desc*(see hep.h for details) a structure that holds all details about the hep header and the receive_info* structure. The callback can return HEP_SCRIPT_SKIP which stops the HEP message from being passed thrrough scripts. Meaning of the parameters is as follows: hep_cb_t cb HEP callback
<function moreinfo="none">hep_version </function> Current version of hep used.
opensips-2.2.2/modules/proto_hep/hep.c000066400000000000000000000473421300170765700177640ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2015-08-14 first version (Ionut Ionita) */ #include #include #include #include #include "../../timer.h" #include "../../sr_module.h" #include "../../net/api_proto.h" #include "../../net/api_proto_net.h" #include "../../net/net_tcp.h" #include "../../socket_info.h" #include "../../tsend.h" #include "../../net/proto_tcp/tcp_common_defs.h" #include "../../pt.h" #include "../../ut.h" #include "hep.h" #include "../compression/compression_api.h" #define GENERIC_VENDOR_ID 0x0000 #define HEP_PROTO_SIP 0x01 extern int hep_capture_id; extern int payload_compression; extern compression_api_t compression_api; static int pack_hepv3(union sockaddr_union* from_su, union sockaddr_union* to_su, int proto, char *payload, int plen, char **retbuf, int *retlen); static int pack_hepv12(union sockaddr_union* from_su, union sockaddr_union* to_su, int proto, char *payload, int plen, int hep_version, char **retbuf, int *retlen); /* * * pack as hep; version depends * @in1 source sockkadr * @in2 dest sockkadr * @in3 protocolo * @in4 SIP payload * @in5 SIP payload length * @out1 packed buffer * @out2 packed buffer length * it's your job to free the buffers */ int pack_hep(union sockaddr_union* from_su, union sockaddr_union* to_su, int proto, char *payload, int plen, int hep_version, char **retbuf, int *retlen) { switch (hep_version) { case 1: case 2: if (pack_hepv12(from_su, to_su, proto, payload, plen, hep_version, retbuf, retlen) < 0) { LM_ERR("failed to pack using hep protocol version 3\n"); return -1; } break; case 3: if (pack_hepv3(from_su, to_su, proto, payload, plen, retbuf, retlen) < 0) { LM_ERR("failed to pack using hep protocol version 3\n"); return -1; } break; default: /* version check is being done at startup */ LM_BUG("invalid hep protocol version [%d]!" "Probably memory corruption\n", hep_version); return -1; } return 0; } /* * pack as hepv3 * @in1 source sockkadr * @in2 dest sockkadr * @in3 protocolo * @in4 SIP payload * @in5 SIP payload length * @out1 packed buffer (pkg) * @out2 packed buffer length */ static int pack_hepv3(union sockaddr_union* from_su, union sockaddr_union* to_su, int proto, char *payload, int plen, char **retbuf, int *retlen) { int rc; int buflen, iplen=0, tlen; char* buffer; struct hep_generic hg; struct timeval tvb; unsigned long compress_len; str compressed_payload={NULL, 0}; hep_chunk_ip4_t src_ip4, dst_ip4; hep_chunk_ip6_t src_ip6, dst_ip6; hep_chunk_t payload_chunk; gettimeofday(&tvb, NULL); memset(&hg, 0, sizeof(struct hep_generic)); /* header set */ memcpy(hg.header.id, HEP_HEADER_ID, HEP_HEADER_ID_LEN); /* IP proto */ hg.ip_family.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.ip_family.chunk.type_id = htons(0x0001); hg.ip_family.data = from_su->s.sa_family; hg.ip_family.chunk.length = htons(sizeof(hg.ip_family)); /* Proto ID */ hg.ip_proto.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.ip_proto.chunk.type_id = htons(0x0002); hg.ip_proto.data = proto; hg.ip_proto.chunk.length = htons(sizeof(hg.ip_proto)); /* IPv4 */ if(from_su->s.sa_family == AF_INET) { /* SRC IP */ src_ip4.chunk.vendor_id = htons(GENERIC_VENDOR_ID); src_ip4.chunk.type_id = htons(0x0003); src_ip4.data = from_su->sin.sin_addr; src_ip4.chunk.length = htons(sizeof(src_ip4)); /* DST IP */ dst_ip4.chunk.vendor_id = htons(GENERIC_VENDOR_ID); dst_ip4.chunk.type_id = htons(0x0004); dst_ip4.data = to_su->sin.sin_addr; dst_ip4.chunk.length = htons(sizeof(dst_ip4)); iplen = sizeof(dst_ip4) + sizeof(src_ip4); /* SRC PORT */ hg.src_port.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.src_port.chunk.type_id = htons(0x0007); hg.src_port.data = htons(from_su->sin.sin_port); hg.src_port.chunk.length = htons(sizeof(hg.src_port)); /* DST PORT */ hg.dst_port.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.dst_port.chunk.type_id = htons(0x0008); hg.dst_port.data = htons(to_su->sin.sin_port); hg.dst_port.chunk.length = htons(sizeof(hg.dst_port)); } /* IPv6 */ else if(from_su->s.sa_family == AF_INET6) { /* SRC IPv6 */ src_ip6.chunk.vendor_id = htons(GENERIC_VENDOR_ID); src_ip6.chunk.type_id = htons(0x0005); src_ip6.data = from_su->sin6.sin6_addr; src_ip6.chunk.length = htonl(sizeof(src_ip6)); /* DST IPv6 */ dst_ip6.chunk.vendor_id = htons(GENERIC_VENDOR_ID); dst_ip6.chunk.type_id = htons(0x0006); dst_ip6.data = from_su->sin6.sin6_addr; dst_ip6.chunk.length = htonl(sizeof(dst_ip6)); iplen = sizeof(dst_ip6) + sizeof(src_ip6); /* SRC PORT */ hg.src_port.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.src_port.chunk.type_id = htons(0x0007); hg.src_port.data = htons(from_su->sin6.sin6_port); hg.src_port.chunk.length = htons(sizeof(hg.src_port)); /* DST PORT */ hg.dst_port.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.dst_port.chunk.type_id = htons(0x0008); hg.dst_port.data = htons(to_su->sin6.sin6_port); hg.dst_port.chunk.length = htons(sizeof(hg.dst_port)); } /* TIMESTAMP SEC */ hg.time_sec.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.time_sec.chunk.type_id = htons(0x0009); hg.time_sec.data = htonl(tvb.tv_sec); hg.time_sec.chunk.length = htons(sizeof(hg.time_sec)); /* TIMESTAMP USEC */ hg.time_usec.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.time_usec.chunk.type_id = htons(0x000a); hg.time_usec.data = htonl(tvb.tv_usec); hg.time_usec.chunk.length = htons(sizeof(hg.time_usec)); /* Protocol TYPE */ hg.proto_t.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.proto_t.chunk.type_id = htons(0x000b); hg.proto_t.data = HEP_PROTO_SIP; hg.proto_t.chunk.length = htons(sizeof(hg.proto_t)); /* Capture ID */ hg.capt_id.chunk.vendor_id = htons(GENERIC_VENDOR_ID); hg.capt_id.chunk.type_id = htons(0x000c); /* */ hg.capt_id.data = htons(hep_capture_id); hg.capt_id.chunk.length = htons(sizeof(hg.capt_id)); payload_chunk.vendor_id = htons(GENERIC_VENDOR_ID); payload_chunk.type_id = payload_compression ? htons(0x0010) : htons(0x000f); /* compress the payload if requested */ if (payload_compression) { rc=compression_api.compress((unsigned char*)payload, (unsigned long)plen, &compressed_payload, &compress_len, compression_api.level); if (compression_api.check_rc(rc)==0) { plen = (int)compress_len; /* we don't need the payload pointer in this function */ payload = compressed_payload.s; } else { LM_ERR("payload compression failed! will send the buffer uncompressed\n"); payload_chunk.type_id = htons(0x000f); } } payload_chunk.length = htons(sizeof(hep_chunk_t) + plen); tlen = sizeof(struct hep_generic) + iplen + sizeof(hep_chunk_t) + plen; /* FIXME no tls support yet */ /* total */ hg.header.length = htons(tlen); buffer = pkg_malloc(tlen); if (buffer == NULL){ LM_ERR("no more pkg\n"); return -1; } memcpy(buffer, &hg, sizeof(struct hep_generic)); buflen = sizeof(struct hep_generic); /* IPv4 */ if(from_su->s.sa_family == AF_INET) { /* SRC IP */ memcpy((void*) buffer+buflen, &src_ip4, sizeof(struct hep_chunk_ip4)); buflen += sizeof(struct hep_chunk_ip4); memcpy((void*) buffer+buflen, &dst_ip4, sizeof(struct hep_chunk_ip4)); buflen += sizeof(struct hep_chunk_ip4); } /* IPv6 */ else if(from_su->s.sa_family == AF_INET6) { /* SRC IPv6 */ memcpy((void*) buffer+buflen, &src_ip4, sizeof(struct hep_chunk_ip6)); buflen += sizeof(struct hep_chunk_ip6); memcpy((void*) buffer+buflen, &dst_ip6, sizeof(struct hep_chunk_ip6)); buflen += sizeof(struct hep_chunk_ip6); } else { LM_ERR("unknown IP family\n"); return -1; } /* PAYLOAD CHUNK */ memcpy((void*) buffer+buflen, &payload_chunk, sizeof(struct hep_chunk)); buflen += sizeof(struct hep_chunk); /* Now copying payload self */ memcpy((void*) buffer+buflen, payload, plen); buflen+=plen; *retlen = buflen; *retbuf = buffer; return 0; } /* * pack as hepv2 * @in1 source sockkadr * @in2 dest sockkadr * @in3 protocol * @in4 SIP payload * @in5 SIP payload length * @out1 packed buffer (pkg) * @out2 packed buffer length */ static int pack_hepv12(union sockaddr_union* from_su, union sockaddr_union* to_su, int proto, char *payload, int plen, int hep_version, char **retbuf, int *retlen) { char* buffer; unsigned int totlen=0, buflen=0; struct hep_hdr hdr; struct hep_timehdr hep_time; struct hep_iphdr hep_ipheader; struct hep_ip6hdr hep_ip6header; struct timeval tvb; gettimeofday(&tvb, NULL); memset(&hdr, 0, sizeof(struct hep_hdr)); /* Version && proto */ hdr.hp_v = hep_version; hdr.hp_f = from_su->s.sa_family; hdr.hp_p = proto; /* IP version */ switch (hdr.hp_f) { case AF_INET: totlen = sizeof(struct hep_iphdr); break; case AF_INET6: totlen = sizeof(struct hep_ip6hdr); break; } hdr.hp_l = totlen + sizeof(struct hep_hdr); /* COMPLETE LEN */ totlen += sizeof(struct hep_hdr); totlen += plen; if(hep_version == 2) { totlen += sizeof(struct hep_timehdr); hep_time.tv_sec = tvb.tv_sec; hep_time.tv_usec = tvb.tv_usec; hep_time.captid = hep_capture_id; } /*buffer for ethernet frame*/ buffer = pkg_malloc(totlen); if (buffer == NULL){ LM_ERR("no more pkg\n"); return -1; } buflen = sizeof(struct hep_hdr); switch (hdr.hp_f) { case AF_INET: /* Source && Destination ipaddresses*/ hep_ipheader.hp_src = from_su->sin.sin_addr; hep_ipheader.hp_dst = to_su->sin.sin_addr; /* copy hep ipheader */ memcpy((void*)buffer + buflen, &hep_ipheader, sizeof(struct hep_iphdr)); buflen += sizeof(struct hep_iphdr); hdr.hp_sport = htons(from_su->sin.sin_port); /* src port */ hdr.hp_dport = htons(to_su->sin.sin_port); /* dst port */ break; case AF_INET6: /* Source && Destination ipv6addresses*/ hep_ip6header.hp6_src = from_su->sin6.sin6_addr; hep_ip6header.hp6_dst = to_su->sin6.sin6_addr; /* copy hep6 ipheader */ memcpy((void*)buffer + buflen, &hep_ip6header, sizeof(struct hep_ip6hdr)); buflen += sizeof(struct hep_ip6hdr); hdr.hp_sport = htons(from_su->sin6.sin6_port); /* src port */ hdr.hp_dport = htons(to_su->sin6.sin6_port); /* dst port */ break; } /* copy hep_hdr */ memcpy(buffer, &hdr, sizeof(struct hep_hdr)); /* Version 2 has timestamp, captnode ID */ if(hep_version == 2) { /* TIMING */ memcpy((void*)buffer + buflen, &hep_time, sizeof(struct hep_timehdr)); buflen += sizeof(struct hep_timehdr); } memcpy((buffer + buflen) , payload, plen); buflen +=plen; *retbuf = buffer; *retlen = buflen; return 0; } /* * @in1 buffer = hep + sip * @in2 buffer length * @in3 version - needed to make the difference betwen 3 and the first 2 protos * @out1 structure containing hep details + headers | see hep.h */ int unpack_hep(char *buf, int len, int version, struct hep_desc* h) { int err; if (version == 3) err = unpack_hepv3(buf, len, h); else err = unpack_hepv12(buf, len, h); return err; } /* * @in1 buffer = hep + sip * @in2 buffer length * @out1 structure containing hepv12 details + headers | see hep.h */ int unpack_hepv12(char *buf, int len, struct hep_desc* h) { int offset = 0, hl; struct hep_hdr *heph; char *hep_payload, *end, *hep_ip; struct hep_timehdr* heptime_tmp, heptime; struct hepv12 h12; memset(&heptime, 0, sizeof(struct hep_timehdr)); hl = offset = sizeof(struct hep_hdr); end = buf + len; if (len < offset) { LM_ERR("len less than offset [%d] vs [%d]\n", len, offset); return -1; } /* hep_hdr */ heph = (struct hep_hdr*) buf; h12.hdr = *heph; h12.hdr.hp_sport = ntohs(h12.hdr.hp_sport); h12.hdr.hp_dport = ntohs(h12.hdr.hp_dport); switch(heph->hp_f){ case AF_INET: hl += sizeof(struct hep_iphdr); break; case AF_INET6: hl += sizeof(struct hep_ip6hdr); break; default: LM_ERR("unsupported family [%d]\n", heph->hp_f); return 0; } /* Check version */ if((heph->hp_v != 1 && heph->hp_v != 2) || hl != heph->hp_l) { LM_ERR("not supported version or bad length: v:[%d] l:[%d] vs [%d]\n", heph->hp_v, heph->hp_l, hl); return -1; } h->version = heph->hp_v; hep_ip = buf + sizeof(struct hep_hdr); if (hep_ip > end){ LM_ERR("hep_ip is over buf+len\n"); return 0; } switch(heph->hp_f){ case AF_INET: offset+=sizeof(struct hep_iphdr); h12.addr.hep_ipheader = *((struct hep_iphdr *)hep_ip); break; case AF_INET6: offset+=sizeof(struct hep_ip6hdr); h12.addr.hep_ip6header = *((struct hep_ip6hdr *)hep_ip); break; } /* VOIP payload */ hep_payload = buf + offset; if (hep_payload > end) { LM_ERR("hep_payload is over buf+len\n"); return 0; } h12.payload = hep_payload; /* timming */ if(heph->hp_v == 2) { offset+=sizeof(struct hep_timehdr); heptime_tmp = (struct hep_timehdr*) hep_payload; h12.payload += sizeof(struct hep_timehdr); heptime.tv_sec = heptime_tmp->tv_sec; heptime.tv_usec = heptime_tmp->tv_usec; heptime.captid = heptime_tmp->captid; } h12.hep_time = heptime; h->u.hepv12 = h12; return 0; } /* * @in1 buffer = hep + sip * @in2 buffer length * @out1 structure containing hepv3 details + headers | see hep.h */ int unpack_hepv3(char *buf, int len, struct hep_desc *h) { /*convert from network byte order to host order*/ #define CONVERT_TO_HBO(_hdr) \ do { \ _hdr.vendor_id = ntohs(_hdr.vendor_id); \ _hdr.type_id = ntohs(_hdr.type_id); \ _hdr.length = ntohs(_hdr.length); \ } while (0); #define UPDATE_BUFFER(_buf, _len, _off) \ do { \ _buf += _off; \ _len -= _off; \ } while (0); int rc; unsigned char *compressed_payload; unsigned long compress_len; struct hepv3 h3; unsigned short tlen; unsigned long decompress_len; generic_chunk_t* gen_chunk, *it; u_int16_t chunk_id; str decompressed_payload={NULL, 0}; memset(&h3, 0, sizeof(struct hepv3)); h->version = 3; tlen = ntohs(((hep_ctrl_t*)buf)->length); buf += sizeof(hep_ctrl_t); tlen -= sizeof(hep_ctrl_t); while (tlen > 0) { /* we don't look at vendor id; we only need to parse the buffer */ chunk_id = ((hep_chunk_t*)buf)->type_id; switch (ntohs(chunk_id)) { case HEP_PROTO_FAMILY: /* ip family*/ h3.hg.ip_family = *((hep_chunk_uint8_t*)buf); CONVERT_TO_HBO(h3.hg.ip_family.chunk); UPDATE_BUFFER(buf, tlen, h3.hg.ip_family.chunk.length); break; case HEP_PROTO_ID: /* ip protocol ID*/ h3.hg.ip_proto = *((hep_chunk_uint8_t*)buf); CONVERT_TO_HBO(h3.hg.ip_proto.chunk); UPDATE_BUFFER(buf, tlen, h3.hg.ip_proto.chunk.length); break; case HEP_IPV4_SRC: /* ipv4 source */ h3.addr.ip4_addr.src_ip4 = *((hep_chunk_ip4_t*)buf); CONVERT_TO_HBO(h3.addr.ip4_addr.src_ip4.chunk); UPDATE_BUFFER(buf, tlen, h3.addr.ip4_addr.src_ip4.chunk.length); break; case HEP_IPV4_DST: /* ipv4 dest */ h3.addr.ip4_addr.dst_ip4 = *((hep_chunk_ip4_t*)buf); CONVERT_TO_HBO(h3.addr.ip4_addr.dst_ip4.chunk); UPDATE_BUFFER(buf, tlen, h3.addr.ip4_addr.dst_ip4.chunk.length); break; case HEP_IPV6_SRC: /* ipv6 source */ h3.addr.ip6_addr.src_ip6 = *((hep_chunk_ip6_t*)buf); CONVERT_TO_HBO(h3.addr.ip6_addr.src_ip6.chunk); UPDATE_BUFFER(buf, tlen, h3.addr.ip6_addr.src_ip6.chunk.length); break; case HEP_IPV6_DST: /* ipv6 dest */ h3.addr.ip6_addr.dst_ip6 = *((hep_chunk_ip6_t*)buf); CONVERT_TO_HBO(h3.addr.ip6_addr.dst_ip6.chunk); UPDATE_BUFFER(buf, tlen, h3.addr.ip6_addr.dst_ip6.chunk.length); break; case HEP_SRC_PORT: /* source port */ h3.hg.src_port = *((hep_chunk_uint16_t*)buf); CONVERT_TO_HBO(h3.hg.src_port.chunk); h3.hg.src_port.data = ntohs(h3.hg.src_port.data); UPDATE_BUFFER(buf, tlen, h3.hg.src_port.chunk.length); break; case HEP_DST_PORT: /* dest port */ h3.hg.dst_port = *((hep_chunk_uint16_t*)buf); CONVERT_TO_HBO(h3.hg.dst_port.chunk); h3.hg.dst_port.data = ntohs(h3.hg.dst_port.data); UPDATE_BUFFER(buf, tlen, h3.hg.dst_port.chunk.length); break; case HEP_TIMESTAMP: /* timestamp */ h3.hg.time_sec = *((hep_chunk_uint32_t*)buf); CONVERT_TO_HBO(h3.hg.time_sec.chunk); h3.hg.time_sec.data = ntohl(h3.hg.time_sec.data); UPDATE_BUFFER(buf, tlen, h3.hg.time_sec.chunk.length); break; case HEP_TIMESTAMP_US: /* timestamp microsecs offset */ h3.hg.time_usec = *((hep_chunk_uint32_t*)buf); CONVERT_TO_HBO(h3.hg.time_usec.chunk); h3.hg.time_usec.data = ntohl(h3.hg.time_usec.data); UPDATE_BUFFER(buf, tlen, h3.hg.time_usec.chunk.length); break; case HEP_PROTO_TYPE: /* proto type */ h3.hg.proto_t = *((hep_chunk_uint8_t*)buf); CONVERT_TO_HBO(h3.hg.proto_t.chunk); UPDATE_BUFFER(buf, tlen, h3.hg.proto_t.chunk.length); break; case HEP_AGENT_ID: /* capture agent id */ h3.hg.capt_id = *((hep_chunk_uint32_t*)buf); CONVERT_TO_HBO(h3.hg.capt_id.chunk); h3.hg.capt_id.data = ntohs(h3.hg.capt_id.data); UPDATE_BUFFER(buf, tlen, h3.hg.capt_id.chunk.length); break; case HEP_PAYLOAD: /* captured packet payload */ h3.payload_chunk = *((hep_chunk_payload_t*)buf); h3.payload_chunk.data = (char *)buf + sizeof(hep_chunk_t); CONVERT_TO_HBO(h3.payload_chunk.chunk); UPDATE_BUFFER(buf, tlen, h3.payload_chunk.chunk.length); break; case HEP_COMPRESSED_PAYLOAD: /* captured compressed payload(GZIP/inflate)*/ h3.payload_chunk = *((hep_chunk_payload_t*)buf); h3.payload_chunk.data = (char *)buf + sizeof(hep_chunk_t); /* first update the buffer for further processing * and convert values to host byte order */ CONVERT_TO_HBO(h3.payload_chunk.chunk); UPDATE_BUFFER(buf, tlen, h3.payload_chunk.chunk.length); if (payload_compression) { compressed_payload = (unsigned char *)h3.payload_chunk.data; compress_len =(unsigned long) (h3.payload_chunk.chunk.length - sizeof(hep_chunk_t)); rc=compression_api.decompress(compressed_payload, compress_len, &decompressed_payload, &decompress_len); if (compression_api.check_rc(rc)) { LM_ERR("payload decompression failed!\n"); goto safe_exit; } /* update the length based on the new length */ h3.payload_chunk.chunk.length += (decompress_len - compress_len); h3.payload_chunk.data = decompressed_payload.s; }/* else we're just a proxy; leaving everything as is */ break; default: /* FIXME hep struct will be in shm, but if we put these in shm * locking will be required */ if ((gen_chunk = shm_malloc(sizeof(generic_chunk_t)))==NULL) { LM_ERR("no more pkg mem!\n"); return -1; } memset(gen_chunk, 0, sizeof(generic_chunk_t)); gen_chunk->chunk = *((hep_chunk_t*)buf); CONVERT_TO_HBO(gen_chunk->chunk); gen_chunk->data = shm_malloc(gen_chunk->chunk.length - sizeof(hep_chunk_t)); if (gen_chunk->data == NULL) { LM_ERR("no more shared memory!\n"); return -1; } memcpy(gen_chunk->data, (char *)buf + sizeof(hep_chunk_t), gen_chunk->chunk.length - sizeof(hep_chunk_t)); UPDATE_BUFFER(buf, tlen, gen_chunk->chunk.length); if (h3.chunk_list == NULL) { h3.chunk_list = gen_chunk; } else { for (it=h3.chunk_list; it->next; it=it->next); it->next = gen_chunk; } break; } } safe_exit: h->u.hepv3 = h3; return 0; } opensips-2.2.2/modules/proto_hep/hep.h000066400000000000000000000145561300170765700177720ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2015-08-14 first version (Ionut Ionita) */ #ifndef _HEP_H #define _HEP_H #include "../../ip_addr.h" #define HEP_HEADER_ID "\x48\x45\x50\x33" #define HEP_HEADER_ID_LEN (sizeof(HEP_HEADER_ID) - 1) #define HEP_SCRIPT_SKIP 0xFF #define HEP_MIN_INDEX 0x0001 #define HEP_MAX_INDEX 0x0012 #define HEP_IDENTIFIER 0x0fee0faa #define HEP_OPENSIPS_VENDOR_ID 0x0003 enum hep_generic_chunks { HEP_PROTO_FAMILY=0x0001, HEP_PROTO_ID=0x0002, HEP_IPV4_SRC=0x0003, HEP_IPV4_DST=0x0004, HEP_IPV6_SRC=0x0005, HEP_IPV6_DST=0x0006, HEP_SRC_PORT=0x0007, HEP_DST_PORT=0x0008, HEP_TIMESTAMP=0x0009, HEP_TIMESTAMP_US=0x000A, HEP_PROTO_TYPE=0x000B, HEP_AGENT_ID=0x000C, HEP_KEEP_ALIVE=0x000D, HEP_AUTH_KEY=0x000E, HEP_PAYLOAD=0x000F, HEP_COMPRESSED_PAYLOAD=0x0010, HEP_CORRELATION_ID=0x0011, HEP_VLAN_ID=0x0012}; #define HEP_STRUCT_CHUNKS ((1<=HEP_MIN_INDEX || _cid<=HEP_MAX_INDEX) #define CHUNK_IS_IN_HEPSTRUCT(_cid) ((1<<_cid)&HEP_STRUCT_CHUNKS) /* HEPv3 types */ struct hep_chunk { u_int16_t vendor_id; u_int16_t type_id; u_int16_t length; } __attribute__((packed)); typedef struct hep_chunk hep_chunk_t; struct hep_chunk_uint8 { hep_chunk_t chunk; u_int8_t data; } __attribute__((packed)); typedef struct hep_chunk_uint8 hep_chunk_uint8_t; struct hep_chunk_uint16 { hep_chunk_t chunk; u_int16_t data; } __attribute__((packed)); typedef struct hep_chunk_uint16 hep_chunk_uint16_t; struct hep_chunk_uint32 { hep_chunk_t chunk; u_int32_t data; } __attribute__((packed)); typedef struct hep_chunk_uint32 hep_chunk_uint32_t; struct hep_chunk_str { hep_chunk_t chunk; char *data; } __attribute__((packed)); typedef struct hep_chunk_str hep_chunk_str_t; struct hep_chunk_ip4 { hep_chunk_t chunk; struct in_addr data; } __attribute__((packed)); typedef struct hep_chunk_ip4 hep_chunk_ip4_t; struct hep_chunk_ip6 { hep_chunk_t chunk; struct in6_addr data; } __attribute__((packed)); typedef struct hep_chunk_ip6 hep_chunk_ip6_t; struct hep_ctrl { char id[4]; u_int16_t length; } __attribute__((packed)); typedef struct hep_ctrl hep_ctrl_t; struct hep_chunk_payload { hep_chunk_t chunk; char *data; } __attribute__((packed)); typedef struct hep_chunk_payload hep_chunk_payload_t; /* Structure of HEP */ struct hep_generic { hep_ctrl_t header; hep_chunk_uint8_t ip_family; hep_chunk_uint8_t ip_proto; hep_chunk_uint16_t src_port; hep_chunk_uint16_t dst_port; hep_chunk_uint32_t time_sec; hep_chunk_uint32_t time_usec; hep_chunk_uint8_t proto_t; hep_chunk_uint32_t capt_id; } __attribute__((packed)); typedef struct hep_generic hep_generic_t; typedef char T8; #define BINDING_REQUEST 0x0001; #ifdef __OS_solaris typedef uint8_t u_int8_t; typedef uint16_t u_int16_t; typedef uint32_t u_int32_t; #define IPPROTO_IPIP IPPROTO_ENCAP /* Solaris IPIP protocol has name ENCAP */ #endif struct hep_hdr{ u_int8_t hp_v; /* version */ u_int8_t hp_l; /* length */ u_int8_t hp_f; /* family */ u_int8_t hp_p; /* protocol */ u_int16_t hp_sport; /* source port */ u_int16_t hp_dport; /* destination port */ }; struct hep_timehdr{ u_int32_t tv_sec; /* seconds */ u_int32_t tv_usec; /* useconds */ u_int16_t captid; /* Capture ID node */ }; struct hep_iphdr{ struct in_addr hp_src; struct in_addr hp_dst; /* source and dest address */ }; struct hep_ip6hdr { struct in6_addr hp6_src; /* source address */ struct in6_addr hp6_dst; /* destination address */ }; typedef struct _generic_chunk { hep_chunk_t chunk; void* data; /* blob data */ struct _generic_chunk* next; } generic_chunk_t; struct hep_desc { int version; union { /* hepv12 describing structure */ struct hepv12 { struct hep_hdr hdr; /* only for hepv2*/ struct hep_timehdr hep_time; union { struct hep_iphdr hep_ipheader; struct hep_ip6hdr hep_ip6header; } addr; char *payload; } hepv12; /* hepv3 describing structure */ struct hepv3 { struct hep_generic hg; union { struct ip4_addr { hep_chunk_ip4_t src_ip4; hep_chunk_ip4_t dst_ip4; } ip4_addr; struct ip6_addr { hep_chunk_ip6_t src_ip6; hep_chunk_ip6_t dst_ip6; } ip6_addr; } addr; hep_chunk_payload_t payload_chunk; generic_chunk_t* chunk_list; } hepv3; } u; }; struct hep_context { struct hep_desc h; struct receive_info ri; int resume_with_sip; }; int pack_hep(union sockaddr_union* from_su, union sockaddr_union* to_su, int proto, char *payload, int plen, int hep_version, char **retbuf, int *retlen); int unpack_hepv12(char *buf, int len, struct hep_desc* h); int unpack_hepv3(char *buf, int len, struct hep_desc *h); int unpack_hep(char *buf, int len, int version, struct hep_desc* h); void free_extra_chunks(struct hep_desc* h); typedef int (*pack_hep_t)(union sockaddr_union* from_su, union sockaddr_union* to_su, int proto, char *payload, int plen, int hep_version, char **retbuf, int *retlen); typedef int (*get_hep_ctx_id_t)(void); #endif opensips-2.2.2/modules/proto_hep/hep_cb.c000066400000000000000000000053531300170765700204240ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2015-09-03 first version (Ionut Ionita) */ #include #include #include #include #include "../../timer.h" #include "../../sr_module.h" #include "../../net/api_proto.h" #include "../../net/api_proto_net.h" #include "../../net/net_tcp.h" #include "../../socket_info.h" #include "../../tsend.h" #include "../../net/proto_tcp/tcp_common_defs.h" #include "../../pt.h" #include "../../ut.h" #include "../../context.h" #include "hep.h" #include "hep_cb.h" extern int hep_ctx_idx; struct hep_cb_list { hep_cb_t cb; struct hep_cb_list *next; }; struct hep_cb_list *cb_list=0; int get_hep_ctx_id(void) { return hep_ctx_idx; } int register_hep_cb(hep_cb_t cb) { struct hep_cb_list *cb_el; cb_el = shm_malloc(sizeof(struct hep_cb_list)); if (cb_el == NULL) { LM_ERR("no more shm\n"); return -1; } /* set cb_el->next to 0 */ memset(cb_el, 0, sizeof(struct hep_cb_list)); cb_el->cb = cb; if (cb_list == NULL) { cb_list = cb_el; } else { /* add in front; no need to iterate whole list */ cb_el->next = cb_list; cb_list = cb_el; } return 0; } int run_hep_cbs(void) { int ret, fret=-1; struct hep_cb_list *cb_el; for (cb_el=cb_list; cb_el; cb_el=cb_el->next) { ret=cb_el->cb(); if (ret < 0) { LM_ERR("hep callback failed! Continuing with the other ones!\n"); } else if (ret == HEP_SCRIPT_SKIP) { fret = HEP_SCRIPT_SKIP; } else if (fret == -1) { /* if at least one succeeds then it's ok */ fret = 0; } } return fret; } void free_hep_cbs(void) { struct hep_cb_list *curr, *next; curr = cb_list; while (curr) { next = curr->next; shm_free(curr); curr = next; } } int bind_proto_hep(proto_hep_api_t *api) { if (!api) { LM_ERR("invalid parameter value!\n"); return -1; } api->pack_hep = pack_hep; api->register_hep_cb = register_hep_cb; api->get_hep_ctx_id = get_hep_ctx_id; return 0; } opensips-2.2.2/modules/proto_hep/hep_cb.h000066400000000000000000000040601300170765700204230ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2015-09-03 first version (Ionut Ionita) */ #ifndef HEP_CB_H #define HEP_CB_H #include "hep.h" #include "../../sr_module.h" typedef int (*hep_cb_t)(void); /* in order to register a callback one must import hep.h header * to know the hep_desc structure in which you receive all hep * headers and also the sip payload */ typedef int (*register_hep_cb_t)(hep_cb_t cb); /* * receive message in hep route * it receives the route id * the hep context must have been set when calling this function * */ typedef struct proto_hep_api { register_hep_cb_t register_hep_cb; pack_hep_t pack_hep; get_hep_ctx_id_t get_hep_ctx_id; } proto_hep_api_t; typedef int (*bind_proto_hep_t)(proto_hep_api_t* api); int bind_proto_hep(proto_hep_api_t *api); typedef int (*load_hep_f)(proto_hep_api_t *api); int run_hep_cbs(void); void free_hep_cbs(void); static inline int load_hep_api(proto_hep_api_t* api ) { load_hep_f load_hep; /* import the TM auto-loading function */ if ( !(load_hep=(load_hep_f)find_export("load_hep", 0, 0))) { LM_ERR("failed to import load_hep\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_hep( api )==-1) return -1; return 0; } #endif opensips-2.2.2/modules/proto_hep/proto_hep.c000066400000000000000000001041141300170765700211760ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2015-08-14 first version (Ionut Ionita) */ #include #include #include #include #include #include "../../timer.h" #include "../../sr_module.h" #include "../../net/api_proto.h" #include "../../net/api_proto_net.h" #include "../../net/net_tcp.h" #include "../../net/net_udp.h" #include "../../socket_info.h" #include "../../receive.h" #include "../../tsend.h" #include "../../net/proto_tcp/tcp_common_defs.h" #include "../../net/proto_tcp/tcp_common_defs.h" #include "../../pt.h" #include "../../ut.h" #include "../compression/compression_api.h" #include "hep.h" #include "hep_cb.h" #define HEP_FIRST 1 #define HEP_LAST 3 static int mod_init(void); static void destroy(void); /*!< Module destroy function */ static int proto_hep_init_udp(struct proto_info *pi); static int proto_hep_init_tcp(struct proto_info *pi); static int proto_hep_init_udp_listener(struct socket_info *si); static int hep_conn_init(struct tcp_connection* c); static void hep_conn_clean(struct tcp_connection* c); static int hep_write_async_req(struct tcp_connection* con,int fd); static int hep_tcp_read_req(struct tcp_connection* con, int* bytes_read); static int hep_udp_read_req(struct socket_info *si, int* bytes_read); static int hep_udp_send (struct socket_info* send_sock, char *buf, unsigned int len, union sockaddr_union *to, int id); static int hep_tcp_send (struct socket_info* send_sock, char *buf, unsigned int len, union sockaddr_union *to, int id); static void update_recv_info(struct receive_info *ri, struct hep_desc *h); void free_hep_context(void* ptr); static int hep_port = 5656; static int hep_async = 1; static int hep_send_timeout = 100; static int hep_async_max_postponed_chunks = 32; static int hep_max_msg_chunks = 32; static int hep_async_local_connect_timeout = 100; static int hep_async_local_write_timeout = 10; int hep_ctx_idx=0; int hep_capture_id = 1; int payload_compression=0; compression_api_t compression_api; load_compression_f load_compression; static struct tcp_req hep_current_req; /* we consider that different messages may contain different versions of hep * so we need to know what is the current version of hep */ static int hep_current_proto; struct hep_send_chunk { char *buf; /* buffer that needs to be sent out */ char *pos; /* the position that we should be writing next */ int len; /* length of the buffer */ int ticks; /* time at which this chunk was initially attempted to be written */ }; struct hep_data { /* the chunks that need to be written on this * connection when it will become writable */ struct hep_send_chunk **async_chunks; /* the total number of chunks pending to be written */ int async_chunks_no; /* the oldest chunk in our write list */ int oldest_chunk; }; static cmd_export_t cmds[] = { {"proto_init", (cmd_function)proto_hep_init_udp, 0, 0, 0, 0}, {"proto_init", (cmd_function)proto_hep_init_tcp, 0, 0, 0, 0}, {"load_hep", (cmd_function)bind_proto_hep, 1, 0, 0, 0}, {0,0,0,0,0,0} }; static param_export_t params[] = { { "hep_port", INT_PARAM, &hep_port }, { "hep_send_timeout", INT_PARAM, &hep_send_timeout }, { "hep_max_msg_chunks", INT_PARAM, &hep_max_msg_chunks }, { "hep_async", INT_PARAM, &hep_async }, { "hep_async_max_postponed_chunks", INT_PARAM, &hep_async_max_postponed_chunks }, /* what protocol shall be used: 1, 2 or 3 */ { "hep_capture_id", INT_PARAM, &hep_capture_id }, { "hep_async_local_connect_timeout", INT_PARAM, &hep_async_local_connect_timeout}, { "hep_async_local_write_timeout", INT_PARAM, &hep_async_local_write_timeout }, { "compressed_payload", INT_PARAM, &payload_compression}, {0, 0, 0} }; static module_dependency_t *get_deps_compression(param_export_t *param) { int do_compression= *(int *)param->param_pointer; if (do_compression == 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "compression", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ {"compressed_payload", get_deps_compression}, { NULL, NULL }, }, }; struct module_exports exports = { PROTO_PREFIX "hep", /* module name*/ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ 0, /* per-child init function */ }; static int mod_init(void) { if (payload_compression) { load_compression = (load_compression_f)find_export("load_compression", 1, 0); if (!load_compression) { LM_ERR("can't bind compression module!\n"); return -1; } if (load_compression(&compression_api)) { LM_ERR("failed to load compression api!\n"); return -1; } } hep_ctx_idx = context_register_ptr(CONTEXT_GLOBAL, 0); return 0; } static void destroy(void) { free_hep_cbs(); } void free_hep_context(void *ptr) { struct hep_desc* h; struct hep_context* ctx = (struct hep_context*)ptr; generic_chunk_t* it; generic_chunk_t* foo=NULL; h = &ctx->h; /* for version 3 we may have custom chunks which are in shm so we * need to free them */ if (h->version == 3) { it = h->u.hepv3.chunk_list; while (it) { if (foo) { shm_free(foo->data); shm_free(foo); } foo=it; it=it->next; } if (foo) { shm_free(foo->data); shm_free(foo); } } shm_free(ctx); } static int proto_hep_init_udp(struct proto_info *pi) { pi->id = PROTO_HEP_UDP; pi->name = "hep_udp"; pi->default_port = hep_port; pi->tran.init_listener = proto_hep_init_udp_listener; pi->tran.send = hep_udp_send; pi->net.flags = PROTO_NET_USE_UDP; pi->net.read = (proto_net_read_f)hep_udp_read_req; return 0; } static int proto_hep_init_tcp(struct proto_info *pi) { pi->id = PROTO_HEP_TCP; pi->name = "hep_tcp"; pi->default_port = hep_port; pi->tran.init_listener = tcp_init_listener; pi->tran.dst_attr = tcp_conn_fcntl; pi->net.flags = PROTO_NET_USE_TCP; pi->net.read = (proto_net_read_f)hep_tcp_read_req; pi->net.write = (proto_net_write_f)hep_write_async_req; pi->tran.send = hep_tcp_send; if (hep_async) { pi->net.conn_init = hep_conn_init; pi->net.conn_clean = hep_conn_clean; } return 0; } static int hep_conn_init(struct tcp_connection* c) { struct hep_data *d; /* allocate the tcp_data and the array of chunks as a single mem chunk */ d = (struct hep_data*)shm_malloc( sizeof(struct hep_data) + sizeof(struct hep_send_chunk *) * hep_async_max_postponed_chunks ); if (d == NULL) { LM_ERR("failed to create tcp chunks in shm mem\n"); return -1; } d->async_chunks = (struct hep_send_chunk **)(d+1); d->async_chunks_no = 0; d->oldest_chunk = 0; c->proto_data = (void*)d; return 0; } static void hep_conn_clean(struct tcp_connection* c) { struct hep_data *d = (struct hep_data*)c->proto_data; int r; for (r = 0; r < d->async_chunks_no; r++) { shm_free(d->async_chunks[r]); } shm_free(d); c->proto_data = NULL; } static int proto_hep_init_udp_listener(struct socket_info *si) { return udp_init_listener(si, hep_async?O_NONBLOCK:0); } static int add_write_chunk(struct tcp_connection *con,char *buf,int len, int lock) { struct hep_send_chunk *c; struct hep_data *d = (struct hep_data*)con->proto_data; c = shm_malloc(sizeof(struct hep_send_chunk) + len); if (!c) { LM_ERR("No more SHM\n"); return -1; } c->len = len; c->ticks = get_ticks(); c->buf = (char *)(c+1); memcpy(c->buf,buf,len); c->pos = c->buf; if (lock) lock_get(&con->write_lock); if (d->async_chunks_no == hep_async_max_postponed_chunks) { LM_ERR("We have reached the limit of max async postponed chunks\n"); if (lock) lock_release(&con->write_lock); shm_free(c); return -2; } d->async_chunks[d->async_chunks_no++] = c; if (d->async_chunks_no == 1) d->oldest_chunk = c->ticks; if (lock) lock_release(&con->write_lock); return 0; } static int async_tsend_stream(struct tcp_connection *c, int fd, char* buf, unsigned int len, int timeout) { int written; int n; struct pollfd pf; pf.fd=fd; pf.events=POLLOUT; written=0; again: n=send(fd, buf, len,0); if (n<0){ if (errno==EINTR) goto again; else if (errno!=EAGAIN && errno!=EWOULDBLOCK) { LM_ERR("Failed first TCP async send : (%d) %s\n", errno, strerror(errno)); return -1; } else goto poll_loop; } written+=n; if (n < len) { /* partial write */ buf += n; len -= n; } else { /* succesful write from the first try */ LM_DBG("Async succesful write from first try on %p\n",c); return len; } poll_loop: n = poll(&pf,1,timeout); if (n<0) { if (errno==EINTR) goto poll_loop; LM_ERR("Polling while trying to async send failed %s [%d]\n", strerror(errno), errno); return -1; } else if (n == 0) { LM_DBG("timeout -> do an async write (add it to conn)\n"); /* timeout - let's just pass to main */ if (add_write_chunk(c,buf,len,0) < 0) { LM_ERR("Failed to add write chunk to connection \n"); return -1; } else { /* we have succesfully added async write chunk * tell MAIN to poll out for us */ LM_DBG("Data still pending for write on conn %p\n",c); return 0; } } if (pf.events&POLLOUT) goto again; /* some other events triggered by poll - treat as errors */ return -1; } static struct tcp_connection* hep_sync_connect(struct socket_info* send_sock, union sockaddr_union* server, int *fd) { int s; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (tcp_init_sock_opt(s)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(s, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } if (tcp_connect_blocking(s, &server->s, sockaddru_len(*server))<0){ LM_ERR("tcp_blocking_connect failed\n"); goto error; } con = tcp_conn_create(s, server, send_sock, S_CONN_OK); if (con==NULL){ LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } *fd = s; return con; /*FIXME: set sock idx! */ error: /* close the opened socket */ if (s!=-1) close(s); return 0; } static int tcpconn_async_connect(struct socket_info* send_sock, union sockaddr_union* server, char *buf, unsigned len, struct tcp_connection** c, int *ret_fd) { int fd, n; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; struct pollfd pf; unsigned int elapsed,to; int err; unsigned int err_len; int poll_err; char *ip; unsigned short port; struct timeval begin; /* create the socket */ fd = socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (fd == -1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); return -1; } if (tcp_init_sock_opt(fd)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(fd, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } /* attempt to do connect and see if we do block or not */ poll_err = 0; elapsed = 0; to = hep_async_local_connect_timeout*1000; if (gettimeofday(&(begin), NULL)) { LM_ERR("Failed to get TCP connect start time\n"); goto error; } again: n = connect(fd, &server->s, sockaddru_len(*server)); if (n == -1) { if (errno == EINTR){ elapsed=get_time_diff(&begin); if (elapsed < to) goto again; else { LM_DBG("Local connect attempt failed \n"); goto async_connect; } } if (errno != EINPROGRESS && errno!=EALREADY) { get_su_info(&server->s, ip, port); LM_ERR("[server=%s:%d] (%d) %s\n",ip, port, errno,strerror(errno)); goto error; } } else goto local_connect; /* let's poll for a little */ pf.fd = fd; pf.events = POLLOUT; while(1){ elapsed = get_time_diff(&begin); if (elapsed < to) to -= elapsed; else { LM_DBG("Polling is overdue \n"); goto async_connect; } n = poll(&pf, 1, to/1000); if (n < 0){ if (errno == EINTR) continue; get_su_info(&server->s, ip, port); LM_ERR("poll/select failed:[server=%s:%d] (%d) %s\n", ip, port, errno, strerror(errno)); goto error; } else if (n==0) /* timeout */ continue; if (pf.revents & (POLLERR|POLLHUP|POLLNVAL)){ LM_ERR("poll error: flags %x\n", pf.revents); poll_err=1; } err_len=sizeof(err); getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &err_len); if ((err==0) && (poll_err==0)) goto local_connect; if (err!=EINPROGRESS && err!=EALREADY){ get_su_info(&server->s, ip, port); LM_ERR("failed to retrieve SO_ERROR [server=%s:%d] (%d) %s\n", ip, port, err, strerror(err)); goto error; } } async_connect: LM_DBG("Create connection for async connect\n"); /* create a new dummy connection */ con = tcp_conn_create(fd, server, send_sock, S_CONN_CONNECTING); if (con==NULL) { LM_ERR("tcp_conn_create failed\n"); goto error; } /* attach the write buffer to it */ lock_get(&con->write_lock); if (add_write_chunk(con,buf,len,0) < 0) { LM_ERR("Failed to add the initial write chunk\n"); /* FIXME - seems no more SHM now ... * continue the async connect process ? */ } lock_release(&con->write_lock); /* report an async, in progress connect */ *c = con; return 0; local_connect: con = tcp_conn_create(fd, server, send_sock, S_CONN_OK); if (con==NULL) { LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } *c = con; *ret_fd = fd; /* report a local connect */ return 1; error: close(fd); *c = NULL; return -1; } inline static int _hep_write_on_socket(struct tcp_connection *c, int fd, char *buf, int len){ int n; lock_get(&c->write_lock); if (hep_async) { n=async_tsend_stream(c,fd,buf,len, hep_async_local_write_timeout); } else { n = tsend_stream(fd, buf, len, hep_send_timeout); } lock_release(&c->write_lock); return n; } static int hep_udp_send (struct socket_info* send_sock, char *buf, unsigned int len, union sockaddr_union *to, int id) { int n, tolen; tolen=sockaddru_len(*to); again: n=sendto(send_sock->socket, buf, len, 0, &to->s, tolen); if (n==-1){ LM_ERR("sendto(sock,%p,%d,0,%p,%d): %s(%d)\n", buf,len,to,tolen, strerror(errno),errno); if (errno==EINTR || errno==EAGAIN) goto again; if (errno==EINVAL) { LM_CRIT("invalid sendtoparameters\n" "one possible reason is the server is bound to localhost and\n" "attempts to send to the net\n"); } } return n; } static int hep_tcp_send (struct socket_info* send_sock, char *buf, unsigned int len, union sockaddr_union *to, int id) { struct tcp_connection *c; int port=0; struct ip_addr ip; int fd, n; if (to) { su2ip_addr(&ip, to); port=su_getport(to); n = tcp_conn_get(id,&ip, port, PROTO_HEP_TCP, &c, &fd); } else if (id) { n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd); } else { LM_CRIT("tcp_send called with null id & to\n"); return -1; } if (n < 0) { /* error during conn get, return with error too */ LM_ERR("failed to aquire connection\n"); return -1; } /* was connection found ?? */ if (c==0) { if (tcp_no_new_conn) { return -1; } if (!to) { LM_ERR("Unknown destination - cannot open new tcp connection\n"); return -1; } LM_DBG("no open tcp connection found, opening new one, async = %d\n",hep_async); /* create tcp connection */ if (hep_async) { n = tcpconn_async_connect(send_sock, to, buf, len, &c, &fd); if ( n<0 ) { LM_ERR("async TCP connect failed\n"); return -1; } /* connect succeeded, we have a connection */ if (n==0) { /* connect is still in progress, break the sending * flow now (the actual write will be done when * connect will be completed */ LM_DBG("Succesfully started async connection \n"); tcp_conn_release(c, 0); return len; } /* our first connect attempt succeeded - go ahead as normal */ } else if ((c=hep_sync_connect(send_sock, to, &fd))==0) { LM_ERR("connect failed\n"); return -1; } goto send_it; } /* now we have a connection, let's see what we can do with it */ /* BE CAREFUL now as we need to release the conn before exiting !!! */ if (fd==-1) { /* connection is not writable because of its state - can we append * data to it for later writting (async writting)? */ if (c->state==S_CONN_CONNECTING) { /* the connection is currently in the process of getting * connected - let's append our send chunk as well - just in * case we ever manage to get through */ LM_DBG("We have acquired a TCP connection which is still " "pending to connect - delaying write \n"); n = add_write_chunk(c,buf,len,1); if (n < 0) { LM_ERR("Failed to add another write chunk to %p\n",c); /* we failed due to internal errors - put the * connection back */ tcp_conn_release(c, 0); return -1; } /* we succesfully added our write chunk - success */ tcp_conn_release(c, 0); return len; } else { /* return error, nothing to do about it */ tcp_conn_release(c, 0); return -1; } } send_it: LM_DBG("sending via fd %d...\n",fd); n = _hep_write_on_socket(c, fd, buf, len); tcp_conn_set_lifetime( c, tcp_con_lifetime); LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd); /* LM_DBG("buf=\n%.*s\n", (int)len, buf); */ if (n<0){ LM_ERR("failed to send\n"); c->state=S_CONN_BAD; if (c->proc_id != process_no) close(fd); tcp_conn_release(c, 0); return -1; } /* only close the FD if not already in the context of our process either we just connected, or main sent us the FD */ if (c->proc_id != process_no) close(fd); tcp_conn_release(c, (ncontent_len == 0 && req->pos - req->buf < sizeof(hep_ctrl_t)){ /* not enough intel; keep watching son */ return; } /* check for hepV3 header id; if tcp it's hepv3 */ if (memcmp(req->buf, HEP_HEADER_ID, HEP_HEADER_ID_LEN)) { /* version 3*/ LM_ERR("not a hepV3 message\n"); return; } hep_current_proto = 3; ctrl = (hep_ctrl_t *)req->buf; length = ntohs(ctrl->length); req->content_len = (unsigned short)length; if(req->pos - req->parsed == req->content_len){ LM_DBG("received a COMPLETE message\n"); req->complete = 1; req->parsed = req->buf + req->content_len; } else if(req->pos - req->parsed > req->content_len){ LM_DBG("received MORE than a message\n"); req->complete = 1; req->parsed = req->buf + req->content_len; } else { LM_DBG("received only PART of a message\n"); /* FIXME should we update parsed? we didn't receive the * full message; we wait for the full mesage and only * after that we update parsed */ //req->parsed = req->pos; } } int tcp_read(struct tcp_connection *c,struct tcp_req *r) { int bytes_free, bytes_read; int fd; fd = c->fd; bytes_free=TCP_BUF_SIZE- (int)(r->pos - r->buf); if (bytes_free==0){ LM_ERR("buffer overrun, dropping\n"); r->error=TCP_REQ_OVERRUN; return -1; } again: bytes_read=read(fd, r->pos, bytes_free); if(bytes_read==-1){ if (errno == EWOULDBLOCK || errno == EAGAIN){ return 0; /* nothing has been read */ }else if (errno == EINTR) goto again; else{ LM_ERR("error reading: %s\n",strerror(errno)); r->error=TCP_READ_ERROR; return -1; } }else if (bytes_read==0){ c->state=S_CONN_EOF; LM_DBG("EOF on %p, FD %d\n", c, fd); } #ifdef EXTRA_DEBUG LM_DBG("read %d bytes:\n%.*s\n", bytes_read, bytes_read, r->pos); #endif r->pos+=bytes_read; return bytes_read; } static inline int hep_handle_req(struct tcp_req *req, struct tcp_connection *con, int _max_msg_chunks) { struct receive_info local_rcv; char *msg_buf; int msg_len; long size; int ret=0; struct hep_context *hep_ctx; context_p ctx=NULL; if (req->complete){ /* update the timeout - we successfully read the request */ tcp_conn_set_lifetime( con, tcp_con_lifetime); con->timeout=con->lifetime; /* just for debugging use sendipv4 as receiving socket FIXME*/ con->rcv.proto_reserved1=con->id; /* copy the id */ /* prepare for next request */ size=req->pos-req->parsed; msg_buf = req->start; msg_len = req->parsed-req->start; local_rcv = con->rcv; if (!size) { /* did not read any more things - we can release * the connection */ LM_DBG("Nothing more to read on TCP conn %p, currently in state %d \n", con,con->state); if (req != &hep_current_req) { /* we have the buffer in the connection tied buff - * detach it , release the conn and free it afterwards */ con->con_req = NULL; } /* TODO - we could indicate to the TCP net layer to release * the connection -> other worker may read the next available * message on the pipe */ } else { LM_DBG("We still have things on the pipe - " "keeping connection \n"); } if( msg_buf[0] == 'H' && msg_buf[1] == 'E' && msg_buf[2] == 'P' ) { if ((hep_ctx = shm_malloc(sizeof(struct hep_context))) == NULL) { LM_ERR("no more shared memory!\n"); return -1; } memset(hep_ctx, 0, sizeof(struct hep_context)); memcpy(&hep_ctx->ri, &local_rcv, sizeof(struct receive_info)); /* HEP related */ if (unpack_hepv3(msg_buf, msg_len, &hep_ctx->h)) { LM_ERR("failed to unpack hepV3\n"); goto error_free_hep; } update_recv_info(&local_rcv, &hep_ctx->h); /* set context for receive_msg */ if ((ctx=context_alloc(CONTEXT_GLOBAL)) == NULL) { LM_ERR("failed to allocate new context! skipping...\n"); goto error_free_hep; } memset(ctx, 0, context_size(CONTEXT_GLOBAL)); context_put_ptr(CONTEXT_GLOBAL, ctx, hep_ctx_idx, hep_ctx); /* run hep callbacks; set the current processing context * to hep context; this way callbacks will have all the data * needed */ current_processing_ctx = ctx; ret=run_hep_cbs(); if (ret < 0) { LM_ERR("failed to run hep callbacks\n"); goto error_free_hep; } current_processing_ctx = NULL; msg_len = hep_ctx->h.u.hepv3.payload_chunk.chunk.length- sizeof(hep_chunk_t); /* remove the hep header; leave only the payload */ msg_buf = hep_ctx->h.u.hepv3.payload_chunk.data; } /* skip receive msg if we were told so from at least one callback */ if (ret != HEP_SCRIPT_SKIP && receive_msg(msg_buf, msg_len, &local_rcv, ctx) <0) LM_ERR("receive_msg failed \n"); free_hep_context(hep_ctx); if (!size && req != &hep_current_req) { /* if we no longer need this tcp_req * we can free it now */ pkg_free(req); } if (size) { memmove(req->buf, req->parsed, size); req->pos = req->buf + size; /* anything that was parsed was already processed */ req->parsed = req->buf; } /* if we still have some unparsed bytes, try to parse them too*/ if (size) return 1; } else { /* request not complete - check the if the thresholds are exceeded */ if (con->msg_attempts==0) /* if first iteration, set a short timeout for reading * a whole SIP message */ con->timeout = get_ticks() + tcp_max_msg_time; con->msg_attempts ++; if (con->msg_attempts == _max_msg_chunks) { LM_ERR("Made %u read attempts but message is not complete yet - " "closing connection \n",con->msg_attempts); goto error; } if (req == &hep_current_req) { /* let's duplicate this - most likely another conn will come in */ LM_DBG("We didn't manage to read a full request\n"); con->con_req = pkg_malloc(sizeof(struct tcp_req)); if (con->con_req == NULL) { LM_ERR("No more mem for dynamic con request buffer\n"); goto error; } if (req->pos != req->buf) { /* we have read some bytes */ memcpy(con->con_req->buf,req->buf,req->pos-req->buf); con->con_req->pos = con->con_req->buf + (req->pos-req->buf); } else { con->con_req->pos = con->con_req->buf; } if (req->parsed != req->buf) con->con_req->parsed =con->con_req->buf+(req->parsed-req->buf); else con->con_req->parsed = con->con_req->buf; con->con_req->complete=req->complete; con->con_req->content_len=req->content_len; con->con_req->error = req->error; /* req will be reset on the next usage */ } } /* everything ok */ return 0; error_free_hep: shm_free(hep_ctx); error: /* report error */ return -1; } static int hep_tcp_read_req(struct tcp_connection* con, int* bytes_read) { int bytes; int total_bytes; struct tcp_req *req; bytes = -1; total_bytes = 0; if (con->con_req) { req = con->con_req; LM_DBG("Using the per connection buff \n"); } else { LM_DBG("Using the global ( per process ) buff \n"); init_tcp_req(&hep_current_req, 0); req = &hep_current_req; } again: if(req->error == TCP_REQ_OK){ /* if we still have some unparsed part, parse it first, * don't do the read*/ if (req->parsed < req->pos){ bytes=0; } else { bytes=tcp_read(con,req); if (bytes < 0) { LM_ERR("failed to read \n"); goto error; } } hep_parse_headers(req); total_bytes+=bytes; /* eof check: * is EOF if eof on fd and req. not complete yet, * if req. is complete we might have a second unparsed * request after it, so postpone release_with_eof */ if ((con->state==S_CONN_EOF) && (req->complete==0)) { LM_DBG("EOF received\n"); goto done; } } if (req->error!=TCP_REQ_OK){ LM_ERR("bad request, state=%d, error=%d " "buf:\n%.*s\nparsed:\n%.*s\n", req->state, req->error, (int)(req->pos-req->buf), req->buf, (int)(req->parsed-req->start), req->start); LM_DBG("- received from: port %d\n", con->rcv.src_port); print_ip("- received from: ip ",&con->rcv.src_ip, "\n"); goto error; } switch (hep_handle_req(req, con, hep_max_msg_chunks) ) { case 1: goto again; case -1: goto error; } LM_DBG("tcp_read_req end\n"); done: if (bytes_read) *bytes_read=total_bytes; /* connection will be released */ return 0; error: /* connection will be released as ERROR */ return -1; } static int hep_write_async_req(struct tcp_connection* con,int fd) { int n,left; struct hep_send_chunk *chunk; struct hep_data *d = (struct hep_data*)con->proto_data; if (d->async_chunks_no == 0) { LM_DBG("The connection has been triggered " " for a write event - but we have no pending write chunks\n"); return 0; } next_chunk: chunk=d->async_chunks[0]; again: left = (int)((chunk->buf+chunk->len)-chunk->pos); LM_DBG("Trying to send %d bytes from chunk %p in conn %p - %d %d \n", left,chunk,con,chunk->ticks,get_ticks()); n = send(fd, chunk->pos, left, 0); if (n<0) { if (errno == EINTR) goto again; else if (errno == EAGAIN || errno == EWOULDBLOCK) { LM_DBG("Can't finish to write chunk %p on conn %p\n", chunk,con); /* report back we have more writting to be done */ return 1; } else { LM_ERR("Error occured while sending async chunk %d (%s)\n", errno,strerror(errno)); /* report the conn as broken */ return -1; } } if (n < left) { /* partial write */ chunk->pos+=n; goto again; } else { /* written a full chunk - move to the next one, if any */ shm_free(chunk); d->async_chunks_no--; if (d->async_chunks_no == 0) { LM_DBG("We have finished writing all our async chunks in %p\n",con); d->oldest_chunk=0; /* report back everything ok */ return 0; } else { LM_DBG("We still have %d chunks pending on %p\n", d->async_chunks_no,con); memmove(&d->async_chunks[0],&d->async_chunks[1], d->async_chunks_no * sizeof(struct hep_send_chunk*)); d->oldest_chunk = d->async_chunks[0]->ticks; goto next_chunk; } } } static int hep_udp_read_req(struct socket_info *si, int* bytes_read) { struct receive_info ri; int len; #ifdef DYN_BUF char* buf; #else static char buf [BUF_SIZE+1]; #endif unsigned int fromlen; str msg; struct hep_context *hep_ctx; int ret = 0; context_p ctx=NULL; #ifdef DYN_BUF buf=pkg_malloc(BUF_SIZE+1); if (buf==0){ LM_ERR("could not allocate receive buffer\n"); goto error; } #endif fromlen=sockaddru_len(si->su); len=recvfrom(bind_address->socket, buf, BUF_SIZE,0,&ri.src_su.s,&fromlen); if (len==-1){ if (errno==EAGAIN) return 0; if ((errno==EINTR)||(errno==EWOULDBLOCK)|| (errno==ECONNREFUSED)) return -1; LM_ERR("recvfrom:[%d] %s\n", errno, strerror(errno)); return -2; } if (lenport_no; ri.dst_ip = si->address; ri.proto = si->proto; ri.proto_reserved1 = ri.proto_reserved2 = 0; su2ip_addr(&ri.src_ip, &ri.src_su); ri.src_port=su_getport(&ri.src_su); /* if udp we are sure that version 1 or 2 of the * protocol is used */ if ((hep_ctx = shm_malloc(sizeof(struct hep_context))) == NULL) { LM_ERR("no more shared memory!\n"); return -1; } memset(hep_ctx, 0, sizeof(struct hep_context)); memcpy(&hep_ctx->ri, &ri, sizeof(struct receive_info)); if (len < 4) { LM_ERR("invalid message! too short!\n"); return -1; } if (!memcmp(buf, HEP_HEADER_ID, HEP_HEADER_ID_LEN)) { /* HEPv3 */ if (unpack_hepv3(buf, len, &hep_ctx->h)) { LM_ERR("hepv3 unpacking failed\n"); return -1; } } else { /* HEPv2 */ if (unpack_hepv12(buf, len, &hep_ctx->h)) { LM_ERR("hepv12 unpacking failed\n"); return -1; } } /* set context for receive_msg */ if ((ctx=context_alloc(CONTEXT_GLOBAL)) == NULL) { LM_ERR("failed to allocate new context! skipping...\n"); goto error_free_hep; } memset(ctx, 0, context_size(CONTEXT_GLOBAL)); context_put_ptr(CONTEXT_GLOBAL, ctx, hep_ctx_idx, hep_ctx); update_recv_info(&ri, &hep_ctx->h); /* run hep callbacks; set the current processing context * to hep context; this way callbacks will have all the data * needed */ current_processing_ctx = ctx; ret=run_hep_cbs(); if (ret < 0) { LM_ERR("failed to run hep callbacks\n"); return -1; } current_processing_ctx = NULL; if (hep_ctx->h.version == 3) { /* HEPv3 */ msg.len = hep_ctx->h.u.hepv3.payload_chunk.chunk.length- sizeof(hep_chunk_t); msg.s = hep_ctx->h.u.hepv3.payload_chunk.data; } else { /* HEPv12 */ msg.len = len - hep_ctx->h.u.hepv12.hdr.hp_l; msg.s = buf + hep_ctx->h.u.hepv12.hdr.hp_l; if (hep_ctx->h.u.hepv12.hdr.hp_v == 2) { msg.s += sizeof(struct hep_timehdr); msg.len -= sizeof(struct hep_timehdr); } } if (ret != HEP_SCRIPT_SKIP) { /* receive_msg must free buf too!*/ receive_msg( msg.s, msg.len, &ri, ctx); } free_hep_context(hep_ctx); return 0; error_free_hep: shm_free(hep_ctx); return -1; } static void update_recv_info(struct receive_info *ri, struct hep_desc *h) { unsigned proto; unsigned ip_family; unsigned sport, dport; struct ip_addr dst_ip, src_ip; switch (h->version) { case 1: case 2: ip_family = h->u.hepv12.hdr.hp_f; proto = h->u.hepv12.hdr.hp_p; sport = h->u.hepv12.hdr.hp_sport; dport = h->u.hepv12.hdr.hp_dport; switch (ip_family) { case AF_INET: dst_ip.af = src_ip.af = AF_INET; dst_ip.len = src_ip.len = 4; memcpy(&dst_ip.u.addr, &h->u.hepv12.addr.hep_ipheader.hp_dst, 4); memcpy(&src_ip.u.addr, &h->u.hepv12.addr.hep_ipheader.hp_src, 4); break; case AF_INET6: dst_ip.af = src_ip.af = AF_INET6; dst_ip.len = src_ip.len = 16; memcpy(&dst_ip.u.addr, &h->u.hepv12.addr.hep_ip6header.hp6_dst, 16); memcpy(&src_ip.u.addr, &h->u.hepv12.addr.hep_ip6header.hp6_src, 16); break; } break; case 3: ip_family = h->u.hepv3.hg.ip_family.data; proto = h->u.hepv3.hg.ip_proto.data; sport = h->u.hepv3.hg.src_port.data; dport = h->u.hepv3.hg.dst_port.data; switch (ip_family) { case AF_INET: dst_ip.af = src_ip.af = AF_INET; dst_ip.len = src_ip.len = 4; memcpy(&dst_ip.u.addr, &h->u.hepv3.addr.ip4_addr.dst_ip4.data, 4); memcpy(&src_ip.u.addr, &h->u.hepv3.addr.ip4_addr.src_ip4.data, 4); break; case AF_INET6: dst_ip.af = src_ip.af = AF_INET6; dst_ip.len = src_ip.len = 16; memcpy(&dst_ip.u.addr, &h->u.hepv3.addr.ip6_addr.dst_ip6.data, 16); memcpy(&src_ip.u.addr, &h->u.hepv3.addr.ip6_addr.src_ip6.data, 16); break; } break; default: LM_ERR("invalid hep version!\n"); return; } if(proto == IPPROTO_UDP) ri->proto=PROTO_UDP; else if(proto == IPPROTO_TCP) ri->proto=PROTO_TCP; else if(proto == IPPROTO_IDP) ri->proto=PROTO_TLS; /* fake protocol */ else if(proto == IPPROTO_SCTP) ri->proto=PROTO_SCTP; else if(proto == IPPROTO_ESP) ri->proto=PROTO_WS; /* fake protocol */ else { LM_ERR("unknown protocol [%d]\n",proto); proto = PROTO_NONE; } if (h->version == 3) h->u.hepv3.hg.ip_proto.data = ri->proto; ri->src_ip = src_ip; ri->src_port = sport; ri->dst_ip = dst_ip; ri->dst_port = dport; } opensips-2.2.2/modules/proto_hep/proto_hep.h000066400000000000000000000020221300170765700211760ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2016-01-22 first version (Ionut Ionita) */ #ifndef _PROTO_HEP_ #define _PROTO_HEP_ extern int proto_tcp_read(struct tcp_connection*, struct tcp_req*); #endif opensips-2.2.2/modules/proto_sctp/000077500000000000000000000000001300170765700172275ustar00rootroot00000000000000opensips-2.2.2/modules/proto_sctp/Makefile000066400000000000000000000002721300170765700206700ustar00rootroot00000000000000# # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=proto_sctp.so LIBS += -lsctp include ../../Makefile.modules opensips-2.2.2/modules/proto_sctp/README000066400000000000000000000033201300170765700201050ustar00rootroot00000000000000proto_sctp Module OpenSIPS Project Edited by Liviu Chircu Copyright © 2015 OpenSIPS Project __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. sctp_port (integer) 2. Frequently Asked Questions List of Examples 1.1. Set sctp_port parameter Chapter 1. Admin Guide 1.1. Overview The proto_sctp module is an optional transport module (shared library) which exports the required logic in order to handle SCTP-based communication. (socket initialization and send/recv primitives to be used by higher-level network layers) Once loaded, you will be able to define "sctp:" listeners in your script. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * None. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. sctp_port (integer) The default port to be used by all SCTP listeners. Default value is 5060. Example 1.1. Set sctp_port parameter ... modparam("proto_sctp", "sctp_port", 5070) ... Chapter 2. Frequently Asked Questions 2.1. After switching to OpenSIPS 2.1, I'm getting this error: "listeners found for protocol sctp, but no module can handle it" You need to load the "proto_sctp" module. In your script, make sure you do a loadmodule "proto_sctp.so" after setting the mpath. opensips-2.2.2/modules/proto_sctp/doc/000077500000000000000000000000001300170765700177745ustar00rootroot00000000000000opensips-2.2.2/modules/proto_sctp/doc/proto_sctp.xml000066400000000000000000000016621300170765700227170ustar00rootroot00000000000000 %docentities; ]> proto_sctp Module &osipsname; &osips; Project
support@opensips.org
Liviu Chircu
liviu@opensips.org
2015 &osips; Project
&admin; &faq;
opensips-2.2.2/modules/proto_sctp/doc/proto_sctp_admin.xml000066400000000000000000000031521300170765700240630ustar00rootroot00000000000000 &adminguide;
Overview The proto_sctp module is an optional transport module (shared library) which exports the required logic in order to handle SCTP-based communication. (socket initialization and send/recv primitives to be used by higher-level network layers) Once loaded, you will be able to define "sctp:" listeners in your script.
Dependencies
&osips; Modules The following modules must be loaded before this module: None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>sctp_port</varname> (integer) The default port to be used by all SCTP listeners. Default value is 5060. Set <varname>sctp_port</varname> parameter ... modparam("proto_sctp", "sctp_port", 5070) ...
opensips-2.2.2/modules/proto_sctp/doc/proto_sctp_faq.xml000066400000000000000000000012261300170765700235420ustar00rootroot00000000000000 &faqguide; After switching to OpenSIPS 2.1, I'm getting this error: "listeners found for protocol sctp, but no module can handle it" You need to load the "proto_sctp" module. In your script, make sure you do a loadmodule "proto_sctp.so" after setting the mpath. opensips-2.2.2/modules/proto_sctp/proto_sctp.c000066400000000000000000000044001300170765700215650ustar00rootroot00000000000000/* * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-12 first version (bogdan) */ #include "../../dprint.h" #include "../../net/trans.h" #include "../../sr_module.h" #include "sctp_server.h" static int sctp_port = SIP_PORT; static int proto_sctp_init(struct proto_info *pi); static cmd_export_t cmds[] = { {"proto_init", (cmd_function)proto_sctp_init, 0, 0, 0, 0}, {0,0,0,0,0,0} }; static param_export_t params[] = { { "sctp_port", INT_PARAM, &sctp_port }, {0, 0, 0} }; struct module_exports exports = { PROTO_PREFIX "sctp", /* module name*/ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ 0, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* per-child init function */ }; static int proto_sctp_init(struct proto_info *pi) { pi->id = PROTO_SCTP; pi->name = "sctp"; pi->default_port = sctp_port; pi->tran.init_listener = proto_sctp_init_listener; pi->tran.send = proto_sctp_send; pi->net.flags = PROTO_NET_USE_UDP; pi->net.read = (proto_net_read_f)proto_sctp_read; return 0; } opensips-2.2.2/modules/proto_sctp/sctp_server.c000066400000000000000000000133361300170765700217400ustar00rootroot00000000000000/* * Copyright (C) 2015 OpenSIPS Solutions * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * -------- * 2007-06-22 sctp_server.c created, using udp_server.c as template -gmarmon * 2015-02-19 migrated to the new proto interfaces (bogdan) */ /*! * \file * \brief SCTP support */ #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #endif #include "../../mem/shm_mem.h" #include "../../sr_module.h" #include "../../net/net_udp.h" #include "../../socket_info.h" #include "../../receive.h" #include "sctp_server.h" #define LISTEN_BACKLOG 5 int proto_sctp_init_listener(struct socket_info* sock_info) { union sockaddr_union* addr; int optval; addr=&sock_info->su; sock_info->proto=PROTO_SCTP; if (init_su(addr, &sock_info->address, sock_info->port_no)<0){ LM_ERR("could not init sockaddr_union\n"); goto error; } sock_info->socket = socket(AF2PF(addr->s.sa_family), SOCK_SEQPACKET, IPPROTO_SCTP); if (sock_info->socket==-1){ LM_ERR("socket failed with %s [%d]\n", strerror(errno), errno); goto error; } optval=1; if (setsockopt(sock_info->socket, SOL_SOCKET, SO_REUSEADDR , (void*)&optval, sizeof(optval)) ==-1){ LM_ERR("setsockopt: %s\n", strerror(errno)); goto error; } #ifdef DISABLE_NAGLE /* turns of Nagle-like algorithm/chunk-bundling.*/ optval=1; if (setsockopt(sock_info->socket, IPPROTO_SCTP, SCTP_NODELAY, (void*)&optval, sizeof(optval))==-1){ LM_WARN("setsockopt %s\n", strerror(errno)); /* continues since this is not critical */ } #endif /* this sockopt causes a kernel panic in some sctp implementations. * commenting it out. -gmarmon */ /* tos optval=tos; if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval, sizeof(optval)) ==-1){ LM_WARN("setsockopt tos: %s\n", strerror(errno)); } */ #if defined (__linux__) && defined(SCTP_ERRORS) /* will SCTP_ERRORS ever be defined? -gmarmon */ optval=1; /* enable error receiving on unconnected sockets */ if(setsockopt(sock_info->socket, SOL_IP, IP_RECVERR, (void*)&optval, sizeof(optval)) ==-1){ LM_ERR("setsockopt: %s\n", strerror(errno)); goto error; } #endif /*if ( probe_max_receive_buffer(sock_info->socket)==-1) goto error; */ if (bind(sock_info->socket, &addr->s, sockaddru_len(*addr))==-1){ LM_ERR("bind(%x, %p, %d) on %s: %s\n", sock_info->socket, &addr->s, (unsigned)sockaddru_len(*addr), sock_info->address_str.s, strerror(errno)); if (addr->s.sa_family==AF_INET6) LM_ERR("might be caused by using a link " " local address, try site local or global\n"); goto error; } if(listen(sock_info->socket, LISTEN_BACKLOG)<0){ LM_ERR("listen(%x, %d) on %s: %s\n", sock_info->socket, LISTEN_BACKLOG, sock_info->address_str.s, strerror(errno)); goto error; } return 0; error: return -1; } int proto_sctp_read(struct socket_info *si, int* bytes_read) { struct receive_info ri; int len; #ifdef DYN_BUF char* buf; #else static char buf [BUF_SIZE+1]; #endif char *tmp; unsigned int fromlen; struct sctp_sndrcvinfo sinfo; #ifdef DYN_BUF buf=pkg_malloc(BUF_SIZE+1); if (buf==0){ LM_ERR(" could not allocate receive buffer in pkg memory\n"); goto error; } #endif fromlen=sockaddru_len(si->su); len = sctp_recvmsg(si->socket, buf, BUF_SIZE, &ri.src_su.s, &fromlen, &sinfo, 0); if (len==-1){ if (errno==EAGAIN){ LM_DBG("packet with bad checksum received\n"); return 0; } if ((errno==EINTR)||(errno==EWOULDBLOCK)|| (errno==ECONNREFUSED)) return -1; LM_ERR("sctp_recvmsg:[%d] %s\n", errno, strerror(errno)); return -2; } /* we must 0-term the messages, receive_msg expects it */ buf[len]=0; /* no need to save the previous char */ ri.bind_address = si; ri.dst_port = si->port_no; ri.dst_ip = si->address; ri.proto = si->proto; ri.proto_reserved1 = ri.proto_reserved2 = 0; su2ip_addr(&ri.src_ip, &ri.src_su); ri.src_port=su_getport(&ri.src_su); if (ri.src_port==0){ tmp=ip_addr2a(&ri.src_ip); LM_INFO("dropping 0 port packet from %s\n", tmp); return 0; } /* receive_msg must free buf too!*/ receive_msg(buf, len, &ri, NULL); return 0; } /*! \brief which socket to use? main socket or new one? */ int proto_sctp_send(struct socket_info *source, char *buf, unsigned len, union sockaddr_union* to, int id) { int n; int tolen; tolen=sockaddru_len(*to); again: n=sctp_sendmsg(source->socket, buf, len, &to->s, tolen, 0, 0, 0, 0, 0); #ifdef XL_DEBUG LM_INFO("send status: %d\n", n); #endif if (n==-1){ LM_ERR("sctp_sendmsg(sock,%p,%d,%p,%d,0,0,0,0,0): %s(%d)\n", buf,len,&to->s,tolen, strerror(errno),errno); if (errno==EINTR) goto again; if (errno==EINVAL) { LM_CRIT("invalid sendtoparameters\n" "one possible reason is the server is bound to localhost and\n" "attempts to send to the net\n"); } } return n; } opensips-2.2.2/modules/proto_sctp/sctp_server.h000066400000000000000000000025651300170765700217470ustar00rootroot00000000000000/* * Copyright (C) 2015 OpenSIPS Solutions * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * -------- * 2007-06-22 sctp_server.h created, using udp_server.h as template -gmarmon * 2015-02-19 migrated to the new proto interfaces (bogdan) */ /*! * \file * \brief SCTP protocol support */ #ifndef _MOD_SCTP_sctp_server_h #define _MOD_SCTP_sctp_server_h #include "../../ip_addr.h" int proto_sctp_init_listener(struct socket_info* si); int proto_sctp_send(struct socket_info *source, char *buf, unsigned len, union sockaddr_union* to, int id); int proto_sctp_read(struct socket_info *si, int* bytes_read); #endif opensips-2.2.2/modules/proto_tls/000077500000000000000000000000001300170765700170605ustar00rootroot00000000000000opensips-2.2.2/modules/proto_tls/Makefile000066400000000000000000000011551300170765700205220ustar00rootroot00000000000000# # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=proto_tls.so ETC_DIR?=../../etc/ ifeq ($(CROSS_COMPILE),) SSL_BUILDER=$(shell \ if pkg-config --exists libssl; then \ echo 'pkg-config libssl'; \ fi) endif ifneq ($(SSL_BUILDER),) DEFS += $(shell $(SSL_BUILDER) --cflags) LIBS += $(shell $(SSL_BUILDER) --libs) else DEFS += -I$(LOCALBASE)/ssl/include \ -I$(LOCALBASE)/include LIBS += -L$(LOCALBASE)/lib -L$(LOCALBASE)/ssl/lib \ -L$(LOCALBASE)/lib64 -L$(LOCALBASE)/ssl/lib64 \ -lssl -lcrypto endif include ../../Makefile.modules opensips-2.2.2/modules/proto_tls/README000066400000000000000000000271231300170765700177450ustar00rootroot00000000000000TLS module Peter Griffiths unknown Klaus Darilion enum.at Edited by Klaus Darilion Edited by Bogdan-Andrei Iancu Edited by Cesc Santasusana Edited by Klaus Darilion Edited by Christian Lahme Edited by Ionut-Razvan Ionita Copyright © 2005 Voice Sistem SRL Copyright © 2005 Cesc Santasusana Copyright © 2006 enum.at Copyright © 2013 Secusmart GmbH Copyright © 2015 OpenSIPS Solutions __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. History 1.3. Scenario 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. Dependencies of external libraries 1.5. OpenSIPS Exported parameters 1.5.1. listen=interface 1.5.2. tls_port (integer) 1.5.3. tls_crlf_pingpong (integer) 1.5.4. tls_crlf_drop (integer) 1.5.5. tls_max_msg_chunks (integer) 2. Frequently Asked Questions List of Examples 1.1. Set listen variable 1.2. Set tls_port variable 1.3. Set tls_crlf_pingpong parameter 1.4. Set tls_crlf_drop parameter 1.5. Set tls_max_msg_chunks parameter Chapter 1. Admin Guide 1.1. Overview TLS, as defined in SIP RFC 3261, is a mandatory feature for proxies and can be used to secure the SIP signalling on a hop-by-hop basis (not end-to-end). TLS works on top of TCP. DTLS, or TLS over UDP is already defined by IETF and may become available in the future. 1.2. History The TLS support was originally developed by Peter Griffiths and posted as a patch on SER development mailing list. Thanks to Cesc Santasusana, several problems were fixed and some improvements were added. The TLS support was simultaneously added in both projects. In SER, the support was committed in a separate “experimental†CVS tree, as patch to the main CVS tree. In OpenSIPS, the support was integrated directly into the CVS tree, as a built-in component, and is part of stable OpenSIPS since release >=1.0.0. Starting with OpenSIPS 2.1, the TLS has been moved to a separate transport module, that implements the more generic Transport Interface. 1.3. Scenario By the increased number of providers the SIP world is continuously growing. More users means more calls and more calls means a high probability for a user to receive calls from totally unknown people or, in the worst case, to receive unwanted calls. To prevent this, a defense mechanism must be adopted by the SIP provider. Since only the called user is fully able to classify a call as being unwanted, the SIP server, based on all information regarding the call should notify the user about the desirability of the call. Information like the caller domain, the received source or the incoming protocol can be very useful for a SIP server to establish the nature of the call. As this information is quite limited, is very improbable for a server to be able detect the unwanted calls - there are many calls that it cannot predict anything about its status (neutral calls). So, instead on alerting the called user about unwanted calls, the server can notify the user about calls that are considered trusted - calls for which the server is 100% sure there are not unwanted. So, a trust concept must be defined for SIP servers. Which calls are trusted and which are not? A call is trusted if the caller can be identify as a trustable user - a user about we have reliable information. Since all the user from its domain are authenticated (or should be), a SIP server can consider all the calls generated by its user as trusted. Now we have to extend the trust concept to the multi-domain level. A mutual agreement, between several domains, can establish a trusting relationship. So, a domain (called A) will consider also as trusted calls all the calls generated by user from a different domain (called B) and vice-versa. But just an agreement is not enough; since the authentication information is strictly limited to a domain (a domain can authenticate only its own user, not the user from other domains), there is still the problem of checking the authenticity of the caller - he can impersonate (by a false FROM header) a user from a domain that is trusted. The answer to this problem is TLS (Transport Layer Security). All calls via domain A and domain B will be done via TLS. Authentication in origin domain plus TLS transport between domains will make the call 100% trusted for the target domain. For such a mechanism to work, the following requirements must be met: * all UA must have set as outbound proxy their home server. * all SIP servers must authenticated all the calls generated by their own users. * all SIP servers must relay the calls generated be their user to a trusted domain via TLS. Based on this, a server can classify as trusted a call for one of its user only if the call is also generated by one of its users or is the call is received from a trusted domain ( which is equivalent with a call received via TLS). Untrusted call will be calls received from users belonging to untrusted domains or from users from trusted domains, but whose calls are not routed via their home server (so, they are not authenticated by there home servers). Once the server is able to tell if the call is trusted or not, the still open issue is about the mechanism used by server to notify the called user about the nature of the incoming call. One way to do it is by remotely changing the ringing type of the called user's phone. This can be done by inserting special header into the INVITE request. Such feature is supported by now by several hardphones like CISCO ATA, CISCO 7960 and SNOM. This phones can change their ringing tone based on the present or content of the "Alert-Info" SIP header as follows: * CISCO ATA - it has 4 pre-defined ringing types. The Alert-Info header must look like “Alert-info: Bellcore-drX EOH†where X can be between 1 and 4. Note that 1 is the phone default ringing tone. * CISCO 7960 - it has 2 pre-defined ringing types and the possibility of uploading new ones. The “Alert-Info†header must look like “Alert-info: X EOH†where X can be whatever number. When this header is present, the phones will not change the ringing tone, but the ringing pattern. Normally, the phone rings like [ring.........ring..........ring] where [ring] is the ringing tone; if the header is present, the ringing pattern will be [ring.ring.........ring.ring........]. So, to be able to hear some difference between the two patterns (and not only as length), its strongly recommended to have a highly asymmetric ringing type (as the pre-defined are not!!). * SNOM - The “Alert-Info†header must look like “Alert-info: URL EOH"†where URL can be a HTTP URL (for example) from where the phone can retrieve a ringing tone. 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * tls_mgm. 1.4.2. Dependencies of external libraries OpenSIPS TLS v1.0 support requires the following packages: * openssl or libssl >= 0.9.6 * openssl-dev or libssl-dev OpenSIPS TLS v1.1/1.2 support requires the following packages: * openssl or libssl >= 1.0.1e * openssl-dev or libssl-dev 1.5. OpenSIPS Exported parameters All these parameters can be used from the opensips.cfg file, to configure the behavior of OpenSIPS-TLS. 1.5.1. listen=interface Not specific to TLS. Allows to specify the protocol (udp, tcp, tls), the IP address and the port where the listening server will be. Example 1.1. Set listen variable ... listen = tls:1.2.3.4:5061 ... 1.5.2. tls_port (integer) Sets the default TLS listening port. Default value is 5061. Example 1.2. Set tls_port variable ... modparam("proto_tls", "tls_port", 5062) ... 1.5.3. tls_crlf_pingpong (integer) Send CRLF pong (\r\n) to incoming CRLFCRLF ping messages over TLS. By default it is enabled (1). Default value is 1 (enabled). Example 1.3. Set tls_crlf_pingpong parameter ... modparam("proto_tls", "tls_crlf_pingpong", 0) ... 1.5.4. tls_crlf_drop (integer) Drop CRLF (\r\n) ping messages. When this parameter is enabled, the TLS layer drops packets that contains a single CRLF message. If a CRLFCRLF message is received, it is handled according to the tls_crlf_pingpong parameter. Default value is 0 (disabled). Example 1.4. Set tls_crlf_drop parameter ... modparam("proto_tls", "tls_crlf_drop", 1) ... 1.5.5. tls_max_msg_chunks (integer) The maximum number of chunks that a SIP message is expected to arrive via TLS. If a packet is received more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending the traffic very fragmented in order to decrease server performance). Default value is 4. Example 1.5. Set tls_max_msg_chunks parameter ... modparam("proto_tls", "tls_max_msg_chunks", 8) ... Chapter 2. Frequently Asked Questions 2.1. Where can I post a question about TLS? Use one (the most appropriate) of the OpenSIPS mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel Remember: first at all, check if your question wasn't already answered. 2.2. How can I report a bug? Accumulate as much as possible information (OpenSIPS version, opensips -V output, your OS (uname -a), OpenSIPS logs, network dumps, core dump files, configuration file) and send a mail to http://lists.opensips.org/cgi-bin/mailman/listinfo/devel Also you may try OpenSIPS's bug report web page: http://www.opensips.org/pmwiki.php?n=Development.Tracker 2.3. How can I debug ssl/tls problems? Increase the log level in opensips.cfg (log_level=4) and watch the log statements in syslog. Install the ssldump utility and start it. This will give you a trace of the ssl/tls connections. 2.4. What is the difference between the TLS directory and the TLSOPS module directory? The code in the TLS directory implements the TLS transport layer. The TLSOPS module implements TLS related functions which can be used in the routing script. 2.5. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.6. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.7. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/proto_tls/TODO.TLS000066400000000000000000000004161300170765700202120ustar00rootroot00000000000000$Id$ * Finish up the documentation item date: 16 June 2005 * Commit test certificates to ease testing item date: 16 June 2005 * tls domains: allow more configurability and test the whole thing item date: 16 June 2005 Mantainer: cesc dot santa at gmail dot com opensips-2.2.2/modules/proto_tls/doc/000077500000000000000000000000001300170765700176255ustar00rootroot00000000000000opensips-2.2.2/modules/proto_tls/doc/proto_tls.xml000066400000000000000000000045301300170765700223760ustar00rootroot00000000000000 %docentities; ]> TLS module &osipsname; Peter Griffiths unknown
peter_grf@yahoo.com
Klaus Darilion enum.at
klaus.darilion@enum.at
Klaus Darilion
klaus.darilion@enum.at
Bogdan-Andrei Iancu
bogdan@opensips.org
Cesc Santasusana
cesc.santa@gmail.com
Klaus Darilion
klaus.darilion@nic.at
Christian Lahme
christian.lahme@secusmart.com
Ionut-Razvan Ionita
ionutionita@opensips.org
2005 &voicesystem; 2005 Cesc Santasusana 2006 enum.at 2013 Secusmart GmbH 2015 &osipssolname;
&admin; &faq;
opensips-2.2.2/modules/proto_tls/doc/proto_tls_admin.xml000066400000000000000000000251241300170765700235500ustar00rootroot00000000000000 &adminguide;
Overview TLS, as defined in SIP RFC 3261, is a mandatory feature for proxies and can be used to secure the SIP signalling on a hop-by-hop basis (not end-to-end). TLS works on top of TCP. DTLS, or TLS over UDP is already defined by IETF and may become available in the future.
History The TLS support was originally developed by Peter Griffiths and posted as a patch on SER development mailing list. Thanks to Cesc Santasusana, several problems were fixed and some improvements were added. The TLS support was simultaneously added in both projects. In SER, the support was committed in a separate experimental CVS tree, as patch to the main CVS tree. In OpenSIPS, the support was integrated directly into the CVS tree, as a built-in component, and is part of stable OpenSIPS since release >=1.0.0. Starting with OpenSIPS 2.1, the TLS has been moved to a separate transport module, that implements the more generic Transport Interface.
Scenario By the increased number of providers the SIP world is continuously growing. More users means more calls and more calls means a high probability for a user to receive calls from totally unknown people or, in the worst case, to receive unwanted calls. To prevent this, a defense mechanism must be adopted by the SIP provider. Since only the called user is fully able to classify a call as being unwanted, the SIP server, based on all information regarding the call should notify the user about the desirability of the call. Information like the caller domain, the received source or the incoming protocol can be very useful for a SIP server to establish the nature of the call. As this information is quite limited, is very improbable for a server to be able detect the unwanted calls - there are many calls that it cannot predict anything about its status (neutral calls). So, instead on alerting the called user about unwanted calls, the server can notify the user about calls that are considered trusted - calls for which the server is 100% sure there are not unwanted. So, a trust concept must be defined for SIP servers. Which calls are trusted and which are not? A call is trusted if the caller can be identify as a trustable user - a user about we have reliable information. Since all the user from its domain are authenticated (or should be), a SIP server can consider all the calls generated by its user as trusted. Now we have to extend the trust concept to the multi-domain level. A mutual agreement, between several domains, can establish a trusting relationship. So, a domain (called A) will consider also as trusted calls all the calls generated by user from a different domain (called B) and vice-versa. But just an agreement is not enough; since the authentication information is strictly limited to a domain (a domain can authenticate only its own user, not the user from other domains), there is still the problem of checking the authenticity of the caller - he can impersonate (by a false FROM header) a user from a domain that is trusted. The answer to this problem is TLS (Transport Layer Security). All calls via domain A and domain B will be done via TLS. Authentication in origin domain plus TLS transport between domains will make the call 100% trusted for the target domain. For such a mechanism to work, the following requirements must be met: all UA must have set as outbound proxy their home server. all SIP servers must authenticated all the calls generated by their own users. all SIP servers must relay the calls generated be their user to a trusted domain via TLS. Based on this, a server can classify as trusted a call for one of its user only if the call is also generated by one of its users or is the call is received from a trusted domain ( which is equivalent with a call received via TLS). Untrusted call will be calls received from users belonging to untrusted domains or from users from trusted domains, but whose calls are not routed via their home server (so, they are not authenticated by there home servers). Once the server is able to tell if the call is trusted or not, the still open issue is about the mechanism used by server to notify the called user about the nature of the incoming call. One way to do it is by remotely changing the ringing type of the called user's phone. This can be done by inserting special header into the INVITE request. Such feature is supported by now by several hardphones like CISCO ATA, CISCO 7960 and SNOM. This phones can change their ringing tone based on the present or content of the "Alert-Info" SIP header as follows: CISCO ATA - it has 4 pre-defined ringing types. The Alert-Info header must look like Alert-info: Bellcore-drX EOH where X can be between 1 and 4. Note that 1 is the phone default ringing tone. CISCO 7960 - it has 2 pre-defined ringing types and the possibility of uploading new ones. The Alert-Info header must look like Alert-info: X EOH where X can be whatever number. When this header is present, the phones will not change the ringing tone, but the ringing pattern. Normally, the phone rings like [ring.........ring..........ring] where [ring] is the ringing tone; if the header is present, the ringing pattern will be [ring.ring.........ring.ring........]. So, to be able to hear some difference between the two patterns (and not only as length), its strongly recommended to have a highly asymmetric ringing type (as the pre-defined are not!!). SNOM - The Alert-Info header must look like Alert-info: URL EOH" where URL can be a HTTP URL (for example) from where the phone can retrieve a ringing tone.
Dependencies
&osips; Modules The following modules must be loaded before this module: tls_mgm.
Dependencies of external libraries &osips; TLS v1.0 support requires the following packages: openssl or libssl >= 0.9.6 openssl-dev or libssl-dev &osips; TLS v1.1/1.2 support requires the following packages: openssl or libssl >= 1.0.1e openssl-dev or libssl-dev
&osips; Exported parameters All these parameters can be used from the opensips.cfg file, to configure the behavior of &osips;-TLS.
<varname>listen</varname>=interface Not specific to TLS. Allows to specify the protocol (udp, tcp, tls), the IP address and the port where the listening server will be. Set <varname>listen</varname> variable ... listen = tls:1.2.3.4:5061 ...
<varname>tls_port</varname> (integer) Sets the default TLS listening port. Default value is 5061. Set <varname>tls_port</varname> variable ... modparam("proto_tls", "tls_port", 5062) ...
<varname>tls_crlf_pingpong</varname> (integer) Send CRLF pong (\r\n) to incoming CRLFCRLF ping messages over TLS. By default it is enabled (1). Default value is 1 (enabled). Set <varname>tls_crlf_pingpong</varname> parameter ... modparam("proto_tls", "tls_crlf_pingpong", 0) ...
<varname>tls_crlf_drop</varname> (integer) Drop CRLF (\r\n) ping messages. When this parameter is enabled, the TLS layer drops packets that contains a single CRLF message. If a CRLFCRLF message is received, it is handled according to the tls_crlf_pingpong parameter. Default value is 0 (disabled). Set <varname>tls_crlf_drop</varname> parameter ... modparam("proto_tls", "tls_crlf_drop", 1) ...
<varname>tls_max_msg_chunks</varname> (integer) The maximum number of chunks that a SIP message is expected to arrive via TLS. If a packet is received more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending the traffic very fragmented in order to decrease server performance). Default value is 4. Set <varname>tls_max_msg_chunks</varname> parameter ... modparam("proto_tls", "tls_max_msg_chunks", 8) ...
opensips-2.2.2/modules/proto_tls/doc/proto_tls_devel.xml000066400000000000000000000037241300170765700235610ustar00rootroot00000000000000 &develguide;
TLS_SERVER
SSL data per connection Each TLS connection, incoming or outgoing, creates an SSL * object, where configuration inherited from the SSL_CTX * and particular info on that socket are stored. This SSL * structure is kept in &osips; as long as the connection is alive, as part of the struct tcp_connection * object: ... struct tcp_connection *c; SSL *ssl; /*create somehow SSL object*/ c->extra_data = (void *) ssl; ssl = (SSL *) c->extra_data; ...
tls_print_errstack void tls_print_errstack(void); Dumps ssl error stack.
tls_tcpconn_init int tls_tcpconn_init( struct tcp_connection *c, int fd); Called when new tcp connection is accepted
tls_tcpconn_clean void tls_tcpconn_clean( struct tcp_connection *c); Shuts down the TLS connection.
tls_blocking_write size_t tls_blocking_write( struct tcp_connection *c, int fd, const char *buf, size_t len); Writes a memory chunk in blocking mode (syncron).
tls_read size_t tls_read( struct tcp_connection *c); Reads from a TLS connection. Return the number of bytes read.
tls_fix_read_conn void tls_tcpconn_clean( struct tcp_connection *c); Shuts down the TLS connection.
opensips-2.2.2/modules/proto_tls/doc/proto_tls_faq.xml000066400000000000000000000056271300170765700232350ustar00rootroot00000000000000 &faqguide; Where can I post a question about TLS? Use one (the most appropriate) of the &osips; mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; Remember: first at all, check if your question wasn't already answered. How can I report a bug? Accumulate as much as possible information (&osips; version, opensips -V output, your OS (uname -a), &osips; logs, network dumps, core dump files, configuration file) and send a mail to &osipsdevlink; Also you may try OpenSIPS's bug report web page: http://www.opensips.org/pmwiki.php?n=Development.Tracker How can I debug ssl/tls problems? Increase the log level in opensips.cfg (log_level=4) and watch the log statements in syslog. Install the ssldump utility and start it. This will give you a trace of the ssl/tls connections. What is the difference between the TLS directory and the TLSOPS module directory? The code in the TLS directory implements the TLS transport layer. The TLSOPS module implements TLS related functions which can be used in the routing script. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/proto_tls/proto_tls.c000066400000000000000000000277571300170765700212730ustar00rootroot00000000000000 /* * Copyright (C) 2015 OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-12 first version (bogdan) */ #include #include #include #include #include #include #include #include #include #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../sr_module.h" #include "../../net/api_proto.h" #include "../../net/api_proto_net.h" #include "../../net/net_tcp.h" #include "../../socket_info.h" #include "../../tsend.h" #include "../../timer.h" #include "../../receive.h" #include "../../pt.h" #include "../../parser/msg_parser.h" #include "../../pvar.h" #include "../../net/proto_tcp/tcp_common_defs.h" #include "../tls_mgm/api.h" #include "../tls_mgm/tls_conn_ops.h" #include "../tls_mgm/tls_conn_server.h" /* * Open questions: * * - what would happen when select exits, connection is passed * to reader to perform read, but another process would acquire * the same connection meanwhile, performs a write and finishes * accept/connect on behalf of the reader process, thus the * reader process would have nothing to read ? (resolved) * * - What happens if SSL_accept or SSL_connect gets called on * already established connection (c->S_CONN_OK) ? We could * save some locking provided that the functions do not screw * up the connection (in tcp_fix_read_conn we would not have * to lock before the switch). * * - tls_blocking_write needs fixing.. * * - we need to protect ctx by a lock -- it is in shared memory * and may be accessed simultaneously */ struct tls_mgm_binds tls_mgm_api; static int tls_port_no = SIPS_PORT; static int tls_max_msg_chunks = TCP_CHILD_MAX_MSG_CHUNK; /* 0: send CRLF pong to incoming CRLFCRLF ping */ static int tls_crlf_pingpong = 1; /* 0: do not drop single CRLF messages */ static int tls_crlf_drop = 0; static int mod_init(void); static void mod_destroy(void); static int proto_tls_init(struct proto_info *pi); static int proto_tls_init_listener(struct socket_info *si); static int proto_tls_send(struct socket_info* send_sock, char* buf, unsigned int len, union sockaddr_union* to, int id); static int w_tls_blocking_write(struct tcp_connection *c, int fd, const char *buf, size_t len) { return tls_blocking_write(c, fd, buf, len, &tls_mgm_api); } /* buffer to be used for reading all TCP SIP messages detached from the actual con - in order to improve paralelism ( process the SIP message while the con can be sent back to main to do more stuff */ static struct tcp_req tls_current_req; /* re-use similar and existing functions from the TCP-plain protocol */ #define _tcp_common_write w_tls_blocking_write #define _tcp_common_current_req tls_current_req #include "../../net/proto_tcp/tcp_common.h" static int tls_read_req(struct tcp_connection* con, int* bytes_read); static int proto_tls_conn_init(struct tcp_connection* c); static cmd_export_t cmds[] = { {"proto_init", (cmd_function)proto_tls_init, 0, 0, 0, 0}, {0,0,0,0,0,0} }; static param_export_t params[] = { { "tls_port", INT_PARAM, &tls_port_no }, { "tls_crlf_pingpong", INT_PARAM, &tls_crlf_pingpong }, { "tls_crlf_drop", INT_PARAM, &tls_crlf_drop }, { "tls_max_msg_chunks", INT_PARAM, &tls_max_msg_chunks }, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tls_mgm", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { PROTO_PREFIX "tls", /* module name*/ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ NULL, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ mod_destroy,/* destroy function */ 0, /* per-child init function */ }; static int mod_init(void) { LM_INFO("initializing TLS protocol\n"); if(load_tls_mgm_api(&tls_mgm_api) != 0){ LM_DBG("failed to find tls API - is tls_mgm module loaded?\n"); return -1; } return 0; } /* * called from main.c when opensips exits (main process) */ static void mod_destroy(void) { /* library destroy */ ERR_free_strings(); /*SSL_free_comp_methods(); - this function is not on std. openssl*/ EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); return; } static int proto_tls_init(struct proto_info *pi) { pi->id = PROTO_TLS; pi->name = "tls"; pi->default_port = tls_port_no; pi->tran.init_listener = proto_tls_init_listener; pi->tran.send = proto_tls_send; pi->tran.dst_attr = tcp_conn_fcntl; pi->net.flags = PROTO_NET_USE_TCP; pi->net.read = (proto_net_read_f)tls_read_req; pi->net.conn_init = proto_tls_conn_init; pi->net.conn_clean = tls_conn_clean; return 0; } static int proto_tls_init_listener(struct socket_info *si) { /* * reuse tcp initialization */ if (tcp_init_listener(si) < 0) { LM_ERR("failed to initialize TCP part\n"); goto error; } return 0; error: if (si->socket != -1) { close(si->socket); si->socket = -1; } return -1; } static int proto_tls_conn_init(struct tcp_connection* c) { return tls_conn_init(c, &tls_mgm_api); } static struct tcp_connection* tls_sync_connect(struct socket_info* send_sock, union sockaddr_union* server, int *fd) { int s; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (tcp_init_sock_opt(s)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(s, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } if (tcp_connect_blocking(s, &server->s, sockaddru_len(*server))<0){ LM_ERR("tcp_blocking_connect failed\n"); goto error; } con=tcp_conn_create(s, server, send_sock, S_CONN_OK); if (con==NULL){ LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } *fd = s; return con; /*FIXME: set sock idx! */ error: /* close the opened socket */ if (s!=-1) close(s); return 0; } static int proto_tls_send(struct socket_info* send_sock, char* buf, unsigned int len, union sockaddr_union* to, int id) { struct tcp_connection *c; struct ip_addr ip; int port; int fd, n; if (to){ su2ip_addr(&ip, to); port=su_getport(to); n = tcp_conn_get(id, &ip, port, PROTO_TLS, &c, &fd); }else if (id){ n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd); }else{ LM_CRIT("prot_tls_send called with null id & to\n"); return -1; } if (n<0) { /* error during conn get, return with error too */ LM_ERR("failed to acquire connection\n"); return -1; } /* was connection found ?? */ if (c==0) { if (tcp_no_new_conn) { return -1; } LM_DBG("no open tcp connection found, opening new one\n"); /* create tcp connection */ if ((c=tls_sync_connect(send_sock, to, &fd))==0) { LM_ERR("connect failed\n"); return -1; } goto send_it; } /* now we have a connection, let's what we can do with it */ /* BE CAREFUL now as we need to release the conn before exiting !!! */ if (fd==-1) { /* connection is not writable because of its state */ /* return error, nothing to do about it */ tcp_conn_release(c, 0); return -1; } send_it: LM_DBG("sending via fd %d...\n",fd); n = tls_blocking_write(c, fd, buf, len, &tls_mgm_api); tcp_conn_set_lifetime( c, tcp_con_lifetime); LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd); LM_DBG("buf=\n%.*s\n", (int)len, buf); if (n<0){ LM_ERR("failed to send\n"); c->state=S_CONN_BAD; if (c->proc_id != process_no) close(fd); tcp_conn_release(c, 0); return -1; } /* only close the FD if not already in the context of our process either we just connected, or main sent us the FD */ if (c->proc_id != process_no) close(fd); tcp_conn_release(c, 0); return n; } static int tls_read_req(struct tcp_connection* con, int* bytes_read) { int bytes; int total_bytes; struct tcp_req* req; bytes=-1; total_bytes=0; if (con->con_req) { req=con->con_req; LM_DBG("Using the per connection buff \n"); } else { LM_DBG("Using the global ( per process ) buff \n"); init_tcp_req(&tls_current_req, 0); req=&tls_current_req; } if (tls_fix_read_conn(con)!=0) { LM_ERR("failed to do pre-tls reading\n"); goto error; } if(con->state!=S_CONN_OK) goto done; /* not enough data */ again: if(req->error==TCP_REQ_OK){ /* if we still have some unparsed part, parse it first, * don't do the read*/ if (req->parsedpos){ bytes=0; }else{ bytes=tls_read(con,req); if (bytes<0) { LM_ERR("failed to read \n"); goto error; } } tcp_parse_headers(req, tls_crlf_pingpong, tls_crlf_drop); #ifdef EXTRA_DEBUG /* if timeout state=0; goto end__req; */ LM_DBG("read= %d bytes, parsed=%d, state=%d, error=%d\n", bytes, (int)(req->parsed-req->start), req->state, req->error ); LM_DBG("last char=0x%02X, parsed msg=\n%.*s\n", *(req->parsed-1), (int)(req->parsed-req->start), req->start); #endif total_bytes+=bytes; /* eof check: * is EOF if eof on fd and req. not complete yet, * if req. is complete we might have a second unparsed * request after it, so postpone release_with_eof */ if ((con->state==S_CONN_EOF) && (req->complete==0)) { LM_DBG("EOF received\n"); goto done; } } if (req->error!=TCP_REQ_OK){ LM_ERR("bad request, state=%d, error=%d " "buf:\n%.*s\nparsed:\n%.*s\n", req->state, req->error, (int)(req->pos-req->buf), req->buf, (int)(req->parsed-req->start), req->start); LM_DBG("- received from: port %d\n", con->rcv.src_port); print_ip("- received from: ip ",&con->rcv.src_ip, "\n"); goto error; } switch (tcp_handle_req(req, con, tls_max_msg_chunks) ) { case 1: goto again; case -1: goto error; } LM_DBG("tls_read_req end\n"); done: if (bytes_read) *bytes_read=total_bytes; /* connection will be released */ return 0; error: /* connection will be released as ERROR */ return -1; } opensips-2.2.2/modules/proto_ws/000077500000000000000000000000001300170765700167075ustar00rootroot00000000000000opensips-2.2.2/modules/proto_ws/Makefile000066400000000000000000000023651300170765700203550ustar00rootroot00000000000000# # Network WebSocket implementation # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=proto_ws.so LIBS= #ALLOCATOR_VER = 104 # ## set CROSS_COMPILE to true if you want to skip ## the autodetection ## CROSS_COMPILE=true #ifeq ($(CROSS_COMPILE),) #LIBWEBSOCKETS_BUILDER = $(shell \ # if pkg-config --exists libwebsockets; then \ # echo 'pkg-config libwebsockets'; \ # fi) #endif # #ifneq ($(LIBWEBSOCKETS_BUILDER),) # DEFS += $(shell $(LIBWEBSOCKETS_BUILDER) --cflags) # LIBS += $(shell $(LIBWEBSOCKETS_BUILDER) --libs) # LIBWEBSOCKETS_VER = $(shell $(LIBWEBSOCKETS_BUILDER) --modversion) #else # DEFS +=-I$(LOCALBASE)/include # LIBS +=-L$(LOCALBASE)/lib -lwebsockets # LIBWEBSOCKETS_VER = 1.3 #endif # #WEBSOCKETS_VER = $(shell echo $(LIBWEBSOCKETS_VER) | \ # sed "s/\.\([0-9]\)\./.0\1./g" | \ # sed "s/\.\([0-9]\)\$$/.0\1/g" | \ # tr -d "." | sed -e "s/^0*//" ) # #HAVE_ALLOCATOR = $(shell echo $(WEBSOCKETS_VER) $(ALLOCATOR_VER) | \ # awk '{ if (($$1 >= $$2)) print "-DHAVE_WEBSOCK_ALLOCATOR" }') # #ifeq ($(HAVE_ALLOCATOR),) #$(warning "Your libwebsockets library does not support custom allocators") #endif # #DEFS += $(HAVE_ALLOCATOR) include ../../Makefile.modules opensips-2.2.2/modules/proto_ws/README000066400000000000000000000101261300170765700175670ustar00rootroot00000000000000proto_ws Module OpenSIPS Project Edited by Razvan Crainea Copyright © 2015 OpenSIPS Project __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. ws_port (integer) 1.3.2. ws_send_timeout (integer) 1.3.3. ws_max_msg_chunks (integer) 2. Frequently Asked Questions List of Examples 1.1. Set ws_port parameter 1.2. Set ws_send_timeout parameter 1.3. Set ws_max_msg_chunks parameter Chapter 1. Admin Guide 1.1. Overview The WebSocket protocol (RFC 6455) provides an end-to-end full-duplex communication channel between two web-based applications. This allows WebSocket enabled browsers to connect to a WebSocket server and exchange any type of data. RFC 7118 provides the specifications for transporting SIP messages over the WebSocket protocol. The proto_ws module is transport module that provides communication over the WebSocket protocol. This module is fully compliant with the RFC 7118, thus allowing browsers to act as SIP clients for the OpenSIPS proxy. The current implementation acts both as WebSocket server and client, thus it can accept connections from WebSocket clients and can also initiate connections to another WebSocket server. After the connection is established, messages can flow in both directions. OpenSIPS supports the following WebSocket operations: * text and binary - can both send and receive WebSocket messages that contain text or binary body * close - messages used to safely close the WebSocket communication using a 2-messages handshake * ping - responds with pong messages. There is no mechanism to trigger ping messages. * pong - sent when a ping message is received. OpenSIPS, absorbes the pong messages received. Once loaded, you will be able to define WebSocket listeners in your script. To add a listener, you have to add its IP, and optionally the listening port, after the mpath parameter, similar to this example: ... mpath=/path/to/modules ... listen=ws:127.0.0.1 # change with the listening IP listen=ws:127.0.0.1:5060 # change with the listening IP and port ... 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * None. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. ws_port (integer) The default port to be used by all WebSocket listeners. Default value is 80. Example 1.1. Set ws_port parameter ... modparam("proto_ws", "ws_port", 8080) ... 1.3.2. ws_send_timeout (integer) Time in milliseconds after a WebSocket connection will be closed if it is not available for blocking writing in this interval (and OpenSIPS wants to send something on it). Default value is 100 ms. Example 1.2. Set ws_send_timeout parameter ... modparam("proto_ws", "ws_send_timeout", 200) ... 1.3.3. ws_max_msg_chunks (integer) The maximum number of chunks in which a SIP message is expected to arrive via WebSocket. If a received packet is more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending very fragmented traffic in order to decrease server performance). Default value is 4. Example 1.3. Set ws_max_msg_chunks parameter ... modparam("proto_ws", "ws_max_msg_chunks", 8) ... Chapter 2. Frequently Asked Questions 2.1. Can OpenSIPS act as a WebSocket client? Yes, starting with OpenSIPS 2.2, it can act as a WebSocket client. 2.2. Does OpenSIPS support WebSocket message fragmentation? No, WebSocket fragmentation mechanism is not supported. opensips-2.2.2/modules/proto_ws/doc/000077500000000000000000000000001300170765700174545ustar00rootroot00000000000000opensips-2.2.2/modules/proto_ws/doc/proto_ws.xml000066400000000000000000000016571300170765700220630ustar00rootroot00000000000000 %docentities; ]> proto_ws Module &osipsname; &osips; Project
support@opensips.org
Razvan Crainea
razvan@opensips.org
2015 &osips; Project
&admin; &faq;
opensips-2.2.2/modules/proto_ws/doc/proto_ws_admin.xml000066400000000000000000000112371300170765700232260ustar00rootroot00000000000000 &adminguide;
Overview The WebSocket protocol (RFC 6455) provides an end-to-end full-duplex communication channel between two web-based applications. This allows WebSocket enabled browsers to connect to a WebSocket server and exchange any type of data. RFC 7118 provides the specifications for transporting SIP messages over the WebSocket protocol. The proto_ws module is transport module that provides communication over the WebSocket protocol. This module is fully compliant with the RFC 7118, thus allowing browsers to act as SIP clients for the &osips; proxy. The current implementation acts both as WebSocket server and client, thus it can accept connections from WebSocket clients and can also initiate connections to another WebSocket server. After the connection is established, messages can flow in both directions. &osips; supports the following WebSocket operations: text and binary - can both send and receive WebSocket messages that contain text or binary body close - messages used to safely close the WebSocket communication using a 2-messages handshake ping - responds with pong messages. There is no mechanism to trigger ping messages. pong - sent when a ping message is received. &osips;, absorbes the pong messages received. Once loaded, you will be able to define WebSocket listeners in your script. To add a listener, you have to add its IP, and optionally the listening port, after the mpath parameter, similar to this example: ... mpath=/path/to/modules ... listen=ws:127.0.0.1 # change with the listening IP listen=ws:127.0.0.1:5060 # change with the listening IP and port ...
Dependencies
&osips; Modules The following modules must be loaded before this module: None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>ws_port</varname> (integer) The default port to be used by all WebSocket listeners. Default value is 80. Set <varname>ws_port</varname> parameter ... modparam("proto_ws", "ws_port", 8080) ...
<varname>ws_send_timeout</varname> (integer) Time in milliseconds after a WebSocket connection will be closed if it is not available for blocking writing in this interval (and &osips; wants to send something on it). Default value is 100 ms. Set <varname>ws_send_timeout</varname> parameter ... modparam("proto_ws", "ws_send_timeout", 200) ...
<varname>ws_max_msg_chunks</varname> (integer) The maximum number of chunks in which a SIP message is expected to arrive via WebSocket. If a received packet is more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending very fragmented traffic in order to decrease server performance). Default value is 4. Set <varname>ws_max_msg_chunks</varname> parameter ... modparam("proto_ws", "ws_max_msg_chunks", 8) ...
opensips-2.2.2/modules/proto_ws/doc/proto_ws_faq.xml000066400000000000000000000011031300170765700226740ustar00rootroot00000000000000 &faqguide; Can &osips; act as a WebSocket client? Yes, starting with &osips; 2.2, it can act as a WebSocket client. Does &osips; support WebSocket message fragmentation? No, WebSocket fragmentation mechanism is not supported. opensips-2.2.2/modules/proto_ws/proto_ws.c000066400000000000000000000235441300170765700207370ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-11 first version (razvanc) */ #include #include #include #include #include "../../pt.h" #include "../../sr_module.h" #include "../../net/net_tcp.h" #include "../../net/api_proto.h" #include "../../net/api_proto_net.h" #include "../../socket_info.h" #include "../../tsend.h" #include "../../receive.h" #include "../../timer.h" #include "proto_ws.h" #include "ws_tcp.h" #include "ws_common_defs.h" /* parameters*/ int ws_max_msg_chunks = TCP_CHILD_MAX_MSG_CHUNK; static struct tcp_req tcp_current_req; static struct ws_req ws_current_req; /* in milliseconds */ int ws_send_timeout = 100; /* in milliseconds */ int ws_hs_read_tout = 100; /* XXX: this information should be dynamically provided */ static str ws_resource = str_init("/"); #define _ws_common_module "ws" #define _ws_common_tcp_current_req tcp_current_req #define _ws_common_current_req ws_current_req #define _ws_common_max_msg_chunks ws_max_msg_chunks #define _ws_common_read ws_raw_read #define _ws_common_writev ws_raw_writev #define _ws_common_read_tout ws_hs_read_tout #define _ws_common_write_tout ws_send_timeout #define _ws_common_resource ws_resource #include "ws_handshake_common.h" #include "ws_common.h" static int mod_init(void); static int proto_ws_init(struct proto_info *pi); static int proto_ws_init_listener(struct socket_info *si); static int proto_ws_send(struct socket_info* send_sock, char* buf, unsigned int len, union sockaddr_union* to, int id); static int ws_read_req(struct tcp_connection* con, int* bytes_read); static int ws_conn_init(struct tcp_connection* c); static void ws_conn_clean(struct tcp_connection* c); static int ws_port = WS_DEFAULT_PORT; static cmd_export_t cmds[] = { {"proto_init", (cmd_function)proto_ws_init, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[] = { /* XXX: should we drop the ws prefix? */ { "ws_port", INT_PARAM, &ws_port }, { "ws_max_msg_chunks", INT_PARAM, &ws_max_msg_chunks }, { "ws_send_timeout", INT_PARAM, &ws_send_timeout }, { "ws_resource", STR_PARAM, &ws_resource }, { "ws_handshake_timeout", INT_PARAM, &ws_hs_read_tout }, {0, 0, 0} }; struct module_exports exports = { PROTO_PREFIX "ws", /* module name*/ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* per-child init function */ }; static int proto_ws_init(struct proto_info *pi) { pi->id = PROTO_WS; pi->name = "ws"; pi->default_port = ws_port; pi->tran.init_listener = proto_ws_init_listener; pi->tran.send = proto_ws_send; pi->tran.dst_attr = tcp_conn_fcntl; pi->net.flags = PROTO_NET_USE_TCP; pi->net.read = (proto_net_read_f)ws_read_req; pi->net.conn_init = ws_conn_init; pi->net.conn_clean = ws_conn_clean; return 0; } static int mod_init(void) { LM_INFO("initializing WebSocket protocol\n"); return 0; } static int ws_conn_init(struct tcp_connection* c) { struct ws_data *d; /* allocate the tcp_data and the array of chunks as a single mem chunk */ d = (struct ws_data *)shm_malloc(sizeof(*d)); if (d==NULL) { LM_ERR("failed to create ws states in shm mem\n"); return -1; } d->state = WS_CON_INIT; d->type = WS_NONE; d->code = WS_ERR_NONE; c->proto_data = (void*)d; return 0; } static void ws_conn_clean(struct tcp_connection* c) { struct ws_data *d = (struct ws_data*)c->proto_data; if (!d) return; if (c->state == S_CONN_OK) { switch (d->code) { case WS_ERR_NOSEND: break; case WS_ERR_NONE: WS_CODE(c) = WS_ERR_NORMAL; default: ws_close(c); break; } } shm_free(d); c->proto_data = NULL; } static int proto_ws_init_listener(struct socket_info *si) { /* we do not do anything particular to TCP plain here, so * transparently use the generic listener init from net TCP layer */ return tcp_init_listener(si); } static struct tcp_connection* ws_sync_connect(struct socket_info* send_sock, union sockaddr_union* server) { int s; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (tcp_init_sock_opt(s)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(s, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } if (tcp_connect_blocking(s, &server->s, sockaddru_len(*server))<0){ LM_ERR("tcp_blocking_connect failed\n"); goto error; } con=tcp_conn_new(s, server, send_sock, S_CONN_OK); if (con==NULL){ LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } /* it is safe to move this here and clear it after we complete the * handshake, just before sending the fd to main */ con->fd = s; return con; error: /* close the opened socket */ if (s!=-1) close(s); return 0; } static struct tcp_connection* ws_connect(struct socket_info* send_sock, union sockaddr_union* to, int *fd) { struct tcp_connection *c; if ((c=ws_sync_connect(send_sock, to))==0) { LM_ERR("connect failed\n"); return NULL; } /* the state of the connection should be NONE, otherwise something is * wrong */ if (WS_TYPE(c) != WS_NONE) { LM_BUG("invalid type for connection %d\n", WS_TYPE(c)); goto error; } WS_TYPE(c) = WS_CLIENT; if (ws_client_handshake(c) < 0) { LM_ERR("cannot complete WebSocket handshake\n"); goto error; } *fd = c->fd; /* clear the fd, just in case */ c->fd = -1; /* handshake done - send the socket to main */ if (tcp_conn_send(c) < 0) { LM_ERR("cannot send socket to main\n"); goto error; } return c; error: tcp_conn_destroy(c); return NULL; } /************** WRITE related functions ***************/ /*! \brief Finds a tcpconn & sends on it */ static int proto_ws_send(struct socket_info* send_sock, char* buf, unsigned int len, union sockaddr_union* to, int id) { struct tcp_connection *c; struct timeval get; struct ip_addr ip; int port = 0; int fd, n; reset_tcp_vars(tcpthreshold); start_expire_timer(get,tcpthreshold); if (to){ su2ip_addr(&ip, to); port=su_getport(to); n = tcp_conn_get(id, &ip, port, PROTO_WS, &c, &fd); }else if (id){ n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd); }else{ LM_CRIT("prot_tls_send called with null id & to\n"); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } if (n<0) { /* error during conn get, return with error too */ LM_ERR("failed to acquire connection\n"); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } /* was connection found ?? */ if (c==0) { if (tcp_no_new_conn) { return -1; } LM_DBG("no open tcp connection found, opening new one\n"); /* create tcp connection */ if ((c=ws_connect(send_sock, to, &fd))==0) { LM_ERR("connect failed\n"); return -1; } goto send_it; } get_time_difference(get, tcpthreshold, tcp_timeout_con_get); /* now we have a connection, let's what we can do with it */ /* BE CAREFUL now as we need to release the conn before exiting !!! */ if (fd==-1) { /* connection is not writable because of its state */ /* return error, nothing to do about it */ tcp_conn_release(c, 0); return -1; } send_it: LM_DBG("sending via fd %d...\n",fd); n = ws_req_write(c, fd, buf, len); stop_expire_timer(get, tcpthreshold, "WS ops",buf,(int)len,1); tcp_conn_set_lifetime( c, tcp_con_lifetime); LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd); if (n<0){ LM_ERR("failed to send\n"); c->state=S_CONN_BAD; if (c->proc_id != process_no) close(fd); tcp_conn_release(c, 0); return -1; } /* only close the FD if not already in the context of our process either we just connected, or main sent us the FD */ if (c->proc_id != process_no) close(fd); tcp_conn_release(c, 0); return n; } /************** READ related functions ***************/ /* Responsible for reading the request * * if returns >= 0 : the connection will be released * * if returns < 0 : the connection will be released as BAD / broken */ static int ws_read_req(struct tcp_connection* con, int* bytes_read) { int size; if (WS_STATE(con) != WS_CON_HANDSHAKE_DONE) { size = ws_server_handshake(con); if (size < 0) { LM_ERR("cannot complete WebSocket handshake\n"); goto error; } if (size == 0) goto done; } if (WS_STATE(con) == WS_CON_HANDSHAKE_DONE && ws_process(con) < 0) goto error; done: return 0; error: /* connection will be released as ERROR */ return -1; } opensips-2.2.2/modules/proto_ws/proto_ws.h000066400000000000000000000042201300170765700207320ustar00rootroot00000000000000/* * Copyright (C) 2015 OpenSIPS Project * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2015-02-xx created (razvanc) */ #ifndef _PROTO_WS_H_ #define _PROTO_WS_H_ #define WS_SUPPORTED_VERSION 13 /*!< WebSocket supported version */ #define WS_DEFAULT_PORT 80 /*!< WebSocket default port */ enum ws_conn_state { WS_CON_INIT, WS_CON_HANDSHAKE, WS_CON_HANDSHAKE_DONE, WS_CON_BAD_REQ }; enum ws_conn_type { WS_NONE, WS_CLIENT, WS_SERVER }; enum ws_close_code { WS_ERR_NONE = 0, WS_ERR_NORMAL = 1000, WS_ERR_CLIENT = 1001, WS_ERR_PROTO = 1002, WS_ERR_INVALID = 1003, WS_ERR_BADDATA = 1007, WS_ERR_POLICY = 1008, WS_ERR_TOO_BIG = 1009, WS_ERR_BADEXT = 1010, WS_ERR_UNEXPECT = 1011, WS_ERR_NOSEND = 10000 }; /* * For now we only need the state stored in the connection * Later, we should probably store info about origin, resoruce. versions, * protocols supported, etc. - razvanc */ struct ws_data { /* the state of the connection */ enum ws_conn_state state; /* the type of the connection */ enum ws_conn_type type; /* close code */ enum ws_close_code code; /* WebSocket Handshake key */ str key; }; #define WS_STATE(_c) \ (((struct ws_data *)(_c)->proto_data)->state) #define WS_TYPE(_c) \ (((struct ws_data *)(_c)->proto_data)->type) #define WS_CODE(_c) \ (((struct ws_data *)(_c)->proto_data)->code) #define WS_KEY(_c) \ (((struct ws_data *)(_c)->proto_data)->key) #endif /* _PROTO_WS_H_ */ opensips-2.2.2/modules/proto_ws/ws_common.h000066400000000000000000000435401300170765700210670ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-xx first version (razvanc) */ #ifndef _WS_COMMON_H_ #define _WS_COMMON_H_ #include "../../mem/shm_mem.h" #include "../../globals.h" #include "../../receive.h" #include "../../dprint.h" #include "../../tsend.h" #include "../../timer.h" #include "../../ut.h" #include "../../pt.h" #include "proto_ws.h" #include "ws_tcp.h" #include "ws_common_defs.h" /* * WebSocket frame * 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+ */ #define WS_EXT_LEN 126 #define WS_EXTC_LEN 127 #define WS_OP_CONT 0x0 #define WS_OP_TEXT 0x1 #define WS_OP_BIN 0x2 #define WS_OP_CLOSE 0x8 #define WS_OP_PING 0x9 #define WS_OP_PONG 0xA #define WS_BIT_FIN 0x80 #define WS_BIT_MASK 0x80 #define WS_MASK_SLEN 0x7F #define WS_MASK_OPCODE 0x0F /* Minimum size of a header - not masked, nor extended len */ #define WS_MIN_HDR_LEN (sizeof(uint16_t)) /* Size of extended len */ #define WS_ELEN_SIZE (sizeof(uint16_t)) /* Size of extended complex len */ #define WS_ELENC_SIZE (sizeof(uint64_t)) /* Size of mask */ #define WS_MASK_SIZE (sizeof(uint32_t)) /* Size of an extened header - both mask and extended len */ #define WS_MAX_HDR_LEN (WS_MIN_HDR_LEN + WS_ELENC_SIZE + WS_MASK_SIZE) /* Maximum size of an extended header */ #define WS_MAX_ELEN ((uint16_t)(-1)) /* Returns the TCP buffer */ #define WS_BUF(_r) ((uint8_t *)(_r)->tcp.buf) #define WS_BODY(_r) ((uint8_t *)(_r)->tcp.body) /* Size of a simple, not exteneded message */ #define WS_SLEN(_r) (WS_BUF(_r)[1] & WS_MASK_SLEN) /* Size of an extended message */ #define WS_ELEN(_r) \ (((uint16_t)(WS_BUF(_r)[2])) << 8 |\ ((uint16_t)(WS_BUF(_r)[3])) << 0) /* Size of an 64 extended message */ #define WS_ELENC(_r) \ (((uint64_t)(WS_BUF(_r)[2])) << 56 |\ ((uint64_t)(WS_BUF(_r)[3])) << 48 |\ ((uint64_t)(WS_BUF(_r)[4])) << 40 |\ ((uint64_t)(WS_BUF(_r)[5])) << 32 |\ ((uint64_t)(WS_BUF(_r)[6])) << 24 |\ ((uint64_t)(WS_BUF(_r)[7])) << 16 |\ ((uint64_t)(WS_BUF(_r)[8])) << 8 |\ ((uint64_t)(WS_BUF(_r)[9])) << 0) /* returns the close code */ #define WS_CLOSE_CODE(_r)\ (((uint16_t)(WS_BODY(_r)[0])) << 8 |\ ((uint16_t)(WS_BODY(_r)[1])) << 0) #define WS_USE_ELEN(_r) (WS_SLEN(_r) == WS_EXT_LEN) #define WS_USE_ELENC(_r) (WS_SLEN(_r) == WS_EXTC_LEN) #define WS_IS_MASKED(_r) (WS_BUF(_r)[1] & WS_BIT_MASK) #define WS_IS_FIN(_r) (WS_BUF(_r)[0] & WS_BIT_FIN) #define WS_OPCODE(_r) (WS_BUF(_r)[0] & WS_MASK_OPCODE) #define WS_MASK(_r) (*((unsigned int *)(WS_BODY(_r)) - 1)) /* Returns the size of the mask, if needed */ #define WS_IF_MASK_SIZE(_r) (WS_IS_MASKED(_r) ? WS_MASK_SIZE : 0) #define ROTATE32(_k) ((((_k) & 0xFF) << 24) | ((_k) >> 8)) #define MASK8(_k) ((unsigned char)((_k) & 0xFF)) #ifndef _ws_common_current_req #error "_ws_common_current_req not defined!" #endif #ifndef _ws_common_writev #error "_ws_common_writev not defined!" #endif #ifndef _ws_common_write_tout #error "_ws_common_write_tout not defined!" #endif static inline void ws_print_masked(char *buf, int len) { static char *print_buf; static long print_buf_len = 0; int current_len; char *p = print_buf; int i, j; for (i = j = 0; i < len; i++) { if (p - print_buf + print_buf_len < 1024) { print_buf_len += 1024; current_len = p - print_buf; print_buf = pkg_realloc(print_buf, print_buf_len); if (!print_buf) return; p = print_buf + current_len; } if (buf[i] < 32) p += sprintf(p, "."); else p += sprintf(p, "%c", buf[i]); if ((i + 1) % 32 == 0) { p += sprintf(p, " | "); for (; j <= i; j++) p += sprintf(p, "%02X%s", buf[j], j % 2 == 0 ? "" : " "); j = i + 1; p += sprintf(p, "\n"); } } LM_INFO("Print buffer\n%s", print_buf); } static inline void ws_mask(char *buf, int len, unsigned int mask) { char *p = buf; char *end = buf + len; /* xor first bits, until aligned */ for (; p < end && (((unsigned long)p) % sizeof(unsigned long *)); p++, mask = ROTATE32(mask)) *p ^= MASK8(mask); /* xor the big chunk, which is aligned */ for (; p < end - (sizeof(int) - 1); p += sizeof(int)) *((int *)p) ^= mask; /* the last chunk may not be processed */ for (; p < end; p++, mask >>= 8) *p ^= MASK8(mask); //ws_print_masked(buf, len); } static inline int ws_send(struct tcp_connection *con, int fd, int op, char *body, unsigned int len) { /* * we need this buffer to mask the message sent to the client * since we cannot modify the buffer - it might be readonly */ static char *body_buf = 0; static unsigned char hdr_buf[WS_MAX_HDR_LEN]; static struct iovec v[2] = { {hdr_buf, 0}, {0, 0}}; unsigned int mask = rand(); /* FIN + OPCODE */ hdr_buf[0] = WS_BIT_FIN | (op & WS_MASK_OPCODE); if (len == 0) { hdr_buf[1] = 0; /* don't have any data, send only the heeader */ v[0].iov_len = WS_MIN_HDR_LEN; return _ws_common_writev(con, fd, v, 1, _ws_common_write_tout); } else if (len < WS_EXT_LEN) { hdr_buf[1] = len; v[0].iov_len = WS_MIN_HDR_LEN; } else if (len < WS_MAX_ELEN) { v[0].iov_len = WS_MIN_HDR_LEN + WS_ELEN_SIZE; hdr_buf[1] = WS_EXT_LEN; *(uint16_t *)(hdr_buf + WS_MIN_HDR_LEN) = htons(len); } else { v[0].iov_len = WS_MIN_HDR_LEN + WS_ELENC_SIZE; hdr_buf[1] = WS_EXTC_LEN; /* len can't be larger than 32 bits long */ *(uint64_t *)(hdr_buf + WS_MIN_HDR_LEN) = htonl(len); } if (WS_TYPE(con) == WS_CLIENT) { /* set the mask in the message */ *(uint32_t *)(v[0].iov_base + v[0].iov_len) = mask; v[0].iov_len += WS_MASK_SIZE; /* also indicate that the message is masked */ hdr_buf[1] |= WS_BIT_MASK; body_buf = body_buf ? pkg_realloc(body_buf, len) : pkg_malloc(len); if (!body_buf) { LM_ERR("oom for body buffer\n"); return -1; } memcpy(body_buf, body, len); ws_mask(body_buf, len, mask); v[1].iov_base = body_buf; } else { v[1].iov_base = body; } v[1].iov_len = len; return _ws_common_writev(con, fd, v, 2, _ws_common_write_tout); } static inline int ws_send_pong(struct tcp_connection *con, struct ws_req *req) { return ws_send(con, con->fd, WS_OP_PONG, req->tcp.body, req->tcp.content_len); } static inline int ws_send_close(struct tcp_connection *con) { uint16_t code; int len; char *buf; if (WS_CODE(con)) { code = htons(WS_CODE(con)); len = sizeof(uint16_t); } else { len = 0; } buf = (char *)&code; return ws_send(con, con->fd, WS_OP_CLOSE, buf, len); } /* Public functions down here */ static int ws_req_write(struct tcp_connection *con, int fd, char *buf, int len) { return ws_send(con, fd, WS_OP_TEXT, buf, len); } static enum ws_close_code inline ws_parse(struct ws_req *req) { uint64_t clen; /* when the header is parsed, parse is moved at the end of the header */ if (!req->tcp.body) { /* check if we have the minimal header */ if (req->tcp.pos - req->tcp.buf < WS_MIN_HDR_LEN) /* wait for more data to come */ goto update_parsed; if (!WS_IS_FIN(req)) { LM_ERR("We do not support fragmemntation yet. Dropping...\n"); req->tcp.error = TCP_READ_ERROR; return WS_ERR_POLICY; } /* check if it is an operation that we support */ req->op = WS_OPCODE(req); switch (req->op) { case WS_OP_TEXT: case WS_OP_BIN: case WS_OP_CLOSE: case WS_OP_PING: case WS_OP_PONG: /* continue to read whole packet */ break; default: LM_ERR("Unsupported WebSocket opcode: %d\n", req->op); return WS_ERR_INVALID; } /* if it has extended lenght, drop it because we can't read it all */ if (WS_USE_ELENC(req)) { /* extended case */ if (req->tcp.pos - req->tcp.buf < WS_MIN_HDR_LEN + WS_ELENC_SIZE + WS_IF_MASK_SIZE(req)) return 0; clen = WS_ELENC(req); if ((clen+WS_MIN_HDR_LEN+WS_ELENC_SIZE+WS_IF_MASK_SIZE(req))> TCP_BUF_SIZE) { LM_ERR("packet too large, can't fit: %" PRIu64 "\n", clen); req->tcp.error = TCP_REQ_OVERRUN; return WS_ERR_TOO_BIG; } req->tcp.content_len = clen; /* body of the packet */ req->tcp.body = (char *)req->tcp.buf + WS_MIN_HDR_LEN + WS_ELENC_SIZE; } else if (WS_USE_ELEN(req)) { /* extended case */ if (req->tcp.pos - req->tcp.buf < WS_MIN_HDR_LEN + WS_ELEN_SIZE + WS_IF_MASK_SIZE(req)) return 0; req->tcp.content_len = WS_ELEN(req); if ((req->tcp.content_len+WS_MIN_HDR_LEN+WS_ELEN_SIZE+WS_IF_MASK_SIZE(req))> TCP_BUF_SIZE) { LM_ERR("packet too large, can't fit: %u\n", req->tcp.content_len); req->tcp.error = TCP_REQ_OVERRUN; return WS_ERR_TOO_BIG; } /* body of the packet */ req->tcp.body = (char *)req->tcp.buf + WS_MIN_HDR_LEN + WS_ELEN_SIZE; } else { /* we should have no problems here, the buffer should be large enough */ req->tcp.content_len = WS_SLEN(req); req->tcp.body = (char *)req->tcp.buf + WS_MIN_HDR_LEN; } if (WS_IS_MASKED(req)) { req->tcp.body += WS_MASK_SIZE; req->mask = WS_MASK(req); req->is_masked = 1; } else { req->is_masked = 0; } } /* do we have the entire packet? */ if (req->tcp.pos - req->tcp.body >= req->tcp.content_len) { /* * decode only if we have something interesting out there * even if we have a mask but it is 0, XOR doesn't do anything */ if (req->mask && req->tcp.content_len) ws_mask(req->tcp.body, req->tcp.content_len, req->mask); req->tcp.complete = 1; req->tcp.parsed = req->tcp.body + req->tcp.content_len; } else { update_parsed: req->tcp.parsed = req->tcp.pos; } return 0; } #define init_ws_req(_req, _size) \ do { \ init_tcp_req(&(_req)->tcp, _size); \ (_req)->op = WS_OP_CONT; \ (_req)->mask = 0; \ (_req)->is_masked = 0; \ } while(0) static int ws_process(struct tcp_connection *con) { struct ws_req *req; struct ws_req *newreq; long size = 0; enum ws_close_code ret_code = WS_ERR_NONE; unsigned char bk; char *msg_buf; int msg_len; struct receive_info local_rcv; if (con->con_req) { req=(struct ws_req *)con->con_req; LM_DBG("Using the per connection buff \n"); } else { LM_DBG("Using the global ( per process ) buff \n"); init_ws_req(&_ws_common_current_req, 0); req=&_ws_common_current_req; } again: if (req->tcp.error == TCP_REQ_OK) { if (req->tcp.parsed >= req->tcp.pos) { if (_ws_common_read(con, &req->tcp) < 0) { LM_ERR("failed to read %d:%s\n", errno, strerror(errno)); goto error; } } ret_code = ws_parse(req); if (ret_code) goto error; /* eof check: * is EOF if eof on fd and r. not complete yet, * if r. is complete we might have a second unparsed * request after it, so postpone release_with_eof */ if ((con->state==S_CONN_EOF) && (req->tcp.complete==0)) { LM_DBG("EOF received\n"); goto done; } } if (req->tcp.complete) { /* sanity mask checks */ if ((WS_TYPE(con) == WS_CLIENT && req->is_masked) || (WS_TYPE(con) == WS_SERVER && !req->is_masked)) { LM_DBG("malformed WS msg - %s %s\n", req->is_masked ? "masked" : "not masked", WS_TYPE(con) == WS_CLIENT ? "client" : "server"); ret_code = WS_ERR_BADDATA; goto error; } /* update the timeout - we successfully read the request */ tcp_conn_set_lifetime(con, _ws_common_write_tout); con->timeout=con->lifetime; /* if we are here everything is nice and ok*/ update_stat( pt[process_no].load, +1 ); /* rcv.bind_address should always be !=0 */ bind_address=con->rcv.bind_address; con->rcv.proto_reserved1=con->id; /* copy the id */ size=req->tcp.pos-req->tcp.parsed; switch (req->op) { case WS_OP_CLOSE: if (req->tcp.content_len) { /* for now we are only interested in the code, not the reason */ ret_code = WS_CLOSE_CODE(req); switch(ret_code) { case WS_ERR_NORMAL: LM_DBG("Normal WebSocket close\n"); break; case WS_ERR_CLIENT: LM_DBG("Client error close\n"); break; case WS_ERR_PROTO: LM_DBG("WebSocket protocol error\n"); break; case WS_ERR_BADDATA: LM_DBG("Data type not consistent\n"); break; case WS_ERR_POLICY: LM_DBG("Bad policy close\n"); break; case WS_ERR_TOO_BIG: LM_DBG("Packet too big close\n"); break; case WS_ERR_BADEXT: LM_DBG("Bad extension close\n"); break; case WS_ERR_UNEXPECT: LM_DBG("Unexpected condition close\n"); break; default: LM_DBG("Unknown WebSocket close: %d\n", ret_code); } } else { ret_code = WS_ERR_NORMAL; } /* respond to close */ WS_CODE(con) = ret_code; ws_send_close(con); WS_CODE(con) = WS_ERR_NOSEND; /* release the connextion */ con->state = S_CONN_EOF; goto done; case WS_OP_PING: if (ws_send_pong(con, req) < 0) LM_ERR("cannot send PONG msg\n"); break; case WS_OP_PONG: LM_DBG("Received WebSocket PONG\n"); break; case WS_OP_TEXT: case WS_OP_BIN: bk = *req->tcp.parsed; *req->tcp.parsed = 0; msg_buf = req->tcp.body; msg_len = req->tcp.parsed-req->tcp.body; local_rcv = con->rcv; if (!size) { /* did not read any more things - we can release * the connection */ LM_DBG("We're releasing the connection in state %d \n", con->state); if (req != &_ws_common_current_req) { /* we have the buffer in the connection tied buff - * detach it , release the conn and free it afterwards */ con->con_req = NULL; } /* TODO - we could indicate to the TCP net layer to release * the connection -> other worker may read the next available * message on the pipe */ } else { LM_DBG("We still have things on the pipe - " "keeping connection \n"); } if (receive_msg(msg_buf, msg_len, &local_rcv, NULL) <0) LM_ERR("receive_msg failed \n"); *req->tcp.parsed = bk; break; default: LM_BUG("Can't handle %d\n", req->op); goto error; } update_stat( pt[process_no].load, -1 ); if (size) memmove(req->tcp.buf, req->tcp.parsed, size); #ifdef EXTRA_DEBUG LM_DBG("preparing for new request, kept %ld bytes\n", size); #endif init_ws_req(req, size); con->msg_attempts = 0; /* if we still have some unparsed bytes, try to parse them too*/ if (size) goto again; /* cleanup the existing request */ if (req != &_ws_common_current_req) { /* make sure we cleanup the request in the connection */ con->con_req = NULL; pkg_free(req); } } else { /* request not complete - check the if the thresholds are exceeded */ con->msg_attempts++; if (con->msg_attempts == _ws_common_max_msg_chunks) { LM_ERR("Made %u read attempts but message is not complete yet - " "closing connection \n",con->msg_attempts); goto error; } if (req == &_ws_common_current_req) { /* let's duplicate this - most likely another conn will come in */ LM_DBG("We didn't manage to read a full request\n"); newreq = pkg_malloc(sizeof(struct ws_req)); if (newreq == NULL) { LM_ERR("No more mem for dynamic con request buffer\n"); goto error; } if (req->tcp.pos != req->tcp.buf) { /* we have read some bytes */ memcpy(newreq->tcp.buf,req->tcp.buf,req->tcp.pos-req->tcp.buf); newreq->tcp.pos = newreq->tcp.buf + (req->tcp.pos-req->tcp.buf); } else { newreq->tcp.pos = newreq->tcp.buf; } if (req->tcp.start != req->tcp.buf) newreq->tcp.start = newreq->tcp.buf +(req->tcp.start-req->tcp.buf); else newreq->tcp.start = newreq->tcp.buf; if (req->tcp.parsed != req->tcp.buf) newreq->tcp.parsed =newreq->tcp.buf+(req->tcp.parsed-req->tcp.buf); else newreq->tcp.parsed = newreq->tcp.buf; if (req->tcp.body != 0) { newreq->tcp.body = newreq->tcp.buf + (req->tcp.body-req->tcp.buf); } else newreq->tcp.body = 0; newreq->tcp.complete=req->tcp.complete; newreq->tcp.has_content_len=req->tcp.has_content_len; newreq->tcp.content_len=req->tcp.content_len; newreq->tcp.bytes_to_go=req->tcp.bytes_to_go; newreq->tcp.error = req->tcp.error; newreq->tcp.state = req->tcp.state; newreq->op = req->op; newreq->mask = req->mask; newreq->is_masked = req->is_masked; con->con_req = (struct tcp_req *)newreq; } } LM_DBG("ws_read end\n"); done: /* connection will be released */ return size; error: WS_CODE(con) = ret_code; if (WS_CODE(con) != WS_ERR_NONE) { ws_send_close(con); WS_CODE(con) = WS_ERR_NOSEND; } return -1; } static void ws_close(struct tcp_connection *c) { ws_send_close(c); } #endif /* _WS_COMMON_H_ */ opensips-2.2.2/modules/proto_ws/ws_common_defs.h000066400000000000000000000022101300170765700220550ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-xx first version (razvanc) */ #ifndef _WS_COMMON_DEFS_H_ #define _WS_COMMON_DEFS_H_ #include "../../net/net_tcp.h" /* wrapper around tcp request to add ws info */ struct ws_req { struct tcp_req tcp; unsigned int op; unsigned int mask; unsigned int is_masked; }; #endif /* _WS_COMMON_DEFS_H_ */ opensips-2.2.2/modules/proto_ws/ws_handshake_common.h000066400000000000000000001222401300170765700230700ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-xx first version (razvanc) */ #ifndef _WS_HANDSHAKE_H_ #define _WS_HANDSHAKE_H_ #define HTTP_SEP "\r\n" #define HTTP_SEP_LEN (sizeof(HTTP_SEP) - 1) #define HTTP_END HTTP_SEP HTTP_SEP #define HTTP_END_LEN (sizeof(HTTP_END) - 1) #define HTTP_GET_METHOD "GET" #define HTTP_GET_METHOD_LEN (sizeof(HTTP_GET_METHOD) - 1) #define HTTP_VER_TOKEN "HTTP/" #define HTTP_VER_TOKEN_LEN (sizeof(HTTP_VER_TOKEN) - 1) #define HTTP_VER_MAJ 1 #define HTTP_VER_MIN 1 #define HTTP_VERSION HTTP_VER_TOKEN "1.1" #define HTTP_VERSION_LEN (sizeof(HTTP_VERSION) - 1) #define HTTP_REPLY_CODE "101" #define HTTP_REPLY_CODE_LEN (sizeof(HTTP_REPLY_CODE) - 1) #define HTTP_REPLY_REASON1 "Switching" #define HTTP_REPLY_REASON1_LEN (sizeof(HTTP_REPLY_REASON1) - 1) #define HTTP_REPLY_REASON2 "Protocols" #define HTTP_REPLY_REASON2_LEN (sizeof(HTTP_REPLY_REASON2) - 1) #define WS_HOST_F (1 << 0) #define WS_UPGRADE_F (1 << 1) #define WS_CONN_F (1 << 2) #define WS_ORIGIN_F (1 << 4) #define WS_KEY_F (1 << 3) #define WS_VER_F (1 << 5) /* for SIP connections, RFC7118 requires sip protocol */ #define WS_PROTO_F (1 << 6) #define WS_ACCEPT_F (1 << 7) #define HDR_LEN(_s) (sizeof(_s) - 1) #define WS_HDR "websocket" #define WS_HDR_LEN (sizeof(WS_HDR) - 1) #define WS_PROTO_SIP "sip" #define WS_PROTO_SIP_LEN (sizeof(WS_PROTO_SIP) - 1) #define WS_UPGRADE_HDR "Upgrade" #define WS_UPGRADE_HDR_LEN (sizeof(WS_UPGRADE_HDR) - 1) /* all flags for req */ #define WS_ALL_REQ_F (WS_HOST_F | \ WS_UPGRADE_F | \ WS_CONN_F | \ WS_ORIGIN_F | \ WS_KEY_F | \ WS_VER_F | \ WS_PROTO_F) /* all flags for reply */ #define WS_ALL_RPL_F (WS_UPGRADE_F | \ WS_CONN_F | \ WS_ACCEPT_F | \ WS_PROTO_F) #define GET_LOWER(_p) \ ((*(_p)) | 0x20) #define GET_LOWER_DWORD(_p) \ ((*(_p) + (*((_p)+1)<<8) + (*((_p)+2)<<16) + (*((_p)+3)<<24)) | 0x20202020) #define GET_DWORD(_c0, _c1, _c2, _c3) \ ((_c0) + ((_c1)<<8) + ((_c2)<<16) + ((_c3)<<24)) #define WS_SHA1_KEY_LEN 20 #define WS_ACCEPT_KEY_LEN 28 /* 20-bytes string BASE64 encoded */ #define WS_GUID_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define WS_GUID_KEY_LEN (sizeof(WS_GUID_KEY) - 1) #define WS_HTTP_ACCEPT \ HTTP_VERSION " " HTTP_REPLY_CODE " Switching Protocols" HTTP_SEP \ "Upgrade: websocket" HTTP_SEP \ "Connection: Upgrade" HTTP_SEP \ "Sec-WebSocket-Protocol: sip" HTTP_SEP \ "Sec-WebSocket-Accept: " #define WS_HTTP_ACCEPT_LEN (sizeof(WS_HTTP_ACCEPT) - 1) #define WS_HTTP_BAD_REQ \ "HTTP/1.1 400 Bad Request" HTTP_SEP \ "Sec-WebSocket-Version: 13" HTTP_END #define WS_HTTP_BAD_REQ_LEN (sizeof(WS_HTTP_BAD_REQ) - 1) /* TODO: protocol should be dynamic */ #define HTTP_HANDSHAKE_END \ "Upgrade: websocket" HTTP_SEP \ "Connection: upgrade" HTTP_SEP \ "Sec-WebSocket-Version: 13" HTTP_SEP \ "Sec-WebSocket-Protocol: " WS_PROTO_SIP HTTP_END #define HTTP_HANDSHAKE_END_LEN (sizeof(HTTP_HANDSHAKE_END) - 1) #define MAX_HOST_LEN IP_ADDR_MAX_STR_SIZE /*IP*/ + 1 /*':'*/ + 5 /*65535*/ #include "../../sha1.h" static int ws_read_http(struct tcp_connection *c, struct tcp_req *r); static int ws_parse_req_handshake(struct tcp_connection *c, char *msg, int len); static int ws_parse_rpl_handshake(struct tcp_connection *c, char *msg, int len); static int ws_complete_handshake(struct tcp_connection *c); static int ws_start_handshake(struct tcp_connection *c); static int ws_bad_handshake(struct tcp_connection *c); /* safety checks */ #ifndef _ws_common_module #error "_ws_common_module not defined!" #endif #ifndef _ws_common_tcp_current_req #error "_ws_common_tcp_current_req not defined!" #endif #ifndef _ws_common_max_msg_chunks #error "_ws_common_max_msg_chunks not defined!" #endif #ifndef _ws_common_read #error "_ws_common_read not defined!" #endif #ifndef _ws_common_writev #error "_ws_common_writev not defined!" #endif #ifndef _ws_common_read_tout #error "_ws_common_read_tout not defined!" #endif #ifndef _ws_common_write_tout #error "_ws_common_write_tout not defined!" #endif #define WS_KEY_LEN 24 static char ws_key[WS_KEY_LEN]; static const char base64alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; #define BASE64ALPHABET_LEN (sizeof base64alphabet) /* we're using a completely random key - no reason yet for something else */ static str ws_rand_key(void) { static str key = { ws_key, WS_KEY_LEN }; int i; for (i = 0; i < WS_KEY_LEN; i++) ws_key[i] = base64alphabet[rand() % BASE64ALPHABET_LEN]; return key; } static inline int ws_client_handshake(struct tcp_connection *con) { int bytes; long size = 0; int msg_len; char *msg_buf; struct tcp_req *req; int to; int elapsed; struct timeval begin; unsigned int err_len, poll_err = 0, err; int n; #if defined(HAVE_SELECT) && defined(BLOCKING_USE_SELECT) fd_set sel_set; fd_set orig_set; struct timeval timeout; #else struct pollfd pf; #endif WS_STATE(con) = WS_CON_HANDSHAKE; WS_KEY(con) = ws_rand_key(); if (ws_start_handshake(con) < 0) { LM_ERR("cannot start handshake\n"); return -1; } /* there should be no req in the con */ if (con->con_req) { LM_BUG("there should not be any con req!\n"); goto error; } init_tcp_req(&_ws_common_tcp_current_req, 0); req=&_ws_common_tcp_current_req; to = _ws_common_read_tout*1000; if (gettimeofday(&(begin), NULL)) { LM_ERR("Failed to get TCP connect start time\n"); goto error; } #if defined(HAVE_SELECT) && defined(BLOCKING_USE_SELECT) FD_ZERO(&orig_set); FD_SET(con->fd, &orig_set); #else pf.fd=con->fd; pf.events=POLLIN; #endif do { elapsed = get_time_diff(&begin); if (elapsedfd+1, 0, &sel_set, 0, &timeout); #else n=poll(&pf, 1, to/1000); #endif if (n<0){ if (errno==EINTR) continue; goto error; }else if (n==0) /* timeout */ continue; #if defined(HAVE_SELECT) && defined(BLOCKING_USE_SELECT) if (FD_ISSET(con->fd, &sel_set)) #else if (pf.revents&(POLLERR|POLLHUP|POLLNVAL)) { LM_ERR("poll error: flags %d - %d %d %d %d \n", pf.revents, POLLOUT,POLLERR,POLLHUP,POLLNVAL); poll_err=1; } #endif { err_len=sizeof(err); if (getsockopt(con->fd, SOL_SOCKET, SO_ERROR, &err, &err_len) < 0 || err != 0 || poll_err != 0) { if (err != EINPROGRESS && err != EALREADY) goto error; continue; } } if (req->error == TCP_REQ_OK) { bytes = ws_read_http(con, req); if (bytes == -1) { LM_ERR("failed to read %d:%s\n", errno, strerror(errno)); goto error; } if ((con->state==S_CONN_EOF) && (req->complete==0)) { LM_DBG("EOF received\n"); goto done; } } if (req->error!=TCP_REQ_OK){ LM_ERR("bad request, state=%d, error=%d " "buf:\n%.*s\nparsed:\n%.*s\n", req->state, req->error, (int)(req->pos-req->buf), req->buf, (int)(req->parsed-req->start), req->start); LM_DBG("- received from: port %d\n", con->rcv.src_port); print_ip("- received from: ip ",&con->rcv.src_ip, "\n"); goto error; } con->msg_attempts++; if (con->msg_attempts == _ws_common_max_msg_chunks) { LM_ERR("Made %u read attempts but message is not complete yet - " "closing connection \n",con->msg_attempts); goto error; } elapsed = get_time_diff(&begin); if (elapsed >= to) { LM_ERR("Timeout waiting for handshare response\n"); goto error; } else { to -= elapsed; } } while(!req->complete); #ifdef EXTRA_DEBUG LM_DBG("end of header part\n"); LM_DBG("- received from: port %d\n", con->rcv.src_port); print_ip("- received from: ip ", &con->rcv.src_ip, "\n"); LM_DBG("headers:\n%.*s.\n",(int)(req->body-req->start), req->start); #endif if (req->has_content_len) { LM_DBG("content-length= %d\n", req->content_len); #ifdef EXTRA_DEBUG LM_DBG("body:\n%.*s\n", req->content_len,req->body); #endif } /* update the timeout - we succesfully read the request */ tcp_conn_set_lifetime(con, tcp_con_lifetime); con->timeout=con->lifetime; con->rcv.proto_reserved1=con->id; /* copy the id */ /* we overwrite whatever is there, since we are not interested * in any other data after the handshake */ *req->parsed=0; /* prepare for next request */ size=req->pos-req->parsed; msg_buf = req->start; msg_len = req->parsed-req->start; if (!size) { /* did not read any more things - we can release * the connection */ LM_DBG("We're releasing the connection in state %d \n", con->state); } else { /* TODO - should we handle whatever data is sent by the client * even though the handshake is not completed */ /* TODO - we need to find a way to move data from tcp_req to ws_req */ LM_WARN("extra data on socket before handshake is completed!\n"); WS_STATE(con) = WS_CON_BAD_REQ; goto error; } /* TODO: parse and verify response */ if (ws_parse_rpl_handshake(con, msg_buf, msg_len) < 0) { LM_ERR("invalid WebSocket reply <%.*s>\n", msg_len, msg_buf); goto error; } init_tcp_req(req, 0); con->msg_attempts = 0; /* handshake now completed, destroy the handshake data */ WS_STATE(con) = WS_CON_HANDSHAKE_DONE; LM_DBG("ws_read end\n"); done: /* connection will be released */ return size; error: return -1; } static int ws_server_handshake(struct tcp_connection *con) { int bytes, total_bytes = 0; long size = 0; int msg_len; char *msg_buf; struct tcp_req *req; if (con->con_req) { if (WS_TYPE(con) != WS_SERVER) { LM_BUG("cannot create handshake as %d\n", WS_TYPE(con)); return -1; } req=con->con_req; LM_DBG("Using the per connection buff \n"); } else { if (WS_TYPE(con) != WS_NONE) { LM_BUG("not a new connection here %d", WS_TYPE(con)); return -1; } WS_TYPE(con) = WS_SERVER; WS_STATE(con) = WS_CON_HANDSHAKE; LM_DBG("Using the global ( per process ) buff \n"); init_tcp_req(&_ws_common_tcp_current_req, 0); req=&_ws_common_tcp_current_req; /* first time here, mark the state as being SERVER */ } if (req->error == TCP_REQ_OK) { bytes = ws_read_http(con, req); if (bytes == -1) { LM_ERR("failed to read %d:%s\n", errno, strerror(errno)); goto error; } total_bytes+=bytes; /* eof check: * is EOF if eof on fd and r. not complete yet, * if r. is complete we might have a second unparsed * request after it, so postpone release_with_eof */ if ((con->state==S_CONN_EOF) && (req->complete==0)) { LM_DBG("EOF received\n"); goto done; } } if (req->error!=TCP_REQ_OK){ LM_ERR("bad request, state=%d, error=%d " "buf:\n%.*s\nparsed:\n%.*s\n", req->state, req->error, (int)(req->pos-req->buf), req->buf, (int)(req->parsed-req->start), req->start); LM_DBG("- received from: port %d\n", con->rcv.src_port); print_ip("- received from: ip ",&con->rcv.src_ip, "\n"); goto error; } if (req->complete){ #ifdef EXTRA_DEBUG LM_DBG("end of header part\n"); LM_DBG("- received from: port %d\n", con->rcv.src_port); print_ip("- received from: ip ", &con->rcv.src_ip, "\n"); LM_DBG("headers:\n%.*s.\n",(int)(req->body-req->start), req->start); #endif if (req->has_content_len) { LM_DBG("content-length= %d\n", req->content_len); #ifdef EXTRA_DEBUG LM_DBG("body:\n%.*s\n", req->content_len,req->body); #endif } /* update the timeout - we successfully read the request */ tcp_conn_set_lifetime( con, tcp_con_lifetime); con->timeout=con->lifetime; con->rcv.proto_reserved1=con->id; /* copy the id */ /* we overwrite whatever is there, since we are not interested * in any other data after the handshake */ *req->parsed=0; /* prepare for next request */ size=req->pos-req->parsed; msg_buf = req->start; msg_len = req->parsed-req->start; if (!size) { /* did not read any more things - we can release * the connection */ LM_DBG("We're releasing the connection in state %d \n", con->state); if (req != &_ws_common_tcp_current_req) { /* we have the buffer in the connection tied buff - * detach it , release the conn and free it afterwards */ con->con_req = NULL; } /* TODO - we could indicate to the TCP net layer to release * the connection -> other worker may read the next available * message on the pipe */ } else { /* TODO - should we handle whatever data is sent by the client * even though the handshake is not completed */ LM_WARN("extra data on socket before handshake is completed!\n"); WS_STATE(con) = WS_CON_BAD_REQ; goto error; } if (ws_parse_req_handshake(con, msg_buf, msg_len) < 0) { LM_DBG("cannot parse handshake\n"); goto error; } if (ws_complete_handshake(con) < 0) { LM_DBG("cannot complete handshake\n"); goto error; } con->msg_attempts = 0; if (req != &_ws_common_tcp_current_req) pkg_free(req); /* handshake now completed, destroy the handshake data */ WS_STATE(con) = WS_CON_HANDSHAKE_DONE; /* finished handshake */ goto done; } else { /* request not complete - check the if the thresholds are exceeded */ con->msg_attempts++; if (con->msg_attempts == _ws_common_max_msg_chunks) { LM_ERR("Made %u read attempts but message is not complete yet - " "closing connection \n",con->msg_attempts); goto error; } } if (!req->complete && (req == &_ws_common_tcp_current_req)) { /* let's duplicate this - most likely another conn will come in */ con->con_req = pkg_malloc(sizeof(struct tcp_req)); if (con->con_req == NULL) { LM_ERR("No more mem for dynamic con request buffer\n"); goto error; } if (req->pos != req->buf) { /* we have read some bytes */ memcpy(con->con_req->buf,req->buf,req->pos-req->buf); con->con_req->pos = con->con_req->buf + (req->pos-req->buf); } else { con->con_req->pos = con->con_req->buf; } if (req->start != req->buf) con->con_req->start = con->con_req->buf +(req->start-req->buf); else con->con_req->start = con->con_req->buf; if (req->parsed != req->buf) con->con_req->parsed =con->con_req->buf+(req->parsed-req->buf); else con->con_req->parsed = con->con_req->buf; if (req->body != 0) { con->con_req->body = con->con_req->buf + (req->body-req->buf); } else con->con_req->body = 0; con->con_req->complete=req->complete; con->con_req->has_content_len=req->has_content_len; con->con_req->content_len=req->content_len; con->con_req->bytes_to_go=req->bytes_to_go; con->con_req->error = req->error; con->con_req->state = req->state; /* req will be reset on the next usage */ } LM_DBG("ws_read end\n"); done: /* connection will be released */ return size; error: /* connection will be released as ERROR */ if (WS_STATE(con) == WS_CON_BAD_REQ) ws_bad_handshake(con); if (req != &_ws_common_tcp_current_req) { pkg_free(req); con->con_req = NULL; } return -1; } static inline int ws_parse_req_http_fl(struct tcp_connection *c, char **msg_buf, int *msg_len, unsigned *ver_maj, unsigned *ver_min) { char *p, *sp, *spe, *end, *cr; int len; str tmp; /* go to the first CRLF. According to RFC 2616: * No CR or LF is allowed except in the final CRLF sequence.*/ cr = q_memchr(*msg_buf, '\r', *msg_len); if (!cr) { LM_ERR("invalid first line: cannot find CR\n"); goto error; } if (cr == *msg_buf + *msg_len || *(cr + 1) != '\n') { LM_ERR("invalid first line: CR is not followed by LF\n"); goto error; } /* update the message and len */ p = *msg_buf; end = cr; len = end - p; if (len < HTTP_GET_METHOD_LEN) { LM_ERR("invalid first line: method too small <%.*s>\n", len, p); goto error; } /* get the first space */ sp = q_memchr(p, ' ', len); if (!sp) { LM_ERR("invalid first line: cannot find method end\n"); goto error; } if (sp - p != HTTP_GET_METHOD_LEN || memcmp(p, HTTP_GET_METHOD, HTTP_GET_METHOD_LEN) != 0) { LM_ERR("invalid first line: method <%.*s>\n", len, p); goto error; } *msg_buf = cr + 2; *msg_len -= len + 2; /* XXX: skip spaces - the RFC says only spaces are allowed, but should we * also allow tabs?*/ for (; sp < end && *sp == ' '; sp++); if (sp == end) { LM_ERR("invalid first line: cannot find Request-URI\n"); goto error; } /* request URI */ for (spe = end - 1; spe > sp && *spe != ' '; spe--); if (spe == sp) { LM_ERR("invalid request URI: <%.*s>\n", (int)(end - sp), sp); goto error; } spe++; /* reverse search for the version */ if (end - spe < (5/* HTTP/ */ + 1/* DIGIT */ + 1/* . */ + 1 /* DIGIT */)) goto version_error; if (memcmp(spe, "HTTP/", 5) != 0) goto version_error; tmp.s = spe + 5; /* start of the major */ sp = q_memchr(tmp.s, '.', end - tmp.s); if (!sp || sp == tmp.s) { LM_ERR("cannot find DOT"); goto version_error; } tmp.len = sp - tmp.s; if (str2int(&tmp, ver_maj) < 0) { LM_ERR("invalid major\n"); goto version_error; } tmp.s = sp + 1; tmp.len = end - tmp.s; if (tmp.s >= end || str2int(&tmp, ver_min) < 0) { LM_ERR("invlid minor\n"); goto version_error; } return 0; version_error: LM_ERR("invalid version: <%.*s>\n", (int)(end - spe), spe); error: return -1; } static inline int ws_parse_rpl_http_fl(struct tcp_connection *c, char **msg_buf, int *msg_len, unsigned *ver_maj, unsigned *ver_min) { char *p, *sp, *end, *cr; int len; str tmp; /* go to the first CRLF. According to RFC 2616: * No CR or LF is allowed except in the final CRLF sequence.*/ cr = q_memchr(*msg_buf, '\r', *msg_len); if (!cr) { LM_ERR("invalid first line: cannot find CR\n"); goto error; } if (cr == *msg_buf + *msg_len || *(cr + 1) != '\n') { LM_ERR("invalid first line: CR is not followed by LF\n"); goto error; } /* update the message and len */ p = *msg_buf; end = cr; len = end - p; if (len < HTTP_VERSION_LEN) { LM_ERR("invalid first line: version too small <%.*s>\n", len, p); goto error; } if (memcmp(p, HTTP_VER_TOKEN, HTTP_VER_TOKEN_LEN) != 0) { LM_ERR("invalid first line: invalid protocol <%.*s>\n", len, p); goto error; } p += HTTP_VER_TOKEN_LEN; tmp.s = p; sp = q_memchr(tmp.s, '.', end - tmp.s); if (!sp || sp == tmp.s) { LM_ERR("cannot find version DOT"); goto error; } tmp.len = sp - tmp.s; if (str2int(&tmp, ver_maj) < 0) { LM_ERR("invalid major <%.*s>\n", tmp.len, tmp.s); goto error; } p = sp + 1; tmp.s = p; sp = q_memchr(tmp.s, ' ', end - tmp.s); if (!sp || sp == tmp.s) { LM_ERR("cannot find version separator"); goto error; } tmp.len = sp - tmp.s; if (str2int(&tmp, ver_min) < 0) { LM_ERR("invalid minor <%.*s>\n", tmp.len, tmp.s); goto error; } for (; sp < end && *sp == ' '; sp++); if (sp == end || (end - sp) < HTTP_REPLY_CODE_LEN) { LM_ERR("invalid first line: cannot find reply code\n"); goto error; } if (memcmp(sp, HTTP_REPLY_CODE, HTTP_REPLY_CODE_LEN) != 0) { LM_ERR("invalid first line: reply code <%.*s>\n", (int)(end - sp), sp); goto error; } sp += HTTP_REPLY_CODE_LEN; for (; sp < end && *sp == ' '; sp++); if (sp == end || (end - sp) < HTTP_REPLY_REASON1_LEN) { LM_ERR("invalid first line: cannot find reason1\n"); goto error; } if (strncasecmp(sp, HTTP_REPLY_REASON1, HTTP_REPLY_REASON1_LEN) != 0) { LM_ERR("invalid first line: reason <%.*s>\n", (int)(end - sp), sp); goto error; } p = sp + HTTP_REPLY_REASON1_LEN; for (; p < end && *p == ' '; p++); if (p == end || (end - p) < HTTP_REPLY_REASON2_LEN) { LM_ERR("invalid first line: cannot find reason2\n"); goto error; } if (strncasecmp(p, HTTP_REPLY_REASON2, HTTP_REPLY_REASON2_LEN) != 0) { LM_ERR("invalid first line: reason <%.*s>\n", (int)(end - sp), sp); goto error; } p += HTTP_REPLY_REASON2_LEN; for (; p < end; p++) if (*p != ' ') { LM_ERR("trailing characters: <%.*s>\n", (int)(end - p), p); goto error; } return 0; error: return -1; } static inline int ws_has_param(const char *p, int l, str ps) { char *pe; str tmp; do { /* search next comma */ pe = q_memchr(ps.s, ',', ps.len); if (!pe) { /* last parameter */ str_trim_spaces_lr(ps); return (ps.len == l && !strncasecmp(p, ps.s, ps.len)); } tmp.s = ps.s; tmp.len = pe - ps.s; str_trim_spaces_lr(tmp); if (tmp.len == l && !strncasecmp(p, tmp.s, tmp.len)) return 1; ps.len -= pe - ps.s + 1; ps.s = pe + 1; } while (ps.len > 0); return 0; } static int ws_parse_req_handshake(struct tcp_connection *c, char *msg, int len) { struct sip_msg tmp_msg; struct hdr_field *hf; unsigned version; char flags = 0; unsigned ver_min, ver_maj; if (ws_parse_req_http_fl(c, &msg, &len, &ver_maj, &ver_min) != 0) { LM_ERR("cannot parse the first line of the message\n%.*s\n", len, msg); goto error; } /* check the HTTP version */ if (ver_maj < HTTP_VER_MAJ || (ver_maj == HTTP_VER_MAJ && ver_min < HTTP_VER_MIN)) { LM_ERR("Invalid HTTP version: %u.%u\n", ver_maj, ver_min); goto error; } /* Parse the Headers */ memset(&tmp_msg, 0, sizeof(struct sip_msg)); tmp_msg.len = len; tmp_msg.buf = tmp_msg.unparsed = msg; if (parse_headers(&tmp_msg, HDR_EOH_F, 0) < 0) { LM_ERR("cannot parse headers\n%.*s\n", len, msg); goto error; } /* verify headers according to RFC6455 */ for (hf = tmp_msg.headers; hf; hf = hf->next) { if (hf->type != HDR_OTHER_T) continue; /* * since all mandatory headers have name length larger * than 4, we can use integer comparison from start */ if (hf->name.len < 4) continue; switch (GET_LOWER_DWORD(hf->name.s)) { case GET_DWORD('h', 'o', 's', 't'): /* Host */ if (hf->name.len == HDR_LEN("Host")) flags |= WS_HOST_F; break; case GET_DWORD('u', 'p', 'g', 'r'): /* Upgrade */ if (hf->name.len != HDR_LEN("Upgrade") || GET_LOWER(hf->name.s + 4) != 'a' || GET_LOWER(hf->name.s + 5) != 'd' || GET_LOWER(hf->name.s + 6) != 'e') break; if (!ws_has_param(WS_HDR, WS_HDR_LEN, hf->body)) { LM_ERR("Invalid Upgrade header <%.*s>\n", hf->body.len, hf->body.s); goto ws_error; } flags |= WS_UPGRADE_F; break; case GET_DWORD('c', 'o', 'n', 'n'): /* Connection */ if (hf->name.len != HDR_LEN("Connection") || GET_LOWER(hf->name.s + 4) != 'e' || GET_LOWER(hf->name.s + 5) != 'c' || GET_LOWER(hf->name.s + 6) != 't' || GET_LOWER(hf->name.s + 7) != 'i' || GET_LOWER(hf->name.s + 8) != 'o' || GET_LOWER(hf->name.s + 9) != 'n') break; if (!ws_has_param(WS_UPGRADE_HDR, WS_UPGRADE_HDR_LEN, hf->body)) { LM_ERR("Invalid Connection header <%.*s>\n", hf->body.len, hf->body.s); goto ws_error; } flags |= WS_CONN_F; break; case GET_DWORD('o', 'r', 'i', 'g'): /* Origin */ /* TODO: always check for origin? */ if (hf->name.len != HDR_LEN("Origin") || GET_LOWER(hf->name.s + 4) != 'i' || GET_LOWER(hf->name.s + 5) != 'n') break; flags |= WS_ORIGIN_F; break; case GET_DWORD('s', 'e', 'c', '-'): /* Sec-* */ if (hf->name.len < HDR_LEN("Sec-Websocket-*") || GET_LOWER(hf->name.s + 4) != 'w' || GET_LOWER(hf->name.s + 5) != 'e' || GET_LOWER(hf->name.s + 6) != 'b' || GET_LOWER(hf->name.s + 7) != 's' || GET_LOWER(hf->name.s + 8) != 'o' || GET_LOWER(hf->name.s + 9) != 'c' || GET_LOWER(hf->name.s + 10) != 'k' || GET_LOWER(hf->name.s + 11) != 'e' || GET_LOWER(hf->name.s + 12) != 't' || GET_LOWER(hf->name.s + 13) != '-') break; if (hf->name.len == HDR_LEN("Sec-WebSocket-Key") && GET_LOWER(hf->name.s + 14) == 'k' && GET_LOWER(hf->name.s + 15) == 'e' && GET_LOWER(hf->name.s + 16) == 'y') { str_trim_spaces_lr(hf->body); /* the key is already in the buffer, so we can just copy it */ WS_KEY(c) = hf->body; flags |= WS_KEY_F; } else if (hf->name.len == HDR_LEN("Sec-WebSocket-Version") && GET_LOWER(hf->name.s + 14) == 'v' && GET_LOWER(hf->name.s + 15) == 'e' && GET_LOWER(hf->name.s + 16) == 'r' && GET_LOWER(hf->name.s + 17) == 's' && GET_LOWER(hf->name.s + 18) == 'i' && GET_LOWER(hf->name.s + 19) == 'o' && GET_LOWER(hf->name.s + 20) == 'n') { str_trim_spaces_lr(hf->body); if (str2int(&hf->body, &version) < 0 || \ version != WS_SUPPORTED_VERSION) { LM_ERR("Invalid or unsported version <%.*s>\n", \ hf->body.len, hf->body.s); goto ws_error; } flags |= WS_VER_F; } else if (hf->name.len == HDR_LEN("Sec-WebSocket-Protocol") && GET_LOWER(hf->name.s + 14) == 'p' && GET_LOWER(hf->name.s + 15) == 'r' && GET_LOWER(hf->name.s + 16) == 'o' && GET_LOWER(hf->name.s + 17) == 't' && GET_LOWER(hf->name.s + 18) == 'o' && GET_LOWER(hf->name.s + 19) == 'c' && GET_LOWER(hf->name.s + 20) == 'o' && GET_LOWER(hf->name.s + 21) == 'l') { if (!ws_has_param(WS_PROTO_SIP, WS_PROTO_SIP_LEN, hf->body)) { LM_ERR("Invalid Protocol <%.*s>\n", hf->body.len, hf->body.s); goto ws_error; } flags |= WS_PROTO_F; } break; } } if (flags != WS_ALL_REQ_F) { /* negate so we can easily compare them */ flags = ~flags; if (flags & WS_HOST_F) LM_ERR("Host header not present!\n"); if (flags & WS_UPGRADE_F) LM_ERR("Upgrade header not present!\n"); if (flags & WS_CONN_F) LM_ERR("Connection header not present!\n"); if (flags & WS_ORIGIN_F) LM_ERR("Origin header not present!\n"); if (flags & WS_KEY_F) LM_ERR("Sec-WebSocket-Key header not present!\n"); if (flags & WS_VER_F) LM_ERR("Sec-WebSocket-Version header not present!\n"); if (flags & WS_PROTO_F) LM_ERR("Sec-WebSocket-Protocol header not present!\n"); goto ws_error; } /* parsing done, free headers */ free_hdr_field_lst(tmp_msg.headers); return 0; ws_error: free_hdr_field_lst(tmp_msg.headers); error: WS_STATE(c) = WS_CON_BAD_REQ; return -1; } /* * The Polar library needs this to be 64 bytes, to avoid overflow * when computing the SHA1 hash */ unsigned char ws_key_buf[64] = "xxxxxxxxxxxxxxxxxxxxxxxx" /* the key len: 24 */ WS_GUID_KEY /* the GUID */; unsigned char ws_sha1_buf[WS_SHA1_KEY_LEN]; unsigned char ws_accept_buf[WS_ACCEPT_KEY_LEN]; static void ws_compute_key(str *key) { memcpy(ws_key_buf, key->s, key->len); sha1(ws_key_buf, key->len + WS_GUID_KEY_LEN, ws_sha1_buf); base64encode(ws_accept_buf, ws_sha1_buf, WS_SHA1_KEY_LEN); } static int ws_is_valid_key(str *key, str *accept) { ws_compute_key(key); return strncasecmp((char *)ws_accept_buf, accept->s, accept->len); } static int ws_complete_handshake(struct tcp_connection *c) { int n; struct timeval get; static struct iovec iov[] = { { (void*)WS_HTTP_ACCEPT, WS_HTTP_ACCEPT_LEN }, /* all mandatory headers */ { (void *)ws_accept_buf, WS_ACCEPT_KEY_LEN }, /* the cookie */ { (void *)HTTP_END, HTTP_END_LEN }/* message end */ }; reset_tcp_vars(tcpthreshold); start_expire_timer(get, tcpthreshold); /* compute the ws_key in ws_accept_buf */ ws_compute_key(&WS_KEY(c)); n = _ws_common_writev(c, c->fd, iov, 3, _ws_common_write_tout); stop_expire_timer(get, tcpthreshold, _ws_common_module " handshake", "", 0, 1); return n; } static int ws_bad_handshake(struct tcp_connection *c) { int n; struct timeval get; static struct iovec iov[] = { { (void*)WS_HTTP_BAD_REQ, WS_HTTP_BAD_REQ_LEN }, }; reset_tcp_vars(tcpthreshold); start_expire_timer(get, tcpthreshold); n = _ws_common_writev(c, c->fd, iov, 1, _ws_common_write_tout); stop_expire_timer(get, tcpthreshold, _ws_common_module " handshake", "", 0, 1); return n; } static int ws_parse_rpl_handshake(struct tcp_connection *c, char *msg, int len) { struct sip_msg tmp_msg; struct hdr_field *hf; char flags = 0; unsigned ver_min, ver_maj; if (ws_parse_rpl_http_fl(c, &msg, &len, &ver_maj, &ver_min) != 0) { LM_ERR("cannot parse the first line of the message\n%.*s\n", len, msg); goto error; } /* check the HTTP version */ if (ver_maj < HTTP_VER_MAJ || (ver_maj == HTTP_VER_MAJ && ver_min < HTTP_VER_MIN)) { LM_ERR("Invalid HTTP version: %u.%u\n", ver_maj, ver_min); goto error; } /* Parse the Headers */ memset(&tmp_msg, 0, sizeof(struct sip_msg)); tmp_msg.len = len; tmp_msg.buf = tmp_msg.unparsed = msg; if (parse_headers(&tmp_msg, HDR_EOH_F, 0) < 0) { LM_ERR("cannot parse headers\n%.*s\n", len, msg); goto error; } /* verify headers according to RFC6455 */ for (hf = tmp_msg.headers; hf; hf = hf->next) { if (hf->type != HDR_OTHER_T) continue; /* * since all mandatory headers have name length larger * than 4, we can use integer comparison from start */ if (hf->name.len < 7) continue; switch (GET_LOWER_DWORD(hf->name.s)) { case GET_DWORD('u', 'p', 'g', 'r'): /* Upgrade */ if (hf->name.len != HDR_LEN("Upgrade") || GET_LOWER(hf->name.s + 4) != 'a' || GET_LOWER(hf->name.s + 5) != 'd' || GET_LOWER(hf->name.s + 6) != 'e') break; if (!ws_has_param(WS_HDR, WS_HDR_LEN, hf->body)) { LM_ERR("Invalid Upgrade header <%.*s>\n", hf->body.len, hf->body.s); goto ws_error; } flags |= WS_UPGRADE_F; break; case GET_DWORD('c', 'o', 'n', 'n'): /* Connection */ if (hf->name.len != HDR_LEN("Connection") || GET_LOWER(hf->name.s + 4) != 'e' || GET_LOWER(hf->name.s + 5) != 'c' || GET_LOWER(hf->name.s + 6) != 't' || GET_LOWER(hf->name.s + 7) != 'i' || GET_LOWER(hf->name.s + 8) != 'o' || GET_LOWER(hf->name.s + 9) != 'n') break; if (!ws_has_param(WS_UPGRADE_HDR, WS_UPGRADE_HDR_LEN, hf->body)) { LM_ERR("Invalid Connection header <%.*s>\n", hf->body.len, hf->body.s); goto ws_error; } flags |= WS_CONN_F; break; case GET_DWORD('s', 'e', 'c', '-'): /* Sec-* */ if (hf->name.len < HDR_LEN("Sec-Websocket-*") || GET_LOWER(hf->name.s + 4) != 'w' || GET_LOWER(hf->name.s + 5) != 'e' || GET_LOWER(hf->name.s + 6) != 'b' || GET_LOWER(hf->name.s + 7) != 's' || GET_LOWER(hf->name.s + 8) != 'o' || GET_LOWER(hf->name.s + 9) != 'c' || GET_LOWER(hf->name.s + 10) != 'k' || GET_LOWER(hf->name.s + 11) != 'e' || GET_LOWER(hf->name.s + 12) != 't' || GET_LOWER(hf->name.s + 13) != '-') break; if (hf->name.len == HDR_LEN("Sec-WebSocket-Accept") && GET_LOWER(hf->name.s + 14) == 'a' && GET_LOWER(hf->name.s + 15) == 'c' && GET_LOWER(hf->name.s + 16) == 'c' && GET_LOWER(hf->name.s + 17) == 'e' && GET_LOWER(hf->name.s + 18) == 'p' && GET_LOWER(hf->name.s + 19) == 't') { str_trim_spaces_lr(hf->body); if (!ws_is_valid_key(&WS_KEY(c), &hf->body)) { LM_ERR("invalid answer key <%.*s> for <%.*s>\n", hf->body.len, hf->body.s, WS_KEY(c).len, WS_KEY(c).s); goto error; } flags |= WS_ACCEPT_F; } else if (hf->name.len == HDR_LEN("Sec-WebSocket-Protocol") && GET_LOWER(hf->name.s + 14) == 'p' && GET_LOWER(hf->name.s + 15) == 'r' && GET_LOWER(hf->name.s + 16) == 'o' && GET_LOWER(hf->name.s + 17) == 't' && GET_LOWER(hf->name.s + 18) == 'o' && GET_LOWER(hf->name.s + 19) == 'c' && GET_LOWER(hf->name.s + 20) == 'o' && GET_LOWER(hf->name.s + 21) == 'l') { if (!ws_has_param(WS_PROTO_SIP, WS_PROTO_SIP_LEN, hf->body)) { LM_ERR("Invalid Protocol <%.*s>\n", hf->body.len, hf->body.s); goto ws_error; } /* TODO: verify accepted protocols */ flags |= WS_PROTO_F; } else if (hf->name.len == HDR_LEN("Sec-WebSocket-Extensions") && GET_LOWER(hf->name.s + 14) == 'e' && GET_LOWER(hf->name.s + 15) == 'x' && GET_LOWER(hf->name.s + 16) == 't' && GET_LOWER(hf->name.s + 17) == 'e' && GET_LOWER(hf->name.s + 18) == 'n' && GET_LOWER(hf->name.s + 19) == 's' && GET_LOWER(hf->name.s + 20) == 'i' && GET_LOWER(hf->name.s + 21) == 'o' && GET_LOWER(hf->name.s + 22) == 'n' && GET_LOWER(hf->name.s + 23) == 's') { LM_ERR("Extensions are not yet supported\n"); goto ws_error; } break; } } if (flags != WS_ALL_REQ_F) { /* negate so we can easily compare them */ flags = ~flags; if (flags & WS_HOST_F) LM_ERR("Host header not present!\n"); if (flags & WS_UPGRADE_F) LM_ERR("Upgrade header not present!\n"); if (flags & WS_CONN_F) LM_ERR("Connection header not present!\n"); if (flags & WS_ORIGIN_F) LM_ERR("Origin header not present!\n"); if (flags & WS_KEY_F) LM_ERR("Sec-WebSocket-Key header not present!\n"); if (flags & WS_VER_F) LM_ERR("Sec-WebSocket-Version header not present!\n"); if (flags & WS_PROTO_F) LM_ERR("Sec-WebSocket-Protocol header not present!\n"); goto ws_error; } /* parsing done, free headers */ free_hdr_field_lst(tmp_msg.headers); return 0; ws_error: free_hdr_field_lst(tmp_msg.headers); error: WS_STATE(c) = WS_CON_BAD_REQ; return -1; } static int ws_start_handshake(struct tcp_connection *c) { int n; struct timeval get; char *ip; char *port; int port_len; static char host_orig_buf[MAX_HOST_LEN]; static struct iovec iov[] = { { (void*)HTTP_GET_METHOD, HTTP_GET_METHOD_LEN }, /* GET method */ { (void*)" ", 1 }, { (void*)NULL, 0 }, /* the resource */ { (void*)" ", 1 }, { (void*)HTTP_VERSION, HTTP_VERSION_LEN }, /* the version */ { (void*)HTTP_SEP, HTTP_SEP_LEN }, { (void*)"Host: ", 6 }, { (void*)host_orig_buf, 0 }, /* the host */ { (void*)HTTP_SEP, HTTP_SEP_LEN }, { (void*)"Origin: ", 8 }, { (void*)host_orig_buf, 0 }, /* the origin */ { (void*)HTTP_SEP, HTTP_SEP_LEN }, { (void*)"Sec-WebSocket-Key: ", 19 }, { (void*)NULL, 0 }, /* the origin */ { (void*)HTTP_SEP, HTTP_SEP_LEN }, { (void*)HTTP_HANDSHAKE_END, HTTP_HANDSHAKE_END_LEN }, /* constant part */ }; reset_tcp_vars(tcpthreshold); start_expire_timer(get, tcpthreshold); ip = ip_addr2a(&c->rcv.dst_ip); port = int2str(c->rcv.dst_port, &port_len); n = strlen(ip); memcpy(host_orig_buf, ip, n); host_orig_buf[n] = ':'; memcpy(host_orig_buf + n + 1, port, port_len); iov[2].iov_base = _ws_common_resource.s; iov[2].iov_len = _ws_common_resource.len; iov[7].iov_len = n + port_len + 1; iov[10].iov_len = iov[7].iov_len; iov[13].iov_base = WS_KEY(c).s; iov[13].iov_len = WS_KEY(c).len; n = _ws_common_writev(c, c->fd, iov, 16, _ws_common_write_tout); stop_expire_timer(get, tcpthreshold, _ws_common_module " start handshake", "", 0, 1); return n; } /*! \brief * reads all headers (until double crlf), & parses the content-length header * * \note (WARNING: inefficient, tries to reuse receive_msg but will go through * the headers twice [once here looking for Content-Length and for the end * of the headers and once in receive_msg]; a more speed efficient version will * result in either major code duplication or major changes to the receive code) * * \return number of bytes read & sets r->state & r->body * when either r->body!=0 or r->state==H_BODY => * all headers have been read. It should be called in a while loop. * returns < 0 if error or 0 if EOF */ static int ws_read_http(struct tcp_connection *c, struct tcp_req *r) { unsigned int remaining; int bytes; char *p; #define crlf_default_skip_case \ case '\n': \ r->state=H_LF; \ break; \ default: \ r->state=H_SKIP #define content_len_beg_case \ case ' ': \ case '\t': \ if (!r->has_content_len) r->state=H_STARTWS; \ else r->state=H_SKIP; \ /* not interested if we already found one */ \ break; \ case 'C': \ case 'c': \ if(!r->has_content_len) r->state=H_CONT_LEN1; \ else r->state=H_SKIP; \ break; \ case 'l': \ case 'L': \ /* short form for Content-Length */ \ if (!r->has_content_len) r->state=H_L_COLON; \ else r->state=H_SKIP; \ break #define change_state(upper, lower, newstate)\ switch(*p){ \ case upper: \ case lower: \ r->state=(newstate); break; \ crlf_default_skip_case; \ } #define change_state_case(state0, upper, lower, newstate)\ case state0: \ change_state(upper, lower, newstate); \ p++; \ break /* if we still have some unparsed part, parse it first, don't do the read*/ if (r->parsedpos){ bytes=0; }else{ bytes=_ws_common_read(c, r); if (bytes<=0) return bytes; } p=r->parsed; while(ppos && r->error==TCP_REQ_OK){ switch((unsigned char)r->state){ case H_BODY: /* read the body*/ LM_INFO("Reading the body\n"); remaining=r->pos-p; if (remaining>r->bytes_to_go) remaining=r->bytes_to_go; r->bytes_to_go-=remaining; p+=remaining; if (r->bytes_to_go==0){ r->complete=1; goto skip; } break; case H_SKIP: /* find lf, we are in this state if we are not interested * in anything till end of line*/ p=q_memchr(p, '\n', r->pos-p); if (p){ p++; r->state=H_LF; }else{ p=r->pos; } break; case H_LF: /* terminate on LF CR LF or LF LF */ switch (*p){ case '\r': r->state=H_LFCR; break; case '\n': /* found LF LF */ r->state=H_BODY; if (r->has_content_len){ r->body=p+1; r->bytes_to_go=r->content_len; if (r->bytes_to_go==0){ r->complete=1; p++; goto skip; } }else{ LM_DBG("no clen, p=%X\n", *p); r->complete=1; p++; goto skip; } break; content_len_beg_case; default: r->state=H_SKIP; } p++; break; case H_LFCR: if (*p=='\n'){ /* found LF CR LF */ r->state=H_BODY; if (r->has_content_len){ r->body=p+1; r->bytes_to_go=r->content_len; if (r->bytes_to_go==0){ r->complete=1; p++; goto skip; } }else{ LM_DBG("no clen, p=%X\n", *p); r->complete=1; p++; goto skip; } }else r->state=H_SKIP; p++; break; case H_STARTWS: switch (*p){ content_len_beg_case; crlf_default_skip_case; } p++; break; case H_SKIP_EMPTY: switch (*p){ case '\n': case '\r': case ' ': case '\t': /* skip empty lines */ break; case 'C': case 'c': r->state=H_CONT_LEN1; r->start=p; break; case 'l': case 'L': /* short form for Content-Length */ r->state=H_L_COLON; r->start=p; break; default: r->state=H_SKIP; r->start=p; }; p++; break; case H_SKIP_EMPTY_CR_FOUND: if (*p=='\n'){ r->state=H_SKIP_EMPTY_CRLF_FOUND; p++; }else{ r->state=H_SKIP_EMPTY; } break; case H_SKIP_EMPTY_CRLF_FOUND: if (*p=='\r'){ r->state = H_SKIP_EMPTY_CRLFCR_FOUND; p++; }else{ r->state = H_SKIP_EMPTY; } break; case H_SKIP_EMPTY_CRLFCR_FOUND: if (*p=='\n'){ r->state = H_PING_CRLFCRLF; r->complete = 1; r->has_content_len = 0; /* hack to avoid error check */ p++; goto skip; }else{ r->state = H_SKIP_EMPTY; } break; change_state_case(H_CONT_LEN1, 'O', 'o', H_CONT_LEN2); change_state_case(H_CONT_LEN2, 'N', 'n', H_CONT_LEN3); change_state_case(H_CONT_LEN3, 'T', 't', H_CONT_LEN4); change_state_case(H_CONT_LEN4, 'E', 'e', H_CONT_LEN5); change_state_case(H_CONT_LEN5, 'N', 'n', H_CONT_LEN6); change_state_case(H_CONT_LEN6, 'T', 't', H_CONT_LEN7); change_state_case(H_CONT_LEN7, '-', '_', H_CONT_LEN8); change_state_case(H_CONT_LEN8, 'L', 'l', H_CONT_LEN9); change_state_case(H_CONT_LEN9, 'E', 'e', H_CONT_LEN10); change_state_case(H_CONT_LEN10, 'N', 'n', H_CONT_LEN11); change_state_case(H_CONT_LEN11, 'G', 'g', H_CONT_LEN12); change_state_case(H_CONT_LEN12, 'T', 't', H_CONT_LEN13); change_state_case(H_CONT_LEN13, 'H', 'h', H_L_COLON); case H_L_COLON: switch(*p){ case ' ': case '\t': break; /* skip space */ case ':': r->state=H_CONT_LEN_BODY; break; crlf_default_skip_case; }; p++; break; case H_CONT_LEN_BODY: switch(*p){ case ' ': case '\t': break; /* eat space */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': r->state=H_CONT_LEN_BODY_PARSE; r->content_len=(*p-'0'); break; /*FIXME: content length on different lines ! */ crlf_default_skip_case; } p++; break; case H_CONT_LEN_BODY_PARSE: switch(*p){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': r->content_len=r->content_len*10+(*p-'0'); break; case '\r': case ' ': case '\t': /* FIXME: check if line contains only WS */ r->state=H_SKIP; r->has_content_len=1; break; case '\n': /* end of line, parse successful */ r->state=H_LF; r->has_content_len=1; break; default: LM_ERR("bad Content-Length header value, unexpected " "char %c in state %d\n", *p, r->state); r->state=H_SKIP; /* try to find another?*/ } p++; break; default: LM_CRIT("unexpected state %d\n", r->state); abort(); } } skip: r->parsed=p; return bytes; } #endif /* _WS_HANDSHAKE_COMMON_H_ */ opensips-2.2.2/modules/proto_ws/ws_tcp.c000066400000000000000000000052711300170765700203570ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-xx first version (razvanc) */ #include "../../mem/shm_mem.h" #include "../../ut.h" #include "../../globals.h" #include "../../tsend.h" #include "ws_tcp.h" #include /************** READ related functions ***************/ /*! \brief reads next available bytes * \return number of bytes read, 0 on EOF or -1 on error, * on EOF it also sets c->state to S_CONN_EOF * (to distinguish from reads that would block which could return 0) * sets also wb->error */ int ws_raw_read(struct tcp_connection *c,struct tcp_req *r) { int bytes_free, bytes_read; int fd; fd=c->fd; bytes_free=TCP_BUF_SIZE- (int)(r->pos - r->buf); if (bytes_free==0){ LM_ERR("buffer overrun, dropping\n"); r->error=TCP_REQ_OVERRUN; return -1; } again: bytes_read=read(fd, r->pos, bytes_free); if(bytes_read==-1){ if (errno == EWOULDBLOCK || errno == EAGAIN){ return 0; /* nothing has been read */ }else if (errno == EINTR) goto again; else{ LM_ERR("error reading: %s\n",strerror(errno)); r->error=TCP_READ_ERROR; return -1; } }else if (bytes_read==0){ c->state=S_CONN_EOF; LM_DBG("EOF on %p, FD %d\n", c, fd); } #ifdef EXTRA_DEBUG LM_DBG("read %d bytes:\n%.*s\n", bytes_read, bytes_read, r->pos); #endif r->pos+=bytes_read; return bytes_read; } /************** WRITE related functions **************/ int ws_raw_writev(struct tcp_connection *c, int fd, const struct iovec *iov, int iovcnt, int tout) { int n; /* we do not have any threosholds for ws struct timeval snd; start_expire_timer(snd,tcpthreshold); */ lock_get(&c->write_lock); /* optimize write for a single chunk */ if (iovcnt == 1) n=tsend_stream(fd, iov[0].iov_base, iov[0].iov_len, tout); else n=tsend_stream_ev(fd, iov, iovcnt, tout); lock_release(&c->write_lock); /* get_time_difference(snd, tcpthreshold, tout); */ return n; } opensips-2.2.2/modules/proto_ws/ws_tcp.h000066400000000000000000000023301300170765700203550ustar00rootroot00000000000000/* * Copyright (C) 2015 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-xx first version (razvanc) */ #ifndef _WS_TCP_H_ #define _WS_TCP_H_ #include "../../net/tcp_conn_defs.h" #include "../../net/proto_tcp/tcp_common_defs.h" int ws_raw_read(struct tcp_connection *c, struct tcp_req *r); int ws_raw_writev(struct tcp_connection *c, int fd, const struct iovec *iov, int iovcnt, int tout); #endif /* _WS_TCP_H_ */ opensips-2.2.2/modules/proto_wss/000077500000000000000000000000001300170765700170725ustar00rootroot00000000000000opensips-2.2.2/modules/proto_wss/Makefile000066400000000000000000000011551300170765700205340ustar00rootroot00000000000000# # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=proto_wss.so ETC_DIR?=../../etc/ ifeq ($(CROSS_COMPILE),) SSL_BUILDER=$(shell \ if pkg-config --exists libssl; then \ echo 'pkg-config libssl'; \ fi) endif ifneq ($(SSL_BUILDER),) DEFS += $(shell $(SSL_BUILDER) --cflags) LIBS += $(shell $(SSL_BUILDER) --libs) else DEFS += -I$(LOCALBASE)/ssl/include \ -I$(LOCALBASE)/include LIBS += -L$(LOCALBASE)/lib -L$(LOCALBASE)/ssl/lib \ -L$(LOCALBASE)/lib64 -L$(LOCALBASE)/ssl/lib64 \ -lssl -lcrypto endif include ../../Makefile.modules opensips-2.2.2/modules/proto_wss/README000066400000000000000000000103771300170765700177620ustar00rootroot00000000000000proto_wss Module OpenSIPS Project Edited by Razvan Crainea Copyright © 2016 OpenSIPS Project __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. Dependencies of external libraries 1.3. Exported Parameters 1.3.1. listen=interface 1.3.2. wss_port (integer) 1.3.3. wss_max_msg_chunks (integer) 1.3.4. wss_resource (string) 1.3.5. wss_handshake_timeout (integer) 2. Frequently Asked Questions List of Examples 1.1. Set listen variable 1.2. Set wss_port variable 1.3. Set wss_max_msg_chunks parameter 1.4. Set wss_resource parameter 1.5. Set wss_handshake_timeout parameter Chapter 1. Admin Guide 1.1. Overview The WSS (Secure WebSocket) module provides the ability to communicate with a WebSocket (RFC 6455) client or server over a secure (TLS encrypted) channel. As part of the WebRTC specifications, this protocol can be used to provide secure VoIP calls to HTTPS enabled browsers. This module behaves as any other transport protocol module: in order to use it, you must define one or more listeners that will handle the secure WebSocket traffic, after the mpath parameter: ... mpath=/path/to/modules ... listen=wss:10.0.0.1 # change with the listening IP listen=wss:10.0.0.1:5060 # change with the listening IP and port ... Besides that, you need to define the TLS parameters for securing the connection. This is done through the tls_mgm module interface, similar to the proto_tls module: modparam("tls_mgm", "certificate", "/certs/biloxy.com/cert.pem") modparam("tls_mgm", "private_key", "/certs/biloxy.com/privkey.pem") modparam("tls_mgm", "ca_list", "/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "tlsv1") modparam("tls_mgm", "verify_cert", "1") modparam("tls_mgm", "require_cert", "1") Check the tls_mgm module documentation for more info. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * tls_mgm. 1.2.2. Dependencies of external libraries OpenSIPS TLS v1.0 support requires the following packages: * openssl or libssl >= 0.9.6 * openssl-dev or libssl-dev OpenSIPS TLS v1.1/1.2 support requires the following packages: * openssl or libssl >= 1.0.1e * openssl-dev or libssl-dev 1.3. Exported Parameters All these parameters can be used from the opensips.cfg file, to configure the behavior of OpenSIPS-WSS. 1.3.1. listen=interface This is a global parameter that specifies what interface/IP and port should handle WSS traffic. Example 1.1. Set listen variable ... listen = wss:1.2.3.4:44344 ... 1.3.2. wss_port (integer) Sets the default WSS listening port. Default value is 443. Example 1.2. Set wss_port variable ... modparam("proto_wss", "wss_port", 44344) ... 1.3.3. wss_max_msg_chunks (integer) The maximum number of chunks in which a SIP message is expected to arrive via WSS. If a received packet is more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending very fragmented traffic in order to decrease server performance). Default value is 4. Example 1.3. Set wss_max_msg_chunks parameter ... modparam("proto_wss", "wss_max_msg_chunks", 8) ... 1.3.4. wss_resource (string) The resource queried for when a WebSocket handshake is initiated. Default value is “/â€. Example 1.4. Set wss_resource parameter ... modparam("proto_wss", "wss_resource", "/wss") ... 1.3.5. wss_handshake_timeout (integer) This parameter specifies the time in milliseconds the proto_wss module waits for a WebSocket handshake reply from a WebSocket server. Default value is 100. Example 1.5. Set wss_handshake_timeout parameter ... modparam("proto_wss", "wss_handshake_timeout", 300) ... Chapter 2. Frequently Asked Questions 2.1. Does OpenSIPS support fragmented Secure WebSocket messages? No, the WebSocket fragmentation mechanism is not supported. opensips-2.2.2/modules/proto_wss/doc/000077500000000000000000000000001300170765700176375ustar00rootroot00000000000000opensips-2.2.2/modules/proto_wss/doc/proto_wss.xml000066400000000000000000000016621300170765700224250ustar00rootroot00000000000000 %docentities; ]> proto_wss Module &osipsname; &osips; Project
support@opensips.org
Razvan Crainea
razvan@opensips.org
2016 &osips; Project
&admin; &faq;
opensips-2.2.2/modules/proto_wss/doc/proto_wss_admin.xml000066400000000000000000000127411300170765700235750ustar00rootroot00000000000000 &adminguide;
Overview The WSS (Secure WebSocket) module provides the ability to communicate with a WebSocket (RFC 6455) client or server over a secure (TLS encrypted) channel. As part of the WebRTC specifications, this protocol can be used to provide secure VoIP calls to HTTPS enabled browsers. This module behaves as any other transport protocol module: in order to use it, you must define one or more listeners that will handle the secure WebSocket traffic, after the mpath parameter: ... mpath=/path/to/modules ... listen=wss:10.0.0.1 # change with the listening IP listen=wss:10.0.0.1:5060 # change with the listening IP and port ... Besides that, you need to define the TLS parameters for securing the connection. This is done through the tls_mgm module interface, similar to the proto_tls module: modparam("tls_mgm", "certificate", "/certs/biloxy.com/cert.pem") modparam("tls_mgm", "private_key", "/certs/biloxy.com/privkey.pem") modparam("tls_mgm", "ca_list", "/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "tlsv1") modparam("tls_mgm", "verify_cert", "1") modparam("tls_mgm", "require_cert", "1") Check the tls_mgm module documentation for more info.
Dependencies
&osips; Modules The following modules must be loaded before this module: tls_mgm.
Dependencies of external libraries &osips; TLS v1.0 support requires the following packages: openssl or libssl >= 0.9.6 openssl-dev or libssl-dev &osips; TLS v1.1/1.2 support requires the following packages: openssl or libssl >= 1.0.1e openssl-dev or libssl-dev
Exported Parameters All these parameters can be used from the opensips.cfg file, to configure the behavior of &osips;-WSS.
<varname>listen</varname>=interface This is a global parameter that specifies what interface/IP and port should handle WSS traffic. Set <varname>listen</varname> variable ... listen = wss:1.2.3.4:44344 ...
<varname>wss_port</varname> (integer) Sets the default WSS listening port. Default value is 443. Set <varname>wss_port</varname> variable ... modparam("proto_wss", "wss_port", 44344) ...
<varname>wss_max_msg_chunks</varname> (integer) The maximum number of chunks in which a SIP message is expected to arrive via WSS. If a received packet is more fragmented than this, the connection is dropped (either the connection is very overloaded and this leads to high fragmentation - or we are the victim of an ongoing attack where the attacker is sending very fragmented traffic in order to decrease server performance). Default value is 4. Set <varname>wss_max_msg_chunks</varname> parameter ... modparam("proto_wss", "wss_max_msg_chunks", 8) ...
<varname>wss_resource</varname> (string) The resource queried for when a WebSocket handshake is initiated. Default value is /. Set <varname>wss_resource</varname> parameter ... modparam("proto_wss", "wss_resource", "/wss") ...
<varname>wss_handshake_timeout</varname> (integer) This parameter specifies the time in milliseconds the proto_wss module waits for a WebSocket handshake reply from a WebSocket server. Default value is 100. Set <varname>wss_handshake_timeout</varname> parameter ... modparam("proto_wss", "wss_handshake_timeout", 300) ...
opensips-2.2.2/modules/proto_wss/doc/proto_wss_faq.xml000066400000000000000000000005511300170765700232500ustar00rootroot00000000000000 &faqguide; Does &osips; support fragmented Secure WebSocket messages? No, the WebSocket fragmentation mechanism is not supported. opensips-2.2.2/modules/proto_wss/proto_wss.c000066400000000000000000000271071300170765700213040ustar00rootroot00000000000000/* * Copyright (C) 2016 - OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-12-xx first version (razvanc) */ #include #include #include #include #include "../../pt.h" #include "../../sr_module.h" #include "../../net/net_tcp.h" #include "../../net/api_proto.h" #include "../../net/api_proto_net.h" #include "../../socket_info.h" #include "../../tsend.h" #include "../../receive.h" #include "../../timer.h" #include "../../net/tcp_conn_defs.h" #include "../../net/proto_tcp/tcp_common_defs.h" #include "../proto_ws/proto_ws.h" #include "proto_wss.h" #include "../proto_ws/ws_common_defs.h" #include "../tls_mgm/api.h" #include "../tls_mgm/tls_conn_ops.h" #include "../tls_mgm/tls_conn_server.h" struct tls_mgm_binds tls_mgm_api; /* parameters*/ int wss_max_msg_chunks = TCP_CHILD_MAX_MSG_CHUNK; static struct tcp_req tcp_current_req; static struct ws_req wss_current_req; int wss_hs_read_tout = 100; /* XXX: this information should be dynamically provided */ static str wss_resource = str_init("/"); static int wss_raw_writev(struct tcp_connection *c, int fd, const struct iovec *iov, int iovcnt, int tout); #define _ws_common_module "wss" #define _ws_common_tcp_current_req tcp_current_req #define _ws_common_current_req wss_current_req #define _ws_common_max_msg_chunks wss_max_msg_chunks #define _ws_common_read tls_read #define _ws_common_writev wss_raw_writev #define _ws_common_read_tout wss_hs_read_tout /* * the timeout is only used by the _ws_common_writev function * but in our case, the timeout specified in the TLS MGM * module is used, so we no longer need this here */ #define _ws_common_write_tout 0 #define _ws_common_resource wss_resource #include "../proto_ws/ws_handshake_common.h" #include "../proto_ws/ws_common.h" static int mod_init(void); static int proto_wss_init(struct proto_info *pi); static int proto_wss_init_listener(struct socket_info *si); static int proto_wss_send(struct socket_info* send_sock, char* buf, unsigned int len, union sockaddr_union* to, int id); static int wss_read_req(struct tcp_connection* con, int* bytes_read); static int wss_conn_init(struct tcp_connection* c); static void ws_conn_clean(struct tcp_connection* c); static int wss_port = WSS_DEFAULT_PORT; static cmd_export_t cmds[] = { {"proto_init", (cmd_function)proto_wss_init, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[] = { /* XXX: should we drop the ws prefix? */ { "wss_port", INT_PARAM, &wss_port }, { "wss_max_msg_chunks", INT_PARAM, &wss_max_msg_chunks }, { "wss_resource", STR_PARAM, &wss_resource }, { "wss_handshake_timeout", INT_PARAM, &wss_hs_read_tout}, {0, 0, 0} }; struct module_exports exports = { PROTO_PREFIX "wss", /* module name*/ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* per-child init function */ }; static int proto_wss_init(struct proto_info *pi) { pi->id = PROTO_WSS; pi->name = "wss"; pi->default_port = wss_port; pi->tran.init_listener = proto_wss_init_listener; pi->tran.send = proto_wss_send; pi->tran.dst_attr = tcp_conn_fcntl; pi->net.flags = PROTO_NET_USE_TCP; pi->net.read = (proto_net_read_f)wss_read_req; pi->net.conn_init = wss_conn_init; pi->net.conn_clean = ws_conn_clean; return 0; } static int mod_init(void) { LM_INFO("initializing Secure WebSocket protocol\n"); if(load_tls_mgm_api(&tls_mgm_api) != 0){ LM_DBG("failed to find tls API - is tls_mgm module loaded?\n"); return -1; } return 0; } static int wss_conn_init(struct tcp_connection* c) { struct ws_data *d; int ret; /* allocate the tcp_data and the array of chunks as a single mem chunk */ d = (struct ws_data *)shm_malloc(sizeof(*d)); if (d==NULL) { LM_ERR("failed to create ws states in shm mem\n"); return -1; } d->state = WS_CON_INIT; d->type = WS_NONE; d->code = WS_ERR_NONE; c->proto_data = (void*)d; ret = tls_conn_init(c, &tls_mgm_api); if (ret < 0) { LM_ERR("Cannot initiate the conn\n"); shm_free(d); } return ret; } static void ws_conn_clean(struct tcp_connection* c) { struct ws_data *d = (struct ws_data*)c->proto_data; if (!d) return; if (c->state == S_CONN_OK) { switch (d->code) { case WS_ERR_NOSEND: break; case WS_ERR_NONE: WS_CODE(c) = WS_ERR_NORMAL; default: ws_close(c); break; } } shm_free(d); c->proto_data = NULL; tls_conn_clean(c); } static int proto_wss_init_listener(struct socket_info *si) { /* we do not do anything particular to TCP plain here, so * transparently use the generic listener init from net TCP layer */ return tcp_init_listener(si); } static struct tcp_connection* ws_sync_connect(struct socket_info* send_sock, union sockaddr_union* server) { int s; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (tcp_init_sock_opt(s)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(s, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } if (tcp_connect_blocking(s, &server->s, sockaddru_len(*server))<0){ LM_ERR("tcp_blocking_connect failed\n"); goto error; } con=tcp_conn_new(s, server, send_sock, S_CONN_OK); if (con==NULL){ LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } /* it is safe to move this here and clear it after we complete the * handshake, just before sending the fd to main */ con->fd = s; return con; error: /* close the opened socket */ if (s!=-1) close(s); return 0; } static struct tcp_connection* ws_connect(struct socket_info* send_sock, union sockaddr_union* to, int *fd) { struct tcp_connection *c; if ((c=ws_sync_connect(send_sock, to))==0) { LM_ERR("connect failed\n"); return NULL; } /* the state of the connection should be NONE, otherwise something is * wrong */ if (WS_TYPE(c) != WS_NONE) { LM_BUG("invalid type for connection %d\n", WS_TYPE(c)); goto error; } WS_TYPE(c) = WS_CLIENT; if (ws_client_handshake(c) < 0) { LM_ERR("cannot complete WebSocket handshake\n"); goto error; } *fd = c->fd; /* clear the fd, just in case */ c->fd = -1; /* handshake done - send the socket to main */ if (tcp_conn_send(c) < 0) { LM_ERR("cannot send socket to main\n"); goto error; } return c; error: tcp_conn_destroy(c); return NULL; } /************** WRITE related functions ***************/ /*! \brief Finds a tcpconn & sends on it */ static int proto_wss_send(struct socket_info* send_sock, char* buf, unsigned int len, union sockaddr_union* to, int id) { struct tcp_connection *c; struct timeval get; struct ip_addr ip; int port = 0; int fd, n; reset_tcp_vars(tcpthreshold); start_expire_timer(get,tcpthreshold); if (to){ su2ip_addr(&ip, to); port=su_getport(to); n = tcp_conn_get(id, &ip, port, PROTO_WSS, &c, &fd); }else if (id){ n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd); }else{ LM_CRIT("prot_tls_send called with null id & to\n"); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } if (n<0) { /* error during conn get, return with error too */ LM_ERR("failed to aquire connection\n"); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } /* was connection found ?? */ if (c==0) { if (tcp_no_new_conn) { return -1; } if (!to) { LM_ERR("Unknown destination - cannot open new tcp connection\n"); return -1; } LM_DBG("no open tcp connection found, opening new one\n"); /* create tcp connection */ if ((c=ws_connect(send_sock, to, &fd))==0) { LM_ERR("connect failed\n"); return -1; } goto send_it; } get_time_difference(get, tcpthreshold, tcp_timeout_con_get); /* now we have a connection, let's what we can do with it */ /* BE CAREFUL now as we need to release the conn before exiting !!! */ if (fd==-1) { /* connection is not writable because of its state */ /* return error, nothing to do about it */ tcp_conn_release(c, 0); return -1; } send_it: LM_DBG("sending via fd %d...\n",fd); n = ws_req_write(c, fd, buf, len); stop_expire_timer(get, tcpthreshold, "WSS ops",buf,(int)len,1); tcp_conn_set_lifetime( c, tcp_con_lifetime); LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd); if (n<0){ LM_ERR("failed to send\n"); c->state=S_CONN_BAD; if (c->proc_id != process_no) close(fd); tcp_conn_release(c, 0); return -1; } /* only close the FD if not already in the context of our process either we just connected, or main sent us the FD */ if (c->proc_id != process_no) close(fd); tcp_conn_release(c, 0); return n; } /************** READ related functions ***************/ /* Responsible for reading the request * * if returns >= 0 : the connection will be released * * if returns < 0 : the connection will be released as BAD / broken */ static int wss_read_req(struct tcp_connection* con, int* bytes_read) { int size; /* we need to fix the SSL connection before doing anything */ if (tls_fix_read_conn(con) < 0) { LM_ERR("cannot fix read connection\n"); goto error; } if (WS_STATE(con) != WS_CON_HANDSHAKE_DONE) { size = ws_server_handshake(con); if (size < 0) { LM_ERR("cannot complete WebSocket handshake\n"); goto error; } if (size == 0) goto done; } if (WS_STATE(con) == WS_CON_HANDSHAKE_DONE && ws_process(con) < 0) goto error; done: return 0; error: /* connection will be released as ERROR */ return -1; } static int wss_raw_writev(struct tcp_connection *c, int fd, const struct iovec *iov, int iovcnt, int tout) { int i, n, ret = 0; #ifdef TLS_DONT_WRITE_FRAGMENTS static char *buf = NULL; #endif #ifndef TLS_DONT_WRITE_FRAGMENTS for (i = 0; i < iovcnt; i++) { n = tls_blocking_write(c, fd, iov[i].iov_base, iov[i].iov_len, &tls_mgm_api); if (n < 0) { ret = -1; goto end; } ret += n; } #else n = 0; for (i = 0; i < iovcnt; i++) n += iov[i].iov_len; buf = pkg_realloc(buf, n); if (!buf) { ret = -2; goto end; } n = 0; for (i = 0; i < iovcnt; i++) { memcpy(buf + n, iov[i].iov_base, iov[i].iov_len); n += iov[i].iov_len; } n = tls_blocking_write(c, fd, buf, n, &tls_mgm_api); #endif /* TLS_DONT_WRITE_FRAGMENTS */ end: return ret; } opensips-2.2.2/modules/proto_wss/proto_wss.h000066400000000000000000000017771300170765700213160ustar00rootroot00000000000000/* * Copyright (C) 2016 OpenSIPS Project * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2016-01-xx created (razvanc) */ #ifndef _PROTO_WSS_H_ #define _PROTO_WSS_H_ #define WSS_DEFAULT_PORT 443 /*!< Secure WebSocket default port */ #endif /* _PROTO_WSS_H_ */ opensips-2.2.2/modules/pua/000077500000000000000000000000001300170765700156205ustar00rootroot00000000000000opensips-2.2.2/modules/pua/Makefile000066400000000000000000000010141300170765700172540ustar00rootroot00000000000000# $Id$ # # PUBLISH # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pua.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/pua/README000066400000000000000000000302421300170765700165010ustar00rootroot00000000000000Presence User Agent Module Anca-Maria Vamanu Edited by Anca-Maria Vamanu Edited by Saul Ibarra Corretge Copyright © 2006 Voice Sistem SRL Revision History Revision $Revision: 9371 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. hash_size (int) 1.3.2. db_url (str) 1.3.3. db_table (str) 1.3.4. min_expires (int) 1.3.5. default_expires (int) 1.3.6. update_period (int) 1.4. Exported Functions 1.4.1. pua_update_contact() 1.5. Installation 2. Developer Guide 2.1. bind_pua(pua_api_t* api) 2.2. send_publish 2.3. send_subscribe 2.4. is_dialog 2.5. register_puacb 2.6. add_event List of Examples 1.1. Set hash_size parameter 1.2. Set db_url parameter 1.3. Set db_table parameter 1.4. Set min_expires parameter 1.5. Set default_expires parameter 1.6. Set update_period parameter 1.7. pua_update_contact usage 2.1. pua_api structure 2.2. pua_is_dialog usage example 2.3. register_puacb usage example 2.4. add_event usage example Chapter 1. Admin Guide 1.1. Overview This module offer the functionality of a presence user agent client, sending Subscribe and Publish messages. Now it can be used with the following modules: pua_mi and pua_usrloc, pua_bla and pua_xmpp. The pua_mi offer the possibility to publish any kind of information or subscribing to a resource through fifo. The pua_usrloc module calls a function exported by pua modules to publish elementary presence information, such as basic status "open" or "closed", for clients that do not implement client-to-server presence. Through pua_bla , BRIDGED LINE APPEARANCE features are added to OpenSIPs. The pua_xmpp module represents a gateway between SIP and XMPP, so that jabber and SIP clients can exchange presence information. The module use cache to store presentity list and writes to database on timer to be able to recover upon restart. Notice: This module must not be used in no fork mode (the locking mechanism used may cause deadlock in no fork mode). 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * a database modules. * tm. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml. 1.3. Exported Parameters 1.3.1. hash_size (int) The size of the hash table used for storing Subscribe and Publish information. This parameter will be used as the power of 2 when computing table size. Default value is “9â€. Example 1.1. Set hash_size parameter ... modparam("pua", "hash_size", 11) ... 1.3.2. db_url (str) Database url. Default value is “>mysql://opensips:opensipsrw@localhost/opensipsâ€. Example 1.2. Set db_url parameter ... modparam("pua", "db_url" "dbdriver://username:password@dbhost/dbname") ... 1.3.3. db_table (str) The name of the database table. Default value is “puaâ€. Example 1.3. Set db_table parameter ... modparam("pua", "db_table", "pua") ... 1.3.4. min_expires (int) The inferior expires limit for both Publish and Subscribe. Default value is “300â€. Example 1.4. Set min_expires parameter ... modparam("pua", "min_expires", 0) ... 1.3.5. default_expires (int) The default expires value used in case this information is not provisioned. Default value is “3600â€. Example 1.5. Set default_expires parameter ... modparam("pua", "default_expires", 3600) ... 1.3.6. update_period (int) The interval at which the information in database and hash table should be updated. In the case of the hash table updating is deleting expired messages. Default value is “30â€. Example 1.6. Set update_period parameter ... modparam("pua", "update_period", 100) ... 1.4. Exported Functions 1.4.1. pua_update_contact() The remote target can be updated by the Contact of a subsequent in dialog request. In the PUA watcher case (sending a SUBSCRIBE messages), this means that the remote target for the following Subscribe messages can be updated at any time by the contact of a Notify message. If this function is called on request route on receiving a Notify message, it will try to update the stored remote target. This function can be used from REQUEST_ROUTE. Return code: * 1 - if success. * -1 - if error. Example 1.7. pua_update_contact usage ... if(method=="NOTIFY") pua_update_contact(); ... 1.5. Installation The module requires 1 table in OpenSIPS database: pua. The SQL syntax to create it can be found in presence_xml-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. Chapter 2. Developer Guide The module provides the following functions that can be used in other OpenSIPS modules. 2.1. bind_pua(pua_api_t* api) This function binds the pua modules and fills the structure with the two exported function. Example 2.1. pua_api structure ... typedef struct pua_api { send_subscribe_t send_subscribe; send_publish_t send_publish; query_dialog_t is_dialog; register_puacb_t register_puacb; add_pua_event_t add_event; } pua_api_t; ... 2.2. send_publish Field type: ... typedef int (*send_publish_t)(publ_info_t* publ); ... This function receives as a parameter a structure with Publish required information and sends a Publish message. The structure received as a parameter: ... typedef struct publ_info str id; /* (optional )a value unique for one combination of pres_uri and flag */ str* pres_uri; /* the presentity uri */ str* body; /* the body of the Publish message; can be NULL in case of an update expires*/ int expires; /* the expires value that will be used in Publish Expires header*/ int flag; /* it can be : INSERT_TYPE or UPDATE_TYPE if missing it will be established according to the result of the search in hash table*/ int source_flag; /* flag identifying the resource ; supported values: UL_PUBLISH, MI_PUBLISH, BLA_PUBLISH, XMPP_PUBLISH*/ int event; /* the event flag; supported values: PRESENCE_EVENT, BLA_EVENT, MWI_EVENT */ str content_type; /* the content_type of the body if present (optional if the same as the default value for that event)*/ str* etag; /* (optional) the value of the etag the request should match */ str* extra_headers /* (optional) extra_headers that should be added to Publish msg*/ publrpl_cb_t* cbrpl;/* callback function to be called when receiving the reply for the sent request */ void* cbparam; /* extra parameter for tha callback function */ str outbound_proxy; /* the outbound proxy to be used when sending the Publish requ est*/ }publ_info_t; ... The callback function type: ... typedef int (publrpl_cb_t)(struct sip_msg* reply, void* extra_param); ... 2.3. send_subscribe Field type: ... typedef int (*send_subscribe_t)(subs_info_t* subs); ... This function receives as a parameter a structure with Subscribe required information and sends a Subscribe message. The structure received as a parameter: ... typedef struct subs_info str id; /* an id value unique for one combination of pres_uri and flag */ str* pres_uri; /* the presentity uri */ str* watcher_uri; /* the watcher uri */ str* contact; /* the uri that will be used in Contact header*/ str* remote_target; /* the uri that will be used as R-URI for the Subscribe message(not compulsory; if not set the value of the pres_uri field is used) */ str* outbound_proxy; /* the outbound_proxy to use when sending the Subscribe request*/ int event; /* the event flag; supported value: PRESENCE_EVENT, BLA_EVENT, PWINFO_EVENT*/ int expires; /* the expires value that will be used in Subscribe Expires header */ int flag; /* it can be : INSERT_TYPE or UPDATE_TYPE not compulsory */ int source_flag; /* flag identifying the resource ; supported values: MI_SUBSCRIBE, BLA_SUBSCRIBE, XMPP_SUBSCRIBE, XMPP_INITIAL_SUBS */ }subs_info_t; ... 2.4. is_dialog Field type: ... typedef int (*query_dialog_t)(ua_pres_t* presentity); ... This function checks is the parameter corresponds to a stored Subscribe initiated dialog. Example 2.2. pua_is_dialog usage example ... if(pua_is_dialog(dialog) < 0) { LM_ERR("querying dialog\n"); goto error; } ... 2.5. register_puacb Field type: ... typedef int (*register_puacb_t)(int types, pua_cb f, void* param ); ... This function registers a callback to be called on receiving the reply message for a sent Subscribe request. The type parameter should be set the same as the source_flag for that request. The function registered as callback for pua should be of type pua_cb , which is: typedef void (pua_cb)(ua_pres_t* hentity, struct msg_start * fl); The parameters are the dialog structure for that request and the first line of the reply message. Example 2.3. register_puacb usage example ... if(pua.register_puacb(XMPP_SUBSCRIBE, Sipreply2Xmpp, NULL) & 0) { LM_ERR("Could not register callback\n"); return -1; } ... 2.6. add_event Field type: ... typedef int (*add_pua_event_t)(int ev_flag, char* name, char* content_type,evs_process_body_t* process_body); - ev_flag : an event flag defined as a macro in pua module - name : the event name to be used in Event request headers - content_type: the default content_type for Publish body for that event (NULL if winfo event) - process_body: function that processes the received body before using it to construct the PUBLISH request (NULL if winfo event) ... This function allows registering new events to the pua module. Now there are 4 events supported by the pua module: presence, presence;winfo, message-summary, dialog;sla. These events are registered from within the pua module. Filed type for process_body: ... typedef int (evs_process_body_t)(struct publ_info* publ, str** final_body, int ver, str* tuple); - publ : the structure received as a parameter in send_publish function ( initial body found in publ->body) - final_body: the pointer where the result(final_body) should be stored - ver : a counter for the sent Publish requests (used for winfo events) - tuple : a unique identifier for the resource; if an initial Publish it should be returned as a result and it will be stored for that record, otherwise it will be given as a parameter; ... Example 2.4. add_event usage example ... if(pua.add_event((PRESENCE_EVENT, "presence", "application/pidf+ xml", pres_process_body) & 0) { LM_ERR("Could not register new event\n"); return -1; } ... opensips-2.2.2/modules/pua/add_events.c000066400000000000000000000071241300170765700201040ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * initial version 2007-05-03 (anca) */ #include #include #include #include #include "../../str.h" #include "event_list.h" #include "add_events.h" #include "pua.h" #include "pidf.h" int pua_add_events(void) { /* add presence */ if(add_pua_event(PRESENCE_EVENT, "presence", "application/pidf+xml", pres_process_body)< 0) { LM_ERR("while adding event presence\n"); return -1; } /* add dialog;sla */ if(add_pua_event(BLA_EVENT, "dialog;sla", "application/dialog-info+xml", 0)< 0) { LM_ERR("while adding event presence\n"); return -1; } /* add message-summary*/ if(add_pua_event(MSGSUM_EVENT, "message-summary", "application/simple-message-summary", 0)< 0) { LM_ERR("while adding event presence\n"); return -1; } /* add presence;winfo */ if(add_pua_event(PWINFO_EVENT, "presence.winfo", 0, 0)< 0) { LM_ERR("while adding event presence\n"); return -1; } return 0; } int pres_process_body(publ_info_t* publ, str** fin_body, int ver, str* tuple) { xmlDocPtr doc= NULL; xmlNodePtr node= NULL; char* tuple_id= NULL, *person_id= NULL; static char buf[128]; str* body= NULL; doc= xmlParseMemory(publ->body->s, publ->body->len ); if(doc== NULL) { LM_ERR("while parsing xml memory\n"); goto error; } node= xmlDocGetNodeByName(doc, "tuple", NULL); if(node == NULL) { LM_ERR("while extracting tuple node\n"); goto error; } tuple_id= xmlNodeGetAttrContentByName(node, "id"); if(tuple_id== NULL) { /* must be null terminated */ if(tuple->s == 0) /* generate a tuple_id */ { tuple->s= buf; tuple->len= sprintf(tuple->s, "%p", publ); } tuple_id = buf; /* add tuple id */ if(!xmlNewProp(node, BAD_CAST "id", BAD_CAST tuple_id)) { LM_ERR("Failed to add xml node attribute\n"); goto error; } } else { if(tuple->s == 0) /* generate a tuple_id */ { tuple->s= buf; tuple->len= sprintf(tuple->s, "%s", tuple_id); } } node= xmlDocGetNodeByName(doc, "person", NULL); if(node) { LM_DBG("found person node\n"); person_id= xmlNodeGetAttrContentByName(node, "id"); if(person_id== NULL) { if(!xmlNewProp(node, BAD_CAST "id", BAD_CAST tuple_id)) { LM_ERR("while extracting xml" " node\n"); goto error; } } else { xmlFree(person_id); } } body= (str*)pkg_malloc(sizeof(str)); if(body== NULL) { LM_ERR("NO more memory left\n"); goto error; } memset(body, 0, sizeof(str)); xmlDocDumpMemory(doc,(xmlChar**)(void*)&body->s, &body->len); if(body->s== NULL || body->len== 0) { LM_ERR("while dumping xml format\n"); goto error; } xmlFreeDoc(doc); doc= NULL; *fin_body= body; xmlMemoryDump(); xmlCleanupParser(); return 1; error: if(doc) xmlFreeDoc(doc); if(body) pkg_free(body); return -1; } opensips-2.2.2/modules/pua/add_events.h000066400000000000000000000026131300170765700201070ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * initial version 2007-05-03 (anca) */ #ifndef _PUA_ADD_EV_H_ #define _PUA_ADD_EV_H_ #include "send_publish.h" #include "../../str.h" /* * should return: 0 if not changed ( fin_body points to publ->body) * 1 if changed ( must be freed) * */ int pua_add_events(void); int pres_process_body(struct publ_info* publ, str** fin_body, int ver, str* tuple); int bla_process_body (struct publ_info* publ, str** fin_body, int ver, str* tuple); int mwi_process_body (struct publ_info* publ, str** fin_body, int ver, str* tuple); #endif opensips-2.2.2/modules/pua/doc/000077500000000000000000000000001300170765700163655ustar00rootroot00000000000000opensips-2.2.2/modules/pua/doc/pua.xml000066400000000000000000000021751300170765700177010ustar00rootroot00000000000000 %docentities; ]> Presence User Agent Module &osipsname; Anca-Maria Vamanu Anca-Maria Vamanu Saul Ibarra Corretge 2006 &voicesystem; $Revision: 9371 $ $Date$ &admin; &devel; &faq; opensips-2.2.2/modules/pua/doc/pua_admin.xml000066400000000000000000000141161300170765700210470ustar00rootroot00000000000000 &adminguide;
Overview This module offer the functionality of a presence user agent client, sending Subscribe and Publish messages. Now it can be used with the following modules: pua_mi and pua_usrloc, pua_bla and pua_xmpp. The pua_mi offer the possibility to publish any kind of information or subscribing to a resource through fifo. The pua_usrloc module calls a function exported by pua modules to publish elementary presence information, such as basic status "open" or "closed", for clients that do not implement client-to-server presence. Through pua_bla , BRIDGED LINE APPEARANCE features are added to OpenSIPs. The pua_xmpp module represents a gateway between SIP and XMPP, so that jabber and SIP clients can exchange presence information. The module use cache to store presentity list and writes to database on timer to be able to recover upon restart. Notice: This module must not be used in no fork mode (the locking mechanism used may cause deadlock in no fork mode).
Dependencies
&osips; Modules The following modules must be loaded before this module: a database modules. tm.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml.
Exported Parameters
<varname>hash_size</varname> (int) The size of the hash table used for storing Subscribe and Publish information. This parameter will be used as the power of 2 when computing table size. Default value is 9. Set <varname>hash_size</varname> parameter ... modparam("pua", "hash_size", 11) ...
<varname>db_url</varname> (str) Database url. Default value is >&defaultdb;. Set <varname>db_url</varname> parameter ... modparam("pua", "db_url" "&exampledb;") ...
<varname>db_table</varname> (str) The name of the database table. Default value is pua. Set <varname>db_table</varname> parameter ... modparam("pua", "db_table", "pua") ...
<varname>min_expires</varname> (int) The inferior expires limit for both Publish and Subscribe. Default value is 300. Set <varname>min_expires</varname> parameter ... modparam("pua", "min_expires", 0) ...
<varname>default_expires</varname> (int) The default expires value used in case this information is not provisioned. Default value is 3600. Set <varname>default_expires</varname> parameter ... modparam("pua", "default_expires", 3600) ...
<varname>update_period</varname> (int) The interval at which the information in database and hash table should be updated. In the case of the hash table updating is deleting expired messages. Default value is 30. Set <varname>update_period</varname> parameter ... modparam("pua", "update_period", 100) ...
Exported Functions
<function moreinfo="none">pua_update_contact()</function> The remote target can be updated by the Contact of a subsequent in dialog request. In the PUA watcher case (sending a SUBSCRIBE messages), this means that the remote target for the following Subscribe messages can be updated at any time by the contact of a Notify message. If this function is called on request route on receiving a Notify message, it will try to update the stored remote target. This function can be used from REQUEST_ROUTE. Return code: 1 - if success. -1 - if error. <function>pua_update_contact</function> usage ... if(method=="NOTIFY") pua_update_contact(); ...
Installation The module requires 1 table in OpenSIPS database: pua. The SQL syntax to create it can be found in presence_xml-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, &osipsdbdocslink;.
opensips-2.2.2/modules/pua/doc/pua_devel.xml000066400000000000000000000211131300170765700210510ustar00rootroot00000000000000 &develguide; The module provides the following functions that can be used in other &osips; modules.
<function moreinfo="none">bind_pua(pua_api_t* api)</function> This function binds the pua modules and fills the structure with the two exported function. <function>pua_api</function> structure ... typedef struct pua_api { send_subscribe_t send_subscribe; send_publish_t send_publish; query_dialog_t is_dialog; register_puacb_t register_puacb; add_pua_event_t add_event; } pua_api_t; ...
<function moreinfo="none">send_publish</function> Field type: ... typedef int (*send_publish_t)(publ_info_t* publ); ... This function receives as a parameter a structure with Publish required information and sends a Publish message. The structure received as a parameter: ... typedef struct publ_info str id; /* (optional )a value unique for one combination of pres_uri and flag */ str* pres_uri; /* the presentity uri */ str* body; /* the body of the Publish message; can be NULL in case of an update expires*/ int expires; /* the expires value that will be used in Publish Expires header*/ int flag; /* it can be : INSERT_TYPE or UPDATE_TYPE if missing it will be established according to the result of the search in hash table*/ int source_flag; /* flag identifying the resource ; supported values: UL_PUBLISH, MI_PUBLISH, BLA_PUBLISH, XMPP_PUBLISH*/ int event; /* the event flag; supported values: PRESENCE_EVENT, BLA_EVENT, MWI_EVENT */ str content_type; /* the content_type of the body if present (optional if the same as the default value for that event)*/ str* etag; /* (optional) the value of the etag the request should match */ str* extra_headers /* (optional) extra_headers that should be added to Publish msg*/ publrpl_cb_t* cbrpl;/* callback function to be called when receiving the reply for the sent request */ void* cbparam; /* extra parameter for tha callback function */ str outbound_proxy; /* the outbound proxy to be used when sending the Publish request*/ }publ_info_t; ... The callback function type: ... typedef int (publrpl_cb_t)(struct sip_msg* reply, void* extra_param); ...
<function moreinfo="none">send_subscribe</function> Field type: ... typedef int (*send_subscribe_t)(subs_info_t* subs); ... This function receives as a parameter a structure with Subscribe required information and sends a Subscribe message. The structure received as a parameter: ... typedef struct subs_info str id; /* an id value unique for one combination of pres_uri and flag */ str* pres_uri; /* the presentity uri */ str* watcher_uri; /* the watcher uri */ str* contact; /* the uri that will be used in Contact header*/ str* remote_target; /* the uri that will be used as R-URI for the Subscribe message(not compulsory; if not set the value of the pres_uri field is used) */ str* outbound_proxy; /* the outbound_proxy to use when sending the Subscribe request*/ int event; /* the event flag; supported value: PRESENCE_EVENT, BLA_EVENT, PWINFO_EVENT*/ int expires; /* the expires value that will be used in Subscribe Expires header */ int flag; /* it can be : INSERT_TYPE or UPDATE_TYPE not compulsory */ int source_flag; /* flag identifying the resource ; supported values: MI_SUBSCRIBE, BLA_SUBSCRIBE, XMPP_SUBSCRIBE, XMPP_INITIAL_SUBS */ }subs_info_t; ...
<function moreinfo="none">is_dialog</function> Field type: ... typedef int (*query_dialog_t)(ua_pres_t* presentity); ... This function checks is the parameter corresponds to a stored Subscribe initiated dialog. <function>pua_is_dialog </function>usage example ... if(pua_is_dialog(dialog) < 0) { LM_ERR("querying dialog\n"); goto error; } ...
<function moreinfo="none">register_puacb</function> Field type: ... typedef int (*register_puacb_t)(int types, pua_cb f, void* param ); ... This function registers a callback to be called on receiving the reply message for a sent Subscribe request. The type parameter should be set the same as the source_flag for that request. The function registered as callback for pua should be of type pua_cb , which is: typedef void (pua_cb)(ua_pres_t* hentity, struct msg_start * fl); The parameters are the dialog structure for that request and the first line of the reply message. <function>register_puacb </function>usage example ... if(pua.register_puacb(XMPP_SUBSCRIBE, Sipreply2Xmpp, NULL) & 0) { LM_ERR("Could not register callback\n"); return -1; } ...
<function moreinfo="none">add_event</function> Field type: ... typedef int (*add_pua_event_t)(int ev_flag, char* name, char* content_type,evs_process_body_t* process_body); - ev_flag : an event flag defined as a macro in pua module - name : the event name to be used in Event request headers - content_type: the default content_type for Publish body for that event (NULL if winfo event) - process_body: function that processes the received body before using it to construct the PUBLISH request (NULL if winfo event) ... This function allows registering new events to the pua module. Now there are 4 events supported by the pua module: presence, presence;winfo, message-summary, dialog;sla. These events are registered from within the pua module. Filed type for process_body: ... typedef int (evs_process_body_t)(struct publ_info* publ, str** final_body, int ver, str* tuple); - publ : the structure received as a parameter in send_publish function ( initial body found in publ->body) - final_body: the pointer where the result(final_body) should be stored - ver : a counter for the sent Publish requests (used for winfo events) - tuple : a unique identifier for the resource; if an initial Publish it should be returned as a result and it will be stored for that record, otherwise it will be given as a parameter; ... <function>add_event </function>usage example ... if(pua.add_event((PRESENCE_EVENT, "presence", "application/pidf+xml", pres_process_body) & 0) { LM_ERR("Could not register new event\n"); return -1; } ...
opensips-2.2.2/modules/pua/event_list.c000066400000000000000000000061021300170765700201370ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * initial version 2007-05-03 (anca) */ #include #include #include #include "../../str.h" #include "send_publish.h" #include "../../mem/shm_mem.h" #include "event_list.h" pua_event_t* init_pua_evlist(void) { pua_event_t* list= NULL; list= (pua_event_t*)shm_malloc(sizeof(pua_event_t)); if(list== NULL) { LM_ERR("no more share memory\n"); return NULL; } list->next= NULL; return list; } int add_pua_event(int ev_flag, char* name, char* content_type, evs_process_body_t* process_body) { pua_event_t* event= NULL; int size; int name_len; int ctype_len= 0; str str_name; if(pua_evlist == NULL) { LM_ERR("ERROR: 'pua' module must be loaded before this module\n"); return -1; } name_len= strlen(name); str_name.s= name; str_name.len= name_len; if(contains_pua_event(&str_name)) { LM_DBG("Event already exists\n"); return 0; } if(content_type) ctype_len= strlen(content_type); size= sizeof(pua_event_t)+ (name_len+ ctype_len); event= (pua_event_t*)shm_malloc(size); if(event== NULL) { LM_ERR("No more share memory\n"); return -1; } memset(event, 0, size); size= sizeof(pua_event_t); event->name.s= (char*)event+ size; memcpy(event->name.s, name, name_len); event->name.len= name_len; size+= name_len; if(content_type) { event->content_type.s= (char*)event+ size; memcpy(event->content_type.s, content_type, ctype_len); event->content_type.len= ctype_len; size+= ctype_len; } event->process_body= process_body; event->ev_flag= ev_flag; event->next= pua_evlist->next; pua_evlist->next= event; return 0; } pua_event_t* contains_pua_event(str* name) { pua_event_t* event; event= pua_evlist->next; while(event) { if(event->name.len== name->len && strncmp(event->name.s, name->s, name->len)== 0) { return event; } event= event->next; } return NULL; } pua_event_t* get_event(int ev_flag) { pua_event_t* event; event= pua_evlist->next; while(event) { if(event->ev_flag== ev_flag) { return event; } event= event->next; } return NULL; } void destroy_pua_evlist(void) { pua_event_t* e1, *e2; if(pua_evlist) { e1= pua_evlist->next; while(e1) { e2= e1->next; shm_free(e1); e1= e2; } shm_free(pua_evlist); } } opensips-2.2.2/modules/pua/event_list.h000066400000000000000000000032711300170765700201500ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * initial version 2007-05-03 (anca) */ #ifndef _PUA_EVLIST_H_ #define _PUA_EVLIST_H_ #include "../../str.h" //#include "send_publish.h" struct publ_info; typedef int (evs_process_body_t)(struct publ_info* , str** final_body, int ver, str* tuple); typedef struct pua_event { int ev_flag; str name; str content_type; /* default content type for that event*/ evs_process_body_t* process_body; struct pua_event* next; }pua_event_t; extern pua_event_t* pua_evlist; pua_event_t* init_pua_evlist(void); int add_pua_event(int ev_flag, char* name, char* content_type, evs_process_body_t* process_body); typedef int (*add_pua_event_t)(int ev_flag, char* name, char* content_type, evs_process_body_t* process_body); pua_event_t* contains_pua_event(str* name); pua_event_t* get_event(int ev_flag); void destroy_pua_evlist(void); #endif opensips-2.2.2/modules/pua/hash.c000066400000000000000000000475721300170765700167260ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../hash_func.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../db/db.h" #include "hash.h" #include "pua.h" #include "send_publish.h" #include "../presence/hash.h" /* database colums */ static str str_pres_uri_col = str_init("pres_uri"); static str str_etag_col = str_init("etag"); static str str_pres_id_col = str_init("pres_id"); static str str_flag_col= str_init("flag"); static str str_watcher_uri_col= str_init("watcher_uri"); static str str_event_col= str_init("event"); static str str_remote_contact_col= str_init("remote_contact"); void print_ua_pres(ua_pres_t* p) { int now = (int)time(NULL); LM_DBG("p=[%p] pres_uri=[%.*s]\n", p, p->pres_uri->len, p->pres_uri->s); if(p->watcher_uri) { LM_DBG("watcher_uri=[%.*s]\n", p->watcher_uri->len, p->watcher_uri->s); LM_DBG("to_uri=[%.*s]\n", p->to_uri.len, p->to_uri.s); LM_DBG("call_id=[%.*s]\n", p->call_id.len, p->call_id.s); LM_DBG("from_tag=[%.*s]\n", p->from_tag.len, p->from_tag.s); LM_DBG("to_tag=[%.*s]\n", p->to_tag.len, p->to_tag.s); LM_DBG("etag=[%.*s]\n", p->etag.len, p->etag.s); } else { if(p->id.s) LM_DBG("etag=[%.*s] id=[%.*s]\n", p->etag.len, p->etag.s, p->id.len, p->id.s); else LM_DBG("etag=[%.*s]\n", p->etag.len, p->etag.s); } LM_DBG("flag=[%d] event=[%d]\n", p->flag, p->event); if (p->extra_headers.s && p->extra_headers.len) LM_DBG("extra_headers=[%.*s]\n", p->extra_headers.len, p->extra_headers.s); if(p->expires > now) LM_DBG("countdown=[%d] expires=[%d] desired_expires=[%d]\n", p->expires - now, p->expires, p->desired_expires); else LM_DBG("expires=[%d] desired_expires=[%d]\n", p->expires, p->desired_expires); } htable_t* new_htable(void) { htable_t* H= NULL; int i= 0, j; H= (htable_t*)shm_malloc(sizeof(htable_t)); if(H== NULL) { LM_ERR("No more memory\n"); return NULL; } memset(H, 0, sizeof(htable_t)); H->p_records= (hash_entry_t*)shm_malloc(HASH_SIZE* sizeof(hash_entry_t)); if(H->p_records== NULL) { LM_ERR("No more share memory\n"); goto error; } for(i=0; ip_records[i].lock)== 0) { LM_CRIT("initializing lock [%d]\n", i); goto error; } H->p_records[i].entity= (ua_pres_t*)shm_malloc(sizeof(ua_pres_t)); if(H->p_records[i].entity== NULL) { LM_ERR("No more share memory\n"); goto error; } H->p_records[i].entity->next= NULL; } return H; error: if(H->p_records) { for(j=0; j< i; j++) { if(H->p_records[j].entity) shm_free(H->p_records[j].entity); lock_destroy(&H->p_records[j].lock); } shm_free(H->p_records); } shm_free(H); return NULL; } ua_pres_t* search_htable(ua_pres_t* pres, unsigned int hash_code) { ua_pres_t* p= NULL,* L= NULL; L= HashT->p_records[hash_code].entity; LM_DBG("core_hash= %u\n", hash_code); LM_DBG("Searched:\n"); print_ua_pres(pres); LM_DBG("\n"); for(p= L->next; p; p=p->next) { LM_DBG("Found\n"); print_ua_pres(p); LM_DBG("\n"); if((p->flag & pres->flag) && (p->event & pres->event)) { if((p->pres_uri->len==pres->pres_uri->len) && (strncmp(p->pres_uri->s, pres->pres_uri->s,pres->pres_uri->len)==0)) { if(pres->id.s && pres->id.len) { if(!(pres->id.len== p->id.len && strncmp(p->id.s, pres->id.s,pres->id.len)==0)) continue; } if(pres->watcher_uri) { if(p->watcher_uri->len==pres->watcher_uri->len && (strncmp(p->watcher_uri->s, pres->watcher_uri->s, pres->watcher_uri->len )==0)) { /* if to_uri defined check it also */ if(pres->to_uri.s) { if(pres->to_uri.len == p->to_uri.len && strncmp(pres->to_uri.s, p->to_uri.s, p->to_uri.len) == 0) break; } else break; } } else { if(pres->etag.s) { if(pres->etag.len== p->etag.len && strncmp(p->etag.s, pres->etag.s,pres->etag.len)==0) break; } else { LM_DBG("no etag restriction\n"); break; } } } } } if (p && p->expires < (int)time(NULL) && !(p->expires==0 && p->waiting_reply && p->etag.len==0) ) /* presentities with expires=0, waiting for reply and no etag are newly added * presentities which were not yet confirmed (no reply received for first PUBLISH) * and we should find such records ! -bogdan */ return NULL; LM_DBG("got presentity [%p]\n", p); return p; } ua_pres_t* get_htable_safe(unsigned int hash_index, unsigned int local_index) { ua_pres_t* p; for(p= HashT->p_records[hash_index].entity->next; p; p=p->next) { if(p->local_index == local_index) break; } return p; } int update_htable(unsigned int hash_index, unsigned int local_index, int expires, str* etag, str* contact) { ua_pres_t* p; lock_get(&HashT->p_records[hash_index].lock); p = get_htable_safe(hash_index, local_index); if(p == NULL) { LM_ERR("Record not found\n"); goto error; } if(etag) { if(p->etag.s) shm_free(p->etag.s); p->etag.s= (char*)shm_malloc(etag->len); if(p->etag.s == NULL) { LM_ERR("No more shared memory\n"); goto error; } memcpy(p->etag.s, etag->s, etag->len); p->etag.len= etag->len; } p->expires= expires+ (int)time(NULL); if(p->db_flag == NO_UPDATEDB_FLAG) p->db_flag= UPDATEDB_FLAG; if(contact) { if(!(p->remote_contact.len== contact->len && strncmp(p->remote_contact.s, contact->s, contact->len)==0)) { /* update remote contact */ shm_free(p->remote_contact.s); p->remote_contact.s= (char*)shm_malloc(contact->len); if(p->remote_contact.s== NULL) { LM_ERR("no more shared memory\n"); goto error; } memcpy(p->remote_contact.s, contact->s, contact->len); p->remote_contact.len= contact->len; } } lock_release(&HashT->p_records[hash_index].lock); return 0; error: lock_release(&HashT->p_records[hash_index].lock); return -1; } int find_htable(unsigned int hash_index, unsigned int local_index) { ua_pres_t* p; lock_get(&HashT->p_records[hash_index].lock); p = get_htable_safe(hash_index, local_index); lock_release(&HashT->p_records[hash_index].lock); if(p == NULL) return 0; return 1; } ua_pres_t* new_ua_pres(publ_info_t* publ, str* tuple_id) { unsigned int size; ua_pres_t* presentity; size= sizeof(ua_pres_t) + sizeof(str)+ publ->pres_uri->len+ publ->id.len; if(publ->outbound_proxy.s) size+= sizeof(str)+ publ->outbound_proxy.len; if(tuple_id->s) size+= tuple_id->len; presentity= (ua_pres_t*)shm_malloc(size); if(presentity== NULL) { LM_ERR("no more share memory\n"); goto error; } memset(presentity, 0, size); size= sizeof(ua_pres_t); presentity->pres_uri= (str*)((char*)presentity+ size); size+= sizeof(str); presentity->pres_uri->s= (char*)presentity+ size; memcpy(presentity->pres_uri->s, publ->pres_uri->s, publ->pres_uri->len); presentity->pres_uri->len= publ->pres_uri->len; size+= publ->pres_uri->len; // presentity->id.s=(char*)presentity+ size; CONT_COPY(presentity, presentity->id, publ->id); if(publ->extra_headers && publ->extra_headers->s && publ->extra_headers->len) { presentity->extra_headers.s = (char*)shm_malloc(publ->extra_headers->len); if(presentity->extra_headers.s == NULL) { LM_ERR("No more shared memory\n"); goto error; } memcpy(presentity->extra_headers.s, publ->extra_headers->s, publ->extra_headers->len); presentity->extra_headers.len = publ->extra_headers->len; } if(publ->outbound_proxy.s) { presentity->outbound_proxy= (str*)((char*)presentity+ size); size+= sizeof(str); presentity->outbound_proxy->s= (char*)presentity+ size; memcpy(presentity->outbound_proxy->s, publ->outbound_proxy.s, publ->outbound_proxy.len); presentity->outbound_proxy->len= publ->outbound_proxy.len; size+= publ->outbound_proxy.len; } presentity->desired_expires= publ->expires + (int)time(NULL); presentity->flag = publ->source_flag; presentity->event = publ->event; presentity->cb_param = publ->cb_param; presentity->waiting_reply = 1; return presentity; error: if (presentity) shm_free(presentity); return NULL; } /* insert in front; so when searching the most recent result is returned*/ unsigned long new_publ_record(publ_info_t* publ, pua_event_t* ev, str* tuple_id) { ua_pres_t* presentity; presentity = new_ua_pres(publ, tuple_id); if(presentity == NULL) { LM_ERR("Failed to construct new publish record\n"); return -1; } LM_DBG("cb_param = %p\n", publ->cb_param); return insert_htable(presentity); } unsigned long insert_htable(ua_pres_t* presentity) { unsigned int hash_code; str* s1; unsigned long pres_id; ua_pres_t* p; if(presentity->to_uri.s) s1 = &presentity->to_uri; else s1 = presentity->pres_uri; LM_DBG("to_uri= %.*s, watcher_uri= %.*s\n", s1->len, s1->s, (presentity->watcher_uri?presentity->watcher_uri->len:0), (presentity->watcher_uri?presentity->watcher_uri->s:0)); hash_code= core_hash(s1, presentity->watcher_uri, HASH_SIZE); presentity->hash_index = hash_code; LM_DBG("hash_code = %d\n", hash_code); lock_get(&HashT->p_records[hash_code].lock); p= HashT->p_records[hash_code].entity; presentity->db_flag= INSERTDB_FLAG; presentity->next= p->next; if(p->next) { presentity->local_index = p->next->local_index + 1; } else presentity->local_index = 0; p->next= presentity; pres_id = PRES_HASH_ID(presentity); lock_release(&HashT->p_records[hash_code].lock); return pres_id; } static void pua_db_delete(ua_pres_t* pres) { db_key_t cols[6]; db_val_t vals[6]; int n_query_cols= 0; cols[n_query_cols] = &str_pres_uri_col; vals[n_query_cols].type = DB_STR; vals[n_query_cols].nul = 0; vals[n_query_cols].val.str_val = *pres->pres_uri; n_query_cols++; cols[n_query_cols] = &str_event_col; vals[n_query_cols].type = DB_INT; vals[n_query_cols].nul = 0; vals[n_query_cols].val.int_val = pres->event; n_query_cols++; if(pres->flag) { cols[n_query_cols] = &str_flag_col; vals[n_query_cols].type = DB_INT; vals[n_query_cols].nul = 0; vals[n_query_cols].val.int_val = pres->flag; n_query_cols++; } if(pres->id.s && pres->id.len) { cols[n_query_cols] = &str_pres_id_col; vals[n_query_cols].type = DB_STR; vals[n_query_cols].nul = 0; vals[n_query_cols].val.str_val = pres->id; n_query_cols++; } if(pres->watcher_uri) { cols[n_query_cols] = &str_watcher_uri_col; vals[n_query_cols].type = DB_STR; vals[n_query_cols].nul = 0; vals[n_query_cols].val.str_val = *pres->watcher_uri; n_query_cols++; if(pres->remote_contact.s) { cols[n_query_cols] = &str_remote_contact_col; vals[n_query_cols].type = DB_STR; vals[n_query_cols].nul = 0; vals[n_query_cols].val.str_val = pres->remote_contact; n_query_cols++; } } else { if(pres->etag.s) { cols[n_query_cols] = &str_etag_col; vals[n_query_cols].type = DB_STR; vals[n_query_cols].nul = 0; vals[n_query_cols].val.str_val = pres->etag; n_query_cols++; } } /* should not search after etag because I don't know if it has been updated */ if(pua_dbf.use_table(pua_db, &db_table)< 0) { LM_ERR("in use table\n"); return; } if(pua_dbf.delete(pua_db, cols, 0, vals, n_query_cols)< 0) { LM_ERR("Sql delete failed\n"); return; } } void free_htable_entry(ua_pres_t* p) { /* delete from database also */ pua_db_delete(p); if(p->etag.s) shm_free(p->etag.s); if(p->remote_contact.s) shm_free(p->remote_contact.s); if(p->extra_headers.s) shm_free(p->extra_headers.s); shm_free(p); } void delete_htable_safe(ua_pres_t* p, unsigned int hash_index) { ua_pres_t *q= NULL; q = HashT->p_records[hash_index].entity; while(q && q->next!=p) q = q->next; if(q) q->next = p->next; free_htable_entry(p); } void delete_htable(unsigned int hash_index, unsigned int local_index) { ua_pres_t* p= NULL, *q= NULL; lock_get(&HashT->p_records[hash_index].lock); q = HashT->p_records[hash_index].entity; for(p= q->next; p; p=p->next) { if(p->local_index == local_index) { q->next = p->next; free_htable_entry(p); break; } q = p; } lock_release(&HashT->p_records[hash_index].lock); } void destroy_htable(void) { ua_pres_t* p= NULL,*q= NULL; int i; for(i=0; ip_records[i].lock); p=HashT->p_records[i].entity; while(p->next) { q=p->next; p->next=q->next; if(q->etag.s) shm_free(q->etag.s); else if(q->remote_contact.s) shm_free(q->remote_contact.s); if(q->extra_headers.s) shm_free(q->extra_headers.s); shm_free(q); q= NULL; } shm_free(p); } shm_free(HashT->p_records); shm_free(HashT); return; } /* must lock the record line before calling this function*/ ua_pres_t* get_dialog(ua_pres_t* dialog, unsigned int hash_code) { ua_pres_t* p= NULL, *L; LM_DBG("core_hash= %u\n", hash_code); L= HashT->p_records[hash_code].entity; for(p= L->next; p; p=p->next) { if(p->flag& dialog->flag) { LM_DBG("pres_uri= %.*s\twatcher_uri=%.*s\n\t" "callid= %.*s\tto_tag= %.*s\tfrom_tag= %.*s\n", p->pres_uri->len, p->pres_uri->s, p->watcher_uri->len, p->watcher_uri->s,p->call_id.len, p->call_id.s, p->to_tag.len, p->to_tag.s, p->from_tag.len, p->from_tag.s); LM_DBG("searched to_tag= %.*s\tfrom_tag= %.*s\n", p->to_tag.len, p->to_tag.s, p->from_tag.len, p->from_tag.s); if((p->watcher_uri->len== dialog->watcher_uri->len) && (strncmp(p->watcher_uri->s,dialog->watcher_uri->s,p->watcher_uri->len )==0)&& (strncmp(p->call_id.s, dialog->call_id.s, p->call_id.len)== 0) && (strncmp(p->to_tag.s, dialog->to_tag.s, p->to_tag.len)== 0) && (strncmp(p->from_tag.s, dialog->from_tag.s, p->from_tag.len)== 0) ) { if(p->to_uri.s && dialog->to_uri.s) { if((p->to_uri.len== dialog->to_uri.len) && (strncmp(p->to_uri.s, dialog->to_uri.s,p->to_uri.len)==0)) break; } else break; } } } return p; } int get_record_id(ua_pres_t* dialog, str** rec_id) { unsigned int hash_code; ua_pres_t* rec; str* id; str* s1; if(dialog->to_uri.s) s1 = &dialog->to_uri; else s1 = dialog->pres_uri; *rec_id= NULL; LM_DBG("to_uri= %.*s, watcher_uri= %.*s\n", s1->len, s1->s, (dialog->watcher_uri?dialog->watcher_uri->len:0), (dialog->watcher_uri?dialog->watcher_uri->s:0)); hash_code= core_hash(s1, dialog->watcher_uri, HASH_SIZE); lock_get(&HashT->p_records[hash_code].lock); LM_DBG("hash_code = %d\n", hash_code); rec= get_dialog(dialog, hash_code); if(rec== NULL) { LM_DBG("Record not found\n"); lock_release(&HashT->p_records[hash_code].lock); return 0; } id= (str*)pkg_malloc(sizeof(str)); if(id== NULL) { LM_ERR("No more memory\n"); lock_release(&HashT->p_records[hash_code].lock); return -1; } id->s= (char*)pkg_malloc(rec->id.len); if(id->s== NULL) { LM_ERR("No more memory\n"); pkg_free(id); lock_release(&HashT->p_records[hash_code].lock); return -1; } memcpy(id->s, rec->id.s, rec->id.len); id->len= rec->id.len; lock_release(&HashT->p_records[hash_code].lock); LM_DBG("rec did= %.*s\n", id->len, id->s); *rec_id= id; return 0; } int is_dialog(ua_pres_t* dialog) { int ret_code= 0; unsigned int hash_code; str* s1; if(dialog->to_uri.s) s1 = &dialog->to_uri; else s1 = dialog->pres_uri; hash_code= core_hash(s1, dialog->watcher_uri, HASH_SIZE); lock_get(&HashT->p_records[hash_code].lock); if(get_dialog(dialog, hash_code)== NULL) ret_code= -1; else ret_code= 0; lock_release(&HashT->p_records[hash_code].lock); return ret_code; } int update_contact(struct sip_msg* msg, char* str1, char* str2) { ua_pres_t* p, hentity; str contact; struct to_body *pto= NULL, *pfrom = NULL; unsigned int hash_code; if ( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("when parsing headers\n"); return -1; } /* find the record */ if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); return -1; } if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); return -1; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); return -1; } } pfrom = (struct to_body*)msg->from->parsed; if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); return -1; } if( msg->to==NULL || msg->to->body.s==NULL) { LM_ERR("cannot parse TO header\n"); return -1; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); return -1; } if( pto->tag_value.s ==NULL || pto->tag_value.len == 0) { LM_ERR("no to tag value present\n"); return -1; } memset( &hentity, 0, sizeof(ua_pres_t)); /* as we have a NOTIFY, we are looking for any SUBSCRIBER-like entity in the hash (we do not know the exact type) - bogdan */ hentity.flag = BLA_SUBSCRIBE | XMPP_SUBSCRIBE | XMPP_INITIAL_SUBS | MI_SUBSCRIBE | RLS_SUBSCRIBE; hentity.watcher_uri= &pto->uri; hentity.to_uri= pfrom->uri; hentity.call_id= msg->callid->body; hentity.to_tag= pto->tag_value; hentity.from_tag= pfrom->tag_value; hash_code= core_hash(&hentity.to_uri,hentity.watcher_uri, HASH_SIZE); /* extract the contact */ if(msg->contact== NULL || msg->contact->body.s== NULL) { LM_ERR("no contact header found in 200 OK reply"); return -1; } contact= msg->contact->body; lock_get(&HashT->p_records[hash_code].lock); p= get_dialog(&hentity, hash_code); if(p== NULL) { lock_release(&HashT->p_records[hash_code].lock); LM_ERR("no record for the dialog found in hash table\n"); return -1; } if(!(p->remote_contact.len== contact.len && strncmp(p->remote_contact.s, contact.s, contact.len)==0)) { /* update remote contact */ shm_free(p->remote_contact.s); p->remote_contact.s= (char*)shm_malloc(contact.len); if(p->remote_contact.s== NULL) { LM_ERR("no more shared memory\n"); lock_release(&HashT->p_records[hash_code].lock); return -1; } memcpy(p->remote_contact.s, contact.s, contact.len); p->remote_contact.len= contact.len; } lock_release(&HashT->p_records[hash_code].lock); return 1; } list_entry_t *get_subs_list(str *did) { int i; str *tmp_str; list_entry_t *list = NULL; for (i = 0; i < HASH_SIZE; i++) { ua_pres_t *dialog; lock_get(&HashT->p_records[i].lock); dialog = HashT->p_records[i].entity; while (dialog != NULL) { if (dialog->id.s != NULL && dialog->id.len > 0 && strncmp(dialog->id.s, did->s, did->len) == 0 && dialog->pres_uri != NULL && dialog->pres_uri->s != NULL && dialog->pres_uri->len > 0) { if ((tmp_str = (str *)pkg_malloc(sizeof(str))) == NULL) { LM_ERR("out of private memory\n"); lock_release(&HashT->p_records[i].lock); goto done; } if ((tmp_str->s = (char *)pkg_malloc(sizeof(char) * dialog->pres_uri->len + 1)) == NULL) { pkg_free(tmp_str); LM_ERR("out of private memory\n"); lock_release(&HashT->p_records[i].lock); goto done; } memcpy(tmp_str->s, dialog->pres_uri->s, dialog->pres_uri->len); tmp_str->len = dialog->pres_uri->len; tmp_str->s[tmp_str->len] = '\0'; list = list_insert(tmp_str, list, NULL); } dialog = dialog->next; } lock_release(&HashT->p_records[i].lock); } done: return list; } opensips-2.2.2/modules/pua/hash.h000066400000000000000000000114541300170765700167210ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PU_HASH_H_ #define _PU_HASH_H_ #include #include #include #include "../../str.h" #include "../../lock_ops.h" #include "../../dprint.h" #include "../../parser/msg_parser.h" #include "event_list.h" #include "uri_list.h" #define PRESENCE_EVENT 1<<0 #define PWINFO_EVENT 1<<1 #define BLA_EVENT 1<<2 #define MSGSUM_EVENT 1<<3 #define CONFERENCE_EVENT 1<<4 #define XCAPDIFF_EVENT 1<<5 #define DIALOG_EVENT 1<<6 #define CALLINFO_EVENT 1<<7 #define UL_PUBLISH 1<<0 #define BLA_PUBLISH 1<<1 #define BLA_SUBSCRIBE 1<<2 #define XMPP_PUBLISH 1<<3 #define XMPP_SUBSCRIBE 1<<4 #define XMPP_INITIAL_SUBS 1<<5 #define MI_PUBLISH 1<<6 #define MI_ASYN_PUBLISH 1<<7 #define MI_SUBSCRIBE 1<<8 #define RLS_SUBSCRIBE 1<<9 #define DIALOG_PUBLISH 1<<10 #define CALLINFO_PUBLISH 1<<11 #define NO_UPDATEDB_FLAG 0 #define UPDATEDB_FLAG 1 #define INSERTDB_FLAG 2 #define MAX_FORWARD 70 #define PRES_HASH_ID(presentity)(HASH_SIZE*presentity->local_index + presentity->hash_index) typedef struct publ { str content_type; str body; str extra_headers; int expires; void* cb_param; struct publ* next; }publ_t; typedef struct ua_pres{ /* common*/ unsigned int hash_index; unsigned int local_index; str id; str* pres_uri; int event; unsigned int expires; unsigned int desired_expires; int flag; int db_flag; struct ua_pres* next; int ua_flag; /* publish */ str etag; str tuple_id; int waiting_reply; publ_t* pending_publ; /* subscribe */ str to_uri; str* watcher_uri; str call_id; str to_tag; str from_tag; int cseq; int version; int watcher_count; str* outbound_proxy; str extra_headers; str record_route; str remote_contact; str contact; void* cb_param; }ua_pres_t; typedef struct hash_entry { ua_pres_t* entity; gen_lock_t lock; }hash_entry_t; typedef struct htable{ hash_entry_t* p_records; }htable_t; htable_t* new_htable(void); ua_pres_t* search_htable(ua_pres_t* pres, unsigned int hash_code); struct publ_info; unsigned long insert_htable(ua_pres_t* presentity); unsigned long new_publ_record(struct publ_info* publ, pua_event_t* ev, str* tuple); int update_htable(unsigned int hash_index, unsigned int local_index, int expires, str* etag, str* contact); int find_htable(unsigned int hash_index, unsigned int local_index); void delete_htable(unsigned int hash_index, unsigned int local_index); ua_pres_t* get_htable_safe(unsigned int hash_index, unsigned int local_index); void destroy_htable(void); int is_dialog(ua_pres_t* dialog); ua_pres_t* get_dialog(ua_pres_t* dialog, unsigned int hash_code); int get_record_id(ua_pres_t* dialog, str** rec_id); typedef int (*get_record_id_t)(ua_pres_t* dialog, str** rec_id); /* for degug */ void print_ua_pres(ua_pres_t* p); typedef int (*query_dialog_t)(ua_pres_t* presentity); static inline int get_event_flag(str* event) { switch (event->len) { case 6: if (strncasecmp(event->s, "dialog", 6) == 0) return DIALOG_EVENT; break; case 8: if (strncasecmp(event->s, "presence", 8) == 0) return PRESENCE_EVENT; break; case 9: if (strncasecmp(event->s, "xcap-diff", 9) == 0) return XCAPDIFF_EVENT; if (strncasecmp(event->s, "call-info", 9) == 0) return CALLINFO_EVENT; break; case 10: if (strncasecmp(event->s, "dialog;sla", 10) == 0) return BLA_EVENT; if (strncmp(event->s, "conference", 10) == 0) return CONFERENCE_EVENT; break; case 14: if (strncasecmp(event->s, "presence;winfo", 14) == 0) return PWINFO_EVENT; break; case 15: if (strncasecmp(event->s, "message-summary", 15) == 0) return MSGSUM_EVENT; } LM_ERR("Unknown event string\n"); return -1; } int update_contact(struct sip_msg* msg, char* str1, char* str2); void delete_htable_safe(ua_pres_t* p, unsigned int hash_index); list_entry_t *get_subs_list(str *did); typedef list_entry_t * (*get_subs_list_t)(str *did); #endif opensips-2.2.2/modules/pua/pidf.c000066400000000000000000000061571300170765700167170ustar00rootroot00000000000000/* * pua module * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-07-03 initial version (anca) */ #include #include #include #include "../../dprint.h" #include "../../sr_module.h" #include "pidf.h" xmlAttrPtr xmlNodeGetAttrByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = node->properties; while (attr) { if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0) return attr; attr = attr->next; } return NULL; } char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = xmlNodeGetAttrByName(node, name); if (attr) return (char*)xmlNodeGetContent(attr->children); else return NULL; } xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name) { xmlNodePtr cur = node->children; while (cur) { if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) return cur; cur = cur->next; } return NULL; } xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns) { xmlNodePtr cur = node; while (cur) { xmlNodePtr match = NULL; if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) { if (!ns || (cur->ns && xmlStrcasecmp(cur->ns->prefix, (unsigned char*)ns) == 0)) return cur; } match = xmlNodeGetNodeByName(cur->children, name, ns); if (match) return match; cur = cur->next; } return NULL; } char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns) { xmlNodePtr node = xmlNodeGetNodeByName(root, name, ns); if (node) return (char*)xmlNodeGetContent(node->children); else return NULL; } xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns) { xmlNodePtr cur = doc->children; return xmlNodeGetNodeByName(cur, name, ns); } char *xmlDocGetNodeContentByName(xmlDocPtr doc, const char *name, const char *ns) { xmlNodePtr node = xmlDocGetNodeByName(doc, name, ns); if (node) return (char*)xmlNodeGetContent(node->children); else return NULL; } int bind_libxml_api(libxml_api_t* api) { if (!api) { LM_ERR("Invalid parameter value\n"); return -1; } api->xmlDocGetNodeByName = xmlDocGetNodeByName; api->xmlNodeGetNodeByName = xmlNodeGetNodeByName; api->xmlNodeGetNodeContentByName = xmlNodeGetNodeContentByName; api->xmlNodeGetAttrContentByName = xmlNodeGetAttrContentByName; return 0; } opensips-2.2.2/modules/pua/pidf.h000066400000000000000000000041401300170765700167120ustar00rootroot00000000000000/* * pua module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-07-03 initial version (anca) */ #ifndef PUA_PIDF_H #define PUA_PIDF_H #include "../../str.h" #include typedef xmlNodePtr (*xmlDocGetNodeByName_t)(xmlDocPtr doc, const char *name, const char *ns); typedef xmlNodePtr (*xmlNodeGetNodeByName_t)(xmlNodePtr node, const char *name, const char *ns); typedef char* (*xmlNodeGetNodeContentByName_t)(xmlNodePtr root, const char *name, const char *ns); typedef char* (*xmlNodeGetAttrContentByName_t)(xmlNodePtr node, const char *name); xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns); typedef struct libxml_api { xmlDocGetNodeByName_t xmlDocGetNodeByName; xmlNodeGetNodeByName_t xmlNodeGetNodeByName; xmlNodeGetNodeContentByName_t xmlNodeGetNodeContentByName; xmlNodeGetAttrContentByName_t xmlNodeGetAttrContentByName; } libxml_api_t; xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns); xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name); char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns); char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name); typedef int (*bind_libxml_t)(libxml_api_t* api); int bind_libxml_api(libxml_api_t* api); #endif /* PUA_PIDF_H */ opensips-2.2.2/modules/pua/pua.c000066400000000000000000000653351300170765700165650ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-11-29 initial version (Anca Vamanu) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../parser/parse_expires.h" #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../mem/mem.h" #include "../../pt.h" #include "../../db/db.h" #include "../tm/tm_load.h" #include "../presence/hash.h" #include "pua.h" #include "send_publish.h" #include "send_subscribe.h" #include "pua_bind.h" #include "pua_callback.h" #include "event_list.h" #include "add_events.h" #include "pidf.h" #define PUA_TABLE_VERSION 8 struct tm_binds tmb; htable_t* HashT= NULL; int HASH_SIZE= -1; extern int bind_pua(pua_api_t* api); int min_expires= 300; int default_expires=3600; static str db_url = {NULL, 0}; str db_table= str_init("pua"); int update_period= 100; pua_event_t* pua_evlist= NULL; /* database connection */ db_con_t *pua_db = NULL; db_func_t pua_dbf; /* database colums */ static str str_pres_uri_col = str_init("pres_uri"); static str str_to_uri_col = str_init("to_uri"); static str str_pres_id_col = str_init("pres_id"); static str str_expires_col= str_init("expires"); static str str_flag_col= str_init("flag"); static str str_etag_col= str_init("etag"); static str str_tuple_id_col= str_init("tuple_id"); static str str_watcher_uri_col= str_init("watcher_uri"); static str str_call_id_col= str_init("call_id"); static str str_to_tag_col= str_init("to_tag"); static str str_from_tag_col= str_init("from_tag"); static str str_cseq_col= str_init("cseq"); static str str_event_col= str_init("event"); static str str_record_route_col= str_init("record_route"); static str str_contact_col= str_init("contact"); static str str_remote_contact_col= str_init("remote_contact"); static str str_extra_headers_col= str_init("extra_headers"); static str str_desired_expires_col= str_init("desired_expires"); static str str_version_col = str_init("version"); /* module functions */ static int mod_init(void); static int child_init(int); static void destroy(void); static int update_pua(ua_pres_t* p, unsigned int hash_code, unsigned int final); static int db_restore(void); static void db_update(unsigned int ticks,void *param); static void hashT_clean(unsigned int ticks,void *param); static cmd_export_t cmds[]= { {"bind_libxml_api", (cmd_function)bind_libxml_api, 1, 0, 0, 0}, {"bind_pua", (cmd_function)bind_pua, 1, 0, 0, 0}, {"pua_update_contact", (cmd_function)update_contact, 0, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[]={ {"hash_size" , INT_PARAM, &HASH_SIZE }, {"db_url" , STR_PARAM, &db_url.s }, {"db_table" , STR_PARAM, &db_table.s }, {"min_expires", INT_PARAM, &min_expires }, {"default_expires", INT_PARAM, &default_expires }, {"update_period", INT_PARAM, &update_period }, {0, 0, 0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "pua", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { load_tm_f load_tm; LM_DBG("...\n"); if(min_expires< 0) min_expires= 0; if(default_expires< 600) default_expires= 3600; /* import the TM auto-loading function */ if((load_tm=(load_tm_f)find_export("load_tm", 0, 0))==NULL) { LM_ERR("can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if(load_tm(&tmb)==-1) { LM_ERR("can't load tm functions\n"); return -1; } init_db_url( db_url , 0 /*cannot be null*/); db_table.len = strlen(db_table.s); /* binding to database module */ if (db_bind_mod(&db_url, &pua_dbf)) { LM_ERR("Database module not found\n"); return -1; } if (!DB_CAPABILITY(pua_dbf, DB_CAP_ALL)) { LM_ERR("Database module does not implement all functions needed" " by the module\n"); return -1; } pua_db = pua_dbf.init(&db_url); if (!pua_db) { LM_ERR("while connecting database\n"); return -1; } /* verify table version */ if(db_check_table_version(&pua_dbf, pua_db, &db_table, PUA_TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); return -1; } if(HASH_SIZE<=1) HASH_SIZE= 512; else HASH_SIZE = 1<n<=0) { LM_INFO("the query returned no result\n"); pua_dbf.free_result(pua_db, res); res = NULL; return 0; } LM_DBG("found %d db entries\n", res->n); do { for(i =0 ; i< res->n ; i++) { row = &res->rows[i]; row_vals = ROW_VALUES(row); if(row_vals[expires_col].val.int_val < time(NULL)) continue; pres_uri.s= (char*)row_vals[puri_col].val.string_val; pres_uri.len = strlen(pres_uri.s); LM_DBG("pres_uri= %.*s\n", pres_uri.len, pres_uri.s); memset(&etag, 0, sizeof(str)); memset(&tuple_id, 0, sizeof(str)); memset(&watcher_uri, 0, sizeof(str)); memset(&to_uri, 0, sizeof(str)); memset(&call_id, 0, sizeof(str)); memset(&to_tag, 0, sizeof(str)); memset(&from_tag, 0, sizeof(str)); memset(&record_route, 0, sizeof(str)); memset(&pres_id, 0, sizeof(str)); memset(&contact, 0, sizeof(str)); memset(&remote_contact, 0, sizeof(str)); memset(&extra_headers, 0, sizeof(str)); pres_id.s= (char*)row_vals[pid_col].val.string_val; if(pres_id.s) pres_id.len = strlen(pres_id.s); if(row_vals[etag_col].val.string_val) { etag.s= (char*)row_vals[etag_col].val.string_val; etag.len = strlen(etag.s); if(row_vals[tuple_col].val.string_val) { tuple_id.s= (char*)row_vals[tuple_col].val.string_val; tuple_id.len = strlen(tuple_id.s); } } if(row_vals[watcher_col].val.string_val) { watcher_uri.s= (char*)row_vals[watcher_col].val.string_val; watcher_uri.len = strlen(watcher_uri.s); to_uri.s= (char*)row_vals[touri_col].val.string_val; if(to_uri.s == NULL) to_uri = pres_uri; else to_uri.len = strlen(to_uri.s); LM_DBG("to_uri= %.*s\n", to_uri.len, to_uri.s); call_id.s= (char*)row_vals[callid_col].val.string_val; call_id.len = strlen(call_id.s); to_tag.s= (char*)row_vals[totag_col].val.string_val; to_tag.len = strlen(to_tag.s); from_tag.s= (char*)row_vals[fromtag_col].val.string_val; from_tag.len = strlen(from_tag.s); if(row_vals[record_route_col].val.string_val) { record_route.s= (char*) row_vals[record_route_col].val.string_val; record_route.len= strlen(record_route.s); } contact.s= (char*)row_vals[contact_col].val.string_val; contact.len = strlen(contact.s); remote_contact.s= (char*)row_vals[remote_contact_col].val.string_val; if(remote_contact.s) remote_contact.len = strlen(remote_contact.s); } extra_headers.s= (char*)row_vals[extra_headers_col].val.string_val; if(extra_headers.s) extra_headers.len= strlen(extra_headers.s); else extra_headers.len= 0; size= sizeof(ua_pres_t)+ sizeof(str)+ (pres_uri.len+ pres_id.len+ tuple_id.len)* sizeof(char); if(watcher_uri.s) size+= sizeof(str)+ to_uri.len + watcher_uri.len+ call_id.len+ to_tag.len+ from_tag.len+ record_route.len+ contact.len; p= (ua_pres_t*)shm_malloc(size); if(p== NULL) { LM_ERR("no more shared memmory"); goto error; } memset(p, 0, size); size= sizeof(ua_pres_t); p->pres_uri= (str*)((char*)p+ size); size+= sizeof(str); p->pres_uri->s= (char*)p + size; memcpy(p->pres_uri->s, pres_uri.s, pres_uri.len); p->pres_uri->len= pres_uri.len; size+= pres_uri.len; if(pres_id.s) { CONT_COPY(p, p->id, pres_id); } if(watcher_uri.s && watcher_uri.len) { p->watcher_uri= (str*)((char*)p+ size); size+= sizeof(str); p->watcher_uri->s= (char*)p+ size; memcpy(p->watcher_uri->s, watcher_uri.s, watcher_uri.len); p->watcher_uri->len= watcher_uri.len; size+= watcher_uri.len; CONT_COPY(p, p->to_uri, to_uri); CONT_COPY(p, p->to_tag, to_tag); CONT_COPY(p, p->from_tag, from_tag); CONT_COPY(p, p->call_id, call_id); if(record_route.s && record_route.len) { CONT_COPY(p, p->record_route, record_route); } CONT_COPY(p, p->contact, contact); p->cseq= row_vals[cseq_col].val.int_val; p->remote_contact.s= (char*)shm_malloc(remote_contact.len); if(p->remote_contact.s== NULL) { LM_ERR("No more shared memory\n"); goto error; } memcpy(p->remote_contact.s, remote_contact.s, remote_contact.len); p->remote_contact.len= remote_contact.len; p->version= row_vals[version_col].val.int_val; } LM_DBG("size= %d\n", size); p->event= row_vals[event_col].val.int_val; p->expires= row_vals[expires_col].val.int_val; p->desired_expires= row_vals[desired_expires_col].val.int_val; p->flag|= row_vals[flag_col].val.int_val; memset(&p->etag, 0, sizeof(str)); if(etag.s && etag.len) { /* alloc separately */ p->etag.s= (char*)shm_malloc(etag.len); if(p->etag.s== NULL) { LM_ERR("no more share memory\n"); goto error; } memcpy(p->etag.s, etag.s, etag.len); p->etag.len= etag.len; } memset(&p->extra_headers, 0, sizeof(str)); if(extra_headers.s && extra_headers.len) { /* alloc separately */ p->extra_headers.s= (char*)shm_malloc(extra_headers.len); if(p->extra_headers.s== NULL) { LM_ERR("no more share memory\n"); goto error; } memcpy(p->extra_headers.s, extra_headers.s, extra_headers.len); p->extra_headers.len= extra_headers.len; } print_ua_pres(p); insert_htable(p); } /* end for(all rows)*/ if (DB_CAPABILITY(pua_dbf, DB_CAP_FETCH)) { if(pua_dbf.fetch_result(pua_db, &res, no_rows)<0) { LM_ERR( "fetching rows (1)\n"); goto error; } } else { break; } } while(RES_ROW_N(res)>0); pua_dbf.free_result(pua_db, res); res = NULL; if(pua_dbf.delete(pua_db, 0, 0 , 0, 0) < 0) { LM_ERR("while deleting information from db\n"); goto error; } return 0; error: if(res) pua_dbf.free_result(pua_db, res); if(p) { if(p->remote_contact.s) shm_free(p->remote_contact.s); if(p->extra_headers.s) shm_free(p->extra_headers.s); if(p->etag.s) shm_free(p->etag.s); shm_free(p); } return -1; } static void hashT_clean(unsigned int ticks,void *param) { int i; time_t now; ua_pres_t* p= NULL, *q= NULL; now = time(NULL); for(i= 0;i< HASH_SIZE; i++) { lock_get(&HashT->p_records[i].lock); p= HashT->p_records[i].entity->next; while(p) { print_ua_pres(p); LM_DBG("---\n"); if(p->expires -update_period < now ) { if((p->desired_expires> p->expires + 5) || (p->desired_expires== 0 )) { LM_DBG("Desired expires greater than expires -> send a " "refresh PUBLISH desired_expires=%d - expires=%d\n", p->desired_expires, p->expires); if(update_pua(p, i, 0)< 0) { LM_ERR("while updating record\n"); lock_release(&HashT->p_records[i].lock); return; } p= p->next; continue; } LM_DBG("Found expired: uri= %.*s\n", p->pres_uri->len, p->pres_uri->s); if(update_pua(p, i, 1)< 0) { LM_ERR("while updating record\n"); } /* delete it */ q = p->next; delete_htable_safe(p, p->hash_index); p = q; } else p= p->next; } lock_release(&HashT->p_records[i].lock); } } int update_pua(ua_pres_t* p, unsigned int hash_code, unsigned int final) { str* str_hdr= NULL; int expires; int result; if(final > 0) { expires= 0; p->desired_expires= 0; } else { if(p->desired_expires== 0) expires= 3600; else expires= p->desired_expires- (int)time(NULL); if(expires < min_expires) expires = min_expires; } if(p->watcher_uri== NULL) { str met= {"PUBLISH", 7}; unsigned long cb_param; pua_event_t* ev; ev = get_event(p->event); if(ev == NULL) { LM_ERR("No event with flag [%d] found\n", p->event); goto error; } str_hdr = publ_build_hdr(expires, ev, NULL, &p->etag, &p->extra_headers, 0); if(str_hdr == NULL) { LM_ERR("while building extra_headers\n"); goto error; } LM_DBG("str_hdr:\n%.*s expires:%d\n ", str_hdr->len, str_hdr->s, expires); cb_param = PRES_HASH_ID(p); result= tmb.t_request(&met, /* Type of the message*/ p->pres_uri, /* Request-URI */ p->pres_uri, /* To */ p->pres_uri, /* From */ str_hdr, /* Optional headers */ 0, /* Message body */ (p->outbound_proxy)?p->outbound_proxy:0,/* Outbound proxy*/ publ_cback_func, /* Callback function */ (void*)cb_param, /* Callback parameter*/ 0 ); } else { str met= {"SUBSCRIBE", 9}; dlg_t* td= NULL; ua_pres_t* cb_param= NULL; td= pua_build_dlg_t(p); if(td== NULL) { LM_ERR("while building tm dlg_t structure"); goto error; } LM_DBG("td->rem_uri= %.*s\n", td->rem_uri.len, td->rem_uri.s); str_hdr= subs_build_hdr(&p->contact, expires,p->event,&p->extra_headers); if(str_hdr== NULL || str_hdr->s== NULL) { LM_ERR("while building extra headers\n"); pkg_free(td); return -1; } cb_param= subs_cbparam_indlg(p, expires, REQ_ME); if(cb_param== NULL) { LM_ERR("while constructing subs callback param\n"); goto error; } result= tmb.t_request_within (&met, str_hdr, 0, td, subs_cback_func, (void*)cb_param, 0 ); if(result< 0) { LM_ERR("in t_request function\n"); shm_free(cb_param); pkg_free(td); goto error; } pkg_free(td); td= NULL; } pkg_free(str_hdr); return 0; error: if(str_hdr) pkg_free(str_hdr); return -1; } static void db_update(unsigned int ticks,void *param) { ua_pres_t* p= NULL; db_key_t q_cols[19]; db_val_t q_vals[19]; db_key_t db_cols[5]; db_val_t db_vals[5]; db_op_t db_ops[1] ; int n_query_cols= 0, n_query_update= 0; int n_update_cols= 0; int i; int puri_col,touri_col,pid_col,expires_col,flag_col,etag_col,tuple_col,event_col; int watcher_col,callid_col,totag_col,fromtag_col,record_route_col,cseq_col; int no_lock= 0, contact_col, desired_expires_col, extra_headers_col; int remote_contact_col, version_col; if(ticks== 0 && param == NULL) no_lock= 1; /* cols and values used for insert */ q_cols[puri_col= n_query_cols] = &str_pres_uri_col; q_vals[puri_col].type = DB_STR; q_vals[puri_col].nul = 0; n_query_cols++; q_cols[pid_col= n_query_cols] = &str_pres_id_col; q_vals[pid_col].type = DB_STR; q_vals[pid_col].nul = 0; n_query_cols++; q_cols[flag_col= n_query_cols] = &str_flag_col; q_vals[flag_col].type = DB_INT; q_vals[flag_col].nul = 0; n_query_cols++; q_cols[event_col= n_query_cols] = &str_event_col; q_vals[event_col].type = DB_INT; q_vals[event_col].nul = 0; n_query_cols++; q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col; q_vals[watcher_col].type = DB_STR; q_vals[watcher_col].nul = 0; n_query_cols++; q_cols[callid_col= n_query_cols] = &str_call_id_col; q_vals[callid_col].type = DB_STR; q_vals[callid_col].nul = 0; n_query_cols++; q_cols[totag_col= n_query_cols] = &str_to_tag_col; q_vals[totag_col].type = DB_STR; q_vals[totag_col].nul = 0; n_query_cols++; q_cols[fromtag_col= n_query_cols] = &str_from_tag_col; q_vals[fromtag_col].type = DB_STR; q_vals[fromtag_col].nul = 0; n_query_cols++; q_cols[etag_col= n_query_cols] = &str_etag_col; q_vals[etag_col].type = DB_STR; q_vals[etag_col].nul = 0; n_query_cols++; q_cols[tuple_col= n_query_cols] = &str_tuple_id_col; q_vals[tuple_col].type = DB_STR; q_vals[tuple_col].nul = 0; n_query_cols++; q_cols[cseq_col= n_query_cols]= &str_cseq_col; q_vals[cseq_col].type = DB_INT; q_vals[cseq_col].nul = 0; n_query_cols++; q_cols[expires_col= n_query_cols] = &str_expires_col; q_vals[expires_col].type = DB_INT; q_vals[expires_col].nul = 0; n_query_cols++; q_cols[desired_expires_col= n_query_cols] = &str_desired_expires_col; q_vals[desired_expires_col].type = DB_INT; q_vals[desired_expires_col].nul = 0; n_query_cols++; q_cols[record_route_col= n_query_cols] = &str_record_route_col; q_vals[record_route_col].type = DB_STR; q_vals[record_route_col].nul = 0; n_query_cols++; q_cols[contact_col= n_query_cols] = &str_contact_col; q_vals[contact_col].type = DB_STR; q_vals[contact_col].nul = 0; n_query_cols++; q_cols[remote_contact_col= n_query_cols] = &str_remote_contact_col; q_vals[remote_contact_col].type = DB_STR; q_vals[remote_contact_col].nul = 0; n_query_cols++; q_cols[version_col= n_query_cols] = &str_version_col; q_vals[version_col].type = DB_INT; q_vals[version_col].nul = 0; n_query_cols++; q_cols[touri_col= n_query_cols] = &str_to_uri_col; q_vals[touri_col].type = DB_STR; q_vals[touri_col].nul = 0; n_query_cols++; /* must keep this the last column to be inserted */ q_cols[extra_headers_col= n_query_cols] = &str_extra_headers_col; q_vals[extra_headers_col].type = DB_STR; q_vals[extra_headers_col].nul = 0; n_query_cols++; /* cols and values used for update */ db_cols[0]= &str_expires_col; db_vals[0].type = DB_INT; db_vals[0].nul = 0; db_cols[1]= &str_cseq_col; db_vals[1].type = DB_INT; db_vals[1].nul = 0; db_cols[2]= &str_etag_col; db_vals[2].type = DB_STR; db_vals[2].nul = 0; db_cols[3]= &str_desired_expires_col; db_vals[3].type = DB_INT; db_vals[3].nul = 0; db_cols[4]= &str_extra_headers_col; db_vals[4].type = DB_STR; db_vals[4].nul = 0; if(pua_db== NULL) { LM_ERR("null database connection\n"); return; } if(pua_dbf.use_table(pua_db, &db_table)< 0) { LM_ERR("in use table\n"); return ; } for(i=0; ip_records[i].lock); p = HashT->p_records[i].entity->next; while(p) { if(p->expires < time(NULL)) { p= p->next; continue; } print_ua_pres(p); LM_DBG("--------\n"); switch(p->db_flag) { case NO_UPDATEDB_FLAG: { LM_DBG("NO_UPDATEDB_FLAG\n"); break; } case UPDATEDB_FLAG: { LM_DBG("UPDATEDB_FLAG\n"); n_update_cols= 0; n_query_update= 0; q_vals[puri_col].val.str_val = *(p->pres_uri); q_vals[pid_col].val.str_val = p->id; q_vals[flag_col].val.int_val = p->flag; q_vals[event_col].val.int_val = p->event; n_query_update = event_col; if(p->watcher_uri) { q_vals[watcher_col].val.str_val = *(p->watcher_uri); q_vals[callid_col].val.str_val = p->call_id; q_vals[totag_col].val.str_val = p->to_tag; q_vals[fromtag_col].val.str_val = p->from_tag; n_query_update = fromtag_col; } db_vals[0].val.int_val= p->expires; db_vals[1].val.int_val= p->cseq; db_vals[2].val.str_val= p->etag; db_vals[3].val.int_val= p->desired_expires; if(p->extra_headers.s && p->extra_headers.len) { db_vals[4].val.str_val= p->extra_headers; n_update_cols= 5; } else { db_vals[4].val.str_val.s= NULL; db_vals[4].val.str_val.len= 0; n_update_cols= 4; } LM_DBG("Updating:n_query_update= %d\tn_update_cols= %d\n", n_query_update, n_update_cols); p->db_flag= NO_UPDATEDB_FLAG; if(pua_dbf.update(pua_db, q_cols, 0, q_vals, db_cols, db_vals, n_query_update, n_update_cols)<0) { LM_ERR("while updating in database\n"); if(!no_lock) lock_release(&HashT->p_records[i].lock); return ; } break; } case INSERTDB_FLAG: { LM_DBG("INSERTDB_FLAG\n"); q_vals[puri_col].val.str_val = *(p->pres_uri); q_vals[pid_col].val.str_val = p->id; q_vals[flag_col].val.int_val = p->flag; if(p->watcher_uri) q_vals[watcher_col].val.str_val = *(p->watcher_uri); else memset(&q_vals[watcher_col].val.str_val, 0, sizeof(str)); q_vals[callid_col].val.str_val = p->call_id; q_vals[totag_col].val.str_val = p->to_tag; q_vals[fromtag_col].val.str_val = p->from_tag; q_vals[cseq_col].val.int_val= p->cseq; q_vals[tuple_col].val.str_val = p->tuple_id; q_vals[etag_col].val.str_val = p->etag; q_vals[expires_col].val.int_val = p->expires; q_vals[desired_expires_col].val.int_val = p->desired_expires; q_vals[event_col].val.int_val = p->event; q_vals[version_col].val.int_val = p->version; q_vals[touri_col].val.str_val = p->to_uri; q_vals[record_route_col].val.str_val = p->record_route; q_vals[contact_col].val.str_val = p->contact; q_vals[remote_contact_col].val.str_val = p->remote_contact; q_vals[extra_headers_col].val.str_val = p->extra_headers; if(pua_dbf.insert(pua_db, q_cols, q_vals, n_query_cols)< 0) { LM_ERR("while inserting in db table pua\n"); if(!no_lock) lock_release(&HashT->p_records[i].lock); return ; } break; } } p->db_flag= NO_UPDATEDB_FLAG; p= p->next; } if(!no_lock) lock_release(&HashT->p_records[i].lock); } db_vals[0].val.int_val= (int)time(NULL)- 10; db_ops[0]= OP_LT; if(pua_dbf.delete(pua_db, db_cols, db_ops, db_vals, 1) < 0) { LM_ERR("while deleting from db table pua\n"); } return ; } opensips-2.2.2/modules/pua/pua.h000066400000000000000000000024621300170765700165620ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PUBLISH_H #define PUBLISH_H #include "../../str.h" #include "hash.h" #include "../../db/db.h" #define INSERT_TYPE 1<<1 #define UPDATE_TYPE 1<<2 #define REQ_OTHER 0 #define REQ_ME 1 extern str default_domain; extern struct tm_binds tmb; extern htable_t* HashT; extern int HASH_SIZE; extern int min_expires; extern int pua_ul_publish; extern int default_expires; extern db_con_t *pua_db; extern db_func_t pua_dbf; extern str db_table; #endif opensips-2.2.2/modules/pua/pua_bind.c000066400000000000000000000024441300170765700175510ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pua_bind.h" #include "../../dprint.h" #include "hash.h" int bind_pua(pua_api_t* api) { if (!api) { LM_ERR("Invalid parameter value\n"); return -1; } api->send_publish = send_publish; api->send_subscribe = send_subscribe; api->register_puacb = register_puacb; api->is_dialog = is_dialog; api->get_record_id = get_record_id; api->add_event = add_pua_event; api->get_subs_list = get_subs_list; return 0; } opensips-2.2.2/modules/pua/pua_bind.h000066400000000000000000000032261300170765700175550ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PUA_API_H #define PUA_API_H #include "send_subscribe.h" #include "send_publish.h" #include "pua_callback.h" #include "hash.h" typedef struct pua_api { send_subscribe_t send_subscribe; send_publish_t send_publish; register_puacb_t register_puacb; query_dialog_t is_dialog; get_record_id_t get_record_id; add_pua_event_t add_event; get_subs_list_t get_subs_list; } pua_api_t; typedef int (*bind_pua_t)(pua_api_t* api); static inline int load_pua_api( pua_api_t *api) { bind_pua_t bind_pua; /* import the pua auto-loading function */ if ( !(bind_pua= (bind_pua_t)find_export("bind_pua", 1,0))) { LM_ERR("failed to import bind_pua\n"); return -1; } /* let the auto-loading function load all pua stuff */ if (bind_pua(api) < 0) { LM_ERR("Can't bind pua\n"); return -1; } return 0; } #endif opensips-2.2.2/modules/pua/pua_callback.c000066400000000000000000000045671300170765700204010ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../dprint.h" #include "../../error.h" #include "../../mem/shm_mem.h" #include "pua_callback.h" struct puacb_head_list* puacb_list = 0; int init_puacb_list(void) { puacb_list = (struct puacb_head_list*)shm_malloc ( sizeof(struct puacb_head_list) ); if (puacb_list==0) { LM_CRIT("no more shared mem\n"); return -1; } puacb_list->first = 0; puacb_list->reg_types = 0; return 1; } void destroy_puacb_list(void) { struct pua_callback *cbp, *cbp_tmp; if (!puacb_list) return; for( cbp=puacb_list->first; cbp ; ) { cbp_tmp = cbp; cbp = cbp->next; if (cbp_tmp->param) shm_free( cbp_tmp->param ); shm_free( cbp_tmp ); } shm_free(puacb_list); } /* register a callback function 'f' for 'types' mask of events; */ int register_puacb( int types, pua_cb f, void* param ) { struct pua_callback *cbp; /* are the callback types valid?... */ if ( types<0 || types>PUACB_MAX ) { LM_CRIT("invalid callback types: mask=%d\n",types); return E_BUG; } /* we don't register null functions */ if (f==0) { LM_CRIT("null callback function\n"); return E_BUG; } /* build a new callback structure */ if (!(cbp=(struct pua_callback*)shm_malloc(sizeof( struct pua_callback)))) { LM_ERR("out of share mem\n"); return E_OUT_OF_MEM; } /* link it into the proper place... */ cbp->next = puacb_list->first; puacb_list->first = cbp; puacb_list->reg_types |= types; cbp->callback = f; cbp->param= param; cbp->types = types; if (cbp->next) cbp->id = cbp->next->id+1; else cbp->id = 0; return 1; } opensips-2.2.2/modules/pua/pua_callback.h000066400000000000000000000042101300170765700203670ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PUA_CBACK #define PUA_CBACK #include "../../parser/parse_fline.h" #include "../pua/hash.h" #define PUACB_MAX (1<<9) /* callback function prototype */ typedef int (pua_cb)(ua_pres_t* hentity, struct sip_msg*); /* register callback function prototype */ typedef int (*register_puacb_t)(int types, pua_cb f, void* param ); struct pua_callback { int id; /* id of this callback - useless */ int types; /* types of events that trigger the callback*/ pua_cb* callback; /* callback function */ void* param; struct pua_callback* next; }; struct puacb_head_list { struct pua_callback *first; int reg_types; }; extern struct puacb_head_list* puacb_list; int init_puacb_list(void); void destroy_puacb_list(void); /* register a callback for several types of events */ int register_puacb( int types, pua_cb f, void* param ); /* run all transaction callbacks for an event type */ static inline void run_pua_callbacks(ua_pres_t* hentity, struct sip_msg* msg) { struct pua_callback *cbp; for (cbp= puacb_list->first; cbp; cbp=cbp->next) { if(cbp->types & hentity->flag) { LM_DBG("found callback\n"); cbp->callback(hentity, msg); } } } /* Q: should I call the registered callback functions when the modules refresh a request? */ #endif opensips-2.2.2/modules/pua/send_publish.c000066400000000000000000000375051300170765700204550ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-11-29 initial version (Anca Vamanu) */ #include #include #include #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../parser/parse_expires.h" #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../parser/msg_parser.h" #include "../../str.h" #include "../tm/tm_load.h" #include "../presence/hash.h" #include "pua.h" #include "hash.h" #include "send_publish.h" #include "pua_callback.h" #include "event_list.h" int send_publish_int(ua_pres_t* presentity, publ_info_t* publ, pua_event_t* ev, int hash_index); str* publ_build_hdr(int expires, pua_event_t* ev, str* content_type, str* etag, str* extra_headers, int is_body) { static char buf[3000]; str* str_hdr = NULL; char* expires_s = NULL; int len = 0; str ctype; str_hdr =(str*)pkg_malloc(sizeof(str)); if(str_hdr== NULL) { LM_ERR("no more memory\n"); return NULL; } str_hdr->s = buf; str_hdr->len= 0; memcpy(str_hdr->s+ str_hdr->len ,"Event: ", 7); str_hdr->len+= 7; memcpy(str_hdr->s+ str_hdr->len, ev->name.s, ev->name.len); str_hdr->len+= ev->name.len; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; memcpy(str_hdr->s+str_hdr->len ,"Expires: ", 9); str_hdr->len += 9; if( expires != 0 ) { expires++; } expires_s = int2str(expires, &len); memcpy(str_hdr->s+str_hdr->len, expires_s, len); str_hdr->len+= len; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; if(etag) { LM_DBG("UPDATE_TYPE [etag]= %.*s\n", etag->len, etag->s); memcpy(str_hdr->s+str_hdr->len,"SIP-If-Match: ", 14); str_hdr->len += 14; memcpy(str_hdr->s+str_hdr->len, etag->s, etag->len); str_hdr->len += etag->len; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; } if(is_body) { if(content_type== NULL || content_type->s== NULL || content_type->len== 0) { ctype= ev->content_type; /* use event default value */ } else { ctype.s= content_type->s; ctype.len= content_type->len; } memcpy(str_hdr->s+str_hdr->len,"Content-Type: ", 14); str_hdr->len += 14; memcpy(str_hdr->s+str_hdr->len, ctype.s, ctype.len); str_hdr->len += ctype.len; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; } if(extra_headers && extra_headers->s && extra_headers->len) { memcpy(str_hdr->s+str_hdr->len,extra_headers->s , extra_headers->len); str_hdr->len += extra_headers->len; } str_hdr->s[str_hdr->len] = '\0'; return str_hdr; } #define PUA_PARSE_PRES_ID(id, hi, li) do { \ li = id / HASH_SIZE;\ hi = id % HASH_SIZE;\ } while(0) publ_info_t* construct_pending_publ(ua_pres_t* presentity) { publ_info_t* p; publ_t* pending_publ; int size; pending_publ = presentity->pending_publ; if(!presentity->pres_uri) { LM_ERR("Wrong parameter - empty pres_uri or content_type filed\n"); return 0; } size = sizeof(publ_info_t) + sizeof(str) + presentity->pres_uri->len+ pending_publ->content_type.len; if(pending_publ->body.s) size+= sizeof(str) + pending_publ->body.len; if(pending_publ->extra_headers.s) size+= sizeof(str) + pending_publ->extra_headers.len; if(presentity->outbound_proxy) size+= presentity->outbound_proxy->len; p = (publ_info_t*)pkg_malloc(size); if(p == NULL) { LM_ERR("No more memory\n"); return 0; } memset(p, 0, size); size = sizeof(publ_info_t); if(pending_publ->body.s) { p->body = (str*)((char*)p + size); size+= sizeof(str); p->body->s = (char*)p + size; memcpy(p->body->s, pending_publ->body.s, pending_publ->body.len); p->body->len = pending_publ->body.len; size+= pending_publ->body.len; } p->content_type.s = (char*)p + size; memcpy(p->content_type.s, pending_publ->content_type.s, pending_publ->content_type.len); p->content_type.len = pending_publ->content_type.len; size+= pending_publ->content_type.len; p->pres_uri = (str*)((char*)p + size); size+= sizeof(str); p->pres_uri->s = (char*)p + size; memcpy(p->pres_uri->s, presentity->pres_uri->s, presentity->pres_uri->len); p->pres_uri->len = presentity->pres_uri->len; size+= presentity->pres_uri->len; if(pending_publ->extra_headers.s) { p->extra_headers = (str*)((char*)p + size); size+= sizeof(str); p->extra_headers->s = (char*)p + size; memcpy(p->extra_headers->s, pending_publ->extra_headers.s, pending_publ->extra_headers.len); p->extra_headers->len = pending_publ->extra_headers.len; size+= pending_publ->extra_headers.len; } if(presentity->outbound_proxy) { p->outbound_proxy.s = (char*)p + size; memcpy(p->outbound_proxy.s, presentity->outbound_proxy->s, presentity->outbound_proxy->len); p->outbound_proxy.len = presentity->outbound_proxy->len; size+= presentity->outbound_proxy->len; } p->expires = pending_publ->expires; p->cb_param = pending_publ->cb_param; return p; } void publ_expired_cback_func(struct cell *t, int type, struct tmcb_params *ps) { ua_pres_t presentity; struct sip_msg* msg; if (ps->param==NULL) { LM_DBG("NULL callback parameter\n"); return; } LM_DBG("cback param = %p\n", *ps->param); if ( (msg=ps->rpl)==NULL) { LM_ERR("no reply message found\n"); return; } if (parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("parsing headers\n"); return; } if (msg->expires== NULL || msg->expires->body.len<= 0) { LM_ERR("No Expires header found\n"); return; } if (parse_expires(msg->expires) < 0) { LM_ERR("cannot parse Expires header\n"); return; } /* use a dummy presentity structure */ memset( &presentity, 0, sizeof(presentity) ); /* copy the MI async handler */ presentity.cb_param = *ps->param; presentity.flag = MI_ASYN_PUBLISH; run_pua_callbacks( &presentity, ps->rpl); /* unlink the MI handler once triggered */ *ps->param = NULL; return; } void publ_cback_func(struct cell *t, int type, struct tmcb_params *ps) { struct hdr_field* hdr= NULL; struct sip_msg* msg= NULL; ua_pres_t* presentity= NULL; unsigned int lexpire= 0; str etag; unsigned int hash_index, local_index; unsigned long pres_id; if(ps->param == NULL) { LM_ERR("NULL parameter\n"); return; } msg= ps->rpl; if(msg == NULL) { LM_ERR("no reply message found\n"); return; } LM_DBG("cback param = %lu\n", (unsigned long)*ps->param); pres_id = (unsigned long)*ps->param; PUA_PARSE_PRES_ID(pres_id, hash_index, local_index); LM_DBG("hash_index= %u, local_index= %u\n", hash_index, local_index); if(!find_htable(hash_index, local_index)) { LM_ERR("No record found\n"); return; } if(msg== FAKED_REPLY) { LM_DBG("FAKED_REPLY\n"); goto done; } if( ps->code>= 300 ) { delete_htable(hash_index, local_index); goto done; } if( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("parsing headers\n"); return; } if(msg->expires== NULL || msg->expires->body.len<= 0) { LM_ERR("No Expires header found\n"); return; } if (!msg->expires->parsed && (parse_expires(msg->expires) < 0)) { LM_ERR("cannot parse Expires header\n"); return; } lexpire = ((exp_body_t*)msg->expires->parsed)->val; LM_DBG("lexpire= %u\n", lexpire); if(lexpire == 0) { delete_htable(hash_index, local_index); goto done; } hdr = get_header_by_static_name( msg, "SIP-ETag"); if( hdr==NULL ) /* must find SIP-Etag header field in 200 OK msg*/ { LM_ERR("no SIP-ETag header field found\n"); return; } etag= hdr->body; update_htable(hash_index, local_index, lexpire, &etag, 0); done: lock_get(&HashT->p_records[hash_index].lock); presentity = get_htable_safe(hash_index, local_index); if(!presentity) { LM_DBG("Record not found\n"); lock_release(&HashT->p_records[hash_index].lock); return; } if(presentity->ua_flag == REQ_OTHER) { run_pua_callbacks(presentity, msg); presentity->cb_param = NULL; } presentity->waiting_reply = 0; while(presentity->pending_publ) { publ_t* pending_publ = presentity->pending_publ; publ_info_t* publ = construct_pending_publ(presentity); if(publ == NULL) { LM_ERR("Failed to create publish record\n"); lock_release(&HashT->p_records[hash_index].lock); presentity->pending_publ = pending_publ->next; shm_free(pending_publ); continue; } LM_DBG("Found pending publish\n"); presentity->pending_publ = 0; presentity->waiting_reply = 1; send_publish_int(presentity, publ, get_event(presentity->event), presentity->hash_index); pkg_free(publ); presentity->pending_publ = pending_publ->next; shm_free(pending_publ); break; } lock_release(&HashT->p_records[hash_index].lock); } publ_t* build_pending_publ(publ_info_t* publ) { publ_t* p; int size; size = sizeof(publ_t) + ((publ->body)?publ->body->len:0) + publ->content_type.len + ((publ->extra_headers)?publ->extra_headers->len:0); p = (publ_t*)shm_malloc(size); if(p == NULL) { LM_ERR("No more share memory\n"); return 0; } memset(p, 0, size); size = sizeof(publ_t); if(publ->body && publ->body->s) { p->body.s = (char*)p + size; memcpy(p->body.s, publ->body->s, publ->body->len); p->body.len = publ->body->len; size+= publ->body->len; } if(publ->extra_headers && publ->extra_headers->s) { p->extra_headers.s = (char*)p + size; memcpy(p->extra_headers.s, publ->extra_headers->s, publ->extra_headers->len); p->extra_headers.len = publ->extra_headers->len; size+= publ->extra_headers->len; LM_DBG("saved [%.*s]\n", p->extra_headers.len, p->extra_headers.s); } CONT_COPY(p, p->content_type, publ->content_type); p->expires = publ->expires; p->cb_param = publ->cb_param; return p; } int send_publish_int(ua_pres_t* presentity, publ_info_t* publ, pua_event_t* ev, int hash_index) { unsigned long pres_id= 0; int ret = ERR_PUBLISH_GENERIC; char etag_buf[256]; char tuple_buf[128]; str tuple_id= {0, 0}; str etag= {0, 0}; int ver= 0; str* body= NULL; str* str_hdr = NULL; str met = {"PUBLISH", 7}; void* mi_hdl = NULL; LM_DBG("start\n"); if(presentity) { LM_DBG("presentity exists\n"); pres_id = PRES_HASH_ID(presentity); ver= ++presentity->version; /* copy etag */ if(presentity->etag.s) { etag.s = etag_buf; memcpy(etag.s, presentity->etag.s, presentity->etag.len); etag.len = presentity->etag.len; } /* tuple id */ if(presentity->tuple_id.s) { tuple_id.s = tuple_buf; memcpy(tuple_id.s, presentity->tuple_id.s, presentity->tuple_id.len); tuple_id.len = presentity->tuple_id.len; } presentity->desired_expires= publ->expires + (int)time(NULL); presentity->waiting_reply = 1; presentity->cb_param = publ->cb_param; if(publ->expires== 0) { LM_DBG("expires= 0- delete from hash table\n"); if (presentity->flag|MI_ASYN_PUBLISH) mi_hdl = presentity->cb_param; delete_htable_safe(presentity, hash_index); } } lock_release(&HashT->p_records[hash_index].lock); /* handle body */ if(publ->body && publ->body->s) { if(ev->process_body) { if(ev->process_body(publ, &body, ver, &tuple_id)< 0 || body== NULL) { LM_ERR("while processing body\n"); goto error; } } else body = publ->body; LM_DBG("Handled body [%.*s]\n", body->len, body->s); } if(publ->expires!= 0 && publ->expires< min_expires) publ->expires = min_expires; if(presentity== NULL) { if(publ->expires== 0) { LM_DBG("request for a publish with expires 0 and" " no record found\n"); ret = ERR_PUBLISH_NO_RECORD; goto error; } if(publ->body== NULL) { if (ev->content_type.s && ev->content_type.len) { LM_ERR("New '%.*s' PUBLISH and no body found - invalid request\n", ev->name.len, ev->name.s); ret = ERR_PUBLISH_NO_BODY; goto error; } } pres_id = new_publ_record(publ, ev, &tuple_id); } str_hdr = publ_build_hdr(((publ->expires< 0)?3600:publ->expires), ev, &publ->content_type, (etag.s?&etag:NULL), publ->extra_headers, ((body)?1:0)); if(str_hdr == NULL) { LM_ERR("while building extra_headers\n"); goto error; } LM_DBG("publ->pres_uri:\n%.*s\n ", publ->pres_uri->len, publ->pres_uri->s); LM_DBG("str_hdr:\n%.*s %d\n ", str_hdr->len, str_hdr->s, str_hdr->len); if(body && body->len && body->s ) LM_DBG("body:\n%.*s\n ", body->len, body->s); LM_DBG("cback param = %ld\n", pres_id); if (tmb.t_request(&met, /* Type of the message */ publ->pres_uri, /* Request-URI */ publ->pres_uri, /* To */ publ->pres_uri, /* From */ str_hdr, /* Optional headers */ body, /* Message body */ /*Outbound proxy*/ ((publ->outbound_proxy.s)?&publ->outbound_proxy:0), /* Callback function */ publ->expires?publ_cback_func:(mi_hdl?publ_expired_cback_func:0), /* Callback parameter */ publ->expires?(void*)pres_id:mi_hdl, 0 ) < 0 ) { LM_ERR("failed to send PUBLISH\n"); ret = -1; goto error; } pkg_free(str_hdr); if(body && ev->process_body) { if(body->s) xmlFree(body->s); pkg_free(body); } return ERR_PUBLISH_NO_ERROR; error: if(body && ev->process_body) { if(body->s) xmlFree(body->s); pkg_free(body); } if(str_hdr) pkg_free(str_hdr); return ret; } int send_publish( publ_info_t* publ ) { ua_pres_t* presentity= NULL; ua_pres_t pres; unsigned int hash_code; pua_event_t* ev= NULL; publ_t **last; LM_DBG("pres_uri=%.*s\n", publ->pres_uri->len, publ->pres_uri->s ); /* get event from list */ ev= get_event(publ->event); if(ev== NULL) { LM_ERR("event not found in list\n"); return -1; } memset(&pres, 0, sizeof(ua_pres_t)); pres.pres_uri= publ->pres_uri; pres.flag= publ->source_flag; pres.id= publ->id; pres.event= publ->event; if(publ->etag) pres.etag= *publ->etag; hash_code= core_hash(publ->pres_uri, NULL, HASH_SIZE); LM_DBG("Try to get hash lock [%d]\n", hash_code); lock_get(&HashT->p_records[hash_code].lock); LM_DBG("Got hash lock %d\n", hash_code); if(publ->flag != INSERT_TYPE) presentity= search_htable(&pres, hash_code); if(publ->etag && presentity== NULL) { LM_DBG("Etag restriction and no record found\n"); lock_release(&HashT->p_records[hash_code].lock); return 418; } if(presentity) { /* handle extra headers */ if(presentity->extra_headers.s) shm_free(presentity->extra_headers.s); presentity->extra_headers.len= 0; if(publ->extra_headers && publ->extra_headers->s && publ->extra_headers->len) { presentity->extra_headers.s= (char*)shm_malloc(publ->extra_headers->len); if(presentity->extra_headers.s == NULL) { LM_ERR("while processing extra_headers\n"); lock_release(&HashT->p_records[hash_code].lock); return -1; } memcpy(presentity->extra_headers.s, publ->extra_headers->s, publ->extra_headers->len); presentity->extra_headers.len= publ->extra_headers->len; } if(presentity->db_flag == NO_UPDATEDB_FLAG) presentity->db_flag= UPDATEDB_FLAG; if (presentity->waiting_reply) { LM_DBG("Presentity is waiting for reply, queue this PUBLISH\n"); last = &presentity->pending_publ; while (*last) last = &((*last)->next); *last = build_pending_publ(publ); if(! *last) { LM_ERR("Failed to create pending publ record\n"); lock_release(&HashT->p_records[hash_code].lock); return -1; } lock_release(&HashT->p_records[hash_code].lock); return 0; } } return send_publish_int(presentity, publ, ev, hash_code); } opensips-2.2.2/modules/pua/send_publish.h000066400000000000000000000040221300170765700204460ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PU_SEND_PUBLISH_ #define _PU_SEND_PUBLISH_ #include #include "../tm/tm_load.h" #include "../../str.h" #include "hash.h" #include "event_list.h" #define ERR_PUBLISH_NO_ERROR 0 #define ERR_PUBLISH_GENERIC -1 #define ERR_PUBLISH_NO_RECORD -9 #define ERR_PUBLISH_NO_BODY -10 typedef struct publ_info { str id; str* pres_uri; str* body; int expires; int flag; int source_flag; int event; str content_type; /*the content_type of the body if present(optional if the * same as the default value for that event) */ str* etag; str* extra_headers; str outbound_proxy; void* cb_param; /* the parameter for the function to be called on the callback for the received reply; it must be allocated in share memory; a reference to it will be found in the cb_param filed of the ua_pres_structure receied as a parameter for the registered function*/ }publ_info_t; typedef int (*send_publish_t)(publ_info_t* publ); int send_publish( publ_info_t* publ ); void publ_cback_func(struct cell *t, int type, struct tmcb_params *ps); str* publ_build_hdr(int expires, pua_event_t* event, str* content_type, str* etag, str* extra_headers, int is_body); #endif opensips-2.2.2/modules/pua/send_subscribe.c000066400000000000000000000627201300170765700207650ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../ut.h" #include "../tm/tm_load.h" #include "../tm/dlg.h" #include "../../parser/msg_parser.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_from.h" #include "../../parser/parse_expires.h" #include "../presence/hash.h" #include "hash.h" #include "pua.h" #include "send_subscribe.h" #include "pua_callback.h" #include "event_list.h" void print_subs(subs_info_t* subs) { LM_DBG("pres_uri[%d]=[%.*s]\n", subs->pres_uri->len, subs->pres_uri->len, subs->pres_uri->s); LM_DBG("watcher_uri[%d]=[%.*s]\n", subs->watcher_uri->len, subs->watcher_uri->len, subs->watcher_uri->s); if(subs->to_uri.s) LM_DBG("to_uri=[%.*s]\n", subs->to_uri.len, subs->to_uri.s); } str* subs_build_hdr(str* contact, int expires, int event, str* extra_headers) { str* str_hdr= NULL; static char buf[3000]; char* subs_expires= NULL; int len= 1; pua_event_t* ev; str_hdr= (str*)pkg_malloc(sizeof(str)); if(str_hdr== NULL) { LM_ERR("no more memory\n"); return NULL; } memset(str_hdr, 0, sizeof(str)); str_hdr->s= buf; ev= get_event(event); if(ev== NULL) { LM_ERR("getting event from list\n"); goto error; } memcpy(str_hdr->s+ str_hdr->len ,"Event: ", 7); str_hdr->len+= 7; memcpy(str_hdr->s+ str_hdr->len, ev->name.s, ev->name.len); str_hdr->len+= ev->name.len; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; memcpy(str_hdr->s+ str_hdr->len ,"Contact: <", 10); str_hdr->len += 10; memcpy(str_hdr->s +str_hdr->len, contact->s, contact->len); str_hdr->len+= contact->len; memcpy(str_hdr->s+ str_hdr->len, ">", 1); str_hdr->len+= 1; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; memcpy(str_hdr->s+ str_hdr->len ,"Expires: ", 9); str_hdr->len += 9; subs_expires= int2str(expires, &len); if(subs_expires == NULL || len == 0) { LM_ERR("while converting int to str\n"); pkg_free(str_hdr); return NULL; } memcpy(str_hdr->s+str_hdr->len, subs_expires, len); str_hdr->len += len; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; if(extra_headers && extra_headers->s && extra_headers->len) { memcpy(str_hdr->s+str_hdr->len, extra_headers->s, extra_headers->len); str_hdr->len += extra_headers->len; } str_hdr->s[str_hdr->len]= '\0'; return str_hdr; error: if(str_hdr) pkg_free(str_hdr); return NULL; } dlg_t* pua_build_dlg_t(ua_pres_t* presentity) { dlg_t* td =NULL; int size; size= sizeof(dlg_t)+ presentity->call_id.len+ presentity->to_tag.len+ presentity->from_tag.len+ presentity->watcher_uri->len+ presentity->to_uri.len+ presentity->remote_contact.len; td = (dlg_t*)pkg_malloc(size); if(td == NULL) { LM_ERR("No memory left\n"); return NULL; } memset(td, 0, size); size= sizeof(dlg_t); td->id.call_id.s = (char*)td+ size; memcpy(td->id.call_id.s, presentity->call_id.s, presentity->call_id.len); td->id.call_id.len= presentity->call_id.len; size+= presentity->call_id.len; td->id.rem_tag.s = (char*)td+ size; memcpy(td->id.rem_tag.s, presentity->to_tag.s, presentity->to_tag.len); td->id.rem_tag.len = presentity->to_tag.len; size+= presentity->to_tag.len; td->id.loc_tag.s = (char*)td+ size; memcpy(td->id.loc_tag.s, presentity->from_tag.s, presentity->from_tag.len); td->id.loc_tag.len =presentity->from_tag.len; size+= presentity->from_tag.len; td->loc_uri.s = (char*)td+ size; memcpy(td->loc_uri.s, presentity->watcher_uri->s, presentity->watcher_uri->len) ; td->loc_uri.len = presentity->watcher_uri->len; size+= td->loc_uri.len; td->rem_uri.s = (char*)td+ size; memcpy(td->rem_uri.s, presentity->to_uri.s, presentity->to_uri.len) ; td->rem_uri.len = presentity->to_uri.len; size+= td->rem_uri.len; td->rem_target.s = (char*)td+ size; memcpy(td->rem_target.s, presentity->remote_contact.s, presentity->remote_contact.len) ; td->rem_target.len = presentity->remote_contact.len; size+= td->rem_target.len; if(presentity->record_route.s && presentity->record_route.len) { if(parse_rr_body(presentity->record_route.s, presentity->record_route.len, &td->route_set)< 0) { LM_ERR("ERROR in function parse_rr_body\n"); pkg_free(td); return NULL; } } td->loc_seq.value = presentity->cseq++; td->loc_seq.is_set = 1; td->state= DLG_CONFIRMED ; return td; } void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps) { struct sip_msg* msg= NULL; int lexpire= 0; unsigned int cseq; ua_pres_t* presentity= NULL, *hentity= NULL; struct to_body *pto= NULL, *pfrom = NULL; int size= 0; int flag ; str record_route= {0, 0}; int rt; str contact; int initial_request = 0; if(ps==NULL || ps->param== NULL || *ps->param== NULL ) { LM_ERR("null callback parameter\n"); return; } LM_DBG("completed with status %d\n",ps->code) ; hentity= (ua_pres_t*)(*ps->param); flag= hentity->flag; if(hentity->flag & XMPP_INITIAL_SUBS) hentity->flag= XMPP_SUBSCRIBE; /* get dialog information from reply message: callid, to_tag, from_tag */ msg= ps->rpl; if(msg == NULL) { LM_ERR("no reply message found\n "); goto error; } if(msg== FAKED_REPLY) { /* delete record from hash_table and call registered functions */ if(hentity->call_id.s== NULL) /* if a new requets failed-> do nothing*/ { LM_DBG("initial Subscribe request failed\n"); goto done; } lock_get(&HashT->p_records[hentity->hash_index].lock); presentity = get_htable_safe(hentity->hash_index, hentity->local_index); if(presentity) { delete_htable_safe(presentity, hentity->hash_index); lock_release(&HashT->p_records[hentity->hash_index].lock); } lock_release(&HashT->p_records[hentity->hash_index].lock); goto done; } if ( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("when parsing headers\n"); goto done; } /*if initial request */ if(hentity->call_id.s== NULL) { initial_request = 1; if(ps->code>= 300) { LM_DBG("initial Subscribe request failed\n"); goto done; } if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); goto done; } if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); goto done; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); goto done; } } pfrom = (struct to_body*)msg->from->parsed; if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); goto done; } if( msg->to==NULL || msg->to->body.s==NULL) { LM_ERR("cannot parse TO header\n"); goto done; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); goto done; } if( pto->tag_value.s ==NULL || pto->tag_value.len == 0) { LM_ERR("no to tag value present\n"); goto done; } hentity->call_id= msg->callid->body; hentity->to_tag= pto->tag_value; hentity->from_tag= pfrom->tag_value; } /* extract the other necesary information for inserting a new record */ if(ps->rpl->expires && msg->expires->body.len > 0) { if (!msg->expires->parsed && (parse_expires(msg->expires) < 0)) { LM_ERR("cannot parse Expires header\n"); goto done; } lexpire = ((exp_body_t*)msg->expires->parsed)->val; LM_DBG("lexpire= %d\n", lexpire); } if(ps->code >= 300 ) { /* if an error code and a stored dialog delete it and try to send a subscription with type= INSERT_TYPE, else return*/ if(!initial_request) { subs_info_t subs; lock_get(&HashT->p_records[hentity->hash_index].lock); presentity = get_htable_safe(hentity->hash_index, hentity->local_index); if(presentity) { hentity->event= presentity->event; delete_htable_safe(presentity, hentity->hash_index); lock_release(&HashT->p_records[hentity->hash_index].lock); } lock_release(&HashT->p_records[hentity->hash_index].lock); memset(&subs, 0, sizeof(subs_info_t)); subs.pres_uri= hentity->pres_uri; subs.to_uri = hentity->to_uri; subs.watcher_uri= hentity->watcher_uri; subs.contact= &hentity->contact; if(hentity->remote_contact.s) subs.remote_target= &hentity->remote_contact; if(hentity->desired_expires== 0) subs.expires= -1; else if(hentity->desired_expires< (int)time(NULL)) subs.expires= 0; else subs.expires= hentity->desired_expires- (int)time(NULL)+ 3; subs.flag= INSERT_TYPE; subs.source_flag= flag; subs.event= hentity->event; subs.id= hentity->id; subs.outbound_proxy= hentity->outbound_proxy; subs.extra_headers= &hentity->extra_headers; subs.cb_param= hentity->cb_param; if(send_subscribe(&subs)< 0) { LM_ERR("when trying to send SUBSCRIBE\n"); goto done; } } goto done; } /*if a 2XX reply handle the two cases- an existing dialog and a new one*/ /* extract the contact */ if(msg->contact== NULL || msg->contact->body.s== NULL) { LM_ERR("no contact header found"); goto error; } if( parse_contact(msg->contact) <0 ) { LM_ERR(" cannot parse contact header\n"); goto error; } if(msg->contact->parsed == NULL) { LM_ERR("cannot parse contact header\n"); goto error; } contact = ((contact_body_t* )msg->contact->parsed)->contacts->uri; if(!initial_request) { /* do not delete the dialog - allow Notifies to be recognized as * inside a known dialog */ if (lexpire == 0) lexpire = 5; LM_DBG("*** Update expires\n"); update_htable(hentity->hash_index, hentity->local_index, lexpire, NULL, &contact); goto done; } /* if a new dialog -> insert */ if(lexpire== 0) { LM_DBG("expires= 0: no not insert\n"); goto done; } if( msg->cseq==NULL || msg->cseq->body.s==NULL) { LM_ERR("cannot parse cseq header\n"); goto done; } if( str2int( &(get_cseq(msg)->number), &cseq)< 0) { LM_ERR("while converting str to int\n"); goto done; } /*process record route and add it to a string*/ if (msg->record_route!=NULL) { rt = print_rr_body(msg->record_route, &record_route, 1, 0); if(rt != 0) { LM_ERR("parsing record route [%d]\n", rt); record_route.s=NULL; record_route.len=0; } } size= sizeof(ua_pres_t)+ 2*sizeof(str)+(hentity->pres_uri->len+ pto->uri.len+ pfrom->uri.len+ pto->tag_value.len+ pfrom->tag_value.len +msg->callid->body.len+ record_route.len+ hentity->contact.len+ hentity->id.len )*sizeof(char); presentity= (ua_pres_t*)shm_malloc(size); if(presentity== NULL) { LM_ERR("no more share memory\n"); if(record_route.s) pkg_free(record_route.s); goto done; } memset(presentity, 0, size); size= sizeof(ua_pres_t); presentity->pres_uri= (str*)( (char*)presentity+ size); size+= sizeof(str); presentity->pres_uri->s= (char*)presentity+ size; memcpy(presentity->pres_uri->s, hentity->pres_uri->s, hentity->pres_uri->len); presentity->pres_uri->len= hentity->pres_uri->len; size+= hentity->pres_uri->len; presentity->to_uri.s= (char*)presentity+ size; memcpy(presentity->to_uri.s, pto->uri.s, pto->uri.len); presentity->to_uri.len= pto->uri.len; size+= pto->uri.len; presentity->watcher_uri= (str*)( (char*)presentity+ size); size+= sizeof(str); presentity->watcher_uri->s= (char*)presentity+ size; memcpy(presentity->watcher_uri->s, pfrom->uri.s, pfrom->uri.len); presentity->watcher_uri->len= pfrom->uri.len; size+= pfrom->uri.len; presentity->call_id.s= (char*)presentity + size; memcpy(presentity->call_id.s,msg->callid->body.s, msg->callid->body.len); presentity->call_id.len= msg->callid->body.len; size+= presentity->call_id.len; presentity->to_tag.s= (char*)presentity + size; memcpy(presentity->to_tag.s,pto->tag_value.s, pto->tag_value.len); presentity->to_tag.len= pto->tag_value.len; size+= pto->tag_value.len; presentity->from_tag.s= (char*)presentity + size; memcpy(presentity->from_tag.s,pfrom->tag_value.s, pfrom->tag_value.len); presentity->from_tag.len= pfrom->tag_value.len; size+= pfrom->tag_value.len; if(record_route.len && record_route.s) { presentity->record_route.s= (char*)presentity + size; memcpy(presentity->record_route.s, record_route.s, record_route.len); presentity->record_route.len= record_route.len; size+= record_route.len; pkg_free(record_route.s); } presentity->contact.s= (char*)presentity + size; memcpy(presentity->contact.s, hentity->contact.s, hentity->contact.len); presentity->contact.len= hentity->contact.len; size+= hentity->contact.len; if(hentity->id.s) { presentity->id.s=(char*)presentity+ size; memcpy(presentity->id.s, hentity->id.s, hentity->id.len); presentity->id.len= hentity->id.len; size+= presentity->id.len; } if(hentity->extra_headers.s && hentity->extra_headers.len) { presentity->extra_headers.s= (char*)shm_malloc(hentity->extra_headers.len* sizeof(char)); if(presentity->extra_headers.s== NULL) { LM_ERR("no more share memory\n"); goto mem_error; } memcpy(presentity->extra_headers.s, hentity->extra_headers.s, hentity->extra_headers.len); presentity->extra_headers.len= hentity->extra_headers.len; } /* write the remote contact filed */ presentity->remote_contact.s= (char*)shm_malloc(contact.len* sizeof(char)); if(presentity->remote_contact.s== NULL) { LM_ERR("no more share memory\n"); goto mem_error; } memcpy(presentity->remote_contact.s, contact.s, contact.len); presentity->remote_contact.len= contact.len; presentity->event|= hentity->event; presentity->flag= hentity->flag; presentity->etag.s= NULL; presentity->cseq= cseq; presentity->desired_expires= hentity->desired_expires; presentity->expires= lexpire+ (int)time(NULL); if(BLA_SUBSCRIBE & presentity->flag) { LM_DBG("BLA_SUBSCRIBE FLAG inserted\n"); } LM_DBG("record for subscribe from %.*s to %.*s inserted in datatbase\n", presentity->watcher_uri->len, presentity->watcher_uri->s, presentity->pres_uri->len, presentity->pres_uri->s); insert_htable(presentity); done: if(hentity->ua_flag == REQ_OTHER) { hentity->flag= flag; run_pua_callbacks( hentity, msg); } error: if(hentity->extra_headers.s) shm_free(hentity->extra_headers.s); shm_free(hentity); return; mem_error: if(presentity->extra_headers.s) shm_free(presentity->extra_headers.s); if(presentity->remote_contact.s) shm_free(presentity->remote_contact.s); shm_free(presentity); if(hentity->extra_headers.s) shm_free(hentity->extra_headers.s); shm_free(hentity); } ua_pres_t* subscribe_cbparam(subs_info_t* subs, int ua_flag) { ua_pres_t* hentity= NULL; int size; str to_uri; to_uri = subs->to_uri.s?subs->to_uri:*subs->pres_uri; size= sizeof(ua_pres_t)+ 2*sizeof(str) + subs->pres_uri->len+ to_uri.len+subs->watcher_uri->len+subs->contact->len+ subs->id.len+ 1; if(subs->outbound_proxy && subs->outbound_proxy->len && subs->outbound_proxy->s ) size+= sizeof(str)+ subs->outbound_proxy->len* sizeof(char); hentity= (ua_pres_t*)shm_malloc(size); if(hentity== NULL) { LM_ERR("No more share memory\n"); return NULL; } memset(hentity, 0, size); size= sizeof(ua_pres_t); hentity->pres_uri = (str*)((char*)hentity + size); size+= sizeof(str); hentity->pres_uri->s = (char*)hentity+ size; memcpy(hentity->pres_uri->s, subs->pres_uri->s, subs->pres_uri->len ) ; hentity->pres_uri->len= subs->pres_uri->len; size+= subs->pres_uri->len; hentity->watcher_uri = (str*)((char*)hentity + size); size+= sizeof(str); hentity->watcher_uri->s = (char*)hentity+ size; memcpy(hentity->watcher_uri->s, subs->watcher_uri->s , subs->watcher_uri->len ) ; hentity->watcher_uri->len= subs->watcher_uri->len; size+= subs->watcher_uri->len; hentity->contact.s = (char*)hentity+ size; memcpy(hentity->contact.s, subs->contact->s , subs->contact->len ); hentity->contact.len= subs->contact->len; size+= subs->contact->len; if(subs->outbound_proxy) { hentity->outbound_proxy= (str*)((char*)hentity+ size); size+= sizeof(str); hentity->outbound_proxy->s= (char*)hentity+ size; memcpy(hentity->outbound_proxy->s, subs->outbound_proxy->s, subs->outbound_proxy->len); hentity->outbound_proxy->len= subs->outbound_proxy->len; size+= subs->outbound_proxy->len; } if(subs->expires< 0) hentity->desired_expires= 0; else hentity->desired_expires=subs->expires+ (int)time(NULL); if(subs->id.s) { CONT_COPY(hentity, hentity->id, subs->id); } CONT_COPY(hentity, hentity->to_uri, to_uri); if(subs->extra_headers && subs->extra_headers->s && subs->extra_headers->len) { hentity->extra_headers.s= (char*)shm_malloc(subs->extra_headers->len* sizeof(char)); if(hentity->extra_headers.s== NULL) { LM_ERR("no more share memory\n"); goto error; } memcpy(hentity->extra_headers.s, subs->extra_headers->s, subs->extra_headers->len); hentity->extra_headers.len= subs->extra_headers->len; } hentity->flag= subs->source_flag; hentity->event= subs->event; hentity->ua_flag= ua_flag; hentity->cb_param= subs->cb_param; return hentity; error: if(hentity) { if(hentity->extra_headers.s) shm_free(hentity->extra_headers.s); shm_free(hentity); } return NULL; } ua_pres_t* subs_cbparam_indlg(ua_pres_t* subs, int expires, int ua_flag) { ua_pres_t* hentity= NULL; int size; size= sizeof(ua_pres_t)+ 2*sizeof(str)+subs->pres_uri->len+ subs->to_uri.len+ subs->watcher_uri->len+ subs->contact.len+ subs->id.len+ subs->to_tag.len+ subs->call_id.len+ subs->from_tag.len+ 1; if(subs->outbound_proxy && subs->outbound_proxy->len && subs->outbound_proxy->s ) size+= sizeof(str)+ subs->outbound_proxy->len; if(subs->remote_contact.s) size+= subs->remote_contact.len; hentity= (ua_pres_t*)shm_malloc(size); if(hentity== NULL) { LM_ERR("No more share memory\n"); return NULL; } memset(hentity, 0, size); size= sizeof(ua_pres_t); hentity->pres_uri = (str*)((char*)hentity + size); size+= sizeof(str); hentity->pres_uri->s = (char*)hentity+ size; memcpy(hentity->pres_uri->s, subs->pres_uri->s , subs->pres_uri->len ) ; hentity->pres_uri->len= subs->pres_uri->len; size+= subs->pres_uri->len; hentity->watcher_uri = (str*)((char*)hentity + size); size+= sizeof(str); hentity->watcher_uri->s = (char*)hentity+ size; memcpy(hentity->watcher_uri->s, subs->watcher_uri->s , subs->watcher_uri->len ) ; hentity->watcher_uri->len= subs->watcher_uri->len; size+= subs->watcher_uri->len; CONT_COPY(hentity, hentity->contact, subs->contact); CONT_COPY(hentity, hentity->to_uri, subs->to_uri); if(subs->outbound_proxy) { hentity->outbound_proxy= (str*)((char*)hentity+ size); size+= sizeof(str); hentity->outbound_proxy->s= (char*)hentity+ size; memcpy(hentity->outbound_proxy->s, subs->outbound_proxy->s, subs->outbound_proxy->len); hentity->outbound_proxy->len= subs->outbound_proxy->len; size+= subs->outbound_proxy->len; } if(subs->id.s) { CONT_COPY(hentity, hentity->id, subs->id); } if(subs->remote_contact.s) { CONT_COPY(hentity, hentity->remote_contact, subs->remote_contact); } /* copy dialog information */ CONT_COPY(hentity, hentity->to_tag, subs->to_tag); CONT_COPY(hentity, hentity->from_tag, subs->from_tag); CONT_COPY(hentity, hentity->call_id, subs->call_id); if(subs->extra_headers.s && subs->extra_headers.len) { hentity->extra_headers.s= (char*)shm_malloc(subs->extra_headers.len* sizeof(char)); if(hentity->extra_headers.s== NULL) { LM_ERR("no more share memory\n"); goto error; } memcpy(hentity->extra_headers.s, subs->extra_headers.s, subs->extra_headers.len); hentity->extra_headers.len= subs->extra_headers.len; } if(expires< 0) hentity->desired_expires= 0; else hentity->desired_expires=expires+ (int)time(NULL); hentity->flag= subs->flag; hentity->event= subs->event; hentity->ua_flag= ua_flag; hentity->cb_param= subs->cb_param; hentity->hash_index = subs->hash_index; hentity->local_index = subs->local_index; return hentity; error: if(hentity) { if(hentity->extra_headers.s) shm_free(hentity->extra_headers.s); shm_free(hentity); } return NULL; } int send_subscribe(subs_info_t* subs) { ua_pres_t* presentity= NULL; str met= {"SUBSCRIBE", 9}; str* str_hdr= NULL; int ret= 0; unsigned int hash_index; ua_pres_t* hentity= NULL, pres; int expires; int flag; int result; print_subs(subs); flag= subs->source_flag; if(subs->source_flag & XMPP_INITIAL_SUBS) subs->source_flag= XMPP_SUBSCRIBE; if(subs->expires< 0) subs->expires = default_expires; else if(subs->expires!= 0 && subs->expires < min_expires) subs->expires = min_expires; expires = subs->expires; if(subs->extra_headers) LM_DBG("received extra_headers = %.*s\n", subs->extra_headers->len, subs->extra_headers->s); str_hdr= subs_build_hdr(subs->contact, expires, subs->event, subs->extra_headers); if(str_hdr== NULL || str_hdr->s== NULL) { LM_ERR("while building extra headers\n"); return -1; } memset(&pres, 0, sizeof(ua_pres_t)); pres.pres_uri = subs->pres_uri; pres.watcher_uri= subs->watcher_uri; if(subs->to_uri.s) pres.to_uri = subs->to_uri; else { pres.to_uri.s = subs->pres_uri->s; pres.to_uri.len = subs->pres_uri->len; } pres.flag = subs->source_flag; pres.id = subs->id; pres.event = subs->event; hash_index=core_hash(&pres.to_uri, pres.watcher_uri, HASH_SIZE); lock_get(&HashT->p_records[hash_index].lock); if(subs->flag != INSERT_TYPE) presentity= search_htable(&pres, hash_index); if(presentity== NULL ) { lock_release(&HashT->p_records[hash_index].lock); if(subs->flag & UPDATE_TYPE) { /* LM_ERR("request for a subscription" " with update type and no record found\n"); ret= -1; goto done; commented this because of the strange type parameter in usrloc callback functions */ LM_DBG("request for a subscription with update type" " and no record found\n"); subs->flag= INSERT_TYPE; } hentity= subscribe_cbparam(subs, REQ_OTHER); if(hentity== NULL) { LM_ERR("while building callback" " param\n"); ret= -1; goto done; } hentity->hash_index = hash_index; hentity->flag= flag; result= tmb.t_request (&met, /* Type of the message */ pres.pres_uri, /* Request-URI*/ &pres.to_uri, /* To */ pres.watcher_uri, /* From */ str_hdr, /* Optional headers including CRLF */ 0, /* Message body */ subs->outbound_proxy, /* Outbound_proxy */ subs_cback_func, /* Callback function */ (void*)hentity, /* Callback parameter */ 0 ); if(result< 0) { LM_ERR("while sending request with t_request\n"); if (hentity->extra_headers.s) shm_free(hentity->extra_headers.s); shm_free(hentity); goto done; } } else { /* if(presentity->desired_expires== 0) { if(subs->expires< 0) { LM_DBG("Found previous request for unlimited subscribe-" " do not send subscribe\n"); if (subs->event & PWINFO_EVENT) { presentity->watcher_count++; } lock_release(&HashT->p_records[hash_index].lock); goto done; } if(subs->event & PWINFO_EVENT) { if(subs->expires== 0) { presentity->watcher_count--; if( presentity->watcher_count> 0) { lock_release(&HashT->p_records[hash_index].lock); goto done; } } else { presentity->watcher_count++; if(presentity->watcher_count> 1) { lock_release(&HashT->p_records[hash_index].lock); goto done; } } } } */ dlg_t* td= NULL; if (subs->internal_update_flag) { LM_DBG("attempting to re-SUBSCRIBE on internal dialog update - skipping\n"); lock_release(&HashT->p_records[hash_index].lock); goto done; } if (presentity->to_tag.len == 0) { LM_WARN("attempting to re-SUBSCRIBE to temporary (non-established) dialog - skipping\n"); lock_release(&HashT->p_records[hash_index].lock); goto done; } td= pua_build_dlg_t(presentity); if(td== NULL) { LM_ERR("while building tm dlg_t structure"); ret= -1; lock_release(&HashT->p_records[hash_index].lock); goto done; } hentity= subs_cbparam_indlg(presentity, expires, REQ_OTHER); if(hentity== NULL) { LM_ERR("while building callback param\n"); lock_release(&HashT->p_records[hash_index].lock); ret= -1; pkg_free(td); goto done; } presentity->desired_expires = hentity->desired_expires; lock_release(&HashT->p_records[hash_index].lock); // hentity->flag= flag; LM_DBG("event parameter: %d\n", hentity->event); result= tmb.t_request_within (&met, str_hdr, 0, td, subs_cback_func, (void*)hentity, 0 ); if(result< 0) { shm_free(hentity); hentity= NULL; LM_ERR("while sending request with t_request\n"); goto done; } if(td->route_set) free_rr(&td->route_set); pkg_free(td); td= NULL; } done: pkg_free(str_hdr); return ret; } opensips-2.2.2/modules/pua/send_subscribe.h000066400000000000000000000040211300170765700207600ustar00rootroot00000000000000/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PU_SEND_SUBSC_ #define _PU_SEND_SUBSC_ #include #include "../tm/tm_load.h" #include "../../str.h" #include "hash.h" typedef struct subs_info { str id; str* pres_uri; str to_uri; str* watcher_uri; str* contact; str* remote_target; str* outbound_proxy; int event; str* extra_headers; int expires; int source_flag; int internal_update_flag; int flag; /* it can be : INSERT_TYPE or UPDATE_TYPE; not compulsory */ void* cb_param; /* the parameter for the function to be called on the callback for the received reply; it must be allocated in share memory; a reference to it will be found in the cb_param filed of the ua_pres_structure receied as a parameter for the registered function*/ }subs_info_t; typedef int (*send_subscribe_t)(subs_info_t* subs); int send_subscribe(subs_info_t* subs); void subs_cback_func(struct cell *t, int type, struct tmcb_params *ps); str* subs_build_hdr(str* watcher_uri, int expires, int event, str* extra_headers); dlg_t* pua_build_dlg_t(ua_pres_t* presentity); ua_pres_t* subscribe_cbparam(subs_info_t* subs, int ua_flag); ua_pres_t* subs_cbparam_indlg(ua_pres_t* subs, int expires, int ua_flag); #endif opensips-2.2.2/modules/pua/uri_list.h000066400000000000000000000044361300170765700176320ustar00rootroot00000000000000#ifndef _URI_LIST_H #define _URI_LIST_H #include #include "../../dprint.h" #include "../../mem/mem.h" typedef struct list_entry { str *strng; struct list_entry *next; } list_entry_t; static inline list_entry_t *list_insert(str *strng, list_entry_t *list, int *duplicate) { int cmp; list_entry_t *p, *q; if (duplicate != NULL) *duplicate = 0; if (strng == NULL || strng->s == NULL || strng->len == 0) { LM_ERR("bad string\n"); return list; } if ((p = (list_entry_t *) pkg_malloc(sizeof(list_entry_t))) == NULL) { LM_ERR("out of memory\n"); return list; } p->strng = strng; p->next = NULL; if (list == NULL) return p; cmp = strncmp(list->strng->s, strng->s, strng->len); if (cmp == 0) { if (duplicate != NULL) *duplicate = 1; return list; } if (cmp > 0) { p->next = list; return p; } else { q = list; while (q->next != NULL && (cmp = strncmp(q->next->strng->s, strng->s, strng->len)) < 0) q = q->next; if (cmp == 0) { if (duplicate != NULL) *duplicate = 1; return list; } p->next = q->next; q->next = p; return list; } } static inline list_entry_t *list_remove(str strng, list_entry_t *list) { int cmp = -1; list_entry_t *p = list; if (list != NULL) { if (strncmp(p->strng->s, strng.s, strng.len) == 0) { pkg_free(p->strng->s); pkg_free(p->strng); pkg_free(p); return list->next; } else { list_entry_t *p = list, *q; while (p->next != NULL && (cmp = strncmp(p->next->strng->s, strng.s, strng.len)) < 0) p = p->next; if (cmp == 0) { q = p->next; p->next = q->next; pkg_free(q->strng->s); pkg_free(q->strng); pkg_free(q); } } } return list; } static inline str *list_pop(list_entry_t **list) { str *ret = NULL; list_entry_t *tmp; if (*list != NULL) { ret = (*list)->strng; if ((*list)->next == NULL) { pkg_free(*list); *list = NULL; } else { tmp = *list; *list = (*list)->next; pkg_free(tmp); } } return ret; } static inline void list_free(list_entry_t **list) { str *strng; while ((strng = list_pop(list)) != NULL) { pkg_free(strng->s); pkg_free(strng); } *list = NULL; } #endif /* _URI_LIST_H */ opensips-2.2.2/modules/pua_bla/000077500000000000000000000000001300170765700164365ustar00rootroot00000000000000opensips-2.2.2/modules/pua_bla/Makefile000077500000000000000000000010211300170765700200730ustar00rootroot00000000000000# $Id$ # # PUA_BLA # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pua_bla.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/pua_bla/README000066400000000000000000000072771300170765700173330ustar00rootroot00000000000000PUA Bridged Line Appearances Anca-Maria Vamanu Edited by Anca-Maria Vamanu Copyright © 2007 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. default_domain(str) 1.3.2. header_name(str) 1.3.3. outbound_proxy(str) 1.3.4. server_address(str) 1.3.5. presence_server(str) 1.4. Exported Functions 1.4.1. bla_set_flag 1.4.2. bla_handle_notify List of Examples 1.1. Set default_domain parameter 1.2. Set header_name parameter 1.3. Set outbound_proxy parameter 1.4. Set server_address parameter 1.5. Set presence_server parameter 1.6. bla_set_flag usage 1.7. bla_handle_notify usage Chapter 1. Admin Guide 1.1. Overview The pua_bla module enables Bridged Line Appearances support according to the specifications in draft-anil-sipping-bla-03.txt. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * usrloc. * pua. * presence. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml. 1.3. Exported Parameters 1.3.1. default_domain(str) The default domain for the registered users to be used when constructing the uri for the registrar callback. Default value is “NULLâ€. Example 1.1. Set default_domain parameter ... modparam("pua_bla", "default_domain", "opensips.org") ... 1.3.2. header_name(str) The name of the header to be added to Publish requests. It will contain the uri of the user agent that sent the Notify that is transformed into Publish. It stops sending a Notification with the same information to the sender. Default value is “NULLâ€. Example 1.2. Set header_name parameter ... modparam("pua_bla", "header_name", "Sender") ... 1.3.3. outbound_proxy(str) The outbound_proxy uri to be used when sending Subscribe requests. Default value is “NULLâ€. Example 1.3. Set outbound_proxy parameter ... modparam("pua_bla", "outbound_proxy", "sip:proxy@opensips.org") ... 1.3.4. server_address(str) The IP address of the server. Example 1.4. Set server_address parameter ... modparam("pua_bla", "server_address", "sip:bla@160.34.23.12") ... 1.3.5. presence_server(str) The address of the presence server - will be used as an outbound proxy when sending PUBLISH requests. It is optional. Default value is “NULLâ€. Example 1.5. Set presence_server parameter ... modparam("pua_bla", "presence_server", "sip:pa@opensips.org") ... 1.4. Exported Functions 1.4.1. bla_set_flag The function is used to mark REGISTER requests made to a BLA AOR. The modules subscribes to the registered contacts for dialog;sla event. Example 1.6. bla_set_flag usage ... if(is_method("REGISTER") && to_uri=~"bla_aor@opensips.org") bla_set_flag(); ... 1.4.2. bla_handle_notify The function handles Notify requests sent from phones on the same BLA to the server. The message is transformed in Publish request and passed to presence module for further handling. in case of a successful processing a 2xx reply should be sent. Example 1.7. bla_handle_notify usage ... if(is_method("NOTIFY") && to_uri=~"bla_aor@opensips.org") { if( bla_handle_notify() ) t_reply("200", "OK"); } ... opensips-2.2.2/modules/pua_bla/doc/000077500000000000000000000000001300170765700172035ustar00rootroot00000000000000opensips-2.2.2/modules/pua_bla/doc/pua_bla.xml000066400000000000000000000017601300170765700213340ustar00rootroot00000000000000 %docentities; ]> PUA Bridged Line Appearances &osipsname; Anca-Maria Vamanu Anca-Maria Vamanu 2007 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/pua_bla/doc/pua_bla_admin.xml000066400000000000000000000114531300170765700225040ustar00rootroot00000000000000 &adminguide;
Overview The pua_bla module enables Bridged Line Appearances support according to the specifications in draft-anil-sipping-bla-03.txt.
Dependencies
&osips; Modules The following modules must be loaded before this module: usrloc. pua. presence.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml.
Exported Parameters
<varname>default_domain</varname>(str) The default domain for the registered users to be used when constructing the uri for the registrar callback. Default value is NULL. Set <varname>default_domain</varname> parameter ... modparam("pua_bla", "default_domain", "opensips.org") ...
<varname>header_name</varname>(str) The name of the header to be added to Publish requests. It will contain the uri of the user agent that sent the Notify that is transformed into Publish. It stops sending a Notification with the same information to the sender. Default value is NULL. Set <varname>header_name</varname> parameter ... modparam("pua_bla", "header_name", "Sender") ...
<varname>outbound_proxy</varname>(str) The outbound_proxy uri to be used when sending Subscribe requests. Default value is NULL. Set <varname>outbound_proxy</varname> parameter ... modparam("pua_bla", "outbound_proxy", "sip:proxy@opensips.org") ...
<varname>server_address</varname>(str) The IP address of the server. Set <varname>server_address</varname> parameter ... modparam("pua_bla", "server_address", "sip:bla@160.34.23.12") ...
<varname>presence_server</varname>(str) The address of the presence server - will be used as an outbound proxy when sending PUBLISH requests. It is optional. Default value is NULL. Set <varname>presence_server</varname> parameter ... modparam("pua_bla", "presence_server", "sip:pa@opensips.org") ...
Exported Functions
<function moreinfo="none">bla_set_flag</function> The function is used to mark REGISTER requests made to a BLA AOR. The modules subscribes to the registered contacts for dialog;sla event. <function>bla_set_flag</function> usage ... if(is_method("REGISTER") && to_uri=~"bla_aor@opensips.org") bla_set_flag(); ...
<function moreinfo="none">bla_handle_notify</function> The function handles Notify requests sent from phones on the same BLA to the server. The message is transformed in Publish request and passed to presence module for further handling. in case of a successful processing a 2xx reply should be sent. <function>bla_handle_notify</function> usage ... if(is_method("NOTIFY") && to_uri=~"bla_aor@opensips.org") { if( bla_handle_notify() ) t_reply("200", "OK"); } ...
opensips-2.2.2/modules/pua_bla/notify.c000066400000000000000000000152041300170765700201140ustar00rootroot00000000000000/* * pua_bla module - pua Bridged Line Appearance * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-30 initial version (Anca Vamanu) */ #include #include #include #include "../../parser/parse_content.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_from.h" #include "../pua/hash.h" #include "pua_bla.h" #define DEFAULT_EXPIRES 3600 static int bla_body_is_valid(str *bla_body) { xmlDocPtr doc = NULL; xmlNodePtr node = NULL; xmlErrorPtr xml_err = NULL; int valid = 0; doc = xmlParseMemory(bla_body->s, bla_body->len); if (!doc) { xml_err = xmlGetLastError(); LM_ERR("invalid body: %s", xml_err ? xml_err->message : "xmlParseMemory failed"); LM_DBG("invalid body content: %.*s", bla_body->len, bla_body->s); goto done; } node = doc->children; if (!node) { LM_ERR("invalid body: no XML content"); goto done; } if (node->next) { /* may only have one root dialog-info node */ LM_ERR("invalid body: multiple root elements"); goto done; } if (xmlStrcasecmp(node->name, BAD_CAST "dialog-info") != 0) { LM_ERR("invalid body: required dialog-info element " "not found (found <%s> instead)", (unsigned char *)node->name); goto done; } if (!node->children) { LM_DBG("valid blank dialog-info body"); valid = 1; goto done; } for (node = node->children; node; node = node->next) { if (node->type == XML_ELEMENT_NODE && xmlStrcasecmp(node->name, BAD_CAST "dialog") != 0) { break; } } if (node) { LM_ERR("invalid body: invalid element <%s> found", (unsigned char *)node->name); goto done; } LM_DBG("valid body"); valid = 1; done: if (doc) xmlFreeDoc(doc); return valid; } int bla_handle_notify(struct sip_msg* msg, char* s1, char* s2) { publ_info_t publ; struct to_body *pto= NULL, *pfrom = NULL; str body; ua_pres_t dialog; unsigned int expires= 0; struct hdr_field* hdr; str subs_state; str extra_headers= {0, 0}; static char buf[255]; str contact; memset(&publ, 0, sizeof(publ_info_t)); memset(&dialog, 0, sizeof(ua_pres_t)); if ( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("parsing headers\n"); return -1; } if( msg->to==NULL || msg->to->body.s==NULL) { LM_ERR("cannot parse TO header\n"); return -1; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); return -1; } publ.pres_uri= &pto->uri; dialog.watcher_uri= publ.pres_uri; if (pto->tag_value.s==NULL || pto->tag_value.len==0 ) { LM_ERR("NULL to_tag value\n"); return -1; } dialog.from_tag= pto->tag_value; if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); return -1; } dialog.call_id = msg->callid->body; if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); return -1; } if (msg->from->parsed == NULL) { LM_DBG(" 'From' header not parsed\n"); /* parsing from header */ if ( parse_from_header( msg )<0 ) { LM_DBG(" ERROR cannot parse From header\n"); return -1; } } pfrom = (struct to_body*)msg->from->parsed; dialog.to_uri= pfrom->uri; if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); return -1; } if ( get_content_length(msg) == 0 ) { LM_DBG("content length= 0\n"); return 1; } else { if ( get_body(msg,&body)!=0 || body.len==0) { LM_ERR("cannot extract body from msg\n"); return -1; } if (!bla_body_is_valid( &body )) { LM_ERR("bad XML body!"); return -1; } } if(msg->contact== NULL || msg->contact->body.s== NULL) { LM_ERR("no contact header found"); return -1; } if( parse_contact(msg->contact) <0 ) { LM_ERR(" cannot parse contact header\n"); return -1; } if(msg->contact->parsed == NULL) { LM_ERR("cannot parse contact header\n"); return -1; } contact = ((contact_body_t* )msg->contact->parsed)->contacts->uri; dialog.to_tag= pfrom->tag_value; dialog.event= BLA_EVENT; dialog.flag= BLA_SUBSCRIBE; if(pua_is_dialog(&dialog)< 0) { LM_ERR("Notify in a non existing dialog\n"); return -2; } /* parse Subscription-State and extract expires if existing */ hdr = get_header_by_static_name( msg, "Subscription-State"); if( hdr==NULL ) { LM_ERR("No Subscription-State header found\n"); return -1; } subs_state= hdr->body; if(strncasecmp(subs_state.s, "terminated", 10)== 0) expires= 0; else { if(strncasecmp(subs_state.s, "active", 6)== 0 || strncasecmp(subs_state.s, "pending", 7)==0 ) { expires = DEFAULT_EXPIRES; char* sep= NULL; str exp= {NULL, 0}; sep= strchr(subs_state.s, ';'); if(sep) { if(strncasecmp(sep+1, "expires=", 8)== 0) { exp.s= sep+ 9; sep= exp.s; while((*sep)>='0' && (*sep)<='9') { sep++; exp.len++; } if( str2int(&exp, &expires)< 0) { LM_ERR("while parsing int\n"); return -1; } } } } else { LM_ERR("unknown Subscription-state token\n"); return -1; } } /* +2 for ": " between header name and value */ if ((header_name.len + 2 + contact.len + CRLF_LEN) >= sizeof(buf)) { LM_ERR("Sender header too large"); return -1; } /* build extra_headers with Sender*/ extra_headers.s= buf; memcpy(extra_headers.s, header_name.s, header_name.len); extra_headers.len= header_name.len; memcpy(extra_headers.s+extra_headers.len,": ",2); extra_headers.len+= 2; memcpy(extra_headers.s+ extra_headers.len, contact.s, contact.len); extra_headers.len+= contact.len; memcpy(extra_headers.s+ extra_headers.len, CRLF, CRLF_LEN); extra_headers.len+= CRLF_LEN; publ.id= contact; publ.body= &body; publ.source_flag= BLA_PUBLISH; publ.expires= expires; publ.event= BLA_EVENT; publ.extra_headers= &extra_headers; publ.outbound_proxy = presence_server; if(pua_send_publish(&publ)< 0) { LM_ERR("failed to send Publish message\n"); return -1; } return 1; } opensips-2.2.2/modules/pua_bla/pua_bla.c000066400000000000000000000141331300170765700202070ustar00rootroot00000000000000/* * pua_bla module - pua Bridged Line Appearance * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-30 initial version (Anca Vamanu) */ #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../usrloc/usrloc.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "pua_bla.h" #include "registrar_cb.h" /* Structure containing pointers to pua functions */ pua_api_t pua; /* Structure containing pointers to usrloc functions */ usrloc_api_t ul; str default_domain= {NULL, 0}; str header_name= {0, 0}; str bla_outbound_proxy= {0, 0}; int is_bla_aor= 0; str reg_from_uri= {0, 0}; static int mod_init(void); static int child_init(int); static void destroy(void); send_publish_t pua_send_publish; send_subscribe_t pua_send_subscribe; query_dialog_t pua_is_dialog; int bla_set_flag(struct sip_msg* , char*, char*); str server_address= {0, 0}; str presence_server= {0, 0}; static cmd_export_t cmds[]= { {"bla_set_flag", (cmd_function)bla_set_flag, 0, 0, 0, REQUEST_ROUTE}, {"bla_handle_notify", (cmd_function)bla_handle_notify, 0, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[]= { {"server_address", STR_PARAM, &server_address.s }, {"default_domain", STR_PARAM, &default_domain.s }, {"header_name", STR_PARAM, &header_name.s }, {"outbound_proxy", STR_PARAM, &bla_outbound_proxy.s}, {"presence_server", STR_PARAM, &presence_server.s }, {0, 0, 0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "pua", DEP_ABORT }, { MOD_TYPE_DEFAULT, "usrloc", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "pua_bla", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { LM_DBG("initialize module...\n"); bind_pua_t bind_pua; bind_usrloc_t bind_usrloc; if(server_address.s== NULL) { LM_ERR("compulsory 'server_address' parameter not set!"); return -1; } server_address.len= strlen(server_address.s); if(presence_server.s) { presence_server.len= strlen(presence_server.s); } if(default_domain.s) { default_domain.len= strlen(default_domain.s); } if(header_name.s == NULL ) { LM_ERR("header_name parameter not set\n"); return -1; } header_name.len= strlen(header_name.s); if(bla_outbound_proxy.s == NULL ) { LM_DBG("No outbound proxy set\n"); } else bla_outbound_proxy.len= strlen(bla_outbound_proxy.s); bind_pua= (bind_pua_t)find_export("bind_pua", 1,0); if (!bind_pua) { LM_ERR("Can't bind pua\n"); return -1; } if (bind_pua(&pua) < 0) { LM_ERR("Can't bind pua\n"); return -1; } if(pua.send_publish == NULL) { LM_ERR("Could not import send_publish\n"); return -1; } pua_send_publish= pua.send_publish; if(pua.send_subscribe == NULL) { LM_ERR("Could not import send_subscribe\n"); return -1; } pua_send_subscribe= pua.send_subscribe; if(pua.is_dialog == NULL) { LM_ERR("Could not import send_subscribe\n"); return -1; } pua_is_dialog= pua.is_dialog; if(pua.register_puacb== NULL) { LM_ERR("Could not import register callback\n"); return -1; } bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (!bind_usrloc) { LM_ERR("Can't bind usrloc\n"); return -1; } if (bind_usrloc(&ul) < 0) { LM_ERR("Can't bind usrloc\n"); return -1; } if(ul.register_ulcb == NULL) { LM_ERR("Could not import ul_register_ulcb\n"); return -1; } if(ul.register_ulcb(UL_CONTACT_INSERT, bla_cb , 0)< 0) { LM_ERR("can not register callback for" " insert\n"); return -1; } if(ul.register_ulcb(UL_CONTACT_EXPIRE, bla_cb, 0)< 0) { LM_ERR("can not register callback for" " insert\n"); return -1; } if(ul.register_ulcb(UL_CONTACT_UPDATE, bla_cb, 0)< 0) { LM_ERR("can not register callback for" " update\n"); return -1; } if(ul.register_ulcb(UL_CONTACT_DELETE, bla_cb, 0)< 0) { LM_ERR("can not register callback for" " delete\n"); return -1; } return 0; } static int child_init(int rank) { LM_DBG("child [%d] pid [%d]\n", rank, getpid()); return 0; } static void destroy(void) { LM_DBG("destroying module ...\n"); return ; } int bla_set_flag(struct sip_msg* msg , char* s1, char* s2) { LM_DBG("mark as bla aor\n"); is_bla_aor= 1; if( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("parsing headers\n"); return -1; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_DBG("cannot parse From header\n"); return -1; } } reg_from_uri= ((struct to_body*)(msg->from->parsed))->uri; return 1; } opensips-2.2.2/modules/pua_bla/pua_bla.h000066400000000000000000000025201300170765700202110ustar00rootroot00000000000000/* * pua_bla module - pua Bridged Line Appearance * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-30 initial version (anca) */ #ifndef PUA_BLA_H #define PUA_BLA_H #include "../pua/pua_bind.h" extern int is_bla_aor; extern str header_name; extern str bla_outbound_proxy; extern str server_address; extern str presence_server; extern str reg_from_uri; extern send_publish_t pua_send_publish; extern send_subscribe_t pua_send_subscribe; extern query_dialog_t pua_is_dialog; extern int bla_handle_notify(struct sip_msg* msg, char* s1, char* s2); #endif opensips-2.2.2/modules/pua_bla/registrar_cb.c000066400000000000000000000056671300170765700212660ustar00rootroot00000000000000/* * pua_bla module - pua Bridged Line Appearance * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-30 initial version (anca) */ #include #include #include "../../dprint.h" #include "../pua/pua.h" #include "registrar_cb.h" #include "pua_bla.h" void bla_cb(ucontact_t* c, int type, void* param) { subs_info_t subs; str uri={0, 0}; char* at; LM_DBG("start\n"); if(is_bla_aor== 0) { LM_DBG("Not a recognized BLA AOR\n"); return ; } if(type & UL_CONTACT_INSERT) LM_DBG("type= UL_CONTACT_INSERT\n"); else if(type & UL_CONTACT_UPDATE) LM_DBG("type= UL_CONTACT_UPDATE\n"); else if(type & UL_CONTACT_EXPIRE) LM_DBG("type= UL_CONTACT_EXPIRE\n"); else if(type & UL_CONTACT_DELETE) LM_DBG("type= UL_CONTACT_DELETE\n"); memset(&subs, 0, sizeof(subs_info_t)); subs.pres_uri= &c->c; subs.to_uri= reg_from_uri; uri.s = (char*)pkg_malloc(sizeof(char)*(c->aor->len+default_domain.len+6)); if(uri.s == NULL) { LM_ERR( "pkg_malloc uri failed\n" ); goto error; } memcpy(uri.s, "sip:", 4); uri.len = 4; memcpy(uri.s+ uri.len, c->aor->s, c->aor->len); uri.len+= c->aor->len; at = memchr(c->aor->s, '@', c->aor->len); if(!at) { if(default_domain.s == NULL) { LM_ERR("No domain found in register and default_domain module" " parameter not set\n"); goto error; } LM_DBG("domain not found - added default= %.*s\n", default_domain.len, default_domain.s); uri.s[uri.len++]= '@'; memcpy(uri.s+ uri.len, default_domain.s, default_domain.len); uri.len+= default_domain.len; } subs.watcher_uri= &uri; if(type & UL_CONTACT_DELETE || type & UL_CONTACT_EXPIRE ) subs.expires= 0; else subs.expires= c->expires - (int)time(NULL); subs.source_flag= BLA_SUBSCRIBE; subs.event= BLA_EVENT; subs.contact= &server_address; if(bla_outbound_proxy.s && bla_outbound_proxy.len) subs.outbound_proxy= &bla_outbound_proxy; else if(c->received.s && c->received.len) subs.outbound_proxy= &c->received; if(type & UL_CONTACT_INSERT) subs.flag|= INSERT_TYPE; else subs.flag|= UPDATE_TYPE; if(pua_send_subscribe(&subs)< 0) { LM_ERR("while sending subscribe\n"); } pkg_free(uri.s); error: is_bla_aor= 0; return ; } opensips-2.2.2/modules/pua_bla/registrar_cb.h000066400000000000000000000020671300170765700212620ustar00rootroot00000000000000/* * pua_bla module - pua Bridged Line Appearance * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-30 initial version (anca) */ #include #include #include "../../dprint.h" #include "../usrloc/usrloc.h" void bla_cb(ucontact_t* c, int type, void* param); opensips-2.2.2/modules/pua_dialoginfo/000077500000000000000000000000001300170765700200135ustar00rootroot00000000000000opensips-2.2.2/modules/pua_dialoginfo/Makefile000066400000000000000000000005361300170765700214570ustar00rootroot00000000000000# $Id$ # # PUBLISH # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pua_dialoginfo.so DEFS+=-I$(SYSBASE)/include/libxml2 -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS=-L$(SYSBASE)/include/lib -L$(LOCALBASE)/lib -lxml2 include ../../Makefile.modules opensips-2.2.2/modules/pua_dialoginfo/README000066400000000000000000000370511300170765700207010ustar00rootroot00000000000000pua dialoginfo Anca-Maria Vamanu Edited by Anca-Maria Vamanu Klaus Darilion Edited by Klaus Darilion Ovidiu Sas Edited by Ovidiu Sas Copyright © 2006 Voice Sistem SRL Copyright © 2008 Klaus Darilion IPCom Copyright © 2014 VoIP Embedded, Inc. Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. include_callid (int) 1.3.2. include_tags (int) 1.3.3. include_localremote (int) 1.3.4. caller_confirmed (int) 1.3.5. publish_on_trying (int) 1.3.6. nopublish_flag (int) 1.3.7. presence_server (string) 1.3.8. caller_spec_param (string) 1.3.9. callee_spec_param (string) 1.3.10. osips_ps (int) 1.4. Exported Functions 1.4.1. dialoginfo_set([side]) List of Examples 1.1. Set include_callid parameter 1.2. Set include_tags parameter 1.3. Set include_localremote parameter 1.4. Set caller_confirmed parameter 1.5. Set publish_on_trying parameter to 0 1.6. Set publish_on_trying parameter to 1 1.7. Set nopublish_flag parameter 1.8. Set presence_server parameter 1.9. Set caller_spec_param parameter 1.10. Set caller_spec_param parameter 1.11. Set osips_ps parameter 1.12. dialoginfo_set usage Chapter 1. Admin Guide 1.1. Overview The pua_dialoginfo retrieves dialog state information from the dialog module and PUBLISHes the dialog-information using the pua module. Thus, in combination with the presence_xml module this can be used to derive dialog-info from the dialog module and NOTIFY the subscribed watchers about dialog-info changes. This can be used for example with SNOM and Linksys phones. Note: This implements dialog-info according to RFC 4235 and is not compatible with the BLA feature defined in draft-anil-sipping-bla-03.txt. (Actually the BLA draft is really crap as it changes SIP semantics) The module is based on code (copy/paste) from pua_usrloc and nat_traversal module. Following you will show some examples of an dialog-info XML document taken from RFC 4235. This will help you to understand the meaning of the module parameters: early The root element is the "dialog-info". It contains the namespace, the version (which must be incremented for each new PUBLISH for this certain dialog), the state (this module only supports state=full) and the entity for which we publish the dialog-info. The "dialog" element must contain an id parameter. The id parameter is usually different to the optional call-id parameter (which is the call-id of the INVITE request) as an INVITE can create multiple dialogs (forked request). But as the dialog module does not support multiple dialogs created by a single transaction, the pua_dialoginfo module sets the id parameter to the same value as the call-id parameter. The "local-tag" indicates the local tag of the entity. The remote-tag indicates the tag of the remote party. The "direction" indicates if the entity was the initator of the dialog or the recepient (aka if the entity sent or received the first INVITE). The "state" element describes the state of the dialog state machine and must be either: trying, proceeding, early, confirmed or terminated. The dialog element can contain optional "local" and "remote" elements which describes the local and the remote party in more detail, for example: early sip:alice@example.com sip:bob@example.org The local and remote elements are needed to implement call pickup. For example if the above XML document is received by somebody who SUBSCRIBEd the dialog-info of Alice, then it can pick-up the call by sending an INVITE to Bob (actually I am not sure if it should use the URI in the identity element or the URI in the target parameter) which contains a Replaces header which contains the call-id and the tags. This was tested sucessfully with Linksys SPA962 phones and with SNOM 320 Firmware 7.3.7 (you have to set the function key to "Extension"). A dialog-info XML document may contain multiple "dialog" elements, for example if the entity has multiple ongoing dialogs. For example the following XML document shows a confirmed dialog and an early (probably a second incoming call) dialog. confirmed early To enable dialoginfo notifications for a certain dialog, you must call dialoginfo_set() function for that dialog. This function can take one parameter which through which you can tell the module to publish dialoginfo only for one side of the call. This is useful because you want to store dialoginfo only for the local users, and you can decide from the script if the call parties are local users and give the correct parameter to this function to tell it to send generate dialoginfo only for the local users. The possible values are : "A" - corresponding to generate dialoginfo only for the caller and "B" - generate dialoginfo only for the callee. If no parameter is given, the module will generate dialoginfo for both parties. It is possible to specify what URIs should be used for caller and callee by setting the the pseudovariables with the names defined as module parameter "caller_spec_param" and "callee_spec_param" before calling dialoginfo_set function. Please read the description of this parameters in Exported Parameters section. If this parameters are not set, the default sources will be used, From header for the caller and display name in To header + RURI for the callee. As the dialog module callbacks only address a certain dialog, the pua_dialoginfo always PUBLISHes XML documents with a single "dialog" element. If an entity has multiple concurrent dialogs, the pua_dialoginfo module will send PUBLISH for each dialog. These multiple "presenties" can be aggregated by the presence_dialoginfo module into a single XML document with multiple "dialog" elements. Please see the description of the presence_dialoginfo module for details about the aggregation. If there are problems with the callbacks from dialog module and you want to debug them you define PUA_DIALOGINFO_DEBUG in pua_dialoginfo.c and recompile. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * dialog. * pua. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml. 1.3. Exported Parameters 1.3.1. include_callid (int) If this parameter is set, the optional call-id will be put into the dialog element. This is needed for call-pickup features. Default value is “1â€. Example 1.1. Set include_callid parameter ... modparam("pua_dialoginfo", "include_callid", 0) ... 1.3.2. include_tags (int) If this parameter is set, the local and remote tag will be put into the dialog element. This is needed for call-pickup features. Default value is “1â€. Example 1.2. Set include_tags parameter ... modparam("pua_dialoginfo", "include_tags", 0) ... 1.3.3. include_localremote (int) If this parameter is set, the optional local and remote elements will be put into the dialog element. This is needed for call-pickup features. Default value is “1â€. Example 1.3. Set include_localremote parameter ... modparam("pua_dialoginfo", "include_localremote", 0) ... 1.3.4. caller_confirmed (int) Usually the dialog-info of the caller will be "trying -> early -> confirmed" and the dialog-info of the callee will be "early -> confirmed". On some phones the function LED will start blinking if the state is early, regardless if is is the caller or the callee (indicated with the "direction" parameter). To avoid blinking LEDs for the caller, you can enable this parameter. Then the state of the caller will be singaled as "confirmed" even in "early" state. This is a workaround for the buggy Linksys SPA962 phones. SNOM phones work well with the default setting. Default value is “0â€. Example 1.4. Set caller_confirmed parameter ... modparam("pua_dialoginfo", "caller_confirmed", 1) ... 1.3.5. publish_on_trying (int) Usually the dialog-info of the caller will be "trying -> early -> confirmed". The "trying" state will be triggered as soon as you call dialoginfo_set on the caller, while "early" is triggered as soon as the callee is ringing (triggered by a 180 or 183 provisional reply). Sometimes, it is advisable to be notified only when the callee reaches the early state and not before. In other cases, it is advisable to notify the early state. This setting allows controlling the behavior. The intended purpose of this parameter is to reduce the rate of notifications (see RFC4235, section 3.10. Rate of Notifications). Default value is “0â€. Example 1.5. Set publish_on_trying parameter to 0 ... modparam("pua_dialoginfo", "publish_on_trying", 0) # Successful call scenario: # # UAC proxy UAS presence server # |--INVITE->| | | # |<-100-----|--INVITE->| | # | |<-100-----| | # | | | | # | |<-18x-----| | # |<-18x-----|--PUBLISH(early)------>| # | | | | # | |<-200-----| | # |<-200-----|--PUBLISH(confirmed)-->| # |--ACK---->| | | # | |--ACK---->| | # | | | | # # # Unsuccessful call scenario: # # UAC proxy UAS presence server # |--INVITE->| | | # |<-100-----|--INVITE->| | # | |<-100-----| | # | | | | # | |<-456xx---| | # |<-456xx---|--ACK---->| | # |--ACK---->| | | ... Example 1.6. Set publish_on_trying parameter to 1 ... modparam("pua_dialoginfo", "publish_on_trying", 1) # Successful call scenario: # # UAC proxy UAS presence server # |--INVITE->| | | # |<-100-----|--INVITE->| | # | |--PUBLISH(trying)----->| # | |<-100-----| | # | | | | # | |<-18x-----| | # |<-18x-----|--PUBLISH(early)------>| # | | | | # | |<-200-----| | # |<-200-----|--PUBLISH(confirmed)-->| # |--ACK---->| | | # | |--ACK---->| | # | | | | # # # Unsuccessful call scenario: # # UAC proxy UAS presence server # |--INVITE->| | | # |<-100-----|--INVITE->| | # | |--PUBLISH(trying)----->| # | |<-100-----| | # | | | | # | |<-456xx---| | # | |--PUBLISH(terminated)->| # |<-456xx---|--ACK---->| | # |--ACK---->| | | ... 1.3.6. nopublish_flag (int) By default, reINVITEs will trigger a PUBLISH. They are actually the only in-dialog request for which it makes sense. In some cases, it does not make sense to republish a dialog state. (e.g. when handling a B2BUA reINVITE). This setting defines the flag that needs to be set in the request route to prevent the generation of a PUBLISH request in case of a specific reINVITE. Example 1.7. Set nopublish_flag parameter ... modparam("pua_dialoginfo", "nopublish_flag", 5) ... 1.3.7. presence_server (string) The address of the presence server, where the PUBLISH messages should be sent (not compulsory). Example 1.8. Set presence_server parameter ... modparam("pua_dialoginfo", "presence_server", "sip:ps@opensips.org:5060" ) ... 1.3.8. caller_spec_param (string) The name of the pseudovariable that will hold a custom caller URI. If this variable is not set, the information in From header is used. If you want to use another caller definition, you have to fill in this pseudovariable before calling dialoginfo_set() function. The format of the string resemples the format of To/From SIP headers: "display_name" or "sip_uri". Example 1.9. Set caller_spec_param parameter ... modparam("pua_dialoginfo", "caller_spec_param", "$avp(10)") ... 1.3.9. callee_spec_param (string) The name of the pseudovariable that will hold the callee URI. If this variable will not be set, the callee information used will be made of To display uri + RURI. the. The format of the string to set this pseudovariable to is the same as described in caller_spec_param section. Example 1.10. Set caller_spec_param parameter ... modparam("pua_dialoginfo", "callee_spec_param", "$avp(11)") ... 1.3.10. osips_ps (int) It is advisable to specify if you use a different presence server than OpenSIPS presence server, by setting this parameter to 0. By default, a trick (version in the Publish body is set '0000000') is used when working with Opensips Presence Server to make the processing faster and this might not be accepted by other presence servers. Default value is “1â€. Example 1.11. Set osips_ps parameter ... modparam("pua_dialoginfo", "osips_ps", 0) ... 1.4. Exported Functions 1.4.1. dialoginfo_set([side]) This function must be called for INVITE messages that initialize a dialog for which dialoginfo information must be published. Meaning of the parameters: * side (optional) - can be "A" or "B" for caller or callee PUBLISH only - if missing, both sides will be published. Example 1.12. dialoginfo_set usage ... if(is_method("INVITE")) if(uri =~ "opensips.org") dialoginfo_set(); ... opensips-2.2.2/modules/pua_dialoginfo/dialog_publish.c000066400000000000000000000221171300170765700231470ustar00rootroot00000000000000/* * pua_dialoginfo module - sending publish with dialog info from dialog module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "../../parser/parse_expires.h" #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../name_alias.h" #include "../../socket_info.h" #include "../usrloc/usrloc.h" #include "../usrloc/ul_callback.h" #include "../tm/tm_load.h" #include "../pua/pua.h" #include "pua_dialoginfo.h" #define PRES_ID_LEN 64 /* global modul parameters */ extern int include_callid; extern int include_localremote; extern int include_tags; /* for debug purpose only */ void print_publ(publ_info_t* p) { LM_DBG("publ:\n"); LM_DBG("uri= %.*s\n", p->pres_uri->len, p->pres_uri->s); LM_DBG("id= %.*s\n", p->id.len, p->id.s); LM_DBG("expires= %d\n", p->expires); } str* build_dialoginfo(char *state, struct to_body *entity, struct to_body *peer, str *callid, unsigned int initiator, str *localtag, str *remotetag) { xmlDocPtr doc = NULL; xmlNodePtr root_node = NULL; xmlNodePtr dialog_node = NULL; xmlNodePtr state_node = NULL; xmlNodePtr remote_node = NULL; xmlNodePtr local_node = NULL; xmlNodePtr tag_node = NULL; xmlNodePtr id_node = NULL; str *body= NULL; char buf[MAX_URI_SIZE+1]; if (entity->uri.len > MAX_URI_SIZE) { LM_ERR("entity URI '%.*s' too long, maximum=%d\n",entity->uri.len, entity->uri.s, MAX_URI_SIZE); return NULL; } memcpy(buf, entity->uri.s, entity->uri.len); buf[entity->uri.len]= '\0'; /* create the Publish body */ doc = xmlNewDoc(BAD_CAST "1.0"); if(doc==0) return NULL; root_node = xmlNewNode(NULL, BAD_CAST "dialog-info"); if(root_node==0) goto error; xmlDocSetRootElement(doc, root_node); xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:dialog-info"); xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "partial" ); xmlNewProp(root_node, BAD_CAST "entity", BAD_CAST buf); /* version is set by dialoginfo_process_body() */ /* RFC 3245 differs between id and call-id. For example if a call is forked and 2 early dialogs are established, we should send 2 PUBLISH requests, both have the same call-id but different id. Thus, id could be for example derived from the totag. Currently the dialog module does not support multiple dialogs. Thus, it does no make sense to differ here between multiple dialog. Thus, id and call-id will be populated identically */ /* dialog tag */ dialog_node =xmlNewChild(root_node, NULL, BAD_CAST "dialog", NULL) ; if( dialog_node ==NULL) { LM_ERR("while adding child\n"); goto error; } if (callid->len > MAX_URI_SIZE) { LM_ERR("call-id '%.*s' too long, maximum=%d\n", callid->len, callid->s, MAX_URI_SIZE); return NULL; } memcpy(buf, callid->s, callid->len); buf[callid->len]= '\0'; xmlNewProp(dialog_node, BAD_CAST "id", BAD_CAST buf); if (include_callid) { xmlNewProp(dialog_node, BAD_CAST "call-id", BAD_CAST buf); } if (include_tags) { if (localtag && localtag->s) { if (localtag->len > MAX_URI_SIZE) { LM_ERR("localtag '%.*s' too long, maximum=%d\n", localtag->len, localtag->s, MAX_URI_SIZE); return NULL; } memcpy(buf, localtag->s, localtag->len); buf[localtag->len]= '\0'; xmlNewProp(dialog_node, BAD_CAST "local-tag", BAD_CAST buf); } if (remotetag && remotetag->s) { if (remotetag->len > MAX_URI_SIZE) { LM_ERR("remotetag '%.*s' too long, maximum=%d\n", remotetag->len, remotetag->s, MAX_URI_SIZE); return NULL; } memcpy(buf, remotetag->s, remotetag->len); buf[remotetag->len]= '\0'; xmlNewProp(dialog_node, BAD_CAST "remote-tag", BAD_CAST buf); } } if (initiator) { xmlNewProp(dialog_node, BAD_CAST "direction", BAD_CAST "initiator"); }else { xmlNewProp(dialog_node, BAD_CAST "direction", BAD_CAST "recipient"); } /* state tag */ state_node = xmlNewChild(dialog_node, NULL, BAD_CAST "state", BAD_CAST state) ; if( state_node ==NULL) { LM_ERR("while adding child\n"); goto error; } if (include_localremote) { /* remote tag*/ remote_node = xmlNewChild(dialog_node, NULL, BAD_CAST "remote", NULL) ; if( remote_node ==NULL) { LM_ERR("while adding child\n"); goto error; } if (peer->uri.len > MAX_URI_SIZE) { LM_ERR("peer '%.*s' too long, maximum=%d\n", peer->uri.len, peer->uri.s, MAX_URI_SIZE); return NULL; } memcpy(buf, peer->uri.s, peer->uri.len); buf[peer->uri.len]= '\0'; id_node = xmlNewChild(remote_node, NULL, BAD_CAST "identity", BAD_CAST buf) ; if( id_node ==NULL) { LM_ERR("while adding child\n"); goto error; } tag_node = xmlNewChild(remote_node, NULL, BAD_CAST "target", NULL) ; if( tag_node ==NULL) { LM_ERR("while adding child\n"); goto error; } xmlNewProp(tag_node, BAD_CAST "uri", BAD_CAST buf); /* if a display name present - add the display name information */ if(peer->display.s) { if(peer->display.len > MAX_URI_SIZE) { LM_ERR("display '%.*s' too long, maximum=%d\n", peer->display.len, peer->display.s, MAX_URI_SIZE); return NULL; } if(peer->display.s[0] == '"') { memcpy(buf, peer->display.s+1, peer->display.len-2); buf[peer->display.len-2] = '\0'; } else { memcpy(buf, peer->display.s, peer->display.len); buf[peer->display.len] = '\0'; } xmlNewProp(id_node, BAD_CAST "display", BAD_CAST buf); } /* local tag */ local_node = xmlNewChild(dialog_node, NULL, BAD_CAST "local", NULL) ; if( local_node ==NULL) { LM_ERR("while adding child\n"); goto error; } memcpy(buf, entity->uri.s, entity->uri.len); buf[entity->uri.len]= '\0'; id_node = xmlNewChild(local_node, NULL, BAD_CAST "identity", BAD_CAST buf) ; if( id_node ==NULL) { LM_ERR("while adding child\n"); goto error; } tag_node = xmlNewChild(local_node, NULL, BAD_CAST "target", NULL) ; if( tag_node ==NULL) { LM_ERR("while adding child\n"); goto error; } xmlNewProp(tag_node, BAD_CAST "uri", BAD_CAST buf); /* if a display name present - add the display name information */ if(entity->display.s) { if(entity->display.len > MAX_URI_SIZE) { LM_ERR("display '%.*s' too long, maximum=%d\n", entity->display.len, entity->display.s, MAX_URI_SIZE); return NULL; } if(entity->display.s[0] == '"') { memcpy(buf, entity->display.s+1, entity->display.len-2); buf[entity->display.len-2] = '\0'; } else { memcpy(buf, entity->display.s, entity->display.len); buf[entity->display.len] = '\0'; } xmlNewProp(id_node, BAD_CAST "display", BAD_CAST buf); } } /* create the body */ body = (str*)pkg_malloc(sizeof(str)); if(body == NULL) { LM_ERR("while allocating memory\n"); return NULL; } memset(body, 0, sizeof(str)); xmlDocDumpMemory(doc,(unsigned char**)(void*)&body->s,&body->len); LM_DBG("new_body:\n%.*s\n",body->len, body->s); /*free the document */ xmlFreeDoc(doc); xmlCleanupParser(); return body; error: if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } if(doc) xmlFreeDoc(doc); return NULL; } void dialog_publish(char *state, struct to_body* entity, struct to_body *peer, str *callid, unsigned int initiator, unsigned int lifetime, str *localtag, str *remotetag) { str* body= NULL; publ_info_t publ; int ret_code; body= build_dialoginfo(state, entity, peer, callid, initiator, localtag, remotetag); if(body == NULL || body->s == NULL) { LM_ERR("failed to construct dialoginfo body\n"); goto error; } memset(&publ, 0, sizeof(publ_info_t)); publ.pres_uri= &entity->uri; publ.body = body; publ.id = *callid; publ.content_type.s= "application/dialog-info+xml"; publ.content_type.len= 27; publ.expires= lifetime; /* make UPDATE_TYPE, as if this "publish dialog" is not found by pua it will fallback to INSERT_TYPE anyway */ publ.flag|= UPDATE_TYPE; publ.source_flag|= DIALOG_PUBLISH; publ.event|= DIALOG_EVENT; publ.extra_headers= NULL; publ.outbound_proxy = presence_server; print_publ(&publ); ret_code = pua_send_publish(&publ); switch (ret_code) { case ERR_PUBLISH_NO_ERROR: case ERR_PUBLISH_NO_RECORD: break; default: LM_ERR("sending publish failed for pres_uri [%.*s] to server [%.*s]\n", publ.pres_uri->len, publ.pres_uri->s, publ.outbound_proxy.len, publ.outbound_proxy.s); } error: if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } return; } opensips-2.2.2/modules/pua_dialoginfo/doc/000077500000000000000000000000001300170765700205605ustar00rootroot00000000000000opensips-2.2.2/modules/pua_dialoginfo/doc/pua_dialoginfo.xml000066400000000000000000000030701300170765700242620ustar00rootroot00000000000000 %docentities; ]> pua dialoginfo &osips; Anca-Maria Vamanu Anca-Maria Vamanu Klaus Darilion Klaus Darilion Ovidiu Sas Ovidiu Sas 2006 &voicesystem; 2008 Klaus Darilion IPCom 2014 VoIP Embedded, Inc. $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/pua_dialoginfo/doc/pua_dialoginfo_admin.xml000066400000000000000000000424631300170765700254430ustar00rootroot00000000000000 &adminguide;
Overview The pua_dialoginfo retrieves dialog state information from the dialog module and PUBLISHes the dialog-information using the pua module. Thus, in combination with the presence_xml module this can be used to derive dialog-info from the dialog module and NOTIFY the subscribed watchers about dialog-info changes. This can be used for example with SNOM and Linksys phones. Note: This implements dialog-info according to RFC 4235 and is not compatible with the BLA feature defined in draft-anil-sipping-bla-03.txt. (Actually the BLA draft is really crap as it changes SIP semantics) The module is based on code (copy/paste) from pua_usrloc and nat_traversal module. Following you will show some examples of an dialog-info XML document taken from RFC 4235. This will help you to understand the meaning of the module parameters: early ]]> The root element is the "dialog-info". It contains the namespace, the version (which must be incremented for each new PUBLISH for this certain dialog), the state (this module only supports state=full) and the entity for which we publish the dialog-info. The "dialog" element must contain an id parameter. The id parameter is usually different to the optional call-id parameter (which is the call-id of the INVITE request) as an INVITE can create multiple dialogs (forked request). But as the dialog module does not support multiple dialogs created by a single transaction, the pua_dialoginfo module sets the id parameter to the same value as the call-id parameter. The "local-tag" indicates the local tag of the entity. The remote-tag indicates the tag of the remote party. The "direction" indicates if the entity was the initator of the dialog or the recepient (aka if the entity sent or received the first INVITE). The "state" element describes the state of the dialog state machine and must be either: trying, proceeding, early, confirmed or terminated. The dialog element can contain optional "local" and "remote" elements which describes the local and the remote party in more detail, for example: early sip:alice@example.com sip:bob@example.org ]]> The local and remote elements are needed to implement call pickup. For example if the above XML document is received by somebody who SUBSCRIBEd the dialog-info of Alice, then it can pick-up the call by sending an INVITE to Bob (actually I am not sure if it should use the URI in the identity element or the URI in the target parameter) which contains a Replaces header which contains the call-id and the tags. This was tested sucessfully with Linksys SPA962 phones and with SNOM 320 Firmware 7.3.7 (you have to set the function key to "Extension"). A dialog-info XML document may contain multiple "dialog" elements, for example if the entity has multiple ongoing dialogs. For example the following XML document shows a confirmed dialog and an early (probably a second incoming call) dialog. confirmed early ]]> To enable dialoginfo notifications for a certain dialog, you must call dialoginfo_set() function for that dialog. This function can take one parameter which through which you can tell the module to publish dialoginfo only for one side of the call. This is useful because you want to store dialoginfo only for the local users, and you can decide from the script if the call parties are local users and give the correct parameter to this function to tell it to send generate dialoginfo only for the local users. The possible values are : "A" - corresponding to generate dialoginfo only for the caller and "B" - generate dialoginfo only for the callee. If no parameter is given, the module will generate dialoginfo for both parties. It is possible to specify what URIs should be used for caller and callee by setting the the pseudovariables with the names defined as module parameter "caller_spec_param" and "callee_spec_param" before calling dialoginfo_set function. Please read the description of this parameters in Exported Parameters section. If this parameters are not set, the default sources will be used, From header for the caller and display name in To header + RURI for the callee. As the dialog module callbacks only address a certain dialog, the pua_dialoginfo always PUBLISHes XML documents with a single "dialog" element. If an entity has multiple concurrent dialogs, the pua_dialoginfo module will send PUBLISH for each dialog. These multiple "presenties" can be aggregated by the presence_dialoginfo module into a single XML document with multiple "dialog" elements. Please see the description of the presence_dialoginfo module for details about the aggregation. If there are problems with the callbacks from dialog module and you want to debug them you define PUA_DIALOGINFO_DEBUG in pua_dialoginfo.c and recompile.
Dependencies
&osips; Modules The following modules must be loaded before this module: dialog. pua.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml.
Exported Parameters
<varname>include_callid</varname> (int) If this parameter is set, the optional call-id will be put into the dialog element. This is needed for call-pickup features. Default value is 1. Set <varname>include_callid</varname> parameter ... modparam("pua_dialoginfo", "include_callid", 0) ...
<varname>include_tags</varname> (int) If this parameter is set, the local and remote tag will be put into the dialog element. This is needed for call-pickup features. Default value is 1. Set <varname>include_tags</varname> parameter ... modparam("pua_dialoginfo", "include_tags", 0) ...
<varname>include_localremote</varname> (int) If this parameter is set, the optional local and remote elements will be put into the dialog element. This is needed for call-pickup features. Default value is 1. Set <varname>include_localremote</varname> parameter ... modparam("pua_dialoginfo", "include_localremote", 0) ...
<varname>caller_confirmed</varname> (int) Usually the dialog-info of the caller will be "trying -> early -> confirmed" and the dialog-info of the callee will be "early -> confirmed". On some phones the function LED will start blinking if the state is early, regardless if is is the caller or the callee (indicated with the "direction" parameter). To avoid blinking LEDs for the caller, you can enable this parameter. Then the state of the caller will be singaled as "confirmed" even in "early" state. This is a workaround for the buggy Linksys SPA962 phones. SNOM phones work well with the default setting. Default value is 0. Set <varname>caller_confirmed</varname> parameter ... modparam("pua_dialoginfo", "caller_confirmed", 1) ...
<varname>publish_on_trying</varname> (int) Usually the dialog-info of the caller will be "trying -> early -> confirmed". The "trying" state will be triggered as soon as you call dialoginfo_set on the caller, while "early" is triggered as soon as the callee is ringing (triggered by a 180 or 183 provisional reply). Sometimes, it is advisable to be notified only when the callee reaches the early state and not before. In other cases, it is advisable to notify the early state. This setting allows controlling the behavior. The intended purpose of this parameter is to reduce the rate of notifications (see RFC4235, section 3.10. Rate of Notifications). Default value is 0. Set <varname>publish_on_trying</varname> parameter to 0 ... modparam("pua_dialoginfo", "publish_on_trying", 0) # Successful call scenario: # # UAC proxy UAS presence server # |--INVITE->| | | # |<-100-----|--INVITE->| | # | |<-100-----| | # | | | | # | |<-18x-----| | # |<-18x-----|--PUBLISH(early)------>| # | | | | # | |<-200-----| | # |<-200-----|--PUBLISH(confirmed)-->| # |--ACK---->| | | # | |--ACK---->| | # | | | | # # # Unsuccessful call scenario: # # UAC proxy UAS presence server # |--INVITE->| | | # |<-100-----|--INVITE->| | # | |<-100-----| | # | | | | # | |<-456xx---| | # |<-456xx---|--ACK---->| | # |--ACK---->| | | ... Set <varname>publish_on_trying</varname> parameter to 1 ... modparam("pua_dialoginfo", "publish_on_trying", 1) # Successful call scenario: # # UAC proxy UAS presence server # |--INVITE->| | | # |<-100-----|--INVITE->| | # | |--PUBLISH(trying)----->| # | |<-100-----| | # | | | | # | |<-18x-----| | # |<-18x-----|--PUBLISH(early)------>| # | | | | # | |<-200-----| | # |<-200-----|--PUBLISH(confirmed)-->| # |--ACK---->| | | # | |--ACK---->| | # | | | | # # # Unsuccessful call scenario: # # UAC proxy UAS presence server # |--INVITE->| | | # |<-100-----|--INVITE->| | # | |--PUBLISH(trying)----->| # | |<-100-----| | # | | | | # | |<-456xx---| | # | |--PUBLISH(terminated)->| # |<-456xx---|--ACK---->| | # |--ACK---->| | | ...
<varname>nopublish_flag</varname> (int) By default, reINVITEs will trigger a PUBLISH. They are actually the only in-dialog request for which it makes sense. In some cases, it does not make sense to republish a dialog state. (e.g. when handling a B2BUA reINVITE). This setting defines the flag that needs to be set in the request route to prevent the generation of a PUBLISH request in case of a specific reINVITE. Set <varname>nopublish_flag</varname> parameter ... modparam("pua_dialoginfo", "nopublish_flag", 5) ...
<varname>presence_server</varname> (string) The address of the presence server, where the PUBLISH messages should be sent (not compulsory). Set <varname>presence_server</varname> parameter ... modparam("pua_dialoginfo", "presence_server", "sip:ps@opensips.org:5060") ...
<varname>caller_spec_param</varname> (string) The name of the pseudovariable that will hold a custom caller URI. If this variable is not set, the information in From header is used. If you want to use another caller definition, you have to fill in this pseudovariable before calling dialoginfo_set() function. The format of the string resemples the format of To/From SIP headers: "display_name<sip_uri>" or "sip_uri". Set <varname>caller_spec_param</varname> parameter ... modparam("pua_dialoginfo", "caller_spec_param", "$avp(10)") ...
<varname>callee_spec_param</varname> (string) The name of the pseudovariable that will hold the callee URI. If this variable will not be set, the callee information used will be made of To display uri + RURI. the. The format of the string to set this pseudovariable to is the same as described in caller_spec_param section. Set <varname>caller_spec_param</varname> parameter ... modparam("pua_dialoginfo", "callee_spec_param", "$avp(11)") ...
<varname>osips_ps</varname> (int) It is advisable to specify if you use a different presence server than OpenSIPS presence server, by setting this parameter to 0. By default, a trick (version in the Publish body is set '0000000') is used when working with Opensips Presence Server to make the processing faster and this might not be accepted by other presence servers. Default value is 1. Set <varname>osips_ps</varname> parameter ... modparam("pua_dialoginfo", "osips_ps", 0) ...
Exported Functions
<function moreinfo="none">dialoginfo_set([side])</function> This function must be called for INVITE messages that initialize a dialog for which dialoginfo information must be published. Meaning of the parameters: side (optional) - can be "A" or "B" for caller or callee PUBLISH only - if missing, both sides will be published. <function>dialoginfo_set</function> usage ... if(is_method("INVITE")) if(uri =~ "opensips.org") dialoginfo_set(); ...
opensips-2.2.2/modules/pua_dialoginfo/pua_dialoginfo.c000066400000000000000000000537741300170765700231570ustar00rootroot00000000000000/* * pua_dialoginfo module - publish dialog-info from dialog module * * Copyright (C) 2006 Voice Sistem S.R.L. * Copyright (C) 2007-2008 Dan Pascu * Copyright (C) 2008 Klaus Darilion IPCom * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-08-25 initial version (kd) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../script_cb.h" #include "../../parser/parse_expires.h" #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../trim.h" #include "../../mem/mem.h" #include "../../pt.h" #include "../../parser/parse_from.h" #include "../dialog/dlg_load.h" #include "../dialog/dlg_hash.h" #include "../pua/pua_bind.h" #include "pua_dialoginfo.h" /* Default module parameter values */ #define DEF_INCLUDE_CALLID 1 #define DEF_INCLUDE_LOCALREMOTE 1 #define DEF_INCLUDE_TAGS 1 #define DEF_CALLER_ALWAYS_CONFIRMED 0 #define DEFAULT_CREATED_LIFETIME 3600 /* define PUA_DIALOGINFO_DEBUG to activate more verbose * logging and dialog info callback debugging */ /* #define PUA_DIALOGINFO_DEBUG 1 */ #define DLG_PUB_A 'A' /* caller */ #define DLG_PUB_B 'B' /* callee */ #define DLG_PUB_AB 'D' /* default*/ pua_api_t pua; struct dlg_binds dlg_api; /* Module parameter variables */ int include_callid = DEF_INCLUDE_CALLID; int include_localremote = DEF_INCLUDE_LOCALREMOTE; int include_tags = DEF_INCLUDE_TAGS; int caller_confirmed = DEF_CALLER_ALWAYS_CONFIRMED; str presence_server = {0, 0}; static str peer_dlg_var = {"dlg_peer", 8}; static str entity_dlg_var = {"dlg_entity", 10}; static str flag_dlg_var = {"dlginfo_flag", 12}; static str caller_spec_param= {0, 0}; static str callee_spec_param= {0, 0}; static pv_spec_t caller_spec; static pv_spec_t callee_spec; static int osips_ps = 1; static int publish_on_trying = 0; static int nopublish_flag = -1; /** module functions */ static int mod_init(void); int dialoginfo_set(struct sip_msg* msg, char* str1, char* str2); static int fixup_dlginfo(void** param, int param_no); static cmd_export_t cmds[]= { {"dialoginfo_set",(cmd_function)dialoginfo_set,0, 0, 0, REQUEST_ROUTE}, {"dialoginfo_set",(cmd_function)dialoginfo_set,1,fixup_dlginfo,0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[]={ {"include_callid", INT_PARAM, &include_callid }, {"include_localremote", INT_PARAM, &include_localremote }, {"include_tags", INT_PARAM, &include_tags }, {"caller_confirmed", INT_PARAM, &caller_confirmed }, {"publish_on_trying", INT_PARAM, &publish_on_trying }, {"presence_server", STR_PARAM, &presence_server.s }, {"caller_spec_param", STR_PARAM, &caller_spec_param.s }, {"callee_spec_param", STR_PARAM, &callee_spec_param.s }, {"osips_ps", INT_PARAM, &osips_ps }, {"nopublish_flag", INT_PARAM, &nopublish_flag }, {0, 0, 0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "pua", DEP_ABORT }, { MOD_TYPE_DEFAULT, "dialog", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "pua_dialoginfo", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ 0, /* destroy function */ NULL /* per-child init function */ }; #ifdef PUA_DIALOGINFO_DEBUG static void __dialog_cbtest(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { str tag; LM_DBG("dialog callback received, from=%.*s, to=%.*s\n", dlg->from_uri.len, dlg->from_uri.s, dlg->to_uri.len, dlg->to_uri.s); if (dlg->tag[0].len && dlg->tag[0].s ) { LM_DBG("dialog callback: tag[0] = %.*s\n", dlg->tag[0].len, dlg->tag[0].s); } if (dlg->tag[0].len && dlg->tag[1].s ) { LM_DBG("dialog callback: tag[1] = %.*s\n", dlg->tag[1].len, dlg->tag[1].s); } if (_params->msg && _params->msg!=FAKED_REPLY && type != DLGCB_DESTROY) { /* get to tag*/ if ( !_params->msg->to) { /* to header not defined, parse to header */ LM_DBG("to header not defined, parse to header\n"); if (parse_headers(_params->msg, HDR_TO_F,0)<0) { /* parser error */ LM_ERR("parsing of to-header failed\n"); tag.s = 0; tag.len = 0; } else if (!_params->msg->to) { /* to header still not defined */ LM_ERR("no to although to-header is parsed: bad reply " "or missing TO hdr :-/\n"); tag.s = 0; tag.len = 0; } else tag = get_to(_params->msg)->tag_value; } else { tag = get_to(_params->msg)->tag_value; if (tag.s==0 || tag.len==0) { LM_DBG("missing TAG param in TO hdr :-/\n"); tag.s = 0; tag.len = 0; } } if (tag.s) { LM_DBG("dialog callback: _params->msg->to->parsed->tag_value " "= %.*s\n", tag.len, tag.s); } } switch (type) { case DLGCB_FAILED: LM_DBG("dialog callback type 'DLGCB_FAILED' received, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; case DLGCB_CONFIRMED: LM_DBG("dialog callback type 'DLGCB_CONFIRMED' received, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; case DLGCB_REQ_WITHIN: LM_DBG("dialog callback type 'DLGCB_REQ_WITHIN' received, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; case DLGCB_TERMINATED: LM_DBG("dialog callback type 'DLGCB_TERMINATED' received, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; case DLGCB_EXPIRED: LM_DBG("dialog callback type 'DLGCB_EXPIRED' received, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; case DLGCB_EARLY: LM_DBG("dialog callback type 'DLGCB_EARLY' received, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; case DLGCB_RESPONSE_FWDED: LM_DBG("dialog callback type 'DLGCB_RESPONSE_FWDED' received, " "from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; case DLGCB_RESPONSE_WITHIN: LM_DBG("dialog callback type 'DLGCB_RESPONSE_WITHIN' received, " "from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; case DLGCB_MI_CONTEXT: LM_DBG("dialog callback type 'DLGCB_MI_CONTEXT' received, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; case DLGCB_DESTROY: LM_DBG("dialog callback type 'DLGCB_DESTROY' received, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); break; default: LM_DBG("dialog callback type 'unknown' received, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); } } #endif static void __dialog_sendpublish(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { str tag = {0,0}; struct to_body from; str peer_uri= {0, 0}; char flag = DLG_PUB_AB; str flag_str; struct to_body peer_to_body; str entity_uri= {0, 0}; int buf_len = 255; struct sip_msg* msg = _params->msg; flag_str.s = &flag; flag_str.len = 1; memset(&from, 0, sizeof(struct to_body)); memset(&peer_to_body, 0, sizeof(struct to_body)); from.uri = dlg->from_uri; peer_uri.len = buf_len; peer_uri.s = (char*)pkg_malloc(buf_len); if(peer_uri.s == NULL) { LM_ERR("No more memory\n"); goto error; } /* extract the peer_uri */ if(dlg_api.fetch_dlg_value(dlg, &peer_dlg_var, &peer_uri, 1) < 0 || peer_uri.len==0) { LM_ERR("Failed to fetch peer uri dialog variable\n"); goto error; } LM_DBG("peer_uri = %.*s\n", peer_uri.len, peer_uri.s); parse_to(peer_uri.s, peer_uri.s+peer_uri.len, &peer_to_body); if(peer_to_body.error != PARSE_OK) { LM_ERR("Failed to peer uri [%.*s]\n", peer_uri.len, peer_uri.s); goto error; } /* try to extract the flag */ dlg_api.fetch_dlg_value(dlg, &flag_dlg_var, &flag_str, 1); LM_DBG("flag = %c\n", flag); entity_uri.len = buf_len; entity_uri.s = (char*)pkg_malloc(buf_len); if(entity_uri.s == NULL) { LM_ERR("No more memory\n"); goto error; } /* check if entity is also custom */ if(dlg_api.fetch_dlg_value(dlg, &entity_dlg_var, &entity_uri, 1) == 0) { /* overwrite from with this value */ parse_to(entity_uri.s, entity_uri.s + entity_uri.len, &from); if(from.error != PARSE_OK) { LM_ERR("Wrong format for entity body\n"); goto error; } LM_DBG("entity_uri = %.*s\n", entity_uri.len, entity_uri.s); LM_DBG("from uri = %.*s\n", from.uri.len, from.uri.s); } switch (type) { case DLGCB_FAILED: case DLGCB_TERMINATED: case DLGCB_EXPIRED: LM_DBG("dialog over, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); if(flag == DLG_PUB_AB || flag == DLG_PUB_A) dialog_publish("terminated", &from, &peer_to_body, &(dlg->callid), 1, 0, 0, 0); if(flag == DLG_PUB_AB || flag == DLG_PUB_B) dialog_publish("terminated", &peer_to_body, &from, &(dlg->callid), 0, 0, 0, 0); break; case DLGCB_RESPONSE_WITHIN: if (get_cseq(msg)->method_id==METHOD_INVITE) { if (msg->flags & nopublish_flag) { LM_DBG("nopublish flag was set for this INVITE\n"); break; } LM_DBG("nopublish flag not set for this INVITE, will publish\n"); } else { /* no publish for non-INVITEs */ break; } case DLGCB_CONFIRMED: LM_DBG("dialog confirmed, from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); if(flag == DLG_PUB_AB || flag == DLG_PUB_A) dialog_publish("confirmed", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, 0, 0); if(flag == DLG_PUB_AB || flag == DLG_PUB_B) dialog_publish("confirmed", &peer_to_body, &from, &(dlg->callid), 0, dlg->lifetime, 0, 0); break; case DLGCB_EARLY: LM_DBG("dialog is early, from=%.*s\n", from.uri.len, from.uri.s); if (include_tags) { /* get to tag*/ if ( !_params->msg->to && ((parse_headers(_params->msg, HDR_TO_F,0)<0) || !_params->msg->to) ) { LM_ERR("bad reply or missing TO hdr :-/\n"); tag.s = 0; tag.len = 0; } else { tag = get_to(_params->msg)->tag_value; if (tag.s==0 || tag.len==0) { LM_ERR("missing TAG param in TO hdr :-/\n"); tag.s = 0; tag.len = 0; } } if(flag == DLG_PUB_AB || flag == DLG_PUB_A) { if (caller_confirmed) { dialog_publish("confirmed", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, &(dlg->legs[DLG_CALLER_LEG].tag), &tag); } else { dialog_publish("early", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, &(dlg->legs[DLG_CALLER_LEG].tag), &tag); } } if(flag == DLG_PUB_AB || flag == DLG_PUB_B) { dialog_publish("early", &peer_to_body, &from, &(dlg->callid), 0, dlg->lifetime, &tag, &(dlg->legs[DLG_CALLER_LEG].tag)); } } else { if(flag == DLG_PUB_AB || flag == DLG_PUB_A) { if (caller_confirmed) { dialog_publish("confirmed", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, 0, 0); } else { dialog_publish("early", &from, &peer_to_body, &(dlg->callid), 1, dlg->lifetime, 0, 0); } } if(flag == DLG_PUB_AB || flag == DLG_PUB_B) { dialog_publish("early", &peer_to_body, &from, &(dlg->callid), 0, dlg->lifetime, 0, 0); } } break; default: LM_ERR("unhandled dialog callback type %d received, from=%.*s\n", type, dlg->from_uri.len, dlg->from_uri.s); if(flag == DLG_PUB_AB || flag == DLG_PUB_A) dialog_publish("terminated", &from, &peer_to_body, &(dlg->callid), 1, 0, 0, 0); if(flag == DLG_PUB_AB || flag == DLG_PUB_B) dialog_publish("terminated", &peer_to_body, &from, &(dlg->callid), 0, 0, 0, 0); } error: if(peer_uri.s) pkg_free(peer_uri.s); if(entity_uri.s) pkg_free(entity_uri.s); if (peer_to_body.param_lst) free_to_params(&peer_to_body); if (from.param_lst) free_to_params(&from); } static void __dialog_loaded(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { str peer_uri= {0, 0}; if(dlg_api.fetch_dlg_value(dlg, &peer_dlg_var, &peer_uri, 1)==0 && peer_uri.len!=0) { /* register dialog callbacks which triggers sending PUBLISH */ if (dlg_api.register_dlgcb(dlg, DLGCB_FAILED| DLGCB_CONFIRMED | DLGCB_TERMINATED | DLGCB_EXPIRED | DLGCB_RESPONSE_WITHIN | DLGCB_EARLY, __dialog_sendpublish, 0, 0) != 0) { LM_ERR("cannot register callback for interesting dialog types\n"); } } } int dialoginfo_process_body(struct publ_info* publ, str** fin_body, int ver, str* tuple) { xmlNodePtr node = NULL; xmlDocPtr doc = NULL; char* version; str* body = NULL; int len; doc = xmlParseMemory(publ->body->s, publ->body->len); if (doc == NULL) { LM_ERR("while parsing xml memory\n"); goto error; } /* change version */ node = doc->children; if (node == NULL) { LM_ERR("while extracting dialog-info node\n"); goto error; } version = int2str(ver, &len); version[len] = '\0'; if (!xmlNewProp(node, BAD_CAST "version", BAD_CAST version)) { LM_ERR("while setting version attribute\n"); goto error; } body = (str*)pkg_malloc(sizeof(str)); if (body == NULL) { LM_ERR("NO more memory left\n"); goto error; } memset(body, 0, sizeof(str)); xmlDocDumpMemory(doc, (xmlChar**)(void*)&body->s, &body->len); LM_DBG(">>> publish body: >%*s<\n", body->len, body->s); xmlFreeDoc(doc); *fin_body = body; if (*fin_body == NULL) LM_DBG("NULL fin_body\n"); xmlMemoryDump(); xmlCleanupParser(); return 1; error: if (doc) xmlFreeDoc(doc); if (body) pkg_free(body); xmlMemoryDump(); xmlCleanupParser(); return -1; } /** * init module function */ static int mod_init(void) { bind_pua_t bind_pua; evs_process_body_t* evp=0; bind_pua= (bind_pua_t)find_export("bind_pua", 1,0); if (!bind_pua) { LM_ERR("Can't bind pua\n"); return -1; } if (bind_pua(&pua) < 0) { LM_ERR("Can't bind pua\n"); return -1; } if(pua.send_publish == NULL) { LM_ERR("Could not import send_publish\n"); return -1; } pua_send_publish= pua.send_publish; if (nopublish_flag!= -1 && nopublish_flag > MAX_FLAG) { LM_ERR("invalid nopublish flag %d!!\n", nopublish_flag); return -1; } nopublish_flag = (nopublish_flag!=-1)?(1<REQ_METHOD != METHOD_INVITE) return 1; if(dlg_api.create_dlg(msg,0)< 0) { LM_ERR("Failed to create dialog\n"); return -1; } dlg = dlg_api.get_dlg(); LM_DBG("new INVITE dialog created: from=%.*s\n", dlg->from_uri.len, dlg->from_uri.s); from = get_from(msg); /* if defined overwrite */ if(caller_spec_param.s) /* if parameter defined */ { memset(&tok, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, &caller_spec, &tok) < 0) /* if value set */ { LM_ERR("Failed to get caller value\n"); return -1; } if(tok.flags&PV_VAL_STR) { str caller_str; if(tok.rs.len + CRLF_LEN > buf_len) { LM_ERR("Buffer overflow"); return -1; } trim(&tok.rs); memcpy(caller_buf, tok.rs.s, tok.rs.len); len = tok.rs.len; if(strncmp(tok.rs.s+len-CRLF_LEN, CRLF, CRLF_LEN)) { memcpy(caller_buf + len, CRLF, CRLF_LEN); len+= CRLF_LEN; } parse_to(caller_buf, caller_buf+len , &FROM); if(FROM.error != PARSE_OK) { LM_ERR("Failed to parse caller specification - not a valid uri\n"); goto end; } from = &FROM; caller_str.s = caller_buf; caller_str.len = len; LM_DBG("caller: %*s- len= %d\n", len, caller_buf, len); /* store caller in a dlg variable */ if(dlg_api.store_dlg_value(dlg, &entity_dlg_var, &caller_str)< 0) { LM_ERR("Failed to store dialog ruri\n"); goto end; } } } peer_uri.s = callee_buf; if(callee_spec_param.s) { memset(&tok, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, &callee_spec, &tok) < 0) { LM_ERR("Failed to get callee value\n"); goto end; } if(tok.flags&PV_VAL_STR) { if(tok.rs.len + CRLF_LEN > buf_len) { LM_ERR("Buffer overflow"); goto end; } trim(&tok.rs); memcpy(peer_uri.s, tok.rs.s, tok.rs.len); len = tok.rs.len; if(strncmp(tok.rs.s+len-CRLF_LEN, CRLF, CRLF_LEN)) { memcpy(peer_uri.s + len, CRLF, CRLF_LEN); len+= CRLF_LEN; } peer_uri.len = len; } else goto default_callee; } else { default_callee: ruri = GET_RURI(msg); to = get_to(msg); len= to->display.len + 2 + ruri->len + CRLF_LEN; if(len > buf_len) { LM_ERR("Buffer overflow\n"); goto end; } len = 0; if(to->display.len && to->display.s) { memcpy(peer_uri.s, to->display.s, to->display.len); peer_uri.s[to->display.len]='<'; len = to->display.len + 1; } memcpy(peer_uri.s + len, ruri->s, ruri->len); len+= ruri->len; if(to->display.len) { peer_uri.s[len++]='>'; } memcpy(peer_uri.s + len, CRLF, CRLF_LEN); len+= CRLF_LEN; peer_uri.len = len; } LM_DBG("Peer uri = %.*s\n", peer_uri.len, peer_uri.s); parse_to(peer_uri.s, peer_uri.s+peer_uri.len, &peer_to_body); if(peer_to_body.error != PARSE_OK) { LM_ERR("Failed to peer uri [%.*s]\n", peer_uri.len, peer_uri.s); goto end; } /* store peer uri in dialog structure */ if(dlg_api.store_dlg_value(dlg, &peer_dlg_var, &peer_uri)< 0) { LM_ERR("Failed to store dialog ruri\n"); goto end; } /* store flag, if defined */ if(flag_pv) { if(pv_printf(msg, (pv_elem_t*)flag_pv, buf, &buf_len)<0) { LM_ERR("cannot print the format\n"); goto end; } if(!check_flag(buf, buf_len)) { LM_ERR("Wrong value for flag\n"); goto end; } flag = buf[0]; flag_str.s = buf; flag_str.len = buf_len; if(dlg_api.store_dlg_value(dlg, &flag_dlg_var, &flag_str)< 0) { LM_ERR("Failed to store dialog ruri\n"); goto end; } } /* register dialog callbacks which triggers sending PUBLISH */ if (dlg_api.register_dlgcb(dlg, DLGCB_FAILED| DLGCB_CONFIRMED | DLGCB_TERMINATED | DLGCB_EXPIRED | DLGCB_RESPONSE_WITHIN | DLGCB_EARLY, __dialog_sendpublish, 0, 0) != 0) { LM_ERR("cannot register callback for interesting dialog types\n"); goto end; } #ifdef PUA_DIALOGINFO_DEBUG /* dialog callback testing (registered last to be executed first) */ if (dlg_api.register_dlgcb(dlg, DLGCB_FAILED| DLGCB_CONFIRMED | DLGCB_REQ_WITHIN | DLGCB_TERMINATED | DLGCB_EXPIRED | DLGCB_EARLY | DLGCB_RESPONSE_FWDED | DLGCB_RESPONSE_WITHIN | DLGCB_MI_CONTEXT | DLGCB_DESTROY, __dialog_cbtest, NULL, NULL) != 0) { LM_ERR("cannot register callback for all dialog types\n"); goto end; } #endif if(publish_on_trying) { if(flag == DLG_PUB_A || flag == DLG_PUB_AB) dialog_publish("trying", from, &peer_to_body, &(dlg->callid), 1, DEFAULT_CREATED_LIFETIME, 0, 0); if(flag == DLG_PUB_B || flag == DLG_PUB_AB) dialog_publish("trying", &peer_to_body, from, &(dlg->callid), 0, DEFAULT_CREATED_LIFETIME, 0, 0); } ret=1; end: if (peer_to_body.param_lst) free_to_params(&peer_to_body); if (FROM.param_lst) free_to_params(&FROM); return ret; } static int fixup_dlginfo(void** param, int param_no) { pv_elem_t *model; str s; if(param_no== 0) return 0; if(*param) { s.s = (char*)(*param); s.len = strlen(s.s); if(pv_parse_format(&s, &model)<0) { LM_ERR( "wrong format[%s]\n",(char*)(*param)); return E_UNSPEC; } *param = (void*)model; return 0; } LM_ERR( "null format\n"); return E_UNSPEC; } opensips-2.2.2/modules/pua_dialoginfo/pua_dialoginfo.h000066400000000000000000000023401300170765700231430ustar00rootroot00000000000000/* * pua_dialoginfo module - publish dialog-info from dialog module * * Copyright (C) 2006 Voice Sistem S.R.L. * Copyright (C) 2008 Klaus Darilion IPCom * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PUA_DLGINFO_H #define _PUA_DLGINFO_H #include "../pua/pua_bind.h" send_publish_t pua_send_publish; void dialog_publish(char *state, struct to_body* entity, struct to_body *peer, str *callid, unsigned int initiator, unsigned int lifetime, str *localtag, str *remotetag); extern str presence_server; #endif opensips-2.2.2/modules/pua_mi/000077500000000000000000000000001300170765700163055ustar00rootroot00000000000000opensips-2.2.2/modules/pua_mi/Makefile000066400000000000000000000010171300170765700177440ustar00rootroot00000000000000# $Id$ # # PUBLISH # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pua_mi.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/pua_mi/README000066400000000000000000000067231300170765700171750ustar00rootroot00000000000000PUA MI Anca-Maria Vamanu Edited by Anca-Maria Vamanu Edited by Juha Heinanen Copyright © 2006 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. presence_server (str) 1.4. Exported Functions 1.5. Exported MI functions 1.5.1. pua_publish 1.6. pua_subscribe List of Examples 1.1. Set presence_server parameter 1.2. pua_publish FIFO example Chapter 1. Admin Guide 1.1. Overview The pua_mi offers the possibility to publish presence information and subscribe to presence information via MI transports. Using this module you can create independent applications/scripts to publish not sip-related information (e.g., system resources like CPU-usage, memory, number of active subscribers ...). Also, this module allows non-SIP speaking applications to subscribe presence information kept in a SIP presence server. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * pua 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * none 1.3. Exported Parameters 1.3.1. presence_server (str) The the address of the presence server. If set, it will be used as outbound proxy when sending PUBLISH requests. Example 1.1. Set presence_server parameter ... modparam("pua_mi", "presence_server", "sip:pa@opensips.org:5075") ... 1.4. Exported Functions The module does not export functions to be used in configuration script. 1.5. Exported MI functions 1.5.1. pua_publish Command parameters: * presentity_uri - e.g. sip:system@opensips.org * expires - Relative expires time in seconds (e.g. 3600). * event package - Event package that is target of published information (e.g. presence). * content type - Content type of published information (e.g. application/pidf+xml) or . if no information is enclosed. * ETag - ETag that publish should match or . if no ETag is given. * extra_headers - Extra headers added to PUBLISH request or . if no extra headers. * body - The body of the publish request containing published information or missing if no published information. It has to be a single line for FIFO transport. Example 1.2. pua_publish FIFO example ... :pua_publish:fifo_test_reply sip:system@opensips.org 3600 presence application/pidf+xml . . openawa yCPU:16 MEM:476 1.6. pua_subscribe Command parameters: * presentity_uri - e.g. sip:presentity@opensips.org * watcher_uri - e.g. sip:watcher@opensips.org * event package * expires - Relative time in seconds for the desired validity of the subscription. opensips-2.2.2/modules/pua_mi/doc/000077500000000000000000000000001300170765700170525ustar00rootroot00000000000000opensips-2.2.2/modules/pua_mi/doc/pua_mi.xml000066400000000000000000000020621300170765700210460ustar00rootroot00000000000000 %docentities; ]> PUA MI &osipsname; Anca-Maria Vamanu Anca-Maria Vamanu Juha Heinanen 2006 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/pua_mi/doc/pua_mi_admin.xml000066400000000000000000000113621300170765700222210ustar00rootroot00000000000000 &adminguide;
Overview The pua_mi offers the possibility to publish presence information and subscribe to presence information via MI transports. Using this module you can create independent applications/scripts to publish not sip-related information (e.g., system resources like CPU-usage, memory, number of active subscribers ...). Also, this module allows non-SIP speaking applications to subscribe presence information kept in a SIP presence server.
Dependencies
&osips; Modules The following modules must be loaded before this module: pua
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: none
Exported Parameters
<varname>presence_server</varname> (str) The the address of the presence server. If set, it will be used as outbound proxy when sending PUBLISH requests. Set <varname>presence_server</varname> parameter ... modparam("pua_mi", "presence_server", "sip:pa@opensips.org:5075") ...
Exported Functions The module does not export functions to be used in configuration script.
Exported MI functions
<function moreinfo="none">pua_publish</function> Command parameters: presentity_uri - e.g. sip:system@opensips.org expires - Relative expires time in seconds (e.g. 3600). event package - Event package that is target of published information (e.g. presence). content type - Content type of published information (e.g. application/pidf+xml) or . if no information is enclosed. ETag - ETag that publish should match or . if no ETag is given. extra_headers - Extra headers added to PUBLISH request or . if no extra headers. body - The body of the publish request containing published information or missing if no published information. It has to be a single line for FIFO transport. <function>pua_publish</function> FIFO example ... openawayCPU:16 MEM:476 ]]>
<function moreinfo="none">pua_subscribe</function> Command parameters: presentity_uri - e.g. sip:presentity@opensips.org watcher_uri - e.g. sip:watcher@opensips.org event package expires - Relative time in seconds for the desired validity of the subscription.
opensips-2.2.2/modules/pua_mi/mi_func.c000066400000000000000000000225671300170765700201050ustar00rootroot00000000000000/* * pua_mi module - MI pua module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "../../parser/parse_expires.h" #include "../../parser/parse_uri.h" #include "../../mem/mem.h" #include "../../mi/mi.h" #include "../../ut.h" #include "../pua/pua_bind.h" #include "../pua/pua.h" #include "pua_mi.h" /* * mi cmd: pua_publish * * * * - body type if body of a type different from default * event content-type or . * - ETag that publish should match or . if no ETag * - extra headers to be added to the request or . * - may not be present in case of update for expire */ struct mi_root* mi_pua_publish(struct mi_root* cmd, void* param) { int exp; struct mi_node* node= NULL; str pres_uri, expires; str body= {0, 0}; struct sip_uri uri; publ_info_t publ; str event; str content_type; str etag; str extra_headers; int result; int sign= 1; LM_DBG("start\n"); node = cmd->node.kids; if(node == NULL) return 0; /* Get presentity URI */ pres_uri = node->value; if(pres_uri.s == NULL || pres_uri.s== 0) { LM_ERR("empty uri\n"); return init_mi_tree(404, "Empty presentity URI", 20); } if(parse_uri(pres_uri.s, pres_uri.len, &uri)<0 ) { LM_ERR("bad uri\n"); return init_mi_tree(404, "Bad presentity URI", 18); } LM_DBG("pres_uri '%.*s'\n", pres_uri.len, pres_uri.s); node = node->next; if(node == NULL) return 0; /* Get expires */ expires= node->value; if(expires.s== NULL || expires.len== 0) { LM_ERR("empty expires parameter\n"); return init_mi_tree(400, "Empty expires parameter", 23); } if(expires.s[0]== '-') { sign= -1; expires.s++; expires.len--; } if( str2int(&expires, (unsigned int*) &exp)< 0) { LM_ERR("invalid expires parameter\n" ); goto error; } exp= exp* sign; LM_DBG("expires '%d'\n", exp); node = node->next; if(node == NULL) return 0; /* Get event */ event= node->value; if(event.s== NULL || event.len== 0) { LM_ERR("empty event parameter\n"); return init_mi_tree(400, "Empty event parameter", 21); } LM_DBG("event '%.*s'\n", event.len, event.s); node = node->next; if(node == NULL) return 0; /* Get content type */ content_type= node->value; if(content_type.s== NULL || content_type.len== 0) { LM_ERR("empty content type\n"); return init_mi_tree(400, "Empty content type parameter", 28); } LM_DBG("content type '%.*s'\n", content_type.len, content_type.s); node = node->next; if(node == NULL) return 0; /* Get etag */ etag= node->value; if(etag.s== NULL || etag.len== 0) { LM_ERR("empty etag parameter\n"); return init_mi_tree(400, "Empty etag parameter", 20); } LM_DBG("etag '%.*s'\n", etag.len, etag.s); node = node->next; if(node == NULL) return 0; /* Get extra_headers */ extra_headers = node->value; if(extra_headers.s== NULL || extra_headers.len== 0) { LM_ERR("empty extra_headers parameter\n"); return init_mi_tree(400, "Empty extra_headers", 19); } LM_DBG("extra_headers '%.*s'\n", extra_headers.len, extra_headers.s); node = node->next; /* Get body */ if(node == NULL ) { body.s= NULL; body.len= 0; } else { if(node->next!=NULL) return init_mi_tree(400, "Too many parameters", 19); body= node->value; if(body.s == NULL || body.s== 0) { LM_ERR("empty body parameter\n"); return init_mi_tree(400, "Empty body parameter", 20); } } LM_DBG("body '%.*s'\n", body.len, body.s); /* Check that body is NULL if content type is . */ if(body.s== NULL && (content_type.len!= 1 || content_type.s[0]!= '.')) { LM_ERR("body is missing, but content type is not .\n"); return init_mi_tree(400, "Body parameter is missing", 25); } /* Create the publ_info_t structure */ memset(&publ, 0, sizeof(publ_info_t)); publ.pres_uri= &pres_uri; if(body.s) { publ.body= &body; } publ.event= get_event_flag(&event); if(publ.event< 0) { LM_ERR("unknown event\n"); return init_mi_tree(400, "Unknown event", 13); } if(content_type.len!= 1) { publ.content_type= content_type; } if(! (etag.len== 1 && etag.s[0]== '.')) { publ.etag= &etag; } publ.expires= exp; if (!(extra_headers.len == 1 && extra_headers.s[0] == '.')) { publ.extra_headers = &extra_headers; } if (cmd->async_hdl!=NULL) { publ.source_flag= MI_ASYN_PUBLISH; publ.cb_param= (void*)cmd->async_hdl; } else publ.source_flag|= MI_PUBLISH; publ.outbound_proxy = presence_server; result= pua_send_publish(&publ); if(result< 0) { LM_ERR("sending publish failed\n"); return init_mi_tree(500, "MI/PUBLISH failed", 17); } if(result== 418) return init_mi_tree(418, "Wrong ETag", 10); if (cmd->async_hdl==NULL) return init_mi_tree( 202, "Accepted", 8); else return MI_ROOT_ASYNC_RPL; error: return 0; } int mi_publ_rpl_cback( ua_pres_t* hentity, struct sip_msg* reply) { struct mi_root *rpl_tree= NULL; struct mi_handler* mi_hdl= NULL; struct hdr_field* hdr= NULL; int statuscode; int lexpire; str etag; str reason= {0, 0}; if(reply== NULL || hentity== NULL) { LM_ERR("NULL parameter\n"); return -1; } if(hentity->cb_param== NULL) { LM_DBG("NULL callback parameter, probably a refresh\n"); return -1; } if(reply== FAKED_REPLY) { statuscode= 408; reason.s= "Request Timeout"; reason.len= strlen(reason.s); } else { statuscode= reply->first_line.u.reply.statuscode; reason= reply->first_line.u.reply.reason; } mi_hdl = (struct mi_handler *)(hentity->cb_param); rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) goto done; addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%d %.*s", statuscode, reason.len, reason.s); if(statuscode== 200) { /* extract ETag and expires */ lexpire = ((exp_body_t*)reply->expires->parsed)->val; LM_DBG("lexpire= %d\n", lexpire); hdr = get_header_by_static_name( reply, "SIP-ETag"); if( hdr==NULL ) /* must find SIP-Etag header field in 200 OK msg*/ { LM_ERR("SIP-ETag header field not found\n"); goto error; } etag= hdr->body; addf_mi_node_child( &rpl_tree->node, 0, "ETag", 4, "%.*s", etag.len, etag.s); addf_mi_node_child( &rpl_tree->node, 0, "Expires", 7, "%d", lexpire); } done: if ( statuscode >= 200) { mi_hdl->handler_f( rpl_tree, mi_hdl, 1); } else { mi_hdl->handler_f( rpl_tree, mi_hdl, 0 ); } hentity->cb_param = 0; return 0; error: return -1; } /*Command parameters: * pua_subscribe * * * * * */ struct mi_root* mi_pua_subscribe(struct mi_root* cmd, void* param) { int exp= 0; str pres_uri, watcher_uri, expires; struct mi_node* node= NULL; struct mi_root* rpl= NULL; struct sip_uri uri; subs_info_t subs; int sign= 1; str event; node = cmd->node.kids; if(node == NULL) return 0; pres_uri= node->value; if(pres_uri.s == NULL || pres_uri.s== 0) { return init_mi_tree(400, "Bad uri", 7); } if(parse_uri(pres_uri.s, pres_uri.len, &uri)<0 ) { LM_ERR("bad uri\n"); return init_mi_tree(400, "Bad uri", 7); } node = node->next; if(node == NULL) return 0; watcher_uri= node->value; if(watcher_uri.s == NULL || watcher_uri.s== 0) { return init_mi_tree(400, "Bad uri", 7); } if(parse_uri(watcher_uri.s, watcher_uri.len, &uri)<0 ) { LM_ERR("bad uri\n"); return init_mi_tree(400, "Bad uri", 7); } /* Get event */ node = node->next; if(node == NULL) return 0; event= node->value; if(event.s== NULL || event.len== 0) { LM_ERR("empty event parameter\n"); return init_mi_tree(400, "Empty event parameter", 21); } LM_DBG("event '%.*s'\n", event.len, event.s); node = node->next; if(node == NULL || node->next!=NULL) { LM_ERR("Too much or too many parameters\n"); return 0; } expires= node->value; if(expires.s== NULL || expires.len== 0) { LM_ERR("Bad expires parameter\n"); return init_mi_tree(400, "Bad expires", 11); } if(expires.s[0]== '-') { sign= -1; expires.s++; expires.len--; } if( str2int(&expires, (unsigned int*) &exp)< 0) { LM_ERR("invalid expires parameter\n" ); goto error; } exp= exp* sign; LM_DBG("expires '%d'\n", exp); memset(&subs, 0, sizeof(subs_info_t)); subs.pres_uri= &pres_uri; subs.watcher_uri= &watcher_uri; subs.contact= &watcher_uri; subs.expires= exp; subs.source_flag |= MI_SUBSCRIBE; subs.event= get_event_flag(&event); if(subs.event< 0) { LM_ERR("unknown event\n"); return init_mi_tree(400, "Unknown event", 13); } if(pua_send_subscribe(&subs)< 0) { LM_ERR("while sending subscribe\n"); goto error; } rpl= init_mi_tree(202, "accepted", 8); if(rpl == NULL) return 0; return rpl; error: return 0; } opensips-2.2.2/modules/pua_mi/mi_func.h000066400000000000000000000021061300170765700200750ustar00rootroot00000000000000/* * pua_mi module - MI pua module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PUA_MI #define _PUA_MI struct mi_root* mi_pua_publish(struct mi_root* cmd, void* param); struct mi_root* mi_pua_subscribe(struct mi_root* cmd, void* param); int mi_publ_rpl_cback(ua_pres_t* hentity, struct sip_msg* reply); #endif opensips-2.2.2/modules/pua_mi/pua_mi.c000066400000000000000000000074631300170765700177350ustar00rootroot00000000000000/* * pua_mi module - MI pua module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-11-29 initial version (Anca Vamanu) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../parser/parse_expires.h" #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../mem/mem.h" #include "../../pt.h" #include "../tm/tm_load.h" #include "../pua/pua_bind.h" #include "mi_func.h" pua_api_t pua; /** module functions */ static int mod_init(void); static int child_init(int); static void destroy(void); send_publish_t pua_send_publish; send_subscribe_t pua_send_subscribe; str presence_server= {0, 0}; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { { "pua_publish", 0, mi_pua_publish, MI_ASYNC_RPL_FLAG, 0, 0}, { "pua_subscribe", 0, mi_pua_subscribe, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[]={ {"presence_server", STR_PARAM, &presence_server.s }, {0, 0, 0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "pua", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "pua_mi", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { bind_pua_t bind_pua; if(presence_server.s) presence_server.len = strlen(presence_server.s); bind_pua= (bind_pua_t)find_export("bind_pua", 1,0); if (!bind_pua) { LM_ERR("Can't bind pua (check if pua module is loaded)\n"); return -1; } if (bind_pua(&pua) < 0) { LM_ERR("Can't bind pua\n"); return -1; } if(pua.send_publish == NULL) { LM_ERR("Could not import send_publish\n"); return -1; } pua_send_publish= pua.send_publish; if(pua.send_subscribe == NULL) { LM_ERR("Could not import send_subscribe\n"); return -1; } pua_send_subscribe= pua.send_subscribe; if(pua.register_puacb(MI_ASYN_PUBLISH, mi_publ_rpl_cback, NULL)< 0) { LM_ERR("Could not register callback\n"); return -1; } return 0; } static int child_init(int rank) { LM_DBG("child [%d] pid [%d]\n", rank, getpid()); return 0; } static void destroy(void) { LM_DBG("destroying module ...\n"); return ; } opensips-2.2.2/modules/pua_mi/pua_mi.h000066400000000000000000000020171300170765700177300ustar00rootroot00000000000000/* * pua_mi module - MI pua module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PUA_MI_ #define _PUA_MI_ #include "../pua/pua_bind.h" extern send_publish_t pua_send_publish; extern send_subscribe_t pua_send_subscribe; extern str presence_server; #endif opensips-2.2.2/modules/pua_usrloc/000077500000000000000000000000001300170765700172075ustar00rootroot00000000000000opensips-2.2.2/modules/pua_usrloc/Makefile000066400000000000000000000010231300170765700206430ustar00rootroot00000000000000# $Id$ # # PUBLISH # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pua_usrloc.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/pua_usrloc/README000066400000000000000000000053141300170765700200720ustar00rootroot00000000000000PUA Usrloc Anca-Maria Vamanu Edited by Anca-Maria Vamanu Copyright © 2006 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. default_domain (str) 1.3.2. entity_prefix (str) 1.3.3. presence_server (str) 1.4. Exported Functions 1.4.1. pua_set_publish() List of Examples 1.1. Set default_domain parameter 1.2. Set presentity_prefix parameter 1.3. Set presence_server parameter 1.4. pua_set_publish usage Chapter 1. Admin Guide 1.1. Overview The pua_usrloc is the connector between usrloc and pua modules. It creates the environment to send PUBLISH requests for user location records, on specific events (e.g., when new record is added in usrloc, a PUBLISH with status open (online) is issued; when expires, it sends closed (offline)). Using this module, phones which have no support for presence can be seen as online/offline. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * usrloc. * pua. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml. 1.3. Exported Parameters 1.3.1. default_domain (str) The default domain to use when constructing the presentity uri if it is missing from recorded aor. Default value is “NULLâ€. Example 1.1. Set default_domain parameter ... modparam("pua_usrloc", "default_domain", "opensips.org") ... 1.3.2. entity_prefix (str) The prefix when construstructing entity attribute to be added to presence node in xml pidf. (ex: pres:user@domain ). Default value is “NULLâ€. Example 1.2. Set presentity_prefix parameter ... modparam("pua_usrloc", "entity_prefix", "pres") ... 1.3.3. presence_server (str) The the address of the presence server. If set, it will be used as outbound proxy when sending PUBLISH requests. Example 1.3. Set presence_server parameter ... modparam("pua_usrloc", "presence_server", "sip:pa@opensips.org:5075") ... 1.4. Exported Functions 1.4.1. pua_set_publish() The function is used to mark REGISTER requests that have to issue a PUBLISH. The PUBLISH is issued when REGISTER is saved in location table. Example 1.4. pua_set_publish usage ... if(is_method("REGISTER") && from_uri=~"john@opensips.org") pua_set_publish(); ... opensips-2.2.2/modules/pua_usrloc/doc/000077500000000000000000000000001300170765700177545ustar00rootroot00000000000000opensips-2.2.2/modules/pua_usrloc/doc/pua_usrloc.xml000066400000000000000000000017411300170765700226550ustar00rootroot00000000000000 %docentities; ]> PUA Usrloc &osipsname; Anca-Maria Vamanu Anca-Maria Vamanu 2006 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/pua_usrloc/doc/pua_usrloc_admin.xml000066400000000000000000000065201300170765700240250ustar00rootroot00000000000000 &adminguide;
Overview The pua_usrloc is the connector between usrloc and pua modules. It creates the environment to send PUBLISH requests for user location records, on specific events (e.g., when new record is added in usrloc, a PUBLISH with status open (online) is issued; when expires, it sends closed (offline)). Using this module, phones which have no support for presence can be seen as online/offline.
Dependencies
&osips; Modules The following modules must be loaded before this module: usrloc. pua.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml.
Exported Parameters
<varname>default_domain</varname> (str) The default domain to use when constructing the presentity uri if it is missing from recorded aor. Default value is NULL. Set <varname>default_domain</varname> parameter ... modparam("pua_usrloc", "default_domain", "opensips.org") ...
<varname>entity_prefix</varname> (str) The prefix when construstructing entity attribute to be added to presence node in xml pidf. (ex: pres:user@domain ). Default value is NULL. Set <varname>presentity_prefix</varname> parameter ... modparam("pua_usrloc", "entity_prefix", "pres") ...
<varname>presence_server</varname> (str) The the address of the presence server. If set, it will be used as outbound proxy when sending PUBLISH requests. Set <varname>presence_server</varname> parameter ... modparam("pua_usrloc", "presence_server", "sip:pa@opensips.org:5075") ...
Exported Functions
<function moreinfo="none">pua_set_publish()</function> The function is used to mark REGISTER requests that have to issue a PUBLISH. The PUBLISH is issued when REGISTER is saved in location table. <function>pua_set_publish</function> usage ... if(is_method("REGISTER") && from_uri=~"john@opensips.org") pua_set_publish(); ...
opensips-2.2.2/modules/pua_usrloc/pua_usrloc.c000066400000000000000000000121271300170765700215320ustar00rootroot00000000000000/* * pua_usrloc module - usrloc pua module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-11-29 initial version (Anca Vamanu) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../parser/parse_expires.h" #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../mem/mem.h" #include "../../pt.h" #include "../usrloc/ul_mod.h" #include "../usrloc/usrloc.h" #include "../usrloc/ul_callback.h" #include "../pua/pua_bind.h" #include "pua_usrloc.h" str default_domain= {NULL, 0}; pua_api_t pua; str pres_prefix= {0, 0}; str presence_server= {0, 0}; /* Structure containing pointers to usrloc functions */ usrloc_api_t ul; /** module functions */ static int mod_init(void); static int child_init(int); static void destroy(void); static cmd_export_t cmds[]= { {"pua_set_publish", (cmd_function)pua_set_publish, 0, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[]={ {"default_domain", STR_PARAM, &default_domain.s }, {"entity_prefix", STR_PARAM, &pres_prefix.s }, {"presence_server", STR_PARAM, &presence_server.s }, {0, 0, 0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "pua", DEP_ABORT }, { MOD_TYPE_DEFAULT, "usrloc", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "pua_usrloc", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { bind_usrloc_t bind_usrloc; bind_pua_t bind_pua; LM_DBG("initializing module ...\n"); if(default_domain.s == NULL ) { LM_ERR("default domain parameter not set\n"); return -1; } default_domain.len= strlen(default_domain.s); if(pres_prefix.s == NULL ) { LM_DBG("No pres_prefix configured\n"); } else pres_prefix.len= strlen(pres_prefix.s); if(presence_server.s) { presence_server.len= strlen(presence_server.s); } /* index in global context to keep the on/off state */ pul_status_idx = context_register_int(CONTEXT_GLOBAL, NULL); bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (!bind_usrloc) { LM_ERR("Can't bind usrloc\n"); return -1; } if (bind_usrloc(&ul) < 0) { LM_ERR("Can't bind usrloc\n"); return -1; } if(ul.register_ulcb == NULL) { LM_ERR("Could not import ul_register_ulcb\n"); return -1; } if(ul.register_ulcb(UL_CONTACT_INSERT, ul_publish, 0)< 0) { LM_ERR("can not register callback for" " insert\n"); return -1; } if(ul.register_ulcb(UL_CONTACT_EXPIRE, ul_publish, 0)< 0) { LM_ERR("can not register callback for" " expire\n"); return -1; } if(ul.register_ulcb(UL_CONTACT_UPDATE, ul_publish, 0)< 0) { LM_ERR("can not register callback for update\n"); return -1; } if(ul.register_ulcb(UL_CONTACT_DELETE, ul_publish, 0)< 0) { LM_ERR("can not register callback for delete\n"); return -1; } bind_pua= (bind_pua_t)find_export("bind_pua", 1,0); if (!bind_pua) { LM_ERR("Can't bind pua\n"); return -1; } if (bind_pua(&pua) < 0) { LM_ERR("Can't bind pua\n"); return -1; } if(pua.send_publish == NULL) { LM_ERR("Could not import send_publish\n"); return -1; } pua_send_publish= pua.send_publish; if(pua.send_subscribe == NULL) { LM_ERR("Could not import send_subscribe\n"); return -1; } pua_send_subscribe= pua.send_subscribe; return 0; } static int child_init(int rank) { LM_DBG("child [%d] pid [%d]\n", rank, getpid()); return 0; } static void destroy(void) { LM_DBG("destroying module ...\n"); return ; } opensips-2.2.2/modules/pua_usrloc/pua_usrloc.h000066400000000000000000000022461300170765700215400ustar00rootroot00000000000000/* * pua_urloc module - usrloc pua module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PUA_UL_ #define _PUA_UL_ #include "../pua/pua_bind.h" send_publish_t pua_send_publish; send_subscribe_t pua_send_subscribe; void ul_publish(ucontact_t* c, int type, void* param); int pua_set_publish(struct sip_msg* , char*, char*); extern str pres_prefix; extern str presence_server; extern int pul_status_idx; #endif opensips-2.2.2/modules/pua_usrloc/ul_publish.c000066400000000000000000000154161300170765700215300ustar00rootroot00000000000000/* * pua_usrloc module - usrloc pua module * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "../../parser/parse_expires.h" #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../script_cb.h" #include "../usrloc/usrloc.h" #include "../usrloc/ul_callback.h" #include "../tm/tm_load.h" #include "../pua/pua.h" #include "pua_usrloc.h" int pul_status_idx = -1; #define BUF_LEN 256 #define ctx_pul_set(_val) \ context_put_int( CONTEXT_GLOBAL, current_processing_ctx, pul_status_idx, _val) #define ctx_pul_get() \ context_get_int( CONTEXT_GLOBAL, current_processing_ctx, pul_status_idx) int pua_set_publish(struct sip_msg* msg , char* s1, char* s2) { LM_DBG("set send publish\n"); ctx_pul_set(1/*pua UL on*/); return 1; } /* for debug purpose only */ void print_publ(publ_info_t* p) { LM_DBG("publ:\n"); LM_DBG("uri= %.*s\n", p->pres_uri->len, p->pres_uri->s); LM_DBG("id= %.*s\n", p->id.len, p->id.s); LM_DBG("expires= %d\n", p->expires); } str* build_pidf(ucontact_t* c) { xmlDocPtr doc = NULL; xmlNodePtr root_node = NULL; xmlNodePtr tuple_node = NULL; xmlNodePtr status_node = NULL; xmlNodePtr basic_node = NULL; str *body= NULL; str pres_uri= {NULL, 0}; char buf[BUF_LEN]; char* at= NULL; if(c->expires< (int)time(NULL)) { LM_DBG("found expired \n\n"); return NULL; } pres_uri.s = buf; if(pres_prefix.s) { memcpy(pres_uri.s, pres_prefix.s, pres_prefix.len); pres_uri.len+= pres_prefix.len; memcpy(pres_uri.s+ pres_uri.len, ":", 1); pres_uri.len+= 1; } if(pres_uri.len + c->aor->len+ 1 > BUF_LEN) { LM_ERR("buffer size overflown\n"); return NULL; } memcpy(pres_uri.s+ pres_uri.len, c->aor->s, c->aor->len); pres_uri.len+= c->aor->len; at = memchr(c->aor->s, '@', c->aor->len); if(!at) { if(pres_uri.len + 2 + default_domain.len > BUF_LEN) { LM_ERR("buffer size overflown\n"); return NULL; } pres_uri.s[pres_uri.len++]= '@'; memcpy(pres_uri.s+ pres_uri.len, default_domain.s, default_domain.len); pres_uri.len+= default_domain.len; } pres_uri.s[pres_uri.len]= '\0'; /* create the Publish body */ doc = xmlNewDoc(BAD_CAST "1.0"); if(doc==0) return NULL; root_node = xmlNewNode(NULL, BAD_CAST "presence"); if(root_node==0) goto error; xmlDocSetRootElement(doc, root_node); xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:pidf"); xmlNewProp(root_node, BAD_CAST "xmlns:dm", BAD_CAST "urn:ietf:params:xml:ns:pidf:data-model"); xmlNewProp(root_node, BAD_CAST "xmlns:rpid", BAD_CAST "urn:ietf:params:xml:ns:pidf:rpid" ); xmlNewProp(root_node, BAD_CAST "xmlns:c", BAD_CAST "urn:ietf:params:xml:ns:pidf:cipid"); xmlNewProp(root_node, BAD_CAST "entity", BAD_CAST pres_uri.s); tuple_node =xmlNewChild(root_node, NULL, BAD_CAST "tuple", NULL) ; if( tuple_node ==NULL) { LM_ERR("while adding child\n"); goto error; } status_node = xmlNewChild(tuple_node, NULL, BAD_CAST "status", NULL) ; if( status_node ==NULL) { LM_ERR("while adding child\n"); goto error; } basic_node = xmlNewChild(status_node, NULL, BAD_CAST "basic", BAD_CAST "open") ; if( basic_node ==NULL) { LM_ERR("while adding child\n"); goto error; } body = (str*)pkg_malloc(sizeof(str)); if(body == NULL) { LM_ERR("while allocating memory\n"); return NULL; } memset(body, 0, sizeof(str)); xmlDocDumpMemory(doc,(unsigned char**)(void*)&body->s,&body->len); LM_DBG("new_body:\n%.*s\n",body->len, body->s); /*free the document */ xmlFreeDoc(doc); xmlCleanupParser(); return body; error: if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } if(doc) xmlFreeDoc(doc); return NULL; } void ul_publish(ucontact_t* c, int type, void* param) { str* body= NULL; str uri= {NULL, 0}; char* at= NULL; publ_info_t publ; int error; if (!(type & UL_CONTACT_EXPIRE) && ctx_pul_get()==0) return; if(type & UL_CONTACT_DELETE) LM_DBG("\nul_publish: DELETE type\n"); else if(type & UL_CONTACT_INSERT) LM_DBG("\nul_publish: INSERT type\n"); else if(type & UL_CONTACT_UPDATE) LM_DBG("\nul_publish: UPDATE type\n"); else if(type & UL_CONTACT_EXPIRE) LM_DBG("\nul_publish: EXPIRE type\n"); if(type & UL_CONTACT_INSERT) { body= build_pidf(c); if(body == NULL || body->s == NULL) goto error; } else body = NULL; uri.s = (char*)pkg_malloc(sizeof(char)*(c->aor->len+default_domain.len+6)); if(uri.s == NULL) goto error; LM_DBG("aor = %.*s\n", c->aor->len, c->aor->s); memcpy(uri.s, "sip:", 4); uri.len = 4; memcpy(uri.s+ uri.len, c->aor->s, c->aor->len); uri.len+= c->aor->len; at = memchr(c->aor->s, '@', c->aor->len); if(!at) { uri.s[uri.len++]= '@'; memcpy(uri.s+ uri.len, default_domain.s, default_domain.len); uri.len+= default_domain.len; } LM_DBG("uri= %.*s\n", uri.len, uri.s); memset(&publ, 0, sizeof(publ_info_t)); publ.pres_uri= &uri; publ.body = body; publ.id = c->callid; publ.content_type.s = "application/pidf+xml"; publ.content_type.len = 20; if(type & UL_CONTACT_EXPIRE || type & UL_CONTACT_DELETE) publ.expires= 0; else publ.expires= c->expires - (int)time(NULL); if(type & UL_CONTACT_INSERT) publ.flag= INSERT_TYPE; else publ.flag= UPDATE_TYPE; publ.source_flag|= UL_PUBLISH; publ.event|= PRESENCE_EVENT; publ.extra_headers= NULL; publ.outbound_proxy = presence_server; if((error = pua_send_publish(&publ))< 0) { if((type & UL_CONTACT_UPDATE) && (error== ERR_PUBLISH_NO_BODY)) { LM_DBG("Usrloc Publish for update failed - try Insert\n"); publ.body= build_pidf(c); if(publ.body == NULL || publ.body->s == NULL) { LM_ERR("failed to generate publish body\n"); goto error; } publ.flag= INSERT_TYPE; if(pua_send_publish(&publ)< 0) { LM_ERR("failed to send publish\n"); } } else LM_ERR("failed to send publish\n"); } error: if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } if(uri.s) pkg_free(uri.s); if (!(type & UL_CONTACT_EXPIRE)) ctx_pul_set( 0/* pua UL off*/); return; } opensips-2.2.2/modules/pua_xmpp/000077500000000000000000000000001300170765700166645ustar00rootroot00000000000000opensips-2.2.2/modules/pua_xmpp/Makefile000066400000000000000000000010221300170765700203170ustar00rootroot00000000000000# $Id$ # # PUBLISH # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME= pua_xmpp.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/pua_xmpp/README000066400000000000000000000073141300170765700175510ustar00rootroot00000000000000Presence User Agent for XMPP (Presence gateway between SIP and XMPP) Anca-Maria Vamanu Edited by Anca-Maria Vamanu Copyright © 2007 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. server_address(str) 1.3.2. presence_server (str) 1.4. Exported Functions 1.4.1. pua_xmpp_notify() 1.4.2. pua_xmpp_req_winfo(char* request_uri, char* expires) 1.5. Filtering 2. Developer Guide List of Examples 1.1. Set server_address parameter 1.2. Set presence_server parameter 1.3. Notify2Xmpp usage 1.4. xmpp_send_winfo usage Chapter 1. Admin Guide 1.1. Overview This module is a gateway for presence between SIP and XMPP. It translates one format into another and uses xmpp, pua and presence modules to manage the transmition of presence state information. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * presence. * pua. * xmpp. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libxml. 1.3. Exported Parameters 1.3.1. server_address(str) The IP address of the server. Example 1.1. Set server_address parameter ... modparam("pua_xmpp", "server_address", "sip:sa@opensips.org:5060") ... 1.3.2. presence_server (str) The the address of the presence server. If set, it will be used as outbound proxy when sending PUBLISH requests. Example 1.2. Set presence_server parameter ... modparam("pua_xmpp", "presence_server", "sip:pa@opensips.org:5075") ... 1.4. Exported Functions Functions exported to be used in configuration file. 1.4.1. pua_xmpp_notify() Function that handles Notify messages addressed to a user from an xmpp domain. It requires filtering after method and domain in configuration file. If the function is successful, a 2xx reply must be sent. This function can be used from REQUEST_ROUTE. Example 1.3. Notify2Xmpp usage ... if( is_method("NOTIFY") && uri=~"sip:.+@sip-xmpp.siphub.ro") { if(Notify2Xmpp()) t_reply("200", "OK"); exit; } ... 1.4.2. pua_xmpp_req_winfo(char* request_uri, char* expires) Function called when a Subscribe addressed to a user from a xmpp domain is received. It calls sending a Subscribe for winfo for the user, and the following Notify with dialog-info is translated into a subscription in xmpp. It also requires filtering in configuration file, after method, domain and event(only for presence). It takes 2 parameters: request_uri and the value of Expires header field in received Subscribe. This function can be used from REQUEST_ROUTE. Example 1.4. xmpp_send_winfo usage ... if( is_method("SUBSCRIBE")) { handle_subscribe(); if(uri=~"sip:.+@sip-xmpp.siphub.ro" && $hdr(Event)== "pr esence") { pua_xmpp_req_winfo("$ruri", "$hdr(Expires)"); } t_release(); } ... 1.5. Filtering Instead of "sip-xmpp.siphub.ro" in the example you should use the value set for the xmpp module parameter named 'gateway_domain'. Chapter 2. Developer Guide The module provides no function to be used in other OpenSIPS modules. opensips-2.2.2/modules/pua_xmpp/doc/000077500000000000000000000000001300170765700174315ustar00rootroot00000000000000opensips-2.2.2/modules/pua_xmpp/doc/pua_xmpp.xml000066400000000000000000000021651300170765700220100ustar00rootroot00000000000000 %docentities; ]> Presence User Agent for XMPP (Presence gateway between SIP and XMPP) &osipsname; Anca-Maria Vamanu Anca-Maria Vamanu 2007 &voicesystem; $Revision: 8740 $ $Date$ &admin; &devel; &faq; opensips-2.2.2/modules/pua_xmpp/doc/pua_xmpp_admin.xml000066400000000000000000000077611300170765700231670ustar00rootroot00000000000000 &adminguide;
Overview This module is a gateway for presence between SIP and XMPP. It translates one format into another and uses xmpp, pua and presence modules to manage the transmition of presence state information.
Dependencies
&osips; Modules The following modules must be loaded before this module: presence. pua. xmpp.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml.
Exported Parameters
<varname>server_address</varname>(str) The IP address of the server. Set <varname>server_address</varname> parameter ... modparam("pua_xmpp", "server_address", "sip:sa@opensips.org:5060") ...
<varname>presence_server</varname> (str) The the address of the presence server. If set, it will be used as outbound proxy when sending PUBLISH requests. Set <varname>presence_server</varname> parameter ... modparam("pua_xmpp", "presence_server", "sip:pa@opensips.org:5075") ...
Exported Functions Functions exported to be used in configuration file.
<function moreinfo="none">pua_xmpp_notify()</function> Function that handles Notify messages addressed to a user from an xmpp domain. It requires filtering after method and domain in configuration file. If the function is successful, a 2xx reply must be sent. This function can be used from REQUEST_ROUTE. <function>Notify2Xmpp</function> usage ... if( is_method("NOTIFY") && uri=~"sip:.+@sip-xmpp.siphub.ro") { if(Notify2Xmpp()) t_reply("200", "OK"); exit; } ...
<function moreinfo="none">pua_xmpp_req_winfo(char* request_uri, char* expires)</function> Function called when a Subscribe addressed to a user from a xmpp domain is received. It calls sending a Subscribe for winfo for the user, and the following Notify with dialog-info is translated into a subscription in xmpp. It also requires filtering in configuration file, after method, domain and event(only for presence). It takes 2 parameters: request_uri and the value of Expires header field in received Subscribe. This function can be used from REQUEST_ROUTE. <function>xmpp_send_winfo</function> usage ... if( is_method("SUBSCRIBE")) { handle_subscribe(); if(uri=~"sip:.+@sip-xmpp.siphub.ro" && $hdr(Event)== "presence") { pua_xmpp_req_winfo("$ruri", "$hdr(Expires)"); } t_release(); } ...
Filtering Instead of "sip-xmpp.siphub.ro" in the example you should use the value set for the xmpp module parameter named 'gateway_domain'.
opensips-2.2.2/modules/pua_xmpp/doc/pua_xmpp_devel.xml000066400000000000000000000002761300170765700231700ustar00rootroot00000000000000 &develguide; The module provides no function to be used in other &osips; modules. opensips-2.2.2/modules/pua_xmpp/pidf.c000066400000000000000000000050241300170765700177530ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (anca) */ #include #include #include #include #include "pidf.h" xmlAttrPtr xmlNodeGetAttrByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = node->properties; while (attr) { if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0) return attr; attr = attr->next; } return NULL; } char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name) { xmlAttrPtr attr = xmlNodeGetAttrByName(node, name); if (attr) return (char*)xmlNodeGetContent(attr->children); else return NULL; } xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name) { xmlNodePtr cur = node->children; while (cur) { if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) return cur; cur = cur->next; } return NULL; } xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns) { xmlNodePtr cur = node; while (cur) { xmlNodePtr match = NULL; if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) { if (!ns || (cur->ns && xmlStrcasecmp(cur->ns->prefix, (unsigned char*)ns) == 0)) return cur; } match = xmlNodeGetNodeByName(cur->children, name, ns); if (match) return match; cur = cur->next; } return NULL; } xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns) { xmlNodePtr cur = doc->children; return xmlNodeGetNodeByName(cur, name, ns); } char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns) { xmlNodePtr node = xmlNodeGetNodeByName(root, name, ns); if (node) return (char*)xmlNodeGetContent(node->children); else return NULL; } opensips-2.2.2/modules/pua_xmpp/pidf.h000066400000000000000000000026621300170765700177650ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (anca) */ #ifndef _PU_PIDF_ #define _PU_PIDF_ #include #include #include #include char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name); xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name); xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns); xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name, const char *ns); char *xmlNodeGetNodeContentByName(xmlNodePtr root, const char *name, const char *ns); #endif opensips-2.2.2/modules/pua_xmpp/pua_xmpp.c000066400000000000000000000164061300170765700206700ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (Anca Vamanu) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../str.h" #include "../../pt.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_expires.h" #include "../../parser/msg_parser.h" #include "../tm/tm_load.h" #include "../xmpp/xmpp_api.h" #include "../pua/pua_bind.h" #include "pua_xmpp.h" #include "xmpp2simple.h" #include "simple2xmpp.h" #include "request_winfo.h" struct tm_binds tmb; /* functions imported from pua module*/ pua_api_t pua; send_publish_t pua_send_publish; send_subscribe_t pua_send_subscribe; query_dialog_t pua_is_dialog; /* functions imported from xmpp module*/ xmpp_api_t xmpp_api; xmpp_send_xsubscribe_f xmpp_subscribe; xmpp_send_xnotify_f xmpp_notify; xmpp_send_xpacket_f xmpp_packet; uri_xmpp2sip_f xmpp_uri_xmpp2sip; uri_sip2xmpp_f xmpp_uri_sip2xmpp; /* libxml wrapper functions */ xmlNodeGetAttrContentByName_t XMLNodeGetAttrContentByName; xmlDocGetNodeByName_t XMLDocGetNodeByName; xmlNodeGetNodeByName_t XMLNodeGetNodeByName; xmlNodeGetNodeContentByName_t XMLNodeGetNodeContentByName; str server_address= {0, 0}; str presence_server= {0, 0}; /** module functions */ static int mod_init(void); static int child_init(int); static int fixup_pua_xmpp(void** param, int param_no); static cmd_export_t cmds[]= { {"pua_xmpp_notify", (cmd_function)Notify2Xmpp, 0, 0, 0, REQUEST_ROUTE}, {"pua_xmpp_req_winfo", (cmd_function)request_winfo, 2, fixup_pua_xmpp, 0,REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[]={ {"server_address", STR_PARAM, &server_address }, {"presence_server", STR_PARAM, &presence_server}, {0, 0, 0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_DEFAULT, "xmpp", DEP_ABORT }, { MOD_TYPE_DEFAULT, "pua", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "pua_xmpp", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions*/ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) 0, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { load_tm_f load_tm; bind_pua_t bind_pua; bind_xmpp_t bind_xmpp; bind_libxml_t bind_libxml; libxml_api_t libxml_api; /* check if compulsory parameter server_address is set */ if(server_address.s== NULL) { LM_ERR("compulsory 'server_address' parameter not set!"); return -1; } server_address.len= strlen(server_address.s); if(presence_server.s) presence_server.len = strlen(presence_server.s); /* import the TM auto-loading function */ if((load_tm=(load_tm_f)find_export("load_tm", 0, 0))==NULL) { LM_ERR("can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if(load_tm(&tmb)==-1) { LM_ERR("can't load tm functions\n"); return -1; } /* bind libxml wrapper functions */ if((bind_libxml= (bind_libxml_t)find_export("bind_libxml_api", 1, 0))== NULL) { LM_ERR("can't import bind_libxml_api\n"); return -1; } if(bind_libxml(&libxml_api)< 0) { LM_ERR("can not bind libxml api\n"); return -1; } XMLNodeGetAttrContentByName= libxml_api.xmlNodeGetAttrContentByName; XMLDocGetNodeByName= libxml_api.xmlDocGetNodeByName; XMLNodeGetNodeByName= libxml_api.xmlNodeGetNodeByName; XMLNodeGetNodeContentByName= libxml_api.xmlNodeGetNodeContentByName; if(XMLNodeGetAttrContentByName== NULL || XMLDocGetNodeByName== NULL || XMLNodeGetNodeByName== NULL || XMLNodeGetNodeContentByName== NULL) { LM_ERR("libxml wrapper functions could not be bound\n"); return -1; } /* bind xmpp */ bind_xmpp= (bind_xmpp_t)find_export("bind_xmpp", 0,0); if (!bind_xmpp) { LM_ERR("Can't bind xmpp\n"); return -1; } if(bind_xmpp(&xmpp_api)< 0) { LM_ERR("Can't bind xmpp\n"); return -1; } if(xmpp_api.xsubscribe== NULL) { LM_ERR("Could not import xsubscribe from xmpp\n"); return -1; } xmpp_subscribe= xmpp_api.xsubscribe; if(xmpp_api.xnotify== NULL) { LM_ERR("Could not import xnotify from xmpp\n"); return -1; } xmpp_notify= xmpp_api.xnotify; if(xmpp_api.xpacket== NULL) { LM_ERR("Could not import xnotify from xmpp\n"); return -1; } xmpp_packet= xmpp_api.xpacket; xmpp_uri_xmpp2sip = xmpp_api.uri_xmpp2sip; xmpp_uri_sip2xmpp = xmpp_api.uri_sip2xmpp; if(xmpp_api.register_callback== NULL) { LM_ERR("Could not import register_callback" " to xmpp\n"); return -1; } if(xmpp_api.register_callback(XMPP_RCV_PRESENCE, pres_Xmpp2Sip, NULL)< 0) { LM_ERR("ERROR while registering callback" " to xmpp\n"); return -1; } /* bind pua */ bind_pua= (bind_pua_t)find_export("bind_pua", 1,0); if (!bind_pua) { LM_ERR("Can't bind pua\n"); return -1; } if (bind_pua(&pua) < 0) { LM_ERR("Can't bind pua\n"); return -1; } if(pua.send_publish == NULL) { LM_ERR("Could not import send_publish\n"); return -1; } pua_send_publish= pua.send_publish; if(pua.send_subscribe == NULL) { LM_ERR("Could not import send_subscribe\n"); return -1; } pua_send_subscribe= pua.send_subscribe; if(pua.is_dialog == NULL) { LM_ERR("Could not import send_subscribe\n"); return -1; } pua_is_dialog= pua.is_dialog; if(pua.register_puacb(XMPP_INITIAL_SUBS, Sipreply2Xmpp, NULL)< 0) { LM_ERR("Could not register callback\n"); return -1; } return 0; } static int child_init(int rank) { LM_DBG("child [%d] pid [%d]\n", rank, getpid()); return 0; } static int fixup_pua_xmpp(void** param, int param_no) { pv_elem_t *model; str s; if(*param) { s.s = (char*)(*param); s.len = strlen(s.s); if(pv_parse_format(&s, &model)<0) { LM_ERR("wrong format[%s]\n",(char*)(*param)); return E_UNSPEC; } *param = (void*)model; return 0; } LM_ERR("null format\n"); return E_UNSPEC; } opensips-2.2.2/modules/pua_xmpp/pua_xmpp.h000066400000000000000000000032401300170765700206650ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (anca) */ #ifndef _XMMPP_TO_SIP_ #define _XMMPP_TO_SIP_ #include "../pua/pidf.h" #include "../pua/pua_bind.h" #include "../xmpp/xmpp_api.h" extern str server_address; extern str presence_server; extern send_subscribe_t pua_send_subscribe; extern send_publish_t pua_send_publish; extern query_dialog_t pua_is_dialog; extern xmpp_send_xsubscribe_f xmpp_subscribe; extern xmpp_send_xnotify_f xmpp_notify; extern xmpp_send_xpacket_f xmpp_packet; extern uri_xmpp2sip_f xmpp_uri_xmpp2sip; extern uri_sip2xmpp_f xmpp_uri_sip2xmpp; extern xmlNodeGetAttrContentByName_t XMLNodeGetAttrContentByName; extern xmlDocGetNodeByName_t XMLDocGetNodeByName; extern xmlNodeGetNodeByName_t XMLNodeGetNodeByName; extern xmlNodeGetNodeContentByName_t XMLNodeGetNodeContentByName; #endif opensips-2.2.2/modules/pua_xmpp/request_winfo.c000066400000000000000000000050021300170765700217170ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (anca) */ #include #include #include #include "pua_xmpp.h" #include "request_winfo.h" #define PRINTBUF_SIZE 256 int request_winfo(struct sip_msg* msg, char* uri, char* expires) { subs_info_t subs; struct sip_uri puri; int printbuf_len; char buffer[PRINTBUF_SIZE]; str uri_str; memset(&puri, 0, sizeof(struct sip_uri)); if(uri) { printbuf_len = PRINTBUF_SIZE-1; if(pv_printf(msg, (pv_elem_t*)uri, buffer, &printbuf_len)<0) { LM_ERR("cannot print the format\n"); return -1; } if(parse_uri(buffer, printbuf_len, &puri)!=0) { LM_ERR("bad owner SIP address!\n"); goto error; } else { LM_DBG("using user id [%.*s]\n", printbuf_len, buffer); } } if(puri.user.len<=0 || puri.user.s==NULL || puri.host.len<=0 || puri.host.s==NULL) { LM_ERR("bad owner URI!\n"); goto error; } uri_str.s= buffer; uri_str.len= printbuf_len; LM_DBG("uri= %.*s:\n", uri_str.len, uri_str.s); memset(&subs, 0, sizeof(subs_info_t)); subs.pres_uri= &uri_str; subs.watcher_uri= &uri_str; subs.contact= &server_address; if(presence_server.s && presence_server.len) subs.outbound_proxy = &presence_server; if(strncmp(expires, "0", 1 )== 0) { subs.expires= 0; } else { subs.expires= -1; } /* -1 - for a subscription with no time limit */ /* 0 -for unsubscribe */ subs.source_flag |= XMPP_SUBSCRIBE; subs.event= PWINFO_EVENT; if(presence_server.s && presence_server.len) subs.outbound_proxy = &presence_server; if(pua_send_subscribe(&subs)< 0) { LM_ERR("while sending subscribe\n"); goto error; } return 1; error: return 0; } opensips-2.2.2/modules/pua_xmpp/request_winfo.h000066400000000000000000000021031300170765700217230ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (anca) */ #ifndef XMPP_SWINFO_H #define XMPP_SWINFO_H #include "../../parser/msg_parser.h" int request_winfo(struct sip_msg* msg, char* uri, char* expires); #endif opensips-2.2.2/modules/pua_xmpp/simple2xmpp.c000066400000000000000000000500641300170765700213150ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (anca) */ #include #include #include #include "../../ut.h" #include "../../str.h" #include "../../dprint.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_content.h" #include "../../parser/parse_fline.h" #include "../../mem/mem.h" #include "pua_xmpp.h" #include "simple2xmpp.h" #define URI_BUF_LEN 256 #define URI_ADD_NULL_TERM(dst, buf, src) \ do{\ if(src->len+1 > URI_BUF_LEN) {\ LM_ERR("Length exceeds buffer len [%d]\n", URI_BUF_LEN);\ goto error;\ }\ memcpy(buf, src->s+4, src->len-4);\ buf[src->len-4]= '\0';\ dst.s = buf;\ dst.len= src->len-4;\ }while(0) int winfo2xmpp(str* to_uri, str* body, str* id); int build_xmpp_content(str* to_uri, str* from_uri, str* body, str* id, int is_terminated); int Notify2Xmpp(struct sip_msg* msg, char* s1, char* s2) { struct to_body *pto, *pfrom= NULL; str to_uri; str from_uri={0, 0}; struct hdr_field* hdr= NULL; str body; xmlDocPtr doc= NULL; int is_terminated= 0; str id; ua_pres_t dialog; int event_flag= 0; char buf_to[256]; memset(&dialog, 0, sizeof(ua_pres_t)); LM_DBG("start...\n\n"); if( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("parsing headers\n"); return -1; } if((!msg->event ) ||(msg->event->body.len<=0)) { LM_ERR("Missing event header field value\n"); return -1; } if( msg->to==NULL || msg->to->body.s==NULL) { LM_ERR("cannot parse TO header\n"); return -1; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); return -1; } dialog.watcher_uri= &pto->uri; URI_ADD_NULL_TERM(to_uri, buf_to, dialog.watcher_uri); if (pto->tag_value.s==NULL || pto->tag_value.len==0 ) { LM_ERR("to tag value not parsed\n"); goto error; } id= pto->tag_value; dialog.from_tag= id; if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); goto error; } dialog.call_id = msg->callid->body; if (!msg->from || !msg->from->body.s) { LM_ERR("ERROR cannot find 'from' header!\n"); goto error; } if (msg->from->parsed == NULL) { /* parsing from header */ if ( parse_from_header( msg )<0 ) { LM_ERR("ERROR cannot parse From header\n"); goto error; } } pfrom = (struct to_body*)msg->from->parsed; dialog.pres_uri= &pfrom->uri; from_uri.s = xmpp_uri_sip2xmpp(dialog.pres_uri); if(from_uri.s == 0) { LM_ERR("Failed to translate uri from sip to xmpp [%.*s]\n", dialog.pres_uri->len, dialog.pres_uri->s); goto error; } from_uri.len= strlen(from_uri.s); if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); goto error; } dialog.to_tag= pfrom->tag_value; dialog.flag|= XMPP_SUBSCRIBE; if(msg->event->body.len== 8 && (strncasecmp(msg->event->body.s,"presence",8 )==0)) event_flag|= PRESENCE_EVENT; else if(msg->event->body.len== 14 && (strncasecmp(msg->event->body.s,"presence.winfo",14 )==0)) event_flag|= PWINFO_EVENT; else { LM_ERR("wrong event\n"); goto error; } dialog.event= event_flag; if(pua_is_dialog(&dialog)< 0) // verify if within a stored dialog { LM_ERR("Notify in a non existing dialog\n"); goto error; } /*constructing the xml body*/ if(get_content_length(msg) == 0 ) { body.s= NULL; body.len= 0; } else { if ( get_body(msg,&body)!=0 || body.len==0) { LM_ERR("cannot extract body from msg\n"); goto error; } } /* treat the two cases: event= presence & event=presence.winfo */ if(event_flag & PRESENCE_EVENT) { LM_DBG("PRESENCE\n"); hdr = get_header_by_static_name( msg, "Subscription-State" ); if(hdr && strncasecmp(hdr->body.s,"terminated", 10)== 0) { /* chack if reason timeout => don't send notification */ if(strncasecmp(hdr->body.s+11,"reason=timeout", 14)== 0) { LM_DBG("Received Notification with state" "terminated; reason= timeout=> don't send notification\n"); return 1; } is_terminated= 1; } if(build_xmpp_content(&to_uri, &from_uri, &body, &id, is_terminated)< 0) { LM_ERR("in function build_xmpp_content\n"); goto error; } xmlFreeDoc(doc); } else { if(event_flag & PWINFO_EVENT) { LM_DBG("PRESENCE.WINFO\n"); hdr = get_header_by_static_name( msg, "Subscription-State" ); if(hdr && strncasecmp(hdr->body.s,"terminated", 10)== 0) { LM_DBG("Notify for presence.winfo with" " Subscription-State terminated- should not translate\n"); goto error; } if(winfo2xmpp(&to_uri, &body, &id)< 0) { LM_ERR("while sending subscription\n"); goto error; } } else { LM_ERR("Missing or unsupported event header field value\n"); goto error; } } return 1; error: if(doc) xmlFreeDoc(doc); return 0; } int build_xmpp_content(str* to_uri, str* from_uri, str* body, str* id, int is_terminated) { xmlDocPtr sip_doc= NULL; xmlDocPtr doc= NULL; xmlNodePtr xmpp_root= NULL; xmlNodePtr sip_root= NULL; xmlNodePtr new_node= NULL; xmlNodePtr node = NULL; xmlBufferPtr buffer= NULL; xmlAttrPtr attr= NULL; char* basic= NULL, *priority= NULL, *note= NULL; str xmpp_msg; LM_DBG("start...\n"); /* creating the xml doc for the xmpp message*/ doc= xmlNewDoc(0); if(doc== NULL) { LM_ERR("when creating new xml doc\n"); goto error; } xmpp_root = xmlNewNode(NULL, BAD_CAST "presence"); if(xmpp_root==0) { LM_ERR("when adding new node- presence\n"); goto error; } xmlDocSetRootElement(doc, xmpp_root); attr= xmlNewProp(xmpp_root, BAD_CAST "to", BAD_CAST to_uri->s); if(attr== NULL) { LM_ERR("while adding new attribute\n"); goto error; } attr= xmlNewProp(xmpp_root, BAD_CAST "from", BAD_CAST from_uri->s); if(attr== NULL) { LM_ERR("while adding new attribute\n"); goto error; } if(is_terminated) { attr= xmlNewProp(xmpp_root, BAD_CAST "type", BAD_CAST "unsubscribed"); if(attr== NULL) { LM_ERR("while adding new attribute\n"); goto error; } goto done; } if(body->s== NULL) { attr= xmlNewProp(xmpp_root, BAD_CAST "type", BAD_CAST "unavailable"); if(attr== NULL) { LM_ERR("while adding new attribute\n"); goto error; } goto done; } /*extractiong the information from the sip message body*/ sip_doc= xmlParseMemory(body->s, body->len); if(sip_doc== NULL) { LM_ERR("while parsing xml memory\n"); return -1; } sip_root= XMLDocGetNodeByName(sip_doc, "presence", NULL); if(sip_root== NULL) { LM_ERR("while extracting 'presence' node\n"); goto error; } node = XMLNodeGetNodeByName(sip_root, "basic", NULL); if(node== NULL) { LM_ERR("while extracting status basic node\n"); goto error; } basic= (char*)xmlNodeGetContent(node); if(basic== NULL) { LM_ERR("while extracting status basic node content\n"); goto error; } if(xmlStrcasecmp( (unsigned char*)basic,(unsigned char*) "closed")==0 ) { attr= xmlNewProp(xmpp_root, BAD_CAST "type", BAD_CAST "unavailable"); if(attr== NULL) { LM_ERR("while adding node attr\n"); xmlFree(basic); goto error; } xmlFree(basic); goto done; }/* else the status is open so no type attr should be added */ xmlFree(basic); /* addind show node */ node= XMLNodeGetNodeByName(sip_root, "note", NULL); if(node== NULL) { LM_DBG("No note node found\n"); node= XMLNodeGetNodeByName(sip_root, "person", NULL); if(node== NULL) { LM_DBG("No person node found\n"); goto done; } node= XMLNodeGetNodeByName(node, "note", NULL); if(node== NULL) { LM_DBG("Person node has no note node\n"); goto done; } } note= (char*)xmlNodeGetContent(node); if(note== NULL) { LM_ERR("while extracting note node content\n"); goto error; } if(xmlStrcasecmp((unsigned char*)note, (unsigned char*)"away")== 0) { new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "show", BAD_CAST "away"); if(new_node== NULL) { LM_ERR("while adding node show: away\n"); goto error; } } else if(xmlStrcasecmp((unsigned char*)note, (unsigned char*)"busy")== 0) { new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "show" , BAD_CAST "xa"); if(new_node== NULL) { LM_ERR("while adding node show: away\n"); goto error; } } /* if(xmlStrcasecmp((unsigned char*)note, (unsigned char*)"on the phone")== 0) { new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "show", BAD_CAST "chat"); if(new_node== NULL) { LM_ERR("while adding node show: chat\n"); goto error; } } else if(xmlStrcasecmp((unsigned char*)note, (unsigned char*)"idle")== 0) { new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "show", BAD_CAST "idle"); if(new_node== NULL) { LM_ERR("while adding node: idle\n"); goto error; } } else */ if((xmlStrcasecmp((unsigned char*)note, (unsigned char*)"dnd")== 0)|| (xmlStrcasecmp((unsigned char*)note, (unsigned char*)"do not disturb")== 0)) { new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "show", BAD_CAST "dnd"); if(new_node== NULL) { LM_ERR("while adding node show: dnd\n"); goto error; } } /* adding status node */ new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "status", BAD_CAST note); if(new_node== NULL) { LM_ERR("while adding node status\n"); goto error; } xmlFree(note); note= NULL; /* adding priotity node*/ node= XMLNodeGetNodeByName(sip_root, "contact", NULL); if(node== NULL) { LM_DBG("No contact node found\n"); } else { priority= XMLNodeGetAttrContentByName(node, "priority"); if(priority== NULL) LM_DBG("No priority attribute found\n"); else { new_node= xmlNewChild(xmpp_root, NULL, BAD_CAST "priority", BAD_CAST priority); if(sip_root== NULL) { LM_ERR("while adding node\n"); xmlFree(priority); goto error; } xmlFree(priority); } } done: buffer= xmlBufferCreate(); if(buffer== NULL) { LM_ERR("while adding creating new buffer\n"); goto error; } xmpp_msg.len= xmlNodeDump(buffer, doc, xmpp_root, 1,1); if(xmpp_msg.len== -1) { LM_ERR("while dumping node\n"); goto error; } xmpp_msg.s= (char*)xmlBufferContent( buffer); if(xmpp_msg.s== NULL) { LM_ERR("while extracting buffer content\n"); goto error; } LM_DBG("xmpp_msg: %.*s\n",xmpp_msg.len, xmpp_msg.s); if( xmpp_notify(from_uri, to_uri, &xmpp_msg, id)< 0) { LM_ERR("while sending xmpp_notify\n"); goto error; } xmlBufferFree(buffer); xmlCleanupParser(); xmlMemoryDump(); if(sip_doc) xmlFreeDoc(sip_doc); if(doc) xmlFreeDoc(doc); return 0; error: if(sip_doc) xmlFreeDoc(sip_doc); if(note) xmlFree(note); if(buffer) xmlBufferFree(buffer); xmlCleanupParser(); xmlMemoryDump(); return -1; } int winfo2xmpp(str* to_uri, str* body, str* id) { xmlAttrPtr attr= NULL; str xmpp_msg; char* watcher= NULL ; str from_uri = {0, 0}; xmlDocPtr notify_doc= NULL; xmlDocPtr doc= NULL; xmlNodePtr pidf_root= NULL; xmlNodePtr root_node= NULL; xmlNodePtr node= NULL; xmlBufferPtr buffer= NULL; str watcher_str; LM_DBG("start...\n"); notify_doc= xmlParseMemory(body->s, body->len); if(notify_doc== NULL) { LM_ERR("while parsing xml memory\n"); return -1; } pidf_root= XMLDocGetNodeByName(notify_doc, "watcherinfo", NULL); if(pidf_root== NULL) { LM_ERR("while extracting 'presence' node\n"); goto error; } node = XMLNodeGetNodeByName(pidf_root, "watcher", NULL); for (; node!=NULL; node = node->next) { if( xmlStrcasecmp(node->name,(unsigned char*)"watcher")) continue; watcher= (char*)xmlNodeGetContent(node->children); if(watcher== NULL) { LM_ERR("while extracting watcher node content\n"); goto error; } watcher_str.s = watcher; watcher_str.len = strlen(watcher); from_uri.s = xmpp_uri_sip2xmpp(&watcher_str); if(from_uri.s == NULL) { LM_ERR("Failed to transform uri from sip to xmpp\n"); goto error; } from_uri.len = strlen(from_uri.s); xmlFree(watcher); watcher= NULL; doc= xmlNewDoc( 0 ); if(doc== NULL) { LM_ERR("when creating new xml doc\n"); goto error; } root_node = xmlNewNode(NULL, BAD_CAST "presence"); if(root_node== NULL) { LM_ERR("when adding new node\n"); goto error; } xmlDocSetRootElement(doc, root_node); attr= xmlNewProp(root_node, BAD_CAST "to", BAD_CAST to_uri->s); if(attr== NULL) { LM_ERR("while adding attribute to_uri\n"); goto error; } attr= xmlNewProp(root_node, BAD_CAST "from", BAD_CAST from_uri.s); if(attr== NULL) { LM_ERR("while adding attribute from_uri\n"); goto error; } attr= xmlNewProp(root_node, BAD_CAST "type", BAD_CAST "subscribe"); if(attr== NULL) { LM_ERR("while adding attribute type\n"); goto error; } buffer= xmlBufferCreate(); if(buffer== NULL) { LM_ERR("while adding creating new buffer\n"); goto error; } xmpp_msg.len= xmlNodeDump(buffer, doc, root_node, 1,1); if(xmpp_msg.len== -1) { LM_ERR("while dumping node\n"); goto error; } xmpp_msg.s= (char*)xmlBufferContent( buffer); if(xmpp_msg.s== NULL) { LM_ERR("while extracting buffer content\n"); goto error; } LM_DBG("xmpp_msg: %.*s\n",xmpp_msg.len, xmpp_msg.s); if( xmpp_subscribe(&from_uri, to_uri, &xmpp_msg, id)< 0) { LM_ERR("while sending xmpp_subscribe\n"); goto error; } xmlBufferFree(buffer); buffer= NULL; xmlFreeDoc(doc); doc= NULL; } xmlFreeDoc(notify_doc); xmlCleanupParser(); xmlMemoryDump(); return 0; error: if(doc) xmlFreeDoc(doc); if(notify_doc) xmlFreeDoc(notify_doc); if(watcher) xmlFree(watcher); if(buffer) xmlBufferFree(buffer); xmlCleanupParser(); xmlMemoryDump(); return -1; } char* get_error_reason(int code, str* reason) { char* err_cond= NULL; err_cond= (char*)pkg_malloc(50* sizeof(char)); if(err_cond== NULL) { LM_ERR("no more memory\n"); return NULL; } switch( code ) { case 300: { strcpy(err_cond, "redirect"); break;} case 301: { strcpy(err_cond, "gone"); break;} case 302: { strcpy(err_cond, "redirect"); break;} case 305: { strcpy(err_cond, "redirect"); break;} case 380: { strcpy(err_cond, "not-acceptable"); break;} case 400: { strcpy(err_cond, "bad-request"); break;} case 401: { strcpy(err_cond, "not-authorized"); break;} case 402: { strcpy(err_cond, "payment-required"); break;} case 403: { strcpy(err_cond, "forbidden"); break;} case 404: { strcpy(err_cond, "item-not-found"); break;} case 405: { strcpy(err_cond, "not-allowed"); break;} case 406: { strcpy(err_cond, "not-acceptable"); break;} case 407: { strcpy(err_cond, "registration-required"); break;} case 408: { strcpy(err_cond, "service-unavailable"); break;} case 410: { strcpy(err_cond, "gone"); break;} case 413: { strcpy(err_cond, "bad-request"); break;} case 414: { strcpy(err_cond, "bad-request"); break;} case 415: { strcpy(err_cond, "bad-request"); break;} case 416: { strcpy(err_cond, "bad-request"); break;} case 420: { strcpy(err_cond, "bad-request"); break;} case 421: { strcpy(err_cond, "bad-request"); break;} case 423: { strcpy(err_cond, "bad-request"); break;} case 480: { strcpy(err_cond, "recipient-unavailable"); break;} case 481: { strcpy(err_cond, "item-not-found"); break;} case 482: { strcpy(err_cond, "not-acceptable"); break;} case 483: { strcpy(err_cond, "not-acceptable"); break;} case 484: { strcpy(err_cond, "jid-malformed"); break;} case 485: { strcpy(err_cond, "item-not-found"); break;} case 488: { strcpy(err_cond, "not-acceptable"); break;} case 491: { strcpy(err_cond, "unexpected-request"); break;} case 500: { strcpy(err_cond, "internal-server-error"); break;} case 501: { strcpy(err_cond, "feature-not-implemented"); break;} case 502: { strcpy(err_cond, "remote-server-not-found"); break;} case 503: { strcpy(err_cond, "service-unavailable"); break;} case 504: { strcpy(err_cond, "remote-server-timeout"); break;} case 505: { strcpy(err_cond, "not-acceptable"); break;} case 513: { strcpy(err_cond, "bad-request"); break;} case 600: { strcpy(err_cond, "service-unavailable"); break;} case 603: { strcpy(err_cond, "service-unavailable"); break;} case 604: { strcpy(err_cond, "item-not-found"); break;} case 606: { strcpy(err_cond, "not-acceptable"); break;} default: { strcpy(err_cond, "not-acceptable"); break;} } return err_cond; } int Sipreply2Xmpp(ua_pres_t* hentity, struct sip_msg * msg) { /* named according to the direction of the message in xmpp*/ str from_uri; str to_uri; xmlDocPtr doc= NULL; xmlNodePtr root_node= NULL, node = NULL; xmlAttrPtr attr= NULL; str xmpp_msg; int code; str reason; char* err_reason= NULL; xmlBufferPtr buffer= NULL; char buf_to[256]; LM_DBG("*** Entered the callback\n"); URI_ADD_NULL_TERM(to_uri, buf_to, hentity->watcher_uri); from_uri.s = xmpp_uri_sip2xmpp(hentity->pres_uri); if(from_uri.s == NULL) { LM_ERR("Failed to traslate sip uri to xmpp uri\n"); return -1; } from_uri.len= strlen(from_uri.s); doc= xmlNewDoc(BAD_CAST "1.0"); if(doc==0) goto error; root_node = xmlNewNode(NULL, BAD_CAST "presence"); if(root_node==0) goto error; xmlDocSetRootElement(doc, root_node); attr= xmlNewProp(root_node, BAD_CAST "to", BAD_CAST to_uri.s); if(attr== NULL) { LM_ERR("while adding attribute to\n"); goto error; } attr= xmlNewProp(root_node, BAD_CAST "from", BAD_CAST from_uri.s); if(attr== NULL) { LM_ERR("while adding attribute from\n"); goto error; } if(msg== FAKED_REPLY) { code = 408; reason.s= "Request Timeout"; reason.len= strlen(reason.s)- 1; } else { code= msg->first_line.u.reply.statuscode; reason= msg->first_line.u.reply.reason; } LM_DBG("SIP reply code=%d ; to_uri= %s ; from_uri= %s\n", code, to_uri.s, from_uri.s); if(code>=300) { err_reason= get_error_reason(code, &reason); if(err_reason== NULL) { LM_ERR("couldn't get response phrase\n"); goto error; } attr= xmlNewProp(root_node, BAD_CAST "type", BAD_CAST "error"); if(attr== NULL) { LM_ERR("while adding new attribute\n"); goto error; } node= xmlNewChild(root_node, 0, BAD_CAST "error", 0 ); if(node== NULL) { LM_ERR("while adding new node\n"); goto error; } node= xmlNewChild(node, 0, BAD_CAST err_reason, 0 ); if(node== NULL) { LM_ERR("while adding new node\n"); goto error; } attr= xmlNewProp(node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:xmpp-stanzas"); if(attr== NULL) { LM_ERR("while adding new attribute\n"); goto error; } } else if(code>=200 ) { attr= xmlNewProp(root_node, BAD_CAST "type", BAD_CAST "subscribed"); if(attr== NULL) { LM_ERR("while adding new attribute\n"); goto error; } } buffer= xmlBufferCreate(); if(buffer== NULL) { LM_ERR("while adding creating new buffer\n"); goto error; } xmpp_msg.len= xmlNodeDump(buffer, doc, root_node, 1,1); if(xmpp_msg.len== -1) { LM_ERR("while dumping node\n"); goto error; } xmpp_msg.s= (char*)xmlBufferContent( buffer); if(xmpp_msg.s== NULL) { LM_ERR("while extracting buffer content\n"); goto error; } LM_DBG("xmpp_msg: %.*s\n",xmpp_msg.len, xmpp_msg.s); if(xmpp_packet(&from_uri, &to_uri, &xmpp_msg, &hentity->to_tag)< 0) { LM_ERR("while sending xmpp_reply_to_subscribe\n"); goto error; } if(err_reason) pkg_free(err_reason); xmlFreeDoc(doc); return 0; error: if(doc) xmlFreeDoc(doc); if(err_reason) pkg_free(err_reason); return -1; } opensips-2.2.2/modules/pua_xmpp/simple2xmpp.h000066400000000000000000000021651300170765700213210ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (anca) */ #ifndef SIP_XMPP_PRES_H #define SIP_XMPP_PRES_H #include "../pua/pua_bind.h" int Notify2Xmpp(struct sip_msg* msg, char* s1, char* s2); int Sipreply2Xmpp(ua_pres_t* hentity, struct sip_msg * msg); #endif opensips-2.2.2/modules/pua_xmpp/xmpp2simple.c000066400000000000000000000254101300170765700213120ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (Anca Vamanu) */ #include #include #include #include #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../parser/parse_content.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../pua/pua.h" #include "../xmpp/xmpp.h" #include "pua_xmpp.h" int build_publish(xmlNodePtr pres_node, int expire); int presence_subscribe(xmlNodePtr pres_node, int expires, int flag); /* the function registered as a callback in xmpp, * to be called when a new message with presence type is received * */ void pres_Xmpp2Sip(char *msg, int type, void *param) { xmlDocPtr doc= NULL; xmlNodePtr pres_node= NULL; char* pres_type= NULL; doc= xmlParseMemory(msg, strlen(msg)); if(doc == NULL) { LM_ERR("while parsing xml memory\n"); return; } pres_node= XMLDocGetNodeByName(doc, "presence", NULL); if(pres_node == NULL) { LM_ERR("while getting node\n"); goto error; } pres_type= XMLNodeGetAttrContentByName(pres_node, "type" ); if(pres_type== NULL ) { LM_DBG("type attribut not present\n"); build_publish(pres_node, -1); if(presence_subscribe(pres_node, 3600, XMPP_SUBSCRIBE)< 0) { LM_ERR("when sending subscribe for presence"); xmlFree(pres_type); goto error; } /* send subscribe after publish because in xmpp subscribe message * comes only when a new contact is inserted in buddy list */ } else if(strcmp(pres_type, "unavailable")== 0) { build_publish(pres_node, 0); if(presence_subscribe(pres_node, 3600, XMPP_SUBSCRIBE)< 0) /* else subscribe for one hour*/ { LM_ERR("when unsubscribing for presence"); xmlFree(pres_type); goto error; } } else if((strcmp(pres_type, "subscribe")==0)|| ( strcmp(pres_type, "unsubscribe")== 0)|| (strcmp(pres_type, "probe")== 0)) { if(strcmp(pres_type, "subscribe")==0 || strcmp(pres_type, "probe")== 0) { LM_DBG("send Subscribe message (no time limit)\n"); if(presence_subscribe(pres_node, -1, XMPP_INITIAL_SUBS)< 0) { LM_ERR("when sending subscribe for presence"); xmlFree(pres_type); goto error; } } if(strcmp(pres_type, "unsubscribe")== 0) { if(presence_subscribe(pres_node, 0, XMPP_INITIAL_SUBS)< 0) { LM_ERR("when unsubscribing for presence"); xmlFree(pres_type); goto error; } } } xmlFree(pres_type); // else // send_reply_message(pres_node); xmlFreeDoc(doc); xmlCleanupParser(); xmlMemoryDump(); return ; error: if(doc) xmlFreeDoc(doc); xmlCleanupParser(); xmlMemoryDump(); return ; } str* build_pidf(xmlNodePtr pres_node, char* uri, char* resource) { str* body= NULL; xmlDocPtr doc= NULL; xmlNodePtr root_node= NULL, status_node= NULL; xmlNodePtr node= NULL, person_node= NULL; xmlNodePtr tuple_node= NULL, basic_node= NULL; char* show_cont= NULL, *status_cont= NULL; char* entity= NULL; char* type= NULL; char* status= NULL; entity=(char*)pkg_malloc(7+ strlen(uri)*sizeof(char)); if(entity== NULL) { LM_ERR("no more memory\n"); goto error; } strcpy(entity, "pres:"); memcpy(entity+5, uri+4, strlen(uri)-4); entity[1+ strlen(uri)]= '\0'; doc= xmlNewDoc(BAD_CAST "1.0"); if(doc== NULL) { LM_ERR("allocating new xml doc\n"); goto error; } root_node = xmlNewNode(NULL, BAD_CAST "presence"); if(root_node== 0) { LM_ERR("extracting presence node\n"); goto error; } xmlDocSetRootElement(doc, root_node); xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:pidf"); xmlNewProp(root_node, BAD_CAST "xmlns:dm", BAD_CAST "urn:ietf:params:xml:ns:pidf:data-model"); xmlNewProp(root_node, BAD_CAST "xmlns:rpid", BAD_CAST "urn:ietf:params:xml:ns:pidf:rpid" ); xmlNewProp(root_node, BAD_CAST "xmlns:c", BAD_CAST "urn:ietf:params:xml:ns:pidf:cipid"); xmlNewProp(root_node, BAD_CAST "entity", BAD_CAST entity); tuple_node =xmlNewChild(root_node, NULL, BAD_CAST "tuple", NULL) ; if( tuple_node ==NULL) { LM_ERR("while adding child\n"); goto error; } status_node = xmlNewChild(tuple_node, NULL, BAD_CAST "status", NULL) ; if( status_node ==NULL) { LM_ERR("while adding child\n"); goto error; } type= XMLNodeGetAttrContentByName(pres_node, "type"); if(type== NULL) { basic_node = xmlNewChild(status_node, NULL, BAD_CAST "basic", BAD_CAST "open") ; if( basic_node ==NULL) { LM_ERR("while adding child\n"); goto error; } } else { basic_node = xmlNewChild(status_node, NULL, BAD_CAST "basic", BAD_CAST "closed") ; if( basic_node ==NULL) { LM_ERR("while adding child\n"); goto error; } goto done; } /*if no type present search for suplimentary information */ status_cont= XMLNodeGetNodeContentByName(pres_node, "status", NULL); show_cont= XMLNodeGetNodeContentByName(pres_node, "show", NULL); if(show_cont) { if(strcmp(show_cont, "xa")== 0) status= "not available"; else if(strcmp(show_cont, "chat")== 0) status= "free for chat"; else if(strcmp(show_cont, "dnd")== 0) status= "do not disturb"; else status= show_cont; } if(status_cont) { /* person_node= xmlNewChild(root_node, NULL, BAD_CAST "person", 0); if(person_node== NULL) { LM_ERR("while adding node\n"); goto error; } */ node = xmlNewChild(root_node, NULL, BAD_CAST "note", BAD_CAST status_cont); if(node== NULL) { LM_ERR("while adding node\n"); goto error; } }else if(show_cont) { node = xmlNewChild(root_node, NULL, BAD_CAST "note", BAD_CAST status); if(node== NULL) { LM_ERR("while adding node\n"); goto error; } } if(show_cont) { LM_DBG("show_cont= %s\n", show_cont); if(person_node== NULL) { person_node= xmlNewChild(root_node, NULL, BAD_CAST "person",0 ); if(person_node== NULL) { LM_ERR("while adding node\n"); goto error; } } node= xmlNewChild(person_node, NULL, BAD_CAST "activities", BAD_CAST 0); if(node== NULL) { LM_ERR("while adding node\n"); goto error; } if( xmlNewChild(person_node, NULL, BAD_CAST "note", BAD_CAST status )== NULL) { LM_ERR("while adding node\n"); goto error; } } done: body= (str* )pkg_malloc(sizeof(str)); if(body== NULL) { LM_ERR("no more memory\n"); goto error; } xmlDocDumpMemory(doc,(xmlChar**)(void*)&body->s, &body->len); if(entity) pkg_free(entity); if(status_cont) xmlFree(status_cont); if(show_cont) xmlFree(show_cont); if(type) xmlFree(type); xmlFreeDoc(doc); return body; error: if(entity) pkg_free(entity); if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } if(status_cont) xmlFree(status_cont); if(show_cont) xmlFree(show_cont); if(type) xmlFree(type); if(doc) xmlFreeDoc(doc); return NULL; } int build_publish(xmlNodePtr pres_node, int expires) { str* body= NULL; publ_info_t publ; char* resource= NULL; str pres_uri= {0, 0}; char* slash; char buf[256]; char* uri; uri = XMLNodeGetAttrContentByName(pres_node, "from"); if(uri == NULL) { LM_DBG("getting 'from' attribute\n"); return -1; } ENC_SIP_URI(pres_uri, buf, uri); xmlFree(uri); slash= memchr(pres_uri.s, '/', pres_uri.len); if(slash) { pres_uri.len= slash- pres_uri.s; resource= (char*)pkg_malloc((strlen(pres_uri.s)-pres_uri.len)*sizeof(char)); if(resource== NULL) { LM_ERR("no more memory\n"); return -1; } strcpy(resource, slash+1); } body= build_pidf(pres_node, pres_uri.s, resource); if(body== NULL) { LM_ERR("while constructing PUBLISH body\n"); goto error; } /* construct the publ_info_t structure */ memset(&publ, 0, sizeof(publ_info_t)); publ.pres_uri= &pres_uri; publ.body= body; LM_DBG("Publish for [%s] body:\n %.*s - %d\n", pres_uri.s, publ.body->len, publ.body->s, publ.body->len); publ.source_flag|= XMPP_PUBLISH; publ.expires= expires; publ.event= PRESENCE_EVENT; publ.extra_headers= NULL; publ.outbound_proxy = presence_server; if( pua_send_publish(&publ)< 0) { LM_ERR("while sending publish\n"); goto error; } if(resource) pkg_free(resource); if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } return 0; error: if(resource) pkg_free(resource); if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } return -1; } int presence_subscribe(xmlNodePtr pres_node, int expires,int flag) { subs_info_t subs; char* type= NULL, *uri= NULL; str to_uri= {0, 0}; str from_uri= {0, 0}; char buf_from[256]; uri= XMLNodeGetAttrContentByName(pres_node, "to"); if(uri== NULL) { LM_ERR("failed to get to attribute from xml doc\n"); return -1; } to_uri.s = xmpp_uri_xmpp2sip(uri, &to_uri.len); if(to_uri.s == 0) { LM_ERR("failed to get from attribute from xml doc\n"); goto error; } xmlFree(uri); uri= XMLNodeGetAttrContentByName(pres_node, "from"); if(uri == NULL) { LM_ERR("failed to get from attribute from xml doc\n"); goto error; } ENC_SIP_URI(from_uri, buf_from, uri); xmlFree(uri); memset(&subs, 0, sizeof(subs_info_t)); subs.pres_uri= &to_uri; subs.watcher_uri= &from_uri; subs.contact= &server_address; if(presence_server.s) subs.outbound_proxy = &presence_server; /* type= XMLNodeGetAttrContentByName(pres_node, "type" ); if(strcmp(type, "subscribe")==0 ||strcmp(type, "probe")== 0) subs->flag|= INSERT_TYPE; else if(strcmp(type, "unsubscribe")== 0) subs->flag|= UPDATE_TYPE; xmlFree(type); type= NULL; */ subs.source_flag|= flag; subs.event= PRESENCE_EVENT; subs.expires= expires; if(presence_server.s && presence_server.len) subs.outbound_proxy = &presence_server; LM_DBG("XMPP subscription to [%.*s] , from [%.*s], expires= [%d]\n", subs.pres_uri->len, subs.pres_uri->s, subs.watcher_uri->len, subs.watcher_uri->s, expires); if(subs.outbound_proxy) LM_DBG("outbound_proxy= %.*s\n", subs.outbound_proxy->len, subs.outbound_proxy->s); if(pua_send_subscribe(&subs)< 0) { LM_ERR("while sending SUBSCRIBE\n"); goto error; } return 0; error: if(type) xmlFree(type); return -1; } opensips-2.2.2/modules/pua_xmpp/xmpp2simple.h000066400000000000000000000021041300170765700213120ustar00rootroot00000000000000/* * pua_xmpp module - presence SIP - XMPP Gateway * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-03-29 initial version (anca) */ #ifndef _XMPP_SIMPLE_PRES #define _XMPP_SIMPLE_PRES #include "../../parser/msg_parser.h" void pres_Xmpp2Sip(char* xmpp_msg, int type, void *param); #endif opensips-2.2.2/modules/python/000077500000000000000000000000001300170765700163545ustar00rootroot00000000000000opensips-2.2.2/modules/python/Makefile000066400000000000000000000021711300170765700200150ustar00rootroot00000000000000# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=python.so # If you have multiple Python versions installed make sure to modify the # the following to point to the correct instance. Module has been tested # to work with 2.6 and 2.5. Python 2.4 has been only confirmed to compile, # but no testing has been done with that. PYTHON?=python PYTHON_VERSION=${shell ${PYTHON} -c "import distutils.sysconfig;print distutils.sysconfig.get_config_var('VERSION')"} PYTHON_LIBDIR=${shell ${PYTHON} -c "import distutils.sysconfig;print distutils.sysconfig.get_config_var('LIBDIR')"} PYTHON_LDFLAGS=${shell ${PYTHON} -c "import distutils.sysconfig;print distutils.sysconfig.get_config_var('LINKFORSHARED')"} PYTHON_INCDIR=${shell ${PYTHON} -c "import distutils.sysconfig;print distutils.sysconfig.get_python_inc()"} LIBS=-L${PYTHON_LIBDIR} ${PYTHON_LDFLAGS} -lpython${PYTHON_VERSION} ifeq ($(OS), freebsd) LIBS+=-pthread endif DEFS+=-I${PYTHON_INCDIR} include ../../Makefile.modules opensips-2.2.2/modules/python/README000066400000000000000000000121141300170765700172330ustar00rootroot00000000000000Python Module Maxim Sobolev Sippy Software, Inc. Edited by Razvan Crainea Copyright © 2009 Sippy Software, Inc. __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. script_name (string) 1.3.2. mod_init_function (string) 1.3.3. child_init_method (string) 1.4. Exported Functions 1.4.1. python_exec(method_name [, extra_args]) List of Examples 1.1. Set script_name parameter 1.2. Set mod_init_function parameter 1.3. Set child_init_method parameter Chapter 1. Admin Guide 1.1. Overview This module can be used to efficiently run Python code directly from the OpenSIPS script, without executing the python interpreter. The module provides the means to load a python module and run its functions. Each function has to receive the SIP message as parameter, and optionally some extra arguments passed from the script. In order to run Python functions, one has to load the module that contains them, by specifying the script name using the script_name parameter. The module has to contain the following components: * A class that contains all the methods that can be invoked from the script. * A method within the class that is called when a SIP child is created. The method should receive an integer parameter, which represents the rank of the child, and must return 0 or positive in case the function was executed successfully, or negative otherwise. The name of this method is specified by the child_init_method parameter. * A global function that initializes the Python module and returns an object from the class whose functions will be invoked by the script. The name of the global function is indicated by the mod_init_method parameter. A minimal example of a Python script that satisfies these requirements is: def mod_init(): return SIPMsg() class SIPMsg: def child_init(self, rank): return 0 A function from the object returned above can be executed from the script using the python_exec() script function. The python method has to receive the following parameters: * The SIP message, that has the structure detailed below * Optionally, a string passed from the script The SIP message received as parameter by the function has the following fields and methods: * Type - the type of the message, either SIP_REQUEST or SIP_REPLY * Method - the method of the message * Status - the status of the message, available only for replies * RURI - the R-URI of the message, available only for requests * src_address - the (IP, port) tuple representing source address of the message * dst_address - the (IP, port) tuple representing the destination address (OpenSIPS address) of the message * copy() - copies the current SIP message in a new object * rewrite_ruri() - changes the R-URI of the message; available only for requests * set_dst_uri() - sets the destination URI of the message; available only for requests * getHeader() - returns the header of a message the destination address (OpenSIPS address) of the message * call_function() - calls a function from the script 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * None. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * python-dev - provides the Python bindings. 1.3. Exported Parameters 1.3.1. script_name (string) The script that contains the Python module. Default value is “/usr/local/etc/opensips/handler.pyâ€. Example 1.1. Set script_name parameter ... modparam("python", "script_name", "/usr/local/bin/opensips_handler.py") ... 1.3.2. mod_init_function (string) The method used to initialize the Python module and return the object. Default value is “mod_initâ€. Example 1.2. Set mod_init_function parameter ... modparam("python", "mod_init_function", "module_initializer") ... 1.3.3. child_init_method (string) The method called for each child process. Default value is “child_initâ€. Example 1.3. Set child_init_method parameter ... modparam("python", "child_init_method", "child_initializer") ... 1.4. Exported Functions 1.4.1. python_exec(method_name [, extra_args]) Thhis function is used to execute a method from the Python module loaded. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Meaning of the parameters is as follows: * method_name - name of the method called * extra_args(optional) - extra arguments that can be passed from the script to the python function. opensips-2.2.2/modules/python/doc/000077500000000000000000000000001300170765700171215ustar00rootroot00000000000000opensips-2.2.2/modules/python/doc/python.xml000066400000000000000000000017711300170765700211720ustar00rootroot00000000000000 %docentities; ]> Python Module &osipsname; Maxim Sobolev Sippy Software, Inc.
sobomax@sippysoft.com
Razvan Crainea
razvan@opensips.org
2009 Sippy Software, Inc.
&admin;
opensips-2.2.2/modules/python/doc/python_admin.xml000066400000000000000000000154711300170765700223440ustar00rootroot00000000000000 &adminguide;
Overview This module can be used to efficiently run Python code directly from the &osips; script, without executing the python interpreter. The module provides the means to load a python module and run its functions. Each function has to receive the SIP message as parameter, and optionally some extra arguments passed from the script. In order to run Python functions, one has to load the module that contains them, by specifying the script name using the script_name parameter. The module has to contain the following components: A class that contains all the methods that can be invoked from the script. A method within the class that is called when a SIP child is created. The method should receive an integer parameter, which represents the rank of the child, and must return 0 or positive in case the function was executed successfully, or negative otherwise. The name of this method is specified by the child_init_method parameter. A global function that initializes the Python module and returns an object from the class whose functions will be invoked by the script. The name of the global function is indicated by the mod_init_method parameter. A minimal example of a Python script that satisfies these requirements is: def mod_init(): return SIPMsg() class SIPMsg: def child_init(self, rank): return 0 A function from the object returned above can be executed from the script using the python_exec() script function. The python method has to receive the following parameters: The SIP message, that has the structure detailed below Optionally, a string passed from the script The SIP message received as parameter by the function has the following fields and methods: Type - the type of the message, either SIP_REQUEST or SIP_REPLY Method - the method of the message Status - the status of the message, available only for replies RURI - the R-URI of the message, available only for requests src_address - the (IP, port) tuple representing source address of the message dst_address - the (IP, port) tuple representing the destination address (&osips; address) of the message copy() - copies the current SIP message in a new object rewrite_ruri() - changes the R-URI of the message; available only for requests set_dst_uri() - sets the destination URI of the message; available only for requests getHeader() - returns the header of a message the destination address (&osips; address) of the message call_function() - calls a function from the script
Dependencies
&osips; Modules The following modules must be loaded before this module: None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: python-dev - provides the Python bindings.
Exported Parameters
<varname>script_name</varname> (string) The script that contains the Python module. Default value is /usr/local/etc/opensips/handler.py. Set <varname>script_name</varname> parameter ... modparam("python", "script_name", "/usr/local/bin/opensips_handler.py") ...
<varname>mod_init_function</varname> (string) The method used to initialize the Python module and return the object. Default value is mod_init. Set <varname>mod_init_function</varname> parameter ... modparam("python", "mod_init_function", "module_initializer") ...
<varname>child_init_method</varname> (string) The method called for each child process. Default value is child_init. Set <varname>child_init_method</varname> parameter ... modparam("python", "child_init_method", "child_initializer") ...
Exported Functions
<function moreinfo="none">python_exec(method_name [, extra_args])</function> Thhis function is used to execute a method from the Python module loaded. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Meaning of the parameters is as follows: method_name - name of the method called extra_args(optional) - extra arguments that can be passed from the script to the python function.
opensips-2.2.2/modules/python/handler.py000066400000000000000000000015451300170765700203500ustar00rootroot00000000000000import sys from OpenSIPS import LM_ERR class test: def __init__(self): LM_ERR('test.__init__\n') def child_init(self, y): LM_ERR('test.child_init(%d)\n' % y) return 0 def handler(self, msg, args): LM_ERR('test.handler(%s, %s)\n' % (msg.Type, str(arg))) if msg.Type == 'SIP_REQUEST': if msg.Method == 'INVITE': msg.rewrite_ruri('sip:0022@192.168.2.24:5073') LM_ERR('SIP request, method = %s, RURI = %s, From = %s\n' % (msg.Method, msg.RURI, msg.getHeader('from'))) LM_ERR('received from %s:%d\n' % msg.src_address) else: LM_ERR('SIP reply, status = %s\n' % msg.Status) LM_ERR('received from %s:%d\n' % msg.src_address) msg.call_function('append_hf', 'This-is: test\r\n') return 1 def mod_init(): return test() opensips-2.2.2/modules/python/python_exec.c000066400000000000000000000066611300170765700210560ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "../../mem/mem.h" #include "../../data_lump.h" #include "../../parser/parse_param.h" #include "../../parser/msg_parser.h" #include "../../dprint.h" #include "../../action.h" #include "../../config.h" #include "../../parser/parse_uri.h" #include "python_exec.h" #include "python_mod.h" #include "python_msgobj.h" #include "python_support.h" int python_exec1(struct sip_msg* _msg, char* method_name, char *foobar) { return python_exec2(_msg, method_name, NULL); } int python_exec2(struct sip_msg *_msg, char *method_name, char *mystr) { PyObject *pFunc, *pArgs, *pValue, *pResult; PyObject *msg; int rval; PyEval_AcquireLock(); PyThreadState_Swap(myThreadState); pFunc = PyObject_GetAttrString(handler_obj, method_name); if (pFunc == NULL || !PyCallable_Check(pFunc)) { LM_ERR("%s not found or is not callable\n", method_name); Py_XDECREF(pFunc); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } msg = newmsgobject(_msg); if (msg == NULL) { LM_ERR("can't create MSGtype instance\n"); Py_DECREF(pFunc); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } pArgs = PyTuple_New(mystr == NULL ? 1 : 2); if (pArgs == NULL) { LM_ERR("PyTuple_New() has failed\n"); msg_invalidate(msg); Py_DECREF(msg); Py_DECREF(pFunc); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } PyTuple_SetItem(pArgs, 0, msg); /* Tuple steals msg */ if (mystr != NULL) { pValue = PyString_FromString(mystr); if (pValue == NULL) { LM_ERR("PyString_FromString(%s) has failed\n", mystr); msg_invalidate(msg); Py_DECREF(pArgs); Py_DECREF(pFunc); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } PyTuple_SetItem(pArgs, 1, pValue); /* Tuple steals pValue */ } pResult = PyObject_CallObject(pFunc, pArgs); msg_invalidate(msg); Py_DECREF(pArgs); Py_DECREF(pFunc); if (PyErr_Occurred()) { Py_XDECREF(pResult); python_handle_exception("python_exec2"); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } if (pResult == NULL) { LM_ERR("PyObject_CallObject() returned NULL\n"); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } rval = PyInt_AsLong(pResult); Py_DECREF(pResult); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return rval; } opensips-2.2.2/modules/python/python_exec.h000066400000000000000000000020241300170765700210500ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PYTHON_EXEC_H #define _PYTHON_EXEC_H #include "../../parser/msg_parser.h" int python_exec1(struct sip_msg *, char *, char *); int python_exec2(struct sip_msg *, char *, char *); #endif opensips-2.2.2/modules/python/python_iface.c000066400000000000000000000026031300170765700211710ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "../../action.h" #include "../../dprint.h" #include "../../route_struct.h" #include "python_exec.h" /* Return the number of arguments of the application command line */ static PyObject* opensips_LM_ERR(PyObject *self, PyObject *args) { char *msg; if(!PyArg_ParseTuple(args, "s:LM_ERR", &msg)) return NULL; LM_ERR("%s", msg); Py_INCREF(Py_None); return Py_None; } PyMethodDef OpenSIPSMethods[] = { {"LM_ERR", opensips_LM_ERR, METH_VARARGS, "Pring error message."}, {NULL, NULL, 0, NULL} }; opensips-2.2.2/modules/python/python_iface.h000066400000000000000000000017031300170765700211760ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PYTHON_IFACE_H #define _PYTHON_IFACE_H #include extern PyMethodDef OpenSIPSMethods[]; #endif opensips-2.2.2/modules/python/python_mod.c000066400000000000000000000207241300170765700207050ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "../../str.h" #include "../../sr_module.h" #include "python_exec.h" #include "python_iface.h" #include "python_msgobj.h" #include "python_support.h" #include static int mod_init(void); static int child_init(int rank); static void mod_destroy(void); static str script_name = {.s = "/usr/local/etc/opensips/handler.py", .len = 0}; static str mod_init_fname = { .s = "mod_init", .len = 0}; static str child_init_mname = { .s = "child_init", .len = 0}; PyObject *handler_obj; PyObject *format_exc_obj; PyThreadState *myThreadState; /** module parameters */ static param_export_t params[]={ {"script_name", STR_PARAM, &script_name }, {"mod_init_function", STR_PARAM, &mod_init_fname }, {"child_init_method", STR_PARAM, &child_init_mname }, {0,0,0} }; /* * Exported functions */ static cmd_export_t cmds[] = { { "python_exec", (cmd_function)python_exec1, 1, NULL, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, { "python_exec", (cmd_function)python_exec2, 2, NULL, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, { 0, 0, 0, 0, 0, 0 } }; /** module exports */ struct module_exports exports = { "python", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, RTLD_NOW | RTLD_GLOBAL, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) NULL, /* response handling function */ (destroy_function) mod_destroy, /* destroy function */ child_init /* per-child init function */ }; static int mod_init(void) { char *dname, *bname; int i; PyObject *sys_path, *pDir, *pModule, *pFunc, *pArgs; PyThreadState *mainThreadState; if (script_name.len == 0) { script_name.len = strlen(script_name.s); } if (mod_init_fname.len == 0) { mod_init_fname.len = strlen(mod_init_fname.s); } if (child_init_mname.len == 0) { child_init_mname.len = strlen(child_init_mname.s); } bname = basename(script_name.s); i = strlen(bname); if (bname[i - 1] == 'c' || bname[i - 1] == 'o') i -= 1; if (bname[i - 3] == '.' && bname[i - 2] == 'p' && bname[i - 1] == 'y') { bname[i - 3] = '\0'; } else { LM_ERR("%s: script_name doesn't look like a python script\n", script_name.s); return -1; } dname = dirname(script_name.s); if (strlen(dname) == 0) dname = "."; Py_Initialize(); PyEval_InitThreads(); mainThreadState = PyThreadState_Get(); Py_InitModule("OpenSIPS", OpenSIPSMethods); if (python_msgobj_init() != 0) { LM_ERR("python_msgobj_init() has failed\n"); PyEval_ReleaseLock(); return -1; } sys_path = PySys_GetObject("path"); /* PySys_GetObject doesn't pass reference! No need to DEREF */ if (sys_path == NULL) { PyErr_Print(); LM_ERR("cannot import sys.path\n"); PyEval_ReleaseLock(); return -1; } pDir = PyString_FromString(dname); if (pDir == NULL) { PyErr_Print(); LM_ERR("PyString_FromString() has filed\n"); PyEval_ReleaseLock(); return -1; } PyList_Insert(sys_path, 0, pDir); Py_DECREF(pDir); pModule = PyImport_ImportModule(bname); if (pModule == NULL) { PyErr_Print(); LM_ERR("cannot import %s\n", bname); PyEval_ReleaseLock(); return -1; } pFunc = PyObject_GetAttrString(pModule, mod_init_fname.s); Py_DECREF(pModule); /* pFunc is a new reference */ if (pFunc == NULL || !PyCallable_Check(pFunc)) { PyErr_Print(); LM_ERR("cannot locate %s function in %s module\n", mod_init_fname.s, script_name.s); Py_XDECREF(pFunc); PyEval_ReleaseLock(); return -1; } pModule = PyImport_ImportModule("traceback"); if (pModule == NULL) { PyErr_Print(); LM_ERR("cannot import traceback module\n"); Py_DECREF(pFunc); PyEval_ReleaseLock(); return -1; } format_exc_obj = PyObject_GetAttrString(pModule, "format_exception"); Py_DECREF(pModule); if (format_exc_obj == NULL || !PyCallable_Check(format_exc_obj)) { PyErr_Print(); LM_ERR("cannot locate format_exception function in" \ " traceback module\n"); Py_XDECREF(format_exc_obj); Py_DECREF(pFunc); PyEval_ReleaseLock(); return -1; } pArgs = PyTuple_New(0); if (pArgs == NULL) { PyErr_Print(); LM_ERR("PyTuple_New() has failed\n"); Py_DECREF(pFunc); Py_DECREF(format_exc_obj); PyEval_ReleaseLock(); return -1; } handler_obj = PyObject_CallObject(pFunc, pArgs); Py_DECREF(pFunc); Py_DECREF(pArgs); if (PyErr_Occurred()) { PyErr_Print(); python_handle_exception("mod_init"); Py_XDECREF(handler_obj); Py_DECREF(format_exc_obj); PyEval_ReleaseLock(); return -1; } if (handler_obj == NULL) { PyErr_Print(); LM_ERR("%s function has not returned object\n", mod_init_fname.s); Py_DECREF(format_exc_obj); PyEval_ReleaseLock(); return -1; } myThreadState = PyThreadState_New(mainThreadState->interp); PyEval_ReleaseLock(); return 0; } static int child_init(int rank) { PyObject *pFunc, *pArgs, *pValue, *pResult; int rval; PyEval_AcquireLock(); PyThreadState_Swap(myThreadState); pFunc = PyObject_GetAttrString(handler_obj, child_init_mname.s); if (pFunc == NULL || !PyCallable_Check(pFunc)) { PyErr_Print(); LM_ERR("cannot locate %s function\n", child_init_mname.s); if (pFunc != NULL) { Py_DECREF(pFunc); } PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } pArgs = PyTuple_New(1); if (pArgs == NULL) { PyErr_Print(); LM_ERR("PyTuple_New() has failed\n"); Py_DECREF(pFunc); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } pValue = PyInt_FromLong(rank); if (pValue == NULL) { PyErr_Print(); LM_ERR("PyInt_FromLong() has failed\n"); Py_DECREF(pArgs); Py_DECREF(pFunc); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } PyTuple_SetItem(pArgs, 0, pValue); /* pValue has been stolen */ pResult = PyObject_CallObject(pFunc, pArgs); Py_DECREF(pFunc); Py_DECREF(pArgs); if (PyErr_Occurred()) { python_handle_exception("child_init"); Py_XDECREF(pResult); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } if (pResult == NULL) { PyErr_Print(); LM_ERR("PyObject_CallObject() returned NULL but no exception!\n"); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return -1; } rval = PyInt_AsLong(pResult); Py_DECREF(pResult); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); return rval; } static void mod_destroy(void) { return; } opensips-2.2.2/modules/python/python_mod.h000066400000000000000000000017751300170765700207170ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PYTHON_MOD_H #define _PYTHON_MOD_H #include extern PyObject *handler_obj; extern PyObject *format_exc_obj; extern PyThreadState *myThreadState; #endif opensips-2.2.2/modules/python/python_msgobj.c000066400000000000000000000313261300170765700214070ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "structmember.h" #include "../../action.h" #include "../../mem/mem.h" #include "../../sr_module.h" #include "../../parser/msg_parser.h" #ifndef Py_TYPE #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #endif typedef struct { PyObject_HEAD struct sip_msg *msg; } msgobject; static PyTypeObject MSGtype; #define is_msgobject(v) ((v)->ob_type == &MSGtype) void set_Py_Type(PyObject *obj, struct _typeobject *type) { obj->ob_type = type; } msgobject * newmsgobject(struct sip_msg *msg) { msgobject *msgp; msgp = PyObject_New(msgobject, &MSGtype); if (msgp == NULL) return NULL; msgp->msg = msg; return msgp; } void msg_invalidate(msgobject *self) { self->msg = NULL; } static void msg_dealloc(msgobject *msgp) { PyObject_Del(msgp); } static PyObject * msg_copy(msgobject *self) { msgobject *msgp; if ((msgp = newmsgobject(self->msg)) == NULL) return NULL; return (PyObject *)msgp; } static PyObject * msg_rewrite_ruri(msgobject *self, PyObject *args) { char *ruri; struct action act; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } if ((self->msg->first_line).type != SIP_REQUEST) { PyErr_SetString(PyExc_RuntimeError, "Not a request message - " "rewrite is not possible.\n"); Py_INCREF(Py_None); return Py_None; } if(!PyArg_ParseTuple(args, "s:rewrite_ruri", &ruri)) return NULL; memset(&act, '\0', sizeof(act)); act.type = SET_URI_T; act.elem[0].type = STR_ST; act.elem[0].u.s.s = ruri; act.elem[0].u.s.len = strlen(ruri); if (do_action(&act, self->msg) < 0) { LM_ERR("Error in do_action\n"); PyErr_SetString(PyExc_RuntimeError, "Error in do_action\n"); } Py_INCREF(Py_None); return Py_None; } static PyObject * msg_set_dst_uri(msgobject *self, PyObject *args) { str ruri; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } if ((self->msg->first_line).type != SIP_REQUEST) { PyErr_SetString(PyExc_RuntimeError, "Not a request message - " "set destination is not possible.\n"); Py_INCREF(Py_None); return Py_None; } if(!PyArg_ParseTuple(args, "s:set_dst_uri", &ruri.s)) return NULL; ruri.len = strlen(ruri.s); if (set_dst_uri(self->msg, &ruri) < 0) { LM_ERR("Error in set_dst_uri\n"); PyErr_SetString(PyExc_RuntimeError, "Error in set_dst_uri\n"); } Py_INCREF(Py_None); return Py_None; } static PyObject * msg_getHeader(msgobject *self, PyObject *args) { struct hdr_field *hf; str hname, *hbody; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } if(!PyArg_ParseTuple(args, "s:getHeader", &hname.s)) return NULL; hname.len = strlen(hname.s); parse_headers(self->msg, ~0, 0); hbody = NULL; for (hf = self->msg->headers; hf != NULL; hf = hf->next) { if (hname.len == hf->name.len && strncasecmp(hname.s, hf->name.s, hname.len) == 0) { hbody = &(hf->body); break; } } if (hbody == NULL) { Py_INCREF(Py_None); return Py_None; } return PyString_FromStringAndSize(hbody->s, hbody->len); } static PyObject * msg_call_function(msgobject *self, PyObject *args) { int i, rval; char *fname, *arg1, *arg2; cmd_export_t *fexport; struct action *act; action_elem_t elems[MAX_ACTION_ELEMS]; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } i = PySequence_Size(args); if (i < 1 || i > 3) { PyErr_SetString(PyExc_RuntimeError, "call_function() should " \ "have from 1 to 3 arguments"); Py_INCREF(Py_None); return Py_None; } if(!PyArg_ParseTuple(args, "s|ss:call_function", &fname, &arg1, &arg2)) return NULL; fexport = find_cmd_export_t(fname, i - 1, 0); if (fexport == NULL) { PyErr_SetString(PyExc_RuntimeError, "no such function"); Py_INCREF(Py_None); return Py_None; } elems[0].type = CMD_ST; elems[0].u.data = fexport; elems[1].type = STRING_ST; elems[1].u.data = arg1; elems[2].type = STRING_ST; elems[2].u.data = arg2; act = mk_action(MODULE_T, 3, elems, 0, "python"); if (act == NULL) { PyErr_SetString(PyExc_RuntimeError, "action structure could not be created"); Py_INCREF(Py_None); return Py_None; } if (fexport->fixup != NULL) { if (i >= 3) { rval = fexport->fixup(&(act->elem[2].u.data), 2); if (rval < 0) { PyErr_SetString(PyExc_RuntimeError, "Error in fixup (2)"); Py_INCREF(Py_None); return Py_None; } act->elem[2].type = MODFIXUP_ST; } if (i >= 2) { rval = fexport->fixup(&(act->elem[1].u.data), 1); if (rval < 0) { PyErr_SetString(PyExc_RuntimeError, "Error in fixup (1)"); Py_INCREF(Py_None); return Py_None; } act->elem[1].type = MODFIXUP_ST; } if (i == 1) { rval = fexport->fixup(0, 0); if (rval < 0) { PyErr_SetString(PyExc_RuntimeError, "Error in fixup (0)"); Py_INCREF(Py_None); return Py_None; } } } rval = do_action(act, self->msg); if ((act->elem[2].type == MODFIXUP_ST) && (act->elem[2].u.data)) { pkg_free(act->elem[2].u.data); } if ((act->elem[1].type == MODFIXUP_ST) && (act->elem[1].u.data)) { pkg_free(act->elem[1].u.data); } pkg_free(act); return PyInt_FromLong(rval); } PyDoc_STRVAR(copy_doc, "copy() -> msg object\n\ \n\ Return a copy (``clone'') of the msg object."); static PyMethodDef msg_methods[] = { {"copy", (PyCFunction)msg_copy, METH_NOARGS, copy_doc}, {"rewrite_ruri", (PyCFunction)msg_rewrite_ruri, METH_VARARGS, "Rewrite Request-URI."}, {"set_dst_uri", (PyCFunction)msg_set_dst_uri, METH_VARARGS, "Set destination URI."}, {"getHeader", (PyCFunction)msg_getHeader, METH_VARARGS, "Get SIP header field by name."}, {"call_function", (PyCFunction)msg_call_function, METH_VARARGS, "Invoke function exported by the other module."}, {NULL, NULL, 0, NULL} /* sentinel */ }; static PyObject * msg_getType(msgobject *self, PyObject *unused) { const char *rval; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } switch ((self->msg->first_line).type) { case SIP_REQUEST: rval = "SIP_REQUEST"; break; case SIP_REPLY: rval = "SIP_REPLY"; break; default: /* Shouldn't happen */ abort(); } return PyString_FromString(rval); } static PyObject * msg_getMethod(msgobject *self, PyObject *unused) { str *rval; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } if ((self->msg->first_line).type != SIP_REQUEST) { PyErr_SetString(PyExc_RuntimeError, "Not a request message - " "no method available.\n"); Py_INCREF(Py_None); return Py_None; } rval = &((self->msg->first_line).u.request.method); return PyString_FromStringAndSize(rval->s, rval->len); } static PyObject * msg_getStatus(msgobject *self, PyObject *unused) { str *rval; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } if ((self->msg->first_line).type != SIP_REPLY) { PyErr_SetString(PyExc_RuntimeError, "Not a non-reply message - " "no status available.\n"); Py_INCREF(Py_None); return Py_None; } rval = &((self->msg->first_line).u.reply.status); return PyString_FromStringAndSize(rval->s, rval->len); } static PyObject * msg_getRURI(msgobject *self, PyObject *unused) { str *rval; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } if ((self->msg->first_line).type != SIP_REQUEST) { PyErr_SetString(PyExc_RuntimeError, "Not a request message - " "RURI is not available.\n"); Py_INCREF(Py_None); return Py_None; } rval = &((self->msg->first_line).u.request.uri); return PyString_FromStringAndSize(rval->s, rval->len); } static PyObject * msg_get_src_address(msgobject *self, PyObject *unused) { PyObject *src_ip, *src_port, *pyRval; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } src_ip = PyString_FromString(ip_addr2a(&self->msg->rcv.src_ip)); if (src_ip == NULL) { Py_INCREF(Py_None); return Py_None; } src_port = PyInt_FromLong(self->msg->rcv.src_port); if (src_port == NULL) { Py_DECREF(src_ip); Py_INCREF(Py_None); return Py_None; } pyRval = PyTuple_Pack(2, src_ip, src_port); Py_DECREF(src_ip); Py_DECREF(src_port); if (pyRval == NULL) { Py_INCREF(Py_None); return Py_None; } return pyRval; } static PyObject * msg_get_dst_address(msgobject *self, PyObject *unused) { PyObject *dst_ip, *dst_port, *pyRval; if (self->msg == NULL) { PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL"); Py_INCREF(Py_None); return Py_None; } dst_ip = PyString_FromString(ip_addr2a(&self->msg->rcv.dst_ip)); if (dst_ip == NULL) { Py_INCREF(Py_None); return Py_None; } dst_port = PyInt_FromLong(self->msg->rcv.dst_port); if (dst_port == NULL) { Py_DECREF(dst_ip); Py_INCREF(Py_None); return Py_None; } pyRval = PyTuple_Pack(2, dst_ip, dst_port); Py_DECREF(dst_ip); Py_DECREF(dst_port); if (pyRval == NULL) { Py_INCREF(Py_None); return Py_None; } return pyRval; } static PyGetSetDef msg_getseters[] = { {"Type", (getter)msg_getType, NULL, NULL, "Get message type - \"SIP_REQUEST\" or \"SIP_REPLY\"."}, {"Method", (getter)msg_getMethod, NULL, NULL, "Get SIP method name."}, {"Status", (getter)msg_getStatus, NULL, NULL, "Get SIP status code string."}, {"RURI", (getter)msg_getRURI, NULL, NULL, "Get SIP Request-URI."}, {"src_address", (getter)msg_get_src_address, NULL, NULL, "Get (IP, port) tuple representing source address of the message."}, {"dst_address", (getter)msg_get_dst_address, NULL, NULL, "Get (IP, port) tuple representing destination address of the message."}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; int python_msgobj_init(void) { /* HEAD initialization */ #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6 PyVarObject obj = { PyVarObject_HEAD_INIT(NULL, 0) }; memcpy(((PyVarObject *)&MSGtype), &obj, sizeof obj); #else PyObject obj = { PyObject_HEAD_INIT(NULL) }; memcpy(((PyObject *)&MSGtype), &obj, sizeof obj); #endif MSGtype.tp_name = "OpenSIPS.msg"; MSGtype.tp_basicsize = sizeof(msgobject); MSGtype.tp_dealloc = (destructor)msg_dealloc; MSGtype.tp_flags = Py_TPFLAGS_DEFAULT; MSGtype.tp_methods = msg_methods; MSGtype.tp_getset = msg_getseters; set_Py_Type((PyObject *)&MSGtype, &PyType_Type); if (PyType_Ready(&MSGtype) < 0) return -1; return 0; } opensips-2.2.2/modules/python/python_msgobj.h000066400000000000000000000020561300170765700214120ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PYTHON_MSGOBJ_H #define _PYTHON_MSGOBJ_H #include "../../parser/msg_parser.h" #include PyObject *newmsgobject(struct sip_msg *); int python_msgobj_init(void); void msg_invalidate(PyObject *); #endif opensips-2.2.2/modules/python/python_support.c000066400000000000000000000050101300170765700216310ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "../../dprint.h" #include "python_mod.h" #include void python_handle_exception(const char *fname) { PyObject *pResult; const char *msg; PyObject *exception, *v, *tb, *args; PyObject *line; int i; LM_ERR("%s: Unhandled exception in the Python code:\n", fname); PyErr_Fetch(&exception, &v, &tb); PyErr_Clear(); if (exception == NULL) { LM_ERR("can't get traceback, PyErr_Fetch() has failed\n"); return; } PyErr_NormalizeException(&exception, &v, &tb); if (exception == NULL) { LM_ERR("can't get traceback, PyErr_NormalizeException() has failed\n"); return; } args = PyTuple_Pack(3, exception, v, tb ? tb : Py_None); Py_XDECREF(exception); Py_XDECREF(v); Py_XDECREF(tb); if (args == NULL) { LM_ERR("can't get traceback, PyTuple_Pack() has failed\n"); return; } pResult = PyObject_CallObject(format_exc_obj, args); Py_DECREF(args); if (pResult == NULL) { LM_ERR("can't get traceback, traceback.format_exception() has failed\n"); return; } for (i = 0; i < PySequence_Size(pResult); i++) { line = PySequence_GetItem(pResult, i); if (line == NULL) { LM_ERR("can't get traceback, PySequence_GetItem() has failed\n"); Py_DECREF(pResult); return; } msg = PyString_AsString(line); if (msg == NULL) { LM_ERR("can't get traceback, PyString_AsString() has failed\n"); Py_DECREF(line); Py_DECREF(pResult); return; } LM_ERR("\t%s", msg); Py_DECREF(line); } Py_DECREF(pResult); } opensips-2.2.2/modules/python/python_support.h000066400000000000000000000016701300170765700216460ustar00rootroot00000000000000/* * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PYTHON_SUPPORT_H #define _PYTHON_SUPPORT_H void python_handle_exception(const char *); #endif opensips-2.2.2/modules/qos/000077500000000000000000000000001300170765700156355ustar00rootroot00000000000000opensips-2.2.2/modules/qos/Makefile000066400000000000000000000020371300170765700172770ustar00rootroot00000000000000# # $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile # # Copyright (C) 2006 SOMA Networks, Inc. # Written by: Ovidiu Sas # # This file is part of OpenSIPS, a free SIP server. # # OpenSIPS is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # OpenSIPS is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # History: # -------- # 2007-07-16 initial version (osas) # include ../../Makefile.defs auto_gen= NAME=qos.so include ../../Makefile.modules opensips-2.2.2/modules/qos/README000066400000000000000000000123461300170765700165230ustar00rootroot00000000000000QOS Module Ovidiu Sas SOMA Networks, Inc. Edited by Ovidiu Sas Copyright © 2008 SOMA Networks, Inc. Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How it works 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. qos_flag (string/integer) 1.5. Exported Functions 1.6. Exported Statistics 1.7. Exported MI Functions 1.8. Exported pseudo-variables 1.9. Installation and Running 2. Developer Guide 2.1. Available Functions 2.1.1. register_qoscb (qos, type, cb, param) List of Examples 1.1. Set qos_flag parameter Chapter 1. Admin Guide 1.1. Overview The qos module provides a way to keep track of per dialog SDP session(s). 1.2. How it works The qos module uses the dialog module to be notified of any new or updated dialogs. It will then look for and extract the SDP session (if present) from SIP requests and replies and keep track of it for the entire life of a dialog. All of this happens with a properly configured dialog and qos module and setting the dialog flag and the qos flag at the time any INVITE sip message is seen. There is no config script function call required to set the SDP session tracking mechanism. See the dialog module users guide for more information. A dialog can have one or more SDP sessions active in one of the following states: * pending - only one end point of the SDP session is known. * negotiated - both end points of the SDP session are known. An SDP session can be established in one of the following scenarios: * INVITE/200ok - typical "INVITE" and "200 OK" SDP exchange. * 200ok/ACK - "200 OK" and "ACK" SDP exchange (for calls starting with an empty INVITE). * 183/PRACK - early media via "183 Session Progress" and "PRACK" (see rfc3959 for more information) - not implemented yet. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * dialog - dialog module and its decencies (tm). 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.4. Exported Parameters 1.4.1. qos_flag (string/integer) Keeping with OpenSIPS, the module will not do anything to any message unless instructed to do so via the config script. You must set the qos_flag value in the setflag() call of the INVITE you want the qos module to process. But before you can do that, you need to tell the qos module which flag value you are assigning to qos. In most cases when ever you create a new dialog via create_dialog() function,you will want to set the qos flag. If create_dialog() is not called and the qos flag is set, it will not have any effect. This parameter must be set of the module will not load. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is “Not set!â€. Example 1.1. Set qos_flag parameter ... modparam("qos", "qos_flag", "QOS_FLAG") ... route { ... if (method=="INVITE") { setflag(QOS_FLAG); # Set the qos flag create_dialog(); # create the dialog } ... } 1.5. Exported Functions There are no exported functions that could be used in scripts. 1.6. Exported Statistics There are no exported statistics for the qos module. 1.7. Exported MI Functions There are no exported MI functions for the qos module. Check the dialog MI functions for a way to inspect the internals of a dialog. 1.8. Exported pseudo-variables There are no exported pseudo-variables for the qos module. 1.9. Installation and Running Just load the module and remember to set the flag. Chapter 2. Developer Guide 2.1. Available Functions 2.1.1. register_qoscb (qos, type, cb, param) Register a new callback to the qos. Meaning of the parameters is as follows: * struct qos_ctx_st* qos - qos to register callback to. If maybe NULL only for QOSCB_CREATED callback type, which is not a per qos type. * int type - types of callbacks; more types may be register for the same callback function; only QOSCB_CREATED must be register alone. Possible types: + QOSCB_CREATED - called when a new qos context is created - it's a global type (not associated to any qos). + QOSCB_ADD_SDP - called when a new SDP was added to the qos context - it's a per qos type. + QOSCB_UPDATE_SDP - called when an existing SDP is updated - it's a per qos type. + QOSCB_REMOVE_SDP - called when an existing SDP is removed - it's a per qos type. + QOSCB_TERMINATED - called when the qos is terminated. * qos_cb cb - callback function to be called. Prototype is: “void (qos_cb) (struct qos_ctx_st *qos, int type, struct qos_cb_params *params); †* void *param - parameter to be passed to the callback function. opensips-2.2.2/modules/qos/doc/000077500000000000000000000000001300170765700164025ustar00rootroot00000000000000opensips-2.2.2/modules/qos/doc/qos.xml000066400000000000000000000022431300170765700177270ustar00rootroot00000000000000 %docentities; ]> QOS Module &osipsname; Ovidiu Sas SOMA Networks, Inc.
ronw@somanetworks.com
Ovidiu Sas
osas@somanetworks.com
2008 SOMA Networks, Inc. $Revision: 5901 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/qos/doc/qos_admin.xml000066400000000000000000000106741300170765700211060ustar00rootroot00000000000000 &adminguide;
Overview The qos module provides a way to keep track of per dialog SDP session(s).
How it works The qos module uses the dialog module to be notified of any new or updated dialogs. It will then look for and extract the SDP session (if present) from SIP requests and replies and keep track of it for the entire life of a dialog. All of this happens with a properly configured dialog and qos module and setting the dialog flag and the qos flag at the time any INVITE sip message is seen. There is no config script function call required to set the SDP session tracking mechanism. See the dialog module users guide for more information. A dialog can have one or more SDP sessions active in one of the following states: pending - only one end point of the SDP session is known. negotiated - both end points of the SDP session are known. An SDP session can be established in one of the following scenarios: INVITE/200ok - typical "INVITE" and "200 OK" SDP exchange. 200ok/ACK - "200 OK" and "ACK" SDP exchange (for calls starting with an empty INVITE). 183/PRACK - early media via "183 Session Progress" and "PRACK" (see rfc3959 for more information) - not implemented yet.
Dependencies
&osips; Modules The following modules must be loaded before this module: dialog - dialog module and its decencies (tm).
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>qos_flag</varname> (string/integer) Keeping with &osips;, the module will not do anything to any message unless instructed to do so via the config script. You must set the qos_flag value in the setflag() call of the INVITE you want the qos module to process. But before you can do that, you need to tell the qos module which flag value you are assigning to qos. In most cases when ever you create a new dialog via create_dialog() function,you will want to set the qos flag. If create_dialog() is not called and the qos flag is set, it will not have any effect. This parameter must be set of the module will not load. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is Not set!. Set <varname>qos_flag</varname> parameter ... modparam("qos", "qos_flag", "QOS_FLAG") ... route { ... if (method=="INVITE") { setflag(QOS_FLAG); # Set the qos flag create_dialog(); # create the dialog } ... }
Exported Functions There are no exported functions that could be used in scripts.
Exported Statistics There are no exported statistics for the qos module.
Exported MI Functions There are no exported MI functions for the qos module. Check the dialog MI functions for a way to inspect the internals of a dialog.
Exported pseudo-variables There are no exported pseudo-variables for the qos module.
Installation and Running Just load the module and remember to set the flag.
opensips-2.2.2/modules/qos/doc/qos_devel.xml000066400000000000000000000040041300170765700211030ustar00rootroot00000000000000 &develguide;
Available Functions
<function moreinfo="none">register_qoscb (qos, type, cb, param)</function> Register a new callback to the qos. Meaning of the parameters is as follows: struct qos_ctx_st* qos - qos to register callback to. If maybe NULL only for QOSCB_CREATED callback type, which is not a per qos type. int type - types of callbacks; more types may be register for the same callback function; only QOSCB_CREATED must be register alone. Possible types: QOSCB_CREATED - called when a new qos context is created - it's a global type (not associated to any qos). QOSCB_ADD_SDP - called when a new SDP was added to the qos context - it's a per qos type. QOSCB_UPDATE_SDP - called when an existing SDP is updated - it's a per qos type. QOSCB_REMOVE_SDP - called when an existing SDP is removed - it's a per qos type. QOSCB_TERMINATED - called when the qos is terminated. qos_cb cb - callback function to be called. Prototype is: void (qos_cb) (struct qos_ctx_st *qos, int type, struct qos_cb_params *params); void *param - parameter to be passed to the callback function.
opensips-2.2.2/modules/qos/qos.c000066400000000000000000000072741300170765700166150ustar00rootroot00000000000000/* * QoS module - support for tracking dialogs and SDP * * Copyright (C) 2007 SOMA Networks, Inc. * Written by: Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2007-07-16 initial version (osas) */ #include #include #include #include "../../sr_module.h" #include "qos_load.h" #include "qos_handlers.h" /* also includes sr_module.h needed by handlers */ static int mod_init(void); static void mod_destroy(void); /* The qos message flag value */ static int qos_flag = -1; static char *qos_flag_str = 0; /* * Binding to the dialog module */ struct dlg_binds dialog_st; struct dlg_binds *dlg_binds = &dialog_st; static cmd_export_t cmds[]={ {"load_qos", (cmd_function)load_qos, 0, 0, 0, 0}, {0,0,0,0,0,0} }; /* * Script parameters */ static param_export_t mod_params[]={ { "qos_flag", STR_PARAM, &qos_flag_str}, { "qos_flag", INT_PARAM, &qos_flag}, { 0,0,0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "dialog", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "qos", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ NULL, /* exported async functions */ mod_params, /* param exports */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, /* Destroy function */ 0 /* per-child init function */ }; int load_qos( struct qos_binds *qosb) { qosb->register_qoscb = register_qoscb; return 1; } /** * The initialization function, called when the module is loaded by * the script. This function is called only once. * * Bind to the dialog module and setup the callbacks. Also initialize * the shared memory to store our interninal information in. */ static int mod_init(void) { fix_flag_name(qos_flag_str, qos_flag); qos_flag = get_flag_id_by_name(FLAG_TYPE_MSG, qos_flag_str); if (qos_flag == -1) { LM_ERR("no qos flag set!!\n"); return -1; } else if (qos_flag > MAX_FLAG) { LM_ERR("invalid qos flag %d!!\n", qos_flag); return -1; } /* init callbacks */ if (init_qos_callbacks()!=0) { LM_ERR("cannot init callbacks\n"); return -1; } /* Register the main (static) dialog call back. */ if (load_dlg_api(&dialog_st) != 0) { LM_ERR("Can't load dialog hooks\n"); return(-1); } /* Load dialog hooks */ dialog_st.register_dlgcb(NULL, DLGCB_CREATED, qos_dialog_created_CB, NULL, NULL); /* * We are GOOD-TO-GO. */ return 0; } static void mod_destroy(void) { destroy_qos_callbacks(); } opensips-2.2.2/modules/qos/qos_cb.c000066400000000000000000000072551300170765700172600ustar00rootroot00000000000000/* * Copyright (C) 2007 SOMA Networks, Inc. * Written by Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-07-14 initial version (osas) */ #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "qos_ctx_helpers.h" #include "qos_cb.h" static struct qos_head_cbl* create_cbs = 0; static struct qos_cb_params params = {NULL, NULL, 0, NULL}; int init_qos_callbacks(void) { create_cbs = (struct qos_head_cbl*)shm_malloc(sizeof(struct qos_head_cbl)); if (create_cbs==0) { LM_ERR("no more shm mem\n"); return -1; } create_cbs->first = 0; create_cbs->types = 0; return 0; } void destroy_qos_callbacks_list(struct qos_callback *cb) { struct qos_callback *cb_t; while(cb) { cb_t = cb; cb = cb->next; /* FIXME - what about parameters ? */ LM_DBG("freeing cp=%p\n", cb_t); shm_free(cb_t); } } void destroy_qos_callbacks(void) { if (create_cbs==0) return; destroy_qos_callbacks_list(create_cbs->first); shm_free(create_cbs); create_cbs = 0; } int register_qoscb(qos_ctx_t *qos, int types, qos_cb f, void *param ) { struct qos_callback *cb; LM_DBG("registering qos CB\n"); if ( types&QOSCB_CREATED ) { if (types!=QOSCB_CREATED) { LM_CRIT("QOSCB_CREATED type must be register alone!\n"); return -1; } } else { if (qos==0) { LM_CRIT("non-QOSCB_CREATED type " "must be register to a qos (qos missing)!\n"); return -1; } } cb = (struct qos_callback*)shm_malloc(sizeof(struct qos_callback)); if (cb==0) { LM_ERR("no more shm mem\n"); return -1; } LM_DBG("cb=%p\n", cb); cb->types = types; cb->callback = f; cb->param = param; if ( types&QOSCB_CREATED ) { cb->next = create_cbs->first; create_cbs->first = cb; create_cbs->types |= types; } else { cb->next = qos->cbs.first; qos->cbs.first = cb; qos->cbs.types |= types; LM_DBG("qos=%p qos->cbs=%p types=%d\n", qos, &(qos->cbs), qos->cbs.types); } return 0; } void run_create_cbs(struct qos_ctx_st *qos, struct sip_msg *msg) { struct qos_callback *cb; if (create_cbs->first==0) return; params.msg = msg; /* avoid garbage due static structure */ params.sdp = NULL; params.role = 0; params.param = NULL; for ( cb=create_cbs->first; cb; cb=cb->next) { LM_DBG("qos=%p\n",qos); params.param = &cb->param; cb->callback( qos, QOSCB_CREATED, ¶ms ); } return; } void run_qos_callbacks(int type, struct qos_ctx_st *qos, struct qos_sdp_st *sdp, unsigned int role, struct sip_msg *msg) { struct qos_callback *cb; if (qos == NULL) return; LM_DBG("qos=%p qos->cbs=%p, qos->cbs.types=%d\n", qos, &(qos->cbs), qos->cbs.types); if (qos->cbs.first==0 || ((qos->cbs.types)&type)==0 ) return; params.sdp = sdp; params.role = role; params.msg = msg; LM_DBG("searching in %p\n", qos->cbs.first); for ( cb=qos->cbs.first; cb; cb=cb->next) { if ( (cb->types)&type ) { LM_DBG("qos=%p, type=%d\n", qos, type); params.param = &cb->param; cb->callback( qos, type, ¶ms ); } } return; } opensips-2.2.2/modules/qos/qos_cb.h000066400000000000000000000050731300170765700172610ustar00rootroot00000000000000/* * Copyright (C) 2007 SOMA Networks, Inc. * Written by Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-07-11 initial version (osas) */ #ifndef _QOS_QOS_CB_H_ #define _QOS_QOS_CB_H_ #include "../../parser/msg_parser.h" struct qos_ctx_st; struct qos_cb_params { struct sip_msg *msg; /* sip msg related to the callback event */ struct qos_sdp_st *sdp; /* pointer to the sdp that is added/updated/removed */ unsigned int role; void **param; /* parameter passed at callback registration*/ }; /* callback function prototype */ typedef void (qos_cb) (struct qos_ctx_st *qos, int type, struct qos_cb_params *params); /* register callback function prototype */ typedef int (*register_qoscb_f)(struct qos_ctx_st *qos, int cb_types, qos_cb f, void *param); #define QOSCB_CREATED (1<<0) #define QOSCB_ADD_SDP (1<<1) #define QOSCB_UPDATE_SDP (1<<2) #define QOSCB_REMOVE_SDP (1<<3) #define QOSCB_TERMINATED (1<<4) /* * Callback logic .... * --INVITE(SDP)--> +---------------- <---183(SDP)---- +---------------- <---200(SDP)---- +---------------- -----ACK-------> -----BYE-------> +---------------- <-----200------- */ struct qos_callback { int types; qos_cb* callback; void *param; struct qos_callback* next; }; struct qos_head_cbl { struct qos_callback *first; int types; }; int init_qos_callbacks(); void destroy_qos_callbacks(); void destroy_qos_callbacks_list(struct qos_callback *cb); int register_qoscb(struct qos_ctx_st* qos, int types, qos_cb f, void *param); void run_create_cbs(struct qos_ctx_st *qos, struct sip_msg *msg); void run_qos_callbacks( int type, struct qos_ctx_st *qos, struct qos_sdp_st *sdp, unsigned int role, struct sip_msg *msg); #endif opensips-2.2.2/modules/qos/qos_ctx_helpers.c000066400000000000000000000466471300170765700212240ustar00rootroot00000000000000/* * Copyright (C) 2007 SOMA Networks, Inc. * Written by Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2007-07-16 initial version (osas) */ #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/sdp/sdp_cloner.h" #include "../dialog/dlg_hash.h" #include "qos_ctx_helpers.h" #define ERROR_MATCH -1 #define NO_INVITE_REQ_MATCH 0 #define NO_INVITE_RESP_MATCH 1 #define PENDING_MATCH 2 #define NEGOTIATED_MATCH 3 #define NO_ACK_REQ_MATCH 4 #define NO_UPDATE_REQ_MATCH 7 #define NO_UPDATE_RESP_MATCH 8 #define N_UNKNOWN 0 /* INVITE/200_ok */ #define N_INVITE_200OK 1 /* 200_ok/ACK */ #define N_200OK_ACK 2 /* early media (http://www.ietf.org/rfc/rfc3959.txt) */ /* 183_early_media/PRACK */ #define N_183_PRACK 3 qos_ctx_t *build_new_qos_ctx(void) { qos_ctx_t *ctx = NULL; ctx = (qos_ctx_t *)shm_malloc(sizeof(qos_ctx_t)); if (ctx!=NULL) { memset(ctx, 0, sizeof(qos_ctx_t)); } else { LM_ERR("No enough shared memory\n"); return NULL; } if (!lock_init(&ctx->lock)) { shm_free(ctx); return NULL; } return ctx; } void destroy_qos(qos_sdp_t *qos_sdp) { free_cloned_sdp_session(qos_sdp->sdp_session[0]); free_cloned_sdp_session(qos_sdp->sdp_session[1]); shm_free(qos_sdp); return; } void print_qos_sdp(qos_sdp_t *qos_sdp) { if (qos_sdp == NULL) { return; } LM_DBG("[%p] prev->%p next->%p method_dir=%d method_id=%d method='%.*s' cseq='%.*s' negotiation=%d sdp[0:QOS_CALLER]=%p sdp[1:QOS_CALLEE]=%p\n", qos_sdp, qos_sdp->prev, qos_sdp->next, qos_sdp->method_dir, qos_sdp->method_id, qos_sdp->method.len ,qos_sdp->method.s, qos_sdp->cseq.len, qos_sdp->cseq.s, qos_sdp->negotiation, qos_sdp->sdp_session[0], qos_sdp->sdp_session[1]); /* print_sdp_session(qos_sdp->sdp_session[0]); */ /* print_sdp_session(qos_sdp->sdp_session[1]); */ } /* * Find a matching sdp inside the local qos_ctx * for the given session received via message _m with the given direction, cseq and method * and return the type of the match and a pointer to the matched qos_sdp so we can properly insert the given session into the qos_ctx->qos_sdp. */ int find_qos_sdp(qos_ctx_t *qos_ctx, unsigned int dir, unsigned int other_role, str *cseq_number, int cseq_method_id, sdp_session_cell_t *session, struct sip_msg *_m, qos_sdp_t **_qos_sdp) { qos_sdp_t *qos_sdp; str *received_cnt_disp, *local_cnt_disp; LM_DBG("received session: %p and other_role: %s\n", session, (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE"); switch (_m->first_line.type) { case SIP_REQUEST: switch (cseq_method_id) { case METHOD_INVITE: return NO_INVITE_REQ_MATCH; break; case METHOD_ACK: /* searching into the pending_sdp list */ qos_sdp = qos_ctx->pending_sdp; LM_DBG("searching the negotiated_sdp: %p\n", qos_sdp); while (qos_sdp) { if (METHOD_INVITE == qos_sdp->method_id && dir != qos_sdp->method_dir && qos_sdp->negotiation == N_200OK_ACK && cseq_number->len == qos_sdp->cseq.len && 0 == strncmp(cseq_number->s, qos_sdp->cseq.s, cseq_number->len)) { LM_DBG("method_id, dir and cseq match with previous session %p->%p\n", qos_sdp, qos_sdp->sdp_session[other_role]); /* print_sdp_session(qos_sdp->sdp_session[other_role]); */ if (qos_sdp->sdp_session[other_role] != NULL) { local_cnt_disp = &(qos_sdp->sdp_session[other_role]->cnt_disp); received_cnt_disp = &(session->cnt_disp); if (local_cnt_disp->len == received_cnt_disp->len) { if (local_cnt_disp->len == 0) { LM_DBG("no cnt disp header ... => %p\n", qos_sdp); *_qos_sdp = qos_sdp; return PENDING_MATCH; } else if (0==strncmp(local_cnt_disp->s, received_cnt_disp->s, local_cnt_disp->len)) { LM_DBG("'%.*s' => %p\n", local_cnt_disp->len, local_cnt_disp->s, qos_sdp); *_qos_sdp = qos_sdp; return PENDING_MATCH; } } else if (received_cnt_disp->len == 0 && local_cnt_disp->len == 7 && 0==strncmp(local_cnt_disp->s, "session", 7)) { /* We may have an offer with cnt_disp='session' and an answer with cnt_disp='' */ *_qos_sdp = qos_sdp; return PENDING_MATCH; } } else { LM_ERR("skipping search for null sdp for %s\n", (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE"); } } qos_sdp = qos_sdp->next; } return NO_ACK_REQ_MATCH; break; case METHOD_UPDATE: return NO_UPDATE_REQ_MATCH; break; case METHOD_PRACK: LM_ERR("PRACK not implemented yet\n"); return ERROR_MATCH; break; default: LM_ERR("Unexpected method id %d\n", cseq_method_id); return ERROR_MATCH; } break; case SIP_REPLY: switch (cseq_method_id) { case METHOD_INVITE: /* searching into the pending_sdp list */ qos_sdp = qos_ctx->pending_sdp; while (qos_sdp) { //print_qos_sdp(qos_sdp); if (cseq_method_id == qos_sdp->method_id && dir != qos_sdp->method_dir && qos_sdp->negotiation == N_INVITE_200OK && cseq_number->len == qos_sdp->cseq.len && 0 == strncmp(cseq_number->s, qos_sdp->cseq.s, cseq_number->len)) { LM_DBG("method_id, dir and cseq match with previous session %p->%p\n", qos_sdp, qos_sdp->sdp_session[other_role]); /* print_sdp_session(qos_sdp->sdp_session[other_role]); */ if (qos_sdp->sdp_session[other_role] != NULL) { local_cnt_disp = &(qos_sdp->sdp_session[other_role]->cnt_disp); received_cnt_disp = &(session->cnt_disp); if (local_cnt_disp->len == received_cnt_disp->len) { if (local_cnt_disp->len == 0) { LM_DBG("no cnt disp header ... => %p\n", qos_sdp); *_qos_sdp = qos_sdp; return PENDING_MATCH; } else if (0==strncmp(local_cnt_disp->s, received_cnt_disp->s, local_cnt_disp->len)) { LM_DBG("'%.*s' => %p\n", local_cnt_disp->len, local_cnt_disp->s, qos_sdp); *_qos_sdp = qos_sdp; return PENDING_MATCH; } } else if (received_cnt_disp->len == 0 && local_cnt_disp->len == 7 && 0==strncmp(local_cnt_disp->s, "session", 7)) { /* We have an offer with cnt_disp='session' and an answer with cnt_disp='' */ *_qos_sdp = qos_sdp; return PENDING_MATCH; } } else { LM_ERR("skipping search for null sdp for %s\n", (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE"); } } qos_sdp = qos_sdp->next; } /* searching into the negotiated_sdp list */ qos_sdp = qos_ctx->negotiated_sdp; LM_DBG("searching the negotiated_sdp: %p\n", qos_sdp); while (qos_sdp) { //print_qos_sdp(qos_sdp); if (cseq_method_id == qos_sdp->method_id && dir != qos_sdp->method_dir && qos_sdp->negotiation == N_INVITE_200OK && cseq_number->len == qos_sdp->cseq.len && 0 == strncmp(cseq_number->s, qos_sdp->cseq.s, cseq_number->len)) { LM_DBG("method_id, dir and cseq match with previous session %p\n", qos_sdp->sdp_session[other_role]); if (qos_sdp->sdp_session[other_role] != NULL) { local_cnt_disp = &(qos_sdp->sdp_session[other_role]->cnt_disp); received_cnt_disp = &(session->cnt_disp); if (local_cnt_disp->len == received_cnt_disp->len) { if (local_cnt_disp->len == 0) { LM_DBG("no cnt disp header ... => %p\n", qos_sdp); *_qos_sdp = qos_sdp; return NEGOTIATED_MATCH; } else if (0==strncmp(local_cnt_disp->s, received_cnt_disp->s, local_cnt_disp->len)) { LM_DBG("'%.*s' => %p\n", local_cnt_disp->len, local_cnt_disp->s, qos_sdp); *_qos_sdp = qos_sdp; return NEGOTIATED_MATCH; } } else if (received_cnt_disp->len == 0 && local_cnt_disp->len == 7 && 0==strncasecmp(local_cnt_disp->s, "session", 7)) { /* We have an offer with cnt_disp='session' and an answer with cnt_disp='' */ *_qos_sdp = qos_sdp; return NEGOTIATED_MATCH; } } else { LM_ERR("skipping search for null sdp for %s\n", (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE"); } } qos_sdp = qos_sdp->next; } return NO_INVITE_RESP_MATCH; break; case METHOD_UPDATE: LM_ERR("FIXME\n"); return NO_UPDATE_RESP_MATCH; break; default: LM_ERR("Unexpected reply for method id %d\n", cseq_method_id); return ERROR_MATCH; } break; default: LM_ERR("Unknown SIP message type: %d\n", _m->first_line.type); return ERROR_MATCH; } LM_ERR("FIXME: out of case\n"); return ERROR_MATCH; } void link_pending_qos_sdp(qos_ctx_t *qos_ctx, qos_sdp_t *qos_sdp) { if (qos_sdp->prev != NULL) LM_ERR("got qos_sdp->prev=%p\n", qos_sdp->prev); if (qos_sdp->next != NULL) LM_ERR("got qos_sdp->next=%p\n", qos_sdp->next); if (qos_ctx->pending_sdp) { LM_DBG("Adding pending qos_sdp: %p\n", qos_sdp); if (qos_ctx->pending_sdp->prev != NULL) LM_ERR("got qos_ctx->pending_sdp->prev=%p\n", qos_ctx->pending_sdp->prev); qos_sdp->next = qos_ctx->pending_sdp; qos_ctx->pending_sdp->prev = qos_sdp; qos_ctx->pending_sdp = qos_sdp; } else { LM_DBG("Inserting pending qos_sdp: %p\n", qos_sdp); qos_ctx->pending_sdp = qos_sdp; } } void unlink_pending_qos_sdp(qos_ctx_t *qos_ctx, qos_sdp_t *qos_sdp) { if (qos_sdp->next) qos_sdp->next->prev = qos_sdp->prev; if (qos_sdp->prev) qos_sdp->prev->next = qos_sdp->next; else qos_ctx->pending_sdp = qos_sdp->next; qos_sdp->next = qos_sdp->prev = NULL; } void unlink_negotiated_qos_sdp(qos_ctx_t *qos_ctx, qos_sdp_t *qos_sdp) { if (qos_sdp->next) qos_sdp->next->prev = qos_sdp->prev; if (qos_sdp->prev) qos_sdp->prev->next = qos_sdp->next; else qos_ctx->negotiated_sdp = qos_sdp->next; qos_sdp->next = qos_sdp->prev = NULL; } void link_negotiated_qos_sdp_and_run_cb(qos_ctx_t *qos_ctx, qos_sdp_t *qos_sdp, unsigned int role, struct sip_msg *_m) { qos_sdp_t *next_qos_sdp; qos_sdp_t *temp_qos_sdp = qos_ctx->negotiated_sdp; if (qos_sdp->prev != NULL) LM_ERR("got qos_sdp->prev=%p\n", qos_sdp->prev); if (qos_sdp->next != NULL) LM_ERR("got qos_sdp->next=%p\n", qos_sdp->next); if (temp_qos_sdp) { while (temp_qos_sdp) { next_qos_sdp = temp_qos_sdp->next; if (qos_sdp->negotiation == temp_qos_sdp->negotiation) { LM_DBG("run_qos_callbacks(QOSCB_REMOVE_SDP, qos_ctx=%p, temp_qos_sdp=%p, role=%d, _m=%p)\n", qos_ctx, temp_qos_sdp, role, _m); run_qos_callbacks(QOSCB_REMOVE_SDP, qos_ctx, temp_qos_sdp, role, _m); unlink_negotiated_qos_sdp(qos_ctx, temp_qos_sdp); destroy_qos(temp_qos_sdp); break; } temp_qos_sdp = next_qos_sdp; } if (qos_ctx->negotiated_sdp) { LM_DBG("Adding negotiated qos_sdp: %p\n", qos_sdp); if (qos_ctx->negotiated_sdp->prev != NULL) LM_ERR("got qos_ctx->negotiated_sdp->prev=%p\n", qos_ctx->negotiated_sdp->prev); qos_sdp->next = qos_ctx->negotiated_sdp; qos_ctx->negotiated_sdp->prev = qos_sdp; qos_ctx->negotiated_sdp = qos_sdp; } else { LM_DBG("Inserting negotiated qos_sdp: %p\n", qos_sdp); qos_ctx->negotiated_sdp = qos_sdp; } } else { LM_DBG("Inserting first negotiated qos_sdp: %p\n", qos_sdp); qos_ctx->negotiated_sdp = qos_sdp; } LM_DBG("run_qos_callbacks(QOSCB_UPDATE_SDP, qos_ctx=%p, qos_sdp=%p, role=%d, _m=%p)\n", qos_ctx, qos_sdp, role, _m); run_qos_callbacks(QOSCB_UPDATE_SDP, qos_ctx, qos_sdp, role, _m); } int add_pending_sdp_session(qos_ctx_t *qos_ctx, unsigned int dir, str *cseq_number, str *cseq_method, int cseq_method_id, unsigned int role, unsigned int negotiation, sdp_session_cell_t *session, struct sip_msg *_m) { unsigned int len; sdp_session_cell_t *cloned_session; qos_sdp_t *qos_sdp; char *p; len = sizeof(qos_sdp_t) + cseq_method->len + cseq_number->len; qos_sdp = (qos_sdp_t *)shm_malloc(len); LM_DBG("alloc qos_sdp: %p\n", qos_sdp); if (qos_sdp==NULL) { LM_ERR("oom %d\n", len); return -1; } else { memset(qos_sdp, 0, len); LM_DBG("Allocated memory for qos_sdp: %p\n", qos_sdp); cloned_session = clone_sdp_session_cell(session); if (cloned_session==NULL) { shm_free(qos_sdp); LM_DBG("free qos_sdp: %p\n", qos_sdp); return -1; } qos_sdp->sdp_session[role] = cloned_session; LM_DBG("qos_sdp->sdp_session[%d]=%p\n", role, qos_sdp->sdp_session[role]); if (_m->first_line.type == SIP_REQUEST) { qos_sdp->method_dir = dir; } else { /* This is a SIP_REPLY and we need to set * the direction for the SIP_REQUEST */ if (dir==DLG_DIR_UPSTREAM) qos_sdp->method_dir = DLG_DIR_DOWNSTREAM; else qos_sdp->method_dir = DLG_DIR_UPSTREAM; } qos_sdp->method_id = cseq_method_id; qos_sdp->negotiation = negotiation; p = (char*)(qos_sdp+1); qos_sdp->method.s = p; qos_sdp->method.len = cseq_method->len; memcpy( p, cseq_method->s, cseq_method->len); p += cseq_method->len; qos_sdp->cseq.s = p; qos_sdp->cseq.len = cseq_number->len; memcpy( p,cseq_number->s, cseq_number->len); /* p += cseq_number->len; */ link_pending_qos_sdp(qos_ctx, qos_sdp); LM_DBG("run_qos_callbacks(QOSCB_ADD_SDP, qos_ctx=%p, qos_sdp=%p, role=%d, _m=%p)\n", qos_ctx, qos_sdp, role, _m); run_qos_callbacks(QOSCB_ADD_SDP, qos_ctx, qos_sdp, role, _m); } return 0; } /* * Add the sdp carried by the given SIP message into the qos context. */ void add_sdp(qos_ctx_t *qos_ctx, unsigned int dir, struct sip_msg *_m, unsigned int role, unsigned int other_role) { qos_sdp_t *qos_sdp; sdp_session_cell_t *recv_session; str *cseq_number, *cseq_method; int cseq_method_id, sdp_match; if ( (!_m->cseq && parse_headers(_m,HDR_CSEQ_F,0)<0) || !_m->cseq || !_m->cseq->parsed) { LM_ERR("bad sip message or missing CSeq hdr\n"); return; } cseq_number = &((get_cseq(_m))->number); cseq_method = &((get_cseq(_m))->method); cseq_method_id = (get_cseq(_m))->method_id; LM_DBG("cseq=`%.*s' `%.*s' and dir=%d\n", cseq_number->len, cseq_number->s, cseq_method->len, cseq_method->s, dir); /* Let's iterate through all the received sessions */ recv_session = _m->sdp->sessions; while(recv_session) { qos_sdp = NULL; sdp_match = find_qos_sdp(qos_ctx, dir, other_role, cseq_number, cseq_method_id, recv_session, _m, &qos_sdp); switch (sdp_match) { case NO_INVITE_REQ_MATCH: if (0!=add_pending_sdp_session( qos_ctx, dir, cseq_number, cseq_method, cseq_method_id, role, N_INVITE_200OK, recv_session, _m)) { LM_ERR("Unable to add new sdp session\n"); goto error; } break; case NO_INVITE_RESP_MATCH: if (0!=add_pending_sdp_session( qos_ctx, dir, cseq_number, cseq_method, cseq_method_id, role, N_200OK_ACK, recv_session, _m)) { LM_ERR("Unable to add new sdp session\n"); goto error; } break; case ERROR_MATCH: case NO_ACK_REQ_MATCH: case NO_UPDATE_REQ_MATCH: case NO_UPDATE_RESP_MATCH: LM_ERR("error match: %d\n", sdp_match); break; case PENDING_MATCH: LM_DBG("we have a pending match: %p\n", qos_sdp); /* Let's save the received session */ qos_sdp->sdp_session[role] = clone_sdp_session_cell(recv_session); if (qos_sdp->sdp_session[role] == NULL) { LM_ERR("PENDING_MATCH:oom: Unable to add new sdp session\n"); return; } /* Negotiation completed, need to move the established SDP into the negotiated_sdp */ /* removing qos_sdp from qos_ctx->pending_sdp list */ unlink_pending_qos_sdp(qos_ctx, qos_sdp); /* inserting qos_sdp into the qos_ctx->negotiated_sdp list */ link_negotiated_qos_sdp_and_run_cb(qos_ctx, qos_sdp, role, _m); break; case NEGOTIATED_MATCH: LM_DBG("we have a negotiated match: %p\n", qos_sdp); /* some sanity checks */ if (qos_sdp->sdp_session[role]) { free_cloned_sdp_session(qos_sdp->sdp_session[role]); } else { LM_ERR("missing sdp_session for %s\n", (role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE"); } /* Let's save the received session */ qos_sdp->sdp_session[role] = clone_sdp_session_cell(recv_session); if (qos_sdp->sdp_session[role] == NULL) { LM_ERR("NEGOTIATED_MATCH:oom: Unable to add new sdp session\n"); return; } LM_DBG("run_qos_callbacks(QOSCB_UPDATE_SDP, qos_ctx=%p, qos_sdp=%p, role=%d, _m=%p)\n", qos_ctx, qos_sdp, role, _m); run_qos_callbacks(QOSCB_UPDATE_SDP, qos_ctx, qos_sdp, role, _m); break; default: LM_CRIT("Undefined return code from find_qos_sdp(): %d\n", sdp_match); } recv_session = recv_session->next; } return; error: shm_free(qos_sdp); LM_DBG("free qos_sdp: %p\n", qos_sdp); return; } /* * Remove the sdp previously added. */ void remove_sdp(qos_ctx_t *qos_ctx, unsigned int dir, struct sip_msg *_m, unsigned int role, unsigned int other_role) { str *cseq_number; int cseq_method_id; qos_sdp_t *qos_sdp; if ( (!_m->cseq && parse_headers(_m,HDR_CSEQ_F,0)<0) || !_m->cseq || !_m->cseq->parsed) { LM_ERR("bad sip message or missing CSeq hdr\n"); return; } cseq_number = &((get_cseq(_m))->number); cseq_method_id = (get_cseq(_m))->method_id; if (_m->first_line.type == SIP_REPLY) { switch (cseq_method_id) { case METHOD_INVITE: case METHOD_UPDATE: /* searching into the pending_sdp list only */ qos_sdp = qos_ctx->pending_sdp; if (qos_sdp) qos_sdp = qos_sdp->next; while (qos_sdp) { if (cseq_method_id == qos_sdp->method_id && dir != qos_sdp->method_dir && qos_sdp->negotiation == N_INVITE_200OK && cseq_number->len == qos_sdp->cseq.len && 0 == strncmp(cseq_number->s, qos_sdp->cseq.s, cseq_number->len)) { LM_DBG("method_id, dir and cseq match with previous session %p->%p\n", qos_sdp, qos_sdp->sdp_session[other_role]); /* print_sdp_session(qos_sdp->sdp_session[other_role]); */ if (qos_sdp->sdp_session[other_role] != NULL) { LM_DBG("run_qos_callbacks(QOSCB_REMOVE_SDP, qos_ctx=%p, qos_sdp=%p, role=%d, _m=%p)\n", qos_ctx, qos_sdp, role, _m); run_qos_callbacks(QOSCB_REMOVE_SDP, qos_ctx, qos_sdp, role, _m); unlink_negotiated_qos_sdp(qos_ctx, qos_sdp); /* Here we free up the pending qos_sdp */ destroy_qos(qos_sdp); continue; } else { LM_ERR("skipping search for null sdp for %s\n", (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE"); } } qos_sdp = qos_sdp->next; } /* end while (qos_sdp) */ break; default: LM_ERR("Unexpected method id %d\n", cseq_method_id); } } else { LM_ERR("we remove sdp only for a SIP_REPLY, not for a %d\n", _m->first_line.type); } return; } void destroy_qos_ctx(qos_ctx_t *qos_ctx) { qos_sdp_t * qos_sdp, * next_qos_sdp; lock_get(&qos_ctx->lock); qos_sdp = qos_ctx->pending_sdp; while (qos_sdp) { next_qos_sdp = qos_sdp->next; destroy_qos(qos_sdp); qos_sdp = next_qos_sdp; } qos_sdp = qos_ctx->negotiated_sdp; while (qos_sdp) { next_qos_sdp = qos_sdp->next; destroy_qos(qos_sdp); qos_sdp = next_qos_sdp; } lock_release(&qos_ctx->lock); lock_destroy(&qos_ctx->lock); LM_DBG("free qos_ctx: %p\n", qos_ctx); shm_free(qos_ctx); return; } opensips-2.2.2/modules/qos/qos_ctx_helpers.h000066400000000000000000000072111300170765700212110ustar00rootroot00000000000000/* * Copyright (C) 2007 SOMA Networks, INC. * Written By Ovidiu Sas * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2007-07-16 initial version (osas) */ #ifndef _QOS_CTX_HELPERS_H_ #define _QOS_CTX_HELPERS_H_ #include "qos_cb.h" #define QOS_CALLER 0 #define QOS_CALLEE 1 typedef struct qos_sdp_st { struct qos_sdp_st *prev; struct qos_sdp_st *next; unsigned int method_dir; /* the transaction initiator: CALLER/CALLEE */ int method_id; /* the method id that is carrying the sdp */ str method; /* the method that is carrying the sdp */ str cseq; /* the cseq of the method */ unsigned int negotiation; /* the negotiation type */ sdp_session_cell_t *sdp_session[2]; /* CALLER's and CALLEE's sdp */ } qos_sdp_t; /** * The QoS context. */ typedef struct qos_ctx_st { qos_sdp_t *negotiated_sdp; qos_sdp_t *pending_sdp; gen_lock_t lock; struct qos_head_cbl cbs; } qos_ctx_t; /* ** AFTER INVITE/183 ** qos_ctx: +----------------+ | *negotiated_sdp| +----------------+ qos_sdp (pending) | *pending_sdp------->+----------------+ +----------------+ | *prev | +----------------+ | *next | +----------------+ | method_dir | sdp_session (caller) | method_id | +-->+----------+ | method | | | | | cseq | | | | | negotiation | | +----------+ +----------------+ | | sdp_session[0]----+ +----------------+ sdp_session (callee) | sdp_session[1]------->+----------+ +----------------+ | | | | +----------+ ** AFTER INVITE/200ok ** qos_ctx: +----------------+ qos_sdp (negotiated) | *negotiated_sdp---->+----------------+ +----------------+ | *prev | | *pending_sdp | +----------------+ +----------------+ | *next | +----------------+ | method_dir | sdp_session (caller) | method_id | +-->+----------+ | method | | | | | cseq | | | | | negotiation | | +----------+ +----------------+ | | sdp_session[0]----+ +----------------+ sdp_session (callee) | sdp_session[1]------->+----------+ +----------------+ | | | | +----------+ */ qos_ctx_t* build_new_qos_ctx(); void destroy_qos_ctx(qos_ctx_t *ctx); void add_sdp(qos_ctx_t *qos_ctx, unsigned int dir, struct sip_msg *_m, unsigned int role, unsigned int other_role); void remove_sdp(qos_ctx_t *qos_ctx, unsigned int dir, struct sip_msg *_m, unsigned int role, unsigned int other_role); #endif /* _QOS_CTX_HELPERS_H_ */ opensips-2.2.2/modules/qos/qos_handlers.c000066400000000000000000000175161300170765700204750ustar00rootroot00000000000000/* * Copyright (C) 2007 SOMA Networks, Inc. * Written by Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2007-07-16 initial version (osas) */ #include /* for snprintf() */ #include /* for memset() */ #include /* For atoi() */ #include "../../parser/parser_f.h" #include "../../parser/sdp/sdp.h" #include "../../ut.h" #include "../../dprint.h" #include "../dialog/dlg_hash.h" #include "qos_mi.h" #include "qos_handlers.h" #include "qos_ctx_helpers.h" /** * The binding to the dialog module functions. Most importantly the * register_dlgcb function. */ extern struct dlg_binds *dlg_binds; /** * Local function prototypes. See function definition for * documentation. */ static void setup_dialog_callbacks(struct dlg_cell *did, qos_ctx_t *ctx); static void qos_dialog_destroy_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params); static void qos_dialog_request_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params); static void qos_dialog_response_CB(struct dlg_cell* did, int type,struct dlg_cb_params * params); /** * The value of the message flag to flag an INVITE we want to process * through the QoS module. */ //static int qos_flag = 0; /** * A helper function to setup all the callbacks from the dialog module * after we find intrest in the dialog. * * @param did The Dialog ID. * @param info The qos information. * */ static void setup_dialog_callbacks(struct dlg_cell *did, qos_ctx_t *ctx) { dlg_binds->register_dlgcb(did, DLGCB_REQ_WITHIN, qos_dialog_request_CB, (void *)ctx, NULL); dlg_binds->register_dlgcb(did, DLGCB_RESPONSE_FWDED|DLGCB_RESPONSE_WITHIN, qos_dialog_response_CB, (void *)ctx, NULL); dlg_binds->register_dlgcb(did, DLGCB_DESTROY, qos_dialog_destroy_CB, (void *)ctx, NULL); dlg_binds->register_dlgcb(did, DLGCB_MI_CONTEXT, qos_dialog_mi_context_CB, (void *)ctx, NULL); return; } /** * Every time a new dialog is created (from a new INVITE) the dialog * module will call this callback function. We need to track the * dialogs lifespan from this point forward until it is terminated * with a BYE, CANCEL, etc. In the process, we will see if either or * both ends of the conversation are trying to re-negotiate the media. * * This function will setup the other types of dialog callbacks * required to track the lifespan of the dialog. * * * @param did - The dialog ID * @param type - The trigger event type (CREATED) * @param msg - The SIP message that triggered the callback (INVITE) * @param param - The pointer to nothing. As we did not attach * anything to this callback in the dialog module. */ void qos_dialog_created_CB(struct dlg_cell *did, int type, struct dlg_cb_params * params) { qos_ctx_t *qos_ctx = NULL; struct sip_msg* msg = params->msg; unsigned int dir = params->direction, role, other_role; if (dir == DLG_DIR_UPSTREAM) { role = QOS_CALLEE; other_role = QOS_CALLER; } else if (dir == DLG_DIR_DOWNSTREAM) { role = QOS_CALLER; other_role = QOS_CALLEE; } else { LM_ERR("Unknown dir %d\n", dir); return; } if (msg == NULL || msg == FAKED_REPLY) { LM_ERR("Improper msg\n"); return; } /* look only at INVITE */ if (msg->first_line.type != SIP_REQUEST || msg->first_line.u.request.method_value != METHOD_INVITE) { LM_WARN("Dialog create callback called with a non-INVITE req.\n"); return; } qos_ctx = build_new_qos_ctx(); if (qos_ctx==NULL) { /* Error message printed in build_new_qos_ctx() */ return; } LM_DBG("setup_dialog_callbacks( %p , %p )\n", did, qos_ctx); setup_dialog_callbacks(did, qos_ctx); run_create_cbs(qos_ctx, msg); if (0 == parse_sdp(msg)) { lock_get(&qos_ctx->lock); add_sdp(qos_ctx, dir, msg, role, other_role); lock_release(&qos_ctx->lock); } return; } /** * This callback is called when ever a dialog isdestroyed. * * @param did - The Dialog ID / structure pointer. Used as an ID only. * @param type - The termination cause/reason. * @param params - pointer to the dlg_cb params */ static void qos_dialog_destroy_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { struct sip_msg* msg = params->msg; qos_ctx_t* qos_ctx = (qos_ctx_t*)*(params->param); /* run the QOSCB_TERMINATED callback */ run_qos_callbacks(QOSCB_TERMINATED, qos_ctx, NULL, 0, msg); /* Free the param qos_info_t memory */ if (qos_ctx) { destroy_qos_ctx(qos_ctx); params->param = NULL; } return; } /** * * @param did - The dialog structure. The pointer is used as an ID. * @param type - The reason for the callback. DLGCB_REQ_WITHIN * @param msg - The SIP message that causes the callback. * @param param - The qos information */ static void qos_dialog_request_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { struct sip_msg* msg = params->msg; unsigned int dir = params->direction, role, other_role; qos_ctx_t* qos_ctx = (qos_ctx_t*)*(params->param); if (dir == DLG_DIR_UPSTREAM) { role = QOS_CALLEE; other_role = QOS_CALLER; } else if (dir == DLG_DIR_DOWNSTREAM) { role = QOS_CALLER; other_role = QOS_CALLEE; } else { LM_ERR("Unknown dir %d\n", dir); return; } if (msg->first_line.type == SIP_REQUEST) { if ( (msg->first_line.u.request.method_value == METHOD_INVITE) || (msg->first_line.u.request.method_value == METHOD_UPDATE) || (msg->first_line.u.request.method_value == METHOD_ACK) || (msg->first_line.u.request.method_value == METHOD_PRACK)) { if (0 == parse_sdp(msg)) { lock_get(&qos_ctx->lock); add_sdp(qos_ctx, dir, msg, role, other_role); lock_release(&qos_ctx->lock); } } else { LM_DBG("Ignoring non-carrying SDP req\n"); return; } } else { LM_ERR("not a SIP_REQUEST\n"); return; } return; } /** * This callback is called on any response message in the lifespan of * the dialog. The callback is called just before the message is * copied to pkg memory so it is still mutable. * * @param did - The dialog structure. The pointer is used as an ID. * @param type - The reason for the callback. DLGCB_CONFIRMED * @param msg - The SIP message that causes the callback. * @param param - The qos information */ static void qos_dialog_response_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { struct sip_msg* msg = params->msg; unsigned int dir = params->direction, role, other_role; qos_ctx_t* qos_ctx = (qos_ctx_t*)*(params->param); if (dir == DLG_DIR_UPSTREAM) { role = QOS_CALLEE; other_role = QOS_CALLER; } else if (dir == DLG_DIR_DOWNSTREAM) { role = QOS_CALLER; other_role = QOS_CALLEE; } else { LM_ERR("Unknown dir %d\n", dir); return; } if (msg->first_line.type == SIP_REPLY) { if (msg->first_line.u.reply.statuscode > 100 && msg->first_line.u.reply.statuscode < 300) { if (0 == parse_sdp(msg)) { lock_get(&qos_ctx->lock); add_sdp(qos_ctx, dir, msg, role, other_role); lock_release(&qos_ctx->lock); } } else if (msg->first_line.u.reply.statuscode > 399 && msg->first_line.u.reply.statuscode < 700) { lock_get(&qos_ctx->lock); remove_sdp(qos_ctx, dir, msg, role, other_role); lock_release(&qos_ctx->lock); } } else { LM_ERR("not a SIP_REPLY\n"); return; } return; } opensips-2.2.2/modules/qos/qos_handlers.h000066400000000000000000000024151300170765700204720ustar00rootroot00000000000000/* * Copyright (C) 2007 SOMA Networks, Inc. * Written By Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2007-07-16 initial version (osas) */ #ifndef _QOS_HANDLERS_H_ #define _QOS_HANDLERS_H_ #include "../../sr_module.h" /* Needed for find_export() */ #include "../../parser/msg_parser.h" #include "../dialog/dlg_load.h" /** * The static (opening) callback function for all dialog creations */ void qos_dialog_created_CB(struct dlg_cell *did, int type, struct dlg_cb_params * params); #endif /* _QOS_HANDLERS_H_ */ opensips-2.2.2/modules/qos/qos_load.h000066400000000000000000000027341300170765700176150ustar00rootroot00000000000000/* * Copyright (C) 2007 SOMA Networks, Inc. * Written by Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-07-14 initial version (osas) */ #ifndef _QOS_QOS_LOAD_H_ #define _QOS_QOS_LOAD_H_ #include "qos_cb.h" struct qos_binds { register_qoscb_f register_qoscb; }; typedef int(*load_qos_f)( struct qos_binds *qosb ); int load_qos( struct qos_binds *qosb); static inline int load_qos_api( struct qos_binds *qosb ) { load_qos_f load_qos; /* import the QOS auto-loading function */ if ( !(load_qos=(load_qos_f)find_export("load_qos", 0, 0))) { LM_ERR("can't import load_qos\n"); return -1; } /* let the auto-loading function load all QOS stuff */ if (load_qos( qosb )==-1) return -1; return 0; } #endif opensips-2.2.2/modules/qos/qos_mi.c000066400000000000000000000152751300170765700173020ustar00rootroot00000000000000/* * Copyright (C) 2007 SOMA Networks, Inc. * Written by Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2007-07-16 initial version (osas) */ #include "../../ut.h" #include "../../dprint.h" #include "../../mi/mi.h" #include "qos_handlers.h" #include "qos_ctx_helpers.h" int add_mi_sdp_payload_nodes(struct mi_node* node, int index, sdp_payload_attr_t* sdp_payload) { struct mi_node* node1; struct mi_attr* attr; char* p; int len; p = int2str((unsigned long)(index), &len); node1 = add_mi_node_child( node, MI_DUP_VALUE, "payload", 7, p, len); if (node1==NULL) return 1; attr = add_mi_attr(node1, MI_DUP_VALUE, "rtpmap", 6, sdp_payload->rtp_payload.s, sdp_payload->rtp_payload.len); if (attr==NULL) return 1; if (sdp_payload->rtp_enc.s!=NULL && sdp_payload->rtp_enc.len!=0) { attr = add_mi_attr(node1, MI_DUP_VALUE, "codec", 5, sdp_payload->rtp_enc.s, sdp_payload->rtp_enc.len); if(attr == NULL) return 1; } return 0; } int add_mi_stream_nodes(struct mi_node* node, int index, sdp_stream_cell_t* stream) { struct mi_node* node1; struct mi_attr* attr; sdp_payload_attr_t* sdp_payload; char* p; int i, len; p = int2str((unsigned long)(index), &len); node1 = add_mi_node_child( node, MI_IS_ARRAY|MI_DUP_VALUE, "stream", 6, p, len); if (node1==NULL) return 1; attr = add_mi_attr(node1, MI_DUP_VALUE, "media", 5, stream->media.s, stream->media.len); if(attr == NULL) return 1; attr = add_mi_attr(node1, MI_DUP_VALUE, "IP", 2, stream->ip_addr.s, stream->ip_addr.len); if(attr == NULL) return 1; attr = add_mi_attr(node1, MI_DUP_VALUE, "port", 4, stream->port.s, stream->port.len); if(attr == NULL) return 1; attr = add_mi_attr(node1, MI_DUP_VALUE, "transport", 9, stream->transport.s, stream->transport.len); if(attr == NULL) return 1; if (stream->sendrecv_mode.s!=NULL && stream->sendrecv_mode.len!=0) { attr = add_mi_attr(node1, MI_DUP_VALUE, "sendrecv", 8, stream->sendrecv_mode.s, stream->sendrecv_mode.len); if(attr == NULL) return 1; } if (stream->ptime.s!=NULL && stream->ptime.len!=0) { attr = add_mi_attr(node1, MI_DUP_VALUE, "ptime", 5, stream->ptime.s, stream->ptime.len); if(attr == NULL) return 1; } p = int2str((unsigned long)(stream->payloads_num), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "payloads_num", 12, p, len); if(attr == NULL) return 1; sdp_payload = stream->payload_attr; for(i=stream->payloads_num-1;i>=0;i--){ if (!sdp_payload) { LM_ERR("got NULL sdp_payload\n"); return 1; } if (0!=add_mi_sdp_payload_nodes(node1, i, sdp_payload)){ return 1; } sdp_payload = sdp_payload->next; } return 0; } int add_mi_session_nodes(struct mi_node* node, int index, sdp_session_cell_t* session) { struct mi_node* node1; struct mi_attr* attr; sdp_stream_cell_t* stream; char* p; int i, len; switch (index) { case 0: node1 = add_mi_node_child( node, MI_IS_ARRAY|MI_DUP_VALUE, "session", 7, "caller", 6); if (node1==NULL) return 1; break; case 1: node1 = add_mi_node_child( node, MI_IS_ARRAY|MI_DUP_VALUE, "session", 7, "callee", 6); if (node1==NULL) return 1; break; default: return 1; } attr = add_mi_attr(node1, MI_DUP_VALUE, "cnt_disp", 8, session->cnt_disp.s, session->cnt_disp.len); if(attr == NULL) return 1; attr = add_mi_attr(node1, MI_DUP_VALUE, "bw_type", 7, session->bw_type.s, session->bw_type.len); if(attr == NULL) return 1; attr = add_mi_attr(node1, MI_DUP_VALUE, "bw_width", 8, session->bw_width.s, session->bw_width.len); if(attr == NULL) return 1; p = int2str((unsigned long)(session->streams_num), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "streams", 7, p, len); if(attr == NULL) return 1; stream = session->streams; for(i=session->streams_num-1;i>=0;i--){ if (!stream) { LM_ERR("got NULL stream\n"); return 1; } if (0!=add_mi_stream_nodes(node1, i, stream)){ return 1; } stream = stream->next; } return 0; } int add_mi_sdp_nodes(struct mi_node* node, qos_sdp_t* qos_sdp) { struct mi_node* node1; struct mi_attr* attr; char* p; int i, len; sdp_session_cell_t* session; if ( qos_sdp->prev != NULL ) LM_ERR("got qos_sdp->prev=%p\n", qos_sdp->prev); while (qos_sdp) { node1 = add_mi_node_child( node, MI_IS_ARRAY|MI_DUP_VALUE, "sdp", 3, NULL, 0); if (node1==NULL) return 1; p = int2str((unsigned long)(qos_sdp->method_dir), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "m_dir", 5, p, len); if(attr == NULL) return 1; p = int2str((unsigned long)(qos_sdp->method_id), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "m_id", 4, p, len); if(attr == NULL) return 1; attr = add_mi_attr(node1, MI_DUP_VALUE, "method", 6, qos_sdp->method.s, qos_sdp->method.len); if(attr == NULL) return 1; attr = add_mi_attr(node1, MI_DUP_VALUE, "cseq", 4, qos_sdp->cseq.s, qos_sdp->cseq.len); if(attr == NULL) return 1; p = int2str((unsigned long)(qos_sdp->negotiation), &len); attr = add_mi_attr(node1, MI_DUP_VALUE, "negotiation", 11, p, len); if(attr == NULL) return 1; for (i=1;i>=0;i--){ session = qos_sdp->sdp_session[i]; if (session) { if (0 != add_mi_session_nodes(node1, i, session)) return 1; } } qos_sdp = qos_sdp->next; } return 0; } void qos_dialog_mi_context_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { struct mi_node* parent_node = (struct mi_node*)(params->dlg_data); struct mi_node* node; qos_ctx_t* qos_ctx = (qos_ctx_t*)*(params->param); qos_sdp_t* qos_sdp; qos_sdp = qos_ctx->pending_sdp; if (qos_sdp) { node = add_mi_node_child(parent_node, MI_IS_ARRAY|MI_DUP_VALUE, "qos_pending_sdp", 15, NULL, 0); if (node==NULL) { LM_ERR("oom\n"); return; } if (0 != add_mi_sdp_nodes( node, qos_sdp)) return; } qos_sdp = qos_ctx->negotiated_sdp; if (qos_sdp) { node = add_mi_node_child(parent_node, MI_IS_ARRAY|MI_DUP_VALUE, "qos_negotiated_sdp", 18, NULL, 0); if (node==NULL) { LM_ERR("oom\n"); return; } if (0 != add_mi_sdp_nodes( node, qos_sdp)) return; } return; } opensips-2.2.2/modules/qos/qos_mi.h000066400000000000000000000023351300170765700173000ustar00rootroot00000000000000/* * Copyright (C) 2007 SOMA Networks, Inc. * Written By Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2007-07-16 initial version (osas) */ #ifndef _QOS_MI_H_ #define _QOS_MI_H_ #include "../../sr_module.h" /* Needed for find_export() */ #include "../../parser/msg_parser.h" #include "../dialog/dlg_load.h" /** * The dialog mi helper function. */ void qos_dialog_mi_context_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params); #endif /* _QOS_MI_H_ */ opensips-2.2.2/modules/ratelimit/000077500000000000000000000000001300170765700170255ustar00rootroot00000000000000opensips-2.2.2/modules/ratelimit/Makefile000066400000000000000000000003201300170765700204600ustar00rootroot00000000000000# # ratelimit module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=ratelimit.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/ratelimit/README000066400000000000000000000447761300170765700177270ustar00rootroot00000000000000ratelimit Module Ovidiu Sas Bogdan Vasile Harjoc Hendrik Scholz Razvan Crainea Edited by Ovidiu Sas Edited by Bogdan Vasile Harjoc Edited by Hendrik Scholz Edited by Razvan Crainea Copyright © 2006 Freenet Cityline GmbH Copyright © 2008 VoIP Embedded Inc. Copyright © 2011 OpenSIPS Foundation Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Use Cases 1.3. Static Rate Limiting Algorithms 1.3.1. Tail Drop Algorithm (TAILDROP) 1.3.2. Random Early Detection Algorithm (RED) 1.3.3. Slot Based Taildropping (SBT) 1.3.4. Network Algorithm (NETWORK) 1.4. Dynamic Rate Limiting Algorithms 1.4.1. Feedback Algorithm (FEEDBACK) 1.5. Dependencies 1.5.1. OpenSIPS Modules 1.5.2. External Libraries or Applications 1.6. Exported Parameters 1.6.1. timer_interval (integer) 1.6.2. expire_time (integer) 1.6.3. hash_size (integer) 1.6.4. default_algorithm (string) 1.6.5. cachedb_url (string) 1.6.6. db_prefix (string) 1.6.7. repl_buffer_threshold (string) 1.6.8. repl_timer_interval (string) 1.6.9. repl_timer_expire (string) 1.6.10. replicate_pipes_to (integer) 1.6.11. accept_pipes_from (integer) 1.6.12. accept_pipes_timeout (integer) 1.6.13. repl_pipes_auth_check (int) 1.6.14. window_size (int) 1.6.15. slot_period (int) 1.7. Exported Functions 1.7.1. rl_check(name, limit[, algorithm]) 1.7.2. rl_dec_count(name) 1.7.3. rl_reset_count(name) 1.8. Exported MI Functions 1.8.1. rl_list 1.8.2. rl_reset_pipe 1.8.3. rl_set_pid 1.8.4. rl_get_pid 1.8.5. rl_bin_status 1.9. Exported pseudo-variables 1.9.1. $rl_count(name) List of Examples 1.1. Set timer_interval parameter 1.2. Set expire_time parameter 1.3. Set hash_size parameter 1.4. Set default_algorithm parameter 1.5. Set cachedb_url parameter 1.6. Set db_prefix parameter 1.7. Set repl_buffer_threshold parameter 1.8. Set repl_timer_interval parameter 1.9. Set repl_timer_expire parameter 1.10. Set replicate_pipes_to parameter 1.11. Set accept_pipes_from parameter 1.12. Set accept_pipes_timeout parameter 1.13. Set repl_pipes_auth_check parameter 1.14. Set window_size parameter 1.15. Set slot_period parameter 1.16. rl_check usage 1.17. rl_dec_count usage 1.18. rl_reset_count usage Chapter 1. Admin Guide 1.1. Overview This module implements rate limiting for SIP requests. In contrast to the PIKE module this limits the flow based on a per SIP request type basis and not per source IP. The latest sources allow you to dynamically group several messages into some entities and limit the traffic based on them. The MI interface can be used to change tunables while running OpenSIPS. This module is also integrated with the OpenSIPS Key-Value Interface, providing support for distributed rate limiting using Redis or Memcached CacheDB backends. To achieve a distributed ratelimit feature, the module can replicate its pipes counters to different OpenSIPS interfaces using the binary replicate interface (BIN). To do that, define the repl_* parameters in your configuration script. 1.2. Use Cases Limiting the rate messages are processed on a system directly influences the load. The ratelimit module can be used to protect a single host or to protect an OpenSIPS cluster when run on the dispatching box in front. Distributed limiting is useful when the rate limit should be performed not only on a specific node, but on the entire platform. The internal limiting data will no longer be kept on each OpenSIPS instance. It will be stored in a distributed Key-Value database and queried by each instance before deciding if a SIP message should be blocked or not. NOTE: that this behavior only makes sense when the pipe algorithm used is TAILDROP or RED. A sample configuration snippet might look like this: ... if (!rl_check("$rU", "50", "TAILDROP")) { sl_send_reply("503", "Server Unavailable"); exit; }; ... Upon every incoming request listed above rl_check is invoked and the entity identified by the R-URI user is checked. It returns an OK code if the current per request load is below the configured threshold. If the load is exceeded the function returns an error and an administrator can discard requests with a stateless response. 1.3. Static Rate Limiting Algorithms The ratelimit module supports two different static algorithms to be used by rl_check to determine whether a message should be blocked or not. 1.3.1. Tail Drop Algorithm (TAILDROP) This is a trivial algorithm that imposes some risks when used in conjunction with long timer intervals. At the start of each interval an internal counter is reset and incremented for each incoming message. Once the counter hits the configured limit rl_check returns an error. The downside of this algorithm is that it can lead to SIP client synchronization. During a relatively long interval only the first requests (i.e. REGISTERs) would make it through. Following messages (i.e. RE-REGISTERs) will all hit the SIP proxy at the same time when a common Expire timer expired. Other requests will be retransmissed after given time, the same on all devices with the same firmware/by the same vendor. 1.3.2. Random Early Detection Algorithm (RED) Random Early Detection tries to circumvent the synchronization problem imposed by the tail drop algorithm by measuring the average load and adapting the drop rate dynamically. When running with the RED algorithm OpenSIPS will return errors to the OpenSIPS routing engine every n'th packet trying to evenly spread the measured load of the last timer interval onto the current interval. As a negative side effect OpenSIPS might drop messages although the limit might not be reached within the interval. Decrease the timer interval if you encounter this. 1.3.3. Slot Based Taildropping (SBT) SBT holds a window consisting of one or more slots. You can set the window_size parameter(seconds) which means for how long we should look back to count the calls and slot_period parameter(miliseconds) which tells how granular the algorithm should be. The number of slots will be window_size/slot_period. If, for example, you have window_size= slot_period=1 second, then after each second you shall lose the call count, but if you set the slot_period to 100 microseconds, then when your call will be outside the window, the calls in the first 100 microseconds shall be dropped, and the rest in the next 900 shall be kept. 1.3.4. Network Algorithm (NETWORK) This algorithm relies on information provided by network interfaces. The total amount of bytes waiting to be consumed on all the network interfaces is retrieved once every timer_interval seconds. If the returned amount exceeds the limit specified in the modparam, rl_check returns an error. 1.4. Dynamic Rate Limiting Algorithms When running OpenSIPS on different machines, one has to adjust the drop rates for the static algorithms to maintain a sub 100% load average or packets start getting dropped in the network stack. While this is not in itself difficult, it isn't neither accurate nor trivial: another server taking a notable fraction of the cpu time will require re-tuning the parameters. While tuning the drop rates from the outside based on a certain factor is possible, having the algorithm run inside ratelimit permits tuning the rates based on internal server parameters and is somewhat more flexible (or it will be when support for external load factors - as opposed to cpu load - is added). 1.4.1. Feedback Algorithm (FEEDBACK) Using the PID Controller model (see Wikipedia page), the drop rate is adjusted dynamically based on the load factor so that the load factor always drifts towards the specified limit (or setpoint, in PID terms). As reading the cpu load average is relatively expensive (opening /proc/stat, parsing it, etc), this only happens once every timer_interval seconds and consequently the FEEDBACK value is only at these intervals recomputed. This in turn makes it difficult for the drop rate to adjust quickly. Worst case scenarios are request rates going up/down instantly by thousands - it takes up to 20 seconds for the controller to adapt to the new request rate. Generally though, as real life request rates drift by less, adapting should happen much faster. 1.5. Dependencies 1.5.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.5.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.6. Exported Parameters 1.6.1. timer_interval (integer) The initial length of a timer interval in seconds. All amounts of messages have to be divided by this timer to get a messages per second value. IMPORTANT: A too small value may lead to performance penalties due to timer process overloading. Default value is 10. Example 1.1. Set timer_interval parameter ... modparam("ratelimit", "timer_interval", 5) ... 1.6.2. expire_time (integer) This parameter specifies how long a pipe should be kept in memory until deleted. Default value is 3600. Example 1.2. Set expire_time parameter ... modparam("ratelimit", "expire_time", 1800) ... 1.6.3. hash_size (integer) The size of the hash table internally used to keep the pipes. A larger table is much faster but consumes more memory. The hash size must be a power of 2 number. Default value is 1024. Example 1.3. Set hash_size parameter ... modparam("ratelimit", "hash_size", 512) ... 1.6.4. default_algorithm (string) Specifies which algorithm should be assumed in case it isn't explicitly specified in the rl_check function. Default value is "TAILDROP". Example 1.4. Set default_algorithm parameter ... modparam("ratelimit", "default_algorithm", "RED") ... 1.6.5. cachedb_url (string) Enables distributed rate limiting and specifies the backend that should be used by the CacheDB interface. Default value is "disabled". Example 1.5. Set cachedb_url parameter ... modparam("ratelimit", "cachedb_url", "redis://root:root@127.0.0.1/") ... 1.6.6. db_prefix (string) Specifies what prefix should be added to the pipe name. This is only used when distributed rate limiting is enabled. Default value is "rl_pipe_". Example 1.6. Set db_prefix parameter ... modparam("ratelimit", "db_prefix", "ratelimit_") ... 1.6.7. repl_buffer_threshold (string) Used to specify the length of the buffer used by the binary replication, in bytes. Usually this should be big enough to hold as much data as possible, but small enough to avoid UDP fragmentation. The recommended value is the smallest MTU between all the replication instances. Default value is 1400 bytes. Example 1.7. Set repl_buffer_threshold parameter ... modparam("ratelimit", "repl_buffer_threshold", 500) ... 1.6.8. repl_timer_interval (string) Timer in milliseconds, used to specify how often the module should replicate its counters to the other instances. Default value is 10 ms. Example 1.8. Set repl_timer_interval parameter ... modparam("ratelimit", "repl_timer_interval", 100) ... 1.6.9. repl_timer_expire (string) Timer in seconds, used to specify when the counter received from a different instance should no longer be taken into account. This is used to prevent obsolete values, in case an instance stops replicating its counters. Default value is 10 s. Example 1.9. Set repl_timer_expire parameter ... modparam("ratelimit", "repl_timer_expire", 10) ... 1.6.10. replicate_pipes_to (integer) Used to specify the instances, that belong to a certain cluster, where the pipes should be replicated. Default value is 0. (no replication destinations) Example 1.10. Set replicate_pipes_to parameter ... modparam("ratelimit", "replicate_pipes_to", 1) ... 1.6.11. accept_pipes_from (integer) Used to specify the instances, that belong to a certain cluster, from which we should expect incoming packets. Default value is 0. (disabled) Example 1.11. Set accept_pipes_from parameter ... modparam("ratelimit", "accept_pipes_from", 1) ... 1.6.12. accept_pipes_timeout (integer) The time between two succesive incoming packets. Default value is 10. Example 1.12. Set accept_pipes_timeout parameter ... modparam("ratelimit", "accept_pipes_timeout", 1) ... 1.6.13. repl_pipes_auth_check (int) Authentication check for incoming packets. Default value is “0†(disabled). Example 1.13. Set repl_pipes_auth_check parameter ... modparam("dialog", "repl_pipes_auth_check", 1) ... 1.6.14. window_size (int) How long the history in SBT should be in seconds. Default value is “10â€. Example 1.14. Set window_size parameter ... modparam("dialog", "window_size", 5) ... 1.6.15. slot_period (int) Value of one slot in microseconds. This parameter determines how granular the algorithm should be. The number of slots will be determined by window_size/slot_period. Default value is “200â€. Example 1.15. Set slot_period parameter ... modparam("dialog", "window_size", 5) #we will have 50 slots of 100 microseconds modparam("dialog", "slot_period", 100) ... 1.7. Exported Functions 1.7.1. rl_check(name, limit[, algorithm]) Check the current request against the pipe identified by name and changes/updates the limit. If no pipe is found, then a new one is created with the specified limit and algorithm, if specified. If the algorithm parameter doesn't exist, the default one is used. NOTE: A pipe's algorithm cannot be dynamically changed. Only the one specified when the pipe was created will be considered. The method will return an error code if the limit for the matched pipe is reached. Meaning of the parameters is as follows: * name - this is the name that identifies the pipe which should be checked. This parameter accepts both strings and pseudovariables. * limit - this specifies the threshold limit of the pipe. It is strongly related to the algorithm used. This parameter accepts an integer or a pseudovariable. Note that the limit should be specified as per-second, not per-timer_interval. * algorithm - this is parameter is optional and reffers to the algorithm used to check the pipe. If it is not set, the default value is used. It accepts a string or a pseudovariable. This function can be used from REQUEST_ROUTE. Example 1.16. rl_check usage ... # perform a pipe match for all INVITE methods using RED algorith m if (is_method("INVITE")) { if (!rl_check("pipe_INVITE", "100", "RED")) { sl_send_reply("503", "Server Unavailable"); exit; }; }; ... # use default algorithm for each different gateway $var(limit) = 10; if (!rl_check("gw_$ru", "$var(limit)")) { sl_send_reply("503", "Server Unavailable"); exit; }; ... 1.7.2. rl_dec_count(name) This function decreases a counter that could have been previously increased by rl_check function. Meaning of the parameters is as follows: * name - identifies the name of the pipe. This function can be used from REQUEST_ROUTE. Example 1.17. rl_dec_count usage ... if (!rl_check("gw_$ru", "100", "TAILDROP")) { exit; } else { rl_dec_count("gw_$ru"); }; ... 1.7.3. rl_reset_count(name) This function resets a counter that could have been previously increased by rl_check function. Meaning of the parameters is as follows: * name - identifies the name of the pipe. This function can be used from REQUEST_ROUTE. Example 1.18. rl_reset_count usage ... if (!rl_check("gw_$ru", "100", "TAILDROP")) { exit; } else { rl_reset_count("gw_$ru"); }; ... 1.8. Exported MI Functions 1.8.1. rl_list Lists the parameters and variabiles in the ratelimit module. Name: rl_list Parameters: * pipe - indicates the name of the pipe. This parameter is optional. If it doesn't exist, all the active pipes are listed. Otherwise only the one specified. MI FIFO Command Format: :rl_list:_reply_fifo_file_ gw_10.0.0.1 _empty_line_ :rl_list:_reply_fifo_file_ _empty_line_ 1.8.2. rl_reset_pipe Resets the counter of a specified pipe. Name: rl_reset_pipe Parameters: * pipe - indicates the name of the pipe whose couter should be reset. MI FIFO Command Format: :rl_reset_pipe:_reply_fifo_file_ gw_10.0.0.1 _empty_line_ 1.8.3. rl_set_pid Sets the PID Controller parameters for the Feedback Algorithm. Name: rl_set_pid Parameters: * ki - the integral parameter. * kp - the proportional parameter. * kd - the derivative parameter. MI FIFO Command Format: :rl_set_pid:_reply_fifo_file_ 0.5 0.5 0.5 _empty_line_ 1.8.4. rl_get_pid Gets the list of in use PID Controller parameters. Name: rl_get_pid Parameters: none MI FIFO Command Format: :rl_get_pid:_reply_fifo_file_ _empty_line_ 1.8.5. rl_bin_status Dumps each destination used for replication, as well as the timestamp of the last message received from them. Name: rl_bin_status Parameters: none MI FIFO Command Format: :rl_bin_status:_reply_fifo_file_ _empty_line_ 1.9. Exported pseudo-variables 1.9.1. $rl_count(name) Returns the counter of a pipe. The variable is read-only. NULL will be returned if the pipe does not exist. opensips-2.2.2/modules/ratelimit/doc/000077500000000000000000000000001300170765700175725ustar00rootroot00000000000000opensips-2.2.2/modules/ratelimit/doc/ratelimit.xml000066400000000000000000000042301300170765700223050ustar00rootroot00000000000000 %docentities; ]> ratelimit Module &osipsname; Ovidiu Sas
osas@voipembedded.com
Bogdan Vasile Harjoc
Bogdan.Vasile.Harjoc@fokus.fraunhofer.de
Hendrik Scholz
hscholz@raisdorf.net
Razvan Crainea
razvancrainea@opensips.org
Ovidiu Sas
osas@voipembedded.com
Bogdan Vasile Harjoc
Bogdan.Vasile.Harjoc@fokus.fraunhofer.de
Hendrik Scholz
hscholz@raisdorf.net
Razvan Crainea
razvancrainea@opensips.org
2006 Freenet Cityline GmbH 2008 VoIP Embedded Inc. 2011 OpenSIPS Foundation $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/ratelimit/doc/ratelimit_admin.xml000066400000000000000000000551401300170765700234630ustar00rootroot00000000000000 &adminguide;
Overview This module implements rate limiting for SIP requests. In contrast to the PIKE module this limits the flow based on a per SIP request type basis and not per source IP. The latest sources allow you to dynamically group several messages into some entities and limit the traffic based on them. The MI interface can be used to change tunables while running OpenSIPS. This module is also integrated with the &osips; Key-Value Interface, providing support for distributed rate limiting using Redis or Memcached CacheDB backends. To achieve a distributed ratelimit feature, the module can replicate its pipes counters to different &osips; interfaces using the binary replicate interface (BIN). To do that, define the repl_* parameters in your configuration script.
Use Cases Limiting the rate messages are processed on a system directly influences the load. The ratelimit module can be used to protect a single host or to protect an OpenSIPS cluster when run on the dispatching box in front. Distributed limiting is useful when the rate limit should be performed not only on a specific node, but on the entire platform. The internal limiting data will no longer be kept on each &osips; instance. It will be stored in a distributed Key-Value database and queried by each instance before deciding if a SIP message should be blocked or not. NOTE: that this behavior only makes sense when the pipe algorithm used is TAILDROP or RED. A sample configuration snippet might look like this: ... if (!rl_check("$rU", "50", "TAILDROP")) { sl_send_reply("503", "Server Unavailable"); exit; }; ... Upon every incoming request listed above rl_check is invoked and the entity identified by the R-URI user is checked. It returns an OK code if the current per request load is below the configured threshold. If the load is exceeded the function returns an error and an administrator can discard requests with a stateless response.
Static Rate Limiting Algorithms The ratelimit module supports two different static algorithms to be used by rl_check to determine whether a message should be blocked or not.
Tail Drop Algorithm (TAILDROP) This is a trivial algorithm that imposes some risks when used in conjunction with long timer intervals. At the start of each interval an internal counter is reset and incremented for each incoming message. Once the counter hits the configured limit rl_check returns an error. The downside of this algorithm is that it can lead to SIP client synchronization. During a relatively long interval only the first requests (i.e. REGISTERs) would make it through. Following messages (i.e. RE-REGISTERs) will all hit the SIP proxy at the same time when a common Expire timer expired. Other requests will be retransmissed after given time, the same on all devices with the same firmware/by the same vendor.
Random Early Detection Algorithm (RED) Random Early Detection tries to circumvent the synchronization problem imposed by the tail drop algorithm by measuring the average load and adapting the drop rate dynamically. When running with the RED algorithm OpenSIPS will return errors to the OpenSIPS routing engine every n'th packet trying to evenly spread the measured load of the last timer interval onto the current interval. As a negative side effect OpenSIPS might drop messages although the limit might not be reached within the interval. Decrease the timer interval if you encounter this.
Slot Based Taildropping (SBT) SBT holds a window consisting of one or more slots. You can set the window_size parameter(seconds) which means for how long we should look back to count the calls and slot_period parameter(miliseconds) which tells how granular the algorithm should be. The number of slots will be window_size/slot_period. If, for example, you have window_size= slot_period=1 second, then after each second you shall lose the call count, but if you set the slot_period to 100 microseconds, then when your call will be outside the window, the calls in the first 100 microseconds shall be dropped, and the rest in the next 900 shall be kept.
Network Algorithm (NETWORK) This algorithm relies on information provided by network interfaces. The total amount of bytes waiting to be consumed on all the network interfaces is retrieved once every timer_interval seconds. If the returned amount exceeds the limit specified in the modparam, rl_check returns an error.
Dynamic Rate Limiting Algorithms When running &osips; on different machines, one has to adjust the drop rates for the static algorithms to maintain a sub 100% load average or packets start getting dropped in the network stack. While this is not in itself difficult, it isn't neither accurate nor trivial: another server taking a notable fraction of the cpu time will require re-tuning the parameters. While tuning the drop rates from the outside based on a certain factor is possible, having the algorithm run inside ratelimit permits tuning the rates based on internal server parameters and is somewhat more flexible (or it will be when support for external load factors - as opposed to cpu load - is added).
Feedback Algorithm (FEEDBACK) Using the PID Controller model (see Wikipedia page), the drop rate is adjusted dynamically based on the load factor so that the load factor always drifts towards the specified limit (or setpoint, in PID terms). As reading the cpu load average is relatively expensive (opening /proc/stat, parsing it, etc), this only happens once every timer_interval seconds and consequently the FEEDBACK value is only at these intervals recomputed. This in turn makes it difficult for the drop rate to adjust quickly. Worst case scenarios are request rates going up/down instantly by thousands - it takes up to 20 seconds for the controller to adapt to the new request rate. Generally though, as real life request rates drift by less, adapting should happen much faster.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>timer_interval</varname> (integer) The initial length of a timer interval in seconds. All amounts of messages have to be divided by this timer to get a messages per second value. IMPORTANT: A too small value may lead to performance penalties due to timer process overloading. Default value is 10. Set <varname>timer_interval</varname> parameter ... modparam("ratelimit", "timer_interval", 5) ...
<varname>expire_time</varname> (integer) This parameter specifies how long a pipe should be kept in memory until deleted. Default value is 3600. Set <varname>expire_time</varname> parameter ... modparam("ratelimit", "expire_time", 1800) ...
<varname>hash_size</varname> (integer) The size of the hash table internally used to keep the pipes. A larger table is much faster but consumes more memory. The hash size must be a power of 2 number. Default value is 1024. Set <varname>hash_size</varname> parameter ... modparam("ratelimit", "hash_size", 512) ...
<varname>default_algorithm</varname> (string) Specifies which algorithm should be assumed in case it isn't explicitly specified in the rl_check function. Default value is "TAILDROP". Set <varname>default_algorithm</varname> parameter ... modparam("ratelimit", "default_algorithm", "RED") ...
<varname>cachedb_url</varname> (string) Enables distributed rate limiting and specifies the backend that should be used by the CacheDB interface. Default value is "disabled". Set <varname>cachedb_url</varname> parameter ... modparam("ratelimit", "cachedb_url", "redis://root:root@127.0.0.1/") ...
<varname>db_prefix</varname> (string) Specifies what prefix should be added to the pipe name. This is only used when distributed rate limiting is enabled. Default value is "rl_pipe_". Set <varname>db_prefix</varname> parameter ... modparam("ratelimit", "db_prefix", "ratelimit_") ...
<varname>repl_buffer_threshold</varname> (string) Used to specify the length of the buffer used by the binary replication, in bytes. Usually this should be big enough to hold as much data as possible, but small enough to avoid UDP fragmentation. The recommended value is the smallest MTU between all the replication instances. Default value is 1400 bytes. Set <varname>repl_buffer_threshold</varname> parameter ... modparam("ratelimit", "repl_buffer_threshold", 500) ...
<varname>repl_timer_interval</varname> (string) Timer in milliseconds, used to specify how often the module should replicate its counters to the other instances. Default value is 10 ms. Set <varname>repl_timer_interval</varname> parameter ... modparam("ratelimit", "repl_timer_interval", 100) ...
<varname>repl_timer_expire</varname> (string) Timer in seconds, used to specify when the counter received from a different instance should no longer be taken into account. This is used to prevent obsolete values, in case an instance stops replicating its counters. Default value is 10 s. Set <varname>repl_timer_expire</varname> parameter ... modparam("ratelimit", "repl_timer_expire", 10) ...
<varname>replicate_pipes_to</varname> (integer) Used to specify the instances, that belong to a certain cluster, where the pipes should be replicated. Default value is 0. (no replication destinations) Set <varname>replicate_pipes_to</varname> parameter ... modparam("ratelimit", "replicate_pipes_to", 1) ...
<varname>accept_pipes_from</varname> (integer) Used to specify the instances, that belong to a certain cluster, from which we should expect incoming packets. Default value is 0. (disabled) Set <varname>accept_pipes_from</varname> parameter ... modparam("ratelimit", "accept_pipes_from", 1) ...
<varname>accept_pipes_timeout</varname> (integer) The time between two succesive incoming packets. Default value is 10. Set <varname>accept_pipes_timeout</varname> parameter ... modparam("ratelimit", "accept_pipes_timeout", 1) ...
<varname>repl_pipes_auth_check</varname> (int) Authentication check for incoming packets. Default value is 0 (disabled). Set <varname>repl_pipes_auth_check</varname> parameter ... modparam("dialog", "repl_pipes_auth_check", 1) ...
<varname>window_size</varname> (int) How long the history in SBT should be in seconds. Default value is 10. Set <varname>window_size</varname> parameter ... modparam("dialog", "window_size", 5) ...
<varname>slot_period</varname> (int) Value of one slot in microseconds. This parameter determines how granular the algorithm should be. The number of slots will be determined by window_size/slot_period. Default value is 200. Set <varname>slot_period</varname> parameter ... modparam("dialog", "window_size", 5) #we will have 50 slots of 100 microseconds modparam("dialog", "slot_period", 100) ...
Exported Functions
<function moreinfo="none">rl_check(name, limit[, algorithm]) </function> Check the current request against the pipe identified by name and changes/updates the limit. If no pipe is found, then a new one is created with the specified limit and algorithm, if specified. If the algorithm parameter doesn't exist, the default one is used. NOTE: A pipe's algorithm cannot be dynamically changed. Only the one specified when the pipe was created will be considered. The method will return an error code if the limit for the matched pipe is reached. Meaning of the parameters is as follows: name - this is the name that identifies the pipe which should be checked. This parameter accepts both strings and pseudovariables. limit - this specifies the threshold limit of the pipe. It is strongly related to the algorithm used. This parameter accepts an integer or a pseudovariable. Note that the limit should be specified as per-second, not per-timer_interval. algorithm - this is parameter is optional and reffers to the algorithm used to check the pipe. If it is not set, the default value is used. It accepts a string or a pseudovariable. This function can be used from REQUEST_ROUTE. <function>rl_check</function> usage ... # perform a pipe match for all INVITE methods using RED algorithm if (is_method("INVITE")) { if (!rl_check("pipe_INVITE", "100", "RED")) { sl_send_reply("503", "Server Unavailable"); exit; }; }; ... # use default algorithm for each different gateway $var(limit) = 10; if (!rl_check("gw_$ru", "$var(limit)")) { sl_send_reply("503", "Server Unavailable"); exit; }; ...
<function moreinfo="none">rl_dec_count(name)</function> This function decreases a counter that could have been previously increased by rl_check function. Meaning of the parameters is as follows: name - identifies the name of the pipe. This function can be used from REQUEST_ROUTE. <function>rl_dec_count</function> usage ... if (!rl_check("gw_$ru", "100", "TAILDROP")) { exit; } else { rl_dec_count("gw_$ru"); }; ...
<function moreinfo="none">rl_reset_count(name)</function> This function resets a counter that could have been previously increased by rl_check function. Meaning of the parameters is as follows: name - identifies the name of the pipe. This function can be used from REQUEST_ROUTE. <function>rl_reset_count</function> usage ... if (!rl_check("gw_$ru", "100", "TAILDROP")) { exit; } else { rl_reset_count("gw_$ru"); }; ...
Exported MI Functions
<function moreinfo="none">rl_list</function> Lists the parameters and variabiles in the ratelimit module. Name: rl_list Parameters: pipe - indicates the name of the pipe. This parameter is optional. If it doesn't exist, all the active pipes are listed. Otherwise only the one specified. MI FIFO Command Format: :rl_list:_reply_fifo_file_ gw_10.0.0.1 _empty_line_ :rl_list:_reply_fifo_file_ _empty_line_
<function moreinfo="none">rl_reset_pipe</function> Resets the counter of a specified pipe. Name: rl_reset_pipe Parameters: pipe - indicates the name of the pipe whose couter should be reset. MI FIFO Command Format: :rl_reset_pipe:_reply_fifo_file_ gw_10.0.0.1 _empty_line_
<function moreinfo="none">rl_set_pid</function> Sets the PID Controller parameters for the Feedback Algorithm. Name: rl_set_pid Parameters: ki - the integral parameter. kp - the proportional parameter. kd - the derivative parameter. MI FIFO Command Format: :rl_set_pid:_reply_fifo_file_ 0.5 0.5 0.5 _empty_line_
<function moreinfo="none">rl_get_pid</function> Gets the list of in use PID Controller parameters. Name: rl_get_pid Parameters: none MI FIFO Command Format: :rl_get_pid:_reply_fifo_file_ _empty_line_
<function moreinfo="none">rl_bin_status</function> Dumps each destination used for replication, as well as the timestamp of the last message received from them. Name: rl_bin_status Parameters: none MI FIFO Command Format: :rl_bin_status:_reply_fifo_file_ _empty_line_
Exported pseudo-variables
<varname>$rl_count(name)</varname> Returns the counter of a pipe. The variable is read-only. NULL will be returned if the pipe does not exist.
opensips-2.2.2/modules/ratelimit/ratelimit.c000066400000000000000000000536271300170765700212000ustar00rootroot00000000000000/* * ratelimit module * * Copyright (C) 2006 Hendrik Scholz * Copyright (C) 2008 Ovidiu Sas * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * * 2008-01-10 ported from SER project (osas) * 2008-01-16 ported enhancements from openims project (osas) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../timer.h" #include "../../ut.h" #include "../../locking.h" #include "../../mod_fix.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../socket_info.h" #include "../../bin_interface.h" #include "../signaling/signaling.h" #include "ratelimit.h" /* === these change after startup */ gen_lock_t * rl_lock; static double * rl_load_value; /* actual load, used by PIPE_ALGO_FEEDBACK */ static double * pid_kp, * pid_ki, * pid_kd, * pid_setpoint; /* PID tuning params */ static int * drop_rate; /* updated by PIPE_ALGO_FEEDBACK */ static int *rl_feedback_limit; int * rl_network_load; /* network load */ int * rl_network_count; /* flag for counting network algo users */ /* these only change in the mod_init() process -- no locking needed */ int rl_timer_interval = RL_TIMER_INTERVAL; int accept_repl_pipes = 0; int rl_repl_cluster = 0; int accept_repl_pipes_timeout = 10; int repl_pipes_auth_check = 0; struct clusterer_binds clusterer_api; int rl_window_size=10; /* how many seconds the window shall hold*/ int rl_slot_period=200; /* how many milisecs a slot from the window has */ static str db_url = {0,0}; str db_prefix = str_init("rl_pipe_"); unsigned int rl_repl_timer_expire = RL_TIMER_INTERVAL; static unsigned int rl_repl_timer_interval = RL_TIMER_INTERVAL; /* === */ #ifndef RL_DEBUG_LOCKS # define LOCK_GET lock_get # define LOCK_RELEASE lock_release #else # define LOCK_GET(l) do { \ LM_INFO("%d: + get\n", __LINE__); \ lock_get(l); \ LM_INFO("%d: - get\n", __LINE__); \ } while (0) # define LOCK_RELEASE(l) do { \ LM_INFO("%d: + release\n", __LINE__); \ lock_release(l); \ LM_INFO("%d: - release\n", __LINE__); \ } while (0) #endif /* module functions */ static int mod_init(void); static int mod_child(int); /* fixup prototype */ static int fixup_rl_check(void **param, int param_no); struct mi_root* mi_stats(struct mi_root* cmd_tree, void* param); struct mi_root* mi_reset_pipe(struct mi_root* cmd_tree, void* param); struct mi_root* mi_set_pid(struct mi_root* cmd_tree, void* param); struct mi_root* mi_get_pid(struct mi_root* cmd_tree, void* param); struct mi_root* mi_bin_status(struct mi_root* cmd_tree, void* param); static int pv_get_rl_count(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int pv_parse_rl_count(pv_spec_p sp, str *in); static cmd_export_t cmds[] = { {"rl_check", (cmd_function)w_rl_check_2, 2, fixup_rl_check, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE| BRANCH_ROUTE|ERROR_ROUTE|LOCAL_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"rl_check", (cmd_function)w_rl_check_3, 3, fixup_rl_check, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE| BRANCH_ROUTE|ERROR_ROUTE|LOCAL_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"rl_dec_count", (cmd_function)w_rl_dec, 1, fixup_spve_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE| BRANCH_ROUTE|ERROR_ROUTE|LOCAL_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"rl_reset_count", (cmd_function)w_rl_reset, 1, fixup_spve_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE| BRANCH_ROUTE|ERROR_ROUTE|LOCAL_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[] = { { "timer_interval", INT_PARAM, &rl_timer_interval }, { "expire_time", INT_PARAM, &rl_expire_time }, { "hash_size", INT_PARAM, &rl_hash_size }, { "default_algorithm", STR_PARAM, &rl_default_algo_s.s }, { "cachedb_url", STR_PARAM, &db_url.s }, { "db_prefix", STR_PARAM, &db_prefix.s }, { "repl_buffer_threshold", INT_PARAM, &rl_buffer_th }, { "repl_timer_interval", INT_PARAM, &rl_repl_timer_interval }, { "repl_timer_expire", INT_PARAM, &rl_repl_timer_expire }, { "accept_pipes_from", INT_PARAM, &accept_repl_pipes }, { "replicate_pipes_to", INT_PARAM, &rl_repl_cluster }, { "accept_pipes_timeout", INT_PARAM, &accept_repl_pipes_timeout }, { "repl_pipes_auth_check", INT_PARAM, &repl_pipes_auth_check }, { "window_size", INT_PARAM, &rl_window_size}, { "slot_period", INT_PARAM, &rl_slot_period}, { 0, 0, 0} }; #define RLH1 "Params: [pipe] ; Lists the parameters and variabiles in the " \ "ratelimit module; If no pipe is specified, all existing pipes are listed." #define RLH2 "Params: pipe ; Resets the counter of a specified pipe." #define RLH3 "Params: ki kp kd ; Sets the PID Controller parameters for the " \ "Feedback Algorithm." #define RLH4 "Params: none ; Gets the list of in use PID Controller parameters." #define RLH5 "Params: none ; Shows the status of the other SIP instances." static mi_export_t mi_cmds [] = { {"rl_list", RLH1, mi_stats, 0, 0, 0}, {"rl_reset_pipe", RLH2, mi_reset_pipe, 0, 0, 0}, {"rl_set_pid", RLH3, mi_set_pid, 0, 0, 0}, {"rl_get_pid", RLH4, mi_get_pid, MI_NO_INPUT_FLAG, 0, 0}, {"rl_bin_status", RLH5, mi_bin_status, MI_NO_INPUT_FLAG, 0, 0}, {0,0,0,0,0,0} }; static pv_export_t mod_items[] = { { {"rl_count", sizeof("rl_count")-1}, 1010, pv_get_rl_count, 0, pv_parse_rl_count, 0, 0, 0 }, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; static module_dependency_t *get_deps_clusterer(param_export_t *param) { int cluster_id = *(int *)param->param_pointer; if (cluster_id <= 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "clusterer", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "replicate_pipes_to", get_deps_clusterer }, { "accept_pipes_from", get_deps_clusterer }, { NULL, NULL }, }, }; struct module_exports exports= { "ratelimit", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, NULL, params, 0, /* exported statistics */ mi_cmds, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, mod_destroy, /* module exit function */ mod_child /* per-child init function */ }; /* not using /proc/loadavg because it only works when our_timer_interval == theirs */ int get_cpuload(void) { static long long o_user, o_nice, o_sys, o_idle, o_iow, o_irq, o_sirq, o_stl; long long n_user, n_nice, n_sys, n_idle, n_iow, n_irq, n_sirq, n_stl; static int first_time = 1; int scan_res; FILE * f = fopen("/proc/stat", "r"); if (! f) return -1; scan_res = fscanf(f, "cpu %lld%lld%lld%lld%lld%lld%lld%lld", &n_user, &n_nice, &n_sys, &n_idle, &n_iow, &n_irq, &n_sirq, &n_stl); fclose(f); if (scan_res <= 0) { LM_ERR("/proc/stat didn't contain expected values"); return -1; } if (first_time) { first_time = 0; *rl_load_value = 0; } else { long long d_total = (n_user - o_user) + (n_nice - o_nice) + (n_sys - o_sys) + (n_idle - o_idle) + (n_iow - o_iow) + (n_irq - o_irq) + (n_sirq - o_sirq) + (n_stl - o_stl); long long d_idle = (n_idle - o_idle); *rl_load_value = 1.0 - ((double)d_idle) / (double)d_total; } o_user = n_user; o_nice = n_nice; o_sys = n_sys; o_idle = n_idle; o_iow = n_iow; o_irq = n_irq; o_sirq = n_sirq; o_stl = n_stl; return 0; } static double int_err = 0.0; static double last_err = 0.0; void pid_setpoint_limit(int limit) { *pid_setpoint = 0.01 * (double)limit; } /* (*load_value) is expected to be in the 0.0 - 1.0 range * (expects rl_lock to be taken) */ void do_update_load(void) { double err, dif_err, output; /* PID update */ err = *pid_setpoint - *rl_load_value; dif_err = err - last_err; /* * TODO?: the 'if' is needed so low cpu loads for * long periods (which can't be compensated by * negative drop rates) don't confuse the controller * * NB: - "err < 0" means "desired_cpuload < actual_cpuload" * - int_err is integral(err) over time */ if (int_err < 0 || err < 0) int_err += err; output = (*pid_kp) * err + (*pid_ki) * int_err + (*pid_kd) * dif_err; last_err = err; *drop_rate = (output > 0) ? output : 0; } #define RL_SHM_MALLOC(_p, _s) \ do { \ _p = shm_malloc((_s)); \ if (!_p) { \ LM_ERR("no more shm memory\n"); \ return -1; \ } \ memset(_p, 0, (_s)); \ } while (0) #define RL_SHM_FREE(_p) \ do { \ if (_p) { \ shm_free(_p); \ _p = 0; \ } \ } while (0) /* initialize ratelimit module */ static int mod_init(void) { unsigned int n; LM_INFO("Ratelimit module - initializing ...\n"); if (rl_timer_interval < 0) { LM_ERR("invalid timer interval\n"); return -1; } if (rl_expire_time < 0) { LM_ERR("invalid expire time\n"); return -1; } if (rl_repl_cluster < 0) rl_repl_cluster = 0; if (accept_repl_pipes < 0) accept_repl_pipes = 0; if (accept_repl_pipes_timeout <= 0) accept_repl_pipes_timeout = 10; if (repl_pipes_auth_check < 0) repl_pipes_auth_check = 0; if ( (rl_repl_cluster || accept_repl_pipes) && load_clusterer_api(&clusterer_api) != 0 ){ LM_DBG("failed to find clusterer API - is clusterer module loaded?\n"); return -1; } if (db_url.s) { db_url.len = strlen(db_url.s); db_prefix.len = strlen(db_prefix.s); LM_DBG("using CacheDB url: %s\n", db_url.s); } RL_SHM_MALLOC(rl_network_count, sizeof(int)); RL_SHM_MALLOC(rl_network_load, sizeof(int)); RL_SHM_MALLOC(rl_load_value, sizeof(double)); RL_SHM_MALLOC(pid_kp, sizeof(double)); RL_SHM_MALLOC(pid_ki, sizeof(double)); RL_SHM_MALLOC(pid_kd, sizeof(double)); RL_SHM_MALLOC(pid_setpoint, sizeof(double)); RL_SHM_MALLOC(drop_rate, sizeof(int)); RL_SHM_MALLOC(rl_feedback_limit, sizeof(int)); /* init ki value for feedback algo */ *pid_ki = -25.0; rl_lock = lock_alloc(); if (!rl_lock) { LM_ERR("cannot alloc lock\n"); return -1; } if (!lock_init(rl_lock)) { LM_ERR("failed to init lock\n"); return -1; } /* register timer to reset counters */ if (register_timer("rl-timer", rl_timer, NULL, rl_timer_interval, TIMER_FLAG_DELAY_ON_DELAY) < 0 ) { LM_ERR("could not register timer function\n"); return -1; } if(rl_repl_cluster) if (register_utimer("rl-utimer", rl_timer_repl, NULL, rl_repl_timer_interval * 1000, TIMER_FLAG_DELAY_ON_DELAY) < 0) { LM_ERR("failed to register utimer\n"); return -1; } /* if db_url is not used */ for( n=0 ; n < 8 * sizeof(unsigned int) ; n++) { if (rl_hash_size==(1< rounding from %d to %d\n", rl_hash_size, 1<<(n-1)); rl_hash_size = 1<<(n-1); } } if (init_rl_table(rl_hash_size) < 0) { LM_ERR("cannot allocate the table\n"); return -1; } if (rl_repl_init() < 0) { LM_ERR("cannot init bin replication\n"); return -1; } return 0; } static int mod_child(int rank) { /* init the cachedb */ if (db_url.s && db_url.len) return init_cachedb(&db_url); LM_DBG("db_url not set - using standard behaviour\n"); return 0; } void mod_destroy(void) { unsigned int i; if (rl_htable.maps) { for (i = 0; i < rl_htable.size; i++) map_destroy(rl_htable.maps[i], 0); shm_free(rl_htable.maps); rl_htable.maps = 0; rl_htable.size = 0; } if (rl_htable.locks) { lock_set_destroy(rl_htable.locks); lock_set_dealloc(rl_htable.locks); rl_htable.locks = 0; rl_htable.locks_no = 0; } if (rl_lock) { lock_destroy(rl_lock); lock_dealloc(rl_lock); } RL_SHM_FREE(rl_network_count); RL_SHM_FREE(rl_network_load); RL_SHM_FREE(rl_load_value); RL_SHM_FREE(pid_kp); RL_SHM_FREE(pid_ki); RL_SHM_FREE(pid_kd); RL_SHM_FREE(pid_setpoint); RL_SHM_FREE(drop_rate); RL_SHM_FREE(rl_feedback_limit); if (db_url.s && db_url.len) destroy_cachedb(); } /* this is here to avoid using rand() ... which doesn't _always_ return * exactly what we want (see NOTES section in 'man 3 rand') */ int hash[100] = {18, 50, 51, 39, 49, 68, 8, 78, 61, 75, 53, 32, 45, 77, 31, 12, 26, 10, 37, 99, 29, 0, 52, 82, 91, 22, 7, 42, 87, 43, 73, 86, 70, 69, 13, 60, 24, 25, 6, 93, 96, 97, 84, 47, 79, 64, 90, 81, 4, 15, 63, 44, 57, 40, 21, 28, 46, 94, 35, 58, 11, 30, 3, 20, 41, 74, 34, 88, 62, 54, 33, 92, 76, 85, 5, 72, 9, 83, 56, 17, 95, 55, 80, 98, 66, 14, 16, 38, 71, 23, 2, 67, 36, 65, 27, 1, 19, 59, 89, 48}; /** * the algorithm keeps a circular window of requests in a fixed size buffer * * @param pipe containing the window * @param update whether or not to inc call number * @return number of calls in the window */ static inline int hist_check(rl_pipe_t *pipe) { #define U2MILI(__usec__) (__usec__/1000) #define S2MILI(__sec__) (__sec__ *1000) int i; int count; int first_good_index; int rl_win_ms = rl_window_size * 1000; unsigned long long now_total, start_total; struct timeval tv; /* first get values from our beloved replicated friends * current pipe counter will be calculated after this * iteration; no need for the old one */ pipe->counter=0; count = rl_get_all_counters(pipe); gettimeofday(&tv, NULL); if (pipe->rwin.start_time.tv_sec == 0) { /* the lucky one to come first here */ pipe->rwin.start_time = tv; pipe->rwin.start_index = 0; /* we know it starts from 0 because we did memset when created*/ pipe->rwin.window[pipe->rwin.start_index]++; } else { start_total = S2MILI(pipe->rwin.start_time.tv_sec) + U2MILI(pipe->rwin.start_time.tv_usec); now_total = S2MILI(tv.tv_sec) + U2MILI(tv.tv_usec); /* didn't do any update to the window for "2*window_size" secs * we can't use any elements from the vector * the window is invalidated; very unlikely to happen*/ if (now_total - start_total >= 2*rl_win_ms) { memset(pipe->rwin.window, 0, pipe->rwin.window_size * sizeof(long int)); pipe->rwin.start_index = 0; pipe->rwin.start_time = tv; pipe->rwin.window[pipe->rwin.start_index]++; } else if (now_total - start_total >= rl_win_ms) { /* current time in interval [window_size; 2*window_size) * all the elements in [start_time; (ctime-window_size+1) are * invalidated(set to 0) * */ /* the first window index not to be set to 0 * number of slots from the start_index*/ first_good_index = ((((now_total - rl_win_ms) - start_total) /rl_slot_period + 1) + pipe->rwin.start_index) % pipe->rwin.window_size; /* the new start time will be the start time of the first slot */ start_total = (now_total - rl_win_ms) - (now_total - rl_win_ms)%rl_slot_period+ rl_slot_period; pipe->rwin.start_time.tv_sec = start_total/1000; pipe->rwin.start_time.tv_usec = (start_total%1000)*1000; for (i=pipe->rwin.start_index; i != first_good_index; i=(i+1)%pipe->rwin.window_size) pipe->rwin.window[i] = 0; pipe->rwin.start_index = first_good_index; /* count current call; it will be the last element in the window */ pipe->rwin.window[(pipe->rwin.start_index) + (pipe->rwin.window_size-1) % pipe->rwin.window_size]++; } else { /* now_total - start_total < rl_win_ms */ /* no need to modify the window, the value is inside it; * we just need to increment the number of calls for * the current slot*/ pipe->rwin.window[(now_total-start_total)/rl_slot_period]++; } } /* count the total number of calls in the window */ for (i=0; i < pipe->rwin.window_size; i++) pipe->counter += pipe->rwin.window[i]; count += pipe->counter; return count > pipe->limit ? -1 : 1; #undef U2MILI #undef S2MILI } /** * runs the pipe's algorithm * (expects rl_lock to be taken) * \return -1 if drop needed, 1 if allowed */ int rl_pipe_check(rl_pipe_t *pipe) { unsigned counter = rl_get_all_counters(pipe); switch (pipe->algo) { case PIPE_ALGO_NOP: LM_ERR("no algorithm defined for this pipe\n"); return 1; case PIPE_ALGO_TAILDROP: return (counter <= pipe->limit * rl_timer_interval) ? 1 : -1; case PIPE_ALGO_RED: if (!pipe->load) return 1; return counter % pipe->load ? -1 : 1; case PIPE_ALGO_NETWORK: return pipe->load; case PIPE_ALGO_FEEDBACK: return (hash[counter % 100] < *drop_rate) ? -1 : 1; case PIPE_ALGO_HISTORY: return hist_check(pipe); default: LM_ERR("ratelimit algorithm %d not implemented\n", pipe->algo); } return 1; } /* * MI functions * * mi_stats() dumps the current config/statistics */ /* mi function implementations */ struct mi_root* mi_stats(struct mi_root* cmd_tree, void* param) { struct mi_root *rpl_tree; struct mi_node *node=NULL, *rpl=NULL; struct mi_attr *attr; int len; char * p; node = cmd_tree->node.kids; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; if (rl_stats(rpl_tree, &node->value)) { LM_ERR("cannot mi print values\n"); goto free; } if (!(node = add_mi_node_child(rpl, 0, "PIPE", 4, NULL, 0))) goto free; LOCK_GET(rl_lock); p = int2str((unsigned long)(*drop_rate), &len); if (!(attr = add_mi_attr(node, MI_DUP_VALUE, "drop_rate", 9, p, len))) { LOCK_RELEASE(rl_lock); goto free; } LOCK_RELEASE(rl_lock); return rpl_tree; free: free_mi_tree(rpl_tree); return 0; } struct mi_root* mi_set_pid(struct mi_root* cmd_tree, void* param) { struct mi_node *node; char buf[5]; int rl_ki, rl_kp, rl_kd; if (!(node = cmd_tree->node.kids)) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if ( !node->value.s || !node->value.len || node->value.len >= 5) goto bad_syntax; memcpy(buf, node->value.s, node->value.len); buf[node->value.len] = '\0'; rl_ki = strtod(buf, NULL); node = node->next; if ( !node->value.s || !node->value.len || node->value.len >= 5) goto bad_syntax; memcpy(buf, node->value.s, node->value.len); buf[node->value.len] = '\0'; rl_kp = strtod(buf, NULL); node = node->next; if ( !node->value.s || !node->value.len || node->value.len >= 5) goto bad_syntax; memcpy(buf, node->value.s, node->value.len); buf[node->value.len] = '\0'; rl_kd = strtod(buf, NULL); LOCK_GET(rl_lock); *pid_ki = rl_ki; *pid_kp = rl_kp; *pid_kd = rl_kd; LOCK_RELEASE(rl_lock); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); bad_syntax: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } struct mi_root* mi_get_pid(struct mi_root* cmd_tree, void* param) { struct mi_root *rpl_tree; struct mi_node *node=NULL, *rpl=NULL; struct mi_attr* attr; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; node = add_mi_node_child(rpl, 0, "PID", 3, 0, 0); if(node == NULL) goto error; LOCK_GET(rl_lock); attr= addf_mi_attr(node, 0, "ki", 2, "%0.3f", *pid_ki); if(attr == NULL) goto error; attr= addf_mi_attr(node, 0, "kp", 2, "%0.3f", *pid_kp); if(attr == NULL) goto error; attr= addf_mi_attr(node, 0, "kd", 2, "%0.3f", *pid_kd); LOCK_RELEASE(rl_lock); if(attr == NULL) goto error; return rpl_tree; error: LOCK_RELEASE(rl_lock); LM_ERR("Unable to create reply\n"); free_mi_tree(rpl_tree); return 0; } struct mi_root* mi_reset_pipe(struct mi_root* cmd_tree, void* param) { struct mi_node *node; if (!(node = cmd_tree->node.kids)) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if (w_rl_set_count(node->value, 0)) return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } struct mi_root* mi_bin_status(struct mi_root* cmd_tree, void* param) { struct mi_root *rpl_tree; struct mi_node *rpl=NULL; rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; if (rl_bin_status(rpl_tree) < 0) { LM_ERR("cannot print status\n"); goto free; } return rpl_tree; free: free_mi_tree(rpl_tree); return 0; } /* fixup functions */ static int fixup_rl_check(void **param, int param_no) { switch (param_no) { /* pipe name */ case 1: return fixup_spve(param); /* limit */ case 2: return fixup_igp(param); /* algorithm */ case 3: return fixup_sgp(param); /* error */ default: LM_ERR("[BUG] too many params (%d)\n", param_no); } return E_UNSPEC; } /* pseudo-variable functions */ static int pv_get_rl_count(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int counter; if (!param) return pv_get_null(msg, param, res); if(pv_get_spec_name(msg, param, res)!=0 || (!(res->flags&PV_VAL_STR))) { LM_ERR("invalid name\n"); return -1; } counter = rl_get_counter_value(&res->rs); if (counter < 0) { LM_ERR("Cannot get counter's value\n"); return pv_get_null(msg, param, res); } return pv_get_uintval(msg, param, res, counter); } static int pv_parse_rl_count(pv_spec_p sp, str *in) { char *p; char *s; pv_spec_p nsp = 0; if(in==NULL || in->s==NULL || sp==NULL) return -1; p = in->s; if(*p==PV_MARKER) { nsp = (pv_spec_p)pkg_malloc(sizeof(pv_spec_t)); if(nsp==NULL) { LM_ERR("no more memory\n"); return -1; } s = pv_parse_spec(in, nsp); if(s==NULL) { LM_ERR("invalid name [%.*s]\n", in->len, in->s); pv_spec_free(nsp); return -1; } sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void*)nsp; return 0; } sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.name.s = *in; return 0; } opensips-2.2.2/modules/ratelimit/ratelimit.h000066400000000000000000000102111300170765700211630ustar00rootroot00000000000000/* * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-26 created (razvancrainea) */ #ifndef _RATELIMIT_H_ #define _RATELIMIT_H_ #define RL_DEFAULT_EXPIRE 3600 #define RL_HASHSIZE 1024 #define RL_TIMER_INTERVAL 10 #define RL_PIPE_PENDING (1<<0) #define BIN_VERSION 1 #include "../../map.h" #include "../clusterer/api.h" #include "../../forward.h" /* copied from old ratelimit module */ typedef enum { PIPE_ALGO_NOP = 0, PIPE_ALGO_TAILDROP, PIPE_ALGO_RED, PIPE_ALGO_FEEDBACK, PIPE_ALGO_NETWORK, PIPE_ALGO_HISTORY } rl_algo_t; typedef struct rl_repl_counter { int counter; time_t update; int machine_id; struct rl_repl_counter *next; } rl_repl_counter_t; typedef struct rl_window { int window_size; /* how big the window array is */ int start_index; /* where the window starts; the window uses * a circular buffer so we will need to know * where is the start of the buffer */ struct timeval start_time; /* time from where the window starts */ long int *window; /* actual array of messages */ } rl_window_t; typedef struct rl_pipe { int limit; /* limit used by algorithm */ int counter; /* countes the accesses */ int my_counter; /* countes the accesses of this instance */ int my_last_counter; /* countes the last accesses of this instance */ int last_counter; /* last counter */ int load; /* countes the accesses */ rl_algo_t algo; /* the algorithm used */ unsigned long last_used; /* timestamp when the pipe was last accessed */ rl_repl_counter_t *dsts; /* counters per destination */ rl_window_t rwin; /* window of requests */ } rl_pipe_t; typedef struct rl_repl_dst { int id; str dst; time_t *last_msg; union sockaddr_union to; } rl_repl_dst_t; /* big hashtable */ typedef struct { unsigned int size; map_t * maps; gen_lock_set_t *locks; unsigned int locks_no; } rl_big_htable; extern gen_lock_t * rl_lock; extern rl_big_htable rl_htable; extern int rl_timer_interval; extern int rl_expire_time; extern unsigned int rl_hash_size; extern int *rl_network_count; extern int *rl_network_load; extern str rl_default_algo_s; extern str db_prefix; extern int accept_repl_pipes; extern int accept_repl_pipes_timeout; extern int repl_pipes_auth_check; extern int rl_repl_cluster; extern int rl_window_size; extern int rl_slot_period; extern struct clusterer_binds clusterer_api; /* helper funcs */ void mod_destroy(void); int init_rl_table(unsigned int size); /* exported functions */ int w_rl_check_2(struct sip_msg*, char *, char *); int w_rl_check_3(struct sip_msg*, char *, char *, char *); int w_rl_dec(struct sip_msg*, char *); int w_rl_reset(struct sip_msg*, char *); int w_rl_set_count(str, int); int rl_stats(struct mi_root *, str *); int rl_pipe_check(rl_pipe_t *); int rl_get_counter_value(str *); /* update load */ int get_cpuload(void); void do_update_load(void); void pid_setpoint_limit(int); /* timer */ void rl_timer(unsigned int, void *); void rl_timer_repl(utime_t, void *); /* cachedb functions */ int init_cachedb(str*); void destroy_cachedb(void); /* bin functions */ extern int rl_buffer_th; extern unsigned int rl_repl_timer_expire; int rl_repl_init(void); int rl_get_all_counters(rl_pipe_t *pipe); int rl_add_repl_dst(modparam_t type, void *val); int rl_bin_status(struct mi_root *); #define RL_PIPE_COUNTER 0 #define RL_EXPIRE_TIMER 10 #define RL_BUF_THRESHOLD 1400 #endif /* _RATELIMIT_H_ */ opensips-2.2.2/modules/ratelimit/ratelimit_helper.c000066400000000000000000000644111300170765700225300ustar00rootroot00000000000000/*: * Copyright (C) 2011 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2011-09-26 created (razvancrainea) */ #include "../../sr_module.h" #include "../../ut.h" #include "../../locking.h" #include "../../mod_fix.h" #include "../../timer.h" #include "../../socket_info.h" #include "../../resolve.h" #include "../../bin_interface.h" #include "../../cachedb/cachedb.h" #include "../../cachedb/cachedb_cap.h" #include "../../forward.h" #include #include #include #include "ratelimit.h" /* parameters */ int rl_expire_time = RL_DEFAULT_EXPIRE; unsigned int rl_hash_size = RL_HASHSIZE; str rl_default_algo_s = str_init("TAILDROP"); static rl_algo_t rl_default_algo = PIPE_ALGO_NOP; /* other functions */ static rl_algo_t get_rl_algo(str); /* big hash table */ rl_big_htable rl_htable; /* feedback algorithm */ static int *rl_feedback_limit; static cachedb_funcs cdbf; static cachedb_con *cdbc = 0; int rl_buffer_th = RL_BUF_THRESHOLD; /* returnes the idex of the pipe in our hash */ #define RL_GET_INDEX(_n) core_hash(&(_n), NULL, rl_htable.size); /* gets the lock associated with the hash index */ #define RL_GET_LOCK(_l) \ lock_set_get(rl_htable.locks, ((_l) % rl_htable.locks_no)) /* releases the lock associated with the hash index */ #define RL_RELEASE_LOCK(_l) \ lock_set_release(rl_htable.locks, ((_l) % rl_htable.locks_no)) /* retrieves the structure associated with the index and key */ #define RL_GET_PIPE(_i, _k) \ (rl_pipe_t **)map_get(rl_htable.maps[(_i)], _k) #define RL_FIND_PIPE(_i, _k) \ (rl_pipe_t **)map_find(rl_htable.maps[(_i)], _k) /* returns true if the pipe should use cachedb interface */ #define RL_USE_CDB(_p) \ (cdbc && (_p)->algo!=PIPE_ALGO_NETWORK && (_p)->algo!=PIPE_ALGO_FEEDBACK) static str rl_name_buffer = {0, 0}; static inline int rl_set_name(str * name) { if (name->len + db_prefix.len > rl_name_buffer.len) { rl_name_buffer.len = name->len + db_prefix.len; rl_name_buffer.s = pkg_realloc(rl_name_buffer.s, rl_name_buffer.len); if (!rl_name_buffer.s) { LM_ERR("cannot realloc buffer\n"); rl_name_buffer.len = 0; return -1; } } memcpy(rl_name_buffer.s + db_prefix.len, name->s, name->len); rl_name_buffer.len = name->len + db_prefix.len; return 0; } /* NOTE: assumes that the pipe has been locked. If fails, releases the lock */ static int rl_change_counter(str *name, rl_pipe_t *pipe, int c) { int new_counter; int ret; if (rl_set_name(name) < 0) return -1; if (pipe->my_counter + c < 0) { LM_DBG("Counter going negative\n"); return 1; } if (c) { if (c < 0) ret = cdbf.sub(cdbc, &rl_name_buffer, -c, rl_expire_time, &new_counter); else ret = cdbf.add(cdbc, &rl_name_buffer, c, rl_expire_time, &new_counter); } else { if (pipe->my_counter) { ret = cdbf.sub(cdbc, &rl_name_buffer, pipe->my_counter, rl_expire_time, &new_counter); } else { ret = cdbf.get_counter(cdbc, &rl_name_buffer, &new_counter); } } if (ret < 0) { LM_ERR("cannot change counter for pipe %.*s with %d\n", name->len, name->s, c); return -1; } pipe->my_counter = c ? pipe->my_counter + c : 0; pipe->counter = new_counter; LM_DBG("changed with %d; my_counter: %d; counter: %d\n", c, pipe->my_counter, new_counter); return 0; } /* NOTE: assumes that the pipe has been locked */ static int rl_get_counter(str *name, rl_pipe_t * pipe) { int new_counter; if (rl_set_name(name) < 0) return -1; if (cdbf.get_counter(cdbc, &rl_name_buffer, &new_counter) < 0) { LM_ERR("cannot retrieve key\n"); return -1; } pipe->counter = new_counter; return 0; } int init_cachedb(str * db_url) { if (cachedb_bind_mod(db_url, &cdbf) < 0) { LM_ERR("cannot bind functions for db_url %.*s\n", db_url->len, db_url->s); return -1; } if (!CACHEDB_CAPABILITY(&cdbf, CACHEDB_CAP_GET | CACHEDB_CAP_ADD | CACHEDB_CAP_SUB)) { LM_ERR("not enough capabilities\n"); return -1; } cdbc = cdbf.init(db_url); if (!cdbc) { LM_ERR("cannot connect to db_url %.*s\n", db_url->len, db_url->s); return -1; } /* guessing that the name is not larger than 32 */ rl_name_buffer.len = db_prefix.len + 32; rl_name_buffer.s = pkg_malloc(rl_name_buffer.len); if (!rl_name_buffer.s) { LM_ERR("no more pkg memory\n"); rl_name_buffer.len = 0; return -1; } /* copy prefix - this is constant*/ memcpy(rl_name_buffer.s, db_prefix.s, db_prefix.len); return 0; } void destroy_cachedb(void) { if (cdbc) cdbf.destroy(cdbc); if (rl_name_buffer.s) pkg_free(rl_name_buffer.s); } int init_rl_table(unsigned int size) { unsigned int i; rl_htable.maps = shm_malloc(sizeof(map_t) * size); if (!rl_htable.maps) { LM_ERR("no more shm memory\n"); return -1; } memset(rl_htable.maps, 0, sizeof(map_t) * size); for (i = 0; i < size; i++) { rl_htable.maps[i] = map_create(AVLMAP_SHARED); if (!rl_htable.maps[i]) { LM_ERR("cannot create map index %d\n", i); goto error; } rl_htable.size++; } if (!rl_default_algo_s.s) { LM_ERR("Default algorithm was not specified\n"); return -1; } rl_default_algo_s.len = strlen(rl_default_algo_s.s); /* resolve the default algorithm */ rl_default_algo = get_rl_algo(rl_default_algo_s); if (rl_default_algo == PIPE_ALGO_NOP) { LM_ERR("unknown algoritm <%.*s>\n", rl_default_algo_s.len, rl_default_algo_s.s); return -1; } LM_DBG("default algorithm is %.*s [ %d ]\n", rl_default_algo_s.len, rl_default_algo_s.s, rl_default_algo); /* if at least 25% of the size locks can't be allocated * we return an error */ for (i = size; i > size / 4; i--) { rl_htable.locks = lock_set_alloc(i); if (!rl_htable.locks) continue; if (!lock_set_init(rl_htable.locks)) { lock_set_dealloc(rl_htable.locks); rl_htable.locks = 0; continue; } break; } if (!rl_htable.locks) { LM_ERR("unable to allocted at least %d locks for the hash table\n", size / 4); goto error; } rl_htable.locks_no = i; LM_DBG("%d locks allocated for %d hashsize\n", rl_htable.locks_no, rl_htable.size); return 0; error: mod_destroy(); return -1; } /* the map between names and algorithms */ struct { str name; rl_algo_t algo; } rl_algo_names[] = { { str_init("NOP"), PIPE_ALGO_NOP}, { str_init("RED"), PIPE_ALGO_RED}, { str_init("TAILDROP"), PIPE_ALGO_TAILDROP}, { str_init("FEEDBACK"), PIPE_ALGO_FEEDBACK}, { str_init("NETWORK"), PIPE_ALGO_NETWORK}, { str_init("SBT"), PIPE_ALGO_HISTORY}, { { 0, 0}, 0 }, }; static rl_algo_t get_rl_algo(str name) { int i; if (!name.s || !name.len) return PIPE_ALGO_NOP; for (i = 0; rl_algo_names[i].name.s; i++) { if (rl_algo_names[i].name.len == name.len && strncasecmp(rl_algo_names[i].name.s, name.s, name.len) == 0) return rl_algo_names[i].algo; } return PIPE_ALGO_NOP; } static str * get_rl_algo_name(rl_algo_t algo) { int i; for (i = 0; rl_algo_names[i].name.s; i++) if (rl_algo_names[i].algo == algo) return &rl_algo_names[i].name; return NULL; } int w_rl_check_2(struct sip_msg *_m, char *_n, char *_l) { return w_rl_check_3(_m, _n, _l, NULL); } int w_rl_check_3(struct sip_msg *_m, char *_n, char *_l, char *_a) { str name; int limit = 0, ret = 1, should_update = 0; str algorithm; unsigned int hash_idx; rl_pipe_t **pipe; rl_algo_t algo = -1; /* retrieve and check parameters */ if (!_n || !_l) { LM_ERR("invalid parameters\n"); goto end; } if (fixup_get_svalue(_m, (gparam_p) _n, &name) < 0) { LM_ERR("cannot retrieve identifier\n"); goto end; } if (fixup_get_ivalue(_m, (gparam_p) _l, &limit) < 0) { LM_ERR("cannot retrieve limit\n"); goto end; } algorithm.s = 0; if (!_a || fixup_get_svalue(_m, (gparam_p) _a, &algorithm) < 0 || (algo = get_rl_algo(algorithm)) == PIPE_ALGO_NOP) { algo = PIPE_ALGO_NOP; } /* get limit for FEEDBACK algorithm */ if (algo == PIPE_ALGO_FEEDBACK) { lock_get(rl_lock); if (*rl_feedback_limit) { if (*rl_feedback_limit != limit) { LM_WARN("FEEDBACK limit should be the same for all pipes, but" " new limit %d differs - setting to %d\n", limit, *rl_feedback_limit); limit = *rl_feedback_limit; } } else { if (limit <= 0 || limit >= 100) { LM_ERR("invalid limit for FEEDBACK algorithm " "(must be between 0 and 100)\n"); lock_release(rl_lock); goto end; } *rl_feedback_limit = limit; pid_setpoint_limit(limit); } lock_release(rl_lock); } hash_idx = RL_GET_INDEX(name); RL_GET_LOCK(hash_idx); /* try to get the value */ pipe = RL_GET_PIPE(hash_idx, name); if (!pipe) { LM_ERR("cannot get the index\n"); goto release; } if (!*pipe) { /* allocate new pipe */ *pipe = shm_malloc(sizeof(rl_pipe_t) + /* memory for the window */ (rl_window_size*1000) / rl_slot_period * sizeof(long int)); if (!*pipe) { LM_ERR("no more shm memory\n"); goto release; } memset(*pipe, 0, sizeof(rl_pipe_t)); LM_DBG("Pipe %.*s doesn't exist, but was created %p\n", name.len, name.s, *pipe); if (algo == PIPE_ALGO_NETWORK) should_update = 1; (*pipe)->algo = (algo == PIPE_ALGO_NOP) ? rl_default_algo : algo; (*pipe)->rwin.window = (long int *)((*pipe) + 1); (*pipe)->rwin.window_size = rl_window_size * 1000 / rl_slot_period; memset((*pipe)->rwin.window, 0, (*pipe)->rwin.window_size * sizeof(long int)); } else { LM_DBG("Pipe %.*s found: %p - last used %lu\n", name.len, name.s, *pipe, (*pipe)->last_used); if (algo != PIPE_ALGO_NOP && (*pipe)->algo != algo) { LM_WARN("algorithm %d different from the initial one %d for pipe " "%.*s", algo, (*pipe)->algo, name.len, name.s); } } /* set/update the limit */ (*pipe)->limit = limit; /* set the last used time */ (*pipe)->last_used = time(0); if (RL_USE_CDB(*pipe)) { /* release the counter for a while */ if (rl_change_counter(&name, *pipe, 1) < 0) { LM_ERR("cannot increase counter\n"); goto release; } } else { (*pipe)->counter++; } ret = rl_pipe_check(*pipe); LM_DBG("Pipe %.*s counter:%d load:%d limit:%d should %sbe blocked (%p)\n", name.len, name.s, (*pipe)->counter, (*pipe)->load, (*pipe)->limit, ret == 1 ? "NOT " : "", *pipe); release: RL_RELEASE_LOCK(hash_idx); if (should_update) { lock_get(rl_lock); (*rl_network_count)++; lock_release(rl_lock); } end: return ret; } /* timer housekeeping, invoked each timer interval to reset counters */ void rl_timer(unsigned int ticks, void *param) { unsigned int i = 0; map_iterator_t it, del; rl_pipe_t **pipe; str *key; void *value; unsigned long now = time(0); /* get CPU load */ if (get_cpuload() < 0) { LM_ERR("cannot update CPU load\n"); i = 1; } lock_get(rl_lock); /* if CPU was successfully loaded */ if (!i) do_update_load(); /* update network if needed */ if (*rl_network_count) *rl_network_load = get_total_bytes_waiting(PROTO_NONE); lock_release(rl_lock); /* iterate through each map */ for (i = 0; i < rl_htable.size; i++) { RL_GET_LOCK(i); /* iterate through all the entries */ if (map_first(rl_htable.maps[i], &it) < 0) { LM_ERR("map doesn't exist\n"); goto next_map; } for (; iterator_is_valid(&it);) { pipe = (rl_pipe_t **) iterator_val(&it); if (!pipe || !*pipe) { LM_ERR("[BUG] bogus map[%d] state\n", i); goto next_pipe; } key = iterator_key(&it); if (!key) { LM_ERR("cannot retrieve pipe key\n"); goto next_pipe; } /* check to see if it is expired */ if ((*pipe)->last_used + rl_expire_time < now) { /* this pipe is engaged in a transaction */ del = it; if (iterator_next(&it) < 0) LM_DBG("cannot find next iterator\n"); if ((*pipe)->algo == PIPE_ALGO_NETWORK) { lock_get(rl_lock); (*rl_network_count)--; lock_release(rl_lock); } LM_DBG("Deleting ratelimit pipe key \"%.*s\"\n", key->len, key->s); value = iterator_delete(&del); /* free resources */ if (value) shm_free(value); continue; } else { /* leave the lock if a cachedb query should be done*/ if (RL_USE_CDB(*pipe)) { if (rl_get_counter(key, *pipe) < 0) { LM_ERR("cannot get pipe counter\n"); goto next_pipe; } } switch ((*pipe)->algo) { case PIPE_ALGO_NETWORK: /* handle network algo */ (*pipe)->load = (*rl_network_load > (*pipe)->limit) ? -1 : 1; break; case PIPE_ALGO_RED: if ((*pipe)->limit && rl_timer_interval) (*pipe)->load = (*pipe)->counter / ((*pipe)->limit * rl_timer_interval); break; default: break; } (*pipe)->my_last_counter = (*pipe)->counter; (*pipe)->last_counter = rl_get_all_counters(*pipe); if (RL_USE_CDB(*pipe)) { if (rl_change_counter(key, *pipe, 0) < 0) { LM_ERR("cannot reset counter\n"); } } else { (*pipe)->counter = 0; } } next_pipe: if (iterator_next(&it) < 0) break; } next_map: RL_RELEASE_LOCK(i); } } struct rl_param_t { int counter; struct mi_node * node; struct mi_root * root; }; static int rl_map_print(void *param, str key, void *value) { struct mi_attr* attr; char* p; int len; struct rl_param_t * rl_param = (struct rl_param_t *) param; struct mi_node * rpl; rl_pipe_t *pipe = (rl_pipe_t *) value; struct mi_node * node; str *alg; if (!pipe) { LM_ERR("invalid pipe value\n"); return -1; } if (!rl_param || !rl_param->node || !rl_param->root) { LM_ERR("no reply node\n"); return -1; } rpl = rl_param->node; if (!key.len || !key.s) { LM_ERR("no key found\n"); return -1; } /* skip if no algo */ if (pipe->algo == PIPE_ALGO_NOP) return 0; if (!(node = add_mi_node_child(rpl, 0, "PIPE", 4, 0, 0))) return -1; if (!(attr = add_mi_attr(node, MI_DUP_VALUE, "id", 2, key.s, key.len))) return -1; if (!(alg = get_rl_algo_name(pipe->algo))) { LM_ERR("[BUG] unknown algorithm %d\n", pipe->algo); return -1; } if (!(attr = add_mi_attr(node, MI_DUP_VALUE, "algorithm", 9, alg->s, alg->len))) return -1; p = int2str((unsigned long) (pipe->limit), &len); if (!(attr = add_mi_attr(node, MI_DUP_VALUE, "limit", 5, p, len))) return -1; p = int2str((unsigned long)(pipe->last_counter), &len); if (!(attr = add_mi_attr(node, MI_DUP_VALUE, "counter", 7, p, len))) return -1; if ((++rl_param->counter % 50) == 0) { LM_DBG("flush mi tree - number %d\n", rl_param->counter); flush_mi_tree(rl_param->root); } return 0; } int rl_stats(struct mi_root *rpl_tree, str * value) { rl_pipe_t **pipe; struct rl_param_t param; int i; memset(¶m, 0, sizeof(struct rl_param_t)); param.node = &rpl_tree->node; param.root = rpl_tree; if (value && value->s && value->len) { i = RL_GET_INDEX(*value); RL_GET_LOCK(i); pipe = RL_FIND_PIPE(i, *value); if (!pipe || !*pipe) { LM_DBG("pipe %.*s not found\n", value->len, value->s); goto error; } if (rl_map_print(¶m, *value, *pipe)) { LM_ERR("cannot print value for key %.*s\n", value->len, value->s); goto error; } RL_RELEASE_LOCK(i); } else { /* iterate through each map */ for (i = 0; i < rl_htable.size; i++) { RL_GET_LOCK(i); if (map_for_each(rl_htable.maps[i], rl_map_print, ¶m)) { LM_ERR("cannot print values\n"); goto error; } RL_RELEASE_LOCK(i); } } return 0; error: RL_RELEASE_LOCK(i); return -1; } static int bin_status_aux(struct mi_node *root, clusterer_node_t *nodes, int type, int cluster_id) { clusterer_node_t *d; struct mi_node *node = NULL; struct mi_attr* attr; str cluster_id_s; str machine_id_s; str state; for (d = nodes; d; d = d->next) { cluster_id_s.s = int2str(cluster_id, &cluster_id_s.len); node = add_mi_node_child(root, MI_DUP_VALUE, "Cluster ID", 10, cluster_id_s.s, cluster_id_s.len); if (node == NULL) goto error; machine_id_s.s = int2str(d->machine_id, &machine_id_s.len); attr = add_mi_attr(node, MI_DUP_VALUE, "Machine ID", 10, machine_id_s.s, machine_id_s.len); if (attr == NULL) goto error; state.s = int2str(d->state, &state.len); attr = add_mi_attr(node, MI_DUP_VALUE, "STATE", 5, state.s, state.len); if (attr == NULL) goto error; if (d->description.s) attr = add_mi_attr(node, MI_DUP_VALUE, "DESCRIPTION", 11, d->description.s, d->description.len); else attr = add_mi_attr(node, MI_DUP_VALUE, "DESCRIPTION", 11, "none", 4); if (attr == NULL) goto error; if (type) attr = add_mi_attr(node, MI_DUP_VALUE, "TYPE", 4, "server", 6); else attr = add_mi_attr(node, MI_DUP_VALUE, "TYPE", 4, "client", 6); if (attr == NULL) goto error; } return 0; error: return -1; } int rl_bin_status(struct mi_root *rpl_tree) { clusterer_node_t *nodes; struct mi_node *root = NULL; if (rpl_tree == NULL) return -1; root = &rpl_tree->node; if (rl_repl_cluster) { nodes = clusterer_api.get_nodes(rl_repl_cluster, PROTO_BIN); if (nodes == NULL) return -1; if (bin_status_aux(root, nodes, 1, rl_repl_cluster) < 0) goto error; clusterer_api.free_nodes(nodes); } if (accept_repl_pipes) { nodes = clusterer_api.get_nodes(accept_repl_pipes, PROTO_BIN); if (nodes == NULL) return -1; if (bin_status_aux(root, nodes, 0, accept_repl_pipes) < 0) goto error; clusterer_api.free_nodes(nodes); } return 0; error: clusterer_api.free_nodes(nodes); return -1; } int w_rl_set_count(str key, int val) { unsigned int hash_idx; int ret = -1; rl_pipe_t **pipe; hash_idx = RL_GET_INDEX(key); RL_GET_LOCK(hash_idx); /* try to get the value */ pipe = RL_FIND_PIPE(hash_idx, key); if (!pipe || !*pipe) { LM_DBG("cannot find any pipe named %.*s\n", key.len, key.s); goto release; } if (RL_USE_CDB(*pipe)) { if (rl_change_counter(&key, *pipe, val) < 0) { LM_ERR("cannot decrease counter\n"); goto release; } } else { if (val && (val + (*pipe)->counter >= 0)) { (*pipe)->counter += val; } else { (*pipe)->counter = 0; } } LM_DBG("new counter for key %.*s is %d\n", key.len, key.s, (*pipe)->counter); ret = 0; release: RL_RELEASE_LOCK(hash_idx); return ret; } static inline int w_rl_change_counter(struct sip_msg *_m, char *_n, int dec) { str name; if (!_n || fixup_get_svalue(_m, (gparam_p) _n, &name) < 0) { LM_ERR("cannot retrieve identifier\n"); return -1; } if (w_rl_set_count(name, dec)) { LM_ERR("cannot find any pipe named %.*s\n", name.len, name.s); return -1; } return 1; } int w_rl_dec(struct sip_msg *_m, char *_n) { return w_rl_change_counter(_m, _n, -1); } int w_rl_reset(struct sip_msg *_m, char *_n) { return w_rl_change_counter(_m, _n, 0); } static rl_repl_counter_t* find_destination(rl_pipe_t *pipe, int machine_id) { rl_repl_counter_t *head; head = pipe->dsts; while(head != NULL){ if( head->machine_id == machine_id ) break; head=head->next; } if(head == NULL){ head = shm_malloc(sizeof(rl_repl_counter_t)); if(head == NULL){ LM_ERR("no more shm memory\n"); goto error; } head->machine_id = machine_id; head->next = pipe->dsts; pipe->dsts = head; } return head; error: return NULL; } void rl_rcv_bin(int packet_type, struct receive_info *ri, int server_id) { rl_algo_t algo; int limit; int counter; str name; char *ip; unsigned short port; rl_pipe_t **pipe; unsigned int hash_idx; time_t now; rl_repl_counter_t *destination; if (packet_type == SERVER_TEMP_DISABLED) { get_su_info(&ri->src_su.s, ip, port); LM_WARN("server: %s:%hu temporary disabled\n", ip, port); return; } if (packet_type == SERVER_TIMEOUT) { LM_INFO("server with clustererer id %d timeout\n", server_id); return; } if(get_bin_pkg_version() != BIN_VERSION){ LM_ERR("incompatible bin protocol version\n"); return; } if (packet_type != RL_PIPE_COUNTER) return; now = time(0); for (;;) { if (bin_pop_str(&name) == 1) break; /* pop'ed all pipes */ if (bin_pop_int(&algo) < 0) { LM_ERR("cannot pop pipe's algorithm\n"); return; } if (bin_pop_int(&limit) < 0) { LM_ERR("cannot pop pipe's limit\n"); return; } if (bin_pop_int(&counter) < 0) { LM_ERR("cannot pop pipe's counter\n"); return; } hash_idx = RL_GET_INDEX(name); RL_GET_LOCK(hash_idx); /* try to get the value */ pipe = RL_GET_PIPE(hash_idx, name); if (!pipe) { LM_ERR("cannot get the index\n"); goto release; } if (!*pipe) { /* if the pipe does not exist, alocate it in case we need it later */ *pipe = shm_malloc(sizeof(rl_pipe_t)); if (!*pipe) { LM_ERR("no more shm memory\n"); goto release; } memset(*pipe, 0, sizeof(rl_pipe_t)); LM_DBG("Pipe %.*s doesn't exist, but was created %p\n", name.len, name.s, *pipe); (*pipe)->algo = algo; (*pipe)->limit = limit; } else { LM_DBG("Pipe %.*s found: %p - last used %lu\n", name.len, name.s, *pipe, (*pipe)->last_used); if ((*pipe)->algo != algo) LM_WARN("algorithm %d different from the initial one %d for " "pipe %.*s", algo, (*pipe)->algo, name.len, name.s); /* * XXX: do not output these warnings since they can be triggered * when a custom limit is used if ((*pipe)->limit != limit) LM_WARN("limit %d different from the initial one %d for " "pipe %.*s", limit, (*pipe)->limit, name.len, name.s); */ } /* set the last used time */ (*pipe)->last_used = time(0); /* set the destination's counter */ destination = find_destination(*pipe, server_id); destination->counter = counter; destination->update = now; RL_RELEASE_LOCK(hash_idx); } return; release: RL_RELEASE_LOCK(hash_idx); } /* * same as hist_check() in ratelimit.c but this one * only counts, no updates on the window ==> faster */ static inline int hist_count(rl_pipe_t *pipe) { /* Window ELement*/ #define U2MILI(__usec__) (__usec__/1000) #define S2MILI(__sec__) (__sec__ *1000) int i; int first_good_index; int rl_win_ms = rl_window_size * 1000; int count=0; unsigned long long now_total, start_total; struct timeval tv; gettimeofday(&tv, NULL); if (pipe->rwin.start_time.tv_sec == 0) { return 0; } else { start_total = S2MILI(pipe->rwin.start_time.tv_sec) + U2MILI(pipe->rwin.start_time.tv_usec); now_total = S2MILI(tv.tv_sec) + U2MILI(tv.tv_usec); if (now_total - start_total >= 2*rl_win_ms) { /* nothing here; window is expired */ } else if (now_total - start_total >= rl_win_ms) { first_good_index = ((((now_total - rl_win_ms) - start_total) /rl_slot_period + 1) + pipe->rwin.start_index) % pipe->rwin.window_size; count = 0; for (i=first_good_index; i != pipe->rwin.start_index; i=(i+1)%pipe->rwin.window_size) count += pipe->rwin.window[i]; } else { /* count all of them; valid window */ for (i=0; i < pipe->rwin.window_size; i++) count += pipe->rwin.window[i]; } } return count; #undef U2MILI #undef S2MILI } int rl_repl_init(void) { if (rl_buffer_th > (BUF_SIZE * 0.9)) { LM_WARN("Buffer size too big %d - pipe information might get lost", rl_buffer_th); return -1; } if (accept_repl_pipes && clusterer_api.register_module("ratelimit", PROTO_BIN, rl_rcv_bin, accept_repl_pipes_timeout, repl_pipes_auth_check, accept_repl_pipes) < 0) { LM_ERR("Cannot register binary packet callback!\n"); return -1; } return 0; } static inline void rl_replicate(void) { if (clusterer_api.send_to(rl_repl_cluster, PROTO_BIN) < 0) { LM_INFO("ratelimit replicate failed\n"); } } void rl_timer_repl(utime_t ticks, void *param) { static str module_name = str_init("ratelimit"); unsigned int i = 0; map_iterator_t it; rl_pipe_t **pipe; str *key; int nr = 0; int ret; int replicated = 0; if (bin_init(&module_name, RL_PIPE_COUNTER, BIN_VERSION) < 0) { LM_ERR("cannot initiate bin buffer\n"); return; } bin_push_int(clusterer_api.get_my_id()); /* iterate through each map */ for (i = 0; i < rl_htable.size; i++) { RL_GET_LOCK(i); /* iterate through all the entries */ if (map_first(rl_htable.maps[i], &it) < 0) { LM_ERR("map doesn't exist\n"); goto next_map; } for (; iterator_is_valid(&it);) { pipe = (rl_pipe_t **) iterator_val(&it); if (!pipe || !*pipe) { LM_ERR("[BUG] bogus map[%d] state\n", i); goto next_pipe; } /* ignore cachedb replicated stuff */ if (RL_USE_CDB(*pipe)) goto next_pipe; key = iterator_key(&it); if (!key) { LM_ERR("cannot retrieve pipe key\n"); goto next_pipe; } if (bin_push_str(key) < 0) goto error; if (bin_push_int((*pipe)->algo) < 0) goto error; if (bin_push_int((*pipe)->limit) < 0) goto error; if ((ret = bin_push_int((*pipe)->my_last_counter)) < 0) goto error; nr++; if (ret > rl_buffer_th) { /* always replicate the pipe to "simulate" pinging */ rl_replicate(); replicated = 1; if (bin_init(&module_name, RL_PIPE_COUNTER, BIN_VERSION) < 0) { LM_ERR("cannot initiate bin buffer\n"); RL_RELEASE_LOCK(i); return; } bin_push_int(clusterer_api.get_my_id()); nr = 0; } next_pipe: if (iterator_next(&it) < 0) break; } next_map: RL_RELEASE_LOCK(i); } /* if there is anything else to send, do it now */ if (!replicated) rl_replicate(); return; error: LM_ERR("cannot add pipe info in buffer\n"); RL_RELEASE_LOCK(i); if (nr) rl_replicate(); } int rl_get_all_counters(rl_pipe_t *pipe) { unsigned counter = 0; time_t now = time(0); rl_repl_counter_t *nodes = pipe->dsts; rl_repl_counter_t *d; for (d = nodes; d; d = d->next) { /* if the replication expired, reset its counter */ if ((d->update + rl_repl_timer_expire) < now) d->counter = 0; counter += d->counter; } return counter + pipe->counter; } int rl_get_counter_value(str *key) { unsigned int hash_idx; rl_pipe_t **pipe; int ret = -1; hash_idx = RL_GET_INDEX(*key); RL_GET_LOCK(hash_idx); /* try to get the value */ pipe = RL_FIND_PIPE(hash_idx, *key); if (!pipe || !*pipe) { LM_DBG("cannot find any pipe named %.*s\n", key->len, key->s); goto release; } if (RL_USE_CDB(*pipe)) { if (rl_get_counter(key, *pipe) < 0) { LM_ERR("cannot get the counter's value\n"); goto release; } } ret = rl_get_all_counters(*pipe); release: RL_RELEASE_LOCK(hash_idx); return ret; } opensips-2.2.2/modules/regex/000077500000000000000000000000001300170765700161455ustar00rootroot00000000000000opensips-2.2.2/modules/regex/Makefile000066400000000000000000000012721300170765700176070ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=regex.so # set CROSS_COMPILE to true if you want to skip # the autodetection # CROSS_COMPILE=true ifeq ($(CROSS_COMPILE),) PCRE_BUILDER = $(shell \ if which pcre-config >/dev/null 2>/dev/null; then \ echo 'pcre-config'; \ elif pkg-config --exists libcre; then \ echo 'pkg-config libpcre'; \ fi) endif ifeq ($(PCRE_BUILDER),) DEFS += -I$(SYSBASE)/include \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/lib \ -L$(LOCALBASE)/lib -lpcre else DEFS += $(shell $(PCRE_BUILDER) --cflags) LIBS += $(shell $(PCRE_BUILDER) --libs) endif include ../../Makefile.modules opensips-2.2.2/modules/regex/README000066400000000000000000000240031300170765700170240ustar00rootroot00000000000000Regex Module Iñaki Baz Castillo Edited by Iñaki Baz Castillo Copyright © 2009 Iñaki Baz Castillo Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. file (string) 1.3.2. max_groups (int) 1.3.3. group_max_size (int) 1.3.4. pcre_caseless (int) 1.3.5. pcre_multiline (int) 1.3.6. pcre_dotall (int) 1.3.7. pcre_extended (int) 1.4. Exported Functions 1.4.1. pcre_match (string, pcre_regex) 1.4.2. pcre_match_group (string [, group]) 1.5. Exported MI Functions 1.5.1. regex_reload 1.6. Installation and Running 1.6.1. File format List of Examples 1.1. Set file parameter 1.2. Set max_groups parameter 1.3. Set group_max_size parameter 1.4. Set pcre_caseless parameter 1.5. Set pcre_multiline parameter 1.6. Set pcre_dotall parameter 1.7. Set pcre_extended parameter 1.8. pcre_match usage (forcing case insensitive) 1.9. pcre_match usage (using "end of line" symbol) 1.10. pcre_match_group usage 1.11. regex file 1.12. Using with pua_usrloc 1.13. Incorrect groups file Chapter 1. Admin Guide 1.1. Overview This module offers matching operations against regular expressions using the powerful PCRE library. A text file containing regular expressions categorized in groups is compiled when the module is loaded, storing the compiled PCRE objects in an array. A function to match a string or pseudo-variable against any of these groups is provided. The text file can be modified and reloaded at any time via a MI command. The module also offers a function to perform a PCRE matching operation against a regular expression provided as function parameter. For a detailed list of PCRE features read the man page of the library. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libpcre-dev - the development libraries of PCRE. 1.3. Exported Parameters 1.3.1. file (string) Text file containing the regular expression groups. It must be set in order to enable the group matching function. Default value is “NULLâ€. Example 1.1. Set file parameter ... modparam("regex", "file", "/etc/opensips/regex_groups") ... 1.3.2. max_groups (int) Max number of regular expression groups in the text file. Default value is “20â€. Example 1.2. Set max_groups parameter ... modparam("regex", "max_groups", 40) ... 1.3.3. group_max_size (int) Max content size of a group in the text file. Default value is “8192â€. Example 1.3. Set group_max_size parameter ... modparam("regex", "group_max_size", 16384) ... 1.3.4. pcre_caseless (int) If this options is set, matching is done caseless. It is equivalent to Perl's /i option, and it can be changed within a pattern by a (?i) or (?-i) option setting. Default value is “0â€. Example 1.4. Set pcre_caseless parameter ... modparam("regex", "pcre_caseless", 1) ... 1.3.5. pcre_multiline (int) By default, PCRE treats the subject string as consisting of a single line of characters (even if it actually contains newlines). The "start of line" metacharacter (^) matches only at the start of the string, while the "end of line" metacharacter ($) matches only at the end of the string, or before a terminating newline. When this option is set, the "start of line" and "end of line" constructs match immediately following or immediately before internal newlines in the subject string, respectively, as well as at the very start and end. This is equivalent to Perl's /m option, and it can be changed within a pattern by a (?m) or (?-m) option setting. If there are no newlines in a subject string, or no occurrences of ^ or $ in a pattern, setting this option has no effect. Default value is “0â€. Example 1.5. Set pcre_multiline parameter ... modparam("regex", "pcre_multiline", 1) ... 1.3.6. pcre_dotall (int) If this option is set, a dot metacharater in the pattern matches all characters, including those that indicate newline. Without it, a dot does not match when the current position is at a newline. This option is equivalent to Perl's /s option, and it can be changed within a pattern by a (?s) or (?-s) option setting. Default value is “0â€. Example 1.6. Set pcre_dotall parameter ... modparam("regex", "pcre_dotall", 1) ... 1.3.7. pcre_extended (int) If this option is set, whitespace data characters in the pattern are totally ignored except when escaped or inside a character class. Whitespace does not include the VT character (code 11). In addition, characters between an unescaped # outside a character class and the next newline, inclusive, are also ignored. This is equivalent to Perl's /x option, and it can be changed within a pattern by a (?x) or (?-x) option setting. Default value is “0â€. Example 1.7. Set pcre_extended parameter ... modparam("regex", "pcre_extended", 1) ... 1.4. Exported Functions 1.4.1. pcre_match (string, pcre_regex) Matches the given string parameter against the regular expression pcre_regex, which is compiled into a PCRE object. Returns TRUE if it matches, FALSE otherwise. Meaning of the parameters is as follows: * string - String or pseudo-variable to compare. * pcre_regex - Regular expression to be compiled in a PCRE object. It can be a string or pseudo-variable. NOTE: To use the "end of line" symbol '$' in the pcre_regex parameter use '$$'. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.8. pcre_match usage (forcing case insensitive) ... if (pcre_match("$ua", "(?i)^twinkle")) { xlog("L_INFO", "User-Agent matches\n"); } ... Example 1.9. pcre_match usage (using "end of line" symbol) ... if (pcre_match("$rU", "^user[1234]$$")) { # Will be converted to "^user [1234]$" xlog("L_INFO", "RURI username matches\n"); } ... 1.4.2. pcre_match_group (string [, group]) It uses the groups readed from the text file (see Section 1.6.1, “File formatâ€) to match the given string parameter against the compiled regular expression in group number group. Returns TRUE if it matches, FALSE otherwise. Meaning of the parameters is as follows: * string - String or pseudo-variable to compare. * group - Number of group to use in the operation. If not specified then 0 (the first group) is used. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.10. pcre_match_group usage ... if (pcre_match_group("$rU", "2")) { xlog("L_INFO", "RURI username matches group 2\n"); } ... 1.5. Exported MI Functions 1.5.1. regex_reload Causes regex module to re-read the content of the text file and re-compile the regular expressions. The number of groups in the file can be modified safely. Name: regex_reload Parameters: none MI FIFO Command Format: :regex_reload:_reply_fifo_file_ _empty_line_ 1.6. Installation and Running 1.6.1. File format The file contains regular expressions categorized in groups. Each group starts with "[number]" line. Lines starting by space, tab, CR, LF or # (comments) are ignored. Each regular expression must take up just one line, this means that a regular expression can't be splitted in various lines. An example of the file format would be the following: Example 1.11. regex file ### List of User-Agents publishing presence status [0] # Softphones ^Twinkle/1 ^X-Lite ^eyeBeam ^Bria ^SIP Communicator ^Linphone # Deskphones ^Snom # Others ^SIPp ^PJSUA ### Blacklisted source IP's [1] ^190\.232\.250\.226$ ^122\.5\.27\.125$ ^86\.92\.112\. ### Free PSTN destinations in Spain [2] ^1\d{3}$ ^((\+|00)34)?900\d{6}$ The module compiles the text above to the following regular expressions: group 0: ((^Twinkle/1)|(^X-Lite)|(^eyeBeam)|(^Bria)|(^SIP Communicator)| (^Linphone)|(^Snom)|(^SIPp)|(^PJSUA)) group 1: ((^190\.232\.250\.226$)|(^122\.5\.27\.125$)|(^86\.92\.112\.)) group 2: ((^1\d{3}$)|(^((\+|00)34)?900\d{6}$)) The first group can be used to avoid auto-generated PUBLISH (pua_usrloc module) for UA's already supporting presence: Example 1.12. Using with pua_usrloc route[REGISTER] { if (! pcre_match_group("$ua", 0)) { xlog("L_INFO", "Auto-generated PUBLISH for $fu ($ua)\n"); pua_set_publish(); } save("location"); exit; } NOTE: It's important to understand that the numbers in each group header ([number]) must start by 0. If not, the real group number will not match the number appearing in the file. For example, the following text file: Example 1.13. Incorrect groups file [1] ^aaa ^bbb [2] ^ccc ^ddd will generate the following regular expressions: group 0: ((^aaa)|(^bbb)) group 1: ((^ccc)|(^ddd)) Note that the real index doesn't match the group number in the file. This is, compiled group 0 always points to the first group in the file, regardless of its number in the file. In fact, the group number appearing in the file is used for nothing but for delimiting different groups. NOTE: A line containing a regular expression cannot start by '[' since it would be treated as a new group. The same for lines starting by space, tab, or '#' (they would be ignored by the parser). As a workaround, using brackets would work: [0] ([0-9]{9}) ( #abcde) ( qwerty) opensips-2.2.2/modules/regex/doc/000077500000000000000000000000001300170765700167125ustar00rootroot00000000000000opensips-2.2.2/modules/regex/doc/regex.xml000066400000000000000000000020321300170765700205430ustar00rootroot00000000000000 %docentities; ]> Regex Module &osipsname; Iñaki Baz Castillo ibc@aliax.net Iñaki Baz Castillo ibc@aliax.net 2009 Iñaki Baz Castillo $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/regex/doc/regex_admin.xml000066400000000000000000000316331300170765700217240ustar00rootroot00000000000000 &adminguide;
Overview This module offers matching operations against regular expressions using the powerful PCRE library. A text file containing regular expressions categorized in groups is compiled when the module is loaded, storing the compiled PCRE objects in an array. A function to match a string or pseudo-variable against any of these groups is provided. The text file can be modified and reloaded at any time via a MI command. The module also offers a function to perform a PCRE matching operation against a regular expression provided as function parameter. For a detailed list of PCRE features read the man page of the library.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other OpenSIPS modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libpcre-dev - the development libraries of PCRE.
Exported Parameters
<varname>file</varname> (string) Text file containing the regular expression groups. It must be set in order to enable the group matching function. Default value is NULL. Set <varname>file</varname> parameter ... modparam("regex", "file", "/etc/opensips/regex_groups") ...
<varname>max_groups</varname> (int) Max number of regular expression groups in the text file. Default value is 20. Set <varname>max_groups</varname> parameter ... modparam("regex", "max_groups", 40) ...
<varname>group_max_size</varname> (int) Max content size of a group in the text file. Default value is 8192. Set <varname>group_max_size</varname> parameter ... modparam("regex", "group_max_size", 16384) ...
<varname>pcre_caseless</varname> (int) If this options is set, matching is done caseless. It is equivalent to Perl's /i option, and it can be changed within a pattern by a (?i) or (?-i) option setting. Default value is 0. Set <varname>pcre_caseless</varname> parameter ... modparam("regex", "pcre_caseless", 1) ...
<varname>pcre_multiline</varname> (int) By default, PCRE treats the subject string as consisting of a single line of characters (even if it actually contains newlines). The "start of line" metacharacter (^) matches only at the start of the string, while the "end of line" metacharacter ($) matches only at the end of the string, or before a terminating newline. When this option is set, the "start of line" and "end of line" constructs match immediately following or immediately before internal newlines in the subject string, respectively, as well as at the very start and end. This is equivalent to Perl's /m option, and it can be changed within a pattern by a (?m) or (?-m) option setting. If there are no newlines in a subject string, or no occurrences of ^ or $ in a pattern, setting this option has no effect. Default value is 0. Set <varname>pcre_multiline</varname> parameter ... modparam("regex", "pcre_multiline", 1) ...
<varname>pcre_dotall</varname> (int) If this option is set, a dot metacharater in the pattern matches all characters, including those that indicate newline. Without it, a dot does not match when the current position is at a newline. This option is equivalent to Perl's /s option, and it can be changed within a pattern by a (?s) or (?-s) option setting. Default value is 0. Set <varname>pcre_dotall</varname> parameter ... modparam("regex", "pcre_dotall", 1) ...
<varname>pcre_extended</varname> (int) If this option is set, whitespace data characters in the pattern are totally ignored except when escaped or inside a character class. Whitespace does not include the VT character (code 11). In addition, characters between an unescaped # outside a character class and the next newline, inclusive, are also ignored. This is equivalent to Perl's /x option, and it can be changed within a pattern by a (?x) or (?-x) option setting. Default value is 0. Set <varname>pcre_extended</varname> parameter ... modparam("regex", "pcre_extended", 1) ...
Exported Functions
<function moreinfo="none">pcre_match (string, pcre_regex)</function> Matches the given string parameter against the regular expression pcre_regex, which is compiled into a PCRE object. Returns TRUE if it matches, FALSE otherwise. Meaning of the parameters is as follows: string - String or pseudo-variable to compare. pcre_regex - Regular expression to be compiled in a PCRE object. It can be a string or pseudo-variable. NOTE: To use the "end of line" symbol '$' in the pcre_regex parameter use '$$'. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>pcre_match</function> usage (forcing case insensitive) ... if (pcre_match("$ua", "(?i)^twinkle")) { xlog("L_INFO", "User-Agent matches\n"); } ... <function>pcre_match</function> usage (using "end of line" symbol) ... if (pcre_match("$rU", "^user[1234]$$")) { # Will be converted to "^user[1234]$" xlog("L_INFO", "RURI username matches\n"); } ...
<function moreinfo="none">pcre_match_group (string [, group])</function> It uses the groups readed from the text file (see ) to match the given string parameter against the compiled regular expression in group number group. Returns TRUE if it matches, FALSE otherwise. Meaning of the parameters is as follows: string - String or pseudo-variable to compare. group - Number of group to use in the operation. If not specified then 0 (the first group) is used. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>pcre_match_group</function> usage ... if (pcre_match_group("$rU", "2")) { xlog("L_INFO", "RURI username matches group 2\n"); } ...
Exported MI Functions
<function moreinfo="none">regex_reload</function> Causes regex module to re-read the content of the text file and re-compile the regular expressions. The number of groups in the file can be modified safely. Name: regex_reload Parameters: none MI FIFO Command Format: :regex_reload:_reply_fifo_file_ _empty_line_
Installation and Running
File format The file contains regular expressions categorized in groups. Each group starts with "[number]" line. Lines starting by space, tab, CR, LF or # (comments) are ignored. Each regular expression must take up just one line, this means that a regular expression can't be splitted in various lines. An example of the file format would be the following: regex file ### List of User-Agents publishing presence status [0] # Softphones ^Twinkle/1 ^X-Lite ^eyeBeam ^Bria ^SIP Communicator ^Linphone # Deskphones ^Snom # Others ^SIPp ^PJSUA ### Blacklisted source IP's [1] ^190\.232\.250\.226$ ^122\.5\.27\.125$ ^86\.92\.112\. ### Free PSTN destinations in Spain [2] ^1\d{3}$ ^((\+|00)34)?900\d{6}$ The module compiles the text above to the following regular expressions: group 0: ((^Twinkle/1)|(^X-Lite)|(^eyeBeam)|(^Bria)|(^SIP Communicator)| (^Linphone)|(^Snom)|(^SIPp)|(^PJSUA)) group 1: ((^190\.232\.250\.226$)|(^122\.5\.27\.125$)|(^86\.92\.112\.)) group 2: ((^1\d{3}$)|(^((\+|00)34)?900\d{6}$)) The first group can be used to avoid auto-generated PUBLISH (pua_usrloc module) for UA's already supporting presence: Using with pua_usrloc route[REGISTER] { if (! pcre_match_group("$ua", 0)) { xlog("L_INFO", "Auto-generated PUBLISH for $fu ($ua)\n"); pua_set_publish(); } save("location"); exit; } NOTE: It's important to understand that the numbers in each group header ([number]) must start by 0. If not, the real group number will not match the number appearing in the file. For example, the following text file: Incorrect groups file [1] ^aaa ^bbb [2] ^ccc ^ddd will generate the following regular expressions: group 0: ((^aaa)|(^bbb)) group 1: ((^ccc)|(^ddd)) Note that the real index doesn't match the group number in the file. This is, compiled group 0 always points to the first group in the file, regardless of its number in the file. In fact, the group number appearing in the file is used for nothing but for delimiting different groups. NOTE: A line containing a regular expression cannot start by '[' since it would be treated as a new group. The same for lines starting by space, tab, or '#' (they would be ignored by the parser). As a workaround, using brackets would work: [0] ([0-9]{9}) ( #abcde) ( qwerty)
opensips-2.2.2/modules/regex/regex_mod.c000066400000000000000000000405661300170765700202750ustar00rootroot00000000000000/* * regex module - pcre operations * * Copyright (C) 2008 Iñaki Baz Castillo * * This file is part of OpenSIPS, a free SIP server. * * OpenSIPS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * OpenSIPS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-01-14 initial version (Iñaki Baz Castillo) */ /*! * \file * \brief REGEX :: Perl-compatible regular expressions using PCRE library * Copyright (C) 2008 Iñaki Baz Castillo * \ingroup regex */ #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../pt.h" #include "../../mem/shm_mem.h" #include "../../str.h" #include "../../locking.h" #include "../../mod_fix.h" #include "../../mi/mi.h" #define START 0 #define RELOAD 1 #define FILE_MAX_LINE 500 /*!< Max line size in the file */ #define MAX_GROUPS 20 /*!< Max number of groups */ #define GROUP_MAX_SIZE 8192 /*!< Max size of a group */ /* * Locking variables */ gen_lock_t *reload_lock; /* * Module exported parameter variables */ static char *file; static int max_groups = MAX_GROUPS; static int group_max_size = GROUP_MAX_SIZE; static int pcre_caseless = 0; static int pcre_multiline = 0; static int pcre_dotall = 0; static int pcre_extended = 0; /* * Module internal parameter variables */ static pcre **pcres; static pcre ***pcres_addr; static int *num_pcres; static int pcre_options = 0x00000000; /* * Module core functions */ static int mod_init(void); static void destroy(void); /* * Module internal functions */ static int load_pcres(int); static void free_shared_memory(void); /* * Script functions */ static int w_pcre_match(struct sip_msg* _msg, char* _s1, char* _s2); static int w_pcre_match_group(struct sip_msg* _msg, char* _s1, char* _s2); /* * MI functions */ static struct mi_root* mi_pcres_reload(struct mi_root* cmd, void* param); /* * Exported functions */ static cmd_export_t cmds[] = { { "pcre_match", (cmd_function)w_pcre_match, 2, fixup_spve_spve, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, { "pcre_match_group", (cmd_function)w_pcre_match_group, 2, fixup_spve_uint, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, { "pcre_match_group", (cmd_function)w_pcre_match_group, 1, fixup_spve_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, { 0, 0, 0, 0, 0, 0 } }; /* * Exported parameters */ static param_export_t params[] = { {"file", STR_PARAM, &file }, {"max_groups", INT_PARAM, &max_groups }, {"group_max_size", INT_PARAM, &group_max_size }, {"pcre_caseless", INT_PARAM, &pcre_caseless }, {"pcre_multiline", INT_PARAM, &pcre_multiline }, {"pcre_dotall", INT_PARAM, &pcre_dotall }, {"pcre_extended", INT_PARAM, &pcre_extended }, {0, 0, 0} }; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { { "regex_reload", 0, mi_pcres_reload, MI_NO_INPUT_FLAG, 0, 0 }, { 0, 0, 0, 0, 0 ,0 } }; /* * Module interface */ struct module_exports exports = { "regex", /*!< module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /*!< dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /*!< exported functions */ 0, /*!< exported async functions */ params, /*!< exported parameters */ 0, /*!< exported statistics */ mi_cmds, /*!< exported MI functions */ 0, /*!< exported pseudo-variables */ 0, /*!< extra processes */ mod_init, /*!< module initialization function */ (response_function) 0, /*!< response handling function */ destroy, /*!< destroy function */ 0 /*!< per-child init function */ }; /*! \brief * Init module function */ static int mod_init(void) { LM_INFO("initializing module...\n"); /* Group matching feature */ if (file == NULL) { LM_NOTICE("'file' parameter is not set, group matching disabled\n"); } else { /* Create and init the lock */ reload_lock = lock_alloc(); if (reload_lock == NULL) { LM_ERR("cannot allocate reload_lock\n"); goto err; } if (lock_init(reload_lock) == NULL) { LM_ERR("cannot init the reload_lock\n"); lock_dealloc(reload_lock); goto err; } /* PCRE options */ if (pcre_caseless != 0) { LM_DBG("PCRE CASELESS enabled\n"); pcre_options = pcre_options | PCRE_CASELESS; } if (pcre_multiline != 0) { LM_DBG("PCRE MULTILINE enabled\n"); pcre_options = pcre_options | PCRE_MULTILINE; } if (pcre_dotall != 0) { LM_DBG("PCRE DOTALL enabled\n"); pcre_options = pcre_options | PCRE_DOTALL; } if (pcre_extended != 0) { LM_DBG("PCRE EXTENDED enabled\n"); pcre_options = pcre_options | PCRE_EXTENDED; } LM_DBG("PCRE options: %i\n", pcre_options); /* Pointer to pcres */ if ((pcres_addr = shm_malloc(sizeof(pcre **))) == 0) { LM_ERR("no memory for pcres_addr\n"); goto err; } /* Integer containing the number of pcres */ if ((num_pcres = shm_malloc(sizeof(int))) == 0) { LM_ERR("no memory for num_pcres\n"); goto err; } /* Load the pcres */ LM_NOTICE("loading pcres...\n"); if (load_pcres(START)) { LM_CRIT("failed to load pcres\n"); goto err; } } return 0; err: free_shared_memory(); return -1; } static void destroy(void) { free_shared_memory(); } /*! \brief Convert the file content into regular expressions and store them in pcres */ static int load_pcres(int action) { int i, j; FILE *f; char line[FILE_MAX_LINE]; char **patterns = NULL; pcre *pcre_tmp = NULL; int pcre_size; int pcre_rc; const char *pcre_error; int pcre_erroffset; int num_pcres_tmp = 0; pcre **pcres_tmp = NULL; /* Get the lock */ lock_get(reload_lock); if (!(f = fopen(file, "r"))) { LM_ERR("could not open file '%s'\n", file); goto err; } /* Array containing each pattern in the file */ if ((patterns = pkg_malloc(sizeof(char*) * max_groups)) == 0) { LM_ERR("no more memory for patterns\n"); fclose(f); goto err; } for (i=0; i= max_groups) { LM_ERR("max patterns exceeded\n"); fclose(f); goto err; } /* Start the regular expression with '(' */ patterns[i][0] = '('; memset(line, '\0', FILE_MAX_LINE); continue; } /* Check if the patter size is too big (aprox) */ if (strlen(patterns[i]) + strlen(line) >= group_max_size - 2) { LM_ERR("pattern max file exceeded\n"); fclose(f); goto err; } /* Append ')' at the end of the line */ if (line[strlen(line) - 1] == '\n') { line[strlen(line)] = line[strlen(line) - 1]; line[strlen(line) - 2] = ')'; } else { /* This is the last char in the file and it's not \n */ line[strlen(line)] = ')'; } /* Append '(' at the beginning of the line */ memcpy(patterns[i]+strlen(patterns[i]), "(", 1); /* Append the line to the current pattern */ memcpy(patterns[i]+strlen(patterns[i]), line, strlen(line)); memset(line, '\0', FILE_MAX_LINE); } num_pcres_tmp = i + 1; fclose(f); /* Fix the patterns */ for (i=0; i < num_pcres_tmp; i++) { /* Convert empty groups in unmatcheable regular expression ^$ */ if (strlen(patterns[i]) == 1) { patterns[i][0] = '^'; patterns[i][1] = '$'; patterns[i][2] = '\0'; continue; } /* Delete possible '\n' at the end of the pattern */ if (patterns[i][strlen(patterns[i])-1] == '\n') { patterns[i][strlen(patterns[i])-1] = '\0'; } /* Replace '\n' with '|' (except at the end of the pattern) */ for (j=0; j < strlen(patterns[i]); j++) { if (patterns[i][j] == '\n' && j != strlen(patterns[i])-1) { patterns[i][j] = '|'; } } /* Add ')' at the end of the pattern */ patterns[i][strlen(patterns[i])] = ')'; } /* Log the group patterns */ LM_NOTICE("num groups = %d\n\n", num_pcres_tmp); for (i=0; i < num_pcres_tmp; i++) { LM_NOTICE("%s (size = %i)\n", i, patterns[i], i, (int)strlen(patterns[i])); } /* Temporal pointer of pcres */ if ((pcres_tmp = pkg_malloc(sizeof(pcre *) * num_pcres_tmp)) == 0) { LM_ERR("no more memory for pcres_tmp\n"); goto err; } for (i=0; i= *num_pcres) { LM_ERR("invalid pcre index '%i', there are %i pcres\n", num_pcre, *num_pcres); return -4; } if (fixup_get_svalue(_msg, (gparam_p)_s1, &string)) { LM_ERR("cannot print the format\n"); return -5; } lock_get(reload_lock); pcre_rc = pcre_exec( (*pcres_addr)[num_pcre], /* the compiled pattern */ NULL, /* no extra data - we didn't study the pattern */ string.s, /* the matching string */ (int)(string.len), /* the length of the subject */ 0, /* start at offset 0 in the string */ 0, /* default options */ NULL, /* output vector for substring information */ 0); /* number of elements in the output vector */ lock_release(reload_lock); /* Matching failed: handle error cases */ if (pcre_rc < 0) { switch(pcre_rc) { case PCRE_ERROR_NOMATCH: LM_DBG("'%s' doesn't match pcres[%i]\n", string.s, num_pcre); break; default: LM_DBG("matching error '%d'\n", pcre_rc); break; } return -1; } else { LM_DBG("'%s' matches pcres[%i]\n", string.s, num_pcre); return 1; } } /* * MI functions */ /*! \brief Reload pcres by reading the file again */ static struct mi_root* mi_pcres_reload(struct mi_root* cmd, void* param) { /* Check if group matching feature is enabled */ if (file == NULL) { LM_NOTICE("'file' parameter is not set, group matching disabled\n"); return init_mi_tree(403, MI_SSTR("Group matching not enabled")); } LM_NOTICE("reloading pcres...\n"); if (load_pcres(RELOAD)) { LM_ERR("failed to reload pcres\n"); return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN); } LM_NOTICE("reload success\n"); return init_mi_tree(200, MI_OK_S, MI_OK_LEN); } opensips-2.2.2/modules/registrar/000077500000000000000000000000001300170765700170355ustar00rootroot00000000000000opensips-2.2.2/modules/registrar/Makefile000066400000000000000000000003261300170765700204760ustar00rootroot00000000000000# $Id$ # # registrar module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=registrar.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/registrar/README000066400000000000000000000763131300170765700177270ustar00rootroot00000000000000registrar Module Jan Janak FhG FOKUS Edited by Jan Janak Bogdan-Andrei Iancu Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. PATH support 1.1.2. GRUU support 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. default_expires (integer) 1.3.2. min_expires (integer) 1.3.3. max_expires (integer) 1.3.4. default_q (integer) 1.3.5. tcp_persistent_flag (integer) 1.3.6. realm_prefix (string) 1.3.7. case_sensitive (integer) 1.3.8. received_avp (str) 1.3.9. received_param (string) 1.3.10. max_contacts (integer) 1.3.11. retry_after (integer) 1.3.12. sock_hdr_name (string) 1.3.13. mcontact_avp (string) 1.3.14. attr_avp (string) 1.3.15. gruu_secret (string) 1.3.16. disable_gruu (int) 1.4. Exported Functions 1.4.1. save(domain [,flags ,[aor]]) 1.4.2. remove(domain, AOR[, target]) 1.4.3. lookup(domain [, flags [, aor]]) 1.4.4. registered(domain [,AOR[, callid]]) 1.4.5. is_other_contact(domain , IPs) 1.4.6. is_registered(domain ,[AOR]) 1.4.7. is_contact_registered(domain ,[AOR],[contact],[callid]) 1.4.8. is_ip_registered(domain ,[AOR],IPvar) 1.4.9. add_sock_hdr(hdr_name) 1.5. Exported Statistics 1.5.1. max_expires 1.5.2. max_contacts 1.5.3. defaults_expires 1.5.4. accepted_regs 1.5.5. rejected_regs 2. Frequently Asked Questions List of Examples 1.1. Set default_expires parameter 1.2. Set min_expires parameter 1.3. Set max_expires parameter 1.4. Set default_q parameter 1.5. Set tcp_persistent_flag parameter 1.6. Set realm_prefix parameter 1.7. Set case_sensitive parameter 1.8. Set received_avp parameter 1.9. Set received_param parameter 1.10. Set max_contacts parameter 1.11. Set retry_after parameter 1.12. Set sock_hdr_namer parameter 1.13. Set mcontact_avp parameter 1.14. Set attr_avp parameter 1.15. Set gruu_secret parameter 1.16. Set gruu_secret parameter 1.17. save usage 1.18. remove usage 1.19. lookup usage 1.20. registered usage 1.21. is_other_contact usage 1.22. is_registered usage 1.23. is_contact_registered usage 1.24. is_ip_registered usage 1.25. add_sock_hdr usage Chapter 1. Admin Guide 1.1. Overview The module contains REGISTER processing logic. 1.1.1. PATH support Register module includes Path support (according to RFC 3327) for usage in registrars and home-proxies. A call to save(...) stores, if path-support is enabled in the registrar-module, the values of the Path Header(s) along with the contact into usrloc. There are three modes regarding the reply to a REGISTER including one or more Path HFs: * off - stores the value of the Path headers into usrloc without passing it back to the UAC in the reply. * lazy - stores the Path header and passes it back to the UAC if Path-support is indicated by the “path†param in the Supported HF. * strict - rejects the registration with “420 Bad Extension†if there's a Path header but no support for it is indicated by the UAC. Otherwise it's stored and passed back to the UAC. A call to lookup(...) always uses the path header if found, and inserts it as Route HF either in front of the first Route HF, or after the last Via HF if no Route is present. It also sets the destination uri to the first Path uri, thus overwriting the received-uri, because NAT has to be handled at the outbound-proxy of the UAC (the first hop after client's NAT). The whole process is transparent to the user, so no config changes are required beside setting the registrar-parameters “use_path†and “path_modeâ€. 1.1.2. GRUU support Register module includes GRUU support (according to RFC 5627) for usage in registrars and home-proxies. A call to save(...) stores, if the phone supports GRUU, the values of the SIP Instance along with the contact into usrloc. The registrar module will generate two types of GRUUs : * public - exposes the underlying AOR, constructed just by attaching the SIP Instance as the ;gr parameter value. These are persistent, valid as long as the contact registration is valid. * temporary - hides the underlying AOR Each new Register request leads to the construction of a new temporary GRUU, while Register requests with a different Call-Id lead to the invalidation of all the previous generated temporary GRUUs. A call to lookup(...) will try to detect if the R-URI contains a GRUU. If it does, it will route the request just for the Contact that the specific AOR belongs to, without appending any other branches. The whole process is transparent to the user, so no config changes are required. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * usrloc - User Location Module. * signaling - Signaling module. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. default_expires (integer) If the processed message contains neither Expires HFs nor expires contact parameters, this value will be used for newly created usrloc records. The parameter contains number of second to expire (for example use 3600 for one hour). Default value is 3600. Example 1.1. Set default_expires parameter ... modparam("registrar", "default_expires", 1800) ... 1.3.2. min_expires (integer) The minimum expires value of a Contact, values lower than this minimum will be automatically set to the minimum. Value 0 disables the checking. Default value is 60. Example 1.2. Set min_expires parameter ... modparam("registrar", "min_expires", 60) ... 1.3.3. max_expires (integer) The maximum expires value of a Contact, values higher than this maximum will be automatically set to the maximum. Value 0 disables the checking. Default value is 0. Example 1.3. Set max_expires parameter ... modparam("registrar", "max_expires", 120) ... 1.3.4. default_q (integer) The parameter represents default q value for new contacts. Because OpenSIPS doesn't support float parameter types, the value in the parameter is divided by 1000 and stored as float. For example, if you want default_q to be 0.38, use value 380 here. Default value is 0. Example 1.4. Set default_q parameter ... modparam("registrar", "default_q", 1000) ... 1.3.5. tcp_persistent_flag (integer) The parameter specifies the message flag to be used to control the module behaviour regarding TCP connections. If the flag is set for a REGISTER via TCP containing a TCP contact, the module, via the “save()†function, will set the lifetime of the TCP connection to the contact expire value. By doing this, the TCP connection will stay on as long as the contact is valid. Default value is -1 (disabled). Example 1.5. Set tcp_persistent_flag parameter ... modparam("registrar", "tcp_persistent_flag", 7) ... 1.3.6. realm_prefix (string) Prefix to be automatically strip from realm. As an alternative to SRV records (not all SIP clients support SRV lookup), a subdomain of the master domain can be defined for SIP purposes (like sip.mydomain.net pointing to same IP address as the SRV record for mydomain.net). By ignoring the realm_prefix "sip.", at registration, sip.mydomain.net will be equivalent to mydomain.net . Default value is NULL (none). Example 1.6. Set realm_prefix parameter ... modparam("registrar", "realm_prefix", "sip.") ... 1.3.7. case_sensitive (integer) If set to 1 then AOR comparison will be case sensitive (as RFC3261 instructs), if set to 0 then AOR comparison will be case insensitive. Default value is 1. Example 1.7. Set case_sensitive parameter ... modparam("registrar", "case_sensitive", 0) ... 1.3.8. received_avp (str) Registrar will store the value of the AVP configured by this parameter in the received column in the user location database. It will leave the column empty if the AVP is empty. The AVP should contain a SIP URI consisting of the source IP, port, and protocol of the REGISTER message being processed. Note The value of this parameter should be the same as the value of corresponding parameter of nathelper module. Default value is "NULL" (disabled). Example 1.8. Set received_avp parameter ... modparam("registrar", "received_avp", "$avp(rcv)") ... 1.3.9. received_param (string) The name of the parameter that will be appended to Contacts of 200 OK when the received URI was set by nathelper module. Default value is "received". Example 1.9. Set received_param parameter ... modparam("registrar", "received_param", "rcv") ... 1.3.10. max_contacts (integer) The parameter can be used to limit the number of contacts per AOR (Address of Record) in the user location database. Value 0 disables the check. This is the default value and will be used only if no other value (for max_contacts) is passed as parameter to the save() function. That's it - the function parameter overwride this global parameter. Default value is 0. Example 1.10. Set max_contacts parameter ... # Allow no more than 10 contacts per AOR modparam("registrar", "max_contacts", 10) ... 1.3.11. retry_after (integer) The registrar can generate 5xx reply to REGISTER in various situations. It can, for example, happen when the max_contacts parameter is set and the processing of REGISTER request would exceed the limit. In this case the registrar would generate "503 Service Unavailable" response. If you want to add the Retry-After header field in 5xx replies, set this parameter to a value grater than zero (0 means do not add the header field). See section 20.33 of RFC3261 for more details. Default value is 0 (disabled). Example 1.11. Set retry_after parameter ... modparam("registrar", "retry_after", 30) ... 1.3.12. sock_hdr_name (string) Header which contains a socket description (proto:IP:port) to override the received socket info. The header will be search and used only if the flag 's' (Socket header) is set at "save()" time. This makes sense only in multiple replicated servers scenarios. Default value is NULL. Example 1.12. Set sock_hdr_namer parameter ... modparam("registrar", "sock_hdr_name", "Sock-Info") ... 1.3.13. mcontact_avp (string) AVP to store the modified binding that is set during cached registrations scenario (when REGISTER is forwarded to another registrar). The AVP will be used to extract the imposed expires from the 200ok reply. This makes sense only in cached registrations scenario. Default value is NULL. Example 1.13. Set mcontact_avp parameter ... modparam("registrar", "mcontact_avp", "$avp(register)") ... 1.3.14. attr_avp (string) AVP to store specific additional information for each registration. This information is read from the AVP and stored (in memory, db or both) at every registrar 'save'. When a registrar 'lookup' or 'is_registered' function is called, the attr_avp is populated with the value saved at [re]registration. When doing call forking, the avp will hold multiple values. The position of the corresponding attribute information in attr_avp is equal to the branch index. An example scenario is given below. Default value is NULL. Example 1.14. Set attr_avp parameter # reading attributes from the attr_pvar when doing parallel forking ... modparam("registrar", "attr_avp", "$avp(attr)") ... if (is_method("REGISTER")) { $avp(attr) = "contact_info"; save("location"); exit; } ... lookup("location"); t_on_branch("branch_route_1"); ... branch_route[branch_route_1] { xlog("Attributes for branch $T_branch_idx: $(avp(attr)[$T_branch _idx])\n"); } 1.3.15. gruu_secret (string) The string that will be used in XORing when generating temporary GRUUs. If not set, 'OpenSIPS' is the default secret. Example 1.15. Set gruu_secret parameter ... modparam("registrar", "gruu_secret", "top_secret") ... 1.3.16. disable_gruu (int) Globally disable GRUU handling Default value is 1 ( GRUU will not be handled ). Example 1.16. Set gruu_secret parameter ... modparam("registrar", "disable_gruu", 0) ... 1.4. Exported Functions 1.4.1. save(domain [,flags ,[aor]]) The function processes a REGISTER message. It can add, remove or modify usrloc records depending on Contact and Expires HFs in the REGISTER message. On success, 200 OK will be returned listing all contacts that are currently in usrloc. On an error, error message will be send with a short description in reason phrase. Meaning of the parameters is as follows: * domain - Logical domain within registrar. If database is used then this must be name of the table which stores the contacts. * flags (optional)- string of the following flags: + 'm' (Memory only) - save the contacts only in memory cache without no DB operation; + 'r' (no Reply) - do not generate a SIP reply to the current REGISTER request. + 's' (Socket header) - look into REGISTER request for a header which contains a socket description (proto:IP:port). This socket info will be stored by register instead of the received socket info. + 'cnn' (max Contacts) - this flag can be used to limit the number of contacts for this AOR (Address of Record) in the user location database. Value 0 disables the check. This parameter overrides the global "max_contacts" module parameter. + 'e(int)' (minimum expires) - this flag can be used to set minimum register expiration time. Values lower than this minimum will be automatically set to the minimum. Value 0 disables the checking. This parameter overrides the global "min_expires" module parameter. + 'E(int)' (maximum expires) - this flag can be used to set maximum register expiration time. Values higher than this maximum will be automatically set to the maximum. Value 0 disables the checking. This parameter overrides the global "max_expires" module parameter. + 'f' (force registration) - this flag can be used to force the registration of NEW contacts even if the maximum number of contacts is reached. In such a case, older contacts will be removed to make space to the new ones, without exceeding the maximum allowed number. This flag makes sense only if "cxx" is used. + 'p0' (Path support - 'off' mode) The Path header is saved into usrloc, but is never included in the reply. + 'p1' (Path support - lazy mode) The Path header is saved into usrloc, but is only included in the reply if path support is indicated in the registration request by the “path†option of the “Supported†header. + 'p2' (Path support - strict mode) The path header is only saved into usrloc, if path support is indicated in the registration request by the “path†option of the “Supported†header. If no path support is indicated, the request is rejected with “420 - Bad Extension†and the header “Unsupported: path†is included in the reply along with the received “Path†header. This mode is the one recommended by RFC-3327. + 'v' (path receiVed) if set, the “received†parameter of the first Path URI of a registration is set as received-uri and the NAT branch flag is set for this contact. This is useful if the registrar is placed behind a SIP loadbalancer, which passes the nat'ed UAC address as “received†parameter in it's Path uri. This parameter is a string composed of a set of flags. * aor (optional) - Variable contain a custom AOR; if missing, the AOR will be taken from the default place - the TO header URI. This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. Example 1.17. save usage ... # save into 'location', no flags, use default AOR (TO URI) save("location"); ... # save into 'location', do not update DB, max 5 contacts per AOR, # use default AOR (TO URI) save("location","mc5"); ... # save into 'location', no flags, use as AOR the FROM URI save("location","","$fu"); ... # save into 'location',no DB update, no reply to send, take AOR from AVP save("location","mr", "$avp(aor)"); ... 1.4.2. remove(domain, AOR[, target]) Explicitly remove contacts behind a given address-of-record. Meaning of the parameters is as follows: * domain - Logical domain within registrar. If database is used then this must be name of the table which stores the contacts. * AOR - address-of-record to be searched (SIP URI) * target (optional) - may be specified in two ways: + single delete - Specific contact to be deleted (SIP URI) + multi delete - IP/hostname of the AOR's contacts which will be deleted This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. Example 1.18. remove usage ... # remove all contacts belonging to the "bob" AOR remove("location", "sip:bob@atlanta.com"); ... # remove only bob's home phone remove("location", "sip:bob@atlanta.com", "sip:bob@46.50.64.78"); ... # remove all bob's phones which are behind "50.60.50.60" $var(next_hop) = "50.60.50.60" remove("location", "sip:bob@atlanta.com", "$var(next_hop)"); ... 1.4.3. lookup(domain [, flags [, aor]]) The functions extracts username from Request-URI and tries to find all contacts for the username in usrloc. If there are no such contacts, -1 will be returned. If there are such contacts, Request-URI will be overwritten with the contact that has the highest q value and optionally the rest will be appended to the message (depending on append_branches parameter value). If the method_filtering option is enabled, the lookup function will return only the contacts that support the method of the processed request. Meaning of the parameters is as follows: * domain - Name of table that should be used for the lookup. * flags(optional) + 'b' (no Branches) - this flag controls how lookup function processes multiple contacts. If there are multiple contacts for the given username in usrloc and this flag is not set, Request-URI will be overwritten with the highest-q rated contact and the rest will be appended to sip_msg structure and can be later used by tm for forking. If the flag is set, only Request-URI will be overwritten with the highest-q rated contact and the rest will be left unprocessed. + 'r' (bRanch lookup) - this flag enables searching through existing branches for aor's and expanding them to contacts. For example, you have got AOR A in your ruri but you also want to forward your calls to AOR B. In order to do this, you must put AOR B in a branch, and if this flag enabled, the function will also expand AOR B to contacts, which will be put back into the branches. The AOR's that were in branches before the function call shall be removed. WARNING: if you want this flag activated, the 'b' (no Branches) flag must not be set, because by setting that flag you won't allow lookup to write in a branch. + 'm' (Method filtering) - this flag tells if the contact filtering based on supported methods should be performed during lookup. + 'u' (User-Agent filtering) - this flag enables regexp filtering by user-agent. It's useful with enabled append_branches parameter. Regexp must follow the 'u' flag and must use format like 'u/regexp/'. + 'i' (Case insensitive search) - this flag enables case insensitive filtering for the 'u' flag. + 'e' (Use extended regexp) - this flag enables using of extended regexp format for the 'u' flag. * AOR (optional)- AOR to lookup for; if missing, the RURI is used as AOR; This must be a variable. Return codes: * 1 - contacts found and returned. -1 - no contact found. -2 - contacts found, but method not supported. -3 - internal error during processing. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.19. lookup usage ... lookup("location"); # simple lookup #or lookup("location","m"); # lookup with method filtering #or lookup("location","r"); #lookup with aor branch search; #all contacts except the first one shall be put #in the branches #or lookup("location","u/phone/i"); # lookup with user-agent filtering #or lookup("location","","$var(aor)"); # simple lookup with AOR from PV switch ($retcode) { case -1: case -3: sl_send_reply("404", "Not Found"); exit; case -2: sl_send_reply("405", "Not Found"); exit; }; ... 1.4.4. registered(domain [,AOR[, callid]]) The function returns true if an AOR is registered, false otherwise. The function does not modify the message being process. NOTE: if called for a reply (from onreply_route), you must pass an AOR (as parameter), otherwise the function will fail. Meaning of the parameters is as follows: * domain - Name of table that should be used for the lookup. * AOR (optional)- AOR to lookup for; if missing, the RURI is used as AOR; This must be a variable. * callid (optional)- callid to check if a contact if registered with this callid (this may help you to make distinction between newly registered contact (callid not registered so far) and re-registration (callid already registered). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE. WARNING: THIS FUNCTION IS DEPRECATED! USE is_registered() INSTEAD. Example 1.20. registered usage ... if (registered("location")) { sl_send_reply("100", "Trying"); ... }; if (registered("location","$fu")) { xlog("caller is registered\n"); } ... 1.4.5. is_other_contact(domain , IPs) This function returns true if there are any other contacts registered, different from the ones from the IPs avp. Meaning of the parameters is as follows: * domain - Name of table that should be used for contacts lookup. * IPs - an AVP containing the IP's that will be compared with the contacts from usrloc. This function can be used from REQUEST_ROUTE. WARNING: THIS FUNCTION IS DEPRECATED! USE is_ip_registered() INSTEAD. Example 1.21. is_other_contact usage ... if (is_other_contact("location", "$avp(ips)")) { sl_send_reply("403", "Forbidden"); ... }; ... 1.4.6. is_registered(domain ,[AOR]) The function returns true if an AOR is registered, false otherwise. The function does not modify the message being process. Meaning of the parameters is as follows: * domain - Name of table that should be used for the lookup. * AOR (optional)- AOR to lookup for; if missing, the source if the AOR is the "To" header for REGISTER request, "From" header for any other sip request. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE. Example 1.22. is_registered usage ... /**/ if (is_method("REGISTER")) { /* automatically uses the URI from the To header */ if (is_registered("location")) { xlog("this AOR is registered\n") ... } }; /* check the From uri whether this aor is registered or not */ if (is_registered("location","$fu")) { xlog("caller is registered\n"); } ... 1.4.7. is_contact_registered(domain ,[AOR],[contact],[callid]) The function returns true if a contact and/or a callid from a certain AOR is registered, false otherwise. The function does not modify the message being process. Meaning of the parameters is as follows: * domain - Name of table that should be used for the lookup. * AOR (optional)- AOR to lookup for; if missing, the source if the AOR is the "To" header for REGISTER request, "From" header for any other sip request. * contact (optional)- callid to check if a contact if registered with this callid (this may help you to make distinction between newly registered contact (callid not registered so far) and re-registration (callid already registered). * callid (optional)- callid to check if a contact if registered with this callid (this may help you to make distinction between newly registered contact (callid not registered so far) and re-registration (callid already registered). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE. Example 1.23. is_contact_registered usage ... /*let's say you want to block users that are not registered*/ if (is_method("INVITE")) { if (!is_contact_registered("location")) { sl_send_reply("401", "Unauthorized"); ... } } /* you want to check the second contact from the message whether it is registered or not */ if(is_method("INVITE")) { if (is_contact_registered("location","$fu","$(ct[1])",)) xlog("caller is registered\n"); } ... 1.4.8. is_ip_registered(domain ,[AOR],IPvar) The function returns true if at least a contact has an IP from the IPavp in it's received parameter,otherwise returns false. The function does not modify the message being process. This function replaces the "is_other_contact" function. Meaning of the parameters is as follows: * domain - Name of table that should be used for the lookup. * AOR (optional)- AOR to lookup for; if missing, the source if the AOR is the "To" header for REGISTER request, "From" header for any other sip request. * IPvar the pvar containing the IPs you want to check the received parameter of the contacts in the AOR against. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE. Example 1.24. is_ip_registered usage ... /* check the source ip whether it is already registered */ if (is_method("REGISTER")) { if (is_ip_registered("location","$tu","$si")) { xlog("already registered from this ip\n"); ... } }; ... 1.4.9. add_sock_hdr(hdr_name) Adds to the current REGISTER request a new header with “hdr_name†which contains the description of the received socket (proto:ip:port) This makes sense only in multiple replicated servers scenarios. Meaning of the parameters is as follows: * hdr_name - header name to be used. This function can be used from REQUEST_ROUTE. Example 1.25. add_sock_hdr usage ... add_sock_hdr("Sock-Info"); ... 1.5. Exported Statistics 1.5.1. max_expires Value of max_expires parameter. 1.5.2. max_contacts The value of max_contacts parameter. 1.5.3. defaults_expires The value of default_expires parameter. 1.5.4. accepted_regs Number of accepted registrations. 1.5.5. rejected_regs Number of rejected registrations. Chapter 2. Frequently Asked Questions 2.1. What happened with the old “append_branch†module parameter? It was removed as global option, as the “lookup†function takes this option via the flag "b" (append Branches) See the documentation of the “lookup†function. 2.2. What happened with the old “method_filtering†module parameter? It was removed as global option, as the “lookup†function takes this option via the flag "m" (Method filtering) See the documentation of the “lookup†function. 2.3. What happened with the old “sock_flag†module parameter? It was removed as global option, as the “save†function takes this option via the flag "s" (Socket header) See the documentation of the “save†function. 2.4. What happened with the old “use_path†and “path_mode†module parameters? They were removed as global option, as the “save†function takes these options via the flag "px" (path support) See the documentation of the “save†function. 2.5. What happened with the old “path_use_received†module parameter? It was removed as global option, as the “save†function takes this option via the flag "v" (path receiVed) See the documentation of the “save†function. 2.6. What happened with the old “nat_flag†module parameter? It was removed, as the module internally loads this value from the “USRLOC†module (see the “nat_bflag†USRLOC parameter). 2.7. What happened with the old “use_domain†module parameter? It was removed, as the module internally loads this option from the “USRLOC†module. This was done in order to simplify the configuration. 2.8. What happened with the old “save_noreply†and “save_memory†functions? There functions were merged into the new “save(domain,flags)†functions. If a reply should be sent or if the DB should be updated also is controlled via the flags. 2.9. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.10. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.11. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. 2.12. What happened to the desc_time_order parameter? It was removed, as its functionality was mmigrate into usrloc module, were there is a parameter with the same name. opensips-2.2.2/modules/registrar/common.c000066400000000000000000000122471300170765700204770ustar00rootroot00000000000000/* * Common stuff * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------ * 2003-02-14 un-escaping added (janakj) * 2006-09-19 AOR may be provided via an AVP instead of being fetched * from URI (bogdan) */ /*! * \file * \brief SIP registrar module * \ingroup registrar */ #include #include "../../dprint.h" #include "../../ut.h" /* q_memchr */ #include "../../parser/parse_uri.h" #include "rerrno.h" #include "reg_mod.h" #include "common.h" #define MAX_AOR_LEN 256 #define TEMP_GRUU "tgruu." #define TEMP_GRUU_SIZE (sizeof(TEMP_GRUU)-1) #define MAX_TGRUU_SIZE 255 #define GR_MAGIC 73 char tgruu_dec[MAX_TGRUU_SIZE]; extern str gruu_secret; extern str default_gruu_secret; /*! \brief * Extract Address of Record * In case of public GRUUs, also populates sip_instance * In case of temp GRUUs, also populates call_id */ int extract_aor(str* _uri, str* _a,str *sip_instance,str *call_id) { static char aor_buf[MAX_AOR_LEN]; memset(aor_buf, 0, MAX_AOR_LEN); str tmp; struct sip_uri puri; int user_len,tgruu_len,dec_size,i; str *magic; if (parse_uri(_uri->s, _uri->len, &puri) < 0) { rerrno = R_AOR_PARSE; LM_ERR("failed to parse Address of Record\n"); return -1; } /* if have ;gr param and func caller is interested in * potentially extracting the sip instance */ if ((puri.gr.s && puri.gr.len) && sip_instance) { LM_DBG("has gruu\n"); /* ;gr param detected */ if (memcmp(puri.user.s,TEMP_GRUU,TEMP_GRUU_SIZE) == 0) { LM_DBG("temp gruu\n"); /* temp GRUU, decode and extract aor, sip_instance * and call_id */ tgruu_len = puri.user.len - TEMP_GRUU_SIZE; memcpy(tgruu_dec,puri.user.s+TEMP_GRUU_SIZE,tgruu_len); if (gruu_secret.s != NULL) magic = &gruu_secret; else magic = &default_gruu_secret; dec_size = base64decode((unsigned char *)tgruu_dec, (unsigned char *)tgruu_dec,tgruu_len); for (i=0;is[i%magic->len]; LM_DBG("decoded [%.*s]\n",dec_size,tgruu_dec); /* extract aor - skip tgruu generation time at * the beggining */ _a->s = (char *)memchr(tgruu_dec,' ',dec_size) + 1; if (_a->s == NULL) { rerrno = R_AOR_PARSE; LM_ERR("failed to parse Address of Record\n"); return -1; } _a->len = (char *)memchr(_a->s,' ',dec_size - (_a->s-tgruu_dec)) - _a->s; if (_a->len < 0) { rerrno = R_AOR_PARSE; LM_ERR("failed to parse Address of Record\n"); return -1; } sip_instance->s = _a->s+_a->len+1; /* skip ' ' */ if (sip_instance->s >= tgruu_dec + dec_size) { rerrno = R_AOR_PARSE; LM_ERR("failed to parse Address of Record\n"); return -1; } sip_instance->len = (char *)memchr(sip_instance->s,' ', dec_size-(sip_instance->s-tgruu_dec)) - sip_instance->s; if (sip_instance->len < 0) { rerrno = R_AOR_PARSE; LM_ERR("failed to parse Address of Record\n"); return -1; } call_id->s = sip_instance->s + sip_instance->len + 1; if (call_id->s >= tgruu_dec + dec_size) { rerrno = R_AOR_PARSE; LM_ERR("failed to parse Address of Record\n"); return -1; } call_id->len = (tgruu_dec+dec_size) - call_id->s; LM_DBG("extracted aor [%.*s] and instance [%.*s] and callid [%.*s]\n",_a->len,_a->s, sip_instance->len,sip_instance->s,call_id->len,call_id->s); /* skip checks - done at save() */ return 0; } else { LM_DBG("public gruu\n"); *sip_instance = puri.gr_val; } } if ( (puri.user.len + puri.host.len + 1) > MAX_AOR_LEN || puri.user.len > USERNAME_MAX_SIZE || puri.host.len > DOMAIN_MAX_SIZE ) { rerrno = R_AOR_LEN; LM_ERR("Address Of Record too long\n"); return -2; } _a->s = aor_buf; _a->len = puri.user.len; if (un_escape(&puri.user, _a) < 0) { rerrno = R_UNESCAPE; LM_ERR("failed to unescape username\n"); return -3; } user_len = _a->len; if (reg_use_domain) { if (user_len) aor_buf[_a->len++] = '@'; /* strip prefix (if defined) */ if (realm_prefix.len && realm_prefix.lenlen, puri.host.s + realm_prefix.len, puri.host.len - realm_prefix.len); _a->len += puri.host.len - realm_prefix.len; } else { memcpy(aor_buf + _a->len, puri.host.s, puri.host.len); _a->len += puri.host.len; } } if (case_sensitive && user_len) { tmp.s = _a->s + user_len + 1; tmp.len = _a->s + _a->len - tmp.s; strlower(&tmp); } else { strlower(_a); } return 0; } opensips-2.2.2/modules/registrar/common.h000066400000000000000000000021321300170765700204740ustar00rootroot00000000000000/* * Common stuff * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief SIP registrar module - common stuff * \ingroup registrar */ #ifndef COMMON_H #define COMMON_H #include "../../str.h" /*! \brief * Extract Address Of Record */ int extract_aor(str* _uri, str* _a,str* sip_instance,str *call_id); #endif /* COMMON_H */ opensips-2.2.2/modules/registrar/doc/000077500000000000000000000000001300170765700176025ustar00rootroot00000000000000opensips-2.2.2/modules/registrar/doc/registrar.xml000066400000000000000000000023271300170765700223320ustar00rootroot00000000000000 %docentities; ]> registrar Module &osipsname; Jan Janak &fhg;
jan@iptel.org
Jan Janak
jan@iptel.org
Bogdan-Andrei Iancu
bogdan@opensips.org
2003 &fhg; $Revision: 8740 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/registrar/doc/registrar_admin.xml000066400000000000000000001064231300170765700235040ustar00rootroot00000000000000 &adminguide;
Overview The module contains REGISTER processing logic.
PATH support Register module includes Path support (according to RFC 3327) for usage in registrars and home-proxies. A call to save(...) stores, if path-support is enabled in the registrar-module, the values of the Path Header(s) along with the contact into usrloc. There are three modes regarding the reply to a REGISTER including one or more Path HFs: off - stores the value of the Path headers into usrloc without passing it back to the UAC in the reply. lazy - stores the Path header and passes it back to the UAC if Path-support is indicated by the path param in the Supported HF. strict - rejects the registration with 420 Bad Extension if there's a Path header but no support for it is indicated by the UAC. Otherwise it's stored and passed back to the UAC. A call to lookup(...) always uses the path header if found, and inserts it as Route HF either in front of the first Route HF, or after the last Via HF if no Route is present. It also sets the destination uri to the first Path uri, thus overwriting the received-uri, because NAT has to be handled at the outbound-proxy of the UAC (the first hop after client's NAT). The whole process is transparent to the user, so no config changes are required beside setting the registrar-parameters use_path and path_mode.
GRUU support Register module includes GRUU support (according to RFC 5627) for usage in registrars and home-proxies. A call to save(...) stores, if the phone supports GRUU, the values of the SIP Instance along with the contact into usrloc. The registrar module will generate two types of GRUUs : public - exposes the underlying AOR, constructed just by attaching the SIP Instance as the ;gr parameter value. These are persistent, valid as long as the contact registration is valid. temporary - hides the underlying AOR Each new Register request leads to the construction of a new temporary GRUU, while Register requests with a different Call-Id lead to the invalidation of all the previous generated temporary GRUUs. A call to lookup(...) will try to detect if the R-URI contains a GRUU. If it does, it will route the request just for the Contact that the specific AOR belongs to, without appending any other branches. The whole process is transparent to the user, so no config changes are required.
Dependencies
&osips; Modules The following modules must be loaded before this module: usrloc - User Location Module. signaling - Signaling module.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>default_expires</varname> (integer) If the processed message contains neither Expires HFs nor expires contact parameters, this value will be used for newly created usrloc records. The parameter contains number of second to expire (for example use 3600 for one hour). Default value is 3600. Set <varname>default_expires</varname> parameter ... modparam("registrar", "default_expires", 1800) ...
<varname>min_expires</varname> (integer) The minimum expires value of a Contact, values lower than this minimum will be automatically set to the minimum. Value 0 disables the checking. Default value is 60. Set <varname>min_expires</varname> parameter ... modparam("registrar", "min_expires", 60) ...
<varname>max_expires</varname> (integer) The maximum expires value of a Contact, values higher than this maximum will be automatically set to the maximum. Value 0 disables the checking. Default value is 0. Set <varname>max_expires</varname> parameter ... modparam("registrar", "max_expires", 120) ...
<varname>default_q</varname> (integer) The parameter represents default q value for new contacts. Because &osips; doesn't support float parameter types, the value in the parameter is divided by 1000 and stored as float. For example, if you want default_q to be 0.38, use value 380 here. Default value is 0. Set <varname>default_q</varname> parameter ... modparam("registrar", "default_q", 1000) ...
<varname>tcp_persistent_flag</varname> (integer) The parameter specifies the message flag to be used to control the module behaviour regarding TCP connections. If the flag is set for a REGISTER via TCP containing a TCP contact, the module, via the save() function, will set the lifetime of the TCP connection to the contact expire value. By doing this, the TCP connection will stay on as long as the contact is valid. Default value is -1 (disabled). Set <varname>tcp_persistent_flag</varname> parameter ... modparam("registrar", "tcp_persistent_flag", 7) ...
<varname>realm_prefix</varname> (string) Prefix to be automatically strip from realm. As an alternative to SRV records (not all SIP clients support SRV lookup), a subdomain of the master domain can be defined for SIP purposes (like sip.mydomain.net pointing to same IP address as the SRV record for mydomain.net). By ignoring the realm_prefix "sip.", at registration, sip.mydomain.net will be equivalent to mydomain.net . Default value is NULL (none). Set <varname>realm_prefix</varname> parameter ... modparam("registrar", "realm_prefix", "sip.") ...
<varname>case_sensitive</varname> (integer) If set to 1 then AOR comparison will be case sensitive (as RFC3261 instructs), if set to 0 then AOR comparison will be case insensitive. Default value is 1. Set <varname>case_sensitive</varname> parameter ... modparam("registrar", "case_sensitive", 0) ...
<varname>received_avp</varname> (str) Registrar will store the value of the AVP configured by this parameter in the received column in the user location database. It will leave the column empty if the AVP is empty. The AVP should contain a SIP URI consisting of the source IP, port, and protocol of the REGISTER message being processed. The value of this parameter should be the same as the value of corresponding parameter of nathelper module. Default value is "NULL" (disabled). Set <varname>received_avp</varname> parameter ... modparam("registrar", "received_avp", "$avp(rcv)") ...
<varname>received_param</varname> (string) The name of the parameter that will be appended to Contacts of 200 OK when the received URI was set by nathelper module. Default value is "received". Set <varname>received_param</varname> parameter ... modparam("registrar", "received_param", "rcv") ...
<varname>max_contacts</varname> (integer) The parameter can be used to limit the number of contacts per AOR (Address of Record) in the user location database. Value 0 disables the check. This is the default value and will be used only if no other value (for max_contacts) is passed as parameter to the save() function. That's it - the function parameter overwride this global parameter. Default value is 0. Set <varname>max_contacts</varname> parameter ... # Allow no more than 10 contacts per AOR modparam("registrar", "max_contacts", 10) ...
<varname>retry_after</varname> (integer) The registrar can generate 5xx reply to REGISTER in various situations. It can, for example, happen when the max_contacts parameter is set and the processing of REGISTER request would exceed the limit. In this case the registrar would generate "503 Service Unavailable" response. If you want to add the Retry-After header field in 5xx replies, set this parameter to a value grater than zero (0 means do not add the header field). See section 20.33 of RFC3261 for more details. Default value is 0 (disabled). Set <varname>retry_after</varname> parameter ... modparam("registrar", "retry_after", 30) ...
<varname>sock_hdr_name</varname> (string) Header which contains a socket description (proto:IP:port) to override the received socket info. The header will be search and used only if the flag 's' (Socket header) is set at "save()" time. This makes sense only in multiple replicated servers scenarios. Default value is NULL. Set <varname>sock_hdr_namer</varname> parameter ... modparam("registrar", "sock_hdr_name", "Sock-Info") ...
<varname>mcontact_avp</varname> (string) AVP to store the modified binding that is set during cached registrations scenario (when REGISTER is forwarded to another registrar). The AVP will be used to extract the imposed expires from the 200ok reply. This makes sense only in cached registrations scenario. Default value is NULL. Set <varname>mcontact_avp</varname> parameter ... modparam("registrar", "mcontact_avp", "$avp(register)") ...
<varname>attr_avp</varname> (string) AVP to store specific additional information for each registration. This information is read from the AVP and stored (in memory, db or both) at every registrar 'save'. When a registrar 'lookup' or 'is_registered' function is called, the attr_avp is populated with the value saved at [re]registration. When doing call forking, the avp will hold multiple values. The position of the corresponding attribute information in attr_avp is equal to the branch index. An example scenario is given below. Default value is NULL. Set <varname>attr_avp</varname> parameter # reading attributes from the attr_pvar when doing parallel forking ... modparam("registrar", "attr_avp", "$avp(attr)") ... if (is_method("REGISTER")) { $avp(attr) = "contact_info"; save("location"); exit; } ... lookup("location"); t_on_branch("branch_route_1"); ... branch_route[branch_route_1] { xlog("Attributes for branch $T_branch_idx: $(avp(attr)[$T_branch_idx])\n"); }
<varname>gruu_secret</varname> (string) The string that will be used in XORing when generating temporary GRUUs. If not set, 'OpenSIPS' is the default secret. Set <varname>gruu_secret</varname> parameter ... modparam("registrar", "gruu_secret", "top_secret") ...
<varname>disable_gruu</varname> (int) Globally disable GRUU handling Default value is 1 ( GRUU will not be handled ). Set <varname>gruu_secret</varname> parameter ... modparam("registrar", "disable_gruu", 0) ...
Exported Functions
<function moreinfo="none">save(domain [,flags ,[aor]])</function> The function processes a REGISTER message. It can add, remove or modify usrloc records depending on Contact and Expires HFs in the REGISTER message. On success, 200 OK will be returned listing all contacts that are currently in usrloc. On an error, error message will be send with a short description in reason phrase. Meaning of the parameters is as follows: domain - Logical domain within registrar. If database is used then this must be name of the table which stores the contacts. flags (optional)- string of the following flags: 'm' (Memory only) - save the contacts only in memory cache without no DB operation; 'r' (no Reply) - do not generate a SIP reply to the current REGISTER request. 's' (Socket header) - look into REGISTER request for a header which contains a socket description (proto:IP:port). This socket info will be stored by register instead of the received socket info. 'cnn' (max Contacts) - this flag can be used to limit the number of contacts for this AOR (Address of Record) in the user location database. Value 0 disables the check. This parameter overrides the global "max_contacts" module parameter. 'e(int)' (minimum expires) - this flag can be used to set minimum register expiration time. Values lower than this minimum will be automatically set to the minimum. Value 0 disables the checking. This parameter overrides the global "min_expires" module parameter. 'E(int)' (maximum expires) - this flag can be used to set maximum register expiration time. Values higher than this maximum will be automatically set to the maximum. Value 0 disables the checking. This parameter overrides the global "max_expires" module parameter. 'f' (force registration) - this flag can be used to force the registration of NEW contacts even if the maximum number of contacts is reached. In such a case, older contacts will be removed to make space to the new ones, without exceeding the maximum allowed number. This flag makes sense only if "cxx" is used. 'p0' (Path support - 'off' mode) The Path header is saved into usrloc, but is never included in the reply. 'p1' (Path support - lazy mode) The Path header is saved into usrloc, but is only included in the reply if path support is indicated in the registration request by the path option of the Supported header. 'p2' (Path support - strict mode) The path header is only saved into usrloc, if path support is indicated in the registration request by the path option of the Supported header. If no path support is indicated, the request is rejected with 420 - Bad Extension and the header Unsupported: path is included in the reply along with the received Path header. This mode is the one recommended by RFC-3327. 'v' (path receiVed) if set, the received parameter of the first Path URI of a registration is set as received-uri and the NAT branch flag is set for this contact. This is useful if the registrar is placed behind a SIP loadbalancer, which passes the nat'ed UAC address as received parameter in it's Path uri. This parameter is a string composed of a set of flags. aor (optional) - Variable contain a custom AOR; if missing, the AOR will be taken from the default place - the TO header URI. This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. <function>save</function> usage ... # save into 'location', no flags, use default AOR (TO URI) save("location"); ... # save into 'location', do not update DB, max 5 contacts per AOR, # use default AOR (TO URI) save("location","mc5"); ... # save into 'location', no flags, use as AOR the FROM URI save("location","","$fu"); ... # save into 'location',no DB update, no reply to send, take AOR from AVP save("location","mr", "$avp(aor)"); ...
<function moreinfo="none">remove(domain, AOR[, target])</function> Explicitly remove contacts behind a given address-of-record. Meaning of the parameters is as follows: domain - Logical domain within registrar. If database is used then this must be name of the table which stores the contacts. AOR - address-of-record to be searched (SIP URI) target (optional) - may be specified in two ways: single delete - Specific contact to be deleted (SIP URI) multi delete - IP/hostname of the AOR's contacts which will be deleted This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. <function>remove</function> usage ... # remove all contacts belonging to the "bob" AOR remove("location", "sip:bob@atlanta.com"); ... # remove only bob's home phone remove("location", "sip:bob@atlanta.com", "sip:bob@46.50.64.78"); ... # remove all bob's phones which are behind "50.60.50.60" $var(next_hop) = "50.60.50.60" remove("location", "sip:bob@atlanta.com", "$var(next_hop)"); ...
<function moreinfo="none">lookup(domain [, flags [, aor]])</function> The functions extracts username from Request-URI and tries to find all contacts for the username in usrloc. If there are no such contacts, -1 will be returned. If there are such contacts, Request-URI will be overwritten with the contact that has the highest q value and optionally the rest will be appended to the message (depending on append_branches parameter value). If the method_filtering option is enabled, the lookup function will return only the contacts that support the method of the processed request. Meaning of the parameters is as follows: domain - Name of table that should be used for the lookup. flags(optional) 'b' (no Branches) - this flag controls how lookup function processes multiple contacts. If there are multiple contacts for the given username in usrloc and this flag is not set, Request-URI will be overwritten with the highest-q rated contact and the rest will be appended to sip_msg structure and can be later used by tm for forking. If the flag is set, only Request-URI will be overwritten with the highest-q rated contact and the rest will be left unprocessed. 'r' (bRanch lookup) - this flag enables searching through existing branches for aor's and expanding them to contacts. For example, you have got AOR A in your ruri but you also want to forward your calls to AOR B. In order to do this, you must put AOR B in a branch, and if this flag enabled, the function will also expand AOR B to contacts, which will be put back into the branches. The AOR's that were in branches before the function call shall be removed. WARNING: if you want this flag activated, the 'b' (no Branches) flag must not be set, because by setting that flag you won't allow lookup to write in a branch. 'm' (Method filtering) - this flag tells if the contact filtering based on supported methods should be performed during lookup. 'u' (User-Agent filtering) - this flag enables regexp filtering by user-agent. It's useful with enabled append_branches parameter. Regexp must follow the 'u' flag and must use format like 'u/regexp/'. 'i' (Case insensitive search) - this flag enables case insensitive filtering for the 'u' flag. 'e' (Use extended regexp) - this flag enables using of extended regexp format for the 'u' flag. AOR (optional)- AOR to lookup for; if missing, the RURI is used as AOR; This must be a variable. Return codes: 1 - contacts found and returned. -1 - no contact found. -2 - contacts found, but method not supported. -3 - internal error during processing. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>lookup</function> usage ... lookup("location"); # simple lookup #or lookup("location","m"); # lookup with method filtering #or lookup("location","r"); #lookup with aor branch search; #all contacts except the first one shall be put #in the branches #or lookup("location","u/phone/i"); # lookup with user-agent filtering #or lookup("location","","$var(aor)"); # simple lookup with AOR from PV switch ($retcode) { case -1: case -3: sl_send_reply("404", "Not Found"); exit; case -2: sl_send_reply("405", "Not Found"); exit; }; ...
<function moreinfo="none">registered(domain [,AOR[, callid]])</function> The function returns true if an AOR is registered, false otherwise. The function does not modify the message being process. NOTE: if called for a reply (from onreply_route), you must pass an AOR (as parameter), otherwise the function will fail. Meaning of the parameters is as follows: domain - Name of table that should be used for the lookup. AOR (optional)- AOR to lookup for; if missing, the RURI is used as AOR; This must be a variable. callid (optional)- callid to check if a contact if registered with this callid (this may help you to make distinction between newly registered contact (callid not registered so far) and re-registration (callid already registered). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE. WARNING: THIS FUNCTION IS DEPRECATED! USE is_registered() INSTEAD. <function>registered</function> usage ... if (registered("location")) { sl_send_reply("100", "Trying"); ... }; if (registered("location","$fu")) { xlog("caller is registered\n"); } ...
<function moreinfo="none">is_other_contact(domain , IPs)</function> This function returns true if there are any other contacts registered, different from the ones from the IPs avp. Meaning of the parameters is as follows: domain - Name of table that should be used for contacts lookup. IPs - an AVP containing the IP's that will be compared with the contacts from usrloc. This function can be used from REQUEST_ROUTE. WARNING: THIS FUNCTION IS DEPRECATED! USE is_ip_registered() INSTEAD. <function>is_other_contact</function> usage ... if (is_other_contact("location", "$avp(ips)")) { sl_send_reply("403", "Forbidden"); ... }; ...
<function moreinfo="none">is_registered(domain ,[AOR])</function> The function returns true if an AOR is registered, false otherwise. The function does not modify the message being process. Meaning of the parameters is as follows: domain - Name of table that should be used for the lookup. AOR (optional)- AOR to lookup for; if missing, the source if the AOR is the "To" header for REGISTER request, "From" header for any other sip request. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE. <function>is_registered</function> usage ... /**/ if (is_method("REGISTER")) { /* automatically uses the URI from the To header */ if (is_registered("location")) { xlog("this AOR is registered\n") ... } }; /* check the From uri whether this aor is registered or not */ if (is_registered("location","$fu")) { xlog("caller is registered\n"); } ...
<function moreinfo="none">is_contact_registered(domain ,[AOR],[contact],[callid])</function> The function returns true if a contact and/or a callid from a certain AOR is registered, false otherwise. The function does not modify the message being process. Meaning of the parameters is as follows: domain - Name of table that should be used for the lookup. AOR (optional)- AOR to lookup for; if missing, the source if the AOR is the "To" header for REGISTER request, "From" header for any other sip request. contact (optional)- callid to check if a contact if registered with this callid (this may help you to make distinction between newly registered contact (callid not registered so far) and re-registration (callid already registered). callid (optional)- callid to check if a contact if registered with this callid (this may help you to make distinction between newly registered contact (callid not registered so far) and re-registration (callid already registered). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE. <function>is_contact_registered</function> usage ... /*let's say you want to block users that are not registered*/ if (is_method("INVITE")) { if (!is_contact_registered("location")) { sl_send_reply("401", "Unauthorized"); ... } } /* you want to check the second contact from the message whether it is registered or not */ if(is_method("INVITE")) { if (is_contact_registered("location","$fu","$(ct[1])",)) xlog("caller is registered\n"); } ...
<function moreinfo="none">is_ip_registered(domain ,[AOR],IPvar)</function> The function returns true if at least a contact has an IP from the IPavp in it's received parameter,otherwise returns false. The function does not modify the message being process. This function replaces the "is_other_contact" function. Meaning of the parameters is as follows: domain - Name of table that should be used for the lookup. AOR (optional)- AOR to lookup for; if missing, the source if the AOR is the "To" header for REGISTER request, "From" header for any other sip request. IPvar the pvar containing the IPs you want to check the received parameter of the contacts in the AOR against. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE. <function>is_ip_registered</function> usage ... /* check the source ip whether it is already registered */ if (is_method("REGISTER")) { if (is_ip_registered("location","$tu","$si")) { xlog("already registered from this ip\n"); ... } }; ...
<function moreinfo="none">add_sock_hdr(hdr_name)</function> Adds to the current REGISTER request a new header with hdr_name which contains the description of the received socket (proto:ip:port) This makes sense only in multiple replicated servers scenarios. Meaning of the parameters is as follows: hdr_name - header name to be used. This function can be used from REQUEST_ROUTE. <function>add_sock_hdr</function> usage ... add_sock_hdr("Sock-Info"); ...
Exported Statistics
<varname>max_expires</varname> Value of max_expires parameter.
<varname>max_contacts</varname> The value of max_contacts parameter.
<varname>defaults_expires</varname> The value of default_expires parameter.
<varname>accepted_regs</varname> Number of accepted registrations.
<varname>rejected_regs</varname> Number of rejected registrations.
opensips-2.2.2/modules/registrar/doc/registrar_faq.xml000066400000000000000000000107121300170765700231560ustar00rootroot00000000000000 &faqguide; What happened with the old append_branch module parameter? It was removed as global option, as the lookup function takes this option via the flag "b" (append Branches) See the documentation of the lookup function. What happened with the old method_filtering module parameter? It was removed as global option, as the lookup function takes this option via the flag "m" (Method filtering) See the documentation of the lookup function. What happened with the old sock_flag module parameter? It was removed as global option, as the save function takes this option via the flag "s" (Socket header) See the documentation of the save function. What happened with the old use_path and path_mode module parameters? They were removed as global option, as the save function takes these options via the flag "px" (path support) See the documentation of the save function. What happened with the old path_use_received module parameter? It was removed as global option, as the save function takes this option via the flag "v" (path receiVed) See the documentation of the save function. What happened with the old nat_flag module parameter? It was removed, as the module internally loads this value from the USRLOC module (see the nat_bflag USRLOC parameter). What happened with the old use_domain module parameter? It was removed, as the module internally loads this option from the USRLOC module. This was done in order to simplify the configuration. What happened with the old save_noreply and save_memory functions? There functions were merged into the new save(domain,flags) functions. If a reply should be sent or if the DB should be updated also is controlled via the flags. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. What happened to the desc_time_order parameter? It was removed, as its functionality was mmigrate into usrloc module, were there is a parameter with the same name. opensips-2.2.2/modules/registrar/lookup.c000066400000000000000000000431301300170765700205130ustar00rootroot00000000000000/* * Lookup contacts in usrloc * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-03-12 added support for zombie state (nils) */ /*! * \file * \brief SIP registrar module - lookup contacts in usrloc * \ingroup registrar */ #include #include "../../ut.h" #include "../../dset.h" #include "../../str.h" #include "../../config.h" #include "../../action.h" #include "../../mod_fix.h" #include "../../parser/parse_rr.h" #include "../usrloc/usrloc.h" #include "../../parser/parse_from.h" #include "common.h" #include "regtime.h" #include "reg_mod.h" #include "lookup.h" #include "sip_msg.h" #define GR_E_PART_SIZE 22 #define GR_A_PART_SIZE 14 #define allowed_method(_msg, _c, _f) \ ( !((_f)®_LOOKUP_METHODFILTER_FLAG) || \ ((_msg)->REQ_METHOD)&((_c)->methods) ) #define ua_re_check(return) \ if (flags & REG_LOOKUP_UAFILTER_FLAG) { \ if (regexec(&ua_re, ptr->user_agent.s, 1, &ua_match, 0)) { \ return; \ } \ } unsigned int nbranches; static char urimem[MAX_BRANCHES-1][MAX_URI_SIZE]; static str branch_uris[MAX_BRANCHES-1]; /*! \brief * Lookup contact in the database and rewrite Request-URI * \return: -1 : not found * -2 : found but method not allowed * -3 : error */ int lookup(struct sip_msg* _m, char* _t, char* _f, char* _s) { unsigned int flags; urecord_t* r; str aor, uri; ucontact_t* ptr,*it; int res; int ret; str path_dst; str flags_s; char* ua = NULL; char* re_end = NULL; int re_len = 0; char tmp; regex_t ua_re; int regexp_flags = 0; regmatch_t ua_match; pv_value_t val; int_str istr; str sip_instance = {0,0},call_id = {0,0}; /* branch index */ int idx; /* temporary branch values*/ int tlen; char *turi; qvalue_t tq; flags = 0; if (_f && _f[0]!=0) { if (fixup_get_svalue( _m, (gparam_p)_f, &flags_s)!=0) { LM_ERR("invalid owner uri parameter"); return -1; } for( res=0 ; res< flags_s.len ; res++ ) { switch (flags_s.s[res]) { case 'm': flags |= REG_LOOKUP_METHODFILTER_FLAG; break; case 'b': flags |= REG_LOOKUP_NOBRANCH_FLAG; break; case 'r': flags |= REG_BRANCH_AOR_LOOKUP_FLAG; break; case 'u': if (flags_s.s[res+1] != '/') { LM_ERR("no regexp after 'u' flag"); break; } res++; if ((re_end = strrchr(flags_s.s+res+1, '/')) == NULL) { LM_ERR("no regexp after 'u' flag"); break; } res++; re_len = re_end-flags_s.s-res; if (re_len == 0) { LM_ERR("empty regexp"); break; } ua = flags_s.s+res; flags |= REG_LOOKUP_UAFILTER_FLAG; LM_DBG("found regexp /%.*s/", re_len, ua); res += re_len; break; case 'i': regexp_flags |= REG_ICASE; break; case 'e': regexp_flags |= REG_EXTENDED; break; default: LM_WARN("unsupported flag %c \n",flags_s.s[res]); } } } if (flags®_BRANCH_AOR_LOOKUP_FLAG) { /* extract all the branches for further usage */ nbranches = 0; while ( (turi=get_branch(nbranches, &tlen, &tq, NULL, NULL, NULL, NULL)) ) { /* copy uri */ branch_uris[nbranches].s = urimem[nbranches]; if (tlen) { memcpy(branch_uris[nbranches].s, turi, tlen); branch_uris[nbranches].len = tlen; } else { *branch_uris[nbranches].s = '\0'; branch_uris[nbranches].len = 0; } nbranches++; } clear_branches(); idx=0; } if (_s) { if (pv_get_spec_value( _m, (pv_spec_p)_s, &val)!=0) { LM_ERR("failed to get PV value\n"); return -1; } if ( (val.flags&PV_VAL_STR)==0 ) { LM_ERR("PV vals is not string\n"); return -1; } uri = val.rs; } else { if (_m->new_uri.s) uri = _m->new_uri; else uri = _m->first_line.u.request.uri; } if (extract_aor(&uri, &aor,&sip_instance,&call_id) < 0) { LM_ERR("failed to extract address of record\n"); return -3; } get_act_time(); ul.lock_udomain((udomain_t*)_t, &aor); res = ul.get_urecord((udomain_t*)_t, &aor, &r); if (res > 0) { LM_DBG("'%.*s' Not found in usrloc\n", aor.len, ZSW(aor.s)); ul.unlock_udomain((udomain_t*)_t, &aor); return -1; } if (flags & REG_LOOKUP_UAFILTER_FLAG) { tmp = *(ua+re_len); *(ua+re_len) = '\0'; if (regcomp(&ua_re, ua, regexp_flags) != 0) { LM_ERR("bad regexp '%s'\n", ua); *(ua+re_len) = tmp; return -1; } *(ua+re_len) = tmp; } ptr = r->contacts; ret = -1; /* look first for an un-expired and suported contact */ search_valid_contact: while ( (ptr) && !(VALID_CONTACT(ptr,act_time) && (ret=-2) && allowed_method(_m,ptr,flags))) ptr = ptr->next; if (ptr==0) { /* nothing found */ LM_DBG("nothing found !\n"); goto done; } ua_re_check( ret = -1; ptr = ptr->next; goto search_valid_contact ); if (sip_instance.len && sip_instance.s) { LM_DBG("ruri has gruu in lookup\n"); /* uri has GRUU */ if (ptr->instance.len-2 != sip_instance.len || memcmp(ptr->instance.s+1,sip_instance.s,sip_instance.len)) { LM_DBG("no match to sip instace - [%.*s] - [%.*s]\n",ptr->instance.len-2,ptr->instance.s+1, sip_instance.len,sip_instance.s); /* not the targeted instance, search some more */ ptr = ptr->next; goto search_valid_contact; } LM_DBG("matched sip instace\n"); } if (call_id.len && call_id.s) { /* decide whether GRUU is expired or not * * first - match call-id */ if (ptr->callid.len != call_id.len || memcmp(ptr->callid.s,call_id.s,call_id.len)) { LM_DBG("no match to call id - [%.*s] - [%.*s]\n",ptr->callid.len,ptr->callid.s, call_id.len,call_id.s); ptr = ptr->next; goto search_valid_contact; } /* matched call-id, check if there are newer contacts with * same sip instace bup newer last_modified */ it = ptr->next; while ( it ) { if (VALID_CONTACT(it,act_time)) { if (it->instance.len-2 == sip_instance.len && sip_instance.s && memcmp(it->instance.s+1,sip_instance.s,sip_instance.len) == 0) if (it->last_modified > ptr->last_modified) { /* same instance id, but newer modified -> expired GRUU, no match at all */ break; } } it=it->next; } if (it != NULL) { ret = -1; goto done; } } LM_DBG("found a complete match\n"); ret = 1; if (ptr) { LM_DBG("setting as ruri <%.*s>\n",ptr->c.len,ptr->c.s); if (set_ruri(_m, &ptr->c) < 0) { LM_ERR("unable to rewrite Request-URI\n"); ret = -3; goto done; } /* If a Path is present, use first path-uri in favour of * received-uri because in that case the last hop towards the uac * has to handle NAT. - agranig */ if (ptr->path.s && ptr->path.len) { if (get_path_dst_uri(&ptr->path, &path_dst) < 0) { LM_ERR("failed to get dst_uri for Path\n"); ret = -3; goto done; } if (set_path_vector(_m, &ptr->path) < 0) { LM_ERR("failed to set path vector\n"); ret = -3; goto done; } if (set_dst_uri(_m, &path_dst) < 0) { LM_ERR("failed to set dst_uri of Path\n"); ret = -3; goto done; } } else if (ptr->received.s && ptr->received.len) { if (set_dst_uri(_m, &ptr->received) < 0) { ret = -3; goto done; } } set_ruri_q( _m, ptr->q); setbflag( _m, 0, ptr->cflags); if (ptr->sock) _m->force_send_socket = ptr->sock; /* populate the 'attributes' avp */ if (attr_avp_name != -1) { istr.s = ptr->attr; if (add_avp_last(AVP_VAL_STR, attr_avp_name, istr) != 0) { LM_ERR("Failed to populate attr avp!\n"); } } ptr = ptr->next; } /* Append branches if enabled */ /* If we got to this point and the URI had a ;gr parameter and it was matched * to a contact. No point in branching */ if ( flags®_LOOKUP_NOBRANCH_FLAG || (sip_instance.len && sip_instance.s) ) goto done; LM_DBG("looking for branches\n"); do { for( ; ptr ; ptr = ptr->next ) { if (VALID_CONTACT(ptr, act_time) && allowed_method(_m,ptr,flags)) { path_dst.len = 0; if(ptr->path.s && ptr->path.len && get_path_dst_uri(&ptr->path, &path_dst) < 0) { LM_ERR("failed to get dst_uri for Path\n"); continue; } ua_re_check(continue); /* The same as for the first contact applies for branches * regarding path vs. received. */ LM_DBG("setting branch <%.*s>\n",ptr->c.len,ptr->c.s); if (append_branch(_m,&ptr->c,path_dst.len?&path_dst:&ptr->received, &ptr->path, ptr->q, ptr->cflags, ptr->sock) == -1) { LM_ERR("failed to append a branch\n"); /* Also give a chance to the next branches*/ continue; } /* populate the 'attributes' avp */ if (attr_avp_name != -1) { istr.s = ptr->attr; if (add_avp_last(AVP_VAL_STR, attr_avp_name, istr) != 0) { LM_ERR("Failed to populate attr avp!\n"); } } } } /* 0 branches condition also filled; idx initially -1*/ if (!(flags®_BRANCH_AOR_LOOKUP_FLAG) || idx == nbranches) goto done; /* relsease old aor lock */ ul.unlock_udomain((udomain_t*)_t, &aor); ul.release_urecord(r, 0); /* idx starts from -1 */ uri = branch_uris[idx]; if (extract_aor(&uri, &aor, NULL, &call_id) < 0) { LM_ERR("failed to extract address of record for branch uri\n"); return -3; } /* release old urecord */ /* get lock on new aor */ LM_DBG("getting contacts from aor [%.*s]" "in branch %d\n", aor.len, aor.s, idx); ul.lock_udomain((udomain_t*)_t, &aor); res = ul.get_urecord((udomain_t*)_t, &aor, &r); if (res > 0) { LM_DBG("'%.*s' Not found in usrloc\n", aor.len, ZSW(aor.s)); goto done; } idx++; ptr = r->contacts; } while (1); done: ul.release_urecord(r, 0); ul.unlock_udomain((udomain_t*)_t, &aor); if (flags & REG_LOOKUP_UAFILTER_FLAG) { regfree(&ua_re); } return ret; } struct to_body* select_uri(struct sip_msg* _m) { if (_m->REQ_METHOD != METHOD_REGISTER) { if (parse_from_header(_m) < 0) { LM_ERR("failed to parse from!\n"); return NULL; } return get_from(_m); } else { /* WARNING in msg_aor_parse the to header is checked in * parse_message so no need to check it; take care when * you use this function */ return get_to(_m); } } /* * shall be done for all three functions * so why not use a macro * * USABLE VARS: * ud - udomain_t * aor - extracted aor */ #define IS_FOUND 1 #define NOT_FOUND -1 #define CHECK_DOMAIN(__d) \ if (!__d) { \ LM_ERR("no domain specified!\n"); \ return -2; \ } int msg_aor_parse(struct sip_msg* _m, char *_aor, str *_saor) { str uri, aor; pv_value_t val; struct to_body *hdr; if (parse_message(_m) < 0) { LM_ERR("unable to parse message\n"); return -2; } /* we don't process replies */ if (_m->first_line.type != SIP_REQUEST) { LM_ERR("message should be a request!\n"); return -2; } if (!_aor) { hdr=select_uri(_m); if (!hdr) { LM_ERR("failed to get uri header!\n"); return -2; } uri = hdr->uri; } else { if (pv_get_spec_value(_m, (pv_spec_p)_aor, &val)) { LM_ERR("failed to get aor PV value!\n"); return -1; } if ((val.flags&PV_VAL_STR) == 0) { LM_ERR("aor PV vals is not string\n"); return -1; } uri = val.rs; } if (extract_aor(&uri, &aor, 0, 0) < 0) { LM_ERR("failed to extract address of record!\n"); return -2; } *_saor = aor; return 0; } /*! \brief the is_registered() function * Return 1 if the AOR is registered, -1 otherwise * AOR comes from: * - "from" header on REGISTER * - "to" header on any other SIP REQUEST * - aor parameter of the function */ int is_registered(struct sip_msg* _m, char *_d, char* _a) { int ret=NOT_FOUND; urecord_t* r; ucontact_t *c; udomain_t* ud = (udomain_t*)_d; int_str istr; str aor; if (msg_aor_parse(_m, _a, &aor)) { LM_ERR("failed to parse!\n"); return -1; } CHECK_DOMAIN(ud); get_act_time(); LM_DBG("checking aor <%.*s>\n",aor.len,aor.s); ul.lock_udomain(ud, &aor); if (ul.get_urecord(ud, &aor, &r) == 0) { for ( c=r->contacts; c && (ret==NOT_FOUND); c=c->next ) { if (VALID_CONTACT(c,act_time)) { /* populate the 'attributes' avp */ if (attr_avp_name != -1) { istr.s = c->attr; if (add_avp_last(AVP_VAL_STR, attr_avp_name, istr) != 0) { LM_ERR("Failed to populate attr avp!\n"); } } ret = IS_FOUND; } } } ul.unlock_udomain(ud, &aor); return ret; } /*! \brief the is_contact_registered() function * Return 1 if the contact and/or callid is registered * for a given AOR, -1 when not found * AOR comes from: * - "from" header on REGISTER * - "to" header on any other SIP REQUEST * - aor parameter of the function * * Contact comes from: * - first valid "Contact" header when neither contact nor * callid params are provided * - the contact parameter (third parameter) */ int is_contact_registered(struct sip_msg* _m, char *_d, char* _a, char* _c, char* _cid) { int exp; str aor; str curi, callid; udomain_t* ud = (udomain_t*)_d; urecord_t* r; contact_t* ct; ucontact_t *c; if (msg_aor_parse(_m, _a, &aor)) { LM_ERR("failed to parse!\n"); return -1; } CHECK_DOMAIN(ud); if (!_c && !_cid) { LM_DBG("Neither contact nor callid supplied!" "First valid contact from the message body shall be used!\n"); if (!_m->contact || !(ct=(((contact_body_t*)_m->contact->parsed)->contacts))) goto out_no_contact; /* getting first non expired contact */ while (ct) { calc_contact_expires(_m, ct->expires, &exp, NULL); if (exp) break; ct = ct->next; } if (!ct) goto out_no_contact; curi = ct->uri; } else { if (_c) { if (fixup_get_svalue(_m, (gparam_p)_c, &curi) != 0) { LM_ERR("failed to retrieve contact value from pv!\n"); return -1; } } if (_cid) { if (fixup_get_svalue(_m, (gparam_p)_cid, &callid) != 0) { LM_ERR("failed to retrieve contact value from pv!\n"); return -1; } } } ul.lock_udomain(ud, &aor); if (ul.get_urecord(ud, &aor, &r) == 1) { LM_DBG("%.*s not found in usrloc!\n", aor.len, aor.s); ul.unlock_udomain(ud, &aor); return NOT_FOUND; } /* callid not defined; contact might be defined or not */ if (!_cid) { for (c=r->contacts; c; c=c->next) { if (!str_strcmp(&curi, &c->c)) goto out_found_unlock; } /* contact not defined; callid defined */ } else if (!_c && _cid) { for (c=r->contacts; c; c=c->next) { if (!str_strcmp(&callid, &c->callid)) goto out_found_unlock; } /* both callid and contact defined */ } else { for (c=r->contacts; c; c=c->next) { if (!str_strcmp(&curi, &c->c) && !str_strcmp(&callid, &c->callid)) goto out_found_unlock; } } ul.unlock_udomain(ud, &aor); return NOT_FOUND; out_no_contact: LM_WARN("Contact and callid not provided!" "Message does not have any valid contacts!\n"); return -1; out_found_unlock: ul.unlock_udomain(ud, &aor); return IS_FOUND; } /*! \brief the is_ip_registered() function * Return 1 if the IPs are registered for the received parameter * for a contact inside the given AOR * -1 when not found * * IPs comes from: * - the IPs avp given as a third parameter */ int is_ip_registered(struct sip_msg* _m, char* _d, char* _a, char *_out_pv) { str aor; str host, pv_host={NULL, 0}; int start; char is_avp=1; udomain_t* ud = (udomain_t*)_d; urecord_t* r; ucontact_t *c; struct usr_avp *avp; pv_spec_p spec = (pv_spec_p)_out_pv; pv_value_t val; if (msg_aor_parse(_m, _a, &aor)) { LM_ERR("failed to parse!\n"); return -2; } CHECK_DOMAIN(ud); if (spec == NULL) { LM_NOTICE("nothing to compare! exiting...\n"); return -1; } else if (spec->type != PVT_AVP) { is_avp=0; if (pv_get_spec_value( _m, spec, &val)!=0) { LM_ERR("failed to get IP PV value!\n"); return -1; } if ((val.flags&PV_VAL_STR)==0) { LM_ERR("IP should be a string!\n"); return -1; } pv_host = val.rs; } ul.lock_udomain(ud, &aor); if (ul.get_urecord(ud, &aor, &r) == 1) { LM_DBG("no contact found for aor=<%.*s>\n", aor.len, aor.s); goto out_unlock_notfound; } for (c=r->contacts; c; c=c->next) { if (!c->received.len || !c->received.s || c->received.len < 4) continue; /* 'sip:' or 'sips:' ?; is this sane? FIXME if problems */ start = c->received.s[3]==':'?4:5; host.s = c->received.s + start; host.len = c->received.len - start; if (!is_avp) { if (pv_host.len <= host.len && !memcmp(host.s, pv_host.s, pv_host.len)) goto out_unlock_found; /* skip the part with avp */ continue; } avp = NULL; while ((avp=search_first_avp(spec->pvp.pvn.u.isname.type, spec->pvp.pvn.u.isname.name.n, (int_str*)&pv_host,avp))) { if (!(avp->flags&AVP_VAL_STR)) { LM_NOTICE("avp value should be string\n"); continue; } if (pv_host.len <= host.len && !memcmp(host.s, pv_host.s, pv_host.len)) goto out_unlock_found; } } out_unlock_notfound: ul.unlock_udomain(ud, &aor); return NOT_FOUND; out_unlock_found: ul.unlock_udomain(ud, &aor); return IS_FOUND; } #undef CHECK_DOMAIN #undef IS_FOUND #undef NOT_FOUND /*! \brief the registered() function * Return true if the AOR in the Request-URI is registered, * it is similar to lookup but registered neither rewrites * the Request-URI nor appends branches */ int registered(struct sip_msg* _m, char* _t, char* _s, char *_c) { LM_WARN("Deprecated! Use is_contact_registered() instead!\n"); return is_contact_registered(_m, _t, _s, NULL, _c); } opensips-2.2.2/modules/registrar/lookup.h000066400000000000000000000047701300170765700205270ustar00rootroot00000000000000/* * Lookup contacts in usrloc * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief SIP registrar module - lookup contacts in usrloc * \ingroup registrar */ #ifndef LOOKUP_H #define LOOKUP_H #include "../../parser/msg_parser.h" /*! \brief * Lookup a contact in usrloc and rewrite R-URI if found */ int lookup(struct sip_msg* _m, char* _table, char* _flags, char* _aor); /*! \brief * Return true if the AOR in the Request-URI is registered, * it is similar to lookup but registered neither rewrites * the Request-URI nor appends branches */ int registered(struct sip_msg* _m, char* _t, char* _s, char* _c); /*! \brief the is_registered() function * Return 1 if the AOR is registered, -1 otherwise * AOR comes from: * - "from" header on REGISTER * - "to" header on any other SIP REQUEST * - aor parameter of the function */ int is_registered(struct sip_msg* _m, char *_d, char* _a); /*! \brief the is_contact_registered() function * Return 1 if the contact and/or callid is registered * for a given AOR, -1 when not found * AOR comes from: * - "from" header on REGISTER * - "to" header on any other SIP REQUEST * - aor parameter of the function * * Contact comes from: * - first valid "Contact" header when neither contact nor * callid params are provided * - the contact parameter (third parameter) */ int is_contact_registered(struct sip_msg* _m, char *_d, char* _a, char* _c, char* _cid); /*! \brief the is_ip_registered() function * Return 1 if the IPs are registered for the received parameter * for a contact inside the given AOR * -1 when not found * * IPs comes from: * - the IPs avp given as a third parameter */ int is_ip_registered(struct sip_msg* _m, char* _d, char* _a, char *_avp); #endif /* LOOKUP_H */ opensips-2.2.2/modules/registrar/path.c000066400000000000000000000056161300170765700201450ustar00rootroot00000000000000/* * Helper functions for Path support. * * Copyright (C) 2006 Andreas Granig * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief SIP registrar module - Helper functions for Path support * \ingroup registrar */ #include "../../data_lump.h" #include "../../parser/parse_rr.h" #include "../../parser/parse_uri.h" #include "path.h" #include "reg_mod.h" /*! \brief * Combines all Path HF bodies into one string. */ int build_path_vector(struct sip_msg *_m, str *path, str *received, unsigned int flags) { static char buf[MAX_PATH_SIZE]; char *p; struct hdr_field *hdr; struct sip_uri puri; rr_t *route = 0; path->len = 0; path->s = 0; received->s = 0; received->len = 0; if(parse_headers(_m, HDR_EOH_F, 0) < 0) { LM_ERR("failed to parse the message\n"); goto error; } for( hdr=_m->path,p=buf ; hdr ; hdr=hdr->sibling) { /* check for max. Path length */ if( p-buf+hdr->body.len+1 >= MAX_PATH_SIZE) { LM_ERR("Overall Path body exceeds max. length of %d\n", MAX_PATH_SIZE); goto error; } if(p!=buf) *(p++) = ','; memcpy( p, hdr->body.s, hdr->body.len); p += hdr->body.len; } if (p!=buf) { /* check if next hop is a loose router */ if (parse_rr_body( buf, p-buf, &route) < 0) { LM_ERR("failed to parse Path body, no head found\n"); goto error; } if (parse_uri(route->nameaddr.uri.s,route->nameaddr.uri.len,&puri)<0){ LM_ERR("failed to parse the first Path URI\n"); goto error; } if (!puri.lr.s) { LM_ERR("first Path URI is not a loose-router, not supported\n"); goto error; } if ( flags®_SAVE_PATH_RECEIVED_FLAG ) { param_hooks_t hooks; param_t *params; if (parse_params(&(puri.params),CLASS_CONTACT,&hooks,¶ms)!=0){ LM_ERR("failed to parse parameters of first hop\n"); goto error; } if (hooks.contact.received) *received = hooks.contact.received->body; /*for (;params; params = params->next) { if (params->type == P_RECEIVED) { *received = hooks.contact.received->body; break; } }*/ free_params(params); } free_rr(&route); } path->s = buf; path->len = p-buf; return 0; error: if(route) free_rr(&route); return -1; } opensips-2.2.2/modules/registrar/path.h000066400000000000000000000025351300170765700201470ustar00rootroot00000000000000/* * Helper functions for Path support. * * Copyright (C) 2006 Andreas Granig * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief SIP registrar module - helper functions for Path support * \ingroup registrar */ #ifndef REG_PATH_H #define REG_PATH_H #include "../../parser/msg_parser.h" /*! \brief * Extracts all Path header bodies into one string and * checks if first hop is a loose router. It also extracts * the received-param of the first hop if path_use_received is 1. */ int build_path_vector(struct sip_msg *_m, str *path, str *received, unsigned int flags); #endif /* REG_PATH_H */ opensips-2.2.2/modules/registrar/reg_mod.c000066400000000000000000000377411300170765700206310ustar00rootroot00000000000000/* * Registrar module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-21 save_noreply added, provided by Maxim Sobolev * (janakj) * 2005-07-11 added sip_natping_flag for nat pinging with SIP method * instead of UDP package (bogdan) * 2006-09-19 AOR may be provided via an AVP instead of being fetched * from URI (bogdan) * 2006-10-04 removed the "desc_time_order" parameter, as its functionality * was moved to usrloc (Carsten Bock, BASIS AudioNet GmbH) * 2006-11-22 save_noreply and save_memory merged into save(); * removed the module parameter "use_domain" - now it is * imported from usrloc module (bogdan) * 2006-11-28 Added statistics tracking for the number of accepted/rejected * registrations, as well as for the max expiry time, max * contacts and default expiry time(Jeffrey Magder-SOMA Networks) * 2007-02-24 sip_natping_flag moved into branch flags, so migrated to * nathelper module (bogdan) * */ /*! * \defgroup registrar SIP Registrar support * The module contains REGISTER processing logic. */ /*! * \file * \brief SIP registrar module - interface * \ingroup registrar */ #include #include "../../sr_module.h" #include "../../timer.h" #include "../../dprint.h" #include "../../error.h" #include "../../socket_info.h" #include "../../pvar.h" #include "../usrloc/ul_mod.h" #include "../signaling/signaling.h" #include "../../mod_fix.h" #include "save.h" #include "lookup.h" #include "reply.h" #include "reg_mod.h" /*! \brief Module init & destroy function */ static int mod_init(void); static int child_init(int); static void mod_destroy(void); /*! \brief Fixup functions */ static int registrar_fixup(void** param, int param_no); static int fixup_remove(void** param, int param_no); /*! \brief Functions */ static int add_sock_hdr(struct sip_msg* msg, char *str, char *foo); static int fixup_is_registered(void **param, int param_no); static int fixup_is_aor_registered(void **param, int param_no); static int fixup_is_contact_registered(void **param, int param_no); static int fixup_is_ip_registered(void **param, int param_no); int default_expires = 3600; /*!< Default expires value in seconds */ qvalue_t default_q = Q_UNSPECIFIED; /*!< Default q value multiplied by 1000 */ int case_sensitive = 1; /*!< If set to 0, username in aor will be case insensitive */ int tcp_persistent_flag = -1; /*!< if the TCP connection should be kept open */ char *tcp_persistent_flag_s = 0; int min_expires = 60; /*!< Minimum expires the phones are allowed to use in seconds * use 0 to switch expires checking off */ int max_expires = 0; /*!< Maximum expires the phones are allowed to use in seconds, * use 0 to switch expires checking off */ int max_contacts = 0; /*!< Maximum number of contacts per AOR (0=no checking) */ int retry_after = 0; /*!< The value of Retry-After HF in 5xx replies */ char* rcv_avp_param = 0; unsigned short rcv_avp_type = 0; int rcv_avp_name; char* mct_avp_param = 0; unsigned short mct_avp_type = 0; int mct_avp_name; char* attr_avp_param = 0; unsigned short attr_avp_type = 0; int attr_avp_name; int reg_use_domain = 0; /*!< Realm prefix to be removed */ char* realm_pref = ""; str realm_prefix; str sock_hdr_name = {0,0}; str gruu_secret = {0,0}; int disable_gruu = 1; #define RCV_NAME "received" str rcv_param = str_init(RCV_NAME); stat_var *accepted_registrations; stat_var *rejected_registrations; stat_var *max_expires_stat; stat_var *max_contacts_stat; stat_var *default_expire_stat; /** SIGNALING binds */ struct sig_binds sigb; /** TM bind */ struct tm_binds tmb; /*! \brief * Exported functions */ static cmd_export_t cmds[] = { {"save", (cmd_function)save, 1, registrar_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, {"save", (cmd_function)save, 2, registrar_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, {"save", (cmd_function)save, 3, registrar_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, {"remove", (cmd_function)_remove, 2, fixup_remove, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, {"remove", (cmd_function)_remove, 3, fixup_remove, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, {"lookup", (cmd_function)lookup, 1, registrar_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"lookup", (cmd_function)lookup, 2, registrar_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"lookup", (cmd_function)lookup, 3, registrar_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"registered", (cmd_function)registered, 1, fixup_is_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"registered", (cmd_function)registered, 2, fixup_is_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"registered", (cmd_function)registered, 3, fixup_is_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"add_sock_hdr", (cmd_function)add_sock_hdr, 1, fixup_str_null, 0, REQUEST_ROUTE }, {"is_other_contact", (cmd_function)is_other_contact_f, 2, fixup_is_aor_registered/*same # of params*/, 0, REQUEST_ROUTE}, {"is_registered", (cmd_function)is_registered, 1, fixup_is_aor_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"is_registered", (cmd_function)is_registered, 2, fixup_is_aor_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"is_contact_registered", (cmd_function)is_contact_registered, 1, fixup_is_contact_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"is_contact_registered", (cmd_function)is_contact_registered, 2, fixup_is_contact_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"is_contact_registered", (cmd_function)is_contact_registered, 3, fixup_is_contact_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"is_contact_registered", (cmd_function)is_contact_registered, 4, fixup_is_contact_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"is_ip_registered", (cmd_function)is_ip_registered, 3, fixup_is_ip_registered, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; /*! \brief * Exported parameters */ static param_export_t params[] = { {"default_expires", INT_PARAM, &default_expires }, {"default_q", INT_PARAM, &default_q }, {"case_sensitive", INT_PARAM, &case_sensitive }, {"tcp_persistent_flag",INT_PARAM, &tcp_persistent_flag }, {"tcp_persistent_flag",STR_PARAM, &tcp_persistent_flag_s }, {"realm_prefix", STR_PARAM, &realm_pref }, {"min_expires", INT_PARAM, &min_expires }, {"max_expires", INT_PARAM, &max_expires }, {"received_param", STR_PARAM, &rcv_param }, {"received_avp", STR_PARAM, &rcv_avp_param }, {"max_contacts", INT_PARAM, &max_contacts }, {"retry_after", INT_PARAM, &retry_after }, {"sock_hdr_name", STR_PARAM, &sock_hdr_name.s }, {"mcontact_avp", STR_PARAM, &mct_avp_param }, {"attr_avp", STR_PARAM, &attr_avp_param }, {"gruu_secret", STR_PARAM, &gruu_secret.s }, {"disable_gruu", INT_PARAM, &disable_gruu }, {0, 0, 0} }; /*! \brief We expose internal variables via the statistic framework below.*/ static stat_export_t mod_stats[] = { {"max_expires", STAT_NO_RESET, &max_expires_stat }, {"max_contacts", STAT_NO_RESET, &max_contacts_stat }, {"default_expire", STAT_NO_RESET, &default_expire_stat }, {"accepted_regs", 0, &accepted_registrations }, {"rejected_regs", 0, &rejected_registrations }, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "usrloc", DEP_ABORT }, { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_DEFAULT, "tm", DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /*! \brief * Module exports structure */ struct module_exports exports = { "registrar", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ mod_stats, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, mod_destroy, /* destroy function */ child_init, /* Per-child init function */ }; /*! \brief * Initialize parent */ static int mod_init(void) { pv_spec_t avp_spec; str s; bind_usrloc_t bind_usrloc; LM_INFO("initializing...\n"); /* load SIGNALING API */ if(load_sig_api(&sigb)< 0) { LM_ERR("can't load signaling functions\n"); return -1; } /* load TM API */ memset(&tmb, 0, sizeof(struct tm_binds)); load_tm_api(&tmb); realm_prefix.s = realm_pref; realm_prefix.len = strlen(realm_pref); rcv_param.len = strlen(rcv_param.s); if (rcv_avp_param && *rcv_avp_param) { s.s = rcv_avp_param; s.len = strlen(s.s); if (pv_parse_spec(&s, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %s AVP definition\n", rcv_avp_param); return -1; } if(pv_get_avp_name(0, &avp_spec.pvp, &rcv_avp_name, &rcv_avp_type)!=0) { LM_ERR("[%s]- invalid AVP definition\n", rcv_avp_param); return -1; } } else { rcv_avp_name = -1; rcv_avp_type = 0; } if (mct_avp_param && *mct_avp_param) { s.s = mct_avp_param; s.len = strlen(s.s); if (pv_parse_spec(&s, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %s AVP definition\n", mct_avp_param); return -1; } if(pv_get_avp_name(0, &avp_spec.pvp, &mct_avp_name, &mct_avp_type)!=0) { LM_ERR("[%s]- invalid AVP definition\n", mct_avp_param); return -1; } } else { mct_avp_name = -1; mct_avp_type = 0; } if (attr_avp_param && *attr_avp_param) { s.s = attr_avp_param; s.len = strlen(s.s); if (pv_parse_spec(&s, &avp_spec)==0 || avp_spec.type!=PVT_AVP) { LM_ERR("malformed or non AVP %s AVP definition\n", attr_avp_param); return -1; } if(pv_get_avp_name(0, &avp_spec.pvp, &attr_avp_name, &attr_avp_type)!=0) { LM_ERR("[%s]- invalid AVP definition\n", attr_avp_param); return -1; } } else { attr_avp_name = -1; attr_avp_type = 0; } bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (!bind_usrloc) { LM_ERR("can't bind usrloc\n"); return -1; } /* Normalize default_q parameter */ if (default_q != Q_UNSPECIFIED) { if (default_q > MAX_Q) { LM_DBG("default_q = %d, lowering to MAX_Q: %d\n", default_q, MAX_Q); default_q = MAX_Q; } else if (default_q < MIN_Q) { LM_DBG("default_q = %d, raising to MIN_Q: %d\n", default_q, MIN_Q); default_q = MIN_Q; } } if (bind_usrloc(&ul) < 0) { return -1; } /* * Import use_domain parameter from usrloc */ reg_use_domain = ul.use_domain; if (sock_hdr_name.s) sock_hdr_name.len = strlen(sock_hdr_name.s); if (gruu_secret.s) gruu_secret.len = strlen(gruu_secret.s); /* fix the flags */ fix_flag_name(tcp_persistent_flag_s, tcp_persistent_flag); tcp_persistent_flag = get_flag_id_by_name(FLAG_TYPE_MSG, tcp_persistent_flag_s); tcp_persistent_flag = (tcp_persistent_flag!=-1)?(1<rcv.bind_address; if(si->adv_sock_str.len) { use_sock_str = si->adv_sock_str; } else { use_sock_str = si->sock_str; } if (parse_headers( msg, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse message\n"); goto error; } anchor = anchor_lump( msg, msg->unparsed-msg->buf, 0); if (anchor==0) { LM_ERR("can't get anchor\n"); goto error; } hdr.len = hdr_name->len + 2 + use_sock_str.len + CRLF_LEN; if ( (hdr.s=(char*)pkg_malloc(hdr.len))==0 ) { LM_ERR("no more pkg mem\n"); goto error; } p = hdr.s; memcpy( p, hdr_name->s, hdr_name->len); p += hdr_name->len; *(p++) = ':'; *(p++) = ' '; memcpy( p, use_sock_str.s, use_sock_str.len); p += use_sock_str.len; memcpy( p, CRLF, CRLF_LEN); p += CRLF_LEN; if ( p-hdr.s!=hdr.len ) { LM_CRIT("buffer overflow (%d!=%d)\n", (int)(long)(p-hdr.s),hdr.len); goto error1; } if (insert_new_lump_before( anchor, hdr.s, hdr.len, 0) == 0) { LM_ERR("can't insert lump\n"); goto error1; } return 1; error1: pkg_free(hdr.s); error: return -1; } /* * fixup for domain and aor */ static int fixup_is_registered(void **param, int param_no) { udomain_t *d; if (param_no == 1) { if (ul.register_udomain((char*)*param, &d) < 0) { LM_ERR("failed to register domain\n"); return E_UNSPEC; } *param = (void*)d; return 0; } return fixup_pvar(param); } static int fixup_is_aor_registered(void **param, int param_no) { if (param_no > 2) { LM_ERR("invalid param number\n"); return E_UNSPEC; } return fixup_is_registered(param, param_no); } static int fixup_is_contact_registered(void **param, int param_no) { if (param_no > 4) { LM_ERR("invalid param number\n"); return E_UNSPEC; } return fixup_is_registered(param, param_no); } static int fixup_is_ip_registered(void **param, int param_no) { if (param_no > 3) { LM_ERR("invalid param number\n"); return E_UNSPEC; } return fixup_is_registered(param, param_no); } opensips-2.2.2/modules/registrar/reg_mod.h000066400000000000000000000067051300170765700206320ustar00rootroot00000000000000/* * registrar module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * * 2005-07-11 added sip_natping_flag for nat pinging with SIP method * instead of UDP package (bogdan) * 2006-11-28 Added statistics tracking for the number of accepted/rejected * registrations, as well as for the max expiry time, max contacts, * and default expiry time. (Jeffrey Magder - SOMA Networks) * 2007-02-24 sip_natping_flag moved into branch flags, so migrated to * nathelper module (bogdan) */ /*! * \file * \brief SIP registrar module - interface * \ingroup registrar */ #ifndef REG_MOD_H #define REG_MOD_H #include "../../parser/msg_parser.h" #include "../../qvalue.h" #include "../../usr_avp.h" #include "../usrloc/usrloc.h" #include "../signaling/signaling.h" #include "../tm/tm_load.h" /* if DB support is used, this values must not exceed the * storage capacity of the DB columns! See db/schema/entities.xml */ #define CONTACT_MAX_SIZE 255 #define RECEIVED_MAX_SIZE 255 #define USERNAME_MAX_SIZE 64 #define DOMAIN_MAX_SIZE 64 #define CALLID_MAX_SIZE 255 #define UA_MAX_SIZE 255 #define PATH_MODE_STRICT 2 #define PATH_MODE_LAZY 1 #define PATH_MODE_OFF 0 #define REG_SAVE_MEMORY_FLAG (1<<0) #define REG_SAVE_NOREPLY_FLAG (1<<1) #define REG_SAVE_SOCKET_FLAG (1<<2) #define REG_SAVE_PATH_STRICT_FLAG (1<<3) #define REG_SAVE_PATH_LAZY_FLAG (1<<4) #define REG_SAVE_PATH_OFF_FLAG (1<<5) #define REG_SAVE_PATH_RECEIVED_FLAG (1<<6) #define REG_SAVE_FORCE_REG_FLAG (1<<7) #define REG_SAVE_PATH_FLAG (REG_SAVE_PATH_STRICT_FLAG|\ REG_SAVE_PATH_LAZY_FLAG|REG_SAVE_PATH_OFF_FLAG) #define REG_LOOKUP_METHODFILTER_FLAG (1<<0) #define REG_LOOKUP_NOBRANCH_FLAG (1<<1) #define REG_LOOKUP_UAFILTER_FLAG (1<<2) #define REG_BRANCH_AOR_LOOKUP_FLAG (1<<3) extern int default_expires; extern qvalue_t default_q; extern int case_sensitive; extern int nat_flag; extern int tcp_persistent_flag; extern int min_expires; extern int max_expires; extern int received_avp; extern int reg_use_domain; extern str realm_prefix; extern float def_q; extern unsigned short rcv_avp_type; extern int rcv_avp_name; extern unsigned short mct_avp_type; extern int mct_avp_name; extern unsigned short attr_avp_type; extern int attr_avp_name; extern str rcv_param; extern int max_contacts; extern int retry_after; extern str sock_hdr_name; usrloc_api_t ul; /*!< Structure containing pointers to usrloc functions */ extern struct sig_binds sigb; extern struct tm_binds tmb; extern stat_var *accepted_registrations; extern stat_var *rejected_registrations; #endif /* REG_MOD_H */ opensips-2.2.2/modules/registrar/regtime.c000066400000000000000000000017601300170765700206410ustar00rootroot00000000000000/* * Registrar time related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "regtime.h" time_t act_time; /*! \brief * Get actual time and store * value in act_time */ void get_act_time(void) { act_time = time(0); } opensips-2.2.2/modules/registrar/regtime.h000066400000000000000000000021721300170765700206440ustar00rootroot00000000000000/* * Registrar time related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief SIP registrar module - time related functions * \ingroup registrar */ #ifndef REGTIME_H #define REGTIME_H #include extern time_t act_time; /*! \brief * Get actual time and store * value in act_time */ void get_act_time(void); #endif /* REGTIME_H */ opensips-2.2.2/modules/registrar/reply.c000066400000000000000000000426041300170765700203420ustar00rootroot00000000000000/* * Send a reply * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-18: buffer overflow patch committed (Jan on behalf of Maxim) * 2003-01-21: Errors reported via Error-Info header field - janakj * 2003-09-11: updated to new build_lump_rpl() interface (bogdan) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) */ /*! * \file * \brief SIP registrar module - Send a reply * \ingroup registrar */ #include #include "../../ut.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_supported.h" #include "../../data_lump_rpl.h" #include "../usrloc/usrloc.h" #include "rerrno.h" #include "reg_mod.h" #include "regtime.h" #include "reply.h" #define MAX_CONTACT_BUFFER 1024 #define E_INFO "P-Registrar-Error: " #define E_INFO_LEN (sizeof(E_INFO) - 1) #define CONTACT_BEGIN "Contact: " #define CONTACT_BEGIN_LEN (sizeof(CONTACT_BEGIN) - 1) #define Q_PARAM ";q=" #define Q_PARAM_LEN (sizeof(Q_PARAM) - 1) #define EXPIRES_PARAM ";expires=" #define EXPIRES_PARAM_LEN (sizeof(EXPIRES_PARAM) - 1) #define SIP_PROTO "sip:" #define SIP_PROTO_SIZE (sizeof(SIP_PROTO) - 1) #define PUB_GRUU ";pub-gruu=" #define PUB_GRUU_SIZE (sizeof(PUB_GRUU) - 1) #define TEMP_GRUU ";temp-gruu=" #define TEMP_GRUU_SIZE (sizeof(TEMP_GRUU) - 1) #define SIP_INSTANCE ";+sip.instance=" #define SIP_INSTANCE_SIZE (sizeof(SIP_INSTANCE) - 1) #define TEMP_GRUU_HEADER "tgruu." #define TEMP_GRUU_HEADER_SIZE (sizeof(TEMP_GRUU_HEADER) - 1) #define GR_PARAM ";gr=" #define GR_PARAM_SIZE (sizeof(GR_PARAM) - 1) #define GR_NO_VAL ";gr" #define GR_NO_VAL_SIZE (sizeof(GR_NO_VAL) - 1) #define CONTACT_SEP ", " #define CONTACT_SEP_LEN (sizeof(CONTACT_SEP) - 1) extern str gruu_secret; extern int disable_gruu; str default_gruu_secret=str_init("0p3nS1pS"); /*! \brief * Buffer for Contact header field */ static struct { char* buf; int buf_len; int data_len; } contact = {0, 0, 0}; static inline int calc_temp_gruu_len(str* aor,str* instance,str *callid) { int time_len,temp_gr_len; int2str((unsigned long)act_time,&time_len); temp_gr_len = time_len + aor->len + instance->len - 2 + callid->len + 3; /* and blank spaces */ temp_gr_len = (temp_gr_len/3 + (temp_gr_len%3?1:0))*4; /* base64 encoding */ return temp_gr_len; } /*! \brief * Calculate the length of buffer needed to * print contacts */ static inline unsigned int calc_buf_len(ucontact_t* c,int build_gruu, struct sip_msg *_m) { unsigned int len; int qlen; struct socket_info *sock; len = 0; while(c) { if (VALID_CONTACT(c, act_time)) { if (len) len += CONTACT_SEP_LEN; len += 2 /* < > */ + c->c.len; qlen = len_q(c->q); if (qlen) len += Q_PARAM_LEN + qlen; len += EXPIRES_PARAM_LEN + INT2STR_MAX_LEN; if (c->received.s) { len += 1 /* ; */ + rcv_param.len + 1 /* = */ + 1 /* dquote */ + c->received.len + 1 /* dquote */ ; } if (build_gruu && c->instance.s) { sock = (c->sock)?(c->sock):(_m->rcv.bind_address); /* pub gruu */ len += PUB_GRUU_SIZE + 1 /* quote */ + SIP_PROTO_SIZE + c->aor->len + (reg_use_domain ?0:(1 /* @ */ + sock->name.len + 1 /* : */ + sock->port_no_str.len)) + GR_PARAM_SIZE + (c->instance.len - 2) + 1 /* quote */ ; /* temp gruu */ len += TEMP_GRUU_SIZE + 1 /* quote */ + SIP_PROTO_SIZE + TEMP_GRUU_HEADER_SIZE + calc_temp_gruu_len(c->aor,&c->instance,&c->callid) + 1 /* @ */ + sock->name.len + 1 /* : */ + sock->port_no_str.len + GR_NO_VAL_SIZE + 1 /* quote */ ; /* sip.instance */ len += SIP_INSTANCE_SIZE + 1 /* quote */ + (c->instance.len - 2) + 1 /* quote */ ; } } c = c->next; } if (len) len += CONTACT_BEGIN_LEN + CRLF_LEN; return len; } #define MAX_TEMP_GRUU_SIZE 255 static char temp_gruu_buf[MAX_TEMP_GRUU_SIZE]; /* Returns memory from a statically allocated buffer */ char * build_temp_gruu(str *aor,str *instance,str *callid,int *len) { int time_len,i; char *p; char *time_str = int2str((unsigned long)act_time,&time_len); str *magic; *len = time_len + aor->len + instance->len + callid->len + 3 - 2; /* +3 blank spaces, -2 discarded chars of instance in memcpy below */ p = temp_gruu_buf; memcpy(p,time_str,time_len); p+=time_len; *p++=' '; memcpy(p,aor->s,aor->len); p+=aor->len; *p++=' '; memcpy(p,instance->s+1,instance->len-2); p+=instance->len-2; *p++=' '; memcpy(p,callid->s,callid->len); LM_DBG("build temp gruu [%.*s]\n",*len,temp_gruu_buf); if (gruu_secret.s != NULL) magic = &gruu_secret; else magic = &default_gruu_secret; for (i=0;i<*len;i++) temp_gruu_buf[i] ^= magic->s[i%magic->len]; return temp_gruu_buf; } /*! \brief * Allocate a memory buffer and print Contact * header fields into it */ int build_contact(ucontact_t* c,struct sip_msg *_m) { char *p, *cp, *tmpgr; int fl, len,grlen; int build_gruu = 0; struct socket_info *sock; if (!disable_gruu && _m->supported && parse_supported(_m) == 0 && (get_supported(_m) & F_SUPPORTED_GRUU)) build_gruu=1; contact.data_len = calc_buf_len(c,build_gruu,_m); if (!contact.data_len) return 0; if (!contact.buf || (contact.buf_len < contact.data_len)) { if (contact.buf) pkg_free(contact.buf); contact.buf = (char*)pkg_malloc(contact.data_len); if (!contact.buf) { contact.data_len = 0; contact.buf_len = 0; LM_ERR("no pkg memory left\n"); return -1; } else { contact.buf_len = contact.data_len; } } p = contact.buf; memcpy(p, CONTACT_BEGIN, CONTACT_BEGIN_LEN); p += CONTACT_BEGIN_LEN; fl = 0; while(c) { if (VALID_CONTACT(c, act_time)) { if (fl) { memcpy(p, CONTACT_SEP, CONTACT_SEP_LEN); p += CONTACT_SEP_LEN; } else { fl = 1; } *p++ = '<'; memcpy(p, c->c.s, c->c.len); p += c->c.len; *p++ = '>'; len = len_q(c->q); if (len) { memcpy(p, Q_PARAM, Q_PARAM_LEN); p += Q_PARAM_LEN; memcpy(p, q2str(c->q, 0), len); p += len; } memcpy(p, EXPIRES_PARAM, EXPIRES_PARAM_LEN); p += EXPIRES_PARAM_LEN; cp = int2str((int)(c->expires - act_time), &len); memcpy(p, cp, len); p += len; if (c->received.s) { *p++ = ';'; memcpy(p, rcv_param.s, rcv_param.len); p += rcv_param.len; *p++ = '='; *p++ = '\"'; memcpy(p, c->received.s, c->received.len); p += c->received.len; *p++ = '\"'; } if (build_gruu && c->instance.s) { sock = (c->sock)?(c->sock):(_m->rcv.bind_address); /* build pub GRUU */ memcpy(p,PUB_GRUU,PUB_GRUU_SIZE); p += PUB_GRUU_SIZE; *p++ = '\"'; memcpy(p,SIP_PROTO,SIP_PROTO_SIZE); p += SIP_PROTO_SIZE; memcpy(p,c->aor->s,c->aor->len); p += c->aor->len; if (!reg_use_domain) { *p++ = '@'; memcpy(p,sock->name.s,sock->name.len); p += sock->name.len; *p++ = ':'; memcpy(p,sock->port_no_str.s,sock->port_no_str.len); p += sock->port_no_str.len; } memcpy(p,GR_PARAM,GR_PARAM_SIZE); p += GR_PARAM_SIZE; memcpy(p,c->instance.s+1,c->instance.len-2); p += c->instance.len-2; *p++ = '\"'; /* build temp GRUU */ memcpy(p,TEMP_GRUU,TEMP_GRUU_SIZE); p += TEMP_GRUU_SIZE; *p++ = '\"'; memcpy(p,SIP_PROTO,SIP_PROTO_SIZE); p += SIP_PROTO_SIZE; memcpy(p,TEMP_GRUU_HEADER,TEMP_GRUU_HEADER_SIZE); p += TEMP_GRUU_HEADER_SIZE; tmpgr = build_temp_gruu(c->aor,&c->instance,&c->callid,&grlen); base64encode((unsigned char *)p, (unsigned char *)tmpgr,grlen); p += calc_temp_gruu_len(c->aor,&c->instance,&c->callid); *p++ = '@'; memcpy(p,sock->name.s,sock->name.len); p += sock->name.len; *p++ = ':'; memcpy(p,sock->port_no_str.s,sock->port_no_str.len); p += sock->port_no_str.len; memcpy(p,GR_NO_VAL,GR_NO_VAL_SIZE); p += GR_NO_VAL_SIZE; *p++ = '\"'; /* build +sip.instance */ memcpy(p,SIP_INSTANCE,SIP_INSTANCE_SIZE); p += SIP_INSTANCE_SIZE; *p++ = '\"'; memcpy(p,c->instance.s+1,c->instance.len-2); p += c->instance.len-2; *p++ = '\"'; } } c = c->next; } memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; contact.data_len = p - contact.buf; LM_DBG("created Contact HF: %.*s\n", contact.data_len, contact.buf); return 0; } #define MSG_200 "OK" #define MSG_400 "Bad Request" #define MSG_420 "Bad Extension" #define MSG_500 "Server Internal Error" #define MSG_503 "Service Unavailable" #define EI_R_FINE "No problem" /* R_FINE */ #define EI_R_UL_DEL_R "usrloc_record_delete failed" /* R_UL_DEL_R */ #define EI_R_UL_GET_R "usrloc_record_get failed" /* R_UL_GET */ #define EI_R_UL_NEW_R "usrloc_record_new failed" /* R_UL_NEW_R */ #define EI_R_INV_CSEQ "Invalid CSeq number" /* R_INV_CSEQ */ #define EI_R_UL_INS_C "usrloc_contact_insert failed" /* R_UL_INS_C */ #define EI_R_UL_INS_R "usrloc_record_insert failed" /* R_UL_INS_R */ #define EI_R_UL_DEL_C "usrloc_contact_delete failed" /* R_UL_DEL_C */ #define EI_R_UL_UPD_C "usrloc_contact_update failed" /* R_UL_UPD_C */ #define EI_R_TO_USER "No username in To URI" /* R_TO_USER */ #define EI_R_AOR_LEN "Address Of Record too long" /* R_AOR_LEN */ #define EI_R_AOR_PARSE "Error while parsing AOR" /* R_AOR_PARSE */ #define EI_R_INV_EXP "Invalid expires param in contact" /* R_INV_EXP */ #define EI_R_INV_Q "Invalid q param in contact" /* R_INV_Q */ #define EI_R_PARSE "Message parse error" /* R_PARSE */ #define EI_R_TO_MISS "To header not found" /* R_TO_MISS */ #define EI_R_CID_MISS "Call-ID header not found" /* R_CID_MISS */ #define EI_R_CS_MISS "CSeq header not found" /* R_CS_MISS */ #define EI_R_PARSE_EXP "Expires parse error" /* R_PARSE_EXP */ #define EI_R_PARSE_CONT "Contact parse error" /* R_PARSE_CONT */ #define EI_R_STAR_EXP "* used in contact and expires is not zero" /* R_STAR__EXP */ #define EI_R_STAR_CONT "* used in contact and more than 1 contact" /* R_STAR_CONT */ #define EI_R_OOO "Out of order request" /* R_OOO */ #define EI_R_RETRANS "Retransmission" /* R_RETRANS */ #define EI_R_UNESCAPE "Error while unescaping username" /* R_UNESCAPE */ #define EI_R_TOO_MANY "Too many registered contacts" /* R_TOO_MANY */ #define EI_R_CONTACT_LEN "Contact/received too long" /* R_CONTACT_LEN */ #define EI_R_CALLID_LEN "Callid too long" /* R_CALLID_LEN */ #define EI_R_PARSE_PATH "Path parse error" /* R_PARSE_PATH */ #define EI_R_PATH_UNSUP "No support for found Path indicated" /* R_PATH_UNSUP */ str error_info[] = { {EI_R_FINE, sizeof(EI_R_FINE) - 1}, {EI_R_UL_DEL_R, sizeof(EI_R_UL_DEL_R) - 1}, {EI_R_UL_GET_R, sizeof(EI_R_UL_GET_R) - 1}, {EI_R_UL_NEW_R, sizeof(EI_R_UL_NEW_R) - 1}, {EI_R_INV_CSEQ, sizeof(EI_R_INV_CSEQ) - 1}, {EI_R_UL_INS_C, sizeof(EI_R_UL_INS_C) - 1}, {EI_R_UL_INS_R, sizeof(EI_R_UL_INS_R) - 1}, {EI_R_UL_DEL_C, sizeof(EI_R_UL_DEL_C) - 1}, {EI_R_UL_UPD_C, sizeof(EI_R_UL_UPD_C) - 1}, {EI_R_TO_USER, sizeof(EI_R_TO_USER) - 1}, {EI_R_AOR_LEN, sizeof(EI_R_AOR_LEN) - 1}, {EI_R_AOR_PARSE, sizeof(EI_R_AOR_PARSE) - 1}, {EI_R_INV_EXP, sizeof(EI_R_INV_EXP) - 1}, {EI_R_INV_Q, sizeof(EI_R_INV_Q) - 1}, {EI_R_PARSE, sizeof(EI_R_PARSE) - 1}, {EI_R_TO_MISS, sizeof(EI_R_TO_MISS) - 1}, {EI_R_CID_MISS, sizeof(EI_R_CID_MISS) - 1}, {EI_R_CS_MISS, sizeof(EI_R_CS_MISS) - 1}, {EI_R_PARSE_EXP, sizeof(EI_R_PARSE_EXP) - 1}, {EI_R_PARSE_CONT, sizeof(EI_R_PARSE_CONT) - 1}, {EI_R_STAR_EXP, sizeof(EI_R_STAR_EXP) - 1}, {EI_R_STAR_CONT, sizeof(EI_R_STAR_CONT) - 1}, {EI_R_OOO, sizeof(EI_R_OOO) - 1}, {EI_R_RETRANS, sizeof(EI_R_RETRANS) - 1}, {EI_R_UNESCAPE, sizeof(EI_R_UNESCAPE) - 1}, {EI_R_TOO_MANY, sizeof(EI_R_TOO_MANY) - 1}, {EI_R_CONTACT_LEN,sizeof(EI_R_CONTACT_LEN) - 1}, {EI_R_CALLID_LEN, sizeof(EI_R_CALLID_LEN) - 1}, {EI_R_PARSE_PATH, sizeof(EI_R_PARSE_PATH) - 1}, {EI_R_PATH_UNSUP, sizeof(EI_R_PATH_UNSUP) - 1} }; int codes[] = { 200, /* R_FINE */ 500, /* R_UL_DEL_R */ 500, /* R_UL_GET */ 500, /* R_UL_NEW_R */ 400, /* R_INV_CSEQ */ 500, /* R_UL_INS_C */ 500, /* R_UL_INS_R */ 500, /* R_UL_DEL_C */ 500, /* R_UL_UPD_C */ 400, /* R_TO_USER */ 500, /* R_AOR_LEN */ 400, /* R_AOR_PARSE */ 400, /* R_INV_EXP */ 400, /* R_INV_Q */ 400, /* R_PARSE */ 400, /* R_TO_MISS */ 400, /* R_CID_MISS */ 400, /* R_CS_MISS */ 400, /* R_PARSE_EXP */ 400, /* R_PARSE_CONT */ 400, /* R_STAR_EXP */ 400, /* R_STAR_CONT */ 200, /* R_OOO */ 200, /* R_RETRANS */ 400, /* R_UNESCAPE */ 503, /* R_TOO_MANY */ 400, /* R_CONTACT_LEN */ 400, /* R_CALLID_LEN */ 400, /* R_PARSE_PATH */ 420 /* R_PATH_UNSUP */ }; #define RETRY_AFTER "Retry-After: " #define RETRY_AFTER_LEN (sizeof(RETRY_AFTER) - 1) static int add_retry_after(struct sip_msg* _m) { char* buf, *ra_s; int ra_len; ra_s = int2str(retry_after, &ra_len); buf = (char*)pkg_malloc(RETRY_AFTER_LEN + ra_len + CRLF_LEN); if (!buf) { LM_ERR("no pkg memory left\n"); return -1; } memcpy(buf, RETRY_AFTER, RETRY_AFTER_LEN); memcpy(buf + RETRY_AFTER_LEN, ra_s, ra_len); memcpy(buf + RETRY_AFTER_LEN + ra_len, CRLF, CRLF_LEN); add_lump_rpl(_m, buf, RETRY_AFTER_LEN + ra_len + CRLF_LEN, LUMP_RPL_HDR | LUMP_RPL_NODUP); return 0; } #define PATH "Path: " #define PATH_LEN (sizeof(PATH) - 1) static int add_path(struct sip_msg* _m, str* _p) { char* buf; buf = (char*)pkg_malloc(PATH_LEN + _p->len + CRLF_LEN); if (!buf) { LM_ERR("no pkg memory left\n"); return -1; } memcpy(buf, PATH, PATH_LEN); memcpy(buf + PATH_LEN, _p->s, _p->len); memcpy(buf + PATH_LEN + _p->len, CRLF, CRLF_LEN); add_lump_rpl(_m, buf, PATH_LEN + _p->len + CRLF_LEN, LUMP_RPL_HDR | LUMP_RPL_NODUP); return 0; } #define UNSUPPORTED "Unsupported: " #define UNSUPPORTED_LEN (sizeof(UNSUPPORTED) - 1) static int add_unsupported(struct sip_msg* _m, str* _p) { char* buf; buf = (char*)pkg_malloc(UNSUPPORTED_LEN + _p->len + CRLF_LEN); if (!buf) { LM_ERR("no pkg memory left\n"); return -1; } memcpy(buf, UNSUPPORTED, UNSUPPORTED_LEN); memcpy(buf + UNSUPPORTED_LEN, _p->s, _p->len); memcpy(buf + UNSUPPORTED_LEN + _p->len, CRLF, CRLF_LEN); add_lump_rpl(_m, buf, UNSUPPORTED_LEN + _p->len + CRLF_LEN, LUMP_RPL_HDR | LUMP_RPL_NODUP); return 0; } /*! \brief * Send a reply */ int send_reply(struct sip_msg* _m, unsigned int _flags) { str unsup = str_init(SUPPORTED_PATH_STR); long code; str msg = str_init(MSG_200); /* makes gcc shut up */ char* buf; if (contact.data_len > 0) { add_lump_rpl( _m, contact.buf, contact.data_len, LUMP_RPL_HDR|LUMP_RPL_NODUP|LUMP_RPL_NOFREE); contact.data_len = 0; } if (rerrno == R_FINE && (_flags®_SAVE_PATH_FLAG) && _m->path_vec.s) { if ( (_flags®_SAVE_PATH_OFF_FLAG)==0 ) { if (parse_supported(_m)<0 && (_flags®_SAVE_PATH_STRICT_FLAG)) { rerrno = R_PATH_UNSUP; if (add_unsupported(_m, &unsup) < 0) return -1; if (add_path(_m, &_m->path_vec) < 0) return -1; } else if (get_supported(_m) & F_SUPPORTED_PATH) { if (add_path(_m, &_m->path_vec) < 0) return -1; } else if ((_flags®_SAVE_PATH_STRICT_FLAG)) { rerrno = R_PATH_UNSUP; if (add_unsupported(_m, &unsup) < 0) return -1; if (add_path(_m, &_m->path_vec) < 0) return -1; } } } code = codes[rerrno]; switch(code) { case 200: msg.s = MSG_200; msg.len = sizeof(MSG_200)-1; break; case 400: msg.s = MSG_400; msg.len = sizeof(MSG_400)-1;break; case 420: msg.s = MSG_420; msg.len = sizeof(MSG_420)-1;break; case 500: msg.s = MSG_500; msg.len = sizeof(MSG_500)-1;break; case 503: msg.s = MSG_503; msg.len = sizeof(MSG_503)-1;break; } if (code != 200) { buf = (char*)pkg_malloc(E_INFO_LEN + error_info[rerrno].len + CRLF_LEN + 1); if (!buf) { LM_ERR("no pkg memory left\n"); return -1; } memcpy(buf, E_INFO, E_INFO_LEN); memcpy(buf + E_INFO_LEN, error_info[rerrno].s, error_info[rerrno].len); memcpy(buf + E_INFO_LEN + error_info[rerrno].len, CRLF, CRLF_LEN); add_lump_rpl( _m, buf, E_INFO_LEN + error_info[rerrno].len + CRLF_LEN, LUMP_RPL_HDR|LUMP_RPL_NODUP); if (code >= 500 && code < 600 && retry_after) { if (add_retry_after(_m) < 0) { return -1; } } } if (sigb.reply(_m, code, &msg, NULL) == -1) { LM_ERR("failed to send %ld %.*s\n", code, msg.len,msg.s); return -1; } else return 0; } /*! \brief * Release contact buffer if any */ void free_contact_buf(void) { if (contact.buf) { pkg_free(contact.buf); contact.buf = 0; contact.buf_len = 0; contact.data_len = 0; } } opensips-2.2.2/modules/registrar/reply.h000066400000000000000000000024371300170765700203470ustar00rootroot00000000000000/* * Send a reply * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief SIP registrar module - send a reply * \ingroup registrar */ #ifndef REPLY_H #define REPLY_H #include "../../parser/msg_parser.h" #include "../usrloc/ucontact.h" /*! \brief * Send a reply */ int send_reply(struct sip_msg* _m, unsigned int _flags); /*! \brief * Build Contact HF for reply */ int build_contact(ucontact_t* c,struct sip_msg *_m); /*! \brief * Release contact buffer if any */ void free_contact_buf(void); #endif /* REPLY_H */ opensips-2.2.2/modules/registrar/rerrno.c000066400000000000000000000017011300170765700205070ustar00rootroot00000000000000/* * Registrar errno * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief SIP registrar module - registrar errno * \ingroup registrar */ #include "rerrno.h" rerr_t rerrno; opensips-2.2.2/modules/registrar/rerrno.h000066400000000000000000000047351300170765700205260ustar00rootroot00000000000000/* * Registrar errno * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief SIP registrar module - registrar errno * \ingroup registrar */ #ifndef RERRNO_H #define RERRNO_H typedef enum rerr { R_FINE = 0, /*!< Everything went OK */ R_UL_DEL_R, /*!< Usrloc record delete failed */ R_UL_GET_R, /*!< Usrloc record get failed */ R_UL_NEW_R, /*!< Usrloc new record failed */ R_INV_CSEQ, /*!< Invalid CSeq value */ R_UL_INS_C, /*!< Usrloc insert contact failed */ R_UL_INS_R, /*!< Usrloc insert record failed */ R_UL_DEL_C, /*!< Usrloc contact delete failed */ R_UL_UPD_C, /*!< Usrloc contact update failed */ R_TO_USER, /*!< No username part in To URI */ R_AOR_LEN, /*!< Address Of Record too long */ R_AOR_PARSE, /*!< Error while parsing Address Of Record */ R_INV_EXP, /*!< Invalid expires parameter in contact */ R_INV_Q, /*!< Invalid q parameter in contact */ R_PARSE, /*!< Error while parsing message */ R_TO_MISS, /*!< Missing To header field */ R_CID_MISS, /*!< Missing Call-ID header field */ R_CS_MISS, /*!< Missing CSeq header field */ R_PARSE_EXP, /*!< Error while parsing Expires */ R_PARSE_CONT, /*!< Error while parsing Contact */ R_STAR_EXP, /*!< star and expires != 0 */ R_STAR_CONT, /*!< star and more contacts */ R_OOO, /*!< Out-Of-Order request */ R_RETRANS, /*!< Request is retransmission */ R_UNESCAPE, /*!< Error while unescaping username */ R_TOO_MANY, /*!< Too many contacts */ R_CONTACT_LEN,/*!< Contact URI or RECEIVED too long */ R_CALLID_LEN, /*!< Callid too long */ R_PARSE_PATH, /*!< Error while parsing Path */ R_PATH_UNSUP /*!< Path not supported by UAC */ } rerr_t; extern rerr_t rerrno; #endif /* RERRNO_H */ opensips-2.2.2/modules/registrar/save.c000066400000000000000000000766741300170765700201630ustar00rootroot00000000000000/* * Process REGISTER request and send reply * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ---------- * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2003-02-28 scrathcpad compatibility abandoned (jiri) * 2003-03-21 save_noreply added, patch provided by Maxim Sobolev * (janakj) * 2005-07-11 added sip_natping_flag for nat pinging with SIP method * instead of UDP package (bogdan) * 2006-04-13 added tcp_persistent_flag for keeping the TCP connection as long * as a TCP contact is registered (bogdan) * 2006-11-22 save_noreply and save_memory merged into save() (bogdan) * 2006-11-28 Added statistic support for the number of accepted/rejected * registrations. (Jeffrey Magder - SOMA Networks) * 2007-02-24 sip_natping_flag moved into branch flags, so migrated to * nathelper module (bogdan) */ /*! * \file * \brief SIP registrar module - Process REGISTER request and send reply * \ingroup registrar */ #include "../../str.h" #include "../../socket_info.h" #include "../../parser/parse_allow.h" #include "../../parser/parse_methods.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_supported.h" #include "../../dprint.h" #include "../../trim.h" #include "../../ut.h" #include "../../qvalue.h" #include "../../dset.h" #include "../../mod_fix.h" #include "../../data_lump.h" #include "../usrloc/usrloc.h" #include "common.h" #include "sip_msg.h" #include "rerrno.h" #include "reply.h" #include "reg_mod.h" #include "regtime.h" #include "path.h" #include "save.h" #include "lookup.h" /*! \brief * Process request that contained a star, in that case, * we will remove all bindings with the given username * from the usrloc and return 200 OK response */ static inline int star(udomain_t* _d, struct save_ctx *_sctx, struct sip_msg *_m) { urecord_t* r; ucontact_t* c; ul.lock_udomain(_d, &_sctx->aor); if (!ul.get_urecord(_d, &_sctx->aor, &r)) { c = r->contacts; while(c) { if (_sctx->flags®_SAVE_MEMORY_FLAG) { c->flags |= FL_MEM; } else { c->flags &= ~FL_MEM; } c = c->next; } } if (ul.delete_urecord(_d, &_sctx->aor, NULL, 0) < 0) { LM_ERR("failed to remove record from usrloc\n"); /* Delete failed, try to get corresponding * record structure and send back all existing * contacts */ rerrno = R_UL_DEL_R; if (!ul.get_urecord(_d, &_sctx->aor, &r)) { build_contact(r->contacts,_m); } ul.unlock_udomain(_d, &_sctx->aor); return -1; } ul.unlock_udomain(_d, &_sctx->aor); return 0; } /*! \brief */ static struct socket_info *get_sock_hdr(struct sip_msg *msg) { struct socket_info *sock; struct hdr_field *hf; str socks; str hosts; int port; int proto; if (parse_headers( msg, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse message\n"); return 0; } hf = get_header_by_name( msg, sock_hdr_name.s, sock_hdr_name.len); if (hf==0) return 0; trim_len( socks.len, socks.s, hf->body ); if (socks.len==0) return 0; if (parse_phostport( socks.s, socks.len, &hosts.s, &hosts.len, &port, &proto)!=0) { LM_ERR("bad socket <%.*s> in \n", socks.len, socks.s); return 0; } set_sip_defaults( port, proto); sock = grep_sock_info(&hosts,(unsigned short)port,(unsigned short)proto); if (sock==0) { LM_ERR("non-local socket <%.*s>\n", socks.len, socks.s); return 0; } LM_DBG("%d:<%.*s>:%d -> p=%p\n", proto,socks.len,socks.s,port,sock ); return sock; } /*! \brief * Process request that contained no contact header * field, it means that we have to send back a response * containing a list of all existing bindings for the * given username (in To HF) */ static inline int no_contacts(udomain_t* _d, str* _a,struct sip_msg *_m) { urecord_t* r; int res; ul.lock_udomain(_d, _a); res = ul.get_urecord(_d, _a, &r); if (res < 0) { rerrno = R_UL_GET_R; LM_ERR("failed to retrieve record from usrloc\n"); ul.unlock_udomain(_d, _a); return -1; } if (res == 0) { /* Contacts found */ build_contact(r->contacts,_m); } ul.unlock_udomain(_d, _a); return 0; } /*! \brief * Fills the common part (for all contacts) of the info structure */ static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c, unsigned int _e, unsigned int _f, unsigned int _flags) { static ucontact_info_t ci; static str no_ua = str_init("n/a"); static str callid; static str path_received = {0,0}; static str path; static str received = {0,0}; static int received_found; static unsigned int allowed, allow_parsed; static struct sip_msg *m = 0; static int_str attr_avp_value; static struct usr_avp *avp_attr; int_str val; if (_m!=0) { memset( &ci, 0, sizeof(ucontact_info_t)); /* Get callid of the message */ callid = _m->callid->body; trim_trailing(&callid); if (callid.len > CALLID_MAX_SIZE) { rerrno = R_CALLID_LEN; LM_ERR("callid too long\n"); goto error; } ci.callid = &callid; /* Get CSeq number of the message */ if (str2int(&get_cseq(_m)->number, (unsigned int*)&ci.cseq) < 0) { rerrno = R_INV_CSEQ; LM_ERR("failed to convert cseq number\n"); goto error; } /* set received socket */ if ( _flags®_SAVE_SOCKET_FLAG) { ci.sock = get_sock_hdr(_m); if (ci.sock==0) ci.sock = _m->rcv.bind_address; } else { ci.sock = _m->rcv.bind_address; } /* additional info from message */ if (parse_headers(_m, HDR_USERAGENT_F, 0) != -1 && _m->user_agent && _m->user_agent->body.len>0 && _m->user_agent->body.lenuser_agent->body; } else { ci.user_agent = &no_ua; } /* extract Path headers */ if ( _flags®_SAVE_PATH_FLAG ) { if (build_path_vector(_m, &path, &path_received, _flags) < 0) { rerrno = R_PARSE_PATH; goto error; } if (path.len && path.s) { ci.path = &path; /* save in msg too for reply */ if (set_path_vector(_m, &path) < 0) { rerrno = R_PARSE_PATH; goto error; } } } ci.last_modified = act_time; /* set flags */ ci.flags = _f; ci.cflags = getb0flags(_m); /* get received */ if (path_received.len && path_received.s) { ci.cflags |= ul.nat_flag; ci.received = path_received; } allow_parsed = 0; /* not parsed yet */ received_found = 0; /* not found yet */ m = _m; /* remember the message */ } if(_c!=0) { /* Calculate q value of the contact */ if (calc_contact_q(_c->q, &ci.q) < 0) { rerrno = R_INV_Q; LM_ERR("failed to calculate q\n"); goto error; } /* set expire time */ ci.expires = _e; /* Get methods of contact */ if (_c->methods) { if (parse_methods(&(_c->methods->body), &ci.methods) < 0) { rerrno = R_PARSE; LM_ERR("failed to parse contact methods\n"); goto error; } } else { /* check on Allow hdr */ if (allow_parsed == 0) { if (m && parse_allow( m ) != -1) { allowed = get_allow_methods(m); } else { allowed = ALL_METHODS; } allow_parsed = 1; } ci.methods = allowed; } if (_c->instance) { ci.instance = _c->instance->body; } /* get received */ if (ci.received.len==0) { if (_c->received) { ci.received = _c->received->body; } else { if (received_found==0) { memset(&val, 0, sizeof(int_str)); if (rcv_avp_name>=0 && search_first_avp(rcv_avp_type, rcv_avp_name, &val, 0) && val.s.len > 0) { if (val.s.len>RECEIVED_MAX_SIZE) { rerrno = R_CONTACT_LEN; LM_ERR("received too long\n"); goto error; } received = val.s; } else { received.s = 0; received.len = 0; } received_found = 1; } ci.received = received; } } /* additional information (script pvar) */ if (attr_avp_name != -1) { avp_attr = search_first_avp(attr_avp_type, attr_avp_name, &attr_avp_value, NULL); if (avp_attr) { ci.attr = &attr_avp_value.s; LM_DBG("Attributes: %.*s\n", ci.attr->len, ci.attr->s); } } } return &ci; error: return 0; } /*! \brief * Message contained some contacts, but record with same address * of record was not found so we have to create a new record * and insert all contacts from the message that have expires * > 0 */ static inline int insert_contacts(struct sip_msg* _m, contact_t* _c, udomain_t* _d, str* _a, struct save_ctx *_sctx) { ucontact_info_t* ci; urecord_t* r; ucontact_t* c; unsigned int cflags; int num; int e; int e_max; int tcp_check; struct sip_uri uri; cflags = (_sctx->flags®_SAVE_MEMORY_FLAG)?FL_MEM:FL_NONE; if (is_tcp_based_proto(_m->rcv.proto) && (_m->flags&tcp_persistent_flag)) { e_max = 0; tcp_check = 1; } else { e_max = tcp_check = 0; } for( num=0,r=0,ci=0 ; _c ; _c = get_next_contact(_c) ) { /* calculate expires */ calc_contact_expires(_m, _c->expires, &e, _sctx); /* Skip contacts with zero expires */ if (e == 0) continue; if (_sctx->max_contacts && (num >= _sctx->max_contacts)) { if (_sctx->flags®_SAVE_FORCE_REG_FLAG) { /* we are overflowing the number of maximum contacts, so remove the first (oldest) one to prevent this */ if (r==NULL || r->contacts==NULL) { LM_CRIT("BUG - overflow detected with r=%p and " "contacts=%p\n",r,r->contacts); goto error; } if (ul.delete_ucontact( r, r->contacts, 0)!=0) { LM_ERR("failed to remove contact\n"); goto error; } } else { LM_INFO("too many contacts (%d) for AOR <%.*s>, max=%d\n", num, _a->len, _a->s, _sctx->max_contacts); rerrno = R_TOO_MANY; goto error; } } else { num++; } if (r==0) { if (ul.insert_urecord(_d, _a, &r, 0) < 0) { rerrno = R_UL_NEW_R; LM_ERR("failed to insert new record structure\n"); goto error; } } /* pack the contact_info */ if ( (ci=pack_ci( (ci==0)?_m:0, _c, e, cflags, _sctx->flags))==0 ) { LM_ERR("failed to extract contact info\n"); goto error; } if ( r->contacts==0 || ul.get_ucontact(r, &_c->uri, ci->callid, ci->cseq+1, &c)!=0 ) { if (ul.insert_ucontact( r, &_c->uri, ci, &c, 0) < 0) { rerrno = R_UL_INS_C; LM_ERR("failed to insert contact\n"); goto error; } } else { if (ul.update_ucontact( r, c, ci, 0) < 0) { rerrno = R_UL_UPD_C; LM_ERR("failed to update contact\n"); goto error; } } if (tcp_check) { /* parse contact uri to see if transport is TCP */ if (parse_uri( _c->uri.s, _c->uri.len, &uri)<0) { LM_ERR("failed to parse contact <%.*s>\n", _c->uri.len, _c->uri.s); } else if ( is_tcp_based_proto(uri.proto) ) { if (e_max) { LM_WARN("multiple TCP contacts on single REGISTER\n"); if (e>e_max) e_max = e; } else { e_max = e; } } } } if (r) { if (r->contacts) { build_contact(r->contacts,_m); } ul.release_urecord(r, 0); } if ( tcp_check && e_max>0 ) { e_max -= act_time; trans_set_dst_attr( &_m->rcv, DST_FCNTL_SET_LIFETIME, (void*)(long)(e_max + 10) ); } return 0; error: if (r) ul.delete_urecord(_d, _a, r, 0); return -1; } /*! \brief * Message contained some contacts and appropriate * record was found, so we have to walk through * all contacts and do the following: * 1) If contact in usrloc doesn't exists and * expires > 0, insert new contact * 2) If contact in usrloc exists and expires * > 0, update the contact * 3) If contact in usrloc exists and expires * == 0, delete contact */ static inline int update_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c, struct save_ctx *_sctx) { ucontact_info_t *ci; ucontact_t *c, *c_last, *c_it; int e; unsigned int cflags; int ret; int num; int e_max; int tcp_check; struct sip_uri uri; /* mem flag */ cflags = (_sctx->flags®_SAVE_MEMORY_FLAG)?FL_MEM:FL_NONE; /* pack the contact_info */ if ( (ci=pack_ci( _m, 0, 0, cflags, _sctx->flags))==0 ) { LM_ERR("failed to initial pack contact info\n"); goto error; } /* count how many contacts we have right now */ num = 0; if (_sctx->max_contacts) { c = _r->contacts; while(c) { if (VALID_CONTACT(c, act_time)) num++; c = c->next; } } if (is_tcp_based_proto(_m->rcv.proto) && (_m->flags&tcp_persistent_flag)) { e_max = -1; tcp_check = 1; } else { e_max = tcp_check = 0; } for( ; _c ; _c = get_next_contact(_c) ) { /* calculate expires */ calc_contact_expires(_m, _c->expires, &e, _sctx); /* search for the contact*/ ret = ul.get_ucontact( _r, &_c->uri, ci->callid, ci->cseq, &c); if (ret==-1) { LM_ERR("invalid cseq for aor <%.*s>\n",_r->aor.len,_r->aor.s); rerrno = R_INV_CSEQ; goto error; } else if (ret==-2) { continue; } if ( ret > 0 ) { /* Contact not found -> expired? */ if (e==0) continue; /* we need to add a new contact -> too many ?? */ while (_sctx->max_contacts && num>=_sctx->max_contacts) { if (_sctx->flags®_SAVE_FORCE_REG_FLAG) { /* we are overflowing the number of maximum contacts, so remove the oldest valid one to prevent this */ for( c_it=_r->contacts,c_last=NULL ; c_it ; c_it=c_it->next ) if (VALID_CONTACT(c_it, act_time)) c_last=c_it; if (c_last==NULL) { LM_CRIT("BUG - overflow detected but no valid " "contacts found :( \n"); goto error; } LM_DBG("overflow on inserting new contact -> removing " "<%.*s>\n", c_last->c.len, c_last->c.s); if (ul.delete_ucontact( _r, c_last, 0)!=0) { LM_ERR("failed to remove contact\n"); goto error; } num--; } else { LM_INFO("too many contacts for AOR <%.*s>, max=%d\n", _r->aor.len, _r->aor.s, _sctx->max_contacts); rerrno = R_TOO_MANY; return -1; } } /* pack the contact_info */ if ( (ci=pack_ci( 0, _c, e, 0, _sctx->flags))==0 ) { LM_ERR("failed to extract contact info\n"); goto error; } if (ul.insert_ucontact( _r, &_c->uri, ci, &c, 0) < 0) { rerrno = R_UL_INS_C; LM_ERR("failed to insert contact\n"); goto error; } } else { /* Contact found */ if (e == 0) { /* it's expired */ if (_sctx->flags®_SAVE_MEMORY_FLAG) { c->flags |= FL_MEM; } else { c->flags &= ~FL_MEM; } if (ul.delete_ucontact(_r, c, 0) < 0) { rerrno = R_UL_DEL_C; LM_ERR("failed to delete contact\n"); goto error; } } else { /* do update */ /* if the contact to be updated is not valid, it will be after * update, so need to compensate the total number of contact */ if ( !VALID_CONTACT(c,act_time) ) num++; while ( _sctx->max_contacts && num>_sctx->max_contacts ) { if (_sctx->flags®_SAVE_FORCE_REG_FLAG) { /* we are overflowing the number of maximum contacts, so remove the first (oldest) one to prevent this (but not the one to be updated !) */ for( c_it=_r->contacts,c_last=NULL ; c_it ; c_it=c_it->next ) if (VALID_CONTACT(c_it, act_time) && c_it!=c) c_last=c_it; if (c_last==NULL) { LM_CRIT("BUG - overflow detected but no " "valid contacts found :( \n"); goto error; } LM_DBG("overflow on update -> removing contact " "<%.*s>\n", c_last->c.len, c_last->c.s); if (ul.delete_ucontact( _r, c_last, 0)!=0) { LM_ERR("failed to remove contact\n"); goto error; } num--; } else { LM_INFO("too many contacts for AOR <%.*s>, max=%d\n", _r->aor.len, _r->aor.s, _sctx->max_contacts); rerrno = R_TOO_MANY; return -1; } } /* pack the contact specific info */ if ( (ci=pack_ci( 0, _c, e, 0, _sctx->flags))==0 ) { LM_ERR("failed to pack contact specific info\n"); goto error; } if (ul.update_ucontact(_r, c, ci, 0) < 0) { rerrno = R_UL_UPD_C; LM_ERR("failed to update contact\n"); goto error; } } } if (tcp_check) { /* parse contact uri to see if transport is TCP */ if (parse_uri( _c->uri.s, _c->uri.len, &uri)<0) { LM_ERR("failed to parse contact <%.*s>\n", _c->uri.len, _c->uri.s); } else if (is_tcp_based_proto(uri.proto)) { if (e_max>0) { LM_WARN("multiple TCP contacts on single REGISTER\n"); } if (e>e_max) e_max = e; } } } if ( tcp_check && e_max>-1 ) { if (e_max) e_max -= act_time; trans_set_dst_attr( &_m->rcv, DST_FCNTL_SET_LIFETIME, (void*)(long)(e_max + 10) ); } return 0; error: return -1; } /*! \brief * This function will process request that * contained some contact header fields */ static inline int add_contacts(struct sip_msg* _m, contact_t* _c, udomain_t* _d, struct save_ctx *_sctx) { int res; urecord_t* r; ul.lock_udomain(_d, &_sctx->aor); res = ul.get_urecord(_d, &_sctx->aor, &r); if (res < 0) { rerrno = R_UL_GET_R; LM_ERR("failed to retrieve record from usrloc\n"); ul.unlock_udomain(_d, &_sctx->aor); return -2; } if (res == 0) { /* Contacts found */ if (update_contacts(_m, r, _c, _sctx) < 0) { build_contact(r->contacts,_m); ul.release_urecord(r, 0); ul.unlock_udomain(_d, &_sctx->aor); return -3; } build_contact(r->contacts,_m); ul.release_urecord(r, 0); } else { if (insert_contacts(_m, _c, _d, &_sctx->aor, _sctx) < 0) { ul.unlock_udomain(_d, &_sctx->aor); return -4; } } ul.unlock_udomain(_d, &_sctx->aor); return 0; } /*! \brief * Process REGISTER request and save it's contacts */ #define is_cflag_set(_name) ((sctx.flags)&(_name)) int save_aux(struct sip_msg* _m, str* forced_binding, char* _d, char* _f, char* _s) { struct save_ctx sctx; contact_t* c; contact_t* forced_c = NULL; int st; str uri; str flags_s; pv_value_t val; rerrno = R_FINE; memset( &sctx, 0 , sizeof(sctx)); sctx.max_contacts = -1; sctx.flags = 0; sctx.min_expires = min_expires; sctx.max_expires = max_expires; if ( _f ) { if (fixup_get_svalue( _m, (gparam_p)_f, &flags_s)!=0) { LM_ERR("invalid flags parameter"); return -1; } for( st=0 ; st< flags_s.len ; st++ ) { switch (flags_s.s[st]) { case 'm': sctx.flags |= REG_SAVE_MEMORY_FLAG; break; case 'r': sctx.flags |= REG_SAVE_NOREPLY_FLAG; break; case 's': sctx.flags |= REG_SAVE_SOCKET_FLAG; break; case 'v': sctx.flags |= REG_SAVE_PATH_RECEIVED_FLAG; break; case 'f': sctx.flags |= REG_SAVE_FORCE_REG_FLAG; break; case 'c': sctx.max_contacts = 0; while (stlen, forced_binding->s); goto error; } /* prevent processing all the headers from the message */ reset_first_contact(); st = 0; c = forced_c; } else { if (check_contacts(_m, &st) > 0) { goto error; } c = get_first_contact(_m); } get_act_time(); if (_s) { if (pv_get_spec_value( _m, (pv_spec_p)_s, &val)!=0) { LM_ERR("failed to get PV value\n"); goto return_minus_one; } if ( (val.flags&PV_VAL_STR)==0 ) { LM_ERR("PV vals is not string\n"); goto return_minus_one; } uri = val.rs; } else { uri = get_to(_m)->uri; } if (extract_aor( &uri, &sctx.aor,0,0) < 0) { LM_ERR("failed to extract Address Of Record\n"); goto error; } if (c == 0) { if (st) { if (star((udomain_t*)_d, &sctx,_m) < 0) goto error; } else { if (no_contacts((udomain_t*)_d, &sctx.aor,_m) < 0) goto error; } } else { if (add_contacts(_m, c, (udomain_t*)_d, &sctx) < 0) goto error; } update_stat(accepted_registrations, 1); if (!is_cflag_set(REG_SAVE_NOREPLY_FLAG) && (send_reply(_m,sctx.flags)<0)) goto return_minus_one; if (forced_c) free_contacts(&forced_c); return 1; error: update_stat(rejected_registrations, 1); if ( !is_cflag_set(REG_SAVE_NOREPLY_FLAG) ) send_reply(_m,sctx.flags); if (forced_c) free_contacts(&forced_c); return -2; return_minus_one: if (forced_c) free_contacts(&forced_c); return -1; } #define MAX_FORCED_BINDING_LEN 256 int save(struct sip_msg* _m, char* _d, char* _f, char* _s) { struct sip_msg* msg = _m; struct cell* t = NULL; contact_t* _c; contact_t* reply_c = NULL; contact_t* request_c = NULL; int st; int ret; int requested_exp = 0; int enforced_exp = 0; int_str val; struct lump* l; char* p; char forced_binding_buf[MAX_FORCED_BINDING_LEN]; str forced_binding = {NULL, 0}; str *binding_uri; if(_m->first_line.type != SIP_REPLY) return save_aux(_m, NULL, _d, _f, _s); memset(&val, 0, sizeof(int_str)); if(!tmb.t_gett) { LM_ERR("TM module not loaded - can not save on reply\n"); return -1; } t = tmb.t_gett(); if(!t || t==T_UNDEFINED) { LM_ERR("Transaction not created on Register - can not save on reply\n"); return -1; } msg = t->uas.request; if(!msg) { LM_ERR("NULL request - can not save on reply\n"); return -1; } if (parse_message(_m) < 0) return -1; if (check_contacts(_m, &st) > 0) return -1; if (parse_message(msg) < 0) return -1; if (check_contacts(msg, &st) > 0) return -1; /* msg - request _m - reply */ request_c = get_first_contact(msg); if(request_c) { /* For now, we deal only with the first contact * FIXME: implement multiple contact handling - see check_contacts() */ if(!request_c->expires || !request_c->expires->body.len) { if (msg->expires && ((exp_body_t*)(msg->expires->parsed))->valid) { requested_exp = ((exp_body_t*)(msg->expires->parsed))->val; } else { LM_WARN("No expired defined\n"); } } else { if (str2int(&(request_c->expires->body), (unsigned int*)&requested_exp)<0) { LM_ERR("unable to get expires from [%.*s]\n", request_c->expires->body.len, request_c->expires->body.s); return -1; } } LM_DBG("Binding received from client [%.*s] with requested expires [%d]\n", request_c->uri.len, request_c->uri.s, requested_exp); /* We will use the Contact from request: * - check if a modified contact was set in avp */ if (mct_avp_name >= 0 && search_first_avp(mct_avp_type,mct_avp_name,&val,0) && val.s.len > 0) { LM_DBG("Binding sent to upper registrar [%.*s]\n", val.s.len, val.s.s); binding_uri = &val.s; } else { binding_uri = &request_c->uri; } if (requested_exp) { /* Let's get the contact from reply */ _c = get_first_contact(_m); while (_c) { if (compare_uris(binding_uri, NULL, &_c->uri, NULL) == 0) { if(_c->expires && _c->expires->body.len) { if(str2int(&(_c->expires->body), (unsigned int*)&enforced_exp)<0) { LM_ERR("unable to get expires from [%.*s]\n", _c->expires->body.len, _c->expires->body.s); return -1; } LM_DBG("Binding received from upper registrar" " [%.*s] with imposed expires [%d]\n", _c->uri.len, _c->uri.s, enforced_exp); reply_c = _c; forced_binding.len = request_c->uri.len + 11 + reply_c->expires->body.len; if (forced_binding.len <= MAX_FORCED_BINDING_LEN) { forced_binding.s = forced_binding_buf; forced_binding_buf[0] = '<'; memcpy(&forced_binding_buf[1], request_c->uri.s, request_c->uri.len); memcpy(&forced_binding_buf[request_c->uri.len + 1], ">;expires=", 10); memcpy(&forced_binding_buf[request_c->uri.len + 11], reply_c->expires->body.s, reply_c->expires->body.len); LM_DBG("forcing binding [%.*s]\n", forced_binding.len, forced_binding.s); break; } else { LM_ERR("forced binding to BIG:" " %d > MAX_FORCED_BINDING_LEN\n", forced_binding.len); return -1; } } } else { LM_DBG("Unmatched binding [%.*s]\n", _c->uri.len, _c->uri.s); } _c = get_next_contact(_c); } } ret = save_aux(msg, forced_binding.s?&forced_binding:NULL, _d, _f, _s); } else { LM_DBG("No Contact in request => this is an interogation\n"); ret = 1; } /* if the contact was changed in register - put the modif value */ if(request_c && requested_exp && val.s.s) { if(reply_c) { LM_DBG("replacing contact uri [%.*s] with [%.*s]\n", reply_c->uri.len, reply_c->uri.s, request_c->uri.len, request_c->uri.s); /* replace with what was received in Register */ /* reply_c->uri - now contains the initial received value */ if((l=del_lump(_m, reply_c->uri.s - _m->buf, reply_c->uri.len, 0))==0) { LM_ERR("Failed to delete contact uri lump\n"); ret = -1; goto done; } p = pkg_malloc( request_c->uri.len); if (p==0) { LM_ERR("no more pkg mem\n"); ret = -1; goto done; } memcpy( p, request_c->uri.s, request_c->uri.len ); if (insert_new_lump_after( l, p, request_c->uri.len, 0)==0) { LM_ERR("insert new lump failed\n"); pkg_free(p); ret =-1; goto done; } } } done: clean_msg_clone(t->uas.request, t->uas.request, t->uas.end_request); return ret; } /** * _remove - Delete an entire AOR entry or just one or more of its Contacts * Parameter format: _remove(domain, AOR[, Contact URI or plain hostname]) * * @udomain: (udomain_t *) * @aor_gp: address-of-record as a SIP URI (plain string or pvar) * @contact_gp: contact to be deleted or domain in front of multiple contacts * * @return: 1 on success, negative on failure */ int _remove(struct sip_msg *msg, char *udomain, char *aor_gp, char *contact_gp) { struct sip_uri puri; struct hostent delete_he, *he; urecord_t *record; ucontact_t *contact, *it; str uri, aor_user, delete_user = { NULL, 0 }; int err, count = 0; int delete_by_hostname = 0; unsigned short delete_port; memset(&delete_he, 0, sizeof delete_he); if (fixup_get_svalue(msg, (gparam_p)aor_gp, &uri) != 0) { LM_ERR("failed to get gparam_t value\n"); return E_UNSPEC; } if (extract_aor( &uri, &aor_user,0,0) < 0) { LM_ERR("failed to extract Address Of Record\n"); return E_BAD_URI; } ul.lock_udomain((udomain_t *)udomain, &aor_user); if (ul.get_urecord((udomain_t *)udomain, &aor_user, &record) != 0) { LM_DBG("no record '%.*s' found!\n", aor_user.len, aor_user.s); err = 1; goto out_unlock; } /* if no contact uri param is given, delete the whole urecord entry */ if (!contact_gp) { if (ul.delete_urecord((udomain_t *)udomain, &aor_user, record, 0) != 0) { LM_ERR("failed to delete urecord for aor '%.*s'\n", aor_user.len, aor_user.s); err = E_UNSPEC; goto out_unlock; } err = 1; goto out_unlock; } if (fixup_get_svalue(msg, (gparam_p)contact_gp, &uri) != 0) { LM_ERR("failed to retrieve value of contact pv\n"); err = E_UNSPEC; goto out_unlock; } /* minimum two-letters for the domain name */ if (uri.len < 5) { LM_ERR("Invalid domain given: '%.*s'\n", uri.len, uri.s); err = E_INVALID_PARAMS; goto out_unlock; } /* a domain/IP address was given instead of a SIP contact URI */ if (uri.s[0] != 's' || uri.s[1] != 'i' || uri.s[2] != 'p' || (uri.s[3] != ':' && (uri.s[3] != 's' || uri.s[4] != ':'))) { delete_by_hostname = 1; he = sip_resolvehost(&uri, &delete_port, NULL, 0, NULL); if (!he) { LM_ERR("cannot resolve given host: '%.*s'\n", uri.len, uri.s); err = E_UNSPEC; goto out_unlock; } LM_DBG("Delete by host: '%s'\n", inet_ntoa(*(struct in_addr *)(he->h_addr_list[0]))); } else { LM_DBG("parsing uri: %.*s\n", uri.len, uri.s); if (parse_uri(uri.s, uri.len, &puri) != 0) { LM_ERR("failed to parse contact uri: '%.*s'\n", uri.len, uri.s); err = E_BAD_URI; goto out_unlock; } delete_user = puri.user; he = sip_resolvehost(&puri.host, &delete_port, &puri.proto, 0, NULL); if (!he) { LM_ERR("cannot resolve given uri: '%.*s'\n", uri.len, uri.s); err = E_UNSPEC; goto out_unlock; } if (puri.port_no > 0) delete_port = puri.port_no; LM_DBG("Delete by contact: [ User %.*s | Host %s | Port %d ]\n", delete_user.len, delete_user.s, inet_ntoa(*(struct in_addr *)(he->h_addr_list[0])), delete_port); } if (hostent_cpy(&delete_he, he) != 0) { LM_ERR("no more pkg mem\n"); err = E_OUT_OF_MEM; goto out_unlock; } for (it = record->contacts; it; ) { contact = it; it = it->next; count++; LM_DBG("parsing contact uri '%.*s'\n", contact->c.len, contact->c.s); if (parse_uri(contact->c.s, contact->c.len, &puri) != 0) { LM_ERR("failed to parse contact uri: '%.*s'\n", contact->c.len, contact->c.s); err = E_BAD_URI; goto out_unlock; } /* if necessary, solve the next_hop towards the contact */ he = sip_resolvehost(&contact->next_hop.name, &contact->next_hop.port, &contact->next_hop.proto, 0, NULL); if (!he) { LM_ERR("failed to resolve next hop of contact '%.*s'\n", contact->c.len, contact->c.s); continue; } LM_DBG("Contact: [ User %.*s | Host %s | Port %d ]\n", puri.user.len, puri.user.s, inet_ntoa(*(struct in_addr *)(he->h_addr_list[0])), puri.port_no); if (delete_by_hostname) { if (!memcmp(delete_he.h_addr_list[0], he->h_addr_list[0], he->h_length)) { ul.delete_ucontact(record, contact, 0); count--; } } else { if (delete_user.len == puri.user.len && delete_port == puri.port_no && !memcmp(delete_he.h_addr_list[0], he->h_addr_list[0], he->h_length) && !memcmp(delete_user.s, puri.user.s, puri.user.len)) { ul.delete_ucontact(record, contact, 0); count--; } } } err = 1; /* remove the AOR if no more contacts are attached */ if (count == 0) { if (ul.delete_urecord((udomain_t *)udomain, &aor_user, record, 0) != 0) { LM_ERR("failed to delete urecord for aor '%.*s'\n", aor_user.len, aor_user.s); err = 1; } } out_unlock: ul.unlock_udomain((udomain_t *)udomain, &aor_user); free_hostent(&delete_he); return err; } int is_other_contact_f(struct sip_msg* msg, char* _d, char *_s) { LM_WARN("Deprecated! Use is_ip_registered() instead!\n"); return is_ip_registered(msg, _d, NULL, _s); } opensips-2.2.2/modules/registrar/save.h000066400000000000000000000040311300170765700201420ustar00rootroot00000000000000/* * Functions that process REGISTER message * and store data in usrloc * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-21 save_noreply added, provided by Maxim Sobolev * (janakj) * 2006-11-22 save_noreply and save_memory merged into save() (bogdan) */ /*! * \file * \brief SIP registrar module - process REGISTER message * \ingroup registrar */ #ifndef SAVE_H #define SAVE_H #include "../../parser/msg_parser.h" /*! \brief * Process REGISTER request and save it's contacts */ int save(struct sip_msg* _m, char* _d, char* _cflags, char* _s); /** * _remove - Delete an entire AOR entry or one or more of its Contacts * Parameter format: delete(domain, AOR[, Contact URI or plain hostname]) * * @domain: logical domain name (usually name of location table) * @aor_gp: address-of-record as a SIP URI (plain string or pvar) * @contact_gp: contact to be deleted or domain in front of multiple contacts * * @return: 1 on success, negative on failure */ int _remove(struct sip_msg *msg, char *domain, char *aor, char *fixed_uri_param); /* * checks if there is another contact except the ones specified in the avp */ int is_other_contact_f(struct sip_msg* msg, char* _d, char *_s); #endif /* SAVE_H */ opensips-2.2.2/modules/registrar/sip_msg.c000066400000000000000000000146761300170765700206600ustar00rootroot00000000000000/* * SIP message related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief SIP registrar module - SIP message related functions * \ingroup registrar */ #include "../../parser/hf.h" #include "../../dprint.h" #include "../../parser/parse_expires.h" #include "../../ut.h" #include "../../qvalue.h" #include "reg_mod.h" /* Module parameters */ #include "regtime.h" /* act_time */ #include "rerrno.h" #include "sip_msg.h" static struct hdr_field* act_contact; /*! \brief * Return value of Expires header field * if the HF exists converted to absolute * time, if the HF doesn't exist, returns * default value; */ static inline int get_expires_hf(struct sip_msg* _m) { exp_body_t* p; if (_m->expires) { p = (exp_body_t*)_m->expires->parsed; if (p->valid) { if (p->val != 0) { return p->val + act_time; } else return 0; } else return act_time + default_expires; } else { return act_time + default_expires; } } /*! \brief * Parse the whole message and bodies of all header fields * that will be needed by registrar */ int parse_message(struct sip_msg* _m) { struct hdr_field* ptr; if (parse_headers(_m, HDR_EOH_F, 0) == -1) { rerrno = R_PARSE; LM_ERR("failed to parse headers\n"); return -1; } if (!_m->to) { rerrno = R_TO_MISS; LM_ERR("To not found\n"); return -2; } if (!_m->callid) { rerrno = R_CID_MISS; LM_ERR("Call-ID not found\n"); return -3; } if (!_m->cseq) { rerrno = R_CS_MISS; LM_ERR("CSeq not found\n"); return -4; } if (_m->expires && !_m->expires->parsed && (parse_expires(_m->expires) < 0)) { rerrno = R_PARSE_EXP; LM_ERR("failed to parse expires body\n"); return -5; } if (_m->contact) { ptr = _m->contact; while(ptr) { if (ptr->type == HDR_CONTACT_T) { if (!ptr->parsed && (parse_contact(ptr) < 0)) { rerrno = R_PARSE_CONT; LM_ERR("failed to parse Contact body\n"); return -6; } } ptr = ptr->next; } } return 0; } /*! \brief * Check if the originating REGISTER message was formed correctly * The whole message must be parsed before calling the function * _s indicates whether the contact was star */ int check_contacts(struct sip_msg* _m, int* _s) { struct hdr_field* p; contact_t* c; *_s = 0; /* Message without contacts is OK */ if (_m->contact == 0) return 0; if (((contact_body_t*)_m->contact->parsed)->star == 1) { /* The first Contact HF is star */ /* Expires must be zero */ if (get_expires_hf(_m) > 0) { rerrno = R_STAR_EXP; return 1; } /* Message must contain no contacts */ if (((contact_body_t*)_m->contact->parsed)->contacts) { rerrno = R_STAR_CONT; return 1; } /* Message must contain no other Contact HFs */ p = _m->contact->next; while(p) { if (p->type == HDR_CONTACT_T) { rerrno = R_STAR_CONT; return 1; } p = p->next; } *_s = 1; } else { /* The first Contact HF is not star */ /* Message must contain no star Contact HF */ p = _m->contact->next; while(p) { if (p->type == HDR_CONTACT_T) { if (((contact_body_t*)p->parsed)->star == 1) { rerrno = R_STAR_CONT; return 1; } /* check also the lenght of all contacts */ for(c=((contact_body_t*)p->parsed)->contacts ; c ; c=c->next) { if (c->uri.len > CONTACT_MAX_SIZE || (c->received && c->received->len>RECEIVED_MAX_SIZE) ) { rerrno = R_CONTACT_LEN; return 1; } } } p = p->next; } } return 0; } /*! \brief * Set to NULL the pointer to the first contact in message */ void reset_first_contact(void) { act_contact = NULL; } /*! \brief * Get the first contact in message */ contact_t* get_first_contact(struct sip_msg* _m) { if (_m->contact == 0) return 0; act_contact = _m->contact; return (((contact_body_t*)_m->contact->parsed)->contacts); } /*! \brief * Get next contact in message */ contact_t* get_next_contact(contact_t* _c) { struct hdr_field* p = NULL; if (_c->next == 0) { if (act_contact) p = act_contact->next; while(p) { if (p->type == HDR_CONTACT_T) { act_contact = p; return (((contact_body_t*)p->parsed)->contacts); } p = p->next; } return 0; } else { return _c->next; } } /*! \brief * Calculate absolute expires value per contact as follows: * 1) If the contact has expires value, use the value. If it * is not zero, add actual time to it * 2) If the contact has no expires parameter, use expires * header field in the same way * 3) If the message contained no expires header field, use * the default value */ void calc_contact_expires(struct sip_msg* _m, param_t* _ep, int* _e, struct save_ctx *_sctx) { int min_exp; int max_exp; /* global or local expire limits ? */ if (_sctx) { min_exp = _sctx->min_expires; max_exp = _sctx->max_expires; } else { min_exp = min_expires; max_exp = max_expires; } if (!_ep || !_ep->body.len) { *_e = get_expires_hf(_m); } else { if (str2int(&_ep->body, (unsigned int*)_e) < 0) { *_e = default_expires; } /* Convert to absolute value */ if (*_e != 0) *_e += act_time; } if ((*_e != 0) && ((*_e - act_time) < min_exp)) { *_e = min_exp + act_time; } if ((*_e != 0) && max_exp && ((*_e - act_time) > max_exp)) { *_e = max_exp + act_time; } } /*! \brief * Calculate contact q value as follows: * 1) If q parameter exists, use it * 2) If the parameter doesn't exist, use the default value */ int calc_contact_q(param_t* _q, qvalue_t* _r) { int rc; if (!_q || (_q->body.len == 0)) { *_r = default_q; } else { rc = str2q(_r, _q->body.s, _q->body.len); if (rc < 0) { rerrno = R_INV_Q; /* Invalid q parameter */ LM_ERR("invalid qvalue (%.*s): %s\n", _q->body.len, _q->body.s, qverr2str(rc)); return -1; } } return 0; } opensips-2.2.2/modules/registrar/sip_msg.h000066400000000000000000000050511300170765700206500ustar00rootroot00000000000000/* * SIP message related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief SIP registrar module - SIP message related functions * \ingroup registrar */ #ifndef SIP_MSG_H #define SIP_MSG_H #include "../../qvalue.h" #include "../../parser/msg_parser.h" #include "../../parser/contact/parse_contact.h" struct save_ctx { unsigned int flags; str aor; unsigned int max_contacts; unsigned int min_expires; unsigned int max_expires; }; /*! \brief * Parse the whole message and bodies of all header fields * that will be needed by registrar */ int parse_message(struct sip_msg* _m); /*! \brief * Check if the originating REGISTER message was formed correctly * The whole message must be parsed before calling the function * _s indicates whether the contact was star */ int check_contacts(struct sip_msg* _m, int* _s); /*! \brief * Set to NULL the pointer to the first contact in message */ void reset_first_contact(void); /*! \brief * Get the first contact in message */ contact_t* get_first_contact(struct sip_msg* _m); /*! \brief * Get next contact in message */ contact_t* get_next_contact(contact_t* _c); /*! \brief * Calculate absolute expires value per contact as follows: * 1) If the contact has expires value, use the value. If it * is not zero, add actual time to it * 2) If the contact has no expires parameter, use expires * header field in the same way * 3) If the message contained no expires header field, use * the default value */ void calc_contact_expires(struct sip_msg* _m, param_t* _ep, int* _e, struct save_ctx *_sctx); /*! \brief * Calculate contact q value as follows: * 1) If q parameter exist, use it * 2) If the parameter doesn't exist, use default value */ int calc_contact_q(param_t* _q, qvalue_t* _r); #endif /* SIP_MSG_H */ opensips-2.2.2/modules/rest_client/000077500000000000000000000000001300170765700173465ustar00rootroot00000000000000opensips-2.2.2/modules/rest_client/Makefile000066400000000000000000000003661300170765700210130ustar00rootroot00000000000000# Makefile v 1.0 2002/12/27 # # rest_client module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=rest_client.so LIBS+=-lcurl include ../../Makefile.modules opensips-2.2.2/modules/rest_client/README000066400000000000000000000217311300170765700202320ustar00rootroot00000000000000rest_client Module Liviu Chircu OpenSIPS Solutions Edited by Liviu Chircu Copyright © 2013 www.opensips-solutions.com Revision History Revision $Revision$ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. connection_timeout (integer) 1.3.2. connect_poll_interval (integer) 1.3.3. curl_timeout (integer) 1.3.4. ssl_verifypeer (integer) 1.3.5. ssl_verifyhost (integer) 1.3.6. ssl_capath (integer) 1.4. Exported Functions 1.4.1. rest_get(url, body_pv[, [ctype_pv][, [retcode_pv]]]) 1.4.2. rest_post(url, send_body_pv, [send_ctype_pv], recv_body_pv[, [recv_ctype_pv][, [retcode_pv]]]) 1.4.3. rest_append_hf(txt) 1.5. Exported Asynchronous Functions 1.5.1. rest_get(url, body_pv[, [ctype_pv][, [retcode_pv]]]) 1.5.2. rest_post(url, send_body_pv, [send_ctype_pv], recv_body_pv[, [recv_ctype_pv][, [retcode_pv]]]) List of Examples 1.1. Setting the connection_timeout parameter 1.2. Setting the connect_poll_interval parameter 1.3. Setting the curl_timeout parameter 1.4. Setting the ssl_verifypeer parameter 1.5. Setting the ssl_verifyhost parameter 1.6. Setting the ssl_capath parameter 1.7. rest_get usage 1.8. rest_post usage 1.9. rest_append_hf usage 1.10. async rest_get usage 1.11. async rest_post usage Chapter 1. Admin Guide 1.1. Overview The rest_client module provides a means of interacting with an HTTP server by doing RESTful queries, such as GET and POST. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules.. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * libcurl. 1.3. Exported Parameters 1.3.1. connection_timeout (integer) Maximum time allowed to establish a connection with the server. Default value is “20†seconds. Example 1.1. Setting the connection_timeout parameter ... modparam("rest_client", "connection_timeout", 300) ... 1.3.2. connect_poll_interval (integer) Allows complete control over how quickly we want to detect libcurl's completed TCP handshakes, so the transfers can be started. A lower "connect_poll_interval" will speed up all HTTP transfers, but will also increase CPU usage. Default value is “20†milliseconds. Example 1.2. Setting the connect_poll_interval parameter ... modparam("rest_client", "connect_poll_interval", 2) ... 1.3.3. curl_timeout (integer) Maximum time allowed for the libcurl transfer to complete. Default value is “20†seconds. Example 1.3. Setting the curl_timeout parameter ... modparam("rest_client", "curl_timeout", 300) ... 1.3.4. ssl_verifypeer (integer) Set this to 0 in order to disable the verification of the remote peer's certificate. Verification is done using a default bundle of CA certificates which come with libcurl. Default value is “1†(enabled). Example 1.4. Setting the ssl_verifypeer parameter ... modparam("rest_client", "ssl_verifypeer", 0) ... 1.3.5. ssl_verifyhost (integer) Set this to 0 in order to disable the verification that the remote peer actually corresponds to the server listed in the certificate. Default value is “1†(enabled). Example 1.5. Setting the ssl_verifyhost parameter ... modparam("rest_client", "ssl_verifyhost", 0) ... 1.3.6. ssl_capath (integer) An optional path for CA certificates to be used for host verifications. Example 1.6. Setting the ssl_capath parameter ... modparam("rest_client", "ssl_capath", "/home/opensips/ca_certificates") ... 1.4. Exported Functions 1.4.1. rest_get(url, body_pv[, [ctype_pv][, [retcode_pv]]]) Issues an HTTP GET request to the given 'url', and returns a representation of the resource. The body_pv pseudo-var will hold the body of the HTTP response. The optional ctype_pv pseudo-var will contain the value of the "Content-Type:" header. The optional retcode_pv pseudo-var is used to retain the HTTP status code of the response message. Since the module is based on libcurl, a 0 value means no HTTP reply arrived at all. Possible parameter types * url - String, pseudo-variable, or a String which includes pseudo-variables. (useful for specifying additional attribute-value fields in the URL) * body_pv, ctype_pv, retcode_pv - pseudo-variables This function can be used from the startup, branch, failure, request and timer routes. Example 1.7. rest_get usage ... # Example of querying a REST service to get the credit of an account if (!rest_get("http://getcredit.org/?ruri=$fU", "$var(credit)", "$var(ct )", "$var(rcode)")) { xlog("Error code $var(rcode) in HTTP GET!\n"); send_reply("403", "Not registered"); exit; } ... 1.4.2. rest_post(url, send_body_pv, [send_ctype_pv], recv_body_pv[, [recv_ctype_pv][, [retcode_pv]]]) Issues an HTTP POST request to the specified url. The request body will be copied from the send_body_pv pseudo-variable. The MIME Content-Type header for the request will be taken from send_ctype_pv (default is "application/x-www-form-urlencoded") The mandatory recv_body_pv pseudo-var will hold the body of the HTTP response. The optional recv_ctype_pv parameter will contain the value of the "Content-Type" header of the response message. The optional retcode_pv pseudo-var parameter can be given in order to retrieve the HTTP status code of the response message. Since the module based on libcurl, a 0 value means no HTTP reply arrived at all. Possible parameter types * url, send_body_pv, send_type_pv - String, pseudo-variable, or a String which includes pseudo-variables. * recv_body_pv, recv_ctype_pv, retcode_pv - pseudo-variables This function can be used from the startup, branch, failure, request and timer routes. Example 1.8. rest_post usage ... # Storing data using a RESTful service with an HTTP POST request if (!rest_post("http://myserver.org/register_user", "$fU", , "$var(body) ", "$var(ct)", "$var(rcode)")) { xlog("Error code $var(rcode) in HTTP POST!\n"); send_reply("403", "POST Forbidden"); exit; } ... 1.4.3. rest_append_hf(txt) Appends 'txt' to the HTTP headers of the subsequent request. Multiple headers can be appended by making multiple calls before executing a request. The contents of txt should adhere to the specification for HTTP headers (ex. Field: Value) Parameter types * txt - String, pseudo-variable, or a String which includes pseudo-variables. (useful for specifying additional attribute-value fields in the URL) This function can be used from the startup, branch, failure, request and timer routes. Example 1.9. rest_append_hf usage ... # Example of querying a REST service requiring additional headers rest_append_hf("Authorization: Bearer mF_9.B5f-4.1JqM"); if (!rest_get("http://getcredit.org/?ruri=$fU", "$var(credit)")) { ... } ... 1.5. Exported Asynchronous Functions 1.5.1. rest_get(url, body_pv[, [ctype_pv][, [retcode_pv]]]) Sends a GET HTTP request. This function behaves exactly the same as rest_get (in terms of input, output and processing), but in an asynchronous way. Script execution is suspended until the entire content of the HTTP response if available. Example 1.10. async rest_get usage route { ... async(rest_get("http://getcredit.org/?ruri=$fU", "$var(credit)", , "$var(rcode)"), resume); } route [resume] { if ($rc < 0) { xlog("Error code $var(rcode) in HTTP GET!\n"); send_reply("403", "GET Forbidden"); exit; } ...... } 1.5.2. rest_post(url, send_body_pv, [send_ctype_pv], recv_body_pv[, [recv_ctype_pv][, [retcode_pv]]]) Sends a POST HTTP request. This function behaves exactly the same as rest_post (in terms of input, output and processing), but in an asynchronous way. Script execution is suspended until the entire content of the HTTP response if available. Example 1.11. async rest_post usage route { ... async(rest_post("http://myserver.org/register_user", "$fU", , "$ var(body)", "$var(ct)", "$var(rcode)"), resume); } route [resume] { if ($rc < 0) { xlog("Error code $var(rcode) in HTTP POST!\n"); send_reply("403", "POST Forbidden"); exit; } ...... } opensips-2.2.2/modules/rest_client/doc/000077500000000000000000000000001300170765700201135ustar00rootroot00000000000000opensips-2.2.2/modules/rest_client/doc/rest_client.xml000066400000000000000000000020061300170765700231460ustar00rootroot00000000000000 %docentities; ]> rest_client Module Liviu Chircu OpenSIPS Solutions liviu@opensips.org Liviu Chircu
liviu@opensips.org
2013 &osipssol; $Revision$ $Date$
&admin;
opensips-2.2.2/modules/rest_client/doc/rest_client_admin.xml000066400000000000000000000261101300170765700243200ustar00rootroot00000000000000 &adminguide;
Overview The rest_client module provides a means of interacting with an HTTP server by doing RESTful queries, such as GET and POST.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules..
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libcurl.
Exported Parameters
<varname>connection_timeout</varname> (integer) Maximum time allowed to establish a connection with the server. Default value is 20 seconds. Setting the <varname>connection_timeout</varname> parameter ... modparam("rest_client", "connection_timeout", 300) ...
<varname>connect_poll_interval</varname> (integer) Allows complete control over how quickly we want to detect libcurl's completed TCP handshakes, so the transfers can be started. A lower "connect_poll_interval" will speed up all HTTP transfers, but will also increase CPU usage. Default value is 20 milliseconds. Setting the <varname>connect_poll_interval</varname> parameter ... modparam("rest_client", "connect_poll_interval", 2) ...
<varname>curl_timeout</varname> (integer) Maximum time allowed for the libcurl transfer to complete. Default value is 20 seconds. Setting the <varname>curl_timeout</varname> parameter ... modparam("rest_client", "curl_timeout", 300) ...
<varname>ssl_verifypeer</varname> (integer) Set this to 0 in order to disable the verification of the remote peer's certificate. Verification is done using a default bundle of CA certificates which come with libcurl. Default value is 1 (enabled). Setting the <varname>ssl_verifypeer</varname> parameter ... modparam("rest_client", "ssl_verifypeer", 0) ...
<varname>ssl_verifyhost</varname> (integer) Set this to 0 in order to disable the verification that the remote peer actually corresponds to the server listed in the certificate. Default value is 1 (enabled). Setting the <varname>ssl_verifyhost</varname> parameter ... modparam("rest_client", "ssl_verifyhost", 0) ...
<varname>ssl_capath</varname> (integer) An optional path for CA certificates to be used for host verifications. Setting the <varname>ssl_capath</varname> parameter ... modparam("rest_client", "ssl_capath", "/home/opensips/ca_certificates") ...
Exported Functions
<function moreinfo="none">rest_get(url, body_pv[, [ctype_pv][, [retcode_pv]]]) </function> Issues an HTTP GET request to the given 'url', and returns a representation of the resource. The body_pv pseudo-var will hold the body of the HTTP response. The optional ctype_pv pseudo-var will contain the value of the "Content-Type:" header. The optional retcode_pv pseudo-var is used to retain the HTTP status code of the response message. Since the module is based on libcurl, a 0 value means no HTTP reply arrived at all. Possible parameter types url - String, pseudo-variable, or a String which includes pseudo-variables. (useful for specifying additional attribute-value fields in the URL) body_pv, ctype_pv, retcode_pv - pseudo-variables This function can be used from the startup, branch, failure, request and timer routes. <function moreinfo="none">rest_get</function> usage ... # Example of querying a REST service to get the credit of an account if (!rest_get("http://getcredit.org/?ruri=$fU", "$var(credit)", "$var(ct)", "$var(rcode)")) { xlog("Error code $var(rcode) in HTTP GET!\n"); send_reply("403", "Not registered"); exit; } ...
<function moreinfo="none">rest_post(url, send_body_pv, [send_ctype_pv], recv_body_pv[, [recv_ctype_pv][, [retcode_pv]]]) </function> Issues an HTTP POST request to the specified url. The request body will be copied from the send_body_pv pseudo-variable. The MIME Content-Type header for the request will be taken from send_ctype_pv (default is "application/x-www-form-urlencoded") The mandatory recv_body_pv pseudo-var will hold the body of the HTTP response. The optional recv_ctype_pv parameter will contain the value of the "Content-Type" header of the response message. The optional retcode_pv pseudo-var parameter can be given in order to retrieve the HTTP status code of the response message. Since the module based on libcurl, a 0 value means no HTTP reply arrived at all. Possible parameter types url, send_body_pv, send_type_pv - String, pseudo-variable, or a String which includes pseudo-variables. recv_body_pv, recv_ctype_pv, retcode_pv - pseudo-variables This function can be used from the startup, branch, failure, request and timer routes. <function moreinfo="none">rest_post</function> usage ... # Storing data using a RESTful service with an HTTP POST request if (!rest_post("http://myserver.org/register_user", "$fU", , "$var(body)", "$var(ct)", "$var(rcode)")) { xlog("Error code $var(rcode) in HTTP POST!\n"); send_reply("403", "POST Forbidden"); exit; } ...
<function moreinfo="none">rest_append_hf(txt)</function> Appends 'txt' to the HTTP headers of the subsequent request. Multiple headers can be appended by making multiple calls before executing a request. The contents of txt should adhere to the specification for HTTP headers (ex. Field: Value) Parameter types txt - String, pseudo-variable, or a String which includes pseudo-variables. (useful for specifying additional attribute-value fields in the URL) This function can be used from the startup, branch, failure, request and timer routes. <function moreinfo="none">rest_append_hf</function> usage ... # Example of querying a REST service requiring additional headers rest_append_hf("Authorization: Bearer mF_9.B5f-4.1JqM"); if (!rest_get("http://getcredit.org/?ruri=$fU", "$var(credit)")) { ... } ...
Exported Asynchronous Functions
<function moreinfo="none">rest_get(url, body_pv[, [ctype_pv][, [retcode_pv]]]) </function> Sends a GET HTTP request. This function behaves exactly the same as (in terms of input, output and processing), but in an asynchronous way. Script execution is suspended until the entire content of the HTTP response if available. <function moreinfo="none">async rest_get</function> usage route { ... async(rest_get("http://getcredit.org/?ruri=$fU", "$var(credit)", , "$var(rcode)"), resume); } route [resume] { if ($rc < 0) { xlog("Error code $var(rcode) in HTTP GET!\n"); send_reply("403", "GET Forbidden"); exit; } ...... }
<function moreinfo="none">rest_post(url, send_body_pv, [send_ctype_pv], recv_body_pv[, [recv_ctype_pv][, [retcode_pv]]]) </function> Sends a POST HTTP request. This function behaves exactly the same as (in terms of input, output and processing), but in an asynchronous way. Script execution is suspended until the entire content of the HTTP response if available. <function moreinfo="none">async rest_post</function> usage route { ... async(rest_post("http://myserver.org/register_user", "$fU", , "$var(body)", "$var(ct)", "$var(rcode)"), resume); } route [resume] { if ($rc < 0) { xlog("Error code $var(rcode) in HTTP POST!\n"); send_reply("403", "POST Forbidden"); exit; } ...... }
opensips-2.2.2/modules/rest_client/rest_cb.c000066400000000000000000000047371300170765700211460ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-02-28: Created (Liviu) */ #include #include #include #include "rest_cb.h" /** * write_func - callback; reallocates and extends the @body buffer each time. * @ptr: pointer to the raw data * @size: size of a block * @nmemb: number of blocks * @body: parameter previously set with the CURLOPT_WRITEDATA option */ size_t write_func(char *ptr, size_t size, size_t nmemb, void *body) { int len = size * nmemb; str *buff = (str *)body; if (len == 0) return 0; if (len < 0) len = strlen(ptr); buff->s = pkg_realloc(buff->s, buff->len + len + 1); if (!buff->s) { buff->len = 0; LM_ERR("No more pkg memory!\n"); return E_OUT_OF_MEM; } memcpy(buff->s + buff->len, ptr, len); buff->len += len; buff->s[buff->len] = '\0'; return len; } /** * header_func - callback; called once for each header. retrieves "Content-Type" * @ptr: pointer to the current header info * @size: size of a block * @nmemb: number of blocks * @body: parameter previously set with the CURLOPT_HEADERFUNCTION option */ size_t header_func(char *ptr, size_t size, size_t nmemb, void *userdata) { int len, left; str *st = (str *)userdata; len = left = size * nmemb; if (len > CONTENT_TYPE_HDR_LEN && *ptr == 'C' && strncasecmp(ptr, HTTP_HDR_CONTENT_TYPE, CONTENT_TYPE_HDR_LEN) == 0) { ptr += CONTENT_TYPE_HDR_LEN + 1; left -= CONTENT_TYPE_HDR_LEN + 1; while (*ptr == ' ') { ptr++; left--; } st->s = pkg_realloc(st->s, left); if (!st->s) { LM_ERR("no more pkg mem\n"); return E_OUT_OF_MEM; } st->len = left; memcpy(st->s, ptr, left); trim(st); } LM_DBG("Received: %.*s\n", len, ptr); return len; } opensips-2.2.2/modules/rest_client/rest_cb.h000066400000000000000000000026021300170765700211400ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-02-28: Created (Liviu) */ #ifndef _REST_CB_H_ #define _REST_CB_H_ #include "../../str.h" #include "../../mem/mem.h" #include "../../error.h" #include "../../dprint.h" #include "../../pvar.h" #include "../../trim.h" #define HTTP_HDR_CONTENT_TYPE "Content-Type" #define CONTENT_TYPE_HDR_LEN 12 #define MAX_CONTENT_TYPE_LEN 64 #define MAX_HEADER_FIELD_LEN 1024 /* arbitrary */ size_t write_func(char *ptr, size_t size, size_t nmemb, void *userdata); size_t header_func(char *ptr, size_t size, size_t nmemb, void *userdata); #endif /* _REST_CB_H_ */ opensips-2.2.2/modules/rest_client/rest_client.c000066400000000000000000000313471300170765700220350ustar00rootroot00000000000000/* * Copyright (C) 2013-2015 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-02-28: Created (Liviu) */ #include #include #include #include "../../async.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mod_fix.h" #include "rest_methods.h" /* * Module parameters */ long connection_timeout = 20; /* s */ long connect_poll_interval = 20; /* ms */ long connection_timeout_ms; long curl_timeout = 20; char *ssl_capath; /* libcurl enables these by default */ int ssl_verifypeer = 1; int ssl_verifyhost = 1; /* * Module initialization and cleanup */ static int mod_init(void); static int child_init(int rank); static void mod_destroy(void); /* * Fixup functions */ static int fixup_rest_get(void **param, int param_no); static int fixup_rest_post(void **param, int param_no); /* * Function headers */ static int w_rest_get(struct sip_msg *msg, char *gp_url, char *body_pv, char *ctype_pv, char *code_pv); static int w_rest_post(struct sip_msg *msg, char *gp_url, char *gp_body, char *gp_ctype, char *body_pv, char *ctype_pv, char *code_pv); static int w_async_rest_get(struct sip_msg *msg, async_resume_module **resume_f, void **resume_param, char *gp_url, char *body_pv, char *ctype_pv, char *code_pv); static int w_async_rest_post(struct sip_msg *msg, async_resume_module **resume_f, void **resume_param, char *gp_url, char *gp_body, char *gp_ctype, char *body_pv, char *ctype_pv, char *code_pv); static int w_rest_append_hf(struct sip_msg *msg, char *gp_hfv); static acmd_export_t acmds[] = { { "rest_get", (acmd_function)w_async_rest_get, 2, fixup_rest_get }, { "rest_get", (acmd_function)w_async_rest_get, 3, fixup_rest_get }, { "rest_get", (acmd_function)w_async_rest_get, 4, fixup_rest_get }, { "rest_post", (acmd_function)w_async_rest_post, 4, fixup_rest_post }, { "rest_post", (acmd_function)w_async_rest_post, 5, fixup_rest_post }, { "rest_post", (acmd_function)w_async_rest_post, 6, fixup_rest_post }, { 0, 0, 0, 0 } }; /* * Exported functions */ static cmd_export_t cmds[] = { { "rest_get",(cmd_function)w_rest_get, 2, fixup_rest_get, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|STARTUP_ROUTE|TIMER_ROUTE }, { "rest_get",(cmd_function)w_rest_get, 3, fixup_rest_get, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|STARTUP_ROUTE|TIMER_ROUTE }, { "rest_get",(cmd_function)w_rest_get, 4, fixup_rest_get, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|STARTUP_ROUTE|TIMER_ROUTE }, { "rest_post",(cmd_function)w_rest_post, 4, fixup_rest_post, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|STARTUP_ROUTE|TIMER_ROUTE }, { "rest_post",(cmd_function)w_rest_post, 5, fixup_rest_post, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|STARTUP_ROUTE|TIMER_ROUTE }, { "rest_post",(cmd_function)w_rest_post, 6, fixup_rest_post, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|STARTUP_ROUTE|TIMER_ROUTE }, { "rest_append_hf",(cmd_function)w_rest_append_hf, 1, fixup_spve_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE| ONREPLY_ROUTE|STARTUP_ROUTE|TIMER_ROUTE }, { 0, 0, 0, 0, 0, 0 } }; /* * Exported parameters */ static param_export_t params[] = { { "connection_timeout", INT_PARAM, &connection_timeout }, { "connect_poll_interval", INT_PARAM, &connect_poll_interval }, { "curl_timeout", INT_PARAM, &curl_timeout }, { "ssl_capath", STR_PARAM, &ssl_capath }, { "ssl_verifypeer", INT_PARAM, &ssl_verifypeer }, { "ssl_verifyhost", INT_PARAM, &ssl_verifyhost }, { 0, 0, 0 } }; /* * Module parameter variables */ struct module_exports exports = { "rest_client", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ acmds, /* Exported async functions */ params, /* Exported parameters */ NULL, /* exported statistics */ NULL, /* exported MI functions */ NULL, /* exported pseudo-variables */ NULL, /* extra processes */ mod_init, /* module initialization function */ NULL, /* response function*/ mod_destroy, child_init,/* per-child init function */ }; static void *osips_malloc(size_t size) { void *p = pkg_malloc(size); return p; } static void *osips_calloc(size_t nmemb, size_t size) { void *p = pkg_malloc(nmemb * size); if (p) memset(p, '\0', nmemb * size); return p; } static void *osips_realloc(void *ptr, size_t size) { void *p = pkg_realloc(ptr, size); return p; } static char *osips_strdup(const char *cp) { char *rval; int len; len = strlen(cp) + 1; rval = pkg_malloc(len); if (!rval) return NULL; memcpy(rval, cp, len); return rval; } static void osips_free(void *ptr) { if (ptr) pkg_free(ptr); } static int mod_init(void) { LM_DBG("Initializing...\n"); connection_timeout_ms = connection_timeout * 1000L; if (connect_poll_interval < 0) { LM_ERR("Bad connect_poll_interval (%ldms), setting to 20ms\n", connect_poll_interval); connect_poll_interval = 20; } curl_global_init_mem(CURL_GLOBAL_ALL, osips_malloc, osips_free, osips_realloc, osips_strdup, osips_calloc); multi_handle = curl_multi_init(); LM_INFO("Module initialized!\n"); return 0; } static int child_init(int rank) { if (rank <= PROC_MAIN) return 0; multi_handle = curl_multi_init(); if (!multi_handle) { LM_ERR("failed to init CURLM handle\n"); return -1; } return 0; } static void mod_destroy(void) { curl_global_cleanup(); } /**************************** Fixup functions *******************************/ static int fixup_rest_get(void **param, int param_no) { switch (param_no) { case 1: return fixup_spve(param); case 2: case 3: case 4: return fixup_pvar(param); default: LM_ERR("Too many parameters!\n"); return -1; } } static int fixup_rest_post(void **param, int param_no) { switch (param_no) { case 1: case 2: case 3: return fixup_spve(param); case 4: case 5: case 6: return fixup_pvar(param); default: LM_ERR("Too many parameters!\n"); return -1; } } /**************************** Module functions *******************************/ static int w_rest_get(struct sip_msg *msg, char *gp_url, char *body_pv, char *ctype_pv, char *code_pv) { str url; if (fixup_get_svalue(msg, (gparam_p)gp_url, &url) != 0) { LM_ERR("Invalid HTTP URL pseudo variable!\n"); return -1; } return rest_get_method(msg, url.s, (pv_spec_p)body_pv, (pv_spec_p)ctype_pv, (pv_spec_p)code_pv); } static int w_rest_post(struct sip_msg *msg, char *gp_url, char *gp_body, char *gp_ctype, char *body_pv, char *ctype_pv, char *code_pv) { str url, body, ctype = { NULL, 0 }; if (fixup_get_svalue(msg, (gparam_p)gp_url, &url) != 0) { LM_ERR("Invalid HTTP URL pseudo variable!\n"); return -1; } if (fixup_get_svalue(msg, (gparam_p)gp_body, &body) != 0) { LM_ERR("Invalid HTTP POST body pseudo variable!\n"); return -1; } if (gp_ctype && fixup_get_svalue(msg, (gparam_p)gp_ctype, &ctype) != 0) { LM_ERR("Invalid HTTP POST content type pseudo variable!\n"); return -1; } return rest_post_method(msg, url.s, body.s, ctype.s, (pv_spec_p)body_pv, (pv_spec_p)ctype_pv, (pv_spec_p)code_pv); } static void set_output_pv_params(struct sip_msg *msg, str *body_in, pv_spec_p body_pv, str *ctype_in, pv_spec_p ctype_pv, CURL *handle, pv_spec_p code_pv) { pv_value_t val; long http_rc; CURLcode rc; val.flags = PV_VAL_STR; val.rs = *body_in; if (pv_set_value(msg, (pv_spec_p)body_pv, 0, &val) != 0) LM_ERR("failed to set output body pv\n"); if (ctype_pv) { val.rs = *ctype_in; if (pv_set_value(msg, (pv_spec_p)ctype_pv, 0, &val) != 0) LM_ERR("failed to set output ctype pv\n"); } if (code_pv) { rc = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_rc); if (rc != CURLE_OK) LM_ERR("curl_easy_getinfo: %s\n", curl_easy_strerror(rc)); LM_DBG("Last response code: %ld\n", http_rc); val.flags = PV_VAL_INT|PV_TYPE_INT; val.ri = (int)http_rc; if (pv_set_value(msg, (pv_spec_p)code_pv, 0, &val) != 0) LM_ERR("failed to set output code pv\n"); } } static int w_async_rest_get(struct sip_msg *msg, async_resume_module **resume_f, void **resume_param, char *gp_url, char *body_pv, char *ctype_pv, char *code_pv) { rest_async_param *param; str url; int read_fd; if (fixup_get_svalue(msg, (gparam_p)gp_url, &url) != 0) { LM_ERR("Invalid HTTP URL pseudo variable!\n"); return -1; } LM_DBG("async rest get %.*s %p %p %p\n", url.len, url.s, body_pv, ctype_pv, code_pv); param = pkg_malloc(sizeof *param); if (!param) { LM_ERR("no more shm\n"); return -1; } memset(param, '\0', sizeof *param); read_fd = start_async_http_req(msg, REST_CLIENT_GET, url.s, NULL, NULL, ¶m->handle, ¶m->body, ctype_pv ? ¶m->ctype : NULL); /* error occurred; no transfer done */ if (read_fd == ASYNC_NO_IO) { *resume_param = NULL; *resume_f = NULL; /* keep default async status of NO_IO */ return -1; /* no need for async - transfer already completed! */ } else if (read_fd == ASYNC_SYNC) { set_output_pv_params(msg, ¶m->body, (pv_spec_p)body_pv, ¶m->ctype, (pv_spec_p)ctype_pv, param->handle, (pv_spec_p)code_pv); pkg_free(param->body.s); if (ctype_pv && param->ctype.s) pkg_free(param->ctype.s); curl_easy_cleanup(param->handle); pkg_free(param); return ASYNC_SYNC; } *resume_f = resume_async_http_req; param->method = REST_CLIENT_GET; param->body_pv = (pv_spec_p)body_pv; param->ctype_pv = (pv_spec_p)ctype_pv; param->code_pv = (pv_spec_p)code_pv; *resume_param = param; /* async started with success */ async_status = read_fd; return 1; } static int w_async_rest_post(struct sip_msg *msg, async_resume_module **resume_f, void **resume_param, char *gp_url, char *gp_body, char *gp_ctype, char *body_pv, char *ctype_pv, char *code_pv) { rest_async_param *param; str url, body, ctype = { NULL, 0 }; int read_fd; if (fixup_get_svalue(msg, (gparam_p)gp_url, &url) != 0) { LM_ERR("Invalid HTTP URL pseudo variable!\n"); return -1; } if (fixup_get_svalue(msg, (gparam_p)gp_body, &body) != 0) { LM_ERR("Invalid HTTP POST body pseudo variable!\n"); return -1; } if (gp_ctype && fixup_get_svalue(msg, (gparam_p)gp_ctype, &ctype) != 0) { LM_ERR("Invalid HTTP POST content type pseudo variable!\n"); return -1; } LM_DBG("async rest post '%.*s' %p %p %p\n", url.len, url.s, body_pv, ctype_pv, code_pv); param = pkg_malloc(sizeof *param); if (!param) { LM_ERR("no more shm\n"); return -1; } memset(param, '\0', sizeof *param); read_fd = start_async_http_req(msg, REST_CLIENT_POST, url.s, body.s, ctype.s, ¶m->handle, ¶m->body, ctype_pv ? ¶m->ctype : NULL); /* error occurred; no transfer done */ if (read_fd == ASYNC_NO_IO) { *resume_param = NULL; *resume_f = NULL; /* keep default async status of NO_IO */ return -1; /* no need for async - transfer already completed! */ } else if (read_fd == ASYNC_SYNC) { set_output_pv_params(msg, ¶m->body, (pv_spec_p)body_pv, ¶m->ctype, (pv_spec_p)ctype_pv, param->handle, (pv_spec_p)code_pv); pkg_free(param->body.s); if (ctype_pv && param->ctype.s) pkg_free(param->ctype.s); curl_easy_cleanup(param->handle); pkg_free(param); return ASYNC_SYNC; } *resume_f = resume_async_http_req; param->method = REST_CLIENT_POST; param->body_pv = (pv_spec_p)body_pv; param->ctype_pv = (pv_spec_p)ctype_pv; param->code_pv = (pv_spec_p)code_pv; *resume_param = param; /* async started with success */ async_status = read_fd; return 1; } static int w_rest_append_hf(struct sip_msg *msg, char *gp_hfv) { str hfv; if (fixup_get_svalue(msg, (gparam_p)gp_hfv, &hfv) != 0) { LM_ERR("cannot retrieve header field value\n"); return -1; } return rest_append_hf_method(msg, &hfv); } opensips-2.2.2/modules/rest_client/rest_methods.c000066400000000000000000000400471300170765700222170ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-02-28: Created (Liviu) */ #include #include #include #include #include "../../mem/shm_mem.h" #include "../../async.h" #include "rest_methods.h" #include "rest_cb.h" static char print_buff[MAX_CONTENT_TYPE_LEN]; CURLM *multi_handle; /* additional HTTP headers for the next request */ static struct curl_slist *header_list = NULL; /* simultaneous ongoing transfers within this process */ static int transfers; static int read_fds[FD_SETSIZE]; /* libcurl's reported running handles */ static int running_handles; #define clean_header_list \ do { \ if (header_list) { \ curl_slist_free_all(header_list); \ header_list = NULL; \ } \ } while (0) #define w_curl_easy_setopt(h, opt, value) \ do { \ rc = curl_easy_setopt(h, opt, value); \ if (rc != CURLE_OK) { \ LM_ERR("curl_easy_setopt(%d): (%s)\n", opt, curl_easy_strerror(rc)); \ clean_header_list; \ goto cleanup; \ } \ } while (0) static inline char is_new_transfer(int fd) { int it; for (it = 0; it < transfers; it++) { if (fd == read_fds[it]) return 0; } return 1; } static inline void add_transfer(int fd) { read_fds[transfers++] = fd; } static inline char del_transfer(int fd) { int it; LM_DBG("del fd %d\n", fd); for (it = 0; it < transfers; it++) { if (fd == read_fds[it]) { transfers--; for (; it < transfers; it++) read_fds[it] = read_fds[it + 1]; return 0; } } return -1; } /** * start_async_http_req - performs an HTTP request, stores results in pvars * - TCP connect phase is synchronous, due to libcurl limitations * - TCP read phase is asynchronous, thanks to the libcurl multi interface * * @msg: sip message struct * @method: HTTP verb * @url: HTTP URL to be queried * @req_body: Body of the request (NULL if not needed) * @req_ctype: Value for the "Content-Type: " header of the request (same as ^) * @out_handle: CURL easy handle used to perform the transfer * @body: reply body; gradually reallocated as data arrives * @ctype: will eventually hold the last "Content-Type" header of the reply */ int start_async_http_req(struct sip_msg *msg, enum rest_client_method method, char *url, char *req_body, char *req_ctype, CURL **out_handle, str *body, str *ctype) { CURL *handle; CURLcode rc; CURLMcode mrc; fd_set rset, wset, eset; int max_fd, fd; long busy_wait, timeout; long retry_time; int msgs_in_queue; CURLMsg *cmsg; if (transfers == FD_SETSIZE) { LM_ERR("too many ongoing tranfers: %d\n", FD_SETSIZE); clean_header_list; return ASYNC_NO_IO; } handle = curl_easy_init(); if (!handle) { LM_ERR("Init curl handle failed!\n"); clean_header_list; return ASYNC_NO_IO; } w_curl_easy_setopt(handle, CURLOPT_URL, url); switch (method) { case REST_CLIENT_POST: w_curl_easy_setopt(handle, CURLOPT_POST, 1); w_curl_easy_setopt(handle, CURLOPT_POSTFIELDS, req_body); if (req_ctype) { sprintf(print_buff, "Content-Type: %s", req_ctype); header_list = curl_slist_append(header_list, print_buff); w_curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header_list); } break; case REST_CLIENT_GET: break; default: LM_ERR("Unsupported rest_client_method: %d, defaulting to GET\n", method); } if (header_list) w_curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header_list); w_curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, connection_timeout); w_curl_easy_setopt(handle, CURLOPT_TIMEOUT, curl_timeout); w_curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); w_curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1); w_curl_easy_setopt(handle, CURLOPT_STDERR, stdout); w_curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_func); w_curl_easy_setopt(handle, CURLOPT_WRITEDATA, body); if (ctype) { w_curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, header_func); w_curl_easy_setopt(handle, CURLOPT_HEADERDATA, ctype); } if (ssl_capath) w_curl_easy_setopt(handle, CURLOPT_CAPATH, ssl_capath); if (!ssl_verifypeer) w_curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); if (!ssl_verifyhost) w_curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L); curl_multi_add_handle(multi_handle, handle); timeout = connection_timeout_ms; busy_wait = connect_poll_interval; /* obtain a read fd in "connection_timeout" seconds at worst */ for (timeout = connection_timeout_ms; timeout > 0; timeout -= busy_wait) { mrc = curl_multi_perform(multi_handle, &running_handles); if (mrc != CURLM_OK) { LM_ERR("curl_multi_perform: %s\n", curl_multi_strerror(mrc)); goto error; } mrc = curl_multi_timeout(multi_handle, &retry_time); if (mrc != CURLM_OK) { LM_ERR("curl_multi_timeout: %s\n", curl_multi_strerror(mrc)); goto error; } LM_DBG("libcurl TCP connect: we should wait up to %ldms " "(timeout=%ldms, poll=%ldms)!\n", retry_time, connection_timeout_ms, connect_poll_interval); if (retry_time == -1) { LM_INFO("curl_multi_timeout() returned -1, pausing %ldms...\n", busy_wait); goto busy_wait; } if (retry_time > connection_timeout_ms) LM_INFO("initial TCP connect: we must wait at least %ldms! Please " "consider increasing 'connection_timeout'!\n", retry_time); busy_wait = retry_time < timeout ? retry_time : timeout; /* transfer may have already been completed!! */ while ((cmsg = curl_multi_info_read(multi_handle, &msgs_in_queue))) { if (cmsg->easy_handle == handle && cmsg->msg == CURLMSG_DONE) { LM_DBG("done, no need for async!\n"); clean_header_list; *out_handle = handle; return ASYNC_SYNC; } } FD_ZERO(&rset); mrc = curl_multi_fdset(multi_handle, &rset, &wset, &eset, &max_fd); if (mrc != CURLM_OK) { LM_ERR("curl_multi_fdset: %s\n", curl_multi_strerror(mrc)); goto error; } if (max_fd != -1) { for (fd = 0; fd <= max_fd; fd++) { if (FD_ISSET(fd, &rset)) { LM_DBG("ongoing transfer on fd %d\n", fd); if (is_new_transfer(fd)) { LM_DBG(">>> add fd %d to ongoing transfers\n", fd); add_transfer(fd); goto success; } } } } /* * from curl_multi_timeout() docs: "retry_time" milliseconds "at most!" * -> we'll only wait "connect_poll_interval" ms */ busy_wait = connect_poll_interval < timeout ? connect_poll_interval : timeout; busy_wait: /* libcurl seems to be stuck in internal operations (TCP connect?) */ LM_DBG("busy waiting %ldms ...\n", busy_wait); usleep(1000UL * busy_wait); } LM_ERR("timeout while connecting to '%s' (%ld sec)\n", url, connection_timeout); goto error; success: clean_header_list; *out_handle = handle; return fd; error: mrc = curl_multi_remove_handle(multi_handle, handle); if (mrc != CURLM_OK) LM_ERR("curl_multi_remove_handle: %s\n", curl_multi_strerror(mrc)); cleanup: clean_header_list; curl_easy_cleanup(handle); return ASYNC_NO_IO; } enum async_ret_code resume_async_http_req(int fd, struct sip_msg *msg, void *_param) { CURLcode rc; CURLMcode mrc; rest_async_param *param = (rest_async_param *)_param; int running, max_fd; long http_rc; fd_set rset, wset, eset; pv_value_t val; mrc = curl_multi_perform(multi_handle, &running); if (mrc != CURLM_OK) { LM_ERR("curl_multi_perform: %s\n", curl_multi_strerror(mrc)); return -1; } LM_DBG("running handles: %d\n", running); if (running == running_handles) { async_status = ASYNC_CONTINUE; return 1; } if (running > running_handles) { LM_BUG("incremented handles!!"); /* default async status is DONE */ return -1; } running_handles = running; FD_ZERO(&rset); mrc = curl_multi_fdset(multi_handle, &rset, &wset, &eset, &max_fd); if (mrc != CURLM_OK) { LM_ERR("curl_multi_fdset: %s\n", curl_multi_strerror(mrc)); /* default async status is DONE */ return -1; } if (max_fd == -1) { if (running_handles != 0) { LM_BUG("running_handles == %d", running_handles); abort(); /* default async status is DONE */ return -1; } if (FD_ISSET(fd, &rset)) { LM_BUG("fd %d is still in rset!", fd); abort(); /* default async status is DONE */ return -1; } } else if (FD_ISSET(fd, &rset)) { LM_DBG("fd %d still transferring...\n", fd); async_status = ASYNC_CONTINUE; return 1; } if (del_transfer(fd) != 0) { LM_BUG("failed to delete fd %d", fd); abort(); /* default async status is DONE */ return -1; } mrc = curl_multi_remove_handle(multi_handle, param->handle); if (mrc != CURLM_OK) { LM_ERR("curl_multi_remove_handle: %s\n", curl_multi_strerror(mrc)); /* default async status is DONE */ return -1; } val.flags = PV_VAL_STR; val.rs = param->body; if (pv_set_value(msg, param->body_pv, 0, &val) != 0) LM_ERR("failed to set output body pv\n"); if (param->ctype_pv) { val.rs = param->ctype; if (pv_set_value(msg, param->ctype_pv, 0, &val) != 0) LM_ERR("failed to set output ctype pv\n"); } if (param->code_pv) { rc = curl_easy_getinfo(param->handle, CURLINFO_RESPONSE_CODE, &http_rc); if (rc != CURLE_OK) { LM_ERR("curl_easy_getinfo: %s\n", curl_easy_strerror(rc)); http_rc = 0; } LM_DBG("Last response code: %ld\n", http_rc); val.flags = PV_VAL_INT|PV_TYPE_INT; val.ri = (int)http_rc; if (pv_set_value(msg, param->code_pv, 0, &val) != 0) LM_ERR("failed to set output code pv\n"); } pkg_free(param->body.s); if (param->ctype_pv && param->ctype.s) pkg_free(param->ctype.s); curl_easy_cleanup(param->handle); pkg_free(param); /* default async status is DONE */ return 1; } /** * rest_get_method - performs an HTTP GET request, stores results in pvars * @msg: sip message struct * @url: HTTP URL to be queried * @body_pv: pseudo var which will hold the result body * @ctype_pv: pvar which will hold the body encoding method * @code_pv: pvar to hold the HTTP return code */ int rest_get_method(struct sip_msg *msg, char *url, pv_spec_p body_pv, pv_spec_p ctype_pv, pv_spec_p code_pv) { CURLcode rc; CURL *handle = NULL; long http_rc; pv_value_t pv_val; str st = { 0, 0 }; str body = { NULL, 0 }, tbody; handle = curl_easy_init(); if (!handle) { LM_ERR("Init curl handle failed!\n"); clean_header_list; return -1; } if (header_list) w_curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header_list); w_curl_easy_setopt(handle, CURLOPT_URL, url); w_curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, connection_timeout); w_curl_easy_setopt(handle, CURLOPT_TIMEOUT, curl_timeout); w_curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); w_curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1); w_curl_easy_setopt(handle, CURLOPT_STDERR, stdout); w_curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_func); w_curl_easy_setopt(handle, CURLOPT_WRITEDATA, &body); w_curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, header_func); w_curl_easy_setopt(handle, CURLOPT_HEADERDATA, &st); if (ssl_capath) w_curl_easy_setopt(handle, CURLOPT_CAPATH, ssl_capath); if (!ssl_verifypeer) w_curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); if (!ssl_verifyhost) w_curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L); rc = curl_easy_perform(handle); clean_header_list; if (code_pv) { curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_rc); LM_DBG("Last response code: %ld\n", http_rc); pv_val.flags = PV_VAL_INT|PV_TYPE_INT; pv_val.ri = (int)http_rc; if (pv_set_value(msg, code_pv, 0, &pv_val) != 0) { LM_ERR("Set code pv value failed!\n"); goto cleanup; } } if (rc != CURLE_OK) { LM_ERR("curl_easy_perform: %s\n", curl_easy_strerror(rc)); goto cleanup; } tbody = body; trim(&tbody); pv_val.flags = PV_VAL_STR; pv_val.rs = tbody; if (pv_set_value(msg, body_pv, 0, &pv_val) != 0) { LM_ERR("Set body pv value failed!\n"); goto cleanup; } if (body.s) { pkg_free(body.s); } if (ctype_pv) { pv_val.rs = st; if (pv_set_value(msg, ctype_pv, 0, &pv_val) != 0) { LM_ERR("Set content type pv value failed!\n"); goto cleanup; } if (st.s) pkg_free(st.s); } curl_easy_cleanup(handle); return 1; cleanup: curl_easy_cleanup(handle); return -1; } /** * rest_post_method - performs an HTTP POST request, stores results in pvars * @msg: sip message struct * @url: HTTP URL to be queried * @ctype: Value for the "Content-Type: " header of the request * @body: Body of the request * @body_pv: pseudo var which will hold the result body * @ctype_pv: pvar which will hold the result content type * @code_pv: pvar to hold the HTTP return code */ int rest_post_method(struct sip_msg *msg, char *url, char *body, char *ctype, pv_spec_p body_pv, pv_spec_p ctype_pv, pv_spec_p code_pv) { CURLcode rc; CURL *handle = NULL; long http_rc; str st = { 0, 0 }; str res_body = { NULL, 0 }, tbody; pv_value_t pv_val; handle = curl_easy_init(); if (!handle) { LM_ERR("Init curl handle failed!\n"); clean_header_list; return -1; } if (ctype) { sprintf(print_buff, "Content-Type: %s", ctype); header_list = curl_slist_append(header_list, print_buff); } if (header_list) w_curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header_list); w_curl_easy_setopt(handle, CURLOPT_URL, url); w_curl_easy_setopt(handle, CURLOPT_POST, 1); w_curl_easy_setopt(handle, CURLOPT_POSTFIELDS, body); w_curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, connection_timeout); w_curl_easy_setopt(handle, CURLOPT_TIMEOUT, curl_timeout); w_curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); w_curl_easy_setopt(handle, CURLOPT_STDERR, stdout); w_curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1); w_curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_func); w_curl_easy_setopt(handle, CURLOPT_WRITEDATA, &res_body); w_curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, header_func); w_curl_easy_setopt(handle, CURLOPT_HEADERDATA, &st); if (ssl_capath) w_curl_easy_setopt(handle, CURLOPT_CAPATH, ssl_capath); if (!ssl_verifypeer) w_curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); if (!ssl_verifyhost) w_curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L); rc = curl_easy_perform(handle); clean_header_list; if (code_pv) { curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_rc); LM_DBG("Last response code: %ld\n", http_rc); pv_val.flags = PV_VAL_INT|PV_TYPE_INT; pv_val.ri = (int)http_rc; if (pv_set_value(msg, code_pv, 0, &pv_val) != 0) { LM_ERR("Set code pv value failed!\n"); goto cleanup; } } if (rc != CURLE_OK) { LM_ERR("curl_easy_perform: %s\n", curl_easy_strerror(rc)); goto cleanup; } tbody = res_body; trim(&tbody); pv_val.flags = PV_VAL_STR; pv_val.rs = tbody; if (pv_set_value(msg, body_pv, 0, &pv_val) != 0) { LM_ERR("Set body pv value failed!\n"); goto cleanup; } if (res_body.s) { pkg_free(res_body.s); } if (ctype_pv) { pv_val.rs = st; if (pv_set_value(msg, ctype_pv, 0, &pv_val) != 0) { LM_ERR("Set content type pv value failed!\n"); goto cleanup; } if (st.s) pkg_free(st.s); } curl_easy_cleanup(handle); return 1; cleanup: curl_easy_cleanup(handle); return -1; } /** * rest_append_hf - add a custom HTTP header before a rest call * @msg: sip message struct * @hfv: HTTP header field and value */ int rest_append_hf_method(struct sip_msg *msg, str *hfv) { char buf[MAX_HEADER_FIELD_LEN]; if (hfv->len > MAX_HEADER_FIELD_LEN) { LM_ERR("header field buffer too small\n"); return -1; } /* TODO: header validation */ /* append the header to the global list */ strncpy(buf, hfv->s, hfv->len); header_list = curl_slist_append(header_list, buf); return 1; } opensips-2.2.2/modules/rest_client/rest_methods.h000066400000000000000000000042321300170765700222200ustar00rootroot00000000000000/* * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2013-02-28: Created (Liviu) */ #ifndef _REST_METHODS_ #define _REST_METHODS_ #include "../../pvar.h" #include "../../dprint.h" #include "../../error.h" #include "../../mem/mem.h" extern CURLM *multi_handle; extern long connection_timeout; extern long connect_poll_interval; extern long connection_timeout_ms; extern long curl_timeout; extern char *ssl_capath; extern int ssl_verifypeer; extern int ssl_verifyhost; /* Currently supported HTTP verbs */ enum rest_client_method { REST_CLIENT_GET, REST_CLIENT_POST }; typedef struct rest_async_param_ { enum rest_client_method method; CURL *handle; str body; str ctype; pv_spec_p body_pv; pv_spec_p ctype_pv; pv_spec_p code_pv; } rest_async_param; int rest_get_method(struct sip_msg *msg, char *url, pv_spec_p body_pv, pv_spec_p ctype_pv, pv_spec_p code_pv); int rest_post_method(struct sip_msg *msg, char *url, char *body, char *ctype, pv_spec_p body_pv, pv_spec_p ctype_pv, pv_spec_p code_pv); int start_async_http_req(struct sip_msg *msg, enum rest_client_method method, char *url, char *req_body, char *req_ctype, CURL **out_handle, str *body, str *ctype); enum async_ret_code resume_async_http_req(int fd, struct sip_msg *msg, void *param); int rest_append_hf_method(struct sip_msg *msg, str *hfv); #endif /* _REST_METHODS_ */ opensips-2.2.2/modules/rls/000077500000000000000000000000001300170765700156335ustar00rootroot00000000000000opensips-2.2.2/modules/rls/Makefile000066400000000000000000000010311300170765700172660ustar00rootroot00000000000000# $Id$ # # Resource List Server # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=rls.so ifeq ($(CROSS_COMPILE),) XML2CFG=$(shell which xml2-config) endif ifneq ($(XML2CFG),) DEFS += $(shell $(XML2CFG) --cflags) LIBS += $(shell $(XML2CFG) --libs) else DEFS += -I$(SYSBASE)/include/libxml2 \ -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include LIBS += -L$(SYSBASE)/include/lib \ -L$(LOCALBASE)/lib -lxml2 endif include ../../Makefile.modules opensips-2.2.2/modules/rls/README000066400000000000000000000214201300170765700165120ustar00rootroot00000000000000Resource List Server Anca-Maria Vamanu Edited by Anca-Maria Vamanu Edited by Saul Ibarra Corretge Copyright © 2007 Voice Sistem SRL Revision History Revision $Revision: 9371 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. rlsubs_table(str) 1.3.2. rlpres_table(str) 1.3.3. clean_period (int) 1.3.4. waitn_time (int) 1.3.5. max_expires (int) 1.3.6. hash_size (int) 1.3.7. xcap_root (str) 1.3.8. to_presence_code (int) 1.3.9. rls_event (str) 1.3.10. presence_server (str) 1.3.11. server_address (str) 1.4. Exported Functions 1.4.1. rls_handle_subscribe() 1.4.2. rls_handle_notify() 1.5. Exported MI Functions 1.5.1. rls_update_subscriptions 1.6. Installation 2. Developer Guide List of Examples 1.1. Set rlsubs_table parameter 1.2. Set rlpres_table parameter 1.3. Set clean_period parameter 1.4. Set waitn_time parameter 1.5. Set max_expires parameter 1.6. Set hash_size parameter 1.7. Set hash_size parameter 1.8. Set to_presence_code parameter 1.9. Set rls_event parameter 1.10. Set presence_server parameter 1.11. Set server_address parameter 1.12. rls_handle_subscribe usage 1.13. rls_handle_notify usage Chapter 1. Admin Guide 1.1. Overview The modules is a Resource List Server implementation following the specification in RFC 4662 and RFC 4826. The server is independent from local presence servers, retrieving presence information with Subscribe-Notify messages. The module uses the presence module as a library, as it requires a resembling mechanism for handling Subscribe. Therefore, in case the local presence server is not collocated on the same machine with the RL server, the presence module should be loaded in a library mode only (see doc for presence module). It handles subscription to lists in an event independent way.The default event is presence, but if some other events are to be handled by the server, they should be added using the module parameter "rls_events". It works with XCAP server for storage. There is also the possibility to configure it to work in an integrated_xcap server mode, when it only queries database for the resource lists documents. This is useful in a small architecture when all the clients use an integrated server and there are no references to exterior documents in their lists. The same as presence module, it has a caching mode with periodical update in database for subscribe information. The information retrieved with Notify messages is stored in database only. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * a database module. * signaling. * tm. * presence- in a library mode. * pua. * xcap. 1.2.2. External Libraries or Applications * libxml-dev. 1.3. Exported Parameters 1.3.1. rlsubs_table(str) The name of the db table where resource lists subscription information is stored. Default value is “rls_watchersâ€. Example 1.1. Set rlsubs_table parameter ... modparam("rls", "rlsubs_table", "rls_subscriptions") ... 1.3.2. rlpres_table(str) The name of the db table where notified event specific information is stored. Default value is “rls_presentityâ€. Example 1.2. Set rlpres_table parameter ... modparam("rls", "rlpres_table", "rls_notify") ... 1.3.3. clean_period (int) The period at which to check for expired information. Default value is “100â€. Example 1.3. Set clean_period parameter ... modparam("rls", "clean_period", 100) ... 1.3.4. waitn_time (int) The timer period at which the server should attempt to send Notifies with the updated presence state of the subscribed list or watcher information. Default value is “50â€. Example 1.4. Set waitn_time parameter ... modparam("rls", "waitn_time", 10) ... 1.3.5. max_expires (int) The maximum accepted expires for a subscription to a list. Default value is “7200â€. Example 1.5. Set max_expires parameter ... modparam("rls", "max_expires", 10800) ... 1.3.6. hash_size (int) The dimension of the hash table used to store subscription to a list. This parameter will be used as the power of 2 when computing table size. Default value is “9 (512)â€. Example 1.6. Set hash_size parameter ... modparam("rls", "hash_size", 11) ... 1.3.7. xcap_root (str) The address of the xcap server. Default value is “NULLâ€. Example 1.7. Set hash_size parameter ... modparam("rls", "xcap_root", "http://192.168.2.132/xcap-root:800") ... 1.3.8. to_presence_code (int) The code to be returned by rls_handle_subscribe function if the processed Subscribe is not a resource list Subscribe. This code can be used in an architecture with presence and rls servers collocated on the same machine, to call handle_subscribe on the message causing this code. Default value is “0â€. Example 1.8. Set to_presence_code parameter ... modparam("rls", "to_presence_code", 10) ... 1.3.9. rls_event (str) The default event that RLS handles is presence. If some other events should also be handled by RLS they should be added using this parameter. It can be set more than once. Default value is “"presence"â€. Example 1.9. Set rls_event parameter ... modparam("rls", "rls_event", "dialog;sla") ... 1.3.10. presence_server (str) The address of the presence server. It will be used as outbound proxy for Subscribe requests sent by the RLS server to bouncing on and off the proxy and having to include special processing for this messages in the proxy's configuration file. Example 1.10. Set presence_server parameter ... modparam("rls", "presence_server", "sip:pres@opensips.org:5060") ... 1.3.11. server_address (str) The address of the server that will be used as a contact in sent Subscribe requests and 200 OK replies for Subscribe messages for RLS. It is compulsory. Example 1.11. Set server_address parameter ... modparam("rls", "server_address", "sip:rls@opensips.org:5060") ... 1.4. Exported Functions 1.4.1. rls_handle_subscribe() This function detects if a Subscribe message should be handled by RLS. If not it replies with the configured to_presence_code. If it is, it extracts the dialog info and sends aggregate Notify requests with information for the list. This function can be used from REQUEST_ROUTE. Example 1.12. rls_handle_subscribe usage ... For presence and rls on the same machine: modparam(rls, "to_presence_code", 10) if(is_method("SUBSCRIBE")) { $var(ret_code)= rls_handle_subscribe(); if($var(ret_code)== 10) handle_subscribe(); t_release(); } For rls only: if(is_method("SUBSCRIBE")) { rls_handle_subscribe(); t_release(); } ... 1.4.2. rls_handle_notify() This function has to be called for Notify messages sent by presence servers in reply to the Subscribe messages sent by RLS. This function can be used from REQUEST_ROUTE. It can return 3 codes: * 1 - the Notify was inside a dialog that was recognized by the RLS server and was processed successfully. * 2 - the Notify did not belog to a dialog initiated by the RLS server. * -1 - an error occurred during processing. Example 1.13. rls_handle_notify usage ... if(method=="NOTIFY") rls_handle_notify(); ... 1.5. Exported MI Functions 1.5.1. rls_update_subscriptions Triggers updating backend subscriptions after a resources-list or rls-services document has been updated. Name: rls_update_subscriptions Parameters: * presentity_uri : the uri of the user who made the change and whose subscriptions should be updated MI FIFO Command Format: :rls_update_subscriptions:fifo_reply sip:alice@atlanta.com _empty_line_ 1.6. Installation The module requires 2 table in OpenSIPS database: rls_presentity and rls_watchers.The SQL syntax to create them can be found in rls-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. Chapter 2. Developer Guide The module provides no functions to be used in other OpenSIPS modules. opensips-2.2.2/modules/rls/doc/000077500000000000000000000000001300170765700164005ustar00rootroot00000000000000opensips-2.2.2/modules/rls/doc/rls.xml000066400000000000000000000021671300170765700177300ustar00rootroot00000000000000 %docentities; ]> Resource List Server &osipsname; Anca-Maria Vamanu Anca-Maria Vamanu Saul Ibarra Corretge 2007 &voicesystem; $Revision: 9371 $ $Date$ &admin; &devel; &faq; opensips-2.2.2/modules/rls/doc/rls_admin.xml000066400000000000000000000260701300170765700210770ustar00rootroot00000000000000 &adminguide;
Overview The modules is a Resource List Server implementation following the specification in RFC 4662 and RFC 4826. The server is independent from local presence servers, retrieving presence information with Subscribe-Notify messages. The module uses the presence module as a library, as it requires a resembling mechanism for handling Subscribe. Therefore, in case the local presence server is not collocated on the same machine with the RL server, the presence module should be loaded in a library mode only (see doc for presence module). It handles subscription to lists in an event independent way.The default event is presence, but if some other events are to be handled by the server, they should be added using the module parameter "rls_events". It works with XCAP server for storage. There is also the possibility to configure it to work in an integrated_xcap server mode, when it only queries database for the resource lists documents. This is useful in a small architecture when all the clients use an integrated server and there are no references to exterior documents in their lists. The same as presence module, it has a caching mode with periodical update in database for subscribe information. The information retrieved with Notify messages is stored in database only.
Dependencies
&osips; Modules The following modules must be loaded before this module: a database module. signaling. tm. presence- in a library mode. pua. xcap.
External Libraries or Applications libxml-dev.
Exported Parameters
<varname>rlsubs_table</varname>(str) The name of the db table where resource lists subscription information is stored. Default value is rls_watchers. Set <varname>rlsubs_table</varname> parameter ... modparam("rls", "rlsubs_table", "rls_subscriptions") ...
<varname>rlpres_table</varname>(str) The name of the db table where notified event specific information is stored. Default value is rls_presentity. Set <varname>rlpres_table</varname> parameter ... modparam("rls", "rlpres_table", "rls_notify") ...
<varname>clean_period</varname> (int) The period at which to check for expired information. Default value is 100. Set <varname>clean_period</varname> parameter ... modparam("rls", "clean_period", 100) ...
<varname>waitn_time</varname> (int) The timer period at which the server should attempt to send Notifies with the updated presence state of the subscribed list or watcher information. Default value is 50. Set <varname>waitn_time</varname> parameter ... modparam("rls", "waitn_time", 10) ...
<varname>max_expires</varname> (int) The maximum accepted expires for a subscription to a list. Default value is 7200. Set <varname>max_expires</varname> parameter ... modparam("rls", "max_expires", 10800) ...
<varname>hash_size</varname> (int) The dimension of the hash table used to store subscription to a list. This parameter will be used as the power of 2 when computing table size. Default value is 9 (512). Set <varname>hash_size</varname> parameter ... modparam("rls", "hash_size", 11) ...
<varname>xcap_root</varname> (str) The address of the xcap server. Default value is NULL. Set <varname>hash_size</varname> parameter ... modparam("rls", "xcap_root", "http://192.168.2.132/xcap-root:800") ...
<varname>to_presence_code</varname> (int) The code to be returned by rls_handle_subscribe function if the processed Subscribe is not a resource list Subscribe. This code can be used in an architecture with presence and rls servers collocated on the same machine, to call handle_subscribe on the message causing this code. Default value is 0. Set <varname>to_presence_code</varname> parameter ... modparam("rls", "to_presence_code", 10) ...
<varname>rls_event</varname> (str) The default event that RLS handles is presence. If some other events should also be handled by RLS they should be added using this parameter. It can be set more than once. Default value is "presence". Set <varname>rls_event</varname> parameter ... modparam("rls", "rls_event", "dialog;sla") ...
<varname>presence_server</varname> (str) The address of the presence server. It will be used as outbound proxy for Subscribe requests sent by the RLS server to bouncing on and off the proxy and having to include special processing for this messages in the proxy's configuration file. Set <varname>presence_server</varname> parameter ... modparam("rls", "presence_server", "sip:pres@opensips.org:5060") ...
<varname>server_address</varname> (str) The address of the server that will be used as a contact in sent Subscribe requests and 200 OK replies for Subscribe messages for RLS. It is compulsory. Set <varname>server_address</varname> parameter ... modparam("rls", "server_address", "sip:rls@opensips.org:5060") ...
Exported Functions
<function moreinfo="none">rls_handle_subscribe()</function> This function detects if a Subscribe message should be handled by RLS. If not it replies with the configured to_presence_code. If it is, it extracts the dialog info and sends aggregate Notify requests with information for the list. This function can be used from REQUEST_ROUTE. <function>rls_handle_subscribe</function> usage ... For presence and rls on the same machine: modparam(rls, "to_presence_code", 10) if(is_method("SUBSCRIBE")) { $var(ret_code)= rls_handle_subscribe(); if($var(ret_code)== 10) handle_subscribe(); t_release(); } For rls only: if(is_method("SUBSCRIBE")) { rls_handle_subscribe(); t_release(); } ...
<function moreinfo="none">rls_handle_notify()</function> This function has to be called for Notify messages sent by presence servers in reply to the Subscribe messages sent by RLS. This function can be used from REQUEST_ROUTE. It can return 3 codes: 1 - the Notify was inside a dialog that was recognized by the RLS server and was processed successfully. 2 - the Notify did not belog to a dialog initiated by the RLS server. -1 - an error occurred during processing. <function>rls_handle_notify</function> usage ... if(method=="NOTIFY") rls_handle_notify(); ...
Exported MI Functions
<function moreinfo="none">rls_update_subscriptions</function> Triggers updating backend subscriptions after a resources-list or rls-services document has been updated. Name: rls_update_subscriptions Parameters: presentity_uri : the uri of the user who made the change and whose subscriptions should be updated MI FIFO Command Format: :rls_update_subscriptions:fifo_reply sip:alice@atlanta.com _empty_line_
Installation The module requires 2 table in OpenSIPS database: rls_presentity and rls_watchers.The SQL syntax to create them can be found in rls-create.sql script in the database directories in the opensips/scripts folder. You can also find the complete database documentation on the project webpage, &osipsdbdocslink;.
opensips-2.2.2/modules/rls/doc/rls_devel.xml000066400000000000000000000002741300170765700211040ustar00rootroot00000000000000 &develguide; The module provides no functions to be used in other &osips; modules. opensips-2.2.2/modules/rls/notify.c000066400000000000000000000635521300170765700173220ustar00rootroot00000000000000/* * rls module - resource list server * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-11 initial version (Anca Vamanu) */ #include #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../data_lump_rpl.h" #include "../../trim.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_event.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_cseq.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_rr.h" #include "../tm/dlg.h" #include "../presence/utils_func.h" #include "../presence/hash.h" #include "rls.h" #include "notify.h" #include #include char* global_instance_id = "Scf8UhwQ"; typedef struct res_param { xmlNodePtr list_node; db_res_t* db_result; str* cid_array; }res_param_t; int resource_uri_col=0, ctype_col, pres_state_col= 0, auth_state_col= 0, reason_col= 0; str* constr_rlmi_doc(db_res_t* result, str* rl_uri, int version, xmlNodePtr rl_node, str** cid_array, str username, str domain); str* constr_multipart_body(db_res_t* result, str* cid_array, str bstr); dlg_t* rls_notify_dlg(subs_t* subs); void rls_notify_callback( struct cell *t, int type, struct tmcb_params *ps); int rls_get_resource_list(str *filename, str *selector, str *username, str *domain, xmlNodePtr *rl_node, xmlDocPtr *xmldoc); int send_full_notify(subs_t* subs, xmlNodePtr service_node, int version, str* rl_uri, unsigned int hash_code) { str* rlmi_body= NULL; str* multipart_body= NULL; db_key_t query_cols[2], update_cols[2], result_cols[7]; db_val_t query_vals[2], update_vals[2]; db_res_t *result= NULL; int n_result_cols= 0, i; str bstr= {0, 0}; str* cid_array= NULL; str rlsubs_did= {0, 0}; LM_DBG("start\n"); if(CONSTR_RLSUBS_DID(subs, &rlsubs_did) < 0) { LM_ERR("Failed to create did\n"); return -1; } /* query in alfabetical order */ query_cols[0]= &str_rlsubs_did_col; query_vals[0].type = DB_STR; query_vals[0].nul = 0; query_vals[0].val.str_val= rlsubs_did; result_cols[resource_uri_col= n_result_cols++]= &str_resource_uri_col; result_cols[ctype_col= n_result_cols++]= &str_content_type_col; result_cols[pres_state_col= n_result_cols++]= &str_presence_state_col; result_cols[auth_state_col= n_result_cols++]= &str_auth_state_col; result_cols[reason_col= n_result_cols++]= &str_reason_col; if (rls_dbf.use_table(rls_db, &rlpres_table) < 0) { LM_ERR("in use_table\n"); goto error; } if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols, 1, n_result_cols, &str_resource_uri_col, &result )< 0) { LM_ERR("in sql query\n"); goto error; } if(result== NULL) goto error; rlmi_body= constr_rlmi_doc(result, rl_uri, version, service_node, &cid_array, subs->from_user, subs->from_domain); if(rlmi_body== NULL) { LM_ERR("while constructing rlmi doc\n"); goto error; } bstr.s= generate_string((int)time(NULL), BOUNDARY_STRING_LEN); if(bstr.s == NULL) { LM_ERR("failed to generate random string\n"); goto error; } bstr.len = BOUNDARY_STRING_LEN; if(result->n> 0) { multipart_body= constr_multipart_body(result, cid_array, bstr); if(multipart_body== NULL) { LM_ERR("while constructing multipart body\n"); goto error; } for(i = 0; in; i++) { if(cid_array[i].s) pkg_free(cid_array[i].s); } } pkg_free(cid_array); cid_array= NULL; rls_dbf.free_result(rls_db, result); result= NULL; if(agg_body_sendn_update(rl_uri, bstr, rlmi_body, multipart_body, subs, hash_code)< 0) { LM_ERR("in function agg_body_sendn_update\n"); goto error; } /* update updated col in rlpres_table*/ update_cols[0]= &str_updated_col; update_vals[0].type = DB_INT; update_vals[0].nul = 0; update_vals[0].val.int_val= NO_UPDATE_TYPE; if (rls_dbf.use_table(rls_db, &rlpres_table) < 0) { LM_ERR("in use_table\n"); goto error; } if(rls_dbf.update(rls_db, query_cols, 0, query_vals, update_cols, update_vals, 1, 1)< 0) { LM_ERR("in sql update\n"); goto error; } pkg_free(bstr.s); xmlFree(rlmi_body->s); pkg_free(rlmi_body); if(multipart_body) { pkg_free(multipart_body->s); pkg_free(multipart_body); } pkg_free(rlsubs_did.s); return 0; error: if(bstr.s) pkg_free(bstr.s); if(rlmi_body) { if(rlmi_body->s) xmlFree(rlmi_body->s); pkg_free(rlmi_body); } if(multipart_body) { if(multipart_body->s) pkg_free(multipart_body->s); pkg_free(multipart_body); } if(cid_array) { for(i= 0; i< result->n ; i++) if(cid_array[i].s) pkg_free(cid_array[i].s); pkg_free(cid_array); } if(result) rls_dbf.free_result(rls_db, result); if(rlsubs_did.s) pkg_free(rlsubs_did.s); return -1; } int agg_body_sendn_update(str* rl_uri, str bstr, str* rlmi_body, str* multipart_body, subs_t* subs, unsigned int hash_code) { str cid; int len; str body= {0, 0}; int init_len; int body_len; cid.s= generate_cid(rl_uri->s, rl_uri->len); if(cid.s == NULL) { LM_ERR("failed to generate cid\n"); return -1; } cid.len = strlen(cid.s); len= 2*bstr.len+ 4+ 102+ cid.len+ 2+ rlmi_body->len+50+1; if(multipart_body) len+= multipart_body->len; init_len= len; body.s= (char*)pkg_malloc(len); if(body.s== NULL) { ERR_MEM(PKG_MEM_STR); } len= sprintf(body.s, "--%.*s\r\n", bstr.len, bstr.s); len+= sprintf(body.s+ len , "Content-Transfer-Encoding: binary\r\n"); len+= sprintf(body.s+ len , "Content-ID: <%.*s>\r\n", cid.len, cid.s); len+= sprintf(body.s+ len , "Content-Type: application/rlmi+xml;charset=\"UTF-8\"\r\n"); len+= sprintf(body.s+ len, "\r\n"); /*blank line*/ body_len = rlmi_body->len; memcpy(body.s+ len, rlmi_body->s, body_len); len+= body_len; len+= sprintf(body.s+ len, "\r\n"); /*blank line*/ if(multipart_body) { memcpy(body.s+ len, multipart_body->s, multipart_body->len); len+= multipart_body->len; } len+= sprintf(body.s+ len, "--%.*s--\r\n", bstr.len, bstr.s); if(init_len< len) { LM_ERR("buffer size overflow init_size= %d\tlen= %d\n",init_len,len); goto error; } body.s[len]= '\0'; body.len= len; /* send Notify */ if(rls_send_notify(subs, &body, &cid, &bstr)< 0) { LM_ERR("when sending Notify\n"); goto error; } pkg_free(body.s); body.s= NULL; if(subs->expires!= 0 && subs->status != TERMINATED_STATUS) { if(pres_update_shtable(rls_table, hash_code,subs, LOCAL_TYPE)< 0) { LM_ERR("updating in hash table\n"); goto error; } } pkg_free(cid.s); return 0; error: if(cid.s) pkg_free(cid.s); if(body.s) pkg_free(body.s); return -1; } int add_resource_instance(char* uri, xmlNodePtr resource_node, db_res_t* result, str* cid_array) { xmlNodePtr instance_node= NULL; db_row_t *row; db_val_t *row_vals; int i, cmp_code; char* auth_state= NULL; str cid; int auth_state_flag; for(i= 0; i< result->n; i++) { row = &result->rows[i]; row_vals = ROW_VALUES(row); cmp_code= strncmp(row_vals[resource_uri_col].val.string_val, uri, strlen(uri)); if(cmp_code> 0) break; if(cmp_code== 0) { instance_node= xmlNewChild(resource_node, NULL, BAD_CAST "instance", NULL); if(instance_node== NULL) { LM_ERR("while adding instance child\n"); goto error; } xmlNewProp(instance_node, BAD_CAST "id", BAD_CAST global_instance_id); auth_state_flag= row_vals[auth_state_col].val.int_val; auth_state= get_auth_string(auth_state_flag ); if(auth_state== NULL) { LM_ERR("bad authorization status flag\n"); goto error; } xmlNewProp(instance_node, BAD_CAST "state", BAD_CAST auth_state); if(auth_state_flag & ACTIVE_STATE) { cid.s= generate_cid(uri, strlen(uri)); if(cid.s == NULL) { LM_ERR("failed to generate cid\n"); goto error; } cid.len= strlen(cid.s); cid_array[i]= cid; xmlNewProp(instance_node, BAD_CAST "cid", BAD_CAST cid.s); } else if(auth_state_flag & TERMINATED_STATE) { xmlNewProp(instance_node, BAD_CAST "reason", BAD_CAST row_vals[reason_col].val.string_val); } } } /* if record not found should not add a instance node */ return 0; error: return -1; } int add_resource(char* uri, void* param) { str* cid_array= ((res_param_t*)param)->cid_array; xmlNodePtr list_node= ((res_param_t*)param)->list_node; xmlNodePtr resource_node= NULL; db_res_t *result= ((res_param_t*)param)->db_result; LM_DBG("uri= %s\n", uri); resource_node= xmlNewChild(list_node, NULL, BAD_CAST "resource", NULL); if(resource_node== NULL) { LM_ERR("while adding new rsource_node\n"); goto error; } xmlNewProp(resource_node, BAD_CAST "uri", BAD_CAST uri); if(add_resource_instance(uri, resource_node, result, cid_array)< 0) { LM_ERR("while adding resource instance node\n"); goto error; } return 0; error: return -1; } str* constr_rlmi_doc(db_res_t *result, str* rl_uri, int version, xmlNodePtr service_node, str** rlmi_cid_array, str username, str domain) { xmlDocPtr doc= NULL; xmlNodePtr list_node= NULL; str* rlmi_cont= NULL; int len; char* uri; res_param_t param; str* cid_array= NULL; int n= result->n; LM_DBG("start\n"); cid_array= (str*)pkg_malloc(n* sizeof(str)); if(cid_array== NULL) { ERR_MEM(PKG_MEM_STR); } memset(cid_array, 0, n* sizeof(str)); doc= xmlNewDoc(BAD_CAST "1.0"); if(doc== NULL) { LM_ERR("while constructing new xml doc\n"); goto error; } list_node= xmlNewNode(NULL, BAD_CAST "list"); if(list_node== NULL) { LM_ERR("while creating new xml node\n"); goto error; } uri= (char*)pkg_malloc(rl_uri->len+ 1); if(uri== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(uri, rl_uri->s, rl_uri->len); uri[rl_uri->len]= '\0'; xmlNewProp(list_node, BAD_CAST "uri", BAD_CAST uri); pkg_free(uri); xmlNewProp(list_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:rlmi"); xmlNewProp(list_node, BAD_CAST "version", BAD_CAST int2str(version-1, &len)); xmlNewProp(list_node, BAD_CAST "fullState", BAD_CAST "true"); xmlDocSetRootElement(doc, list_node); /* go through the list -- and add the appropriate 'resource' nodes*/ param.list_node= list_node; param.db_result= result; param.cid_array= cid_array; if(process_list_and_exec(service_node, username, domain, add_resource,(void*)(¶m), 0) < 0) { LM_ERR("in process_list_and_exec function\n"); goto error; } rlmi_cont= (str*)pkg_malloc(sizeof(str)); if(rlmi_cont== NULL) { ERR_MEM(PKG_MEM_STR); } xmlDocDumpMemory(doc,(xmlChar**)(void*)&rlmi_cont->s, &rlmi_cont->len); *rlmi_cid_array= cid_array; xmlFreeDoc(doc); return rlmi_cont; error: if(doc) xmlFreeDoc(doc); return NULL; } str* constr_multipart_body(db_res_t* result, str* cid_array, str bstr) { char* buf= NULL; int size= BUF_REALLOC_SIZE; int i, buf_len= 0; db_row_t *row; db_val_t *row_vals; str cid={0, 0}; str body= {0, 0}; str* multi_body= NULL; str ctype; LM_DBG("start\n"); buf= pkg_malloc(size); if(buf== NULL) { ERR_MEM(PKG_MEM_STR); } for(i= 0; i< result->n; i++) { row = &result->rows[i]; row_vals = ROW_VALUES(row); if(row_vals[auth_state_col].val.int_val!= ACTIVE_STATE) continue; ctype.s = (char*)row_vals[ctype_col].val.string_val; if(ctype.s == NULL) { LM_ERR("empty content type column\n"); goto error; } ctype.len = strlen(ctype.s); body.s= (char*)row_vals[pres_state_col].val.string_val; body.len= strlen(body.s); trim(&body); cid= cid_array[i]; if(cid.s== NULL) { LM_ERR("No cid found in array for uri= %s\n", row_vals[resource_uri_col].val.string_val); goto error; } if (append_multipart_body(&buf, &buf_len, &size, &bstr, &cid, &ctype, &body) != 0) goto error; } buf[buf_len]= '\0'; multi_body= (str*)pkg_malloc(sizeof(str)); if(multi_body== NULL) { ERR_MEM(PKG_MEM_STR); } multi_body->s= buf; multi_body->len= buf_len; return multi_body; error: if(buf) pkg_free(buf); return NULL; } int rls_notify_extra_hdr(subs_t* subs, str* start_cid, str* bstr, str *hdr) { int len; int lexpire_len; char* lexpire_s; char* p; lexpire_s = int2str(subs->expires, &lexpire_len); len = 7 /*Event: */ + subs->event->name.len +4 /*;id=*/+ subs->event_id.len+ CRLF_LEN + 10 /*Contact: <*/ + subs->local_contact.len + 1/*>*/ + ((subs->sockinfo && subs->sockinfo->proto!=PROTO_UDP)? 15/*";transport=xxxx"*/:0) + CRLF_LEN +/*Subscription-State:*/ 20 + ((subs->expires>0)?(15+lexpire_len):25) + CRLF_LEN + /*Require: */ 18 + CRLF_LEN + ((start_cid && bstr)?(/*Content-Type*/59 + /*start*/12 + start_cid->len + /*boundary*/12 + bstr->len + CRLF_LEN):0); hdr->s = (char*)pkg_malloc(len); if(hdr->s== NULL) { LM_ERR("while allocating memory\n"); return -1; } p = hdr->s; memcpy(p ,"Event: ", 7); p+= 7; memcpy(p, subs->event->name.s, subs->event->name.len); p+= subs->event->name.len; if(subs->event_id.len && subs->event_id.s) { memcpy(p, ";id=", 4); p += 4; memcpy(p, subs->event_id.s, subs->event_id.len); p += subs->event_id.len; } memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; memcpy(p ,"Contact: <", 10); p += 10; memcpy(p, subs->local_contact.s, subs->local_contact.len); p += subs->local_contact.len; if (subs->sockinfo && subs->sockinfo->proto!=PROTO_UDP) { memcpy(p,";transport=",11); p += 11; p = proto2str(subs->sockinfo->proto, p); if (p == NULL) { LM_ERR("invalid proto\n"); pkg_free(hdr->s); return -1; } } *(p++) = '>'; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; if(subs->expires> 0 ) { memcpy(p, "Subscription-State: active;expires=", 35); p += 35; memcpy(p, lexpire_s, lexpire_len); p+= lexpire_len; } else { memcpy(p, "Subscription-State: terminated;reason=timeout", 45); p += 45; } memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; memcpy(p, "Require: eventlist", 18); p += 18; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; if(start_cid && bstr) { memcpy(p,"Content-Type: multipart/related;type=\"application/rlmi+xml\"", 59 ); p += 59; memcpy(p, ";start=\"<", 9); p += 9; memcpy(p, start_cid->s, start_cid->len); p += start_cid->len; memcpy(p, ">\";boundary=\"", 13); p += 13; memcpy(p, bstr->s, bstr->len); p += bstr->len; *(p++) = '"'; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; } hdr->len = p - hdr->s; return 0; } void rls_free_td(dlg_t* td) { pkg_free(td->loc_uri.s); pkg_free(td->rem_uri.s); pkg_free(td); } int rls_send_notify(subs_t* subs, str* body, str* start_cid, str* bstr) { dlg_t* td= NULL; str met= {"NOTIFY", 6}; str str_hdr = {0, 0}; dialog_id_t* cb_param= NULL; int size; int rt; LM_DBG("start\n"); td= rls_notify_dlg(subs); if(td ==NULL) { LM_ERR("while building dlg_t structure\n"); goto error; } LM_DBG("constructed dlg_t struct\n"); size= sizeof(dialog_id_t)+(subs->to_tag.len+ subs->callid.len+ subs->from_tag.len) *sizeof(char); cb_param = (dialog_id_t*)shm_malloc(size); if(cb_param== NULL) { ERR_MEM(SHARE_MEM); } size= sizeof(dialog_id_t); cb_param->callid.s= (char*)cb_param + size; memcpy(cb_param->callid.s, subs->callid.s, subs->callid.len); cb_param->callid.len= subs->callid.len; size+= subs->callid.len; cb_param->to_tag.s= (char*)cb_param + size; memcpy(cb_param->to_tag.s, subs->to_tag.s, subs->to_tag.len); cb_param->to_tag.len= subs->to_tag.len; size+= subs->to_tag.len; cb_param->from_tag.s= (char*)cb_param + size; memcpy(cb_param->from_tag.s, subs->from_tag.s, subs->from_tag.len); cb_param->from_tag.len= subs->from_tag.len; LM_DBG("constructed cb_param\n"); if(rls_notify_extra_hdr(subs, start_cid, bstr, &str_hdr) < 0) { LM_ERR("while building extra headers\n"); goto error; } LM_DBG("str_hdr= %.*s\n", str_hdr.len, str_hdr.s); /* don't open new TCP connections if connection is down */ tcp_no_new_conn = 1; rt = tmb.t_request_within (&met, &str_hdr, body, td, rls_notify_callback, (void*)cb_param, NULL); tcp_no_new_conn = 0; if(rt < 0) { LM_ERR("in function tmb.t_request_within\n"); goto error; } pkg_free(str_hdr.s); rls_free_td(td); return 0; error: if(td) rls_free_td(td); if(cb_param) shm_free(cb_param); if(str_hdr.s) pkg_free(str_hdr.s); return -1; } dlg_t* rls_notify_dlg(subs_t* subs) { dlg_t* td=NULL; td= (dlg_t*)pkg_malloc(sizeof(dlg_t)); if(td== NULL) { ERR_MEM(PKG_MEM_STR); } memset(td, 0, sizeof(dlg_t)); td->loc_seq.value = subs->local_cseq; td->loc_seq.is_set = 1; td->id.call_id = subs->callid; td->id.rem_tag = subs->from_tag; td->id.loc_tag =subs->to_tag; if(uandd_to_uri(subs->to_user, subs->to_domain, &td->loc_uri)< 0) { LM_ERR("while constructing uri from user and domain\n"); goto error; } if(uandd_to_uri(subs->from_user, subs->from_domain, &td->rem_uri)< 0) { LM_ERR("while constructing uri from user and domain\n"); goto error; } if(subs->contact.len ==0 || subs->contact.s == NULL ) { LM_DBG("BAD BAD contact NULL\n"); td->rem_target = td->rem_uri; } else td->rem_target = subs->contact; if(subs->record_route.s && subs->record_route.len) { if(parse_rr_body(subs->record_route.s, subs->record_route.len, &td->route_set)< 0) { LM_ERR("in function parse_rr_body\n"); goto error; } } td->state= DLG_CONFIRMED ; td->send_sock = subs->sockinfo; return td; error: if(td) { if(td->loc_uri.s) pkg_free(td->loc_uri.s); if(td->rem_uri.s) pkg_free(td->rem_uri.s); pkg_free(td); } return NULL; } void rls_notify_callback( struct cell *t, int type, struct tmcb_params *ps) { if(ps->param==NULL || *ps->param==NULL || ((dialog_id_t*)(*ps->param)) == NULL) { LM_DBG("message id not received\n"); return; } LM_DBG("completed with status %d [to_tag:" "%.*s]\n",ps->code, ((dialog_id_t*)(*ps->param))->to_tag.len, ((dialog_id_t*)(*ps->param))->to_tag.s); if(ps->code >= 300) { /* delete from database table */ db_key_t db_keys[2]; db_val_t db_vals[2]; unsigned int hash_code; subs_t subs; memset(&subs, 0, sizeof(subs_t)); subs.to_tag= ((dialog_id_t*)(*ps->param))->to_tag; subs.from_tag= ((dialog_id_t*)(*ps->param))->from_tag; subs.callid= ((dialog_id_t*)(*ps->param))->callid; if (rls_dbf.use_table(rls_db, &rlsubs_table) < 0) { LM_ERR("in use_table\n"); goto done; } db_keys[0] =&str_to_tag_col; db_vals[0].type = DB_STR; db_vals[0].nul = 0; db_vals[0].val.str_val = subs.to_tag; db_keys[1] =&str_callid_col; db_vals[1].type = DB_STR; db_vals[1].nul = 0; db_vals[1].val.str_val = subs.callid; if (rls_dbf.delete(rls_db, db_keys, 0, db_vals, 2) < 0) LM_ERR("cleaning expired messages\n"); /* delete from cache table */ hash_code= core_hash(&subs.callid, &subs.to_tag , hash_size); if(pres_delete_shtable(rls_table,hash_code, subs.to_tag)< 0) { LM_ERR("record not found in hash table\n"); } } done: if(*ps->param !=NULL ) shm_free(*ps->param); return ; } int process_list_and_exec(xmlNodePtr list_node, str username, str domain, list_func_t function, void* param, int* cont_no) { xmlNodePtr node; int res = 0; str uri; str *normalized_uri; xcap_uri_t xcap_uri; LM_DBG("start\n"); for(node= list_node->children; node; node= node->next) { if(xmlStrcasecmp(node->name,(unsigned char*)"resource-list")==0) { xmlNodePtr rl_node = NULL; xmlDocPtr rl_doc = NULL; uri.s = XMLNodeGetNodeContentByName(node, "resource-list", NULL); if (uri.s == NULL) { LM_ERR("when extracting URI from node\n"); return -1; } uri.len = strlen(uri.s); if(xcapParseUri(&uri, &xcap_uri) == 0) { /* TODO: Check if the xcap-root is 'local' */ if (rls_integrated_xcap_server) { /* TODO: validate AUID and XUI? */ LM_DBG("fetching local \n"); if (rls_get_resource_list(&xcap_uri.filename, &xcap_uri.selector, &username, &domain, &rl_node, &rl_doc) == 0) { LM_DBG("calling myself for rl_node\n"); res = process_list_and_exec(rl_node, username, domain, function, param, cont_no); xmlFree(uri.s); xmlFreeDoc(rl_doc); } else { LM_ERR(" not found\n"); xmlFree(uri.s); return -1; } } else { LM_ERR(" is not local - unsupported at this time\n"); xmlFree(uri.s); return -1; } } else { LM_ERR("unable to parse URI for \n"); xmlFree(uri.s); return -1; } } else if(xmlStrcasecmp(node->name,(unsigned char*)"entry")== 0) { uri.s = XMLNodeGetAttrContentByName(node, "uri"); if(uri.s == NULL) { LM_ERR("when extracting entry uri attribute\n"); return -1; } uri.len = strlen(uri.s); LM_DBG("uri= %.*s\n", uri.len, uri.s); normalized_uri = normalizeSipUri(&uri); if (normalized_uri->s == NULL || normalized_uri->len == 0) { LM_ERR("failed to normalize entry URI\n"); xmlFree(uri.s); return -1; } xmlFree(uri.s); if(cont_no) *cont_no = *cont_no+1; if(function(normalized_uri->s, param)< 0) { LM_ERR("in function given as a parameter\n"); return -1; } } else if(xmlStrcasecmp(node->name,(unsigned char*)"list")== 0) res = process_list_and_exec(node, username, domain, function, param, cont_no); } return res; } char* generate_string(int seed, int length) { char* rstr; int r,i; rstr = (char*) pkg_malloc(length + 1); if(rstr == NULL) { LM_ERR("no more memory\n"); return NULL; } srand(seed); for(i=0; i'Z' && r< 'a') r= '0'+ (r- 'Z'); rstr[i] = r; } rstr[length]= '\0'; return rstr; } char* generate_cid(char* uri, int uri_len) { char* cid; int len; cid = (char*) pkg_malloc(uri_len + 30); if(cid == NULL) { LM_ERR("no more memory\n"); return NULL; } len= sprintf(cid, "%d.%.*s.%d", (int)time(NULL), uri_len, uri, rand()); cid[len]= '\0'; return cid; } char* get_auth_string(int flag) { switch(flag) { case ACTIVE_STATE: return "active"; case PENDING_STATE: return "pending"; case TERMINATED_STATE: return "terminated"; } return NULL; } #define MAX_PATH_LEN 127 int rls_get_resource_list(str *filename, str *selector, str *username, str *domain, xmlNodePtr *rl_node, xmlDocPtr *xmldoc) { static char path_buf[MAX_PATH_LEN+1]; int checked = 0; str path; str *doc = NULL; str *etag = NULL; xmlXPathContextPtr xpathCtx = NULL; xmlXPathObjectPtr xpathObj = NULL; if (filename==NULL || username==NULL || domain==NULL) { LM_ERR("invalid parameters\n"); return -1; } if (xcapDbGetDoc(username, domain, RESOURCE_LISTS, filename, NULL, &doc, &etag) < 0) { LM_ERR("while getting resource-lists document from DB\n"); return -1; } if (doc == NULL) { LM_DBG("No rl document found\n"); return -1; } LM_DBG("rl document:\n%.*s\n", doc->len, doc->s); path.s = path_buf; path.len = 0; if (selector->s) { while (checked < selector->len && path.len <= MAX_PATH_LEN) { if (selector->s[checked] == '/') { memcpy(path.s+path.len, "/xmlns:", 7); path.len += 7; } else { path.s[path.len++] = selector->s[checked]; } checked++; } path.s[path.len] = '\0'; LM_DBG("path: %.*s", path.len, path.s); } *xmldoc = xmlParseMemory(doc->s, doc->len); if (*xmldoc == NULL) { LM_ERR("while parsing XML memory\n"); goto error; } if(path.len == 0) { /* No path specified - use all resource-lists. */ *rl_node = XMLDocGetNodeByName(*xmldoc,"resource-lists", NULL); if (*rl_node == NULL) { LM_ERR("no resource-lists node in XML document\n"); goto error; } } else { /* TODO: move this to xcap module? */ xpathCtx = xmlXPathNewContext(*xmldoc); if (xpathCtx == NULL) { LM_ERR("unable to create new XPath context"); goto error; } if (xmlXPathRegisterNs(xpathCtx, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:resource-lists") != 0) { LM_ERR("unable to register xmlns\n"); goto error; } xpathObj = xmlXPathEvalExpression(BAD_CAST path.s, xpathCtx); if (xpathObj == NULL) { LM_ERR("unable to evaluate path\n"); goto error; } if (xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr <= 0) { LM_ERR("no nodes found\n"); goto error; } if (xpathObj->nodesetval->nodeTab[0] != NULL && xpathObj->nodesetval->nodeTab[0]->type != XML_ELEMENT_NODE) { LM_ERR("no nodes of the correct type found\n"); goto error; } *rl_node = xpathObj->nodesetval->nodeTab[0]; xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); } pkg_free(doc->s); pkg_free(doc); pkg_free(etag->s); pkg_free(etag); return 0; error: if (doc != NULL) { if (doc->s != NULL) pkg_free(doc->s); pkg_free(doc); } if (etag != NULL) { if (etag->s != NULL) pkg_free(etag->s); pkg_free(etag); } if (xpathObj) xmlXPathFreeObject(xpathObj); if (xpathCtx) xmlXPathFreeContext(xpathCtx); if (*xmldoc) xmlFreeDoc(*xmldoc); return -1; } opensips-2.2.2/modules/rls/notify.h000066400000000000000000000054351300170765700173230ustar00rootroot00000000000000/* * rls module - resource list server * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-11 initial version (Anca Vamanu) * */ #ifndef _RLS_NOTIFY_H #define _RLS_NOTIFY_H #include #include "../../str.h" #include "../presence/subscribe.h" #define BOUNDARY_STRING_LEN 24 #define BUF_REALLOC_SIZE 2048 #define MAX_FORWARD 70 static inline int append_multipart_body(char **buf, int *buf_len, int *size, str *bstr, str *cid, str *ctype, str *body) { int add_body, len; add_body = !!body->len; len = 4+bstr->len + 35 + 2; if (add_body) len += 16+cid->len + 18+ctype->len +2+body->len; while(*buf_len + len > *size) { *size += BUF_REALLOC_SIZE; *buf = (char*)pkg_realloc(*buf, *size); if(*buf == NULL) return -1; } *buf_len += sprintf(*buf + *buf_len, "--%.*s\r\n", bstr->len, bstr->s); *buf_len += sprintf(*buf + *buf_len, "Content-Transfer-Encoding: binary\r\n"); if (add_body) { *buf_len += sprintf(*buf + *buf_len, "Content-ID: <%.*s>\r\n",cid->len, cid->s); *buf_len += sprintf(*buf + *buf_len, "Content-Type: %.*s\r\n\r\n",ctype->len, ctype->s); *buf_len += sprintf(*buf + *buf_len,"%.*s\r\n", body->len, body->s); } *buf_len += sprintf(*buf + *buf_len,"\r\n"); return 0; } int send_full_notify(subs_t* subs, xmlNodePtr rl_node, int version, str* rl_uri, unsigned int hash_code); typedef int (*list_func_t)(char* uri, void* param); int process_list_and_exec(xmlNodePtr list, str username, str domain, list_func_t f, void* p, int* c); char* generate_string(int seed, int length); char* generate_cid(char* uri, int uri_len); char* get_auth_string(int flag); int agg_body_sendn_update(str* rl_uri, str boundary_string, str* rlmi_body, str* multipart_body, subs_t* subs, unsigned int hash_code); int rls_send_notify(subs_t* subs,str* body, str* start_cid, str* boundary_string); extern char* global_instance_id; #endif opensips-2.2.2/modules/rls/resource_notify.c000066400000000000000000000536641300170765700212340ustar00rootroot00000000000000/* * rls module - resource list server * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-11 initial version (Anca Vamanu) */ #include #include #include #include "../../trim.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../pua/hash.h" #include "rls.h" #include "notify.h" #include "resource_notify.h" /* how to relate resource oriented dialogs to list_uri */ /* sol1: use the same callid in Subscribe requests * sol2: include an extra header * sol3: put the list_uri as the id of the record stored in * pua and write a function to return that id * winner: sol3 * */ static str su_200_rpl = str_init("OK"); int parse_subs_state(str auth_state, str* reason, int* expires) { static str unknown = str_init("unknown"); str str_exp; char* smc= NULL, *ptr; int len, flag= -1, unknown_reason = 0; if( strncasecmp(auth_state.s, "active", 6)== 0) flag= ACTIVE_STATE; if( strncasecmp(auth_state.s, "pending", 7)== 0) flag= PENDING_STATE; if( strncasecmp(auth_state.s, "terminated", 10)== 0) { *expires = 0; smc= strchr(auth_state.s, ';'); if(smc== NULL) { LM_DBG("terminated state and no reason found\n"); unknown_reason = 1; goto set_reason; } if(strncasecmp(smc+1, "reason=", 7)) { LM_DBG("terminated state and no reason found\n"); unknown_reason = 1; goto set_reason; } len = auth_state.len- 10- 1- 7; if (len == 0) unknown_reason = 1; /* reason attribute is optional as per RFC 3265, but is required * when building the RLMI doc, so set it to 'unknown' */ set_reason: if(unknown_reason) { len = unknown.len; reason->s= (char*)pkg_malloc(len* sizeof(char)); if(reason->s== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(reason->s, unknown.s, len); reason->len= len; } else { len= auth_state.len- 10- 1- 7; reason->s= (char*)pkg_malloc(len* sizeof(char)); if(reason->s== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(reason->s, smc+ 8, len); reason->len= len; } return TERMINATED_STATE; } if(flag > 0) { *expires = -1; ptr = auth_state.s; while ((smc = memchr(ptr, ';', auth_state.len-(ptr-auth_state.s))) && smc+1-auth_state.s < auth_state.len) { smc += 1; if(strncasecmp(smc, "expires=", 8) == 0) { str_exp.s = smc + 8; str_exp.len = auth_state.s + auth_state.len - smc - 8; if(str2int(&str_exp, (unsigned int*)expires) < 0) { LM_ERR("while extracting expires value\n"); return -1; } break; } ptr = smc; } return flag; } return -1; error: if(reason->s) pkg_free(reason->s); return -1; } int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2) { struct to_body *pto, *pfrom= NULL; str body= {0, 0}; ua_pres_t dialog; str* res_id= NULL; db_key_t query_cols[9], result_cols[1]; db_val_t query_vals[9]; db_res_t* result= NULL; int n_query_cols= 0; str auth_state= {0, 0}; str reason = {0, 0}; int auth_flag; struct hdr_field* hdr= NULL; int n, expires = -1; str ctype= {0, 0}; int err_ret = -1; LM_DBG("start\n"); /* extract the dialog information and check if an existing dialog*/ if( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("parsing headers\n"); return -1; } if((!msg->event ) ||(msg->event->body.len<=0)) { LM_ERR("Missing event header field value\n"); return -1; } if( msg->to==NULL || msg->to->body.s==NULL) { LM_ERR("cannot parse TO header\n"); return -1; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); return -1; } memset(&dialog, 0, sizeof(ua_pres_t)); dialog.watcher_uri= &pto->uri; if (pto->tag_value.s==NULL || pto->tag_value.len==0 ) { LM_ERR("to tag value not parsed\n"); goto error; } dialog.from_tag= pto->tag_value; if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); goto error; } dialog.call_id = msg->callid->body; if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); goto error; } if (msg->from->parsed == NULL) { LM_DBG("'From' header not parsed\n"); /* parsing from header */ if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); goto error; } } pfrom = (struct to_body*)msg->from->parsed; dialog.pres_uri= &pfrom->uri; if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); goto error; } dialog.to_tag= pfrom->tag_value; dialog.flag|= RLS_SUBSCRIBE; dialog.event= get_event_flag(&msg->event->body); if(dialog.event< 0) { LM_ERR("unrecognized event package\n"); goto error; } /* extract the subscription state */ hdr = get_header_by_static_name( msg, "Subscription-State"); if( hdr==NULL ) { LM_ERR("'Subscription-State' header not found\n"); goto error; } auth_state = hdr->body; /* extract state and reason */ auth_flag= parse_subs_state(auth_state, &reason, &expires); if(auth_flag< 0) { LM_ERR("while parsing 'Subscription-State' header\n"); goto error; } if(pua_get_record_id(&dialog, &res_id)< 0) // verify if within a stored dialog { LM_ERR("error occurred while trying to get dialog record id\n"); goto error; } if(res_id== 0) { LM_DBG("no dialog match found in hash table\n"); /* The PUA module removes the subscriptions shortly after a 200 OK with Expires: 0 is received, * so if we can't find the subscription for this NOTIFY and it's in the terminated * state just reply a 200 OK, nobody should get hurt. */ if (auth_flag == TERMINATED_STATE) goto done; err_ret = 2; goto error; } if(msg->content_type== NULL || msg->content_type->body.s== NULL) { LM_DBG("cannot find content type header\n"); } else ctype= msg->content_type->body; LM_DBG("NOTIFY for [user]= %.*s\n",dialog.pres_uri->len, dialog.pres_uri->s); /*constructing the xml body*/ if(get_content_length(msg)) { if(ctype.s== 0) { LM_ERR("content length != 0 and no content type header found\n"); goto error; } if ( get_body(msg,&body)!=0 || body.len==0) { LM_ERR("cannot extract body from msg\n"); goto error; } LM_DBG("[body]= %.*s\n", body.len, body.s); } /* update in rlpres_table where rlsusb_did= res_id and resource_uri= from_uri*/ query_cols[n_query_cols]= &str_rlsubs_did_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= *res_id; n_query_cols++; query_cols[n_query_cols]= &str_resource_uri_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= *dialog.pres_uri; n_query_cols++; query_cols[n_query_cols]= &str_updated_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val= UPDATED_TYPE; n_query_cols++; query_cols[n_query_cols]= &str_auth_state_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val= auth_flag; n_query_cols++; query_cols[n_query_cols]= &str_reason_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val.s = reason.s; query_vals[n_query_cols].val.str_val.len = reason.len; n_query_cols++; query_cols[n_query_cols]= &str_content_type_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= ctype; n_query_cols++; query_cols[n_query_cols]= &str_presence_state_col; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val= body; n_query_cols++; if (expires > -1) { query_cols[n_query_cols]= &str_expires_col; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val = expires + (int)time(NULL); n_query_cols++; } if (rls_dbf.use_table(rls_db, &rlpres_table) < 0) { LM_ERR("in use_table\n"); goto error; } /* query-> if not present insert // else update */ result_cols[0]= &str_updated_col; if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols, 2, 1, 0, &result)< 0) { LM_ERR("in sql query\n"); if(result) rls_dbf.free_result(rls_db, result); goto error; } if(result== NULL) goto error; n= result->n; rls_dbf.free_result(rls_db, result); if(n<= 0) { if(rls_dbf.insert(rls_db, query_cols, query_vals, n_query_cols)< 0) { LM_ERR("in sql insert\n"); goto error; } } else { if(rls_dbf.update(rls_db, query_cols, 0, query_vals, query_cols+2, query_vals+2, 2, n_query_cols-2) < 0) { LM_ERR("in sql update\n"); goto error; } } done: if( rls_sigb.reply(msg, 200, &su_200_rpl, 0) < 0) { LM_ERR("failed to send SIP reply\n"); goto error; } if(reason.s) pkg_free(reason.s); if (res_id) { pkg_free(res_id->s); pkg_free(res_id); } return 1; error: if(reason.s) pkg_free(reason.s); if(res_id) { pkg_free(res_id->s); pkg_free(res_id); } return err_ret; } /* callid, from_tag, to_tag parameters must be allocated */ int parse_rlsubs_did(char* str_did, str* callid, str* from_tag, str* to_tag) { char* smc= NULL; smc= strstr(str_did, DID_SEP); if(smc== NULL) { LM_ERR("bad format for resource list Subscribe dialog" " indentifier[rlsubs did]= %s\n", str_did); return -1; } callid->s= str_did; callid->len= smc- str_did; from_tag->s= smc+ DID_SEP_LEN; smc= strstr(from_tag->s, DID_SEP); if(smc== NULL) { LM_ERR("bad format for resource list Subscribe dialog" " indentifier(rlsubs did)= %s\n", str_did); return -1; } from_tag->len= smc- from_tag->s; to_tag->s= smc+ DID_SEP_LEN; to_tag->len= strlen(str_did)- 2* DID_SEP_LEN- callid->len- from_tag->len; return 0; } void get_dialog_from_did(char* did, subs_t **dialog, unsigned int *hash_code) { str callid, to_tag, from_tag; subs_t* s; *dialog = NULL; /* search the subscription in rlsubs_table */ if( parse_rlsubs_did(did, &callid, &from_tag, &to_tag) < 0) { LM_ERR("bad format for resource list Subscribe dialog " "indentifier(rlsubs did)\n"); return; } *hash_code= core_hash(&callid, &to_tag, hash_size); lock_get(&rls_table[*hash_code].lock); s = pres_search_shtable(rls_table, callid, to_tag, from_tag, *hash_code); if(s == NULL) { LM_DBG("record not found in hash_table [rlsubs_did]= %s\n", did); LM_DBG("callid= %.*s\tfrom_tag= %.*s\tto_tag= %.*s\n", callid.len, callid.s,from_tag.len,from_tag.s, to_tag.len,to_tag.s); lock_release(&rls_table[*hash_code].lock); return; } /* save dialog info */ *dialog = pres_copy_subs(s, PKG_MEM_TYPE); if(*dialog == NULL) { LM_ERR("while copying subs_t structure\n"); lock_release(&rls_table[*hash_code].lock); return; } (*dialog)->expires -= (int)time(NULL); lock_release(&rls_table[*hash_code].lock); } int send_notify(xmlDocPtr * rlmi_doc, char * buf, int buf_len, const str bstr, subs_t * dialog, unsigned int hash_code) { int result = 0; str rlmi_cont = {0, 0}, multi_cont; xmlDocDumpFormatMemory(*rlmi_doc,(xmlChar**)(void*)&rlmi_cont.s, &rlmi_cont.len, 0); multi_cont.s= buf; multi_cont.len= buf_len; result = agg_body_sendn_update(&(dialog->pres_uri), bstr, &rlmi_cont, (buf_len==0)?NULL:&multi_cont, dialog, hash_code); xmlFree(rlmi_cont.s); xmlFreeDoc(*rlmi_doc); *rlmi_doc = NULL; return result; } void timer_send_notify(unsigned int ticks,void *param) { db_key_t query_cols[1], update_cols[1], result_cols[7]; db_val_t query_vals[1], update_vals[1]; int did_col, resource_uri_col, auth_state_col, reason_col, body_col, ctype_col; int n_result_cols= 0, i; db_res_t *result= NULL; char* prev_did= NULL, * curr_did= NULL; db_row_t *row; db_val_t *row_vals; char* resource_uri; str body; xmlDocPtr rlmi_doc= NULL; xmlNodePtr list_node= NULL, instance_node= NULL, resource_node; unsigned int hash_code= 0; int len; int size= BUF_REALLOC_SIZE, buf_len= 0; char* buf= NULL, *auth_state= NULL; int auth_state_flag; str bstr= {0, 0}; subs_t* dialog = NULL; char* rl_uri= NULL; str ctype, cid; query_cols[0]= &str_updated_col; query_vals[0].type = DB_INT; query_vals[0].nul = 0; query_vals[0].val.int_val= UPDATED_TYPE; result_cols[did_col= n_result_cols++]= &str_rlsubs_did_col; result_cols[resource_uri_col= n_result_cols++]= &str_resource_uri_col; result_cols[auth_state_col= n_result_cols++]= &str_auth_state_col; result_cols[ctype_col= n_result_cols++]= &str_content_type_col; result_cols[reason_col= n_result_cols++]= &str_reason_col; result_cols[body_col= n_result_cols++]= &str_presence_state_col; /* query in alfabetical order after rlsusbs_did * (resource list Subscribe dialog indentifier)*/ if (rls_dbf.use_table(rls_db, &rlpres_table) < 0) { LM_ERR("in use_table\n"); goto error; } if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols, 1, n_result_cols, &str_rlsubs_did_col, &result)< 0) { LM_ERR("in sql query\n"); goto error; } if(result== NULL || result->n<= 0) goto error; /* update the rlpres table */ update_cols[0]= &str_updated_col; update_vals[0].type = DB_INT; update_vals[0].nul = 0; update_vals[0].val.int_val= NO_UPDATE_TYPE; if (rls_dbf.use_table(rls_db, &rlpres_table) < 0) { LM_ERR("in use_table\n"); goto error; } if(rls_dbf.update(rls_db, query_cols, 0, query_vals, update_cols, update_vals, 1, 1)< 0) { LM_ERR("in sql update\n"); goto error; } /* generate the boundary string */ bstr.s= generate_string((int)time(NULL), BOUNDARY_STRING_LEN); if(bstr.s == NULL) { LM_ERR("failed to generate random string\n"); goto error; } bstr.len= strlen(bstr.s); /* for the multipart body , use here also an initial allocated * and reallocated on need buffer */ buf= pkg_malloc(size); if(buf== NULL) { ERR_MEM(PKG_MEM_STR); } LM_DBG("found %d records with updated state\n", result->n); for(i= 0; i< result->n; i++) { row = &result->rows[i]; row_vals = ROW_VALUES(row); curr_did= (char*)row_vals[did_col].val.string_val; resource_uri= (char*)row_vals[resource_uri_col].val.string_val; auth_state_flag= row_vals[auth_state_col].val.int_val; body.s= (char*)row_vals[body_col].val.string_val; body.len= strlen(body.s); trim(&body); ctype.s = (char*)row_vals[ctype_col].val.string_val; ctype.len = strlen(ctype.s); /* if all the info for one dialog have been collected -> send notify */ /* the 'dialog' variable must be filled with the dialog info */ /* 'buf' must contain the body */ if(prev_did != NULL && strcmp(prev_did, curr_did) != 0) { if (send_notify(&rlmi_doc, buf, buf_len, bstr, dialog, hash_code)) { LM_ERR("in send_notify\n"); goto error; } pkg_free(dialog); dialog = NULL; } /* for the new dialog -> search the dialog info and * fill the dialog structure and start a new rlmi document */ if(prev_did== NULL || strcmp(prev_did, curr_did) != 0) { /* Get a subscription from the did */ get_dialog_from_did(curr_did, &dialog, &hash_code); if(dialog == NULL) { prev_did = NULL; continue; } /* make new rlmi and multipart documents */ rlmi_doc= xmlNewDoc(BAD_CAST "1.0"); if(rlmi_doc== NULL) { LM_ERR("when creating new xml doc\n"); goto error; } list_node= xmlNewNode(NULL, BAD_CAST "list"); if(list_node== NULL) { LM_ERR("while creating new xml node\n"); goto error; } rl_uri= (char*)pkg_malloc((dialog->pres_uri.len+ 1)* sizeof(char)); if(rl_uri== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(rl_uri, dialog->pres_uri.s, dialog->pres_uri.len); rl_uri[dialog->pres_uri.len]= '\0'; xmlNewProp(list_node, BAD_CAST "uri", BAD_CAST rl_uri); xmlNewProp(list_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:rlmi"); xmlNewProp(list_node, BAD_CAST "version", BAD_CAST int2str(dialog->version-1, &len)); xmlNewProp(list_node, BAD_CAST "fullState", BAD_CAST "false"); /* xmlNewProp creates a copy, so we can free rl_uri now */ pkg_free(rl_uri); xmlDocSetRootElement(rlmi_doc, list_node); buf_len = 0; /* !!!! for now I will include the auth state without checking if * it has changed - > in future chech if it works */ } /* add a node in rlmi_doc and if any presence state registered add * it in the buffer */ resource_node= xmlNewChild(list_node,NULL,BAD_CAST "resource", NULL); if(resource_node== NULL) { LM_ERR("when adding resource child\n"); goto error; } xmlNewProp(resource_node, BAD_CAST "uri", BAD_CAST resource_uri); /* there might be more records with the same uri- more instances- * search and add them all */ while(1) { cid.s = NULL; cid.len = 0; instance_node= xmlNewChild(resource_node, NULL, BAD_CAST "instance", NULL); if(instance_node== NULL) { LM_ERR("while adding instance child\n"); goto error; } xmlNewProp(instance_node, BAD_CAST "id", BAD_CAST global_instance_id); auth_state= get_auth_string(auth_state_flag); if(auth_state== NULL) { LM_ERR("bad authorization status flag\n"); goto error; } xmlNewProp(instance_node, BAD_CAST "state", BAD_CAST auth_state); if(auth_state_flag & ACTIVE_STATE) { cid.s= generate_cid(resource_uri, strlen(resource_uri)); cid.len = strlen(cid.s); xmlNewProp(instance_node, BAD_CAST "cid", BAD_CAST cid.s); } else if(auth_state_flag & TERMINATED_STATE) { xmlNewProp(instance_node, BAD_CAST "reason", BAD_CAST row_vals[reason_col].val.string_val); } /* add in the multipart buffer */ if(cid.s) { if (append_multipart_body(&buf, &buf_len, &size, &bstr, &cid, &ctype, &body) != 0) { pkg_free(cid.s); cid.s = NULL; goto error; } pkg_free(cid.s); cid.s = NULL; } i++; if(i== result->n) { i--; break; } row = &result->rows[i]; row_vals = ROW_VALUES(row); if(strncmp(row_vals[resource_uri_col].val.string_val,resource_uri, strlen(resource_uri)) || strncmp(curr_did, row_vals[did_col].val.string_val, strlen(curr_did))) { i--; break; } resource_uri= (char*)row_vals[resource_uri_col].val.string_val; auth_state_flag= row_vals[auth_state_col].val.int_val; body.s= (char*)row_vals[body_col].val.string_val; body.len = strlen(body.s); trim(&body); } prev_did= curr_did; } if(rlmi_doc) { if (send_notify(&rlmi_doc, buf, buf_len, bstr, dialog, hash_code)) { LM_ERR("in send_notify\n"); goto error; } pkg_free(dialog); dialog = NULL; } error: if(result) rls_dbf.free_result(rls_db, result); if(bstr.s) pkg_free(bstr.s); if(buf) pkg_free(buf); if(dialog) pkg_free(dialog); } /* function to periodicaly clean the rls_presentity table */ void rls_presentity_clean(unsigned int ticks,void *param) { db_key_t query_cols[2]; db_op_t query_ops[2]; db_val_t query_vals[2]; query_cols[0]= &str_expires_col; query_ops[0]= OP_LT; query_vals[0].nul= 0; query_vals[0].type= DB_INT; query_vals[0].val.int_val = (int)time(NULL)-10; query_cols[1]= &str_updated_col; query_ops[1]= OP_EQ; query_vals[1].type = DB_INT; query_vals[1].nul = 0; query_vals[1].val.int_val= NO_UPDATE_TYPE; if (rls_dbf.use_table(rls_db, &rlpres_table) < 0) { LM_ERR("in use_table\n"); return ; } if(rls_dbf.delete(rls_db, query_cols, query_ops, query_vals, 2) < 0) { LM_ERR("in sql delete\n"); return ; } } opensips-2.2.2/modules/rls/resource_notify.h000066400000000000000000000022511300170765700212230ustar00rootroot00000000000000/* * rls module - resource list server * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-11 initial version (anca) */ #ifndef RLS_RES_NOT_H #define RLS_RES_NOT_H #include "../../parser/msg_parser.h" int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2); void timer_send_notify(unsigned int ticks,void *param); void rls_presentity_clean(unsigned int ticks,void *param); #endif opensips-2.2.2/modules/rls/rls.c000066400000000000000000000607041300170765700166060ustar00rootroot00000000000000/* * rls module - resource list server * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-11 initial version (Anca Vamanu) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../pt.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "../tm/tm_load.h" #include "../signaling/signaling.h" #include "../presence/bind_presence.h" #include "../presence/hash.h" #include "../pua/pua_bind.h" #include "../pua/pidf.h" #include "../xcap/api.h" #include "rls.h" #include "subscribe.h" #include "notify.h" #include "resource_notify.h" #define P_TABLE_VERSION 1 #define W_TABLE_VERSION 2 /** database connection */ db_con_t *rls_db = NULL; db_func_t rls_dbf; /** modules variables */ str server_address= {0, 0}; str presence_server= {0, 0}; int waitn_time = 50; str rlsubs_table= str_init("rls_watchers"); str rlpres_table= str_init("rls_presentity"); int hash_size= 512; shtable_t rls_table; int pid; contains_event_t pres_contains_event; search_event_t pres_search_event; get_event_list_t pres_get_ev_list; int clean_period= 100; char* xcap_root; unsigned int xcap_port= 8000; int rls_restore_db_subs(void); /* xcap API */ str db_url = {NULL, 0}; str rls_xcap_table = {NULL, 0}; int rls_integrated_xcap_server = 0; normalize_sip_uri_t normalizeSipUri; parse_xcap_uri_t xcapParseUri; get_xcap_doc_t xcapDbGetDoc; /** libxml api */ xmlDocGetNodeByName_t XMLDocGetNodeByName; xmlNodeGetNodeByName_t XMLNodeGetNodeByName; xmlNodeGetNodeContentByName_t XMLNodeGetNodeContentByName; xmlNodeGetAttrContentByName_t XMLNodeGetAttrContentByName; /* functions imported from presence to handle subscribe hash table */ new_shtable_t pres_new_shtable; insert_shtable_t pres_insert_shtable; search_shtable_t pres_search_shtable; update_shtable_t pres_update_shtable; delete_shtable_t pres_delete_shtable; destroy_shtable_t pres_destroy_shtable; mem_copy_subs_t pres_copy_subs; update_db_subs_t pres_update_db_subs; extract_sdialog_info_t pres_extract_sdialog_info; int rls_events= EVENT_PRESENCE; int to_presence_code= 10; int rls_max_expires= 7200; /* functions imported from xcap_client module */ xcapGetNewDoc_t xcap_GetNewDoc= 0; /* functions imported from pua module*/ send_subscribe_t pua_send_subscribe; get_record_id_t pua_get_record_id; get_subs_list_t pua_get_subs_list; /* TM bind */ struct tm_binds tmb; /* SIGNALING bind */ struct sig_binds rls_sigb; str str_rlsubs_did_col = str_init("rlsubs_did"); str str_resource_uri_col = str_init("resource_uri"); str str_updated_col = str_init("updated"); str str_auth_state_col = str_init("auth_state"); str str_reason_col = str_init("reason"); str str_content_type_col = str_init("content_type"); str str_presence_state_col = str_init("presence_state"); str str_expires_col = str_init("expires"); str str_presentity_uri_col = str_init("presentity_uri"); str str_event_col = str_init("event"); str str_event_id_col = str_init("event_id"); str str_to_user_col = str_init("to_user"); str str_to_domain_col = str_init("to_domain"); str str_watcher_username_col = str_init("watcher_username"); str str_watcher_domain_col = str_init("watcher_domain"); str str_callid_col = str_init("callid"); str str_to_tag_col = str_init("to_tag"); str str_from_tag_col = str_init("from_tag"); str str_local_cseq_col = str_init("local_cseq"); str str_remote_cseq_col = str_init("remote_cseq"); str str_record_route_col = str_init("record_route"); str str_socket_info_col = str_init("socket_info"); str str_contact_col = str_init("contact"); str str_local_contact_col = str_init("local_contact"); str str_version_col = str_init("version"); str str_status_col = str_init("status"); str str_username_col = str_init("username"); str str_domain_col = str_init("domain"); str str_doc_type_col = str_init("doc_type"); str str_etag_col = str_init("etag"); str str_doc_col = str_init("doc"); str str_doc_uri_col = str_init("doc_uri"); /** module functions */ static int mod_init(void); static int child_init(int); void destroy(void); int rlsubs_table_restore(); void rlsubs_table_update(unsigned int ticks,void *param); int add_rls_event(modparam_t type, void* val); int parse_xcap_root(void); static struct mi_root* mi_update_subscriptions(struct mi_root* cmd, void* param); static cmd_export_t cmds[]= { {"rls_handle_subscribe", (cmd_function)rls_handle_subscribe, 0, 0, 0, REQUEST_ROUTE}, {"rls_handle_notify", (cmd_function)rls_handle_notify, 0, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0 } }; static param_export_t params[]={ { "server_address", STR_PARAM, &server_address.s }, { "presence_server", STR_PARAM, &presence_server.s }, { "rlsubs_table", STR_PARAM, &rlsubs_table.s }, { "rlpres_table", STR_PARAM, &rlpres_table.s }, { "waitn_time", INT_PARAM, &waitn_time }, { "clean_period", INT_PARAM, &clean_period }, { "max_expires", INT_PARAM, &rls_max_expires }, { "hash_size", INT_PARAM, &hash_size }, { "to_presence_code", INT_PARAM, &to_presence_code }, { "xcap_root", STR_PARAM, &xcap_root }, /*address and port(default: 80):"http://192.168.2.132:8000/xcap-root"*/ { "rls_event", STR_PARAM|USE_FUNC_PARAM,(void*)add_rls_event}, { 0, 0, 0 } }; static mi_export_t mi_cmds[] = { { "rls_update_subscriptions", 0, mi_update_subscriptions, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_DEFAULT, "xcap", DEP_ABORT }, { MOD_TYPE_DEFAULT, "pua", DEP_ABORT }, { MOD_TYPE_DEFAULT, "presence", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "rls", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { bind_presence_t bind_presence; presence_api_t pres; bind_pua_t bind_pua; pua_api_t pua; bind_libxml_t bind_libxml; libxml_api_t libxml_api; bind_xcap_t bind_xcap; xcap_api_t xcap_api; bind_xcap_client_t bind_xcap_client; xcap_client_api_t xcap_client_api; LM_DBG("start\n"); if(!server_address.s) { LM_ERR("server_address parameter not set in configuration file\n"); return -1; } server_address.len= strlen(server_address.s); if(presence_server.s) presence_server.len= strlen(presence_server.s); /* load XCAP API */ bind_xcap = (bind_xcap_t)find_export("bind_xcap", 1, 0); if (!bind_xcap) { LM_ERR("Can't bind xcap\n"); return -1; } if (bind_xcap(&xcap_api) < 0) { LM_ERR("Can't bind xcap\n"); return -1; } rls_integrated_xcap_server = xcap_api.integrated_server; db_url = xcap_api.db_url; rls_xcap_table = xcap_api.xcap_table; normalizeSipUri = xcap_api.normalize_sip_uri; xcapParseUri = xcap_api.parse_xcap_uri; xcapDbGetDoc = xcap_api.get_xcap_doc; if(!rls_integrated_xcap_server) { if(xcap_root== NULL) { LM_ERR("xcap_root parameter not set\n"); return -1; } if(parse_xcap_root() < 0) { LM_ERR("wrong format for xcap_root parameter\n"); return -1; } } /* load SIGNALING API */ if(load_sig_api(&rls_sigb)< 0) { LM_ERR("can't load signaling functions\n"); return -1; } /* load all TM stuff */ if(load_tm_api(&tmb)==-1) { LM_ERR("can't load tm functions\n"); return -1; } bind_presence= (bind_presence_t)find_export("bind_presence", 1,0); if (!bind_presence) { LM_ERR("Can't bind presence\n"); return -1; } if (bind_presence(&pres) < 0) { LM_ERR("Can't bind presence\n"); return -1; } pres_contains_event = pres.contains_event; pres_search_event = pres.search_event; pres_get_ev_list = pres.get_event_list; pres_new_shtable = pres.new_shtable; pres_destroy_shtable= pres.destroy_shtable; pres_insert_shtable = pres.insert_shtable; pres_delete_shtable = pres.delete_shtable; pres_update_shtable = pres.update_shtable; pres_search_shtable = pres.search_shtable; pres_copy_subs = pres.mem_copy_subs; pres_update_db_subs = pres.update_db_subs; pres_extract_sdialog_info= pres.extract_sdialog_info; if(!pres_contains_event || !pres_get_ev_list || !pres_new_shtable || !pres_destroy_shtable || !pres_insert_shtable || !pres_delete_shtable || !pres_update_shtable || !pres_search_shtable || !pres_copy_subs || !pres_extract_sdialog_info) { LM_ERR("importing functions from presence module\n"); return -1; } rlsubs_table.len= strlen(rlsubs_table.s); rlpres_table.len= strlen(rlpres_table.s); rls_xcap_table.len= strlen(rls_xcap_table.s); /* binding to mysql module */ if (db_bind_mod(&db_url, &rls_dbf)) { LM_ERR("Database module not found\n"); return -1; } if (!DB_CAPABILITY(rls_dbf, DB_CAP_ALL)) { LM_ERR("Database module does not implement all functions" " needed by the module\n"); return -1; } rls_db = rls_dbf.init(&db_url); if (!rls_db) { LM_ERR("while connecting database\n"); return -1; } /* verify table version */ if((db_check_table_version(&rls_dbf, rls_db, &rlsubs_table, W_TABLE_VERSION) < 0) || (db_check_table_version(&rls_dbf, rls_db, &rlpres_table, P_TABLE_VERSION) < 0)) { LM_ERR("error during table version check.\n"); return -1; } if(hash_size<=1) hash_size= 512; else hash_size = 1< 65535) { LM_ERR("wrong xcap server port\n"); return -1; } } return 0; } /** * Initialize children */ static int child_init(int rank) { LM_DBG("child [%d] pid [%d]\n", rank, getpid()); if (rls_dbf.init==0) { LM_CRIT("database not bound\n"); return -1; } rls_db = rls_dbf.init(&db_url); if (!rls_db) { LM_ERR("child %d: Error while connecting database\n", rank); return -1; } else { if (rls_dbf.use_table(rls_db, &rlsubs_table) < 0) { LM_ERR("child %d: Error in use_table rlsubs_table\n", rank); return -1; } if (rls_dbf.use_table(rls_db, &rlpres_table) < 0) { LM_ERR("child %d: Error in use_table rlpres_table\n", rank); return -1; } LM_DBG("child %d: Database connection opened successfully\n", rank); } pid= my_pid(); return 0; } /* * destroy function */ void destroy(void) { LM_DBG("start\n"); if(rls_table) { if(rls_db) rlsubs_table_update(0, 0); pres_destroy_shtable(rls_table, hash_size); } if(rls_db && rls_dbf.close) rls_dbf.close(rls_db); } int handle_expired_record(subs_t* s) { int expires = s->expires; s->expires = 0; /* send Notify with state terminated*/ if( rls_send_notify(s, NULL, NULL, NULL)< 0) { LM_ERR("in function send_notify\n"); s->expires = expires; return -1; } s->expires = expires; return 0; } void rlsubs_table_update(unsigned int ticks,void *param) { int no_lock= 0; if(ticks== 0 && param == NULL) no_lock= 1; if(rls_dbf.use_table(rls_db, &rlsubs_table)< 0) { LM_ERR("sql use table failed\n"); return; } pres_update_db_subs(rls_db, rls_dbf, rls_table, hash_size, no_lock, handle_expired_record); } int rls_restore_db_subs(void) { db_key_t result_cols[22]; db_res_t *res= NULL; db_row_t *row = NULL; db_val_t *row_vals= NULL; int i; int n_result_cols= 0; int pres_uri_col, expires_col, from_user_col, from_domain_col,to_user_col; int callid_col,totag_col,fromtag_col,to_domain_col,sockinfo_col,reason_col; int event_col,contact_col,record_route_col, event_id_col, status_col; int remote_cseq_col, local_cseq_col, local_contact_col, version_col; subs_t s; str ev_sname; pres_ev_t* event= NULL; event_t parsed_event; unsigned int expires; unsigned int hash_code; str sockinfo_str; int port, proto; str host; result_cols[pres_uri_col=n_result_cols++] = &str_presentity_uri_col; result_cols[expires_col=n_result_cols++] = &str_expires_col; result_cols[event_col=n_result_cols++] = &str_event_col; result_cols[event_id_col=n_result_cols++] = &str_event_id_col; result_cols[to_user_col=n_result_cols++] = &str_to_user_col; result_cols[to_domain_col=n_result_cols++] = &str_to_domain_col; result_cols[from_user_col=n_result_cols++] = &str_watcher_username_col; result_cols[from_domain_col=n_result_cols++] = &str_watcher_domain_col; result_cols[callid_col=n_result_cols++] = &str_callid_col; result_cols[totag_col=n_result_cols++] = &str_to_tag_col; result_cols[fromtag_col=n_result_cols++] = &str_from_tag_col; result_cols[local_cseq_col= n_result_cols++] = &str_local_cseq_col; result_cols[remote_cseq_col= n_result_cols++] = &str_remote_cseq_col; result_cols[record_route_col= n_result_cols++] = &str_record_route_col; result_cols[sockinfo_col= n_result_cols++] = &str_socket_info_col; result_cols[contact_col= n_result_cols++] = &str_contact_col; result_cols[local_contact_col= n_result_cols++] = &str_local_contact_col; result_cols[version_col= n_result_cols++] = &str_version_col; result_cols[status_col= n_result_cols++] = &str_status_col; result_cols[reason_col= n_result_cols++] = &str_reason_col; if(!rls_db) { LM_ERR("null database connection\n"); return -1; } if(rls_dbf.use_table(rls_db, &rlsubs_table)< 0) { LM_ERR("in use table\n"); return -1; } if(rls_dbf.query(rls_db,0, 0, 0, result_cols,0, n_result_cols, 0,&res)< 0) { LM_ERR("while querrying table\n"); if(res) { rls_dbf.free_result(rls_db, res); res = NULL; } return -1; } if(res== NULL) return -1; if(res->n<=0) { LM_INFO("The query returned no result\n"); rls_dbf.free_result(rls_db, res); res = NULL; return 0; } LM_DBG("found %d db entries\n", res->n); for(i =0 ; i< res->n ; i++) { row = &res->rows[i]; row_vals = ROW_VALUES(row); memset(&s, 0, sizeof(subs_t)); expires= row_vals[expires_col].val.int_val; if(expires< (int)time(NULL)) continue; s.pres_uri.s= (char*)row_vals[pres_uri_col].val.string_val; s.pres_uri.len= strlen(s.pres_uri.s); s.to_user.s=(char*)row_vals[to_user_col].val.string_val; s.to_user.len= strlen(s.to_user.s); s.to_domain.s=(char*)row_vals[to_domain_col].val.string_val; s.to_domain.len= strlen(s.to_domain.s); s.from_user.s=(char*)row_vals[from_user_col].val.string_val; s.from_user.len= strlen(s.from_user.s); s.from_domain.s=(char*)row_vals[from_domain_col].val.string_val; s.from_domain.len= strlen(s.from_domain.s); s.to_tag.s=(char*)row_vals[totag_col].val.string_val; s.to_tag.len= strlen(s.to_tag.s); s.from_tag.s=(char*)row_vals[fromtag_col].val.string_val; s.from_tag.len= strlen(s.from_tag.s); s.callid.s=(char*)row_vals[callid_col].val.string_val; s.callid.len= strlen(s.callid.s); ev_sname.s= (char*)row_vals[event_col].val.string_val; ev_sname.len= strlen(ev_sname.s); event= pres_contains_event(&ev_sname, &parsed_event); if(event== NULL) { LM_ERR("event not found in list\n"); goto error; } s.event= event; s.event_id.s=(char*)row_vals[event_id_col].val.string_val; if(s.event_id.s) s.event_id.len= strlen(s.event_id.s); s.remote_cseq= row_vals[remote_cseq_col].val.int_val; s.local_cseq= row_vals[local_cseq_col].val.int_val; s.version= row_vals[version_col].val.int_val; s.expires= expires- (int)time(NULL); s.status= row_vals[status_col].val.int_val; s.reason.s= (char*)row_vals[reason_col].val.string_val; if(s.reason.s) s.reason.len= strlen(s.reason.s); s.contact.s=(char*)row_vals[contact_col].val.string_val; s.contact.len= strlen(s.contact.s); s.local_contact.s=(char*)row_vals[local_contact_col].val.string_val; s.local_contact.len= strlen(s.local_contact.s); s.record_route.s=(char*)row_vals[record_route_col].val.string_val; if(s.record_route.s) s.record_route.len= strlen(s.record_route.s); sockinfo_str.s = (char*)row_vals[sockinfo_col].val.string_val; if (sockinfo_str.s) { sockinfo_str.len = strlen(sockinfo_str.s); if (parse_phostport (sockinfo_str.s, sockinfo_str.len, &host.s, &host.len, &port, &proto )< 0) { LM_ERR("bad format for stored sockinfo string\n"); goto error; } s.sockinfo = grep_sock_info(&host, (unsigned short) port, (unsigned short) proto); } hash_code= core_hash(&s.pres_uri, &s.event->name, hash_size); if(pres_insert_shtable(rls_table, hash_code, &s)< 0) { LM_ERR("adding new record in hash table\n"); goto error; } } rls_dbf.free_result(rls_db, res); /* delete all records */ if(rls_dbf.delete(rls_db, 0,0,0,0)< 0) { LM_ERR("deleting all records from database table\n"); return -1; } return 0; error: if(res) rls_dbf.free_result(rls_db, res); return -1; } int add_rls_event(modparam_t type, void* val) { char* event= (char*)val; event_t e; if(event_parser(event, strlen(event), &e)< 0) { LM_ERR("while parsing event = %s\n", event); return -1; } if(e.parsed & EVENT_OTHER) { LM_ERR("wrong event= %s\n", event); return -1; } rls_events|= e.parsed; return 0; } static void update_subs(subs_t *subs) { xmlDocPtr doc = NULL; xmlNodePtr service_node = NULL; if ((subs->expires -= (int)time(NULL)) <= 0) { LM_WARN("found expired subscription for: %.*s\n", subs->pres_uri.len, subs->pres_uri.s); goto done; } if(get_resource_list(&subs->pres_uri, subs->from_user, subs->from_domain, &service_node, &doc) < 0) { LM_ERR("failed getting resource list for: %.*s\n", subs->pres_uri.len, subs->pres_uri.s); goto done; } if(doc==NULL) { LM_WARN("no document returned for: %.*s\n", subs->pres_uri.len, subs->pres_uri.s); goto done; } subs->internal_update_flag = 1; if(resource_subscriptions(subs, service_node) < 0) { LM_ERR("failed sending subscribe requests to resources in list\n"); goto done; } done: if (doc != NULL) xmlFreeDoc(doc); } static struct mi_root* mi_update_subscriptions(struct mi_root* cmd, void* param) { struct mi_node* node = NULL; struct sip_uri parsed_uri; str uri; int i; subs_t *subs, *subs_copy; LM_DBG("start\n"); node = cmd->node.kids; if(node == NULL) return init_mi_tree(404, "No parameters", 13); /* Get presentity URI */ uri = node->value; if(uri.s == NULL || uri.len== 0) { LM_ERR( "empty uri\n"); return init_mi_tree(404, "Empty presentity URI", 20); } if(node->next!= NULL) { LM_ERR( "Too many parameters\n"); return init_mi_tree(400, "Too many parameters", 19); } if (parse_uri(uri.s, uri.len, &parsed_uri) < 0) { LM_ERR("bad uri: %.*s\n", uri.len, uri.s); return 0; } LM_DBG("watcher username: %.*s, watcher domain: %.*s\n", parsed_uri.user.len, parsed_uri.user.s, parsed_uri.host.len, parsed_uri.host.s); if (rls_table == NULL) { LM_ERR("rls_table is NULL\n"); return 0; } /* Search through the entire subscription table for matches... */ for (i = 0; i < hash_size; i++) { lock_get(&rls_table[i].lock); subs = rls_table[i].entries->next; while (subs != NULL) { if (subs->from_user.len == parsed_uri.user.len && strncmp(subs->from_user.s, parsed_uri.user.s, parsed_uri.user.len) == 0 && subs->from_domain.len == parsed_uri.host.len && strncmp(subs->from_domain.s, parsed_uri.host.s, parsed_uri.host.len) == 0) { subs_copy = NULL; LM_DBG("found matching RLS subscription for: %.*s\n", subs->pres_uri.len, subs->pres_uri.s); if ((subs_copy = pres_copy_subs(subs, PKG_MEM_TYPE)) == NULL) { LM_ERR("subs_t copy failed\n"); lock_release(&rls_table[i].lock); return 0; } update_subs(subs_copy); pkg_free(subs_copy); } subs = subs->next; } lock_release(&rls_table[i].lock); } return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } opensips-2.2.2/modules/rls/rls.h000066400000000000000000000131701300170765700166060ustar00rootroot00000000000000/* * rls module - resource list server * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-11 initial version (Anca Vamanu) */ #ifndef _RLS_H_ #define _RLS_H_ #include "../../str.h" #include "../xcap/api.h" #include "../xcap_client/xcap_functions.h" #include "../pua/send_subscribe.h" #include "../pua/send_publish.h" #include "../pua/pidf.h" #include "../presence/hash.h" #include "../presence/event_list.h" #include "../signaling/signaling.h" #include "../../db/db_con.h" #include "../../db/db.h" #define NO_UPDATE_TYPE -1 #define UPDATED_TYPE 1 #define NOT_KNOWN_STATE 0 #define ACTIVE_STATE 1<<1 #define PENDING_STATE 1<<2 #define TERMINATED_STATE 1<<3 typedef struct dialog_id { str callid; str to_tag; str from_tag; }dialog_id_t; /* rls_presentity table structure: - LIST URI (string) - presentity URI (string) - presence state (string) /-- the following ones needed when updating in db on timer --/ - auth_state (int) - reason (string) - updated (int) */ typedef struct rls_resource { str pres_uri; int auth_state; str reason; int updated; str* instance_id; str* cid; struct rls_resource* next; /* the last 2 parameters say if a query in database is needed */ }rls_res_t; extern char* xcap_root; extern unsigned int xcap_port; extern str server_address; extern str presence_server; extern int waitn_time; extern str rlsubs_table; extern str rlpres_table; extern int hash_size; extern shtable_t rls_table; extern int pid; extern int rls_max_expires; extern int rls_events; extern int to_presence_code; /* database connection */ extern db_con_t *rls_db; extern db_func_t rls_dbf; extern struct tm_binds tmb; extern struct sig_binds rls_sigb; /* xcap API */ extern str db_url; extern str rls_xcap_table; extern int rls_integrated_xcap_server; extern normalize_sip_uri_t normalizeSipUri; extern parse_xcap_uri_t xcapParseUri; extern get_xcap_doc_t xcapDbGetDoc; /** libxml api */ extern xmlDocGetNodeByName_t XMLDocGetNodeByName; extern xmlNodeGetNodeByName_t XMLNodeGetNodeByName; extern xmlNodeGetNodeContentByName_t XMLNodeGetNodeContentByName; extern xmlNodeGetAttrContentByName_t XMLNodeGetAttrContentByName; /* functions imported from presence to handle subscribe hash table */ extern new_shtable_t pres_new_shtable; extern insert_shtable_t pres_insert_shtable; extern search_shtable_t pres_search_shtable; extern update_shtable_t pres_update_shtable; extern delete_shtable_t pres_delete_shtable; extern destroy_shtable_t pres_destroy_shtable; extern mem_copy_subs_t pres_copy_subs; extern extract_sdialog_info_t pres_extract_sdialog_info; /* functions imported from pua module*/ extern send_subscribe_t pua_send_subscribe; extern get_record_id_t pua_get_record_id; extern get_subs_list_t pua_get_subs_list; /* functions imported from presence module */ extern contains_event_t pres_contains_event; extern search_event_t pres_search_event; extern get_event_list_t pres_get_ev_list; /* xcap client functions */ extern xcapGetNewDoc_t xcap_GetNewDoc; extern str str_rlsubs_did_col; extern str str_resource_uri_col; extern str str_updated_col; extern str str_auth_state_col; extern str str_reason_col; extern str str_content_type_col; extern str str_presence_state_col; extern str str_expires_col; extern str str_presentity_uri_col; extern str str_event_col; extern str str_event_id_col; extern str str_to_user_col; extern str str_to_domain_col; extern str str_watcher_username_col; extern str str_watcher_domain_col; extern str str_callid_col; extern str str_to_tag_col; extern str str_from_tag_col; extern str str_local_cseq_col; extern str str_remote_cseq_col; extern str str_record_route_col; extern str str_socket_info_col; extern str str_contact_col; extern str str_local_contact_col; extern str str_version_col; extern str str_status_col; extern str str_username_col; extern str str_domain_col; extern str str_doc_type_col; extern str str_etag_col; extern str str_doc_col; extern str str_doc_uri_col; #define DID_SEP_LEN strlen(DID_SEP) #define DID_SEP ";" #define DID_INIT_LEN (2* sizeof(DID_SEP)) /* did_str= *callid*DID_SEP*from_tag*DID_SEP*to_tag* */ #define MAX_DID_LEN 255 /* not to exceed db field length */ static inline int CONSTR_RLSUBS_DID(subs_t* subs, str *did) { int len; len= (DID_INIT_LEN+ subs->callid.len+ subs->to_tag.len+ subs->from_tag.len+ 10)* sizeof(char); if(len > MAX_DID_LEN) { LM_ERR("Max length exceeded [%d]\n", len); return -1; } did->s= (char*)pkg_malloc(len); if(did->s== NULL) { ERR_MEM(PKG_MEM_STR); } did->len= sprintf(did->s, "%.*s%s%.*s%s%.*s", subs->callid.len, subs->callid.s, DID_SEP,subs->from_tag.len, subs->from_tag.s, DID_SEP, subs->to_tag.len, subs->to_tag.s); if(did->len>= len) { LM_ERR("ERROR buffer size overflown\n"); pkg_free(did->s); return -1; } did->s[did->len]= '\0'; LM_DBG("did= %s\n", did->s); return 0; error: return -1; } #endif opensips-2.2.2/modules/rls/subscribe.c000066400000000000000000000544061300170765700177710ustar00rootroot00000000000000/* * rls module - resource list server * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-11 initial version (Anca Vamanu) */ #include #include #include #include #include "../../dprint.h" #include "../../data_lump_rpl.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_event.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_cseq.h" #include "../../parser/parse_from.h" #include "../../parser/parse_supported.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_rr.h" #include "../presence/subscribe.h" #include "../presence/utils_func.h" #include "../presence/hash.h" #include "subscribe.h" #include "notify.h" #include "rls.h" int counter= 0; static str su_200_rpl = str_init("OK"); //static str pu_421_rpl = str_init("Extension Required"); static str pu_400_rpl = str_init("Bad request"); static str pu_500_rpl = str_init("Server Error"); static str stale_cseq_rpl = str_init("Stale Cseq Value"); static str pu_489_rpl = str_init("Bad Event"); static str pu_404_rpl = str_init("Not Found"); #define Stale_cseq_code 401 #define SUBS_EXTRA_HDRS "Supported: eventlist\r\nAccept: application/pidf+xml, application/rlmi+xml, application/watcherinfo+xml, multipart/related, application/xcap-diff+xml\r\n" #define SUBS_EXTRA_HDRS_LEN sizeof(SUBS_EXTRA_HDRS) -1 subs_t* constr_new_subs(struct sip_msg* msg, struct to_body *pto, pres_ev_t* event); int update_rlsubs( subs_t* subs,unsigned int hash_code, int* reply_code, str* reply_str); xmlNodePtr search_service_uri(xmlDocPtr doc, str* service_uri) { xmlNodePtr rl_node, node; struct sip_uri sip_uri; str uri, uri_str; str *normalized_uri; rl_node= XMLDocGetNodeByName(doc, "rls-services", NULL); if(rl_node== NULL) { LM_ERR("while extracting rls-services node\n"); return NULL; } for(node= rl_node->children; node; node= node->next) { if(xmlStrcasecmp(node->name,(unsigned char*)"service")== 0) { uri.s = XMLNodeGetAttrContentByName(node, "uri"); if (uri.s == NULL) { LM_DBG("failed to fetch 'uri' in service [invalid XML from XCAP]\n"); continue; } uri.len = strlen(uri.s); normalized_uri = normalizeSipUri(&uri); if (normalized_uri->s == NULL || normalized_uri->len == 0) { LM_ERR("failed to normalize service URI\n"); xmlFree(uri.s); return NULL; } xmlFree(uri.s); if(parse_uri(normalized_uri->s, normalized_uri->len, &sip_uri)< 0) { LM_ERR("failed to parse uri\n"); return NULL; } if(uandd_to_uri(sip_uri.user, sip_uri.host, &uri_str)< 0) { LM_ERR("failed to construct uri from user and domain\n"); return NULL; } if(uri_str.len== service_uri->len && strncmp(uri_str.s, service_uri->s, uri_str.len) == 0) { pkg_free(uri_str.s); return node; } LM_DBG("match not found, service-uri = [%.*s]\n", uri_str.len, uri_str.s); pkg_free(uri_str.s); } } return NULL; } static int http_get_resource_list(str* owner_user, str* owner_domain, str** doc) { str body = {0, 0}; str *doc_tmp; xcap_get_req_t req; xcap_doc_sel_t doc_sel; memset(&doc_sel, 0, sizeof(xcap_doc_sel_t)); doc_sel.auid.s = "rls-services"; doc_sel.auid.len = strlen(doc_sel.auid.s); doc_sel.doc_type = RLS_SERVICES; doc_sel.type = USERS_TYPE; if(uandd_to_uri(*owner_user, *owner_domain, &doc_sel.xid) < 0) { LM_ERR("failed to create uri from user and domain\n"); goto error; } memset(&req, 0, sizeof(xcap_get_req_t)); req.xcap_root = xcap_root; req.port = xcap_port; req.doc_sel = doc_sel; if(xcap_GetNewDoc(req, *owner_user, *owner_domain, &body) < 0) { LM_ERR("while fetching data from xcap server\n"); pkg_free(doc_sel.xid.s); goto error; } pkg_free(doc_sel.xid.s); if (body.s == NULL) goto error; doc_tmp = pkg_malloc(sizeof(*doc_tmp)); if(doc_tmp == NULL) { LM_ERR("No more pkg memory\n"); goto error; } doc_tmp->s = pkg_malloc(body.len); if(doc_tmp->s == NULL) { pkg_free(doc_tmp); LM_ERR("No more pkg memory\n"); goto error; } memcpy(doc_tmp->s, body.s, body.len); doc_tmp->len = body.len; pkg_free(body.s); *doc = doc_tmp; return 0; error: if (body.s) pkg_free(body.s); return -1; } /* * Function that searches a resource list document for the user and then * looks in the document for the service uri * returns: * 0: success ( service uri found) * -1: server error * doc : * NULL: document or service uri not found * pointer to xmlDocPtr structure if service uri found * */ int get_resource_list(str* service_uri, str owner_user, str owner_domain, xmlNodePtr* service_node, xmlDocPtr* rl_doc) { str *doc = NULL; str *etag = NULL; xmlDocPtr xml_doc = NULL; xmlNodePtr snode = NULL; *rl_doc = NULL; *service_node = NULL; if (xcapDbGetDoc(&owner_user, &owner_domain, RLS_SERVICES, NULL, NULL, &doc, &etag) < 0) { LM_ERR("while getting RLS document from DB\n"); goto error; } if (doc == NULL) { LM_DBG("No rl document found in database\n"); if (rls_integrated_xcap_server) goto done; /* Use xcap_client to try to fetch the document */ if (http_get_resource_list(&owner_user, &owner_domain, &doc) < 0) goto done; } /* Document is loaded in doc either via DB or HTTP */ LM_DBG("rls_services document:\n%.*s\n", doc->len, doc->s); xml_doc = xmlParseMemory(doc->s, doc->len); if(xml_doc == NULL) { LM_ERR("while parsing XML memory\n"); goto error; } snode = search_service_uri(xml_doc, service_uri); if (snode == NULL) { LM_DBG("service uri %.*s not found in rl document for user" " sip:%.*s@%.*s\n", service_uri->len, service_uri->s, owner_user.len, owner_user.s, owner_domain.len, owner_domain.s); xmlFreeDoc(xml_doc); goto done; } *rl_doc = xml_doc; *service_node = snode; done: if (doc != NULL) { if (doc->s != NULL) pkg_free(doc->s); pkg_free(doc); } if (etag != NULL) { if (etag->s != NULL) pkg_free(etag->s); pkg_free(etag); } return 0; error: if (doc != NULL) { if (doc->s != NULL) pkg_free(doc->s); pkg_free(doc); } if (etag != NULL) { if (etag->s != NULL) pkg_free(etag->s); pkg_free(etag); } if(xml_doc) xmlFreeDoc(xml_doc); return -1; } /* * Not used anymore int reply_421(struct sip_msg* msg) { str hdr_append; char buffer[256]; hdr_append.s = buffer; hdr_append.s[0]='\0'; hdr_append.len = sprintf(hdr_append.s, "Require: eventlist\r\n"); if(hdr_append.len < 0) { LM_ERR("unsuccessful sprintf\n"); return -1; } hdr_append.s[hdr_append.len]= '\0'; if (add_lump_rpl( msg, hdr_append.s, hdr_append.len, LUMP_RPL_HDR)==0 ) { LM_ERR("unable to add lump_rl\n"); return -1; } if (rls_sigb.reply(msg, 421, &pu_421_rpl, 0) == -1) { LM_ERR("failed to send reply\n"); return -1; } return 0; } */ int reply_200(struct sip_msg* msg, str* local_contact, int expires, str* rtag) { char* hdr_append; int len; int lexpire_len; char *lexpire_s; char* p; lexpire_s = int2str((unsigned long)expires, &lexpire_len); len = 9 /*"Expires: "*/ + lexpire_len + CRLF_LEN + 10 /*"Contact: <"*/ + local_contact->len + 1 /*">"*/ + ((msg->rcv.proto!=PROTO_UDP)?15/*";transport=xxxx"*/:0) + CRLF_LEN + 18 /*Require: eventlist*/ + CRLF_LEN; hdr_append = (char *)pkg_malloc( len ); if(hdr_append == NULL) { ERR_MEM(PKG_MEM_STR); } p = hdr_append; /* expires header */ memcpy(p, "Expires: ", 9); p += 9; memcpy(p,lexpire_s,lexpire_len); p += lexpire_len; memcpy(p,CRLF,CRLF_LEN); p += CRLF_LEN; /* contact header */ memcpy(p,"Contact: <", 10); p += 10; memcpy(p,local_contact->s,local_contact->len); p += local_contact->len; if (msg->rcv.proto!=PROTO_UDP) { memcpy(p,";transport=",11); p += 11; p = proto2str(msg->rcv.proto, p); if (p==NULL) { LM_ERR("invalid proto\n"); goto error; } } *(p++) = '>'; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; memcpy(p, "Require: eventlist", 18); p += 18; memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; if (add_lump_rpl( msg, hdr_append, p-hdr_append, LUMP_RPL_HDR)==0 ) { LM_ERR("unable to add lump_rl\n"); goto error; } if( rls_sigb.reply( msg, 200, &su_200_rpl, rtag)< 0) { LM_ERR("failed to send reply\n"); goto error; } pkg_free(hdr_append); return 0; error: pkg_free(hdr_append); return -1; } int reply_489(struct sip_msg * msg) { str hdr_append; char buffer[256]; str* ev_list; hdr_append.s = buffer; hdr_append.s[0]='\0'; hdr_append.len = sprintf(hdr_append.s, "Allow-Events: "); if(hdr_append.len < 0) { LM_ERR("unsuccessful sprintf\n"); return -1; } if(pres_get_ev_list(&ev_list)< 0) { LM_ERR("while getting ev_list\n"); return -1; } memcpy(hdr_append.s+ hdr_append.len, ev_list->s, ev_list->len); hdr_append.len+= ev_list->len; pkg_free(ev_list->s); pkg_free(ev_list); memcpy(hdr_append.s+ hdr_append.len, CRLF, CRLF_LEN); hdr_append.len+= CRLF_LEN; hdr_append.s[hdr_append.len]= '\0'; if (add_lump_rpl( msg, hdr_append.s, hdr_append.len, LUMP_RPL_HDR)==0 ) { LM_ERR("unable to add lump_rl\n"); return -1; } if (rls_sigb.reply(msg, 489, &pu_489_rpl, 0) == -1) { LM_ERR("failed to send reply\n"); return -1; } return 0; } /* * Function called from script to process RLS SUBSCRIBE messages * - returns: * 1 - success * -1 - error * - sends an appropriate reply in every case * */ int rls_handle_subscribe(struct sip_msg* msg, char* s1, char* s2) { struct to_body *pto, *pfrom = NULL; subs_t subs; pres_ev_t* event= NULL; str* contact= NULL; xmlDocPtr doc= NULL; xmlNodePtr service_node= NULL; unsigned int hash_code= 0; event_t* parsed_event; param_t* ev_param= NULL; int init_req; int reply_code; str reply_str; /*** filter: 'For me or for presence server?' */ reply_code = 400; reply_str = pu_400_rpl; memset(&subs, 0, sizeof(subs_t)); if ( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("parsing headers\n"); goto error; } /* check for Support: eventlist header */ if(!msg->supported) { LM_DBG("no supported header found\n"); return to_presence_code; } if(parse_supported(msg) < 0) { LM_ERR("failed to parse supported headers\n"); reply_code = 500; reply_str = pu_500_rpl; goto error; } if(!(get_supported(msg) & F_SUPPORTED_EVENTLIST)) { LM_DBG("No 'Support: eventlist' header found\n"); return to_presence_code; } /* inspecting the Event header field */ if(msg->event && msg->event->body.len > 0) { if (!msg->event->parsed && (parse_event(msg->event) < 0)) { LM_ERR("cannot parse Event header\n"); reply_code = 500; reply_str = pu_500_rpl; goto error; } if(! ( ((event_t*)msg->event->parsed)->parsed & rls_events) ) { return to_presence_code; } } else { goto bad_event; } /* search event in the list */ parsed_event= (event_t*)msg->event->parsed; event= pres_search_event(parsed_event); if(event== NULL) { goto bad_event; } subs.event= event; /* extract the id if any*/ ev_param= parsed_event->params; while(ev_param) { if(ev_param->name.len== 2 && strncasecmp(ev_param->name.s, "id", 2)== 0) { subs.event_id= ev_param->body; break; } ev_param= ev_param->next; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("parsing 'To' header failed\n"); goto error; } if(parse_from_uri(msg)<0) { LM_ERR("failed to parse From header\n"); goto error; } pfrom = (struct to_body*)msg->from->parsed; if(pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); goto error; } /* verify if the presentity URI is a resource list */ if(pto->tag_value.s== NULL || pto->tag_value.len==0) /* if an initial Subscribe */ { struct sip_uri fu = ((struct to_body*)msg->from->parsed)->parsed_uri; if( parse_sip_msg_uri(msg)< 0) { LM_ERR("parsing Request URI failed\n"); goto error; } /*verify if Request URI represents a list by asking xcap server*/ if(uandd_to_uri(msg->parsed_uri.user, msg->parsed_uri.host, &subs.pres_uri)< 0) { LM_ERR("while constructing uri from user and domain\n"); reply_code = 500; reply_str = pu_500_rpl; goto error; } if( get_resource_list(&subs.pres_uri, fu.user, fu.host, &service_node, &doc) < 0) { LM_ERR("failed to get resource list document\n"); reply_code = 500; reply_str = pu_500_rpl; goto error; } if(doc== NULL|| service_node==NULL) { LM_DBG("list not found - search for uri = %.*s\n",subs.pres_uri.len, subs.pres_uri.s); pkg_free(subs.pres_uri.s); return to_presence_code; } } else /* if request inside a dialog */ { if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); goto error; } /* search if a stored dialog */ hash_code= core_hash(&msg->callid->body, &pto->tag_value, hash_size); lock_get(&rls_table[hash_code].lock); if(pres_search_shtable(rls_table,msg->callid->body, pto->tag_value, pfrom->tag_value, hash_code)== NULL) { lock_release(&rls_table[hash_code].lock); /* reply with Call/Transaction Does Not Exist */ LM_DBG("No dialog match found\n"); return to_presence_code; } lock_release(&rls_table[hash_code].lock); } /* extract dialog information from message headers */ if(pres_extract_sdialog_info(&subs, msg, rls_max_expires, &init_req, server_address)< 0) { LM_ERR("bad Subscribe request\n"); goto error; } reply_code = 500; reply_str = pu_500_rpl; if(init_req) /* if an initial subscribe */ { /** reply with 200 OK*/ if(reply_200(msg, &subs.local_contact, subs.expires, &subs.to_tag)< 0) goto error_free; hash_code= core_hash(&subs.callid, &subs.to_tag, hash_size); subs.local_cseq= 0; if(subs.expires!= 0) { subs.version= 1; if(pres_insert_shtable(rls_table, hash_code, &subs)< 0) { LM_ERR("while adding new subscription\n"); goto error_free; } } } else { if(update_rlsubs(&subs, hash_code, &reply_code, &reply_str) < 0) { LM_ERR("while updating resource list subscription\n"); goto error; } if(get_resource_list(&subs.pres_uri, subs.from_user, subs.from_domain, &service_node, &doc)< 0) { LM_ERR("when getting resource list\n"); goto error; } if(doc== NULL || service_node== NULL) { LM_DBG("list not found( in-dialog request)- search for uri = %.*s\n", subs.pres_uri.len, subs.pres_uri.s); reply_code = 404; reply_str = pu_404_rpl; goto error; } /** reply with 200 OK*/ if(reply_200(msg, &subs.local_contact, subs.expires, 0)< 0) goto error_free; } /*** send Subscribe requests for all in the list */ /* call sending Notify with full state */ if(send_full_notify(&subs, service_node, subs.version, &subs.pres_uri,hash_code)< 0) { LM_ERR("while sending full state Notify\n"); goto error_free; } if(resource_subscriptions(&subs, service_node)< 0) { LM_ERR("while sending Subscribe requests to resources in a list\n"); goto error_free; } if(contact) { if(contact->s) pkg_free(contact->s); pkg_free(contact); } pkg_free(subs.pres_uri.s); if(subs.record_route.s) pkg_free(subs.record_route.s); xmlFreeDoc(doc); return 1; bad_event: if(reply_489(msg)< 0) LM_ERR("failed to send 489 reply\n"); goto error_free; error: if (rls_sigb.reply(msg, reply_code, &reply_str, 0) == -1) { LM_ERR("failed to send 400 reply\n"); return -1; } error_free: if(contact) { if(contact->s) pkg_free(contact->s); pkg_free(contact); } if(subs.pres_uri.s) pkg_free(subs.pres_uri.s); if(subs.record_route.s) pkg_free(subs.record_route.s); if(doc) xmlFreeDoc(doc); return -1; } /* * function that updates a subscription in hash table * sets reply_code and reply_str in case of error and * if different that server error * */ int update_rlsubs( subs_t* subs, unsigned int hash_code, int* reply_code, str* reply_str) { subs_t* s, *ps; /* search the record in hash table */ lock_get(&rls_table[hash_code].lock); s= pres_search_shtable(rls_table, subs->callid, subs->to_tag, subs->from_tag, hash_code); if(s== NULL) { LM_DBG("record not found in hash table\n"); lock_release(&rls_table[hash_code].lock); return -1; } s->expires= subs->expires+ (int)time(NULL); if(s->db_flag == NO_UPDATEDB_FLAG) s->db_flag= UPDATEDB_FLAG; if( s->remote_cseq>= subs->remote_cseq) { lock_release(&rls_table[hash_code].lock); LM_DBG("stale cseq stored cseq= %d - received cseq= %d\n", s->remote_cseq, subs->remote_cseq); *reply_code = Stale_cseq_code; *reply_str = stale_cseq_rpl; return -1; } s->remote_cseq= subs->remote_cseq; subs->pres_uri.s= (char*)pkg_malloc(s->pres_uri.len* sizeof(char)); if(subs->pres_uri.s== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(subs->pres_uri.s, s->pres_uri.s, s->pres_uri.len); subs->pres_uri.len= s->pres_uri.len; subs->local_cseq= s->local_cseq; subs->version= s->version; if(s->record_route.s && s->record_route.len) { subs->record_route.s= (char*)pkg_malloc(s->record_route.len); if(subs->record_route.s== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(subs->record_route.s, s->record_route.s, s->record_route.len); subs->record_route.len= s->record_route.len; } if(subs->expires== 0) { /* delete record from hash table */ ps= rls_table[hash_code].entries; int found= 0; while(ps->next) { if(ps->next== s) { found= 1; break; } ps= ps->next; } if(found== 0) { LM_ERR("record not found\n"); goto error; } ps->next= s->next; shm_free(s); /* delete from rls_presentity table also */ } lock_release(&rls_table[hash_code].lock); return 0; error: lock_release(&rls_table[hash_code].lock); return -1; } int send_resource_subs(char* uri, void* param) { int duplicate = 0; str pres_uri; str *tmp_str; subs_info_t *s = (subs_info_t *) ((void**)param)[0]; list_entry_t **rls_contact_list = (list_entry_t **) ((void**)param)[1]; pres_uri.s= uri; pres_uri.len= strlen(uri); s->pres_uri= &pres_uri; /* Build a list of uris checking each uri exists only once */ if ((tmp_str = (str *)pkg_malloc(sizeof(str))) == NULL) { LM_ERR("out of private memory\n"); return -1; } if ((tmp_str->s = (char *)pkg_malloc(sizeof(char) * pres_uri.len + 1)) == NULL) { pkg_free(tmp_str); LM_ERR("out of private memory\n"); return -1; } memcpy(tmp_str->s, pres_uri.s, pres_uri.len); tmp_str->len = pres_uri.len; tmp_str->s[tmp_str->len] = '\0'; *rls_contact_list = list_insert(tmp_str, *rls_contact_list, &duplicate); if (duplicate != 0) { LM_WARN("%.*s has %.*s multiple times in the same resource list\n", s->watcher_uri->len, s->watcher_uri->s, s->pres_uri->len, s->pres_uri->s); return 1; } return pua_send_subscribe(s); } int resource_subscriptions(subs_t* subs, xmlNodePtr rl_node) { char* uri= NULL; subs_info_t s; str wuri= {0, 0}; str did_str= {0, 0}; str *tmp_str; int cont_no= 0; static str ehdr= {SUBS_EXTRA_HDRS, SUBS_EXTRA_HDRS_LEN}; list_entry_t *rls_contact_list = NULL; list_entry_t *rls_subs_list = NULL; void* params[2] = {&s, &rls_contact_list}; /* if is initial send an initial Subscribe * else search in hash table for a previous subscription */ if(CONSTR_RLSUBS_DID(subs, &did_str)< 0) { LM_ERR("Failed to create did\n"); return -1; } memset(&s, 0, sizeof(subs_info_t)); if( uandd_to_uri(subs->from_user, subs->from_domain, &wuri)< 0) { LM_ERR("while constructing uri from user and domain\n"); goto error; } s.id= did_str; s.watcher_uri= &wuri; s.to_uri.s=0; s.contact= &server_address; s.event= get_event_flag(&subs->event->name); if(presence_server.s) s.outbound_proxy= &presence_server; if(s.event< 0) { LM_ERR("not recognized event\n"); goto error; } s.expires= subs->expires; s.source_flag= RLS_SUBSCRIBE; s.extra_headers= &ehdr; s.internal_update_flag = subs->internal_update_flag; if(process_list_and_exec(rl_node, subs->from_user, subs->from_domain, send_resource_subs, params, &cont_no) < 0) { LM_ERR("while processing list\n"); goto error; } LM_INFO("Subscription from %.*s for resource list uri %.*s expanded to" " %d contacts\n", wuri.len, wuri.s, subs->pres_uri.len, subs->pres_uri.s, cont_no); if (s.internal_update_flag) { s.internal_update_flag = 0; rls_subs_list = pua_get_subs_list(&did_str); while ((tmp_str = list_pop(&rls_contact_list)) != NULL) { rls_subs_list = list_remove(*tmp_str, rls_subs_list); pkg_free(tmp_str->s); pkg_free(tmp_str); } while ((tmp_str = list_pop(&rls_subs_list)) != NULL) { LM_DBG("Removing subscription for %.*s\n", tmp_str->len, tmp_str->s); s.expires = 0; send_resource_subs(tmp_str->s, params); pkg_free(tmp_str->s); pkg_free(tmp_str); } } if (rls_contact_list != NULL) { list_free(&rls_contact_list); } pkg_free(wuri.s); pkg_free(did_str.s); return 0; error: if(wuri.s) pkg_free(wuri.s); if(uri) xmlFree(uri); if(did_str.s) pkg_free(did_str.s); return -1; } opensips-2.2.2/modules/rls/subscribe.h000066400000000000000000000024721300170765700177720ustar00rootroot00000000000000/* * rls module - resource list server * * Copyright (C) 2007 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-11 initial version (Anca Vamanu) */ #ifndef RLS_SUBSCRIBE_H #define RLS_SUBSCRIBE_H #include #include "../../parser/msg_parser.h" #include "../pua/uri_list.h" int rls_handle_subscribe(struct sip_msg* msg, char* s1, char* s2); int get_resource_list(str* service_uri, str owner_user, str owner_domain, xmlNodePtr* service_node, xmlDocPtr* rl_doc); int resource_subscriptions(subs_t* subs, xmlNodePtr rl_node); #endif opensips-2.2.2/modules/rr/000077500000000000000000000000001300170765700154565ustar00rootroot00000000000000opensips-2.2.2/modules/rr/Makefile000066400000000000000000000003441300170765700171170ustar00rootroot00000000000000# $Id$ # # rr module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=rr.so LIBS= #DEFS+=-DENABLE_USER_CHECK include ../../Makefile.modules opensips-2.2.2/modules/rr/README000066400000000000000000000412551300170765700163450ustar00rootroot00000000000000rr Module Jan Janak FhG FOKUS Bogdan-Andrei Iancu Edited by Jan Janak Edited by Bogdan-Andrei Iancu Copyright © 2003 FhG FOKUS Copyright © 2005 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dialog support 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. append_fromtag (integer) 1.4.2. enable_double_rr (integer) 1.4.3. add_username (integer) 1.4.4. enable_socket_mismatch_warning (integer) 1.5. Exported Functions 1.5.1. loose_route() 1.5.2. record_route() and record_route(string) 1.5.3. record_route_preset(string [, string2]) 1.5.4. add_rr_param(param) 1.5.5. check_route_param(re) 1.5.6. is_direction(dir) 1.5.7. Exported pseudo-variables 2. Developer Guide 2.1. Available Functions 2.1.1. add_rr_param( msg, param) 2.1.2. check_route_param( msg, re) 2.1.3. is_direction( msg, dir) 2.1.4. get_route_param( msg, name, val) 2.1.5. register_rrcb( callback, param, prior) 2.2. Examples 3. Frequently Asked Questions List of Examples 1.1. Dialog support in RR module 1.2. Set append_fromtag parameter 1.3. Set enable_double_rr parameter 1.4. Set add_username parameter 1.5. enable_socket_mismatch_warning usage 1.6. loose_route usage 1.7. record_route usage 1.8. record_route_preset usage 1.9. add_rr_param usage 1.10. check_route_param usage 1.11. is_direction usage 2.1. Loading RR module's API from another module Chapter 1. Admin Guide 1.1. Overview The module contains record routing logic 1.2. Dialog support OpenSIPS is basically only a transaction statefull proxy, without any dialog support build in. There are many features/services which actually require dialog awareness, like storing the information in the dialog creation stage, information which will be used during the whole dialog existence. The most urging example is NAT traversal, in dealing with the within the dialog INVITEs (re-INVITEs). When processing the initial INVITE, the proxy detects if the caller or callee is behind some NAT and fixes the signalling and media parts - since not all the detection mechanism are available for within the dialog requests (like usrloc), to be able to fix correspondingly the sequential requests, the proxy must remember that the original request was NAT processed. There are many other cases where dialog awareness fixes or helps. The solution is to store additional dialog-related information in the routing set (Record-Route/Route headers), headers which show up in all sequential requests. So any information added to the Record-Route header will be found (with no direction dependencies) in Route header (corresponding to the proxy address). As storage container, the parameters of the Record-Route / Route header will be used - Record-Route parameters mirroring are reinforced by RFC 3261 (see 12.1.1 UAS behavior). For this purpose, the modules offers the following functions: * add_rr_param() - see Section 1.5.4, “ add_rr_param(param) †* check_route_param() - see Section 1.5.5, “ check_route_param(re) †Example 1.1. Dialog support in RR module UAC OpenSIPS PROXY UAS ---- INVITE ------> record_route() ----- INVITE ----> add_rr_param(";foo=true") --- reINVITE -----> loose_route() ---- reINVITE ---> check_route_param(";foo=true") <-- reINVITE ------ loose_route() <--- reINVITE ---- check_route_param(";foo=true") <------ BYE ------- loose_route() <----- BYE ------- check_route_param(";foo=true") 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.4. Exported Parameters 1.4.1. append_fromtag (integer) If turned on, request's from-tag is appended to record-route; that's useful for understanding whether subsequent requests (such as BYE) come from caller (route's from-tag==BYE's from-tag) or callee (route's from-tag==BYE's to-tag) Default value is 1 (yes). Example 1.2. Set append_fromtag parameter ... modparam("rr", "append_fromtag", 0) ... 1.4.2. enable_double_rr (integer) There are some situations when the server needs to insert two Record-Route header fields instead of one. For example when using two disconnected networks or doing cross-protocol forwarding from UDP->TCP. This parameter enables inserting of 2 Record-Routes. The server will later remove both of them. Default value is 1 (yes). Example 1.3. Set enable_double_rr parameter ... modparam("rr", "enable_double_rr", 0) ... 1.4.3. add_username (integer) If set to a non 0 value (which means yes), the username part will be also added in the Record-Route URI. Default value is 0 (no). Example 1.4. Set add_username parameter ... modparam("rr", "add_username", 1) ... 1.4.4. enable_socket_mismatch_warning (integer) When a preset record-route header is forced in OpenSIPS config and the host from the record-route header is not the same as the host server, a warning will be printed out in the logs. The 'enable_socket_mismatch_warning' parameter enables or disables the warning. When OpenSIPS is behind a NATed firewall, we don't want this warning to be printed for every bridged call. Default value is 1 (yes). Example 1.5. enable_socket_mismatch_warning usage ... modparam("rr", "enable_socket_mismatch_warning", 0) ... 1.5. Exported Functions 1.5.1. loose_route() The function performs routing of SIP requests which contain a route set. The name is a little bit confusing, as this function also routes requests which are in the “strict router†format. This function is usually used to route in-dialog requests (like ACK, BYE, reINVITE). Nevertheless also out-of-dialog requests can have a “pre-loaded route set†and my be routed with loose_route. It also takes care of translating between strict-routers and loose-router. The loose_route() function analyzes the Route headers in the requests. If there is no Route header, the function returns FALSE and routing should be done exclusivly via RURI. If a Route header is found, the function returns TRUE and behaves as described in section 16.12 of RFC 3261. The only exception is for requests with preload Route headers (intial requests, carrying a Route header): if there is only one Route header indicating the local proxy, then the Route header is removed and the function returns FALSE. If there is a Route header but other parsing errors occur ( like parsing the TO header to get the TAG ), the function also returns FALSE. Make sure your loose_routing function can't be used by attackers to bypass proxy authorization. The loose_routing topic is very complex. See the RFC3261 for more details (grep for “route set†is a good starting point in this comprehensive RFC). This function can be used from REQUEST_ROUTE. Example 1.6. loose_route usage ... loose_route(); ... 1.5.2. record_route() and record_route(string) The function adds a new Record-Route header field. The header field will be inserted in the message before any other Record-Route header fields. If any string is passed as parameter, it will be appended as URI parameter to the Record-Route header. The string must follow the “;name=value†scheme and it may contain pseudo-variables. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. Example 1.7. record_route usage ... record_route(); ... 1.5.3. record_route_preset(string [, string2]) This function will put the string into Record-Route, don't use unless you know what you are doing. Meaning of the parameters is as follows: * string - String to be inserted into the first header field; it may contain pseudo-variables. * string2 - String to be inserted into the second header field; it may contain pseudo-variables. Note: If 'string2' is present, then the 'string' param is pointing to the outbound interface and the 'string2' param is pointing to the inbound interface. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. Example 1.8. record_route_preset usage ... record_route_preset("1.2.3.4:5090"); ... 1.5.4. add_rr_param(param) Adds a parameter to the Record-Route URI (param must be in “;name=value†format. The function may be called also before or after the record_route() call (see Section 1.5.2, “ record_route() and record_route(string) â€). Meaning of the parameters is as follows: * param - String containing the URI parameter to be added. It must follow the “;name=value†scheme; it may contain pseudo-variables. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. Example 1.9. add_rr_param usage ... add_rr_param(";nat=yes"); ... 1.5.5. check_route_param(re) The function checks if the URI parameters of the local Route header (corresponding to the local server) matches the given regular expression. It must be call after loose_route() (see Section 1.5.1, “ loose_route() â€). Meaning of the parameters is as follows: * re - regular expression to check against the Route URI parameters. This function can be used from REQUEST_ROUTE. Example 1.10. check_route_param usage ... if (check_route_param("nat=yes")) { setflag(6); } ... 1.5.6. is_direction(dir) The function checks the flow direction of the request. As for checking it's used the “ftag†Route header parameter, the append_fromtag (see Section 1.4.1, “append_fromtag (integer)†module parameter must be enabled. Also this must be called only after loose_route() (see Section 1.5.1, “ loose_route() â€). The function returns true if the “dir†is the same with the request's flow direction. The “downstream†(UAC to UAS) direction is relative to the initial request that created the dialog. Meaning of the parameters is as follows: * dir - string containing the direction to be checked. It may be “upstream†(from UAS to UAC) or “downstream†(UAC to UAS). This function can be used from REQUEST_ROUTE. Example 1.11. is_direction usage ... if (is_direction("upstream")) { xdbg("upstream request ($rm)\n"); } ... 1.5.7. Exported pseudo-variables Exported pseudo-variables are listed in the next sections. 1.5.7.1. $rr_params $rr_params - the whole string of the Route parameters - this is available only after calling loose_route() Chapter 2. Developer Guide The RR module provides an internal API to be used by other OpenSIPS modules. The API offers support for SIP dialog based functionalities - for more about the dialog support offered by RR module, see Section 1.2, “Dialog supportâ€. For internal(non-script) usage, the RR module offers to other module the possibility to register callback functions to be executed each time a local Route header is processed. The callback function will receive as parameter the register parameter and the Route header parameter string. 2.1. Available Functions 2.1.1. add_rr_param( msg, param) Adds a parameter to the requests's Record-Route URI (param must be in “;name=value†format). The function returns 0 on success. Otherwise, -1 is returned. Meaning of the parameters is as follows: * struct sip_msg* msg - request that will has the parameter “param†added to its Record-Route header. * str* param - parameter to be added to the Record-Route header - it must be in “;name=value†format. 2.1.2. check_route_param( msg, re) The function checks for the request “msg†if the URI parameters of the local Route header (corresponding to the local server) matches the given regular expression “reâ€. It must be call after the loose_route was done. The function returns 0 on success. Otherwise, -1 is returned. Meaning of the parameters is as follows: * struct sip_msg* msg - request that will has the Route header parameters checked. * regex_t* param - compiled regular expression to be checked against the Route header parameters. 2.1.3. is_direction( msg, dir) The function checks the flow direction of the request “msgâ€. As for checking it's used the “ftag†Route header parameter, the append_fromtag (see Section 1.4.1, “append_fromtag (integer)†module parameter must be enables. Also this must be call only after the loose_route is done. The function returns 0 if the “dir†is the same with the request's flow direction. Otherwise, -1 is returned. Meaning of the parameters is as follows: * struct sip_msg* msg - request that will have the direction checked. * int dir - direction to be checked against. It may be “RR_FLOW_UPSTREAM†or “RR_FLOW_DOWNSTREAMâ€. 2.1.4. get_route_param( msg, name, val) The function search in to the “msgâ€'s Route header parameters the parameter called “name†and returns its value into “valâ€. It must be call only after the loose_route is done. The function returns 0 if parameter was found (even if it has no value). Otherwise, -1 is returned. Meaning of the parameters is as follows: * struct sip_msg* msg - request that will have the Route header parameter searched. * str *name - contains the Route header parameter to be serached. * str *val - returns the value of the searched Route header parameter if found. It might be empty string if the parameter had no value. 2.1.5. register_rrcb( callback, param, prior) The function register a new callback (along with its parameter). The callback will be called when a loose route will be performed for the local address. The function returns 0 on success. Otherwise, -1 is returned. Meaning of the parameters is as follows: * rr_cb_t callback - callback function to be registered. * void *param - parameter to be passed to the callback function. * short prior - parameter to set the priority. If the callback depends on another module, this parameter should be greater than that module's priority. Otherwise, it should be 0. 2.2. Examples Example 2.1. Loading RR module's API from another module ... #include "../rr/api.h" ... struct rr_binds my_rrb; ... ... /* load the RR API */ if (load_rr_api( &my_rrb )!=0) { LM_ERR("can't load RR API\n"); goto error; } ... ... /* register a RR callback */ if (my_rrb.register_rrcb(my_callback,0,0))!=0) { LM_ERR("can't register RR callback\n"); goto error; } ... Chapter 3. Frequently Asked Questions 3.1. What happened with old enable_full_lr parameter The parameter is considered obsolete. It was only introduced to allow compatibility with older SIP entities, that complained about a lr parameter without a value. This behavior breaks RFC 3261, and since nowadays most SIP stacks are fixed to conform with the RFC, the parameter was removed. 3.2. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 3.3. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 3.4. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/rr/api.c000066400000000000000000000030401300170765700163700ustar00rootroot00000000000000/* * Copyright (C) 2005 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2005-08-02 first version (bogdan) */ /*! * \file * \brief OpenSIPS RR module (record-routing) * \ingroup rr */ #include "../../sr_module.h" #include "loose.h" #include "record.h" #include "api.h" #include "rr_cb.h" extern int append_fromtag; int load_rr( struct rr_binds *rrb ) { rrb->add_rr_param = add_rr_param; rrb->check_route_param = check_route_param; rrb->is_direction = is_direction; rrb->get_route_param = get_route_param; rrb->register_rrcb = register_rrcb; rrb->get_remote_target = get_remote_target; rrb->get_route_set = get_route_set; rrb->loose_route = loose_route; rrb->record_route = record_route; rrb->append_fromtag = append_fromtag; return 1; } opensips-2.2.2/modules/rr/api.h000066400000000000000000000050731300170765700164050ustar00rootroot00000000000000/* * Copyright (C) 2005 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2005-08-02 first version (bogdan) */ /*! * \file * \brief RR module API * \ingroup rr */ #ifndef RR_API_H_ #define RR_API_H_ #include "../../str.h" #include "../../sr_module.h" #include "loose.h" #include "rr_cb.h" #define ROUTING_LL (1<<1) #define ROUTING_SL (1<<2) #define ROUTING_SS (1<<3) #define ROUTING_LS (1<<4) typedef int (*add_rr_param_t)(struct sip_msg*, str*); typedef int (*check_route_param_t)(struct sip_msg*, regex_t*); typedef int (*is_direction_t)(struct sip_msg*, int); typedef int (*get_route_param_t)(struct sip_msg*, str*, str*); typedef str* (*get_remote_target_t)(struct sip_msg*); typedef str* (*get_route_set_t)(struct sip_msg*,int *nr_routes); typedef int (*loose_route_t)(struct sip_msg*); typedef int (*record_route_t)(struct sip_msg*, str*); struct rr_binds { add_rr_param_t add_rr_param; check_route_param_t check_route_param; is_direction_t is_direction; get_route_param_t get_route_param; register_rrcb_t register_rrcb; get_remote_target_t get_remote_target; get_route_set_t get_route_set; loose_route_t loose_route; record_route_t record_route; int append_fromtag; }; typedef int (*load_rr_f)( struct rr_binds* ); /*! \brief * function exported by module - it will load the other functions */ int load_rr( struct rr_binds * ); /*! \brief * function to be called directly from other modules * to load the RR API */ inline static int load_rr_api( struct rr_binds *rrb ) { load_rr_f load_rr_v; /* import the RR auto-loading function */ if ( !(load_rr_v=(load_rr_f)find_export("load_rr", 0, 0))) { LM_ERR("failed to import load_rr\n"); return -1; } /* let the auto-loading function load all RR stuff */ load_rr_v( rrb ); return 0; } #endif opensips-2.2.2/modules/rr/doc/000077500000000000000000000000001300170765700162235ustar00rootroot00000000000000opensips-2.2.2/modules/rr/doc/rr.xml000066400000000000000000000027311300170765700173730ustar00rootroot00000000000000 %docentities; ]> rr Module &osipsname; Jan Janak &fhg;
jan@iptel.org
Bogdan-Andrei Iancu
bogdan@opensips.org
Jan Janak
jan@iptel.org
Bogdan-Andrei Iancu
bogdan@opensips.org
2003 &fhg; 2005 &voicesystem; $Revision: 8740 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/rr/doc/rr_admin.xml000066400000000000000000000323571300170765700205520ustar00rootroot00000000000000 &adminguide;
Overview The module contains record routing logic
Dialog support &osips; is basically only a transaction statefull proxy, without any dialog support build in. There are many features/services which actually require dialog awareness, like storing the information in the dialog creation stage, information which will be used during the whole dialog existence. The most urging example is NAT traversal, in dealing with the within the dialog INVITEs (re-INVITEs). When processing the initial INVITE, the proxy detects if the caller or callee is behind some NAT and fixes the signalling and media parts - since not all the detection mechanism are available for within the dialog requests (like usrloc), to be able to fix correspondingly the sequential requests, the proxy must remember that the original request was NAT processed. There are many other cases where dialog awareness fixes or helps. The solution is to store additional dialog-related information in the routing set (Record-Route/Route headers), headers which show up in all sequential requests. So any information added to the Record-Route header will be found (with no direction dependencies) in Route header (corresponding to the proxy address). As storage container, the parameters of the Record-Route / Route header will be used - Record-Route parameters mirroring are reinforced by RFC 3261 (see 12.1.1 UAS behavior). For this purpose, the modules offers the following functions: add_rr_param() - see check_route_param() - see Dialog support in RR module UAC OpenSIPS PROXY UAS ---- INVITE ------> record_route() ----- INVITE ----> add_rr_param(";foo=true") --- reINVITE -----> loose_route() ---- reINVITE ---> check_route_param(";foo=true") <-- reINVITE ------ loose_route() <--- reINVITE ---- check_route_param(";foo=true") <------ BYE ------- loose_route() <----- BYE ------- check_route_param(";foo=true")
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>append_fromtag</varname> (integer) If turned on, request's from-tag is appended to record-route; that's useful for understanding whether subsequent requests (such as BYE) come from caller (route's from-tag==BYE's from-tag) or callee (route's from-tag==BYE's to-tag) Default value is 1 (yes). Set <varname>append_fromtag</varname> parameter ... modparam("rr", "append_fromtag", 0) ...
<varname>enable_double_rr</varname> (integer) There are some situations when the server needs to insert two Record-Route header fields instead of one. For example when using two disconnected networks or doing cross-protocol forwarding from UDP->TCP. This parameter enables inserting of 2 Record-Routes. The server will later remove both of them. Default value is 1 (yes). Set <varname>enable_double_rr</varname> parameter ... modparam("rr", "enable_double_rr", 0) ...
<varname>add_username</varname> (integer) If set to a non 0 value (which means yes), the username part will be also added in the Record-Route URI. Default value is 0 (no). Set <varname>add_username</varname> parameter ... modparam("rr", "add_username", 1) ...
<varname>enable_socket_mismatch_warning</varname> (integer) When a preset record-route header is forced in &osips; config and the host from the record-route header is not the same as the host server, a warning will be printed out in the logs. The 'enable_socket_mismatch_warning' parameter enables or disables the warning. When &osips; is behind a NATed firewall, we don't want this warning to be printed for every bridged call. Default value is 1 (yes). <varname>enable_socket_mismatch_warning</varname> usage ... modparam("rr", "enable_socket_mismatch_warning", 0) ...
Exported Functions
<function moreinfo="none">loose_route()</function> The function performs routing of SIP requests which contain a route set. The name is a little bit confusing, as this function also routes requests which are in the strict router format. This function is usually used to route in-dialog requests (like ACK, BYE, reINVITE). Nevertheless also out-of-dialog requests can have a pre-loaded route set and my be routed with loose_route. It also takes care of translating between strict-routers and loose-router. The loose_route() function analyzes the Route headers in the requests. If there is no Route header, the function returns FALSE and routing should be done exclusivly via RURI. If a Route header is found, the function returns TRUE and behaves as described in section 16.12 of RFC 3261. The only exception is for requests with preload Route headers (intial requests, carrying a Route header): if there is only one Route header indicating the local proxy, then the Route header is removed and the function returns FALSE. If there is a Route header but other parsing errors occur ( like parsing the TO header to get the TAG ), the function also returns FALSE. Make sure your loose_routing function can't be used by attackers to bypass proxy authorization. The loose_routing topic is very complex. See the RFC3261 for more details (grep for route set is a good starting point in this comprehensive RFC). This function can be used from REQUEST_ROUTE. <function>loose_route</function> usage ... loose_route(); ...
<function moreinfo="none">record_route()</function> and <function moreinfo="none">record_route(string)</function> The function adds a new Record-Route header field. The header field will be inserted in the message before any other Record-Route header fields. If any string is passed as parameter, it will be appended as URI parameter to the Record-Route header. The string must follow the ;name=value scheme and it may contain pseudo-variables. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. <function>record_route</function> usage ... record_route(); ...
<function moreinfo="none">record_route_preset(string [, string2])</function> This function will put the string into Record-Route, don't use unless you know what you are doing. Meaning of the parameters is as follows: string - String to be inserted into the first header field; it may contain pseudo-variables. string2 - String to be inserted into the second header field; it may contain pseudo-variables. Note: If 'string2' is present, then the 'string' param is pointing to the outbound interface and the 'string2' param is pointing to the inbound interface. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. <function>record_route_preset</function> usage ... record_route_preset("1.2.3.4:5090"); ...
<function moreinfo="none">add_rr_param(param)</function> Adds a parameter to the Record-Route URI (param must be in ;name=value format. The function may be called also before or after the record_route() call (see ). Meaning of the parameters is as follows: param - String containing the URI parameter to be added. It must follow the ;name=value scheme; it may contain pseudo-variables. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and FAILURE_ROUTE. <function>add_rr_param</function> usage ... add_rr_param(";nat=yes"); ...
<function moreinfo="none">check_route_param(re)</function> The function checks if the URI parameters of the local Route header (corresponding to the local server) matches the given regular expression. It must be call after loose_route() (see ). Meaning of the parameters is as follows: re - regular expression to check against the Route URI parameters. This function can be used from REQUEST_ROUTE. <function>check_route_param</function> usage ... if (check_route_param("nat=yes")) { setflag(6); } ...
<function moreinfo="none">is_direction(dir)</function> The function checks the flow direction of the request. As for checking it's used the ftag Route header parameter, the append_fromtag (see module parameter must be enabled. Also this must be called only after loose_route() (see ). The function returns true if the dir is the same with the request's flow direction. The downstream (UAC to UAS) direction is relative to the initial request that created the dialog. Meaning of the parameters is as follows: dir - string containing the direction to be checked. It may be upstream (from UAS to UAC) or downstream (UAC to UAS). This function can be used from REQUEST_ROUTE. <function>is_direction</function> usage ... if (is_direction("upstream")) { xdbg("upstream request ($rm)\n"); } ...
Exported pseudo-variables Exported pseudo-variables are listed in the next sections.
$rr_params $rr_params - the whole string of the Route parameters - this is available only after calling loose_route()
opensips-2.2.2/modules/rr/doc/rr_devel.xml000066400000000000000000000137051300170765700205550ustar00rootroot00000000000000 &develguide; The RR module provides an internal API to be used by other &osips; modules. The API offers support for SIP dialog based functionalities - for more about the dialog support offered by RR module, see . For internal(non-script) usage, the RR module offers to other module the possibility to register callback functions to be executed each time a local Route header is processed. The callback function will receive as parameter the register parameter and the Route header parameter string.
Available Functions
<function moreinfo="none">add_rr_param( msg, param)</function> Adds a parameter to the requests's Record-Route URI (param must be in ;name=value format). The function returns 0 on success. Otherwise, -1 is returned. Meaning of the parameters is as follows: struct sip_msg* msg - request that will has the parameter param added to its Record-Route header. str* param - parameter to be added to the Record-Route header - it must be in ;name=value format.
<function moreinfo="none">check_route_param( msg, re)</function> The function checks for the request msg if the URI parameters of the local Route header (corresponding to the local server) matches the given regular expression re. It must be call after the loose_route was done. The function returns 0 on success. Otherwise, -1 is returned. Meaning of the parameters is as follows: struct sip_msg* msg - request that will has the Route header parameters checked. regex_t* param - compiled regular expression to be checked against the Route header parameters.
<function moreinfo="none">is_direction( msg, dir)</function> The function checks the flow direction of the request msg. As for checking it's used the ftag Route header parameter, the append_fromtag (see module parameter must be enables. Also this must be call only after the loose_route is done. The function returns 0 if the dir is the same with the request's flow direction. Otherwise, -1 is returned. Meaning of the parameters is as follows: struct sip_msg* msg - request that will have the direction checked. int dir - direction to be checked against. It may be RR_FLOW_UPSTREAM or RR_FLOW_DOWNSTREAM.
<function moreinfo="none">get_route_param( msg, name, val)</function> The function search in to the msg's Route header parameters the parameter called name and returns its value into val. It must be call only after the loose_route is done. The function returns 0 if parameter was found (even if it has no value). Otherwise, -1 is returned. Meaning of the parameters is as follows: struct sip_msg* msg - request that will have the Route header parameter searched. str *name - contains the Route header parameter to be serached. str *val - returns the value of the searched Route header parameter if found. It might be empty string if the parameter had no value.
<function moreinfo="none">register_rrcb( callback, param, prior)</function> The function register a new callback (along with its parameter). The callback will be called when a loose route will be performed for the local address. The function returns 0 on success. Otherwise, -1 is returned. Meaning of the parameters is as follows: rr_cb_t callback - callback function to be registered. void *param - parameter to be passed to the callback function. short prior - parameter to set the priority. If the callback depends on another module, this parameter should be greater than that module's priority. Otherwise, it should be 0.
Examples Loading RR module's API from another module ... #include "../rr/api.h" ... struct rr_binds my_rrb; ... ... /* load the RR API */ if (load_rr_api( &my_rrb )!=0) { LM_ERR("can't load RR API\n"); goto error; } ... ... /* register a RR callback */ if (my_rrb.register_rrcb(my_callback,0,0))!=0) { LM_ERR("can't register RR callback\n"); goto error; } ...
opensips-2.2.2/modules/rr/doc/rr_faq.xml000066400000000000000000000032601300170765700202200ustar00rootroot00000000000000 &faqguide; What happened with old enable_full_lr parameter The parameter is considered obsolete. It was only introduced to allow compatibility with older SIP entities, that complained about a lr parameter without a value. This behavior breaks RFC 3261, and since nowadays most SIP stacks are fixed to conform with the RFC, the parameter was removed. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/rr/loose.c000066400000000000000000000652041300170765700167520ustar00rootroot00000000000000/* * Route & Record-Route module, loose routing support * * Copyright (C) 2009-2014 OpenSIPS Solutions * Copyright (C) 2001-2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2005-04-10 check_route_param() and all hooks for keeping reference to * Route params added (bogdan) * 2005-10-17 fixed socket selection when double routing changes * the port or the IP address (bogdan) */ /*! * \file * \brief Route & Record-Route module, loose routing support * \ingroup rr */ #include #include "../../ut.h" #include "../../str.h" #include "../../dprint.h" #include "../../context.h" #include "../../forward.h" #include "../../data_lump.h" #include "../../socket_info.h" #include "../../parser/parse_rr.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../mem/mem.h" #include "../../dset.h" #include "loose.h" #include "rr_cb.h" #include "rr_mod.h" #include "api.h" #define RR_ERROR -1 /* An error occurred while processing route set */ #define RR_DRIVEN 1 /* The next hop is determined from the route set */ #define NOT_RR_DRIVEN -1 /* The next hop is not determined from the route set */ #define ROUTE_PREFIX "Route: <" #define ROUTE_PREFIX_LEN (sizeof(ROUTE_PREFIX)-1) #define ROUTE_SUFFIX ">\r\n" #define ROUTE_SUFFIX_LEN (sizeof(ROUTE_SUFFIX)-1) int ctx_rrparam_idx = -1; int ctx_routing_idx = -1; #define ctx_rrparam_get() \ context_get_str(CONTEXT_GLOBAL, current_processing_ctx, ctx_rrparam_idx) #define ctx_rrparam_set(_param) \ context_put_str(CONTEXT_GLOBAL, current_processing_ctx, ctx_rrparam_idx, _param) #define ctx_routing_get() \ context_get_int(CONTEXT_GLOBAL, current_processing_ctx, ctx_routing_idx) #define ctx_routing_set(_param) \ context_put_int(CONTEXT_GLOBAL, current_processing_ctx, ctx_routing_idx, _param) /* * Test whether we are processing pre-loaded route set * by looking at the To tag */ static int is_preloaded(struct sip_msg* msg) { str tag; if (!msg->to && parse_headers(msg, HDR_TO_F, 0) == -1) { LM_ERR("failed to parse To header field\n"); return -1; } if (!msg->to) { LM_ERR("To header field not found\n"); return -1; } tag = get_to(msg)->tag_value; if (tag.s == 0 || tag.len == 0) { LM_DBG("Yes\n"); return 1; } LM_DBG("No\n"); return 0; } /* * Parse the message and find first occurrence of * Route header field. The function returns -1 or -2 * on a parser error, 0 if there is a Route HF and * 1 if there is no Route HF. */ static inline int find_first_route(struct sip_msg* _m) { if (parse_headers(_m, HDR_ROUTE_F, 0) == -1) { LM_ERR("failed to parse headers\n"); return -1; } else { if (_m->route) { if (parse_rr(_m->route) < 0) { LM_ERR("failed to parse Route HF\n"); return -2; } return 0; } else { LM_DBG("No Route headers found\n"); return 1; } } } /* * Find out if a URI contains r2 parameter which indicates * that we put 2 record routes */ static inline int is_2rr(str* _params) { str s; int i, state = 0; if (_params->len == 0) return 0; s = *_params; for(i = 0; i < s.len; i++) { switch(state) { case 0: switch(s.s[i]) { case ' ': case '\r': case '\n': case '\t': break; case 'r': case 'R': state = 1; break; default: state = 4; break; } break; case 1: switch(s.s[i]) { case '2': state = 2; break; default: state = 4; break; } break; case 2: switch(s.s[i]) { case ';': return 1; case '=': return 1; case ' ': case '\r': case '\n': case '\t': state = 3; break; default: state = 4; break; } break; case 3: switch(s.s[i]) { case ';': return 1; case '=': return 1; case ' ': case '\r': case '\n': case '\t': break; default: state = 4; break; } break; case 4: switch(s.s[i]) { case '\"': state = 5; break; case ';': state = 0; break; default: break; } break; case 5: switch(s.s[i]) { case '\\': state = 6; break; case '\"': state = 4; break; default: break; } break; case 6: state = 5; break; } } if ((state == 2) || (state == 3)) return 1; else return 0; } /* * Check if URI is myself */ #ifdef ENABLE_USER_CHECK static inline int is_myself(str *_user, struct sip_uri* _uri) #else static inline int is_myself(struct sip_uri* _uri) #endif { int ret; unsigned short port; unsigned short proto; port = get_uri_port(_uri, &proto); ret = check_self(&_uri->host, port, proto); if (ret < 0) return 0; #ifdef ENABLE_USER_CHECK if(i_user.len && i_user.len==_user->len && !strncmp(i_user.s, _user->s, _user->len)) { LM_DBG("this URI isn't mine\n"); return -1; } #endif return ret; } /* * Find and parse next Route header field */ static inline int find_next_route(struct sip_msg* _m, struct hdr_field** _hdr) { struct hdr_field* ptr; ptr = (*_hdr)->next; /* Try to find already parsed Route headers */ while(ptr) { if (ptr->type == HDR_ROUTE_T) goto found; ptr = ptr->next; } /* There are no already parsed Route headers, try to find next * occurrence of Route header */ if (parse_headers(_m, HDR_ROUTE_F, 1) == -1) { LM_ERR("failed to parse headers\n"); return -1; } if ((_m->last_header->type!=HDR_ROUTE_T) || (_m->last_header==*_hdr)) { LM_DBG("No next Route HF found\n"); return 1; } ptr = _m->last_header; found: if (parse_rr(ptr) < 0) { LM_ERR("failed to parse Route body\n"); return -2; } *_hdr = ptr; return 0; } /* * Put Request-URI as last Route header of a SIP * message, this is necessary when forwarding to * a strict router */ static inline int save_ruri(struct sip_msg* _m) { struct lump* anchor; char *s; int len; /* We must parse the whole message header here, * because Request-URI must be saved in last * Route HF in the message */ if (parse_headers(_m, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse message\n"); return -1; } /* Create an anchor */ anchor = anchor_lump(_m, _m->unparsed - _m->buf, 0); if (anchor == 0) { LM_ERR("failed to get anchor\n"); return -2; } /* Create buffer for new lump */ len = ROUTE_PREFIX_LEN + _m->first_line.u.request.uri.len + ROUTE_SUFFIX_LEN; s = (char*)pkg_malloc(len); if (!s) { LM_ERR("No memory pkg left\n"); return -3; } /* Create new header field */ memcpy(s, ROUTE_PREFIX, ROUTE_PREFIX_LEN); memcpy(s + ROUTE_PREFIX_LEN, _m->first_line.u.request.uri.s, _m->first_line.u.request.uri.len); memcpy(s + ROUTE_PREFIX_LEN + _m->first_line.u.request.uri.len, ROUTE_SUFFIX, ROUTE_SUFFIX_LEN); LM_DBG("New header: '%.*s'\n", len, ZSW(s)); /* Insert it */ if (insert_new_lump_before(anchor, s, len, 0) == 0) { pkg_free(s); LM_ERR("failed to insert lump\n"); return -4; } return 0; } /* * input: uri - uri to be checked if has maddr * input: puri - parsed uri * outpu: uri - the uri to be used further */ #define RH_MADDR_PARAM_MAX_LEN 127 /* The max length of the maddr uri*/ static inline int get_maddr_uri(str *uri, struct sip_uri *puri) { static char builturi[RH_MADDR_PARAM_MAX_LEN+1]; struct sip_uri turi; if(uri==NULL || uri->s==NULL) return RR_ERROR; if(puri==NULL) { if (parse_uri(uri->s, uri->len, &turi) < 0) { LM_ERR("failed to parse the URI\n"); return RR_ERROR; } puri = &turi; } if(puri->maddr.s==NULL) return 0; /* sip: + maddr + : + port */ if( (puri->maddr_val.len) > ( RH_MADDR_PARAM_MAX_LEN - 10 ) ) { LM_ERR( "Too long maddr parameter\n"); return RR_ERROR; } memcpy( builturi, "sip:", 4 ); memcpy( builturi+4, puri->maddr_val.s, puri->maddr_val.len ); if( puri->port.len > 0 ) { builturi[4+puri->maddr_val.len] =':'; memcpy(builturi+5+puri->maddr_val.len, puri->port.s, puri->port.len); } uri->len = 4+puri->maddr_val.len + ((puri->port.len>0)?(1+puri->port.len):0); builturi[uri->len]='\0'; uri->s = builturi; LM_DBG("uri is %s\n", builturi ); return 0; } /* * Logic necessary to forward request to strict routers * * Returns 0 on success, negative number on an error */ static inline int handle_sr(struct sip_msg* _m, struct hdr_field* _hdr, rr_t* _r) { str uri; char* rem_off; int rem_len; /* Next hop is strict router, save R-URI here */ if (save_ruri(_m) < 0) { LM_ERR("failed to save Request-URI\n"); return -1; } /* Put the first Route in Request-URI */ uri = _r->nameaddr.uri; if(get_maddr_uri(&uri, 0)!=0) { LM_ERR("failed to check maddr\n"); return RR_ERROR; } if (set_ruri(_m, &uri) < 0) { LM_ERR("failed to rewrite request URI\n"); return -2; } if (!_r->next) { rem_off = _hdr->name.s; rem_len = _hdr->len; } else { rem_off = _hdr->body.s; rem_len = _r->next->nameaddr.name.s - _hdr->body.s; } if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) { LM_ERR("failed to remove Route HF\n"); return -9; } _r->deleted = 1; return 0; } /* * Find last route in the last Route header field, * if there was a previous route in the last Route header * field, it will be saved in _p parameter */ static inline int find_rem_target(struct sip_msg* _m, struct hdr_field** _h, rr_t** _l, rr_t** _p) { struct hdr_field* ptr, *last; if (parse_headers(_m, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse message header\n"); return -1; } ptr = _m->route; last = 0; while(ptr) { if (ptr->type == HDR_ROUTE_T) last = ptr; ptr = ptr->next; } if (last) { if (parse_rr(last) < 0) { LM_ERR("failed to parse last Route HF\n"); return -2; } *_p = 0; *_l = (rr_t*)last->parsed; *_h = last; while ((*_l)->next) { *_p = *_l; *_l = (*_l)->next; } return 0; } else { LM_ERR("search for last Route HF failed\n"); return 1; } } /* * Previous hop was a strict router, handle this case */ static inline int after_strict(struct sip_msg* _m) { int res, rem_len; struct hdr_field* hdr; struct sip_uri puri; rr_t* rt, *prev, *del_rt; char* rem_off; str uri; struct socket_info *si; unsigned short port, proto; hdr = _m->route; rt = (rr_t*)hdr->parsed; uri = rt->nameaddr.uri; del_rt = NULL; if (parse_uri(uri.s, uri.len, &puri) < 0) { LM_ERR("failed to parse the first route URI\n"); return RR_ERROR; } if ( enable_double_rr && is_2rr(&puri.params) && #ifdef ENABLE_USER_CHECK is_myself(&puri.user, &puri) #else is_myself(&puri) #endif ) { /* double route may occure due different IP and port, so force as * send interface the one advertise in second Route */ set_sip_defaults( puri.port_no, puri.proto); si = grep_sock_info( &puri.host, puri.port_no, puri.proto); if (si) { _m->force_send_socket = si; } else { if (enable_socket_mismatch_warning) LM_WARN("no socket found to match 2nd RR [%d][%.*s:%d]\n", puri.proto, puri.host.len, puri.host.s, puri.port_no); } /* mark route hdr as deleted */ rt->deleted = 1; if (!rt->next) { /* No next route in the same header, remove the whole header * field immediately */ if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) { LM_ERR("failed to remove Route HF\n"); return RR_ERROR; } res = find_next_route(_m, &hdr); if (res < 0) { LM_ERR("searching next route failed\n"); return RR_ERROR; } if (res > 0) { /* No next route found */ LM_DBG("No next URI found\n"); return NOT_RR_DRIVEN; } rt = (rr_t*)hdr->parsed; } else rt = rt->next; /* parse the new found uri */ uri = rt->nameaddr.uri; if (parse_uri(uri.s, uri.len, &puri) < 0) { LM_ERR("failed to parse URI\n"); return RR_ERROR; } } else { port = _m->parsed_uri.port_no; proto = _m->parsed_uri.proto; set_sip_defaults(port, proto); si = grep_sock_info( &_m->parsed_uri.host, port, proto); if (si) { _m->force_send_socket = si; } else { if (enable_socket_mismatch_warning) LM_WARN("no socket found to match RR [%d][%.*s:%d]\n", proto, _m->parsed_uri.host.len, _m->parsed_uri.host.s, port); } } /* set the hooks for the params -bogdan * important note: RURI is already parsed by the above function, so * we just used it without any checking */ ctx_rrparam_set( &_m->parsed_uri.params ); if (is_strict(&puri.params)) { LM_DBG("Next hop: '%.*s' is strict router\n", uri.len, ZSW(uri.s)); ctx_routing_set( ROUTING_SS ); /* Previous hop was a strict router and the next hop is strict * router too. There is no need to save R-URI again because it * is saved already. In fact, in this case we will behave exactly * like a strict router. */ /* Note: when there is only one Route URI left (endpoint), it will * always be a strict router because endpoints don't use ;lr parameter * In this case we will simply put the URI in R-URI and forward it, * which will work perfectly */ if(get_maddr_uri(&uri, &puri)!=0) { LM_ERR("failed to check maddr\n"); return RR_ERROR; } if (set_ruri(_m, &uri) < 0) { LM_ERR("failed to rewrite request URI\n"); return RR_ERROR; } if (rt->next) { rem_off = hdr->body.s; rem_len = rt->next->nameaddr.name.s - hdr->body.s; } else { rem_off = hdr->name.s; rem_len = hdr->len; } if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) { LM_ERR("failed to remove Route HF\n"); return RR_ERROR; } rt->deleted = 1; } else { LM_DBG("Next hop: '%.*s' is loose router\n", uri.len, ZSW(uri.s)); ctx_routing_set( ROUTING_SL ); if(get_maddr_uri(&uri, &puri)!=0) { LM_ERR("failed to check maddr\n"); return RR_ERROR; } if (set_dst_uri(_m, &uri) < 0) { LM_ERR("failed to set dst_uri\n"); return RR_ERROR; } /* Next hop is a loose router - Which means that is is not endpoint yet * In This case we have to recover from previous strict routing, that * means we have to find the last Route URI and put in in R-URI and * remove the last Route URI. */ if (rt != hdr->parsed) { /* There is a previous route uri which was 2nd uri of mine * and must be removed here */ rem_off = hdr->body.s; rem_len = rt->nameaddr.name.s - hdr->body.s; if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) { LM_ERR("failed to remove Route HF\n"); return RR_ERROR; } del_rt = (rr_t*)hdr->parsed; /* mark route hdr as deleted */ del_rt->deleted = 1; } res = find_rem_target(_m, &hdr, &rt, &prev); if (res < 0) { LM_ERR("searching for last Route URI failed\n"); return RR_ERROR; } else if (res > 0) { /* No remote target is an error */ return RR_ERROR; } uri = rt->nameaddr.uri; if(get_maddr_uri(&uri, 0)!=0) { LM_ERR("checking maddr failed\n"); return RR_ERROR; } if (set_ruri(_m, &uri) < 0) { LM_ERR("failed to rewrite R-URI\n"); return RR_ERROR; } /* mark remote contact route as deleted */ rt->deleted = 1; /* The first character if uri will be either '<' when it is the * only URI in a Route header field or ',' if there is more than * one URI in the header field */ LM_DBG("The last route URI: '%.*s'\n", rt->nameaddr.uri.len, ZSW(rt->nameaddr.uri.s)); if (prev && prev!=del_rt) { rem_off = prev->nameaddr.name.s + prev->len; rem_len = rt->nameaddr.name.s + rt->len - rem_off; } else { rem_off = hdr->name.s; rem_len = hdr->len; } if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) { LM_ERR("failed to remove Route HF\n"); return RR_ERROR; } } /* run RR callbacks -bogdan */ run_rr_callbacks( _m, ctx_rrparam_get() ); return RR_DRIVEN; } static inline int after_loose(struct sip_msg* _m, int preloaded) { struct hdr_field* hdr; struct sip_uri puri; struct sip_uri puri2; rr_t* rt; int res; int status; #ifdef ENABLE_USER_CHECK int ret; #endif str uri; struct socket_info *si; int force_ss = 0; hdr = _m->route; rt = (rr_t*)hdr->parsed; uri = rt->nameaddr.uri; if (parse_uri(uri.s, uri.len, &puri) < 0) { LM_ERR("failed to parse the first route URI\n"); return RR_ERROR; } /* IF the URI was added by me, remove it */ #ifdef ENABLE_USER_CHECK ret=is_myself(&puri.user, &puri); if (ret>0) #else if (is_myself(&puri)) #endif { LM_DBG("Topmost route URI: '%.*s' is me\n", uri.len, ZSW(uri.s)); /* set the hooks for the params -bogdan */ ctx_rrparam_set( &puri.params ); /* if last route in header, gonna get del_lumped now, * if not, it will be taken care of later * Mark it now as deleted */ rt->deleted = 1; if (!rt->next) { /* No next route in the same header, remove the whole header * field immediately */ if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) { LM_ERR("failed to remove Route HF\n"); return RR_ERROR; } rt->deleted = 1; res = find_next_route(_m, &hdr); if (res < 0) { LM_ERR("failed to find next route\n"); return RR_ERROR; } if (res > 0) { /* No next route found */ LM_DBG("No next URI found!\n"); status = (preloaded ? NOT_RR_DRIVEN : RR_DRIVEN); /*same case as LL , if there is no next route*/ ctx_routing_set( ROUTING_LL ); force_ss = 1; goto done; } rt = (rr_t*)hdr->parsed; } else rt = rt->next; if (enable_double_rr && is_2rr(&puri.params)) { force_ss = 0; /* double route may occure due different IP and port, so force as * send interface the one advertise in second Route */ if (parse_uri(rt->nameaddr.uri.s,rt->nameaddr.uri.len,&puri)<0) { LM_ERR("failed to parse the double route URI\n"); return RR_ERROR; } set_sip_defaults( puri.port_no, puri.proto); si = grep_sock_info( &puri.host, puri.port_no, puri.proto); if (si) { _m->force_send_socket = si; } else { if (enable_socket_mismatch_warning) LM_WARN("no socket found to match 2nd RR [%d][%.*s:%d]\n", puri.proto, puri.host.len, puri.host.s, puri.port_no); } rt->deleted = 1; if (!rt->next) { /* No next route in the same header, remove the whole header * field immediately */ if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) { LM_ERR("failed to remove Route HF\n"); return RR_ERROR; } res = find_next_route(_m, &hdr); if (res < 0) { LM_ERR("failed to find next route\n"); return RR_ERROR; } if (res > 0) { /* No next route found */ LM_DBG("no next URI found\n"); status = (preloaded ? NOT_RR_DRIVEN : RR_DRIVEN); /* same case as LL , if there is no next route */ ctx_routing_set( ROUTING_LL ); goto done; } rt = (rr_t*)hdr->parsed; } else rt = rt->next; } else { force_ss = 1; } uri = rt->nameaddr.uri; if (parse_uri(uri.s, uri.len, &puri2) < 0) { LM_ERR("failed to parse the first route URI\n"); return RR_ERROR; } } else { #ifdef ENABLE_USER_CHECK /* check if it the ignored user */ if(ret < 0) return NOT_RR_DRIVEN; #endif LM_DBG("Topmost URI is NOT myself\n"); memcpy(&puri2, &puri, sizeof(struct sip_uri)); } LM_DBG("URI to be processed: '%.*s'\n", uri.len, ZSW(uri.s)); if (is_strict(&puri2.params)) { LM_DBG("Next URI is a strict router\n"); ctx_routing_set( ROUTING_LS ); if (handle_sr(_m, hdr, rt) < 0) { LM_ERR("failed to handle strict router\n"); return RR_ERROR; } } else { /* Next hop is loose router */ LM_DBG("Next URI is a loose router\n"); ctx_routing_set( ROUTING_LL ); if(get_maddr_uri(&uri, &puri2)!=0) { LM_ERR("checking maddr failed\n"); return RR_ERROR; } if (set_dst_uri(_m, &uri) < 0) { LM_ERR("failed to set dst_uri\n"); return RR_ERROR; } /* There is a previous route uri which was 2nd uri of mine * and must be removed here */ if (rt != hdr->parsed) { if (!del_lump(_m, hdr->body.s - _m->buf, rt->nameaddr.name.s - hdr->body.s, 0)) { LM_ERR("failed to remove Route HF\n"); return RR_ERROR; } ((rr_t *)hdr->parsed)->deleted = 1; } } status = RR_DRIVEN; done: if (force_ss && !preloaded) _m->force_send_socket = _m->rcv.bind_address; /* run RR callbacks -bogdan */ run_rr_callbacks( _m, &puri.params ); return status; } /* * Do loose routing as defined in RFC3261 */ int loose_route(struct sip_msg* _m) { int ret; ctx_routing_set(0); if (find_first_route(_m) != 0) { LM_DBG("There is no Route HF\n"); return -1; } if (parse_sip_msg_uri(_m)<0) { LM_ERR("failed to parse Request URI\n"); return -1; } ret = is_preloaded(_m); if (ret < 0) { return -1; } else if (ret == 1) { return after_loose(_m, 1); } else { #ifdef ENABLE_USER_CHECK if (is_myself(&_m->parsed_uri.user, &_m->parsed_uri) && !(_m->parsed_uri.gr.s && _m->parsed_uri.gr.len)) { #else if (is_myself(&_m->parsed_uri) && !(_m->parsed_uri.gr.s && _m->parsed_uri.gr.len)) { #endif return after_strict(_m); } else { return after_loose(_m, 0); } } } int get_route_params(struct sip_msg *msg, str *val) { if(msg==NULL) return -1; /* check if params are present */ *val = *ctx_rrparam_get(); if (val->s==NULL || val->len==0) return -1; return 0; } int check_route_param(struct sip_msg * msg, regex_t* re) { regmatch_t pmatch; char bk; str params; str *rparams; /* check if params are present */ rparams = ctx_rrparam_get(); if (rparams->s==NULL || rparams->len==0) return -1; /* include also the first ';' */ for( params=*rparams ; params.s[0]!=';' ; params.s--,params.len++ ); /* do the well-known trick to convert to null terminted */ bk = params.s[params.len]; params.s[params.len] = 0; LM_DBG("params are <%s>\n", params.s); if (regexec( re, params.s, 1, &pmatch, 0)!=0) { params.s[params.len] = bk; return -1; } else { params.s[params.len] = bk; return 0; } } int get_route_param( struct sip_msg *msg, str *name, str *val) { char *p; char *end; char c; int quoted; str *rparams; /* check if params are present */ rparams = ctx_rrparam_get(); if (rparams->s==NULL || rparams->len==0) goto notfound; end = rparams->s + rparams->len; p = rparams->s; /* parse the parameters string and find the "name" param */ while ( end-p>name->len+2 ) { if (p!=rparams->s) { /* go to first ';' char */ for( quoted=0 ; plen+2 ) goto notfound; if ( memcmp(p,name->s,name->len)!=0 ) { p++; continue; } p+=name->len; while( plen = 0; val->s = 0; goto found; } if (*(p++)!='=') continue; while( ps = ++p ; ps=p ; plen = p-val->s; if (val->len==0) val->s = 0; goto found; } notfound: return -1; found: return 0; } int is_direction(struct sip_msg * msg, int dir) { static str ftag_param = {"ftag",4}; static unsigned int last_id = (unsigned int)-1; static unsigned int last_dir = 0; str ftag_val; str tag; if ( last_id==msg->id && last_dir!=0) { if (last_dir==RR_FLOW_UPSTREAM) goto upstream; else goto downstream; } ftag_val.s = 0; ftag_val.len = 0; if (get_route_param( msg, &ftag_param, &ftag_val)!=0) { LM_DBG("param ftag not found\n"); goto downstream; } if ( ftag_val.s==0 || ftag_val.len==0 ) { LM_DBG("param ftag has empty val\n"); goto downstream; } /* get the tag value from FROM hdr */ if ( parse_from_header(msg)!=0 ) goto downstream; tag = ((struct to_body*)msg->from->parsed)->tag_value; if (tag.s==0 || tag.len==0) goto downstream; /* compare the 2 strings */ if (tag.len!=ftag_val.len || memcmp(tag.s,ftag_val.s,ftag_val.len)) goto upstream; downstream: last_id = msg->id; last_dir = RR_FLOW_DOWNSTREAM; return (dir==RR_FLOW_DOWNSTREAM)?0:-1; upstream: last_id = msg->id; last_dir = RR_FLOW_UPSTREAM; return (dir==RR_FLOW_UPSTREAM)?0:-1; } str* get_remote_target(struct sip_msg *msg) { int res; struct hdr_field *hdr; rr_t *rt,*prev; str *uri; int routing_type; if (msg == NULL) { LM_ERR("null sip msg\n"); return 0; } routing_type = ctx_routing_get(); if ((routing_type == ROUTING_LL) || (routing_type == ROUTING_LS)) return &msg->first_line.u.request.uri; else if (routing_type == ROUTING_SL) /* set by loose_route(), recovered from previous strict routing */ return &msg->new_uri; else if (routing_type == ROUTING_SS) { /* searching for last header field */ res = find_rem_target(msg, &hdr, &rt, &prev); if (res < 0) { LM_ERR("searching for last Route URI failed\n"); return 0; } else if (res > 0) { /* No remote target is an error */ LM_ERR("couldn't find any remote target !\n"); return 0; } uri = &rt->nameaddr.uri; if(get_maddr_uri(uri, 0)!=0) { LM_ERR("failed to check maddr\n"); return 0; } return uri; } else { LM_ERR("Invalid routing type - %d\n",routing_type); return 0; } } #define MAX_RR_HDRS 64 str* get_route_set(struct sip_msg *msg,int *nr_routes) { static str uris[MAX_RR_HDRS]; struct hdr_field *it; rr_t *p; int n = 0; int routing_type; if (msg == NULL || msg->route == NULL) { LM_ERR("null sip msg or no route headers\n"); return 0; } routing_type = ctx_routing_get(); if (routing_type == ROUTING_SS || routing_type == ROUTING_LS) { /* must manually insert RURI, as it was part * of the route deleted to make up for strict routing */ uris[n++] = msg->new_uri; } it = msg->route; while (it != NULL) { if (parse_rr(it) < 0) { LM_ERR("failed to parse RR\n"); return 0; } p = (rr_t*)it->parsed; while (p) { if (p->deleted == 0) { uris[n++] = p->nameaddr.uri; if(n==MAX_RR_HDRS) { LM_ERR("too many RR\n"); return 0; } } else LM_DBG("Route [%.*s] has been deleted\n",p->nameaddr.uri.len, p->nameaddr.uri.s); p = p->next; } it = it->sibling; } /* if SS - remove last route */ if (routing_type == ROUTING_SS) n--; if (nr_routes) *nr_routes = n; return uris; } opensips-2.2.2/modules/rr/loose.h000066400000000000000000000064431300170765700167570ustar00rootroot00000000000000/* * Route & Record-Route module, loose routing support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Route & Record-Route module, loose routing support * \ingroup rr */ #ifndef LOOSE_H #define LOOSE_H #include #include "../../str.h" #include "../../parser/msg_parser.h" #define RR_FLOW_DOWNSTREAM (1<<0) #define RR_FLOW_UPSTREAM (1<<1) extern int ctx_rrparam_idx; extern int ctx_routing_idx; /*! \brief * Do loose routing as per RFC3261 */ int loose_route(struct sip_msg* _m); /*! \brief * Check if the our route hdr has required params */ int check_route_param(struct sip_msg *msg, regex_t* re); /*! \brief * Check the direction of the message */ int is_direction(struct sip_msg *msg, int dir); /*! \brief * Gets the value of a route parameter */ int get_route_param( struct sip_msg *msg, str *name, str *val); /*! \brief * Gets all route params as a string */ int get_route_params(struct sip_msg *msg, str *val); /* returns the remote contact, based on the routing decisions made */ str* get_remote_target(struct sip_msg *msg); /* returns an array of the URIs in all Route Headers */ str* get_route_set(struct sip_msg *msg,int *nr_routes); /* * Check if the given uri contains lr parameter which marks loose routers */ static inline int is_strict(str* _params) { str s; int i, state = 0; if (_params->len == 0) return 1; s.s = _params->s; s.len = _params->len; for(i = 0; i < s.len; i++) { switch(state) { case 0: switch(s.s[i]) { case ' ': case '\r': case '\n': case '\t': break; case 'l': case 'L': state = 1; break; default: state = 4; break; } break; case 1: switch(s.s[i]) { case 'r': case 'R': state = 2; break; default: state = 4; break; } break; case 2: switch(s.s[i]) { case ';': return 0; case '=': return 0; case ' ': case '\r': case '\n': case '\t': state = 3; break; default: state = 4; break; } break; case 3: switch(s.s[i]) { case ';': return 0; case '=': return 0; case ' ': case '\r': case '\n': case '\t': break; default: state = 4; break; } break; case 4: switch(s.s[i]) { case '\"': state = 5; break; case ';': state = 0; break; default: break; } break; case 5: switch(s.s[i]) { case '\\': state = 6; break; case '\"': state = 4; break; default: break; } break; case 6: state = 5; break; } } if ((state == 2) || (state == 3)) return 0; else return 1; } #endif /* LOOSE_H */ opensips-2.2.2/modules/rr/record.c000066400000000000000000000326401300170765700171050ustar00rootroot00000000000000/* * Route & Record-Route module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-04-04 Extracted from common.[ch] (janakj) * 2005-04-10 add_rr_param() function and all corresponing hooks added (bogdan) * 2006-02-14 record_route may take as param a string to be used as RR param; * record_route and record_route_preset accept pseudo-variables in * parameters; add_rr_param may be called from BRANCH and FAILURE * routes (bogdan) */ /*! * \file * \brief Route & Record-Route module, loose routing support * \ingroup rr */ /*! * \defgroup rr Route & Record-Route Module */ #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../str.h" #include "../../data_lump.h" #include "record.h" #include "rr_mod.h" #define RR_PREFIX "Record-Route: "CRLF #define RR_TERM_LEN (sizeof(RR_TERM)-1) #define INBOUND 1 /* Insert inbound Record-Route */ #define OUTBOUND 0 /* Insert outbound Record-Route */ #define RR_PARAM_BUF_SIZE 512 /*! \brief * Extract username from the Request URI * First try to look at the original Request URI and if there * is no username use the new Request URI */ static inline int get_username(struct sip_msg* _m, str* _user) { struct sip_uri puri; /* first try to look at r-uri for a username */ if (parse_uri(_m->first_line.u.request.uri.s, _m->first_line.u.request.uri.len, &puri) < 0) { LM_ERR("failed to parse R-URI\n"); return -1; } /* no username in original uri -- hmm; maybe it is a uri * with just host address and username is in a preloaded route, * which is now no rewritten r-uri (assumed rewriteFromRoute * was called somewhere in script's beginning) */ if (!puri.user.len && _m->new_uri.s) { if (parse_uri(_m->new_uri.s, _m->new_uri.len, &puri) < 0) { LM_ERR("failed to parse new_uri\n"); return -2; } } _user->s = puri.user.s; _user->len = puri.user.len; return 0; } static inline struct lump *insert_rr_param_lump(struct lump *before, char *s, int l) { struct lump *rrp_l; char *s1; /* duplicate data in pkg mem */ s1 = (char*)pkg_malloc(l); if (s1==0) { LM_ERR("no more pkg mem (%d)\n",l); return 0; } memcpy( s1, s, l); /* add lump */ rrp_l = insert_new_lump_before( before, s1, l, 0); if (rrp_l==0) { LM_ERR("failed to add before lump\n"); pkg_free(s1); return 0; } return rrp_l; } /*! \brief * build a Record-Route header field */ static inline int build_rr(struct lump* _l, struct lump* _l2, str* user, str *tag, str *params, struct lump *lp, int _inbound) { char* prefix, *suffix, *term, *r2; int suffix_len, prefix_len; char *p; prefix_len = RR_PREFIX_LEN + (user->len ? (user->len + 1) : 0); suffix_len = RR_LR_LEN + (params?params->len:0) + ((tag && tag->len) ? (RR_FROMTAG_LEN + tag->len) : 0); prefix = pkg_malloc(prefix_len); suffix = pkg_malloc(suffix_len); term = pkg_malloc(RR_TERM_LEN); r2 = pkg_malloc(RR_R2_LEN); if (!prefix || !suffix || !term || !r2) { LM_ERR("No more pkg memory\n"); if (suffix) pkg_free(suffix); if (prefix) pkg_free(prefix); if (term) pkg_free(term); if (r2) pkg_free(r2); return -3; } memcpy(prefix, RR_PREFIX, RR_PREFIX_LEN); if (user->len) { memcpy(prefix + RR_PREFIX_LEN, user->s, user->len); #ifdef ENABLE_USER_CHECK /* don't add the ignored user into a RR */ if(i_user.len && i_user.len == user->len && !strncmp(i_user.s, user->s, i_user.len)) { if(prefix[RR_PREFIX_LEN]=='x') prefix[RR_PREFIX_LEN]='y'; else prefix[RR_PREFIX_LEN]='x'; } #endif prefix[RR_PREFIX_LEN + user->len] = '@'; } p = suffix; memcpy( p, RR_LR, RR_LR_LEN); p += RR_LR_LEN; if (tag && tag->len) { memcpy(p, RR_FROMTAG, RR_FROMTAG_LEN); p += RR_FROMTAG_LEN; memcpy(p, tag->s, tag->len); p += tag->len; } if (params && params->len) { memcpy(p, params->s, params->len); p += params->len; } memcpy(term, RR_TERM, RR_TERM_LEN); memcpy(r2, RR_R2, RR_R2_LEN); if (!(_l = insert_new_lump_after(_l, prefix, prefix_len, 0))) goto lump_err; prefix = 0; _l = insert_subst_lump_after(_l, _inbound?SUBST_RCV_ALL:SUBST_SND_ALL, 0); if (_l ==0 ) goto lump_err; if (enable_double_rr) { if (!(_l = insert_cond_lump_after(_l, COND_IF_DIFF_REALMS, 0))) goto lump_err; if (!(_l = insert_new_lump_after(_l, r2, RR_R2_LEN, 0))) goto lump_err; r2 = 0; } else { pkg_free(r2); r2 = 0; } _l2 = insert_new_lump_before(_l2, suffix, suffix_len, 0); if (_l2 == 0) goto lump_err; suffix = 0; if ( lp ) { /* link the pending buffered params and go at the end of the list */ for ( _l2->before = lp ; _l2 && _l2->before ; _l2=_l2->before); } if (!(_l2 = insert_new_lump_before(_l2, term, RR_TERM_LEN, 0))) goto lump_err; term = 0; return 0; lump_err: LM_ERR("failed to insert lumps\n"); if (prefix) pkg_free(prefix); if (suffix) pkg_free(suffix); if (r2) pkg_free(r2); if (term) pkg_free(term); return -4; } /*! \brief * Insert a new Record-Route header field * And also 2nd one if it is enabled and realm changed so * the 2nd record-route header will be necessary */ int record_route(struct sip_msg* _m, str *params) { struct lump* l, *l2, *lp, *lp2, *ap; str user; struct to_body* from; str* tag; from = 0; /* Makes gcc happy */ user.len = 0; lp = lp2 = NULL; if (add_username) { if (get_username(_m, &user) < 0) { LM_ERR("failed to extract username\n"); return -1; } } if (append_fromtag) { if (parse_from_header(_m) < 0) { LM_ERR("From parsing failed\n"); return -2; } from = (struct to_body*)_m->from->parsed; tag = &from->tag_value; } else { tag = 0; } l = anchor_lump(_m, _m->headers->name.s - _m->buf, HDR_RECORDROUTE_T); l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, HDR_RECORDROUTE_T); if (!l || !l2) { LM_ERR("failed to create an anchor\n"); return -3; } /* look for pending RR params */ for( lp2=NULL,lp=NULL,ap=_m->add_rm ; ap ; ap=ap->next ) { if (ap->type==HDR_RECORDROUTE_T && ap->op==LUMP_NOP && ap->before && ap->before->op==LUMP_ADD_OPT && ap->before->u.cond==COND_FALSE) { /* found our phony anchor lump */ /* jump over the anchor and conditional lumps */ lp = ap->before->before; /* unlink it */ ap->before->before = NULL; ap->type = 0; /* if double routing, make a copy of the buffered lumps for the second route hdr. */ if (enable_double_rr) lp2 = dup_lump_list(lp); break; } } if (build_rr(l, l2, &user, tag, params, lp, OUTBOUND) < 0) { LM_ERR("failed to insert inbound Record-Route\n"); return -4; } if (enable_double_rr) { l = anchor_lump(_m, _m->headers->name.s - _m->buf,HDR_RECORDROUTE_T); l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, HDR_RECORDROUTE_T); if (!l || !l2) { LM_ERR("failed to create an anchor\n"); return -5; } l = insert_cond_lump_after(l, COND_IF_DIFF_REALMS, 0); l2 = insert_cond_lump_before(l2, COND_IF_DIFF_REALMS, 0); if (!l || !l2) { LM_ERR("failed to insert conditional lump\n"); return -6; } if (build_rr(l, l2, &user, tag, params, lp2, INBOUND) < 0) { LM_ERR("failed to insert outbound Record-Route\n"); return -7; } } return 0; } /*! \brief * Insert manually created Record-Route header, no checks, no restrictions, * always adds lr parameter, only fromtag is added automatically when requested */ int record_route_preset(struct sip_msg* _m, str* _data) { str user; struct to_body* from; struct lump* l, *lp, *ap; struct lump* l2; char *hdr, *suffix, *p, *term; int hdr_len, suffix_len; from = 0; user.len = 0; user.s = 0; if (add_username) { if (get_username(_m, &user) < 0) { LM_ERR("failed to extract username\n"); return -1; } } if (append_fromtag) { if (parse_from_header(_m) < 0) { LM_ERR("From parsing failed\n"); return -2; } from = (struct to_body*)_m->from->parsed; } hdr_len = RR_PREFIX_LEN; if (user.len) hdr_len += user.len + 1; /* @ */ hdr_len += _data->len; suffix_len = 0; if (append_fromtag && from->tag_value.len) { suffix_len += RR_FROMTAG_LEN + from->tag_value.len; } suffix_len += RR_LR_LEN; hdr = pkg_malloc(hdr_len); term = pkg_malloc(RR_TERM_LEN); suffix = pkg_malloc(suffix_len); if (!hdr || !term || !suffix) { LM_ERR("no pkg memory left\n"); return -4; } /* header */ p = hdr; memcpy(p, RR_PREFIX, RR_PREFIX_LEN); p += RR_PREFIX_LEN; if (user.len) { memcpy(p, user.s, user.len); p += user.len; *p = '@'; p++; } memcpy(p, _data->s, _data->len); p += _data->len; /*suffix*/ p = suffix; if (append_fromtag && from->tag_value.len) { memcpy(p, RR_FROMTAG, RR_FROMTAG_LEN); p += RR_FROMTAG_LEN; memcpy(p, from->tag_value.s, from->tag_value.len); p += from->tag_value.len; } memcpy(p, RR_LR, RR_LR_LEN); p += RR_LR_LEN; memcpy(term, RR_TERM, RR_TERM_LEN); l = anchor_lump(_m, _m->headers->name.s - _m->buf, HDR_RECORDROUTE_T); l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, HDR_RECORDROUTE_T); if (!l || !l2) { LM_ERR("failed to create lump anchor\n"); goto error; } if (!(l=insert_new_lump_after(l, hdr, hdr_len, 0))) { LM_ERR("failed to insert new lump\n"); goto error; } hdr = NULL; l2 = insert_new_lump_before(l2, suffix, suffix_len, HDR_RECORDROUTE_T); if (l2==NULL) { LM_ERR("failed to insert suffix lump\n"); goto error; } suffix = NULL; /* look for pending RR params */ for( lp=NULL,ap=_m->add_rm ; ap ; ap=ap->next ) { if (ap->type==HDR_RECORDROUTE_T && ap->op==LUMP_NOP && ap->before && ap->before->op==LUMP_ADD_OPT && ap->before->u.cond==COND_FALSE) { /* found our phony anchor lump */ /* jump over the anchor and conditional lumps */ lp = ap->before->before; /* unlink it */ ap->before->before = NULL; ap->type = 0; /* link the pending buffered params and go at the end of the list*/ for ( l2->before = lp ; l2 && l2->before ; l2=l2->before); break; } } if (!(l2=insert_new_lump_before(l2, term, RR_TERM_LEN, 0))) { LM_ERR("failed to insert term lump"); goto error; } term = NULL; return 1; error: if (hdr) pkg_free(hdr); if (term) pkg_free(term); if (suffix) pkg_free(suffix); return -1; } /*! \brief * Appends a new Record-Route parameter */ int add_rr_param(struct sip_msg* msg, str* rr_param) { struct lump *l; struct lump *crt; int rr_found=0; LM_DBG("adding (%.*s)\n",rr_param->len,rr_param->s); for( crt=msg->add_rm ; crt ; crt=crt->next ) { if (crt->type!=HDR_RECORDROUTE_T || crt->op!=LUMP_NOP) continue; /* found a RR-related anchor; we are looking for the * "second RR lump" (having data on "before") or for the * "buffering lump" (having also data on "before") */ if (!crt->before) continue; if (!rr_found && crt->before->op==LUMP_ADD_OPT && crt->before->u.cond==COND_FALSE ) { LM_DBG("buffering lump was found\n"); /* this is the "buffering lump" */ /* RR not done, but some RR params are already buffered -> add a before lump to the existing buffering one */ /* get the last param attached on the anchor */ for( l=crt->before->before ; l && l->before ; l=l->before); /* add the param */ if (insert_rr_param_lump( l, rr_param->s, rr_param->len)==0) { LM_ERR("failed to add buffered lump\n"); goto error; } return 0; } /* this is a "second RR lump" */ LM_DBG("second RR lump found\n"); /* RR was already done -> have to add a new lump before last param */ for (l=crt->before ; l && l->op!=LUMP_ADD ; l=l->before ); if (l==NULL) { LM_CRIT("BUG - second RR anchor has no ADD on before\n"); return -1; } if (insert_rr_param_lump( l, rr_param->s, rr_param->len)==0) { LM_ERR("failed to add lump\n"); goto error; } /* double routing enabled? */ if (!enable_double_rr) /* done */ return 0; /* continue looking for more RR headers */ rr_found = 1; } /* param already added to existing RR headers ? */ if (rr_found) return 0; /* RR not done, no other RR param added so far -> create the phony anchor and add the lump to it */ crt = anchor_lump(msg, msg->headers->name.s-msg->buf, HDR_RECORDROUTE_T); if (crt==NULL) { LM_ERR("cannot create phony lump for buffering params\n"); goto error; } l = insert_cond_lump_before( crt, COND_FALSE, 0); if (l==NULL) { LM_ERR("cannot create conditional lump for buffering params\n"); goto error; } if (insert_rr_param_lump( l, rr_param->s, rr_param->len)==0) { LM_ERR("failed to add buffered lump\n"); goto error; } return 0; error: return -1; } opensips-2.2.2/modules/rr/record.h000066400000000000000000000031411300170765700171040ustar00rootroot00000000000000/* * Route & Record-Route module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-04-04 Extracted from common.[ch] (janakj) * 2005-04-10 add_rr_param() function added (bogdan) */ /*! * \file * \brief Route & Record-Route module * \ingroup rr */ #ifndef RECORD_H #define RECORD_H #include "../../parser/msg_parser.h" #include "../../str.h" /*! \brief * Insert a new Record-Route header field with lr parameter */ int record_route(struct sip_msg* _m, str* _param); /*! \brief * Insert manually created Record-Route header, no checks, no restrictions, * always adds lr parameter, only fromtag is added automatically when requested */ int record_route_preset(struct sip_msg* _m, str* _data); /*! \brief * Appends a new Record-Route parameter */ int add_rr_param(struct sip_msg* msg, str* rr_param); #endif /* RECORD_H */ opensips-2.2.2/modules/rr/rr_cb.c000066400000000000000000000043011300170765700167070ustar00rootroot00000000000000/* * Copyright (C) 2005 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2005-08-02 first version (bogdan) */ /*! * \file * \brief Route & Record-Route module, callback API * \ingroup rr */ #include "../../mem/mem.h" #include "rr_cb.h" struct rr_callback* rrcb_hl = 0; /* head list */ void destroy_rrcb_lists(void) { struct rr_callback *cbp, *cbp_tmp; for( cbp=rrcb_hl; cbp ; ) { cbp_tmp = cbp; cbp = cbp->next; pkg_free( cbp_tmp ); } } int register_rrcb( rr_cb_t f, void *param, short prior ) { struct rr_callback *cbp, *rcbp; /* build a new callback structure */ if (!(cbp=pkg_malloc( sizeof( struct rr_callback)))) { LM_ERR("no more pkg mem\n"); return -1; } if (prior < 0) { LM_ERR("negative priority not allowed\n"); return -1; } /* fill it up */ cbp->callback = f; cbp->param = param; cbp->id = prior; if (!rrcb_hl || !prior || (prior < rrcb_hl->id)) { /* link it at the beginning of the list */ cbp->next = rrcb_hl; rrcb_hl = cbp; } else { rcbp = rrcb_hl; while (rcbp->next && rcbp->next->id < prior) rcbp = rcbp->next; cbp->next = rcbp->next; rcbp->next = cbp; } return 0; } void run_rr_callbacks( struct sip_msg *req, str *rr_params ) { str l_param; struct rr_callback *cbp; for ( cbp=rrcb_hl ; cbp ; cbp=cbp->next ) { l_param = *rr_params; LM_DBG("callback id %d entered with <%.*s>\n", cbp->id , l_param.len,l_param.s); cbp->callback( req, &l_param, cbp->param ); } } opensips-2.2.2/modules/rr/rr_cb.h000066400000000000000000000034331300170765700167210ustar00rootroot00000000000000/* * Copyright (C) 2005 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2005-08-02 first version (bogdan) */ /*! * \file * \brief Route & Record-Route module * \ingroup rr */ #ifndef RR_CB_H_ #define RR_CB_H_ #include "../../str.h" #include "../../parser/msg_parser.h" /*! \brief callback function prototype */ typedef void (rr_cb_t) (struct sip_msg* req, str *rr_param, void *param); /*! \brief register callback function prototype */ typedef int (*register_rrcb_t)( rr_cb_t f, void *param, short prior); struct rr_callback { short id; /*!< id of this callback - used as priority */ rr_cb_t* callback; /*!< callback function */ void *param; /*!< param to be passed to callback function */ struct rr_callback* next; /*!< next callback element*/ }; void destroy_rrcb_lists(); /*! \brief register a RR callback */ int register_rrcb(rr_cb_t f, void *param, short prior ); /*! \brief run RR transaction callbacks */ void run_rr_callbacks( struct sip_msg *req, str *rr_param); #endif opensips-2.2.2/modules/rr/rr_mod.c000066400000000000000000000216401300170765700171070ustar00rootroot00000000000000/* * Route & Record-Route module * * Copyright (C) 2009-2014 OpenSIPS Solutions * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module interface (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) * 2003-04-01 Added record_route with ip address parameter (janakj) * 2003-04-14 enable_full_lr parameter introduced (janakj) * 2005-04-10 add_rr_param() and check_route_param() added (bogdan) * 2006-02-14 record_route may take as param a string to be used as RR param; * record_route and record_route_preset accept pseudo-variables in * parameters; add_rr_param may be called from BRANCH and FAILURE * routes (bogdan) */ /*! * \file * \brief Route & Record-Route module * \ingroup rr */ #include #include #include #include #include "../../sr_module.h" #include "../../ut.h" #include "../../error.h" #include "../../pvar.h" #include "../../mem/mem.h" #include "../../mod_fix.h" #include "../../context.h" #include "loose.h" #include "record.h" #include "rr_cb.h" #include "api.h" #ifdef ENABLE_USER_CHECK #include #include "../../str.h" str i_user; char *ignore_user = NULL; #endif int ctx_rrdone_idx = -1; #define ctx_rrdone_set(_val) \ context_put_int(CONTEXT_GLOBAL,current_processing_ctx,ctx_rrdone_idx,_val) #define ctx_rrdone_get() \ context_get_int(CONTEXT_GLOBAL, current_processing_ctx, ctx_rrdone_idx) /* module parameters */ int append_fromtag = 1; int enable_double_rr = 1; /* Enable using of 2 RR by default */ int add_username = 0; /* Do not add username by default */ int enable_socket_mismatch_warning = 1; /* Enable socket mismatch warning */ static int mod_init(void); static void mod_destroy(void); /* fixup functions */ static int direction_fixup(void** param, int param_no); static int it_list_fixup(void** param, int param_no); /* wrapper functions */ static int w_record_route(struct sip_msg *,char *, char *); static int w_record_route_preset(struct sip_msg *,char *, char *); static int w_add_rr_param(struct sip_msg *,char *, char *); static int w_check_route_param(struct sip_msg *,char *, char *); static int w_is_direction(struct sip_msg *,char *, char *); static int pv_get_rr_params(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); /*! \brief * Exported functions */ static cmd_export_t cmds[] = { {"loose_route", (cmd_function)loose_route, 0, 0, 0, REQUEST_ROUTE}, {"record_route", (cmd_function)w_record_route, 0, 0, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"record_route", (cmd_function)w_record_route, 1, it_list_fixup, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"record_route_preset", (cmd_function)w_record_route_preset, 1, it_list_fixup, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"record_route_preset", (cmd_function)w_record_route_preset, 2, it_list_fixup, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"add_rr_param", (cmd_function)w_add_rr_param, 1, it_list_fixup, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"check_route_param", (cmd_function)w_check_route_param, 1, fixup_regexp_null, fixup_free_regexp_null, REQUEST_ROUTE}, {"is_direction", (cmd_function)w_is_direction, 1, direction_fixup, 0, REQUEST_ROUTE}, {"load_rr", (cmd_function)load_rr, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /*! \brief * Exported parameters */ static param_export_t params[] ={ {"append_fromtag", INT_PARAM, &append_fromtag }, {"enable_double_rr", INT_PARAM, &enable_double_rr }, #ifdef ENABLE_USER_CHECK {"ignore_user", STR_PARAM, &ignore_user }, #endif {"add_username", INT_PARAM, &add_username }, {"enable_socket_mismatch_warning", INT_PARAM, &enable_socket_mismatch_warning}, {0, 0, 0 } }; /** * pseudo-variables exported by RR module */ static pv_export_t mod_items[] = { { {"rr_params", sizeof("rr_params")-1}, 900, pv_get_rr_params, 0, 0, 0, 0, 0 }, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; #ifdef STATIC_RR struct module_exports rr_exports = { #else struct module_exports exports = { #endif "rr", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /*!< dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /*!< Exported functions */ 0, /*!< Exported async functions */ params, /*!< Exported parameters */ 0, /*!< exported statistics */ 0, /*!< exported MI functions */ mod_items, /*!< exported pseudo-variables */ 0, /*!< extra processes */ mod_init, /*!< initialize module */ 0, /*!< response function*/ mod_destroy, /*!< destroy function */ 0 /*!< per-child init function */ }; static int mod_init(void) { LM_INFO("rr - initializing\n"); ctx_rrparam_idx = context_register_str(CONTEXT_GLOBAL, NULL); ctx_routing_idx = context_register_int(CONTEXT_GLOBAL, NULL); ctx_rrdone_idx = context_register_int(CONTEXT_GLOBAL, NULL); #ifdef ENABLE_USER_CHECK if(ignore_user) { i_user.s = ignore_user; i_user.len = strlen(ignore_user); } else { i_user.s = 0; i_user.len = 0; } #endif return 0; } static void mod_destroy(void) { destroy_rrcb_lists(); } static int it_list_fixup(void** param, int param_no) { pv_elem_t *model; str s; if(*param) { s.s = (char*)(*param); s.len = strlen(s.s); if(pv_parse_format(&s, &model)<0) { LM_ERR("wrong format[%s]\n",(char*)(*param)); return E_UNSPEC; } *param = (void*)model; } return 0; } static int direction_fixup(void** param, int param_no) { char *s; int n; if (!append_fromtag) { LM_ERR("usage of \"is_direction\" function requires parameter" "\"append_fromtag\" enabled!!"); return E_CFG; } if (param_no==1) { n = 0; s = (char*) *param; if ( strcasecmp(s,"downstream")==0 ) { n = RR_FLOW_DOWNSTREAM; } else if ( strcasecmp(s,"upstream")==0 ) { n = RR_FLOW_UPSTREAM; } else { LM_ERR("unknown direction '%s'\n",s); return E_CFG; } /* free string */ pkg_free(*param); /* replace it with the flag */ *param = (void*)(unsigned long)n; } return 0; } static int pv_get_rr_params(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str val; if(msg==NULL || res==NULL) return -1; /* obtain routed params */ if (get_route_params(msg, &val) < 0 ) return -1; res->rs.s = val.s; res->rs.len = val.len; res->flags = PV_VAL_STR; return 0; } static int w_record_route(struct sip_msg *msg, char *key, char *bar) { str s; if (ctx_rrdone_get()==1) { LM_ERR("Double attempt to record-route\n"); return -1; } if (key && pv_printf_s(msg, (pv_elem_t*)key, &s)<0) { LM_ERR("failed to print the format\n"); return -1; } if ( record_route( msg, key?&s:0 )<0 ) return -1; ctx_rrdone_set(1); return 1; } static int w_record_route_preset(struct sip_msg *msg, char *key, char *key2) { str s; if (ctx_rrdone_get()==1) { LM_ERR("Double attempt to record-route\n"); return -1; } if (key2 && !enable_double_rr) { LM_ERR("Attempt to double record-route while 'enable_double_rr' " "param is disabled\n"); return -1; } if (pv_printf_s(msg, (pv_elem_t*)key, &s)<0) { LM_ERR("failed to print the format\n"); return -1; } if ( record_route_preset( msg, &s)<0 ) return -1; if (!key2) goto done; if (pv_printf_s(msg, (pv_elem_t*)key2, &s)<0) { LM_ERR("failed to print the format\n"); return -1; } if ( record_route_preset( msg, &s)<0 ) return -1; done: ctx_rrdone_set(1); return 1; } static int w_add_rr_param(struct sip_msg *msg, char *key, char *foo) { str s; if (pv_printf_s(msg, (pv_elem_t*)key, &s)<0) { LM_ERR("failed to print the format\n"); return -1; } return ((add_rr_param( msg, &s)==0)?1:-1); } static int w_check_route_param(struct sip_msg *msg,char *re, char *foo) { return ((check_route_param(msg,(regex_t*)re)==0)?1:-1); } static int w_is_direction(struct sip_msg *msg,char *dir, char *foo) { return ((is_direction(msg,(int)(long)dir)==0)?1:-1); } opensips-2.2.2/modules/rr/rr_mod.h000066400000000000000000000023531300170765700171140ustar00rootroot00000000000000/* * Record-Route & Route module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-03-15 License added (janakj) */ /*! * \file * \brief Route & Record-Route module interface * \ingroup rr */ #ifndef RR_MOD_H #define RR_MOD_H #ifdef ENABLE_USER_CHECK #include "../../str.h" extern str i_user; #endif extern int append_fromtag; extern int enable_double_rr; extern int add_username; extern int enable_socket_mismatch_warning; #endif /* RR_MOD_H */ opensips-2.2.2/modules/rtpengine/000077500000000000000000000000001300170765700170265ustar00rootroot00000000000000opensips-2.2.2/modules/rtpengine/Makefile000066400000000000000000000004141300170765700204650ustar00rootroot00000000000000# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=rtpengine.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/rtpengine/README000066400000000000000000000562601300170765700177170ustar00rootroot00000000000000rtpengine Module Maxim Sobolev Sippy Software, Inc. Juha Heinanen TuTPro, Inc. Edited by Maxim Sobolev Edited by Bogdan-Andrei Iancu Edited by Juha Heinanen Edited by Sas Ovidiu Edited by Carsten Bock ng-voice GmbH Edited by Richard Fuchs Sipwise GmbH Copyright © 2003-2008 Sippy Software, Inc. Copyright © 2005 Voice Sistem SRL Copyright © 2009-2014 TuTPro Inc. Copyright © 2010 VoIPEmbedded Inc. Copyright © 2013-2014 Sipwise GmbH __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Multiple RTP proxy usage 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Parameters 1.4.1. rtpengine_sock (string) 1.4.2. rtpengine_disable_tout (integer) 1.4.3. rtpengine_tout (integer) 1.4.4. rtpengine_retr (integer) 1.4.5. extra_id_pv (string) 1.4.6. setid_avp (string) 1.5. Functions 1.5.1. rtpengine_use_set(setid) 1.5.2. rtpengine_offer([flags]) 1.5.3. rtpengine_answer([flags]) 1.5.4. rtpengine_delete([flags]) 1.5.5. rtpengine_manage([flags]) 1.5.6. rtpengine_start_recording() 1.6. Exported Pseudo Variables 1.6.1. $rtpstat 1.7. MI Commands 1.7.1. rtpengine_enable 1.7.2. rtpengine_show 2. Frequently Asked Questions List of Examples 1.1. Set rtpengine_sock parameter 1.2. Set rtpengine_disable_tout parameter 1.3. Set rtpengine_tout parameter 1.4. Set rtpengine_retr parameter 1.5. Set extra_id_pv parameter 1.6. Set setid_avp parameter 1.7. rtpengine_use_set usage 1.8. rtpengine_offer usage 1.9. rtpengine_answer usage 1.10. rtpengine_delete usage 1.11. rtpengine_manage usage 1.12. rtpengine_start_recording usage 1.13. $rtpstat Usage 1.14. rtpengine_enable usage 1.15. rtpengine_show usage Chapter 1. Admin Guide 1.1. Overview This is a module that enables media streams to be proxied via an RTP proxy. The only RTP proxy currently known to work with this module is the Sipwise rtpengine https://github.com/sipwise/rtpengine. The rtpengine module is a modified version of the original rtpproxy module using a new control protocol. The module is designed to be a drop-in replacement for the old module from a configuration file point of view, however due to the incompatible control protocol, it only works with RTP proxies which specifically support it. 1.2. Multiple RTP proxy usage The rtpengine module can support multiple RTP proxies for balancing/distribution and control/selection purposes. The module allows definition of several sets of rtpengines. Load-balancing will be performed over a set and the admin has the ability to choose what set should be used. The set is selected via its id - the id being defined with the set. Refer to the “rtpengine_sock†module parameter definition for syntax description. The balancing inside a set is done automatically by the module based on the weight of each RTP proxy from the set. The selection of the set is done from script prior using rtpengine_delete(), rtpengine_offer() or rtpengine_answer() functions - see the rtpengine_use_set() function. Another way to select the set is to define setid_avp module parameter and assign setid to the defined avp before calling rtpengine_offer() or rtpengine_manage() function. If forwarding of the requests fails and there is another branch to try, remember to unset the avp after calling rtpengine_delete() function. For backward compatibility reasons, a set with no id take by default the id 0. Also if no set is explicitly set before rtpengine_delete(), rtpengine_offer() or rtpengine_answer() the 0 id set will be used. IMPORTANT: if you use multiple sets, take care and use the same set for both rtpengine_offer()/rtpengine_answer() and rtpengine_delete()!! If the set was selected using setid_avp, the avp needs to be set only once before rtpengine_offer() or rtpengine_manage() call. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * tm module - (optional) if you want to have rtpengine_manage() fully functional 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.4. Parameters 1.4.1. rtpengine_sock (string) Definition of socket(s) used to connect to (a set) RTP proxy. It may specify a UNIX socket or an IPv4/IPv6 UDP socket. Default value is “NONE†(disabled). Example 1.1. Set rtpengine_sock parameter ... # single rtproxy modparam("rtpengine", "rtpengine_sock", "udp:localhost:12221") # multiple rtproxies for LB modparam("rtpengine", "rtpengine_sock", "udp:localhost:12221 udp:localhost:12222") # multiple sets of multiple rtproxies modparam("rtpengine", "rtpengine_sock", "1 == udp:localhost:12221 udp:localhost:12222") modparam("rtpengine", "rtpengine_sock", "2 == udp:localhost:12225") ... 1.4.2. rtpengine_disable_tout (integer) Once an RTP proxy was found unreachable and marked as disabled, the rtpengine module will not attempt to establish communication to that RTP proxy for rtpengine_disable_tout seconds. Default value is “60â€. Example 1.2. Set rtpengine_disable_tout parameter ... modparam("rtpengine", "rtpengine_disable_tout", 20) ... 1.4.3. rtpengine_tout (integer) Timeout value in waiting for reply from RTP proxy. Default value is “1â€. Example 1.3. Set rtpengine_tout parameter ... modparam("rtpengine", "rtpengine_tout", 2) ... 1.4.4. rtpengine_retr (integer) How many times the module should retry to send and receive after timeout was generated. Default value is “5â€. Example 1.4. Set rtpengine_retr parameter ... modparam("rtpengine", "rtpengine_retr", 2) ... 1.4.5. extra_id_pv (string) The parameter sets the PV defination to use when the “b†parameter is used on rtpengine_delete(), rtpengine_offer(), rtpengine_answer() or rtpengine_manage() command. Default is empty, the “b†parameter may not be used then. Example 1.5. Set extra_id_pv parameter ... modparam("rtpengine", "extra_id_pv", "$avp(extra_id)") ... 1.4.6. setid_avp (string) The parameter defines an AVP that, if set, determines which RTP proxy set rtpengine_offer(), rtpengine_answer(), rtpengine_delete(), and rtpengine_manage() functions use. There is no default value. Example 1.6. Set setid_avp parameter ... modparam("rtpengine", "setid_avp", "$avp(setid)") ... 1.5. Functions 1.5.1. rtpengine_use_set(setid) Sets the ID of the RTP proxy set to be used for the next rtpengine_delete(), rtpengine_offer(), rtpengine_answer() or rtpengine_manage() command. The parameter can be an integer or a config variable holding an integer. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE. Example 1.7. rtpengine_use_set usage ... rtpengine_use_set("2"); rtpengine_offer(); ... 1.5.2. rtpengine_offer([flags]) Rewrites SDP body to ensure that media is passed through an RTP proxy. To be invoked on INVITE for the cases the SDPs are in INVITE and 200 OK and on 200 OK when SDPs are in 200 OK and ACK. Meaning of the parameters is as follows: * flags - flags to turn on some features. The “flags†string is a list of space-separated items. Each item is either an individual token, or a token in “key=value†format. The possible tokens are described below. + via-branch=... - Include the “branch†value of one of the “Via†headers in the request to the RTP proxy. Possible values are: “1†- use the first “Via†header; “2†- use the second “Via†header; “auto†- use the first “Via†header if this is a request, or the second one if this is a reply; “extra†- don't take the value from a header, but instead use the value of the “extra_id_pv†variable. This can be used to create one media session per branch on the RTP proxy. When sending a subsequent “delete†command to the RTP proxy, you can then stop just the session for a specific branch when passing the flag '1' or '2' in the “rtpengine_deleteâ€, or stop all sessions for a call when not passing one of those two flags there. This is especially useful if you have serially forked call scenarios where the RTP proxy gets an “offer†command for a new branch, and then a “delete†command for the previous branch, which would otherwise delete the full call, breaking the subsequent “answer†for the new branch. This flag is only supported by the Sipwise rtpengine RTP proxy at the moment! + asymmetric - flags that UA from which message is received doesn't support symmetric RTP. (automatically sets the 'r' flag) + force-answer - force “answerâ€, that is, only rewrite SDP when corresponding session already exists in the RTP proxy. By default is on when the session is to be completed. + internal, external - these flags specify the direction of the SIP message. These flags only make sense when the RTP proxy is running in bridge mode. “internal†corresponds to the proxy's first interface, “external†corresponds to the RTP proxy's second interface. You always have to specify two flags to define the incoming network and the outgoing network. For example, “internal external†should be used for SIP message received from the local interface and sent out on the external interface, and “external internal†vice versa. Other options are “internal internal†and “external externalâ€. So, for example if a SIP requests is processed with “internal external†flags, the corresponding response must be processed with “internal external†flags. + auto-bridge - this flag an alternative to the “internal†and “external†flags in order to do automatic bridging between IPv4 on the "internal network" and IPv6 on the "external network". Instead of explicitly instructing the RTP proxy to select a particular address family, the distinction is done by the given IP in the SDP body by the RTP proxy itself. Not supported by Sipwise rtpengine. + address-family=... - instructs the RTP proxy that the recipient of this SDP body expects to see addresses of a particular family. Possible values are “IP4†and “IP6â€. For example, if the SDP body contains IPv4 addresses but the recipient only speaks IPv6, you would use “address-family=IP6†to bridge between the two address families. Sipwise rtpengine remembers the address family preference of each party after it has seen an SDP body from them. This means that normally it is only necessary to explicitly specify the address family in the “offerâ€, but not in the “answerâ€. Note: Please note, that this will only work properly with non-dual-stack user-agents or with dual-stack clients according to RFC6157 (which suggest ICE for Dual-Stack implementations). This short-cut will not work properly with RFC4091 (ANAT) compatible clients, which suggests having different m-lines with different IP-protocols grouped together. + force - instructs the RTP proxy to ignore marks inserted by another RTP proxy in transit to indicate that the session is already goes through another proxy. Allows creating a chain of proxies. Not supported and ignored by Sipwise rtpengine. + trust-address - flags that IP address in SDP should be trusted. Without this flag, the RTP proxy ignores address in the SDP and uses source address of the SIP message as media address which is passed to the RTP proxy. + replace-origin - flags that IP from the origin description (o=) should be also changed. + replace-session-connection - flags to change the session-level SDP connection (c=) IP if media description also includes connection information. + symmetric - flags that for the UA from which message is received, support symmetric RTP must be forced. + repacketize=NN - requests the RTP proxy to perform re-packetization of RTP traffic coming from the UA which has sent the current message to increase or decrease payload size per each RTP packet forwarded if possible. The NN is the target payload size in ms, for the most codecs its value should be in 10ms increments, however for some codecs the increment could differ (e.g. 30ms for GSM or 20ms for G.723). The RTP proxy would select the closest value supported by the codec. This feature could be used for significantly reducing bandwith overhead for low bitrate codecs, for example with G.729 going from 10ms to 100ms saves two thirds of the network bandwith. Not supported by Sipwise rtpengine. + ICE=... - controls the RTP proxy's behaviour regarding ICE attributes within the SDP body. Possible values are: “force†- discard any ICE attributes already present in the SDP body and then generate and insert new ICE data, leaving itself as the only ICE candidates; “remove†instructs the RTP proxy to discard any ICE attributes and not insert any new ones into the SDP. The default (if no “ICE=...†is given at all), new ICE data will only be generated if no ICE was present in the SDP originally; otherwise the RTP proxy will only insert itself as an additional ICE candidate. Other SDP substitutions (c=, m=, etc) are unaffected by this flag. + RTP, SRTP, AVP, AVPF - These flags control the RTP transport protocol that should be used towards the recipient of the SDP. If none of them are specified, the protocol given in the SDP is left untouched. Otherwise, the “SRTP†flag indicates that SRTP should be used, while “RTP†indicates that SRTP should not be used. “AVPF†indicates that the advanced RTCP profile with feedback messages should be used, and “AVP†indicates that the regular RTCP profile should be used. See also the next set of flags below. + RTP/AVP, RTP/SAVP, RTP/AVPF, RTP/SAVPF - these serve as an alternative, more explicit way to select between the different RTP protocols and profiles supported by the RTP proxy. For example, giving the flag “RTP/SAVPF†has the same effect as giving the two flags “SRTP AVPFâ€. + to-tag - force inclusion of the “To†tag. Normally, the “To†tag is always included when present, except for “delete†messages. Including the “To†tag in a “delete†messages allows you to be more selective about which dialogues within a call are being torn down. + rtcp-mux-demux - if rtcp-mux (RFC 5761) was offered, make the RTP proxy accept the offer, but not offer it to the recipient of this message. + rtcp-mux-reject - if rtcp-mux was offered, make the RTP proxy reject the offer, but still offer it to the recipient. Can be combined with “rtcp-mux-offer†to always offer it. + rtcp-mux-offer - make the RTP proxy offer rtcp-mux to the recipient of this message, regardless of whether it was offered originally or not. + rtcp-mux-accept - if rtcp-mux was offered, make the RTP proxy accept the offer and also offer it to the recipient of this message. Can be combined with “rtcp-mux-offer†to always offer it. + media-address=... - force a particular media address to be used in the SDP body. Address family is detected automatically. This function can be used from ANY_ROUTE. Example 1.8. rtpengine_offer usage route { ... if (is_method("INVITE")) { if (has_body("application/sdp")) { if (rtpengine_offer()) t_on_reply("1"); } else { t_on_reply("2"); } } if (is_method("ACK") && has_body("application/sdp")) rtpengine_answer(); ... } onreply_route[1] { ... if (has_body("application/sdp")) rtpengine_answer(); ... } onreply_route[2] { ... if (has_body("application/sdp")) rtpengine_offer(); ... } 1.5.3. rtpengine_answer([flags]) Rewrites SDP body to ensure that media is passed through an RTP proxy. To be invoked on 200 OK for the cases the SDPs are in INVITE and 200 OK and on ACK when SDPs are in 200 OK and ACK. See rtpengine_offer() function description above for the meaning of the parameters. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.9. rtpengine_answer usage See rtpengine_offer() function example above for example. 1.5.4. rtpengine_delete([flags]) Tears down the RTPProxy session for the current call. See rtpengine_offer() function description above for the meaning of the parameters. Note that not all flags make sense for a “deleteâ€. This function can be used from ANY_ROUTE. Example 1.10. rtpengine_delete usage ... rtpengine_delete(); ... 1.5.5. rtpengine_manage([flags]) Manage the RTPProxy session - it combines the functionality of rtpengine_offer(), rtpengine_answer() and rtpengine_delete(), detecting internally based on message type and method which one to execute. It can take the same parameters as rtpengine_offer(). The flags parameter to rtpengine_manage() can be a configuration variable containing the flags as a string. Functionality: * If INVITE with SDP, then do rtpengine_offer() * If INVITE with SDP, when the tm module is loaded, mark transaction with internal flag FL_SDP_BODY to know that the 1xx and 2xx are for rtpengine_answer() * If ACK with SDP, then do rtpengine_answer() * If BYE or CANCEL, or called within a FAILURE_ROUTE[], then do rtpengine_delete() * If reply to INVITE with code >= 300 do rtpengine_delete() * If reply with SDP to INVITE having code 1xx and 2xx, then do rtpengine_answer() if the request had SDP or tm is not loaded, otherwise do rtpengine_offer() This function can be used from ANY_ROUTE. Example 1.11. rtpengine_manage usage ... rtpengine_manage(); ... 1.5.6. rtpengine_start_recording() This function will send a signal to the RTP proxy to record the RTP stream on the RTP proxy. This function is not supported by Sipwise rtpengine at the moment! This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. Example 1.12. rtpengine_start_recording usage ... rtpengine_start_recording(); ... 1.6. Exported Pseudo Variables 1.6.1. $rtpstat Returns the RTP statistics from the RTP proxy. The RTP statistics from the RTP proxy are provided as a string and it does contain several packet counters. The statistics must be retrieved before the session is deleted (before rtpengine_delete()). Example 1.13. $rtpstat Usage ... append_hf("X-RTP-Statistics: $rtpstat\r\n"); ... 1.7. MI Commands 1.7.1. rtpengine_enable Enables a RTP proxy if parameter value is greater than 0. Disables it if a zero value is given. The first parameter is the RTP proxy url (exactly as defined in the config file). The second parameter value must be a number in decimal. NOTE: if a RTP proxy is defined multiple times (in the same or diferente sete), all of its instances will be enables/disabled. Example 1.14. rtpengine_enable usage ... $ opensipsctl fifo rtpengine_enable udp:192.168.2.133:8081 0 ... 1.7.2. rtpengine_show Displays all the RTP proxies and their information: set and status (disabled or not, weight and recheck_ticks). No parameter. Example 1.15. rtpengine_show usage ... $ opensipsctl fifo rtpengine_show ... Chapter 2. Frequently Asked Questions 2.1. How do I migrate from “rtpproxy†or “rtpproxy-ng†to “rtpengineâ€? For the most part, only the names of the functions have changed, with “rtpproxy†in each name replaced with “rtpengineâ€. For example, “rtpproxy_manage()†has become “rtpengine_manage()â€. A few name duplications have also been resolved, for example there is now a single “rtpengine_delete()†instead of “unforce_rtp_proxy()†and the identical “rtpproxy_destroy()â€. The largest difference to the old module is how flags are passed to “rtpengine_offer()â€, “rtpengine_answer()â€, “rtpengine_manage()†and “rtpengine_delete()â€. Instead of having a string of single-letter flags, they now take a string of space-separated items, with each item being either a single token (word) or a “key=value†pair. For example, if you had a call “rtpproxy_offer("FRWOC+PS");â€, this would then become: rtpengine_offer("force trust-address symmetric replace-origin replace-se ssion-connection ICE=force RTP/SAVPF"); Finally, if you were using the second paramater (explicit media address) to any of these functions, this has been replaced by the “media-address=...†option within the first string of flags. 2.2. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.3. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.4. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/rtpengine/bencode.c000066400000000000000000000367451300170765700206100ustar00rootroot00000000000000/* * Copyright ?? * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2014-06-17 Initial upload */ #include "bencode.h" #include #include #include #include #include #include /* set to 0 for alloc debugging, e.g. through valgrind */ #define BENCODE_MIN_BUFFER_PIECE_LEN 512 #define BENCODE_HASH_BUCKETS 31 /* prime numbers work best */ struct __bencode_buffer_piece { char *tail; unsigned int left; struct __bencode_buffer_piece *next; char buf[0]; }; struct __bencode_free_list { void *ptr; free_func_t func; struct __bencode_free_list *next; }; struct __bencode_hash { struct bencode_item *buckets[BENCODE_HASH_BUCKETS]; }; static bencode_item_t __bencode_end_marker = { .type = BENCODE_END_MARKER, .iov[0].iov_len = 1, .iov[0].iov_base = "e", .iov_cnt = 1, .str_len = 1, }; static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, const char *end); static void __bencode_item_init(bencode_item_t *item) { item->last_child = item->parent = item->child = item->sibling = NULL; } static void __bencode_container_init(bencode_item_t *cont) { cont->iov[0].iov_len = 1; cont->iov[1].iov_base = "e"; cont->iov[1].iov_len = 1; cont->iov_cnt = 2; cont->str_len = 2; } static void __bencode_dictionary_init(bencode_item_t *dict) { dict->type = BENCODE_DICTIONARY; dict->iov[0].iov_base = "d"; dict->value = 0; __bencode_container_init(dict); } static void __bencode_list_init(bencode_item_t *list) { list->type = BENCODE_LIST; list->iov[0].iov_base = "l"; __bencode_container_init(list); } static struct __bencode_buffer_piece *__bencode_piece_new(unsigned int size) { struct __bencode_buffer_piece *ret; if (size < BENCODE_MIN_BUFFER_PIECE_LEN) size = BENCODE_MIN_BUFFER_PIECE_LEN; ret = BENCODE_MALLOC(sizeof(*ret) + size); if (!ret) return NULL; ret->tail = ret->buf; ret->left = size; ret->next = NULL; return ret; } int bencode_buffer_init(bencode_buffer_t *buf) { buf->pieces = __bencode_piece_new(0); if (!buf->pieces) return -1; buf->free_list = NULL; buf->error = 0; return 0; } static void *__bencode_alloc(bencode_buffer_t *buf, unsigned int size) { struct __bencode_buffer_piece *piece; void *ret; if (!buf) return NULL; if (buf->error) return NULL; piece = buf->pieces; if (size <= piece->left) goto alloc; piece = __bencode_piece_new(size); if (!piece) { buf->error = 1; return NULL; } piece->next = buf->pieces; buf->pieces = piece; assert(size <= piece->left); alloc: piece->left -= size; ret = piece->tail; piece->tail += size; return ret; } void bencode_buffer_free(bencode_buffer_t *buf) { struct __bencode_free_list *fl; struct __bencode_buffer_piece *piece, *next; for (fl = buf->free_list; fl; fl = fl->next) fl->func(fl->ptr); for (piece = buf->pieces; piece; piece = next) { next = piece->next; BENCODE_FREE(piece); } } static bencode_item_t *__bencode_item_alloc(bencode_buffer_t *buf, unsigned int payload) { bencode_item_t *ret; ret = __bencode_alloc(buf, sizeof(struct bencode_item) + payload); if (!ret) return NULL; ret->buffer = buf; __bencode_item_init(ret); return ret; } bencode_item_t *bencode_dictionary(bencode_buffer_t *buf) { bencode_item_t *ret; ret = __bencode_item_alloc(buf, 0); if (!ret) return NULL; __bencode_dictionary_init(ret); return ret; } bencode_item_t *bencode_list(bencode_buffer_t *buf) { bencode_item_t *ret; ret = __bencode_item_alloc(buf, 0); if (!ret) return NULL; __bencode_list_init(ret); return ret; } static void __bencode_container_add(bencode_item_t *parent, bencode_item_t *child) { if (!parent) return; if (!child) return; assert(child->parent == NULL); assert(child->sibling == NULL); child->parent = parent; if (parent->last_child) parent->last_child->sibling = child; parent->last_child = child; if (!parent->child) parent->child = child; while (parent) { parent->iov_cnt += child->iov_cnt; parent->str_len += child->str_len; parent = parent->parent; } } static bencode_item_t *__bencode_string_alloc(bencode_buffer_t *buf, const void *base, int str_len, int iov_len, int iov_cnt, bencode_type_t type) { bencode_item_t *ret; int len_len; assert((str_len <= 99999) && (str_len >= 0)); ret = __bencode_item_alloc(buf, 7); if (!ret) return NULL; len_len = sprintf(ret->__buf, "%d:", str_len); ret->type = type; ret->iov[0].iov_base = ret->__buf; ret->iov[0].iov_len = len_len; ret->iov[1].iov_base = (void *) base; ret->iov[1].iov_len = iov_len; ret->iov_cnt = iov_cnt + 1; ret->str_len = len_len + str_len; return ret; } bencode_item_t *bencode_string_len_dup(bencode_buffer_t *buf, const char *s, int len) { char *sd = __bencode_alloc(buf, len); if (!sd) return NULL; memcpy(sd, s, len); return bencode_string_len(buf, sd, len); } bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len) { return __bencode_string_alloc(buf, s, len, len, 1, BENCODE_STRING); } bencode_item_t *bencode_string_iovec(bencode_buffer_t *buf, const struct iovec *iov, int iov_cnt, int str_len) { int i; if (iov_cnt < 0) return NULL; if (str_len < 0) { str_len = 0; for (i = 0; i < iov_cnt; i++) str_len += iov[i].iov_len; } return __bencode_string_alloc(buf, iov, str_len, iov_cnt, iov_cnt, BENCODE_IOVEC); } bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i) { bencode_item_t *ret; int alen, rlen; alen = 8; while (1) { ret = __bencode_item_alloc(buf, alen + 3); if (!ret) return NULL; rlen = snprintf(ret->__buf, alen, "i%llde", i); if (rlen < alen) break; alen <<= 1; } ret->type = BENCODE_INTEGER; ret->iov[0].iov_base = ret->__buf; ret->iov[0].iov_len = rlen; ret->iov[1].iov_base = NULL; ret->iov[1].iov_len = 0; ret->iov_cnt = 1; ret->str_len = rlen; return ret; } bencode_item_t *bencode_dictionary_add_len(bencode_item_t *dict, const char *key, int keylen, bencode_item_t *val) { bencode_item_t *str; if (!dict || !val) return NULL; assert(dict->type == BENCODE_DICTIONARY); str = bencode_string_len(dict->buffer, key, keylen); if (!str) return NULL; __bencode_container_add(dict, str); __bencode_container_add(dict, val); return val; } bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item) { if (!list || !item) return NULL; assert(list->type == BENCODE_LIST); __bencode_container_add(list, item); return item; } static int __bencode_iovec_cpy(struct iovec *out, const struct iovec *in, int num) { memcpy(out, in, num * sizeof(*out)); return num; } static int __bencode_str_cpy(char *out, const struct iovec *in, int num) { char *orig = out; while (--num >= 0) { memcpy(out, in->iov_base, in->iov_len); out += in->iov_len; in++; } return out - orig; } static int __bencode_iovec_dump(struct iovec *out, bencode_item_t *item) { bencode_item_t *child; struct iovec *orig = out; assert(item->iov[0].iov_base != NULL); out += __bencode_iovec_cpy(out, &item->iov[0], 1); child = item->child; while (child) { out += __bencode_iovec_dump(out, child); child = child->sibling; } if (item->type == BENCODE_IOVEC) out += __bencode_iovec_cpy(out, item->iov[1].iov_base, item->iov[1].iov_len); else if (item->iov[1].iov_base) out += __bencode_iovec_cpy(out, &item->iov[1], 1); assert((out - orig) == item->iov_cnt); return item->iov_cnt; } static int __bencode_str_dump(char *out, bencode_item_t *item) { char *orig = out; bencode_item_t *child; assert(item->iov[0].iov_base != NULL); out += __bencode_str_cpy(out, &item->iov[0], 1); child = item->child; while (child) { out += __bencode_str_dump(out, child); child = child->sibling; } if (item->type == BENCODE_IOVEC) out += __bencode_str_cpy(out, item->iov[1].iov_base, item->iov[1].iov_len); else if (item->iov[1].iov_base) out += __bencode_str_cpy(out, &item->iov[1], 1); assert((out - orig) == item->str_len); *out = '\0'; return item->str_len; } struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, unsigned int tail) { struct iovec *ret; if (!root) return NULL; assert(cnt != NULL); assert(root->iov_cnt > 0); ret = __bencode_alloc(root->buffer, sizeof(*ret) * (root->iov_cnt + head + tail)); if (!ret) return NULL; *cnt = __bencode_iovec_dump(ret + head, root); return ret; } char *bencode_collapse(bencode_item_t *root, int *len) { char *ret; int l; if (!root) return NULL; assert(root->str_len > 0); ret = __bencode_alloc(root->buffer, root->str_len + 1); if (!ret) return NULL; l = __bencode_str_dump(ret, root); if (len) *len = l; return ret; } char *bencode_collapse_dup(bencode_item_t *root, int *len) { char *ret; int l; if (!root) return NULL; assert(root->str_len > 0); ret = BENCODE_MALLOC(root->str_len + 1); if (!ret) return NULL; l = __bencode_str_dump(ret, root); if (len) *len = l; return ret; } static unsigned int __bencode_hash_str_len(const unsigned char *s, int len) { unsigned long *ul; unsigned int *ui; unsigned short *us; if (len >= sizeof(*ul)) { ul = (void *) s; return *ul % BENCODE_HASH_BUCKETS; } if (len >= sizeof(*ui)) { ui = (void *) s; return *ui % BENCODE_HASH_BUCKETS; } if (len >= sizeof(*us)) { us = (void *) s; return *us % BENCODE_HASH_BUCKETS; } if (len >= sizeof(*s)) return *s % BENCODE_HASH_BUCKETS; return 0; } static unsigned int __bencode_hash_str(bencode_item_t *str) { assert(str->type == BENCODE_STRING); return __bencode_hash_str_len(str->iov[1].iov_base, str->iov[1].iov_len); } static void __bencode_hash_insert(bencode_item_t *key, struct __bencode_hash *hash) { unsigned int bucket, i; i = bucket = __bencode_hash_str(key); while (1) { if (!hash->buckets[i]) { hash->buckets[i] = key; break; } i++; if (i >= BENCODE_HASH_BUCKETS) i = 0; if (i == bucket) break; } } static bencode_item_t *__bencode_decode_dictionary(bencode_buffer_t *buf, const char *s, const char *end) { bencode_item_t *ret, *key, *value; struct __bencode_hash *hash; if (*s != 'd') return NULL; s++; ret = __bencode_item_alloc(buf, sizeof(*hash)); if (!ret) return NULL; __bencode_dictionary_init(ret); ret->value = 1; hash = (void *) ret->__buf; memset(hash, 0, sizeof(*hash)); while (s < end) { key = __bencode_decode(buf, s, end); if (!key) return NULL; s += key->str_len; if (key->type == BENCODE_END_MARKER) break; if (key->type != BENCODE_STRING) return NULL; __bencode_container_add(ret, key); if (s >= end) return NULL; value = __bencode_decode(buf, s, end); if (!value) return NULL; s += value->str_len; if (value->type == BENCODE_END_MARKER) return NULL; __bencode_container_add(ret, value); __bencode_hash_insert(key, hash); } return ret; } static bencode_item_t *__bencode_decode_list(bencode_buffer_t *buf, const char *s, const char *end) { bencode_item_t *ret, *item; if (*s != 'l') return NULL; s++; ret = __bencode_item_alloc(buf, 0); if (!ret) return NULL; __bencode_list_init(ret); while (s < end) { item = __bencode_decode(buf, s, end); if (!item) return NULL; s += item->str_len; if (item->type == BENCODE_END_MARKER) break; __bencode_container_add(ret, item); } return ret; } static bencode_item_t *__bencode_decode_integer(bencode_buffer_t *buf, const char *s, const char *end) { long long int i; const char *orig = s; char *convend; bencode_item_t *ret; if (*s != 'i') return NULL; s++; if (s >= end) return NULL; if (*s == '0') { i = 0; s++; goto done; } i = strtoll(s, &convend, 10); if (convend == s) return NULL; s += (convend - s); done: if (s >= end) return NULL; if (*s != 'e') return NULL; s++; ret = __bencode_item_alloc(buf, 0); if (!ret) return NULL; ret->type = BENCODE_INTEGER; ret->iov[0].iov_base = (void *) orig; ret->iov[0].iov_len = s - orig; ret->iov[1].iov_base = NULL; ret->iov[1].iov_len = 0; ret->iov_cnt = 1; ret->str_len = s - orig; ret->value = i; return ret; } static bencode_item_t *__bencode_decode_string(bencode_buffer_t *buf, const char *s, const char *end) { unsigned long int sl; char *convend; const char *orig = s; bencode_item_t *ret; if (*s == '0') { sl = 0; s++; goto colon; } sl = strtoul(s, &convend, 10); if (convend == s) return NULL; s += (convend - s); colon: if (s >= end) return NULL; if (*s != ':') return NULL; s++; if (s + sl > end) return NULL; ret = __bencode_item_alloc(buf, 0); if (!ret) return NULL; ret->type = BENCODE_STRING; ret->iov[0].iov_base = (void *) orig; ret->iov[0].iov_len = s - orig; ret->iov[1].iov_base = (void *) s; ret->iov[1].iov_len = sl; ret->iov_cnt = 2; ret->str_len = s - orig + sl; return ret; } static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, const char *end) { if (s >= end) return NULL; switch (*s) { case 'd': return __bencode_decode_dictionary(buf, s, end); case 'l': return __bencode_decode_list(buf, s, end); case 'i': return __bencode_decode_integer(buf, s, end); case 'e': return &__bencode_end_marker; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return __bencode_decode_string(buf, s, end); default: return NULL; } } bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, int len) { assert(s != NULL); return __bencode_decode(buf, s, s + len); } static int __bencode_dictionary_key_match(bencode_item_t *key, const char *keystr, int keylen) { assert(key->type == BENCODE_STRING); if (keylen != key->iov[1].iov_len) return 0; if (memcmp(keystr, key->iov[1].iov_base, keylen)) return 0; return 1; } bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *keystr, int keylen) { bencode_item_t *key; unsigned int bucket, i; struct __bencode_hash *hash; if (!dict) return NULL; if (dict->type != BENCODE_DICTIONARY) return NULL; /* try hash lookup first if possible */ if (dict->value == 1) { hash = (void *) dict->__buf; i = bucket = __bencode_hash_str_len((const unsigned char *) keystr, keylen); while (1) { key = hash->buckets[i]; if (!key) return NULL; /* would be there, but isn't */ assert(key->sibling != NULL); if (__bencode_dictionary_key_match(key, keystr, keylen)) return key->sibling; i++; if (i >= BENCODE_HASH_BUCKETS) i = 0; if (i == bucket) break; /* fall back to regular lookup */ } } for (key = dict->child; key; key = key->sibling->sibling) { assert(key->sibling != NULL); if (__bencode_dictionary_key_match(key, keystr, keylen)) return key->sibling; } return NULL; } void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t func, void *p) { struct __bencode_free_list *li; if (!p) return; li = __bencode_alloc(buf, sizeof(*li)); if (!li) return; li->ptr = p; li->func = func; li->next = buf->free_list; buf->free_list = li; } opensips-2.2.2/modules/rtpengine/bencode.h000066400000000000000000000606071300170765700206070ustar00rootroot00000000000000/* * Copyright ?? * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2014-06-17 Initial upload */ #ifndef _BENCODE_H_ #define _BENCODE_H_ #include #include #if defined(PKG_MALLOC) || defined(pkg_malloc) /* opensips */ # include "../../mem/mem.h" # include "../../str.h" # ifndef BENCODE_MALLOC # define BENCODE_MALLOC pkg_malloc # define BENCODE_FREE pkg_free # endif #else /* mediaproxy-ng */ # include "str.h" # ifndef BENCODE_MALLOC # define BENCODE_MALLOC malloc # define BENCODE_FREE free # endif #endif struct bencode_buffer; enum bencode_type; struct bencode_item; struct __bencode_buffer_piece; struct __bencode_free_list; typedef enum bencode_type bencode_type_t; typedef struct bencode_buffer bencode_buffer_t; typedef struct bencode_item bencode_item_t; typedef void (*free_func_t)(void *); enum bencode_type { BENCODE_INVALID = 0, BENCODE_STRING, /* byte string */ BENCODE_INTEGER, /* long long int */ BENCODE_LIST, /* flat list of other objects */ BENCODE_DICTIONARY, /* dictionary of key/values pairs. keys are always strings */ BENCODE_IOVEC, /* special case of a string, built through bencode_string_iovec() */ BENCODE_END_MARKER, /* used internally only */ }; struct bencode_item { bencode_type_t type; struct iovec iov[2]; /* when decoding, iov[1] contains the contents of a string object */ unsigned int iov_cnt; unsigned int str_len; /* length of the whole ENCODED object. NOT the length of a byte string */ long long int value; /* when decoding an integer, contains the value; otherwise used internally */ bencode_item_t *parent, *child, *last_child, *sibling; bencode_buffer_t *buffer; char __buf[0]; }; struct bencode_buffer { struct __bencode_buffer_piece *pieces; struct __bencode_free_list *free_list; int error:1; /* set to !0 if allocation failed at any point */ }; /*** INIT & DESTROY ***/ /* Initializes a bencode_buffer_t object. This object is used to group together all memory allocations * made when encoding or decoding. Its memory usage is always growing, until it is freed, at which point * all objects created through it become invalid. The actual object must be allocated separately, for * example by being put on the stack. * Returns 0 on success or -1 on failure (if no memory could be allocated). */ int bencode_buffer_init(bencode_buffer_t *buf); /* Destroys a previously initialized bencode_buffer_t object. All memory used by the object is freed * and all objects created through it become invalid. */ void bencode_buffer_free(bencode_buffer_t *buf); /* Creates a new empty dictionary object. Memory will be allocated from the bencode_buffer_t object. * Returns NULL if no memory could be allocated. */ bencode_item_t *bencode_dictionary(bencode_buffer_t *buf); /* Creates a new empty list object. Memory will be allocated from the bencode_buffer_t object. * Returns NULL if no memory could be allocated. */ bencode_item_t *bencode_list(bencode_buffer_t *buf); /* Adds a pointer to the bencode_buffer_t object's internal free list. When the bencode_buffer_t * object is destroyed, the specified function will be called on this pointer. */ void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t, void *); /* Returns the buffer associated with an item, or NULL if pointer given is NULL */ static inline bencode_buffer_t *bencode_item_buffer(bencode_item_t *); /*** DICTIONARY BUILDING ***/ /* Adds a new key/value pair to a dictionary. Memory will be allocated from the same bencode_buffer_t * object as the dictionary was allocated from. Returns NULL if no memory could be allocated, otherwise * returns "val". * The function does not check whether the key being added is already present in the dictionary. * Also, the function does not reorder keys into lexicographical order; keys will be encoded in * the same order as they've been added. The key must a null-terminated string. * The value to be added must not have been previously linked into any other dictionary or list. */ static inline bencode_item_t *bencode_dictionary_add(bencode_item_t *dict, const char *key, bencode_item_t *val); /* Identical to bencode_dictionary_add() but doesn't require the key string to be null-terminated */ bencode_item_t *bencode_dictionary_add_len(bencode_item_t *dict, const char *key, int keylen, bencode_item_t *val); /* Convenience function to add a string value to a dictionary, possibly duplicated into the * bencode_buffer_t object. */ static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict, const char *key, const char *val); static inline bencode_item_t *bencode_dictionary_add_string_dup(bencode_item_t *dict, const char *key, const char *val); /* Ditto, but for a "str" object */ static inline bencode_item_t *bencode_dictionary_add_str(bencode_item_t *dict, const char *key, const str *val); static inline bencode_item_t *bencode_dictionary_add_str_dup(bencode_item_t *dict, const char *key, const str *val); /* Ditto, but adds a string created through an iovec array to the dictionary. See * bencode_string_iovec(). */ static inline bencode_item_t *bencode_dictionary_add_iovec(bencode_item_t *dict, const char *key, const struct iovec *iov, int iov_cnt, int str_len); /* Convenience functions to add the respective (newly created) objects to a dictionary */ static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val); static inline bencode_item_t *bencode_dictionary_add_dictionary(bencode_item_t *dict, const char *key); static inline bencode_item_t *bencode_dictionary_add_list(bencode_item_t *dict, const char *key); /*** LIST BUILDING ***/ /* Adds a new item to a list. Returns "item". * The item to be added must not have been previously linked into any other dictionary or list. */ bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item); /* Convenience function to add the respective (newly created) objects to a list */ static inline bencode_item_t *bencode_list_add_string(bencode_item_t *list, const char *s); static inline bencode_item_t *bencode_list_add_list(bencode_item_t *list); static inline bencode_item_t *bencode_list_add_dictionary(bencode_item_t *list); /*** STRING BUILDING & HANDLING ***/ /* Creates a new byte-string object. The given string does not have to be null-terminated, instead * the length of the string is specified by the "len" parameter. Returns NULL if no memory could * be allocated. * Strings are not copied or duplicated, so the string pointed to by "s" must remain valid until * the complete document is finally encoded or sent out. */ bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len); /* Creates a new byte-string object. The given string must be null-terminated. Otherwise identical * to bencode_string_len(). */ static inline bencode_item_t *bencode_string(bencode_buffer_t *buf, const char *s); /* Creates a new byte-string object from a "str" object. The string does not have to be null- * terminated. */ static inline bencode_item_t *bencode_str(bencode_buffer_t *buf, const str *s); /* Identical to the above three functions, but copies the string into the bencode_buffer_t object. * Thus, the given string doesn't have to remain valid and accessible afterwards. */ bencode_item_t *bencode_string_len_dup(bencode_buffer_t *buf, const char *s, int len); static inline bencode_item_t *bencode_string_dup(bencode_buffer_t *buf, const char *s); static inline bencode_item_t *bencode_str_dup(bencode_buffer_t *buf, const str *s); /* Creates a new byte-string object from an iovec array. The created object has different internal * semantics (not a BENCODE_STRING, but a BENCODE_IOVEC) and must not be treated like other string * objects. The array pointer and contents must still be valid and accessible when the complete * document is encoded. The full length of the string composed of the iovec array is given in the * "str_len" parameter, which can be negative, in which case the array is iterated to calculate the * length. */ bencode_item_t *bencode_string_iovec(bencode_buffer_t *buf, const struct iovec *iov, int iov_cnt, int str_len); /* Convenience function to compare a string object to a regular C string. Returns 2 if object * isn't a string object, otherwise returns according to strcmp(). */ static inline int bencode_strcmp(bencode_item_t *a, const char *b); /* Converts the string object "in" into a str object "out". Returns "out" on success, or NULL on * error ("in" was NULL or not a string object). */ static inline str *bencode_get_str(bencode_item_t *in, str *out); /*** INTEGER BUILDING ***/ /* Creates a new integer object. Returns NULL if no memory could be allocated. */ bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i); /*** COLLAPSING & ENCODING ***/ /* Collapses and encodes the complete document structure under the "root" element (which normally * is either a dictionary or a list) into an array of "iovec" structures. This array can then be * passed to functions ala writev() or sendmsg() to output the encoded document as a whole. Memory * is allocated from the same bencode_buffer_t object as the "root" object was allocated from. * The "head" and "tail" parameters specify additional "iovec" structures that should be included * in the allocated array before or after (respectively) the iovec structures used by the encoded * document. Both parameters can be zero if no additional elements in the array are required. * Returns a pointer to the allocated array or NULL if no memory could be allocated. The number of * array elements is returned in "cnt" which must be a valid pointer to an int. This number does * not include any additional elements allocated through the "head" or "tail" parameters. * * Therefore, the contents of the returned array are: * [0 .. (head - 1)] = unused and uninitialized iovec structures * [(head) .. (head + cnt - 1)] = the encoded document * [(head + cnt) .. (head + cnt + tail - 1)] = unused and uninitialized iovec structures * * The returned array will be freed when the corresponding bencode_buffer_t object is destroyed. */ struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, unsigned int tail); /* Similar to bencode_iovec(), but instead returns the encoded document as a null-terminated string. * Memory for the string is allocated from the same bencode_buffer_t object as the "root" object * was allocated from. If "len" is a non-NULL pointer, the length of the genrated string is returned * in *len. This is important if the encoded document contains binary data, in which case null * termination cannot be trusted. The returned string is freed when the corresponding * bencode_buffer_t object is destroyed. */ char *bencode_collapse(bencode_item_t *root, int *len); /* Identical to bencode_collapse() but fills in a "str" object. Returns "out". */ static str *bencode_collapse_str(bencode_item_t *root, str *out); /* Identical to bencode_collapse(), but the memory for the returned string is not allocated from * a bencode_buffer_t object, but instead using the function defined as BENCODE_MALLOC (normally * malloc() or pkg_malloc()), similar to strdup(). Using this function, the bencode_buffer_t * object can be destroyed, but the returned string remains valid and usable. */ char *bencode_collapse_dup(bencode_item_t *root, int *len); /*** DECODING ***/ /* Decodes an encoded document from a string into a tree of bencode_item_t objects. The string does * not need to be null-terminated, instead the length of the string is given through the "len" * parameter. Memory is allocated from the bencode_buffer_t object. Returns NULL if no memory could * be allocated or if the document could not be successfully decoded. * * The returned element is the "root" of the document tree and normally is either a list object or * a dictionary object, but can also be a single string or integer object with no other objects * underneath or besides it (no childred and no siblings). The type of the object can be determined * by its ->type property. * * The number of bytes that could successfully be decoded into an object tree can be accessed through * the root element's ->str_len property. Normally, this number should be equal to the "len" parameter * passed, in which case the full string could be decoded. If ->str_len is less than "len", then there * was additional stray byte data after the end of the encoded document. * * The document tree can be traversed through the ->child and ->sibling pointers in each object. The * ->child pointer will be NULL for string and integer objects, as they don't contain other objects. * For lists and dictionaries, ->child will be a pointer to the first contained object. This first * contained object's ->sibling pointer will point to the next (second) contained object of the list * or the dictionary, and so on. The last contained element of a list of dictionary will have a * NULL ->sibling pointer. * * Dictionaries are like lists with ordered key/value pairs. When traversing dictionaries like * lists, the following applies: The first element in the list (where ->child points to) will be the * key of the first key/value pair (guaranteed to be a string and guaranteed to be present). The * next element (following one ->sibling) will be the value of the first key/value pair. Following * another ->sibling will point to the key of the next (second) key/value pair, and so on. * * However, to access children objects of dictionaries, the special functions following the naming * scheme bencode_dictionary_get_* below should be used. They perform key lookup through a simple * hash built into the dictionary object and so perform the lookup much faster. Only dictionaries * created through a decoding process (i.e. not ones created from bencode_dictionary()) have this * property. The hash is efficient only up to a certain number of elements (BENCODE_HASH_BUCKETS * in bencode.c) contained in the dictionary. If the number of children object exceeds this number, * key lookup will be slower than simply linearily traversing the list. * * The decoding function for dictionary object does not check whether keys are unique within the * dictionary. It also does not care about lexicographical order of the keys. * * Decoded string objects will contain the raw decoded byte string in ->iov[1] (including the correct * length). Strings are NOT null-terminated. Decoded integer objects will contain the decoded value * in ->value. * * All memory is freed when the bencode_buffer_t object is destroyed. */ bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, int len); /* Identical to bencode_decode(), but returns successfully only if the type of the decoded object match * "expect". */ static inline bencode_item_t *bencode_decode_expect(bencode_buffer_t *buf, const char *s, int len, bencode_type_t expect); /* Identical to bencode_decode_expect() but takes a "str" argument. */ static inline bencode_item_t *bencode_decode_expect_str(bencode_buffer_t *buf, const str *s, bencode_type_t expect); /*** DICTIONARY LOOKUP & EXTRACTION ***/ /* Searches the given dictionary object for the given key and returns the respective value. Returns * NULL if the given object isn't a dictionary or if the key doesn't exist. The key must be a * null-terminated string. */ static inline bencode_item_t *bencode_dictionary_get(bencode_item_t *dict, const char *key); /* Identical to bencode_dictionary_get() but doesn't require the key to be null-terminated. */ bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *key, int key_len); /* Identical to bencode_dictionary_get() but returns the value only if its type is a string, and * returns it as a pointer to the string itself. Returns NULL if the value is of some other type. The * returned string is NOT null-terminated. Length of the string is returned in *len, which must be a * valid pointer. The returned string will be valid until dict's bencode_buffer_t object is destroyed. */ static inline char *bencode_dictionary_get_string(bencode_item_t *dict, const char *key, int *len); /* Identical to bencode_dictionary_get_string() but fills in a "str" struct. Returns str->s, which * may be NULL. */ static inline char *bencode_dictionary_get_str(bencode_item_t *dict, const char *key, str *str); /* Looks up the given key in the dictionary and compares the corresponding value to the given * null-terminated string. Returns 2 if the key isn't found or if the value isn't a string, otherwise * returns according to strcmp(). */ static inline int bencode_dictionary_get_strcmp(bencode_item_t *dict, const char *key, const char *str); /* Identical to bencode_dictionary_get() but returns the string in a newly allocated buffer (using the * BENCODE_MALLOC function), which remains valid even after bencode_buffer_t is destroyed. */ static inline char *bencode_dictionary_get_string_dup(bencode_item_t *dict, const char *key, int *len); /* Combines bencode_dictionary_get_str() and bencode_dictionary_get_string_dup(). Fills in a "str" * struct, but copies the string into a newly allocated buffer. Returns str->s. */ static inline char *bencode_dictionary_get_str_dup(bencode_item_t *dict, const char *key, str *str); /* Identical to bencode_dictionary_get_string() but expects an integer object. The parameter "defval" * specified which value should be returned if the key is not found or if the value is not an integer. */ static inline long long int bencode_dictionary_get_integer(bencode_item_t *dict, const char *key, long long int defval); /* Identical to bencode_dictionary_get(), but returns the object only if its type matches "expect". */ static inline bencode_item_t *bencode_dictionary_get_expect(bencode_item_t *dict, const char *key, bencode_type_t expect); /**************************/ static inline bencode_buffer_t *bencode_item_buffer(bencode_item_t *i) { if (!i) return NULL; return i->buffer; } static inline bencode_item_t *bencode_string(bencode_buffer_t *buf, const char *s) { return bencode_string_len(buf, s, strlen(s)); } static inline bencode_item_t *bencode_string_dup(bencode_buffer_t *buf, const char *s) { return bencode_string_len_dup(buf, s, strlen(s)); } static inline bencode_item_t *bencode_str(bencode_buffer_t *buf, const str *s) { return bencode_string_len(buf, s->s, s->len); } static inline bencode_item_t *bencode_str_dup(bencode_buffer_t *buf, const str *s) { return bencode_string_len_dup(buf, s->s, s->len); } static inline bencode_item_t *bencode_dictionary_add(bencode_item_t *dict, const char *key, bencode_item_t *val) { if (!key) return NULL; return bencode_dictionary_add_len(dict, key, strlen(key), val); } static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict, const char *key, const char *val) { if (!val) return NULL; return bencode_dictionary_add(dict, key, bencode_string(bencode_item_buffer(dict), val)); } static inline bencode_item_t *bencode_dictionary_add_string_dup(bencode_item_t *dict, const char *key, const char *val) { if (!val) return NULL; return bencode_dictionary_add(dict, key, bencode_string_dup(bencode_item_buffer(dict), val)); } static inline bencode_item_t *bencode_dictionary_add_str(bencode_item_t *dict, const char *key, const str *val) { if (!val) return NULL; return bencode_dictionary_add(dict, key, bencode_str(bencode_item_buffer(dict), val)); } static inline bencode_item_t *bencode_dictionary_add_str_dup(bencode_item_t *dict, const char *key, const str *val) { if (!val) return NULL; return bencode_dictionary_add(dict, key, bencode_str_dup(bencode_item_buffer(dict), val)); } static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val) { return bencode_dictionary_add(dict, key, bencode_integer(bencode_item_buffer(dict), val)); } static inline bencode_item_t *bencode_dictionary_add_dictionary(bencode_item_t *dict, const char *key) { return bencode_dictionary_add(dict, key, bencode_dictionary(bencode_item_buffer(dict))); } static inline bencode_item_t *bencode_dictionary_add_list(bencode_item_t *dict, const char *key) { return bencode_dictionary_add(dict, key, bencode_list(bencode_item_buffer(dict))); } static inline bencode_item_t *bencode_list_add_string(bencode_item_t *list, const char *s) { return bencode_list_add(list, bencode_string(bencode_item_buffer(list), s)); } static inline bencode_item_t *bencode_list_add_list(bencode_item_t *list) { return bencode_list_add(list, bencode_list(bencode_item_buffer(list))); } static inline bencode_item_t *bencode_list_add_dictionary(bencode_item_t *list) { return bencode_list_add(list, bencode_dictionary(bencode_item_buffer(list))); } static inline bencode_item_t *bencode_dictionary_get(bencode_item_t *dict, const char *key) { if (!key) return NULL; return bencode_dictionary_get_len(dict, key, strlen(key)); } static inline char *bencode_dictionary_get_string(bencode_item_t *dict, const char *key, int *len) { bencode_item_t *val; val = bencode_dictionary_get(dict, key); if (!val || val->type != BENCODE_STRING) return NULL; *len = val->iov[1].iov_len; return val->iov[1].iov_base; } static inline char *bencode_dictionary_get_str(bencode_item_t *dict, const char *key, str *str) { str->s = bencode_dictionary_get_string(dict, key, &str->len); if (!str->s) str->len = 0; return str->s; } static inline char *bencode_dictionary_get_string_dup(bencode_item_t *dict, const char *key, int *len) { const char *s; char *ret; s = bencode_dictionary_get_string(dict, key, len); if (!s) return NULL; ret = BENCODE_MALLOC(*len); if (!ret) return NULL; memcpy(ret, s, *len); return ret; } static inline char *bencode_dictionary_get_str_dup(bencode_item_t *dict, const char *key, str *str) { str->s = bencode_dictionary_get_string_dup(dict, key, &str->len); return str->s; } static inline long long int bencode_dictionary_get_integer(bencode_item_t *dict, const char *key, long long int defval) { bencode_item_t *val; val = bencode_dictionary_get(dict, key); if (!val || val->type != BENCODE_INTEGER) return defval; return val->value; } static inline bencode_item_t *bencode_decode_expect(bencode_buffer_t *buf, const char *s, int len, bencode_type_t expect) { bencode_item_t *ret; ret = bencode_decode(buf, s, len); if (!ret || ret->type != expect) return NULL; return ret; } static inline bencode_item_t *bencode_decode_expect_str(bencode_buffer_t *buf, const str *s, bencode_type_t expect) { return bencode_decode_expect(buf, s->s, s->len, expect); } static inline bencode_item_t *bencode_dictionary_get_expect(bencode_item_t *dict, const char *key, bencode_type_t expect) { bencode_item_t *ret; ret = bencode_dictionary_get(dict, key); if (!ret || ret->type != expect) return NULL; return ret; } static inline str *bencode_collapse_str(bencode_item_t *root, str *out) { out->s = bencode_collapse(root, &out->len); return out; } static inline int bencode_strcmp(bencode_item_t *a, const char *b) { int len; if (a->type != BENCODE_STRING) return 2; len = strlen(b); if (a->iov[1].iov_len < len) return -1; if (a->iov[1].iov_len > len) return 1; return memcmp(a->iov[1].iov_base, b, len); } static inline int bencode_dictionary_get_strcmp(bencode_item_t *dict, const char *key, const char *str) { bencode_item_t *i; i = bencode_dictionary_get(dict, key); if (!i) return 2; return bencode_strcmp(i, str); } static inline str *bencode_get_str(bencode_item_t *in, str *out) { if (!in || in->type != BENCODE_STRING) return NULL; out->s = in->iov[1].iov_base; out->len = in->iov[1].iov_len; return out; } static inline bencode_item_t *bencode_dictionary_add_iovec(bencode_item_t *dict, const char *key, const struct iovec *iov, int iov_cnt, int str_len) { return bencode_dictionary_add(dict, key, bencode_string_iovec(bencode_item_buffer(dict), iov, iov_cnt, str_len)); } #endif opensips-2.2.2/modules/rtpengine/doc/000077500000000000000000000000001300170765700175735ustar00rootroot00000000000000opensips-2.2.2/modules/rtpengine/doc/rtpengine.xml000066400000000000000000000046371300170765700223220ustar00rootroot00000000000000 %docentities; ]> rtpengine Module &osipsname; Maxim Sobolev Sippy Software, Inc.
sobomax@sippysoft.com
Juha Heinanen TuTPro, Inc.
jh@tutpro.com
Maxim Sobolev
sobomax@sippysoft.com
Bogdan-Andrei Iancu
bogdan@voice-system.ro
Juha Heinanen
jh@tutpro.com
Sas Ovidiu
osas@voipembedded.com
Carsten Bock ng-voice GmbH
carsten@ng-voice.com
Richard Fuchs Sipwise GmbH
rfuchs@sipwise.com
2003-2008 Sippy Software, Inc. 2005 Voice Sistem SRL 2009-2014 TuTPro Inc. 2010 VoIPEmbedded Inc. 2013-2014 Sipwise GmbH
&admin; &faq;
opensips-2.2.2/modules/rtpengine/doc/rtpengine_admin.xml000066400000000000000000000617111300170765700234660ustar00rootroot00000000000000 &adminguide;
Overview This is a module that enables media streams to be proxied via an &rtp; proxy. The only &rtp; proxy currently known to work with this module is the Sipwise rtpengine . The rtpengine module is a modified version of the original rtpproxy module using a new control protocol. The module is designed to be a drop-in replacement for the old module from a configuration file point of view, however due to the incompatible control protocol, it only works with &rtp; proxies which specifically support it.
Multiple &rtp; proxy usage The rtpengine module can support multiple &rtp; proxies for balancing/distribution and control/selection purposes. The module allows definition of several sets of rtpengines. Load-balancing will be performed over a set and the admin has the ability to choose what set should be used. The set is selected via its id - the id being defined with the set. Refer to the rtpengine_sock module parameter definition for syntax description. The balancing inside a set is done automatically by the module based on the weight of each &rtp; proxy from the set. The selection of the set is done from script prior using rtpengine_delete(), rtpengine_offer() or rtpengine_answer() functions - see the rtpengine_use_set() function. Another way to select the set is to define setid_avp module parameter and assign setid to the defined avp before calling rtpengine_offer() or rtpengine_manage() function. If forwarding of the requests fails and there is another branch to try, remember to unset the avp after calling rtpengine_delete() function. For backward compatibility reasons, a set with no id take by default the id 0. Also if no set is explicitly set before rtpengine_delete(), rtpengine_offer() or rtpengine_answer() the 0 id set will be used. IMPORTANT: if you use multiple sets, take care and use the same set for both rtpengine_offer()/rtpengine_answer() and rtpengine_delete()!! If the set was selected using setid_avp, the avp needs to be set only once before rtpengine_offer() or rtpengine_manage() call.
Dependencies
&osips; Modules The following modules must be loaded before this module: tm module - (optional) if you want to have rtpengine_manage() fully functional
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Parameters
<varname>rtpengine_sock</varname> (string) Definition of socket(s) used to connect to (a set) &rtp; proxy. It may specify a UNIX socket or an IPv4/IPv6 UDP socket. Default value is NONE (disabled). Set <varname>rtpengine_sock</varname> parameter ... # single rtproxy modparam("rtpengine", "rtpengine_sock", "udp:localhost:12221") # multiple rtproxies for LB modparam("rtpengine", "rtpengine_sock", "udp:localhost:12221 udp:localhost:12222") # multiple sets of multiple rtproxies modparam("rtpengine", "rtpengine_sock", "1 == udp:localhost:12221 udp:localhost:12222") modparam("rtpengine", "rtpengine_sock", "2 == udp:localhost:12225") ...
<varname>rtpengine_disable_tout</varname> (integer) Once an &rtp; proxy was found unreachable and marked as disabled, the rtpengine module will not attempt to establish communication to that &rtp; proxy for rtpengine_disable_tout seconds. Default value is 60. Set <varname>rtpengine_disable_tout</varname> parameter ... modparam("rtpengine", "rtpengine_disable_tout", 20) ...
<varname>rtpengine_tout</varname> (integer) Timeout value in waiting for reply from &rtp; proxy. Default value is 1. Set <varname>rtpengine_tout</varname> parameter ... modparam("rtpengine", "rtpengine_tout", 2) ...
<varname>rtpengine_retr</varname> (integer) How many times the module should retry to send and receive after timeout was generated. Default value is 5. Set <varname>rtpengine_retr</varname> parameter ... modparam("rtpengine", "rtpengine_retr", 2) ...
<varname>extra_id_pv</varname> (string) The parameter sets the PV defination to use when the b parameter is used on rtpengine_delete(), rtpengine_offer(), rtpengine_answer() or rtpengine_manage() command. Default is empty, the b parameter may not be used then. Set <varname>extra_id_pv</varname> parameter ... modparam("rtpengine", "extra_id_pv", "$avp(extra_id)") ...
<varname>setid_avp</varname> (string) The parameter defines an AVP that, if set, determines which &rtp; proxy set rtpengine_offer(), rtpengine_answer(), rtpengine_delete(), and rtpengine_manage() functions use. There is no default value. Set <varname>setid_avp</varname> parameter ... modparam("rtpengine", "setid_avp", "$avp(setid)") ...
Functions
<function moreinfo="none">rtpengine_use_set(setid)</function> Sets the ID of the &rtp; proxy set to be used for the next rtpengine_delete(), rtpengine_offer(), rtpengine_answer() or rtpengine_manage() command. The parameter can be an integer or a config variable holding an integer. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE. <function>rtpengine_use_set</function> usage ... rtpengine_use_set("2"); rtpengine_offer(); ...
<function moreinfo="none">rtpengine_offer([flags])</function> Rewrites &sdp; body to ensure that media is passed through an &rtp; proxy. To be invoked on INVITE for the cases the SDPs are in INVITE and 200 OK and on 200 OK when SDPs are in 200 OK and ACK. Meaning of the parameters is as follows: flags - flags to turn on some features. The flags string is a list of space-separated items. Each item is either an individual token, or a token in key=value format. The possible tokens are described below. via-branch=... - Include the branch value of one of the Via headers in the request to the &rtp; proxy. Possible values are: 1 - use the first Via header; 2 - use the second Via header; auto - use the first Via header if this is a request, or the second one if this is a reply; extra - don't take the value from a header, but instead use the value of the extra_id_pv variable. This can be used to create one media session per branch on the &rtp; proxy. When sending a subsequent delete command to the &rtp; proxy, you can then stop just the session for a specific branch when passing the flag '1' or '2' in the rtpengine_delete, or stop all sessions for a call when not passing one of those two flags there. This is especially useful if you have serially forked call scenarios where the &rtp; proxy gets an offer command for a new branch, and then a delete command for the previous branch, which would otherwise delete the full call, breaking the subsequent answer for the new branch. This flag is only supported by the Sipwise rtpengine &rtp; proxy at the moment! asymmetric - flags that UA from which message is received doesn't support symmetric RTP. (automatically sets the 'r' flag) force-answer - force answer, that is, only rewrite &sdp; when corresponding session already exists in the &rtp; proxy. By default is on when the session is to be completed. internal, external - these flags specify the direction of the SIP message. These flags only make sense when the &rtp; proxy is running in bridge mode. internal corresponds to the proxy's first interface, external corresponds to the &rtp; proxy's second interface. You always have to specify two flags to define the incoming network and the outgoing network. For example, internal external should be used for SIP message received from the local interface and sent out on the external interface, and external internal vice versa. Other options are internal internal and external external. So, for example if a SIP requests is processed with internal external flags, the corresponding response must be processed with internal external flags. auto-bridge - this flag an alternative to the internal and external flags in order to do automatic bridging between IPv4 on the "internal network" and IPv6 on the "external network". Instead of explicitly instructing the &rtp; proxy to select a particular address family, the distinction is done by the given IP in the SDP body by the RTP proxy itself. Not supported by Sipwise rtpengine. address-family=... - instructs the &rtp; proxy that the recipient of this &sdp; body expects to see addresses of a particular family. Possible values are IP4 and IP6. For example, if the &sdp; body contains IPv4 addresses but the recipient only speaks IPv6, you would use address-family=IP6 to bridge between the two address families. Sipwise rtpengine remembers the address family preference of each party after it has seen an &sdp; body from them. This means that normally it is only necessary to explicitly specify the address family in the offer, but not in the answer. Note: Please note, that this will only work properly with non-dual-stack user-agents or with dual-stack clients according to RFC6157 (which suggest ICE for Dual-Stack implementations). This short-cut will not work properly with RFC4091 (ANAT) compatible clients, which suggests having different m-lines with different IP-protocols grouped together. force - instructs the &rtp; proxy to ignore marks inserted by another &rtp; proxy in transit to indicate that the session is already goes through another proxy. Allows creating a chain of proxies. Not supported and ignored by Sipwise rtpengine. trust-address - flags that IP address in SDP should be trusted. Without this flag, the &rtp; proxy ignores address in the SDP and uses source address of the SIP message as media address which is passed to the RTP proxy. replace-origin - flags that IP from the origin description (o=) should be also changed. replace-session-connection - flags to change the session-level SDP connection (c=) IP if media description also includes connection information. symmetric - flags that for the UA from which message is received, support symmetric RTP must be forced. repacketize=NN - requests the &rtp; proxy to perform re-packetization of RTP traffic coming from the UA which has sent the current message to increase or decrease payload size per each RTP packet forwarded if possible. The NN is the target payload size in ms, for the most codecs its value should be in 10ms increments, however for some codecs the increment could differ (e.g. 30ms for GSM or 20ms for G.723). The &rtp; proxy would select the closest value supported by the codec. This feature could be used for significantly reducing bandwith overhead for low bitrate codecs, for example with G.729 going from 10ms to 100ms saves two thirds of the network bandwith. Not supported by Sipwise rtpengine. ICE=... - controls the &rtp; proxy's behaviour regarding ICE attributes within the &sdp; body. Possible values are: force - discard any ICE attributes already present in the &sdp; body and then generate and insert new ICE data, leaving itself as the only ICE candidates; remove instructs the &rtp; proxy to discard any ICE attributes and not insert any new ones into the &sdp;. The default (if no ICE=... is given at all), new ICE data will only be generated if no ICE was present in the &sdp; originally; otherwise the &rtp; proxy will only insert itself as an additional ICE candidate. Other &sdp; substitutions (c=, m=, etc) are unaffected by this flag. RTP, SRTP, AVP, AVPF - These flags control the &rtp; transport protocol that should be used towards the recipient of the &sdp;. If none of them are specified, the protocol given in the &sdp; is left untouched. Otherwise, the SRTP flag indicates that SRTP should be used, while RTP indicates that SRTP should not be used. AVPF indicates that the advanced RTCP profile with feedback messages should be used, and AVP indicates that the regular RTCP profile should be used. See also the next set of flags below. RTP/AVP, RTP/SAVP, RTP/AVPF, RTP/SAVPF - these serve as an alternative, more explicit way to select between the different &rtp; protocols and profiles supported by the &rtp; proxy. For example, giving the flag RTP/SAVPF has the same effect as giving the two flags SRTP AVPF. to-tag - force inclusion of the To tag. Normally, the To tag is always included when present, except for delete messages. Including the To tag in a delete messages allows you to be more selective about which dialogues within a call are being torn down. rtcp-mux-demux - if rtcp-mux (RFC 5761) was offered, make the &rtp; proxy accept the offer, but not offer it to the recipient of this message. rtcp-mux-reject - if rtcp-mux was offered, make the &rtp; proxy reject the offer, but still offer it to the recipient. Can be combined with rtcp-mux-offer to always offer it. rtcp-mux-offer - make the &rtp; proxy offer rtcp-mux to the recipient of this message, regardless of whether it was offered originally or not. rtcp-mux-accept - if rtcp-mux was offered, make the &rtp; proxy accept the offer and also offer it to the recipient of this message. Can be combined with rtcp-mux-offer to always offer it. media-address=... - force a particular media address to be used in the &sdp; body. Address family is detected automatically. This function can be used from ANY_ROUTE. <function>rtpengine_offer</function> usage route { ... if (is_method("INVITE")) { if (has_body("application/sdp")) { if (rtpengine_offer()) t_on_reply("1"); } else { t_on_reply("2"); } } if (is_method("ACK") && has_body("application/sdp")) rtpengine_answer(); ... } onreply_route[1] { ... if (has_body("application/sdp")) rtpengine_answer(); ... } onreply_route[2] { ... if (has_body("application/sdp")) rtpengine_offer(); ... }
<function moreinfo="none">rtpengine_answer([flags])</function> Rewrites &sdp; body to ensure that media is passed through an &rtp; proxy. To be invoked on 200 OK for the cases the SDPs are in INVITE and 200 OK and on ACK when SDPs are in 200 OK and ACK. See rtpengine_offer() function description above for the meaning of the parameters. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>rtpengine_answer</function> usage See rtpengine_offer() function example above for example.
<function moreinfo="none">rtpengine_delete([flags])</function> Tears down the RTPProxy session for the current call. See rtpengine_offer() function description above for the meaning of the parameters. Note that not all flags make sense for a delete. This function can be used from ANY_ROUTE. <function>rtpengine_delete</function> usage ... rtpengine_delete(); ...
<function moreinfo="none">rtpengine_manage([flags])</function> Manage the RTPProxy session - it combines the functionality of rtpengine_offer(), rtpengine_answer() and rtpengine_delete(), detecting internally based on message type and method which one to execute. It can take the same parameters as rtpengine_offer(). The flags parameter to rtpengine_manage() can be a configuration variable containing the flags as a string. Functionality: If INVITE with SDP, then do rtpengine_offer() If INVITE with SDP, when the tm module is loaded, mark transaction with internal flag FL_SDP_BODY to know that the 1xx and 2xx are for rtpengine_answer() If ACK with SDP, then do rtpengine_answer() If BYE or CANCEL, or called within a FAILURE_ROUTE[], then do rtpengine_delete() If reply to INVITE with code >= 300 do rtpengine_delete() If reply with SDP to INVITE having code 1xx and 2xx, then do rtpengine_answer() if the request had SDP or tm is not loaded, otherwise do rtpengine_offer() This function can be used from ANY_ROUTE. <function>rtpengine_manage</function> usage ... rtpengine_manage(); ...
<function moreinfo="none">rtpengine_start_recording()</function> This function will send a signal to the &rtp; proxy to record the RTP stream on the &rtp; proxy. This function is not supported by Sipwise rtpengine at the moment! This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. <function>rtpengine_start_recording</function> usage ... rtpengine_start_recording(); ...
Exported Pseudo Variables
<function moreinfo="none">$rtpstat</function> Returns the &rtp; statistics from the &rtp; proxy. The &rtp; statistics from the &rtp; proxy are provided as a string and it does contain several packet counters. The statistics must be retrieved before the session is deleted (before rtpengine_delete()). $rtpstat Usage ... append_hf("X-RTP-Statistics: $rtpstat\r\n"); ...
<acronym>MI</acronym> Commands
<function moreinfo="none">rtpengine_enable</function> Enables a &rtp; proxy if parameter value is greater than 0. Disables it if a zero value is given. The first parameter is the &rtp; proxy url (exactly as defined in the config file). The second parameter value must be a number in decimal. NOTE: if a &rtp; proxy is defined multiple times (in the same or diferente sete), all of its instances will be enables/disabled. <function moreinfo="none">rtpengine_enable</function> usage ... $ opensipsctl fifo rtpengine_enable udp:192.168.2.133:8081 0 ...
<function moreinfo="none">rtpengine_show</function> Displays all the &rtp; proxies and their information: set and status (disabled or not, weight and recheck_ticks). No parameter. <function moreinfo="none">rtpengine_show</function> usage ... $ opensipsctl fifo rtpengine_show ...
opensips-2.2.2/modules/rtpengine/doc/rtpengine_faq.xml000066400000000000000000000056021300170765700231420ustar00rootroot00000000000000 &faqguide; How do I migrate from rtpproxy or rtpproxy-ng to rtpengine? For the most part, only the names of the functions have changed, with rtpproxy in each name replaced with rtpengine. For example, rtpproxy_manage() has become rtpengine_manage(). A few name duplications have also been resolved, for example there is now a single rtpengine_delete() instead of unforce_rtp_proxy() and the identical rtpproxy_destroy(). The largest difference to the old module is how flags are passed to rtpengine_offer(), rtpengine_answer(), rtpengine_manage() and rtpengine_delete(). Instead of having a string of single-letter flags, they now take a string of space-separated items, with each item being either a single token (word) or a key=value pair. For example, if you had a call rtpproxy_offer("FRWOC+PS");, this would then become: rtpengine_offer("force trust-address symmetric replace-origin replace-session-connection ICE=force RTP/SAVPF"); Finally, if you were using the second paramater (explicit media address) to any of these functions, this has been replaced by the media-address=... option within the first string of flags. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/rtpengine/rtpengine.c000066400000000000000000001351531300170765700211750ustar00rootroot00000000000000/* * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2014-06-17 Imported from rtpproxy module */ #include #include #include #include #include #ifndef __USE_BSD #define __USE_BSD #endif #include #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "../../str.h" #include "../../flags.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../error.h" #include "../../forward.h" #include "../../context.h" #include "../../mem/mem.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../parser/parse_uri.h" #include "../../parser/parser_f.h" #include "../../parser/sdp/sdp.h" #include "../../resolve.h" #include "../../timer.h" #include "../../trim.h" #include "../../ut.h" #include "../../pt.h" #include "../../pvar.h" #include "../../msg_translator.h" #include "../../usr_avp.h" #include "../../socket_info.h" #include "../../mod_fix.h" #include "../../dset.h" #include "../../route.h" #include "../../modules/tm/tm_load.h" #include "rtpengine.h" #include "rtpengine_funcs.h" #include "bencode.h" #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #if !defined(PF_LOCAL) #define PF_LOCAL PF_UNIX #endif #define DEFAULT_RTPE_SET_ID 0 #define MI_ENABLE_RTP_ENGINE "rtpengine_enable" #define MI_MIN_RECHECK_TICKS 0 #define MI_MAX_RECHECK_TICKS (unsigned int)-1 #define MI_SHOW_RTP_ENGINES "rtpengine_show" #define MI_RTP_ENGINE_NOT_FOUND "RTP engine not found" #define MI_RTP_ENGINE_NOT_FOUND_LEN (sizeof(MI_RTP_ENGINE_NOT_FOUND)-1) #define MI_SET "Set" #define MI_SET_LEN (sizeof(MI_SET)-1) #define MI_NODE "node" #define MI_NODE_LEN (sizeof(MI_NODE)-1) #define MI_INDEX "index" #define MI_INDEX_LEN (sizeof(MI_INDEX)-1) #define MI_DISABLED "disabled" #define MI_DISABLED_LEN (sizeof(MI_DISABLED)-1) #define MI_WEIGHT "weight" #define MI_WEIGHT_LEN (sizeof(MI_WEIGHT)-1) #define MI_RECHECK_TICKS "recheck_ticks" #define MI_RECHECK_T_LEN (sizeof(MI_RECHECK_TICKS)-1) #define CPORT "22222" #define ctx_rtpeset_get() \ ((struct rtpe_set*)context_get_ptr(CONTEXT_GLOBAL, current_processing_ctx, ctx_rtpeset_idx)) #define ctx_rtpeset_set(_set) \ context_put_ptr(CONTEXT_GLOBAL, current_processing_ctx, ctx_rtpeset_idx, _set) enum rtpe_operation { OP_OFFER = 1, OP_ANSWER, OP_DELETE, OP_START_RECORDING, OP_QUERY, }; struct ng_flags_parse { int via, to, packetize, transport; bencode_item_t *dict, *flags, *direction, *replace, *rtcp_mux; }; typedef struct rtpe_set_link { struct rtpe_set *rset; pv_spec_t rpv; } rtpe_set_link_t; static const char *command_strings[] = { [OP_OFFER] = "offer", [OP_ANSWER] = "answer", [OP_DELETE] = "delete", [OP_START_RECORDING] = "start recording", [OP_QUERY] = "query", }; static char *gencookie(); static int rtpe_test(struct rtpe_node*, int, int); static int start_recording_f(struct sip_msg *); static int rtpengine_answer1_f(struct sip_msg *, gparam_p str1); static int rtpengine_offer1_f(struct sip_msg *, gparam_p str1); static int rtpengine_delete1_f(struct sip_msg *, gparam_p str1); static int rtpengine_manage1_f(struct sip_msg *, gparam_p str1); static int parse_flags(struct ng_flags_parse *, struct sip_msg *, enum rtpe_operation *, const char *); static int rtpengine_offer_answer(struct sip_msg *msg, const char *flags, int op); static int add_rtpengine_socks(struct rtpe_set * rtpe_list, char * rtpengine); static int fixup_set_id(void ** param, int param_no); static int set_rtpengine_set_f(struct sip_msg * msg, rtpe_set_link_t *set_param); static struct rtpe_set * select_rtpe_set(int id_set); static struct rtpe_node *select_rtpe_node(str, int, struct rtpe_set *); static char *send_rtpe_command(struct rtpe_node *, bencode_item_t *, int *); static int get_extra_id(struct sip_msg* msg, str *id_str); static int rtpengine_set_store(modparam_t type, void * val); static int rtpengine_add_rtpengine_set( char * rtp_proxies); static int mod_init(void); static int child_init(int); static void mod_destroy(void); /* Pseudo-Variables */ static int pv_get_rtpstat_f(struct sip_msg *, pv_param_t *, pv_value_t *); /*mi commands*/ static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree, void* param ); static struct mi_root* mi_show_rtpengines(struct mi_root* cmd_tree, void* param); static int rtpengine_disable_tout = 60; static int rtpengine_retr = 5; static int rtpengine_tout = 1; static pid_t mypid; static unsigned int myseqn = 0; static str extra_id_pv_param = {NULL, 0}; static char *setid_avp_param = NULL; static char ** rtpe_strings=0; static int rtpe_sets=0; /*used in rtpengine_set_store()*/ static int rtpe_set_count = 0; static int ctx_rtpeset_idx = -1; /* RTP proxy balancing list */ struct rtpe_set_head * rtpe_set_list =0; struct rtpe_set * default_rtpe_set=0; /* array with the sockets used by rtpengine (per process)*/ static unsigned int rtpe_no = 0; static int *rtpe_socks = 0; static int setid_avp_type; static int_str setid_avp; /* tm */ static struct tm_binds tmb; /*0-> disabled, 1 ->enabled*/ unsigned int *natping_state=0; static pv_elem_t *extra_id_pv = NULL; #define ANY_ROUTE (REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE) static cmd_export_t cmds[] = { {"rtpengine_use_set", (cmd_function)set_rtpengine_set_f, 1, fixup_set_id, 0, ANY_ROUTE}, {"rtpengine_start_recording", (cmd_function)start_recording_f, 0, 0, 0, ANY_ROUTE }, {"rtpengine_offer", (cmd_function)rtpengine_offer1_f, 0, 0, 0, ANY_ROUTE}, {"rtpengine_offer", (cmd_function)rtpengine_offer1_f, 1, fixup_spve_null, 0, ANY_ROUTE}, {"rtpengine_answer", (cmd_function)rtpengine_answer1_f, 0, 0, 0, ANY_ROUTE}, {"rtpengine_answer", (cmd_function)rtpengine_answer1_f, 1, fixup_spve_null, 0, ANY_ROUTE}, {"rtpengine_manage", (cmd_function)rtpengine_manage1_f, 0, 0, 0, ANY_ROUTE}, {"rtpengine_manage", (cmd_function)rtpengine_manage1_f, 1, fixup_spve_null, 0, ANY_ROUTE}, {"rtpengine_delete", (cmd_function)rtpengine_delete1_f, 0, 0, 0, ANY_ROUTE}, {"rtpengine_delete", (cmd_function)rtpengine_delete1_f, 1, fixup_spve_null, 0, ANY_ROUTE}, {0, 0, 0, 0, 0, 0} }; static pv_export_t mod_pvs[] = { {{"rtpstat", (sizeof("rtpstat")-1)}, /* RTP-Statistics */ 1000, pv_get_rtpstat_f, 0, 0, 0, 0, 0}, {{0, 0}, 0, 0, 0, 0, 0, 0, 0} }; static param_export_t params[] = { {"rtpengine_sock", STR_PARAM|USE_FUNC_PARAM, (void*)rtpengine_set_store }, {"rtpengine_disable_tout", INT_PARAM, &rtpengine_disable_tout }, {"rtpengine_retr", INT_PARAM, &rtpengine_retr }, {"rtpengine_tout", INT_PARAM, &rtpengine_tout }, {"extra_id_pv", STR_PARAM, &extra_id_pv_param.s }, {"setid_avp", STR_PARAM, &setid_avp_param }, {0, 0, 0} }; static mi_export_t mi_cmds[] = { {MI_ENABLE_RTP_ENGINE, 0, mi_enable_rtp_proxy, 0, 0, 0}, {MI_SHOW_RTP_ENGINES, 0, mi_show_rtpengines, MI_NO_INPUT_FLAG, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "rtpengine", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, 0, params, 0, /* exported statistics */ mi_cmds, /* exported MI functions */ mod_pvs, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, 0, /* reply processing */ mod_destroy, /* destroy function */ child_init }; int msg_has_sdp(struct sip_msg *msg) { str body; struct part *p; struct multi_body *m; if(parse_headers(msg, HDR_CONTENTLENGTH_F,0) < 0) { LM_ERR("cannot parse cseq header"); return 0; } body.len = get_content_length(msg); if (!body.len) return 0; m = get_all_bodies(msg); if (!m) { LM_DBG("cannot parse body\n"); return 0; } for (p = m->first; p; p = p->next) { if (p->content_type == ((TYPE_APPLICATION << 16) + SUBTYPE_SDP)) return 1; } return 0; } static inline int str_eq(const str *p, const char *q) { int l = strlen(q); if (p->len != l) return 0; if (memcmp(p->s, q, l)) return 0; return 1; } static int rtpengine_set_store(modparam_t type, void * val){ char * p; int len; p = (char* )val; if(p==0 || *p=='\0'){ return 0; } if(rtpe_sets==0){ rtpe_strings = (char**)pkg_malloc(sizeof(char*)); if(!rtpe_strings){ LM_ERR("no pkg memory left\n"); return -1; } } else {/*realloc to make room for the current set*/ rtpe_strings = (char**)pkg_realloc(rtpe_strings, (rtpe_sets+1)* sizeof(char*)); if(!rtpe_strings){ LM_ERR("no pkg memory left\n"); return -1; } } /*allocate for the current set of urls*/ len = strlen(p); rtpe_strings[rtpe_sets] = (char*)pkg_malloc((len+1)*sizeof(char)); if(!rtpe_strings[rtpe_sets]){ LM_ERR("no pkg memory left\n"); return -1; } memcpy(rtpe_strings[rtpe_sets], p, len); rtpe_strings[rtpe_sets][len] = '\0'; rtpe_sets++; return 0; } static int add_rtpengine_socks(struct rtpe_set * rtpe_list, char * rtpengine){ /* Make rtp proxies list. */ char *p, *p1, *p2, *plim; struct rtpe_node *pnode; int weight; p = rtpengine; plim = p + strlen(p); for(;;) { weight = 1; while (*p && isspace((int)*p)) ++p; if (p >= plim) break; p1 = p; while (*p && !isspace((int)*p)) ++p; if (p <= p1) break; /* may happen??? */ /* Have weight specified? If yes, scan it */ p2 = memchr(p1, '=', p - p1); if (p2 != NULL) { weight = strtoul(p2 + 1, NULL, 10); } else { p2 = p; } pnode = shm_malloc(sizeof(struct rtpe_node)); if (pnode == NULL) { LM_ERR("no shm memory left\n"); return -1; } memset(pnode, 0, sizeof(*pnode)); pnode->idx = rtpe_no++; pnode->rn_recheck_ticks = 0; pnode->rn_weight = weight; pnode->rn_umode = 0; pnode->rn_disabled = 0; pnode->rn_url.s = shm_malloc(p2 - p1 + 1); if (pnode->rn_url.s == NULL) { shm_free(pnode); LM_ERR("no shm memory left\n"); return -1; } memmove(pnode->rn_url.s, p1, p2 - p1); pnode->rn_url.s[p2 - p1] = 0; pnode->rn_url.len = p2-p1; LM_DBG("url is %s, len is %i\n", pnode->rn_url.s, pnode->rn_url.len); /* Leave only address in rn_address */ pnode->rn_address = pnode->rn_url.s; if (strncasecmp(pnode->rn_address, "udp:", 4) == 0) { pnode->rn_umode = 1; pnode->rn_address += 4; } else if (strncasecmp(pnode->rn_address, "udp6:", 5) == 0) { pnode->rn_umode = 6; pnode->rn_address += 5; } else if (strncasecmp(pnode->rn_address, "unix:", 5) == 0) { pnode->rn_umode = 0; pnode->rn_address += 5; } if (rtpe_list->rn_first == NULL) { rtpe_list->rn_first = pnode; } else { rtpe_list->rn_last->rn_next = pnode; } rtpe_list->rn_last = pnode; rtpe_list->rtpe_node_count++; } return 0; } /* 0-succes * -1 - erorr * */ static int rtpengine_add_rtpengine_set( char * rtp_proxies) { char *p,*p2; struct rtpe_set * rtpe_list; unsigned int my_current_id; str id_set; int new_list; /* empty definition? */ p= rtp_proxies; if(!p || *p=='\0'){ return 0; } for(;*p && isspace(*p);p++); if(*p=='\0'){ return 0; } rtp_proxies = strstr(p, "=="); if(rtp_proxies){ if(*(rtp_proxies +2)=='\0'){ LM_ERR("script error -invalid rtp proxy list!\n"); return -1; } *rtp_proxies = '\0'; p2 = rtp_proxies-1; for(;isspace(*p2); *p2 = '\0',p2--); id_set.s = p; id_set.len = p2 - p+1; if(id_set.len <= 0 ||str2int(&id_set, &my_current_id)<0 ){ LM_ERR("script error -invalid set_id value!\n"); return -1; } rtp_proxies+=2; }else{ rtp_proxies = p; my_current_id = DEFAULT_RTPE_SET_ID; } for(;*rtp_proxies && isspace(*rtp_proxies);rtp_proxies++); if(!(*rtp_proxies)){ LM_ERR("script error -empty rtp_proxy list\n"); return -1;; } /*search for the current_id*/ rtpe_list = rtpe_set_list ? rtpe_set_list->rset_first : 0; while( rtpe_list != 0 && rtpe_list->id_set!=my_current_id) rtpe_list = rtpe_list->rset_next; if(rtpe_list==NULL){ /*if a new id_set : add a new set of rtpe*/ rtpe_list = shm_malloc(sizeof(struct rtpe_set)); if(!rtpe_list){ LM_ERR("no shm memory left\n"); return -1; } memset(rtpe_list, 0, sizeof(struct rtpe_set)); rtpe_list->id_set = my_current_id; new_list = 1; } else { new_list = 0; } if(add_rtpengine_socks(rtpe_list, rtp_proxies)!= 0){ /*if this list will not be inserted, clean it up*/ goto error; } if (new_list) { if(!rtpe_set_list){/*initialize the list of set*/ rtpe_set_list = shm_malloc(sizeof(struct rtpe_set_head)); if(!rtpe_set_list){ LM_ERR("no shm memory left\n"); return -1; } memset(rtpe_set_list, 0, sizeof(struct rtpe_set_head)); } /*update the list of set info*/ if(!rtpe_set_list->rset_first){ rtpe_set_list->rset_first = rtpe_list; }else{ rtpe_set_list->rset_last->rset_next = rtpe_list; } rtpe_set_list->rset_last = rtpe_list; rtpe_set_count++; if(my_current_id == DEFAULT_RTPE_SET_ID){ default_rtpe_set = rtpe_list; } } return 0; error: return -1; } static int fixup_set_id(void ** param, int param_no) { int int_val, err; struct rtpe_set* rtpe_list; rtpe_set_link_t *rtpl = NULL; str s; rtpl = (rtpe_set_link_t*)pkg_malloc(sizeof(rtpe_set_link_t)); if(rtpl==NULL) { LM_ERR("no more pkg memory\n"); return -1; } memset(rtpl, 0, sizeof(rtpe_set_link_t)); s.s = (char*)*param; s.len = strlen(s.s); if(s.s[0] == PV_MARKER) { if ( pv_parse_spec(&s, &rtpl->rpv) == NULL ) { LM_ERR("invalid parameter %s\n", s.s); return -1; } } else { int_val = str2s(*param, strlen(*param), &err); if (err == 0) { pkg_free(*param); if((rtpe_list = select_rtpe_set(int_val)) ==0){ LM_ERR("rtpe_proxy set %i not configured\n", int_val); return E_CFG; } rtpl->rset = rtpe_list; } else { LM_ERR("bad number <%s>\n", (char *)(*param)); return E_CFG; } } *param = (void*)rtpl; return 0; } static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree, void* param ) { struct mi_node* node; str rtpe_url; unsigned int enable; struct rtpe_set * rtpe_list; struct rtpe_node * crt_rtpe; int found; found = 0; if(rtpe_set_list ==NULL) goto end; node = cmd_tree->node.kids; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if(node->value.s == NULL || node->value.len ==0) return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); rtpe_url = node->value; node = node->next; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); enable = 0; if( strno2int( &node->value, &enable) <0) goto error; for(rtpe_list = rtpe_set_list->rset_first; rtpe_list != NULL; rtpe_list = rtpe_list->rset_next){ for(crt_rtpe = rtpe_list->rn_first; crt_rtpe != NULL; crt_rtpe = crt_rtpe->rn_next){ /*found a matching rtpe*/ if(crt_rtpe->rn_url.len == rtpe_url.len){ if(strncmp(crt_rtpe->rn_url.s, rtpe_url.s, rtpe_url.len) == 0){ /*set the enabled/disabled status*/ found = 1; crt_rtpe->rn_recheck_ticks = enable? MI_MIN_RECHECK_TICKS : MI_MAX_RECHECK_TICKS; crt_rtpe->rn_disabled = enable?0:1; } } } } end: if(found) return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); return init_mi_tree(404,MI_RTP_ENGINE_NOT_FOUND,MI_RTP_ENGINE_NOT_FOUND_LEN); error: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } #define add_rtpe_node_int_info(_parent, _name, _name_len, _value, _attr,\ _len, _string, _error)\ do {\ (_string) = int2str((_value), &(_len));\ if((_string) == 0){\ LM_ERR("cannot convert int value\n");\ goto _error;\ }\ if(((_attr) = add_mi_attr((_parent), MI_DUP_VALUE, (_name), \ (_name_len), (_string), (_len)) ) == 0)\ goto _error;\ }while(0); static struct mi_root* mi_show_rtpengines(struct mi_root* cmd_tree, void* param) { struct mi_node* node, *crt_node, *set_node; struct mi_root* root; struct mi_attr * attr; struct rtpe_set * rtpe_list; struct rtpe_node * crt_rtpe; char * string, *id; int id_len, len; string = id = 0; root = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (!root) { LM_ERR("the MI tree cannot be initialized!\n"); return 0; } if(rtpe_set_list ==NULL) return root; node = &root->node; node->flags |= MI_IS_ARRAY; for(rtpe_list = rtpe_set_list->rset_first; rtpe_list != NULL; rtpe_list = rtpe_list->rset_next){ id = int2str(rtpe_list->id_set, &id_len); if(!id){ LM_ERR("cannot convert set id\n"); goto error; } if(!(set_node = add_mi_node_child(node, MI_IS_ARRAY|MI_DUP_VALUE, MI_SET, MI_SET_LEN, id, id_len))) { LM_ERR("cannot add the set node to the tree\n"); goto error; } for(crt_rtpe = rtpe_list->rn_first; crt_rtpe != NULL; crt_rtpe = crt_rtpe->rn_next){ if(!(crt_node = add_mi_node_child(node, MI_DUP_VALUE, MI_NODE, MI_NODE_LEN, crt_rtpe->rn_url.s, crt_rtpe->rn_url.len)) ) { LM_ERR("cannot add the child node to the tree\n"); goto error; } LM_DBG("adding node name %s \n",crt_rtpe->rn_url.s ); add_rtpe_node_int_info(crt_node, MI_INDEX, MI_INDEX_LEN, crt_rtpe->idx, attr, len,string,error); add_rtpe_node_int_info(crt_node, MI_DISABLED, MI_DISABLED_LEN, crt_rtpe->rn_disabled, attr, len,string,error); add_rtpe_node_int_info(crt_node, MI_WEIGHT, MI_WEIGHT_LEN, crt_rtpe->rn_weight, attr, len, string,error); add_rtpe_node_int_info(crt_node, MI_RECHECK_TICKS,MI_RECHECK_T_LEN, crt_rtpe->rn_recheck_ticks, attr, len, string, error); } } return root; error: if (root) free_mi_tree(root); return 0; } static int mod_init(void) { int i; pv_spec_t avp_spec; unsigned short avp_flags; str s; ctx_rtpeset_idx = context_register_ptr(CONTEXT_GLOBAL, NULL); /* any rtpengine configured? */ if(rtpe_set_list) default_rtpe_set = select_rtpe_set(DEFAULT_RTPE_SET_ID); /* storing the list of rtp proxy sets in shared memory*/ for(i=0;i\n", setid_avp_param); return -1; } if (pv_get_avp_name(0, &(avp_spec.pvp), &(setid_avp.n), &avp_flags) != 0) { LM_ERR("invalid AVP definition <%s>\n", setid_avp_param); return -1; } setid_avp_type = avp_flags; } if (rtpe_strings) pkg_free(rtpe_strings); if (load_tm_api( &tmb ) < 0) { LM_DBG("could not load the TM-functions - answer-offer model" " auto-detection is disabled\n"); memset(&tmb, 0, sizeof(struct tm_binds)); } return 0; } static int child_init(int rank) { int n; char *cp; struct addrinfo hints, *res; struct rtpe_set *rtpe_list; struct rtpe_node *pnode; if(rtpe_set_list==NULL ) return 0; /* Iterate known RTP proxies - create sockets */ mypid = getpid(); rtpe_socks = (int*)pkg_malloc( sizeof(int)*rtpe_no ); if (rtpe_socks==NULL) { LM_ERR("no more pkg memory\n"); return -1; } for(rtpe_list = rtpe_set_list->rset_first; rtpe_list != 0; rtpe_list = rtpe_list->rset_next){ for (pnode=rtpe_list->rn_first; pnode!=0; pnode = pnode->rn_next){ char *hostname; if (pnode->rn_umode == 0) { rtpe_socks[pnode->idx] = -1; goto rptest; } /* * This is UDP or UDP6. Detect host and port; lookup host; * do connect() in order to specify peer address */ hostname = (char*)pkg_malloc(sizeof(char) * (strlen(pnode->rn_address) + 1)); if (hostname==NULL) { LM_ERR("no more pkg memory\n"); return -1; } strcpy(hostname, pnode->rn_address); cp = strrchr(hostname, ':'); if (cp != NULL) { *cp = '\0'; cp++; } if (cp == NULL || *cp == '\0') cp = CPORT; memset(&hints, 0, sizeof(hints)); hints.ai_flags = 0; hints.ai_family = (pnode->rn_umode == 6) ? AF_INET6 : AF_INET; hints.ai_socktype = SOCK_DGRAM; if ((n = getaddrinfo(hostname, cp, &hints, &res)) != 0) { LM_ERR("%s\n", gai_strerror(n)); pkg_free(hostname); return -1; } pkg_free(hostname); rtpe_socks[pnode->idx] = socket((pnode->rn_umode == 6) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); if ( rtpe_socks[pnode->idx] == -1) { LM_ERR("can't create socket\n"); freeaddrinfo(res); return -1; } if (connect( rtpe_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) { LM_ERR("can't connect to a RTP proxy\n"); close( rtpe_socks[pnode->idx] ); rtpe_socks[pnode->idx] = -1; freeaddrinfo(res); return -1; } freeaddrinfo(res); rptest: pnode->rn_disabled = rtpe_test(pnode, 0, 1); } } return 0; } static void mod_destroy(void) { struct rtpe_set * crt_list, * last_list; struct rtpe_node * crt_rtpe, *last_rtpe; /*free the shared memory*/ if (natping_state) shm_free(natping_state); if(rtpe_set_list == NULL) return; for(crt_list = rtpe_set_list->rset_first; crt_list != NULL; ){ for(crt_rtpe = crt_list->rn_first; crt_rtpe != NULL; ){ if(crt_rtpe->rn_url.s) shm_free(crt_rtpe->rn_url.s); last_rtpe = crt_rtpe; crt_rtpe = last_rtpe->rn_next; shm_free(last_rtpe); } last_list = crt_list; crt_list = last_list->rset_next; shm_free(last_list); } shm_free(rtpe_set_list); } static char * gencookie(void) { static char cook[34]; sprintf(cook, "%d_%u ", (int)mypid, myseqn); myseqn++; return cook; } static const char *transports[] = { [0x00] = "RTP/AVP", [0x01] = "RTP/SAVP", [0x02] = "RTP/AVPF", [0x03] = "RTP/SAVPF", [0x04] = "UDP/TLS/RTP/SAVP", [0x05] = "UDP/TLS/RTP/SAVPF" }; static int parse_flags(struct ng_flags_parse *ng_flags, struct sip_msg *msg, enum rtpe_operation *op, const char *flags_str) { char *e; const char *err; str key, val; if (!flags_str) return 0; while (1) { while (*flags_str == ' ') flags_str++; key.s = (void *) flags_str; val.len = key.len = -1; val.s = NULL; e = strpbrk(key.s, " ="); if (!e) e = key.s + strlen(key.s); else if (*e == '=') { key.len = e - key.s; val.s = e + 1; e = strchr(val.s, ' '); if (!e) e = val.s + strlen(val.s); val.len = e - val.s; } if (key.len == -1) key.len = e - key.s; if (!key.len) break; /* XXX make this prettier */ err = "unknown flag"; switch (key.len) { case 3: if (str_eq(&key, "ICE")) { err = "missing value"; if (!val.s) goto error; err = "invalid value"; if (str_eq(&val, "force") || str_eq(&val, "force-relay") || str_eq(&val, "remove")) bencode_dictionary_add_str(ng_flags->dict, "ICE", &val); else goto error; } else if (str_eq(&key, "RTP")) { ng_flags->transport |= 0x100; ng_flags->transport &= ~0x001; } else if (str_eq(&key, "AVP")) { ng_flags->transport |= 0x100; ng_flags->transport &= ~0x002; } else goto error; break; case 4: if (str_eq(&key, "SRTP")) ng_flags->transport |= 0x101; else if (str_eq(&key, "AVPF")) ng_flags->transport |= 0x102; else if (str_eq(&key, "DTLS")){ err = "missing value"; if (!val.s) goto error; err = "invalid value"; if (str_eq(&val, "passive")) bencode_dictionary_add_str(ng_flags->dict, "DTLS", &val); else goto error; } else goto error; break; case 5: if (str_eq(&key, "force")) bencode_list_add_string(ng_flags->flags, "force"); else goto error; break; case 6: if (str_eq(&key, "to-tag")) ng_flags->to = 1; else goto error; break; case 7: if (str_eq(&key, "RTP/AVP")) ng_flags->transport = 0x100; else goto error; break; case 8: if (str_eq(&key, "internal")) bencode_list_add_string(ng_flags->direction, "internal"); else if (str_eq(&key, "external")) bencode_list_add_string(ng_flags->direction, "external"); else if (str_eq(&key, "RTP/AVPF")) ng_flags->transport = 0x102; else if (str_eq(&key, "RTP/SAVP")) ng_flags->transport = 0x101; else goto error; break; case 9: if (str_eq(&key, "symmetric")) bencode_list_add_string(ng_flags->flags, "symmetric"); else if (str_eq(&key, "RTP/SAVPF")) ng_flags->transport = 0x103; else goto error; break; case 10: if (str_eq(&key, "via-branch")) { err = "missing value"; if (!val.s) goto error; err = "invalid value"; if (*val.s == '1' || *val.s == '2') ng_flags->via = *val.s - '0'; else if (str_eq(&val, "auto")) ng_flags->via = 3; else if (str_eq(&val, "extra")) ng_flags->via = -1; else goto error; } else if (str_eq(&key, "asymmetric")) bencode_list_add_string(ng_flags->flags, "asymmetric"); else goto error; break; case 11: if (str_eq(&key, "auto-bridge")) bencode_list_add_string(ng_flags->flags, "auto-bridge"); else if (str_eq(&key, "repacketize")) { err = "missing value"; if (!val.s) goto error; ng_flags->packetize = 0; while (isdigit(*val.s)) { ng_flags->packetize *= 10; ng_flags->packetize += *val.s - '0'; val.s++; } err = "invalid value"; if (!ng_flags->packetize) goto error; bencode_dictionary_add_integer(ng_flags->dict, "repacketize", ng_flags->packetize); } else goto error; break; case 12: if (str_eq(&key, "force-answer")) { err = "cannot force answer in non-offer command"; if (*op != OP_OFFER) goto error; *op = OP_ANSWER; } else goto error; break; case 13: if (str_eq(&key, "trust-address")) bencode_list_add_string(ng_flags->flags, "trust-address"); else if (str_eq(&key, "media-address")) { err = "missing value"; if (!val.s) goto error; } else goto error; break; case 14: if (str_eq(&key, "replace-origin")) bencode_list_add_string(ng_flags->replace, "origin"); else if (str_eq(&key, "address-family")) { err = "missing value"; if (!val.s) goto error; err = "invalid value"; if (str_eq(&val, "IP4") || str_eq(&val, "IP6")) bencode_dictionary_add_str(ng_flags->dict, "address family", &val); else goto error; } else if (str_eq(&key, "rtcp-mux-demux")) bencode_list_add_string(ng_flags->rtcp_mux, "demux"); else if (str_eq(&key, "rtcp-mux-offer")) bencode_list_add_string(ng_flags->rtcp_mux, "offer"); else goto error; break; case 15: if (str_eq(&key, "rtcp-mux-reject")) bencode_list_add_string(ng_flags->rtcp_mux, "reject"); else if (str_eq(&key, "rtcp-mux-accept")) bencode_list_add_string(ng_flags->rtcp_mux, "accept"); else goto error; break; case 16: if (str_eq(&key, "UDP/TLS/RTP/SAVP")) ng_flags->transport = 0x104; else goto error; break; case 17: if (str_eq(&key, "UDP/TLS/RTP/SAVPF")) ng_flags->transport = 0x105; else goto error; break; case 26: if (str_eq(&key, "replace-session-connection")) bencode_list_add_string(ng_flags->replace, "session-connection"); else goto error; break; default: goto error; } flags_str = e; } return 0; error: if (val.s) LM_ERR("error processing flag `%.*s' (value '%.*s'): %s\n", key.len, key.s, val.len, val.s, err); else LM_ERR("error processing flag `%.*s': %s\n", key.len, key.s, err); return -1; } static bencode_item_t *rtpe_function_call(bencode_buffer_t *bencbuf, struct sip_msg *msg, enum rtpe_operation op, const char *flags_str, str *body_out) { struct ng_flags_parse ng_flags; bencode_item_t *item, *resp; str callid, from_tag, to_tag, viabranch, error; str body = { 0, 0 }; int ret; struct rtpe_node *node; struct rtpe_set *set; char *cp; /*** get & init basic stuff needed ***/ memset(&ng_flags, 0, sizeof(ng_flags)); if (get_callid(msg, &callid) == -1 || callid.len == 0) { LM_ERR("can't get Call-Id field\n"); return NULL; } if (get_to_tag(msg, &to_tag) == -1) { LM_ERR("can't get To tag\n"); return NULL; } if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { LM_ERR("can't get From tag\n"); return NULL; } if (bencode_buffer_init(bencbuf)) { LM_ERR("could not initialize bencode_buffer_t\n"); return NULL; } ng_flags.dict = bencode_dictionary(bencbuf); if (op == OP_OFFER || op == OP_ANSWER) { ng_flags.flags = bencode_list(bencbuf); ng_flags.direction = bencode_list(bencbuf); ng_flags.replace = bencode_list(bencbuf); ng_flags.rtcp_mux = bencode_list(bencbuf); if (extract_body(msg, &body) == -1) { LM_ERR("can't extract body from the message\n"); goto error; } bencode_dictionary_add_str(ng_flags.dict, "sdp", &body); } /*** parse flags & build dictionary ***/ ng_flags.to = (op == OP_DELETE) ? 0 : 1; if (parse_flags(&ng_flags, msg, &op, flags_str)) goto error; /* only add those if any flags were given at all */ if (ng_flags.direction && ng_flags.direction->child) bencode_dictionary_add(ng_flags.dict, "direction", ng_flags.direction); if (ng_flags.flags && ng_flags.flags->child) bencode_dictionary_add(ng_flags.dict, "flags", ng_flags.flags); if (ng_flags.replace && ng_flags.replace->child) bencode_dictionary_add(ng_flags.dict, "replace", ng_flags.replace); if ((ng_flags.transport & 0x100)) bencode_dictionary_add_string(ng_flags.dict, "transport-protocol", transports[ng_flags.transport & 0x007]); if (ng_flags.rtcp_mux && ng_flags.rtcp_mux->child) bencode_dictionary_add(ng_flags.dict, "rtcp-mux", ng_flags.rtcp_mux); bencode_dictionary_add_str(ng_flags.dict, "call-id", &callid); if (ng_flags.via) { if (ng_flags.via == 1 || ng_flags.via == 2) ret = get_via_branch(msg, ng_flags.via, &viabranch); else if (ng_flags.via == -1 && extra_id_pv) ret = get_extra_id(msg, &viabranch); else ret = -1; if (ret == -1 || viabranch.len == 0) { LM_ERR("can't get Via branch/extra ID\n"); goto error; } bencode_dictionary_add_str(ng_flags.dict, "via-branch", &viabranch); } item = bencode_list(bencbuf); bencode_dictionary_add(ng_flags.dict, "received-from", item); bencode_list_add_string(item, (msg->rcv.src_ip.af == AF_INET) ? "IP4" : ( (msg->rcv.src_ip.af == AF_INET6) ? "IP6" : "?" ) ); bencode_list_add_string(item, ip_addr2a(&msg->rcv.src_ip)); if ((msg->first_line.type == SIP_REQUEST && op != OP_ANSWER) || (msg->first_line.type == SIP_REPLY && op == OP_ANSWER)) { bencode_dictionary_add_str(ng_flags.dict, "from-tag", &from_tag); if (ng_flags.to && to_tag.s && to_tag.len) bencode_dictionary_add_str(ng_flags.dict, "to-tag", &to_tag); } else { if (!to_tag.s || !to_tag.len) { LM_ERR("No to-tag present\n"); goto error; } bencode_dictionary_add_str(ng_flags.dict, "from-tag", &to_tag); bencode_dictionary_add_str(ng_flags.dict, "to-tag", &from_tag); } bencode_dictionary_add_string(ng_flags.dict, "command", command_strings[op]); /*** send it out ***/ if (bencbuf->error) { LM_ERR("out of memory - bencode failed\n"); goto error; } if ( (set=ctx_rtpeset_get())==NULL ) set = default_rtpe_set; do { node = select_rtpe_node(callid, 1, set); if (!node) { LM_ERR("no available proxies\n"); goto error; } cp = send_rtpe_command(node, ng_flags.dict, &ret); } while (cp == NULL); LM_DBG("proxy reply: %.*s\n", ret, cp); /*** process reply ***/ resp = bencode_decode_expect(bencbuf, cp, ret, BENCODE_DICTIONARY); if (!resp) { LM_ERR("failed to decode bencoded reply from proxy: %.*s\n", ret, cp); goto error; } if (!bencode_dictionary_get_strcmp(resp, "result", "error")) { if (!bencode_dictionary_get_str(resp, "error-reason", &error)) LM_ERR("proxy return error but didn't give an error reason: %.*s\n", ret, cp); else LM_ERR("proxy replied with error: %.*s\n", error.len, error.s); goto error; } if (body_out) *body_out = body; return resp; error: bencode_buffer_free(bencbuf); return NULL; } static int rtpe_function_call_simple(struct sip_msg *msg, enum rtpe_operation op, const char *flags_str) { bencode_buffer_t bencbuf; if (!rtpe_function_call(&bencbuf, msg, op, flags_str, NULL)) return -1; bencode_buffer_free(&bencbuf); return 1; } static bencode_item_t *rtpe_function_call_ok(bencode_buffer_t *bencbuf, struct sip_msg *msg, enum rtpe_operation op, const char *flags_str, str *body) { bencode_item_t *ret; ret = rtpe_function_call(bencbuf, msg, op, flags_str, body); if (!ret) return NULL; if (bencode_dictionary_get_strcmp(ret, "result", "ok")) { LM_ERR("proxy didn't return \"ok\" result\n"); bencode_buffer_free(bencbuf); return NULL; } return ret; } static int rtpe_test(struct rtpe_node *node, int isdisabled, int force) { bencode_buffer_t bencbuf; bencode_item_t *dict; char *cp; int ret; if(node->rn_recheck_ticks == MI_MAX_RECHECK_TICKS){ LM_DBG("rtpe %s disabled for ever\n", node->rn_url.s); return 1; } if (force == 0) { if (isdisabled == 0) return 0; if (node->rn_recheck_ticks > get_ticks()) return 1; } if (bencode_buffer_init(&bencbuf)) { LM_ERR("could not initialized bencode_buffer_t\n"); return 1; } dict = bencode_dictionary(&bencbuf); bencode_dictionary_add_string(dict, "command", "ping"); if (bencbuf.error) goto benc_error; cp = send_rtpe_command(node, dict, &ret); if (!cp) { LM_ERR("proxy did not respond to ping\n"); goto error; } dict = bencode_decode_expect(&bencbuf, cp, ret, BENCODE_DICTIONARY); if (!dict || bencode_dictionary_get_strcmp(dict, "result", "pong")) { LM_ERR("proxy responded with invalid response\n"); goto error; } LM_INFO("rtp proxy <%s> found, support for it %senabled\n", node->rn_url.s, force == 0 ? "re-" : ""); bencode_buffer_free(&bencbuf); return 0; benc_error: LM_ERR("out of memory - bencode failed\n"); error: bencode_buffer_free(&bencbuf); return 1; } static char * send_rtpe_command(struct rtpe_node *node, bencode_item_t *dict, int *outlen) { struct sockaddr_un addr; int fd, len, i, vcnt; char *cp; static char buf[0x10000]; struct pollfd fds[1]; struct iovec *v; v = bencode_iovec(dict, &vcnt, 1, 0); if (!v) { LM_ERR("error converting bencode to iovec\n"); return NULL; } len = 0; cp = buf; if (node->rn_umode == 0) { memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; strncpy(addr.sun_path, node->rn_address, sizeof(addr.sun_path) - 1); #ifdef HAVE_SOCKADDR_SA_LEN addr.sun_len = strlen(addr.sun_path); #endif fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd < 0) { LM_ERR("can't create socket\n"); goto badproxy; } if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(fd); LM_ERR("can't connect to RTP proxy\n"); goto badproxy; } do { len = writev(fd, v + 1, vcnt); } while (len == -1 && errno == EINTR); if (len <= 0) { close(fd); LM_ERR("can't send command to a RTP proxy (%s)\n",strerror(errno)); goto badproxy; } do { len = read(fd, buf, sizeof(buf) - 1); } while (len == -1 && errno == EINTR); close(fd); if (len <= 0) { LM_ERR("can't read reply from a RTP proxy\n"); goto badproxy; } } else { fds[0].fd = rtpe_socks[node->idx]; fds[0].events = POLLIN; fds[0].revents = 0; /* Drain input buffer */ while ((poll(fds, 1, 0) == 1) && ((fds[0].revents & POLLIN) != 0)) { recv(rtpe_socks[node->idx], buf, sizeof(buf) - 1, 0); fds[0].revents = 0; } v[0].iov_base = gencookie(); v[0].iov_len = strlen(v[0].iov_base); for (i = 0; i < rtpengine_retr; i++) { do { len = writev(rtpe_socks[node->idx], v, vcnt + 1); } while (len == -1 && (errno == EINTR || errno == ENOBUFS || errno == EMSGSIZE)); if (len <= 0) { LM_ERR("can't send command to a RTP proxy\n"); goto badproxy; } while ((poll(fds, 1, rtpengine_tout * 1000) == 1) && (fds[0].revents & POLLIN) != 0) { do { len = recv(rtpe_socks[node->idx], buf, sizeof(buf)-1, 0); } while (len == -1 && errno == EINTR); if (len <= 0) { LM_ERR("can't read reply from a RTP proxy\n"); goto badproxy; } if (len >= (v[0].iov_len - 1) && memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) { len -= (v[0].iov_len - 1); cp += (v[0].iov_len - 1); if (len != 0) { len--; cp++; } goto out; } fds[0].revents = 0; } } if (i == rtpengine_retr) { LM_ERR("timeout waiting reply from a RTP proxy\n"); goto badproxy; } } out: cp[len] = '\0'; *outlen = len; return cp; badproxy: LM_ERR("proxy <%s> does not respond, disable it\n", node->rn_url.s); node->rn_disabled = 1; node->rn_recheck_ticks = get_ticks() + rtpengine_disable_tout; return NULL; } /* * select the set with the id_set id */ static struct rtpe_set * select_rtpe_set(int id_set ){ struct rtpe_set * rtpe_list; /*is it a valid set_id?*/ if(!rtpe_set_list || !rtpe_set_list->rset_first){ LM_ERR("no rtp_proxy configured\n"); return 0; } for(rtpe_list=rtpe_set_list->rset_first; rtpe_list!=0 && rtpe_list->id_set!=id_set; rtpe_list=rtpe_list->rset_next); if(!rtpe_list){ LM_ERR(" script error-invalid id_set to be selected\n"); } return rtpe_list; } /* * Main balancing routine. This does not try to keep the same proxy for * the call if some proxies were disabled or enabled; proxy death considered * too rare. Otherwise we should implement "mature" HA clustering, which is * too expensive here. */ static struct rtpe_node * select_rtpe_node(str callid, int do_test, struct rtpe_set *set) { unsigned sum, sumcut, weight_sum; struct rtpe_node* node; int was_forced; if(!set){ LM_ERR("script error -no valid set selected\n"); return NULL; } /* Most popular case: 1 proxy, nothing to calculate */ if (set->rtpe_node_count == 1) { node = set->rn_first; if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()) node->rn_disabled = rtpe_test(node, 1, 0); return node->rn_disabled ? NULL : node; } /* XXX Use quick-and-dirty hashing algo */ for(sum = 0; callid.len > 0; callid.len--) sum += callid.s[callid.len - 1]; sum &= 0xff; was_forced = 0; retry: weight_sum = 0; for (node=set->rn_first; node!=NULL; node=node->rn_next) { if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()){ /* Try to enable if it's time to try. */ node->rn_disabled = rtpe_test(node, 1, 0); } if (!node->rn_disabled) weight_sum += node->rn_weight; } if (weight_sum == 0) { /* No proxies? Force all to be redetected, if not yet */ if (was_forced) return NULL; was_forced = 1; for(node=set->rn_first; node!=NULL; node=node->rn_next) { node->rn_disabled = rtpe_test(node, 1, 1); } goto retry; } sumcut = sum % weight_sum; /* * sumcut here lays from 0 to weight_sum-1. * Scan proxy list and decrease until appropriate proxy is found. */ for (node=set->rn_first; node!=NULL; node=node->rn_next) { if (node->rn_disabled) continue; if (sumcut < node->rn_weight) goto found; sumcut -= node->rn_weight; } /* No node list */ return NULL; found: if (do_test) { node->rn_disabled = rtpe_test(node, node->rn_disabled, 0); if (node->rn_disabled) goto retry; } return node; } static int get_extra_id(struct sip_msg* msg, str *id_str) { if(msg==NULL || extra_id_pv==NULL || id_str==NULL) { LM_ERR("bad parameters\n"); return -1; } if (pv_printf_s(msg, extra_id_pv, id_str)<0) { LM_ERR("cannot print the additional id\n"); return -1; } return 1; } static int set_rtpengine_set_from_avp(struct sip_msg *msg) { struct usr_avp *avp; int_str setid_val; struct rtpe_set *set; if ((setid_avp_param == NULL) || (avp = search_first_avp(setid_avp_type, setid_avp.n, &setid_val, 0)) == NULL) return 1; if (avp->flags&AVP_VAL_STR) { LM_ERR("setid_avp must hold an integer value\n"); return -1; } if ( (set=select_rtpe_set(setid_val.n)) == NULL) { LM_ERR("could not locate rtpengine set %d\n", setid_val.n); return -1; } ctx_rtpeset_set( set ); LM_DBG("using rtpengine set %d\n", setid_val.n); return 1; } static int rtpengine_delete(struct sip_msg *msg, const char *flags) { return rtpe_function_call_simple(msg, OP_DELETE, flags); } static int rtpengine_delete1_f(struct sip_msg* msg, gparam_p str1) { str flags; if (set_rtpengine_set_from_avp(msg) == -1) return -1; flags.s = NULL; if (str1) fixup_get_svalue(msg, str1, &flags); return rtpengine_delete(msg, flags.s); } /* This function assumes p points to a line of requested type. */ static int set_rtpengine_set_f(struct sip_msg * msg, rtpe_set_link_t *set_param) { rtpe_set_link_t *rtpl; pv_value_t val; struct rtpe_set *set; rtpl = set_param; if(rtpl->rset != NULL) { ctx_rtpeset_set( rtpl->rset ); } else { if(pv_get_spec_value(msg, &rtpl->rpv, &val)<0) { LM_ERR("cannot evaluate pv param\n"); return -1; } if(!(val.flags & PV_VAL_INT)) { LM_ERR("pv param must hold an integer value\n"); return -1; } set = select_rtpe_set(val.ri); if(set==NULL) { LM_ERR("could not locate rtpengine set %d\n", val.ri); return -1; } ctx_rtpeset_set( set ); } return 1; } static int rtpengine_manage(struct sip_msg *msg, const char *flags) { int method; int nosdp; if(msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1) || (msg->cseq==NULL))) { LM_ERR("no CSEQ header\n"); return -1; } method = get_cseq(msg)->method_id; if(!(method==METHOD_INVITE || method==METHOD_ACK || method==METHOD_CANCEL || method==METHOD_BYE || method==METHOD_UPDATE)) return -1; if(method==METHOD_CANCEL || method==METHOD_BYE) return rtpengine_delete(msg, flags); if(msg_has_sdp(msg)) nosdp = 0; else nosdp = parse_sdp(msg); if(msg->first_line.type == SIP_REQUEST) { if(method==METHOD_ACK && nosdp==0) return rtpengine_offer_answer(msg, flags, OP_ANSWER); if(method==METHOD_UPDATE && nosdp==0) return rtpengine_offer_answer(msg, flags, OP_OFFER); if(method==METHOD_INVITE && nosdp==0) { if(route_type==FAILURE_ROUTE) return rtpengine_delete(msg, flags); return rtpengine_offer_answer(msg, flags, OP_OFFER); } } else if(msg->first_line.type == SIP_REPLY) { if(msg->first_line.u.reply.statuscode>=300) return rtpengine_delete(msg, flags); if(nosdp==0) { if(method==METHOD_UPDATE) return rtpengine_offer_answer(msg, flags, OP_ANSWER); if(tmb.t_gett==NULL || tmb.t_gett()==NULL || tmb.t_gett()==T_UNDEFINED) return rtpengine_offer_answer(msg, flags, OP_ANSWER); return rtpengine_offer_answer(msg, flags, OP_OFFER); } } return -1; } static int rtpengine_manage1_f(struct sip_msg *msg, gparam_p str1) { str flags; if (set_rtpengine_set_from_avp(msg) == -1) return -1; flags.s = NULL; if (str1) fixup_get_svalue(msg, str1, &flags); return rtpengine_manage(msg, flags.s); } static int rtpengine_offer1_f(struct sip_msg *msg, gparam_p str1) { str flags; if (set_rtpengine_set_from_avp(msg) == -1) return -1; flags.s = NULL; if (str1) fixup_get_svalue(msg, str1, &flags); return rtpengine_offer_answer(msg, flags.s, OP_OFFER); } static int rtpengine_answer1_f(struct sip_msg *msg, gparam_p str1) { str flags; if (set_rtpengine_set_from_avp(msg) == -1) return -1; if (msg->first_line.type == SIP_REQUEST) if (msg->first_line.u.request.method_value != METHOD_ACK) return -1; flags.s = NULL; if (str1) fixup_get_svalue(msg, str1, &flags); return rtpengine_offer_answer(msg, flags.s, OP_ANSWER); } static int rtpengine_offer_answer(struct sip_msg *msg, const char *flags, int op) { bencode_buffer_t bencbuf; bencode_item_t *dict; str body, newbody; struct lump *anchor; dict = rtpe_function_call_ok(&bencbuf, msg, op, flags, &body); if (!dict) return -1; if (!bencode_dictionary_get_str_dup(dict, "sdp", &newbody)) { LM_ERR("failed to extract sdp body from proxy reply\n"); goto error; } anchor = del_lump(msg, body.s - msg->buf, body.len, 0); if (!anchor) { LM_ERR("del_lump failed\n"); goto error_free; } if (!insert_new_lump_after(anchor, newbody.s, newbody.len, 0)) { LM_ERR("insert_new_lump_after failed\n"); goto error_free; } bencode_buffer_free(&bencbuf); return 1; error_free: pkg_free(newbody.s); error: bencode_buffer_free(&bencbuf); return -1; } static int start_recording_f(struct sip_msg* msg) { return rtpe_function_call_simple(msg, OP_START_RECORDING, NULL); } /* * Returns the current RTP-Statistics from the RTP-Proxy */ static int pv_get_rtpstat_f(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { bencode_buffer_t bencbuf; bencode_item_t *dict, *tot, *rtp, *rtcp; static char buf[256]; str ret; dict = rtpe_function_call_ok(&bencbuf, msg, OP_QUERY, NULL, NULL); if (!dict) return -1; tot = bencode_dictionary_get_expect(dict, "totals", BENCODE_DICTIONARY); rtp = bencode_dictionary_get_expect(tot, "RTP", BENCODE_DICTIONARY); rtcp = bencode_dictionary_get_expect(tot, "RTCP", BENCODE_DICTIONARY); if (!rtp || !rtcp) goto error; ret.s = buf; ret.len = snprintf(buf, sizeof(buf), "RTP: %lli bytes, %lli packets, %lli errors; " "RTCP: %lli bytes, %lli packets, %lli errors", bencode_dictionary_get_integer(rtp, "bytes", -1), bencode_dictionary_get_integer(rtp, "packets", -1), bencode_dictionary_get_integer(rtp, "errors", -1), bencode_dictionary_get_integer(rtcp, "bytes", -1), bencode_dictionary_get_integer(rtcp, "packets", -1), bencode_dictionary_get_integer(rtcp, "errors", -1)); bencode_buffer_free(&bencbuf); return pv_get_strval(msg, param, res, &ret); error: bencode_buffer_free(&bencbuf); return -1; } opensips-2.2.2/modules/rtpengine/rtpengine.h000066400000000000000000000033151300170765700211740ustar00rootroot00000000000000/* * Copyright (C) 2003 Porta Software Ltd * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2014-06-17 Imported from rtpproxy module */ #ifndef _RTPENGINE_H #define _RTPENGINE_H #include "bencode.h" #include "../../str.h" struct rtpe_node { unsigned int idx; /* overall index */ str rn_url; /* unparsed, deletable */ int rn_umode; char *rn_address; /* substring of rn_url */ int rn_disabled; /* found unaccessible? */ unsigned rn_weight; /* for load balancing */ unsigned int rn_recheck_ticks; int rn_rep_supported; int rn_ptl_supported; struct rtpe_node *rn_next; }; struct rtpe_set{ unsigned int id_set; unsigned weight_sum; unsigned int rtpe_node_count; int set_disabled; unsigned int set_recheck_ticks; struct rtpe_node *rn_first; struct rtpe_node *rn_last; struct rtpe_set *rset_next; }; struct rtpe_set_head{ struct rtpe_set *rset_first; struct rtpe_set *rset_last; }; #endif opensips-2.2.2/modules/rtpengine/rtpengine_funcs.c000066400000000000000000000261261300170765700223720ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-06-17 Imported from rtpproxy module */ #include #include #include #include #include "rtpengine_funcs.h" #include "../../dprint.h" #include "../../config.h" #include "../../ut.h" #include "../../forward.h" #include "../../resolve.h" #include "../../globals.h" #include "../../pt.h" #include "../../parser/msg_parser.h" #include "../../trim.h" #include "../../parser/parse_from.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_content.h" #include "../../parser/parser_f.h" #include "../../parser/sdp/sdp_helpr_funcs.h" #define READ(val) \ (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)) #define advance(_ptr,_n,_str,_error) \ do{\ if ((_ptr)+(_n)>(_str).s+(_str).len)\ goto _error;\ (_ptr) = (_ptr) + (_n);\ }while(0); #define one_of_16( _x , _t ) \ (_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\ ||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14]) #define one_of_8( _x , _t ) \ (_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ||_x==_t[5]||_x==_t[6]) /** * return: * -1: error * 1: text or sdp * 2: multipart */ int check_content_type(struct sip_msg *msg) { static unsigned int appl[16] = { 0x6c707061/*appl*/,0x6c707041/*Appl*/,0x6c705061/*aPpl*/, 0x6c705041/*APpl*/,0x6c507061/*apPl*/,0x6c507041/*ApPl*/, 0x6c505061/*aPPl*/,0x6c505041/*APPl*/,0x4c707061/*appL*/, 0x4c707041/*AppL*/,0x4c705061/*aPpL*/,0x4c705041/*APpL*/, 0x4c507061/*apPL*/,0x4c507041/*ApPL*/,0x4c505061/*aPPL*/, 0x4c505041/*APPL*/}; static unsigned int icat[16] = { 0x74616369/*icat*/,0x74616349/*Icat*/,0x74614369/*iCat*/, 0x74614349/*ICat*/,0x74416369/*icAt*/,0x74416349/*IcAt*/, 0x74414369/*iCAt*/,0x74414349/*ICAt*/,0x54616369/*icaT*/, 0x54616349/*IcaT*/,0x54614369/*iCaT*/,0x54614349/*ICaT*/, 0x54416369/*icAT*/,0x54416349/*IcAT*/,0x54414369/*iCAT*/, 0x54414349/*ICAT*/}; static unsigned int ion_[8] = { 0x006e6f69/*ion_*/,0x006e6f49/*Ion_*/,0x006e4f69/*iOn_*/, 0x006e4f49/*IOn_*/,0x004e6f69/*ioN_*/,0x004e6f49/*IoN_*/, 0x004e4f69/*iON_*/,0x004e4f49/*ION_*/}; static unsigned int sdp_[8] = { 0x00706473/*sdp_*/,0x00706453/*Sdp_*/,0x00704473/*sDp_*/, 0x00704453/*SDp_*/,0x00506473/*sdP_*/,0x00506453/*SdP_*/, 0x00504473/*sDP_*/,0x00504453/*SDP_*/}; str str_type; unsigned int x; char *p; if (!msg->content_type) { LM_WARN("the header Content-TYPE is absent!" "let's assume the content is text/plain ;-)\n"); return 1; } trim_len(str_type.len,str_type.s,msg->content_type->body); if (str_type.len>=15 && (*str_type.s=='m' || *str_type.s=='M') && strncasecmp(str_type.s, "multipart/mixed", 15) == 0) { return 2; } p = str_type.s; advance(p,4,str_type,error_1); x = READ(p-4); if (!one_of_16(x,appl)) goto other; advance(p,4,str_type,error_1); x = READ(p-4); if (!one_of_16(x,icat)) goto other; advance(p,3,str_type,error_1); x = READ(p-3) & 0x00ffffff; if (!one_of_8(x,ion_)) goto other; /* skip spaces and tabs if any */ while (*p==' ' || *p=='\t') advance(p,1,str_type,error_1); if (*p!='/') { LM_ERR("no / found after primary type\n"); goto error; } advance(p,1,str_type,error_1); while ((*p==' ' || *p=='\t') && p+1 found valid\n", (int)(p-str_type.s), str_type.s); return 1; } else { LM_ERR("bad end for type!\n"); return -1; } error_1: LM_ERR("body ended :-(!\n"); error: return -1; other: LM_ERR("invalid type for a message\n"); return -1; } /* * Get message body and check Content-Type header field */ int extract_body(struct sip_msg *msg, str *body ) { char c; int ret; str mpdel; char *rest, *p1, *p2; struct hdr_field hf; unsigned int mime; if (get_body(msg,body)!=0 || body->len==0) { LM_ERR("failed to get the message body\n"); goto error; } /* * Better use the content-len value - no need of any explicit * parcing as get_body() parsed all headers and Conten-Length * body header is automaticaly parsed when found. */ if (msg->content_length==0) { LM_ERR("failed to get the content length in message\n"); goto error; } body->len = get_content_length(msg); if (body->len==0) { LM_ERR("message body has length zero\n"); goto error; } if (body->len + body->s > msg->buf + msg->len) { LM_ERR("content-length exceeds packet-length by %d\n", (int)((body->len + body->s) - (msg->buf + msg->len))); goto error; } /* no need for parse_headers(msg, EOH), get_body will * parse everything */ /*is the content type correct?*/ if((ret = check_content_type(msg))==-1) { LM_ERR("content type mismatching\n"); goto error; } if(ret!=2) goto done; /* multipart body */ if(get_mixed_part_delimiter(&msg->content_type->body,&mpdel) < 0) { goto error; } p1 = find_sdp_line_delimiter(body->s, body->s+body->len, mpdel); if (p1 == NULL) { LM_ERR("empty multipart content\n"); return -1; } p2=p1; c = 0; for(;;) { p1 = p2; if (p1 == NULL || p1 >= body->s+body->len) break; /* No parts left */ p2 = find_next_sdp_line_delimiter(p1, body->s+body->len, mpdel, body->s+body->len); /* p2 is text limit for application parsing */ rest = eat_line(p1 + mpdel.len + 2, p2 - p1 - mpdel.len - 2); if ( rest > p2 ) { LM_ERR("Unparsable <%.*s>\n", (int)(p1-p1), p1); return -1; } while( rest>16) == TYPE_APPLICATION) && ((mime&0x00ff) == SUBTYPE_SDP)) { c = 1; } } } /* end of while */ if(c==1) { if (rest < p2 && *rest == '\r') rest++; if (rest < p2 && *rest == '\n') rest++; if (rest < p2 && p2[-1] == '\n') p2--; if (rest < p2 && p2[-1] == '\r') p2--; body->s = rest; body->len = p2-rest; goto done; } } error: return -1; done: /*LM_DBG("DEBUG:extract_body:=|%.*s|\n",body->len,body->s);*/ return 1; } /* * ser_memmem() returns the location of the first occurrence of data * pattern b2 of size len2 in memory block b1 of size len1 or * NULL if none is found. Obtained from NetBSD. */ void * ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2) { /* Initialize search pointer */ char *sp = (char *) b1; /* Initialize pattern pointer */ char *pp = (char *) b2; /* Initialize end of search address space pointer */ char *eos = sp + len1 - len2; /* Sanity check */ if(!(b1 && b2 && len1 && len2)) return NULL; while (sp <= eos) { if (*sp == *pp) if (memcmp(sp, pp, len2) == 0) return sp; sp++; } return NULL; } /* * Some helper functions taken verbatim from tm module. */ /* * Extract Call-ID value * assumes the callid header is already parsed * (so make sure it is, before calling this function or * it might fail even if the message _has_ a callid) */ int get_callid(struct sip_msg* _m, str* _cid) { if ((parse_headers(_m, HDR_CALLID_F, 0) == -1)) { LM_ERR("failed to parse call-id header\n"); return -1; } if (_m->callid == NULL) { LM_ERR("call-id not found\n"); return -1; } _cid->s = _m->callid->body.s; _cid->len = _m->callid->body.len; trim(_cid); return 0; } /* * Extract tag from To header field of a response */ int get_to_tag(struct sip_msg* _m, str* _tag) { if (!_m->to && ((parse_headers(_m, HDR_TO_F, 0) == -1) || (!_m->to))) { LM_ERR("To header field missing\n"); return -1; } if (get_to(_m)->tag_value.len) { _tag->s = get_to(_m)->tag_value.s; _tag->len = get_to(_m)->tag_value.len; } else { _tag->s = NULL; /* fixes gcc 4.0 warnings */ _tag->len = 0; } return 0; } /* * Extract tag from From header field of a request */ int get_from_tag(struct sip_msg* _m, str* _tag) { if (parse_from_header(_m)<0) { LM_ERR("failed to parse From header\n"); return -1; } if (get_from(_m)->tag_value.len) { _tag->s = get_from(_m)->tag_value.s; _tag->len = get_from(_m)->tag_value.len; } else { _tag->s = NULL; /* fixes gcc 4.0 warnings */ _tag->len = 0; } return 0; } /* * Extract URI from the Contact header field */ int get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c) { if ((parse_headers(_m, HDR_CONTACT_F, 0) == -1) || !_m->contact) return -1; if (!_m->contact->parsed && parse_contact(_m->contact) < 0) { LM_ERR("failed to parse Contact body\n"); return -1; } *_c = ((contact_body_t*)_m->contact->parsed)->contacts; if (*_c == NULL) /* no contacts found */ return -1; if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) { LM_ERR("failed to parse Contact URI [%.*s]\n", (*_c)->uri.len, ((*_c)->uri.s)?(*_c)->uri.s:""); return -1; } return 0; } /* * Extract branch from Via header */ int get_via_branch(struct sip_msg* msg, int vianum, str* _branch) { struct via_body *via; struct via_param *p; if (parse_headers(msg, (vianum == 1) ? HDR_VIA1_F : HDR_VIA2_F, 0) < 0) return -1; via = (vianum == 1) ? msg->via1 : msg->via2; for (p = via->param_lst; p; p = p->next) { if (p->name.len == strlen("branch") && strncasecmp(p->name.s, "branch", strlen("branch")) == 0) { _branch->s = p->value.s; _branch->len = p->value.len; return 0; } } return -1; } opensips-2.2.2/modules/rtpengine/rtpengine_funcs.h000066400000000000000000000025361300170765700223760ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _RTPENGINE_FUNCS_H #define _RTPENGINE_FUNCS_H #include "../../str.h" #include "../../parser/msg_parser.h" #include "../../parser/contact/contact.h" int extract_body(struct sip_msg * , str *); int check_content_type(struct sip_msg * ); void *ser_memmem(const void *, const void *, size_t, size_t); int get_callid(struct sip_msg *, str *); int get_to_tag(struct sip_msg *, str *); int get_from_tag(struct sip_msg *, str *); int get_contact_uri(struct sip_msg *, struct sip_uri *, contact_t **); int get_via_branch(struct sip_msg *, int, str *); #endif opensips-2.2.2/modules/rtpproxy/000077500000000000000000000000001300170765700167425ustar00rootroot00000000000000opensips-2.2.2/modules/rtpproxy/Makefile000066400000000000000000000004141300170765700204010ustar00rootroot00000000000000# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=rtpproxy.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/rtpproxy/README000066400000000000000000000613271300170765700176330ustar00rootroot00000000000000rtpproxy Module Maxim Sobolev Sippy Software, Inc. Edited by Maxim Sobolev Edited by Bogdan-Andrei Iancu Copyright © 2003-2008 Sippy Software, Inc. Copyright © 2005 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Multiple RTPProxy usage 1.3. RTPProxy timeout notifications 1.4. Dependencies 1.4.1. OpenSIPS Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. rtpproxy_sock (string) 1.5.2. rtpproxy_disable_tout (integer) 1.5.3. rtpproxy_timeout (string) 1.5.4. rtpproxy_autobridge (integer) 1.5.5. rtpproxy_tout (integer) 1.5.6. rtpproxy_retr (integer) 1.5.7. nortpproxy_str (string) 1.5.8. db_url (string) 1.5.9. db_table (string) 1.5.10. rtpp_socket_col (string) 1.5.11. set_id_col (string) 1.5.12. rtpp_notify_socket (string) 1.6. Exported Functions 1.6.1. engage_rtp_proxy([flags [, ip_address [, set_id [, sock_pvar]]]]) - deprecated, rtpproxy_engage([flags [, ip_address [, set_id [, sock_pvar]]]]) 1.6.2. rtpproxy_offer([flags [, ip_address [, set_id [, sock_pvar]]]]) 1.6.3. rtpproxy_answer([flags [, ip_address [, set_id [, sock_pvar]]]]) 1.6.4. unforce_rtp_proxy([set_id [, sock_pvar]]) - deprecated, rtpproxy_unforce([set_id [, sock_pvar]]) 1.6.5. rtpproxy_stream2uac(prompt_name, count [, set_id [, sock_pvar]]), rtpproxy_stream2uas(prompt_name, count [, set_id [, sock_pvar]]) 1.6.6. rtpproxy_stop_stream2uac([set_id [, sock_pvar]]), rtpproxy_stop_stream2uas([set_id [, sock_pvar]]) 1.6.7. start_recording([set_id [, sock_pvar]]) - deprecated, rtpproxy_start_recording([set_id [, sock_pvar]]) 1.7. MI Commands 1.7.1. rtpproxy_enable 1.7.2. rtpproxy_show 1.7.3. rtpproxy_reload 1.8. Exported Events 1.8.1. E_RTPPROXY_STATUS 2. Frequently Asked Questions List of Examples 1.1. Set rtpproxy_sock parameter 1.2. Set rtpproxy_disable_tout parameter 1.3. Set rtpproxy_timeout parameter to 200ms 1.4. Enable auto-bridging feature 1.5. Set rtpproxy_retr parameter 1.6. Set nortpproxy_str parameter 1.7. Set db_url parameter 1.8. Set db_table parameter 1.9. Set rtpp_socket_col parameter 1.10. Set set_id parameter 1.11. Set rtpp_notify_socket parameter 1.12. rtpproxy_engage usage 1.13. rtpproxy_offer usage 1.14. rtpproxy_answer usage 1.15. rtpproxy_unforce usage 1.16. rtpproxy_stream2xxx usage 1.17. rtpproxy_start_recording usage 1.18. rtpproxy_enable usage 1.19. rtpproxy_show usage 1.20. rtpproxy_reload usage Chapter 1. Admin Guide 1.1. Overview This module is used by OpenSIPS to communicate with RTPProxy, a media relay proxy used to make the communication between user agents behind NAT possible. This module is also used along with RTPProxy to record media streams between user agents or to play media to either UAc or UAs. 1.2. Multiple RTPProxy usage Currently, the rtpproxy module can support multiple rtpproxies for balancing/distribution and control/selection purposes. The module allows the definition of several sets of rtpproxies - load-balancing will be performed over a set and the user has the ability to choose what set should be used. The set is selected via its id - the id being defined along with the set. Refer to the “rtpproxy_sock†module parameter definition for syntax description. The balancing inside a set is done automatically by the module based on the weight of each rtpproxy from the set. Note that if rtpproxy has weight 0, it will be used only when no other rtpproxies (with a different weight value than 0) respond. Default weight is 1. Starting with OpenSIPS 1.11, the set_rtp_proxy_set() function has been removed. The set is now specified for each function. If absend, the default set 0 is used. Also, engage_rtp_proxy(), unforce_rtp_proxy() and start_recording() functions have been deprecated and replaced by rtpproxy_engage(), rtpproxy_unforce() and rtpproxy_start_recording() respectively. IMPORTANT: if you use multiple sets, make sure you use the same set for both rtpproxy_offer()/rtpproxy_answer() and rtpproxy_unforce()!! 1.3. RTPProxy timeout notifications Nathelper module can also receive timeout notifications from multiple rtpproxies. RTPProxy can be configured to send notifications when a session doesn't receive any media for a configurable interval of time. The rtpproxy modules has implemented a listener for such notifications and when received it terminates the dialog at SIP level (send BYE to both ends), with the help of dialog module. In our tests with RTPProxy we observed some limitations and also provide a patch for it against git commit “600c80493793bafd2d69427bc22fcb43faad98c5â€. It contains an addition and implements separate timeout parameters for the phases of session establishment and ongoing sessions. In the official code a single timeout parameter controls both session establishment and rtp timeout and the timeout notification is also sent in the call establishment phase. This is a problem since we want to detect rtp timeout fast, but also allow a longer period for call establishment. To enable timeout notification there are several steps that you must follow: Start OpenSIPS timeout detection by setting the “rtpp_notify_socket†module parameter in your configuration script. This is the socket where further notification will be received from rtpproxies. This socket must be a TCP or UNIX socket. Also, for all the calls that require notification, the rtpproxy_engage(), rtpproxy_offer() and rtpproxy_answer() functions must be called with the “n†flag. Configure RTPProxy to use timeout notification by adding the following command line parameters: * “ -n timeout_socket†- specifies where the notifications will be sent. This socket must be the same as “rtpp_notify_socket†OpenSIPS module parameter. This parameter is mandatory. * “ -T ttl†- limits the rtp session timeout to “ttlâ€. This parameter is optional and the default value is 60 seconds. * “ -W ttl†- limits the session establishment timeout to “ttlâ€. This parameter is optional and the default value is 60 seconds. All of the previous parameters can be used with the offical RTPProxy release, except for the last one. It has been added, together with other modifications to RTPProxy in order to work properly. The patch is located in the patches directory in the module. To get the patched version from git you must follow theese steps: * Get the latest source code: “git clone git://sippy.git.sourceforge.net/gitroot/sippy/rtpproxy†* Make a branch from the commit: “git checkout -b branch_name 600c80493793bafd2d69427bc22fcb43faad98c5†* Patch RTPProxy: “patch < path_to_rtpproxy_patch†The patched version can also be found at: http://opensips.org/pub/rtpproxy/ 1.4. Dependencies 1.4.1. OpenSIPS Modules The following modules must be loaded before this module: * a database module - only if you want to load use a database table from where to load the rtp proxies sets. * dialog module - if using the rtpproxy_engage functions or RTPProxy timeout notifications. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.5. Exported Parameters 1.5.1. rtpproxy_sock (string) Definition of socket(s) used to connect to (a set) RTPProxy. It may specify a UNIX socket or an IPv4/IPv6 UDP socket. Default value is “NONE†(disabled). Example 1.1. Set rtpproxy_sock parameter ... # single rtproxy with specific weight modparam("rtpproxy", "rtpproxy_sock", "udp:localhost:12221=2") # multiple rtproxies for LB modparam("rtpproxy", "rtpproxy_sock", "udp:localhost:12221 udp:localhost:12222") # multiple sets of multiple rtproxies modparam("rtpproxy", "rtpproxy_sock", "1 == udp:localhost:12221 udp:localhost:12222") modparam("rtpproxy", "rtpproxy_sock", "2 == udp:localhost:12225") ... 1.5.2. rtpproxy_disable_tout (integer) Once RTPProxy was found unreachable and marked as disable, rtpproxy will not attempt to establish communication to RTPProxy for rtpproxy_disable_tout seconds. Default value is “60â€. Example 1.2. Set rtpproxy_disable_tout parameter ... modparam("rtpproxy", "rtpproxy_disable_tout", 20) ... 1.5.3. rtpproxy_timeout (string) Timeout value in waiting for reply from RTPProxy. Default value is “1â€. Example 1.3. Set rtpproxy_timeout parameter to 200ms ... modparam("rtpproxy", "rtpproxy_timeout", "0.2") ... 1.5.4. rtpproxy_autobridge (integer) Enable auto-bridging feature. Does not properly function when doing serial/parallel forking! Default value is “0â€. Example 1.4. Enable auto-bridging feature ... modparam("rtpproxy", "rtpproxy_autobridge", 1) ... 1.5.5. rtpproxy_tout (integer) Obsolete. see rtpproxy_timeout. 1.5.6. rtpproxy_retr (integer) How many times rtpproxy should retry to send and receive after timeout was generated. Default value is “5â€. Example 1.5. Set rtpproxy_retr parameter ... modparam("rtpproxy", "rtpproxy_retr", 2) ... 1.5.7. nortpproxy_str (string) The parameter sets the SDP attribute used by rtpproxy to mark the packet SDP informations have already been mangled. If empty string, no marker will be added or checked. Note The string must be a complete SDP line, including the EOH (\r\n). Default value is “a=nortpproxy:yes\r\nâ€. Example 1.6. Set nortpproxy_str parameter ... modparam("rtpproxy", "nortpproxy_str", "a=sdpmangled:yes\r\n") ... 1.5.8. db_url (string) The database url. This parameter should be set if you want to use a database table from where to load or reload definitions of socket(s) used to connect to (a set) RTPProxy. The record from the database table will be read at start up (added to the ones defined with the rtpproxy_sock module parameter) and when the MI command rtpproxy_reload is issued(the definitions will be replaced with the ones from the database table). Default value is “NULLâ€. Example 1.7. Set db_url parameter ... modparam("rtpproxy", "db_url", "mysql://opensips:opensipsrw@192.168.2.132/opensips") ... 1.5.9. db_table (string) The name of the database table containing definitions of socket(s) used to connect to (a set) RTPProxy. Default value is “rtpproxy_socketsâ€. Example 1.8. Set db_table parameter ... modparam("rtpproxy", "db_table", "nh_sockets") ... 1.5.10. rtpp_socket_col (string) The name rtpp socket column in the database table. Default value is “rtpproxy_sockâ€. Example 1.9. Set rtpp_socket_col parameter ... modparam("rtpproxy", "rtpp_socket_col", "rtpp_socket") ... 1.5.11. set_id_col (string) The name set id column in the database table. Default value is “set_idâ€. Example 1.10. Set set_id parameter ... modparam("rtpproxy", "set_id_col", "rtpp_set_id") ... 1.5.12. rtpp_notify_socket (string) The socket used by OpenSIPS to receive timeout notifications. Default value is “NULLâ€. Example 1.11. Set rtpp_notify_socket parameter ... modparam("rtpproxy", "rtpp_notify_socket", "tcp:10.10.10.10:9999") ... 1.6. Exported Functions 1.6.1. engage_rtp_proxy([flags [, ip_address [, set_id [, sock_pvar]]]]) - deprecated, rtpproxy_engage([flags [, ip_address [, set_id [, sock_pvar]]]]) Rewrites SDP body to ensure that media is passed through an RTP proxy. It uses the dialog module facilities to keep track when the rtpproxy session must be updated. Function must only be called for the initial INVITE and internally takes care of rewriting the body of 200 OKs and ACKs. Note that when used in bridge mode, this function might advertise wrong interfaces in SDP (due to the fact that OpenSIPS is not aware of the RTPProxy configuration), so you might face an undefined behavior. Meaning of the parameters is as follows: * flags(optional) - flags to turn on some features. + a - flags that UA from which message is received doesn't support symmetric RTP. + l - force “lookupâ€, that is, only rewrite SDP when corresponding session is already exists in the RTP proxy. By default is on when the session is to be completed (reply in non-swap or ACK in swap mode). + i/e - when RTPProxy is used in bridge mode, these flags are used to indicate the direction of the media flow for the current request/reply. 'i' refers to the LAN (internal network) and corresponds to the first interface of RTPProxy (as specified by the -l parameter). 'e' refers to the WAN (external network) and corresponds to the second interface of RTPProxy. These flags should always be used together. For example, an INVITE (offer) that comes from the Internet (WAN) to goes to a local media server (LAN) should use the 'ei' flags. The answer should use the 'ie' flags. Depending on the scenario, the 'ii' and 'ee' combination are also supported. Only makes sense when RTPProxy is running in the bridge mode. + f - instructs rtpproxy to ignore marks inserted by another rtpproxy in transit to indicate that the session is already goes through another proxy. Allows creating chain of proxies. + r - flags that IP address in SDP should be trusted. Without this flag, rtpproxy ignores address in the SDP and uses source address of the SIP message as media address which is passed to the RTP proxy. + o - flags that IP from the origin description (o=) should be also changed. + c - flags to change the session-level SDP connection (c=) IP if media-description also includes connection information. + s/w - flags that for the UA from which message is received, support symmetric RTP must be forced. + n - flags that enables the notification timeout for the session. + zNN - requests the RTPproxy to perform re-packetization of RTP traffic coming from the UA which has sent the current message to increase or decrease payload size per each RTP packet forwarded if possible. The NN is the target payload size in ms, for the most codecs its value should be in 10ms increments, however for some codecs the increment could differ (e.g. 30ms for GSM or 20ms for G.723). The RTPproxy would select the closest value supported by the codec. This feature could be used for significantly reducing bandwith overhead for low bitrate codecs, for example with G.729 going from 10ms to 100ms saves two thirds of the network bandwith. * ip_address(optional) - new SDP IP address. * set_id(optional) - the set used for this call. * sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. Note that the variable will only be populated in the initial request. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.12. rtpproxy_engage usage ... if (is_method("INVITE") && has_totag()) { if ($var(setid) != 0) { rtpproxy_engage(,,"$var(setid)", "$var(proxy)"); xlog("SCRIPT: RTPProxy server used is $var(proxy)\n"); } else { rtpproxy_engage(); xlog("SCRIPT: using default RTPProxy set\n"); } } ... 1.6.2. rtpproxy_offer([flags [, ip_address [, set_id [, sock_pvar]]]]) Rewrites SDP body to ensure that media is passed through an RTP proxy. To be invoked on INVITE for the cases the SDPs are in INVITE and 200 OK and on 200 OK when SDPs are in 200 OK and ACK. See rtpproxy_engage() function description above for the meaning of the parameters. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.13. rtpproxy_offer usage route { ... if (is_method("INVITE")) { if (has_body("application/sdp")) { if (rtpproxy_offer()) t_on_reply("1"); } else { t_on_reply("2"); } } if (is_method("ACK") && has_body("application/sdp")) rtpproxy_answer(); ... } onreply_route[1] { ... if (has_body("application/sdp")) rtpproxy_answer(); ... } onreply_route[2] { ... if (has_body("application/sdp")) rtpproxy_offer(); ... } 1.6.3. rtpproxy_answer([flags [, ip_address [, set_id [, sock_pvar]]]]) Rewrites SDP body to ensure that media is passed through an RTP proxy. To be invoked on 200 OK for the cases the SDPs are in INVITE and 200 OK and on ACK when SDPs are in 200 OK and ACK. See rtpproxy_engage() function description above for the meaning of the parameters. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.14. rtpproxy_answer usage See rtpproxy_offer() function example above for example. 1.6.4. unforce_rtp_proxy([set_id [, sock_pvar]]) - deprecated, rtpproxy_unforce([set_id [, sock_pvar]]) Tears down the RTPProxy session for the current call. Meaning of the parameters is as follows: * set_id(optional) - the set used for this call. * sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.15. rtpproxy_unforce usage ... rtpproxy_unforce(); ... 1.6.5. rtpproxy_stream2uac(prompt_name, count [, set_id [, sock_pvar]]), rtpproxy_stream2uas(prompt_name, count [, set_id [, sock_pvar]]) Instruct the RTPproxy to stream prompt/announcement pre-encoded with the makeann command from the RTPproxy distribution. The uac/uas suffix selects who will hear the announcement relatively to the current transaction - UAC or UAS. For example invoking the rtpproxy_stream2uac in the request processing block on ACK transaction will play the prompt to the UA that has generated original INVITE and ACK while rtpproxy_stop_stream2uas on 183 in reply processing block will play the prompt to the UA that has generated 183. Apart from generating announcements, another possible application of this function is implementing music on hold (MOH) functionality. When count is -1, the streaming will be in loop indefinitely until the appropriate rtpproxy_stop_stream2xxx is issued. In order to work correctly, functions require that the session in the RTPproxy already exists. Also those functions don't alted SDP, so that they are not substitute for calling rtpproxy_offer or rtpproxy_answer. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. Meaning of the parameters is as follows: * prompt_name - name of the prompt to stream. Should be either absolute pathname or pathname relative to the directory where RTPproxy runs. * count - number of times the prompt should be repeated. The value of -1 means that it will be streaming in loop indefinitely, until appropriate rtpproxy_stop_stream2xxx is issued. * set_id(optional) - the set used for this call. * sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. Example 1.16. rtpproxy_stream2xxx usage ... if (is_method("INVITE")) { rtpproxy_offer(); if ($rb=~ "0\.0\.0\.0") { rtpproxy_stream2uas("/var/rtpproxy/prompts/music_on_hold", " -1"); } else { rtpproxy_stop_stream2uas(); }; }; ... 1.6.6. rtpproxy_stop_stream2uac([set_id [, sock_pvar]]), rtpproxy_stop_stream2uas([set_id [, sock_pvar]]) Stop streaming of announcement/prompt/MOH started previously by the respective rtpproxy_stream2xxx. The uac/uas suffix selects whose announcement relatively to tha current transaction should be stopped - UAC or UAS. Meaning of the parameters is as follows: * set_id(optional) - the set used for this call. * sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. These functions can be used from REQUEST_ROUTE, ONREPLY_ROUTE. 1.6.7. start_recording([set_id [, sock_pvar]]) - deprecated, rtpproxy_start_recording([set_id [, sock_pvar]]) This command will send a signal to the RTP-Proxy to record the RTP stream on the RTP-Proxy. Meaning of the parameters is as follows: * set_id(optional) - the set used for this call. * sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. Example 1.17. rtpproxy_start_recording usage ... rtpproxy_start_recording(); ... 1.7. MI Commands 1.7.1. rtpproxy_enable Enables a rtp proxy if parameter value is greater than 0. Disables it if a zero value is given. The first parameter is the rtp proxy url (exactly as defined in the config file). The next parameter (optional) is the rtpproxy set ID (used for better indentification of the rtpproxy instance to be enabled, for example when a rtpproxy is used in multiple sets). The last parameter must be a number in decimal representing the new enabled/disabled state. NOTE: if a rtpproxy is defined multiple times (in the same or diferente sete), all its instances will be enables/disabled IF no set ID provided (as second param). Example 1.18. rtpproxy_enable usage ... ## disable a RTPProxy by URL only $ opensipsctl fifo rtpproxy_enable udp:192.168.2.133:8081 0 ## disable a RTPProxy by URL and set ID (3) $ opensipsctl fifo rtpproxy_enable udp:192.168.2.133:8081 3 0 ... 1.7.2. rtpproxy_show Displays all the rtp proxies and their information: set and status (disabled or not, weight and recheck_ticks). No parameter. Example 1.19. rtpproxy_show usage ... $ opensipsctl fifo rtpproxy_show ... 1.7.3. rtpproxy_reload Reload rtp proxies sets from database. The function will delete all previous records and populate the list with the entries from the database table. The db_url parameter must be set if you want to use this command. No parameter. Example 1.20. rtpproxy_reload usage ... $ opensipsctl fifo rtpproxy_reload ... 1.8. Exported Events 1.8.1. E_RTPPROXY_STATUS This event is raised when a RTPProxy server changes it's status to enabled/disabled. Parameters: * socket - the socket that identifies the RTPProxy instance. * status - active if the RTPProxy instance responds to probing or inactive if the instance was deactivated. Chapter 2. Frequently Asked Questions 2.1. What happened with “rtpproxy_disable†parameter? It was removed as it became obsolete - now “rtpproxy_sock†can take empty value to disable the rtpproxy functionality. 2.2. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.3. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.4. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/rtpproxy/doc/000077500000000000000000000000001300170765700175075ustar00rootroot00000000000000opensips-2.2.2/modules/rtpproxy/doc/rtpproxy.xml000066400000000000000000000025371300170765700221470ustar00rootroot00000000000000 %docentities; ]> rtpproxy Module &osipsname; Maxim Sobolev Sippy Software, Inc.
sobomax@sippysoft.com
Maxim Sobolev
sobomax@sippysoft.com
Bogdan-Andrei Iancu
bogdan@opensips.org
2003-2008 Sippy Software, Inc. 2005 &voicesystem; $Revision: 8740 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/rtpproxy/doc/rtpproxy_admin.xml000066400000000000000000000707771300170765700233320ustar00rootroot00000000000000 &adminguide;
Overview This module is used by &osips; to communicate with RTPProxy, a media relay proxy used to make the communication between user agents behind NAT possible. This module is also used along with RTPProxy to record media streams between user agents or to play media to either &ua;c or &ua;s.
Multiple RTPProxy usage Currently, the rtpproxy module can support multiple rtpproxies for balancing/distribution and control/selection purposes. The module allows the definition of several sets of rtpproxies - load-balancing will be performed over a set and the user has the ability to choose what set should be used. The set is selected via its id - the id being defined along with the set. Refer to the rtpproxy_sock module parameter definition for syntax description. The balancing inside a set is done automatically by the module based on the weight of each rtpproxy from the set. Note that if rtpproxy has weight 0, it will be used only when no other rtpproxies (with a different weight value than 0) respond. Default weight is 1. Starting with &osips; 1.11, the set_rtp_proxy_set() function has been removed. The set is now specified for each function. If absend, the default set 0 is used. Also, engage_rtp_proxy(), unforce_rtp_proxy() and start_recording() functions have been deprecated and replaced by rtpproxy_engage(), rtpproxy_unforce() and rtpproxy_start_recording() respectively. IMPORTANT: if you use multiple sets, make sure you use the same set for both rtpproxy_offer()/rtpproxy_answer() and rtpproxy_unforce()!!
RTPProxy timeout notifications Nathelper module can also receive timeout notifications from multiple rtpproxies. RTPProxy can be configured to send notifications when a session doesn't receive any media for a configurable interval of time. The rtpproxy modules has implemented a listener for such notifications and when received it terminates the dialog at SIP level (send BYE to both ends), with the help of dialog module. In our tests with RTPProxy we observed some limitations and also provide a patch for it against git commit 600c80493793bafd2d69427bc22fcb43faad98c5. It contains an addition and implements separate timeout parameters for the phases of session establishment and ongoing sessions. In the official code a single timeout parameter controls both session establishment and rtp timeout and the timeout notification is also sent in the call establishment phase. This is a problem since we want to detect rtp timeout fast, but also allow a longer period for call establishment. To enable timeout notification there are several steps that you must follow: Start &osips; timeout detection by setting the rtpp_notify_socket module parameter in your configuration script. This is the socket where further notification will be received from rtpproxies. This socket must be a TCP or UNIX socket. Also, for all the calls that require notification, the rtpproxy_engage(), rtpproxy_offer() and rtpproxy_answer() functions must be called with the n flag. Configure RTPProxy to use timeout notification by adding the following command line parameters: -n timeout_socket - specifies where the notifications will be sent. This socket must be the same as rtpp_notify_socket &osips; module parameter. This parameter is mandatory. -T ttl - limits the rtp session timeout to ttl. This parameter is optional and the default value is 60 seconds. -W ttl - limits the session establishment timeout to ttl. This parameter is optional and the default value is 60 seconds. All of the previous parameters can be used with the offical RTPProxy release, except for the last one. It has been added, together with other modifications to RTPProxy in order to work properly. The patch is located in the patches directory in the module. To get the patched version from git you must follow theese steps: Get the latest source code: git clone git://sippy.git.sourceforge.net/gitroot/sippy/rtpproxy Make a branch from the commit: git checkout -b branch_name 600c80493793bafd2d69427bc22fcb43faad98c5 Patch RTPProxy: patch < path_to_rtpproxy_patch The patched version can also be found at: http://opensips.org/pub/rtpproxy/
Dependencies
&osips; Modules The following modules must be loaded before this module: a database module - only if you want to load use a database table from where to load the rtp proxies sets. dialog module - if using the rtpproxy_engage functions or RTPProxy timeout notifications.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>rtpproxy_sock</varname> (string) Definition of socket(s) used to connect to (a set) RTPProxy. It may specify a UNIX socket or an IPv4/IPv6 UDP socket. Default value is NONE (disabled). Set <varname>rtpproxy_sock</varname> parameter ... # single rtproxy with specific weight modparam("rtpproxy", "rtpproxy_sock", "udp:localhost:12221=2") # multiple rtproxies for LB modparam("rtpproxy", "rtpproxy_sock", "udp:localhost:12221 udp:localhost:12222") # multiple sets of multiple rtproxies modparam("rtpproxy", "rtpproxy_sock", "1 == udp:localhost:12221 udp:localhost:12222") modparam("rtpproxy", "rtpproxy_sock", "2 == udp:localhost:12225") ...
<varname>rtpproxy_disable_tout</varname> (integer) Once RTPProxy was found unreachable and marked as disable, rtpproxy will not attempt to establish communication to RTPProxy for rtpproxy_disable_tout seconds. Default value is 60. Set <varname>rtpproxy_disable_tout</varname> parameter ... modparam("rtpproxy", "rtpproxy_disable_tout", 20) ...
<varname>rtpproxy_timeout</varname> (string) Timeout value in waiting for reply from RTPProxy. Default value is 1. Set <varname>rtpproxy_timeout</varname> parameter to 200ms ... modparam("rtpproxy", "rtpproxy_timeout", "0.2") ...
<varname>rtpproxy_autobridge</varname> (integer) Enable auto-bridging feature. Does not properly function when doing serial/parallel forking! Default value is 0. Enable auto-bridging feature ... modparam("rtpproxy", "rtpproxy_autobridge", 1) ...
<varname>rtpproxy_tout</varname> (integer) Obsolete. see rtpproxy_timeout.
<varname>rtpproxy_retr</varname> (integer) How many times rtpproxy should retry to send and receive after timeout was generated. Default value is 5. Set <varname>rtpproxy_retr</varname> parameter ... modparam("rtpproxy", "rtpproxy_retr", 2) ...
<varname>nortpproxy_str</varname> (string) The parameter sets the SDP attribute used by rtpproxy to mark the packet SDP informations have already been mangled. If empty string, no marker will be added or checked. The string must be a complete SDP line, including the EOH (\r\n). Default value is a=nortpproxy:yes\r\n. Set <varname>nortpproxy_str</varname> parameter ... modparam("rtpproxy", "nortpproxy_str", "a=sdpmangled:yes\r\n") ...
<varname>db_url</varname> (string) The database url. This parameter should be set if you want to use a database table from where to load or reload definitions of socket(s) used to connect to (a set) RTPProxy. The record from the database table will be read at start up (added to the ones defined with the rtpproxy_sock module parameter) and when the MI command rtpproxy_reload is issued(the definitions will be replaced with the ones from the database table). Default value is NULL. Set <varname>db_url</varname> parameter ... modparam("rtpproxy", "db_url", "mysql://opensips:opensipsrw@192.168.2.132/opensips") ...
<varname>db_table</varname> (string) The name of the database table containing definitions of socket(s) used to connect to (a set) RTPProxy. Default value is rtpproxy_sockets. Set <varname>db_table</varname> parameter ... modparam("rtpproxy", "db_table", "nh_sockets") ...
<varname>rtpp_socket_col</varname> (string) The name rtpp socket column in the database table. Default value is rtpproxy_sock. Set <varname>rtpp_socket_col</varname> parameter ... modparam("rtpproxy", "rtpp_socket_col", "rtpp_socket") ...
<varname>set_id_col</varname> (string) The name set id column in the database table. Default value is set_id. Set <varname>set_id</varname> parameter ... modparam("rtpproxy", "set_id_col", "rtpp_set_id") ...
<varname>rtpp_notify_socket</varname> (string) The socket used by &osips; to receive timeout notifications. Default value is NULL. Set <varname>rtpp_notify_socket</varname> parameter ... modparam("rtpproxy", "rtpp_notify_socket", "tcp:10.10.10.10:9999") ...
Exported Functions
<function moreinfo="none">engage_rtp_proxy([flags [, ip_address [, set_id [, sock_pvar]]]]) - deprecated</function>, <function moreinfo="none">rtpproxy_engage([flags [, ip_address [, set_id [, sock_pvar]]]])</function> Rewrites &sdp; body to ensure that media is passed through an &rtp; proxy. It uses the dialog module facilities to keep track when the rtpproxy session must be updated. Function must only be called for the initial INVITE and internally takes care of rewriting the body of 200 OKs and ACKs. Note that when used in bridge mode, this function might advertise wrong interfaces in &sdp; (due to the fact that &osips; is not aware of the RTPProxy configuration), so you might face an undefined behavior. Meaning of the parameters is as follows: flags(optional) - flags to turn on some features. a - flags that UA from which message is received doesn't support symmetric RTP. l - force lookup, that is, only rewrite SDP when corresponding session is already exists in the RTP proxy. By default is on when the session is to be completed (reply in non-swap or ACK in swap mode). i/e - when RTPProxy is used in bridge mode, these flags are used to indicate the direction of the media flow for the current request/reply. 'i' refers to the LAN (internal network) and corresponds to the first interface of RTPProxy (as specified by the -l parameter). 'e' refers to the WAN (external network) and corresponds to the second interface of RTPProxy. These flags should always be used together. For example, an INVITE (offer) that comes from the Internet (WAN) to goes to a local media server (LAN) should use the 'ei' flags. The answer should use the 'ie' flags. Depending on the scenario, the 'ii' and 'ee' combination are also supported. Only makes sense when RTPProxy is running in the bridge mode. f - instructs rtpproxy to ignore marks inserted by another rtpproxy in transit to indicate that the session is already goes through another proxy. Allows creating chain of proxies. r - flags that IP address in SDP should be trusted. Without this flag, rtpproxy ignores address in the SDP and uses source address of the SIP message as media address which is passed to the RTP proxy. o - flags that IP from the origin description (o=) should be also changed. c - flags to change the session-level SDP connection (c=) IP if media-description also includes connection information. s/w - flags that for the UA from which message is received, support symmetric RTP must be forced. n - flags that enables the notification timeout for the session. zNN - requests the RTPproxy to perform re-packetization of RTP traffic coming from the UA which has sent the current message to increase or decrease payload size per each RTP packet forwarded if possible. The NN is the target payload size in ms, for the most codecs its value should be in 10ms increments, however for some codecs the increment could differ (e.g. 30ms for GSM or 20ms for G.723). The RTPproxy would select the closest value supported by the codec. This feature could be used for significantly reducing bandwith overhead for low bitrate codecs, for example with G.729 going from 10ms to 100ms saves two thirds of the network bandwith. ip_address(optional) - new SDP IP address. set_id(optional) - the set used for this call. sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. Note that the variable will only be populated in the initial request. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>rtpproxy_engage</function> usage ... if (is_method("INVITE") && has_totag()) { if ($var(setid) != 0) { rtpproxy_engage(,,"$var(setid)", "$var(proxy)"); xlog("SCRIPT: RTPProxy server used is $var(proxy)\n"); } else { rtpproxy_engage(); xlog("SCRIPT: using default RTPProxy set\n"); } } ...
<function moreinfo="none">rtpproxy_offer([flags [, ip_address [, set_id [, sock_pvar]]]])</function> Rewrites &sdp; body to ensure that media is passed through an &rtp; proxy. To be invoked on INVITE for the cases the SDPs are in INVITE and 200 OK and on 200 OK when SDPs are in 200 OK and ACK. See rtpproxy_engage() function description above for the meaning of the parameters. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>rtpproxy_offer</function> usage route { ... if (is_method("INVITE")) { if (has_body("application/sdp")) { if (rtpproxy_offer()) t_on_reply("1"); } else { t_on_reply("2"); } } if (is_method("ACK") && has_body("application/sdp")) rtpproxy_answer(); ... } onreply_route[1] { ... if (has_body("application/sdp")) rtpproxy_answer(); ... } onreply_route[2] { ... if (has_body("application/sdp")) rtpproxy_offer(); ... }
<function moreinfo="none">rtpproxy_answer([flags [, ip_address [, set_id [, sock_pvar]]]])</function> Rewrites &sdp; body to ensure that media is passed through an &rtp; proxy. To be invoked on 200 OK for the cases the SDPs are in INVITE and 200 OK and on ACK when SDPs are in 200 OK and ACK. See rtpproxy_engage() function description above for the meaning of the parameters. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>rtpproxy_answer</function> usage See rtpproxy_offer() function example above for example.
<function moreinfo="none">unforce_rtp_proxy([set_id [, sock_pvar]]) - deprecated</function>, <function moreinfo="none">rtpproxy_unforce([set_id [, sock_pvar]])</function> Tears down the RTPProxy session for the current call. Meaning of the parameters is as follows: set_id(optional) - the set used for this call. sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>rtpproxy_unforce</function> usage ... rtpproxy_unforce(); ...
<function>rtpproxy_stream2uac(prompt_name, count [, set_id [, sock_pvar]])</function>, <function>rtpproxy_stream2uas(prompt_name, count [, set_id [, sock_pvar]])</function> Instruct the RTPproxy to stream prompt/announcement pre-encoded with the makeann command from the RTPproxy distribution. The uac/uas suffix selects who will hear the announcement relatively to the current transaction - UAC or UAS. For example invoking the rtpproxy_stream2uac in the request processing block on ACK transaction will play the prompt to the UA that has generated original INVITE and ACK while rtpproxy_stop_stream2uas on 183 in reply processing block will play the prompt to the UA that has generated 183. Apart from generating announcements, another possible application of this function is implementing music on hold (MOH) functionality. When count is -1, the streaming will be in loop indefinitely until the appropriate rtpproxy_stop_stream2xxx is issued. In order to work correctly, functions require that the session in the RTPproxy already exists. Also those functions don't alted SDP, so that they are not substitute for calling rtpproxy_offer or rtpproxy_answer. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. Meaning of the parameters is as follows: prompt_name - name of the prompt to stream. Should be either absolute pathname or pathname relative to the directory where RTPproxy runs. count - number of times the prompt should be repeated. The value of -1 means that it will be streaming in loop indefinitely, until appropriate rtpproxy_stop_stream2xxx is issued. set_id(optional) - the set used for this call. sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. <function>rtpproxy_stream2xxx</function> usage ... if (is_method("INVITE")) { rtpproxy_offer(); if ($rb=~ "0\.0\.0\.0") { rtpproxy_stream2uas("/var/rtpproxy/prompts/music_on_hold", "-1"); } else { rtpproxy_stop_stream2uas(); }; }; ...
<function>rtpproxy_stop_stream2uac([set_id [, sock_pvar]])</function>, <function>rtpproxy_stop_stream2uas([set_id [, sock_pvar]])</function> Stop streaming of announcement/prompt/MOH started previously by the respective rtpproxy_stream2xxx. The uac/uas suffix selects whose announcement relatively to tha current transaction should be stopped - UAC or UAS. Meaning of the parameters is as follows: set_id(optional) - the set used for this call. sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. These functions can be used from REQUEST_ROUTE, ONREPLY_ROUTE.
<function moreinfo="none">start_recording([set_id [, sock_pvar]]) - deprecated</function>, <function moreinfo="none">rtpproxy_start_recording([set_id [, sock_pvar]])</function> This command will send a signal to the RTP-Proxy to record the RTP stream on the RTP-Proxy. Meaning of the parameters is as follows: set_id(optional) - the set used for this call. sock_pvar(optional) - pvar used to store the RTPProxy socket chosen for this call. This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. <function>rtpproxy_start_recording</function> usage ... rtpproxy_start_recording(); ...
<acronym>MI</acronym> Commands
<function moreinfo="none">rtpproxy_enable</function> Enables a rtp proxy if parameter value is greater than 0. Disables it if a zero value is given. The first parameter is the rtp proxy url (exactly as defined in the config file). The next parameter (optional) is the rtpproxy set ID (used for better indentification of the rtpproxy instance to be enabled, for example when a rtpproxy is used in multiple sets). The last parameter must be a number in decimal representing the new enabled/disabled state. NOTE: if a rtpproxy is defined multiple times (in the same or diferente sete), all its instances will be enables/disabled IF no set ID provided (as second param). <function moreinfo="none">rtpproxy_enable</function> usage ... ## disable a RTPProxy by URL only $ opensipsctl fifo rtpproxy_enable udp:192.168.2.133:8081 0 ## disable a RTPProxy by URL and set ID (3) $ opensipsctl fifo rtpproxy_enable udp:192.168.2.133:8081 3 0 ...
<function moreinfo="none">rtpproxy_show</function> Displays all the rtp proxies and their information: set and status (disabled or not, weight and recheck_ticks). No parameter. <function moreinfo="none">rtpproxy_show</function> usage ... $ opensipsctl fifo rtpproxy_show ...
<function moreinfo="none">rtpproxy_reload</function> Reload rtp proxies sets from database. The function will delete all previous records and populate the list with the entries from the database table. The db_url parameter must be set if you want to use this command. No parameter. <function moreinfo="none">rtpproxy_reload</function> usage ... $ opensipsctl fifo rtpproxy_reload ...
Exported Events
<function moreinfo="none">E_RTPPROXY_STATUS</function> This event is raised when a RTPProxy server changes it's status to enabled/disabled. Parameters: socket - the socket that identifies the RTPProxy instance. status - active if the RTPProxy instance responds to probing or inactive if the instance was deactivated.
opensips-2.2.2/modules/rtpproxy/doc/rtpproxy_faq.xml000066400000000000000000000031351300170765700227710ustar00rootroot00000000000000 &faqguide; What happened with rtpproxy_disable parameter? It was removed as it became obsolete - now rtpproxy_sock can take empty value to disable the rtpproxy functionality. Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/rtpproxy/examples/000077500000000000000000000000001300170765700205605ustar00rootroot00000000000000opensips-2.2.2/modules/rtpproxy/examples/4to6.cfg000066400000000000000000000055671300170765700220520ustar00rootroot00000000000000# $Id: 4to6.cfg 7232 2010-09-23 15:13:16Z osas $ # # Simple application level gateway config script. # # Assumes that SER/rtpproxy run on a machine, which connected to # both IPv4 and IPv6 networks. # # Correspondingly, this machine has two IP addresses: one IPv4 # and the second one IPv6 # # For example: # # 192.168.0.1 - "internal" IPv4 address # 2002:1234:5678::1 - "external" IPv6 address # # rtpproxy started with `-l 192.168.0.1 -6 /2002:1234:5678::1' option. # ser started with `-l 192.168.0.1 -l [2002:1234:5678::1] option. # # ------------------ module loading ---------------------------------- loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" loadmodule "/usr/local/lib/ser/modules/nathelper.so" loadmodule "/usr/local/lib/ser/modules/rtpproxy.so" # ----------------- setting module-specific parameters --------------- # -- nathelper params -- modparam("nathelper", "natping_interval", 15) # ------------------ main fun below ---------------------------------- route { # initial sanity checks -- messages with # max_forwars == 0, or excessively long requests, # or those that don't addressed to us if (!mf_process_maxfwd_header("10")) { sl_send_reply("483", "Too Many Hops"); break; }; if (msg:len > max_len) { sl_send_reply("513", "Message too big"); break; }; if (!(uri == myself) && method == "INVITE") { sl_send_reply("403", "Call cannot be served here"); break; }; if (method == "REGISTER") { if (af == inet) { save("location-inet4"); } else if (af == inet6) { save("location-inet6"); } else { sl_send_reply("403", "Call cannot be served here"); }; break; }; if (method == "INVITE") { if (lookup("location-inet4")) { # Comment out three lines below if you want # RTP for IPv4->IPv4 calls to go directly # between UAs if (af == inet) if (rtpproxy_offer("FAII")) t_on_reply("1"); if (af == inet6) if (rtpproxy_offer("FAEI")) t_on_reply("1"); } else if (lookup("location-inet6")) { if (af == inet) if (rtpproxy_offer("FAIE")) t_on_reply("1"); # Comment out three lines below if you want # RTP for IPv6->IPv6 calls to go directly # between UAs if (af == inet6) if (rtpproxy_offer("FAEE")) t_on_reply("1"); } else { sl_send_reply("403", "Call cannot be served here"); break; }; } if (method == "BYE" || method == "CANCEL") unforce_rtp_proxy(); # Do strict routing if pre-loaded route headers present if (loose_route()) { t_relay(); break; }; if (method == "INVITE") record_route(); if (!t_relay()) sl_reply_error(); } onreply_route[1] { if (!(status=~"183" || status=~"200")) break; rtpproxy_answer("FA"); } opensips-2.2.2/modules/rtpproxy/examples/alg.cfg000066400000000000000000000052351300170765700220110ustar00rootroot00000000000000# $Id: alg.cfg 7232 2010-09-23 15:13:16Z osas $ # # Simple application level gateway config script. # # Assumes that SER/rtpproxy run on a machine, which connected to # two non-routable letworks: 192.168.0.0/24 and 192.168.1.1/24. # # Correspondingly, this machine has two IP addresses: 192.168.0.1 # and 192.168.1.1. # # 192.168.0.0/24 - "internal" network # 192.168.1.0/24 - "external" network # # rtpproxy started with `-l 192.168.0.1/192.168.1.1' option. # # ------------------ module loading ---------------------------------- loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" loadmodule "/usr/local/lib/ser/modules/nathelper.so" loadmodule "/usr/local/lib/ser/modules/rtpproxy.so" # ----------------- setting module-specific parameters --------------- # -- nathelper params -- modparam("nathelper", "natping_interval", 15) # ------------------ main fun below ---------------------------------- route { # initial sanity checks -- messages with # max_forwars == 0, or excessively long requests, # or those that don't addressed to us if (!mf_process_maxfwd_header("10")) { sl_send_reply("483", "Too Many Hops"); break; }; if (msg:len > max_len) { sl_send_reply("513", "Message too big"); break; }; if (!(uri == myself) && method == "INVITE") { sl_send_reply("403", "Call cannot be served here"); break; }; if (method == "REGISTER") { if (dst_ip == 192.168.0.1) { save("location-internal"); } else if (dst_ip == 192.168.1.1) { save("location-external"); } else { sl_send_reply("403", "Call cannot be served here"); }; break; }; if (method == "INVITE") { if (lookup("location-internal")) { if (dst_ip == 192.168.0.1) if (rtpproxy_offer("FAII")) t_on_reply("1"); if (dst_ip == 192.168.1.1) if (rtpproxy_offer("FAEI")) t_on_reply("1"); } else if (lookup("location-external")) { if (dst_ip == 192.168.0.1) if (rtpproxy_offer("FAIE")) t_on_reply("1"); if (dst_ip == 192.168.1.1) if (rtpproxy_offer("FAEE")) t_on_reply("1"); } else { sl_send_reply("403", "Call cannot be served here"); break; }; } if (method == "BYE" || method == "CANCEL") unforce_rtp_proxy(); # Do strict routing if pre-loaded route headers present if (loose_route()) { t_relay(); break; }; if (method == "INVITE") record_route(); if (!t_relay()) sl_reply_error(); } onreply_route[1] { if (!(status=~"183" || status=~"200")) break; rtpproxy_answer("FA"); } opensips-2.2.2/modules/rtpproxy/nhelpr_funcs.c000066400000000000000000000220111300170765700215700ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-11-06 body len is computed using the message len (it's * not taken any more from the msg. content-length) (andrei) * 2008-08-30 body len is taken from Conent-length header as it is more * reliable (UDP packages may contain garbage at the end)(bogdan) */ #include #include #include #include #include "nhelpr_funcs.h" #include "../../dprint.h" #include "../../config.h" #include "../../ut.h" #include "../../forward.h" #include "../../resolve.h" #include "../../globals.h" #include "../../pt.h" #include "../../parser/msg_parser.h" #include "../../trim.h" #include "../../parser/parse_from.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_content.h" #define READ(val) \ (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)) #define advance(_ptr,_n,_str,_error) \ do{\ if ((_ptr)+(_n)>(_str).s+(_str).len)\ goto _error;\ (_ptr) = (_ptr) + (_n);\ }while(0); #define one_of_16( _x , _t ) \ (_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\ ||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14]) #define one_of_8( _x , _t ) \ (_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ||_x==_t[5]||_x==_t[6]) int check_content_type(struct sip_msg *msg) { static unsigned int appl[16] = { 0x6c707061/*appl*/,0x6c707041/*Appl*/,0x6c705061/*aPpl*/, 0x6c705041/*APpl*/,0x6c507061/*apPl*/,0x6c507041/*ApPl*/, 0x6c505061/*aPPl*/,0x6c505041/*APPl*/,0x4c707061/*appL*/, 0x4c707041/*AppL*/,0x4c705061/*aPpL*/,0x4c705041/*APpL*/, 0x4c507061/*apPL*/,0x4c507041/*ApPL*/,0x4c505061/*aPPL*/, 0x4c505041/*APPL*/}; static unsigned int icat[16] = { 0x74616369/*icat*/,0x74616349/*Icat*/,0x74614369/*iCat*/, 0x74614349/*ICat*/,0x74416369/*icAt*/,0x74416349/*IcAt*/, 0x74414369/*iCAt*/,0x74414349/*ICAt*/,0x54616369/*icaT*/, 0x54616349/*IcaT*/,0x54614369/*iCaT*/,0x54614349/*ICaT*/, 0x54416369/*icAT*/,0x54416349/*IcAT*/,0x54414369/*iCAT*/, 0x54414349/*ICAT*/}; static unsigned int ion_[8] = { 0x006e6f69/*ion_*/,0x006e6f49/*Ion_*/,0x006e4f69/*iOn_*/, 0x006e4f49/*IOn_*/,0x004e6f69/*ioN_*/,0x004e6f49/*IoN_*/, 0x004e4f69/*iON_*/,0x004e4f49/*ION_*/}; static unsigned int sdp_[8] = { 0x00706473/*sdp_*/,0x00706453/*Sdp_*/,0x00704473/*sDp_*/, 0x00704453/*SDp_*/,0x00506473/*sdP_*/,0x00506453/*SdP_*/, 0x00504473/*sDP_*/,0x00504453/*SDP_*/}; str str_type; unsigned int x; char *p; if (!msg->content_type) { LM_WARN("the header Content-TYPE is absent!" "let's assume the content is text/plain ;-)\n"); return 1; } trim_len(str_type.len,str_type.s,msg->content_type->body); p = str_type.s; advance(p,4,str_type,error_1); x = READ(p-4); if (!one_of_16(x,appl)) goto other; advance(p,4,str_type,error_1); x = READ(p-4); if (!one_of_16(x,icat)) goto other; advance(p,3,str_type,error_1); x = READ(p-3) & 0x00ffffff; if (!one_of_8(x,ion_)) goto other; /* skip spaces and tabs if any */ while (*p==' ' || *p=='\t') advance(p,1,str_type,error_1); if (*p!='/') { LM_ERR("no / found after primary type\n"); goto error; } advance(p,1,str_type,error_1); while ((*p==' ' || *p=='\t') && p+1 found valid\n", (int)(p-str_type.s), str_type.s); return 1; } else { LM_ERR("bad end for type!\n"); return -1; } error_1: LM_ERR("body ended :-(!\n"); error: return -1; other: LM_ERR("invalid type for a message\n"); return -1; } /* * Get message body and check Content-Type header field */ int extract_body(struct sip_msg *msg, str *body ) { char c; int skip; if ( get_body(msg,body)!=0 || body->len==0) { LM_ERR("failed to get the message body\n"); goto error; } /* no need for parse_headers(msg, EOH), get_body will * parse everything */ /*is the content type correct?*/ if (check_content_type(msg)==-1) { LM_ERR("content type mismatching\n"); goto error; } for (skip = 0; skip < body->len; skip++) { c = body->s[body->len - skip - 1]; if (c != '\r' && c != '\n') break; } if (skip == body->len) { LM_ERR("empty body"); goto error; } body->len -= skip; /*LM_DBG("DEBUG:extract_body:%d=|%.*s|\n",body->len,body->len,body->s);*/ return 1; error: body->s = NULL; body->len = 0; return -1; } /* * ser_memmem() returns the location of the first occurrence of data * pattern b2 of size len2 in memory block b1 of size len1 or * NULL if none is found. Obtained from NetBSD. */ void * ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2) { /* Initialize search pointer */ char *sp = (char *) b1; /* Initialize pattern pointer */ char *pp = (char *) b2; /* Initialize end of search address space pointer */ char *eos = sp + len1 - len2; /* Sanity check */ if(!(b1 && b2 && len1 && len2)) return NULL; while (sp <= eos) { if (*sp == *pp) if (memcmp(sp, pp, len2) == 0) return sp; sp++; } return NULL; } /* * Some helper functions taken verbatim from tm module. */ /* * Extract Call-ID value * assumes the callid header is already parsed * (so make sure it is, before calling this function or * it might fail even if the message _has_ a callid) */ int get_callid(struct sip_msg* _m, str* _cid) { if ((parse_headers(_m, HDR_CALLID_F, 0) == -1)) { LM_ERR("failed to parse call-id header\n"); return -1; } if (_m->callid == NULL) { LM_ERR("call-id not found\n"); return -1; } _cid->s = _m->callid->body.s; _cid->len = _m->callid->body.len; trim(_cid); return 0; } /* * Extract tag from To header field of a response * assumes the to header is already parsed, so * make sure it really is before calling this function */ int get_to_tag(struct sip_msg* _m, str* _tag) { if (!_m->to) { LM_ERR("To header field missing\n"); return -1; } if (get_to(_m)->tag_value.len) { _tag->s = get_to(_m)->tag_value.s; _tag->len = get_to(_m)->tag_value.len; } else { _tag->s = NULL; /* fixes gcc 4.0 warnings */ _tag->len = 0; } return 0; } /* * Extract tag from From header field of a request */ int get_from_tag(struct sip_msg* _m, str* _tag) { if (parse_from_header(_m)<0) { LM_ERR("failed to parse From header\n"); return -1; } if (get_from(_m)->tag_value.len) { _tag->s = get_from(_m)->tag_value.s; _tag->len = get_from(_m)->tag_value.len; } else { _tag->s = NULL; /* fixes gcc 4.0 warnings */ _tag->len = 0; } return 0; } /* * Extract URI from the Contact header field - iterates through all contacts */ int get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c, struct hdr_field **_hdr) { if (*_hdr==NULL) { if ((parse_headers(_m, HDR_EOH_F, 0) == -1) || !_m->contact) return -1; if (!_m->contact->parsed && parse_contact(_m->contact) < 0) { LM_ERR("failed to parse Contact body\n"); return -1; } *_hdr = _m->contact; *_c = ((contact_body_t*)_m->contact->parsed)->contacts; } else { *_c = (*_c)->next; } while (*_c==NULL) { *_hdr = (*_hdr)->sibling; if (*_hdr==NULL) /* no more contact headers */ return -1; if (!(*_hdr)->parsed && parse_contact(*_hdr) < 0) { LM_ERR("failed to parse Contact body\n"); return -1; } *_c = ((contact_body_t*)(*_hdr)->parsed)->contacts; } if (*_c == NULL) /* no more contacts found */ return -1; /* contact found -> parse it */ if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri)<0 || uri->host.len<=0) { LM_ERR("failed to parse Contact URI\n"); return -1; } return 0; } opensips-2.2.2/modules/rtpproxy/nhelpr_funcs.h000066400000000000000000000024731300170765700216070ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _NHLPR_FUNCS_H #define _NHLPR_FUNCS_H #include "../../str.h" #include "../../parser/msg_parser.h" #include "../../parser/contact/contact.h" int extract_body(struct sip_msg * , str *); int check_content_type(struct sip_msg * ); void *ser_memmem(const void *, const void *, size_t, size_t); int get_callid(struct sip_msg *, str *); int get_to_tag(struct sip_msg *, str *); int get_from_tag(struct sip_msg *, str *); int get_contact_uri(struct sip_msg *, struct sip_uri *, contact_t **,struct hdr_field **); #endif opensips-2.2.2/modules/rtpproxy/patches/000077500000000000000000000000001300170765700203715ustar00rootroot00000000000000opensips-2.2.2/modules/rtpproxy/patches/rtpproxy_timeout_notification.fix_patch000066400000000000000000000112461300170765700305070ustar00rootroot00000000000000diff --git a/main.c b/main.c index 15ec6ee..7740191 100644 --- a/main.c +++ b/main.c @@ -68,6 +68,7 @@ static const char *cmd_sock = CMD_SOCK; static const char *pid_file = PID_FILE; static rtpp_log_t glog; +static char *notify_socket = NULL; static void usage(void); static void send_packet(struct cfg *, struct rtpp_session *, int, @@ -117,6 +118,7 @@ init_config(struct cfg *cf, int argc, char **argv) cf->port_max = PORT_MAX; cf->max_ttl = SESSION_TIMEOUT; + cf->max_setup_ttl = SESSION_TIMEOUT; cf->tos = TOS; cf->rrtcp = 1; cf->ttl_mode = TTL_UNIFIED; @@ -126,7 +128,7 @@ init_config(struct cfg *cf, int argc, char **argv) if (getrlimit(RLIMIT_NOFILE, &(cf->nofile_limit)) != 0) err(1, "getrlimit"); - while ((ch = getopt(argc, argv, "vf2Rl:6:s:S:t:r:p:T:L:m:M:u:Fin:Pad:")) != -1) + while ((ch = getopt(argc, argv, "vf2Rl:6:s:S:t:r:p:T:W:L:m:M:u:Fin:Pad:")) != -1) switch (ch) { case 'f': cf->nodaemon = 1; @@ -205,6 +207,10 @@ init_config(struct cfg *cf, int argc, char **argv) cf->max_ttl = atoi(optarg); break; + case 'W': + cf->max_setup_ttl = atoi(optarg); + break; + case 'L': cf->nofile_limit.rlim_cur = cf->nofile_limit.rlim_max = atoi(optarg); if (setrlimit(RLIMIT_NOFILE, &(cf->nofile_limit)) != 0) @@ -266,9 +272,7 @@ init_config(struct cfg *cf, int argc, char **argv) optarg += 5; if(strlen(optarg) == 0) errx(1, "timeout notification socket name too short"); - cf->timeout_handler = rtpp_notify_init(optarg); - if (cf->timeout_handler == NULL) - errx(1, "can't start notification thread"); + notify_socket = strdup(optarg); break; case 'P': @@ -764,6 +768,14 @@ main(int argc, char **argv) atexit(ehandler); rtpp_log_write(RTPP_LOG_INFO, cf.glog, "rtpproxy started, pid %d", getpid()); + if (notify_socket) { + cf.timeout_handler = rtpp_notify_init(notify_socket); + if (!cf.timeout_handler) { + rtpp_log_ewrite(RTPP_LOG_ERR, cf.glog, "can't start notification thread"); + exit(1); + } + } + i = open(pid_file, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE); if (i >= 0) { len = sprintf(buf, "%u\n", (unsigned int)getpid()); @@ -772,7 +784,7 @@ main(int argc, char **argv) } else { rtpp_log_ewrite(RTPP_LOG_ERR, cf.glog, "can't open pidfile for writing"); } - + signal(SIGHUP, fatsignal); signal(SIGINT, fatsignal); signal(SIGKILL, fatsignal); diff --git a/rtpp_command.c b/rtpp_command.c index a2c126f..02fa7e3 100644 --- a/rtpp_command.c +++ b/rtpp_command.c @@ -795,8 +795,14 @@ handle_command(struct cfg *cf, int controlfd, double dtime) lia[0] = spa->laddr[i]; pidx = (i == 0) ? 1 : 0; spa->ttl_mode = cf->ttl_mode; - spa->ttl[0] = cf->max_ttl; - spa->ttl[1] = cf->max_ttl; + if (op == UPDATE) { + spa->ttl[0] = cf->max_setup_ttl; + spa->ttl[1] = cf->max_setup_ttl; + } else { + spa->ttl[0] = cf->max_ttl; + spa->ttl[1] = cf->max_ttl; + } + if (op == UPDATE) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "adding %s flag to existing session, new=%d/%d/%d", @@ -882,8 +888,8 @@ handle_command(struct cfg *cf, int controlfd, double dtime) spb->fds[0] = fds[1]; spa->ports[0] = lport; spb->ports[0] = lport + 1; - spa->ttl[0] = cf->max_ttl; - spa->ttl[1] = cf->max_ttl; + spa->ttl[0] = cf->max_setup_ttl; + spa->ttl[1] = cf->max_setup_ttl; spb->ttl[0] = -1; spb->ttl[1] = -1; spa->log = rtpp_log_open(cf, "rtpproxy", spa->call_id, 0); diff --git a/rtpp_defines.h b/rtpp_defines.h index 0c54838..c0c761b 100644 --- a/rtpp_defines.h +++ b/rtpp_defines.h @@ -95,6 +95,7 @@ struct cfg { unsigned long long sessions_created; int sessions_active; int max_ttl; + int max_setup_ttl; /* * The first address is for external interface, the second one - for * internal one. Second can be NULL, in this case there is no bridge diff --git a/rtpp_notify.c b/rtpp_notify.c index 20e6990..33dff2d 100644 --- a/rtpp_notify.c +++ b/rtpp_notify.c @@ -207,6 +207,7 @@ parse_timeout_sock(const char *sock_name, struct rtpp_timeout_handler *timeout_h return 0; } + struct rtpp_timeout_handler * rtpp_notify_init(const char *socket_name) { @@ -331,7 +332,7 @@ reconnect_timeout_handler(rtpp_log_t log, struct rtpp_timeout_handler *th) assert (parse_hostport(th->socket_name, host, sizeof(host), port, sizeof(port), 0) == 0); n = resolve(sstosa(&remote.i), AF_INET, host, port, AI_PASSIVE); if (n != 0) { - rtpp_log_write(RTPP_LOG_ERR, log, "reconnect_timeout_handler: getaddrinfo('%s:s'): %s", + rtpp_log_write(RTPP_LOG_ERR, log, "reconnect_timeout_handler: getaddrinfo('%s:%s'): %s", host, port, gai_strerror(n)); return; } opensips-2.2.2/modules/rtpproxy/rtpproxy.c000066400000000000000000003216471300170765700210320ustar00rootroot00000000000000/* * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-12-01 unforce_rtp_proxy introduced (sobomax) * * 2004-01-07 RTP proxy support updated to support new version of the * RTP proxy (20040107). * * force_rtp_proxy() now inserts a special flag * into the SDP body to indicate that this session already * proxied and ignores sessions with such flag. * * Added run-time check for version of command protocol * supported by the RTP proxy. * * 2004-01-16 Integrated slightly modified patch from Tristan Colgate, * force_rtp_proxy function with IP as a parameter (janakj) * * 2004-02-21 force_rtp_proxy now accepts option argument, which * consists of string of chars, each of them turns "on" * some feature, currently supported ones are: * * `a' - flags that UA from which message is received * doesn't support symmetric RTP; * `l' - force "lookup", that is, only rewrite SDP when * corresponding session is already exists in the * RTP proxy. Only makes sense for SIP requests, * replies are always processed in "lookup" mode; * `i' - flags that message is received from UA in the * LAN. Only makes sense when RTP proxy is running * in the bridge mode. * * force_rtp_proxy can now be invoked without any arguments, * as previously, with one argument - in this case argument * is treated as option string and with two arguments, in * which case 1st argument is option string and the 2nd * one is IP address which have to be inserted into * SDP (IP address on which RTP proxy listens). * * 2004-03-12 Added support for IPv6 addresses in SDPs. Particularly, * force_rtp_proxy now can work with IPv6-aware RTP proxy, * replacing IPv4 address in SDP with IPv6 one and vice versa. * This allows creating full-fledged IPv4<->IPv6 gateway. * See 4to6.cfg file for example. * * Two new options added into force_rtp_proxy: * * `f' - instructs rtpproxy to ignore marks inserted * by another rtpproxy in transit to indicate * that the session is already goes through another * proxy. Allows creating chain of proxies. * `r' - flags that IP address in SDP should be trusted. * Without this flag, rtpproxy ignores address in the * SDP and uses source address of the SIP message * as media address which is passed to the RTP proxy. * * Protocol between rtpproxy and RTP proxy in bridge * mode has been slightly changed. Now RTP proxy expects SER * to provide 2 flags when creating or updating session * to indicate direction of this session. Each of those * flags can be either `e' or `i'. For example `ei' means * that we received INVITE from UA on the "external" network * network and will send it to the UA on "internal" one. * Also possible `ie' (internal->external), `ii' * (internal->internal) and `ee' (external->external). See * example file alg.cfg for details. * * 2004-03-15 If the rtp proxy test failed (wrong version or not started) * retry test from time to time, when some *rtpproxy* function * is invoked. Minimum interval between retries can be * configured via rtpproxy_disable_tout module parameter (default * is 60 seconds). Setting it to -1 will disable periodic * rechecks completely, setting it to 0 will force checks * for each *rtpproxy* function call. (andrei) * * 2004-03-22 Fix assignment of rtpproxy_retr and rtpproxy_tout module * parameters. * * 2004-03-22 Fix get_body position (should be called before get_callid) * (andrei) * * 2004-03-24 Fix newport for null ip address case (e.g onhold re-INVITE) * (andrei) * * 2004-09-30 added received port != via port test (andrei) * * 2004-10-10 force_socket option introduced (jiri) * * 2005-02-24 Added support for using more than one rtp proxy, in which * case traffic will be distributed evenly among them. In addition, * each such proxy can be assigned a weight, which will specify * which share of the traffic should be placed to this particular * proxy. * * Introduce fail-over mechanism, so that if SER detects that one * of many proxies is no longer available it temporarily decreases * its weight to 0, so that no traffic will be assigned to it. * Such "disabled" proxies are periodically checked to see if they * are back to normal in which case respective weight is restored * resulting in traffic being sent to that proxy again. * * Those features can be enabled by specifying more than one "URI" * in the rtpproxy_sock parameter, optionally followed by the weight, * which if absent is assumed to be 1, for example: * * rtpproxy_sock="unix:/foo/bar=4 udp:1.2.3.4:3456=3 udp:5.6.7.8:5432=1" * * 2005-03-22 support for multiple media streams added (netch) * 2005-07-14 SDP origin (o=) IP may be also changed (bogdan) * 2006-03-28 Support for changing session-level SDP connection (c=) IP when * media-description also includes connection information (bayan) * 2007-04-13 Support multiple sets of rtp-proxies and set selection added * (ancuta) * 2007-04-26 Added some MI commands: * nh_enable_rtpp used to enable or disable a specific rtp proxy * nh_show_rtpp used to display information for all rtp proxies * (ancuta) * 2007-05-09 New function start_recording() allowing to start recording RTP * session in the RTP proxy (Carsten Bock - ported from SER) * - obsolete by rtpproxy_offer/rtpproxy_answer * (osas) */ #include #include #ifndef __USE_BSD #define __USE_BSD #endif #include #include #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif #include #include #include #include #include "../../dprint.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../error.h" #include "../../forward.h" #include "../../mem/mem.h" #include "../../mod_fix.h" #include "../../timer.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../parser/parse_uri.h" #include "../../parser/parser_f.h" #include "../../parser/sdp/sdp_helpr_funcs.h" #include "../../db/db.h" #include "../../parser/parse_content.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_multipart.h" #include "../../msg_callbacks.h" #include "../../evi/evi_modules.h" #include "../dialog/dlg_load.h" #include "../tm/tm_load.h" #include "rtpproxy.h" #include "nhelpr_funcs.h" #include "rtpproxy_stream.h" #include "rtpproxy_callbacks.h" #define NH_TABLE_VERSION 0 #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #if !defined(PF_LOCAL) #define PF_LOCAL PF_UNIX #endif #define DEFAULT_RTPP_SET_ID 0 #define MI_ENABLE_RTP_PROXY "rtpproxy_enable" #define MI_MIN_RECHECK_TICKS 0 #define MI_MAX_RECHECK_TICKS (unsigned int)-1 #define MI_SHOW_RTP_PROXIES "rtpproxy_show" #define MI_RELOAD_RTP_PROXIES "rtpproxy_reload" #define MI_RTP_PROXY_NOT_FOUND "RTP proxy not found" #define MI_RTP_PROXY_NOT_FOUND_LEN (sizeof(MI_RTP_PROXY_NOT_FOUND)-1) #define MI_PING_DISABLED "NATping disabled from script" #define MI_PING_DISABLED_LEN (sizeof(MI_PING_DISABLED)-1) #define MI_SET "Set" #define MI_SET_LEN (sizeof(MI_SET)-1) #define MI_NODE "node" #define MI_NODE_LEN (sizeof(MI_NODE)-1) #define MI_INDEX "index" #define MI_INDEX_LEN (sizeof(MI_INDEX)-1) #define MI_DISABLED "disabled" #define MI_DISABLED_LEN (sizeof(MI_DISABLED)-1) #define MI_WEIGHT "weight" #define MI_WEIGHT_LEN (sizeof(MI_WEIGHT)-1) #define MI_RECHECK_TICKS "recheck_ticks" #define MI_RECHECK_T_LEN (sizeof(MI_RECHECK_TICKS)-1) /* Supported version of the RTP proxy command protocol */ #define SUP_CPROTOVER 20040107 /* Required additional version of the RTP proxy command protocol */ #define REQ_CPROTOVER "20050322" /* Additional version necessary for re-packetization support */ #define REP_CPROTOVER "20071116" #define PTL_CPROTOVER "20081102" /* Support for auto-bridging */ #define ABR_CPROTOVER "20090810" #define CPORT "22222" /* param names to be stored in the dialog */ static str param1_name = str_init("rtpproxy_1"); str param1_bavp_name = str_init("$bavp(5589965)"); pv_spec_t param1_spec; static str param2_name = str_init("rtpproxy_2"); str param2_bavp_name = str_init("$bavp(5589966)"); pv_spec_t param2_spec; static str param3_name = str_init("rtpproxy_3"); str param3_bavp_name = str_init("$bavp(5589967)"); pv_spec_t param3_spec; static str late_name = str_init("late_negotiation"); /* parameters name for event signaling */ static str event_name = str_init("E_RTPPROXY_STATUS"); static str socket_name = str_init("socket"); static str status_name = str_init("status"); static str status_connected = str_init("active"); static str status_disconnected = str_init("inactive"); static int extract_mediainfo(str *, str *, str *); static int alter_mediaip(struct sip_msg *, str *, str *, int, str *, int, int); static char *gencookie(); static int rtpp_test(struct rtpp_node*, int, int); static int unforce_rtp_proxy_f(struct sip_msg *, char *, char *); static int engage_rtp_proxy4_f(struct sip_msg *, char *, char *, char *, char *); static int fixup_engage(void **param,int param_no); static int force_rtp_proxy(struct sip_msg *, char *, char *, char *, char *, int); static int start_recording_f(struct sip_msg *, char *, char *); static int rtpproxy_answer4_f(struct sip_msg *, char *, char *, char *, char *); static int rtpproxy_offer4_f(struct sip_msg *, char *, char *, char *, char *); static int add_rtpproxy_socks(struct rtpp_set * rtpp_list, char * rtpproxy); static int fixup_set_id(void ** param); static int fixup_stream(void ** param, int param_no); static int fixup_offer_answer(void ** param, int param_no); static int fixup_two_options(void ** param, int param_no); static int fixup_unforce_warn(void ** param, int param_no); static int fixup_engage_warn(void ** param, int param_no); static int fixup_recording_warn(void ** param, int param_no); static struct rtpp_set * select_rtpp_set(int id_set); static int rtpproxy_set_store(modparam_t type, void * val); static int rtpproxy_add_rtpproxy_set( char * rtp_proxies, int set_id); static int _add_proxies_from_database(); static int unforce_rtpproxy(struct sip_msg* msg, str callid, str from_tag, str to_tag, char *pset, char *var); static int mod_init(void); static int child_init(int); static void mod_destroy(void); static int mi_child_init(void); static int engage_force_rtpproxy(struct dlg_cell *dlg, struct sip_msg *msg); /*mi commands*/ static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree, void* param ); static struct mi_root* mi_show_rtpproxies(struct mi_root* cmd_tree, void* param); static struct mi_root* mi_reload_rtpproxies(struct mi_root* cmd_tree, void* param); void free_rtpp_nodes(struct rtpp_set *); void free_rtpp_sets(); int msg_has_sdp(struct sip_msg *msg); struct dlg_binds dlg_api; /* TM support for saving parameters */ struct tm_binds tm_api; struct rtpp_notify_head * rtpp_notify_h = 0; int connect_rtpproxies(); int update_rtpp_proxies(); static inline void raise_rtpproxy_event(struct rtpp_node *node, int status); static struct { const char *s; int len; int is_rtp; } sup_ptypes[] = { {.s = "udp", .len = 3, .is_rtp = 0}, {.s = "udptl", .len = 5, .is_rtp = 0}, {.s = "rtp/avp", .len = 7, .is_rtp = 1}, {.s = "rtp/avpf", .len = 8, .is_rtp = 1}, {.s = "rtp/savp", .len = 8, .is_rtp = 1}, {.s = "rtp/savpf", .len = 9, .is_rtp = 1}, {.s = "udp/bfcp", .len = 8, .is_rtp = 0}, {.s = NULL, .len = 0, .is_rtp = 0} }; static int rtpproxy_disable_tout = 60; static int rtpproxy_retr = 5; static int rtpproxy_tout = -1; static char *rtpproxy_timeout = 0; static int rtpproxy_autobridge = 0; static pid_t mypid; static unsigned int myseqn = 0; static str nortpproxy_str = str_init("a=nortpproxy:yes"); str rtpp_notify_socket = {0, 0}; int rtpp_notify_socket_un = 0; /* used in rtpproxy_set_store() */ static int rtpp_sets=0; static char **rtpp_strings=0; static int rtpp_set_count = 0; /* RTP proxy balancing list */ struct rtpp_set_head ** rtpp_set_list =0; struct rtpp_set ** default_rtpp_set=0; /* array with the sockets used by rtpporxy (per process)*/ static int *rtpp_socks = 0; static unsigned int *rtpp_no = 0; static unsigned int *list_version; static unsigned int my_version = 0; static unsigned int rtpp_number = 0; /* DB support for loading proxies */ static str db_url = {NULL, 0}; static str table = str_init("rtpproxy_sockets"); static str rtpp_sock_col = str_init("rtpproxy_sock"); static str set_id_col = str_init("set_id"); static db_con_t *db_connection = NULL; static db_func_t db_functions; static event_id_t ei_id = EVI_ERROR; rw_lock_t *nh_lock=NULL; static cmd_export_t cmds[] = { {"unforce_rtp_proxy", (cmd_function)unforce_rtp_proxy_f, 0, fixup_unforce_warn, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"unforce_rtp_proxy", (cmd_function)unforce_rtp_proxy_f, 1, fixup_unforce_warn, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"unforce_rtp_proxy", (cmd_function)unforce_rtp_proxy_f, 2, fixup_unforce_warn, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"engage_rtp_proxy", (cmd_function)engage_rtp_proxy4_f, 0, fixup_engage_warn, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"engage_rtp_proxy", (cmd_function)engage_rtp_proxy4_f, 1, fixup_engage_warn, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"engage_rtp_proxy", (cmd_function)engage_rtp_proxy4_f, 2, fixup_engage_warn, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"engage_rtp_proxy", (cmd_function)engage_rtp_proxy4_f, 3, fixup_engage_warn, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"engage_rtp_proxy", (cmd_function)engage_rtp_proxy4_f, 4, fixup_engage_warn, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"start_recording", (cmd_function)start_recording_f, 0, fixup_recording_warn, 0, REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"start_recording", (cmd_function)start_recording_f, 1, fixup_recording_warn, 0, REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"start_recording", (cmd_function)start_recording_f, 2, fixup_recording_warn, 0, REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"rtpproxy_unforce", (cmd_function)unforce_rtp_proxy_f, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_unforce", (cmd_function)unforce_rtp_proxy_f, 1, fixup_two_options, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_unforce", (cmd_function)unforce_rtp_proxy_f, 2, fixup_two_options, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_engage", (cmd_function)engage_rtp_proxy4_f, 0, fixup_engage, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_engage", (cmd_function)engage_rtp_proxy4_f, 1, fixup_engage, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_engage", (cmd_function)engage_rtp_proxy4_f, 2, fixup_engage, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_engage", (cmd_function)engage_rtp_proxy4_f, 3, fixup_engage, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_engage", (cmd_function)engage_rtp_proxy4_f, 4, fixup_engage, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_start_recording", (cmd_function)start_recording_f, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"rtpproxy_start_recording", (cmd_function)start_recording_f, 1, fixup_two_options, 0, REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"rtpproxy_start_recording", (cmd_function)start_recording_f, 2, fixup_two_options, 0, REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, {"rtpproxy_offer", (cmd_function)rtpproxy_offer4_f, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_offer", (cmd_function)rtpproxy_offer4_f, 1, fixup_spve_null, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_offer", (cmd_function)rtpproxy_offer4_f, 2, fixup_spve_spve, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_offer", (cmd_function)rtpproxy_offer4_f, 3, fixup_offer_answer, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_offer", (cmd_function)rtpproxy_offer4_f, 4, fixup_offer_answer, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_answer", (cmd_function)rtpproxy_answer4_f, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_answer", (cmd_function)rtpproxy_answer4_f, 1, fixup_spve_null, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_answer", (cmd_function)rtpproxy_answer4_f, 2, fixup_spve_spve, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_answer", (cmd_function)rtpproxy_answer4_f, 3, fixup_offer_answer, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_answer", (cmd_function)rtpproxy_answer4_f, 4, fixup_offer_answer, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"rtpproxy_stream2uac",(cmd_function)rtpproxy_stream2uac4_f, 2, fixup_stream, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stream2uac",(cmd_function)rtpproxy_stream2uac4_f, 3, fixup_stream, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stream2uac",(cmd_function)rtpproxy_stream2uac4_f, 4, fixup_stream, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stream2uas",(cmd_function)rtpproxy_stream2uas4_f, 2, fixup_stream, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stream2uas",(cmd_function)rtpproxy_stream2uas4_f, 3, fixup_stream, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stream2uas",(cmd_function)rtpproxy_stream2uas4_f, 4, fixup_stream, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stop_stream2uac",(cmd_function)rtpproxy_stop_stream2uac2_f,0, NULL, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stop_stream2uac",(cmd_function)rtpproxy_stop_stream2uac2_f,1, fixup_two_options, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stop_stream2uac",(cmd_function)rtpproxy_stop_stream2uac2_f,2, fixup_two_options, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stop_stream2uas",(cmd_function)rtpproxy_stop_stream2uas2_f,0, NULL, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stop_stream2uas",(cmd_function)rtpproxy_stop_stream2uas2_f,1, fixup_two_options, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"rtpproxy_stop_stream2uas",(cmd_function)rtpproxy_stop_stream2uas2_f,2, fixup_two_options, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {0, 0, 0, 0, 0, 0} }; static param_export_t params[] = { {"nortpproxy_str", STR_PARAM, &nortpproxy_str.s }, {"rtpproxy_sock", STR_PARAM|USE_FUNC_PARAM, (void*)rtpproxy_set_store }, {"rtpproxy_disable_tout", INT_PARAM, &rtpproxy_disable_tout }, {"rtpproxy_retr", INT_PARAM, &rtpproxy_retr }, {"rtpproxy_tout", INT_PARAM, &rtpproxy_tout }, {"rtpproxy_timeout", STR_PARAM, &rtpproxy_timeout }, {"rtpproxy_autobridge", INT_PARAM, &rtpproxy_autobridge }, {"db_url", STR_PARAM, &db_url.s }, {"db_table", STR_PARAM, &table.s }, {"rtpp_socket_col", STR_PARAM, &rtpp_sock_col.s }, {"set_id_col", STR_PARAM, &set_id_col.s }, {"rtpp_notify_socket", STR_PARAM, &rtpp_notify_socket.s }, {0, 0, 0} }; static mi_export_t mi_cmds[] = { {MI_ENABLE_RTP_PROXY, 0, mi_enable_rtp_proxy, 0, 0, 0}, {MI_SHOW_RTP_PROXIES, 0, mi_show_rtpproxies, MI_NO_INPUT_FLAG, 0, 0}, {MI_RELOAD_RTP_PROXIES, 0, mi_reload_rtpproxies, MI_NO_INPUT_FLAG, 0, mi_child_init}, { 0, 0, 0, 0, 0, 0} }; static proc_export_t procs[] = { {"RTPP timeout receiver", 0, 0, timeout_listener_process, 1, 0}, {0,0,0,0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_DEFAULT, "dialog", DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "db_url", get_deps_sqldb_url }, { NULL, NULL }, }, }; struct module_exports exports = { "rtpproxy", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, NULL, params, 0, /* exported statistics */ mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ procs, /* extra processes */ mod_init, 0, /* reply processing */ mod_destroy, /* destroy function */ child_init }; static int rtpproxy_set_store(modparam_t type, void * val){ char * p; int len; p = (char* )val; if(p==0 || *p=='\0'){ return 0; } if(rtpp_sets==0){ rtpp_strings = (char**)pkg_malloc(sizeof(char*)); if(!rtpp_strings){ LM_ERR("no pkg memory left\n"); return -1; } } else {/*realloc to make room for the current set*/ rtpp_strings = (char**)pkg_realloc(rtpp_strings, (rtpp_sets+1)* sizeof(char*)); if(!rtpp_strings){ LM_ERR("no pkg memory left\n"); return -1; } } /*allocate for the current set of urls*/ len = strlen(p); rtpp_strings[rtpp_sets] = (char*)pkg_malloc((len+1)*sizeof(char)); if(!rtpp_strings[rtpp_sets]){ LM_ERR("no pkg memory left\n"); return -1; } memcpy(rtpp_strings[rtpp_sets], p, len); rtpp_strings[rtpp_sets][len] = '\0'; rtpp_sets++; return 0; } static int add_rtpproxy_socks(struct rtpp_set * rtpp_list, char * rtpproxy){ /* Make rtp proxies list. */ char *p, *p1, *p2, *plim; struct rtpp_node *pnode; int weight; p = rtpproxy; plim = p + strlen(p); for(;;) { weight = 1; while (*p && isspace((int)*p)) ++p; if (p >= plim) break; p1 = p; while (*p && !isspace((int)*p)) ++p; if (p <= p1) break; /* may happen??? */ /* Have weight specified? If yes, scan it */ p2 = memchr(p1, '=', p - p1); if (p2 != NULL) { weight = strtoul(p2 + 1, NULL, 10); } else { p2 = p; } pnode = shm_malloc(sizeof(struct rtpp_node)); if (pnode == NULL) { LM_ERR("no shm memory left\n"); return -1; } memset(pnode, 0, sizeof(*pnode)); pnode->idx = *rtpp_no; *rtpp_no = *rtpp_no + 1; pnode->rn_recheck_ticks = 0; pnode->rn_weight = weight; pnode->rn_umode = 0; pnode->rn_disabled = 0; pnode->rn_url.s = shm_malloc(p2 - p1 + 1); if (pnode->rn_url.s == NULL) { shm_free(pnode); LM_ERR("no shm memory left\n"); return -1; } memmove(pnode->rn_url.s, p1, p2 - p1); pnode->rn_url.s[p2 - p1] = 0; pnode->rn_url.len = p2-p1; LM_DBG("url is %s, len is %i\n", pnode->rn_url.s, pnode->rn_url.len); /* Leave only address in rn_address */ pnode->rn_address = pnode->rn_url.s; if (strncasecmp(pnode->rn_address, "udp:", 4) == 0) { pnode->rn_umode = 1; pnode->rn_address += 4; } else if (strncasecmp(pnode->rn_address, "udp6:", 5) == 0) { pnode->rn_umode = 6; pnode->rn_address += 5; } else if (strncasecmp(pnode->rn_address, "unix:", 5) == 0) { pnode->rn_umode = 0; pnode->rn_address += 5; } if (rtpp_list->rn_first == NULL) { rtpp_list->rn_first = pnode; } else { rtpp_list->rn_last->rn_next = pnode; } rtpp_list->rn_last = pnode; rtpp_list->rtpp_node_count++; } return 0; } /* 0-success * -1 - error * */ static int rtpproxy_add_rtpproxy_set( char * rtp_proxies, int set_id) { char *p,*p2; struct rtpp_set * rtpp_list; unsigned int my_current_id; str id_set; int new_list; /* empty definition? */ p= rtp_proxies; if(!p || *p=='\0'){ return 0; } for(;*p && isspace(*p);p++); if(*p=='\0'){ return 0; } if(set_id < 0) { rtp_proxies = strstr(p, "=="); if(rtp_proxies){ if(*(rtp_proxies +2)=='\0'){ LM_ERR("script error -invalid rtp proxy list!\n"); return -1; } *rtp_proxies = '\0'; p2 = rtp_proxies-1; for(;isspace(*p2); *p2 = '\0',p2--); id_set.s = p; id_set.len = p2 - p+1; if(id_set.len <= 0 ||str2int(&id_set, &my_current_id)<0 ){ LM_ERR("script error -invalid set_id value!\n"); return -1; } rtp_proxies+=2; }else{ rtp_proxies = p; my_current_id = DEFAULT_RTPP_SET_ID; } } else{ rtp_proxies = p; my_current_id = set_id; } LM_DBG("Adding proxy in set-id %d [%s]\n", my_current_id, rtp_proxies); for(;*rtp_proxies && isspace(*rtp_proxies);rtp_proxies++); if(!(*rtp_proxies)){ LM_ERR("script error -empty rtp_proxy list\n"); return -1; } /*search for the current_id*/ rtpp_list = (*rtpp_set_list) ? (*rtpp_set_list)->rset_first : 0; while( rtpp_list != 0 && rtpp_list->id_set!=my_current_id) rtpp_list = rtpp_list->rset_next; LM_DBG("List %sfound (%p) for id %d", rtpp_list ? "" : "not ", rtpp_list, my_current_id); if(rtpp_list==NULL){ /*if a new id_set : add a new set of rtpp*/ rtpp_list = shm_malloc(sizeof(struct rtpp_set)); if(!rtpp_list){ LM_ERR("no shm memory left\n"); return -1; } memset(rtpp_list, 0, sizeof(struct rtpp_set)); rtpp_list->id_set = my_current_id; new_list = 1; } else { new_list = 0; } if(add_rtpproxy_socks(rtpp_list, rtp_proxies)!= 0){ /*if this list will not be inserted, clean it up*/ goto error; } if (new_list) { if(!(*rtpp_set_list)){/*initialize the list of set - executed only on the first call*/ *rtpp_set_list = shm_malloc(sizeof(struct rtpp_set_head)); if(!(*rtpp_set_list)){ LM_ERR("no shm memory left\n"); return -1; } memset(*rtpp_set_list, 0, sizeof(struct rtpp_set_head)); } /*update the list of set info*/ if(!(*rtpp_set_list)->rset_first){ (*rtpp_set_list)->rset_first = rtpp_list; }else{ (*rtpp_set_list)->rset_last->rset_next = rtpp_list; } (*rtpp_set_list)->rset_last = rtpp_list; rtpp_set_count++; } return 0; error: return -1; } static void fixup_deprecated_warn(char *oldf, char *newf) { LM_WARN("function %s() is now deprecated - use %s() instead\n", oldf, newf); } static int fixup_unforce_warn(void ** param, int param_no) { static int warned = 0; if (!warned) { fixup_deprecated_warn("unforce_rtp_proxy", "rtpproxy_unforce"); warned = 1; } return param_no > 0 ? fixup_two_options(param, param_no) : 0; } static int fixup_recording_warn(void ** param, int param_no) { static int warned = 0; if (!warned) { fixup_deprecated_warn("start_recording", "rtpproxy_start_recording"); warned = 1; } return param_no > 0 ? fixup_two_options(param, param_no) : 0; } static int fixup_two_options(void ** param, int param_no) { if (param_no == 1) return fixup_set_id(param); if (param_no == 2) return fixup_pvar(param); LM_ERR("Too many parameters %d\n", param_no); return E_CFG; } static int fixup_offer_answer(void ** param, int param_no) { if (param_no < 1) return 0; if (param_no < 3) return fixup_spve(param); if (param_no == 3) return fixup_set_id(param); if (param_no == 4) return fixup_pvar(param); LM_ERR("Too many parameters %d\n", param_no); return E_CFG; } static int fixup_set_id(void ** param) { int int_val, err; struct rtpp_set* rtpp_list; nh_set_param_t * pset; char *p; pset = (nh_set_param_t *) pkg_malloc(sizeof(nh_set_param_t)); if(pset == NULL){ LM_ERR("no more pkg memory to allocate set parameter\n"); return E_OUT_OF_MEM; } memset(pset, 0, sizeof(nh_set_param_t)); p = (char*) *param; if(*p != '$') { /* Fixed value specified for RTP proxy set */ int_val = str2s(p, strlen(p), &err); if (err == 0) { pkg_free(*param); rtpp_list = select_rtpp_set(int_val); if(rtpp_list ==0){ /* simply mark it as undefined and we search it one more time * at run-time, after the database has been updated */ pset->t = NH_VAL_SET_UNDEF; pset->v.int_set = int_val; } else { pset->t = NH_VAL_SET_FIXED ; pset->v.fixed_set = rtpp_list; } *param = (void *) pset; return 0; } else { LM_ERR("bad rtp set number <%s> specified\n", p); pkg_free(pset); return E_CFG; } } else { /* proxy-set is specified as an AVP */ str lstr; lstr.s = p; lstr.len = strlen(p); if ( pv_parse_spec( &lstr, &pset->v.var_set) == NULL ) { LM_ERR("bad rtp set variable <%s> specified\n", p); pkg_free(pset); return E_CFG; } pset->t = NH_VAL_SET_SPEC; *param = (void *) pset; return 0; } } static int fixup_stream(void **param, int param_no) { int ret; pv_elem_t *model; str s; if (param_no == 1) { model = NULL; s.s = (char *)(*param); s.len = strlen(s.s); if (pv_parse_format(&s, &model) < 0) { LM_ERR("wrong format[%s]!\n", (char *)(*param)); return E_UNSPEC; } if (model == NULL) { LM_ERR("empty parameter!\n"); return E_UNSPEC; } *param = (void *)model; } else if (param_no == 2) { s.s = (char *)(*param); s.len = strlen(s.s); if (str2sint(&s, &ret) < 0) { LM_ERR("bad number <%s>\n", (char *)(*param)); return E_CFG; } pkg_free(*param); *param = (void *)(long)ret; } else if (param_no == 3) { return fixup_set_id(param); } else if (param_no == 4) { return fixup_pvar(param); } return 0; } static int fixup_engage_warn(void ** param, int param_no) { static int warned = 0; if (!warned) { fixup_deprecated_warn("engage_rtp_proxy", "rtpproxy_engage"); warned = 1; } return param_no > 0 ? fixup_engage(param, param_no) : 0; } static int fixup_engage(void** param, int param_no) { if (param_no < 2 && !dlg_api.create_dlg) { LM_ERR("Dialog module not loaded. Can't use engage_rtp_proxy function\n"); return -1; } return fixup_offer_answer(param, param_no); } static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree, void* param ) { struct mi_node* node; str rtpp_url; unsigned int enable; unsigned int set_id; struct rtpp_set * rtpp_list; struct rtpp_node * crt_rtpp; int found; found = 0; if(*rtpp_set_list ==NULL) goto end; node = cmd_tree->node.kids; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* RTPP URL node */ if(node->value.s == NULL || node->value.len ==0) return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); rtpp_url = node->value; /* enable/disable node */ node = node->next; if(node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); enable = 0; if( strno2int( &node->value, &enable) <0) goto error; /* set id ?? */ node = node->next; if(node != NULL) { /* shift params -> move enable over set id */ set_id = enable; /* read again the disable */ enable = 0; if( strno2int( &node->value, &enable) <0) goto error; } else { set_id = (unsigned int)(-1); } for(rtpp_list = (*rtpp_set_list)->rset_first; rtpp_list != NULL; rtpp_list = rtpp_list->rset_next){ /* if set_id given, check only the list with the matching set_id */ if ( (set_id!=(unsigned int)(-1)) && set_id!=rtpp_list->id_set ) continue; for(crt_rtpp = rtpp_list->rn_first; crt_rtpp != NULL; crt_rtpp = crt_rtpp->rn_next){ /*found a matching rtpp*/ if(crt_rtpp->rn_url.len == rtpp_url.len){ if(strncmp(crt_rtpp->rn_url.s, rtpp_url.s, rtpp_url.len) == 0){ /*set the enabled/disabled status*/ found = 1; crt_rtpp->rn_recheck_ticks = enable? MI_MIN_RECHECK_TICKS : MI_MAX_RECHECK_TICKS; crt_rtpp->rn_disabled = enable?0:1; raise_rtpproxy_event(crt_rtpp, crt_rtpp->rn_disabled); } } } } end: if(found) return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); return init_mi_tree(404,MI_RTP_PROXY_NOT_FOUND,MI_RTP_PROXY_NOT_FOUND_LEN); error: return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } #define add_rtpp_node_int_info(_parent, _name, _name_len, _value, _attr,\ _len, _string, _error)\ do {\ (_string) = int2str((_value), &(_len));\ if((_string) == 0){\ LM_ERR("cannot convert int value\n");\ goto _error;\ }\ if(((_attr) = add_mi_attr((_parent), MI_DUP_VALUE, (_name), \ (_name_len), (_string), (_len)) ) == 0)\ goto _error;\ }while(0); static struct mi_root* mi_reload_rtpproxies(struct mi_root* cmd_tree, void* param) { struct rtpp_set *it; if(db_url.s == NULL) { LM_ERR("Dynamic loading of rtpproxies not enabled\n"); return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } lock_start_write( nh_lock ); if(*rtpp_set_list) { for (it = (*rtpp_set_list)->rset_first; it; it = it->rset_next) free_rtpp_nodes(it); } *rtpp_no = 0; (*list_version)++; /* notify timeout process that the rtpp proxy list changes */ if (rtpp_notify_h) { lock_get( rtpp_notify_h->lock ); rtpp_notify_h->changed = 1; } if(_add_proxies_from_database() < 0) { if (rtpp_notify_h) lock_release( rtpp_notify_h->lock ); goto error; } if (rtpp_notify_h) lock_release( rtpp_notify_h->lock ); if (update_rtpp_proxies()) goto error; /* update pointer to default_rtpp_set*/ *default_rtpp_set = select_rtpp_set(DEFAULT_RTPP_SET_ID); /* release the readers */ lock_stop_write( nh_lock ); return init_mi_tree(200, MI_OK_S, MI_OK_LEN); error: lock_stop_write( nh_lock ); return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN); } static struct mi_root* mi_show_rtpproxies(struct mi_root* cmd_tree, void* param) { struct mi_node* node, *crt_node, *set_node; struct mi_root* root; struct mi_attr * attr; struct rtpp_set * rtpp_list; struct rtpp_node * crt_rtpp; char * string, *id; int id_len, len; string = id = 0; root = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (!root) { LM_ERR("the MI tree cannot be initialized!\n"); return 0; } if(*rtpp_set_list ==NULL) return root; node = &root->node; node->flags |= MI_IS_ARRAY; for(rtpp_list = (*rtpp_set_list)->rset_first; rtpp_list != NULL; rtpp_list = rtpp_list->rset_next){ id = int2str(rtpp_list->id_set, &id_len); if(!id){ LM_ERR("cannot convert set id\n"); goto error; } if(!(set_node = add_mi_node_child(node, MI_IS_ARRAY|MI_DUP_VALUE, MI_SET, MI_SET_LEN, id, id_len))) { LM_ERR("cannot add the set node to the tree\n"); goto error; } for(crt_rtpp = rtpp_list->rn_first; crt_rtpp != NULL; crt_rtpp = crt_rtpp->rn_next){ if(!(crt_node = add_mi_node_child(set_node, MI_DUP_VALUE, MI_NODE, MI_NODE_LEN, crt_rtpp->rn_url.s, crt_rtpp->rn_url.len)) ) { LM_ERR("cannot add the child node to the tree\n"); goto error; } LM_DBG("adding node name %s \n",crt_rtpp->rn_url.s ); add_rtpp_node_int_info(crt_node, MI_INDEX, MI_INDEX_LEN, crt_rtpp->idx, attr, len,string,error); add_rtpp_node_int_info(crt_node, MI_DISABLED, MI_DISABLED_LEN, crt_rtpp->rn_disabled, attr, len,string,error); add_rtpp_node_int_info(crt_node, MI_WEIGHT, MI_WEIGHT_LEN, crt_rtpp->rn_weight, attr, len, string,error); add_rtpp_node_int_info(crt_node, MI_RECHECK_TICKS,MI_RECHECK_T_LEN, crt_rtpp->rn_recheck_ticks, attr, len, string, error); } } return root; error: if (root) free_mi_tree(root); return 0; } inline static int parse_bavp(str *s, pv_spec_t *bavp) { s->len = strlen(s->s); if (pv_parse_spec(s, bavp)==NULL) { LM_ERR("malformed bavp definition %s\n", s->s); return -1; } /* check if there is a bavp type */ if (bavp->type != (pv_type_t)(903 + PVT_EXTRA)) { LM_ERR("store parameter must be an bavp\n"); return -1; } return 0; } static int mod_init(void) { int i; float timeout; if (rtpproxy_autobridge != 0) { LM_WARN("Auto bridging does not properly function when doing " "serial/parallel forking\n"); } if (nortpproxy_str.s==NULL || nortpproxy_str.s[0]==0) { nortpproxy_str.len = 0; nortpproxy_str.s = NULL; } else { nortpproxy_str.len = strlen(nortpproxy_str.s); while (nortpproxy_str.len > 0 && (nortpproxy_str.s[nortpproxy_str.len - 1] == '\r' || nortpproxy_str.s[nortpproxy_str.len - 1] == '\n')) nortpproxy_str.len--; if (nortpproxy_str.len == 0) nortpproxy_str.s = NULL; } rtpp_no = (unsigned int*)shm_malloc(sizeof(unsigned int)); list_version = (unsigned int*)shm_malloc(sizeof(unsigned int)); *rtpp_no = 0; *list_version = 0; my_version = 0; if(!rtpp_no || !list_version) { LM_ERR("No more shared memory\n"); return -1; } if (!(rtpp_set_list = (struct rtpp_set_head **) shm_malloc(sizeof(struct rtpp_set_head *)))) { LM_ERR("no more shm mem\n"); return -1; } *rtpp_set_list = 0; if(db_url.s == NULL) { if (rtpp_sets == 0) { LM_ERR("no rtpproxy set specified\n"); return -1; } /* storing the list of rtp proxy sets in shared memory*/ for(i=0;ilock = lock_alloc(); if(!rtpp_notify_h->lock) { LM_ERR("failed to alloc timeout notify lock\n"); return -1; } if (!lock_init(rtpp_notify_h->lock)) { LM_CRIT("failed to init timeout notify lock\n"); return -1; } rtpp_notify_h->changed = 0; rtpp_notify_h->rtpp_list = NULL; if (init_rtpp_notify_list() < 0) { LM_ERR("cannot find any valid rtpproxy to use\n"); return -1; } } else { exports.procs = 0; } ei_id = evi_publish_event(event_name); if (ei_id == EVI_ERROR) LM_ERR("cannot register event\n"); return 0; } static int mi_child_init(void) { if(child_init(1) < 0) { LM_ERR("Failed to initial rtpp socks\n"); return -1; } if(!db_url.s) return 0; if (db_functions.init==0) { LM_CRIT("database not bound\n"); return -1; } db_connection = db_functions.init(&db_url); if(db_connection == NULL) { LM_ERR("Failed to connect to database"); return -1; } LM_DBG("Database connection opened successfully\n"); return 0; } static int _add_proxies_from_database(void) { /* select * from rtpproxy_sockets */ db_key_t colsToReturn[2]; db_res_t *result = NULL; int rowCount = 0; char *rtpp_socket; db_row_t *row; db_val_t *row_vals; int set_id; colsToReturn[0]=&rtpp_sock_col; colsToReturn[1]=&set_id_col; if(db_functions.use_table(db_connection, &table) < 0) { LM_ERR("Error trying to use table\n"); return -1; } if(db_functions.query(db_connection, 0, 0, 0,colsToReturn, 0, 2, 0, &result) < 0) { LM_ERR("Error querying database"); if(result) db_functions.free_result(db_connection, result); return -1; } if(result == NULL) { LM_ERR("mysql query failed - NULL result\n"); return -1; } if (RES_ROW_N(result)<=0 || RES_ROWS(result)[0].values[0].nul != 0) { LM_DBG("No proxies were found\n"); if(db_functions.free_result(db_connection, result) < 0){ LM_ERR("Error freeing result\n"); return -1; } return 0; } for(rowCount=0; rowCount < RES_ROW_N(result); rowCount++) { row= &result->rows[rowCount]; row_vals = ROW_VALUES(row); rtpp_socket = (char*)row_vals[0].val.string_val; if(rtpp_socket == NULL) { LM_ERR("NULL value for rtpproxy_socket column\n"); goto error; } set_id= row_vals[1].val.int_val; if(rtpproxy_add_rtpproxy_set(rtpp_socket, set_id) == -1) { LM_ERR("failed to add rtp proxy\n"); goto error; } } db_functions.free_result(db_connection, result); return 0; error: if(result) db_functions.free_result(db_connection, result); return -1; } static int child_init(int rank) { /* we need DB conn in the worker processes only */ if (rank<=PROC_MAIN) return 0; if(*rtpp_set_list==NULL ) return 0; mypid = getpid(); return connect_rtpproxies(); } int connect_rtpproxies(void) { int n; char *cp; struct addrinfo hints, *res; struct rtpp_set *rtpp_list; struct rtpp_node *pnode; LM_DBG("[RTPProxy] set list %p\n", *rtpp_set_list); if(!(*rtpp_set_list) ) return 0; LM_DBG("[Re]connecting sockets (%d > %d)\n", *rtpp_no, rtpp_number); if (*rtpp_no > rtpp_number) { rtpp_socks = (int*)pkg_realloc(rtpp_socks, *rtpp_no * sizeof(int) ); if (rtpp_socks==NULL) { LM_ERR("no more pkg memory\n"); return -1; } } rtpp_number = *rtpp_no; for(rtpp_list = (*rtpp_set_list)->rset_first; rtpp_list != 0; rtpp_list = rtpp_list->rset_next){ for (pnode=rtpp_list->rn_first; pnode!=0; pnode = pnode->rn_next){ char *hostname; if (pnode->rn_umode == 0) { rtpp_socks[pnode->idx] = -1; goto rptest; } /* * This is UDP or UDP6. Detect host and port; lookup host; * do connect() in order to specify peer address */ hostname = (char*)pkg_malloc(sizeof(char) * (strlen(pnode->rn_address) + 1)); if (hostname==NULL) { LM_ERR("no more pkg memory\n"); return -1; } strcpy(hostname, pnode->rn_address); cp = strrchr(hostname, ':'); if (cp != NULL) { *cp = '\0'; cp++; } if (cp == NULL || *cp == '\0') cp = CPORT; memset(&hints, 0, sizeof(hints)); hints.ai_flags = 0; hints.ai_family = (pnode->rn_umode == 6) ? AF_INET6 : AF_INET; hints.ai_socktype = SOCK_DGRAM; if ((n = getaddrinfo(hostname, cp, &hints, &res)) != 0) { LM_ERR("%s\n", gai_strerror(n)); pkg_free(hostname); return -1; } pkg_free(hostname); rtpp_socks[pnode->idx] = socket((pnode->rn_umode == 6) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); if ( rtpp_socks[pnode->idx] == -1) { LM_ERR("can't create socket\n"); freeaddrinfo(res); return -1; } if (connect( rtpp_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) { LM_ERR("can't connect to a RTP proxy\n"); close( rtpp_socks[pnode->idx] ); rtpp_socks[pnode->idx] = -1; freeaddrinfo(res); return -1; } freeaddrinfo(res); LM_DBG("connected %s\n", pnode->rn_address); rptest: pnode->rn_disabled = rtpp_test(pnode, 0, 1); } } LM_DBG("successfully updated proxy sets\n"); return 0; } int update_rtpp_proxies(void) { int i; LM_DBG("updating list from %d to %d [%d]\n", my_version, *list_version, rtpp_number); my_version = *list_version; for (i = 0; i < rtpp_number; i++) { shutdown(rtpp_socks[i], SHUT_RDWR); close(rtpp_socks[i]); } return connect_rtpproxies(); } void free_rtpp_nodes(struct rtpp_set *list) { struct rtpp_node * crt_rtpp, *last_rtpp; for(crt_rtpp = list->rn_first; crt_rtpp != NULL; ){ if(crt_rtpp->rn_url.s) shm_free(crt_rtpp->rn_url.s); last_rtpp = crt_rtpp; crt_rtpp = last_rtpp->rn_next; shm_free(last_rtpp); } list->rn_first = NULL; list->rtpp_node_count = 0; } void free_rtpp_sets(void) { struct rtpp_set * crt_list, * last_list; for(crt_list = (*rtpp_set_list)->rset_first; crt_list != NULL; ){ free_rtpp_nodes(crt_list); last_list = crt_list; crt_list = last_list->rset_next; shm_free(last_list); } (*rtpp_set_list)->rset_first = NULL; (*rtpp_set_list)->rset_last = NULL; } static void mod_destroy(void) { /*free the shared memory*/ if (default_rtpp_set) shm_free(default_rtpp_set); if(!rtpp_set_list || *rtpp_set_list == NULL) return; free_rtpp_sets(); shm_free(*rtpp_set_list); shm_free(rtpp_set_list); if(nh_lock) { lock_destroy_rw( nh_lock ); nh_lock = NULL; } if (rtpp_notify_socket_un) { if (unlink(rtpp_notify_socket.s)) { LM_ERR("cannot remove the notification socket(%s:%d)\n", strerror(errno), errno); } } } static int isnulladdr(str *sx, int pf) { char *cp; if (pf == AF_INET6) { for(cp = sx->s; cp < sx->s + sx->len; cp++) if (*cp != '0' && *cp != ':') return 0; return 1; } return (sx->len == 7 && memcmp("0.0.0.0", sx->s, 7) == 0); } #define ADD_ADIRECTION 0x01 #define FIX_MEDIP 0x02 #define ADD_ANORTPPROXY 0x04 #define FIX_ORGIP 0x08 #define ADIRECTION "a=direction:active" #define ADIRECTION_LEN (sizeof(ADIRECTION) - 1) #define AOLDMEDIP "a=oldmediaip:" #define AOLDMEDIP_LEN (sizeof(AOLDMEDIP) - 1) #define AOLDMEDIP6 "a=oldmediaip6:" #define AOLDMEDIP6_LEN (sizeof(AOLDMEDIP6) - 1) #define AOLDMEDPRT "a=oldmediaport:" #define AOLDMEDPRT_LEN (sizeof(AOLDMEDPRT) - 1) static inline int replace_sdp_ip(struct sip_msg* msg, str *org_body, char *line, str *ip) { str body1, oldip, newip; str body = *org_body; unsigned hasreplaced = 0; int pf, pf1 = 0; str body2; char *bodylimit = body.s + body.len; /* Iterate all lines and replace ips in them. */ if (!ip) { newip.s = ip_addr2a(&msg->rcv.src_ip); newip.len = strlen(newip.s); } else { newip = *ip; } body1 = body; for(;;) { if (extract_mediaip(&body1, &oldip, &pf,line) == -1) break; if (pf != AF_INET) { LM_ERR("not an IPv4 address in '%s' SDP\n",line); return -1; } if (!pf1) pf1 = pf; else if (pf != pf1) { LM_ERR("mismatching address families in '%s' SDP\n",line); return -1; } body2.s = oldip.s + oldip.len; body2.len = bodylimit - body2.s; if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf,1) == -1) { LM_ERR("can't alter '%s' IP\n",line); return -1; } hasreplaced = 1; body1 = body2; } if (!hasreplaced) { LM_ERR("can't extract '%s' IP from the SDP\n",line); return -1; } return 0; } static int extract_mediainfo(str *body, str *mediaport, str *payload_types) { char *cp, *cp1; int len, i; str ptype; cp1 = NULL; for (cp = body->s; (len = body->s + body->len - cp) > 0;) { cp1 = ser_memmem(cp, "m=", len, 2); if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r') break; cp = cp1 + 2; } if (cp1 == NULL) { LM_ERR("no `m=' in SDP\n"); return -1; } mediaport->s = cp1 + 2; /* skip `m=' */ mediaport->len = eat_line(mediaport->s, body->s + body->len - mediaport->s) - mediaport->s; trim_len(mediaport->len, mediaport->s, *mediaport); /* Skip media supertype and spaces after it */ cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len); mediaport->len -= cp - mediaport->s; if (mediaport->len <= 0 || cp == mediaport->s) { LM_ERR("no port in `m='\n"); return -1; } mediaport->s = cp; cp = eat_space_end(mediaport->s, mediaport->s + mediaport->len); mediaport->len -= cp - mediaport->s; if (mediaport->len <= 0 || cp == mediaport->s) { LM_ERR("no port in `m='\n"); return -1; } /* Extract port */ mediaport->s = cp; cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len); ptype.len = mediaport->len - (cp - mediaport->s); if (ptype.len <= 0 || cp == mediaport->s) { LM_ERR("no port in `m='\n"); return -1; } ptype.s = cp; mediaport->len = cp - mediaport->s; /* Skip spaces after port */ cp = eat_space_end(ptype.s, ptype.s + ptype.len); ptype.len -= cp - ptype.s; if (ptype.len <= 0 || cp == ptype.s) { LM_ERR("no protocol type in `m='\n"); return -1; } /* Extract protocol type */ ptype.s = cp; cp = eat_token_end(ptype.s, ptype.s + ptype.len); if (cp == ptype.s) { LM_ERR("no protocol type in `m='\n"); return -1; } payload_types->len = ptype.len - (cp - ptype.s); ptype.len = cp - ptype.s; payload_types->s = cp; for (i = 0; sup_ptypes[i].s != NULL; i++) { if (ptype.len != sup_ptypes[i].len || strncasecmp(ptype.s, sup_ptypes[i].s, ptype.len) != 0) continue; if (sup_ptypes[i].is_rtp == 0) { payload_types->len = 0; return 0; } cp = eat_space_end(payload_types->s, payload_types->s + payload_types->len); if (cp == payload_types->s) { LM_ERR("no payload types in `m='\n"); return -1; } payload_types->len -= cp - payload_types->s; payload_types->s = cp; return 0; } /* Unproxyable protocol type. Generally it isn't error. */ return -1; } static int alter_rtcp(struct sip_msg *msg,str * body1, str *newip, int newpf ,str* newport, char * line_start ) { static const str field = str_init("a=rtcp:"); str buff = {0,0} ; str type; int offset; struct lump* anchor; str body, value; body.s = line_start; body.len = body1->s + body1->len - line_start; if( extract_field( &body, &value, field) < 0 ) { LM_ERR("Unable to extract rtcp body\n"); return -1; } if( newpf == AF_INET6 ) type.s = " IN IP6 "; else type.s = " IN IP4 "; type.len = strlen(type.s); buff.len += newport->len + type.len + newip->len ; buff.s = pkg_malloc( buff.len + 1 ); if( buff.s == 0 ) { LM_ERR("Not enough memory\n"); return -1; } sprintf( buff.s, "%.*s%.*s%.*s", newport->len, newport->s, type.len, type.s, newip->len, newip->s ); offset = value.s - msg->buf; anchor = del_lump(msg, offset, value.len, 0); if (anchor == NULL) { LM_ERR("del_lump failed\n"); pkg_free(buff.s); return -1; } if (insert_new_lump_after(anchor, buff.s, buff.len, 0) == 0) { LM_ERR("insert_new_lump_after failed\n"); pkg_free(buff.s); return -1; } return 0; } static int alter_mediaip(struct sip_msg *msg, str *body, str *oldip, int oldpf, str *newip, int newpf, int preserve) { char *buf; int offset; struct lump* anchor; str omip, nip, oip; /* check that updating media-ip is really necessary */ if (oldpf == newpf && isnulladdr(oldip, oldpf)) return 0; if (newip->len == oldip->len && memcmp(newip->s, oldip->s, newip->len) == 0) return 0; if (preserve != 0) { anchor = anchor_lump(msg, body->s + body->len - msg->buf, 0); if (anchor == NULL) { LM_ERR("anchor_lump failed\n"); return -1; } if (oldpf == AF_INET6) { omip.s = AOLDMEDIP6; omip.len = AOLDMEDIP6_LEN; } else { omip.s = AOLDMEDIP; omip.len = AOLDMEDIP_LEN; } buf = pkg_malloc(omip.len + oldip->len + CRLF_LEN); if (buf == NULL) { LM_ERR("out of pkg memory\n"); return -1; } memcpy(buf, CRLF, CRLF_LEN); memcpy(buf + CRLF_LEN, omip.s, omip.len); memcpy(buf + CRLF_LEN + omip.len, oldip->s, oldip->len); if (insert_new_lump_after(anchor, buf, omip.len + oldip->len + CRLF_LEN, 0) == NULL) { LM_ERR("insert_new_lump_after failed\n"); pkg_free(buf); return -1; } } if (oldpf == newpf) { nip.len = newip->len; nip.s = pkg_malloc(nip.len); if (nip.s == NULL) { LM_ERR("out of pkg memory\n"); return -1; } memcpy(nip.s, newip->s, newip->len); } else { nip.len = newip->len + 2; nip.s = pkg_malloc(nip.len); if (nip.s == NULL) { LM_ERR("out of pkg memory\n"); return -1; } memcpy(nip.s + 2, newip->s, newip->len); nip.s[0] = (newpf == AF_INET6) ? '6' : '4'; nip.s[1] = ' '; } oip = *oldip; if (oldpf != newpf) { do { oip.s--; oip.len++; } while (*oip.s != '6' && *oip.s != '4'); } offset = oip.s - msg->buf; anchor = del_lump(msg, offset, oip.len, 0); if (anchor == NULL) { LM_ERR("del_lump failed\n"); pkg_free(nip.s); return -1; } if (insert_new_lump_after(anchor, nip.s, nip.len, 0) == 0) { LM_ERR("insert_new_lump_after failed\n"); pkg_free(nip.s); return -1; } return 0; } static int alter_mediaport(struct sip_msg *msg, str *body, str *oldport, str *newport, int preserve) { char *buf; int offset; struct lump* anchor; /* check that updating media-port is really necessary */ if (newport->len == oldport->len && memcmp(newport->s, oldport->s, newport->len) == 0) return 0; /* * Since rewriting the same info twice will mess SDP up, * apply simple anti foot shooting measure - put flag on * messages that have been altered and check it when * another request comes. */ #if 0 /* disabled: - it propagates to the reply and we don't want this * -- andrei */ if (msg->msg_flags & FL_SDP_PORT_AFS) { LM_ERR("you can't rewrite the same SDP twice, check your config!\n"); return -1; } #endif if (preserve != 0) { anchor = anchor_lump(msg, body->s + body->len - msg->buf, 0); if (anchor == NULL) { LM_ERR("anchor_lump failed\n"); return -1; } buf = pkg_malloc(AOLDMEDPRT_LEN + oldport->len + CRLF_LEN); if (buf == NULL) { LM_ERR("out of pkg memory\n"); return -1; } memcpy(buf, CRLF, CRLF_LEN); memcpy(buf + CRLF_LEN, AOLDMEDPRT, AOLDMEDPRT_LEN); memcpy(buf + CRLF_LEN + AOLDMEDPRT_LEN, oldport->s, oldport->len); if (insert_new_lump_after(anchor, buf, AOLDMEDPRT_LEN + oldport->len + CRLF_LEN, 0) == NULL) { LM_ERR("insert_new_lump_after failed\n"); pkg_free(buf); return -1; } } buf = pkg_malloc(newport->len); if (buf == NULL) { LM_ERR("out of pkg memory\n"); return -1; } offset = oldport->s - msg->buf; anchor = del_lump(msg, offset, oldport->len, 0); if (anchor == NULL) { LM_ERR("del_lump failed\n"); pkg_free(buf); return -1; } memcpy(buf, newport->s, newport->len); if (insert_new_lump_after(anchor, buf, newport->len, 0) == 0) { LM_ERR("insert_new_lump_after failed\n"); pkg_free(buf); return -1; } #if 0 msg->msg_flags |= FL_SDP_PORT_AFS; #endif return 0; } static char * gencookie(void) { static char cook[34]; sprintf(cook, "%d_%u ", (int)mypid, myseqn); myseqn++; return cook; } static int rtpp_checkcap(struct rtpp_node *node, char *cap, int caplen) { char *cp; struct iovec vf[4] = {{NULL, 0}, {"VF", 2}, {" ", 1}, {NULL, 0}}; vf[3].iov_base = cap; vf[3].iov_len = caplen; cp = send_rtpp_command(node, vf, 4); if (cp == NULL) return -1; if (cp[0] == 'E' || atoi(cp) != 1) return 0; return 1; } static inline void raise_rtpproxy_event(struct rtpp_node *node, int status) { evi_params_p list = NULL; if (ei_id == EVI_ERROR) { LM_ERR("event not registered %d\n", ei_id); return; } if (evi_probe_event(ei_id)) { if (!(list = evi_get_params())) return; if (evi_param_add_str(list, &socket_name, &node->rn_url)) { LM_ERR("unable to add socket parameter\n"); goto free; } if (evi_param_add_str(list, &status_name, status ? &status_connected : &status_disconnected)) { LM_ERR("unable to add status parameter\n"); goto free; } if (evi_raise_event(ei_id, list)) { LM_ERR("unable to send event\n"); } } else { LM_DBG("no event sent\n"); } return; free: evi_free_params(list); } static int rtpp_test(struct rtpp_node *node, int isdisabled, int force) { int rtpp_ver, rval; char *cp; struct iovec v[2] = {{NULL, 0}, {"V", 1}}; if(node->rn_recheck_ticks == MI_MAX_RECHECK_TICKS){ LM_DBG("rtpp %s disabled for ever\n", node->rn_url.s); return 1; } if (force == 0) { if (isdisabled == 0) return 0; if (node->rn_recheck_ticks > get_ticks()) return 1; } cp = send_rtpp_command(node, v, 2); if (cp == NULL) { LM_WARN("can't get version of the RTP proxy\n"); goto error; } rtpp_ver = atoi(cp); if (rtpp_ver != SUP_CPROTOVER) { LM_WARN("unsupported version of RTP proxy <%s> found: %d supported," "%d present\n", node->rn_url.s, SUP_CPROTOVER, rtpp_ver); goto error; } rval = rtpp_checkcap(node, REQ_CPROTOVER, sizeof(REQ_CPROTOVER) - 1); if (rval == -1) { LM_WARN("RTP proxy went down during version query\n"); goto error; } if (rval == 0) { LM_WARN("of RTP proxy <%s> doesn't support required protocol version" "%s\n", node->rn_url.s, REQ_CPROTOVER); goto error; } LM_INFO("rtp proxy <%s> found, support for it %senabled\n", node->rn_url.s, force == 0 ? "re-" : ""); /* Check for optional capabilities */ rval = rtpp_checkcap(node, REP_CPROTOVER, sizeof(REP_CPROTOVER) - 1); if (rval != -1) { node->rn_rep_supported = rval; } else { node->rn_rep_supported = 0; } rval = rtpp_checkcap(node, PTL_CPROTOVER, sizeof(PTL_CPROTOVER) - 1); if (rval != -1) { node->rn_ptl_supported = rval; } else { node->rn_ptl_supported = 0; } rval = rtpp_checkcap(node, ABR_CPROTOVER, sizeof(ABR_CPROTOVER) - 1); if (rval != -1) { node->abr_supported = rval; } else { node->abr_supported = 0; } raise_rtpproxy_event(node, 1); return 0; error: LM_WARN("support for RTP proxy <%s> has been disabled%s\n", node->rn_url.s, rtpproxy_disable_tout < 0 ? "" : " temporarily"); if (rtpproxy_disable_tout >= 0) node->rn_recheck_ticks = get_ticks() + rtpproxy_disable_tout; if (cp) raise_rtpproxy_event(node, 0); return 1; } #define RTPPROXY_BUF_SIZE 256 char * send_rtpp_command(struct rtpp_node *node, struct iovec *v, int vcnt) { struct sockaddr_un addr; int fd, len, i; char *cp; static char buf[RTPPROXY_BUF_SIZE]; struct pollfd fds[1]; #ifdef IOV_MAX /* normalize vcntl to IOV_MAX, as on some systems this limit is very low (16 on Solaris) */ if (vcnt > IOV_MAX) { int i, vec_len = 0; /* use buf if possible :) */ for (i = IOV_MAX - 1; i < vcnt; i++) vec_len += v[i].iov_len; /* use buf, error otherwise */ if (vec_len > RTPPROXY_BUF_SIZE) { LM_ERR("Command too big %d - max %d\n", vec_len, RTPPROXY_BUF_SIZE); return NULL; } cp = buf; for (i = IOV_MAX - 1; i < vcnt; i++) { memcpy(cp, v[i].iov_base, v[i].iov_len); cp += v[i].iov_len; } i = IOV_MAX - 1; v[i].iov_len = vec_len; v[i].iov_base = buf; /* finally solve the problem */ vcnt = IOV_MAX; } #endif len = 0; cp = buf; if (node->rn_umode == 0) { memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; strncpy(addr.sun_path, node->rn_address, sizeof(addr.sun_path) - 1); #ifdef HAVE_SOCKADDR_SA_LEN addr.sun_len = strlen(addr.sun_path); #endif fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd < 0) { LM_ERR("can't create socket\n"); goto badproxy; } if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(fd); LM_ERR("can't connect to RTP proxy\n"); goto badproxy; } do { len = writev(fd, v + 1, vcnt - 1); } while (len == -1 && errno == EINTR); if (len <= 0) { close(fd); LM_ERR("can't send command to a RTP proxy\n"); goto badproxy; } do { len = read(fd, buf, sizeof(buf) - 1); } while (len == -1 && errno == EINTR); close(fd); if (len <= 0) { LM_ERR("can't read reply from a RTP proxy\n"); goto badproxy; } } else { fds[0].fd = rtpp_socks[node->idx]; fds[0].events = POLLIN; fds[0].revents = 0; /* Drain input buffer */ while ((poll(fds, 1, 0) == 1) && ((fds[0].revents & POLLIN) != 0)) { recv(rtpp_socks[node->idx], buf, sizeof(buf) - 1, 0); fds[0].revents = 0; } v[0].iov_base = gencookie(); v[0].iov_len = strlen(v[0].iov_base); for (i = 0; i < rtpproxy_retr; i++) { do { len = writev(rtpp_socks[node->idx], v, vcnt); } while (len == -1 && (errno == EINTR || errno == ENOBUFS)); if (len <= 0) { LM_ERR("can't send command to a RTP proxy %s\n", strerror(errno)); goto badproxy; } while ((poll(fds, 1, rtpproxy_tout) == 1) && (fds[0].revents & POLLIN) != 0) { do { len = recv(rtpp_socks[node->idx], buf, sizeof(buf)-1, 0); } while (len == -1 && errno == EINTR); if (len <= 0) { LM_ERR("can't read reply from a RTP proxy\n"); goto badproxy; } if (len >= (v[0].iov_len - 1) && memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) { len -= (v[0].iov_len - 1); cp += (v[0].iov_len - 1); if (len != 0) { len--; cp++; } goto out; } fds[0].revents = 0; } } if (i == rtpproxy_retr) { LM_ERR("timeout waiting reply from a RTP proxy\n"); goto badproxy; } } out: cp[len] = '\0'; return cp; badproxy: LM_ERR("proxy <%s> does not respond, disable it\n", node->rn_url.s); node->rn_disabled = 1; node->rn_recheck_ticks = get_ticks() + rtpproxy_disable_tout; raise_rtpproxy_event(node, 0); return NULL; } /* * select the set with the id_set id */ static struct rtpp_set * select_rtpp_set(int id_set){ struct rtpp_set * rtpp_list; /*is it a valid set_id?*/ LM_DBG("Looking for set_id %d\n", id_set); if(!(*rtpp_set_list) || !(*rtpp_set_list)->rset_first){ LM_ERR("no rtp_proxy configured\n"); return 0; } for(rtpp_list=(*rtpp_set_list)->rset_first; rtpp_list!=0 && rtpp_list->id_set!=id_set; rtpp_list=rtpp_list->rset_next); if(!rtpp_list){ LM_ERR(" script error-invalid id_set to be selected\n"); } return rtpp_list; } /* * Main balancing routine. This does not try to keep the same proxy for * the call if some proxies were disabled or enabled; proxy death considered * too rare. Otherwise we should implement "mature" HA clustering, which is * too expensive here. */ struct rtpp_node * select_rtpp_node(struct sip_msg * msg, str callid, struct rtpp_set *set, pv_spec_p spec, int do_test) { unsigned sum, weight_sum; struct rtpp_node* node; int was_forced, sumcut, found, constant_weight_sum; pv_value_t val; /* check last list version */ if (my_version != *list_version && update_rtpp_proxies() < 0) { LM_ERR("cannot update rtpp proxies list\n"); return 0; } if (!set) { LM_ERR("no set specified\n"); return 0; } /* Most popular case: 1 proxy, nothing to calculate */ if (set->rtpp_node_count == 1) { node = set->rn_first; if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()) node->rn_disabled = rtpp_test(node, 1, 0); if (node->rn_disabled) return NULL; goto done; } /* XXX Use quick-and-dirty hashing algo */ for(sum = 0; callid.len > 0; callid.len--) sum += callid.s[callid.len - 1]; sum &= 0xff; was_forced = 0; retry: weight_sum = 0; constant_weight_sum = 0; found = 0; for (node=set->rn_first; node!=NULL; node=node->rn_next) { if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()){ /* Try to enable if it's time to try. */ node->rn_disabled = rtpp_test(node, 1, 0); } constant_weight_sum += node->rn_weight; if (!node->rn_disabled) { weight_sum += node->rn_weight; found = 1; } } if (found == 0) { /* No proxies? Force all to be re-detected, if not yet */ if (was_forced) return NULL; was_forced = 1; for(node=set->rn_first; node!=NULL; node=node->rn_next) { node->rn_disabled = rtpp_test(node, 1, 1); } goto retry; } sumcut = weight_sum ? sum % constant_weight_sum : -1; /* * sumcut here lays from 0 to constant_weight_sum-1. * Scan proxy list and decrease until appropriate proxy is found. */ was_forced = 0; for (node=set->rn_first; node!=NULL;) { if (sumcut < (int)node->rn_weight) { if (!node->rn_disabled) goto found; if (was_forced == 0) { /* appropriate proxy is disabled : redistribute on enabled ones */ sumcut = weight_sum ? sum % weight_sum : -1; node = set->rn_first; was_forced = 1; continue; } } sumcut -= node->rn_weight; node = node->rn_next; } /* No node list */ return NULL; found: if (do_test) { node->rn_disabled = rtpp_test(node, node->rn_disabled, 0); if (node->rn_disabled) goto retry; } done: /* Store rtpproxy used */ if (spec) { memset(&val, 0, sizeof(pv_value_t)); val.flags = PV_VAL_STR; val.rs = node->rn_url; if(pv_set_value(msg, spec, (int)EQ_T, &val)<0) LM_ERR("setting PV failed\n"); } return node; } static int unforce_rtp_proxy_f(struct sip_msg* msg, char* pset, char *var) { str callid, from_tag, to_tag; if (!msg || msg == FAKED_REPLY) return 1; if (get_callid(msg, &callid) == -1 || callid.len == 0) { LM_ERR("can't get Call-Id field\n"); return -1; } to_tag.s = 0; if (get_to_tag(msg, &to_tag) == -1) { LM_ERR("can't get To tag\n"); return -1; } if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { LM_ERR("can't get From tag\n"); return -1; } return unforce_rtpproxy(msg, callid, from_tag, to_tag, pset, var); } static int unforce_rtpproxy(struct sip_msg* msg, str callid, str from_tag, str to_tag, char *pset, char *var) { struct rtpp_node *node; struct rtpp_set *set; struct iovec v[1 + 4 + 3] = {{NULL, 0}, {"D", 1}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}}; /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 1 */ STR2IOVEC(callid, v[3]); STR2IOVEC(from_tag, v[5]); STR2IOVEC(to_tag, v[7]); if (nh_lock) { lock_start_read( nh_lock ); } set = get_rtpp_set(msg, (nh_set_param_t *)pset); if (!set) { LM_ERR("could not find rtpproxy set\n"); goto error; } node = select_rtpp_node(msg, callid, set, (pv_spec_p)var, 1); if (!node) { LM_ERR("no available proxies\n"); goto error; } send_rtpp_command(node, v, (to_tag.len > 0) ? 8 : 6); LM_DBG("sent unforce command\n"); if(nh_lock) { /* we are done reading -> unref the data */ lock_stop_read( nh_lock ); } return 1; error: if(!nh_lock) return -1; /* we are done reading -> unref the data */ lock_stop_read( nh_lock ); return -1; } struct rtpp_set * get_rtpp_set(struct sip_msg * msg, nh_set_param_t *pset) { pv_value_t value; int int_val; int err; struct rtpp_set *set; if (!pset) return *default_rtpp_set; if (pset->t == NH_VAL_SET_FIXED) return pset->v.fixed_set; if (pset->t == NH_VAL_SET_SPEC) { if ( pv_get_spec_value(msg,&pset->v.var_set,&value)!=0 || value.flags & PV_VAL_NULL || value.flags&PV_VAL_EMPTY ) { LM_ERR("no PV or NULL value specified for proxy set " "(error in scripts)\n"); return NULL; } if ( value.flags & PV_VAL_STR ) { int_val = str2s(value.rs.s, value.rs.len, &err); if (err != 0) { LM_ERR("Invalid value %s specified in PV as RTP proxy set.\n", value.rs.s ); return NULL; } } else if ( value.flags & PV_VAL_INT ) { int_val = value.ri; } else { LM_ERR("Unsupported PV value type for RTP proxy set.i\n"); return NULL; } LM_DBG("Variable proxy set %d specified.\n", int_val); return select_rtpp_set(int_val); } else { int_val = pset->v.int_set; LM_DBG("Checking proxy set %d\n", int_val); set = select_rtpp_set(int_val); if (set) { LM_DBG("Updating proxy set %d\n", int_val); pset->v.fixed_set = set; pset->t = NH_VAL_SET_FIXED; } return set; } } static char * pkg_strdup(char *cp) { char *rval; int len; len = strlen(cp) + 1; rval = pkg_malloc(len); if (rval == NULL) return (NULL); memcpy(rval, cp, len); return (rval); } static int rtpp_get_var_svalue(struct sip_msg *msg, gparam_p gp, str *val, int n) { #define MAX_BUF 64 static char buf[2][MAX_BUF]; str tmp; if (gp->type==GPARAM_TYPE_STR) { *val = gp->v.sval; return 0; } if ( fixup_get_svalue(msg, gp, &tmp)!=0 ) return -1; val->s = buf[n]; val->len = (tmp.len>MAX_BUF-1) ? MAX_BUF-1 : tmp.len ; memcpy(val->s,tmp.s, val->len); val->s[val->len] = 0; return 0; } static int rtpproxy_offer4_f(struct sip_msg *msg, char *param1, char *param2, char *param3, char *param4) { str aux_str; if(rtpp_notify_socket.s) { if ( (!msg->to && parse_headers(msg, HDR_TO_F,0)<0) || !msg->to ) { LM_ERR("bad request or missing TO hdr\n"); return -1; } /* if an initial request - create a new dialog */ if(get_to(msg)->tag_value.s == NULL) dlg_api.create_dlg(msg,0); } if (param1) { if (rtpp_get_var_svalue(msg, (gparam_p)param1, &aux_str, 0)<0) { LM_ERR("bogus flags parameter\n"); return -1; } param1 = aux_str.s; } if (param2) { if (rtpp_get_var_svalue(msg, (gparam_p)param2, &aux_str, 1)<0) { LM_ERR("bogus IP addr parameter\n"); return -1; } param2 = aux_str.s; } return force_rtp_proxy(msg, param1, param2, param3, param4, 1); } static int rtpproxy_answer4_f(struct sip_msg *msg, char *param1, char *param2, char *param3, char *param4) { str aux_str; if (msg->first_line.type == SIP_REQUEST) if (msg->first_line.u.request.method_value != METHOD_ACK) return -1; if (param1) { if (rtpp_get_var_svalue(msg, (gparam_p)param1, &aux_str, 0)<0) { LM_ERR("bogus flags parameter\n"); return -1; } param1 = aux_str.s; } if (param2) { if (rtpp_get_var_svalue(msg, (gparam_p)param2, &aux_str, 1)<0) { LM_ERR("bogus IP addr parameter\n"); return -1; } param2 = aux_str.s; } return force_rtp_proxy(msg, param1, param2, param3, param4, 0); } static void engage_callback(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { if (!dlg || !_params) return; /* engage */ engage_force_rtpproxy(dlg, _params->msg); } static void engage_close_callback(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { str value; static nh_set_param_t param; if (!dlg || !_params) return; LM_DBG("engage close called\n"); if (dlg_api.fetch_dlg_value(dlg, ¶m3_name, &value, 0) < 0) { LM_DBG("third param not found\n"); param.v.int_set = DEFAULT_RTPP_SET_ID; } else { param.v.int_set = *(int *)(value.s); } param.t = NH_VAL_SET_UNDEF; if (unforce_rtpproxy(_params->msg, dlg->callid, dlg->legs[DLG_CALLER_LEG].tag, dlg->legs[callee_idx(dlg)].tag, (char *)¶m, NULL) < 0) { LM_ERR("cannot unforce rtp proxy\n"); } } /* moves parameters from branch avps to dialog values * returns the values moved */ static int move_bavp2dlg(struct sip_msg *msg, struct dlg_cell *dlg, str *rval1, str *rval2, int *setid) { unsigned int code = 0; unsigned int flags_found = 0; unsigned int ip_found = 0; unsigned int set_found = 0; pv_value_t val1, val2, val3; str param3_val; if (!msg || !dlg || msg->first_line.type != SIP_REPLY) goto not_moved; /* check to see if there are avps stored */ if (pv_get_spec_value(msg, ¶m1_spec, &val1) < 0 || (val1.flags & PV_VAL_NULL)) LM_DBG("flags bavp not found!\n"); else flags_found = 1; if (pv_get_spec_value(msg, ¶m2_spec, &val2) < 0 || (val2.flags & PV_VAL_NULL)) LM_DBG("ip bavp not found!\n"); else ip_found = 1; if (pv_get_spec_value(msg, ¶m3_spec, &val3) < 0 || (val3.flags & PV_VAL_NULL)) LM_DBG("set bavp not found!\n"); else set_found = 1; if ((flags_found|ip_found|set_found) == 0) goto not_moved; code = msg->first_line.u.reply.statuscode; /* only move branch avps if a final response has come */ if (code >= 200 && code < 300) { if (flags_found) { if (dlg_api.store_dlg_value(dlg, ¶m1_name, &val1.rs) < 0) { LM_ERR("cannot store value\n"); goto error; } } else { val1.rs.len = 0; val1.rs.s = 0; } if (rval1) { rval1->len = val1.rs.len; rval1->s = val1.rs.s; } if (ip_found) { if (dlg_api.store_dlg_value(dlg, ¶m2_name, &val2.rs) < 0) { LM_ERR("cannot store value\n"); goto error; } } else { val2.rs.len = 0; val2.rs.s = 0; } if (rval2) { rval2->len = val2.rs.len; rval2->s = val2.rs.s; } if (set_found) { /* Store Set ID INT value correcty in dlg */ param3_val.s = (char*)&val3.ri; param3_val.len = sizeof(unsigned int); if (dlg_api.store_dlg_value(dlg, ¶m3_name, ¶m3_val) < 0) { LM_ERR("cannot store setid value\n"); goto error; } } else { val3.ri = DEFAULT_RTPP_SET_ID; } if (setid) *setid = val3.ri; LM_DBG("moved <%s> and <%s> from branch avp list in dlg\n", param1_name.s,param2_name.s); return 1; } else if (code < 200) { if (!flags_found) { val1.rs.len = 0; val1.rs.s = 0; } if (rval1) { rval1->len = val1.rs.len; rval1->s = val1.rs.s; } if (!ip_found) { val2.rs.len = 0; val2.rs.s = 0; } if (rval2) { rval2->len = val2.rs.len; rval2->s = val2.rs.s; } if (!set_found) val3.ri = DEFAULT_RTPP_SET_ID; if (setid) *setid = val3.ri; return 1; } not_moved: LM_DBG("nothing moved - message type %d\n", !msg ? -1 : msg->first_line.type); if (rval1) rval1->len = 0; if (rval2) rval2->len = 0; if (setid) *setid = DEFAULT_RTPP_SET_ID; return 0; error: return -1; } static int engage_force_rtpproxy(struct dlg_cell *dlg, struct sip_msg *msg) { int offer = 1; int setid; str param1_val,param2_val,value; int method_id, has_sdp, alloc = 0; int moved; static nh_set_param_t param = { .t = NH_VAL_SET_UNDEF }; LM_DBG("engage callback called\n"); if (!msg) goto done; if (dlg_api.get_dlg && !dlg) { dlg = dlg_api.get_dlg(); if (!dlg) { LM_DBG("dialog not found - cannot engage rtpproxy\n"); goto done; } } if (!dlg) { LM_ERR("null dialog\n"); goto error; } /* parse cseq header */ if(parse_headers(msg,HDR_CSEQ_F,0) < 0) { LM_ERR("cannot parse cseq header\n"); goto error; } if(msg->cseq==NULL || msg->cseq->body.s==NULL) { LM_ERR("cseq header empty\n"); goto error; } /* check to see if this is a late negotiation */ if (dlg_api.fetch_dlg_value(dlg, &late_name, &value, 0) < 0) offer = 0; has_sdp = msg_has_sdp(msg); method_id = get_cseq(msg)->method_id; LM_DBG("method id is %d SDP: %d\n", method_id, has_sdp); if (method_id == METHOD_ACK) { /* normal negotiation - ACK cannot have SDP */ if (!offer && has_sdp) { LM_ERR("not a late negotiation - ACK cannot have SDP body\n"); goto error; } /* late negotiation without SDP */ if (offer && !has_sdp) { LM_ERR("ACK of a late negotiation that doesn't have SDP body\n"); goto error; } /* valid normal negotiation */ if (!offer && !has_sdp) goto done; /* late negotiation */ } else { /* sequential request without SDP */ if (!has_sdp) { goto done; } /* if it is not an 200OK */ LM_DBG("handling 200 OK? - %d\n", msg->first_line.u.reply.statuscode); } /* try to move values */ if ((moved = move_bavp2dlg(msg, dlg, ¶m1_val, ¶m2_val, &setid)) < 0) { LM_ERR("error while moving branch avps\n"); goto error; } /* don't have them, try to get them from the dialog */ if (moved == 0) { /* nothing moved - the values should be in dialog already */ if (dlg_api.fetch_dlg_value(dlg, ¶m1_name, &value, 0) >= 0) { param1_val.s = pkg_malloc(value.len + 1); if (!param1_val.s) { LM_ERR("no more pkg mem\n"); goto error; } alloc = 1; memcpy(param1_val.s, value.s, value.len); param1_val.s[value.len] = '\0'; param1_val.len = value.len; } else { LM_DBG("flags param not found\n"); param1_val.s = NULL; } if (dlg_api.fetch_dlg_value(dlg, ¶m2_name, &value, 0) >= 0) { param2_val.s = pkg_malloc(value.len + 1); if (!param2_val.s) { LM_ERR("no more pkg mem\n"); goto error; } alloc = 1; memcpy(param2_val.s, value.s, value.len); param2_val.s[value.len] = '\0'; param2_val.len = value.len; } else { LM_DBG("ip param not found\n"); param2_val.s = NULL; } if (dlg_api.fetch_dlg_value(dlg, ¶m3_name, &value, 0) < 0) { LM_DBG("third param not found\n"); setid = DEFAULT_RTPP_SET_ID; } else { setid = *(int *)(value.s); } LM_DBG("fetched: param1 <%s> param2 <%s> set <%d> - offer? %s\n", param1_val.s ? param1_val.s : "none", param2_val.s ? param2_val.s : "none", setid, offer? "yes":"no"); } param.v.int_set = setid; param.t = NH_VAL_SET_UNDEF; force_rtp_proxy(msg, param1_val.s, param2_val.s, (char *)¶m, NULL, offer); if (alloc) { if (param1_val.s) pkg_free(param1_val.s); if (param2_val.s) pkg_free(param2_val.s); } done: return 0; error: return -1; } void engage_tm_reply_callback(struct cell* t, int type, struct tmcb_params *p) { if (!t || !p) return; /* engage */ engage_force_rtpproxy(NULL, p->rpl); } int msg_has_sdp(struct sip_msg *msg) { str body; struct part *p; struct multi_body *m; if(parse_headers(msg, HDR_CONTENTLENGTH_F,0) < 0) { LM_ERR("cannot parse cseq header"); return 0; } body.len = get_content_length(msg); if (!body.len) return 0; m = get_all_bodies(msg); if (!m) { LM_DBG("cannot parse body\n"); return 0; } for (p = m->first; p; p = p->next) { if (p->content_type == ((TYPE_APPLICATION << 16) + SUBTYPE_SDP)) return 1; } return 0; } static int engage_rtp_proxy4_f(struct sip_msg *msg, char *param1, char *param2, char *param3, char *param4) { str param1_val,param2_val; struct to_body *pto; struct dlg_cell *dlg; struct rtpp_set *set = NULL; pv_value_t val1, val2; str aux_str; LM_DBG("engage called from script 1:%p 2:%p 3:%p 4:%p\n", param1, param2, param3, param4); if (!(msg->first_line.type == SIP_REQUEST && msg->first_line.u.request.method_value == METHOD_INVITE)) { LM_ERR("this function can only be called from invite\n"); return -1; } if ( (!msg->to && parse_headers(msg, HDR_TO_F, 0)<0) || !msg->to ) { LM_ERR("bad request or missing TO hdr\n"); return -1; } pto = get_to(msg); if (pto == NULL || pto->error != PARSE_OK) { LM_ERR("failed to parse TO header\n"); return -1; } /* to-tag field is empty*/ if (!( pto->tag_value.s==NULL || pto->tag_value.len==0) ) { LM_ERR("function can only be called from the initial invite"); return -1; } /* create dialog */ if (dlg_api.create_dlg(msg,0) < 0) { LM_ERR("error creating dialog"); return -1; } dlg = dlg_api.get_dlg(); if (!dlg) { LM_ERR("cannot get dialog\n"); return -1; } if (param1) { if (rtpp_get_var_svalue(msg, (gparam_p)param1, &aux_str, 0)<0) { LM_ERR("bogus flags parameter\n"); return -1; } param1 = aux_str.s; } if (param2) { if (rtpp_get_var_svalue(msg, (gparam_p)param2, &aux_str, 1)<0) { LM_ERR("bogus IP addr parameter\n"); return -1; } param2 = aux_str.s; } /* is this a late negotiation scenario? */ if (msg_has_sdp(msg)) { LM_DBG("message has sdp body -> forcing rtp proxy\n"); if(force_rtp_proxy(msg,param1,param2,param3,param4,1) < 0) { LM_ERR("error forcing rtp proxy"); return -1; } } else { if (dlg_api.store_dlg_value(dlg, &late_name, &late_name) < 0) { LM_ERR("cannot store late_negotiation param into dialog\n"); return -1; } } if (param1) { param1_val.s = param1; param1_val.len = strlen(param1)+1; } if (param2) { param2_val.s = param2; param2_val.len = strlen(param2)+1; } if (param3) { /* get the set-id */ set = get_rtpp_set(msg, (nh_set_param_t *)param3); if (!set) { LM_CRIT("set no longer here - forcing the default one!\n"); set = *default_rtpp_set; } } else { set = *default_rtpp_set; } if (route_type & BRANCH_ROUTE) { /* store the value into branch avps */ if (param1) { val1.flags = AVP_VAL_STR; val1.rs = param1_val; if (pv_set_value(msg, ¶m1_spec, EQ_T, &val1) < 0) { LM_ERR("cannot store <%.*s> param", param1_name.len, param1_name.s); return -1; } if (!val1.rs.len) { LM_ERR("cannot store flags parameter in branch avp\n"); return -1; } } if (param2) { val2.flags = AVP_VAL_STR; val2.rs = param2_val; if (pv_set_value(msg, ¶m2_spec, EQ_T, &val2) < 0) { LM_ERR("cannot store <%.*s> param", param2_name.len, param2_name.s); return -1; } } if (param3) { val2.flags = PV_TYPE_INT; val2.ri = set->id_set; if (pv_set_value(msg, ¶m3_spec, EQ_T, &val2) < 0) { LM_ERR("cannot store set param"); return -1; } } LM_DBG("stored values in bavp\n"); } else { if ( param1 && dlg_api.store_dlg_value(dlg, ¶m1_name, ¶m1_val) < 0) { LM_ERR("cannot store flags param into dialog\n"); return -1; } if ( param2 && dlg_api.store_dlg_value(dlg, ¶m2_name, ¶m2_val) < 0) { LM_ERR("cannot store ip param into dialog\n"); return -1; } if (param3) { param2_val.s = (char*)&set->id_set; param2_val.len = sizeof(unsigned int); if (dlg_api.store_dlg_value(dlg, ¶m3_name, ¶m2_val) < 0) { LM_ERR("cannot store set param into dialog\n"); return -1; } } LM_DBG("stored values in dialog\n"); } /* callbacks setup - only once */ if (msg->msg_flags & FL_USE_RTPPROXY) { LM_DBG("rtpproxy callbacks already registered\n"); return 1; } msg->msg_flags |= FL_USE_RTPPROXY; /* handles the replies to the original INVITE */ if (tm_api.register_tmcb( msg, 0, TMCB_RESPONSE_FWDED, engage_tm_reply_callback,0,0)!=1) { LM_ERR("failed to install TM callback\n"); return -1; } if (dlg_api.register_dlgcb(dlg, DLGCB_RESPONSE_WITHIN|DLGCB_REQ_WITHIN, engage_callback, msg, 0) != 0) { LM_ERR("cannot register callback\n"); return -1; } LM_DBG("registered engage_callback\n"); if (dlg_api.register_dlgcb(dlg, DLGCB_FAILED | DLGCB_TERMINATED, engage_close_callback, msg, 0) != 0) { LM_ERR("cannot register close callback\n"); return -1; } return 1; } struct options { str s; int oidx; }; static int append_opts(struct options *op, char ch) { void *p; if (op->s.len <= op->oidx) { p = pkg_realloc(op->s.s, op->oidx + 32); if (p == NULL) { return (-1); } op->s.s = p; op->s.len = op->oidx + 32; } op->s.s[op->oidx++] = ch; return (0); } static int append_opts_str(struct options *op, str *s) { void *p; if (op->s.len < op->oidx + s->len) { p = pkg_realloc(op->s.s, op->oidx + s->len + 32); if (p == NULL) { return (-1); } op->s.s = p; op->s.len = op->oidx + s->len + 32; } memcpy(op->s.s + op->oidx, s->s, s->len); op->oidx += s->len; return (0); } static void free_opts(struct options *op1, struct options *op2, struct options *op3) { if (op1->s.len > 0 && op1->s.s != NULL) { pkg_free(op1->s.s); op1->s.len = 0; } if (op2->s.len > 0 && op2->s.s != NULL) { pkg_free(op2->s.s); op2->s.len = 0; } if (op3->s.len > 0 && op3->s.s != NULL) { pkg_free(op3->s.s); op3->s.len = 0; } } #define FORCE_RTP_PROXY_RET(e) \ do { \ free_opts(&opts, &rep_opts, &pt_opts); \ return (e); \ } while (0); static int force_rtp_proxy(struct sip_msg* msg, char* str1, char* str2, char *setid, char *var, int offer) { struct multi_body *m; struct part * p; struct force_rtpp_args args; struct force_rtpp_args *ap; union sockaddr_union to; struct ip_addr ip; struct cell *trans; memset(&args, '\0', sizeof(args)); m = get_all_bodies(msg); if (m == NULL) { LM_ERR("Unable to parse body\n"); return -1; } LM_DBG("force rtp proxy with param1 <%s> and param2 <%s>\n", str1 ? str1 : "none", str2 ? str2 : "none"); if (get_callid(msg, &args.callid) == -1 || args.callid.len == 0) { LM_ERR("can't get Call-Id field\n"); return (-1); } args.arg1 = str1; args.arg2 = str2; args.offer = offer; for (p = m->first; p != NULL; p = p->next) { int ret = 0; if (p->content_type != ((TYPE_APPLICATION << 16) + SUBTYPE_SDP)) continue; if (p->body.len == 0) { LM_WARN("empty body\n"); continue; } args.body = p->body; /* there is not a problem if the set is not got under lock, since * after we have it, we will never delete/change it */ args.set = get_rtpp_set(msg, (nh_set_param_t *)setid); if (!args.set) { LM_ERR("cannot find RTPProxy set\n"); return -1; } if (rtpproxy_autobridge) { if (nh_lock) lock_start_read(nh_lock); args.node = select_rtpp_node(msg, args.callid, args.set, (pv_spec_p)var, 1); if (args.node == NULL) { LM_ERR("no available proxies\n"); goto error_with_lock; } /* XXX: here we assume that all nodes in a set should be similar */ if (args.node->abr_supported) { if (msg->first_line.type == SIP_REQUEST) { ap = pkg_malloc(sizeof(*ap)); if (ap == NULL) { LM_ERR("can't allocate memory\n"); return (-1); } memcpy(ap, &args, sizeof(*ap)); if (str1 != NULL) { ap->arg1 = pkg_strdup(str1); if (ap->arg1 == NULL) { pkg_free(ap); LM_ERR("can't allocate memory\n"); return (-1); } } if (str2 != NULL) { ap->arg2 = pkg_strdup(str2); if (ap->arg2 == NULL) { if (ap->arg1 != NULL) pkg_free(ap->arg1); pkg_free(ap); LM_ERR("can't allocate memory\n"); return (-1); } } /* we don't remember the node, since it might not be * available later when we execute the callback */ ap->node = NULL; msg_callback_add(msg, REQ_PRE_FORWARD, rtpproxy_pre_fwd, ap); msg_callback_add(msg, MSG_DESTROY, rtpproxy_pre_fwd_free, ap); if (nh_lock) lock_stop_read(nh_lock); continue; } else { /* first try to get the destination of this reply from the * transaction (as the source of the request) */ if (tm_api.t_gett && (trans=tm_api.t_gett())!=0 && trans!=T_UNDEFINED && trans->uas.request ) { /* we have the request from the transaction this * reply belongs to */ args.raddr.s = ip_addr2a(&trans->uas.request->rcv.src_ip); args.raddr.len = strlen(args.raddr.s); } else if (parse_headers(msg, HDR_VIA2_F, 0) != -1 && (msg->via2 != NULL) && (msg->via2->error == PARSE_OK) && update_sock_struct_from_via(&to, msg, msg->via2)!=-1) { su2ip_addr(&ip, &to); args.raddr.s = ip_addr2a(&ip); args.raddr.len = strlen(args.raddr.s); } else { LM_ERR("can't extract reply destination from " "transaction/reply_via2\n"); } } } } LM_DBG("Forcing body:\n[%.*s]\n", args.body.len, args.body.s); ret = force_rtp_proxy_body(msg, &args, (pv_spec_p)var); if (rtpproxy_autobridge) { if (nh_lock) lock_stop_read(nh_lock); args.node = NULL; } if (ret < 0) return ret; } return 1; error_with_lock: if (nh_lock) lock_stop_read(nh_lock); return -1; } static inline int rtpp_get_error(char *command) { int ret; str val; if (!command || command[0] != 'E') return 0; val.s = command + 1; val.len = strlen(val.s) - 1 /* newline */; if (str2sint(&val, &ret)) { LM_ERR("bad error received from RTPProxy: %s\n", command); return -1; } return ret; } int force_rtp_proxy_body(struct sip_msg* msg, struct force_rtpp_args *args, pv_spec_p var) { str body1, oldport, oldip, newport, newip ,nextport; str from_tag, to_tag, tmp, payload_types; int create, port, len, asymmetric, flookup, argc, proxied, real; int orgip, commip, enable_notification; int pf, pf1, force, err, locked = 0; struct options opts, rep_opts, pt_opts, m_opts; char *cp, *cp1; char *cpend, *next; char **ap, *argv[10]; struct lump* anchor; struct iovec v[] = { {NULL, 0}, /* reserved (cookie) */ {NULL, 0}, /* command & common options */ {NULL, 0}, /* per-media/per-node options 1 */ {NULL, 0}, /* per-media/per-node options 2 */ {" ", 1}, /* separator */ {NULL, 0}, /* callid */ {" ", 1}, /* separator */ {NULL, 7}, /* newip */ {" ", 1}, /* separator */ {NULL, 1}, /* oldport */ {" ", 1}, /* separator */ {NULL, 0}, /* from_tag */ {";", 1}, /* separator */ {NULL, 0}, /* medianum */ {" ", 1}, /* separator */ {NULL, 0}, /* to_tag */ {";", 1}, /* separator */ {NULL, 0}, /* medianum */ {" ", 1}, /* separator */ {NULL, 0}, /* notify socket name */ {" ", 1}, /* separator */ {NULL, 0} /* notify tag */ }; char *v1p, *v2p, *c1p, *c2p, *m1p, *m2p, *bodylimit, *o1p, *r2p; char medianum_buf[20]; char buf[32]; int medianum, media_multi; str medianum_str, tmpstr1; int c1p_altered; int vcnt; memset(&opts, '\0', sizeof(opts)); memset(&rep_opts, '\0', sizeof(rep_opts)); memset(&pt_opts, '\0', sizeof(pt_opts)); /* Leave space for U/L prefix TBD later */ if (append_opts(&opts, '?') == -1) { LM_ERR("out of pkg memory\n"); FORCE_RTP_PROXY_RET (-1); } asymmetric = flookup = force = real = orgip = commip = enable_notification = 0; for (cp = args->arg1; cp != NULL && *cp != '\0'; cp++) { switch (*cp) { case 'a': case 'A': if (append_opts(&opts, 'A') == -1) { LM_ERR("out of pkg memory\n"); FORCE_RTP_PROXY_RET (-1); } asymmetric = 1; real = 1; break; case 'i': case 'I': if (append_opts(&opts, 'I') == -1) { LM_ERR("out of pkg memory\n"); FORCE_RTP_PROXY_RET (-1); } break; case 'e': case 'E': if (append_opts(&opts, 'E') == -1) { LM_ERR("out of pkg memory\n"); FORCE_RTP_PROXY_RET (-1); } break; case 'l': case 'L': if (args->offer != 0) { flookup = 1; } break; case 'f': case 'F': force = 1; break; case 'r': case 'R': real = 1; break; case 'c': case 'C': commip = 1; break; case 'o': case 'O': orgip = 1; break; case 'n': case 'N': enable_notification = 1; break; case 'w': case 'W': case 's': case 'S': if (append_opts(&opts, 'S') == -1) { LM_ERR("out of pkg memory\n"); FORCE_RTP_PROXY_RET (-1); } break; case 'z': case 'Z': if (append_opts(&rep_opts, 'Z') == -1) { LM_ERR("out of pkg memory\n"); FORCE_RTP_PROXY_RET (-1); } /* If there are any digits following Z copy them into the command */ for (; cp[1] != '\0' && isdigit(cp[1]); cp++) { if (append_opts(&rep_opts, cp[1]) == -1) { LM_ERR("out of pkg memory\n"); FORCE_RTP_PROXY_RET (-1); } } break; default: LM_WARN("unknown option `%c'\n", *cp); if (append_opts(&opts, *cp) == -1) { LM_ERR("out of pkg memory\n"); FORCE_RTP_PROXY_RET (-1); } } } if (args->raddr.s != NULL) { if (append_opts(&rep_opts, 'R') == -1 || \ append_opts_str(&rep_opts, &args->raddr) == -1) { LM_ERR("out of pkg memory\n"); FORCE_RTP_PROXY_RET (-1); } } if (args->offer != 0) { create = 1; } else { create = 0; } to_tag.s = 0; if (get_to_tag(msg, &to_tag) == -1) { LM_ERR("can't get To tag\n"); FORCE_RTP_PROXY_RET (-1); } if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { LM_ERR("can't get From tag\n"); FORCE_RTP_PROXY_RET (-1); } if (flookup != 0) { if (to_tag.len == 0) { FORCE_RTP_PROXY_RET (-1); } create = 0; if (msg->first_line.type==SIP_REQUEST) { tmp = from_tag; from_tag = to_tag; to_tag = tmp; } } else if ((msg->first_line.type==SIP_REPLY && args->offer!=0)|| (msg->first_line.type == SIP_REQUEST && args->offer == 0) ) { if (to_tag.len == 0) { FORCE_RTP_PROXY_RET (-1); } tmp = from_tag; from_tag = to_tag; to_tag = tmp; } proxied = 0; if (nortpproxy_str.len) { for ( cp=args->body.s ; (len=args->body.s+args->body.len-cp) >= nortpproxy_str.len ; ) { cp1 = ser_memmem(cp, nortpproxy_str.s, len, nortpproxy_str.len); if (cp1 == NULL) break; if (cp1[-1] == '\n' || cp1[-1] == '\r') { proxied = 1; break; } cp = cp1 + nortpproxy_str.len; } } if (proxied != 0 && force == 0) { FORCE_RTP_PROXY_RET (-1); } /* * Parsing of SDP body. * It can contain a few session descriptions (each starts with * v-line), and each session may contain a few media descriptions * (each starts with m-line). * We have to change ports in m-lines, and also change IP addresses in * c-lines which can be placed either in session header (fallback for * all medias) or media description. * Ports should be allocated for any media. IPs all should be changed * to the same value (RTP proxy IP), so we can change all c-lines * unconditionally. */ bodylimit = args->body.s + args->body.len; v1p = find_sdp_line(args->body.s, bodylimit, 'v'); if (v1p == NULL) { LM_ERR("no sessions in SDP\n"); FORCE_RTP_PROXY_RET (-1); } v2p = find_next_sdp_line(v1p, bodylimit, 'v', bodylimit); media_multi = (v2p != bodylimit); v2p = v1p; medianum = 0; opts.s.s[0] = (create == 0) ? 'L' : 'U'; STR2IOVEC(args->callid, v[5]); STR2IOVEC(from_tag, v[11]); STR2IOVEC(to_tag, v[15]); if (enable_notification && (rtpp_notify_socket.s == 0 || rtpp_notify_socket.len == 0)) { LM_DBG("cannot receive timeout notifications because" "rtpp_notify_socket parameter is not specified\n"); enable_notification = 0; } if(enable_notification && opts.s.s[0] == 'U') { struct dlg_cell * dlg; str notify_tag; dlg = dlg_api.get_dlg(); if(dlg == NULL) { LM_ERR("Failed to get dialog\n"); goto error; } /* construct the notify tag from dialog ids */ notify_tag.len= sprintf(buf, "%d.%d", dlg->h_entry, dlg->h_id); notify_tag.s = buf; LM_DBG("notify_tag= %s\n", notify_tag.s); if (strncmp(rtpp_notify_socket.s, "tcp:", 4) == 0) { rtpp_notify_socket.s += 4; rtpp_notify_socket.len -= 4; } else if (strncmp(rtpp_notify_socket.s, "unix:", 5) == 0) { rtpp_notify_socket.s += 5; rtpp_notify_socket.len -= 5; } STR2IOVEC(rtpp_notify_socket, v[19]); STR2IOVEC(notify_tag, v[21]); } m_opts = opts; for(;;) { /* Per-session iteration. */ v1p = v2p; if (v1p == NULL || v1p >= bodylimit) break; /* No sessions left */ v2p = find_next_sdp_line(v1p, bodylimit, 'v', bodylimit); /* v2p is text limit for session parsing. */ /* get session origin */ o1p = find_sdp_line(v1p, v2p, 'o'); if (o1p==0) { LM_ERR("no o= in session\n"); goto error; } /* Have this session media description? */ m1p = find_sdp_line(o1p, v2p, 'm'); if (m1p == NULL) { LM_ERR("no m= in session\n"); goto error; } /* * Find c1p only between session begin and first media. * c1p will give common c= for all medias. */ c1p = find_sdp_line(o1p, m1p, 'c'); c1p_altered = 0; if (orgip==0) o1p = 0; /* Have session. Iterate media descriptions in session */ m2p = m1p; for (;;) { m_opts.oidx = opts.oidx; m1p = m2p; if (m1p == NULL || m1p >= v2p) break; m2p = find_next_sdp_line(m1p, v2p, 'm', v2p); /* c2p will point to per-media "c=" */ c2p = find_sdp_line(m1p, m2p, 'c'); /* Extract address and port */ r2p = find_sdp_line_complex(m1p, m2p, "a=rtcp:"); tmpstr1.s = c2p ? c2p : c1p; if (tmpstr1.s == NULL) { /* No "c=" */ LM_ERR("can't find media IP in the message\n"); goto error; } tmpstr1.len = v2p - tmpstr1.s; /* limit is session limit text */ if (extract_mediaip(&tmpstr1, &oldip, &pf,"c=") == -1) { LM_ERR("can't extract media IP from the message\n"); goto error; } tmpstr1.s = m1p; tmpstr1.len = m2p - m1p; if (extract_mediainfo(&tmpstr1, &oldport, &payload_types) == -1) { LM_ERR("can't extract media port from the message\n"); goto error; } ++medianum; /* If the callee wants to neither send nor receive a stream offered by the caller, the callee sets the port number of that stream to zero in its media description - don't engage rtpproxy for such streams */ if (oldport.s[0] == '0' && oldport.len == 1) continue; if (asymmetric != 0 || real != 0) { newip = oldip; } else { newip.s = ip_addr2a(&msg->rcv.src_ip); newip.len = strlen(newip.s); /* update the AF */ pf = msg->rcv.src_ip.af; } /* XXX must compare address families in all addresses */ if (pf == AF_INET6) { if (append_opts(&m_opts, '6') == -1) { LM_ERR("out of pkg memory\n"); goto error; } } STR2IOVEC(newip, v[7]); STR2IOVEC(oldport, v[9]); if (1 || media_multi) /* XXX netch: can't choose now*/ { snprintf(medianum_buf, sizeof medianum_buf, "%d", medianum); medianum_str.s = medianum_buf; medianum_str.len = strlen(medianum_buf); STR2IOVEC(medianum_str, v[13]); STR2IOVEC(medianum_str, v[17]); } else { v[12].iov_len = v[13].iov_len = 0; v[16].iov_len = v[17].iov_len = 0; } if (!args->node && nh_lock) { locked = 1; lock_start_read(nh_lock); } do { /* if not successful choose a different rtpproxy */ if (!args->node) { args->node = select_rtpp_node(msg, args->callid, args->set, var, 0); if (!args->node) { LM_ERR("no available proxies\n"); goto error; } LM_DBG("trying new rtpproxy node %s\n", args->node->rn_address); } /* if we don't have, we should choose a new node */ if (rep_opts.oidx > 0) { if (args->node->rn_rep_supported == 0) { LM_WARN("re-packetization is requested but is not " "supported by the selected RTP proxy node\n"); v[2].iov_len = 0; } else { v[2].iov_base = rep_opts.s.s; v[2].iov_len = rep_opts.oidx; } } if (payload_types.len > 0 && args->node->rn_ptl_supported != 0) { pt_opts.oidx = 0; if (append_opts(&pt_opts, 'c') == -1) { LM_ERR("out of pkg memory\n"); goto error; } /* * Convert space-separated payload types list into * a comma-separated list. */ for (cp = payload_types.s; cp < payload_types.s + payload_types.len; cp++) { if (isdigit(*cp)) { if (append_opts(&pt_opts, *cp) == -1) { LM_ERR("out of pkg memory\n"); goto error; } continue; } do { cp++; } while (!isdigit(*cp) && cp < payload_types.s + payload_types.len); /* Check EOL */ if (cp >= payload_types.s + payload_types.len) break; if (append_opts(&pt_opts, ',') == -1) { LM_ERR("out of pkg memory\n"); goto error; } cp--; } v[3].iov_base = pt_opts.s.s; v[3].iov_len = pt_opts.oidx; } else { v[3].iov_len = 0; } if(enable_notification && opts.s.s[0] == 'U') vcnt = 22; else { vcnt = (to_tag.len > 0) ? 18 : 14; } v[1].iov_base = m_opts.s.s; v[1].iov_len = m_opts.oidx; cp = send_rtpp_command(args->node, v, vcnt); if (!cp && !create) { LM_ERR("cannot lookup a session on a different RTPProxy\n"); goto error; } if (cp && (err = rtpp_get_error(cp))) { /* check internal errors */ if (err >= 7 && err <= 10) { cp = NULL; args->node->rn_disabled = 1; args->node->rn_recheck_ticks = get_ticks() + rtpproxy_disable_tout; raise_rtpproxy_event(args->node, 0); } else { LM_ERR("unhandled rtpproxy error: %d\n", err); goto error; } } args->node = NULL; } while (cp == NULL); if (locked) { locked = 0; lock_stop_read(nh_lock); } LM_DBG("proxy reply: %s\n", cp); /* Parse proxy reply to */ argc = 0; memset(argv, 0, sizeof(argv)); cpend=cp+strlen(cp); next=eat_token_end(cp, cpend); for (ap=argv; cp= ((char*)argv+sizeof(argv))) break; } } if (argc < 1) { LM_ERR("no reply from rtp proxy\n"); goto error; } port = atoi(argv[0]); if (port <= 0 || port > 65535) { if (port != 0 || flookup == 0) LM_ERR("incorrect port %i in reply " "from rtp proxy\n",port); goto error; } pf1 = (argc >= 3 && argv[2][0] == '6') ? AF_INET6 : AF_INET; if (isnulladdr(&oldip, pf)) { if (pf1 == AF_INET6) { newip.s = "::"; newip.len = 2; } else { newip.s = "0.0.0.0"; newip.len = 7; } } else { /* handle all possible cases properly * 1) second argument w/ip passed to offer/answer (args->arg2) * 2) no second argument, rtpproxy response contains ip (argv[1]) * 3) no ip in rtpproxy response (started using unix socket and no -l param) * must revert to default of proxy ip */ newip.s = args->arg2 ? args->arg2 : argv[1]; if (newip.s == NULL) { newip.s = ip_addr2a(&msg->rcv.dst_ip); pf1 = msg->rcv.dst_ip.af; } newip.len = strlen(newip.s); } /* marker to double check : newport goes: str -> int -> str ?!?! */ newport.s = int2str(port, &newport.len); /* beware static buffer */ /* Alter port. */ body1.s = m1p; body1.len = bodylimit - body1.s; /* do not do it if old port was 0 (means media disable) * - check if actually should be better done in rtpptoxy, * by returning also 0 * - or by not sending to rtpproxy the old port if 0 */ if(oldport.len!=1 || oldport.s[0]!='0') { if (alter_mediaport(msg, &body1, &oldport, &newport, 0) == -1) goto error; } nextport.s = int2str(port+1, &nextport.len); if( r2p ) if (alter_rtcp(msg, &body1, &newip, pf1, &nextport, r2p) < 0 ) goto error; /* * Alter IP. Don't alter IP common for the session * more than once. */ if (c2p != NULL || !c1p_altered) { body1.s = c2p ? c2p : c1p; body1.len = bodylimit - body1.s; if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf1, 0)==-1) goto error; if (!c2p) c1p_altered = 1; } /* * Alter common IP if required, but don't do it more than once. */ if (commip && c1p && !c1p_altered) { tmpstr1.s = c1p; tmpstr1.len = v2p - tmpstr1.s; if (extract_mediaip(&tmpstr1, &oldip, &pf,"c=") == -1) { LM_ERR("can't extract media IP from the message\n"); goto error; } body1.s = c1p; body1.len = bodylimit - body1.s; if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf1, 0)==-1) goto error; c1p_altered = 1; } /* * Alter the IP in "o=", but only once per session */ if (o1p) { tmpstr1.s = o1p; tmpstr1.len = v2p - tmpstr1.s; if (extract_mediaip(&tmpstr1, &oldip, &pf,"o=") == -1) { LM_ERR("can't extract media IP from the message\n"); goto error; } body1.s = o1p; body1.len = bodylimit - body1.s; if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf1, 0)==-1) goto error; o1p = 0; } } /* Iterate medias in session */ } /* Iterate sessions */ free_opts(&opts, &rep_opts, &pt_opts); if (proxied == 0 && nortpproxy_str.len) { cp = pkg_malloc((nortpproxy_str.len + CRLF_LEN) * sizeof(char)); if (cp == NULL) { LM_ERR("out of pkg memory\n"); return -1; } /* find last CRLF and add after it */ cp1 = args->body.s + args->body.len; while( cp1>args->body.s && !(*(cp1-1)=='\n' && *(cp1-2)=='\r') ) cp1--; if (cp1==args->body.s) cp1=args->body.s + args->body.len; anchor = anchor_lump(msg, cp1 - msg->buf, 0); if (anchor == NULL) { LM_ERR("anchor_lump failed\n"); pkg_free(cp); return -1; } memcpy(cp, nortpproxy_str.s, nortpproxy_str.len); memcpy(cp+nortpproxy_str.len , CRLF, CRLF_LEN); if (insert_new_lump_before(anchor, cp, nortpproxy_str.len + CRLF_LEN, 0) == NULL) { LM_ERR("insert_new_lump_after failed\n"); pkg_free(cp); return -1; } } return 1; error: if(!locked) FORCE_RTP_PROXY_RET (-1); /* we are done reading -> unref the data */ lock_stop_read( nh_lock ); FORCE_RTP_PROXY_RET (-1); } static int start_recording_f(struct sip_msg* msg, char *setid, char *var) { int nitems; str callid = {0, 0}; str from_tag = {0, 0}; str to_tag = {0, 0}; struct rtpp_node *node; struct rtpp_set *set; struct iovec v[1 + 4 + 3] = {{NULL, 0}, {"R", 1}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}}; /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 1 */ if (get_callid(msg, &callid) == -1 || callid.len == 0) { LM_ERR("can't get Call-Id field\n"); return -1; } if (get_to_tag(msg, &to_tag) == -1) { LM_ERR("can't get To tag\n"); return -1; } if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { LM_ERR("can't get From tag\n"); return -1; } STR2IOVEC(callid, v[3]); STR2IOVEC(from_tag, v[5]); STR2IOVEC(to_tag, v[7]); nitems = 8; if (msg->first_line.type == SIP_REPLY) { if (to_tag.len == 0) return -1; STR2IOVEC(to_tag, v[5]); STR2IOVEC(from_tag, v[7]); } else { STR2IOVEC(from_tag, v[5]); STR2IOVEC(to_tag, v[7]); if (to_tag.len <= 0) nitems = 6; } set = get_rtpp_set(msg, (nh_set_param_t *)setid); if (!set) { LM_ERR("could not find rtpproxy set\n"); return 0; } if (nh_lock) { lock_start_read( nh_lock ); } node = select_rtpp_node(msg, callid, set, (pv_spec_p)var, 1); if (!node) { LM_ERR("no available proxies\n"); goto error; } send_rtpp_command(node, v, nitems); if(nh_lock) { /* we are done reading -> unref the data */ lock_stop_read( nh_lock ); } return 1; error: if(!nh_lock) return -1; /* we are done reading -> unref the data */ lock_stop_read( nh_lock ); return -1; } opensips-2.2.2/modules/rtpproxy/rtpproxy.h000066400000000000000000000066621300170765700210340ustar00rootroot00000000000000/* * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2007-04-13 splitted from nathelper.c (ancuta) */ #ifndef _NATHELPER_NATHELPER_H #define _NATHELPER_NATHELPER_H #include "../../str.h" #include "../../pvar.h" #include "../dialog/dlg_load.h" #include "../../rw_locking.h" /* Handy macros */ #define STR2IOVEC(sx, ix) do {(ix).iov_base = (sx).s; (ix).iov_len = (sx).len;} while(0) #define SZ2IOVEC(sx, ix) do {(ix).iov_base = (sx); (ix).iov_len = strlen(sx);} while(0) struct rtpp_node { unsigned int idx; /* overall index */ str rn_url; /* unparsed, deletable */ int rn_umode; char *rn_address; /* substring of rn_url */ int rn_disabled; /* found unaccessible? */ unsigned rn_weight; /* for load balancing */ unsigned int rn_recheck_ticks; int rn_rep_supported; int rn_ptl_supported; int abr_supported; struct rtpp_node *rn_next; }; struct rtpp_set{ unsigned int id_set; unsigned weight_sum; unsigned int rtpp_node_count; int set_disabled; unsigned int set_recheck_ticks; struct rtpp_node *rn_first; struct rtpp_node *rn_last; struct rtpp_set *rset_next; }; struct rtpp_set_head{ struct rtpp_set *rset_first; struct rtpp_set *rset_last; }; struct force_rtpp_args { char *arg1; char *arg2; int offer; str body; str callid; struct rtpp_set *set; struct rtpp_node *node; str raddr; }; /* used in timeout_listener_process */ struct rtpp_notify_node { int index; int fd; int mode; char* addr; struct rtpp_notify_node *next; }; struct rtpp_notify_head { int changed; gen_lock_t *lock; struct rtpp_notify_node *rtpp_list; }; /* parameter type for set_rtp_proxy_set() */ #define NH_VAL_SET_FIXED 0 #define NH_VAL_SET_SPEC 1 #define NH_VAL_SET_UNDEF 2 typedef struct rtpp_set_param{ int t; union { struct rtpp_set * fixed_set; pv_spec_t var_set; int int_set; } v; } nh_set_param_t; extern rw_lock_t *nh_lock; extern str rtpp_notify_socket; extern int rtpp_notify_socket_un; extern struct dlg_binds dlg_api; extern int detect_rtp_idle; extern struct rtpp_set_head ** rtpp_set_list; extern struct rtpp_notify_head * rtpp_notify_h; int init_rtpp_notify_list(); void timeout_listener_process(int rank); /* Functions from nathelper */ struct rtpp_set *get_rtpp_set(struct sip_msg *, nh_set_param_t *); struct rtpp_node *select_rtpp_node(struct sip_msg *, str, struct rtpp_set *, pv_spec_p, int); char *send_rtpp_command(struct rtpp_node *, struct iovec *, int); int force_rtp_proxy_body(struct sip_msg *, struct force_rtpp_args *, pv_spec_p); #endif opensips-2.2.2/modules/rtpproxy/rtpproxy_callbacks.c000066400000000000000000000046711300170765700230240ustar00rootroot00000000000000/* * Copyright (C) 2010 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include "../../parser/msg_parser.h" #include "../../msg_callbacks.h" #include "../../proxy.h" #include "../../mem/mem.h" #include "rtpproxy.h" void rtpproxy_pre_fwd(struct sip_msg *msg, cb_type_t cb_type, void *mod_args, void *core_args) { struct proxy_l *p; struct ip_addr ip; char *cp; struct force_rtpp_args *args; assert(cb_type == REQ_PRE_FORWARD); p = (struct proxy_l *)core_args; args = (struct force_rtpp_args *)mod_args; if (args->raddr.s != NULL) return; hostent2ip_addr(&ip, &p->host, p->addr_idx); cp = ip_addr2a(&ip); args->raddr.len = strlen(cp); if (ip.af == AF_INET) { args->raddr.s = pkg_malloc(args->raddr.len + 1); if (args->raddr.s == NULL) { LM_ERR("out of pkg memory\n"); return; } sprintf(args->raddr.s, "%s", cp); } else { args->raddr.len += 2; args->raddr.s = pkg_malloc(args->raddr.len + 1); if (args->raddr.s == NULL) { LM_ERR("out of pkg memory\n"); return; } sprintf(args->raddr.s, "[%s]", cp); } force_rtp_proxy_body(msg, args, NULL); } void rtpproxy_pre_fwd_free(struct sip_msg *msg, cb_type_t cb_type, void *mod_args, void *core_args) { struct force_rtpp_args *args; assert(cb_type == MSG_DESTROY); args = (struct force_rtpp_args *)mod_args; if (args->arg1 != NULL) pkg_free(args->arg1); if (args->arg2 != NULL) pkg_free(args->arg2); if (args->raddr.s != NULL) pkg_free(args->raddr.s); pkg_free(args); } opensips-2.2.2/modules/rtpproxy/rtpproxy_callbacks.h000066400000000000000000000021521300170765700230210ustar00rootroot00000000000000/* * Copyright (C) 2010 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _NATHELPER_CALLBACKS_H #define _NATHELPER_CALLBACKS_H #include "../../parser/msg_parser.h" #include "../../msg_callbacks.h" void rtpproxy_pre_fwd(struct sip_msg *, cb_type_t, void *, void *); void rtpproxy_pre_fwd_free(struct sip_msg *, cb_type_t, void *, void *); #endif opensips-2.2.2/modules/rtpproxy/rtpproxy_stream.c000066400000000000000000000141511300170765700223720ustar00rootroot00000000000000/* * Copyright (C) 2008 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include "../../ip_addr.h" #include "../../parser/msg_parser.h" #include "../../sr_module.h" #include "../../ut.h" #include "rtpproxy.h" #include "nhelpr_funcs.h" static int rtpproxy_stream(struct sip_msg* msg, str *pname, int count, char *setid, char *var, int stream2uac) { int nitems, ret = -1; str callid, from_tag, to_tag; struct rtpp_node *node; struct rtpp_set *set; char cbuf[16]; struct iovec v[] = { {NULL, 0}, {cbuf, 0}, /* 1 P */ {" ", 1}, {NULL, 0}, /* 3 callid */ {" ", 1}, {NULL, 0}, /* 5 pname */ {" session ", 9}, {NULL, 0}, /* 7 from tag */ {";1 ", 3}, {NULL, 0}, /* 9 to tag */ {";1", 2} }; if (get_callid(msg, &callid) == -1 || callid.len == 0) { LM_ERR("can't get Call-Id field\n"); return -1; } if (get_to_tag(msg, &to_tag) == -1) { LM_ERR("can't get To tag\n"); return -1; } if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { LM_ERR("can't get From tag\n"); return -1; } v[1].iov_len = sprintf(cbuf, "P%d", count); STR2IOVEC(callid, v[3]); STR2IOVEC(*pname, v[5]); nitems = 11; if (stream2uac == 0) { if (to_tag.len == 0) return -1; STR2IOVEC(to_tag, v[7]); STR2IOVEC(from_tag, v[9]); } else { STR2IOVEC(from_tag, v[7]); STR2IOVEC(to_tag, v[9]); if (to_tag.len <= 0) nitems -= 2; } if (nh_lock) { lock_start_read( nh_lock ); } set = get_rtpp_set(msg, (nh_set_param_t *)setid); if (!set) { LM_ERR("no set found\n"); goto end; } node = select_rtpp_node(msg, callid, set, (pv_spec_p)var, 1); if (!node) { LM_ERR("no available proxies\n"); goto end; } if (node->rn_ptl_supported == 0) { LM_ERR("required functionality is not " "supported by the version of the RTPproxy running on the selected " "node. Please upgrade the RTPproxy and try again.\n"); goto end; } send_rtpp_command(node, v, nitems); ret = 1; end: if (nh_lock) { lock_stop_read( nh_lock ); } return ret; } static int rtpproxy_stream4_f(struct sip_msg *msg, char *str1, int count, char *setid, char *var, int stream2uac) { str pname; if (str1 == NULL || pv_printf_s(msg, (pv_elem_p)str1, &pname) != 0) return -1; return rtpproxy_stream(msg, &pname, count, setid, var, stream2uac); } int rtpproxy_stream2uac4_f(struct sip_msg* msg, char* str1, char* str2, char *str3, char *str4) { return rtpproxy_stream4_f(msg, str1, (int)(long)str2, str3, str4, 1); } int rtpproxy_stream2uas4_f(struct sip_msg* msg, char* str1, char* str2, char *str3, char *str4) { return rtpproxy_stream4_f(msg, str1, (int)(long)str2, str3, str4, 0); } static int rtpproxy_stop_stream(struct sip_msg* msg, char *setid, char *var, int stream2uac) { int nitems, ret = -1; str callid, from_tag, to_tag; struct rtpp_node *node; struct rtpp_set *set; struct iovec v[] = { {NULL, 0}, {"S", 1}, /* 1 */ {" ", 1}, {NULL, 0}, /* 3 callid */ {" ", 1}, {NULL, 0}, /* 5 from tag */ {";1 ", 3}, {NULL, 0}, /* 7 to tag */ {";1", 2} }; if (get_callid(msg, &callid) == -1 || callid.len == 0) { LM_ERR("can't get Call-Id field\n"); return -1; } if (get_to_tag(msg, &to_tag) == -1) { LM_ERR("can't get To tag\n"); return -1; } if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { LM_ERR("can't get From tag\n"); return -1; } STR2IOVEC(callid, v[3]); nitems = 9; if (stream2uac == 0) { if (to_tag.len == 0) return -1; STR2IOVEC(to_tag, v[5]); STR2IOVEC(from_tag, v[7]); } else { STR2IOVEC(from_tag, v[5]); STR2IOVEC(to_tag, v[7]); if (to_tag.len <= 0) nitems -= 2; } if (nh_lock) { lock_start_read( nh_lock ); } set = get_rtpp_set(msg, (nh_set_param_t *)setid); if (!set) { LM_ERR("no set found\n"); goto end; } node = select_rtpp_node(msg, callid, set, (pv_spec_p)var, 1); if (!node) { LM_ERR("no available proxies\n"); goto end; } if (node->rn_ptl_supported == 0) { LM_ERR("required functionality is not " "supported by the version of the RTPproxy running on the selected " "node. Please upgrade the RTPproxy and try again.\n"); goto end; } send_rtpp_command(node, v, nitems); ret = 1; end: if (nh_lock) { lock_stop_read( nh_lock ); } return ret; } int rtpproxy_stop_stream2uac2_f(struct sip_msg* msg, char* str1, char *str2) { return rtpproxy_stop_stream(msg, str1, str2, 1); } int rtpproxy_stop_stream2uas2_f(struct sip_msg* msg, char* str1, char *str2) { return rtpproxy_stop_stream(msg, str1, str2, 0); } opensips-2.2.2/modules/rtpproxy/rtpproxy_stream.h000066400000000000000000000026041300170765700223770ustar00rootroot00000000000000/* * Copyright (C) 2008 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _RTPPROXY_STREAM_H #define _RTPPROXY_STREAM_H int rtpproxy_stream2uac4_f(struct sip_msg *, char *, char *, char *, char *); int rtpproxy_stream2uas4_f(struct sip_msg *, char *, char *, char *, char *); int rtpproxy_stop_stream2uac2_f(struct sip_msg *, char *, char *); int rtpproxy_stop_stream2uas2_f(struct sip_msg *, char *, char *); #endif opensips-2.2.2/modules/rtpproxy/timeout_process.c000066400000000000000000000355561300170765700223500ustar00rootroot00000000000000/* * Copyright (C) 2010 Voice System * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * Initial version 02-04-2010 (Anca Vamanu) */ #include #include #include #include #include #include #include #include #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../trim.h" #include "../../resolve.h" #include "../../pt.h" #include "../../sr_module.h" #include "rtpproxy.h" #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #define BUF_LEN 255 #define POLL_DEFAULT_SIZE 8 void update_rtpproxy_list(void); int socket_fd; int nfds = 0; int nr_events; int pfds_size = POLL_DEFAULT_SIZE; struct pollfd* pfds; #define IS_DIGIT(_c) ((_c) >= '0' && (_c) <= '9') void timeout_listener_process(int rank) { struct sockaddr_un saddr_un; struct sockaddr_un *s_un; struct sockaddr_in saddr_in; struct sockaddr_in *s_in; struct sockaddr_in6 *s_in6; int connect_fd; char buffer[BUF_LEN]; char *p, *sp, *end, *start; unsigned int h_entry, h_id; str id; unsigned short port; struct sockaddr* saddr; int len, i,n, left; int optval = 1; struct sockaddr rtpp_info; struct rtpp_notify_node *rtpp_lst; str terminate_reason = str_init("RTPProxy Timeout"); int offset = 0; if (init_child(PROC_MODULE) != 0) { LM_ERR("cannot init child process"); return; } if (!rtpp_notify_socket_un) { p = strrchr(rtpp_notify_socket.s, ':'); if (!p) { LM_ERR("invalid udp address <%.*s>\n", rtpp_notify_socket.len, rtpp_notify_socket.s); return; } n = p- rtpp_notify_socket.s; rtpp_notify_socket.s[n] = 0; id.s = p+1; id.len = rtpp_notify_socket.len - n -1; port= str2s(id.s, id.len, &n); if(n) { LM_ERR("Bad format for socket name. Expected ip:port\n"); return; } memset(&saddr_in, 0, sizeof(saddr_in)); saddr_in.sin_addr.s_addr = inet_addr(rtpp_notify_socket.s); saddr_in.sin_family = AF_INET; saddr_in.sin_port = htons(port); socket_fd = socket(AF_INET, SOCK_STREAM, 0); if (socket_fd == -1) { LM_ERR("can't create timeout socket\n"); return; } saddr = (struct sockaddr*)&saddr_in; len = sizeof(saddr_in); LM_DBG("binding socket %d to %s:%d\n", socket_fd, rtpp_notify_socket.s, port); } else { /* create socket */ socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (socket_fd == -1) { LM_ERR("Failed to create unix socket\n"); return; } memset(&saddr_un, 0, sizeof(struct sockaddr_un)); saddr_un.sun_family = AF_LOCAL; strncpy(saddr_un.sun_path, rtpp_notify_socket.s, sizeof(saddr_un.sun_path) - 1); saddr = (struct sockaddr*)&saddr_un; len = sizeof(saddr_un); LM_DBG("binding unix socket %s\n", rtpp_notify_socket.s); } if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval)) == -1) { LM_ERR("setsockopt failed %s\n", strerror(errno)); return; } if (bind(socket_fd, saddr, len) == -1) { LM_ERR("failed to bind to socket: %s\n", strerror(errno)); return; } /* open socket for listening */ if(listen(socket_fd, 10) == -1) { LM_ERR("socket listen failed: %s(%d)\n", strerror(errno), errno); close(socket_fd); return; } pfds = (struct pollfd *)pkg_malloc(pfds_size*sizeof(struct pollfd)); if (!pfds) { LM_ERR("no more pkg memory\n"); return; } pfds[0].fd = socket_fd; pfds[nfds++].events = POLLIN; for(;;) { nr_events = poll(pfds, nfds, -1); if (nr_events < 0) continue; /* check if the rtpproxy list needs updates */ lock_get(rtpp_notify_h->lock); if (rtpp_notify_h->changed) { /* update list */ update_rtpproxy_list(); rtpp_notify_h->changed = 0; } lock_release(rtpp_notify_h->lock); rtpp_lst = NULL; /* there is a new connection */ if (pfds[0].revents & POLLIN) { i = sizeof(rtpp_info); memset(&rtpp_info, 0, i); connect_fd = accept(socket_fd, &rtpp_info, (socklen_t *)&i); if(connect_fd < 0) { LM_ERR("socket accept failed: %s(%d)\n", strerror(errno), errno); continue; } /* if it is a unix socket, try to authenticate it */ if (rtpp_info.sa_family == AF_UNIX) { s_un = (struct sockaddr_un*)&rtpp_info; /* check if the socket is already opened */ lock_get(rtpp_notify_h->lock); for (rtpp_lst = rtpp_notify_h->rtpp_list; rtpp_lst; rtpp_lst = rtpp_lst->next) if ( rtpp_lst->mode == 0 && !strcmp(rtpp_lst->addr, s_un->sun_path)) break; /* if not found add a new one */ if (!rtpp_lst) { /* leave the lock for a moment */ lock_release(rtpp_notify_h->lock); rtpp_lst = (struct rtpp_notify_node*) shm_malloc(sizeof(struct rtpp_notify_node)); if (!rtpp_lst) { LM_ERR("no shm more memory\n"); return; } rtpp_lst->index = 0; rtpp_lst->mode = 0; rtpp_lst->addr = 0; /* copy the socket name */ len = strlen(s_un->sun_path); rtpp_lst->addr = (char *)shm_malloc(len + 1); if (!rtpp_lst->addr) { LM_ERR("no more shm memory\n"); return; } memcpy(rtpp_lst->addr, s_un->sun_path, len + 1); lock_get(rtpp_notify_h->lock); rtpp_lst->next = rtpp_notify_h->rtpp_list; rtpp_notify_h->rtpp_list = rtpp_lst; } } else { /* search if I can find this connection */ if (rtpp_info.sa_family == AF_INET) { s_in = (struct sockaddr_in*)&rtpp_info; lock_get(rtpp_notify_h->lock); for (rtpp_lst = rtpp_notify_h->rtpp_list; rtpp_lst; rtpp_lst = rtpp_lst->next) if (rtpp_lst->mode == 1 && memcmp(rtpp_lst->addr, &s_in->sin_addr.s_addr, 4) == 0) break; } else if (rtpp_info.sa_family == AF_INET6) { s_in6 = (struct sockaddr_in6*)&rtpp_info; lock_get(rtpp_notify_h->lock); for (rtpp_lst = rtpp_notify_h->rtpp_list; rtpp_lst; rtpp_lst = rtpp_lst->next) if (rtpp_lst->mode == 6 && memcmp(rtpp_lst->addr, s_in6->sin6_addr.s6_addr, 16) == 0) break; } else { LM_ERR("cannot accept this type of connection\n"); } } if (!rtpp_lst) { lock_release(rtpp_notify_h->lock); LM_DBG("unknown rtpproxy -- ignoring\n"); shutdown(connect_fd, SHUT_RDWR); close(connect_fd); } else { /* valid connection - checking if already connected */ if (rtpp_lst->index) { LM_DBG("rtpproxy restarted - update connection status\n"); shutdown(rtpp_lst->fd, SHUT_RDWR); close(rtpp_lst->fd); } else { rtpp_lst->index = nfds++; if (nfds > pfds_size) { pfds_size *= 2; pfds = (struct pollfd*)pkg_realloc(pfds, pfds_size*sizeof(struct pollfd)); } } LM_DBG("rtpproxy accepted\n"); pfds[rtpp_lst->index].fd = connect_fd; pfds[rtpp_lst->index].events = POLLIN; rtpp_lst->fd = connect_fd; lock_release(rtpp_notify_h->lock); } nr_events--; } for (i=1; (nr_events && ilock); for (rtpp_lst=rtpp_notify_h->rtpp_list; rtpp_lst;rtpp_lst=rtpp_lst->next) if (rtpp_lst->index == i) break; if (!rtpp_lst) { LM_ERR("BUG - rtpproxy not found\n"); lock_release(rtpp_notify_h->lock); continue; } rtpp_lst->index = 0; lock_release(rtpp_notify_h->lock); nfds--; shutdown(pfds[i].fd, SHUT_RDWR); close(pfds[i].fd); if (nfds == i) continue; pfds[i].fd = pfds[nfds].fd; lock_get(rtpp_notify_h->lock); for (rtpp_lst=rtpp_notify_h->rtpp_list; rtpp_lst; rtpp_lst=rtpp_lst->next) if (rtpp_lst->index == nfds) break; if (!rtpp_lst) { LM_ERR("BUG - rtpproxy index mismatch\n"); lock_release(rtpp_notify_h->lock); continue; } rtpp_lst->index = i; lock_release(rtpp_notify_h->lock); continue; } LM_INFO("Timeout detected on the following calls [%.*s]\n", len, buffer); p = buffer; left = len + offset; offset = 0; end = buffer + left; do { start = p; /* the message is: h_entry.h_id\n */ sp = memchr(p, '.', left); if (sp == NULL) break; id.s = p; id.len = sp - p; if (sp >= end) break; p = sp + 1; left -= id.len + 1; if(str2int(&id, &h_entry)< 0) { LM_ERR("Wrong formatted message received from rtpproxy - invalid" " dialog entry [%.*s]\n", id.len, id.s); break; } sp = memchr(p, '\n', left); if (sp == NULL) break; id.s = p; id.len = sp - p; if (sp >= end) break; p = sp + 1; left -= id.len + 1; if(str2int(&id, &h_id)< 0) { LM_ERR("Wrong formatted message received from rtpproxy - invalid" " dialog id [%.*s]\n", id.len, id.s); break; } LM_DBG("hentry = %u, h_id = %u\n", h_entry, h_id); if(dlg_api.terminate_dlg(h_entry, h_id,&terminate_reason)< 0) LM_ERR("Failed to terminate dialog h_entry=[%u], h_id=[%u]\n", h_entry, h_id); LM_DBG("Left to process: %d\n[%.*s]\n", left, left, p); } while (p < end); offset = end - start; memmove(buffer, start, end - start); } } } struct rtpp_notify_node *new_rtpp_notify_node(struct rtpp_node *crt_rtpp) { char buffer[BUF_LEN]; char *p; struct hostent *rtpp_server; struct rtpp_notify_node *rtpp_lst; rtpp_lst = (struct rtpp_notify_node*) shm_malloc(sizeof(struct rtpp_notify_node)); if (!rtpp_lst) { LM_ERR("no shm more memory\n"); return NULL; } rtpp_lst->mode = crt_rtpp->rn_umode; rtpp_lst->index = 0; rtpp_lst->next = NULL; memcpy(buffer,crt_rtpp->rn_address,strlen(crt_rtpp->rn_address) + 1); p = strrchr(buffer, ':'); if (!p) { LM_ERR("invalid address %s\n", crt_rtpp->rn_address); goto error; } *p = 0; rtpp_server = resolvehost(buffer, 0); if (!rtpp_server || !rtpp_server->h_addr) { LM_ERR("cannot resolve hostname %s\n", crt_rtpp->rn_address); goto error; } rtpp_lst->addr = (char*)shm_malloc(rtpp_server->h_length); if (!rtpp_lst->addr) { LM_ERR("no more shm memory\n"); goto error; } memcpy(rtpp_lst->addr,rtpp_server->h_addr,rtpp_server->h_length); return rtpp_lst; error: shm_free(rtpp_lst); return NULL; } int init_rtpp_notify_list(void) { struct rtpp_set * rtpp_list; struct rtpp_node * crt_rtpp; struct rtpp_notify_node *rtpp_lst=NULL; if (!(*rtpp_set_list) || !(*rtpp_set_list)->rset_first) { LM_DBG("null rtpproxy set list\n"); return 0; } for(rtpp_list = (*rtpp_set_list)->rset_first; rtpp_list != NULL; rtpp_list = rtpp_list->rset_next) { for(crt_rtpp = rtpp_list->rn_first; crt_rtpp != NULL; crt_rtpp = crt_rtpp->rn_next) { /* if it is an unix sock - don't put it in the list */ if (!crt_rtpp->rn_umode) continue; rtpp_lst = new_rtpp_notify_node(crt_rtpp); if (!rtpp_lst) { LM_ERR("cannot add rtpproxy to list\n"); return -1; } rtpp_lst->next = rtpp_notify_h->rtpp_list; rtpp_notify_h->rtpp_list = rtpp_lst; } } return 0; } int compare_rtpp(struct rtpp_node *r_node, struct rtpp_notify_node *n_node) { char buffer[BUF_LEN]; char *p; struct hostent *rtpp_server; if (r_node->rn_umode != n_node->mode) return 0; memcpy(buffer,r_node->rn_address,strlen(r_node->rn_address)); p = strrchr(buffer, ':'); if (!p) { LM_ERR("invalid address %s\n", r_node->rn_address); return 0; } *p = 0; rtpp_server = resolvehost(buffer, 0); if (!rtpp_server || !rtpp_server->h_addr) { LM_ERR("cannot resolve hostname %s\n", r_node->rn_address); return 0; } if (memcmp(n_node->addr, rtpp_server->h_addr, rtpp_server->h_length)!= 0) return 0; return 1; } void update_rtpproxy_list(void) { struct rtpp_set * rtpp_list; struct rtpp_node * crt_rtpp; struct rtpp_notify_node *rtpp_lst, *r_prev, *rl; if (!rtpp_set_list || !(*rtpp_set_list)) { LM_DBG("no rtpproxy set\n"); return; } LM_DBG("updating rtppproxy list\n"); /* add new rtppproxies */ for(rtpp_list = (*rtpp_set_list)->rset_first; rtpp_list != NULL; rtpp_list = rtpp_list->rset_next) { for(crt_rtpp = rtpp_list->rn_first; crt_rtpp != NULL; crt_rtpp = crt_rtpp->rn_next) { /* if it is an unix sock - don't do anything */ if (!crt_rtpp->rn_umode) continue; /* search if it already exists */ for (rtpp_lst=rtpp_notify_h->rtpp_list; rtpp_lst; rtpp_lst=rtpp_lst->next) if (compare_rtpp(crt_rtpp, rtpp_lst)) break; if (!rtpp_lst) { /* if it doesn't exist add a new one */ rtpp_lst = new_rtpp_notify_node(crt_rtpp); if (!rtpp_lst) { LM_ERR("cannot add rtpproxy to list\n"); return; } rtpp_lst->next = rtpp_notify_h->rtpp_list; rtpp_notify_h->rtpp_list = rtpp_lst; } } } /* search for deleted rtpproxies */ r_prev = NULL; rtpp_lst=rtpp_notify_h->rtpp_list; while (rtpp_lst) { /* don't update for unix sockets */ if (rtpp_lst->mode == 0) goto loop; for(rtpp_list = (*rtpp_set_list)->rset_first; rtpp_list != NULL; rtpp_list = rtpp_list->rset_next) { for(crt_rtpp = rtpp_list->rn_first; crt_rtpp != NULL; crt_rtpp = crt_rtpp->rn_next) { /* if not the same type */ if (crt_rtpp->rn_umode != rtpp_lst->mode) continue; if (compare_rtpp(crt_rtpp, rtpp_lst)) goto loop; } } /* if it gets here it means we couldn't find the old rtpproxy */ LM_DBG("removing rtpproxy %s\n", inet_ntoa(*(struct in_addr*)rtpp_lst->addr)); /* remove fd from poll vector */ if (rtpp_lst->index) { if (pfds[rtpp_lst->index].revents & POLLIN) nr_events--; nfds--; if (nfds != rtpp_lst->index) { pfds[rtpp_lst->index].fd = pfds[nfds].fd; pfds[rtpp_lst->index].revents = pfds[nfds].revents; for (rl=rtpp_notify_h->rtpp_list; rl; rl=rl->next) if (rl->index == nfds) break; if (!rtpp_lst) { LM_ERR("BUG - rtpproxy index mismatch\n"); return; } rl->index = rtpp_lst->index; } /* close connection */ shutdown(rtpp_lst->fd, SHUT_RDWR); close(rtpp_lst->fd); } /* remove it from the list */ if (!r_prev) rtpp_notify_h->rtpp_list = rtpp_lst->next; else r_prev->next = rtpp_lst->next; shm_free(rtpp_lst); /* r_prev remains the same */ rtpp_lst = r_prev ? r_prev->next : rtpp_notify_h->rtpp_list; continue; loop: r_prev = rtpp_lst; rtpp_lst = rtpp_lst->next; } } opensips-2.2.2/modules/script_helper/000077500000000000000000000000001300170765700176765ustar00rootroot00000000000000opensips-2.2.2/modules/script_helper/Makefile000066400000000000000000000002721300170765700213370ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=script_helper.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/script_helper/README000066400000000000000000000061701300170765700205620ustar00rootroot00000000000000Script Helper Module Liviu Chircu OpenSIPS Solutions Edited by Liviu Chircu Copyright © 2014 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How it works 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. use_dialog (integer) 1.4.2. create_dialog_flags (string) 1.4.3. sequential_route (string) 1.5. Known Issues List of Examples 1.1. Setting use_dialog 1.2. Setting create_dialog_flags 1.3. Setting sequential_route Chapter 1. Admin Guide 1.1. Overview The purpose of the Script Helper module is to simplify the scripting process in OpenSIPS when doing basic scenarios. At the same time, it is useful to script writers as it contains basic SIP routing logic, and thus it allows them to focus more on the particular aspects of their OpenSIPS routing code. 1.2. How it works By simply loading the module, the following default logic will be embedded: * for initial SIP requests, the module will perform record routing before running the main request route * sequential SIP requests will be transparently handled - the module will perform loose routing, and the request route will not be run at all Currently, the module may be further configured to embed the following optional logic: * dialog support (dialog module dependency - must be loaded before this module) * an additional route to be run before relaying sequential requests 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * dialog (only if use_dialog is enabled). 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.4. Exported Parameters 1.4.1. use_dialog (integer) Enables dialog support. Note that the dialog module must be loaded before this module when setting this parameter. Default value is 0 (disabled) Example 1.1. Setting use_dialog ... modparam("script_helper", "use_dialog", 1) ... 1.4.2. create_dialog_flags (string) Flags used when creating dialogs. For details on these flags, please refer to the create_dialog() function of the dialog module. Default value is "" (no flags are set) Example 1.2. Setting create_dialog_flags ... modparam("script_helper", "create_dialog_flags", "PpB") ... 1.4.3. sequential_route (string) Optional route to be run just before sequential requests are relayed. If the exit script statement is used inside this route, the module assumes that the relaying logic has been handled. By default, this parameter is not set Example 1.3. Setting sequential_route ... modparam("script_helper", "sequential_route", "sequential_handling") ... route [sequential_handling] { ... } ... 1.5. Known Issues The Max-Forwards header is currently not handled at all. opensips-2.2.2/modules/script_helper/doc/000077500000000000000000000000001300170765700204435ustar00rootroot00000000000000opensips-2.2.2/modules/script_helper/doc/script_helper.xml000066400000000000000000000017471300170765700240410ustar00rootroot00000000000000 %docentities; ]> Script Helper Module &osipsname; Liviu Chircu OpenSIPS Solutions
liviu@opensips.org
Liviu Chircu
liviu@opensips.org
2014 &osipssol;
&admin;
opensips-2.2.2/modules/script_helper/doc/script_helper_admin.xml000066400000000000000000000101661300170765700252040ustar00rootroot00000000000000 &adminguide;
Overview The purpose of the Script Helper module is to simplify the scripting process in OpenSIPS when doing basic scenarios. At the same time, it is useful to script writers as it contains basic SIP routing logic, and thus it allows them to focus more on the particular aspects of their OpenSIPS routing code.
How it works By simply loading the module, the following default logic will be embedded: for initial SIP requests, the module will perform record routing before running the main request route sequential SIP requests will be transparently handled - the module will perform loose routing, and the request route will not be run at all Currently, the module may be further configured to embed the following optional logic: dialog support (dialog module dependency - must be loaded before this module) an additional route to be run before relaying sequential requests
Dependencies
&osips; Modules The following modules must be loaded before this module: dialog (only if is enabled).
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>use_dialog</varname> (integer) Enables dialog support. Note that the dialog module must be loaded before this module when setting this parameter. Default value is 0 (disabled) Setting <varname>use_dialog</varname> ... modparam("script_helper", "use_dialog", 1) ...
<varname>create_dialog_flags</varname> (string) Flags used when creating dialogs. For details on these flags, please refer to the create_dialog() function of the dialog module. Default value is "" (no flags are set) Setting <varname>create_dialog_flags</varname> ... modparam("script_helper", "create_dialog_flags", "PpB") ...
<varname>sequential_route</varname> (string) Optional route to be run just before sequential requests are relayed. If the exit script statement is used inside this route, the module assumes that the relaying logic has been handled. By default, this parameter is not set Setting <varname>sequential_route</varname> ... modparam("script_helper", "sequential_route", "sequential_handling") ... route [sequential_handling] { ... } ...
Known Issues The Max-Forwards header is currently not handled at all.
opensips-2.2.2/modules/script_helper/script_helper.c000066400000000000000000000137501300170765700227130ustar00rootroot00000000000000/** * script_helper module - embedded scripting logic * > record routing * > dialog creation, matching and message validation * > sequential request routing * * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-03-01 initial version (liviu) */ #include #include "../../sr_module.h" #include "../../route.h" #include "../../script_cb.h" #include "../tm/tm_load.h" #include "../dialog/dlg_load.h" #include "../sl/sl_api.h" #include "../rr/api.h" static int use_dialog; static int create_dialog_flags; static char *seq_route; static int seq_route_id; struct tm_binds tm_api; struct dlg_binds dlg_api; struct rr_binds rr_api; struct sl_binds sl_api; int run_helper_logic(struct sip_msg *msg, void *param); int parse_dlg_flags(modparam_t type, void *val); int mod_init(void); static cmd_export_t cmds[] = { { NULL, NULL, 0, NULL, NULL, 0 }, }; static param_export_t params[] = { { "sequential_route", STR_PARAM, &seq_route }, { "use_dialog", INT_PARAM, &use_dialog }, { "create_dialog_flags", STR_PARAM|USE_FUNC_PARAM, parse_dlg_flags }, { NULL, 0, NULL }, }; static module_dependency_t *get_deps_use_dialog(param_export_t *param) { if (*(int *)param->param_pointer == 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "dialog", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "rr", DEP_ABORT }, { MOD_TYPE_DEFAULT, "sl", DEP_ABORT }, { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { "use_dialog", get_deps_use_dialog }, { NULL, NULL }, }, }; struct module_exports exports = { "script_helper", MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, &deps, /* OpenSIPS module dependencies */ cmds, NULL, params, NULL, NULL, NULL, NULL, mod_init, NULL, NULL, NULL, }; int mod_init(void) { LM_DBG("initializing module...\n"); if (seq_route) { seq_route_id = get_script_route_ID_by_name(seq_route, rlist, RT_NO); if (seq_route_id == -1) LM_ERR("route \"%s\" does not exist! ignoring\n", seq_route); } if (load_tm_api(&tm_api) != 0) { LM_ERR("failed to load tm API\n"); return -1; } if (use_dialog && load_dlg_api(&dlg_api) != 0) { LM_ERR("failed to load dialog API\n"); return -1; } if (load_rr_api(&rr_api) != 0) { LM_ERR("failed to load rr API\n"); return -1; } if (load_sl_api(&sl_api) != 0) { LM_ERR("failed to load sl API\n"); return -1; } if (__register_script_cb(run_helper_logic, PRE_SCRIPT_CB|REQ_TYPE_CB, NULL, -1) != 0) { LM_ERR("cannot register script callback"); return -1; } return 0; } int run_helper_logic(struct sip_msg *msg, void *param) { str totag; str status_404 = str_init("Not Here"); str status_500 = str_init("Server Internal Error"); int rc, seq_request = 0; LM_DBG("running script helper for <%.*s>\n", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s); if (parse_headers(msg, HDR_TO_F|HDR_CALLID_F, 0) == -1) { LM_ERR("failed to parse To header\n"); return SCB_DROP_MSG; } totag = get_to(msg)->tag_value; /* sequential request */ if (totag.s && totag.len > 0) { seq_request = 1; if (msg->REQ_METHOD == METHOD_INVITE) rr_api.record_route(msg, NULL); /* if not RR_DRIVEN */ if (rr_api.loose_route(msg) < 0) { /* attempt a full dialog search (not the usual quick did lookup) */ if (use_dialog && dlg_api.match_dialog(msg) < 0) LM_DBG("failed to match dialog for <%.*s>, ci '%.*s'\n", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s, msg->callid->body.len, msg->callid->body.s); if (msg->REQ_METHOD == METHOD_ACK) { rc = tm_api.t_check_trans(msg, NULL, NULL, NULL, NULL, NULL, NULL); if (rc > 0) tm_api.t_relay(msg, NULL, NULL, NULL, NULL, NULL, NULL); return SCB_RUN_POST_CBS; } sl_api.reply(msg, 404, &status_404); return SCB_RUN_POST_CBS; } } if (msg->REQ_METHOD == METHOD_CANCEL) { seq_request = 1; rc = tm_api.t_check_trans(msg, NULL, NULL, NULL, NULL, NULL, NULL); if (rc > 0) tm_api.t_relay(msg, NULL, NULL, NULL, NULL, NULL, NULL); return SCB_RUN_POST_CBS; } if (tm_api.t_check_trans(msg, NULL, NULL, NULL, NULL, NULL, NULL) == 0) return SCB_RUN_POST_CBS; /** * for sequential requests: * - optionally run a given route * - relay them and do not trigger the request route at all */ if (seq_request) { if (seq_route_id > 0) { LM_DBG("running seq route '%s'\n", seq_route); if (run_top_route(rlist[seq_route_id].a, msg) & ACT_FL_DROP) { LM_DBG("script exited in the seq route\n"); return SCB_RUN_POST_CBS; } } if (tm_api.t_relay(msg, NULL, NULL, NULL, NULL, NULL, NULL) < 0) sl_api.reply(msg, 500, &status_500); return SCB_RUN_POST_CBS; } /* record-routing for initial requests */ if (!(msg->REQ_METHOD & (METHOD_REGISTER|METHOD_MESSAGE))) rr_api.record_route(msg, NULL); if (use_dialog && msg->REQ_METHOD & METHOD_INVITE) dlg_api.create_dlg(msg, create_dialog_flags); return SCB_RUN_ALL; } int parse_dlg_flags(modparam_t type, void *val) { str input; input.s = val; input.len = strlen(val); create_dialog_flags = parse_create_dlg_flags(input); return 1; } opensips-2.2.2/modules/seas/000077500000000000000000000000001300170765700157665ustar00rootroot00000000000000opensips-2.2.2/modules/seas/Makefile000066400000000000000000000003171300170765700174270ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=seas.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/seas/README000066400000000000000000001617171300170765700166630ustar00rootroot00000000000000Seas Module Elias Baixas VozTelecom Sistemas www.wesip.eu Ronda Can Fatjo, 9, 1p Parc Tecnologic del Valles Cerdanyola, 0 8520 (SPAIN) Phone:+34 933968800 www.voztele.com Copyright © 2006 VozTelecom Sistemas __________________________________________________________ Table of Contents 1. The Sip Express Application Server User's Guide 1.1. Application Servers 1.1.1. Sip Express Application Server module overview 1.1.2. Application Servers 1.1.3. Dependencies 1.1.4. Exported Parameters 1.1.5. Exported Functions 1.2. WeSIP Application Server 1.2.1. The Servlet programming paradigm: Sip/Http Servlets 1.2.2. Configuring WeSIP to work with SEAS 1.2.3. Configuration Examples 2. Developer Guide 2.1. Internals 2.2. SEAS Protocol 2.2.1. The SEAS protocol 2.2.2. General codification of a header List of Figures 1.1. SipServlet UML diagram 2.1. Overview of Seas Event Dispatcher process operation 2.2. SIP Messages and control flow within SER 2.3. General codification of a SIP header in SEAS protocol 2.4. Example of a from header SEAS-protocol codification 2.5. SEAS-codification of a SIP URI (byte meanings are shown) 2.6. Example of a SEAS SIP URI codification 2.7. SEAS codification of From and To headers 2.8. SEAS codification of a Contact header 2.9. SEAS codification of a Route Header 2.10. SEAS codification of Authentication/Authorization headers 2.11. SEAS codification of a SIP First Line 2.12. SEAS Headers Index section overview 2.13. SEAS SIP-Message codification 2.14. Different kinds of SEAS codified Events and Actions List of Examples 1.1. Set listen_sockets parameter 1.2. as_relay_t usage 1.3. Typical example of an HttpServlet 1.4. Typical Sip Servlet Example 1.5. Server 1.6. Service Chapter 1. The Sip Express Application Server User's Guide 1.1. Application Servers 1.1.1. Sip Express Application Server module overview SEAS module enables OpenSIPS to transfer the execution logic control of a sip message to a given external entity, called the Application Server. When the OpenSIPS script is being executed on an incoming SIP message, invocation of the as_relay_t() function makes this module send the message along with some transaction information to the specified Application Server. The Application Server then executes some call-control logic code, and tells OpenSIPS to take some actions, ie. forward the message downstream, or respond to the message with a SIP repy, etc. The module acts implements a network protocol acting as the interface between OpenSIPS internal API and the external Application Server entity. There's only one relevant function, as_relay_t, exported by this module. This function receives as a parameter the name of the application server to which the message should be relaied. Every message relaied to an Application Server is automatically associated to a SIP transaction (a transaction is created for it). Just after the message is relaied to the Application Server, the script stops its execution on the message, because the control of message-processing is now in the Application Server. In the context of SEAS module, relaying a message to an App Server, is _not_ done in SIP protocol, but in a special protocol by means of which the SEAS module and the Application Server comunicate efficiently and seamlessly. This procotol is specially designed so that a message doesn't need to be parsed again once it arrives at the Application Server. This protocol carries information regarding the internal structure of the SIP message (to avoid reparsing) and also information about the associated transaction (recall that invoking as_relay_t indirectly calls t_newtran). This way, all the SIP-Transaction machinery, and the SIP-Message parsing, is handled at the OpenSIPS core, while the execution of the Application Logic is carried in the Application Server. The SEAS module and protocol provide a means by which an external entity can utilize OpenSIPS as a transaction-stateful SIP-stack to act on behalf of it. This means that this external entity (which we call the Application Server) is notified whenever a SIP-Request enters OpenSIPS, and this external entity can then order OpenSIPS to execute some actions, either replying the request, or generating new UAC transactions. This version of SEAS works with VozTelecom's WeSIP Application Server. This Application Server is a SipServlet JAVA Container. 1.1.2. Application Servers When OpenSIPS starts and SEAS module is loaded, a new process is spawn which listens on a server-socket (IP and port are specified as a parameter in the config script). From then on, the Application Servers can connect to that socket so that OpenSIPS can relay messages to them. When an Application Server connects to the socket, it sends its name through the socket, so every App Server is identified with a name. Within the OpenSIPS script, invoking as_relay_t() receives a string as a parameter, which specifies the name of an application server to which the message has to be sent. If that concrete application server hasn't already connected to the module, the function returns a negative value, otherwise (the Application Server is connected), the message is relaied to it. 1.1.3. Dependencies 1.1.3.1. OpenSIPS Modules SEAS module relies on the Transaction Module (TM module) for operation. 1.1.3.2. External Applications Using the SEAS module requires to have an Application Server running and connected to a particular instance of OpenSIPS. At the moment, the only Application Server that works with SEAS is WeSIP Application Server, which can be downloaded from www.wesip.eu, and used freely for non-comercial purposes. 1.1.4. Exported Parameters 1.1.4.1. listen_sockets (string) The listen_sockets string tells SEAS where to listen for incoming connections of Application Servers. It has the form: "ip:port". SEAS will open two server-sockets on that IP, at the specified port, and another at port+1. Application Servers must be configured to connect to that port. In case this parameter is ommited, SEAS listens on the default IP which OpenSIPS is using, and opens the ports 5080 and 5081 to listen for Application Servers. Example 1.1. Set listen_sockets parameter ... modparam("seas", "listen_sockets","127.0.0.1:5080") ... 1.1.5. Exported Functions 1.1.5.1. as_relay_t(String name) Creates a new transaction (if it isn't already created) and sends the SIP Request and transaction information to the Application Server specified in the parameter. Every Application Server connected to OpenSIPS through the SEAS module, must be identified with a different name. This function can be used within REQUEST_ROUTE. Example 1.2. as_relay_t usage ... if (!as_relay_t("app_server_1")) { log("Error sending to app server"); t_reply("500","App Server not connected"); } ... 1.1.5.1.1. Return value In case the Application Server is connected to OpenSIPS, the function does _not_ return, the Application Server is now in charge of processing the request, and it may then reply to the request, initiate new transactions, or whatever the application being executed wants. In case the Application Server identified by the string parameter passed to as_relay_t() is not connected to OpenSIPS, the function returns 0, so that the script can continue processing the request. 1.2. WeSIP Application Server At the moment, the only Application Server known to work with SEAS is WeSIP. You can download a copy from www.wesip.eu. WeSIP is a converged Sip/Http Servlet Container. 1.2.1. The Servlet programming paradigm: Sip/Http Servlets Servlets are pieces of code that encapsulate the logic of an application. Servlets are deployed into an Application Server. Whenever a user requests service, the Application Server processes the request, and passes control to the servlet. The servlet then executes some logic, may it be a query to a database, the execution of a business process, the creation of customized content for the user, or whatever the service programmer could imagine. When the servlet finishes the execution, it creates a response and gives it back to the Application Server, which is in charge of making it reach back to the user. The Application Server implements the network protocol, it takes care of everything needed for a proper communication between user and server, so the servlet doesn’t have to care about these things. The servlet uses a set of resources from the Application Server, such as Session management, service routing or chaining, and request/response header composition. In HttpServlets, a service programmer has to implement a method in a JAVA class, which could be called doGet() or doPost(). Whenever an HTTP request arrived at the server, one of these functions was called with the request as a parameter, so the logic of the application was executed over that particular request. HttpServlet has been extensively used over the past years, in all kinds of business and web services. This is how a typical HttpServlet looks like: Example 1.3. Typical example of an HttpServlet public final class Hello extends HttpServlet { protected void doGet(HttpServletRequest request,HttpServletResponse resp onse) throws IOException, ServletException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println(""); writer.println(""); writer.println("Sample Application Servlet"); writer.println(""); writer.println(""); writer.println(""); Enumeration names = request.getHeaderNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); writer.println(""); writer.println(""); writer.println(""); writer.println(""); } writer.println("
"+name+":"+request.getHeader(name)+"
"); writer.println(""); writer.println(""); } } The successor of HttpServlet for SIP networks, is the SipServlet API. Making most of the success of HttpServlet, the SipServlet API follows the same programming paradigm, so that SIP application programmers can reuse their knowledge in the field. SipServlet API works the same way as HttpServlet: an Application Server implements a SIP Stack and executes all the complex protocol logic. It receives and pre-processes the requests from the network, and at the right moment, passes control to the servlet doXxx() method, where the programmer implemented the application logic. Depending on what kind of SIP Message it was, a method or another will be executed. For example, if an INVITE is received, the doInvite() method will be invoked in the servlet. The application can then access all the parts of the request and do its work. When the service has been executed, it passes control back to the Application Server with a response, so that it can be forwarded to the user, and the service be satisfied. Sip Servlets can be used to implement basic SIP network functionalities (such as Proxy or Registrar servers), but their true power emerges in the implementation of value-added services, which greatly surpasses the basic service functionality of plain SIP servers. Examples of value-added services, are Virtual PBX or IPCentrex, Attended call forwarding, Instant Messaging, etc. This is the appearance a typical SipServlet: Example 1.4. Typical Sip Servlet Example public class ProxyServlet extends SipServlet { protected void doInvite(SipServletRequest req) throws ServletException, IOException { if (req.isInitial()) { Proxy proxy = req.getProxy(); proxy.setRecordRoute(false); proxy.setParallel(parallel); proxy.setSupervised(supervised); SipURI rrURI = proxy.getRecordRouteURI(); rrURI.setParameter("foo", "bar"); req.setContent("Method is INVITE", "text/plain"); proxy.proxyTo(uris); } else { log("re-INVITE"); } } protected void doAck(SipServletRequest req) throws ServletException, IOException { log("doAck " + req.getRequestURI()); if (req.isInitial()) { throw new ServletException("unexpectedly got initial ACK"); The servlet programming API is event-ridden: every time a request comes into the Application Server (may it be an Http or SIP one), the specific servlet is executed and the service provided within it. It is a very straightforward way of programming services, and the Servlet API provides very easy and powerful means to access information about the SIP-session or Http-session, about the request or response, about the state of the dialog, or whatever it is needed. The application programmer has a rich framework of resources that allow him to focus only on the service logic, without having to worry about the underlying protocol specifics (SIP or HTTP). Figure 1.1. SipServlet UML diagram SipServlet UML diagram The Servlet programming language is JAVA, which offers a wide spectrum of programming API’s dealing with all kinds of techniques, tools and resources, which also are available seamlessly from the Servlet context. This makes the SipServlet API very desirable for all kinds application developers. SipServlet allows a rapid SIP application development and deployment, and also provides a reliable and secure framework of service execution (the JAVA sandbox and the Application Server execution environment). 1.2.1.1. Converged Http/Sip Servlet Containers SipServlets achieve the most of it when they can be deployed along with HttpServlets, in the same Application Server (also known as Servlet Container). This environment truly realizes the power of converged voice/data networks: Http protocol represents one of the most powerful data transmission protocols used in modern networks (think of the SOAP web-services protocol), and SIP is the protocol of choice in most of the modern and future voice over IP (VoIP) networks for the signaling part. So an Application Server capable of combining and leveraging the power of these two APIs will be the most successful. Convergence of SIP and HTTP protocols into the same Application Server offers, amongst others, the following key advantages: -It doesn’t require to have 2 different servers (Http and Sip) so it relieves from maintenance problems, and eases user and configuration provisioning. -It offers great convenience to the application programmer to have all the classes related to the different protocols handled within the same code. -As it eases development of interactive and multimedia services, realizing the power of well-known web-services and intermixing them with new voice services. These are some simple, but suggestive examples of services that could be developed within a converged Http/Sip servlet: -IP Centrex: through the use of the Web-interface, users could have a layout of the office in a web page, and see what phones were ringing at a given moment, so they could pick-up a call ringing in another phone in their own desktop. Or they could forward a call to another party by clicking on the web page and selecting which of the office phones it had to be transferred to. -Voicemail: users could upload an audio file to the server through a web-page, to be used as the automatic answering message, and then also download their voicemail through the web-page, or organize the messages and remove the old ones. -Instant Messaging: users could continue a voice call by starting or joining a new Instant Messaging session carried over a web-page. -Click-to-dial: users could initiate SIP sessions only by clicking a link on a web page, without the need of the Web-Browser being SIP-aware nor needing even a SIP phone: the server could handle all the logic so the user who clicked could receive a call from the server’s SIP network. 1.2.2. Configuring WeSIP to work with SEAS The WeSIP Application Server configuration file is based on the Apache Tomcat configuration system: It is an XML-formatted file, in which the different components of the server are specified. The default config file that comes with the WeSIP distribution package should be suitable for most of the deployment configurations. 1.2.2.1. Server The topmost element in the XML configuration file is the "server" which has 2 xml attributes, called "port" and "shutdown". The former specifies a port on which the WeSIP AS will listen for the shutdown command, and the latter is the magic word that will make the server shutdown. Example 1.5. Server if you send the magic word "SHUTDOWN" to the port 8005 of the localhost, the server will stop cleanly. 1.2.2.2. Service Nested within the Server element, must be a "Service" element, with an attribute called "name" which specifies the name for the service. This attribute is not very relevant, you can call it whatever you like. Example 1.6. Service Within the Service element must be two or more elements: the connectors and the engines. A connector is the instance that will receive messages from the network. You can specify HTTP connectors and/or SIP connectors. Every connector needs an attribute called "className" which specifies which class will be responsible for receiving the messages from the network. For HTTP connectors, the classname must be "org.apache.catalina.connector.http.HttpConnector" and for SIP connectors "com.voztele.sipservlet.connector.SipConnector". 1.2.2.3. Connector The SIP Connector uses 4 attributes: className="com.voztele.sipservlet.connector.SipConnector" specifies the classname of the connector. minProcessors="5" specifies the minimum number of SIPprocessor instances (and threads in the pool) to process incoming SIP messages. More processors should allow more load to be processed. This is the minimum number of instances, even if they are spare and not working. maxProcessors="10" specifies the maximum number of SIP processors used (a negative value specifies that there is no limit). addresses="localhost:5060" Specifies the SIP address and port in which the Application Server from which the Application Server will process the SIP messages. This Addres is where OpenSIPS listens for the messages, so in fact, OpenSIPS is listening on them, but OpenSIPS passes the messages to WeSIP, so WeSIP must be aware of this IP/port. Warning this attribute MUST match one of the listening points declared within OpenSIPS in the "listen" parameters. For example in opensips.cfg: listen = tcp:localhost:5060 listen = udp:localhost:5060 Within the SIP Connector element there must be an ExtraProperties element, containing nestes Property elements. Each property element specifies a parameter for the SIP Stack. Each property is specified by a key and a value. The most significant keys are: * com.voztele.javax.sip.SER_ADDRESS This specifies the IP and port in which the OpenSIPS is listening for Application Servers to connect and register.This specifies the IP and port in which the OpenSIPS is listening for Application Servers to connect and register. Warning This needs to match the listen_sockets seas module parameter within the OpenSIPS configuration file. Ie.: modparam("seas", "listen_sockets","127.0.0.1:5080") * javax.sip.STACK_NAME Specifies the name identifying this instance of the Application Server. Warning This is the name you will set in the OpenSIPS configuration script when you invoke the WeSIP Application Server, by calling the as_relay_t function. This is the name you pass as the parameter of the function. If you have different WeSIP instances all connecting to the same OpenSIPS, they must each one have a different STACK_NAME", and within OpenSIPS you can call each of them by invoking as_relay_t() with a different name. Example: * com.voztele.javax.sip.THREAD_POOL_SIZE (integer) Specifies the number of threads there must be in the pool to process incoming SIP messages. If unspecificed, the default is "infinity". * com.voztele.javax.sip.SPIRAL_HDR This property tells WeSIP and SEAS that every SipRequest and UAC transaction generated from WeSIP, must spiral through SER, and will be added a special Header called "X-WeSIP-SPIRAL: true" this will make all the outgoing messages pass again through the OpenSIPS script, so that they can be accounted or whatever the configurator wants. For example, the configuration script could go: route{ if(is_present_hf("X-WeSIP-SPIRAL")){ /* account, log, register, or whatever */ t_relay(); }else{ as_relay_t("app_server_1"); } } 1.2.2.4. Engine The Engine must also be nested within the Server element, along with the Connectors. It must have a "name" attribute with whatever name you feel like. It needs to have another attribute called "defaultHost" which will be the default host to which to pass the incoming request (in HTTP/1.0 the requests dont have a Host header, so they will be passed to this default host, in SIP, this attribute doesn't have a meaning.). In order to have this Engine handling also SIP messages, the "className" attribute of the Engine must be "com.voztele.sipservlet.core.ConvergedEngine". Within the Engine, there can be one or more Hosts, each one specified within a "Host" element nested in the engine. 1.2.2.5. Mapper A mapper is used to map an incoming request to one or another SIP or HTTP host. In case it is a SIP request, the mapping is done based on the sip.xml deployment descriptor rules. The classname of the SIP mapper MUST BE "com.voztele.sipservlet.core.EngineSipMapper". The "mapper" element must also have a "protocol" attribute, specifying which protocol this mapper handles. In case of the SIP mapper it must be "SIP/2.0". The HTTP mapper's classname must be "org.apache.catalina.core.StandardEngineMapper" and the protocol attribute "HTTP/1.1" 1.2.2.6. Realm The authentication in HTTP is performed in Apache-Tomcat through Realms. The memory realm is (textual copy from the Apache-Tomcat javadoc"): "Simple implementation of Realm that reads an XML file to configure the valid users, passwords, and roles." The classname must be "org.apache.catalina.realm.MemoryRealm" A "pathname" attribute can be specified to tell the Realm which file contains the usernames, passwords and roles. If not specified, it is "conf/wesip-users.xml" 1.2.2.7. Host A Host represents a VirtualHost in HTTP/1.1 servers, so the requests will be dispatched to one or another virtual host depending on the Host: header. In SIP this doesn't make much sense, because there's no such Host: header, and virtual hosting is not done in this way. Every host must have a "name" attribute which specifies the name of the virtual host, it must also have a "nameSip" attribute which MUST MATCH the IP or hostname _and_ port" specified in OpenSIPS listen parameters and in the Sip Connector the hostname and the port must be separated with an underscore. for example: nameSip="localhost_5060" or nameSip="192.168.1.1_5060" The next important attribute that must have the Host element is "appBase" which declares the directory where the WEB and SIP applications reside. It usually is a directory called apps in the directory from which the server runs. The attribute "unpackWARs" says the WeSIP Application Server to unpack the Web or Sip Application Archives (.war or .sar extensions) found inside the appBase directory. It should usually be set to "true". The "port" attribute specifies the port where this host is going to receive SIP messages . This only has to do with the SIP protocol, not with HTTP. It must be the same as the port specified in OpenSIPS parameter "listen_sockets" (for the seas module). The "autoDeploy" attribute tells the host to monitor the "appBase" directory for new application archives (.sar or .war) so they can automatically be deployed. This parameter should be set to "true". The "className" used for the Host _must_be_ "com.voztele.sipservlet.core.ConvergedHost" 1.2.2.8. Mapper Hosts must also have a nested Mapper element, but when the mapper is inside a Host (and not in an Engine) the classnames must be "com.voztele.sipservlet.core.SipHostMapper" for the "SIP/2.0" protocol and "org.apache.catalina.core.HttpHostMapper" for the "HTTP/1.1" protocol. (2 mappers must be nested inside the Host). 1.2.3. Configuration Examples In general, you can configure WeSIP to work with your OpenSIPS in two ways: have 2 OpenSIPS instances, the first acting as Proxy/Registrar/Redirect and the second cooperating with WeSIP to act as the Application Server. This is the preferred deployment layout, as the first OpenSIPS works as usual, and the requests that need special services are relaied to another OpenSIPS which acts on behalf of the WeSIP AS. This configuration profile distributes load (call-routing logic in one instance, and Application Services in the other), and is also more fault-tolerant. On the other hand, you can have all your call-routing logic and Application Server on the same OpenSIPS, having one script handle all the logic, and then invoking the App Server at any point. 1.2.3.1. opensips.cfg in standalone log_level=3 # debug level (cmd line: -dddddddddd) log_stderror=no # (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 loadmodule "/usr/local/lib/opensips/modules/sl.so" loadmodule "/usr/local/lib/opensips/modules/tm.so" loadmodule "/usr/local/lib/opensips/modules/rr.so" loadmodule "/usr/local/lib/opensips/modules/maxfwd.so" loadmodule "/usr/local/lib/opensips/modules/usrloc.so" loadmodule "/usr/local/lib/opensips/modules/registrar.so" loadmodule "/usr/local/lib/opensips/modules/textops.so" loadmodule "/usr/local/lib/opensips/modules/seas.so" loadmodule "/usr/local/lib/opensips/modules/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("usrloc", "db_mode", 0) modparam("seas", "listen_sockets", "127.0.0.1:5080"); route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; if (!method=="REGISTER") record_route(); if (loose_route()) { append_hf("P-hint: rr-enforced\r\n"); route(1); }; if (uri==myself) { if (method=="REGISTER") { save("location"); exit; }; lookup("aliases"); if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); }; if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; append_hf("P-hint: usrloc applied\r\n"); }; route(1); } route[1] { if(!as_relay_t("app_server_one")){ t_reply("500","Application Server error"); } } 1.2.3.2. opensips.cfg working as WeSIP front-end log_level=9 # debug level (cmd line: -dddddddddd) log_stderror=yes # (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 listen = tcp:localhost:5060 listen = udp:localhost:5060 mpath="/home/elias/src/sipservlet/seas" loadmodule "modules/tm/tm.so" loadmodule "modules/seas/seas.so" loadmodule "modules/mi_fifo/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("seas", "listen_sockets","127.0.0.1:5080") route{ if(!as_relay_t("app_server_1")){ t_reply("500","Application Server error"); } } 1.2.3.3. Server.xml Chapter 2. Developer Guide 2.1. Internals The SEAS module runs within the Open Sip Express Router aka. OpenSIPS. OpenSIPS uses a pool of processes to execute the script logic on every new message received. These are called the worker processes. One of these processes will be selected to process the script, and at some point it will find a function invoking the relay of the SIP message to one of the Application Servers registered. This function has been called as_relay_t, which stands for Application Server relay (the _t stands for TransactionStatefully), and receives as the only parameter the name of the application server to be invoked. The process will execute the as_relay_t function, which looks up in a table if there is a registered Application Server with that name. If there is one, the process will craft the SEAS header for the SIP message being handled, put it in a shared memory segment, and write the address of that segment to a pipe (4 bytes pointer in IA32). This way, we will have all the OpenSIPS processes composing the SEAS header along with the SIP message, and putting its shared memory address into that pipe. This technique of inter-process communication avoids race conditions because writing to a pipe is granted to be an atomic operation if the data to write is less than _POSIX_PIPE_BUF, which usually is 512 bytes. At the initialization of OpenSIPS, the SEAS module creates the discussed pipe, so that all the OpenSIPS worker processes inherit the file descriptor associated to the pipe. Then it spawns a new process, which will be the one to open two server sockets, and wait for the Application Servers to connect and register. Each Application Server wishing to receive events from OpenSIPS, will have to open a socket to the module (the port and IP of the socket are defined at start time in the script). After connection, it has to print its identification name. The SEAS process (from now on, called event dispatcher) will then register it in its internal structures, so that the OpenSIPS processes can push events for it. The following picture, shows the internals of the SEAS Event dispatcher process: Figure 2.1. Overview of Seas Event Dispatcher process operation Overview of Seas Event Dispatcher process operation Within the SER server, the flowing of SIP Messages and control flow, is depicted in the following diagram: Figure 2.2. SIP Messages and control flow within SER SIP Messages and control flow within SER 2.2. SEAS Protocol SIP is a very flexible protocol. It can be very easily extended with new features, and SIP entities have a high level of freedom in composing the SIP messages, for example setting IPs or hostnames in URIs, reordering header fields, folding headers, aggregating/scattering headers, etc. This flexibility, though, makes it difficult to implement efficiently, because parsing of text headers requires a lot of state. OpenSIPS implements a very efficient parsing mechanism and SIP-transaction machinery. The goal of the SEAS protocol is to keep all this information that has been already extracted at OpenSIPS, so that it can be reused at the Application Server. 2.2.1. The SEAS protocol The SEAS protocol is a layer of information regarding the internal structure of a SIP message that is added whenever SEAS sends a SIP event to the Application Servers. The protocol is used for communication between OpenSIPS and the Application Servers. Once an incoming SIP message has reached the worker process within OpenSIPS, it copies its content into a private memory area (which is, a memory chunk not shared across processes). In this point, the message first line is parsed to know whether it is a SIP request or response. OpenSIPS uses a technique called lazy-parsing, which consists in delaying the parse of headers until some piece of the code requires it. As the SIP message goes traversing functions and the script code, a function called parse_msg() gets called again and again, and the SIP message gets parsed further and further. Each call to parse_msg passes an integer value argument (32 bits) in which every bit signals a header to be parsed, if they are already parsed (because a previous invocation of parse_msg), the function returns immediately, otherwise, the SIP message is scanned and parsed until all the headers requested get parsed. In each call to parse_msg, different parts of the message are analyzed, and different SIP header-specific structures get filled. Each one of this structures, give quick access to each of the parts of a SIP message header. For example, a Via header struct is called via_body, and has these members: name, version, transport, host, proto, port, port_str, params, comment, received, rport, etc. each of these members gives quick access to each of the parts of the header. For example, a via header like this: “Via: SIP/2.0/UDP 192.168.1.64:5070;branch=z9hG4bK-c02c60cc†would have the member proto pointing to the “U†of “UDPâ€, and a length of 3, the host member would be pointing to “192.168.1.64†and have a length of 12, the branch member would be pointing to “z9hG4bK-c02c60cc†and a length of 16, and so on. This structure is the result of the parsing. All this meta-information regarding the SIP message structure, is stored in a sip_msg structure, using dynamically-allocated memory segments. OpenSIPS defines different structure types describing different SIP headers, such as via_body, to_body, cseq_body, via_param, and so on. These structures are generally composed of another kind of structure called str. The str structure is a key component of OpenSIPS's high performance. In the C programming language, a string's length is known because a '0' (null-character) is found at the end of it. This forces each of the string manipulation functions to keep looking for a '0' in the byte stream, which is quite processor consuming. Instead of this, OpenSIPS defines a structure composed of a char pointer and an integer. The char points to the start of a string, and the integer gives its length, thus avoiding the '0' lookup problem, and giving a significant performance boost. This structure has been quite useful to the design of the SEAS protocol, because it enables the description of the SIP message anatomy by giving pointers to each of its fields, and integers describing each of its lengths. Knowing that a SIP header does not usually occupy more than a few characters (always less than 256), the pointer in the structure has been relativized to the beginning of the SIP message or the beginning of the SIP header, and the integer giving the length, has been casted to an unsigned byte (256 values, so 256 characters maximum length). When messages get transferred from OpenSIPS to the Application Server, it is optimum to keep this worthy meta-information regarding the SIP message, so that it can be used at the AS part. For this to be possible, it is needed to store the pointers to each of the syntactic structures and their length. In general, pointers are variables that point to a region in the memory of a computer. The region of the memory is counted from the 0x00000000 address in IA32 architectures (from the beginning). C provides functionality to do any kind of arithmetic operations over pointers (add, subtract, multiply and divide), so that the euclidean distance over the one-dimension address space can be calculated just by subtracting a base address from another pointer. These pointers will have to be transmitted through the network, along with the SIP message, so for the pointers to keep their meaning, they need to be relativized to a known point, and the most meaningful known point in a SIP message is its start. So making the pointers relative to the message start, gives two important features: first, it makes the pointers still valid when they arrive at another computer (because they are relative to the beginning of the message), and they occupy far less memory, because from a 4-byte pointer (in IA32) it gets translated to a 1 or 2 byte index, because an important amount of redundant information is elicited (we already know that each of the parts of the message belong to the message, so why carry the message begin address in each of the pointers ?). The SIP messages are composed of protocol headers and a payload. The headers section don't usually surpass the 1500 byte limit, amongst other reasons, because the usual Maximum Transmission Unit in Ethernet networks is 1500 bytes and the protocol was initially designed to work on UDP. For that reason, 11 bits should be enough to address a particular region within the SIP message, because it yields 2048 positions. The closest greater value to 11 bits multiple of a byte (the basic TCP network transport unit) is 16 bits, or 2 bytes, which makes it possible to address 65536 positions from the beginning. For the SEAS protocol to be extensible and platform-independent, all the 2-byte pointers or indexes to each of the message regions are sent in network-byte-order, or big endian. This is also useful in the JAVA part to retrieve the indexes, because the JAVA natively uses a big-endian representation of integers, regardless the architecture on which it runs. For each kind of standard SIP header (this is, the headers referred to in the SIP specification) there is a code specification, regarding the composition of the header. Each one of its parts points to one the several components of the header. For example, a From header always has a SipURI and may have several parameters, amongst others, a tag. Then, the From header code has a field indicating where the URI starts, a codification of the URI, and several pointers that point to each one of the parameter names and values. This is the codification of the From header. All the other headers have a similar codification. 2.2.2. General codification of a header Every header codification, regardless it is known to the server or not, begins with a 2-byte unsigned integer, which points to the beginning of that header counted from the SIP message begin (a SIP message start based pointer to the header). Following these two bytes is another byte giving the length of the name, and another byte giving the length of the entire header (including name and value). Figure 2.3. General codification of a SIP header in SEAS protocol General codification of a SIP header in SEAS protocol For example: Figure 2.4. Example of a from header SEAS-protocol codification Example of a from header SEAS-protocol codification 2.2.2.1. Codification of a generic URI As the SIP URI is one of the most used types in a SIP message, a special structure has been defined to describe the contents of it. A URI is always included inside a SIP header, or may be in the first line of a SIP Request (as the request URI). The codification of any URI is as follows: Figure 2.5. SEAS-codification of a SIP URI (byte meanings are shown) SEAS-codification of a SIP URI (byte meanings are shown) What follows is an example of a SIP URI codification with the SEAS protocol. Figure 2.6. Example of a SEAS SIP URI codification Example of a SEAS SIP URI codification The first byte in the encoded-URI structure, gives the index where the URI starts, counting from the beginning of the SIP header where it appears. The next two bytes are flags indicating known fields present in the URI (such as port, host, user, etc.). All the following bytes are uri-start based pointers to the fields that are present in the URI, as specified by the flags. They must appear in the same order shown in the flags, and only appear if the flag was set to 1. The end of the field, will be the place where the following pointer points to, minus one (note that all the fields present in a URI are preceded by 1 character, ie sip[:user][:passwod][@host][:port][;param1=x][;param2=y][?hdr1= a][&hdr2=b]) it will also be necessary to have a pointer at the end, pointing two past the end of the URI, so that the length of the last header can be computed. The reason to have the “other parameters†and headers flags at the beginning (just after the strictly URI stuff), is that it will be necessary to know the length of the parameters section and the headers section. The parameters can appear in an arbitrary order, they won't be following the convention of transport-ttl-user-method-maddr-lr, so we can't rely on the next pointer to compute the length of the previous pointer field, as the ttl parameter can appear before the transport parameter. So the parameter pointers must have 2 bytes: pointer+length. 2.2.2.2. Codification of To and From headers To and From headers follow the same structure, so the same codification structure has been used to describe both. The structure is depicted in the drawing: Figure 2.7. SEAS codification of From and To headers SEAS codification of From and To headers 2.2.2.3. Codification of Contact The contact header is one of those SIP headers that can be combined, which means that if several headers of the same type are present in the message, they can be aggregated in a single header, having the header values separated by a comma. Thus, a single Contact header can contain more than one contact-value. For this reason, the Contact codification is composed of a several Contact codifications concatenated, and a byte at the beginning telling how much Contact codifications are present. The code is depicted in the following drawing: Figure 2.8. SEAS codification of a Contact header SEAS codification of a Contact header 2.2.2.4. Codification of Route and Record Route headers Both Route and Record-Route headers follow an identical structure, and it is also permitted to combine several headers into one, with their bodies (or header values) separated by commas. In this case, both kinds of headers follow the same structure, defined as follows: Figure 2.9. SEAS codification of a Route Header SEAS codification of a Route Header 2.2.2.5. Codification of Accept and Content-Type headers These two kinds of headers carry mime type and subtype definitions in the form “type/subtype†(ie. text/xml, application/sdp or whatever). For internal handling of this headers, SER codifies the known types and subtypes into a single 32 bit integer, with the highest two bytes giving the mime type, and the lowest two bytes giving the subtype. The difference is that Accept header can also be combined, carrying more than one header value in a single header row. Thus the Accept header has a leading byte giving the number of mime type/subtype integers present, while the Content-Type only uses 4 bytes (a 32-bit integer) giving the type/subtype. 2.2.2.6. Codification of Authorization headers SIP has inherited the authentication scheme from HTTP, which is based on a digest scheme. There are several headers regarding these authorization scheme, namely Proxy-Authenticate, WWW-Authenticate, Authorization and Proxy-Authorization. All of them can be codified using the same schema, which is as follows: Figure 2.10. SEAS codification of Authentication/Authorization headers SEAS codification of Authentication/Authorization headers For each field present, there are 2 bytes, one pointing the place where it starts, the next giving how long this field is. The URI is a special case, and is composed of 1 byte telling how long is the URI structure, and then the encoded URI structure. 2.2.2.7. Codification of Allow headers Allow headers carry request methods that a user agent or proxy understands or is willing to accept. In SER, request methods are codified into a 32-bit integer, each of its bits signals a different kind of header. The Allow header is codified copying that integer into the payload of the header. 2.2.2.8. Codification of Content-Disposition headers The content-disposition is encoded within 2 bytes: the first is a header-start based pointer to where the content-disposition value starts, and the second is its length. If there are parameters present, each of them uses 1 byte pointing to where the parameter name starts, and 1 byte pointing to where the parameter value starts. From these two values, the parameter name and value lengths can be inferred. 2.2.2.9. Codification of Content-Length header The content length header is codified as a 4-byte unsigned integer, in network byte order. 2.2.2.10. Codification of Cseq header The Cseq header is codified using 9 bytes. The first one is a number corresponding to the internal value that SER assigns to that request method (the method ID). The following 4 bytes are an unsigned 32-bit integer according to the Cseq number. The next two bytes are the header based pointer to the beginning of the Cseq number and its length, and two more bytes pointing to the beginning of the method name and its length. 2.2.2.11. Codification of Expires header The expires header is composed of 6 bytes. The first four bytes are an unsigned 32-bit integer with the parsed value of the header (which is the number of seconds before a request expires). Then follows 1 byte pointing to the beginning of the header value (the expires value as a string) and a byte giving the length of the value. 2.2.2.12. Codification of a SIP message 2.2.2.12.1. The general message information section In SER, not only the headers are parsed with a high degree of optimization, but also the first line is. So for the SEAS protocol to realize this improvement, a codification for the first line of every SIP messages has also been defined. The first two bytes of the codification are a 2-byte unsigned integer. If its value is equal or greater than 100, then this is a response, and the integer represents its status code. If its value is smaller than 100, then it is a request, and the integer represents the method of the request being transported. Figure 2.11. SEAS codification of a SIP First Line SEAS codification of a SIP First Line The next two bytes are an unsigned integer which is a pointer to where the actual SIP message starts, beginning from the start of the codified payload. The next two bytes are also an unsigned integer giving the SIP message length. The next bytes differ on the meaning depending on whether the message is a SIP Request or Response. In case it is a Request: The next two bytes, are a SIP-message-start based pointer to where the method begins, and the method length. The next two bytes, are a SIP-message-start based pointer to where the Request URI begins, and the request URI length. The next two bytes, are a SIP-message-start based pointer to where the version identifier begins, and the version identifier length. In case it was a Response: The next two bytes, are a SIP-message-start based pointer to where the response code begins, and the response code length. The next two bytes, are a SIP-message-start based pointer to where the reason phrase begins, and the reason phrase length. The next two bytes, are a SIP-message-start based pointer to where the version identifier begins, and the version identifier length. In case the message is a SIP response, the following bytes correspond to the Request URI codification. The first byte is the length of the URI codification, followed by the URI code. The last byte in this set, is the number of headers present in the SIP message. After this byte, goes a section, called the Message Headers Index, which gives quick access to each of the headers and their codifications present in the message. 2.2.2.12.2. The headers index section As it has been already discussed, the aim of SEAS project is to achieve as high a performance as possible. One of the techniques enabling high performance in text-based servers is the so called lazy parsing. To enable the laziest possible parsing at the Application Server endpoint, a mechanism has been used so that access to a requested SIP header can be delayed until the application requests it, and the access can be direct to that header, without parsing the former headers present in the SIP message. Recall that one of the performance drawbacks of the SIP protocol is that headers of any type can be spread all along the header section, not having the constraint of putting the most critical sip-specific headers at the beginning and ordered (which would be, in fact, very desirable). For this to be possible, there is a section right after the beginning of the payload (the general message information section) which is a kind of hash table, giving quick access to the codes (as explained in the previous sections) of each of the headers present in the message. This sort of hash table, is composed of triplets of bytes. The first byte of each three is a code indicating which kind of header it points to (whether it is a From, To, Call-ID, Route header, etc). Then follows a 2 byte network-byte-order integer that points to a section in the codified-header where the body of this header is more specifically described. This gives really fast access to any of the headers. For example, if all the Route Headers were requested by the application, then a lookup in this table would be necessary, looking for the value '9' (corresponding to the Route header) in each of the positions multiple of 3 (0,3,6,9,12, etc). This can be done in a extremely fast and easy way, as this snipped of pseudo code explains: for(int j=0,int i=0;i #include "cluster.h" #include "seas.h" #include "../../dprint.h" #include "../../mem/mem.h" char *cluster_cfg; #define eat_spaces(_p) \ while( *(_p)==' ' || *(_p)=='\t' ){\ (_p)++;} /** * Parses the PING configuration string. Its format is * "ping_period:pings_lost:ping_timeout" * ping_period : time between pings * pings_lost: number of lost pings before failure * ping_timeout: time to consider a ping failed * * returns * 0 if no clusters present * -1 if config is malformed (unable to parse); * 1 if config is successfully set */ int parse_cluster_cfg(void) { char *p,*start; int n,k; struct as_entry **entry,*tmp,*tmp2; if((p=cluster_cfg)==0 || *cluster_cfg==0){ return 0; } entry=&as_list; while (*p) { eat_spaces(p); /*get cluster name*/ start = p; while (*p!=' ' && *p!='\t' && *p!='[' && *p!=0) p++; if ( p==start || *p==0 ){ LM_ERR("cluster names must only contain alphanumeric chars\n"); goto error; } if (!((*entry)=(struct as_entry*)shm_malloc(sizeof(struct as_entry)))) { LM_ERR("Out of shm mem for as_entry\n"); goto error; } memset(*entry,0,sizeof(struct as_entry)); if (!((*entry)->name.s=shm_malloc(p-start))) { LM_ERR("Out of shm malloc for cluster name\n"); goto error; } memcpy((*entry)->name.s, start, p-start); (*entry)->name.len=p-start; (*entry)->connected=0; (*entry)->type=CLUSTER_TYPE; (*entry)->u.cs.name=(*entry)->name; /*get as names*/ eat_spaces(p); if (*p!='['){ LM_ERR("Malformed cluster cfg string %s\n",cluster_cfg); goto error; } p++; n=0; while (*p!=']') { eat_spaces(p); start = p; while(*p!=' ' && *p!='\t' && *p!=']' && *p!=',' && *p!=0) p++; if ( p==start || *p==0 ) goto error; if (!((*entry)->u.cs.as_names[n].s=shm_malloc(p-start))) { LM_ERR("Out of shm_mem for AS name in cluster\n"); goto error; } (*entry)->u.cs.as_names[n].len=p-start; memcpy((*entry)->u.cs.as_names[n].s,start,p-start); n++; if(n>=MAX_AS_PER_CLUSTER){ LM_ERR("too many AS per cluster\n"); goto error; } eat_spaces(p); if (*p==',') { p++; eat_spaces(p); } } p++; (*entry)->u.cs.num=n; /* end of element */ eat_spaces(p); if (*p==',') p++; eat_spaces(p); entry=&((*entry)->next); } for (tmp=as_list;tmp->next;tmp=tmp->next){ LM_DBG("%.*s\n",tmp->name.len,tmp->name.s); } LM_DBG("%.*s\n",tmp->name.len,tmp->name.s); entry=&(tmp->next); for(tmp=as_list;tmp;tmp=tmp->next){ if (tmp->type!=CLUSTER_TYPE) continue; LM_DBG("cluster:[%.*s]\n",tmp->name.len,tmp->name.s); for(k=0;ku.cs.num;k++){ LM_DBG("\tAS:[%.*s]\n",tmp->u.cs.as_names[k].len,tmp->u.cs.as_names[k].s); for (tmp2=as_list;tmp2;tmp2=tmp2->next) { if (tmp2->type== AS_TYPE && tmp->u.cs.as_names[k].len == tmp2->name.len && !memcmp(tmp->u.cs.as_names[k].s,tmp2->name.s,tmp2->name.len)) { tmp->u.cs.servers[k]=&tmp2->u.as; break; } } if(tmp2) continue; if (!((*entry)=shm_malloc(sizeof(struct as_entry)))) { LM_ERR("Out of shm mem \n"); goto error; } memset(*entry,0,sizeof(struct as_entry)); (*entry)->type=AS_TYPE; if (!((*entry)->name.s=shm_malloc(tmp->u.cs.as_names[k].len))) { LM_ERR("out of shm mem\n"); goto error; } memcpy((*entry)->name.s,tmp->u.cs.as_names[k].s,tmp->u.cs.as_names[k].len); (*entry)->name.len=tmp->u.cs.as_names[k].len; (*entry)->u.as.name=(*entry)->name; tmp->u.cs.servers[k]=&(*entry)->u.as; entry=&((*entry)->next); } } for(tmp=as_list;tmp;tmp=tmp->next){ LM_DBG("%.*s %s",tmp->name.len,tmp->name.s,tmp->next?"":"\n"); } return 1; error: tmp=as_list; while(tmp){ for(k=0;ku.cs.num;k++){ if(tmp->u.cs.as_names[k].s) shm_free(tmp->u.cs.as_names[k].s); } if(tmp->name.s) shm_free(tmp->name.s); tmp2=tmp; tmp=tmp->next; shm_free(tmp2); } as_list=(struct as_entry *)0; return -1; } opensips-2.2.2/modules/seas/cluster.h000066400000000000000000000017341300170765700176250ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef cluster_h #define cluster_h #include "../../str.h" #include "seas.h" #define MAX_CLUSTERS 10 extern char *cluster_cfg; int parse_cluster_cfg(); #endif opensips-2.2.2/modules/seas/doc/000077500000000000000000000000001300170765700165335ustar00rootroot00000000000000opensips-2.2.2/modules/seas/doc/images/000077500000000000000000000000001300170765700200005ustar00rootroot00000000000000opensips-2.2.2/modules/seas/doc/images/image005.gif000066400000000000000000000123711300170765700220020ustar00rootroot00000000000000GIF89aÊäw1!þSoftware: Microsoft Office!ù,¹âƒOÿÿÿÿ¿ÿ¿ÿÿ™ÿÎÿÿÿÿÿÿÈI«½8ëÍ»ÿ aižhª®lë¾p,Ï´7Öx®ï|ïÿ@àˆt¹‰ˆÁ¤rÉl:Ÿ¸!’b,N¡Ø¬vËí¾¤ÖLÕK.›Ïhå0l½¦ßð¸|~tÆT;}Ïïû™kx†‡ˆ‰_zGŠŽ‘’“”•–—˜™š}RžŸž›¢£¤- §¨`¥«¬­±²³´µ®¹º¢C¶¾¿³…»B©ÅÆÇÆÃ#À;ªÊ>#ÔÕÖרÙÚÛÜ׃Ñ~½ÎãÁßà5ÓÚëÝïðÜæçsâ¶Ç¿ÂôèêþÙîâ 8o{µð=+hÐT?«Ed7°"5† ψCf,–¾Œ0ÿÒQ›ø¢E‹A’镪N1•3Džl×î¤À”0»|òMYŸrÆ9Ó&AFBW"jn)ÏPIW…x­æD«Fßጚå'­„–¶äzbêȪ$³%ûæ¥KPyÀ²Ý0uÝU’iÕfÛ:÷É)_Éík›Ýxy»‰”ð¨€q‰á;7]âm5½qÒ²c'p}BŽ;úsau™çÝÌY›gÓKB“îV6lÁgKJÔ µYÅoC±][rÜ ´…‡ÝùòDßf_+ÿÁì©àL¯# û·5»Î5CÇ&}zê·ReÔüv´ßÁDm ºÍîubzj={ã™ÂÝÿ åÝñPUVŸ}D˜_û¥‡Š“‰€9¸œaݤàE 6háƒÄS]ÜMÆŠàbDЏ!Øag(`#käH¢4šVi®•*’@_D…Ç[Ž66éä§ä¸#NsL)Þa`hE‚L4¥Nn&å”~õŒGùŒEJ‘F óbœÖùw͉¹`Mr´—ŽhB¨&O„Þ“+pnyd‹tvyçEt×?!þ( è}ÅL¦šFóbXŠGg¨r“—¤9Žwæ¥IpZN¡ ±£|ß½£'p8ZjÞœÛÙÙ+œ.¨A•&2èt–éõ§Ÿº"»èÿ¢¾‚:Æ´ÇV9èW†ÊšGnÊŠwŒkÇ&5'´¾òÚë¹ÚQ§ÚnHŸ©%Æl³Ê;j悊® ®"£i¸Ây)j§îi&¸îñz/ùF d õ—†d1<ð‚†¡úœoÛPl–;µ%[ì¡ÎÊyq5`ÒTëÆ¹Ò0¹!ã;òÍ%{U¬±oÒóuM]ÜÉ|ò¨*ómæÖlqLçlr›0¶R Ê>-‹goÅ8‰ð®ä:Lò¾åµ°žRF6ŠV¯|Ô'O6¹u½ùR‹3ØgGæ[CTφBt÷eÕIÛm p´cQë Gµ´Cà^ýŽíþäýóÇ…³àæÿ°›ï#[Ý)ü6Fչ䕑ºà,_~ÎÃ{£"Õ7™¯’ Þ=Éz<”›.yïª÷ÞúgÄu ;?zÔ>Êñ9˜åäêj{³:ä§C }ð¤"áÅ+ » rWè:%ã¾²×ýapãêW“z?KxDfX¿#W6¯6öóûðÚ§:j\Ox R^ÙÊšîmg`àP¸¾ "ЀècŸ,¢V0€Äóºò°ÀÐCqüèØ3˜½ÖO{¸º^=BÛ™P g»!æÆg¼è)ƃ2äÆ _ÈBòP(| í@×.uˆD ð¢8D *P%ykb8|Ã"ÿNq…V”¡™Ø—j‘J\N¡F*Ž1‹gŒ#‡A)†1Šn´#åÈÇá¤Ñ…`\ãŨÇ+öñ[K[¸v¶¬Æ½èlUºþU·Êÿ®K_[[°Bužuîu ÛÖV ÷»ë$.\«JЩ²Tºåµ®z±ûÙÉžv´~Õîvo‹YÎ ¸àÍï?-K[ÈvôžðMoq/ÊÞØž7¹íðDok`¹2Xºx5DoõKaaŠ÷²N®f—;ÖÛ¶Ãñ=.‡]»\èx´&~/~+ÌâÂò÷«°En‚ â—ÅK mŠ«+YÃîø·¨gÜ×çꕽm/`mL]Ù"ÄÎ(’'ìMÓ¾xÄrõpb¬áËøÉP6çˆÍ;UòòÂä\±–±¬ÛêæØË³U©y9läŸv¶¯±-óJçì[´ÒåÈ™=.“Áíc×Á\þ²¸\çˆ+ÛÏïx‹?.“ÛxäýÎù¥œRg¹å<ÿöžÉÿÙrßüÝ—¹¼½½ó ‡;àèÞv’ÎòõYØv•/}·îs>§9éJ§0ÍáÙÎqf|ši¤áöàŽ‰îæÙ‰šö­®ìmïÛw°ì¼S·Ìö»j÷n8Áû=ð†+á›—ø¶#¾ñg]| ¯ôÇS>«’GÇå;ÎQ†zþ󠯥„COúÒ›þ~Ã:€êWÏúÖ»þõ°½ìgOûÚÛþö¸Ï}í3;Ýûþ÷Àß}ð‡Oüßs´øÈO¾ò—Ïüð>&;ò ýêÿøÖϾö·ïûçƒûà§=õÃOþÖc¿üèOô½?õ§üîÿùãOÿúãžý!±¿üõ¯ýÿùóÿþRÀz# zŸðÝ·Xà «‡ € è Éħ€x Îë˜{Xˆh{ø÷ H x H&h*¸|þ‚±7€8~4Hƒ(ƒ»‡€×'(h~>x hƒ8Å„À‡ƒ9Ø€@8„̓:èzJ(„T8…S…RȃI„VèƒW]x„,(…^X†MÈ|Pˆ…5ˆ„7X„W¨† ¨…ÆW†fÈ„3è†H¨{yØ}tx†è„h¨p{ X…8ˆY(a%xˆh‡„Ȉ×Ç‚H…kè€Hˆx‡Fè(h‰™(€rȇðˆ~øz“˜ÿ„’艎øª8}‚ø‰H„œ‹„Šz†o†w¸~c¸„¥H‰Ö—†p¨‹mH‹3h‹ˆ‡dø‹Î׈hØ‹¾èŒ³Œ¯‹›øƒ†hŒµ¨ˆ=ÈŒ_¨ŒÀ8}Ð8äø†OXÖ˜‚²¨‰¨T%„u8„£HŽ8ŽØ(á(}Â8ˆóȉæhŒ#è{(‚\¨„æØùˆ|™Œdè†Ì˜˜ÈŽi|È8‘²·‡™‘9{i )|!yŒ9’&I€y’ͨ’懎,É‘©9/)…3©‘©W““R“ È“ûÈ“Ÿ¨“*€‘!I”ù“@‰ˆB:Ii”‰”I9ÿŒ)y’N9‘P•X¸”{Ó”>é’X”Si’U)‘Wù•2¨•eÁ•8Y–f¹hici•]‰"§W—vIK‹u—z¹—Ÿð—€˜‚9˜„Y˜†y˜ˆ™˜ИŽù˜™’9™”Y™–y™‘ù–_‚™œÙ™žù™ š¢9š¤9™Š™šª¹š¬I˜ŒYš°›–©™w"›¶y›¸™›ºy™§Ùš¾ù›À¹˜»9œ¡I›6@œÈ™œÊ¹œŽÙ›ÁùœÐ™˜¯ÉœÔÙœJUؙډ™ÎÞù9Û™œÆi<ãyžè‰Ý œ¨°˜Ràžžâ9š#™¯ù ‰Ÿ¶Yÿž€‘ž±YŸùyŸž àŸ·¹ž¿Ùž €É Þ9Ÿ¢    §ÐœúùŸ×i ¤)¡Ú˜ê¡ª¡Jò‰š‚9ðž&ª¢+úœZœJ¡ó)¡:²ÉŸt!¢ô£ j¡/ª£‰ .Ú¢ j¤ Ú FŠ¢Ñ¤ŸÉ¡ ¥>JP ¥ô™¡Bú¤<*¥Ú£]š¥J¢ßɤJz˜dz¤Hš ÿ¹¥Tʦ5j¥a`ª¥öÉ¥?ú¦N:§”I¤Áy¦Gj¦Ÿ0¦kZ§mJ¨ °ŸXª§¼é¦Œ: Šê™|ÊžKš¦JŸ‚ ›V*£†ê¨7š¨º§Œº©ú©C*ÿ¦:©€ ž':¨Sš©^ª›8:,¤:›¡Úªy:«{jªMŠª'Z¦ªÚ ¬ú¥…j«Ã«b€« º©ÊЬª«Ðé§Z©*ú«ÀŠ©xj§Ãš­¹i¬"À¬’i£@Š­4z«Þ:¡ÎZ¤ƒé ÑJ­äÊ™‡ ¢ §*§åJ £Ú¦ñÚ®õ© J©–:­Ôº ûy¯ø*S «žZ¯‹§+¬ [™ü°[˜úº¯ û°»›;±[­kšû±"‹¡ÃÒ±&k˜[®ÜZ#Û²z®'{²)ë­+û .{³˜ ³1Û±3ˬ5+8´q*;[´+´?KB»´ÍÊ9|ùÿ´PÛ?œµT[µ B µZ»µ\Ûµ^ûµ`¶b;¶d[¶f{¶hK¶I+iÛ¶nû¶p·r;·`ËQt{·x›·zK·kK{û·€¸‚{¶v;¸†{¸ˆk¶}‹‰Û¸Žû¸Š ;¹”»·‹K•›¹šk¸…»¹žû¹b{¹wº¤[ºmÛ¹¦‹¶#µŸºp+ºàºs»º к²{·¨{»cK»  »ª»X¾û¶¼k»Á+¼’[¼¡»µ«¼¬K»È¶°û¼ª«¼Î»¼Ì+½‘‹µØëµÃ˵ջ½[½à»»Ô{½Ë;¾e›»ãÛ½Z;èë½Àû¾u[¾áû½òÛµê ÿ¾Ãë ÷Û¾ñÛ¿Þ[¾ü À_›¿Û»¿R@Àµû¿ |¾Ö›À Œ¿Ç+¿ìÁ ,aìÀö›Áþ«½L¿,¾\Á ¿ü¾$ÜÀ"¬Â \Â&<,ý›Â ¼Â3ÜÂ.ÜÁ0|¿2LÀ4ÌÃ6|ÜÃ@à <Ä@lÀFŒ½=œÄŒÄLŒ¼KüÄ#<ÁR|ÀE\ÅS|ÂXü¼Q¼ÅD¬Å^¼]Æ(LÅdì»c|Æúk7VÛÆP›—nÇr¼GüTÇv|ÇxœÇz J’ÓÇ~üǀȂ<È„\Ȇ|ȈÈ{¼‰ÜÈŽüÈÉ’,É‹¬“|ɘœÉš¼É€lœüɠʢ<ÿÈ30ʦ|ʨ¼ÉžœÊ¬ÜÊ®LÈ¥üʲ<Ë´¼Ê´|˸lʱœË¼ÜË™l˾ÌœȻ<ÌÆ|Ì\ȼÌÌÜ6Åü;#09ÑLÈÓ<Í€Œ ÈÌ'€ó7e’ÈÖ ËÊÜÌâ¼ÌϼÍ߬CPÈÕ¬ÍŒÍÆ|Î}â ñìÍìLÊá<Îø,ÌåÜ6RÏðÜÎõ¬Î=ÌÿüÍéŒÎ-ÈÿœÌ!`ÊÜ %™¼Î“üÐ7BÏ­¼ÏÒlÍýìÏüÍ % ÍҌР­Ñ5²Ñ%­Í Ï(MÒlS&õ¼ÒMÑmÒó¬È÷ Ê/MÒ˜üÑ¼Ó ÈA Ê ÓèÜ'<]Ó-Ï<½ÿÓÑ2½Ñ ÒëLÑL­ÔW]ÒÎ@ÍÍ>ÍÐ ÊKÐ’<Ôˆ<Ö}ÖdMÔ2pÍ'ýÖM­Ò|2ÓmÓ" Ór}Ó!íÓU Ò~]ÓÔP½Ô7×s}Ø‚ Ö Ö1Ø-Ñ-ýÐuýÖçÌÕ]Ù”]Õ˜m׉ýÉE=Ï+mØRÍÎ_­Ô}ŒÙæüÕš=ÒzMÚŽÖмחMשmÖOÌœÜÒ…ýÒSÕ“MÕ&MÓ¦=ؾ-×£ Û¢üÙÚšMÛ­íÏ!ýÔF×Î Û’íÚÈ­ÚkÍÑ‚Íԭی۹mÕ‡]ÞÍ׎Ý×iM×Õœ­Þ½Ý¿ÜÖý×¥ÿÝé}ß…ßù=ÝÐßÞÝíÝØÖýÛ[ÝÙ},ÞŸ|ÝNØÔ½Þ¯×þ½Õœýß.ÜlnmßæmØ^à§Mà»áèÍá^ÝÜ áýà’£à¡ìÜîà¾ á ŽÝMâ^=àÑÜôíÇðmÜc}ÜÅ⬠Ü7¾ãäÒCNá²ÕO^ݶm#0ÞÓ"®ÝInãþÜûÜ[Ø_Ž×.®Ê?näÍߎޡmÎG®Õk^ಠæÞå=žÒíæB>â‚\å—üä,þá\¾åXæuîçIm×2ᡬÜùüÓòÝèÎœÓ ~ÕÁMä,Näƒ^äN^éš^ã‡þè”\ˆænå£Î =Êä-æ™ÞÏkžé€½Ð4ÍÞp­Õ+îã^ê ê¸Îç©Lã:ã¸Þ¢ìÄ>ʼŽÊ½ãN]ì‘~ëÌþìÖ¹œÕÊÞÐNåÃ~íÚÉǾíÞnê0ðíâþÈÝ>îæ8Œ~îêî$å¾îê^ÉIàîòîÇðó~ïÍ^ï=;opensips-2.2.2/modules/seas/doc/images/image007.gif000066400000000000000000000125011300170765700217770ustar00rootroot00000000000000GIF89a\øw1!þSoftware: Microsoft Office!ù,Qà‚ÿÌÿÿÿÿÿÿºÜþ0ÊI«½8ëÍ»ÿ`(ŽdivAª®lë¾p¼žtmßx®ï|ïÿÀH€@,ȤrÉ$‚ШtJ­Z¯XݰÉíz•ϬxL.›ÏèôöËn/Ãé¸|N¯Ûék·~¿ûÿ€‚ƒk2‡ˆ‰+F}„Ž‘’S†–—˜™š›œ›y“¢£¤¥¦ žª«¬šy¡§²³´µxŒ­¹ºŸG±¶¿ÀÁÂ<¯»Ç»¯°ÃÌÍÎÏ(½ÈÓ¬Ê¾ÐØÙÚ¿ÖÔÞÊËÛãäå£Ýßé˜á׿îïðfìêôìñøùúWóõéáâö HÐF?Þ¶+Ȱ¡C!`~ð¡Å‹‚ûä ÿÜŠjo0ŠI¢1N'3¥´dlå%—.y…$I³¦9“;®³§S¥*Šm : hÌ—wœ4(ѧPMÍé)ËÈš.ŒÊµë­T±^K6Y“­^ÓªEeR²+‚<»¶®Ý8mߊuaõXS§w –òW.ÏžKËêú x°ãÇ9 ‡í›X±e•]ÐBÞÌ9Ç)ë­ Î‹æÎ¨SK` ÚÞǾbY¾þiZõ9E¸sëÞÍ»w"}ŒaùþâËiÛu‚ïYμ¹sã àÄ 3}õ`@Qþ¼{›ãÈçp÷N¾¼ù™¤‡¢þ@ºƒFàqŒ?OŸQøHóëë߯'þ{ëÿÙ5à}¬' ?ü%øÙ}ä§àƒáß <€fH!†U8¡s2H†‡–X_ˆ²—↲Ø"$š¸Š"b£Œ8>G㋾ȣ‹>fçB7æÈÆŽ5vhä’îØc?j(e…J2© ’Ifå–ü9Éa‹~dX†P$—teéÇ™h¶ißPx¡.– çwÄå¹HjÞÁ¦›nzIe†sþW'Œß  è¢Œ6êè£Fú¨ƒvöÙ߀¢)è˜) ä…Z&*騤–Ú(¥–&—éªÌmZœ ˆ1N¤™©¸æ:錩ŠÇê¯nTzN­,¼¡ë±ºz(l¯ÿ7`º¥žÐ‚âŽ!1ô‚ìµ¥’¸,³48kekÑ&a3Ñ2‚í¹‘jË-[Àö.5Òâí «1R «ï©Ím». Þ2 î»(½Ùí¼ôúJ±ÆîëpŒþþ+BÀKL°+ö!¬!ÅØú™Ãƒ(ñiñÅë´@޲ ï{cÄ#§×n°(›5î8Ú¶¬ïË1gQrŽ'×lÁÚä i¾Ž"ͨҧ®`j‘0÷¬ÁÏ8]s¼Û=éÖKÀ´¢_}´ŽR[AµŒV£Œu6§Ëõ¢ù†ývºÝE]¶g›˜öÅkcÓöÑs{-x×Èži÷Ýä}e%B·Ò÷3/»4áƒÃ]¸wÿ‡#ÑÌo¨ ¡'GÅd «<îLä€On9ØI§+›™kÞçI´ðùdе¤WhßÜéé:J~*åbÃí:©Æ.;´÷b» ï®{UÔËDìï~jL,ÂߨÅó‹ùò?(~âžÐãÎ;O¤åBmÂBj/¿°†w¿zå¬KþÒä)O~vÍ×çnç–ž„&.Y¹Þ‚Q¿Ô /÷+ÞþàVÿ-Ï|ô±•ÇÒWÀê¥~±×ÆÈU7ý=<¡þ*ø?ZPB0$àîÊòÁظ/cØìÀç´ûQÎk=Džy,¨9 ñ ÑCL `sÛ‰˜›ßn†¸–8 êU`ÂŽvÿšõBX04¡cb CèDv±I$“âpºe:a‘NbÚ£øÅ$R4a,M™1Ç.±}Ì ©*2MiPqÔ óÒA¸Œ¦zÖk )ÎHI"ÈgUËrO! «CÊ1€’ùÉlÖ—»QöÎ ¶¨$%/™©Lb(LˆŒ%céBαF¹!š,TyF.²Š~+’¥˜f?D†v_-xÙE_ —ŠÐ¥™‚é£8“ˆÉìO6ɶLfЙ{#˜éàô%!’“´Ä¦-+ùÇ)xó›5HEãæ²Çè ÓÄ4dùf¶Íº¥ò´3.æYºC3øôÔ=÷ ¬~ú³=•Õ5Ìiÿ…7ÔMñ”AÅ¥KE@ÀŠ9¤¥HÑùÉ_m’ÓYÏ&?*Òƒ~À¢ESFºQ=J¿ùTD¹Eš”™*ÔB)T)$Á4¦[ê¾j ºÚÅ3§QÐŒCÏâMPÁ–‰ êïFØž¯šRÚ#„ãű"GJÕ(S#YO…Aõ_}ó ‰'¼Óª;Õç§Ô“U a¬<²W ízV´šà kÅŒSÏàÑ©ó£ÖÒ¶†pRUbõ¯$í¤0õD0y–Lƒ-«h k"¸Ö.±lmköA Y1Ï\’½e %ÌÂj–Xí*EýúYø„v S©i—ŠZÅ⋸±Ndc{¬Êÿòr·¶õ¤f+Ñ+²T“éü-YIkX€E¤¸ë@re \Ø27Y1½ŽÊX”YÜz¯Å.h [+úr÷C‡ýî I9:Sžò¸¶pík%tÞæ¦—¥ mo‚ßKÔëºÚµï}!Tœ4RŒ¤\¸ŒàÎμ~Úó:T_¸7ŽÚ-OÃjVû·WªF½«_bXtû ¯%ÉÇå†8[#V(Š=Œâ)¥´Sê%1|:ÕâÑ:ù32NŒÛDãC2wfŠ=ÖNvøÇà ò,Nå.•™KU>í…i›p—a<Ö¡Á¼Â‹v9 dÆ—Ç:ÀJȹ¡,úˆÅgŠ¥™ÿ¸û!ïÜüÌžB£t²ÿyž)‹–Zt 7]VPhº¾ú#¯¼è6kY[6è, é0Oº›•¾W1\lÖYëùÖ–&,ÕD}õ}p‰—¡g£oúÑ·‚Õ­¦Û«i‘ç^H¸M†v“9-mgך?ûŒ¯1ìÒí˜Ø(`­— ìÀB°i*4m)}D\[ÛÓ1tÁ™így?Ù<ÙîõšèÁ¿Dn÷vælîs NlrsõºaÝîw;<¬™·®Á€íèdÆŽŽ4`ûR †4 <¿_.8 ã¦BîjÙcÎ`”m ï‰O[Â0ïôÃCmqmƒn”y|‰9¾ØÕªÈÿV]ÉI.r!¢|—*¿õ¥#L_Cµé-vzÌËSóÏÐ5йÀƒ^òs+-á¯3Ò+}f?¶åÏ<Ë ‡½gNSýNH½3?Ìνrûðë­› øÎì ÏXfq4ÁMîuÂOï^û)üþw÷D¼Á!èð)ü®ìf¼•à~V¹ÛHò(¤|×GÏ¿£/^óhö i=o6ÐKÐäxúØLo Ô§Þñ÷e}•Èm÷ØŸìÊæ{ÊmÏ$ÕsW÷”=Ø‚zü±@ݘo¡üŽüPñ>Ù“4J¥?nÓ\½®|*Šò“ýæªrŸ±¢Âþìƒt®VÈÃî×­‚gz}õ—Ôì<¿ÿ<Òoÿ½çµUN‰´Y#åLNÐIóm‚ÅOú7"Á‚€ È€Ô`¼%]p”Y6~­¦¤}€R} X^Gþ÷SƒPz5€e7\‡€Zg&‚>Ó$xát(HR&vNŠrýwKt×J2øyXƒ`Ó*…°P:ˆ`×уõç‚™ƒB8„»Ç$18˜ƒ:QÒY ÷„?huÏÓhßàF… ÃFx„Hˆ7+–‚-•E˜>h…1†°^:†Jhh}Eh„<“8œµ„\+t†vØ/zøoªÕ‡µô‡5ˆ«!dÐEbQ2‡¼V‡ê1‹ÈgèˆÇ¤†k(‰ÿæ)—%F&d_x€€h†׉㊡(Š3yáó|ÐÇ+ÅöXï§R ñUµŠu‰d„VV\±(‹‰£'7Gz%(¦‘öfkWG°ø‰Ê8\3b?Gt†‡x†3©V†cˆ‡Ø¸ˆÉ¸ÜH‹²{#'[…\ ¥A(CൎlcލS‹Î6²ðx_ô>騇úè78ÿøŽ…g‹Ç#by ùt™¦fùxR ˆ~SøÁcwÙ|ˇx‰§LÉNè ŽžÓ‘û÷‘k’ùußcB—'2«ö\ˆqL”a²¡ÞF,ðp‘½pl2 ‘•|m˜’@ÿ\<‰e9×mž8.ñÇ6Æ÷‡~RB"™”¿·”6ÈM™^Q™e8fcÆUW <¤Úø’ b“)$tJ¹~I D©#g¹'lG&xøˆŒEi;¤?Ì÷z‡7‘7I–¥°— Ò—{aCŽnqEƒ‰ZÅЕu£•[‰’i–Û&= ˜‰Cd¨ØR*%Úáh‡ø–ù`¾B|ì'¢¹oЦh‹—½8ïg(oD‰—¢VpY˜³I›GR–v&™?9{³Õ•šr²!œtM›„)›xá™ÜÙÊI[Ì™eTiSq¶D&_ÔD€C5‹™™X÷ÀŽ$s›‰Æm—F"”9ÿ4 §˜Šœå„í¹V Ÿb™ŠÈ“cÔo”© 'HFMûYd­µ^ˆšÙqJ„à)J@Yš0±s Ê‹³UéiMÂéŸÿÉTz¡bgÝY,:1ñS<È`ÒÕŽ4Uœ=§¢É‡œ"`éÑF"*T4Љ‡v¢SÙm¤ÃQÚ©£QÅ£â~m™…<Ò „¢ŠøšÄ©–h)=Jº¤Tà¤Krœ`*e»èxC6o¸Œ7ª¥~ɦM%^U0¦9"¦rª¿ˆšÒY^e‰K¶ f’nª¤”pêNuZZr@ ´§[p…§~ˆhóIšãù_9ú¥‡Z"tz©TÔ~Žº—Ù¤H4šÿ7©<׈A ©ø•¨¨ yž¥áÆz£Fj¹™ „Z¨x¶ª’©¸ê˜­Ú©Žug±š ¿–¤>Ù¥•*»ª ºš¬M¾ZÏj£©âY¬oš&–ʬ4‡Ú š!ZTÑš‰€*«ô™aö¹ qÚ­õ±¬êj‚ ®,øŠ“q ±ñ—{ˆ­ÈÚ®çÁ®úZÍ⪽êSúvsZŸšµúEéÚ¯äÁ¯ ‹¯ÿ®8E?Þ×¢Æh¡ùú°Ýá°‹±r°¿*®‹ê»±ªZ²ÉI${šNÒyÙŠ²ËÁ±0kªk' »9.›±3«2»³ºw³œZ«g¨;˳'[´ k#+‹ Ïÿª¨ÞúHk´Üµ¶Ú!@‹7Žê´yI²j‡Ž7u_0ÛÔ³3ë’Š%qUm9{ªTem_Dq¦ñµ ro:ââa±xKW‰º´eZ^÷ø\·ú¶J·ƒÛ¶nÛ?t;·]À¤Ê·ò‡jƒ·ºP6¸fGYx÷ö>ãiÚ7[º„Cõv±FÀ¸#â¸ïñ™ÔXÒH¹†Ët˜shG»´uø§t¶¶]K‡RǨ+ª[! ô¹kx’µ » ƒ,w¸`k»›;seV»j+sŸ¼ÁËXWûZÕÒºt¦,l+¸Q'½O¦+7oÕë¶Ôûl·›¸G½Û®ÖȺ&麯ÿ˵â{múKmÆhièco&qŽïö<-d@µ {l ˹ŸY¿ßÛ¬Ê~.ºÝ-º‹º].Þã éuè[äÆÁÂeŽ©’þ\M²­K®ærÞçr{Ä2Uê ¤á͆åupÙ˜ÎÌêÝÕä*‰ÆCäÆ=ŽçŽm!®Ý›®¾l]âŠ.Úwà‚>éˆ=äI~ãÓ½×ËžÀ;ßÏþéÑ.܉NímÄØÎëNÛþéRÜÕŽÁ.ì¼âÎìäîìÁþà€ÎØ^äMWºOŽêä½*WïP®Ù'ÞÂ&Áïý.‹ÿþÛŽ ÄĽÂÏÛ鯌³¾ðÖÙŒžèÆ}ÜŠs]Læ©öÇà-ëf¼ñÏä&߇ZžòT¸ò,/ƒ.ÿò ó2OÚ( ;opensips-2.2.2/modules/seas/doc/images/image010.gif000066400000000000000000000204421300170765700217740ustar00rootroot00000000000000GIF89a`øw1!þSoftware: Microsoft Office!ù,Uî‡3f™Ìÿ3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿÿ3ÿfÿ™ÿÌÿÿ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌffÿf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌffÌ™fÌÌfÌÿfÿfÿ3fÿffÿ™fÿÌfÿÿ™™3™f™™™Ì™ÿ™3™33™3f™3™™3Ì™3ÿ™f™f3™ff™f™™fÌ™fÿ™™™™3™™f™™™™™Ì™™ÿ™Ì™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿÌÌ3ÌfÌ™ÌÌÌÿÌ3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3ÌffÌf™ÌfÌÌfÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÿÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿH° Áƒ*\Ȱ¡Ã‡@<@âÄ‹3jÜȱ£Ç CŠŒh1cÅ‘(Sª\ɲ¥Ë—ž”x€Å’6e¬Y“ Oœ0ƒ J´èË™6iÞÌI3§Ož›ê\ Ô¨Õ«X³juZçÍ¥ êLÕéÖ³hÓªíXÕkÔ¯aá–+u­Ý»xóV-+¬ÌŠ]ýž›·°áÇ÷"^̸±cÂ#K&*h²å˘‰²`qeó•Ì C‹îÈB¶VØ] ðy´ë×° –>M»U«Î±sëÆ›oÔÀO b½»¸ñĨ}ÿ®}›óñçÐÏö¾XŸ}k¦_&­x!H²0H+®`Qšà!èbZäA~Ñüè#?š²™]]-´#BK¾èÑtW˜\g­dw…rÉ¡ˆ×fZWbm%ÖwE…Z%ùÖ™fA…¦Ol¦)ØXN†…šŒ®`ã -® ·Ú€:¶+" y „*bvZb¥dTqÅ"dh6ÊÔNM:™a”öè p‰¢µZ—v(jx ¢W_“&…”’ªÖÅ”Xq*ÿ9çŒÊÕI 6ƒÜèài•…ß5×ì~Ã+ì±Äb +Ze&¤©úeœŽ¶µf¬rjH[Á•¶©‚j­¶ë¨ä†Š +¬¬çÒ˜ FÖ»n¹IéN¬bËPŒƒÔ‰à®ŒY%p½¾Ôc°cSðÁ'Œ°²¦˜RJ)¥¢´Y äÙëÚ¥Rn{-ÞvÙlQ¬•+²|òRJºé²‹’¸€Úv¤Å¡á›#–þŽ0K+@Z4ÊëϾù¬lÏòÇŠ)G§ qJ á5ˆÂœÆáÉ×1§[mVJp<£6ìÐ^ÓÖµ²× t &§ûqFz¶,âƒúéI¦Ô᫯r·ö{Å¿¼nÿµ‚i'(øàX¶‚îáI—bäÜq ÷ão_³ÞÚtëe¦¨qKÛÕàjµY4g‹MÛ×c hý!}2Ú3~Ñf‚”(;p³ëYyݳÎü[Íû ¸á(ë>8°8"n¶ÉK³%H¡Cîç4¡×ÝŽÕ¨ÝÚ­ ßÖ—5z,ÍÃ`)þo"¢zÒ¬«½‘gæŽ+*Ko9ùbs«oüòÞ·V¯†„† Ò„4§A(=ˆCWñ‡\a{”DÁ Z°‚?òMväW˜K±À¢âö<¶½h° "‚hÙ¹”æ%ÚÎpç[]Ê\§ǵï†^ Ä9ˆ—ìj8‚È-ÿ¬§¿VÜl%KO PSdg] LtÀÃ/É{—Žõ£b«‹\4Ö‘§.Þ%CU"ÐÞ¬6B¬yŽõA—ÁŒ8® An?æ»bÚÊÈ1e‡€ôÍÙÒµC3¦È6L4•\‘¿½ÙÌwé ™oöGàDèi\ðŒg²Å5${P˜(FÊ„aIä£!Óò+Áñ4ŸšS;—•ÏÝf³aÅp˜H6ÈŽ?ÞSÊZ÷É­²\Ã:ß uH¹UŇͩ¤8TDâd%‰ÂÑÎòR¡ÓØ&B¶Ñ8©º,*)pA#Z:×Y4r*M•ÎÜ „=z¦æ„³¬Oej ÿÇ–ˆ‰{ÛÁÜFÐ`¢o˜ÍŒŠÃ÷µ†Ž®jx]+¹ºBÆSž›Šæ¦éH€+ID±T³hžœü&¸@ˆÑÐ'ˆëá—Ò&Î𼨢~øÊìÐ3Jÿâ””°ò9SîKVÔ²€yº¥"0†ÃŒ_X$ÚÇ¡Ã#S¡Ê:‹ê´–Ó• Ä7#v©VI¢á¾)Pløë\*kQy6s†…YVPéÃ2(¸ ÏŠ‰+EN¿ê¬€V{ß)³7àX…K¨‹œmb§,óYÖ Uß'™U‘ô ‹•—ò8Ña2¡„…Ét%®áèÊ£8BkQBšÿNqD\knWÊIÅ5IèÚS‰å½9¾í’¼¬â›Úg:h±Ãù==Ö!£U“ÿ“, -»]ƒÊ˜÷ŠÆtaù¥’è…©;(3›ë©fÔ@? ¨7Óú»ô”  šâ y»ÉM&­œ¨ €.}¨ׯ.™¢â²[Wæ²d‹•®„]6ŸÇösY,说Ê05uV¯fï•=¡†³ÁmùÊÌÒ¢­¢¨}0LúÞÉR)¶Ø ïáª( 4Bûµ©è0—nV?{X(ÁI¥)^2“¶±©êpc…‰žé /­³Oë²€ªL ³áœh6ešY˜¬“jB:oâ’ÿ5Óê/çìÝazÕÊÒyé´†¢ÖÚþ¸¶A²;±àÃR;¥[e“±Êx¶—œY:óçx˜þïAWWšh8²Àß ÏëTþlÕ¨ËÙêŒdJ+ Ì*DÍtL]i§†x™w.¶’ÌÔ“¿@« ñº¹Ÿ’W[ÁJ±f6†ÊzÓú¿âö9ýdýF¹¿»u4h‰-î±ìåà11ø¸áÄîpIK„þd>þ¼e™EÒ&~…0³âœæÛÔŠ#š:ï‘6ãáÿ.x§B-  ¥QñjJONÕ¦=wÄÝ‹Ã).¯ ÇLûüh{àL¬!ò³lÅ$ÀE:ä);Xå W `ؤ¦t§iM™º[¤Eš/›b5¬°.!~…îдéC[gòÚÔ¶_ûÃAgL-!Ûª\U5Ô'"-®ƒEëæ†WX®þ®àVDÐY8y+nʇ·&­ï»2ýè{C鬓ëNð<Ò3?7TS—ÐÛÝri8GpIæVL?ŠßÃ/žv*u»í×øôR±7g}x¢^+ªÑžò½oDõ§gÒšËÝjˆà'¦'„‚G}“!*£C_ëfEj ”†3Ø‚¶õÿÓÆ¡Lúhâ „ÙÑÎ23ûãíÂöûDfô—ùþê->ùËoþЦ؀EŠÓ²çg¨R!`ê'W“g4öFo÷vE‚õ?û'5X¦iq”æ~§ƒ†(Ýe>m—6ƒ”qs˜ÖÅDV€6(t¹õo¹¥¶³)¤[Gõ€€åƒ=’*‡Ñ|Á%pBJ%ÈO"^ÂÝj­ CVˆ2V¨'¦ÀT¡f±òœáUcrBz¦(FÈgh)³$grDWŽ“Ã}š´>¥Áqö‚OÁAþP«ÑC" Gó@xÅs¯wI´·G{´8˃{*ÿx>äO¿e!‰4@ª5H2i¨†è2êG†)t:i#m‰“2•}Ñ# …Ÿ>4&â%UQò2Þ!ˆ%ðnÀçagKhgZPÀMØÑ6$B;˜hM”HO¯T5 ¢"¬A„¹x»˜, ‡P/ˆ.—6m«Ó HyËâ@Ž#EÍ£Žþ1‰Ê‡!\7cõDG£’Ôˆ(eȉ٨n3fŒ'|¢VQ–v¢¶7ŽÈlΗh̸Œ ù {P);â.1·rÏbzîbƒˆå TB8»¥‹òt µˆ8áèy<÷k<èkäCÌ#AÎc(Î=ƒ§päV-dÿá*…')7¹‹ÐP÷TIò1I¡‡@d*-±÷¢Và¡Ufg·xÛuŒZîsC_÷Ri-rAx¨—“8ùrO²!Í1Y28|ƒ%JÉ‚ü+°ôV6“³)“W—BèãEÈb,ÉÒ—>ót1W‘oQzoB«Â"jr|"‚âQUòŠEè7Sñníw{N8›Q^Ç*’˜7)©—z7ì–˜™uBg™¬1>Þ„X׈å–;'[Ä3Û¢ä›óÈã…ÆM, 6"”Í‘…Å™”gqcŸY.‚4Lã‹dY+Ä>Ùa Þ4YÑ"ÎÿTáÃN@cJCcž=<Ô÷u’iÆIÖC>Uó!]ï¹kI/ÒaLcóŸ¢³3zRzŠÝ±Xd? ÂHpÔgžûk¡gd2Â3S‚ã€ÏV ÄYñIµÑ‹gøÉÎÇDqB'ô>éè60  »†Yhw°aœ­p?B# *MßY%ái¢÷Tª¡eq%nU”nÊv8¡…¡‚lɵ@¢v ‹Y=£OöI“™zA-21QžHJ” rB}w-`*å=h¢»¦t–^Ëd£¯ ¶² "ä \ ¤*4¡(j¦¥kD3 ¢¥;D†Qÿ¢e:Æ!$Xç™ Ä#N‡Ó¡º‘o$*3@–R2a ¹'”#Zv¡Pºªn‚ó2ú$¬`™Ö2ÑZé£psr5šŸQR›"¦w㑉#Ó…‰¾ h*ƒƒ=¬ú¬c:Súç7Áe‹p3(N4Gì(¥þÕžÖXù]s²FÖ±ZiÔ¥R1Î2.áÉ'Ðú®ðŠ å«Ç'a°$Z`âB§¦^v:5÷4v‚'®à->º+h鎔93FÔñú°ðÚ°LD¯j(:X`•H<˜¤t™º¡œz1§z=œ¢œ"Zçšä’¬X¨‰Z¦û²«*¦Ø@±˜ÿ‚ö˜bÌ9y$bz‰Å²j_°S°Ø³Qh‰%Óªî ³N ³Ëš||&oÍáLAAb ˜ªISê0Uz°s#´Aôé?¢zCØó­Wö´lû²F;~ôu¬0±˜³ŒtÇmûZ§*›¬Á¹;…HG;B4Õ¨Aʉm»¸ðúVòÚ·x8]s›áD[Û}¥©š P¢h•Þ†!Û,»k@%Ýùh:±ŒÛº0«¶ºˆ~ýj7ç2¼švý:5çÁDe¹£*j–BÛœ™x‹óÅ´‡šÉëºÌ ­+:[;–@£pÔk pèâ­k‚ª‡b’1 ÿâÇYÜDOÅûJ²e›Í»¾NkDÏÔ¦¿Ô-¤vY—U »{(D¹å˜’‰;[U‚ºâj¶©ªì{ÀPJ³±cPv[4¸’þu<›Ê¹ænG M‚e˜¥?Ú¥õDÁ©ŒÀ$<8 ¼ËÖËó x{{&§L½äQ*¶XŸÓT”¢ø¿ŒŸÊIÀ€ZÂ@Ìªî ½Ëp8ò6KÇ_Æãµ° V9Ä2q‰±URžFè*a°ã!€:¾AüÅhyÂè¿{ –·Eix§Æö»½[±‘Þù­([R²'µÃD[cg%à Æ~L b,;f$ {ý~‚&Á›ÿ;’ §â¦D»X÷aÓò5*·É.ûÇ,ÅW&»·šB¥¶÷צ¥œj?Û¹ãò ¿¸o±)*r#c2BÓµEÞy˲xŠÉɼ¬ܤ3X—~Bæ­|Ñhøª‰E¹!« ;•K84/RKhÚǽüʼn{ð«· Ã?—i¬s¿[‚Á\š¨JkMÍ ›4–¶’ÚÁ™L˛͜ìÉIyƒ¡õoü¬¡Äì±<ÇÚ„C ‚‹EéH>u´ä:ÍŸ4uL2ÂöÄ4{º4q=ÈlΚ†Êc²™“BVr ¿²C”OQcrc¬ŒË¨*ÍíÇ µ8ÿ?ê‰üFЏÓ?x4îPž&ÐÙ!ÉeÎËW22OBÑÓM›€êrêQŒ¬ASè=#X‚‰HQ@ÔvA¿!Ó "mºäÛª’(Œ¼N½ÖÛ #maÓ ƒAÇf4}E°6Néë¯î,Ò7$¸ZÓlÍËÚÃWÑèÁxsŒ_íÚ)MD=›¯DT·½ÖÇx Q¹NzBßx:^[}SÙ·[»‘HbÿÛÜ´Å{í+ÝÜ@|3ó\C Ö:À¬XUÞúMÂØÐÑßh6)j:£™•æù{‹È}à ãà~Àœ›Þ¡d,°‡#—°£×3¨}ù&Ì=âÙüÄèf›—©0@Ž-( âY!Ø6Ä…ýŽ*Kq—w¹‚AäEÄGÝNζ-åwë¿b%ÄœìrU. ÄWbWRæ4IÜtæR"´PîºGnâV–˜™ ç9›|ÀEêVn%‹zîVÓ%À©¡‘@jäìç wnŸÿÄ(jê2‹Àc~}~æ~>›—ãVr9"Þ¾*ZÍqžã©‰z_¡2'u§™†®ZÂy®Hýë/·ñèÿ‚º…°@¼´q¾n6w˜2˜ç(¹~|Qѹ-=ñ¸f WsQ+†^C¿nϾ‚½"çÆÎŸðÒ•¸ìÙÈ/z¢îЂç"… êáÅÖ>ëœí_δm>î­Ëßçw®îŽnîíŽéÍó^„™nïOKëù¾%ûÎï0Ûe,ñÜÿ>/ðÛì)AÎxð!!î ß¼S¾2•t 8ñ6˜ðï¼+;¬®OïÞñNk M³#N"?ò6(ã&ÿ´ÉÿÞ8™G$6@-ïò&!ñ1¯ï7ÛùÊ¥ªó«Øó2ŸçN‚7Mo(šhô¯ƒôN[èá΄¢â™,lÔ/õŸDõn;ìZDY~©—e,çìÎ^ï`°=ó11ÓyCA’ñ^ÿÐ`¿ªkâ†.ÖÆâ›`øŸ0çy4x÷|—÷PúóL²"Æx•îs 2tˆŸ€<¯ø}BKú3„¯€úèàèCÎFÿÊñ6Îøühz cÃ5 úµT^ù{æ»gÚ´&oðæ¦€•ÊNE3<àG}|¤ša„*ò`ó쑵æ=Ÿ†áD²'ûûX¦¬ˆ”ÿo˜-Gì'nè­úúíû(¬D¥ü„–ã$pð„ì¥ip¶.zXGšö÷AþÍÝõ6øa £8­6‚ x­Õ5VÑšb1«‰WXÀˆ1@lìRãH‘3–ôhòcÉ“-]¾„SæLš5m¾yó$ „=}þTèP¢B[,Èâ¢Î˜X]™­EVHFS(uêTV]»–2U*ÐÒ•#U¶Ì ’cJ³2Uš„Û–é\ºuí6=«“… ¢}ýþŒMióÞ `ª0 S ¯™2µ§*•±5dŰ•©h©î! wmYÃÕŽf»ò¬a¶£Yª¾[öÿl·‡u¸b°ßÀ½}½"’¶Æ®P6r|!DѰ¶r®p«×ˆÕeš³¬lÛ»Oó a‹dÃÛeÍó÷zö‚ÏŸLìP‹Ý¦,ßw¼p!æÎÖ©Z´)µ÷$Ð.r«Ì¢àêÒ¼¬j/¾æ+Щ±XAh¼Ü®(!èô“Ê!®¨K,ó D1ÅáöAZ9®ïbÛ (% ¬•+xCH)ÃÆŠ±áÐ0+*Ë/É̪⠢R>;QE)§ÔK©´ŒB*iªÑ§.qì­à k@§®‰ÆÊª¢™ïÁhšÓï(­Ž¢"‰°Û2O=_O£üT°ÃÝp›©Lÿ »bG¯Ó7-·Œo ‹ÈÔ±Ìö³Ô¡þ<«(Ê=?·£F=Ê"?_T°)Hߺ±Ñ¤€Z0O§ºÈ(»âÒ¬Fs:êX)OP‡ •>?RÊO,;l1JCõ|ð§VÙ‹Ö§Íä 3T¯(!Zè–tˆ¡p™ìo¢‰@#6]7µO¥r›QƒâNÝweW(½Ü!A>âñQYi­j! º‚EüÖ×®ì¤HXu'~<1­ @Œ=UÒ‹®….ú€ŠWZ „±£Ò>Mlªp-ÃæÁLù÷!ÿ:¥8çŠ7‚7Q@÷ÒñË u†IÔ‚æ[ èÝÆÓÁÝ ËyVZÿ?ÌJת®žÎa¯‚õ”h¯eô›ùü$ÌX¥3Ö¯5ZlµÅ\" dh³}Y!›[ÒeÌ4kX(לA§,[AŽ{8p´oŠ`VN–Jºý¿šòêîìšqÍáÑÊvkSƒæk Ä5—Ûë–Jt†\-ÍŠnÙ5…ØÜ§2ßu@w$,àaDðËDs'‰xâ¨C`BüС_Ÿžkã§UÌöRê(,êÜô‘Jâðu޳ù¦bˆ¡Ë"Ú´};Ëý{úÜÙ…±Í¸=ï^ÌQçËÐøñ£&˜œx%'†@Ï€µ“˜ü'ª6Fc@ZT*§LljcÜÿÄgŸÿ½Õ,}G˜6s¹›=ƒ \ýè3¦¸m F£RÚÒrºÊÏB!ÕVxe@+,ci è']LH -²?õÐ'ˆó³!DpãÁ’0„Vô[Er !Ή+,[ ‰88Ò Bcõ« ©GS‰ƒrZDæ9Vgަ@Z¶ºøµëµÈˆ'õ"Œ­C{\ dp,Ѭ€ñL$©"‹te\ˆ$¾vt ÖG< ªËBÍò£¡Ã^Ñ•¦°È*é4Lfri+˜Že¬Ý„2h÷!e)‰•˜ÇA…PxŠ ™GRåI¶3—Æ&".pÕÿ2jN{ ¿hÆ^†rY£Â†5SH«Xއ’‘ tÀâ$Èø"†[X<Å9±“Y¦<ÜôY/‰¸4' *œþ›^ö2˜7é 4³/AŒË¡™™'=]H¸MîóO¤Ó^©'?¥Pg+8)&r”'•ȇÅaÁ²V•ˆFíw„<7‰¨±Üø3•ŸqØgŸ@h¡T±]²¨9Ô½µ”hõUÆÌHÄìÓ©OÄ© ­ð¼‘¶"¥x¦©â G®UuëÜ>MeS¢P˜+ ¦Âf*è¡Då¢W·²™±O9|Ѩ䪹@•^™J)@“«•Žêj{]`áIV¨vñ¬ÿ:K«_­±ä¦ƒoEìæxæ'Œ¥³Ýãc½¶‚¾Z³`)jA›YÖª±µ¯…­kc;[Úµ¶·P^ž—ì òY¸.ØÚ’øxg.¢•(ÜÆ\æjq5YË[äR<•¡$º|„OÙ®D˜ævW¹ZܶÓ´¸F8Úù¼c%NbPÞ…ïpuËÛÓ¤d¾ÅK‹}çʶµÂ®‡ßàÆåºâ5qË‹üê7Á)ÄH²,à6¥ŒÀ&nmRƒç²Æ4èEnn?’(÷Ä´€ Æ.¼â…xO׳ -ÅP±ØÆÃù-ê2–1›Ø jÑ o<ä×Vï›Øÿ°Ž”JC"7y¯žôÞh`W qhNÆ2bqó¢£5Q=r–Å\KOæU,ðÂÉ̈»1·Y[^ŸÆ"1¹ÙΘDêS™HÁ;÷YˆÙ+³4&ûÙÐsÅeõ>·çOÚѨ«` Iõ 3ºøÑ—ÎíDç¼›m)+̘5¨’…›A Ackt¨U=Ìß)ÙEÈÂÏÎ'UךJ†éØ Ë\*Â\P^ÿ´u°+Ê×s‰0$•°•]±ce”¬ˆŠ¡!²ljÓ¦©438iJ*¥ÑºÚßFO OfSÒ‰­—(+¸ÕÍ _4” ýÓî’½nzÛd/p&Òô©£x³Þÿ– ÐáRïÑý,QÒö,À ák1tþY ý½pŠ·D¦ÝÔç]å¬×ŠwÜ;ëbïãm—÷ r»³½Z“w\AĈ·IÞrš×Üæ7ÇyÎK®s§b7±´Î÷;]ê=@·A°s….×Õ*¿à ïÊÔRÜ?¸êÆ ïÕüô¢/=˜ 6T~© ^ª«ÊêºUpÙÓ u¯“ù¼(ûâlvžW4n©îx£ô¶¯ ëF‡Mà9,xï¸Æð¤ <~årxò¦·ï‰EoÑÌ«õÅg÷î­ÉðtßBß×äýñËæûçEïç€;opensips-2.2.2/modules/seas/doc/images/image016.gif000066400000000000000000000331461300170765700220070ustar00rootroot00000000000000GIF89a6w1!þSoftware: Microsoft Office!ù,.9‡3f™Ìÿ3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿÿ3ÿfÿ™ÿÌÿÿ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌffÿf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌffÌ™fÌÌfÌÿfÿfÿ3fÿffÿ™fÿÌfÿÿ™™3™f™™™Ì™ÿ™3™33™3f™3™™3Ì™3ÿ™f™f3™ff™f™™fÌ™fÿ™™™™3™™f™™™™™Ì™™ÿ™Ì™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿÌÌ3ÌfÌ™ÌÌÌÿÌ3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3ÌffÌf™ÌfÌÌfÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÿÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹)^Éȱ£Ç CŠI²¤É“(Sª,à +cÊœI³¦Í›8sê¤éòʵV×½ÜI´¨Ñ£H“*]*³'«hÑ~Fk%¨%Ó«X³jÝʵ«Ç+XµÂtlÙk.½ª]˶­Û·'] úIhÔ³­ÒÂÝË·¯ß¿[2K¶0áíXl̸±ãÇ9¾Jöڵʗf¶Œí²b˜‘C‹Mš¯P‚ «>¼úš{ª–žM»¶ír/_ŽÖ¹÷nß¾…^i551èÛÈ“+_nQñê笱a…E+³?§eν»÷å.±ñÿ–~YúøÞæ¥g%õîµ»B¿ËŸOÿñ+„yÍ,¾°ú=‚±& 6.Wß&¨ bIçàƒbˆbPÑåÞ…ué¥à†vˆÓ¦DʃüñGVK¦hØ‹yèâ‹0†äÒk F¨Þdša¦cŽ”‘eŠ1)ä]€úW˜\Ð5¹6¦°2!‘TViå@ %È–ï‘'˜nÀ)&z­DYÊ-^©æš^1aj&™˜zNª¦+Q"%‹löé'}.ÕÒtÀYÖ=XzŒvv+²RJ)@þi饶Ý7¢Kn‰ + fWbaüEõ”XfJ ¦¬¶:ƒf±ÿPèp'BJœ÷h¤f¢éê¯À6¢ˆdåõ¢¯’]„üM”yî™f°ÔV«–K¶Ú¥¢¬Ì:Õ¢äÙõS+‘F:i¥Ö¦«îRFžšP©*T fX]tZ¦¤R¢»î¿ëÄ Xb•¥_X¨I~êMåð®öªXÀW|ˆäÊzÝ5#‚zЉZ{g±­”ÒZ¬òÊ(¹Dn·3 §Ybe±° ¦\×ðuº’û2¤ç²,ôÐ i*¢ %›ÝOÅiÂ@±'5Ç"î[ŠªDg­usÙ^W\­“ê êyÍóÏxJêëÖl·½Ð°R‹Èñ§8+ýd#ËíìÉzÿ§ۀ€\uWMÜOÖÜÞ\ûLÁåJJ©à”·m¤ß‚9;¢PúÎUY">zˆ¨ò;eå¨gÍ ¯±ˆÏ`·bh K¹­åJ+W4 JQûXÁ]x¡ÛùàÄËÊàe‚VÃZEGÚ˜—ØŠEº:˜ù V¨CÎ`cåÚHJÓ·*ˆÃ ^77y…r`Y¾FP`Öô¨] âMoŠ-±°ƒ¹Ͳ‰DƽÔlhÚ ‘ÊÕ«,õ¦iWèæ’<¦ë:/)PÙWµT®«p%Št òU,u­g±ëÑœ>ų‚ÁÓ€ÁŒ«`Wâ º‚u nÒÓ{Š3½eçPQÓ›Ü~ÙÆÁZ6%Œ@ €Ãnu°B!â²õPË4.REÛD/ËZ“8h0  âf rŸ ÿ½æ ¡ÛƒD~>NtÌkë%[KÜ‘´óXذŽBPG924v-ÁíáV ¹Ûų¸ØåHf¥È[*D‘=iÉk¬ÁåUe0sã#¹œ—Ë¢*2»ðµÈkSC+ZÍõ M¥$I–8¬ÇTŒÜ¹¢ÔŠøØ!RäùòB§Â"Ä:a<Þ9—[Ž¥WŠxŠ+ûEÖhøÃЧ³%wb㻢dÅùZâ?Sàǯb±L€ ØJA Ä8ÈqY´H8=h!† @  e>í R‰ëÛ!†S­×Žc9þ°ˆëÈ"ED1"$!+˜ÈºÁˆÀ"L ­§¯GÙÀñˆ­¬Îÿ,Š1–ß^ò!·Ïtx¾%×·3Ã7W¯ΚœÙ&$¼l㌢Ehý ËsœÏü\ܡƥdññ}rbƒš!ž¤¢¢%iFÌÙx±î¢4q\/úFHÇ›Þì§’b*f0Ѹڙ¢ÔÃùZ,ªnm m9èX Žv󕇓³û@öŒU²dãÆž`_B.e‘} }ì:&¦³Á_¤¢ˆRLV©…±™­×WÓl†µvÒ؇œ†Ùb{I‡©M5vBQާ~\*Èiá”~gc¯¡Øþþ÷'¯ÓÝtÆÐ N.·3¢(àö-ÉÝ-iºÓòÿÖö–,¾ñ»Ü [ù-§cb‚LYãÅFùËwÎiƒØ^´PfYsž½ÛkÆfÎ néïÕGß9²d“뼟jV’±£îïéPýæ·‘Ò­\×Bd©\/.Ït¨Ë›@Û®2ÁÁ-ëºÏöîuÚá ÷úšÜFLWU²>÷ƒÈVÙ†Ö;]=x»ÓýñgWüÞ§——± ü|ð6ö|ÁK·ã½îJ=»¬%øÃƒž³§{é'ŸÀ¯'ÆÇEûœž>ð–Ïõwÿ4Ú rûÆ#~¶ºÏ;ëë§0£Ô™…ï6‰>î¶\ôÐO¼â•=zp5ô¢ws]%?|ÔiÎüÿÛüÀœëÜÓ‘Ç»ö¹ÿxÒ£_ýÝŸzèšÌkžÕ eçÖyŸþϯú¨T«G}Ò—{ìlci.3Vgs4÷zUÖyñ†eG‘G}x©÷yy·zX9ú7_žG'Î!hr÷|x`1—(Åw‚ç§|¨ñtçy)ˆcÉeƒ¦W|̧ƒ5h%ÈØ^×]ö—|6â)ö÷ƒ@X€kQGõÇ‚4¨|4gpÈ„CB€Â—{¸ÕgX8†ýÇ{…wõ×|¸r~í¤0 –|Z¨&¦g€ÀW‡·}züç…ªgwCˆ`=ØIÌ—… ¶`#æNs¸&X‡êÿ—zyè‡Â·‘8†Ø§zÈpw3{V8õAr¸ˆ[¨wÚׇçxþ‡zgèf'‰`8W؃çur¸],Øv¤Èˆ¦èŠ®H†w‰€‹•舱€$ƒ7bˆÇ'—¶|û·‹l‰€X†ÆX퇇vø‡í§ŠA Bayà%Š÷‡lO·f€$Ôò{ï·xXhÜÇ…!‘i3·|Šx•猶ˆ‚êhYâu—qa—”0c—…ýø/hèûUkØrCWb#–0ü˜×&Ì5K[‡(³”µ7Š)lÙvr‘Œà5u’—µ@âç€çhuѨ’Ä…77XxŇƒ•ÿW…ò2ù*âd=—Ž÷só÷‘/ˆ Ø…Bè=Ic”O ¡wé‘GT_:‰”JÙ”D ãrm–‰W(‚ì#ìV§èd¸~iÉ“ii†gØ Ù“ÇcaÿdM†'ÿXž¢L1â–­†ÄøŠ˜øŽðˆoÙ”´ø0ß’R=ŒE"Ž$ù—[ †–™ŠÜ8‰Û§•¨–”È•€#5šŸ#%AVÊ’2}–†Ö÷~ÿgŠÉÁ÷™t*ÿÕº™›håJ@vx‚¦iD2}‰™˜ÑWœ—]˜‰5ø;ƒÒIšïÂ1`•žul:S%ŇoÉŽ[™~€ÿ)‰JyÍ’úöø".åg1Û±œ»§|㜠rRÓéOú9š`ixAh1syŸ*ÑB–Áž¦¥  ªžšñÂaXhgŸ¿B¡*-Ñ>u¢"ZWt}*O“$z %*.'ªž)Z¢ó·¸¢ä¤o:£*W³p0:P ›±œ‘#<ª#A ¤œÁ3)¥àBÔRo: êµD£RPrFlĤ—Òu-uS²ˆH”D½øI$`±O2&j&lº¦>¢6gbTÁB‹½YƒAg X/I$NYG²Oiö“!SZ¨ùQ,ºf¥8ú+Uÿ}ÌVª'Ñ¢ iöt&‡ƒùuêñà19£9ýä¾@ÜNÜ›[g³ýB×—x2¾åѲ к ð.’¦n2й§Åý¥YÞænþæ^ºfèˆÅWxD›(¾$>{p>žÍYœ×¤bj­8Ý:«'ú0h^×§ÿÔØödðKsœ"\éiö·×[ÉDè­ãˆË/E¼¨Y.#(É€pòÍŒLu Žlø›Ã¸ê}IÊB2Z7ØG‹/Nd®'W¾è?!\,éÀÎM_>á»ù¯žÇwèi>˪Êxî½zŒl=Q~ˆRîF+7¹10røÁ3t+ ê²€lႌá.ÎAì쎱“]ã$³¬¡9TNï¤SåàÛÞóhuÛ³1Ñä‹dQKÚþ$µ‡ Û¿TàêÞïíÎî¹ÇQŘþ”nì,>®Æm\¾½ú9·vj),&¥…†^Ý@sÝŠÞ;1Ýð,o#ß]Oœøÿ"ôN0õ¾^?°ÈŠÍÍÜ´ ­ú"ÃÁº!;kÜÚ û)¯Þ9¯ð¼ÞòP¿àyüz^ÀsýPbS]?³õ”²®Iù#dO'†c»Ñ˜h_î]ßâ)Ÿ:4õpÿàûô¨ÛêK8?:{Ï<>“ð>®íÔÄß6j©-5"˜}—x_ïøîô ±äB#ÇpõO.‚p2`½Àmì3öù*tJÅ-øÄ40ËD!ºr¬ÞÒßúg~îpŠCs™×[ã•é/¯7çãXî"í•KeÂ^àýûœÎÞâü=äIïÒ ú÷™}ëaëù ~l´ƒ) 0íÿ{ûÁ®­S?áâŸÜ ú|óõrâÙt63Ã[,/áÇ~á’“îwˆS®ÖyK' ÞõùÚä•Àk¬ 4h[4S¬Xµjh ¢Ã‰>ŒÈ*«RYY¹@äH’%MžD™RåJ–-]º¼"q +lY°%À•@5±ýLx°B„ Vô¸qã'¸YóÊA?¯`TµÕOØ^~VìX²e^ý™VíZ¶mݾ…Wî\ºuí®t妠VyR|Ø*Z+Â…oý›XñâÅK™ 4µ•YÊ•-‹Õ›¸•¾z¡â$ŒíÚ`¤—&ü1GSKjвÿZ«?ù^!Ì¢íeÞ½}ÿ>É¢ê]âÅG^¼/N¡­î1ÅpsA‚3©m‘UôˆnGÚQ©G+¯GŸ¬^îÕ¯m%jÕhØVÇ`Ɔâ=2Mi6lt÷œª*ÜhKK=„P¤¶J®B /ÄЭåv¢¯+¶c!Ò;J°Àìûë¢íRdÑ!+¥É&‹°ÆY 5ÁÒÊMú +1HWË(aK +©¦²JÀ殂’Gµl¤²Ê±&Ì0K-·œ++¨ |‹'‰±¢¹™BóÌ…6êoE69rsÅŽ/A„ÈJ=y‹i³4…ò©@A®¹Æ¾üÿ®».Íïè„s©ØL²­¬²*ÁJkÒ­¹á¸Ú³SOà2TQµÌk>ºü‚L£Qm3WUCñÅWt‘Åþ +“/W=å5¬Ìþrõ°ÁÞ»F×…^E¶Ues¬5$QjKÀªø**¬¶Ò­/¬X°–Ó^¿½qTqÇ%®Ônëº:V¸•ˆÝ?Ï|QÿäõˆÞ:º)DY·_•Ø“.(øˆ >C­;a4#úÎ?GŸ‚8âFJkSµZ!ðªæ ¤ðªÎÎõ7äË@%·d“/¾s'‹íòë!½D¼b+rü ÅZ3º™ÖÊ ÏG9h‘plõ´‚‡=¬&À–Vifå<26‰ÿŸ‚ô­áòŽ@9Æj宄Û¥O&;9j5ôììãÄ\·/(4 d§+®/š{=ò/o¼=Òkný ;ä>ÖkXƒ†Â›èö£›qÇ‘ª“%”X¥·´­i8Ú°„+pÏê°lÑéB‹@ç ¹–)Õ@VØHrœ ‘¦œùÛ9wSÙ©èdiü¼×_»B½Pl°cežéÍsÍÙG˜šê•4¤PëìÐëàC†ê30G'Ÿ­Ù„»–ÒŸ–»É9 ÓånݘÖåÖÕ©(rý“Ò¨}…ÿæ×÷x°Å]ƒ] ƒpˆu¸ì(ûñ“j%'þMn$# -¦ÿ&±e*J]ƒÈ«U•Ï„pÙÖô’µú…ªeÏc ›®€ò<Ïv¦¸UYÓØçg&úá{E´`݉ ŒÑ”Ö4ÅØp5¹3Òk¦wA§L¬%nù`è¨BÜË-B´‘©N8Æ@­6ºU^B¸¥ÙìÁOczÛ_Ê´‰¬‰KÊ ²¯Cå Œ\šfˆ¡¤P2q "ùÆ8ùÇ‚_±^’ºÒ–³M+J¡Ô¿öÇô섌d|…Ôw-ŽM\÷SLÜBôVÙŽ5qÍ+Mñ²ß¥Y©á¤ž†W4Óô>Ofbš%E²hðzaÂØ€¤-­ˆÒ}^É%oJøIò1ÿéR¦3ÝUFi²+X"‹ŠSv²‡íD£ ûSÊVõ?>Þ'šºdw¦ÃNë %~÷'ÃôÙ¨ÿLQ,ÆÜ ùºF-õõ%™ºY™·ÞIQ³dL¢V¦jcªœKtwR‘0õR‡Ø'Fù¨‘`ó! –TH@[h•ˆ8¤–h‰Á&Œ¤<³Dò˜mIé&¥›.v.¥l¨CE•=¬H&sjhBE׊B;qUCì£?Þ}¨WUN~j£@"¬(A!îĪ;(VðC¾±)¤Öh:ƒj¥‹;µ([¶ºÎ Õ…šS’2Ý*  ~r…ÀôhŒÓCŠØÒwÊraR–Ö¹ò‰hÿ$Mbd C'Âô‰Ñ›éCÐÊØK­q-SyÒ2–0‘vS5‰é욥­àf™h1PƺæY2ž/+àaXþ4b ”§¿¥ìåfèOÔš`zÙV bO²Žu¬x¼‚cfÌš´5#Ç6µ1žŠ²¡Åæj³¤±¾àuk¤Å®PÏg…V^Dq¯™!_NjR¥W-¼+.oˆÈ["Ú²¤/•U­¬³"EƇ&RÕ 4­­$t’8UcE%Ó3¾lªC›\¨)Åû¾Ÿ„ò¨ܰ\úönÄ>eº‚rÿfßh…$¿•éÓ XÅ;V)êLæ|SŽãÄ('E¬œƒ ´*0eÊš^±ÿšTj2›eJÄkI©j+dÛÕfK+W_ˆçÂ>nq ±þ}O4êc!ü¾˜¡D[×ÌèÇÄR¶¹okßj®­É¶Á=1¥|x­óvh6Yþ£'-äˆIyŒjT&m”ªåº,Z\L5óX3nÙm/ #–Þî¶i~ЧªT؃²&(%wA²ïö<|©H±Z„#ìë]x‡ºÙ<%ËKõLOËèhFoØÅ‘ ÑâH?‡Ü©ì­Ùfp§eÏÉM7yÈ|â ¼ÎÆ¼…Ϥâ*’‹Á–A}Íæ‘6^‹xƒÏpc=*.î¥C·îu¼/F\`¯dp4fŧíö:áˆùœÖt§ÿyKGƒÚ”Ì ˜`{ WC™¢TA_×P¦Ú;ïlá-鱆nŒA…ÅÚО%%o’KÊŠõnÉ.Óì½X!£nvvÌ)H§¿Nö–»‚ÐZ0GÚKQåÊ;Þ”&¹FK†ðNjáVU†ñ»0u¸]¶–ÁRo qù™%Ç:\NŽr–ô‘Èeˆ*ã9¡ “)Ó€»¯*Ym­õn¡ÌmJ&/›¢”ÒÃfèº0GWŠ­ºg(èªë¥…YGü\fÈõ”ã)1wZe¾™ÒQˆèð19|ÙLZÊRšR ÉnÑmÄS•ärÚ*]Z¸…–Àñ½Kÿ{ahÌqœ0.˜;·¿ÿ{Òmñ)yY"ù+]–ýìü“3=ûØ«0^-QÂXçI­¢Ž Õz(¸HFœÆûíÖŠOR¦aÐC徃,7ïÕo›ß¿ÄË‹)Uo)ßšÖÈRŠ V-_²¸(š/{·“¸½¬h°;¡žˆA‰Ú˾Y7Z•ï›­mÉŸ1óCˆ9•Y¿ ”‹_k?–ÈŒ}ŠŽ@ØŒýa 3Q1¶´©(Aé–—.&¡$‰Ú«Ï 7àØ>»è¾ïëô1µá"[39‰¡:TB¤ó½>v,ެ<áŒBñc‚l »øò¾%  l¦Ã«]ÌÈQ,3“ÊOAC–𸕔½öÈ¡l ò«éŒðk°¨¸Äã‰JrL7tìÉõƒÉ äÊ0ÚJ“Pï‹¿+›HƒÑÁ°I  .2¼Š\ŸèûDתµÔQìäÀŽ»)“ ºãËIêKNLh ̾ìʹ ¯dCÃX¡ž¹D*[Ÿ3üÊ’°=Õ)J¦Ú–²ô x<ÆèC7Œ˜›„ºÐlÆŠM¬°¶j+»ÜLmŽ(± ¼KÎê+Gó³2º½½ôËÝLÞLÿh*LAÉ bÃÛ©X•êDÚ{ŠhÌ †œ‹Äì8„GøàÅj1 £13J±,MÒ¬(jÏñÌÇFŒòDÏ×üNêΚìµhD(¸ë9QŸ)* y‹ˆ#ÇÄšHÁHãN¯Î漓ÿ[•¼(!|Cƒ± æˆÉ ¡ÉjÑÁ­øŒ9DÆÌɖƒ±À;ÍEMð\ÏÐ4<5O$¤Fм‰ÑEM­°hÙAmª c¨]‹MÌQ-Æ£#(µ”ÙœÿTò2(ñÄäP+AÈ Û!ªLP«¤Ã­ÄJã`ªÆ|ÈðáÒq, ìDF±T-ÞNÕ1•^&Lõm¿ÊíÎìXeáFmQ5‚QÔÌßáe2/±œ³ÑšÃ¥Ñ¨Cµ§Ÿ/ÍÏbß”`‘ÎjN ®à Ó¿kLlU=â’Ú¸°ØÜx@/EÊ?;Íö,•‡$´„åNÝ-7¥Æÿ6ã%cöà>¡h,ÔC=>#@‘¨gÒ” ²píÏ« â"þ§ä%þŒù`_A1 ÖÐw[U7¶ÃŠÔPòŒ£Œa]Æ„$_ñªä¶ZOÒláÐ,àêÍQ^ãŠzÑSfH‚̦‰ŠO¡|5ÙkZ5íag¼Mää^É]+ÆA‡ÔÞ ÝcibÙ;) fÂom­öYØ‹ǃô»/¥fá=`¹`æ6,/ kÑMc4®=‚=—Ô«”mÆÓ±yÍñºžK&ûì"mÊ©"T`呤3-E}Ø]æeèm0VÀN•ß­}bp á%GÆŠGD·3Ü¢ Þäø£Òÿ…¾æ¬3νÝW­ÎèBÑÃ)S˘y0%ó9Ñ2­i¹ ÕûÄ&Y°ÙÐg¡4¨`vÞMÌ`M¡Üªò¹5øŠíe3´âyžmN¸2¤_jžèÝÛH“àèj¡Ï7ìO†X"œÊa ­„«Wï­´8éKÜ~%в²ÁË[¿mi°XZÈó8u U'Öf 4<ˆ ^àaÊâ?¨œÖ-‚QatË…Fè£>4zë«wÃÙÓó3¬¹‘à 3å#E¨ÉÝ+„Ñ…ár†[´ì[ eÝÌÈË–ü2dIÖ‹PÜÐÀ¤¶†dëœ@ƒ!‰¾F\§ü>Lª°:žf‹¤íÌõë’óàécYÿIaíþCÛ$Ô9´%Ö·ôÏõ¹8ÕåìÔUî Eîæ^nŸ5k×᪭µ‡\Žl6Ê…ä‘P\ãØ^Ž;PNþng•èÛF¼I̲±¶y¬å%ëçvn¡=À˜ÎGœ^=¹­FÓ@5¿…¾Ae¾½Ùî‘[–GŸþ^óN<ç]“)ë=™[ž:B[=þÝb£¤îjN­ÞÆ‹æ ð¾d¾vVïµáú³È|±d}pHmÇ/mË+æMÖA»ÖiÉ •ÍiìjfPïµH]ñ ÿ'צf93\Ó”=Ù[,jáq°äp yIæˆhòî5?¾í¤ò,7ë^¶P–|燖G*ß Éðÿ¦oõÛfõq×ò6KZÓáÿÞÂØp¿êœ5ö[Ötó?'JçëÅ|Q½qÕ–L>÷ë=ßÚÜEp,ôH÷FöMdŸ‹í%ßñÓ® °Äóò‹_©ó;ófsIÿóB[G‡.¯ñ>tŸðÆN7üôrôp¿FqRtA×â¡.Ê¢>mé» W_ ø…^nÍÌaœèQ·õHÇ] õU¯qoTôãxÊ’½¯'Çeæ¾±ÐÁ+s%¬õdWöÏønM×pºêî ÙU2u ëª4í¹dι6Óow&}IðŠ\n£‘Mï _ŸÆSêj ëÀÛ°*6JiÊd[z¯÷¡Nÿ¥ Îä U& F¯ÁiñoM‚Ò=Áû‘Õ‹†÷—ím¦‰-Ý)&3æˆŠŠ¿OŽö“ÉÚñeL9vtt3Ev’Îʤíp>Ø´$ÑÓ˜zvù£[­^Ö]ú’gI»?k1HçùNI:8LJGTV•OŽs9(£·¾s7!Í©öÙß=óÒ±Ýó¬ÜùªO°Í „¯îƒ{Õ!új± ­n'Ä2lØP§ÎÌl-.E{t{=ðfŽèábøß«{ãØÅRZѼ¯|-sHŸfâó‘KxüD1ZBÄO|{ïN)µH¡´z|±‰ü‰~Z <ËОÙk‡lýx[ýÿÑ?Ga椅zýžUUy7ïÊü;v¬Ÿ áňMáZ¿Óý}îÝóR¿ßÏÑúÎþ¤¢á$Êú‰m©W?ž¬µOV,Çb§RçG<Îýè?GšVÓMŒº×Çï×>íßþì~*êYEAØ,hð „ 2lèa«,ZQ €M‹+±µ"@à@Œ‚(¶ºÂÂà•W²léRaJ2gÒ¬ió&Μ:wòìéó'РB‡îTIRJlTf$i²cÇ,$ jUèR™UµÒÜ:séV¯WeN|iö,Ú´§nd1qêÔ‘9¶j:7ÀÆV‚Vìè6ªÚÀiKÿŽ-lø0âÄŠ‡J¤˜Ñ$‹½WÊÅx*G¤Y#Þì¹kM°\[lú´Z¤õN]Z¶ñÜŽW–\MqrÁˆ|Qó~x…3ðà‡Û˜uíÛeõ~|Û5qÆ¡„­êÕú×Äe{sïŽðxÝŠqMJ¤=w¯ìå·ArœêýýÁºÑçÓ¯oßæR±1žÏ8פ@˜•$Ñ}<‰E‚ÔUW‚‰•„¨ÅõÜ^ù¹&{$EF^$éµÛExEáTšx"ŠW-E]%•å€NmU]›¥h؆m7"gE4QE)Å…Mk•™F#Õ¥$‡sq$bÝEt#•UZ© myÑ•ÿ^‘Hב˜‘wå•EyfKl9†á…åå!z¸A†æiH‘¹'ŸÑ­(ž€MtmM)Ç^_6öi¢Dx:ªji†TF­d’y‘)Ù“·í–ÒF &È¢¥šŠc‡ØL6[lÿé¢\.¶ªÙ©>(*®àE×Iš}ô§ sº8'!¾Š«Z%Öº,³EÅÙ¡DÖ9,A†²†RŽÍ ‡(²hN(È^ªºFyÍqª—‡‚jê"¡°ÝÙ­K&i;/½)™’qDF6«Oîw™G,ÐKÜ­ðFÙÚH³MÕn£º9e›°.—ßœ#ÄmA1 ¼ñ©_ ×JEj¹äšZÿž¢Å#–§¦â©d¯œ çusG)Øj˹-%WH”ª>ÿý¨ö®T~”Zºû×n™>RkS¿ À¦|§©Ýj§3Zny ’ógÀù °‚XñŒÜTž·ÈNƒ3ž[(BñYð'u3 i"2»î=\ÐùÒÅ¢òM©„6$J´:D©µ)N„B°À•=ãŒä†=qËKB…ƒ+Péq!ºx;'ò¬|¿1"ò¬)‡\×{bex³æà :YtVâª%&†ã ¶¸t*8[¢ÆÎ¨Ç›Èma¬IÊÒJöµ ebÜ#uŽ¥FíˆñËÓ"¢¼¿™‹Š˜Yâ!iÄ-näo ûUœš¶¼Ì¡«5Ì™˜&޾üñ+Žÿdˆf¼'IªgbL£Þ®˜ÊTÊí(ÇëcéÆçr—:¡Êä8—·W*ä”@Úb%HÅ%ÊǘšTIÿó—ÿ½ÐfÍ»‹!3iÍœŒ™ )ÒÌø?±1S—ãÜã í8 ÕˆD/Jb¸è(Ïu¾Ó*'4g3EÁi2³†ýÔ£qF¶¥]%)ŒRô%±qЫ`  )IE-jƒN¡\caÃXKaµˆ"%Ê25ªR‰ó¤×èÐX*Ô‰.e .WªÓ‹æñ¦ðT×~G÷eSlyñ)V„·ÓWê¥!Cª1/ô09†Oš­‚*N—ªQ&°Nj)Vm8E¡Ž´¤F5iÿX±¢U.™lKk?ÁB(B-l Ò„+sÚ3$¤iD[]Ì¢ÈE¯75¤,×y3ÃZåHm´Xõuɦ”%‘;ëSÎi8R•”àõ˜¾æNx⵸=“PÕ¶.2ÛÚìRŸóÐ}mTšfò—øôôÖÆþs‚Yò›7Ä:¼~Rh2²¡»ÚŒNvC”Åëÿè™É©^´µÃ-_Ç2D^4¹p¢l‚n戻ÝÕž"O_G³iá ÇÒê+gÝ­o['Ívf$§[‰ì,Tî»àá·Á®ð…3\ߦÂ.q¸Š%â©óÄ3ŽÜŠú+U¶2hBsçqšÐZù8=Ž”k¼å“w™´ %{&æ¢I2Ék~r;Cúw<ß¹£inq—}QÙâôÏdr!ä])&Òuîé4£9æX.:ÖÏvôšÎUMÓ½Þq¦£²æ'§¤#õµãUŸºÊítГþ°‹öyéS¿qÕ³^[;opensips-2.2.2/modules/seas/doc/images/image018.gif000066400000000000000000000202401300170765700220000ustar00rootroot00000000000000GIF89a)Lw1!þSoftware: Microsoft Office!ù,>‡3f™Ìÿ3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿÿ3ÿfÿ™ÿÌÿÿ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌffÿf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌffÌ™fÌÌfÌÿfÿfÿ3fÿffÿ™fÿÌfÿÿ™™3™f™™™Ì™ÿ™3™33™3f™3™™3Ì™3ÿ™f™f3™ff™f™™fÌ™fÿ™™™™3™™f™™™™™Ì™™ÿ™Ì™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿÌÌ3ÌfÌ™ÌÌÌÿÌ3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3ÌffÌf™ÌfÌÌfÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÿÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜÈQb€„ô dÉŽ(Sª\ɲ%Á“GV”)ӥ͛8s²„i§Èš>‹V­á¥Ï¿|I¬\ÓJ4^ñŸ|ý%Øœ}¬ØÊ5BÈ~ V8I,B €ø ~À‚…$:W’ ­`󡊲˜âƒ¦„¨ q4~Uœ5þ–ã:âèc@ò(d_©pÑDá’2™ä’¬ÄÇŸˆ"²`å•U^ie–Zr‰¥–_‚¹ž˜cŠéå–džY&˜j¶™fš)™t…ƒ.Ö¹â-2&‚ހ͟€*è „j衈&ªè¢Œj Göá'à5LNú`¥Rj©€úIžŸ†*ꨤ–êz)ˆ-âùb«°¾ÿzŘéjê­¸æª+¡WhTÒ (^#쥓«éƒÆVš¬¥2žgë®ÐF+m¢#R5æ­ª¨í‹ÛvËí·Þj+¢³Ó–k®¹½ÎTÒ™*éî¤ïV/¼šÊ (+Vð‰Ý³Š¶2+»Ø°¨¿þ"zÅÁÿyŹ€.œèª§Rd_¶àV®Å_H”Õú*Š k+²Èø©ñŸ)¢Ü0~/.¼pÊ*œ2Ì€–sÍ*ã|s 6 jº™d…)Âb£ä5,"}tÒFlÓH?­$ʬ”ÂJ ù’Û¨ Wœ|_­ÜWðÁ\Ìn†gø2Šçp×wÍnØ £hÛ£€ , ÿÜ06yª¶ |Ÿ¼hÇ™T2Æ7ÎøŸ¦\-¹•Z7êo߀\0Êù݇bÙ&³Ë5Û Œ¢ÏgãwßÂesý'á(;Ì.ë)úpÀ ×Üùí?#øßªHðÄo|ñÈ?<+‘G^ÊóY›ÇYÀ§>;ÀùÉí:ꀇoýànÞõë3C¼ºøØ;Ì5öºŽ=£¨.dÈŒçïx·6ùÆäXåu°îeÎt«:6ø–ŸüÀnv $œ¿?öYÏl*b]Ãw¶Šnµ«ÞËV0¾ñ®Q@Sˆˆ¢ñ'* P.4 ÇÂÒp†0ô_ƒªf « úºÎôÿv>vmUn#ØWÕµb |"Ë^ºœÎ}LY782-NŠŒ[ö‚‹…g »µÆ5Or>œâÆ3ÄP¹íVÑü†®†©j€BÚŸi¼Aò†L$6vÈHæ9²=ŒžÇpE3S5°’|,׈ÿ™‘j eŠÐ¡h˜¢¦0¥#¸1ÊI/“°Œ%£R˜?6()#d t‰HB%‰­&##× HúP’ŸÊ•íU·‡õm~wDY†x6«AAst¥s_ÂxSmÒ0 f46¶Æ¨/EæLçÈÄÎh°B•ÿ«šäZùMðÔÑŽŠz™ÁfFÿÂiÆ.Œ¥3Y L( †Ðm„Ý2Eõ(ûÍi‡¨,&E)3˜Ô¡ó DëÜ“ZÕLÙ2G·fÆÍJ*²’UWMÚí1ƒ‚‚]Ãæ'¹…ÌzGÌKU?vÒÁ„P)Ñ"u¢S[„|ÕƒÞÉTTÂsžràÏJV·Rñv.ã]ØšÕ±¡Ìph7C¶Ì&ÞuæcàNIEKÆÌicÀôPÕ¬Æ( ?@æ‰ÉCQª[ )ïr6¦msØ0Rߺ÷"&þ³f ý›ÿeM×á4‚Ëݨêi—@tjL…€X¡`vgJm;ßùÆÅ‘ž€]Tù¾wPÞÿŃXUw[3ˆYUeØ‹ÂÆ­àÞƒ\UdQèÇ9…(Cx%óa5BnÊIÅOF5ÚWdÒ±T»­ÚR$:›Þö}¹]iá„û· Zé|¬Ûfûê/îan²¯OýøS=‰È òÖ8·+­Ö”Nm-T]9É\½tTú%˜,£ÕVÃ<4˜ê0‹%â=©IKºë€„‰Q³‡Wó®x>ú1]Õ÷Á¾g_ÎT¾3vH¼Ø¦2µTÕºó©®]ðŒ»Ãâ]ìù#ßöP²¨è¨Iž]§Œ$í–¸‘o|^)Tž"ùËÐêi-; 8•Âì¹À%]µ)¦5ÿ•  ƒ“ æ:3¬Â=™“€¨Ä.aE#DÿÖ]9ô!I±,¯Wæ«1¯ö×WÚùÑÓrQÈL- ¢U—Óg„6=)v–’µ«”§œ%½/CúÔ³lnƒÆŠ [éôŠr•gí!±l»Žt^$;Z5‘é×À¶°‡MìbûØpbËO¤¤=dXDe×jUûéO#Ρ5zvÄí ÉÛÝR¸¿-npËÇ–Eš ô„¥®QŠI#Ž.^ãŠèaîpÑXãu‰ö ÎSºy¸\ËÒ´åé‚SûÀ ~­ùÍð™<´A•+Ž"Ú:»·µ^3šeŽ6üãYvPé³V/Œ‹ÿ¬vhWþc`b;žÚ¹ÌUMâìri]û°•ã½ózïõÞ(þ¡¾gN¢4û»å¢UjPS俦·üé Ž§ÂçLôª¿äá5¯2¡9¤"{ý¢^ßn–wmõ²ÓØÆî,%]¡Eæêâtó)_UØšÝìèŽ+Ý>Ô!¼Bhã%ã‡vçvý{ÜÅøWPtÐ-|™+¾é­ j Ž Ü£2DÀÿñE|ü_@À˜@;uRW?AœÏú¡ç¿ÔFŽéüƒHDþóýc¡$£kª÷~Ö·liÇrÓ&yO30Šà€ø€ƒ`$¬Ä1¬@€G|<T¨|Žd ^‰à $X‚'h‚$h‚,€}•b,àNÈpF× H‡€ŸÖ}¹6!çG ‰àƒ@øƒB„"Ò~ì4ƒûöG{]zׄ·V;d 4ð®P…´P…®p…Vh‚_ЂÄQx…„%âzz‚-8¸r:Ø<â_ð: ‰à-®Ð†S(,ÀJÆTbXtsBLV ôöuMòsR8ü׆_°‰ˆˆü§~<ÿd+ "L{h!F§ ¢èt(€W³†å‡‚Øà ÛB X~E¸÷Ãr•8#§a­‹`—n¹Öj0~ùW‚¤ø'UØ pH~¼w-O(L­t5Ñð‡¸p9'Lî„ ¯Šè ÜBŠ­`‚æ—vøG*—vÅØd Æ7€ˆ/"²‚qîä<¶Hù‡U(´àƒ'˜~.ˆó†ß8%á‹Á¤9g ¦wøŒAªÈ†p胣XЧ¨ÒG¸‰‡uòPW°2xìd5‘c ¸»8ŠÔ¨‚]ˆÃqÁD‘ómLVò{¦@ŽwÈZØÀJÒÿ8Ô¨-Öh‚s‘g(ƒ,Ù’iveäWW³‘øbTˆ‰´ ôH‚éƒøoCI”Fò’¨$Ž,`ço79O Š&Ø/bŠæ§"’€?–•S`™Q÷•;$T=d ù‚‹+8’½ø‹'9‹Ú—ðWÉ<ð4NW “c @e9Õ¨"Øx~vÈŠ„9#%14¸q­ðš%:/"6s3Q·"¥1Ñb;ÄuS\³¤è³2»7MõeYÐ"¦3!£ÐBBúu;¾…¤¯ã5 äA5-r:§Tª+Á…"-SXgzBb#?Jÿê@S§JdZ¤L„M°ÃX5P2%3•ZD©1©ujM.J( ªtZ¤ƒ¦§vª¨:¨¥K®úªS)ÃE‡â¨:Ã0¬Z(HôD‚2«!ªÙt¤2®ƒX祤*B†ºLÂêG©Aàó2÷µX ôb ä7ÙÃD´M±£[ðe>؃PžJ7§3_¹ƒ=Ñ*­ƒªA·“6 T£¤:ãƒYLTXÇÕ>ä%?Ä¥±ãU7u[Î 7€Ò®îZ«‚CY.ƒXïµ:—š¯H´{³bBaÓ£ÕVõÅUéSf'S_òú7£SIk?Tš;ò*¯»³@€2¢C:˜ "aå5 £[ÿE;û[Õ(q3Q%[K©j;èå2TR´{¿ S+;#e:dc´ÆEX§§#R"‹I? ´°ÊPc±(Y«µ [* ë©_{(ak[{¶ “¶È±¶#·”´ª„Ò«ø´«%ã¶o+…2?ËÕ/Ïâ^5íµ5gv£Üj7ï#z»·t« ³ÔXPM(%Yåú[ÅZP2:¤åº6ø#>Ó¸Z6=kB¢sVŒ5;ÉE:ÕÇ_©v“ÒC …rœ1Ôu·pjíV ò\zW,b S`’ ÔtwTwÚ"1'Æœwc¸$hFÃwØ%b>gb/(t¶ÝY9}ѧ-×ùšV{=†Õr—Ò–mÛdÊUò{—"@´BÿWÅàmÖ‡Ö¦}ÜFÇ!O ß²8FmÒ]yvMÛ¯üÐèÆB1’%—vËNƒqô·Ô‰vؼ|ÜIÑÏØ9í–cLwp ŽpÔÏ%ãNu¹%xõÛòÆß`wÖÓLЉízÂ$Z¯crç¸H)”ærÀçàæ}aô&)1“o§y•íÑÅM}Nà+G1 ÃtPgm|*-àEu`íÅbF_·ä>ÞÝÑYìáØ!†rnyå×–âC½âFmsSæwM>‹ƒ×ä6Žož×××rÇìäv¬u¦çZ­âÖmäW£~Þ]Wz€Ç‘N^Úg®ÖT]‘ÑÿÝv¬%NÞ·è‹>ÛÕ}Ú{ Ç2‚~žÙ2½ÑÆMä"¡ãѰ„2‘‘×}ŒßoîèBé— áÀ´„yŽ|P¸™´þsOî韎v ècmþr‰Ù<¿Ž˜q¶åuÞå§yhô†éµ¾éÅè\ægÈëiX‹oTíÔ~ÏÄ.év~á–nk¤·ì´žzùB D~‰þ)‘*7í¦ Ö¾îíÎî MOïTî©Þ`턃HÜ|µï?GÜøÆ+T9îoú9pÞtcO /9 ï‰ÙÇàØöYìW0—,LNb]‹ÜÅïo¤Cð\‘væýS{ ³`+wЈyVG//jòÿóè’ÓÖêWqaº÷}Fâo‚ü®ïý>]%€h ‰ ¶*ÔWÑI tßy9ç(ó0íq´„í|ƒXÉÁÊZ䙸‘|c阔m¹a×ÞîÔÎ|s’’MXÇ…öêᬰM„´L%6›ø÷±ù«fâl¬0—{°†Q¡iQÿ2ö_ÉWK©k’ÏC•/’8ãùˆ•b|¦pú'MQ¡"qn äòжÚ¨pŽY%`ðW®rY¬0êcªtÑ¿B—ïµCgß‘LÙûVÆw(˜b>ŒJœ_µC“$+mQÂŽ˜¦o}8€@Õ™f¯†Q¬¯ÒÔ0†ÿsÈ¡Œ”o鈞I]/?TG|ï™ùÅY²ß²†Â¸1&¸Ô<¨tQ¯ù÷ߌ莃ůVqE‹VذµºbPáBl>„1b€,X]Ę‘U4S¥<š d…¢ÅV­X™DYòäÊ’&%¾„SæLš5mÞÄ™SçNž±±¸’Ð …W 2DÊ“b€+X™jeê)GlN™‚¢b kYuöÕ+X±ÑzžE›VíZ¶m…J(Ç0} ’³( ÆCÛD³²÷£SÎEKM“Òy}•ªÇ„Ñ> 黇ëq-?U?ÿõŸÿÜs»8Æ_ílšÂysúCàþôFó½®gî!|R8Ú%ЂãA |HAÿôI¤o’ÉþuA‹l,PšyÎs@NPzÔ²KòJ8ÃÍ=w©ó˜«y)v¦c! …Ø–y­9>‚›hè¶ì щ/©Ï³ºe?9or\˜P^À'vqA#Iv~õ¿ölOuáT½èDÙÜå=W¨H£ž¦Æ™|,ukÄ£Vöô9s9 Bt¤ ‰Çj¥KÌ!—„dC1õE…ƒ$$ ™‚½ૠ—„##»ƒ™ Fò‰i[K<Ó¶DÚ%ŒBòN=ùÉŠ´Ä•~bÊ%#¸ÈUÖr3Zå+¿gÉD â@hѤ-‡H]ær—|<BNÉ­åL˜Ã €Û\陊œÿç²|cE¸¥¦gÚ’"Tl ÿ†BQvmt¾Ô䜺ÙÍoº … 4d¿0MlŽf™Lúå:½YÍôq‰\«‹×ªešLÆhNÎÔ'+GB.—íé3ŸkY"“)o©3¡ì”'cxÉ–qÔ¦1Ï”uÑ„¶R¢^£Ð$-i^γ?%))PW2”’ý$§•Ì©/­Ä4¦ - SVˆÉ„Àfò<êËdÙSŸ–”žûÊ)¹nµ87Sª?iS˜Ñ~b#ŽJeB.i:…ÕS›Zf+½˜ “ ég9âK´î3 B!SBŠÂ“ò+™FmI°ÉÔºr$áÄK}dC—X2œÿUE¬^‚YX ó{`$:«•DVU°Óœ¿²JÙüÅÒˆpä«/¥cȱҔnºŒæÏFKÚôUs—ö\ŠM¹ÇP~œá¿hëEÓ¢S:u‘NAˆ9Q]>ô•ZÂe+†+ÄÃ"–&#(ÅZ# ññ¡4ÅVÅ:]IfÔ^ªV{ ÙWªv‚ïlç%É[Â8Rñ3 ü‰!í©T™¹V£AÉë7‡zÅùª˜ºˆ\kZsÓÁ,Mæ%KRà ªu OÍkf«9Ñ«u÷Á×<)…+üTsµí3Vc0—¤ùae’ò•"N Z%ŒZ?¡ö©é.Msl¥¥ S¾0F euÉZ#vžÿ°i#œL ™ÁØä—ïšY·í¹í Ú$~ˆí–H lPqÚäl¿µl!- V_œ6´aÕªU½TXÅ:•€¨ k§ÊÜÍÌšePn‡ÈEê7[¸™$9>ªY×6©e¤ý˜ˆYñVŠE_é]ÑU!:ljU3¾¦—.üXÒx¼†åmV¹ÉMÞfT¶_äl‰s<âK”ª¸íªd}{Rë.9´Ûôî”\ÙEbUþEð JXÚn¶H¿ q—;ç=çÍ>Ì¡ó$ ;opensips-2.2.2/modules/seas/doc/images/image020.gif000066400000000000000000000202741300170765700220000ustar00rootroot00000000000000GIF89a”ów1!þSoftware: Microsoft Office!ù, ç‡3f™Ìÿ3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿÿ3ÿfÿ™ÿÌÿÿ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌffÿf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌffÌ™fÌÌfÌÿfÿfÿ3fÿffÿ™fÿÌfÿÿ™™3™f™™™Ì™ÿ™3™33™3f™3™™3Ì™3ÿ™f™f3™ff™f™™fÌ™fÿ™™™™3™™f™™™™™Ì™™ÿ™Ì™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿÌÌ3ÌfÌ™ÌÌÌÿÌ3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3ÌffÌf™ÌfÌÌfÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÿÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿH° Áƒ*\Ȱ¡Ã‡#Jœ¨0@Š3jÜȱ£Ç CŠI²¢Å“%Sª\ɲ¥Ë—0Kž Àâ ”1sêÜɳ§ÏŸfj­• ™@“*]Ê´©Ï“5¯±ºÖJê5›8jÝʵ«W„'¯°ŠjQ³­®dýʶ­Û·)iZdѪª]«w«Þ\ ·¯ß¿€ Îda ­a¢vϲRË7°ãÇ“î ÀÊ.6˘©^Ö\wräÏ C³„º‡¬Ù²DQ›N}Ú¬ Ï¢cËž-ñ¤ V›qgÖ͹7ïV¦ ^¤M¼8í°u¯¡¦Ê|ù]çÍ™W ´¢±ñëØßμRøwîï»Áûÿ¾|Ôzöó蕞½êµöï`º|¾rªiͧßÏÿ%TA—¨›€h`ȘEý5è`\¶ÑwßreU…b8á}*×Ê^†(âFP]ÁY‚(¨bŠBÓˆ0ÆØÐv¼]³™îݨcŽ<¾·£=bƒ +¥XÁ ŒH&)Ð^,œ¸â“,B©b4°È/*©¥ˆP±‡MY6‚ù¥b–Iæ™a¢9¦B¶B¤)¥yä–tî7Y]Qæ)ež¦Xéç•YÖ)¨qƒhfšˆ®©è¡‹ªYV›¬ôYŠ)}"çpƒf*Ûd·}‰`4Ÿ†z ¨£*+~–ò§•X² é«¡…ÿõ¨´bSë­¶æŠë®ºöZëe¨¢Ú§¤p^ ë±ÑÄŠžÌîy ¤¦éªUê*²ØþÀ € ªlz ®âæ*ä·æ–.º¶lŸ¨Nªª¥Fbší½mÑ瀤zŠ[¿ð¿¬[4¦D3Ö°©RÛ*¾³¥,‘¸…k1¹«qº_榛î¾;­¼ÆFlòVú"¬ª¨¦²Ìo€8ZUÙ‘"L$µ¬zòÎê­àî¤c,ôƇ{®Âu)²°#SjeÉ ¸æ°ÇEXZ”J>Õt@ãÖ&¹º¸;ä`OŽ°Ø–Ÿ]mÚ±'’²å‰•4bTY-érSˆÞ©ëÍ7Ã$¿®ü÷¥ÌB 4 bû‰¨ê1©fr{g s†KUõ°66æàç?ç¨2ÆÂP§«°â6IÈt«IZeˆ¢:î9^sÒŸ#Bµ±â&7©Œt>eÀùFfÂ+œ´*u³jåìZL¡CÚ63 ^¡JÍñ®ò"é{zÙÒø·ªð‡`!ÌØÿ §»ÎàiEmÏn&ÂHgÖ¢Áâ3V¼¦Ox¢ÕQX  ‘)7gžo¸@† ^ošÔ¦ÈÆ ÌÎ(Äíº†Á¼@¶ªÊíj¸Çànx”k˜ £ØÆ6Nì¯aÆÒ»òlGLc<]b“·™™Ñ]<\c!˜²ªØ„Q™#p@y’éµ ~¨Ô#ül7¿šI ŠÈÛ$'«½×¬à&·YÍ{X¡È~QÒ.ÏKNÞP§´6­Rš”e +è1A¨ƒ„RñBÍ>ÊŒxd›–Ù.‡Beª…¤Äi‚ËçLÈc’ÄÞ ™Ã6­‡öòæ ç&ÍBV¸É'¡pÿDß1Ñç ›@mV9mrSž?äŸ(N¬Ô”V gˆ9̪³Œîd½‡P VRÉ£÷ð̇úÓ Dé@Èm2–Í ?wCHÉ1ÏÄL0©Î¼µs{ïLfL•GOâ%W3{ÍfÚe»¦5~3#hý`‰¿¡&î¢èjÈõÔ\†˜ «Tb!“£V%êìv¾¼èCìTåêTºŠ°xM+øfjQ1Ú0oš±¤`ÛIؽÓoBÕkæŠ*×£†§*L}ªSÿI<'Zî¥UU¬àê¹âg’Ÿ ìBÛ9íöŒÁ’—ë|¨ÙÅ®¤•éâï²Ç/ÿ±õ·µ+ñ*KBû²µ°ãk]¶h…ëErHK®aG«\Ô"­À \Q#·DxA&¢á]l[µ¨z·»à¥ÜTyÂè·ŠYå¥ á…ó—¢í¬r™{ZLsµñ4¯Ú>ÊÒk°À d@DRIM`rˆ.\‚³ÙÛ¼êWº,p×çX€.ä`’|^Q«º{XdÏeíƒyÆXÇ‹)=ß—è§`·2a-¶¬A1ÛÍG³¥åé0wÌáûؾŝׯ߀¦ôÈÈ¡´*3¬%+^Otéýj<ä“ WÇ;Å^Ìå¾¥QNT®rĦ;WɆmÅ®L3ŒÕ,U—ðÿrWóÑ,°Ž• ÜòêPkÌþé¬4i…œwFµÛô·Ì”ÊÆh/7úÑõ-–ÄA›lb(+™ÃìY7h2ŸGmßó³2–†áXQ‚ÐMÑcY´£g éZã,Ÿv[ªñ…c›D ÏÙ¶›‹Æb‹ŒiÈÞ3å5Îò)p×Ù¢Úö° žš®¸›¬«DÂnûÉÛÜþvÃö…“À0~ÐÆVÛòY8µT©°zƱÆ4zϺޑ2Åd"ŠQ7¥Yô¼àP*Jy—•C’òÙ.È7oAW0Ÿdÿ#!ͽð¥ÛðiG(wVÞÊö2¾ç½çzQÍÎxÿ¦øƒèƪíá s‘«û”O.ÂXÛo8Þ³¹Ö¶SyƒF·¶éû Ëçô6N.Î ä¶Žú´^n‘lúÓ„´ÐùCº¢ ±p{¨"•Œˆ;i ‡¢Ú颹\áÕpßzz¨G®„àØŠ¹v“)òi,~ܬ7¼ªßì\£üð¬ûyè^÷‚Ì$Ð ûX>ß^¹V-RÁCòí›M8/}Ÿ„ßp7³â³Ãx¹ äñ§gỬÀ-w9³&Á†ªfߺ٧±„&·B¿±>Øìþ:¥—Ûd"xTmÍuåüp³vœ‘ò@ènyK·<õ²þï|º¡^0 5ÿ&ŸºÉþöe§ý¼LÎEÄßisPòÛòáП‚÷ljö·Ÿî—Î:¶y•ó1W`$V°t±Ö|U‚k!G¡—hRÁ+p à‡q±ügþW7Gaws!G¶U‚*²'((/pÒl5ÑiY÷‚Õ‘öÒ ’_1'4ˆƒ¬¥"vƒ&aƒhUH:ò—Õåfk¶fiG^z0çö€@78G¢ƒógZx…Ha…7¸…2˜5ˆ)(A|©÷…^¨5„£ƒ3²-‚ ~ɶ,j4)PxH'VÀæ~Zw†Ø÷‡8ƒöˆX„eè…‚ˆ†h…„ÿ¨…æÅ†£óIoèv´6-Mh%&qR(…Ž7†‹èx¨ˆXˆúwˆE—…ŒHІTøŠ­%‰¥“ØàL+4V ‚Kƒ –'å6 Ô{/8X2ŠgˆzføŠ‚øx>XŒo˜…ß'ˆÊÈŒƒ‹²87¯ Øq1ÜÂm6“p ò?}‘‚¸ŠŒ‰£ØŽÆ˜Ž¢ø‰îøŽ†È…`¡Y×H76!$tÁ}øW”‚ èt劉vv}ò‹§wˆÕXˆ£H=X… ÉŽiøŽö¨Wù87­m¦Wä3%Ò‰O¥8H…d8†¹’¨Š ~éŠ>Ä©†mÿ´‘\UµxBâL!‰9@˜†EYóØ…ÇŸ¸ƒ5éŒFIU‹ ™· y•u“8¹"²>¹‘U¹·1–A¹?^Y'?©•²xOiQDaÉrB„iù•`ù‘Úè\t¯H'—Ñ•uÙBqrOýØ"7¯×;˜t9˜Cw\ô‘qI‹Ý7–‰ ˜û™’ÉuTi–W–¯‘—¥7š…It\ù™H²–wI.\dŽéB.Ÿdš·š¬)#`Ù;›‰ qÄxtÑ—t‡’ž¹›‹W‹ØP™Ù÷D•m— SÈ #Ñy¹“Á7šR™ 9›Øÿ0¼sœóx"|?)š¸Y7¨¹œÚh–Óɘa†Šè"¾S˜ÚøžùI=ï™ $žäIŸ5‘Š÷‰ŸXÉ;qù›£“H É s3 Œ–ÜhŸšžµ ‡É\%žtS•¨¹[)´‰—Py¡ é…‰tJ:’·aÒù˜ØØ)‰ÉE ‰¢ûñ\eíÙ¡= vä—Js³—wÄ9žê¨£ÀW˜5¡@i›Š—·—Ã9£¹I ’Τ;µY˜æé¤¢¡œ`©HÇ–¤Ó)ûH™u¡¦[J¢êÉEYù£LJ¦É™ ‹š-J ðéš#*§G  …Y¥xš§+úš©Ù†¸ÿ¡¤º¨gÉ¥¼ó“bp¡‡j럸Áœm8š¹œª¥q*©¶ žþ—©³Q:û8¥"ʪz>¹˜F! FÊUÏ  }ªšªühš¨‰šUºÁ“ÏùZZؘ¦¤¹«¼Ú«!7&7Wé¬>žáIª¥*«ûi›vʪœ ­~±;Ü*SZ7ET–úyOa:’ Ë© Á·§‘*®oA¥»ã©ñ::î%«ûÚ‘iÊ™Ë:œUùœè¥ñ¨öꌉ››©­”É¡Wi˜ôz§rz™WŠ•< ®•)›‹© Ë]:²ê>9±Á¹¯{±ð¯üé¢n*« ²!Ë£ÿ£¯ýy°'[©yIœ(|–š¡¢*œêú“¯º²5«Ôs´t7±,ê—–ª?;7îªKŸñz®‹´IËt·¤ÂÙ“Ã%Ÿ¥Ê¥³I«mêŸU¹˜Z©•]k³¥·¡kê—•I«áJ‰4£J5¯‰¹·?Y¶o«Îi§£‰¥Dz·°©Ÿss©‹k¬ci«ëµ:©¢jÑ“z‰Û“è´ŠJ³‘›“k›dé·ˆ £fʸÔã¥>ºwû¹>±‘hÛ´èL퉹/ÊŸù­ÂW«Yy¹®+¸:9š³ªº˜é¹rZ›ï™•WK™ûy¸Æû»<º¯©T-ø¼’z®ýJºk›a °¸ ½ ÿ;¹¥I¯Œk»²9´ê¬yû§9˵à›ùhŠ;ªäZ¯$ú–Jµ¤d ®dù¢ û¾;±‘û¸dù¿:ÀRšçº¦yžß À=º@©•@Y»Së1`yä±½º±  Á:!½ðé¿i½+Úž%LwtÚ˜ ¸ Ü¡ûj¹,¡ÜI­Ãºž°)£üÂð;¹>©Tû¼2Û·*¼¥×·?ZHcz,‰¦Š»µîz±ôëšíʪªK©ìQß§6²ø–èÛ¢ kk–ÎZ¨ù[ÉÊI$©#‰I.":›cü·ÚX¼L„ ”¨/ëÂà3Wù¦ "5CØSJºS,¡fÿ™«ÝÚ ˆ©;àišlœyƒN BÈ’øŸÖ«¬)œ¥º£­Ä9¥”È{eù$9<·7¨­‹¹Ø;³Œ:½¤*E¦l¤Ô)² M¬–î¹¢P›š5¼싺܋šÌÙÃßdG ´—ª¡rÉ'ƒ®F»Ãfj¿Ä̾ÅaÙ¯¯¬?âSÅÚOºñ!nü*Úmk¨Ìú¨™¦«­†ä^Ã%ÎptÊò‚Ù JyˆŽ‰'U‰¥[üÑ™—ßù¸Ì¹¿ólÏ ½º*âHH"Ìèršì¥8ë¾áž<»“`KÊËGµ*ÎèDÃ+â?½L%R–nL¬jÑ«ÑíL:žÿŠ'›¼Ì Ó¸œÂ4|Â)Ðܨ ÂZ`iGÁú¨2=ßڑêL ˜71š7X¡ËT9«Ñýå¬ æ‘Ÿ ý ²¦û›ÔËÕ>…¬‚lÖjqÇ"Íè± ü–+Â$˜K§ß<—‚êÃ<ÏîeÖ71\vÔ˜å¥ âÖ×aŸôPHwDx¢Ï|aÓØŒ×·Zz1Î;¤ Ü‚­Ù“J™ý|äD>´Ýw˜˜ËxVµÒô|퉕A­¤+âØ)Ý·œÓ>ª"ùA‘-Ù©ÝuiÅŒgÍ—V  Œ½º¬zΡOfÍÐÚ[ØYáÇBÉÀ­Y'Aί]ÎC ‚] æÿ¼ÏpñÔ: Õœý;ßM¢Ëã™§ÝQáS%íÐJêÐc‹Ï¿3È¢!ÞQÝܺ!ÛBÛ\YÛ¦ª_·Œ",\¸ÝM·A- Žà(#Ò:½Ö£]·ÑüÙ™‘-ÝCÕÞPݸ̓´ªÙܧŒ+q¤ßXÉ¢~àoÝØÅ[gÊÜý×'£I·Åý!ø< #þ×mÏš¥‹ Øô- ö}‹NqÞe)»± *ÚÍ$˶ Þ^qÝQMNÚé£üà âHõÙ”GžnIŽËFëáR½E$ÒŸr$ØÆ]šôLÏÚ{D²Ù;½cØ`1Ñ/hySçr±åù,å¢=ÚZšÊoÿ‚s¾5^Õ+žãk®ÔHåÎEx2šP‘˜ÓZÚpyã0m ˜è^Éq¬àj.Ô—É;þ æÊ=h·Ýü(bZp.ĸìÜgKéÃämŽÏ(ÔjÁáXY“..tc.¬uôP&èjÖ­]Î_:©EâÝÈ*$4q¥‹í1-Ë‘—æ*WíÍóL,ªèËIÏÜäJŽëبë\1¾ßîå‘})è×>×àî›Þ‘Ž}m"Hw9né•Lð¤.Ä\åîZÑãoºíxnÎâ1*«)[¿;¾}–¼ä ¤4Ž[íâ òâ¬ÁŒ©ðNñÔÜÎìÂä‰tÜÞæ&ÿ`¯ÿ¾¡æ<¸¤v´â# ÐnYÏuáÍóKï®;哟¾‰›ãKÞ?:í}>ÚsAzÝTùì‡ÙÐê>Ú¦YNÿ åtÛÌ(òÜIÿñ ŽB\ ÕYõbúLúLÕ ºóºÑ‚|É»p¹éÉ¢îaåhÝä×¾“ö|©ÿâ…î¿À!âlð íøZÁðýÝuËÜ]þì¼nD“jÎ3-§¢Ï¥£Ÿ›¦Ï¤§_ú¨¿úªßú¤ÿú©û¬/û®û¶?û·_ûµáç;½œ'ºÑÞkðt¿âñÇËGo ^?¤^æFîw>è>ùd¾¸û\¿µiÓRʘ×ÿlÇAqAL»ÆÜýÝמ]z¦ÉÃâyþ"\Ý®ORÍ$±¯–zÖÃ9Þg-¼Q¯ß]Þ…îû![@X¶+[a{ØŠ… V¯\X1# +RäH’²8yc+ŒØ8ºl9¡@(`$è ‹+yê̹PèP–CZÔhO¤M]>Äy±•ΖE™:Åš5kÉ%I"œJP`E!D»3@E)¾eQ#ÛŒ#BÄûÐë^¾}ýþ!ÅŠ‚ &ä¹0aK±suÆÊrd£­T)J†_Š=yVèK΢OBmŠú®ê¤s¡²ŽÚÑ#ÿꌧ£z„¨s&N­_ã­}TöðÚ¹^Þ ¶âNÉ‘yŠ5ŒðJÜ‹„'_{ð&]ÿÕnaÒhFG\6ïÀëk¯¿D Òût¢œ‹’–¨x!}—ú=Æäï( ¶Ã¨,Ãö{(€‹ $ð,ÃXâi§¨„H¥Zi"õ0Ú B ©2‹'”&ºÈ¨G\0Á'ºé8„^Ï:dë õp¢Q#Ãà‚k2ëâ²k½»Â#²È¾^¤®%´¤3O;÷ØêÉÇæ 3+)äëO¨ªÑ3† ÜO ݲ4Í¥² ʬˆF\hÌОŠÍŒÆD‰¢Ð®âH20¿ìŒ):³Ìï¥1¯º §üÿnãLÐ+\|FÁžŒ« é–ûl¢$sš ;ì/®ŒŒ5ÔG$¨'‚8"¨Ô“ÂkÇL#zðÇÂbÂqÉFom´Diº‰´Ón ­4`Ë| )‰˯*Z¥/J9ÃÔB…ÊJU"–¦¢©ÙÛ®º)ÕiÏó@š ÍØ—LÅ5]\!kµ ¢++;Žx̔ޠ”NȼDå7¼„"E/Hˤû>²deËÒÉt…Hºœ9‰»ŠØ£liêR¦³Jë©´Ò…ÓØý¦“S©7õ‰Ì‡ïÄØ%š<Ã6¨§ò Jægaæ¸6i¢ˆË,…ø2uaT;¡œR"L6úÅI傺_©ÿ½,S]Ñ*Z´ÆÊm:„ÏòºFÝÂ"h_ÀZz̲šà,í ¡vjX“«ë‰Vg{vÙZC5no©8â6ʩËkI4È>yý’¥ ‹ à SÖS f1„QGE"˜Ý»š·FÇé%½ÞÃÜÓ7¢©W¬S¥ÏJ5v”~š,M¡T0Ji›¢¿:ûO\œÚj\†ŠG~çº'œx¡â™øÚxÖ ú0§R^zÉÞÞÐŽ¨þ?;»ÇVËñïHs¯e:/wåÍ)þW¥Lh¬É2JŸuýׯ–¼çBüì\3{á/#I²Öó;2-E+„`SJA jÅRæ«`È•ÿÖÕ/uuQXuJÇ#N±j5©Û_ G2˜†<ÍFœ±‘]Z(¯{Ýn2UƒÚ§Ó½Ê0Oƒ?Ô…X¼-hˆC4’Ó<ÅiíH~2²ï\“Ãî¨ÐŠ8,ËÓvb9w½È`J«×ý®#Àªn‡GDcÕ¸F6bTì+c^²´0¾=júà ­xE‡§,ñàÀ~$¦Ùp`žc˹HF6R<Ñ”[âGÇájKŒ‹#÷çÇ@Ò0çRê8¿ìPI9(Ôd*U¹Ê7þ‹Š–¥OÒ2—<š•ü²$u:µÅ÷%l’¤¼] ­cÀüÝÒ˜ÇD&ç”xÃI¾¥„ôaâ+å’ÌVÊÿJŽ}ŒŽîF7ÊÎO-ãù 5Å9NU.2œì™ã¸N7’³H3šºy0:ÚðŽ?Â_"ݹO~¦Ð‹Ö‘ä=Å8P}öó;®$&^˜3?ªÄ2–Âd§ :QжRAYK%MXËÕT–/Áœé‹˜½›³ñ“q|ä<÷Àq^oí/:§ZÐm6ð…ã¿ô¸Ã›¶ôuÅs^Mw™Ê¢&EÞUûÆr†ô± d>'šÍ„–0¦ ÍÞ×ùÐÆ66“ee6k˜Á£pˆÕüêX{xÂÆ6¶‘½\n³øÁÖEïŽß½_QÏÛÏáf¶™Ó½oà¦ËÌê5/£{#ݨO—÷H¿tÁyüeWóÛᵞõ”aÜÁ]ºÁäÆs©¥ñ‡w—Ën÷·>íDúÔ×ó¼ßíq–'{ÜvFq¹©ã?óÔ?ñ²%­ò–÷œ®p³°±‹^¢ œÒØ-yªEîs¦/6áM‡º–uªyÝUÇzI;opensips-2.2.2/modules/seas/doc/images/image021.gif000066400000000000000000000042601300170765700217760ustar00rootroot00000000000000GIF89a Àw1!þSoftware: Microsoft Office!ù,»„€|||hhh€€€ŒŒŒššš½½½§§§²²²ÙÙÙÐÐÐÇÇÇÀÀÀáááéééðððÿÿÿÿ` Ždižhª®lë¾p,Ïtmß&H|ïÿÀ pH,ȤrÉl:ŸÐèO$­Z¯Ø¬vËÍR»à°xL.c¿æ´zÍnKÑî¸|N'Ãëø¼~o¼ <€€ƒ‚|‡ˆ‰o;E  ‘”’ Š™š›A~@ £¥¥¤œª«ˆž?§©¦¤¨£¬¶·r®> ¦Ž¿¿¦ƒ"ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÊaºQâãäåæçèéêëìíîïðñòóôâÞŒZáõüýþÿ |wÌ7(û*\Ȱ¡Ãz»|’ð¡Å‹3ú‹Èe¢“ŠCŠ)’ãM@ÿ’\ɲå?“úòyqI³¦Mw0½È¸«¨NªHlhÏÃÿ Ø*lÓRë( pÀ­7vëí¦Ÿ ¹#6«bµž‚ɶšÛ¼C!`€,ôèîøŠ[Ú»[À½&@€²ìÝ÷ïÿ`@Ì«˜ÃŠFìà ¬Û/r?©1¬;îǼ ÞÈàò(»¦òÊÆZõ]ÈdŽLÈ—š£³İ+Î}Âdµ@<Ç=°ÉæšSì6óF±€š=`Ò@˜l/’æ9 j“$:Ñ—VM„#ëB@²f€[×E$PÛmˆZ*Ú=«=$ÃLr€¤½00Èž Lòp‡ª÷bSè÷Ç*ð€¤¤X~ùÜž€»0нxúÿ¨ŽH—êé'¼‡úꬷ 6€”„ÛË)`sÎÔÄÇœß=“39Btgí±ÐpEÞãïßÃËÄ£Å{ïå ÿ‘ò;o|’Ò§D½±ó׋\çtÔûzlóY…åÏ ì‹>TÏC¯ÎÏ„ó}úêçìhð»,Vñ“;~vÿÝ)à÷78û5ì( TàÑ%1—½&”àAlië=É 7x¢„‰ë‚#Ïò³¨ …á qR³¯¸D†3¤!yð)Ì_íá¨$ \0dˆ ‡h‘‘E °Ø!„C&ŽeK-c^› Ö7AU´âVjØÿèˆÍ{z\‡Â(ÆyÐï „pdAœc(‘m¤Ç…g(:æí‹+ZbW²G„‹t£énŸÖ9ò‘tA!u%<R*dL¢Ÿ,yÉÞd’’›b'û1IƉ“£dá'M¹A¦Ò0«œ"*_ÙÄX6Ì•´ÄJ)S‚Ë\þÅ–K¥/²K S+ÅÜÐ1Ã’L-,ÍLÑ3‘ LgNS—Õ”æ5£Mm“›Ùôæ7a¹C.“œ<É9¡’LÏ8-jëDÊ$åF6K ±ljŒ'PV ˆI1®€ÔgcøÙGJÔŽh9)':a(ÒZ‘Œ¨D'ªªpº*¡û\hS0ê“n^ÿ”£5ñè˜@J‹Ž”¤4)}PR“®”¥.Q)ƒ`S—Δ¦,‘)•pšS›î”§$Ñi˜€TŸ•¨%1j'šTÚŒ©êR¡š© ŠªUUêU±z«Â“«‘ªÖä8u i hYUyV%Ô‘¬k…ˆQß׆U¢¢¨^÷Ê×xõ3^ÐG`K¶­À;ƒ"ZeÆ–Á±ã,.&KÙ6ˆ©²˜Í¬$K9,8° Z«Âg¥0Ú(” §uBhw¤…´J!Y€™ñÁ*¸ö ôÄgè9 )ÈM²uÑêè[)JPX…Û®ÀËY¡Ÿ•"-ë@ 7ÿY .J–H¥­Ñ­ÂºŠZÇI—»¢•€w±‹¼ZáÙmo/ä[ÛòºWºð•nxk¤Ùþúw—ý¯€ŒøÀ%D°‚<;øÁÜá,„'\ÙSx—„Û ln¹¾°,,â®Àk`€"°/°˜3j‚о[â× w‚¤€‹€´ hÀ¾ ueøÆÂE²r,'29ÇâÒpï€,`sJR–¥p§ `-›rŽA!å}¹,W¦ñ–#á5/ï»^3¤šÝ¬:Û$¾³žÓç=ûyÄmþ³ “<èBã¡Ï†N4[Íh7 ºÑŽp¤'½Ù¾ZúÒ˜ÎtB;opensips-2.2.2/modules/seas/doc/images/image024.png000066400000000000000000000225551300170765700220270ustar00rootroot00000000000000‰PNG  IHDR´¯ò«ÿÞsRGB®ÎégAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<$ìIDATx^í[ÒÝ8Ž„=ëímx³©z› õË?Ö±å¢a\$ERv„£ªt!Aâc"àÓÿóõßÿûúÆÿqž2ÐüÃ9x ßž2Žƒ‹òm‚ð$trý#ï‰ßZGŸ×ç{í ïGÏ ¶ÏEm=í>þ´„…F1«4Ä6ä™§ÁÌCQgÍɤ4HÑ;ÚѾ½ÿëÔÊÜižñ;éÓˆŽ/nx€Eí´À¶Iˆ‘öÛwe›VØ„ÎÅÎÏ1äbè `–BfÚ@Ÿµž‹v‰l'ÐÊ9< ”¦¶Ö‡¤TîóƒÍ‚ù8ͶÁð„ûúg¦ôóG†^xݳ€è#R³Áº-†'š§Ñ)…#GdÑŸ¥îä킌vŽ,$;=O ©ÿ7Ǧ»@M  ô£’/:ô.*õf; 4¦B¿Y8öÚÅ\Th*4š*U[¥Þì*4š ýfàØkïNTh*4š*U[¥Þì*4úY ­• òÚŸ·ó±Ñ|¼y{âØŸ:1ä`Èñ¬ƒ*õ<•z³O©ÐTh*ô›€c¯½£Q¡©ÐThªTm•z³¨ÐTh*ô›€c¯½;Q¡©ÐThªTm•z³†úŸ¾ Õmý8‹ü9,ôw/¬ŸÀ’?Ôb9ÖûÁ—è'¸¬t±ÚÔlmDFó7£È†]îw}Àüýû×Wµæ¼äc¢4H¼ Œ€í{ #}{Ïxãë›72»À¶ÂÎ. O˜ #¨=ø,ðP`=î… ¼m{ÐÖ˜½ëíŽf‰„Üåâöÿ g÷‘ZÂŒBQh+4ð`‹ÚÍ, J=¨3[p­ÝF>c¬lµÚWé¹ÐÌÔ(,ísÑ!ª¶¡õ— g"åDÛGæ·ÕŸµ³d¯G6T¿= ³¦,½Û]t«–(pÚÐ[>gƒÄ½pX*ëÍq¯Ý½6ÞõôÌH¨‘U¯H5=˜"={ÕÏÚ9™?/TBúu!Ç1à3Vnÿ‰‚)&ÚŽ¥¢Ùë­=¨’Zï ÀSúÈöó]­ÿD\i‡÷~´ÐªÞ‡úœˆ˜«œvý™ÊŠJÕùƒn¡^5x©ìOÚïÂÚ5ï¶k´ÿÐÈÖ9jßgZ}„4ÐÙÎ,•åõº? õq¥ç/ºÒ`iËóÕŸ@³|*0ÛE 4&л¬VÚùüã¯<ˆӣÒÑ6i`%¬Ä†—Œ‰#ÚñžöjÙwKZïÌ8òŠ’Mš_V—Ž0q÷»Ý!ÇÌzèvZ˜2×µ#Å$£geÛ¬E×.ˆŒ#=[íØ®Òôìzè ¸hŠWÂv%ÐÖù¼§w]KGËE.w9–(dݮϦ¾¢zÐQ¨ -ídqD@XYO¹«h»Œ|WØj?²k÷û) GªîNCÃ/ÆlïEÛ¾ål$FÍ(lo¨`Áé…Pè.ÕkÓÎPÃ@ÂŒ¨pëÄ L֖﵇¨lƆQx…g…ÑõÍÚ=³Nöžnö£p&ÐY˜Ñqg•Ø W²`ìú<ô1QWÔC·aˆ·õjênM¸Ž m@Zèc…CÑiˆöñ¦;R\ tÖeÚ®ÿ1¯è $Ôoœ,t®ª<÷FA Ý~x¡!¶ŠSßdºsÁàmõ‘³çf€¶Àôv‘h|ˆó5Ðfm…hˆ}w<} j¤zh-ÆôœéM¤¯ÊëphßÚ¢ÓB(†ŽB–öýv!F!„' ­SèHþ>ô;*º…ºú5µ­nóÛ³šiª_ÉFÓö¬‘ã*åä®ç¡zÄ8K-yýžºèÑyaaÅ»—½b샻ÆïaÂ@žÄšõЬ‡~ÒŠæXžµCQ¡©ÐThªÚ³TíIþ¤BS¡©ÐOZÑ˳v›çì{ðÓnúMe€ õ,…z»?C3†f ývàøëîjTh*4š UW¡Þî*4š ývàøëîPy…þþõ¨M8ëÂÙã›Ð?`þçÛ?__„š‹ºh¨†ý æhBý,UëQªï`@ ˜ 5Þè#g~B,ÿ…ß¾ýïÐö|¾?Úâ«}#öñ™XH0…þ/õB=ƒ|w¤­îêwÄæ·¾ }LPÔ- Ç¿ŸÚ o¯EЬÁ…*kd‹vÿç¸Çv™·ÂuǸS@{P[?@cª]×À–“’ËRøèºöbŸq˜ñ‡8öL˜TjÿǪ[ÑiGB{c°ŽÜ!Z«„:=¾yû;i…þ¼ŸŠÑOƒeÚƒJ:Ž@çתÏu}†ÑÀ2@Ï9¬Eí 9ö]C@G0ËPcõG¡§êš-ü(Üäß¾F \ùŒ„J‹uWÚþö‚ür…Ž€h•Ò;’ËœnD}òþ^füu;ÐcùìsAœå[]´jl–ƒßÖNh–ŽÕ¦¼ °ÕãÍÍzhÂ\|GÃf=4a.óç˜ÚXM˜7€º§Ê®7U-XTÛ-HÐhÕÞÌ>ÙÖÜ“L¡;KGe¦0ë¼*EB<Ÿ ]–ƒÌó0Ð?áÌÿÍ•¨ù„^*qF-Ñg#[˜úÞ\ òÐÔ¬‡Þ†ŒV}6 ´5ë¡ sÀ»€n¡f=4a®3|ÊaÆ+?bêh0¬‡&ð#3ïw+4jDô!våG¡wtÈzèg.´ËFÁ×à«rl—Ÿ¿o±Ü4ë¡ïsþÞí@?qR9¦û)Óâ„ô>H3sŸšõÐáÉNÆ|vîBÉÍzhÂ\|GÃf=4a.3žXa=4aÞfèž*»«cÃÑ’NÖYÏ]¯ö7Ú>rT„¤|®JÂftQöŽÿÉïÁ@kUví¯ù{¿äý˜ökI=5 ¨gß^üL¿Gà[mDé}ÖY¯Û R@{PGõÐK%#£¿Y-Àµôl×ÚÔl’ã|²j®[h j¤„ÔSÏÖ¹X–²ž×µ´:ú·["'x6Y‹·J¨mçû]@·PG0[*˜«hÍ1™~=ÇèuaDfu}BuÖ»=Gñl6´ˆN52!K·»CŽuðÁÜ:^‹Û ûÑ}ˆÉ°#Û¾ÎxmEcŠÂ+tNùœ¾H–]mòË®SÌÕ¾%ÐZ8°zâÙß5‹êµ@¨k€º{^ ô&5 wƒ²Kÿy YÍB¥Â"šõЄ¹0ÌŸo#x+ùóY¤äÕnÀmŸŽc¿8ºùëÇ Ôû9ú-‹ú7À?`>€ÎB}¼ßþù71üò’þ¢”½vÿ¼½;j›—…]Ñ÷ ûïjúœà£\ô…Ú#ôÈ»=m·ïȾ£ÿŽú›uŸ@û»# t/Ôþ/“þTèVùÚç#çyªo©©v]¶£ý·dfÁy "Ú½ÎûžrÏZ0»·“Ú‚úPîÜh ·@n‰(¨|Þ) < ´Dd‡ ü…·;tWÚŸZBÁ,!ÔÀE¶s ÔVÙ,@å µc-Öh‘޳ Õj´à®„bç¶»€n¡Î:U ÚR0  rk ÐûœêtÆsQ¨q¶ƒ(·¢§Ðš­‘Z!ö Ê-^i—´£G@»žòÌÐè$h[º¥ÔšÃÛ÷µ¦>  žŒBGÀY \ ©Ú])Z°¨/žþÜ £IŒ ˆÞ_q?RÖ6°8ô!Љô;U2êîEWè»'ý×õN(4:©|î¾’šõÐ,!-,9 YM˜ Ãü99ƒ·Gþ>4a.34š0o3 ôq\ÕþÒ(ú«£šúgΜg=»ò¸-“„wÇM`ª03äøþŸo_íŸYP£f!DABûŸíœ»ú=Žêí©@ ·…üg1/ÔmZ7J9·éÞ6=žIyG‹ÁJ?#;Jd6}^Ýìû èæãß?jþÚ•5¢Ø¸”V Z{±)rXÔŸµ-°£þxì ÛZSé¾6Žö¶Ó,Ü–bŸ×Ñz‹ìö,ù j XÎ_*äÛ>RÜß ô©æ­ªG@Ëû3ºmSª®œëYÏn½èc’ÛÐc 4¦DãÞhxÐÉð S¤¸žBËE™é—Ïæ€wO9äßðî\+vnU8‚M[¼èâÐÖ£¶¨#û­q÷Î#ß‹á†€ÎÆœ•&>RÖJ¶Ò–ØhŽ  Ïc»¨±ª÷#Å®j7íÊîm}˜q¢óÍ9[3gxqÓ¯¬çØ€<Ь‡&Ø…ÁÎÍzhÂ\æÏy?Û±š0‡šõЄy˜a ÓŽUõÐòdeÆøÊc»‘¬*¼[n×ã)Wmeäz'§JbeÆÂìƒ7½—*NêUêÑÔ·¥°™tx”æF•5Ó'SßkΞÿð­\½w×C{!‡¥¶èõ¨^DS2´m¹è4ðߤ”wµ\=trhjm-Tu£É·úÔTÙM¢þx¿_ÙS!‡tàõн@#êڣП/çãoí4ÿ”ÿ®=S%vÛ “F”ÎÚzѭ܃Ç4:Õèù@‹eÈѯª3ôhµ]ö£P†rÀ–Š·ýh ÁSèòž>ùQ¸rèÈÙ3WÒVÆžHY‘þøÌz0{çzT¡{ËÄÅ^Q2Ó>¶u/ü¬‡fÖíQi}¼8‰Ž”㟺“äf=4Á.,n9 YM˜ Ãü9Ù‚·ÖCæâ0ã@³š0o3 ôq쵪ºM~ÈD ¼›tN~&{9Û–‘lìl[vn¯\=4’ê¾jÂeš¾í'“Ì™iß]ýÎÃʶRÅI½JM}GI+Å­)z›F—€zí ITYµšk1eÞŸœ)÷ûО"y!BÈL" i´ð4…lÒÀ_©p»÷µE=´¥¬VJ;Ju·ª­©á  ÛÐ e¢±ìÚ*ûS!‡kU=tû¡Ø£zr2#`£û¨s4H3 ­}O }¿õ9¨8iÄÁÖj9Öû(̶Á# ¢ìYP¼qj*Σ?~þ̧å ùäŶ‘“­œvë×T´}¯…/Šy5(4°dû=@Gó¢Ýׯ.Öh®ß~:rÚ'1RÖ'Žù c‚€®T½rÒ#Å^i ûÂBÖCwf Øêy‹“èxÖslÀ@hÖCìÂ`ç€f=4a. ³{l÷WìÃzhÂ\fhÖCæ `†>ޝVÕC·I-±2úÕ|uû£öeÞ_y¬8’-ÎŒiôÙrõÐW'q¬ÌÝèD®~¿Jâçjeç5UœÔ«Ô™Ôw¤^ÚÛK){j6#UžM­#ÏŸµVŠ^+@®µ5$mI,/óâ•dÁ»êù’õÐ2,h«Mªç ­ý¨ÖíâМn©§¦÷¼Õ¯DF-{ûÔÿUpö´»M=t´ÅfdìÁŽÚÐk‹§ÚÖ.§‚ íZ‘íÑ }_õ\*ä“·²:šÈÈÚÖ*¡FÎÚ†Û¶zmAí±ìDì?²=òÃU ¢íBÅI#hmQÑÄE±`4±šú ±cäp n6äȶð[×Ck h)¥µKhÀ£[©·ˆª„:èw{.rHG]ùûОã=¬m7Ïèù ;º€ â¤‘/tM1µm_Â)²å#Ï  g>B£>­ùu$ßj9îü}hx+Þ•‡ísW|F’^ŸHøC@ûÔH¡wžôhØylo¶zçzh$\ÈÆÎo¦úØY½IJ·:HUìËUÛÑù¬é(Î@èdíF••K;ú>²v›·Ь‡¦B?F¡YM˜‹Ãü9î…¶ÖCæ `†>޵îþ}èêGkZ‹PF2·WÙT¥Ý’õÐZÒ£2ÐU’4•çhð©â¤^¥¶R½çõ¨¾AK#Ëkgº|æu™‚·ìÕœ¥Á…*«6òšLíkå« ªÔOézèÖIÖ–~åu¯æ" 12ji)|t=‚J ­²¥|=´§UIK¹åäiª-Y²oO3@k Wk[›‹v¬Ù>WA¶²ŸTÈ!'oE=täDt»× †HµÅ’±Ár®¶ "[¬û+ªÖTœ„Æ~‘c³[¸7FNncKDåZ¥¶bf´QµŒõ·µ Uîj{  G«í,Û-\‚è)ªöž !$\šÃ#»,Àµ…†(wh÷‘9²Â¥«á©Ø>t䈊›e2öHYgÙÂvâzèQ…ÞÉÚ‡b?U2† ™ÇÑgXÝ‘©t¾üX-ÎZŽMÈÍzhÂ]îЬ‡&Ì…aþœJÁñë¡ sq˜q YM˜7€ú8’º³:: öÑ»QfÞÁɼ^aÏ“ÚܦÚ›ôQ@ª$FÐÅ÷$g%UœÔ«ÔQŠ9ªÙ÷e{Zû2elA›Y 賞½V:]^Ÿíè·´W¾ºu4¥~ ïÊú´m™MÔÀGûäszr¦l=´2O{ÏȘGK}GöV uÐ1îð\*ä ve=4¾.=¼˜½6BÅIhì¨am«‘zµJ©©ŸŠDꋪbÏZÔ6CŽë)ôhµ]ÏG¡ÏʽldË×>¼2‹5‚\»oÙ‹,Ø^•zó{Б#wš@ ºÞÂÆþ[! GºÊDz Ó kªØN;°p…õЉ ¡Â ºsžðâ$:žõ0šõл0Ø9 YM˜ Ãü9Å‚ãÖCæâ0ã@³š0o3 ôq¤µªZ&Ð3ð(CïDŽãVíe>3Æö”6ÊÕCg²y+“!QZ{è_eOµ~RÅI½J=šúÖê6d›=}´ïx;CF-ÑgåŽ"mÑîk‹½PwÛS¶ZNL»ÝkáEtM.Šìóš£2ji)|tÝûnpªö_ºZ )"45oÕ7 i,ÕÚ²+O•P§*À Ÿfè÷ÿ|û:ê7Ú?ÚÄ^]}Ð~o›Ï(4^“6‡Š“и0R1+ΕÐxªdÅš#!DÎ ‹HƒU°Hq¥- 9r z´Ú.óÁæ}œEq°æÙ·¼¥ÆÞš|t-ºÙè ºÞòtvk½ròî²%RÖ+Ç̶q•†€U虹 hïcsæøØ¯âZx|¶[á ÑÉcN~ÓüáÅI›äòßä<Žõï…žšõÐ,T*,n9 YM˜ Ãüùη-ÖCæâ0ã@³š0o3 ôq±ªúª“ï”ÆÊ»WÂÙ#Y×+ìyZ›åꡯJ`  Ýu¤#» ÔlÓ U®¨?Y/â-L´O>g/”’õБ²µÈg=%EÙìöoAª%Y»C¶OB þàù1Q•ë¡¥âY (Ž@gµÁƒ´-ŸAWÔ7ïÿ 6Tœ„ÆÑömÅÒgÜÛþÓRƒ.Ó®¶ô­Ù…"Ò*ôœxz´Únä£Ðƒ¬·Ý™…ˆ}Ö"ò"•·pè'«·þ>pª.8èQ…®:x4þ®n?íûwQò÷¡Y>‚S_Íñâ$:>›&ø÷Ÿšõл°¸å€f=4a. óçøÞYM˜‹ÃŒÍzh¼Ì0ÐÇYí“롽 %¼ƒ ‡Ë¢*­>¥·í»Þ‹R3íêÍN¿¦zæd#myÎß1QU%Í]ù߇öŠz4Õ‹j(N5nÓÎR¡½vå{Q{š´:ÍnËÆö£±Dõ5£µ83J^]Ý:ÀRT:DÙEm¶ Äršf‹Ö—ö¾·ˆ³Ï·ÐF!F¤–Z[=ó×¾õ¹m=´·jŽÐTq^ºTµhG‘}z°GÛ|d[4Gžj·»€Q—¥è™EÍÁ_}hñàÎõЖÒi“…zy@k¡MgœªŸ¥œYp·úp uv Þ„eUE:iô¿½íY‹3öjª©dv—X±£…aù ZœÞüZ}F;+¤ÐèÑj;kûj·Ûh’,…mÛFb6"hý"vY)´¦ÒÚ<²Ï£cDæÆ›c X+ì°B/ÏV¨}kU! ¬H>3¯P'»ÅÏœûl,;³ïL[¬‡Þ$†¨SÆñ=ÏZáWO[W½Ãz耾 ‚'µ‹'Ññ¬çØ€<Ь‡&Ø…ÁÎÍzhÂ\æÏw?±š0‡šõЄy˜a ãš»ê¡{Î^g/iI-ñïvEà˜=OÞøG²Í™y-_ÝsþÚ³PgXË̤Wx¶J¢dº¯´É­ôûÐhK½: M‰¢vÚw¬Ô½—¦Õ€‘)kÙšFSßçÜYýJßg«· ¼´}kSXEbP²º¹ÝK§[À·×#‡Z[o´f´«Ï‚Å[ѼÈùÐÀȨ¥g£g‹o¦Ïf5†ÖÔù(Nj ”#Ú˜Ú3ªwàÚê• £ zj©¯¶`4e–j¨Á穚\4N©z­}š8XþÊÂe-xè% }tR©ÚSh,dËõ€ŽîE*8²Ø-zæËšÇH·ZB=²¢­-&ëLK±ÑCS éHÄ&Oe‘÷‘P¨§Õ!G$&ÖÜ.9NC¤Jg·#9ÁÖjÖ¶fO9Û-4êÃÚªex µƒ.DkAyaŽæìÌóLѼô*wäwí¾fKf>£Á»•Fƒ1àÍïÞ9¯ÞN³³O  GÿÆÊÎt¥íw-CŸ+ǹ²mÖCÉÚ­tú“û‹“èxÖslÀ@hÖCìÂ`ç€f=4a. ó绎§XM˜‹ÃŒÍzh¼Ì0ÐÇñÒNõÐVËKØÀ;Õ/ÇzI›l[Ñó#™Ú¨í§Ýd=4ê¤Þsà*I‰^ûÑùÙñ¹ò¿M-[ ™ŽµÒ³V*>Rd+¬Aá-ˆ;ÓÆ;ü×®+/´5Ç¿&¿)µÂd[la±œÚ:Ô‚K†Q€×—ž´÷ÖAX€ öh Óç@œ5†’¿íA&— )t;q š’[ï ×#¨½Å"w…*¡Î,ø®h'rHŠQ׫FÔÈ.`µ—mû¯­îØÉ'ZÍŽø*NBÅ‹hÈᩨvïêQâhaGsƒì4 9&=Zmg,·wÍ©òšöÎùŒ·E[}YaG[# A®ÝæÄ…ÌñÔg‡ºê¤D ÚÍ­?VÊÑ9î}zT¡{CßÓ>Ñw{ŸÓbàÞ¶øÞ¼ÂzèćÁ›ÞUs‰'Ññ©SŒ«ÆvýE•šõл°¸å€f=4a. óç´ ÞÂXM˜‹ÃŒÍzh¼Ì0ÐÇÕŽõÐðî8ËJ€X‰ YýZÉèúìþwjïÕõШ£¢DÊÕ‰ËλúEçíŽçRÅI½J]!õ-Õ4J¡·‰$Ý쩸t¬·@˜ú;ë~ÅïCË-Ú ¹n©bF-‘~Ú¨e%ÛEv‡Víó5õÐ+]®" ˜4gf€Ö`Õâq rúT…l¥]©£5ì ?"Göª‘öც£Ug‘©¿° Fmˆœh©®œc þU¤Ñ8ªÝ‡Š“Fœ(ÁÈÂæ©c´Ïêû …öÙ ‘¬ñTƒêN{  G«íÚm\sJLôÎ GàÚ"ÃË~MM£kÑý6²v©ÌNs'\wô …wîõyµ½ŒeÇN"®äzT¡¯ÀŒ´û¨’5¡f=ô&)ÝžE÷Æwðâ$:žõ0šõл0Ø9 YM˜ Ãü9í‚ã,ÖCæâ0ã@³š0o3 ô™æÖj¢¿’1utFÜ&d‚ÞMŠLþÊ£½‘lînóêæ ¬›Ç/¶fA-mŠÞ©è*É—çnÄŸ©â¤^¨W¦¾µ4º¦ô^Š}¾M¹Gé{­vú¦]ÒÿÚ}¯ýh*¿û¸zèÖ‰ZM†KÏóZmÆŒíßRøèºvegÚöˆzèV)5ÀÎkVL]÷Ú—1Fu#Gjve€¦Bÿú˜jÅ¿ýõ~ K•߇¶ Žœ‚޶/Ûý Ðùz¨8id Ec;T%‘-¾mËŠ©³íŒ<î)uôq)ç!‡qÄ%Uzäky·B @í#òÊBox´2<ñ[U¼?¤ÐäÅÐ+í”u¥-oê z·zè‘-~¦ó­0jflëÏ8›õÐE²Š3ÿ¨Í^œDdzžcò@'k7¨ž¦L=t ¿µ}^q]†Hß„BklÙç‘9ò€¶v Kh¢ëø£»R¹ß‡–“®9MN:ªí{ˆ–òF;„f»¥PÑ6, ÐV‰{²sAñW4=p§B9AUë¡#•±œhâƒÞÓœ“q~´ˆ‘ð'²ÿ–@FïVl§½ªmÚÊ¾ÓÆ’m¨ÑÓÎÈ\Dbà©f¤¸rþ—„§Áwý>t»eJ'ËíT›mR½vFžGTí»u6bSþdû• FcCÕÞ ¹dø×.âžPãw{ÖˈBtÌwÅÏÀþúkfwÌK¤¬wØÔÛ'ë¡''[ºq#Ð^ˆÓ;ž»Þc=t ïàiýþ? <4Ug–IEND®B`‚opensips-2.2.2/modules/seas/doc/images/image025.gif000066400000000000000000000517221300170765700220070ustar00rootroot00000000000000GIF87aˆBw!þSoftware: Microsoft Office,ˆB‡   <<<666%%%!!!222999...,,,((($$$???&&&---###)))'''444""":::+++*** 111888///333777===555;;;>>>000LLLWWW^^^XXXCCCTTTFFFSSSUUUYYYOOOVVV\\\RRRPPPBBBDDDEEEZZZ___]]][[[@@@AAAMMMGGGIIIKKKHHHNNNJJJQQQvvv```bbbooosssxxxtttyyyeeedddzzzcccnnnwwwrrrlllmmm~~~kkkiiiqqqfffuuuppp}}}|||hhhggg{{{jjjaaa‰‰‰’’’–––‘‘‘‡‡‡ŸŸŸ„„„†††”””•••ŽŽŽ“““€€€ƒƒƒžžž˜˜˜™™™ŒŒŒˆˆˆšššŠŠŠ………‚‚‚œœœ›››‹‹‹———®®®¤¤¤£££¹¹¹   ¢¢¢¯¯¯µµµ¬¬¬»»»«««©©©¡¡¡ººº¶¶¶¸¸¸¾¾¾···°°°´´´±±±¼¼¼¥¥¥²²²ªªª³³³¿¿¿½½½­­­¦¦¦§§§¨¨¨ÛÛÛ×××ÖÖÖÕÕÕÞÞÞØØØËËËÂÂÂÇÇÇÀÀÀÓÓÓÚÚÚÐÐÐÑÑÑÃÃÃÆÆÆÌÌÌßßßÏÏÏÄÄÄÍÍÍÒÒÒÎÎÎÈÈÈÜÜÜÅÅÅÁÁÁÔÔÔÙÙÙÝÝÝÊÊÊÉÉÉøøøûûûüüüùùùýýýúúúþþþòòòóóóéééíííëëëççç÷÷÷äääöööñññêêêãããèèèïïïðððôôôáááâââæææìììîîîåååõõõàààÿÿÿÿ» H° Áƒ*\Ȱ¡Ã‡#J|è¿oÝÀñóf0œEqÃ+ãÄ“(S.üæ­—/rüÊý2Ž ¿sÀ‚ñÉ]:‚Ï©J¥8r]:ôð L—Nù…óæËƒ]ÔYô¦5*¿nQÐÈOcŸ/üÔ]ËV!¿a<ñCÌÏ'~ˆYÇÏ ~ÅÆxE†Ý7@€Œ‘ Ä/” b Èð†_'Œ™lË™ó¸c±X ;"‚Œ‰AüF)!„Ÿ„˜  T!çD•A È €!Èxv^¾–Ÿ©ÎüJà§‹~e”ãGæ€Sˆ ÿX,EvhFQÁÏÓwiQã ÝWæøUŠS·¿@xA?g4Ì a(ÃF;Ë !Ò  ³S,³NH0sœ!ª$A@Iù•xRI‡D D È„á‚:_HÐBü ³M¸á"/¨ÁO" ¼39 tÁO3¬ò N¼Á/0€1’‰TB¤<ÁÄÃ<ó„+èxã FôÔ#ŽGö8ÓM9÷¨ÕÍ9ítÃÎ=åˆóÍ:õ€…=Uöù8áÚMEÏbÏNy(G€†ÓÍ™M•Q8 éh7álæç¦uõ›œ†*ꨤ–jꩨ¦ªêª%zåê«°Æÿ*무Öjë­¸æúê0¼öêë¯À+ì°Äkì±È&«,Ì6ëì³ÐF+í´ÔVkíµØf«­®Üvëí·àvËê¸ä–kî¹è¦«îºìvVIQaúM¼@y•è”mE?#‘Ò8Q‰£è@e•7âà Ö»ªVJЙÐ ?ëÜ# 1Ü‘:dzÌ3àxSN=i 1å¨CÌ4Å“04ˈ#N>ÔCÌ7æäó?½ÈÃp7ZUc39ÝÔS8ëó³©1³Î¥à€#ÊPÑJŠ„E®LÉ=1xñÃ"ãÌ£HÉñãŠ?0"M*8?!Åü¸Á„P ÿb ΄ӌë|#Î}Þ<‡Ø‚˜GT‡uUVEÚ'ÂÐøáÉ9#yƒÏò¤¥ŠŒÌÁO=TÌÏ/GDÎ:HС?¯„,ûôÅ ?ŽbQ!´‹ET”SÇ#ü|!ÊFæ$’wX$ (˜2 *ŒBK'vD:PÔ"I:šR‰9$~Lò2y³L‰$ÃO”,ÁŒ'×tº GøàΑ•Å!ÀADìl^Q?p ‹# kxA: o¤ãˆ>¢FŽgæ–Ñ+¬Csp†,!["dp& $?!Œ Ä1Y`D#óDÿ¬A†?ÌÀ mhGÀ"Œ*ÈãZ¹B QrØav©Hx ‹# lx.Îy ߀Ú}º‚4\ËB˜¢€ 4ð£t0‚–´P …•Hó0F"ü°Ã&tñQ嘄9ó:T [!0¶p‘px‚…€D$¡ 5ƒøÅAá2À£h¸ÆrÁ8d`¾È‚ÆÔ§n|£¶‹ÀàoXAúðÁœtˆ.xáž8À:ü˜ÁeÁŽÀÄpqŸ¹¢Kˆ‚="áˆ2ŒDrüÀG¾Žq,â醸‘ ,ò¬°Vô@ óƒ*ƱÿŽ5ØcÕ€9FdDûÖP _Ð tfqˆqèâ÷Hƒ&¨  fæG¹È†Њû|äh‡>6ò –T䛈©"æ11µTLcªˆ>ìSÓ›Î+¥¹éW–S›§ei‡}Ò¢Ž©ŒÉ§ü@E%q‡s\ªOüˆÅ¬aÒà#ñÀ:ÎAVtÜ#±xƒZ ‘|Üc¬ç@‡YÝz¯Æu¬o5+XßzŽ·ÖÕ¯b½`û֯ƣ¯‡íë=ÈW|È•X„3”º)ä® âGªÀÙÎv¶>ð‚h Ïšö´¨M­jW»Ú$A´\èÁ,®Z%ÿ ¤`Í­nwËÛÞúö·À ®pu;Q-Í–ã—˜\E9å6WQ^i®¬œŠSœÎk+[ÀªËÝîâô¸UrØ@ì¤ Vp5÷¨‰6ÌA V(ƒÀ0+ÊQi°B'ò€+XQ ~\ã¿Ì`ï~!ŒòÚÒ 1+¨¹ŽtÔDðP†rÌÅ•eÔjÝЄæVÀÁ_IB"jpÿ0w¨ÄÑU rÝC(FZÄQŽHÐCÐðvq{@A­ÇÒazÐèÅ%Já~@ƒ Z©DŸ#Ay #AÀ:Ê@ ‡““š„4Ôá Ðá·ø?V‡3ƒP ÄÎ-¨œS‚!$+@£šÞhE–…|ÜK8B3`Q‡páÐ >ܰ„J¨£dÓ"F1Ž>ôa Bâm±Ù#â+á0EŽi a DxG(œð AŒÃ”D3Æqe,à ŠhÝ©¼AŠ,ˆ! \-HYBfrî+óÊT¦¤B\wLͧ>ó3˯4Š)SóÊìÿá*y€uŸñ#îðrЬhB3T{Øë°¿=Ö‘|àŸÿÿ‡ù`þ€üçýÇú×÷÷ øú§€ø€ð𠘀 ù0î·)üp(˜{ $X‚&x‚(˜‚*¸‚,Ø‚ƒP$ø ž&*å„ìƒ:¸ƒ<؃>øƒ@„Bøƒ–s*–/¯‚0ãçæ÷2ó„QQS±r]Pø±Lø„Z臃0ùR>¢²ÊQªà FÚÐ eñ í Ò à é  Ð jè&Zñ uxÞ@‡0 Á` uø ð  ¾à Õ 2 §ÿ³ uxs¡æP‡ÒÀÒ 4 f’çÆ·8‘YŒp6 ‰`C€<óÀv@^PŒà $P¶˜ Kä>pPp°°°uเGÀ‹ƒ@ v¤B`k`Npð¥°LŒÍ€ãÞ°hPmË@^f§p ³„5È\ ‚ЋD0€° ü ˜0 Șð¾ð(Ÿ°‡£œpØ ‘ ³ðà@ßà ÆŠ–s e° »`ÐÐŽì€)‰à ¡ Bp8î02𢠩P £ðœPæ lp€ çÈéÿH ~“  &±p˜zv° z0Œ CÀy° &Ð.ÐøAí0B ßà ÀG  a06àc €°g ‡kBr8‹0\¹ áÁà3HÃ ï„ ò€ëà!`q@ À ­ÀŒ¦ºpwmAN¦@ › vË𓔊€dw‚ð_ðfí!ì  4]±d@§püÂ|° ÔOó E€ tÐkÎКtט/¡óbNx FÀypzj`hQ _ §À]Ðйâ¶”pw`Ó0eü`zÿÀ“ I x0 š@>` öà}p¤šˆ)¼ð ðÐ ?€ EûÐOpzqÐ"Q > ü u` eÓÀ b þI—éP€¢ ‘°ê cÀæÀè4`ðüðEwÐÅP¥PEÑà•  zCá0 z ŽÀü@ Òp š0ZÀx ì ¦àú0JÀ®à MÀ\à@ á`o€ã@k °€7¤ÀE@žpT —á° 4å Oj í “ Ð‰ –ÚpÞ°“šÀ¢-Š Y W0q…MÿØ„°b~à z0©®@+æ7—z©²‚©ü©›…q `€ cp‰&­ h*·ÕЪ®úªÕ  ²ª ¯ ÂàRW »à ²Úª³ «´ê«°«Áú«Ãê«Åz¬­ê ¹€ •À ö`4§ªem°PØš­Úº­\ð¢åFpÛ:®äZ®æz® uâ@pk—%(á]ôZ¯öz¯øš¯úŠSGÈ)Qó¯°;°[°{°{°í²° Û°Ë.±;±´" {±›±»±Û±û± ²";²$+&{²(›²*»²,Û².û²0³2;³4K±6ÿ{³8Ë-»³<˰`س¬ò ´åò$•ð„J´¡ò5|Pu qõÒ`‘NÑn¢1JÛÚ 3#D÷AúP ó 3ï’YQÃÖÐ ä8 5ñ`ó Ï€0—g¡YkÞ N:p)àaë9[ %ƒ’FDtÑÒPƒ,aKˆ«ÐðwâÀX?«´’W~ë5`Õ0ñPøp \Àò \†0 rÚðïö@ ™5@ tƒ Ù E  ´@ Ú /‰™›µüÐK;@` ¢k”P ð „ÕÀ 5@ A©ŒÂ0úÿ`¡@ÕPºðÁ { u{ð‰òp¼È‹K*  _á ñv=’0^°f }(@€ gR}a lÀX°P` –!æ@¿š»%J€N‰‚Fðv –0 r@ ~@~нü ¾½ð.áÀ D  íÛÀ b ubð ‹ñ ÁY;êàp£H~’Î#pà RP l°Hϰöép ™5(ä°§TñQ° ð`»p ñ°Dy›UV/æ¸ lxc h n Žb[n{Wx]×…µklB5Ä@ ¶  ¾ÿ;EÈCñ AÜ“/˜êÈmQ¢h°›–\*ü  IÀp 3QëÑÈ aµù"µ@È`ô˜ï[Mú` e»Åò¯üp Ô01ãå¶G#·ÞÀ+b¢·€·–ì ôÐ ð·)H»eY0¿D(äà ˜Ê ŒÀ m Ž¢Å5Šrä°̰;ó C'Ä;[ÄäPËì¹Qð½ü‹è° K÷p KPv b°Rïô0öà·; £ hž€ \ ÂP‡ªD`¼›œ/ú €0­ ºs`¨{ •0ïÐ!À½eÁ=ìv˜² ˜à ep ?ÿ`ûR—îp#0¿-C†„Èýû¿ :P@À\q ¼À—<Œ ü \Áß0DÁ? 4e#•úÁ>z@ ˜tp Œ Â,ìÂ( 2< KPíÀ×€ k ukÀ zÄêL´”‚/GQÞ Qp"£¦£À|A j€¹éð[|“ÐRÕ_p  ÷àñЯ›lË'õ|† qàüÀ e@ j0»êp8õ2¾ØµQ¡ÊY;Èà° ²€ ÂÈP |aÊ] Þpê0ù"Ç1 dÐ_ÏíÂà,`ovEÀ‹ÃË_¡Å5w@ Å€ ˆ€êÐz¡cÀ[ :N., ÜP2É=‹PÒX𠜰 PàeûpuÐ ª€gš¤Ð¢àÅ0Ôªið“/¾pm° ¨püÀ d Œ0–‘kÛTêp%5ŠåP Ô oÀëÉ £@Îp‡àìð u`žÐ ³ØÊ 4½hÅ0ↇ@ê@ rÐ †ps ]p 3@g<›F ó ΀pº»äî`ÍŰ¢Ô á@i° ß` iÿÀâÀ …`”  ãð   &1…<á iðãðå n` ¥ð×æÒ¯~¼/V 0d±ÀØu ¢ „4¥Õ%“ÛXç}.òŠ ÏFÚ?2îp“Ö(l_ѱ s€ º]Ýk¡À@À—ešÍ‘gQø§ìx8/jÁÜAÛ@óõã2¿àiÐ 9p:ñ.løw g´ð½vpw°”")JØÆã «0&ò¤àÑ œ‰ò.!“(ƒ(ø0 †¡¶ÜÀà cHpñ`3àÔ Hjð¼þ· áP:™žÀ l^ïÿpüÀâç já ´Pü`HK NÀ¾0ÛÐŒÀ(÷Ð5¡å40ï ìP ã!o#~Ý D˜PáB† TNœòòeÙòlœ7rQVyãG/RŸ4v–TÃdI¿uõ™¡C¯œOx²ñ«#(̾6fD‘A+ 6~ü,Qޱpü|%áçNJ½0\Ñ#ó.LžŠ >V,C~ߌè¸÷GƲoݾ¡ƒ‡¿8>´Ê¦…Ÿ /oʹša§Š!{Æ]rd­‹§5s¢Ø!áϸ,ç¾ñsÔ¥ :uná©!¦‹ÓGãô€iÓ:€¾Ž…Û¡ÿÄ(5BLã×™>~Ø*#è?mXÊñ·k  !µRQ‹²RÞ5»’ÕáÆù¨<ü°À» HOA~ẩã7jÏy]Bñ[¤G GüÀ@z-[ÿþˆÞÌûJœyÆÐå–>Àpci¤’P(aš<4!ÚLPá"šòfy‚U¼€DkE‡j¬˜ç2MêàGœiö§pØÉãpÑ94‘‚;N‘¡üö#2¶ËÆAè›wðÈã~bñäŒRàpK2<„ág•<(ù¦‘z¾ ]ø“Ÿ7ò(F7LÙ$~Щc•o¾™e›¶ºá§nš:$N¾y„D0¥•;ÿ‹D6pº¬(<¥y¢¨‹âIJñô†³qrk OI‹b§Œ]0!%7ôÆéì¼¢:£ò(M5QYÇ<°À™žEgUçrÞ©gÈY‡•oò gW‡j%Ö oÀT¶Yi÷sÕ<ŽAnZm·ÕÖÕiŽ`A ,£=(Û„VE½Ü çÒ±Nå $nç•ñ²l:†žøÅ\ ÒEžpƹ“X~±uàp¤!eÎpôA…j¾çÐoõp’òæ•dšr‡—éVœqˆ‘@ÀA_ ú5hã`ð Ÿrzq§pC/zðˆNp‚u?Ð RìaÛ0êˆsÆ)ÃFxŠ4øÑ†NˆÂ@F%*çƒ0h!|ÿS}Èè6Ñ VI [Á LÀƒfŒñÅ ÌŒ p@CJa 4ƒ ÄHAð°…;H¢,èpž72jpœ‚¸èB,„@¦(pOƒ‘˜Âc!K+p@ƒ»DȆÐ ¹?äa„úñ£r˜CQäÑ~ä¡ ‚è† n¡ UØeY`×Õ./ˆÁôЋ6¤ LDê=èàrÒ.TXCgðE¥1Ov̓ À :Q‡FáMðC#"AZlpŸ)Eú †L”§ˆG¤.,ÊV`ÁÕÀC# q8 Ђ ¨)à…äÉÕ¨Ã4t[©$Éc Ö(ÿ+Ö0¦>”a „‰0¶PžuŒÁß(†è1ŽCLÂXÙàżñŽNŒcÙXƒ!øŠ`äc Ó€…$yÜJöAD ÅBÓXöYRáÃ-Ì#KWž Dì"²®ó\æ•䥻æ<›ix! DÀ…:ÇâŽ2žë÷xߤ·ÕIJ5UªbÉCt+gQk«]¦HÂ6vÜjQ;tV`p#UjRJãK()eQ±´ "èâ±8ÄñgÌÀ"ÁšAÏ‚‚7DæG3æ ‡sð¹-á¸Ì¢6ežr†ƒ»dÅ(ú ¯„â“’6•GxʆJö7TÁ M°` ÿ¦½¨Œ6Nh‡;¸á ~p£ …ýªÁ NßÜØ”3¸@opCÖ•`Ä3ÒZŠ2ÈCcÕp‡Ææ‘\½ƒ8-øQj¼!šn&8Ôa S¨bÀt@ƒàÀÈ*¤À†&c O€6Š€%íZ£ mˆ„2œ@ °{€z3:3Èb‹àCù‰J”çÔhŠ2¸ÀzAxx”1làZ°­€G‰¿p  < Ï®`ÁI°A ð÷@†$"Æ`Ø`Ûfƒj€C Q‰Ä$pžÚâs”áˆED!ÿW=Ô ¡è†%à·Qã2òÃ'ØÁˆì`Ø¥YTÐÝסEÙ$‹lâ !ù‚úPmˆ`Ôņ½p"wè gÖ‡3ˆÃ cºÈ ‡÷¨Á+Y[¨ƒC¨ƒN¸‡08„J@„>¸ƒG KP~؇*@6ÀNÀ„G„GX„.‚N:2 UÈ‹‡4(±O Á:¨ @2@œs¦<)mƒgÈ"3£r¨„aÈ´VX„\( gXHp•>ð„g(Pð†xˆ…&À:€‚=P,0NØ…O`‚K'`a(HƒfH!(…/ ƒA@0†1:؆Pȃ9…A¸‚0˜ƒ9’(H…Sp9àƒaˆ<ØBˆ?8(>…¢#( W(…XT‡ÿ @~¨k°(˜ƒaÈk¨> 8`ETKH`‡[h~ð)`9”&à‡[h;h;M  ¸HèƒPÀx€…¸‡S/RÀƒ(°Ÿ"@'°~¸ƒ˜RÈYh…C°‚Ú  0~?Ø|p…9X†(@…k@ƒ%p8~  (ˆM‘‘pÐEØ…C6à‡Z€2°ƒðº¦fÈv`B@pØ„h‚œy|ðÎu ‚îj„:¯¢4à‡TÐŒTÀ~`…9Ø,ñdè>HJ€ƒlP…i`˜ê ¯]P G X;àÿ^h{H@(…UÀ‚l°†Ú4E~¸†HQ—·zȃ8h]~È…=è‚凗xN±p•S¸iÈ¢8Ô avJ˜`…: „0&X?Є?pÎFø¾ahJ`T(‚‰ÁS,„@1@„1 …MðC„>°?ƒ\P= Køƒ&õ„¨؇úm5oH‡I˜Õå#8ð€h:UòQ~(+¸ƒ¶Ë9JÈ‚n87P$à‡! ¨5 ][‚ˆ=ˆ7†,ø'!¨;@‚rÿžág8}(0øEx€+ˆèóÎ`X!õ½J°„7tX~È‚ç£â0 ˜WYô`1p’)À!À2>‡¨5®5â1†¾›'ﲚIµ„Jȃh…:X7 (Ð|„:p'6XK0àá.rø„£*&}˜tø? 0H„Kè‚O(ƒ:ÜrÉ…m6f*Xùu8_˜¶m8„Û´†cHqè…g˜bh—A8„sˆ‡1è„rL8q([˜¢Rc˜‡1r„C`‡z˜'DHeˆçfþ¦R„]p‚]áD$Ú¤•Ü©}¨ÿph08F`‡øSd„@u('oQ_#h‚ô1ˆƒ=@¡É5’_£N”º-!»Ü"á[–¨…0ǥ驪•pxˆ!È4ÆÕèSÓGþ—ËðÝeé´òèEoØ cPðÜ&Èiч7(]Gƒ­&IÙ†W@dñ¤F¨…DHÐo³‚€Ä¢xEà³Lp=›´LyG˜…㭠У\І-Ðd¨ ij8‡kdhqøa˜…(±f8…›…7pPƒwP6nXeÇc‡x˜'h¨…¡¢£e(48„r͹yô{hˆ€ NÿÛ\”CPƒ8Øm`ƒ %†<Ø„X<0†*`8˜„ J°†0è10†#ø'~ ‚øQÀ†Ë`, “#h(ñOqët#[$ž[ð©ÿdØ]x>vQHƒ<°e¨ƒ5hSð; ÊsNQ¯Y¨ ;h…010-Ø~X½²Ø…_(˜(BZ؃50a#…6¸ƒØ=1Ï…ap18jfWg‡F¨lI†FÐÃf†_g mzq¨`(¯]†F˜¦F°„x09e#zqx†Ðæ‡ýE†p¸>P`zІ˜^.âƒP˜OP'¨õÄkÑN‘%¢u@à…HHsÙÓh[i¢Ådûdè=2H¶ý qX¶¥ÞiWÜöoàUš—™žwÙ‰Y°‚R!¶„ —hAé±pÛØ ø‹2™o@€ÿ&pê±Æ-#%¬%ÝR.°Hµb‚íyù†y0S8ªæ*øƒhqXƒàt”.@†Ø©…CHÈk%‚š¥aóG~ØE…¸â­×¨™·op”L kZú}ÈI@0€8MŠ‘ŒaèvÀ†P˜œâå‡vE…TGdH……MT }H…R!t•‡{H…¸’7È|U€÷÷q|ð†aèz…R|°‡Yìq&y>…f`D¨®°XØ_›Ùêk¨ƒ.hYÀ‚(ÀT˜8Vƒú‘9@ƒ6ˆ…Y€ƒ;ЃV(ƒ2hÿ[°…=ƒxA(8È…9àC~Èñþ†aøWqlY‚N°ƒ.ØR0ƒSX:èÝt¦ø.„xˆ†U‡â2X!€‘ÿþ÷ñ:B ƒ3€„Jè$˜ƒ@†?ø‚aȆiH@xaˆJsˆ|I3M_•?&æ iÃf?~Ø”ðëQ]·q¦ÖdJ4ê ?hiƼIÃÏ’ŠÝV²léò%̘2Y~óÅKÞ½.ªøy2àÃb7oø˜ðK¶ &~}ê¸ùTÆÒ%=>’é#ì½;É¢å;ÈÕvpâ|™ÖîŠ"ìœÝÚ‘0ŠùaôÖ.Œ›k¸ „L³¦$¿BBTÿÎ,løðJ~¹ZýÚ3m¿QæT‚릨NœÔØ„sí¸8ªr±‡Ž [lºÄ!…*N;ÄîÐYÂǘ›7lm¢ÇžN}Uá÷Z­o‰GñEÖ%LS_r £…aÄÚ·'ÎÍ™lÞ,†Ã¸Ò›7^ô²ñûÅk¿iê„™³‡Œß+^øøCe,¢kîðó/÷ƒO"¥´A 9¼Â/mãN·Œ#Ž6͈³’8éü"?õð" ?ʰO¾lÈ‹ˆ)6'¤’]K BfQD@÷ME)‘J…3d|E.Ý„“#?Ï ’ ,*-Ù+AÖ£8KÞØ"ÿ—…y£ !$ÃOe]²¸dDe²Î8lšù&œ.©ƒ&?Þ@#Îxq"Žšzúù'•á Ä+uÆÔg7j"ZY™ˆ®Ä'‹}: (  z“ $œ‹¡/ Ù?ã$ú飤~“gKŸ’Ê¢8ÊQúª:ß”³Î* ´ð;@”§: ŠS‡€iîñ ?½tA':eðÈ:àDB]<ò³ 'œ¬R‘:%N¶8§È>cɪ¯ê)ë)™ä²ÃˆÀ¯|³áxŽS…:Ór ?# ‘ 2Wðã Ètò7Ës¿LãM>ÓXÆ;î°qË©ÏPãMe×ãÍ5ãȵ¶tã‹ÿº|Ðhî›â4¨Œ+…cB¼`ƒ ®ð#;†€‘Ç ÆÔ†¤´Q‰÷ü@‹`¬¸üÐÊ IÔòÃÀi¤ÁLDÌ,Q'4ªx³á ëð³-dM ±t±È {¬Ìr—Þ˜È=ÒÜ¢‹|ÀƒI4+QÚ,?Ô1G0a ÂZ.ü¬aHüpáÎ$ž¨#GqÔÅ5]ó]ÚŽC‹—|’ Ší[ülbÆÜtsùM/ºœÏÅü”ÀÞTD8?Ô ¡G%üdR‡å¤R„m”ÂOeœaù:˜ÂÏ)nèÁÄDDŒ¥Ó¥¤=uHøIì¥ìA‡±‹q½ÿí~*†Ë/p4³$+÷ˆšh7$8t`bÈÊ¡gðÃW(FòP†m¨ ‚[Äöø¡„að ÿá‡1Rò SŒÂUÊ\˜hLí qÀjW?)fû 0Æcóå¶°{a eÀ¥ bO3l-cCó G>¼‘$DcÒhC/zÑiŒ­zÁÁ}Ø¢p Ç;`ÑŽ`Lj†4ì„ AƒAÔÎF=bRe@„#Q múF-h¡Ãb4¢ÅÒÿV2¥¹ôÈüX¤ßä}Lb‘`ÏŠ^µ%F&2’žlÉœúØ p ê“¦*Úá-“IH¡Ò0‡$<@‚Ôb®r -³ÔŽrt2Q*bZ5v4³0ê€f-{9“9­1hf Ts%êž7â0™4ñCZP‚4€$ºD„#ÙbRD¼qO2ñ㢰±Ô‘­qD½2?Ü‘}D„ƒ¨¦6Wòvð¢¯hB^ ‚¼âØÐ7Ø‘ˆiAԀĵRÁˆxø¢ÓˆF5 ±5äà×Z$¬ÁfHóàG+a ~HB솒'Uü=÷0Å7ä ~؇Ž-taAÈ•âh‡'€Ól° 9Ðÿ dp-žY2"PE"†QaŸC”‰.a ÔðÁ+âZP ‘ØD,ò€ :ŒB 퀌#äP§UCxØ?ª¡…NÔ!uˆ…FQ?´À]=¥7Ü¡sø¢ñà%X0 ˜¤ ?ÖA„2$Eq@@Ç’t¡/,ƒI˜ÄÁ94‚˜@ƒØ3{‚ʨÇàÁ#@|o êàS! AiˆAüXDÈM𣠗8í)¿ŒY c¦Ðà,*0‡P·ÆÂ’§ :\bu*/–ÀÞ'€áà€‚Ø[‰5xáçP1¤Q. C îà'”ÿE+m£ d`‡èø†2°¡ì­B à{J~tB¾€Ã0–d y|ŠO©ø‚¨p :x!É&¼`„émá ^È-H  (Dc^ C4” |Á{ÈC ¡{D¤ßûF(Š¡œlÉáüØF¼°†a<¡{pÃ!cS*+8ÔçY’%\¸#ü .|Q§yàB§ãØ6–Á“ÒƒÂÀ´z¡vô‚ÚÀE§áJWˆÁà‡/ò±"p¼ÃÔíÁE=øqŸ_lCöhcD#³‡Hä u|dE®tž4y£ð؈ª>&Æ[±E-1+•²œ{ÔÑ#ËÑÿ–xã€ðƒ"‚±33›K@*å¶ýÊ‹°"V ‚"ôA¬EŸ€HŽ4£ºI ?Ì-°ƒ'ò#AÆÄ@ 4Â7hÛy¸ÄB†¿=J)yœaÌR8\dDJÒGqÇH¤L á&ØÀx€B2$9ð8ÈÃ:ÔCöÉ8:cLÆ5éC:ÔC=\"¥ŒÃ9œE8 9TÆ5Á„:ŒÃ=(Ã.$€ è@x@&èÛË¡S›Á,Á)\‰1  IC#Љ4HOšxË/t%àÀ,.üTŽt¥1霷˜%™8›°‘ !|ŠC(½Ü½£4B1òC6œÁ8 J8ÜÃ,ˆÂ*AÐÀÖy€&°ÃxD•,¨ÿƒ)”*ðƒÚÍmÃ(¨3”Á4Ö<Œƒ-,ÃNÂø‚9àÜ”Á1Ä.¬D„&¼Á3ÔƒÔ/DÄ8C2¼Á4zC"Ü2ØÃ5äWõ“mâ¦nFD&ØAqíÃD?CØÃ8LÂ!œ 0”‚rxC'ÐÃ8Ä‚9 C4)¬ƒ3Ø*øÀ] >4B0Ð!ØÃd@t€ P€-ìÌ<ˆA&ļBôÌ‚<ƒ9xÁ&ÈÀ ,CôA4Â#zƒ,‚ôÂ"HÂHÂôÔ‚,‚B-¨%˜Á.ÔÁè€.pÌ( p‚ Â"ÌÂDÿ#<Â#¤èж¨‚(àA! A'¸%°A$¸:0A¬hÃ-耂7&A`'ì tA&`ÁÜ'‡Üƒ#ÔÃ>üÂ:˜A&¤À H@ăúñC<ûõÁ(Á(Á4 Ö¤BäÝäÃP ƒè/˜Bl ?Ì!(:TÃì ôÆ(B((>€‚ œ‚<¤dC}ò7h ×x*¨VCƒ„Á1ôœÁØA„Â#íC@Ý’`Ä7TÃ$‚ PÃþ€0‚„À©"åÃ'äC0àA1.ÃÄáEŒÃ+tÀ"ìÿ¦#ÁìÃ’ô 5‚ܜ„ìA2´B¸Â=ÃÔB˜Á˜A%˜7œA˜Â;´À$È|¦tÂ*ìW5¸-àC+¼-„@/lƒ ,?(,Ã>)ìÃ4+`9À‚ÌmÔeÉ£ˆå0Ã6 Æ0ðÁ`Bð8·.‡)p!7ðÈ3èCžœZ'ø@hA5 ACC¼d|‚ϭæÁ1ð+À?ƒx" 6D(Â2€D4¤6üøÀ |æ/A5D‚DÁ0´ð#°ÂHƒ4¸-ÜÊ­lC@#C(ø@@ÿÃd0,8„Ã6§ì”Ê Ã% ÁB(  ­£ôY=üY¶è“ ƒ2´Ã~¸ƒ2˜M:D\J¦?àƒ208´CL’ƒ€dƒ‰€Ãdô‚2”ƒÝ€Ã<[/Ѓ>܃öʃ½ƒ8ȃ2ˆ?ƒ2Œ(üÀ=(Z±DïôV/(C<Ð…2ü?”ÃÐÂ/ÄÁ3@ä9Ð0šZ8̃2°‡;”2¨ƒOÂDÞý@Ô{J Dž‡«ðÈÿ´ ÔýÈÎ|î\pÌxðéÛ©(RPð×¹Ç>BÊC“ÜÃ'¼×pÈÃáì WļX CB< Bðx¢ÿ[AvÃ_r Ìí# ]DC)ÌCy±+RÏ´'à0º›ˆã¡œ >¦RPˆcb²Œ0ê$b|C£<Üó;BÔ€cqKàJ¢C5 ¥<˜Cú>tƒ<2 CKÊçƒm}±,åCÇuÖmÇQšÃ6|B4¥ ´ |îËñˆ7U>Ò` 5\ ;£tÓÕI>yK8ðELp*0TŽèãEêœ\úcbà?ÕÂ%„B"É=& éã#IýâªÀ#L„ƒ9xÂ,°@€Í\ò!Ho*਒B˜×[54R¬‰€‡)$A+\ÿƒ+¸B2®Áœ? B¼B,D*À+DÄ œC+¬‘Ã" ËBpç7`BdC>€‚.ìf=ÑNaÃp•Û?P'Ç6äÂy~B:ðÃ,àÃ6Ô–:<XО‰Ã9hB=ÄÂ"Üà LÀ t@`@ŠÃ<¸.ÀAœBìÂxBTAø€4<‚¤ì‚TA¶®ÀTT¸A*°Á.TÂì5ÔÁ\A¬Â-$ @"‚4‚%ôÁ !pÂ&PÂ.°(PÀ$PÂd8”‚ŒžÁµÈ¡ÔI/PƒÀñƒXƒ:Á „ÿÁ.0"¤'ÈÁ.LA¼ô9HB0¬C0hÌ  €€A9Ü–9σ ª@A2ØðƒàCìEÀ!O~œÁ€Ö/EàAÕEÁ<  ÌÁxB9¤&C.˜(àÁ(ÚX‚„7 :¨ü‚4hÀ@Á$ÔÁ[`v8’•Â#ÈHƒŒÁ"˜„)”v„<|B= ÃÑ6˜€G AzÐ&¼?püÀHA;¸Á.€P.| $dÁ",ÂxÃÃ,I,ØA 2,(A8œÁƒðA+ÌBƒ+œ;¨B% B8ÃØzA1Fÿ&Ä?¨ØB>pCK—;ØBpÊø™à ‡Ì#Ä6LY‘ãü7?ä”kG¶ŒBÒºÁÒÂÅ9å,,Á>ب6xA¬ÁB$ôÁÃ)AÄÁÆ;€Ã(Áä‚6Ьá$‚LÕ1¬A=`B¤2˜‚xÁ.t°Á C2ˆ‚,„Á8M4<ÁLB6dB A3tD@ö†ƒ1dÂèÂUÓm‚Ä ƒ$f8³—Âo’xXÄDŽC6ÌC9 o6LÆ9DÜ8Z:dƒ<ìL6tC9ÈC;ă³ò’ÜC6 ?Ø#σE¤ÿCTeƒUj{ÌÂàC¬Ñ¶ƒÃÏÃ~ÔC³ÓE6„K;ˆ+LãÃ*²D9€{gƒžƒ7°C6|wP•°Á<0Lø‘¾…±r q¢|ü8Œ‡á zˆ¡8V†5*‡Ã]âyZ›8†ƒ4¨ª\ˆ0†¼’\Ôµ‰E˜C)Ä6¨• š«xÏ£±8\ˆ†8 ƒ-ì-T\1#1/{’]ÆÃ,CO1ŽC9, BF¤K˜òL”0MLý£¨’"§J™È1¼>ØAð€ }5½¢È%J>ƒ2(Ú¡¬ƒ94Ê9àäc=¼CPÜ’3p/8”ƒ©Ü¹Cÿà @ • ^7Á'‡ƒh.È“’É(´‚<Â4êœ<…B Â&³¶Ôn¶ s8HÂ}ðÃ2˜DÁ…ƒ=xÂ.¨BpÀ­´@L‚<\¦9ÀÁ*:tB°×Œt5- ÈÎôB\Ã(€Â4¤ƒA2x C2ÈA æ(äB.ÜEL“5œƒ$LF&à1Ä@â§ë³6º ðë¶aC‡!F”øPÜ=GöF½!×Â@ !Øâ'®žWjZè’C M£*X^ɦèL$.™êв£L$0„<éñјHqñ© Š)~üYáçMÿÛ3qàø))VÎÇ 7´ ú©N¬ONT»–mCqøú ƒ—ÏœN9LHðî›7|NÂuK‚¿>d¼øŠ¤‡Ÿšq~œRÂï=ƒÖá)DnR(d â|‘Ôæ¿3"ù%#Âܸ´üŽñY”ŠÕ~²ð¬Á”‡Ÿª$iÛî0ܽO΄ÁÆOY KSûÆË¢.TBvø!Z“$¿!çÂì⇫—q–°Øñ£‹_*E‹EûýŽVUü ¬zŠM ë†ÞÈ!$Ž^HჟXܨƒ<øybµá N~F1%7¨ù†ŸjÀg!p¼1…ˆE¸«„ 9ê9ã~LdŸ8”ÐÿCcÒ€DˆMfe‰'¬É¤ƒuQˆcÖ0†1yÊ/ø G–>›Ð•1øA'.¾Ø§Ža4áb 6ƉP̶ø)%zÄ8Æ›n c¨µzÔagrêÁ‡ŸvÔéfwø¹§vÀI…HìàkœzVd'pÀ©~ÈaGœyȰ…™=ìñyî‡!}Êé&vêá³oÔ©ç›yÆlU-~`)¢4Î!pÂgMoÂÙõÃ]à §H*±DNFƒíFœÀ–ý¦/O÷Œ…”jÒÒµ!jöÙe­uÕÛ‡À9G–Xd9Ç›5]U«§Äɳ-u4ägw¿­×[ožªry´ÿ×ßnœvШavlmhOÊ7áV6XtöPÙ~Spø)§‹@ÞDŒRç¾> †›b¦ñOL“ýFmØ¡x-p²Á‡Ñvòa8ã)kEfС…üÙ] ½9âѧ¡B‰(¤Ð/ßÖæg©žúf]~¾)‚~QD”çÀQǵ<'Du¤|Ã飗§®aòç¿©Ç“ZLIƒƒ¢¶äg¿ÉçŒWˆ`‡—/‹îz‚Ÿ@Ú %_¾P…—NnùæbîŽECOxä”Eðýc“q¾!f¯¿ùãÑCîÁå‹JÆÅkÌHÅî»…ó@ÜéÿDuh fÀ–qÄ!Ç>9ZH… \ìx¤Š-làbŸD¾ð£GÜÀå‰Ö(„Ÿ5*Ùƒ;’Ø%@YÄ l¼p ª,¦B`\…>X>žÐ8àHÃ!Ô áaÄ›ˆ8â `ÜÉPƒ+^ üÁS~qÂ7¾ñÂð#dàC0q~„ m0C'&“/ Áü „!t8¬áˆÂ-ø±~ä5ªa Ç2_üàŸP…mdÝðæ¿± p¾OTið?„ÁƒIŒ$:[h‡#B ˆ;tãeÂ:øasÈA< 8ºa‡-¼áõ ÿ~ qðb·€hq}¤ÁOFX£V)«‚ÀC0 ÔN„! –ÀC7ˆ0™/gBºP†øQE2Ä×à‚!Î`K, éˆÃ:Æq Cä£ KX=VÁB(ÁaˆN1Œ#ôÁ`CÞP +@‚™xÊ*lSd"J½Á>±„3äƒ É…1×@²U¾ªÍؤ±&6ºiO•;Ê1”#r‡Ú¾z0aóÀG`ö„ŽpŒCî˜Ç8Ê!p¬¡Æhƒ;֔ц¨#O^s‡<ø¡“fžÁáÇ*”0xS"ߘ¥8lª,†xÈ9mŽoì¢ßð†;¢Ax?xÄ7b6 cM«;qŽ\ Á:¨ vÀƒÀbú4èЂ>ˆ¡‹8„¶@‚%¤ÃPðƒ2±?tâz@1Öp8´‚l°„ dEB´ˆD&ˆà$ÂöpM-Ò0’^¼cYéXÂ;°‘†*c ð‚"ôp $®âhÇ'”!vPƒ ·8Á4 ‰]ýÅã€Â [È É0 N`ò‘6@‚o? LvxÎ%Ñ 6¼ÿŸ˜ÜàŠ'TÃ5žPƒBš´×Q„Á Ù0Ä%ø‰5¡'ü8Ä\lÁo:ÇøB6øŒ ‚ÞˆGЈP í¸ÄŒ0ß,°sü …-z(‰B‚æ&¢Ž}x¡ †à‡Ê }Ðâ —h13hA„z¸†¼éF;BÕ{°á$D;0†=T‚öÁ"¢M¼VÊâ•‚‡×òbpm8‡–°t„!ã † ܆%b£XB|a 9@÷€†ªà‹Thb¡ÈÄ4ªðP¸#KPÃ;òЊ§PBãÇ XQND°wXB%îQ‡{T •ÿpÄ[åZŠm8£ÉXõ®50^y OÇâEÙÑŽe kll@„‡‚ÕŽvPÏSšS;‚<ñ#{¸Å ‘–yèÃ-ž L;D…«nŒí´¨eÅÒ0‚F¸XSUªN³¥,MEw¨ÞhÆ/rµsE·ñ%DˆÚ•mc˜^ê+£²õz)ù†n;¦ag„ÊYþÚ½$”/¬:¿Ub}Ê:þ€ŽsŸüÞâGÎa†väõøW»Öø2QÍ6$^Ûg‹Ë&Ê}ÄÆ#<°A À¥nE~a²²…XÁdÁeÀ¡|¡ù.¨~¡ p¤Âá°AüŽ-ÀA¸a²Äaö¡ôÿäªh`: jà *þF" pká öàV/Lø¶ k¼o$†+^ølA *WàFÈ  ¡‡¢¨ú  +~Púà±ø¡Êà/cl:Aê€H`¼x` ìpªá >A Ê¡ŒÀÀaæ¦7úÀÂÁRáDá ì á a„|`HAÁ:! <á °¡Dal xa$ìJ¾Ád…ì-¥Þ  ÁAþ@Ý@.®¼Áþ@r¡ øa4 v `z¬ž Р” aªà ð V/ !ö@ÿÒ€$ÁÒ  ! ¤Ѐ ÚàæŽ ˆ€ÀÍø¡löaQ¾Š ž!Á”A&á .Á¨A³`ªg¢¡ôẠš`ÀÃ/†@¬v¨0$ ž"¤€ä€=ì˜ä/ø¡oaš _Áß á¼¯|@<` ÈÀv­×~MXÊÝÜ záØ’mÙš ²+®Vå~Aø(cúÀ?¼A–`(&¡ Ì€A’! Ð ¶ÁþÀ!~`L¡nÈ‚A8á2Ïô€Ð  Àá®á”àÿ\æ€5ÎA£”ÅfM(Ìa!,œ!Z̰F.Ô–Îa‹*”@è@®DâÁ2¹à ö!ö€ áÀ ¸ÒA `àa–@ ”aB!tÁ¸À«& ØÚ€žB&ÁëBÊéjâÁD Aº@,6œœ¯LŠAÔ Ö$÷±PHz*JpŦ°_¾ac< ^+O4lWþj^4,=ñÚ@$ÁÆÊÊ Uz …ôÉfàª7Žàj \ì*¨”e–xQÅb:„²ªª÷"ôC®bBêÁ®[xïZfBÿ£ëAI°¥ÐžAت¯üBmØÅEÇäaEFqÔ^(/V  ì$"4ëkJoænòe8«a†ô!8æ¼€Z`Æà KžaO¬:haŸÜÄgÀL¿TÒÁxœA! ðøÜäZÚÔøö*¤(±/""°!C¯tIFrá :ÀF  î88æ Í!_Æà€ Îà‚­I‚P­¦b¸z%_x%_úÀè€ÄòÅÃ4uT-õ3õ)z j^j @a²!0Do¯C^„°´Ž5† Z´¦„A^AÖ€b@< ê ãÿ| 2 ÚŠ€Øa Du9AÂÁŒŠ|¡ !~á ¾ ðáÒ€ðáÒ@ Ž¡è`B¾à¤Ááá’С›Þ‘,¡þ@!ŠàðáÒ`ðaÎ ¡òàPccÂ!"á*ÂHD¡œA„`VaÖÁY—Àz"¼6£ù  v`*€n1a¡¡ Á6Á–  bæà+á ²¡Á ¸îà PAÏta0 p!!®@^!¨@ê <á 2aNáüàðÀÿÐàR 0çÒ xAž ß¬ÀjYƒzàøaœ!Šê 4á ”aöàî`~aL@Z–"Ê¡>L ¦a (àJA!Ò!W("Ÿ¼€øˆô`Ví)ÈàÔ …Öà :a^a®Þ*û Ê! ŠÁPÀô€ #náú£Œ@ "¡ ø@]®@haFr îàÁq… ²aa€ƒ€€ö€BÒŒ`ÁÄJ xu-¾<á’æ† ÝJHˆ !a öÁášá†Þ –AÈÇ¢AÿÁöá<Á ÌaNAò€!FAü  øö ’`!>ŽÁ H!&æ,aô ĆŽÀPÁâÀ~zBÁРp{À(êbn °¡¸À’!,7Xâ¡s!‚Bp2¹AC Ïg¾á„àì À Ž¡ˆ Ö¡Ô` 6ÁT£Ž¡À€"á’A ¸`ÖÎü¢ ’@`œØ¡¢¡ˆÀ²È4¡”À„ʈ æ æaôàaÍС„@ ¸Að  | Þ!c ¨¦M"š½a$™j¡èáŽïHsè šaMP®!‚Å5*m¢x%XÞÓëÀ†œ{Åëz ÃyÑ9LÄz¼®œ9††À5ÒYœ_£Ç9Xd da€AVmºô$XKÃäÏ¥@¡^VO"ïA/D›ï¢1º¢átD9£Iâ:ùB”¢?”M¤á‚8N+LÝ”O»! ;opensips-2.2.2/modules/seas/doc/images/image026.png000066400000000000000000000514071300170765700220270ustar00rootroot00000000000000‰PNG  IHDR¦ÚÄózîsRGB®Îé pHYsÄÄ•+R­IDATx^í¬UÕïéK¡‚ mŠí\µÎ€ùä“#Gެ—¹/½ôÒæÍ›'NœX=úöí;zôèê…¯E@RKàøñã­­­ˆÌ9Éëîîž+÷©"PwÚ9: øÅFÅBÙrèšUö±¶;§ž¦wØ€B!pè×UW]Å5w¸ÏSÜ‘eQTrD’1#uˆ@ ¨•w^¦D¶¿Šea°•‡DjѤAÅ—ˆ1:xðLk‘ïÑZ 5åˆhÿþ“o¿ý¶ â¬Y-!‘­<³*Ô‚ šêB(ÖÊ+á7…E\&‰€ZyU/´Âb šAÈ™éZ†¬ÌÍÓ;!póo@£kzÇôËKÌöíï—çQ¾D ¡ èmÂÙGûÎBüÿø¸ÝG'Có7Sr0°cˆ_«‹ bj+ïÞ& NÁ‰@MHòÆ#£–¨ I^Õ±{vomv\ÁQ0ôõ ‰˜®îm"H£äU=§<»·ì e¦”·Y@%É(¯{[IŒò+õ" É«yŸî-ÛØYGõPe±”‘žP÷öôéˆýþʈB^D %$y5ʈ`÷¶àËYt§¥wQO#gÀ䃉ìÞ¢¹‰†­ÀD E$y5ÊŒ`÷¶X”n¥Ùѳ„¨±MË–8»™ú$Òuoý7Iõ VnD U$y²ƒ×¦hŠÏ+/]÷¶„/ƒ6Ù¦C#|˜qnHwŸÎý'™Ç®Å&Lô…}¦ËøØìÞ–vÏh£œdÛ¡>Iˆ$ É+€5AS"ÖÏFò 9pÝÛbÑv °°&|˜qnÈÁƒlêÄŽ-[ØY Áp®{[:]lÁ îc q)ɽT€$/ÌÖg—ssòYðfÈuoÍåE]T,_Ù”¹¾_±X˜ Ç~Èt„ -!:Ÿy|Ö½-–8>qU¯X+d(F@Á§½l„º‡ ¶éÒžrÙ' ÐFð ¬I0¡E5‰S‘ˆ@ÓPǶi³V È' ÉS©ÈI^†2[I¨TòŽ;¶gÏžg{ÇŸ8qBLE@D µÊ|cûꫯîÞ½›Úì£\Áƒ™´×]wÝäÉ“ýÓÜÕÕµzõê‰'ú{‰ë’}JVuä><¦CD k*zcKSnÅŠëׯçó]ùzJnÒâ[²d ²˜5²J¯ˆ@Ê ÄëØ¢e+W®Dì,U4èX@ƒÎæÇÚb)>dQª—òì—y"51$쮓ϯuttД›3gŽ“¼öövî̘1à ªG«0kL•^Ôð•<”kÛ¶m–ŒË.»lþüùcÆŒ)˜ª›o¾ùÞ{ïuª·eË–Ô&^†‰€d€¯ä9åBËîºë®– 5xð`÷.‚^°º·Y+UJ¯¤–€—äÑÄsãwÓ§O/­w–TÚz®¡Ç»ÝÔ¦_†‰€dŠ€—ä9ÍBÅŠõgó©1U… ùÑ*ÌS%VD µ¼$ïý÷ß·¸OÒø¤‡×¼Ð@"}Z…>ʈ€TH ZòXPáæß 6¬Âøä]D@êH Zò‚ß»9rdmUÔ" "P!hÉë>û]UòúõëWa|ò." u$-yAã4*WǬRÔ" •ˆ'y•ǧD@D Ž¢%oèСf/1Ø*ªŽ¶*j¨@´ä]z饯!ï" "Ñ’Çê1·ŽÂ½Êð´^;†z‚’3Úˆ–<ìp_\=r䈿Yè݃>ÈÞ*ëÖ­“öùs“Kêð’<7eþâeËÔäãöÕK€B^’wýõ×[ßýòß#àõ×_7;ØlJ³[ü³D.E@ªGÀKò,·ºvß¾}>»~nÚ´É-S›4iRõ E@DÀŸ€—äÜÔ©SÝKŒ 6”Þ½;pà€ÁNñþ›¯øÛ-—" "P_É£¡ÇNy}×]ËoîÙÇ€œÞ¡’ßÿþ÷Ë0K^D@D â}Ô‘ÆÝöíÛƒ6CÔÜûÜÐ7yÄŽðÌq‰´[uŒD$" e(ÿ£ŽtQQ1^G¸¸‘?6L¶#(…ôgù>†Þ• y¸|;¶.\TláÂ… ,°O8º>Ø¿Üç)_>Ó[Ú¸™!÷" Õ&¯c[ÐfêÙžze·éÔ±­v6+|È2ò;¶©ÑšCìÊÖ»,ç„Ò."Pc±;¶5¶Oщ€ˆ@‚$y ÂTP" i' ÉK{É> Hò„© D@ÒN@’—ö’}"  ä%SA‰€¤€$/í9$ûD@$ ÉK¦‚H;I^ÚsHö‰€$H g•[£g•3Tµ'Ðݧ+™t¤@kkŸÖómJxÁYÚ,{D 6víê‘âÕµ,===¥ë!µòüaæ\.ÛÕó§?ý)ž¹®>ŽÑ£©Ûk|P†´ éSûˆkœÎÆŠnm®š{~a¶ò$yñòóþ-‡Z~>+ž¹®2îµÝsûÔAy$yUÎØ²‚’<½¾( «<‰€4&J%ïØÙÃÿû¶ JV‹€4r$uÛºu+ŸõY²dÉ#g|¹YúãgÍÀLihX±%›uvvò5ÛÐÇ. `ßÁàãghßž={–‰ hZ1$.¬}°Ñ}Ö‡/]ðéw¸ï`à`Û¶mëÖ­kZlJ˜ˆ@cð•<ôî±Ç£gÉDæ:::hÍñéwðïäÉ“ðéwè2Çó‘ðˆo9ò‘3{„DÒnNrJ•ˆ@ð’TØ0Ÿe»©|õÅ믿n±>Ü_ïpO{Ð5ôL4uøèY¶ëø£¥ÚÅ<ÅM± øýó”_‚9—‘Û}PÐÏ÷B áð{Ýøœ>i”›j §P%ôÁú£GfrÇèoûÛÄûÕƒ)Z|½BÆò ^Tâ…q­œ—ÀAPÁ/<äoè~!®vÊ£ÂèØ×T´´´D…~ŽJÚ-×5ŽBæÜw÷¡Ìñ~¼DÂyšÛÚ ¿ÃA­¾à×èàÆ\SŽ{‹x¡Jwûm„¼œýUœç7>GŒÞSær·F ¦R4åBæh!æÚt¹ •Z‡,nã_T )|ë×çI–Ѿã/úôBãÎy9W÷<­1tõíW‡î×(Áq¢‰¼Ó§O»µ#GŽŒrÎí¥—^׋ܗG€v™ÕêTËVÐí´ºû¹žHžêõìÚÅo!/öÒÚJ'%xZ“¡·qÞýVìå!mT_ÝÝ4ÊrÝáÛo/¸×ªdE"Ô™0•,ØçÍmÓ§O隸QpEH^÷Ù ]Ô~ýúÅMÕˆ#Ì ºÙ¼â¨‘ûînÚeÄ…råÆ\‚ÛõVÈOÙ9Ïžîn+å8y!ëÑß}üœ6ä ž½rQº_£„+š<ÖX£ÁU¢î1 sÓ0ra”~³ÑÚJyˆ5®•Úœ‰~cëL/#Áýû÷OmÊ›É0×%)¶ƒUwÁŠŲuФAMS±7S^G¦Åª±ˆ½´Z[môíÜ mkn$.÷®£ÈÈ/µ`®jlü#†ä5~b›4gkE_Ãõ¦»Xߤè8½=Ù\çHGÝ ”˜ ²-òUÕY÷¼·årXO®Ih‡Ý±‘ßœúÏh©;Ÿ8HòâÐJ¥[+äÑmðÞŠý¼jüìܘNÁŸŠõXuÔ•@ï êúbghŠY]Îð¨2±BÂ8 1Úÿ¯ïë ¤ÂÈ#$oèСƒqeìˆ djO…¶fÇ;e®ÄtP)·Šúƒ!¹Ñ™ÒGþ/!7âÓ;¦cuûíkÒê= OzŸ#CÅŽdFõxå…ð1 bÚG79'óÖ—ž8•¬U -Bò¾óï¸è?ù䓸¦|øá‡q½È½(1¤ "ÿ0ØÀß™#×{ÍÈ´•oÓ>«Þ›¬noÜreS‘‹6$WöQ°=ˆðÙtʆµû¬^,1´ljï1Bò RÉtb7ÏMЫ} 1F›QU쬰”‡ß¼·¶ÚÜ+W¾]צð„ÕFš1›}:d^ƒ~­­Öî£lX5=­=õ¨£Çò®¸â KEÜéćvsú@—z)3°wîhá³¥^%¸×£½Š-pœ-ßVÄMøò'¬¦ “Ì9Ÿ€±sÑ[¹KöR+wtw—꺶æ†ùpu^!*Št>–<60ÓѯgŸ}Ö?n7Ú‰ž›ø.—Ž€â_½ƒS ò/6l×ÚÊ»ÚÒ³”é$`ƒæãÝݶÍÍeAË躖ðÕ4–hÉ£oëv‚Bòh»ùä4.ô4—×]w¹)€ÍM‰îËä•òÜ"ܼUGçlhõzñWžÍòU=ÖŠÏMM/¾ ŠMJ·Y)vXsÏš~ Ìç-Yõ’–HÈÑ’G4Á 6lعÿzçÚƒ —Þ_/‘dd<[VúšuI‚¥œKL=Í8ÒNþÙN(o¢ nA=G¾óNö¼‰œg{Ä÷¡ 4´Ør´Æbå%y4ô¦OŸn £{Ë–PlZ°YAO^8½£K{×]w5‘F´Ö*öÊqþ J›¡’«ð ï8pf9Z¾‰Ýˆy&›m†ÙönÂ×¶½Ë_JaCnŠ /[Îh¦Ñ/É#‘ Ʊ²{{Ë^Ç|çŒO[°›žµéA¾}±råJןÅñ½÷Þë3+²Ñ!ÖßþÞ¹&VÄÏÔÒgçëS?SÐMïÂK)ZϬBãgpnF^Îcn$Û6D µ ëŸÒŒYàóv¾ TϦÌMxâÚ¶(¼t¬µMÊs3òÌ#¾l¯Š•Ó|LMCîEo´’–Ýoû['jÅ€Øñž—î°§Þi‹Ðs$+Ù²ÐþŽ2Eö¼-9‡º·¼üì#Jp©½$yy×Ûr,ýK¨MoÚ-B“Âç^RÅš7Qž¯¤l.;œÊ· F„Í™3‡-ŽÙ1ȵø‚¸É»6OæóžzWvÒšÓckn7”Ò=žö M€ÞMSlzÛ©1·¹ÓªŽbzG<Âí1åŽsû¬§Ì€·5-u¤€Ûê&–¡åùŠE=Çkå…,d?¨#GŽ=z”ûìõõ¯½<™S+¯Yß}ÚÒi_ÉŒ%ÈO=_³Ì÷¯¹æþîÚµkúôeÁRñÅ_¼ýöÛÏ=÷\~y@OMR?<ñæ§÷îÆûï¿on‚7ƒîÍÇgŽçþO<Š‹äàæ¶Û®Ä Îo("îK-KÂWãFöÒK/]ýõÚ9.·‚îwî<úôÓOSøè„}Jꪫ®Ê¯ô(L+:s7æòÛßÞuË-·ëðßíú?O=õÞ7mšŠË*sîó«à×RÌrBÞ¾½µ„s€wZ"ûöíëèèjƒPáoÛ¶í Ò^Öû1 bq­ZÅ|¯ (‰pN HgÏžÌÓ-[ñÉ@šHÖžâ€0Î&L˜@Óél¢&‘eÐæüÅÊÁæüÉúüò`áo¹i¢yñÅt*i!˜Äȇ[i­Zó;gÍÌÞÒa¹¼}ûbç Gæ‚wjœ;±ÇòøŽ-Š^c+›2º®®(($mÕªŽ_¬\øôS+øŽ¥á¬V˜hî w”<\RVøËœŽCˆ êI°Åp}kà~Tî°û¸ ßyä›MA—víP²ÑVA;‘oônÆŒÖÁÁq±ˆ¸%M™§å%Š,6! õC™ÜÇM²Þ8óÂçëÝñb\…+êÅ dbéòPžµù¾Èqò$ t<%9” ì,Ø+O*ҲÉ-yÄÄ7ù|mÙQÊ£ 4‹BI½JMHUÉI§ìR9Ëngç~ÜsŸßƒÕ™üEt(R|M8$pHXÁîŒ1' q'w¨ØCwÌ%’¼ôbæÏ¿‘¿n0ˆ_#!:¸hÑ$sôN,¡;‘c7™*$»w'½óî™–ŸjnRÉ®ßl}¾˜3ŠÄðáÃ8Z8wÞ9Š)Q„L¾»ZKŽÚ¡ Æ“@PåHÑ¢è‡N þ¬±qãkÈÓÂù®{re-Cqìº6TÔ4‹ò»)¸ B\•¨zt±‡vœkD˜öÑ%¯^ŒM2ÃÿdeÁj x³„3àŒ5ŠÊ24.f9Râ5…Õ“'OæÌæZ\ôHè©X˜Î#žäñ™Z÷ùÚ-[¶¤3I a25aÁRÞû†ëÌ‹6ª~ ÅV/1¨J)Ž[·´*QUQ`$­T4—q(´µÝª ¼tà64ú _‘#•—zÓ ‡NÆŽCöU5’öfšjãIÞE]4qâDKªº·5(ß%^íû¤Iƒøz‹UÞAÈf6R<¬•Š.»!¼0Q±T^È\†Cç´¶ÿ2ƒvï‘©¡o)8© –ÙÕsOò°ãæ›o¦n7ƒÔ½­^Æø‡ÌŒ„ãDº3þàò£>Šå^ŽƒJÏ(=uÉ…cí»nøZ>Û ËÀhµ…Žü½uŸtFõx}ìivíKBlÉÃÄ©S§ª{[û¬ÊÑÚw”°Ð£Ê»3ô†x¯:ó_ÀÙkª@*?†ÓÀ¤m°a¸‚¹É蘵¯™™\ÌO_~ùeJBÁeåå!’ªÜÀ†8°“9+‘¾êâ É£Rš>}º™«îmyÙÆ@£¼ýR¾™~e•¤MI-Va¾óÎ;<½vlxÖ;7+ïÎD¦‹—ÅtaÚÚÚ˜5FWšЩ­Ø#ÓR_Ô%¨Õï~W@#ì¦U6æ¬ gÊ Ã©ÁiF¡¹òP¬ÔUB]¶I66?‰¿\»9+•„\ ¿åHvŒ3FÝÛJòƒÅ@Åjl– ±vÅ:L^/VaÚÐ ¹Pì¥A…ݙҩs“lì×È:›øZ “,ûåU;šÅà©ã‚¹I¥âÈX ήIH1 ‚dµNþò² R+%æ‡ã_ð­³l|pVJÁ™›)Éß2%ëÕ½­$ )Vc‡z­ƒàªk›³’_aÚâGŠ/s¯Š™áº3X‰©ù~‰Ášà$ìd»6_“MKJB㥼MDçÅ( !8¹à_0Çh@ñ›Y•aÎèöRARHŠ­²p ´òPFz ¾±Å<+ºÅæ'¥¶Œ½àÌ!³î-Õ w¬{;sæÌ2€fÖ ½¶Nhøð×l"Ã1¨ ·`uÝ;gås8ÛBqœ1ÁÅ–Xò#im-µû.áX˜q!óÆù¾l1Cx¹¶çªŽ`“ɨLCÄAOó…¸(jæÕ›5k1:â^DÄÈÍM›Z©u~ÿÜ›¼¹¢âad#ÔÒçujÁ\ !àÞÖKÏ÷ó_×Ú ¤˜G4£„mµÉe¯Í£Ø=Å6P¹ì²Ë.\´ŒÒßÙÙÉÂÛÐSç…w»K–,)mU›Ìn¬X2¸yTceP-­­õæQ%Ò¦··µÌxÅ%"P!Š:¶·º·漋€ÔŒ@’‡­z{[³ SD" •HFòÔ½­$äWD f’‘¼üîío¼Q³4("ð$˜ä…º·ÌÔó´@ÎD@D f’”¼`÷¶f PD" "àO IÉ uoýK¨ J§"ç[œœÌSME®MF6_,Í1™•awÌyàÔ©SiÈ üO­ÂÔÛfŸ·Ð Ž6‡LMp*rò’¦W_}ÕÖÞfSòìŒîcçu,7D=iÒ¤bûÈó4U¦²‚*¸Lªi$•ÿü¡¾Å€Ø_Ø÷‡Ÿ-ß´fÍÜb–°¼—O²<ó̺šZkÉãË>o¾™û¼î\À®È>DöìÙãj¶É“'—öÒd Î(:|ä) ß¾yíßOìØ±£t)O‰©,’gû¿àD’<Ÿš¿IÞæÍ›ÁåÕÊóÇZžË擼ÔÖ–¡ JOžaÃöÝ»wKòÊûùø’ä™ä%üú½܈€ˆ@½HòêE^ñŠ€Ô€$¯Ð¥ˆ@½HòêE^ñŠ€Ô€$¯Ð¥ˆ@½HòêE^ñŠ€Ô€$¯Ð¥ˆ@½HòêE^ñŠ€Ô€$¯Ð¥ˆ@½HòêE^ñfšÀCu8ûú%?þñC\¼õÖ1®S¥Lõ¡'É󡔌Vü\püaqÁiº‹dâH(”25¡×:˜)S¦<ÿüóÄzüøq[¾wïÞïÝ2òÛ¶íåï|ÖÙù«5krk¤¬Àð/7ùÜç¯Í…{Ä}ó’àQÌTâåD©ÓcªOª%y>”’qÓóQ¿Ùû*amüçç8­(³ò?™Ð ¥LM4ݵ lܸ+Ùª±4hЈ#Ö;ß0áï^~å(}sȘÇ|þü;LѸÉyÿÿðÃsgÅŠ·Þzë±cÇLû¸˜9s¦=zòÉ'ÇŽ›l2Š™ºn]nϕуû¥ÇTŸ„Kò|(%ãfܸq”fÂbC.Ø>䦛nš;w'òG‚sÊ”v«6éïØ\r“ œñˆŸö”GãÇO±ûÉXÙJR¦ò›t©£G*ø7A;=¨Іb«!>uoE¢7¯§\}õ`¾• +¾!Ã>F½¹<þÔ C‡åzäȑÆ]xÏ=?à;å9rÑ1¿uízsÃ#®³õyz¿üåÒM›6ñïàÁ¹º3T¢’‚SÐT‹+m¦F&Y’‰(I4ܨϭEF-¸ß*=SÊý÷=LÄ‘šÜƶ¹Éõòå˯;”&¥œjßug’4ñlX‰˜Jëà•Þƒ´TÃȆ±@°, \X{³ îÐvãäÿRgp‡š’kz¯\ýºGîf²d š4 =¦F&ÜKòhz,9{ØhTäôé8;轺-òiÓY ¦»Ê_tpÞ¼y\¬]úO TsÁû;ZsÖ¤”sBàäÂ=2˜Ê/Ñt.èÁYºtÄ%·à]•r<®UݧßT/É#mŸ=¶oßÎ}|蘗™rcu8GÁ J6ãÁœtgxmç : @ôq~Ý#w3qŒ‰˜ŠÎTÒ•¸‘Y0Ä­z9^9Ìô›ê+yŽ*ÆK¢ÊÑ(„Ò\ÇœU©Ã’H.4©‰¤W44Ø’Gjy]Í×|:Ù2^D ›Ê‘wf̘\$ï1êÇòÜÒ¬»ººV¯^=qâÄêeIß¾}WuŒ®<üû·jùù¬P8=Ëv-^ÜævïæÆ¯=óLnÅúìDð³å›Ö¬™[ÌŒô˜ºaÃv6\X¾ü6gj[Ûâá˦ûì^ÛM [[}Ü&éfÙ®ž!mCBŸìÜ?mÚ´Y³ÎìuøÝÏï˜ó€­ž®ïq¨÷(]–-Û5{öìúÚIìù¦¶·w¸q€W¯Íí¨6÷üÂÀ.kÇ[Z6oÎí]~+ÏêÞÖ½pÈ€”¸üŠþìh¿áú9!)Áù쨳o¹å–úi±GšZI¦WÔÊ#bÖ`tvvÚö£Gf{K³&Ë­<«ØmÓ§º´¾-šTÌ L½mö¹f{}­ ™Ú­¼ú"mšØlåU*yAuãÚuo³,yMSÎê›I^}ù§*ö%¯¢Ž­AQ÷6U…Cƈ€” €äº&'«‰€4d$O““"³e¤ˆ@2’—ß½ýðÃWD@ÒF 1É uoÙF4mI•=" "¤ä»·"+" )$À$•PªV¬XñÞ{ï¹›\}‘ÂlnD“šf’Ê–-g¦×Ö=&MTú ßk×vó‘ܺۉ!Sœ¤’¼ä''cº$/ ¨mhÉÓ‚³2Ê^Uœ%/y¤0¸ö6ƒ’§Õe”r¼4åê Ã?.XqàÀÊc’ ¯Zpoj‚­<¯±<>¢Ê†C† ñÉ&'³øÌ¼ >ÜÇK3¹ùðÄ›£Fzÿýÿ[÷óé§VØŠÅb¦Ž7®îvbÀÃ?üÑG5S1PZÒIÀKòØ"… T8ÚÛÛ=“Áb[óâVÝzz”3¨/É«^ô YD@jI@’WKÚŠKD Î$yuÎE/"PK’¼ZÒV\" u& É«s(zZäÕ’¶â¨3I^3@Ñ‹€Ô’€$¯–´—ˆ@ Hò꜊>›XSuÁñ¤ Nƒà.RŤLõá&Éó¡$7"0žúýËÞW tã??Çiz÷Ú¿çD0mG™êƒN’çC)7=ÔyàÀ;„ÅÅüo½uŒëdBO4”25Ñt×.0–6¿újNòXÀÎÁÅïŸ{ó¦›nêìüÕܹ‹àoç”)í|ð7)0v—Üäg<¢qaOy4~ü»Ÿ`JŠ™JDœ(uzLõIµ$χR2n¦L™òüóÏ[’ÙWn÷îÝû½[FlÛ¶—“²k…{ÍšÜ7Õ­¿Ã¿V|qÀ}þš)\¸GÜ7/  šêÌ£g©HÐÎÆ jذ =JÈvÈðïÕW&EwþÃ-K—Îçƒß,Qߺu+78s<7qcIæzÁ‚©–äùPJÆÍ¸qW¾ùæ›HårĈhÅ”³nÝ:"=¸ßã?>þ¦h/¿r”óþ;þž-F¸ÃÆ«·Þzë±cÇL5¸˜9s¦=zòÉ'ÇŽ›Œ‰gCIÊTZ£þv 'Íýï|È®b‰›šlÂk»;wÒ†š1ã&Z|ÖÖãèÛ¿…¿+W®¼in.½ôRþ^xaNר;Í=Í<÷(ñT3Õ"J•©‘i—äE"JØ_¡ ÅþZhW‚)ô§ 3f ñÆo>|˜XÇÏÍ¡C‡r=räH*Û{îùvìØqäÈôÑ,ã •°•½ÁUn*i¡¿Æiæ!ôU2µɯv˜4ÜvïÞm-2*?Úz.FêEÊ€vq¤Õonr½|ùòkÇ¥‰·iÓ¦¿|~È•‡j˜Ý@¦F&_’‰(IlÃM›ŽŸ=]Š{KK®~¶ƒFU==Wô)ÇÜ·¾ Ç_ zÿþýñ…hÒµ)½©w…v'b*rŒœÁßs…†5wøëׯ·äÐP¢Úã‚v=Åœ7oÿ®]úOÜá‚Zs®¡Ê5“ ÷ˆÞn5ø4ÕâJ›©‘É—äE"JÒãÓN渠­GèÖÖCãc$áC׸Ë<úƒæ†G\ÿfëó4ô~ùË¥Tìü;xpnèÇuˆ’4´OŸDL½ûî»±“sذa—\r‰¥K‡#`ƒw–ûvA»ÞÝAûø—nß2Æ5Éq«5ƒ.Cá$9ßÔé1µtÚ½6‚'ˆ={öØøhÜã®»îâËg¥}uuu­^½zâĉq÷wß·oßU£ýÝsyÿ–C-?ŸzÚ³l×âÅm®Í¨_{æ™Ü]Ùo'è׸RN‡×jøXGmöþNÄÔ ¶Óì]¾ü6—À¦ùö…6‚Uhq\ÿàs•Ï©S|·¬ŒãôéÓq,÷°ŽCQ¥K"¨ÈÔDÒ«@š€:¶)Í>×Á1û\'"…æ6©)¤'“jL ¶äñŲâ6Þ¤CD@Ò@ ¶äa4*æ¤!‘²AD@Œ@9’'v" "Р$y šq2[D ’¼r¨Éˆ@ƒä5hÆÉlrHòÊ¡&?"  J@’× '³E@Ê![ò>þøc62òÔ{”.å)1•ÍS?ûì³æÛV€\-o?Ž*‡>ß8âò+Jmf“ZSë)y,8»÷Þ{=óç‰GPMÖÊëMÑ 'Ožô¤TUg“& *±­Þáw?gCªªàxÈÔæØIÅ?ùrY‚@%oÉ’%ÉfOóI^²|²š$/›ù^0Õ J^ì×Êh\’¼ÆÍ;Y."›€$/62yh\’¼ÆÍ;Y."›€$/62yh\’¼ÆÍ;Y."›@ì©ÈÌËÓ$•æÐÎbg‚<"Р“Tú¬íîéé¹øâ‹•« xÿý÷¯¾ýö>‹/àO¼ ¯¾@õb%&R"›`^^[uŒEEŽ#Üÿ–üïgôÓ½¶›UTµç¿lW϶!".ëó§*% ´zéATCòbåO«°Ñ%%E=»zba‘ãHýúõ´hR¤3¤Nò|Œ–›*HPò^zé¥òŒlúV^yXä+)’¼¤H6C8II^UY4|+¯ªtxI^¡,=’<½±ÍRiPZE ó$y™/ Y" ÉËRn+­"y’¼Ì,äe)·•VÈ<I^æ‹€ˆ@–Hò²”ÛJ«dž€$/óE@D K$yYÊm¥U2O@’—ù" "%’¼,å¶Ò*™' ÉË|ÈI^–r[iÌäe¾€d‰€$/K¹­´Š@æ Hò2_@²D |É;|øðÖ­[W¬XÁ¦Çvp½nݺW_}5K•VF"PŽä!j¨ÛÊ•+÷íÛ÷Þ{ï}|öàúàÁƒëׯGþž}öÙF [E@²A žä8q‚v¢†º9>|ÐÇv DòÅl0T*E@†@ ÉCï:;;iÇYâ.»ì²3f<üðîc;þüŽŽî›dQª×0A†Š@6ļ'žx‚æ›a™0aÂÂ… o¾ùæ:P\3†ûãÆsªÇx_6H*•"  @ÀWò診Î,»™3g–H\{{ûèÑ£Íã}4€„LÈ/ÉC³ÜGliÁѸ‹$óýïŸ>s¶cÇŽH÷r " 5 à%y/¾ø¢uiQ±©S§ú˜E'wøðáöZãôéÓ>^äFD@ªMÀKòÞx㠳㢋. Þ•6ކ/4x¹1gΜj'Cá‹€ˆ€/ÉûóŸÿla9Ò'Psƒ8ú룰r)" eˆ–¼cÇŽ¹µ#FŒ(;&yºˆ–¼#GŽ8+û÷ï_w‹e€43îî>¡3ñÔÖ l®M,ñáDKÞÑ£G-X^D <8~ò!"àG »ÏÁõ¬Ùø [(û{ÌšË Î?‚Íê¤Pf(–¤B†Sƒ„TÃìt…É8RèLܾD͵‰%>/É›4i’…Ìpžÿ>ïÝLÌé=h$^~ùåñmˆÖ>W¯¼=xZt†v‚é'ÌP,Õ¨Øk™(¨lð’<Ëmü¹{·× šun3Ñ+®¸"›p•j´ð’<Œv =vÍÛ´iSéd°FmÆ ®‰ç¹ùJÚÐÈæ#à+ylwìvýñÃÇ€œÞyî'Ú ìd¶ˆ@È!y¤-ø] tÍ^È>rönãÉ“'³#|ÑÁ" ML žä³Ï˜¹Éz!:ܧ ¼`Á$¯‰Á)i" H ¶ä‘HûŒ™}Â]CàP@®¹Ã}ºÀš•Òˆ¥A6‹@Ó(Gò Š}™CàP@®¹£—M_h”@h\åK^ã¦Y–‹€d–€$/³Y¯„‹@ Hò²˜ëJ³d–€$/³Y¯„‹@ Hò²˜ëJ³d–€$/³Y¯„‹@ |åË/¿\ºt)=ÕqæpWW×êÕ«Y[½è۷搜ѕ‡ÿ–C-?ŸUy8 !AÝk»çöîÏVãcÙ®žÜžùŸÝ6­Æö4utl#è—¾µ¹=ëæžï˜ 9ÞÒ²yófIòü8žu%ɋǫ&®S%y';÷úé§_|qM’ž•HÞÿý«o¿ÝKõ$yV(ªÛÊ[Ûí6ÌJ¬~:Q ÏuÚ$oÚ´i³fµTŸP†bhoïp㯖¼$¯’׳l×âÅmîSp*‰ÕLj[Ûâá˦ûÄ Éó¡ÔÐn”<½¾hè’ ãE@âäÅã%×"  M@’×ÐÙ'ãE@âÐÛx¼ ¾±ÕX^<ˆ~®38–×ÕõÂsÏ=çðð1Õ…óo»üŠþ!`ùÎî¼sTp(Ù|÷»ß]¾ü¶ßûîÛÈ®®;™—¶lÙ®Y±}ûâÒnp€÷GÝýâ‹/ÞrË-÷ÝwC0´Ãï~þ Vâš>}Y‰ˆ|ÆÁËûª_ñ“«jزåо}ûìC"ì»õ½ï}oÊ”¡ù‘Qøž~ú£GÚ£o¼qîùSÖ®íÞ¿ÿµ×^*y8þÉOžæ/¥ŸRøÀ’5ÅRBì”ÎnÌÞùÙ`ð#Kî ý1²³óŒ æ¦X\ù~«Å·AÂ5ìo¿ýö„ ®¼òÊAƒ:t)A5BŠÀg|bEûë¿þë7Þxgóæ˜=û‹`Öÿ©÷عsB¨8¹LùÖÀ_¼ßá!ÌoûÛnnÛŽ@‚÷{ç`Ñ¢Iíí¯£°ÓÚþ[°/.WÊÉþ¡C‡~þùçL>bSý}û&„*g4 Y´’wÍ5×ðõ]½GðÇðÅ_Pj)O¡’G”N(?<ñ&œ”<® ÖîØ#ÜXéÏçâüÒ¦˜7oýŠÎ§MÝÞõ b®¶'-Å"²X.÷šdU·Ü©qÄxW­ê4ÖZº»Ñƒê¦MgfÖR‘;l9î&Áô*¢ÓùÔSO¡€A#Ÿxâ‰Q»0¿H긹iÓ|—Lb”®à÷(¿‚C†4“ç‹$Ç¢–LåNÁˆjÃÜKòø¨ÅK/½T¶AK–,)Ûo{´RþÀ‹©©[W×pWcÓqà•@'Yƒó+G 4*,y!züœ¶o?7-½ìRN83fç³'k׎rM,'9¨°Ej½;(å< Þiâl-#i´ôˆÙ³g‡æ9ñïõ×§†£v$—Ét>.ˆ³üI&:O?ýô”)çTìúë¯'V­ù]~÷¶ #KxÁ¶¶¶6¾óEY¥Ñg—üä$iÙ¡ù¾¾àã>åe[ÖÜm¡”SPBýJ'•³С¬SjÏ×»~ &.4µ‚ èï,W›%›Æ)-M,$.ºÕ”r’£É‰eGÈÈtšçù~áLƒÈj5F6Š9Ãý\²Þ²ÃŽI“QrÈ—;ÏŒ‡”a›§j>⢬¢w´7)‡ù,žAUÛ™¯äñŸ‚‡³/ÒAµSÒXá¿üòËßÐxœ%á+ºÑo¶>Ïy÷LËOâ ýÇ`)§Ë‰¡˜Á›U"CË‚iTÒú@û(ñ“S¥Ø›)Xy(Øý &³´3TÇ/¿ržºQr¬{[IyøãÿH%:ÉôPX\è÷m/‡—ä±ãÓ‚‡ûÎÙu×]WÌM:S^_«J,à }:5£b?†Q£FÑ“e\,˜§D•$ðäÉ“”éÐúÙXw†–ýk†ªÝ{ª$9YðÛÒrœdÚø¬;È£»îº‹›toˆ@µJ%:^xá‹P€ÄEL\ü”ﲩܣ—äUB¨ÛÂ#Tø(mŒ7Wؽ¥‡Â ŠÐù»]ÿ'” šu4*)å¼A®F¦?C‡áøk_ûZÈ #'vo)Nt;Bg~¿•î3=tÊèð·¼Æ.%y5^‹è(Žvog̘Áë¶Ð™?ØDɶ׻nP¯Ékº8x_Oš ö=¹Éè˜=*ýí@&µà¦àZ‰toKSÇBºÏ”:Fƒƒ¼)Ì+I^ê2ÅØÅÚw7ܮعYa÷vÀ€Œ†ŽPo;Q:†¬ëz‘’:¬)6†ÌoDc27W®\i#t4ÖpVìݳóК‚¯éÞ–æg³ð¬ÔUXöªQ’¼j.>ã çT7†]˜>bè0RÊ©ç †bï@Š•òÊ»·>¥œjºN´ SÞ©O6ûÅʤk•‡Þ ð/7yd³RŠ9ã3„('lZU,B×½ NÌô³.ÚsSlúUŠnh…ûÑžkîB’Wsä½òjµà 2¥œdn˜Á2{ –/ŽÖ©D׊%Àuo«QÊm®)s¯¬”‡æ¬Ô‡i#Ç:~n0”AÈ„½5¢5g Âì‘Ö€â>üÍÕ!ÓmæféMú¬{z¿áìà[̳2ɞͣ ¾¯§ìÑtå~ æÇø$!èF’—X2îišÙ 2e×ÉÅ×Jyð¿ué8¸¶îùa0ó“ú¿ôì'û…”QÊ ¾±um›kúÙ/³Ä‚¶dÀ5i(”&'QÕõ®˽8¢}Ç¿Ü ¶â©`¸Ãùä“Òv§ÄÔ‡~ø£>òE,w"P.I^¹ääOD  T*yléuYïqÁ4`òe²ˆ@¶T*ysæÌYØ{Ü|óÍÙ"§ÔŠ€4 J%¯“,“E@²K@’—ݼWÊE ƒ$yÌt%Y²K@’—ݼWÊE ƒ$yÌt%Y²K@’—ݼWÊE ƒ$yÌt%¹þXSuÁñØÁ§ä.êo_À‚2Õ‡›$χ’܈@Âz>ê÷/{_%Ðÿü§éÝkÿžÁ´ dª:Iž¥dÜ<ôPçï?þñC\¼õÖ1®“ =ÑPÈÔDÓ]»ÀXÚüê«9ÉcÙ’­\â#Å7ÝtSgç¯æÎ];§LiÿàƒÏ¸I±;¸ä&8ãEˆ {Ê£ñã§ØýSRÌT"âD©ÓcªOª%y>”’q3eʔ矞°Ø=íÔ©S\ìÝ»÷{·ŒØ¶m/'e× ÷š5›­Îçä_+¾8à>Í.Ü#î›—MuæÑ³T$hgã5lØ…G¥ dÙdø÷ê«“¢;ÿá–¥KçßÞ{°ºiëÖ­ÜüáÌñÜÄ%™ë ðhÕªU¸ooo·¦4híÚG û쳉›šlÂk»;wÒ†š1ã&Z|n•zßþ-˜±råJp‘Ñf’Ý47—^z)/¼0§kÔöˆfž{”x*Š™j¥ÊÔÈ´Kò"%ìàÀ´¡&OžLv%˜BjÐÀ1cÆ o¼ñÆáljuüøñÜ:t(×#Gޤ²½çžðhÇŽGŽAÍ2¡P [Ù\妒úkœfB_%S«‘üj‡IÃm÷îÝÖ"£ò ®R§^¤lPhf Ž´ú­sÀM®—/_~íØ¡4ñ6mÚô—ϹòP ³ÈÔÈäKò"%é€m¸iÓñ³§ CqoiÉÕÏvÐ裪§§àŠ>å˜ûÖ—AãøKAïß¿?¾Mº6¥7õ®ÐîDLEޱ“S»Näg üõë×Û}JT{\Ю§x ƒ¶‘òÚ¥ÿÄ.¡5Gñàª\ã€8¹pèíV˜ï½4ÕâJ›©‘É—äE"JÒãÓN渠­GèÖÖCãc$áC׸Ë<úƒæ†G\ÿfëó4ô~ùË¥Tìü;xpnè§JÛv%bêÝwߜÆ »ä’K,]:¼³Ü· ÚõîÚÇ¿œpûæ1®LŽ[­t 'qÈù¦† H©¥Ó.ÉK¼l” R‚`™ .¬ÐïX›]ã>zÁ#«ü©Ø¹ºtÜÍdS’ˆ©ü2Ílû‰ZºtÄ%7§/Á7œ¸O¿©•Jëvœ8q¢@³…up\z«ÔaI„g™šHzHC(GòP7ÞŽóqÉ’%œ=|ðAþå¦Í6ÒQ!×Á±p‚•|…!'î½LM<í °áÄ–>ÍŸ?Ÿ åö‰¤#,Ê" "^’‡l¹ö;›)Vì8p +þœêÑÖ ”d¶Ìˆ–<ÞϾûî»F !+ظËç8uêT×Ãe™@‹€ˆ@DK‚e]Z$ !ó4š¶ÞôéÓ]÷Öêúˆ–¼×_ÝL>|8Bæo.íA×ÐcR‹¿G¹*ˆ¼àšŠàxOkPIséºÆžåLD@ªA BòNŸ>í^Ô²Ì3®¶±—È2?ýéOÇÿ׺Ÿ·Í^د_¿ñ­#xÙXw;1S¿þõ¯W©Ì|åË/¿\ºt)Ûu|ËžnÄM•Ù'±:¶øb]qÌôxÀvþÈ?ºººV¯^=qâÄ*%’`ûöí»ªctåáß¿åPËÏg…ÂéY¶kñâ6·•0—-Û5{öìÊ£«0„C½Çš5s‹…“SÙ<õ³Ï>[¾ü6gj[Ûâá˦ûè^ÛM [[}Ü&éfÙ®ž!mCBŸìÜ?mÚ´Y³Îm –d”Y «½½sÀ¼òxmnwè¹çvŒ>ÞÒ²ysnGî’ÇLã¸À™ÀÌܬIéíêzáäÉ“qqUÃý¤IƒJo«—ZS%yÕ( ¦$/vÆÕ²•Û8y(D@’§rá$(yÑolÅ]D@š†@„äÙ‡8x‰QÆŽxöÕ%†‹ ä5 J%DD ý"$ï;ßùŽKÃ'Ÿ|7=~øa\/r/" Õ#!y¼¢­d:±›Žç&èU/% YD@" Då]qÅJÜéÄÌPqsúªú-®ÈDʈ€hÉsëjÑ/›£çy¸Ýh'znFಜ‰€ˆ@y"æåY lÅ´l»æ»3—_~yddn3.™ä\z¿©&›Š G|4Ç$•Ãï~þ³åiÙ2’5£‹M*?U¦Ò/œ˜Oœà$/Éã]-ß»pû©°EJéV[PïØ?™ýDKñ擼µk»ùò¬Ï»Ún"§"§ÇÔÎùç>tÛ4’÷ßïûÙê®ÿQ팎 ÿµ?±cÇŽÒKq:;÷?²äžÈ ªí ßÔZK)d›O÷euþeã<:¼ùëÏ¿£?ËðÅs™Z“I^zVqiÁYUœ> Îh:ýã¾ó‡ªZâø ûþ@{³´ämÜøÚ3ÏÔÿ“5ù¦ÖAòLõ¶oßüÖÏE]Ä,– /¼§ÌGáýFð›gèݽ÷Þë3¯ù$/µE'ôÛ@Sbê† Û©,›o­$ÏGŽCnª*yѯ/œ5tfÙY`ôè3‹óíŽl„gÝØÐ—ÏhâØGïÊ "/" "P’Gôdç̙à „ÏÍ× FÌM;>wÛ•ò _" "àO žäY¸¼±EøØX…ý ìë?èàÃ?ÌM‰?}¹¨1r$Ï™H¿•Þ®I:¨f]3Oщ€Ä%P‘äÅLîE@D ¾$yõ坨E@jJ@’WSÜŠLD ¾$yõ坨E@jJ@’WSÜŠLD ¾$yõ坨3J ³óWo½uŒÄ¯Y³™k.>øà3»HÛÑ@¦ú “äùPJÆ Ëh.8~‚°¸à´@ÝE2q$J™šPŠk̵c‡îÝ»—Xßxã >hÉë—˜éE áDþzï¼c²h7ù׬ä&§¹1­ =J61ÅL ÚS}.É󡔌›žúýËÞW kã??ÇizǦÉ„žh( dj¢é®]`7Lø;ÄÎâã 3ÈÖž={X¹ôó_ýï-úŸÈßüÐ_>?´jÕ* 7ç>ô¿øwîÜE¸gþ?*ùøã[ݹ|ùò>øÀ=bõ§Õ¬IÅLeõÙS}’,É󡔌 4[3Ö½¿îÍ›nº‰ÂÊIÑyè¡NÎ)SÚ­CI²;¸ä&8ãµ+ö”GãÇO±ûÉXÙJR¦nÛ¶×¥Ž†©°_¦œ:uŠLgˬïÝ2â_ÿõ_¹3lXn‡Ž»ï¾{ÆŒ›~ò“ŸP4èåWŽr“EMH›‘#Gâg<¢îd²Š>rMi™9s&µ aþeëòêh S#!Hò"%逆»ÂY‹Œ6m=:;*3~üx7ă8Òñ±Š›6jÃX2M<Ê7#;ü ’4îü°1•fÝ+½i©ž© ò¸qWîÜyF§¬™OBh¯qŸ îpýË_.å/ÿRgÐÌçäšÞ+Ö´§—ÀÿâÒ|UƒFAS]\©252ù’¼HDI: X»Í¥©¥ië:5)¾óæÍ˕ڥÿDW… ª}ZsV°(å\ãÀúA\¸Gæ=ñ#Sù)Ò;ãà‚Ÿ¥KG\Ôs7^ù-ç«ô—d➬ûô›*ÉK6Ç£C³Á;Ž‚ècÏœýû÷ÿæ1VásÐä:èÅ=r7£ãŽé"S±Ó™šø°zÌ5ªsªŸ :«)Óy¤ßTI^:KNŸ{îùAPˬےΣLM'@YUK^’Ç+pÛí=ò`n‘}¯e2—ˆ€øð’<æ=FŠ9ضmÛ#<ÂÞÈÌNàS>Ȉ€ÔŒ€—äŵ†/ñ]G>}k3ouˆ€ˆ@JÄ“<¾æÃ÷.Š3f̘0a‚û ÂÇG ÕÖKINË Ä“<<ð½‹b³Ì˜I¯–©¶Õãé-" )![ò|ìf-”ûÜ-ßóöñ"7" "PU‘<ìfÚ½kèén 2RQˆ€ø¨–äUiá§O’äFD@Ѝ–ä¹õÒB/" é!ð•/¿üréÒ¥,q/±pÏæÜa4ocy;ác=óò˜§â饫«kõêÕ'Nô ¹<7}ûö]Õ1º<¿A_÷o9ÔòóY¡pz–íZ¼¸Íõå¹qãkÏ<³®òè* ~¶|Óš5s‹…“S7lØÎ† Ë—ßæLmk[<|ÙtÝk»IáÙ¡ɸY¶«gHÛPÄ';÷O›6mÖ¬ÜV7‡ßýü¿ß÷³Õ]ÿ#™(+…Ý÷x—Xº0tvîdÉ=D’Œ×|SÛÛ;Ü8À+׿vT›{f\íŒ=ì²v¼¥eóæÍü_ÉcO·}ûöYl¼½åmFi’¼dJJ^(’¼*µ`}$g÷Ý·ñôéÓUµÄ3pjå¹!1øD© =ƒª¶³©õ”¼{ï½·DjÙ㈆Ã{ï½gnhΟ?àÀY“¼yóÖW»Lx†O•ÓÕug1ÇdVzLe^ç¢E“œ©ÍÑÊóÌ&9+M n’+cÐ;ô‘I|‘¾š¬•™^9ð! Éó¡”7 J^U^_ vŒ 2êç£wÉ3%SD bKÞe稛[aFz˜Ìr4Ä.Í»¦»l¨ x’‡º-<ÿ@Ý­s+ÌxKë^\Ô%=ŠTD@Jˆ'yâíïdê!y+V¬tH!$ÏRT=ÞØVõ{K)ä(“D@‚@b’gªÇ@Ÿ%›Ï­³CrC ‘" Ù!ðTd¶ŒçÃ]†ÏsR.5I%;Î?¥;Iåšk®™4éÜw¸ý“,—Å,[¶+¡Kaǵ_}\zÁ Ü9sæDf¤$/Q4¨äñ}âž]=̯j'yÈâ6¯(j/y˜Åë ·£££c̘1¥mmÉ#_|±W–È‘·ß~{ôª·©Zcëc°ÜT‘@]$O^¸Tûto]òrùÇÂe hísþâðbÁKòßÐÁÕEò Æ[Þ`ºÈšAòº”4¸ñ’¼ÏÀDÍ’¼$ߨ Ÿ:uª[•öñZ#Ñd)0(‡@µ$ùÉÓ§OwmÙ²¥ëäGD@%à%y×_½}ȱôÎQ!Ãxká>ÿø£ý(Q³˜ˆ€”CÀKòh²¹9ÆŠ$øùÇXåXD@ªAÀKòª±ÂÚäÕž¹b¨I^ÝÐ+bÚäÕž¹b¨I^ÝÐ+bÚäÕž¹b¨I^ÝÐ+bÚðÚ/¯Úfimµ 7wøu\cûÁ!~[4w¤(u===C† } œM?Ž·´lÞ¼C%yñrëþ-‡ZfµÄó#×U&ÀþtmHO͵‡Ís´N•ó¶œà) ¡² É+‡£ùéÜòÓO?-ß¿|V‡@[›Z[Õ!Û¡Jòš"•?AÉÓë ?fr%"Ð$yM‘J„ˆ€Iž'¹h ’¼¦ÈF%BDÀ€$Ï“\‰€4I^Sd£!"àG@’çÇI®D@š‚€$¯)²Q‰ð# Éóã$W" MA@’×Ù¨Dˆ€øäùq’+¦ ÉkŠlT"D@üHòü8É•ˆ@Sä5E6*" ~$y~œäJD )Hòš"•?’øàƒê…¯E@ÒLà§?ýéÔ©SÏH^š •m" " å%SA‰€¤ÀÿaÁ‰‚½xigIEND®B`‚opensips-2.2.2/modules/seas/doc/images/image029.gif000066400000000000000000000225251300170765700220120ustar00rootroot00000000000000GIF89aúw1!þSoftware: Microsoft Office!ù,ù‡3f™Ìÿ3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿÿ3ÿfÿ™ÿÌÿÿ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌffÿf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌffÌ™fÌÌfÌÿfÿfÿ3fÿffÿ™fÿÌfÿÿ™™3™f™™™Ì™ÿ™3™33™3f™3™™3Ì™3ÿ™f™f3™ff™f™™fÌ™fÿ™™™™3™™f™™™™™Ì™™ÿ™Ì™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿÌÌ3ÌfÌ™ÌÌÌÿÌ3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3ÌffÌf™ÌfÌÌfÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÿÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿ± Øj Á‚ &T¸aB‡bƒ8"Ç-NÌȱaGŒC‚QdI’?ž\™ÒdK”YÆ|)s£Ë™8mÂÔYS£O•4ƒæüy“§P£C&-*±©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý»xóêÝËW/Ñ{*EJ8ðQÃKF\x±ã¢Œ#?V y²àʘ)k¾¼ù°eÏ™9÷Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸áJ ºsbѽÿN|xb‡È_&¿¸\góçÅó6.Ýwuáףﶾ;ãÜàñ¶ÿºB¾¼ùóèÓ«_Ïþ¼ ððãËŸO·  AóoÔߪÿþ‰þñ÷Ÿ€øŸ WÐ§à‚ 6ØÕ}¤çà„«u§ÝgÔY˜!†Ùm8]‡Ø\ñJ îD M&!ˆ²Èa‹0¾(ã‡1~Há`ABþaƒ¢@#ˆ AÈñ˜bC%â¨ä’LÚ†_Sø½‡" âGåx‚°€M–äi)¢ˆ­ ¦ˆZFéT˜M¦©æš¨é˜Ð}"†ØŠ–>†¢ ÑTù„ý'aWÆyÅŠH²i¨\5r7£¢4.ꣽ Ó—iIg‚˜R9¨¤hj*âˆsRº%‚>%ya£¨2ªê£©²ºê©&ÿj¨› }êãœY&~ƒBxŸŸsúy߯ƒîš"¡²&«ì²d=É‘}[*„&œ6ù÷iŸ`Þùž~M¡Éì·à†k­{!+î¹T%ê꺰²ëá«ïbçHÍqÝq%ªÛî¾ñòëb«þêKºL’ë—©'¬°¬ÎBÙ#VAfåíÂWܤÁ±ÀÆ7%©AYre®Å$ ü/¼'»›rÀ^ô1EW\:"y{Ž×k fùdÍóÒ<©¥ÜïÐ+mòÑ-MÉôaì#Â,&•Tryç•^RÀ}T‡‰ó±3-öØá5œ• eY©˜!&€}1£ÀÓm_±uÕ?vÛ1Ù|÷ÿ-›Ó‚Ìýt+‚ãÝö­j“‡+y>"ˆâ°‚w¶ßß"-´Ò–£œ¹Êó–d7 —ú–˜&úœ jé{qŠ^&'­¸9Ë—ÏntÒ¶ƒH9|NñÃùå·mŸûq»%O¼ûòÌ÷ev¹{7/ýôtõn—òÔËšûö¸w_»÷£ö‡_Ÿ}ŽO~˜ç‹Ÿ>úé›O¾ûþ ©9ø*sÿýýóÛ˜ýk\¶çÿÿ\ÏþHÀ´ØçH׋^ÈÀ­X¯.Øk …ì—¿ Ö~´ÛIç2è*Ùaðvø»`9HÂIp5¬ÏäNÈÂJäy~Q  gØÂÎ%‚4dEhÁbîƒ:yÿ#¶úЈ ìaU–C¾ØQ+l¢ üLlŠX,àã‚Ã,â‰?áÄ Žñ]c•¸Fº‘ŒLñb^¶¨›(ÊñŽ}ÓS”~5¬]íñ~ d9,BN2Ä£"ÅæÇB:ò‘,$Ç"YÈÔQ²\¤mÀÈÉ!zR^ÁìêƒÌb“ø+¾±ŽÁ“ã›ÕPë ²‡Lä"ùÈHN2û–l¾&—ïÉê‹2“¡”;'t]Mú6tLS¥™Ï eºAŸfͦ×iC]ºA£žÐ©kjš"$U±x;b ÿ䊌V"ÆrmUã͸£)5›V½ T;ˆØ:5öh jS¨Õöš½Sg«úǯih}^-9[sÛ#Ðäõ¾J-nº©ÜgFØ£O#m5Q{ÌÖ>6¶]m)›BÌfP½ç³ovÇ»IíNÓ»åsï ,aþ2ÐÞå¨$jÞéúwºJ:–|o%w_ øv)î@[—#bÚ-qENò‘›œ³4ÿ+OÉW~ò–WÉŸ±BøŸgNóšÛüæ8ϹÄË’óžûüç9‡¸Äà ô¢ýæt‘æÑ—ÎôLöû4;YÔ×2u±X<éB‡ni>®»å^KÖ­‚¾·<ý!c+^®~•´¿Éã«zFÜN̤KeÞt­ÓáΚZŽP>y-×v‰LŽ‚°;’ºe¬Â/DˆÚ4Ì"üI¬"ùZ¦Ÿ*Læ(*ŽìPRn­Ò>ž¯ÁDÛ›— —,÷̧5‘üñ–?Ë.¾©ï¸Âsïj×~÷¼NäGaŠÆÏ°4ü] i*gw8Y…÷¤Ù;+¡?{>¨_+»;<ú[¢Ó¯„…maVú¾6¼ÿÓƒ·^äk0oh€ÐfþhùZ®Ö òÚú¹›ÒïÄ U°UIÇùÑKzB)ûäö÷´f&ªƒPU3(PÐ8bâ'C)Ä"'ž—wª§@›r+ h3~Ò+†Ó+¥#{µ6˜“f,$G8µ)R³[$ø%9³}% hM¡ò:šr:±9¬:{5oó9O£Oh7ý3GX!ò#ÕD:8ø„©×»f+¦S›’[ˆ43B’°$ƒw’‚è–8|¦Ã5âÄ'Èõ{\“„€ÃwmwxUB•BŸ[Þ’[À„ P†^hxÖ÷xC’Y£b'xLùÒ ’ˆÿEJPÁ5|Vr[ãLÅ8q‰o£6Ö´52:C¨:–h3#%p–W[c7O3c';Ò2Ê'I¸yWHH ‚>Š£:É…€/åIF‰‡y@“ù&Rr\‰C(Uò'šå2óWA˜u¶¢+vH*{4a";ŠÓŠ4¨v¶6i¼¨'v‹¼r‡4SH¡h,aH|ƒò6!å¡OÔZ‚³+‚C3«ÓxÕ”€Œƒ7Qc‰›•+¤x‰ƒ"v3IG9¢@¼B°Ä›2,°$'‰C‹YÈŽ1†]ÑiÆr†RH˜$|8&‹C-fzk=h‹¤C*c‚†S£-eÿ8ˆq¬`ˆ¢…¼h'¤Â9$½B€{§PSB&\ó{°U,éOQ9^â6¥èŠo#•ø±Šù42Š¡R%«¨)XXG×#Ã3|Ä G¹+Æ'É´¦{R-{²3)Ñ+¹‡{ÿ‰já4t-6á}Û2^¿r@Ì—UÑt‚XAlp-µh~Ûb …I{›výÁì537?â+ å™ù‘3öQ•Èè–’ò€’%{'ÒÀ™Ix%uS|_'‡—[Ávr‘|µu:ÉÄÆ›£áwÞxfvxË4Œ™QcÖáN&#yàÜiÔS›W^>tˆ,1Lº7qú“*Óù—¶i@¸ÿ©ºÂy&ãéIëùÁž¨ÑžèI˜‰(ŸUQž}å{ÿ…‚ŒéRÑE-×7 õŸV±j’ ¯gƒ = ‰Š3> ¡„ŸT¡Ÿ!Ø ¡NÁzUqžÝJ8:6¨N±ä1¿ùxeŠì§Á©—ü£|4£‡ÄGûD£‚„£†”£1Ê£»²|²ˆœñ6<""ò˜%]€m3{ ÉœÁXRð_h#M¨%S&¦—' ò,hSvrM¡):€‚68‘3E4DÅks¦£¨®h3hC&’¢„PØ¥1ñŽQh]2¢õ¨úƒª„…Ê™€Š¨ÿ¨¨‚ÿš¨J¨qã8¡³¢þ·Š£¸¡É:Ÿw¡¹R©¸ƒ„s˜ ¨u·épФPŠ3–¡â€:ø%àaúåLT£6¢Ã+\Rª É©oø)—È ö9µ ŽªZ-eb¤‘•nSo*gv¬QªÎô{6ƒ­ ùiz º)¤6y5¥S’}ƒ:vª_Øzß —™¦©£©_ˆ~ÐJ|:sâ„@˜IpaÇO„e¥™}¾š©Dú¦Yh]Ô  z+ÍJ•SÓOr“u Ú‘Rñ5H¸‹rc®þ„Ùg¦´š¢àŠ3!{ ¯¿j•ÁâŠS%Ýê—4ŸP×-Åù¤!A8ÒÿŠ+Ø(„Ä÷#s“%Ö4BÚSÊ œâ„Mª„RÒ9ŒáœI!g¦À’5eú¬j#ðåzÔÉ) G®R2"ÿˆ:R µ>2:âj_ä3Çòmšñiqvšrb—°O±° šª÷t[}Ç¡š¨ïi¬fé\³u÷©[×®!ýêž'Xv&é¯&ª¸×Ó¸Z1±··Ma·øe¸Êç›s¡¹ô¹w‚‹Bƒ‰—”º†¤º¬[I¶UÓº²;»©ÛxµÖŒ´›»ºË+kŠJ鸻À+»qòÚɶÞÑ»gÑ?‰¶¼é±vÌû¼æÑ¢hfÐû¼œK†Õ ½úv¥ëº¼ÿG–û¡ž[ãË盼“˯” ®å;G×û¶ïë©Ã ²y¸òAœ÷cœ²&ˆÿ›+¯«Añœ{“JnKc\ÀîK"í«BöU˜[¡á;L +¢;/ºOÜ£ üÁ"Â$<Â&\Â2º·ñKuÜé{ŒûÀ.ëD&šr,wÃ.—Ã8¼Ã:,ƒ2<[ŒÛÂ4Ü’ —ø‹ú@zºAûkÀ8À¥”µ¥<4 ŒÁáH«Ä§Q¼W0Èk,uB¼c¼{â{Äw‘Äð¡ÂE,¢e¬/g¬SsÜ,ëwܹAŒ¸¤QÇ+ì¢jllü›?ƒÿœMœLó‹L{9uw ¦y<i;z2ûÅh¤µàùÇvñÈêKÄoL¬º¿º¡)<ÜÊ=ìÊkçʲüÊ:¼½daŠ´<˺L\—| ¹üË»¼[uh«?ÌE½|wüÄeUöÁ>[,˼eÏ\ÌÉ£ÌÐ\Í¥w%ÑLÍOÖÚÜÍì“:ÞlÍ8yCÙ\ÎW¶1á¬ePFÇõ œÊD`‘‹ ºn„Ë…ð DÛÕŠíJÏdç}ÿI¼°f¼ÚƦ¡K“ÌçvÊ»éÔç €×ÐÙ½àDylb0 í½‚'ÍÁ¶xí{Ì1¤ÑÛ•:è!½K¢¼ÑKÒò!ÒoÒr,¡çs£::Ó5ÿZÓaB™6º£:MÓ>* œ\üQˆ}ém_¬ú4ÔNìItÉµÌ™ÔøS 2B@̹œ%\ ÌÀL‹ìoßâÑ¢vÑeEpÝÖ©±È "ʳqЭ¡ÖÅ,3âò½KâÕ¢–,r]šØ‡wT±æTNMÔÁxDQõÓ™S[ ØaY¸Å;¦Iu=$mËËÂÒr¤m¤FÙÉ[Dhý\˜mEdÍáná!þàâîŠÒó)Á»â,N»îÛ┄¨Ù{h£ $3μĪ3¾77Þã„Ò£MØ4Õ;fu²Á ¿Ñ}aK®o¢gcm"q¿­î ‹MoMNÜ´Qå_$å}IÞ¡,•åLæÆmvdÞÜp7å©Qå\nJiŽÉdþæ¦\ߦ{-EŽnðeå#4—”EÞâ­N /»æÚ$ÿñ$U–Ýz}sß3çvntþÜ_Ž.a.v“N¾¹éz<=.¡ÎæTçûkç($D^E|Ωý=¯~ç¬-äÇ5èˆ^rrç³} âæŒê/k1•N÷VäÀÍ™ž#ÀÎ’ô+bžik¾ìbáæÏ^¦g<ìn¡í¦«ë<‘çž±ç·Þ^~ÞC€ÎÀ‚^k5Qèîrè´¾ ìíŒ.t¤în|“íÕ®žÓcìL“ìÇ?f~íxœïˆü迎åžÛÞ~$Z ünëÿ¤nÝë§]ÚˆnÅ ÿÒymÊq\=‡½þÀ£.í \1þÎÇ^Nðò‹ì,EòÜÛìJ¬òÁÿí!Ou_ï~ÌÈ2߸mÝ."îïÞAGŽAçnÚ7<{ÄÐ~Daì^Þ@rÔê3>BÂŽQß0¤wi’Y?*[/ž¼ÞBÉ|#í4K}ä;goždî–Êè&Ždö4Xö³µõoöߘ› Cï—~.'Ÿ×pqæÅóSp.ßw"&ðØior×÷U‘êuBÞ¯îVîHCñè!9•9_ñ4–12–UçÄÏ Üø…ï¾gøó‘ c­{OŒ/À¨øª/Ç‚.Å^ú…Z°iÍVC£^ÅŠujV›:wš¢Ãõ¤Q±e‰.íjÕ,TAWܾ½Ò®[¹së½û6ï\»|ù² ëwoÜ·^ËÞœXðb¾Šÿ2îëÖðÚ™­XQ.H3T—~7Ö{°ãÈpYÆ 95h¾]#dQU¶ÿ Vµiß®=[¶mݼqó®›¶ïÝÂsãnœ÷VŠ%!Ž4‰°mpäÇW'ž»rîÛ½Wmþ¹sŽì½è·ëÓ³,£=üùëÕÓ¿oŸþ{þõÙçÿ¾†^#¯£ñLš.;ë~ë®·üB#,®:&ûh3 bŽ2—6 C¤r*P<‰¤Á†–ò|.¢Ë{±Ä×DÌŠ6o\­™d¢G¤:,kl4ó!a"+—z,ò*&£"ÒÄ&“IlN²¨•4²*¶C‡"ˆÊF»DQ%F;Q=éT)ÇŒŽÅQO…IÑ e¨Ò”OÚ|•µ1XXÏ<«P«hÄ6¥+Ï ×áHY]ËÆ†á½ÖF"¯Ø´•Ø8öÓT|™ísH‘5ä_7­wÜeǵM-²„¢M]•ù[C÷Œ Ú’«åyÈ=9ÌcÀX>z£Ó Mù4ƒ-ZSöÿŒæX(ÞTç¡=¬-ô°©ºÏ¦=f/!BkËY==S¦­PÛs)X7-ôè¶ëÖH´¶;.«áð^Œ1Û>ë ´£PÒTN5û´”?èÝ©oœØŠµ >= K/³I·–{éªÖ›V4±Ö¨P©ùììÛ¶hÛÓkŽS×{õœþ›ÖP®Uª*P´7òXÓ­{–—c}£>ƒxÛoç4o×…Ýè×M¥W~k—b£Öf‡#ÒÕáª)˜®Žåþ¹Ú´É…VZÉ:Øßo…z{·˜Þ颣R×ðsÌ–Û†$@ˆi `ÌÉP€?CÁ­q¬ƒØÕTW·ØP!u#Ü&E5ÿ°Ëc³TK\Ô?}ÕÆSo9Ÿ×±Ñåët Þ´lS/\ ‹qTÛ¼zDz«ÁŽ!>S!u$*õ\Ëw}*áäD¢@lp– aXAm+ƒ9ÙŠÇ=¦õP† 3 s—gÉ)I ©âµÊ׳Òì]/ÍF$ã89zð‹X”Kåw@ü,7ÕßFu½æè¤TÀ½»íÄt ñÝè´Å©_=Î6O„aʤè9=M«m“,šXÆÀÅ "RªÞ¥/fpi M ¥EI9r¯…Ö XÉF¿_i*6c›—ãè­AÁÒWFⱌ-©QK`:ë]ê&U:¶©ÿnyb úžW²”¯i`¬^-ùÔ…a ›õN’õ–ÎñcîË[üFƒ‘žñ©q-»îbãK¶oi^ë 4ýY5C­Ne¹,ºR1q”§Ù¶OW:+—HÁõCvfK!ƒ£ÞÛ 9Éı‡n{ÈÛX³—ô—JCO¹9¹±L¢¥“ ý¼õ-dp!5Ã`71)ÍòsŸÐr[›x4ªù3`…ºÞÐJW9œ" dü”àÎšå“ Áª]jÉ͵È%§¡ g ‡Ë—ÂÖ\Q8nÍWâÌžvéJZÒÕÀ6&9ºd¢À›ˆQ²$.±f쬆õè[Ñ:¤hÜˬÿhTkf2£ÖÁR 9‰×^Û¢ΆJ¯'ìlBH”ž‰®píCNû^HÇ<²vvLû$fÕÙù*P˃(H§#T² °ÀÓë‡P™Àh&в?-ë˜ ÙÅ><•Hn¸ݳ®5•æ"È<XÙÈ^öXiâ*é”!Tfš½"Ë.ÚÉмNA£U¸×t·‰½¡ "“yn¯¬/‘KýÔ_ÇÆ(60;­—lµ×dLpg:I´™r’ŠwÍûÜzÁk¢ÔI­ÞKkÁ’%u*S:Ky]Wµ:£n¬8KOÙ ëC}½˰%Çj=ja,žt„d8 —dŇ#ÿ®QØÅY¥U.¸<^–EG¬¦Œð¸cÁ½gÅv.aew,`-² ‰,£0‹ÊE–ó^«ÒuV~}–¸ôÚØÇFv¯‡h'§½Ž0Άö³¥mjOÛÚÕÆöµµ m®1Èc¦ëömroÛÜåF÷¹{ oƒû”è‰zÜÆ‚xß Þò¾·½ESïºíßþÖ7½ÿÿ-ð€ó{ÞÏw¿Û-f†/|<¶MwÄÏ=q‰WüÚ›FI˜‚M1ª)Õ§ÎTTB½q¤œ™2í"ùS>^è”er¯ûߪX¢¢Dļå+òëÊÅ7웯Èj«‘Í{^k|èÀ®V³t¾¥¥SzÈ& ™Ð‰~ô£,Ñ*æ´Ô©Î“o_Zƪ”¹‹ÉJÍÁNvY­ý멈+(B—;ü&Ü {ÞÙÞõ¶Ã=E Ùžft½¿]ì†/|ßí\é­×DÑM±“ÍêÀ7ÝЂXÁÿZwìb½ñ51 ËÉÃíŸg:WQ¦Ud·×}udoŠÁ£_ôôpìuÿÎÕSÅ ¦P§ìu/|â—¸÷;úîñS’À¥LGؖϳgÛdöªCŸê!Ôݟbjåß)ñ*þ¾Ûû~øñ£d*qAÛôÃß%D­Ÿü|·×Yâ÷$Ôõ÷{ù÷.~ŽõB›å¿lÍÀ¿t¿ï+:ë#¹­xžk 1Lq>•ë¾Ê8%íƒì9ÏC¾ œˆŽ3 îk9 Ü9œÅ» ôÀ4AÚ¼>³€À Ĭ@{»ÅkÁòŠ, ܸÅZ1<¤”wƒ¿Bå+ºÈóŒ'„Â(”Â)¤Â(Ä8¬Èµ*ÔÂ-äÂ)Ô:T3ÂŒ«Á.$Ã24C*Ì#¦ ÿÉcÃ5tC«yC˜“Ã6„Ã:œÃ8¤Ã;´Cɻ”<=Ä<D<$Ä=,D@üB=ÛEtFtÄF„ÄG”ÄH¤ÄIœÁ›€8CÔÄ@<ÄAÜDOäÄO¤™>ij0¼ÄÔë=\>ébÅVtÅW„ÅX”ÅY¤ÅYLÄTÅ–[6ò@¥ýË¿$üEäAvû4_l?_ôAÄ+·!ì±0¬gt¸0{80CƼƽcAhL³[l+Þ9Å)a  Ã”sAhÌAéXs‹\$9<1tÜ´F¢ …ù”ã› =™ ¶Q3w Bžˆ˜qVYÂãÛFSäœ îƒÇ¡ÃŒ)Aœ@ÇSÿ D ˆ¼Ho4H`ÔÁ`ôÈdÔ?ë6–ûHèXlTÆ” ÉÈŽ2*<¼ËF•F<,ç°™#Féš$B”DÁƒ4Â4³Èä[=aG›Çw<1>>***000(((888$$$:::""",,,&&&444'''222---+++XXXQQQ@@@NNNHHHZZZPPPBBBEEE___ccc```tttkkkpppxxxhhh}}}rrrwwwŸŸŸ”””………‰‰‰›››ƒƒƒ‡‡‡———•••¯¯¯¿¿¿±±±£££§§§¹¹¹­­­···ßßßÙÙÙÌÌÌÏÏÏÇÇÇ×××ÑÑÑïïïóóóæææççç÷÷÷èèèÿÿÿÿ€‚„†…ˆ‡Š‰Œ‹Ž’‘”“–•˜—š™œ›ž Ÿ¢¡¤£¦¥¨§ª©¬«®­°¯£‚¶·¸¶¹¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×Ô‚WÝÞßÝàãäåæçèéêëìíîïðñòóôõö÷øùúûüýþû#qC'îŸÁƒ*\Ȱ¡Ã‡#JT7b[º‚3jÜȱ£Ç Cz ` ·ã0Š\ɲ¥Ë—0cÊŒW±$8)gêÜɳ§ÏŸ@Ý‘4Ù '8•A“*]Ê´©S~5‰^1ú éÓ«X³jݺsèÍœ\ÊK¶,¨_š]˶­Û·Ýÿ¼„K·®Ý»=ÑÎÅË·¯ß¿åž³ $Žˆ+^̸±ãÇŒ#”¹²åË‹q‚¹³gÆ¢ÀÔ;Ø^ŽÏ¨S+v±ÕE„µÆn˜ƒõØ/4FP2ZàÉ%E«Úà rH‰Ö¯3ÎÆº|am²Ä3îm±› Ý&¯Ý£qä°Ë6Wøüvné¼_ &¡cÇíÞÆ«ëÞñ»V×áÉÊGX^,ýˆÓ©WÝTW´0|á wžwÇÝ—ÜDû1¡Aý…õDº´ÞRH€àšs¡Föe…ŸrâiT!W#:”aK¤Eć!–ÓâD%bu"„)f´âV72ô"K‚™S#9AF”ãÿU;Jt$POîó£VI*4äJ¤©`q šø “=N4eVU&t¥HE–å7e:´äSMF´fOsâ3&VmtfHYª¹%;íí°ƒåH1„=o:g:FíÀD9î½SçNã-AÂ$/nèö‡¶Wjÿ;¥'uúBpÇ”¾<‚7…5êÞ®Îälý<ï—C„²¶"Ú¥Ž_’ì"Ò>¦äoÖêFáA?ޤ‰W ‰þǤBd}/Á < ¸”ò£éaU ÂÉ‚Ñ`KTè*Ńû¡Fx?yÀÐ å Êù•ô.LÊ ó!ÃŒˆ°†ñ¢?r”&ä‡!¢:‚%Þ£ˆL :¦¸í\O‚aâOœˆ)~ÄŒ¾ƒ‚E‰ÄÈO˜ûˆ}Bƃ ±#w4bE{´1"ëÑZäöE.EúQÑïX´F!I F:˜ ¨EHü5RI&T ’Çt’ÿ{üIëñÇÀP-g—+¤3¬M6䓈Ó"tI+=’Hâb«¼ˆ¿”à—À ¦0‡IÌ`R ÅL¦0-ÀÊŒ]@™ÐŒ¦4ƒ KMóšØæfI%_fó›Ó4À-±tJ-Õ# CH§:×ÉÎvº“<€ÞIOvŽÓ)T¨g;{}ªÓ>ð';›P–&T&8ÂAOpÐuR,èl(PpP~64NˆÉS Dà…iG PuXÀ!eG ™tpF)]‡ ðÐt¤€¤O¡!G=R…$1u)bŒ ÔÀ4¨çHApŠ ¥H5 ¤c¥,ÿÍiåà’$œà ¥Ä‹SŸšŽ±B5ªãÀjVÏÁÄ­àHhŠ¥.µ¤MÑ"¡VIàg …ë7ÚêV¶*æ­‚½B s€¦&欉Ukfpɶ”)oäv^’žŠÆ @Ml\J€Ä@v2(AP«Š¶ %Ѐ [Ø–`(AEûƒl`w5Go9€€ŒÀ)ddUMtá¸Ý«]¨ÐØu!¢‡ .›Žô.»W¨BjšŽ'\à*¤9 6ËY€µ€ïvUzÚÄz–4x»ñ\vT¡xÍë)B7^r¤ À0/ÚÄR!VGÿhö‹×®#”|±Àn×á]­R/gè_sÏáv>!»uìF +ØG8J x+ìŽQ¬œ\ݯHæÈ_èŠvÅïÐpv]ìŽ&Ô7)5ðGÈk_ lØ¿ã­WáqÝìŽà½îè°€?lÎ<¼WŽ†á¢ ˆB,áÁcêp9ljqv@Yy¤Ø.U°À‰ã¡ 09¨S–·lpq Z‰œkìΣ ˜Á™cZ Ïc`qHa;éx¸àþ i¢·S,®8HÁ‹ãñƒ ä Ó‰B \Ù*¤€¡1`$\à÷ìÊ))¶5%wäÔQ¥…íÿAà@Ȩ_ž`iß# #u‚M—¨QxA,€ƒ!¬Z&£†^©-c¤ ÁúåGh€\ /ȹáBX€Ûó BR@™ÐàIxr[ŒSç}4!µðå €0„Pi؃l/HÒw§@áýx‚^à‚àøÞ7Y¨ð‚ ààÜû^p§Z ¿ çŠq´l% á4iJ€p^l@¤6ö±ëçàã¡Âȳýݬ<á8À Œž&¸À®”*ÁàyCÐIó„M8¡Ž2„‘Ÿ n7×…” ´æI>ñÿ ÌÀÚy "p²ïÄ IxA .`€äÀâyî{ô7Ë}îþh‚^€”ßIp|D”à‚ìý%U0‚äšv”æI‚ï’'Tx¤ÀÀ ^ „=ƒ„ 3À€ñw$‚þâ¶À (þzáU‰N`€?ç+‰BëqÏ \àÛ­¸L@Ëônõó!Q=V¢€où‹„ zžÉ,„Ÿ4z&U@4°|:!kÙ—Eå4:à|ÁqdAœçÔF~P}01_11zA˜÷YÒ0>gí6ùtQôÄH ‚ï´k†å‚íT*@ƒíDÿ€Q%€xÿP8ØNA¸N¨möEÂ;£3bèà€0áÏNÆÔT(Lp€ïðT¥^ÜtWLàcøKp‚ò _؆Ž~ÞðÞt†p†¿ôQçTzn¸‡ˆÑLí@E`-ìÕW¦¦†1qgÿ vñ„ýP>NØ PøˆèŠ86¦J󉡉8`‚2<%Xˆ<1‰ýP‰ïÀˆüàˆÁ‰ì@Šü`ŠlRK ¡D¬¸µHÕa,ÝÐ7J·Š†…¼§°H>~HwŘhq‹éàŠû0ŒÝp‰–TʈÓxr!”Ô?È¿ø̨Îx1Ljÿ„ÇøˆWPæðùŽÐhC²ø„ݸ‰ñø‡ïÇ€¡iÀ¸áH:㘪˜ŒóØŠÁ˜ìøŽA‹i‹ IÍ·nॎø°é€Š1W޾hy¹y‘õd¡¸_i‰©ÿ˜èX#Y© öP$³€½(Z-I%y'™)9+I99/™1©’ ™¤ x37©pØC);i=‰?)A9SE9G ”I‰‚Á^ÚhŽå°•±C5S`*Ó ®².úÃaZø+Å—K°R —ßà3pi|e• x©—|ù àŠäp˜ãÀÿ˜Þ –ß ,‹“-v‰‰hàà˜}IIàð•®â3|YuÎsíAÒ•°çõÂGÿ×ÙÄKÐi[ ~Ý@ÝÔü9½ƒÝjx¤“fƒ Ê ÞHÉãíì¼ÂÌyFÙ±´LÍÚó SbÝÉâÎ.i|­é=IPËœ4Ú–5Q pážáî Ðáþá â">â$^âÎ`â*¾â,Þâ.Þâ!ð/>ÿã4^ã6~ã8þá°9Þã>þã@ÎâäD^äFNâ¥Ö©O,…NþäPåR>åT^åVþä5ÀW¾å\Þå^þå`æb>æd^æf~æhžæj¾ælÞænþæpÞå 0ßÀ½Šc}çxžXNßyÎOpÞyž®cUàßýðÛçÜç!¥{ŠNÐÖèà@æÕß]ç^âgü7åVï–ŽF)ÐÕAç‰ÎSñ,÷ù ¾ðÍ#•Ý?]é§Înöà1ÑS½m¯®"¡~Akë?]9Cà!@6l¢º.«¾ÌÍ¿ž¼&fÑ݇~JÒ;‡sÿ뢽Ï’>èù0í¢líeñ_ N¹„h ´ì¤œël!UÍæ>¢‡îeñ½>Åö1©ŒëàÞì)uïlïAAŸ¾£ºôwélâÔ:†Ì€!ÒÕãíßàwvx…`†UPr_ò&ÿMýŽ’'¿ò,¯Lþ©Õò2?ó©.ßF#˜®F1\]„>?-øóEè6íJpBŸôJ„Dÿ U°î×ãK?õTïOHõôU¿õ\¯N3°H)/åTpTÁó!ÏGEßá>ôáÆmí-qßù€ð´ñ;xèïÚï ˜Ìs/¼ïöé²ÿ÷1øqc¡èxÏ+rPaëhoŠÏìoñ¯í—H!üìøo±"'Ýnê•_ÿ˜…/ÿ‘†Î«ïHAme4ú‹$sҔʖ¿öìÐö®_03@êû0û+4@îîðøÎ±H1Ò¨ïûªüë ü1ñF@‰oý?aNfG¸·Ý×§4Ÿ¯Øÿ!ÓHâý>aOÉl±"Ïž^£c銕ö¢$ÿ€p%8Hx¥Q˜¨¸ÈØèøIøòRXå$™YH©Ùéù JèT9zªºÊšã’8ÄZ81(±£˜ÚX ÉÄ$)åH,¸Ä¸ÿ,¼h Ê)ëx¼JŒ,蜈­-  ùýX9Õ(匽8Ť^Èûîߨlyß|eì^xÏÜï+yŠF°…KW"zŠ|A‚Q„ ‰+)Tl3LtUÄ‘œ¢!Ý4‰#8hš'CˆLü¸Hd"‚:êÈX¨¢ÆE0MVBÉè$#ˆ)>‚ÑhÇN£/ ѱ£ž„¨6bT¤§L` ¹é)# ›öœ8e2«ÑfD ¡ \»9GBQªìIJðÊ’_N¾Ò™ ÿ‘-ˆL~ 83“"“sŒTB0¨~I,á¸ï§Òo™'‚¨—¯_Â;¹r80pŠÆ+(qµøÁ-–|Dv*pñ1“Bù$¬o¯Ðw ußàÙµ«×Æž[k܆XWÁH ^_äÇ„¶-¡Ãm^¶YyÚFÂu¸õ–Ar]A×BvÉçÈ^1„‰ELÔ‘N(ê MCt„Ø1…ÙŒ¹(eT‹Wt¤‹6Vwi?ÕwZ'EÐf+ vE(¶Ó‘gíèœR5f´ÛYNV”Z^ÇV8G B‰9ˉ)B´ `å#S‚h©”vê„Ù ;AE“q­11‚ÿFIÂP¦du_!KL6„FÊì`%Es^á¤v‡™çžwNA©¥  “é9¥d“-µá+…$!O\·ÌUä r—#Ý4‘K`øãdèì ƒ vÔdG!ÛTD0 9‘mRúXX$l‚“ä0}¾dØêEYŸ¤H!U\ëÞEJ¨:”ùÈIB¤ÐÜ™ìZ”¯Ò #çu- "SÃ.e–Q >¶ÏÀ3KÑHÅ2„g‡¢’H®(Ý*H~I®d;ˆ]·ß¾'&²ž¢¥ “ŽkrQÎ^ê{˜²ê‡‚dËŠ‡³‚¨êÎp¬q2é]á_0耄†µWvÿÂF›Ó‚`Ö‚·DLð€™z„K¬ƒaa;HÛh\ Ï‘ð' ³9fvÙc×^Ù^æÖ§ÓX3&UŸeó‡iÚ.§/4ä H%ýW±èaw5M[zcçåSDHðòÞ}þ•»Ww®—Z#šÈO¼ÅqÇ/6ÝÀ“Ó†ÔaåaõÔœË!è}îË£H£G7×ÁÍNÛW-¢óN”k-?G¨”жŽX_&ôvl¢Úž°-‹ÛÝG¢2ã“ü þù|¿q", TìîßH§D/ıô3FMMî[ÄóÁ Uæ+`1ºwä }LFíc‰ÔÇÿ~ç; ±?W 3¨Þ‡Ƚ ªpúJWxÁ“X  „¡ é1êÁΆ<DôF0Áwøììa [hD†âˆ‚k¸DÒc§à`å½(Á„\D¯x>*ªâ…`Š8’€/(–qƒ…8åaÅ5¶B„Wx‚…ÀBH¡_”£‘H2úâ¨Bê#ÆAV±[d£"ë=*DV9™Pþ—p¼à)y™3:…’ƒÐWô´½X¾˜>9ß-™i-]¢¤–Ðÿ¬KYÍD¨r’yÄ2*“Ì^,3›õ’fÛîHÎgü¤ £9Ó9Aä ˆŽ„ç'8T‚CNÒzƒXMžú„ÁXº€-¨AŠÐ„*t¡ m¨C Z‚w2°­¨E/ŠÑŒFT8H#"#šÑŠt¤ME«î€¤,m©K_W ÁÃì&€‚±µp2B/í©OJÃîUá?µè bPT‘Òð¤A­O’ZQ@5£8਻‡ƒ©:TªZµè/, „aòS–‰B°iÏ´z"u_GÕºˆ Hr‰L…+)NpÅ™f…•#„eV» ¶'¸@£¦ö Bÿß:ØAÌÀú4"X÷:Ö«Dv³«ˆ‚ð:#Ê@œ½Â pUÖ•³=y¨×½Ú…¥­m'r€•.±sÝìrÛ[æÀ£‘uBnñjÄ„³'ô¢mŸ+‰äÖ°F¬ld›[öõк‘¥An)[Ýú°R³Ð-/#<›[Кw…§Ím;׫¤÷µ‘%&yá‹_ܦ·­øÆoÓÛÈþžoé r9;^ØHt°°y¥›^ê®P Ü]l Ý*v³ÞM/xWH…à2÷zÂé2êàò:a,`AŠ`X…ŒS­iÂŒ0„ЪÐï-m†€ ¤¨Â$ ´§“è\ïÚqúGòcýµ98Æk£›À ¬¢½-K_f{ÿRÀ«Ÿ€] ‰GJN ¶=(ž9ØÔoB÷ií=ïöÿ\DÆ'½€•ÏBÞûúH~·À«ï ä››ù¡À¾ÏÖGÓ3øÃûòš_ó_#úï _àËBâð~¿&ÄoWõWetØ–ë÷zxG}þ÷ñGjä§NÚwMÜÇy?³€È ømWéÄß·^ú×3QW+ÌÆ€ ¨  ² ±zRe´‚¹ä rvÑ@1<Ââ3@è{¨ ¼a,ïðbHc$Ñ=&¸ öåƒî·‚‘à9A Yó#‚0ÕrÄÀ \¸Ø@,4ƒ Õ»çÿ@›ÄHsÕâ…aBpøüpzA<˜6Y¸zq,acÜ ž0‚  ; &b†ƒ Ø€ Õ²æÀè°ˆ€ˆˆ±À`!’ˆ ×°rXÙ „Œà„ª{As…8mUȈ¡w5.!LóW0`*³±Sž'7Xó9 ‚'F<’†6XA›P°A,ˆ±4—ˆ¶Á”R1.Ñ¼ÑØ“„Pav½2Ä£9å1ˆ ŠÅ÷>„ŒãBE ýÁáO±ÎØã%»ñ7éX0ð(Q~¡5‘ŽšPЧ…¨xgS( ( ]²ÈÁ’:‚ÿ.A)€, ÈÑ1‚Q8ƒ8Åøl'^¢Ž ã‘‘LÀ5£×6YRò’ÆB|øC‚M§ çQ€b1*ù4abíá"‡Ñ93¢ ¼10 &™é#  °—‚µ‡ŒÀŠ q…`ó‚B"E–%©9RzÆB‹Xs(U2‰-"æĸ†ÏÖ†hó–'Ù—3‰Ÿ6Iƒ“}%`iàòÙMAˆÛ— ˆˆ"Žñ–³—ØÑ—Ì"òbòÙ4z‚,z‚=ä⤩9õ˜•ÁÔ}…丕\©’à‚È)×!4‚)Rzš3ÿÔx3Æ2ƒ±p8Q’b¢†$J:(‡Ã„R1¿!ÑI N¹…Är':Ѓ Q›¹IC@–Ø)K"®˜ ãx`„Í© ¸ Òyœ=¨R1Q¢4ó · Î)<’=‚@¸)|Q—xV¯ ]¹ ¦™Ltƒ0ž‘š+„ž*x>¨s K’ `• À;© ‰À é‚(EØ! (…#ª%JN'Ú_ª•Ðõ¡Ÿ¢†è¢$›µ%£øE£4 F7ê ã&¢;º3=ZZ? _Aª¢#ö|Hú¢JÊYLº^Nª~H6[dÇ`é)¥0šMVj^Xš_º™ÿ#qbBŠg` eÚL3ê˜eª¥ÒsØñsRÊ£×§™0€`D¦¸š?fj¦dæ¦j×§’ð§W¨%h¦'xBtfgª§ƒ@P›Ê©ê©Ÿ ª¡*ª£Jª¥jª¡æ>/§êª¯ «±*«4ª©²j«·Š«¯z‘ú„ˆ6Šf©—*?–"Åj¬ÇŠ¬Éª¬ËʬÍê¬Ïº¬O÷Q­Õj­×Š­Ù:v>O ­ß ®á ­ÇÆMj "jª®ë*¥³6µvkì*¯óŠãÖ îp¨ôª¯ûjO9Ú¢ü °›mFª£k°ûdþêš˰ ]û¯+±+X 륋± M»°ë±ëI¬ K²%»F{±&«²++vÐ µR]%³3K³5k³7‹³9«³;˳=;U"pµ ´0´EK´Gk´I‹´K«´MË´Oë´Q µS+µUKµWkµY‹µ[«µ]˵_ëµa ¶c+¶eK¶gk¶i‹¶k«¶m˶g ;opensips-2.2.2/modules/seas/doc/images/image032.png000066400000000000000000000322441300170765700220220ustar00rootroot00000000000000‰PNG  IHDRüe‘ïÖ6PLTEÿÿÿ€@@€€Î”dbKGDˆH cmPPJCmp0712Hs¼1;IDATx^ízä*®„gæìû?ó¦m°ÁSB†Nå»wçLËEéGÈt'óç/¿Œøcýe¤‹ajü¡5VX[iÏjžß‡›åÒÚJëxfýš@tØ,•ÖVþù÷ÏL•°ÎØ/vÙÚJÂï “uƼõNÿnåùËkÛ ¿w²‘,xkø’øOVæ¯a¦~o0°£áON{öfç§Ï ì}+?`±ñÂof¨µ•l{ÌRS d1o½Ç·¶’ð{'Û:cÞz'Žom%á÷N¶uƼõNßÚJÂïlëŒyë8¾µ•„ß;ÙÖóÖ;q|k+ ¿w²­3æ­wâøÖV~ïd[gÌ[ïÄñ¯V?³?׳Ÿô?~~ïd~3‡s+7´ï|·ˆOÔ~³Ô𜬕'åáãûÛÛ¸Ûn?п½ËÉöp¾XÿØŒyßí«ãg•?e?B¾ÿ Køé–û7¯Ÿq üÞ¸°í1s¸ N|¬íû¸­†L á7K Ûž¡V•ÿøþ‹Ý2øã7¿w’r‡‡Þí«oV¨üGuKàÚõ„ŸjÜ®¼= ³ò{ã¶ÇÌáûiÏYõ›ðÎE ¿YjØö ¶2â{Ž_ãpízöcdå÷ÎÏ=>+¿™çÖV²ò›¥†•5+ ÿjóÖ;q|Vþ‰“S”f±Õæo¨×ÚJV~Ãä~_3 ¿¯¿öÑ­3f¯p™ˆÖV²ò{§Þ:cÞz'Žom%á÷N¶uƼõNßÚJÂïlëŒyë8¾µ•„ß;ÙÖóÖ;q|뉔ð{'›ð›9lm%á7K ßá]ÍJ¿ZƼõNŸ•âäðM.ßä~_í£[gÌ^á2­­dÛãzëŒyë8¾µ•„ß;ÙÖóÖ;q|k+ ¿w²­3æ­wâøÖV~ïd[gÌ[ïÄñ­­$üÞɶΘ·Þ‰ã[[Iø½“m1o½Ç·¶’ð{'Û:cÞz'Žom%á÷N¶uƼõNßÚJÂïlëŒyë8¾µ•„ß;ÙÖóÖ;q|k+ ¿w²­3æ­wâøÖV~ïd[gÌ[ïÄñ­­$üÞɶΘ·Þ‰ã[[Iø½“m1o½Ç·¶’ð{'Û:cÞz'Žom%á÷N¶uƼõNßÚJÂïlëŒyë8¾µ•„ß;ÙÖóÖ;q|þê’‰“S”FøÍ2fm%+¿Yj*¬3æ­wâøÖV~ïd[gÌ[ïÄñ­­$üÞɶΘ·Þ‰ã[[Iø½“m1o½Ç·¶’ð{'Û:cÞz'Žom%á÷N¶uƼõNßÚJÂïlëŒyë8¾µ•„ß;ÙÖóÖ;q|k+ ¿w²­3æ­wâøÖV~ïd[gÌ[ïÄñ­­$üÞɶΘ·Þ‰ã[[Iø½“m1o½Ç·¶’ð{'Û:cÞz'Žom%á÷N¶uƼõNßÚJÂïlëŒyë8¾µ•„ß;ÙÖóÖ;q|k+ ¿w²­3æ­wâøÖV~ïd[gÌ[ïÄñ­­$üÞɶΘ·Þ‰ã[[Iø½“m1o½Ç·¶’ð{'Û:cÞz'Žom%á÷N¶uƼõNßÚJÂïlëŒyë8¾µ•„ß;ÙÖóÖ;q|k+ ¿w²­3æ­wâøÖV~ïd[gÌ[ïÄñ­­$üÞɶΘ·Þ‰ã[[Iø½“m1o½Ç·¶’ð{'Û:cÞz'Žom%á÷N¶uƼõNßÚJÂïlëŒyë8¾µ•„ß;ÙÖóÖ;q|ÿœâ¿Ÿ¯‰ XNá·KÙÿÚ>Øoà~»|ý%üvfºÀ`¿ë$üvù"ü†^^ÁLº”V¿R~ýÞæ}Ã|~K3ïôoÅúCv‹ÚŸ…ŽþÖâ·ÂXÎæûc±í1ÌqŽæùtÚ}Äm\ðoÇ1œÎׇ"üv)Î@Í 9Âì1æ²\¢ÚÉe$ÂoÁ@x*=¿ôð"øãõœÿ¹o&H<‹y}y ¯KpÆbdòÚãc¬^š¦T×Aûy.]â>Wþnï%8öí—#IŒýË1æå¢ÿÛ7¹ t§ð÷YWm=~àŒE:ià›$lßÌ'pi±‚ý½dåY× -)Äçó«àÏ=•y.Íq(áMƒèJ+søo´çÙeÄ‹*Wµ‚A"Á ÿ‚a„¿äžó•øÌÛÂõz÷ðØÐ|SìIuÞ¶ _8‚ð71’ò+xéµIÐq‘ö8“›@ÓkžöT,R±À­<¡>'å¼H‹ÿ¶¹ <ØÍÊ3GËKr(ß·it=+77°v!üu#’r“:ùx^‰ð•èY={¢{½1„?eÑ–û˜y‹^%¥È>ß>ü%üVØïkȾü9„,~|Ú^ùô3üfåþ(û.éöXQ¦»Ë¬Gýð›cïUöw¼ðÿíKà×ÁoÙåx6çyåóè}rõ¿ñ#r¿~—rïÛñœ|zÿóaý—= ÿ ø]±÷îK¼Î‘jMµ»Y#ºyì_ÿ˜Lz7%!•#ªÿ¯Ú¾~¯æþVT29h•Sæ!V©­G}#ücÊý åØ÷Üç÷õAßÿPì÷SHëjÔŠ7pŸÉ¤Œ·¶å„þõ¯ÿ•ú‡¯·„²W\ÖS^‰ðð¿V“^Aßù=/´× GÄIÆ, ÿ»Yx±¿µìnè„ò/«ü¯ï¿/ó7 u¯çA³Ö«üï–ûWÎxJ ~yñ}ÅÃðJðOý;g<³ã¿|-· ,ÿT¾NStgé}nš^dàµÓÃ?]=™ý Î}_lbø§Ã~žŽç„n¦¥x] s&0Q9%üóº6!kJZåix6ø§jîo»úŒMö ±èh˧Ìë<ðÏ[îc®§­±Ó »¬’ÙR<ü³yR.lS6µ¸i—ÀËðO¹៳ãIž|;z‘7/™!ó¯Á¿F¹Ÿ¾ãYãÜgÒ#Ñà_ ûý½Ë7k$|ïEdÞÞÚŒàÿcýg¼ µd#+ ZGi)óª7ʘQ˜Ã;ëxŤô¯›â•~š­#[Ç{6ÒúnvñŒ"…!üC–•u¶¿iýóïŸi¼!•ßM³5¬#Ü=ŸW½‘2£0gåwi…¤\—êŠî®à³‘¯÷0çw.¯ýAn9¢6­£y¥Ø; ‰@ò& »á;ðתÕÛšWt«üï²åµ‰±6ýùû©ò?']á~þõ»Ï±WüÛöêñ—üïÀÿ‘6£æÝ}‚Ÿ]áß&þoû#ÿ[úÒey½ÿŒšËðϨn{N>“{‰ WøÃ´¶É%k`«÷û7—¦FÍ•Ê--ÁÍ9ÜmVþ Ô‚?´;ö½ùÙáß_šþy4·àŸG)^ùÏ"óžúAðï¦äÀYùï–½×öd•Í-øgtWTù_òyü¡Ö‡NõÚ°NXù³V-ëJÃ"¦€:wEð¿¤~ü8˜íÏŸ3•ý½ï™°òO¥ùþ©”ŠÛžWÕ»Á_²ÿÞ;m®¯˜d·w¥’tˆáîSåŸÅg#_ MÏ:š×QŠU~ÂÿèÀˆÚ´Rë(%üº…½Ÿ ¹µ+lÇW êˆF¾…aÛS,+º»B‘1òÕ( á'ü;•¿iQ{À: v¥ìù“¯øáàýv«»¾‘ŠïAÚo?Íe¥ÅÏ…„$²_z¥Þöìoùœoô´ËT>ÂÎg£¢R `^žºÝm᚟撻znßz ñþðÉv)÷Ƈ!®ðGã?ïèÆdm˜O¾Q©j/Ÿö$…?*?ˆP7ìøwveÛŸçÏK¿_ûØ ÿÁ¾¢óy-YùOøãψ$ä)+ö‰ÓÀÓ~á¡X …ÿ,èg- އŸË¹ûë§éùƒàô§†s(Ò™…µSï\ùc% ¥)þ‘ϱà˜ÝûÚžø‹–"?ÉV è§¹Ô¶Çï]¬Ü‘9Á  òªHßrUŸÃ"œºoüß»Ì £3ü»ðÂzŽuñcmï¿Éu´=m’ŽÜÌìÍ¢ì"ûCÞB¬·=Ù¶Æv§ÿV8ÏÎÁgOøÓr™@”Ö¦Zåðé!)‰˜üGgý°“˜U¤[­+t.[âd’Ž'òr Ñ&ÄÎgPY‹(Lù-Ébh» vUþÖtk†Ühá.Ô^¦š¿þÉÒ³Rë(UT~˜»¥kä«Q˜¡ó:š×QJøá5üÐ’¼{ÚÓ5»Š„œötI ù)%üš¼ LÏ:õt¥„Ÿð8= Zǰ¯Šxá9Úí[FEÅ( {þò9 6Ðä~ó²a¤Ì( á'üÍõd·t¨Ÿ‚4ûsÀÆl¦5rÓ¼ŽÒbÏo-ßÌg+ø›ëU6Ànu?œ(É$5Gûi6JÒÐ}•=˜Ú?VH :ŸÀ±c訅Z›ÖѼŽRuv×û¤*›õul{îØÕN$Õó.]#eFaXùyÚÓ\OvK׈Z£0„Ÿð~»ÕͶ‡mOõX¥¹Ð ¬üž?z¹¢»+œªùj†mÛžf±µë Œ¨5 Cø ?á·[ÝìùÙó³ç¿9°ÎnµŽR¾ÉÕܸÚXùSæ†Ù F¾…aÏÏž¿Yí £µFa?á'üv«›¼|àå/x‡î«|“«¹…UפÙC +?+?+?+?+ÿ…£'U£0Cӳ޿u”òœ¿»ÙIúD¶=‰‹„Ÿçü‹jÍ·ŽÿÜðOûë)x—Ñl-tÄAr²‹[Ë7ë ŒŠŠQöü|“«¹¿Û-]#jÂ~ÂOøíV7ÏùyÎÏs~žóÝWùos ã;¼ˆE+6•„Él¹Ë5{¢gÛö‡mÛ¶=ŒvT£0Cӳ޿u”–jì¼ê”…!ü<êl¶Êv'FÔ…!ü„ŸðÛ­n>ðò—¼|ຯò¨³¹…ñœ±hŦ’ð#™å9Ó%ŸZd×ùjfèÆ¼Žæu”ò¨³YÉÚìV7xùÀË^>ðÝWÙó·K|e+ÖåvûX¾p„»„¿;i#Ò³N'½ŽRöüÝÈ'Õ‚ŸêL\$ü<í1XTk6„Ÿð~#F4•ìù»“5"=ëÔÓu”²çïFž=åtÆÀQŸÆ6ïÒ5RffèIô:š×QÊÊ”ƒÆ¶=k>š~ÂoàᯙhWvT£0l{Š_Ñ]žötW@»Õ]—°Rë(eÛÓ\úEƒ‘ ôÉ îÓ#½J*I:ÒCøwð ?„‹Ù ‘ç!?pŽP•„Ÿð£¬XŽû}ðïÏl}Ï^PÀ²"lIKK$C4ØOsY*I:›ÅÐÊOø±¤´G‰` nßÛb„T’t<¦‘ð‡šÏÊc3J ³t<¦r$ü±ß™«ï‰ÔCô‹cè%’!Ü£F~T’t<¨ˆð‹Œ SÐ1L$C4¸CLÇ%RIÒñ $Â/2V4LAÇ0‘ Ñà1—H%Iǃ’Âv;3õ=g·ô=¢Á`:†‰dˆwˆé¸D*I:•Dø§? vœféxTá'ü(+fã¤0KÇ£BÇÁŸö:óô=i¹o–~Ñ`4òq"¢Ár-=WH%IÇÚÂÿ¿ä Öç=ð¿ô«u3ÑàV°þ×E2Dƒû5I®”J’އµŒƒ—4OÍO-jÖüîÁp"ÄWÔ¬±Q4_ÌMÂÿñId¬h0–…žQ"¢Á=jä×H%IÇŠ?á0q"…Y:Lø ?€‰Ã)ÌÒñ€äÑð’^"2V4Øo2"¢Á~šyÏ?ÆXé]DlˆK•àãE2Dƒq š‘RIÒñ€6V~¶=&C¤0KÇ’ ?á0q"…Y:<~žóI†ˆ` †n¯$•$$ü¬ü&C¤0KÇ’ ?á0q"…Y:Lø ?€‰Ã)ÌÒñ€dÂOøL†Ha–Ž$~Â`â0D ³t< ™ð~‡!R˜¥ãÉ„Ÿð˜8 ‘Â,H&ü„ÀÄaˆféx@2á'ü&C¤0KÇ’ ?á0q"…Y:<~@Ò CDÆŠûMF$C4ØOsY*I:˜ágå0q"…Y:Lø ?€‰Ã)ÌÒñ€dÂOøL†Ha–Ž$†ßø#ͬ¿ˬ—Œö»‡_ä“)ó{ü¼k~ì«Ã¬oÉÓkcŠðK¤c×Vÿ3Á;üÀ¬%CòX縥ĺò·î'|}m|ÖVOø»a^Xþçß¿ðŠuU8#'ƒè³U[ZýþcC²`ãV:g¯üKã³öÒ%ü­Åsyý¾v«[ÿýiªp¯'|¬"?U~«{øùò¸oéäC•_w‹|kùâÊßópƒÕN]d¬íÑÝã)­ºÈ˜úË£+J™¨íÑÏ•%¬ÈÕáîmÏÏ„þüýTùíàl?=û|ãüÛöj|麓<´=V‘•aõäòÑʯ¸Å—WþøíÐlÿ¿íüoéKBø-"·à·¸G±í1ñ¥Õö(nÃoáÐVÿ¬J:gLå¿ÂN4¬ˆc]á‹é\V‘[ð[Ü£ ¿Eä&üý7áï¿Å/¨ü§9¡Ý ˆžÛºžÂ#pã7î$Çåá±­ÚÈhEÆá_M}ºÙvXÔ¿CoT~pƒ@‡.‚ŸUëÚ–‡Ã¿+™èdQ…GØÄŸ?›pÌ$Ì¢ &ZöQý“ã§êÏ=â$ ¤c{µt›˜8Á#g)üûÕ@øßøy– øhmü^øû½$Ö‡XšÂR  ‚ðE‡åµ§÷>"EþðiŽÜ AX þ¢´ìçNÂG«ŽI¤>î”?Tþû$>n„›†]þ s~¬‹Â¯~‡w¯œˆ–ãû‰?GÑN¶åvÏ¿Ñ#ÌQªo—¶௉Ž)Û“{ŸÖ±…n«øshq«KÖþÙœ!aðWÅ'tž©ú ÿ9Ѭòœžì—ÖÕYúî«ktÏŸ€fñŸWùûöx)íi‰>*>‹µŽ:“<ÛPa[ð?‰ÞçsKlÒwí“Îî‘ùR-Û€¿,~ÇõXx{‹Y óøæÍÏ}H.“è ÿvÛcA|£þhÞEO þBÎÖZ%¬þ˜¤ðoËaÃ7[Â5õ÷òžˆn‡Õˆ,íÈaé\Wo\Îg~n-®-<üwÃþΖ¿rX3q-ÀÙí.ɹ®òú{¤5ÑGÅOž[rR“[V+ÿ}×(xóö þgñû"½Ò™KÚ¶„ÛÖ•Ã_™„4üï?> ÆÄ{BåLw’Ç_Z•>MˆÂbµs“þÿ¨ì¡Ëé™G„ꀧöÀ{\|n’°(ü¹øœûÐöl‘ø¬ëy|à½L¢'üÛð{Ÿó_Ûá3M³í Û³0ìsÛsÛ$äѯ]C)ÂQ_;ZOü³=»˯(¢ý¯*<áL=á/ÛCø;*Æí’Á•_*„_VVù;¢#•¿;ì õ¥Ûôîµ¹~õ9¿&ÁŸk ¿¶ò«2Ðn{TáYÛ#õŠð~)3øx¶=‰W~›º_äS¾õ=ØöàËhiV~V~!‚‚á¬ü¬üu\Øó –+fÖÚûÛ ù·¬¿ÎwxÝ"']³Û=¬ÏùÍoòç^ù­ï‘ýH‘uÓÜ‚—mÛž_Ûö´‡ðuëµ»vã°¶úïo{„p·†þÔ!ÂßäåÕ¶§¥Nø:á'üdÞýxƒD)0–ð~“cÈ»ð󗼿ö—ð~Â/Ù¬ܲ SØýªäœŸêsø¯„?Dý"þj† ?á—8À^‰[|à}pkí“òµÕÿ¤…•_´’%ƒ±kã³¶zšmôÂñ­ákã³¶zÂߢóòºßc©_d>ðòWˆyy¸¢~‘ ?á'üüQœ+_þÀk‚k«ÿ~ø[ô _gÛöG‚Ì»mD)0–ð~“cápkíÆamõl{$Ëøã–p|køÚø¬­þûáç9²þ¸t/ňŸçoUgžöÔbåo±ónÏÏÊÏÊ_'”•¿µzYùYù%Œ”ÏÃ>ßµn;[ªXùYùYù[«{Ýzí®Ý5¯­ž§=óç›Âñ­ákã³¶zÂߢóz6&ß¾6>k«'ü-: ÿƒC„¿…:¿ÂOø[~±CÅ_¼"ŽÒ¼àËÏù›ó— àiOùÛÏ—ó~Ö÷øþž_Ævs´uÖnÖVOø›¸ç?+¿™wx%J±„Ÿð˜CÞ…ŸoHrÅ¥{=¹¾ý®N ÙÀXÂÏÓ“K{y¥òÓ ‹£4/øòÓV~Vþú üÍú@|*ñ´§ÅÛ¶=-Fn¯óM.±e… ¬Ûë½`íÞamõüÇ)Ä+ìŸñ×)À8ð¿ÒÔüîáÙÏ¡Í7“çáðÈþˆ¯qåÿ¹—Ÿ9~‘ýðÙï„_\ûá ìWÞ_‡þÍbr‡ÕõÿL¥ ëW~}MÕ›ô;/ЭÈoë.«ë'ü­ ‡×ÿg‘CÈbá÷btuýûÝæŒ®ü §.Ã6Õô8„$ü²ä+R@øeV_F+œÜ×ï.~‘Óé¹ÞEüÁÿÙ»wÈ2ª!+»ÆÂFÐÜ’¡úCF{Í!ü‚ |Jø÷\]Ò ï"ELnŒÑØT írhå\]ÿb•ÿ=ø÷;›Þß!d…}cáI×ã9ïøÍ?ëRð{*¿Ê¦zË¿2<– ]¼G1ë,i„_ÑP­Ïêú ?o,E¢t‡ÕÂé±Á¬®?ébû²úk*¿C¦B~°–eO-½•ðËܾ?Éõ:Þ×o‰ùEæ’n ¿þs_ìÛ!J²/ýÂ/UsUýÙÙ]WV ?Zoãü°,N{DW×OøQt2írèþ²º~ŸîŠ];äý>!Ù·.ý«ëÏØï3gtÛÂj=ìé—Mp‡%øí…Ç–ß-rÖµyÞE›‚_¿ ïŒò]þïÊ'g#p€ð ÌâÐïr`4üFO›}Ip¸¹CÈÆcoßÔkW­®ÿòØ+3‡ðËüºŽ^žÕõ~”_‡L;„dåGó¹ëO+¿ÌiVþ¿úùîÖœðö> éw^r_¿»øENççz—þà„_á}l¿ó’ûúÝÅ/2á¿exŒÙ°nî’=¿¤.°çÝr Õ!$áÓ†õ§€mÌi>ðöøÕÏ'p·þà„°—¼:“§‘À ?`’¦;¬†ïwR¬ÞÙ[wY]ÿÏüú§0ºò·’Á×éÀ0ÿ0«y£Ù ü³e„z†9@ø‡YÍÍæÀhøûŸN œs¸¹CHžóËRÝŸÂ/sšçü=~õó Ü­?8áìå9¿Î$Åi$pc˜¤9æ9?dpmP?ŸÀmûƒ³òö²òëLúÊÊÿgÀj»—”Æý;ÊŽ—Ò{\À;±þqê÷;µç žÂ^¿½êâö¬>#à[À±ûÆQ­°ηBÊ>ŒFn$Ö5›”ñ?˜ V®;œo…•µ‡!7ëG‚¶¥Á#€Û‰§°üÿþaF.aòQ÷ïpÞIé}v-íŸ+Äú‡©ßçÌA<®„–ùÎç¥ðƒ‡íiŒÏ%ŤžX~¾U|–)>Ü ÜUôkÙæp`øFëþÝðw¸¶[}ËDõ;-í øåê7á»|ä)6ΘCG BtUÚ.Žó)O©?ºæ€É‡ýQ¥å&3~¾?:¡°L{\Û¬®*=8jïWï?î|¥Àõ8áEi/M[mß×Åþ×ôW¯ãöÑÑ$°òæý[Hûý†--§À]íQb]áïsí¼j÷+37ÝT´§ðËÕïûÅ]tÎL’ ¨çïc÷ì:¯ðïÈmš#ñ‘ãm²ç2åë°bÛwSγ÷1¢ÞȬX˱Lcuu¬žZ×N‰™YùDÞIÒ¯Qdw¯pXÒÙDûñüKæpôT=å‡þ/.G¡BĽ ®üoI ˆE\ÿ^÷boëL¶žŽo& åÓýÌÃüìéDæZèðÎÍõ41ÛcÃ8Ô; õ9ü ÕÚãáÒJ&^ë™2 îðŸåüRWsÖÓ åmG|xéÓ’At.ŸöÎÓñ´¥S÷ØØQÐÅåx,@vM±~ú üÙ6°p&ܽÄSÈ[åÞp>-çèuú÷üIU¾ïÛâ¶gï”.òà—¿e 1¹f?“@Ûžçµ®¥Â3ÝÉÃÜ2Šõw«ßl >y¦,nøÇkï^â)Â'ëeÆpä(â´ýy u.’¼ÙOJ/R½öLîëð?i©î»­ûw8ß­´L}Ñwgø{r^‡¿ð¸kÒöt¤ à¯?êŒ}ö¹ÿÆ$¥ö>nui›ÆÝZpÛ n¨ú±¾ìe°° ܵD,MáQ ¹‡óY×,q-Vömfá„áøóúp×ëïW­üg‚’N!™º€ÅS0¬ü½=SþÜq,õK¸Våµ™H}­ûw8¯ª7·[Ú?±ÄúÍÕ_|†ÀÄS°aÆÊˆãñöžZ`òiå—°mÝ¿Ãy+ךú[Ú Ó‡Òˆ$ðìù5“)_Ûº?á·÷<Øò¿góš«ò?­¯öÓ>+Å?œ ÚžçÅÌ¡£þ„{ªŠ·êb¬b“'ü„ƒé:JůêbL/áÇ|*B¼—Í9gÛw&¶=Ýôþ†uª…¬ºË)’À7x±Yd£¸:A´pH&1L½ãâ|UsQ]Œ9Nø1ŸzÛqô9¶=â)ölo¢ú~¡ÕËKz<^Jïqíµ‡÷ÍÇM.~¸ÿçHÕBV]Œ©…'菉?6Ï('¥wÚªgÛƒ|°)¸„Ó'ÙH|Z2Ÿ–öbýÖ. ¿x Fm£¾±Hß|àíp~>ˆwbýÃÔ~Vþ¾š±× ç'ü½¨HYùË D¼#ü„¿ê@ 1<ìùE´µüïylaϦ e>áìÖòŸð; ¶Ì'üTƒ—µü'ü„¿ˆ:ÁExÆ^9<ìùE¸ ¸có Tüª.Æ<&„±xÒQ­ûw8?À5øŒ\¾x‡©‡çБ­±‰ð[¬ˆ=èš7Ïù±Yd£†áÓZ¸Ú6mpåž™i„褾?ž'¥…G2à^\ö¸‚—jrðÿ ¯Ê„êbÌvxòNZàûcÓq<—"üx ,²p]+ßI ៾ç3\ bFu1¦†ÏIKëþG NJû*¿Xÿ0õpÏ/žáÇÖ^ó»Ãùaø´îDZþaê >›U\_ -€Äð ©øU]Œ‰~lvõFr?Ø;QPÉ`ýX—9DY*~UcÆÀ“wÒß›Îg”“Ò»íÕïóq™áÇhu0ŸðcÖ~>'¤àûã uRÊʧ ŒTeBu1&†ÏIKëþâ£ÂCKû'býN>×hæ žÂq/Õ\T~ÌþQ8„¿Û^Â/‡‡•_„°€Yù½ÎPZæw8? dà'%býÃÔÃsOmXÿÅ(Âoñ´ŒÑׂÏf×µ´îßQv†áÓÒÎ^ŒÁò¨iD¸k&Bøa÷œ|æiOÁÂcyˆx'Þ¹?ÛžHš·=ê¾H£p¨²êb¬¢! dÛSéJÛ?%?ªót6@þ;êEñ`0ùp&†-&é¨Öý;œàZ°„ð?§[• Õņ-øÞ>íÁf‘à¿Xÿ0õŽsˆ“VÍEu1f;áÇ|*‚½“Üb@Î…mD}>V5ÕŘf8NZàûcÓùŒrRÚ÷À‹Ë¶è“Åwãçù!Ëœ"ü÷|®åØÁÿóVª¹¨.†ÆW¾“––ù|àÅòØ;ªåÿOÜŽXìbNÀuõ|NZZæw8老¯íë¦~àOápJ5ÕÅX-hÁg3‘º–Öý;œà ޼lSÏ¡#¬üÐê#üÓ÷ü„ßë …ðþr•°¶àcÛÓß²}®—Í9>ó‰§`ÃÌ#?Ô!Þ‰ÉsŸI€Ï§lC’ÒH ›RžöˆË„ŠÕŘÔ|6[Xë@ø±<öŽòß‘“ÓžÿR\ù´ð§È¹u²¹¶ÿ—àÿ±åŸÿ\4œ¤<Þ¿Ïy'¥÷°MïzôS¿ß¨5‡ž)ì1µË#Z“?^wÒß褴~\ó9r˜z þž)ØÀßçy¯üo^iÊVÕý™Ô+Úµ•QN¾¢W2àí„ÿ–½ÿÞ)CjŠVÕ½þ7è'ü:øûŸ¶Ô°_t4‰þ.íÁ€þ)~Âo¾ å ¿Ü3+>û/¾÷—kí2ÝñîsèïÓÎÊoÍÐ>á7·µyÄ€{~ Õ¿~ÙöÜ[gÂ?ýÝoÂ?ÚõûýöÀ™è/;ÆSꞪíéÔζǘ¡€=á·öõ)áéöý„‰`å7È›Ðsöüž—BÄ’–þYà—êž©íéÕζÇxHAøõ zÎʯ÷¼Aš¯O„Ôs¯÷ü©ëœ÷øF=»4´_ÞÀqø{ÚyΟ¥õ½DèèZUw~¬×,©)KÏðÔ=Ý0˜ððhô(÷çj{¦º?XÅÀí[Gì‡^óþ »qõ;fð÷ûl.›ýÐÖ²ò·zþ³ÕƒÇµSƒ?FÓcS«üÍÛdo.¬`³%¼Rh&üâ"Ù¼`KÊ‘õ½0íåÎq°ááA">O\.âQ烋²Þï8¾Çò2üq'‚—šU~P…Ägšª¬¢wÑc3^óë•¿ÃçÚ%¬üf•_ž¿{&Ûaå>þϼ“Bø –c.!üˆÏú%ìí3+?’Çëi@Ç5’KôØ°ç—ø}ËžŸ=¿†ŸOÇ>ö¨S©6½œð~%N„_iàœ—ïZwüR×ÌBÛã(·Z=‹ñ’ÃùyþúÂó~Ó×Löüš²É¶‡m†öüJ÷¦½œ•I~ÿòö™GHyÔÙã’¾ç︫Å%l{Øö(9båW8çåÞÛ±>ðjÈaågå×ðÃ^¥{Ó^ÎʤF¿yûÌ^$|àíq‰¼®M‰wEÒ×LöüˆØó³ç×ðÞ_éÞ´—³ò#©Ñï_Þ>³çGòÈž¿Ç%öü®M‰wEÒ×LöüˆØó³ç×ðÞ_éÞ´—³ò#©Ñï_Þ>³çGòÈž¿Ç%öü®M‰wEÒ×LöüˆØó³ç×ðÞ_éÞ´—³ò#©Ñï_Þ>³çGòÈž¿Ç%öü®M‰wEÒ×LöüˆØó?õüÞ¿SC]3 ð{k¾ÇWÏb¼dþê’fÑ`åoZÄ^Ä¢Ç~$kúæÍÛg>ð"yäoKú¶§ã®—°çç9¿’#V~¥s^î½ë±ái†V~V~ ?|àUº7íå¬üHjôû—·Ï|àEòÈÞ—øÀÛáÚô—xW$}Ídϯˆ=?{~ ?ìù•îM{9+?’ýþåí3{~$ìù{\bÏßáÚô—xW$}Ídϯˆ=?{~ ?ìù•îM{9+?’ýþåí3{~$ìù{\bÏßáÚô—xW$}Ídϯˆ=?{~ ?ìù•îM{9+?’ýþåí3{~$ìù{\bÏßáÚô—xW$}Ídϯˆ=?{~ ?ìù•îM{9+?’ýþåí3{~$ìù{\bÏßáÚô—xW$}Ídϯˆ=?{~ ?ìù•îM{9+?’ýþåí3{~$ìù{\bÏßáÚô—xW$}Ídϯˆ=?{~ ?ìù•îM{9+?’ýþåí3{~$ìù{\bÏßáÚô—¸ÿ£ jl =¿»èÛ Ô³/™ÿ8²øþ9!¤cœ%ÂKÞÇ׼߱¨ü­6Lo£mï¤ØªÝ£yk¾Ç×Ïb¼f¯Ï#|Ÿÿø™Ÿo^cIEND®B`‚opensips-2.2.2/modules/seas/doc/images/image034.png000066400000000000000000000164651300170765700220330ustar00rootroot00000000000000‰PNG  IHDR—pÁ¨XPLTEÿÿÿ€ø€€ƒß¨¼bKGDˆH cmPPJCmp0712Hs¼ÍIDATx^í‰v£0 EÛfþÿ›'àð" ¿œ9“¼Iº–d›4?¿xAò4ð#oH4ð .D €K‰VÁ˜À%¨p)Ñ*¸5.%Zc—`@¢À¥D«`Là HÔ¸”hŒ \‚‰—­‚1K0 QàR¢U0&p $j\J´ Æ.Á€D €K‰VÁ˜À%¨p)Ñ*¸5.%Zc—`@¢À¥D«`Là HÔ¸”hŒ \‚‰—­‚1Máò‡ú¥ÐnÔ*˜b¸çô—_Küün¾ñ»}bÿÛ/„ßö»îÖA ×øËÅt ƒËMûoK‹o½–˵t ƒËÍ&\š »—t?m¿2³W¬Ç­ é@—6~[Ão{/¬„^ÇýÜ\AÚ¸ôN2ŽêÎq„y#—+è@!—iP;¦]¯Í/Or™$Û~O.£ŠËh=nrÉoà¶o ¬ÇM_Dò¹lákãøz:h¸n#»¡ÁRQêNÞ‘_Ž)U£$¦F¦Ø5u'm4PYÚlª^W6¡W´Æ¹YGËÅFLC5EÁ%¸¬á$*C âø‰¨ ÙHziý<ÞDÒ‚F›¼…KòÏ)Ù[ÔÄÈTøËâÑby'ÆÔÎeùtµÍOé€z~Y©Í2u“ê8îd?î®zú÷¼ØS6i#çZ•*8íò)0#ƒK³ä2Çû»}óÏ[º1ÝAˆ)â^OÙ„ˆK75­PöÉ”äÍÆZ‚ª×—‡GĘº9õ®@c`ÓðiiuçsÚ×qéŽ]‘eë€ ˜£«aêæžË¿ü7ãQcJC›êý¥{0ߦ4!DzéNzÑ%d=ƒj±)rìfÖ yà‘Îå.œ•ÖÚÃáù".(>ÕŽE>|–Äeãi"³U~”Ë|•вn(äC2¸ ™U´½Á|·û‰„yÊ&”ù¥[Õ=ä1Ï6H'Ý?¥3 7—Œ%“Ì"KæDt/‘éi.]`¶N0O&ó3sù .#›&¹Jü$ÅÅ¥‘", œ¿p9DWå×rÙ¯’¸&¸¤Ñcs+àþ²š À¥@.ÍaÝë¡gFð¥Þ¶ô¨ç—Õ)âø\u©íùð>Ñ€&ŠUÁ%µF+Û—ãx¥íj‹=å+jÇW*.Áå?\u_Ã%y¢lÄ9$y×í¾†KõK½Þ¶ .Ço!y ¸—`wÛ%¸—·bÐB]pùôßÍ"6(>§K®ÐÚá/á/kY™Y\‚Ë™¼Õö.Áe-+3ËKp9“·Ú¾À%¸¬eef9p .gòVÛ¸—µ¬Ì,.ÁåLÞjû—ಖ•™åÀ%¸œÉ[m_¯á’üÁ(|N·–!Žr¯á’C98çÑjE«àq¼“éEÀ%¸œ]E‡à\V`2½¸—Ó¡«è\‚Ë L¦—àr:t‚K\RïÅjüœn»-EžúŒõü²2?ò9ÝoßįŠ(KmOpIbVb,ÿH5³ê.¹­÷ﺃ›ÛÜ£#lÿs“Úï?Ç/Ì .;%œ2õM-Ãå¿ßK0on×+TZÉ©¥ ×\M€KY„‚Ëwsy9ßOÆU¸ÜÂÙEH»¹-Ë—´Œ†‡ËÏq¥ áZv÷^piÃø%¶÷z”Z‚‡ËLÚkPÁå)‹úËK±ûƒÄ´ÏïgÇí³½…ÿìõíÂvûx×\¨x-â/M? ä7·+ô(´#—;›¾ä'ÿ[ñ®)~õú’û}Ëì{¶Ö½Ý¹ô@î^Óô˜¡˜Q{A¤Ò:apùf./ãÀ@üÙÀlcvî"M ßúŽ\MßʬÁ¥ó„'ñæ¶Ð]1¬ \–€_Çq›]~­¸—Á=âÍíŽi)¤ —<òÙ-¤=΃Ëxûèe\—ncÓ¤Ÿàò½\^ÆaA"l·¯Ãeì ñæ6O̚Ѫ.£C »Z_Á_þ‹_97·gÄÓ‡.ã£I·‰´—¸¤Ä«»-¡\ÞRÙ-pSEøË&uÉ¥*ÙŽ•Zì‡ü²°uÛ¢@²²b¨—d6mnHœ¿D%¸lƉ¬‚0.EQ .É0knH—¨—Í8‘UÄ¥8*Á%fÍ ‰áR •¸d:ãjFfJ…„K&É+vER)‚Ë@A…;áiµc U˜ÆxÛ¬P*Á%-^Ý­ÝÔ×òM³b©\ƒKß^ÁÅå•è‚©—}~ˆ¼Ö|.ES)‚˰0ÖÙ×Û[”xîWj^…bO}sÃÍpÓuOYr³ª‘»\æTtáTJär·CjŒ#¦ç†*Q —¹ä]4Az"ºx*ÅriÐ4ÿ¤Þ‘Ú«ÖláÍ0÷-¤º¸Ì%ßµId»x»]•2¹4<s86í•äj nDj í^Mæº'㧃ävvº)šJzú›I rÑUP)‘Ë(¿<vgÿÕry"yË © þÍÅî3.•P)’K‚CwIã —»›q1Û†ýpE‘¿´k¾(}qëŸgÆ’‚ûÍ”¤ rÒÉÔÀb¯)¿(|\û G®$Ž{§©Þ_^J~І›¬û,6}6îï%þ’‚J6GväP—Î7„,«1³RÇsÉãHѨ…c§¡ra.Ó,Ëû³{d™‹WêÇui\ì,ù§rõ#í\î_ßgDp1hÁí7”–广¤¢’KÉŸÏœ9‰7W³(Qz}·î¡£\71dMûDL\RR™r™ÌµœO¥>GØß±N™#qÆBýe2NÉ7IKeÂ¥ß%ð¢äWNÊ!"€ËQ÷ËRŸKêá&O-˜ÆM2ìRûC²ˆ3rœ ÏýÁ%µ¡HÚSÊ¥[%'sÉ~Õa÷ª°—eÙ–·î †å±ÎÚqœdÚÄ™o’» ¿7WÚ2‰6îìæŸßb€<.Kûê”ÚÊeaŸˆRjaé 8,W¢G¢Xn×?Ñã'òóKp9m÷…üÂ^Þ!8Û_­O,EîèàØH`~ .•ryr>UÊ/ËifœˆŽã<Ð3¿“~·5à?Ìq<ì_&çS&‰´w'±Ü^,l'Á_R0EÑÆ+òK†O—¬•'WËsyó¥±ÄÕµñ/ ÏNÿº\Ö»TŠáa¯þÁ„šÉAŒv.‡²¸¿4_&+áõy•¿Öèâ\î_¡-ã.c;€KT~¿”õMùå°RÁå° ‰˜Íe(Jµ³¸2)fê¦Ïv<ƒI§¢˜8žd<’_¯Ç¯A—aë€Ë]Ã_о«¾·Ïöþ³×· Ûíã]sÁïõ¹œ»Zã8·ÌàÒs¹³iàK~ò¿ïšâö5édŠÇEÝÍŽ“û<ƒa8Œë”ï¼äIO³ÔûÑÔŒÚdØFºõÇÔM‹áðÜ›â)¸´ÙÆìÜEš@¾ô=‚/ÇÁål.K1åQ”·&Šã-.§¾¬Àü\Næ2É/­o (îŽò‡Æùe=k-%Áe‹¶8ËÎ~ž(ã†À(Ž»õ¸ â>€#޳p tÝ3›K"Ý2-Hà/‰ì3Ü ¸ŒU¸&—ûÊRòk’êœLkî«cÝ3eÝÓɤ©Èœ4ƒ˜ºiQ¸”Ïe “ ˜5ãxË4™UV]~i–åà’ ¬Ç©4¹…óu¸ Zcz&@æçtÓSg6Ñ©W{KrI6±¯¶ X:lt’µG¹¬¿Õ[±¾Ð8žúKɉ?§»X~Åqë€K µºÃÈIž©›M`ŸhÚ*·Å,IYì_v«î´¢P©hŸç=ôT²üHŠa*â2YERÈž·}u½¶· .¯6M_¶ES\‚K’h[—à’–(šÖ´rÉv25cC·ÅtØ'’¿OĽǜ ¿l™B”eµúKJD¸‹þûê,2cÿ’R­LŽ ûD”Fi þRøº‡;wê/g¤ù ¢¯ã/Á%Ö=×=à\‚Ë‘”¯î¤èH À¤‘2uÓ¢ì_Â_Jô—à\‚ËOÎZûDÂ÷‰à/á/á/Y}`Kãð—ð—-¼Ì* .Áå,ÖZú—ಅ—YeÁ%¸œÅZK?à\¶ð2«,¸—³Xké\ çÏm`ÿRâþ%¸—à²%ÔÎ+Ëôø ž &1ᜀ’ •¸&ÉÁ倒ÀËk`tlU±¿½°Nÿ§’'yv¦nZ¬ .wmËd}ÞBOÙ£ãàéE\«JÅfrd¢?§+ŽÎKáû—J 4ʬR±á/G /¼>¸„¿”ˆ(¸—à’Lˆãdª”Ùü%ü¥D2µrÉt4%ùï JćmLJ¹œtÀ”.°Y ?¯'Sàòy;c¹À%¨¨p)Ñ*“.õ.†ðYTìJËJE‘—W*—äÀU6.Áe%*S‹Kp9¸ÊÎÀ%¸¬Dej1¥\2C~›M´ür*ŒQgZ¹äÒ¸äÒl[»à2Õ¸l㇫4¸¸¬çﬤ°)øJ.Ç­äT²œ›¯È¶IÈ4HÞ›&øK2es"l6éæòsTuéB¸–Ým·›Àe»1œûî®y_‘h ^ƒ .ï QS‚ÈX5]Õ”aó#þòóûÙqûloá¿_s}»°Ý>Þ5:^l:€¿ì°†©Âf“!.7úööÍýä¯ïšâí/6€Ëvcˆã r»×4CÍP̨íU¸ìv"½*¿­Çf“nQ60Û˜»HÈ÷€¾GðWÇq¶½×°°}"Ny»D¸,yÄ8”GQÞN@Äñ[OtRëžÍ¥q<`xô—»£<„wä—½X–íšßÝ}œŠ†8nŒ¸tëqÄ}u5ú]ýÄ_vnlÜõq}?éT—cÂõÔfÓÖõ¸™÷“_i§l6é^÷LVÇ6N®.µrÙ¹ß6ªÆx2°ÙD8—qÐ`Ó¸lB\nê d‚Ë̉<ÇS'Ífáþ2&“Mð—ð—Mp…Í \Â_váÃYéK&¸\ŽËýˆPú \ÊKºØl"OÔ²×…¿4zÁ9$gTnmù¥Ó¸le‡­¼;·ä{t…ÿsºdÊ—dªkû—‰þÀåNDµqÞsP$¸$"‹®¶µŸÞ}u:åv¶ÄfyK¼S ±é@—’öóØl.i¿ï¬ÓïTWC¯VÕ¬‚lsS™¿œ¥ïš~Øl YàIp‰ç6pÞ30«²ÍMÊ8ηùo[îúð*›UXÅ•%*ÖãWaÝÃ6ÇzVá/{…«­.k55­¸”·He³‰]kHÕC•k$¿7VM+de8å­µ×u’é`RCCªª\#`¥±jš¢(Ã)o“¨ïwCªª\J“±j+Ã)o»¨¯vCªª\ƒH»±jZí.Ã)o¯¨/uCªª\CG¯±jÚî(Ã)ï¨ïsCªª\Ãűj:h+Ã)/¨orCªæÛûw-Ëúc¥¬ò‰ú×9Äå×Û°í±º†Û<{iNy Ïç:¹ææAxpÙFƒ.w¡X\ç(0'ê>6ËÔM›±QšQÄ®“ pɈ€Ü¦é\'—‡ôš©¹Zyd®“ ˜ãvS7+[_¸ìƒ®Óó³ý´ý·¿{¡Íº¨NQ1pY§²·—êv—;š1—×{ Æø‚Ë{}-S¢Çu¹tûF›Qóƒó¨Ñq¿ãŒürì*ms§þr£Ì1jøsÄZp£77FøËJk-VìÚuFŸõ‰¸4™¤s’6ãsc<÷Vǃ¬[ÜS×iþ8“ÿÁ®{2.“1µ´‡¬ôð-)Œ©[7¨ø°Ê®ÓYÃ¥ç%·iñv!ßáŽüòa³+é>sÖevç—å4ù¥D 3u;™…ýK“MÆëqóK´wíEw\Š2¸ªÁ×ùótË<^Ťâß å°W…„œÁ×ùù€K96ÁHœ.¸Süå˜þ®mþ"MåÑw«žÀe«ÆPþ«Ãäö—Bˆ<“àRˆE0ŒLð—€B¢À¥D«`Là HÔ×çtq>.ÑÚzÆ©ÇV+\®dm=²‚K=¶Zi¤àr%kë‘\ê±ÕJ#—+Y[¬àR­V)¸\ÉÚzd—zlµÒHÁåJÖÖ#+¸Ôc«•F .W²¶YÁ¥[­4Rp¹’µõÈ .õØj¥‘‚Ë•¬­GVp©ÇV+\®dm=²‚K=¶Zi¤àr%kë‘\ê±ÕJ#—+Y[¬øœ®[­4RøË•¬­GVp©ÇV+\®dm=²‚K=¶Zi¤àr%kë‘\ê±ÕJ#—+Y[¬àR­V)¸\ÉÚzd—zlµÒHÁåJÖÖ#+¸Ôc«•F .W²¶YÁ¥[­4Rp¹’µõÈ .õØj¥‘‚Ë•¬­GVp©ÇV+\®dm=²‚K=¶Zi¤àr%kë‘\ê±ÕJ#—+Y[¬àR­V)¸\ÉÚzd—zlµÒHÁåJÖÖ#+¸Ôc«•F .W²¶YÁ¥[­4Rp¹’µõÈ .õØj¥‘âï²®dmU²þñ¼0¹eUzÆ`Û4Àƒå¸l3J? ÿó­+2«ÖâIEND®B`‚opensips-2.2.2/modules/seas/doc/images/image037.gif000066400000000000000000000320061300170765700220040ustar00rootroot00000000000000GIF89acâw1!þSoftware: Microsoft Office!ù,a߇3f™Ìÿ3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿÿ3ÿfÿ™ÿÌÿÿ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌffÿf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌffÌ™fÌÌfÌÿfÿfÿ3fÿffÿ™fÿÌfÿÿ™™3™f™™™Ì™ÿ™3™33™3f™3™™3Ì™3ÿ™f™f3™ff™f™™fÌ™fÿ™™™™3™™f™™™™™Ì™™ÿ™Ì™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿÌÌ3ÌfÌ™ÌÌÌÿÌ3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3ÌffÌf™ÌfÌÌfÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÿÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿH° Áƒ* ¡Ã‡#JœH±¢Å‹3Vd¡±£Ç CŠIRâ“(S& °§”Ë@.KÁ|s¦Ìš8i꼹ӦϜ<ƒþì t¨Ð¢¥DkÅtiS¦­œF…*µ*Õ«O³NÕj•+Ö­`»†ý*¶,Ù³^ÓŽU[õJ•pã* p3+˜vñÞ•¹7/_½€ÿ öK¸¯áÀ…¼81ã@X]“Ly²åʘ/kÎÌy³çΠ?‹Mz´éÒ¨O†,·µkðÖ-»6íÛ¶sãÞ­»7ïß¾axºÔjñ«Ç‰M>•ùqãË£CŸŽ\zuêʱ7·žý:Ó+¯ÃÃÿí¸|ãóŠÓ?^o^}{ö}#«NM¾ýúøïëÏÏ2xñ.N²ÑTàmu m þ´àl ¨à„Þ¤Zkeh[n¨a‡ r(â‡#zX–[¦xyð¹‡^‹0¾'ã‹36œg­à¨cf9ò¸£f=ùceA9¤eE"y¤dI2¹ä5MBùd+¬©h¥@Ü–ZvÉå—^êv¡vÏy×ݙۑ¹še²¹fšfÂé¦so¶)çÅýw¥•äÑè§‹€Æø§ &eÑp–èf‹bÖèe"ªè¤ŒRꨥb*i¥œ^Úi¦ŸnÊŠž{¦ÈRD¥z”ªFµŠêª°ºÿÊ*N–"‰¸šx«®¶öšë®»¢Xª©u Z#¡È«ìŸ‡îçlÐ>+m´ÔŽVå°f æ¶arëm·¾ ‡gäÒi.wãž«¦ºqÚén¹è¾Ën+¤b^Ÿ…«o¾ü&Kc³Õ<íÀL°ö8`„ #è0ƒCñƒS(a…Âñú«¯w ,Ço,ò‰o%|o ý.›ò¾þ® Y”0O)³4 Y³‘7+™³“;KÙsÌ?_krkÚ‚ûíÑF'ýå˜í»n¼N7=oºPO-oÕU×;t\ø¶ìµÊ_³\À‘NV6+g§­©Ùk£Ý¶Ú¡²·ÛsÃíéÝ Žºõk§Îÿêw¬¯Ê øß‚N«Ç '>rȈ/®xãÑ»7ן¶å`g~Àìyç ΙГ§T4Ò¨+­:êâ^ýôëRc ûœ²Ç>;ÕNi]úI]k޹ØÀcιè¡Oüñûé¾û\_ì|ÃG/±ô7ýô³Õêøö3îýãßsO•äËóŽòåèûž>³@Ûì>Îïë?Ïóû\ûðç/¿þôóo¿ÿT*YùÌ—º®î€Þbínç:Û9pÄ]8'å Ð ½ Þú~ÇAöím ¬[ñ&ª¼™°„(” UxB¦nz» ïZB¸ÀÙ°†8œ §½îï‡â¢ÿƒòÉpE•Û Õ§¾=ì¡!,àÈC¢ˆ+°yX4› E‰P1g3žACº#ätL£×h+ ,x¢ L‘Aâ Vˆ¢'5BpTaÁìÈŠ5Ùñ Ü£%¸H ÊÉ‚f„ Ê4ÈDJZ20zp#  p>È”f‹d+²X-D`ú¹c(¯0ÆVr’f\ØÄ,v½ê=o–ÖÃ%¸¸+ܦ‡h1…[¬8Äbb…O´‚ |(ÌSј3b$ÒÉK.Ñš3Ê$KÒs£Ÿ]‘¬ìßþÆ)ÎrþOIˆ´Â(ÏiN˜µ"Š{ ';ç‰?yÖ“Ieœ¦$רÿÆ~z ½üqÇŠ<Òj}däTL±E:ÒueÁAk×È„Ê –GÌ`+¹Q%Å 2úàÜ›°…'}a¢ö°‚+ºð¥)…)IaR»¡Ô¦*¥ FeØ·ÞЧ94\P±‚óÍ ˜½Âc!Ÿ¹+Aš$²‚CÃU’éƒIäè5;ŠÈì¤` DXÇšÚŬ¦0++ЪV´rÑ®ôŒS1™»¬õ.iµ+[õŠ×¶ö•¯0Eׂåsšhä§bý‰—<†2ЬdY €ÉZö²]¤DÊÙ°ìa³ôª,fGKZÌF¦³ ´èšvzAnõµ™Ë#d\1ˆVÿ ‚¶¶Åímk»ÛÜòV·Àý-o±`…k pÍâ¬h`¡·Ð ®o§]áúv€aI9-ÖP–Ô£¥.k)Þðæ’‹¾„ oÁÞöÚ¶½î…o|å»^ø.—)¦`)„H–ûJe¹ô}¯| _ÏwÀ$e…~ùËà¨HSŸ,Òª„±Ù—<Êd¶Õ¥mp5,\ûÖû]®eò ×{ÒÓ3ËÍŒs;œáo¸Åؽ‰OìÍßï°‘L,c›: ã…ôDdƒLdù^È-¸ÂVœªZ«íAÉV°|Åç¹ÈX‘YÀ&£–œõnù\;a®~ 2ÁðtY@Û+`Áÿ-Ðò Ù^-ßѶXÈòp¬;âBJ»5}› ¬à© Gv—Ý\ÛAÚѽ-`ïháÆmU ´7}71/¯§Bª¨CMêšÕ¨¥xb}m+HD"Ò@†õœ[ÑD 9Òš]5{R•* FÛW¤L`A&Ñ‚hAœŒëw¾úÕ‚@ô‘Wíe¦(ó× Žœ¯Š¥¬RØÌšë]R¨«h!k™ÇÖò‘×ä$_AÙŠ&÷œÃy™V” t"îÌŠ§+íHcÁØÓ¶õ‘õ˜ìX³—º—6[ ÖÉ]úà8–;Žø¡RÚ™¾2Ÿ+ëH×ú¹yž³S‘ åí<ù¡äÿ ¨#UúÞqÞŒþwŸ“ýìâÜt2—“_åTôËgòôîÈümØúI,HŒš{Ëæ›kvÝèž³ ¥>o._œº~æ ' ÖŠá„ÆÐ¿ y¼ù\tçyêÿ†zŸ+}Ýl&ß…½ÐK^ìÙ’¼çµ{yíÎ_6(õ-piàÁÓ—×aaÅp|¸`µHYð…¿¹œà“4ñ-­*UŒØj‚»è*cic0üâ|· æppán$ÐÔž:²Âü·oÔ»˜Å¥gqÂôdÎì~8zxF%Nü£EØâWN¾òƒœäè(øçÁvÓã—Oý+s9;®W(ÊÃÌí3NòóàGÿÅÓ³téVWÞè‡nÖ]èzœª0ßp{úÍO>ÓöÒ‘êºûcÊÿý7jî“j?UjA5€£-;¡jï…HQ¤k§÷€耈׃2]7"Ä6•¥l(`x`xpÕ¦ •mŒÃy9æmáWfìq+ÀãbofkˆÖ ÑrÏÕ !wƒìiN·zôÖ-•Ê”µoÏ–„ˆæ n6ƒƒÄ„OXk·gе{š‘iq‡ÂÇSÅ×…KÓI¸qq»¶\µÖ„®æfƒT†É††#·l#_Í÷.'g;Ï×@-×^­¦qB–gYæ\'d€xlˆòµsËák?GQÔ}At+ÿz†![è±tˆöo® ïöoRˆ´–ƒR¸v®—dS§[ëú%wAhò'€øoèvG\Ö®rpFkå6uèæfm7N•…§€{Sww7^Äh^·Ä }§wêµ€ZVk>r®ÇqŒÖ|YVvº8mí5d‘t¿Â + 1Ó—qÏFg7së6gsr®fe"¸xd¡xLõ8(qXëAîAz}æ„°Hkï&dºHvV†hRèjÂÅzûÓ‹õÄOr„M‡†çjÆFiWÀ„„'Hv4Š»e…I’}¿ç{A³mܦc<æ…¶aœb¸|YV}ÌWrgóR‡P3}ʇ.ÿ _×w'NtÚW.À¸5Žht+˜ŒíQ~ö—”óÇg¥˜6^‡R\´iòw~½%õ·”ø7)®wS#Ä•ÈXBC£f€X–†¯¢€ ¸–È–ë¥^±\&’}à8m!w™—î5‚kq€mÜ3¥‚DitãçQ{v•T©˜@¨½w¥G¨”‹I™ºe…œ‘_¾HF"yU$i’%)F PÑvš¦™š¨¹šªÙšNUrçrãÒ“dq暸ɚº‰›†82ÙdŠØ&A94Cɂƹ‘(+HpJÍÉÏ G–Ó9t…[ùu¯W YÔ9Yß)YáÿéÕÙwªáz ÷Ãi2¸wÃhŒxwŒ7aaŨŒI¡y\‹”!‘a‚þ™!bõŸ&"˜\hlj>ömÝ|6¶9Á6£™íô‘°× òäT:¡! –ûšZ q$qµ}Ü¡xñ—Ùá_À™Z>)/â’ˆ,ºž Sœ~%n"¥i5%EeÅ~ËÔ•9Åi^y7Ùé@ú~ bI€ci–B…fNŠHSAR á †ŸTÚ1²·¥ÁÒ™F˜Ê‡i9(i£ Hñ˜é‰E½Ø¦—±…­õ¡ÁÁE‘^ß‚’¡Ù:+j;nÁv"—ÁÙ§0j&=×¢„*£öB£+ÿÙHbz eœÃ3FÑpJú¶›éJ—º©˜¡¨ØÒžy÷žÊÈJÆØàÇ7ª, òIª²1¥ZÊkV¡_Zz« ’y€Ù1:§ºŽ%­€ Ã:¬NuJý‚–e¶  É W¨©Nò”ê¬ÍZ­°·¦`­Ó @rú]t:ûY¬Ä:®ôJ®&ŸÉ:ˆj5ª:U‰²“$Ê¢ƒz.´J¨?9Iʨ§D®þZ¬]—]1’°•£CŠRï´J¡ô£“ñ¦F*¤Aª£ RE¡´œGÚ–ªÃ"€Q:+bµ°ÄŠªâ !ëE5+ŸÅ¤<„«]Ö‚@¬Çzª‘“^* _ÿa¥ ¡N5«!½ú]‘ÚQŽ¥dÿ:´âz¬{Т‡¦­ÔLƒD® »p£’Šp*:< ­YØ­cö¡nTV#K´DH¾![{Z¨g³Pq¶6Ø\$¬ë:¯në<*ldËH[*úš9¥°¡äµ]Û·0J„† ,P°ÄWê·Åú§Ûå©®„³ŒC`)ª¯Z^[û²Ø0´—Ûµ™ë¯Ä ©?Ñw£êž¢yf‹¶!‹¶VºL»ººc¢:»³V¹ž·Dn$´ˆË·^«ªžGq5ʬú>Žú²·;² ¨Ûú»ÚJcÀgªÈۼ턵ŸV|VP\‹»ÃëµÇZq0º{Ú¶Ñ_Îÿ„¶â;¾åº`Þ fn;S÷:¯u»'w;(áj½ò‹¸z®mD¸[B¦$¬ÿ*²NÛµF+±›¿0…S›u± ¹ŒÈ±MÊüz㺹Á<¬lÁl¥¾$H*kC²z+ »“ä;Ââû§û»¬»RYEP0H[Ú³cö³„¢¸ó{Ã×Ë%À»J -Š'H8l½$%HK0ƒÆ£‘uGwÄ€S‹K-Ð;tŒeªâzÁ—[ÁWŒÁZœÅ\¼¹Vš’" £Ô+¬$\Æ#®òšÆMC/R„‘fÜe¦´°Âtí{%ï›á¿×»ÇAœ° z•J‰tº„L²†,²ˆ °ûÿ;µ©¤ªÙ—È… ÉßÑÆ —<ùº¹E¨jÅœŒÅ¼Ål¥-ŸºôÁ"²œlüƪŒ¶T̲ÛS®§DÆ«<ªÄ'¦·Qyœ}}Üˈ[¼4\#¾{¼ 6vĵ‰|¶§«Ìã{ºCüÄΫ¼Új¥×É{ÍÖLȪ”tÒŒ$QLwþJ“ ÊW<ÁåŒÁç\Î]¬Îl²`ˆ@#jQâ Q4Ë«\ºs¯P¡¸ölÏVzÜ%ÓX2Ð]Ð-Ð MÐ m:ßw9y,¬¯éË À¦ºQ›SmƒlLÆÙ|ÈØ Ò°ˆqŽÎê}Æ–›ÙLÞä—°£¼4-à䫾޶nqÚtBàW>ÚQtQÞºå‡Z†‰ûæo´¦GË9]µí}•ÛÌì¿÷µá½¬Á-Àl3×6þN0Üå©zÀ[À¬ÝvÔç4ÿžèˆ^®É•70ÍeóÅe»}dy¦nÑFu îzJv`æ ̤T^å®çä¤^êïäw>åÔÕªá“ÜêZ}¹°È²Úá)~ë¶žë(ÎD2ðÕy[±v_ÆæçÇnw~îéÁL#y\؆ü¯ˆ”ÔE]힌 Ь N-|®è-J»î'>®ÂŠHX$JÞ¾èé>ãˆd-o¡ØË‡“9ÉG¾F¡Ž×£^êúNÙß~ £÷^Æ1ÞÏ™kGªt¾ÙËê¾ðënØðºZa.Ö³=ñ®ðæÜ.#ç íµ¢dç·+ëç…"ÜIÜ O×Ȍ넲tmîKëL'Ïðêÿî£ñè7`âó‘'d%˜|KŸÜ$Ëdûnê]–‘¨nŒªŽù=Ú&®Ê,è³nÕikð-õXõZ_×[oð\ÿõ^ïõYîëÀžz¶÷a·‡ö¸µìæsáЮÇ| ÄHmí[Ø(ï— nbMÒí2N+.Ø OÝ.OÌÝœÚ2óëNRô¤ãÕ§‰E>ïõ®FAßÌ#kâEÿä¨kƒ£é%ñl5“OÕàNù×}Hžý¶ÝVö©¯õVÏõ­¿ú¬¯úc¿¯-ñH¨Äœhû=ïóqNܦíÃÛuüK÷óm÷Å-ò1’Ñ„Î÷ˆ_×&oƒ,Oâæ^Ò*õDJÖüØ/ã4ßiÝÿ­—ørcÞm >C@mŸ¿ñÄkù—¯Ü¡­ùIïÁ>Äêã ë*Úû½ÌÓ-õˆ„«áh!Hà@‚ D˜° ‹­¢µ‚ø0â•Z r5¨F7fÔd ,3~4™²•€\¾„ 3@©@4YÕ tÓ&NYÙI“!ÄV‚ˆÅÖ*éR¥M™.ÄB6ªJ«R]zõêQˆF½¶ p…çØ 9`u-íZµmÙ¾U;ôëÜ¢uéÞ­{…ES»}¿ê…Øí`Ál)îÅ›ØïbÅv{‹A•-_Æ<ˆò +‚®d™ELÒ2qÒD}Z5j(­œ°—ëÿlÚO6½rënÞ¼iËâJjâ«k6t˜\¢òä•Çþ]:×ÜF»2HñáòíÍ™sgîü¡ ØžGŸ~ ^äß!^qùå|±è4¨äÉúüý·,­´|ê©@²Ì²¢¢ŠVðI®Ù’‚È6 oKŠ<«zëí6à ˬ² ¤ -È #ÌÁÆCQ ÄRÌ‹_q°=Âê¬(lôêÆpäqGu2G£Â FVà‹í²2[³&•­I†¬È¥*¨2€Xàr¼ô²Ë/ÇäRA3W€n:® ¤ðB¾„ÃÐ76£ˆ J)Ï<Ï9?½“ÈK5¥-·ëÿ®²(½ú”(F-ª!©Ô£´ÒõrkèQŠ,â(#ÿ:MÉÓPû¬+O¥²¥öx­ÕŸ^u5Ö̈́Š¡=jB]ÛÄF/§´š°.DëêðÃoj²V”-±¡Ÿõ+*ÿr±­eáºv°lÙÚöІ´4\ôð#ïšnÓjE²ÍBc·]wG ´ó4ޏִ|í8ÙEŠW¥Â/5½²“^ƒùl´;ñM“_~«›6;äºOa÷^΢&·ã…Œ¢(<åà@>Réûe•ú0^Ó<„DËÁ5ûÝ5gÜXÀÊß9ñêPf™E\+šÀŒ&Ìh§e:ÇO é·ÿ¤v‹ê¢žÑ«ê 2¯ ® úz½ƒºÎí ‹œz-$÷²¬.Ù®¬I¹-›;nº-cÈe™öг&<ýFíï¾dzaBÙD¼ßû*ñŧ#øÎÀ'ü8HÿìÓpë¸Úü¨Î7|Ïla£Õš…ËÅÜQÕÏvkÅ5ô½LUŸ¨¢‹Nb!£+°p¥…üBÒϳîk âר3•XÒû¥™Ž…ÞX¡ÚûÜgëwÝKgÎû*6fè‰6ÒÈ¥¡}VZh F¿µ‹\ÏÄÎþ÷ã§~ûõ2;G´GT÷2áôe(Ó‚Ýݧ€ô]gîã™’ø¯yΫW ¦/‡)N`Ž» ræ40ÿ»g‚{˜ê,F1ÃUp:kŒÄF(2Љ0a‘Úg8öºqI¥/! a+Hf2W0?, à}¤Ò‚á± { ø¿ÿ<ÐJŠž÷Œe³\áìzW˜õbÈ=±í@àS_³š6F§}aDc[²-½¯3otcá8G9Fņs)WaÖv™‹hÆ%)‰pòs…>æ†3x£Œ@:R™¼=p^‚; $)GœÂíëpÄ ãÚ#°AÕIr«‘¤åh‡:Ô Ê„ƒBá}´¨QžÎ•0Ä#ÈI6ùѲ~dsßWdGÊhlêvÉÝ a<ápF8Ì [!’+ôpwšùŒ¨VÒ2½ ÿ(Š^,Ë­FgEu³YÜÕèèÒ=lš%YØbÖ[ÆGÆ™ig4W:µ%Ï´°/1©õYG8¾­E:—¹úç®Ë|† í‚—#%¸Ð<ͪz§Ô&+¹®a{!wJÑBÕ(…ž™ EºB‹iŒtëáR>ã?8fS‚$ÈbBFÒ®,e1ÊÉtÚD'Z³œ?­‰­Âr³ÅySg6¼ Q´kâŒVS TY¡4g±³|OcQÔŽ¶Õ¤a-åSC¸ÄO7†F=ÂÎþ®¦–µÕm‘ouk\ïÆHjºlU„›¤àô:É@Lé¡Dl¹"Q‡Qt¯•ÂR·ØæhŽ6ÿ…,Qb—±’±¯Lë6÷#„äæmSRÐÛ\g´îRS¶Û©MU«²ù0Ï‘©ªô`6£¡róŠG &3N±ÈÖ@`LŸÉGFwB­ZiL£=Û 6²î³ S+azPê¾Ë‰Xbhvéˆl”£³!l';8í'‡ûŽw¿+Y.W;u!F1jÒX¦‡Ž–’i{Z¨C+™É¿ÿp€|%…JÑÀ±Íɽ6—8¦5© þ¦u2é9u±©!B®p­1¬*ž†Ñ?Š£æÖ‘³ÿ4É®»bÇë‘y åà" ãRÐÖ’ë%Jx åIó5Šåe+!bJÏ&•w©ÿ¬é„|Ù%'ÇB±$ñ¸VZ©ÑÍ´•iq–µŒÝ ¸© çm­‡TÐmoXCê­—wrÎy¢SUîUÍxÜxº¹Íw®çW#ÆÜ}:7¦)¨²$³eB÷´¼­èYj[d‹7*@9ôEã;鈨ÇGÆHÏ»_ôrš¾v±Ô}Õ“ß‘ª¸Ð§v1}Ûel¢Iƒº}\R!Bf Ïh^5†«ÆÕ´­3ÎØé0Š´ÚUb§-ig«×JlK´Þ…"Å65ª¥½7¼"ö°Õƶàf%ºï6šƒÎö$ŒÙÆZ2²¶~,{e’ÛÝ­`ÅW7¤„ØÒ~ç)í7g‡9,OÛßV‚-«ÿžÍØä6Ö^Ü˼YURd.rók9ŸÎ Nt>*¶>›øÏ)zLaýïiÏëÐ'Ÿà¶9êm:m,P@yHI _ò,ºÈ:l/eA:sùR:½²©¯”[Jåqîk¿Ñ&ù©}:p57]Ñ–D¸À>ÜMù$×0‹xpßÒŠîÂyÃx 6^>¬u²³Â× Ò²§¼nÆ„%ÅI÷÷‹AIã¹×’Äñ« YþX±Z±3¶è»»©¿š…Aê‘eƒÌäÅ’'=¢”¨)Õºò»®pß²5¯Ž`Î µFüš:tíÄù±°™[ô´V[¢Rð¯ƒ:ì2¥s@eú´°^çW{ØNÿt úò˜Ï²Éò“çPz63çÃ7âÿ˜ç>ç4á×ë:ÒᇜÆþH…ì Ûwíè¹aL‘ý^ÕKß<ÓjcB…^ì ÁuÓ‡flúﺫñָ׋›UIÕÿ¼¶?³óºîc©£Ÿfë Óá5¤3¿»«kƒÀ¼’@k›À¾ªˆÂË1q*³Ã°º¤@Ä·}“·tC·ÐY7Äk7dÁË2£·Îª¥Qó‘Šp¥~k@-{žôãAù# ‡Â•Ír?P«èA/Ê:Œƒ Úʹ¦Ù?»¸²“B|Ê4Ü븡ó¿`VPŸ‘ËAl¾0¾é¹±ÁÒ¿ØHCÿ†’9øÚ4@ZC¥®d7+4@t¹+@#B,Äæ‘»»³;ÀSÅVdÅHz:Ú(Á¿p³Eº#Žd¼ÅkV@“2ŒŽÃKÁÒ<^|0 µïSéÛÅ^ú½S¬&¿#½H<ÄšpµéŠÐcˆ8jü­Ù³³Ó Ƕp¨Š›|GAœÈÿ2…)yH®QF®)Â…ÉCSÔÇA¿jœF’ä‰=+‚ÑšØÐƒ’D°§Àµª?™d+Û¶vªGšÔɨŠIž,¶š ”Çú™©ø?h É{À ·lʧ<,•û*ö€D„J¦ÔEclARª¹†¬2xL»w<Šb$KÚ¹Wƒ©[’°­@ÐJËKÊžêF“ôÆ ­)Q5ºôÁIäDõ‰†)¹Ig‹@ ÄÂD.¼Ëe3«wÃŒŒgŒË˜>‚¤Ì1¼—ØèÇÌ„ƒÄÄä9&4²‡„¿©ªH„$?ÓTV°£°ÎèÌÓÄÈ,0—Lĺ,=(½,I¾tÌ4ºÈÿ t½bSèËÞ,Ì@ÀŸØPΔ².4ÎÀIÙ|¢û;«Îë¤Îì¼ÅU´NíÄEØ(ËfdAfÄ9‚+{ Ï·TÏôdOGÁAéi¬MÝœOÚDDÓ[GulGý¬³63Ä0òX= ½þLÇÍOÝO5ÐuÐ}P‚LøœLÍ´ÐÊÄÐ14HÏDM=¼˧Ø¿%ÑפHÎ,M¥)ø´«¼´Oú´Mm:˜,¶´Ñœª£±À·žÌÑÅÑ£œIŸÒ-R! Àè”Î¥tJ«tR¦|Ò&…Ò)•RQÏ+]Oˆ@LÙÊöÄR/ÍÒËzO•‰¹|Q™Ùƒÿ-iÎúdÓû,Î7A°©zNÇ´•½H#ARA L8]‹|$ÓéÌв±5¬L+€ÄA!UÑMÑHEQ΄© Í>×\+ˆ-9QG}@O-§RxV°•œÐƒ@؃›PÕ@К0…T5 WMWù # *IÕ5QÞôÓ:ýU;.±P‹¢p .„·¨"Å´ç„Rô_aÖk0 f5Ö´ NSH%•ÍT|ÅîÜÎR°˜[Ð( ™8Wt „(Ô‡+ÔBí+;y×y-Ø’oÅÎËJ0Ø.XÆcUuEõ;Bɇ°‚÷ù&½°‚V°ÿ£hØÜxˆ”âRŒ†¨À1Ð%•Ï5q5t5UtE×R([͉™ •7š¸E…V(‡#U• YúÄOõÙýÙ{dP¡P¢õÙobˆfm…0—Ü0»¸H‹ð–#1›k(ʉk(’¨H‹°Ç¨ÐZ£É ä¤Vç”=@ Ô eÔš€rʼnwE #ÄÕ‚ (  “ÀÛÓ¨•wÅ ”eTKíTØ”TOTÃåPò;ú’7rˆ.2›7б²Ø¾Â{„‚äˆØ½ØXRä·$?nÌ‘<ÓÉY³(Ø”­Û¾jÙŸè-´0ÔŸ ՞ЋÙ=_Y§:R¼ÿuýç REÒàR"­ÏX Sð\ÕdâÔT{Â$EóPMoYÕ™EÎÞýËbekP@ÏP ³ƒVúݸdÒ«¬RÂ)˜R8×R [q_Óob…=0ÖÒ½0JCáR+P^·œ`^SPÝAQ”Œ ¯õ å¤ r5T ~±jN[Ù׳¸ÜTS÷-Ý5ƒÓ.Š‹f%‰ÍZÕì f%ÛzZ\µx'¾‚ð%`MòMoI).¬âz‚ÖeÕ €jµ“ÿñõ^›ãÚÓWR .^þùØn]Û ò;ͼcŸ0Ž<¾ÐF%Üó²Xˆ@ ü1;eŠ S`©è«hx ®(¸ÈfUdÓÁŸÃýã‹!Íî@OŠÜ䙋ØM¾±hQ<,ߤ$Ý6uáÜíÕ"Þó`oÉZq{‹2.dá¸muZµxâÎxå`ÅbNì^¶fJ,åôVpýNeæÎ~]fgnæìüW=`ô¼Fª æÜ÷+¯µ#Ñy³³G:^à0EgðÝ2s·NÛâT–gÚìY µçe‘`k‰â†M ¹Z(&P.´Gè)Ž€îŒ(6Ú{.Ð…vè†nhÿ´%Sµíã:¶èÌ\íŽØÙ76Ÿ½èÝò” ´°¡²1›º•– vcÜ–fé—†Tñ8f}kÒm²öXË~™ÆfmÆÞˇfG†ŽmØæOÚZˆžmÜÆÇÿ9ݽöm¾Ž4˜lK&ìá6ìâlä–iÕ~™xvnÇ~Q•j¨®jáµîâ¥jêÖî§æîìîn©IìBÌj®þj¯&ïó`q+mÔfïõvo´æmó]ëÖ~nú.=ÊÆoÉÖïüFæTàðßn>¿Nná.ðÂ>lWîãV˜ðþÂÅ®o׎ðùÛï ço —lÏÁdælÍîlÐöðïp+mïÓ~oÓFñOmÿÖ< ‡î_µz¶í¢ÕíÛÎí¯í§qç–»¦Pð 9'n"gp7ò7îÐõï'rqû†r¬›îï¾î)÷î+^*Çî,Çò*OÒ&Ǿ)ï1GoÿóNï3/`Wó?qwsîHk¼žï'§óç†ë Çs Ïó?oSò?ò+r$'ô%_p%?ðáÖð„p¯sǾs=—ô=Ÿt¶s÷ìÍÎôæMqinóP_ó7õìsd†gGŸpUgZW—íçñ—õZ·ñÚöq t@òŒ.ôDöA7ô$?ôǽteõ(pé®î-÷òíîrg—v+çòjW›Kó2's4ßv3Ƕ°&u6/õqwÒŽs óžx•]EveÛd7'JWŸ+h²ëÞ&.;ð•8y׺\§c uµ@“]Ÿ zÏE‚¯v`ŠÌeéÿ½@΃lX¬ui^ø‹¿˜E7¿F/‹³p÷ hñ›0Yxô /Øè‚”<5Ì(äwèNxbW@1”xãæñá¬'aœ‹ÙÚClÖÓܸÈä0YäV,¢µ"تÁ„‚¼r¥•ÃV‚ ´b!ˆ ÃhLlå1aÇ Ž 9ò €”*W²léò%̘+ Ä*P)›8uæ¼™3P€žXèñiÓ ‹šA•–ZÁÂJϧG“&͹'èÒR4wÞLjÔ§ž§M÷ µ4 {rm+4gV×䲊Fwî\»xéê½[×o^À÷ÿf‘ôJÒhWl^3ÅB  ¹‚X\  ØæãÀ×j¾¸‚ÕÍ+¬Vè5;±îd¿™ï ÚcøÊf(¬V6 %ÀãÕšýöÝÛ›/]”2‡/þ2ÀžRÊq.WžÓ9N×̵:·RwP饴Õ çXŸK/±|EòéA›»à¾&RêW\geÏŠçÍ÷?·ìÑ$Iþ' H8 ‚"fØk]A@ѰÀQ4®Uä F"F+]äÐdÿ9äA¦6`NÆÂ]øXE]Ñb+'Hà7xEÆùøcLYñ4äWJUUY9%H 7e@±X)a ¥ÓM.ÅÊÿVG¶%ÖPE…^MMrÅV‘oéa‚µù¦›q‰—¢ÍÅå5uÊ• °ÝEeybÙlCÑeÑ5M††§\¥vÍ£‡ª¶Ç\øéPf)î šnrz 'P@Š:*4=·Ó©©F·ªK’SÚá—äPkÁz8IŪ’éõ—jVÈáäT¯îÄ⫪*‹SŠ% ’HÐ:‹ ´ =[Q "YÄ#GSJc ±à`+u¶bb+0$M1’›Xj¶Âe´Ÿ bÊE}z„[AC=‘v%Ö¬µÔXmI‘º°]ºEäN@ åUX,â––«AAa˜q¶B”JYá”ZùDìÿÄØÅzN±SÈq”•j¦ù䜟êœ3œé΋WŸáF™@H`g×,Ö( êÎ6ô€"6#l“áVµ‘ÎÕ4× -È s¥ ÚÖCo½3Ú{ Ç0ÛAN‡êÛªšõ\xÊ!E få½ÓÜH- [;á‡wNsÃWÝV(Ž·”,îAÖvUÙmßOŦÚ\«8jž`Žo®£H1TЄrnP]ÿÍ zGžþúG™4®‚©³þ¹ç;öØ6ï-meóÃ7 ¼[P><ðÉCŒòŸâ›­D?=`ÒWO=aÖÓ™í^ˆj×÷x…/×ø×”þô‚ &>öà·Ï~õ¡ö>ÿJ¦Â½,þ÷ëŸ?xùû¿¿þš†8þ¯?;àÁx­Œ$ ŒÈÀh)°¤`Ç:T0 NpZ¤ÖÚè×;‡)¯y& žÞNXBâ5e€*t΀ӦßìÆ7‚¡á`xs÷:ì!(D‘ˆC”¡C(¶!:NäOÜ øDýP1ŠUœ¢³ˆÅ-JQŠþ¹ìrÆ1бŒ¸;cÑHF5š‘GKá©^(GÂpŽ+¼cÛ¦´ñ‘g}ü£ ÈA Rò{#ÛìW@2r‘Žl$$©,‚%̃–Üà%+‰ÉMj²“¼`&?ùÁQ†²‚£T""IEÂ<âÑŽ¬ÿ|¥+]¹ÇB²–´¼¥-s‰KA¢2•¢2殈ª`Æm˜Ê"¦y?d2ó˜Î\æ$Ù(Í4RsÕ4ã5§iÍmzξ\Øï`IÇX޳œ­4g¡w½ufï}äs§ùà‰>vºžðk§=ß™Ïxîó|‡üæ/#)PI´ •¤:ÈIQ–R¡ž4eCA¹PˆNÔ¡ MH/jœU’óœçGeyÄ"ш&-)JIªÒžt¥8|©[ÊÒ”ÎÔ¥ÁѨ*“ÓÅ+îT‹=å"OƒêS¡5n_Ä&7µ‰Ô¥*µ©Ù|ê½‰Ó ©£ ½ªU³ªG]ru—]ýªWÃ*˜Nµ8Š4(ÅZšÖµ’}¨E) W‰Î•”¥ë[ójÊŒ–ÕmXE§VC ØÁ¬†+b«Ø6ñµ¯Ç À œYÉF¶i•¬e)«ÙÌr³ž½,h7ûYц¶³¥­i)‹—1èc­ek[[ؾֵµ­m_‹[–Õ–··íínƒ Üáþ¶¸º%îqkëÇ6lwË-•s£[?éBwºÖ­.vŸ«]ên÷ºÝÍ.wÃë]ñ‚w¼æ-/z¿«^ò®÷¼íM/{ãë^ù¦;opensips-2.2.2/modules/seas/doc/images/image038.png000066400000000000000000000332701300170765700220300ustar00rootroot00000000000000‰PNG  IHDR™DzgÔhsRGB®Îé pHYsÄÃÚj˜Ü6^IDATx^í ´ÅÙ®ÑD@¢ÈV HÜŠˆ7È"^APx¸¨A•Åú1A=?âÐ9KAA$(`hDQ\*Å5ŠèBÊÅ Äü(z¢çÙûÓJÓsëžÝ³§gúíµôtõÕWOÕ¼SUÝ]½×wß}WO›ˆ€”8½¤e9kpâĉ_ýuN3ˆ@„ܪU«–½+iYŽ*~î¹çÎ=÷\µª²ÿ&Ī€[·n­ªªZ°`A¬¢Šy0Ò²ÜZ6dÈÑÝ*b^‘ ¯œ¬\Yok«VÒ²Puºw(k‹€ˆ@< HËâY/ŠJD iY8^²ˆ'iY<ëEQ‰€„# - ÇKÖ" ñ$ -‹g½(*p¤eáxÉZD ž¤eñ¬E%"Ž€´,/Y‹€Ä“€´,žõ¢¨D@–…ã%kxÐó˜9ê…gËóxsÊó»>ûì³´®ûô©¬ªWoe½z‹m¨¬¬ʇ=7ÒîÞ½Û÷(ö«W×_·nÙî·ß~Ý**ªöL;yéÖfÍš]Ü®zIÙ5Æ}*+}6œÂ¬Aƒ#»6rfiã<òÈ#ÍU¦Í²°³„ŒRŠR]L-ܰaC&³ Ì&St•Å63öôž2þlVGî£Ïƒ+~Ϊ4¤;wîôFúõë·k÷u*“,HÿƒžÇ ‚iOõËÂ3 ‚/ÉçŸþMºÍ¥Æàõ×_ç«îÛHË2 ÞƒØ,\¸þí·ß6Õ@È0˜õúë|ͼf¾Tø_¸~}Šûz˜yu6gœ©Å%žk=ñ`°~ýz"ô•ÅÂf³°3™áàé Ý÷±Âà£>J-8fœrÆVGiU2Õ,{U‚o”úå—_®f µÈ$ê—åÀ˜_¿lLM?bBŸê/pÚ&>kÖëœÚÿýûömåýõ&-_ŒI}¿_»Ê,<ð@_![³fM§N\ï }9öØc]¿Œïþ;ì0_³}öÙÇb£o…Dº¼‚4)âá+Šo<Ö"lW„ µ%÷nÝ*\éÌ ¥2¤½ ÎÁb3{o©}1SºæÍ›£e,ÓäëóZZ—5œM³|Þ|Yä¬J‚íw•ñJ‘óety¬+„4 )L9¥ÆÆyŠC¡¼ò‘“C@ÔK·nEžÈ‚‚{…5;"Îf™ ˜µÌŠN@ZVÀ*¨žùNùóåG'‚¯_Ú‰-,™ïçßÔù{s—6çÔ2bÇ·Ú:S™¶ q:mÅÒ“Ö• ÆÂfæ;½YÙ¸-;‡ •D)ÐY[˜ Š´åLˆ%s´5{ï58"—#qË€7g 2ˆ„€´,ŒiœÐŽ™ñý1Óä3å»Ô·U«œ#Mo*ß÷*{ðß¾}û,#M²N“#µvmÙ²ÅúGÁ·<8øœ#Ž”Åé>¢†´)3ŒÙ{¯Aª’Î/³fÞ?æ+Àz¯±§!Ë<HËò€( _Æ\¾?¦ÆSóõË>Òô ™Ozr~]÷eù®":©qº ò@EM1âž<†â꟩CŠéo‡™¸²¡z¦‘fªdÜMÿŽÍ:bÌ”ÁPK«çÑ òN"-Ë]î„6ôý¥Mf#¬E?܇ålÚÙûŸøà‘G9¹Õ¾¡J¿ìíí|…V­Ú’ú÷Áßœqì›ëÕãÔÏ~ö³Íþå<·¨W¯â«ŠU[¶0wÞíçÿþfb¿fgcº/½ôáókw-}k+;ß~ûíá‡Þ£ÇO]‡ãM›65‡¯}ü£M›6uìØŸ^ÿÿü¶9÷Ýw_|šÙ{ï½—6εkwycðŸ´o|Öàÿø6dúÖ[[qB7dLÏß«Q]–ŸWlÞ\3‹³O?ý”˜¯?»©óŠ©ÒÚÏyk+4.L÷Ó}[íKévîl|ÒáûXZÇ„:"•¯ŒÐ{ñƒo¾üòKW59«’¨¼ä½´7~Û 2–{¨ö·¯**úöí*UÂõ SŽß3LÙ¯-šú˜MêP+ÓqKânÝÊž0»—o83rÁ`å!êÇÌuŒ ÏŸ‡âZ´,…õqÆÒqËÎ?¢€´ƒkÞõœ•³”–DËò¨ %G@Z–GcÐ|YДDD v¤e±«$"iYДDD v¤e±«$"iYДDD v¤e±«$"iYДDD v¤e±«$"iYДDD v¤e±«$"iYДDD v¤e±«$"iYДDD v´NFŽ*aÍŸAƒõô¬Ì»:T@eG€u2xÔ‚ Ê®d,´,\^~–°Ê˵-ÛüEåUú(K3lØð‹.¥Çr÷%-+÷®Ûò͘1ƒ ‡Z·Ù*7¨§ù25‚( mÛkìlÙ²Åöé=ñïÎ;ù—¹³?üðÈ6;â‹£Y³fv‡ôeÌöÆoðfmûÎ2mIèôYZ:} H‰ÿÒK/¥ÏÅz”6:N»uìØ1S™œ@Ã…D‰2¡ð1¼âŠ+ÚµkGo‘æÏŸ¤È™b.ÝãÒ²Ò­»ò‰üœs~õÀ0â0`€•ÊË'4Sj6&°í8“PôAl[°`Aöù~šåªU«F“7’XZwðàÁ¤íß¿?Ÿ~úiöÝð6»ÏÔ:I‹Â—³{·Þz«q`¼i—ŒÃ9'“˜HËb^A‰Òåa.Ì&¤z÷î`ñýd1bÿìÓa‚ߔ˾´H ‚B†ƒ®ç7lØp¦“0àà5×\³páBvè2ãfÉ-½3vƒ|bbFÎ’œ\Ð5›Šâ#£ŠšÍò²Að–z÷Ij™œXHtQù—âdBákóæÍ³ IBÏ®Q£Æi‹lAfï„–nƒ“–•nÝ•RäMj6û²¹¸½ûÌï0p³SèßáÿY³Ù—™ƒH˜©Æ˜1c–/_Ž”Ì;wÙ²eŒª0àš£Îði·¡aLsˆVâ%bBã“&MâàôéÓù—#t íëb@=É-°äÎŒätÊðƒ„ÝpÃæa ? ‰ê­·Þ"*ޏ"§ ­ ·tQ)]&© cZB6¶”7m‘qøÔSOYײü6­‘]~uZÌ}lz4+V,7} ¾!wÜqG$w¥Ñ§£‹6 ]fœ<ì<,)©]P.¿Mý²ò«ÓD—ˆþˆM™‹Z–ŸÕAÀ½°×Avuœ…úeu ¼Ì³+z¿Ìø"(Y./¦ÖÃRß•Á¢Ô“Y‘ô 3“bˆ­úe+·Å$JÈ4BF²ø³@-CZV °r+"P§¤euŠ[™‰€ˆ€´¬@`åVD N HËê·2(iYÀÊ­ˆ@–Õ)ne&"P Ò²•[:% -«SÜÊLD @¤e+·" uJ@ZV§¸•™ˆ@HË VnE@ꔀ´¬Nq+3–¬ÜŠ€Ô)iYâVf" "P½~Ù'Ÿ|Â;© ”Ü&ŠÀ“O>IyÏ>ûìD•Z…-Öûvë5UkëçU¿NµÎ_[_¬ò+ߨ~} q0¹#Põê£>zöìÙöñ-Û´ihÕP1R!À2¼»w¯pZ¦ù²R©8Å)"€´LíCD HËÊ¡Ui™Ú€ˆ@9–•C-ª " Ò2µr -+‡ZTD@¤ej" å@@ZVµ¨2ˆ€HËÔD@Ê€´¬jQeÐó˜¥Úªâ®yÿ4èR•aiÌv¯¸Ã ™ÖlÅŠ;Ò·‡Ýºtaiƒ›¥ššE–øWÖ[¹{Eu¹Ò»êÒ¥Êóðzúü4èÒ…Çßõü{©6ýâö=)-+É ?Ô»ï¾ëBÿ裚7o޳矢͞=âå—_æÔþûï¿cÇŽT3„`Öð“ªÓŽYäûnO™2˜$cÆ,ªÖÌ0fiuÔQ#GÎq§²ÇnΛ7/SÅ 8Ð)lÎâd„>©®@á ©$ÛA²ƒ––•|ýÛØû•¦#óü”)HW’LŒ¼gfkÖ¬2dª©ž‰;©_ìT- h†ó´§•Aâ·¥ƒª#œ1ãõ×gy}:?A hZvæ™g:ù³RÓWE.;uêtñÅßwZK¾Y$¯Z'£´ëœúó åaÌÕµ¦×³zö÷CB:>h–¯§†#dkÉ’ ^ Á˜ÞPv4Í2ä&õï{m ¿KÞ¶íîꄟæ'T}åÂ9ÒFq6lØPÚ­AÑ{hî¿ÄšÃóÏOaHèíeXø~¢\í~èe0廚v­kבŒ°ÐDWòN8Ê3Ï<óïCé¨4Ë4`ü9k%Tsz“A–•X%2ldò+mÐ(—¿$Ô éI_¶ª*ÔpíÚùÞ³&‚ÏOœG@³,N‚ÄŸ³Jò( Ï'ˆ¤²²2g^2(Ò²R©©0qÖô¹¸t˜^òjŽnÞ¼Ù{dR)çH3ˆÙ’ }˜¢òýåÀ†)^õÜP¤ËÉäš÷ ñັár—u, HËbY-‘ò®›BÊ9ÒÌiÖ¬YOdÑ·ÕÜõ²€dO_ ½f‡ñ8—hu[FÔURLÒ²bÒ/TÞ5_rw[YÚ\š4i’zÜw!SxÙͪ†Eï|Þ;Â"(uÈ"¬\ôàýâ’(ræbG’\›€´¬Ø52»S,m"îuàS(3bÞмöÌú3›Ö®ÝÅ©NlÉuRÍÒ$þœHò.`5œªªöí‡,_¾|ÆŒ93’A –•PeU‡šzÒ À’÷ÞµU-IÕ‡ý›]IÌôœ€ !¹i#“bš»€f©¹Œ?g­ä]@<:”2rÛZZ>9³–A< HËâY/£BƒøΚ5ÜÛuB±¸YÝÛºøûÞVÿþ· XK&Lð™1ÿm÷e)¶ !ñ–M@3Ÿ“€ñ笕Ú°ú'aäHþåÎáœÉ Tè¦R©©=â4I²‡“8a}(ß±&pÙÍì¾ï#–=E„ï3LÍH•–©ï™„œñ{#2õÅÔyú ÄIê}ÿæ™»mi¦ª$D"ƒöÝ÷ÿ£qãÆ=÷ÜsïîØQÕBWuJ¦EœtÒyÍ›wÜk¯vîÜYÝËèÚõ¸A·Wµhá-¼fLöŸ|òuÁo½fØTT|ÉEÆõöH[Yy"Çy»=;ø eFª´Û‰5®l ¿Y~\ùã6Þ´ÎIb¼Ï>Ÿ¤ãmÛv§ŒìXµ•î+ú׿6ôéÓÇ"W¿¬äjP‹€TÐó˜j" eH@sÿeX©*’$€´,•®"‹@–•a¥ªH"@Ò²VºŠ,eH@ZV†•ª"‰@ HËXé*²”!iYVªŠ$ $ -K`¥«È"P†¤eeX©*’$€´,•®"‹@–•a¥ªH"@Ò²VºŠ,eH ô:Zг [A€"E¼`€e"Ù øÖɧe,ÑÇ{zš5k&ʉ"°eËÞú‚áDAPaãF VZ6cå l¼êª«âV*ÅSP·ß~;‹¸­ZÐ\&Oî÷á‡4 9!Ã;lôèy&-ËZÒ“Ô– ~ÒêÕ«“Î:yåo×®ÝÔ©/æQn­Å˜4%ˆ;]ÇŒ{ )> ¤eA(ÉFD î¤eq¯!Å'"„€´,%Ùˆ€Ä€´,î5¤øD@‚–¡$¸–ކŸˆ@Ò² ”d#"wÒ²¸×âB@Z„’lD@âN@Z÷R|" A”€–±àÌK/­\³fMòølHEÚ´ 9žéTeJ6—°ö†*W"PÒ­_V÷kþÜpà /¼ð+¦¡hG}ôÔ©SÁͲ k×®µýîÝ»{+€e¶®½öZŽ bãÇ·S¶ü–·äK–,±UØ8uýõן~úévœ'ïçÎëâüé§Ÿ¶\.½ôÒþýû;ì°öÒó_ÿõ½[;å‚\¶lÙÍ7ßìr¹òÊ+Ï=÷\ËÚ×bnºiÒ‰'Veê€ Õ_|AÀæª.›ÖɨKÚIË+ëd ÙÃ?Œ¾ )tÒf5Í·ÚU9§lCX*Òzp|áÏ?ÿ|²áá7Þà_ŽÏŸ?ßË6’ 8®ƒ†RLš4)mcš9s&gSO¡V.vLaqˆ[œ»\¦M›FräÏŒIÕ³gOÛGȲGUUU…[6 Ø\%­Å«¼"@¬Ç˜›6}Èw~¿ýö³2 >üœs~•¥<ˆö¯¾ú*ÒÀŽë¼àaĈ«Z´8Œ´Hûni\’Ðe»ÿþYæ–}Ô0íØ“S7ÞxcÀö„Cìqnöì ­?ùÉO2%Ï•7•ù|ÿý#‘™$„@¬µÌĈñÝ1zUS‡´ÕƒÑa9í´ÓÞ|óMú2> ­ug|NN?½û»ï¾kÆ 4`ÈyÇ·§úgˆJg•ôÂ'C0·™â·^KÊâD9ÕCö¨vïÞmóhü"'‡„´]S¼b­ej£ªF=ôÐCˆš}ÇmcêŠî_õÏ>û,m5éÎ0wÖªU«´#ͱcÇfiÖ¦U匊®"òʬ¨¨ðÎèÕ&_¥r"k-cækçÎt£èñ¦ÇÄTQ*}ït•uåZ·n½uëV¯¥]д™o²ÉF²^ãQ£F¥iuÔQLrùFš¤eÌm–qëõÉ¥€L“\9£"S›wãú“}åÔþTˆŠ@¬µlΜ9·Ür‹+j›6m»W¯^\4@=®ü>vîÜÙ¦çÝF«Þ# Q ßJ3@³iæ”Þ½{/]ºÔë“K+V¬È¨HË…T.hº iÈL’@ ÖZÆd?’ÄW×f£®¾új&ÔƒÔ (,/¿ür¦·lŽiýúõô¶HK¯Š}Žpœë AƒQ¦Þâ€jpwEÚ¼¸tðú믻S¾ù2â4ÑAòˆŸ,,¼e¹‘"`Tx¶›Têàθ œe#ñ!ð£qãÆ=÷ÜsïîØQÕbÉò´!®Ü¼²EMצn @ÿèì³ÏÞµk¯Û{ï½û÷xÁýÈzï½ë7kvpÛ¶mÙgj,m<'Ÿ|òÏ~vø[o½±lÙ3:uBêׯoaŸwÞyÛ¶m[¾üÙíÛ·ãsðàv·••‡z(E¬ÞŽ?þø¯¾úÊœ{sÁ€»CÚµëÈN“&wìØá£aIÈå믿~çw6lØpÆg Ä^³o¾ùÆ<¸ƒ£‚Ieeå'Ÿl9î¸ãê¦ÈåÅ_Ü\¯^R›–,™Étgm<(m)¸ë®»Î9çÒ<"ß¼yó¿þµ¡OŸ>–6î÷ÊæQB%‰œ€î•©:‰¸WVõ-"  Äz¾,`d&" Ò2µr -+‡ZTD@¤ej" å@@ZVµ¨2ˆ€HËÔD@Ê€´¬jQe–© ˆ€”iY9Ô¢Ê " -S(Ò²r¨E•AD@Ï–« ä&PÏ–³úÓ9çœã–)gm»Çœ¥™l&ïÖ¢Eõâ%›6mògé:œxwérŠ[o‡wÞy'¯Èa¹nݺy×wbÎ|5 X]jðàÁ,HežÉzÀ€nP[Ç ‡ì¼ÿþZ÷Ò/³$wÛIÊŽ°Ú(‹ô±ž¹ðZ üÒ=÷Üã³w1g‰Ê›Ä[ÆÜ %/ =[ž6%J*Å‹¿ùæ;®ôìsÄ}ä ©ïúã,9åÌHâ>²s饛¡#ˆ/‹`m» /Âêžnuvî½÷^ÔS¬o΢Fnwœ{×(^±b9xãß›nºÉ»D‹-m$Á'ž;vìH.üË2¢H• ›0úf ý²?<ø\ŹùhŒçÚQl'`‹›Û‹flŸ›ísSnßû‘uÛh‹O™2…uÌ1£7Äßm·ÝÆët‹Ð#vî¾ûn”ŽãØ+]©Ò¾ ‡³íÛ·Oû¤[“Ý\áÏ– ÿ¢kô鄚KÆ»•Ü­ß—=*+…L¹Ðâ‚WCHË¢ (‰$À*¡¼U‡¢óÚ-F‹Ž½2)¡‡ÅœÞ÷oÙ˜Ôõ¹èF¥}oËôTÒnpêN!g¾…ཀྵrFå5æ½DRÕn¾ì±ÇÆ?ñÄ%Q0-=zôê56ZŸ>oÇŸ´zõêeÁ2å¾wÇ 8îVöþy{G½wãå^öVy;ˆV3f,É>_r+ǯÑJ›ÖìQ%ëß¹ÍÄ992Æé±¹0Œmç²Ë.C’,w³ä ;^W,¼n íõÕ™Ðù rFå¦ð fY¨RÌmTóeá´lÆÊÔª÷'¨ …”ó˜xà˜Z5´ ñZËè9UB¼ÄÕF˸DÀ›Xíkï´ÆÇ‡•Ù™#KÕ2Þ•ƒN¹Tˆ /»±« NËØa΋ñ Q–¥ ®¯,©uäÓ²œQ! 8¡û–]"£j Qi™Æ˜QÕˆü$‚€›>cNÍû*BßÛd'.0 ï{e“htvºtéâ…e#Mß[1àU­\õ¾Š,-â–-õu9Éš2ÕGΨÜdÐ,~âVßÒ²¸Õˆâ)=¼^ž{é÷^Øî€ïã a¾÷±¢dÄšZr{íVv"xcVÎ›ËøñãíÍ>i·€Q‘–ëü›z#H! Ra²2_eeeϲE Zf½­mÛ¶yGš^Lˆ«DmܸEcò+“Y*Ù éºi7ö‡ ª†d,"„@R´ £Gÿ¤$uòÞ0±v;7æË®¿þzÌž}öÙ´øè¸ÙL¿[;8gBún6ׯ†ߌ[J’ˆ@N Ò2DY™>ÝÿRÔÍ»¨y›6m0K}mµ¡dÎkÌ[À„Þù²œõ!ü$HËÄ‘¦Ç—és±Ñ#c…õ:djØÂ&̯†”JD diDFŒáfßÙiÐà'dtÙ°aCÞ>Í|Ù“O>É0“Þ™ÃÇkŸ3¡Ì”0K’ µ"°´ÞXbI´×zÿI¬õº*³Öû¯+ÒÊGD $nŒY •¢E@B–…F¦" 1$ -‹a¥($Фe¡‘)ˆ@ HËbX) ID 4iYhdJ "CÒ²VŠBM@Z™ˆ€Ä€´,†•¢D@B–…F¦" 1$ -‹a¥($Фe¡‘)ˆ@ HËbX) ID 4iYhdJ "CÒ²VŠBM ÜZŒ¼mÖ,½F(4å2H0dÈÔªªª‚døð“V¯^]Ð,ä<†¢Z‹1œ–Å„B*hÙ!Cʦ8*H@³fÍš:õÅ€Æ^3ºV»w¯˜={¶”–åÁPI B€¦¹²ÞÊ‚¸–Ó¨ªG?Ÿ.¿´,ƵªÐD@ði™æþ““¡ˆ@Œ HËb\9 MD 0iY`T2ˆ1iYŒ+G¡‰€& - ŒJ†" 1& -‹qå(4À¤eQÉPD Ƥe1®…&"˜€´,0*Ѐƀ´,Æ•£ÐD@¨~sâĉ7ÜpCà$É2lذ¡·À_}õU²Ê¯Ò†€·]ý¿š­0ù”¹W#˜>}º²ZËʼ¸!‹·}ûöÛo¿}æÌ™ûï¿×®];tèÐì ¯'ŸykíÚµÿûßÏ?ÿü›nºéÐC é^æ %ðÜsÏÝxã´œ=z´jÕê„_â@¬þǶ7ß|ƒÊÊʱcÇöîÝ;¡ŒjWliÙüîºë®›o¾¹gÏž hÿÓú™Ø®ýï}ž~úéiÓ¦ 8ðúë¯oܸqíjA©Ë™Àúõë/»ì²/¿üòìF»²õ/«ÿ>uú£_|ñ}ü–Ž(g޹Ê&-ûžݱk®¹fëÖ­£Gw;²Q»\ܾ??é¾G–/_¾`ÁuÐKš½­+®¸â¦qò«˜ Švó-sFŒ¸ê¢‹& WmÊ+-«¦÷É'Ÿ\pÁ§œrÊö;+,Mkyȇ°ie_Þf̘1þüÉ“'·þÉ7aKzáè‰;w¾îºëÂ&L¬½´¬ºêÏ<óÌáÃþ#ø/§¯¹¼þñ×W_}õ¢E‹Ô;Kì)µà‹/ž2eÊCwÞ”7“¡co?ýôÓéÖåí!Q uOF½aÆuêÔ)o!£¹0³vý¨Áýúõc š¨Ö£Âf"ðöÛo?žYmÍÕƒ>È(µ6N’“6éZÆ'×%¯½èWµ¬r¤!*× jéGÉ˃ÀyçÇÏ[CK_ño¹¥ÿ Aƒô¤U$]ËFÍè2©œ6â¼yó6nܘÓRåM€«ámÛ¶­MOßñá2WÕ¹I¨¼‰ERºDÏ—Ýwß¼‡šÿäȦW¹¬É•Pwó^$5$'¥E€>ÔÏþóE‹Æ¿ž³€íÎê÷ /h66;¨D÷Ë2n\ÌÙ’‚üú׿~à‚Û˲ü¬ZµŠ[^#2qAsÉ’%åÇ*Ú%WËøý\¶lÙÿ:­ƒèÿ]ð}+þ8¸n×jv¸å þøìk|ä`ê)çù^\ªÉÚhÛhiy{â‰'˜9õÅL»âÏÞµèY·omŒ¦e§8ÎÙÔòvìØqéÒ¥¥Å¡î£M®–ñûyÌ1Çøˆsã+—Øó%£FͧŸõ§ÿöüÚOimüqß¾­8è;åó€–­X±¢î+R9Æ„À£>ê}>‰¨¸µ¢ÝqMœT}ýõ×|ä kHü¦ÒƼf¾²tïÞ‰ŒIcFrµìwÞñÝÝj}®¿¿º™Ùg¤@?ë‚_ÿòoû­í§?ýéÂ…ë¹Êî;å«Ú–-[â9¶õ­À M`Æ ‡µú¥7—Fmùìûçáøå~l. |öÙgfóáú¿ÙG8~EŸÓøAõi×C¹£»ÐÁ—´ÿäjM§¢¢Â[yß}÷‹nݺq9’¾Øë¾lÞ¼¹;Ë#uôËø½åº§ï”¯ú9²­¹¤Û„‚Ï›¬xểîÿK/½ÄÓãõë×wfYð{Éo§ï`ÚÜù) b–wäe0¹ZƼ>-£:ùUä_믡_VÁûî»/Gìç”üë=•ÚxЏ Z†ŠO?ý´iÓ¦¾„LuM=Œ_ÙA¼¬ûÏÆ¸’ùíä® úþÙ³kÖ¬Ù–-[ò)9I’«epÀG}ä­é½öz•9 &û_~ùe0MF­W¯CirÊÍÑzOùÚ £‰ÔÖœœö”ð’ò›çkT9è ƒh9Ìë#dÿѳãäÉKùÈ~×ÖMéÂÓÞhc‡~8ŸØ%fiS1bI’„ãÍ^üäÞ_–öæ2~3Bº»ig‡¹¯]_çWô›/×§=åEL[Ü´i“n1Kì·ŽÔ’%|÷dØÕpk<© iŸ}[¡kœÂÌíûò‹3è—iu©,í*¹Z¶råÊÁƒ¯xèÞh¿u#'Og~DËDKµ„¼üñ<½ÉMÿ®Ô,^À“Lš‡ÍÞ ’;Æd­;†Šnò"ªoËóÏ?î¹çFåM~JŽ«Â²øp´as—K¹Dë³ü¼%W˨Knúê©ï/GRµü~²{챑x““R$ЫW/~Ï¢œ‹¬É­Ïòó–h-ûÍo~óÐCEX©øÃ®¼òÊÊUɰµ­Ýã"µŸ¡Ã»ï¾{ê©§ÖÞUy{H´–Ñ>Z´há®NÖ²¦isÏ<óÌðáÃkéGÉKëdünJõó!‘lãÇ?vÓM“4ëŸf¢µ :¼öæÞ{£™þçZû˜1cÔær¶¹²7à7’5-Ò>Y¶ìôï¸,®…ÿƒpKº–1"à%¬­VnÅàÎÛ¡C‡ÖÒ’—©S§2}awÃæ½ÑÓ·WIäí!Q “®eT6?zmÚ´=evÞÏ'wxÜzë­y{PÂ2#pðÁ£A<ñÆå üŠÆ{ ‡ ›qÛm·é8J˪AMš4‰^U~½3Ölá†sçÎÕè2`›Kˆ4gÎ^j“Çu:t—_~9B¦)ÿà­%¹÷ʦ2âI€;î¸=øŽ48^´ÓºukÝå¼Á%Í’5Óy© +Añºß€ËÿóëÈMü:êæžP­EZ¶.†Š£FâÏÄù–iôÚÙ3t£eÜЏŒ“F€Å3˜>›9sfß¾}ùË_ÚãJ©£ÑgŸ}öþûïnjޣԤªey¥eiòr¦{ÕïxÜ÷è£nР3Z]³ñ”Ò%—\¢Ö²ñ%*9´;ï¼sáÂ…ÜMM£¢;yófVâ8¯)aŠMdù5 iY6n¬vÍã#4²÷Þ{ïˆ#ŽÀô´ÓNãB~3ókmJŒB¹^}õUn¶àöFŽ´lÙúÄ«4¢¬eó–åÈ/êYgõÖ[?d—;cY”5^2=`ÀÍîGUÉºŽ™›$CÎ5kÖð&êܦ²`˜D[´h‘ÞÚŒV +iYnL>ø FóçGöTJî,eQøüóÏõ¦¸ëYZ–&L{—„´,Âf'W=öX„Vo Œª1HËrt¿œ 3Ý2ÿQÑ—Ÿd°¦•]Ą̃ڀ´,Iûý´M]³¨š]ÂýØÓ h˜Uc–e#É y3¦³à"@TÜå'ɼ?fFÕ¤eÙH>üðÃ_}õ•³àýLfFÕòëÇ;ÀÔ03Âf -Ë“²O9åîþoذ!;lÜå!}¹J &^Û¶mK[:ðÀÛ·oÏÎîÝ»È!ò"ë^ÙÜHé‹uïÞ}ݺu¹Me! ð:’n¸A÷Ê–ÃPý²¨Hʈ@1 HËŠI_y‹€DE@ZIù(&iY1é+o¨HË¢")?" Å$ -+&}å-"iYT$åGD ˜¤eŤ¯¼E@¢" -‹Š¤üˆ€“€´¬˜ô•·ˆ@T¤eQ‘”b–“¾òˆŠ€´,*’ò#"PLÒ²bÒWÞ" Q–EER~D@ŠI <×/»öÚkY6*®_~ùåêÕ«O8ᄨâgäÈ‘½{÷ŽÐ¡\šÀÊ•+Ga.o¼ñFeeå•ÏÖ­[ßzë­7ŽÊaiù)O-ÛgŸ}–.]aM¼ÿþÆ–-Êá³Ï>»iÓ¦éÓ§GåP~ê€Àøñã©5Þ4U^Ñ6*¢4hoEiÕªUT––Ÿ²Õ²o¾ù&¶51cÆ ~ä¥e±­ ´¡e;vllÃ>òÈ#Ÿ~úéÄj™æËbÛ2˜ˆ@Ò²°d*"[Ò²ØVA@Z–LE@bK@ZÛªQ`" !è:fX7nœ3g'ú÷ïÏU!®swìØqÞ¼yÛ¶mã`‹‡]tÑÀŒSLu³6ôŠ•6똼¢|ÕªUöÌûî›wî¹½§Nêâ>|x¦ƒ>ôÐÐ÷é:f±ÚF|óýío‹ŠÑž¸ç–(W¬XÁ»¦—-[vVͶk×ö‰'Æ7zE4…óøãp+íç‹/¾àçßGŒ9em¬_¿~±)DÉ¢1fúªzóÍ7ùaœ9s¦÷tUÍÆýúüØ–L +Т@ž¸—Î>Ð~¸Ñ¿mÛcÙIýÈ)kcíÚµ[¿~}Q/½Ì¥eiêlÁ‚´<~/½ôRwÚŽ°Ñk›4iRéUµ".$驚-HæüvZ«¨¨Hì-¯A@¥µ‘–¥ÁÂÔÆW\¢~úéü¢šÝ4ŽØ¦v–wƒKZBfZ¯«Ù‚ÌñÛi L?–y´iYhLm0#Æ”?sdÍš5Ë«’ˆ€Ô1]ÇLܺc6©ÁÕ(¦i,DÕÓuÌ:nå‘d—ÇuL×xØqíÇš“ ÉûÑw*lغŽ–X"ìm ÖŠj-/*!K>òN¶\ûñ­Éãý˜Øåz"i/cF‚QND@ŠL@ZVä Pö" ‘H–1Æ„‚ÝæÃÆ>G¸þÝ¥K»ÎåK»ñ‚[d9h7øðqذavÖrM€³Ž>Æ–Äîoä¢sHÂH*INâLÀZˆEx×]wñÑZš5*kÖ<03ËÅ‹{ÛŒk®qÒØ0 -™7k®Î¡n=Kß¾+ÇíÇ?þqj±^yå•¡C‡N›6S¬ìÚ·o_ŽtîÜ™ñìÙs}ôQ>r‹ãèÑ£7lØÀ¿v–´n'x0?7ß|³gŸä|tG2¡eFç­ñ—g™ÆÕl©e£!±¹¶áÚ•×ÒÐ6hTÔ> ÌÚŒµ7ZÊ56goí¦ÛrbÅfݺu9ÍÊÕ Aý2´¼eË–ÜfÁÎO<áæb.\È W‘xÐ’'1ù|øáÅÜãc÷ñ‘ßÛ1cÆØ¯ÁÖ­[¹ûÌž¤ã ž8a‡ŸÊýöÛ¯k×ÎìóT€9¤ç…b‹ŠÀ9çüŠº¦WE3Ÿ[¶l±6@Ëá#ÖãÙ8òÄ­dÖG³×>pÊ:\tĸ+$6¡kæÓêù¹Lµ–,-ƒf=y÷ΈãŽkgÁÙU$îTüýïoÆ¿(GÚ´icm¤I{‰päÉ'ŸäÞn\·,Ì¡µHmeO€‡ÆxàFŽ<Ãk…å¶DkgŸ}61 ¡JȂմiS>¢hü¦"vÖ–L¤ÜTûü(Ò¨FŒa>Í¡ývjK%8-£{ÏCH=zôp,ìá8»ƒ†E‹ä—“ÆÇGš=^ÎRÖGã÷“VhwrßyçüÌr³ÇùQ¥‘™{ªN—Øò•³Š~íµ×¼7÷[£²î¿= ÇG~Þè×_sÍ5ˆ§h‡,¾BCºãŽ;hQ<Ã4}úTÌh„ÐêXKÃ14‡îV¡„° ^Ìiϸ5iÒÄÚ­„}ŽðïäÉ“ÝÜ?’ôØcÑù§µÑ#Ãa²Y[~réó»‘)C%{õêeˆ:ÜÚÌ®wJ8x}ȲäЊˆ™fСCvøhG¼sÿ4×#zdü ò3Io £!™B¡‰§œr *6wî\ hTˆ õ×0ÐÜö¶¡ûþ‹ðÝÑ}ÿE€^ë,ó»ï¿ÖÙ†p ûþCÀ’©ˆ€Ä“@‚Ƙñ¬E%" iY$åDD È¤eE®e/" iY$åDD È¤eE®e/" iY$åDD ÈÊöþ²¥K—mæìy¢xÓ¦M ¦Ž¿¿¦ƒ"ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÊaºQâãäåæçèéêëìíîïðñòóôâÞŒZáõüýþÿ |wÌ7(û*\Ȱ¡Ãz»|’ð¡Å‹3ú‹Èe¢“ŠCŠ)’ãM@ÿ’\ɲå?“úòyqI³¦Mw0½È¸«¨NªHlhÏÃÿ Ø*lÓRë( pÀ­7vëí¦Ÿ ¹#6«bµž‚ɶšÛ¼C!`€,ôèîøŠ[Ú»[À½&@€²ìÝ÷ïÿ`@Ì«˜ÃŠFìà ¬Û/r?©1¬;îǼ ÞÈàò(»¦òÊÆZõ]ÈdŽLÈ—š£³İ+Î}Âdµ@<Ç=°ÉæšSì6óF±€š=`Ò@˜l/’æ9 j“$:Ñ—VM„#ëB@²f€[×E$PÛmˆZ*Ú=«=$ÃLr€¤½00Èž Lòp‡ª÷bSè÷Ç*ð€¤¤X~ùÜž€»0нxúÿ¨ŽH—êé'¼‡úꬷ 6€”„ÛË)`sÎÔÄÇœß=“39Btgí±ÐpEÞãïßÃËÄ£Å{ïå ÿ‘ò;o|’Ò§D½±ó׋\çtÔûzlóY…åÏ ì‹>TÏC¯ÎÏ„ó}úêçìhð»,Vñ“;~vÿÝ)à÷78û5ì( TàÑ%1—½&”àAlië=É 7x¢„‰ë‚#Ïò³¨ …á qR³¯¸D†3¤!yð)Ì_íá¨$ \0dˆ ‡h‘‘E °Ø!„C&ŽeK-c^› Ö7AU´âVjØÿèˆÍ{z\‡Â(ÆyÐï „pdAœc(‘m¤Ç…g(:æí‹+ZbW²G„‹t£énŸÖ9ò‘tA!u%<R*dL¢Ÿ,yÉÞd’’›b'û1IƉ“£dá'M¹A¦Ò0«œ"*_ÙÄX6Ì•´ÄJ)S‚Ë\þÅ–K¥/²K S+ÅÜÐ1Ã’L-,ÍLÑ3‘ LgNS—Õ”æ5£Mm“›Ùôæ7a¹C.“œ<É9¡’LÏ8-jëDÊ$åF6K ±ljŒ'PV ˆI1®€ÔgcøÙGJÔŽh9)':a(ÒZ‘Œ¨D'ªªpº*¡û\hS0ê“n^ÿ”£5ñè˜@J‹Ž”¤4)}PR“®”¥.Q)ƒ`S—Δ¦,‘)•pšS›î”§$Ñi˜€TŸ•¨%1j'šTÚŒ©êR¡š© ŠªUUêU±z«Â“«‘ªÖä8u i hYUyV%Ô‘¬k…ˆQß׆U¢¢¨^÷Ê×xõ3^ÐG`K¶­À;ƒ"ZeÆ–Á±ã,.&KÙ6ˆ©²˜Í¬$K9,8° Z«Âg¥0Ú(” §uBhw¤…´J!Y€™ñÁ*¸ö ôÄgè9 )ÈM²uÑêè[)JPX…Û®ÀËY¡Ÿ•"-ë@ 7ÿY .J–H¥­Ñ­ÂºŠZÇI—»¢•€w±‹¼ZáÙmo/ä[ÛòºWºð•nxk¤Ùþúw—ý¯€ŒøÀ%D°‚<;øÁÜá,„'\ÙSx—„Û ln¹¾°,,â®Àk`€"°/°˜3j‚о[â× w‚¤€‹€´ hÀ¾ ueøÆÂE²r,'29ÇâÒpï€,`sJR–¥p§ `-›rŽA!å}¹,W¦ñ–#á5/ï»^3¤šÝ¬:Û$¾³žÓç=ûyÄmþ³ “<èBã¡Ï†N4[Íh7 ºÑŽp¤'½Ù¾ZúÒ˜ÎtB;opensips-2.2.2/modules/seas/doc/images/image043.gif000066400000000000000000000037411300170765700220050ustar00rootroot00000000000000GIF89a Èw1!þSoftware: Microsoft Office!ù,Ä€hhh|||ŒŒŒ€€€ššš²²²½½½§§§ÇÇÇÀÀÀÙÙÙÐÐÐáááðððéééÿÿÿÿ` Ždižhª®lë¾p,Ïtmß&H|ïÿÀ pH,ȤrÉl:ŸÐèO$­Z¯Ø¬vËÍR»à°xL.c¿æ´zÍnKÑî¸|N'Ãëø¼~o¼óÿ€n~E‚‰Š‹I„A‘Œ–—–Ž@ˆ; ˜¡¢|š?œ  <¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁ¿A¥>œ ¬­ ÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâÞ@ÆHãêëìíîïðñÛåS;oòøùúûüý×ô>ÎI篠ÁƒFØC`…#Jœøa+{QRÜȱ£B‹ž0BÑ豤ɓî@ÿ:,B¥Ë—0ÿ™ù¤eÌ›8Qª¤éÄfΟ@%î¬â3¨Ñ£ý†ÞCÊ´©>¥JÚêHªX³’›IT«×¯Õ¬ÖK–¬ØžeÓj=Û¤¨Ú·AÙ2q ·îM¹KèÚÝ«“ëR¾€c₨SÃÀˆ_ö)Á¤J‡K.¹¸'P˜NÞ̱2Nª>5ÄAº´éÓ¨S«FíY§̘iæLÛ€Š~£ÖÞ-NÀmÜõºòÞí7¸Ö,‰+Çæ{r"z—ó6.îùèÒiS¯žûjvéÍÙY‚ý;âíêÆ3?<|ÊîcÙ×F/>Zùœé×þÿyýûÄS[þ%`UöX ÿ_î壞9 îuà;N!\ îSál–5a<FÖ!X‚˜à\#~•¡?!:“bV%âÓ¢'/b£Œ'aˆa.ÖèÔ8ò‡Ä©"¢G­ˆÐs Ú‘HäS9Š*ͬ¦å–\vyš^ºðÜ ”QÞ4¥†U6’æOk²)¤woƧœ WçKwâ9šž{žÔ§ŸhIƒR9g|‡"Ú×¢÷5ÚQ¢„†d¨¤QZéJÐaÊ‘¦ŠæÙŸ§꟣’Š©§šªª±ê`›èÀª¬³BêÚ)µÚz®¹Šê ;@¯¾úl°¨ A²Éò³lÂöAÿ(Ð´Ñæ3-µÍÒÙ->ß‚ëê° ›ã’—}8æ»íÂS.³çZ‹-¶›ba½ïÜ‹¯¥I,ЀcFöð:LȺ¡@±«p¸ð8 ;ÌÓµ®ëCh‡æÈ$—,˜&³¯<<ëlf6CãŽ9ÅVØn;Íád¼é€(ò\œÏ?_*´5Dó3cy<+É^¬»óÑ×$Í¢}Rp@ÄQMÓ6'z ØR'Àurf?cõ’ö­½€LG‹6UK{½7V}=wDÓü÷Z´N½ðá^^ïà9Þ-ã`Iî+åf%Þu²˜—e9©Ÿô¹ÿ¤«5z ¥¿5ž¿7š:\Ö1™Â¨‡~—æŸÅ2ío¾¾×sàrÊÄ_ÊÆ'/ÃxËÈœ·¾vz‡Ñ6ý‚Õ#v=~Ù'¶½yÝKö=x¶ó{ÙØ—øù×¥åøó©ß8ûäÉ>qðKv¿rù¶ÿrý“üTG¿õ´g€°+ „vó?öÐCüÿ5™úçZ±`08ßDÐ{l†>8"Eƒ)²ÎŽ(¨¾è9’ DÂf–ÖhóÚ´Ö @±p€yÁER]îq-D:¬X5+†¤N¶¢ž-¿ €ï©È–$ËÙë¶µJð¬ À2ÙJ`´¿ à8¶èx-pÔ€ëòì¾’š Ýâ,[cíp•:]ŽJ·@‹å®[]¼V÷»î.xÇ^òšÞ=¯zñ¹ÞöÒ!½îoOÄ+ßúÖ„¾öÍ/Þð«ßþ²„™°€€;opensips-2.2.2/modules/seas/doc/images/image044.gif000066400000000000000000000067751300170765700220200ustar00rootroot00000000000000GIF89añQw1!þSoftware: Microsoft Office!ù,íKÿÿÿÿ„©ËíW˜´Ú‹³Þ|ƆâH–&êʶî Çò,ççún¦ô Òl¼¢ñˆù„Ì&˜ŒJ§¼¥óŠeA©Ü®÷aÍŠ›Û¯ùÌ ×¿2ú ¯²çϸýŽSÓ÷Z¼ÿÒF¡2S±¢çRØØè¨¨å³h8™B)ƒ™ùÈé‰VhAhuXC9¸¨ÙâÖéúöIŠziy:KHzjK;Óúúë *Ê«+[¼kœÜ Ü|&,©«ZL¬<ºé¬6Tk-íþm¸m.mJ±¼ ¼n½˜WN¶ä²{Žo»›\òÏpý»æ§!ßõçùzýýãKEý-ñã/}õKqß}ôêN¾ò{¿Qâ˳‚úï«¿?ýÖâ˜Àïì“›ÐÿsU \àJ2@‰pdù_úü'A¦1N02^ý|—·™ 0•( Ñ”²À6s‡éàèþ'í1N>ì?X<÷I7,œà°pW¿Rn†ö©áÂZæP€vë¡í,G´¥No>ü!ï2HE ÿ>qvQƒ}GÃîŒZ¬ÉÁ‚X6'¢ÐpãÔ"òQ~cœ¢—è/ý)0Š­sÃh>z°¬Ã!7xà ê\ܣɯþ¹’ W I·ùIÐ}”Ðt%@êO|*tÿŸèB¡Ä{V¢½(GÄM¥PŒE)šN‚zŠ#i>MŠÐâ/ ­ÑäÞùÐ~ªs§.d E#šÒ“~Ÿó„–-ŠÔŸv4§™)»šT¹íE©K£Oõ‰U¡P¥µ'þ²ºÑIÈT¢þ—FƒºÕ´”¨Le'T• V´µªeõ`\“W”t„ qiJG4×N”¦NžéÓ·Mõ˜^MŽc+V¦šN•­ÄëXŠV„px¯l,+ëVªêu´\=6“9Éoží®¢Ý«ZE«HcÖ5µ¨-$dáÙZÈ,VºÜæ ?+QÊÖ…Ú¼¦8M9Ü·›!ÿ7këÙ`¶r–mn}ÎhÏê*S¤Ö½î´¶^^!)¼ä]Ñv¹‹i™A½$AXƒØŒ¸”w¾èNJàk§ÅÜ—&:ÙïEð»&”ؾú0 œ£‹Æ¿?é¯ÿëàCÁ"pzµ!«…d˜ &V­Šôa ‹¦Q?!ñ@€µ$X 'Ä+fñªBå)ƒJƚʌ1LãJ ¸Ã`â1 ‘»©Âl¢¯‘•5dT¹WAGnrŽ]‘\å.—¹×ŒpùÞÅ'r¡‰|èn/ÿ'»Â(±¨wyi|³sòÍÛÛØ8:Äu ;…oùˆÇ 9ÔyÎ6£‡®œŠyÏÿ}?§k‰ûúiÆ5™wÝu´aïwì¡®×RݺÇnêõ¥×:ìv»×ñ-o›—ÜnëŒ{Êi}vt^£dïúÃÙïwƒÝãé Ífžp¥ƒÒÞÙY9À·=mÐq~} ¿¹™§ŽæÒ*«¯JàùúzÓû<-=Ý%?ÝÏ·{òZ–3—U?û{·^+¯O^äÁÌ{Nn¦ïvò¹üyzš¨74ðáMnì›ûúÏ÷wîÎýïSß÷{Æ{ïóßï^üèß;#£¯ãg¿ûÝ„?½äŸçÿáÅþ·‹=íõOü—læ'€XpÏÄ~Êg~ÕgéW~ëw~§G~© (]} 8›·º‡€Íç}¸Lx{È—€Xxø~è€ñCñæ‚î×~‰7‚3hƒ¸'‚õ‡ƒ½å|'xƒ)ø‚8„geMv€ØâØ0öà GxdL¸„Nø`ò…ôu>va¶`UdFd\(b^è`¨`_HagX†i(†kØ…Ö`l‡s8†pˆ†-æbxødž€c&¦aHc€È ~ø#{x‚Øy˜Š8c†ˆ(ZXŽxˆ’('6‰T"der…Xèa›^5†,ñ!þÿ× ßa"Óá‚$Xež^9h‚Ú§nÁ×y®8²¸ Heª(…£h‹¬çƒ!ƒµ„¹˜Š H‚(dxȆ‡“Dµgv´˜¿Huˆdm¤hG¸vj;ˆ^@¡ Älœ×TôÁH:ÍØDÀ¥&pÀ&‹p'މ¨‚®tŠcrfÙÊ™5¸›SYŒxŒÙù?©nÔY„+¸œ¾ž4ÈœáIIåyÿ“¦¨žÒ¸“ãyMïYžÛ)„õ°‰eôiŒùÉw“ØyŸû©Iú©ü : ý ƒÿÉžÚòIŒú Õ™LêŸI9“ J„èÙKº úÚ€Z¡"*žJž&:~(:Ÿ*jð©”z¡Þ‰“.zžæ™¢2ú¡4   ¡ñÉ¢ê£A ¤$š£Cj¤Àø£1Ф8ª¤Dʤ3 ¢¼É Â£@Ùžú‘¥-‰¬H^ËH¥S*{÷奟¸¥™d¨R¦fZ;opensips-2.2.2/modules/seas/doc/images/image045.png000066400000000000000000001536061300170765700220340ustar00rootroot00000000000000‰PNG  IHDR™‘`øà5sRGB®Îé×CIDATx^í] €ÅÕf× –›¹tAQXP95 Q0h¢Q®D“ˆ$¿¬11‘% ¢ñW¼¢DQA°ÑxÁ( ((ëÊ* r©âµüßÌ뮮飺¦§g¦göu&Ø[ýêÕ«¯Žþúõ«ê‚Ôáƒ`F€`F€ÈA sÐf6™`F€`F€ˆ!À\–û#À0Œ#À0Œ@®"À\6W[ŽífF€`F€`.Ë}€`F€`F W`.›«-Çv3Œ#À0Œ#À0—å>À0Œ#À0Œ#«0—ÍÕ–c»F€`F€`˜Ër`F€`F€ÈU˜Ëæj˱݌#À0Œ#À0Ìe¹0Œ#À0Œ#Àä*ÌesµåØnF€`F€`æ²ÜF€`F€`ræ²¹Úrl7#À0Œ#À0ŒsYîŒ#À0Œ#À0¹ŠsÙ\m9¶›`F€`F àÀŒ#À0Œ# #PX¨ë騩©aèF€È"Ìe³>ÍÔ"ªªª6lØ *ܪU«öíÛÛ Ø±cÇý÷ßß¹sç¡C‡FÊÊÊÞ½{¯_¿¾gϞѷVX¨ÏSêóݰd=Œ#àD€¹,÷ F€HŠŠ ê¹zõê^½z¡°áÇϟ?ßµT¨6l.ù¾2Ú·oßÊ•+ ªõò£ô)S¦Œ;¶¤¤¤ººzöìÙeeeEEE”¥7S`æÚ{¥¿Ãueo]÷­³’LpÓÛð¬¨50—­5MÍeÒƒêO<bç«®Y8\!6yòd/y}¿ì¼yó6l¨Ã¡} KVDvâĉàÜ¢tPðE‹MŸ>= tV‡¼ž¬ÁS“…ÅKþ=¾ËÔ6,ÀY#PÛ`.[ÛZœëË„‰\’#GŽ4hPX\VÓ8¼Ü8p ècV¸,hô¨Q£öîÝ+˜+(xóæÍyä ¡Y…pÅÔüµï*k¸öøj[ÞKåÐe^ë 0Œ€@€¹,wF oáÃ[ø]»võë×tÄ ”kÅŠ¨pƒ ðvÞÄeË–Ñ‹r$âê‚ 6mÚÔµk×óÏ?_5¸!/^Œ¨P¨j×®ÝèÑ£éÙåË—#fàŠ+® =¾~Y*ˆÚƒH*¥ÀH”‹˜Ô-ZÀT"²¸ ÿ.ªÖ¡C‡ÒÒRü ý`·T_Áq!¼gÏ\íӧϺuëž{î9¹ TdÞ´iS D‘¯6KÈ÷|ÖYg¡‚D[Ç7sæL¹ëŒ?~Ö¬YÛ·ow§©‡)øë©Ñ#¯j^ö¦¶ÌkÓÔX-#7uʸ"Œ@^"ß!ßÅ_ b‡·áä7Í/ÄŸ nsæÌÁIyyùÚµk‰˜žwÞy_~ù%â>átÄ;tð9bu8ß¼yóõ×_U ¾—_~9!†ŒmÚ´±¡ç¥Gd¢È°ŠBfqÌ;KÁ`Rÿþý¯¹æ*IÜÔv€"€ÇE]„ì¨ ´lÙ’ÞxãŸ~ú)˜1ª0cÆ ‘’ ‰GqÂy¡—dK‚tÐpéùçŸÇ¿ Ð6(…®¦õ…¥ÃVÊ€U5âwP:¹õ“·ÕË«¾i9ÃÊ1jð`)Hɰ é(°ø—&>4!À\6MÀ²ZF ›ÀC > ß!¼˜Ä±@Ådƒ@éÚ¶m ì´iÓà°Ä%¬^‚‡•Ü·ðwÂÅŸ.Ò!‰óx@8øn¡Â÷Ù©S'œà_œ“SÖKO08@1?ùä8;áy:u*”€Ž Ÿ+ù_q wÊn¸W…cq• …´‰“&M¢K¨$ ¨îÝ»#宻úÎ;ï œ@]vÙe¸DÔŸåƒRèj:WJwúªñË-òª°VÔÈcž‘ZtZzpÂK]80$éUI*˜q*ÙSÏ‹§D¢L#ˆG¢W+|0¡#À\6tHó_¡¸…ðI&бø)Z€±u+|¢Xí„Kô’]çpգȈ›Ÿü%! º ï,RP±2ÌK^Î ûɪyˆ\8IÝ+¦Y¨‚ÂÊá°ç®®¡Ÿ>óËWI…“Ô¦‚yŠy1@ð …Aêó*‚H@Wñ((¼’bñ"žqÕõ)&Q^:„[äTAù­·ÞŠ„¹ÄŠLÄÆÈ– ˜Gv’$H§"p‚ªpبH‡ýT 9tǵvÐIœÕ‡3bÄD+ d|1ÏäìÊeCÀ·3&À\6cPsAŒ@æ@ )¦yç®GZÞDÝðèãˆ(Õù*BºòðøººQat>øàƒÃ…¯×¦øŠéq¸ËzQÙìØ€?E0ÝÚÕëT…¢.ê«jœŽØó^«Áï ‚:ü³!@Èà' ÍVàö¯ÀÁˆÕÃ@pŽ?È*Z7I‹_ýuzÉîµxÑÙ7䈈7ݽ{7ç _ÐYâÄð€b‘{î¹BPâº"“VFžyæ™°W¬q„<ù(H÷Ž;îÀK 996@Ž(°§‹k Á …‰TUÄ È´¸ja‘`lH-ÿ)e…Ä4ÄÝÌŸ’è¨öíE`.›ó»/Ç¡3rÀ$ ù§k„€â%¾—GVvÁÚÎÑ@¶”Ô=²®!³¶D›Ö+p–ß;Gqá~Œ=w±P Dÿâ\çmsÇ^±†}±iüÖ.àÅOf´¡÷$´>‚ÑñõcÛ6vTí€xSìfÞ‰EQ´Õ€8°5â’Œµ²#¼›È1J‡w–´â@\,̃œ²‹ÒqNóšÏÄYál¦MQP *Ô«v­[·ÆUg)[·nE:üÖIòi;‹… ‹e’ƒ]ã,<º²/6.fþC9|±BÆ0ÄŠ“%÷§å‹ HA†SÔ35› 6I‹1 ZI%ŠRDIKW—ITUò°Úä ]'‹5P”ášÝ(ýbÌeÓqÞ– `±š<Ð( |cäì^Â^Q6yWv«ˆ+/9ª'ðŠ+P,ùre´±>„åü®GÞö¯ÈWŒ¾¦‹³æ–´²kðê7jð ×ÉÚ\΢+…¾&Œh"bd…« ¡±X‰EŽXl/€eþ8OÛ`¹ŠÏ/Û/B¼Yø"…ƒ‹¥DIie±p³iÓ¦ðc äE¢ˆm°­ÈÄS-Ù„÷ƒ>À^8‡—¸©`ÒX³……ÿU,šÄsÑÙ¢vØëƒ>¼âU;|cʼnp|Z'G¥'}HAVPÁ©,j%GHEHtÔ¤¯5˜ÉL9¬Vâ¿"4–ø*qJ"ÈR0€ñ²?®>Á‡jùw²ˆ¹Z‘Ö™ð%[Q®&K·|¥DˆM3$ žHF¼ÙŒV0 •c,JLhˆèÙÁ(’N’nÁ4gàµ_i8Õ›k¿Œ/…JU´ùbqÅ镱Q^qÉ5]vÐ’¤-ÅõOßDW¿/).^Q–ìôµ]uúƒ…§*W&-Ë'PXøcüý¯ý˵7aï­|ìeùV'™ÈŽ{“}íYhßY'X¾›׬ TÕVyË6Z‰]>l!(®‹“Â…VFºê±­È¤Õb&ø«ÓuÑ^ÖºÖŽ¶å¦òçª .Ž_M¿¬±öëO‘a¾XqÉ4?fÙ|±†Œ¡ÊÆ|e·m¢[(lÌɇ* 6‰q‚/Ö0ÞRh8g%,QaÁ4ãŽU+ÎUJ´l0 ²¨¯k°m³ü™x>“§ '®iqâU2È"¸eç©»sÙ¤¦Ž!àÆe³Ëb‰ù¹þ«Ht²Xöe±BÌëDÖ㤼NÖëê<¶”Æ<Ìeswø "ûó(±Ø]ÄGë5mQ·Ilû^û?ß!Îsm…åw‡MgÁðàEªøî164Àg š5k'h^b¨Y)Pü=zˆ=˜i#g»U«\ÖÅÆ©UüðöÅÊêXX_S(ñN'‹ Åš’&ç´¿¯7 bS5-UÁ$”q½®tSØŸ \§Žˆ²Ð䞦X"Ç¢š‰ XTÏâ¸d”UgR;ù¼ö‘º+qŒæÀd1/ó¶ß?)£,fs[ÊW}/ÙȨL‰t†ÃRÉnVàµ}A²›ØBÈf9‘»_®" ¿ÔþåÚ¨|ôߊù£ûmY½tÏÇ>xaAå-㑲euå¶µ+è5ý—T‘Ì]] Äoóó ƒK8‘/!û+w•AìÙëF¬yàVÈD6ÚM zR(«Áð‘-ø\ñY,Ú@α^Šös­Í>ž‚O‰oáb3Zlä,»i5Áñˆ(0_º“ãm»ùGÂ;p#6 `œ[y2:CcEQ`D˜ïî)xÕÔ,Å e)ÆË{3Š!!Ñ ÿXoùE.kV‚ˆQ0eJXàg±þ ¼DFC­©L" $U…Ö¹fóeLŒý²ƒ: 2ý²Æw¥ŠÙ¼³šÊb²¯Ôà¸ûöí¯®þô®¤¤uQQ]œ ׊Š3‹¶y:‰˜Úþu&:ý²Š8Ÿ*Ü®²Œ—/ÖKF–Çw¿®‚ûesnÉléúuQ‰+ØXY±`°á3= ¶Ñ)ޝ>ßñò7®yt–œˆô-o­~èâ^$sÞ´GJÏ­d¢C¾tåÂõÅG—~·ß[ ç>÷ûØvTÝ/7è×Ó®ÛO-šÇ­] ?NŠÁ´òÁ¯pÄ¢¦`Å¿ƒ @Ú¢ T*V8}úôY·n]Ë–-“Ý,†Ï˜?=³!Ñ ëá‹ÉÉ‘R\‚á×4«c½+§w_¬Q®8*IÚ|±¦¨x—oœ˜þWÙ­)Ç×ÚÂWM1EPÝ›P²/Ö´ßÇWiÆP ÈÝ0,¶“qtîJÌeSªµ4¯—U0WÍKv"»oßWO?½äÎ;¾öÚK6,Z¿~ã®]»Ï:«Ï§ŸnëÞ_¥j¯ÇY©™4#HÒ&ï$¬B@Íe‰¾Ø|É <Õ£8—½:R³F-í÷IV;šD´õîSbßìôV¨}ôÊÁ}Ç– ‚ ™«î;¯3ÕÛFsåKÄe!%·÷¬Oògý~v·&|ã7IüÒ.M»¡µµƒËþ9Îe±4‹Óº²X3›Ld,6.žH\ÍB¤V2Æäz ‹éЪ ’6nýr÷W߯å Z7-út×WV^+·¤'Š¡±V4®댑€Üÿ =2Rw%æ²µužH¡Þ.«IUQ¤« VNO ³eew——ß³iÓ3%%-ÉÞªª®¼òæåËß\¿~¾ƒË =2¿¤ÄXJUÕ&¸rKJkòrÁZòÊ}©ý¬B‰šÅÊbªóÂÂ'ˆÎp }'糚ßßÉ*¸ìo"ã‘ú*+ŸûbðÕ&MR½²òÛ}{:˜ÎZ¤ƒ°þÝä²?œ±Èë’¬jéeËæ”#oÛn}/{xYÄÛûÏ!yg#^Í5Ïä²óÉ~™·ú²X+ÄÖæÂu_à%Äë©dªµ¶+fXìðS:v;¶õï}ºs÷þf ë¶.n¸êO‹;‚‹Vn‚Pë¦õzÓò¸öÍoj<ãUoýâƒO¿xmãöÃÕ;¢EƒÇÞ¨¾ñ‚ñ³]û¾þö»ÆõÛÿí÷[wî[»i线´¨·éè•ׄ%FîÊ>`yݘÇ/Ù…lFìZêw¬¥YøkÿïÜ#"uWâxÙæ1[p5²Çù§°S¾äzn£}øÒ*ÙÉ“jÙ˜øë}÷Ý$•å$‹”"¸¦‹°„oœG¯´ºK3:–´y…ÆÒ%ßc½öŽÖªwéŠHs³¾[; "[öVTbd)†µÐ¼µ?óÛ+?ÿ Jlmݱ ®Ê)IôAq b"—пù呚†¡u¨EC‰åá‘>©´6Ë[5 v,ð2#(üSfÀö 3h4.cý#­ $ÒmXtèÏÎï ";ó_«ÿñâú…¯~ðàKU¾ðV¯N­žØÞ Þ[¾Ø_±ºzÚ‚×¾Øû5¿þæ»»+ÖU¼öÑ–Ï÷¿ùÑ®g^ûøÑÊwÉö¥o~|ûÓëf<[õ§oV}´«ã‘M/ÔqÔ©P%جO+kÍ^VhaŸQY+N7Sk,á’ÂaEhl|¡˜å‹a» ‘¾VàAú:°f没ãŒ6'«×ŸI³X¢›+W®Ä‹¯4 ¨Á•KKÛÍž-ÁuY,1ѹsŸ]°`‰™KsãX5‹%ö©`±tIÍbmJ¼-w¶("àÅh1úÝÛÑ"²àp[´"(?^³|ְΕw”}óÅâv ›w8ÔF@î2a%qÈYv~`ìÃÚØÉÑç²r-˜ÎFqŒY#Ë<xÑŠ.㽿z—µ¸Šø¦±¸+ÎWY¬$i2aS¢ƒN8²e³ðȾ¿®Ó8;¬S°åó¯f.z„ÕZìerÁ/öÄÖxlݵ׶ÀkÃg{ìbÑÕÞøô•ªØ75ºÕü½¬cû —Y\ü¿Òb,A@W›ÙV†Yõ2Î Ž+JÕIH1ð•øtĺ sÙˆ5H”kVT/!–ÀALc´¡±ø &N¼}ÇŽÏåÐØNbQçñC'ßû'œÏ™óôرÓLyEyNSÅ Ù sVYXíúõ’”s‰-Ü{Æ\@ŒŠÌ àl£”èë#GdAÝJºô7­—_ö :ž.[As·¾µúíçbß—ÂÑgäØœà²h¦Ü›tk›Å‰,Ök— ‹P^AkÊÊ¡±.,6.VpbÇØ·ÍvíŽ1T‹¹ìþú»åoo¦Àº@×ͳ¥f.ÁÁÌPÝx¾çÞ0¾×»Óá ël:•Iya%ÿ±‹ 6=̆/V6)^žÂ×/È Nd± ‘Ê‘èŽÌe#Ñ ¹i„/‹• «ó\frÀæg­Óµk‚eÖ¬ùÍ›Ÿ1oÞD„À€'Â;+E ¨ªúðÖ[ÿ9bÄdüÊÊfãOºŠÐ‚[o}Ø$²uî¸cAYÙ=óæ½ˆKsæ,,+»âßêj|\±fÎœŠ²²ûËÊÀ¿ÕÕŸ‘Kµ¢âÕxÊ?Êʬ¬\‹ßˆS Î1âO+]³®.X[‚—ŒSL–ÌÍR›¬¶yõÊ×G÷ã´ƒÆÞPÒ=ö(qü릱å}›¿·¤„Õö2¸Ux]ú¼zÃÎUë*æUÜr äO9î7‹75k]âÔÍ4U“]³Q²Žˆçéï‹•BB Zéé‹%ÞirMƒäoìM—­¸Z§qì¨Sç¸ö-„óR¼÷ÿhÛÁû„ß×p.[ÞߘK•²˜Ëyâ<zèR»õ-gªÿf[¤ÖôךÁ‰îX_¬Á€ãÿ1ª L¼9b}…¹lÄ$'ÍQZ×™¶º°X¢¡gžyrß¾]£F• <¡¢âåĘטdeåë;\±bÝüù·<øàoßygSçÎ?Z½ûÀÀf^;ƒõGÇôëw\ëÖØ=çÀèÑg”—ÿ³¼üaüâ›|!åôòòGâ¿yf)5C‡öÚ¹swyùcøÍ›W¹rå»·ÜréìÙã,X>lØ”ŠŠUÊ@[Ì€ðàÚ­:XV<ädç¨UF 2tpaÈþš·-÷à #þ8ÛÖ4ÿ7lÝ3ód³åxÙƒk$_Ú»móço(jØð›î*«Ü4â3QDd«ïjØ_Þa:ñ‘j±>36Àð+£5]jRD‹/š¡F*ÈuJ'n,Ö•=µ†Å Þ¬Áe§—ŠàR¸¶k¸¾“¢È'*Y'ͲÓ3~Å"^Â^¥¸„uˆ?Lÿ¯‘×äÖ¢f îaÃÁG ´¨,B˜è«‹µD¤£Èô沑iŠœ4$k{×/GÄnqqã©S.£²|ùÚaî<øgØÍ@øq««· 8bW\q.‹Š1b þœ:õ!üYZzäС}„’þý»zò€ È1IIy¬"EE‡ØR¨”fÍP:N®¿þ‚ÒÒ¶cƜٷoG¤ V¾cÇ—&Uøe½X¬-ÌÀ+¸Ö³s`slÉGÆpm *]ÙÛÞ©9oä#ü«W·è”KÆüß3ëÏÐsëøç¯F}½k‡°nTqÄb ¤Ÿ|é˜^ý»Š_û.=›·.‰rŶ‰š.\¸0''ã¼5Ú gÄ·s¥—÷‰,Öä‚$*{=MV*£DÝâôM°XC¡¥9ñª¤°àõ÷Œ€nÇ´úõˆžmšb+eSOAÁâ·>¼â-}\»Y*Ù&YhÒj²)váð&õåëo¿k󞸠éÇM`¢V¼e4ù¹ÚåAqt$ˆ,Ë + ÝJ*Ôjƒ(u9æ²Qj³Eöºú_Q{ä€#ÅK –>`@M›žž<ù ˜åË×Á KnW¼âöÙåtµC‡6¤¼aÃØ°`A%6á’Š#)Ámq¨‚DŠ¢\$ûõë$hëEL¢+V`±KŠÛ(ü²¶§…ë"yo.±X¹šw¾Å0YºúáÊÊš¯÷ÑŸm)=ÿº)7>»þÔK­Úß0>ýe[àe‹—õZû•1²®F¢á¨ƒÎt6:#×-¢@¼ú'3%¦ñwœ’‡ù–Ü$®n,VDŽºÄ …&¡+(X´jÓï#2-v`¿­ëFôÖ«ÍkäŠeÛ{$²FbŸ’"ËÙ'´:ìØãÊw·Ë ¼$ÂëÃ`‚K^[“Ū™ñ fD•]TÞ þ–¿6:=„,a.µÉE{l¤VTÁîjA¶J¡Ã欭SRÒjÊ”qK–Ì>| ÍÔ©’ðsϽJéK—¾QQ± ¿eËb àØ°a³¤“Òl‹±dS>fÁ=йs[ʼ~ýÇE–ý¬1ªX¢sQ—MÞëÏ\ìyn³ÅZщÛZEÛaÙÍkWÊVÑþè3/ú -ŽŒÝuÅÕ{rE­¾IÙ3ã=뱄él†°2XÙ |¦ÇT0@;‹…Í,6&%IšA¡‚É%xj…BRlÅÎ]òî’5›â»ÄŽÝKÊ.éÕ­¤™<` À%9«ä„pˆ¦ kÕ¸. ÃJ¯³Ohyêñ±ýFÖmÚõÂ[Û„W¸]MãEŒ­å͵ÓP#Ã0Eºj²j9Ö!ÁYkQwƒ"íNKÈpQÇ\6B‘ƒ¦¨Ý±¾NYÔØ&c‘9„Æ Ž8`@÷ùóË—,™Ñ·oBiÁ‚ň.€@|§­ØÑ¶móZÃ;{Ùeg®_ÿüúôÁ·ˆœ~M™P ȉw:=ÍÂ’b–Ú]»°©Š¶Ú³o8,iS׿`×ÈS“E0ƒkýf¿_þñ_ÕËÏ9-<õ‡ÆÛ¢ú ä«¢šÎ,ŠKG@aU ®YüËt6ÃØ}“;ó͹ œq«ÜY¬IdU¾X÷ÐX T•X¬p{ÊÁ6.øïÕÍXô&>@@á3?Üyxߣ$U‰ ¼d]±cWͼ‡öéØ|Ìà£N=ªýá _~kë/m˜ÿÊ'²‡Õ©Ö ¹q!›¤ØPV¸rmÞeËˬXàEz]ýµî~Å1—õCˆ¯{" ;Sç:NYYFvIÆXã²eoš[qb`´/¼pçðáÉ"ißYJ¨)--A€¬ø7R2TÛ^^!©š‰aÆÕ¸38^+ªš•#4VŽÊ¹ –lT8Y~Ô¦xæ²Qk‘²ÇÕ뤭T#YXND–d~S¦<@2nߨoòäËânׯÛ,X°[Æ’0öØš;÷ùú±ï\»x[i®Äˆ‚qŒï)+²mÂf#qÙ2ãÛƒ]»â³Ô6Š,{jí•rTÓDk‹O°Ù™CÝ£Ö™:÷ƒ0€_ö§Sgß²pÕŸm¾{ˆWÍ{sqÅ‚[Ë6¿³vÚ‹ë9¡'½ߺ± ésÛ/–Ž׎ªœ7çÝW+q ¹äK8G Òs7´ÀVAa|d™×ÿÀ bæ›t2Ib±å2H.ñEùáhúb‰ zØ" lA&Ç+8½KÓ=j¨Ú³¿æ¾ß]»q;™wt«F‰/ñ…Õ‹5\¨&Ä2÷5 ,Ö¤¿&7{FM L,bA ²U‰¼ÜÎb-I¦ 莨IØ:ÈzǰÀ\6j-’[öøú\Q/‹%°eÁÈ‘¿5·#ˆ‘¼}û¾~â‰Ê¾}¿îº’æAƒzŒw>É{[¿~×àÛƒOêÔéˆâb쥓>üTXºt-¾}笱ôiÓŒÁ¥KßZ½úýÙ³ÿ#¡ïâÁ]´è5ï,ØðâÅoAxÚ´‘%%M½÷1H*RÖæÍ•º¹Õ+j©µir†«¶ôÄ>g]:æØzâ߉³æ}\÷6í:œwõu—þzJÉ1¥¢,œ÷>cè-O.{tÓñC–®}àÒiç”/á)röpmΰ¶yÚ¢ê`ç»ZÚ§#PíñÓÿe‘P‰ö‰SÓIi¸5eK$-.iz3U,Vˆ›ŽO“[.RÓkz\»bá•éï#/€]´ ¨î¡±uI–›ûHk¿(£Q[À«É×A¾XI¡l]³ùb 8¨ ¦t ÙY+l7J‘X¾KÄEüÀ “ˆ@ßM`.±É%sœ.X͵«ôwÑ¢iS§Ž{è¡gÇ¿µ¢b9ö(¸üò?¶k×ráÂrrÊ⇨ƒéÓÇOžü#Bnùò·/~}êÔ+hYúÝvÛÕ}ûbÈî]»wïñ-pñŧ ûÒØ±3î¹ç¹±cÏðßqG…´q¬áT0 tîÜÿVT¼>qâ?—/÷½b„Ó%§¬"xÀ+jV&¬²7×F‚s©[ÔZ[3L¹‚×´Y±œ±Ý±¥øÙƒiΧ\Î>Ì9§{ g¯yÃôP 3“žš ÌÃk8>‰Î)}±6w¬Ó£)x¤ …'Û"¶í‡Lã¬ñ³]û`Þç{¿1, XDËÔ,9PÊ%HµÔÚlËä–g5ÜÒ‰,–üÐ2€âÜ4T ’-pH`±qýQ›ç 0îÓQ³Œí‰,ÉTS³Æ´Pt!ßAv)«ûÊ)u°ð ŸK øAñ/œ””´,*¢È{ö¸L,@k¿šcQÕÕÛZ´hX\ÜЖ½ªê#è,)iîp!”º¬ìÑòò§PÞ¢E×ÚmÇŽÝÛ¶}WU_  {lNh矲˜\}¯ó˜|aaþãå:ï¼ó"ÛUjƒa"°ïÉj»?¯6T?/ëxaIlг8himkyO.Ú³™R&Ü‹äÆa¹0]âbI ö$)ûbm—d…m“²Ú=—ñÌÖÿðׄs«{ÈÁ·?½N^žEüæÂ®êúHåûo}¼[\3ø˜#[4ÀgiÿþâFÓcjhýýÇÙÿÖöÂ[;,ãMú-ü¨ ÐU!m‹5d%g´Ãkâe!à3ýÄ&Úf9Fq&Œuêüêt|>3Bw%ö˦uxÖå:üÕæˆ,Î91vnz^cçp¾Æ÷((‰¬ËÝ­—9Â$²v°U¸cM"›pé%%Å’ZÛ/y'XFPØÒÒVÅÅøÐ‹ÚÁ,®Ú"b½r¹FÊ 6\úR×qáG‘þDB>9M3P׎È{d`|:?>B„M¢Vf€E« ¢å[5_ƒÝ“x FhJÃyéNdEü¨äÂ4J©SpxÓ¢ çtn¯s™á¹¸4ò”ö ²ÿ]ûé[›wË.[úˆWãú‡ÚˆlˆpÍÄŽ–MêIòk—«)¹H…[8Á$+” ‚0ÜÅv_¬þîÖm"Ú&‚ÂómF:d 3$UsÙ¤àba›ÓQQ'UõrXÊe¯ÚI‚áÙ^Ê“6gÔ©Hwª²½Ê—Y¦ë>6{4ÃaÕ¹|kÁÞ¾wù±ì‰kAð‘y\Y¬ ^j¾†7¡j—ˆ(_¸K/Íq´_Í;‚ Ð-(0 öêÿ7ß=¼øÝ¶~9²‡+N?fH÷6CºµG¶Ýá ÿµâÃçÖn¥÷öøü>|põè#^Šx^§3»~x£ÃŽkÛðÌ.-~Ô¿A}Lë†Ã{·>ã¸ææ +ÖVæÄqŽkl°ñr¢çΈ+ÀÀ,Ã&c²X#úÖ %dd+Â|3ßQA¬:»­KŒq˜A4ƒæ¡‘õ³Û±;»/61¢ Æ{ A_lÜòqÂfä‚ô ^\3c ¤R¬î­šÔÝòÅ×$Ûð°ƒÛŽx³:ÛwóÙ—ß$„9Äþ0blŠÝÂ!\|±q“‰9ªƒ ,s©zV>q&UÛ€@¢¤.A"fÄÌhɸè/6‰Ô]‰ý²AÇçÓÝ5HÉþNÙ«öË:s%ëyµùVÉêê‹u&NܪªÍ‹¿M­}ÿýÿÅŸ‰‹½¼uÙâ | µygm.dîmQG /¾¹ˆŒ!àÕÛ8Ì ãÐzÑ/;‰#RìðAú,ð2_ËÛ|–¦EDAÜ+gßú%ˆ¬á:ÝóÍ÷ˆ(x{óžÏvƒÈJV™+ëñL–ñ†SY<‚Õp²¦k³-"ºŒ">Ãá‹•D b…ÞàY$sÙ(µFŽÙbc¥ ÎJ—Ä¿¾ìÖ‹ÅÚÒ]õhæb>Á¬EE‡Üwß•ë×—¯_?å–[~àÆË½4Øx³l˜f­È’c=£š{paþå µ°G¶ÊX ½â6ÑË~Dâ³ôÆ\⸱: 1ñ^]¦›‚eºEªLw¯é4•˜©ùŽ^ o5|™óMà¦1‹L«Ì ‰CK¶oô‰©Îd‰(K– g´c‘T«æ¦GZrÜz±Xa¨Ñ1LÂ+ž¢Öa˜ËF­Er˧“U¦_^îX94Ö&£«vÐ ÿ«>:}¥Bk”mŒŒ–”4ÃJ¯ÒÒ–ñNZIž]§¬—ÃՕκ:’é1€H#À‘¦ù„@¤»Zm3Î Ô´xª``êd±–U¸o¤ø§ ¤DW—Mü¨¥Ñ ˆq†ŒÄA-ªÁ#MBg±¤Ec³-h6<¤&%=¶êÄknRj"‹%–oQa?_¬A¤%mq½†ñ€¥®È\6J­‘c¶È!ž2kT¸] ¼\ý£N‚kã¸^ ¼|Y¬3ÆÀ©J-c“·¸²[›Už¼9Ñé+W9ÇzF-4÷Ô–…XÜÁ¿ü@@Ñ9Ì ó£Ûˆó”œ¬Æ«x‰ÅJ.XáÇ4¸›Iã,Ÿ¨é©Lt™J.UÓÏ›À#‰ ’ë—°ð±Ê,Óð`š,›dô¥Iuã MzHj=X¬)•+1TY9qO¢¶V鎈CÌ+sb‹Å ]™ïþ%2—õLj%<PGÁÚ´N+(¯Âë¼äÅb]_è»F©*|±6·¨/OõõË:i±Ó£ìå‹•Y>÷Áè"@{aâ€#…ù@t{[í³,ÁiWƒ£Ùi¢DÔ(W¢3Ò¤¶%3I¡ÃèµH¦ù–_Å2Éi*È¡E|%v+1`Ë4…„Ë4ãšÞcóªÃ+è°i‹¿6áaÀ¤¨"0CöÅÊO‚²SíLƵžÈ\6j-’CööÅ‹Õ 'põ›*‚|©ª+ÁuÒVžêt¸*¨¶+‹U‡Xm9Ô+j¯©ùô’½6×åäÃù¶¡Qlp<±$Iz«n½M·|“6_¬ý½¼ô–\’t}õoê4ݱ‰ oüÝfÙ㤴Lÿ®à¸D{åͶ¤xËNS,!Z ÎI%·¨«*ÖÀDµÄÉmAÂomqâõ´jŒ4E„rŠxâ©t8£iE¢“»^ò cpº„Õ± jÞì¥MÎe;wzpJœW…ÙN¬äºç`ר}&G™ÿmÛ\½{׎([Û¨çzmWûúuj,ÞªÓø„^ï2 Íky…ÃRb~V+…ÝJañbLÂgÏçytÕæ1nN“ì&D#x±X#hÁà`±ñÊX5òf±²ŒÀÇ`¹¢R„£©ÓàÄFk'†ÆJ¾Øù(t Óæ²Qj³E/›Tä€ì¦µeô"”®¡± ß§ût $ðuÊúzÜÚU'ûes¬Ó+ÌíѼ0Ý„ |ôÏW”_?¾{qø=t÷­HT«Cºµxlóê÷ªÒmaèÏŸ™5‘ydìÜtp&FÄ=›²ÃRø"eZ¦òÅ’™?š Å©¡j—ð¶ÚÝ¥ ѹDuc¾Ø1“ %²/VrÖúøb‰¿JÔ¾X÷ÐX²ÕŠ( SäHö+沑l–Ü0Ê+^VUs5'õ´8)£:Å—ƒú†À VmHVWïܱcwâÞ^lÛ+½b dG©§ØŒ‹÷ìÙ/û_/ºè„¾}ÛÞ­OŸÛ\ã \I¶Wd‚©Í¹ŽQ{ îØ¤0oÛ-.›†½ÀVþ¯òGC¢ˆ T$REªì”àè%¥¼ÐÊ¢‰‚å"‡$ñAÁädòjúh˜R“¨Ùy§ÁìâJd_¬©Êæ>Z±:Jpk“žÆCc…‘V$bm2K£ªÔ"—Q­ÀGøbÝYl¢^+Ök°^ÉÃÉKè™áÑé*Ð >] c@55ÿKü2­X †‹²Ë–þ¬SUµ©sç+¨ŒE‹þ8tèÉfzuié8/+ûÇ”)—;t*¼¿¢ Q´ ¼6œJì)••ï xÛ¢Eã‡=^¹ƒ\¨\®ºD7d\°²PX‹ôºÅžwÞyº-ÇriF€†ÆÆ/ñÜ’Þ㨆ÆÍäg“&_ÓEa/=[ñÚ«F¼ìi§Ÿuò© ¼ÿ«}OΛûߟkÖ¼þ<}Ȱ÷ßY?æ×ãò?ýá0RxÎÃîØ '¢ˆ ïV½øÌ¢5+W —.yY‡Ž¥$üÈýs6´‰Î/½rìŽmÛfNŸúÌ¿üè§ã&\C›#ð4h =¸ô伇H´Óq]Ï1ríë«IÕ¼g–ýé8ŽndxvÔ\–‡[:ÀwÕI£éW3_Œ_5•* â%X¬çI®GãfTk\¡ñOÂUó-»QžGDyÕð¡’ùžjL#.Ö2Râå–m–‰'ÊÜWD˜6 P¨tGD­¨˜%,'Iq±rc˜Ù`¯†i±Œ??¥£sWb¿lÆfþäå”Ux%VüëÒ¥oÃ;묎_ùúYm^þZ9|ÖÅ/[Yù.ˆlÜ8M°¿k‹Žu†Æ:…µjwþuž|®X&Ö´þ|±W§Êßà!C¿Ø¹óoÓÊñûjÏþÛ´)“¯û׿?Xþ×™?¿þü¹û‹]HÿbÇü Kú;tìÔë¤~øQ®W_®ܳ3ˆì¬¹ó‘wã»ïàÏ·^_MW‡M¥à7óÖ©+–.Fx.”üóÞY·üßu²‘j=Çv,-=®+éy÷íµ>¿/Y¨zù¥çÒ„ª&‘Íç^ݺï×¥×îqê&(é‹M°œµvª*¼  ïñ-}ä…5þ%ï¦ð°JåC%iÙ=»äÀLôźhv#© ªâú©™¿J¾X»Z$ˆ¬éKuF˜²Tkf!ÿ3]•}±²P„: sÙ5F®™ât‚:]Œ6Š&W1v©ªªzӦψAÐ…NæÌy¦¬ì¡øonuu,PuΜçÊʦ¥àWQ±º¬l^ü÷heåÛÕÕÛËÊ+(=bÄ••ë¿Å㸫WXVöÄøñs ÆŒ1 ëºL=ox;Yvÿý+ÊÊ–•-Šë«¬¬"þûweåû2Í­¬Ü0~ü“ñßSøUV"¬&Lº¢âݲ²èWYùauõçee/üaĈÇñ§[L0J¶¹–å”\ëµØ^5Û¾QzYw|8×/ªµi³f$Oñ¯[6Wß=­®}m%þ<òȒɘŠûΛ7/>óœ¡ÇÄ}±8zŸÜâG¹.9g G^v­?váü9ã¶©d€¼"­ýÑ&üòú+ÆN =`¢ÈNb¾z Ó¨aCʸñ½wÀ_ÿýßU=OêKöøV6˜@-î³Q¯ºù^Ý$”Æ{qƒ‘¡tc±±úΰ$ è|ø%§´ÿíÅÝnùщ·\ÚãÚs;|Lü×áê3Ž¹æœŽÙõæKºß¶¡Åš¥$î¢eF2¾K–ew³-3À 14Ö-.Ö54ÖůɽÁ¦£cÀ\6ê#9Âö ú¥^ƒ%_µ‰1¿}ûößqÇ¿$ngx4GP^þxyù|üöíû £G÷//_ÿ=\;th;÷”—?…ß¼y˧N}ºkWÄ*ÔY°`åÀS7Ò^Ò«×ûõë0s楋ýlÁ‚׆ ûÛ¼y+ãb†ïN–uêtx¿~íûõk×ßyçÎ}åå/à·gÏ×"wΜWœÝ¡CÓ™3/˜9óüaÃ:xÏœ9+EE†=&žñ¿øÍ›·nêÔ—»v½É]° jàÀ‡vìØçñÍ3 ‡ÓGá^À¦%" èì‘ ±XĘþC?ejj6äãöì߇;F 8ëŽ[÷ïß×÷´½úô“UÉò”^ùü³”žJ) âŒóßO-@T€y—3òyÎ0¤Iû- Ð$ô˜î!(sÍuÝNìùè¿_xµjÓàs†jÖ7)1´ÙÍ‘²QÍ Sƒ^<5ÅÂvË¥*ÇËZnÕx·XZµýÑåÕ•k?¥Ê¾´öÓ¿¿¸¿{^ú¿¿ýçý^Úøõ·ßËnט긧Öî.•é²!dùb|º¦áPRt*ÉãiÔÍt€Ê`4`÷Å KŒ:š¾aª¿å[5Èu<ÙŸTÕÛ8µX/Ùo:{Afç¨uæ²Qk‘²ÇòDºÑ/׌ÚÝ?ü¬ÿ¨_ÿÂY³þOJx/_Tt¨„B¬”¢¢ƒSŒwôÍšÕ§ômÛ¾œ>}ÔÈ‘'7ˆRV¬žÔšÕ«?;6×§OŒ¤vèPL2£FÝW\\4tèq °” ‹ùY›5‹…Ń_Â#;vìø{Ø0ø®bf÷é#ÐcÇþ«¢Bìp@dܶmÏôéƒGŽvÀ¥`±ÂÕ-À§9‚@ú6 b1>?ñâ0ö‚¾ Þá÷<9ææÄqKÙ¤K†F@êYçX;ÔÚäIå‹ÏQ–W—-ELm, ÷# ·úcÛZË*Ó$gŠŽq[úƒáp£t8},)ñ­i#$Ad9X6ó#ÏäWÏ3(™- V¦¬¦×6ñ¿LFwîùƬ‹åñ%Åîøê¿omkÛ¬nœ:ÕÆdkL ¡‰d7.Gƒ‰þã”'ÕàÈ&‡&øŒÉCÄ”3 ¨ —ÏL %#Igø_ ½‚á7¿,/Ï/˜J¼t5ŠsÙ(¶JŽØä/ëŒ5j†ÐØË.4{öÏãÛôP.qÈ‘ ”HZpåXÒWœç» ‘»•ŦN}†r‚¹"cIIrÄŽwªTº«rr”ŠBÜu—qÿ.)iLy‹‹ ¾{ÿý¯'Ö… ëf&oãà ½àxÙéõf ×l›úéÚÓ@X[+ƒ¯ôÓ«é’q?ßã(åïÍëeÒÙÕ¯,rj¯Ÿ­zœò¸TñT,bGë6mêп^zÙËkÖã×ëä>”WB•3EG0àØNÐSý,h ²=²ý:[" ,ÖŒ÷”]˜´[–Ë´ Jãc®Ÿ×¢ªd7ê{[÷&²XóÓ_& MpÐÆs¢ƒnZúìT¸þ¡õl·‚=›¼“ü¡M¨¢°Ï¤˜öÐX‹%Ç*QZÉ{íÇbMJîÍbI«éÔ¡e.›­ñ™åªé—;kç£mÛ—–¶3æ¬E‹~ëæÓ Ò"‘^r¹"Ù™h¤ Ц\,±¨è¼lýú²éÓ`–îÔ#|¥t)–±ºzׂëèƒlQºÅãÄKs“š×bRÂkj’C›o5®)–%àÒ¶=ß¾øö¢ƒT(ù>ü©&u5ˆ²%"|±‚K“’‚3J7¬wPB€é%âéô×Õ'HB °|±¢Þ±Frg±Tm«tÊâÆb-!“»ZA=ï,sÙ¬ Ïü(ÔÉe]·0âÌ`Pª»Ÿ0hÖ{ÑŸ.ÞÖø%Ò){jm¥BòÔº8t~ àÞ±c•_iiéáq©pñ )Q/;ÅÇ[N˜m$ïÛ‡÷V c‡ßת»YqQ}/gm~ô–ZW Ag/ ?pVF“nmò!°>öˆ’ñÖÑ‹íÚ¹cóÇÕÅÍ‹Ënžòð“‹„ƒöÃ6¸ÊSâW#%äüšܦÑÁÂÛ¶QáÑÍ:ºéÁmÂT“µJï\\Yl|¬‹H„˜2é&‹¥~Íã9GÇwÌ–¹—“Åڡ͹h\Ehl¿~Fà©Äh]…e®i10y¡p#A°[‹à®[·Ùã{ ¢D‘ËæKvR^Èf»[:1ÑùH ²È'Qhq¶!9mQ¯¿P¼‰6¨«çò¾Y3hrwõ³îܾíÁ{fÓ¥!ç ½oî¼Þñxƒ† b¡£N¿ì×_íCâÙç›Î.|rÁC÷Î!I\šÿðÜE±Wõò½DXeqYÓ=éöË¢-„aì”M®Og\Úr™šþH"fÂKk8M/©áª$2*\ªq‡¤Å[…;2Nf-þVPpb»ÆÛ402š,öWçU÷Ѓ>ýüëýß֜ѥù¸3JJšÕ•ܵ1:6>ç„⯿=ðé—ßÓªè§ý[Ô¾>ùAo]+¤qÑÁ ³­bøbëÔ9¢É!cNmÖ²Ñ![v·ÿ»;5øù€â£3á¢C O=ªÞU}ŸÙ¹~«†Ñè Kz6ìݾõ›Ô;è¬ÎEú íRtL Ð\ÓLnÓ.­éÛþÐzSÍÃæ‹%`â8XNX.qÁÁb#dÀ\6ãC3 ”®†ŸUâmòÚ/»ÿRvÄb;‚DëôÂzEÐÊ~S™ƒ ˆc‰¥¥-ÅßwÝUépÛœ¯‚>ºÄ”–6ïÛ÷HS›K¨Ci)V• (HÐ3øÁ-À@È»fÌŸ®S{j"è,ª oJ(>E¸]€ÿ]²~V¡.Ò»o¿õw7L:ÿÂáVYÖ]ÌpßÞöçòï›CXSÕµ[7ÐÙž½{SJI»v”ã¹g½ñúê—+#±ÿ AWŽ1\³¿úÙØ³ô›ò»² |lÇNpñÚü²_íÛ‹ÙNaŒŽËyd¼Ôµ;žS­@µ‹Õ$²µ§»F±¦Ä+ɨìL5¹Y¼×—¼¡’ÏUd0¼‘¤Ð jñ|äB-8êð"ãM>q»‚‚ÒVE§wm±ý˯¿óù;[÷¯Ø¸û­wvHáŠ-3ð°w|Óc[Õ›»bÛŸì{ﳯ_~÷ äع ù_—nسt#bpc”üwãÞ—7ÆGqAˆì…=š¬ÿdÿ‹ïîݰã»ÕýÔØ]§Î™Ç5èÒòPÓ¦ñÁŸUs((i:‡\Ы]ÝW?ÜÿÍw1ÿË–=ß?_…­~bǶ=5Ÿî®Äô“ݾþ®ÎŠMßnü\z]iy¤ãõ7}°ÛMtÉèÈKp™ÝHõ沑jŽÜ2ÆËëiआ2Ù•³¸ªn— ‘£dÄl$ÒÒ9{öH’[°à9sèse00gÎr)*€Db¹â‰6m¸KùÉOº“ÜŽ˜˜b)ÕÕ±9 ÇìÙç8 Œ\Žø _¬ð1Û¼Ô¹ÕIØZ|!Ϻ—4«›’wöó;ž¦â·¿ž(`]ùÊòmš7=¬€~]iwÓ “pµcÇNä+…üô?Çv“Å1}ê”÷ãA±\8|ñóÏýdä\}ðÞ9øF×ÿœW¿^ì½;~#/ýÓ8m½oÎ¬Š§ž0pqµü/Ó¯7ClQ.hôïo™zZÿ”ëîÛnVͽÿ˜zï̘{˜Ž9wß=°vÿ¹†åÏøø‘æ¤X,GÊfq~Öx`€x•/ŸÇ­rKW+;Pá<þÈFƒo~Æq±ßð^­Ž-ê‰/ë Ú4-Û…·Õà¸n‹yX›7:”ôàèÔ²^ö W}°‡LDÚ»Û¾ùæ»ØÀ‡ V¦¼1åfP¥îÜ<õõÍ_Vlþò»5aëÉ::Ö¯wh!îÛ[¿Ý±÷{¤ÝüÿnüúµO¾}zݾ•›¾þdw͆]ßúEìR‡æ0ˆ«'F l(ܸ+vÉbòñ?È¥mÏeO, æŽc`eø|å§€,v GÑÌe£Ô9f‹üêÜIÎì){÷Æ'»wã‘Ô&@m§M»”$—.­Z½zãìÙ/IØØ2’B<›Æ4ìÜi<ûÆå ÉÑ£{õíÛž4ŒûøˆàóƒÏêÔ©9m2Ю]ººhŠûhñâ ŽÀ¡ªë¸q½ ¹bm­UgíÚ­ø[nßR¡â_F;¿À0ãéÙÛK&$dO$Á|šSÈt¶IÝàÞYx@‡œ;ôö»g~ùÍõï¦?N¡Û1ä…ä ÿ]Ö©´´¤]Éœ|èÑùw̘}ôÑÎ<{ÎáÎNì ‹"V½¹þí › Ò%œàÏ­_ìÅ%ü í´D®_L¼^„ì0Õ™¢£B³PûSñм€]t&²95zˆÄZ±fP*‘苵‰™ŽY¢¦"&Á$džcöó½ß~úù7ÀoËç_ý-\›ä5 ݸí«Í;ö¿³¯ŒðK@CsiëX ìûฒO¬ÞùÊû»W}ô•é0¶2 r‰@‚âïØóݾo1ó<ÿÙ´ë[Hƒãx,è–L±qÇw»¾ŠQäO¾¬Y^›KìÚkÇî2­Ç¢c‰Åâÿ=Z¼q‡Id’ÝXl˜ø!yªÝX¬i]DûsÙˆ6L.˜%8¥:X¶¦ªê£ŠŠ•×\3GTjÔ¨ÛçÌy¾²{ÈÁ VêÅŸ4|xoÈ{ß=÷,;v€È{ÇÏÓ**Þ(/§íiëÜyçKUUŸVT¬5ë¿”2eʳH1Wz4oÞe“'¦K ¼ùÎ;ŸÝu×E xôèîãÆŒK³f½òÄo ÔéØ/¶¼|©©­²ª*ö½1p_ì;mÚÆ͛7ï-| aذGá‘>ýt¢ÅqÃÞ//_n¶ºªjGEņY³Þ0U½‚“Ñ"Mö¿:r¡#°€Î FÛø°BüÈã‘ù_Q½"üPnqq1ˆ#ö4pµÁëòâ~)Z–3€¶h¢¤ˆ¬FòHz´ÙÑNÛȃ OªIë€Â®ß²¿ª-û–oüòUˆØ‘¨s‚ê]ß>¸lË+ìAìstÃKz·8¡¤ÕYh>›ÑÖ©>jä_ûè‹ïKð‚ 6j’Eƒ)Æ’[aµœ¾qŠ¥ ÙÍ_ÖPÁÍbQ°‚ËÆ$ Ç®å6ݸ«†\³]ZÇ|ÀdÛÑŽ±åû¸ºX~O_¬ä•¥râ9¯5-’©®¡0½íž¬ö‚ø f² ÕvùÂØkR¼9Ŧ­Â‰({] ™:ÑÆç †WUõ ‡!Æ>ÑKêôËÊ)FÞx#Ù E`À¶m»ã:ñ"Én|UÕgØ®Kl+i U š÷íû6]p ¤¤‘ù)›ŒÍY u!1ô¾ØØ¥ÂÂØ»Z¯{0¿Í­IÇŽÝßH¡l¹U±¶¡åBK.®€êǃ+‹íL#å7¬0‚_M÷ªåæŒó>óźáN^XÙ+ÉK+èܦþûÆ>s3ùf°Xá‹m×,¶âªz'¢ˆNÚ>G5ìut£uïYùáÞ#›za¯Ø§ÿò Çd~=¤ þ;í?Æ·Ä,5õ@`âàX®§ßøâýíß’âKNlÔ¦É!Ÿ|þíc¯ƒ+Çtÿ¯Sçâ Z7>øÓ/¾{ì}Hýa·"ü‰¸‚åÕßZB&éìÐô sñé‡_ûjçWšÕ+ìuÄÁÏ¿ç®Ie†4©®›/ÖÎW‰\Û{€¡pÂÉ1’»ûe³8Ts½hbж5^òê~Ùq«¢õ¼TZÚª¤¤iâ—Ä/gˆ­ÍÙCß5(-möiÒÜ%XÚ%]r­šEš±¿lii3ü„;VB-k#ܾlÛ—çzoaû äx0-üŠe=2„­èvܱLd£0he¯ªp[±›&‘ÞDr‹ ǪAÆG$£yÙbwfPR6íú¦zgìݽ¨ØA—÷kÙ¯S“…¯ïXúÞ <áà­SçØ1l•n ýü‘¼¥VäÀacs1‰ÈJA&µ¾Rƒ¢#§ÿCxíÜ{0îuD,&á„V¿ 0‰¬©ÔÐ$;_Åc€hpË›ØlÌ8 ݹl„#×LQÐ5A+mL×I]\5»²d/IgzRÙüÄ®ºíd±”"þuž¨Y¬ðÝæZï`{½•©UýC ñ‘|’:ÀSf±Ldsw8ZÜPê‚/šïôé¿n_ÞŠ³Fºj#š„‰Ì}Ím¼ä2 NëØß³]þî}þ­ñÞzoHîþ*öŠ¿´U=ƒy“\œ­žÚ¡¾idS$ž½}O,cq|yÇ0Ô0‰ïÇŸo(3¬•¬ä@z×SÕñðƒ›Ö-lÓ¸ðƒ]ˆ[0„ Öì`±&¢2¯ue±$wÒ:ü´éZÌe#Ò¹h†ŽGVönÚ¥ ‹½$G©Óáê•ÑéyUxm~V/·®ÌPtu¯úrzvò]9ØÀFmEÞ\ì!l³'ðùÙö„Â.’ø™7 ã®Á&‹Á(Ù½=²@K[Ãk*|±_Œ3D‹§šïõ}± ¬”2kq¥Ÿ•|±ë­S§G{¼Ç«³k8%¹R-Bg¸Z >Þ[}UÚ¦Þc»Æ’NüðLU7Þè¬áÜŒ nÝ_žÛ öPÁb©b ãAÞïlû.ÑVÃt³.&êk>ýnÏ×±ÛÇùÇúÁNÚœËæ‹¥ˆf2¼1[éTàŒŽ¥e.›¶˜ÿŠ]C\=² 2ê‡@‰©jpXAlÕ—ˆ“Zó—‹£")UDØ}±&›½SUÈ“fªÖâü6®éd±"¶Õõ’œ¨ðÅê+ñ¢Å¢ 5ùve¨ŠÈ 5‹õrÁʾXöËæót֕Ѧþª½–h‹Å¢«1‘Üx3\«V«#JNÙ8Q3/š/êÉÁH”<–/–ôµnûN,Ž¦Æ—f…‘Wƒã¿?‹m§xbû'µoЭm>EÛ¨!±ã¤’¢sº4Æ Ör½²Hí¢Õ¿S¬ñºîŒægßpûîïßß_€׺g,¤õÄ’ØçgÐ%{ý/¼ó¶,Àº®žmˆ"Ç„{—Šø×ÞÃ&_ñ”:øJÎZÄ¢ÌÃ"¤FÀš-ßÃ5ûÞ¶ï÷Ç8­ « ¾X;‹5üضf7Úܯ.NÛÈtÞÇ 2M‘;†˜û<•¸ºß•™ÙÜ …¤ÚÁ àœ^416•(·_Úl'r.ÍsW1‘Hí-ûem)ÖÕÂÂÙø#:+Fs§«æ†¥ .†^pÁ6£¿Žï¸Î‡ØŠ™Ä#@\¬PÀ,6j}Œn4eÿ|Íâ¬æY‹5Ù*„ o«EaãiÖ?±Ó¾7):äø#âÛ]øãkÖ}[¿øæ•cdÔ®¹NìZ0¬[³†õb$q±oîÚ±¯æêÓšzpáŽÝß-|ó‹±ðƒ˜æÓŽ.ê^RDè=]óÑWÿû`¿q7òø–‡â;´tßîZ½ùʈoÑѱ.èì;[¿ýrÿ#š´{ÍÒ¾ù*NI±GÁñ­i×Ì`±Ø~kãÎDµâªiÿöôEÍÆ·¾œ¢‘M’oouY¡qÍ5î`üI±ÝÄ¢sWb.µñ›ö˜\öI7 û]™¥‹…¼à—jJªO‹… Χ6§ý”’Ô¿®Â"‘šX‹Å’daalkÞèÌ9ÐGsÍD™Î’íNRKŸâ€FØpH…ÅBÙö+“˾."; Z*|±‹…ýæ«|ái´³XAuc•5\ºDt-_¬áåI³HìQ°ÿÛlKŠ‹*.:ø½í•”Ô9¶y̽úþŽïd+|¤E‡´itð'»¿ÿ G°bt¯Ú4Œulãc]¦K–ø®qÈYb¥Jâ§#»:ïÍø> V޹x²+‘ua±†¨Ü9LÅÌe#8dؤä0¹ì7§“q*X¬óµ>,ñ¥Â: …W…”h“qf‘Å4Ï…˜«<ᜋ¥ Ìe“ë 9+­ÃhQ¹o¾¯¥¤öЃB¦°ÔS˜ÈFsÄÐæÆ‡×à_â©&©• §DFÝ" ̼FÔ@¬¦n,6žûÇ¢yq¶å2äD“k Nl1aÙglÊ[Úâgæ?Vå(Q¸“Ö‘¯;ܱ¤¤{«ƒŠðÊÇÆz2«RV§êŽ55Œ?)æ'ŽŽ‡…ãe£9ŠsÂ*[j°m Ôá°¶½|·)P بsfx¹¿Ú?57‡ÉæD·O£‘NRE ˜l7 P:úŃÚòÿ'ê+Cxi—¬€3‘Mc‡Cu<ºUg³­„^Æ:*ZLEb´Ò8¬IÎ8‹Iqî+ôÐ5‡Œ!`¤[%Å ²IÉòš¢qÞlõÆ…ˆÁ&ÊKYLÚ+ÖcÅò·iPкaÜ­{pÁ ­zs‹AdhaËAK•’¶‰Žl”nÒfª£8Ä,ce £iCÕÁ1¡ÂY;”É.ª5ŽD-£ó 8òÚ§ƒ–ªë < ôïòÎS{°ÃK5M1–ÀFdóºå|åèFóÛGÞLð˜Z.K“¾Ï8é³þWcI²/6.âT`óÅ QaM¿¥ŠJ!®g¬6‹ŸFÅ/˜¦›R‰In¾XÉVɃk)íÜ¢pÐ1±ïoÑn\ïl«S6¡Ù"{p *p 5óIì¶N¨ùe™Ëæü€Ï|˜ËfsÅ]œKYiŽ êÅhQ´©Å¥Üåµ^üUÑùƒµ™`¸e8Ýhnš·VzóïQ`c™æ*0·ˆ‹¢úDXÔ`Æ2· Q (¯_D‹%Þ|ò÷<ÂXöÞ¶š6Æ6£•š)´ˆgÓ3—Íðpàâ2‡€âÖ›9#j_I|cÎï6÷V ^ d¾¶Ëö ÿ+µiˆ^XRȃ%‡ qÙß=ºN 5ü©2»UøbãÌQ¸kU¾ØDê(ùw _«Ä¡ãÚˆ¬§/ÖFI-k¨ ޙྵe±I9òõowPóúØÙà­b%†)äåeMp°’‰IÖ_Q³1—Í¡¡Ä¦&‡€ïM79u,­‡ßžõpÊm)ÍÁ¥æµ€àûšl®;(ÎNÔGèü•Šãaâ|ä®\ö±u";qtðTâ˜RtABÌ€céXœ5&RG‹uÈx°XƒrÚ‚ L&h‹:°©7‹59¬Iw%ÚkÑ[C‘¼`RS¾jkµµ7‹µ—"÷À4®w´Ö~qŒAäpî¤y»ÍÝ FÓr¾IG³]ÒaU²CÌ—ÚÊF†Hsu8«(:MäUèç’Ž®˜&—}‹61PûbãÌËÅʯÜ}Y¬Skmœ¥ðÅšLVpW3³;›È6Í¿„T"¡6°õc±Ž¸Xw†/Ö$ö±ÿŽë{.Î*æ²’µ¥ˆdo´µ—4דoÕi8rê´¤¨mšªnæ*›ÍC#M˜µÄeÿøÛ†·UöaÊLTå‹$ѤÂ&3‰©´zË—åÄT,ð²|±2Õ£ $JhÒV‰û¹dÞHR l43 ¼l -è=Ò™Ëffp)Y@ ð-6 ¶æQ‘|ÃΣÆL¢*á·Ðin&9« 5It£‹—½y~•ôjÐ:bUþq (ܱ޾XCÎÁt]|±&C•™ªiŒI:…ãU"¦vyÃ~ÑT#³~¦¦øÓÆb{õÉ}le`.á¡Ã¦¥†@¸7×Ôl©E¹ùÎ]‹Û­ª<î*<òi,ð†9ÑoÍèÄøGâGM¶`Z‹mø_›i#Pk;?Wœ0\ØØöq`d€°( p©e«Í$&5äò6wm‰Üíó¶ûúU¬6to? ¢~=óÓ×~E½O°}Œ#ÀF Ïnü™¿GFž3¦ <ëÒiB)»j3?N™Ëf·Å¹tF€`2¢³¸+•fþ¾˜Šµœ7Ý0—M7©ëÏü˜e.›z«±F€`¢Ž€¼’¦&þÅ„Èr‚ÌߣÞxlŸ„@dû-·’@ óC˜¹,w?F€`ò'—u­s†‰BæïyùßÒù^à wÑ|‡3-õËü¸f.›–†d¥Œ#ÀD M.«c³&™ÈüýLÇx–Éu4»_®W3§íÏüØg.›Ó†gF@ ¹¬Vy,ĤˆsYñÙ‘ O¥Ñ2ÏeyÙPŽ•0Œ#À0Œ#ÀdöËft.’` #À~Ù ÎÅÕNÄ@£–|döËfg.…`F€`F |˜Ë†)kdF€`F€È Ìe3ƒ3—Â0Œ#À0Œ#>ÌeÃÇ”52Œ#À0Œ#Àdæ²™Á™KaF€`F€æ²ácÊF€`F€`2ƒsÙÌàÌ¥0Œ#À0Œ#À„sÙð1eŒ#À0Œ#À0™A€¹lfpæRF€`F€`ÂG€¹lø˜²FF€`F€`Ì À\638s)Œ#À0Œ#À0á#À\6|LY##À0Œ#À0Œ@f`.›œ¹F€`F€`ð`.>¦¬‘`F€`F 30—Í Î\ #À0Œ#À0Œ@ø0— SÖÈ0Œ#À0Œ#˜Ëfg.…`F€`F |˜Ë†)kdF€`F€È Ìe3ƒ3—Â0Œ#À0Œ#>ÌeÃÇ”52Œ#À0Œ#Àdæ²™Á™KaF€`F€æ²ácÊF€`F€`2ƒsÙÌàÌ¥0Œ#À0Œ#À„sÙð1eŒ#À0Œ#À0™A€¹lfpæRF€`F€`ÂG€¹lø˜²FF€`F€`Ì À\638s)Œ#À0Œ#À0á#À\6|LY##À0Œ#À0Œ@f`.›œ¹F€`F€`ð`.>¦¬‘`F€`F 30—Í Î\ #À0Œ#À0Œ@ø0— SÖÈ0Œ#À0Œ#˜Ëfg.…`F€`F |˜Ë†)kdF€`F€È Ìe3ƒ3—Â0Œ#À0Œ#>ÌeÃÇ”52Œ#À0Œ#Àdæ²™Á™KaF€`F€æ²ácÊF€`F€`2ƒsÙÌàÌ¥0Œ#À0Œ#À„sÙð1eŒ#À0Œ#À0™A€¹lfpæRF€`F€`ÂG€¹lø˜²FF€`F€`Ì À\638s)Œ#À0Œ#À0á#À\6|LY##À0Œ#À0Œ@f`.›œ¹F€`F€`ð`.>¦¬‘`F€`F 30—Í Î\ #À0Œ#À0Œ@ø0— SÖÈ0Œ#À0Œ#˜Ëfg.…`F€`F | 8¾VÖÈ0Œ#% -ÏEMMM”Lc[üA@ ´ÜeÕÕÕÏ>ûì¦M›š6mÚ¹sçAƒ!¥´´4Ć©¬¬Ü³göéÓ§¸¸X¡yõêÕíÛ·ÿðÃaIQQ‘Ž ì—ÕA‰eF€`F€È%ªªª@I}-Ël×®]Û¶m§L™2aÂÈׯ_ÿ¡‡’3jªR”Õ²eËañcÛ¶m ±9sæ€ò‚ì‚ÈÂû¡¹¬o+³#À0Œ#À09†Àwܱoß>µÑ¸á†&Ožp8ÛÖWx)«ˆ. ×ýˆF SÄKy¼ˆG`@óæÍAjA(±ç€ï/…2fæÌ™²?¶0ÀK-¢Pn·nÝV] jˆLtþüù/¼ðŠ+f̘ዳ—ĶR^Xm‚Ñ‚s¯\¹ÒU-È7¨ç-·Ü‚PÚ'žxB–±í9€ø]¨µí v>pà@0r0Z_³5˜ËjÅbŒ#À0Œ#À„€€ÚËèZH¡8hUþÀ2Œ#À0Œ#À¤„¨­²B )½a‡ƒS¸E]UŸyæ™”~å•W" „*v¼¢tì Ð€xVp_xéD¸"Z@v‘"„ÀIU…*Šm…CYÖ¬Yc3›6[ù†Nœ tÔÑ&ƒà lt@8Goݺ5þÅþb6É­[·"~\ŸÙòÂCF€`¼G€÷1Èû&æ Fß=¹@=Ŧx•/6¥¢…ü8äÕýT#Ȉ]º°Òß¶‡xbaiLj‰ýˆ,â Í³z XE›'Ð~[T 6UÂTJ;p¡Ú~Á&tè¤D[ŧÅKâĶF¤\NLlbü­Û3ÿÉ0Œ@"ÀßJÈÃFå*Eo%ÀjDÀ©iÛÁ@Q *€oR~Õo+œ£¤„pUö_ÂåiËâÔƒ‘…\¤6%d•M­ñ"cd3D(ÑU•W5á¾Eèöv?h|”‹ÒÔ~Yæ²Ñ l#À0a#À\6lDY#à‚€&—eì\?~|=ÆŒCWAˆ±ÂÌÆn]3r¼,÷(F€`F€`,#0}úô×_]lì…dEÝ´^ö±_6Ë-ÇÅ3Œ€¼C4•m_F9‹sƒF†Ô‰ûe¹W0@€ý²)‚Œ A}úôY·n]Ë–-5Ã0˜Ë¦;ggL 0oÞ¼Q£F¡$,Jèß¿?N°!žÚ±Z‹Òm¹r13—Mw'aýŒ`.›•nÀ1Y eähذ!eÀ>ÛxRÇrùàƒÒžÛi=³5eÊ”´ÁÊF€`#À\60tœ‘`²‰­ê½âŠ+Òj^xÝxãô u>F€`"ˆÇD°QØ$F€°#@Ûµ  Â#K{ÇÀ;KÊPˆ•È3hÐ $b_q|ƒ±U«V´t€d°Ëw»ví† B»É@}k‘¾ÇC{^|ñÅtY&Nœ8kÖ,q)w# 8Æ€#8Æ  ;‹`¿lV`çBF %Ķ۴é ý ²K|—þĺÔ " VŠ?±[!ÖÉŽ9’–Ê‚ S®;î¸:Ák'Mš„«dÜÊ•+›5k–’¡œ™`F Í0—M3À¬ž`BE|tĈ`œ6­ðÅÒçg°é]Âǯ¹æ:‡GîUdÍsEÌÀ³Ï>+kg½þúëñ‹_ WÉ÷ë×Äà‘Í]§l¨-ÀÊF€ˆÌe£Õl #À¨@€,}nÑ&’zíµ×"4«µp¯jïÞ½I ÿ~ùå— ©›7oÆù< kœ•ñgF€È-˜ËæV{±µŒ#P§¸¸øª«®rqæ™gRâóÏ?ÎÚ¹sgñÙClÝ…t|©QØ á6`(F€`òæ²yЈ\F Ö!ž*ꌭgéwòäÉ8¹óÎ;ÁVu`ë¾(T€j]á 3Œ@þ"À\6Û–kÆä/p¸Ò÷`°ÕÀÇ,*zÑEá¯5NY¤PLâ O«W¯Î_x¸fŒ#ÀÔ"˜ËÖ¢Ææª2y€ÀîÝ»E-‹Ý äJa×úz‚í ´58.äÁb±Ã—⣸®(c“¯<«À0Œ@ž!À\6Ï”«Ãä ‘`Ÿ´x ¾d‹PWãÇoÞ¼9v'ã €Åaˆ4@¼ v$xä‘Gù^½zÓ¦´_ì;‹‚–.]JÒ¦³8¨@´¸~ýúù†,ׇ`¼@€¿•ÍÈ•`PÒ¯¾úÊÆeé"ü¸Û¶mkÑ¢…ëU/¡›Ñ&›+j ÂßJˆZ‹°=y‰+!+ÍÊ\6+°s¡Œ#Àdæ²…› «­0—ÍJËsŒAV`çBF€`F€`B@€¹l ² F€`F€`¬ À\6+°s¡Œ#À0Œ#À0! Àñ²!€È*F€ˆ8¹/+[kjj¢c [8^6+mÁ~Ù¬ÀÎ…2Œ#Àø#M" »#k˜?¦,ÁäÌeó®I¹BŒ#À0Œ#ÀÔ˜ËÖš¦æŠ2Œ#À0Œ#wp¼lÞ5)WˆÈ/ðƒuëÖíÙ³ÇY­®]»–””¤^]|Òvýúõø`XRßPH½\¡aΜ983fLˆ:mªr4^6jfGÍžd;Œ×hêСCiii²ÚôåQ.>݇OôÑ÷öä"Y»v-¥8¯ê!$ñ­éÞ½{cDã‹Ö²§˜…ãeS0`ö|0Œ#yÆGsÜ¢E‹p—¿}ûöÅŸHß»woŠæ ;zP®¯ŒŽ€¬Õ!p¢“7˜LtÓ•\Q3;jöØE³Ê£iÕªUô'¾Õ¼iÓ¦4µ2 £N¾/íuUß$Lø~5Õÿâ<õÉA¿t’=$ÙŒ,Ÿ cð€³1Œ@&hÖ¬G$øoî»ï>ü9kÖ¬3f¤h n´Ó¦MëÓ§Z|KS¦LI±,d·éAufÇ´:ÆR7›5DÕ«W/[¶LÇHy4Áy9}út<.X°`äÈ‘:ÙÈ`paˆ FkÓаaÃ:mYð­é‰'öë×ÞÕà_œ#é©+g G€¹lĈÍcwó›4iRŠ_ýõêÜo¼ñÆåË—§X–«D¤5À E›9{ôÀ›úk®¹&˜EEEƒ B^t着ª`JÔ¹0¸0ÄB !ð*èé§ŸÆ“-U„h¤ =5b‘B€ãe#Õl #À¸#PVVV^^Žkx/X,^ç!.%Ù…›1Ü?»ví‚WF¾wÂkµxñb¤·k×®mÛ¶T  ¿aÜ‹A¡á¬³ÎBú€Èåƒû"þ$ß2",¢xqËÄ«ÌÇ{ìˆ#Žž-\}çwð¢³iÓ¦_|1ùŠ|õ>ìÏ=÷² ¸=ƒmàÝ+Và¤Aƒ]ºt# ú/ºè"¸À ôŒšÙÁìAk®\¹’ú$µ¦Ü”è`ˆØF;v,õ\¥ÆEDøùçŸO­/:õv¹{ƒÈ¢ã‰"NaßèЩÐãM"…Ö¯__t3…mGýû÷§Á(ǼRéèên¸„*4oÞœÎiàÐ æÎ‹âPü9lØ0ü‹·Ìt   F=\è$=ÑO<ñ)Šb(Á3gÎp2~üx ÛíÛ·g,žãeeü3wžJ€çeF 3 ô¦ET*ÞWÊ1‚K–, Rˆ¼¸'ó( o9qŽqŽÛžÈHšéOÜöèôÙQ.¡V€K$)â\Eð.ÝŒq #òâVMÊ¡ÖW¨²# òB؉Lf 'T’ôm…ˆzzÙ5³ØC}mD=–zjJüKÍ-z $ñx†ÆEëSG¥ OJGW}XtohF— Žs’ñÒ#Š£IôR}Û „ªCe “„N-¤:’0j„ºàÀ º=ž‹«4‚4BO”"F=t§<Àå¾DcW}HX/’IéqÄNJ 3Œ#n•ÄMŽÂ[Å[M7H¤Ó½Ÿnu ‘DOÅ]VpPâ—B’*Ý,Å=Øëf I¡‡Øî—¤A0¢˜T´àÍÎE0¶µ_ô'H ÙF7i‘p àT®h…$,Ýmª£?jf'kµ&zˆÜa¨â¢)Ѹè*èÒÔ!)câ\æ¯ô<æìE‚ T½ôµÔÏÑÁhÔà\¬”RÛFöÕ]Q~´# P 1›ÁÔ«åQFÄ2:AÃAè!k=’Áòc-©²=?èt¹e˜Ë¦`°ì/K#ˆF€È €w¬#Û¢E 9Нq)žoâ„Ê¥!€CÄÿ âƒQ¶|È!0 Qæu¨¯ª‘ä«9sÙœh&6’` ÕzÝuב' ‘p4Ј=hÿŠƒ‚ ¢ üµþ$×½i{G·d(=¥û´ú@²n±¸U¿ð  ÑöXn]Y'îå~Vðõ(" vN%ã@ mÁÍòZ%ˆç«Í›7£ã1 }»¥R^0NPÏ[o½±¤êÚ*ôÈ)FÜk“f/ÛlE‹qGÁâè«›N!°®‡º?Ó@(2ž eU¡l,ÅÃ6B€¹l Ø8#Àd8{°T…ÊÇýÒæ°A"cáÆOnÕ —wÝuèì=÷ÜOþÅùÔ©S^(Ò‰å#ô>§n¸!€o÷òÁƒƒŽÀo”=œ¸ä¨ À/Ǽèô ý,öÊ+¯ÄRBM´ÕÙ©'} €sÓC#ˆ,^ž¤¾Çœ@'[¼•>|Xsú`.›>lY3#À¤ ÜÛÄÊ'¸i‰n¶jÕŠÊ£UØtû§`8î½öZpòuÁiJüÀy€†"&Z’:ëäÊêZA¾W¯^ȨŸàTE˺m‡¨lºe½éA@´¦¯'•ÊîFÚÅ‚ê“ðñ#´ÝXÇ%©Ð¸¢à¦b‘–¬;Пx8„y8´ˆ×A¯>ð°ç*@ ốB8Q?RН?¸*T_ gŒÌe£Ól #À$À„ èΊ{m! ÂJ«¹ÁV{€û>m@7uz-K!­8¼<²Dœ"Âq‚;7¼¿(ÂFpOUóñZ _ˆ…¸^zðb…Š=Œh&}?\²húÀ+ê¥pðã!ŽÞ…`V¯’Ñ3i ?\¹è´èÃø¾1½^§ñ?þJ¼¾‰€øZ\E¯SèI¥Òô>„^ýÑÄ¢+y€œyæ™HÄ0ôzV¤Ò)ဵ8ÄŠ.ºJJpÀ ±†ê@@1f!Ù£Güë$Ç”BWùÈg‚-ã\Œ#Àd0B±ÿÝ;ÅÞU´ñb¢³8äor:'qñ­K±þšvA‚xqp•ö9›ÑöX¤‡ŠضzZ($Ä’pÚÖ@G$‘öÐsa  (ÃB–«›#Ùø™i\ßR¢fv{ä=§DŸM‰±ØŸÐ ±RO“[_$¢Wˆ>&¶¥ ‹èiN=ŠÑ$ÚBad¨OŠaâìx4vä–¥ ¨F¶çD?Ç%Œ;Ú½ú¼ð‹OѺê!yDì)&J'Òúuh[æ} |u:ø[ ùü Âucj'ä1’Ãõà 5j” ÜGi=µ|ÀKD[Ùcé N²7—àZÃæ ¾¡{”  \²*M=T › 4Ø&ÿ‹ +cÔÌlZ}I'<€ #'®­õ¡Dt? }‘ºvZW=)¶äU\­W¯žÚJˆa‚瘢a¢}î|W.Ìt6”OOk‚ÆßJÐ*\1æ²áâÉÚF rঈ›"å à:ÂK[x"gnz LÂÒcޮ֍™5{tq¬r ÄçwÖtŠðÄ' ´cáÂ…¾Ÿ!ÂÃ\6D0õU1—ÕÇŠ%F '€wßÛÄ‹WÙ‰…€ZlÒ)>9›“Ó3Z¦_¶555z:²&5î5{²Ö0‘,#\vúôépÍÒ÷¢Áeõ}á¡Ô‰¹l(0&«„¹l²ˆ±<#Àäômw¬Œ¡]ëñ‘wìlóC3sÙ¬´#sÙ¬ÀÎ…2Œ#à‰€‹½¶ÖâUxÕ¨{Í=>Xº-sÙZÛ5+Î\V¨pŘˆ‹'kcF 86[sï/‚ëÊ—œ…?½C§•цËe[g~Y[¾ô—,׃¹lV€¹lV`çBF€°#À“îû%Â+ÿš&m.«þ†E€:2Í ZƲ0—ÍÔrAÌe³;Ê0Œ€…€Ý{ÿuŒNŠ^q[:´š\VŸ¿®œåÝÛ{\¡~õ™Úêc•Iæ²ÙYsÙ¬ÀÎ…2Œ#` @îŸÈ¸„ˆ@áÓÃuÐ*¸¬‚¿®œÚr´Þc=i.“Ú{N`UÌeC—JF沩 ÇyF€Ž@1z`RpEœS‰@áO¦…Åh\֋® ¿**×ËÚ2©Íâ€`.›ð™Ëfv.”`j; ¬è¿®íp¤¹þ…?þK(tV<°jNhþ×dñè5ÆÅ_ˤ6YS—g.›:†40— gaF %ˆìƒÿ—’®Œd®úddžÏvQQ­7hߢqqƒz¶’÷}ómõö/)±´M1 £—B¨SxùŸR§³®\6‹Ö “Ô2£ÍL£R˜ËfmQsÙ¬ÀÎ…2Œ@íEÀºÛ=49ú(T¬yÊS//³ÍÔÙ?=wô)]Š=D¤ƒ¶vþõ,úóÀÜ2:)=EQÇÉœrÝ“‹Úiqú`)¼¬œ”`xN"»êïYsÄúBÔëêOm€úúÁN˜Ëf¥W$±X2+öq¡Œ#ÀäÖ­l¯  â¿y+Þvë£N"‹{ï¿'þóÅ}ß~'UAj(Q5eã•ÿëçÝöXõÎÝáÆ$ÙhýÝ\…£Ld:Ì“-Lª¾ù4â¸.µöËÖ†Væ:2Œ@$°ˆì? ƒ”FìØ½¯ùØ[I¤ï±Güd@÷¶MîÞÿÍüWÞ^ðêzJ_4iÔÐÇÒyÕ'Û;_?ƒÎ<|\ú:7¸W³uEåO½,Î'ÿàÔ)ŸžI@ ôGQœ¯ÃR¦¼«ï‰®#V`Ï«ØG›¡þÅ~Ù X sÙ¬ÀÎ…2Œ@­CÀºÉ=ò»œ¨|ÅkïûËÃ0uøÉÇ=ø³ E8âb'>ôŸYϯŠ1Ô3{Íüé0ƒËnÞÞyâÝ—÷{ƒËŽ4NÖOÿyiÛæ¢âP2ã?+'ýó9JÙû`™®| GÝL¥(¸¬Íq»úÞœ$²Ìž?µ­/ƒÏ@äeÌe³Ò¬cعPF€¨]Xw¸y7×)(Ì™_¼•Z4ª_tØaÂfœìwqÜf ФºX*ê(’bQVÅ¡d“ÅÅØ¢±ÌÂRƒ†ˆ^‘ îØûjVßWSõËå_¬ æ¡_Q»*×67`.››íÆV3Œ@î `ÙGÿ˜aÆ–Jq êFÏz~å­ —Uï@T«AFôÇn™?ñÒ)£ÎJ(Âb®&mu¦ÈœU}5Íì¶ÍáAgeª÷Úý5‘lÖ7F]ðµfF›;³[ªB€¹,÷F€`Òˆ€Ed+¯SpPýzÛnxŸ.ͤ¹Ï¶›0mÄm󿼏ºr}õŽ=_»WÄâ¦fM)&O¯zG\lѸQV©A£$ÒYyÔkÔà—Ó¾XWãc•2¦³iü¬:Sp¼l¦ærF V"`qÙùÖþ¦¹‚DõöÏGNÿçòw«O~úez”¶=\\ªÚüYçkoÆXðgJ/þ:™=ÍÓù®|{ã¬ÿ¬ ?¡jÊ¥gg“±-~B*»×ÿ‘Ûѱ¾xöø1‡Ïú‚”´ÇË& Y˜Ë†"ë`FÀ ëÆ¶ÀúîTnAµc÷Þ+Ö9ßÕì%xºTõ1¸¬ñµØO'ù|›·o§vó&Ž.iÞ$‹°·wíõóœÈ ´{\n0Z^ Jd. ŒÉ*a.›,b,Ï0Œ€.tc«yÒØÙJ7[ôäö}ýíú·¾÷ɶʷ6Îzv™làö‡þPܰ¾Áen¸c\©=¹Ë ŸÿeýºÛ)¶Ä’–ê×mð÷'§œõóvw=V…νÀ‰¯¾eðÞý_޹¨ ¥@þšiç]0±³,‰Œ«×W¦²)ØÑmKÉηnPèñëÑ|ˆ.Ìe£Û6l#Àä:5‹þ–ʯÙÍ;íÊ ÿa7ϪXõ¶l þ{÷#tµßqŒK±ýŸtì/‹«iÞ/6Eý±Æ¢£ Î«o-~ìùY£ÏùE“FÅøó¨#J']>ýâ3lj-®êV$êJ2øÉ‰ÈÒëøÄþþdù¼çf8·ÇzüÅÙo¼·|Äà1±Œu ?í—óºuì+K.^ýô¾¯÷¤º/˜Y/…ž7çYûÎæú¸cûkÌek[‹s}F ƒ`íWÎþ.Ø[ 5ì÷3ú]kÙC ñà þ¤K};ݧóÑVEQkgJ”‰[{ÂÈÂ7Þ‹…¬~g© ßõê |Ué“mNÞa[âÑGÑÓçNr2ypÜX)U•âR›ÃKFŸ{­ø—~}ǨÔ *ë÷½¹µ1ÍàäÀE…‡sÙð°dMŒ#ÀÄÈõŠ(iÙbÉ4kG­åë7–Ï{?œˆvžzåðâ&Íé†âcZõp‹šgçPÕ5ˆ-J»yöØ;ç•}±w±É.Çô,nÒÂåë¼J‚K ámuRÒ˜ ¶NŸünà3ËæÅ‚âzúu?“NV­¯Ä%¬yw"w?ø´J¤Ïq {`á­[vVËja*TÑ%!o1ì¸g2Іô;MZ7¿6.}ãy+×`.›ë-Èö3Œ@D¨ùÏ=)¾õÎzöÝJ7Í6íê:!žýËËq )}Ã6u—cèâUÐkUy΂òS~Òüþ§oýtG5^Ћp9ž"áŽ@*uPùZ%Œýa™óý~,1~Lº}Ô•7~ü…9X FE|¾gG,´@Ö†ó‚:yæóÎ0úâ³ÇÞúà¤ÁcÛA’4ãdÂÔó¶îüx숲ç_yâ¼k;oÜ\eJzâbÐÿÙ®Íøõî2À=ä .Ëß‹è´Âf¹!Àûp¿`F dŒ ž‹ÅJæÍQUý)Õ¥¨î¡%‡çM½l)<ëJ¤¼õdÍÊ·*\6P¾:óÆEzÅvl ã¸ñÁo?uÀ™áÏvnþÝŒ±Ý;õg•3Ê:ndD „ËÆÜu|‡žr¢ÜO·UŸqu;\zñï›Z·(!ÄÕ;æ–Íž_þøôUÈNçâ’, %/¿þìÏ£hÁã/Œï‹\Ã>Ú¤»9ïc4dad`¿l(²F€`œD904yÛJÛ·¥_I«¹ìoy¼±Þÿ¤.^ºgÓØN ;þ–aKWWˆ­Dº¼9€H,iÝ¡×ñý—?´ý‘¿,Ø{¨×Ÿ=fþôUgõN×¼³ü‡{}°¹Šäéy›5nIðÝ¢ºõ¿þfŸ|‚¼"— ɱ³~»¨Ï ƒlz¶l¯žxëȳúWoŒ@šÙ5˳Z® À\6WZŠíd\C òá¡Ñ`Í‚…ñ^†÷õ^hÓ²äW—Où÷Ìõã.6í“/Þ¯/Û¡¤¿¦MŠATJ—Ž=ï¼aþCS—œm2ÚÕë5g&™5â8êÕ+‚äS^xã½?c,¿£ý!b!D|ã±¶°|àIC!OÒ±áãõ×Ý:tùùW¨C3Ö?ÍÙ\›mj·½ÌekwûsíF }„ÊÉ 3€@¼?,óyQ(é¯~<å¶ßÄö ûÏ2‹ŠŽ#åšèeµ\ÊI' øËõŽ»$Fš?Ù¶Iæ rö·6¬þɃízÕ㣻ÆUÓ‰‹Ï"8‹3hnA³ú]„ó›îsý*·5Hß°`ÍŒ@è0— RVÈ0Œ@äßãs–ì#oº÷6­ÅÂ)™í 8égŸ:\Ó/«Ãº|úN±}äáF½àŒËPJÛ–í¸¬¹3/¾­0ü—½ÖT-ÿÁ£…·5-PX§C;có¯'^°<Ç6Si×yøÙWtïÛ<á¯soÔᲊ0Ž@ài.:0—N[°%Œ#_èЖ‰†³Î ·_¹ûà&ÿÅαÁÈDŠÕYM™Ø%qȉçkÖ/ŸzÏÄ]؈ÀøhËpͳNn¤ÄµíÝ¿20àçîÚ½}݆ÕFQðÈÆ³ß~CÜsüò‚G±³XÜà%++d=ˆF@ÌÄQ±Í f]u3ïg=à Àb™Èæ×T•óµa.›óMÈ`ˆ"5–Æöè ïLØ_vÂ¥e=uǽóo]òjÅcÏÌÁù“w¯êÚ±'ü Ø  ¢ržèu@Š-HQ/±ºtØ8„Üñ¡JPÖëë—ÝQ6¯YãbÊ8ç‹Pʯ¦Ž‚Ì-KJÚv B§Î¹;ÝŽ H˜ùÈ*hØ ‘¿¹zR~{ÇØŽgTm\3¨OlÍÙÆc,džÖc‰X÷ãúПcn†B“úª-³ØˆN5µÞ,Þ“«Öw€`ÂFÀØ“ë ÂVÌúÒŽ@á)±-Ö.ÚC¤Ýõ原oÃG¿ðE®ÐËÞÿõ>*å“Ϫ¿Ú¿¯Y“M±¹lâA—³KÉdý‰ì›·V·mY";—l‰Á,?öLkg.vÄ&‹!ïh–,b©È3—M=ÎË0Œ€ Ö“Ëžd€rÂ~’µï¿È ùës¿¹ Þy™ËÇ.ùœÌe“ÇŒs0Œ#à‡€áš]ñ/?A¾! û\k6,f"k”ƒ ¿,;e“í£Ld“E,Eyæ²)ÈÙF€ðtÍÖ¼²ÑÉ! O>Ön¬d.k´£$|ýËÉh™±åPßÎoS™ËæwûríF ;XaXKžæ£êüJ(mdš ÷W¿oÿ×Õ[>#9_{ª·lÛ·?$[4i\ܤ‘¿öð$ {ß§ý`)sÙ¬Gõwù’­Ìh™Ë†×ûXSJ0—M >ÎÌ0Œ€F˜Áª§¢‚žç(Š˜|åÈë~ta†y¡l¨vçác(åÀêgÔh”ýíå÷ŶXtÇÍCO=)ÝÐÉú {‹?™È L\¹,]5úv “þLöP.ËŽìæÎÁ0Œ@ˆ1$­ R‘QšjxÞ¯~_½u{ÚÍ𪂼íª5ÍXÑ·Y3õ#"‹#Sæ@AŠn,;eÓ8k°ê$`.›$`,Î0Œ€ÖÍ>ÝüÈ´g܇Mþé¥â'Ì\þæúÙ žÉ{**ªg™ä (lVHe¼ÐMËj| ¬=zÝœ¥ì#À1Ùo¶€`ò+jöõçÓWÇ‚îƒIùú'ï+=ÊÚª3ýפÛçÐÕ½+*Šê–>3BÑ\v÷}å÷< U‹îš2ô´“CÑ髤°Ç™$S½œ_š[h•ôu‰—õ“Ì#À\6ó˜s‰Œ#P‹°èìšÓTí‚n§\ö©d.‹DÐÙú'Ñ´ëÍ«KW|Ÿ:øÉÖ'^üoÓF †õï+çÝñù+Þ\¿ì·H²_·ã!\ܤ1ýYõAõ†>ÁIƒ¢zzu“ëå¼DªHfhã£Sôgåª7V¾UµëË=0 ÷ñ¥PUv×½å÷ü—ÝUn^ýö»«×¿»é“­¸ÚõØ£úuïRÒêp¹h[A¨&ìoצåð3N–;› °û”Xý Y ž’“×¶Kàì3œ5˜ËF­EØF€È7,:ûÆKé¨[Á ƒHíúýÃ…Ëž4ÄvUÈ/º{ê°Ÿß@Wgß4q̈at>ïÙ—FýúNS!Oü„µó?ÆIßnǽð÷ÛdwoŒŒþ}..9…‘xàÍŤ${Êßç’¤8&_=zç_Îzìi9»—0ÒùËoG1x¼leU[rß_m„[.´Ð|øh%sY ˜#{³SÖ98%¢p¼lD†ÍbÝ3Òa•8ÌêÏxô)!I¥ôívNúd yÅ…ÆÂÿ•k«DuV¼‰óØ1mâx©8‰ËÆë^ýé¶IÓg‘äìßM¢R^¸÷¯‚¤ 2 áÊUo–ω¹oAv7=ÿ8$gþvâ’î¤ìÃ&ܰoÿ·¦rA1ÍLû*XT·ž+àñ戛_ã%_ +èÅÜ s[¡‡÷W=œXŠ`èì€ð½¦acož6lÂoè7jÒͳ5¾  8öâ ¬rMùÉc.Ÿò‹1Cô›ÿ×[ŠêÕ…ÀÒ×Þ¤‹¸„t‘çH¡K‹–.Gú “{ÒŸ“¦ÏÜñÅ—$yÿSÆö±½»—PM]\lí{PÂð³޹ø|’„·\{µ„±á&]¹ÎàÇ׎þaI›V$< wi×O áů¾î¬Ú¢æ‘çF]/ìj8›?y=²v„XŽ”MaÄsÖŒ"À\6£psaŒ#Pk°èl—þ!ÓY%¦ãFþ`Þm,0V´yôëÑÅfÉ&=딓l—b)ñ㉖õlrÅ›o#÷ZðÜôíÞeÀI=\vÙëHÕˆ³ÊbÅM›ÀZÃ:˜§­±ââGÃEˆÓ¿F PúæmØ:׌0«Ö¡¤­äB48B÷’ç¸Â6=˜ÔÚY*W+Î]6W[ŽífœCÀ¢³ÇŸ&5˜}ó¯Íš&~K¼{ÓKOÌüݤ"+qÙ%GØÌX¾Æ`™-›7³]Š¥Ä˜Lœ;êc¸fUÆ<µË̼?Aø-òT4¥››È6lPß&Ù¬±±U‚H& ÿëÎÃ~$~c÷ÒÛÙÀÁeK;´÷B¸àKG›n…&+#@ذS6禗Úl0sÙÚÜú\wF€È4=îÔÂãN ‡Ñš•èß»ÇЧˆß€“N´³XƒJšœ$ÎF:•”´g—ÎÃÏŽ…œÎš÷äŽÏ¿œwÊâÒ¿¯—µ\ÃÎõV–†3aÜÈ 'û±ó×µc'—u+ Ì\¹¬@ õ Ì 2='py©#À½6u Y#À0I {¼ ;ŸUÐdë¸Q’²5UïÙ ‹¥Ä°Iq銋†Râ‚ç+`‹ƒ:u Á…@'ZcŸñcýÆm¥,^±Ú5c ˆ.ã¸ê‡çOùÕ8çoä°3u¸l jó & Ѫ+>–w×î_ ‡øÁNÙ$Æ3‹Fæ²h6`j t¶´_ÊJ†O‡ŽyÊh¸-ï|ð1l+ Ã9R([¿'ˆô>ݺRâØ›þD'q 6ìŵnÑ‚’&ýåîêO?òK–‰ˆ‘xÖ)Æ×¿žx¾R¶§ß%W—Ý>ûÖ{–4xÖ«0²qÈà[t¶Km4hÕ…‰l-›†ò¨ºÌeó¨1¹*Œ#;€EYñ¥} Kïåõß|[µÖÛ"Õ[~Pß^}»Çè)8åà_S¹òõª›ðïå¿þ±Ìq£.‚Œðƒ7k2yüOdÔå«f€´UíBpò‰ÃÏ6¾q0ò—7R)•ˆ(™fÔEH–ÏzàÖ{ÿ¹ú­*ü¦Ìzö å‰ç–Ô¯WÏ« ¤Ø¶nD–ÒD+´<¾VÓÙÜ:l)#`G€¿ûÅ}‚`l" ¾ ãUï¾À”‚cÖ?ûXl͓ߡ–¯þdËÈ_–-}­SÍð!§ßvÃ/c!ÒQùêk4ŽfßrØKŒ(!RµáÃÎC.¦?¼÷*ȉB²o®Ý:wœõð¤,šsÛÐA†“X.ÂfÕ’ÎBX°S'TØÑð鯰Ǹr|¶¾6~ýëðÎì”õ6|=°_6Âæ1Œ@-@ ÁAÛñä8ýÒ‰eLšUò%mZ/œ}Ûì[&ƒYÊ,)N»Wmæ 8©§ìy|gãí%–v8jý³ƒ‹ “Ç_r›5nd¦XuA«ž|P†Ì¸K‡C.%–h䌚DÖFsÁêôâù!ÉD¶L3y^EöËæysõF W½ƒ1‚eº0³h?|´û¾ÚtܽÁì¤"Z4k‚Íe}5ìØõù¶ŸCL!_hz©I›þ2&ÿmïÔïl‹Nì‘õíw,u˜ËF½…Ø>F€¨U8íÊZUýT*[xlo9»>‹¹dð·¿›çt¶yGëÅl¬Ri)ÎË„‹sÙpñdmŒ#À„€€Ñ¾¿*¥ù«¢ð˜^)²ØÚFg™Èæïh¨5c.[[ëÌ093Zßf ‘ÅÊe%8hßË7mócÙ#ëÛ³X —H#—µÍ¹„ Ûʸ!À¯á¸_dç\Z³Áü @V Š@¡…ŒÏç [Bž2ì;ÞÏ:[| ³Øt_6!l˜Ë†(ëË_B¿Yæ/T\³ðpõÔ6R뤰:}Ó†y®3Z&²áKÖ ÒÎeS™e8”R/%°†d3êËkJúŠ)‚] ,»W®Ú\³xé)..Në-3ýœ‹ÈÌY4!-.ÊX ¦„rUVV>ú裧vÚ!æñÛøü‹K;vì-¡YPˆb¤ÊÙ–,Yòóø±xñbÅ$åÕ‡Ôê̼®·±dýŠY¿R^AƒkE×=M’Õ2^½=d4g!ý¦0F1å\a‰˜ dë[×)7@¡³x=$è7Và¢]ëžJ?伌@šˆ— <Ï:ie²cÏVtÿþý/¹ä’n¸4¯]»öñã¹çžëÒ¥ËèÑ£[µj¾«3 kVÊWL(¨ÞQGE}ôÑò4­¶Sh†7×kºTj»¤c§WçÎÕr¯"Â*Zº4FVË¸Ž…À°øN/^šdÔÌ–X~fàê®óè›&ê™Å¢w?ÎÈd lrYÍIME` ꌂŠ¢ëÕ«wñÅÿéOBÊàÁƒ_{íµó`Rw©¤fêN:}?pâ˳mšái;×™åÕüÒ ˆW\Ó³;Sû²äŒ H.¨–# x0ÓG&ØÄ hÍ,a‰9õpx•mZVC­ž{m¾ýþÆ’Œ@ÖÈ4—ÕœÔtøk²/@Ѷ,çž{.vß}÷¹SµI¾˜†m?¼ Ph~衇tx³¬9€È¢k–‹t<³g}bt#5QJ…Ârx•þÓ¸×Ô!Ò]Û+©gx9ÙVŠÔúºNtÜÉÞd5;*‹1¡#!.l’Õ«:ÜW;¯\ZZJJæÌ™£˜Î\ÍVL ç_T}5ÿ÷¿ÿ½ñÆ5'q_mÎÉÔ+KºgêT(¯~?aIF E|‡°¯~ß'd/ ¶¢kgxÀ±EX)ðô} wNò¾Y\g=²(¦}¦°¾ãŽ¢†@Ú¹¬âaQ‹ÀÓ}²E'Uí˜ —.]JkÂÞyçgžy†Î…6€2"œ“R @¹ð/IVWW;íGlŠ@úöíÛ!ƒs¡JKÓä¼H|6~¼üòË8§Kûöí›?þ9çœ Ÿ~úé»ñ‰"#Bþ?ñåâ’­½H€Âˆ}üñǤÒ±sçN‘K¤ËëçÄÕdY¯ú&áÚµ\‹P7zR]B³?³#z¿ ¬A±V…W¡bÃLëë˜p½•(Lj6Ó™‚ôý#IMjÕYx´29@Ú¹l0¼˜Š¯¶5ï £YÇÔ©S‰w>öØcgŸ}ö™gž r F{ 'üà?øÙÏ~&˜.øå•W^¹gϺI{ì±×\sà©Ä\xÖYg‘†#Ž8â¢øÈWÉÖ±K/½ôÔSOݸq#ñˆ#†~â‰'BŽÇñC† Ù¶m›mÚa;w.‡Õ¯_ÿ¦›nj×®¦oX²zõj|=ü‚ .À9ìZÂE|f·oß¾¨¨è׿þõù矪JÕG‰P{Þyç :fÿïÿ;餓°fnòäÉ|ðÎOŽ~ø¡ÿüópšâ¤G §TЊ+@þ@—-[vûí·#YŠzõÕWƒÃ‰â°Oío~ó×»îº ‰²0È;V¿ûÝïÀù „JDq„ûçO=õ¨0øåƒ>î ?®¬Dž­ ,î[„a$þü÷¿ÿ-#`Cô2‚†â*ö#CŠðwïÞœ•Ô‚ãþøÇ?ÆŸØÚö/ù „a[ïÞ½qiÕªUr+Àܵk׺uëºíl/×öÕ¡¼¶ÆUdñº kef¸r)9„@0*W0ØÄ ÉTAb|…^EÁH°YÄbá•‘Óþë “3ܘQ.&y ’ }ýõ×)¼JÜM“Ýð*Ѿλ˜¢ÝÃb½94ÙÔZ…@6¹lR¨h•¹4³8Åà }øá‡ñj,“x$RÀGS½¬ÇAd/¾ø"8+„mý‰ü© ©¶°Qð`Ù€Ÿüä'Äå X¤ †~Mœ€˜ &m»ÿÑŸðéâßuëÖQ°, @"¾û èåÇüõ×_?hÐ QM,ë„hذa4·v‹8Çþe'NÄ ü¸"—9ãŒ3¨\E‹x]ÊÀ4­ÙOjÕÁ•Í0Ÿ£dÔìð =¼Š˜+â¦ð` 6 ý˜ñHŒð*< ÿâ¿ x§t„WA-Þ)Ñ<ŒS:  xx‘…]î…ðª-Z<ñÄТ¸,Ù ¯’çL…U´»×%é×I”3<¸8F )²Àe5'Pg5lƵÎúyiƬ G&žàq\wÝuàŽÄ#¢Qm 35Rð.ž„…1äãÄa‹ °Íð¶’M²ÂB¡PØàe<Å`"–°X¸KqÉ+ÒÛ¶m ò)§œ‚숅½óÎ;.\èÕ«À\³¹fQ €÷$Ü$ YQ®ë%i×f[¸¬7©áÄÂŒ@0ô'+×ÞžóÐ,K =¼ äs¦ˆ›ÂÛ*̱ð†Þ}÷ݨ ŠÃÓ> HGxôSH!‰‰ŽþDqxö†33½OÃ$Œ—`AP=–§#¼J¾;¸vžp'F×®âÕ%’êWÁz>çbBA C\Vñ쨮F€ŒÉÎÔêáªi€sº¡ÙßiÌ‘GéKCI`Ò¤IBƒ× L11Q(ŠÃ¤Œ¡±âÀŸ²B×:‚Å^~ùåXþ…h|'‚äÕhˆ« ¸ þ‡W›ñŠb$¼*âkÚ…a^—|û‰fÓ‡2YIíDÀ·*Èâ™M1Ð4ÙIºÃ«ˆ)0¦b™Á½÷Þ‹WX¦ºâŠ+xà$"`‰>þ’Öð*ÛÉÅ€Œ.ÁmL`Ò4é¯R“Kùªæô›¾¹´vR®uN v.›Ô„+ 0ÅkfÑÌÉ«ÕzÄ.èdoêžá«Yq7r¢½~ýzE¸ª‚ŸíÅ’µ)S¦à­MëŠÃÕ`¸f‘]ãÑ´ÂÓl»h6ºk-Xy]òí¾91°ÙÈ(#¬È¥™Å)–Éð*ø>)Œž¦# [ÂñÖ[oÉnx•ëÔ‡'L} Ö±µ‹"ux2Š,rx•˜ý\û§k{éÜlÕÑÉâ¼1)îQMl#ÒÎeõQÃOÁÕ¼†Ÿ:‹¦f1òDÆà-•˜Ž]5ãÕ¿\/ÍSL1 ÆF!¼o¼ñ†4¼Ë³é”-D -B]±þL„8á)^s(0VË¡hø§]€›„¸]ÉYÔ˜»^ÕÌ⊭W?ñÕ©ßY’ðB@Í0¸%K2ô òÒœÉð*[Å1uÐF.´Á‹°0áU˜£°g"­—Å‚0xˆEp…³u\ëà]¦ð*DÜR ¯Âò{‡ë .‹¬—Ç2#+D‚˦i¦N}B­è«ÊI’È1‰7e¶¯ Ð’/øh3 ÇW%±@Á@&)(¬óôÓO‡$VQ`á‚«­Y³FÑAi¯ƒæÍ›;etʹÈo×,‚tÉùH– «›ÀõªN«%kFX»°0C/¬êäžÐÛEý˜¤À-@Fßo›=“<ÉèˆÉI=¼Šf¹ŽMßÇNè‡W¹Î¥`±ðÎb¥,¶ÐµÕ¼ a`Ã"¼Š”Sx•C¯Š(*è›E1cÛ ÐœNÕMŸ7£ž+’dó†ª9ój ÀbŠáª°Ðw.Àrš‘AIeÛÞ~ûmü9zôhÛLÏÈwl_€È- ™u²@ßôë×Ä` 6—¡—eð`-×1Ç#—NH¡xQ.úÒóÜöÉGÊèj$võ‚—°­,\¼rí¼²(î^®ƒÍµtú•kA¾ºÚl"ØRè„)YX^@X ¤ècŠÒuz¯søë”•”æ`³¢<äbxjÝ»1oc°Xy¥¯ NTiï<ÃcÉNláU‘‚ںΥ¡dQ°äÌL}<í0éC Ó\Öw8)F²úQS³¯¶nÝJf`„ú¶!ˆ$mšÅKv,È¥u /ÇbsYçvZøv-•…$ÔΘ1þQ#Íô=تª*‘"JfìÝ»W\ÅJ/~‘ýø(WëÖ­›4iB»(Ð8h!Ú¬Y³u€ï8 ¸Þ|óÍø4]¤ßrË-âë˜ï¹çªi{ٸΆ䚅Úë>êÕŠfòÍâ5Zè wà…E’ÂµŠµ¹"ÉÆò”œjf QÌkÛ¦#üVx¾áâÊá\§²¯Â +̺ýë_1C*ê+ 7 ekÖ^EÑN…êÖq5 ÙIÌ·øÚÀó#dˆËúÎ ŠÉZ£¦f_1@8¥Ø~4bc)×ìHÿ¿ÿû?2!Vø”€m/X¬v ¦6mÚ`CèÁK«n¸ôku噗α9+®‚éB 7ü‰/¾R¹xç… n‰˜Â6l^#ö;D ÎËÊÊH ü¸È.”Ã*|…‹¾\@”ÐWH3¸&Ew!~⸠žx¹†D|·Ó:>…€} H VcKH,çž»¤â« °Ðy×A \³¿üå/åU_rÅ!`k\E3y]ÒlYgY×Kj;mfûþ™Ibäk è#î†s޵m¾½²kŠ IE¡¾ªœÃG?¼ÊkèÑ;6ºv¦3ÈáU64ÔáU‹-‚|«V­’š.œ&Ù«¼&:Eíä,B¿¾U:­¦è{¾ÙõGK2™A @}#OÅz‘úÍ7ßè+Ñ4&D1…ª`—d$Îñî)À~ŠX@s«Œ >„‹?ñµxRA qw©¼{@KlYH-JwÝ”W`Û:‰EEE"6^aÔ‚¾y+î—®-+½yóæ—^z‰öhtV\­'p¯‚|-W”””àªó‹—HïWôpE8AMMþÐ`ÉÌ J{‘’o¿ýÖËfÍLή™%E1<ÓOÀù( ßyˆ"0?à*^ÝÈ’¸Š×Dø6B¤ð¾^~…‡ü‰Çu‘ˆW@Ó¦MÃ{*z¼'ÍÈ.ˆíºÄ3žÞ!w€ØVª7ÆŸxöK 0w‰x<Ì÷ïßó«ðá|X[_ѧ €¶P4¦8¼ ƒK¯Ø`›Š€ª>}úàdùòåÐÛ  ³%¥Èó¡­±ð„OÆ`·ZXî;óxµZ²3¿Në'«“Þݘú23`¹F€È_ÖnÅ3¢mŽÐ®j1Ås§ú‘Ô×N×ì˜û0Ý+¾ æqÚ VìnèUœ—‘®éH¤e]‰,]µYJDæA€&‘…DÅë~鬻ºiH¢|³û øvcY Ý.½¤ŒaáÔH±AEïÒ™Áäά?çhNz 攕ðª‡zQXªyóæ¡ú?ûÙÏÄp–ëä)YláUtU^%ˆ,$ñÞ ÿ‚t‚ÁƒOãœV8€Î"à ‰ØÇ€¶Ä(þãÿ@l•oxUÄ^eƒÝ«?(ú‰o¯N@gêã…50™D ›\VsrK,ðxö5ÀwŠq½yñN£ÒÏ¢¶Ùõªf¹wºfyï½÷pïÁ%8BÞ|óM„XøfQß¹})fà–µÝÏ·h×±š"éÉäøç²’E ÙÆ Ð…4³„%Fz²^Nyë­·Þÿýˆ‚ ¼$ϰÊ^%>%ˆ†s†W ¾¨¯£áO8G¹Xæ…£D,Û¢]FŽyíµ×b,"¬(eöìÙ}ûöEð‡W%;dXžÈYˆ1°=ž*%uêï«M- ¸šŠæ¤Ô6jÔ5•ß‘Ù*îªÍ«ˆ¤Š¦i]sRECjÇŽ‹Ü°Õ9Âm…77Ù†NþÂŒ¤€j×®2ú¾hKjƒŽ1Ðà– ¥9¼J´š:¼J ê.Olè|G”ÔÔa×"j K²œzàÖÅgim1]x‡%"~@l+Þké̽^•á‘ËÅÕr2ç— Ë‹@ ¦£d\X‘]­Y\õ"y^…z©¥-p|öÙgN ëÔæ¥'@’ÅG±ãÞÇaEšmó/× êÔZ–Ñ1CÍΓm#9"YwŽN–‰8¡4ºb:»½/ ¾ÚLò”›ìÈÒ ¯¢ùÑü¯D6€®³%…W¹YÈ#]Ät‰j"¶J„…¥­¤æÞÚ^åÛKY€´ûe¿þúk£}pb¢QkKÊÙ&Tù–®ZÒ†ñðÅÒç¿é@´¾ëZA/êì…†‚ħ5 |xg‡ÒÏ<óL×…ÉVD§ÒÔ|2P ¿,mÄG-G@ö²ûúe}{¬æt§)lú"Š™ŽùVh†_ÑbQW²v˜LÒšáUàîXR§,vžÁ‡rdô’­]Zñ÷U.¬Õ|%Uˇ?W?ëd™ËúΕi¬}K0û(  -­3¯Û°¬ \ŠoÓ§Ò|¾S¹C·oß)¶f±YŸ¿¢f1Z/.ëÛc}û¼&7ÒÑ`€Û…øúš‰Ë>öØcÎ L¤úåÆGQD-¯ŠÚHd{j™‹1çA¯—5¶™Z-&®z>°µ¨:QE¹:—äBhóÊ’Å¢ÅÔï„TQAߊxÝètí9ÎÁìÛú¶šzYËD¶¶Í•:õu:Йîä™G]Ž6שO1Š}/éèLPrÕ“ G&R>ýôS¯YZg\'[®€ÚgWm:w¢Z^¥3.X†H7™óËzÑç,æ;•{ (Šð-]-@s²YÄ.Ùú†U´¸¹’BE3¹¾&PØé{Év³ôª”ðË2…M÷””7úó#¼Š˜Ÿí <5á«ÚØôê‹/¾ ÇwÜÀm«¾‚Í?é°Vg¬…áUê­µóf sEr´sÙýû÷ëáKtÂR5L²¤Pad²ög±è`·¯ ú¶là§_žê[´¯ÑOˆËòÁè# æ²auÎ…f·@ LI•`®€OÖ³ø6oç ”_w ¯"aæ²ú³K¦,ÄØx¤Îë0§ŒxéãåT¨¥ W6ˆh Í5K‹³§+,^Xy!©®ˆ­,×9TQ¢ºùä«êÁ£.Bä•;âe]·åJ÷(eýù€Î¸ðí޾ɎV[W÷5lˆ³Y²5 ±hÅdÖ´ïzCtÞ×t:O²@i6e~Œ>®E~#.«ÉT\¡×á¾^m¦3Ô…ÚæZxå„>Ù,:E«ie¸Ôj¢éK.uwÞôqÐD˜m~ω©Ôk¿œÛûvlß§;_ê54+u‰©O¤áÎ?É>¥{MV:p%;çЩ9Y…Õ÷Rœ—Èâ²ò3¥ë”ªù$àiUS³æª¦6ÙN¯,:3µ>{v-EÓÚêRä[B€æÓ¤¿j ëk¡+øbÈ3£ÍÀô”CE8Ylˆ4ÂëQѱuJ÷R«9èô[R¬WgbLSÑŠgº©?]§òp"ê(}Ürh豩ŒH;—UL—òxֈיҡYT1þø– ñ)p\Âçm4'²WìPK–ˆ¢aY•ì]Jg¦v΃X†Œo©c3Åd›Om¡oëܧÕ÷cWú`F£åÏwÀ-Ÿ²¸úb}+¨3â\e< ºööd,“5ÆËN×ô•Òáñ^s²~Ýu¦kY›º"ú3›ÓBçítLŒ¾=™( öµ_‚ðÙæS¯Ê{a1’Õ¨)²¸dË‚º<ú裿øÅ/°v«V­°•Ì›o¾‰ïÙ³Vщ µ«V­Â*Ýûî»L8qbïÞ½{õêµ}ûö?üðá‡~úé§éÒW\µÁñ寮]»â{ßøoMaè#>¬…O“3¦AƒøæBãÆO9åØÓ§OçG 3cR !ô  TäÁDv|Éß÷zàJ^%šÐ‰ÉQG…Dõ7l“úü)´1 ŽÂ h³!©FôjARâ\öªžÜ%ðì—Šæ…†•%,=ê±`F 7‹Î”ø¾æÛmRœu¦¾d6©¶!Q.`‚ð|š.AdñQÖ{ï½wݺuøÜ"• Ïè-·ÜÚ Žkûd×ücpV°XðZ¹Wácß'œpRyä‘å¹çž»í¶Û@Iñïå—_®æ— ²W]uUëÖ­ÿö·¿Ñ—!B ’ ý¿ÿýOpYQw/>&®žû4Y/¢î¨à²×w.ö½OûöÀV}ôÑGûrY*EŸ 1—àl«Ù|ê¶“¹¬oï 0Aivøp5Ц9™è Ïd§—­ULP©4®oÞ`µmL锢.ˆ¹l§)6ɉ@Úc hœÐáÚ¾—Ô³˜ovE¡6Íj;+++Adÿú׿ " Íðª‚Ë¥³ÍÈäª<í´Ól—ð±o§Iøâë¯~õ+¤_wÝuø~ŒS›\MxdÁYúÓŸâc⢠à¯ð‹Œ²ü²eË@ Õ áUw¯t0cQ¯&Pã)îÇêŽá;ë”âÛ Õ€'5q{ãœT,œEôÛW15¥Òùu:¼ï¬è:¬Ô“Ù,âšDðɶÙÏÕ…åÉÎ?Y¯Âû·§žzjùòåú÷×É9Ùì:­¯?ÝijËâpã¢MÒÎeÕ“¦óª]¾',Í:3,dþùÏSxCm‚Õ]ýõòô¡_)¹h᣽úê«áõtʼn˜Co¿ýv×¢E [ƒ=ÿò—¿´ÝEp›OŠnê7A²±| WßíÔÖ×Bß‚œ–kêÔWúŒGS!‹ePÚTÑÍÔ=ЗvÓì•Ë™Žçá?þñ—\r â‹6n܈ÏÏâáyÍš5x%õÑG¡u05ážÆñ„Ožó‘‚t|'?ùÉOð5:y…«xy…“xAÿ6ªç5?ÀŒ)S¦\zé¥x?†­òòr¼ž‚?þ8Ì6÷Úº–×ÜKé0`æÌ™]ºt?~üÞ½{uH¼úîà;YéOM¾ÄVVÖÇÀ„…@Ú¹l²C]Q1/B¦~ õÞ:V6†‚\mTt»víÜËë’3\³8èÞà„Žn”Ž©Ü91!*×–8þüE‹yMÖš­£3úN£Éîì ¾EèØ©EX£+ö–1¬'0©·càÎïÛçSÔ¬9@àÀDÁ82ŽXðÛßþvÈ!“&MÏÏ`¨x Ÿ>}úù矵x[… ,¤ ËpòJÅ!¼êÖ[oÅU°X¤ãO,Àë&ߞ«Þÿý'žxâ /¼ tèÐAÁ5uæ"ßæS´~~LŒÆ#ga²ÆeõçSMªá;í:ç,^’à— Áxï/¾†°WÛº4׉,öÏþ3isÎ9 ¶6D°HWÔgžy|ѷЩWš³ª¦XR³¿×Lͳ#>tž¯½]ý›E½´©õˆ© ó vÔºçž{.¼GÂc-öySMRÖæ_x^ˆmÙ²E¬Vî5Õ«tú’m®Öq+3&}C†53H;—UŒ(yÜu° žÉ¢ÈèÜ óûÏþs|u–d°o€\ ÍMŠʉŒ&ãwÀWú»ßýNÐY›%N3°qÁ€ú÷zÞš©Ûˆ2 p‚…h»víÒ‡Ú—þä›ß \JàAÅ5¦>™é1…ë4Ùùïsð “ ÈL}£FrNŒÂ<¯OáU¨,^m!®€ˆ¬þÔ¤9ÝiŠæI ÛšIM¦yD3G í\ÖkÚuŽ_ÏA€,®<&€dY¼x±«6D©R°Á_|¡žË’º¯Ès“m†B”­Sˆõ5×\£3=aÓƒ &À)+/Ò²U6öÂ}œÍ÷Þ{ïaõ˜ Ãk¯½öÜsχž6mÚþýûõ½ÉNý&k}c\Çj€#>æÙ¼¬  è‡ê.ê:FÂÊ¢©GŒwlÂ5cÆ ¬ ñFÑJx%%϶â§:ÖÚ ð-Eí¢^*ÿÿ÷wÝu—˜'õñLqÊJj •Ë 6òèf"Ž@Ú¹¬×Èq}.Ôt8GfRÓh°¢Q(ˆškÑpU"ÝËÍ©9xÕÝ5¶¶µÝóèÛ]Xƒ…±Ä%:ß 1bk-è'ûŢܭ[7S¤ Êuþä“OfϞݲeK×ÛOëÖ­ó›ßàÖ±½ôÒKŠ™Ú•ñ{ÝÒt„]%ÀÀc_EÐ8‹ Ò ÏØlÃܵ,WmI=ðÛfç`D -îøÃ0?P:mó'ôàD1r“š®Ii‹NxÙƒÉ{ã´r{Mbê1’+MJ8¬‰‘Ç8#}ÒÎe5‡w²ó~¸3µ×´k+ëý±LÕæÀ²§E‹áM¾è(¦]dÄFŒø“–CÉzÄj ±1­kÝÕsÖøñãŸzê)ÚÂÝÄû}Ì­b£ÏØÒ%¼úÇ¿øâ9,¡-i)"¾T°XXŽu‘’·ß~Bü¬ø`:%bšÆ‹Å’’uDÐâ g¢DÛ °Í-eY»v-]MjòMJ8¬™:p¡ÑÞlatP<)y]J%‹ëèpNtšEËV" f ñ BõHOeº–mŽ`x¾GƒmmP}¼˜ÂÓ¾¢Él]QÚÑ×éU„× N=:7åè +¶„ 2Áe5§KÉ×uü§2ãëOî5‚sD¯üíoÃ8®¼òJÌòð’Ò'µp€ª"”ŽŸ ¹Ø`¬é Ž8Á§³¨¦ðj€CXTÜwÎ"§VVV’POèÄZ®K.¹¤qãÆe£±„NZ•…‚°C$¼­Mš4Á†¸Y Ÿ !U $†Íñ¡ZZ † ¿à^%*L*Ži;ì0臃l˜Ò9báô}ë­·(ß"¨N½³IúÖZ`; œÑV®—~NgRG@MY\é‚"KR¤0Ä¢±¶Øü_ÆD3®É £ORW0‚áU¨6ÞÆ®ÛX-p÷Ýwcµ€ºÏèSX17&Û “*«i‚ÑßdMeyF , Ò×e cD™VDɇ¢D¯K®éjË“Íâ[4È(v–¥ŠÀs‰]«Ÿ¢‰¢ŽÉVP§ dÙœShn*®»sãnBX`€ é ¬¤S0j„÷†ðµØÚ‹$‘ÝyÉuÔïš:O±ˆ°²(¨Â3ƒS¡XÖ­_q–ÌohêÃ÷œ]«™ìÔDœF¿'‡;ÿ¶-ZÌ™3û¢Ì?¥¥¥xÂG”¼˜ðòê¯ý+\¶7²ÍЦ`-<üûN˜ÎŠ Ry¹à‚ œfàã…x+…åªdòb6à ‚þiÃWTX‚ÃIdE¡¸ä@x«ìÁ¥9Ù*”¤šÈªÅ[á”TN…)f×±e&¤f³pç§¶1cÆà‡¶¸&¼«‘ÅGE\2æ}xêøù矣­÷ìÙC-’Mü<ŸÑѹå…2+ŠA0"˳"OYyƒÀAb·ÿЫtóÍ7C'–yziv~^cR1VÃÊ¢ž’²–¦׊ûN:j*”bv…åÎÉW¿ÏøÖ:©N¢YnàBúñr‰ØMÓy©S§Nšö°X-A€¦>x%LIe 0؃eùöÛo§OŸþÙgŸ!Z À –:½!’þÆoÄÇ®Q_øhÁb{ì1P^ü‰õUUUØSqMxçƒt#P D KH¡ä裴OÝ7Èìï¾ûÑmÛ¶!¶jë֭Ѐ¸|¸BáVÚíÚµ“•àOˆÁNÈŒ1ÏêäÆN,Xl€Dl¿€*T )¨¢ü)dμlA)bQ,(;¼­ô<Wî³Ï>‹ºà6 ‚‡~8¨|EE}‰Å@çADŽaÅx‚ œQmÙfžújɬ•ëÕL{Œ=¿ÊGR¤0ØÌëÕ*IÝ'Ã>\Ö«3¿øN^€Òi'’¾–ø¶µÎ˜Iª”`Ñ1CÈð‹¶¤àªåÂy^…Ö´Å5mß¾‰àj¶×AÉÎB:Ã\–áðªTF–ÚNý<õ¥‚9çÍâ²I‘Èp™bRE«ieºgêd9Y²ö¸ö*Í NSL‡—'û¤¡9’²PS'sÙdby `ã²aMhaém””[¹23—úÎ!¾#=PÚ}IýÙ>©"WÄkä2—å9-'H{¼,Æ¡s(R¢-Ý5QžvƒeqDW“h¦Ö·VL¸Š,Š ¨¬…²I¾Ï·u½h±¶Á ð­…/:X†H:c\ž…¼þÄV/=ŠI@‘Å×*/r@§)úÎí^EÛjŠXR:]9h€é4ÅBÓ1X'#ÒÎeuæňu½ä"ëõ¥†:ä2Y Ï:ƨ3túnà™:c6)tªÌ2µ¤f3*l3pýGS¹Wß*ßYQŒô`Úã7\1J«ŽïØÑ¬ˆ¯`r´sÙÏè¾YMÇÏ¡xr ×?¡óˆì;û¤è«i_¤œ jcÔ7×yõG¦ºèëaIF@1úÔ¼Öµ‡'›Åw"u–â›%”¹×—íùŽÁÔgEýçaß)ZÆ$)a‘Ñ·¾ê¡¬PžŒ@>!v.«ÿŒî5 #âl°Íû¾Ó‡ŽCÁÉÔZêÀ“¯o­½ÆFàŒòm/­/E Ój+ÏE¨GéŒÊs…N¢Õ´2À,äZkÍ Çw ¨µ‚|‡H}-W?œëÞÁ Vçb"Ž@&¸¬ú¡Óu@xìN%‹¦³AçQ^gö÷¢°€Ê‰™ZAÙÃ#E_…ÎÀË@:f°LÞ# ~P×'::d.©§n}˜õª3êLªBF¨(OŒêêøŽ…ÌPØÌ”â[Y`4H;—õåmšì^ã_‡;Ú°7Kê3u€ûÏÔšýÛKÌ×{”¢~ÎÎx=)eà©[ó©Xžœ5³¸N×¾“¼«€/[ ”×T¯~œNê™6)á¤àÊüPÌûÀCžÈ"iß“+‹uã¢pào؆‹g¾j£=¹°ªÊ(ž¬\/)(…‚>&UŠ/És}lV7\)夰:]+)Æ–”°fuB7RGa² ŠïP O}Á°å\C í~ٌՄ bF R„ëìÌ¢£¨ê»Q]Ù’æÛ§`\*ÑפÄ‹ŠöÝÚ L }GAŠúêgF äÑ/ëU“… f¸’\#>ð¹Ëô)g͹ˆùewìØáj¼]SÔ4Ù,aym‰ ª› €óXæ|ÉÖÚ•(ëtߊÈJ’%£Ô:Õô•I¶jì—õ…”¢€sÙ(´ÛÃ0—`ãG®X±bèСY±¸,Œ@Þ À1yÓ”ùZæ²ùÚ²\¯ !1.[UUµaùV]»v-))ñªgeeåž={lW4h0`À€ A“bV¯^ýÄO”——gÌÑå¬%sÙl´<—™F˜Ë¦\VÌeÃ@‘uÔb2ÆeáküðÃÁÕÆŽKx7núôéEEE ø+**† E‹eËO™±ÞAô}Ê”)Ë—/Ï"—õª/‡We¬'pA@ cS_êÂEä:ü.,×[í¯-÷ìÙs̘1›6mêÛ·/ª=kÖ¬3f¨ëþ:<~¤Hd÷íÛ} árF5¯½öÚè›Ê2Œ#À„…sÙ°d=Œ@†@\Á]wÝE…Mš4 žWuÁâGŠÆÍ;7E ËÞ°aÃŒ•Å1Œ#Àdæ²Yo6€Húõë‹<!@àAÒ*’É€ðØL>–eF€`ÒŽ@âeÓ^'.€Èw°¬sçΈ—E˜ꊃyóæy­+++ƒ ¢H£ XM•ìn>‹K1£ŽšÁ/›bËröH!Àñ²‘jŽZn ûekyàêç0¿øÅ/@gQ,uºîºëÒÒ:gÎÚ€F€`F ‚°_6‚Â&1>_výúõðÅ<˜–íOž<ÙÕùªðË‚þ®Œ»v킆~ýú 4HlŒP]]=uêTrýâÀNtÒ§OŸuëÖÙ6üB"V§áª3~W^v&_íСCii©\UÔkÍš5k×®Eb»víæëµƒ¶,xî¹ç¨¾´äG ã]ý²¶ʰ7ÙQG¥ØÑ,]ý²é@•uf öËf y.×ù[v|Î09X,3þ…µb[¤<òÈ#NûÁqq8Ó‘Á È‚“U«V‘‹)K–,!aWÈ+¸,¥lß¾}ïÞ½³gÏ Rdý‚õB›í©%f,gBy¤ÃØ€½ «PA! m mpmô'L’SlUž6mdPŠÓ¤œht6’`FÀëÖ #Àä 2—…Í`~‚V &*êâÊeIƒMXÐV™> bê‡è&qJÛ!˜¨ó %Ò)Yȃ¡Êå"QÐk‘Î ËE¹8‡Œ`Õ`«^\–а•›+ÍÍv2Œ#À(àxYöÖ39^ÄýJոᆠ®B ®¼òJ;Ûü‹.ºH88u@¹æškH ±6ù+®¸)ˆOÀ¶KØPlÈ!r"vÉ]°`ö…•_ú#Zu"(ê@òØaa ¤ÇC=„¯E`Ï]p\ñ‹/¾ØÕlrùå—#$uêÅ2Œ#À09„sÙj,6•ðD`äÈ‘ô]gØâÅ‹!æÜtL‘ Àb/•d½{÷&ù¥K—Ú,Ã÷u)åù矗/Ï–9+R°K.»wïnS1ª81^•¿ì²Ë(º–#vÖ5þú±0´;ůEpçcF€ˆ&Ìe£Ù.l#4&L —òðqªwàZ¶lİ–…ÙQª¯s’à‘ô~»ÏÚ¸ï³Ï>KQ­wÞy§\-¼ª”I:±­£DA—±@͉ˆkY ,ÿ¶ÛnóZC–4Êœ`F b0—Xƒ°9Œ@Pè¥<1H8VAU½4Á/‹KøÈüš¶¨th®ñïß¿?•"sMÄ<ðÀôq28€e—êüùó±ãl¶#PÔ» ÐUìT,0ØÐ`àÀݺukÞ¼y²yYž`F W`.›+-Åv2þ€€Þwß}$7jÔ(×÷òÄ/ñ/m‰åuˆ¹Ô¥";­Ä’©3Ãp÷â½?]l›gµhÑ‚¶î„EJM68ŸCà PAÎÂ0Œ@-G€¹l-ï\ý|CäRì< ^¶aÆP*/–yQX9e±O-ÎéœÄ´ \Î`¯Bu"t“2²n‹ç®·I©baF€`È"À\6²MÆ1‡£0VZ¶sçN›"ŠC”7`1f63@‘¯ä”%·®¸D+ÀÇ•Kl©#tÅ~aIY+bˆÁ¡Á¤“ÊËÂŒ#À09sÙœh&6’H|K¬î*hï-¤{Ñ;'è¬ý"…ˆ ÷'¨*|«ˆ( §¬| +À å:CÎ:ë,Æ¿œ•ÎZ|“,9âÒ(î–[n! ô+ ,ÎÂ0Œ#˜Ëfv.”H/àpØxU|ËV˜ØÀ•ÞÕ\ûEš‰¼Â †"À¶“ÊËÂŒ#À0G€¹lĈÍc\ †çúR^–Ω[® ¯-bI±kUAAAýúõ{õê"+ïÆÚªU+ÊŽÐ[pMÄÂîٳǩpĈH‘²²]úÉO~âµ7‚ˆjŶ¸rαbmŸ9ؼy3éw‘ Ïóâ?9†QÐ"+æÌ™ú:3#À0YC€¿ðË0¹‚ÀöíÛᡤu]tà)HWTŸ·ÅÂ)§À¦M›œ ª ì”$(Jt- 6€ïÝ»×y—Ÿ™U‰Œ¢^T)ü …8‘sÁ½JN\ÙW’ÁUøž8¸$G\àu¹Òôl'#À0Œ€¸5Í3Œ@F€ƒÓ+þ—Ö®] +4hÐ¥K¯Wùˆ+€;–¶¤õ2¹ªªÊë*º:Ÿ…'uݺuä÷…?¸sçΚÛÜfE.„`F Š0—b«°MŒ#À0Œ#À0:p¼¬J,Ã0Œ#À0Œ#E˜ËF±UØ&F€`F€`˜Ëê Ä2Œ#À0Œ#À0QD€¹l[…mbF€`F€ÐA€¹¬J,Ã0Œ#À0Œ#E˜ËF±UØ&F€`F€`˜Ëê Ä2Œ#À0Œ#À0QD€¹l[…mbF€`F€ÐA€¹¬J,Ã0Œ#À0Œ#E˜ËF±UØ&F€`F€`˜Ëê Ä2Œ#À0Œ#À0QD€¹l[…mbF€`F€ÐAàÿC(P{“ºû´IEND®B`‚opensips-2.2.2/modules/seas/doc/images/image052.gif000066400000000000000000000333761300170765700220140ustar00rootroot00000000000000GIF89a7Fw1!þSoftware: Microsoft Office!ù, ;‡3f™Ìÿ3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿÿ3ÿfÿ™ÿÌÿÿ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌffÿf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌffÌ™fÌÌfÌÿfÿfÿ3fÿffÿ™fÿÌfÿÿ™™3™f™™™Ì™ÿ™3™33™3f™3™™3Ì™3ÿ™f™f3™ff™f™™fÌ™fÿ™™™™3™™f™™™™™Ì™™ÿ™Ì™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿÌÌ3ÌfÌ™ÌÌÌÿÌ3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3ÌffÌf™ÌfÌÌfÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÿÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹hÜȱ£Ç CŠI²¤É“(Sª ‰±¥Ë—0cÊœI³¦MšnêÜɳ§ÏŸ@ƒ J4h΢H“*]Ê´©Ó§PJµª@V³jÝʵçÔ®`…b K¶¬Ù³¿¢]˶­Û·pÕÂK·®Ý»>åâÝ›‘¯ß¿€õ¶+x°áÃf #^̸±ã¡ŠKžL¹²ÄÈ–¹ŽÍ̹3NÏg7ƒMš"æÒ¨S«{zµë×°“¶Ž ™¶íѳoÿÌ­»÷_Þ¾ƒ n8ñãÈiOþR4óçqÛ:-W­óÄË¡k¯šÝéל߭Oÿݸ½¼ù†Ý›~°yzÚëçã›OÏ”:ûõ×Ãg-¿WúKµwTx}È[ú§àN Ê–ßUã Hà} V˜\ƒHEaZWDÞ~††EõÑ{æ×ÑZ Šè¢W¡=˜¢Œâg v/æX[bÎØ#„¢¨ãª‘G YÇaqD6Iš‘bY¥“TvXeDS^éd–ZvéåA\~)f—aö×â˜h.™&˜e®é_›nÆ"œrÖi¦7â¹&óéY'Ÿ~z¡ „Ž èvgJä¡Ú%ªèŒ>*ék‘Njii•2—饮mŠœ§œê¨‚Jê©‹™:œ£¨Ê§ªp¬¶ÿß«²ÖŠ­¶æž¸êÚp&JØc°Ã Ká±Ä"kl²Ì.ël±Ð*m¬ õê+c³Dmpëí¶Ý†»­·â‚Kî¸âvkn¹çªÛ.ºç®ûí»òº›.¼ìÞ[/¾I”`íµ#>TB$µlðÁ'¬ð 7ìðÃG,1Äþ,gnL¬ñÆwìñǵ”ൗÛÀ §¬òÊ,/,rK$—¼êCÜòÍ8çqÅ2ï)°Í:-´Î<÷l(D­ôÒ*¿ŒÑ¿Fïu2Ð)G’DÁÝI w[×µ\mõ×WìµÍe#|¶ÄiíôEPG}+ÍT§\BÁýb ¶Á‘ ÿÜ7×mDÒ·àÛì7·µìmp·‚ßqÒ[sÑr{9Ê,'Að×\;n5Á’ã6á;^K艧®yÙm€ÝúéY'Þ:ØëM0¸7S^ù•ßüúê!'®9Ê%h¾xí§›²ñ¢ßwò̇ÜúÝÅ·á8õÒ[/üä#ǽû®až¹äØûºõ_‰F%ˆuû×’ôë[à÷áÁ‡ÍõÕwkþùÖ܃™÷¾÷–lÕ-eÛºÝëÚ×¾¶I.pÏk˜äøg»ÅMÌ^ÈúÆÀÀµmeº#à–~–»²%0²K[ÿ¶¦¼Å©0xó³퀗:Óå-x¬ƒÝä(ÂÉôÎwž ¢õÿ6÷5”A°…¥+âí–—·¾mëzãB¡öpÈ­» .w#ëás¦Æ´Ó!,mLµ&?/òMq‚##ßÔ˜Æ5¶ìmá¡ÑbÀ.NÎŽ ást¹0âÑcüãÄô¸GýPˆL$+³Bb "îK¤$™¶HÓÈÑ‘`©cи°èÝÌfœì"!1Y¡>N²`‘ä˜'QyÊ]’”AÚÐø×/u®_›»]á¼¶:ÖïtO$âÖpÉÁ° eã dŸËàp1hüãZÍ>w¿ "Ž[Ú+œÿ®Æ¸jŠMlØÿ¨w5Ä ­’ye3³¢I¢åïz˜<[.ÀíÏjà’'+§ÿµl¢òu4¢âr6ÊuºŠ„ÐÌ_ÒhH½õÝK)$Wè2¶MÍeÌq¸ (Áb·Iuô.‡tçõPOkn4\Û @‡·Ïa&ϘÖd%2ºL¸yô£Þ¤WFÁ2Ž®ƒÛÀ²†8~eìæQÃVΰÁoƒÛ;g÷pj›vÚ1ƒ­TZAKù,iyµYÓê*X¿*Ö²†õ¬dEëXUeÊ?N1«Z½éoVB׺Úõ®xÍëI”R<î®oÌ¢ÉöøLÀ„‚¬˜9«ö±Ûê‚äúÆÊ¤­ÍìÆ$û&ÂÊR³ äTg6džö´ Cçe(kÖþÊTŽE­lKçZ©ÿ ¶³_ûtë/òö·¾ în…ÛÛá·¸Èîq•›\â2÷¹ÎîrýU[z–fÈ®v·ËÝîz÷»à ¯xÇKÞòš·¼H,¬®ët€ð¯|çKßúÚ÷¾øÍ¯~÷Ëßþî7½0ûÔmùJ³÷ú÷ÀN°‚¼`T׺‹Åm{ Ìà [øÂίƒÅ^ôdøÃ ±‚ü´ç´´q¯ˆWÌâÇ—Ä6=މ cÙ˜d‹Â.αŽ¼aG˜ÀfñüøÀÁ~±âk## ¾Pr|‹l'Ã×ÊS&ò•IÐ"«˜Å=ÖÑŒJ3w È$xÀ’±ß3[¹ÿËðU3}ß<ä“ ÊF3|Ïç%Î\fsˆaGw˜!_1Ÿ÷L‚(Ï7»xï¢;€c#ÃÒy¾³Ð,h=¿÷É€& ALhF øÇrH*ýá ÒW†ô’E}g4ÖsÞrvãœæ3ûy¾®ÆõŸÝÛ*·8Ìb阅”¡‡$zÅ·nô¦7Íe5‡ºØ˜µ|£|fJ[™ÊŸ¦5¥)mP#àÜz^1²_´l ‘çÔª6³|‹,èiyÜvf4­í­â'ÃzÓE¾5Žè—Ú’ŠË„ÞgÐpvt¾kMíjOyÉQÆ2µÁ}e6OúÉæ6øhI[ÀvGÂèÿau†]}îOŸûÈ¡ntšÏè*# Òlöw¯/î™·<Í×ñºÙ=“ŸÈ WùÃw¼_¥ Ýè*:ÔŸ‚1¦[ýêü=x: ­ðòäÆáX{ص~x»eê4*QÅÎv±ÝEËÖKÜQŽh§[ÝîmÏðÛçDê (U?vÖñÞtVÏzÔV';–þn”ºÈìuÏû• ¯_\ËñXW<Òßx©¿»ït¯å,ê[ œækövmŽ€M»å]>÷šÝû€ÓÓÛç~¾9æ?¼w¾£ºÙA²¨ßËm5[ä]~¯½Ó¼d ›Ê8ßr¤‹½åo/äÍ×¶º9Oîs=Þ‚?þÿäŸ|šß\Î=ÍýdçsÙæ@uÅÝ«b9[½Î<ôBÀÞjZ˼ל6| Fi×|¾FiAeÆqþ§f‡7|µæbšG3§%57~[&€G{~¦]šÖkÔÖmâ§]TrÕ×ʧ}"Ö{¥2`©–t-Fp èfrfk7‚è÷^µjØmCFmh°†f»vlÞ—Iú—$bs’—c¨jIÈS¸^Î6z ¶hO(t#g…_rͱv[8†-X…šƒÀ'ƒd¸†zg†Zᆽxl8‡…èñ}=ôuXH‡|8_v(È;¡—${؇†è‚¥„†j|†Øˆúÿ…ˆ ‡º1Î7n–x‰˜˜‰š¸‰œØ‰žø‰ Š¡ø‡Õ"‰Ÿñpd€«ØŠ¬øŠ®‹°8‹²X‹´x‹¶˜ŠÀ»Ø‹¼ø‹¾ŒÀ8ŒÂXŒÄxŒÆ¸‹ JèlGöŒHÒÔ8ÖXØxÚ˜Ü¸ÞØàh]˜ªpŽè˜Žê¸ŽìØŽîøŽð¨0ôXöxø˜ú¸üÈÙ2ˆ ðYy™ ¹ Ùù‘y¦ÈÎfŽëˆ쨑¹‘Ù‘ ©Ž)ôèøh’÷ˆ’ö¨’õÈ’%y’0YXÀ4qÿ)‘:¹“<Ù“>é“écªfðX”Fy”Hé$ÉLÙ”Né”þØŒªö“TY•Vy•yøç_$ÀŽ_¹Ža©Žc™Že‰ŽgyŽiékù•K9Ì€qysiuYwIy —rYR`RÙ8‰•„Y˜†É“Z`xX-D™”Žù˜Gù–O9™” •ÿˆtR‘“‡¹™œÙ™”B‰hé)’iš!™Ž¥©š§‰Žà’ó›¨ ›´“+i›-‰ 3Y“ Ø ¾ œ¿ ÁIœÃYœÈyœ`òƒé™ÎùœU šÉ†]ìxØùi”ù˜’Y™àžõÿ•á›æ‰ ­ ‚`žW í¹ž¾yç‰ ê9ŸÅ©&èú¹Ÿ:™˜%Æp¢ÉŽÙ•7×Û¹Žp€Ûùâù ”ù—-1Ÿ‚ÀíÉèi¡î Ÿ­Àê¡W ŸóÉ$Ì©™üy¢(jþcš$£¹ŽÀ è¨ p4ª 4Z£9j£éxàjæ?ª£çˆ£ñ¡H žä‰‚A¡ðy­Ð¡öÙž"*­ŸWzžË©j&š¢^ºŸÒ9 ÷¢è¨‘ç8d° ps Šmê µ‡t P£ „€¦ º§}*§pš§ ù–µ™’¸ù’ˆº¨·ÿ)“4)…A¡íiž­ ¢ç‰¡š¡YjŸ[*˜]ú¥ ê™aJtÔÙŽ1zŽtZ¤çÈ kº gj¤‰¦Ø©£  ~š¦1ª ³ÚŽš¤¾šKjöY¡ŠžÀÈ*Ÿ•JŸðI¬–jž$:•¡:­Î¹¢…Ö¢ §myª@jŽi6d Ê ¢v`Ž1ê`g¦ _  Š1Ú®}ª A*–Ji—}I—ùНúÚ¯ü:ZžöŸVú¬ôi¡Èzž•j°Ñê©Ôú°›i­¦¶˜Ib êÈ­©j«ª”«Ê£]¶ ë®EZ«rʭƧ«dšŽ½ú«,KÁºÐ¤ç ¥¾Y©Rÿz©æ‰¡–šž#j`\ ±BK˜£JªcZ¦›yzÚ ² Úe Ц `£«V®Ö‰·ÚzX ¯ÝzŽ«yޝIQÀp&‰¬Ç§`’&ÀôØ»ÈZ·õh÷ˆQ“»©ð·+³æ¹¡šžÚžËê›îY¸ ŸÐj€ @«ÍY˜p¹™@‘—Kšk °¹ pº 0Xù©¢Z‘QA‰ïˆ‘<êØy樲›¦"k³{Ž›†‘æØ˜×©«Eš£ë¸”}ÐóX€ ,Ðö8-@·ó(½} ½•‰¼ö(Ë ¬…¸Þ›öÿ¹°YJ¥ê¥ç¹žâË©!Þ¹‡Ùœð0¿Ù “¥Kù[õ;`¿V9º`:Žq˜b){  ì˜K ½q‹ 0öÈQÎ˼Œ pK-P¶ÕkÁ¨PÁÒ{ ,x+½|ÐzÂQ€½ô°Ñ¾í ¾[Ã6|ÃÅ!Ãî[•‹•ýK¿ô+À™¿|º‰ºCŒÄ€ºЃ‰¿NL¿NlÄMÜœPü0ºõÛ¿€¿ñkÀ\Ž <Æù–-`WpÁ%Œl<½º¹·!l ·&p }€·l¼Â|€¼,0w̼΋ƒ ¼¬Á.¬ÁS¡Ãíÿ[ ëÈpÃ’<ɾ© ÉŠ ´“˪K•lÄA|º¹£+À—«ÄÙß9óÛÅpºÿ[¿«ü™¯ŒºDœÊAü™¨L´¬ÛºÙšH›šÁŒšÄÌš…š›e»·& ·+|¼}0·| Ç(y Ñ|¼~p ¬ÆÕ;x[Ç~€*\Á- ·Ö«›, É™¼Î€KÉî\Ãì¼Î¡Ê©ëÊš[º€º0Ê ¼Ä¡«™ýŒËK\º)Êö ¿Pº§KÀ¸œÏ¸ ¿ÛËT‡4KÆýŽÆ[||Áóˆå,Í‹ m½Ý\Ò3™Á@Âó¸Ñ!ÜÊ[ÂÚ ÇóÈÿÏ6í ïœÓæ 6Ép˜Jœ¿²¬¢ =ºJÜÄC ÐC}ºƒ™ÏC“ûLÐBlÔ Õœ)±¦A±Ùê•` –sŠŽmi»êˆ¦dÙÕöº”{—ƒ@·& Ás‰·Æ!ÝÍl·ÝÂ| "½·Ø‹·-à¼i¬·…ÜWà—€)¹6ý³:ì Éê¸:]Ãò)ÃŒÍÎAÏÑ™“\lHË ÜÏC-Ëö›¿OÌÏ ¿öË϶,Ë=¿ýÅŠ‰­ï[ÑðxnèhÛí(²=*£ ü–&Ìo -&`Í{ÎxlÜ,°½"¼m-ÒËËÑë¼|€¬tÛÇÚÇÊ˼ÈÊшÿÊ­Ã1‹…»°6l¥9Í­pòì!ü“õ{¹Z̹]ÜÏF|¹®lL‘,ß³LÐU½ÄõÄ®ìÅN¼Ï´ÑM2p:ÌÆ|Û${¦­ºièx‘¦ÛÎÇ @áÞábë’Î{¨v«¨#›‰jâ}[™,7‹ € ï ãèí¡›¥6«¸3¾Þ+Ã’{˜=´ï=´ Y´†DÑŽ9§æH§ê® Z{UF»ÚI»§:§ÜÊÀ-{åøø²;¾Ãq³…{¶ªž:»žZ¡ô ¥˜*æØ ÞÁ¾ß›$BçT‰Õé$ÑaøËI®®ªç »2Z«Ajë^ÖIÛGŠåˆÿ°‰½\^­°¶P*¥7K¥Qš©J©"Šæ:ž~“A.ç >ä`<‰4“ÀïHµN›«2Š„ê®³*åðj~¦ŽÑ‰žèZžÀâ™Z³z³„ëë!J©™*æ›N´¡žìiç'v´Â ¶ {Ðkºê›F¯g†®8ç­»Å̆zâ³)î"ž›ŒÚÑʤä½ëŸXJ³™îëlÞ° ñãÊžï©ÌìõaäI‰¦Ô^£M¾§ ZeC¦ 0dL¤sÊÛI¹²µ¡·.-Þî0¢jæ!z½î¡:‹ñk^ìV"­ú^òIçe'Û¡­hM–4:¤ð•Þÿn¤ªó/O£mÙÕ’¹—¨Àó>¿¯x ôz‰Øº©5K©F_³Fï¸æ‹žÃ‰Ÿ ï&ê(%8Ûõ‡ñ-;ñl©‘Óš™S_öD^äí5ša íÏNšÀÌöß÷®Yî$nî%>îç^÷/™âR!¸aOÉcïŸ^öB~ö/XªZŸøêñ\ž^ÿÎö^-„?õU¿y* ®øœ/¶ß²0¼¹!õ•?´—Ï›™ï!³ÞùcÌøŸO™ï!6 &ƒ_úkø‡ïìqïöm¿ö¼¿û¾?’v?üzoâyoüwï’|_Š˜)ø¶Ÿï¸?YþÎú‰ïú¯ÿ”±?Ñ4óüÐÿ¿•aœö9o¯âÖão–-oþ;/ô|é¯AÏþCÏþ¡ÿ†AúÜ?­§¯jW&7G§µw§Þÿñ‚À,H¡A… d¸°!„Q¢`¹˜Ñ"FŽ3vÜøÑcGŒ!AŠ,‰Òc‹\¾„3@˘5mÞÄ™&M.TèP¢EEšTéR¦M™ÎìUêTªU­^Å•'Ι]e°qàëX°bÁž Kv¬YµlѺ]«¶¬\p碽ÛVnZ¼|õöµ›6‹©P³®¹U' +?vòdÉ•)_¶œófÍ9öºqWÄ¥MŸFÚgOžÍ‚[öÿlÚµm߯[÷nÞ½w R¬ZøêÂÖŒGž\ùræÍ?‡]út鄯jxøvîÃ˄ɢ‹oòåÍŸGŸ¾‹õî¿ã\±Šú|úõíßÇ¿üUííýÿçï¦à/=D0AÝ€+ì=w*Œü&¤°B £ca+ä°CľëÏ%ñjs„Di+ÅgK‘ÅekÆc‹‘Æa«q½=p*ä3®šå„TŽÈäŒDÉã” rH'‹|òÈ(“œrI'WØG-·”ê=_S0L1Ç,A.,îB5×d³>öö+ìL9Ûñ&ñ°© OÚôœOÙüŒ PØ…PCÿóDtÏDû\ôOÙtœ“ª,cНMK/Åô8ýL›4RO­êÔ'0É$µTSg1S«Pý[5 3…5V 3ìñS[=\uDoœ¥Æ]UÖÅ`eÖÆb6Yb]„TÕHsRJ(§•¶Z*©½ÖZ+±ÝVÛ&¹ýVS/o%À¹õTu×M/ÕrmZõ:Yç¥w¾7Ûñ]}µÂ×ÎÙ8`ޚݩ*­7a…•Û´Vg †Øáër"P`‹/®Í]BܲÕW9aZ#&Ù;ƒýåÕ×^SfùØ•]V9æ–•…àãíÚn³åygŸÃí蟭a’è*…FÚhpƲd§QSlC×ÿHE‘تƶ¬Å´·®µþz–°ÃÔâxXsµY‰vÍC¦ïÞ~Y{ºä¨sº³Ñ@õžE†¯Fd!ð¯Ä<ð+ø.T¶+¬æ³­ ‘b»ÂðÉg9rE7g46›Í¦ á W±Ë¬h»Â@¬aA:V¬€¹†%¦¸n»O†7]'|ÂF„wßÏëbìàq“¡‹»È\ú&µl®pæÎc6Ñfs•Õ§Ó>öãF¶=|Pq¯I×—iFvÙº¶š…VdDx¬.1€+¨\†Ãý> ëoÅÆ@ÇÕì+]ˆÑüdP"jí ŽÈܲ$h,fÉmzÏòQê’V4ÿ&±@H«ÈP s:k”®tÖ!cŒsº'§ ÄBZ1Õx¡\¤×!ç +Pa KˆÃÓ‚u:<šÒ´Õ4ñ5±KpÊ™‚Ç‚Ì,bûŸàdó»Y6 „Í㸸8扭xbüâû`#ˆÃñn=ÌcÁk„'&éíëlmâ*„;Ö±nu¡5þhœ+¬‚­+a÷`‡:A:v¤;N÷ŽÃGØ!’‡4NëX°=5Yð%õœø »áÄ|cBÄzº(D´Âjˆ˜_üfs9ÞÅ1sóãÝâ®ðD\¡^ ÀßþEÅ+4°˜»SÅô¹‚ÅKtB$ QˆÀÿP>€ Üö¬ Ã@HÒ=löX±ÇÔð8=<ç6Ñf…×,"ÚVð6 Í.1¡rK° }òûìg@ÿéO€T E(AzÐ…‚î“û—‚R›çqñj¹Œh-¿¨¼0°f©O*ƒ7Òªm±j²,£lžG¦:ÂK”&ë=+$Äqfru—¬T9ŸÆh/èdÛ óH"ršç¬Të^;ǰM>?bø³Oªî³UÅjVµªÕ«Rµ«[ëVJ”ÁŒWýâï!WŠm¥#Íä¡ÑÇɲî+#ó x€÷-Nk+í"樸ôQp‚…ef(1(¨n0‰ÿDЦ„ÔBk6¦„Ô\/i r’ΗÔ$ëäs…ÇÒ¨¯cÅO¹ ´U#p5”P ç4%Òv²/­ÉW÷¿°R•­Tõ%X¯PUôÖ¸Um&£»9€˜³b+ÛÀ/†6‡‹ßü¾HE«ùM€½$œþ(÷5åqT¥ÑÃmÇ$Õ&D¢Ó†ÛDm#ÈT’š›-í|å‹Î¢n¾”Üžc ÊÈ5yò¡ZÉj1qܪþv«Å.ƒ›\òÅ$oœs†÷¦aÍuÎÃþð†CÜa—XÄ53𻜩AïÅŠ²²çÜ6¦[lÂÃņ/ãGlü¶p»¥â‚Ù8âb•Š6¶ÿêpœcÖ¸rþ´c…wÂ\ŒU™]-½çbµ"ÓgŠ„ó’êa°ÊÆ_ÇHÎqŽ ¨æïÍÞ'ŽãçVÙ—îë± È@lÔy¬3å¿dfVB£ï|‡=´a]XF'ºÑ%J¬KµÜ“Æz«¶Cãàc5½´LszÓâ:n+¿óÙ}f£žo\ç Íi¦*œw+Ã+¹ÆGV°›w e+W&T¶r°K…årݱËÇŽêÆºdäV4ûƾt\œõí×ÚÎvsUå\c¶’Ù}Jnv³ÉœdAPøÀevºMé^ŒÅȆw}bìk}Uϱ63„ÝG@¶îzßx¶1¶ÿñîàò˜·9®uª{<\=”ŸŒ¨º%N¶V¥75Ö‹wÆïfçDÛö.5Ž·ëßV.¸l n‹©íÂéyÔ ~5“ócòºÝ×ÙÐ)íhžÚÐ>G4Ðw.ôGφÝ2±8Ô2ˆéOÛ¶éL÷tÔ-i&rE*4–pÖ³Îg›ÐèšxØ)þ4ckÜìö¢k´¾v­¼ëQ&k ÅáÄÑ}î#®;ÞïnbŸØï$>:¹V|vÂKgÞ‡ÖÙ¾xÆSu }þj¸»n°Ò¸ÙU¥¼W)_».[ìŸ÷ ±w’ôÓ`¼ð§gÇo“Æ·Þõüì±á†[¹–;×äc\ºzÿd<ÛžŠ=¶19ó½ç3 :ò‡®ü¢«(ðŸ$=§–.u§SêSºc­_õáãd—ß÷qaÎx=üã¿´ Ü6ßÛ«pƳUáÏdù÷þL¿Í¡OσÞÿ¹½[);Ô#@äH1zû$”‹£øQ<­r°­â:<¦âr—ƒ9”¿!ó'ë3„Ã<9c¿2êmÛ7´6¬R,ÆŠ¸ÿcÁÜx>[¼”AÎ’®03˜Û7Ì›¼Ëc3¯³ßâÁ£3jÓ¶]ëA_ú§¢¢«Ú1r.Ã!.“56zÂߣ=‚—¸Û˜lÁ.¤ô‰è+ Ó›AÔS=«»‰ÝÂAÿ*r5™.ûy²n›=…›5•{2à A"ŒµWÃ@€ò*à@8Êc@·s3)Ô6Ucµ:¼¼,û:JãBãƒDæ;¾BK>JT´ĹI[Œhé4ì³>íóÄP¬>Q´îë’¹ÁB\¸›3€C8}»*ƒµ̱ )µÁŒ9ìAÛó'9“CÌ{¶CÜCYƒ¶`t¿‚ Cš8+bÆftÆg„Æh”Æi¤Æj´ÆkdF1$%I1À’ðÆpüFq GpìÆq<Ç0Gr\ÇnlÇr$GtdÇxTGz|GytG{ÔÇt¥®hYSÅ{kÂWsŸøÁ1Ú‹¼;Sìr‹5\ûÀr,®ÿ]¢<®kÈ÷«µá¢6ÞÂÈ‚äÈfKA©Xøƒ*8É”DÉ•TÉ–dÉ—tɘ„É™”Éš¤É›´ÉœÄÉüƒ’$;ª(=0…@Ê¢$Ê£4ʤDÊ¥Tʦdʧtʨ„Ê©”ʪ¤Ê«4ê‘TA*<3‚$ <„³:£6#Œ93²aüâ=!³¢~ó­ôÛ19ãÅ>óEUk¹ ê’?h†¾ôË¿ÌÀÌÁ$ÌÂ4ÌÃDÌÄTLÄüƒŠÓDŠ1hÌɤÌÊ´ÌËÄÌÌÔÌÍäÌÎôÌÏìÌ3”´4Ì3U´H_z²6,È« ¸ð35DÄÃD¹ò;Æbd«8z¼×k<Š •Èÿ¿<Á ÎÀNÀ,οUO?ÕÓ>Ô?Ô@%ÔC5T÷Ge‹ŠSõº, €…ÒJµÔKm)½ ÕÒNõTOÑe{T0õºžðÍiLT LULVýKWõKXíKYmZµÕTÅÕUÍÕVÝÕWýK;ÝT©(•Lk¸LcµLd­Le¥LfLg-Öc•ÖdÖe­Öf½Ög=VOÕÖÛ¶0…»LdPL5×suQMe m<ŒV‰ÌO…×xµQ.]­3˜»W5ıòëªü¼O!#¹ßê·?Û2m†ä¤SéLØ4]X7mØ8}Ø9=Xÿ…X†­ØçÖu¥R;!VD…Ï÷„†öüX@ÙB-Y YÙ”%Y–Lù”²›pT¶C¦;Ã?àP5l°ÞbÀ‚ü6¸\ÀÙÃÍqÕB™ TtEÚ¤eLvý³‘×§…Z M±‡KŒÅHö;µlƒÅ}KµÈ»MióR-X&UÚ²5[ÀÌØˆq¦ŽÚ¶uÛÉäÖ‘ôQݬ3|+Dsµ6¼3\ËC…\¸à"XŠ![­‚x¿¬‚fxÃ̓£=ÛÅLÜJU×›ÙXx±R…ØÉ4…Í +ض}ÛÏ´‚NåRª°Úƒ4¬m³­µª|%Ë[µ°Ôœ0Xˆ½Ø¾|‚?øƒÿQX¾LÜ'ȃTèÉQX„ÍÝã¥Xä-N²eÞåEÛëdÄìôb]Ùë…†(…RØ(€†@ðÜR`…=`ìmYó5Y—EY=PÙö]TÓ˜Ùµ;&%c@®ËÛ!Œ# <¦Ó½ùA±¥Ç]ÌÃÞ¾üÝ0Þf¨²mÜÆM…*Hà<ȃž`à­àfÈîK ã”üË^྅ Þà?øMÎT Fa&ýàžÐÉý3ÖèP˜ÌÒ5…Ò-Édw…†=âÞ\É]ÉäÞ!.=Éß!†ЭÑ@ÐƒŽ Ý)fâ âíÅâ%b¡”Ì=°S]Ð<@|:Ñÿ äÁÉëÀ5Fã4~]«BÆ›+Z˜8Õ^Õ¿,ÜÁM\FU6`6ÜQ€f0IëÌT@&ä>Üd^àäQØ]¿lLIæKîÉf€fä&d~‚^T(ÉZµãYýUé-6 ìXh…W>"'.…%¶ah(6Ö¬,ÚÒÊÞ.](˜eï…SfÇ(_Þ$fSX†‚g~fd&­¬ ]æ…†âÒí^gvâh¥Öd[ÚñÖoýÑÀ-W ý`HÖÝO.ÉÈ5`ãE`ÅMdI.a vd¿ô`þ]ÎàÞõ]¿”`n†~&d&Ûíg åƒþçÿ•a½¤Þ.ña íÞØ\îÝæ’ÔÜɼehpf[Þdir2bl®L0éì½åíu ŽÌ]žL–®enžLa®åÈÞ/.]=Ý©@g Uç›0ØçmÎVÜT8†àUêâ,TPÜKN…XÜ'HÜÂ-ƒ®‚MÆàèêªÞà*¸j°&èÄ=†ß­‚ȃAd.å–ê ždIÆÝçM[Þ¬ÜòcôUOžæèòµLVØáRðÞ×ÙƒÁXa6…Ïýa(1ŽæÒ!lÉ„jZì¸øTiÃ6lªl‘¢Ò'žN®eÐõëD…Ù«ˆß¡f;^gh >`¥þËz®gÿ†îK¾dÒfRON䇿ä%Û*ˆ\“Lî„V`N†6^àæË¦P¦eIáPÃæa‘ÖáË éïÝŸÞÜ!Ææí4ìÆ°eÔ–Ì®eV ÖÎÞ¦%~éZ&ohÐiÉÜáÔf_ Öë‡%D@—¸]µN¬þç=Æm¿åDNh¬.܃fëßuð=FîPäÅÝã<˜äÞëå ×ä Þ]O.ã=IB΃ˆhÍëRÕ íÅ\ưïem¶Ì‘é×ÉÞî f(è^hÊbÞ¦mÚa(¸‚Ì-SJžÎiÍåé!·‚éõqïÅ©õ¶e€pÎÐrž^ÊýoÁ‹YŸÜÿ…ÞåçƒÌN`©>h¥N¥î`7¯ófsÜæàöfYÒ§ôÅÞ÷5õj_P©àÇ%ð Ì9oÑ“Lð(-õœÁîÑõÐ"ÎL.çP0Vô -ã'²öSp¨c]µw^½w_Í÷;ÞwTîwSþ÷[ øTžÑ¨VlgkMx„Wø†gøÿ‡×Ö…x‡ŸxˆçeõòÕSÐxŸ4l×öOÚ‰öuõ²hs7y¨ jŽwT‡ç…^‹Ýu[‡õ˜wõ—Ÿy VåÑÛk ëëö=_Ÿ—v GÙÕ&öøtqWùí8Évwz'y}qÚ“§zxÚ|Iz’óú®‡Ñ^—úÐõª'{Íø1ÿò¬ç‘VQu¯w{BÇú3ñ˜²§{=C¤W{÷Ðx®oõ¾§y¿·ù»|™|‚¿ÓŠ&>ëzc'zg_ü¢|ÇOjŸ÷¼ÿ”qIŒ¦ûÍ_̨—c®ûПWsfTËÇÒ¯w|W}}_}~o}}€}ŸýS–}œ/øÿž8øŠçý‹·øWÎVßï}à—xá7~â§øã‡å³×JÓ×Ìß Íçüé7LÏGºß‰výí÷Ì”wþU&I—‡ùÁ¯ùñ7ÿ›?Áû´×zJëùŸ‡ÿ ÿ¡üÆ7z´ÿþË/ýë~ê÷ÿÀˆ?,hð „ 2l8°¡ÁÐ&R¬hñ"ÆŒ7rìèñ#È,"B\àaÉ”*W²léR%J†'”ÈÓì&Μ:wòìéó'РB‡šgÅˤJ Æ„¸¢TȨR§R­jµÁ“Jµ.íêõ+X’)g’-kö,Ú´j×–¥Áö-ܸpa6 ˲®I¹z÷òíë÷/Y»‚ÿ. ¯áÄ ¿(n,±c±‘ 3žlù2æÌI!k\£3hËœC³üLútVÔªÁŽ^ý²²ëØK[ËNH£¶fÚ¸wæ½ô¶ïͺçm:8òä—‹+'»ùXæ…¥×~ý:ö®Ô•Ïî{{ìîÞYƒOÜ|Cëèc—_ |½W®ðU·7>¡|ÐõQ¿¿ïßü~¸©7_~HÚ€ÿ)˜Üµ‰· ~ë=aI RØ’…±%x¡hëõÇ!D‚8„ÞW`nޏ¢l"ª¶!z(fæ"f0²xc‡Nˆcb4^¶#‡ùdoÚHdXCNf"’2"ÉšŽ:¹œ‡OZ阒 éÝÿ”9š·å•a¾”%hLŠù™µpfš8ö&œl±'uÎtšyî…€ž}úiÖ™2d¦ WY(¢O‚™(‹‹2úè…‡BÊ¢¤“ZJ¡£—*H¨¦þW©§%†:ê‚™’ºž©§ªŠ¨«ªè*¬Ù¥ksœÒzkl¶â:^«»úšÙ¬¿ò¬°ÅÖ«±¾!›,³ƒÛ,ÐJkÙ²Óº¦«µÙ*õ¬¶šqÛ-¸Ungã’{®Aߢ«¶ëºkP»ïjf®¼Ýª[¯³øê‹½ûÖ¯¿ÐÞpWñ¬-Àƒe°ÂÒÜðkË›°ÄIQ\ñ­†VDeUÖA‰ (~MeÙ¥ÿe cL¤i5˜FCË/ÇL̹LóÌÔ ÀÍ:ç¼sÏ@ËlóÐ<tÎ0Ä'A-@ sM;}ØÕ5ˆ \ÕO/m4ÍZõpHgu4Ú8=Ðjƒí3Ñn›ývÛpÓý3ÞqÛ=7ßE¯ìÚX(2Èà…~¸á‰#¾¸âƒÁ8äG>¹ä•SÞ ˆÁ †_Ààœ{ÀàƒîE‚Ïyá^° 8âÌÙzꕯN9ž;ï–û®ù ½ ÿ»ï_hü÷R_$òE®`ƒÍ5ÏC/}ôÏW?½õÔkŸ} Ø{=øÛ/~øÙ›?¾ù‚°@½úÞ·ÒÊó’ýÿ,D/È×´@+ײþõäg>Ôï ÐÛ_ÿ°Q?Â"±¡>ø]ã ‚ 2xA ¢~­`Á x¾ò¡¯„$ì!wø… ñ‡F,"¨DBP‡$¢sX¿ZP‚¸ }(ˆ+ñŠYÜ"AøAÀ¯~‚@DvFøà[¼B+®ÀÅ‚ؗ¨Ç$òшƒHD¹ÇAòp3M Œwœ/ˆo|äùù¼î=O’Þ³äõ0)=MV’—ô¤ÿÖ'Iv2{„ 7˜ÆòÏŠÿÄ_±I²@¬„¥ÇÅöaƒŒ½´£ú®!BGB0”²ì%,KùÉH‚r“ÍÄF ŸyLfRs™Ö¼^+dxÈÌÐÀéJ„ÙCq†S‰älÅ9Ns²sî,ç;ËÈr2P?œ“2HÆZ~hÄ¢ì2hžWœÿ¹¿ÂÒ€Wè'2?(ˆ~Ú‘âÜ¢ç¹Ã ° ðó¨=uÒ‘*‘pñô!IOúÑv–SeÛüŠ"’ˆ¾¦Ü[¡ sŠÓÚ”—Л"ø6¸‚ù}äŸ1mJJòoúÔ`R›HÆõ­—ð{^û|šÁúÙ”§5­i4»*Ö¯ê´|‰¸ØÿKW’H~²­DL§ ãêÖ¹6Q‹¾Ì!+ÚøAöpŸV c¯˜CŽ^°©;Ô_Fá§> JÔ—‚UãúêGŠq®ríã/kÙÍòðxi]H ¡Mƒd¡¬d-! N;VÓ²Ö„@•^©?F„å³ßóhÅšŠð~'Áe zAŸÊµå5ò9=Ú¾Ö¶ê›Åj½ÚÚêEsºª½® ÑúYˆÏ6´H)<Ã+R¸¢”¥"5oyUÒòfñ£N¨ìÄEu~Їy¬=¹ÒÇâr—²[le'K9ªñ Ø`…Ÿ˜ØòuŠ+U/xÏûN“®÷ÁâMï…-,ázv»üúƒÒÿi]ëv/º&Æ.N[ATìŪÒË£÷AÔèP5¦žËQ=AlÐY Á ¦Ë ÒX÷3²¯JÅÊîpÅ‚¸ªŠ)ˆâ0¬S¾rk;ìátu÷ 4p…fùHÞ0s¶Ìd&3 ¾fæPÍj¤™ã|f9û0³tž3½¹eì•_à̤499MlÔ †þã¡ BE#zщ~´£#ÝèI3ºÒ¦ô¥--iMcÚÑjvˆÑùéD*/Ó¦Þô©;­jN³:Õ­~\ c]M@3SË{@—½¬ˆ óüvÅð‚Mç‹¿üä-ŸùÇc¾òìté×SRhmBœéI?=ÒSozÕ+½õ¨¿®áoýå4k½ö\¿½ísûÝkVÏ¡o¸²)ýGáozø•6~¢‘¿håŸøÎ?~ñ£ÿüäKúÓ_~õ©ýæ[¿ûÚ÷>ö·ýï“?üàç~ùÑoþC{ý÷ìrpÔ ÿùã:þö§¿üëÿûë¿ÿùÿ?ÿàþ  ÿ    6`> BàN F R`ŽJ@;opensips-2.2.2/modules/seas/doc/images/image054.gif000066400000000000000000000326701300170765700220120ustar00rootroot00000000000000GIF89aAˆw1!þSoftware: Microsoft Office!ù,;„‡     /01*+,,-../0#$%;=?78:"#$9:<<<<%%%((( 000888333###,,,+++111:::!!!***===777$$$PRUJLNNPREEEQQQXXXPPPHHH@@@TTTNNNAAAWWWLLL^^^Z\`mptjlp{{{xxxlllcccppp```jjjhhh}}}tttvvvbbbaaayyyy|‘”š‡Š”˜žƒ†Œ‚„Š………”””‡‡‡ŸŸŸ„„„———‹‹‹ƒƒƒ•••“““ž¡¨®²¹¡¥«¯¯¯§§§   £££···¿¿¿±±±¼¼¼¥¥¥³³³­­­º¾ÆÑ×ßÅËÒÙÙÙÌÌÌØØØ×××ÏÏÏßßßÇÇÇÊÊÊÅÅÅÝãëèî÷÷÷÷óóóïïïæææçççÿÿÿÿH° Áƒ*\Ȱ¡Ã‡#JœHqa€‹32 Pð"G1jI²¤É“(Sª\ɲ¥@Ž]ÊœIs`Ìš cÂ숳§ÏŸ@ƒ RãÍ—JTšs(Á£Ab܉ôfT‘6§‚Ü S¤Nž™:K¶¬Ù³hÃNªÍ‡l^mûvçW¦v¹¾­ZwkÚ¿€ luêݬR»ZÕ›÷cÔÄ}¿BvÌØ V¯iïjÍkÙ±d»Zõ>‹t°éÓ¨SÏܼ—4UѰGó}]ºuã¾ Ç Š7¶ßʇ-W– ›¶êãÈ“+GØû©W¬¥Ï =øo°Š¡_¼'å颕^ÿí}Û¯[Ê¡—«_Ï^5g×Ãá×.ßz~|ãÂùþ}ݽ~ûÿ( ZÔ]'Wž™WkBæ`l—yv[aÖ6`Fý]¨áHþ×ᆠ†(âˆr˜Ò‡.¡HâŠ,¶è"]ÄÅhñ17£}8þæ–Œ/öèã>Zgß{³·×eÓM˜ä}4é${;ú¶Ñ“JÇävßE™l’騟‰Í¨"‰QÚ¤K‘6¥…j±Õ%•PòD¤—çß›F9$šdš×v9gϽԥn†v„'cH‚–¨”p"W'U赩àbDŠç§EhÒ·â¡rÚ9šuÆ-Šš›tz™#y¢Š_¤ËÑÿ穪[æ*¨E*ùª †—*–n©ë¦§BšXW‰Vj!wšîùY˜¼Â*m´ÒÒ¬’ÎÒJœ©ƒ&$ë"Šê—«V µšÛÒ¢Íyk+¹ Jzd‚‚Jiaô:wídˆñ©î¿÷ôÞ°ÙîÖnjè¬ðÂ.Ò¦vœŽÊÚ\ W gQó=(ÅþeñÇ ‡¬Ò¾sæV+nó(2§ ¯ìòeöˬš|Þû(vöš—oË/÷ì3ˆ®¢ü®»A—*&“%ÿ¬ôÒ‡:jàö{e”ÊVŠ,ÍLG:)­¤FíÏY¯Û$¸¦l)Õ'‹¶ºÜ= zº½íÚ¦%Ø,¶dkÚ¬p˜eÿI,›t;¹o®W†{ëß%¤ÓŒó )°6¢=7؉·G!¢:níÜS^¹åcN7ÁK~øâ¢7m2åž÷Òë°ª¯·V¯Æ©çÞ3dz?Ù:†Æê.¼ÂþîÞP:MŒñðÌWûjn¸þ¼ÐÍWdôÿ/öaioý÷„‘ä}숛 þOIŸ¢IãS™pûêó«Xü#£?ÌìÓ¿Òô†ÞŸ»çþ{ëôg¿€ÖkÝS7¦"0bK3ž­ 2M‚LÑ)bAõy¯ƒj•Ö²®L„>»Ÿ _Ƴ† …»«‰ E6À šOƒ1¤É A¦@ši5,˜ÿoh¿!mFÄZ1˜Äòåï„èk¢ÊžhÄîðx>¹"ñp¸DB±DRü ÍeÅ/fQŠN^½CCÞ„ˆU"‘÷F™ŒqZrœ£×ÈE–Q{Œ#ý˜G‹uðŽÎÓ¡ ½H0Q…‹Td#чHò Ò†üØ!ùhÇH^²b•ŒüBé¾B‚’Žž[*YBÊý ¯•¾“ä#Ù8É3¢±“«Dâ Q™Ëõq’•ftä,?¹ÁQÂ-›ü¥.•Y¿6¥Ž·T%0]fÁcƲ½,‰5‘ÉËlŠ/š¿ãßÂ&¸Íkެœæ·HÿÏzâïDûD >vÀuB“}¡À ÊІ:ô¡¨D'JQ† "¨(Da`z42ð¨C @Æ(‚ï"e( R ¥/€z’€5`â¦8Í©NwÊÓžúô§@Å)Š^Ô¢õ¨G}Á‹°€¦:õ©NeT§JÕªZuª pÀU›úlõ«` «X¡šE¤šõ¬g]C|bS´ºõ­=*\çJW¥¶(3ðƒ^÷Ê×¾úõ¯€ ¬`»Wð*„M¬b»Ø,”•®E«ZiÚÖÈZÖ¨r½¬fj×u³ åi0ÚÒæt²Þ1­j×ð“ʪ´™}-h;û©ÏÿÊv³I8ìmAËZív³¨­IM«Ùز´]‘mƒÊ50â¸>mR Ùà꺵.M\‹]··»hMܟ*¡ ¨xsšÛé®×­½Mí{%»Vœ w¾gý.~‹*Þ•×§êÅ^6Üôjøƒ.‰6`bÁþNý€àF`‚ ¾„Œ‰KX¸¨Òåà~ͪ];Ž©ñÅ wOüSý²¸§ý yª…óÞ´lhn#°°ˆ,‚ P/}`L¨A P&jð,øAÉ5hÚ^¿8¨)¾î•Zb—ÜwË>u1˜qãý×§~À‚z•Œ %3bÿJ`„Ø€FðX F^ÄMÕ g%ìxmPƒ…6`á¨!^˘}Úåuétm¤sz‰EXZÏÆt¥ñÜÓÖWÅ‹ŽkDÎê2ohÆA½„„Ì6+Á8æ1°ð‡/Ï|ÞsŸá¬5ø¡×~С©¬ÛPï4˫ɩ„­…Ò[§l¨ j ä6,[½j˜v šÓF³äËÆê¨Ã­SSoˆÔ;­Ás±dNc!ÁK¾µ’mЈ[çY×mÎ÷ÚÐã%˜Ê¤Åi’ý\Ú)C»g3æ,`„³ÛÓ¾½i#¸­äF4¢Óõî°Ä5^éNcââÆ4& ýñ¿Y§lÿ^òMÛh<«¼Û­%wNżhs_ÕA…³ðLí:Ë Z°8ž+n`ƒOùÐÖV8&‚ŽèbßTÉp.-ƒwZi÷yÃN>ò"Ò{ÚÇÞØÁ7µÔÝäû¡ ŒrÇŽÓ,|ÙJÞy”¡<ë?ý¦öÆi¯o qû®ØØ4³Í/„nó¾7ÑKÁ鎌‰…[ çýƒ… äs»ñf·qÒ¼ãOšÍ‹¿p#öžïnZËÇ‚ØU~멺ÐJ¸Ä—þa¨½ämxréS^ú¼ïÙJ¨Øoêm–ü=ÔóàCXxŸ>»»UV4NÕ¬…çÚàl`ù‚ýÌgµ:å>6tÿ¾)ë/"ØÊÆ»Ë÷¬@óÙ&¾éÔÛ,ç=§W͘G/ŸqiÛçòïww‡Slæ{·¶~}'\Ç·hÉ·eË' Í·eˆ×:ÅÑ–o&`Z`m¦u³—kùfoJöksÆg®–~#‡€‚Fz¦q#7l‡¦^~Ðj Æ5–v–Æw'‚‹ÐlÍ%€¥7€/—c,Øul%sâxe 8'sÑ—x7Ea7t§X0`BöjZÀo:‚C‡ƒf‚BXzv&q½†€0炇pÍf gd³&ƒ‡Æö‚þe5&h»‡‚ FmJV„-èwHxS È„/²„sµa;u€"ÿ÷`H58•v¦^6ЃOv~Øj˜`oö…y¸xïF†A„sÖc¿¦†ÄwzÉÖS¼ÇbÅ· 8f‡øbMè!ŠW¬˜‚;Õ‹Ñåt×kÏUjà`£Çr¶a¦a6uæŒÖ\ç·``7|†S͵azÅSð—">‹'6‹)näv‹,–‹ÿQ^=ymv^ˆ Ieéei'|÷xôVc)çˆü¸du†pFmê¶S’áfäh?…ˆ ßèµfèxbê%ìxhVx ÊØ~nfm—p çå`,'’zw†¦~d¸`x¦e †7…ØS09Ž^'s ÿyæØSöoi¨Ø×k5ùZ‡“ vVŒˆSÎøVÉìˆgu¹öc)|î|†•z—wØk€ævF¶dÇþ§S( ™“ìZ«fdixv9õkCiTjiyc38oix Ûvdâ–v†sª„¼j{ûW¤·wk,¹d±wÌykI+¦<¥kQ®Ç¬û±k y [²áV —‰Àºtïz«òø—Eå`²Í<¹ÿVðæc »}-›r(:r0‹e TI—•çžV}+çŽM–dÂÖx{ §­JЭûKøz—cÐdg`‡&emFkK·l¶lm ¶ç`ÌF£ŸuúV"'¤;Ê£ýyrtµÏ:!h•cÇ®úù|ߺtŒ´\–FÚÔ©Utak`U‹€bûŽU[¶âšV›úmw¹[†ËS9b»»ÈS¬joÚuÚVdI¦ WŒºhehm ²)˜fú©~7Ök@fŒïfªò…„uY™{[Ïwµºž[£9…·—S>v¶ËªmÐ~öp¥·o9ú£ö\xÛ¼1išGi ÿ–¤wq:U¤–fao†±tY¥áö»#Q›È7n…ø¹Ê±\i&lk‡º@W•ÏK¡ÓûaXøa°¦o‡m6Àœ©­kiy¹û“–C+s»_ô+)ËåjöÆtµÎ달Úf‹|`©gg&€¹wv„¥é^¾Ë¾Ææ¾¿ (¿HXÁÈqÁ/zoòèvEÖ•øútý¦S¬fG‹³&–W9Ÿ.Œ“<2 |ªH8Áø…Ãî±\Ýêc¢Xƒø\*Ķ&¯O÷\=¨´útJfÆ.k­hYˆ¨ùÀnüÄå¼,FÅóeŪ±\AtwˆiXˆGa|‡™O©it¥ÿç·‰|ÆfIŸN̆Ìevg‚æ`Nû²ÍöšG© §g56—ŒÁçhÃ2‡Ç§b¼ûÅÆzTÙ£˜&rC:rf¤Ù‹ix‹b0ìS½v¯ê«ì†m=†}†¦aÅ|v9’ŒÐlËerŒ4l‹¦Ln¨ŒªŒ_ |³G%hO ‡rƇ;÷©7ˆ¢´FÎ ¶mZ»tùÌê„T¹ö}÷vv.ÇkZ`8ÖÃŽÛkêdóÚt|bvü^Õ\7×<_¬ìºÛÜ’ùV±l0¥wh¿Vh&ѲwÂrÆê;Ê’ T¹&€¹–{Íæa<æè‹…†…ƒh˸ºëL¯S<Íá6ЦQЇÿÉAå§ù6•¶LÖ†éuÄAÝÏ2äÎu&€;v™~Ænq–},y¿zUhÆ¢²—©B+Å ÓÆ&Ó„AÓÏ™”<%ÊsuÐ ¼ÍY™ou¹¶Î ’͸lÏ¥¹txÕ½[TièÅ×¹a£'a£ç“.Gz:& zT2œÑ,‘Z]j‰W(Úcý—‚ɺ6ÍÑng«U›Æî(ldy0p”évjÀ†ÍÎk¹ÍuÝ]‡ÿÆÔÑ»dMvvb b•ÿÜ©]É-}/ŒÕ¥Œ°ó‹¼o5qëùÑèvêõg6ÙoEÖÚ\Þw}š¦]Ë}eÍÝ]Ï ^ v߬gòÜvNÆk¾Fßn•ÍÞÅùݾû ¼ ùߨà™áÕJ a* ̯idOv¯J¶o[÷ÛM|š»¼hã-ýýbN]ž^ýŽÃ&lxfmÌœg›'gÔ‰ßj+>“ø j…øâÐãg1ÛáfßîÆ)>fA-^Ç‹]s]ˆÎ^ÔqlÞáfäÇ…äf¡äÆÆäZîäÃþ¾YÀXnþæoŽ"yçŠÕt®X3påH˜åa±å:tç‰õ€NXŽå ‡ÿžèˆ¾èŠÞèŒþèŽé>é’îè^”žé•Né°éž®é‹Žéw5ãÐÜ@þŸžê™ ê®þèpKˆš4[ë¶~ë&›€ë¼Þë·>UMØ„Iì#Nž2do{ì ’ìÍDHÏ1ë9AíÀ„ìÐÎA³Ô9"Rìw%5Ù®NÒîÖ9%/áþM˜¤-7BÞÞ'àžîÚ´Hãr.ˆÛìÔ#ïÊN@bá&ån³Ô8úž.çÎKVÒ=ÖŽíøž#¿ïZ£8äž= óï/ Oð–dð¡³ñÇ¡ðÝN OP‰ÔMcè”/gb+!¿;ïþñϤ2(rò÷ÿ®!ýÞ1¿1eOÝ´)1_ó3Ä-ðN#+ÏB-/cIŽPG±L°€[€ Àdá7 ã.¦s󓸄QâD(^”èIãFŽ;Ɖ£±‰'DzÊѤ O-b”øò L…4~èÔ‘H#€‰Ñ¦Í EÎl8ôèR¦M>5J*EU«> éidI,spD:5¬A©c(dÕ# bk,ë6iÜ©JåÞÅ›·a}ýþXpàƒ \ ¨ZÆž}éJÒ䣑_7ºÔ+•nÁÍ3·4Þ¦­ÞvI/ì1uEΧ]¿vûÓÿlÚµm߯[÷nÞ´_,mõ ƒ´o %X²™KoƒˆWÃí‘ ô&£I›†ÝüèuDɇGŸ¡ìÞíÝ¿wÿfpúŒ1ß•¹Á ¡ž™JÔñ!©˜è4(ìðî»ÖÔcm©ë4c0B b> /„O>ŒêãÐ#³Äzi1rÚH4d†Î À0ž˜Ä*I¢ c’ó2oÂób1Ç R  1$²ÈÛ4¼¨C%5ºÄîðh‹Ï4Ú¤ƒ!jѓDŽ ¡Ã Ò²Ñ5Üq¼ƒd½!„“H$©Z²Ã5’‰‰ªtèi£H2@ KŽìp¢‡ÿˆ"Œ@6*ó´3Ñ jGÛ¤4½7ãÄô½9')Gê ®I·2Ä'< ð­³êkÔ9ñ t¢I+¥õ´K3Åu·M%Ê$>Õ¾W¡’éƒM<š£ È a9 $ÐUékÇ|_KÍ"…‹*‹Úl–hÛn'þ"ËÊX·ŒBšxä C)Ã&ñx¤/aéd–p0ä‹;väà¨àŠ180U5©g•ÙÌ„NÀ¶0LjÎá†!fz*‰'æ¶â‡ÿRj¢ Oâx+O8)c¤­›(e’ÑdlM{dÝ&âPNT¦†¢ƒ>Ô™`håº6=©2Ø =:¤„.ZºiÙzj\¥vÈ2°ÖÚ“/¼Ödm¶.ƒ%®K²<-9p(¤g¡ B!;ÐRÄ9gÔvV,‡Ÿ²I‡RÕ’¤‡I2±cƒ˜x<¼w£WSÆrüj“F’#yC*—#­äB*Ã^O¢écÈ ‘Ãí™ ò!'«ºWdÝÕaH íhâïN€ ¯pßç¿øàáž!¯5’ãÓAš€ŒV8&@ã”á V3 Õþ7’âë|o;ˆ<1ÙU¥`ÐÁ$ ‘ºÿH•/Z¼‹L~¾(‹#à–@H?BÄ~÷+Rþ¬úh/VÔH$ž ƒ0lL[ˆÂðð¥ñ­În<–Þ’E±Ï„9ÃYVØB*R†Š“¡BhHÑí!ô€ØÃB¬Ï,"<ˆŸ)* ø—G QB«Ø(i|`õh^ñBYLÈCÕE~±>¡³ùŽh¾<¢G&ˆcG$ñDàAŠ6Üc&ûèÇ GÂj$LèÆà dY#ÞÚèÈŠ qIOÐÀ)«•IZd“œŒ'7"Np,+ÑDÙ¬¹ª`RV !%h)K¸¤ÒI«„Í#u`ÆúHÿb5ty]-±uK\öñdö·‘¬Õ‰ÌH2cÊ¢‹>?iÝ9ÙXB@’ ‰D¶Ë+AS›Täf7u¥KOÁBãðNp"l×ôÑMpâd IWr ³.yª.!êd ;ûÉ#/ÐÇ ñìª[^’¥NŒ¡a DÍ8"‰@@asÐHÇŠQ™úîMJ(ªhÓ%, l€ ‹‰,Ö6ÿ‹X„m«›oä#ÇÊ#pÐGd-›åœH$ŠÙ`…¯K«Z–™H…t@®j‰„КVb•†#™ ƒ] ƒ(D¡àÁ‘—î•¶}­Õ_1Ñ6 ¶¦J@,&Ôð‡Ù6§¨îbÙàXÆN¶±Ë]l#tº?•º€½î‘šT_šäeiK—óD ’ã9BM"k%’Z«ˆÕ¸á5=R%$žÖUYªJ$ Qˆ¿uD¯^=.r+¥\ü ‹¬"‹ FØ@ ØeD 0Œ ,üa»~`ƒ…i£ôªA§)la «†ÈjJñˆ½EPr)µSXçÿ´2_öj%søÍÿ/‚0…-à ˆ©Cø[•Õ  ÐT7d éHã‰`%W̳”0@%{cL,B €å.#ØÐØæ.BÃmnü „ ®˜ vÎéd/l]7×øÆu¾sm*kp:Æe+gA·öåëSÁÔȼñ±.r`Ë©„ R(åCrP! " BlÁº3ˆRp†9,x N- žµ‚êÁ§Ífšƒ¤\Àºδ¡îl«_B Zøz±0Ù3B ƒž¬† YcÂÆJP³Õ@TEtzŸÄÁD9!:„AÀžÈO:!„ÔLYŽ AA †K‰è$ 2 ÿ‚-J¹±<WŒ³.Gh$ÀÚˆ™ âÒš¿‚ ™Ñ0¨Šïóˆ!hW X8¦ó;h"ŽØ„¨–Gв;³ëLó†óÀã[?Ì»?ØÈ?ýó Óëµù•‰ê­ø¬G0& 8‘àÈ„è €@@ Z§Â!ü/ûƒ à[úƒ ý*AÃ9AÄè#ˆ-òq©/’ÁêÓ€ØÁµX b 8(1ÀàØ DÃC!Bÿ†ù½ö+3ä㹂k¦yB”ÂØ"‡¢/¥¢ºƒ(x·Ž˜„ ‚„Ñ€Y‹05—àÄ«ŠMà!ä¹5„©1¸©ã%´D9œCæƒB#¹C !âó9ØC­úÀ,¬?Ð: ƒ'pĉI8+ˆ8 ð[ã¾EäJDÆÈ((!L¿˜¢Ä"|:$´ ã°ŠÚÛDNìÄl©Cý EØ"èšSœ•PÅUlE­Û-@¼H“ïs‚4)Fëк ª @и)<4|F‘‚F] È„t‰¯B8(G0Å3*ņ[­;§cŒF¹˜Fç«Fé¼U.Ë„-(è-ÿCÑ ¬EüÈ¡Ú:X01Ò'èƒJ)Ê*90(–Œƒè)«J˜Þ[ƒ½O”<½ä²È„Ó>Ž8ÇhÁ½-ƒÞê¡8"IÂ1Iö#1¨2(§Ë™(„ø‚ÄIOÔI YH†ìüIÐÈÈŒšG—à¦<&&äµÐÁ Œs„vñ”—4ª4 DC‡¬¾®¶¯Kž<Ÿ¬¢Ì™…‘GE¢¶lKdœ-H„­ÙšK{4GÓ9OÈ6LiÌ?"LÕ¦Æ8›‹Ã‚øFð+¸tŒ2¨[Ëyô˜‚áF:X¿´ìL6IHÿÒ ËIªà°Ã4«ÍXLc˜Æ„MŽzËæØ3ðHQ0<8ƒ€Ã›tÎÞ¤”ߨàÜãЄ´X¸¬0E¬á„ti—R ‰ H²ÐÖnõÖlÐü$™¡sU¼VV*Vc= :@À/iBpˆ€P•Võ¨X~õ×~ØØ€%ص€‚EØUØ„ÝW7Kc €ï¡ÿ­Ä‹è³IBUösˆ p‚¡äˆ0øÉNˆð½|E.Pͱd¿ÜúØ„Œu `U @àEv­Õ3MˆÈÑ.Û§P”Õ#•–mY‡Ì€že =È ‰ /ˆÕuÕ…Pí琢Lи0Z¢ÅÙAZÔóKp­uòA½(¬=@Ý4Rª­Z—` µ€Ûôˆ@¸Ù![*úÚ‡[&é™( ¼-=ø·Óð€HêˆH ‚©…[CMʳŠNà—ÉœÝÜÛÞé[‡øÛ^…V…È€ÀƒB8 2CtÄNƒì$V¦I `@ÖcڎدÝÜÌU±ýÛÿÝPl‹Ü8p)q¸Üæ”ϰUˆY’aÜ@Ýõá¡ÞýKlQÇ}\5ˆEÐx¯¥ÞZ²^-ÂÞì¥^t,Ñ…ƒöé.8Ãé-_Ã9ß@J_­ßã²ÞØõH ƒ@ôJ€Ê#ßý-Ú6é\ˆ½Àx,ITWåäÞœÝ]…ˆ›NƒÅ¥;аÍF`¾UàüõUÞüP´RCôƒà×mÞ¸Eˆ Ý®(ƒ-€‚( Ø`Žáf¡û}Û±Ha 4ÖHN!þ\ V%‡ðQ„BÀƒ- „2ªŠâùQbvšpàÖ)âÞsJ3ia… YÿXÅÜ,®”-æbÞ‘G£Mb ö^½Z9'Fc#Tã!žCäl!½ßäÔÙ€Žh-T €ÈD6˜ëÔ¬‘?ËåèùÙãêÅI?VL9ÖOA®`ø*ŽJ(:ðdˆQ&#¨bTVeµ¢c‚(Kâ3‰“A Ž±äÀâL¾¼M†]b¤Ï%nâ})K;`]XˆøI‘݈ež§2>` ÏÛˆk†8Àd_¶_`Þ[šˆ¼ŽƒÙE…ç’Ž8gYžåðÑN’Xèfo†Ý®dcˆ‘‰«õˆºíˆ1@æ<(CŽhw&æX‹gÇX8é¹âÿ4Ægæg”ͪ(Ä“03ˆ—u0è+Ž®­w~ÈŽ`Æ—T «¸ç‰Î–}fiب݈Mªƒiyµi…ö_PYé—¦—ê#É=!eñ 8£f”)饮‹.b¾&á_Ýsm (ÈN ìà ìgzçV,å¨4È9kªG¦Ÿök>¾l-;ȾÆ(çT)Îf[rÆІi’.êB˜*M(¾–Á ƒºÓ˜œÓNX^ýÍl¬¾mÍeEÿ:èƒX,„ø ÓÞíÞþm*nO î¦ðâ¤` 9ø‚ µÈA™s1 ˆÆÛ½Îí‘ÞnÝ.iOÀƒÜ@(;è‚èlå>. ï'ïò>ïô^n¢¾Ž«›”™I.„2`ÉG ®Í›î^koéÝži@Ø‚'ˆ‚1˜]nFpgpWì…>ˆ‰t¹šB¦`Zžä9£ ¥_ïkçgx®ù¾kTQìóðØÑî’5Ù7q'ÙÞOÆ_]ñícè¯pQ€°ÒíJ„)8{5S–fríEqúPqjÔ6 ÀÁð Ô%_ˆ°ð'·ãÿa&sJÙêçˆWó˜oµò‹ ^`2; ƒ#ÐÅ Pò3—eúj>ÏÀ6׈)—ò— (ƒ3虎ƒXt¦È€%SÃ*‘~ô·=ëª`s*?m©† ؃ª˜¼µ0KgŠ ÈÑèxkIxsSGRAWoƒ€õV'Uƒ4öhf½põ¢Hƒ ö§íu7ÊtGÍY¯rO¿8P‹(¨ÝÄËÛbg+p°ðÑ+ öçe—uNïôc$öˆWµm‡‹D.´]™Uwö;vOvpgî²¥ˆ `ŒFT Z÷Ž® aߊý÷²öö©¶w¢s˜Ð`d ÿ(x‡kŽØA©x;®÷BÏøàð÷CevY9÷jús5F”úß‹w„×x•çøe_xŒÐ( wºP”Wˆq§;²yiîá%ƒú ø ŸŸX—yŠðÜJ)µØ' ƒŽø 6ù̞ǣx­÷„˜uùc” BØfŒ•ºÈXßùƒè‚ô†­ƒµ'ÕóÁùÓáźo ›5zJ£þ`*¶â3*¾+ú‚qäÙu0Û™_¹/ɱ؀+ØË+ˆÉ¯ü½‡ u¢ÖáCé-*¦wÈ'ˆÏ¸ “êY0á0ȉ- ý1•ÐÆè¡EÙ§}ÿÛçî8¯×ô×/ §Ž„?TÃD‰×Ç~ ¶‰Ðµ[ ä‚ pô‚`þ.{þè‡sÞï}P9ü‚wjÐpýäÇ×$Û€eõ‚-˜‚ Àˆ(ÿóOÿ²æ{íÿįä¿ìNÆå\ÃØÄŒÿ,hð À"lè° O=ÅiRÆ¡/_<É)óåÑD‰>’,iò$Ê”*W²l™rKȘ2·¸¬ió&Μ8æäYҧΠ(š3bÈ8ËxâdH©§!G ­jõ*Öª`Êìêi‹Ñ¬bÇ’]ÖåÙ†iË-zéD¥ RŒc7&U¶z÷ò½ÉÕkLš}ÿ»6åáƒ>òl à0cÂF³„+Q)'¨…âœxUËž4M켞ùõøò[–ž>ìü¾Ž îÎ;;vÚæŸ€~Gz þFpb¹e•zöM•…:$ÝtÕY¸WQ²8Þ‡g±FÞd~X^M".‚Y•˜ž„ðm8c…õIˆ8›[ÿ È ãùx¢y;¶h$õןUÂãg9J)†¦i8¥^/>¸˜ÿw$qgb‘,ž'cê˜`PXsXº)Y•€áøf[BžXæAâI$LÞ‰ÛN þéc~6‰fPApÄÉ …8Sq "©&qlTˆTmÒ¹)[qzu%§¡"6jO¡þ(”ž\TFrhbÈ\žà ‡#ÊÝ%¢êj˜PšQä®tv¬Z¢:)T€t©\±–QÆ#ƒt {­U?@©)¶ÝÚ¥NÄZwªPDXòÅ ´>KQ]Ž|q‘R› á-½7}°‰„™Po½·‰[¬©ˆ eB!PR2/¿ ¯¤À¦¡‚ÂÞ&ö/¡“yU)دW™Ð„‹ŒR |L§Hÿ #_»àU?wlVø`Æ„RÈ[øÁ·*û< LDÒÕ$S˜ðs°i¹ì%§ä¶A,t·€àÃÈ8„05Õ¸q7$Ù}¨tY`KvqØmµÔnóõ_ÅÜä™n:-7ß}'< ‡ÛÉàÞ~³·á‰+nlƒzÞéÚž@й¸À=S~9æóy¸gÙ‚^9æ/fÞ6k±}ÙÚäHš¤öè.">zá­G\¨ I².ûP†Y~yì¸ÿmhv©—žPocs¼ñÄÿXòà•~»â©ãÞ»ïüºædfo^,Ý܃©zõ«‡ï8ù㋼}lŽs<¾9¢ú^ßÿñ¦Â?®ùˆCŸ|´‹·dãò.ÿ}ÏsÄÙŸ~v—¢Ú€NvÔã_ÒÌö?þ¸ïxîë’0¹Î­,y4 óÔ²ÁÉ‚„ …\¦6qdz›픽»ùNE( ØNØ4Û‰2§Ã‹JØ:Þ°ˆùàá8$$6îuôž£xÄžíG8ŒŸùÖ¢C)rQW3T’öj÷½òUo‹]<ãÈÌèÄ5fNh|c¿@4ª0ï‰p¼#]—Å<ò±š¢éÂpë~‡ã©H)m‘ŽNd! )IF†p7¾!áØüä<çU²‚¨«‰¾ÂN²”5j ·9ïtv®ÿDd"M)ËüÐÏ’²ˆ†×>O‚²’üܸ²Y:Ž4Ñ?·Äµ²‰àŸ0‹)MïqˆÌä“°©'C–¼›&8Còam± ':Í]­3î¬Z$ß)Ohžsžöb<ï©O•UÓ–õÜ'@­Æ+’2 =hÿªÐ…¦ }¨B щêS¢½¨<¥‡ÑrPý(HëÒ‘’T%=iDQªÒZt¥.íbK_*Ó"Æt¦6ÍçMsúΚ괧mô)PÓÉÓ µoC-*R“fº¥.Õ›I}*LM Õ©ÒÔ©T½*ÿT‰Õ­:(}·4¤HMw@Rè¨\ êÿµÊ’ʬgõ© ±'GýUî•£[ߪÓ’(™ì{¦/ T&É­Uªz=lx@L#™sp4¬ò+Y¡¤õ[£¼Ø’Ti(ó¨u²žEÑ`K´XôÕƒ«i’×ϲö:m-lÃ¦ÑØÒi¯­-n6ÛÜòV ½ým[v›%„¸Æe Uç¶ÚãVT‚AláçÇÌÓ °Ÿv Ñr™;ÏÊRwŒ–+c•i çŠqEÚEl3{Í»>®ºrüb3¯XÜó•»ñ…î_ãGZ r÷qªË%}ÏÚM漣O¯œ`•ÆO«nµè|,a‘&·°æ0†sá sx4î0ˆIÿâªÂ$ñ[ ÈD ;óu%+Š<Ð*8½ß½¯³ãpjуÑ_e8Þ:ÂrÇ2¶±{“ÜÝeª¸Å,6rRׇYQž–O3Îoa å©Z·»šÄ¯\Õ›Ú'o9¶·5f™{«c§¹Í”u3œ[ç4/6²ž3P…)Äç—iDÆsGÉÛgãa2„A¤ß¡X¾ÌÚ™„€Vi—Ùûcö^—Œ¥ÝQ–×üh˜‚ðÐB¼e£ÇLäì1ù¿›†ô{ûçU2‘ Ö¤+aO}R%kóÀ¡]ð­¦Y1[YÖ!% y¨ßò:™Ïs,d}[5jZÙ¿fK³íÙhK[²Ô®6§y Öw7]ÛQe3·½mSàé˜bzž/7 Ju3ï˜âF©m¤×OºÖxÕâ{÷KOêtó°Óè6ôóÜo’>pÀôVµ±8ð•~uºþÜ·`y£íŸYÑ ÿhÅÙ˜q1î™Ìt·?þÔ‹©$/y}QãH«œÄ,_d@;opensips-2.2.2/modules/seas/doc/images/image056.gif000066400000000000000000001226221300170765700220110ustar00rootroot00000000000000GIF89aùæðÿÿÿ!ù—,ùæ‡   ! ###%&('(*++,-/0.0123356878:;<<<>@>@BAA9CCDDFIFHKMKCKKLLMPNPSWWMSSTSUXVX[[[\[]a^`ddaWccdadhehlnkajkljmqmptxxkrrssvzux}|||Fw¿y|F¢tX¬‚}€…ƒˆkµ}¾žÿ„‚uŽŒƒƒƒ‚…І‰Ž‡‹‰’Œ•“˜›˜‰“““‘•›”˜ž›››–𠙣›ž¨œ ¦ž¢¨¥¢“¯¬›£££¢¦¬¤¨®«««¦«±©®´¬°·®²¹»¹¥µµµ±µ¼³¸¿»»»¶»Â¹½ÅǬ»ÀǼÁɼÍå¿ÐçÅïÌÓµÏÖ¸Ð×ºÒØ½ÃÃÃÁÆÍÃÈÏËËËÅÊÒÉÎÖÍÒÚÕÛÁØÝÆÚßÈÓÓÓÐÖÞÛÛÛÀÐçÄÓèÉÖêÌÙìÔÙâÒÝíØÝæÙßèÝâÍßäÐÜâêÖàðÛäñâæÓäèÖæéÙìé×éìÝäääàæîëîàëëëâçðäêôèî÷íðâóòæõôëóóóø÷ðúúõþþþüûÿÿÿÿ/ H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó‘ ¨hl#à©Õ«X³jÝÊÕ' (ˆ1Pj¼@ª@*&tK·®Ý»x›Z" bÑ$:IF}hÉRBª'4¸0ðB¹ çL¹²åË”í sP p¢@€†¸˜`VÙ™  ‘ª'ô `ç’= 3r ›Ñ%Ï '$Ú=È¥D01é’ê  L[³÷ïàËÿ¯iÉÀ…(½ 6]É„KT]¢C ÊqÆ/ˆ`dÉ„c–$Ò€ZYàƒ .\âB Á ¤Ÿ$“øg]vL¢>TÒEv8@#˜p#ŒÀÙ%"¨0ÞŠ,¶èâ‹%¢B`Ÿ@ vI䂈¶Í€q—0@’nQЃLb€ Ò1¤@“ ™£’”e’u!€%[¢˜à$´ãšl¶éfxŒô€šÖ¹eå%QL`€ˆpÜYZˆ¥@¥x‰L:ØÃ%|ÀXƒ*0¨¼™õ–v©f¨f‹,àVZ–$ñÞ›¤–jê©VYÀ– ©™†¹ÿðÁqRYÙ†Ág¨@8'E[—TÀ­8ºE¦Öqå%bV²ÀY8j¨VkíµØÎ”H mXçfÅ d¥•Æ%b€Ÿƒ]2Á~Ej¡µ“€U5¨.»Œœ•.£ÖÙ€ Þ†x‰§Ñ€Df«ð 7ŒQ„  ÐÖ)n10"ÌÚ*#&¼¦€ ³A »^âd½²È*¶z¬`TØÝÔ ìV‘ÌêðÏ@-tW{u1ôÑH'­ôOI(ëÒPG-õÔ#™f4ÕXg­õÖ\wíõ×)öØd—möÙh§­öÚl·íöÛpÇ-÷Üeƒm÷ÝK ˆ#|÷ÿí÷߀.øà„nøáˆ'®øâŒ7î8ß=â-ùäAàÈ#˜g®ùæœwîùç ‡.ú褗nú騧®ú#ŽDNùë°ßdùê´×nûí¸ç®{é­Çîûï0;ûðÄoüñ«÷üòÌ›$<òÐG/ýô¶+ßüõØsô<õÜwï}÷Ög/þømÿýùè§{øä·ï>Bæ«/ÿüô{Îþûø¿ýü÷þýù  øöç¿zôIÚÂ#²¨dCΠew¹Zð‚ÆK –bp°]ÝÑŒÙü´–¸d ‹y@è$¬ª¡t”@Ep‚8” 1ÈÞNƒ“ Ñ¹ÿL°ÌI šé“–¸D"QléÁ’E@D#Ò‰!.¤‚$¾`ˆŽ¨!‹Rˆ sHÆàUЇhL#é4ƒˆài–Б4…´Ñp @ÚøÆÄ1a qá<òE="dŒeLäJv¨ÆFªQƒ8"B4Ó2…”‡“ €žÉ+2Ä.0@m¨E^BTa!ÀÔAQÕH“°ÄgŒx.ùȉlð¹-Mv6Ó¹ÀR ƆüšAZ WV‚É„€,&€ ¨ ÃÜ¥|r)€bæ •P%À)NEB‘ŽL'5È`M2XȆT¤‚\ííÜÙB,ÿIôÀ`K`¨,Õô@  V @#‚àÄ%¸À‚>P£DСHB’à‚pš ®éB "ù4“1—hcZðÐÌ*Pl¹€ R@…ºÑ¡ˆcD#jPT? ‹[::Àh*e©K/QP4¡L SJ-s* ê̪5¸@Ä@ Äz¢™(OŠ ×ä ñ$±¯†Õ pù@®¾ÈÀÆ6n~F|}öŽÙ"Î :³rš‚¸“¨ iJ%V{ØkáÑ•¿üŸ)oyÌÃ]Ê%Ÿ»ôÑ­ïPé8T@ìWí=‡qÿÏ®ŸýlŸØ' ¥XŒPJ»û›Àl(­pÂò”4"Àì©6«–'¾—¼4ufxFDº‘XZhõÇIòl–À42ðà{`D¢`D Œð~Ù'ò'&÷|£}Óƒ¹“nH!{8aƒ.(AåVetÓƒbƒëT}Nƒ6A„9˜?;h`­³7ó8I(?4xFHSx„îó„´Õ:@ˆd‡xˆã¡‡Žä‡§Fˆi(ˆŠX*ˆøHÿ8:†„‹8‰mòˆh‰\h‰¢ãƒœØ‰žø‰ Š¢8ŠtC‰w¡‰=„‰Ç£Š¤7MøŠ°‹Ã„²X‹¶x‹¸è„¦h¨„[¸Š½h?fø‹©Œ2ˆ@l¸‹WaŒÄŠÔGŒÏ(:[ —ÃH 9Và¹sKĈ'pƒÄ˜ãŒÇ،ɨŒOÁŒ„ŽÃ»ƒŽÓˆ9WàV*`+p0`* HP1ÐR(`˜SˆR`3а9Ž0 0Žà?p9ް)"°‰! èŽéH<€ÈŽFÁ’$ºC“3ˆNŽÿÀ(€9Ù(*àŽ00/p00Žð(ð“ôVà3ð3‘ ©9ŽP#P‘Ž.Ð#°300@0“ (ÐþH”Fi–|#:6Ù’µµŽ0É2é?q¹>wÉ:XUŽpŽà> ÷Hp9Uð“R ˜Ô8 /p`/ð R°9C°0¨ey˜˜˜“D †‰˜–¹F{)—zY—\±šsiŽñ¸—Θ“ @$ > +€GÀ€(0–+°&šp0œ ‰™šÓ€›*ùS@(a°@œÆ‰9JÿPà๑¬3 o)Œ²Éšüó’®)ˆHŠmÓžœ“—ÕC›XÅ‘¬“ž©žgDŽ Ã‘ÑG ™ÃŸähŸ|© î …tŸHAˆK˜‹†ó„øY;J;è8 èÆ9š<ö“žëy °Ù ªŸúªŸ J¢-šŸ/Š¡8Ù‡: 2£&ê=(š¢=±¢8š<Ê¢V²9¢¼ã¡V"ª•%š£?ô :¤'ª†-š¡@Zràh#ðª9¢¹9U ¡CŽŠbê‰ „[Ð'€nàJp¥Mꤼ¥Q*Ÿ?J‘Bš:†Ø¦¸¹¢“šÃ"àìI¥™£‘ZÿÙœ(pÐ(0‰é à'°#ð¦#€U ¦b©! É y*P6੘Ã@°p@+{ð[°“?cZ‘˜z«z`¤VY§vº’yÚŽ{z¬ÍŠ:ú§é(@’&J@’z6`•‰‘÷Ù§˜Ã• 0zO9ð©¡Yœ–/@3pÈéœS0ð3E©,ðªŽÐ›(`¯˜ó«Sy!@–$ð©[ð(@{€jš¯WPDIS𡃥ÉzˆËêSš†1ú§@œnÀ( ¯F9•>`dz X©•6ÿ›9âÚŸŽ€.Wà6°®;À/pöx ˜; ¦ J€93€p,”NÉ´˜ƒ7 šA ‰9»š9Vе/àSP×H¤Èú±Ч!ë#;‡%k>9‰JšBðsšÑ9>Pr›ù‹­s—|˜Û)pðÚ+е?°¸bð'€à¸'€´‚Àk™{(pJ – !`À½I˜“AðA  ‹k «+@¶jJ+nP»lPA›Ë§Ïê¶Ô·vY¼ ª¨Ð?€J’9žåù( –)Gв™CÖ™¤Lz$Êÿ»’ÚŸ_zŽä; Xpåh¾J¾ º¦: é[rk¼­ˆ¼y£¼õÛ¼u[ ZAô É:å86á[¦ññ˜£À: ‰çû9 ¼¿ö;›ø«sÛ‡A:¾µÃÀáª|ˆ À~j¾'Ê¡ ¢äض̧œ,=Ñ?"LÂ!Z¾o[¸?ˆŒœ¤Ð:?l{À+œŸ-¡ú›³¦cˆüCŒÃ9 ŒZ @ܱ[Øž *ÁAŒ;;:Ä1ñ \¥;L¿ŸS£ìi6Qì¢Hª9^€e«:Xà#0‚:÷ˆ9¿{#®bššØ¨´®e|Å~ ·Zl\¬Ã^<À6|:Iÿì¢LðÇË‹9+°J@UªHp,€0@ªJàªÐœ'NP9‘X»˜ƒbð+P L‰ ´>»—©ÊË”úan`««ù³7 È>,ȃL…œ‰ý{Ÿ5,Í<Æ–ƒÃ É‘üW`©øú—jê.‹+`°Ÿ:G™‘’‰‹n°'0-Ë–3³7ÀÇð^¹ê •À®U0«Ž0–c9½Z­`L¼Êì Í,¥E¬Í›Á}üÀGzÃ… 9Ú,‡Ž# @¢™\{𬣵™úú±Sð«¬9zÿË·ˆ©A¸ì«/೓ MÐ×ÈÍJëJ¹Ä­>YÑ,ñÌN|ÈgL͈|§³Í.)<—³?%}®+œ¡{+¹˜ã¨ ²Óm< ûÒ»ÃyBðÒG€S‰¸ ¸ß ‘º6˜’ º$°à¸$PÔçèxkÆK½†N­§7Ù¬ Ãbü¢?L‘VìÇÞÚ±êɾ\¦T­³!ú£ÙÍ;Ù•3Ñó¼q :„š9{àÖÍUÑbœÈž­9ª‰|HÑXŒ¡­Ú¡`0Æú9p»™£0@Ìu0µm "ÿñ¬ …G²7»Su ðbŽAà$ðÐ^0pé7ë`DÐ:"D2´#~b™ES“àmÖõT "Bá"`½îZ0áqÞã}›Ð+×äižÕ{½Ù;•çH@n@¿ê)ßU „ =Í)Àˆ†`Qk:aŠâ¥“î s6piL:ÐÔîÐr©…@ ~ÞŸÀ1 !°þ°•^¾p@žqAE $ñ'0V®%nQÒ:¢u ^Á""0m@áÉ?sþÙîkß(îâ'ÿŽÕG¼?ßNß#ž9<šžú\ð'¨Üo0œ”Š(€Nª^À'§,¬RJÀºî.ÊUÀôpšœ/Éš+@2{]{ô¿ëãŸÊ¥ ÀØ9ß’×L<6®Àù¼àí_pðàpF{Ð7àÕ(u®‚10+Ån]o°ñ‰`R2 ÅþëÁn< "o tà3+Ÿ\¼§›ÈSLÚ4ÆsÅÝ…Ž`Ôê› ”{y°® ýpù|ÐëœÏ©ûúÊZoÎè\¼N°ë0;?»€›U0•?T»’ÿ©Îú:MkèÇÈâ épþ¹ à|³À$ð÷˜ó `¯Ž€p°‘z U©Ñ¨S‘2­jÕiÓ©O³jÕ+Õ®\Áž-:¶Šƒ@âMx×½.5@À)8‚¢àD!ˆÁ¸˜„o%Á} [ÁÈÊè¾2%ŒfÊˤÈ?Â,€fÀ£XG˜-Ðq¼ò‘¯üøÆ-«{X‘ ‡<ô%ežyAèÒ¼€wAáhN8…øª9L^à‚C¢ØÐ1WˆÌGÃ3DœBXƒ 6­(S°AQ†µA"ea¿Œ“ý¹¤8êmŽ[á㼘3œÁ™Î¼Ä“¯^BÿåN•’V¾´¹µaâMg˜rD"ki:œÀ ŽØCf &d œ8„O‚òôGP.u+À€0°` yQÂ{~÷>|` ‚O s‘ˆ>À§úY½¢¥$( Ü`ÍcN˜5Uáºé£b&©£Ð™¦­w‰4´Ô¥/æJS:«jþêf„JÔH½iÈjÉ7ÀáÍRÄm* QèY-… Ž77RªÆH˜:ÍUI…rRÇÍTVŒCXÓ‡—Žu¬qˆ¦WeUS|m³M¾* Vwj)¹ñ~ÏWTà‡:¬‡£ÐªjU¯*W*©‹«T«Ÿ(ÆþÁ±ÿ€ D!$+YD‚QÒL,ŸØ H£êë„嚢è¥L–ß9ìQìà˜ÀÖ¢CÈ\k#oѨ¶3¬iiØÐj5³Ì  cçð98ö±„@ndEˆÿ!³¿%ÒfáÊÎ>É®º•Zâ²úVìL©M ¾#F\,c3Òój¬1ïÇB&¹ Z0BD`óz$#˜X¸´Û])ñ–`¾….rVj\E.£ñ?CäQ2Mp¤;Ý“Š¥šºmãuÿzÀЂ·¨q Õ$bð0Âl‹›%èPôÂXn* ñ6Kð@[pزõâô@aÿ›€EJàÛäÃy3ˆ£2<X•5D!kÜGøÿ#Ä3φ1ÇtÊÌٰ͈@ꊘɅìÊu ­¥ê¦xÅÃÛ‡E£(Ä}0Â/Œ6„$üÙŽQÞËŒ Ð&ø€‹ÓW[ò/·oFñ×I9(2X„• ‘eB‚Ë€`Ô— a³’µ¬föôqÒì4®fIÏ æ) h›*ŒN…ž9iHÇ ¼àr PBP€ ­ (äœrøQPQÓ‚–R ‚ ´¬Uùl€I,‚.H¸µ`n·d- ï\1#b%!SÂÅô]ÓPÞjoÿWëŸÈàÊ–0© a\FÍÌa…xÄ%žV‚‡­°Â8«LŒÝ6¶ìRü‚÷‚<| ä%bÚ=Ð¥/(Š%Ÿ½‚8À ?¨ÚµQL°ÀRÕßo8Lž /WU'2`T!b´eFÉá€Ã¤þ‡CxÕ ·l ntáÔÔ]_w¦õDWMýŠ_”¤Ë¾N¬€r/ˆ=_P"˜?â…Eá`f”PAQxã=éÏ™ôÁ7o\«*ŽôåfFp¸ þ0\ãR¾ËÈ•lãAÍõ ÜZ7žDÆEO©Ð’ýçÔ (|ä㈀¢€y¸À ÜlÀÌ=3ÏÆÏ ¸0ÿ¡0¡‚ÅNN9ø žÀ„wÉГĨž(~¦2°¬%–a̡ꦦú©“+á„;wœ èiè[“]`G±ôì&€µ¿YF “¬›M_‰L·&>0ß]äsÚðF3ç{ƒ“¾hhT«2,3„äÒ>BȺEØ<˜ ;(P+ñ» ò‹7³¿[S Ž-c³|£Á¿ü[˜ýã8ô¿Þ¢ìl”J€'Ð:C€‘lÀ…{,T €?¸º„£Àïƒ H„â` ¯â@;ñ@hA™Âìr fq£ò+?p,™›ÙpCqAƒÁ(û¿ÿd*:å @éK5(к+Cq@Çj¸äºD𾘸@Kø€è‚KЂ€€ ƒ(h€¸€DÄhÄ@DF@DB¼„$P€ pð›'L•FÑBQܹ¹Z ³ó8±Ëèø;鋘/’é×zZÜ/yë’q óé‚’±1º€.8CÓê?5”Á3£A0¸‘KÔºÀ:´„…k8ªC.4„E0€„˜PBé+D5ˆ€D¸À… XBØé‹‚K0 ƒK0 <ǰ`„KˆPìSœŠG)Å-\²ô•£)â,Bj“¬ÿ;°/¸ Ãðé¹1àK౸‘›Û€[äHPƒõJÆÐSÈÂr²5¿Äc˜0¸ÊB.éÓAèk<-“>ËK®"üFXÂ$|­K¼„I €(ø€–PJƒ`˜£¬"ƒH FK8ʤ\ÊK ‚”ƒL HIÈaªBÏ>WlÅàsŠ‘ò°W¬H€Ÿ›Ÿ¹EöñH‰Kùù–›’¤·¹AÉ÷é¿‚aË4l˜ŒÉˆD(Ç,B(éC:Bx¼í³¾Ë[µQ3„ʪÀ—øCxAʦ4ˆ(ð§@4ˆ6˜ª´ÊIÐÀ¬Œ‚ˆàJ¯Œ°<ŠHK‰|Â]Ñ•ÿ½1±–ü¦©²;h4R#ºlŸ2RÊyCÎ@ãKKðË@K‚(£ hƒ3,ËM{Éf\L ¨I˺ºBÐ:8|¼!Œ@䲺"D Ç$LØ4¤L„¸G€>¸„ˆGxaMªLKŒM¤´È Eá¼Â`!ÅÄM©R™8kä7!KrKœ“ÀK ÌÛ0ÌÃÔ´üÎO»²¬ÛL­«„¬B/ƒ,«Ë2¬ëC |­%Ct€IdÄxDÕ¼„ÿÜD«”¾ù¼Ñ$0pHP¬Z—í-=EƒñÍè˜Â ¥®„Ð+ÑPpqÁíäNýQÌí‰ð €¸º„ÿ„°ôüÉnì¾pDB$™ƒàXÒÃlI/ŃÜ*=$T¼«¾--ÄܽDUÔE•21õ‰ðʬ,ù‚dÓŸôÆöüÌ#qD¼E¸ÓnÒÓ…<-°4ó(Kj)A²¤HB¥“ý;TªH¿Y%ÄtÔZ1¸ „­¬9¬,ž´TóA  Ê8½U0Õ©XU™TÅ9ÕL! UµÂRÍP™ ‚ñ5HÛ2nÅãU=Ö¾ÉUk¤CË‚¾+ÓÕnLOCXO…ÓríNQíMf×÷KE®XFõ×UÔj½B‹G\t/ zc]L¸ÿ^™’ñãSV¹ZFzýª^M:v%S]M¸võÆU»:M5VŽ:(UŠŒuP–ý,‰Ì)¦8?ZµÙ›ÅÙa‰:¹€Žh-›.8›¯ÉLJ¹/ò²¯Ðˆ9Œœ›º0lSF[UYÅBH!Ïue8S@Ö„óÚ=ÜT„èœÐÀ4ŒƒˆM>bÖf]V˜ÅPQÝWàh[VÄ“9Ñ+‘‰CÚucڣɔ¼€„5Úø™Ëö9™¨mY'Ū­Z«½ZGQW›ÄÀ:‘½ÆÍ½²MåOÛÔŸµ‰ØlB¶…۸ݙ|½—ÓÅ™´‹ºµ[8‘“ €DéÔ˜_¼Ý  ºÿF Pèhƒ ÈG^êd£TqÜÜl^‚ÜÈåK0¸‘EWIeÀ^½:;5î•Ñ5 Pƒ…ÄØLDßBL—t9ÄDŒ-P€¨„ŠHFtDH<_M|™¶uÙ—eUÖm]¨À ØÝó°–Ûº-ÁÒŠ(”Z4”^er®š¼ZuµÆÎÔ`ñ¼FÎüÞÏœJ# éSˆúŒÊ.Jh_uÄ@K`„€¼„ykŽ(+ÐwìÏ~ì#ÖUÝïàÇm\NàçpUR± Faæ%à&›`•Z„&n®ö¬FFéÌÕÜÍźæŠÓ|<ð£¼òõbhƒö%Mƒ‚ÿ ˜€ ¢#ͧŒJ¬DÊîá»ñÒ â·Z”$bêâQ‘ÐAÙar]b\…„B^HøŸEˆâF™â þÞÎ|âöœO0Pà–˜ÓKh5Ðdƒ ^2ÞÊKPƒ˜Ó$ˆ6ÞʧôÑ8þ_Ö `NŠ-h - 9 6 ø€@ Ôcœ¡ÛaÞcÛèãW³_‰e¥ÚA&qŒ„E˜fjþ€ÀG±,y…dè›â&.Í h *¸¼ˆ6(€¨Á<>æNæ9a\£hfg†f\m”Uƒ<ôfÊÍ`p„„p¤ÃçK¸€y$R;€N%ÄJ¨éDdß>ÄIÜ Ø°ÓOŒ ŒØßV¾ÑW&¤Î³ØØ@bP„ P9 ˆ*°[*Š- îiPõÎh±£*;­×ú¥\CËÁ{æo†Ã›ÜÁÝæDG G:Ç”u 5ÂŽIÜ d¾ .@àp„ !`$ÊHŒð)0œuqcÎÍF°è´ž¢¯)/Œq¯ôZ¯öÚ˜Ùœ´h§~ܹ¦ëåÈUi´„@Oÿƒ;–¶F G¾nÏ&þÆ—Ž ÃÒïDlSõçZ&ÂP‚„=ØÈ€(#Àj£Ø ؃iKŠÄí»)c1Þ´‘›³„soK@2eÜqý€ øŠ@XƒG`ƒ@ Eè4¶ÊŸ°ô²šh‰I¸Gº¶Á€BÐ9tâDVä'nâEìEàCÁ–^Ü”í']Š,9ZÛ—~íìÙfPÏ>o¥è¹>34D´BKD«´«q´„ ¨€§BŠ5ø€€F$0¢ßÂÊšˆ?J‰˜ Ú é7l”?ÈÉ&®fD¦°öî oâ+ƒ\ÿæ†Û9£Ž›ú#/kL•bg¢Ü?Éäu 8 6ÄYg}Z5Úi†X2!gP¼F†¶ÝfHn»Yf‰mD±ÁB)”D _L0b‰ø¢fá½w^|D±G”JíÑh£Œ5ºÞz=ú8$‘EÓŽ@‰¥_~B#”QJ‰‚ R¶ m¥•aÿ ZÂk²aH›m¸!¢ÛDv¨‘P’Hˆtµ¢B“ðA‰°¦%s 0%  ‰}F&¹$JNÞhÞ¢5‘‘J:év:ÞM7¹(§Xå¦Ö}‡ mùYi^¶€˜†âj™¥¢)‘(`@Q$Ô (ÐX¨—ˆ €$zõú«‰žFÙhwÆ÷(Žè9 ³3.:-¥ÙjëcLÕz*¸áŠ;.¹åš{n°Êª ê‹£ð hˆD"P@AÉæ*¿±v¨.ÀËZŸ·ÖöxŸF{çm†^‹í¶K,ߥ-Ét1Æk¼1ǯGYŠx<2Çé #ÿ¸€¢ÊY!ˆ˜¶ iïBÈ»¤Ù\Û†ÿž¼ó€ U1‘I* ÄɬÑ+½ôÂ"3=Ò“Ø ­)ÏŸV °»Š&‡›á@ši¯Ú\زzX5ÚTNM´ÏîAŠ_ÂlOÝðo'ý4Þxß-qÔµm±Éiû}uÕ Vù²ËYjY³–`ç|æ"Ž>ùDûtÐÌ"*íß{û4­åy‹>ñfk+Ý7D¡Cøä)Sžª1wÍu¼nYvivlh¶ä¯ûÎêšš®c£)É8Û%Pê‹aëZ˜ž~Ð#p;ZÿLBDŽm‰&|"óP(ÄI­xSLëzF8 f††3¬¡ hV%ši).“ ™9 r±jNLÞ‹Ø‘ÍÉïsÁ»ØyäB±;1J¹•Å…¼q…V ۥØáP|‹ˆ×÷âuÀA(2mÄÈ9hÄh!ñ'!ì$àâèÇQvÄ(|Tá ¿!Èz[ÿ¬$§t¯Í˜¦4‹0DãšÂm—¤–(=‰®a¢kЧĢé>IÊeÊñ—ð©"!ƒI-Bj/‘½TW.dˆîER—<Û5öËLd ™9Iæ¤cêÑ&Îdf éÃÎ"AS•K¹Â)8nÊò4\»å#%©@pêSY—œ§‘Jw:'ÞM™ð|¨ûÚYÂzâSšJWA'‰E@Â{ÿ<àF¿)R^ft]Ò”h<:1‡ödo,…(L7R(„Ö-•]%5[YÒòtJ—$'ÞtREÅqy4©è.uTGÙô…ljO£*Ur©ñ©ÒPƒòÒšõH]){ðçU&6õ©S=+ÿZ TÕ±ž«z  Q­úUÒ•<Á+]VW2‹B5­~ýk4ÛÔ¥+¯Z%!þä:WêÝÈsè4lIÊÊWÀR¶²©;©b UXÑ%öz[]l¶7ì•{Å©eSkÙ<v¥x*gëÇG¶‚–H˜K^‰òÈbñ´÷T-pûTÞºí¢´fÛ÷ÙÚæOhm{li©(YÔ·ºSnhEV×Ñ-އ=.sݳAlᕎºÝî3§û[ë²·§ol-Ó2K<úQ¾áa½õX°úöhíýïN ^íÀ÷t ­ï€ï;”LBª¼Ûmzû+SXŸ&î|ý˜´cvWÁt˜C÷ÿ»4Š:u²>q%X`¥­xÄF›§}=¬UŸÝˆ´Ÿƒ0ç$¬(ó˜‹KŒ1ü0¼¿¹1È2îÉ`—Û‘³ÉN~21{,å.±Å³r|±EÓ™ÉRÜ|´]&¦³Ìfîë”ÓüW 9ÈB;ÜeAr½%Y÷aþð9?}þ³ŸÍg4«¹Ð×-*—ƒ‚e½5/ržszÝÚûä?þÉ¥‘éMcºÓšÎ¡ -êÇqÑÙ2rMó›UTC‹jÄÔü¹ ="Ô£¾u8SÈêŸÙ™™,]ê®C®äÖYÓ Cg“ Gb\3ÛÂW 6rçú¨ƒB;zUÒ®Æ@kÿÎ8 Ó½®õ·¹këf“;†)45¥ÐÍ]ɵÚLãvƶ“m•’UÖç É’s¤ìq—»ß¿ƒk¢£ Úó´Û݃w1Vçä!›#ùtö¼eû»âë  º%•ñ‰«Ç«?8¥‹çûl«ŸÖ·þ(nñ•cPŽ)ÿ¸Ã^¢ØG?t3ÝÖÑÈU"ñ 5y/åWÏúÕw]“¶‡iÈõîÑá)™úDƒ~úá[§Q±—=…O!ϧEùÉ—}íé,d¦GO&À§TFŸöQ–žøÞ¯Îµš¯üã#ÿ¸ä‡þâ%ýý5Âüž‡O¾ˆãÌ]k™Ýÿ>þ+³(ñßùËÏœÿ„ó­_zQóhÞO@W~`ŒÀ ±ÄüÀx8ÔûQWþe`4Öf`Òÿ•ß^ ×„}à‡Ñ¬FÀœ8Ì8˜€ ˆ@°€ ¼Àt Ì@\ \@ <ÿÂT@ tà  €$8‰rŸð­K¸hàq`z úÝÒÞ2M   W ŽÀ < TÁ#ÌÀ ¼@~ÌÀ¼À (Á À@¸ÀTLBÀÀ#øÀ8‚Ì€#ü@ä! ÌÀ œ ÀÁDjXúT•ÜÝâY ×ñ(^‚áä‰"ß!)ª :Œñˆá**Ú·­ F¼Ìá FÀÀÄ `#\ Èá#TÁ8‚L#ã#Á(ÁLdÀLAÁÅÝŸ”Ä—¤_ë•âfXÇ„|bq ª ¢7ªžš#¾ÞWqZ Nÿšõq„,^„ ¢HÂÄl  €Œ 0€|À Ü€2Áb1 dÀ?²ÁA €RTã%>6¢žê¥¢ìÉÝ7BÄ$B,DèŠ8^9žc¸p£Aœ¢:z ;"Á¹¢ ™ “plŸ¾ÏedðYÒùy¤ÿ…dC$A#|€ ô@ \€ A"´…hÁô€LÂJ NK2'nãž"kÌdù™¢†•|½#=Éö \%Ït¤Q¦"R&„%\@a´AhÁ®AAtÁ%ô€a^‚fWúR(Ö%íb9š¥jÄžf˜w‰Ý+ºÓveÿ¦OÊÒÈåÉ¥c’b°h˜€ˆ@_îJbA &ÌÀ__.¦5æhªbMåd† 6Ng ÒÁ,]<:œgzPsebñå¦cË$BBl8%Uj P¥A^å_$AmÚæ‰‹sâ”ò¹ÞoB_y®'{6™ª9JqÚÕÁxR0­Mó,ççs‚çYIæT¤úUÒ¬ee~„‚,GTÁ xÁtDø€8£G¸AœÀ¼A\nJÜçÄ1gfŒ¢~’¦ˆògöèç‰b‡Š¨Â,ÝÒˆ$£Ì@(Áv€Ü€ÜÀp@ÂÁ ¬ÿÀ ¬Œ€ä?æã rÀÞ.^ÄœÀ „€P „€< X ¬€ ÁÀÀEÊÔ‹†hx–¨]®(ŠZR‹zŠ¥‹ÞÏ;±¥x§<… â0æá#XÁÜÜ€R©‘:€¢¼€8B¾¡XÁ# tÀä äá À!ºÁ 8Bàá <"!zfšÝ•%›~ kH8¾ië0^uª€ ¨Éiª€¨†О›ZäÕê­ªA˜ ìjøê€RÖyU<ÒH4`€@Øaª ªø c/>,L¨£Ú".*$Q §"Eÿ£EdÁ X(¼QÁ 'YåçܵªzRHB«<ÄI6DÁʪW2ž—¨ 4ìÃ.,í9Õ].‹äIì„8,ÄZÆ–£³ÎÍÌ! Ð`ÀEf@–"éÉb*‚ R º:Á œ@„A?þ£ì#@ ü, -F¦ÇÚ+È„!g¨hzn üE\XeU>¥Aˆ€¨€ àà©—HB |€ ,¯ª@ Ô¯š@LÂÄRÓ:•äMÂÙ¦íÚº®Â­ÜNÅ®Y2yœ¾bË„^ÄáâÜ~('KŒÐ[ñ+o’'‚Œ¸è¦à­˜ÿ@" æ%ð€lÁ®\ÀÃö€ ô@ ôÕŽ­É^Ì€ÆfìÃAÌmNA *¶îëZ‚ÆÊn³:•_)3áé‚íž¾ ãÆWݲ¨ó…eon¢XÒéBDT¦\eBh |#ô€J¦êúãa¯ l@¼­ tÆVÂü­ª¤NÅHŠ/ù¾m p´À„¬/àRDZ…NÁœÅ×Ú'òB®G2¯GnŠü¶AU¶€\-`!HF[èêôÀ÷ÎeœΧl0>%oD¬í¨‰ ˆ°š,« ¬‰Ú€ÐÌÁÜ´lÎåm؈pêø+ifpAÍéêßû^ÿŒÃ.J–° q_r–»ÍÔFx5bDâvæ# ¤FTñ0úÀzD t@  óWhâð:ê°…y0Èüpå¨1ÿÌÚª€$8lÏq¯òíˆ&‘U oKÄ”ià`T€ L€ ”k¬@°À€•b)¼€X)Ìì P¥f’©‘VAx€ê•æ ) ij©„©ÔÁ#ÔÁ˜@Àìâd ߨ°°Žf{ž ¿N—_ñZ0CŸûªË_'qB$s Ó/ML L©B€ 8 ²¬€ £¡rê ¼!©ž@øâ—f1i8ÿA¬¤:"<·R) ¼8"’* L <ê  ´"ýä²;&'tû>–Çèò/ëŸ1HD’²$ó2+„±Úªbq áѪíšQ(c½Bê< ò#¸Á ¬À4ª½rñ DèžtjÄK‡+$ðÁLgD=Ïa-î4êbJÀÀX³@ƒ¶HBj~˜ñ„Tûú_%\uU§bV{IÓJ52ŒC?´D¨A˜uæf&5@ dÀ¯î'vÚwåU‚áÄ–2 „¬€Á ðá A\À @ä .) œ„À HÀ<°ÀE ¶†¾ÿÀ(AÀÁˆ?ž€Ø`¤Ĭ£Ñ²#p6>ºÁ@Ë'Ô$Lõ"¬Áh%LBAâXäõÀÃz  ·ó%HžôòuµW 1X‹uÚµ%LmdeX­Sö#x­ P¸@tAyŸèD@cmr·w?ex»À8åy§7È”›áÜ€±L¼ ŒÂÄðF`ÁØl_O~èRÔJuDøÅ¯«¨0b\@LÂ$p%À´A\ãÁÁlÀ„ÀPàÉœ°´A#ˆ8‰{ÉR2‚ˆè8mNHÿAr@X¨A x§‘èx ¬ßLwW·uW Y«€ 0‚@÷kZ‚çnÁ„®ézíœ ´›ªµSý¡w¹%|y˜¹ Ä€HǦ9TM´ÕQñqD1 ÊqÄõy(c½¨%BLtA X‚˜À$¸@ <@ PAðŠŒ€x‰ Tg"´ÁÐP $A¨´ÁDtú§ë®dX@¨ÁAˆkX8†“z˜@º©sº$ÁÐ A<¹òR¹1[ù•ï vOHÖZÂ`ZBõâ6¶ù„Ø<,¸×­•T»%\{¶oû˜{ÿ;¸‹;{±D÷²¼oV­Úq¹£w +’DÚ@4F¯#¹%\¥­:ù<·%PÁì~ ØAðÀ—ëîªOo«t+ü„|yD˜€†?ºl%¥C€ôÆ9aXB\|}·ÚÆ€tCû€Jû´L…7ßµWe&ByÁ"p€ LeÐGA½ cˆ_Î{€˜ò¼ €<Ó_½ @Pɲ`[Ç0tCkýÖßÑŸß»N@Ö+bK”÷`tA¬tÁ¸@ÐÁˆ€Ü&$üÂGÁÄ€ Lxûs?w47šß=Æg7nËîsO‚Ûÿ{ŸL%n‹À@÷ßOÿH`&ü4·Ê‹mËüÄ‚5Ÿ×< óþ¾ÐÓÂõˆ®(X–Õd(IKŒ)sÕΩ™É´äv"¤F`2‚Üqø„¨¦%@\þá_À"L l7ß3Â÷@"XÀ \€ÌöáGÿ ¬Hì½ÅïÛ»=Ü«¬#\ß?ˆ® ç?»ç/ô‹nJº”•èSU1‡~6’hÃ/êÅ?ëç+@àèQAƒ&täh`B‡!2$‘bE„Nºr£Ê‡XŒ±Åb'>2™ÐÊI‹ 9*lHÑRÍšm´ØÔ©³ÒNŸ:õÜ™è§MF:I²Ô#J5'MòtÿªTK—. €•+×EÁ†ësë̈[± L«¶kÛ¬ Ѻ•;—n]»wñæÕ»—o_¿yŽ­©õoa·Z6œvqW³1VŒŒR`Ê—'_¶¸ð±fÏ™ ®À0Š #V„ØóË ƒ(Ä0Q% Pø1Æfp)¡=¬kÀ } Gf›Ð¾+áËRË-¯ÊêJ¹„O>Éöó²@¹ò{d!¸ lÓÍ7áо8»Î9ýʰ 1ÓsÏÊ4ìs¾@ âlPB½ : Ñ55áâÑ‚2 BˆPs¥ fî7|Pá+™!«?0-è %š‹•â‘ @ é:™YÓW_à VX°¾üRËb‘MVÙýŠE“CÎèŒVÚi©ÏË ðZ¾üäB2ý¼ÌPD1ó–܃,œ$ BðȆP8Á G¶h@$•( =Ž;ÿA[ƒ8•X…#à8®¸Và(ˆ„Åî,6ˆ ò"Æ”8)%@¸A‚ï.òsØšiÃ-RNÂŽ6€ £&:€È‰f5,‰"æœwv½k¿âR(K†.Ú&¡²Å+ÙúRˆÍj¡ŽZê©ÓjhKÎ,LA9ítðÐBÃ5]@ÏûkBÅ>´…&AÒ²1ƒ” ¯Á∷/Lû‘“-Q£6Tè¡5´Ø  ^¾§6> ÃŒ CåÈÛ8ùg4ù/i³N³Ü§©]ôÑëèên0OÕ±ýÒk2Ïþ3v5Ɇ{\¸eÇý[ƒÚÖ%ßÅ5ùä.b°ÄŽ z°ÿ$†.,¡ˆåkŠÁƒ>0ႦºØ )Ëï :s±6çOO/>Xƒ!ÒÑO_}48=±ðƒŸó¬õz±'r‚à(9%EØà)¬láʛť7´=fvÀs ì*·.¨Àmð@òz@K$! @ˆBM€ d¯&Zà÷ìâ½ïËMžƒÌ°‚=°n}5´á´Úw,ñÑÐ0ôSÚ|H€¿‚„`$pD…X„à0Ø@JàÒÀ !0xRœ"˜Å@¹ð!œ€ .ÐAK$Â/¸À$q¸€p \ðÆ$AŽ(¬‹÷èð˜€ Ø£Ô N'“0€ÿG5|0,òÓÚì¸p/t  »á%1Ù&¾Ç‘õã¡Ö:™–‰€’(GLH4ÛžFðˆqù«ï¨™ÛZ¬HN€7Jw ”`S¬b¢(S(‰ #¬M¾)Í&@0BMÔà›dÓFH‚N8¸K0B=xP„DèQZ@Þ¬Jn5 %0ß#ÄPv}2“ýô'`Â$ÏøIm&"0ˆ  ØGŒ mC0‡ÌbD.Lâ{ªå-‹™KÍPQ—f±ÂoªP Ì b¨Î V †Û„;¹ Ä#¨c‚´”pplVŠ‚laŽðA°óÿ/pà Hröà%„`6UA•Á“¡g…Y¢&Tb ‚hA-¨C MÐ¥Øä–PAz“¥)=`^b<#0kšœLP=0`˜p¨@ úOÁös“bÒ$?íª&âvùÎBšà?ƒ¸¡w³* 5¤ÑÍh¶Š‚ ¼ U¥/p@npÀáTPú“^Ѐ—VJ#ŒR»Z$)Úi±+S F$BŽn…\È„«ªÍMÏUu…h›ÚÂ7Kˆ@i­Iæú<æ=ïš+Œ¸è@4€€0žÄà$ v°õ%l@ïDßÿ…‡¿ elÿ{Y-îRmœe@…àV¡³ª20‚)t€ ‚J5+ äA%A¦шO˜áiÓª`«|g¸i•šÜp×$x¹˜õå…ÛïiÁ*Ø@´›‚.p³°Ê–¹<º@(Ö©‚v^3 jÍq éI‘FÄFM xCJàÀ@¿ö5³ús¬+sŽNféAAhôÊ‚œäG’Ý“tâü'ŽÊÀ*¦Y`°#`G4©‚ÁøÀÂaSSx„Æ^°€:¨Ä#A@B¿ÜÀE ¬™ÔJŽjÔ§Ú€_ñ’ñ ŽhT×øÆ<Ô ëÜóÊÉÿŠ‚­öÔ)}`Gk>󯩕æ,•YÇÄÎÓÜ|Pø@§¯ôé–Ýl9;â¼¹ƶçÌö™Õ¸Û%Š0‹;"’!¦ÓÛjÛùº‡²Ž­­Ø;‰ÀŽà| öêØý®aûb­¹J°û/[Û¯š®À+`¡Ó¹sÄVp…+4•nO®äøt2b‡ã02"òÇÆÒ8;Ê,Ï%±lO ®àŠê°R_€þÊ P 0`:¹ÓAÿ½“krHþ^/”6ib zAó\à/µ«’ -½B€:§$!Ü€êöàÿÏeñ¢Mã@fù6f±B_qÖ1xsþU"}vëd"H`ŠÄ•òÓˆXÒqóó7r‹Ô[}¯ñÚ†QÑæ§ "aŠê¨’ã¾€9jc‚ÉS–/lõ‘\3µ¹Ú3-÷œÀ n@Ušà$óPH ¾œnsuR bUâ@Œ7yã`–7âàoWp ÷Ž×pÓFw± bÉrí)·ð¶÷{Õ8µ%¸-/^€F`„ëTfªV Šk:ÅQ½¶ŸVcuAþ{ïí$À H –`.«!€´PœŽï"Vÿ@æ`þà‚ÿ€¡8¸ Áÿp—l9 ~ R 8ϰ`íLö‡ ¢VE ‰üèP †L†Q„.×a3WÛ hj3« ”àV¬TàW%T…^Ìñ× lY7BÖ§XˆÅuË,Ê4´°  X’GΑfƒ/Ø‚ÿAƒ7¸‚bÕ D˜VI8=+(HV†úØéú²oû¨ üÀ/´ÀXu±Vçf ° :5‰™ ™÷D‘Ù²œ¶ŠuräÊ7R E㤀’U Bµ¦#à`"&^(ÆötÉ×u‹M‹ûílA}C . ’¡Q5S~Ú”ÔÒØŒÕ¸6X !ŽÿïÕ Òàf÷à†màR¨ÞHTPy˜§_Ðbpk°›\à+rQ–c"xÙÙ̯ðÎù’1yr)Oã–ŠË-R󆓯•R¡4zû—íH…\ÈØრÁ˜ ƒƒY þ Vi¶o›Ù™Gtç8‰FKè™GÕª:ÑÒxðxœÉ¹{ø9QJq&è£Âv“;ôïž™ñøä¹ÏBŸÑ ¥I¢uó, Y˜/ŽÓÀy‹ú¨cÖyçØ½§ŽsPØàç vìšazÒ òR¢ À¢ çü«ÛRú@— ¦]ÍãXzb3Ε}‡–iú8m:}ÿœ´“d`xúƒ ú‚b’Yœ׸9øƒᮽs“Ä0aO9ýuØän&@(;.ål²%€,û 1›²˜u×zw6ÃmN/A½nPÀU“.®Cp®¯x¦Å.oàƒkâ§BZ*¸‚š7øƒCÛ'††þXèuí–ƒmnŽ ÍΚû¹íÌ¢»–µÏ—ŠÎys‰ø?"?Ò™µ?C¹]›[`û»õB´ÂƒÂ%¡ÿ@°3XƒÝ8Ád@¸ñÒ$“0"SÈ©É"v1 ¥'Á @²RÑ  ¿ ¨Àü³«8´‡Ð€èæ¢ÎÛ—àÚM[¼Ç¦¼ÿm(ÃÅ6/Ôû+ÞX¨z  š{[ƒ‘ù¾ñ{†{zkÕõZOÀ k"Ü`Fà-Çw|w|œÇٙó»ø$£“´ R›m(–Ã)ÃÃoè¼åIZoþšÊúƒ5¨Ó˜Å3˜ ±÷»zgüp=‰¼© Jï"/û¶XÀͯpÍÛœ U“%¼eÃæÈ£È­_âl×YÃýü´ÑÊÍ{Ãÿd/¨\ D’´âÄóÚ¶ÜŒ‡ÚÅ aÄœ$U¨/Mà/ÀL`0i…{lMçD£<‘Y‘yÕ-¹âèÅÕ³­Wî<Â=ŽÂѮϽN \ÕÉ™±Ð ýÐφxyºÆÿ ÿ’º&T\¨À¯5˜ ?8Ó™Z'Òó<±ýÚý{9m•±¤ Ä]ÜooÜÉÀÌâú\?=£öŒ¥Kñu×¼k%Ãé|½ÃƒýF+/Ÿ#XÙ ¡&¬™Õ[Ú™½¯»œ ú°a|ÌmBVÔZT^¬dtü¨´¿¦õèzn>@ h•äE^’ÕäãC~äïôÐÚ Þ·ÕåPx½ÝãÚß÷ýб,B)¿QœZ/™Aؾ%ÝÑ[á-¿!!Æw‚Ní4úòTOÛ¬ÛßÇ=‘o¯mXrwBë/“æi:ÏÝyž7K?.\RožžÑß}]çwþ¦ÿ³lõ è}ÚÑ“9@±Z€x æ{¾£ 1Ýœ¾{ÌpzõWµIX·ýM¬>N;¶ïB&âÕ¹òKæAľcâ-bA|£n¾ó]®ãžß›¦L è"<Ù©<0$A ²˜·Ü·¥]šÞá·„¼MÒî&ŸJP‡{Ê٪à fº Æøi2ØÏ9k­AOÞ¯d,¯¤×³¨ýˆüÛSuàþ>öÃõ îÛ¾±üÑ ¡ |[÷ _A/!z / +î"°âþÛŒÌ5Éÿâ’À <ˆ°`GmÁÂ⣉ NÄ‚Q"ÅŠ >ŒÈe££ÿ7št„’dG“,[º| ÓåÈ•1)ά9‘&Ά QúI2hPE?uÆ4º3©Ò¥JG&| 5ªÔ©T«Z½Š5k¨B·”¡5 @H†!K–µD(®\¹†ê’‰j ¦nmU€@ÀY­`‹8±âÅŠ½®z˜±äÉŽ[¾TŽ£=0Ĭ¼ùÎ#ΞE– ½¹óg•5}²f ;iJ¤/gï¤ ÓhÑ¢±möv4 eïá²+_>Ž<¹òå—»~ kÈ,¢³i§#ö   ¹fÍÚ]–ª:Ô\j«âÃ-ÒOè‚9¾–ÐùAÁ‡J/L¸°U 4pA"èÿ}Ð|ÇE6Yƒ‰ÇA :è „Fe‘#$<‚¬Æ##pèáI%iHâ‡6½ÖZJ§ãŠ/Ö4cnLMÂSŒGéÈ£I°ÄKVøÀ.Y€Q\áà0,¨ð‚  ‘ˆ“"hqA"Lr F Ĉ .˜E¼&Ðq‰ ]èËo¼Q4« /œÜ—$]&IcNgfXhW€D÷f]'R™i¢“°YeÐ{ mÅ1Á èéU1 Ĉ†N9I–—}j‰½]ÄÛ†»íºÐC¾ûR!‚À]D½*s¨¦*Ùà {âA°ðȆ+vÄ (æš“×`‹‹²ío µè«LqóèìÝGYà _Tƒ#Wˆø#ø0$ SL´ /4 E FÄ_–ã ß$1|`#=ÿ˜Ðƒ —Ó¡‚%þ†~yB oÍzëR9TÄA)&XgC]w×qï†$²T“`€ ›,«¼ü˘í, Л„’ê,ó%=·%PО ‚ Œhу%”[ƒ[°õy)èÛ†%F¸à¼ÂTr5Öl‘„¿ë­NäÂZh{D¸¸P€”QÞZ⚺é-7dÉÜZCœd=p)” ´tTŒ  ¨ &Q…˜m ‡K\&2ƒ-8âMzÌà^FÔ@¢ƒ,éZ¸×%6„$Q E|ÊêüÅ(¤+cùRu¢ƒ;³ …cuñŽ]žÿ¨ôk T¸þîÕ —Hcÿ¥¨óDaç¹@ .á³IUê{aS÷„f¢±¥ˆ–À¡%|ð.ö%-v‚ ,ÑȪ-‡Ao¡PP*±?)b&D8‚`†p„ÐG&ncJ;¨7c¥‚œ ŒPéÊG`@4²TV€%T`hâÂå¤pp CD`„v©`ML‚ ´`ˆjpA ’`Ä(`Ô´&6b Jš,§&ÕB±*Ö:4 î¼ãE³¢.`T.0ÄKÍpA}ØãÏÀçPDPÚ° l@#˜" ˜((€@Œ>ù5¨ÿkF°„2Eˆ¨¡›*p¤ ¢¬qa¤PsÉÆh2Cµ¡iM;‚ ÀÒ‚p‹ekX™“ b0ƒìÑNORTþ`"UøAˆrœG’欪U•SO‹‰iÓ‘ÓåÖ1+ÜHY¯z é1TUŽZãR¡`2k3Å©#œàá´í©wÍ+M憼êµ"@–N}ú«Âr¤)ˆÍQ]9Jß«­h­¬e§‚ð€E«w™ÒV°‚ÇžV9«/«·’Jk,¥Ð\KUWŠ„`3A1[ØV¢ínÛ¦XÃ7•…UåP‰:X=V$Iýc)‹ÚèJ÷ÿ‹¨®fˆÈ@vËnhå´]Cvº S-c‚Ò:ó®º[:®PPÀIœ`"¸›MÞkšá«” ªbõ»ÜX8ZGu yŒ`," ®îvÕ";‰y·.‰èj…Ã[áñ&¸9/}zý§^ý±WK§ÙÀ®°´1„U¸LD£8¿fɲÛ_Ó •5nHs3¨ÜûÔÀç©Raزá$3‹`2ƒ­ žÛu…ž]…Dx?«a%/H(å 1ÖF|ª•Àà0`­ƒ1›™·c.s¯z,Ùá8Ç=ªqKçcå‚9ÚDH,xr ]ˆÔ%šô¤ƒ¤ F$ÿ£µ éËÈÎ,û›C”¥,±î‚QÓaÉrT’ ÀËq i˨õ>‰– L 58@°ƒL¥˜!TÀ7ÀÀ™u}ƒ^ë¸!ºæµ¯Hà‰ÜxÎ9*)yŒ7!;6ÚÉ5¥I°°Š @ XAN°‚ø§pÁ"e¯|ti=(bD`‚ÑÚЩ®7Ud€2’dìØÝ.Å$¶CXY-øl§¯B‡8Z‚0àœŠ‘Õ(TÛ+j5BGÙ¢†Gbü‘¿u3tXá ´ÜkCB~,”Üä'9Éi­NRäÆü°³-¨ß>ïyG;ç3E°ð À k8#ÿ*0ƒ¼€èžx „îm®s8¼”úr´G¶/=hôÄ¿ž0Åe$á·u7ë]Í <ØÍ¬Û=ý”.ÀÈ—èB}\†ž | ±ƒ´p÷øµgù v­(Fsœc„ôe;™`z‡â‰‚ƒs©êœå>ÖMÍ›%“SÒù&°+ý—hçÎ÷8õtîE²M‘ A0PBJ"eÐ[ @¤Ü%éCh~ÀsC,tâo|“D’ŠÙŸ¼ÝEh—àl¯nf¡ ÷„Xb@æ$ÑGÑAlê“æ~‰¨ ü—ùB€> ß0’bÆs $¡ò Æé³¡úÿˆs9‡g&As(7¦§€£=W€˜#6$ðBJP° tp/I0GN§á#*ë&RŒf4á£àtó‡|`Av0€Ð`5^ÕÅd؇}YÑF`3Q@0 Œpa_` ðP„Ghqä‚~¡Ð$Mt =)€]p•0'ÃV±õ27@öå#ÃV†q†À•z’z=KBauh‡wˆ‡yX‡§'ÂÒz¯ä‡Òö€:—UhˆÈa;…@v&vÁ„e€€1„0OóTOÑ{A§u IP_0sÔ‰¦B…‡ø^–ÿ*`!âAC *æC‹³x[¯x‹07Ìr€ohth‰@ŒÅhŒÇˆŒÄh‡|ƒ˜ Ñ(Óø%¨v›eD@lˆ·#Ol2ð“X‰ó4&…0VUyƒ!mÐŽç!x4‰b wV±GÖ˜ª¸Z±ut?À70X ±k `É%J ùf©Ô"r#_b¿“Œ™Œ†ÐÅX‘§ŒªˆÈ²#I’%i’Eá@'É’¬È–…]4ƒbÇ’€10Ž“HPc…YéHvp¢ÖKbw  ¥S" ’Pwõ±”ž8ÿ*(ðD/)!kEW¸Æòõ#òW%aây–bÉ‹w“7¿8‘tˆ‘q)—ÅØÙÕÖŒ}(’ÐÂWQå—} ˜ù—)`DFqÔˆ˜‰©˜‹iz¬²Y€8ðNÊwÍWvq's‰…0ZZy`þhIuÅ °°ðe_ŽÐ+ð§©c£Yš®‰lô@?ñy;!:@s)—‰‰—1ò‡®GÁJÅécÇ9JÉiœÊ‰œ@˜ä’¡’,IÕi׉×Uñ˜©E€€oJà|—É>yŽB陨 `k\™I^Éñ^@+“€ÁcùTô‰ÿö9›´™nɹ©º›¼y‘$q  ê jBñ›y¹y„X ñœÍ²H¡½¸÷c!™Ôƒ%pRž?”–’žÒ>– =„4JSnM£/z'5TSW82ÏÆ=*€,ç@:€ÙJo#‘4‰À  š Š@ŒŒ@ŒPšRšTj¥Äú ˆÂ¤A1¡¯$ˆzÖ¡ƒ&…¹¡FZ¦ô¡S^T1uÁH²;Üa‰?É™aÔ¢êÙ. ’@>æÓè£>ìc1`((?ôÓ¦ Òlóš:l“ © YKA $ù¥ꥋ ¥P ªQÊvVš¥Êÿ¥ºYŒª¦À¹¦87Ú6!«½dÐ阞5é“„À(J^4'{]‚d „dHÀ§HŒäH>¢ùðpŸÒú¯­ÓZ­y¡¦A`Ê]q¥çª¥žŠ(¢Ú(U*¥ˆÒ®Rʰª©ê¥Z ¦jœÖöª+«hJ«Ú³ÿÇ•ê†ýµSËr—ù• U ªVÚ¤ ¯ò®»)ÿ¯í ªMº¥žê®øjSýÚª¯”¡¿!I6EG Ó\ àB0Ÿ*,( VM" K10.``!P·p°&p0.1ÐT_op·(°kꘘF;wño’YhѼ“²™È²ê Ÿñ¹£?›yA{S9!´„ÕµGjJÌHXQ µ¯Ë´] ¯ ú´ñj„W»›M[»L[«Å2¦6µ_û\\áB¶±Wøt'°Á±A7tl€ŠpI÷'=ê6àÒ¢ö Wà6ðSÀUp³„C3àûZÈ!¶aBqE$ßÉÿE¼óE!ãë¹\Ù¨ ãž”œ¥[†¨w›Y  \î »°ÛMë´N*ÁP‹»ˆ²Pj„º´¯«©’kJdµ Å«°s¦±±­Žp¶ÉòØF4ܲ'/`{ÐTŽ ð›,„.äkðB(4B}HÀà‚«[Æe¡C¹bQ5ðN¡Õ“* À,À–UIÂZ]&¯È:f|–<¡Æ&һȲ¦‡¡$1Áu ªŠ`Áž »V›ÁLŠ0Á*!sõ»vc‹,BÇÛ,'0Ž€m&±0°Êk¨( K5-×ûJpšG ¸(Pÿ¸°7ÐT‚+›ŒH·¼aj¦æ0FS,&gQÅ`õUßU]üÅÑÕalU¿Uµ ‘>ð*0Æ ÌÌμÌkÙlHj›*µu¬ÍUzµ]ê®V»»¯ËÍ! µ_JÂÀ)ÇçlÿúÄýXË-|«6òT"ñ'1Ïþ×—šÈŒ)dÒµ£«e"¬¡5Zƒ€Á<]* Æ£dt¨™+ 1_}[³"ÑC»gµÙz#IÇÚ¬ÍyŒ Ü¼Á}Ì’PÇ@Ò"|ÌD[ÈnÌ€ê¼Îð ¶Ä;Æì<œˆ5ÈŒ%¿³¬j,| ˆƒþæ]Ü%о|2Pa>ÐI­jÂAšA· ‚ÿ#¤é(ðÔ ´šN ® Ë‚y@Ñ(’`ÒŒ0 bMÖcm(Ýì0¬›O ÖmýeÝ(cMÒa=Ö#ܯQz+yÓŠÈ«°ñ|×~ ÅÅÜdðÓa²]S6a¡uƒaá­Ô‘MqÂñ±W ‘Ÿ!h·[àÙÔœgäy×#MצMÒCΤÝ($ª&íœ_RÚfÍaÖ L›ÒÖD±Û¶Á2Ý}"5Þ®*Þ¤}â'¾2.è¼Ñ†Â¥¬â‚âŽâ,b¤dÞ¿MÓ1MçÑeÑ'}ÛÅv p>­ƒ•þÅ;.êLÿ¡°”Âþç–éç>â# 螚办èŠî6=é<"s£®ëÔê¨Å`¿®Yèd]9x}PÖëÇNb+ÉeÊcteÈêNËá#¾êWî¤ë½Á²®í䪗<?Á–6‘ãN’Žì¨ ‘î5ˆƒ†Ýdïþéç.ïôëâÓV0€ê¯~í[NëåWžíÚÎêüõ@·~©âΗ‚˜ Ïðƒ9ï/ñ?éÄ­Ïß=Î9 ”0 •`詺ÖQîñ–è$±í×~â¯òï5GO´/AœÈ¹œ5Oó7ŸœKñ;ÏóG^ï¹Jø>Ííö*Ö#ÿç$¿êHïñÿò)åcïuÓ0¿ßª[PMðUà^ .qIRxÁjädt—ö‹fm¯ö=÷qV{®?%ôcpÖÖò&â$Oò'î÷ ¾òÿñNÛñg-õSoVŸ—G€<,3жDùNpWpâB.æ²/°ëòQðBnô£øB0ý&“ú“ qrOûµoy??Üt÷xo(…¿÷ø2ÃOòHÖêº2‰¯ø±ýí÷MÜI~Ð7^8`8/ÀÃ#pXp3P Ñ8ã6Ôn|D˜£OS—Ÿ£~£S:þÒþ-ˆÐdûõoÿé…ÿû¹ñÊg-Ä“, $8Ð`ÁI“б0À˜;c$Žq@‘#ŒŽmäØÑãG!EŽ©‘¤È*HtQå…£*!^VqãÊ U6ÎÀòÃ’]€Äèri¨‘KF´\bz)JQ˜jAziC’$Q§VmÚ5‰ K]ÅŽ%[ÖìY´iÕ®eÛÖí[oåÎ¥[×î]¼yõîåÛ4€É“> ¤Ã‰ñàQ ácÈZšlвÀËâ‰8±âBÄE&š081(¡°"k×NlÞØÓaÅ /#`,@„‘DTˆ z)ÉR-)TlhÈC IªF10iÿÑ¹Š¥b@…‰D}Å'_ž©Œ¸èͯgßÞý{ønÿ’¦_¿dH‹ +^ܘò)«ä?Ì(Ãì@´d¡;»ãAϺȑŠL³ïÂÂ0|¤Š4âÐÂG4‘ÂøJ4ñDòÔ3cÅKÔCñEc”ñ½ù4´q´Ð@£ð¡‰\ <ð d¡‚Dè2ËDСìL"ÐþÊ#‡@¼ÑF+˲¤fôòËÕKcÌ1ÏH£Å¸ÀTsM6׬ñJ83ÜÈ"ŒѯÇËcÏ<ôXHÁM2À…øÜÓI<ÆH”")ßœ²Ê8áÜ2Ò»lóRLóJ“LNÉD3SPCU¯7)5µ£ÿ1š¤R%òQÏ>õÕÏÏ ”ÐZûäCV>T4ÂŒJ 1XÐN½pÒ8IuYf›ROŽ8 cZj¡…¶Åf³Õ6[aTGÀvLìU cÕƒtÓå#Aj­u’@Q7ÝYƒô5±bíñQH½ Ù+•½„2»Þö`MãúC9æøã@‘ØB ±x!Öxã»õ÷F:MùNWó,WÖyãdevWv¤‘—Y^™eyéåÕÞ{¡œÊ,§ÜÑãAX0,a„-„¶ô¡˜²C8ì(«‹í˜¢šc¬ÙRïa®»ŽxbB"=32Îúl´É:èút\Õ‘U7Ïÿ÷œU]AfvÙç)qdeA¯y×›}]”Q‘)D–ßµÙñF,Và1Y¡ŠV!€ zøÀ… €0Š«à† Î…¨jC;TpÁBO{vg™"â®äë°„ìÍHÃlÚ‹7ž­Æ',äæ.¹nsÓ|æ¿3’;ÄAÑ;pzùèÈÉÀ·Ôä=b|hSÑ ‹2wCŒ©`†^ !€’âA¦&‰á0¢Eá¶`µ €¥&èA zPµãqL–@",VˆBHìsØÄðôœÁL^š"xBúE}ÊûˆÛ¸Ô*ˆ˜Ìnç:7ˆ¼íMÿDrËÞög³ïXᎈ•/®$‘ëHˆ „(¡R¢3‚Dו(Ä€(—ðq¦b‚P*L»b 7&DPæ‚Ä ò»4P+qäÔ´ˆgF<ž­|, óFâ¼Bïn6”—ÞªW§ÇùMo8ܬô¾ÅŒA|:3LIã³~9N‰)ÚH % {à $DaùÓ‚ –C‡Ò‰ "p¨fˆ´A8*PÃ*óˆ0,¢‚”É 2ôLËa”!0X‹U0˜¿¤&ÇöÈG¸°4$ $¬P&½ºÌoˆü›öþ¸¢«^M"Ã$÷Å눛ÿ¬=#°jæ³E ¦!(C7@èiØÖÆfRìb@£>Ú¬kbsNº§óÈ5à .‡:¬“FzèCu.ˆ‹içÍÑÒÌ3NîbiK]úR˜Æ´Ve– !6CdpAÈ”ÁAwˆe4b Åé4izÔL¡”…Ú¼'É.=tMOœS–"Ï©QuÎ ’x()%áÏ1N©Íã[YÍzV´bÒ/JkZ—F™ÆU®s¥k]ÝÅ4`Ah&C QA‹I º›@+öÆŠ!¨He¬›ìi, !’0•a¬vEÈpZµ£"Z$#)«­võJ{¬ÐÎ'Ñz.m¬'½KÿÈÚúZØÆV¶³…í[S”ÆX@‘(Æ(h1Šiƒtã*öWC,¶±ËíXi)E'"þ‹QÏóæ75jHªöíª‚Óƒ ‰3®Nr!0kÞjµ¤RÔNVµÅç\Ì›ÞSµw’«‰¿ŠàŠ2h–¨À@,ß·€¾É²í|ÓøŸ…<¡ø+15¸»cõ¸&°} ÃÉö™›ƒ­a¾ #‹ü¡wµÊNñ&¬Þk¸2Yâõ«Å)vnÀŽ…!÷;°(ÿ€JÌ8Àÿ ¨Ãºã/âÇsŽOmÄT"W„?'³.„g6áq*¹Âè  §a’’¡B)µsˆEü3'1Ëyn­úA€1p„ B`‚4b 0Áܰ‘Qc:i$±‚3…|Ä =°g˜ù‚Æ b°øõëŸ0Hˆên¨€pœé|ìö4úBCógUÝËN/oK¦0µÿÌ='?Ñ\­yO¢ìÉñ3]¦‰Ù»å±8¤,æ…€FƒqAð4²ÀÍ(Ã#T`؉@ ˆAPp< !ø76`‚ÀÁà1àÐ@ð†€£ ®N-^‘…ÿgYX ЂJ´ …hAØŒ+XBØ¡ŸKD‘}ó½€›4x³ˆ'ŒFUª€®¶„ íÝCkûGÒ7£É}É–>ý_“îxZfÚvodWÛ$؇‚ìaŽØÂP€7°Џý^pÌ€Þ6à¸TàGÀà Ž°Á¦à¡   ï"€ fà}/ÝòYˆYpˆ…Ôà°2H³^÷š_ãF,bÅ®båŒsÐóEç¢avX}í]]wª›Í óÖd 7©"‰î§¬PÁ önƒèÿd4é@`€\ãU·ËÕ»LD"ò“÷ç^+ã—7sÇ3¶Z¿µà¿gJ ˜¢ÿУ¦Ñ ž“HÓ“AR=Ž‘%»¶BK: cº¸Œ Œ´Â&sÓ2»7ú€¹a8p&êˆ-pLãºP¥ 8;!ˆ5!8‚¶{%X8‡Cˆû€Ûà‡ƒD,˜Ÿ®“, 俵h)g,¨¸›jÓ+ß‘¿Š!¶Î³9²è P€$¼„6H Ãü¥ ‰ÈÚ½»„*ïʬ {œÖÈÒ( {¤Ï ½èÚKZ±ôÊ@J³º»¾Òš”ÿ ÉBÄz9lDFÔÀ ”©© DÀ àò+ ‚±üâC 6,¤?-  Pƒø€ ÐTTÅ¢è’.Ñ‚€€ ƒ€ø€Jxι€.‰‚h€ HKhÅ(Š2L›3Ä=|ÄøÊ=<04<¤Ä¡CGx=îÊ6Z©’MêC?ÄÀÞƒ:µ¨²P± 4–sœ¬aÃÂ@ÂqÄó€¼B° º)4*C(‚"›¶N4™®šû<´PH ˜ ‚KH€¥0„PˆýKK`F`ŠZÚ?á³Ø€) 5ˆ€dTF5ì#)y¶ñ¹BÿºÌÒ,”°kĪîÊÃŒHGg$‰eüÆvT!-«´/Êfà r$‹¼Zˆ¿² L„<ù«Â”9KŒ¦Pì ‹½bŠp ¦€€6€Å(ø€®‚ ˜RóÈý+K¦`Pˆ°˜HI´ùIŽ(½ Á¤ŠÈÌ¢ž@«Ésò¬ÀÑÉÚƒ'v,‰Û3Ê— GqG´81ƼÄ+/ûŒ¿b¨ €à«‹gúµ,$H³˜0Pˆ‚™¯ËKh€±lK³d 5h×L`ËpËK€Ëb쟺´Ë¬ÁËL"˜DZ½jT$tÒIÓ¨µÅœL*sÌ©#Ê¢¤ÎÇÿô¸›ª{ü«°‘¼JôÌãz¦™«¿³ˆ‚ h *¸€4CŠ6 €H0$€4«È‹U˜ (KˆËýÉðàÂNâ,Εì½Ä½±cCq‹ÄÈú£’HÌòJPíd-BÊòƒPíP…ÇòÀµîdJÍ´ ùóL…‚¦™ã1Ò,‹ £K K; EV¬ÑxE”Å`lƒ Ѐ ¸P€п(Pa$F¤ËÅš3<Î{Pº¢(9ñ= ½P"²NõzÒ÷@aÚLS3ÀªG 04E϶N¦`S/5° \PøÒ7¥-ªRÇ 5B"šN-•.å½7m`BJÿÜL›â§B=S4.5e 7-Ð@-°Ñ“Ò>5–ZÃËÀøPMÝÀ@5˜YPE`Z„óÎÍ\T4EDhÑH}SpKÃJ-Â,E•Ò Ð-U ´S]åÈlÕ±˜€Q«5Ö[ƒHˆ„E€„Uw)‹&ä² ô0PõÕVU6躞XM¢rãÓn Dlm #X‚ øMáèx8à⸀×´ `VdUVe°å«SE®Eø«j…‹DW3êÐ$ì2 W"²Õø* ‡W†ÝNƒ] K¸Kp6Ø"2Šº¨È#@ƺXÊŠ¶²©}½)ú°äÿB„]Õ ‚¹HPp½`„ 0£Šu“„Õ9Ø—üVLJ[›3• -€0Ô±š‘ š¤PŠ»È«½ê+4"ÔîdÙ…U&P¥Dšu :ÈOK ò µ FèÊZ5L£Ô‰­L*³@[MÚo\ÚÆª•3û ·Ø€ð° : % H ÈåAâ@¥¥ WÜò€¨„ÞÒÌP}<kÖU=ÛB]„Æm‹.0¸µZËb\Žð¿K°xÚYlƒØÝcM‚sÅÛ/Ñ[{*Z£õV#Ü)íÕjªˆÃµ„潕æMJ6¡/‡XüB#Pÿ ]R-ÛìY˜¦´]Ó 0€ß˜„Dh€¤X†´Ö½„Pô½õeß±(3cm“Œ½Y0V…ß³ @Í÷u Ú- `<ȹ°YœÕY7¥‹D8«ÚkÉ€8p‚Áx‰Á€áð›ZE$‘‰v°“hœÄcCäýCåÍ£…xÞ[Éa9ÜÆ[úJܱ`Â×emVkÍ^P%X$_·h#PË( € ˜ PFèÊK°€/°˜€)¶b,n K Q R×´#X²À²€Ô• 7&  ƒ®h]µX[Œ Ã> <‹DðÂÈÿ¤`Ò³R,€7ØÈžv$ DN-—DÇèI¹q öÛèòZÝ&\ÜN¡ÖaQåþ’1³„p^4#S‹ù§?Xˆ‡)&gŠ&Ï«‹bäÂ;^š$(/˜€ý$cÛ²?n“µ­„±è‚×mèÅ[„Ú®ÈϦèÚµ]Ü…]cì‚pf€fŽPTÜù]ÒbŒ5YÜH¦)à6ÅŸP€¢¸E×ÝeÎ]ã0Ò"àqT¾¥Gn—Ô ÁX €ƒ=(€€Xˆ ¨`ðAÈ€p€èHdŽ€Ø78жh¼ Ðè+ÿ`ˆ¦€ Øh€—Ž€ P`€„戚¦x8€½³‚`|Mþ‹8Õ( ‰-Þ?úd²¼Qžê†j?.æ/ŒÇa²„àµCõL ŸâÄÑ| ;0E0€6PëÑ]7¦ËH‹4Ŷ}`a&fQQϱ8ßôõÂ.¡‚Úœ×ÿKMXO÷ ‹øßú ŠK0Hìþ銰ƒÑ½„‘\©0°ã¬lÓñ-_…”ЧýM¦h!°_¾¦_ÉìçAþg $Þzzd% €ƒHÈ>`èGph}ÑàGh‚؈ ã >€ G€ÐÿX؃G@n Ñ /xÅQî „aù ÝN È€[¥æK‰=`ífPœp€ñF§>ÂåB3ªîï[Ù²§ù©!‹«‹—‹ÆC#(@(€B€‚’ã+Ê„bú Í“˜ š¹Zv ;¸œm©ÈgPHe I`ZñÃ`ø]L‰‚ °œ5‚(žb§H÷U*ìK榘º5ÎÖñ¸â¹¬Ë.éñ—l;H³JðM¸”ËÆVà$¸Û¦pbµ¬+—„ºDË X’° *¦H‚ÚT_¶:€ÙVJÖ €è G8Ø>€=¨îàÿF}AHK @10P‚F`ÄGî ÀG(€(  €C߃ùÜþ 7Pô&P„¼$¾=„=háJ®ŒàtBOK @€@@j=µ½æ#Â@ÿÎuÙ2 w=Ô1‚Õi׉2Bð,œ › Z*€(.ÃÚŠ9Oˆcd[[ÁV¨qác I un]8‹$0.ðeo‡T'µ”p÷¦˜ìxK`Ó6¨qãÐò;Vk6¥Ë/ÀMã fKùe¦ˆ‚Ýdí7ÌIYØHq„G¾Ø$ØsG8õ…Þˆ7ô¿@‚ XÄ@p‚hÀxtŽ À ÿP ¸Þˆ@;y‘'ù9y„ŠßˆSç…à™I@‚–WЦ&­üÔtÓ'©ÖuÿÞ2ÿ " B b ‚ $l<sÊÀhb(”ý@€?+$¶™Èä€x„È%ºøL×4MÁ€o ¸–k¸oë¯Ë¹¯{õJ;0ÍÚ%€>Y’´”|gŠ,w´^ë>¨„&%-Ðþ4ÊgŠD€¸·]×A> áõIC>•G€æ;ïGèmG íŽw„:MÞn8Xu ï1ùúÝÖä㦘'€GEÈ}G„ÝŸ(Öïíë¡TÿùáÇ1PàÖ\­uܳ-é5Ã¥gzRþ½,Ú¢.Ê\-#1VÆS-ÍÀÉK£ ¨¼ ®BùÈ~’¦ÿ¥3€˜äBA&ì\ê¡—6ˆØÈ¥Š—Èh QE$%\ØÐR€Š“˜P`q+×’\¿V àèÙ²eˆu4É,Û¶nߺu4à Y,9R¢`A8ˆ8à€Á£\q @Š) ß0; å~} ÈQâN8 @6ЀÂT6ÿX™¬ázPðûhlkcx”˜ãСáÇ+8ñâÆ#‡º+Z¯`ŸC.}:õêÒÑZÊ®};÷îÞ¹£«E…Š t}P!"‘@º1ô¡Mzj6ô˜ÞܹÅ2 !‚H!…hˆ†´€ˆÙPI€¡„R!"2@b†rØ¡‡‚¢ˆ#’¸Ñ çVŠà•œ‹ÆÕÖX1šE#eͨÜ$9¾uã/ÎFVŽ?™œo@"™¤’Bž¸_‰OBI]ƒ|W¥•ÛM%Šá=W’ ˆ@H€úà€„ð@J¡!€TXá"_jY§wâ™§žúU—ÿ¢YháµÖ’ƒG$¡‡²ehqŠöx$¢"ºœ‰ÍíY©‡T’„é•›2ˆé‰uRzÝ%_6 "þ`˜n²i!®bʰˆ¥·âš«®–~Úå+¶©°@2é°ÅÊèè°Ëº(餽¦™NÛÝ~×b›­¶ÛöÉ¥uÿ] `™`‚¦««Â '…Ö­»ïÂ/Š}Ò¨ZÌâ›h¾B6›$²‰*»¯À‰ö ­¼•V[ µûuç ¶ g±WÀâU±Å—dp¨~ià~¨HH4ˆi œ¯V¨nœsÚz°Ë/ÃŒ§Á]…‹ÿ¼,ÎKîL¬‘~朳³1»[ívwzÿ-ÒH[ÖÎË5¹±‡_žúñ¸†Ô@Cœæ¦笆̙ˆ#DM@¹@ð&¢Uu2Å aÜH}·”ô‹cÐÌâ a@G0à,àm±@–h="'\Ï{:tdóÊiÄÚ1}åÄO{+µˆµf¨ŽŽÊU£§ê´.26å/ 0G)ÐÀÚ"ZbÛÕY2ûÌ_ÉD·‡2Ý-eZk=¾¯‡þè÷#^¸C ÄEðóÅ-½r 9ߟzþ:¨–“ßô%‡÷xm2Púh™Šúêóω^âL@&\… Ìn*hCLb‘6˜ `"r“˜”§=¡ÿƒrw‰@@"˜?`»K$AˆEŒà‚Kh!¹@ʤm`&Að¯"1€ $`m4,à¸@òðQp G¾ò»°ämoNpÀcÄ „À¡9ˆ."¡ X€¼ ˆ 0À! :ð+<⊠èKhP`ÁD8Ì ðÃÙ‘p"`¶ @ ð;+4ò‘üÍ"Â1¸Q  ˆ X´€€‚ 4’…BŸ÷ %9'áïNF+§6·Ê'ŠhNì[˜d€‘™Ê@©›Ÿ!؇ˆÖ¹.–ï@"ÿ ‰ ì¤ ÍdÄ%(H£t¸„ b@Žð`$¡Â%a€J\B-)gEZ ’¬M 0D‘‹Ð@,A€h!,ç»Ä"@M Rá&1Ñ%d-,°­`°©Mn®³$vøæ%xÀ°ü.<ÃIÞ’àP7ÌF ȃ#fÐ8a6ˆiÀ‡GˆAKèÀh`ú“ºa~±ÁKp…ç=ØCaüh¸* õ `* S,8à>OMˆ™þ´¿ Pg¦:?8RÞ0€:<‚¨¡¤À=V¶’yQó–2£4KZjÎi·Ô$ÿ ö±[âZ]"âÄ>°-™}}×7{°žD@ Q(€(`0‚J—Pƒ>Ax…–S“í%D¥X˜ÀL@ Ñž$ ¨¥àV ÀtA]`!%Z’Í^`Ÿ¥ƒT@…I0”$¹Ë-:{„6w¤íÉOªÛŠ0‚x]™—`‹$°#ð”,GàYö`›´øÆH(Àl'p1JDhA¼€½ØƒáðB$¤W ûêm „[•,#ˆÌÀà±,2-ÈphL#D@ Á·Þöø¸1R÷ò©½âr²ú+`«dKe,Æ#Š,¸Â5:°ÿ-@Æ<ØÄfOïJŒHD’p‰t! ØJT.±“ž\Àà@•­<#äp´ €n â•ÜŠ`‚nëˆU*Ñ€ô@¼ëß”·2 *$Ђñr[çŠTùÊ ìîqQ^v‰?)ÔyÓûŒÅ½ñõÍ$Ž€ #hÀ ²0`–ôâ ²¬áo¿QñˆpêG¸!¤)À $xÔ^Èõ#°ë?¢÷ª øôH¼¸œxÀ†3kiXŒWû ÆH†kì/Ê9:&Ñ<¿/BNí;f“9m\qÔ"PNfg OÐe&@.ÑqŠ÷ÿ0@,¢ å&¨òIì`€âaÍ 4@Ü„¢9_Bµìvw]>C·"ªEÊI1€~¿)òî®j 1ŽøàÏ u¢y4°<À x êP/m›ÙÜ<àBJáÀß@ Nc.ÔG¸tÕ¤ƒ>}U—Z¨K5MYˆpi²(”Bp„ðU¢wÝ7¨CØðˆF(!d©€±ñG¥µ¨£!@òZ mžoÛçֵɇmâHÎRêØ·¬Ëøù˜{ו’·’ÙP%Ð<ôð@þ¡´&á6ðŽ£d WÉÊVìÐKË”E¢-°d+ÿ@ŸQ¢ŒÄ+#G›"@“ÜÑ“iŒHŠ$¼Tr—¼¢èxsG`6“°BxÓ˜ˆ¨&4+P ™OǨÿ%ÔGS!f ( ˆP–A@+x€ €ù+À c}„c”† T@€‰µF(y†ó™ß‹8ÎݽØâÉRßYÎ߇Þ=Idé’û€ û$BdH†8`²™†ˆ¶iLñ ÌX O²Êòð B£ü‹¡ YÌWõÈóáÅÍ)³Ê’ ߕ϶ár‹®–î’ÈÀZ`Z`d áÞ5ÓèYnÈÄ\GŒ0VO²ðàÿ¢  Ð_èA°ÔH âþ Î<â˜ðiáZÛÒ`Nvþ@B$,‚òØL!"Öá!"¢ñ•¯˜—6à‘¡¾ÌÈŽ0NY¬ ÀÔÝ9â2‡VaµA vxâ®ô!$ô¡¢aE–).á &â+&bŒÔ&â‹¡¼¡‹ebÒbvâ(Š € „4@  €– \LhÇ$@pÇf™,Zã5j¡,ªà.2OÖ"&6‹&r#/>‹/† hЏ€v¨£%l‡H™%0Bzô@ h€ z¨‡°Ð€6 ¤–ÌM¾E‡AêІìt,dÿE0Y~¼Í@I‡Ý d ÒË-Žc”E $ h¤7rcÝébF¾Rø äjaE"$c Ôi%iª@ô€dG¼ô@XÂ=ö@ dQ>IðT$Xôð(¤Â}ECÚÛ8å¼,åAêµ]dF>Ê^hÏöD¢HæËã, V*GÞ™cRIô@;fG;A?å$=ÅMæ¤èd ødô:A@e`‚ÈõÐä ‰tíæ -Ä-æÚ„Ði ‘E(@>¹›¨ÀL€Oz&h®ÍœÁÞBÐ)ÀT $Ñq1Qdn!½|¡YúŒÿnÜÅTÁ0À"±Qt@â8‚ùæa€!YŠ£VžD & @ l@£1vA;VÂÈäà„%˜Ç¸G"l@ ìã´€hÀ_Fç{ÎXCU EÔD™ˆƒÄ“>•Ä4UEÅÓ<ÕSJð'±5eæhé˜ES"Àj5hÜÖù°BÐ#5=ELÌF“8©ÍhEÀlJ FÚf8‚@Á§éÁ0Uª…†8Ó‘ ÀM)L ‰öKã”ä.>'|†E(nJZ ©@–„t%DAuQ€PdÜù(éIx$×ge×’~WF((@ÿìÖš™E°±¥idÌ–V„lM@ðÏÄlix­Öt¡„†h#šeŒˆÁˆX# L€HøÁHI$¡ÎV¢ÐÜŸBN )(©‘é5–Ÿ™“ªœ‰€ê’z—Ê“¦—ª\¬*o%…d„4@Æ5€ÜjE4›ž›#™¥šê…ªˆê‡¤–hä쨎,Î#(ÀÕ1‰¥>JÏ”å²ÊT§:‘–·Š+Xl\íyÈU¥”Ö^ÄMÂļ…ªÁ!œ$‚ «ÁAWÂÍÄX\€ƒ•=h{ZD… €…JYÿÀ•»ž‡Feª"kÖ&· pÁX„Y¹—ö„Áª©Xt€ #qBâ¥VªÅb*|jj(rê¸"âì­ëí5„bžJ°ÞTäÞ±ŠVT¦ëõÏè¹YDÁÁÐBtA%$홦D´Áh€ç@JD€LDAí<кêiVZ,’¸A$€0ŒEg(h¸a€ìFoDkÊî¶þ¨¶ì{¾,ƬÌò-®ðÀËž~­¿˜(%¢ ã̶êL¡4'-ÞmtNK2n3njßZn_ýíÞŽˆàn+UlpäÕÓ4®#>®`Fî:^^@Їz ‡ ô#¨€ ˜ÿ´G— ø$ƒ\nïú.ŸŒèçv®ö¨‘„î*)îXjîF.#hAX‚^ZˆÀdG˜@¤ÀìP¸‰âï’oùNΈo+%ï7.Êè‚aéæéj‡ÀcOZ‚¨göBcvØ´€%ôï:ò®ùpïrnúîm´áÌ¶Ž£pô­üÞ¥%¸Àz´,‚«“ ¨ÀDïïp kH Å Õá#ðH20Ž­o[‚,\Xp¼Å¥“A‡¤ÇA€E~…ž=B¬þämß-¯ Ë,$ì»FC.m¶pï²Î û.‰ÿÁðßÌÀ ÀøÀøÀt ̰€ ¼À €ð£z®î¡^À‚×ìš@¬® l“î² ÷BT„ˆ€B}†ŠÏV;ñ¸v±=\y|€ÝØ@”Á› Á gqñ¢²ÜâbûÖ-±X@Z}AΈÀ,€Ü0 LA{±À 4Àø€P꥽ñå%àeRÄV0‚öö€ 3¨€% r3óV¨A ð€|@E2¸~%W²·Z€t ¨œžÁåN ¨€Dõç8)Ÿ28 vq+»ˆ@€@°À$TA¼€#LÁ-ç2ÿT!Ý8Â4Á óÚðÀ ¬{ZDÀ™Q o@$Ah4ýrÅ5OÂl@Ë(±$×8‡³¸¶Ä©%4ir]€­¡Ó%XÀX‚—&h±V){m=§rQ›d¶Åð#`€l$'¬@rVÀ d@àr§Ç pA-_ áñ¬€zl”˜@yÐÁ»@ $wé$ µZû±E\sB¬Èd1ŒµÁRKë5uÄD,, ‰W”ÀLÀ_K‹ˆ6 /Q«ò7ö*‡/IüYP6 É Âo§f˵a ÊÀ^SŽÌŽ• €!Ô+ºŠ×I|@¸kÈ%e¼h#ÿc{O¿/àIöñ}áã ¶5fË!²Ï¨„¶hÃLP EQT„ͦxHðl4÷6ˆÐ3m?¶cÛ&²ä¶+`t·®·€·ww“7y%kRsqäT7d7Š>õ*•7h‡vÐw}ŸpÇw~Ÿo².6u³/XÞ¶rdw :Gv„ˆë÷­ w}3x}ãw‚“÷tû7€ó ·îè€ß³¡`ÕÄ…sèdE± 1XÀÇWœ8„ëIpÇA‹»x¸xŒŠŠ·„Oø¤V8Žxú w#ÖÅ ” ˆAaTÁ ¬@ðPˆçPDêZD¿îMAˆ€²2®GIpÏÿÌÁüA˜O…ˆbiyKÛøÿ·Œ8®€»w*cÁ  ¸Á€FÜà @ä‡ð€˜D2›À2Kôõ&÷¬#3§@_žù–‡v˜Cz¤ÂHˆÇû€÷x7ºQ"a§c±š/°±¤w{÷ø`—EÀ€EdyÿÄÀ0ã/`’õ#ƒtrÏú¦ƒHhO: Dº¤KZ¦‡w¦·¯w÷›û7¶Ú¢ óxÖ…p8T)AœÕÌ9Á&”åÇx˜5”g0|ÈGëÚ~0ûçXB›´ ¤CˆÈüûœA½78}kúº£¹²‚:¥²9’:µÿ»l¶¾ïIÿ­dÇü„9 ‚±Ë}¿ø½ÇAä»Á—°†õ{XŠz‰*Š+N§ƒ|È‹üÈ[ü‡´OÂ7Ö›üZÌû´ø€ùÄû€Ì Z´KÉ_ü½d¼ÆwcHJ{² ‡½\ÌÐ}ÑýÑkÛÍýÒ<Î[„/À¬dGa¡…°—9¼Ç»›S1¡´Ó—/Æï¼Êú<ÀxhJèjF6=اÊ/¹Št±Ï—Â;˜Û}¼Ÿ‹¹\È׃ýïŠýØëøÆ_w\ }Ú«ïÚã]b|ã;þãC¾®8˜(Œ±‹Œ P°ê‚°Ë¼@œ´ €Üüß“oà ¾Xn|÷<[\‹êŸe°}ÿZR ÓÓ~íÛþíã~íG÷—„x€$”€Í‚%™„À¼ÌCZPº…x=釽Ο>ÙsüŸd‹ë8ì/~ Z?ï>"X‚`‡šˆ÷WH!l>ÃÛ=Ës½¸ù½ó[®³Ox©zÅ`Ëö ö+IÛ·\ôOàäcIÄ“2BdÉR!B„þü` B†²˜‘ŒE—8vôødH‘#I–4yeJ•+Y¶tù¦ÉŽ=²ygN;yöôùhП5…5ziR¢4pê”hR©7›vœ9ÕfÕ˜W±vÝU*ذb¢”h‡¥Tøt"B@!J¬h§ÿED7–õûp`Áƒ –IÓkbÅ‹7v¬´éS±yj½Äuªe’OEbêˆ $b8rÂĈF[˜¸àÆ&ëÐ[€Zñ!tÊVzL6ªÙðÙ½… µPd ¡–RÈ|ŽÃ¹ ‰[4¨ÑpvíÛ¹w÷~ræ$ÊãÉS-žü$¦’Ñë´ìYéS²#åÏ÷¿(„šH`Ô¤ÉE…G|¢¦-TpD=ÂxD+LPA%*ˆ¡.°á+BhŽ LŽ c¨â‡/@xãAP¨à¶ÌìËî¬,É" B²`Ž KZ@h‡B’N¢‰&²Ž¯ïŒ<É$•Ü ±öœLÌ·'¥ÿ,j)š šò‘÷¢¤Ò¾úœ²ÊËû¶ü ›V¸"«I°x…öPo 6Qpƒ ¹`†^8†´¡E!Tð®pƦ¨©Š P(TH˜Á›< 8—8I8Dœš¨Ü HáÇ…ì¢ÈºëúZÖXeu«K±¼×\³rÌñ´ì*Óû¼¬#ü„*Óþ,}‹ÀjÖ?G2 Bˆ”`ôG„ ÔR7|Pá…Cmè ŠG&©b„ú€‰š®hÑE¿Âü¨S§j n CØZþXHUV‰Äî¤Dè%.3)ަâˆ%³W]q­Øb,ÁRã]»ÿ츧`A¶>s*3K@8aGšÍi BCS‰ÙcPB7!ŽÀó%V@áˆQñƒn ±C1rF‹ üÌ’ãß^|‰dû„+‡j°(_K&¨„¹ 2`¼¬[„`“zØ@”Èjä‡éžï¼— /ã¾s2ÙoôÄ\±_±ù#b'Ü= üËòï¨rÈ_ ʬ5ß|ØK:Í˺|[@D ˜s m€«³®í’ 5.±D>˜@‹Úoïba²´h‚èø>¨ä…&¸€¬(hà‚Dh·½Þõ¾{yœûî}ÅÉÒíDzJü¬®nÉØ£ÄÿCo}¤ü«z¨óÑï¼£³(d/ƒ h/DˆãÁgØB 1$‹°íU$Q.aÌ.T¸D" -\˜YØavŒ`G\„…]ÆHÄ%x°æðN ÈÞ i¸’™”Ï{‹Áayè¾K~6Äệ8•ȇSžr þ±?ÄSÎYH$R@+Vdm­‰ LèÁI0Çi@&Á´ƒQø€G€0 ´æ„P#G!€ †q¨áùØ[õPc€¼X娿ò.^µ– ÁGµ?R%ˆ‚±Ÿ|ðÇ?O¡9`#àD*b@C$B‹!™„ `ÿ Ì)ã%ƸÊ ñ2säˆPÆ$ˆ ޲¬#í8’Ç>þò—|cä0‰Y¸‹ýðp‘LIú~SÌ 0gÙŸ|œhåÔ`HT´¢Eî’ÅŠ$ è.à#\¢ @ÐI·\ƃ ´D60» ”àvD¡ /ñ6æôRÀ( oèÌ'Ñ ˜r!=F> –…s•èD)ZQ‹*3šù£¦% Q„΀X4è\e’ D¡#y´ƒ\¼Üµt¼ãà%~÷¼6l@Á.áD@aQPô¤÷0_©×+hBÛƒP¦ å¢Q•*E“ZU—È@#ýsŠ ‚ˆ!5ÿ1“­kIYÂKŽ˜ÕªiU+}ùT·>•)q•ë\éZW»Æ£kÕ«$-Õþíå-[íßþ<9VC °%hýç^«Va¾õ{=$CÏÍÆ^ö%g±Û°ºOÉ@9OÑ_X k¿~³©Uíâ${ÈÖ’Ç©\ZílaÒ)H,XÅê§š³Õ‚´*¬{â" ÚWµK}ír>Áå¹ÑåAîg„,‚mÓ­§xØÃbõ“‹8,j¥[^¤*—¹:L¯d-kÞã>ñS#-tž¨Ý‚üV¼{.v]ò–$ P€\ ˆ‚îU°Ü´ÞÆÄÖÁ•s®¯ »`ÕÚ-ÿãŸ_§¹?ïrÖÐ˱:|pvwIOÂâ|€w´ðŒýáß8¡í¥ñe5” @ævý ØïÊ žM2$qÛ¯¤ pñ%º¼3NohØ%ì -X¹ XF)G„ \BÆ;Vóe¦Úf7¿Îq–óœé<ç5KW³pŠ[sÿrvAòŸùëYìW%b3€Œ0‰D4`„tPs$h)_"*€ô%$Mé`ÐBšï\jSŸÕåEˆv4‹² D#‘X„¬‰¼_Bó—ÉÄmIŒðÆ(xP#–à |Á‚–7aÛ#PÿDjYáT_ÛÙÖ¶¬&a z¤ FPØ s–´HùtÁ®¬#ÁnÿÞöÏ7nåýÚ½Í#‹½DJð… Ð3ßçÀ /koá WøÂc’„$Ô“pAbp!€.Ь²¶ý#ûì)Kâ aŽCâ…Ô´ˆN‰d‡N´áæ)Fk?ÊÎa~X´W؆œáIWúÒn‰ XBmèÁ© „.¡¼ëÁÖh½ç¯€yK\,¡á–Ï iÛV¨;\`À @i• èû"€$¨<»ã#hûÎtÃñjþ² ÿD ‚©s$ÜWïx0ƒsê_Wâ×B%ƒ²•îT©JÚ/ÊÄŸõ©?<ý^²Á¯„‰Ð 0ž4háœIÈ|f={§à@ì Ý—%ú€Ù…“D*’ê™ß|秺>dCˆô7Cá„Ý"?ÈØ^MÅÞ…¤‘ùóÉ_~óÓö)Òwõú×Oý/a/«ùkb5Gg±µÅû£ÈêÈz~ÿÿÏ«-Ø pýÞbÐ~ðŠœh8 áB9‚„U@Å›3PñfÏ Ð=iåK&é~4ê‰ ›À¦ŠôO¤¾«í6cP·£?Ð 0ýbÕ`·(é³ÿ 2)T6銀Ëôf “P —é›EÂ4Î@($ $¬PS8l6ê¯)›:ÉŠì`¤ô±–Ð Ï ïCýœ Ù"$€àœ8"ã $ã&÷Ú@ì@\àA.À„Žèýò¬D‹£<‰´äK¬øÂÒQ—£ #ðz)>À!ëXH¶ò.À,¡L R âPn®†,âO‰¸ªç/¬Äа2ÑkÑý%‘ •) b`ò.Án#´À>ÇìêÀ#OÈs:°à«9ëcQ#hâ¯UÏ)tÑ —‘¦T@6€æPAJ(Ü ÿáÚ@ãT@ 6aè#’^„³²*°¶+ŠJË":+¬1ÿ •n¹ñ½‘06%%M·¨«>”#õ¼ Í! +Ò"µ ÿr‘ ÙoÏ % ’% ñSŒ¬»®ã »üñ"W’%Õ,åãëŒd”Ñ#I¢¾6 %CÌ´þª‰R²%(L#ݯ>pT¢ÏÕ¨od᮪bkÒ$2â}«»° #úG¼°+(·’+ÑÏ×pXÔo 9rdÆÄ)G$ýÂ¿ŠŒ»8+É<«¿ú‹¸J¬+ëÒ.õJ#mP,w±,}"2*U‚Ð:KÐÞ²ÐüK.ïR1s òr#ŸµB&ÓÿR0"Ü ÄnM. 1;Ó3gÈ1“χ™²„ƒó3SS5…R4m4Í’2WS6g“ÆB³5]í5ý!i“7{ÓÂló6'±4µ"6}Ó8/ƒ2ÅD7‘Ó9Ÿ3¹”³sS2¡Ó:¯Ó±"Q&Y;»Ó;÷8õ2)ÇsúÜð;Ï=×*<[5ÓÓ=ßsIè L` i)P@ H.ÀÖ¯ÛRiý¶Æm4áÓ@{Œ Õà‚A-ÁŒ`ýŒ€ž,!âT€â4À„€9.>€ïTDG4V6ÈA?]€Èjiýž.ê¦!®îê²c ¸€D{ÿÔGd9¢ ÔA?’ÀÕªŒñO fÔ$¯ @±ó~ÔJ¯”0´àT6  캀H'à‹,ÁõB%ã6 8ôZàö€€JwKç”NÑG: ´NóTO™ðNpOÿPCb=E³=ÕPÓsP³P•Q»S#õsú“=•RçôQôI;ŽéPâz€öШ໠ F7+UôQ!A zÀ&Ïx`"GqÃ, \ ÌN5UUD/ÕÕp£4›Ôìv„YtUZÏSXÔ \ Ï![/ÎT` ¢ \•uQ§R];3Q7ò\ÓÕ]ír] ²]ß•^·2^%q^ëU_[ò^µs_ÿõ8ûµ–`}s Ù3_ Vaò)äµ8bsX¦s^"Öb“;/Vc7–á!ÿ STARDIV 5.0 …N*2;opensips-2.2.2/modules/seas/doc/images/image058.gif000066400000000000000000000505471300170765700220210ustar00rootroot00000000000000GIF89a^ðÿÿÿ!ùˆ,^‡  ###&')+++-/0.0133346878:<==<>@>@BHF>CCDDFIFHKKKLLMPNPSPMFWWMSSTSUXVX[__U[[\[]a^`dff\bcdadhfhljkljlpmptpndxuirsssv{ux}}p{{{Fw¿y|C¡rR©~~†ƒˆb±‰r¸•‡„xŽŽ‚‚‚‚…‹†‰ŽŒŒŒ‰’•˜–‡“““‘•›•˜ž›››–𠙣œ ¦Ÿ£© Ž§¥“¯¬›£££¢¥¬¤¨®«««¦«±©®´¬°·®²¹¶´¢¾»ª´´´±µ¼³¸¾»»»µºÁ¹½ÅÀ¡‘È­»ÀǼÁÈÈűÍÔµÏÖ¸Ð×ºÒØ¼ÃÃÃÁÅÍÃÈÏÊÊÊÅÊÒÉÎÖÍÒÚÕÛÀØÝÅÚßÈÓÓÓÐÖÞÜÜÜÔÙâØÝæÙßèÝâÍßäÐÝãëáåÓäèÖæéÙéìÝãããàæîëîàìììâçðäêóèî÷îðâòðäôóèóóóþþþüûÿÿÿÿ H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€ L¸°áÈ+^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУKŸN½zÍCسkßν»÷ïàËÿO¾|xëèÓ4Ͼ½û÷ðß«Ÿ¯Þ;‘ ¬À>`@ÿØ ßVp7@Ù­@ñ™ç~6Ø }V×ÝÄò±Z!wy€†rß+pH’Kr˜:úxˆ&XÀ $0AˆJ&ð˜LN¥C²é¦‡hZ@Ä“QÒ(hsÞ=PÀAdyæ<ÀŸ†L @" ü1Á dñG¥‡¼qÀ`g¨¢òv(ô0§¾p¥§zÿ ¤†DHjHOÂÇö`Á®:ÆÁ©§¤2j¨ÆÆÁë!¾ö˜k±† *mrߡĞù㘠À‰ÛñªÃ â@‘>0Á¼Q€ OÇ»ñ2û¶#0¬§Lpï(ºh™bvúb÷‡ #<€ºìº ¯¼ôÒð·b7ïÄÓv\œxpÈcvÚKâ|@Ä!™2aÁv…<1‚1Ï\FYñ`ÇïËÝ•¬ÝÁDj 0ÃÈ tv5Ó,s8"vMâñÕÀu¸h Ç™s|ø£L„»­¹eìÊ5YBfÛ mÁʶújÈîxv»ÿ¨Ã¯ÈáókÃÜzø7ߺÂ-Öï¦5À@ ÿÀ§Že ð™§fwä—J`Bm&Ѓ©÷pˆ\ðÁZ¼‚`š®÷‡Ù5o¾˜úœ¥ŸÞ:ëmºÎDŠÁOÍ:ôùô¹Åh}xŠöÂõÜoGý÷µu/~‚J?>÷à§Ûùì·¾úð³æþüôËÿý¨Õ¯ÿþçáï?iåI™²ó‚íÜ'?û9DÉD þuojààÿ&šòüÍKÒ†t$&¹F¼ž˜ÀC4«Qð„œ)O»F÷‚¨ ;7"‘†Èd°³ àmBÃÌð:5 PRâ!ÿ¬„%-5ªK¤ ÓŽÒľ(NÄSbtPÀCÄáVBV QÈÅ̧ 8J`°í QŠZ ­$5´\ao8Äp€€X} YÇ*UèRµ*»ÁªS4X®þè)`ilX^ À¼°ó‚„n®Ûb'IòŒÀu}@–J˜ka«C+ØÐ°FìXÀ$¯Õ«÷J@¾öu€~ý+`Ù!uàOÙI׺v°0¨q€å0$IRò™Ûþc€”“:‘‹„¶·mÈQZšÀÒö´C4íf9ÛYÏé²îxSiÚi°²Å*›Ø‰·¾›@7Ù{±³Ý·À“òï‚ËhàO¸ÂÎð†;üá·K"NñÙL¼âoÍÄ/žñŽŸfãùhpq’‹üärÿ!¹ÊWÎò–»üå07ÌgNóš£üæB1ùVtŽóžë„ã\ºÏ‡>¡kÅèDO:K~t¥;½%LÏJÔŸNõª[ýêXϺַÎõ®{ýë`»Øo³ò¯ÔüìhO{ÚÇŽ[•Må;ïC æN÷ºÛýîxÏ»Þ÷Î÷©³=­n'¡ß€@âðˆO¼âÏøÆ;þñÄàÿÎO’›‡çT)<ä7ÏùÎ{^ò”ç«å݃ù¨hÞó¨O=êAz¹â»=“?ÊéUOûÚ/žõ­GëëÙ{£ÌÞöÀ§=îsßÏÝÃ~*¿¾ò;?|âCÓø¼G¾á—O}Î7ßù”„þå¥_ýîG¾÷Ø¿ßÿèãSzÂOßûèOüõÃÏÅñçüMI~íÏŸ~ůŸý'ì°ö¿cyðEþ‚0  Œ‡CxQ°‡§ yPÀóçø>%xã1~¸hxr€%Pð-0;P‚1°%;R°N1€#ð‚`P`%ÀMÀ[Ði0(ÐFÐ&ôgXÓƒo+Ç,·Møx(° -†-  ,Q/hP‚1 ;ð%ÀJ,0%1ð,%PZ°„Lè„“}-GBÜ×xP,Ð…‡ÿb˜‚( P€††'5‰>à >@‡‚`‡ €sÀE`x`x1ðkP†*)”CIØ” Fe ÇF“ D…P ÿ(>  5—Œ7(<à‚™Ó§¬xx‰IpP‰†wpŽp>‰—y©øö&ð®S `#°H)……ù €x,€[HS°!€wP™ ð‡i–ó¼©XÇY™m(<€ö8ˆ©?)¨Iµ( ÿ  0Š€ŠEЙ5𕙇ǜ”XžÀ‡·ˆ×5pxBð€Œ·’Û‰5Ýß9á Ikpx0¹ˆƒ‰Çœàwà•!Q p¡° ‚Pp øyxkçXipš ?Šˆ>q †ù“ó8£Ù©~ÁWšpžw¹¢êÓ¢üw›A•Ûx-Ð,`—Ô·kj’Ê£Ñ"À!üÅmÛa8ÙO0Š›2*ˆõ˜~'  P*-¸¢†P´c(ól` L Stƒ\ù28Ð¥ ñ¥A*£@y·Ç§gÊ ÓU$à(:ÿÀ 9d˜:0:`:0@„ê~ÚTI£j}›Z¨Ê0~ 3Ô¨‡°:0kV0ðt:nA0ªÑ© ñ©¡ª’ƒJªÔ3Ea$À!|§Zú%Ap¬hðl&@· ºº¼ ¨ùޏ ¬¥Árá£CÓª¼½”ÚJQøvp‡Þ’à:Ÿj­×ªçêH ðg˜î‡¯C® ¯åªˆY¯½±¯å!”骮ùÚþм°›g¦ûíš“ÑrJѰ¡v0бj7±ºQ±O¹‹'+²†Ê"Ùj)k/«²ÉA²›1ÿ ž2›4[²6[7›³Åa°¼×²Eñ³"a´@+B{yDKHÛ§I[ö~îÚ³4ñ´Qk¯eDzU‹XË©Y;¸´R8zMÛ¯>¶Ò…÷Ú# ˳^›¶j †ø¶¶©_›«sK·,k²r»·Í±³]{yË… ¸´!¸g+‡« ù¸¹’Û¸g ²–{¹1—yÚa‘.•‘ Кù¹ߪÜš±bA¹PrÇw®ûº°»‹»œÄ¹‡0…P›3‰”7Yºa·§ë´©‹¸2'¯«ˆ³«µ›_‹Y›| F)€N9¸¸I¶T¸°:¡ºÂK¼Si¼Çû«šÿ›|¹h‡ð—Y›„‰ íн³8¼ÜÛ“ßk{‹Øô)àšåû¹V¾ƒé»A*!ÉKÚ‹¶ï+¿ÿÀ(¡¸ ÀÒê¾’Y+ ý‘9&$@!ÁDé½< | Œ›úø¢ „% b$'œÂrÀ<°áÛ à'¸Ô›̸“C‘!±Be€>L#2<Ã\!ü¿}KÂa±Ãrqö80aFˆ0F(<Ōī÷Á&!¥Tê:A€¥Ú‘¬\jk»º³ì{µ%ž„ ˆ°ÂqIˆIdžGŒÄó+{O ¦#ЦoÚšrJ§vxÿÊ HÕ À`üPüÀ–q&3EÇ1MPML@g2Äz H€jp&@–%0bù½ì{ˆšŠP1à¨ú"“z•z©:°§†)e\É1É@Ú[#]£s<ÁÈ1Ps0SˆÐ5|Ãéêx(`™;à˜P€‡@D(Dh„HQÐ%k€&ÀpP&ðG0ÎrP¦À<##ð2¸Ì ® «²J«Ü¦¥7¼µ°·;!ÌNq°m*ìmš3œÉ„3P9ʇ…Z(SPW %@rðˆê‰J81}ø‡oi((ÿÀ ÀSEèÒ!æªÄ=F c¬p¿Éš˺ÍÊ”DàcÛ8Ì[ÁÐ,)¤é‰'–ô‰(°‚@, ç¨Š¬˜;€10ˆšÍ‚P„§(ªVA³N]¶;…þ»ÐoL¼æˆ‹% ‹ii™‰‰+àÕ ÎhØ8E€B  Ðð€‡m?­Äë+ž‚g·é˰_±NGÕ!‘›éø&yŽõ8w3ŠŠ,P °Ú™×Ö«®»®¶8 Ú ™t´Rù¨’>9R×¶Á{ÛkÀºÍ¤­Ã<9t¿Ô ÜÏ}±ÜÁ­ ɽÆ3ÿç’«‘º&q”“Û0qÝ8݈Á0Çñq&’A‹db ÔÍßbÜ>q˜¬lÜd¹Ì½¹“ç»>°¥9lì}.Ž~ðÍ!õ­á ±Ç|l !>â"^âlPá?7ÜÂ'HP†7tà•Ï™^I°ÊÌwϹ/ç©Ý]”Ø“Ϋ” À”Ó;y^I~µ(.[Ò%þ8^h¢^Y€Dd,`]Ê•C  pUîD}²Ñ}A‹l@$>âhNl~æMn2ÜŒ$À%€ƒ'$p PÐ Úâ‡z†×?€¬ÿ¸ÍôgÏKÇã˸ÛÄÖ—ü»à\ÚK>™>›Ž»òˆå¸²Q·òˆb¦"©âˆ,(üÖ„Ê+ˆ +¸fμÞë¾þë¾þæE§âˆÇ>>@}@ƒ<]¤ˆ×â/ãðÒÃYÒ'ýy±çènzÜa¿øû±¹¿ã{·;n/.ߊT1|#LÔøâêê.ïŽb0†Ñ²îáwA‹mðïnP_vPðv€z°ð ¿î[¦‰‡Ó¡ˆè1 (‡'j)¢ Ÿ1pè„þŸß‡v)×Ü ÑéïãÚþò![LÿðëîêU“B ÑB4?qõŽÂ×ež¬ã[m€ônð_ð ¯ ¯ð ï$W‹-ß½ž×Œ=B †]Ò?Àñ¤ °Ÿ#½•„.Ÿ©·ÝoÃHŽîVغ±;÷t?÷Fó-=Àîlƒn#ëAÍð€Âsl|8~¿0ÍQ3¥h®´(ðL_KßôŸð Ÿ Or ÿÛÂuï«Þ¦¾népàÙÀl_Lð0ìNnæ/=Y``sCó=…„˜0À!Š-^ÄxQ`C=FìøQäȃ B™RåJ–-]¾„ 3ÐF’5mÞ´yˆ&Nž=}þtc¢Eâ©“4©£yÀ£†ªU­Š!#FH MÆ<SìX–a[ÎìšVmÚ"ÐÚãP&­ÉHq§B®k9úýù•ì`Â…Q¢œXñbÆà˜GòäÉH•ֱÔèÓ:x¶’aZ4Ñ¡Mou\P°Lí˺DœZöÿâOt*ñá…?|6˜ø`e‚&AÐ|ˆcbÅ&pìMØw¶ôÙW»ÆŽ=vuîݽÇ ÃŸCzÌŸ?93SΞa°_þüù¨S_ÏžŸðvðŸþï)A&È‚ þxà²8$‡t8Ä TPpLÐBè¢N¡ÿ>1DG ODñC ðÓ¯E°6ì/FgäN¼=ö èFoT/³Ü°c«øÜp£G£ôˆê¼ýè0_sq?Ø`¤Ñ§5R‘¤‰&Á',TÁtÀËŠ8øƒ8ŠîÒ)(ˆè#9礳N;ïÄ3O=÷¤RK³V á A, Aÿ†øs0³¢Ø¦¾Xƒ…DSâ¯Êê(à€¬°´SÅÄóCGŠÆ#U½§Üð¬ UÝh£GÍ¢J½=žjÒ¯'¡Œ’¬I©ÔS›þË #E $€·|ŽÍ,‚ð ßL "ˆ> ªµ¤]så–×k‰Ý¶‰ú¡ƒ@*C¥5P0…(:0!Š?¡x …/F@¡#vX·]´¤„/îÀ¥îصW_€D¬à‚$^¸b›Ä àÆó(ÄAìÀìÔ:¶"²ä"‹L WaE難²UëÖn£TØb¯‚ŹÍ&ʹço‚Yµmgv±f?Ϻ‚(†AV’ÿà¡-…¢Abø€…bØ!ê©×Ø@Ž‚§ˆ%˜ÒæÔˆø8D,`‚‰˜Àa+Øû,g C6`€O|·«„aÖòØ#óò Hå§ZÝJ=Ìê@ùó#cuYñ‚îJf•†&ú°³~~!ž{Îö’f§Ý¢§ B} Õ[ÏÎè‘dž9Œ¡…?c˜b@9ÒØÁ„• à!Š8IŠ˜w… ¶ĉxÑ×cgl€¸‰àCjC„îÁùã>$Žþ@äžØÀ@ @¢OF6úOT$C©së`fBÇ”ÎIz€̳¤ÿÞ†u«ÞK&%<w¹Óˆ–ž‚ÂÝäM akH¸³m‚ >•`-@€JP!ð*%Ô„,0Äì@ ?DÒ0^­()ÁÚVBÀ `@ä6D0!€ˆøƒÌH& àxÀÞP<Áq £¹™‡"…¨ ~9]°(¼ŒÊŽT:óøÁt´ cX˜îñ„(¬ˆí<,, Ë#üÝ$)©Ië°.,§áILé:#žd ”Áf®-îQ-ê›ØF˜ ¤qa‚ Rˆ/%àÄ1mÙŒíB„PAT€‡*Øÿ!#;D!õ°@•L) S¤S®+ÓíI÷IQ:Õ¹Nu.s!—Äd&¢›€R’¢Ô)ʔܡ-` \âÏ€ Ô0kXÖ B}ºó! Ëå@âQØrÛP¹¡†@„!àR‘‚Ô¡²‰œN@ŠHà?u¸¦x²y9nZ&‘칦Ñc:= ’'êMàOÍÈžø,ZCÂÏSb„”•¢*#LiŠSˆ@u Jº¤ @@ß¶·p0AÈÚ+¬5¨©‘©J0YÇ5¯Å m;È5¯Z ëp` ÈW×H0D€¡$ÁZ«A‚M‚aOVÐýÁ¥jtbÒ<€Fô4õbÄC…=ð;Tø·Nÿ¬$}è(­… Q,ÝbÁ8ß¾ä•t'qyÒÈÕ“ÅB TRház'Ù6JšðAt`-0ßJŠÐ‚+”@•X^ Hm.kK„„‚}Àv·@ ‚ äË‚Áˆi¨¯¦°ÿ€æ»g´»è0Õ¼/ˆ@Ã`†ŽrD×UïÃb#õuᚎT0}vƒ5;ÔÌÅÒÛ„-©Åiô~=X5OËÅW²ßHA4¸̰Ä`( Á0ƒ0€âá„”€_H6|¥0…*Âw ùмÌxHÙCx< Êœæ5t€8æIPÿ„ ˆ‚X:àú!è>Àî. à}²U‚,w€{îYM ›TÓΓ{¯çx Ž‡ÓžƒžÁî4ðÒq*òUw·Ü9ˆý£—…Œåtne–9½FNfe+¡jDý¿Hõ¬á?¨B%Uâ?D>á:‹ä[˜®úŽU«±°c?‘ »=è‚NÛ&¨H)#<ˆ¦*È+óS$<@¿¼s¸œ-÷K/ø;ˆ­°C[0ÇR@‚ÁüºÔ`5êCÁ4A ô˜ € ä>Êq ðˤ§€CHÒ: …,õÓ»¤®ô4dA!©Ê?J=ÌÂÜ¿ÕQ¾¬±Å‚ÂO2­=°ÿJÛA"Â"|Š$Ô´%dÂj¸R;CÂ'芘€?(ƒŽ8µ2 €Ý™§ë# +„ÄH4‘ÃÃCß¡D| ®K<µ2Ì N”± ´﫜Y?Ls€‰Ã®•±;$§<ÜCÚêÃ?<„ („20D!\<„À‹ŠS!Ô‹,é‰HÄÂ/°-D>” NüLt ³©ŠE)|¿X Š,»î« ¼À8&´´rd¸õÃFΚEŠ AàÅC\2`€@?`DbFœqÄG¼BLl-• /tÆ]‚ гÀ9"³ˆ‚wÈu‘šÿ5ã8$ |4àC`(3 Hƒ 8¥Á?KÄFkÔÁt\ˆ äFs1 ô%Ø% %À%Љ:4Gs¼ ÔC–´ÜÉ‚\<–2 ]|Çòˆ|¼­z’¿ú (è€8#‹(€”«(p€1+d¤ÆHj‰¬7y¡`órø—@›hæÉ—[ vK"©I$Hè€oC½Ü¬-vBLbä §,CÊ*ôƒ§ œîs±ÓòÆJ3;£€ rtÅtÂ|L[:¡HøÃA4xCXÊ{|OÔ‹ÿËŽ8ÓµœÓFÉAhÿ‚”ÍÕA"ŸµL9¤s9è9²o›Ì¢."¨耫q—fì” î •ÌÇLÄ$ÏòÜÇG”ÉܔБ YCÊÏ÷ÊÐ *ðüĦ„Í*©d ¤c€vC"Ý”(±$KãZLƒ”²(N4Û½Þ‚"Ð'¢/¨p(`<-B E€"ð@ð!°½ ]FoÙNÙ) µÛO K 8Ù“µÑÅåC ©LËô Ó'ˆ8Áù\ QMû,!ü4CëØÏØ4Ëì(‚ ˆ=“‚®„€¨€/ €´ ž‚DFá.ábÿ,Ì“Ä?ÃT ï4! aôDžE›«Dÿã ŒÁ®Ñ‘hÏ‚ÂʹÀ"5RËI¿MR%E&–›yÒK|F°h%D $RƨRPœà§0ÌBKÝ­; 9m>RåÔ‰‹§TU¡©D©ÔQ48ÉÁ @­œ!-Å!,1¼CT¼ûÌ dÔ*qT¦tÒHõÂ<¥àL@dÍ?7]*ñô S=4iýa¡8UtÊUm¾‚¯¢Ã¯@p¯˜€ã LÈßÜÔG N‹ÕY‘ ÓÚ¦X!T¨xÏ^Õ×ú ÖeV×ܧÆc… ¤‚!gí2fÕ•”Õi…S¯€Vÿ÷‘ð\̉8‰"^!W€²„w‰—ÙS3¬¤ž-è€4²°Ü‚Å\AíƒU T@Õ®m¬L]Ý©žå×~µ$·€‹?¤ »øEay´†`ÌT•ThœWUÖeUX”d‹iJˆ¥Ög•Y}TNuØm… @‰Át+P6H—·4«iy‰ ¸Íƒt9jûš—-K­Å7l•!1y%» r1!4EŸ%\XZK² CÝà f Žá(ŽãHŽåhŽˆ'ýWÒeV„ÅӜڜ ó$]Û‰ØR½«•ÕåZbÅÛòÌX”­™²Hèi—Ÿë6ÿ ˜ÀÞd××)Ý÷ •?Õ´›ÌõìÆ#Ü_=\K9#Œq‘ Ñ Áç€TGµS‚UX¨õBFù²|ùv9SAÌŒ,ºªl·uñÈ“0›#3Ù&øJ#”2#h‚D=–hXÕ}¬mÑÕ%Ï'ÅVQZÖ­¡8 à€@ ½?¡P‚Ùë²<êY øÆ›‚ ˆ˜µ–´l’ ñèí‹ÌöœUçeÏèíYÃÞ%å’?ø’“C“29“4©‹!%T[ÚÚA5óÕÔI2%1Ùíéš0¾Ô‚è1[·¨Í”˜KÔ 9ÿA¹`­‘à8à”Hc¸6¤Úñ”Ùn`ÕÈÚ=&ß``ü•¼õÇ‚@e´ÔS"@ú;ؘ%d‚)ïÛŠB؃BœÌÿhÞíãáóp¤Ù ?øa. HÅ4Z– (Mg–  –&¶Öl½Vþ¬‰N ]Ï5¡°`à= æ  ƒ `¨SN”àž x²¬D 80¨@€‚ðžró¹­a×s‚Äã_aà8ݧæ{äÖØ[ÆØó}7­mÉñȑӲÂÈÜŠôÔd¾m$žr¤|æ©G‚ÓåQ^ÒÆÜ}ô»üÜ\C6XíLß°a¼"€"8‚ÿ¸90³á 6x2³9Ø_@aNƒH n×ko¾·pæãqö‰ d(µ•Uâ\§,MÃC YRëçȱYQÞ{Æç}fÈRùg€æÃhc=è\¾Û ´ár7¬!á¬AÐ ˆ¥CŽj9]ßéã–Þ§˜¶e)žÁ„ÁéŒÙ«ò@jÌÐ.—‘Yj}Îç~v$Nj¥nÔ'î$bথÔÐUQ´@›öT×Ðå˜aiÔ]ª×Œim-dÎeÓÔÅN»É1<81kÒ®˜„ YÔºÆç­¨ëñàkѬ>¹‚ÖbQÃFÖ¡Yl°MlÂf°ÝcÇ&ëÿw‚ì@–ìcŒíu¾˜PI Ëa )êî[y%æg½vnÔNmwzä[¾?DA$Ä0DDTÄs>ÌâoÄŒZ˶íÂÖÆk¥ííÀžÑñ†o)ÆwÍ–*WÜ 7 PICÔ|Þoþöƒ£Fjéžnï›–1}òkëÛúéš@š:»Ô‡ ¹hbÁ0È+ºƒ ØŸŒõ´ýÛŸ®Ú¸ÜØÞøÇ%ã@å`çHþ‹ þÚžb.¤l(o¿áYíðlítWnîƒÉïËrãr;;yæÑ£'Ïž„~` jèð!Ĉ'R¬hñ"ÆŒ7rìèñ#HŠMÈÂÄÄŸ‡td9”#†ŽCV,¨X9B‡ 1„ìÙ‘¡Ï d4ÄAjâQ¤¨ -E¢ì0jõ*Ö«R¡ ‚R5kQªY¯â)ô,Ú‹f{hÛö\¸nݦ­k÷nE~îéÒÏÁƒðÿÀØ#7À¡xT¨‚'@@•/  YÉ–»‘Æ{ìSa|%:òž!§Ðgÿ¤Újk†¶ ˜·œ+îÇ5یֹé¢ÛñŽìÊ©i™kÎg•|kÚ;YCôXlÞìÏOK× |ÉÌÙ ú‘ÄIú`‡dK™Ëeô­àú<5ÛmCä–ΕFãÚpÍ4Þyë½7Xs·í·ÛlëÕB"ó5mBRXÙ`,— óÙ Ñ8啃·Ütm\w›wÍ7è¡3í´å–~:D…ÍU8_êrâˆ^ˆ ez¼ìr`ša89ê½?­à—.{·Ò¢|™Áÿî;êz ¸ºÈZ'¤G[3Th{ãDP̆Ë9óá‹ï!ø8üÞŸO:óÊìàÙÎuøÿë­ÀÅ`J 9ÿÚ[82ïÞ'Àò§|"žú²ó•ã‰gaècŸïÜG@9…L?úKê7½Ü Z{xLË,˜Ìè)m&„A&¨Â†Æ€:Ÿ| `(@% "pÄàFYÃ{\àì¡lH5Hp{'AÒm/±`B2;=Ì`2(ˆþ3ÂܦBy0áBú*²±=qa‡Î‡  ^d—N€ M³j—’Ô•ñP©  B€¾EuSt#‡t¸ ¶E@ÜL`H&Ƴq²“à:£ÒH’²”yžúжªëŒÊ,ÿàA X%> ‚Q˜ nå'ÂTPÐ%&ºFžî‘¦,`(£—AK:oz‰Âš€bæINÄ Da ÔxÌmšŽ’#WŠ€‚ Á^®”˜,óC‹qÀ Uñšà@¼s# hÌrí“›¾€èDÉú-Óp‰Ûä4;iÍPŠ2›kô'DYèÍ É1i`AŸECGLËõ3¢¤ÑË š!-n&¬3\êÉ3^3>ñI¡Gc*À‰š+ Ä(N‰ÖÑáÉtYDHCªE?@OdÔ“æ4MxM½¨Ñ¥=}*óhÚŸŠÞ4§Ýhåv ÕÏ DhIç‚¡,Ä„à:kÿÚÊú:€ò¥£Ü*\+'UþT”‘VÕ¨V•׸Þe%¨[SI²ª­j][ꇗj“¯ŽÚ\í£©+$ @A ´0½9P£ë¥H@4h¯ãÛëcëÊÂPÏ‚m¡Þa {BP‚òM…ijsû±ÈÖg²&(J ~@^G;Üù NÀ‚ç´Àb1¨á ×€ž"@€ë™Â Q4p;0CŸØ·ÓêÖQÍl-%_›R³²÷¯ ½ƒBúÐòÒ×§¨´Ê0ÛË ‰ŽvD–Ë‚4aÏu• jÐD’V™ÂúEQM À@¤"›†ÚvÕ—QˆEiA3ÿÈЙ© 3 åXûÖ³˜Q¼õMq…VÍJ¸;Ä*§«L!0ÂüH*\q`Eé,v¥¤°PA€WHÃjà6Üâ04 “„-WÐ…&3C~hì”Ãܦ»‰+”-Á0Kq’S9ƒZ€€/èë'°U |P„‡E *I6ŠÆC°Ñ>,Ÿi AÆÄ'e1w$¤ZV¬BDŒâ…°—¡ d(n­éHÞ7iEùt™D®ÈǦxëlÞ ì¶Eo:#¡,D•·LÔ{U±¡„¯­ýÐê]û‡Ì¤q×]ƒýdVwŽ×ªkB Ö³Fl®'íeŻڻñõhê*ìÿ`«úoÖæPRg{5µŽ6ºCùíusµÓAÛ6¼»Í6b³Û!…ê½a­ìp»7ÝþÆð.ð¼à×›ærL„? Û¢Ñ6¼s*o©Ñ»Þã @S]ªñs¼ãÿ8ÈC>ñŽ/sš# ~LÞ9<4ž‹¸°G~³Š[\Y‡ù󊹸¥låŠ~ ϱE®–ƒææ8_ÍãXs6ýèÃÜ)Ѓ¾³¤›+gRï¹}mþ¹ª:=}T§èÒ©Øô®3²£V¿:¥¾ã³£ëæu7Ù­*s›©=ìvãzÜM{¶·}AÅì»Îê¾¢†y7ÞÜk&x»›ï‡÷úÞxŽJÿ~ò"<^®ùÍs¾àá[üSG„Dhˆé="‡, þôvûã‡ùʧò´¯ýåqÝSÄÀ’Ô#‚ ðÈ \oü»À>öãÍÏí'úÂG_úoç½^±/‘' xÂð]’ ÀåŽwþèúõèûœrëgÿô-‚yí§%þ1À1€ Äã}´ +êÔì‰ÍûÉbÔå"ôùIÛíœ æÞ.ÿ!*â Âü– %>„"> ?@0£2"DŒ€¼At"žžÁC|@ <„_+Ž£Þ•†ôÍEÚ¤ºdÕGº#¶œã;ºX<Ê#Š!ðdcfbCpŸ€L"÷¡3ÂF„dczâ"O¶ÀCXì£Cp@Ô¡8n[Tâá:¦KÀÅ:zdà•VâJ¾!ÂfY’e€ð!6Ä ¥C< !0#"H@Üd3:aC¼C¥_†â(–b `EÆ%8VDŒ…@ @¼@,uÀ€€ŒUÖ¤Á]QEiéS9ÿbV" ¦ŒW~å7"÷5äCØë¡Aa¨&l ]J!Z!éá_@ rBšÀ¦¡¬¡ÌS6DFB%V0@QÁ«Ì‡ŒýÖHŒ È”@ À L@äw¬Á @˜AvÆ L@ìy'xÑó ÅÉ=D,¶£í}fJ.JŠ&Gt&$Y€à¥QÀP"0€æ‚2€ à'ú%"¨@àè)Z€>€26ä!ˆ€ˆ øYÁü šàE6åSnd™0€Q À"ÅDØE @pEA Ä R–tÉ`Xx„E’ìÿÀîhvÉ-ÍgœCÜ'ÚhH@i”†¦jDç ÜÛ\)D¬@Šö_ùyÌݸ¨ ™ Íd×U´À’²€¬ðhäY ´Ò™ °) ©›  ¼® “ªEÁéç~®¡ªðp)Œm©¢š`aöߣòS©5@™ ”€dN~Yvš¾tg4A+Í)® ¿”À`€§æ =½çˆêŸ¾ l^CXÝŒÆÄ |€ Œ€!”A˜@ÀÁÔI)ÔPÊ­æê!¸ôê¯ë°âžJ6ê¯Ù'µBbD¬@˜nÌË-ͨ5MF-¸‚«× Ü£0Ü“RŠJðæ DJ¬ÿÀ!ôlàÞ R\º8\0A ¸+¼Ê«´òçµf›µìªMå“áÁÊ–ÖkºNʵ¬@KÄE0€  ÄÎØ§±ŽÂĺ+ÅZ¬õÁcéÁ@<ã @¬l`†6Éšì3 EL X"ËÀì¼mÂâœã%Þ£P ¾òfLH,¼ZÞÎÎÜÆ-¿*-À–Î @4c*"‚¸!Dè€ØlR­Õf1ÖÅØZDÍRÄéå,[,-ÏþNÂ"Þ] mD°]Ñ 𪯾kWÎmÆ"fÓÚ-ÞþjÒ^¬ÈVN %D€-DÀ0@ Âá–mCˆ¥ |cK€ Xÿ`L€ZA(&ÀdA(2Àn0€¯¢®ê:åj.é&€é"êÀd"É>À €!°î)îe<Àü$ë®ì>n²mÛÒ]*}Ì­úª¥0¬Ë)œgR¯•ºÍ!<@|@ÈäƒÀãÄ"”@3z/øâ¥Ø€Ÿ O‚´!"ô€ÔoÕB$€¾Á<+‚7ÄM¾/)úoÚ쪀įîn¥µŠ ‚î(0ó씨ZÀ$ÂÓ½m¢U%gÞ‡”¥\Ïaï*€ö'Ž A<€(b­Ö:Ä`ÈoCа c ‚ÿ¢ #°âÀ;Do ï0n˜ÀBDñ»®@/ïöÀ<Œp@CpK1^òO Ÿ! @?€](t0µAÁÁÌ, «ÐD/ä¡põuŒÎ¡¤UZ¯HÀðöVŽX¦bÖ>„ï­¬À¥ë €ò¨L¡Y04D/qC0@D!¨†(–òܰQºž(?°âî0Á.0" ²*ƒH3º^·ò>ió6„쎭äVÄ0‹-Θ0Üâ”ܾö6) Þ"ÏPé.2ÛÄòJ¡`-r2ñ8ÄHÀ\­æ°Ø0"ȯëÅÁ '×òÿÈA3NíãðA&ú^Üó€N®ëÕï<£ÁL4#{Vq&öÀØó*ß䀾cûsØ.o­þ2" À(OÄThð*¯ÿ­a[\mClcbôhtp4éíÝM=/ ëÍ2Û'Ú¡m@­ ²…4Ë4 ]‹ìí´ZNà_$ÀZ ¬lðÁ¬¥Q@L€ü4Rçå 0À\.#xnú¢èÆî˜@hu  /® ÚBn(vµ!X€\À(<ÀŒÀ  T¯`€ƒpùqõI«…/ÿ²†ö@;ÄÀNnG¤í¸OÿÄ#[ßx¿^°ÿrL@<ˆ^EOv>Á a´/@þ°a÷û¿&šsv‹¿Þ¶+óY93÷¹ôI¹HÔt}ºøP®å"çb5b`î$@ð¥ðÆ®é¾îZš!Åb®æv<ˆ¥4a*3À’üðFµñšµì†»ìowƒˆÆ?ë™|¸Cͼ7ï[ $€Üd<àZ€ˆ{)DØHð9@=EO}´o6Kûqµ·yቤÊ=œ ת-VÞ,B Ù·ð?xï püª³â/Ð|‚â2hpþ}D$Àn€ˆÓýýZÁ”àû²g!ÍóýC€-\=ÂÿoBþ”=¤‹i–v¾ç{^¶•yo}Î4|HžË#9éÍP9œ 1"0À…{òéÙ$2þAùñDoN@d—-à¯1Ç#ñ€"ãù__«12îò81ï:Äø2À#Òþ(Âþ@Ëñlãòæ+^Ɖœ÷?ø…Ó´ Ÿ «œl³ë· Ofrìƒáì«mã2/Oî~þ;‚r¿##?@ :$"C˜X(¸pá€8 8ÀPÁ:BÔ¸‘c€‚9†9’dI“'Q¦T¹’eK—/G$ˆfM›7qæÔ¹“'Ï@ a3@Q£ %Rº”©Q§L¡*:”ÿjU•0¬fÍ:1"4ðA„1âÄ‚ 0Äg@hjLÇ,¢8aÇZˆëõAA"&ª­{­ÛCh Ëb€®_ù@|8b– §jÕ¼™sgÏŸcÎì9štéÒ?A§îèÔéǨ¯aÃάšvJ¬µq3œø‚Á,ˆ˜H0A¬DhWp…A‚&ö2œkˆ`A&¯‹¦/¢¿ “3¨Ž–¹sÀ#¸¢ùïòÏü¾1w}û÷ñ •išÿþ¨óK5’ŠÍ@ÙL° Ûô¬®­ŠN#Cº›o61ÌPêöóÏÃspáXûȨQŒêÂ?cEª$|ÿ‘¥­"PÆqÌ)Ñ@ìñCuTi@ÖR,R)LRÉ%™lÒÉ'¡ŒRÊ)©D2È+±ìˆ¾,¹ì·} “? ½ÜhÈÖÊ‚j„A‡C\Á„ )#2€ãµËìÓÏ?ÍÜÐA ] L1í‰L/DªDõ^s@)&b`ªŒé!ˆ= íÔS.müTÔQ?â1ÑSCäóÆFW+J£¡’ô²°”LøƒSRy刉Ðèb4iØb9`€˜=D¥a3 Ù”ø8 PU{ÍËCQí–¦EGò$WÍ„MV&Þ\êR±Õ¶SÞØ¨:ÝæE‰ i5RÌ¥z½’ÿ–ßœå¨Z3Ûu×`¹­) FA+L¸"&HÃÛÓ ¶ÕÍ]j…æ¬ÓØ2>¸Ï``+šcx`–H tpν–Ñ0da¡bž@Þ ØXË rˆ! ãš]È„ È‚h™Õàê¬òèBú·ŸøÀ«-`¢<—½;àä6Ùî+¦i™é ¦@!|˜ Š J¨€†PƒñŒèÀ„(¢è „(Ö@Áà¨À„Žx\SqWãG;ƒÕHvï&u.>ø‘7¬õˆ­‡ù#&avÙibƒ³h÷Þøÿ#öÙ­ ŒY¹í*ÀÙLXžö‰~C#ØÌŒË>Ø‚„?k"ÞYFDvDTða¢'ùy¶d§ûÕ’[¯ÿ¾¼¹… @cŠ® €@4B ‚(b1 BP‚h!>0å€$€SH`((Š~YWmT·:ÙÏSsa" ³Â#L^A&⬃0¡á `‚'T)‘ˆA2™=`† yCP«ô ‡€Àtç¬? @P¡¡¹X8qˆ9D""zàCÐéˆ/Œá@„Ò®n,´#†ð÷­"´ '˜Â x(ð [ÿX0„™" <( Ò°Ä` ‚˜à‚t:1f6v?¢¨„wÄ‘ 3RD.d0IeAþð„$ `ôÙË_™˜ôøeYx@Bts.rï–a<‹,i)L;U„d«0„„Žó3å5„?'D_øcàL5HA¬ÀÃ$H„`EX§´‚ à @ €Î˜„›9!„R8J¨”›,zdÞ’–µ@ñƒ0#34ðA‡j ÌÚ Ú–„¢"Ù"ú–V*”1#-*B?ŒgOPÈ!&‚ºæ-c¬Hšr”§(ìdMaÿˆ¾íä§‹\dßDsÔ@°  Pj?I„:6VØÔÚÓ ÍïLr`…šÙŒ¥]mÎsÄZ³0$`… è€ •ù@L°‚ŒVz eqk0Îæ–·"É#f}ôÉùy6KIÉBL‚9x@µÊA Þd ¨à:p“ t®Û„¸½õn~vû]ñFIJ–nf‰Ê§P*) ³ÂG›{ˆè`SVþÀZ¥ÿXu)œ}JSÐ4^«&¼æmÍ{^¨v6À£jo¨ÃÓ¢–¿Y‚iÑpZáª4BIQŠ´SX+&ñdû£@„ A –Ú8u ,v“ÿä(&±@ûKЯNÄ'6òKL|ä‚"ø&k¸ 8P‚/âP Á @P´` 5YM†À£^„Kpé’\¦ÿ†¸È&²@Û¬d? ì$–±ò¯ùÈYο5 F p [Ђ›0€&(aM¦°€PÇ›½&‘ÚàSu”qæs}jé:—än¡ß½ôl¨O+9Å6é  b€„ Áÿ1‚ùf“/ BŠ*™”æN­÷2@^áªOR¶š¡a–É{\޶ìfÿáWá¨×,àPó¡F›Ú¬†µßAnòCD¶m…ˆeeÀ63™=[lAZÑŽÆ€¤™uiF¶‘ýl'—€BîpÎþ ×^þ ¢LãÕÒÿ.©±mq‘¬ï܈×p€àu;ì ‚Ç1‚à¥z)¹ù P¬•!çKö¼Âøé5§o{˜p²>¶³ÞÂgâyʹ¿5nà›÷¨§j„ÛgŒg<ê1c¾G `2+@v¿Là†+ÿýbA>ôq!i|Àß¾ÅU2!gg€>PÒ±èÀáÉHwú¼}‡J,{ÝÜõ·ZÁ¥«xÔ9mla—:áÎÚ_¹îí°@t¸ÓLˆ,Ó;‰E¢4¹Í0ö xA:ÿú@s!±œe-=RLÞ§dó’×ÖÔ+oyO¿§oÞúD˜ïݽ|¾õ=ÌôGÿ¼`„FúFCe"]LÀõ‰ƒn Y"`Xp„vÕ‰RÔ -ˆ`2j¦•$úÆ'U«Ï,ù€ªÿ°Éù¶ÎÓŒûüÂðª:¨ï/®#;®!‚ Xâ#l ë°Ê‹Œ ±‹!â ¬ÿN/÷謘Ðj¬h® Úª¯øí9 Îÿ¼‹ò½REòŒ#óØÌÃhÐ3fBìk0Ð[Ê‹/-È ]Nd»ˆ°3`‡´xE £½Žð“/ëXmOÖŒ ›Ð ¯ðøÆ·lð/+»Ž¡ï ¹® L áQ² ÓPÇä…Ø°ÓæpÄò°·E µPÁ¸ðÈöP…þpÀîìþ6b1Ϫ_Áå{FâXRMKqÉÑ‹*[ç‰lC‚_ö/0FÕ"1Ó"àeãPQ_… ðLlñq1u±»VMïæÙ FŒ¦†gÂ-k¶fºæk´¦hÿª8îMfòmßž-l~ÆÞžQ,h†¬ b^àÙäMõ*eV Ù†w&ß̆ßàF§hñß1 Ö¤MÞ$NBÆNðDOT¤ë܇vÆhzVàèpN{º¨,À{èã.Ä¢å–îxXÆéø|nÈ,àvb¥þ`.ìè r.*²å8®!É'y<Òá1%ûÓ`R*E]0ESvEãxȇàˆB¤¨Èî$àìŽh‹ ²1Àh÷Ú®^<‚ñ’h‰ oŒ„Èön¥öÎx€w˜`'%`†2Ã…ŠòíŠ)«R)¯ Ur,Ó@)hÅVpEWö1ê†Ï;zé—BO˜‚2¯j¨(ÿ—Ž¥Þ2lé) *'bÈæ-³2äÈôŠ%øô2—P’,“YrR´«ÔFæ W¦‰> 3ßïŒìÏ5èR-ô@_Èâ¢Z‰3¥êï ¶ï"*#OïjPó3áuZÓñ¯¯P³1³71/6Î-A†NÖ…d$ï­¡®îꬠ£¬öÊ5œóà „ÁãÄ‘³w@`­"ÓŠõJhFŸ³6"8¬­X^ðŒ¶³ˆ|s>-ÎCŒ>— ?õ“Ïì3E€q?§ @”Äú“”T4AÅ«@±JACÑA!Ô Ÿ¯-#T·*ôB—luø«AÉ’@êL,ÿ……O‚Ü ¶­AþCYtC $´F«´N+µV«µÐàµbk¶V˶|ð1²b¢_RÑóN‚5qç‰EZ¨bE[´IUt)Ž+¹æàpÀ¹ ë¤‹º¬K°k2™´·Àç Ãk6 ŠÂ-Š oßm<ÀX hj¨f´ƒLÁ¦ ²@z âÙØÆ‰‘j ÍÚfItPí¦@Ü ¾je¾êë¾ò‹ ö«Ã¾”·ÔJÀxØGæîå0Õ,ÖÍiÁÎÖ 3BÒ几>ÂÚG _î'1 Gn$•Pmõø–" L1&,µ6ÅÂ0LÃ8¬¿sÖªæÎŒÿÌ€ð˜•@¢à@h0Ã/óΘUŽ0,¢¤£¦¥¨§ª©¬«®|!~€‚—¶ˆŸ™U›½¾¶²³ÂÃÄÅÆÇÈÉ´·»Í&¼6¹)sֱŨmÌÞφœÐâ›ãÌÁÊéêëë۷ѾŒò<Ô#¡®­ùøûú­îè„ýë&D 3‘ðM= ð"λÐ ;@•<öï’ÄEãÿÌíx눳 Ò°¡—!`b! ®×GEÀbd‡2£Nu }´’ž´3ýø)MÊôTP =—q#ÄàÁ–VXͪáÕ¬[±Î<ÊA•yfÏ¥ÔùoX”K×­¬¹r㢉‹ì©³g‰–ëA`O@¬XBã~+D•:uد^µb¾ŒU¬Ì($Ñò RíNŒwëªVÚ5_Õ{Q£ö3Y.3Ò¥'N±{C RˆE16Ř)ãK“7<¡¶š T{$ä|Y³æ¯nÍRX‘&Ýd­]›ÌÏòpç¶E—º¯7ï£ÍÁ3þ„»ÆÃÈÿ²ç¶A·ÿÓXAg×YwvÛ]ÑÛ7"Ãl«'Ûx§Å†amÌA¨ÄƒP©Ü~$ GbpÈ5¥â>øÑíàƒ rV#W:XŸ‡güTz­íd†?jHW{ñ(ˆܧߓü‘D‹íÔR Nã9Š%“JrP_íá•¡^ÃàõmhiL‡aÓ£ ‡"xÚY¢q)*ç§?U)`œKîHh>%ª(P‡rgèIPJ¹Ø‰‹õ×…˜¹c£ =ŠÉžb¦ ..jª ·0QY¥ªaÂé ¥Ç­hë+˜òd%§_¾ Ñ« ŽÚã©Ä §ÁúúKsöQÿs¥¾〼±[4ðÖHnxœ5Ñ+nš·Å'N¶¸áÔíºåœ‡°üöç¼·êY¸i µÕÊ©l<îrKTH fÄF’‡a…§ƒï±c-"°Á‘F¶“S„Ü듾ƒökí¿ƒä­|EÖ®É$Ãаk³V¡6ã Tî3àŒÎ"©œ3Î+È[/½D—âqF O+òÈGh¬rMëöŒnˤ3dl´-\ä^zuf[j™ÒÍ‚ U±Hc<ñ»TSžûYšÜ¤j€o¾lªÃ|÷í÷߀.øàAô6ŸÏR§\ðªàZ}á‘?jm¤†WCs^V“nÿâ(—ÍùÚG +ô)p¯ˆçÐËÙ(IWÖCß40ì±Ïn{í¸@»î·óßF©Ú*`àh²òámí8…–‹=!æ­™‡^åä¦Êr»îv>Øõž#OBò`“§cÅ‘wžç_ƒ_w?Àú®{Ë.?ïó×Oÿýöçÿý ŒD5¼›Ô¹„-,k “ õlF¶ï9p àûMú 0ÁÒ¡¯NÅA]rØ·ºÖ¹î Ø{7ÂÜ•Ðw(ÁÑr´&WwÇÕà‰$ ¦ … ÈaÖΔ-@e*WÉL¼Ò‡ìØ.}¢É¼uRŸ¥6I)Jd²@™ÕRe3}±g^±m£Ó :çØ¤4<á î|çâùÎ?TÓ¸f ¬xÂ~ÓŸÆD¡7WÐÂ…ªY¬jÜ8êJô×â :Ÿ•ÀÓÿ¢ó¼è.zQ;öô‰‚lró¤Ûœâ@%ÊRLx*X e¨Ld‹NÔ¦ªšè:wÊN  A£ñGçé¾{J@¤v$é€P€ó©N•ÝJQ&m/jÞ2ÐV惘æå5ZsÓL“ô‚ìª'ÓªZ}FUmC}°ä3ÉÿTTžÅ+FƒZÔ^"5©üòdJQJX(N5§,½IÁâ‘6ø,«4t‘8ÅUÎ-HbêâDc‡2·’Ž–**]r@;¯ È¯@Åh=ÓàQý°•í?éÇÚÚOz¤T=¾œi+³<ûEZÁé› …ñH1ÓXµq3ŦµUWÿåã˜E5ñ•¯–t%¢%á:ÄíŽoˆnk§P3JԼ÷|-`e`ÒÁº¥‡mš |‹6áf̾ Bd)4¹™-·¡B¹,㌷³"Öı%LËÓU0ƒvÊS¬ÜxA\vÄC=¯yÏÛÚ£zÔ£J S¡úÏ7Õvñ¥î|ü¹áuq儳!ý—¹e-xübÇVµeÖ5b%¯I¹šO}ÞrsÄ«Z¢ÊÓÉå…ç‡Cúa ¶hoa·œÒ»Š —Úvq.î¹Ð’ãâånŒãš†™xÆã*º$ÖÒ’¨qˆÎ`üÙZNØ‚ã³0“y©IõZ9³=±ÿ¢I¼h/{€¸Á ňÃ2²™2æìlo DHfWÈB µ$‘LdR/Ù§§j­¡× -¿÷ÕRt4DwX„I·¿`½´3}0“L‚#r0úd è8ÚÏDö-ã5hB¯¶­f´´£jbÞÉzgÎôdu `^û+B¨k§%êTk²ÊU±\Íåv«Ô^&·k†eo'ó×üí„s)ÇL¶µèþ°ºe›ÛAêöàOxn¯ {WcÞôvxzSorK†X8´]ÀnX{¼”÷ζ9· ñ]K»I'¿y ¨‹›ûÙì FLíE× •ÈG^rÊžœ)§¨ËMÿunšãO:þrîÒŸÛ£PºÔÉàt!å+WŠ¿1Î:£½‰žvµo¾;¦oçUO»Ú]ª¬·Ö?tÓnq¾Ë1·qæV–¹:îî¾×Ïì¿Z»àʶ'¸ÁÅNâÙ¸ø>ÝÊÛîº×¿¾šýò6ÿÝ7ÑNøÎÿ¼0ÖPòw³«ä ‚ZR¶;Æñs¤ûýõ†Ý¼çgOûYØ >‘‚/aG—:ÑYŸw°üø¸M¾mi;?ÀC·öÐþÆÄÔéóé»p/²Ü¥úTŸòûTºøA¾bé›?ú4Áýç¾{W;Žoì·ð%ùɳºòbϼþqfÿàÿ€hS÷çw€ D}F´€¥ç]£vtcÝ7|ö~%u˜¸Øø˜?Î÷À&x‚(˜‚‹bZØ‚•Ð$.ƒT÷t2Xƒz@ƒ6(ƒ€ºà+òl)"ZÆAÅFZ¤AW,ß×z•wð„P…R8…TX…VèÐ;#X%˜‚^x‚+XQ=×mñ2†˜v{fHSÙ`†_ÈÐt¸€2 nàÖⲄ¸TNx…|؇}x"X~þ׆_†Íf†–†jxiˆe$G,mø†=hWµ’u­°FD³ $‡‡÷·ON¸P˜¡(Š~è‡Y8ÿB€‡]Hˆ(hˆå¦ˆ‹>Šèˆhi‰p¨€&a‡JXS6p°{ˆ…Q8Œ{hŒ¥ø‡ØV°Š¬h‚®8ˆxˆG‹Óˆflø…’˜_¾a‰Þ¨K¼H,œÈ:ÀhŠè8…ȘŒW˜…”мàŒÏX‹¯8†Öˆ¶˜^¸ÜHGB'‹½è‹šTŽpŽO¸ŽR¨ìH…€¸tu&¬Há8‹Ô8oùØsYr¸˜‹vó ™ šX,@1§w²¥ÃXŒ¤ØUxŠ(ÆŒÍøŒ­¨(¦‹&W†ö!;¹†cØ‘“¨‹u0’Æb’­e€Ä˜é’Ê‘&ÿIˆ)°˜ˆ=Ɉ×È“ü˜‚þxÔ‡ub© Dy*%i”䈒 ”+¹Ž+锩…2)•4 †¬”ƒæqcX‰ˆ38M9¹ò“Ú˜õPqBP–¦r–F™Oj™e ‰…˜Žpù’ËØ4Ò0•mX•dr@l¶—øØfbø™d(ó”Aù‘c™šÌV‘+ˆ–¿ó–“ —0yLQI—uÉš‰$«ñˆŒr š©k:T\]‰‚_ –é6xÙ‚>Åš­‰–ú›J9›“ùw›ƒ˜›»)3¶6SžœŒä—%Wœªsœ'˜œÓ"FSž" 6éšÓÙ˜PÖé”îÈ?Ú‰ÿ™…h“0èa%SáYžÂ œÕ8žä q¦yš!R—æoð™(Ši’óIŸÁ(›÷ÉŽØI?9wüé…Ýé&ß) ÃIœJšÜfž ZšƒI˜2:¾INïi•‰ÙW‹ÉIjŽmi…ǘ¡¡ÈòUª‚þéS¼9¢ 5 Š %Ú™LÚ¤7¶ ÿøœÚ™Tª“fi£Š£ˆŽº”>Š•0¤5)¡ÿ)¢š¤Pª—'Z¥)ú¤Ê¢ýØp¨i 1š÷`4ª§ZJóÙ¥LiŒ‘)ŠÇ™‰¨É˜Ÿe§ieš›ºY¤’q¤kºPJŠ‘oÚ§*§(ªkR:¥á§ÿ¡4ºÿ‚ê‹ cl¹”ÈØ£­*™¦8¦fj—h*X:iyÊJm:ªÚ0ƒ»*¡VÓ—Á꟪œMB–îI–šÊ§Z©š£€¯ZŒƒê¥°ú‡@J¦Ê’ºd”*3cu©qº<ÕS¬~š&m†®q¬ë‰«¢º\¤Š¥5êšw¤ª‡j¨ y­ü ¦± •Ü*¤J‘®bž½*¯4ÃHìê¬ê:ž »îú|ÏÙžÕ8£ôjªö­3÷‰ Y­Û¯_ú’ G]´šž„X‚þå0Ïi°+k© ;® {®h® ”± Ú¢.ºœÍаðJrh°±çø–Ÿˆ¨…šH›­|¸¡òsÿmÐ…%ˆ™*û­á“«œ©—1 ž3Ë@ 6;6¥š³uŠmi±9£@û¬ö—4åhŸaZеim*vRë†éùnø[óEQ¡.ûµc£‰.ë«a» ‚«‡+-‰› K±¡Ú³$ú³”Ŷ“ç¶Ò*p·O™+°x{·&8µº‰kâ*¶á¸”Õœ­d¸K³ˆ¸¸4Õ¸Èð¸1¶dh›¢jK’è%ŸL”¹±É¹ ɨIY·ºŠ¤‹œ¥Ë_vÁºVJ¸º©²k ´ËÓ+¹Ëe»Ÿ„ÕH¥ÛæöÚKÀ¸¹ÂË£²j¶0µˆ·Ê˦{°g8¶ú½j*½¯ëµ5{ÿ¿¨[½Æ`»·Ël¹Kœ»k,½+C»n#[¾ê¸­¶g·ò¨·£Ë¾ïû²7‰“ËœÑë¤×‹³˜ª¿RÁ¿Åà¿À½é½¼k¹—{À)©Àr{¾vêÀ^¹·R[‚Dio ÂîA¿½i¢ŒÃ¼²ØààÂõ£ËʧLN|£¿»wùšÀÖÊ£r[²_–¾©äš©ÃH* ]+¶B (Ä·¨³/ ¯Ø ³”KÀ(ìu­3pEÅMÛŽ- °ea(µj«zªÅµÈÅ•š¤_Œ¸ùë#7Æà¿·kÄˬIœ®áûÆÒÇꨨ¤Ø£‰zÉÚŠŠUlÅ{«sª‘«>…«ÿ°=<ÈkJÆ`ÿˆì$œ¢&ÌÆ¬4D{¨r²Ä²ÕÚ¹ºÉv{ÅV›ÆZëǧ+³¥»?<»]îãm äõIßkí´äؽÜÉo Ô+Î% åÜ ]åÍQã«$ÑÃð¸a0ãxê·N>Nì- ‹ãd’Ê`Þã;þã^æN²ïhäÞìËÖ«¦o¯qçs¾ââíâzžçÊàÿ¿_ÐàªÊ]Ø™’\‰nábnÖŽBÛhNH’žÝn~µ˜îÊrÞâtnàR.@2á3tìvñU}ûUS©žPÞÒ Þž¤7®§\Ñ®ýê<Žád¾Ø¾À%­æÝŠÒ3›0—Žà+ýë®Ëéuļ8C%~C•“5@rÛéi¼‚ýé[®86ÐÃ5ª¶í¿ÑÖ´Âß¾. ªã^—ºn鼎îÞëÀÎîÂîÐV~¤Ú=3Fâļ`ïXþ¹§}Ñ©mè´&Šæ‹ë±®£ÏÖ¹sX$‘땾Ãç>å™.ñë>ì¾â:‰$ 9k=ònˆï9ý Ëÿ¥Ñ,Í-fε.ó¯BómîðùÞÛäªâ<ßî MìD‚9óîïÅ^ôéìÔ À¾È|jرÜí.ÿò?îÞV_îäñð8ïëš>ñ]_ñvö":Fï5,ö·–FöÔôV:©< ßVð.÷X˜ðïzÒ _óæ®÷/®ôêNÊÁŽÎÃþù¤­à/<ò)’Ýô¨êj9ëõÝÖuÿá”~õ6ïùžº:/úOúñÍtÓãøOŽÁÌíú¯÷Óõ <õèkûlŽ÷¨¡ûZÏÝ‚È/Òó¥ïÙÄ]ª¯—¬¯ÚÊ­”äÎ_¾D®yiзOý®aýŒýò¬ÿýáíõëZÆe°ÆêÈßåä£4TŠôæÝ#G`Ht\§u_8–gÚ9×—”L’^ÐÀ GFi©\š€(Jr.­WlV«œ¢ª[püÅ›RqZ½4C¯Ý‹dâ¯ÁH¢!¸¥€@€6»Â²@ÄAB¬¹9&ŒÈ>€•ÍMÎNÏOÐPÍž½ŸK’!¼V­ª¯¸?*ÃZ±.Z[]+Â^´]àG®·G·ÅáàŠV=Õ”ÔA/åZ\)d^§ ¹l p ðpqññK¾÷vøwùxúyû>hgÖV¤×§'Sª]›¶¦Ú¯‚¶z”P!/†Šÿ¸4r$çÁ4Ìœ=ó%F‡\26†”8()„ci®¹qàÈ$¡nRîDääIÊçΟ:…öz*ˆ>Uüúù'ìØ¬b!wl(µÐB^­¦iã ±lÅ‚£q£ŽÒšX¤³JŠ’[¶Œã¤å·˜yÏÑDa3»{ë ,Øh¤—”.%Ò´íÓh¹ÜÚ¡Jv²Ó2 -OîVX ÉÄÕeölŸ´bÞ`¤D®£Sι{ÎC̽”Føía4ÐvÀ‡ÊÞ3¸¼ÃÏN/fì8ÖÛÈQ/ì±™óÍÓÃtΪõ˜°”™­k1}ÚÀÕcÇß‚»¹¤6•îâ~ÿé­~¾»OÜ$Ü¿°ÿþ’Ck9ÆŽÈb.T0AíØ¨®Á: ên=©¸sÐñ. ï; ¯(ï¬ó!©Œç É5a»E6:ä°ï¥Üb¬¯’ýþ(oÔ‘¨}p„Ä+©Ó“%:ï <ðA%‹”𲋨¶ íJn@‹R‰A½‰˜€åÄö‚!C4¿Ùƒ7ð?8ßlàGÔ²1C¨ªªÉW˜äóÉd üÐôÊnP ºÜ(YhÃ-y´«”H3MÝÒ©±· xô´SPsä‘N‚䃹¥¨ "R2áB” ì&|53X»ÛsVEaT­Çf-¡ƒ(ÿ¥ÌÒK%É´¯MSp3Nf弇TS÷@µUý\‹­&+›•ÖZÝö±BÏHrÐ\UÙUµkê«ÂIY·Xc×L¶ÍE µ^|q€Ö¦u%ÏU»TÛoÁ ׂqÿœÒЄÿÄ£™ÓêÓI«‚u÷Lx'áƒMœõ¸ÙyöµóÎj] ¼cÔ í3–·³vV¬¼Ývaq«rw—‡#òË ºYÒž/&6ãŽEö&ŸŽûÍžâ’NiwD>Ë_FðPX¬»öÄkXðb#E`Æe¥Q—![ãÔŒ¸fÒd/'Ù`%/ZïW¯¬aáP°TD~,ÔsZ¨ ¹îA cj©HÑ~ã%}¹º#rofš|’º™Epq±ûž _çÅ,n-Ž!%‰ii.*Ì^õÉÌ%yasÿ´|Ÿ-K°È]F”—ˆÉ_R~I™À3&‚ª¥e¢ìžý¤g’,ôNíÉn”%Eˆ5³—R`TW@@W>«§O¶\/§¯0d187‡âRˆõ#ꨎ¢3(ý°\6LFÜ3¤Y`UµYO’Þ´ ©=½²'”n ¦8Ce*6¸ÍÒ› go$úÖüÈyÏC'0K躜æ5”+ƒg_!ÅÎêÔ|¥÷²3ѨÇ)Øbì¹J®4$25M¶ž*¡;TÇp2TqÕGEêE©3½n!Ÿ%\, «?N•ŽÎ<,W*†ÇØþïš“-+åÎ G…vóc„ÿò”׷ʵ¢­+FQ‹MÝŠÉJ¬¤ør±Hi©©¯#(=kk á ‚ÍšC({‰TÔTa™¥ÆO‰æÐ‡rªió}¤$í 5}‘¶ˆÍ=í] ÖèVe£‰£G¹Ÿ^EÁó¼ê•ôX µ™%æyóñÛÁmÂÆV?‰ _Ï"׃ðP.R pb§XÅXP‹·‘QOXµ¬­®îÔÉO)Ñrÿ,¦=¥X^Ø}F*F…e/70öî='܇;{·Ï’3´S-Gøxe,3µ°~åj_ýºEÙs¥N5¦àxìUÏhmÙ¬poχÄ—iÉaBp¡œÜÖ6‰8¢%ÎrŸÿuv楚2ª·-óá®4 šŠn† ‘}Â$76¯‡öð7ñLFJ´›•ýüéÚ®7¾c¡÷yhIo¹±Y0"÷ÒÁ'LëteÓa;[Ú¸yq¯ÃgP{¢v.œ¦cèØq62f6]lU)B3Ö¤U¯ð2« ‘Fnœ½´ D«ipŸÎÓÂ&·±Ñê_š:È)›ô©¹ø?dì«Ä. £ehní OÉ,ª3fî¼kLëÙ×À.wÁé­‚xŽêæ>Í<à“4¥¯&ßÌçÛj¯›ùt%¥ßãÞŒÊN3ÎÓL^r”;à'÷Á¹òÚ±¶ÙXïVìÿl•t7ÑZD„¦­ßYs&­>E$PŸìV_'Ä@gyË¡Y³ëúø: ×®—S%¬{YÞžq)Ÿ~ΑŒ?˜½y9¼DîCN»œLoz°]Ž8µPÌ„¥»Ö;œX6wêƒv5×Üo·|Hyû…¨°Ðµràx8WÞöOþkP¥•‰¨zùÝf~™bmð£ã†ëÇÍ»ÎÔŽàÑv(޽RE¯3Ø”iµSÙt‡|Ÿ%/—Ç;·ÿ­¹ …Üj²Ú·^ Íkï{k9ÎÁÍáfyt^7~y´¯=–oß'ʧ¶÷ñÜ+Ý?ü ç[¼êîVL¿3Ô7(sŽBüpu½xLÿÄ>í@¤~õùxý„ÆÝv‚•:ï¹–ÍÎÃ?4³° ¤˜+ˆó«僢¡Ó¬Ä{¾n;#¥›À9a;û³¾§³.éˆþÓ<‘ê@øè;Ï?ðûŠâ¼ÉH@¶I¿|S½{@£ƒ>L#9¤¯HÂ/“«¿ Œü¢ ¬‹'!Ü2xÀ‰K˜RdØ9¾k$¼s;<áú7øË% 1ÜÁ³èÁíYÀR+Á“:@„³#l˜Â{Â`ADsAƒ(»b¯ø¢Ù“?ûÉB-t.4/ÙCDkÀöb½s½y±Bé“;¼ÃKÈÃRBÄëC':&ÿ›‹¬ˆ“8B¬8#¸¸óQCHÛ8µr?¶’Ao ·9 ­I¤ÄaË@óB>¤:í㸛›®AL³B´¸,ƒBà’ÂæƒÁÖcESˆÄé³@YÜBZ\¢îêº9O”¸­#@i3D2EÎ`Ã1dD³sDe¡Crì‰XlFt&|[8iÔ¾w ÁÕ °ÐK³ ÛŽãF,Æ c¾T¤Âø‰¾+Ô%sBGgÌ@hL·vÔ¨\¶­â²&d)q82$+|¼²àF%òFÛÇ7Gz±Áû:¹¬¯übF‚äKd L„"M´Ä»5ï³¼ŸAÕT"E$;޼8|=eÜYÛ‡ Ês±AÉÿg³[LH&Áݱ&jlœ'¤!ÌÑHðr¾„¿ø{Åù ¢ÔÓz*¤dGtÉñS³wã·iˆÊšl›\=«¦œ LÔÆÊ’J$CE¢‹ËFŒÀøsÅ­$§»’¯œ¼°Tœ½dI†;¶ŒHÉ‚G†T†L ”¥Å4ÆÆœËž´ËŸÄK’é¯ÌÌ–Ø$A¥ŒFk|¦ ˺$œIÃD/G+Mª´ÇœÄd ­„Ì:lMÊÌKËœMìˤüËÚéÌ”M ãKbìÇ÷ ‚«KTÎSžèA8£4Hç<¡–ÌÛ´ÅE«HßBÌÿõRLTEÅûÇ$I‘I,¹É ÏÊĽY4Oè$ËõAD;KêÍ [GZãÇÓ¤O´Ï€óN,,²J²$ØPìlËÝL¿„ÈêTP ÅÈÖÎ$BÍptÌ(+ÇÈ7“ñ,¶óÌ·U¢ôœÎÀÌF÷|3­·|A½J e< õIð„ÑÿľË\' ÕÌÚDÈ5Ðé°Nø´6ù\ÄáŒmÑãÔ¾ñJæÐ%UÇÝ;éܤýÅQÌÇëžmC7$N]Mæ±Ðs S%­ÑeP'=Ó¥ŒÒÝÑF«RÓá ÒÔRãäRF¢#í¤R*ò Ч;HÎ|ÒKTÿ]S‹lSÙtÐùDÔUÍ"]ÆGMH%’IÓ(,SõÐèÑM-R¼7­•‹%ØNM±ýÌOüL9á ¶¹Ó×\U=º&U¿ESMUSZeS>µIPÅRÆìH¬”@:U#;Óc}9J…&KýP–2KêœÕ<Ö[e—+ÅÉ,„ Z$EÎá Ö¹BT-]Öå›VÚôSÛ”UhMW³š˜ °TUlUÔd,U#=ÕUˆÑþÛGíPL%×2”ÒË8?àY×ÉUSX´%¡¢×.5ªnuÍ6r%¢|Ù9Øðû×5×4Åh-Ø‹õÑju×kÕəР\ÚVÿI\PÕÁÓü£ØgsS~ÅX€…RÅY‚õ-jšÊv…ËŸ•Ó™ ‘pjTµ×åBR¤e„bàG,bÚ5ÓšUϨMPAµ¢´•’7ýÆwÕ˜0š¡uXÖ4Z–ÅÐUUZ̸¾qÕreÛÎãØQŠªÚÄ4ÔbDQ %c9Ù`ÉÖIòÍ£ýV¸\•’YÄ}ÕŒ%Ü TN¥Ú¸–¹ÝȺ½ÈÐÑV½-Z¾]NÎKÏ=ÛOM]B]=\ÓÍYÔY)Ù1ãÕ­C´CY¯-É•õϱõ ³í?eåÝð‚Õß-]te€Z(ƒE[¿KXäÝZ3’]¢5 ÍõÖ–ýÛ鵯ÿDlZôÄÞL<×åÞNeÜøt\ñ]Å€òÍ4Q`àè´ç¥«è%Àö5¾êuU™“ß>m[\QÜÁköÜÓð}P’ÕZ®ý‹x`aá V´ÝõW$QÝíWîݧÍT ~Ó;—˲ZýÕàñ½¥Гma!â(`.Ö¾mމ]`mz_ë=‘fýÓÖ£·©.M¸]³Ùáø¢ +ôØ„0c"á>̤P1¿u_ñÉ]ê¥Ö'Î1fVú•Zû}OüµÒÐâªâbu8Fâ"~ áB.ã0b4VŒ|O6–"&ÊIÖëõÝù½Yÿ·=ÝAýÞÝ-Ûöã߆ІmCc8dDž3N_F¶8Z̓K[[²ÍÎ~Î8&3KŽ`àÝÞî=Ì<.TGáãå^¥‚ä‰Rä@åTnæÇdeiQc~‚j–G¶å7vKøUÛÂÅÑgµã_Ö\Nvá?øäúš,ŽæRpç &dyNäh®]!`kΦà9˜ýh…3ç™uÚµç)¦bƒ>Ä+nÕsžÂ†¶›U1_pçwf扮hXeiÎè‡yå<ØçKìfÿ'h(žcšíetg;EèÔShtŽP‡^•¦iÐSÆéS¾èFU5æhZexË?Jææ\.K6ÃÕÞ¤~Ï•FOvi¨†éuø`¬®hÞiT•Xdj ¾`^À´&肾ãAâ–IYèJ‹ëº‰QЛ®k"Vd+†ÞÛmN¾n`¿¦ã]æìp–Z¥^£rÆå·~\c–ëä)`ˆlÉâ»Æè¼NR±ÎìÞì£l“Fê mýbjõKì§Ò¨îZÕfíÖ&a­Þꈕm² W±¤í¢îìÛì ÖdªeÛžd/Pì\cl¾„e)`0Fdãá׆íåN`=„nÿí.ëi8kš XÝm5ìnÜan¹n_mg«¶ky¦ìÊÛËÓ±¦Nðƒƒï‘öfµÞíúmõËbýNTÔæ ÿÎé«&cN¨êò6czn¹øƒÔ®NIÖ@÷†ÓÅB 'Ý2´ÙÏlÞ®à²=l³n¸îÆFe~çCòOnåXöijIñZ\qºíwòÇýfg=ir©b¾}ñ¡niÓ~iåÅðÕŽl0ä'òg>ï#€$÷jójà°—jr¢¶V§lëÜÆí*ŸopBMè=æîwñòòmÅwøoЦj3Oep®N17ßh",TIööÙÿ:×òKhÒ¥nÆrYÃtÍŽ VÙ¶ýÖØ%Ò0?å«rSþp?âái|~ôYsVƒGJ—òª„ò·uÏ.é<púN¾?Àñ÷Nl@ A?uoûñgæ"þï!wuXôzÆ×j¦u~t¼*Ï>½t]×`<—b=¯î”®g_'ëZF¼ew†/>nòqFÇvŸÎvV`1ì o÷lpŸs]Öôìåt”žÚë¾ñûÎßaNö/hwU`x/ck÷Ͱ]óYosX¶uŠè9÷w¬í÷ìŽ_é&érïtëF÷pÏPQï&†oº~ø!FsôVsñtd½¬ôޝ<;öl_„p•6øÿ#Cx’hðQzéAOfR v—c#ùFþ¸;pKÇùtùq—oaßóŸådWˆ‡fwÆ>zTŸé¥wmyŸ÷xzšÇì“×U#‰rŽï(Æz¿ò’Çh?Rp½‡Yd{ÿòuH¥/ûˆÇk$Ÿy”{çõ(¨zøL·òSºûçÕ““üz•{fû1g3Nä—?{´—y‰Ý{½h'zÑ…ü¿®ûÉ?w¼O‘ט}Ù¯}Ú¿}Û¯ýø¶Ö†ûfOú‰Vú|hzôVû·÷$Ë/¼®Ë†œ'÷«‡Ú‘x¾ö¾ü˜VøÖUþžky0tB6t·i«öüD}ë'ýÿ_êºÿsn§Mý®?Ôè—áé·{ØoÍôÁ}ýÏýýïÿ„÷{@rÒŠÈz í¿¶hMv8‡é¨éÙ®mÊš¬ZÛõˆ ï%?ÈÀ‹Fâ#H*™Me X:«Ö+6[¥>ŸÑ¨‡«“ËV±Í«Íî÷úÚ~€§ð;>y4HAÀO]Ÿà a![žœ“Å¢ÆG¡É$€L Ž Ž%Ȩ̂ÍAÃNdÆŸ‡ÐQjQkk@ ¢,Ôk­í-nîílÞœoƯlÛ/°ðqÕ^¤©FTÀs4ô´t5õµu6ö¶u]ð£øGG¡IF f{gæËçgÎ()sªªj+ÝÖXaÿKcΔXùÂrh!/b›A<¦¬P¾„¤6rä Y¸q‹y€i%(@eŠçîÒ‹vôVˆê˜q¿#þÐpy%å[E8íæp"Ñ¢x$Ê¡¨ÑÅAj«ÊíªÕ¬Û>2d"rd¹sèF`w‚“¦Mób¬<;󆽎òá̹ªË¿ƒt†{:ŒP¿G5M*Ø Ó3N¿‰*h*_¹’ -Žèõk’ÄBYö³Ì·¢G׫i3Á‡ºv‡ìå'㥀c»iX80í¼®÷ÍMÆqÈX‡k-NüWa!1OМÁäXueC“®>:îéÔ«uâå¶oijÃk±ÿÍ7íÄ[*“¿\ŠðÉò7&·œ„9çÐÑ¥¬dý¿u¡Üƒj§lg^ºuáSè…7”ƒ±™·žaíéuaìYèÄ{<üP Ç7¢ˆ[iˆÈrøéÇYgdù Œoa—ª­– w¯5¸ayãñˆ¡?VÈ£zBùc‡~€â|MRß,)2·âFžQB]Œ1 (36Ú…ãn úø#… ‘™`kôFf‘j®yf’LEU‰$Úy”F݇Ÿš™Sågë`™%€3rÔåwåèJdæá™i¢©Ä›DÆÛ‘<ÆéÃ’a8ùiy¢xŸ~²ØYƒ i4ê“( ÊÚSÿ£‘–9­ é¨`mRZ)’Fð!U§ÍÔyìZÂkTðINI§2Ê$ª®J𡇂èeN`ê%&Ì2i®ƒWîHeºá¦¥k,¨ñ2 l/{ªøˆ´‚X Úµ×nÉå¶°²¶—.l°…äFª«›â*å+Bô²+,±?!{q²&žXïÏšÚQ:#XÛ¯Œ:Lf ·üxË(x¶þjæ™ ÿêpQ‹ª)Ž Œ¼=ó¼ñRöN-È©’Lè¿r¡,0à„ê²›0³i.…4e3ж;×ÎtfüõÅË‚$4fsDm Gg™­¶¯ ìtË.+3Õ»¢»[™VÕîœ?û¯ÿØ]u\*Ñg­6ŒI+]`Êýõ4ÔKý£Ìë Ök3|]{1èw®Ù_‘´)! Šxâ&ŸÌ8ÓpïùÜS_ZõÝ@¾|yE|{Ùß’nßàC?—ï´‡³Îê€Ú¢Öx*²™ëô”×Ýpî’JÎ;D¾‡:ø¢ã*ø½›É…¶YËèúë@ÏÝÀs_QíÖßn÷Â÷G=?A›öíwðœ î9Ëx@]êRʼn2°| Ø*€¹/v‹õÂ%·ëÍ,{“ÚÔüç¹Ï­„xšÜ¨Šg:ó/P.” ]ôBÆp4d߇  Îjõ³”þ:˜¿tfÿ]›+°zð?P€(äXùˆ¼ä}¦?V¬"¯¨Å,f±y§Ñá]â9VˆD<×ñf$þ!C„ 3À±©P$§›¢¾nhÃ=Î5ì# ½¨-0ÆJŒ¡:"©È\4h‘Ž|¤.8¸=HRò‘Aœd%3©Hp²“Á!›8Ÿ9’ÏxL ·¨ÊT²R‹‚ÜÈ90Cв–=Lã­4ˆK#Ù2xK$aƒ) R’®ŽãpÄ)OÈ?2S˼á+a™ Ò²—Ö4b{ŠqKûaêš=û%ð¼ù½ñ‘΀ÉäO+Ó¹Êu¦2š¢fmÄiM6 ®›º¤[å *p ³ŸÝÿxb½L‰Çi‘…64(:C„öG¡î\Æ4߯C}6‘žÅ´'@É£MŠ6é—¡äè3*c2™Õ;SªÎ•Nðu è$LcSJ‚´¢>TW†ÓšRpÎð'PB@’‚EŠòQÝ3ýèÌf*Õ†…(O£Ê‹/—"õÍF¥Ú6ŸjÕbW­ Q!Å“öa_*=+K«øTŒ€²«Z¥j”vúÕôÈÕ­„ðèOM¨×á• (Ûñ惶¦&µ°LuáZïÚV»ò®zÂ(9¹iUÆ~’2”…Wd£X£­žMë$«ØË’V“¦Ed]ÃpÚÕî"Ÿ¤íeö*Û«ô$›=§ÿ2«Û¥òÖ©û\ìk¥êXud¸Ò ®‡Ψþ°€ÅíI>+]–жbÈE®qe3Õ ^WIÊêl‡™Ó¥<—¬yTh ŠEôt¡î­®u»{Ùì×Ü•¯< @Ê67°c`;Ý·¾:Ãï|ɸ݅!˜S‘È+xƒÙ_<ü÷˜œ¬a{›aÂ’ÀÀ•m°]é;ÑßÁúý(c'|‡ ‹Ð.hc,]ÄoU0}J,ß3¼/8X\Ò Uû将i [ßXnÅÕqwy¼ÜÆùqBÎ ‘;+ã-ŸUÉJ.“mÉÚ1ߢ¸d³¥aó…wQ¸r~²ÿ<¤yÃvîð7Áæ=ó¹Ï£}ppÛÌÀ¹9r–ŒY¹¬hWæÙÏŽ~4¤ãÛ5ÿ¸Êñ$4Xþä$ô¹7½n¯§=íå/GºÔ¦†4QâmöB…v±yQµèY3ºÑ§¾5®œjûx¶‚Žˆ®lRPÑÙÈÆF²omëe3›²¢O 1¨Ý {¬Ä&0¶»è³T7»ÛÞ ·yMi°Õ*J`ñ°‰}ì;¯{ÔKþ6¼ãÝËpG›Õõ¾Yø©Dg{Àî&µ¼.ðmë¹Çã›´GÌMï›ÝÈvøgþÜS¼âòᶪÝ*¿¿âņ£5ÈS"ñ‰[¼ä&—4 {Ôÿ1N[ {cžÐ™7”æ÷eÁO®óoc|Ê5Ý8qûÀð}÷;äe¹’v®tcÜà*'7Ë–žûáu¾:•½ô­ ¼é>i8ŵܗW©èýFzÒ¹®vy{ýà{ {«…NöÔ­ëV¸s¾ö½‡ùÙÅ­wÂËι£Òì´F{Úù®ø[7Ýén/Üë;uªCÜîF¼œô¾øÍ#·ñ_çèry5xÂÃØðþn¢ß9¯ú>{þñ& ½ä Gy™×¼öëµ¹zñŒzͯ¾÷Rm|Æ» ûjö€ô¥7:¶1Ÿyß3_×¼÷ÓÁ7å>Ö€²<öy«üå7¿û>à£>†ÑÿßÈçòöìýõ;øÐw=äŸti¹—¿îöxúóË{öóçòùæ£>Ò£<öãúE;opensips-2.2.2/modules/seas/doc/images/image060.gif000066400000000000000000000001111300170765700217700ustar00rootroot00000000000000GIF89a;w1!þSoftware: Microsoft Office!ù,€D;opensips-2.2.2/modules/seas/doc/images/image062.gif000066400000000000000000000334121300170765700220040ustar00rootroot00000000000000GIF89a6sw1!þSoftware: Microsoft Office!ù, k†   333<<<%%%??? +++222(((000888,,,&&&QQQ@@@XXXHHHEEEWWWPPPSSSJJJLLLVVVZZZAAAccctttrrrgggyyyjjjkkk{{{```xxxppp………”””ƒƒƒŸŸŸ¿¿¿£££±±±¯¯¯···¥¥¥¹¹¹§§§³³³ÌÌÌÙÙÙÏÏÏßßß×××ÇÇÇóóóæææ÷÷÷çççïïïÿÿÿÿ€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜‹›œŸž¡ £¢¥¤§ž™ª«¬­®¯°±²³´µ¶—º»¼½¾¿ÀÁº·ÆÇÈÉÊËÌÍή\ÒÓÔÕÖרÙÚÒ5ÏÞßàáâãäËÑÛèéêÖ5›åïðñòóôÉçÔQQëÔRRÚWþÖu«G° Áƒ Þ“ÖÁCC$û¸xðp .A. t—°£Ç C®ZÅ.6„pA"—$A’H ÒRZ’‰.YÊDá •X„9)DJLv"“*]Ê4äB. ;¨TâáFO%‚xH‚âFW ±q‰‡¯{~•hÃÅ(ÿ´ê£Ö®©ÝY†éÝË·¯_î ~ôt¥Up¿Þxi5- ™R<(brmص_oJÛšUlµƒC«2±´éÓØ ˆ^}h¡Ù°7àÚˆ’D Y)Y¥(¹ ŠÕ ivX©s³›“tîL—#ëçH£žN}jèÏŸF ĬÕâUÏ¢èjrëL‘Âô t8pàž™O½þ"éÕóë¯vÝ~èÂù„E'q…Xø“ÏIsI£Ï€ƒsI–…Ÿ9ç߆ƒà·U P`:ZMäAƒè !“iýqhWaûUGŸ‹z¨NdQ\ÁÒ X`>3uOŽ:ø£ -Æ"ÿwÁãtu1Y£i1p’QõÐ ãQ¤MAD†&a©„D(4zKJÉ”“Ož6£›ØÙ¨±„BÆf Y_¡PFÁ•P‚ö9܉dâÄi-ÒéTœûE)i¥E¡Ql_‚)Un±¡·Ö4ÊmÕé£'J…|Ez©GÔ ë¬´Öj뭸檫¬)¼ZŸèiƒV7™„ 7E•bTuMaMÄ…±Èªª*\a•檯!ðÁ·à†+î¸ä–kî¹ß‚ !·ƒ,M*­´çLB$¡4ɘa q¦¼ñqÙÅÔ*»ÁIiDs"|—» GœÍ¶×£°ÿÄéXZñÃw¼ ÅËs±ÇÙ4²R“Œ1È'¿3²Êì¬Û²H)ü0Ë3ó²ÍóåÜT»-ôÐD]C>Ó³3Ï\hœ4H  +õÔTWmõ <-2Ó꘬5B5s½Î_;³4ÏN— ¶Ø“­¶9loãõÛõ„7unÓÌÙ6§-zËc÷ݨåø-|ÃÜp„p8<ƒÞæãÏ$®²Óà… 2SþLä’ì¹3ˆ`ú騧®úꬷîºéÂÒ¨0ú8 ‡nÝíÍ@0ÄïÀ/üðÄoüñ¿¯àŽDPc;ïàä®{:†CÍ5R hÍ…f;ÍI#Ê €ÿ,hQÍóÖ>ý“Õ§?’5]}ep5VÚ …6¸4 …_ZXÂ5Z€ð€L ÈÀ:ð,à†¤·>m´Ï}™8‡FŽ2­GákE0¹A„p…® á$‚Kø‡šn8€ Ö°)fˆŠÒð†6Ì!Ea V05<²'¤V (á Žµª&. üS¡F¸0EÓÔ…|æ›úžÓ9Ñøð‡Ö¸`-Á¡œÅ"Õ¨×f’ø’&Z *pCæÇ°A€yZ¬OCC€×ùñ€ ¤ E@€1"N›Z…¯̆ Bp£>ö´9B…ŽûÐfÇÎagÿƒ‘òFIÊRšò”C€!mqƒÅ9ΰ,‚IN‹MS¬bi–9èÑ?6º‚÷¸p…!åèI˜¼Æ0E·ÊYÀhAÓ8P¤éïi$ 2Á å$³ò“ÀLc¼¤Á’ñÔ$?‘´Í6ø§"H5“–³nþÅ…Å Jt¥`¤£Ÿ@r(3yÔKTˆ¯¡\á'ñ‚ D Z FRñWP K ‚£@FIºÜÝ;eOk„/,þÛ¥>¹L"æLIðM@7S!)!‰’‚gQ—^JP²RÚFÁÅIbþÞˆí©CŒ% çöËš±%ùÊÍJ%Ò#™žh8«ÿÂBn°P«juVZ•ˆ —GÑd¬ç>šÔFt´c~óÏHíÂÏ€úÃ,'¡Éf’£«,Å|L)rͰÂEAÃ2kF 4/mµ€ ÁDPÌg8¹0qUŽz¥ªV£ ¬'‰°’V¨˜™A„§]Õ¾0B—Ü ™è`ëc10‚/p#* ´l®M fX€$ a:hš â¬4ü‘=ð(Bsч?|D̰S›ˆtçn“á“ ,bYˆÁ"$ ƒ,øödÈeÊ=&0{‰w¼Æ4d  ÐN P¶°ó†,¾K™¯Ç®Râ×È¢ÿ2€ è`E."š0 þx³`ÄÛƒmA#PÈ@ï4² aCX’ þp×€ÀEP«" ÍzÔÓ"…á€!ü[%‚T¨†Œ[1£Ì&è«@o Ö8QdeyT;sëãYT  Ö¨‚qak™(Â5Îì0''¥®Óº±•ÆSÉ$ ´8AmWȺçñ\„K8Eâx^2™‡2\À OPR Œ^C·]fD™­¡&@ÅÖ˜Âìˆéjˆ¹ÉÀ$Kˆ’pƒ3 6¤Í‡CÒd”JÂÆ"a!õ™‚ ‘ìñï$r\T´ ÷`™z‘†E˜à“b› „†ÿ`lŸdqƒx³›]l€zÆT ¯´2žã„U„`}hWË9Ûf«L“J¡R’½^y1S—ƒÝŠM˜×¼ `À½0æ};à°¬à}ozßÉF˜›i& :tXl¼­”©¡×±Úo¬‚šÊ±6“Ózò‰!T¥êX}­íÑûǘëÂ9´r؉K)‘ †•hܵ Íu.dñ+@}(ÿtt\Dç±_É-xrYŒtàŒh¹\Al“3)}zxzÉŠLÞôX<ÝR·OØ;b§±Ðsz$輻ƒ}fcOˆ‚CÌH³=n:Ü©žŽì ià³™Ýïnˆÿ¼ëý¸|×F$Íè•ejƒÛ$Ùà _Ã+"îÐÁüAàü+A&'Ù3»‰é’z±$ …$E·Þ6ʳÂò—ß{Mz&f…ÊšÂÓŠä G)ÄÆHDbNoúk•MÞõ‚€="4ÏæÄOµÈIXò,;_i.ö»øLn"8îÕøÈW…ò—/û_i[é0RîETÞ#KPÉ+Ôoèi\YòáÊߎxlDæh1ó‡/JÂ{¡Ž„TT/ÑK0s|®7~‡à|¢AuS AZµÖ B ÑS8ؤu˜™ •W~˜B /•ê zl„ ‹y­g‚–€‚)ØÿæçQ4¦ŽWƒ6X úuš ‚Ð1w!”‡ƒ„`ƒá„ó95†ãQei„[Õ°cÁ>Axƒ¯g„Ï!¬‡ çqÙ@TÛe+1†"Ö…“À„v†¬gZeNš²gpAZÒP‚Ö_EEoägÓÒmX h|KWJ¸„‡·:X'¢ö¥F[`QO©VpÑRPQõô~'4,¾1k\åŸÕkÞ—mcã†o般(c;htÂmiÂùnÓåga‚,´¨«"sæôn¨8zú±ˆ„7„…wŒñ §§—jGEú@siRTGÁHlq¸vŠŽV8ª( p˜ÿ|r¸/7sE1sòR7Mm1/z5(>g ïXm‘tñ÷ŽÇ؈þ¸1Ê9Õ`_W·¶Ñ`Ûu#˜ŠüØÿ{x„Ö`v¦1‰¦‘v\Ø ‘­‰Gw̤‘EøxA&i’ï =?˜Î…CŒ9s’4‰’$ *9 9I‘IzüZ ·†#“3 ;¹Š‘”âÀy0ŸP]ñ“äT)uNŒ¤WÀ¯u¾—ˆ1b”9L eÙv%ÕDµ7·I"²DTéS@µU«‚þrU¤%$³ATh˜‘u–I€ù…Nõ%Ò'/Õw‰j?^!ÿoB"G"tzuVŒe…ØTƒÉå™ú‡m~_‘ì'û“R>á˜u‰šB[´…V'b9–áÀ™#Ù´É Òñq‘臯¥)ð2šRKìv,V¡)B¡Xçt[7ƒ™ñp›x—’|—5‘=Ñd 2-<òœÕ1 Îõ],¤ŠØVÐI~ïpžBÈw-Hžé,Ér†µwv~ÙLê9H)ØvD$Ã`1ižù™žú¹ƒ ¹«tŸ… ¶ù‘Ø0…7f™Ó0(èàpUh6÷Ù0÷r/ü ú¡ Šml8‘¤i†dhT× ™,døBŸÕ‹õԡܲC:T£ ÿA4š£9d– šj t(gwXg?ɇ„(Gq¡‚؇…oA p<¡‰‚È6°ˆ8 ±ù)`4`Z+<ÐaZ¦qÅ‘‚9 È’hj_jaµjšÈ‰\ÅUÚd%“¸t:¦XEÄV·æ} ¨š3¡?uç+PŠö£M¨¦‰Š¼(‹ÞV‹ â‹qa\5gB¥¿( •¥ÙöFB n!’&F4EBy¨¾¢¨‹jNæ¨ä¸”›ÕŒ(‘Ðh%ŠHÕø(×h%ÙXª½:ri% 25ªëw&T†¤ÙöHa¨]j›®j 0¡g/Æj¨ô“-çă*«âØÿ£!«µpŽ1§Ž5׎R¬)ñùSÃbd1æ4”ƒÒC×Êúñ¢KÑÚÓJqgëP(a—Ù*IŽöq)®< ¢>Êw‰…Ùp<ó4ø¯ã°X&"øÂYãáXÑSõ4²ÖBv‰(6÷[•*3g ·ìñ°|Ò²‰2ÕÐ¨Ï ©a8‘AWi3H@aÄ-;] Áè°àÑ‹Ap&&Rbš[“Ñd¢U38°:’¨Ø«âzäJ HF«¶I;-œ½Á°<\ÑZåR›RBŽbí¦*±hò±Yqÿ#Ör <›¶±¶xñ» ”{¹•‹¹ßô*qëHr"è·bÒ,é¸(ú`«Ò}¡éÕUAœ1ƒÅáݧ²©’*Ž®;®'ccöc`t“¨×°*Á?¿QIX¢Uê!&og…7å=qdQK\â«!·‹¢\𸻫¶'ãVÐb³àVàªô6qûªè€¶ã¹!3`\À¾µà\°ð6Ä ¿!¾›é³¾r¶¿ý; öËM@7ï ÀÙ ¿L«óbM;±`ÀPª6ÿ Áê ÀiJÁƒdÕÐ^÷b\@€S6ìÁÖ Á!¢£fÕ ÿ°@ÂÕa_0H>üÃ@Ì:±ó[’ë F :ì 4\ Y0e³¨4ÅT\ÅÅÃdIÀt¢¾ØðÄ®ÄÔp¾ZÓÁ.¼ œÅ"ü*Ô ð@_ÓÂe< g,G\›y uLdÇgKÄ,Ãͧ£¦` yLpÌÇs<›Z,y\Èô°Ç|üÂ~ÛyõàÈôpÈqœÈ8¹ÈÉxɘüœš\Æœü ¡œƒ$õÈ6ºÊ:*£ìÂ¥ì c'Ȭ\Ë´|Ë5z §LÈÉøÊËÏv!`¦Ä\+9PÌÈ\.°Â¼Œ¨ƒáË ÌÎ u ‘œGÀÌxÌ.»œrÐ ÿÀÒÜ agÍ×LÙÌ 5™Îê¼ÎD¿®\Î} kKÍäÜ µµߊËtÓ˜Ï× 0Æ ¡Óg­èÍdð\ çlÄÉÈÎýÐòܳ—PÏ×àÈÂëÀ¸M÷‡°Ø*dòÑ:øƒÐ = GàÎwñÍð_’KÏ+ª‡Ìü³*Ë!P'¢© XQj—@}&Êqœ8m8Í? ÁHQtX(¡4h )½ÒÔ°ÐzÜËëp`¢³Û •K)áÌ ã¬ ""VÛ"ÄYñ~a»ª¶Bwé(%!×Xû·ú°~r?õZ`‘cë³ÑÚPÕ¸1ÿ^ Ýu+V'’,ÕÂÑÓ0kd\R$¨2žž  ^íêPÕ&PBQV¥÷ÐÃPÆzN{¢/0]MQã$ñ­*dÿ²ããÙ¤réðß÷Ïþàžq2ÚO>íQÎðÛàð¡~來ÕÔt#j ¥Z|2h´Ðíhl ;ÏóÙàóVÍò°oVÏoXõZŸõ\¿õû–àõ]?öb_ö\/¼²Ó¯Šó+Ï Äp÷r¿:ðoþóÚœrÔ¶÷|ß÷~ß÷Nð÷‚?ø‚…œÿÐ aGVÜøŽÿøÄ3ÎðTŸrP¿Èè„¿ùœßùžïl_ÄÇøô—OÕÃýŸŸúª¯ú?÷QWú逌0û´_û¶û¸Ÿûº¿û¼ßû¶ïúiL ¤û~0ÈŸüÊ¿üÌßüÎÿüÐýÒŸü–vŒûÚúx1rP ˆ6ÏÞ“ïÞö£Oün®ÒSü—7³?EÒŠÇm½lîÏØ«Šmþø>Ï€ 8HXh8¨Â¥¸ÈØèø)9IYéxÄ€@eÉÙéùÉut8: hÓ‘$Å•äá‘Ä…òªÄÕÑáQËåÄåÑâê"{Œ%ÿÜ5\<|à…$…Û!µkCl%uSŒäÁu¬ìq4½@*?O_oŸ¯¯ßï_úOP€D  )úsžUëÅc%ïÅßkVшc%-®ÈcŠ;öxÈõ¼˜c‘ŠÌh$jgÁ–d“â‰bHIå”VV‰å•ZfÉ¥–l f—b†I¦”Ì#$šD:#’l~e£=½YdšPVEzîÉgŸ~þ h {B!h¡ÿ†ªç  ©Eºé(CKÞ)ŽvÞ)Õ@•Òg£j¨¢ŽJj©¦žŠjªª®jê m*é¢L 㥘Fµ&­©É Z£º¶é¯Lj¬‹l²Ê*[À­äiJ‰·ô‰·tP—$>ò$µLˆP§†Ød®Âz¥5¬Ën»î¾ o¼òÎKo½ìºðZç¶F®³ý˜«ˆEÌ-Ç bDZ3 aŒ4¥#zq!±2Šë¾¬a²A lÌqÇ rÈ"LrÉ0ŸÅ¨õë/lsa8%¢#6È(sKw ï%‹7ˆá‹LwàMHlsO°Ô `Aw3 kÿ¼ŽÂ²V/«\P°X?4u=snýUÕ-Ûsµ´¯ Ô8X¼d\K/¹#T†# Ø‹afQédL^“iwSSL9¥ÄO2v=®¯`£+Ö⮥ì¸Cb­&%Lbt\H¬ó-zµ.oµÓ°LQ”ÃÒ\A¶ÈÃ9•~f‡Sü´‘Çúê툋¦î MN¹²P6öVk½f[‹Ú‚6" ßúˆW°’$´°©E„L/¡ØÚê¶Ÿ³.À"„+P ­º-­ O»Ûäªõ¶Ží­îøÉèJ÷“?È#•‹]L9–™y„k!ÑÒ–:5µxÀ.Ï‹Þôª7½¨æu³ _mÆÛE$B‹Ï¼ˆ—¶ºEìíߣ窭H¨Äª'1•Ú—{AXwr ³ ÄB µ jÄN1^Ô×°‡?€+¨#IˆÎM”ßÞ hßP‡-®—Ù<‡¬„Åo“œ‹)ÿF®Ãî1VP=^x¯¥ÕSi_ƒÿj޳ö§aÿùªvʱÁjƒ¸µÝ0yìã,Æ¥Ÿ-ðQNW½ÆØ‚KæP‰M ïͬßuŽï¤cÇaYËtNÙG¤—÷§C}µ¯’«¾1DŠš“ôf YyŒs®³£åLj+¬¢Á0‹‚xYÁŠ;+" J¤ˆ+Èfôín㼸F?zÕMdeSG–”Ü`¿oJtíEU³z×¥x­¿R¢_›º¿Ìe„®y½ëùþÓÖòE5ØŽìU+ÛDÝE\Bĉ±Ö§.ö" mGÇuX`¨3³bÂbÕ$‹Èt¥Ó‹îd˜±¸v"¸ÃMç«n.Ïx.ôsLr!ÿYôµ¯ëwçÍØØká o¸ÃÞ.äßá@N‰a”m¬äË+«qTŒšI+  6P") ÑÉm§ËÝùò˜Ã|æ2oç{)í«å¡F–0X:¢ ,F ¹^6gÖîçôfzÅÓ¢÷¥Äç [,€Ž Åp§%J‚¬“0ØC‰ÙNpÙ‘íX¤¦{%‚¨ ¤Ž: Þ‰Õó"1u/ýìT%;ß}iC!DcçŸt~»ˆ”PšÊ·jã¥íˆ($aå–‚Z¨M’}>Þñ›¯ó´é÷ÎÿXôùþÄq‰ÌSå0£[Þ'Ô{J" ’6HèI?•Ûãÿ¾eió#D#"voÔˆ…”‰ÅÕ ²m–à=8by’g{›ØO¦H^Þö$¨hJ5Gs¯èб‹îÄŠždÓÕIN@(¸ÈIŒ(` à'Œò’ÃhŒîµèÀŒSÒŒÌH%Ï(ÑHÎXÐh`Ó˜ØèÜø×ŽÐMCÈG Š\ ¾¨ŒRÁ2ÐŽ·‚Žª¨ñHUPZö$ç +!{ÅwB@y °_såkk–°ŽühS: F@¹ÿ"þè0¼p ° ¯àÖ0é)@ P©Q ‹Àìx†Ùnò¶µ?µv$;Ò@ÛÂY¹1!’gÂ3¸0A@£‚a Ë1’”…“GÁ3Ð&9P \PR"•è1 ç[Üá[ˆ± >Ô Ùö8_”Ʊd+!7 â£Ò Ôð?"\Ì ”ܱ•YùM Ž €I.É-5µ ³ [oa€Q–%fm1 €Å¡Äà_I ³’U(´?û£¶`-~•,i˜‡ZàY­ùˆiV;Ai¦ kck(ÿ–O !Ͱ /Á ûqF1kS8Í£Óq0ÿ£G6ý±̳o¬ó%I›`d€XÙXa››“Õ˜¶p ‘y ;ñÊàuÀ x¯À ‹¡‚@q Æ™nÈG“Iö æÓÅp óQµ¬9ž°ó"ž J[ñ&Ü ¡§hs*åÉàR¡ :ž¥¢¡œÁ¡ÿd¡#JZï„¢UQ¢×•+š¢0êÊ\'*£·v£ñ =ê£? ¤A*¤CJ¤Ej¤GФAÚ ÚLº‰ 0C ¥SJ¥Uj¥WŠ¥Yª¥[Ê¥]j¥$£wâ¤OJ£·õ¢a*ÿ_hŠe*S6ª¦f÷¦SÑ¢‚t¦q §vúsêFnЧ Ö§¡§hT§ê§„Úlj¢†ú,Šª:FƒÊ¨£©öà¨^ħ“š)˜J©ˆê¢cj˜žÚˆœJ§šÊ ªˆ•êDJªaªeÈ?À‹±*«‚Bü¸Ý8Žâ¨«¸º«áÈ«Î(à«Ãš«¿J¬½Z¬*}@ëå¬Ï ­çõ­Z[Ð5«×ª'PàØÊ­Hp(ª{jõ–ŽÁˆª%B‰QW•@{ÌG'ªº‰ÎV®’p®÷g‘ ¦-yÚ ³æY´r©™H®•€ Ò Rd ײ’€xæŠÿ®èH|Š¡“Å7®°q±€ÔS*Ó®Ž"¯™gÑ Éѯ^ñ §7²ÃײÛG0pn'>¿€KŃ”¥H傪`w¼A-»)Ûˆ; ¨  ˜† ²F»0ÄðWæ0 ±°žµµ6à I9 ç «@ ìfã Š$— ÷Zç(yàP‘QRB•P(p-q Á@7º` yçƒGK­©…jÝЙ¨)œ#=¬Õ,˜ÉGãák3>ƒiÓÓ ® D#œØ23Ûn[}þX±Ú)q9<(y_&a…)±9);®q¦‘ÿ „ )‹Þ ¦³¶oQQk¸7°9³puÈ :¨°NÑ»ÒâÂñ¦+}¶ v_{‡X‹dYEYû1oaˆ£+%Ûˆq&´! 5!Ü!8â¹Náʼn½ úë?ÌC>¹0 åÐ 5¶´7+¿ãs±“½àºZŸÆ¶©'1H…Uœ$H«ˆJ»´""5ã—ö1=ð'-Â{%Ø 7H4½É? { ‚ ŠéNˆ ©?^¡½»g›ãá2ŠˆX)꫈ôš#Ö‚bƒU);Œ{¨ŠD|† +Þ³†´âĤÅŸ¦¯]¬¯XcÄgˆÄõúYLÿzẄy1\`1µ5R\†TLÆŸ`ƇºÔò`ñ»å`T&‰–6a\†c<ÇŒPÇ›·^Gf׳u2ÂUw#V±`ÆãšH—ŒÉ™¬É¬ò%øj³s8B4Nf·,Px”û+‚<„„\ÈŠpÈ›G±½‡©¤ÈƒÄ Æ»y¡ÊºÇC(ǯl ±Üxùz ’•dJD/vd%-Á§pùƃ‹Z®üÊÆüw¶¹ @±iw&WlžVÁœ»ÄÌÚÌw¶ ²D< ·ÁZlÍŒ…Í…¬Î|W¦) ù8Á„ÌΓpÏe·Å0ÄÊAXÏs<Ðe·É íЪrÿÎ] ½ªašÐdLÑ-£-ÑÑ £]¯ Ò#ÊÑí$]Òj­ÝêÒ/=]°ÒŒZ^ÑjÓ7Ó9­Kî5Ó†*ÒåªÒ=ݤ<”iQƒb`AYj*Ü-Ì©Ï:,Ô> u“€Rá! LÜry¿\ ÛÕÈçÐA=ÕŸJRÂý‘RpÙ™ÒQ EA náúRC‘ÔÚ»]( ¶w3C×`u·ßc×q‘UQ7ØÏÑÂQ~Q]ºgý§*0Äv%Ò\©Ã弅 ̦ÐRoÇTª›d… +³+…‚­ùQ"^G“Ü»Ø03å`Ùv”1 ÿf ÙR -Ò0\R4…F· ⇋@9Åsû %U{ ä‚ Á uVýZ‚Ö’hÚjŒ®{”x¼ÝÛ&IÒ6Q }"¸R X« ´ûµ˜‘³¥- V¸Vs gµsÿaW Æ;—ƒ.……xÇä]ÞüxF%E5nØe;á@ÈY7˜q%–|« Ræ€bL˜Ô4’ xÚG¡ÂGá‹!5 j3Ö­Áà në1k6Á¸iÁí±EQÖ6®¦b$ÔŒ(&ÞF"jî<ÞB®¦?Ž5î䬨´J¯Án !OÈW. â|R>å¨ÈÿC¸YÓ¬Õ adS†OÙ|×W-zÂäÅ<æ7úÛ‹`O/Q`ò´Õ0·qÝËtCdNiy ~?4 ”ø`k¡®¢v={þP¯7xŠö †+q`½<ãt\ç7ª«Ð¾10Ûm£à¦ª¼EE_R‚àE áXÜÜ[ªözBôξ~¢ª(AƒÖÄmiB_\ˆÚ#^Îæoù4rÊÑI¬ÈHMÃurq£ ÛòÚl·½ÍQ!ކÚ#Ô~‚Å'éé‰õ³õ\±2ôPVbñ+) bø žÿT’+µ rj¼²ò ])© (‡fRì)À$Áí=‡ÿÃH¼ŽÏúTÊn·îúÊ5Î7™*¦‚EK…„âjxûîRäEª½÷nî,¶óîHŠº;‚»#WI!bWÓ’iºóþ!òœ _¨òÌY£öëä—?#† Æá5c6¼þûý)!¥ITšoÿý/Æÿ`œïïzRè_ýðGÀCÿK 94>:ðŽÑa®gß}©P[·î 1&ñ2“TfšatÐmD‘Ÿu¨X%`9áÃuöF @Tr ‹L«—¡(W{£µ†$k-Í[;A»ZuúAÊQH4³âÙlgwk%KzÂaqFÍÿÅ‘»4ç¼Á™-æ )86#I„åÜ4XTà6·¼Í.ì0„:xßîèV¿6þRQ’2ø&–¨jsà[>1°†ùSá {ø:ܰˆ¯úáW(ÃרÄ#bÖ`jH) 1BMLã)hI“1Ñí`¹b÷íøÊÔ’d<Ã×øÈ‰ñm;ç6šA…‰n”‚Ër Õ—ˆt”ÈE²–Ýc”5 "2›nTÞ‹ä†fúSqÚ„å,oùÍ×ñ­¼†‘9¨Ì*VK‰hæ(˜ÿ)¶YF†³ é¡ j[¹ÑSHfHº(ú$])ןèA[,Þ¤ øi>Áéó^0å¤IïҗNµ:ôg«^¥gŽÑÀ›L hUÛš(ŒÒ~P(?Z×úÖÀöIˆ}=iTûØõ¸Ï3Z|(G,Äf ²§-¡(…÷¸0pBk Ñ®Ñæ0µÇýß¾Õ>BTXmUUQL=Bt$$ ñ§˜…»AÆ&7°ò®¿R–‚‰¹{7̼*8°ÒP·ï-n};*e£MêcJf2 1Z¢D`Ñ"/dø~òýðToUºÁ%[#Ë”eù‚d²¸3¢ð±ÿYä$.¹ÎÃÁÝb|wLÔÑ«š)&©‚q€”/œ|çP÷Æ‚/É¡^:ýéQϺ6ø Pfóg8ùzε¾óa‹½À$'»–Q|vܦ]í56{Š£‚ÙÁ8Sj’pÛ) w¨óÒ8ÁKõ£ å-ï"œ€Š}?¸w÷]ç[]òCšŒSàkíN&öÉÏ©ƒ¥n< _r5z÷Ë1£Å™…Y§Û ±@–%AB Ñæí¤÷ðÉWAg=$"¼áÍ™”ô¡cÙ›–æ²ýès¯ï®¢T‰6Ä¢÷–áDøG–3lˆ·ˆƒ*ù̧¶5 Ns¤P)A“4ºfôiNK(ë…7¿_ÆðOC­=ƒØcú·Ôþû–kýÇ!A3€þIHmlw€E–€Ó8Xx˜¸Ø8h?;opensips-2.2.2/modules/seas/doc/images/image064.gif000066400000000000000000000372761300170765700220220ustar00rootroot00000000000000GIF89aŸþw1!þSoftware: Microsoft Office!ù, …ð†     333<<<%%%???,,,!!!:::((($$$&&&222 ***000888+++)))QQQ^^^LLLTTT@@@HHHPPPZZZXXXccctttwwwvvvhhhooo```}}}kkk{{{ppp………”””•••“““›››ŸŸŸ———‹‹‹£££±±±¿¿¿¥¥¥µµµ¶¶¶­­­§§§³³³¹¹¹ÌÌÌÙÙÙßßßÏÏÏÇÇÇ×××óóóæææ÷÷÷ïïïçççÿÿÿÿ€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ‚£¤¥¦§¨©ª«¬­®¯¬ ´µ¶·¸¹™c½¾¿ÀÁÂÃÄÅÆÇÈÉÆºÍÎÏÐÑ–¼ÊÕÖרÙųÒÞßàáµÔ×CDcX.ÚÇ^7.ñYc7Ní¿._ØÌâýþÿ‘³öÁÅ1<tïɘ'ñÐe‡£ƒóˆ¸BÊ7æ}8øÑÉ—‡Œ™è¢¢—! UB!2Ä…C(ðÐ9ù[ÀŸ@ƒF¨, J…C>@17“gA"]x€é‹"hzðò$”xXàaáù ë—'<ŸÏËŸÿ3ýûûøñó£Ï¿ÿ7ûù(à5ñùgภ6èàƒF(á„Vhá…€à†~r †(âˆ$–hâ‰(¦¨âŠ,¦ˆ€lÆ(£$hãÃì7ãŽ<Þ†ã@ S`DYcHæ§cÿ‘Lîxd’Pª7d“Trød”XîSå–^™å—ðÁÈå˜ôy æ™Ä,Iæšæ™‰æ›¿LÉæœ³%Åxæ©çž|öé矀*è €~@硵! À¢Œ6êè£F*餔Vj饔. &¢œÖç0^|÷iœ–5óÓ\8ç>È´ä*¬Â„Ãw$:†œ¦öú_/‰YäjO´zÃN„,\wûÅDCh–× P,ëcƶuƒ²P0ÖËRºîêë¸â°JCdá°ê¨ÅÚgµZBî& Õ7¨¬¼‘åXp½°ã¾°£+¯ä&œ 5Dˆj^Êy•b^,kQÿÃ÷âpú"D°/882Àe¡Y/…«¦Â,ߘgg!s/Xx‘ê/2§ú§z³/Xd¡O¨(gá…Á8ü 'Â-7 X0KU¦Ì6‡+®ÓXÓâ¦Õ_2õ×”lÍu/úЊãÊ`§= ªÈ¬*«9fóÕt¾ ñq:*]ã„GIéµÚ€/BͲòz@¬³ÉªûñµÅq,´ÒäÑV[×êÀÐÞ®äÑF+Wp§EkR®Ÿøé4žÛªºìzP/¼™7y½7Ü›ïºÔMµmj½Ø=0Þú~áz±Y8¾=š»V°ß›¢îü! ;¬Ö&cüÅËe}±;=Å>QÿO`l7î6}¡1Ç_¸ðÎ_AQ°~?o¿eñŒ{Î4W ÿÍáÊÆà³Íƒg>£ÚJ€Æš}lg‹ûŽ·~ô·û.0Q‹ÛØ|q“ ¡­ͳàœÄ¶Á$UP„j#a:HÀïdg \ Ï48 ¥Ge! É=_탗MÈ·.€yÀ úsþ^˜·‘]¾¸K:æ÷©îðk¨Ê f“º(ÆnÔ*–’Å‘í¨d‹ŽÂDZE01Á3!Á—bÔ“¨Œ†]»¢³¨$v,1PË /è8¦xgôc/ S=xáK#Ÿ¹›BØÑ1êÜíMVÔ£ÓØf‘VQ&ÿYøzB^ä'/vìwQÔ\Mdf˜k1Ä y åÀƒ_|oiš¼_`¨õ4•o7Щ¢s‰Ea°MóÀ#˜>HÌb–k™lf¶*‡ ¬[¢úEpdW Q9Ab—3¥¹É^„„lûVºr«W̉»I/ÈR±/ ‹úÈÂLÄøK¸åº±xŒ«%ý0LãRuyšÁ x·¢,†A›ÇÍÆlù&øj©5p”àûæÿ:ïËäRaï“a¬áÙWzö…¨.9Ü÷@+g’ñ¸þ èÞd3†+1ÛÝže7uä-)Aô “¬Ôìm]ÊùŠISœ6R‹&a9Z³är=XCwr<¥-—Éh:ÛT§F"x¶ÿ”®…wþ$K¥‹-áEPpŽeä·»h-ÚÁrù* c(õÙiÇîÔƒQZÙá @¶CL [¥(u‚fŽ¥|¥z9ÈÒÜó¢on9KŠöUºÕ®…EiA‡gH]Ü$[í˜ÁHf¢N&Ž#*Ð{–R©÷hPm“dºÙõÜŠ¦„÷Ê㇢w¿ÎxÎCŒö] „F5ϪÇQóg¬:t”íÜ¢½³*+njÔ 5ÌBáe_ud¤€»µðg46„ŠIÜ7»˜áüIÓö 3Î)îCRæÍŸé@ßù®l7}®2Xkù]Å:Ɖæ¼å#·`SoðÔÿ‰¼[h61'ÊHr-ÇU&#/¬GEÒnz$.epŽÁ²³4˜oÉó#D+èl„JdxëíKG»©CÞ5ÐSÉõk9ëµqn¾Ê^ðI&’ÍaÀÒd³W0ì™4ü1ôcëºs>›8÷4:Ÿ§i:ÁÙ×¾eÅßXKøàŽTo„ݶ¦CÒ7¦Œù2²õ³ß#öWIæÉÜ ÔC0,R%H³w:øv¼ô7§Üuv}qu{½°ßVÓQÁ4HH6ûCÀ|v†:îG%â%X,§ ør3H4FgF€x68#%È$²' 7‚ xg¶‚´T Q},Ø .g*'hÿ$PDbÑ¢/-æU Vb^+øø]Låt¥£~g8ÌÖ-ÎouShÁwƒ^¦GJfyauÇòL–£‘2ß“5¨`§KÆÔ3ô@ÇÄBà¢>e(|ª·zøñ¹‡fÈt$‡‡6b…’ƒDÒ0ˆ„Xˆ†xˆˆèˆ¸ˆŒXˆð‰ˆ’X‰„ØgèQE ›0/@šðF ‰Žˆ,£G0Z ™`00 °cbŠ‹€ŠäB:à [@—aÐ `¢h \à G ‹ŠÀ‹ãB/ P •PÅè `@• R ;àŒ‰ÿ¦’0 S“ðZ aP”€ÂÀâhäX*‹Ã€ Ðà† I@ 1P…pˆ`¥p¦ð ¦‘¤  p‘¥€‚ œrSƒ˜S'Iˆ–p’šÀ’"i/¹‹xÆh$¹1¹‹9Ä 5i“7‰.‰ 4Ù“ƒ°“8˜“›@”Æ&”‡ð“—”J‰”h”-©”#I• 9“RÙ“PY’Y ”V¹•‡Â”+Ù•1 –aI–cI•f9'b Bh)’kÉ–oI qùyVImI—sYu¹&N9•jy—C‰•:i‰†y˜Œx…‚Y•:Ùÿ!}© É%y9 ‘É•i —Y%‰ ™Y Ù§TIš 9š¢Yš¤yš(I˜Ò™ }2€!°›Ò-R›¶Y"(š¹šEÙ |x9`™ª¹!¬ù ÙœÁé9P›y ljœÎð›ëÀ_lu¼%ôu6’z‘ïñnÃ0œ2y”1{I ÊfdÑ[dqÑ5'1à!9ósçıF-aO´õ„$[. 1Ÿ537ö`çéœí)칡9ôžÀ`eé@@TöéYö „©Axö 5F;10ÉÿuŽ“bA¢@“\VF>Iá>Jš* èùŒÅÉ™Šé D-¥ä£"ê õ^1;Á7ðå“PÑÂv†HdjŸK1yù‚ š ù"[?§EJ²%kI ͉“»iœ½ ™Q*h¨G1uÔåk±NQcÎ]‘𕵢b¨ó9Ÿ¹Ç¹¡ÞÁà‚H7¡X¾°¤ãؤÐù¤Í0¨o„u&}úŒjú§¹@Ë©¨ê“ªj · ˜Pº«¼ª¡¸š¬Êº¬ÌÚ¬Îú¬Ð­Òª¬ °Óz­Øš­Ø ™Ói'ð­àÿ®â:®äZ®æz®èš®êº®âš‹Lªž–é„2¯ôZ¯wBöš¯úÚ'U€¸}3 ;°[°{°›° »° Û°[°3`¬ðº®J¬X¢þú¯ÓY±› ½º”¿jÛ±H‚±û #K²Å0«ãè¡)«sa}‡ë¢ñë`ªî2G³‚Ab²'+¨*‹ k!›/{RK£Y§4ŸÔ0ðÀÞ©.sÁê²´p!£.aJÖ9-á7´˜ I;´ÀÀ²>é²7’38 ['!?²°â¢à‘\Ô1[ê³.;ñq+Å3õà·ƒÿ™±ß¡u >ûj»¶¾Ù¶n{ªÄ9±Ïˆ¹ë‘·²å¢( ¡Qo#Š/Ñ…Ñ>ŒA=ªh/®¥1ÔSÇA=¨Á·6R¹´œ™[ p»”r{#”OdL@#*/4bAôÅK3ð´3>SdÃBFÃi8»½»±¿« E{•œ;ŽžÛ½î¡½Ò‰²ä‹ Á “ÇŠ㛾í`¾ç ™¯)›ö{¿ø›¿"›+¬ ¿?›±–ë (p›|ÀœÀ(’›~¾ ‹˜Á<Á<Á«Ü ÀÄð½xy´xù¾| ò[«Â*»¾ 9¼!¼»<À¹jÂ$ËÁƒéÀK ÿÃ+Œ #ܾ¸`ËÂx©Â7 9¬ÃžÉÃÄ*Ã#éÁCiÄA-¼½èÛħ*±þ›L,ÅÀ0Ä4\ÄXÜ HŒ’[Œ´]ŒZœ ¾ÛÅ><”@<Æ÷PÆf<òº¯r<ÇtL(>пol™WÌÆnܔР­€È‚<È„Œ–é¡ÞÊ®Š¼ÈŒÜÈŽüÈç€Á‹9™’ W°»ÉœÜÉžüÉ ¬°?°‘·¬&¸ÆÝÔ{%¥LÅ@ÛÇ~\ÉJ<’À)1@D+?„r¸fu4ƒ3’æ $Q_¯üÄ­¹˜ŒÆ„V1]sTF†ñF'Ulôù¢·¬o¯öV…yÄ Ëi)ÿ˜– ˜¬ÌN0]†ÇFo'³ÃHÊ!n¬ál7æO¥W„qèÍziÌÏé¾ô·ÜÆ,ò‚m¥Jax-Õ æo¬ñ-‡Å‡ö|Ïà<Ë9µÇÕ`+$ÛЗlÌm]»Q-Žñ4!Jô÷;ö×§Á.¼.߃Ê÷À1Þÿœg0òàÞí `í˜áa8´æÍâÜ­)®âžÇ»(Þ÷@4exä)¼âÞÚûœñE>;¸+Ožã$¼ª¨æ¦9æ:Ή­Çq·xÑ"JS0sÛ9"#:æ×b¢Ô ‚•¸=A45‚PþÃMRÁ‚>è*Ià‚@Öu+Ô“Pè#Ö¦=/¡1ù|”Ê1‘m_aÅÍÈéÇ\$—ÉÚq3£mØ@74 ´B-Äi>Så©r41¹{fë·N$• ê « bØ6B5ÌÞì^.߀úÿéÛ]äÊàìíéjèó} 5“¼AÇ®wC,Pâì;·ÞnîµÌQ—ôCpWß"[½ 4š6i„æË\Å3‚¥#vfOäC¸ +õ©x%»íÜî¤å.ß¿¹Ì5S,ІPxe-¡{­l›3G¸ö¥áGsNÖj-E3°Ë·.-qH5ñ«Öñ<Ñ4ñB¾¶bþódô@¿Tâì²4õwDð wPîüÛ’mŽã>40$®ôjqFÙ„D‘ím»E0v0ŸJÏÆ¨Dé?"îž§ úÛöAàör¿ 4 ÓS.ÆÃþ/»cÐ(v×Eº97òÃ^ûƺ¯¤ÿ÷(&éôC”éÿö*¿ö¡ínR˜ÐÊÍó¥¼0]¼Pôö ã¶Û8úÖn#á Í =›o÷Jþ¿ÿi8²¼Yâä˜ï¬ 5Ñ;S³c‡³R!0´uü> ºv‘TX Wú œïùDîÚ ö¸ ^¸ƒ¡ú)ÊO˜!PgŸ¹£Yû*Çß^+³zaŸ1QEó¢WkQ¯¢.êÿ×¹3-ï Kàú^‰æEáC€046HXH…e¸˜5æäµh849ôÙI4&Ùéù :¨…PjzŠšªº Àú »J°8ä1X;æÁ‰cûá3¤¨ûÁ[›üÿÅë‚óõäár£8Äû1ö5æâAôQ}ý¡9öe™íôÞ«-ŽuõáR‹ó¹´‹êÊÿߊ_Ž1Yn‚¥Ñ"Nyж',P*ÄlS%K Ž©Øð‹‡/+6ò’…á kƒ¦BÄÒÃC<â8é"”ÎFü©ÊС³ =!6„X®z8><ÁÑaµNpýz⢠(âˆL‹vÊkn$õÀÕ ‘œN¾%mxî/(ØaC/. ¸x‚oÑ’¡B‡å ¸`0ÇñPeΜŽqä2§íF–nŽ%R*HðI–†·åګȳÁªØUõ¢É¯ÿéÑŽQ£™§nž> -ìû_QC$Ê‚%9¡¨Ç!iC9(¹¥1É1!‡ˆÜ‹¶jXÊ’S>¨øtˆXœcùÒˆ¼óèÑSÏ·ï'ðà¦æ«¸wÐß1Мh¦“ºT“K÷Ìô%“`auþ=QZN±ô„Y‚`ÔZK¢¹@„llÝá›rÏn&‚Ò}ÿا¢*ãNê4c‚Öâ),ö3P!^à€Ã –UÌ@ñÔ [!%HYDXFD”šLâ8TeY=šaqƒPj’Ò6@‚HˆC¤•Xšæn¦˜#+;ÆiÊ‹nÞ‰gž„èó¥ÌiJ„TX"ÿ;±¤szƘ…eN(º¨Npú™  qÚi¦š‚2XŸtZÚŠ ›ŽJª¦“RZª«pB«®¾ k¬²ÎJk­¶ÞŠk®¯2€§Ÿ Tj°Ââyªª{ J,Ël³Î> m´ÒNKmµÖ^Ûl 8þKbºa¢ %ˆ›N€y4lº‹‹*¨9bªn¼0ò)¥ Ú בCí2M945Ô9jM§Vy¸Ýb¨¼ê²Û.²§ÀëpÅ:uZ/·°$¶XEš<Y“­äÚg¤!dËH¸ù•cáÝfq°ûén‹ÇŒ³`ñTóŠÀæ·ÍSýý×eˆšYˆu7¢‘mALiÿúå,3)§z57SM5Æ<ÓŒ˜¨cüäG™å˜OE©f5 yY—8ôõejqMêÌ_½5Þ1Óû5رˆMèV‘VS $N4ĉߦZuÏÁõí¸Ã^û¼·œbWÎyŒzÇ)ùäã 8景²ùèªïô9èYk½zΗ#&øÆ©{â‘VA* dV¡8‘E£±/Úz‹¡ûƪ®Ê/Ï|óλÊÀ¾º>8u·!®‰—[ ø„H‡@!²%Y(¢½"V6VÑ ‹ŸgñÆ¿®,¶òÏOýö3«íôïw›CÄT(4´lid3DJnð± ÇÞËn¢3öÕÈ}ô9ža('ÁM•Žÿvµ{¾ b¤¡…2š L_Z"¥èô…„¹€„-n‘ºeðM»š‚ÁFjvÝê æÈ%‘Ò@!Jæã„g U ±C䈒-ò…$N°†Èº!¥¨® öЇ¨3 ,²(>Ê è¢‰(XÁ×åÐŒwâáÆ¸”Û±qthô²Æ9ÖH‹o„c*ä¨.ŽèET¬¢I*7¾âŽýÛqtÂuéD.`ŽqDö¤ªb¤p4©Á™Žz°À-b–  qÜ; ž´ ³ãL¸ r¦Íãå!›@Yˆ:ÚñÀÜá¶H¹HÅ4%•¤ŽT!6= Nÿ(¾`B–í`$*3AˆÔ~YÌ1ó˜W+@ΉÎtªsìl§;ß ÏxÊsžé¤(9xºûŒm^b³šfNÓ ½›¼@—Ól V‚ 4´¹ Bœ2œâäd'_犌D£ݨG; ÒŠ4¤$©IKŠRêOE òV! p®‰ÄPÉv‚'¾A@‚Á#ˆNõ£¸2sœ+}QŠÔÃø›“ˆPKõ”†Q”¨EMªU¯jÈ|V µU‡Õ°Š5bK=Åæ‚‰Hh§¦YðÂNuC¾®nÒ¨ž«]ÅZ×?9²\Î|Ï#ýª~fA`Éér Ñ'<…Ar]—ÿEíu×ÈJ¶ªÁ¹5BƃVRŠ…á$¢Ã JTR•L«FPF‘‚ æc!;ÙØÊ¶Re­O.[ž‚=d&7  Ú[Pb¡ íío‰P°·µ¢x­Vg ]»æU¯Õó©õ¶±mˆÌK íp²Ðíh(s¿ ÖèªWºµýW5\(·Ò¼ö0œ“iYbXXÍt“Ahƒ7I+µ×ÌÎØ®…` ëúÍÍ®ö¤ìÒh{uÚ¿¶¶·iûà«ojƒHí'z GPø¼Üîö·ß-bþÉyQs ÐJDq–@œ#4Scÿ^j›Ê÷»NÝg—¢cÁé¬ÀüM´\zŠ€6ã[í4é o®ƒolPI"”cؤo-6+¢YÐÆÕ—fm¼‘ —SnŽóœë|ç<ï¹ÏtžÙƒ@„‰ –XÄM¾GÓvbÖv|æ4¯¹Á­îëI‹M+`ÌÓÖÚqGëË&{ùvyQ]Gfg{ÛÙç´/BÃèÛ~÷kW7­Ÿá@ø…odª“PY©.i$¾ëiíX{;¥ÝlྡྷòL«F+ùb‰ ‰HØ_H%Žwiï3¡'••oD¤¬JÍÈò)À¸.·Òù\ÎÒ2G²Lê;¿Úºÿ=@:ÖÌî@Ó &ì|(aÜ yA•‹Y^ò˜ì:sø¹˜¥0´Qìl4.ùKf¾:„¼SÂd&ߌ`ûÚÍRÞç]ïqæk?çV7Xc×47°Å$¬ôŒ{|]JÐÌCê&_÷e3!QÚp7OR7ù‡V‚°ñ$ç1ƒÅ@3Qm’)Œ·_m×{¾W]‡SíÚõHnõ7… ÁÊP!HØa@ÖS"x‚Ç1ƒàñ‚µ A¥{»G'&ÐA(„CH„Eh„Gˆ„Ih„)€Jè„O…N`wòfN%,XH5Qµ)ÊRìçv¬€Ã&ww¢È âVÿ†Ä‚~£ô' åu,E]c¸†ÄÓ†Ãd…»¡0ÑñV‹RÖq'Qc“‡r‚wx‡ûõ…"e‡;@u!e^QXQ Žñ  £bhÜð½…'¾Ã‡h˜ÆWì!aÓa4”ÁQQA„‹‡•Šês r è!aÌÁƒU‹ké"iB‡rX‹H‡Ôådø ºp æ ñuIq t Ç!¨$ 07ûÙˆBÄ Q ¹€@¹±û%Û¸ kÑ †æ P¡h`¸å8€òU€Øg8_dZ“ 2 òJ±dQÂ%ŸçOè²Ê€F¶Ðÿ {q|• i×ÀDޱn‘2sÄH)Èθ æµÜµÀ!NA0Ô Þ ÁC ÇË ™˜ç[°¾ä¬æ(XA¬¶‘OñþñD£øq„ÁWg¿eSóUq&¾\^0gâùØ[›ESSyŽå‰Fo¡PñYM‚p ņ! FŠäÔ"$¹&Y 5… ÈuÇ÷Lô€ÑØ% BY cáâàz! \òÇLÔä\"!Mâaѹ€™ôø õW–qNéƒÀf]{AyA‰õ`¸ R6“Óàp^" VM¢%Ä%š†ÿ&hQ7ÚÔ–¶±›­qb†C—UH'x©™D“e” C´ ø&^0 N7 –€•äS_Ç gr ÇE4A#L%µÁê9%žŸ gˆi•ˆ\ëI*òJ1',ñ#ÂX—”åÌè8"ƒ@IÒ9öèHt‚Ó/&( Ì'Îy‡6bŸÀ’¡Uó”wi º º‡:. Scˆõ!:n/¸aS5 é•#àôd£7Š£9ª£;*1*£]Dbû¢'6u¡>Îx|Š ê€'–Ñ9{?Jpò@…d'  à¥_ ¦a*¦cJ¦ej¦gŠÿ¦iª¦k*¦+ ª9H&BfÖd*a!BD|›`†iÞsPÞS"ŽàŸšäpÀaL@ p@¡"z"Z=@ãuj_?ä#šFoœf{üaë¹KOQë&Mj“Ü¥hœ€kC•ª6°‹Ð¯:«×ª_·Š«¤Yšil…¢.7láÔ ÀK`°,ðØ",º­„§Z¯=è 0÷$[ÚZ¯ò:¯úÔ¯xbü°sÕ­ôÊÑ c’'ø–nJ¨ºjÿU[°Žõ¡]4ôÖå“o$²6À` O¢œÕ&@¢ ½µ|;e~st†¨¯ `Gþãpd 72w1šÐ¤ã³jccM¦)±›ëƒ d$ÂF/ë' ‰ µSKµUkµW‹µY«µ[˵]{µU ±Y§OùÒ¤˜hrT–!ùѰ!B‹ .4!Ù•´õ¬ƒä´t² }ë· ¸+¸ƒK¸…k¸‡‹¸‰«¸k±J³[UtBDDü!Sˆ‹còµ4¡Ÿ 1HÒÇçG¥.¶‰h,+³wºpGtˆõE#@©‚  K3îz 1»ÿºõ¯Û»Úºº[ ªÛ»”ŠvÁë9Ãû:Æ»º$w;¡°@²;Á<ëcFkF¼à¼2‹¼D—±ØcoÛ“™i“Obþšñ$† h%6¹ i¤+EyK'¼û¼­ †šc³—³„rä鳩¥@ÝðÚiËç%¢fô·OñQ¥š½üÛ¼úë½\vÞJ¶%ÇBF£Bï¸O8fžÕ`=VbP’O¦[Š3ºq7X„¿9Ò½ ½¦I°ªHG¹JwO¹! \g2ËÖb$K ¡¤5º $u÷ËÁWƒQëµWŒÅY¬Å[̵`{±+a±{'úi#‚ÿƒRLì; Q—å´å\Nä° ²šBb:!žL«å4>Ò].[*¾â¥®Î§^LÜ3¶ÞÀýWãRˆch šõÐ&²ô ŽrìñÂë½~ç’%±pf<飯g(sÉ•ªÜÞ&Kä(å{bîíîÍa°ŽÏ²[ÁÍ©Ëgê`ÆûñO§• tÜFä(35ªTÎOB‘xvÙ‡–)·îŒnç…üПËÿ–Xɱ›a»Î ÂI®!¤À€ÎK~Mµ à#!›AMs ôÍÓ ¯ðCWÈÿ Ás55¼=‚̱SjáüHŃ•‚(hƒ‚èSØÒ¾ò,w1®Ú9ê[xÔ*ôô|w´ÎØÏÎð¸®SôI¡žàê/èÍ‘¢ëˆ5ƒÛõjuÁW„Ÿ…І€}¿˜ÓôWîÑöc¯HnØ!»QkM¿œe/õf'Ô'­"RIú——D‘2Ñÿ× ™÷ÝPÁ™” %e*ìp3^K6M*]MèP¹êjÇîóìî“eÒ˜õY\™D,ŒJ*—è-élâgÀPP¦±ÿ ¢l²?6¦Ñl,Aª¹Gö€Ÿ#§W..›· —ä67$ÚB‰yßgyû9ѤфÂéÕT7‘f"šSsbX1VïÉLõBEï0ê¿þë§ôá¤ìñ_úòŒü‘Õþ‚,ÿI‚ƒ„…†‡ˆ‰Š‹Œ†9c’’PP““PCš_—žž^D™šCNŸ§¨Yc•©’N¡X®³´Z޹º»¼½¾¿‰ÀÃÄÅ…‘Y7’_œ™–NÊc_.¯C–^YD¦ÒÚ’YÖcX’XC’ØæßDœXå«_DëÜC«¬ô_ÌìàcYOòÿ$Q«EÐÕ-c*\¨HÇÿ! ÁñÁš&,.°8ÉâAÖ¥'X¼Ü(§Ì =eþˆ˜ƒ2r\8sc†ÜÈŠµ²Æ¹po /øÆ¸°ô“)"6±|JÍå'H Jõt¢Õ«XfÝJ,FIš¼@!rCeT¡cpà`YÎ\9,8ȕ͒ʼn8r05õs!ê¦@V™t¹ëÂK8´š2ŽñÒÑ¥_J0§JS•«eÂ2cÞ¬¹3çÏžCƒ-º4éÓ¦S£^íy!¤NñÆÜp‚c•L7,ሇãÒl1e>I+<™·¨±¿Üx"­ì§»Íí”ù%ÙD¡CbÏžiüùZI3'O®|9ëÒ«_Ͼ½û÷ðãËoÿ^¾ýûøó›H°0’øÿø\]D^yXYRà‚ 6 (ôšƒVha*" ^èᇠB؈$–`†>Ôá'7xà‹õ4èB>²yp–ƒN¼èA:H–+"&4á$O w =Ñ£ŒjÝ™'P&[“–°…Z.¨Õ )_ý„N†­B8´ë§aÔŒ4É’åš[Ì™¨¤ÙŽ,JÍè.JA1¯4ÍY޽1§÷MJ 7Á™ÔfÍ Kÿ ñNŒTQÀXA±‹XÌvðÇoü*—K>ØçGSÉ}gdz*+ƒL4šøóÍE,)óªbXüœF•oÖSš¨’&L‘‰xD‰UCM4û šZŒ£ (ŠjJ,œJ‡&>u#•EHHþÁQ%Ÿp&ÚˆŠãL‘ 'ŒÐ °¨œLdÃÆ6O€a(Âæ({ܯ¯8‡ sˆ˜°P/„…O`h-öY *'ÿ bwâ7‰¶p“L¾LD E¹ÀK\$ÕMe¢', Œaü]¼0.r™ïŠ I_çÈÅÖcHtÌc­Ç^dQ€ü»ÈB’ˆ}ÜÅ|ƒF:ò‘Œ¤$'IÉJZò’˜Ì¤&3éƒ H(„k2Ò$’Ѥm„‰†4"™‹¨°Œ¥,gIËZÚò–¸Ì¥.wÉË^òRÑW“}©dM†¡Íwzu¿H©…(¬Êx8‘ÊC¾‘•Åc5·9™A£eŒëMìdf˜rèéh3ùÊÖ\ð¨PO†Q[¸¸é¡Ub“¤§>¥âM`¼,ôxÂìr# äxËP”ÿM >yBeŸ²ç=¡MˆZÔƒŸìD+ì“”‚¼ášê1Qˆ´;켨ƒ$:QDäS¥0½D?JÙ¥1eK[zˆŠæÔ¢3íJÔò”/%êEƒÚ‹šZˆÔTjŒzÔAøTªÜdê.&DÒô}ñ6Bш”P ¦%FXýU«šÔ´ÒS«»ðŠKâ¡™äK”9É]œ´–|AK[ÊY›[Otͪêb‘›L¬bËØÆ:6>¸À''Ò·°èéŠñ†ð f/3Þ-׋Þ`ÕZXÃ:¯ô¥jWËÚÖºöµ*(-!kœr„‚ùÂ’6ë0AÕê³ÔÈÈ•&ÿ‘²ÑH¶¦eÄU H¸æb¨^]2†…z¬¢_Ài?4›]¡$³Ì%ÈZÚÖðÒ¹¹pj…>eÞ©Œ÷¨ËmoÑÛˆ¡Ê7•ïmiyï;Gú6B½®Ø˜vµ¨Šýñ·ŠÈM®"â{`ùw¯¡­4u˜—Ää¦4ª0¢ZItX¦xœß‰î·Á&zð"r à©$s^`Uæ~†M!Ð(|Ëßò–#¡8á·øYp'Ñ þ–x¢ F±‡T Ë+$]R¼Šô¼Ê#ÑIGYdìçøî/+ÇŒÍ+DLN„êA–´s9ËÉ­ª”4»à\i$¡«4ÌÿàRó;FN°‚¡„JúЈN´¢ÍèF;úÑŽ´¤%­„1² À& lŒI,èA"µ¨GMêR›úÔ¨NµªWÍêV»úÕ Át¦ƒb2{úÖ¸Þª¬Ï¬ÊNçú×À.Æ®;a˜Q° «0vSa %ºh ¤­ƒMí`¿¦;$)=¬Ucªiv1ÅÞ66ºû%üH Tµ1¶Å´ÐÖE@¼°$CN»ÚøÆu(kA9j(sÄÔ"(“eÞ2II¤“¯å˜lY ù†”Ž4Ç$Hã˜SÂSÍ{çûã¦}Í0Fgœí®€òÒtn–3”»…áÞ– nëÜ  ÿÅâ/ïÕb^…1P¸¹ÐyZÓÌ•-dÉÂ4þ‘¥jm1QI p¾›K°*ÎÑ( «Ò¦³~Q-ãtq¥íë¡›ÝÚÆè4Öõ»p3ègûÓÎëÁ]îx×}ëÆ»çýï–¡ûbt›¥yŒj:ElšŽ›IÉ>Ï£ßOy«ˆ| aÊU—r0%;c) œxÃoÉj_z鹡|å“ݘˆØ:=RŒU˜ªÍqò•Ï}\Ö²0ÑäñsG2öŸ-.ðŠÚ:ûQ Õ™£"ŒCŠÑXR6z‡¯)9‹‡ï_¿a§Dþöe×½øÝvíh”w¸³2tÎ1½ˆÃŒÿîRþ_\°­‘¤žFAJ(fý­é„nºÃ;ƒas²V\„{ã—€4å +TP–à@Íp ÄÒc€D9ôc†&Rý05Ô PA ä€CÄBŽrÔTo~ Ø‚\±wBpIVÑ0æ Jàç‚:8w|§G¸ƒ@x0x`ØkAx„å±f³J¢ ã ¸µ pBr„R @TqJ}Ç‚HØ…ß$WkruÜF7í`'Åts¡'Wâ„S¢$_P+ÍqFÅ7dÆbaò“/Å¢$ÅW9Æ(h{+è…„hûFÖP;æàE¢ çs …E¢8íàô*Ã7Š(ÿ<ÎáPƒ‡1pñeQ$-! ó‰ƒXˆ¬BÆã€"áy¡ÝfDÊ@_àov1Œ!Ë• 31|y5Šƒ!Š/W+qès.á)µ…­8®ã ÕEMtQ!]£DFÛÕtÁ õ %׎֥a“ %ÜXŽç0a¡mÒxFHöè C(_Û2ƒ òƒ÷8~‚׃âÿ˜{ùˆ 'UȆƒuGYy‘ aÝ•1x?•¸;0±;`1>1’uéùwÐb!ñbË"c¹Q+ØpFòVc½q ãcsÒ?؃ ’yçdÀe…ñT¦~Ûày˜5À(ÿ‰ÿ@o!f|w“8w\õ k‚Ð`F5ÐÕ£7c‘½c ÃNEh É…M9) ʰi6I–eYˆI!Üø‘lÙ–„è“v—Vp—z¹—’–—|ùhU0—t‰„ j`˜ˆj‡©˜‰!†¹ ‹é˜ŒY™”y™“™™©™–É™˜j ›9šIš’5˜¨¹°B ™ Vb°©9›U%’®iX c!@›ÀÉJ—0¦¼) b ÁÙœçÓ*à-E[` àœÚY°Ÿà›<%¶é [@ÛyžY¨ ÐRãùÿ R€žòùPŸöyŸöLØ„ŸüYŸóùŸ–wkþ©ºz …@ ¨ z  *~Ú è¡)¡ J¡•‡¡Úœ xº¡Áù¡y'¢ Šš$*w'Z¢ƒ™¢gÇ¢*Š“.:t1ú¢!9£ g£4j8šo;š£ÔØ£Õ¤>JˆBjmCªE lIz¤JÚŸNú¤ýé‚Kʤ¿V Vj RJ¥ 8¥;È¥Zêi^š¥_ ¡!:¦ Z¦fZ¡À¦iZUlº¥mš¡h§”÷¦gJ§"9§x:¢zº§-Ú§~ú§k¨( ¨„*tv:~‰z¨e²¨ºç¨Œš"*§‘Ѝ†Z©Azÿ©˜Z¥šº©œ:¨žŠo“Z§¡úq£ê¡¥Z ðj¬új0­««&£©Z †ó—¸ŠhWp¹Ú«P˜´Z«ß$ŸpoWz¬¬‘¬Èº¬§‘£àaí ‹171’àHµ `ÑZ!† µ0mŒôXâÚH7ã:® pª¬ø¬Ö/’ò|åP$ƒ3BYïC“á J‡Š¹£$“³ PõsÈ¢‘UÓ…¬Ð@[> af|×>Ê®ø .†£2—À1_„¸\_0PÐ/†±:$3dAxS!YÂNA2‹vðÒ~M±08a)-ëF Ë(Öêê–k‚ÿA8óZ]ýF‚[§ €¿³ˆZã& ,JÂ8£“#óÒ|ŒÓÀ’€¶Á’¤ã¶7^äbC.2#O£'Œ :#‚'*ˆ ë¬Þð¶ñ’4æÔ ’2#cs;97p‹ÒqlÛp`,/"³£p²Ì±#.â²/"` Û"»ó9“B‚™Ïà“ß09 ,-â'ƒap b q"M>¡t’°N¯`$¡P(òsNiá»v‚(¹˜%]RÔpá'7@Ë·Äj ¥{pS+ð·9r;åp½‚0'³7BÁ0Bc1$óR2ºýò v‚ÿ‰q9S-U† ´(ç'ºBQ%“&7Sƒ{K£ì½Ö³¢Ò›»ºë“ñêº~#¾~â“7“³A2— ¾7d-»#7ãßÀK0N@1zcv/« s3R; Ï À̰7¥BÑå%ÆFŽ$?DšR<´ § *ap&õ ¥ÐCfPÉ«6TÄgñ¿/À/Û`-ü¢¥\ÜÅ^üÅ`ÆOªŸÆà°u'Å*JÅœ¶W|`YœÆ/<À±pv|ÇxœÇz¼Ç|ÜÇ~üÇ€È<Ç<›@ Š¼ÈŒÜÈŽüÈÉ’<É”\É“\ýAK¤†Lº¼É’€Æ%ªÆÑËÆžüə셢L¬¤\Ê  ¢©,— ÑÆüõÆ¡¼É«ìÉ­¼¡¯¼–ŒË§Ü… ÆÂ<ÌÄ\Ìø ½¼É¹l¡»¼”ÓÛS¥ì¼¿L˜¶üÌ…°áz®Ú¼Í‹e…lȶ6©õZä\ÎæLK[ ½q|ËÊüÍëlÍ… Ë÷EË®\ÍÉlÈË,¡Í<–÷Çô¬ËöÃò,_ù|¡Ý^ÿÌÌùÑÐýÐ!PýüÂ}¡ÊšÑ̺Ѭf’ÐæµÐš{Ph¾zÒ(«&0Ò;蘇ùÒ.Ó0=Ó2]Ó4}Ó6Ó8½Ó:mÓ¾;opensips-2.2.2/modules/seas/doc/images/image065.gif000066400000000000000000000044521300170765700220110ustar00rootroot00000000000000GIF89aª‚w1!þSoftware: Microsoft Office!ù,ix‚ÌÿÌÌÿÿÿÿ™ÿºÜþ0ÊIk 8ëÍ{X^(fÔhžhª®lë¾%[p ï|ïÿEîG,î‚’À`Él:ŸÐ¨tJ­Z¯Ø¬v;EξaL.›Ïh1zÍ&†Æx —Û½%wÏïûÿ€x`„8m‡iˆŠn8vŽtw%•–—˜™]…œB‹‹jŸ‡o’r‘¥Fƒz𭮝°Rª4¢ˆ¡¶k¤¨E§¼@”±ÂÃÄ‚´…e¸Š¸Ë¹f»¿>¾Ò<³ ¬ÅÚÛÜQ×ÇIͶâϺÕs èéÙÝïðÃßàyänÉcÉj÷bÎãέÓAmÝ<ñ*tu^ƒþÜDÔ7qŸ¾Ö1¨nÿ Á` CŠôÓÐ!q.Z¤˜2£FHÑE²H*#sê´RÒ$”Ð$†úGqb9F90Àôp䦘Pif€¤*ÕívjÝ ¥§É|,…^¬HÔ_Ù£ú)­³v©Õiê4¸=Bs.œƒî¸êÍéÕ!X²CË®ìçR©¤RÛ"®y$nÌ©T«.މ5ÉÞË:ûÒ+œ‹óKÄQ“Îíåx´ÔÇÖgÅÌz¡fpž £=sø±\ÑW‰Ì¤¼v*dɪ-÷É–—Jq’X)9ÞDóÖÍ}Öš­Œº2 ÊÄÉ…¸–ç{¼_=ºôÖ%¦÷‡ãS¸oŸIý%’ãϯßÝýäÿüØ€Ëh_~õ9ÇŸå½vLlŸ@J{Û½ç^$m ~( r6g_‡~ÈaˆzXbŠäñuzëIÈ …ÕhW£|߉¨!+.Ç`^þ¢sû=ñ#‡CŽØšƒ´8ãÙ2üèòL4Ùut!ŽYx$’B:¤—]r¹a€'ªHâ™K¾Øm·H™Hg4Jc£œXb¡e˜™$˜bªX¢‚xþy&ˆiªFˆªÄR?‹‚5ØJFAç/sRZçx"¨ç¦š÷ß§Ù_€yæÙ©’¬1Ù‰£ euùvQ.QéÞ¤¼`XÞ®Û¨Ê Fê™)”f…Õh¬mØZ!®ÿ¨èÊë³ÂøŠŒz¯F4TPÈ^ëd²Ì–Ri®—B+®1†Ö#YF›íXÚF*)ctZXåjãÖ‹‰´„$Zl±³¢ôꣳš¯¥òÞz¨½[‚/2¶c·’|Ûl¸ WÌS¹y¬çîlÊÞXð²[,rw'‘Æf7< ÄHì-Å#ÇlØœ [Ê)³ìˆËÃ,³ÌtÔL‡É&ÿÄ<*'›óÀà~ì±p?GíMÉ$}44 qÎ3=±ÓñB-õØöamöZ=%͆ÛÃØ‹MöØAŸõÕ6ØÜiè< ÝMÛ}wÔy§õÞX'ž× nŠ•ó~8ÐÿT{14ÛCó}(äÖu¶×/Ó{9â%H8›]ËÍsË>Ÿ>.Òç­Tëð½¾sì²C»ð¶W×:é¨éFù[© ïlï1ÿ>CðÁ†®–UwõV0ãF½õ-ϼÈÎË}Q‘ƒvZ]KÁ[P[W¡Zp!?røÓý½tdçó–\Ùÿ†[]×››åäg/úm-F÷ û¨¾ã)ð¶±ïX(Œ=tÔ©dCÈð¯=ïÛ`bØ2A bÆ€~ ÝýL±;ÂÙ„4¦3a-¸ r Wœœ éâ5ïÉpv4¼ qw¥ö,†? ×4Äò¹îx ËC¶ÄÇ5Qzăÿµ8À)6(ˆØ°!ZDW·,¶°‹^¬ Ç·±1J.ºÓxB0‚@ŒG!cáÌ(ÇøÑ‘WUüÜ3øF§†sü£^™±AÞ:<¤ñ¨ÈTÙÑŽt#$áŧù±’j "G)Iì•òƒŸe/9Êñ”ìH¤*3sICd2°”àϘÊY.²–6¦8'Ì̳ÂL¦2—ÉÌfŽ –4€¦4§IÍjZóšµÃ¦6·ÉÍnzS”ß §8ÇIÎršÍœèL§:ש&F²óðŒç4Ý)ÏzÚóž>¡'>÷ÉÏ~¦ÎŸ ¨@Û9Ђô  C¨BÊPÅ5ô¡å§>#JÿÑŠÔ¢Íh9'ªÑŽz4š ©H¡ÉÑ‘šô£%=©J1êÌ–ºT+©L4ÓšÚT|7Í©NW±Óžê4¥> ª<*Ô¢®“¨FMêF•ÊT"µ©PÝæS£JÕjNµªX]#²ÊU€Æ «`ÝçÂJÖ{޵¬hgÑÒÊVu®µ­p]j\ç*WºÚõ®#kI˜ä ÚÕ, Ï£éVÕ¤¿>Ä/„M(O+NÇ16_Èhðì¸×yþÓª]ì¡ÆiµÎvmœ+ÙIöZ´acmDC­Ú„V4ÕMqš{íC@{ګѶj«=çRË7ÕÂ4´l[Í`ÊÛÑ­´ÂÝ-m‰)Úßÿ.N¹Ãe\ã<÷Û·ZssÑu(r­Ö¸àvö°Z£.r…ÖÝŸ<7¼³=çyukÞìÊÜ .z{Þø.×´Ùµ/~wûYõÆ7º¶•ï{…ÛÞýJ—À˜…­ÚbÞêâ¶ÁN/l ßúN·µ<].Õ$laW½ .¯{•ëÚävX¯.±ˆ<\âÎwÄØ5l€Ik]j&¿àu¯‚ÿëPðŠW½#îpŽÙ+c›ý÷¶96pÚ Ìãõ‚8¿I&ð ”¼cø¹MΛi=ÇÍ;YÈ[³ÙÛ[$“Ä%žñhµ+`0cÈô1Úâ¼bÒžÊhfr†ý[g/¿ùÉòõ³c¼Þ·jùѶb>.—E[`þ¶YÉ®%Ÿ­«â/9Íðó…/dÙš7¶ßõt¤Ç{g+:кM3›ï»æãu¨À¬çU_].çZÐÖïœ5­wÍë^ûú×À6lê>LÓ†è:Øþ¬,‚7Xd×TÒt°—Ç[iÞn.ÌŠf±³Uêh·XƦ/™ËëÛ:÷xÛy5w‹#¬àk×ÅOÆ6•îÆªÇR·ãˆ­ç#¹Þ!õ÷§EC 8W»RwÆO{ýsÞ\³GÑvŒ|Ði«} ÙxãÓR_½lηõ+2ƒÊßÿ—C.4ÞzÏ|4á†ïc¶ÉgD;ñÖFgpzä§7’ºå#Gòqد“LòÅŠ:–…Ó¡È’(¬\¸¼r'‘N»ÄÓò;¢¶ôŽd–2¾¼ L€<}=¿k(}sâ l§¼ƒ¿oª~ÊŠ¾ïKæ(×·ï><‰ ú§ó/ª¯µþ ©µâ–ú-ÀÒ{Ÿ…eÈy„ ð8u¦\±ˆÚâýn…gÅ YÈ»D4¨ç„xe°xÏòÖ6(­ølWࢶÂ+ѦT!¿T.ªy.ƒíš†<8 hÑíz‹ðLý¾4D}9  ÿNå­Î0ϰYƘQ±Š½Lƒ@ƒ˜ÿK&±Ùil]+]#ºƹ-o”‹Z7v»ÒI± ¡è÷rD*:Âw ß»…Ç:¦ŠÕÈ!ܸvEtÅM €cE"‡¦C3BgNã\!;—Æ­í,Ò\ºNš m5¡ÏÈv<„ §„Y$)«C’XìØ×\µ-úM“fKƒ'wÙLÚ±\)¹31&®xDPåætFJu.–\eà>è ^ò”Z$™¹8Gà’gO“f(u³(¢˜Œ'·)‰Q†ÓÖ¼&*"·¸!tÑeÔƒÄäGL+¦«£å­X9Ô}Í‘U›#¿ñªxz’€› ¦Jéÿ>‡>¢†èD=#ÀæMÁyòĨH‰çQ)€t—#Mi E1(õ­`/µUYÆƳCõ|æGWzÏ–f0¢/mæ(ÚÊf¶²Ÿ1]h%ZÓ–ô¤MäVG9Í£&õzg êW£ÙР5«sŽOgÖƒN´°a|gU+Õ¥^t¬ MéA‹šÆœVö¡+}ëZÇ:ÕÓ.t¨7}m_›—.±—Ü`f[[ÖVÖñ¶Ñí]bÛšáFq‰iœmX#ÚÚ‹V÷­/ýçH¿»“ñVñ¼==nþšzΈ¶»ÿíÉ€'yà¶³¾ül[wZ¿ o8²Qß‚ 8â®sÈ3ð6ØàGr˜b’‡nËʆu­/Ns«\â,ß±ËMè=šTÐýÿ¶x©kîñÿ‚œÜê^ùΗáó¬:è®>s¯µiß|ä9_òÒ™î@9,VWŽz·ç\õ$_éYWúÖ‘v@Epa'5¡Ýä¢üìtN:Î׎4ó¬|);AÜí­km³9åõ޻ݿÌw´ùÝW/JšÃ9ŒøÅ÷WïXo<½d¨G]ÉÈäòž÷Ñóžö½kžZÞ;ØKcZA˹ò-/}æO?´^à¢ÇûÇ1vÚsí®ï8ìuüûÞ>·xîŸùâøøh™¼‡¯uæÏú ‘~È©¯tçûyN>å—ï}óžôàϾø§OþØ[¿÷égèµãö_ÜñW?ÿ±ÉnôLOýË*Gß—v\¶~þ–¢crûöqGlw—m Øq‡nñUlh{ûGuýG‡dåçmEvmvgˆ2 È(ç€ÿ€(€ ¦wÈ6‚%Èá)ˆ‚57`ˆƒ—'{ÑV3˜€˜ƒ_VvDÈ`;¨Œçƒ"„A8ñæ‚'§‚<`Ix„Ï—uA‡…ö„Ph€äæ`xa8†/H€Ê¶x$è…e†(nºgtç·{lÈ Rxƒ(‡7‡rX‡Ûp‡¯gÕ÷æ‡è†xhuïG‡‹Èˆ†ˆ €(|‚Ø}ø÷ˆJ‚ˆ‡¸‡>ˆ~–x8˜(‰šHˆØhFŠØö‰7eŠ6‰ŠW‰¨˜Š§„6·‰@ƇœøŠ,ŠEÈŠÍ犸 ‘¸‹£è~ªèm¿ø ÁXÜ׊îwŒA¤‹ÊÈ‹½WŒçŒÈ¨]ؘØUŒ"Öh@ÚŽáh]âè"å¸4,–;opensips-2.2.2/modules/seas/doc/images/image068.gif000066400000000000000000000001111300170765700220000ustar00rootroot00000000000000GIF89aªw1!þSoftware: Microsoft Office!ù,€D;opensips-2.2.2/modules/seas/doc/images/image069.gif000066400000000000000000000350251300170765700220150ustar00rootroot00000000000000GIF89a?(w1!þSoftware: Microsoft Office!ù,;‚ÌÿÌÌÿÿÿÌ™ÿÿ™ÿÿÿÿºÜþ0ÊIi¸8ëÍ{®`(Ždižhª®lë¾p,Ï4¸qç;Þë9 /ø ԎȤrÉl:ŸÐ(´H¬ò¬Â!OÊíz¿à°xLQ¯h­úì+»ßð¸|N—²³xöÝXïûÿ€‚b{XyiyƒŠ‹ŒŽ…ˆz†|—˜™š›K’j‡ŸEœ£¤¥¦§‘•ªi{¨®¯°±n– ž k•²º»¼½3 ¶ž­¾ÅÆÇÈ «·:ÉÑÒӯ͓Ù[ÔÜÝÞpáÎÏ=ÄßèéêN´ Ø¡Úmëõö÷.íÌäùø h!Ä…TóÊ%ÔG°¡C{ Íøsvî¡Å‹ß"œ8¯ÿ"Æ i´ÁQžIz!SªÔ5²B¼[Wʜɩ¥…’0ÒÜÉ’Mw8)MêI´è Ÿû‚b‰i´©Ó2H#¼ê©Õ«„RˆÛʕ֯`Ù­ E¶–YfhÓ†]ËIÔ}gᎷlÛ»x_¼•:×®\µ~ó Lb/„ÀˆëÎ%̸ñá±}#^츲cÃÊ$ÿÝL—3fË Ÿ~¦¹³gµ¦C«Î;ºtâɨ3§~Ü™Ÿ%¤­Wë¾”û4êש”-Øð¿|’o¾ÍÌøðÛÅwK'Õ;¸kÅh½6?~{mxÀLL9yºÞ§«÷ ù÷õÓïÒ&Oú;sðÝ¡?Ï|½Þÿíù& xµxÐß|Ö}§`w îWÖyût%á„Vhá…f¨á†vÈáNÕ—àr‘,#_~áx¢x¢ÈƒèU÷*2¦â{ê£ÝŒºÕˆÎîIby<®çcFލä}Ey¤7@Cé-(U•…59ГÝD¹¤fT¦x sf7Þ2^éŒZv b’"úfšbЉ^™x’!}1¶‰$M^ÆY—„ WÞ|çªçq¶qv¥‡F*餔Všá\r(޳•˜¦\Ê ª}w¶Ãž~’‘©Voª ¨‡Ùiž‰€)ªhmj‚ºjª¬f(œœ¶„%¯Òìz‚±Ñl k`ÿÄ „l Ï"£¬””Õ‰£. Ûi³uD;‚·ÆLûelž^ã™Û1·&Šàr«¦­öJ-¬Êhgo•ꈭ»s´kC¼(¼êY…ýñg*£ûÝ¹í£–6ìðÃw¼¿º¬ñ™‡ß}L’º0¿ûzá¯K{±Aöêz/£Dâ 2m‡12/âŠh¶/w2±¯3Õ\ZA9‹¼³Ì%› ðÇAÏ243ïâ3½îÐf˜rp%rMWÝ3°Ëb¬£ÊÑDzË0ÚùrÖQó,ÓÓÞ\çÛT¦‹+³9£­5ÓEC¬†Z Ç_íiÄ„ŽªKa7,l#í­3¸¾’“[±ÚÿDc®Rã£í8ëÊ“£Û\Q‹Ë3Þ—Ìé·¥çuâB§nr×V­¹â§¯Äyí¶gž{ì[[L»ÞĆ3ñ µníí\(ÊîF§÷yØ.Žêâs‡}¦óp/¸¿§=´ ÿm:„bã—¾ƒVn‰øû¾¯ÍõÀöÍçvø¯_«ö Geø„ð‹Ÿ'?áµíX³Þý¨Ö T!ªlý;JQ¾æ¹î[?C w¦wª‘ÅD§"Ûá=ÇMv¯“Ñ2½”ð…(´  "¥$Õƒ&|Þq·Ã2&rßCÙ×ÎE½O‰pt¦€a}HAÂ1‡Ù‚Ûã6&Ò¨‡L`ÿóDY£P÷K_ßÄXÂ(‘y[L#^žÈ.HQnCr`µ@4‚o‰2Ôâ­§—”±ƒTià½3êQwü!óq¼>‹y´ã ÃF”Q^MÌ$ñ8IE*, fËÒÜø.Ivò‘ܤÙéeØëÕf)ƒGrò”¨Ä%YiÉ·½rV£2r“)[šò e4æ*?ÉHÐ…ÑA¤E™K]°šb¡$ØÆ¢±Ï\´œ&±™Íc"ó–a!eᦾ±µHEcŒ25yMz¦r—âdÝ%WhÎ&’œLØç4Ô™õ3 ¼'"ÛBÐb¡³œõçC¿ÒÿÐ#42¡Ö\h’9Q¬T4IÝë¨ÎD:Òˆ^壮’4‚&¿¥Mÿ:hIM:SU¥Z1b©fKØ`+–ˆj©FæiÓ¢U¡{̧×>Bô±1W: ç'f:{"õªXÍè ËIsH?•ꢨj.š‚£%©G¡STÕ}"WÂÔÕ¢¡ªÕ-hÕjZ-³*³s[deÑöÞJ"¡òE¦Jø§U÷Z™¾®ˆ=ë]ñÙ“ú"”–˪^[Y3‚F %d^!jV‹~öO¥5ídU›ZÑ v±œ…mlë¹L7…V´«õìm›ÚÖþb´nM=i[ßþ6·¸5®Qz+[ÒÿU³›='__ûÜD*·–È].ui«Ñèz7¸Ýnw¥»Ûã^—(Ì­îwÁ{^µ¶šÊ®<…‹Ðò’N¼Ðu.wÇË^mBI¾î¥ïpù»–ôÚ!>¦U‚w47ÏÁ VðM5%áV8˜õ'„-Ì`d^˜5]ê°‡3Ìø¢‰5±†IÌУ”,^±ò>Œa¦Å˜-~°ónÐø>¶h-bL™C2’Oû_ûÀOn/O¢%)yÒ°—nÄrý´Ü¡V^9¥Y • @æ2›ùÌhN3—@€6»ùÍp޳œkÄf9ÛùÎn¦3ž÷íZçõÜóÌkƳ9¶^|݃®ÔÚô}èÿ¸à…-r  Õõ6üÈ•Íö:g½ò®ÿ¼ÆmhyÆKÛöY§;ÖÿŠ™º·ö»¿½ë'ŸvÝ[þî´wýñ•{ÌÃþE7NÎç_/|¬O¿ö#|ž<4€ˆa»„̨g5¾Qmúð‡›õp7>åm?ûÙå]ýÅ·~ß•ŸÏø^²¿üÿúåîuØÇ>þ—kÉ—·€—§óÇo‰’Y“{û€ï·~ xв}VoÀVzg†zÀj©ÆÐsѳ ‡~”W€þ‡€ü0Ôç|ì‡{ xy¯d4o÷€×w‚íçxg€˜y,ˆ{¸{Ø|L%6Ç#}øz˜g|K¸.4bh€¿VzxzV~W‚áÇ9E‚§‚à÷ah~&€ÓWƒ+è~<ˆƒ§n8ƒÐ‚† h†ÿ—ƒÇjh¨‚›×ÒG€ºñ791å†y臯&LOxÜ÷ ÞGjW(~_¨…†à‡w`(†X‚¸…ˆgƒz({’·xmø‰ÿXŠpXrHŠžH|v|•çƒjxƒ¥8„AA„„†X€Ê燘ˆT°wÐˆî€…Ž¶‰Æ‰§×…ʼn˜˜…æ—ŒîÕŠ@Hw®˜†üD×G˜8µ©|àhyB7tƒ*¨‹g˜íÔH¸X}¯¸‹ÌWyù£}ŠÈ b÷}˜‰aèŒÇH~$¸…ÇHŒ•VwugEWu©mGƒ Y P˜RHøøŒšx‘c(‚äóûˆ‘)q É)’!y‡z×%éç æ€&1‘Û0Œ©—o,è…’ø…ÎH†Q×*É“qø=铨”?¹’‰8zˆ«–WrÆÿÖ”i”E)”ßr’$™’A‰•C)•I„”ÂØm<7Dz”UH“>G”S9’:H•‘qç6sãh8^™”Ž–ªq ÉyÉ—WÙu}é—k©–Y)˜,Y—S¨”P·sx¹“„Y˜€˜/'™“I™Šg™p©tˆ“`Ù˜Œ¹˜gÉ•U‰–[É–6à–\W™ƒ©•2#b­“‚Gx«neY‚ŽÉš£)šf`•«‰›»Iš¿©›DEF³™œIoÈh›5)ãw›† ™ªÙ›‘)ÓIj‡’Ï©6{BLÓP ›Œ¸pÊà‡÷™ù˜zÎY&)œ§ œmÉ›Öy™ª‚&3˜/WÿóL÷•ÈÙŒÊ9–ɉžQišíÉž.ŸÐIŸ *Ÿñ™™àp È.0¥ Ê ëY¦òU£70£Ú¦‰y—XêmyùŸ#êžïi P¤¹ ©î ©bªžGé’êƒ*‘ª˜fgž:ÿÚ¨”êŽJ¦aŠª©ªªcJ¤¥ÊÆ)ž‘–b©hžš€jª¯z–ú¨»ª ½Êª˜JÆ)›z锣ڧ&:Ÿvʬ~Z¢ËÚ zê¬Ñz˜±j¬Œ -´º—f¸ƒ°8Ÿ›×­(™á Šßú§rè­çŠ®éʆãŸå®sé*± ˆª Н¤úŽø‡—Öïׯ·|°MH}ëš–›{K°$¹°û¯W ±IÈ~¡ønõŠ­qj« ¹¯0ˆ¡×n;‡åj‡:²´²W‡²é¨²»)ë°ìʲuø®y­ š¬Wz«ûÊ‹1+³èÊ„?›° +Ck³A)´-+±ÿk´K‹´Fª´5k7[¬9;›¢Šµ¸:„Í'®T{²7H²”6p\ ²L ¶»(¶cûoe«¶@+¤m{´&{”›©±tjÛº“Ó(ø¶S¹·më·¥ ¸a+¸HJ¸i{¶A«|k±û°Œ¸_K·ˆi·ç‡¬ZÛ³Cꪹ*Áʹ˫§©£« à¹t–«¨˜{¬Zº¹¤û«‘𹯠»Á »•Zº™pº6—ºÈ¶¨«¬ÓJ­Á+­|Z§ÍJ¼r—‡¦™V{¯:û-yÛº­ º®{»´Ë ¨©¼‘¦½{6¯Z‘±W˺ùú¼{º¼Æ[­ÐZ¼åk¾ë۽ܫi¢×¼oÚ±ÚʳÀÿ«¾í ¿ï»¶ù«¿ç;¼é›¼Ì[·á›­;K¿ûë²Â‹¿ ,ÀýË¿¬¸F:¬]‰³Î›¹õ‹ÀÿËÀÈ˾ ìÁìZ½ºº±‹»˜ » ǻƶº¼ÁÁ†{©A:©$\Â× ¶›»à{Áâ˱úz¿/üÁÛ›ÀÌ¥Ó;»«ŠÄ" «<¿?|ÀOì ÂLÃK ¬9<ÂIl½5Ü=;ìÄäkÑ‹«[ÌÅe¬ÅWlÃ7ŒÃ&,ºk|_Ü™Œ·öKÄ1ì«gì¹mŒÅ{ÌÇ]Œ½}ì(¬p*¼”,ü»vL´F<ÃV|ÄhœÆjœÇ~,É0È!WÀˆ ½u<‰f‰]ÆeÿÂÉüƒ[¶vŸŒ±M,ÇQ¼T§¬_¼ã$qLG­l^JóÊ©2)'Þk˱ËÈS˼l5¾LMÀÌA3ÌãTÌÆ|6ï¦ËËœ4ÈlÎüÌuÓÌjcÑGYƒIeåOY–Ôy@ãËÑlÓ¬^ÝŒ] RËãHQ–ÎÜ\Pâ¬Ì¥ 1¦[æCFl’?tUhJÝ)F^¥+‚õÏÁ{MЉÐëãÓE4X*ò@E„=S*ÍS=E}’ÐÝR Ö\P„|Ä?“Ÿ#¢NuÕM% O,ärU`U6Tµ',ÍLM-ˆ ôMSå26ýN9$>Ýkåì65ÔÛ„<]db„¢¡ 4T.¥`NxÕéòÒÖWNÓtõ)ö¹Óa]ÕD-ÕI–_¼¡b&Y'ýÕŠÒrÍS…uCeåVEœ6}>Mº"w=×yÖ]Ø…íV?×¾A×pÔHý;öVA-$“#ŽÓ‚Ô×íGp8Dp40™ÝÙ7 WŒ}Öv]9úŒÒ ýÓB­ÚëÜÒÞ I½^?Tú|Ù5íÑ^u0qÅúAcCÓ¿­ÑÌ$+¨-ÐÔýSòÑS£CÑl’¦@Øa}¿Ñ׳-eÞ,PŽíÎÉüËŒ`,Ý-Ͳ}Î÷EÌà½k–Ý5QÉ„“XÔ\Í‚¬ ÿA‘pßîBÎô}‰pßYÖ8}Ù¯}XNÞÁÝxEsã=ÏP±ß qœÉE[C3‹ýRò|7÷œ% N•ìàÂßx³Ñæ3B¡ÒU-+s5VÃ}F8нÐÝâ»%fâÏó˜Ü ݹ=âsÜ‘ãÛüìУ ßõ]äUáßLà4Ô­½ÙŒ/%nÖƒØDУ}Ö®}-S.ÚYnå•Ò;îäèÚ¥Ëómä~äÚ <ˆíME¸å½íRHÄÚÍÍ'®$Ô‡ÍTj²‡‚/qVέÕ9Ý(þÜ!TÒzÞÕ„mºŽæSâ›Ôæ_•ØÛüå„Eæ§(LÌÒ很ÿÈpÞÔ{½èb=ç-ÚK.ØCÞèü=ö½æX“=Kå0…ÙÕMëǽښ½S Mé^è¨Ü”=ݰmÖ3=LKêÒþët¾êgÞêéBM„eÝÇë³ÏÖÐÍÝ?Ñ$Ñ2ÍÏR}ãx‚ÛkãùÜã–#:mÒdÄÐtµwÅêi~äêð, nà³áìñìøÒþ#ìÍO½à%êlæ^ìè¯ïø}Ëöîê QðÏ+Díá´|ñ?ñ®¬;à2áçNÞÎP¤éZ^U ®7ýîòŠ@ñ/«ó…:%ßΧ8ˆ1D<¸áò2ªó¸óð Ç_óöÿ ëI^ѽ¨Öqî@îíš Ý ½î¼‡ã‡¢@0.ÖãnâÃîýÌîQža¾ýõ°tÕ’nP ôñ“TÔŽÚ_NÚwÍç¶xD¡®ÜSÕ³´ŸÜxÝJ¾Î¸þÙrÍéø9æãñïèGcíÓcÄ1M Ó“ÝæLê9Sdժ؃Þa‰^í$ØQmŸ”OàÊâvNÕ Îø4_¿W¥M÷{ßé "GJîT†×œêÅ®ûu}ÚeÎä—.ìtØ1zŸÇ¯öáòïöÒÕåž­õ‹nù½˜ÒÄDh=L7]çi]ìDû—Ÿíû‚é‹Íùš» ô°ý%çtÿNvîÜ‚¥õÉ>ÑàŸõÙNÿ*EJËÊ JU“ï6;ë¦X´=5jW–ê”±çÚÍtmßx®‡{ïÿ½€ahCd‘¸<2• 0*­ZgÔ«vËíz¿à,xL.‹ÌhœeÍn»ßï´KnÓïø¼¾fßûé}_Q„sØÏZÛŬãªÍráh*k/$º6(ï-g$êï)÷ ð"÷bü#ä•ôžÿ@8p`ŸêB¬ _+Šþ<裘¸oÝ”‘c¦ÌÚ¤yS‚`h/Å šø¡l…Eb¼)oÂL¶’&;{?êdÉmæÎ£,‰&-į¥/2§¬6µ’È«$¥ž»4QåÎy]©%B¸.kR‡bèÙ,«’l¾š4i‘Û6®Öƒ^•ú2Êt.¢L`ëU;˜ð2žcK:;™×®c­å¼ûûô/¢µÎÚÁûx2R¨{Ãõ ´´^¢¿ò4ZØjkyXû$¶ØSZœ|Ÿ~¾EzÓeдé"E9t¶n½A•/ýyôiÜugnœ}vYî×°±Ó)5í­¿›ðùuD©ÞÚ,§žÿ .ƒ Ugœý¯>ì’…ñ;sÇŸ§1df6Ì öy„Š‚£pw˜q J[¯Y–Wàé¡v[lŠƒÜ™“$–hb‰s!%®8¢xþ£g†¨ØŒ.´ãŽ<öèI Æ&âŽrùhä‘HvÑ"‹±ÕS’ô}g˜‹×r Se £ 7°Ýv#d`îƒQÛp M•ß,ÉØ“l&挛}8Ù•sôÒf޶zîÄ›€úIg. ©)Im å~ñçÖB-%ä_{åG#EùõßRîáר?úÕgi>’J£QêÑ£žæè–˜–f„ª)@6ic FFešÿ€=×ys÷¤\^j6TSÖ KÙr¿Jk~öäÝnm-…f©…,)†F‚èµ_BWX0åæëaØüVd'ÐÃë¸l­«.…ïq4Zh1Ã[bòVtnuèNªÐ}Ó6Xk›NÊ&ndœ•Ûk¶‘eÆï™ÍÅù.kwÔœg\YP_ÈZžù:íÈ Œë#ÝBòmÀ+YÜÔ UmÄœ]—Œy"÷†¯u©…fs³uÍ×nÉAÿ Ãq #Ýžqóø2'Š&‰êª3÷îÒý,ªzù¨[Vä!øéx r,«¨ñÔB虥òÚöáWðy®H77rJm«á.¬¢’k µÿáwùw¢#ŽŒ˜@®ã,JNy´8N±”—wîùç´ú𭜃núé¨Ë(zw€Ç,.`zéºkNIfí1ç)hì˜?Øûí„ò=UË,/îìHæ<8ÅÂ;áË—Þçàƒ2¿Kâ³Hÿ ñŽhNvó‹ÂÝÐÕ!xžùÿW‘»ðåÈŸÍZÔ©]í oGXÃG~§¡¢©ümÓ×ÅdRüÅj{cã'Õ­9ËÊÉ×|!eÑL8J£ÙÄœR.u%Ç1Ó‘›ž£A­q 49›×Ø4ˆdp¯Þs“öŽÄ3+­%„ÚZ ƒ3&ªùk.äº -N–ú¹ S5Úÿ`ÿ7DA¬HY€Vu`·Ä4%0aÇ›a®È´8m ‚¾ÓYgzã&€ÉPc$$ãÌF(âådOü?†Fð.sƃ™}TÃ:‘Z$›µˆhº鬈%”£tøµ5 b(” Ñ ,åmPltácX5«yÍ`«Œ8+èÀQ}zÛx@w¬¤|ïs¥!Ý?µSzŸ}#‹!Çr¡xáäF׺=¦î˜}Cæ`„ù‡îR™Ðl0£yÇsƤ¦6·MfªNY´#7ÇIÎÔyóG× ç9ËÉÎvj“yü£;çIÏË­3Δg=÷ÉÏäÁ3ÏÜf3Õ¤ÿßa/zýLhã “O„i³a½»žó‚qO….¬¢&§Å™«U]Æ£º ä S‰7,R0زhB1Z,n”¥‡…–µéP’g]³©RnÂtDñÜGùxÃ"º±+§ìU¼œS1/öT¡?MGP¿Õâ1G¨4-¤ GIúާO¥fUËÐP…î:3]ãMöKÿ Íl ëJÿ©Q}"®–ã²!ÝÌÖÔ«—¤ÔÓXåÊPººÔ®„­CbK;<Ô‡ dìó$KÙȆ®®Ble7ÛOÍf³Tå¬hG;Ã~«“¤M­j ª²~u¡«-c=[ÚÇfÖ÷Ì1h+ÛÞÆÿtu‡½­<(4Y%2l¾M®éx =ОñDĪrÞ¦æ*÷º‹0­ñ`á†Y÷"šÁ®xϪÝtr—^Ò½ˆ|²:Þön¸œ BnYx\X‚ʽøí(t÷ t:j¶³,kóK`ú7¸ßÓ1 Ì`vÞÓµÖm°„zàøNøÂ~ðTÏ< {x³¶m‚Õ[)íöÃ(]ˆÿë±á¢T½o¡®CRLcÊ­ÁcáowOX\m±·Æ@¦a…7¹è¢mºàýq—¬8 9ý:2Œ™öE&[9F7vf7æ‹“W®'9|½²˜±“e"¶g³š«Rf/x¨î>×Lç8KˆÅÿ³(Öú:"/»ÕËYjeýrȪKªÎLn󷜾òÉNW¡â"yÜG\:Œ•Fô>ã"C$m]{IxéÓKJyYö»4ŸÁªiürD:F‘ÛÚåAF£Ô «›5%ÖÀ¶°‡MìbûØÈ†n=_]Ð}Îë–z‰©~W—êú–:×—ëõyV|éc's[nõŒ†ßiÿHÛ,t‚óAϵuIÀã`—ëQïЋµÝð‡Çd È?}ß.Ç}íÏnÖ¯ŒG‹WSu¥ÙUóšòÝîŠý:àkäsŒÄ ÔH¥ÐpšjÌK¥½ì{^v&‰^òm¾ù­QšÊQ‰‡ï‘([ÈϽœg'që•qf¹íÚØføžgŽƒ¸öNogòa¶_Ýöº½ÛQKT5?üf¯ûçÿ[s~ðüï^¿¿½…}ºƒüòŸ¿ßOÎí£[¯wRQÞ§bõGâ÷oñwqqÅpÜF=áxŠ÷3•çj¸éw 8ôbyLå)fäAH×c 'Yí—}ö7zî1{¥Öx­ä€x³­×A x ,xƒ&rV…~‹ #«Gs—0Á?V‚Àõ[<ˆ‚È—oç€^Ðæh¾$}Hµ47€Mg€K¨„=ømç4M@EY¼u‚^¨}N8†ÝSYe¨ƒkx€`˜ÙÓ…\è~h¨rxgLˆv˜‚¸‡vy؆ü·ƒ_¨†€(‡‚H‡|x‡*˜‡zx†X‡Œø‡a ZÿÒ_-£…Í4ˆo¨ˆj—†éÀe„×€áqÖFm,lj0ä†׈?Ø|y#XX%‚z3kc³ŠžØ‰}Ø„÷‡ƒFvz'·Q2¨²D*Öç/´ÇŠ›ÈŒÍ˜£§!@ˆ‹}DŒ»XëõH‹‰„¸‡Ýè‡n'%…+($›bzWXk1È‹ÞØŠÎøM…(UÍ´u[Ød»8L÷ˆñzðx`WÔŽú Ž-Ecf¸’ÈŸvéŠÏØ‹Ùþ˜bù y‘Ééý¨ìHõŽ\D&ÄÊW9i‘ø$Š ˆ\TR¯òJ¦²gý•Š/Q[ùØ‘+ù‘¾ÿè’±(…³x)#4i¦VÂk§ö)ù • iˆâXtã3Œxe µ|W9}ª²-oÖ“ù“îC ŒÓÈ{ä,Ž‚…ºfÔ‚ãPlH¸wb9–¹ž4ŽF'“üRªò@È…ß%sÐS‘@™—‰©—ÉCH¡ƒ˜e ‘,ɘB¦˜|t’©’˜É™–éˆÉè“•¹˜¤ùŠŽè€w)”)•Ayš¹ø”¢9š“9‰§šxYšž‰›båš#I–¬¹š±éS¼‰š”9›°œ¦Y›ué”Ày›ºÙMÃùš¾‰œ¹iœ­©œ‡’ÏéœÖÙ’®i›Ùœá)žਗ਼Û9ž²ÿIaÃyž9Ôù›ŸˆËÉ“QŸø Mš¸Zî)’äùžð©Ÿ †–$RŸayŸÜYì™a™Ÿè™žšœîµ™ ê Z¡·cíE¡òÙë١݄⅊ݣ¡Ú`¢':uó(¢0„¢¥¢Éå¢+Z’3*†O£B£9j£¨ƒ£1ꣶ¡£AÊ£7ê˜Ui¤/)¤bE¤¤¤CªYNš¤H*¥Pª¤!ºŸ‚c¥CzLz£•3¥RJqXzQ`š¤\Q¦¯€¦iJ[]Ú£ ¦¦iz¦ï(2¦æG xš§zº§|Ú§2 äbû"=ƒŠ=9d iâ§Šº¨ŒÚ¨Žº_Ð--6`FÿÙw&)*÷:º©œÚ©žŠƒ‘JV_–*®tzÚòQâ3RÃÈym”¥ °«²:«´Z«0¸š«ºº«¼º« P«À¬²z«½Z¬Æ©¯*¬Ê:«Äz¬Îš«°¬Ò «Íú¬Îú«Ó:­Õj­½­Ù*­ÛÊ­¼ª¡z’[y7„1Ž”TêT'†yWÉú­Â®âê«ñ*¯ÀJ¯õ ­÷Š¯Ìº¯Æê­þJ«ú °Ø:°¶ °ãz°û¯ k¯åj—ð27Ky<™Wrw3Žp‰¨ÛÓ¯ +»¯ û± û° K²±²õ*°$«²â:²-k²üê±ë²ÖJ®’JF³€8CG5ÿ!OäPgE³k³7K´k´Ï ³E+³ºÊ²5ë´üвÃ*µ,‹´Ùª´Çг¢ºA!ÓBkTìš,W!´®JµÔ*µ¸Ê´M«¶lë¯Z»µo‹¯q°X ·V˰w ®n{«9[F0ÉK¨©ªUlópvâ8sK·j‹¬h[²}‹¶u[¬PÛ°“Û­XÛ¯wK®Ðª°WK°+°¢Ë¹&˵ö(Z‹+¯—{²’Û¸©›µ®ûºÚ»ó¯‹Kº‘ê¹zˬ) ²½k» K¬«‹¬‹eª%»³¹­«¼(;¼O ³šk«Ôš²×Ê­sºÉ:ºÁK¼~›»Ö»»Ãú»½¼)»2kºÑÿù8Ôºmû²”»·Ãʾl¹yë»ã˸}ë±Ø+½öK¿”û½ó껯:À⼜‹³Zû¹á;½À;¾¸[º~›¾Ûq¯ð;¯"û¾tË»i¹œ´±«¿¶›½¿­|À¤‹³¬LÀÌÂܹq«À÷ëÂ4¼½Þû°è+Áì°À¼¾"Œ½(|ÂŒ¬¼¶ÌÃÓ»ÁõÛÁ\¾ÙÛ¿þ{¾ÐÛÃ5,ÂiÛ¬ÝK¼GÀ+Ü¿4|Â7ü½̾MìÃNìÄN›Ã];Y—©5´<Œ­À;ÇÔ;ÄX¬Åúм÷›ÄQ ÁP\Å¿«¹L°TÜÂ,l»7œÅa,·l¿‡üÈ&¼ÈKÿ«Ç|›·Å»Æoœ ¯q¿#ìÀœÈ¢|ǘ›ÁqL»L ÄŸì°y«¿…½^<Ê»Å*œÄV|Ë~Û½1LÉËê¼F|ÉJ¢tŒ$F%Ïò6â +mŠeLìÅüÇŠ<Í¢\ʪ«Á}ŒÃ.üÇ|ÜÍJœÆSìÈÐüÄW ÿLËË+ÅGœ¼à Ìr3êµ± 7éÏž'F\u¶3 Êã\ÇÂ+Ëk‹Á×|ʾtÌÀ;ÈLÎÞºÊkü*ÉÎ¼Î•ÜΆ‚/éaIÃ#¨ŒîRE¿‚\vÊn\¾=lËË3Ðçl¯¦<ÃæëÇ-|Æf Å߬Î-Åÿ›Í"ËËÊêËÜzRÄÑ-$Ò2K­©«È4MµBí¸R­ÐÍK»OÝÊ=-Ôj,vòü4ØhT s,’¤4Ö–Ô*,Ñ?Õ̳ù»ÓQ]ÕàlѽܷîLylSôµq`iíiis6ŽRÒçÖõ+×ڬغk×kýÖ ‹¼¾|µdÜ˶Œ×Xªw æØØtíÙp­ÕŸ ÎŒÓ]ÑH ¿‚lÉ™m©r¥ÖùŠÕ£ÍÓ³ÚÌZ¹A-ÛüëÈrÌÛÿ<ÓbœÚ7Ÿ Ü(ü²¼d8š‰œÛµÏݾuMÈÞœÛѽÏsÌÐ,ýÏÁíĽÐà]Â-¼ œÜAÿfÌØ¤ÍØ[°íÓííÊ*ýÄ?¼½ÀÝÖ*<ºÝÂÞËÝG›ÊØ]À⻤ü½ym®ôÜa <RTC™M)îùÞ }Ý\ÐÕ »qÍULÎüíß+›¹ÍöíÒJ+ÃÈüÛ³làÞd Õ³àTÑàWÕŸD]Ä@ÎÊë]Û‹ÝÀ íÛ Ñ¢íÉĽÍm`âº[Ú*MâÇ â× ¾âÞ‡lÃY\Är{àÎ#?õÂA³¸H¹fª¡¢?È´(ÄÐwØìß>¾ä½â|\Â%>ÝØ<ç$þÂý½äµ|Û)íÃWîÒÿ-ÜeL¾t>àäÍÖï«åsXWó€ÿ9¸Èâcye%䮌gˆBdb&Ýæ!®Þ<]Ùòkä¼­èÈ-ßü äDå)ìéƒê-~´Œž=˜7ªÏå4còC–žë¨y¨;“{è&¶A³e°¾Ø®À°üç}Ü:}çëûç†>Ħn·[]ä£}ß²žÑ×*·Î©A±™i‹e;bÊ…6yÌ‘Sì|þæíÓ·œêë;í¯~Õ~áÍ~ì“<ësØEøœ"¥Kë•R¶8¶“Š’å®îíŽï“lÚÅ¿)Íì¶Mï -ñÏî!®ïh’ã%UJt±”5™ÌS6¡†I,ÅL;^µõŽ¿o¹VÚ=nñÝ­ðYnØÅ×Ü ›òª«ÛÐÿmí;ÿò¯Í6ó­Ãû€ðnþósÝó÷îò´-óïô>ôD¿ÃNOíU?ÑL_ñJŸôYßÕÎÚSOõ0ÜFÿécó]¿îgåPÏãŒñiæÚTB©M½;!ZödõÖŒô@¯÷¬ë÷3ûõRoŸ¥ybh>LŽÄ(¿òŠ¿Ç‹ïóŸáðýÓHK´–¯óÔùnߨ‰ïøÿöj²?·hxShk“K¥2m£RæË†ìÞL[­F ÕÐ û#\Ù?ûa<²·/¿¸O½±¾µ»Ïû¶oûB¯ûÛûÅoÈB/ïãmÄ®ßÌüÖ;üp/_[eJÞý:ek”tÖ;ecšÛÀÿïÜÏ/è+MäFìçxîáyËk±Éíz¿àpØ‚Z2.Ž-„cƒl¸5Y± _-ÏÞ~Gneuwxk|~%-)ƒŽ…7o}p€/fv‘™z••!‡sŽgœnˆ–Šr8b`TGCVTW?RUZº´»H¶±¯ÁÂÃÄ@£,hw„¨l& ¬˜-ʧjžÎ‰qÑÈÓ¥Õ7Ø«ÚÍtÉÞ,’ͪ¡ÐÛèdéÌ%ì~ÿ{ÇòñéiÖë©íÈ*f, 0"PúвW R˜8ôÅäW/Y3j$˜OE ~ý`Lš4îR„2M¥Pç†d@“£æÉÒä¡—‹NTH¹i%³E7ï:æóQO¢"[ÊH O}!“݃³ƒX>©¨õᬶ¬|­¸$âÃZ±LDˆ«Û·]â™Kœ3>9£ÒÝ›ÔÝÝlø ö [ ÀM –séo=OŠ#mŒ÷ŸÎÈ‹c&ñ8\1m¹„þìc4éÓ¨EëŠ΃k׆`¯fÝ÷õëØiÏÅm;Fï˺Ëô¶Íû]pÂÃõ—{¼8ñá{Rß ZºZ®Ö³kÿGx¼&t ·g³v~-9sÚäÙ??>y3÷ìÑ»¿\|{øàe_Ýο¿ÿÿcØ'Ù|žÔpâw r Ö·`k .÷ ƒ–3a†·€vèavña–^‚!˜axŽX pªH`‰&žhaŠ2z²á‡8æ¨#10æâ|=fV£qÍ Fˆd,.øãz7n„KVML9†[YT·£‡AòõäzÖ&c—t}ù€>&©ä„f†‡¦bmjh ]9g•X sç– ¾)¤‘d–©f ÞI(=Lúù§¡Šz™dto™eVÕÉDdYIÝ¥ ±eDZ5lÕXe´)ŸX6*ÿ裪î6h«ˆ2ºä‡Za“-²e¤º×EeQ™ËD QV¾Nª‘Â뉵"¨³B-„‰†)æ­×VËí®Í^äE¨JÔ2KXÆŠ•nkU‘%³RRë-·aƉ"›¯Ê‹á´ÚÚ{d½ºî©Ñ¤çlX–*”´»¬¥ðÓí¾´êKq¶ø²j±¿¹Íj­Å[ .VÀ$‹gW¢Rzî w¬¸ê±Ä sLd‘¬z,kÆü\1ÈÛ~+0Í“š<ݰ(_·U¹HƒúË”P'ûîÌ4ûÜ3Ïëü±ÖN2·@Û<CÏ\6Õ¤y-öšN¾Ê5ØoGèuÐ âÚÿØnwö–¡¢Ía€.øà„nøáˆ'®øâŒ7îøãG.ùä”÷í÷å˜g®ùæœwîùç ‡.ú褗ÞßÔ¢½"ðÞ·Pɺéø:ì´×1ê'ïˆî¶Ã5ûÀ½/|æ –»»Ê›:Ý÷òžŠº{¦Æ'ýðý÷;õØgÚËíRÊrÓ½î‚Z yß©öÁ\;úì·OöÊž6¬¹á‹å„¢þî§§ºÿëéþ  ?³?ˆÁïx¼°ŸW†.™P}èÃŽtL³…qYG~ôÝ00h0ÎA°4Ÿ{ Â|U¬«„ùsà6@Õ(î:‰K–PƒÀ¦­|Üœ …åÿÁµôïSKsøHMqåyçë_ I&³êEáˆ$l u,§Ä)B1!ÓKžˆ)^TQ"ãKZó8¥Ä ºk‹(+•­Æç SÑ+£w¸Ã%Ò0ƒ<¢ŸÍ×@ü50aÇ–yÂî)„ÅJ™cQÀ”Mñ`{D ôp¸®Z.´#³2éD²`ø« ü>ÉHR/ŒcqZ S(I¯” Œ.tÈ%™Gîé1+¤¤Ë4ÉKíqr}}Ôe0í'ÌöÂu9ZDsNäã¶åUî}:»ECýê#ýðÀ¬'¬{½ÁVÿºØÇÎ7²›ýìYWë3é fºÊíp¯·Zišè­’¹îqÏ»%¹œä 7ý²‡mhvÉ8ãW‘ՇǻÞÓ¹ßöä^ý¯ÃwzÏÏZŽŒÏ|PïíƒæŽ¨?Ìô¶Ÿ‰ä–Å;ìšO½ôh§÷®õœ<û›N…«þö=Ť£Ët]ÖW• ÷û†zÜ_«zBtÅr” ÷ûôzhE˜sðøØïaåêÿXüì{ÿ½ô÷ǯyô’ÿüè/i—ÓÏ~öC³¨¼Óõ½zwhŸŒ«ÔÕÛÛ¬¥î·ßË„F2ôvVÖuŠ—>Z‡€tr€ÿ‡Z”×t¢'jEclT8Vi†×|èöhBæjd4T}‡y¢…l–Å?LsJ}÷jˆ× ÈbÏr4wL—G]»ƒ´t]º…A3Xq&nìVy9èƒ6d;55ÜSr-è€}tnÄ„m£ô0æGbí¶_ñ¶J÷Õn@ØXBØ=0XbE']ÌFrWh‚þ—„PUpÈTƒòVtur†pð†qÈ%iÆW´·…%Gqbl h†@†]¨‡jakµ„Ç•@’ÿLJ#&Hõ“ˆYÈs[ì…ÝF`~ˆUÌöw.U}8*:'P–ös6‡DÏGM¦…cÒ—ž¶PåyKxyw‰Œ·:t—#T·z´¸‹i¶\1'X¤_Æ‹ÄXŒÆxŒÈ˜ŒÊ¸ŒÌ¨#{c‹¤Š4E†ûÇvýg'8'ò׌™W†xUWËDpÏèDC£ÖH€ÜX‹ÈCtX‚ÕÅVk¤ië(`ây½GIÆXó¨4ÒaŒ…P&È‚éøu38a½%>kXCǦN´v_˜S7nöxò¶?R€ùpBu[àS_ªhnë3lx¨´‘iH?LjT$^IGDÿia‰†zÕN{V{"×z_%„?¸qŠ$`/‰u)s¼‡j öo‡`ÒøGM•‰Š¨^i…‰´R…8”P7j2wcQxAsœe„Ò•¢6sLçX)¸cˆN´tX¹xtäŒo9—|ƒy²~ïC—z¹—|Ù—~ù—€˜‚9˜„)<[P|ówŽhÕ“WRX6:8V‹©ku&”…Ùh©e™‹ªS}ؔوŽâç‹ßx™K5dšxd¡ÈŽž¥lK'Y«Ù\!XsZ¹V¦*uxò¨iè˜YÀ™x„§–¦™U/˜`A(\èÖƒÎFy¼&’Ï)[;è…‡sÜv„‡äœžü¼%pOWœ†çF^ÖäˆãÖRËI‚h©n°‡ÔHmW•;IJß5cKÈ„¹¥_*™™à g±Õovxqù[æ`ÿmJiøfzó™ Ü•h…i¸gžÙŸ·£oúv‡òÄNchˆŒÛ)(F\~÷z߆B ‘÷Pbužxi¡ú„C·j–&cKÆ’,Xm§¸]=Gжi›5I+©?BWX°¹£ FW¢0 eU:eèMºb¥}Þ(¥a;opensips-2.2.2/modules/seas/doc/images/image100.gif000066400000000000000000000132441300170765700217760ustar00rootroot00000000000000GIF89a­¢ðÿÿÿ,­¢‡   ####++22###!)!#+++++&0&(2()33-8855 ==$3331=12>>===5C59G9!B «UAš%.€¼£ ó8È=²‡üc"IÈFÒ‘ˆ„¤"—D®€_:ˆBex«\‘ñ“Sl£–Þh#h‹]L%|¾¨Ôü! &XÀ'µÅ-‘ɆÃ¥)ͳK^¢R•À¬N%·ŒÙ²EÀVð¥¯Ƭ‰ ¤¢åØËáð0˜Ø¤+õ#…x‚Ø*ˆ'šì[€¡8+Ê6V3bq”#þ48Ÿà!9HÄuôÐÿñ9@Ž pTGÉ èþ†©ŽG”£Œçß Ozʇ÷D±Ï~6蟋 r.`‡VGÉéþ¶‰‡^¢p„D1HÑÞü²:ö„>Ëp‚Ö9.˜@ZÀQ-$ @@rÌÐÓ ¨”7”S@`uØÂ ð= G ¸€ƒæ@ÀÁ{ªzÕŸu¨ 2ðEtåªW à@À ëbhV\Ú˜’’CÓ¤æM©C8H„°‡Eج‹¨ì"|°„<  ²9 ëd+{Y’a9zÈ©e‡E$‹ÐÃgÚ²ÿ"çwXJS›ÑÊjK@Jm›‡´öµŠ@B:u±(@@àƒ„#„MvtØ Õt7<³h|0ªS1 èM€>—p< b`rJk^ôf@½øaŽ\&È7³(/rê€Ù÷à6¿V{3ðhBxýïPúžD °G#á¨!á­ÈvcƒË&–¦Ýõîb§S€Üj4b€­rÌD(‚ -C æKÖ3g¸ÊÙ/G#Œœ ãuÇ–Åìpàÿ"‡Ð'Ž…›œ&YŠ®…{âŒ@@!‘… ¤ 4€‚G¨¡ÿ©„à° .päOøÎ‹^*ʘÎTY'F1±ÃGò."¹‰ÈƒT*R2ÈØ5¸ƒÖ+ÚE?Ù¶¨mñ|YëZØâ”~ð"|œÔ–4³cñ,}æä}­n‘“ˆ`Ø(h@`æG42õA~#F@ @ó#ÐàÈÔ8‚Ólö\Ø>§X…×PˆÝCM¿xe˜À€ƒE$‚`A v, ´ ˜ ÷¸Ëåæáø¦ƒÆýÖ.‚ î†7•GÛá’ø¶ƒ"@ð¬99hfW~ßS˶~Ös¢a_ yÁQ™òì ÿЛàCP ÏFj`#ŒTHšØä¨/…É |@€¥Fìš@£0Û;\1ƒ†œMg 6&øò#„PÝGB[xer†Ähèîœg—E›‚ ”¥`”¨EùÊ !ˆBˆ>.™–çŠF?ú ›þ'F>òï‘ü$¿qŸàËŽÐrÄùnЈHÐTЧ˜ŠÈ²w•ØtxÈ(@~$±‰Ø]? ƒê¡€¼1Œ1 ŠS˜‹Át‰;AÈö‹0µ‡ù§wÕ¨wËÈŒª´‹RŒÒdwòdŒ£‚‹Úø ~ä#Î(-,hmfò4O'XŽ>âwWƒg|ÄQá6$ŽâéBŽòØEéx9Óȉ¿…“,`p?Z$éCúühb”AŠp€–,_À4‘p”ñqŽ!B’Îb’?2‘OQ‘I׎Ø×’i**éYÿØ‚0Ù$.ù’"9“¥B“@ÉE™<ëŒ; G×ø?yBy5ôxÏr“M‘“츔9Ò“ÅH‡}7”‚•™%º¤“XYJèKY“,T”tHY–ˆu–d˜–"•`©–ÐA•LÁ’š£•h —qI—xYOÙ#lDnùIi#~‰–M9˜Å£—K‘…ÐÔr O€©b‚ ™”s˜–ä¡)£Iš¦yš¦É£)š¨Ùš®Išäñ«Yš¬ùš‰𢹛³É›¬¹›RâÃÅiœÈ™œÈIœÇ™œÌYœÄÐ9ÒÕiœÏ©œË‰ÔÙ×ùÜyœÌÿùœŠÑœÚyžè™žê©œ·™ÏV—Çaò™‰_7ŸöŸøyúY’¹ŸþÉŸÿ *sZ ÚŸš f  Ú Òâ : ¡ŠZ¡:š¡Š“ú¡s±¡ :¢E!¢$z¢@a¢(º¢;¡¢,ú¢6á¢0:£1!£4z£,Q•£Ðk[çÚjg®ˆ¯t¦¯¾ ¬\gU(°aé:®åª«(Á«‚­iÀ{ƆlÊÖ°ð°`vlÉÆªÑèZ`€lÒ±Ki@Ìælè:«$»uá<FÀ°a!ÿ‹±»(€0€4s²òI+k²Í´à°[³€^%Kl »°§ê²$÷(§r(Àrâl`µ)·r2•*=pE«lW ¶P;.ç0ç²d6¶ûª±Gµ`¶gf©i›µÏ–Q‡ жoKb«dK‚ûž\kµEÛ4S¹µÁ°)Ëa]Ûu“j¹Vëuaª2•ug u#‹¹ªZ`'v•ë¹Î*ºÅq <Ã3$—uy»¹_0¥{ºSûª+»¡»•[µxëk4^‚:¨” y‹Ë€»V‡«ËÛ¼giÀ¹(ð.¶–*y”·Åvy~ÿægÕ+×[¹Ê[·PKrÙKfÛ+½Ïû=ƒ µ[ky–yk½¢û½÷kr‹ë±Æ´k¹’‹ÉëU+¬ÂWuv;­ Ìa)€)ðZ·à=¯=5|æ+Åw À²Üú{Á,ºáå{¼½ç[µŒÁüÀ<+XP´)[|V…|Î6®'LÜȇÀ]kµºV°ç;Â+\ÀJ¼ÄLÜÄNüÄP|¦,«¬ÊÂ1ª1ÅÖeœêªXÅ.Q«1«!ÆtÛfLZ<˜—®ºÆ`¼¿Vl¬š®ëúaþº­¾á®ÒZ­wìÀÉê¬ÐÚÇçêÇìªfÞ¯ÿÁÇ1È‹­[kÈÁŠªêÊ®€gˆŒZ`fi†ƒp»aâz<“X BÐSVfy›É ÈqLs f¬ 1Ë{@ë¿‹lBp˹|±Û4k³UL·¸lu¼ç"«l½ðÒš‹ÃÇw¾…}Apª­À|œÁÕÜÂ]«Âª]u¥}ÚÌÁû÷ºÉ |ŒÄ›ÌYprELÊáÅ3A€ÔàÍ 0É»×{$ìÛ ¬Ù$ñlY ¯,aÝØãGÝ2ÿáÍ`Ä-Þâ ¨ÒÞÞê½ÞìÝÞtAÆaİÇT|Ä Ñ³îðÍi um\i¼ª™ÝZðÅù=¢ÞÆm­gÀÈ—Œ® >)PÎà¬{<È­È’¼Èµü áìkõ|àV¡ØãLÍ<ÂÂ<²ä\aýë<Ù4£ÖÇ,Ï¿‘ÍJ+­<3´7®ÌÑRGâÞ¸ÎÃuíµX«µíÖ‘ŒÕqV ­ÓH®¶ ÔP[¸‡ËÏ¡“,äQÁѺmµ9Ô¯}ÕÅáÈŒ»Ï\‡Ô°-æ³K3½‹Â»}ÎëåQ‘â'.pݾ‚í½ä”Ù“Öy]ãn}Ï“j×Ä6¾P¾ZŽ®èmçKq  àæËÚ0|ÛU{AÀ²˜ÇÜÁÊ3µ½»³ÍÖ‹ÜÚ–J3ŽÀÃlê¼k®’ŒÄ ¡Ý*ÑOëqq¤œäÝë'¡Ãº>ìÄ^ìÆ~ìÈžìʾìÌ!ÿ STARDIV 5.0 >,¦;opensips-2.2.2/modules/seas/doc/images/image101.gif000066400000000000000000000160161300170765700217770ustar00rootroot00000000000000GIF89a­¾ðÿÿÿ,­¾‡ ##++33<<###+++333<<¦ãX'Lz¨œ!@Âwhx«‚ÿЪx­’yè©gÜc(p©¦À[§J©ð€bÔ(æ~dU°* DF@«@çÙ­•­´¢¶ª¥€¸®Áv@–´õ7¯Â¶+,±IÉ…À¯îÖkïœð"uï¾üö›/Q4LÂÀlðÁ'¬ð 7ìðÃG,ñÄWl1Åg¬ñÆÿûRƇLð$,òÃ&Ÿ¬òÂ$§¼òË0ÌñÌ3{ìÈ„0ðŒàrÃ! P±tD0pÎ-  ÜôÃH“°˜È<_tË`AÌ\sMó×ÛÜÎ!$½tÄ!ðóÁkó[“óÎ=¯ýôÓ »­³‹>‡ÿì¶Õj³€ôÝõá'ƒ ¶Ø,e¬7Ý$[ Øü-–$S0ÀM‚ä’UÎó P`øÎ ŒðÀÞQóùäp÷À\ÉaÂH‹à¬Û˜ð‰œMðå4Ìs °5è”[®Xñ$hιÐÌ‹éÒ‰@âÜ‹¬ø×Œ¯„s”¯´ˆ0 ¼ô·†08üB£¯>žà, ˜¾3 ŸùD`¿õ mvòÓÙ›ö8“ñ¬#˜@@@‚HÀ`ﻕñ°½  m÷k 2xAù©( ûJ ¬­{0œØ÷h>•8nn,€€n8ÿð $``ìÓ!}è3¼@g {\wØCîiˆÞGˆ7ƒE r$øÀ6,ˆC|@ʪ6Âh®Š?D#u¸EüQ‘‰lŒ¡+6CŽ †)!Ý´W¼–}@mT@¯(4#¶lgx!î È3BžnvFt_Ù†CHLŒd„@Ê,€Èuo“nsä#%ç3S.’ìsdÉJæ6Iîñ– ë£y’–-‡ò‹ÝúbT<( h>“@û‚‰Ä’m2Œ¿L˜Þ’6f†QvN#AIð&’M rû" ?™,”±`•hÖø4½ v°$&ÉÖ‰Ln¦ð%£àßpÉÿO‡érc>ø#/I"H. tD,Ùg 5¦¡žC¨Ù8‚þ1 ‡•üœbºÉ§ÕŽ·#Ü (z2ŒcŒžG¶P(à”´Ì¢DІ¾4z€èfšÇ~úaÿÔ˜@úè` èADv°€ˆ€!OuŠãXFU¶UUšW}aÊDPNÄ9ð¶«ñfùÓ²æ2¨ªSw€T¥B„©Než2³Úõ®xÅ+ZÓªÖ‡à¨CUª À€ÌÕhÀ4 T,Àhª[ØÃ €˜AàÂÚ`²„5l_/›|ÖŽ…¬d…Ò¯Öº6X!*C `€Úÿ ©;`|ÉîÖˆ °ÛÈú ·»ííPk`ÈÀ8(ry+Ù¨à³8h€†»[¬6(¯ ¯x—”Ù*ä¯mA.à€ ,@©0p€°d@ ÊÚë€÷ô DmÀ À_ÿ T =(€f ü~(ã°„›TÞ†5½(È ÐX 0ØÁ ÎpA¢Z¢2à"&HT£šàß—ºDŒ´, Œ 4FË‚ Å–” Ztá1\v—@ ¡"¸KŽÓ"ä K#æ ¨lÀÖÀî@‡k D| èÀðmꕳÜ×?ЖrØ ¤ÿÅÜõwc<*Á `èB  ô°€ô ZÖ#—=ç--PÀ8ÓIÙ¥Ée1ônâ"ŽÈ˜V1`*XÅ.€>è.€ 4XÐÀ$;Ø|Z $VqûÙV¿Ú+~³‚Qk€t€t{( Êõ°5°@Q0šÆéW-0Ds£š®gA†ŽÀ·ÌÓm'àK* V•–¨nMÝNÀ·ýc D@ÚTµPlEÛÀUº×=é”%¹ù@ŽU`¤Š¦Æ%p© 4´L ½öXæMè±”à3H±žìe7NcyÀDhßtäÀ˜ÿŽ2PÜ ƒ ÄXZ((ã‚Ü™UþYUÐ4>ø¸`äjqÏiüdèʼn:›TÉËR‚ R`=$P:{Æ2óšúçIújŒTôé0R: ЂÙŒY“ òã Xà@¢‘UY,€i=D¯9†^›÷§pÇyÓ\°sôœ,(ð`h>Ÿ“K9å*÷ɬp*³–YT'Ë&¥(CŸG‡ñw „m– ¡ ûÔÙý—"`kßÞÒÇ’€ö вg½¤é½јõd·wë§nÜ“ '˜ÎÛž¡5ý:7ç EK±þ÷Hýy˜ƒ}ík~RcÙ¼ÿ„<zí¥ ¥G Ÿòb„?‘þ¾„„盿-ð;K×7td¨æD%j°±%e€€#Øf!PqùSLJ(˜%¹a}'ð%å''°&âG(Prª± %Xzà×"Ó·zõ7÷7qN÷)òqrì—iîgb’#dxP †Ö‚¡G*¤×wð)Çn%Ð*0+p-P!nQÈêñ„-`hJC/r¢±°õ–yIr_R…Ç¡|«b°ScÑš‘u7׃5s9r6g„ÆQs_Èg&À+@瀇>%öço’³x bi5hƒKÿÕTQ5……k Wqap±¦9˜‡š'DÈfˆ.èo@§ÕvþÖuÝö·"%PSˆpUØfߢ¥xŠÓ¡ŠÀŠ!¯ØCð1mf§¸˜dÑp2 (0 —´&¼˜y7.Ód‡qÅáŒ'ŠAˆp”ö!ш'puÑ9Àf•ø`Ae”h‡u'N&öÑÆ%$Ðù8ƒ±…Žéè5@X ðŽ’HyU–ZÆõT ¾ö_ UYáòk:ÐÐ^%j‰Õ.G‰µXJEZ¦…û r«3{+ ŒˆŽÇua¡ÿ6\l¶ÒåÆ%P XO5]Êå“le\—xT›k>@/æVñ[ÁPØ¥]71“Z¹•n±~èba_ÁO%–0UC©VúU`=`–Ê¥‰l¥rôe@R)ñ5_ †`»f\ù—€ùd^É~f)fI– Ö`0&”Dbña0¦‰L©‰oFl±aV-vW2p%2Ò6†c‚(i^øMÆcàsÝÁxò±dhácܨº!d d†Öofp.PŒáF¢Dqqf¡£—¦ìñ¤Ì¥"Gr¿qƒ:aždasqÿW ¥˜빨ÌÁ¨¬âw^7xç‰eÀ%G×!ƒWx‡g_ª:7<”*w¬·ªµ¢M‡ŠP'uØFx#÷gcq¦5wsrg©¤Š© hdwŒhW"áÑUwwF¬DØÂѨTwª© %ŠÇxˆš¨/* 8zkq…’ª‚òdahnˆe±!e®7àŠåçoìJ|ïúy£øB‚+¸µGn;h|ç…3R£§«s(® H®Øx®{–|Û‘†Í—¬§~§—}d×}ª~à‡¯Âg¯ãHèG%ÙšŠª±â`!)«°«w®«—®(þÇ‚‡ÿø¢ý—!ôwˆ‹=»˜o\"ˆæ4¬þÁ€„f°‡°Šâ² r®Ìaxè ‡ýA‚¯Ú"[Ë­LKdÄq³0ˆŠŸ2²*¹­ˆBˆè1„f¡>Gƒð‡ÈOû"AZ²§„_²³£¸·dh³£(·žH¸Q8…5׆‚…I†[(À!`$„K·^w·ìÆhÈ|lØY»«EÈgwȇ~ˆc­‘Ò*?x³‰›.`¶Y‰¶á÷ŒXÇÆ¢›1»ç§<Ú†/Òdêj+P²ºë0¼ÁÈz@h~à˜-„#Ï˶[º*Ãø«×B¹rI& °ÿ¡»Ù»¬µ_Ç+} Ê蹂X(ט¨èÜè§(0½žØ¼ÓQúº5Ñ”±‰\–¨’¢0˜öÒž€ÑdAú.ÿ[›x QÀ áŽ4¡ÀõÂÀ 0™$îr<`a‰(yZ„¢™˜Â†¹ •`ÀVÀŽ.¼Y ™‰•Ãù˜0œ”9Ã2ÁÁF¼’$lÂÇ%Y×]X)ÞÑ& k° 8p™ °[”Åàe¶açÅÞÂÍäKáÝÞ–SíÔ2Õ ¶Ô…ѸvQåç ÿÖË;¾PgÓC•Ç1Pï¸0À-ŽË>@籕Gá„láN¹äxžñmy¸µfÐe\æeɹœÇµfrNÕÏÌŽÐEeÞ ž> ž”ˆiük; ™æ}uX) ^í•å¹ÍéV¬œá|ãÀ•ãw^êHáäó}¡¡6j¥vj á„Ú”:à^ÀšYÚÅí+ÚëÀ‘ ð“0„èN ]p‘É.bÿíì|œÛÄÞëÖÞÔ âÁMæ»…f¤ë¼.P8ï¨ÅÐ9ëDåïŽüT‡ÎaO­V¤>ð5Qð¼mÝÿ%ß°Â쮘QõðúZ îsŽ™Ûõ“Çÿ™ë­>êŸç×ß\9¿ÝÍó fÏi¼î*ª˜=ó×~ô·MƒÕþãòH?Ý=OèK ;_bJ?Ü>õA[ `zHN¬mÁ1ÑvÎôWÿákßô\Ÿôä:b£Ýó=œÌ) Òªî•Õ[lÅFÞöC•õ±õô…ÿö|T;`ïDÜ[Б„<Ò Ù"éßSOÅNüì,ž\M@,øRæödoø£ø]ïjï>–ÉÉ&*P'•o†ù¹Ì-Ô ¶_=­TðݦOö„ŸúNüªÏèÕ €È–`6¾ûlI—viûïœÊQ-ÔSÝ `¶ÿ^üüîýAúª“€¡ÿç}%èÑ– ôXû…¬`Ò©œb¦êsµ€ P¤òáCà@‚ ˆÐàB†6„QâDŠ-^ĘQãFŽ) Øá£‡À!P€ÀRúH¹£‡H3@¨p¥= œá Æm4X°€ _dHèÑaN©>”еêV®]½~Ö¢e5Øô£ÁÀ ÖMÊÁG HK2ÄéC+×½óöåx•¯XÂ… FœØ [ÿ†˜ê`‘3ZVœYóf·w0¸€Öï䯘/“®*ØqgÖ­]¿†uÿWÓSÖ‹;önÞ½}o¼]ÚkðÔQWÿFž\yrâ´‡Ïök¼ørêÕ­kn®;5ôŽª§_Þ·hÈÜ¿;6\zVñíÝ»¾Ð˜pöã|ÓkôÎþý~þˆs„œï¾îžË ·Ú*:°?ì­¹ @õêÀtˆʪAm[/:G$‘¹Ùèð©ºa‡0h-"®‡ Êê¿õ.0 rèÁ%J°D#,Œ8~©È› z1Ã=,P  4hJ×ð… BÑ#rI3ÏÌ µ£)0(gLÊ x ò! ÓÍÊzË%êÁ´2Ñ$´Ð:ÿ ZS% /{s l„Š¢àpPÊ­tÜ“ X ¤DyZôICG%µ;Ôr`’‡&(Ê‚r0àPh¬†È“4D<øòÓ6!´TaGm.L0X±U{ƒ/%%ml*H3-èNPHU"‡7܆šÃ)B½Ö,4 ©ÏzÄÝqQs€n¨‰Qly€ñ…¦|0Y2Å%¸`úÎÛ®Jtp`0õ­r‡“ ¸Ær8÷Û‚7ö`ýìSøã`ñâ¸dR=ä‘IÆöc“_6ådÑ 9å•‚9g$eÖSeçZNYg¡ä™UšoÆ虇fú½¢1õÙ@íÔš¦¼§!>úçš{®ºkë®N7k©ëóºlåÀ¾é©¶îYm]͆{7´·¤íôß6:n½a›AÝ–p¶÷&;œGxá‡'¾xãG>yå—g¾y矇>zé§§¾zë¯Ç>{í·ç¾{ï¿?|ñÇ'¿|óÏG?}õ×g¿}÷߇?~ùç§¿~ûïÇ?÷!ÿ STARDIV 5.0 :,Ž;opensips-2.2.2/modules/seas/doc/images/image102.gif000066400000000000000000000725051300170765700220050ustar00rootroot00000000000000GIF89aJ5ðÿÿÿ,J5‡    $##$##+"++0&2(22###"+""**+++&0&*4*-9-&00)33.995* 9-"55 >2%::#3332>21=====5C58F8;K;6CC9GGŒ{µoؽw .7xïàÄ“+Gk|4pç’—KŸþµ9ëÊЩkß®Õúoì×¹‹ÿÕ;ìçáÉ«_Ô¼jäìãË×éþvöùøó׬ïú¾þÿªä—ç½G`€&øÑ_é}g_ƒ F(¡E†9!xN¨á†–a~Èáˆ$Fæ!ˆ¦XâŠ#†â‡¾È⌠&ãqÒ¨£~…ñwãŽ@òØ#Bä‘êõè#|H6I’PF9¤“Tjå RÆ`•\nç˜è™8`—dJ÷%‘þUvb™l w¦Š~)X›têö&Œiйf|Ò†'œŒöXt}ª×XÆ)%¡†6Z¢K.êè¤wAЦk’:ƒ1 u²ˆ@Atr‚@pLyÂÈCŒxBé«@Yÿ gk‹Ž‰Ä2ËtÑ…"Îx‚D® ÄɧÎÑ ªŒ¬zê@ÑEËhÑE¸zÑ…Î0ã,ž0#mÌt„"›ü,¬èº$럿 ÚcZl@ 3^xqB3Œt! Až‚*j4ã̲uÂë"Hdá…0³ë½Š¸ ¬" 3l°3ùî›îÆ‹±›¨ASnªH0Å.ã 1‹ *'*ŸÀ²3‹42°Ê½®Ê› 4rÉ;{á ¹©®zrÊÒº3êLŠl°Ì&ðæs¤Ð¾8:3šÒ”$Ùãä(‰¤]R„g+!߈6“ÍŒGè…¶Œ Ð/-ªŒH02LV R§”$ERÌÁÓ*ˆJåsÓL'Nò™T‰æ‚¦(›jòñšØüàyBÂM‡lS Y0S)ÆpBE›ÛìQÕ˜;Ì󓹕µ€ÿz²,Ìa'8Ýù9%SJ‡$äý²Å½DJ Äh¤"Àö=ut$ÞtA›’чÒ“ì$ɈϦ…r¢CÏÒQb¶s£Lii‡³ŒÎr`µ,©>sÙÏöò—9è)5"Sg¦K)êæ–X¢ HuŠRƒZζdtªå_T9z´ ÆåªOuêV“ÚU¿Á¬}éX‚Uª*ó«be¦Z×J1“ábê8Òª U®G¥«RÚª§ÀàJW¼’}‡ÏEÄÑvžÈ°ë…a$ÿH´kd=sZÈ”°£PI` RN²o¤¶ôš™AŒ|¥TYèqRÜ®ªgžµ 'ÒÛ‰6¸u%g”jyÓ–§QË—ÄV5Í®xúµU-# ‹`ÛITÙÝK~¼Ç¼«¯(‘þþ—»¬€ß ‘~ÅlEpÃ:á#…°Jš§bíÉXg K @h†ôš—…çÙ‘>ÖjZká/X0‚¼—ýÒÚ¯ ̘qüæÇþÆ5´,nq¬J ãÿ•ì¹"=r¯ZFÁ”yÁjDqìÛ Ù¯DÌÑfÂöÒ2j¶Ô??uÂÂK‡ÖìØ”+ X¨^9ÿ¼YfÜK€«³Èn~³‹1:Í–ÐÙ¥kVq›©¬gŸü¹ÏjN4D äŠü¹Ðæ_USòh£ “Ðþ4¤?ûbÂUÄÁA`FøJåµ ª+ÅR¤° 7Ý“¶B)yfV'rö¿áÙ‘Äöð»†,F”ÚÒ+±óª«¬iVÓÃ=ª˜Á:!,™ýïbìSvÁn,êŒ×«ô3ª)¤êT»Õ|&ÌÙ¬NÈÌ Á˜à¸WÆäÝ5â× ;u ½=è;›·qþ k˜KæÙ–[êÜ÷0®6æ[:ƒm43µ¶çÍíçùÞø7¢…»ðKÛۻņøœ‡ËTy+Ú£˜¶²Å5Ž:wÜãÁÿÞ¶£»Ýp’sšã[ŠI¥ãÍð•;<ä.‡É¡c.s”ƒüâ™~xÎgòè¿h! NC•^’-$@~ÁØÀуïƒÃjh†mIy4•S¸¿ºMŠ€H. Žöœã× 7œ÷׳†™'l½ ¡à|›PU®vÕË.p‹Ø#§7žq.öcÔ «©Ò™ëÛB{ *ph’^N8Ûã:Æ×+æÕëèvÆnENóac\è…߸J612¥'ÜÕ²@ÜnðÚìÜé^•扡…Î0”£Ïxéx§žÒ(éDÙ‚ Œ³¿^Žº¬oA"?0©gÁQoºÆÔ¦€ëÞ™ÿ …‘K ,Tø4>Ð[~|ÕsÅqÖÊÂànú «¿ýÃE¢Ï¸~ôóÿû×JyƒziF|§wXolÑQÂf€ö× ˜gE€ßT¤GHqoÁ€^×þW€Øuˆ€é$ø!(‚HÙfX)¸_pu‚¬Á~Åg‚1ø€ È‚uFƒøu9¨‚nÁ5„7‡ƒAȃ`!%6x6x€H˜„Q9^õƒ€æ‚JX„R¨ƒeQ…V¨pXèƒ-¸…óÇRf¥…cø„\¨†dø‚ÝÁ`_llè†=؆eh†vÕ„‹&†N†v8‡dáizˆ€È‡Wø‡X'‡†u臎ÿ¸‡ˆ¸†€3q¸958ˆÜå8aWJŸ5sœX&žX”HˆÄÄ ŒæQ<—U88©¨s­¨.¯ˆ$¡XW±Èˆ1ФX`Ó´‰KU‹‚‹&±ˆA2‹´xŠ·(Œ•èhy8N(ˆ~¶Œ‹ÃxVD؋Ȃ™H8¼˜=Îø‹ÝXVÒ¨#ÄgQè…ÉøuæØŒFië¸_í8#&·]ñ„AÖ(Töxø˜ú(T52\Àð99U° ™ ¹ ÙW% ‘ùJ"‘ Hi‘ ÙR©‘ I‘…á‘ùUòX‹#E™’*)’.¹ 9/ù‘DÕ#39‘ÿ’7É‘:¹“5I7‰“#ˆ€°’FY(y”Jù-”“€á”B)i2é” ”VÉU™•B$R “3H”K¹”ÍT”cy”Mù•#Ù•Q©–Py_y•á–ZÙ–qù“¡–`9”åx–h™”~©’i©—S)EvI—l —ˆ™˜„É“~A˜{I•й˜C(|f˜)Y–˜)˜¾™ I‘ž™†9™”9šžÉ“¡ù©–}¹™HI!®ùš—š¢I!´Y›ŽF› ¨›œÁ›!ä›+˜…‘›©™ÄÉ’¹š¿ œRĜͩœD圹 •)œ“qœÈ ›ØÉ”ÉyšÑ)_žáÿIü#žfž%–Û Æ¹ƒÙ˜‘ä9Þù i  "ÙŸ™ò‰`ŸùinäQèÉR–¹žì ˜ØÙ”¸ ˜µ0\i° *÷9žû9 Ÿ© Š›ø™ ¢yà¡É¢&*:. Ÿ!‘œr€ 7i Àã8Ù!ÚžØÉY¡ Y 3ú’ÕTĹ´° 6 pº€ 7°"л  5 _:V° | U:„œ @RÎ Zu“ ™¢ŸÉ£-ú¢ÜÅ ¢º@¥»p°2P »0Sÿ`w`2à§¥“A¨Wš¥$*T w° 7€¥Zº À0†ÐPŽá '°' ]* £:ʧ/Y ¤¬ÙŒE*¡ÇÙ”¶`¡° ¹"@>€ƒÐ^Z‹Ú¨ŸZM¥vPɧTw`ÛZ¨7°­†°»@`º@ƒš&³ë3̶h 9²°§…Š0Šª Êʬ»¹p¯Ùº ³PUê60¨z  ›: q­“¡­»À­qà° ‰±ƒú©q0°° =À˰6 0§!9 “<Ê"@"@y$ÿ ©»`pTÙ$ॎA§AÐGpÖæ ÖY”ÀŸ €À ›‰¤°Ù´ ð´Q;µIª˜³à¡x®» µ ¶° †Ð0ù°…I0Ÿ"Àù"PаÐS€<* 0w »`¶ z€}0©p¬e£Ìˆ§{µW»£³` ®w€áz¬»Ÿ Ào›³`k  ¤Ÿ)±ѶóÙq;·u;¥ )·1 <0¨I"€sÀ›à*P«_7êóÙ;Z P¶°à³a{®q0  ¨=+¶Çk¶_Âlœ ¸Î 0€ó À ›ÿ©™Ýû½á»µKz¬²Ð> ·…š1P%¤»¶q­Š9À£s€¹¸€8@Ûê¿° 80$ `n“ØË ŒË¸ôºp¯‰ vº ê{<ª£€”§ŒŒ¶p¬3ê¢ÞD±pÙù» û» .: ¿˜Ë¢„P¸«»ñ:¼¸ê²v`ï ¸°=0Âfë³vð˪ãÄàZÀ ÚÛ…:·@¾À ' :ð ÿè`X€Àð :°$°š©Å\ìÅ`,ÆÀ@ÆfŒÆjÌÆnœœSʧ°`$쿳3°Çjº¥Šÿ™…¼ 2€´À£¸0º‘|±£; p¦2Ð4 îÊ>ñºG¤ ÃUZÁvÐÈ%È80Ⱥ¹ 9Á Ùµ [ȇŒ£K¿a©p©üȺà¢xÐË8ð˵;9ì ËP³/pÅ Á²“Äœ}P4l㺠ÉKÈ ÁÍÀ £´bW+ˆÀžÿØ ÿXt ·êŒ  ÿè šIÎ `Îè ê ììÎð òLÏöœÆiYo€T€¹•<ɸ@ @³»ÐÉ4yÀ¡ ™ 6 3€8 0Òp° "=PÒ²pû, ›€qjK=ÿÜ!€Â’«°»à…üУúÁ[Ê i1@ ÄœT ·›Â&—¸ÐÑ=̵œ 40U½Ô7|̽rP½×@{ £Ñ¼£MÑ4«“º = E¼ pà³LJÑo² Ó Í,€@Å ± ¶Ø‡A °Å)µÀ@$`ðôœÿ˜óüÎUøŒ’‰ŒíØ-Ùõ\Ù—™½Ù ’³°É `e{ÒPÒhZSÃ]ž Ê C¯:;ImÒ@±}®àÊ4ªŸÙL?¼£³]Û·ÍÑ}˜;#`= ¼0[ÛC[0ÿðÝ+õ1Ê @25Љ…øÀ¼@Çè|à ÀP’];@Ú÷ ;@ùÜÞY”ð-ßô=ßmÙù½ßï©—Wu› þ¡ ºÜ½éàç)áDÍä=pœ„²^@«¶êª°º/Íp´I;|À Ç‹ P¿ð 5`Ïïì½Í òí ðÆŠýR›â+ÞâÏóìß4žà¥y¢¾œ ªÛGŽäE®äÉ­ ØÐ{Ù⣟â%{²){½áÌž¼öĸŠóŠÖ`89ëíßL ¾5Ðâ÷=ß@W@À @@ýíÞEÙæo®q^ @-‡ÿºçB~—FžäD®àBíè¶Iá,éFÍÍpœO›Ð 0Œ»»½ ¿;ÅUìå¤Á ± l2Ðê‡XgFéÞ÷ütçÙéh³.òÝØ¸NÇ‹.• Né¼méJ앾äÏiìDaæ # ÐO­± ;<ßÎñæÈ̸–:&Íg.ëg‰7KÞ+Fª”æ.è^œÝ©ì“Žì£³ÛÅ.ïËŽïÞäI£Í ¿t 1ŒËêËŒÌ{Ý×é§êlĸ»»aærHLÁªîïüþèÞè¯ñŒ®ÜìÌ> ¼ €WÌfMÖnçáÐ{«ÿI—Áí¬.m›`Þ1:ëNœÁž¡ù®ï"oôGòû¾ñLÎôS1]–u1  ËÔ'îØW@¼7^¾Ò 5Ðÿ ö³Iö“cï6ÍökôÂäöFQ²¶Za*@,ü„ó©s9#ñ$Nñ‡À ›eÙ ð:P†ñN?ï#ïJ?ᑟì“où¿ƒ'GkýFUßó[?8Yø®i¤uPŸÚpOL ò™ïñÂá•_ï¯Dê‰Ø_™FJ~Ðúiûq/÷˜?ä´ï”T+ûÈŸô³¯C::§ßûÙž: ÙÂÏAtßöÛÿ_g´¹üMßüK¯–àù—¿½K;ýBŸ”?€ë©ïøjYÓÉOôæÿüÎOþ¶oü’¯—娞ˆáLà@‚D80†n1„QâDˆ^L¸ð‡Ž_=~´ˆQäH»LžD™RåÊ#]–\S&J.m:ƒ9SgÊ7mæÜ´¥OŒ@ƒêìI´èÑ£C•>…úH£¨7 ôr3«B«Àª4ÐÀÄG²«DAƒ5Ê–Mzö¢Q¦1¯B¥[—¥\…zuÞÅëwf\¾8ˬ4ïá]„ ?>+ŒäŒkþ$˜8ó[Î?]Ø™³æª‹_ækú0i«ŒW~.íZ%ÿìØ²QÒm%kʽ}cnväĉ,κPÄ™p Hš  %H@”ãì*ú-ï¢Úßâ–«Ú/w’ºi>¯¼bó&É¿lß}üõ¿íßGxÓÏ¡žÌ-1¨è2Ëλê#IA²Þó)½ºP /¾] œ+> 3lÁ5œ¯½ ñ#15gˆq@ Nü iTØ 8ì Bð'A ïF<ÌMÄÛ£ð, ™â¾)ò¨ k3oÉŸÄÏ¢f8Ébƒ fƒçA‚Æ+½|IGŠŠÌH̉F,ÈÍC33ú K2(2ÓdsMÛÚ„2OÌ–y®žûÓ™N‚ˆ.ÿÿ(P043¢6Ck”¡9[ó±°8ƒr2°#í¬ë—ÙtÍÓó,/S=O5±“ NÀ€@ã08Á™ € QëÖEE‚4ÒI7‹TRûòD™1v%SÙ[©Øc“ÝET÷85ÉÙ˜} Îf¡U ÛlµÝöÙoS“ÉÚ•¼Ý Uu‘dׯ`ùU¡wÁ“Ã]úÀÁ =¬!ŽZ¬°¢]aÁ)Xˆc/ROÂW_+f€ã_+Ü¸àƒ¹=iY¢€êƒ‡!–øŠ FøÓÞ·_‰&Ø`6yM”ùõ`–-~9æ Qêøãˆ¹å‹SÒx]£1ƒ3A_s|7^:ç ‚]ÿôHØ*¬Ø=(¦:¦¢)5 j©©ÖÅj¬µ¸ëqw;ê©ã(ûê rÑ#m˜yw±ßÞÅ쬷ޅ8br:?”öîÚo­)|m!SBn³ç®›ï”ê=UÌwí®i¦ƒ-|©“`‰ƒŠ>nc]d¨EcO·ËNÒMOØ ÕYwýÞ»ñ^xôÒùHø Õgh=aØuf{—ÙaO}õÖžpÿz7iyÔo>ðÝO Ý •f^ø]ˆïúøQ3GãvÃlzz¼æí>£“ì°Â;f‘Á BzhC‚ìkÉŸ²Öt*Lá~3°ÿü—»Z ÐqLÚýìG‹¤Nÿs ïz'Áÿûåoýû_‘—<âOd oc'RЂ L!ÅÒ•>" =JkTáz•C÷åM7˜»ÔNâÄÁøÐ6ñóަȥ¦6±(ëcè>·´ßQ&_û‰…†HŸBðˆ êP5U''–± ››û¤($ø DÔÓMÄØ#ó ÑŠ„c©Å4šÑ7häÍͤÃ6V±T{dÌ•¸D=Q6Šl’% HrŽ•ÏøFF6²“wr¤käJ>"R0}”dÒT©F*–~—|?ùE8>’”‡Ñä]ª•©´yUHWzΕš¥-k)Ê[ &—œLžm°L=QÿÒ]k,&6{ÓK¢™Ò™°ü2“™È<ÆÑ›zù%5…$ÌÎió}Æü#4c¹Èyšóœuy&=½¨LuÒК¬ä!á¹ÍC.“™Åe3ý2Ê{2ˆòìçÑþÅVòò•Ç$ã8ɉPg*T=夥F;ÑôMt˜}§;=:¡†©¥rZ©KczHÚ“¤™3i;+šÒÎT(/•8ÁfS‘.¤üŒ¦lÒySõ­’¢9œ"T1ª›iÒÉ“I¥P‡R޵«eªDÙ9—‹–gEëY€µÀ­o}« ºº5iM« W¾Âu®s¥k]ÏŠ¼ª•­{Ý«_Ý*Wƾˆÿ…E,_÷ÊW¹úµ²k…d%ÛWËþõ²€„ld#«ØÆ:–°š-]'ëÙ¿Ú5¬buêIUú›fÄ?·u†VŸ´Ô×öÖ·¿î6C;\âö'¡m+[»XÀV–·5Jåsƒ;]êV׺M=nsÛŸížJº&ºnxÅ;^ò³¼6œäyÕ»^öž÷»e,®q›øÞöÖ×¾÷ýã}ãKßá׿ÿð|ñ[\3ò7ÀF0€ \CÃ7Á†p„Á«ßÐúqÁ#I@°á›TU.›XA¶ò%¾|mÄÕ=±oR\e`à7-&ñ€ý;\ ;QËPЇÏr‚a„B:ÆÈ×€LÍ!wX$0öÿ ’…à 3¸°émbˆQæðG ªÁ„!/°Êñ€€ãèa_v†'N°ª%$ÌcÖ‚NÐ…x9×€"@/«™Í¹qâç´Ù ]>³3Agg ÃÅvNNš×\èà€@ÐaŽsþ#MHúÏ)³ ¾ tšÐÂÎpdtêM›ù˾Î@ÞLfNƒZ ®Ȭi‚4)ð‚‹=]èV‹zГ^r™í _« W^R6š›ýìñ6ÙŸO®qà€@@ PáŒeHÜà„ bErãx„À—…«el`2ŽfwÜM¨ Lº8Iw'ÿBPï{»x?Ü%x£…mª#äv€@´0î%ÜÑïnB4 œˆÜöVø „"fÀãÇÆŸƒrœÈ{ÝNi·¢áíñ̸œÜú–9¢Xœ€ÉhÁÅ,7ù‰Ð f€ßñvƼo>j_éJ'ÏlòZûÚh®ÍM©ÅÙ„•½ÔŒ‡»ÙĘ̂‰”¾ ¼˜^ØÁ]“~û¸FdçD€ð"¸£HEÏa»ÛáÎ › {€.øä``)ZFº£42þíî I¡CbyÇCð HŽ×KÞŸ²/IîÎ@{ï¾ö³§ÌVÆ÷&@¿w Œ¾ðÂØDB_ÿ{µ;ãôøžQï7q(à‹×ê%MkÖµãdiK`»ÎÉ^Óþ2Óß¡R}ºû›ðW"{ö2¥*ù^ûùˆ–“®FÈhFâ'ù­”ç3»K›?~ü£eÔÀ ò7?»Ë­²sŠê;'¹>Ö›;s3âs1ÿ3ˆýýü“¾µsÀ+I@߃½… ¯ãC>$ '"ïí3ˆë1ÔË@‚ˆ:SþÈÀe(€63´Á9Eà7dÀ!;¤Arë“?YøA…h¸¡+<0´ËBä“Ü F¨ÁA)”.A ˆĉ†c?,Lº'ô“fÃd#½³Aäÿ«ÈÀtüB‘çX„ŸK€`ÈÂ-Lº¡+7 3>YA5Ó€ ЂLÄ€1s†º#ˆ\Y· ôDP”  8?ÀPL”.X34sFX7@EO›ÅJÜŠ;\Ì-ID+$ˆU¤ÅZ< µdlFPÆX\³YÄWÙÅ\ÓDRt’Þó= EË•os38‚,6Y E°E @3u¼ÆM0GtÄ@Lôµ9›?ã;0 A ó#$ Š§hr,Œ"ó-ƒ¼ ÿP÷ëÀܯ‡Œ¯<áGd7ÿ1+)ý*[³ÑÇëj‰ü7\‘Ü-Š4É“Dɉ„"’T=’dIúRI””əĩ†ü¾– É›dI‰I¥±ªZ’ËX1‹•L $±Dq‰CƒÔ¡|­­{’ŸLI›Éœü¾—”ȃTJŸX’6[ËÃÊ`7ü’¢)-TŒ‘Ë׺±
ö¢Ë ÃÉ»ÌJ¬,Éйw3›ÃËNÃÍ+¸3j¸ÒãS¬  [ºWA?ƒÏXÙ8Wq •CCœºAôÎøÄú5/áO…9$ :–S9–k¹¤£·­#C£3Ë0·§ËÆró·çÐÏeÐ3Rˆ÷Ïc$œ8ûšÎðÀÎꓼÌNâ¾±+»®äÁµk»ÆÃ¼ 6‹XS<ÎóÂÓ†EÞ›¼[A¿‹¼½Àˆ­OÏÃPÒʼ%³ˆÜÛ;¯k†&•R¾{ዾ+¥ÒÞÄ1o̵èS´¡<+Õ;,íOÿõ½½0=Ë3,Còl4DC%·ÌÚ˜CEãÑ®Ä;<”%Ã?9ûDÄ ¤Bg8Áô ÂÕ¹mż#ÌRèÃ<ÜVb ±ŒAHAfh×?ôŽ„Ž)3Öe ×þW (Žw×?LS@LÖ$T˹ÔU_uI<•ÎMGÿRäS^ÔoTFkÇ×´Øl,TIûÄPÍylÐ^ì=c”‘^Ua™LXé‚ùë½f@Yì`ÙV@TœFõ²/Ç›7x4ÒVN_ Ew,UÅÄ•µDõ3Æ.Z£-“ÅÄŒ]”1QõÕöšSIRÈÖ„¹¼/€ÌZërXììZé¤ÉS\3 Ð×­lÛz‹µ\ÕÚ:åU¼ýÚûØÛ¶õ[¯mØ­ÅKˆ]¯¾ýÛÃ-/Ã]K½U[ÂeXÄ…ÜÈ-ÀeÜëÄ[À•$¦¬3E•¡ ±¦DCŸ JuBÊÞ]#KSÍ.ÅÅ®¼­Óµ-ÜTZ–¸4š°LÁöÚ%Ý} ÿÝEÝhK]¥®´}]Çu¯âÜ´RãÍD¹5cKÞØ<ÌÙäMàäÌ‚°ÌÁdNÚ|Î5/°Da¨’`†eW›Í]‹5Çœ3ß³Dn¼¾d:ëÀ1 ðéSNì5ÌOëÏ$Íæð.ûÞðŒå͵öe>Ø Mä|Þ(ÎÙ¬³üÅÌæ$¶ÌHLì¨Þá¥\;%QØKýì¸ ÎžÖ½P ÏUºATÐP”ëÐçøÐÌH7FH€Ë‚E _r38+ `„¨;7äNB¼¹}k´X±ÅN[Ñ}á úatËÅv†.ºfWœãN–ºg3Р#ˆ õánb,f¸ÿ¤{8"Þé"^;õ`÷BÒ÷;Ó.ý¾͵9î;ÏãÒÁóÆÙ%<ìcS¸{ÓpR†uk-ØakÒ󜲽 Jm=pfF$ð„ºe@á äéiä8¶ˆCˆDν݋»I~¿ \SÁ[ׂ@ÒF>XNfeè*´HîH´Üâ½\étU\RY@BTQ•¿RÅÄÄ`TZ¥@[m?Ð]¿QVäˆ;aóÔ}äI&â=Hh† ðŠ›Õ4Ô„?L½[…fQeU|ß 4f‚0çQÍG0ñS`N#-Sã»ÅåÊucãM\l½’{ÕBP׊»Cw Bx-ÂD )@„…Vÿƒ%Lµ´tf¶P”:Fø¹rÂbbÕ9iifs€¬%|èŒèÜVNè±èDn±LÔ‘¶ˆi­Cr+Ø6Š€ít=X•.B#\Ö!ÛÛ`×½Ó]vȈD ¡eF¤eÀŸ] Új¤EoìÅaœÚ‹­Úh¤E¬uæ˜ÅFØ€#ð¹VlY‹%ÈRô7‰ÝÄàÓÆpLT¤[„%¡ZqYVdZV·šÕ9²–Z7›Ø–5’mNkÜÁb¤[!Ú`ìêXÁX°æY¤½ ••Ó¤nã7FÛˆüÝH¾0[É%íuaÝÖÔ^Īk0«ˆ(º-íØÖœD¼Ü^má›Kâ’íÿÝ.íÓnÝ´íÁ½ÉÜ®0Þ6nÈõíþªíánHM\³ ãÝ!Ê©›Êƒ…Š KØ0:›¦¢QÝûX¼UÓåíävíåÞ•æfa÷‚,ƒ%Ë/œnËŶã‹,Áî‘@KTi3±\XþæÜÝFoônæfj÷Æ+›ÞQÆgƒ;ÿ½¾NvàÒlõíôe`Ø$ˆâ¬p] LÁœàé­5ú›çåì³Ã´à[T4Fs4Î4àÎçÿköpÆ>{n[b"õÔaŸ1c?™6XiEÂ:j‡7|Aˆê,”K÷·@IXÿ§ði#5x0Ÿžh0ÉW„øv‹?hø>ca@Bg˜è/ñ÷%#xvåh‚ÞèóÃ%ó×lå[Öjjšþ[{Ÿ‹#GðB÷ZÀt¡ÙsÄ€ybüÆMôB½¶ÚþxZ·îؽ¦Å%¡ÙÆFF½–XºVÙÞZÔêk_½ÁfÆ¿klLìPäê¾¶jèØØÁFAuw©oÅW稟ìô[ÞUCnçŽ]ÈíãÆˆÑ‰À‰£|:ÕìC·\¿Oü؆íØz|ï"ô½öŸüÌ×|’Òùø¶üžßûÍýÑW¾Åïû¥v|ÒWýÕ7íÊ?ðõÆ|Š„ÊÐ=óö‰ê. ÿÛpÐðÊÇÀ}¬p‘¨­/¬ Gš“J¿íüö vÐWr” ^_Ôr‘¨ï‚ þÇŠê/î÷‰ë?Ö›°=þ넸d¾œw}ܾ|Ÿ¯¯PC3¾œÍÅTÎŽëVûËàÌÍãËEv1ê*€8àÌ™-G6x:؃Š.ì"àD„Z‚lÚ´aexù(,ˆÌ(:ó" E'4tæéDJa, zÁÙðfN‡%Ž©¡Æ…5 &Eêìaĉ,=‚i#E— ʤyè ›1u¶LBbˆL»ó-ܸrçÒ­k÷.Þ·eóòíë7îÞÀ;Ëÿî-HQðßÄŠî䌘ƒfˈ\ħ#›!\KíäÊ— 6vîdµÎš£ì̲3f.› ôiÃEI;3ý8ÁJ/R)nP ÀqkÑk99c”`˜³,‹Þä¬ÓéeV¾¶QoÊÛuÿ\*ÓÛ½O«÷Ý™·ïÖçY†~=º8õµÖ±›_ž"3eåLjm2ÛA2¶ ƒ ÞU˜ƒú…˜g,–ŸnJ¸!‡e…µ‰f±¡™œpÆZžx3)2ÕLp­HÑŒ-Ɔ`mºÁ¸›n/Ƹ ýGœZdT£‹“[#Z씢Ž?: Â@pvE'(’Àÿ@*X ¤R%¹ZŒ>Vh&˜äy‡äAø±%šSVyåj¢%€ Ìgmqø' xA(¡Šå¡jHa¡òÀ•Ãæjœ ô^YhQšâŽoÍxdˆ6Êv1qc†)žêÌŒC¶©ZŠ›$Œ 9™èª›ŒWY(²+¨aV¸êªï墫”º‰è{;ê*¤BΦ #µ&¨š£Ù:¨¶2ZØ…‰2Ú-¹z'Ldg‰ô«Yœ5#€‘ï–œ3ìFy#b™åt±Õg/pÍYY,œî(_KÞ9ãfŠêÖ;S´Úúäg›Å¸Lbéû]o´Ægoæ%¼Û©ïZ|æÿÉ Ç½ö.Ëãªk¼“ Ó9£E´u‚u‰”o¹A3Æ­Ð ~‹!¸Ì*Zt¹tq5y„AH×¥„#ïItZ¬PÝV±'ÔtP'mÝ&TåÐGhaêÉÎlmlÜa´ÁNø™|ä_ï&ñS·R4·™6 A‚ôt?á„Á­vww'¼[§’SN£×U/ŒmE°÷Mˆ+þDpÄJ¨ ‚®;#6ÓµçE´íŠ-.Ò‰æ®-”6c-¹W*’Åïy|ƒË'ÏÒðÏKáôºûž´†‹ú^= ¸sÿ×Ó ­ô}\á1>ƒæ£O>ûµ{ß>`×÷λÿÒðÛ?þùë¿öüó*?ý˜5.ÿ°€< ­—@¥+\ÜÞ¼Åx¡bxñÈ ˆ6ÁÍ¥‚vñ`]'ˆ0#œË óByÕƒ‹I¡\\˜ ÞÅ…0Ü Â"-*ªõs`A Ä!±ˆCä!Å%ÆåÐÙõÌ’Ã$Ö…‰p ÆæXH—÷¡ðMxq"c¬ø0z‘Gv!#ëÇA$îP‡»{`'„@Fd¤lqø“™Ô,:IG ”¬¼„+|ü NÂÂ’«t)~Ü Tˆ2–#œàÈkäV(‰$p›Ð šAIKÊ‹êŠãiÁG6¥&‘ÿÄ(ׂÊP%$Z¸HF(ÂJMrò:89›!½BËØ—„ÜÊ…ÉÊŠÓ‘‰ü£P¢²šQ2ýCàµǾp~Ã8ÍÓ¬ƒ*ì';þ±cúÃ…¦œêTSÞÔšszf2+‘äA2³O Y;¢ô¹šRf‘œ¾d= ¥u®7"[Y`ÜI‘úÀæe …glâÏÔ°¨ ¨k* º ”?uÕçŠrRÛ˜%`D(¬ÙÍüe“¤óûËLÛçfˆ ž H¼œ%§ÔˆFCµRïu *©D2ÌRçÔ™ Ã;$2‘†A¥ o/)ºjLwbJDÁh\‚ÿ—bª©£žèdi*Oð ,¥Ô¨LEªVUÀÕO U4 Z^&¯8Ý5¦n2HT›j2ªN¬Ö¬6ÇÉÊHh„ š±/Ø,WÀ’׳⇹}–%øÑTi¤Ç(¬–¢\Š°È™F‰B®½T\Æ:ÚÐþ·t²Xú[¿² °u=Ògeû$ðµYÄ-.‹;ÒL¹µ´OêgUK*©Ü6‹lÌéýj D’±¼æ=âဠÁb‰YÆf%/™É7©ð™iÕbú†¬3áÌ"¼³ wÁ !œéÄh€³2x5ðòÄú””ÅSMdPðk&•Ám¸,s »,ÿß!ø ^¿öY,Š´ì2‚]âÉø ÅÚeÄÐüïk&Òàw…•‡àµŸxÈ !"êYDÁÈ„HÓ±°t‰Û áHw¸'ç÷=N¾S0{„,x§lPËšÜDçŒ !HXðàDW¶L'joÑí½€¹üÈùVÅÒ\˜s#×#uN$‚½òjÈlfä­YR÷¢eŸºÑ)ÙpL®²•§ì¤,«`Ëñ2Ö{͹?̧5-ê 9oÔ¦ŽP¨Ù×饥z0­>5¬¥¾XÓZ\5ö†öêZóº×¾.×®«‡kWŸ·Øæý5²“­ìÜ{zÃÞ‘—-íiS»{>–¬6%mÿ_‹1.k”K·ßòí»Lp…*¤á¹ÑM‚{'ãã¸í"CÆ€0ÜÈn¶ôž­m|ÐÞXûï*R‘/PJ£]‚ço4¶q¢ C8}>2.ÆÕŽbdxSTó›iØùtxy¥„(e0=qÊ2£IVz-Šð`©”WBš7¢ zNî LD#¹/AÎsÝÜ)4?fÏ“™JE®™2gzŠŽŽ¨¢7äèìŽH´K¹Ó“¬"¦HHb”¨„%±TúÓ!NëŽÿׯ~;ÜÑ«?Ž´Ÿ`nOns®‹%”6.ÅÀJ×’Nƒ@æ ^¨àJíf–ÀôæRäºö&øŽÞÿŒtï¤ñþPvZ¨œe(¤ç úýþ=‰xWjÕDëÔ#È®Êñ‚.]1Å’LLR–TñžaÛÒ,ÈÛù_yÂÌÖj­˜†HŠoMEúaÀ–Cd'X‹o½Uk½–ãÅ\†HŠÂ}E.WsYܱ‹ïœÊ^j髬– Z f «”–f9ÈÐ óņA¤à4ÿ †xµ±³) ÿ X¿4C‰áÑjíYA€Ç0\L†Õ©DFŽUX}EŒxԘƠ€ qFÀ0zhaŒX…T!dÁåÊ(‡…%€…ÅS=ÙXQàX€ÙŸÅ¢ŽuaeATÕëñHËh†Z ÚWŒb²1¡í¬šƒ[šN0"ÄÓ¸™ ÍÙTT—­¢1 úš–½"E`˜5BÛ¼Íâ@MC̼bˆbžÑM£áÙ"‰Yœ=Îé0Ì’Q¥YZ­Ê-JEaFn(BˆÀŸä±ŠÚ„ÄÞáéDY£Ú&ö^ö€Kܵc^ܲ•ÿ<Îc]p¢û( ·”=*Û¬í£?þOú^¡èã?¤AN›=2MMuO¤C>¤¯%dÑ,$CÊ]¬™Û×qM»E\Áy›Äá…¿ ŽÜD–䣨c¹£JBa­É–Ãí„ÚÅäbüX0ÐIæd=¢¤DêÜí„'€™_0‚'FrÕ¥šd] 3a…3 RÔ çµ’ÊMÄÕÞÍlQÁôuÆ÷åˆ(UR5±„‚è$Z6Ü ¡ÀÝ2¸MtØ fXÇ"x‚'8"„i]0Ã2\6D_¤C©^#°žž½4ªéݓ¸Haþa"fXÔDXNDã½ÿ…k¤¥gb§ýÞ¶›'(BC(B'%ãÇ]6BðÉñxt‚iÊMC0B:m‹[ÉŸ3ð¬(¦qX_aÍ߬T%š—óQÉþõ_¢„ZdæcpVqæH}fZödаe[['xÁi¦&]^xxŸÃ"t"ôL½ð‰w>…zŠîÄ‚ „@ãjkU£[eàΧ^DŠ0`&‚˜`wÁÅYZçgb'°‰æhšn¾epÒ_*€ "'lBhÁ&0`jÁ00hU0öK ÜW}9"$jQ]í—&Žâ¡lXŒ ¦è#¾äÍS¼¡Žµ!gú „!¨N*ÿ(¹hçv䆬V[J“µÞApã1 ¡£I£C¸âcŽÖ‘`),jc”vã/ºYÔ€Û\ÚÕ¥F$—„*iI®$œ‘¶FœÖ)KJÈ2T§›î)ŸÞc¶éQŸ*¡¢ä@ Mܪ¢.êþêÎé;2ª¤N*÷8ª£ú$(Rª¦njþ©Bf*P¢)_¥Qrª©žªZ¤®*Ü)‚„Â¥ÜÌ%Ep‚]â¥^2_ú%`bhtDªþê¦^ªûÛ]¶'jªæ¬²¦'¸&lflÒf0Þfn+µRª°~ªeM#@k½€'KtÂx–çy¦'E({VÝ{V«º*êµb+eÿ̓Âê„Vè…fè†v臆èˆ~e˜èºþë ¶ëD6d 0)Àìž ìÀZä†ä)Â>¬›*,¢2,ÄVìÃJìŸþ$_ +‡¨æ¤X,ÈF¤©ÁtAytÁØ,CÉ‚',Â[âŒYÀ*ʪ,®vÁ`Ú(‚0À*'(«ÌåËþ”«JÆ®ŽMÈ&m@ŠÚÛ1zºžknŒÓÊÄ•¼lm¢¦±véDlÎ&ÙPhÖþìkàåt¼,mŽ­¹Ú¦Ò®í­š±¥mÕ˜çÖÁm[eÖfícŒkºjm½|'¸¶D0¨fPòí¶¦­Êª*«&®â.­¡>V±C0d"lÀ2h(‡RÿÄäVnÝÂìDèÌ‚.æn„‚èWd „r’ˆÖë„-¼vÂ_v Ìdµb¬ãB¬Wþ”lY^ìí¶­Û*éï’í¯ÑÄšð¯°)ohæ.Á2o¥Boo·<¯ôæÛõúõ&éFQRÄ÷fï‰o£ŽìÛµ¬M¤ìÊ¢ïWAÁ"hËšllmìbh'쬞Šïö’¯ ˜o±M-Ô>"Kð•œíáצÊ2°'œ­ÿÚ`‡WðžÝæíÜò‰çð­Œ"ݦ­w‚ð‹ê÷ï/——äR®åŽ®æ²pÝfMÙæZ®ˆ’è OÇBãö°+nõž09ÿoòžð¥š°Ãä¨[CV“61ñ"¯ Bñ£ê&G/S¬ñcq#q¡rq;ÛÚi±bì0Šñ?óp¤6ˆjòí\„ñ·] ŸúÖìúÒoû¾oüvÁÍö¥\Þ%`dÈëÑJªßq'æqypÔðÓ0…&pצfÖœí]^-·.£6²#+d›×Ëm¹jp[p°«çË^Çëf­;ÊײJÚ-2C2­0纰’Àðé¶Ä(z膆h˜¦¶ÂîçÉ2ì+Òd«‰².gçË©ÒNsW³Ÿòrï£6Ï)7w3/³m8{ê8O¤cóÚžÿs.§3¢–±W¬;7Ê7Sñ=çã¶µ3.Û3<dz<ì/]t‚Í(p¹ qîöó¡þó‚–q.!Á2,CSR(Oûb†Í„ò&èïW¨€«úeE_ÇÎû/üj]lð¥ȪGƒ4ä2ô;ô6kZÜžëm-%S-Ùl43Lr×*…æ¢E+3è4R¨#pC\ÆPƒÙP5NÓ´Û´8/ñÛ©2§2Õm.å†ÜzAÏÈ1ÙTAÃ-3ð-ݪæ Cuϼr½”µPÚ²j›U[Vg5Ó[ _î½¾0çRàÌtŒ.3|®4šþÔû2Ã_ómdËð(ƆÛhÿA0$³ekÁb“p§~b^{Ë^óu_ën£˜5òâubPóh‹¶7ï3Ÿ²]=7tkÓ¶VqĪ6NÝtm_õm[oŸÊvhwHoÛöoÏ3‚ ÷jóvq÷5×±nW:7·k1t£Úpã.uWw)‡rtsso7h´3 ´_d$€Ozk#ŠB·rï6i‹÷xã4ÉÒïEÓFFDÞÍŽ3WÚ„H#I£§I›kJ2K²ÓÎeL+A?2v/w}Û÷½6Uò$H%³ñH_€øÙ–fR÷ÄRÇF%;õ˜Õ‹³Î¦jNµª}÷I^8†† Ñ)Ÿ§W{޳ÄiÿøNtçu,‚Z³µ*»ue“ëWL“£öZññV8}O÷gxŽÿ50¸0SDˆ× ¹·X8¶O6—s.+·xbs62#õe3¶scù}K÷;_¹œ—6nG”Sð”Ï9xÛ¸Wùq³óú9f7º ã9¡zŸ?úŸ[¹¢[x)#7Þ9¦×¸¤O::=òÛ|wºqsz¤WzyO¸\ ·zS5‡§:Cú¡Sz“º¨ã¹@OtIï·Yô7€'8Jë÷ÎrçïHÇ,»QÊ*9ã8•׺oÓº¦±Öa1œøN°‡û´—S¯8…Þ%O—8QŸæË 3…g:³/ú¨CÿûƒÔšE41ìx5“MÖ´õ2c0‘´±*˜Éõ²×d¢ÿ/ ³; ¯]D/ƒ–v0ö¶Ãò™[.ßâæèæïc§®ÏkÀ#z´—º?|Çj|‡·§;¬ßɃü\äs¶ûÉ ¼¬¼Ê»<>Ó|¬:ÊüÌKpÍÛ¨¼ÍËüÎ<wÓÏ7»Ç¯ûÐó¼NŽüòú<Îüº×ôÒ§ê©}`±—4±ÿúɪt ·4Î>ø"D8ª[3Ô¿<Ç—¼ÒW½š¸[{Š7õ%?µ‹?kŒ¿8Q{1Ñ›<ºü¦³=ƒF°ÑG}Úäßþ¦qÐK=ÚŸýá#>Ëç¶ãß|ãïÿ¤Î³}äéàW¾³/>Õ#~àûïæ÷}á3½Ðƒ~Û—°çÇ<å'ýç£~«þäó}ë‡üë}¢:î-Ï>ã“>ïß>î¿ñÅjléï}íçüãϼðC,ñ³~ñ[þÔ?;惪Å.ç_ô¯¾^ƒ>õƒl÷f?ö‡ÿʧüÒ›vÒv¿ó§ÿó§>wC~žÓ3Óþú¿ú·¿ûwÈÆ¿ü×?øCÿ¬s?@(ÐYAƒ&T¸aC‡!F”8‘bEg1Z\@ãÆŽ 9v ù±âH’'Q¦T¹’eK—1š|9“fMš1e’̉`Ä:ötøs¦P›õITiS§ >•:•ÿêÁ˜+™^Œú0«Åve ¶*Ï­eÍ&E»ö¦Z¶oá–<›2+Ρ-¿^õèÔ.Û¾~õÆŒÒí`Ç­9'Q±óþµú´ñã¶’«VF¼y¢eΟ¥*^¶òW¼AKó¤œ:3ëÖšAÇ–=û°gŸŒKË´ 1²ëÝ:sÿ&üµkÚÇ‘'OœysçÏ¡G—>zuë×±g×¾ÝúKøÞÅ™‰›6œ(ÿЋBóÛ?|o±ÙÈeï8À€¡SÁÄ”;‰» ,ÐÀLPÁ¥SF¸ˆ‚Ùà ù:a˜‚„‘Ï= ÊP#fì{O€eœéDDüH cXlÑÅaŒÿeh¬ÑÆq¼q aÌÍÅs RHWä±ERIw4òÈÒ^rI%›tÉ$§¤²H+¡Œ2K-¤)B«9ÁO òä„ôh›7ÈbS€)<‚)hD„Oµxa…?YìTS®uåŠ/ޘŲ}uûÉ“.Yn‰ï†×¼ó^™Ù!zy½˜gÞ$ˆ…vœÂf8ÉBÂravfa„Q¡$ -Èp†D„ú 5½œ§œ‡)ØaD¯©³Ç&Ùoº„]íÙm—1÷Œ‰½o²qo{a½eå{øÙ[vÿIð÷dvf™JDÖ3H”4Ù&_ÓœZqFhÅ‹qÿ6S‘6E'Œ@WÂZ¤D6ÐP‹(`Á1N h‚F•Ø€^à BÒN¦:pŒ|âbè „c8BSE+AÝuYSr(zŒRàó`A  <¢®Ç(4ÀÐU€1n,UJ|*‚Y¶,-=ƒûˆf]ŠéÔ”L$BŠ!àò `BH)aŒ OfŽ®*£Ø@X•Q(¡\ðÓ Ø;Ò¶H§=ý©"r ¡ÎS›G}‘AõÉ‚PøÓ8Å1Dáe²ÈÌ”áŠüô A« Z 8B­” „>A¹ÿ¶Rb«À«hä_uV¨Zf‹qØcœbžteƒGñ€%Dâ± HÁ…!€ _Yr4\D>\YC' qha»åJLfZ^-‚¢ÂŒ4üPe(T¬ CÍ„d52jA!S5W8gÃ4)Ý™bž²)IñÔ¨ŠÂ @B áÌN†¬¬gM«?¡¬ù}’2‘e¬A¯Êx…¾”Ì•ÕE²”D XÄW’ÂAECYá ZÉÀó’ô›<€ `™ºÅ0¤`‰K)¥yM†0d&uó’+>“ðd³uÊó´1yŒ€†Wb‘XtwgS”¾hô‹¨‹U@y¥Ñßë$ƒºÈŸÆB Z0Wæ×x‚/CP„Ó¯¨l‹J1f‚ФPya[P \CA%€J•ó½Y¶%á— aÒYÇ+Q˨_Ì7tçeË[γ¶âs¤7ÝgÍ`ÆÓéeôPqjz.”Ô¥¾=gì«_Èž¶h[”dQ7¦ÿUê»=Ï :aËʱRápt;ÚFNîØ@J¬Éò^)¤Ì"*tL€@‘€5Ã5ñ‰4[Œ Ñ­•ÊS=†çkÎ"#ƒá­ ÀOÍÐi(^“ÖŪ¬ù€¹öqÌœ=j/‰Žf@ Ë0 (‹ÃŸVˆÏ€–³‘„D骦™¸&3öYÃl ‚ÍpvüŸÍìÎ(Zõÿ¤œmú@OG˜œ!ŸûR£šÕNŒb˜Z›E‘8¸ ÇëX•Ð0Ö çLõ:Íï”Áÿn ÔÎEÔι(Æ“ À+LÓ6`®Δ¼dÌv‡Ï6O‹&¡£ÿ$, $ ­N ήá‚Ïž+•XOv"0ÿŽ:L%Çq€.éL‡…üê¥ ”Îns"%rfrŒÐ €0sºn=š†GB tœAtŒO9æoíZ¤X†@®°ŠEŠat²Î Ñ@ €/º 4Àù®Fz)þnaoðzj@?ïP ÷Ž \飫æJA>h0ŽÎ X¨rDÇì¦p\ªð?­,´™¬àÊ‹™s1±Ê1Ã,1‰@™°HT 2s3e‹EBaLuK ³Eþ,Ð\)á$H^@Nÿi$ðpΆ3×îÆ ré,FoômíÑµŠ À„´@Õ& GzZÄXBR/uRßB.U@ â{€N|ñ©T@FõvTa˜áJÃòàŒÓÅŽ¡ ”Ó,)PaË+`:UÁ:±“sÇ‚€EDXo“UØò7±*°KV ãV+á‚ê`À HèÔNñôQÅ(Êè+åVÐà»Æ PßæPi„ôzl¡ª\§ò\Q‚È£€D€ k LèÒì„ÍÕ.Bjò‚ZíB¢ÖLÐäR2Åkê„üž-gªöÕ%…bÓÔ¤¥ ^ój½6ÙE*à›ÿ–=ˆƒÔã K$=0Àk“'{ò_s3KzÓa,F`/CYÄ­P ” ¼\$ ÞJHÊbFEU´²r§$ Ä€c–B]ÒŽìÝ—Y€™È „€ ‚SL; GINí@@ 4>·!†A=Bˆ~`BZXÍëŽîZ¢eZBë„ð"ŒW=ÈΛnì ¢ìœWãë¥z£z™—?³å-Î0÷T/o%Ñ÷-¦`]ä¾Ö´ ÑTMóëX%ÆYa–sc„ B@”@tWÉŽÚq8KWHrì œ`ã“$€ <P¸Î|Яü`Æ|4ǂͮy¦Fª&rÿ:øƒŸ Ó¯wb>ˆfž´iØÂ‘Þ@ÿ­T½N—EŠõðÏmøà2Nbî·Nç,fYd³K‹X†gGÜpuÕ\ø#ÎHša¼à}˜'jÌc„ƒŸ/  ;5$Às´xp>õÃFâŠi&}Š”+úU׆E¯’V¤†G6 …@®tر.ÎdËp4Ÿu ¢Gq× 9Øù™ž¸#ÁÚ#ÀCÔÙTS{ §yQñ箘˜‘}Þ} ¢3™\4åyÆçÃæCU¹ÄÚx#ÞFÖW ˜pT€'Q-ãx–”tŸR_‚M…_h”ÿÿ1CRø 0ˆ"7xf@²†¶xšmè™RoÕ6#Ñ ´¹™!H‚bY–Y—›‰—{y7 8˜q9—Y}òwÇyžÃFpÑÙbṓ˜-±–ñ9Ÿ‘¸º ë‘é9@ö5.ä˜%ËÔ—M‰ŽZ¡7,¡w`z¡Ëy º “Ш6zŽÍ¹¨ø²¢ùy—CzŸmù–Zž5:.†E<ú¢øE¶EjÚò†$‘,¡ Ô7%º§‹å£ÁŸ‡ø§™¥ÇkŒ’? Æ‹)Â]Â]‰Òùº¹¥Ížš'‰z-Ùí£º«±ÚpeV¨¿zµZ˜[D:åDJ„­×•"Ôu"â$ÿ‚ªçÚªSL¦ÊÚ¬ÙyÉú¤Ú¢I:¢3š"¸ÎyÞC~D¥á·[Ħjû¶mx»·³Œc„˜;I4¡b—·u;¹%¸D¶÷L·¥[IPÁ¶9!•›E´;F@á¹AZ·m;K¨»¶#»y„„»nŠ›½Á;É%Ô:^Þu¦©­bgF|‰¡½  /â^ÔÃ?7$ÁÑ…ZŽNh¤è üZXÛ^Ü*\QÆ·ÿ…Ž  (! Z@ LA ¶À ² ¼@ €`¾*º_! ¤ \€à Ž \!¤4A ^ p< \ÄE| X` N| º ËT –`É;0Ð`ÆküÆs|Ç{\ȃˆü¤`¶¶„’ ÉYÀÄ·` J‰¢\ J `㸠<¾ @Á(!áO¡ ¾ äKéáiY¬|åË€âcþâ*ãŸó¸©ãÍÝAÞ>èÁä—»èiäè€ ¢ ßc~曾æÍÀUþD^á·€á%ÁÖ»Wÿ¼>éÅ^ˆÜ 0^ãž›Œ/HÇtPGuXR$ÚÙ¸‹¡Nq^ ër‹ Gƒ9b1@Ü[ÁÕØÛ;¼HfÄ  ä ´¨LAÈ êsÝ މF;E/ïË~ãy›VHA ˜ îà ôG â¾Sõž·Õàõi^öçÌUjÿö{ôqx÷·^ÎÑ@õkl{øaàk¾ú]…ó•ÿrÿDŸôŸ¿è©_úY_ø‰?öÞ¶×@W’÷µ(£Ä¿÷Wå÷§ý¯?àeŸöm?î™_þbÌ1JbŽªùztéÓÕ(7ÈÜyPI¾``ìòõc<¾ ’ù·wáዟþ˜ÆyË5ÑÜc‡ÿ)Æb¶SŠ|M¦Le¼EÅoåG\+¦q&à1­ f b`¼· ×Õ¡vùgÑpâm˜šH|ÍH DpRΰ IÁ8àŒ=š$@'`à Ižœ°Z8Ó't±IcÉÕOlp$#Μ$‘VfIÒ–lVSlp„K5¢ÂY´uå•]A)%•3îÉgŸ~2µÚ|‚zŠhDÉp!ʼn‚ä¡’þ…⣈"è£v8©¤•ZºÐ¢ZÊi§„~ *¦£n©©“¢ ª2È”±ê¡¥º:„±Êš)« ý ,°S9£H|5£B°Á[¬²Î>Ëg«¸ÿªë®»Þ:íAÕZ *¶ÙbÇíµÒ~Ki2áÆêí·Ûžkë¸ä+»…ô+´öÚeä@èxo¿%廯¿ûëî»àÊËj k‹0©“».Âéf¯ÄCÜp¢O±¼ô65pÈ"LrÉ}^ü­¡#º±«[¬ð¯ iÌ2`.Í5ËŒÐËì¶ìjÇ?ËhrÑFô½(g+ôÐ6Üô¹@wê³ÔKO[u¸Sw5·[¿ªóB'MvÙfŸÒÕ¸víµÚ¦²míׄ®Ûo‡½3ÏÑÝ­Ý\ã=¶¿^”t¥^‹l‰")n éç2m¡M9ÁOœµÖ~{ŠwÞ<ÿg÷朇-÷©€‹N¨Ê:ÞoáŒã•À2`¦”Õ]0ãÀ°|J^yïJ_þ.ß}¯nç¥ *|Âzï}:ñÅ“Ž:µ§×{¯¨„SŠœà@˜KrY;UH4£ÅLÍq Æ6%€G¨æ°Äl ''¡ÁøVº¿A˜QªÐ…Éùn€ÀŠž @:çqÌx%‰›HîƒIð§¿7¶/€·‰mëä.wƒ^`R’qn:¦;{ßïùÒ÷ïjço*û¶IÃ;Ù[{3×¹x¼Û ^æúÖ ?I·7n—Èg2vÉ Ë¬ˆ?{°Ñ¶â `eu¡%é ìçAcÆŽÀ“į~ÿ—á'ïvèVqƒì 2#éK1! ‘.BØšS™a 3¹B«SÅ$~&:Iœ¾ÁeFåÈZhIS°.a±ëî$^Wán©Þ»pëp¡ÜA˜u¦Ð×¾%){·Þ—ÁÒ‰Xvg.XNC‹Ú&Y£ 0`,›X¡BËXªyã.ênÊh·ì¡²‰`ïòëu@§ú뮌C±|ßÒ0бÃst±3Jo_ð"aP ‘·(zÝϽòl\߆‘/û§ƒ—Å 10 ×ï>ö^¬æ^§ YÐ÷žÉ/å¤=£Oû”PÊüumòÞDÉ’T ésRcÃ¥þ'©ä%ã¸ÿe}±IÂðI¡4JäG–lD6~g‡"ÅIù¥ŒàF,€\¡€)Q€P Ã)¡Ig7RvþW%¨]áX-6‹Ódž'Ræ'ƒdƒ~|AM$0±g©å{wR¦'¤d˜F&!„“|Œ“P: GxLUÑ A‘ii—_¹“ƒÓ„{»5R„ChØËäB_âYq%T…]hXaY¸ƒœe„ù5†W8FK÷„ù…V¡†txz?±WBxB¥dIhz3ˆˆ>f/`ff&ePn$k d&WkÑEhpn÷„e´†Æ|¼ö‡íæ$QÇ%PCßSÿŠ„c$s>uPt%²Æ`†Ÿ8$K˜/ablÆkªø$û„Š_‹T‰HŒÓlèOÃYÀH‹òÖ$ù¤d¢h‹¶–fô´ ·hlÎ`pÆ'°¦j~2gè6މØZèˆâH2HCÇbe¯V~ìx§¦öÈ©¦‡¨Ž"Sƒ÷ƒtÙS6^Ppàvq“f\<4\#·?©o+‡'Çer-· å6pÚE°=ì¶ýh/)’%YX;‘_Qw‰F™4x6”_õå’1¹_s!@‹÷{HxuØ…w&é,$ ”CéW´—zÖÇz¦D¨thšHOMybK)@÷Gÿjù‡zªg}:F”&u„^I&2›EoIx6gqö:¼sb‰x¶ŽâjWâ–Yœ„‚qäd` {I~¥”—¼ƒÀçK u—[ùC]é/¼ƒªµy±Whˆ©vj𤖥k°¶˜wQa™a(é@!GÈ’òµyt'Pü%@†¸„tNx„†É•öò?<rhXoI’ßV\ùàV›$tr"nm\ëæ]î¦ &#ñrÞhno´WœkyœÙ]÷v$ãóœïqoTÄ&øFqÞùq5¶p's&·êy› n´5k!y4͘œ«økzÒ— Eÿb¦©Pû¹€DÆ=ŠRRšÀ(›³ù,:„<Ôwm±u-D ?it9´Czy¡5©hdÇtö’ùçx¦jIŽåtc¡ná ÞXG ÓA÷a.á%Žõwc1š‚G“5ê 4äGgM!¤¤wc´B-*w- “GGySø“=sÐ2¥ Ú;BY†JÜw‡4¡Õw}]ê—RùF ‡b€ùa™ThûzЧ•Á7ìƒN7\:& uTbšh{ƈÚw‡Ji¢ôÔ§X¢|}I‡*}qJd`ÊzÊw”a:ŠšGŽú¦¾ãxÐ’©Vz~ö"¨[zN±IaX•Q)aý‰ÿ—dÚyi  ˆÚª…)¢¨¦'±¢ˆ¨‚ši“‚?±¢Dx%a‚X¢¥tB&½ÚøC ¬¨˜B…i‚…I﫜j­Ûd/-´KчY’ÌdyÈ™]¡­F𣏣_øgŒ§‰ûX„ÛVø™ú6§ðJ‡:zMzº«Q'Mq¸W{Ø #‘®J‡Él8wrš°È´°dØ¥°I‡{H ¡é‡EÈg&±Cá w­û«Ê² Õ $­¨›L²ŠÚH²½¦‰j‰¸ØªÊ† ÌvœXi( IßX«›èljÒ²'±ld2P°h,ß(Œ^BylÕT´‘èI[ÿ•òD¢³Ï¨²ÁxŠ-a²ô$k`¡ Îh2c‰• ²EéZ!S¥3¸¶c{¶o‹6X ,d»'›:ƒvûXp«·4¸·}ë·+¢€+¸ƒË©rK¸‡‹¸?f¸)Ay#ãft›JsÙfY4nYŽU ¹IqŽ‰Ë¹`fŸ+Sê'2LXX¬¶Z÷™úèªBIºëºþ(3Á›ÐÄ$hBžæéFöùrØ ’ó†pªZ·[qñIoï“$ÃÛqþ&à5»Ý‰n×ù‘É ^¿k¢VB]œt]H!bj¡¼]pœÿbnÃI~ÝÆçÖ Ð#@‡ÉÑK¼\¡¼Î ¼×k]\¢#öùº~ Ô¶ÿ¡ª^u÷wLƒ3꓇;SÑvtg£vW¢JÇzÇ ˆ¡Œwý;^2J¢-zÀ?Z‹Nq` æ öaG&9Bšfÿ5`V¦@ñ¡ÃðAFbœ7À6uWBZw?g ,œ¿}«R=BO«J…z¨\Ú˜X”z«çEÃËW¨è¦ [f$Å8†}€GÐw;•hºV™ESì NœTì 06iO²½Š Yÿ5e½6e·&Hà ™ÄY²ÄY©;ŠÅ>cO‚Ä? ı»—Dì ìç« øÕ:˜xªU–È%a¬öU­¢*‚…s—Áz—•xÁýZ˜§í½Û…Ùμœ Û,‡[µ-\îK¼Ã+ÙýM&evv—hj¡+dšÀ=ˤˆWuפ Þ†–ÁËMÜñ]ÆÍªo4¬[ŒÅ]Êÿ|*p{ŠÊ¦6&Æ"jF(lna§©š_Káßhê{mšßû}{tz|îX…:© (£NáòÍá Èi¾'eg*­'1ÉŠ< öE· ÂpÝ~§X§£”«&.× Õ:É=;â&QâÓ ƒ––E¡êØ^Õ!†ìZ®Ø¤Ì¿*Íëì®âÐLÁŸÉ°Ø½sØÍIέ kµcˆ£ˆåoxÌ9i%SæLþ¯Ô„hGÎέzæ Ó±DÎát‰ÒµaAk²¯CÑÆRµÿR³%}Ñ̨³WòdžB& ´r²RKŒV 3kÒÀzŒ0át¿臸ç3[ÓÆÆk•é…#¶r.ê?ÿÕ£nê§Žê©®ê«Îê­îê¯ë±.ë“ef fvnô8×u悼î¶q”YdëÖ׆]¹—;ë†ùI›«’‰iv™E“ÐþØÆþ,œé‡dSZ|¢ºË¾•;AÀmŸ§=‘Î¥ž¾ÝÌI½¨Trø ¬ñËžË;ô™&й\©-n­ýÙ¿ï‘×m­íœ×ÉÛÝ8»)aïÀnkÛ+žìû›½i›$aî'¾ô›½à®iâuAçÃ=‹x9 ‚j·gÌ`o¥&!¤š;5LG=7vWR«üÜ®:¢Ò=Þ,?…jÂY†“ë0¿^ZÊñô(Σú•®u `Fÿô¼Ã F˯a?©nè×}¨áWÅÂ@ÆÃ"eX„Z §y|y«Ü`Î}Ù÷zUyõ.àÂPdllYujãCŒ¦ ƒÿŸ[Ig*l«÷õÝhLÊTdVŸâ*ѳŒ¬ÙX—ÂÊùçËÈÚ´ˆ‚”šBµšÉÌ*¢‹¯!ø{r߀¨<«Âjã• nd¦¬—§©>^½bø¬©oÎFø?¦øP¯Y/Rki3å Å=µ“†ÜLàܰ^ø­¹³±=ˆBM!áXîä–õ®QîëºÎUN°l>ŠÊOÌ}þ霉w²§Õ´ô´üëú¼Ü‰¹?aVo³8Ëô²hÿÐ/Ý…ŽŸÉŸÒ–0hqæì„§‚Î:m8aQA]"tæé„@‚ 7læL@Á Ä ´¨¡‚&tÖà g 6K@ò„I‚,#N,¸I e)W~lùÒcO €ŽtFÓ Å„)‡9Û„aÃRg6Ñ‚áhÓšEa%ê4ëË.X: Ù”(՟˘ SQ°QεyõîåÛ×ï_À&\Øðaĉ/fܸ1’F›ÕíûÖñå¼–1ë¼yp³‚вx&]ÚôiÔ©U¯fÝzÙÕË2['–=ÛvË‹@˜ÝæÝÛ÷oàÁ…'^ÜøqäÉ•/gÞÜùsèÑ¥O§ÞÛKBÿÍ~³ÞηvbŸ0^g ¸YíeD8HÚ©`‘ƒ»&ÿ5pøïk5/óÚ·¾ßÿªp@‰{k>½ä«#ÁTì„©tÂN0fÐ#K®N.$LAù& BgÌ+?þ´ ŒÃSTqÅ˼@ bRä(²ˆ!còâ"/:¡£F0PÁ¥‚d@‹#6 èÈ$—„I%ϲ?–|ºj-8¡‹ñ^Œ€k´ G–ÎK(½‚ ˆ¹ˆo-F¸ì„Mg€FF{$óÈ 6©ÈG]„O1ÿ$3¡.´ìB¶d*È)Ë<â„F[tÐI1@4>7ˆf&­”ERK5µ°ÿÚð³˜^Ûm*‹ŒL˜š\uVbØÍ‹þˆ!aÄû5Da‹ö„\wÕ™ek‘ Øz³È„TU¨Õ ^µÂ3‰"@¤MxoÚ„†I"?ò–Uf³½Õ+ÜkWÛµR][zYjë#[a¥¶Ù݈Lˆ“#f*o7g´`$&yc*ˆ`ƒO•xbŠí“Í©pU€ЪMèÇMBc A¢ VÙ¬‹Ãòu¦69ùݵ6)Øf>jKBjÁ¢Êà8!3 BÓ™H€;=’K/˜9 Ob,c aŒº¶Œ7–zè„Ú’©k¡9¥êfab%Æ-8鸶ÿE4Þ`´ïˆz;î¯+öûïk+«¦M¢Ý«6Ž ¯òæ”É‚™)–!™ñem.(giëõcÃõ ãaÄuæ[g¡Ìͽ S¡™ ¼êqÅy¦ªó7'œöµö½v™ "fÙf8ÉB­¯‚Ù4“Uî¹)á‰üyèŸ;ðñe `0G"¦V®OÖ^{û+³ëûðYÊU.E¢ÝÙCòkµ¾ UXI‹™Õ,è„tãcDÄF8à:ApƒǬù…È>Õ»ž~l‡ÀêK$2a ýFò6Ð,"VËM3À)ÐtÂp@8™eˆÂA·4Ë^ ]8—Àd|%9‰~$‚= äÿ,["žù"ç3•…-H<ÌKU”"—÷$†+ ñâ; 8 G@ž q àiQß°ñ–Þ¤†–qâZ–FœäN$D)£Ê¡‚#d!V iÈÂÄ⌠!H(!Kb(gàð…ƒ$$©:&šËr4ÈéL!ùHHN"Wa!c&©›HfR“›äd'=ùIP†R”£$e)MyJT¦R•«l”Wr E©)‘ìø Ò%<ö L}fYúÀ0»0÷‚ËÆè’!¼de3£c<[ö¦–ÆLj"v˜Éφ¨ÙlÂC¿$Ì5#”#g¦“9ЬV˜ô䨙ˆj‘ÿb«62 +a…IQ’’Àªµ£(-‚Kïú”¡àOJ-’I*@= bÏ LE&‰Ú’Ê誄²¤ `ÊӘʇzJ‘6êΚmd)(ø¼D(ŠdjS%š<Ÿt†´ž‰¨ÃXê0:öTC5;‹t¯yÑìa7M¿¼Ò¬ølïY†V°˜¥«¢ÔkVµ"§²bM)’{ÞF wù ª#ÊÎø•V ŒµoÊ[¾”ê±°ÆäXè뽮ŒfM$äò+»pV…)o`L-«3Π׫ÆGm™s|.GTÌǨL›×|7¹u6m–ëÉÍ0ÇYÒ¦Ìcûëÿ6A“Ýè2kiKÈÞB«¼(Bd$kiW»³³Í¡ã›{†µ±õí#hZËz§T0>.!¤=m-&ݺ©ànÌt,hçö1ÝŽLómÏ€[Þ̦÷7›=`ìò¼áínqÓ5­ 73Õ.#!R#)÷¾·yEêØÚW³›yÈ­ò%Ú€Y"8ØáîÁ ¶]jûú@ú^®­„} ñw²n2/¾1)ðçË3·¢W½+¶ {©—@¡ÐƒÍHO—á€IÕ&¡A­†È£‘Ö= ž©ƒŒÉ¬«¦uäÆl€ AúɘÉâ|q'Tã$·Œ¯å9`mÿµcÃYwz>aAHÈ]-WÙÉP¶à” få51òdeqŸW³Y4Öð¡ Á€o¤C…l$qIѧt7G9ÚG a„xîxè'ÒÑ!¹aC/‚è”tdà #J½D%ÐO´è§srÀ!¦E‰ª–îtý4ëžñqÍÈã-e”¨z(6¨-êÁŽ$Ô~f¶'!£r>G‘̉v³­}Ê×ÄH1!&Ð%+‰n_[Üã&w¹Í}nt§[ÝÊÙå|ö³¬uÇ[ÞœÄ&:W{&xÏ[ßû~áO¨G ÉŸü&xÁŸ·à¼R«7xÃ^ª+3Wm¤mÛÃ-~ñ-8ÀÔzîï0þq ‡\ä#'yÉý!ÿ STARDIV 5.0 <L:;opensips-2.2.2/modules/seas/doc/images/image103.gif000066400000000000000000000211701300170765700217760ustar00rootroot00000000000000GIF89aRåðÿÿÿ,Rå‡ $$++33<<###***333<<³ÏVk­sÑ:ví¶Üšmc݆+nŽß2f\ª|Gmk®›š…X+[º§¹©ªèB >àe $§Þ¾ýŠê½×Zp&¿ø;š½Û•»˜™Òm8Z°Eù©j-(0A~-¼:ÅIBw@¢ ! "ï™8é.»³ zZ§pʧÐi¢5ߌ3ϦZœ˜¢@#ø9ŸÄñÍWß}æLZ #d‰3{ ¥Òüñª§¯SΩózíÅÜ×HNlh!|àÂôªp€ÓcWB‘ ÿ X8`Ùg“Íqh% ˆ@‰#<]ÔôÙ‡Ÿ-³¬Â¦.° æ»p9hš Ë!àX@úÙ© é9æ¡‹ÞîÒMŸÖxÞúE€¯°}tP`Š uŠ€‚("‰&z(úh%H,Ç$¨}6ð,X#Œ2ÞMi”vèBô²°å (`zvª@hXpùð²ÿ€Øï«ý‹1Ú/ÚϾosñ!Q‰ND#@BÈ€B3€4ŠH›š¢`CÌŠ¥Ð; Ù‰^¢ q°Ó¥íé+R¼ÒŠø?(ùLŠîƒ´Ðhp1cs<C\íË` @ˆÿD£> ÀE<+[—¬„%-¨]C*Ò‘sDÑt¤ýhúÒ˜Îô¡} é«hšÐùô›Òé‹”š"§Fu£)ÍêVW$Õªî3¢--kXKÄÖÉ3®I"êY/dב4°_Mê?¯zÒ´>¶E†]ld+Õ¾vö²£mjYs&Ìf°³ìp;"»þ¶BÄ}r—ûÙÄî¶«cî[S{Ùµ®t¤ýlnuK{Ü+öHÂml{WÛßðxºÿÝnp¿{àÓ®w¨­­ð{ü×ù^¸Hø-ïuÜâ¸Ã'Òp‚t|ÍÈnðk¿¤ãÿÙ¶0À èàðÁ À è Ø ÅC”›ãìÞxƃtƒû¼á#/ºÈéíŠÿ1Ìes¨ãÜãûn6ÏEît{}èx `€²3d @ÈÇNtháMß:§{à l€SŸ98Àƒ«{;ëÚî·ÒÁ-÷\^ëÀ‚vµ[ší÷@ ps°»ûç-y»Ögà¼Ç$úž8âÏõÃCÜôd_ÀjsG{àߨü@À 0À‘ÁдŸ[ò$﹯cöW3ýä]Oj°w äÀó:oýGv|ÂO^öèAC^ŸòE§~õÛw<ÿDïƒ@úÕ>¿ØË¾û”ã ðÁ Ð÷Þ‹Þš7µ¯)O0€°x>à¸= ÐÀi6Ð Ð8m˜Çùwz)zÑx>Ðжêwk«F|)§} ñ(çgäg~ ñè·"‹‡€ }>@{0> ƒ }àÿ‡ð€}ç ø€àà@„¥G~X·0€ 9à{±vh|&‚òW‹ð…aˆ4P…Èi ÐwW(>×vŧ|…xz<Ð,Øs«Æ#8€ÿ€ˆ€ ØI¸„'Xƒ•ƒ~X€5Xs3ÿ@ˆJ˜…Á20…>0„uË7CØ0Ài4à1P9àð‰;°†>Ç—Šlˆ…è…¡~=0…3 V‹t¨[èvÁ‡T( ™¸Àp 醨–“ÐS20u¥'”( ×l8’ɸŒ9‘ p‡DY³ˆ}=0•Ã2É5˜•ðÖ‘¼™™,˜‡Â邲W~5 ¸¨h5P“°–>€mé2‘óǙצ~ÛÙ™AY}KÿgkcYå™›Één]x—i7 ÀˆøžXÿÇŸE¸‰’7y9¹“3„¢ ”£·aQ7}·)ž?Gƒ«h‰19“sYyxˆ×çm=™•9™ÚW‘˜Ž&‹”)™Á9{Ò¨ Ñ‘xàœ%º£@Öé–á8ƒ>Ð{ñ—ŸûY _‡’ä œqž‰}‚)tÓˆmŸ£YšŠšãfÐ6ÀÐ/Àœ€=ÀÐ0—5_z‰_ºˆo(*xjê“€‹—é¡ßY/ ¢Òæ…4螌HŸ °Ÿ¨£ýyŸ ˆ,(¨ j} ÿ ¥Hjt A¤A¤zz¤?š¤'· Ÿhš`Oj¿&À™/°D˜¥0`/°À/¥Tª“•骀ªЧ *”:p°Ðw~ ¨Á£w:¨ hzmJzáù¨§Ù¨Úy2'¤RG­i’ÊZnÇç½èx:X*åu2P/PŽ5ucx®ÏЬ¤÷† ­Ž‡–‡Éš­Ú¤æI­•Š­ôª|ÛšyE™…È3°à‰Du1PVºw2 ®ìz‰°w¥(ªðꬺʱïz©ùʬ>z¯u©¯Y(­’:­Ú±@ú‹¿ ~Á}ÒJjKÿ»¨«¹¦7›ž$˨"[¯=[²?›«C+°ü:¤þÊžH7|‹§‚¡~6™Bë±»«<Ë òj²¿W´Ë ²!´>›p;›k)™rH€È ‡„JêG{¶'ºÇ{€ém5;‡Jµ_ë·W»µ«±ö*¶AŽøWû±¸9¸j7³Uë‹z‘n{bȶnë{€†høYñg}çy›r{;f  º¿¦º_kÂöy¦ ±P@s«¦‰³‹Ë¸øú»a ¶Y¸I‰•\›µK¶ÇKºëY˜ÜYœ˜+¾µP ¶ˆ‹L9n¥{ ª;ÿàðz6p/Àð‰R:«ò'¾Hèéûk7@|ÁZ¬{l»:°€¥Dؽq>T×µ­+¼¾ ¼c‹Àth•²Ù­ÁËÀÑ Áz¡1pÐÛ¶ ©¹¤ë‘{9•m(°½«s6ð‰<€¥ ÐæJ„9IªÜy‰*ŒŸ99n7 U»;p±¸Øñ[¬SZ¥gšWš¥4€ƒ«Z¾/€gºÂFs½§<à°Â1PÂAÀn:©¡¸l*Á#›¼`L¸…»Àcl´eK³]Xy €ƒJ¢ô'ŠÓ›rÕû‘ ë¢«½# zßJ¥Q÷œ€¨Ëº},€ì€Ÿ ÿjÃð4»/€{§z¿0\«¯ªâJ2€7Эúª5ðº¡:pÈÐw…LLÀ»«³¼¹†Û¬e,Ʊư<ËÂGÁ¹ü‹•w잇ºx2Ðë{:Šh´h‰º×@­ÚºÇÓ×»Èð °é*Õ<€Ò\Ž7pÍ ƒÝÌȈ¼“€{0@÷ ’àꮕ‰ª5°É·Íð<ù;Ê;ÎØ¼[Ž¡Å×9–-'µ,k”Ë‹¼f¬À M˵L´V;Ái,ÑG‚­ªÇ€kË\n7@¾À;ÜrZÎÞ<ÍWÊ7̰< /p6€ºË¬øÊbÿ+¸^ ÑMÆ }Ë ½Ój<Ñ¿¹ œ£xÊw}Ë·½u6ÑÍÓÕR=ÔS=ž@­½º¼tÐìw̚ѼÕP­Ð==ÖdMÕe-˸ŒÖim¶YÍÖW-Â^­Ó>×Ò¸ÔwÓ_}Æ8Ó{íÓrí×AÝÖ?к†Ô 3€ºˆÝÌk4½ÓM­×}½Ñ€ý×”Ö—½Ë„-Ôj©Üj¾Ò<ü¿TzÂ𻪨ÈÃ5`¾*¿°+»Qÿ9„c›Ï>» ašzç““-Ö˜mÖ¿ÝÙU ÜÄmÙ™mÕ›-ØÑ ÖF¾±*ÉéŠÅz [™êì÷)®6¬¿üë¿6ÿÀA )“Tê¥`ºJ¼œYiÛ¤ÜÝYúÝì[ÀLš½½Ô•ÝØ÷]ßÁýÔûßù½¯oý¸ÊÔÌ-g¯'Û錡ØÇ'<ÈŸ˜à±ª‰¸ª|zÅS\Å=pÅ]~ÿ ®øÉ·ÚÂŒ P¼RLÅVŒÅR÷yJ–úÙk]Ü@ûßyã„Kˆ˜E یͅà›]à–v[*Ú"]Ò‰ÔÉQÒ°ÍNéÚ£\ÊŒÊàzºKºŠ1«ÝÎÚÈÞTŽÈV”º]u›Üã5nã3>ÜÂ}Ü2¾æïŠÀ‡ Àz/€†ÆíÖÉýã'+ä}¶ÈV ¦IÊüœÍÿLÿ§0ðÏ1^¾Þ¯]èþüÍR§€@ªá'êÊ:‹ãnÞ¬ˆ©ˆV¸ãy”<€Põ{ê©^¿¥Þl¬®ê}Ƕ„©Á¯þµ 07gðx×>N±¾êwIvËÚp˜š=¾Z±áémÊdg¾Üt­ßÄ}íë xd,ç…ÊßÍzº>u^™xŽæìÆ™è—¸î=Ší°îîQ¦pž°Éü×€“ÇãÍ‹¡î£üî>@ïý¸©¡Þ]ØÒáíŸs&î™ÈgJíçy^j¹þíõθ|ÈëÜxëd,t‰#Ÿæb,òúðý>p8°„EÊ„³ÇïÿÕ ò‡«ò z‡´ìñØ;Àãí8OàÚnòl^ôøÌí­{ï/Ö>ʇú.÷ïõÚÆaõ ߺXÞZœõ]wi˜7àñ¶¹ñƒ}[ïŽ]ßµ8 Ÿ Ð1|ö°ÈôO‡ªg¡íÙ™G»°ás]ñv=z-/õCî´ õZ …мvW‘oö£7ùsæ”ÀSÏ¡çNõ4–/€ù÷w°É5P›úÁð‡ßôºxÆþùÖÞt=àò Œôª‰µ_ öe'öN_¯o‰eú_kð¡ü™/ªÌï¶°óþÍñ•»'ó3ÆñüòÿýèÄ$HŠÈmN,Šaoq¿ò«hëfkñÕ¶è]=èØ¿âúŒ®«…?ãSßßd Œœ‘ÁÇ@‚}0˜Páƒ7t‘aA„-¬èãaB.Ìø‘aH‘I–L@eÂ;^ 逆z”T’‡Ž/,°ÃäÀB}pÀ€ò “J*DøóiJ‘nt¨AÇŽ^ø°¡¡ƒ ‚W³ÞÈÀÁcر0ÀXH²éÔ¸çú(ÀSé]§$9`@jŽ I—ê@ HÚø1d˜Š12vLyà²U-äû44Ε%?Nº¹óÄ: 3Xm·ôÿÀ À —`Þ¡>v0¸qU¦Ÿ?˜ñÕøhÐ7pè°@ôˆÑGAçÐŒõñ¢Föí6àÖ¥hœµ]õ Û3Çù¾}Cõð=ŸOÿøìåËùãßO¿üP²=»ÌòA«nðá|ЬBP,²ÄƒPBbH,½=ìM¾úæ+0ÄþFѲ+óoÅõös±C‹û@«ªP‡6xf考ÊšP´:ðH!wÀ@¹ÅÜ«4SÅ­¬òJÅb”qF.¡jñÆ¿Ä2Ë£DÊ(™#óI3U¤òMºÚqËüè\ O)asFÑYÛl…}˜Ó–¡bXår×5ÖÚ[i¦(Ç|0 išð²` Zjý÷S—WN8e–™öê‚ÿ&Í\V¥6)b‰•FÖ¤wìñÇ ‡¤pÂ2@Ò‡ñ|ké’§Føiª£žû>¬Î)o½÷æ;ïúb–›R«ÇÜz鸱½™ën•Õsðºí>¼é í»rË/Ç>yå—g¾ÜÞ›‡^øá›_úè¯Ç>{í;uvû剗~MË­÷¾|óÏGÚv¹ò , Ó§xOïÁïËsòáÇ?ý·X:ê¬ÃŽvöç*äa~Å‹Ýý¸@6ÐÐóâ¶w@FPÄ`5¸Áÿ•†‚›Þ ÈA–Є'üÑ÷Á ²Ðd(„a e¸ÂžÏ…UË]âf¸CöyÁ3ß «v=!úЈGœáøÊWDâ0ÑVH„b¥X?%Np~Dœbµ¸E˜UO{NÔ¹8F2BˆW,#ÀÒ¸F6–Ð~+œo,ã‚@$<ÀÀm2ð L¦0õÈA`ákc"9A ¦/4Ð ]ì8I^„Ñ @b’—ÌŠ&A¸HR–ÒŒ bƒ0 Ûñ8Ø D†‰Žà€ 䶉ÍDdš:FÒ Ã,2GiJf6S‘ÔÍo”ƒ€ÿ†,8h„ ð T3#ÈQ›T.`€S0iÙBƒl2$yç29Ozn1•º™`.à€ܤ"=(€fàP3–PèG4°ƒ`@ YhA"*ÏzVÔ¢> $ 2bÖ‘(D š‘I&Ô˜ÌcÚ©‚dxn¹€ d-e)AfZS”]Ô§ST%]èHÉ’t0ˆ! .ð,‚tÏ2êS/€?€§Ãbˆ*wPù–ÿ ¨¶éƒntO&eHK ò…¨U#¡èOåÊAZ¦’•¼È ¶ªÔ´^ ÁA‚J“½^D:¥»ˆ&SMV2`ÿ¢,i@eº€t€>ˆN6#;‘ÓhÆœlf0ÐÙŽvm®§íaPûJJβ–“u@.wÙKüÒÁ\ˆÅJ”¦4 ° ¬B`L¬Îç&áa¨ňZ毮¾aÀ4«ykfS¬Üô&H†Tqš³œRˆY¹¨>4¸xÑÍ8ËùÝÍ”S1Ý]:ÐJí/'ln~M¨Ê|ò’Ÿþü¨@ jÐJ‚õ#â]©†ƒƒ^¢W!ç5ˆPzàVDư†úÅp ŸËÑ„l6¤©G}`R”¦t¥Öæ@x€,€¦0xÌšîF7%žÌ‰ H/Ud¹ó{c†…ŒA­r•1g ÿëXËZà_D• ˜ à ⢘!ER`¤Â‰D•© qê—ñg¼!—yŠ1XZùX¢ŽX²·¬ìe3kV´.ä/La“UcÞùœ ùMpÄl¬*D¯6H`íH/šÙÑ(<î ’‹ÑF·Ê ñí¢äGwš„óU*|)Íi)žÑÓ§Fõ¡H]ê §ÚÕ¯v—µØjX×ÚÖ²Ùs}k^÷o÷-£¬}=l_ïZ×À&v²{-l."[ÙÏ®u# mjÛÚÙº®v¶_CmwÛÛüñ·Å=n$jú#C5kQ¥ZPú>«{ìãcþHnzû¹’±H­—¾†gÊ L(ÿ³ü³z (ˆ«Éd7s¶Á¼z,¿<Ñ8ôê—Ï£ tkzÕºJÎ.h¶ÛÝõþ™ Sä ‚÷•%S>»‡Ešx:KÇ*õA &˜œ|Å¡w1OoZ—Æt§€‡=šøÛö~¾¤ž;B%kÿ¾geÒ–Vs=Ÿ"wõ«¾.Ysd‘XÞPö”Y2e¶ÆÞú€Â|‡üa“´ÉŸ7ÀJWï–¾>¼<ÉdOßâ×ÍäT³c!Ûd8[³”í|óõ‚MÃ<‹´ª¹¾Œ’â;2Û#:åK7› £0à{¾š’¾í‰´ßÀ t ÷c¬5›¿º›¬8»?Íj²…ø¿ÿÛ$À8§p¬¥òP«/ tAšÀ”ÁTŒ8ÄÁŒ¡‰³l8Bm˹¼ú-›¸ DBåáÁ.;ª¦jÂ$„BbB‹˜BÂB¸(ÄÂ,ÔÂ-äÂ.ôÂ/Ã0Ã1$Ã24Ã3DÃ4TÃ>5dÃ6tÃ7„Ã8”Ã9¤Ã:´Ã;ÄÃ<ÔÃ=äÃ>ôÃ?Ä@ÄA$ÄB4ÄCDÄDTÄEdÄFtÄG„ÄHÌÀ!ÿ STARDIV 5.0 C=”;opensips-2.2.2/modules/seas/doc/images/image104.gif000066400000000000000000000520571300170765700220070ustar00rootroot00000000000000GIF89a6Óðÿÿÿ,6Ó‡     ####$$++""*"++0&2(22;;22###"+""+++++&0&)3).9.)33-995* 8-"55 >2%<<$3332>21>>===5B58F8;J;6CC9GG7e|Wº(–y—'b£P€@'¤BA€ƒì!)ž¤˜æš`h ›„äÐç§ †jÛŸŠ"¡Y~§ØIxb§!ž’ÿ‚ƒ¤lRj©š>2鉆€…”šy¨¨Äkìb¤žzÝjÊ¢Z¨qÈ!J‹‡¢)9àô.gëajŽ@BXë„L°¦¬Lë&»l³ƒ:{mÞ«ï¾ðÊK%½ÍÚ+¾ül°¨þ ð¼tðÃGÌXÂ¥2ÜžÀg¬qÁêUlq½ÎwJÌ!…Ò˜·ùB6!˜»ñÌw̬Šÿ{ƪÖf@(Ȭ˜xÁJ†€(CÒ¬´Ä5¦óÓ‚Œ±^ ˆD„BH8€7`&#†ˆî0 õ¡ëæ¤D÷÷ß(£è ™ "JÖKç}pÓN{ÿüžÔöšˆÀþx(?éi7æ¸cì:f$’”éÝ‹‡ÉXȵ¤­÷çÓÔWʪZ"F´Öœ‹À(DŸ îºx:çœuè-Ñ`Šy( Qˆç ßïLåL:ΪN¨¡‰­NHëËy[éì’uºÑËuòhš’ŠR‚Æ—|L‚)¯þÍOÿõ|Ï“’‚8x€xjý­ëv2¬b¾ ì•’v'qÏ|üò0S1ltXJåCˆ L kµºÞ¥”ԮƠK]–[Î(²µ­¢@€\W¨@ô ÆoQ›Wò"h¬Õ±°36äSɳCd¹ÌËβÿ€(ŸÜô°2Gœà 9“ÄZ¡guM|NIñÃZñŠX„`ÃÃÄÇðe‰LlâÅ M±Š/làC¬HÃیыù"1#G8>QufŒÌn`ˆ…`‘DFìbdÞ8GÕ‘\¤"kSGÇìQ;CH–L„4–|ˆ*šà¨Â!})H’I’€@x.€€ ä8‡9ÈÊQž£•¯ŒeClY‘¾”áè†@ÊÑ„(` å8€én䔩\å@Ð]„—çàe)‚` ó–ø,Ù³M”ôñ'¢ácDÒ¨ “ ÖH.>9UäKç\£D²ÈÿÏ~ΰ!þ h?/ùç&Ÿ%1¨PÒ É‹1$”Ѧpd’c e Â9ÐÀ„T ä¶8G*RA4ܳe@¸ÑŽÞ“D ]#B}JIÆt¡-¹éiÚ<…¡AŒäBjZ¾h# ¨¸<ÓpOTàbž¹Å*DŠ4lC ¨ C2zO“*iD QS£S¢ð´§Ý iÊ* .O¨~ë@ŽªÌ&”M}ªG‚‹©¦AÝHΊVÙ|)%±3S¦¬µ±Wq[S²Ö­¸u}Ú¨ÓB×\Tó¢=9š€Š”ƒ ¨`BÐP†–£eÐRáÅÿRò²mlVÒ YÊö*—…¡f7»XsFö·ÇEîIt•à6t¸î+®q¯¢†²0×·\qnPA™>éšD¹I©.YÀû]ñjE»o…î?ÛùÎxž#{•ˆ=¥kÞªÔ,j¸¯Lôkôb–»a•(E-ŠQ¢¡ ñ©HIêU”ª”¥.-.¥2a­ä7'žŠAfS65·˜9ðJX½Fuª‚µ*VµÊÕÚÖ^ŽJŒ§’߻įÀý/ÔâŠEº2Á®L-1TùêWÀ –°†EbƒÜ0;ÅÉM¹0O Ü” ƒè¬ƒ¥gËqQ4PÁ£-í0Q«ZÖº¶²¥í‹QE奴ùÿ(5þÉ›•be¸×ÞíÈœÃKÝ=·ÄÏG©³Ce Ñ?æ™"€†3Uâ,”DÿTÇñY§û }èˆ8Ú(—Ɖ”‰’é¡:Òñi¥-–NïwÓE1uP>eQdÈÁÅ^íºùbIÕœf ªÝ•O÷2 ä­;úѽ2%Í…ƒÉÀå–2aÍ…Áu®‘Âh¥Hþµ?GœWXŸCÖ騊³ºU\Ø«$ºv£á¬î?Å× (»ß÷ö+8G`ÑPØ(9±é&K»CRí'¿;Ûñv—¿f‚P!£©°…™A[nœVÍòxP4®gŽßøà!jµ«GÍÅäÿ)¸ŒÁâ_Dw’$GÊå<å™ä™ÍÈM)s‚ØÜ'?oˆÊíËrõ‰äŠ=7HÐ{²ô„ì+MGŸÑÖWÓ3Ö³N¦Bl—¨ïÄëzVÀî’àŠüìÁ.ðF‰-[Ù&]v³#œ²kúÔ¥ n¿KÖÀp[ÈWÿ¶±{Ur—ÁÜ]…¶\ì~÷?½+ŒgÉÞÚ÷¿Ì[ÈõÎŽó½ï~cuÉŠ‡K䯒Çeô*™ÿåÏN~èZü4ÁuõÑ‚þì§þŠÈ,ˆ·Á}Zo=ôjiú9þ´èÿèï—F"&lk—`o—lr7Zt'zuáhë·ÿ͇V¤TçTó7xãÆbˆçbq3hý× øxiôcåd˜y›§o‚åy‡poQ‚û…h#è6¸’—>žåz {ñp¸G{¶`{W[5Øu‘ƒ x}Ñ•Pÿt~u÷8;€Ë¥ETX… á„X˜'xIQØ…^xWxY(æT†Þµ†!¨t`¨†b(<'aÃQ_sÈ„uh‡lô†ÜQ]i˜‡j1†eEd@æ0dÞvÿHa‡C!‰&˜}it «El¸ÐRM@)Eä L€ ß” .€ TÀeUUZ¸0ŠG…ZÆ– )%w• r׊…•l|æqF‰Wˆ–Ø]£5dæ&R‰XnÇÈW¥• ¨°‚i°æpŒåÀoÒxŒ^EZU†‡ Ǩ *Fm¾¨‡€(ŒÃA·`R{Uª@†õm‡ £$kÿÆå€ ŸT¡øIipU¤õŽð…dV'û&Ž]÷tÀHsæHu€¡Re ˆ ‡ç° ¶[Üàm¢x`å [¨ä ‘é‘Ùõ÷ZePqÄôŒ)‘FÈkºWa tkaˆF|!‘OÿH“L7“Rh“0Ö€8Æ“_Ç4Y“GùX8‡D©M)|?™”9Yƒsö”Çç“ßÅ÷FßdP P¥ep ‘_Yq¿§ƒ{XVé}Xù“[l«ø^¼(ä°ZØ„o± «Ðç` {‰_l™oY‡iRyY”ÑhàxU¦eæ@.0Lpe° ^¥Tïyl1ˆBÇ}m±˜ŒyE÷d0xo38N‘oß¶d@î(f·x&X~0‰‰ƒÑ›1œñvŽ‚’äÐ’aœ–)ÈœäàT€XKA^Âi…méÕysqé‡ÝUš¼É~ÔGIÙÿ¹N«GDÞ‰áw1ž¥ç¦Ùa\h”è)p½×dî•@¹_¨|êyì™ÿ©Oø¹}cñ[zýi˜öyžå™Ÿú)s§— q %A¡µ…æ·hèÚq5Ö¡ga¡…¡ïY•¢ º¢+šqoQ¢ QS\ÉÚ°9XÚð™ò‡›Ó‰ úU :¡,Ú¢uÇh":Tm8—ÏÉov‰nç  ÷† «0O¹lº˜Šð•—°˜MW¥–ï5Š W—›XV‹Ÿ¨u’ç£äuAÊCš¢ù÷¦ ú]6ŠÐ Ð(‘ÉY÷¦yðµ *vŒÎ_Õˆ ÒxZä Q±VZ©ÿ lŽy¥•ÝXvj*^;emúoš©º©pŠ0šP*\©«9WQ:¥µoò(xódªh0’ù^Ÿ´ æ†Xö8’þ(ƒÏ÷‡,Š ˜fcŸZzrmžz¤97£zðdœÈù¤ p£´j„­¨‘ð¥œÝ™|õIª€ Æ©«%i‘¡¶C:¬àÇ©#q¬z‘¬*8‰Û «©g®ç:‰ІêZ¡ÁЬ슡zp¬ÿ tý*ûŠ|Dê¯Û‡Üašî*gçZ¬Ó—¯yذ;±LW°ÔF±qj±ò)ë±N¹©Q–°*²Z%»°ë©)‰ {^0ÿå¦,+±ëf馲O°^±˜9«³Éª¬à@ÛknšjE[³IGF‹¤$˃O[µïª¦™´V»µžv³˵`[ ^û«a[¶û°Tk¶j»[c •kû¶& S‹™” ·v;]›¡ûärz{·~‹·ë†¥s‚û·†Ky$;…{K¸ñy¸Ž ZkYæY®Œ«¸{¹n¸U' 諘û¹Š‰´º9º¤;ºHYº¨›ºº9°ªÛºiÕ£*r9庴[»…˘໺»»¼Û»½ ¾¼Â«»À;¼Æ{¼Á[¼È‹¼sK Îû¼Ð½Ò;½0½Ö{½Ð[½Ø»½Ò¹|—PÙÿ¾â;¾ä[¾æë½ÁÌ;¼ ãð¾ð¿ò;¿ò«¾ÇûBíK¿ú»¿ïÛ·£±¼Ì À÷+À,¼Ê[À¾Û¼Ü»ÀΫ½ ̽üÀØ‹¾ŠiºŠi¾œÁãKÁµ¾€‘ÀüÂõ{À ,Â(ì¿¥ÀÉË ìÂ.LÂ0  ,ÁlÃÛÁ8ܽ4¥Á>|¾(øÃB,¾\TÜŒÂ),à üI¬Ä!¬Â§1ûËÄ,lÅT<¼XŒÀ5¼ÃÝëÅÖ«Ã`ÜÀ=<ÄBlˆ`Æg<»Gl¼î Åük¿nŒÄZ Çq¼}[LÀy,À{œÅíëǹÛÅcÜÀƒ½b<ÆE|IjÿüÃh¼È>œÈdµ¾ÌkÇû+ÇZÜÇoLÉó+Å©È}¼¼Ÿ È4,Ê‚<ȇ ƧìżNެÁÜÊ É–LÅ™¬Éñ;Ë3\˶ܿxìÇ °@Ë`¼Áмë Ê ¨𠺫¿ðÇž<°…œ½Ùü¼©¼Ã«j°ËAÎå+Ë¡<É»|Ëç|¿é¬ÎȵÎÂ+ðlÀ`Ìõ¼»Ê¬»Ì  àð Íœ»ýlÍ~\ʈ¼Í„ŒÐß¼Fä\ÎãÜÐDÌÆžÜÎð‹Ë0¬Ë»ÌÉóì»@€N±»° ½­ÐÁ¬Ð$÷ÿ "pÒ¼Ó2m÷¬»ùœ»û,ÕLÃ/ 0ÔºÐÍ-M N­Ðe ÑáûÊT æLÊÍËZ½ÕíÇýÀ<Ò» à à ¹ë ¿ þÌL  4\ÏÁàvÝ ðÖ/=ÝÖËÜÌÍ ÔHȤÌÔÛÜÍ8ìØ6¼Ð tÕU‚ÐшPÕЃ¹;CMÜÌmË-¼ à¿ü `@ mÉ]Р­È`Ç_Å¿lÍÞ 0í»Ë L»Â ¬ Üw Ó ð,ÀÊÍ ?Ö„½­ º;‰ÿ=Ê×ü«O ÙLÞ,ÙEÙXmÙ-šÍÙüìÏßà `×»PÚ4¼ÕãÚû À»r oÜRã w@ ºÝËY,ϼÐÞ0Á=ÜÊ}ܬ\ϬÃáàÀ×¼û à ¹Û ÀíÝàð#Ôþ\Å¢¼ÙâÝÔOÕÛ ã<‘ƔƗ-¾˜€ €¦¾B˜¾“`,0  <€ $ øçö‹â¹mÀÏÖ'mMP#íå$ÐQ0sÐï[ Åü-¼ºðß»¾ î[ `àÍàTÜÛ.nÝ®Ölÿ  àÏmÀ°$NÓi=÷ »ð @ânPÝàÀn»X€á #À ÝâÄ»Ø1ÞØã=Þ=,0ë™íÞ­¬ãâ[ ` Øp PÕG^ ( Ð Õ Lž ¶Îf‰ %І©®»l€áÀÛ OÐÖ,Í‘þ hžÛãÐtp  ãÀt@çð|绫 !nྲ í¼ÛTÜÑ Ìþ áþÏÐï)Í à/àÏp0O@éà "ЀàPðO`žþ 0àÚN`×Z® ÀíZ¾êáírÀ Ü, 6ì Ð5ë=‘ưãU Ë6o.ÿ? À* p Ø€ã× Ù0 B¾ÏPXýÞ²¤Ðߺ»¤¿àWÐ $Þà÷ ¼0%Р㠲 àí(ð¼ Ȭ»VêÁá %ð¾µ PvlöïìÉz/Ê È1O° Ü 6Ü €òÙlÞ ü÷9‘ÆàòʾB° ×€ä cÐÙp EŽC€ëHÞåkë×p?Bï‘Õ0nH¿…èàlàåŠÞ V€ç#ž»pߦM~^O %`[Çu¼»PÌm-Â,à²àçäþ¾\0 ž÷½÷|ÿïÆ¬ò"ßÀ„ &ðã›à¼M€Ó@ (ÐÀ&€&pþÔ@_`€Ã4žøqNà@‚ D˜PáAÙ8¨àÀT¶†Ù¬eË&&Q6h ªe´­RŒ×,D ÆlÕ”Â8ÑaÉ\2À5ŠšS€RèÐs€'¸onw4Ø7pÞxû¸] ¼%JcÜ8ZÆ’`@lìZ¶mÇEWî\¹º<Äe#.€ã¢HA6VŠ·…Ù=)ô(]Æç p¹ñbÉ•-Ë¥|Y²O¢)4hà €hÍ,8£–ì5jѦQûÒgÿf°%Pk5§ÕÔ8b\øpâˆG~œsg:txP&aæ €†¥Œ‰T¨ð°$%!ŸUàèL%“Õ 8àÀAHL(.X“ [šU@A °ÉI8Ê=‹Zl€ X _‚ì<èÀ ³X: f A )ƲƒÃV ³Ëvé .¶QcÂé"µ0fEÃZL,%Í*sqHºŠ42ɨ”t,È :Ö¦!RÀR‚h¨áã„, ‚XKæ6F¨¡†´ô-97YàM9‡s²3º9ÇœrAh¹'j(€•Xj¨” ÿxZD¼E†0=‹*â‚:««ˆ%K/½”§lYB;Œ&ý³3¸’„Œ1 “â •É#W…u-ĆB’ÉR]Å W&kµ5T(XfÊ*o. ¶üF~ ÓLã„“&Î9•{mZ9ýì¬rÎ!çnÎI ¸é \`âœ\Va…mòœb…ÈP,›ň;KzÀˆñ:Â)¤j´»FPEÅ4aLÅ@¡¬ñ”"_luÈZOe˘dˆµ­ŠuªcXgULÉrLy¯”¼uÈ“ãz9ª‰- Xj¨Œ†™4açGˆ¥Æ –q`Ë> †x¦†ii­îY¨ÿÝ<%ž@ÜsÊy@ 2R)J r@(çrϹeŠŸêà#B³¹¦z‚ÑlP@l,¹à©šV…'\â™™ûXWŒEf‘W%g¼0 i]¬•¯|.Ì œR¤¼™4j"ÁÀ ¾hÍnøjþ¨Á‡"nóÍÓ½`Mê©©yzwå`[sV¸E *VÊœs8³åÐíÀœn¢»Eù”0ЏpM%í:¿¾pñ-ÿð÷6·8òÇrõ3ŠòË+G?ó%5‹y²Ã¨Ö÷âü§6ˆ#€þMM€ÃÁQ€§s¤A:çØ† VA@ ÄM0ˆ9nAÿÔk|,a؆©òY L02‰  àƒ„2Ÿ¨ô•=î}’«ßeÜwÃqL®d+k™âìg™q<Žî§.±„QžÌáT dx×òÄ&€,¾‹Ê3‡¬gŽï‰ÀxR ÈÏ(ã§bH”Q àòF1ÃCéQ2;¼!É„4?B12~<ä—µE GÒh$œ€÷€(€ cTÅ ‚†hr] è€tpR‚-|qÜ  h*ˆ`€‚ü”Q•ûkFP•ÅDAxU’bx€Uè ¾ |€jác‹2È÷ùÿp) "®²iÈÇX&‘Ià“”èÄH©œ¼3ßñæX)T&ì–±;1’l\_/|çÄê(Œ¸â~PG4•V€ã è8¬Ð†`tàTňÇÁ>WQS}‚\"'C?nršû€æ$Ž“8y°Ö#ËN-]ëlg*ŸOKab@õ„cóé«}ÆÂyDŠDÀXDHÙ…,°BhA "Žhjt2|4‡¬â÷Clr´£¥H™! G°¤×Ji‘(Æ9Æ1%ß“XéJXjÇž¨Üi¨F5€`€cFG±ËWà !µKµ+b`ÑÈQtWÿÕj ­z¤Êm3)¢Áâ"Íf¶~–íÕÌ€`IRò ,àƒ×à 6h5Rk´6 °€` †’0LÀ‰0 ÉÔÈ„ÚÅ ÖXÒŒ`LàºF²´­â„«;ÿ”Æ5@&wÕéuS²ôu8Š+b€ö¥Ú –Š…`l,Ðä¡4+ Ù·lõš m’.6*Äì¥U&âGE[3j0¢¬ñmlLKÒÖ°æ { ’#¦Ñ$ƒEènNC N MZÍè€3¦Á‰ äîÔh„Œ³¥êŠwÙ%ávOh`‚%൥­Ó ¾‚cGùÿb(Ör»Ø%8àppÄ¢™àÂû²Ú_#}Ssü¨3j2Æ@˜þjcL*‚Æ‘` LP JÂÑÜ-„;7íYÀ•°´¦h<«PÓš¤1 •.c4 ±µ8À1 »8Žë¥1-GK3d²¯â¯cm¹-÷Q²^MÙ§§`}~N8ÆB–ëž& ÄšÑ=ûs i1â¸Â¹5„@ DÒº ©´¥ßŠã¼eÓ0ì´AD-`0O›eÕ³u*»í4ÓeÍ,Ûi…ã  -ZÁ!÷4¦!´`k"׸Óx&€-5#Þ=»™´˜QèhлÆîaÂ*ÿ¢íìgnAËÎî±mlíô…ZÕ{ä¯5Ç Ä3 ±Ûšc5OiÜÀã@¹Y# Õ±ýÆ5kÍàšu_€ F§ÚÓQCäÈ­ø`ìdee‹6ÂáÚl¡Ç´Ùâ|ø^°9`ûÔ…DóÅåòí!)qç4)³î?«œŒïúן}ôƒ'DK§ŸÓå×UŠ__+™„î/>àS*û™·ã>:@.è2@Z`‚9˜q8†&訅.`ñ‹È[¿Êª8Lj»n’ºÍÊ8½¾E‚¿i =ù°{’æÃˆjàÀs€J½ÂQ?žâ•XxLj…‚¸Z°‘ ¿q … ³ÆÐ…¤‹“IBSÀ9Ø‚3àZ ¨ƒZƒZ‡.¨…Z S[;—A¨“ºôS$á„CŽÿHPÃ$"Ž4L8DŽqÊ?HMøƒä¸A¯K>¼Ú.MyÀAÙ€¾×Ó®ãû˜X¨‚,ˆ3€730ƒ5¨2`+ˆ<»qðÂ<À\À|@ œ@& ì‚&¨FtDH|`(3@ƒ*{6¸&PB¹€<YX Zð>;¨Âa‡3èÂ/¿0ÔÞ#nê@˹EŠ ø‚Hèƒfƒ/@©G°=0mœC–³FIÈÆmìÆo ÇqlC2Ç/È„×RÇ0Ѓ߂=˜°ÃßAA:Ê»=,ŠÈ0°›ê˘„l D”¿aª]XÐÿI$&oxKìAPóB9ˆÂ)¬Âq¸Â,ÜÂd$B˜ƒŠ‰È‰ÔaÈ6Œd–tBO$@ä‚bÆq°ƒY0è‚ †c˜9XÆÜkÆ tg$ÃfT¤?øŸ 1nÄ †GøÆw¤•‹Jjx„© ƒÛЬôJ­d¤×‰Êi`CÜ˸J< H0K^ûG€¤¿íâù‚‹pˆA)À†$œþCœ"ù7À‚dƒXð«`x…ÜÈ/³É_ Æa,Æc4ɼƒ9ÌÄÔ6`ÌpLÈäD¥ë˜3è‚-0 Œ’<ókÊncÊ?:Ceàƒk 1=„ i†¯ü­¹4'ÿÖP†>ø8ÄMÝ|„<”C7NáäÍ=ÈMØM;LNJ«Kz6ìÃ8H¤›‹VÀ‚¦hƒ]˜‚5ð€Ñ‡`€™üÁ:@œ,†îãIŸ„ÀÊÈÂa¬ïOñÜ…*0OôTOö„8ÈbÍ "nK™hĸÊR$Fø/`fGM‚<°€èÄp,Gû#`„e¨P Àƒ åÍfàPål„eÕƒL( ÕPEŽíì´ì,£tŧ‡ü?XÔ4 VQ¼^t8ò0¥Ü•ÁÊÈŸh쿜)•*}U¶€Ü.-UÄ•(†Ó Ú™E#T‹:'­¼ÿ4µ¦;Ãê:§ÞY$,2ý ì$=&íK5Üû¯f4S©‹ÍDš»r²RªyÓž«Ni»SMÃDˆ)G•SÿËSL;O4Â@Ê6Å&%U’@µ8 jSÁ%ŠTñ¢Óñ±Sò‰)î LÊ#5Ð" Z˜U¤ª‹ÊÔ2}MÆ`P áÔŽòT¹Ó§7%V»KÔŒ HD Ók!ñ@Pp0èEп– §K %0Dá±é€æ(ƒ&°…ëSÒSÝãÓ‰ë²2ó=ÊbÒûÕAõ¼Q]+e=V@áR¹…ŠÐ”J€Á³†k­h xlÀž`Õpå–q- ÿ\X8/å£t%¢vmÍ$MÐy VS URÅW*UtíW÷èJД¿£Œ(ØDI Uˆ%‰mUiK¿uUÕTÝÓÀó{Œ_e3{¾b5¶}»MÓQJ©„%¸€OÙ˜]Œ€† ÙKyØ®9‡nØ’åÙIÕWZ]-?¡õ¶ý£Ü‹]«‘õŸ…)\rhˆ‚qXOqÙ¸¼i‰€£†¥l8 (U×0šS‹å!Œ%’³eˆ¡M[ì!b[Í[­SÚcZ†8ÙêÀè‚yYÿà€ 8!Œà›¾98‰U )(ƒ°mÜÇ£Ô틸ÿ\Ü]]5oC Ί×1”Œ^}[¤S¹½ÏE£deTL£[Åp\L%Û/õ]iSÐ3Å ÄÞí}WÍU^rZ)¬s^е[¼»Ë.½Þ‹­Þ÷!ÓßÝÔ×ôÞúÍ\zŦÍå:æ-¾óUÔô¥˜E=ÈÝ ŸÕ&ÉÕ^à%S©_ï%^˜YÂÍÁíâß©SRõß÷€^õ;màaÎ:ƒáán‘­Váþ`Äh> aža6˜†a£páÖáþ`6aÎ"â"Îa£b6â%.bûa Fâ#nâá avàa"®b-âìÝÑNb,fb+¾bãÿ*Îâ#vb%c&öâ}=UëÛ.(¦cïÍ`QQÄ;ÖcùaÓ×=¦”‰ùc@FßëT¶:FäDVäEfäFNdBöß>†d=~ãIv« Fã66âéµäýéäO¾äå ŽßQ޶1£ç¤Vê=fê¦þ槆êHžj¡–êªþÜ«ÆêiÖê­Žh¯¶é®ëë±æ²6kzNk”F뵸¶v뛎kp†ë¹~庶ktÍëŽÆë½v+¿Öè¾ll‹ìÂÖ+ÄiÅŽfÆ~èÃvìl††ìÉÆcËviÌæÊÖl>väÏVæÎ–kÑîdÎ&mª>íI6íÔÎjÖ†äÕvíc…íÉžíØ®ËÚvlܶíÿ‡Üí…îm‚ÖmÅîß~kâFmãFjävÞáVî…kîv~î|fîÁžîèþkë®ÎêÆîlííVkï¦hð†cñoòÎnóŽjô¾mõÞfínöÆf÷Flù†o4ªï<¾o®Îï_Þoh¦oêîïÆppdþoÀ>ðþNð½^ðünp»~ðúŽð¸žpö®ðµ¾pôÎp³Þpòîp°þpðñ­ñí.ñª>qëNq¨^ñçnq¥~qåŽñ¢žqã®q«.pÏñ9Ýñ³îqSýñjòuò]¾ñŸ>òÞNòœ^rÛnò™~r׎ò–žòÔ®ò"ßï+ÇòûÖò-‡ï.÷rõó07ïÿ1'sñ6ó3÷î4WsìfóÃQx€9 ±E[h‚ ŽÓ®Ûó[þó5§ã£æ°óƒXo!?és¡8t|táþg¢Ž¢‚ˆ—˜ëÁvmT%>)<&Ø(ƒ)Qÿ$wuR2õ2€К:——€4Xà“Vsˆ—y__t4`> —qWsAuawöv¡„¸ôKZžr=Wb_ôTw>AusuövÙ†höpwuTWu>aö—ôx&j;‚Ž—:4Pn©ô­€sÝô‚ Ø–TÀ à“mФ±)›³ˆ~ÿw B>ÿ()žM7x³×´Y›‰Gx@8nPs€Ø¯© n!Š·xUF +ƒzŠùƒ—X䣨é1=–Çø(yå©ù¢xM ›2ww¡¾÷H…çéz±µyø< xBúrð‰ÄHæy&ˆž8zpúshø‚ð“Ä(°…pp虞2¨žszè£ qu*° :æqèáêÑסw Ó³úº§úJs{m’µ§ú»G{å1üð{¥o WgBß–ÐPž R{¤|Çgz¬÷  Š ºó¦÷‰­ßh>ÿ ý‚Ð R{ ÂhètrU˜öž_ýÿhý:ˆÊ¿| ZŽÜ—6ëéôà‡}Ýß ~-âü7ßìnê;!\€}& —~g ¬çüè?TÀ зwmˆ,ˆîßð×z‡ßü r €.â0#ø—ä¸mðÚs¸"óç rÛÎÛFÎÜ9sªHð®&™9'à!9Ú:|`UÁ 1jäx0áÂŒ Z$hNÁ‹gÒ¬ió&Μ:wòìéó'РB‡z`ô(¢J—2mêtgRlb• ‚¸@pÝ&óeM[:09øæ¹\+@t(CS,Yrç@t£‰¦ÃŠ˜.aªÅðÁíV®©ÿªëö¡4+:äz¨*ïûyÕ²-³u±*‚+?4gµ Ö–)·¥™xqc™„×¶½Ü!sZÖnÏ”üô6îܺwóFz”7ðà‡/~“ È æ\þü¹åëïO< Nät°›€z =…'‚3ÙB^ "FS35(!M ú·!|Fqø!ožSanv´›j"Ú´ C ®À G8Y‡Ñ€3¥¸“Š ê¨ÝŒ;nø wrYTF¶ØXÆ 4dY礀 ß=´J.ܵ„-"éÿg”­0H*I•VÖh]˜I6Væ•rÕäjZåw¤–_š¦Ø˜ç$õd”‘ °~æÙ"™U¶I uÞÉAS,F…9úx)¦™B—‘\©Hä'.5ÐØ6+pzާõ8¢åœsew®rsf¨ÝˆT²8jAyeäj›2ñZj7­:Y#A§¦ê'©yäÑ-ùšë9³ÒôQµ $lMtékµg² ,²ªB+-A®žSFf«jú.¼ñ.eËér+ê^ôÚ› Aú4 -¸eÍø¥- DéBåX–¿ÛˆkhB Ø’U¸Ö{N9÷‚eã³3pNÀ3QðÿL„f·uZ2ÁÛ’¼±Ä¦…\ÎȨ BE9Êû3ÐAϼ¯h6WìoÇæ:ˆq7š2BsmcËÓBÌt2寵!­õ^×NX1ÉU‹M`*=“·-QïÅ5}¶h`iB«då®Ð}ûýn±r¡"ÑH5Ul]ƒó{Ž­¥2 J1ú Ò6"EÎm£:ñáÆ¦Qnàç(^7Zc98tâä!)½`ÛI]ލæ}:L·|JQÑûþ»pÁ)!mÆU^º05’vØiÆVéý.ß¼´QI(oÕ$îv%O[l`pbŠI/“mµ-ÓóJÛr}ÿ¹¯Žö9Ÿ…Ö>ðýûÿP|¶”¨¡¢"< ¨@‡OLP×#(Á R°‚¼ 3¨Á r°ƒü CØ?â$G2ÑÐ|à²á\H„.|apX¾ÍQö€†s#ò°‡Ê‘Ô h6eML‘T(¥sLA[£˜¸‚'i \Š ‘lá¥Hñ‰[Ä¢œp±Àè‹7!@]ÒÔ§‡ˆ‘+0B"¥ºh@32ŠNyT—Öò¥T*c3cÔÂT$:úN ˜Š‚Šó g9‰Å!áMéHœ p·ØX ç¹35‹ca—¹.é£YG” ¹V­DªQËZ¦ÿ4¸º6,S©Ð%±¬ßL P—YÎ$#.CˆºØõJVêŠnˆ–‰®r¨² ƒ™I0a€PÝr„9L$q^d´Å G†?Ñæ$oÉßÙª ·ˆZRâæÇ‡Á$;3àEÊyγÉDžQ Ù9ôd„1a`³'?ýi°|m¬_ è'ÃîI“Z²Sc4q§ xfO’ ,fŽ´Î(j²‚QÁ/Q¨Î"*Dƒ"e¿«å3…¨µ1m@[8㦠ũO„$Ù›ÈQ†AõÑIv¼#ž¾(ÇCUñŠçä눴Š&RÑk9}OmħÆð‘ ³²”PhÀš UFºi~Ìq 2tÏmëÚÿYî–7Œ=l¬eÛŒÄJÖkE-m¢yMä ¼2ð%þê—ÙÄ)òõ« çPë9ô–·–MF¦ƒ _9âÅα”Ýg`'€tàRR2ãËtÀHÉì¥:%^V“$ªË ׿'dšª•fkU•J`~6 Õ0]‹ªOµÍ# ᆶ˜ùÉÁX§šÁ­I4W©Ì®õç$ h±\õ9«)@wçˆrÝ”Ⱥ IåTÙÜ’°Ä¼ÝˆäÎ2ZŽÍŽ0$«‡‘Àˆcù®yaI€ÕÝŽ6©#p>Àºÿžƒ “I}‡Ò‡@´ù+Å>æµ%­¡0;Ù=¿ÿÛmR g¶h€ fЉ~ؤËç@ Ê3¥Yg¢&Zqצ§Ž¾ÄU2i±9l\“Á3²‚gÊd{hè^øôÇVˆpeÉF“Œ’Ÿ™><-~®Lüª§ä2ʆ Ò{ˆ.õËÆ*sÃ3W¡B–WcñEj1ßaS7ƒæÁ£)3ðpXY¼%6+&la÷U¼ÌcÉñ‹IY—0vÒ©ë6,ëç¢éÕ¡ŽF[l_l×m¸@#ml^[MÍÒ˜"àV3Nà!çƒùeãî*2ߤ‘m&¡SœÙ{¢_KÒÎa'9£'ÁªJ¯9Ì;Š5W¼ç•[jEGÜÿPc«rY‚Ë ßöZÛ ™‰´{F’•xD¼ï#+Ø)y0*S©Ê›µRe¯àÏAß3‹íµx&í£M_þBeÁȹ4[†™Ðçìé›áþ>^Á.S=#o2eŽóffÂçÐ8'Ì><9Êû3kàÔ(ËI9Ìc¯•ëfÞ2J%-4¶èTR’+âfOZxó¡]Ö9Ùat\Ô_›X‡†8ÚyÑ£ó4Õ«Yâõ&#ú4P5±”¸¼Åªú1iè YWPåŽh@CµTm¢Ê$! §ARE¡p¹0a%]w”ص.r­FjRÛ:{ÚÒ®.°Þ<çOq|²pÙ” Ñÿü'BÇ&s¾H§‡‡Dâ™&ãÔKMÛž·¤çe¤ñ¥*ÚN·aÍÔòêÏ¡ Àˆ ©`{‚ë.o…nYMg•j¹‘Åx´tk;&ºr[zL¦«·³•=íÏaûÖK½w¹Q‘çŸb¢à ',º6ÛúìóèÛ+#©@Y\Q&ˆ¤¡Öß&FGdkëlÚ€Ì*Ê€ûÙvu±h\„ˆ=@9lh½%Hüi”EÉ͘SÔ™‘Äý‘‰[ YQgXà Õôè‰5ÜõËS I‘TјÌÝßÑ} –XQeIMqF RL&–¢à’ ÞàC€•T­güÉOÅ¡Xÿ‡5Y–`Ú]V§Mù8Ý‹-Me1©Y ÛÍÍbÍUêþ^% r¸€9€@À^†«QÚ\±šLlab] £èiè9óUê­Š²| è]K³¢l%‹±ÜEkåá­œ^'ýŠýÈ,e×±PH¶¥ êáò%“òµrÉLŽØE,â¸D"yt›z!"»…J‘jœ[_˜êH¸ÉÐ2Gžƒ±¨ä”Ûûñ^¶Ø[b=€4A¸Íbؘä$K¹¥›hÐaþȸ‰ð¨ ÅÔŠ=à<}XûáOxŒ!ÓÑ,HÄt’©LÂ,LÃxÍLÿ ;©u%Ú§¹ßÎ- êØù½ßˆ¥Óú¡ŒÆcyDÏþõÄYløÅlàm„œÁÝO¯ÝÙùÇ 5ºÅ*¬À”Á€($ÿœðÉïáÔñt¯¤Â‚ØùP$ú,œ•\Vä¢b‰Ã%P¢Ú šÈUšÙLá[pXÝÝŽÅå!’ ÖXZNJº!ÖPÜÑœUýˆ¢µ!Ù¼¡¨í$ßx‹V:=%yTžP¼ÜR´\õa *«¬"³)æˆ#Mcá%âh7ÕNÃ5>[HÀ’°U^¾Î^PâvùÚ0JDU~-:_z› Ú„·ÈÎ_¶D`†%|ØÜYfŠA&ÿˆ—UÙ3%\CöZHŽ$Ð[ÀIH’ ýLÃm&k†æÇÌód wá ÷„Å¿…ODÖÙD®ží —±Oò°MÙäaìÓjf¦r¦‡XêGsò¬-§tNÐ<çN¼Ûtf§vn瑟S\ž÷æAˆ?ZŸw‡$Aw®§'StßÓÕøõW»¢ä 8Y'{  ÚB &a=Ry`JØIÏMƘ¡×a¡ ¡à)¡™$áÙ„òàMðØ‘€ž '%ÅçP˜£ÜÇ„ ‰úˆ!®% éI×CD×sëu"¶|â½ÐÄûPŸd’ËòRM¼ÿ(kÍh.WÁAØŒ¢ Á§8í6î²Dy¶‡ ”î96šµ¬ã½SèA ü¥‹ü¤B©ÌP§¹Ù@rÿy#HÙ#¯yšÎ‘Ü>Ýi•jrR ÑÎàQÞ1U¬J](blÉfÑÕy‘àjK‘™ žQqºÑžÒŸ*¨Ö‘]=j’¤VêtàaÌ—Vx°Ú¾ÕÚXáMÌHÚ”¤ºLWŠ:ya§……QVϲÚò$ԉЌl"dDWêWáž¹•¥mMÎÞ¡ –Ïb«EáÆ­š bÌCÌ-`ñçMÿ%æ …b†w5xõ0&aÒfÅ|î´"0† „­nc¦âZ.›µ½‚ÉJ•C„å¼å¢‘¥Kºµ%cÖÍbÚâ2âÚDlÆn.óÖöšÚÚ³(¤Þf”囡(äØ`™gçú di|™ÛŠ›ï™DšUå˜É¤BÆMvœ~¹¤EzmÚb¯j móæ¯vD'tèçϘ¥þ0~`gÿÊ›wM‹…T(HE‡þ„Ϩ§nüy®\ 0ËË–ÅÝ,«NøŒÿú„C€Ó”þÜÛf0 ÃRªÝ•ŨƠžüçMtpLÀ©fê–‰©vV±j^žÕ¥ÿJ‡ ÅLj¬½° °z^0 …yšpå91y¸çx\ë#^âj˼«“Ú û‰iM˾òå'²Ý½6—¹ÎÄ01¢9àŠ®Ì(þ(¸žÌ:k'n®Šl°Õ8•c(†ç Wñ}’ÇÒº¡Q­ÈíÍêDÛéÐÂ쎟õËÖÄD±£ÂôS,ÕÙÄ#Ë^”™žXŠ9í“B* R^èÝàÎ ‚þ  î°i Þ6(ÞþY(Ö­ „¶0ÝÈÜáDì]ƒù/qg\hWá²â! ÒÔh¥zvÐjñ%ä^¥¬:ò[á* KMA”Ú©aeOÎÕR¦Mda˜hüÿ¨¥ +>m â­·î!é½kÆüaN4kŽÒëë]K´Ì^íÝÞDÞÚñîÕEï ¢Ãj±ð½Ê Ÿ1õsWt!Ÿ@Kß`ä–‘ÖJ"BN“†JvHÛ¦…1vîÁÖ¢•Έî®Ú77£[²*~Û2BØ^R¬è¢„Ã@ÛìÈ1Å€r}¬éÈWLÃ62mI‰2™‚ãöÚßÌ™mÒÚþé_A·Ý˜ÎS0þ_±‚Í`.`>^rVGà;5uî"v)× ŸnGÕfZæó~ŒüÍùRàK÷æœmíÆ9‹m„ÙZ­E¨O †ï¾µ³¯‚­öÂ/­Cµþúÿ$Q%Ì‚³deáØddÖȰ&t£Õi›~Š®ª!n3ÄÜa§É¡dÕ¤«¹j¿ÀªÓŽ0Nhn”¦ðO¤%Åu¹5[ŒÀ¢c°J£ŽJðñ~¶.Vc/JìxùŸ& Χãac2>7ñ6csçôuI#MîbÀ>c¿: ã@ÎM{lafS„°#6g~\÷.¤Â‘ÏCÊdgmEÊýRLF&Gz¤aK/›’ ÁQ ždJ®¤DâµoþwÅŤږw5ŽoÙbœjÈ&öŒ¯|÷r†xNøöP𶈧øˆ_櫸‹¿8Œ¿Ë y0t4ˆ{¿E3ëvù‰Ç8Kÿi‚”°p¨‘ûñÒ½'—PyÂ9”C°*3/?‘ƒ~Ç>• ÖÄ«à.òi §Öà/{U†:3„ÜW2/èT)s ¢ ×ñ œË V(í8‰1†Vp®RH!Gz>ñ ûâ0õè(jqµöŠMÈñBsÌUˆ>ÓX'•n¯êÖuWìeôôÝ1E—§éYº¶vê K¥S+ñ± ?„ Ñ ÿ6HG~öñ ÷ ;e&+`i{Œñݰ'Í.»ßyn_zSwéZgx>í>Š*lrBÑ£<†û•äÕœ¬ï¨£®`‚°r·ù•\TI¨ j…(ŽKÿ³†ú0ªRJ£Á‡JˆIá¥*õÉÜ­y5(¼¤óÔk§!z¯×&±zš°_õÜ¥`^¥T^ú]uõlïâfG®ciÆè;ZÕ&rëÃÿr«&~ú©î¨¶ôh‚"é%ºf+"¾³0z'Ó`$ë"Št’¶ë¢— ði:?'_HÿLZê%v³âu1¬¡X,ùÈâˆü.} ÚèL7]'HÝ™´ª»Ocg:1N<³[Mñ>yëlƒ ý%©3ìG1|æ\}ßä©ñú"?»š(Y:dÛ»¯‡2žŠ)(ë½»²Ã+Tû=и¦-˜O“l `À`tí]™-ùö˜™$œlÿdGˆ~'¶£…“ñ­·Ì¥XÐTá ønÆï„¿äa u>T‚EÇ“ëû ­Âó­Z|Ÿë$¯¦¼²º XÒ>Þ׿±æmV$káÒ6#ò¾¸6duóÏüx‰ëbtðo}HSPЬë~wJïL#}‚Kæ*’nÄýy1½Ò7ãdfìwmlì®4Mtmž´K|½C°4¼ùˆó§×Çþ7Eÿ @˜k2 •s¸@$LuŽ!C4VœÀМ†¹V|ÀP¦!ChVtÈ%Ñ!ÄŽç@tóxqˆ'ž³åb ™‚XxnÅȆ¶:t`B˜YºäØP Aƒ)K–8‡QÿéÊŽM›d€¢E™A‡ž{ñ¨WŒ—Æ<'òd[·oáÆ•;—n]»wñæÕ»—o_¿<˜pá·æ\V¼˜qcÇ!G–<™reË—1gÖ¬Ùˆˆ Ó4TKwt^r7—îÚ*ÝÎdá–>—5ÝÚ›qçÖ½›wo¾+¸T«:.q»³S'o=8Ãmp¹vDþÖ¸Ûê¾±g×¾;ßΠ¢° h.˜HLÒ'ÕÏ; XÏvjË÷禄¤bŽáª.ªÏ3ø(£ [ÚR¥<\ Š mÔc/©û »š;ÇÃØ¬› ÊÁà‹uVÒŽdŒÀ†v]Õ/[>=§œ˜¸ìõÔréÐë€?HEmÖ–g£5'hAÀ‘Xç:¸Z&¤=õ­Êé‰ m\ŒŽYgÇ}€ÐŸ·\eݵ[r§EÿI¥GÅÍ–¤oeM³›ʸ…?/ÏÙÖ…nçíõà„ûÖ±V㛯=û<¼Se´‘YÔ0>@È%•ò(¥QÃÛ²q=*c 6PÌ„‚ãÊã'ÜÆFÏpÌ0=ù$¼p×oö黡†¦ùQ—Ó‹îi¢$²9e‹ç"øØ-ÿ¥Ö*¤lÑ’º®d2û"ø[Å [ :§›pÕVÖ-*VqÁÒØ©¬´…û©î“¼ü»ì·,œp™âžû$sn!ÃUµZm۠Ѽ„\r®ÃÒ5Ûl´ÒcR”¡T‹ µËUμR^9wŠÓ=ÛB}ZÓ6§D;°òßÁ^iöl_\MC‡'ÿ¼ÏܭIJ‰â‰,¼Î+6€‘¶ùŒó·º¡³“ûor €o×è"ß|\ÌfÇÜî]ó·jH•]Wy ´&øµ(‹ÔçœPY B² \QÆW>6„K\!à'¿T¤‰ü1ÊÂ>†¸Ou1© Í‘‹8m¯0ž«Ÿ±ôe¯~5ð„rC˜ÂÌÕ%nƒ ‚RÀ´–´¯{udb04×{l¡ô¸ «(–ØX0rqã… U¾à%0±ñd^@o×®Xñ„H!á[€"¬…S°ùKÐr@/že#6bBVâ6“šÜ„*/iMONÒë5ì(«OGSÇ¥4d,DZcFÿ62:«ò„r´IG:=D*XÑÊhÆ‚…¬Å'_Œ i‘ù%qªÓÜäÜâ¥÷lÃnÛ(ãrÊ­L U¬Áz•8XR$r®’áØD9¸.ѲŠÅúš,£w$.†Ò“ͼJbœÍ/‚}T ø¯èx郀¿Äºm0€?+è_¯È1¿Þ¯!Ýüf8e%¯ÊÊw Š ç¹ wV¬h 6!%Ny €(ßÛ'Ȩս‚šíoì”æC!Qí€Òl¤¿¬¢„ ¡¤p@BUh¯Œ²#O:©Ix$¤9³£JŸƒ,Ò%å%‘ì:RÈŠ"ò¢i)Cl¡ÿ‘¡l´£VÑ)PÁB£  uêS¡Õ¹à­.ˆ‘Ë­pƒUÀXUª]õêW£I&F«š)+XÑšVµ®•­mÅËŽzô#Õ]Õ/SªË¢d¼2Ä®wõÌ\íV'Gæ¬n5ìa¹CQ»0L/ÃUpþ‚ÕX)K«ºz c§c˜Â"–³Í E5´W)­fSØÚ9J ªm¨“@MÈPÊ¢“Œ(?ØO[‚à >?y-9bû1¾¶ˆµ¢%¤lEÄ1"¥ö&2“ZCØÄ³Ù‡Zk­O[2˜yÖ6õ+IWãÝâ4Ö.›mË^ÏÑWµRt“¢]ý’÷%ìE¤{fJj¼)’ÿ6¯z•ïŒÅRÕ3Àú•õ²äß)8¾Ù€¨€;Ýz !0F…Ó=fp¬ãÆ•Ì]^-/“¬xi•—`ÁeÅz1q¨ÌëLjV+ŠüR¦†6¸1„vjâOŒ92*QZë–LŽxÂwYTB›iLäŠ%Ç!Ø6n‚ÅŽà’!F.Z5‡T‘ DgT£«[™üÖX°ðM˜¶µÕF'Î>anNe+£«y$ Ù†äV0ÂôiȆDƒ2í*µÿ Ȭ›5Óú„Αn´‰„ÄæŸ­¸¤…ªbϸKŠš™£ôÎÈdc9YHuËÛPû¶$3SÿÕ_##ªÉ&Llp[lj Ô$W.ÖÀ ó ¾ÇÖ +´¿åp³`å+«Ï zÃô•pzÕÀå!ØzaR…‚C†,¸½ ÞŠ‡A,ºúbÛ*ÕŽ6‡£7_¹]û¾v^ð~ù¥r‡ø©3>ã?gu)Й뮷!ýÈf'›ŒùÔà98H`3%|Þ U°øüyÍ]áSŒ© ù3Á:Yð~dËñp&Äâð܆ºçq´r™‹Î~ó©¼èã.Š2nï ^Ä–m|f=Õy¨qœoŒyˆÒÚq¦¶‰60Ÿ ÌO÷ù2ÉxôwIùÓ¾4ö)6Œe)S÷y|yÔTÿ$©ÿ§)GšðF­àDº-cXȨöC¦Q‘Db¤ÕhòH•Ž;ƒ9á†%ã6õDKˆ‘`¥yÛ¬p8£Í\11\±|îß%AžH¬ìOOqLè4Ùƒ35j,6?¦>Ô˜OÖŸ\yëZÄw™*ŠV{Ý7†'U:-~Žk«â&S?úß ží™VüÉî<4‹®Æñ© ç–Ì;.À䟰,KM5Ѷý†,|±ïç¿5¾û» Œ ‚Vÿû£ÒàtFŒýYÁÊ0ùT«ÝgGƒ×N-€<”*&<µzJïÌ¢%NjñØ¢ñüÏ—X)Vïë"PHÚÎíÂþ<ðêöroUÿDp0JQ0ÕýÜ/GXp0^P¥c°j/2kÄ:"IêB½ì‚å‚IÎf(.nPð£Ý¢€îB ÇkñB-ZŒ®–ƒ9 «Å0ËQ¦°h ½ð ýâαSŠÞí4ÝrŒO"Ã. ƶBÞÄ܇ßðíR lz&ì€Ü-Ýë&TáyVàÌjO»ÿ•°â²Ré$&± GO¯ì&¸Õpæ ‚¨ÌÌ,y¨‰"ÚTéãZ¨a¸… ¸Ì\¢.ÔzÎQ´.…¤ß¶¨Ê \Œ§êŠËj¬\šàŠ@· >jë¶ÄpDè0A¤A .\â kæ@¶Ë;-#7-¹t¦ñ*nD#²º˜€ ¢fwïé‡ÔŽÎmr!1DÍ%E–ÜÐq¼¤–d!ˆÍmÞѱJÍõÂÌphOó‡Zlò€ ç×’’"@àœ>ÁB,ÃÖ !ÌTÚâOÄblˆPöMtJ¥ÃŒ*År×|Qt",z¼$Ãd‘H*Lý$è&ò©…ÿŽ¢¦€uî².¥ƒäøcøŠO  ,ƒÚ‡ $N"²¨àè)"n×"Læ"èuDî—Ï2í2û ˆ Ò@vèý ˆë¼ìôjL^«-ÐE]ØçD)é¬FÄ14ë1 §L›lÓÊþÑ`Ì1T¼¬…¢CË<Б‘ÓbЍ@â¤ñˆ’rb'Váþò»>À'Ô®(ŽèNÍ>j½âsÊ$vŠïÐòíN¥9GËÐSná3PaÖvÏ(Oâ'khŸ;oô†oB>ˆÔ¦È?Ii¦DÍ×$ïqzé„è/™"AÁPªNðA…Ìî Ú‡ æ&äì’úÔ‘!ª!ògÿÎi|nd‡3—æ13.2ñqz(äâç/AH./´5Þé DHB%*u”JÆ3‘, GòfŽÌ“ašjüö(&Âû‰ìZ£ñI<)Ï¢N¢”þ¨/M¯$"";© hÔ"“<㦢·zôLÑT7¸*MÙ´MÝôMá4NåtNéô¼þ 0.q0t0ÛB6S.ª±NuP­ð+ÑVÜBÛ" Ob oŽP!5RåbÓôŒÍÔÃÏMÐVË K4"mDQNÐ ñìc<’BÓ0ÓÒ’cBõ%õ­xÄGHpã›D5˵̨Q,÷"OÁèN³CÛ˜/Áôí¾-ÜÿÃ2S e¾âk˜ =×±<“g-9Eå“çÛSc5.@M3pQ#W®0 uùB(ëâÅ|*€Žó„ŽèŒNl¶.éĦ"¸?©L…t“Éù‘ë2öÆ•.@+i¶Ë¹ŠfîpÏ$Còdâpex¬V+#%¶´º‘bs«g>ÄTƒf%sÐ1#ýŽÑêìÒT‹e“Æ·ðd!;Ji™ ú@¯Wr-ÅüU˜öæ (5‘²@ç@ÓSÇœRÔŠÖQ¶-ØkϲxPæxŒÛnQÚ€q[W§utçÙÊ`!d‡Ýry¾ÀjQ¦¬QæÒY­Zá+ÄÿªMüò«½Ú‰kÝn^—ü¬â£o´˜ÔšX4”Ò‰àVË€§-8îEý3ÃO3]T€<4§%G§–\p4ùUˤ —‚NÇvóØž£†*b _]ˆbZÙdZœnÖî 7‰Œ‰\w´–ÅmìÆTÈ5­õôµYjÍm·ƒ^°nÖû7¥4Õ¨”p-‚‘¬µù>¥ú.&®T&²”))o=³zß³L¯ksçA7§&ˆg–i§ˆ'YI¾ ¨Ž™€–œOÕr‰@'Ø4ÔŽr‹„©,L³'½HØš ßÌ·7ÃßÒ'ý, 0É¡D ˆÀ¸ š¡&WC;,žÿHO*=ˆCeƒ@ bN€z²bñqã €2ILx,Ø*BüÎ/™ÏùÈÈ}˜2@mï:pKëÃI·“0, K‰Tc{k¤¢4" t;ý槦ˆÄJÓ¦p*Ši-zÅ.$N ÌÀs·,{ø‡ÕXý¨ª/Õ7"tå8MÅêÄ ŠGç8õxù¸ýø90„5wuÍzuX#iç*P¹‘Á^Ÿ0Ôu†u]Ýu9“ÏtS±$5r ¥†iº‘´îðQ:·¤KQ–´@RU5ù•4o %ÀäDÃN"mAnw¥m{qz~'{ÉR[ –‰ k“L÷ó¨7Ó1ˆšÎˆŒ'‰à³E¥¬` ¦˜±ÙCï4†xis¾„˜v)({ùËSi—2›Õ¹ UôŒná0«ð N8žÌ!Ÿj±vá‹X¸ï&S€*Óƒ§¢×y 9ËŒ½4n°—ŠY bs;Õ±”°¸1%<<$3331>11>>===6C68F8°w’€"¨àI­(àÙ‹0ÆøTh4*$˜(ß"=8r‹'9±p޼²Èå5¡ÈIÉŠ®„ÅŠ¹™¶Vn¯8âÃI®LYå[Ô^¾b%pùÊC‚w+–Ü*¥HÀý† L¢„¥¯À)çw+ÐÈ+âJ¥™oý9$œ¹­X¦•¤™å–É¥$(—tÚ‰g“F"ùŠ’À1òJ*ô™ç+{*ú%¤k.ÿøJrÊhë­¸âT㮦RÔªHXЄ+‹µj ª¾ra(_˜x«(à„#®4rðUûŠo¯(°J´ÓŠ@ÃÛœ›æazÒv, €,—©‡R»ÆÆ«¼÷&—*ºàVËosô2jž´·²îIÙnk­¿(%ÜŠ½Éò¹,p:hXÀ® Ì0µŒ`zæ¸ÈåªòÊ,£ÄëË F—(P*àÅHbf¥ÒÁIŒ0‹l´A‹gKnºâˆm#M@òsÓ*EyÎ:'oÂóbz±`øªŠµ‰qݬ\aSÒØ;›—˜Âj£’Ò¶¥d¶Åaõüó²CŸÿD›¾` lžÓnŸt2¨´¬øâ2Âü²Ì)5ñD*â>r4ïžä3ÐÌ–zjª†±"@qÅ¡TAyâå*£kޝ¦Â™ø©ºÕw9Ï*=‰z+¢“•žf…¬^–Ÿ„y  ì.ØÄ /:éÅ¿r|¶x)ò–쮳Ŋêö¢¤;‹½G÷©æ{í¹*ù~|ªØŸJý+Ö£t–Œ×oÿ^ŽóºUW^1¢q"[rExЃàîTåë\@ À=§C ›ês› éüÁÃôŸÎÏ4 àÂ3ò˜ç?éYŒðbÕ†ðm(ay´7U­ƒ¯`ßÖˆø  Â!xÿbØš!‘ð>'¬@†Èǹ(1PDµ!hñjû¹Ÿ·•üí ,–ÑÇX”&XŠŒhL£O¼X#¢Àk&˜™瘓%ÑñŽxŒ €>úñ€ ¤  ȘåñˆL¤"U¶G|„¤#%™GFò’“Äd% ¹ÈNzò“ œQ#@Rò±”¨<¥*™Êä(¨HÄL2#Z®Dx„Z¸âh&xDCIÌb“(Üä*•ÉÊeš²™ÐT¦|èã ;Æ2.ÁæIêÄä &%‹)LÐ"æóœèLçUF™Êh>“™ð|§<¢ŠîÙ =„!{ÜÿŸÕ´†>ö!O~úɹÉQ›Êxx`ª—G:Übu).Yô¦8M'F;ÊSúô£vóšYTº”Ìl#T…Y¢PþDñ`¯ˆ·rJÕª~r§>ugOµšôM oÉY_àX•3²!õ.•ãb ö·ÑÍV«\ñˆU®Ú•£>Í!5'ßM¬öŒæ¾÷ K]É7åfÁ6g{a5ç\'KÙÅÕ¯wÍ(W]‰£ Ȳ°LOóR¥ >)ç…ˆ’! ¥×R—ðÒ+¬ø¥ w˜Ú"ÿVö¶¸mÙe5‹ÙÞf4·À ®p×¹GÞw«x®r—ÜÝ"÷¸ye®t§WçfÕ·=• Z©×íþ+”Þ-Ôß8Jèfö¹œT wo^§‚²½ Õ"y‹ÛÇëš»ù ‰6]ź„Gð1bNl¹½¸Â ¨âb¢Šáˆ%àß·É?Íd¿6áÐÔ¹¢ ^QKƒ1D˜?Ø'NVÑŸ—À7%0Ìç<†ïyøÓx%6vãJè[ßóÚ¹¹ÇAÚ1S2™ï$>bÀ%ãƒZ¨ˆe(ù ’Ô É<9!"0F>f‘e‚|àÿ‘äì!‘3c„Îk–‚@ØÀÀ9i^s›Íæ||"‡&ˆRßYLá\îrB-e*[Ë Ù2GÝC¿“Ò)rB\à{C®îñZç[íp‡2£¥÷ºG¤Èf·ûBH©u½û〷¸ß­õ¼þð÷:â¢øÅC¹ßövzÒýHKVÞÞ•ì»ãóçGoññþ¼á?/IËï›à§4%ß7ùÇÏóZó®÷{è7/‡Ø3þÖÃN=+ùNöÖÛžµÿ½ëƒ/|‚¿øÍäíFžYùI6aÇà¼< 䵨H-Ž|…¿û~ÿ¾ÿðÅ_|Õ7;÷»O?ß Ivl!ËZ¸‡=´Ð†-ÀA ´H1ˆ‘ZãlÇpmUfSpÜ~A~ ¨v{h{æ—vÁ|Ê´z•Çvì´P ñÜ7 ³}úg ýw ¶ì°flm` ³À}ø€ 86xn8H{6X‡~•„yA¸w qR0ö0ƒý7 ù7Æ€‚qЂï@ p0 ü'³Ð‚"˜ƒ è…È€;(z¸wºÇzGHdv„Çö0õ÷S0 q[0 Rp Äm@ ÷€màvH 1°€`Xcxˆ½–ˆ‡ÇˆŽÿçƒog†é‡†gsŠŽx‰{”‰zlj‡‰°‡n‘¤zB˜y–¨‰˜ˆŠåæ‰uÇŠ²W†æÇ|õ¦Š4⊶¸†9Ѝ7‰«W‰—ÒG}Ö—ا}†˜‹r°‹¹ˆÎ(vÑw¾èÌ'„Í'Œ¾'îòGö‡úÇþ€H€( ˜‡Ïh|í¨kÓÈuñ¨vÕØ²8‰¦È~Äè x]H‚&(Å€‚þ·‚-8 /ƒLXƒï(óØñ:è…õÈ÷Èzg„H¨„LH NHQ˜S˜Ux…Y¸f\¸Œï(‘‰,im/Éu¹|£˜I%çokÿˆŒn‡[ ‡t(vˆ‡zȇ~ˆ‚¸„¨’+Ù’Ž“Ñæ”í6“@ŒQÆožÇ”•XéZ)l]YnR)‰”X• ·•Xñ•fù…Ň–„‹×V“ùvŠi l9—‰|uma‹gH–Ûh—g ˜Ðˆ—‡¸—o ŒG'xQŒùP}‘ŒPø„Sk±}v™—f‰™ÀW˜ni׉ù¨˜Ý˜ñÇ“÷÷„ùP O(øp ö7€x€ë¨”X©™Li›M©ˆ†é™}™˜úØ‚#X‚ ¹š÷°‚L†0(ƒ4H›µ)˜#›0#¯×™q‘§‘I¸„Ü瑨‰Œp .ÿàgV'¹…"éœÏ A¼âžº¶›× —0‡“û¨“oŽ>I[Pù·‡}xZÀ‡G™”ìÙŒìÙð¹+ ÊN½E“øXJV™ I¡Ñ ¡¡Åeëv†¾Y‹Z¡!ZšŠºÉ¡Õæ|s¢#*¢-%ú1Êy(*Šš†ù¢%1£ÝÇ£Gá£0#Ÿz£Ú¨xŒé˜™a ’I™a™- ¤¿'¥Ñ©‰ xXZd£Yšàxšú·š­ùšè(› ¨£ª£ A¥"¡¦5šOGu=Gy½wpFøÕ÷Ä™!Ç ə̙ i¦hÊlÚžVúƒÖ¸J"ÿgŸ¸‘ÜÙ„ßy á™ãI’åyž)Y¨ùp¨8¥¨è¦‹ª¨EêuÚ†ù‡sÈŸþ™ê‡ˆƒXˆœÚ©µzžª ¡JªiE:n·:˜Á:¹‘»‰öè«¡Im¡8¬.鬶*ª(ªÉÊ«4‡¬Ðª ÅÊ‹Ã7­¥š¥—­?¬Ûz¡Þªt’Wt¥'süÖ~ÓטǨ¤ùÀ¤1™ ¥ÃZ®[§¯Ïz¬%ŒÇïGšßX^*`êšç›êX¦Ðʯæ±$j‹ÍÊ—ßpuZ w:œ9¯Æ‰œ.¸œ ©ž…*±Òf²²ÿŠQsŽz„ÛÙ‘™¤•z©%ÿižž](®*땶׳#Q±‡ÉÊl¸“ú¹ªÑŸHéªD) :«$Ë©g:¢@Ë•¹®·¢õ&´âºWÛHak¨í˜®èzsÔ¬‰÷G¥÷µºhµÒê¶oç—w~r»£!:¶ ¡·çÖet;o^{·æJ¡|«–rë·:p‚K…Ë Ýz·ˆ+¡·¸Œ› k—Û–¡–¸§ñÚ±ôšöºøJ¹™¹2úy¨+m‘‹£þF°¦)Ž»§aº°éˆ€kºî˜«­‹¥³ ±‘ ʧ}ú§"Ûœº‹¹¼«º¦ri+pø³Ý9³×W³ä 8«…›º¼ˆ8—½ÿë»ËK·ýž¨Ê“û¹´­úªP+«HI«Þû½™é¼ñ—W¿.Ú’½¾Q™¸ãÆ¢ø»£ü««ËÅ,ÝÒ:ý”< U«ÑpüÓ‹h¸:ØŒJ½Ô6 “LMÔmÔ;m|ûÔO½¯VÝÔ,Õ5MÕZý”YýÕ`ÖbM¹.ÍÕZ{L-aÍÖd Õ»vÖhÖKíÖWvoÓs½ˆwÔV-m­×{ Õk Ø…-؃½‰í×J-Ô}­Ø‹?yÝn‘m×=Ó“-¶•mÙ™ Ùe¿r½Ù!ñÖŸ±§Øp­—¤MØYmØ­Ú[ÝÚ»–بÛ<Ú´© “v·ÚEýˆZ;Üá Å€GÊÄܪ§Ü̽wÌ­ÜhªÛ•xËò×ÝØ½ÝÚÝÝÜÿÝ`ÝßíÝä=Þà-ÞåÞæ-xŒÞëýÞäŠ0ô]ßö}ßøßú½ßüÝßõ­zþà>àŽÇ~àžàÒµÿªþàá>á òpážá¾ánáþá Žáâ$®áx\â(.âð6ä@åàâ0þâ2ã4>ã6^ã8~ã:žã0à-¾ã@ÎãA>äBÎãä`à?^äD¾äJÞä3~äi ®×6áT^å^á)žâ#žå!¾å\þá'þå]o.^f~æhžæj¾ælÞænþæP^Ieþæt^çv~çfŽÇsŽç|Þçw¾àÈV>è>åê°ˆÿžèŠ¾èŒ®èêPáØ-æ`é’¾á@镞áxŒé™~áò½ç~ꢎæªê£~ê~®ç¨¾ê¡èåGè°þàr-è‡Þè¶~ëë€å` tân é»Þë¿ì(>ìÄ^ì¾ì®é{ÇìÇþìоvãðâg@€ƒ`ç€çßÎêyîH׎í@Üîíà.îãÎe×NÙ¾íÝ^çá®îì^®.|´Žëüîè³^ëýðº.T`Ö uâ°ìð â ¿ìoðò€ð _íŒwá _ñ_â¯âk×âCPâPâ0ïtx®òì^êf>ò%òvÿÎò3ïzN0oò(ÿæ4_ç=Ïêùþ{ûðýþïD/ðˇÝË0Öá· 0ÏpáðT½àð3àéò°ôMáOõS/UõY¿õ]ïéJÏô>öROõVõZÏõží`ÿöP÷f?÷io÷wò§0%æ—€ Þ`æ`CÀ:`æD@×®  ’Oùâàå „Ÿæ‡Ÿø‹_ÿø‘?ù•_—Ÿù›ßùŸú±¿êz>ø…橯øŒïøû–ùš_œïù û-åô·nôÐëº~ / á΀Πtÿ@žÎ ò $ÀÞšÖýÚÏýÞÏÞà/þä¿ìçŸáÙ¿ýÝÿýá?þ[¾éñáó¯þöïþ@ÞÀòD˜0a€q—X+±7ÝÊ 1À¦r¥R @b9q"Ïò†@[9rßÊ…)QæLš5 æ YÎŽ˜+^Ìø’£G"#–$w2åÊ–/Ö„U£Bª ö¤h£Æ¡a5‰R%K—_¥ž¥y“êZ¶mݾ…Wî\º¨[—Wï^¾}ûª­‹ð®_Â…õ¬P ¼[/¼Ee ´'3·ØrãÇŒ%Ë£l4fÍ¢9?Ž<¹ræË¬ "®ÊØ1ÿêÏ¡]C#Ý6[†;{ZñÛ€rãrŠCàTb¡,.,>3'ZêSqŽÓÉbfôrËO>½sçЃË_ýìî…ã|ËäîÝ8DäÊ#’Î]bzõRÙ'0@„k0Ãäë?¹ D°Aë y–Y Ô@« ‚‡×,£ÚT“GC=,-3 )ììÂÐL,ÅÇ(Ä1,qÃ?,hArN1@øÎ“O€£ G€‘:É@HDŠÐ/"%ûC릜€R¢øˆÇH’œ²&„ò<‰Â¤2* rHá¸ôR0—l²œ'£,Í4¡z@?ÿ´@¼tpÀÁÿâ0BÿT3¬€¦D:š!Àyè A´å@Ä‚$¤Â á™´ÒK3umÓNg$èÑHI¥ÔRL5õpÕÝŠPžPG-UVTSÕÓ[ ‡œŠRœA¶1@«""îË<±+G“g5¢›±HÊ3Ë=m²‹ÚŒHvÙf/z¶œhåœ6"kYWÛ•¸ÀÛo¥{‹!c‘%É\g¡=²Ýj¯w[!ë½—Ï@f¸áµ\´°>óUG et ^tâ ‚ "Æ&AKåYmS‘IÐ " Æ$Hyå–=|ù5E&Ùä[PVy åé9³|9”™f›oÁ¹h–ÿODZ˜‡Í¢qΠ щ‚pস8 (‡ëHÚ¡m"Œ¨¹MI›‚µFÏ®‘Ä9¯Á¶Dl²ÍXm¶Ý†[¹1 Ûp¼÷˺8®ÿ{ì²»,ÜîÃwx;î¹ë¾ûqë&½t9Ž˜QïRç€JZÐø.t ÈëœÖ 2ˆ% V@ç°F%ì¬xãOñxå‹Ïuyçu[úy鱯•Úž$zJtí!¿î¬ì·×žFëkú|Ñ=6=}õà üŽdRÏ }Þð@ÙXÙk¿€:à uPàPG&!¼?%ozÈ#Þ—ç@ BW¬ ñ<Æÿóu0o8¹žEø’ÈЄ$\_ Uø°Š!À{™ýìÇÂu à£È@ÿþ·Žsp0±ð@^*ú‰‚ 5¨DäE‰$Ö Gh%jIуⳢg¸B.±b ó‚„|àBü@ Ôp»t AˆÂS1œcA¨„ìh·ŽpPy‰E ð2Ä"žƒOä"U¤È×8‘‘ ag¥HJok²¤ùäØEN&f0€À  ùå%xëPÃ$ÂÁ€àEâëÅcg¨/Ú1`‚G‰WÆ0B$â:D±É‡=Ð ÏkžóމLc"3_‡<Þ2·Á*Jÿ$ߺæã!XÄ?áªI°ù¸q! à—šòU͈d3MîLXZ°ŸovÒž¤;Tƒ—I´ ˆ‚'Y¼2@ÿˆiüR¬«„Ž€† ¤c€ä:¹Ž,T1¬òŽØ™Š¤ÎÄÕôJÚĸp0ßzéãHÑ ”­žTL÷ Ž£L¨g[\ªÓÇ€¤`ILvÏ2 ˆ@؇ÕÑQŸyù#,r¨Ñ$xB–5%(’¢nP©àðB|€ Ú…F ‚e€!/˜Á×ÚÖ·Ê#®$Á2Üp×¼Nxlu+\åJW»âu‚ÿÑCl_ÿØÁ:ö±-‡8†‚(! `Ôe†\∈7(‘Mˆ)XhE+Ô–Cµ£-m98±‚ßÑ&°©:»§YÎzV¶¸5mmo[ŽÖ¾6¶¡%‰rW;Ò^B·¼]oib‰ ,U¨™Ýlg? ]ê–ö´©]-s] ÛãҽȽnogBD¬ $Aeêú`|à#ÇX˪ں«–2BÈK’°Êà©á•çH‡ÆPÖ³Ò/im A"…4;<ƒÌG˜LœGH9yr‘ß»²(™É¶ur ü )SÙÊ2!‡,A-Ž7>2I„fÛ™ÌIÉ’›Ìg6O¹Êóõ 2!þÔ…ù]Þ‘{DÀ´€ÃAÜñ8 R8È1\ ´Zp Øp?ÜÙqŸêB„„$hô+PCÖŠ ôz¶0a<º‚Ø/A.ä³i¤%@6–D½ÄØÈVög˜}Y¡:Ú ¶¶©-Òc0&¾ !wçÌÝXm”¦ówS Ý–@XÀ °³ ØÆzÿÀyn3\bÝI¥½#o˜É> /¼ïï}÷›&EX‚À«Gpu+§Ýg¸¼Gr{Oßúæw5 àN`€Ñ•|4¤Ó7€tTª÷ˆÀAÚ@ œ Ä"¸G>6] -¬^T„×1‰(¨.@¨ ¶üÔÚ ˆ(V˜öŽYEu«c]ÜZŸ‘„¸.«gÄJKéØËþõ³£½@"&Š$E¤îÂ937"Ðp™xÉÝÙF¿ýn‰tE¿©Q¾ #`àî˜; " ÔFÉï<äuRøš,¡UÊ9â>÷»ßýïÙ»å%‚yÁk‰óˆ.‡S;˼t’î¯ Š‘ÿ-Ø ºZˆ|úîˆj1ŽNÃE©¡Œ@8% i©1ŠÞyñDVбF D¨šp¼b[™4࿊u\~“Êãû ÿø×/,–vûüðOŽEÄþúgŇzAÓ+p‰CX­n`¦@o1@;A-¿ë@ªrÐ.™8Ë oHÌ¿+8À\@–h@‰x@Dˆ@‰˜À ¼Àr`‚"€9î*rø?Ñó@–ÁDÀˆ+At@—PÁl¶D+¸Ã]ÈCØÃ¤é¿búÃ:DDAdDB䶬Ù Ð 0ÐÉ)‰@ 3H¯LœÇA ‚2°·h‰NH  €2˜‰3h€@í.†ð (Ð!W´Q$EwYS@EUdÅ`” X”EZ”ˆSÀE€€×Ã\!‡LÜÄN4a ÅQ,ÅcLÆUlÅfŒÅYœ ð†ˆØ&ŒÃ'|‹˜4|ˆY8ˆv8ˆX>{`}̇v°‡£Ã~d¾¨“¾ª’Pºu$@ƒX`*”ÿ‚&&j»J&E¢Fj$Ì*ŸL"Ð!‘Üñ I“Iá‚GÒqªˆ€-X¾Zp„€ƒ¨É|5ø€60Jž;H„4êúØÑ QuÈ€CwÄ ,1ÌÈé¡‘‹T"HRÉI§­¼¤òÊ{Ê–œ‹Þk 2DH ó¤Ì‹QÀ¦tÊʪ”ºd&©ìH¼,·°K½á˾¬ž¿„)²L¡Ÿµ³$Ê„|:uhÈH¨¦„Cµ¢J©» ÊKªôÁL*âÌÎËÏ¬Ž±$L‚º`£0X‹5dÃuhJ´”˜îÃKË´Ëiª1ÍÄ,ÑTJÒÍÝ\§ÿÞÍÒN1LÌÔL‡ê[Œq͸4Œ¹¤ÍçÔË”ŠÎ·N’äÅêÔ¸ÄÎìÎîÄ ØDÍéKÌ¢4"¬D¢Ú¬6Üì¶éüÍí\ª&tO,rOïòÎá]""}¢ÌtÕ(:ÔÅN&ÝP'­U/ RZò„DÙ ¯2¥‹ ¼H(%½øµ6 †¢[ˆfÕS rVæ¹ÐÖÐNGÛ2AT}UPmÝÖ%J “G WYB‡‹J×t0€t]Xÿƒ°…- †-PhV‚è;x d˜yÙmëØ Ù‘M¢’ž‘EY·ÐPYéaÙ¥žï‚ÕôÐö<Ô•¹[­ÈÅT8ÕIàU;’S‡?Òteȼ… 0)Øt£Ëƒü˜à;€†/ƒ.x†^˜;˜‚x€†)pƒè7Û] 0xÐŒŽíÚ¯…^:гE[µe[;p[¸•[ŽåZ¯í‚»•‚:(Û³MÛµmÛ·ÛÌÜÚº[±%[¾mÜ¿ ÜÈP4ÕlØXžŸ}JZª5ð›X Oø€  Ôú1W@¥"X ©ÅØŸÊÿvȇ›D!žŽ¥ƒ.˜/Ø…]0™;è:è¤é…­õ«ååÍØ;(ÞãM^êu^è•^啇;¨^‚ØÚìE^ðeÞîƒèõXô­^'*_ã=ßåmÞç]ßï¥^jºÙÅQÅð×ù]Ò Ó•KÔ]‡AÔF Ò­ƒ[ ˆ]¸yÀ]è^7Ø…’Ý< à\˜[;xàæ`<Èõí a&_†àØ` Þ…9°ßnß6RyèØvá ®à Îàî`­ìM~—î™O"îß…ÀÙk`H#àæ$Ø´”My˜\)/X†®àgxÛH†g˜:` Ö…æéØÿg ‚/ø‚,æá]àb0ðb0c.ã~+^ã1Þâ.þâ0Öã2†_®Åã,a]xã8öc:Ößj]âêÄÖ€mâüzâØ¬ªnµÈ鉇9ø‚.Séqƒ¯©y%ˆ9åGSéÉäM&·W­ÖÿÏÐåL"æ×ŽäP­dýR]U*Q¶J Yž bÍ’Lcβbff™Pfü Ôzær˜f$¦ÏZî¢I¶(Žº]æH`®Ë{]O1½³=™f+òÌBUõ8gtÆf{Òf¿ÈÏ ]QޤMpF©qÎWC]dÞYÞpåœ}gNŠg§+]9=€#)æÏ^.Rq~Yÿr~˜FîÍtþç‰æ_Zh¡ºe)¥¥X2W†6¢{†VŒÄÐ"æ~¶VGÖYZÝè*h;­ªƒJ¨†ŒÐKvèhÅgÛÄW_†åW6â‹® Š®è—æ¢˜Ṽ–S±Òµnžb^†jŸ~˜¾D~†dvéªh&6jÓìh7]L“Ó°inèG¢êN–êSÞçuÝ3íg·~뮆é¯^ÔÅä5È: OœŽê"MkSþS‰ÖêýUg€Îhº®kKÚ¶k(>k{.é¬lÕµ.çVNì4jÅv¤ÞÒ~ìŸ=Uýëh=iÓÎMFÎl›=lÎîQ]ææ¾.ÏÉ>Oˆ.&@jXÝêÿš°f‘´ÑÍví…ñlò@îäVîåFî sîxnæ–îäž0ènîëžîì®îíÆîꦑìÖnëŽnä*ð6ïé.€Hoä^ïôvoõ†ï÷–ïø.€ú¶ïû^ïóÖïä~oøŽïÿžïpúVïúÞïïþöogðoðünÓî-]×o†qP ·ÚÒñpáq 7Y¾ÿð…qq„ ñüBñþŠðšðw\ñpÖèxÅñÏq ?oòFIŸŒ!Gržñ$gò"wr˜nò(—ò)§òyòõ1ò*×ò-çráÔqÓìò0ó1èÿ/‡r2Gó4Ws5s,_ó7‡ó8—ä+WŸ,—ó;Çó7oó:Ïó>÷ó?::t@'ôB7ô=tCWôE—sD—pF‡ôH×óDÇ'I·ôKïrG7QLçôNorM¯tOõQÏqJïlRGõTiP?uUwõWçYVo;‡õZ·u#2õY¿õ]çõDÍu(íõ`ö{’u`öcGv>ô3OöfovòÆõewsg§vaWîúuá®öm¿uæÆviWvnwTŸîoßtf÷tÇtð6÷PŸvu‡wHòh?÷w÷{t ×÷}ç÷~×w|ø?|8ïOqƒ—ñƒwñ…Wø†Oøÿ‡Gøˆgø€§ø<‡vœÈîŠ×ø‡i‚wñrçøù@±’ªXî‘Gù”÷$O‚On•‡ù˜*¿ø… u™ÇùZ¯yÅÈùžïùÏ—›÷ù¡'õ—_'ú¤O÷ä~q¸8:£Wú¨wv¦o ¤—ú«×y _˜§z¬÷ú-§z ïú¯'{!_î¦7Ñ“/ûµ‡óìFûÒðŒgû¹Gr·Ÿø– q¹§{ºö÷¾÷û¿üÀüÁGî%¯Õ¼'üÄWüÅgüÆ7o£fn‰wxɇø‰§üË·üÌŸ|ͯüÍ÷üÎ}Ìÿ|„çlÑ}Î7ýÔGýÕ}Ö?ýÖ‡ý×—}Ä¿öXû‡ßûÿÜß踇úúTî·×ýà§ëå«ýD½}áGþ!çz«'èÂOþçŸòåú߇þê×òåÇr­·þíÏtæ7"íçþðßr®‡Ràÿó—ò±{ôgÿ'Cïoÿøÿô…ù·ÿ)§ÿû×ÿ*ߤõßÿÿˆ|,hð „ °¡Ã‡'R¬hñ"ÆŒ7rìèñ#È"G’,iò$ÊŒ+®Léò%̘2gÒ¬ió&Μ.[êìéó'РB‡-jô(Ò¤J—2mêô)Ô¨R§R­jõ*Ö¬Z·ríêõ+ذbÇ’-kö,Ú´jײmëö-ܸrçÒ­k÷.Þ¼z÷òíë÷/àÀ‚ÿ®ùê0âÄŠ3nìø1äÈ’'S®lù2æÌš7sîìù3èЯ cmú4êÔªW³níúujÒWaÓ®mû6îܺwC–m•7ðà‡/nÛwÕW(h~Z€d¬nC©\]1tͬ*œÞ~8{# ^ewìýWÆ×³7œ*ùé©D~ôwyÉù¿o>/ú|*‡q Êa>vÞ"×µ· ƒ˜½7•r« ¦JÓ9ñÄa>pPÁê …¤Ò„x­|ç„@ò }¯@Âx"Öʇ…gtä¡hA/ƨØ#0Z@à9 °ãŠPЯhÈ¡z¯¢*O,Ù$b$bAÿ=ÂÈA€Ovx#tðw.~à‘=4’Ø”C¾Rätež™&}OÀ†-úæ+v¢yX+ 4x(¢‘=(y T @“‹ôàHz„æÄ",:òÊ"Ø„"‡°)+ ¸+˜H!v&¾ÂÊÓ)Ò‹+¢2^ª«¦y¬¾âÊ*±¾2k­¯Ü**b®bú꾪×bb¼䫪¾Âª²™Âjâ ¹^Ë](ÖëaÀ ;붯 ÊâaøJŸ·¦»î+øš(¾ù.U„Šõ À½ŠtÐM¸Òâ±:¬€©Ð׈LvPA”¿BÛˆ„ pp¹Ì1WŒqb$¿ÒÊÿÆ-ºâ1»‡ Lpsf­bÿê1ÄSürÁ2ÆjÎ!WÐJÛ8óÑ›Ì*yí¾û±ÎŒÌÌô+*æ{u¢ûB_b®X €¨X #÷e—Jd.|p”">LkcÏŒrÓ7Ï ÷b&'}2t٭̰Ødß§7Éù3ÞÆŽèà÷}{7vy#í3ß8Ît‹î"ÛHâ–ÿ¼+¨`=:ƒ>5t:ê©«¾:ëªÛÅ5bM<‘ ÅRÂÍCÌ-¢­¶¨Œ¼’ŠÇð ` Œ‡U@à+ÁN—­*{ «è¿'V*ò­°"Àò´þzÜ3õ–n¹Ý×ïýa¸'oê°ÜA_`ÝÓ+Pýÿõٯ¾"x:,+DŸÊýÔ“?ÄÀU¤;`qL‡` q`>È@Jðdàë–Ó0b<¯HDÆ\Ñôàf+PÚ&ª'lhE~‚Q–˜'dÊFÀ+Nͱ̿(†@ ‘œÂS9ï0hê€IhÂÃ<ˆ:"Íì¦&.-l„%t\#:àƒ&¤©‡ ƒœœ8'.å(‹[LSv4×2/ÎI‹\< *°„À9 Çti ï¨G ò‘!¯3Í~$à Ò‘ŽMW!©;ö1‚{ôc#óØH¹œŒ”i„|IºPi²“µa$$CéÈH>r”¢¤¤'S©ÊU”¦ÿ|¥$EËW¢’•¶¼%.ÛãÊYò²”³D%šn˜ˆÉ(H2Å\Œ"ñˆëhÐ9- dc*ðˆ.eš‘ÉŽ5#)&Õ03Ç$n\á„ T tQŒr” 0GˆLL3 5™l^† 8@çŠ&4ç:èÄáµÒ™IDíÒ—%å$ãòŠªÇœ¼feè+Ô{Q,ÞcP(£1Æ€ŠÁèdt7Wt  &rèä,COKŠÆ£]CŒÆ„Š€tâ¡bìU3ÄXÔ25ÍŒ|¥&Ä,yíºiÆ,€T}õ¤—•åA颊p”N'ò”Ĥ mhLqªÒ•~U³9ÁWÍŠbÿ‹ÄÔP qà«hD+6ÕW4"d¿bkQŸ‰"«‰uŠ€jYZ»ê Wàõ0M!b‘ˆD\gi¢Ó#Ö¤ÂJ[ý“^ø#8 IV%ˆDD"˜ˆ<`GO{­ñ&ŽcL°ÊeI5"Fµýúœ…0¤£ÕH¯nÂ,aÅSÔȸödðhl-6UÙ2U'N…êSai—,*FyÂ[l50f‹\b…–m#GA °–zŲ13Ž.Æ>14zËŰQ½Š}§–­øŠµcE: uà~‡Bu`%+¼Aaf-Â/ ŒïNuÁ¢ÚT§>ÅÉÊVÀïTÅtêÿšHpgbä-§šx:Pr*à®ñwÈ©IUJSõ}–¨Ä…_þFÉžzT z€ ýNã¤hŠ¿ÓBö” Mªt£û˸ˆ¸dsËNÁȳ&®e×(cΚ%þ k`=»ŸÁÞÄp@rE šÕÌ›¡'@ šÈZ–-ÇY=¨XØ6…û¤Íj Éè6§·ϵ{Èò¼7Ê)adb™¨ (†ºˆ§ó"OïWMHémg°17¬il›ÓBƃãYÎÆl*à»GPuÔß¡©³öd)Cù¹sQk÷F7Ú­øÕ>?:Õ[è7AËÕYÙ|—tâFD긢(n+ÞQ.ŠE*Ä.ú¢/þ"MT-vDëÈ¢ú‘ëãC#Dˆb/šEC@0jD.F#2£G#2*âÅâ2jã3ZãEp£9ž#(#:®ã1RãBp#Cÿc3²#ë=¢8r<Îb±Õc>6B#F@ADA ÄAæCB:ê,¤AF$BJ¤BR¤;VDB>¤ERDN¤GVd/NãF~dBþ£5Þ#JÞ£I:dëÄ#>E4ŠäSäL¼ M®äN˜c,RO^IJä>â$A¤CÈdE6EÞ¤R(eLåE %L0äc7ªcKBå;:#L#T0eSj%V’-zb)žcX®"X…W&ÅZ¾DZžå+&#*8¶ä1 À;äÃØAC-hD-Ã`´eRÈAU€aêDbÂå0Êå”ù"8Ö%ê°ÃhA>hÁ=؃´ÿÁÀÐÂ@1ä-Ã1°Ã=´°AfNg ¦`fa.æSÈmÚn2¦MŒ%?BãéPPCCÐB1`fæ,ÌÂ_†¦-æ1ذC>ÌÄAÃ,fjæSJÅn6ExâÄxòfczcÍee:.;ÄA>HÁØÃv’æ,€æ@ÃsÆu¾-ÀÁ,Œ¦@Ìu&ç`rEnEyÞÄš§Lø¦c²D8 D{æÃ1€=h&g¾ÃÌÂ@ÜÃÌ‚1ÀAÃ=Àf¸ƒ‡ÒB Èf`,¨U$¨PÈhMÐ(ƒ6僦"ZFèÂ(VØhNéLøèÂÿ„ƒ¢ç)©'ò&‘fE“é“„©[æh’Zå’že”þ薢ĔÆÄ—RéNXiîèU²^ê%_„_¦‹òE—r©nÆiJЩ˜šÄ‘V%–Ö#eZ&fbhg~fhŽfižfj®fk¾flú…Ê)˜6ªI„éŽ)’Î¥™#q'r*'sD1ƒ}J>Cgª&kº¦=Àfˆ–ë‹úhÎŽDÕê¬XÊåIÒ£=èrm>´¬@HÁ=Hg;Tçufç|vç\ÐlXèÕŠ„Ûb-9VêÖ®£à*}‚í1Pk>¸€@k «€ú*¹²ÅܾmbÆmH0.ÝŠÏãê0äêm†n(Alÿg‚或è=h‰†ë¸¶­o8.¿&îãªD ¢lJv¬½ÊFx¢nº¦®QdìOÞîQæ®QJ¦<î‹ìzE‚š.F/íÆ¥ÆÆª9úna)ñÎnñÖîî,7º.šæ%14â1€f›&„Ì’Åïâ«Í~oó>¯»F¯ô¶N×ÂAr¢¦‹R&µjA 䢲A×~¦1Àæà >í¢ŽÅ÷žkÈJéÿ’o9ÊëpC¨¨Š@-L'A´'C-HÚ–j›j'tÎç,lÁ©z¯` ìø Äpç¡ÏŽdî.dµÎ;n|¾CD{¶CàCüþ'›jï_Ö‚1(-lA±Bÿ1#f³cUÜ,ñ†0 —o™¦DQ®Ž¼C;°Á,h€t¶ÁsJ¨{ Dü~n¤è‰Î‚çƒ-ÃþŠk‹.Ä ãn§0 ¿1-¦Çq×ëOÄí¾2q;13le)aŒði¿^í÷1F©‘ºdp¾G"òœVì«ò±"ûñy òã„$Ci³ï%3#Wi 7déºàRXò@0«*‡òGÐâ°¬iB°ƒ ´ä¥D´Ã°*D÷&1*#E+Ä—³+o£»q2ë®9β=À*-Ûæ;P+-ØÂüÞÃçú§ü6­Ñæ¸8CÓ*jÔs0…1‘&ÿò1×mƒrchÀ;ÜCÄ ôçÙʰ °à©ª­1 D@wª;´Á{´©Ž3:§óŒº31Ã-D¿ó"×;0?³f^âCŒæl·Á=Üv?ÿ35ß2¼>ÄÁ¸&xâhÀºÄxK„·fC¶ã#FîFǰ„jw>°Á1¼,tO8u?g†Gw6¿tJ6h㬂#„ðžø]3xƒ/#F³ƒÿ´Ëðn[¸-üåP÷mGgx à;Ôø1ÿж8Ó„T§°USõT’ø-šx*S2y;ôùJv_¼j½7H8‚Ï­–¯xNRù^ ë•·G¨8Їv”[ô—?r•ÿ+N—xGtù²þ¯œ¿3‹_ê_(ë˜x™›¹AØu‡ò·xž#ó›7ùÁúùŸûD ÷ñ 7¸@`ôCÄTúaoÄö»î9œ“6Sȹ¢?ï£d ´^¾Csª‹CÇC lpfB3KÁ, z¨ï1HL-°f7'ªgÒB6ƒ­w:1™7ãĆz]«x£“ï¨änÏs=³wRç@°íÓÞ'‡Ã,Ø3>C¶®·O“;COAgÿg›*4©ñ.ûf{ú§ ³³cí¨££`F¸vC¶ºú}ºg˺014'Áÿeœ­ºƒ4…‚&Ãg:L§#1C.“oìYßû*c<Æ_ì´ŸcŒ7ðŒo{¥³íz¯ßwŒ¼„¦<Â+¼üµŠn(R»CÄ‘Ç+*î+§#úœ;ÅÆ‹ðÏ;kÇåB(-¯r÷T˜b¿î¼Å÷¼x}Ƨ¹²OùêP„;\zW²sÖzÓ£xÐGûYK½¡söK®E#*,Ó—¢×ߦÁŠ=׋eX›ýÄïlÅ«}Dß&Ô?Ø =W½[´ÄX×=³×5ÞEÞ3æ´3[“ÅJÐlÚ¾¾BýáÓ»Ûwÿ=jÏk[4~_ þfïýÚ{ïä eâ+¾)g¾SöÛë}Þ‡þÁV>ä›wL‡…CÌíã'n޾“«…çó&î»d¤#»AÌ6jRs/D¦‹râÖþŸ'fï7.ëgùó3có7„©3w®;wûúí,Ø‚_JsÛúÜCö6·¯žmŠ Ä®o°þÒzØNmÿ:µfóéÎ=¥Êû³'7W·í“FiµÓ3@܃3Ë-8ìò%ÌÇNÂcpŽÑ²§]>c³ò›Q A|÷¶ØÛ¢ðb>ZÇîʼnã_Iˆù¦ä›Å&Nc qæÔ¹3ŸŸ åà9”hÑ|Bê JôgN¤I¡F• TÿÎT«W±f5Zu¨O®ZÁ†;–lÙ©OÍò|˜0ä,„RÕ0!ijlEÌ÷Π±8OÒ:™Oн|p´TtX+Ÿ­‹oíc§bÌYÄ¢Íê•ëÒ´<5'õʳ©ÎПÖF½š5é¯89·–=›ví¡§WÛÍ·å];6³BÌmcK!;mØ4ŒH NbƤÀ™r/"»ß"îí•‚ÓãZƬÏa¯{>Ê0óÝÓÒ¦;ÜR9~ŸvþÎØ9U›¶õõ~ÿdó‹ï¿ ,0ªú ,ª¼¡ÚIŒ,·Š¯*Ç¢0! ©2ÊÂÿòKÐè¾’°¿I,‘µ MôEûD\1« 1ìÿ´/‘Æ©ÑFyì±(}D HØt¬ÍÂ"¤qÇ •D’É'¡Ê ÁÒq´Ù(´rIýJ”qÊ.­üRL¥óÅ*µÜòAÑ´L’D5Í$0M8ã¬K;DÓIÙ œª2¤OÛætÐC©DôD=ÔM¦üLÊQ4ý°PC+Íô,M?{/€OßÃ0³KYX3žøf±"ýqçi•YèA¾wHEÞõÛ?iæ9M60é¡©¾¸êÌ>­c…ñýa¨ƒÖêR »–ºì«Ñ6«èW]ÞúÀƒoûšlmùÛgº‰†;í½eÅRÝ¿ÿ®ðÁ /ÜðÃO\ñÅoÜñÇ!7|æÈ)¯üîÊ1·¼µµPÇóÏA]ôÑÍÜôÓQO]õÕYW4xä‰'öÙe¯vy€ÝöÝoçÝ÷Þ'÷à‰¯/€qOÿ^ùå™oþøæ¡>ù祯~ù£1ÿxôí¹½ôÖÁ_üñÉ'4yÐO_ýõÓ€ý÷á_þöçßøqÈ!§ýùßßÿþø?r/ÿ3`¸¿â/ < Ö= ’N{êXÇ1˜A n0ƒêøžîˆ÷»ß`õ¨=N˜B®P…-D!¨@(¿ń&dá ]˜ÃOÅ0x3¤] sˆC!ªp‡!ŒkÄ!.Ñ…EáÕEÑŠBŒZfêÇ>÷e‘‹]Übq÷#ý•ƒŒe4ãÑxÆŒ1mtc9ÖøF9¢1‚´£çÊÔ9 r}\Ç÷ÀX¿˜…4ä!ÿ ª,j vë$"!ÉO-2YTß##™ÉD~Q~ŒÔ¢&AIÈI ²’Ÿ %(¯˜ÈŒ« ä+íÇJ/Šq޵$cm9G\æòuôã/;˜Ç=“˜€„%ü0yJH*’”´²¤#•™ÉQÎYϼd4¥ÉÉøU™Ø”¤6‘Y+k¶Ï›Ël[®^ ÎcRÔü‘¸Qƈƒ—åðn9ŽzöRŸûl£/‰ La´˜bù”,×Ù>–³ Bh8‹¸Í…2Ô„mæð$Jцº’š0ìäD5jÑŽF™ ¥h*’΄®³|§6È8ÀoÔóžûÛ¥?ÕÈFœš1‚¨ö|úÿKc®”œ­è4;‰ÑNõ¨”§)™ŠÔm*u›Lm*)Ÿ M«¢”£^|(QùUwžL݈ifʉd ¦(Rð"ôÓHA pK ` ­ä€vp o§t|Ñ0ƒÊÁ&–Ck2O*Õ•B–¡’M(e+ÛRXb¶œ–e©UëÁUø!€h@ªYø=ãóc­úàñÅglqˆÀt>4Ã~ï|À(ð´~3ÍF^Å‘?3¢Ü@€7Êq|z­ä8ÆLìô¦……ãaÛX †wƒßë1`ÒΊô±êõ&{‰šÞ­v5¾î}/}'k_lÿŠö}p2_ù ¸vÀéƒÆ›AÛÿîbèóooÉÏrd÷–@ 0Lq ",ÀÀÊa‰!ñº!€ pÀ |ƒ°8m±wo ^òjp±3Æ cã;_Õ²s¾`-jTwÜJÐz½C>gfÄJÞ¦o_Â\¹ ˆ`Ë.@‚<}·ø Ü0`^@$HF—¿fõ8}³•˜qÄÿ>X‹ïÜÆY¿q‰˜± '"Šb#1ø´ù'€ÂvwÑ2¶ñóÄ266(m% XaÉ Î¢§9»_"o¶Ç>Æ‘ÕG€h:·é -™ ‚ô¹!ÿ½€Ÿ˜|MŸ¯¿ @d <^Èîsâx4\¦ƒäÑ p x !e ØÍ–Gµa‹`Ëã xsœëaþ¡•(M‘‰=—CK Ç6 @ÏBà“ ¼Ç¡hîöÆßÍפgéGÀÒ²råƒãadûÌŸFŸ|êÐøÅã ™&2©_eÂ7¿ó•僼YzHȬ$À @.€ÀÖï@®ºë}vÉG›oAõýç &º¶#p ¼€é€ÇÑ•QàŸÑ´mjíàÿ¦û6ð\wZ÷J2ˆc9ØA]D°`KÿÀ'Ã^¿.Z§Þíé£þ"Icp+ÈÀ b±Ž  "Å+ñ¨áëˆDZc ç4¨ƒ 9È=¼Á„Õˆ@=†qœ€åÙdr: /ÀÖ½ Ì,Z¿`˜w "À‹øâ!¨Á5"‹üj=}ÂC_lãqô.C[Ð0ºÐgÛnÖçPÛ¤îâ²M”Á•”Lì ÐÎ ì6ƨàà¨îíàÖ  Òÿ.±î‚Î!ÐaÀáÒ!V`ÂV0.ÒðèÒ&°çèÝ'ªàþp'äáØ|îÔê ü °!Jh úÀõˆlÀ’Üào/ã‚Ì‹PÉxKéäáÚ²mÛÆÙbÈPÛ’¯œÏ„Þà ®!°¡XO“¦‰Èp”áãÖpZ wÐGxnAvùÀên þx}(O}F/¶Ž ûÏà¿è€›!`Ç$cîÌ:ñP^€ÐÇ hkÉJ éÌ‘~ä펹Ë]ì°ÔÌ!ƒJ0¨"m<€ ZàСsÖ!`ÿ< .HrðvÐád'Ñg€0Ú à rì}Âo‹à` ÂÀ„²Àæê0”4î}páÔ‡dí£Z.÷Dn亊®.@Né˜îœNÿÈmé˜.°á‘`@ê!ªÁ(r¦ÈœXižÀ€äÁA ›v¶(¡à LÀwî}€`&›ÁÓØÌ³ ¬ì2M¶.^€ º@̾ï¼}€² †r}ša"à¨ ÑœŽ¹4hqѲò5ÐàxÅs |ñ‚`>àOño o’àQ:H.ˆ-ÿ×AuЃxP},Oû/¬@Ö0 ÎÌ}z¡†! LHPÒ‘ [êè1}x!·0¤º‹t/šêÁ"Üpè„.}œ©1‡êÁ‘ ÓG«.ä¡x!5‰~ä°.6YÒ%Mã¼¶‰8MM‹Œ³A‹9÷‰Ñ¸ ¼.¯WpÔ`jðsðò?fïÖá ðÖ!ð .›±;.Hªñc¤2â&NAvàa ä!@v‚ð‘jÀ1Mè úà± À’èà473²Žó3ùQ}°ñE}Îì,0±âÚ°ÿÚñ ¡¬¡ªOA͉ÉP‘áC5} ‘&ë±ö 6×KþTê8=3Þ‰ 2Áî¶ÒŒ!— 3=ÄòÆ,hŒ1¢à£1ÃÐÄS+ !àÔa< . Ô.—±ס.ÀÔ .H ÞÔ¼<Ï×.À ÐÍ}¤,Ëìw:RjÎ}b«n ´þà@“äÔÇ:ï?ÀöJ*  À  ‹<-çò±§âaÌ@@¾}ú1åaÌ>@Ò “°ˆ„áB`Æ9*oÓÖXÕTÑ &À#·È7×oÜO3‹GÙÉGÿ”ì¬2aœS—¦5”tZy©ŽàIãs`!Ò¡M“Àð¦@! 2@˜B˜Âu$! h¬/»(FÏ~) J”5+¼@ `bn©pï/ÁH´ ÷|ŒBe3÷ŒLþ’9™U"VÉêΨ† ( úiÊ °6V®>6 D€€0@Ò*øÊ­Ò p@ÊH VàʡР0† >Lë(,Ç2är‚ Ü“NË3\ÓAØÕ§ÔÀ, Ç)£é> D-Èàî@J ‘.Û¾€ ÐǶÀ BÏÁì f ÿ€V©o®àN@ uB¿pa÷‘2Ñ‹&V€TÝ,ÁÆa¦¢Ën©HËÁq—ë–. !L– ¼¡¸Ê!"à–Èè>À¸"`€$÷‚–詞Ô!zjÎÁ=G‚@’ ÚôV N×A2`x'¡Æ|êsæÕÈæï} Á©ipQéá|Íd@ð`rÁ#/.·:ñÔ`êp&à´À„.rA+Ö3¥wzÛw‘¶p›õp·)q»î·R€ÊPŒ@,»ú©:lhˆJ° ,ŒRLÃ60ÄP X¬Ÿ¶A¸, îîê]ÿãµ¼Æ ï´ö²t p ¶çÐG ¬RxXIÎìæÂ1&²"§ÁÆàÖwzQXak~÷%~/Nˆ–ˆÇëȈ0àÏ-‚;a‰ÕnÐÞÖˆØæ§ô‹Íˆ®t 4[kɃ‰©j?àjEØK޼&ïz“0Þ7›:S~qmcÀö31cÔlÀ°áÂ@þ–}ŒÔJM}YåtÎ4zÔ~ÌYÕŒäŒt@ŠNæ-“I·XØÜ ÞöGô© àßÊas¥•ŒºAÐJ^×î €˜€Œ¤+ÊA­0@ìüjŒ4€ ·ƒoÙÆ¤ Ìôð(º/ $ÿä Hà¨àÝ@³ >àpà¨3ëØlïxf²ò€d‡†PI@†M0G/fu>` ‚!”z +4}òàJÜ\m‘Ÿ:ªOÑ Ï9WŒ˜ˆˈg‘ŒË¸€Ê(ȸî^ ':á>ÆÔ!fp£ÎÁ ¡ÿt!˜ þ4 ñ ÀʺàO¯-½ÌW¦{˜3¹¨šCn‘»-Û¤ÚœqOÚûªÝ7«±hYÕÙ~Øy_Ž´9»rI+­Ý¨ì®éóÍœ@ ˜ë ^do‹’{ñàT•.P ,2þ¸:B3«}wŒa# D‰£ŽÎ åØäÈí4£®È(J¸JY[é²1›’o¬Ãº³7û9A;¼Øu´Õ:®=Ëd V µa'¶ñ?%Pòà(¸qu¦©©¹‡»° {yqÏP²í˜yAÈíg”¤ö‹`š ¼Žåû~°»³­[—¶›±*Z½;´Á›?ÿÓUå!Jspôÿ’a½Û{ …Tô¾ä›`¹pBÅyEñ;Åõ‘²U)Ûg”^¯ÆGm©{9|Á‰öÁM°‚˜±}ÊN30 • f€¬ ½“á2óàTWu|mU¦w»›>õ‡‘{a¬ÊñÆSʲ¥û’2Û4²»žÎº;ÛÇq—IÛKOœ·wÔ¾3KÎç¼Åï[˽œ˜ï7ÌÅ<Ç÷¥ÁåȳŸ³ÌÛ(v ËñîØü»é“ÎMÜÑwïÅ}8ÇËýû%£›Ïy³•f‘Ç;Ð{I͇|Ñ)¼´í”"^Üí§Ø)Ù… ÝWJ¬~“ Ûps¾Ú-°ãѼǃž—³‚ôè‚FAL3€[Gàÿ×Õ£¾Q^ cÝæóÞ„ó\ÆÃ\ìu<×ɾ׿EÈY>í•‘í3@Þþð^p…©C)húþ๓ë‡AòAÉšP.Ù7ò•‰ò-¿¾68_“<ÿô¯*¡2þáIÿ”ZBÙ!vÚÿ~Ì‚I!¢Óˆa×뮆¿„ŸøÏÈøÝˆa×ó½ ²‚è÷(€ñáÞÛ[^}záOá§Êö“"Æ€ Äà‚Áø@ æá¦€ ¤`ÈÀú ¤` ¦àõÖüãf_BXlcȈ¹LÊž,ô®M!#eØ›}‚Ù3%@y;rìuÇ£Hd!ÿEÊ P/ØÀ‚-òÑ2Ï!ÄadlT¼8ežF“Kò$Éå°•*d(3âÄŠR0êÜÈ$OŽÈf­'`Ñ„/>TjÓb›Oä;‹ö,Ô©Sײ} ÷­Û¸ͦ½›/¹rœŒœé”F‡¡oiÒ *×)Æ &; •{ ù±^¾~ öVXâ‹wHŠyrß¿ aN£¹g&1†l¯l¼Ô­»;·îÝ»cÏþ6Û°ÄZ—!ïå¹}®1^Ç^VÀôr3ÃÎ37nèÈëƒN—>;¢¸GÌ›1Á‚ñ©çGØžhõÈ šrO=>HÿXÝuÙ=ÓFwßÿÅ`‡ä™$=C¥·^{úùáÞ0õ #P~ûE7]€ØiW wàѱł"ׄ칟|ôÙ‡_0úõ! ‡Z÷!‚gG‰åÕ•RŠê­8a…f(c‹6~çá€!˜ ‰¡Œ*JŸDÒ\¨’†4ö7Û\t‰IW™r™it㔃È¥”³‰!ä|sF Œ³I |9öšd{µY 9›8†™ˆ³ÉaœÒ§Ÿlº '£„Ѝ¢|öéÜswÕÆ\§Ëeªé¦h@pÁ £Ø–œ§Ÿ†ªéNáF2òìb‡<Ðp'<»ˆX«IÐ#оÔCl0Ôó0óÕ7 ’~ ÛŸG°ÿÊJ«­¸êJG<½ôz"V}è1l±ÇþÌ[’a)=û‡«M;k­·º‘ë®òðÂí”AînJã*ûb³ê‹,0îÊoµóÖë¾WÛ¯0ÿšË¬³ðìVÂò^»«¶ùšç­°Äú‹l¹Â80»ƒš×f’‰&]÷øóIБ³—6…˜Ñ £ƒRŽÞĉ£NVŽÎ<;6H'åìð Ñ‹6ZÒJw☠NCM4ÖT³*§ªŽ½جÖfÙžšÍj^s5cÇÛÊCG/òÌðÌ.!Íí«·Ø\1ÆÕTY°57¼ƒ4ÖØ°Áí,Ür×Z·<4ä½÷Ç@ t àcÿPã802%nxããB.ùÜ•_¾ ßi~’ß1Mè†#®8ã„ÿñ êq«n7ë™?<烫Kîè¹›^°ï“Ó<æ¯_¥Ò߃þ¯ò‹3Ï;˜²Á|fÍâŸX3Û“YrF–t£ÃÏKò7š¸æMûGï…¾úõRÊPðL¸¦;8 ¦ð—¾õµ¯Fø_ég?Lµ6iS[§ØÖ*uTЂ¼Á`ØüÓ‘[€á ·xÆ èŒ.ÐáÒ3aú6²ʰëà ‰Õ—‰„.Lá@7×¹†7Œ!=Þ`".i‡%5Qm÷#¤!C³—CÒƒaÚ {3Áï9ò‘͉d˜À'¾_Ùq“õˆÖëÈÉ5b²|¡Ü$ä>‰’Ršñ”L¥*¹È².¾ì“o™Y[ ¤)’ºÜå×é˯Y28“¤ämù±S7ÈÌå(cæÊWÊГ´’4gÈJ?Fóš#Ëæø¶ÉÍN>Màäæ7ÕÇjB—É\S0ùNxÆS‚ÃÌK1ÙLI.’õLË’hYNiæP± g7e PƒÆðŸ¨TèA]–P‡Š3–é$h™Æy¢›Íÿó€}M/;Zµ~*sŸÇì§ZîIÉ|^Ò¢šT(5«ÐWz3“'Kk:SRJôœþ„è˜,Ê[†œ‚)l€iÔvT¥Ä$i%M:R’2•6½èNs Í«ÞT«©L«:&œj$L*\ÀZÕQk­RA:U{:7q=é\KjRµ ´¡¨:½ZJ†¶R¬>ìU «M±Rtf Zm–Kw®U©¥ì<ãÕ}ÖU®wݬ^·êR¬’“«+hC{Zƒò48ˆ*-…êXv†é­K¥mem{Y‘¢ô‘›Íì2=ë¶ÔšS´/#-LMÛUä–öª‹%gPž [Æ5²ÿm­šeßy]ìêö®eË«oñ™W½¶–¦}lbË;^¢7¹‡eoy›;K_½”¥²ýnãÙŸì*2Õ i=¿›Rïî–ƒÀ%®Uß+Ük8¬Æhy¬à±~/½Ayn~ÍúXóI6©ùåïR7 ×ívVÀÜ-ðcåÂ\UÅ+v/j]¬Z óQºSêð‚c MµNÖÆÆo1;` ö6Èj31…GûÞ#w½ËeòqÛÛäÐÂ÷¢æ±: ä)uÇ<î±/õûeÏuÈ%opa<ܳ°¡¼ñY•ûä£9ÍaʲyªÜe€Ö—6÷Ån—ûL0ïÈe†*‘Éfâ?+Z#ÿ@£°h7ÚÑŒŽôŸ'Ýh@ZÓ˜t¤3méPcZ#ît êT§zÔ›>u«?MiU/zÔ Öt¬]kYëºÒ»–ô©9íèMwzÃ&¶§/l[ËØŸ½n¶³ŸÝa1;µ·Ð®¶µ¯ílk{ÛÜî¶·´„û,ãÎG¹Ë=›sëUÑè‡€w›Û»‹nw¨ìmæH˜·ù–w=å pq œÜŸ7Ánð„á WxÁñ‡Kœá÷ÄNñŒc|ã×xÇ9~ñ‹<äù†ÎŸñ]rK÷{'O¹Ë[•í—Ë|æ4¯¹Ísyóœë|çñŒoüàïøÈ§ò’¯¼åÃNùËk¾ì›ï¼ç ŸùÏ‹þ桽éOoæÒ£~õRg½ë_ÏyØËžêªŸ½íY_ûÛëÞŸ»ï½ïÿûûà×<÷Â/~â‰o|×#?ùÌW|óŸ|èKßöËŸ¾ç«oýìsûÚ·<÷»þ؇üß¿ùù~þî—?ýì÷zû­¿þ÷Ë?ßñŸ¿Þëoÿü«\ÿÌÇ?ÿÿÿX|þ'€È{è{ˆ€¨€ ¨u è€ühxhI7ØtÈ:—Øs‚ã'‚#˜s%h‚Ú‡‚)è~,è‚Þõ‚£·‚1Ø|3Hƒôwƒ9x:¨y6ȃ¿çƒ?oBH„X„Ž„G8{I¨„­Ò„BÈ„OèvR¨ƒQH…Uw…7h…Y\Hƒ[è…`è…›'†YX†cè}høwg¨†‘džRø†mÈxqØ„t(‡ w‡yg‡y(x{X„~ȇΈoˆƒ¨‡†Øv…ˆˆp§ˆ<؈‹Èv˜ƒ’‰“W‰–x‰H‰_˜‰šØ‰]·‰Ÿ˜u¡ø‚¤(ŠxŠ£˜ÿŠÿgŠ,ØŠ«ˆ°u¯(‹!X‹tw‹óG‹#¸‹¹Hz¾¸ÀÈ~½ØÄ(Œ2gŒ˜ŒÇ˜rËhÎÈŒ©è7áxÕ}ÚhsÙÈúöÝŽÙçž8ŽÓWŽ˜ŽÌØkç8tî{w÷qðèrëxŒöè‚øŒú˜‚ü˜‹þÈ‹ô¨|)~iz ‚)‹ É ¹ŠyyŠIù‰é€ É É*°sÄ .q€ És(YO"I’'©)&‰Šðøm–6}`‘¤’7çîp*™“9×&µ“=©)?Œ9rGrÚÇïZà haÿ #)<³ p w!’" 7™ÐZ ]y .0’A™_–ùàp€q1 y¡•\yi¹–ø•.À}©–.À–'Y–1Еù•°áfd9Ę. ˜^)`im —tp –]I žé`—x™~i˜æ˜ƒ)ù˜g!™Ä š‡9…yƒyyg´öДgñ›@ ù` -yÄy“³0jÑ•ìà÷ ¿Yšiáœùg!Ny€ø€œÊI’öpùPšÅ0•ófžØù›R ÃÉÆymP y¡ñ™ê‰pòùÿœÑù›b èÉøpPŸÂi퉞ïÉŸ w¿™ö™ŸÃyžÙ€¸yƒÅ‰´1ðƒI ëInvó‰#jœ(i¢ @ €— Zn*š÷€’j„-šÄ*àÐÅn<ꣀn±±š"0˜f!žhñBJ¤m¹ž6š8Z¢ã¶š[@™ pœgA `ФS:¤(ǤNjnva¤x‰¤*|Ù‘*FIs,Y’.Yrxz0‰x€zy6‰Ç áf "J¢g!䆢bz¥ ‰(y¢Ä²A£Xš¥àÙ…æF©ói©wÅÀ"°£—Šÿâ9n"Ð†Š¨[€ŸùШâFª¦š¢‘º©›²ªAÙ1P ®é¦¨J«¥*ìp¨ù` °*«b*¬NX|„:A~*sC™>™rÒʧˆ‡­Œ7œj —*°jÀ“5:–ŽšïÀÿ)›ŽJœìp¶©®ìš«Õ:nŸ:œÀ–í`á6©ø`ú¯ší0ŸßzẬçÚœþ:å6¯Ü  œªêêÀ–+ŸðZ°Ãé°áV ‡‰°ù ° k°¯Î:§Û˜$Z•j‰•{¹˜)š™o9–ey–©Y˜¶¹£7{“q9—uy—y™•[¹˜µY™—ɳk¹) ¹³£yÿ¥yšyY›¬i™®‰°y¯m0™W[­‰¹˜³yµK;˜`K–fÙ•UÛ–@+›^›œÇ‘uúS`·[ ¬U™™X‰®mùšÉ¨èê—Ð^ù·8«¥Àù$‰¯MK¸z+Â鸅ëO É—ø`·S€·±1û÷é·€›¸ya¹5[ Ì ‹;¹Ê9’Ÿ{.P³˜+𫬱‘–@¸,|ºÉ›¾ œÂIœàyœÉ¹œÚ¹®+ù@ÖéžÆK¯£k¢ãY¼ šžënÔûžþd±z  «Ø»ŸÖ;J¡yŸàëOç[ æk¡Íû {¡ÙÓْαÝɾE8«O·ÿjIø-زiÁ¤!º£‹¥æö¨¼¢já¨/£q © \¯«'º£= §Aš¦|¤ç†v]ú¥aº£dj¦ø€¦U*k:˜ ü¦?š¤h±šnªÁ0œÂøð¢RpÂŒ¢Î‘©,ª„ßgžy)vÆ­©y¬‰ÊÀi1«|« <©ܬO\£7J±Œ¯¡š£J¬Zü=»Z°¾ ¬ÌšªµZ¬I¬¬³êÅ¢Šv¬«kLnµ ÇQ¬Àl¥P¼­êFì­à*®äJŸ{‹¿Ÿú®+¥Ç‹¿£Ë± ‹Á»¯ýê©+°«½*Ã+›×Ë”’ü¯#›%{²(™ÿ²+{Éë©Íi+ ÊÝZÉÛ·<ÈŠ|©y¬Ç¿X|sÛ‘u{·y›™WÉÆ ¸ ‹ÁƒK¹)z¸b9º©Ë¸}‹Ì+¹Èü´Qiº¨«ºÔì¹÷ »[<³»¹±Ê³¸›ÌjQºíJ˜! …[•' ÂIÍÆŒ¢Îº8‹Ë¬'¨/—ÏwaÙërýŒÎ5¨÷Ðè¼Ïaóýû¿œoû›‚=LØ Ð/7Ñ>+sÖÚ6?)ËûWCŒ‹Ý—¶Šù¶_Û³fK˜N›³j˶m ¸³¶j+¦@ E{¶ð1º'½š!´=û¶0mÓ8-¿?½³ÈzÓ9ÍÒö¬³5›l{´|Ùÿ´«Y¹¡‰Ÿ+Ý6™Ë¤§¾mz¾ñû¾íÛ ÉKâŒ÷ ŸþD¿þ‰¼™Bœ¿™Óà‹Öò Ö:¿Êëo —Ck­®¿þÌÖø{×cžÒ[žñK°KÇL…ÕY=|2”4|¤\¥/Œ¤ ŒÁ™òÃdÝ©ÜÙœ½žÙùÂpJÚ9¼Ã-J£:«:J ÊŠ¡\ÅvìÄ Û5,ÁmÊ¥í`Ž~]Ö‚]r ÑÃ4ÐÑŠ(yÐ, 6?9Ñu™­¿!ÜDÙOÇ uªêí@ÇslÆpìÈóéдm£t¼ÅYŒ«ã½£²ýžpÜÆ©Ý¬t\©ìíÀò ª£-ØmÿßB·Êu ÞÁ±«¿=„à'ÑÄomsý‘x±Ñw1”mÁÍIÜ þ¸ áWw0 ªŒ²­¯¯LɨÜÄóI˵̮xН„%žSn´ðž<°^°ó)ã–ü©.ž0nÇ)¦M-Ø*ŽÎ·½ÉCnã±¼Øói0wü-|i»³ny“V}AÓh+¶:”P¾˜VÎ×\nÔBRžÒE}NM³Z^ÍEmT.´m0«`k^^’4ýԋɵTnÒøIçeŽçQ›y±çjç,Îi©Îm Í'»Ãœº(@Ìo‰§#.¦Ë¼×Þ–1 l½Ô\è. =½•”Kÿé¶ éœnÇ”nÔsi«Ê\̪Žôܸñü™Ö«©ËÐ>€ôkÖvl¼pýè;×ÄY×ïYØåùãHýÛ¼þãÍ϶Ü×xÍßâI¼‰Ý¼—ÌØœ+jèÛ¨zÔŸØô9¡÷™)_ äbÝ’ä ºh-¾k§Þ•7ï.7n³ µ÷g|¬«ž½Ä¥©Ú2*ΆðpÚèÚÚÙûÚŒß^‰«:š£¯Û ÜÛ *ÜîÁp ™KÜÄÏÂþ~ÃŒÛSË×NòéN¥MþÑdxsP+ºwöhßíï%ß süÞpLé8o×ý=ß&ïð+ºßá™ÅS àÛ­íxñàmÿ8ÿOnøj¬¯êïæfÆy‘ð@?ŸU¬iŒõÄ Ù/‡9ícÙn:ã7Ζ4®Êl?Ìjÿ厪îΞ®Ð.ÈEÞùŠÊÁ±½=noonÉf!÷=y¯èZ ~Œöü²éÞìÙ‹¯¤\» >öõh|=oé{ÍœbzêïÏŸÕÔܸÄðù©Ž®µ°¸pÐê®.é}ëª;ëjÙèµÕvAÏiñ<›è»£§Ÿ²Þ·ßL»œ?Ê­ûî«Îú¾Á²[üy±è—ߌXï%7n´ïÉwïÙŸxÎ]áüLÜÉý’EiRÛÊÜyÇ¡{¼€”óbÙ|0/ó÷à·)áÖͰϑÿǾƒ¿ÑàpwÄ'ù$XÐàA„ .dØÐáCˆ%N¤XÑâEŒ5n¤g€‹6s!ÂE;Úú΂qÄ’/À,Ž ÔââÃ|ùdÒˆ¯§ 6)W¶|™o@-"væëù3¨Í|F] -¨r¨­\ >ÕcêÓ)ÄHšl6i¾µ'’•šÏXIîà–”‹pÀ;§Pë¼+"/UŸ@ݎ䋲+GÈ‘%O¦\ÙòeÌ™/‹¬Òžˆ{ùÜÁÌ'ÒƒÐM ªÌ€˜]CçkSK)ÁbZ^UùµP˜v²‹ovm"sï~Œ5ßò¬]‹å;ÝzúçÿУ¡Å.š´ð|ÄOÎÚ}tB‡8žùñZÛ©ƒönµfüùõïçßßÿ…8ʘb éƒ T"F·|Ú!m ûXÇh1P„·îèÚ(æ*‘ˆÙB Ñð‘Â|,ŒC¥8ôðªDrÎÅ›ËG.Ö4PŠÜé°FÄÇÄÝX bÂ@ý²‡È¬PèH‚TÄÆ ‚äÆÛô¢ŠãK‰ÒÓl̹Ȅ M5%PÁ)ìËGÄ ¼ Ÿ0£ O„ lÑ9‚ð)† ”¢ò¼<'ܳÏ?%´ÐÕº”Ð@¸J„¶ò$&Î@“ӱФs M23Qÿ)çdp$>óñ3+OKMHÃ6k%ȱ’´nƒpÃ_ë®·p½&¯Üg‚NM˜°ÂQÊ{ 6 7gÊå´ãÛä[í: M¼Ž¾ó¶3ï»õÜ+ög;W/<ö¨33:øä»½9_»k¾ì>¸‡YÚ¾”fo^:á­c¾>ÛyOi½.W¢«fé?ÇŸŒVõý“‘5,mÄqÊ…Ä2É%chòD*§T’I.åIw#Õª*t!6ÄOKM‚Põ¤%fm  •’¥¦k ó›Sý¶t¿ÿé/AŸc_ûLˆÄ‰@ ½ÒO Ox¾P3¬¹Ï¤ µK ÿSxš¨Šò(YU£ê¡©^ö¹)ð­z• ?5«ÝH+ˆõŠÁ«Â&–«ˆ1Ñ¥2µÅNjŠÚ"¡ ÍxF4B1ùQwε-½«ñj½D7¾C_sJ¿ð¯{ŒÇ(Ø\®T-¬aËGÄÞ8±=Þq,ÑJÅøè‚8FfäJu P±8«“óÚ×½ôX±²Á±š\c+]I¬¾2‚››é¤—’ÄqŽq.pßü8Íuír†É\IR—Î ÄszaD‡6ÒÙu•ÛÊÌ|Ô"x»ÝÞócB(—‹Hã¶æË¿í%'•C¤@Øæ‚˜½’MÃÿZ#›bOzfÆž¤2ÉAò”O‡Ì³!±”å~ÒWÐÉlá]9¨eð„ö±“¯èD%ªŸ|ºà_ª#¨B~5Ј~©£!•Ègj‘‘’4#_áYN¦B²  ­ EkÚkZæ±²œåh'ƒZΤ&–$¨7q©:åÓúŒ%Î$” îñ¼öÀÄ-p¨š;?"4bf…ÎÉ‘V{U ¡#½’‚ÀÄ´3¢r®e)Ui[ÝúÖýHÈ5°ÙÞhž”6ÜÒ8AN¦S×gù5{²ìs41ïÙ|±ÔÐ\ïr<¨F{µøc>Ø@‹ÈÚ&ò)¡€|Ó«YÄIF¦ahãäŽØ$î]©²œÿccs<@ÁU¶³¥mf"”0âC‚,ˆ¡ÔÁ1°KÌ ó·¿„1¶(üó`“ÊÛHo%mÈ,Lâ×YÀPéa¢¡ªd“§ÜÃÆ`Ã1Nê@ËQ(ƒ±­íDÊŸ}ΩŸùgEÞk5ÍNæ¾ ¡g˜âË:2>Ї ªâŸ‚EFÅí‰\Ìa>v¨©ÃJŒ桞j1DŽ‘ÀV"§ÙwƉØõ®q¸k(mW …èâ W I1Âlm¯? Özj!Ý¥ÈGW—_ÉøØ!ôD ùGiYR’˜d¥]8™+AJ2”s¥QbÊHâ*apÌ&+¹Œ*%k’Éôõ°«¸ÿö›ZÀDb\ˆgK›ÐÆ­7o޳«"0¦)dRi”ÔS˜ÃWcˆ°4­/m RÏ£T¦šä©ˆjVnFU¢X•nòUa¯xœÕJg3¬cm¤Y©F”AÿŒ•‹¡©S–Š©¦†æ¦SòjÈÒ—¦ehy Os®› DÐ=#´OÅY’ÅUWÖü6µi|t3¶Øüµ.ËÉËsî—ê± ·ÛLb| Ó—–÷VóñŽ#Eô$63±id@;0yt½Ý]%ûºlVö²Õ5,sØë`À|΢å×iL+šÔº`µÈsma"ÿêÙd:”ÕËÂÛ:sJ°ÏGS`+X‚PÏuï8lCéŒàVS(G.L™‡b¤ç²ÝyÀoû@Fp‚9oq>ÌhêX÷G¼1ˆ€´{⥼â%¯ymt·õnhê)B`kât3'‰&",àwos(˜dð·±à½ÇÝn‡½5W™_ÑueRÈø]?€Ÿ­àÛcÜxÜŽb™mA]¨‡x7žRð»K¬âî^}Ŷhñ‹‘'ã„ØÁ®Ò"§«á$º½Â£:†:z+:1ºVêSâE¬>à»)ï#‚äJf9ÛzVšãÏš4ãQ[Aa3Äïr9?ÿâvÆ3ñ ÆgEùÿy ÷*'%:îéjCЗ(‡š¥f˜UË„á¤Ä&ÙÉì ß]¢A‘J@ßçcàÿýßýC¡¹‘(Eñ6·À1>sC7us¸¹6‚9©7K{; Ü|Û7ƒè·d6¾ñ ÀQ6ç¿róÀ Ì´‹"†¯ ›iú³dƒ¦Y··)D €qRŠ— ·ìA2ù? ä?¼ˆ¡B#Ü= ‹ŒýbˆaÉ“ÿÊ*²ñ‹‚HÂü€B¡`ˆ ü?ü;B† <.üÂÚ¢BŽ2þ*ˆõ+“2‹‰:þHÊZ®,B0œC:¼R»µ¹8µvH5£j2L³7Zƒ5Õ8Z#šÿZ ]C+^µ®H¡È˜H“4¡a“a A”ª\3ˆ?d!˜éÙÂC H« B<š8Ô½:LEUäã+¸,’³,Ì:9ᙾ•{3—ó˜‹9Wl¸è˜½š£E›xœ«0Êã!­ÔجÃ:–«3ð Ž{Æ“@Ö8ž]ŒÃUÜFnÜø©;(A»ÇS»%.Ís¸.š¯ª»øÁ;°k.é¢lk+kœ“°R9w,ˆsäX;á"»y‹|ÔG~ÔŸ{h¬ ÜB„Â1Ô +ü/%¼1‹ˆ€`€ðH†ÐȽ,êÇK= 0Öÿ‹»»0ØûÙ8ÐÛ1/ºÇ$8yŒ˜œFª¥œ î0Ì;¢•§œÃP FtŽKt´­8$iµŸÚ‰UC «h¹«EO,Ä(µNÜÄO«‰P+Í£I­ÌCÅ©Ì_« Cs±ùÌÝ“Á„ÁÛKD’ LÁ”5câöyA½ùÀܶ¼¦ãlÄÙdÄÇ¥4@QA°ÿi@v»‘x:A{k'TNµYÌ–À`¢§ÿ«Cc-e`!¤,½¡v¢ËµËáÕÆäÓ;eSMUTœ-¬1'vš’âdNÛÐ6áDAñŒÎ¢A d›ôD6»á¬ÁÔd:°" ÌÁdWUUSÝÆ%ÜTbUŸ +V„(B…8VnÄJd}VhU/¬CšêDJÛª]+ªY{ÍhíVoýV žh|-ò“+}¹þWu]WvUß».8#‘ǹkW{V‹ÂˆÖ1“Pñ|š+Ä~M£¤”¢x5J) ·{mØŠpŒ4”Ê®´¨È8C5Ü`‘X†HCŒpÖ5*Ô¸QD…¹Ô¾\qT‡…ÖÅXš^ë‹™ÿQ‰6¸*çà©L\Í–R«¨Á)QÍŀēêªL³Öƒ˜Õd¸Î*”©‹ËܶˆÙ×TÍÐC>LDP[DO|!òŠ7¤Õˆ[MYbå,Ø),ݹÉÇ㙹UPƒØATΠπ¾qEÝÁ¹‹3Ú»m¬ï Ðßø–¼9”»8XT8Ã!·P±mÜÚÂ’ß: ü þqŽ|$† E€ì’¼sY(Êë Ÿ)J"ʶ{I†ÃQ Ñ÷•ìÎø-Íc q¤&ÎÅ-u\Þ¥­ó¢1ª\ָܻÉ;Ó‚ G ]·kÒèk «‹R„èÍ1Ò–ƒÿR‰( JëT°è Kr{¼ÏK¼Þ-ß0|£‡”£¹>EÂ!+žµ„ËDÝÒE¥”P_=êZˆ‰i£BÔ²ÉTê`!?4#vâ2u)’n޼¬°²Œ3ð®=STº4_ .(´ t èµc2vÈ“… ¦ ¦Áç,6Um¶ÈªØ*i¢ÎDÇ ,›`EÌÐáZ{³…sK·;¡|^EI3ºqÒÅîq _Ü=O}íÀÎ`)&©Ÿ#–*ž fÕ« ÍLEI‘|CMì'1¾Ââ[“¤ M1 €+XpÂuÜN¡·J!{ëF1…ˆˆØ8Nˆ2®ØlÓ ÿõ¨­” ‹ä)Nä#Ó8˜Í«•Dç@ [x´ª² GL¡ñ–ÒÍݸ–­Ì™ÁŠ˜ ¡ÙÌÔ™ª-ªGF™mµžµYtl5¡UdZ8%Ф²<®š×aŸI-FœkÆkÆ[4Ûá!çó ïaÛÖZ\[è›Ûت[ßZÎæ»ÑÎÝ’žT (yGZQ^¹ðÑÐÒ†Èû]Ñ;ž,]·;]dýc2ãÊŒX¥Øe½|ˆpÒ·|ž‡è’,d†°B€²ÈÞi«<þR„XÈô‘½ +S”74}»)M^O!éàÿ5 yF 3^ËAÞJ¹æ}Iç}]èÕ:é…V@d=Þ¨‰¢èmêcHêß{c‹^ˆ=&Ó)Üh•BÐH•?]^à:e¾úe)3’ÝËd¥{I_Qò³öÝ«´ER{ø¤‘_’¥_IÅ_^Iþ}$ÿmè `¡æÆŒ“YZu ššÁž¹‰5lµPÌd›mÍU;êEehŸì•ƒ1°² N+«›ǫ̃¡µUËVœõ5Ñœ4JLKd ª&©[Váuê^¬¨0i:Olcá@À_}mïîìànA¾!¦—>mpâšvÎ'¶Á(¶äÙta.av§ΑþœÿfS¯ÆûôŽ\®Âæ¾ý[§ën;Ð¥9h¾OÙPh¦NÊ>Ôj9‹è9×ÄZ9U\‚Fµõž÷T{UÖлЃ¸b]1ðÈÈb6%gw.Qý±¢CÓé¼ W¯?94—T±óÎ]¤ .‘ì…vЧ`!ƒ¯ñ*¯ŸzÍ‘ÝåðõðoÌ%’t´×i…¡ö™c‡eiºqí•é rÜŠÝ'm^ ÄIòݱÙÀ¹÷¶¹0ñ‚`1dò¼í°ìLi/­½”&^ ¼ðÕÖf1w%¯®ßz!ïÛH¥äRT4ÇSòKòùM2¬ˆ˜¾²Š×0¦>³!>0ÿ“?yq닾>›ó­Ûµ\¿sGG¨íÔa‰"îj§ _ŠJ?U{ƒs'¦ßàÄÓ<£-˜Ëff©ëëÆ7}£…\IUÝÞ¥âFáäî¬nÁìUUtc…0h‹àõnê‰.èù:h¶œâòéb‹Hö`QðGï×Ãj§FhB–ˆ¾jŒàØ¢®e;.$ŒèögoŸPÞD£íDAKÚ©XZIsÚ´¸VË–ZECeÖ´ÚÏüë–E ÁFw}dÂNlgµLklªwUæäp?ø5Ò|¸ýå[ðv »ms˃9=Ü’KÜÖHï\¾_úÓnÞisï‡PñÜò~sò@ïýÿVo„où5ªÜ«[]ºLçÞw£]Ó=»â_xÔ]Ç!%ÇsK/#fªðÏpNyW¯W×4މ‡øâŒ€‘vûzê^/öƒ_öÅÖ×`§Ê¶‚y­³^E•Q…-1 _Ìôù^œ=š^ àýq#m!/"›¿¼¥_nX6²ŽvÁóØ„Ðö®ÅG|„ˆjÕÙ¾0¥´$è;`aŠª£_‰WJà¸K¹Fô23O{½—ËÎYO7ôø³_•åÒDM‘™‚Aă`dG®÷ÊìÄËíït÷Qvì«¥©PnM~ x™=«Tí¾€äÓΙÔVlÜÿGL¼ <œe]1½HâôánûôL—›M·<$¾À%¦`äõ]Ýýè¤ôº)ú£ÿœL'âøçtòtZ õh¥æ½ÊO€È—/@1æ+¦E >„²0±|Æ\ä³'â^>w ȸ±ãÇÇò±saÇ8%æÃGÑ"F‡+íå›5%Ÿˆw6GzÈ1_›Z+}æã8ó¢M8u ìù¦ÌŠL5r êPê@‘Y?†\ÉÎG… ›"m8´¨I”5Â+w.ݺvïâEØ0'›¼~ÿŽ)pVßÀr÷Ò*lx1ãÆŽC–û.B›b aæ£#†ˆ¾2'"œ\yo܈ ©2ÿÈG,Âf)Hw]Ýúõç˜ ˆ-$i´í‡¸…à‰T ì®jæÌæ·C盛 .|*o|©‹ߊô ±66¿æž;¨hËÏs#LÞ¹¶õÈîßû…ãB„”¡ðï;”OßþcýRpâ €S1lHSD Ù²…@!=è>J$—iI×1;ÉŇex›ƒºuSˆ¾åÝtmøÐ‚ù4ÈC'²˜¢t\Uwb‹rqå!uÄŒ¨Õ„žçÐ1"0è 2Ù¤“OB¥”SÒeOCø €eCÆp(ňÒå“¥@VšˆÏ1üATa>¶gÏí§Ca §œl2ðKù¸„ÿá^K ¤çS;á˜ìÄiQ—[Ø;g*hNæŸ ¥æ¦õh(¢ùÀyÐdÈ}D¦–5úhL\ ä嚘R«¬³ÒZ«­L#‚ Ô’Y.à3…Sl1"fd¡`‘ËÒ’Ï'¹¨¦sù3ßmPGí®×îÖ[-¸G…¿† «oЇS¥ÕŠÀm°Ã«ì²pâynºQ '¹4²Ë­vÝö‹-„ÄÄ åºk¯å:o>Âëà´·R\±Åcœ±Æ ”ØÆƒìWmIÇ@|`Œ@yþfcq ä²]ÄèZn\2ÛSÒ\0ßÅsÈ?t~óý'´Ñ?߃ÿ™›âdŒt˜—Ì‹95].¨*'\˜©³\VÇÅõÑc“]¶Ùg˜4ª%o‡Óù"T €“ˆ0K»'±ë‚œ´11}kÍgÆ ¤÷³žßá–Ø8L¶|Yᣅ .°ÇáRä£ù[üøŸg¾¹bh³Þºë¯Ÿ­ö˜OãDLjù° —;Ég î—Y¥P/„‰Õ-…‘ñZ!Ôéò`:–× åvBk-LP·f »÷߃¾­²“Œa -ÔrEp ưq è%Z‡m0ÙfÎåÿolÞç3Ìa^ (c̨Ç3û+àhÄç@»€­JÓûÿ`rÖ2¸øì/>‹ ]6èšal8› 32Á6g(î¸Ë4Ã4¹lÁ1pVš…%LíhNÄùÐÖf«Â¥S@ŒM&8¤¥)IZ¢ˆ„Â'>¤„q± ]>å*J .R¤`¢S5¿`­‹=´É 7ÅB1³H¾ T„eµ0£-"³)Dào˜ÒT¥c:‰ÑO‚AÅ©íx*Q¢Š©bª,‰‰Uùp•Í,•‘+‰éLP|bô­Æ!„r–+ ('¸àÌ‚gïø(Ey9½hŽsI‰@×âIÃ!Δéh²,wxq0œD-Wz-ŸKÜÞÿ7¿9r0‘œ@9FbF•{eã<‚“6Ìí{ïÀ#´0”·A„‘ës´B•ÑLõ X·´…2lå\6k¶Ú…Ïæ!²ŸÜXÁö„ñÊWàzWÄêù«†‰ YÎjç&Åß­÷ªöt¹ç¤ ]™Œ@âŠE¡ážZhiÂ`”Ö éõØh”ÜÍ´DésK>ôÔ’éQ%y^9¾vÒ°xt‚0ÙiJRúQŽÂH ÅVE3êb5«" À^üf;ãÄà5ÄÏGò72˜Há$x‡í¤@æ´Ãkåii×dÒÕPÆ! ”Q=!Ä¿"f‹~×aqÄ ÿÐÝtç;a YáâW6¶ËV+kÙËÖ…«QiljØÎBèU0ÚÂ1f8D jzGœej²à•‰/Šzêyöf€@õanyxHÄú–;€¥në÷Y߯D°"f›ëÜÊ^´#™z“G•™RQוDM iHëÒÂklÈž¾%“˜ ²’’ôRª„bÉȧ|šž˜ö9ÝMùï +ù^Á«ÔAÚÆ¿$Ãoçâ ›ç"8Áìä®>i­ª¦ ¤|°m²… …ž6˜×º0[¸@ mK¬/}‚‰¡ñ‚è£ÈõØÞh3\j:qÍ¥áÃFŽÂå°c»ÕÿòX Øšê`j뺜/H†Ã „EwIÒa%óâA¼4Y.̲‚»œ—½ÆË»P•©ôä2ߌIr £výòE¼ˆí‚13ã4ÕyVd@‰×OhÂRuÎÔœ.òKÕ,«hØ<æ6zKZ„Ó#ÊDÜŸ —:Y3&´/E𨃴áqs›æ3átéÕMtyk&(ySÊ6“Ñ£LµAGÑ<ã:×@#³PAŠ–Õ›8•-vÂTå%u*1Õ¨KÛÐ,¥AU ÀÖÞJ‚ý£œ&Îã(°›sšàù!aê±{º§>qM¨º^7»5v!ö v6¤™«jë[Ôÿ:Yâ_cõ¢Ù|@s İŽRXdoÃöÔ€ø™WàÖ°â;[Bàbéímßæàú«·¼á­ÀbÆãíŽ •see85‹à£X—3í˜Aé *Ò‘…âD1b,Êâ,Ò"ë”\IÙa]¤dLÙÊÕ•‰PÚÕâ0c1ã1"c2*ã22c3:ã3Bc4Jã4Rc5Zã5bc6jã6rc7zã7‚c8Šã8’c9šã9¢c:ªã:²c;ºã;Âc<Êã<Òc=Úã=âc>êã>òc?úã?d@ ä@dAäA"dB*äB2d‡C:äCBdDJäDRdEZäEbdFjäFrdGzäG‚dHŠäH’dIšäI¢dJªäJ²dKºäKÂdLÊäLÒdMÚäMâdNêäNòdOúäOeP åPeQåQ"eR*åR2eS:åSBeTJåTReUZåUbeVjåVreWzåW‚eXŠåX’%A!ÿ STARDIV 5.0 ~> K;opensips-2.2.2/modules/seas/doc/images/image106.gif000066400000000000000000000740451300170765700220120ustar00rootroot00000000000000GIF89a6×ðÿÿÿ,6ׇ    #$$#$$**##+"**0&2(33<<22###"+""+++++&0&)3).9.)33-995* 9."55 >1%<<$3332>21>>===6D68F8žFÁ÷íÈ”tÖigVUæ9ÑU¥Ýé矀j5„j衈&ª¨¡òè£FzÔ Pé¥Yšé¦œ ¤)y’†*ê¨\QÚé;Ÿ¢Š©§§¦šjm¤Æÿ*묓Jåꪪ¶ªë©°ž5ÁÐ ÕQ… ¢È°faß‘@;›´F+­U¦²º«µØÞÊ+mª¢HCõT§h0Z^8‘¨9í»ð&Um®ØÒkï§ÚÖÛ«zˆf9œÒnM§L Jhjû­†S*<¤²Dbȸl~‘M¦J*;pŸ)ˆ r\¼$—Ô¼ùÞ»jʯr›ž‰Lîz: Á>Z‡ÎÛõn–VZpÃ!Ò£ˆ&'­4O(ãÊòµööŠ@(i"eª €Ís3€èdNã”_~ì¹§„),‘ÒKÇwÓPçûtƒ1"ç!"Xkÿm³)8çwbQèÞ$´*f¸¡È›ÉíøÒt×{·ÊÛfž* ¢Š)3‚bž‚si§$¹$âc§k±|娢 ÷ã´O9å’×ÝhᣉÈü¡;,3Ðç&œÚ͹ÓYXêux*:T/¦"˜Q@\íÜÛn«Óàw:ùxÝ—o~\·oø žïþû¥~¯ûµãúWÒù-[rþðe«úì«·îg2þùˆdìPþ·¾ùe‹}äÛK¥E(³ð&œV‘FåδœQ†µqé¯*€^°Já¥B hyŒ*X¼±™Ð)L@@¿lrˆ_Õ ¨ÐÁÔÿv€Š Ö$‡;\  ÃbZYbˆˆ$¦nD*œa c·DZ1'7DŠ7H+#:¥ƒ ø¨'… !-‹šF:PŽ‚€Àïx…Þ†+¤Žï¸c÷øwèk#â`‡Y°U–jÇ/r…4@R ‰\ä@úøÇB Ž¾ˆA$IIK$“€, "H= „“ï$*bH9R¤e„j£Hâ¨KD}ð—ôº[¡t©‘\3#ÆDH2y^.“™Íäe/}©Æîʃмå3³Ùm*SšÜü&8/Ήx“˜ŠÂ:˜»C•³"‹Š§<çÙÍyÚóžçT>÷IÿÏ…ðóŸ‰z6ß)Îq¶1ž´ø|IÐÂ Ó m¨@½Q»ô¢-F1*QH.*¡+sZ:;Z—g”¤ÊÔèIQZ©Wå“¥‘'Hרª‘´Z+½iK©Sz*§=…çF‡úÑ ÎÄR@5*R ‘¥25$ö\'®ü‰Ð§¾ä¥VUiV‚Õ­˜!¶ÿÀ”} 0Üwh–³ž­hIkÚ'ˆy̬Es™Ÿ\[,«“͋­nyë[÷·Îv..{‘«\æ:WÎtuëKhŸ:eú´gw¿Þñâ• ç¯zÙë^øºB¾ôí´ ¹9ëwÖ:«jN(QàøÀïHð‚ÒàGxÂï¨p.œá wZ ·ff´³9í³.­Õ糡¼éèn¢×Öæ0ïù펨xºÞ.7§ÖÚÑF¶Ï™ÿ~6Åk³rµ¼œǶJ÷YjGƒÙ¼ gor—Û\_4ºünm~;¬î˜#sŸPèí$u{ê/·÷+x9`‘†÷äÕóMù¶_n®“ÇëÌ4:Fþ gCaØ”ìã+x!a^(; äh°†µ¾õ¡ºÜ178>ƒ>d»§ìèìïÑ+úSÂó$çñÝ·mñbºê݇G¼âßéÆƒò‘ÿàg²ùÂt>O–ÿÈXÒt(\ WˆÃ3ÿÚÉw›Ð¡}<7^ÙËfä"'ùžO~ZÖûu«Ÿ¯Kð_¤f½cTé6OxÎ)Íó4øü¹tçûð[2ý©TŸ{KB­³ðtº¨ ¾ÐüÓ, {Íê,Q ÝüÍï¼ÔMýÔØ,ÕTý¬Þ Îú\ÇœÁFëÀû½}Ü­² /À,æP¶ÐŸ€®‘èZ P €®Ó 躯‘p)P) ØÒûšÈ @v×z½ |í×€-ØÛ@؆؊Ìú‹®s Ùw½ y½×}ý×Û؃כ؋ýÒO\Ô KÔ²íÌ·„Ô޺¸»m¾þTÆ`ŒÆe½Ák|¼F‹Î,lN4Ä‚p’mÚ–½ Ñ€Ù; 8  ØÀ?ÌÜÎMÚ“ÿ]Ù¨=ÝèZÝ×Ý@ÍÄÍýÜ”}Úè*Þ:×å­ÄžÔµÌ,ÛÛÛdÌ߉ëßaÍUÀ=ÎX,ÏÎÛÅõ¬Îì<ÆÇ †Œ†,׋¼ ' ÒM6ì8ÈÕ° ƒðѰØÔ@ €®ÇìÙÊnáÑ€áÛ áîá .â$nâç}Xéªâ~á¾áÐáNÃ4^âÛpâ¯ÛµßE½ß­1ÜÛO¾ÛáªÔÏ Lm´Z­ÎÚÜÕèÀ΂ì°‡¬Ü"ÄÛð  ¤-p6<a€Ùc &° —` a`Ê!Äi¾æmþæq>çuÿŽ®wžç{Þç^ß讂®æl nçŽèvŽçzÎç~¿odßµ-´}ßKê·Ýäתê«Îêe.à®þê¡ÙÄgKêMë0‹ê‘Žë3·@¿Àá S¬äBÍ䬮Ûü­ì½=属º¼Î²­í5ûÙÔ¾²º®›ÃÞ {³PlìÍü°¶ PÈÎìRþ즛ìˆ|í*;íîί°ïòþÄp KìáP+ÐEð ›  gÐáð E(à›íMeê O à;î„üàP^ ƒlîþíì®íô¾Ã!=ò$ß·nòlûÄàP¬€ßÿë àfàáÀ Àíf`ð›P ð îÊü°líÖpýñÇ0ñÃÏôNÿñ&;/ÐêÍÞîëZ*«õ/ËPÃò ïê ÀýÊõ+› Oà ¯¿(ÿ®dϯf¿²  ’þ®á`øÞðë* Àð™ôá` ×àd BßDm=°E÷…ܪkíÖo=ÆÍ~ôïß–/VVû¶€q-õfήLܯ­¿²Ÿ0 ‚¯¶Ž÷*ûúþZ –€®B´oí¬û0«¬w­ )?¯O,ÜÐíá° ð@ø†ø3¿ N³-ÅPžÿpðÞî¹ûàå²€ùçÞÅì^Çt€ÆC«úç» Ø&`G Ì5ðÉ 8ä”Ù–qÁ„¨2jÜÀ¶`Û¶jãc&&V„Z˜ñ¡43x`!6 &LÙVðà6bp r°°š„…–@$h¡B†·A”HÑ"FÛ"á(úñÝR¦M>…ºTä6’&Qª¼ÁÒ%L™4·Ùĉ5¡LŸ@'V¼ØqÀ Û´M-* ê\ºuŸgàZ8¾á„ãôƒ¯#á®)ðÎLpݾ…ûàq8v¡âId¼ÞðíÖï€3 @°rbEâ¾­ýZŽ ÀVÿ¶ Ã[÷nÞ» ·ðÞÅ0@œ¸qæ¾a@'ÙZæ 7ÇŽ®2ì¦"'¹.€J ,¤ö`5жeöm*µècÊ·Ô³Ú6QŠ*à(©Œäšê»ðäû¨¼jÎ[O=ö܃Áõê[ï>òkh¿þŠÒ„Hd‰;î *ñ4=Û{/¾ù(¤ÆB ;ÐÐ? &JÞp;Œ-œ¸i ¯o~@ÒˆÂÂy¤̸O. Ò‘¾|/ºQap:s4ÑüÊ„I¬ Ç +]»H¦®Ë.NÜ »E„<ÐBNæ¨ÅNÛôÜó9t0ÿ´´8,GÜ&šÄ$>–>’é¾ihФÄ+EÏ«©"Q Q%ôÆhŠG#¦ŠÆ0&‘TJ²SM’•ÖNO4*ÔQ³ñH$@”ÏͺXuV\g­¦V£nT×õ<]TQSØŒ “ °Ë#¹Œ%W*6Ïmm2Gœ@—2c1ófÊoÀ­Îøb¾ !£ÝqÝ„3Ñ=Æ.`‚{[4¶èAç: &8a˦Âf’!,à5Új(”ÆÖަÚx¡h¼Êõ(b8•X3ªÆb 2Î5äm:~öc™(yY£ !ˆ ±ÜËXv曢9S›™äjy.ŠÛmÿn$ÜëRàHc®6€Ä«ÝsÍ@áºiWb»ðm2¡×0{ýÂw³pøõ×wfb¾sàoEàt‚ÀïDÕ>v›iâÃF€øÐÆ-¸ØOš¦QÀ=m&×Ö€ý¸Ò&×i ЍŽPzeÇ+|òÊy\óm4w¼s·@_HômH7õ…T_(‰ŸfüÝ…¦]r–f¿<óÍuÿœ¥ÐG©ôå…߆øm  ¦{¬Öèߨ`çwøå|°Å†_ìäç‹oÀae|¾ HìÆè+ì†7O°¦5æû‘âþ8ÁñF|C ý¢c8tÄàrš_l¶Q ÿ X €XÈ$Ж\ý@ƒW8ØAŒˆ°-ƒ8a¨µKtPaèÈ€¤€X«ªI;ð‡Ž0Z&D¡ ;h†@aÆjhjDXÀàEPh! ¢‰¸ ñ‰)\È ™HF'Æp†Û˜b5"µmà€|J¡‹Êñv<€ïx–BŽ´.xÂ;~±ÕŒC}VXÁ ÐÐøU’M¼Œî"„ " *p nûêWgYK˜ÎI¡ Å)ˆ€˜Sž&å¤ÏéiP;’Pô-¥ ]{Ç8\‹+He)¼€ÂSÜÑ 4€€’)…ßOÕ‰ÕJ&”.þÔ“La V9yU*5ÎM…ÚSa®•­Êì§[…š¼ìÑ.pSVðG¾c\ì@_ɽ¹c¥[å*º´JÒÄʯ•ÿ5%kœÄÚ@šBV­reh[1ËÐÅ6n³9MhÖð€+Œ+` :€ÚE‚ ]ó…i@ÐMÄ6Ö55’(´Š¾dâúªYÆ·vôf²‚‹lv„›\å^ö³Ýés*Ü,I—³Æ¢h¹ÂfÛÛ¾‹“FxÌ* À ¾ M\ílHVÜãÔô¸«,LqkÝé‚‘¾M/ýîkPê2œ‰Ô.wY °VH¦/( €‚~÷±0}ovØ‹› 3¾ð=å^çî·˜šõ°1û ÔC3¿p‘Þ•‰´¦ÿ»À^Ðû`—À½–­'ŽÁVbÛ—ÇÆ<ñÐ~,b¸¾/ÅmÿX&ìÆ—)ñ%ƉqwÜ{ùr8Ç‚ÃpZ6d"”ËÁ,ò›¾ Ì {÷È|‰²T¬!pô…Fhó“g¼·*'*Ë´sÁ6L\„uxÌqññŸf©º—iVævŒÛpü¼á/yÃ!gïÍÆ{C,£Ã76h:ÂØiƒ7ü鿍!–~6´R¼œj\:ЬöHòXfE§˜Ñß ƒœ´J'˫酩™C< Û8b 6Oc oÚ9[†uR@mñ¸zÕ¬NèbáêœÍföö·÷v:@ÂÀ¾ 7°è€ ÔÐ0÷<oAÕ  @ÿDàntÀ›óVXpï—ê›ß+ ÀNpX@€ið@®›Êê[æFº>ðwÇãg¸M¡Mím¤ªÕ×N5¢¿ÊòX×…é³v¸îv>6pÇðÆtœ·8œÐq 0L D¿4Ç7}ŒèÚqw0V°ô¦£ãéÎÆ÷Ï«ŽŽ¡Ï)ëJgºÓ‰±ÊBèB@Ñpv®«ì´Dõ]^s˜Zæú¥yù¸=±p#ºÖg–9t # °Àñ0:.m CQÀMÙ!&€|Ÿc¼9òÊ[ód_;ž¯€„Qè(ýå3OtdÚò¬wýubÿzÍ‹zåQß]渿Ճ?–ÎÁ­ü½N1ØÍèaOlY@aöwŸÓ¦Ÿ}šÃù¼§}±-ýÝt–·>ê_Ïñ /žü÷$ýù«ýÚÿ>ðÓNõÞÿ®AšïøFF¼åó¶ÄCàPŽ˜"½À)†°;ˆ<©ã<„½ó+†®û:ô© Ž t6s°@ôºð{¶µA@ < AtXÀ|@¼³?š[)áû³ý2ã³A0F3]ëN`'FÈ*ŸÃ:ñ€È“¬ú)<ï"áSZ§¿”„,P”D±DLDMÄ8àDß3AOD‡Kœd9ü+±C¬9BÄ/;¬D ’œµçÈ‹½ø´ ‡L¸‰³ñ 1 ‡3ðpðFBò‡'˜$°I.C  <5 ÆL¼F;y†é³+Óiô€m Ç3&CìuÔv1wxÅ…pGy\ÇzlG{lBLG|¤Ç{ôÇŒ`ÅC“EE@ï’] “pè†-GøoHÈÐ@3Ðÿû±ÑØ„NR€¬Ž”¯=s©ù2¾àID,²x²˜E\ÉpÞÚE"h3oˆ¬—vâ‹L0€è¨€o¸†ñ)Ž„°ûHqÌ'>ô»W“ÁZ”ü±XH”ò6F €½xh³L ¾ð†ÀI¹ÑÉ] ×ð¨*Ž46 Â>äzKø²½¥DEj›Á’TÉîÀ˪̤‚\I½à (“kØ’phÁ€þ™Iì4òê†/1,¨<É É¸ä›d£ËQlË8ItÊTÔGÐäK«D<ÊL¾Fä‹Up€®¬è´¥¹1'1Fð èN¸ÿ˜-£¼Ì: IÜÌÚK©ä1ª„+å,1æ,ÍÃã.ßx²$Rj³ž£³áäÎãü*´úLà»Ë‘ÌK1#ÏÒ4Í#£Nžs—í´§ÎtˤEùDÎótÎ+ÏB;Oô¬­ƒN[dO`óH—‚OÏ4ΙbJASEXûϨÄKÔLDé´­õä9%Î|Ò±ÊÌÐê²Ï½tEåÏnóKd>á|ÏõN ª©HЄG=IEO m¬]ölO)¤,N¥Dк$–€”®!}.m%g2ÒÏBÒeI½J;Ñ ýÑù$Ð3"¹ÑÏe,õÔÁE“Ò¥RXbQ+ÿ,…ÅдK.½?/ÑÓ\Ém%°Ó;µS=åÓ=õÓ>åÓ>ÅÓ<ýÓBTC-ÔA=Ô@ÝÓEET;}ŽA%TFJuÔ>}I5Ô@­ÔNõÔO€J UÕRÕS%ÕT€P]ÕVuÕNÕT@ýÔY¥UT5ÕTÅÕ[½ÕZUMõÕ_VEýSNåUOÕU[EVU%UV}UV…U8ýUYmÔbõTÜ Ö`ÕS?ÕV<…ÓnõÖo î(, iR¨H§sM´„Ð0«ÅuåÏqHtW©qµWu×|Õ×}ýÖÿ£Ÿ|õW~Ø%Ø‚5؃-Ø€½ €E؆u؇…؈•ØÿTØ»`Ø‰ÅØŒÕØå؃­Ø~ºØŽÙ‘%Ù’5Yí¢Ø=Ù•eÙ–uÙŒýØîPÙ—¥ÙšµÙ›õÒ˜}“™ÅÙžõÙŸÚJÙyÕÙ 5Ú£EÚ†-Zo"Ú¤uÚ§…Ú“]Ú¥}ލµÚ«ÅÚ‡ZžÍÚ®õÚ¯åÏ­mZ°%Û²5[kZp¥Ú³eÛ¶5Z±U[·•Û¹¥Û•ŵ]›ºÕÛ½%[¸íW¾ÜÀZ¿õV¼ÜÃE\%Ün5ÜÄuÜÇ…Ö´ý[È¥ÜÊÙÅ\ËÕÜÍXÌÍYÎÝÐMXÉ-\Ñ5ÝÓÛ»åZÔeÝÖ<Ï ÑÆuÝÙ¥[HÍÒe\ÚÕÝÝ}“ÿ;ZÕ[Þ^Ô•ÔßÐÕÞä…\_5^ä“ÝpUÞè¥\`mÞ×E^éÅÞÚ Öêå¶ç]Øì_½½Öñ%ßò5ßóEßò ßõÛUÙ^ö…ßøÝWß ‰_•ßûÅßÈõæÍßþõ_çµÛÞåÖÿ%àþ¢íH˜6àfàµÁÛí'ïmà ]¾ ¦à Ý Â` öàéu' þàŽ_úM &áN^Ö.Vá>]¦5†á®\ŽP¶á®[[æá öÚâßAâ«Mß$Vâ%fâ&vâ'NßÎ…â)¦â*¶â+Vâ·ÕÔ#æbˆåß’ýâ.㎠c¥5âÿ1Fc›=cÅâ4vã›õaÆíà7¦ã".Ü9®ã<ÖW^=öc¤åcW ä?&d8ä.äD~ÚCEväE¦µG–ä¤-3FžäKvÙ ÃcLæäÜm¥NåŸÅ­P&eœåRFå—=åTfå•ÝäV†åX–åY¦åZ¶å[Æå\Öå]æå^öå_æ`.MU æb6æcFædVæefæfvæg†æh–æi¦æj¶ækÆælÖæmæænöfìõæpçq&çr6çsFçtVçu6fpfçw†çx–çy¦çz¶çevç{Öç}æç~öçfgp.€@€‚&çè„Vè…f膖fp€ÿS8gpèŠ¶è‹ÆèzhS0æR˜‰^&P…Ѐ Øb€&  %  @U€%Ø XbFhUX xiR æTÈè ê¡&jg†è‚ž€ fCÐDРŽiU`‚B fHU0„(UPªEP…S@ ¦èS €˜öhbÎC(ê¶vë·vèFfŽV…BØ€  %¨jb& æCXU hbÖŸFhD@€ È ˜ .‰†ëɦìÊžgˆ–ìbN @Ÿ êCàœök¥VÁ&l V–&lU@°ìØ–íÙVg/Æâ-X™¶ëÿbV& … @E€mUÈÑnmU ÓFm`kRk™ë¿&æQÈlÚÆîìÖîföb™ `¦uªïÞ_ïæWU è‚F€Spjb&„“ÖÐ0îœNîÀ^‚T&àé›> lâfëí&ðßîîoòïïïÖ×qÎi—ð §pbFððÆpWðÏðç¯ðñÙ¾p OðÏðWnŠgñwqe.q7ñßð}ñÇqñGñ_ð'è ˜°Žæ&˜æB@E0òFニîgò àïk6òeqs¦€ó&òRàéìTP‚‚iUàrÿ ðòc¾òWsîÖZ§ñ—ñhUP„æ4oæ8 ¸é97fú†f@…çÆf… P…&ÆÖiž>ìŸ>æTàTXŽ.€E?îÂ&i“V…TØ—ækS@T¸jc&„*7píS—hEÈë xõˆõž&ílŸucVž¦€­6u3—è”^é–~é¨Vm XtS°sU@ŠŽ÷¨&fw'æxWk¶Îé™ÖDXæq?s™¦i ˜rM?pNó„ÿt—sŽVlÿU×ó»ÖTkV?n³Fk‹_kf¤6í}¿wbžjD0nÕVæf'æ&h‚SˆøBî”?éœÆx2·ø²>kš7f–·ëTpw‰vy¾ê¬Þê®ÎwUrà˜ÞébÖóß^‚ Ѐé^zbŽøÈîu"wl˜­l–÷ùရþkƒ/ðïô³ïð|5o(˜n׆tTŠ_õmWlÆvlȾndÞõc.ìÂÆk½V‚f_‚D0ye¶i£7ö¸p§&kb¾ûÆ~lèŽü¼/fD€tÈgüãûÓp«çh0?é©—éSæ¦óHÒ÷ómÇôdÆü{ÏéÂN…w'{í6{ÿ…—ñÝ·ñºÆjÓŽ}xŸ{gWS¨yÈùhnýmGèÏíÑFv—næCP‚EPêà—{¿þh»Oþ¾~me~Í—}ü.mûVæRpöRxw¹O…SÕÖj{þÃ/ìßô.hÀÆ~vÛ¿}ìˆw,hð „  ØpàCßM¬èðâÂŒ7TUÀ”*U©2Uà”*B:<¢RehBÈ!O’TÈQ(CòìdO!…*Z©*©¤êéTUª;•<™rå© 8c6¥iSÕV™^o†\„ê‚R!Ï@©R•L°BZ’óTªLr„¤ HU¡!7ÿ$R5Š©* …‡4„3®Ð§=Mº57ªäÌš7sîìù3èТG“.Í™#êÔ¨-BÄ(Ñ5EØUÓÞwç(MM å! ;–ÀTÅd‚U‹4P˜`÷(UÍPG€2®ÌT:¶óࡈøByhªŠ·o”M lh_êøÜåÍŸG–ï<ä„Eª1§ Qs¿ÁuÓ\ªÔR‡œBè°S)L°ÁN¦lЛ~ªD8áNèY\›¡'àR!afŠ)ª¸"‹-ºÈSm1Êk¯µv£9Ɔ£Œ=ô"A 9$i©H$’I*¹$“šùøäF4FTãŽ:RI%”O6ùbÿd[&Ù¥—aŠ9&™CfyæARN)›xÑÏŠs…ïXÑ;V¤qÅ;l<ñÊ@½ðòÎ+¯\ÁÆßí¤Áä@¡ùßû~m¾¯#>;íV/÷B"KD£8m¼Ò‹ì¸ò·+¾üÂyç±€Îãä m\þ7é€ïëFìµk¿½`,öÔ¼Ôû;O@ÑNÄÏ÷@¾(߆8å¼ÂÆÿ;®|^¿8ï¤o½¾Øã+;÷¨=ïÁa¹Ø@È÷ PÎ lÀ;žàŠ´ã ®xB؆ϵÃri^á‚ÖõïzÙóŸS¨BDðSrC ÷P(/拆+¼áöx;*Dh۳Ḁ8C1‡-TÜ Ÿ@!†‹‰ìrb£X5F-J-£›Ýð¦7䩯o&¬áà(Å1òŒŠa³¢Ç¹ÉUîr™ƒ=:ÑUÏt¨S뾈/1‘Œ~äš¿‡FŽõîwÁ^ýŒÇÅwôByókÞó¢7½Ì‘p%„ÿ¨É¯ñMQù̇>õí îÓ_üæW¿û¹"ûÿ»ä1–ÉMÒ2{;´Ë@Ö†w,°|G'( ^0ƒ|GÓðÁŽPO4Ù,k)MqÒ…«ñ—°hMdm³Óü&ÿªH´A…ÌmÝôÕ9Å•Np²ÓfLY.X²u’Šžà²g;ó™¥j"ñš,Û¢)‘w¾…xŒ±D>õ©ÐŒý,ž@1Ç8vyOèHgG4PnuO¨ä3ŠÐ…’v·œQ.…GÊEúB ósô¤ç DVÏ !ÍYBKªÓÜž,Si"K9_Ðï+x9`‘û ¤•ú)¹rŠ&©r‹ª;½ê(æÓ•uô—Á$ÈÐp…WðBƒ¼HfÿÈQABÕ›7Õ™U±ºS~ÊÓ!JÜîÌ)ÒÅU®$¥ëC:»¾öˆ°Ö2¬_ÛÉÏZ dz}+Ï›Xp.VQŽe›dk“Ùemv²´¬l :¦7ö ¤¥¨B ºWur­³žý#hC;±ˆfnsÈE3ú‹võ£6j×\ûÚ1R1°ÆUP‹w¼€žö¥õ“äL©×Ö&®¶iÂnuH¬žÙ*¹ë#QrÔ¤.õ~ø{êoY[¶ëb÷†Úõ•ÀžÕUJ¬b%«YѪֱ6Ó’mm{üÞ^A †˜­nÕØà˜VßcÛ6/؈i#0^±aõn¸Ârm°;¹ÿ[דQ8!%fÖ‰=l¸·Å4šÜhû6]êJøp)V1ÛF¬±Ž¶š‹ãçB7ºÒ.uñ=ÿ;a_UÇãœX/ð†Hå.²‘˃äs¥wé’È»1“§È,މrÊ%í;N ?ùÑ©ç}¥Ý:Ø0ÿuÌëeå¼ZßaZƒfE¦AÈÖ.'Y{`¦óМ¼ã»Ú¸Æµ;4¢uÆhOÚp~Ç¥Qé’*z¶´£ ½é…vú]ym´œ8j}–:‰kó¤3]ÕU+Ö²—ýrª—Hkʶ‹ÇíˆqûúëÔ"™ÆÆV¡¬wÍI[ìǖ“¨mÒ ‹bT£Eîm± }ìÿ&[Ù kµ¶ ,å•2÷p„dLÓ]J,ÖDì¶·õnD]¬Ì¤DsxŠT¥ºùÍÙžõ¶Wïxó‹ÒJijé+L„u¬eÝ ~×Ú_B7kàÚ&¸çMoã:à7´8ÆÁ¥ñCxÎÿxÈ£8r»–Üä"ÈSŽ6·¼ã//bÌenRƒ¯<Œ'·ÎWØs–g1oàE3jT›00çÜËA·0Í!'¹ºñÇ 2‰|Ç#còç8:ƒ ç;àyw¹É»²ó²<Éš:Œéµl:Ø5òÝÛ”ïKe›YéÊ;]Óo{íV²g '¦Ÿ‰VAO|é7×$ÜŸ8Ù.ÿÍåÿfä%ß½¤ùÕ‡gæ5Ÿ2ÎWÚó—½èÛ6ôΛÞï¨O½×V_zP?þï°$ÍOÝzÎ*4ô·ÇÏ_½ÞÚOÓ÷¿Yî…kâÿø‰†¯¿®`·…ˆciÈè;Äa^ƒÕ™®×§ñÏBè?oì`Þ1äձ؛úIç ^¡ÇcGwÙ祵ïÊ z‰ŸÉ ]ÌXt@9 O¬€;À– „8QO,@ÒLñÒð¼B;°+0`±æø7íŠBDQIŸS=AÞ°û=Ïûq›ùÂ.µÔñ¼Âß°`æXAÚy]¯” Å Äœ ¦ ;€ÿ+€;<œy‰\ýÎßDÙß+ !üZ9<ÁäÉHŠa á·¹L÷Ä8ð`ÆÈʦJÚ“¬!ŠáÓh’“XzºŽÆáç%VÞæÌþ! îá–žÞdâÓ ¢Ë"#6""Ê[úˆp¹&ú#:žAhb%Z $¢ÌÁ|¦m"*¦¢¦¤â&b µb(ŽÙ%f˜î±¢-®¢-¢¢g¡.ÆâÌùQ9}b.‚b= c'”*úâÛØ!Ù„1f–1×-*ãÎÁVå ã4c66b.R#áÔÒ5D7þà6â0zãv£¹˜X9âb2rã9¢ÿ#†IÓì9ã;’c/šã3Êã<ª£î‰ã=ÒJ@Âc;ò#œŒbý#@ã°äc{Å£A†eÕÜBrC.XAFdúãF\dC#‰Á¢F¤b …¤ËxäHn¤Ü¹˜).šKÆäÙÈ$MÖ¤MÖã¼Ü¤N:”² „P¥P%Q¥“íd‹E :,%S6¥S>åSTN%U2¥TV%VfåT^¥Vj¥¢À6„¥XŽ%Y–¥Y‚¥Y¦¥ZŠ%Z®¥[’%B MLQÖ¥] åQ"¥q)eWf%Wö%Uþ%`¦S &af­¼¥b²åb.f[6æZÆ%ƒÝ%e%]V&fe^ÿf`®ƒg~&h†¦h†æK¦ gB¥ažfjžfW®&g~%d¾åcÆæYÒfd.ØOf&fŽXnê&en&kæh'qzfijKp6¥kær&çV:§U&¦m¦ålN'cZgYJ&÷ô¦oÚ%ovço> @çR@qž'i*Egsf{’§UÂ'lb'\Òg}Ú'[â&xÞåwîg]'tš'zèq" {(|z¥|J'~nCubçƒZ§væþçeVèP¨s 耞g:‚nh‚*({2(~Fèt¢¨mNèa(Qö§‹¥†&'‡v(q~(D„¨(Àôèaþå1tV©SšÿW B@ÔS*@1¼g_Îgƒª(mZil²hàÅ(^^(—ÎhpÖ¨Ž&ŽJyS €1°&š2å1@ÀÂiS©› :@1 C-ØéR@”.¨x6hX€ n¡ ª–ÒwÆ(Œ~©xJikŽiq–ézè“æiS ÀDÜÂRÞ„ê0 Ã,`ÀÀ€TÊB¬Èé-ˆ¬@0 ƒ,tÀ ¨œ2%.å1Ø©š–ç1,åüi‰*¢*–Bf¢Î΢ºh£2ê£ ët@L¦ò©VÚ©˜J*h€zBjVb«,À@ ”§§ƒ Ã1(À¨¢ÿƒ9ƒ+€¼k«²« : Ü+:ä뜩¯æ©,,@šk€J'H[jClV$kc ²ê'—j¦—ƨDë®é9Èðë¬v¥¶rëpR*¸b%Wka.¥9(­JASÊÂ,%2€,DÁRú+­*À ° @ðl¿æ*Ìì2€00å°®,V€t €Ã†%`ClRƒP¬c*Õ XÆj쌌m8§Ë.e ÄÁ¢Ã9@ˆ@,¥\Á°€§¶-X t@¨Ã€g.بʆhÚ¦)Ì­íRî,Îêìô,œÊB Ôìäÿ­Sž˜CÏÊì¯ÎÁ¤©1HmUÎ'D[jí%  @(„¥¬@ÄFB @tí6\‚ €€ Äî6 @$(ëÅŠíØvlÙŽ-pþêRÂÁäÚé,í9¸-Ð:Ã\¯¶:ƒg~8èÃ:ˆ¯á~+ybkâ–çⲫ»Âl1 ÀšÖ+÷z®Ài¯–l0Ü/:äïS²€,åLnÚŽ€,,¥šš.U2o÷„¥8€;@ÖRƒTÃ6ˆÂ„%6hƒ6ˆ HÃPÃ6ŒA×Zp5hƒ(HÀ6hXÄB¦Å ªcW³bèwÀ5Ä(#ð6¤nXº06D‚¤€$K€Öþ ¤€A$AX®0@r Lr{íX’²lb¬(€3B8À~~§pÇú0J +¦¢C ÀAÛ¢2ÐÂxö2e/ñ“€ïA¬ƒ2”2©·r1tb+ލ_&²Á@ðÕ:r$àYb‚hí A$AX>C×ns ‹å Çæ:Ÿÿ(Æ @7€CPº2xþ𼄃 ¥'¸. °PxPfB¨Àt€p¡,`ð])pž†ìÈó1\¯9€ç C0,€çj+ xæ $ó:À(Ø®¥¾45&|ڳƄ%#·pÖNƒ€BXÆn%pó6ÜÀDƒh- tí4OoCìjC;7¦)»%M«vÀ¤ABìÑüó5ȳfeÐn|CAtB‡Ã7Áë²,e7€€7€ƒ5t@8€ƒô*¬@8\ƒtC8$48l ôBš•½\å,@Àƒë)wÀß)ˆØÿÂc Ì TJë HªK;§4‡iLŸ®5Ï67rÖö.X€„Á6`ÜÁlà ¤ ï^ÂkÇö6dá²sò‘ä;<ÀtMõ¯e8@8€?Ós\ƒƒ8B8pC_‡ƒÐõ&Ù¦ >ïpA;€ ô€ TÀ7|Cn~ƒ„C&ˆw8XC„ƒ·ÚH;0æ9”¶UŽfì¤>3_Š(LŸvTδ‰ÚgTGf¡:·)€Ý°ÃÃò9t@€ pá/¬@¬:;XÁ ¬h P泌¥#¨€ \€È7}Û÷^;h[e&ü8ü¸WÓÿ3|Ë7¥o óºƒô÷`xy†&3|€ ˜oK«/a¶T:ùk*øs^ySbyy68}>¸X‚Y>õbšùYJ‘P+U;<€@¤ç<;d!Ru#EÎ;¸ƒŠ·r[¥+¯Â5$ù}‡ƒ7l‚ä°y‡C7€?‡C¥ y|óµ7xwØ5-÷ù’¶|¢ìhø»„è4sù‚«"Wi„§³}JxQxŸ¯aßM,(¹Ÿ3//<€ |x¸C9\u/ø9¢p5^†'üPúÀŒk:§ƒC7µ{»Æà÷ß·'pÀT|5ZPBB €\8œÿÕC(9“o(©‹æh'§–ÿ¨ª¯ºV‚ùéŠ9„¾zcª¹Z.«¼$·@H@Œƒ ÄB>/@A¸FÀŸ@<Ï3Zÿ€ü€ z8päÝùÉÈa§tÓ#Û»02è€Oø’špªÁN\ñ"xk‹m7)}‹}¿”xä߆?-å‘úùûñ—oêù¢Ý¥£ÁA.þù&ëÓ îPĸJy¿+IÂÄö>ù%o‚i« Y>—? >oü ‡ýTåÁ¿ð‰ø¸ó¹Å„  ©È;fDx]«Üž.HÁ"e‡3ÙàC×ÁZO„}!aK¶0¦ð!Zc¡ØfTCÎÐ…á“`éW¼,v„@ôbµ„H½#N$Œ¦ë¢ªZ’%¾†|¯Q`ÁØ×@*Ò¨7RÔH®8Á-jn—;㉱"öå•dÿÿÀ'¶dàAq‹tÈ ³µ;ôͱŠ&±£ó(¿>ÆMd9Ì` A¦2æmŒú󆶺!€½ {àÄ^ ;½ñ'4Z¤3¾48CO¸ƒÔÑ (€a»ÃY>'F²ŽtŒ#/YK„e~›d›5VÈPn“M‡Ü‹6—ö TªòƒÜG'È)Yþœ´©Ñ-æ’[ÈÁ–x°ƒÖð]èÂëÀC2ÖŒu€!»H&CÞx¾ÞA‘’³"Ï‚ AÐG¢õAKEçó‰‰n-¥(QnŽô>£DZ)ÍtJ¾t#«<8Ðp`J$Fna.ÜB 0ˆÔðÿ~¹Y€A<}cÐ5àB!ºÈÃ:ôÀOÔ \æ‹$¹Ð†ï†ü‘ &†0L”á FÊЇm\}HÁÑm`âe¸YÍŠVµbÂ~HBYåú $ˆµ 8D5ø€†¨|°Â Ž’–v’”²Äñæ÷&Ε¦“áÈ„È%€Îúg²,¬ ¸ tÔ‚žÈ*ÌQ 8ð‹ž¹å0¿° ]誹&Z  fÌ ŸÊÔÝ$‘;¹K‚ê¡^…Ò„e‰¸V­I²„Z/ÙùuºÚ¸D ´AÝ2$©~ÐF%â:‘ ¢»Ù•Êøpl\wÒ­Ý*›_ÿ^¥Pé^Xº—8ÀXÌLkjÄ›b„rØ)=áp ƒµ´¥‘¤Ò±0x!W…‰K²\Nm•«ì{ß3!LĵŸØÆ ¨Q‰‰~×¢2A±hìL¼8Æ‚Ð&´?™@#cpov cû®X¤úurê¼YZ˜ÀNÖXÀ^p pX€–©¦ÕûdU#ñ²ŸLTàˆ½aÿNÞ‹ 2aë¤-8SkL!$±šÜLµO#5\®ˆu¶0¿(Zk‹Öý%lFkÙ«¤I·UmÂtè¼ñM™"´Yþ¶‰³gÕ¹!Àþð3ñœ)d;4‚õd;yAW[ÛÉØÎºõÇ_^ë™Ýõðÿ²ÚÌd“8bò¾˜½/8miã›ßÖö÷ìˆxH€“QÝ Àn ÞÄ;÷Ä$&6FŒjÖ߯¸ª/ަÇüA(Õ_ÇC£‹€ ®©³»®ðŠ ›Ä8ØÊµØC‰K ¿/Ï/Í´oº@ýÜ·‘¯t$&‘èWgxûêç.Ýx.OûÓ×^ÒÀïÍètŽû:´Nœ’ÛìÊîsIÉ~ï½ÿ]¿T'mÓCèM·ë¸K¼E¸’”{¤#h|o^¨D¤wŽïrõ$åe¶íf2º¿Ž¹3S‰‹DNve¯^sfŸ ñåW{ØÍò`ž½7¾ÜžŒ¾\é[dú… /øº!þåZ¿ÝÉ/Ÿ¤ÍŒÔ»){ZJÿðÔ×_ègôµÍ¿ûa31ÄÏy}“¿üƒÀ×Ïkro÷‚­÷êî÷æø0eÞB¢þTÿôHéüŽÿBÉüØéù<¨öþ S:ð=°S±å<Ñ“>ñÒ>Ã?çR?¹“?ßÒ?±@ÕR@§“@ËÒ@•AÁRAŸS>…ÒAßBRBaŽBÆBŸŒA·RC“C­ÒCyDÿ£RDq“D—ÒDiEREa“EƒÒEYFCûFEÉFëG¥RGé’G}ô&]Ràˆ4.”.xV€"Ú€"Ž´šö¢4A]R¤g.Žô-V€*Bhž.²ˆÂô/eÔ‹ * V ¬Àn|a@ÆA\:àZ"IïBz ¬ðô~NW`*BOŸ€Ò@V€ &¢ Àðâ  ÖOUI ÕÔtÐ@"2µP” àO_ÁQß:à UR•PÇS•ôS_BM;à ¨¥O¡€^US»ÔO]Ou•WC5VÿÔSAV)ÕOÿ•U#uR'Qù4 vU5ËTL `RÛA"ØÞÎT"ÚxõMëB¤Ç ÀLðt–Ô.ºUU)B\×µ]AMƒ“ŒzáÞT^SuU{Á $‚Z–^ɈtÈAÜ¡@"ÒàÒ3\A`^;`` –-ºõÒ\À$â`WÕ"@¶À>6d+6`1V)ÖbÝõàubëU]ß]Ídc4Hõu"^Á\ >•VEQµhÛa-ÖhŸ ð‹hÉu-òµK÷µ€`€–jM6 zZ Vj©ÖhßAT¯€œvc…ãl%µÈáÿÈó(âg]>U2ävj›N2V@ü6jç¶nïöþöd›i)5O•–i[´J¯T"Ĩ%†¶h%B8œ6i¯öqVqÛÕ%¬ö–ö²v_;·`€J—"Ü¡ÐH77%BNeÖ`V‹óve—vm×%.7s¯àw÷ìFNív+bvk7y[wt‘7Z#Wtwv% gýµtŸàxÅU_S—z'¢lg=iÅU$ÂPË÷|ñ•{±÷wv}Û7\ qZØAÜ×}õ‡tÆáa%bÀ…cû—ò—Z`Á»W"¾{íWf‰€OÖ>&xXõWØ—‚ÿÓwz)Â|ïu~KóZHH‰ô ž ®àxý5N™×uó´öTt?·W; ‡Uuå— :`Iui3µ‡g8N%–‡}x"€Tït"`Áˆ½µˆØh¸‰W P Ö…aøx…&‰ŸŠ¥XX)‚Œµ˜‰“ÕN}8„߈­X"xˆxL±…cò H'/ÜÁ¿SSÒ. ¹?t‘ÉÙ8y¤¦´%Øadɤ’M¸L¦TÊ@h-&9Z•4“E8d-Y"&œ>Ù:Ÿ0(ÿ“Ë“¸Z샓[ÂPwdKï<ëµ”çœy1“#ûôOUZÕU'"ÿ:Àˆ¸Eu˜ƒÕ™Ýw˜›õ”™™WU0¨9P¯¹™)˜—•˜™ôY)ÕR§¸WEuTKU õ ™ ÕQ£XE·›K¹U'µR/õlWùU™%ó]ã~i6d [›YdsöL¶aWa€ Ã5¡ãxU…c¢evIÍ·[Û ”íõfKW£g–u _Ñ•¡-Baöqwduׂ¡YB\š—ä¤ýuI7¶cº?ó2G÷tÇ×~NwVuqQv" •@Rƒ–ZWq‹Ö¨7ªyA†–ޝ—~Š–k×p—uE•%ºhuYâo©v-Šš—…F0ÀZoùv”ÿ}ZM2„…:£U†Ëá¨cW–wœWkƒš¯1zz?÷t³ZªC÷ˆ‰Ã«ƒú¯;w Ur[¦¥·8¯Z"úz²[~s­Y†#½$C˜„Eš®ú^aUø~À‚Ý!ƒ?Û¡OÖµ³ù¶;šUKYµqÖ±´`C˜ +Xfج]­+úƒ+·©åµ {bÖ¼W†ù1íú®mXqíøŠç˜\À Ð`dɘRϸ•X¸ÉÛ¼[¸•¹Q‡7ZÃ[¸'BŽ…{P?ʸs×ûqõ›"0Û‰÷½Ï»"<}9õ…¯€VŽ‰ÞŒ×¹ÎŸßÜÍ[âé‡õÍ)kƒÝásX¢· ýˆ{!»[×ì]MÙÿñ—ƒ—¶×¶á¾ø7—Ó×ÒQ×¶Ä;—O¿;d€KE»!ø ྶ÷ž‚uýìmظ‘{m ¸"ÿ"øî=8—Q²u[ÉÕ[ÁÕïËõ\縢KxÓC=¤qvuÕÖLPºÒKý@½öɨds{ÕE7e¡KXˆ“{aV€S]0"½hëu!ÖcuÖÕ·iA ‰…c¾Ù`x£Œc8=¥¿‰![Ž»¿KÜ>Ò;üÓÛ±¥_Ìá´½w^"ÎTòÉ÷æiØPµ ÂßÀ)B¿á˜¿ý["ª¸%”'P®\y÷NÂw¿V€èf¡Ä‰+Z¼ˆ1£Æ-*(7ñ• ÿмãeE"„îLD‰°Â•ïZž|0òIw a¾“ùH‰ ÒlÇ€Wƒ#w–{¦O^8]è䉿»ãÞ‰tÂä;š4ŸFÅŠÐ'РC.äúî ¯–í¦­zÕ¬T‹_ÃŽí‰wgÇÁ„ SÄêJ¬áÅ‹2~ 9òã+±$[¾Œù²vÅàË¤ÏÆ,]JT«°¨M^P,¢f;Q«Ýš Xî…ÄÉÖ`1Î3胳»Ã­»gÊŸE…Æ^üÝ8°ÄÒ´=ñxîŠÂßÅ:¨{æñƒÙ8|™|FÇêÛ»ÇÈÄ“÷ôë[Ðö/ÞO²û“j¦-TŽœÿ¹Òš€ïü'B]hà;ÂF”J¥1èà‚<¹#€;ìðàƒYU6Îîì‡PY­¨aMBx oΩDâo=ð`Q†Ø"‡îÀ"¢~üä†ÒÙ‡d’J.Éd“N"©€LÉŽ@„/ l9Ž‚/u g^6ôPD§‰… ±ÑÁ ¤ÑÆCé·%¯ Dæœ `^¿ 4PAÄU”–[Þyf˜͸gŸ"‹›±éfžjçV²(–ìYúd¨¢ŽJj©¦žŠêbì…JYaî¸j¬µ­o2iëi HJ¡‹åºª¬ÂKìcÁ2ß|Å.«*FŽk_°+ÿsYÍÞÊl¶ÚnËm·’ †.< .¼ —;V¬°‚Xèv® üÙVUTï½´±‚Ÿx:ÄÕ+l DNíËen+´ÓŽ ¬.»&å«ì;l°ÂÀ¼l™fqiqÌ,ûgO4ˆÅ ñëoœ»q»­ÛÁÿ tÐŒàË;OñDô‚ ÔUð;½,ÇS|L ÀŸãH*EZЋ~nJýNÁ ó”¢Fu ‹Ô¢ñJñcðÓ,±(‘sÆÈâJÿÅXŽnl¸PÙ€tsíµÒSK$wM1!”,c¡­ö;l¶ÁB_>Q®äiNÜäÿy>[{ÀlQœo«\×1“SPíEUN&ÒdSè9v7ÉîŽ\-¡ÅâÕÅ’†Ûý:VÁ:÷šÇ¯=ÐÎ ¾ ñKÅ·æ¾»U‚!¤V}™D“ðÞE»öÀc.4´–©üx IÞ³¤S”ãϾ- oZ¼I¤nâ½Y»¬=€.ùŸtGœ pxÅsX9,×?ºA$ͳv€BXu  f£›‚ È?ä¼£;ß™Ít¸ô9)^ãjLÌõ®š¬Kf<ƒW¸æõ•õd.4Qnø§)J‡ ‰E\À¦ºP\Ša¼pè#&ñ/ b‘h9])ꉛʡ½æ³¯ÿ~5`ðã“Cµ"¢®FH{›PÖ•*¹ÈC Ê—¤ƒ±DAì ¶‚£^9Vã{‡ê ƒÉÑG¶[Hî&!Þø ’¯hM,@+(R*Í‚ü˜¡…,²C½OXÈ…R…öIÚÑÆR4°5-qQ[ZåÆb5¬Mdo‘ÌJÑb“K^NˆŠùO]Ø$K˹ÒDí°eÓª&¯Òb–+ 4¯V º´!‹nÄÛ5y5¸®ùâkÎäæX*CÜL‚Ü2cË*L! T^¢”¦&bÏ1­à0S¤z3HÆVHƒÁä9©6Õm(¼@Sz¬u’‡¤©Ð|E°òéÉ8ñÿÓLYª¢:E¤€Òezbe}RוյÎÝËÞf—ÑUU ´i^°|2Ž”ï*Ää /`’´ƒv\NNóV^§i§YPˆªG!Å¥Û£›ê°ÕN$½*2­2©WÑw¿ ä/…Ù¡½”5›Þ.©Nµ&E”Ê=îÏ(¿ÇY= W£U“€>aªr4›Ú¹…€d¥©Xg#@m±ïTÉúªc/6RÑHqÜP‡Ty¤;N’Ã<–€ ’ÎR䕯èfY阈²u i•IÀlæÐœœMPF÷´GOöñF©‹ûØà w¸ßrÔ­âéÑ•©µ gCELNP¢ÿé±çi\`4 T 1.I#â%^`W»oR3–œD¥ÉKkR¨>7z0ïźĭ¯}ï;¿û2ˆQ ~ÿ à§j¿¬$0cÌ]+ tã]GVeºŠlÌMÐZƒ•4á*xÃî°ûÌ©Ü>x"õ£È´V9@Q‚Ø>'n¤‡_ c ±cåâ…±¨ÜžÕx!4³™ %&•…±e1$ ‚ÿ”Æ/Jäb£¢‡'EˆÍпZ¤±•(‘»ƒÉl`ƒ’‹¼ã‹y̬&o\9Í$&Ó—ÙY㜵nœ”óëˆÂæˆNdF Y³ÞÒ·8w.BÃüfÍéæ;ñùmådÿ3™É“aõYØœ®•F ¼Vg¥¬[ºïLO²œ¦íT0üòÎU*NuÔÀÓ p|E‘­„Õ Á©ÁZúÖ¶²h5ž©e}—•F÷јi1ü,TLsÊo=9ö`+‘ok3øUëI–j0®Š„ĉ ²™ ÂÁnez^(i 8kâ<%"Ì5`Á=×Î|Æ;YtaELlÌpÙÉd|Ó§˜2%ùQZVcÌÀeWQ^ï—rðçPL]$ ážÂÕÂ*°ßW"³äP«ÈÁÏ¡¸qn‰ZZþ±ÌHžªÚ¾©àIiTÇ# EïPÑ(§>‚R罕Nžÿ±êȱV!¯íÑ‹òÈ=BKR·oD1ÏUÄç¨U]çÐÎ÷c½æ¿“šr§¤Üì8ÒÒ.²ÝfVr¹¸w¼y%kW Mên”¢½2ik^»ŸáþŸ¡x¨Ñ¡EÙiž Î, »¬žÛ1­Äb¼–ã$(: ŸîÍhCàkä3Ά¾IŸ ¨®CyÍgêž=q(a¿Ë?RˆM%€½‹½`³Ú.¶N5ö¼2¿«¥é&lá|°˜„éÉ`Qp-„uækéNa §ÑœDOhÊù¨þU"¦îÞO»O’ïãU[]?ú Ââë5Å âö°Ù½Z‡z¯p²?íÚ-ÿbZámt…?³BtWÙ·ú£Â^ &¿0o%TÈnî‡XÀÆZlg"^¡l¹'¥”"!µK–å+ÑvügWR?‡l:‚‚4¢Zm´ [WYstZÌÅ@-?ÁZ¿°3{"&·µ >È.ètåsΦO…{6EÁ]¨—]æ„QÈ'{¤‡PEq^lP…yX²¥@”b†"FÇ%'—’\°ç%˜2rmø-³ÑIÈ—ruÒbÅ]Q(ˆƒX,ýÅ$˜Fˆ›ƒo‹1% Sr1ùeiIR‘V•–ˆ_…`£$ˆ˜‰˜±lá‹xi¥˜$bl…ÿŠŸÈŠ­-wr2cì d,3F/ÓC¼`£Ø®ð·Ø3jˆCc|*sü3e —q1¤r"÷ûF1Ôq§/¶ˆe®ˆÙH‹‡KÑtƒÞt6…vIs¼8ºHgŽwgéäÆS6“Ex±vËÔŽåxsÓSxhŒ§8ß6g+ ŽÚ¸˜ˆ9§d¤˜$§“a™æ‰›†tÄbjT´º†;1¡;»×uaŽPµ{Ñ×¾3M•ØkA|oaW@Õ} È"º×kÐw=©L‘~™j Y,쳊hb?HS©i),önº¦o·'o"¶I)nÑ1ÕÁ"XÿVÛVÀñ?Ú7ð—”èAIå”6éEÕ¸†JDŒ·Äc§’B<Æã9¸¶<¤kΣkÐ#=Ôc=¡ƒ=©]•íw8™›Kr”x•”ÓA9æyo±QW¡c@oh…;š}ñæ£ $b¨4¢Aí&@Ì“¢Ä4yuÈAôÇ£9ÚXë“£9)‚6±[«dšAb™4›wV"6xƒpôsK§¡ªUyˆ¦›Yš›¤@RÓ”HN·NRh4Úé1”dI˜ôšTFgêiJteJ©šØyÈÇžeŸ Qžl¸\ôI.؆fHOeOùÉÿy&Ÿ ·©È¨ÞAP¥zˆBz3‚žHQ0QuQ—ºžûÔž•*¬F£‘£®BH“QÃʬÍZcJ¦Ë­Î:b&” )nƒ±‘A̦ŠÕÛJ­ã*4Òæ„”¸çŠ-ö-¹¡ƒ¡®$fìJ®õj*,$‹<qIv/×ÁC±hÍH-ˆI¢s—·DPôoÜ´o5†˜™3dzY–ÇhF8Td û[VôqTÔ±[eé‰1c¯%;*ôhfüX¨;š²Ý(>»”xŠúÕ鲺”7p·‚† K‡Š7Ð)’óœÊ)žhYté´f9‹XpwxÖ„³Óävˆ×³Œ"ÿ¦+Æ­îz£‚d­º¢ª1iM2a™-(õ% £È»&8ã—ºV°iËÆ‘Øf¨j¶k¢(¤@JU­®€’é—WLQ6!~ƒ[@r;~k!µ¹ya!v­™mH—Š‘5‹$'v¬'[WÍAnun)ú€j[· a<릤¿…Wû§³•º"E<`Ú¤ÕòÃ4~Lk6¡YEi|Mõn‹;\×r83:“cðƒ˜;¦±`Æ–Y¦–Ó™+S±n)Œhô¼“2²ü†‹³±°siqoÅC{Œ-Çd+ðenf©’Zqú!xós4Q‘2Ç\Ñ)"=øƒí°¾½dHˆD5wêÿ1p‚´4sˆZ¯ÊIBÇ[pú¢‰´î[zj ì¿NËMðë»ÂUw‹G8?;9꘲ä8vÈDxä(h‡wº™ŽÁyNx¶²±¦ãxƒÇÔ%\LÚihÉÎá¦!üK²âN²ÈOiÒ…š®¿¨º‡+—« +PPkª e¸P†^së·zJ„ôºK櫦‘©ñµ†Üõ…Éq».‘»^Æ_]\¸fjw[H2úáaT:5’‰õ=0Y¶º·ß&}'º"»§Òxœ¸3W#¹é")l@9*™‹¬NbˆÜ2É–QÉ1Aö7ÀXL>aùU¬nr…µò6¸ÿ߆”²[€žkn¼—”ò·n^É»g›ÊܤIwI Éô±Ë̲‰C¨-¿¬oçÌ/ös$Øsàằƒ0ÒKŽñZÍ,@.X›dÀ(–Â%‚º:ûgpDÑ<ƒPuxšÞœr¦¨ùGŽl²í\™o({›Œ·z•‡i‘z–w % Ô´µ¿ zjøN÷ À¦7ŸV¨]fhÄ­·Eòù©œr%Äáö¤ÏîlÑ*6úhÝú®…a¹Ær}–¹oCµÉ`Køˆ00½ŒÑ1?V˵ñÑŒÓ;™“«5»tÒ›·aŽJ)ÓEÝ$µ½Ù»'“Ùo°²N´ÿ¼Á˜$c2¿ ¾HdæË–0‡ÔY­Ôs½Ñh— p.71J¼l9qɸ‹é½^ŒF-×ï!´'<žáh×·´x¬HǹÃAnò7“‚èHNã œwm‡M´¥×gÇùÂd·u§¼Ãw'h¶¬Ë(ÒsÍ,ºjˆ,Uj>¦Ö{ ”,ê WP=n»Ç*e>y£¤M!³Ök,Ü|¡ÓÆ DÈðéÖµ-lQ¨Ó ¶LY@a oxËyµn x_ZË D•LÚ”Á•ÏÝæäm4áß&ËK©ÜCêa°Ø±³ØÕÀ˜#Qܤ’™p"©ðUõ­„M·ÿƒì!ÎÃãÒ=× RΡ䦢YÀšyÀ·=§v„<6ÇM0XȬ"ì!ƒsÄÅ<Ô÷ͧö›€GÃxÍØ‚û×ñ=*êÉÅÀÊQ¯—©êr%b!^W¨\s" Í=¼ªâó OóÙ^ÿ|âíØ1ÍMfhOñ¬ÅÒó†ï9Bný^ÁÚaÉ‘9X£ÝÈ$îumåBC”d”ç­mYæa>–© ˜ ©Ÿ´ÎYÌbÎæaÞ©ñ{«&þ/ çJÞæwŽçy®ç{Îç0v¬šc`âj˜&®Ÿ-a»¢”]«0ÝçÞÓ8Ô;í“Þô:˜ÆÓ)fÒíÁèŽîé½e:4ÿp/2ip½é–-'°oÝE¥(ûF’™Ôq Å«½QfÖÑêiÍp^ E”ùéÃN¹ûX³‡ªMû â3Œ7Rvh›uñÌŽ¿‹[×!nN`Ù|­²3,x?Á8iw4Ȏͤ؂ÈÒHÔÞ»µ“˜j¡Ò‹þ1ížÒ4­†4µVÇx%·ìñ{k XR»{ߣ•ÿ>º¥‡oÄ=ÛÛ+¥ÛG“ZùÛzhU'TUEˆCm`™^Ó£|µ§Xš?¯£Üéôs+#o?èÍå»+øoܦ•.Ÿ¢ÖîåÌÝÝòÝøçÖÚ'¥º®fWÌ=ˆÏÔJ}ë7VPqhàÿe÷zóÒ—cŒÒ‹Œ@¦Œîí5H¼Iÿ.Üë_íëo™q³ö+¢2þ ]›Uì0Tæl;â7νäì¢IÍVHÁùX×<© n§ú=ƒoƒ„5¡„Aïšž‰ÎÙÁioä¸vÇ’ZFûï玔­wÌd‰÷Ÿ¤Qœ™½ØÆžw nfuÇÊ}†¨‡Ç-bÝz"~gl]¡{«‹ **‡ñËг¸r®QNþã°'ä³Êë|ب Û  䬘á¹Ç¥§gd›bÀñ¡o"£ÒÛbû·*É’Tä’­c¡¨FÛПýbÛV¸Áñi‚åX—Lì¦ñaZöÆkÿX\ñ¤ƒE•œj¥› PÞ |ÇË Á‚(—° @„îz¡ð‚ ß¹SðÐ"BîŽ38°B‰^ÌXð໓+9~¤YÓæMœ9uÞd³Ä“v;…%ZÔèQ¤I•.eÚ´©¸Š1nìÈŽÀ¸X 28uê;v"Ý +Ò"€XïÆ1¹âÖw٠ЪܰÉ^L;î;v Äe Üp#Ëw\Y¬6oY„ÇœiµîÙ¾ç²{ç Êã½S5²,§€³g™ï:eÝÚõkرeϦ];)ÔÈ”;¾ûµâ†4kí ÄñWïØt(aQ@Ï¿ÂZpyõÿÞ :·Bú@ãÈBÿž}û»ð °j>Ð|šôÉ­w¨.ywïßÜ3’Èë÷vŒ[¡XVº.®þ:ø‰3©êî;Û"”pB +´ðB 3|犴trÇœFÓP'iûpD¤Øé€Ä¢T$ªÄwÚ8JÆ•ˆ¢±Áx¡¾šJt17œ~JÇêP4òH¡Øù‰5‘tÎI( ò¦Æ†š’Å' ®¡FkÒ¦Èɪ¦¯º’Ëš®Ô)Ì(ÛtóM8ãd*–\XÎ=ŸVÐJ€4 àE!Wh,gÅìôˆN;W$È+VX ˜0sÅç|’® \x"®>yy§Ñ®ÉŸ@ ‡ÿL7- +@øÑ; ÂTÓ¸Ú°¼<¹dC€Ø@¬Ðõ­3V©bÕUaÅsÇqÈ逳4|%h×^9õSÇjã¸q(Z!(9Ãw\ra ,¨;• ÊÙ®|éêr"¨6Ô}‡Ýs•St ^^©IÁêÀ´îU@¤¯pפҀ¥0ÊpÔ鯩®´ ÚØ÷Ý®zA¯:{ÙÎ0…B^£|‰]‰bµ@VÝM~(zYÁ¬­ S^Ó:ƒaQ@L4ä+÷ȿĉȦ’îRK¡”&ŠiY[::Ç—ÆY‘—\Ðt`ÄtAºÊáE'¼¸$µ6.ç4zù7`ÿ¿2t´2_ñ4Ø.–ã©fâåŠD¶±v(¨ë¯qNLçŽ~b`…ØÖú,ŽÏzkÆ'  y£©ØŽ 1‹Õïê·4ŠÍ¦¼|º(4oªý(Ø—šu‚hP²D,–+~‘ˆz'Þ7š¨"€?y+¬¾ *‘ˆ÷»Fâts©ðæ­÷y›Ú#¤‚–HûÒ—>øEAxë£ö¾‡©{µ@|'–Õy?ŠÔ㈠[ŠÔ¤8ÕªK¥ 1|B`²TF-_ñéZÇ!`­¸å-pÍKp $€+f9§'È–µª2Y <Ö«†Õ¼r‚²R leŠÿwoÉ+bF—1…M2 àX3&¾Ã‡H^6³“e¦F_ñDžpˆf/ ôÆbÔ<É´Ï2AÌÙ»3&…$q‰èkÐLÔøÃË ¡ âÛèTšÓ@D#ãÐÿü—”,ºJHôv‘ŠI|ˆlÏ2Ö<Åìl¨¹HІöŽ¢a®+žòEsd’´Ã4 X׊¼¨±•%’‘©kËRwÊ& Ç*ù1°ò^Í;/\`4(ê=S$ÈB²¬óä—ÁDH|µ+¤R=ùÀw4w@á P¸áÒƒ•å43Ù}Ø•iѧ÷ñ p,R¿a–n™`ÿLMæ‰Ì¸É/)ãd”-Ôõ±?íCÖÍ{8òÊø–g8Z’®?/9ÝTXéG !Æ ˆ\ãØ˜W(@S.XÛCO˜¹Žn7 uïv‰Ó±§– 5‰â¸&¹:T§F1‹+ µS Žh{1- óÖiËqv„m}ÜJˆÚ >ªN“G=MìFÉÞ½DdI=jƒÚ·Ô=U1£jò‚zV´¦UNìx€˜`²Ÿ 䉰˜žƒhzTX¾“d¿ë%÷(Pá‰q—¥ZPšÆ`%¯\¤Š?É)š.>•™$DÆ*1^…Œjålg=[¡ôP;Œ4¥ÙÕkÖ³œ½BgÿDýYUТ€OuTЧ$s?·âO’I'Ö®¤}íóêB{N‹ð"¶ º¬¾xdWü¸ó³Ó¥nu¯ÖÓŸ’ »¬Ù®u½û]ðRw„@9Úx3Èó†W½ëeo{Ýû^øÆW¾ó¥o}í{_üæW¿ûåo…v$^&å¿BqšP˜ö%!7 ƒûû`—kÀ6Q“€";¡Àîva3àM&\¦‡XÄn²!o{ÂO HTåTÖ‰ïÖ)šÀÂW¾gžð ¸B; Ô¡yQBÒªÄ8t±®xå«©”P#°Ú£©Ym T+¶ð„'0jGÙq—½\! áò“àr˜Óÿ¥Kví¬&ä¨\–ƒ€“ Có ÝõI²†™T¹,¥^"3”=QeýŠXØÈ\ ‹,L#„>Ì—ýhÚL%qõœ÷Ö7­rNrñGÐŽø¢xª_çÀV:àÔqx«)¥sÚT–Ü´q•ÛBÞ7³ŒFo.à›-§rk¹AZØÃfÒøÌúìíï .eWUkTmHÎWÝ gOÕÚåÛk¶1âÒõ-êyK6ñ¸'n•[Ýë.ÊmǨôX‹Q"ÑˆÕ $ðÕä#Æ#Â1g¸Õ[Í<¢‹ x°ÞÈD±ìeÑòFªHšÈ›ó²[ãWmtƒ“Íÿmvs÷<ñyÊä‹”ƒe¯àjɹ3šaÖ¶«÷sƒâiO`nÌ™Î}‡6¹¹¼Ñ\Ç8$À€|ˆÌq¤'6'RzÓþt¨G]êS§zÕ­~u¬g]ëzÀ€®GEWú¯ƒc£‘ÛèpXšÑÚ·þöqáÆí"{N&¼­høEs¿I§g‡#¸^Nr×–‰¼‘F=*‡Z ©bh¬²Xeq¡t^!-gE¹ÈIüO8¶Í,€,™a’s¥eÅySCæ1š4dJ‡É1ªá !$xÜ;‰ð·Ü3žmñ:Ñ0¢œå*]3Ì̆LCr¦‚çwü¾]ð*[ðÙ ö˜ÿLúÕ3ûŒ¾Oºr‹M˜Çöœ{ôi÷’¶©©Íl‘ÈÚs~{êm->‚jÙ ÚÝOY¸ ¶°YJ’r‡íAŒHr5r@ǹ«I“¿W«œÿS)fSVƒ ÅI?ÔÝÛ6šH‰ŠÈ6ú£(®7ü‰ü…Ÿ*kC·¨8&:¶”)‡È6Ô6c³‰o»6û+¤Z¨ Y?ïy7¼ˆ8À"xË"r€âkŸ‡+,¿È¿ùi¹ ,3QB¾P‹²aB¬`+·B˜… ´Äº8ƒË,-ü³Á "ûX¬0B"œCÛøÀ…¢9ð8˜ -å(âò¥œ!›™"ìṘËCñ <ÞêC= X{=ß‚=4šH-èj§þ1½ÜâŽÛ:®¨ÀDr¢CR´¯"²°³ê®R\ÅýR’MQŠ»—ôbEZ¬E[¼E\ÌE]ÜE^ìE¬£šyá;‹0»Øéñ»ä:F›H0_lÆùÊs ØÈ;1Ù0„`k± sFnìFoüFp GqGr,Gs21=====5B58F8;J;6CC9GG¥^ÄS À³T€]˶­Û·pãÊE*Vf€:WÌÝË·¯ß¿€Ó¬[O:5ç°ƒ§† <-&žÁöíÈ]”pÆ)§“S>~sæ©çžOÖéT¥ñ)è „~çgC…&ªè¢QÊ£F*ixŽ.4S€±Ó 5ê“§›VF”Y<àÂy ÅiwàѪaŸÚÿÔê«J¥ Ô¬ZÙ:Óf<€&X¸þ4F©,ì¤3Uª–-d§“§ºæDa¬u×z06­µÞùj´Úä-Sx5nVåÊj‡tlÛà|d¹Šì`Ê"´ëL»î¥ØÀÄ1Á£‘Ù ~Æj™Ñ¨ÙdÁ ¼‚ CÀ0i A€æˆ×ÎÄ‚¦¥ K Ϊ©wD6A×ô/tÙÅ<Á̦¡ö²a5<üY "Ït1e†]–ÙfÍär’9A€}÷ÝpæL\õÕRS“Ãd*h›|ÂV%ÕèF¬1鎴Ê!|gHERË‘’ôÕ´B´¥@LtÄÓè('…u-,N"«ºšj1wX¦n"É*¤ Þj®5† ´ ÙaÕ¬Z$2<Àc £‰^nP€‰ÿ)‡+`Á—‚óúÄóµZc­v¸€ÛœJvcð ¤Ö76©3ŽjmRÚÓfÇ ÐÎiJÕ¦ló]Ò­ ’ÚWCªÉ¹?)“dФ°hM½¾ºÉ~ÑæÚ'f¹Ý­‰¶:¦ œw‹Tےɼ4^þT½EÃÏq2ßM¶7¸Å|.f‡ôúdÁø/\`–Ç0˜¾Œí@ 4"0Ћsw  ‚Òõ“‹ÇÅ“±@ d&Ø~´EàºñŠdL ûø@,¾IÜÙ+CNçW’ü&oÅÉ8¤ ”›2e¶T9'P6ò‘¹å+SùÉoér^¸ / ¹'8Ä«=%‡ŸdâáN8Æ-^ñzkÚãÉÂÅ}ð’'üä(ÿg8¾ùÍ锋˜åýÖøÀÍ óOdä879É]^s”püP4¯tÏG­ò™ïÜæ„É9Ïu®tKO!¤ú¹s}ô™Ké“ÖyÕ›ÞôJ?½•?é×s}õ‡—ëµ^ºÚ‹ÞuOáìÄv:ÐáÞ’°£½îZïºÂó~é·ƒ–î;Ù±ø»§šïk?zÛe,x~#G7Úoø¢oñlÇ|š#Ÿ/ÎËÐó´ñ;á+¯ïÄs]óŒO½Û%z¤ƒžô>×üé¯z±o¾R¯wvîaíË›þ÷^ñîYû»Ÿ÷–=æÚãÃÈùS‚>L¤/wä[ïËG}ó…ûâ£úÖ'¶ÿ¿±Ÿræ·üuAè•¥þÚ‡ÿâùö½ånþÏsßý‡jÿö߯nŠËÿÿ{G{UÇ~û‡~¢¶Çðçä—y´gwÑwè}®§€ Hq ¨x²h«G€ßg‡6o˜}k—q x~˜‚(‚¶róg‚1(n(H|xƒ6ˆuè‚ qÊW~ˆG‚è9;EˆGH„<¸m÷|ƒôÆ‚Jh€ùg|Kø—…Zèƒ9è8„!—„Wøq>˜¡Ö„ˆƒ*è…-8†Û6ƒp8€ÈÆ…i˜€_†'†nxx%(€±qGy¡‡HH…]†{˜|}‡˜††2ÿ—~v¨†xøm„˜ˆ*—¤ušXƒ.÷•hŸx¡H£ø|–Ør‹øÈ؉.QŠ"‹#!‹ A‹„a‹.Hh˜§lR‹Œ!Œ‚§Hk©8r«è=ø‹’ØywoÄȺøƒ–·Œ­ø‡Î‰kÈyxŒÈ¸‹å&„ȶlØÞ(àènqX…åh„éÑH‰ëÈÕè„r8‡0hò8“Hmx°ÖŽÚÇý¨u-1Áá‘ Iq—Œ Ùƒ§Ó‡ˆŽ6'‘¼—銋†qé‰yŽ)©ŽYj#9[˜‘|øŠÙ‘*É’-ÿsIÙ芅’”!”A”¢˜“¹“7“?¹’YÞf”†'’©(oL¹‘7)…Ï•H©“fÈ‹[¸…è”9…Ù•¸–ŒL¹–by”YY–fù‘hItUÉ“aÙ– H–6Y“½&•wG•Êh—Zx)á— a˜‡ÙwÆ8—iéŽã˜…$é–qù–zÙlˆ‰t€y‘KÙ‹äxt§ ª@ @ á~@~àq™ Áš áš›!›ß–™·–l)qq @ð´ð² ?à@@ Q}0Œ°´Ð}Q š²àQЩ Ç©šA›ƒ¸•|ÉjÜém¶ÿ‰g’4H § }ÀÀ)ªÉ`šÁМQ¨ |ÐR@œªÛY™uX}rɘ7Ljœ&“Õ&´: îI ðéÉ9PŸRp ªÐQ ¡Ì)¡§@ Zá)%:'úš:h ªŠ Ú„é–ž´àœ?@0 ± |0²|RNÀœ²0œNp MÐ$ ÝI™J ˆÈ¢ò† / gW¢)J [ª]:”TZ¥[‡›dšq'º¥_ºš‹¦ÂF˜#ˆ¥,¡¥Pé–é¦å§Ùx‹PZ'kʦ+ЧoÚ§-GŽe—¦vúàÿI¨…Zv›ÈŠ+A§Œú§¼¨BG†eº©—¨–jŠ—Ú¦Žºh‰Fƒ­è©“©•Rú£šv>gžWG©º¨ÚªÍXh ª²šªŠy–¶:“¯Š†±ú©ä©a¬¬­v}08¬OÉ««Š“¿ê§ÖR·«ÏZ«:­ÔZ­y­Äº~º7¨Ü ¬ý׋‘‰¦ÐZ…\é«åJwi¨·®QÚ—äZ®é§Ù8tØ —Áç®Ü ¯œ(¯Å˜­þÚ}ÛŠ¯zŠ…æ™¯³I¯(­¬ª° ˰tX±ój°{¹jÊš˜ë°îv±Œ±íš°+u"{¦$«±õz§°çú­,Û²ÿË {¬÷š²¸Š¨àê²€:¥(«°WǧsZ²8+´Jk}œ ™Ý¬Ìx´ªÊ®»´È'ŽO袲Q+µ7+®U¶WkòÇ|g7°…‰´`+¶öHdKy¶]««@k²l{²á‡µè‡*Û°y9µ/‹·wKzz€VZ´W¡%žýÚ¶V;¶ú·|Û³ IŠu ªC+¸Lû¶L·Þj´%Ù´ðv¢[ºõFº¦›ºã†º¹®û~…« k±óŠk¹€k·iéº+»œ „“û¹hë§¹‹¹ ÄB|šò ÔvY½ãŒ’ÈÏêÑϦl:}«±ZÌÑÉ ÐG²pÔÑÙÅîÉ.¬Ÿa,È3šœpšK‰Õ mŽìÈÏçlÍñ–Óñ+¯vWN`Ъ¤€ ŠœŽ°Ç’}¤Np»ùǜ狎ڙÎsÍébáé“îÛŠøáÉÞOÞè~øèl>ÅZë”NÓŠ~æŒ^Ž‚>j§Ð qÀU­×ÿ˜Ï Þ¢¼‘Ï]êÀn¥žfo¦Ò’}孽ыà#ìØ©³Ð:à:ÐÔ™¶­.ã½÷êÉNêÜÎìš.ìG¡n-ÔAÔƒ|šV=ÈV  :@ïÖ^ï®Žìø­ì²nêóKž$ð!zÖANÕ©‰×>Ÿ§Y´°ð6­ç¯ÞK>ê¾ï¤ ΦˆÖ¦cÿì!M¨ ÈX~מå*òùIòÛl¼ qïç±>ñÝN¿¡¯CÚÄ>æ'ò2|ã+àIÏoŠfë3<–ƾë‰âúnõü>»5½ÓÈ­ä^õRí–^õ">ö#Ç©þôiOã·ö{ôuNñ퀈¼uo÷dŽïöoßòǾsGîÉo½Ñ}å.ÄÐ9ÙŒ¯á>àBÿoxßëèî]ôºqûÙŸyŸ­Ô|Àï¹ú´šèžŸ¥ïÔA¯ë&ÏùÙÞŸú¦.úâ=nF>€Ö$ÜúO Ãüì6ɵ^¦‘üü¸ýOøl¯òÉíݽ¯Þ ÿWo³àå©°Dº‹ý 8/ä þEªÈ_žù’.øñhý¡ë†øÚ(ºOKëá,Ÿ}ûþÿÿAKà@‚D˜PáA… 2tQ¢À)-^ĘqbE=~Ô±¡H´HšL(ÀJ–+K¾|ÉæÌ„2iÞ$h3ãIªðV>ÝtóÕ/¯kÄO¿ý‰ªÿº¢Œ»ø.·ÝšÈ@¼à’% >B˜e¸âˆk„Ád@«üë¿üD4 Ä­œˆÅÎ(ÛÎ)™0ZÆëãóháãðb1®¸ã!N1îŽlj $—dI%›œê2I„.0S¼ò­£²[Ð  Ž;:¥ >2%øp“ QËR:-ªîÎ.« 1Ë›TÄJÏ­íK?ÿñ<ÔN)§d”J+m Ò=‰ô"ÄbDJÒ‹ ¦Jí*Ñ0>±Ô”4I?¬K1½­T‰8ýÓÓm´Ö¡`EWJ äÒ Ð2uõU?óœÕÖCí4Øt UËZ•UeóY±FEÐÚH§íˆYœœÕŽÐg½t‰[³ª•µOZSÃöVs,ÜIUeRû€ˆe& ƒ¬ËÛ’ð“—¢¾ÒÕöÚw©‹—Wë–<ˆhù¡ÍĤ¥ú€˜E–,”ʼn(še ôÖ‹>ùè³oQ X²×56Öƒ7MxË…k3è)ú`$b)᱑FŽëÃ>ˆ&¤pi - aè ÿ+ö,Ab™Ôv%XÝ™©­yWBqHgðrû9h"ÉëÐi>A¨Gð9.ȵ…òÈ'óŽRæ‚—ýöoìýZذ›õ¬)‚Ìv‰£¢bâþh$ ¢èCc'œHM53@åÍ8çTÅ Âa6µëm_Ž™k¢®n vÁE…–ܸ /DpuqÕ[‡Ivä\m¹e-—Â}Ä­g}õåwjÞÝäÁÖ´øìúžÞåç» <ÙîQÏVë앯^áÚm/w|íg§9úÔ··Ô}¿ÕωxóUÅ;ýìUDPga­ªŒ·>ú DxàûÝS–T@þÍ}ˆ]'½Ð~6; ’z“2ÿá)X tÞ AàAït<` Ã׬#§<´èØÇÚ ÙMNÐA<–¨ï½O„"!»~˜‘>æ‚Ad¡SÍ‚GKkš,¤ …¨ ÉCQšÐ%?îù.xU  VA®‡”’ŽþÀ#¸%ÍG9 ÛÔ¤€ ZhÑ"|ã[ýΈ5$2ƒ*Œß éWD!zÑ@ȻфH±‰q.MkŠãÚæXG‰ÄcÜ£û8ÁBöŽŒ<Õý2x»ñA„S• &7)›´e€„¼O°¬·"RfÏ”«$âùFŠf\ž$#,igÈCú²”$&Wt©L^&ð!Àš0Q(KQÿÓ%ø:f3 ¯ev±™LIß^ùIPBj–´\ÉÃ" z¨7Ìác²P–sˆäK&7ÕK%‹Ôìæ(yöƒXH‘гèP{²øD¦9íP I忾©Éj³Edß4˜Ï ²'¹‘[d!I:jñmŒè.G‰Gì¥/…iL¥©Q3бYìiœn> ’RRsœ«gJ™™«óÓH2’—0ûP\N‹I[ÂÕ” Ä}:s$ýìƒÉTŽ’1`‰ªDc§Ø…±•¤¼þ¾øÉ©²²©ôëß*ÖŠÚE®Èë]µ¹"~%áRå •uNrFó_rElX7•X½æ5±ÿ½Z¡DüºÅt‹±—mìÁ0;غzõO˜=%u@;UЊ˨´¡MVà  Ñ"¶p­uíC;»•}}§.qm­Ms[Xæ–¯V•¬þ¾ÒªÀ6y¼5çl‡22ö¸ÇdECYQƒg\øå„º¹îcºÝ¦êªNj-wd5ßÊ6 žýliukÝÑ"§¼u+ ï”öæ•ïÍa ûׯ¨w½¬/fðûL‹F¶“ÓÝ,^ÔJß'÷¼ü_ƒÿ àË:–±,p† |Ø of.6ï[Õá ‹˜½µplƒ+߈ðVŽÌ‚Åc›Ø–2ÎJ‰{âJ"êø'¨ô±dOÛ¢ ‹ŘùÿŽ)¬âzÚxzÐäðr&Ë_<y-Ä]–•…<ä-ßÓ4ZŽqH°¼[0ûI¿…Å^Žíe*7³WžòbßÜbA™ÕË_žófœSËsu›^EdŽõhç0¹9i–³ ¹Üâ"Y@{F´¡›ÜU+Ž{ƒî§hÛb¨*{¸ñ¥ï», `ýˆWÉÏy²©U½jV·JNFõ§U*"S»T¦Í1E®u½k^÷Ú×*15ñ\=lb›Õ 1v²•½lf«Ú?¾†v´w­iWû×ÂnE¶µ½mms§Û°w¸Å=nr{%ÜFw¶½­îr·ÛÝàfIºÓ½n¼ÛÞäÆ1¬½o]KEÞèÀ¿ÿ»-pn\à'x¾m„/œàQiø¿×ÝŠzßÛâc ¿ùMm[{€,TxS nñ‹G\Þ"g8Éïï3%å*·w¾;îq¸,ÜäwøÍîpž?¼çgË—2r˜»ã!™yµ9žthü[:/¸Ræ]t£CáR8ÕÛÍò„èZÇ·OôÍô¦×<ä]ϹϾö³}ÞAï:Ö¿öp}'d/û‚ð~íxY½ÛH‘8ÝÍíwuþä‚7×nø¬#¾îbß{¯ý}vµS^ânw;áyq¡+~î‚·{Z"Ïë¥ÞNßtçîxxk¾%«g½ç%.û—Çò¦ÏõäÑ^ÿùÝ_ók×|Îá¾øsœõ°½¥pŸûÐ,ÿôØŽ{È_üÅKßöÁ§~à§{Üëž÷'OûÁüñ3|øwýöíüÒõ ,ÿ·mv_Ÿþ?>ò¹ozï‹_à P轓³‚³„ܶU@8K€VHh mKJ(¸ûã<ú«8ÇK¾;b¿æ[¾÷óŠà³¹íÓ@õKÁí[ˆ ›#80A¿€@@\À|@¨„V„Ì6ÀÀ¶‹¿ ,Bõ{¶Á#d½0Aës<(ŒÂ®Ë?™SÂT¸ 8ä6 ‚È€BȶBÈ€ Ih…ÿ@°€ 0Kh@°€¨Á2Ü€ Ø€HˆÃ9,Ô6Ô6l!T78ÄÜ6.äÀó¿D<*LB&Ô»,¬ž*„=4¹´ ä9TÁtKÈ6VB+dÁ”pÁY¸R”7”@P72„„ hKH5€U „èA+ÈJ08¬CK¸€KhIh€]„Ã>ä6@Ì6AäÁ8Tm‹F",?#¼F$ĵå€%ä¿&ÌÆùã6ÀÀìDG½ìÛ¶$CVÀ¾üƒÂ¤Dƒ€Áÿ‹8ƒ[…ˆÃP·}̶K@àGгA@H@ÈÈ‚DÆfÔÁAT€€GжÿÂ[DÅXAL\¿y;ÜëÆ¾»¿8>(D7B¤8C,„;ÜHhØ€ ÐmƒBX(È Ð À€p˜£½AÌZtÇlcC74D0ÃB¨·DȀȀM ;,”DU´¼VŒ8 \…ðGŠ‹ÃøGP·‚`€vÌA,I“CI eÅ%V8{å/õDQÍ„1……ÿ PCÓl˃ °…ÓVØÊ¦Ä€‡Í(…<ÿ;Õ5QÈ€Q(P°€Ü#Ö<øS…SX‚U+ìƒVjÍÀiÔAT„V€®l*˜IÂäH¿½DMEÁäΡl…¢|ÉI t‚;”„ÈpK¦Ä€ˆØtm\‹\3€X€ÿ@ƒ±e:žGs2¸°„Ú+Øû‚ 5¸Y̶, ƒ€€M‹›…°«\’ÛÞpëLÔºï¼ÔÚ\ÐÐõU·-ÛÓ ÙQ Y (…·­Í¸Ý¶ÛŒÖØÝ“\;YØ6ζ¼ÔHñ¥:ò_¾m±\Q]^'*W0€Þý]ÒM:k}(ƒXÈ‚€ÓR.Õ×lƒº”Q!¸RBÕº(uBŽìÞžØÞ]ú…ƒ €P.]ÒUƒø€%’]"¸Ý_þÅ_Úíß•­ÝŽ?½E¼<Ô±uà~Ë5 ±E] ’ ]ØUjkài#;`ÿŸmT8„„¸AèÔfN–^¬¸¨µÕ.º d;Ö:Ùüܶ6-N`>"¾ÐgíS#Nb$N8–8Ûý9>޹p…Äm €Þ_%]Œ-…%HYS@ƒmƒ(är¥X]#]+Æb-6Vp‚-0 AOáàÆ,X†Í|…cH€E p€2•X<>ÕËm¼•G¦[‚°€Ùç3dEƹDž·EÎf‚sd câòsâeæK(îÛ¬ÅÕÓ˵4øZ ä´Íµ=X]%hj«àÞõÕ±ãdOå‘e‚¶-å²½eÿ`æÏ{7æ^va†ÆÄg&»ÿÏí7==â…U8ýUbnfä@ gõôÞÄ ´Ÿ`àj†à<±UBNݵm[S…6°åæÛÜ]#]vwF[Õ݃¶ …znR(èCSf•cáplèÞ[ˆ^Ôû8ŒÎèŠVD¶èò#`#¥¿q6Óî Ng}sȵ˜P–ß%h[Q[ >c};ßÜ3“Fi•V]Öui˜–€ ¾²¡&9Tͼv=èÚ{hTÄàÈsÝmŽÁ¹}]©ÞhÌÓàíÐërµ­Fgš.]x˜±6…þá ~ m1Ng²ýê°ëøå³Nëe“Pîufîænm£¦\‡žmm±+XSxƒuoŠvRÌ‹WH:„£>®Óã§Å ýqKrŠ#n¶Dò'g…WáW r*‡lä¾;œ9§fÉkÒA]Oóökïþîÿ…¨ñ\…³ÝñkÞcTìÅμ&Ï9ËE´,WR¦“h/oqÞÛû3î>æk¶;o‰­Ó÷Ý»÷Žd7sðfòŽvR:g³ŒÓòáÝà‘ sŸqF—ä<¦ñ0ô5gêAUtÅÖt9?A@¯s;'W§t3Ótí½7Ø~tû»jxœØO';Dï9ðfôÞvYGsT¯8s^àIßíJ—ñBõs•ï~uX/sT^^u©.u`ïk@éq]jV?vW'óq#_Yö£¦jÄ÷ýëvÞ.Âj×èk·j×¶Un'ìó¬÷=¯VpÄ.P¹Fàw^öetx…«… seóµsjò[t 4õÿ-Ä]‘6öU'ÞT.p…&H‚&ø„DÈ-WðH‚À„$PÍ|¼Ä•·¸}¼g§Sißmjoâ7D‡Çö¼xɾóVϯgoy(8-@‚DH„-€….P„-ÀXHL¸øÁã¹on«Õ‹w$ØxOðx…‘/ù“Ï—'ï@O7«?I}7{{ƒùDð‚ÇSê{õÌswn†÷ð4à–W9˜ßv¥{o§xdG·BÀ‚{„Ã_{X¸x(0„pƒ{XðDpz¨—ú¶çëÄG|Å'º¡?(8ú¤¯Ì¤ú$H¾ßvyÓ|Õç|G®8­çú&ðúy±7ù$Èÿ.ðø$ÐW9RP‚°€ È]c Ðñ}³Vð^øDoø«~YÃß|Ìmù­ïú¯÷°'ùÛ/û¿ßF´÷CÇBø"(„'0,¨'x*hF8*‚ôo<­'y$؄ȟüNP$LˆN)¶À* €V [âñ„ÐX,9y…!‰*@N\Y¸0@AE\ 2˜È ,/‰¶`‚•&. "L¸°áÈ+^,”qH,(SË(H­„ÕK—‰bÂjÊEBZZ·j`*Æ R¥òÐcÊÔPgײm{WZ6‡Ò­[w®Ý¼uèíë7/ß¿‚áÿÆ•‹ç '&®XJRÄ F&„P0¤J™ä²²e’©2gÖ,lúôV¯nW³vK¸këØ­ ®mû6îÚ=f)©Õ Ž«ŠPɰj•V„:Òµ<óÕÌèP’‰np÷ÂÞX-ou©H Ç“wo]¤+Il:™2‘'IFdHÐ`ì µsïø=üxÀ17H°ˆD’I(yqHT/Qå^ˆde{@ÊZhÌ`Š| „llÑÆ^‚åU¢‰w¥¸â‰,êõ‰xeAÄo…øGœqÈ)7 GEÁ‚—)ø™K0vÝ„¹¦ZˆMš#“NÊ£’UZ‰›~ M‚Eÿ6¶RE!­˜pÉ ByI—Ö©¹&’CMrE—)¦%e*GÙPi²YÐ+ÓU§fMnÂéåœ&Ôi& ù(Ò'= 'î©ß|õ „,_HHejk€Àœ%A(R>Š.*˜ªy¶Újª­Žˆj‰”\A„—V{-¶´zHCR‰ U@"ÚŠ%'TÑÜžíF—e Ý~k‚ã–{nºëæé.¿Á+/¸õ’k®–Ћf¿}RǦ³$®ñ¨ŒZª”ÓÊõª«/ĪÆ&r¬1§†m+/%áBÂCÿ)T¾÷ë皥eKmµ­AYól3ë|e– ÷ënÏø3ÃA¿ª'Ñ€†,(`x–†¥˜Bª©TÆêâÇGw¼âÕXSØ5‹H'MZÃÏF‰³ˆ$¢½ÚÒ;»]˜Ñ­=ö»`sM·Òv§87ÞH.i a™B–YSSì¤Åz¶u+Y3Øã!Ç­ªs}#Ù¶’g¯}*´›§ý6è¨MŽ5tîõ=zØ–Ï”Ÿâƒ©Nöߤ(AÀ±ÕÏB"㎫Ú:ãÀûeqWÂ÷%6Þ2g«ùÚÓ26æ¡»úÝU¥„®ÐM}Š>šž<÷&"þßž¯•¸‹²¨¸µï7ùב¿^ÿë:?óÍæŸE¼ô¡‡ïzU:£ˆíO/JÄõ²GÀžh~Í*ŸþÐÇ>Á¨o!̘ûêrAÅ]°¼ ÿþ÷ç|oöÓßþÔ†Âèõof"žœ‚ºåG~„ÅUø8¾Y®l°Y!ªzÇŠ RÐ#4"+?¸1Ð.ÞS86åaë~5Àî~Árñ…Çs _ä@ÖÙp~>¤Ùy·µ&6gˆ|ѵ<0ž±JV¬–óPh ,r±al ÙH‚Oû¢ã’FÁ$Qà,X€"à†³$àp«ácjz¼7/Ž1z\åTGÅkÝ1ZyD!ÿúh?Cz$”=ÿd¥¢èK…’|"Å"¹GQ” ØŸ%Ý"ÁŽ Àk'[1KeÆoŽó«Ÿ õ˜¿4ªttâ—9¾±‘Ñ™Ï\Z)M5L ±9ÈÄØ9WBmÊò„¨T¡þXXM+]³.µœâ2ï™´@yóug §”¬¦É5Þ!ét‘6›É¾vF•YD£ùR9OR.SÐñ';YQú ‚ "ûŒ‰Î‚Öä Ýódj2ŠQyÚ ˆƒgD:QªTu5dœ>‰ÖÍ5r4?õœ@ ÒæÔ¤¤eŸB‰™M–êF§¤æLi J:‚ò¢¢¬ç 9úO§‚tEÜØ&ÑTÔ¢š´“ÁKªÿÏ4êÎ>Ô¥M’hTí8Ö’@u­«]ózWäÕ®s­ ëò#X@°…%ì\€¼.¶®@~ «×¿U±ˆ=la1KYÁ2¶³Žõ¬_ǪXÃ’–°š…&ܤÙÕcŠ4/_Ý I)[V'qx_“í^?«[¾.6´•,B,+Ü¢®r®zí+_“«\åBé¯ÇÅër£ËW¦ÆUt”½.v³«Ýír·»Þý.xÛ]Ñq5F‰=no%kPæ’u¯È-©t§ÛÞîÆ·¾x½+]û ]øÚWrâýïwgº»Ó‚À¾– ¬àW·Áº0q,áy~÷¹Ë…0wû¨Ý s¸Ãþ0ˆ)ÿâK˜Á$–+eO¬â³¸Å¦®‹c,ã·¥xÆ6¾1Žs 7ó¸Çf«ƒ,ä!;ÆD>2‹#Œä%3¹É¤t2”g\Ç(S¹ÊH6²•³¬å-s¹Ëôô2˜Ã,æ1Ëd>3šÓ¬æ›yÍn~3œã<=9Ó¹Îv¾sñ¬ç=óÏmî3 -h"ÿyІ>4¢“œèE3ºÑ6.´£#-éIï Ò”¾4¦3½$MsºÓž¶ã§C-êQ[zÔ¦>uKêU³ͪn5¬c½åW˺ֶf2­o­ë]ó8×¼þ5°[ìë`»Ø¶±“­ì™"{ÙÎ~¶ÿ -íi¸ÙÔ¾6¶u“íÿms[•Öî6¸§ýíp“{Ùã.7º‰}ît³{×ën7¼eýîxÓ{Õó®7¾E}ï|ó›Óûî7À)ýÑ/8Â}ð„3Ð o8Äýñ‰ß:zS¦8ÆmñQf¼ão)=.òEo|®#?ù KþW”³|Ï*¯qËcg _\æ6ÏMÀ€ç®¡[DBÀ)pÅçLÄîmŒ^  }+F'úÍá-€XXIén A*â2-«§æ¶AE¶°®õ¤GÞ PE\~ !‹A2€ ð!pDa€žP]NøAðN GÀ=`çŠÞùî„ [‘Hƒ®ðÿ! À;áƒöY¨=M0ŒÉiá÷@ðjg{ ãwü¡óŸG|ÜùN‹(0Þñ©‘ÜñÞۧ—üà ¿yןæôm½t°ˆàk¾ì­,À€ÉÓBZq‚h‹³kEXþÛã²}ª÷ArÁ;*„‹ X¿÷\?-ÊO‹  D$dÁ~Z¸Ÿ?ÐÊî„^W4-¨ìŽõÑöiňú¾_ŸùÍ_ý¥ÆÐ*$€ö1Àõõ\hÅúµŸÐ*˜i$à: îŸû9ߪqWô @|Þ"ü_j "žÊmÆ,pà"0@ æÿ€ù8Ò‚=Ò>Î <²ã1ššÎíì£ä€Áë5BÜÉ6ö âYÀÞíãIjEæY€@¢X€ìäìY@Ó !¾dL’äÚeóÁdÑEÁÚáç¤HrâˆdÜ`JÒ$ÕY Öeàm…4ÝM OJdÐY@ö…€ààG†ä"NËVf$Zâ×Ud •Ì ¤¥\¶ÐÃißÞÍ%^æ¥^î%_vÔµÐ_šF,” ô &'rÑ_®%ñŒH`öåµ­å•À•a&aFdÿÌŠbÆA:ÿæ§^é1`ò-Bæ!煀繤ð!àáÝïQŸV$^T®Þò9ÝßU%lÒBkÆÅX DA ŽæAþÝg2Ž^U #oúf'Þífo&ì­¤j§Vä&TÞå¡fó‘î'\ä&gj `àò_ žàûú夸Y£üÉ¢Ê{Âß|rÊöYŸo²'aŽçê žŸª_ð'a6à{V£õItŠ |šŸ{(ª¤þÙå€Ê£ö`†g§½îbä@ â!2¢JÞêäa¾&&n¢a,¶¨‹jEïM!‰v!1ÞV¡Hb2¢¾ aèÿä"¨â Â⋲(:–á6"ʈ‚êy¨¿™"ØÕ¡1 +ãn#1Ö¨4²b“Ê苆ék"©ð"‰`)›r⓪©Rò &©˜JbKºá*>é–ê©-v¨•f dBè:2$?&äBî)‡Æ…?’#Œžc>jyÆŸ\äá ª ¦2ªîNü§“žÝâæƒ 5b#¨6)¨~ª†¦BVi bšåÝæVú¤Wj§P2©ob%#Τì‘)>©Jk°B" ü@ü&Ü ¥èáªàmež.B³>ëœén6Þ+¦äJÎBžnŲ2éY:µrâ²Öê¤A&¡©ÿRñÁë±kºÕeƒ¹«•Pe$ ½ö«¿þ+À¬À¬£1±ºª¦jbÞFcš¾J*j0¦ŽÉ ÁššØa$…ˆ_ªb&Ù}ÝòxìÁÊX!Vì¨Ñ^ãÑ"pr¢s*çãEÞä]§å™ÞÚµx ñÆÊ&hmÊ,êMbiž¦ýµæÏÎ+h’žàÉfÊ­vçÎjÅÛÅ]Ö¥¬Ã¢sÆÅÓf@Ö߀ŸÔ’,µ!mø'îçMVhW\hõ]_öqŠzN"ƒ¢jW¼g÷©m¶m“†Ý¦^* Šmۊ턦7Öß,ˆ­ip_à ëönÚ*`ö©§áz­œµ!\@© Ò†¢ÿÂÆkòáZjê‘Ú(©iö¡Û†®ÙŽꡞ†èkŽh‰ÎBëî”®(%ê`ìæ¨ F!2©˜n…ç~^íâèìJîäÖæöi媢*¤êk´d0cÈøâ(bî&è#Nï" a—ÞíVX/ª`é*¾)—þ¢ù¶d#z©mp߸ )W8" £*Z¯ù"oœQ.7ª#^ê¨>!¨NdEFCŠë,ÀÅÛ:(œr —e;:¤”rbc’@vj¡îo±Sn%°â¤G‡å/û¶¹v±= «¤V¶L cü€” ¯¥oÆåV´á"$kT®õ°BeNhOkƒim¯¶šµåÛÀñ•À¥Î·xÓÛú µýÌX{¯·|Ï7}×·}ß7^Nfè²Zúnuæ]Ð…,êw\1ã7¦xyrÇrÅfÆÅÅNvG'¬ƒ‰y#x²' -ËÝ-'`(3b1#Ÿò!çsJ3)o…Öžru>3&¿2ªt§à}§/Ï^í¡lj®bβ8€KgÕJ³ŠÏÿìî˜l¬²Ô¦8†™Ýz³\Ôó=ç3>4~Jh}’m†s9Ç…7ç-C»3–cà†r"§Ì°áÂ…•[—K ¨à>tã. Tê#–³`’#™MŸîL‹uLR´’2!1fîšµaÏ 6ÁC,Q¯ôëRi!믟cï7Z@¢Câ’R"¡:êJefº½Öyèˆ/Tr÷ù]G"\Tµó®iŸT‹¯žÚµèLw–Þ´ò²¯ýn– ¬_b÷¥ºRŸTÇùk¶:¨#™6a”ºö½pD>êÿ½†ÿfª³¯so6Ä¢6ÿÆê¬>zý™yjoæ´o6DRãªfã#òµD2ÿ£*:!BV»${=w™ñ*2{PÖ0,Z7™Þ°¶ž%ñ qH6nÇÅw÷`mßêmÈKå*žå.f«üoËd¸æ© |RJå²¼¼Gz¼È<É—¼ÉŸ|Žñ7Ê{-%¯<_zr*¿f1÷2ðAs&o'’»¼Í19›èšK´—g(ë|ÔetᥗªJb¡½Ì »ú¾ècã:ƒ;}ËöÑ«ê8š&iû`¿Æ[=ËiülOcuk÷²b!ÛÆs1 dª¼ôHl!ß="ÂÚF„7øãþLÿ^äWIËKσ«6ã7þ·ÊÅ‹90Ó¢l-Í»7Èx,»_n†¸Íšæl ç̓áÕf\Œ>a'·r4‡¸g lž2×¼ÌKgÌw>‹9¾…^3†Êòtßz–·mÍã’³<—¡›k…Ýè,(z.nÊ¢#úgšWsÐ;¿Þ†ê”Û- Nâ”Ç-…"ÿŠ)¿Òûy-~û&Ú´ä|!*´t4@Тµˆ 9,̸–*N)¤`aT‹$aáφfP%"AƒS=ŒøQÀBŠ´f%Xð`B†/iY\ä‘–,!J|Ù’ä@ž>‹Â”ù‘iS§O¡F•:•ÿjU«W±fÕº•kW¯_Á†}z*C¢1êp*R Q¢g1jRöíÍ…P¡"ñˆK‹ÔF;—¤D¼€îdr$­–ˆ.k·-\™›0|ñhKÉ•AVVÜÓ­e±§Q§V½šukׯaW%kÖt¬§¢bÈ64ÎX g .ñæZ©–†` t,Ì™ûaNë7Lá£b0Ë6nZ¨b1H74oŠÝsWN4Zôß?ȾÝy,Z|t¨¿>Ô¯ªöñK -éb+ÐÀLPÁ[›Í/¥r$„ ,pb·ÆJ¨‘ B°ÀZ¢° –¢èÐüq¡G”B s:ÿQC-裭-ÑÅ iÙ0¼c ´ð1b14´h!’Dÿ¢0‡XŒD’(%‰ZÄ ¦lO¦ddPÌ1É,ÓÌ3Ñì 㦚…„¨êJs±ØÜ”ÓÎ;ñÌSÏ=«Š%ƒÄÂIOAù,ÔÐCMTÑEmÔÑG!TÒI)­ÔÒK1-PŠ…â¤jÁÚ4ª:ªT§NÅêS5ÈLaUVHߚΪÆë­T›âõ)[£v+\ç|uÖc‘MVµF&Ì`¼Oÿ´OŠÅÇœø!ƒ¥à ÐÈv[þ°€„“ „(°Õah»tLŠQ™+Yêö[øPÿNÇ -ŠOŠ Ot1`ZÊ=÷ZpÅ=1\meªarÍE7±»ŒB]v[õP~ý`a•-Ùä“mklÿìëC­i«½€QQ'¢fþnDÿð•bá» ‘†þ£Åe’¡ÅÆkÉ'Z¤ÇýÄ7•ŸIÊî¿ ¬¾–gZ|-çš'¢YEœÍ&ºg¬mk90Þ @jf©ãú8¬OÎ[ïYGû«³ž¨¥NfÓŒ}é%´éëîØšå¥Ñ íÿ€‘Ó§ !âBˉŒrØh–ĺ:©ÃeBýtÓH_|!ȉ‚{TÕMª‰ó½qÏ}Ò¾)˜ºö¬¥nö²w×ÿ vÐô"×ò¤^E<°Ìî 01žLuøè—Êqì±^žÙ;ß3ÝÑOÒðr•¥íûK_?‡°peú:ì ›ÀÀ^Ò<„=ÏXÛ{°äóøÇ;ºÑß g™à.‚z CX?!±‡{Ô¹MnÔBÊHϲ@´È‚8Ax„Ã’ñ&¢(L&yŒ—ìó*Vî#JR]“&Ä#³”h\NŠáº&ã=™lO{„+?¢%npE"ZÒRŒ8B-nñdáCåE0†EŒ\4ãÑ"æ_ˆZcÁòÆ4ΑŽu´ãñ˜G=î‘}ôãHAÈq qŽ@ÿÀ€-  hLU|eWEòb! cWirôdldá·Ø§£¼ %Iu¹ª‹dÐË$TP‰ÊOÎ’–V)dµˆâH’Øç'Mñ»ûä7 cÀ¿8¤›p¹ ˆÉÌ—Ç&Ò¯cŠÌ™+ÚØK Æ¢$ÌbpùABÆ(@ó›‚B–¹}å`ß g-áOª„Ò¦$"ÉTŒ¨DR#Þܨf7Ó ­ZÅNä6µºuq#iIÕ\wùkŒà‰DÞÒF'|ˆ I±(yäR‘FF”ma@àÿ9…²¨[fî$ÛÁIí6ÇAÚiÎ&É+ OÑyÿª”ÞSØò±¥uªÀÛg^E…ð¥sƒkTñJK É Š[)-H µ^*ÍV`À¦t°ãÐXŒå ?þI´•qˆBúÀ6à\ç:LúR¢ègŽ€c^Q HUä@‘?hã`OA­¿‚ ƒ9ëÃ[~è$AeQ >$oÇçPj ñq$øA°¶¡}ˆ¢#ÒAt„Ð&IDU¢EŽR›Ý¨ÖI»ÝõîwÁ^ñŽ—¼å5ïyÑ›^1à¤`/»dÙkë)›dM–@_°ÿ©2•[™¤z¼ ŠAóÅ$ª`ÃbI’üÊ%­²«Oø@ŽfÈšy0˜¸sŒÙÔP³R¯>l\1T˜Àf4Ž]m¶U;Á9Ƈ‘ `#ûðÆC­+4Ù„ñ;Òbc"SÃC²1¶ fbt Yša¢ð“Scá„Ò ¢Ù£ÅGa²ÐÅ¥¬tE;ZÙtæA|u3kß‘Ix⽤ʽ(I†æUÔƒ*{ojP,ߡղ•‹Òf’˜í‚›2« œè°H™u@ˆSgr’„ôM|´_Y0 ,"&øÊ’£­¡Täs¡ïÊ´H×$%A)ÖN%͹Ç8ÿu­Ën §h]{ÅÂæ[ëB>CVJÿ­ƒ¶>è]t£?Œñq¿¶\&3ÕñYïu÷ÅeCÃ+×rÊõÇgD/)Æ‹‰‹0.rŸTb¥±JI¼õ¸•pTã…H±ÃÇ­KÝå6ˆÄfÏ÷ÄöÖŠ?ÄøÑ3µ¦®è—OeDúÓ‘å'@uÂŽ’#Ô±žu­oë]÷ú×Áv®¬*_vŠ}Â_KJeUUQ7Ö+ÅwJ-˜¿‘d:U<žV2„•ÞþˆåxÁžð…7üáŸxÅ/žñwüã!yÉOžò•·üå1ŸyÍožó÷üçAzÑžô¥¯e@!ÿ STARDIV 5.0 †:‡2;opensips-2.2.2/modules/seas/doc/images/image108.gif000066400000000000000000000242271300170765700220110ustar00rootroot00000000000000GIF89aFðÿÿÿ,F‡ ###+++333<<ä,AÀoéÓ©VýõÂÙ q[fhÛ¥îÑ { P#pà „èB²@Ï8 V·î;½úõì×ÿÞž¹Bó( /.qAFäè²`òÀ 4CÐ@và@kp( €7Pf PœÚV `(ð§ÀZf{4`€jøéw‚À +¶ø" ,°À (8 @) Þ@äWÀrJ˜#n×ãt0" %žˆÂ’(x`‚ 6ewd’Åè" ¨]Yã6€e"8‡iúžŽJæçc×À…‘WPp š™À£ ´)‚ fW¤—tåœ@%`(ž fÂf"`èÛÁ™ (IP 7A`W ÿ 8v˜ ( jPªœ€@d`‚¥(àyáxl²Jæ~ÚZ¥—ú‰ª}'tªjÑ’9-±˜ÚÅ«¯À n¨o’$ªÛæê©·- #mÍ äÀ·îö碚n†ª¡;R[lk pµ¤áA\€,ǾcŠêÀ. «º °Ÿôy å@€ pž ”î Lo‚É®ü ²(˜0š«_PÁ¦Á,ª– ›hH@4pŽ"4ý´@À3OºlÉI°4 v³NÒŒéÖ%{mqCÁÑI# 6p) 0vÍ—Ý®@Q;ÿ]«¨U_ ó›.3”f·†v×8*PŸA 0ŸPœl–c#§}‚¨ƒ«|·Íˆ ´÷P‹Í*A£©\ºËƒÛ £ÌbÓL: CŠ€j—PkL¢j T€Â‰¬> šló¦}ÐkŽ è­¸òÝ];Ù´|6öw _üñ$_8½¢Ûž·÷ Ÿ=½ÒãØ½ì/ëÛúÊY_Ð  PÝA Ó,·9(>1ÝúL@ L*W»‹ÎýPÐ-P½¬nÑ $°:œqEÕq|f< a¢q–@@T‚¨^:ANPÉÐ}«C*8­¦çTXNÍÿZX–à„ëa….Œýê¦AR‰°– gØ!a ‡î ' F GY„àà€¨í ;ßĹ.qŒ¡“Lª6Ȫ3&Ñ(ÜZ“"pn‚G²PÏ´D>È3FÂÝ«´"¹  ¤‚YˆF€‰Fdä›’<Ó p©ø2%—ð…‚¸rîéŒ"UCD•¹Ë Œ´30 NµäÜÚ@)%üÒ^häá,pHöò—1å(YÉÈ$ €%ý¼¥9z!rg €¦(ƒy0H l‰ã¾ÒCÀ ³œc$úð×LHÀ— ”J­&0ÿ#¿Ä‡$ûì'd6P²­K€ùçHÉ‚^t¨D €ŠNô¢íKE-šÑ£àF¡‚ièA0¹W ¡')½2ÓMá ’¸ ‰nBR˜4?‘L)(eÇR¥l”£ùhGY"žÄ` eqÖ®5àꜸ{*:¥¨œ?M 5ŽBœH¥>Щ¤X¡JõT5)?ªDŠ:T”X AŒÒÒ8¦ÑÄt '°@Ò2©N%˦2û\á(d!¾Úšҗd‚…õ†Jbb£Ü9T…ék`9äWA”C„å“QÒZQ*yè?‹%Ðd±Ô¨¶šDT+•¿Î¥Qÿ¼K G5dð&…us¬kÕ$;Tœ­åжÿòVc)õ­j¡@^8Êj¢ €» „·Ý$Ž©® Ê› —(¤­(mÐ/ ¾Ë‚±‹ív];º-“k&Ãì]6:9ì Øõ­ev 'Zfˆ Ü”–×Ä-7WŠƒ¯×ú65ˆ&—4ì¡öÛÛO’Q¸h ïdM†²e²ï‚‡cïHD5Aóýå§AläIáÙ§9dƒqÍìÝ‚8§jÌû,‚ëÄ}ÎËê”@0iR2¶Y¶dÖáàJ)V«ì\g=ÃÍSÄ"€{g§F)ê%­·TÀtóU\u ¬˜êàrÇ:ûÀÿ-|¡y?&B’¼:æe7»KÐNð‡mÆÄÚñÂzœãý½/>¾3ÌœCÍ…F§öôæ7EOOì=^û÷¿>!$õÅZýdZoË_ž¢ßÖfzAf‰{x`FBʼnS¸Bp&K$}­A}xv}ä§}a±‰œè‡~ø‰i—~‹¨e3(lùAlØÇ–lÀ4Ód‰¸ñ"d¯a/{‡Çñƒ1iûWý°˜ ¤eZýr §ZWW-fvxxt‘Ñö!g Ññ)¡ŽR„ƒèQyòQ„<‹6^åÅCV6*²Õ‡åˆoŽ÷Qâ!\ø'O%(‚(b(†42Jh)sÅ$2«¥qi²&wo`¸„;r‘$‡{¢ú¨aiÓdTöaµÇeyhv!“+$+´BNÿç.»R)ä,ÃR-@t5Ãtþ˜s+³ò7K÷,ùÒn'u›â1ðb”s0ó7›˜|úBe‰ó`Y®#C¼ÖŽk¥†3a‡+A“çÈ:Ã3†ó3ÁÃ6DóDoˆrÃ4~sEyÉ1V#xù“`žSmÙ3Àx/:46Tes§6‹·Y©•¡WBY< ™aŽ o¡™Xy¥i¿Æ4)F¼“ ¿³[%@<ƃ<àÃ<Î{Î?ÔØà³nª9³çdc“Qb6‰üã?9hb)@*ŠÅqp…Vö( çIp’©5† P’' @!³5ÚÿÙ‘ †pBÕ¹|Ž!“B#Ä!ãcB˜ÕDE/C3„}3tçW”>Ä$ ô#4üÉœaÄ:󩘇Ã~«ñ÷F8ш;x æu? ;9)”—±1•-%; *•C[UY€ s¤W%•a”9ù\Ig+º:#š–ë™õ˜d8›4J-f¡Ä€¸”J§ÄJ®”M±”LÐ3­6•¤£™$OGªMÈa‚÷MNÁè4šCVŠ£áFô"G˜prˆ03:ðb„cxŒ©`óz(Ðx”Skõ™Pt Vn‡È! de®c£acgh¹ub`ÚÿàFl—›À!›„ù:¤ãHÞäa Tz»gœ‘OÀ@@“ˆ@fy£>W&¨ŒA¨…êd)ÁPc‰ìé ²î'Ep‚ŸWE¬b©ƒcFXÔ ð×FšN¡2 `<Ö·•F~œµGV$Clšj9èx|±ˆ†‹ÊÄc¡Ë&L£1¤+IÅAN†¥Zú­,è¥ÆŠ @Ÿß;â:Ì´K^â«øv£³H‹ñŒÉÈËH&Í(³ºÕª¯«º¯ü òøC{Ôñލ5‹£;¨‹œ2'‰h ù£ i"©"Ò)‘i‘Šå»‡¨yp!™”7i+9ÿ©+6Æ“¿â“ç”1:”ÏR”W§²Ûê‰|h„/[1+³—¹3=³¦AWûÕ6t™xx95hj˜Yˆñõ+«°°Š ‹±Ë´A½³š9ëšä3‰’ú<µÙ›¸I_KØz¶¶¨8šþ'S?&š¦&~@™mšï™ %„D¬ãDöùºªŸ8Äœ;t^Âú­Ú‰šk´iÅr"’àWY2ŽÛŽöäy#nG'§é‰–´£šÄI?JLIJâ*®­ôJI:K(L=ѲžX´v^ü8usV%è²(s»¹1qIHWtBˆ #Wø‘”6j¥IqÕ˜ÿ‘Â{¨‘¨EA¸f‰,^+¹˜.¹eJ·IûëV\b”px2 7‡­Q‡I¥ºzZ|Ž˜¯»´hG¾«ç›•%q­™§¨#ÖŽ“Çvvé5ƒ¸¿™-~‡ˆUié6Á¦­7QÀe;¿]¡ ̹[[–G“c“ø—‘6r• nã;~ä½|#ôâ¢!_\,côE”»y…ß~à‚ÍâFnƒi>\KîÍ´÷Rã¿çàE^è ìâ?ÿ.;Vj+º #ׄ¤rþ&xxÍMaå­åzžäÃã…#ã|IãªÊ0â‹ãf^çO[ŠHéÈ­ãtNê6èJÓä(`›´êd”Èõ}éŽëN˜“®‡«Žà½Žç8ºçê7Åm¾gµç`-èÃ\ìÒŽéÄ>ì <弞ÛÕžçÓΈˆ¾N‹~.‚®žlHèÜnèPmçמ⒭ݬ®Þ¥îÞðîîôžîønyð} Ò¸àêwæW~¾ñ.ðÆ>ë÷¾ÛßâWE;QÝ+®ëí~ðƒ[ðØŽð­~ê _æÔ®îb7ß"?ò?Làmê9¾í Ÿí,_å¿ñ_àˆÿaÜ(ïò*¿ðù¾‡û>H‘Öÿ.ìØóó¯,ïDo–nñ0ßã?nÖmïCòQ_º¯ô>‘ôG/å„Mò\ßõ|¬õKßíRÿÙPßò}óS?öEŸóF/öÞÝönÚwžõÿöl_õo÷ϲÖ>æ+oõky÷{÷M}ò5Oðhö³ÈÜi/ôŽ?ÙAÏÓõ>÷yÄŒ¯ø‚÷T?ø²HñÁ‹oÊ=Õ—¯ÑjYÒ=ú†×=œò¯}ššyK­´%ÜØ\,s¸Mö‘/éôë³]% ûŽ=èž/陟˜|é[vºŒî¾ñÃÏ.ó€OüޏoÚ¨Ù«Œù0+ýKÿÿüÔöp?Êïý@¡Ò‘iòš¿ý©ïñ„ÎüâíÑþænöÒ?ê@˜û0Ÿ’¨Þó_÷Bà@‚ D˜P … >lÈâA‰-^ĘQãFŽ +vtø£H%MžDÙ‘dB’+SBtùòaÌ‘lÞÄ™S§N;}þ´‰èPŸ2UièÒ›=™uúô'RªU7Ò,ˆÕêÖ¤&pV,دcÍr ÃZ¶mתu·CìÞÅ{·n^¾´rŒ2(A# F|¸pbÆiI<†2É•I¤ÕYófÍ8Öð7ðè—.¥žF}š´Ò“eφuýšìÿB¹náÖfK·/ß½»õ®.³éBÆÅW¼ÐreÊË'/ýÙsôÎÁ­5½AûvîÚ@^|øïãÍŸ7_=ùëB[ˆ 6mÜoëçêooý¢Ûÿg)-ä;n@Çœ› ÁÇ0£®³3ó@ Ê®; Õ[ 3äpCô"Ä䥓­Ñ¢¯¾ÛTÌO?»øó Ä g$n@Ål4ì@›CÁ§kPF'¬ÐBî<<I“\RÃÿL‹ J)§Œò£Ø^»Ëq[‘Ë]„q7!‡°@qAǼ ºC{sL2Û+ÒHïšT²IñòܳÌÁ€JA#°²Ä,ÿÏJë¾¹ëËþ\|qNIYB3M4ÕtŽÇLÝü1ÎI?ÍŠB;ÄSÏñôS©A©,T¾CÍJô¾.kÓ Ò0û’TÒÌDŽ×Æ”cSÓå| ÒS]A­ÓN>É35ÃeÁËU0 W²Uø^+V­LHý:ÙJ}%Øƒå´ØÃý4Y#ŸÕ°ÙõàÖ+(©­’DWOÔ–KnñÛzÙ­Š\/5wÍÓ¥Ȇ –ÔÝ K•—É%)¦@ñ%TßkùÝ’ÖoåM`ˆç4±”sL8St…zÎuORâîè­Øâéd‰ãŽ5d¡U®nµ•æšÇEX(6/{Ùá†ÿUšHQG½óâœÓCõ:}¶V6lÅêWd¢Ó䪉dGL‡:f©AÃxíÖ®Æç­÷ìÚº¯9K¡‹¶mdÕ¶ûɶm|Û2ag8º¹¥K¼L¼GÕ{ohûîo|Gtp¢ ·ñÊý^ÜÀ–á^æ8©>½óË•¥Xóx/æyZÀ=[tYI÷6éØQ¿´éÇûõ™‡ÎfRµ¶ýö½îyw ÷54d£GLæ;O½×Õ‘o}ré`ÿ>0ç·Ë\szs§lÞÏ~èßÏ&9¯ü÷Kµ•s4ÞÓŽ'bMmyýÌú²vªè½zºûÜüBW?µ¥‚D®L‡Àÿ­üÏRns’שrÐ* Ü@ûöæ@ÙU/‚×ûG‡¿Ã Ï„ ŸqBHÀª+H7ä U¸56O!{aTRƒ%.1(N, …ó'(òD(Udâ±ø')Re%ÀÚ=é9ë¤Vݺ¸F@©‘o¤HK¶8G©ø-"òƒc5ÂE/^…5{Ô£ùH‹ 2+x$d"#ÂŽ|Ñ¡¤"%yD:V*“Ä$aùHHf¤%œÌd(ÝJR,@‘ŒãNÚŬ°(¥%,e1JFñ•É¢J†3KYŽ’—¿ì .wYCîÑ—ÀŒÝ1‘¹Ì‰Œ©˜ Ì3K©LiVÿ“B¨¼[kniM6R“›ßt'KÓAp®Ñ›å¬¦3ÇyBtJñœí\¦:SòÎp“ƒô´'/å)-væøô',÷™Í‚4 £1èA-¹P†“"ØôÊ zÐÄ%4 =ã5Å9O‰N”¢UÃh>  À0A d¥ÃÊ@#*Èœ|´r!µ'P @^ Ô“Å¢Ä,hM ‚ ä;½ÈP'"U„”€6]$V 4 p€&€ T'P€ À‚¤• 8ÁŒŒºQ~†H•±ÀðjÖ‹µ¯±êFPNœÂS§<-@IU*T—¢àÿ H\b8`rÍêHÖi×»¢à$SQ Ô¯h@*Öµ.D '-xŠZ ¤­mRÀWmi ‚€§ ëA€ûͶS§%M,$)‚4à’Èp€°ÕjGöl©× \½ˆ*b-äÁàœ2„»hfQ›ø:%€yBZ ¶4¦/ljË6œ|®ðQP,€À%m¬½`H ¾96s”d«/Ô…$õçmÎ}¥Þ ¼w3ÿAÝo þ{ÑË8LšWj‚3ÏàÖÔ)” ðÞ{‚7¼A$Jÿ÷ðt*=®8‰Ï¼«KjÄÂié…5îì•g¬¡/§ã)/Žñ©@3ª”n9ËsŽì\v›&Ÿ¶pƒšÛÛŽ»·~YÍm  è;·9Ô¥ùL…û•R_DZÓFz­³[)Ø‘u„ZQìJÓúÙ%§)›]íE}{:eºpÆ]Wi·û¤œùraÚDâÍÉK"x‚–¢xÏ;ÊFÜL›”ôã«!|GüªÃgäÓM|<“* …§é®@Ü W´ªÕ»°õ6ÑûÜ èLwºPS‹cTÃÕ³¬e+è'ïÿÚ:³^·F÷-º…^Üã&× Šžfæ‘ip!=פV/{KYXö±‘ °}ñ ýNÚ½–qS1À_Pœú–…od£`ä¿”ûÿM)}ìk#XÁ FH¼1¯ü_2_#x¿{182íâ. ±ë2<Û±\ë¤Ð“p«ì.Y«®ë"@k°´ø±;c¿ç+²#K2ÅZª&{²(K>þÓ§Í›ª’¢½²BC±Á:9k@ Ó3qÉ*€¡·—Ê€7ËÁ ›Á„ð³´«ZB‚8CC4åjAœ%ÿËÐê) P5*0 -(¸‚µYã) °5ÿ\ˆ ø¯ˆ«[ '13¶*Ã.X5\ã³]C^;¯66bË¿+ÄÂý³œŒ€ªŒ1j³6ÏÚ´N[KCµLã6ð¶ 9JsT{½!ˆIô4ȶ¸,H¼¶pƒÀq+·s :u³.þr·CÌÂDì%’ÈÃE‘ëELÒB½ûÅfF`ÔÅbD;d4ÆSF»A¼fD¨c„FvyÆi "i´Æ»ËÆ@Â)5ÛFmüF8ò¹Œ Çv)Ç7ÇŸ;G2©Æu5ÿQGwÄ,y,8¯»:zœG| 9«û:}dFLŸ¶ëG€|‚ìŸdÛ‰‰ —ÊcBÀB¾–kGƒœ9õ©©ÿÇ;†DˆÝ«ª‡œˆËs.‰Ü8?ø<¡»=Ó«3X×{­Kó¶Þ«­ (½Ö .Ó*ˆñ»´ŸB=žâ­à‹ÅÐÓ$˜ü½ž$0Ýc¿âK4ŠH$F{$Éç+0éK?ù*? <úÒ¾…è¾ë“¯õk¯ /Å:4üصù‹Êì¯÷0ù«>ãª?઀û+6пdÊd*3›Àt1\ÀXˆáÁ<ã½Ï L|±#{­wÛÁ<A!#²ÅìÀô #€ 4@É*Á%CA(Ó+¼ÌËÓ©7›P²|3ƒHB ÂlB\ÍÔ<lL< €ˆAkÈ:sÂÿÝÌLô,*LŽ»ÑL&„lDðB=Ãó2ÃÐ178$K5ì2»CXûµçË€4@¯4L€€Ã?ô5AôN,û6];Ï^£¯A$ÄacLxSJäª %sÄPES¼4T¬³O$€¢ËÄ—ü³SÌ´t‹D‚8ET7o#7€ÅšlPœóÍV$7s»*½6v35Ç$ˆ»¤2ü„:^4ѦLц[J,lÑ…ÑŒÑuQ-'Uˆ”ËPM8]>Ž*µÑõÑ!B“T=àC:¥ŠS‹½"í$(Õ<¯¼*¶L©³D®Šñó>)%R)ý;›L#Ó±ßTËÿ¡âD1eO6uSˆ|Ó8Õ¸0•Ó: ;;ÅS¬¢Ó<åSpìÓ?5,@ÔÔB…8CETfÚÓDTýì#F…Ôˆ©;rŠÔJ%¥òKÕT§Ì¥]ÙÔO-»{´7P%Ušª¹L-ÕT'˜›#UuUÁ(¹WµSkS‹ÒŒ8©/5ˆ[=«ŠG‡àU–гŒà-(K.ÐÀjx€Ò*:£D­h*…ØI¯«ˆpÒÛ2J$«=É"Ê--P-Ü Ù‹ÉeÒ&m:ÿ5I…ÈU2.|ÊrÝ·ú: HWX×*½ €ø)È4 Î£R¡XÖª¢µû²2d /5¸Ä³Êº,·ü-*e"®¬Ë>ÓJø µ.5EyÕÙ†8€P ,Ђª èDŽe4é,B÷YW«Røþª€U¬ÂüK&;S‡€ÍšmØ\k±äµíºÀÈÌÌ¿42cˆÄ̪5U©âĶ¥Ìl%7:S2gx:«0¨B€ `ˆ­ÅÀ Ð [Þ³²×¤AMÛ+ëÍÏŠÂ3BÝ 4àì\ õ,lˆØ7‘û³ÑµU7“Z›E\F£5 ‰0“íÚÿ’\±uÃÿo Ä=ÄÑëÄ/ÍըϊÏç¤Ã1³Jô<Þÿâ½íô²¼eÃÆbOãùL6¨ÊXØ]­’NH‰¨€P€¸ª—ŠÜÝZÅ Ð{EµHÒE,HÛÛóPIä´RtE EXYÄ6”"€L»ß M]„´ ã³üU0À(€}å^ïýÞ ¶à Æà Öà æàöàá6Èv}×x¥,£œIHaÑ+X%>›á €’=Ù”€œ•J¡Ú­¾ ÀR-M˯Œá –ˆ¯e_¿­ÛÀÄ[ d€Ð Á!sÛ!Î`õÝ]Ö-Ý »b³Á#”[Ú”bÃÝmÞ ‚^ˆÎ] ³1ûáôÄÞ/®`*f_ ½6K`ß D£+:c`7ö㉬ ³?&äB6äCFäDVäEfäFväG†äH–äI¦äJ¶äKÆäLÖäMæäNöäOåPåI !ÿ STARDIV 5.0 < ;opensips-2.2.2/modules/seas/doc/images/image109.gif000066400000000000000000000373631300170765700220170ustar00rootroot00000000000000GIF89a>tðÿÿÿ,>t‡   3 8 =###+++333<<< CKN O!S$\'a(d+k-r/v0w0yDDDKKKTTT\\\ccckkkssszzz4„6ˆ8:“=™ ?ž @Ÿ A£"Dª#F°$H´%Jº&MÂ(OÆ(PÇ(PÊ*TÒ+VØ,XÞ-[ã.]ë0_î0`ï0aó2eþÿÿ™ÌÿÌ„„„‹‹‹”””›››£££ªªª´´´»»»™ÌÿÌ™ÿÿ™ÌÿÌ™ÿÿ™ÀÀÀÊÊÊÓÓÓÝÝÝÌÿÌÌÿÿäääëëëóóóþþþÿÿÿÿµH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê”&M£JJµªU…O¡^Ýʵ«×¯3µ‚K¶¬Ù³Å¢]˶­Û©jßÊK7§Mˆ2T€@$"hñ+1.¬ˆ+^œU ãǵFžœ¸®åË4Hà«åÎA¥Œ0E è 8Yͺ5kÕ®cK~B»¶íÚnë~"ŠïßÀ}N\øAÊÈ%'ŸìxyäÃΣKgÎ09f ŽÐ‚¤D¾K"ÿED„ÛËÞnþÁˆ,<`â–ðã –?¿}p ,!~ˆ¦„x”• D`‚ZA‚Õ¥Û…°]øšc»é–[‡¸9V\pÜaV¡âŠ,ª@‹0ºè˜4Öh#ܨ#ŽŽ1áã@ú@D yXH&©$’áä“P:Ùd”TVIå”V>‰¢A áå—`~¹åu7 €X€0ÅwW ©Åö%!‚@ðÉI§XT‚ZPa@ž'<Ðfžpd§ðA¨„WÀ¨›p6JÅ„A0é`Œpi¢XY¨ak~:ˆ¸‘J[o& —êo(¾#‹®¾ÿº¢d;Þ˜c­6JVdCî ä–,)lXfYl–ÈJ™¬”©…鬗c’Y“Wa‚`"q€ Hð@TPBð…;.|EH Û!1‚@pjË­·œšK®cˆ„Ý‚ðÀs¤•¥ø"°CѪ¨Ÿn¡©§BŒjª%®Úª¬³b<댸âØqºú*¤È?;ì’ÇV™ò²*³œ°cÏ:û²´1 pÅ¡ÅwH„`PI€¢A|G¼k0¼ôœÐB;–(R8=ÐÒUz©_X§6ó‚-‡K,¢Å«Ç¥ÆUÄŠ1­ßw$÷Z÷‘''¹r”{³ÿ eß73˜3Óüµx…§‰v|YÅ㈠„ÏZ€°Ÿ€ ʸiQ9|9k!…£‹7.©@y\„Œð:§Ç.¶l ¯6ª©’:±‰S|ÛnË wÇr#O·Èv3wÞÄ. ¸ßALÏluƒ^¿ ODXF€ 6Ð÷]a_Íyæ žx” …BPÄû=`Dy „0»ZÈç ^øiAà7¡ù æZÐ}Øç>RHk!ŒOÃ¥Æx*w·Ùxg6¥-xÀžÆŠ÷ªãá*y'\ž¯š·ÂçåÍzÖs™Ë—½!î"8€€²pìm„4!ŸDÿÌw•D¡?é!GžrÁ†eÐa½ó]AX*VQ„oc[Û8†¼ñB$c we2èE/Y1”Þ ±WC^„‰ ÉŽø ‚…„ˆƒ9Á„¤ ´äIT N#€„  @p…4èA #Bt8 ñH íyO|æSŸJ槇Üz†‚@FFDˆ(ùZît·ÊÝI‘wg£XÚ p±jÑ„µBa.UHÆ0ŠÑ…'ƒ!õïµÑ±`BÌ$%ðOvdÔù-Á¥!ÁüJwºGjš‹ âŠà3‚ ‘òæ›ì“¾=e*OÔ:%CÑÎp¨©Î*ðÿDÝm𕾋%ðfYË,‹'ô¢BÆ<_–z¦•ŠIÃì)±‚pDµ–p4:FZ‰‚øŽà>±è‹_Üúì ‚5,øÅœÝC”£äÕ­o¥k]í X¡hê-,ˆÅ^÷ÄgZšÈ0 þDQìÐïFdEâÔx·Dh.ÊÐ:˜ÃЍD¯´Æ…t©ˆ¡ˆbb¦Ì!Á£¡Âù# ħ¤¡šÕ|ÈÒ€¹T 0 fŠ9DáhýSÿvÚW½jC{ «¢ÊV¶ò¨Jâ ŠE¨B^Ô厪ÚË0>ô…jÜ*W¥WÑÁ‰e¬^«ŒA'…håOÖœ¦iÜÚ¿’ÿêl ¨ûœéðU?]A D(ç¥Æ·WFéV JÀœæúttÙ'u ûžè$“ÅR¥±|ìÃÊK¢íƒ•-aT17ªò²HcD/V…¥UÑj©«X9æ˜S;Ôf-}"ÐÊ" ‚¬u¶o=` H–¥x€)óºÒ,8j`ò·¿øH@$8_Ý÷=ð#¢"MÙ?NÚgˆ2Ï(’IÈ©‰2Á®µ›ÔÈv·©'¢ìÚlyYÍÚʼ`¬ÛUcÆ3Ó½|ƒoB¾ZÃ:G¨Y¢|«r¾S õ‘g¡ì‰vò‰ËN!jØh 1ÞHö» oŒH¸f©nÿÈéåÕ¹Täö"™¢llr2ŸeŠt;våšN›†RK\÷:ÈMÙÅÜsÕDÆEu"dwSc ”©:.›[´iN»YG>¶Õy‰g^­e¡E²²H›g‹î™Ï}ŽÈŸ[ʵÁ6­°t*ÚAþŠ4þ!öÑbÆ™¹Pïʼ;6è?ýãÌŽZΞ=µ’ìì^<{U¾¯†u¬rÙ±nq¿ .sÛ(Î8rÔ5“+åN7¨/QæqöÙO~NÚCg>vš“mÙe“·‹ÎrC£Md3R[´ÖŽ/X³M_”œ`-, tÕªœÇþn¹Ç^ê’Å\`3G£ªßËj¯"çÕ )$€J$ X”ýA±Ö¨…€[=O{PŠ®Z¶‡BnϨ¦ãNwʬ}QBð©8 >Z¶$…9ªýðˆO¼JL*­ì«_ß*w¶USŸSŠÏ¼æ7ŸLM4„™ëau-zÌ;˜·œO½êWu3rw4€¹…îÉ×ôæ9™t¯û…X÷ ½g½ðÙHñp} ¦_=)@œËŸá¶DAˆüÞ Á¾öË’ýã8RZ(Áÿƒ´ öx¥c/p~JLŸó äü8ñÿ2!SááÌ…ð%·Ï¥\T¨ @:x÷`Záe˜7x¦#Ø(ðä~¬£'ÛäzŽBeV.q'p&ßÔ ‚?ÁS*…Ip)Œ‡y‹¶’Q\$˜XS(±‚+µ3UhQ~@û2/%(‚@„3QzˆÀž'T;¼X˜ç||ÅR¶FM˜45(W8Ø?$°nE„Bø…`¸е[Z@& *ÅM¹÷>ÊÅ‚¼uzc(áâ\Ô†r[ˆ8â&S" 3å:aˆ‚HÉG€AsR|d;ø£?ïÿBPÈ|îÓ?'XaÅG`‘`$¦#„7‰ƒ8ФxÿWЍ˜Š\Ñ}ªØŠ®øŠ°‹²8‹´X‹¶x‹¸˜‹º¸‹¼Ø‹¾ø‹À蚢0ð¦×§pÉIQžA¾' aJáó&àZ¿…øXÊ(éc €Htb3¬H  Œð:Ñ Q€Á(„6CŒqŒ ñW¡?óaÄ?@ôQ eÀ'X @# Š,õsHdøÛHùÝgð8 x‚öˆb˜4`š$íçIû×u¡UI…B~ê·ðb÷4bÛ(’¤Õ§!ÿ°(Žä þqH2AzD5}4Ž 1ŒavhÇõ· q¸vXFYŽ+u~ Aüs(†”bîM „ŒÉèyˆ'zbœ}ê$ƒ{·kCSGðQÛt€k)Hðsi5N>jY–[x…‚‹‚˜CMÖ„Me8?ëøÀ˜xi”Ç1ò[V¶˜¹-æx$ ‹cX è-—} (ÿØw¼%e÷ôÃõ•üWRóx)….êÂ.1h/Xƒ'™X#€Yp­“/USh?øuÅ:‚Æ4“烜òLJ8)ÐIk¼%RG¯DW“… a öAýÿ¡4È'P‘±›¡w0Q~°Ù0\žWzUø›SH„º‡’¦#F0žŸç…oB]Û©œÜqŸOSiXÔ‰ym` –É(}Z™\2 xâ ¡ÒGW™žœáõ©*–Š'[B„ŽÇž™ ï9|O!Ÿ“#{ Ãn™CsXeÏu{¼I…‰µ3Gèh\S" IY@à\>“£E°£EJ®†Ee{¨ Z[ ™êFW}2Œ{ˆZ]Z’”XPa£ËÕe—B´U¦wÄ£„!ey²é“/º}Ê”„Éw‰ïbq#€q¡x`½IF }Ѝ~¤Ùÿx €¡`3Y‰’X&À'xõ:b1q‹´lE[$¥%àZ˜¹3}â©hRay˜§zG zå§ q“Ä/—"«‘|¨ª|¢‰Q€žyÊzoç# 3¨ÇºªY€—­%A­Aát¬Èš¬*Ñ&ÆšŽ"L¹}þ¨­©g_æš®yªsêÚ®óÈ™ (‘ÔØÐø¨Æø_J)éàš °î:°"ñ !›RŒ¹Z¯ ÁˆR®G§17¹LQ±‘ŽÿJ°›ZìºL2j“q~4©a–DH†´™¸“KÉ~5¹ ´R\ù‘î‘ZÿY0-ë"°ˆtòbXvvy~]w³[´ ‡®¡d©šf~h [ñÄ´m™HÒJ˜|Y“„)¬Ù´î¤'¨—ŽAx fߣ€SÛ£F›¶I¤mdÅ”óiœ¶‰S¹Ùœ5E:éi¬€¦œÃœ+eNPñgUv›ÛA>° Zñ|i…h‰"+„ª¶[Ô²ÄHŸ€•4îœøiS  h™;O˜gNË9#êµ ‰ú¡k˜7¢„¹°›Oò¦Q2ú‡»¥¦8Ú\:J¤€ˆ"G 9JZÁ夲•©øÒm¯ƒ¸‹yŽ1™Lú†vGµ7ð¡¦è¥±›½^ÕFDn }ÿ*G‰j²6‰<—Нªú[w‰aúJºq¸ZâÃ5œè©—7¿ñc@ö‹“ãK>ÿ«½™pg°¬½;¯ÜưA¡G¡“›Àj»À ûì«Áñ±¦¨0lkÁFË‘R#²4’&F’(ÃA4)é·I#i“à'~#wQw"ŒÀ$ ¹¯:–“ô^––Qûôf9) XxWvÄód!@€,Âç½üÃïi K›†¸r;htK/+%yYó[‡;Sd¼›¦™Ð¡§ˆðz± !Á\Ì‹ò ƒ¸VŸ¡KXØ7XÀºKŒ{ª¹¸–Fÿè¢K¶s¼dHË[3ì²æ[ 7ìŒy\‹l»Ò†ÄË»d¨Û8:YÃÄÅõÉqX†gøƒ,ÇѽtÕĵϥšRLÅ™|‹^̧†«€*¨À*Aý[躿’hˆÀÅ¢|ÅX<»½y;On Á¹|Íëȯ¬ÃÚiȬ›‰¼ÈØ<Î#ñÈš¶ÍÛÛ`¨»O Ê¢a†hXÅä<Ï;¬ÍXœu†õ|Ç Ì ¤ÌˆHÏíÊýÁ¶F`€É á¯?aÎѼÃ#\ÐíŠ[—‚±9} i±Ñ ›KMá‘!F@Ë[?9É0áÑh›Z­½:÷S—™ÿ²)^›)”é3²cö\_DWÇ/ͱR`ëÂpûg¯KkÌÓ]¡Ô%¥÷g7Mª»Ò==w9ÕîJæf3œ\ÓçÈ)Pá'“"nK, ,ÉÄÕíJþâ¨æ¬T´zfJWoYíÖ~í}ýׂ]k=؆m§XwËqÀÌÁÃÜÛØt'0WçØÖáØŒÅ–=Ù™mwŠ ›‰- Ú¢ÚOAP”£Ú  8Æ*™†Úª=Ú¬rûæ±-ÛÆ6PÈÛ·½Ú¹­ÛÂcÛ½-³MÛ!$ܽ]ÜhÜ2ÜÄýÛ˜–rï*VÎMÜ­íÚàÅÛÉ ÝNõÚÕÿ­ÜÀÍÜÍíÜàÝû¦Ý·]Þæ}Üã½Ý×­*µÞª­ÞUäÝÃMßÝ-Ýc‘Cɰ±±üÙäýÞ´tÚßÍÝ$bß÷à9&Ýò=ß ŽÝç}àþÞ§EÞ.áìýà¸]á žÞßµM,¾D9÷ÝžÝnÜÞâËÍÞííÞ.ÎTÈMã5îT ƒá+ÎÜ.Úøà)÷ã¤â®ß`áÅJÙ©”²#}²)œ(3[«“4«åüÈDNÚ=.Îã¦=äpžã:n¯úRà¼VFO¡£š H§‚N'Ðg^ÚtÿN"3æ‰~";>çe.ã‡â¾ælnäþè8ãWdé©Ýæþâ žæjÎ}Ãe¸éIyôBƒ€›ÖÛ\è;¬âqžf“.æñMæá-鸾ÞÇ]ë³.ê›é¾èNêÃÞå .ââ=i5wżWSX# ¸{ ÜJÝþë!äëœ~ì»^ßcé¹®æÜÞíŠîé®íÞ>êê¾îÁ>îÃNâLi¥…iq¸¼€^íWžL²Þè¬BìÅîïjðŸNé™Fðénîç^îÂ^ç_äÆ~îïåíîðÈŽé¿¶ÜÃϽ­JÞ¾[©æˆü;! ²¿&ÁÍIÔï ïè Ïë¥ÿóà>â4Ÿß.îð.ññŽŽî@ŽñrÎîOK½ñŸôüñšÇò©áò ¯ã7/ä·®ó5/ÞSïæfþíW?óVßõîÎèEáDÿòÿþ•~ß>ó\Oõoþõn¯ëp¯õ½Þöqö—¾ößóÏósO÷¥>ݱžíc?ñoö{o÷¡øY¿øÞøŽï÷eõ?oøÉ~äUÿî2ïÁ˜öÉ­÷Æù˜ÿö“¿ùoøh^ø¨ù™ôß÷goùBŸùbøFo'.™lžŸÞ oô¢_é®ÿú¶¯Á¯c¢?ú^_ú¦_ù|¯ú²ÿû,M¹G»lƒ¯ö°ÿïÇÿ?ôÌóX¯øÊ^÷ùÎ_þã÷ýÏ_üg!Da-y$»Â"k’L]P¿óŠÞý´_ûEàÀ$@ËB-„<„¢BƒV´˜p¡C‰…PÌx1dA†=Nl8² J( 7žŒ’%Æ‘.ÂDI3¤ÎŒ6þTèP¢EEú-J"h¹‹–)NÕRÂSMX:µjC%IÍžêó¬IœxZ|‹"Μ3Y¶,I÷¡Ìšw}²…Éw§ß—zVi°ÇÃ=gæ¥Û.áÂmSÚ}ŒVófΜ]f1 É $<È¢¥ˆ H ±pìÿèÒ§±¨íœ;(ÇùØwGÉÁCŽ|¹oæÊ–¯Ì<\bqã;‘V¾¼æõ“ÔR¾™ü9ôãºÍŸÏ-`!hÑ!€FúÈ$#d[Eâþ§zôýy#u+@$°@lèÀT°Á7rÐ-#tP) '¤PA#Ä0CKâÐà 7l°C ±ÂKì¯E‹â½+b¡•Ш8 µ"À¢ÑF-ø{1·ÿŠD2I%—d²I'Ÿ„2J)ŸŒ14-–ˆ‚JÐ" @aûŽ`¬º2Ë-‡œÒ¨#ý3²³7ÝõŒSNCÿ=«óÐB7#TPG…4RIlÔ³J•RT³K…Út#ó:mhRQG%µTSÑu­K%”sSVu{UÕO]MõT[oÅ5W8›\±Vü5VX{E¯W_uE6Ye— µMŠŽEUX(?|RX™Å6[m“:!PhõTÊgÝMÀ%S½v[u×-•H?¿Eµ¤)›×Ms]„÷Î Ùå·_JA‚%ú7$°„ ‚½Œ BXá!†`à(­Ë>óííQiEQÝtû%™Tʺ"´,N†J*ª¬J"6Ãz¹¡¥xªY-@8bPq%ý8ÒK¶ðB¢®–!‰]BÂÓ@@Çÿ’H6Ó¸m=+‰œâ Ÿ£ä¸Å°Ï½é<÷5;mTSÓ‚ùË/¨,’ ‚ö€ª2\@ÇÞsÔ KZmFÑühz–¢G6¡2 !¯H- ²˜±F-ÞÖš`†¢ðzc¾15õïS/Ü?ÂKÏU"x``^S f/b5¹Ì[‹¯žâyo`_á =Ýý©Ûi Þ,â2žIÒGM>©åj~ÚÓQÇÞ[ÑÑRbB-jz²‡êþ,ðñž·ìHË7ëüýü¾>{ùG Á¥v‰Ù£·Ÿâ%.ÎXšö¶ìEÒ AY&†¿…èü#˜ÿÛ pbLRÈ•Àÿ2P4ûë\ÿDP± VPbcŠÉâÇ®íÍÏYXaYÊ´À¯A?üÀÍžbš…eJW6‚²ÐÐP57܈uÖC«üÐz¿ã“ɸ †H´ÙåvÈ&NQd+]ø§•Ék! ƒÍUp.öÉj¶ù„ÖC¯3J!ZÀ›ÕF†¼kqúšTF½¡Qfy ×gBãFÒ\-kØ™ ž7Æô•„mn»#"—7›Äí}¼¢ˆ%‚ÉCòÇ”™ë¤~"¾A Iй¤K©¹Å±'•裤¾Œæ< æ2ƒ¸QȨ°ÄQ^#‚~\¢ÈÅA%H—k&(§@ÌËÿ™Êdã"¹øLÌMj’ç&5¥`M-$3‘™#Rå„ÄJ_ªj—ƒ+P;_i-Ý`««Xµ½Sv)4»«àšØY$ØŸùÛgçúÉ€ P a£nL`Ï!TŸà'-ú€œÁwò¬Vü4R±AÑ%WÈ Ÿ„R· TpßL JõFÒ’ˆ¦u2—Oà5—Új§ i/‘òÓžÞô‹,2ª¥`ÊÂ;q©I…äH¡ªÔ§~‘S-,\U§j8jm•ª ŠÛ´Ö«%¢í«XqêUQ •­Ñ:ª‡-¹E1®$©V©Ò·jê¬}uç ., Ö®ÏÂ+H K"P%°ÿÎâ+Z5ÎmQö°z£eS„ÕÍrö±ŸmkW¦®Ñ^Ö­$-­iEOжW¬*l]kVAvu¶·õ¦cÓ†TÜ–‹C½®$=K´÷ªR5nræÔâ*·³¶unt£ Æà¾Sº×Ånv} ]ív×»Ó=íwÅkc•×¼çEozÕ»^ö¶×½ï…o|ã;Þ;EÁ¾÷Åo~õ»_þö׿ÿp€ ` ØÀFp‚¼`G¡À †p„%<áG–­@컄.ÁÀI8À€—`€Ä#ð0…UÜ߯ØÅ/†ñ‚[c×ØÆøµ°W1Œ„ ŸìÀ Nqƒl_#´îÆ/žÿñ‘•¼ä'™ÉO†ò€s¼U k8 ^Â~ôÁ¾P;²|‚@`ðð–p„, 8~—`„Û÷8@¬eW¹ÇYþp,`^‚°hüÄö}ðà`Ò®²‡9¬„£yÀ0Á}•a'ë7Ñ‹>@£ý äH`ҥ毥)mã)OÃ$>€N¦„pú!^§£ ‚©exØ!NÂ8=š, C&²‘“`lH€ÏÞq…=` ÿwÈ౩\N+¡ÐÎo·`_¬; î&ð}áÝdpÿÝ«´7|‚Ü—ßïö6¹ÿƒ}n|>îž÷[ÌfZÓ×®ÖtO`€„AÍK(Áì‚€À¾¿þÅ5ÞlþÒÙÎÖŽ‚`}mþúùÊÞr—¿Œg1ÿ¯ÌgNótÎæ%¸Î÷íùž{þæÜ9Ïîùð|„”Y )F‚_£ë9 d6³§ ,@@³VóºÁ,fï5lÖUGºö€"Øå›ŽBØ=|š0sÚ7o\Üu¾æ6èBâü`ºƒ`ÛQˆ:´Ñnß»›¹q[ïú×ö(žÓe‡íŒg=÷|ím‡p­¡ší—+áz‘íxñh>}=,uSÙç·ï¨Y.ÿeB ×»îõ»-lbO[ÊŽB²—}qgC»÷`só…j¿œ÷BPC@¿Ó¯öô‘?ë„»ÀI0ÀÁ²lû[ó—¿µ9Üv#T,À·ö½®— ü×{˜øÅ~:äã?f›¿!Ó¾F›6{°ÿ:~²øÛ?H‚%83ò3¿äK¿,+·öã¾Y‹H>jg‹‚ú»7ls¸´0¶—Û?ð2€#Ô‹ƒ ¾@³i[·%ÈAÜ/“ A «¶ܽüÊ4œ¸Š»¸Œ³8Žs=;‘{¿ü2¶ˆ»=×S9|·I‹s@9ë8ÿ€Žû8OãÁò³/ CÿüRÂóÀý”+°÷S‚¼#0ÀÛ´'´¸,SÂ`Bûµ«¹=ŒBü29K‹‚> ûÂ}39¼º&D³3 75ŒÄûbD¼/8D¹+\@;d°»"Å I‘S”£Ç"=¸+‹;€.\ ˆ€Òð8x ¦³¯-{Å6»7ÛÛ¼£Âû«·Ò‹½ÔS¶-d=×c¶bܰD·a¼2,FÝë¹ó¾-ìBûJ¼üŠÃQ[Cf›µ¸À5$²‹3DäÁ¼ºOÁjL¹tT¸aIJ3Æ,;G2ì?f¼2gܯ 3#˜½<Æsįmô7vœDc¶ÇK#G\½4ìÄuÌ=ÿ©ãAÝ =TdSäÈLEÀZ¹fÉü4Ù0M“³} Æ;´2,›¶\·ô° N³ÁÌÁ4µŒF‹¿h´FÆûº #·”C@$À›äG4¤4ôã±`“ÉÀkD©ãDäGwdImÃÁ‹"è·ÀôãCd€œJS;A+0¯´¯p½},J% ã[‚ ËElÚcÊóK¿+S¶KHK±óÁ÷ÃÊQLANYÉżÃpcEW„EY¤Å-¼Å:ÓEX¤=)œÀ KG2ÆžDûãBÙß;‚[<[­³/¦äÁq56“Lð8Eó¾Hã¼´Ë=B“ÇÿÒ;<‹ÌY“ÿ©ÌœN^ܯ$U£´X$ÎFÄ/%P4ß;·Å»ºã|0×ÌE<ã»Ù”Î3ó>iÌMwÄÈ KÌÝ`Ìõl9oºý*É÷D0!L1LI•¼Ï÷¤Ï}´O’„Ïü”2÷ôOýÐ;ÁÞäOÿBÉ{³ÏE²ô4+ö”P›Ð E2 ÅPLƒP¥85Fs4Ö¼:WóÀÿ’µ¬PÍн?eQ½¤ÂøÞ|4ƒk7®\ÑCP EÑõQ ýÑ U¸ }.ù2Ò#EÒ$UÒ%eÒ&uR›"Ò(•Ò)¥Ò*µÒ+ÅÒ,ÕÒ-åÒ.õÒ/Ó0Ó1%Ó25Ó3EÓÿ4UÓ5eÓ6uÓ7…Ó8•Ó9¥Ó:µÓ;ÅÓ<ÕÓ=åÓ>õÓ?Ô@ÔA%ÔB5ÔCEÔDUÔEeÔFuÔG…ÔH•ÔI¥ÔJµÔKÅÔLÕÔMåÔNõÔOÕPU¶ò=S Í8;X¦RU¶ ²›…((Ç!™¢ú‰+@“>Jôh\åÝÖQM–cjŠaeˆ)pU!žòšQÃk[.³Õ~q¤ÐU ˆ€iB"AV£øV†b%¦†è`È#`U•ócˆ%0¬ý&s݈ÎiÕ#x$(~ ‰a‡ñ’„y-P×`W-HU ø ºV†HXÿv؃]¥:˜,‘˜¦È˜¦ˆ©… ãáˆeØpØ]Õµ b J8Ù‘‚º‰¬ žm¯D3“ÝÿØ‚* wm ˆWÇY…ÂXþà8!×mYÖÑ`¬ÐŠ)xŠÕa +a xE#`›eZ­%, °ZÎq™±ˆ™õX[§¥Ú­¢;k±PY xÛ¶U«)ꢔ9WÊI[«P’Õ‚¢›Û—I¯¨[8×J™+jˆ$Ð#0€i"Ú†` §(ܱàP-Ú\À•«€¬­"Çõ"À  ¨ÞyÚl€ß«ÖÕh‡9,È<£  2™Uÿc}Ý€p pð ȵ xð›np‡îÕ¦éž€ð€ ›€p ¸iÈîø¶n  Ðîj8jùr¡~oP Hjùæi&×jÈœf€‚Pr Ð °éð>*·òœÖÈñÀqÿ·îï À›¾r/¿òwlé–®æs h ð¼^q›¾oo °·iðñœ¶€p €qç€ x€!¿éx€p€!—où¦tÐr ôØ›Þ8ÇéP*_p Ø`€°€ HïPrL×tN·ép€LÇœv8v ð oðÇ 8ÇéXôoˆqn/t Ȱinÿœ¶þ6sB‚€ðXÇiúÆé0÷§sÇ^n§êiE×óp€`w›Îpu&Ïs_í.€PÀiÿÐrœÞî*ß°ixw/w€w{Ïëç€øq÷sø € ð€W‡w@ô¸q @+·÷8€Žÿx˜?ð@œöêwšï/*Wˆr X t X(€ 8p`íÞ@€ @€ èsuÇi(€ `rçw à÷÷(õ›þÀo›®­7û{?l;Ÿ.·nØX€€×qGH·é(ôC¿é¶Ïé0€ ˆŸx(w ` Çxp€ÐW€rt›VMW Èɇr§8ÏÈ©·ixqÿȸ€z·iYð¼†}} ¨éÏ/þ¡¯ñˆ·iˆxš§x›¾½gRïü-÷s øu½ï®¨ø„_üo¿i`(ü@ï…g€Ï€ øÚÇéð~÷ç{ÃÎwjn~ˆ;€PpB4Ð@D€„480Ð!Æ120@xÈG n8|A#BLü„E >6v¤B¤–1 „Iƒ§‹ wbCÁŇ]œD©Ò$J‡0,`tAUæÁ7ôÜa@Â"ôqp—@È‹õ+Ânˆ¸Ö.B3`ˆšCå[¸‚ÿ.lø0âÄŠ3F,@ äÈ’'S®lù2æÌš7sîìùóäQ!®xAÈ `$`w¢Ãº_w¸ô! '`.€ÜpkCÇ6’™ˆŒ¦0Жy{‡× /€xÈàÂCYXÙaˆ : ¯èÒ{ñâg…2#ç9€üøá}` ªEysâÂk ù0Ð4¥@ @¤ À`”?, x :´Cy8œ]²Id¡¨ÅR1à‚©¸"‹-ºø¢b6#5Úx#Ž˜‰†’k@ÀB„„V ‘ÆE@@M&5 ( ÂJ y eC@L°ÿ” (à•+ý`Á`A xI%ì°xÐ #bpB/xéÀà€ ÌçÇ}»ÿ~hP7‰ÿIl«øQÀƒ*0ŸØT¨ÌØ@y`À=—|Ä~@Ð\2@Ýð†öÉŸ‚®BÁÞüÀ°€¡?àrx²ƒ z …v€^½p(˜Oñ‡ œ0A+ø_ëòe(GÃAâB.P)>ùž–f%ÅÊ)Én?hÀ®,'\äÜIŸ鱯ŒÿÒüÒ²¿a$$A)KVbƒŽ¼„„¼‚U Éå€Krã‰ÆAHî^pïÐ'$ ¸ ¢ó ê4è„Ù±Tå€òˆÓ&G o‘*ùȲ‚×9$ÿH’CB¢‚nmo k ƒA‡¨-*(xÛ š2·ˆü@M1#2¦Æe:í`Q Sž䃢lª'kƒ 6 € «,0àÈ!Ó‚‘´Ñ!#y4hNH‰9¡ÁN”B±@Å)y²€ä¤BÊ[Nš[Y +g/f q L,e¾>"d$#èXE^š%h¯ €4H&Hù…Ff’T`΄UHt˜ÁЧ?ÿ‘ã"bç"¶1‹GZÀ›¸ä7'ÜÍ xzŸ zd78° Ö†IZÇ?Ø\ºÃÞÐg?è)•{àsò 5<ÜÏ?ý& À{Äÿ †ó‘\e1QiÍ c»l€{e±K ¨8Qû do )`Ï5Ò’ö]‚ 4óÄ(T•S`ú”`f‚œ¬ šXž°º›\)³–ò’±|ÔÙa¹ N ˜ÓÌ ê–E!*±‡Z›gU¼K)ÀXí Bb@Æ6„P&´Ìd>à´5„  €?ÝE£Ê`LÀnwéÎDe˜¶»ÊlaÛ£Š)ìj/ÊÖѺµ±ô‡0jIÁ5½+ß‚W¼ö¥y–_Æü eÀWwWÚùØŒõ½/‚;³ß3¸Á^ß,a=¸Â¾°Ò"<á ÇÃþ0ˆ•ÿÉásf^¿"€š„c}í0›KŒ)3 Ì,f!¾1Ž‘¥ao¸bGaÑnyÅ­ ¦P×äY%9ÇNNÚŽy,a÷x¨+ å‚„¹kn®®(¨{~—»˜}¤sEtÉå2‡ס$vÀSB;Ûùx)\ïÄ f(E©R…»²ò˜÷¸œÄÉù|2¢3e)#˜ÊQi×& S”ÐH`_d’Æpl[ZaN\X%À‹*P`YdòÁ„ðØáIHNø 4`…%5 »3C\#¤@”Ipôç‘ù€l²AÍ0ÐÍD3Ûa‹f´}Í¡¨Äi€Q’ÿK?è8–Dd¥ƒ?Š“‚-·¹í ’%’¤NL¸¢²ø€/Èd¹sòO‚2—KÉôR®b¯¹ÄU=±c+ pÜf3üaÐ~xd¤m©tbS›FÄÈLW%qÒ-xãCÉGŠÒ’—ÄD#  ÚùÎxŠFµ/¨Àhî —©T%ÓD©RXY_ TUk8ÐûõlˆVâø‹H×~ó•Èà ¨imð”£F%o@zÈŒåd@ ‹PR°Tx_ç!#úˆ\ˆ’¡ªR{íîéAp¼5 Òx5Q-] ÉjÖC½ïé"úÃ%nõÁo}ñcŸüÚÿŠð‡ŸÆâ7¿ýîgXúÕÿ>ö¿¿þö×Wüåß·y °"I<Œ (À¬¬Øw€€ã½D à ŒÊôä@ÔVRÄÉ ˜Š£(@‘l­M®ØŒø0ŠäjÇôt€rÁŠüŠÉø£,@–”EÎÀΠ„À3!Ä À Àý<@Àˆl€ÀÌèÿN@“Ý_ˆåŸþíͼÀÀ_ùGS€™`èÀ‘”G† ÆäÀPHÚÄ×8¡ˆ…P@¥E ]ÀÉÄFD |MÙ ÓwÀWwl ¡Q˜ Bd@¸1‘ øÛÙÐG¢„ ð!F¨FœH…¬™œB…$Y`eÆÈrؼ؆]LÀ®†™JÔ€ŒFDÌ͆üÀÂ}\ª¸ÅÀÑG ýØXŒMkØÅ†DÅÜ$^©|+ÞbYä`D=EÅ/:bM¢Ý`!9¬G02_&~˜n"ÓŒ‰w¨½!‰á!„ˆ ËwȆ'Y +]Q…YEÀáëÀÿ†¥´WlDD:êâE`a¥|„ t\„ €6yŽ¥XøÞµµ#2&L‡—ˆÑ@øNC$$”øß4>Y5ZãÇÌ¡@|Æ,‘NLXFà€ NJ D4 < Ä ôFddKÖd7Ñâ У’Àä²!#^À€š @?:Ä™ÀÅÜØua}ÌÅà}Å!~Å\9>}hUàäC`p‡Sren$ˆu¤GNLÅ € ¨e,! BUÔ ‡l Öqñ…_ÀâG|”CÐâ_äÓ‘ÀEUR^Ö$)¢D`¢…ZÐ"Hà‘2_2b„GÅ)¾b¦DE`$ÿ&’eb˜åYFLŤ€—ÉÖAOLF`qÄuyK€ˆ]Á8bŠF0æ,P‘D&0¦ }LÀÌh)A ”ÈD‰ãƒØ@ØGTÊ ˆÅ{ôÕl’ˆPî%m\„_•¡‚0H$ˆ ŒÍ[qz&5†f£ ‘”SåYjɤÅʬ(ÀnøHD¸f®ìJsùJYåEC˜  Š®äf ý  Ú'r)W&õ!“´`s‰ 4…< nTäÀ&%  ÌÊ<ŠãiD]Ef%×rõ‡Œí@v}ÒEàÀžäcÀ„GŒ¢ŠyÞh¢çÀÐ_²,€4†Þ¨>XŽêÿ¨»ðèþ^g&©a©‘² ’2©” ©“>©aM)–f©ØXiaE©–~iýU)—†Ì` £ÉaÔ˜a¨ikp—bÔ˜î}4àÏ`„‰t$H–.)˜n阒ÔÁ˜ ¢i“~¦ØXc ÌñÁÅE @{ဠ#þ©¥ºX  *\ôeTå$è\N P‡4ÌÍŽÙCpÀåÐÀ›Å™S$E àŽî°*dIuÀÙÛ x M  TT]^(ä¥&+úe*3E¡OvÇw„Ç 5D*R(@NäÀ À£¤Ò¼Ì ¨ÑGPkGì@<Ñ ¡ O)ܯ´ÿפÎu|‘]0Š-¡²þ©˜2«8k§^PÓ%É“ Û&áÆ%5êCØÅ¸ièÛ%¹@¦1Ô!R½Æœ„ “]“VJ©Ÿæ«Sð«Í §VÅAÕ¹ÁÀ1ÅEè¹£$®(RÌÍ“d䒺ƅX8@“èiqY”J¨nTG¹…ÈŠì¾’ìŒ *oBëAST™´fÔŠ…-:…]\•=U\¨¢k¢¨EèÞE¨ÓEp@Ë&m².-Ó~ÆÁˆž&ègIà!M­o‰£ØÚ‰Åçé"“ àkÙÍúGqA¡tDÙΆt}’u‘†ŠŠ (ªÛêkÜÎÿ`½\^nb„¬çVD溗2Œ€¹iè¦.‹ÀíèŒê¾nбnëfFé®íΗìÎîeÔîí* è– ÷}Ÿð†ßðŽ_ñ–ßñž_ò/ñ6¯ñŠ®î6 ïö.õ&SîF/eLoõn/„aï5r/ø^ØõzoÄ…owýn¸ oì’ïGš¯û2Øø²ïÁȞܾ/³0ïó⯑°¯ÇŒ Gx" \ÿ°³ù/Z"ðû©ïìÅ/ùj/Op¼@°÷J0/†gðÒ(°hr0çp7Üÿžc/—0 ¿H Gï ·pòÊ0ÔŒpÁÄ0 çðgÚ0Åèðpû½°îâÿ°q ó°Iq‹± ñì±Gq#q»@±+ ¿Ÿ·®_qoñèv1gqƒq抱—1)´±€À± ×±ß1ç±ï1÷±ÿ1 ²àà rr&²"/2#7²#?2$;rÃñã %#2"/²!Óñ%G2#@‚2(r%oò!{2*§²*¯2+·ò#ÇñÇ2ßddв-kÁ-ç2.ï².÷2/ÿ²/ó.ã-³fs/Ûr,ó0ÏH1#³1C³4G35O³5W36ß²,o37w³738‡³839—³9Ÿ3:§³:¯3;·Ä³;¿3<dz<Ï3=׳=ß3>ç³>ï3?÷³?ÿ3@´@4A´A4B'´B/4C7´C?4DG´DO4EW´E_4Fg´Fo4Gw´G4H‡´H4I—´IŸ4J§´J¯4K·´K¿4LÇ´LÏ4M×´Mß4Nç´Nï4O÷´Oÿ4PµP5QµQ5R'µR/5S7µS?5TGµTO5UWµU_5VgµVo5WwµW5X‡µX5Y—µYŸ5Z§µZ¯5[·õP!ÿ STARDIV 5.0 C;Z&;opensips-2.2.2/modules/seas/doc/images/image110.gif000066400000000000000000000513501300170765700217770ustar00rootroot00000000000000GIF89a>tðÿÿÿ,>t‡   3 8 =###+++333<<< CKN O!S$\'a(d+k-r/v0w0zDDDLLLTTT\\\bbblllsssyyy4„6ˆ8:“=™ ?ž33™ @Ÿ A£"Dª#F°$H´%Jº&MÂ(OÆ(PÇ(PÊ*TÒ+VØ,XÞ-[ã.]ë0_î0`ï0aó2eþÿÿÿ™ÌÿÌ€€€ŒŒŒ•••›››£££ªªª´´´»»»™ÌÿÌ™ÿÿ™ÌÿÌ™ÿÿ™ÀÀÀÊÊÊÓÓÓÝÝÝÌÿÌÌÿÿäääëëëóóóþþþÿÿÿÿ¹H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊûÓ„&L*@ ¸´Í˜åÙ»xóæ a-—ü¥"0B„éÚÕ˸±c””Œà¢¤D„µM"À%¼ED„$¸t†Úóƒ[&y Á„ÝÌ› vþZn š„0ÍàbB@\†sù !8AæÎßB‘…ËêÖ‹ysö ú±÷ïàÿÈa ˆ*—ÉkáRÅîS¿Ïe=—H¸X1°þÄõì-&Ð|¢ ú°…¬×^XAß\H(Z˜š@Æ·_uG„°ßz®aØ`€\¸ax5àâ‹0Æ(ãŒ4Öhã8æ¨ãŽ<öèã@)¤ŒÅ‘YaB\¹)q€ HðÀVP©UyejGH ¡I&P{NB)åŠZbià@½)a€™$AÊU¸¦s„'A{ÖÙœíQöd”S¦¹"‹3Å¢Œ6ʨ¢ŽF @”Vji¥“^ª)Rtêé§žr ê¨RpÅ©¨¦zª©ª¶zXÄÿ*묲ÂJë­8¡ë®¼îšk¯ÀðİÄ;,B$«ì²Ê"Ëì³ÐF+í´ÐPĵØf‹­rqÛÐxY—)‚A[,AÂY¢©XyÖMÖ'™ç"”îºnæò™§@ZØVŸzþÛ§$1Šýt/»ˆ&éÄNü¨¦gŠ1¦¤Ž*jÇ¡ºª*«"£jë­´žŒr¬¿ÛkË.ë*¬±4;K­ÍÔæ¬óÎÖjësÞy¤}å–…‰–Ej[°ÅÒ¢ °D½ „6"þ4AP7š¸\P¡ „~Q‘ŇXgÚÃuXß@g§­ÅÑÕÂÙ\Ú¥5b2ÿí4‚EìRÅ/J¸ÅoLiâ r©‡\òª“›¼rÊ—×óË›ûJsÍ:ã¼óè¤/ÛóÏÙ½æCãt™=ð@ Ë5ÁµGðî!€Â 'qÀqgÅ>ûs¶ã.×’è»fÇ3a;a,ôÍ¥}½í‡)uÖI  Ùé}òä.8K‡#^øÅŠcÿâ‘wê8È$—œ¿þ™×Ú?0Û\]6³ÏKtÑB`阳ӡîZªKŽL6t„Ú<$‚<Ù‚Ö“öQì}†›ßâDx¿Ž•TûsU Uø?þo€ìœÌ X,>ˆ Ìá ˜ºƒ`P%KšNuÿŽÄAÁyPR „ãGÂúA®~+YåVÕB•­ †Á’¡ HCšN‡`œ–ÁñŒ;9¢£Ô¿ù-qSN<¡Ç¦Å‘Uñ…ZÜ¢¹h@/6+Œ€Ü!!èC3¢ñ-J¢“øÆM5Šq¤ã_uÇþa‘s2äãçü˜,N’tcD¤’ºXO"'È\¾e€ •R, !‚¬‘7aã£)Â(4ÒRrU0?UG“MÒŠWÄã÷HÆî“Ð åÏF颖˜2lYå-‚¤‚$&!ÛØù†KœèÒp¼$á##7LÉM®˜–{¡23ÉÌf>Á“ž„fèIÿHƒTÓ!(A”ð¼Ø\§D\0 j&P%t1Ê ¨  ›‡ä€RrŒèF;€…„f›ȉ4iѨ-G*AÛLÔy -gJιHþ˜ë|\;í'I:V2s—äUPghO|ê3ÒôY!r ¨=Wó@ìƒ!èj tC…DU@$MΖ9¬\hå­"HE±¤%KµÊÕ¸(P2E MÏySù¹1Ž‘¬<©(OKæq¨[´ç=ŸyÔ0&U[K½à@ÊD(…i1]úRm”3/ˆ®n ŠBJRк N‡i·Vg¨–†tœ-Uh…Ú¼ÿžd¯étcNñØwó§—C¬3©X£6Œí¡?‰è–z¹Pv™¼,ÛRÑâu.s1-t—{JÓ:× µ´å)룮¸„S.sí®(ó]ÛÞV‘|íe_} X(öT°À½âaëÙLã7‡ÉÝ–™ 7¤!æ«Ë¡ÚÂæR¥¼­N¶âÖÐ˦Åg<(ÛkWÛÒ!zÍiàe©r Œ!cÓ½%Á­Må»[öV¿-,P÷KÜ¢2ö¿ p?9 P‚v«!žhB@dà7 ùN™Ýõ OÈqy/\Ðã­”´±ÄÞíˆó€¸ØÒ•ke“wc>Ú¡˜$*~ß|}Ùb¾Xÿdƒ¥¤ŒƒKãÎit Ä1€ù ´k„‚ü³@*xæï¤¹pkžïNŸÈÎû¾3¿(.bïl,ÿê”|æŒ8DðqZ3ž.´c8ÿ’ölŒ-9O;ó·‹7¾ôètÜgQÛú%¤¦˜©ã·è^;šsÖï2kÜßXËzŸü4ä­—’!9ûÙÐŽ¶´§MíjóˆÙØÖ+¶Ííns{§½ö¶¸‰Ðk(Ž[Üåfç¹½ î8®ûÛí6÷»É]_Î{ÛÊnøð€µ"H$³È¸u´´®õÿN+K+2Ëó"äšÙîÈ?“soz³ÓÝóN·½ï­qüU<ÞêÎ8È7þðãÿa‚«Ã‡o$œZ x[TÛrU¶"Ú|Ä#~‘MÓéã¨yÉëíqŽýäF¿¸¼K>ò¢=èN/9FBðoÕ&gˆ µ¨À¡òÅf6 I)d#s¹ C´ºAp‚æPa]e-ho°–ѱ€£uKsÀgQžWäE Е.ôu›…ƒ'|Ô õÅÛEGúÓ/y©_D ”SÏ !Ùeë]7ÑYS4§ÔÀ¼ìªuÒ@Âëϱ"A‡!ÁÌ:ZMõ7j]ˆ°Ja’°‡jÈÎ%Âú‡÷^&Gˆà“®SŒO¾ù…g|ä=–xÞ.ÝðM¯ü¹O}Ž_dàª<€ÿÔâ–AIIN—­×1dÊž©µª?‡f.~ò‹Sù™‚—“P‚ÓÊô¥µ^ó'é—Ý”/1|q€ ¡€È×Ë—qÓ×}Ïg}$W.}Û7ÂÄqÙ‡xÈ¡‚r=·z‚Ä^|²0zúÑ~íÕ.ør^À7&ëUK(¨J §Jˆ‘‚ü×0tƒ¢!€s‚0Ô…W1R5S •}SeRÕW&Q¥¬PÚñ ÷*‘oˆã|ÒGy ‚fHb(Ld¸}¨†(‚öC‚ñc!@P{6¼'†zcbJã7^c <%0˜‡~x‚ßÿ´:Tàƒ%@6ä”]c¶b7xÃG°Hðˆ"¡V®|wµT…}XŠVEzôAµ´UacWV"ªˆ °|&†Õ—ˆnGw†kȆ¿Œi‡÷†ˆ†Ðw†‡&™ÇS0%Ï#;@ÖeRe %P>ØX;Í;RVVk%ëauÕhzÕÎ#„‘¨´VVŽcFð|—dKF.ñ8j|n% `™&E[ÿ×R­ue$¶îg" v3½èfׇÇHL©‘ÎÈ‘#á&rr8‡ÌhŽ÷x‘^–€Çgä3i¿·@ÿ˜Ó5Öƒk7Zà.rUaÈ‚>)pês[É|*¹’ìŒÈøxR¹Œ)9*$Ù†Éx’¥â”O)ŒÏˆJps“2Å€¤˜a|à Ö`œˆ[Óañb:ØR³å–Xb[C&@Öø…My•c¸‘Tù8BÀhP•'†e2‡I˜è¦Œ)˜ƒ’,éw'1©a´³Dö;a">ÿøc§¤eÊc‚D©¢ ޳ƒ‹a s‘ Á˜"‰•ˆ™˜ßV?É™ÆØ‘#蕤ò˜ÅØmg˜%9ŒÄě솙Ì鼜Ÿr˜ÇÙ˜ø#œÚW›¶ n *Ö‰›ø6LÒ©•½ÿٌމo(á€6ár͹ÏI™Rð˜Ó‰n¶›×Y˜#i?ÊIœPÔ`¹•Á Ÿâ©˜¾‰’wžf9깞²¹´iŸ¶y›õ) ÍÈŸ:ŒÂ©Ð)Lj•؜əŸøV•“Y ݳwCdQ1uQ¨L¨;¨vµ!ûvWhK·‘‚wÎÓ¢¦!Lá±¢ Jí¹† ú uÚ ãj™OŠB:¢ÊÄ8œ¹¹˜tHnC%ºw.Çeª¨|R¦§W"ç‹]Õ-W¥ bEV«ø6Ä‘*R¦1±ýæ… p :GJž’¤ jq”GŸ¿)˜Ýÿ‰¡î*Uº¨WZ¨•)¥K: ]é}A0”Q„] %É]¡z\â%`ÒZ3$„…A­Ê-™õVc%XRª£êU^ NÑ«X1¨òiñé _ùžVJ¨È¥å‰¨¼©™Êt…Ú¤Jº¥˜ê¤Ë¹©ã©ñrY9é«*HÒå„“”øÒ0“št¢/ÔCe±Ô;9Uîò­1Ñëçcš1wdÕ$Pw«C£Õa…ÙÂj¬B¬Õ¢HŸØz©”š¥ÍÚnÐÚ°ßy°Ç:•z±Éº}7SE7\`7"§H‰'ö‰¶‡¾¡`æ4²ç’E2.bó5îÿªl)Uƒ!si‚((ëp"€©¡Š_ê-m±¦œØ¦u`$$imC2µ?bµT›#X[µYÛµ6’rÜCß“¢«YefXvÇã™E6¦éck—ëeKÌÓ£îºJ½ó;“ñ¶¤ 0AHv¸Š“ÿrKñªr µ8}š¡¡Ý¦¨äÙ¤ÛɬE'“ú¬ìD±½æ¨Èi?1Q“+¨ß—‚2!S𭜪´:U3ç®O†ŒË¡òY©KœÐÊhJ± Kž—›¹&”».ò¨¡â¹Ê*L`‘oGaeƒ$&«4áUˆk‚‰0«®±»¸fÈŸ¼Ë}´»’;˜UÿÊ»Îꋇ¹n9ßkBœGÆ[»ÄT¤!qŽýe¶Ûãv®‘‚sñü°°ë²›½ë°+©e¾Ëz»ÖZÀ‹j¾‘i˜ŠBë Eík°¿¼: Úx”K¼ø©¥ œ¾¥2±•*ÎçÀìLJÁ\*<¥œÁðÀ¾ÛÁŸûÁ–JÀ ü°9¼Àäët(Lœrd¨›ÛÂÅÙý‹?p‘‹2Ü4ü”6|¼ÛŸlY ÂIüÕÄËËÂ7ì‡:¾(ô.‡LüÄc‘|Ǥ lÆ ±#ù˜YŒÄr\ÅÂèÅäv?Ú˾FüÂ{ÆR¥Q&ª~lÿŒÊ›plŃ<ÇqœÇžZLÉ&|}||Ä;œÉE<Æœ<ÉàÖ±OåY¹§!a‡Ùæ²CöAºáp°Ìžn¼Á„¾Žk¹ÉÓzÇtŒ©ÑJ›ü1» È ,È:Ëœ¿³!€CN#¬q¤ ‡ËF\Æ*Ìÿì»ÚçÅ'Â8¬S¼¹Ú EËœ»ª‹d°T%rG£A…¡uëwM8 Â!"pMx=ú¼¯Î¡ó2ꜵ¬ñ©Û+¢\»Ä*ÎJÅžìŒàŒ,À<¼Ç|Îì”Îb9v,ú‚q'@{íWp3‹ŸwÏ‹±´áµŽZÒ”ÿQU_zÓ¥ W(-YΉj Ë-Êú¡ØìÁñ ÎÂkÌ]¼­S}J‚MÐoÒ¨:zV€«U`«‡².ˆÏ­K'0V‘a²­|bù[½% âÖÖÅøÓÙ|É%ôÇ•¼ËɬÇÈľÌÅÈM­ÄO]ë<%`§”$Ðô­³‚éç‚VÐÒa­J$›:I®—ÝÙ-”216›DÂuÇ~,ÉÚ[ÂãìxÜI’Y¼‚m×è\ØÑÌ1Êa"0.ÏÛ5t©*í‡ëˆkâ6ëxÿ’³\°³ÈýÜj6Àý4„(r=ŸZ̽ë Å ÆÛÿ Ã|ݽæk¾¡,Éê;Û¨]Û¶M®±V$ 015h¶oKA¶IÐßXša«&à\l«·÷ëZvœù¶õmÍØ«S ԭ­×ÖŠÀCÌKÁS™,ðÆ•]ádLÛ½Þ7žIqÝÓà§ÝÍ>ÔâÛ½m(\Þ¶{ÞíáN â6!âHAâXz¨ÚýÐ…ÉІI’¬máâýâ,îÂèÝÍ„mã·µ7²µ^+mPî#SåÎâ"UnåZ¾#L¾l: Í¥ÞÉîâ›°ˆŠÅº\æ;Ø€¼]þk\oñ’X#„=ñåæ×:ä]æôä\èÅÞâÆú½ž¹Ûüàl>ê‰þæï íÜô\®[Ö¨ºYîÊ“ µ"èí ·Ji~¹:é¤Ñ2æÈ¼î„NïÝ{Åkžé˜;ïíîî¼,L¿Å¥çúxÍÕï1®Ýª®‘H]üî'Þ®=ôï1dÿì#Ÿåè=³.pŸidÖ‹v·uõîÚ·4oË6Ï,OêxíìŽé8Ë"ÞìhÞì<òù÷$1ãrr@‡ïÈ®øŽ)œŸ¬àŒ¾îàíñbÞ¡bù#A ­ùåþ,¨ÿø†®îIÜÝ ïb‡Iû’ºÉ¦Ïöjïö«ñ•ïúˆ´ùÌÂñDûß›)>ùàëûÊǘŒ¬Âýöóö˜ÿ.òÈHÊÿëÌßö¬?þ[*軯‘Ñ2‚iî›>è\üÎüßFáO¬ŸÞg~ïó¹þöcýAD ) ,(„ B†R„â†P¬èPâÆ b¬€ËH’%MžD™RåJ–-]¾„SæL–h’ÌòàæN™'xºÙ¤¢rŒ2¤Ç‚G‘NÂÔ¡T§O‰(e€èV®E¥±Ú‘©B©ÃbÍŠÖbX±Õ~üWî\ºuíÚÜ™Óîݽ&ƒ²Õ(Û·©²%"˜ia³g©v…üµ*RÆd;¦Êx2eªTû~Z´LF"h2²I¨ŒáÁÿˆ-\’<`B'—Õ­_ (BÉ­I˜.BBj.À…÷vÍ…x„Ç¡¿n^¢x“$€}pÙÍO]ŒX±Û˘=B†ßøéùõËK<±½UÍžGÿ0À¾X‚ &"à" ´à¢ŠÝä¢$¬0€AÝ\°A`b¤%D©¶’8äŠj#QAäâÃUÔp$R£Á¿Â/¥ü#o?¤,[ÌGÎÞƒ/²û’*«¾xÌLHŽú[L@)§¤2¦ ¶0€ %øN‚j;B A cܲË´øËÄ–‘¤¿¢ J @øîK7á´O¾äâÊ,ñð¢ÿå$É Ó³ ¬üœ4¯3„à;r¢$uÏ-̓ҭ*?5T¼°Ô2“¦€ ¶$FPb„‘XTÂÔ’•n  ðKD~)Ö“¶°_‘ 1Ë<ÔPe—e¶YgŸ…6Ú‹’¶Zk¯}–Zl¯Õv[o¿wÛPÇ%·Ðb»×˜„LS§,҉ב²¨m â 4 .¨81Ð{éý7Á|°‚`ìmŽ\\€8b‰#~xb‹Õq 7æXc9ä…¹d“OF9e•W™d‘/²8æŠc¦X¡:ÆyŠYæ¹gŸYƸ\¡‡þiÔ,y“í¤ ¡éŽSBÿHØmµ”x$& àúäL9B­Æz뮑¾z醥ËR7 =TFš'žyî O çŽw6ÙåŸÿ\e¿_®Ûî¹k¾Yïù¼qÇ_&:rÉ'¯‰ršâ–ópŠ5wán¿_ä$DGbðÑ‘8=uÒQ_]uÖ_w=öÖgüu˜9/\f.l^)oÐužöàe¾xâŸÝsË—gžJå›Q¥Üeƨß/¹öÇ·çÙtÂ9ï¼zÞ¥¿žqîÏïùyè×g¿ý•ŸþâêÏ™(ìGùtô÷™(•o×\ü$&’ñeìwæã_K¦>÷5ÐÜÉó8ÀùFq[aœöB†<ÿã σ^íô2n‚# ¨@šŒ„áðⓑЪ†-¡áÚ^r›À#1€¸`€\­C¡Rß QXÁÞ]p+¤óÛ[è8 ºP‰á àHÆ·••ïƒãÅ(¼ÆÐŒŸÁ¡lX°ÊÝð%Ub½j˜…æ‘ kœRÁ§Dúgéʤ8Åž‰°d§+¡á²¸;Ètq…„laÏ8I™ á‡LA0™À;à)Íi˜”ä@€9#éÎweÔ¨†5Ñ!Ip„ Qˆ¢ã§ú¸G&’¯~ðáYA(LâUqu K$ÍøX¤úQq„$ú$IIjVÿ3r»Ä]/ øÇ"/š…4dÊ’I=š1“+Ž4¿¹½iZÓï¬6¨M”8“cæä:SÎ Z‘—s3'QÐùÌ1†qÃ,è‘ÐNx6Ô¡áÊ?Mh=nâSeÄLè0ù¿+žÐœŸ{äùºN†>Ô¤'ýÉ ;JO¼ý2 ùÜHÑ罕!r¥šc&HÓ‰>™~³¤(jPò’›N”~÷´è1•ª²ž>Î5]ê8åÀ"é” Ük*!G÷S¡vÕ«r"ªDíFÑŽuógÄCBVÇÏ¥ú3›Y„Uõ¶3ŒÂ®§u­+2¿ºW¾ÂD¥bU&Y7Lþɯ¢Ûè O&Uº‰¯‘ÿëö «O®öÕ² ýë[ªB¤Úï~0­bE6RÑ´>³)`uÇH.Ê5gêì™Z}zYÚÖ6³ó\$g+Ž™OŠ¢•l}öÔ“W©Œ¥`nÏéZœÁ–e“]çVk;ݾÞÖ„,eîÞúù8ášö´CîærËÚ–>²®ÝEhzѺPê¶×«* W|å{­!Ä·[óÅ/´ˆ’_i ¡¾ü—{ TøØÀñý/¸î{à/˜Á f0¶<ᇪTŸ UíÅZR_/fo£gQÇz³!씤Fñ;-LÙ ÓmÃö|mö@ܸð.q¼Ò+±‰g›bSrŇU/éDØ• !Pÿ]8¼äÖ‹›-2’3š×ÊöØÊåZ1I[<@–ÆÍÅßvgœ¾!¯¶È_Ö¼ffÙ§[¦X‘eÔᙣpFa—ÒhV™Í–’›ûŒçÎÉyÏSàík‰û:;“™ÐÊ3ò¡¯ IéÚÒÌûñ”5]f §$Ò‡ölYƒ©é —:µš1J>æÞ’šÊ—†5å IN»ØÓrÒWÊʸß6zeNæã­qÍgZÇÚØD›5!kÍeaï¹+ƒÅ^¯}ýëeÇù$Ÿž3±•}ln;Œ¨®vžUÝEfzlÚ4w¡M‚ílë¸Ï݆·.¿mjWcÕD¾6¹áºQÓÛßav/ÿÙÝî«29½ìw‘5o-ßÛÌ%a·VÊýÙ¶žÛ­¸…òÀ Y5+Üã¢IöÓ´ˆ4ÚŵøbGNo<ÆÿxÌ÷òH>:ß~ ¨Bú׃\Š_dË]æhVZæG§K¦ÿЕ¯û$9×yhEšÀSc|¬Bzš}®P?#Ýë4gaÓYŽsò»…èuÐçߣr|Ç^‡{þf‡wzì&Ù7Ñûo´§=Üõ¬Ûß÷)ÅQFür‰^‚ vŠ}$ߊ¶]×8èn™Õ‚ºu+øb^JÈe ßÅxÞ–GÞÈ•áo‹õ­¯;Ä2¯ù&š·ÞïºçaRÿËÃ#Í7°‘ mlƒ› ù–ÎIBqªsÆ·ö›ýT¡ìKw÷v~ö1§{ÑñßÀ`Ä".@HB²¼2D~¹H:ÌŸ{Êy6³èK?Õµ×±õÙ™áú÷ñz:3‚À @>Â.îÛ Þ½>É021“Wщ4Á@ Š=½6‹?ùû5èS¤éÛ¦«º«ÛC«2«?Ú£¾t"À4¼1 Ã[@Y! TQVq4¡A’ -ø Bz«˜l¬¬§ë¹A»7#ô¿òaAtA„Á¾=9?ंa—‘pxá‚#…‘‘{ ‘}1#ÆãÀÿ—‘½ëBÂòÚ)ýã3B ÁgŠÂœÂû«ÂÏPÀðó=¬Ù‚¦ §Ñ©¡WJ72ðš4ÜÀ5D™"tC*„Ãg’ÃÇÙ*‰ªC;LB(ÌÃŒ=fëÃRÌ¡ÄùsN/KÌ.Ž©¼!œ<{Ã-V| EQ´:|3Å*T:Ô“ÅäYÅ]|¸Ô›LœC^bEO¼D<ÌÅ=äÅ^D@5LEXFsEÈ™ú±ŽºÆ[TÁgEk“F¤FI´Æq·Í똣«`¡+ºFf|ÅÞÊÅTGz,GžÐ€#` Åà %h b‰Â#è9Ç5œÇ'ãÃ;lGŠcµ ¶ÿ¶›«{„ÆbÜǸ(Ä|yD•P<숀*#— ìh _„G`d†¬Hvܘs®°ÓœÌ{Iü ÇPÌH»Ûȸ€£Ñ#H:ºÜØ ‘0ˆ€ Šá’ŒÙØ‚T¥^áš¡¬!í0%ÉYHù«/›³HÉ1‰¤µµÃƇüD\Gbäɞ䉨9‰,0::‚@?.(>¼€€[² ¹¼¥Ú0?$8žœË0”•ù—·A¶HLÅÑrÜ(¿‘0Á Д%QÌ)̺ AŽM)kNCU QMÔšÜ#`¥Ð“®d,U‰EUŠ­X˜ ‚Åü“-NZ‰Í3a“¬ÑÑz-‰Ö|¨9°bå´Z‘` ô’ ô+UR­ƒÇ´Ê§ÉT"ÝìV.5±2…*"W`-g\Ú™€)’NÕ«µ M ´Áê,\b [Á5ÜÍ’À ƒ½VÎÛme4£¥È =XM­pÝ\À‰X½MÖ¾] +ØB*=¹\]™ýËäåEÜ&€ ½-PÿØ AÜ‘¨ÏÚ5uÉÙ¡ú\³K ŸýYš±IϽÜo ]”Û¿£5]¾EÝÔý“–ÅiQ*Ð p#•×sá‚ ×æø¾ï³•Q}J¬=Á-Q:ªM²Q¹SÞéåÙÂz[ £Å˜™½ŽZŠó/Ñ=ŸÒ]Þ½MÕíå¶2BZŽFÍ--ð2K‡tÏI•ÌÑ.Å¢²¼}àÓ•àX£àÕÀqÞç˜èCàå¥ÐaåTì-aí=aX“¤ ¦^žJ™­«Ìäš[æ·>¤Çq`¦TÎaKÛaîÙ²4`!ÞÒS,6™:¬$ÞÙ%Žà&n ‚ŒY„œVž(=ÿ¿ýŒiâá\ÓXÜÖ)¦â¶â×2bˆ%a%FV&c•IÇ…Õg¥‰3– g­ 5†báÛ#¡ó<Â!žãmü®#nà;îâ<þâ=¶J¡¤£¤\J’ Ê~m%ã#äP åuJá>£å¬ ä3¿å;Ž”j§5FX¬Ò*dza-­âcKuÊ`$¦äµµd¥Åd™YÔ´Wþ ”9 Y‘Ýg¾ÙõÓ‰À,‘ ÁËÀÙ@é÷C<›çä []É¢»,c7­G!%RëåÕMæ7ÝÞãììÏeÖ¡²å’ ä~V“-hM2i\ Z ÞxÄ ¼ Cñ_ÿ<V!\B©›ÌEVçuÆÐz”É,þaÎåL9öe6fLÆg~íœÜt\R¡ÜÅmÎV‘@Êu£òûÁY ‘›ŽhÒÔhñÓÖûií êž&êj9æl¶OYN<Ãs9^håORyêáÝãC1tÝ’ Š\Ò—6˜E[¿ªå=JRznÇkáùË'<@ºíá…œ£V‚üUf“ ÑÛMšþUiEÄùŒš©Ñ‰Þ_¼hļ¡Ãž 5&ë¶&bn¼°´Vë]¾Ð¶$æ+…ës=j“blÜ)ëÌlÇw$SÖï\¨#U&Ç~ä‰íÖ–ÍÞì ³’ÆväÕvçöኃXÿúkÓngÜ^CØŽmÌšmÏVícuG}’lGÆÞ¶l³f^¸Žëáæ1 ¦í*¶íäþ¦åv´ogéF¸Øîo™ØÂýŽKif‰4ðj¨Çn4KàvîŽ-µ;ï‘vpÚì¢,¾uŽåÛÖ IçPÚ<Ö`ŽuMåâ K2L"PNBÊw%åö=å°ÞéÞ1ovή?ðöž¢ð\ðß>ñ:j¹L?hŽpnY!‘¬¡‚,ÁY+ßæÿ’¨%"7‚Mj6 l¶ ë²›Ïf.òí ¾[ÔûÑ(÷ÖžoÓ¶ï>4Í ìç³Å@éLfvƒ–Ú3!?’8-ˆ€&&Í ø‹ÚdÍœåBð¢soµóCÕí(¾<*­dÌîÑ@¯B™ÖÁ_ D‡MfÞšvõG‰8Ñ-€€xDýð¬…éôô õø†Å[`Ý©#!>wë>qV‡Á«CäÅ—¥]—ð/o1'Ck7‰$8ŸX˜M7véô¤ût õhŸPÿýÛº¦"q%ïóèžö—€§©š¼ß³ÙŒq°°a_¾V›ÿl“°Ä<5RÙ@àe§!vx7vyGöÞôóÖÆ`R_}÷í~/OÓ¦o£o“seš÷R{­‹*õ¿Q+”n÷w§ö—Ÿ¤˜—™™?Õš7õ³r5öæy'Mu›·¸ zHüø9y+…zpU¨Ì 1T¿ì¬Ÿ6©Ÿz"ú‹1zgÂú/r½;jÓÛ?‡{±WÖ,H §ð€´ðx"*¢nûgQÆ÷"稢&ü±ä‘èc6R‰»_‰_8Ekq]X/Ÿ¬šEiòzè¶GöLíڮ؟åäXŽŽ}%I¤TJ¦d×ú•VºñWz" øß“ñшÒÿe×àÉüßÁbIîÕÌ÷ùÍÓNyÔgo.‘ÑknÙÚPÑzþqsœÅ滼œírÜ?=¸/7æ ž† ¼m{þv.fsýRLvèS\tP“äƒå|Vͯ­—.ô/h¯uN³ˆZp)hð „ $Èð $'R¬hñ"FŒC$Éè "G’9$dÉ”r0å%L˜C†L‰È1cÇ:)¢LY²§O‘+ºŒi&#J—2U 4èSŸCYR­jõ*Ö¬Z·ríjpË ØR%!*PÀY+á¶]—Ë\ÔœÁl’{ÿs©ò`ïÚƒw½^ÊÐæÎÈ‘7J¾µä—V-z”æKÈsæ¤HZâ雪Sƒ úÓõHÇ =™´)îÍ$uÇfìû7ðà‡g1»E€YI¸P9°%‹)©d9P¥`ò³×[r.—‹ˆØõf@e:“Ä Hp±âœË‘ÃåÏ3ç²xøl«¢+ó·H¹ÿM¼¹™€šuVh¡¸ Dj›PÖfÔm¸1U †øi¸!‡zÅ!1— <ÐDAMDÁ%pbˆçÝÁwwÁÁvs—¢‰-r!Œ@˜ˆ@Âa\¨ø€²mèdB6¥6%jU®f%•QVÿ–]ªÖšk™=!”}F¡i81X‘ƒREÛ„5Y؆è!žyê¹gC|ú9\™­¹à£ñ§›˜cÂ(YF%ˆ”•†~)i–•‚I¦Œ¶§ms^˜i¦Š:*©WÝW*ªé7h…2ˆhl¡¾Ó£²ZY›²²ç„zZ'™© ;,±Å«iK·Væ*—’m–¨¢®iêY­¶FÖ,€¹;+§¡yº°‹;.¹åšû²Rzié•í¦¶¥šîNt´ÑB%aµÖRišò¶;¯öJ%a·¾Î®´ç*¼0à §«ìda›QTõ ¬ÒùÚ:1Īi+.·ë¨Û6|2ÿÊ eqØ]J@}W`Щ)?ÜñG®¾š’Ån¾±–mÀ>Õ­·##|oÊM;½2ÒqÁvôUµ˜ÕNߌ³FûN*ô¥&I¥ëC@%FìbûqÂ!sj°…Kì4ݨ2±"Ž{…B%ñ€&°ÌE D`w/â}&Ž`Ö HBâ.CÀ·}%ˆŠåÞ¬öº¡©óEì6H’ÅB‘½ÐIHïÇu¤l3½+Òr*]2Èuëî§wØYa@ßG¬]x‚'N£Ž½ã^A% AÍÝßZ HÌž¯ »üî:2w$|¦4ybÌmr“XÈ„2«&‚ÅI4GÌæ¡–9¾*b é&<ãù';ú ïâ‰8ƒÒLg¾5̺#@ëPÖP“’)dg?ªP<"Kž}èV¾‰3=†‹aš‘ð޹s'µèü®™Pw6¢Ü¬ÙÕ 2³èS¢s•:ņQ󳣸Šiz”×E±¤&5¦O "µû T….ULó‰ºñs§µ)€:òÑE±o6ìhPJ¿Æ=àqnü[à’d¹V– J€‹ÿ $G¹–!@‘N#±D p”¶ç_=•¯ÆŒv!I]IêºØé%ŒÍ7Ö»þ°%ÿº&xáS„=#§L¸û5ŽZ˜'ˆ½`U\`ÓX«"fÐÙ–ºéæ·H,, ’óØÇèôp“\·èœ:®¤†{lä§³¶n¬ä-ǯŒ”“«¾éæëu{¤2›ˆŒbÜr˜°Hæ2œ)_¤ÊK[|­ÆÔõÁ/ù¿jÆ/…gçAo‘Éü©©C*a íÍüsŒƒ&Í7ºÒÞœ3«½¾%Nk&Yö—£]låûF:ÐZ¶4ªuçå>ß„ÄÍjÓa\¶Œî¤Í¬F€¡"`„¸oc|îpªƒ­5ý3»ìå±y™ìb3»ÙÎ~6´)ìiŸ,]€¶Í²s™mÿYn;ÚÞþ6¸ÃMij“ÛCÖ.5¶oUÓˆytÔ¤Žr9»ëîœÂ{§&w¹óŸsoש™þwd^,k,ÓúÈóN,ÁËyo}3¼Xü®w`-Aþêµ_Y8£zÍf‰ã×¶Ý·ðÝð‘wåá çÔºwBâVUØ#G·¤{zð[õm '9ÎEeòš£¼ÑìÆq^Þï)Ozæ™Òr®t?MëãSˆí­5*[•·¼‚Fwzu±zu˜KòBKÿzžšsšø\'çTÛº/¾õ¡«ØÖ ];Ä;•t°ÓýIª{Ö›\ºŸ7‹Åa{ð#yþé¢g÷ʶùÜë®øàˆíú%Ôˆ«¡Çÿè2/ü»Oþ¾›/¾ó¾ÉøµÏêû}âS·:æé­ùv<õüæïùÙGôM¡7µdVNßuqbT¢üêêöÿÂ=ør9í“_{¢:Ae¿HÊ3Ò`¢¹žæ‡7ŸÖ«tÄ#_ùÞo óKí|–Ç ú’¯:tµõçã ø°··ì¿/ÿöAiûgòµBuï=ÒDóô\Ú£û]ßà]†eØûQçÍŸΆlØŸ£ÈÕY—ô•_­9˜®íÚeÔð)TòÒu߆ ¯õ¯5U­°ŸDè_ÎôËÞ\ñàbe_B "EŠ æGŒ`îIàîIÝùÝ `¼É`_ÿÂæ ŠÇnƒÍèœÞþ%Ñz`ÁeSš ÷ýJ~aC !Eà™uÏrTé ™¸­a0]!*œÒˆâÞ!æ!r›¶‰›¢ "!¢!""æ!BÛâ Öa"Ú!FâNb"Z"%f¢&n"'v¢'~"(¢6¢° RCâLä!&^"%®b(¾",Æ¢,Îâ'Ž")¢š“@â Î/+â/b0Ò"1£1£'Úâ- ¶*òb/fâ0â4"£5^#6Z£2.#—Š. "4†c5îa$Žc6ž#:¦#'n#7&Y3>Æ3Šÿ#4¢9V¢:Þ#>æc&²c;ÒXÆ¢Èã<Ú!ì@€À€  ÀB‚£€&b$BF" P€L t" ¤52€È€ê@(¤jÀK*ÀÀ$¾€ xB¤$(À Ø! ¤ÀXC^ÀäáMæä&òc?"Ø´"äLLÀÂWì€Þ€%€XîâÞ€& ÀX&bhÀSA X'²å ~c1*ÀYÞ!¨d \€f@Aˆ¥ø%`â¡$dY^¤XÊ€FR@ðÀQlÿ@.bNeU‚ÝUâh%LN Øaœå,ÀFf ZÀ4@Ø¡]€d@Ô@Ò@0@¨e@"$b@Kb@ü0Üfh€äaRA ea"@ÄÀ0ÀKÊÀ8€x8p 'qjd 8çpþ¦s2@l§xçA À0€ä@€<%*€(¦]þ€‚êä€"hÖ@flžedðÀ(@¾]Þá¨]îch~Ýh biÊcLŽdƒRhdA ÀæE¢$8ÆÀ$R¨qeˆÚ¡¼,ÀAæ‡nÿdÀƒÚ!¤@ $æƒj'xäed§z ð€A |h|À‡B"0À)[ò€<%‘â¡@.gˆä\bh  @}j€ Á¼€*€@<% 0À¢æ À(@oÎ@•²æ^6æ@¬aÊfdÂ[Æä!¬æ¾ç¡Fj"Re‰êÖ‰b@†#†Ž%‡¦ÀtJ¦/Þ€ŒB&©úâCn€ P(’'_>æeÆ€„§ a§ª@8€dÀ ÀÄÀSÖÀtb@zÀmÚh8€o"€¼@bú@¯Æ@ðªj*±jg«jªxëN:ÿi€«j©¯j°g^æ¼6¦Ðe¡Vè @ Á À Ôêèäâ 8lbêB6 hfÅÞáÄê:jl2l©žªÒ¥ªf%4ª¦."@¢§­òaŒV¨*¤ d€Œ'….Àcž«—êEAŒ& ÜæÄ@j ¤@0À´Þæ£þ,p $f é €NB" äìÎ^­ 6)>íƒ"i¯â€¶¾g¢8èÙ>hò¸è 8èrâeÀ `j¤iLB§ª-h‚,v!¦h*â@è" ,ÞBæ$ÒªËÚcðaþÀ<%Fb¨Ÿ.aÞÿÀðÝÚa}ÖkÊÀ‡R€³>¨üTëµfÀ `'‚^€ŸjéAvé(ÀÍ^$aòçÍžéîÚácÒ€<.cÂé–¦@œ&ðŽítjîrÂ$|åå_úhÖ-x¦uÂëãòn†ÊfñÁ 8©`…ª§€NïönbQù-¹‰ìÜò"L^€.²íHÆæâ–åDž¤%Î îžÖkx^lŸªgK*@ÙjdåZPÀd€ (+)D©x@Üo`€€Â æ¤€x6@z£ª'*°Bb¡*g*ÀÅ&ì,@}ÂâJeØŽ-gp¼$ðòÿ§Àä¬ðQâ@Kqîø¤¬fñ†åz¨âÀBÒ¥Qz.â2±&šªûBüªb=Jâºb=ö*-þ@dæv¾©>Îq$.€€£±C•1'bâ@¢1"Êm,Á x$0'²"·¯kâ*†# +2%Wr"çq#ÃÓ#K£K²%2(«#&g27m²ø$M¢b'{2%j$IâˆR§G‚äÆ2(s°'zdèrZÀL:¥>±8€yžåœdJÒ"#“r¥m²lªò*³r Þ1ªì ²qàâ[ïÌe ϱ5«j(Ö€–Š%Ða^@ÙÂÿ$ h«½ZÀ–^ã(;s1™r¤ê¦¬f Ô0ðápj¥+A LWAs>§‘ê&oú&€+€Ds'~¶pEK´”í²ÀHêg €D¿'{âæX@ ¬g|âa¡:g}ÆÀ} ±~2Àø§p ¨G¾ÀùÚaŸš'zj&š¯òäk§!RñNb- èmèîYÖs163>Ç4/$<&dfœúbz€Jª€A§¢Ò ÜaÆQk ó¨&-€Ÿæ[6¨œ†i]'è@i4Àt@”+¬&Ç¢©šéV?®”)›ºéƒv58ƒu¬èÿèéæÀ1¿ê¢€rÀžjéSê²Oû¤j?*nzl€jÈ3Õ^iH*5 À Òâ=Wuˆ.iFª.zíü@´ªøŽu*6@ì*ò€è*a²¥q-°¤.Ú¬×Æm8Û!àµjæênk½B¨°¶+<¥ÕëG"uQ뼯q3n¾vjÎ@ÔêÎÚa ,«Lgvë{ce¤b€ÞÞ! p%¤gPÚ¡Xf6Ö¾"Uóö–™2òñÓòáÔ¡Fâróá pd€:ßáĬ¡Ò¬ÚìDShàÀ{Knc^·w-Ò (g•Ú%8¨Ö*"În-¼fÿ7qéòµÆl(*6×kÏæëmþ7ROÀI*3¾¶ö f ²­eáÀíÚ$ÜÊânKø A3pƒhVZgzöêXë!ÌÀY%ä.çéçåÀX¦]&È©‚Óµ½N訮1³®(oc:(çççj¤î:ºzï9GöñZ€¢‹mQ—- ÀÀ1Rªe‚ìèÞfˆžv€âŸÇ$QG¶~³¶R1@ïWÊb„“y™9jó„°uK¢8ò©Ÿb‰«p÷ê°úé@·äÅ‚°€ñ;»GihðýBbA–õÿöwcºò {» Û! Û°ÓæðAî°‘Ûÿ¡ Œ³ö¦13*8°DpQ@p~"ˆ c±#]Ž©@*i €x,„ãzªm²0î"Y¿¢ {¢Ãq ó'<"…¦@‡²!Ž9ÄïŽÄ㪞q(jó+ 2"“<"´TÒü!ÞúÉ[¥o cÅû"νÐç£Îï¼¥¼$³üÐ/=Ó×¢ÑÒSsÓO=ÕÃbÑ?=d=òL’l°¢+G¢FÚr!v$z†¤â²ØW½ÚâÕcýOI ºêXöÎ:µÆo.ï j ý-„3žQmzøIýÿ P$aXô éoL8sOX¥XåAp@̃üÐÁŽâAfBÜà´£ŒI†RᬈDÀ˜\È…3xR1îí-2c£¥xGØ´Ø ÷¦€.,¤ jI Íè’ %Ph’›0‘FÏd”‚¢ÜÀœdˆ2° X·’µœ '`' Ty3>håéP’$ˆ  ð3ƒJm`ɱ™3|ºÈQ¨å†¨›¨ù“P&.{ Ê/šÑjÑ"@P²h­•j[¹ðßbà ˜ Mø¼Ú{.‚4  –#ˆ ™êF[ÊÁ”€ž €ÔÿÊüõ@Þü¶_«fêà¤z¦¬. Ðkáì neK\P(*°vtc€c'Û×\zÁ€€XÀQ9@R¬²ƒœ8"°Öe¢}ÛhyÏûw9þβ|èÎ+÷¿~LzœàÒ¢4žp…/ü+0.øÃ§£„˜Yç¾;xYžqkÜá÷x]"P…ëtüVwñÆQžòz‘üãôfZŒÛ³Ÿ![L€À@à0¼*£n¤V˜ àÔ¥7#0=™í#ÇL®r©OÝb,o¹¼®î‰|Ðã‚›^.@`[âØЙõ². Hÿ€ÈIp.x|e­7_´b€À Î+v‚Ž*1åw°4k˜iëঠœsÐ\âOª7ÇêWg´¨ÚøF.@ UA@P²«¬œZXŸÒÄ2…÷-º™‡À+ß©¨gE€3üVnÏœåœ*÷ é=¬ CD™7v](e«œ|ò¾·üó6p- )A·È,£OBéÃbU“ùKbIB šÎêÛÝâxÏŠJ=\YÅóà.üÁ$€\†`ÀvÏ*,`¯Šã5€Î+&R@Øx(bQO&`€´<ŽåÍöª2ä5j@J80*¢DJÿ"€"O>`Jr`4@w‚/&øç•€ ÀÜÆ—hÐÓäJ®6ÀJ7à"ï18À@.ö ¸‚d«Äv&  âBà’F,F/ –ËÐi¤ƒ Êõœ  ð¼Œ¦©&Íý°Â•`@ÂLäÐÐ((I#xàvB()Ÿ"Bl>BFDÄ º$ÞˆÂtƒ=r`ßcß b“‚ÀàUDzà­,@)xèM¨J&À¶O ËÞÌ1ô¤È €¤jà”"ÑQ Z'Ô¾Xã {H!¶äT1 —ðêpD»¸+e¬€}¸àÒeôÿT¦Z€.úÌ ͯhB »Ä‚hÞç¬ÈàÔð*PŠ C^€7Žâ À($ì5b€p¿ ‚È € ¢‚(ã®\bK@ÎÑ(ܱRloÂ"\)l'|,r7ÂB#€Ñ) ‚_+¶T€>Ë>f``Q®Þ>¶â!{€$gbN ‡±%¡oöLlT@8`€7lPÂv«–¶b1@$#‡#±^â! ,àt4Â6–o'q£ÊŒr0|cE†ƒ³¢bÀŒÀN Ãb´²)#Àð š"7Ã72£BŽ%]ò-?&[ qXÊ—Ðÿ `Y@Âè=`9–ãfBŽr#ì,J0SòlëÉM–(Ê öÃC¬¤CüãÍÌì(çvÀB8qB L8^ 8ö ˆÜ­bÐò'€`5•/V CA,̓.7g¢Î-á28 N.1ó‰±DIrfˆ’ø†5>QÂDSqJª$3àâÆsôð ó ¥Q<`Hä95Qþä5úDŠcÚBMÖDQE9§’4QbÀn*ÀQ˜BÐÍušÍlþ3@P#~Ï~êÓQ4‚-WR8!T8‰Ó˜¶¦ù.å&ÊB.B9Ô%'twxSCÿEtå:´D_2G4EU4:L´E[îCW4FeTS\´F F±o0­++zt¶lÄï4&Ôæ&^erTŠ ¯ZgFw8m4JñGƒÀ0 ‚.:°"Éhä5BÔÒ&*,@¥bÐIƒ J¥tMuGù'ßä/ò `K.Ð"¼„B°.pxÒ÷,çõ´éÔsºfNHãN)AV¾x´°à3GŽMŸ”M55Æp42T ï²àƒ=Âó§)pk.åp'rÀ…FuU/i'ø¦¢$f€5öÉk¾f¡Ê4)'ǶÌÔQS/FM7uYÿÕEcÂS­¢&R&~À¢2#À'r"#c%ɰ•!a#>’7RU&¨uHô~r\*#‘5Y™u^“ÈM[õYu㤒¿^c)WÙ²/X€QÌF¥˜2&D3)ðâ*V­äaÙÒfª o¡Œb!T@Õ  ×+ãµb”•^E6Tœ5&¬TËâɃ:qOäŠ21$3–-û²8"¿4ä7Øò3ÑãV¢ ÖI'>Õ•€ÀQ8à_?6+œÀ*–6*š6&ž,FvjÁñÞ²boÓÂÝXöNX£=ÄeÑ3*`I§­ÙP‚7t`€œOÔÖ7­‚””ÿ&@‡\‰ ¥( Ë’¶YB–j7/¨tã#îëoS4p·qé¢p5.KäFqc”q÷rå§d)wss=7j4—sEÞ,÷sM×, wtUW^O·u±#uW7vé¥t]÷taWvqpkwwÕ+ts÷wá…vysox—÷†7y×w7w£V„Wy·x›·z»Tz±×-¨”Rf¢uhËzÁwC³w|}Åw  ?nà5PÑ·}i”|á—¬„Ä}q÷yg%zãw^í­Òè·%˜ýþÈ øF8€÷á xÅ÷€xj˜GÔ~—˜Y×Ki$xƒkå‚=X/¹ö"‚9¸-øƒGv½ÔJRm…­-Õ¬í…a8†ex†i¸†mø†q8‡ux‡y¸‡}ø…K'ˆkgˆ‰¸ˆ vv¸ˆ•x‰™‰/΄¡8Šç¢xª*»¬¸Š±ø-²x‹¯¸‹³XŠÁ8ŒÅxŒÉ¸ŒÍøŒÑ8ÕxÙ¸Ýøá8ŽåxŽé¸ŽíøŽñ8õxù¸ýø9y ¹ ù9‘y‘¹‘ù‘!9’%y’)¹’-ù’1y!ÿ STARDIV 5.0 C;Z&;opensips-2.2.2/modules/seas/doc/images/image111.gif000066400000000000000000000561141300170765700220030ustar00rootroot00000000000000GIF89a>5ðÿÿÿ,>5‡   4 8 =###+++333=== BKN O Q$['b(d+l.r0w1{DDDLLLSSSZZZbbbllltttyyy4‚6‰8Ž<–>œ ?ž @Ÿ A£"Dª#F°$I¶%K¼&NÃ(OÆ(QÌ*TÔ,XÞ-Zã.]é0`ï0aô2eþ€€ÿÿ€ÿÿ€€ÿ̃ƒƒ‹‹‹•••›››£££ªªª³³³»»»™ÌÿÌ™ÿÿ™ÌÿÌ™ÿÿ™ÄÄÄÊÊÊÓÓÓÝÝÝÌÿÌãããëëëóóóþþþÿÿÿÿ±H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³hÓª]˶­Û·jÈK·®Ý»xóêÝË·¯ß¿€ L¸°áÈ+^Ì× ¸ACžL¹¥d‚r+󼬹³çœ†þëÐe¼VÁ¤“P(g5 Ù¤ P^À„¤–X@0)£s’›DÖ6ùdLö$Y–X ad¨é¤PtaU^!Àc(€Cæk¤ÁÒâmÄ ù`ˆHÀçŸ,bèã"4oŽ’]áãªvê§îš¢QùqÆWù¯8êùÀˆ%Á Fø@¬:GðøíLô7;X3Å5MÌô£’ê9l¯‡xå¼cº\³éúÛòÒ& û´Nmo‹“V!€®º–죷X€kà×a„¯¾é®;7»a«ÿͶÙâ• 8»¾¹²ëÞu{A ȆZ£Œu¯št+$9ã''þóàÝ >x˜ÉÆ‹íœ]nÏ}нOïÉLÓ´f¾#¡O P¨Q§bu õ¨O~ª’ÿô—‘î1í2Ö»Ðn(Äå SÄ›Õ@B—±L‚‘:È C‡¼kÍk Ù2 ¬àBô‘¯cÚ© tVˆÂ†Xbt‚Ùÿ8&9` ÔÇܶ7Žàe‡<ì¡løg;:MM F$ 6–Uí„z K ¥h’FL2~˜ËB ¿4Îo>àêö"Æ1’±7TT…ö9ˆ@³«T)ç£ÍU„1v\Š]îH3J+Фc ¹HF†Ä‘Ï‚$sòM²ä‰–$aSçÉMVò!¥T 'C©Q‚.•¦œK'™FVЕ£»,'©¨çM *kiËð@MB *=B¦2uR’Xàd¶9Þ“‚em…WI¸kÈXû/§þíºÑíq+ˆËÖm—#è½$O’<œÐs=WhìcQ'Ø= zmí'Gh&#ÌÊÌS ‚R†6„º“”ïÕˆ‰P‘)/SíìõŒi])¤¾“¡ç.$Õ*Éß‚’I1t“âŽg½Ói1{³‹=âañS¡Â•gŸT T¸,º•oÐ!YXI‚*´Ú›»ËD™çA.Y,ûævªÈWËV±¸ºä¿ÒÉ܃òN§#æ¯8Jâ=qŠT°ƒu™ÿÆÈôNÓv9:“°Ì^Q¯@ô Ù?Y’]­ƒá a9ÓD‡smd"­Ó5X'.˜!ŠèÕ•¤Ò‡†±A`× Ù}‰ÉÀ›´eNÉKÐúÉx®L¤Á£iQ¢úÔÓR4¦ÃœjÊ¬š…†¦5¬=ÚP¾ºÕE¹5w€ÝJ]ïºb½¾Kifýäd‚Ú{¢V%1c}ÉišÙµ!ö½œË\SÚÚ¼®ö*å©ìLk›Û_‹óTpÛ—Ro¤Ýß>ö'íÜagçÜï–ñTï Íy—û×ø>º›ìíÒDÙ¶—7q°{['à3žó¸Ÿ}ËeO<Þô†4Åm‰…ûÒ·´>hÇh·àØÿÝx¾}ñÃúúÓ5Êa¾m{ÜÜÿ¦¹É«Øq}×\Ê>O8ÃK.puKâ˜ù8ÐßÌr’7;æÐö"KYœxj»Ö#9Ñ“Îóà`#U/ã×ýÝêE§¼è…†ÏcƾïŸû°0s‚{ÅänÒ]îªTù#ÉCÑô]éP·ÛM¢Áç°ðˆ5¼õžI®(þÝaG6µGx›ïÜ»¥­ü¯sZßD¾˜òÕüÞÅ'OÉ»ô'ô— xÔ/^ôg½é]Ÿú˧]Ò³§ýÊUßí·^…S?Œßƒ_÷¸ÿï'qxuÀã;Þ˜¡‚ô§Oýé úØ¿>ö³¿ýê‹k à¿ÿøÃ€ñ›¿üæ?úÏ/¸ÿýðw?âOÌ¿þñ¿?þåŸyÞ»W|M€8€x€M`€8€ ¸€N8Op˜8Ú×}ÖçÒ× (‚Ý÷}ë'~èw‚)x‚K°‚*Ø~û'1(ƒ3h5èRÊ'ÌçDø€=X€?˜€A˜€X„h˜„‚8‚L‚Oh‚,Ø‚Sè‚ëg…é'.7¸…\ƒ8ÈxÙtAØ€H† h†gh„Eˆ„I˜K…LH‚%…¢1…à‡…(h‡Th‡Z8ƒú·ˆ7ض·zY†ˆˆ¨ˆ 8„ÿ¨†Ȇm¸Žñ„T ‡Þg‰˜è}uȇU¨‡x8~}è…]HŠ~HˆhgˆXÁˆ>øƒ¬„c‰‘8‰nX‰pè„·8‚ø‰,Šyȇ0hŠÂ8Œø÷…°†èŠc¸Œ±(‹GH‹ø†q8¹èRØ‹¼˜½Œ‚è‡5hƒ§ˆ9ø;ønÌØƒ¯(„Í茒8‰Òˆ‹Ö¨‰t8' ¨*ŠÜXŒ¥Ø^ˆŠ\gtW‘ŽyŽèèŒÏOðŽñ Y‚»ˆiW˜õˆùIÆèwˆy†™†ÙŽm¨sØ'©‹ôè‰Ù’/8'ûx‘1©‘þ˜n·!ÿ™ˆ9™ˆé$©„¶H )”Öø÷è’Wˆ0éLÙ”€X“:¥Š8©ŒT‰ŽŽØ“?©&¹}›Ø„Dé+)‘¢XÀ¸”Ä(“NYŒP)`½·Š;ÙˆUY#‰ ”(™‰Õ–B–G)–ìg–ü˜‚Y‘kYŽü!†q ’‰)’ìH—[É}w ™*¹—,Ù—H™…©‘3©™á˜|`Oˆi•‹©“ë(‹YY‹ö"‘‰—“é‹äç—cY–²™ð—‘œÙâø™šeø–­èŠXé˜v9”)¹šÖg”Iy™ê'›¶Y›´éœ‰p…Ø–S)šÖÙ›¥ ‰§ÃYœ\©š­É—Éÿ ›(˜™ù÷œïלéY˜'ç‘£¹ˆ¾ù€Á‰ÉšÞyŸÙ‡œYHž¯Éœè ŽiI˜¹yŒ ™Œ×©˜ʘ¦)œ©™—’iœ!¨ŸËi™:‘€©Š–O9 )sî™ ¤ ¢‰¸Žñ|‚1|& (š¢Ð'©HVÁ¢*ª{ûC »I£º7ŽÎa˜8 :º<Ú£­ñ£Ç¤BŠDjFz¤¹¡›-%>~a|2J¢2¥ŒTZ¥Yª»±¤gá¥ó!b:¦d:¦’ñˆ¦jº¦lº¦Ð¦po§m:§tê¦sÂzº§|º§Ð€¨‚ ¨:¨†Ú…z¨‚š¨ŠÿЍ˜ñ©’ ©0©–J©¢šº©œº©Ð© ª©`ú¢ÿ(•IU¦¨*¦g:’wʦvz§¯ «­Š§Ò§¶ª§Œª¨¹ª«º¨½J¨z©’Z©Â:¬™ªú©ÈÊ©£ZªÓÙ‘™ª¨ºªì8«j«qŠ­Ùj­rš§·Ú§»j¨á*®¿J¨åÊÄZ¬?®ê*.Ëʬïê©MÑ¥]·ŠÒZ¦Ôš¦Öª­uÊ­üZ§Þú­~z®åЍ;®‹¬íª®‘ê®ñ ʯÍj“Ï*Sìq?F]À„<µb±€±ñU_Ñ´í!Ýc‡x¯dš¯Úé¯û ³û°Ëÿ¨7‹³{® [¬ìê³Ç*± ±ó*¬Á0¢+º%CÙ‚´P#µ–^÷‘"A²cf¯,«ªsª1;«ÿêªþJ³›³K°jÛ«èʰëê¶ë´ï±s[´§G)DzzSQR£y{õ2<œõ." ¢4Â<Ðõ<Ñ#Nœ‚`9FIB ¦+’ %T_á´`RådyÑÚµBà²j8¶n¶b[¶µZ³¸º¶l»³¿Ú¶ û³Âê°Bû°•%Špl$3÷eNWµ[¨ó»Á«·Ú2?ð’$€Ò0E2'êÃ>jò>{3>Ì«fq“-R‹1¯%/Pÿ‚$M’c›ûö3/+&Až¹²¤kºFˆº×ªº­ ¿ÝÚº®‹¶h›¶³Ë³¢á¶¶{©¸;·C»»lIªE È’Na½V`2ƒÀ ¼&÷ÁDÁb5ÁW’‘@XÄ@p"ã5/ÿs\’(óR¾G D"TU¢µ‰Æµ]ë¾k(³ó‹Ãók¶ßŠ¿²»¿³Û³· ·¼¬tkÄv«r0–e2†#Ú#ƒ69uÁ\³7QÀE ´7o¥ÂÜ+.òS]¤'!¬¹y C7e[†,V'>¤[º_[­òK§ô[Ç<|«> »Š¿B ÀD,·F<ÀIÜ¡S0ÅQ@&4sæÿ„f…,0AVÁZÃ/þbjS x1ó£ÅFàÁQ7ró?"GàF7“‹bëc¾OõFzu02liì[Ãq¬¯«[˶¼Ãö[³y ļ¼Ç}l©ÿ Ì€Œ¬GL̃\±J2D°,8‚VUðòC%K¬ÌÌL¹÷ñVEs¸ òŠ+#YÜÁ\¬5W£¼ˆs<‰‹ã8Ÿl$%ž4–òE€†„G±Ì²6Lu<Çt̺ຯÛË{üþܿµûÇs2À‚LKwÛŽf ¦qû|¯ý‰:Эwl«»»z|¨´»°þ;Ì¡ZÌ)}ÌÌœ&;ýÅ7GÃüÿ<Ë/ÛÑýŠÓ›Ëg;Ò$кúË“ÌCÒ ªÒGÍÒÎú4Ñ6}º½­:íª ®>M®W° mÒµkÔÉúЊä¤HuÑÒšÑÕpúÏ3ËÓ=œÕ¾êÖÀºÕ>ËÐâк«Ô¼² LÃAÀ>ôÆf}„hÓ· «Uͧ!}Ðpí¨r=Ä'Ýк Ö\JqKK¯nܾOý¾ƒM¶SM«MЉͫ‹]ÒsýØuÙw Ñ×M„ÎTcã$B¿áר›}ß-§MÕlÇ‹­¿"ͶB=¬tÔ_½ÚaxËd!C4¾û”½¶=wðf¥Wª¥ÚXÿª+êݬݡ82µeQ{Ò'K$Η ×=K“'[LJ±KMÁ1S55ÆTS7ïÍtä±LFÑߟaAKe2MEÊÜD¼!àñ{Î6ßÕáÍl8jH:6HâœÏ·Áà>àÁáÎá‡(F.ßž¤}Mâ ^ßѱ5'áîv("îà öâ|˜×Òð×3¼Üä}A*¾ã<þI+îwó6äç„¥1>Ù6ú¤`Agjgx'¥3jäÀw¢ßÍå%*IèÔå Ô¢PÎä›7åo—sþ”ËNTå¹ÙæF+ãOQãµ]䂿‘qÅÖ|†%çswb]Vhžýv¢ÿûæ‡åj#åcý]ªu[«²}¾–{^|Caç Žç[®æ{Nf€þ{‰^£™>è…Åé>tA~ézþäëËh&næ£éinvŸy‡Žè‹¾u?¡é¸qãç¶~p±ôrféHëÈu´^ë¡îèˆgì®î¢¤þà²{ÍîìµIÒ>í£¾MÊîãD^èçÄCæÞíºžw¼î;¦>Z¨èÛÎíªNìȾë¥í§Nî%~ì¸>ìé^ïê~ï׎ŒÙÞãoîïÿÞâO'ðË>îÿR9Äçüþêî,î¡^‘5‘²3v?ñÔî /îGî®óÊšôñÞ¾t oí _òº4uTÿN²¹8&*¡»êŠNéßóAGòg~;1v¾6”½ž.ô^é ‘ì°>òÏ콞µ£1C¤¾Ucd÷ƒòÎôïôôÍÔQ/_iìEaãÅtìõÆõ-íN[.ÏLæULtô îæéÅ× i[zë^äÏî¡7Ï`s2ŽSÎÞñhÏíÞoŸôBïîÎö21Ñ|÷— ¹7–`´ïgçøa?ðúø1ñÒžæìÏ?D?ÝÜ¢½rI†9AFêëÞø´oÙS3Q…õ#ÃèC戯s@?ëŸÿáá1öÿ£5fïú›Ñê¿oÑ £¢Ÿ[ßnûÞɨ,7‹ÿüY‡îIÿøÚÿèÃ÷,p‚ßU…9qÅê¹Þt§ãU üOïð½Ïüça+ Åù%AùÅÎý7ï¢ÞþEà@‚ D˜€Â„ >„QâDŠ-^\xQcE=~ô¸Q¤ÆŒ#+–ˆáƒ“A¾,ÙR&˜EÆ$ør¦Lœ9wþT¨ÅžC1Ö it$RJ•2-êbÍ¥©J•&V®]½>ÔúU!S±¡Ú,Û’lZ°:oZEÊÖ§U¹uíò¼Ûpm^¸{- ¸—oA·ož­«5ì`ÆŒ¾Ú˜°ßŠ€íF–Ló£É³PÙ*ÎZòãË0EçLrH„Jäš½y(ÿâ× Oß¾Œ¶n̸ÇÊ>ôg•š{§í[¹XÒ¥ó.¼ü7ÚàÍ-7Þ4oréÝZOüºx﨩ßÑÈ %Š¿4àÇÀ¶Ëßšþ³påÑñ›ÿo£* `ɽì®H?£¸ °ÁDî¼þÈ PB%:"¢x@ Ëê£ëB9š°< „ÐÁ#DhŠâ°¸‰ H/OäÑ¡Ëú1¼ {lpŠ$¢¸*¼BGÌ­D"§<ŒÊÁ,´Rª+Bˆ ‚€ŽH"%¤Pê B¡¸$ ˆà"r/«Ì2Ï!óœiO‘ ˜ xà$ÿÂî CBCø’ð|nAŒÀ"ŠDu“›ü+e˜eîg« v63{Á6HM6Ý„SN:°ót½M·^·¯êœ[ >A„ÁÿÍ—géEDét!xàvÿÒ•s‚âî;ùìï7ZJü}Kýî 6?í –„Ulr)š´ðvÀÑÜçqYžüb>Ÿ(hgb`ZȧÿfðJ!®Ì5³t­«\ ±õ¬†0J.ì‘©"ÃÐ|Ìs"™ë>f°…µ†ŸùJ(Òpˆ™ñá¿Ò?‡lP‰ÙV·ò+aŠÅiW¶”’Da\_ú⥒D1b…‰ ©Ö •SÄ•ѰÂÞ»rF±QŽw„–þðè:z'Š!ºâÙc ,ba‹pò"ÙÀ¨È=†ŒüI!9ÉJ=’’j¹d&hIM>¨“Ÿ!'A‰¢Q–ò~¢4%ßR¹J¤¡’•zye,YGY𥖷¬$-q9•]örJ®ô¥$}9L>ê’˜xÂTg”¹Lf6әτf4¥9MjVÿ“6 2æ1 ó*ðÀ›ßg8Å9Nr–ÓœçDg:Õ¹Nv¶Óï„g<å NI³—¼Ò<õ¹O~öÓŸÿh@ÕYÏljÓ%ÜhBºP†6Ô¡è$¨ ËNuó¡ÅhF5ºÑqF™õB9:R’–Ô¤ñô(Ã@ÊËŠžÔ¥/…©KSºÍ•†´¥åì€tŠ‚or hÀ ¼)* 6*x €HêR›Š쀨:=ª8[ ªv`¨NE€¾™ðÔ›[íj8‰úS|•ªbåÁ| T¡ò@åA U°î€hE€W P ­1E¬7g:—šÔDÿx§0 »šU²~jÀƒÜ ®)e àÙΆs´œõì7K»ÐRö›6€gw€TÔŠP€7a+[ÚªÖ³1Ø+N N d³šµ+^É*ÜÔê6¶qݬ7i°€ÄVW±ßq¬Añ©#rÂÖ¯àôî7O€ÚzÓ)¨miÁYZõz³½çç Îö~S5h x ßr>—Pj}m0€ïò`¼Ékx€æîw¾âœ®u«»Øƒ66»‚,8_àqnœ2Èm{Ì^°F÷¹˜*‡EL^p:@Pk™ÚTó Â)Ð@\P ÄØ´žuÁƒQLc¸ ÆÞÿ1‚½‰ƒ½ Å:.Œ a #–ÂW³°JoÚá#ÓÈqsGLÚ§·Á+§ Tà€øµ¾<Ðzõúݬ™ºë%,ô[^o~ù›IÞr»ùfÔ¹m–nn«Ó+ó0Ë4MfwP`çØä}/OÙ[æMƒÓ¿<°€YŹè·¾;€P€¸@­{ªgSPcø›—Ž+¾kƒùZµ{¦ò¢_êĪ|ôÑŒ­d9/€¿îÀ²Æµ1rÕ Ú˜9œö­7S@€j{®¶ûLo¾`ßöf Îá6ÀÕo¶€üZäò`èå2Àg »Ðþ¥¢ÿ…-SìûØXæ`9wЪ*À¬;ÐW ÷‚©w¬9;ãàÅ«¾x8o0†»zã«îøZðMRÓ€äX€É³mã šS­¶\©:Xœª¨vª›Z€øü§2öÀMzåíf²éQzIu°DÝêç$¶a´|pd'üêÍA[¿>vz|ë\G¸žÈ¾v¶o”éhïºÚÛ>wº+4ëž4øÁŸ~™º÷Ýïü|;ÜÓž¥ ÿÝð‡GçÝI"Q½‹ñ‡¼8)¼wJRþ50@G2€Íw^óŸç¼G6úÐgÞó¤?½çMzЧ¾õ¯g}ìOÏúÕ“Þö®_½L¯ÿ{Þ÷Þ÷¿~ð…?|âßøÇ×=–"™÷Æ Þùþ;;Ü-ÿüceú¼>õ-Ü“ìï±ûÚ_)÷µÿ}ðO”ü•·fúÕ¿~ö·ßýï‡üå?Ï”ßþ÷Çþõ¿þ÷ßÿÿÀÀ$À4ÀDÀTÀdÀtÀ„À”À ¤À ´À ÄÀ ÔÀ äÀŒ úÁÁ$Á4Á¬?Í(¿óó@Š åã:lAG‚´ç“Áô ñ£¾ÄAÒAìA_)”(0€A™¨!|"¤nb¾ê B,™I‰0›Ðš¹Â ñ Å›–èC;< #„™Øÿ!š %PÂ`žx)ˆ(0¤7<ˆB:¤.²¢A*+xÃDÙÃD~Q¡dšÂGcÁ8¼ˆ¢¹»t©pDOÙBÉ€#˜4D–¿Á"ø,ÈÄMÌœ“9` )x€)? D%QE+|€ÊéD$äĺˆ»‹€¡éÅ`Ì‹8N1Câ¢,¼pq4Š(( {!ˆ˜+0€Fi„´#8§ù€0GaÅŠ‰Å{ùÆc™Cãa)ÌÅæ» •ˆA,f$`qF&d™!ˆrŸ€ ÈÇÕh  Ç®x˜¡cÄ0+˜År¤ ÿ©a;”GòICÈ‚ÈH¤×i¡Es|›{!IÃY–îÉ¥w¤Âad›*˜Èɘܱ“)Øx—È–2) E“ù‘˜Ÿ´š\Å6 jˆsÁ‚˜|9–§±ò¹ÅŸ˜;ÌêÇj­Ü§ä¡*eŒ|E#d‰"P’³Üz’SÔ“DÌ2tJoǸJÉ·9É¥ÁÊ~ÜÀ(Ê‚L¿Dǽ¤K¥ØÈ)%•<•Ád¢)Ç€€\H˜©‚ÀÉÈÌÜLÄ$ˆ-*”\àÉ)Š€ AÍ òîyÉ”>Oa›#XÉÿ¾)g)Ç’¶)L,À Æ*NßL FœšM<¸Ì)—l#ÚëÛA¾`³DKꤚĢ «ÎÐÉÞ!JùIÌÂñN0TÈ4<€^P˜C¢MàÌ~¡ž ÁNüÑN Ì‹Î,ŽÙȹ©ÎÕ,ážâl›0¡Ã$ˆL­B3²Ëíû‚Q¬ÐùOç3É@ •P˜X"•þT‘„Dë|¢¼=8eTB¦l"‚ÈÂ_ô9rÑÝl,6tŒ{¬¢„ ftFšHœ!Å–PºÐÆ‚ÑF´’K ÃÙQÊ“¬Hœt4Ü”…¸RŠÜ!&…IE<)…•éK#ÿ"(B,R¨DFD²Ðš)½TǾ,(Y : Ǿ¤Á1½K¾ ÇbŒ5TR"=Äüàîè£=5I¡LÓT̘àÒaNIýSÝ,üøÒâ€Ô‡ŒÈšL‹t´>Ñï€A¼ðœP|Ëï4˜;õ =NW}ÒÆØÑM½ >åKRt˜Jø§ÙU©É3Ž£° çi„Œ“û4Ð|ÁMÚŒ PõÁ&­©ó»T‚(˜ÌnÆÂ¡ʘTå¿[ÕÅ»hÏéôKèlWe$Vl%ŠÔdMWm ?A}ÖÅ‚ù¬Ï”€ÖâÄÍ£X¿í8UTWx²×ê¸Ù-Yó»¤îëŒñàÙlT¤<ÙÃÙL»umڽרӞ¥XHrZF­ÙZí*Z«=×Ý@ˆx1ëÉY'ÛBsIQ1ÖL [mzÚ¤q[þùY8ìÒ”Áì´9z £0ÜúH²Z†Å>ÅE‡ELÉÉ—+°¡‘J*”)U[¬œó¼\š [­ÛcºÛÒÉ[T•ˆ…$ä9Îä¡Ê`å˪AÙu®<]b*]Ô=]ÿF•F\ÖÖu”HŸÃaÉÍ Ú Þßm`”ýZ¼ð TÁZíÉà}í_Ŧf³Å`­íت5áÙPÈo ”<˜P—(¸‘` 0”˜¦HÜ^Ü­m\"Ц»É¡áD$À‚"4±FšÀ(˜È ¾% î/>a UL@œ!›ô›ÿ‹œ&$¾]#ö>0†á-î –…c†Yá7ŽcMX5.U„X'\S4LÂbB'„Âù‰MêáÊ­‹@ÛÐý Mâ Îã0Ö_¨ áJÔ/tW²ˆ.Ì^°‘Q)‚e‘œˆT–^¹й…£¾c¡8YÅØc.b„ÄÃ`Ĩ¤C;tC„‚Ñ_!ã.Ò@:\ƒÒîC`á[^yS>|f÷8fALƱ–b±–§ äK¦eô˜Ž a¼‚(ENOôE8çS<5R¹æžŒñÊ’é›%ÎIñŒ\tLÅ¥¤ÒW¬ç€>I[Ä'/æc$òæ9NánÒÿmœÆJ¬blÔÆh”›f™Ìy„Å2îÀdÅÚí𼓤È^<;Ä—`µJá|€‚ÖÕ+]å]>YgKfhÂ+g„ÉÇœšŽbYäaΜ!0æ7„Ô’€i‘î›å$GÝ•¦!Þ’ÌÞ˜Vê9ýM1Þ„þæÆcpþ¥žŽ‘íõJšKË®ÜB9eeÜ·ìµ|H©´\ë5˜yµëÖŒ[w>‚x™*-޹nU0”KXŽ˜¯n nl+é Ì|ÂÒøüÌÐ$( Í¡¾‚D^]+zÍØÔÏêAP¹_š-íh¾CdtÐ]ÍÖ4‚ЖÍ~¥ß8)‚Ñ6;ÿ®uãpvè±&ëdùd• %îŠ@Ñ;áí…†lÍŒ]‰“H‰éÖˆä~¡åžåIþmº µcçömM¦’ÅHZ»-Úö kî&oà†‹†XÂ&DˆRìMrÒ3¢£ ^àò†îƒeŸ¶ÑURhnÒo­‹$›(ðÚàéàöå:3DmuißfæG<ð‡¦Ùð<Ò‹Ÿ gãçn(°“vÆÜ¡T*Í¡,eâ#¥ó6Uïüih_ÚŠÎOûÁÓ{9Îy…ñOš>óžY £º´Ë&™€l±Ÿiñ-O ñ!‹ ǤÔxˆÖ¹Þ¡©ƒª^“aÐÿ6¥ì»òVBr@ý€ÄmP¹ (p^VüVí>@,çŒ5§cþE@=/6ïs N@@7p÷Îå:î$Œ&—`ô„u?>¯0ñ&°ôKÇôK€LçôMçôNÿôNw‚Q'õRu‰¡‚TWõUOu`õW§W‡õU—õYWõ|‚\×õ]ÏuCoî6ÙJuL÷ôa/öao‚c7vSgv'@u[ouhvi¯uhÇu^Çv_÷àA't!GvMÿveuqÿthvSvk—öX_÷j·õkÇö]×ö,çvööoOöpÇ÷|Çws?wRO÷wo÷o÷œˆw^Ÿ÷=v¢=ó}'wÿbwx‡ÿw€w ‚Wwj_÷X7øƒ×õ„ôDgÚExdøL/ybŸøS¯xŒgù‹§öçø'ðøC_ãù~×wœï÷”wö•wyoyk‡yŽŸy¼«÷`ox7ö‘wžøYwwXúWŸzªúƒ/úÅ[x±Múœ_z¥'y§÷y ÿy¨Ïxx'úìYÛn¿y¯w¦û”z©·x²z—ˆù^W{.®¥¬w ¸/wÀw±Oº7û ÷«w¿' ¾—%Æ×Á7yÉu¹ûÃ/{º/x¼Ï{È·¥­Gï®'y·ÿú¦·üÂG|ÍÏ|©Wülß{Ð']a'}Pûe?}š0|ÿÕ·ûÄçü˜÷üX>z†¿÷ÙŸüÚüÛ¯zZÏ}ª?ûÖGø×~®þѧ~â¯ü‰Ÿ{æO}íùÞO{µuüXúýË |p7þr'|Üß~VOþ[o~ïÇzèy?oûêûó¿þÏ~ö_~þßüˆ' „ 2lèð!Ĉ'R<ÈÐ"ÅŒ7rìèñ#H‰C’,iò¤È&*W²\  %Ì—0cÎŒéä&Μ71 âó'PŸ=ƒJ¨Ñ£?G(èôÉH”R§Vl•*Ö¬Z·^äêõ«TbÇ’-kö,Ú´j׊MÈö-ܸ á‚­kò*¼v÷òå«·/àÀ‚ÿ.lï_ÊGLÌø1äÈ’'ƒELùòcǘ7sîì9²åÏ¢ëj-9.êÔªW³ní:µéª]cÓÆZºöb]ôsêâ9~ï·;úôê׫/ï9¼ùøåƒfoÿ>þü¼Ýw†Oÿÿ\2f~x wÿù' ‚ *F p$€€0ô–(ìæA»µÀ»!`Co-(€ò +R¨@ ¾IH¡…+P„¸60:˜Ã» pcŽ!šˆb; (9ªˆ qüq¶ ƒñY™eÿXà p1É (°› ì–A1Þ @o6æ5ìö%€ Ã!ò†§˜w‚Ù›„8ƒ5Ɇ»0ènrÒiçn4,@¥r[b†%—âiÚ©t^ @‹»)`Ã4ð°ƒ:ð € ¶`Ao3ðžxî@½áÉ©<ü©~òp‹î–œÂBʃ­ÀYŠirŸRÆ)¨ÐY›íGþ6'.ÜZé¥)h°1Üà€ ðA ½íà@¨à'‹Î†K€Œ`Š{'’„+0o2( ,70ŽÏ[ï½¼MKíqÛžf·òYœñFÞúF@ ênÄËìÿntЂ<,°C”öVƒ \š'˜ÜØ€ ‡<² ½¹@0šˆÃÂúúF³ÍŸIq• ÎÆ±§Tï屯`¦@0«8€ a:AÏ´0fp¨šë³Yó°uмÉÙ›ìêçÜJ7É3Ü 5qC†­ÕàNÚpàòÐ@Ø.LÀ› ´ ©@ˆ.(@ko8\C°l§=(ãzïVhˆ5$ÊÃñò°Á¹¢ß°y°yÉ×°²•®ÿ|%,c)ËYÒ²–¶¼å[nwÊ]òò”mé%0ƒ)Ìa³˜Æ<&2“©Ìe2³™Î|&4£)ÍiR³šÖ¼&6³©Ímr³›Þü&8Ã)Îq’³œæ<':Ó©Îu²³î|'<ã)ÏyÒˆ¸¼'>ó©Ï}ò³ŸþŒ¥9U)àHB=‘HNRGSÀB^˜PÔxh^ª€G8 ðB4!•‚L(`á !ˆ@FÐÒ—Æ )I9êщN„¡ ÄÈEIb‚„Ht" €B†Š¢BF=‚Z „NÁ%°P… ba °¤ð, !¹ÿYÍÚÒª^5«yA(O{ IlbÄ©$‘(S#),¯”À%Ì!GP«xØ„@UÀ®0… hle¯°X,$¶°q•ëE|ªÈºb!)…ÀJ—J‚@€°X0Â$P‚˜ŽÍË"p×*ä¦#-"@‚„Ôö¶ÈínM«R–†«D5,b«V›ºU!W@ pVìB`³ÝègA;5mµ´UøjXÇšÂF©U« ÆÊ“#”t![m+ PZÏæ…¾I@êyÁ*VÂ8T—]µ*†Õ*€¯a5ÂÒ* õ ¸…[*) XÁ¯Hzÿ×ͺ’Ô¦€c#ûÈ–Fõ"@È€%j‘+Dµ·ÑÍï~ÁëY›ÅP±`œ»TÆ>§IBH!€R“>`¸HŽÀˆÑ$KùEéA ½ˆö¤eluŸËb›ªuÀu=k{c%XÇ<>B˜µÜA.8!έ‚˜ËÜGõ½Xˆ¯ŽWÌc…°5ÁYÕo_8f<ëYΨ17ëØ‚b!¥®›k Û”@»³Eê ÝÛ+kÑ ùt¦•{iGΑ Z,SVó/]¤¦J`Ò€µŽy¥5Ilýê^þØ|È­™ìØp”µ„µÂRõ À!Bv#Vÿ˜À¤®=mj7ÚQú{ËÈöI”ªÇeÏ‘½H…¶´Õ $`¡ÆÂ¤ #`! Q pzç×r›Òä•J±ï2–±»šÂÞ&FÀÍÒ!(u²•^®j]ÜLÅ,Vû%Á=W‡K½ž‚zM`+˜\Þî^1JÐòq‡üáïqŠÑ*Â` P},dÑÊç@K'-ˆ\xÈù;^º6ί$@]†„:ªÛbËÑ‘^ósC\Úy–0šCÀoFƒ]ÐQMxFh驲$æK¿fÃç¸–Ž­í[7x6ãî£kDïÝ:KÒmž÷k¾ïÔá{¹ÏôÚ=íÑQËÝGNM¿[ÿEë²A‰X¨;íO<×o~¥ïàήwíØÜA÷ÅÃP*B¦8B,.ñŒ·ö϶åxE°Žç ÖAû8w•~Ò·øŸWÞòv÷©nïˆ^LŸßCøâ¾5)_éH!;ÇÂ|nÇÝìY¶cC áxt BÖ¬aÄJý\ ZVd^` ²žÆLEçÆ"n„þ`VbB@@`q_‰=Ÿs„"þµJ|âVœ¢F8WPá‹å¿q˜6š.öâ~½ÆlÉÖX¹LÍ#J>bºÝ”ŒQÛj}_†›"!DL[SMZd¥áΚèÚ|“ WüÿVA±”E¢”ù 5`Ke†-:" šâ6s£zÑP÷½Ð$š MãÉQV H?R .†µh/z…„*¾–LmÖ€µwñc#Jä(æÍí W\$WÙM!BTáÁ×X]á…AäH¶à?ú…å¡ÞIv\OrK÷Y:²!BlZ§M”I™MRcZca4ÝyÅNªP¬ 4^ÌMVcNÚ…Ç`J’ÅàH†®‰T¯QTV6åV’FWFå.‚倨$V2e4Á%yPäZ^[BåSþdá\:¥îÈ[fEáÙ%]Ф\BÓaâeu žã­[an[æ‘_*&`Fê=&a ÿ¦dN惼GDâäDbff^}^`ê¥d‚æYþeZ~eiz¡m ¦)Ffg¦PkZækrå±Ífl^ gvæEVæ3-¦f~&úæor¥c¦&_†ençenPoJåƒÐfmz浄¦VŽæ^6gb†£p'iª¦E§3§i‚"Zçwb§NÞ¥Y†gzNg\rgXœçuÚ¦~¾?zç|âgÌõçxš'bú'I’á•ÈfJhc‚ç¶Ù ,í"ƒZ(t¾'yf(‚Ц QfüÐs¦†Bè4vhwªÐTh{®[^Œ(pj§sÂè‰J¨(v kÈj¬'}¾èjÜNŽnYîÝVÿ^‘ºF¡g3©çÒhXÐhˆz^möCñ¥“næÕ¨nfÉ•žf‹¢§h¹@ö¨md©’2“Öå˜ hšÖš–J'—ø”cDiÛÅ"Š¢¥›~iÛg}ÖéÚéÓŸ*Ÿ.)ŽþÓ-…–¢®e£ræ£Êgƒžé2µißÕKȦ6¦bj§zªN„êNo þ§ônÙº/¨nÊ&ìítïÚ²,ñ²®Åêo¼’¯Öþ/{­$ù,2mOM/À"0À^ïüo¹Þïö"Å÷îoø¯Å*oóðݦïƒí³Šmõ†ðû’,§­öBðéF0Ì2®øV° ÷o^”¯½j°åvpßniûаç*°Éʯ ×/þ*® ÿÅc¬qÝúïÎ.1ï­ c.ûÞ.-üî®#¬?, ±Û±®¾0·®opK.WÆÇ"jœN1çVqµ–0Ÿ0ïŸ+ qÕ¶°Ç0 £o'«úÒb Hîºq[ï/­§0#ϱªz1Öâq$'1—±[2 ?qí2ñ±q!“p"î"Óq#—²«Þ1O²$«+ëùºò½âë—“§„! l.÷pè6ð(?°#“2«Brƒï*Ój+›¯ ϰ£q-Ó-7F¤Î£N3OX³Öas—Š''( úí Ãé7§h7{è8ãp9“ó9ƒl8³ÿó:»ó&{ó;‡í3S4 ©6sR>__ókœ@´@4 ´AtA´@'´BôEôDG´DC4L´ESôEOtEg´DßóÇõ3=siC/ôHtIC4'qtDo4K÷€KstLË4?׳ŸâèI£tN§´BótO?ôKÏtF µEuQ×´ÿ,NŸ´OtSôSC5P³´QkôKÃôUÃ4R·³‹4SïôW3õTËtVWuK—õV§3žj.X‡µJïôXuY_µY·tZdz9r[—tT›´Xß]ÏuP£u8%FbÀÖ€6îž&DWB ¶b/Ä–Ä1*ÿ¨Wóõ^7t_;4`v`ƒvPß5^sF1&X¨—i—QÍâI dÆxtïl6g»µJÇõE×5F‹6U“6ô.„DeçøIADØp?D5þ×IÙTHaÙFY›+fÛ¶Ñžk5ãlíæãÙvOã6gëvQ 6U686DHÀˆ]¬ÉãàÞbÌ{»wq¯WóiWI‚aÕÑ›½ñU¾é¼>$f³µx‡·fÿõ\„¶oË5]÷ú*Dh×FÁv[ÄZ†ox_=ÔÐmt!ÄwY8ŸÀc.ä6z·¶€·S7¸S“·UK8YWxaëiúEÿ˜Òؤ ¹U„ÙÙCU™Ù˜Š¿Ð EÔ½ÖpA¹z¤‚Câ?ã¸Aw¶NC¸[ðxŸ·§7wU’G] ÙàYÀ¤ùAš¸2˜ƒYži”‡ib¿Mù‹¯ØÐ%8㆗ùƒ¿5˜Ï‘˜S8™öCLÁ@w- VÁðÛY Lz¥+DP1ù”E7H=uG¹ŸW9jÚp‡—df#ú¡ßv¢çÅ¢ïö­õ£ƒtgTåUjy¬Óú¬;8_ëxG›÷˜ÿö®Û´XîZY.øíÚx—s¹Tv£_;£+»™ iôF;µ“ô°ÿ´µ';¹ãz™ÓœŠƒ‡ÿû³{—ûYçú±Ÿ»7¥{ÔA'nÞG”¢¶$ê>©6[ø…oÔCETa¤vG\¶BÍsWß$WII4Ö”¸…€7ú–F97aU¼LͨïìuU´<ÇÛÞmžÃ‹35ÚÕ7À‰•>ÆÚT‘<†ýwVø~Ï|Ìg9|q«Ë ˜Ëí· âÊ?<Š–@’¿¼=€8$Í'Ä%ÞX S=&–ßk/Ä÷…_c=–Š Ñ=^Ò+}: 6ÝB\ža~½btñù ¹}w9Qí_ÿýŸÓ‰ú-ÓöÙ:¤‡ ÷ÑÙõM Ý!ÀÍëyV19MNÁÊöîåÿeàáï™–ý¿g‹œ^¨™.Ôà3WAã_Ü¥!YH)å:ÒŸa‘Zª¹¡”Ù ¦Õ¥#Ä Öà;®º¶zÕx¾@Ai•Ú³ãõ$>·gîéä9í+E0|%qLk)lðdõûèŽ6?¯+õµåÄ6³“óp_6aø‹ÿö𙿜?÷÷åvj™û«ÿ&Å?ˆÍ¿-¯¿ý‡þC³þ(@`8`Aƒ&T¸aC‡!F”8Ñ!€„)fÔ¸‘cG 1~9’¤À%=žD¹’eK—,UŒù’fM›oæl9S§Bž=*ñçÏ¡G‘Mºô¡Q¤N™F•ºóÿâT«WBÅ*T+×­_Áv,–¬×²K»M{–íÖ±máº\å\véæ5{¯^¿ûþõ €0a¾n'–úVq㦎Å–<9pÍÊ1odœ™³ÌÎ)‡Fü™4̪¥;_ÎZ2XÕ¨aÜ»ñkȬ »¦½[óiÞŠmßf6øïϳë-^[4ñäÏ"‡w9óÉd«Oî[{Üì‰)—ýÞݯtòØÏ˼Ž>}wóíGÃo-wxø©¹ß×}?÷mýœßûo±ÿ óO@;ð«ñÀ[MAÇ|ð) k£°AÀ,´ŠÁ sÚCËòûð(E$©¾ùJtÃ'd‘¸ItÿQ¬eì°ÆO¼1>ƒbÔ1A½ŒxàJÈHr *b  ‡,ò&€ÊÑG i¼R.¿¤0` ¢H€* "„êòK,¼I€*qÓËåQ/%0ÈKƒ"(°À3!Háˆ@#€ ‚0ƒ” 4­È‹*ÐDTÑ(®!‚FÀ¢$ø §8ëŒ*ÂREêÑ% Áˆ+ÄLòQ±€,ˆè V]…U¦$ª+°â* öQ," Ma‰5‰òõM8íC­,µIU—¢(B‚[±³ ˆ`Ö_ÃW¶Ž8@‚P0‚Úb!•”R,âÿ÷ƒ>  _¿å1ÛnGäá”PÖ¬É$8” ‡/†‚Ž!Í|šÍ#6&è $F ÷Zlû[8a:]6ѯ)eÓÜ=ᥠ$@ Ls³ZR€6£˜"YO+=ºˆg‡³ X¯V©¹B1fµ¾´™ÿ=‹õ€*>0b ¨¡èúë‚VFTH°2*5"‚H¨T‰ÞN‚¿ÓE:µZֺʬ WŠÃ}½+0ñQü±Å+¥®`Ƀ†ó­7¿«s5ÿ<¢ËE/¦SMÿ1¯¶'­œ!*zóEËM½DÔm÷̯6—t}¡¶ÃŒ@ jÎ=CÜÇÿ‚ô’0jtYK“„ PÂPi "€ûÚ½/•ÞÐäÓCÞøåIzYeŸ_“”`sð“Þ„`M‹ }_y`EBRHþÉÂ8ò™$räCŸ·øu¯(ŒL^ô‚F®`€ÚÍ~bâWÿõÀô}) ¬ûŠh¾ÜP$²óDƯ’e T^Æ*øB’ăÁÈ=LiŠSÒ âǯ¿}€L„¢^ü2µ©N/zIB¢ 0¼ÙA€ˆqžÞÕÄÔ‰Ðv$üˆE¦pXiD#` %Ø6‹ÄP}McÓKH¦xË}ÒºÁ€Æ+ ‹LDØâ…ÁM«Zù3Aÿµ¦+­[Ò&UG߉ދ[üËµŽ Œ‹o³ gÁ(žqožœ` `Ô¬_ô’BÀÇEá^Vx%F]áøÀ¼à«r¬–²S ýÌ.0y^ÔΞˆ ÌÐd(#|(ÐR ¶ŒšÄªeMj„›ð*&¿N˜Âqž™ìÄÄ"CUÁQxZ ©³EAŒ|ÄÂ6v-Þ3jS¨'"0€~œ˜JÀ· áT,"8ýÆ·t£ñè(g÷€*ÆM“œÈFá¶ÌÐ G­RçO ÚÿÕŒv3`Ú`µ³F­jÐp™×­­óia_Ë Ø©…3d0+Ÿ–ÕŒnAÆlÚ]÷úÖ v¯¨áåR«öXm¹S+ƒ>ϺÚj€‰+}ìwSØÜìnuj € ¨`Úž°¹y@ƒð ³ €~¯YÌ.(ö¼ p„ï»ß '€ ~Zu3€¦žð…cøßÞn/¸Jjè¶ݪÅxº×}ëv§¹Û²î¶½M­,`˾æbUó :¸³ @ »Ö¨-zŽ>oÈàå¨Ý8˜q0žG—èF·µÉó+ZçvÜ©F5¢‡Î´zÖÿ°»k÷ÔûÕ,^í0l_ï`@@à3=ÁbNÀ}>w½ßÚ8ûÚP \G­v÷ÖPfÛ aÙS{ lyYÎÀ–iàeW—Yòrw6Í_Ú@ò¨ÅÓkàúÓº€¨}ÁnÚ$;µ‡n€à}}ÜßבF½ê±p˜6øDg~Í5/^”G™@ Gí: a°xqdÀhcÀͺ/<˜ý]¼ûïìWí & ~ÁÃÿïòŸ@ ¾/ïò`ÿh-à €ï.Âpà(@ÿ¾îû6 ÀÐïÍjÏLË!Ìj/ûÀkû¸Šûÿ¾MðQðòÄÎóÄjåRðaÉD¿\0mðÇkU6,}ð§‹óˆ›Éû€ð‘ð½Vм¦ª“ð ¡Ð¶„ЮˆÐx0 ±0 ³k ãKÜ +4€0Âư Åð ɰ0Æ0 Ó0 Í ßÐ ÝÑ0ëðé0íPñpý° ÝPq ± ñ1q± ÑqL£ -ʱ(1KHP?.±¯„'2Q>4ñ÷« ½A‘êIÑELqS±%q[‘gÛƒcñCf±¬lñç¯v‘)ä»K1´1•q™±ÿñ¡1¥Ñ³ˆ±­ñ±1µq¹±½ñÁ1ÅqɱÍñÑ1ÕqÙ±Ýñá1åqé±íññ1õqù±éc2 r ² ò 5ËýQ±r!K=ÖÊ’-Tip"&&$r:6#U$/Ò#µ%OQ$†$YÑ$3g”G˜b ñCGQ%%hZ²!`@˜ð¼h’"àÉ$`ES@ †‡ÀcJl œ*S,Óê9~ò¦²¨MŒÈ0ãæåSTš$ ˆ8fˆª`{º§/±Gy†9ƒˆ‰x§&2ˆ°à¦ —B…-5”N©$vÓ*Aà@@:%¥ ]J‚ns.y29XÒ*TÓhˆ…nR,É bhÔüó+Ç>sb7­h”@šÖé“(³.½Ã2ç 3[³•2È©\¨c>æ”â’˜ÿº©~"ƨâRC•É&î\eRŠCáS/QeC’7v3B'S‚ÊéBâ= ¢œjs”ˆÊGqÔ&B3IrHh“7! ¥r+`p‡Fwð7êóEãR?MV&D‚‘þic ÔDÁ‰žÌÈ©t&mTVЧ$>å¦nåQÇG´¤ªhLCÉNÝ”$V¹+ED•y¨8‰@J©\¦G; ó,,A@RM9DF‰¢pÿ¨;еBlõó’Ã:ÿI0ó„ZõˆŠÓ[Ö£q^äY·#Z[ð74…Jó4£å>i~¸õ`Ú‚Y5Äj äSŸë7PV´óºÓ—€)<]HY¢>èB^§Â]¡U>›Ð^WèID taß4å4F–`MÅ`/aÇÒ8Ìt¢XÔl:ö`t< «Ñ¢[ ¶9ŒõÉUk("¢Jec8VvÔª¸HerFDf™B`kv5õ«x$’"g{ÖgöXƒV©P6:žÂh‹ÖJ’–e—v‹à5`ŸjwVj3Viõ|æÂ‹^ö*•jökÍJlǶ`=U\‹0"„=`UcsnÿME<Ú–neÕn{ocRoo5mãö#kp¥UpïVn ×kçqûVq1ÃQWw?(bEiˆ!–"$7a)7q ·&[!rÜʶ´àJ­¸¯2T(‰Ò$’8©us ‹^ÉŽo·…e.K#îrR³Ò$öI‚ÔtKw‹kŒttײ-ei›ø©Tç‘')\Q˜lµ¸j,ö&N¥]Tft±]Ô…]Ä…\¦IšB @¦uNÉCñHIõŒ:ô„~õޱX“©bÁÕ&c4‚d¥bئxÌX˜Ò˜Yÿ$%…BbG@–?‰Hïå–öfG˜9¹“}ù"~Âgl†Lîòt†g¼9 ÊFV¢¸M”†K¿ôhèLÊ©²ž/¹k‹7±™p0v›¹e ÔlĆl̦Z& o8‡#@9íoЄN­(Jå”_ð4‡®ÅNã•«¹eu9³¹ié0ËPõ!Hzn9xA6ù.¶€ùB²"õ&"bz¦7ºj;ƒ?ºŸOÇ'ø§;ڃ墳‡‚§sZ=Z6(«¨qÓ€†‚Iœ$!ü²4€˜7œq©uz—ý7ᩃ’ÂLîÙ¨¬—µ±—÷j²P.¨w n(‡¬3‰ÿz([…uŠªhŽ[ˆ‡–¨7ùÄOŠç‰þF >%11¯‹hz€³<¶ñj¿ˆ­µ™aˆu»ÌeŽ")]·jWþh]w掔  `pùTé”Ô§³%™’#«q±@»7–³Ôc³pb²c5lT‰•0`v©y¡9Ka‰—ž7E‚”­>ËÉ@‡‰YnÉ6›°p›©3‹hƒpœV·õšT9B#óš²i›Šé›Ä3¼‹Ÿ’䴹ʘFÔ¼)sº}‘tÔZb5X©ÇŽ|ß:vÖDž\”hfóö©Ÿ:Û¡|t6 êL§iœ)¡ª¥\…< ¤[9ë¾½Ú¨‘÷«ÿûÛHÖ$£`§^*pöW)):Ž}”=Wês 6-ŸÈä¥Â¢1©¢Ÿô/`û‡÷‚Ã:‚Ïv­OÒ!ñ[X3]fÇ›²Y¹ûÃ÷Ö‹œ"}œ!ë!«ÛÊiVy*ëšñ§ë š ÌÅ|ÌÃÌÉüÌÍüÌÉ<ÍÕ¼ÌàÍá<Îá#€ ìüÎñüÎë<Ïù|ÏùÏýüÏõ<+ž Ð ýÐ ]²ŽüpóŠÍÛÜÑÕÒ#½ÍÇäüÒßœÎ=Ï]Ð;ýÏ?Ô ÑIý }Ê]6Ã)]Ì%}ÍWÕ_½ ,Óå\Ó7]ÏoÝÎCÓs]×G½ÔýÔó{ÉS7Ö[½Òÿc]ÖÖkÝ$z vÐ{=Úý׽Ѕ„7øË½Û½}ÕgÙ3ÝÙ§½ÜsÚÝ$®=ع\Éó¶Ñ¿ÒÙ_=ÜÅÝÖÏÝÜoÝ}]Ý×ÛÛ=Ÿá½Þã]Þ—]ÜÇ]yžß~á­ýÚ³ÝÉwàÁ½àýàß÷]ß7ß¡ýá=âÍvr¹àMþäåáÞã>ß§äK]äm–£ÓÖâÑ<ÙçÝÍU^ã;žã=Ýáýýße>È=·ä+å ¾ÞUÞ xþç]~ã_>è×}è»m)>éé#}é›Ô}Þë£^yþà³xØ/óê/^ë']éwžÜŸ¾çß¾ãÿažÔ©ÞÝÿí×>ëõþѹÞí¡þïáþÜçÑë^àë?ñ·¾íþëûÜñû|ðÙ½ìµ?í÷óSžñYò¥=ì`ìM=àøòó^ñOŸï9á=×A_ô ¿ôé*ç•]íoží3Þï÷^ð¥âI¨kÞöéøY½ï?îÁðåþ÷C>ø­9u± ™d£V)êh¤9-)^`¡Ht™ƒƒuÐA ð°ÀØW’ *8Pªwܱـ~=ZŠ){ç‘Tƒ Dùj È`'Iyr‡Ã ¥z•+«¶"*¤(i´<ÿ9+mµQÊXq)ܹCllà‚qd Ÿ-„gÒXØ=j{§»'²{«  &’6îÉ™$½ÌÆ$€’ŒZ‹° /¬¶÷×@¸.¬&Ò «•¨À`.(@AI7dZC#ŽŒŠpÁDA;xã9yWˆONùÏ|GÄxä_®y¤…ÿWzè@çgúéH~.zë®ÏEzQªo>»æ¬¿Ž»ë©7ÕîµÃåûï¨çN|ñ'Æ’ðD¯ün<ýóHO}ôÖ0}õÐgŸ=öÛWϽ÷âß}ù߃>ùè‹þôî¿/@üòÏOýöߟ=òR5¿0óüÿÀÜô.€ÕòˆÀª<åy ô\A-0‚I2 /ˆAÌÉ.ƒ ºž?Šp„$,¡ OˆÂªp…,l¡ _ÃÊP†Á± oˆÃêp‡<ì¡Ä qˆD,¢ˆÄ$*q‰Ll¢ŸÅ(JqŠT¬¢¯ˆÅ,jq‹\좿Æ0ŠqŒd,£ψÆ4ªqll£ßÇ8ÊqŽtN@!ÿ STARDIV 5.0 C;ÉT;opensips-2.2.2/modules/seas/doc/seas.xml000066400000000000000000000022471300170765700202150ustar00rootroot00000000000000 %docentities; ]> Seas Module Seas Module Elias Baixas VozTelecom Sistemas www.wesip.eu
Ronda Can Fatjo, 9, 1p Parc Tecnologic del Valles Cerdanyola, 08520 (SPAIN) Phone:+34 933968800 www.voztele.com elias.baixas@voztele.com
2006 VozTelecom Sistemas
&admin; &devel; &faq;
opensips-2.2.2/modules/seas/doc/seas_admin.xml000066400000000000000000001067311300170765700213700ustar00rootroot00000000000000 The Sip Express Application Server User's Guide
Application Servers
Sip Express Application Server module overview SEAS module enables OpenSIPS to transfer the execution logic control of a sip message to a given external entity, called the Application Server. When the OpenSIPS script is being executed on an incoming SIP message, invocation of the as_relay_t() function makes this module send the message along with some transaction information to the specified Application Server. The Application Server then executes some call-control logic code, and tells OpenSIPS to take some actions, ie. forward the message downstream, or respond to the message with a SIP repy, etc. The module acts implements a network protocol acting as the interface between OpenSIPS internal API and the external Application Server entity. There's only one relevant function, as_relay_t, exported by this module. This function receives as a parameter the name of the application server to which the message should be relaied. Every message relaied to an Application Server is automatically associated to a SIP transaction (a transaction is created for it). Just after the message is relaied to the Application Server, the script stops its execution on the message, because the control of message-processing is now in the Application Server. In the context of SEAS module, relaying a message to an App Server, is _not_ done in SIP protocol, but in a special protocol by means of which the SEAS module and the Application Server comunicate efficiently and seamlessly. This procotol is specially designed so that a message doesn't need to be parsed again once it arrives at the Application Server. This protocol carries information regarding the internal structure of the SIP message (to avoid reparsing) and also information about the associated transaction (recall that invoking as_relay_t indirectly calls t_newtran). This way, all the SIP-Transaction machinery, and the SIP-Message parsing, is handled at the OpenSIPS core, while the execution of the Application Logic is carried in the Application Server. The SEAS module and protocol provide a means by which an external entity can utilize OpenSIPS as a transaction-stateful SIP-stack to act on behalf of it. This means that this external entity (which we call the Application Server) is notified whenever a SIP-Request enters OpenSIPS, and this external entity can then order OpenSIPS to execute some actions, either replying the request, or generating new UAC transactions. This version of SEAS works with VozTelecom's WeSIP Application Server. This Application Server is a SipServlet JAVA Container.
Application Servers When OpenSIPS starts and SEAS module is loaded, a new process is spawn which listens on a server-socket (IP and port are specified as a parameter in the config script). From then on, the Application Servers can connect to that socket so that OpenSIPS can relay messages to them. When an Application Server connects to the socket, it sends its name through the socket, so every App Server is identified with a name. Within the OpenSIPS script, invoking as_relay_t() receives a string as a parameter, which specifies the name of an application server to which the message has to be sent. If that concrete application server hasn't already connected to the module, the function returns a negative value, otherwise (the Application Server is connected), the message is relaied to it.
Dependencies
OpenSIPS Modules SEAS module relies on the Transaction Module (TM module) for operation.
External Applications Using the SEAS module requires to have an Application Server running and connected to a particular instance of OpenSIPS. At the moment, the only Application Server that works with SEAS is WeSIP Application Server, which can be downloaded from www.wesip.eu, and used freely for non-comercial purposes.
Exported Parameters
<varname>listen_sockets</varname> (string) The listen_sockets string tells SEAS where to listen for incoming connections of Application Servers. It has the form: "ip:port". SEAS will open two server-sockets on that IP, at the specified port, and another at port+1. Application Servers must be configured to connect to that port. In case this parameter is ommited, SEAS listens on the default IP which OpenSIPS is using, and opens the ports 5080 and 5081 to listen for Application Servers. Set <varname>listen_sockets</varname> parameter ... modparam("seas", "listen_sockets","127.0.0.1:5080") ...
Exported Functions
<function moreinfo="none">as_relay_t(String name)</function> Creates a new transaction (if it isn't already created) and sends the SIP Request and transaction information to the Application Server specified in the parameter. Every Application Server connected to OpenSIPS through the SEAS module, must be identified with a different name. This function can be used within REQUEST_ROUTE. <function>as_relay_t</function> usage ... if (!as_relay_t("app_server_1")) { log("Error sending to app server"); t_reply("500","App Server not connected"); } ...
Return value In case the Application Server is connected to OpenSIPS, the function does _not_ return, the Application Server is now in charge of processing the request, and it may then reply to the request, initiate new transactions, or whatever the application being executed wants. In case the Application Server identified by the string parameter passed to as_relay_t() is not connected to OpenSIPS, the function returns 0, so that the script can continue processing the request.
WeSIP Application Server At the moment, the only Application Server known to work with SEAS is WeSIP. You can download a copy from www.wesip.eu. WeSIP is a converged Sip/Http Servlet Container.
The Servlet programming paradigm: Sip/Http Servlets Servlets are pieces of code that encapsulate the logic of an application. Servlets are deployed into an Application Server. Whenever a user requests service, the Application Server processes the request, and passes control to the servlet. The servlet then executes some logic, may it be a query to a database, the execution of a business process, the creation of customized content for the user, or whatever the service programmer could imagine. When the servlet finishes the execution, it creates a response and gives it back to the Application Server, which is in charge of making it reach back to the user. The Application Server implements the network protocol, it takes care of everything needed for a proper communication between user and server, so the servlet doesn’t have to care about these things. The servlet uses a set of resources from the Application Server, such as Session management, service routing or chaining, and request/response header composition. In HttpServlets, a service programmer has to implement a method in a JAVA class, which could be called doGet() or doPost(). Whenever an HTTP request arrived at the server, one of these functions was called with the request as a parameter, so the logic of the application was executed over that particular request. HttpServlet has been extensively used over the past years, in all kinds of business and web services. This is how a typical HttpServlet looks like: Typical example of an HttpServlet public final class Hello extends HttpServlet { protected void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>Sample Application Servlet</title>"); writer.println("</head>"); writer.println("<body bgcolor=white>"); writer.println("<table border=\"0\" width=\"100%\">"); Enumeration names = request.getHeaderNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); writer.println("<tr>"); writer.println("<th align=\"right\">"+name+":</th>"); writer.println("<td>"+request.getHeader(name)+"</td>"); writer.println("</tr>"); } writer.println("</table>"); writer.println("</body>"); writer.println("</html>"); } } The successor of HttpServlet for SIP networks, is the SipServlet API. Making most of the success of HttpServlet, the SipServlet API follows the same programming paradigm, so that SIP application programmers can reuse their knowledge in the field. SipServlet API works the same way as HttpServlet: an Application Server implements a SIP Stack and executes all the complex protocol logic. It receives and pre-processes the requests from the network, and at the right moment, passes control to the servlet doXxx() method, where the programmer implemented the application logic. Depending on what kind of SIP Message it was, a method or another will be executed. For example, if an INVITE is received, the doInvite() method will be invoked in the servlet. The application can then access all the parts of the request and do its work. When the service has been executed, it passes control back to the Application Server with a response, so that it can be forwarded to the user, and the service be satisfied. Sip Servlets can be used to implement basic SIP network functionalities (such as Proxy or Registrar servers), but their true power emerges in the implementation of value-added services, which greatly surpasses the basic service functionality of plain SIP servers. Examples of value-added services, are Virtual PBX or IPCentrex, Attended call forwarding, Instant Messaging, etc. This is the appearance a typical SipServlet: Typical Sip Servlet Example public class ProxyServlet extends SipServlet { protected void doInvite(SipServletRequest req) throws ServletException, IOException { if (req.isInitial()) { Proxy proxy = req.getProxy(); proxy.setRecordRoute(false); proxy.setParallel(parallel); proxy.setSupervised(supervised); SipURI rrURI = proxy.getRecordRouteURI(); rrURI.setParameter("foo", "bar"); req.setContent("Method is INVITE", "text/plain"); proxy.proxyTo(uris); } else { log("re-INVITE"); } } protected void doAck(SipServletRequest req) throws ServletException, IOException { log("doAck " + req.getRequestURI()); if (req.isInitial()) { throw new ServletException("unexpectedly got initial ACK"); The servlet programming API is event-ridden: every time a request comes into the Application Server (may it be an Http or SIP one), the specific servlet is executed and the service provided within it. It is a very straightforward way of programming services, and the Servlet API provides very easy and powerful means to access information about the SIP-session or Http-session, about the request or response, about the state of the dialog, or whatever it is needed. The application programmer has a rich framework of resources that allow him to focus only on the service logic, without having to worry about the underlying protocol specifics (SIP or HTTP).
SipServlet UML diagram
The Servlet programming language is JAVA, which offers a wide spectrum of programming API’s dealing with all kinds of techniques, tools and resources, which also are available seamlessly from the Servlet context. This makes the SipServlet API very desirable for all kinds application developers. SipServlet allows a rapid SIP application development and deployment, and also provides a reliable and secure framework of service execution (the JAVA sandbox and the Application Server execution environment).
Converged Http/Sip Servlet Containers SipServlets achieve the most of it when they can be deployed along with HttpServlets, in the same Application Server (also known as Servlet Container). This environment truly realizes the power of converged voice/data networks: Http protocol represents one of the most powerful data transmission protocols used in modern networks (think of the SOAP web-services protocol), and SIP is the protocol of choice in most of the modern and future voice over IP (VoIP) networks for the signaling part. So an Application Server capable of combining and leveraging the power of these two APIs will be the most successful. Convergence of SIP and HTTP protocols into the same Application Server offers, amongst others, the following key advantages: -It doesn’t require to have 2 different servers (Http and Sip) so it relieves from maintenance problems, and eases user and configuration provisioning. -It offers great convenience to the application programmer to have all the classes related to the different protocols handled within the same code. -As it eases development of interactive and multimedia services, realizing the power of well-known web-services and intermixing them with new voice services. These are some simple, but suggestive examples of services that could be developed within a converged Http/Sip servlet: -IP Centrex: through the use of the Web-interface, users could have a layout of the office in a web page, and see what phones were ringing at a given moment, so they could pick-up a call ringing in another phone in their own desktop. Or they could forward a call to another party by clicking on the web page and selecting which of the office phones it had to be transferred to. -Voicemail: users could upload an audio file to the server through a web-page, to be used as the automatic answering message, and then also download their voicemail through the web-page, or organize the messages and remove the old ones. -Instant Messaging: users could continue a voice call by starting or joining a new Instant Messaging session carried over a web-page. -Click-to-dial: users could initiate SIP sessions only by clicking a link on a web page, without the need of the Web-Browser being SIP-aware nor needing even a SIP phone: the server could handle all the logic so the user who clicked could receive a call from the server’s SIP network.
Configuring WeSIP to work with SEAS The WeSIP Application Server configuration file is based on the Apache Tomcat configuration system: It is an XML-formatted file, in which the different components of the server are specified. The default config file that comes with the WeSIP distribution package should be suitable for most of the deployment configurations.
Server The topmost element in the XML configuration file is the "server" which has 2 xml attributes, called "port" and "shutdown". The former specifies a port on which the WeSIP AS will listen for the shutdown command, and the latter is the magic word that will make the server shutdown. Server <Server port="8005" shutdown="SHUTDOWN" > if you send the magic word "SHUTDOWN" to the port 8005 of the localhost, the server will stop cleanly.
Service Nested within the Server element, must be a "Service" element, with an attribute called "name" which specifies the name for the service. This attribute is not very relevant, you can call it whatever you like. Service <Service name="WeSIP-Standalone"> Within the Service element must be two or more elements: the connectors and the engines. A connector is the instance that will receive messages from the network. You can specify HTTP connectors and/or SIP connectors. Every connector needs an attribute called "className" which specifies which class will be responsible for receiving the messages from the network. For HTTP connectors, the classname must be "org.apache.catalina.connector.http.HttpConnector" and for SIP connectors "com.voztele.sipservlet.connector.SipConnector".
Connector The SIP Connector uses 4 attributes: className="com.voztele.sipservlet.connector.SipConnector" specifies the classname of the connector. minProcessors="5" specifies the minimum number of SIPprocessor instances (and threads in the pool) to process incoming SIP messages. More processors should allow more load to be processed. This is the minimum number of instances, even if they are spare and not working. maxProcessors="10" specifies the maximum number of SIP processors used (a negative value specifies that there is no limit). addresses="localhost:5060" Specifies the SIP address and port in which the Application Server from which the Application Server will process the SIP messages. This Addres is where OpenSIPS listens for the messages, so in fact, OpenSIPS is listening on them, but OpenSIPS passes the messages to WeSIP, so WeSIP must be aware of this IP/port. this attribute MUST match one of the listening points declared within OpenSIPS in the "listen" parameters. For example in opensips.cfg:listen = tcp:localhost:5060 listen = udp:localhost:5060 Within the SIP Connector element there must be an ExtraProperties element, containing nestes Property elements. Each property element specifies a parameter for the SIP Stack. Each property is specified by a key and a value. The most significant keys are: com.voztele.javax.sip.SER_ADDRESS This specifies the IP and port in which the OpenSIPS is listening for Application Servers to connect and register.This specifies the IP and port in which the OpenSIPS is listening for Application Servers to connect and register. This needs to match the listen_sockets seas module parameter within the OpenSIPS configuration file. Ie.: modparam("seas", "listen_sockets","127.0.0.1:5080") javax.sip.STACK_NAME Specifies the name identifying this instance of the Application Server. This is the name you will set in the OpenSIPS configuration script when you invoke the WeSIP Application Server, by calling the as_relay_t function. This is the name you pass as the parameter of the function. If you have different WeSIP instances all connecting to the same OpenSIPS, they must each one have a different STACK_NAME", and within OpenSIPS you can call each of them by invoking as_relay_t() with a different name. Example: <Property key="javax.sip.STACK_NAME" value="app_server_one" /> com.voztele.javax.sip.THREAD_POOL_SIZE (integer) Specifies the number of threads there must be in the pool to process incoming SIP messages. If unspecificed, the default is "infinity". com.voztele.javax.sip.SPIRAL_HDR This property tells WeSIP and SEAS that every SipRequest and UAC transaction generated from WeSIP, must spiral through SER, and will be added a special Header called "X-WeSIP-SPIRAL: true" this will make all the outgoing messages pass again through the OpenSIPS script, so that they can be accounted or whatever the configurator wants. For example, the configuration script could go: route{ if(is_present_hf("X-WeSIP-SPIRAL")){ /* account, log, register, or whatever */ t_relay(); }else{ as_relay_t("app_server_1"); } }
Engine The Engine must also be nested within the Server element, along with the Connectors. It must have a "name" attribute with whatever name you feel like. It needs to have another attribute called "defaultHost" which will be the default host to which to pass the incoming request (in HTTP/1.0 the requests dont have a Host header, so they will be passed to this default host, in SIP, this attribute doesn't have a meaning.). In order to have this Engine handling also SIP messages, the "className" attribute of the Engine must be "com.voztele.sipservlet.core.ConvergedEngine". Within the Engine, there can be one or more Hosts, each one specified within a "Host" element nested in the engine.
Mapper A mapper is used to map an incoming request to one or another SIP or HTTP host. In case it is a SIP request, the mapping is done based on the sip.xml deployment descriptor rules. The classname of the SIP mapper MUST BE "com.voztele.sipservlet.core.EngineSipMapper". The "mapper" element must also have a "protocol" attribute, specifying which protocol this mapper handles. In case of the SIP mapper it must be "SIP/2.0". The HTTP mapper's classname must be "org.apache.catalina.core.StandardEngineMapper" and the protocol attribute "HTTP/1.1"
Realm The authentication in HTTP is performed in Apache-Tomcat through Realms. The memory realm is (textual copy from the Apache-Tomcat javadoc"): "Simple implementation of Realm that reads an XML file to configure the valid users, passwords, and roles." The classname must be "org.apache.catalina.realm.MemoryRealm" A "pathname" attribute can be specified to tell the Realm which file contains the usernames, passwords and roles. If not specified, it is "conf/wesip-users.xml"
Host A Host represents a VirtualHost in HTTP/1.1 servers, so the requests will be dispatched to one or another virtual host depending on the Host: header. In SIP this doesn't make much sense, because there's no such Host: header, and virtual hosting is not done in this way. Every host must have a "name" attribute which specifies the name of the virtual host, it must also have a "nameSip" attribute which MUST MATCH the IP or hostname _and_ port" specified in OpenSIPS listen parameters and in the Sip Connector the hostname and the port must be separated with an underscore. for example: nameSip="localhost_5060" or nameSip="192.168.1.1_5060" The next important attribute that must have the Host element is "appBase" which declares the directory where the WEB and SIP applications reside. It usually is a directory called apps in the directory from which the server runs. The attribute "unpackWARs" says the WeSIP Application Server to unpack the Web or Sip Application Archives (.war or .sar extensions) found inside the appBase directory. It should usually be set to "true". The "port" attribute specifies the port where this host is going to receive SIP messages . This only has to do with the SIP protocol, not with HTTP. It must be the same as the port specified in OpenSIPS parameter "listen_sockets" (for the seas module). The "autoDeploy" attribute tells the host to monitor the "appBase" directory for new application archives (.sar or .war) so they can automatically be deployed. This parameter should be set to "true". The "className" used for the Host _must_be_ "com.voztele.sipservlet.core.ConvergedHost"
Mapper Hosts must also have a nested Mapper element, but when the mapper is inside a Host (and not in an Engine) the classnames must be "com.voztele.sipservlet.core.SipHostMapper" for the "SIP/2.0" protocol and "org.apache.catalina.core.HttpHostMapper" for the "HTTP/1.1" protocol. (2 mappers must be nested inside the Host).
Configuration Examples In general, you can configure WeSIP to work with your OpenSIPS in two ways: have 2 OpenSIPS instances, the first acting as Proxy/Registrar/Redirect and the second cooperating with WeSIP to act as the Application Server. This is the preferred deployment layout, as the first OpenSIPS works as usual, and the requests that need special services are relaied to another OpenSIPS which acts on behalf of the WeSIP AS. This configuration profile distributes load (call-routing logic in one instance, and Application Services in the other), and is also more fault-tolerant. On the other hand, you can have all your call-routing logic and Application Server on the same OpenSIPS, having one script handle all the logic, and then invoking the App Server at any point.
opensips.cfg in standalone log_level=3 # debug level (cmd line: -dddddddddd) log_stderror=no # (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 loadmodule "/usr/local/lib/opensips/modules/sl.so" loadmodule "/usr/local/lib/opensips/modules/tm.so" loadmodule "/usr/local/lib/opensips/modules/rr.so" loadmodule "/usr/local/lib/opensips/modules/maxfwd.so" loadmodule "/usr/local/lib/opensips/modules/usrloc.so" loadmodule "/usr/local/lib/opensips/modules/registrar.so" loadmodule "/usr/local/lib/opensips/modules/textops.so" loadmodule "/usr/local/lib/opensips/modules/seas.so" loadmodule "/usr/local/lib/opensips/modules/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("usrloc", "db_mode", 0) modparam("seas", "listen_sockets", "127.0.0.1:5080"); route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; if (!method=="REGISTER") record_route(); if (loose_route()) { append_hf("P-hint: rr-enforced\r\n"); route(1); }; if (uri==myself) { if (method=="REGISTER") { save("location"); exit; }; lookup("aliases"); if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); }; if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; append_hf("P-hint: usrloc applied\r\n"); }; route(1); } route[1] { if(!as_relay_t("app_server_one")){ t_reply("500","Application Server error"); } }
opensips.cfg working as WeSIP front-end log_level=9 # debug level (cmd line: -dddddddddd) log_stderror=yes # (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 listen = tcp:localhost:5060 listen = udp:localhost:5060 mpath="/home/elias/src/sipservlet/seas" loadmodule "modules/tm/tm.so" loadmodule "modules/seas/seas.so" loadmodule "modules/mi_fifo/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("seas", "listen_sockets","127.0.0.1:5080") route{ if(!as_relay_t("app_server_1")){ t_reply("500","Application Server error"); } }
Server.xml <Server port="8005" shutdown="SHUTDOWN" debug="0"> <Service name="WeSIP-Standalone"> <Connector className="org.apache.catalina.connector.http.HttpConnector" port="8080" minProcessors="5" maxProcessors="75" enableLookups="true" address="localhost" acceptCount="10" debug="10" /> <Connector className="com.voztele.sipservlet.connector.SipConnector" minProcessors="5" maxProcessors="75" addresses="localhost:5060" > <ExtraProperties> <Property key="com.voztele.javax.sip.SER_ADDRESS" value="127.0.0.1:5080" /> <Property key="javax.sip.STACK_NAME" value="app_server_one" /> <Property key="com.voztele.javax.sip.THREAD_POOL_SIZE" value="10" /> </ExtraProperties> </Connector> <Engine name="Standalone" defaultHost="localhost" debug="10" className="com.voztele.sipservlet.core.ConvergedEngine"> <Logger className="org.apache.catalina.logger.SystemOutLogger" timestamp="true"/> <Mapper className="org.apache.catalina.core.StandardEngineMapper" protocol="HTTP/1.1"/> <Mapper className="com.voztele.sipservlet.core.EngineSipMapper" protocol="SIP/2.0"/> <Realm className="org.apache.catalina.realm.MemoryRealm" /> <Host name="localhost" nameSip="localhost_5060" debug="10" appBase="webapps" unpackWARs="true" port="5060" autoDeploy="true" className="com.voztele.sipservlet.core.ConvergedHost"> <Mapper className="com.voztele.sipservlet.core.SipHostMapper" protocol="SIP/2.0"/> <Mapper className="org.apache.catalina.core.HttpHostMapper" protocol="HTTP/1.1"/> </Host> </Engine> </Service> </Server>
opensips-2.2.2/modules/seas/doc/seas_devel.xml000066400000000000000000001023771300170765700214010ustar00rootroot00000000000000 &develguide;
Internals The SEAS module runs within the Open Sip Express Router aka. OpenSIPS. OpenSIPS uses a pool of processes to execute the script logic on every new message received. These are called the worker processes. One of these processes will be selected to process the script, and at some point it will find a function invoking the relay of the SIP message to one of the Application Servers registered. This function has been called as_relay_t, which stands for Application Server relay (the _t stands for TransactionStatefully), and receives as the only parameter the name of the application server to be invoked. The process will execute the as_relay_t function, which looks up in a table if there is a registered Application Server with that name. If there is one, the process will craft the SEAS header for the SIP message being handled, put it in a shared memory segment, and write the address of that segment to a pipe (4 bytes pointer in IA32). This way, we will have all the OpenSIPS processes composing the SEAS header along with the SIP message, and putting its shared memory address into that pipe. This technique of inter-process communication avoids race conditions because writing to a pipe is granted to be an atomic operation if the data to write is less than _POSIX_PIPE_BUF, which usually is 512 bytes. At the initialization of OpenSIPS, the SEAS module creates the discussed pipe, so that all the OpenSIPS worker processes inherit the file descriptor associated to the pipe. Then it spawns a new process, which will be the one to open two server sockets, and wait for the Application Servers to connect and register. Each Application Server wishing to receive events from OpenSIPS, will have to open a socket to the module (the port and IP of the socket are defined at start time in the script). After connection, it has to print its identification name. The SEAS process (from now on, called event dispatcher) will then register it in its internal structures, so that the OpenSIPS processes can push events for it. The following picture, shows the internals of the SEAS Event dispatcher process:
Overview of Seas Event Dispatcher process operation
Within the SER server, the flowing of SIP Messages and control flow, is depicted in the following diagram:
SIP Messages and control flow within SER
SEAS Protocol SIP is a very flexible protocol. It can be very easily extended with new features, and SIP entities have a high level of freedom in composing the SIP messages, for example setting IPs or hostnames in URIs, reordering header fields, folding headers, aggregating/scattering headers, etc. This flexibility, though, makes it difficult to implement efficiently, because parsing of text headers requires a lot of state. OpenSIPS implements a very efficient parsing mechanism and SIP-transaction machinery. The goal of the SEAS protocol is to keep all this information that has been already extracted at OpenSIPS, so that it can be reused at the Application Server.
The SEAS protocol The SEAS protocol is a layer of information regarding the internal structure of a SIP message that is added whenever SEAS sends a SIP event to the Application Servers. The protocol is used for communication between OpenSIPS and the Application Servers. Once an incoming SIP message has reached the worker process within OpenSIPS, it copies its content into a private memory area (which is, a memory chunk not shared across processes). In this point, the message first line is parsed to know whether it is a SIP request or response. OpenSIPS uses a technique called lazy-parsing, which consists in delaying the parse of headers until some piece of the code requires it. As the SIP message goes traversing functions and the script code, a function called parse_msg() gets called again and again, and the SIP message gets parsed further and further. Each call to parse_msg passes an integer value argument (32 bits) in which every bit signals a header to be parsed, if they are already parsed (because a previous invocation of parse_msg), the function returns immediately, otherwise, the SIP message is scanned and parsed until all the headers requested get parsed. In each call to parse_msg, different parts of the message are analyzed, and different SIP header-specific structures get filled. Each one of this structures, give quick access to each of the parts of a SIP message header. For example, a Via header struct is called via_body, and has these members: name, version, transport, host, proto, port, port_str, params, comment, received, rport, etc. each of these members gives quick access to each of the parts of the header. For example, a via header like this: “Via: SIP/2.0/UDP 192.168.1.64:5070;branch=z9hG4bK-c02c60cc” would have the member proto pointing to the “U” of “UDP”, and a length of 3, the host member would be pointing to “192.168.1.64” and have a length of 12, the branch member would be pointing to “z9hG4bK-c02c60cc” and a length of 16, and so on. This structure is the result of the parsing. All this meta-information regarding the SIP message structure, is stored in a sip_msg structure, using dynamically-allocated memory segments. OpenSIPS defines different structure types describing different SIP headers, such as via_body, to_body, cseq_body, via_param, and so on. These structures are generally composed of another kind of structure called str. The str structure is a key component of OpenSIPS's high performance. In the C programming language, a string's length is known because a '0' (null-character) is found at the end of it. This forces each of the string manipulation functions to keep looking for a '0' in the byte stream, which is quite processor consuming. Instead of this, OpenSIPS defines a structure composed of a char pointer and an integer. The char points to the start of a string, and the integer gives its length, thus avoiding the '0' lookup problem, and giving a significant performance boost. This structure has been quite useful to the design of the SEAS protocol, because it enables the description of the SIP message anatomy by giving pointers to each of its fields, and integers describing each of its lengths. Knowing that a SIP header does not usually occupy more than a few characters (always less than 256), the pointer in the structure has been relativized to the beginning of the SIP message or the beginning of the SIP header, and the integer giving the length, has been casted to an unsigned byte (256 values, so 256 characters maximum length). When messages get transferred from OpenSIPS to the Application Server, it is optimum to keep this worthy meta-information regarding the SIP message, so that it can be used at the AS part. For this to be possible, it is needed to store the pointers to each of the syntactic structures and their length. In general, pointers are variables that point to a region in the memory of a computer. The region of the memory is counted from the 0x00000000 address in IA32 architectures (from the beginning). C provides functionality to do any kind of arithmetic operations over pointers (add, subtract, multiply and divide), so that the euclidean distance over the one-dimension address space can be calculated just by subtracting a base address from another pointer. These pointers will have to be transmitted through the network, along with the SIP message, so for the pointers to keep their meaning, they need to be relativized to a known point, and the most meaningful known point in a SIP message is its start. So making the pointers relative to the message start, gives two important features: first, it makes the pointers still valid when they arrive at another computer (because they are relative to the beginning of the message), and they occupy far less memory, because from a 4-byte pointer (in IA32) it gets translated to a 1 or 2 byte index, because an important amount of redundant information is elicited (we already know that each of the parts of the message belong to the message, so why carry the message begin address in each of the pointers ?). The SIP messages are composed of protocol headers and a payload. The headers section don't usually surpass the 1500 byte limit, amongst other reasons, because the usual Maximum Transmission Unit in Ethernet networks is 1500 bytes and the protocol was initially designed to work on UDP. For that reason, 11 bits should be enough to address a particular region within the SIP message, because it yields 2048 positions. The closest greater value to 11 bits multiple of a byte (the basic TCP network transport unit) is 16 bits, or 2 bytes, which makes it possible to address 65536 positions from the beginning. For the SEAS protocol to be extensible and platform-independent, all the 2-byte pointers or indexes to each of the message regions are sent in network-byte-order, or big endian. This is also useful in the JAVA part to retrieve the indexes, because the JAVA natively uses a big-endian representation of integers, regardless the architecture on which it runs. For each kind of standard SIP header (this is, the headers referred to in the SIP specification) there is a code specification, regarding the composition of the header. Each one of its parts points to one the several components of the header. For example, a From header always has a SipURI and may have several parameters, amongst others, a tag. Then, the From header code has a field indicating where the URI starts, a codification of the URI, and several pointers that point to each one of the parameter names and values. This is the codification of the From header. All the other headers have a similar codification.
General codification of a header Every header codification, regardless it is known to the server or not, begins with a 2-byte unsigned integer, which points to the beginning of that header counted from the SIP message begin (a SIP message start based pointer to the header). Following these two bytes is another byte giving the length of the name, and another byte giving the length of the entire header (including name and value).
General codification of a SIP header in SEAS protocol
For example:
Example of a from header SEAS-protocol codification
Codification of a generic URI As the SIP URI is one of the most used types in a SIP message, a special structure has been defined to describe the contents of it. A URI is always included inside a SIP header, or may be in the first line of a SIP Request (as the request URI). The codification of any URI is as follows:
SEAS-codification of a SIP URI (byte meanings are shown)
What follows is an example of a SIP URI codification with the SEAS protocol.
Example of a SEAS SIP URI codification
The first byte in the encoded-URI structure, gives the index where the URI starts, counting from the beginning of the SIP header where it appears. The next two bytes are flags indicating known fields present in the URI (such as port, host, user, etc.). All the following bytes are uri-start based pointers to the fields that are present in the URI, as specified by the flags. They must appear in the same order shown in the flags, and only appear if the flag was set to 1. The end of the field, will be the place where the following pointer points to, minus one (note that all the fields present in a URI are preceded by 1 character, ie sip[:user][:passwod][@host][:port][;param1=x][;param2=y][?hdr1=a][&hdr2=b]) it will also be necessary to have a pointer at the end, pointing two past the end of the URI, so that the length of the last header can be computed. The reason to have the “other parameters” and headers flags at the beginning (just after the strictly URI stuff), is that it will be necessary to know the length of the parameters section and the headers section. The parameters can appear in an arbitrary order, they won't be following the convention of transport-ttl-user-method-maddr-lr, so we can't rely on the next pointer to compute the length of the previous pointer field, as the ttl parameter can appear before the transport parameter. So the parameter pointers must have 2 bytes: pointer+length.
Codification of To and From headers To and From headers follow the same structure, so the same codification structure has been used to describe both. The structure is depicted in the drawing:
SEAS codification of From and To headers
Codification of Contact The contact header is one of those SIP headers that can be combined, which means that if several headers of the same type are present in the message, they can be aggregated in a single header, having the header values separated by a comma. Thus, a single Contact header can contain more than one contact-value. For this reason, the Contact codification is composed of a several Contact codifications concatenated, and a byte at the beginning telling how much Contact codifications are present. The code is depicted in the following drawing:
SEAS codification of a Contact header
Codification of Route and Record Route headers Both Route and Record-Route headers follow an identical structure, and it is also permitted to combine several headers into one, with their bodies (or header values) separated by commas. In this case, both kinds of headers follow the same structure, defined as follows:
SEAS codification of a Route Header
Codification of Accept and Content-Type headers These two kinds of headers carry mime type and subtype definitions in the form “type/subtype” (ie. text/xml, application/sdp or whatever). For internal handling of this headers, SER codifies the known types and subtypes into a single 32 bit integer, with the highest two bytes giving the mime type, and the lowest two bytes giving the subtype. The difference is that Accept header can also be combined, carrying more than one header value in a single header row. Thus the Accept header has a leading byte giving the number of mime type/subtype integers present, while the Content-Type only uses 4 bytes (a 32-bit integer) giving the type/subtype.
Codification of Authorization headers SIP has inherited the authentication scheme from HTTP, which is based on a digest scheme. There are several headers regarding these authorization scheme, namely Proxy-Authenticate, WWW-Authenticate, Authorization and Proxy-Authorization. All of them can be codified using the same schema, which is as follows:
SEAS codification of Authentication/Authorization headers
For each field present, there are 2 bytes, one pointing the place where it starts, the next giving how long this field is. The URI is a special case, and is composed of 1 byte telling how long is the URI structure, and then the encoded URI structure.
Codification of Allow headers Allow headers carry request methods that a user agent or proxy understands or is willing to accept. In SER, request methods are codified into a 32-bit integer, each of its bits signals a different kind of header. The Allow header is codified copying that integer into the payload of the header.
Codification of Content-Disposition headers The content-disposition is encoded within 2 bytes: the first is a header-start based pointer to where the content-disposition value starts, and the second is its length. If there are parameters present, each of them uses 1 byte pointing to where the parameter name starts, and 1 byte pointing to where the parameter value starts. From these two values, the parameter name and value lengths can be inferred.
Codification of Content-Length header The content length header is codified as a 4-byte unsigned integer, in network byte order.
Codification of Cseq header The Cseq header is codified using 9 bytes. The first one is a number corresponding to the internal value that SER assigns to that request method (the method ID). The following 4 bytes are an unsigned 32-bit integer according to the Cseq number. The next two bytes are the header based pointer to the beginning of the Cseq number and its length, and two more bytes pointing to the beginning of the method name and its length.
Codification of Expires header The expires header is composed of 6 bytes. The first four bytes are an unsigned 32-bit integer with the parsed value of the header (which is the number of seconds before a request expires). Then follows 1 byte pointing to the beginning of the header value (the expires value as a string) and a byte giving the length of the value.
Codification of a SIP message
The general message information section In SER, not only the headers are parsed with a high degree of optimization, but also the first line is. So for the SEAS protocol to realize this improvement, a codification for the first line of every SIP messages has also been defined. The first two bytes of the codification are a 2-byte unsigned integer. If its value is equal or greater than 100, then this is a response, and the integer represents its status code. If its value is smaller than 100, then it is a request, and the integer represents the method of the request being transported.
SEAS codification of a SIP First Line
The next two bytes are an unsigned integer which is a pointer to where the actual SIP message starts, beginning from the start of the codified payload. The next two bytes are also an unsigned integer giving the SIP message length. The next bytes differ on the meaning depending on whether the message is a SIP Request or Response. In case it is a Request: The next two bytes, are a SIP-message-start based pointer to where the method begins, and the method length. The next two bytes, are a SIP-message-start based pointer to where the Request URI begins, and the request URI length. The next two bytes, are a SIP-message-start based pointer to where the version identifier begins, and the version identifier length. In case it was a Response: The next two bytes, are a SIP-message-start based pointer to where the response code begins, and the response code length. The next two bytes, are a SIP-message-start based pointer to where the reason phrase begins, and the reason phrase length. The next two bytes, are a SIP-message-start based pointer to where the version identifier begins, and the version identifier length. In case the message is a SIP response, the following bytes correspond to the Request URI codification. The first byte is the length of the URI codification, followed by the URI code. The last byte in this set, is the number of headers present in the SIP message. After this byte, goes a section, called the Message Headers Index, which gives quick access to each of the headers and their codifications present in the message.
The headers index section As it has been already discussed, the aim of SEAS project is to achieve as high a performance as possible. One of the techniques enabling high performance in text-based servers is the so called lazy parsing. To enable the laziest possible parsing at the Application Server endpoint, a mechanism has been used so that access to a requested SIP header can be delayed until the application requests it, and the access can be direct to that header, without parsing the former headers present in the SIP message. Recall that one of the performance drawbacks of the SIP protocol is that headers of any type can be spread all along the header section, not having the constraint of putting the most critical sip-specific headers at the beginning and ordered (which would be, in fact, very desirable). For this to be possible, there is a section right after the beginning of the payload (the general message information section) which is a kind of hash table, giving quick access to the codes (as explained in the previous sections) of each of the headers present in the message. This sort of hash table, is composed of triplets of bytes. The first byte of each three is a code indicating which kind of header it points to (whether it is a From, To, Call-ID, Route header, etc). Then follows a 2 byte network-byte-order integer that points to a section in the codified-header where the body of this header is more specifically described. This gives really fast access to any of the headers. For example, if all the Route Headers were requested by the application, then a lookup in this table would be necessary, looking for the value '9' (corresponding to the Route header) in each of the positions multiple of 3 (0,3,6,9,12, etc). This can be done in a extremely fast and easy way, as this snipped of pseudo code explains: for(int j=0,int i=0;i<table_length;i+=3){ if(payload[i]==9) results[j++]=i; } this would let in the “results” array all the indexes in the headers table that refer to a Route header. Then, the Route codification for each of the headers could be reached thanks to the two-byte unsigned integer that follows each of the header identifiers.
SEAS Headers Index section overview
So a SIP message codified by the SEAS protocol, has the following layout:
SEAS SIP-Message codification
SIP Messages are a fundamental part of the protocol, but they are not the only one. Transaction play a very important role in the SIP protocol, within SER and in any JAIN-SIP implementation. For this reason, the SEAS protocol also needs to define and implement some semantics regarding transaction handling. The events related to a transaction are: Incoming Request, Outgoing Request, Incoming Response, Outgoing Response, Timeout and Transport Error. So the SEAS protocol defines a specific format for each one of these events. Internally, SER stores the transactions in a hash table. This hash table generates an integer for each transaction applying a hash function to its Via branch parameter, this integer is the hash index, and it identifies in which slot within the hash table the transaction is stored. The transaction table usually uses 65536 entries, so the hash collision is pretty unlikely. Anyway, every hash entry is in reality a linked list of transactions, so in the case a hash collision (two transactions being assigned to the same hash slot) the transactions are added to the same slot, each one being identified by another integer called the label. The label within a hash slot, is initially generated randomly, and then increased by one each time a transaction falls in the same slot. So every transaction is identified by a hash index and a label. For incoming SIP requests, a transaction is generated at SER, and the SEAS module gets that transaction identifier (hash index + label), then grabs the source and destination IP, port and transport from every message, and crafts a SEAS RequestIn event. This kind of event carries all this information within it. In order to send Responses out for the Server Transactions, JAIN can send a type of Action messages, that order SER to send them to the network. These messages follow a structure very similar to that of RequestIn events: they start with the Action length in bytes, then follows a byte giving the type of action, then follows the Hash Index and the Label associated with the transaction that is being replied, and finally the SIP Message in raw format. It doesn’t use the SEAS codification described above, because SER can easily parse the JAIN provided Response to process it and send it out, so the pre-parsing is not needed in that direction. In order to generate Client Transactions, that is, sending SIP Requests out, JAIN utilizes another kind of action called Seas Request Action. In this case, when JAIN generates the Request to be sent out, it doesn’t have any means to know the transaction identifier (hash index and label) that will be assigned to it by SER, so a new mechanism has bee implemented to correlate JAIN requests to SER transactions. Basically, JAIN-SIP assigns a unique identifier (an integer) that is incremented by one for each new Client Transaction generated. This identifier is passed to SER along with the SIP Request, so when a SIP Response arrives to SER regarding that transaction, SER sends a ResponseIn event to the JAIN stack, containing both the initial integer identifying the transaction at JAIN and the hash index and label that have been assigned to the transaction. This way, JAIN can correlate its own identifiers with the identifiers used within SER.
Different kinds of SEAS codified Events and Actions
In case there is a Transaction Timeout, it is notified to the JAIN SIP Stack by passing it a Seas Incoming Response with a flag called Faked Reply, and a Response code number 408 (Request Timeout).
opensips-2.2.2/modules/seas/doc/xml2sgml.sh000077500000000000000000000004131300170765700206350ustar00rootroot00000000000000#!/bin/bash xmlstarlet sel -E ISO-8859-1 -t -c "//chapter[@id='user']" seas.xml > seas_user.sgml xmlstarlet sel -E ISO-8859-1 -t -c "//chapter[@id='devel']" seas.xml > seas_devel.sgml xmlstarlet sel -E ISO-8859-1 -t -c "//chapter[@id='faq']" seas.xml > seas_faq.sgml opensips-2.2.2/modules/seas/encode_allow.c000066400000000000000000000044071300170765700205720ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * ===================================================================== * * Filename: encode_allow.c * * Description: [en|de]code allow header * * Version: 1.0 * Created: 21/11/05 20:40:25 CET * Revision: none * Compiler: gcc * * Author: Elias Baixas (EB), elias@conillera.net * Company: VozTele.com * * ====================================================================== */ #define _GNU_SOURCE #include #include #include #include #include "../../parser/parse_allow.h" #include "../../parser/msg_parser.h" #include "encode_allow.h" char *mismetodos[]={"UNDEF","INVITE","CANCEL","ACK","BYE","INFO","OPTIONS","UPDATE","REGISTER","MESSAGE","SUBSCRIBE","NOTIFY","PRACK","REFER","OTHER"}; /** * Encodes allow header. * * TODO: Does not support the UNDEFINED header type !!! */ int encode_allow(char *hdrstart,int hdrlen,unsigned int *bodi,char *where) { unsigned int i; memcpy(&i,bodi,4); i=htonl(i); memcpy(where,&i,4); return 4; } int print_encoded_allow(FILE *fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix) { unsigned int i,j=0,body; memcpy(&body,payload,4); body=ntohl(body); fprintf(fp,"%sMETHODS=",prefix); if(body==0) fprintf(fp,"UNKNOWN"); for(i=0;i<32;j=(0x01< #include #include "../../parser/contact/parse_contact.h" #include "../../parser/contact/contact.h" #include "../../parser/parse_uri.h" #include "encode_contact.h" #include "encode_uri.h" #include "xaddress.h" #include "encode_header.h" #include "encode_parameters.h" #define HAS_NAME_F 0x01 #define HAS_Q_F 0x02 #define HAS_EXPIRES_F 0x04 #define HAS_RECEIVED_F 0x08 #define HAS_METHOD_F 0x10 /* * encodes a (maybe aggregated) contact header. * encoding is: * 1: flags * 0x01 this is a star contact (*) *[ * 1: number of contacts present * N: fore each contact present, the length of the contact structure * N*M: the contact structures concatenated *] */ int encode_contact_body(char *hdr,int hdrlen,contact_body_t *contact_parsed,unsigned char *where) { int i=0,k,contact_offset; unsigned char flags=0,tmp[500]; contact_t *mycontact; if(contact_parsed->star){ flags|=STAR_F; where[0]=flags; return 1; } for(contact_offset=0,i=0,mycontact=contact_parsed->contacts;mycontact;mycontact=mycontact->next,i++){ if((k=encode_contact(hdr,hdrlen,mycontact,&tmp[contact_offset]))<0){ LM_ERR("parsing contact number %d\n",i); return -1; } where[2+i]=k; contact_offset+=k; } where[1]=(unsigned char)i; memcpy(&where[2+i],tmp,contact_offset); return 2+i+contact_offset; } /* Encoder for contacts. * Returns the length of the encoded structure in bytes * FORMAT (byte meanings): * 1: flags * 0x01 : there is a Display Name * 0x02 : there is a Q parameter * 0x04 : there is an EXPIRES parameter * 0x08 : there is a RECEIVED parameter * 0x10 : there is a METHOD parameter * 1: length of the XURI-encoded uri in bytes. * [2]: optionally, 1 HDR-based ptr to the displayname + the length of the name * [2]: optionally, 1 HDR-based ptr to the q + the length of the q * [2]: optionally, 1 HDR-based ptr to the expires + the length of the expires * [2]: optionally, 1 HDR-based ptr to the received param + the length of the param * [2]: optionally, 1 HDR-based ptr to the method + the length of the method parameter * N: the XURI-encoded URI. * [N:] optionally, HDR-based pointers to the different header-parameters * */ int encode_contact(char *hdrstart,int hdrlen,contact_t *body,unsigned char *where) { int i=2,j=0;/* 1*flags + 1*URI_len*/ unsigned char flags=0; struct sip_uri puri; if(body->name.s && body->name.len){ flags|=HAS_NAME_F; where[i++]=(unsigned char)(body->name.s-hdrstart); where[i++]=(unsigned char)body->name.len; } if(body->q){ flags|=HAS_Q_F; where[i++]=(unsigned char)(body->q->name.s-hdrstart); where[i++]=(unsigned char)body->q->len; } if(body->expires){ flags|=HAS_EXPIRES_F; where[i++]=(unsigned char)(body->expires->name.s-hdrstart); where[i++]=(unsigned char)body->expires->len; } if(body->received){ flags|=HAS_RECEIVED_F; where[i++]=(unsigned char)(body->received->name.s-hdrstart); where[i++]=(unsigned char)body->received->len; } if(body->methods){ flags|=HAS_METHOD_F; where[i++]=(unsigned char)(body->methods->name.s-hdrstart); where[i++]=(unsigned char)body->methods->len; } if (parse_uri(body->uri.s, body->uri.len,&puri) < 0 ) { LM_ERR("Bad URI in address\n"); return -1; }else{ if((j=encode_uri2(hdrstart,hdrlen,body->uri,&puri,&where[i]))<0){ LM_ERR("failed to codify the URI\n"); return -1; }else{ i+=j; } } where[0]=flags; where[1]=(unsigned char)j; /*CAUTION the parameters are in reversed order !!! */ /*TODO parameter encoding logic should be moved to a specific function...*/ i+=encode_parameters(&where[i],body->params,hdrstart,body,'n'); return i; } int print_encoded_contact_body(FILE *fp,char *hdr,int hdrlen,unsigned char *payload,int paylen,char *prefix) { unsigned char flags, numcontacts; int i,offset; flags=payload[0]; fprintf(fp,"%s",prefix); for(i=0;i #include "../../parser/parse_disposition.h" #include "encode_parameters.h" /* struct disposition_param { str name; str body; int is_quoted; struct disposition_param *next; }; struct disposition { str type; struct disposition_param *params; }; */ int encode_content_disposition(char *hdrstart,int hdrlen,struct disposition *body,unsigned char *where) { unsigned char i=3; /*where[0] reserved flags for future use*/ where[1]=(unsigned char)(body->type.s-hdrstart); where[2]=(unsigned char)body->type.len; i+=encode_parameters(&where[3],(void *)body->params,hdrstart,body,'d'); return i; } int print_encoded_content_disposition(FILE *fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix) { int i=3;/* flags + urilength */ printf("%s",prefix); for(i=0;i #include #include /* * Encodes a content-length header. * encoding is as follows: * 1: length of the payload. * N: Network-Byte-Ordered(little endian) of the * multibyte number represeting the length (now, it is * a long integer) */ int encode_contentlength(char *hdr,int hdrlen,long int len,char *where) { long int longint; longint = htonl(len); where[0]=sizeof(long int); memcpy(&where[1],&longint,sizeof(long int)); return 1+sizeof(long int); } int print_encoded_contentlength(FILE *fp,char *hdr,int hdrlen,unsigned char *payload,int paylen,char *prefix) { long int content_length; int i; memcpy(&content_length,&payload[1],payload[0]); content_length=ntohl(content_length); fprintf(fp,"%s",prefix); for(i=0;i #include #include #include "../../parser/parse_content.h" #include "encode_content_type.h" int encode_content_type(char *hdrstart,int hdrlen,unsigned int bodi,char *where) { return encode_mime_type(hdrstart,hdrlen,bodi,where); } int print_encoded_content_type(FILE *fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix) { unsigned int type; memcpy(&type,payload,sizeof(unsigned int)); return print_encoded_mime_type(fp,hdr,hdrlen,&type,paylen,prefix); } int encode_accept(char *hdrstart,int hdrlen,unsigned int *bodi,char *where) { int i; for(i=0;bodi[i]!=0;i++){ encode_mime_type(hdrstart,hdrlen,bodi[i],&where[1+i*sizeof(unsigned int)]); } where[0]=(unsigned char)i; return 1+i*sizeof(unsigned int); } int print_encoded_accept(FILE *fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix) { int i; unsigned int type; for(i=0;i>16){ case TYPE_TEXT: chtype="text"; break; case TYPE_MESSAGE: chtype="message"; break; case TYPE_APPLICATION: chtype="application"; break; case TYPE_MULTIPART: chtype="multipart"; break; case TYPE_ALL: chtype="all"; break; case TYPE_UNKNOWN: chtype="unknown"; break; default: chtype="(didn't know this type existed)"; break; } switch(type&0xFF){ case SUBTYPE_PLAIN: chsubtype="SUBTYPE_PLAIN"; break; case SUBTYPE_CPIM: chsubtype="SUBTYPE_CPIM"; break; case SUBTYPE_SDP: chsubtype="SUBTYPE_SDP"; break; case SUBTYPE_CPLXML: chsubtype="SUBTYPE_CPLXML"; break; case SUBTYPE_PIDFXML: chsubtype="SUBTYPE_PIDFXML"; break; case SUBTYPE_RLMIXML: chsubtype="SUBTYPE_RLMIXML"; break; case SUBTYPE_RELATED: chsubtype="SUBTYPE_RELATED"; break; case SUBTYPE_LPIDFXML: chsubtype="SUBTYPE_LPIDFXML"; break; case SUBTYPE_XPIDFXML: chsubtype="SUBTYPE_XPIDFXML"; break; case SUBTYPE_WATCHERINFOXML: chsubtype="SUBTYPE_WATCHERINFOXML"; break; case SUBTYPE_EXTERNAL_BODY: chsubtype="SUBTYPE_EXTERNAL_BODY"; break; case SUBTYPE_XML_MSRTC_PIDF: chsubtype="SUBTYPE_XML_MSRTC_PIDF"; break; case SUBTYPE_ALL: chsubtype="SUBTYPE_ALL"; break; case SUBTYPE_UNKNOWN: chsubtype="SUBTYPE_UNKNOWN"; break; default: chsubtype="(didnt know this subtype existed)"; } fprintf(fp,"%sTYPE:[%s]\n",prefix,chtype); fprintf(fp,"%sSUBTYPE:[%s]\n",prefix,chsubtype); return 0; } opensips-2.2.2/modules/seas/encode_content_type.h000066400000000000000000000025561300170765700221770ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ int encode_content_type(char *hdrstart,int hdrlen,unsigned int bodi,char *where); int encode_accept(char *hdrstart,int hdrlen,unsigned int *bodi,char *where); int encode_mime_type(char *hdrstart,int hdrlen,unsigned int bodi,char *where); int print_encoded_mime_type(FILE *fp,char *hdr,int hdrlen,unsigned int* payload,int paylen,char *prefix); int print_encoded_content_type(FILE *fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix); int print_encoded_accept(FILE *fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix); opensips-2.2.2/modules/seas/encode_cseq.c000066400000000000000000000071551300170765700204120ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * ===================================================================================== * * Filename: xaddress.c * * Description: Address manipulation tools * * Version: 1.0 * Created: 17/11/05 02:09:44 CET * Revision: none * Compiler: gcc * * Author: Elias Baixas (EB), elias@conillera.net * Company: VozTele.com * * ===================================================================================== */ #define _GNU_SOURCE #include #include #include #include "../../mem/mem.h" #include "../../parser/parse_cseq.h" #include "../../dprint.h" #include "../../ut.h" #include "xaddress.h"/*for SLOG*/ /* Encoder for CSeq header * Returns the length of the encoded structure in bytes * FORMAT (byte meanings): * 1: method_id * 4: cseqnum in network byte order * 2: HDR-based ptr to the method number + number length * 2: HDR-based ptr to the method name + method length * */ int encode_cseq(char *hdrstart,int hdrlen,struct cseq_body *body,unsigned char *where) { unsigned int cseqnum; unsigned char i; /*which is the first bit set to 1 ? if i==0, the first bit, * if i==31, the last, if i==32, none*/ for(i=0;(!(body->method_id & (0x01<number,&cseqnum)<0){ LM_ERR("str2int(cseq number)\n"); return -1; } cseqnum=htonl(cseqnum); memcpy(&where[1],&cseqnum,4);/*I use 4 because 3261 says CSEq num must be 32 bits long*/ where[5]=(unsigned char)(body->number.s-hdrstart); where[6]=(unsigned char)(body->number.len); where[7]=(unsigned char)(body->method.s-hdrstart); where[8]=(unsigned char)(body->method.len); return 9; } int print_encoded_cseq(FILE *fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix) { unsigned int cseqnum; char *what; memcpy(&cseqnum,&payload[1],4); cseqnum=ntohl(cseqnum); fprintf(fp,"%sCSEQ NUMBER=%d==%.*s\n",prefix,cseqnum,payload[6],&hdr[payload[5]]); switch(payload[0]){ case 0: what="UNDEFINED"; break; case 1: what="INVITE"; break; case 2: what="CANCEL"; break; case 3: what="ACK"; break; case 4: what="BYE"; break; case 5: what="INFO"; break; case 6: what="OPTIONS"; break; case 7: what="UPDATE"; break; case 8: what="REGISTER"; break; case 9: what="MESSAGE"; break; case 10: what="SUBSCRIBE"; break; case 11: what="NOTIFY"; break; case 12: what="PRACK"; break; case 13: what="REFER"; break; case 14: what="OTHER"; break; default: what="UNKNOWN?"; break; } fprintf(fp,"%sCSEQ METHOD=%s==%.*s\n",prefix,what,payload[8],&hdr[payload[7]]); return 1; } opensips-2.2.2/modules/seas/encode_cseq.h000066400000000000000000000020671300170765700204140ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../str.h" #include "../../parser/msg_parser.h" int encode_cseq(char *hdrstart,int hdrlen,struct cseq_body *body,unsigned char *where); int print_encoded_cseq(FILE* fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix); opensips-2.2.2/modules/seas/encode_digest.c000066400000000000000000000166201300170765700207330ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * ===================================================================================== * * Filename: encode_digest.c * * Description: functions to encode/decode/print Digest headers ([proxy,www]-[authenticate,require]) * * Version: 1.0 * Created: 20/11/05 04:24:55 CET * Revision: none * Compiler: gcc * * Author: Elias Baixas (EB), elias@conillera.net * Company: VozTele.com * * ===================================================================================== */ #define _GNU_SOURCE #include #include "../../parser/digest/digest_parser.h" #include "../../parser/parse_uri.h" #include "../../parser/digest/digest.h" #include "encode_digest.h" #include "xaddress.h" #include "encode_header.h" #include "encode_uri.h" #define HAS_NAME_F 0x01 #define HAS_REALM_F 0x02 #define HAS_NONCE_F 0x04 #define HAS_URI_F 0x08 #define HAS_RESPONSE_F 0x10 #define HAS_ALG_F 0x20 #define HAS_CNONCE_F 0x40 #define HAS_OPAQUE_F 0x80 #define HAS_QoP_F 0x01 #define HAS_NC_F 0x02 /* * encodes a digest header body. * encoding is: * 1: flags * HAS_NAME_F 0x01 * HAS_REALM_F 0x02 * HAS_NONCE_F 0x04 * HAS_URI_F 0x08 * HAS_RESPONSE_F 0x10 * HAS_ALG_F 0x20 * HAS_CNONCE_F 0x40 * HAS_OPAQUE_F 0x80 * 1: flags * HAS_QoP_F 0x01 * HAS_NC_F 0x02 * 2: hdr-start based pointer to where the scheme starts + length of the scheme (must be Digest). * * for each field present, there are 2 bytes, one pointing the place where it starts, * the next signaling how long this field is. The URI is a special case, and is composed of 1 * byte telling how long is the URI structure, and then the encoded URI structure. */ int encode_digest(char *hdrstart,int hdrlen,dig_cred_t *digest,unsigned char *where) { int i=2,j=0;/* 2*flags */ unsigned char flags1=0,flags2=0; struct sip_uri sipuri; if(digest->username.whole.s && digest->username.whole.len){ flags1|=HAS_NAME_F; where[i++]=(unsigned char)(digest->username.whole.s-hdrstart); where[i++]=(unsigned char)digest->username.whole.len; } if(digest->realm.s && digest->realm.len){ flags1|=HAS_REALM_F; where[i++]=(unsigned char)(digest->realm.s-hdrstart); where[i++]=(unsigned char)digest->realm.len; } if(digest->nonce.s && digest->nonce.len){ flags1|=HAS_NONCE_F; where[i++]=(unsigned char)(digest->nonce.s-hdrstart); where[i++]=(unsigned char)digest->nonce.len; } if(digest->uri.s && digest->uri.len){ memset(&sipuri,0,sizeof(struct sip_uri)); flags1|=HAS_URI_F; if (parse_uri(digest->uri.s, digest->uri.len,&sipuri) < 0 ) { LM_ERR("Bad URI in address\n"); return -1; }else{ if((j=encode_uri2(hdrstart,hdrlen,digest->uri,&sipuri,&where[i+1]))<0){ LM_ERR("Error encoding the URI\n"); return -1; }else{ where[i]=(unsigned char)j; i+=(j+1); } } } if(digest->response.s && digest->response.len){ flags1|=HAS_RESPONSE_F; where[i++]=(unsigned char)(digest->response.s-hdrstart); where[i++]=(unsigned char)digest->response.len; } if(digest->alg.alg_str.s && digest->alg.alg_str.len){ flags1|=HAS_ALG_F; where[i++]=(unsigned char)(digest->alg.alg_str.s-hdrstart); where[i++]=(unsigned char)digest->alg.alg_str.len; } if(digest->cnonce.s && digest->cnonce.len){ flags1|=HAS_CNONCE_F; where[i++]=(unsigned char)(digest->cnonce.s-hdrstart); where[i++]=(unsigned char)digest->cnonce.len; } if(digest->opaque.s && digest->opaque.len){ flags1|=HAS_OPAQUE_F; where[i++]=(unsigned char)(digest->opaque.s-hdrstart); where[i++]=(unsigned char)digest->opaque.len; } if(digest->qop.qop_str.s && digest->qop.qop_str.len){ flags2|=HAS_QoP_F; where[i++]=(unsigned char)(digest->qop.qop_str.s-hdrstart); where[i++]=(unsigned char)digest->qop.qop_str.len; } if(digest->nc.s && digest->nc.len){ flags2|=HAS_NC_F; where[i++]=(unsigned char)(digest->nc.s-hdrstart); where[i++]=(unsigned char)digest->nc.len; } where[0]=flags1; where[1]=flags2; return i; } int print_encoded_digest(FILE *fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix) { int i=2;/* flags + flags1 */ unsigned char flags1,flags2; flags1=payload[0]; flags2=payload[1]; fprintf(fp,"%s",prefix); for(i=0;i #include #include #include "../../parser/parse_expires.h" /* * Encodes expires headers (content = delta-seconds) * 4: network-byte-order value * 2: hdr-based pointer to begin of value string + value string length */ int encode_expires(char *hdrstart,int hdrlen,exp_body_t *body,unsigned char *where) { int i; i=htonl(body->val); memcpy(where,&i,4); where[4]=(unsigned char)(body->text.s-hdrstart); where[5]=(unsigned char)(body->text.len); return 6; } int print_encoded_expires(FILE* fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix) { int i; memcpy(&i,payload,4); i=ntohl(i); fprintf(fp,"%sEXPIRES VALUE=%d==%.*s\n",prefix,i,payload[5],&hdr[payload[4]]); return 1; } opensips-2.2.2/modules/seas/encode_expires.h000066400000000000000000000017721300170765700211420ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ int encode_expires(char *hdrstart,int hdrlen,exp_body_t *body,unsigned char *where); int print_encoded_expires(FILE* fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix); opensips-2.2.2/modules/seas/encode_header.c000066400000000000000000000373251300170765700207110ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * ===================================================================================== * * Filename: xcontact.c * * Description: Contact encoding functions * * Version: 1.0 * Created: 19/11/05 14:33:38 CET * Revision: none * Compiler: gcc * * Author: Elias Baixas (EB), elias@conillera.net * Company: VozTele.com * * ===================================================================================== */ #define _GNU_SOURCE #include #include #include #include "../../str.h" #include "../../parser/msg_parser.h" #include "../../parser/hf.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/contact/contact.h" #include "../../parser/digest/digest.h" #include "../../parser/digest/digest_parser.h" #include "../../parser/parse_rr.h" #include "../../parser/parse_content.h" #include "../../parser/parse_cseq.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_methods.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "encode_to_body.h" #include "encode_contact.h" #include "encode_digest.h" #include "encode_via.h" #include "encode_cseq.h" #include "encode_route.h" #include "encode_content_length.h" #include "encode_content_type.h" #include "encode_expires.h" #include "encode_allow.h" #include "xaddress.h" #include "encode_msg.h" #include "encode_header.h" #define REL_PTR(a,b) ((unsigned char)(b-a)) #define MAX_CONTACT_LEN 50 #define MAX_XHDR_LEN 255 #define SEGREGATE 0x02 #define ONLY_URIS 0x01 #define HEADER_OFFSET_IDX 0 #define HEADER_LEN_IDX (HEADER_OFFSET_IDX+2) #define HEADER_NAME_LEN_IDX (HEADER_LEN_IDX+2) #define HEADER_PAYLOAD_IDX (HEADER_NAME_LEN_IDX+1) /* * This function encodes an arbitrary header into a chunk of bytes, * ready to be sent to the Application Server. * * The header codes start with this encoded-bytes: * 2: SIP-MSG-START based pointer to the header (including header name) * 2: length of the header * 1: length of the header name */ int encode_header(struct sip_msg *sipmsg,struct hdr_field *hdr,unsigned char *payload,int paylen) { int len=0; unsigned int integer,*methods=0; char *hdrstart,*tmp; unsigned short int ptr; struct to_body *tobody=0; struct via_body *viabody=0; struct cseq_body *cseqbody=0; char *msg,*myerror; int mlen; msg=sipmsg->buf; mlen=sipmsg->len; hdrstart = hdr->name.s; if(hdrstart-msg<0){ LM_ERR("header(%.*s) does not belong to sip_msg(hdrstartname.len,hdr->name.s); return -1; } ptr=htons((short int)(hdrstart-msg)); if((hdrstart-msg)>mlen){ LM_ERR("out of the sip_msg bounds (%d>%d)\n",ntohs(ptr),mlen); return -1; } if(hdr->len>(1<<16)){ LM_ERR("length of header too long\n"); return -1; } memcpy(payload,&ptr,2); ptr=htons((short int)(hdr->len)); memcpy(payload+HEADER_LEN_IDX,&ptr,2); payload[HEADER_NAME_LEN_IDX]=(unsigned char)hdr->name.len; switch(hdr->type){ case HDR_FROM_T: case HDR_TO_T: case HDR_REFER_TO_T: case HDR_RPID_T: if(!hdr->parsed){ if((tobody=pkg_malloc(sizeof(struct to_body)))==0){ myerror="Out of memory !!\n"; goto error; } parse_to(hdr->body.s,hdr->body.s+hdr->body.len+1,tobody); if (tobody->error == PARSE_ERROR) { myerror="bad (REFER,TO,FROM,RPID) header\n"; pkg_free(tobody); return 5; goto error; } hdr->parsed=(struct to_body*)tobody; }else tobody=(struct to_body*)hdr->parsed; if((len=encode_to_body(hdr->name.s,hdr->len,tobody,payload+5))<0){ myerror="parsing from or to header\n"; goto error; }else{ return 5+len; } break; case HDR_CONTACT_T: if(!hdr->parsed) if(parse_contact(hdr)<0){ myerror="parsing contact\n"; goto error; } if((len=encode_contact_body(hdr->name.s,hdr->len,(contact_body_t*)hdr->parsed,payload+5))<0){ myerror="encoding contact header\n"; goto error; }else{ return 5+len; } break; case HDR_ROUTE_T: case HDR_RECORDROUTE_T: if(!hdr->parsed) if(parse_rr(hdr)<0){ myerror="encoding route or recordroute\n"; goto error; } if((len=encode_route_body(hdr->name.s,hdr->len,(rr_t*)hdr->parsed,payload+5))<0){ myerror="encoding route or recordroute header\n"; goto error; }else{ return 5+len; } break; case HDR_CONTENTLENGTH_T: if(!hdr->parsed){ tmp=parse_content_length(hdr->body.s,hdr->body.s+hdr->body.len+1,(int*)&integer); if (tmp==0){ myerror="bad content_length header\n"; goto error; } hdr->parsed=(void*)(long)integer; } if((len=encode_contentlength(hdr->name.s,hdr->len,(long int)hdr->parsed,(char*)(payload+5)))<0){ myerror="encoding content-length header\n"; goto error; }else{ return 5+len; } break; case HDR_VIA_T: if(!hdr->parsed){ if((viabody=pkg_malloc(sizeof(struct via_body)))==0){ myerror="out of memory\n"; goto error; } memset(viabody,0,sizeof(struct via_body)); if(parse_via(hdr->body.s,hdr->body.s+hdr->body.len+1,viabody)==0){ myerror="encoding via \n"; goto error; } hdr->parsed=viabody; } if((len=encode_via_body(hdr->name.s,hdr->len,(struct via_body*)hdr->parsed,payload+5))<0){ myerror="encoding via header\n"; goto error; }else{ return 5+len; } break; case HDR_ACCEPT_T: if(!hdr->parsed){ if(parse_accept_hdr(sipmsg)<0){ return 5; } } if((len=encode_accept(hdr->name.s,hdr->len,(unsigned int*)hdr->parsed,(char*)(payload+5)))<0){ myerror="encoding via header\n"; goto error; }else{ return 5+len; } break; case HDR_CONTENTTYPE_T: if(!hdr->parsed){ if(parse_content_type_hdr(sipmsg)<0){ myerror="encoding content-type header\n"; goto error; } } if((len=encode_content_type(hdr->name.s,hdr->len,(unsigned int)get_content_type(sipmsg),(char*)(payload+5)))<0){ myerror="encoding via header\n"; goto error; }else{ return 5+len; } break; case HDR_CSEQ_T: if(!hdr->parsed){ if((cseqbody=pkg_malloc(sizeof(struct cseq_body)))==0){ myerror="out of memory\n"; goto error; } memset(cseqbody,0,sizeof(struct cseq_body)); if(parse_cseq(hdr->name.s,hdr->body.s+hdr->body.len+1,cseqbody)==0){ myerror="encoding cseq header\n"; goto error; } hdr->parsed=cseqbody; } if((len=encode_cseq(hdr->name.s,hdr->len,(struct cseq_body*)hdr->parsed,payload+5))<0){ myerror="encoding via header\n"; goto error; }else{ return 5+len; } break; case HDR_EXPIRES_T: if(!hdr->parsed){ if(parse_expires(hdr)<0){ myerror="encoding expires header\n"; goto error; } } if((len=encode_expires(hdr->name.s,hdr->len,(exp_body_t *)hdr->parsed,payload+5))<0){ myerror="encoding expires header\n"; goto error; }else{ return 5+len; } break; case HDR_ALLOW_T: if(!hdr->parsed){ if((methods=pkg_malloc(sizeof(unsigned int)))==0){ myerror="out of memory\n"; goto error; } *methods=0; if(parse_methods(&hdr->body,methods)!=0){ myerror="encoding allow header\n"; pkg_free(methods); return 5; /*goto error;*/ } hdr->parsed=methods; } if((len=encode_allow(hdr->name.s,hdr->len,(unsigned int*)hdr->parsed,(char*)(payload+5)))<0){ myerror="encoding allow header\n"; goto error; }else{ return 5+len; } break; case HDR_AUTHORIZATION_T: case HDR_PROXYAUTH_T: if(!hdr->parsed){ if(parse_credentials(hdr)<0){ myerror="encoding a digest header\n"; goto error; } } if((len=encode_digest(hdr->name.s,hdr->len,(dig_cred_t*)(&(((auth_body_t*)hdr->parsed)->digest)),payload+5))<0){ myerror="encoding allow header\n"; goto error; }else{ return 5+len; } break; default: return 5; } return 1; error: if(tobody) pkg_free(tobody); if(cseqbody) pkg_free(cseqbody); if(viabody) free_via_list(viabody); if(methods) pkg_free(methods); LM_ERR("%s",myerror); return -1; } int print_encoded_header(FILE *fp,char *msg,int msglen,unsigned char *payload,int len,char type,char *prefix) { char *hdr_start_ptr; short int start_idx,i; memcpy(&start_idx,payload,2); start_idx=ntohs(start_idx); hdr_start_ptr = &msg[start_idx]; memcpy(&i,payload+HEADER_LEN_IDX,2); i=ntohs(i); fprintf(fp,"%sHEADER NAME:[%.*s]\n",prefix,payload[HEADER_NAME_LEN_IDX],hdr_start_ptr); fprintf(fp,"%sHEADER:[%.*s]\n",prefix,i-2,hdr_start_ptr); fprintf(fp,"%sHEADER CODE=",prefix); for(i=0;i #include #include #include #include #include #include #include #include "../../parser/msg_parser.h" #include "../../parser/parse_via.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "encode_header.h" #include "encode_uri.h" #include "encode_msg.h" #include "xaddress.h" unsigned int theSignal = 0xAA55AA55;/*which is: 10101010-01010101-10101010-01010101*/ char get_header_code(struct hdr_field *hf) { switch(hf->type){ case HDR_CALLID_T: return 'i'; case HDR_CONTACT_T: return 'm'; case HDR_CONTENTLENGTH_T: return 'l'; case HDR_CONTENTTYPE_T: return 'c'; case HDR_FROM_T: return 'f'; case HDR_SUBJECT_T: return 's'; case HDR_SUPPORTED_T: return 'k'; case HDR_TO_T: return 't'; case HDR_VIA_T: return 'v'; case HDR_ROUTE_T: return 'r'; case HDR_RECORDROUTE_T: return 'R'; case HDR_ALLOW_T: return 'a'; case HDR_ACCEPT_T: return 'A'; case HDR_CSEQ_T: return 'S'; case HDR_REFER_TO_T: return 'o'; case HDR_RPID_T: return 'p'; case HDR_EXPIRES_T: return 'P'; case HDR_AUTHORIZATION_T: return 'H'; case HDR_PROXYAUTH_T: return 'z'; default: return 'x'; } return 'x'; } /* This function extracts meta-info from the sip_msg structure and * formats it so that it can be used to rapidly access the message structured * parts. * * RETURNS: LENGTH of structure on success, <0 if failure * if there was failure, you dont need to pkg_free the payload (it is done inside). * if there was success, you __NEED_TO_PKG_FREE_THE_PAYLOAD__ from the calling function. * * The encoded meta-info is composed by 3 sections: * * MSG_META_INFO: * 2: short int in network-byte-order, if <100, the msg is a REQUEST and the int * is the code of the METHOD. if >100, it is a RESPONSE and the int is the code * of the response. * 2: short int in NBO: payload-start based pointer (index) to where the SIP MSG starts. * 2: short int in NBO: the sip-message length * 2: METHOD or CODE string SIP-START-based pointer and length * 2: R-URI or REASON PHRASE string SIP-START-based pointer and length * 2: VERSION string SIP-START-based pointer and length * 2: short int in NBO: start of the content of the SIP message * [1+N]: in case this is a request, the length of the encoded-uri and the encoded-uri * 1: how many present headers have been found. * * MSG_HEADERS_INDEX: * N*3: groups of 3 bytes, each one describing a header struct: the first byte * is a letter that corresponds to a header type, the second and third bytes are a NBO * inidex to where this struct begins within the HEADERS_META_INFO section. * * HEADERS_META_INFO: * M: all the codified headers meta info structs one after another * * SIP_MSG: * the SIP message as it has been received. * * The length of the structure, will be ((short*)payload)[1] + ((short*)payload)[2] * * TODO: msg->parsed_uri msg->parsed_orig_uri_ok, msg->first_line->u.request.uri * buggy and little bit fuzzy */ int encode_msg(struct sip_msg *msg,char *payload,int len) { int i,j,k,u,request; unsigned short int h; struct hdr_field* hf; struct msg_start* ms; struct sip_uri miuri; char *myerror=NULL; ptrdiff_t diff; str body = {NULL,0}; if(len < MAX_ENCODED_MSG + MAX_MESSAGE_LEN) return -1; if(parse_headers(msg,HDR_EOH_F,0)<0){ myerror="in parse_headers"; goto error; } memset(payload,0,len); ms=&msg->first_line; if(ms->type == SIP_REQUEST) request=1; else if(ms->type == SIP_REPLY) request=0; else{ myerror="message is neither request nor response"; goto error; } if(request) { for(h=0;h<32;j=(0x01<u.request.method_value) break; } else { h=(unsigned short)(ms->u.reply.statuscode); } if(h==32){/*statuscode wont be 32...*/ myerror="unknown message type\n"; goto error; } h=htons(h); /*first goes the message code type*/ memcpy(payload,&h,2); h=htons((unsigned short int)msg->len); /*then goes the message start idx, but we'll put it later*/ /*then goes the message length (we hope it to be less than 65535 bytes...)*/ memcpy(&payload[MSG_LEN_IDX],&h,2); /*then goes the content start index (starting from SIP MSG START)*/ get_body(msg,&body); if(0>(diff=(body.s-msg->buf))){ myerror="body starts before the message (uh ?)"; goto error; }else h=htons((unsigned short int)diff); memcpy(payload+CONTENT_IDX,&h,2); payload[METHOD_CODE_IDX]=(unsigned char)(request? (ms->u.request.method.s-msg->buf): (ms->u.reply.status.s-msg->buf)); payload[METHOD_CODE_IDX+1]=(unsigned char)(request? (ms->u.request.method.len): (ms->u.reply.status.len)); payload[URI_REASON_IDX]=(unsigned char)(request? (ms->u.request.uri.s-msg->buf): (ms->u.reply.reason.s-msg->buf)); payload[URI_REASON_IDX+1]=(unsigned char)(request? (ms->u.request.uri.len): (ms->u.reply.reason.len)); payload[VERSION_IDX]=(unsigned char)(request? (ms->u.request.version.s-msg->buf): (ms->u.reply.version.s-msg->buf)); if(request){ if (parse_uri(ms->u.request.uri.s,ms->u.request.uri.len, &miuri)<0){ LM_ERR("<%.*s>\n",ms->u.request.uri.len,ms->u.request.uri.s); myerror="while parsing the R-URI"; goto error; } if(0>(j=encode_uri2(msg->buf, ms->u.request.method.s-msg->buf+ms->len, ms->u.request.uri,&miuri, (unsigned char*)&payload[REQUEST_URI_IDX+1]))) { myerror="ENCODE_MSG: ERROR while encoding the R-URI"; goto error; } payload[REQUEST_URI_IDX]=(unsigned char)j; k=REQUEST_URI_IDX+1+j; }else k=REQUEST_URI_IDX; u=k; k++; for(i=0,hf=msg->headers;hf;hf=hf->next,i++); i++;/*we do as if there was an extra header, that marks the end of the previous header in the headers hashtable(read below)*/ j=k+3*i; for(i=0,hf=msg->headers;hf;hf=hf->next,k+=3){ payload[k]=(unsigned char)(hf->type & 0xFF); h=htons(j); /*now goes a payload-based-ptr to where the header-code starts*/ memcpy(&payload[k+1],&h,2); /*TODO fix this... fixed with k-=3?*/ if(0>(i=encode_header(msg,hf,(unsigned char*)(payload+j),MAX_ENCODED_MSG+MAX_MESSAGE_LEN-j))){ LM_ERR("encoding header %.*s\n",hf->name.len,hf->name.s); goto error; k-=3; continue; } j+=(unsigned short int)i; } /*now goes the number of headers that have been found, right after the meta-msg-section*/ payload[u]=(unsigned char)((k-u-1)/3); j=htons(j); /*now copy the number of bytes that the headers-meta-section has occupied,right afther * headers-meta-section(the array with ['v',[2:where],'r',[2:where],'R',[2:where],...] * this is to know where the LAST header ends, since the length of each header-struct * is calculated substracting the nextHeaderStart - presentHeaderStart * the k+1 is because payload[k] is usually the letter*/ memcpy(&payload[k+1],&j,2); k+=3; j=ntohs(j); /*now we copy the headers-meta-section after the msg-headers-meta-section*/ /*memcpy(&payload[k],payload2,j);*/ /*j+=k;*/ /*pkg_free(payload2);*/ /*now we copy the actual message after the headers-meta-section*/ memcpy(&payload[j],msg->buf,msg->len); LM_DBG("msglen = %d,msg starts at %d\n",msg->len,j); j=htons(j); /*now we copy at the beginning, the index to where the actual message starts*/ memcpy(&payload[MSG_START_IDX],&j,2); return GET_PAY_SIZE( payload ); error: LM_ERR("%s\n",myerror); return -1; } int decode_msg(struct sip_msg *msg,char *code, unsigned int len) { unsigned short int h; char *myerror=NULL; memcpy(&h,&code[2],2); h=ntohs(h); /*TODO use shorcuts in meta-info header.*/ msg->buf=&code[h]; memcpy(&h,&code[4],2); h=ntohs(h); msg->len=h; if(parse_headers(msg,HDR_EOH_F,0)<0){ myerror="in parse_headers"; goto error; } error: LM_ERR("(%s)\n",myerror); return -1; } int print_encoded_msg(int fd,char *code,char *prefix) { unsigned short int i,j,k,l,m,msglen; char r,*msg; unsigned char *payload; FILE *fp; fp = fdopen(fd, "w*"); if(fp == NULL) return -1; payload=(unsigned char*)code; memcpy(&i,code,2); memcpy(&j,&code[MSG_START_IDX],2); memcpy(&msglen,&code[MSG_LEN_IDX],2); i=ntohs(i); j=ntohs(j); msglen=ntohs(msglen); for(k=0;k #include "../../str.h" #include "../../parser/parse_param.h" #include "../../parser/parse_to.h" #include "../../parser/parse_via.h" #include "../../parser/parse_disposition.h" #include "encode_parameters.h" #define REL_PTR(a,b) (unsigned char)((b)-(a)) /** * Returns how many bytes in *where have been used * * TODO this is little shitty, someone should unify all the param flavours * to a sigle universal type of parameter (to_param,param,disposition_param) * the way is done here, at least, we dont have the parameter-hanling code spread all around. */ int encode_parameters(unsigned char *where,void *pars,char *hdrstart,void *_body,char to) { struct param *parametro,*params; struct to_param *toparam,*toparams; struct disposition_param *dparam,*dparams; struct via_param *vparam,*vparams; struct via_body *vbody; struct to_body *tbody; char *mylittlepointer,*paramstart; int i,j,paramlen; i=0; if(!pars) return 0; if(to=='t'){ toparams=(struct to_param*)pars; tbody=(struct to_body*)_body; for(toparam=toparams;toparam;toparam=toparam->next){ where[i++]=(unsigned char)(toparam->name.s-hdrstart); if(toparam->value.s) mylittlepointer=toparam->value.s; else if(toparam->next) mylittlepointer=toparam->next->name.s; else mylittlepointer=toparam->name.s+toparam->name.len+1; where[i++]=(unsigned char)(mylittlepointer-hdrstart); } if((toparam=tbody->last_param)){ if(toparam->value.s) mylittlepointer=toparam->value.s+toparam->value.len; else mylittlepointer=toparam->name.s+toparam->name.len; where[i++]=(unsigned char)(mylittlepointer-hdrstart+1); } return i; }else if(to=='n'){ params=(struct param*)pars; for(parametro=reverseParameters(params);parametro;parametro=parametro->next){ where[i++]=(unsigned char)(parametro->name.s-hdrstart); if(parametro->body.s) mylittlepointer=parametro->body.s; else if(parametro->next) mylittlepointer=parametro->next->name.s; else mylittlepointer=parametro->name.s+parametro->name.len+1; where[i++]=(unsigned char)(mylittlepointer-hdrstart); } /*look for the last parameter*/ /*WARNING the ** parameters are in reversed order !!! */ /*TODO parameter encoding logic should be moved to a specific function...*/ for(parametro=params;parametro && parametro->next;parametro=parametro->next); /*printf("PARAMETRO:%.*s\n",parametro->name.len,parametro->name.s);*/ if(parametro){ if(parametro->body.s) mylittlepointer=parametro->body.s+parametro->body.len; else mylittlepointer=parametro->name.s+parametro->name.len; where[i++]=(unsigned char)(mylittlepointer-hdrstart+1); } return i; }else if(to=='d'){ dparams=(struct disposition_param*)pars; for(dparam=dparams;dparam;dparam=dparam->next){ where[i++]=(unsigned char)(dparam->name.s-hdrstart); if(dparam->body.s) mylittlepointer=dparam->body.s; else if(dparam->next) mylittlepointer=dparam->next->name.s; else mylittlepointer=dparam->name.s+dparam->name.len+1; where[i++]=(unsigned char)(mylittlepointer-hdrstart); } /*WARNING the ** parameters are in reversed order !!! */ /*TODO parameter encoding logic should be moved to a specific function...*/ for(dparam=dparams;dparam && dparam->next;dparam=dparam->next); if(dparam){ if(dparam->body.s) mylittlepointer=dparam->body.s+dparam->body.len; else mylittlepointer=dparam->name.s+dparam->name.len; where[i++]=(unsigned char)(mylittlepointer-hdrstart+1); } return i; }else if(to=='v'){ vparams=(struct via_param*)pars; vbody=(struct via_body*)_body; for(vparam=vparams;vparam;vparam=vparam->next){ where[i++]=REL_PTR(hdrstart,vparam->name.s); if(vparam->value.s) mylittlepointer=vparam->value.s; else if(vparam->next) mylittlepointer=vparam->next->name.s; else mylittlepointer=vparam->name.s+vparam->name.len+1; where[i++]=REL_PTR(hdrstart,mylittlepointer); } if((vparam=vbody->last_param)){ if(vparam->value.s) mylittlepointer=vparam->value.s+vparam->value.len; else mylittlepointer=vparam->name.s+vparam->name.len; where[i++]=REL_PTR(hdrstart,mylittlepointer+1); } return i; }else if(to=='u'){ paramlen=*((int*)_body); paramstart=(char *)pars; j=i=0; if(paramstart==0 || paramlen==0) return 0; /*the first parameter start index, I suppose paramstart points to the first letter of the first parameter: sip:elias@voztele.com;param1=true;param2=false paramstart points to __^ each parameter is codified with its {param_name_start_idx,[param_value_start_idx|next_param_start_idx]} */ where[j++]=paramstart-hdrstart; while(i #include #include #include "../../mem/mem.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" #include "../../dprint.h" #include "encode_to_body.h" #include "encode_uri.h" #include "encode_header.h" #include "encode_parameters.h" #include "xaddress.h" /* Encoder for From and To headers. * Returns the length of the encoded structure in bytes * FORMAT (byte meanings): * 1: flags * 0x10 : there is a Display Name * 0x20 : there is a tag parameter * 0x40 : there are other parameters * 1: length of the XURI-encoded uri in bytes. * [2]: optionally, 1 HDR-based ptr to the displayname + the length of the name * [2]: optionally, 1 HDR-based ptr to the tag + the length of the tag parameter * N: the XURI-encoded URI. * [N:] optionally, HDR-based pointers to the different header-parameters * */ int encode_to_body(char *hdrstart,int hdrlen,struct to_body *body,unsigned char *where) { int i=2,j=0;/* 1*flags + 1*URI_len*/ unsigned char flags=0; struct sip_uri puri; if(body->display.s && body->display.len){ flags|=HAS_DISPLAY_F; if(body->display.s[0]=='\"'){ body->display.s++; body->display.len-=2; } where[i++]=(unsigned char)(body->display.s-hdrstart); where[i++]=(unsigned char)(body->display.len); } if(body->tag_value.s && body->tag_value.len){ flags|=HAS_TAG_F; where[i++]=(unsigned char)(body->tag_value.s-hdrstart); where[i++]=(unsigned char)body->tag_value.len; } if (parse_uri(body->uri.s, body->uri.len,&puri) < 0 ) { LM_ERR("Bad URI in address\n"); return -1; }else{ if((j=encode_uri2(hdrstart,hdrlen,body->uri,&puri,&where[i]))<0){ LM_ERR("failed to codify the URI\n"); return -1; }else{ i+=j; } } where[0]=flags; where[1]=(unsigned char)j; i+=encode_parameters(&where[i],(void *)body->param_lst,hdrstart,body,'t'); return i; } int print_encoded_to_body(FILE *fp,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix) { int i=2;/* flags + urilength */ unsigned char flags=0; flags=payload[0]; fprintf(fp,"%s",prefix); for(i=0;i #include #include #include #include "../../mem/mem.h" #include "../../parser/msg_parser.h" #include "../../dprint.h" #include "encode_uri.h" #include "encode_parameters.h" #include "encode_header.h" #include "xaddress.h" #define REL_PTR(a,b) ((unsigned char)(b-a)) /*The XURI is one of the most important parts in SEAS, as * most of the SIP MESSAGE structured headers (that is, * headers which have a well-specified body construction) * use the URI as the body (ie. via, route, record-route, * contact, from, to, RURI) * * the XURI is a codified structure of flags and pointers * that ease the parsing of the URI string. * * 1: The first byte of the structure, is a * HEADER_START-based pointer to the beginning of the URI * (including the "sip:"). * 1: The next byte is the length of the uri, so URIMAX * is 256 (enough...) * 2: Flags specifying the parts that are present in the URI * * as follows: * 1: first byte * SIP_OR_TEL_F 0x01 it is SIP or TEL uri. * SECURE_F 0x02 it is secure or not (SIPS,TELS) * USER_F 0x04 it as a user part (user@host) * PASSWORD_F 0x08 it has a password part * HOST_F 0x10 it has a host part * PORT_F 0x20 it has a port part * PARAMETERS_F 0x40 it has a port part * HEADERS_F 0x80 it has a port part * 1: second byte * TRANSPORT_F 0x01 it has other parameters * TTL_F 0x02 it has headers * USER_F 0x04 it has the transport parameter * METHOD_F 0x08 it has the ttl parameter * MADDR_F 0x10 it has the user parameter * LR_F 0x20 it has the method parameter * * All the following bytes are URI_START-based pointers to * the fields that are present in the uri, as specified by * the flags. They must appear in the same order shown in * the flags, and only appear if the flag was set to 1. * * the end of the field, will be the place where the * following pointer points to, minus one (note that all the * fields present in a URI are preceded by 1 character, ie * sip[:user][:passwod][@host][:port][;param1=x][;param2=y][?hdr1=a][&hdr2=b]$p * it will be necessary to have a pointer at the end, * pointing two past the end of the uri, so that the length * of the last header can be computed. * * The reason to have the OTHER and HEADERS flags at the * beginning(just after the strictly-uri stuff), is that it * will be necessary to know the length of the parameters * section and the headers section. * * The parameters can * appear in an arbitrary order, so they won't be following * the convention of transport-ttl-user-method-maddr-lr, so * we can't rely on the next pointer to compute the length * of the previous pointer field, as the ttl param can * appear before the transport param. so the parameter * pointers must have 2 bytes: pointer+length. * */ int encode_uri2(char *hdr,int hdrlen,str uri_str, struct sip_uri *uri_parsed,unsigned char *payload) { int i=4,j;/* 1*pointer+1*len+2*flags*/ unsigned int scheme; unsigned char flags1=0,flags2=0,uriptr; uriptr=REL_PTR(hdr,uri_str.s); if(uri_str.len>255 || uriptr>hdrlen){ LM_ERR("uri too long, or out of the sip_msg bounds\n"); return -1; } payload[0]=uriptr; payload[1]=(unsigned char)uri_str.len; if(uri_parsed->user.s && uri_parsed->user.len){ flags1 |= USER_F; payload[i++]=REL_PTR(uri_str.s,uri_parsed->user.s); } if(uri_parsed->passwd.s && uri_parsed->passwd.len){ flags1 |= PASSWORD_F; payload[i++]=REL_PTR(uri_str.s,uri_parsed->passwd.s); } if(uri_parsed->host.s && uri_parsed->host.len){ flags1 |= HOST_F; payload[i++]=REL_PTR(uri_str.s,uri_parsed->host.s); } if(uri_parsed->port.s && uri_parsed->port.len){ flags1 |= PORT_F; payload[i++]=REL_PTR(uri_str.s,uri_parsed->port.s); } if(uri_parsed->params.s && uri_parsed->params.len){ flags1 |= PARAMETERS_F; payload[i++]=REL_PTR(uri_str.s,uri_parsed->params.s); } if(uri_parsed->headers.s && uri_parsed->headers.len){ flags1 |= HEADERS_F; payload[i++]=REL_PTR(uri_str.s,uri_parsed->headers.s); } payload[i]=(unsigned char)(uri_str.len+1); i++; if(uri_parsed->transport.s && uri_parsed->transport.len){ flags2 |= TRANSPORT_F; payload[i]=REL_PTR(uri_str.s,uri_parsed->transport.s); payload[i+1]=(unsigned char)(uri_parsed->transport.len); i+=2; } if(uri_parsed->ttl.s && uri_parsed->ttl.len){ flags2 |= TTL_F; payload[i]=REL_PTR(uri_str.s,uri_parsed->ttl.s); payload[i+1]=(unsigned char)uri_parsed->ttl.len; i+=2; } if(uri_parsed->user_param.s && uri_parsed->user_param.len){ flags2 |= USER_F; payload[i]=REL_PTR(uri_str.s,uri_parsed->user_param.s); payload[i+1]=(unsigned char)uri_parsed->user_param.len; i+=2; } if(uri_parsed->method.s && uri_parsed->method.len){ flags2 |= METHOD_F; payload[i]=REL_PTR(uri_str.s,uri_parsed->method.s); payload[i+1]=(unsigned char)uri_parsed->method.len; i+=2; } if(uri_parsed->maddr.s && uri_parsed->maddr.len){ flags2 |= MADDR_F; payload[i]=REL_PTR(uri_str.s,uri_parsed->maddr.s); payload[i+1]=(unsigned char)uri_parsed->maddr.len; i+=2; } if(uri_parsed->lr.s && uri_parsed->lr.len){ flags2 |= LR_F; payload[i]=REL_PTR(uri_str.s,uri_parsed->lr.s); payload[i+1]=(unsigned char)uri_parsed->lr.len; i+=2; } /*in parse_uri, when there's a user=phone, the type * is set to TEL_URI_T, even if there's a sip: in the beginning * so lets check it by ourselves: switch(uri_parsed->type){ case SIP_URI_T: flags1 |= SIP_OR_TEL_F; break; case SIPS_URI_T: flags1 |= (SIP_OR_TEL_F|SECURE_F); break; case TEL_URI_T: break; case TELS_URI_T: flags1 |= SECURE_F; break; default: return -1; }*/ #define SIP_SCH 0x3a706973 #define SIPS_SCH 0x73706973 #define TEL_SCH 0x3a6c6574 #define TELS_SCH 0x736c6574 scheme=uri_str.s[0]+(uri_str.s[1]<<8)+(uri_str.s[2]<<16)+(uri_str.s[3]<<24); scheme|=0x20202020; if (scheme==SIP_SCH){ flags1 |= SIP_OR_TEL_F; }else if(scheme==SIPS_SCH){ if(uri_str.s[4]==':'){ flags1 |= (SIP_OR_TEL_F|SECURE_F); }else goto error; }else if (scheme==TEL_SCH){ /*nothing*/ }else if (scheme==TELS_SCH){ if(uri_str.s[4]==':'){ flags1 |= SECURE_F; } }else goto error; payload[2]=flags1; payload[3]=flags2; j=i; i+=encode_parameters(&payload[i],uri_parsed->params.s,uri_str.s,&uri_parsed->params.len,'u'); if(ihdrlen){ fprintf(fp,"bad index for start of uri: hdrlen=%d uri_index=%d\n",hdrlen,uriidx); return -1; } ch_uriptr = hdrstart+uriidx; urilen=payload[1]; flags1=payload[2]; flags2=payload[3]; fprintf(fp,"%sURI:[%.*s]\n",prefix,urilen,ch_uriptr); uritype=flags1&SIP_OR_TEL_F?"SIP":"TEL"; secure=flags1&SECURE_F?"S":""; fprintf(fp,"%s TYPE:[%s%s]\n",prefix,uritype,secure); if(flags1 & USER_F){ fprintf(fp,"%s USER:[%.*s]\n",prefix,(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; } if(flags1 & PASSWORD_F){ fprintf(fp,"%s PASSWORD=[%.*s]\n",prefix,(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; } if(flags1 & HOST_F){ fprintf(fp,"%s HOST=[%.*s]\n",prefix,(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; } if(flags1 & PORT_F){ fprintf(fp,"%s PORT=[%.*s]\n",prefix,(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; } if(flags1 & PARAMETERS_F){ fprintf(fp,"%s PARAMETERS=[%.*s]\n",prefix,(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; } if(flags1 & HEADERS_F){ fprintf(fp,"%s HEADERS=[%.*s]\n",prefix,(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; } ++i; if(flags2 & TRANSPORT_F){ fprintf(fp,"%s TRANSPORT=[%.*s]\n",prefix,payload[i+1],&ch_uriptr[payload[i]]); i+=2; } if(flags2 & TTL_F){ fprintf(fp,"%s TTL_F=[%.*s]\n",prefix,payload[i+1],&ch_uriptr[payload[i]]); i+=2; } if(flags2 & USER_F){ fprintf(fp,"%s USER_F=[%.*s]\n",prefix,payload[i+1],&ch_uriptr[payload[i]]); i+=2; } if(flags2 & METHOD_F){ fprintf(fp,"%s METHOD_F=[%.*s]\n",prefix,payload[i+1],&ch_uriptr[payload[i]]); i+=2; } if(flags2 & MADDR_F){ fprintf(fp,"%s MADDR_F=[%.*s]\n",prefix,payload[i+1],&ch_uriptr[payload[i]]); i+=2; } if(flags2 & LR_F){ fprintf(fp,"%s LR_F=[%.*s]\n",prefix,payload[i+1],&ch_uriptr[payload[i]]); i+=2; } print_encoded_parameters(fp,&payload[i],ch_uriptr,paylen-i,prefix); return 0; } int print_uri_junit_tests(char *hdrstart,int hdrlen,unsigned char *payload,int paylen,int fd,char also_hdr,char *prefix) { int i=4,k=0,m=0;/*1*pointer+1*len+2*flags*/ unsigned char uriidx=0,flags1=0,flags2=0,urilen; char *ch_uriptr,*aux,*aux2,*aux3,*uritype=NULL,*secure=NULL; FILE *fp; if ( (fp = fdopen(fd, "w*")) == NULL) return -1; uriidx=payload[0]; if(uriidx>hdrlen){ fprintf(fp,"bad index for start of uri: hdrlen=%d uri_index=%d\n",hdrlen,uriidx); return -1; } if(also_hdr) dump_standard_hdr_test(hdrstart,hdrlen,payload,paylen,fd); ch_uriptr = hdrstart+uriidx; urilen=payload[1]; flags1=payload[2]; flags2=payload[3]; fprintf(fp,"%stoString=(S)%.*s\n",prefix,urilen,ch_uriptr); uritype=flags1&SIP_OR_TEL_F?"sip":"tel"; secure=flags1&SECURE_F?"s":""; fprintf(fp,"%sgetScheme=(S)%s%s\n",prefix,uritype,secure); fprintf(fp,"%sisSecure=(B)%s\n",prefix,flags1&SECURE_F?"true":"false"); fprintf(fp,"%sisSipURI=(B)%s\n",prefix,"true"); fprintf(fp,"%sgetUser=(S)",prefix); if(flags1 & USER_F){ fprintf(fp,"%.*s\n",(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; }else fprintf(fp,"(null)\n"); fprintf(fp,"%sgetUserPassword=(S)",prefix); if(flags1 & PASSWORD_F){ fprintf(fp,"%.*s\n",(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; }else fprintf(fp,"(null)\n"); fprintf(fp,"%sgetHost=(S)",prefix); if(flags1 & HOST_F){ fprintf(fp,"%.*s\n",(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; }else fprintf(fp,"(null)\n");/*can't happen*/ fprintf(fp,"%sgetPort=(I)",prefix); if(flags1 & PORT_F){ fprintf(fp,"%.*s\n",(payload[i+1]-1)-payload[i],&ch_uriptr[payload[i]]); ++i; }else fprintf(fp,"(null)\n"); /*user=phone;transport=udp*/ if(flags1 & PARAMETERS_F){ aux=&ch_uriptr[payload[i]]; aux2=NULL; aux3=aux; m=(payload[i+1]-1-payload[i]); fprintf(fp,"%sgetParameter=(SAVP)",prefix);/*SVP = Attribute Value Pair*/ for(k=0;k<=m;k++){ if((aux3[k]==';'||(k==m)) && aux2==NULL){/*no parameterValue was found*/ fprintf(fp,"%.*s=;",(int)(aux3-aux+k),aux); aux2=NULL;/*resets the parameterValue-start pointer*/ aux=aux3+1+k;/*points to the next parameter*/ }else if((aux3[k]==';'||(k==m)) && aux2!=NULL){ fprintf(fp,"%.*s=%.*s;",(int)(aux2-aux),aux,(int)(aux3-aux2-1+k),aux2+1); aux2=NULL; aux=aux3+1+k; } else if(aux3[k]=='='){ aux2=aux3+k; } } fprintf(fp,"\n"); ++i; } if(flags1 & HEADERS_F){ aux=&ch_uriptr[payload[i]]; aux2=NULL; aux3=aux; m=(payload[i+1]-1-payload[i]); fprintf(fp,"%sgetHeader=(SAVP)",prefix); for(k=0;k<=m;k++){ if((aux3[k]==';'||(k==m)) && aux2==NULL){/*no parameterValue was found*/ fprintf(fp,"%.*s=;",(int)(aux3-aux+k),aux); aux2=NULL;/*resets the parameterValue-start pointer*/ aux=aux3+1+k;/*points to the next parameter*/ }else if((aux3[k]==';'||(k==m)) && aux2!=NULL){ fprintf(fp,"%.*s=%.*s;",(int)(aux2-aux),aux,(int)(aux3-aux2-1+k),aux2+1); aux2=NULL; aux=aux3+1+k; } else if(aux3[k]=='='){ aux2=aux3+k; } } fprintf(fp,"\n"); ++i; } ++i; fprintf(fp,"%sgetTransportParam=(S)",prefix); if(flags2 & TRANSPORT_F){ fprintf(fp,"%.*s\n",payload[i+1],&ch_uriptr[payload[i]]); i+=2; }else fprintf(fp,"(null)\n"); fprintf(fp,"%sgetTTLparam=(I)",prefix); if(flags2 & TTL_F){ fprintf(fp,"%.*s\n",payload[i+1],&ch_uriptr[payload[i]]); i+=2; }else fprintf(fp,"(null)\n"); fprintf(fp,"%sgetUserParam=(S)",prefix); if(flags2 & USER_F){ fprintf(fp,"%.*s\n",payload[i+1],&ch_uriptr[payload[i]]); i+=2; }else fprintf(fp,"(null)\n"); fprintf(fp,"%sgetMethodParam=(S)",prefix); if(flags2 & METHOD_F){ fprintf(fp,"%.*s\n",payload[i+1],&ch_uriptr[payload[i]]); i+=2; }else fprintf(fp,"(null)\n"); fprintf(fp,"%sgetMAddrParam=(S)",prefix); if(flags2 & MADDR_F){ fprintf(fp,"%.*s\n",payload[i+1],&ch_uriptr[payload[i]]); i+=2; }else fprintf(fp,"(null)\n"); if(flags2 & LR_F){ i+=2; } fprintf(fp,"\n"); return 0; } opensips-2.2.2/modules/seas/encode_uri.h000066400000000000000000000030201300170765700202460ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../str.h" #include "../../parser/msg_parser.h" #define SIP_OR_TEL_F 0x01 #define SECURE_F 0x02 #define USER_F 0x04 #define PASSWORD_F 0x08 #define HOST_F 0x10 #define PORT_F 0x20 #define PARAMETERS_F 0x40 #define HEADERS_F 0x80 #define TRANSPORT_F 0x01 #define TTL_F 0x02 #define USER_F 0x04 #define METHOD_F 0x08 #define MADDR_F 0x10 #define LR_F 0x20 #define MAX_XURI_LEN 23 int encode_uri2(char *hdr,int hdrlen,str uri_str, struct sip_uri *uri_parsed,unsigned char *where); int print_encoded_uri(FILE *fp,unsigned char *payload,int paylen,char *hdr,int hdrlen,char *prefix); int print_uri_junit_tests(char *hdrstart,int hdrlen,unsigned char *payload,int paylen,int fd,char also_hdr,char *prefix); opensips-2.2.2/modules/seas/encode_via.c000066400000000000000000000171331300170765700202330ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * ===================================================================================== * * Filename: encode_via.c * * Description: functions to encode VIA headers * * Version: 1.0 * Created: 21/11/05 02:30:50 CET * Revision: none * Compiler: gcc * * Author: Elias Baixas (EB), elias@conillera.net * Company: VozTele.com * * ===================================================================================== */ #define _GNU_SOURCE #include #include "../../parser/parse_via.h" #include "../../parser/parse_uri.h" #include "encode_via.h" #include "encode_uri.h" #include "xaddress.h" #include "encode_header.h" #include "encode_parameters.h" #define HAS_PARAMS_F 0x01 #define HAS_BRANCH_F 0x02 #define HAS_RECEIVED_F 0x04 #define HAS_RPORT_F 0x08 #define HAS_I_F 0x10 #define HAS_ALIAS_F 0x20 #define HAS_PORT_F 0x40 #define REL_PTR(a,b) (unsigned char)((b)-(a)) /* * encodes a (maybe aggregated) via header. * encoding is: * 1: flags * 1: number of vias present * N: fore each via present, the length of the via * N*M: the via structures concatenated */ int encode_via_body(char *hdr,int hdrlen,struct via_body *via_parsed,unsigned char *where) { int i=0,k,via_offset; unsigned char tmp[500]; struct via_body *myvia; if(via_parsed) for(via_offset=0,i=0,myvia=via_parsed;myvia;myvia=myvia->next,i++){ if((k=encode_via(hdr,hdrlen,myvia,&tmp[via_offset]))<0){ LM_ERR("failed to parse via number %d\n",i); return -1; } where[2+i]=(unsigned char)k; via_offset+=k; } else return -1; where[1]=(unsigned char)i;/*how may vias there are*/ memcpy(&where[2+i],tmp,via_offset); return 2+i+via_offset; } /* Encoder for vias. * Returns the length of the encoded structure in bytes * FORMAT (byte meanings): * 1: flags * */ int encode_via(char *hdrstart,int hdrlen,struct via_body *body,unsigned char *where) { int i;/* 1*flags + 1*hostport_len*/ unsigned char flags=0; where[1]=REL_PTR(hdrstart,body->name.s); where[2]=REL_PTR(hdrstart,body->version.s); where[3]=REL_PTR(hdrstart,body->transport.s); where[4]=REL_PTR(hdrstart,body->transport.s+body->transport.len+1); where[5]=REL_PTR(hdrstart,body->host.s); if(body->port_str.s && body->port_str.len){ flags|=HAS_PORT_F; where[6]=REL_PTR(hdrstart,body->port_str.s); where[7]=REL_PTR(hdrstart,body->port_str.s+body->port_str.len+1); i=8; }else{ where[6]=REL_PTR(hdrstart,body->host.s+body->host.len+1); i=7; } if(body->params.s && body->params.len){ flags|=HAS_PARAMS_F; where[i++]=REL_PTR(hdrstart,body->params.s); where[i++]=(unsigned char)body->params.len; } if(body->branch && body->branch->value.s && body->branch->value.len){ flags|=HAS_BRANCH_F; where[i++]=REL_PTR(hdrstart,body->branch->value.s); where[i++]=(unsigned char)body->branch->value.len; } if(body->received && body->received->value.s && body->received->value.len){ flags|=HAS_RECEIVED_F; where[i++]=REL_PTR(hdrstart,body->received->value.s); where[i++]=(unsigned char)body->received->value.len; } if(body->rport && body->rport->value.s && body->rport->value.len){ flags|=HAS_RPORT_F; where[i++]=REL_PTR(hdrstart,body->rport->value.s); where[i++]=(unsigned char)body->rport->value.len; } if(body->i && body->i->value.s && body->i->value.len){ flags|=HAS_I_F; where[i++]=REL_PTR(hdrstart,body->i->value.s); where[i++]=(unsigned char)body->i->value.len; } if(body->alias && body->alias->value.s && body->alias->value.len){ flags|=HAS_ALIAS_F; where[i++]=REL_PTR(hdrstart,body->alias->value.s); where[i++]=(unsigned char)body->alias->value.len; } where[0]=flags; i+=encode_parameters(&where[i],body->param_lst,hdrstart,(void *)body,'v'); return i; } int print_encoded_via_body(FILE *fp,char *hdr,int hdrlen,unsigned char *payload,int paylen,char *prefix) { unsigned char numvias; int i,offset; fprintf(fp,"%s",prefix); for(i=0;i/*setsockopt,bind,accept,fork,pid_t*/ #include /*setsockopt,bind,accept,listen*/ #include /*TCP_NODELAY*/ #include /*strcmp,memset*/ #include /*errno*/ #include /*close(),read(),pipe,fork,pid_t*/ #include /*poll*/ #include /*signal*/ #include /*time*/ #include /*memcmp*/ #include /*waitpid*/ #include /*waitpid*/ #include "../../ip_addr.h" /*sockaddr_union, ip_addr*/ #include "../../hash_func.h" /*T_TABLE_POWER*/ #include "../../mem/mem.h" /*pkg_malloc*/ #include "../../mem/shm_mem.h" /*shm_malloc*/ #include "../../dprint.h" /*LM_**/ #include "../../locking.h" #include "seas.h" #include "ha.h" #include "cluster.h" #include "seas_action.h" #include "statistics.h" #include "event_dispatcher.h" #define PING_OVER_FACTOR 2 #define MAX_WRITE_TRIES 10 char *action_names[]={"NONE", "PROVISIONAL_REPLY", "FINAL_REPLY", "REPLY_FIN_DLG", "UAC_REQ", "AC_RES_FAIL", "STATELESS_MSG", "AC_CANCEL", "JAIN_PONG"}; struct unc_as unc_as_t[2*MAX_UNC_AS_NR]; /*this is for the Action Dispatcher Process */ struct as_entry *my_as; extern int sig_flag; static int process_event_reply(as_p as); static int handle_as_data(int fd); static inline int print_sock_info(char *buffer,int wheremax,int *idx,struct socket_info *s,enum sip_protos type); static inline int send_sockinfo(int fd); static inline int add_new_as(int event_idx,int action_idx,struct as_entry *as); static int dispatch_relay(); static int new_as_connect(int fd,char which); static inline int read_name(int sock,char *dst,int dstlen); static int handle_unc_as_data(int fd); static int open_server_sockets(struct ip_addr *address,unsigned short port,int *fd); /** Main loop for the Event Dispatcher process. * */ int dispatcher_main_loop(void) { struct pollfd poll_fds[3+MAX_AS_NR],*poll_tmp; int clean_index,i,j,k,fd,poll_events=0,socks[2],chld_status; int as_nr,unc_as_nr; pid_t chld; struct timeval last_ping,now; struct as_entry *as; sig_flag=0; is_dispatcher=1; as_nr=0; timerclear(&last_ping); timerclear(&now); signal(SIGCHLD,seas_sighandler); signal(SIGTERM,seas_sighandler); signal(SIGUSR1,seas_sighandler); signal(SIGINT, seas_sighandler); signal(SIGKILL,seas_sighandler); strcpy(whoami,"Seas Event Dispatcher process"); /*I set process_no to -1 because otherwise, the logging process confuses this process with another from SER * (see LM_*() and dprint() and my_pid())*/ process_no = -1; if(open_server_sockets(seas_listen_ip,seas_listen_port,socks)==-1){ LM_ERR("unable to open server sockets on dispatcher\n"); return -1; } for(i=0;i<2;i++){ poll_fds[i].fd=socks[i]; poll_fds[i].revents=0; poll_fds[i].events=POLLIN; } poll_fds[2].fd=read_pipe; poll_fds[2].revents=0; poll_fds[2].events=POLLIN;/*pollhup ?*/ poll_events=0; unc_as_nr=0; if(use_ha) spawn_pinger(); while(1){ if(sig_flag==SIGCHLD){ while ((chld=waitpid( -1, &chld_status, WNOHANG ))>0) { if (WIFEXITED(chld_status)){ LM_INFO("child process %d exited normally, status=%d\n", chld,WEXITSTATUS(chld_status)); }else if (WIFSIGNALED(chld_status)) { LM_INFO("child process %d exited by a signal %d\n", chld,WTERMSIG(chld_status)); }else if (WIFSTOPPED(chld_status)) LM_INFO("child process %d stopped by a signal %d\n", chld,WSTOPSIG(chld_status)); for (as=as_list;as;as=as->next) { if(as->type!=AS_TYPE) continue; if(as->u.as.action_pid==chld){ for(i=0;iu.as.event_fd));i++) ; if(i==as_nr){ LM_ERR("Either the pinger has died or BUG found..\n"); continue; } /*overwrite the obsolete 'i' position with the next position*/ for(j=3+i;j<(as_nr+unc_as_nr+3-1);i++){ poll_fds[j].fd=poll_fds[j+1].fd; poll_fds[j].events=poll_fds[j+1].events; poll_fds[j].revents=poll_fds[j+1].revents; } close(as->u.as.event_fd);/*close the socket fd*/ if (as->u.as.ev_buffer.s) { pkg_free(as->u.as.ev_buffer.s); as->u.as.ev_buffer.s=(char *)0; as->u.as.ev_buffer.len=0; } as->u.as.event_fd=as->u.as.action_fd=-1; as->connected=0; destroy_pingtable(&as->u.as.jain_pings); destroy_pingtable(&as->u.as.servlet_pings); as_nr--; LM_WARN("client [%.*s] leaving (Action Dispatcher Process died !)\n", as->name.len,as->name.s); break; }/*if(action_pid==chld)*/ }/*for(as=as_list;as;as=as->next)*/ }/*while(waitpid(-1)>0)*/ }else if (sig_flag) { LM_WARN("received signal != sigchld(%d)\n",sig_flag); } sig_flag=0; clean_index=0; LM_INFO("polling [2 ServSock] [1 pipe] [%d App Servers]" " [%d Uncomplete AS]\n",as_nr,unc_as_nr); poll_events = poll(poll_fds,3+unc_as_nr+as_nr,-1); if (poll_events == -1) { if(errno==EINTR){ /*handle the case a child has died. * It will be done in the next iteration in if(seas_sigchld_received)*/ continue; } if(errno==EBADF){ LM_ERR("invalid file descriptor passed to poll (%s)\n", strerror(errno)); return -1;/*??*/ } /* errors */ LM_ERR("poll'ing:%s\n",strerror(errno)); poll_events=0; continue; } else if (poll_events == 0) {/*timeout*/ continue; } else {/*there are events !*/ /*handle connections from server sockets*/ for(i=0;i<2;i++){ if(poll_fds[i].revents) poll_events--; if(poll_fds[i].revents & POLLIN){ poll_fds[i].revents &= (~POLLIN); if((fd=new_as_connect(socks[i],i==0?'e':'a'))>=0){ poll_tmp=&poll_fds[3+as_nr+unc_as_nr]; poll_tmp->fd=fd; poll_tmp->events=POLLIN|POLLHUP; unc_as_nr++; LM_DBG("Have new %s client\n",i==0?"event":"action"); }else{ LM_ERR("accepting connection from AS\n"); } } } /*handle data from pipe*/ if(poll_fds[2].revents & POLLIN){ poll_fds[2].revents &= (~POLLIN); poll_events--; if(dispatch_relay()<0){ LM_ERR("dispatch_relay returned -1" "should clean-up table\n"); } } /*now handle receive data from completed AS*/ clean_index=0; LM_DBG("Scanning data from %d AS\n",as_nr); for(i=0;(irevents) poll_events--; if(poll_tmp->revents & POLLIN){ LM_DBG("POLLIN found in AS #%i\n",i); poll_tmp->revents &= (~POLLIN); switch(handle_as_data(poll_tmp->fd)){ case -2:/*read returned 0 bytes, an AS client is leaving*/ clean_index=1; break; case -1:/*shouldnt happen*/ LM_ERR("reading from AS socket\n"); break; case 0:/* event_response received and processed*/ break; default: LM_WARN("unknown return type from handle_as_data\n"); } } if(clean_index || (poll_tmp->revents & POLLHUP)){ LM_DBG("POLHUP or read==0 found in %i AS \n",i); clean_index=0; poll_tmp->revents = 0; for(as=as_list;as;as=as->next){ if(as->type==CLUSTER_TYPE) continue; if(as->connected && (as->u.as.event_fd == poll_tmp->fd)){ close(poll_tmp->fd);/*close the socket fd*/ /*TODO we should send a signal to the Action Dispatcher !!!*/ as->connected=0; as_nr--; /*overwrite the obsolete 'i' position with the next position*/ for(k=i;k<(as_nr+unc_as_nr);k++){ j=3+k; poll_fds[j].fd=poll_fds[j+1].fd; poll_fds[j].events=poll_fds[j+1].events; poll_fds[j].revents=poll_fds[j+1].revents; } --i; LM_WARN("client %.*s leaving !!!\n",as->name.len,as->name.s); break; } } if (!as) { LM_ERR("the leaving client was not found in the as_list\n"); } } } /*now handle data sent from uncompleted AS*/ LM_DBG("Scanning data from %d uncomplete AS \n",unc_as_nr); clean_index=0; for(i=0;irevents) poll_events--; if(poll_tmp->revents & POLLIN){ LM_DBG("POLLIN found in %d uncomplete AS \n",i); poll_tmp->revents &= (~POLLIN); fd=handle_unc_as_data(poll_tmp->fd); if(fd>0){ /* there's a new AS, push the uncomplete poll_fds up and set the AS */ for(k=i;k>0;k--){ j=3+as_nr+k; poll_fds[j].fd=poll_fds[j-1].fd; poll_fds[j].events=poll_fds[j-1].events; poll_fds[j].revents=poll_fds[j-1].revents; } poll_fds[3+as_nr].fd=fd; poll_fds[3+as_nr].events=POLLIN|POLLHUP; poll_fds[3+as_nr].revents=0; as_nr++;/*not very sure if this is thread-safe*/ unc_as_nr--; }else if(fd<=0){/* pull the upper set of uncomplete AS down and take this one out*/ poll_tmp->revents=0; for(k=i;k<(unc_as_nr-1);k++){ j=3+as_nr+k; poll_fds[j].fd=poll_fds[j+1].fd; poll_fds[j].events=poll_fds[j+1].events; poll_fds[j].revents=poll_fds[j+1].revents; } unc_as_nr--; /** we decrement i so that pulling down the upper part of the unc_as array so that * it doesn't affect our for loop */ i--; } } if(poll_tmp->revents & POLLHUP){ LM_DBG("POLLHUP found in %d uncomplete AS \n",i); close(poll_tmp->fd); for(k=i;k<(unc_as_nr-1);k++){ j=3+as_nr+k; poll_fds[j].fd=poll_fds[j+1].fd; poll_fds[j].events=poll_fds[j+1].events; poll_fds[j].revents=poll_fds[j+1].revents; } unc_as_nr--; i--; poll_tmp->revents = 0; } }/*for*/ }/*else ...(poll_events>0)*/ }/*while(1)*/ } /** * opens the server socket, which attends (accepts) the clients, that is: * params: * address: * address to which to listen * port: * base port to which to listen. then port+1 will be the socket * for action's delivery. * fds: * in fd[0] the action socket will be put. * in fd[1] the event socket will be put. * * returns 0 on exit, <0 on fail * */ static int open_server_sockets(struct ip_addr *address,unsigned short port,int *fd) { /*using sockaddr_union enables ipv6..*/ union sockaddr_union su; int i,optval; fd[0]=fd[1]=-1; if(address->af!=AF_INET && address->af!=AF_INET6){ LM_ERR("Only ip and ipv6 allowed socket types\n"); return -1; } for(i=0;i<2;i++){ if(init_su(&su,address,port+i)<0){ LM_ERR("unable to init sockaddr_union\n"); return -1; } if((fd[i]=socket(AF2PF(su.s.sa_family), SOCK_STREAM, 0))==-1){ LM_ERR("trying to open server %s socket (%s)\n",i==0?"event":"action",strerror(errno)); goto error; } optval=1; if (setsockopt(fd[i], SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval))==-1) { LM_ERR("setsockopt (%s)\n",strerror(errno)); goto error; } if ((bind(fd[i], &su.s,sizeof(union sockaddr_union)))==-1){ LM_ERR( "bind (%s)\n",strerror(errno)); goto error; } if (listen(fd[i], 10)==-1){ LM_ERR( "listen (%s)\n",strerror(errno)); goto error; } } return 0; error: for(i=0;i<2;i++) if(fd[i]!=-1){ close(fd[i]); fd[i]=-1; } return -1; } union helper{ as_msg_p ptr; char bytes[sizeof(as_msg_p)]; }; /** * Sends event * * returns * 0 OK * -1 couldn't read the event from the pipe * -2 couldn't send the event * TODO this should be FAR more generic... for example, there might be events * which are not related to any transaction (finish event, or error event...) * we should separate event-specific handling in different functions... */ static int dispatch_relay(void) { int i,j,retval,tries; union helper thepointer; i=j=0; retval=0; read_again: i=read(read_pipe,thepointer.bytes+j,sizeof(as_msg_p)-j); if(i<0){ if(errno==EINTR){ goto read_again; }else{ LM_ERR("Dispatcher Process received unknown error" " reading from pipe (%s)\n",strerror(errno)); retval=-1; goto error; } }else if(i==0){ LM_ERR("Dispatcher Process " "received 0 while reading from pipe\n"); goto error; }else{ j+=i; if(jtransaction) event_stat(thepointer.ptr->transaction); if(thepointer.ptr->as == NULL || !thepointer.ptr->as->connected || thepointer.ptr->as->type==CLUSTER_TYPE){ LM_WARN("tryied to send an event to an App Server" " that is scheduled to die!!\n"); retval=-2; goto error; } j=0; tries=0; write_again: i=write(thepointer.ptr->as->u.as.event_fd,thepointer.ptr->msg+j,thepointer.ptr->len-j); if(i==-1){ switch(errno){ case EINTR: if(!thepointer.ptr->as->connected){ LM_WARN("tryied to send an event to an App Server" " that is scheduled to die!!\n"); retval=-2; goto error; } goto write_again; case EPIPE: LM_ERR("AS [%.*s] closed " "the socket !\n",thepointer.ptr->as->u.as.name.len,thepointer.ptr->as->u.as.name.s); retval=-2; goto error; default: LM_ERR("unknown error while trying to write to AS socket(%s)\n", strerror(errno)); retval=-2; goto error; } }else if(i>0){ j+=i; if(jlen) goto write_again; }else if(i==0){ if (tries++ > MAX_WRITE_TRIES) { LM_ERR("MAX WRITE TRIES !!!\n"); goto error; }else goto write_again; } LM_DBG("Event relaied to %.*s AS\n",thepointer.ptr->as->u.as.name.len, thepointer.ptr->as->u.as.name.s); LM_DBG("Event type %s \n",action_names[thepointer.ptr->type]); retval=0; error: if(thepointer.ptr){ if(thepointer.ptr->msg) shm_free(thepointer.ptr->msg); shm_free(thepointer.ptr); } return retval; } /** * receives 2 indexes in unc_as_t which correspond one to * the events socket and the other to the actions socket * * returns * 0 on success * -1 on error */ static inline int add_new_as(int event_idx,int action_idx,struct as_entry *as) { struct unc_as *ev,*ac; int j; as_p the_as=0; struct as_entry *tmp; ev=&unc_as_t[event_idx]; ac=&unc_as_t[action_idx]; the_as=&(as->u.as); the_as->action_fd=ac->fd; the_as->event_fd=ev->fd; the_as->name.len = strlen(ev->name); if(use_ha){ if(jain_ping_timeout){ if (0>init_pingtable(&the_as->jain_pings,jain_ping_timeout,(jain_ping_timeout/jain_ping_period+1)*PING_OVER_FACTOR)){ LM_ERR("Unable to init jain pinging table...\n"); goto error; } } if(servlet_ping_timeout){ if (0>init_pingtable(&the_as->servlet_pings,servlet_ping_timeout,(servlet_ping_timeout/servlet_ping_period+1)*PING_OVER_FACTOR)){ LM_ERR("Unable to init servlet pinging table...\n"); goto error; } } } /*TODO attention, this is pkg_malloc because only the Event_Dispatcher process * has to use it !!*/ if(!(the_as->ev_buffer.s = pkg_malloc(AS_BUF_SIZE))){ LM_ERR("unable to alloc pkg mem for the event buffer\n"); goto error; } the_as->ev_buffer.len=0; as->connected=1; the_as->action_pid=0; for(tmp=as_list;tmp;tmp=tmp->next){ if(tmp->type==AS_TYPE) continue; for (j=0;ju.cs.num;j++) { if (tmp->u.cs.as_names[j].len == the_as->name.len && !memcmp(tmp->u.cs.as_names[j].s,the_as->name.s,the_as->name.len)) { if(tmp->u.cs.num==tmp->u.cs.registered){ LM_ERR("AS %.*s belongs to cluster %.*s which is already completed\n", the_as->name.len,the_as->name.s,tmp->name.len,tmp->name.s); break; } tmp->u.cs.registered++; break; } } } if(0>spawn_action_dispatcher(as)){ LM_ERR("Unable to spawn Action Dispatcher for as %s\n",ev->name); goto error; } if(send_sockinfo(the_as->event_fd)==-1){ LM_ERR("Unable to send socket info to as %s\n",ev->name); goto error; } return 0; error: if(the_as->ev_buffer.s){ pkg_free(the_as->ev_buffer.s); the_as->ev_buffer.s=(char*)0; } if(the_as->action_pid) kill(the_as->action_pid,SIGTERM); if(jain_ping_timeout) destroy_pingtable(&the_as->jain_pings); if(servlet_ping_timeout) destroy_pingtable(&the_as->servlet_pings); return -1; } /**prints available sockets in SER to the App Server. * format is: * 1: transport identifier (u for UDP, t for TCP, s for TLS) * 1: length of socket name (sip.voztele.com or whatever) * N: name * 1: length of IP address (192.168.1.2) * N: ip address in ascii * 2: port nubmer in NBO * * returns * -1 on error * 0 on success */ static inline int send_sockinfo(int fd) { struct socket_info *s; unsigned char i; char buffer[300]; int k=0,j; buffer[k++]=16;/*This used to be T_TABLE_POWER in opensips 1.0.1, now its hardcoded in config.h*/ for( j=PROTO_FIRST,i=0 ; jnext,i++); if(i==0){ LM_ERR("no udp|tcp|tls sockets ?!!\n"); return -1; } buffer[k++]=i; for( j=PROTO_FIRST ; jnext) if(print_sock_info(buffer,300,&k,s,PROTO_UDP)==-1) return -1; write_again: j=write(fd,buffer,k); if(j==-1){ if(errno==EINTR) goto write_again; else return -1; } return 0; } /* prints sock info into the byte array where * returns 0 on success, -1 on err * the message sent is as follows: * 1: protocol type (0=NONE,1=UDP, 2=TCP, 3=TLS) * 1: name length * N: name * 1: address string length * N: address * 2: NBO unsigned shor int port number * * TODO buffer overflow risk */ static inline int print_sock_info(char *buffer,int wheremax,int *idx,struct socket_info *s,enum sip_protos type) { int k; unsigned char i; unsigned short int j; if((wheremax-*idx)<49)/*31*name+17*ipv6+2*port+1*type*/ return -1; k=*idx; buffer[k++]=(char)type; if((i=(unsigned char)s->name.len)>30){ LM_ERR("name too long\n"); return -1; } buffer[k++]=i; memcpy(&buffer[k],s->name.s,i); k+=i; i=(unsigned char)s->address_str.len; buffer[k++]=i; memcpy(&buffer[k],s->address_str.s,i); k+=i; j=htons(s->port_no); memcpy(&buffer[k],&j,2); k+=2; *idx=k; return 0; } /** * Handles data from an AppServer. First searches in the AS table which was the AS * that sent the data (we dont already know it because this comes from a poll_fd * struct). When the one is found, it calls process_event_reply, which in turn * looks if there's a complete event in the buffer, and if there is, processes it. * * returns * -1 on error * -2 on read()==0 (the socket has been closed by the other end) * 0 on success */ static int handle_as_data(int fd) { int j,k; struct as_entry *as; for(as=as_list;as;as=as->next) if(as->type == AS_TYPE && as->connected && (as->u.as.event_fd==fd)) break; if(!as){ LM_ERR("AS not found\n"); return -1; } k=AS_BUF_SIZE-(as->u.as.ev_buffer.len); again: if((j=read(fd,as->u.as.ev_buffer.s+as->u.as.ev_buffer.len,k))<0){ LM_ERR("reading data for as %.*s\n",as->name.len,as->name.s); if(errno==EINTR) goto again; else return -1; }else if(j==0){ LM_ERR("AS client leaving (%.*s)\n",as->name.len,as->name.s); return -2; } as->u.as.ev_buffer.len+=j; LM_DBG("read %d bytes from AS (total = %d)\n",j,as->u.as.ev_buffer.len); if(as->u.as.ev_buffer.len>5) process_event_reply(&as->u.as); return 0; } /** * This function processess the Application Server buffer. We do buffered * processing because it increases performance quite a bit. Any message * sent from the AS comes with the first 2 bytes as an NBO unsigned short int * which says the length of the following message (header and payload). * This way, we avoid multiple small reads() to the socket, which (as we know), consumes * far more processor because of the kernel read(2) system call. The drawback * is the added complexity of mantaining a buffer, the bytes read, and looking * if there is a complete message already prepared. * * Actions are supposed to be small, that's why BUF_SIZE is 2000 bytes length. * Most of the actions will be that size or less. That is why the 4 bytes telling the * length of the Action payload are included in its size. This way you can use a fixed size * buffer to receive the Actions and not need to be pkb_malloc'ing for each new event. * If there is a particular bigger packet, for example one carrying a picture (a JPG can * easily surpass the 2000 byte limit) then a pkg_malloc will be required. This is left TODO * * returns * -1 on error (packet too big) * 0 on success */ static int process_event_reply(as_p as) { unsigned int ev_len; ev_len=(as->ev_buffer.s[0]<<24)|(as->ev_buffer.s[1]<<16)|(as->ev_buffer.s[2]<<8)|(as->ev_buffer.s[3]);/*yeah, it comes in network byte order*/ /*if ev_len > BUF_SIZE then a flag should be put on the AS so that the whole length * of the action is skipped, until a mechanism for handling big packets is implemented*/ if(ev_len>AS_BUF_SIZE){ LM_WARN("Packet too big (%d)!!! should be skipped" " and an error returned!\n",ev_len); return -1; } if((as->ev_buffer.lenev_buffer.len<4) return 0; switch(as->ev_buffer.s[4]){ case BIND_AC: LM_DBG("Processing a BIND action from AS (length=%d): %.*s\n", ev_len,as->name.len,as->name.s); process_bind_action(as,&as->ev_buffer.s[5],ev_len-5); break; case UNBIND_AC: LM_DBG("Processing a UNBIND action from AS (length=%d): %.*s\n", ev_len,as->name.len,as->name.s); process_unbind_action(as,&as->ev_buffer.s[5],ev_len-5); break; default: return 0; } memmove(as->ev_buffer.s,&(as->ev_buffer.s[ev_len]),(as->ev_buffer.len)-ev_len); (as->ev_buffer.len)-=ev_len; return 0; } /** * processes a BIND event type from the AS. * Bind events follow this form: * 4:flags * 1:processor_id * 1:Address Family * 1:address length in bytes (16 for ipv6, 4 for ipv4) in NETWORK BYTE ORDER (fortunately, ip_addr struct stores it in NBO) * [16|4]:the IP address * 1:protocol used (UDP,TCP or TLS); * 2:NBO port * */ int process_bind_action(as_p as,char *payload,int len) { struct socket_info *si,*xxx_listen; struct ip_addr my_addr; int i,k,proto; unsigned int flags; unsigned short port; char processor_id,buffer[300],*proto_s; k=0; *buffer=0; proto_s="NONE"; net2hostL(flags,payload,k); processor_id=payload[k++]; for(i=0;ibound_processor[i]==0) break; } if(i==MAX_BINDS){ LM_ERR("No more bindings allowed. Ignoring bind request for processor %d\n",processor_id); return -1; } memset(&my_addr,0,sizeof(struct ip_addr)); my_addr.af=payload[k++]; my_addr.len=payload[k++]; memcpy(my_addr.u.addr,payload+k,my_addr.len); k+=my_addr.len; proto=payload[k++]; memcpy(&port,payload+k,2); k+=2; port=ntohs(port); print_ip_buf(&my_addr,buffer,300); xxx_listen = protos[proto].listeners; for(si=xxx_listen;si;si=si->next){ if(my_addr.af==si->address.af && my_addr.len==si->address.len && !memcmp(si->address.u.addr,my_addr.u.addr,my_addr.len) && port == si->port_no){ as->binds[i]=si; as->bound_processor[i]=processor_id; as->num_binds++; LM_DBG("AS processor with id: %d bound to %s %s %d\n",processor_id,proto_s,buffer,port); return 0; } } LM_ERR("Cannot bind to %s %s %d !!!\n",proto_s,buffer,port); return -1; } /** * processes a UNBIND event type from the AS. * Bind events follow this form: * 1:processor_id * */ int process_unbind_action(as_p as,char *payload,int len) { int i,k; unsigned int flags; char processor_id; k=0; net2hostL(flags,payload,k); processor_id=payload[k++]; for(i=0;inum_binds;i++){ if(as->bound_processor[i] == processor_id) break; } if(i==MAX_BINDS){ LM_ERR("tried to unbind a processor which is not registered (id=%d)!\n",processor_id); return 0; } as->bound_processor[i]=0; as->num_binds--; LM_DBG("AS processor un-bound with id: %d\n",processor_id); return 0; } /** * params: * the filedes where the data was received. * returns: * 0 if this fd should be taken out of the poll_fd array bcause it already has AS name. * fd if an AS was completed (returns the fd of the events socket) * -1 if there was an error * -2 if client disconnected and should be closed and taken outside the poll_fd array */ static int handle_unc_as_data(int fd) { int i,j,k,len; char *name1; struct as_entry *as; /*first, we see if the data to read is from any of the uncompleted as's*/ for(i=0;i<2*MAX_UNC_AS_NR ;i++) if(unc_as_t[i].valid && unc_as_t[i].fd==fd) break; if(i==2*MAX_UNC_AS_NR){ LM_ERR("has received an fd which is not in uncompleted AS array\n"); return -1; } if(unc_as_t[i].flags & HAS_NAME){/*shouldn't happen, if it has a name, it shouldnt be in fdset[]*/ LM_WARN("this shouldn't happen\n"); return 0;/*already have a name, please take me out the uncompleted AS array*/ } LM_DBG("Reading client name\n"); if(-1==(len=read_name(fd,unc_as_t[i].name,MAX_AS_NAME))){ /*this guy should be disconnected, it sent an AS_NAME too long*/ LM_ERR("Bad name passed from fd\n"); unc_as_t[i].valid=0; unc_as_t[i].flags=0; return -2; }else if(len==-2){ LM_WARN("client disconnected\n"); return -2; } name1=unc_as_t[i].name; /* Check the name isn't already taken */ for(as=as_list;as;as=as->next){ if(as->name.len==len && !memcmp(name1,as->name.s,len)){ if(as->connected){ LM_WARN("AppServer trying to connect with a name already taken (%.*s)\n",len,name1); unc_as_t[i].valid=0; unc_as_t[i].flags=0; return -2; } break; } } if (!as) { LM_ERR("a client tried to connect which is not declared in config. script(%.*s)\n",len,name1); unc_as_t[i].valid=0; unc_as_t[i].flags=0; return -2; } unc_as_t[i].flags |= HAS_NAME; /* the loop's upper bound, * if 'i' is in the lower part, then look for an unc_as in the upper part*/ k=(i>=MAX_UNC_AS_NR?MAX_UNC_AS_NR:2*MAX_UNC_AS_NR); /* the loop's lower bound */ for(j=(i>=MAX_UNC_AS_NR?0:MAX_UNC_AS_NR);jdstlen || namelen==0){ LM_ERR("name too long to fit in dst (%d > %d)\n",namelen,dstlen); return -1; } try_again2: if((n=read(sock,dst,namelen))<0){ if(errno==EINTR) goto try_again2; else{ LM_ERR("trying to read %d chars into %p from fd=%d (%s)\n",namelen,dst,sock,strerror(errno)); return -1; } }else if(n==0){ LM_WARN("uncomplete AS has disconnected before giving its name\n"); return -2; } dst[namelen]=0; return namelen; } /* handle new App Server connect. * params: * fd: * fd on which to accept. * which: * if the fd is the event one, which='e', if is action, which='a' * * TODO: not very reliable, because if someone connects() to one of the serversockets * but not to the other one, then synchronization would be lost, and any subsequent connect * attempts would fail (remember, we receive a connect in event[] and wait for a connect in action) * the point is, the connects must allways come in pairs, if one comes alone, we lost sync. * we should put kind of timeout in connects or something... */ static int new_as_connect(int fd,char which) { union sockaddr_union su; int sock,i,flags; socklen_t su_len; su_len = sizeof(union sockaddr_union); sock=-1; again: sock=accept(fd, &su.s, &su_len); if(sock==-1){ if(errno==EINTR){ goto again; }else{ LM_ERR("while accepting connection: %s\n", strerror(errno)); return -1; } } switch(which){ case 'e': for(i=0;iname.len,the_as->name.s); return -1; } if(pid==0){/*child*/ my_as = the_as; is_dispatcher=0; dispatch_actions(); exit(0); }else{ the_as->u.as.action_pid=pid; } return 0; } opensips-2.2.2/modules/seas/event_dispatcher.h000066400000000000000000000025171300170765700214730ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../ip_addr.h" struct unc_as{ char valid; int fd; char name[MAX_AS_NAME]; char flags; union sockaddr_union su; }; /*uncomplete as table, from 0 to MAX_UNC_AS_NR are event, from then on are action*/ /*should only be modified by the dispatcher process, or we should add a lock*/ extern struct unc_as unc_as_t[]; int process_unbind_action(as_p as,char *payload,int len); int process_bind_action(as_p as,char *payload,int len); int dispatcher_main_loop(); int spawn_action_dispatcher(struct as_entry *as); opensips-2.2.2/modules/seas/ha.c000066400000000000000000000244431300170765700165310ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include /*atoi*/ #include /*gettimeofday*/ #include /*poll*/ #include "ha.h" #include "seas.h" #include "../../mem/mem.h" /*pkg_malloc*/ #include "../../mem/shm_mem.h" /*shm_malloc*/ /** if any of these global ping vars is set to 0, then * this kind of ping is DISABLED */ char *jain_ping_config=0; int jain_ping_period=0; int jain_pings_lost=0; int jain_ping_timeout=0; char *servlet_ping_config=0; int servlet_ping_period=0; int servlet_pings_lost=0; int servlet_ping_timeout=0; int use_ha=0; pid_t pinger_pid; static inline int parse_ping(char * string,int *ping_period,int *pings_lost,int *ping_timeout); static inline int send_ping(struct as_entry *the_as,struct timeval *now); /** * returns: * 0 if no High Availability * 1 if High Availability * -1 if config error */ int prepare_ha(void) { use_ha=0; if(!(jain_ping_config || servlet_ping_config)){ jain_pings_lost=servlet_pings_lost=0; return 0; } if(parse_ping(jain_ping_config,&jain_ping_period,&jain_pings_lost,&jain_ping_timeout)<0) goto error; if(parse_ping(servlet_ping_config,&servlet_ping_period,&servlet_pings_lost,&servlet_ping_timeout)<0) goto error; LM_DBG("jain: pinging period :%d max pings lost:%d ping timeout:%d\n", jain_ping_period,jain_pings_lost,jain_ping_timeout); LM_DBG("servlet: pinging period:%d max pings lost:%d ping timeout:%d\n", servlet_ping_period,servlet_pings_lost,servlet_ping_timeout); use_ha=1; return 1; error: return -1; } int print_pingtable(struct ha *ta,int idx,int lock) { int i; if(lock) lock_get(ta->mutex); for(i=0;isize;i++){ if((ta->begin+ta->count)>ta->size){ if ((ibegin && i<((ta->begin+ta->count)%ta->size)) || (i>=ta->begin && i<(ta->begin+ta->count))) fprintf(stderr,"*"); else fprintf(stderr,"="); }else{ if (i>=ta->begin && i<(ta->begin+ta->count)) fprintf(stderr,"*"); else fprintf(stderr,"="); } } if(lock) lock_release(ta->mutex); fprintf(stderr,"\n"); for(i=0;isize;i++) if(i==idx) fprintf(stderr,"-"); else fprintf(stderr,"%d",i); fprintf(stderr,"\n"); return 0; } /** * Parses the PING configuration string. Its format is * "ping_period:pings_lost:ping_timeout" * ping_period : time between pings * pings_lost: number of lost pings before failure * ping_timeout: time to consider a ping failed * * returns * 0 if config is not set * -1 if config is malformed (unable to parse); * 1 if config is successfully set */ static inline int parse_ping(char * string,int *ping_period,int *pings_lost,int *ping_timeout) { char *ping_period_s,*pings_lost_s,*ping_timeout_s; ping_period_s=pings_lost_s=ping_timeout_s=(char *)0; if(string==0 || *string==0){ *ping_period=0; *pings_lost=0; *ping_timeout=0; return 0; } if (((*string)<'0')||((*string)>'9')) { LM_ERR("malformed ping config string. Unparseable :[%s]\n",string); return -1; } ping_period_s=string; while(*string){ if(*string == ':'){ *string=0; if (!pings_lost_s && (*(string+1))) { pings_lost_s=string+1; }else if (!ping_timeout_s && (*(string+1))) { ping_timeout_s=string+1; }else{ LM_ERR("malformed ping config string. Unparseable :[%s]\n",string); return -1; } } string++; } if (!(ping_period_s && pings_lost_s && ping_timeout_s)) { LM_ERR("malformed ping config string. Unparseable :[%s]\n",string); return -1; } *ping_period =atoi(ping_period_s); *pings_lost=atoi(pings_lost_s); *ping_timeout=atoi(ping_timeout_s); if (*ping_period<=0 || *pings_lost<=0 || *ping_timeout<=0) { return -1; } return 1; } /** * we spawn a pinger process. * * some day we could spawn a pinger-thread instead of a process.. * * returns: * -1 on error; */ int spawn_pinger(void) { int n,next_jain,next_servlet,timeout; struct timeval now,last_jain,last_servlet; struct as_entry *as; if ((pinger_pid=fork())<0) { LM_ERR("forking failed!\n"); goto error; }else if(pinger_pid>0){ return 0; } strcpy(whoami,"Pinger Process\n"); is_dispatcher=0; my_as=0; /* child */ if(jain_ping_period && servlet_ping_period){ next_jain=next_servlet=0; }else if(jain_ping_period){ next_servlet=INT_MAX; next_jain=0; }else if(servlet_ping_period){ next_jain=INT_MAX; next_servlet=0; }else{ next_jain=next_servlet=INT_MAX; } gettimeofday(&last_jain,NULL); memcpy(&last_servlet,&last_jain,sizeof(struct timeval)); while(1){ gettimeofday(&now,NULL); if(next_jain!=INT_MAX){ next_jain=jain_ping_period-((now.tv_sec-last_jain.tv_sec)*1000 + (now.tv_usec-last_jain.tv_usec)/1000); } if(next_servlet!=INT_MAX){ next_servlet=servlet_ping_period-((now.tv_sec-last_servlet.tv_sec)*1000 + (now.tv_usec-last_servlet.tv_usec)/1000); } timeout=next_jain=jain_ping_period) { gettimeofday(&last_jain,NULL); for(as=as_list;as;as=as->next){ if(as->type == AS_TYPE && as->connected){ send_ping(as,&now); } } } if (servlet_ping_period && ((now.tv_sec-last_servlet.tv_sec)*1000 + (now.tv_usec-last_servlet.tv_usec)/1000)>=servlet_ping_period) { gettimeofday(&last_servlet,NULL); for(as=as_list;as;as=as->next){ if(as->type==AS_TYPE && as->connected){ send_ping(as,&now); } } } }else{/*impossible..*/ LM_ERR("bug:poll returned %d\n",n); goto error; } } return 0; error: return -1; } /** * sends a ping to the app-server, and uses now as sent time * * returns * 0 on success * -1 on error */ static inline int send_ping(struct as_entry *the_as,struct timeval *now) { char *the_ping; as_msg_p aping; int pinglen,retval; unsigned int seqno; struct ping *pingu; aping=(as_msg_p)0; the_ping=(char *)0; retval=0; if (!(aping=shm_malloc(sizeof(as_msg_t)))) { LM_ERR("out of shm_mem for ping event\n"); retval=-1; goto error; } if (!(the_ping=create_ping_event(&pinglen,0,&seqno))) { LM_ERR("Unable to create ping event\n"); retval=-1; goto error; } aping->as=the_as; aping->msg=the_ping; aping->len=pinglen; lock_get(the_as->u.as.jain_pings.mutex); { if(the_as->u.as.jain_pings.count==the_as->u.as.jain_pings.size){ LM_ERR("Cant send ping because the pingtable is full (%d pings)\n",\ the_as->u.as.jain_pings.count); retval=0; lock_release(the_as->u.as.jain_pings.mutex); goto error; }else{ pingu=the_as->u.as.jain_pings.pings+the_as->u.as.jain_pings.end; the_as->u.as.jain_pings.end=(the_as->u.as.jain_pings.end+1)%the_as->u.as.jain_pings.size; the_as->u.as.jain_pings.count++; } memcpy(&pingu->sent,now,sizeof(struct timeval)); pingu->id=seqno; } lock_release(the_as->u.as.jain_pings.mutex); again: if(0>write(write_pipe,&aping,sizeof(as_msg_p))){ if(errno==EINTR){ goto again; }else{ LM_ERR("error sending ping\n"); goto error; } } return 0; error: if(aping) shm_free(aping); if(the_ping) shm_free(the_ping); return retval; } /** * Initializes the high availability (ha) structure * * returns * 0 on success * -1 on error */ int init_pingtable(struct ha *table,int timeout,int maxpings) { if(maxpings<=0) maxpings=1; table->begin=0; table->end=0; table->timed_out_pings=0; table->size=maxpings; table->timeout=timeout; if (!(table->mutex=lock_alloc())){ LM_ERR("Unable to allocate a lock for the ping table\n"); goto error; }else lock_init(table->mutex); LM_ERR("alloc'ing %d bytes for %d pings\n",(int)(maxpings*sizeof(struct ping)),maxpings); if (0==(table->pings=shm_malloc(maxpings*sizeof(struct ping)))){ LM_ERR("Unable to shm_malloc %d bytes for %d pings\n",(int)(maxpings*sizeof(struct ping)),maxpings); goto error; }else{ memset(table->pings,0,(maxpings*sizeof(struct ping))); } return 0; error: destroy_pingtable(table); return -1; } void destroy_pingtable(struct ha *table) { if(table->mutex){ lock_dealloc(table->mutex); table->mutex=0; } if(table->pings){ shm_free(table->pings); table->pings=0; } } /** * event_length(4) UNSIGNED INT includes the length 4 bytes itself * type(1), * processor_id(1), 0 means nobody, 0xFF means everybody, 0 struct ping{ unsigned int id; struct timeval sent; struct ping *next; }; struct ha{ int timed_out_pings; int timeout; gen_lock_t *mutex; struct ping *pings; int begin; int end; int count; int size; }; extern char *jain_ping_config; extern int jain_ping_period; extern int jain_ping_timeout; extern struct ping *jain_pings; extern pid_t pinger_pid; extern char *servlet_ping_config; extern int servlet_ping_period; extern int servlet_ping_timeout; extern struct ping *servlet_pings; extern int use_ha; char * create_ping_event(int *evt_len,int flags,unsigned int *seqno); int prepare_ha(); int spawn_pinger(); int print_pingtable(struct ha *ta,int idx,int lock); int init_pingtable(struct ha *table,int timeout,int maxpings); void destroy_pingtable(struct ha *table); #endif opensips-2.2.2/modules/seas/seas.c000066400000000000000000000500371300170765700170720ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include /*memset*/ #include /*errno*/ #include /*close(),pipe,fork,pid_t*/ #include /*wait*/ #include /*SIGINT,etc*/ #include "../../sr_module.h" #include "../../ip_addr.h" /*ip_addr,hostent2ip_addr*/ #include "../../tags.h" /*init_tags*/ #include "../../socket_info.h" /*get_first_socket()*/ #include "../../resolve.h" /*resolvehost*/ #include "../../mem/mem.h" /*pkg_malloc*/ #include "../../mem/shm_mem.h" /*shm_malloc*/ #include "../../dprint.h" /*LM_**/ #include "../../error.h" /*ser_error*/ #include "../tm/tm_load.h" /*load_tm_api*/ #include "../tm/h_table.h" /*cell*/ #include "../tm/t_lookup.h" /*T_UNDEFINED*/ #include "encode_msg.h" /*encode_msg*/ #include "seas.h" #include "seas_action.h" #include "event_dispatcher.h" #include "statistics.h"/*pstart_stats_server*/ #include "ha.h" #include "cluster.h" /* Exported Functions */ static int w_as_relay_t(struct sip_msg *msg, char *as_name, char *foo); static int w_as_relay_sl(struct sip_msg *msg, char *as_name, char *foo); /* Local functions */ static int seas_init(void); static int seas_child_init(int rank); static int seas_exit(); static int fixup_as_relay(void** param, int param_no); /*utility functions*/ static void seas_init_tags(); static inline int is_e2e_ack(struct cell *t,struct sip_msg *msg); char seas_tags[TOTAG_VALUE_LEN+1]; char *seas_tag_suffix; char whoami[MAX_WHOAMI_LEN]; int is_dispatcher=0; extern int sig_flag; static char *seas_listen_socket=0; static char *seas_stats_socket=0; struct ip_addr *seas_listen_ip=0; unsigned short seas_listen_port=0; struct as_entry *as_table=0; struct as_entry *as_list=0; int write_pipe=0; int read_pipe=0; struct seas_functions seas_f; static cmd_export_t cmds[]= { {"as_relay_t", (cmd_function)w_as_relay_t, 1, fixup_as_relay, 0, REQUEST_ROUTE}, {"as_relay_sl", (cmd_function)w_as_relay_sl, 1, fixup_as_relay, 0, REQUEST_ROUTE}, {0,0,0,0,0,0} }; static param_export_t params[]= { {"listen_sockets",STR_PARAM, &seas_listen_socket}, {"stats_socket", STR_PARAM, &seas_stats_socket}, {"jain_ping", STR_PARAM, &jain_ping_config}, {"servlet_ping", STR_PARAM, &servlet_ping_config}, {"clusters", STR_PARAM, &cluster_cfg}, {0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "seas", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, &deps, /* OpenSIPS module dependencies */ cmds, 0, params, 0, 0, 0, 0, /* extra processes */ seas_init, /* module initialization function */ (response_function) 0, (destroy_function) seas_exit, /* module exit function */ (child_init_function) seas_child_init /* per-child init function */ }; static int fixup_as_relay(void** param, int param_no) { int len; char *parameter; struct as_entry **entry,*tmp; parameter=(char *)(*param); if (param_no!=1) return 0; len=strlen(parameter); for (entry=&as_list;*entry;entry=&((*entry)->next)) { if (len== (*entry)->name.len && !memcmp((*entry)->name.s,parameter,len)) { pkg_free(*param); *param=*entry; return 1; } } if (!(*entry)) { if (!(*entry=(struct as_entry *)shm_malloc(sizeof(struct as_entry)))) { LM_ERR("no more shm_mem\n"); goto error; } memset(*entry,0,sizeof(struct as_entry)); if(!((*entry)->name.s=shm_malloc(len))){ LM_ERR("no more share mem\n"); goto error; } (*entry)->name.len=len; memcpy((*entry)->name.s,parameter,len); (*entry)->u.as.name=(*entry)->name; (*entry)->u.as.event_fd=(*entry)->u.as.action_fd=-1; (*entry)->type=AS_TYPE; pkg_free(*param); *param=*entry; } for (tmp=as_list;tmp;tmp=tmp->next) LM_DBG("%.*s\n",tmp->name.len,tmp->name.s); return 1; error: return -1; } /** * Sets up signal handlers */ void seas_sighandler(int signo) { struct as_entry *as; if(is_dispatcher) sig_flag=signo; switch(signo){ case SIGPIPE: if(is_dispatcher) return; LM_INFO("%s exiting\n",whoami); if(my_as->u.as.ac_buffer.s){ pkg_free(my_as->u.as.ac_buffer.s); my_as->u.as.ac_buffer.s=0; } if(my_as->u.as.action_fd!=-1){ close(my_as->u.as.action_fd); my_as->u.as.action_fd=-1; } exit(0); break; case SIGCHLD: LM_INFO("Child stopped or terminated\n"); break; case SIGUSR1: case SIGUSR2: LM_DBG("Memory status (pkg):\n"); #ifdef PKG_MALLOC pkg_status(); #endif break; case SIGINT: case SIGTERM: LM_INFO("INFO: signal %d received\n",signo); #ifdef PKG_MALLOC pkg_status(); #endif if(is_dispatcher){ for (as=as_list;as;as=as->next) { if(as->type==AS_TYPE && as->connected) kill(as->u.as.action_pid,signo); } while(wait(0) > 0); exit(0); }else{ LM_INFO("%s exiting\n",whoami); if(my_as && my_as->u.as.ac_buffer.s) pkg_free(my_as->u.as.ac_buffer.s); if(my_as && my_as->u.as.action_fd!=-1) close(my_as->u.as.action_fd); exit(0); } break; } } /** * wrapper for the AS transaction-stateful relay script function. * */ static int w_as_relay_t(struct sip_msg *msg, char *entry, char *foo) { as_msg_p my_as_ev; int new_tran,ret=0,len; char *buffer,processor_id; struct cell *mycel; struct as_entry *as; static str msg500={"Server Internal Error!",sizeof("Server Internal Error!")-1}; buffer=(char*)0; my_as_ev=(as_msg_p)0; /** * returns <0 on error * 1 if (new transaction was created) or if (ACK for locally replied 200 with totag) or if (ACK for code>=300) * 0 if it was a retransmission */ new_tran = seas_f.tmb.t_newtran(msg); if(new_tran<0) { ret = new_tran; goto done; } /*retransmission: script processing should be stopped*/ if (new_tran==0 && !(msg->REQ_METHOD==METHOD_ACK)){ ret = 0; goto done; } as=(struct as_entry *)entry; if(!as->connected){ LM_ERR("app server %.*s not connected\n",as->name.len,as->name.s); goto error; } if(as->type==AS_TYPE){ if((processor_id=get_processor_id(&msg->rcv,&(as->u.as)))<0){ LM_ERR("no processor found for packet with dst port:%d\n",msg->rcv.dst_port); goto error; } }else if(as->type==CLUSTER_TYPE){ LM_ERR("clustering not fully implemented\n"); return 0; }else{ LM_ERR("unknown type of as (neither cluster nor as)\n"); return -1; } LM_DBG("as found ! (%.*s) processor id = %d\n",as->name.len,as->name.s,processor_id); if(new_tran==1 && msg->REQ_METHOD==METHOD_ACK){ /* core should forward statelessly (says t_newtran)*/ LM_DBG("forwarding statelessly !!!\n"); if(!(buffer=create_as_event_sl(msg,processor_id,&len,0))){ LM_ERR("create_as_event_sl() unable to create event code\n"); goto error; } }else if(!(buffer=create_as_event_t(seas_f.tmb.t_gett(),msg,processor_id,&len,0))){ LM_ERR("unable to create event code\n"); goto error; } if(!(my_as_ev=shm_malloc(sizeof(as_msg_t)))){ LM_ERR("Out of shared mem!\n"); goto error; } my_as_ev->msg = buffer; my_as_ev->as = as; my_as_ev->type = T_REQ_IN; my_as_ev->len = len; my_as_ev->transaction=seas_f.tmb.t_gett(); /*does not refcount*/ if(use_stats && new_tran>0) as_relay_stat(seas_f.tmb.t_gett()); again: ret=write(write_pipe,&my_as_ev,sizeof(as_msg_p)); if(ret==-1){ if(errno==EINTR) goto again; else if(errno==EPIPE){ LM_ERR("SEAS Event Dispatcher has closed the pipe. Invalidating it !\n"); goto error; /** TODO handle this correctly !!!*/ } } seas_f.tmb.t_setkr(REQ_FWDED); ret=0; done: return ret; error: mycel=seas_f.tmb.t_gett(); if(mycel && mycel!=T_UNDEFINED){ if(!seas_f.tmb.t_reply(msg,500,&msg500)){ LM_ERR("t_reply (500)\n"); } } if(my_as_ev) shm_free(my_as_ev); if(buffer) shm_free(buffer); return ret; } /** * wrapper for the AS stateless relay script function. * */ static int w_as_relay_sl(struct sip_msg *msg, char *as_name, char *foo) { as_msg_p my_as_ev=0; int ret=0,len; char *buffer=0,processor_id; struct as_entry *as; as=(struct as_entry *)as_name; if(as->type==AS_TYPE){ if((processor_id=get_processor_id(&msg->rcv,&(as->u.as)))<0){ LM_ERR("no processor found for packet with dst port:%d\n",msg->rcv.dst_port); goto error; } }else if (as->type==CLUSTER_TYPE) { LM_ERR("clustering not fully implemented\n"); goto error; }else{ LM_ERR("unknown type of as\n"); goto error; } LM_DBG("as found ! (%.*s) processor id = %d\n",as->name.len,as->name.s,processor_id); if(!(buffer=create_as_event_sl(msg,processor_id,&len,0))){ LM_ERR("unable to create event code\n"); goto error; } if(!(my_as_ev=shm_malloc(sizeof(as_msg_t)))) goto error; my_as_ev->msg = buffer; my_as_ev->as = as; my_as_ev->type = SL_REQ_IN; my_as_ev->len = len; my_as_ev->transaction=seas_f.tmb.t_gett(); /*does not refcount*/ if(use_stats) as_relay_stat(seas_f.tmb.t_gett()); again: ret=write(write_pipe,&my_as_ev,sizeof(as_msg_p)); if(ret==-1){ if(errno==EINTR) goto again; else if(errno==EPIPE){ LM_ERR("SEAS Event Dispatcher has closed the pipe. Invalidating it !\n"); return -2; /** TODO handle this correctly !!!*/ } } //this shouln't be here, because it will remove the transaction from memory, but //if transaction isn't unref'ed iw will be released anyway at t_unref if kr (killreason)==0 // a wait timer will be put to run with WT_TIME_OUT (5 seconds, within which the AS should respond) // this is a bug !!! I think this is why we lose calls at high load !! //t_release(msg, 0, 0); seas_f.tmb.t_setkr(REQ_FWDED); ret=0; return ret; error: if(my_as_ev) shm_free(my_as_ev); if(buffer) shm_free(buffer); return ret; } /** * creates an as_event in shared memory and returns its address or NULL if error. * event_length(4) UNSIGNED INT includes the length 4 bytes itself * type(1), * flags(4), * transport(1). * src_ip_len(1), * src_ip(4 or 16), * dst_ip_len(1), * dst_ip(4 or 16), * src_port(2), * dst_port(2), * hash index(4), * label(4), * [cancelled hash_index,label] * */ char * create_as_event_t(struct cell *t,struct sip_msg *msg,char processor_id,int *evt_len,int flags) { unsigned int i,hash_index,label; unsigned short int port; unsigned int k,len; char *buffer=NULL; struct cell *originalT; originalT=0; if(!(buffer=shm_malloc(ENCODED_MSG_SIZE))){ LM_ERR("Out Of Memory !!\n"); return 0; } *evt_len=0; if(t){ hash_index=t->hash_index; label=t->label; }else{ /**seas_f.tmb.t_get_trans_ident(msg,&hash_index,&label); this is bad, because it ref-counts !!!*/ LM_ERR("no transaction provided...\n"); goto error; } k=4; /*type*/ buffer[k++]=(unsigned char)T_REQ_IN; /*processor_id*/ buffer[k++]=(unsigned char)processor_id; /*flags*/ if(is_e2e_ack(t,msg)){ flags|=E2E_ACK; }else if(msg->REQ_METHOD==METHOD_CANCEL){ LM_DBG("new CANCEL\n"); originalT=seas_f.tmb.t_lookup_original_t(msg); if(!originalT || originalT==T_UNDEFINED){ /** we dont even pass the unknown CANCEL to JAIN*/ LM_WARN("CANCEL does not match any existing transaction!!\n"); goto error; }else{ flags|=CANCEL_FOUND; //seas_f.tmb.unref_cell(originalT); } LM_DBG("Cancelling transaction !!\n"); } flags=htonl(flags); memcpy(buffer+k,&flags,4); k+=4; /*protocol should be UDP,TCP,TLS or whatever*/ buffer[k++]=(unsigned char)msg->rcv.proto; /*src ip len + src ip*/ len=msg->rcv.src_ip.len; buffer[k++]=(unsigned char)len; memcpy(buffer+k,&(msg->rcv.src_ip.u),len); k+=len; /*dst ip len + dst ip*/ len=msg->rcv.dst_ip.len; buffer[k++]=(unsigned char)len; memcpy(buffer+k,&(msg->rcv.dst_ip.u),len); k+=len; /*src port */ port=htons(msg->rcv.src_port); memcpy(buffer+k,&port,2); k+=2; /*dst port */ port=htons(msg->rcv.dst_port); memcpy(buffer+k,&port,2); k+=2; /*hash_index*/ i=htonl(hash_index); memcpy(buffer+k,&i,4); k+=4; /*label (is the collision slot in the hash-table)*/ i=htonl(label); memcpy(buffer+k,&i,4); k+=4; if(msg->REQ_METHOD==METHOD_CANCEL && originalT){ LM_DBG("Cancelled transaction: Hash_Index=%d, Label=%d\n",originalT->hash_index,originalT->label); /*hash_index*/ i=htonl(originalT->hash_index); memcpy(buffer+k,&i,4); k+=4; /*label (is the collision slot in the hash-table)*/ i=htonl(originalT->label); memcpy(buffer+k,&i,4); k+=4; } /*length of event (hdr+payload-4), copied at the beginning*/ if(encode_msg(msg,buffer+k,ENCODED_MSG_SIZE-k)<0){ LM_ERR("Unable to encode msg\n"); goto error; } i = GET_PAY_SIZE(buffer+k); k+=i; *evt_len=k; k=htonl(k); memcpy(buffer,&k,4); return buffer; error: if(buffer) shm_free(buffer); return 0; } /** * creates an as_event in shared memory and returns its address or NULL if error. * event_length(4) UNSIGNED INT includes the length 4 bytes itself * type(1), * processor_id(4), * flags(4), * transport(1). * src_ip_len(1), * src_ip(4 or 16), * dst_ip_len(1), * dst_ip(4 or 16), * src_port(2), * dst_port(2), * */ char * create_as_event_sl(struct sip_msg *msg,char processor_id,int *evt_len,int flags) { unsigned int i; unsigned short int port; unsigned int k,len; char *buffer=NULL; if(!(buffer=shm_malloc(ENCODED_MSG_SIZE))){ LM_ERR("create_as_event_t Out Of Memory !!\n"); return 0; } *evt_len=0; /*leave 4 bytes for event length*/ k=4; /*type*/ buffer[k++]=(unsigned char)SL_REQ_IN; /*processor_id*/ buffer[k++]=(unsigned char)processor_id; /*flags*/ flags=htonl(flags); memcpy(buffer+k,&flags,4); k+=4; /*protocol should be UDP,TCP,TLS or whatever*/ buffer[k++]=(unsigned char)msg->rcv.proto; /*src ip len + src ip*/ len=msg->rcv.src_ip.len; buffer[k++]=(unsigned char)len; memcpy(buffer+k,&(msg->rcv.src_ip.u),len); k+=len; /*dst ip len + dst ip*/ len=msg->rcv.dst_ip.len; buffer[k++]=(unsigned char)len; memcpy(buffer+k,&(msg->rcv.dst_ip.u),len); k+=len; /*src port */ port=htons(msg->rcv.src_port); memcpy(buffer+k,&port,2); k+=2; /*dst port */ port=htons(msg->rcv.dst_port); memcpy(buffer+k,&port,2); k+=2; /*length of event (hdr+payload-4), copied at the beginning*/ if(encode_msg(msg,buffer+k,ENCODED_MSG_SIZE-k)<0){ LM_ERR("Unable to encode msg\n"); goto error; } i = GET_PAY_SIZE(buffer+k); k+=i; *evt_len=k; k=htonl(k); memcpy(buffer,&k,4); return buffer; error: if(buffer) shm_free(buffer); return 0; } static inline int is_e2e_ack(struct cell *t,struct sip_msg *msg) { if(msg->REQ_METHOD != METHOD_ACK) return 0; if (t->uas.status<300) return 1; return 0; } /** Initializes seas module. It first parses the listen_sockets parameter * which has the form "ip_address[:port]", creates the pipe to * communicate with the dispatcher. */ static int seas_init(void) { char *p,*port; struct hostent *he; struct socket_info *si; int c_pipe[2],mierr,i; /** Populate seas_functions*/ if (load_tm_api(&seas_f.tmb)!=0) { LM_ERR( "can't load TM API\n"); return -1; } if(!(seas_f.t_check_orig_trans = find_export("t_check_trans", 0, 0))){ LM_ERR( "Seas requires transaction module (t_check_trans not found)\n"); return -1; } /** Populate seas_functions*/ c_pipe[0]=c_pipe[1]=-1; p=seas_listen_socket; port=(char *)0; seas_listen_port=5080; /*if the seas_listen_socket configuration string is empty, use default values*/ if(p==NULL || *p==0){ si=get_first_socket(); seas_listen_ip=&si->address; } else {/*if config string is not empty, then try to find host first, and maybe port..*/ while(*p){ if(*p == ':'){ *p=0; port=p+1; break; } p++; } if(!(he=resolvehost(seas_listen_socket,0))) goto error; if(!(seas_listen_ip=pkg_malloc(sizeof(struct ip_addr)))) goto error; hostent2ip_addr(seas_listen_ip, he, 0); if(port!=(char *)0 && (seas_listen_port=str2s(port,strlen(port),&mierr))==0){ LM_ERR("invalid port %s \n",port); goto error; } } memset(unc_as_t,0,2*MAX_UNC_AS_NR*sizeof(struct unc_as));//useless because unc_as_t is in bss? if (pipe(c_pipe)==-1) { LM_ERR("cannot create pipe!\n"); goto error; } read_pipe=c_pipe[0]; write_pipe=c_pipe[1]; seas_init_tags(); if(0>start_stats_server(seas_stats_socket)) goto error; if(0>prepare_ha()) goto error; if(0>parse_cluster_cfg()) goto error; return 0; error: for(i=0;i<2;i++) if(c_pipe[i]!=-1) close(c_pipe[i]); if(seas_listen_ip!=0) pkg_free(seas_listen_ip); if(use_stats) stop_stats_server(); return -1; } /**Initializes SEAS to-tags */ static void seas_init_tags(void) { init_tags(seas_tags, &seas_tag_suffix,"VozTele-Seas/tags",'-'); LM_DBG("seas_init_tags, seas_tags=%s\n",seas_tags); } /** * This function initializes each one of the processes spawn by the server. * the rank is 1 only when the main process is being initialized, so in that * case the function spawns the SEAS process to handle as_events triggered * from the other SER processes (executing the script). * the new process created, then goes into dispatcher_main_loop(), where * it reads() the pipe waiting for events produced by other SER processes. */ static int seas_child_init(int rank) { int pid; /* only the child 1 will execute this */ if (rank != 1){ /* only dispatcher needs to read from the pipe, so close reading fd*/ close(read_pipe); return 0; } if ((pid=fork())<0) { LM_ERR("forking failed\n"); return -1; } if (!pid) { /*dispatcher child. we leave writing end open so that new childs spawned * by event dispatcher can also write to pipe.. */ /* close(write_pipe); */ return dispatcher_main_loop(); } return 0; } /* this should close the sockets open to any of the application servers, and * send them an EOF event or something that signals that SER is beeing shutdown, * so they could do their cleanup, etc. */ static int seas_exit(void) { if( seas_listen_ip!=NULL && seas_listen_ip!=&(get_first_socket()->address)) pkg_free(seas_listen_ip); return 0; } /** * search within a given AS, if any of the registered processors is bound * to the receive_info structure passed. If there is one, it returns its * identifier (number between 0 and 128), otherwise it returns -1; */ char get_processor_id(struct receive_info *rcv,as_p as) { int i; for(i=0;ibound_processor[i]!=0 && (rcv->dst_ip.len == as->binds[i]->address.len) && (rcv->dst_ip.af==as->binds[i]->address.af) && (!memcmp(rcv->dst_ip.u.addr,as->binds[i]->address.u.addr,rcv->dst_ip.len))/* && (rcv->dst_port==as->binds[i].dst_port) && (rcv->proto==as->binds[i].proto)*/) return as->bound_processor[i]; } return -1; } opensips-2.2.2/modules/seas/seas.h000066400000000000000000000106541300170765700171000ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SEAS_H #define _SEAS_H #include #include "../../str.h"/*str*/ #include "../../ip_addr.h"/*ip_addr*/ #include "../../sr_module.h" /*version,etc*/ #include "../tm/tm_load.h"/*tm_binds*/ #include "ha.h" #include "cluster.h" #define MAX_AS_NR 5 #define MAX_UNC_AS_NR 5 #define MAX_AS_NAME 15 /*#define AF2PF(af) (((af)==AF_INET)?PF_INET:((af)==AF_INET6)?PF_INET6:(af))*/ #define MAX_AS_PER_CLUSTER 10 #define HAS_FD 1 #define HAS_NAME 2 #define MAX_BINDS 10 #define MAX_WHOAMI_LEN 30 #define AS_BUF_SIZE 4000 #define ENCODED_MSG_SIZE 32000 /** EVENT FLAGS */ #define E2E_ACK 0x04 #define CANCEL_FOUND 0x08 #define AS_TYPE 1 #define CLUSTER_TYPE 2 /** ACTION identifiers **/ #define T_REQ_IN 2 #define SL_REQ_IN 3 #define RES_IN 4 #define PING_AC 5 #define BIND_AC 6 #define UNBIND_AC 7 /** ACTION identifiers **/ #define SPIRAL_FLAG 0x00000001 #define net2hostL(dst,from,index) do{ \ memcpy(&(dst),(from)+(index),4); \ dst=ntohl(dst); \ (index)+=4; \ }while(0); extern char use_stats; extern char whoami[]; extern int is_dispatcher; extern struct ip_addr *seas_listen_ip; extern unsigned short seas_listen_port; extern int write_pipe; extern int read_pipe; extern char seas_sigchld_received; extern int jain_ping; extern int jain_pings_lost; extern int servlet_ping; extern int servlet_pings_lost; extern struct as_entry *as_table; struct seas_functions{ struct tm_binds tmb; cmd_function t_check_orig_trans; }; /*TODO listen_points should be dynamically allocated ?*/ typedef struct app_server { int event_fd; int action_fd; str name; pid_t action_pid; struct socket_info *binds[MAX_BINDS]; char bound_processor[MAX_BINDS]; int num_binds; str ev_buffer; str ac_buffer; struct ha jain_pings; struct ha servlet_pings; struct cluster *cluster; }as_t, *as_p; struct cluster{ str name; int num; int registered; str as_names[MAX_AS_PER_CLUSTER]; as_p servers[MAX_AS_PER_CLUSTER]; }; /** * SER processes will go through the as_table, doing if(valid && memcmp(name,his_name,name_len)==0), * when one matches, they will put the as pointer inside the event that should process * that event. * If eventually the as becomes unavailable, the dispatcher will set valid=false, which should be * atomic operation. This way, we prevent having to put a mutex on the array, which would make * it slower , as only one process could be accessing it at a time. */ struct as_entry{ str name; int type; int connected; union{ struct app_server as; struct cluster cs; }u; struct as_entry *next; }; extern struct as_entry *my_as; extern struct seas_functions seas_f; extern struct as_entry *as_list; typedef struct as_msg { struct cell *transaction; char *msg; int len; int type; int id; struct as_entry *as; }as_msg_t,*as_msg_p; char get_processor_id(struct receive_info *rcv,as_p as); void seas_sighandler(int signo); char* create_as_event_t(struct cell *t,struct sip_msg *msg,char processor_id,int *evt_len,int flags); char* create_as_event_sl(struct sip_msg *msg,char processor_id,int *evt_len,int flags); static inline void print_ip_buf(struct ip_addr* ip, char *where,int len) { switch(ip->af){ case AF_INET: snprintf(where,len,"%d.%d.%d.%d", ip->u.addr[0], ip->u.addr[1], ip->u.addr[2], ip->u.addr[3]); break; case AF_INET6: snprintf(where,len,"%x:%x:%x:%x:%x:%x:%x:%x",htons(ip->u.addr16[0]),htons(ip->u.addr16[1]),htons(ip->u.addr16[2]), htons(ip->u.addr16[3]), htons(ip->u.addr16[4]), htons(ip->u.addr16[5]), htons(ip->u.addr16[6]), htons(ip->u.addr16[7])); break; default: break; } } #endif opensips-2.2.2/modules/seas/seas_action.c000066400000000000000000001321701300170765700204260ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2008-04-04 added support for local and remote dispaly name in TM dialogs * (by Andrei Pisau ) * */ #include #include #include #include #include #include #include /*assert*/ #include "../../mem/mem.h" #include "../../dprint.h" #include "../../str.h" #include "../../pt.h"/*process_count*/ #include "../../ip_addr.h" #include "../../tags.h" #include "../../error.h" #include "../../ut.h" #include "../../parser/hf.h" #include "../../parser/parse_fline.h" #include "../../parser/parser_f.h"/*find_not_quoted*/ #include "../../parser/parse_to.h" #include "../../parser/parse_from.h" #include "../../parser/parse_cseq.h" #include "../../parser/parse_content.h" #include "../../parser/parse_rr.h"/*parse_rr*/ #include "../../parser/parse_via.h"/*parse_via*/ #include "../../parser/parse_param.h"/*parse_params*/ #include "../../parser/parse_uri.h" /*parse_uri*/ #include "../../parser/msg_parser.h" #include "encode_msg.h" #include "../tm/t_lookup.h" #include "../tm/h_table.h" #include "../tm/dlg.h" #include "seas.h" #include "statistics.h" #include "seas_action.h" #include "seas_error.h" #include "ha.h" #define MAX_HEADER 1024 #define SPIRAL_HDR "X-WeSIP-SPIRAL: true" #define SPIRAL_HDR_LEN (sizeof(SPIRAL_HDR)-1) #define RECORD_ROUTE "Record-Route: " #define RECORD_ROUTE_LEN (sizeof(RECORD_ROUTE)-1) #define VIA "Via: " #define VIA_LEN (sizeof(VIA)-1) extern char *seas_tag_suffix; extern char seas_tags[]; pid_t my_parent; extern int fifo_pid; static inline struct sip_msg *parse_ac_msg(hdr_flags_t flags,char *start,int len); static inline void free_sip_msg_lite(struct sip_msg *my_msg); static inline int calculate_hooks(dlg_t* _d); static inline int process_input(int fd); static inline int process_pings(struct ha *the_table); static inline int ac_jain_pong(as_p the_as,char *action,int len); int process_pong(struct ha *the_table,unsigned int seqno); int print_local_uri(as_p as,char processor_id,char *where,int len); int dispatch_actions(void) { int fd,n,ret,timeout,elapsed_ms; static int ktimeout; struct pollfd fds[1]; struct timeval last,now; /* now the process_no is set, I delete the pt (process_table) global var, * because it confuses LM_*() */ pt=0; fd=my_as->u.as.action_fd; fds[0].fd=fd; fds[0].events=POLLIN|POLLHUP; fds[0].revents=0; my_parent=getppid(); snprintf(whoami,MAX_WHOAMI_LEN,"[%.*s] Action dispatcher",my_as->name.len,my_as->name.s); if(jain_ping_timeout && servlet_ping_timeout) ktimeout=jain_ping_timeoutu.as.ac_buffer.s = pkg_malloc(AS_BUF_SIZE))==0){ LM_ERR("no more pkg mem\n"); return -1; } my_as->u.as.ac_buffer.len=0; if(use_ha){ timeout=ktimeout; while(1){ gettimeofday(&last,NULL); print_pingtable(&my_as->u.as.jain_pings,-1,1); if(0>(n=poll(fds,1,timeout))){ if(errno==EINTR){ gettimeofday(&last,NULL); continue; }else if(errno==EBADF){ LM_ERR("EBADF !!\n"); }else{ LM_ERR("on poll\n"); } }else if(n==0){/*timeout*/ if (0>(ret=process_pings(&my_as->u.as.jain_pings))) { return ret; } timeout=ktimeout; }else{ /*events*/ if (0>(ret=process_input(fd))) { return ret; } gettimeofday(&now,NULL); elapsed_ms=((now.tv_sec-last.tv_sec)*1000)+((now.tv_usec-last.tv_usec)/1000); if(elapsed_ms(ret=process_pings(&my_as->u.as.jain_pings))){ return ret; } timeout=ktimeout; } } fds[0].events=POLLIN|POLLHUP; fds[0].revents=0; } }else{ do{ ret=process_input(fd); }while(ret>=0); } return 0; } static inline int process_input(int fd) { int j,k; k=AS_BUF_SIZE-(my_as->u.as.ac_buffer.len); again: if(0>(j=read(fd,my_as->u.as.ac_buffer.s+my_as->u.as.ac_buffer.len,k))){ if(errno==EINTR) goto again; LM_ERR("reading data for as %.*s (%s)\n",my_as->name.len,my_as->name.s,strerror(errno)); return -1; }else if(j==0){ pkg_free(my_as->u.as.ac_buffer.s); close(fd); LM_ERR("read 0 bytes from AS:%.*s\n",my_as->name.len,my_as->name.s); /** we return, so we will exit, so our parent (Event Dispatcher) will receive a sigchld and know * it should tear down the corresponding AS * what still is not clear is what will happen to events that were put in the pipe... */ return -2; } (my_as->u.as.ac_buffer.len)+=j; LM_DBG("read %d bytes from AS action socket (total = %d)\n",j,my_as->u.as.ac_buffer.len); if(use_stats) receivedplus(); if(my_as->u.as.ac_buffer.len>5){ process_action(&my_as->u.as); LM_DBG("(Action dispatched,buffer.len=%d)\n",my_as->u.as.ac_buffer.len); } return 0; } /** * The ha structure (high availability) uses a circular (ring) buffer. A linked * list could be used, but it would involve a lot of shm_malloc/free, and this * would involve a lot of shm-lock_get/release, which would interfere a lot * with all the SER processes. With a this ring buffer, the lock_get/release only * involve the SEAS processes. * This function scans the ping structures in the buffer, computing the elapsed time * from when the ping was sent, so if the ping has timed out, it increases the * timed_out_pings counter. All the timed-out pings are removed from the buffer (the * begin index is incremented). Because the pings are added always at the end * of the buffer, they will always be ordered in increasing time, so when we find one ping * that has not timed out, the following pings will neither be. * */ static inline int process_pings(struct ha *the_table) { int i,k,elapsed; struct ping *tmp; struct timeval now; tmp=NULL; gettimeofday(&now,NULL); if(the_table->count==0) return 0; lock_get(the_table->mutex); { print_pingtable(the_table,-1,0); for(i=0;icount;i++){ k=(the_table->begin+i)%the_table->size; tmp=the_table->pings+k; elapsed=(now.tv_sec-tmp->sent.tv_sec)*1000+(now.tv_usec-tmp->sent.tv_usec)/1000; if(elapsed>the_table->timeout){ LM_DBG("ping timed out %d\n",tmp->id); the_table->timed_out_pings++; }else{ the_table->begin=k; the_table->count-=i; break; } } } lock_release(the_table->mutex); return 0; } /* Because TransactionModule organizes statistics based on process_no, * and process_no are only assigned to SER processes (not to Action dispatchers like us ;) * we have to simulate we are the FIFO process, so TM thinks that the transactions WE put * are put by the fifo process... static inline void set_process_no() { int pcnt,i; pcnt=process_count(); for(i=0;iac_buffer.s[0]<<24)|(the_as->ac_buffer.s[1]<<16)|(the_as->ac_buffer.s[2]<<8)|((the_as->ac_buffer.s[3])&0xFF); /*yeah, it comes in network byte order*/ /*if ac_len > BUF_SIZE then a flag should be put on the AS so that the whole length * of the action is skipped, until a mechanism for handling big packets is implemented*/ if(use_stats) stats_reply(); if(ac_len>AS_BUF_SIZE){ LM_WARN("action too big (%d)!!! should be skipped and" " an error returned!\n",ac_len); return -1; } while (the_as->ac_buffer.len>=ac_len) { LM_DBG("Processing action %d bytes long\n",ac_len); switch(the_as->ac_buffer.s[4]){ case REPLY_PROV: case REPLY_FIN: LM_DBG("Processing a REPLY action from AS (length=%d): %.*s\n", ac_len,the_as->name.len,the_as->name.s); ac_reply(the_as,the_as->ac_buffer.s+5,ac_len-5); break; case UAC_REQ: LM_DBG("Processing an UAC REQUEST action from AS (length=%d): %.*s\n", ac_len,the_as->name.len,the_as->name.s); ac_uac_req(the_as,the_as->ac_buffer.s+5,ac_len-5); break; case AC_CANCEL: LM_DBG("Processing a CANCEL REQUEST action from AS (length=%d): %.*s\n", ac_len,the_as->name.len,the_as->name.s); ac_cancel(the_as,the_as->ac_buffer.s+5,ac_len-5); break; case SL_MSG: LM_DBG("Processing a STATELESS MESSAGE action from AS (length=%d): %.*s\n", ac_len,the_as->name.len,the_as->name.s); ac_sl_msg(the_as,the_as->ac_buffer.s+5,ac_len-5); break; case JAIN_PONG: LM_DBG("Processing a PONG\n"); ac_jain_pong(the_as,the_as->ac_buffer.s+5,ac_len-5); break; default: LM_DBG("Processing a UNKNOWN TYPE action from AS (length=%d): %.*s\n", ac_len,the_as->name.len,the_as->name.s); break; } memmove(the_as->ac_buffer.s,the_as->ac_buffer.s+ac_len,(the_as->ac_buffer.len)-ac_len); (the_as->ac_buffer.len)-=ac_len; if(the_as->ac_buffer.len>5){ ac_len=(the_as->ac_buffer.s[0]<<24)|(the_as->ac_buffer.s[1]<<16)|(the_as->ac_buffer.s[2]<<8)|((the_as->ac_buffer.s[3])&0xFF); }else{ return 0; } } return 0; } static inline int ac_jain_pong(as_p the_as,char *action,int len) { unsigned int seqno,flags; int k; k=0; net2hostL(flags,action,k); net2hostL(seqno,action,k); process_pong(&the_as->jain_pings,seqno); return 0; } int process_pong(struct ha *the_table,unsigned int seqno) { int i,k,elapsed; struct ping *tmp; struct timeval now; gettimeofday(&now,NULL); tmp=NULL; if(the_table->count==0) return 0; lock_get(the_table->mutex); print_pingtable(the_table,-1,0); for(i=0;icount;i++){ k=(the_table->begin+i)%the_table->size; tmp=the_table->pings+k; if(tmp->id == seqno){ elapsed=(now.tv_sec-tmp->sent.tv_sec)*1000+(now.tv_usec-tmp->sent.tv_usec)/1000; LM_DBG("Ping-Pong delay: %d (timeout was:%d)\n",elapsed,the_table->timeout); if(elapsed>the_table->timeout){ /*if this ping has timed out, all the more-ancient pings will also be * timed out*/ the_table->timed_out_pings+=i; }/*anyway, when we find a ping in the table, we remove all the pings that are more ancient (if there are any..)*/ the_table->count-=(i+1); the_table->begin=(k+1)%the_table->size; break; } } lock_release(the_table->mutex); return 0; } /** * ac_cancel: * @param the_as Application Server structure which sent this action * @param action action payload * @param len the length of the payload * * This function cancels a previously initiated UAC Transaction. * it receives the HashIndex and Label of the cell being cancelled * and invokes t_cancel_uac from the transactionModule API which * cancels the transaction. * * Returns: * */ int ac_cancel(as_p the_as,char *action,int len) { unsigned int flags,ret,cancelled_hashIdx,cancelled_label; struct cell* t_invite; /* Disabled after CANCEL changes struct sip_msg *my_msg; struct as_uac_param *the_param; str headers,body; char *p; */ int k,retval,uac_id; /* Disabled after CANCEL changes body.s=headers.s=NULL; my_msg=NULL; the_param=NULL; */ k=0; net2hostL(flags,action,k); net2hostL(uac_id,action,k); k++; net2hostL(cancelled_hashIdx,action,k); net2hostL(cancelled_label,action,k); /* Disabled after CANCEL changes if(!(headers.s=pkg_malloc(MAX_HEADER))){ LM_ERR("Out of Memory!!"); goto error; } headers.len=0; if(!(my_msg=pkg_malloc(sizeof(struct sip_msg)))){ LM_ERR("out of memory!\n"); goto error; } memset(my_msg,0,sizeof(struct sip_msg)); my_msg->buf=action+k; my_msg->len=len-k; LM_DBG("Action UAC Message: uac_id:%d processor_id=%d, message:[%.*s]\n", uac_id,processor_id,len-4,&action[4]); if(parse_msg(action+k,len-k,my_msg)<0){ LM_ERR("parsing sip_msg"); goto error; } if(my_msg->first_line.type==SIP_REPLY){ LM_ERR("trying to create a UAC with a SIP response!!\n"); goto error; } if(parse_headers(my_msg,HDR_EOH_F,0)==-1){ LM_ERR("parsing headers\n"); goto error; } if(0>(headers.len=extract_allowed_headers(my_msg,1,-1,HDR_CONTENTLENGTH_F|HDR_ROUTE_F|HDR_TO_F|HDR_FROM_F|HDR_CALLID_F|HDR_CSEQ_F,headers.s,MAX_HEADER))) { LM_ERR("Unable to extract allowed headers!!\n"); goto error; } if(flags & SPIRAL_FLAG){ memcpy(headers.s+headers.len,SPIRAL_HDR CRLF,SPIRAL_HDR_LEN + CRLF_LEN); headers.len+=SPIRAL_HDR_LEN+CRLF_LEN; //headers.s[headers.len]=0; //fake_uri.s=pkg_malloc(200); //fake_uri.len=print_local_uri(the_as,processor_id,fake_uri.s,200); //if(fake_uri.len<0){ //SLM_ERR("printing local uri\n"); //goto error; //} //my_dlg->hooks.next_hop=&fake_uri; } headers.s[headers.len]=0; // let's get the body if (get_body(my_msg,&body)!=0) { LM_ERR("failed to extract body\n"); goto error; } if(body.len!=0){ if(!(p=pkg_malloc(body.len))){ LM_ERR("Out of Memory!"); goto error; } memcpy(p,body.s,body.len); body.s = p; LM_DBG("Trying to construct a Sip Request with: body:%d[%s]" " headers:%d[%s]\n", body.len,body.s,headers.len,headers.s); } if(!(the_param=shm_malloc(sizeof(struct as_uac_param)))){ LM_ERR("no more share memory\n"); goto error; } */ if(seas_f.tmb.t_lookup_ident(&t_invite,cancelled_hashIdx,cancelled_label)<0){ LM_ERR("failed to t_lookup_ident hash_idx=%d," "label=%d\n", cancelled_hashIdx,cancelled_label); goto error; } /* Disabled after CANCEL changes the_param->who=my_as; the_param->uac_id=uac_id; the_param->processor_id=processor_id; the_param->destroy_cb_set=0; */ ret=seas_f.tmb.t_cancel_trans( t_invite , NULL); //ret=seas_f.tmb.t_cancel_uac(&headers,&body,cancelled_hashIdx, // cancelled_label,uac_cb,(void*)the_param, 0); seas_f.tmb.unref_cell(t_invite); if (ret == 0) { LM_ERR( "t_cancel_uac failed\n"); as_action_fail_resp(uac_id,SE_CANCEL,SE_CANCEL_MSG,SE_CANCEL_MSG_LEN); goto error; }else{ /* Disabled after CANCEL changes the_param->label=ret; */ } retval=0; goto exit; error: retval = -1; /* Disabled after CANCEL changes if(the_param) shm_free(the_param); */ exit: /* Disabled after CANCEL changes if(headers.s) pkg_free(headers.s); if(body.s) pkg_free(headers.s); if(my_msg){ if(my_msg->headers) free_hdr_field_lst(my_msg->headers); pkg_free(my_msg); } */ return retval; } int recordroute_diff(struct sip_msg *req,struct sip_msg *resp) { struct hdr_field *hf; rr_t *rr1; int i,j,k; i=j=k=0; /* count how many record-route bodies come in the response*/ /* this does not work, I think because of siblings for(hf=resp->record_route;hf;hf=hf->sibling,j=0){ */ for(hf=resp->headers;hf;hf=hf->next,j=0){ if(hf->type != HDR_RECORDROUTE_T) continue; if(!hf->parsed){ if(0>parse_rr(hf)) goto error; j=1; } for(rr1=hf->parsed;rr1;rr1=rr1->next){ i++; } if(j){ free_rr((rr_t**)(void*)&hf->parsed); hf->parsed=NULL; } } /* for(hf=req->record_route;hf;hf=hf->sibling,j=0){ */ for(hf=req->headers;hf;hf=hf->next,j=0){ if(hf->type != HDR_RECORDROUTE_T) continue; if(!hf->parsed){ if(0>parse_rr(hf)) goto error; j=1; } for(rr1=hf->parsed;rr1;rr1=rr1->next){ k++; } if(j){ free_rr((rr_t**)(void*)&hf->parsed); hf->parsed=NULL; } } return i-k; error: return -1; } int via_diff(struct sip_msg *req,struct sip_msg *resp) { struct hdr_field *hf; struct via_body *vb; int i,j,k; i=j=k=0; /* count how many via bodies come in the response*/ for(hf=resp->h_via1;hf;hf=hf->sibling){ if(!hf->parsed){ if((vb=pkg_malloc(sizeof(struct via_body)))==0){ LM_ERR("Out of mem in via_diff!!\n"); return -1; } memset(vb,0,sizeof(struct via_body)); parse_via(hf->body.s,hf->body.s+hf->body.len+1,vb); if (vb->error != PARSE_OK) { LM_ERR("Unable to parse via in via_diff!\n"); free_via_list(vb); return -1; } hf->parsed=vb; j=1; } for(vb=hf->parsed;vb;vb=vb->next){ i++; } if(j){ free_via_list((struct via_body*)hf->parsed); hf->parsed=NULL; j=0; } } j=0; /* count how many via bodies were in the orig. request*/ for(hf=req->h_via1;hf;hf=hf->sibling){ if(!hf->parsed){ if((vb=pkg_malloc(sizeof(struct via_body)))==0){ goto error; } memset(vb,0,sizeof(struct via_body)); parse_via(hf->body.s,hf->body.s+hf->body.len+1,vb); if (vb->error != PARSE_OK) { free_via_list(vb); goto error; } hf->parsed=vb; j=1; } for(vb=hf->parsed;vb;vb=vb->next){ k++; } if(j){ free_via_list((struct via_body*)hf->parsed); hf->parsed=NULL; j=0; } } return i-k; error: return -1; } /** * ac_reply: UAS transaction Reply action. It replies to an incoming request with a response. * @param the_as The App Server that sent this action. * @param action action * @param len length * * function description * * Returns: what */ int ac_reply(as_p the_as,char *action,int len) { unsigned int flags,hash_index,label; struct cell *c; struct sip_msg *my_msg; struct to_body *tb; str new_header,body,totag; char *ttag; int i,k,retval; static char headers[MAX_HEADER]; ttag=NULL; my_msg=NULL; i=k=0; net2hostL(flags,action,k); net2hostL(hash_index,action,k); net2hostL(label,action,k); if(seas_f.tmb.t_lookup_ident(&c,hash_index,label)<0){ LM_ERR("Failed to t_lookup_ident hash_idx=%d,label=%d\n",hash_index,label); goto error; } if(use_stats) action_stat(c); if(c->uas.status>=200){ LM_ERR("ac_reply: trying to reply to a \"%.*s\" transaction" "that is already in completed state\n",REQ_LINE(c->uas.request).method.len,REQ_LINE(c->uas.request).method.s); goto error; } if (!(my_msg=parse_ac_msg(HDR_EOH_F,action+k,len-k))) { LM_ERR("Failed to parse_ac_msg hash_idx=%d,label=%d\n",hash_index,label); goto error; } tb=(struct to_body*)my_msg->to->parsed; if(tb->tag_value.s && tb->tag_value.len){ totag=tb->tag_value; }else{ totag.s=NULL; totag.len=0; /*if(!(ttag=pkg_malloc(TOTAG_VALUE_LEN))){ LM_ERR("Out of memory !!!\n"); goto error; } totag.s=ttag; calc_crc_suffix(c->uas.request,seas_tag_suffix); LM_DBG("seas_tags = %.*s\n",TOTAG_VALUE_LEN,seas_tags); memcpy(totag.s,seas_tags,TOTAG_VALUE_LEN); totag.len=TOTAG_VALUE_LEN;*/ } LM_DBG("Using totag=[%.*s]\n",totag.len,totag.s); if(0>(i=recordroute_diff(c->uas.request,my_msg))){/*not likely..*/ LM_DBG("Seems that request had more RecordRoutes than response...\n"); goto error; }else LM_DBG("Recordroute Diff = %d\n",i); if(0>(i=extract_allowed_headers(my_msg,0,i,HDR_VIA_F|HDR_TO_F|HDR_FROM_F|HDR_CSEQ_F|HDR_CALLID_F|HDR_CONTENTLENGTH_F,headers,MAX_HEADER))){ LM_ERR("ac_reply() filtering headers !\n"); goto error; } headers[i]=0; new_header.s=headers; new_header.len=i; /* If it is INVITE and response is success (>=200 && <300), we mark it as local so that * SER does NOT retransmit the final response (by default, SER retransmit local UAS final * responses...*/ if(is_invite(c) && my_msg->first_line.u.reply.statuscode>=200 && my_msg->first_line.u.reply.statuscode<300) c->flags |= T_IS_LOCAL_FLAG; /*WARNING casting unsigned int to int*/ get_body(my_msg, &body); LM_DBG("Trying to construct a SipReply with: ReasonPhrase:[%.*s] body:[%.*s] headers:[%.*s] totag:[%.*s]\n",\ my_msg->first_line.u.reply.reason.len,my_msg->first_line.u.reply.reason.s,\ body.len,body.s,new_header.len,new_header.s,totag.len,totag.s); if(seas_f.tmb.t_reply_with_body(c,my_msg->first_line.u.reply.statuscode, &(my_msg->first_line.u.reply.reason),&body,&new_header,&totag)<0){ LM_ERR("Failed to t_reply\n"); goto error; } seas_f.tmb.unref_cell(c); retval=0; goto exit; error: retval = -1; seas_f.tmb.unref_cell(c); exit: if(ttag) pkg_free(ttag); if(my_msg){ free_sip_msg_lite(my_msg); pkg_free(my_msg); } return retval; } static inline struct sip_msg *parse_ac_msg(hdr_flags_t flags,char *start,int len) { struct sip_msg *my_msg; my_msg=NULL; if(!(my_msg=pkg_malloc(sizeof(struct sip_msg)))){ LM_ERR("ac_reply: out of memory!\n"); goto error; } memset(my_msg,0,sizeof(struct sip_msg)); my_msg->buf=start; my_msg->len=len; LM_DBG("Action Message:[%.*s]\n",len,start); if(0>parse_msg(start,len,my_msg)){ LM_ERR("parse_ac_msg: parsing sip_msg"); goto error; } if(0>parse_headers(my_msg,flags,0)){ LM_ERR("parse_ac_msg: parsing headers\n"); goto error; } return my_msg; error: if(my_msg){ free_sip_msg_lite(my_msg); pkg_free(my_msg); } return NULL; } /* Actions are composed as follows: * (the action length and type as always= 5 bytes) * * TODO performance speedup: instead of using * dynamically allocated memory for headers,body,totag,reason and my_msg * use static buffers. * */ int ac_sl_msg(as_p the_as,char *action,int len) { struct sip_msg *my_msg; str *uri; struct proxy_l *proxy; rr_t *my_route; int k,retval; unsigned int flags; enum sip_protos proto; my_msg=NULL; k=0; net2hostL(flags,action,k); k++; proxy=0; if(!(my_msg = parse_ac_msg(HDR_EOH_F,action+k,len-k))){ LM_ERR("out of memory!\n"); goto error; } if(my_msg->first_line.type == SIP_REQUEST) LM_DBG("forwarding request:\"%.*s\" statelessly \n",my_msg->first_line.u.request.method.len+1+\ my_msg->first_line.u.request.uri.len,my_msg->first_line.u.request.method.s); else LM_DBG("forwarding reply:\"%.*s\" statelessly \n",my_msg->first_line.u.reply.status.len+1+\ my_msg->first_line.u.reply.reason.len,my_msg->first_line.u.reply.status.s); if (my_msg->route) { if (parse_rr(my_msg->route) < 0) { LM_ERR( "Error while parsing Route body\n"); goto error; } my_route = (rr_t*)my_msg->route->parsed; uri=&(my_route->nameaddr.uri); }else{ uri = GET_RURI(my_msg); } my_msg->force_send_socket=grep_sock_info(&my_msg->via1->host,my_msg->via1->port,my_msg->via1->proto); /* or also could be: my_msg->force_send_socket=the_as->binds[processor_id].bind_address; not sure which is better... */ proxy=uri2proxy(uri,PROTO_NONE); if (proxy==0) { LM_ERR("unable to create proxy from URI \n"); goto error; } proto=proxy->proto; /* uri2proxy set it correctly */ //TODO my_msg->recvd if(0>forward_sl_request(my_msg,proxy,proto)) goto error; retval=0; goto exit; error: retval = -1; exit: if(proxy){ free_proxy(proxy); pkg_free(proxy); } if(my_msg){ free_sip_msg_lite(my_msg); pkg_free(my_msg); } return retval; } static inline void free_sip_msg_lite(struct sip_msg *my_msg) { if(my_msg){ /**should do the same as in free_sip_msg() but w/o freeing my_msg->buf*/ if (my_msg->new_uri.s) { pkg_free(my_msg->new_uri.s); my_msg->new_uri.len=0; } if (my_msg->dst_uri.s) { pkg_free(my_msg->dst_uri.s); my_msg->dst_uri.len=0; } if (my_msg->path_vec.s) { pkg_free(my_msg->path_vec.s);my_msg->path_vec.len=0; } if (my_msg->headers) free_hdr_field_lst(my_msg->headers); if (my_msg->add_rm) free_lump_list(my_msg->add_rm); if (my_msg->body_lumps) free_lump_list(my_msg->body_lumps); /* this is not in lump_struct.h, and anyhow it's not supposed to be any lumps * in our messages... or is it? if (my_msg->reply_lump) free_reply_lump(my_msg->reply_lump); */ } } int forward_sl_request(struct sip_msg *msg,struct proxy_l *proxy,int proto) { union sockaddr_union *to; struct socket_info *send_sock; int ret; to = (union sockaddr_union*)pkg_malloc(sizeof(union sockaddr_union)); ret = -1; hostent2su(to, &proxy->host, proxy->addr_idx, (proxy->port)?proxy->port:SIP_PORT); do { send_sock=get_send_socket(msg, to, proto); if (send_sock==0){ LM_ERR( "cannot forward to af %d, " "proto %d no corresponding listening socket\n", to->s.sa_family, proto); continue; } LM_DBG("Sending:\n%.*s.\n", (int)msg->len,msg->buf); if (msg_send(send_sock, proto, to, 0, msg->buf,msg->len, NULL)<0){ LM_ERR("ERROR:seas:forward_msg: Error sending message !!\n"); continue; } ret = 0; break; }while( get_next_su( proxy, to, 0)==0 ); pkg_free(to); return ret; } /*Actions are composed as follows: * (the action length and type as always= 5 bytes) * 4:uac_id * * int request(str* method, str* req_uri, str* to, str* from, str* headers, str* body, transaction_cb c, void* cp) * TODO performance speedup: instead of using * dynamically allocated memory for headers,body,totag,reason and my_msg * use static buffers. * */ int ac_uac_req(as_p the_as,char *action,int len) { unsigned int flags,cseq; char err_buf[MAX_REASON_LEN],processor_id; struct sip_msg *my_msg; struct to_body *fb,*tb; struct cseq_body *cseqb; struct as_uac_param *the_param; dlg_t *my_dlg; int k,retval,uac_id,sip_error,ret,err_ret; str headers,body,fake_uri; char *p; headers.s=body.s=fake_uri.s=NULL; my_dlg=NULL; my_msg=NULL; the_param=NULL; k=0; net2hostL(flags,action,k); net2hostL(uac_id,action,k); processor_id=action[k++]; if(!(headers.s=pkg_malloc(MAX_HEADER))){ LM_ERR("Out of Memory!!"); goto error; } headers.len=0; LM_DBG("Action UAC Message: uac_id:%d processor_id=%d\n",uac_id,processor_id); if (!(my_msg = parse_ac_msg(HDR_EOH_F,action+k,len-k))) { LM_ERR("out of memory!\n"); goto error; } if(my_msg->first_line.type==SIP_REPLY){ LM_ERR("trying to create a UAC with a SIP response!!\n"); goto error; } if(parse_headers(my_msg,HDR_EOH_F,0)==-1){ LM_ERR("ERROR:seas:ac_uac_req:parsing headers\n"); goto error; } if(parse_from_header(my_msg)<0){ LM_ERR("parsing from header ! \n"); goto error; } if(check_transaction_quadruple(my_msg)==0){ as_action_fail_resp(uac_id,SE_UAC,"Headers missing (to,from,call-id,cseq)?",0); LM_ERR("Headers missing (to,from,call-id,cseq)?"); goto error; } if(!(get_from(my_msg)) || !(get_from(my_msg)->tag_value.s) || !(get_from(my_msg)->tag_value.len)){ as_action_fail_resp(uac_id,SE_UAC,"From tag missing",0); LM_ERR("From tag missing"); goto error; } fb=my_msg->from->parsed; tb=my_msg->to->parsed; cseqb=my_msg->cseq->parsed; if(0!=(str2int(&cseqb->number,&cseq))){ LM_DBG("unable to parse CSeq\n"); goto error; } if(my_msg->first_line.u.request.method_value != METHOD_ACK && my_msg->first_line.u.request.method_value != METHOD_CANCEL) { /** we trick req_within */ cseq--; } if(seas_f.tmb.new_dlg_uac(&(my_msg->callid->body),&(fb->tag_value),cseq,\ &(fb->uri),&(tb->uri),&my_dlg) < 0) { as_action_fail_resp(uac_id,SE_UAC,"Error creating new dialog",0); LM_ERR("Error while creating new dialog\n"); goto error; } if(seas_f.tmb.dlg_add_extra(my_dlg,&(fb->display),&(tb->display)) < 0 ) { as_action_fail_resp(uac_id,SE_UAC, "Error adding the display names to the new dialog",0); LM_ERR("failed to add display names to the new dialog\n"); goto error; } if(tb->tag_value.s && tb->tag_value.len) shm_str_dup(&my_dlg->id.rem_tag,&tb->tag_value); /**Awful hack: to be able to set our own CSeq, from_tag and call-ID we have * to use req_within instead of req_outside (it sets it's own CSeq,Call-ID * and ftag), so we have to simulate that the dialog is already in completed * state so... */ server_signature=0; my_dlg->state = DLG_CONFIRMED; if(0>(headers.len=extract_allowed_headers(my_msg,1,-1,HDR_CONTENTLENGTH_F|HDR_ROUTE_F|HDR_TO_F|HDR_FROM_F|HDR_CALLID_F|HDR_CSEQ_F,headers.s,MAX_HEADER))) { LM_ERR("Unable to extract allowed headers!!\n"); goto error; } headers.s[headers.len]=0; /*let's get the body*/ if (get_body(my_msg,&body)!=0) { LM_ERR("failed to get body\n"); goto error; } if(body.len!=0){ if(!(p=pkg_malloc(body.len+1))){ LM_ERR("Out of Memory!"); goto error; } memcpy(p,body.s,body.len); body.s=p; body.s[body.len]=0; LM_DBG("Trying to construct a Sip Request with: body:%d[%.*s] headers:%d[%.*s]\n",\ body.len,body.len,body.s,headers.len,headers.len,headers.s); /*t_reply_with_body un-ref-counts the transaction, so dont use it anymore*/ } /*Now... create the UAC !! * it would be great to know the hash_index and the label that have been assigned * to our newly created cell, but t_uac does not leave any way for us to know... * only that when that transaction transitions its state (ie. a response is received, * a timeout is reached, etc...) the callback will be called with the given parameter. * * So the only way we have to know who we are, is passing as a parameter a structure with * 2 pointers: one to the app_server and the other, the identifier of the UAC (uac_id). * */ if(!(the_param=shm_malloc(sizeof(struct as_uac_param)))){ LM_ERR("out of shared memory\n"); goto error; } the_param->who=my_as; the_param->uac_id=uac_id; the_param->processor_id=processor_id; the_param->destroy_cb_set=0; shm_str_dup(&my_dlg->rem_target,&my_msg->first_line.u.request.uri); if (my_msg->route) { if (parse_rr(my_msg->route) < 0) { LM_ERR( "Error while parsing Route body\n"); goto error; } /* TODO route_set should be a shm copy of my_msg->route->parsed */ my_dlg->route_set=(rr_t*)my_msg->route->parsed; /** this SHOULD be: shm_duplicate_rr(&my_dlg->route_set,my_msg->route->parsed); * but it will last more... */ } calculate_hooks(my_dlg); if(flags & SPIRAL_FLAG){ memcpy(headers.s+headers.len,SPIRAL_HDR CRLF,SPIRAL_HDR_LEN + CRLF_LEN); headers.len+=SPIRAL_HDR_LEN+CRLF_LEN; headers.s[headers.len]=0; fake_uri.s=pkg_malloc(200); fake_uri.len=print_local_uri(the_as,processor_id,fake_uri.s,200); if(fake_uri.len<0){ LM_ERR("printing local uri\n"); goto error; } my_dlg->hooks.next_hop=&fake_uri; } my_dlg->T_flags=T_NO_AUTOACK_FLAG|T_PASS_PROVISIONAL_FLAG ; ret=seas_f.tmb.t_request_within(&(my_msg->first_line.u.request.method),&headers,&body,my_dlg,uac_cb,(void *)the_param,NULL); /** now undo all the fakes we have put in my_dlg*/ /*because my_dlg->route_set should be shm but we fake it (its pkg_mem)*/ my_dlg->route_set=(rr_t *)0; if (ret <= 0) { err_ret = err2reason_phrase(ret,&sip_error,err_buf, sizeof(err_buf), "SEAS/UAC"); LM_ERR("Error on request_within %s\n",err_buf ); if(err_ret > 0) { as_action_fail_resp(uac_id,ret,err_buf,0); }else{ as_action_fail_resp(uac_id,E_UNSPEC,"500 SEAS/UAC error",0); } goto error; } retval=0; goto exit; error: retval = -1; if(the_param) shm_free(the_param); exit: seas_f.tmb.free_dlg(my_dlg); if(headers.s) pkg_free(headers.s); if(body.s) pkg_free(body.s); if(fake_uri.s) pkg_free(fake_uri.s); if(my_msg){ if(my_msg->headers) free_hdr_field_lst(my_msg->headers); pkg_free(my_msg); } return retval; } /** * len MUST be >0 */ int print_local_uri(as_p as,char processor_id,char *where,int len) { int i; struct socket_info *si; str proto; proto.s=NULL; proto.len=0; for(i=0;ibound_processor[i]==processor_id) break; } if(i==MAX_BINDS){ LM_DBG("processor ID not found\n"); return -1; } si=as->binds[i]; switch(si->proto){ case PROTO_UDP: proto.s=""; proto.len=0; break; case PROTO_TCP: proto.s=TRANSPORT_PARAM "TCP"; proto.len=TRANSPORT_PARAM_LEN + 3; break; case PROTO_TLS: proto.s=TRANSPORT_PARAM "TLS"; proto.len=TRANSPORT_PARAM_LEN + 3; break; case PROTO_SCTP: proto.s=TRANSPORT_PARAM "SCTP"; proto.len=TRANSPORT_PARAM_LEN + 4; break; } switch(si->address.af){ case AF_INET: i=snprintf(where,len,"sip:%d.%d.%d.%d:%u%.*s",si->address.u.addr[0],si->address.u.addr[1],\ si->address.u.addr[2],si->address.u.addr[3],si->port_no,proto.len,proto.s); break; case AF_INET6: i=snprintf(where,len,"sip:[%x:%x:%x:%x:%x:%x:%x:%x]:%u%.*s", htons(si->address.u.addr16[0]), htons(si->address.u.addr16[1]),\ htons(si->address.u.addr16[2]), htons(si->address.u.addr16[3]), htons(si->address.u.addr16[4]), htons(si->address.u.addr16[5]),\ htons(si->address.u.addr16[6]), htons(si->address.u.addr16[7]),si->port_no,proto.len,proto.s); break; default: LM_ERR("address family unknown\n"); return -1; } if(i>len){ LM_ERR("Output was truncated!!\n"); return -1; }else if(i<0){ LM_ERR("Error on snprintf\n"); return i; } return i; } /* !!! COPIED FROM MODULES/TM !! * This function skips name part * uri parsed by parse_contact must be used * (the uri must not contain any leading or * trailing part and if angle bracket were * used, right angle bracket must be the * last character in the string) * * _s will be modified so it should be a tmp * copy */ void get_raw_uri(str* _s) { char* aq; if (_s->s[_s->len - 1] == '>') { aq = find_not_quoted(_s, '<'); _s->len -= aq - _s->s + 2; _s->s = aq + 1; } } /* !!! COPIED FROM MODULES/TM !! * Calculate dialog hooks * * This is copied from modules/tm/dlg.c * * Maybe a reference to the original function in TM * could be reached via handlers or whatever... */ static inline int calculate_hooks(dlg_t* _d) { str* uri; struct sip_uri puri; if (_d->route_set) { uri = &_d->route_set->nameaddr.uri; if (parse_uri(uri->s, uri->len, &puri) < 0) { LM_ERR( "Error while parsing URI\n"); return -1; } if (puri.lr.s) { if (_d->rem_target.s) _d->hooks.request_uri = &_d->rem_target; else _d->hooks.request_uri = &_d->rem_uri; _d->hooks.next_hop = &_d->route_set->nameaddr.uri; _d->hooks.first_route = _d->route_set; } else { _d->hooks.request_uri = &_d->route_set->nameaddr.uri; _d->hooks.next_hop = _d->hooks.request_uri; _d->hooks.first_route = _d->route_set->next; _d->hooks.last_route = &_d->rem_target; } } else { if (_d->rem_target.s) _d->hooks.request_uri = &_d->rem_target; else _d->hooks.request_uri = &_d->rem_uri; _d->hooks.next_hop = _d->hooks.request_uri; } if ((_d->hooks.request_uri) && (_d->hooks.request_uri->s) && (_d->hooks.request_uri->len)) { _d->hooks.ru.s = _d->hooks.request_uri->s; _d->hooks.ru.len = _d->hooks.request_uri->len; _d->hooks.request_uri = &_d->hooks.ru; get_raw_uri(_d->hooks.request_uri); } if ((_d->hooks.next_hop) && (_d->hooks.next_hop->s) && (_d->hooks.next_hop->len)) { _d->hooks.nh.s = _d->hooks.next_hop->s; _d->hooks.nh.len = _d->hooks.next_hop->len; _d->hooks.next_hop = &_d->hooks.nh; get_raw_uri(_d->hooks.next_hop); } return 0; } /** * Strips the "" topmost via headers. * Leaves only the topmost "" Record-Route headers. * */ int extract_allowed_headers(struct sip_msg *my_msg,int strip_top_vias,int allow_top_Rroutes,hdr_flags_t forbidden_hdrs,char *headers,int headers_len) { struct hdr_field *hf; rr_t *rb; struct via_body *vb; int len,k,rtcnt,i; len=0; rtcnt=allow_top_Rroutes; rb=NULL; vb=NULL; for(hf=my_msg->headers;hf;hf=hf->next){ if(forbidden_hdrs & HDR_T2F(hf->type)){ LM_DBG("Skipping header (%.*s)\n",hf->name.len,hf->name.s); continue; }else if(hf->type==HDR_VIA_T && strip_top_vias > 0){ /** All vias MUST be parsed !!*/ for(i=0,vb=hf->parsed;vb;vb=vb->next,i++); if(i<=strip_top_vias){ LM_DBG("Stripping vias [%.*s]\n",hf->len,hf->name.s); /** skip this via header*/ strip_top_vias-=i; }else{ assert(i>1); vb=hf->parsed; while(strip_top_vias--) vb=vb->next; k= (hf->name.s + hf->len) - vb->name.s; LM_DBG("Stripping vias [%.*s]\n",(int)(vb->name.s-hf->name.s), hf->name.s); if(k+VIA_LENname.s,k); len+=k; }else{ LM_ERR("Out Of Space !!\n"); goto error; } } }else if(hf->type==HDR_RECORDROUTE_T && rtcnt>=0){ if(rtcnt==0) continue; if(!hf->parsed && 0>parse_rr(hf)){ LM_ERR("parsing Record-Route:\"%.*s\"\n",hf->body.len,hf->body.s); goto error; } for(i=0,rb=hf->parsed;rb;rb=rb->next,i++); if(i<=rtcnt){ if((len+hf->len)len,hf->name.s); memcpy(headers+len,hf->name.s,hf->len); len+=hf->len; }else{ LM_ERR("Unable to keep recordroute (not enough space left in headers) Discarding \"%.*s\" \n",hf->name.len,hf->name.s); goto error; } /** is this dangerous ? because the rtcnt is the control variable for this conditional 'if' * so if I change rtcnt value in one of the statements... what then ??? */ rtcnt-=i; }else{ assert(rtcnt>0); rb=hf->parsed; while(--rtcnt) rb=rb->next; k= (((rb->nameaddr.name.s) + rb->len)-hf->name.s) ; if(len+k+CRLF_LENname.s,k); LM_DBG("Allowing RecordRoute [%.*s\r\n]\n",k,hf->name.s); len+=k; memcpy(headers+len,CRLF,CRLF_LEN); len+=CRLF_LEN; }else{ LM_ERR("Out Of Space !!\n"); goto error; } } if(hf->parsed){ free_rr((rr_t **)(void*)(&hf->parsed)); hf->parsed=NULL; } }else{ if((len+hf->len)name.s,hf->len); len+=hf->len; }else{ LM_WARN("Too many headers. Discarding \"%.*s\" \n", hf->name.len,hf->name.s); } } }/*for*/ return len; error: return -1; } /** * ERROR action responses are composed of: * 4: the length of the event * 1: the event type (AC_RES_FAIL) * 4: NBO of the uac-action-request identification (uac_id) * 4: the sip_error code in NBO. * 1: (unsigned) the length of the string. * N: the string * */ int as_action_fail_resp(int uac_id,int sip_error,char *err_buf,int i) { char msg[14+MAX_REASON_LEN]; int n,k, ev_len; k=4; if(i==0) i=strlen(err_buf); if(i>MAX_REASON_LEN){ LM_ERR("Error Reason bigger than MAX_REASON_LEN\n"); return -1; } msg[k++]=AC_RES_FAIL; uac_id=htonl(uac_id); memcpy(msg+k,&uac_id,4); k+=4; sip_error=htonl(sip_error); memcpy(msg+k,&sip_error,4); k+=4; msg[k++]=(char)(unsigned char)i; memcpy(msg+k,err_buf,i); k+=i; ev_len=htonl(k); memcpy(msg,&ev_len,4); n=write(my_as->u.as.action_fd,msg,k); if (n < 0) LM_ERR("error while writing\n"); return 0; } /* * This callback function should be used in order to free the parameters passed to uac_cb. * This callback is called when the transaction is detroyed. */ void uac_cleanup_cb(struct cell* t, int type, struct tmcb_params *rcvd_params) { struct as_uac_param *ev_info; ev_info=(struct as_uac_param*)*rcvd_params->param; if(ev_info) { shm_free(ev_info); *rcvd_params->param=NULL; } } /** * This function will be called from a SER process when a reply is received for * the transaction. The SER processes only have acces to the EventDispatcher * fifo (not to the ActionDispatcher) so EventDispatcher will be the one who * will send the event to the AppServer. * TODO WARNING !!! there's a clear MEMORY LEAK here, see exit: at the bottom of * the function... it should free ev_info !!!!!!!! * I have disabled the free() because It may be that we receive a retransmitted 200 OK * if the ACK gets lost, that 200 OK will make SER invoke this callback a second,third, etc time... * */ void uac_cb(struct cell* t, int type,struct tmcb_params *rcvd_params) { as_msg_p my_as_ev=0; int mylen,code,i; struct as_uac_param *ev_info; char *buffer; UNUSED(code); ev_info=(struct as_uac_param*)*rcvd_params->param; code=rcvd_params->code; buffer=0; if(!ev_info || !ev_info->who){ return; } if(type == TMCB_LOCAL_COMPLETED && !ev_info->destroy_cb_set) { if(seas_f.tmb.register_tmcb(NULL, t, TMCB_TRANS_DELETED, uac_cleanup_cb, (void*)ev_info, NULL) <= 0) { LM_ERR( "register_tmcb for destroy callback failed\n"); goto error; } ev_info->destroy_cb_set = 1; } LM_DBG("reply to UAC Transaction for AS:%.*s code: %d\n", ev_info->who->name.len,ev_info->who->name.s,code); LM_DBG("transaction %p Nr_of_outgoings:%d is_Local:%c\n", t,t->nr_of_outgoings,is_local(t)?'y':'n'); for(i=0;inr_of_outgoings;i++) LM_DBG("UAC[%d].last_received=%d\n",i,t->uac[i].last_received); if(!(my_as_ev=shm_malloc(sizeof(as_msg_t)))){ LM_ERR("no more shared mem\n"); goto error; } if(!(buffer=create_as_action_reply(t,rcvd_params,ev_info->uac_id,ev_info->processor_id,&mylen))){ LM_ERR("failed to encode message\n"); goto error; } my_as_ev->as = ev_info->who; my_as_ev->msg = buffer; my_as_ev->len = mylen; my_as_ev->type = RES_IN; my_as_ev->transaction = t; if(write(write_pipe,&my_as_ev,sizeof(as_msg_p))<=0){ goto error; } goto exit; error: if(my_as_ev){ shm_free(my_as_ev); } if(buffer) shm_free(buffer); exit: return ; } char* create_as_action_reply(struct cell *c,struct tmcb_params *params,int uac_id,char processor_id,int *evt_len) { int i; unsigned int code,flags; unsigned short int port; unsigned int k,len; char *buffer; struct sip_msg *msg; if(!(buffer=shm_malloc(ENCODED_MSG_SIZE))){ LM_ERR("create_as_action_reply Out Of Memory !!\n"); return 0; } msg=0; *evt_len=0; flags=0; if(params->rpl==FAKED_REPLY) flags=FAKED_REPLY_FLAG; /*length*/ k=4; /*type*/ buffer[k++]=(unsigned char)RES_IN; /*processor id*/ buffer[k++]=processor_id; /*flags (by now, not used)*/ flags=htonl(flags); memcpy(buffer+k,&flags,4); k+=4; /*recv info*/ if(!(params->rpl == FAKED_REPLY)) { msg=params->rpl; /*protocol should be UDP,TCP,TLS or whatever*/ buffer[k++]=(unsigned char)msg->rcv.proto; /*src ip len + src ip*/ len=msg->rcv.src_ip.len; buffer[k++]=(unsigned char)len; memcpy(buffer+k,&(msg->rcv.src_ip.u),len); k+=len; /*dst ip len + dst ip*/ len=msg->rcv.dst_ip.len; buffer[k++]=(unsigned char)len; memcpy(buffer+k,&(msg->rcv.dst_ip.u),len); k+=len; /*src port */ port=htons(msg->rcv.src_port); memcpy(buffer+k,&port,2); k+=2; /*dst port */ port=htons(msg->rcv.dst_port); memcpy(buffer+k,&port,2); k+=2; }else{ /*protocol*/ buffer[k++]=0; /*src ip len*/ buffer[k++]=0; /*dst ip len*/ buffer[k++]=0; /*skip src port and dst port*/ buffer[k++]=0; buffer[k++]=0; buffer[k++]=0; buffer[k++]=0; } /*hash_index*/ i=htonl(c->hash_index); memcpy(buffer+k,&i,4); k+=4; /*label*/ i=(!strncmp(c->method.s,"CANCEL",6)) ? \ htonl(((struct as_uac_param*)*params->param)->label) : \ htonl(c->label); memcpy(buffer+k,&i,4); k+=4; /*uac_id*/ uac_id=htonl(uac_id); memcpy(buffer+k,&uac_id,4); k+=4; /*code*/ code=htonl(params->code); memcpy(buffer+k,&code,4); k+=4; /*length of event (hdr+payload-4), copied at the beginning*/ if(params->rpl != FAKED_REPLY) { if((i=encode_msg(msg,buffer+k,ENCODED_MSG_SIZE-k))<0){ LM_ERR("failed to encode msg\n"); goto error; } k+=i; } *evt_len=k; k=htonl(k); memcpy(buffer,&k,4); return buffer; error: return 0; } opensips-2.2.2/modules/seas/seas_action.h000066400000000000000000000043101300170765700204250ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "seas.h"/*as_p*/ #define REPLY_PROV 1 #define REPLY_FIN 2 #define REPLY_FIN_DLG 3 #define UAC_REQ 4 #define AC_RES_FAIL 5 #define SL_MSG 6 #define AC_CANCEL 7 #define JAIN_PONG 8 #define AC_FAIL_UNKNOWN 0x01 #define FAKED_REPLY_FLAG 0x02 struct as_uac_param{ struct as_entry *who; int uac_id; unsigned int label; char processor_id; char destroy_cb_set; }; /** * ACTION processing functions */ int ac_reply(as_p the_as,char *action,int len); int ac_sl_msg(as_p the_as,char *action,int len); int ac_uac_req(as_p the_as,char *action,int len); int ac_encode_msg(as_p the_as,char *action,int len); int ac_cancel(as_p the_as,char *action,int len); /** * Utility functions */ int forward_sl_request(struct sip_msg *msg,struct proxy_l *proxy,int proto); int extract_allowed_headers(struct sip_msg *my_msg,int allow_vias,int allow_Rroutes,hdr_flags_t forbidden_hdrs,char *headers,int headers_len); /** * Action Dispatcher process functions */ int dispatch_actions(); int process_action(as_p my_as); /** * Callback Functions */ void uac_cb(struct cell* t, int type, struct tmcb_params*); void uac_cleanup_cb(struct cell* t, int type, struct tmcb_params*); /** * Event creating functions */ int as_action_fail_resp(int uac_id,int sip_error,char *err_buf,int err_len); char* create_as_action_reply(struct cell *c,struct tmcb_params *params,int uac_id,char processor_id,int *evt_len); opensips-2.2.2/modules/seas/seas_error.h000066400000000000000000000021701300170765700203030ustar00rootroot00000000000000/** * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../error.h" /** opensips ERRORS ARE NEGATIVE, SEAS ERROR CODES ARE POSITIVE */ #define SE_CANCEL_MSG "500 SEAS cancel error" #define SE_CANCEL_MSG_LEN (sizeof(SE_CANCEL_MSG)-1) #define SE_CANCEL 1 #define SE_UAC_MSG "500 SEAS uac error" #define SE_UAC_MSG_LEN (sizeof(SE_UAC_MSG)-1) #define SE_UAC 2 opensips-2.2.2/modules/seas/statistics.c000066400000000000000000000274641300170765700203410ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include /* superset of previous */ #include #include #include #include #include "statistics.h" #include "seas.h" /*SLOG*/ #include "../../mem/shm_mem.h" #include "../../resolve.h" #include "../../ut.h" #include "../../dprint.h" #include "../../locking.h" #define STATS_PAY 101 struct statstable* seas_stats_table; int stats_fd; char use_stats=0; pid_t pid; static void sig_handler(int signo) { switch(signo){ case SIGTERM: LM_ERR("stats process caught SIGTERM, shutting down..\n"); close(stats_fd); destroy_seas_stats_table(); exit(0); default: LM_DBG("caught signal %d\n",signo); } LM_WARN("statistics process:caught signal (%d)\n",signo); } struct statstable* init_seas_stats_table(void) { /*allocs the table*/ seas_stats_table= (struct statstable*)shm_malloc( sizeof( struct statstable ) ); if (!seas_stats_table) { LM_ERR("no shmem for stats table (%d bytes)\n",(int)sizeof(struct statstable)); return 0; } memset(seas_stats_table, 0, sizeof(struct statstable) ); if(0==(seas_stats_table->mutex=lock_alloc())){ LM_ERR("couldn't alloc mutex (get_lock_t)\n"); shm_free(seas_stats_table); return 0; } lock_init(seas_stats_table->mutex); return seas_stats_table; } /** This will be called from within w_as_relay() * * TODO handle locking ? */ void as_relay_stat(struct cell *t) { struct statscell *s; struct totag_elem *to; if(t==0) return; if(t->fwded_totags != 0){ LM_DBG("seas:as_relay_stat() unable to put a payload " "in fwded_totags because it is being used !!\n"); return; } if(!(s=shm_malloc(sizeof(struct statscell)))){ return; } if(!(to=shm_malloc(sizeof(struct totag_elem)))){ shm_free(s); return; } memset(s,0,sizeof(struct statscell)); gettimeofday(&(s->u.uas.as_relay),NULL); s->type=UAS_T; to->tag.len=0; to->tag.s=(char *)s; to->next=0; to->acked=STATS_PAY; t->fwded_totags=to; lock_get(seas_stats_table->mutex); (seas_stats_table->started_transactions)++; lock_release(seas_stats_table->mutex); } /** this will be called from the SEAS event dispatcher * when it writes the event to the socket * * Parameters: a cell OR its hash_index and its label * * TODO handle locking/mutexing ? */ void event_stat(struct cell *t) { struct statscell *s; struct totag_elem *to; if(t==0){ /*seas_f.tmb.t_lookup_ident(&t,hash_index,label); BAD bcos it refcounts, * and there's no way to simply unrefcount from outside TM*/ return; } if(t->fwded_totags == 0){ LM_DBG("seas:event_stat() unabe to set the event_stat timeval:" " no payload found at cell!! (fwded_totags=0)\n"); return; } /*esto da un CORE DUMP cuando hay mucha carga.. warning*/ to=t->fwded_totags; while(to){ if(to->acked==STATS_PAY){ s=(struct statscell *)to->tag.s; gettimeofday(&(s->u.uas.event_sent),NULL); return; }else to=to->next; } return; } /** param i is in milliseconds*/ static inline int assignIndex(int i) { return (i/100)>14?14:(i/100); } /** this will be called from the SEAS action dispatcher * when it receives the action from the socket */ void action_stat(struct cell *t) { unsigned int seas_dispatch/*,as_delay*/; struct timeval *t1,*t2/*,*t3*/; struct statscell *s; struct totag_elem *to; if(t==0) return; if(t->fwded_totags == 0){ LM_DBG("seas:event_stat() unable to set the event_stat timeval:" " no payload found at cell!! (fwded_totags=0)\n"); return; } to=t->fwded_totags; while(to){ if(to->acked==STATS_PAY){ s=(struct statscell *)to->tag.s; gettimeofday(&(s->u.uas.action_recvd),NULL); break; }else to=to->next; } /**no statistics found**/ if(to==0) return; t1=&(s->u.uas.as_relay); t2=&(s->u.uas.event_sent); /* t3=&(s->u.uas.action_recvd); */ seas_dispatch = (t2->tv_sec - t1->tv_sec)*1000 + (t2->tv_usec-t1->tv_usec)/1000; /* as_delay = (t3->tv_sec - t2->tv_sec)*1000 + (t3->tv_usec-t2->tv_usec)/1000; */ lock_get(seas_stats_table->mutex); { seas_stats_table->dispatch[assignIndex(seas_dispatch)]++; seas_stats_table->event[assignIndex(seas_dispatch)]++; (seas_stats_table->finished_transactions)++; } lock_release(seas_stats_table->mutex); } /** * stats socket sould be an IP_address:port or unix://path/to_file * TODO handling unix sockets and IPv6 !! * * returns * 0 if no stats * 1 if stats properly started * -1 if error */ int start_stats_server(char *stats_socket) { char *p,*port; unsigned short stats_port; struct hostent *he; /*use sockaddr_storage ??*/ struct sockaddr_in su; int optval; use_stats=0; port=(char *)0; he=(struct hostent *)0; stats_fd=-1; p=stats_socket; if(p==0 || *p==0) return 0; if(!init_seas_stats_table()){ LM_ERR("unable to init stats table, disabling statistics\n"); return -1; } while(*p){ if(*p == ':'){ *p=0; port=p+1; break; } } if(!(he=resolvehost(stats_socket,0))) goto error; if(port==(char*)0 || *port==0) stats_port=5088; else if(!(stats_port=str2s(port,strlen(port),0))){ LM_ERR("invalid port %s\n",port); goto error; } if((stats_fd=socket(he->h_addrtype, SOCK_STREAM, 0))==-1){ LM_ERR("trying to open server socket (%s)\n",strerror(errno)); goto error; } optval=1; if (setsockopt(stats_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval))==-1) { LM_ERR("setsockopt (%s)\n",strerror(errno)); goto error; } su.sin_family = he->h_addrtype; su.sin_port=htons(stats_port); memcpy(&su.sin_addr,he->h_addr_list[0],4); if((bind(stats_fd,(struct sockaddr*)&su,sizeof(struct sockaddr_in)))==-1){ LM_ERR( "bind (%s)\n",strerror(errno)); goto error; } if(listen(stats_fd, 10)==-1){ LM_ERR( "listen (%s)\n",strerror(errno)); goto error; } if(!(pid=fork())){/*child*/ signal(SIGTERM,sig_handler); serve_stats(stats_fd); printf("statistics Server Process exits !!\n"); exit(0); }else if(pid>0){/*parent*/ close(stats_fd); }else{/*error*/ LM_ERR("failed to create stats server process\n"); goto error; } use_stats=1; return 1; error: if(stats_fd!=-1) close(stats_fd); destroy_seas_stats_table(); return -1; } /** * stats socket sould be an IP_address:port or unix://path/to_file * TODO handling unix sockets and IPv6 !! * * returns * 0 if no stats * 1 if stats properly started * -1 if error */ int stop_stats_server(void) { if(pid) kill(SIGTERM,pid); return 0; } void serve_stats(int fd) { union sockaddr_union su; int sock,i,retrn; socklen_t su_len; char f; /* we install our signal handler..*/ signal(SIGTERM,sig_handler); signal(SIGHUP,sig_handler); signal(SIGPIPE,sig_handler); signal(SIGQUIT,sig_handler); signal(SIGINT,sig_handler); signal(SIGCHLD,sig_handler); while(1){ su_len = sizeof(union sockaddr_union); sock=-1; sock=accept(fd, &su.s, &su_len); if(sock==-1){ if(errno==EINTR){ continue; }else{ LM_ERR("failed to accept connection: %s\n", strerror(errno)); return ; } } while(0!=(i=read(sock,&f,1))){ if(i==-1){ if(errno==EINTR){ continue; }else{ LM_ERR("unknown error reading from socket\n"); close(sock); /** and continue accept()'ing*/ break; } } retrn=print_stats_info(f,sock); if(retrn==-1){ /**simple error happened, dont worry*/ LM_ERR("printing statisticss \n"); continue; }else if(retrn==-2){ /**let's go to the outer loop, and receive more Statistics clients*/ LM_ERR("statistics client left\n"); close(sock); break; } } } } /** * (from snprintf manual) * "The functions snprintf() and vsnprintf() do not write more than size bytes (including the trailing '\\0'). If the output was truncated due to * this limit then the return value is the number of characters (not including the trailing '\\0') which would have been written to the final string * if enough space had been available. Thus, a return value of size or more means that the output was truncated." */ int print_stats_info(int f,int sock) { #define STATS_BUF_SIZE 400 int j,k,writen; char buf[STATS_BUF_SIZE]; writen=0; if(0>(k=snprintf(buf,STATS_BUF_SIZE, "Timings: 0-1 1-2 2-3 3-4 4-5 5-6 6-7 7-8 8-9 9-10 10-11 11-12 12-13 13-14 14+\n"))){ goto error; }else{ if(k>STATS_BUF_SIZE){ j=STATS_BUF_SIZE; goto send; } j=k; } lock_get(seas_stats_table->mutex); if(0>(k=snprintf(&buf[j],STATS_BUF_SIZE-j,"UAS:dispatch: %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d\n",\ seas_stats_table->dispatch[0],seas_stats_table->dispatch[1],seas_stats_table->dispatch[2],seas_stats_table->dispatch[3],seas_stats_table->dispatch[4]\ ,seas_stats_table->dispatch[5],seas_stats_table->dispatch[6],seas_stats_table->dispatch[7],seas_stats_table->dispatch[8],seas_stats_table->dispatch[9],\ seas_stats_table->dispatch[10],seas_stats_table->dispatch[11],seas_stats_table->dispatch[12],seas_stats_table->dispatch[13],seas_stats_table->dispatch[14]))){ goto error; }else{ if(k>(STATS_BUF_SIZE-j)){ j=STATS_BUF_SIZE; goto send; } j+=k; } if(0>(k=snprintf(&buf[j],STATS_BUF_SIZE-j,"UAS:event: %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d\n",\ seas_stats_table->event[0],seas_stats_table->event[1],seas_stats_table->event[2],seas_stats_table->event[3],seas_stats_table->event[4]\ ,seas_stats_table->event[5],seas_stats_table->event[6],seas_stats_table->event[7],seas_stats_table->event[8],seas_stats_table->event[9],\ seas_stats_table->event[10],seas_stats_table->event[11],seas_stats_table->event[12],seas_stats_table->event[13],seas_stats_table->event[14]))){ goto error; }else{ if(k>STATS_BUF_SIZE-j){ j=STATS_BUF_SIZE; goto send; } j+=k; } if(0>(k=snprintf(&buf[j],STATS_BUF_SIZE-j,"Started Transactions: %d\nTerminated Transactions:%d\nReceived replies:%d\nReceived:%d\n",\ seas_stats_table->started_transactions,seas_stats_table->finished_transactions,seas_stats_table->received_replies,seas_stats_table->received))){ goto error; }else{ if(k>STATS_BUF_SIZE-j){ j=STATS_BUF_SIZE; goto send; } j+=k; } send: lock_release(seas_stats_table->mutex); again:/*mutex is released*/ k=write(sock,buf,j); if(k<0){ switch(errno){ case EINTR: goto again; case EPIPE: return -2; } } writen+=k; if(writenmutex); return -1; } opensips-2.2.2/modules/seas/statistics.h000066400000000000000000000060071300170765700203340ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../../hash_func.h"/* for TABLE_ENTRIES define*/ #include "../../locking.h"/* for TABLE_ENTRIES define*/ #include "../tm/h_table.h"/* for struct cell*/ #define STATS_CELLS 50 #define UAS_T 0 #define UAC_T 1 /** * stores statistics about a given APP SERVER, * for instance, how much it lasted to respond to * a given incoming request transaction, how many requests went in * and how many responses went out, etc. * this should be about... 16*4+20*4+4 bytes...64+80+4=148 bytes each cell */ struct statscell { /** 0 = UAS, 1 = UAC*/ char type; /**difference between a request_event and a reply_action*/ union { struct { struct timeval as_relay; struct timeval event_sent; struct timeval action_recvd; } uas; struct { struct timeval action_recvd; struct timeval event_sent; struct timeval action_reply_sent; } uac; }u; }; /** Transactions statistics table */ struct statstable { gen_lock_t *mutex; unsigned int dispatch[15]; unsigned int event[15]; unsigned int action[15]; unsigned int started_transactions; unsigned int finished_transactions; unsigned int received_replies; unsigned int received; }; extern struct statstable *seas_stats_table; /** * Initialize and destroy statistics table */ struct statstable* init_seas_stats_table(); int stop_stats_server(); static inline void destroy_seas_stats_table(void) { /*deallocs the table*/ if(seas_stats_table){ lock_destroy(seas_stats_table->mutex); shm_free(seas_stats_table); seas_stats_table=(struct statstable *)0; } } /** Statistics server process * functions */ void serve_stats(int fd); int start_stats_server(char *socket); int print_stats_info(int f,int sock); /** * Statistics functions */ void as_relay_stat(struct cell *t); void event_stat(struct cell *t); void action_stat(struct cell *t); static inline void stats_reply(void) { lock_get(seas_stats_table->mutex); seas_stats_table->received_replies++; lock_release(seas_stats_table->mutex); } #define receivedplus() \ do{ \ lock_get(seas_stats_table->mutex); \ seas_stats_table->received++; \ lock_release(seas_stats_table->mutex); \ }while(0) opensips-2.2.2/modules/seas/utils.c000066400000000000000000000112121300170765700172670ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * ===================================================================================== * * Filename: utils.c * * Description: * * Version: 1.0 * Created: 19/01/06 15:50:33 CET * Revision: none * Compiler: gcc * * Author: Elias Baixas (EB), elias@conillera.net * Company: VozTele.com * * ===================================================================================== */ #include #include #include #include #include #define _GNU_SOURCE #include #include #include #include "../../parser/msg_parser.h" #include "../../parser/parse_via.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "encode_header.h" #include "encode_msg.h" #include "utils.h" #define MAX_ERROR 32 static inline int memstr(char *haystack,int hlen,char *needle,int nlen); int buffered_printer(int infd) { int i,k=0,retval; char *missatge=0,*myerror=""; struct sip_msg msg; static char mybuffer[1400]; static int end=0,last=0; while((i=read(infd,&mybuffer[last],1400-last))==1400-last){ if((end=memstr(mybuffer,last+i,"\n\n\n",3))<0){ last+=i; return 0; }else{ end+=3; while(end<1400 && (mybuffer[end]=='\n' || mybuffer[end]=='.' || mybuffer[end]=='\r')) end++; if((missatge=pkg_malloc(end))==0){ myerror="Out of memory !!\n"; goto error; } memset(missatge,0,end); memcpy(missatge,mybuffer,end); memset(&msg,0,sizeof(struct sip_msg)); msg.buf=missatge; msg.len=end; if(!parse_msg(msg.buf,msg.len,&msg)) print_msg_info(1,&msg); printf("PARSED:%d,last=%d,end=%d\n",k++,last,end); free_sip_msg(&msg); pkg_free(missatge); memmove(mybuffer,&mybuffer[end],1400-end); last=1400-end; } } retval=0; goto exit; error: printf("Error on %s",myerror); retval=1; exit: if(missatge) pkg_free(missatge); return retval; } int coded_buffered_printer(int infd) { int i,lastlast; char spaces[50]; static char mybuffer[1500]; static int size=0,last=0; memcpy(spaces," ",2); do{ lastlast=1500-last; i=read(infd,&mybuffer[last],lastlast); printf("read i=%d\n",i); if(i==0) break; if(size==0){ size=GET_PAY_SIZE(mybuffer); printf("size=%d\n",size); last+=i; } if(last>=size){ printf("should print message: last=%d, size=%d\n",last,size); if(print_encoded_msg(1,mybuffer,spaces)<0){ printf("Unable to print encoded msg\n"); return -1; } if(last>size){ memmove(mybuffer,&mybuffer[size],last-size); last=last-size; }else last=0; size=0; } }while(i>0 && i==lastlast); if(i==0) return 0; else return 1; } int print_msg_info(int fd,struct sip_msg* msg) { char *payload=0; char *prefix=0; int retval=-1; if((prefix=pkg_malloc(500))==0){ printf("OUT OF MEMORY !!!\n"); return -1; } memset(prefix,0,500); strcpy(prefix," "); if(parse_headers(msg,HDR_EOH_F,0)<0) goto error; if(!(payload=pkg_malloc(MAX_ENCODED_MSG + MAX_MESSAGE_LEN))) goto error; if(encode_msg(msg,payload,MAX_ENCODED_MSG + MAX_MESSAGE_LEN)<0){ printf("Unable to encode msg\n"); goto error; } if(print_encoded_msg(fd,payload,prefix)<0){ printf("Unable to print encoded msg\n"); pkg_free(payload); goto error; } pkg_free(payload); retval =0; error: if(prefix) pkg_free(prefix); return retval; } static inline int memstr(char *haystack,int hlen,char *needle,int nlen) { int i=0; if(nlen>hlen) return -1; while(i<=(hlen-nlen) && (haystack[i]!=needle[0] || memcmp(&haystack[i],needle,nlen))) i++; if(i>(hlen-nlen)) return -1; else return i; } opensips-2.2.2/modules/seas/utils.h000066400000000000000000000017261300170765700173050ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../parser/msg_parser.h" int buffered_printer(int infd); int print_msg_info(int fd,struct sip_msg* msg); int coded_buffered_printer(int infd); opensips-2.2.2/modules/seas/xaddress.h000066400000000000000000000016311300170765700177550ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 VozTelecom Sistemas S.L * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define ONLY_URIS 0x01 #define SEGREGATE 0x02 #define ALSO_RURI 0x04 #define JUNIT 0x08 opensips-2.2.2/modules/signaling/000077500000000000000000000000001300170765700170065ustar00rootroot00000000000000opensips-2.2.2/modules/signaling/Makefile000066400000000000000000000003251300170765700204460ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=signaling.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/signaling/README000066400000000000000000000057141300170765700176750ustar00rootroot00000000000000signaling Module Anca-Maria Vamanu Edited by Anca-Maria Vamanu Copyright © 2008 FhG FOKUS Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.4. Exported Functions 1.4.1. send_reply(code, reason) List of Examples 1.1. sl_send_reply usage Chapter 1. Admin Guide 1.1. Overview The SIGNALING module comes as a wrapper over tm and sl modules and offers one function to be called by the modules that want to send a reply. The logic behind the module is to first search if a transaction is created and if so, send a state full reply, using tm module, otherwise send a stateless reply with the function exported by sl. In this way, the script writer still has the call on how the transaction should be handled, state full or stateless and the reply is send accordingly to his choice. For example, if you do a t_newtran() in the script before doing save() (for registration), the function will automatically send the reply in stateful mode as a transaction is available. If no transaction is done, the reply will be sent in stateless way (as now). By doing this, we have the possibility to have same module sending either stateful either stateless replies, by just controlling this from the script (if we create or not a transaction). So, the signalling will be more coherent as the replies will be sent according to the transaction presence (or not). Moreover, this module offers the possibility of loading only one of the module, sl or tm, and send reply using only the module that is loaded. This is useful as not in all cases a user desires to send stateful or stateless replies and he should not be forced to load the module only because the send reply interface requires it. 1.2. Dependencies 1.2.1. OpenSIPS Modules At least one of the following modules must be loaded before this module: * sl. * tm. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters * None. 1.4. Exported Functions 1.4.1. send_reply(code, reason) For the current request, a reply is sent back having the given code and text reason. The reply is sent stateless or statefull depending on which module is loaded and if a transaction was created, as explained above. Meaning of the parameters is as follows: * code - Return code. * reason - Reason phrase. This function can be used from REQUEST_ROUTE, ERROR_ROUTE. Example 1.1. sl_send_reply usage ... send_reply("404", "Not found"); ... send_reply("$err.rcode", "$err.rreason"); ... opensips-2.2.2/modules/signaling/doc/000077500000000000000000000000001300170765700175535ustar00rootroot00000000000000opensips-2.2.2/modules/signaling/doc/signaling.xml000066400000000000000000000017361300170765700222570ustar00rootroot00000000000000 %docentities; ]> signaling Module &osipsname; Anca-Maria Vamanu Anca-Maria Vamanu 2008 &fhg; $Revision: 5901 $ $Date$ &admin; &faq; opensips-2.2.2/modules/signaling/doc/signaling_admin.xml000066400000000000000000000070201300170765700234170ustar00rootroot00000000000000 &adminguide;
Overview The SIGNALING module comes as a wrapper over tm and sl modules and offers one function to be called by the modules that want to send a reply. The logic behind the module is to first search if a transaction is created and if so, send a state full reply, using tm module, otherwise send a stateless reply with the function exported by sl. In this way, the script writer still has the call on how the transaction should be handled, state full or stateless and the reply is send accordingly to his choice. For example, if you do a t_newtran() in the script before doing save() (for registration), the function will automatically send the reply in stateful mode as a transaction is available. If no transaction is done, the reply will be sent in stateless way (as now). By doing this, we have the possibility to have same module sending either stateful either stateless replies, by just controlling this from the script (if we create or not a transaction). So, the signalling will be more coherent as the replies will be sent according to the transaction presence (or not). Moreover, this module offers the possibility of loading only one of the module, sl or tm, and send reply using only the module that is loaded. This is useful as not in all cases a user desires to send stateful or stateless replies and he should not be forced to load the module only because the send reply interface requires it.
Dependencies
&osips; Modules At least one of the following modules must be loaded before this module: sl. tm.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters None.
Exported Functions
<function moreinfo="none">send_reply(code, reason)</function> For the current request, a reply is sent back having the given code and text reason. The reply is sent stateless or statefull depending on which module is loaded and if a transaction was created, as explained above. Meaning of the parameters is as follows: code - Return code. reason - Reason phrase. This function can be used from REQUEST_ROUTE, ERROR_ROUTE. <function>sl_send_reply</function> usage ... send_reply("404", "Not found"); ... send_reply("$err.rcode", "$err.rreason"); ...
opensips-2.2.2/modules/signaling/signaling.c000066400000000000000000000142371300170765700211340ustar00rootroot00000000000000/* * signaling module - interface for sending sip messages * * Copyright (C) 2008 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-11-5 initial version (Anca Vamanu) */ #include #include #include #include "../../sr_module.h" #include "../tm/tm_load.h" #include "../sl/sl_api.h" #include "signaling.h" /** global variables*/ /* TM bind */ struct tm_binds tmb; /* SL bind */ struct sl_binds slb; int sl_loaded = 0; int tm_loaded = 0; int sig_send_reply(struct sip_msg* msg, char* str1, char* str2); int sig_send_reply_mod(struct sip_msg* msg, int code, str* reason, str* to_tag); static int fixup_sig_send_reply(void** param, int param_no); static int mod_init(void); /** exported commands */ static cmd_export_t cmds[]= { {"send_reply",(cmd_function)sig_send_reply, 2, fixup_sig_send_reply, 0, REQUEST_ROUTE | ERROR_ROUTE | FAILURE_ROUTE}, {"load_sig", (cmd_function)load_sig, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_SILENT }, { MOD_TYPE_DEFAULT, "sl", DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** module exports */ struct module_exports exports= { "signaling", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ 0, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) 0, /* destroy function */ 0 /* per-child init function */ }; /** * init module function */ static int mod_init(void) { load_tm_f load_tm; load_sl_f load_sl; LM_NOTICE("initializing module ...\n"); /* load TM API*/ if ( (load_tm=(load_tm_f)find_export("load_tm", 0, 0))) { if (load_tm( &tmb )==-1) { LM_ERR("failed to load tm api\n"); return -1; } tm_loaded = 1; } /* load SL API */ if ((load_sl=(load_sl_f)find_export("load_sl", 0, 0))) { if (load_sl( &slb )==-1) { LM_ERR("failed to load sl api although sl module is loaded\n"); return -1; } sl_loaded = 1; } if(!tm_loaded && !sl_loaded) { LM_ERR("neither 'tm' nor 'sl' module loaded! Sipreply module requires" " loading at least one of these two\n"); return -1; } return 0; } /* * sig_send_reply - function to be called from script to send appropiate * replies (statefull or stateless) * */ int sig_send_reply(struct sip_msg* msg, char* str1, char* str2) { str code_s; unsigned int code_i; if(((pv_elem_p)str1)->spec.getf!=NULL) { if(pv_printf_s(msg, (pv_elem_p)str1, &code_s)!=0) return -1; if(str2int(&code_s, &code_i)!=0 || code_i<100 || code_i>699) return -1; } else { code_i = ((pv_elem_p)str1)->spec.pvp.pvn.u.isname.name.n; } if(((pv_elem_p)str2)->spec.getf!=NULL) { if(pv_printf_s(msg, (pv_elem_p)str2, &code_s)!=0 || code_s.len <=0) return -1; } else { code_s = ((pv_elem_p)str2)->text; } return sig_send_reply_mod(msg, code_i, &code_s, 0); } /* * sig_send_reply_mod function - sends stateless or staefull reply depending on * whether a transaction was created and on which modules are loaded( tm, sl). * */ int sig_send_reply_mod(struct sip_msg* msg, int code, str* reason, str* to_tag) { struct cell * t; if(reason== NULL || reason->s== NULL) { LM_ERR("empty reason parameter\n"); return -1; } /* search transaction */ if(tm_loaded) { t = tmb.t_gett(); if(t== NULL || t==T_UNDEFINED) { if(!sl_loaded) { LM_ERR("sl module not loaded and no transaction found for the" " message. Can not send reply!\n"); return -1; } goto sl_reply; } if( tmb.t_reply(msg, code, reason)< 0) { LM_ERR("failed to send reply with tm module\n"); return -1; } if(to_tag) *to_tag = t->uas.local_totag; return 1; } sl_reply: if(slb.reply(msg, code, reason)< 0) { LM_ERR("failed to send reply with sl module\n"); return -1; } if(to_tag) { if(slb.get_totag(msg, to_tag)< 0) { LM_ERR("failed to get to_tag from sl\n"); return -1; } } return 1; } /* * * fixup_sig_send_reply */ static int fixup_sig_send_reply(void** param, int param_no) { pv_elem_t *model=NULL; str s; /* convert to str */ s.s = (char*)*param; s.len = strlen(s.s); model=NULL; if (param_no==1 || param_no==2) { if(s.len==0) { LM_ERR("no param %d!\n", param_no); return E_UNSPEC; } if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for param no %d!\n", s.s, param_no); return E_UNSPEC; } if(model->spec.getf==NULL) { if(param_no==1) { if(str2int(&s, (unsigned int*)&model->spec.pvp.pvn.u.isname.name.n)!=0 || model->spec.pvp.pvn.u.isname.name.n<100 || model->spec.pvp.pvn.u.isname.name.n>699) { LM_ERR("wrong value [%s] for param no %d!\n", s.s, param_no); LM_ERR("allowed values: 1xx - 6xx only!\n"); return E_UNSPEC; } } } *param = (void*)model; } return 0; } int load_sig( struct sig_binds *sigb) { if(sigb==NULL) return -1; sigb->reply = sig_send_reply_mod; return 1; } opensips-2.2.2/modules/signaling/signaling.h000066400000000000000000000031461300170765700211360ustar00rootroot00000000000000/* * signaling module - interface for sending sip messages * * Copyright (C) 2008 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2008-11-5 initial version (Anca Vamanu) */ #ifndef _SIG_H_ #define _SIG_H_ #include "../../sr_module.h" #include "../../parser/msg_parser.h" typedef int (*sig_send_reply_f)(struct sip_msg *msg, int code, str *reason, str *tag); struct sig_binds { sig_send_reply_f reply; }; int load_sig( struct sig_binds *sigb); typedef int (*load_sig_f) ( struct sig_binds *sigb); static inline int load_sig_api( struct sig_binds *sigb) { load_sig_f load_sig; /* import the SL auto-loading function */ if ( !(load_sig=(load_sig_f)find_export("load_sig", 1, 0))) { LM_ERR("can't import load_sig\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_sig( sigb )==-1) return -1; return 0; } #endif opensips-2.2.2/modules/sipcapture/000077500000000000000000000000001300170765700172125ustar00rootroot00000000000000opensips-2.2.2/modules/sipcapture/Makefile000066400000000000000000000003311300170765700206470ustar00rootroot00000000000000# $Id$ # # sipcapture module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=sipcapture.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/sipcapture/README000066400000000000000000000531321300170765700200760ustar00rootroot00000000000000SipCapture Module Alexandr Dubovikov Edited by Alexandr Dubovikov Copyright © 2011 QSC AG Copyright © 2011 http://www.qsc.de __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Parameters 1.3.1. db_url (str) 1.3.2. table_name (str) 1.3.3. rtcp_table_name (str) 1.3.4. capture_on (integer) 1.3.5. hep_capture_on (integer) 1.3.6. max_async_queries (integer) 1.3.7. raw_ipip_capture_on (integer) 1.3.8. raw_moni_capture_on (integer) 1.3.9. raw_socket_listen (string) 1.3.10. raw_interface (string) 1.3.11. raw_sock_children (integer) 1.3.12. promiscuous_on (integer) 1.3.13. raw_moni_bpf_on (integer) 1.3.14. capture_node (str) 1.3.15. hep_route (string) 1.4. Exported Functions 1.4.1. sip_capture([table_name]) 1.4.2. report_capture([table_name],correlation_id[,pr oto_type]) 1.4.3. hep_set([data_type,] chunk_id, [vendor_id,] chunk_data) 1.4.4. hep_get([data_type,] chunk_id, vendor_id_pv, chunk_data_pv) 1.4.5. hep_del(chunk_id) 1.4.6. hep_relay() 1.4.7. hep_resume_sip() 1.5. Exported Async Functions 1.5.1. sip_capture() 1.6. Exported PseudoVariables 1.6.1. hep_net 1.6.2. HEPVERSION - string,int 1.7. MI Commands 1.7.1. sip_capture 1.8. Database setup 1.9. Limitation List of Examples 1.1. Set db_url parameter 1.2. Set table_name parameter 1.3. Set rtcp_capture parameter 1.4. Set capture_on parameter 1.5. Set hep_capture_on parameter 1.6. Set max_async_queries parameter 1.7. Set raw_ipip_capture_on parameter 1.8. Set raw_moni_capture_on parameter 1.9. Set raw_socket_listen parameter 1.10. Set raw_socket_listen parameter 1.11. Set raw_socket_listen parameter 1.12. Set promiscuous_on parameter 1.13. Set raw_moni_bpf_on parameter 1.14. Set capture_node parameter 1.15. Set hep_route parameter 1.16. sip_capture usage 1.17. sip_capture usage 1.18. hep_set usage 1.19. hep_set usage 1.20. hep_set usage 1.21. hep_relay usage 1.22. hep_resume_sip usage 1.23. sip_capture usage 1.24. hep_net usage 1.25. HEPVERSION usage Chapter 1. Admin Guide 1.1. Overview Offer a possibility to store incoming/outgoing SIP messages in database. OpenSIPs can capture SIP messages in three mode * IPIP encapsulation. (ETHHDR+IPHDR+IPHDR+UDPHDR). * Monitoring/mirroring port. * Homer encapsulation protocl mode (HEP v1/2/3). With version 2.2 comes the new HEPv3 support using the proto _hep module. Also header manipulation support for HEPv3 has been added. See Section 1.4.3, “ hep_set([data_type,] chunk_id, [vendor_id,] chunk_data) †for more details. If you want more information about hep protocol check this link. The capturing can be turned on/off using fifo commad. opensipsctl fifo sip_capture on opensipsctl fifo sip_capture off 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * database module - mysql, postrgress, dbtext, unixodbc... * proto_hep module - if hep capturing used 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Parameters 1.3.1. db_url (str) Database URL. Default value is "". Example 1.1. Set db_url parameter ... modparam("sipcapture", "db_url", "mysql://user:passwd@host/dbname") ... 1.3.2. table_name (str) Name of the table's name where to store the SIP messages. Since version 2.2 it allows strftime-like suffix for having time formatted table names. Default value is "sip_capture". Example 1.2. Set table_name parameter ... modparam("sipcapture", "table_name", "homer_capture") /* change table name every day */ modparam("sipcapture", "table_name", "homer_%m_%d") /* if today is 13-04-2014 it will exetend to homer_04_13 */ ... 1.3.3. rtcp_table_name (str) Name of the table's name where to store packets captured with report_capture function. Since version 2.2 it allows strftime-like suffix for having time formatted table names. Default value is "rtcp_capture". Example 1.3. Set rtcp_capture parameter ... modparam("sipcapture", "rtcp_table_name", "homer_capture") /* change table name every hour */ modparam("sipcapture", "rtcp_table_name", "homer_%m_%d_%H") /* if today is 13-04-2014 13:05 pm it will exetend to homer_04_13_13 */ ... 1.3.4. capture_on (integer) Parameter to enable/disable capture globaly (on(1)/off(0)) Default value is "0". Example 1.4. Set capture_on parameter ... modparam("sipcapture", "capture_on", 1) ... 1.3.5. hep_capture_on (integer) Parameter to enable/disable capture of HEP (on(1)/off(0)) Default value is "0". Example 1.5. Set hep_capture_on parameter ... modparam("sipcapture", "hep_capture_on", 1) ... 1.3.6. max_async_queries (integer) Parameter to set the maximum number of 'INSERT' queries of captured packets to be done in the same time, only if the DB supports async operations. If OpenSIPS is shut down, the remaining queries shall be executed. The query buffer is limited 65535 chars, so probably no more than 30-40 queries can be done in the same time, depending mostly on the size of the inserted sip message, since it's the biggest part of the query. Default value is "5". Example 1.6. Set max_async_queries parameter ... modparam("sipcapture", "max_async_queries", 3) ... 1.3.7. raw_ipip_capture_on (integer) Parameter to enable/disable IPIP capturing (on(1)/off(0)) Default value is "0". Example 1.7. Set raw_ipip_capture_on parameter ... modparam("sipcapture", "raw_ipip_capture_on", 1) ... 1.3.8. raw_moni_capture_on (integer) Parameter to enable/disable monitoring/mirroring port capturing (on(1)/off(0)) Only one mode on raw socket can be enabled! Monitoring port capturing currently supported only on Linux. Default value is "0". Example 1.8. Set raw_moni_capture_on parameter ... modparam("sipcapture", "raw_moni_capture_on", 1) ... 1.3.9. raw_socket_listen (string) Parameter indicate an listen IP address of RAW socket for IPIP capturing. You can also define a port/portrange for IPIP/Mirroring mode, to capture SIP messages in specific ports: "10.0.0.1:5060" - the source/destination port of the SIP message must be equal 5060 "10.0.0.1:5060-5090" - the source/destination port of the SIP message must be equal or be between 5060 and 5090. The port/portrange must be defined if you are planning to use mirroring capture! In this case, the part with IP address will be ignored, but to make parser happy, use i.e. 10.0.0.0 Default value is "". Example 1.9. Set raw_socket_listen parameter ... modparam("sipcapture", "raw_socket_listen", "10.0.0.1:5060-5090") ... modparam("sipcapture", "raw_socket_listen", "10.0.0.1:5060") ... 1.3.10. raw_interface (string) Name of the interface to bind on the raw socket. Default value is "". Example 1.10. Set raw_socket_listen parameter ... modparam("sipcapture", "raw_interface", "eth0") ... 1.3.11. raw_sock_children (integer) Parameter define how much children must be created to listen the raw socket. Default value is "1". Example 1.11. Set raw_socket_listen parameter ... modparam("sipcapture", "raw_sock_children", 6) ... 1.3.12. promiscuous_on (integer) Parameter to enable/disable promiscuous mode on the raw socket. Linux only. Default value is "0". Example 1.12. Set promiscuous_on parameter ... modparam("sipcapture", "promiscuous_on", 1) ... 1.3.13. raw_moni_bpf_on (integer) Activate Linux Socket Filter (LSF based on BPF) on the mirroring interface. The structure is defined in linux/filter.h. The default LSF accept a port/portrange from the raw_socket_listen param. Currently LSF supported only on Linux. Default value is "0". Example 1.13. Set raw_moni_bpf_on parameter ... modparam("sipcapture", "raw_moni_bpf_on", 1) ... 1.3.14. capture_node (str) Name of the capture node. Default value is "homer01". Example 1.14. Set capture_node parameter ... modparam("sipcapture", "capture_node", "homer03") ... 1.3.15. hep_route (string) Specifies what path your hep messages should take. Possible values are the following: * none - don't go through the script; do directly sip_capture(); * sip(default) - go through the main request route; here the message is parsed and you can do anything you want with it; * any other string value - define a route name through which your hep messages should go; the message is not parsed because of efficiency reasons; from here you can modify the hep chunks(if hep version 3 is used) and relay the hep messages to other hep capture nodes; Default value is sip(going thorugh the main request route). Example 1.15. Set hep_route parameter ... modparam("sipcapture", "hep_route", "my_hep_route") ... route[my_hep_route] { /* do hep stuff in here */ ... } ... 1.4. Exported Functions 1.4.1. sip_capture([table_name]) Save the message into the database. Meaning of the parameters is as follows: * table_name (string) - the name of the table to store the packet; it can have a strftime-like formatted suffix in order to change it's name based on time; if not set, modparam defined table will be used; This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_RO UTE. Example 1.16. sip_capture usage ... if (is_method("REGISTER")) sip_capture(); ... /* table name will change every day */ sip_capture("homer_%m_%d"); ... 1.4.2. report_capture([table_name],correlation_id[,proto_type]) Save the message into the database. If you want set the protocol type you have to define the table name, even if you pass over it(report_capture(,"$var(cor_id)", "$var(proto_type)")). Meaning of the parameters is as follows: * table_name (string) - the name of the table to store the packet; it can have a strftime-like formatted suffix in order to change it's name based on time; * correlation_id (string) * proto_type (int) - protocol type number as defined in hep protocol specification. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_RO UTE. Example 1.17. sip_capture usage ... hep_get("utf8-string", "0x0011", "$var(correlation_id)"); if($var(correlation_id) == $null) { xlog("NO CORRELATION ID! SET SOMETHING OR DROP"); $var(correlation_id) = "absdcef"; } $var(proto_type) = "3"; /* 0x03 - SDP protocol */ report_capture("rtcp_log", $var(correlation_id)"); /* setting the first parameter, even if setting it to null, is m andatory in order to be able to set proto type */ report_capture(, $var(correlation_id)", "$var); report_capture("rtcp_log", $var(correlation_id)", "$var); ... 1.4.3. hep_set([data_type,] chunk_id, [vendor_id,] chunk_data) Set a hep chunk. If not exists, it shall be added. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_RO UTE. Meaning of the parameters is as follows: * data_type - data type of the data in the chunk. It can have the following values: + uint8 - byte unsigned integer + uint16 - word unsigned integer + uint32 - 4 byte unsigned integer + inet4-addr - IPv4 address in human readable format + inet6-addr - IPv6 address in human readable format + utf8-string - UTF8 encoded character sequence + octet-string - byte array * chunk_id(string value with hex/int or string identifiere of chunk) - id of the chunk to be added; most of the generic chunks are in the internal hep structure. For these you can skip the data_type and vendor_id since they are already known. Generic chunks that don't have built in support are the followinig: 0x000d(keep alive timer), 0x000e(authenticate key), 0x0011(internal correltion id), 0x0012(vlan ID). You can set these chunks, but only with vendor id 0x0000, other values shall result in an error. Timestamp(0x0009) and timestamp_us(0x000A) chunks can't be set. For chunks that have built-in support you can also use strings instead of chunk ids as follows: + 0x0001 - proto_family(CAN'T BE SET; it shall be automatically updated if you change the type of the source/destination address from IPv4 to IPv6 or else) + 0x0002 - proto_id; since it's quite hard to know the int values for the protocol one can change this value using the following string values: o UDP o TCP o TLS o SCTP o WS o WSS o BIN o HEP + 0x0003 - src_ip + 0x0004 - dst_ip + 0x0005 - src_ip + 0x0006 - dst_ip + 0x0007 - src_port + 0x0008 - dst_port + 0x0009 - timestamp(CAN'T BE SET) + 0x000A - timestamp_us(CAN'T BE SET) + 0x000B - proto_type; for this variable there are predefined strings which can be set: o SIP o XMPP o SDP o RTP o RTCP o MGCP o MEGACO o M2UA o M3UA o IAX o H322 o H321 + 0x000C - captagent_id + 0x000f - payload + 0x0010 - payload * vendor id(string value with hex or int) - there are some vendor ids already defined; check hep proto docs for more details. * data(string) - data that the chunk shall contain; internally it shall be converted to the requested data type Example 1.18. hep_set usage ... /* modify/add a generic chunk */ hep_set("proto_type", "H321"); /* add a custom chunk - int */ hep_set("uint32", "31"/*chk id*/, "3"/*opensips vendor*/, "132") /* add a custom chunk - IPv4 address */ hep_set("inet4-addr", "32"/*chk id*/, "3"/*opensips vendor*/, "192.168.5 .14") ... 1.4.4. hep_get([data_type,] chunk_id, vendor_id_pv, chunk_data_pv) Set a hep chunk. If not exists, it shall be added. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_RO UTE. Meaning of the parameters is as follows: * data_type(string) - same meaning as in Section 1.4.3, “ hep_set([data_type,] chunk_id, [vendor_id,] chunk_data) â€; can miss if it's a generic chunk * chunk_id - same meaning as in Section 1.4.3, “ hep_set([data_type,] chunk_id, [vendor_id,] chunk_data) †* vendor_id_pv(writable pvar) - will hold the vendor id(int value) of the chunk * chunk_data_pv(writable pvar) - will hold the data inside the chunk; some of the generic chunk data come in specific format, as following: + 0x0001 - proto_family(string) - AF_INET/AF_INET6 + 0x0002 proto_id(string) - see Section 1.4.3, “ hep_set([data_type,] chunk_id, [vendor_id,] chunk_data) †for possible values + 0x0003/0x0004/0x0005/0x0006 src/dst_ip(string) - ip addresses in human readable format + 0x0009 timestamp(string) - time and date in human readable format + 0x000B proto_type(string) - see Section 1.4.3, “ hep_set([data_type,] chunk_id, [vendor_id,] chunk_data) †for possible values Example 1.19. hep_set usage ... /* get a generic chunk */ hep_get("proto_type", "$var(vid)", "$var(data)"); /* get custom chunk - you must know what kind of data is there */ hep_set("uint32", "31"/*chk id*/, "$var(vid)", "$var(data)") ... 1.4.5. hep_del(chunk_id) Removes a hep chunk. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_RO UTE. Meaning of the parameters is as follows: * chunk_id - same meaning as the chunk_id in Section 1.4.3, “ hep_set([data_type,] chunk_id, [vendor_id,] chunk_data) â€. Example 1.20. hep_set usage ... /* get a generic chunk */ hep_del("25"); /* removes chunk with chunk id 25 */ ... 1.4.6. hep_relay() Relay a message statefully to destination indicated in current URI. (If the original URI was rewritten by UsrLoc, RR, strip/prefix, etc., the new URI will be taken). The message has to have been a HEP message, version 1, 2 or 3. For version 1 and 2 you can relay only using UDP, for version 3 TCP and UDP can be used. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_RO UTE. Example 1.21. hep_relay usage ... $du="sip:192.168.153.157"; if (!hep_relay()) { xlog("Hep proxying failed!\n"); exit; } ... 1.4.7. hep_resume_sip() Break hep route execution and resume into the main request route. WARNING: USE THIS FUNCTION ONLY FROM A ROUTE DEFINED USING hep_route PARAMETER. Example 1.22. hep_resume_sip usage ... modparam("sipcapture", "hep_route", "my_hep_route") route[my_hep_route] { ... /* resume execution in the main request route */ hep_resume_sip(); } ... 1.5. Exported Async Functions 1.5.1. sip_capture() Save the message inside the database. The query is being done asnychronously only if the database supports async operations. The query might not be executed exactly at this moment, it depends on the max_async_queries parameter. Example 1.23. sip_capture usage ... { async(sip_capture(), capture_resume); } route[capture_resume] { xlog("insert executed\n"); /*continuing logic here */ } ... 1.6. Exported PseudoVariables 1.6.1. hep_net Holds layer 3 and 4 information(IP addresses and ports) about the node from where the hep message was received. The variable is read-only and can be used only if it's referenced by it's name. Possible values for it's name are the following: * proto_family - can be AF_INET/AF_INET6 * proto_id - it's PROTO_HEP since you receive the message as hep. * src_ip - IPv4/IPv6 address, depending on the proto_family, of the sending node. * dst_ip - IPv4/IPv6 address, depending on the proto_family, of the receiving node(OpenSIPS hep interface ip on which the message was received). * src_port - Sending node port. * dst_port - Receiving port(OpenSIPS hep interace port on which the message was received). Example 1.24. hep_net usage ... /* received this hep packet on interface 192.168.2.5*/ if ($hep_net(dst_ip) == "192.168.2.5") { /* received this on 192.168.2.5:6060 interface */ if ($hep_net(dst_port) == 6060) { ... /* received this on 192.168.2.5:6061 interface */ } else if ($hep_net(dst_port) == 6061) { ... } } ... 1.6.2. HEPVERSION - string,int Holds the version of the hep packet received on the interface. Example 1.25. HEPVERSION usage ... if ($HEPVERSION == 3) { /* It's a HEPv3 packet*/ ... } else if ($HEPVERSION == 2) { /* It's a HEPv2 packet */ ... } else if ($HEPVERSION == 1) { /* It's a HEPv1 packet */ ... } ... 1.7. MI Commands 1.7.1. sip_capture Name: sip_capture Parameters: * capture_mode : turns on/off SIP message capturing. Possible values are: + on + off The parameter is optional - if missing, the command will return the status of the SIP message capturing (as string “on†or “off†) without changing anything. MI FIFO Command Format: :sip_capture:_reply_fifo_file_ capture_mode _empty_line_ 1.8. Database setup Before running OpenSIPS with sipcapture, you have to setup the database tables where the module will store the data. For that, if the table were not created by the installation script or you choose to install everything by yourself you can use the sipcapture-create.sql and reportcapture-create.sql or the sipcapture-st-create.sql SQL script in the database directories in the opensips/scripts folder as template. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. 1.9. Limitation 1. Only one capturing mode on RAW socket is supported: IPIP or monitoring/mirroring port. Don't activate both at the same time. 2. By default MySQL doesn't support INSERT DELAYED for partitioning table. You can patch MySQL (http://bugs.mysql.com/bug.php?id=50393) or use separate tables (pseudo partitioning) 3. Mirroring port capturing works only on Linux. opensips-2.2.2/modules/sipcapture/doc/000077500000000000000000000000001300170765700177575ustar00rootroot00000000000000opensips-2.2.2/modules/sipcapture/doc/sipcapture.xml000066400000000000000000000021221300170765700226550ustar00rootroot00000000000000 %docentities; ]> SipCapture Module &osipsname; Alexandr Dubovikov alexandr.dubovikov@gmail.com Alexandr Dubovikov alexandr.dubovikov@gmail.com 2011 QSC AG 2011 http://www.qsc.de &admin; &faq; opensips-2.2.2/modules/sipcapture/doc/sipcapture_admin.xml000066400000000000000000000732061300170765700240400ustar00rootroot00000000000000 &adminguide;
Overview Offer a possibility to store incoming/outgoing SIP messages in database. OpenSIPs can capture SIP messages in three mode IPIP encapsulation. (ETHHDR+IPHDR+IPHDR+UDPHDR). Monitoring/mirroring port. Homer encapsulation protocl mode (HEP v1/2/3). With version 2.2 comes the new HEPv3 support using the proto _hep module. Also header manipulation support for HEPv3 has been added. See for more details. If you want more information about hep protocol check this link. The capturing can be turned on/off using fifo commad. opensipsctl fifo sip_capture on opensipsctl fifo sip_capture off
Dependencies
&osips; Modules The following modules must be loaded before this module: database module - mysql, postrgress, dbtext, unixodbc... proto_hep module - if hep capturing used
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Parameters
<varname>db_url</varname> (str) Database URL. Default value is "". Set <varname>db_url</varname> parameter ... modparam("sipcapture", "db_url", "mysql://user:passwd@host/dbname") ...
<varname>table_name</varname> (str) Name of the table's name where to store the SIP messages. Since version 2.2 it allows strftime-like suffix for having time formatted table names. Default value is "sip_capture". Set <varname>table_name</varname> parameter ... modparam("sipcapture", "table_name", "homer_capture") /* change table name every day */ modparam("sipcapture", "table_name", "homer_%m_%d") /* if today is 13-04-2014 it will exetend to homer_04_13 */ ...
<varname>rtcp_table_name</varname> (str) Name of the table's name where to store packets captured with report_capture function. Since version 2.2 it allows strftime-like suffix for having time formatted table names. Default value is "rtcp_capture". Set <varname>rtcp_capture</varname> parameter ... modparam("sipcapture", "rtcp_table_name", "homer_capture") /* change table name every hour */ modparam("sipcapture", "rtcp_table_name", "homer_%m_%d_%H") /* if today is 13-04-2014 13:05 pm it will exetend to homer_04_13_13 */ ...
<varname>capture_on</varname> (integer) Parameter to enable/disable capture globaly (on(1)/off(0)) Default value is "0". Set <varname>capture_on</varname> parameter ... modparam("sipcapture", "capture_on", 1) ...
<varname>hep_capture_on</varname> (integer) Parameter to enable/disable capture of HEP (on(1)/off(0)) Default value is "0". Set <varname>hep_capture_on</varname> parameter ... modparam("sipcapture", "hep_capture_on", 1) ...
<varname>max_async_queries</varname> (integer) Parameter to set the maximum number of 'INSERT' queries of captured packets to be done in the same time, only if the DB supports async operations. If OpenSIPS is shut down, the remaining queries shall be executed. The query buffer is limited 65535 chars, so probably no more than 30-40 queries can be done in the same time, depending mostly on the size of the inserted sip message, since it's the biggest part of the query. Default value is "5". Set <varname>max_async_queries</varname> parameter ... modparam("sipcapture", "max_async_queries", 3) ...
<varname>raw_ipip_capture_on</varname> (integer) Parameter to enable/disable IPIP capturing (on(1)/off(0)) Default value is "0". Set <varname>raw_ipip_capture_on</varname> parameter ... modparam("sipcapture", "raw_ipip_capture_on", 1) ...
<varname>raw_moni_capture_on</varname> (integer) Parameter to enable/disable monitoring/mirroring port capturing (on(1)/off(0)) Only one mode on raw socket can be enabled! Monitoring port capturing currently supported only on Linux. Default value is "0". Set <varname>raw_moni_capture_on</varname> parameter ... modparam("sipcapture", "raw_moni_capture_on", 1) ...
<varname>raw_socket_listen</varname> (string) Parameter indicate an listen IP address of RAW socket for IPIP capturing. You can also define a port/portrange for IPIP/Mirroring mode, to capture SIP messages in specific ports: "10.0.0.1:5060" - the source/destination port of the SIP message must be equal 5060 "10.0.0.1:5060-5090" - the source/destination port of the SIP message must be equal or be between 5060 and 5090. The port/portrange must be defined if you are planning to use mirroring capture! In this case, the part with IP address will be ignored, but to make parser happy, use i.e. 10.0.0.0 Default value is "". Set <varname>raw_socket_listen</varname> parameter ... modparam("sipcapture", "raw_socket_listen", "10.0.0.1:5060-5090") ... modparam("sipcapture", "raw_socket_listen", "10.0.0.1:5060") ...
<varname>raw_interface</varname> (string) Name of the interface to bind on the raw socket. Default value is "". Set <varname>raw_socket_listen</varname> parameter ... modparam("sipcapture", "raw_interface", "eth0") ...
<varname>raw_sock_children</varname> (integer) Parameter define how much children must be created to listen the raw socket. Default value is "1". Set <varname>raw_socket_listen</varname> parameter ... modparam("sipcapture", "raw_sock_children", 6) ...
<varname>promiscuous_on</varname> (integer) Parameter to enable/disable promiscuous mode on the raw socket. Linux only. Default value is "0". Set <varname>promiscuous_on</varname> parameter ... modparam("sipcapture", "promiscuous_on", 1) ...
<varname>raw_moni_bpf_on</varname> (integer) Activate Linux Socket Filter (LSF based on BPF) on the mirroring interface. The structure is defined in linux/filter.h. The default LSF accept a port/portrange from the raw_socket_listen param. Currently LSF supported only on Linux. Default value is "0". Set <varname>raw_moni_bpf_on</varname> parameter ... modparam("sipcapture", "raw_moni_bpf_on", 1) ...
<varname>capture_node</varname> (str) Name of the capture node. Default value is "homer01". Set <varname>capture_node</varname> parameter ... modparam("sipcapture", "capture_node", "homer03") ...
<varname>hep_route</varname> (string) Specifies what path your hep messages should take. Possible values are the following: none - don't go through the script; do directly sip_capture(); sip(default) - go through the main request route; here the message is parsed and you can do anything you want with it; any other string value - define a route name through which your hep messages should go; the message is not parsed because of efficiency reasons; from here you can modify the hep chunks(if hep version 3 is used) and relay the hep messages to other hep capture nodes; Default value is sip(going thorugh the main request route). Set <varname>hep_route</varname> parameter ... modparam("sipcapture", "hep_route", "my_hep_route") ... route[my_hep_route] { /* do hep stuff in here */ ... } ...
Exported Functions
<function moreinfo="none">sip_capture([table_name])</function> Save the message into the database. Meaning of the parameters is as follows: table_name (string) - the name of the table to store the packet; it can have a strftime-like formatted suffix in order to change it's name based on time; if not set, modparam defined table will be used; This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_ROUTE. <function>sip_capture</function> usage ... if (is_method("REGISTER")) sip_capture(); ... /* table name will change every day */ sip_capture("homer_%m_%d"); ...
<function moreinfo="none">report_capture([table_name],correlation_id[,proto_type])</function> Save the message into the database. If you want set the protocol type you have to define the table name, even if you pass over it(report_capture(,"$var(cor_id)", "$var(proto_type)")). Meaning of the parameters is as follows: table_name (string) - the name of the table to store the packet; it can have a strftime-like formatted suffix in order to change it's name based on time; correlation_id (string) proto_type (int) - protocol type number as defined in hep protocol specification. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_ROUTE. <function>sip_capture</function> usage ... hep_get("utf8-string", "0x0011", "$var(correlation_id)"); if($var(correlation_id) == $null) { xlog("NO CORRELATION ID! SET SOMETHING OR DROP"); $var(correlation_id) = "absdcef"; } $var(proto_type) = "3"; /* 0x03 - SDP protocol */ report_capture("rtcp_log", $var(correlation_id)"); /* setting the first parameter, even if setting it to null, is mandatory in order to be able to set proto type */ report_capture(, $var(correlation_id)", "$var); report_capture("rtcp_log", $var(correlation_id)", "$var); ...
<function moreinfo="none">hep_set([data_type,] chunk_id, [vendor_id,] chunk_data)</function> Set a hep chunk. If not exists, it shall be added. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_ROUTE. Meaning of the parameters is as follows: data_type - data type of the data in the chunk. It can have the following values: uint8 - byte unsigned integer uint16 - word unsigned integer uint32 - 4 byte unsigned integer inet4-addr - IPv4 address in human readable format inet6-addr - IPv6 address in human readable format utf8-string - UTF8 encoded character sequence octet-string - byte array chunk_id(string value with hex/int or string identifiere of chunk) - id of the chunk to be added; most of the generic chunks are in the internal hep structure. For these you can skip the data_type and vendor_id since they are already known. Generic chunks that don't have built in support are the followinig: 0x000d(keep alive timer), 0x000e(authenticate key), 0x0011(internal correltion id), 0x0012(vlan ID). You can set these chunks, but only with vendor id 0x0000, other values shall result in an error. Timestamp(0x0009) and timestamp_us(0x000A) chunks can't be set. For chunks that have built-in support you can also use strings instead of chunk ids as follows: 0x0001 - proto_family(CAN'T BE SET; it shall be automatically updated if you change the type of the source/destination address from IPv4 to IPv6 or else) 0x0002 - proto_id; since it's quite hard to know the int values for the protocol one can change this value using the following string values: UDP TCP TLS SCTP WS WSS BIN HEP 0x0003 - src_ip 0x0004 - dst_ip 0x0005 - src_ip 0x0006 - dst_ip 0x0007 - src_port 0x0008 - dst_port 0x0009 - timestamp(CAN'T BE SET) 0x000A - timestamp_us(CAN'T BE SET) 0x000B - proto_type; for this variable there are predefined strings which can be set: SIP XMPP SDP RTP RTCP MGCP MEGACO M2UA M3UA IAX H322 H321 0x000C - captagent_id 0x000f - payload 0x0010 - payload vendor id(string value with hex or int) - there are some vendor ids already defined; check hep proto docs for more details. data(string) - data that the chunk shall contain; internally it shall be converted to the requested data type <function>hep_set</function> usage ... /* modify/add a generic chunk */ hep_set("proto_type", "H321"); /* add a custom chunk - int */ hep_set("uint32", "31"/*chk id*/, "3"/*opensips vendor*/, "132") /* add a custom chunk - IPv4 address */ hep_set("inet4-addr", "32"/*chk id*/, "3"/*opensips vendor*/, "192.168.5.14") ...
<function moreinfo="none">hep_get([data_type,] chunk_id, vendor_id_pv, chunk_data_pv)</function> Set a hep chunk. If not exists, it shall be added. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_ROUTE. Meaning of the parameters is as follows: data_type(string) - same meaning as in ; can miss if it's a generic chunk chunk_id - same meaning as in vendor_id_pv(writable pvar) - will hold the vendor id(int value) of the chunk chunk_data_pv(writable pvar) - will hold the data inside the chunk; some of the generic chunk data come in specific format, as following: 0x0001 - proto_family(string) - AF_INET/AF_INET6 0x0002 proto_id(string) - see for possible values 0x0003/0x0004/0x0005/0x0006 src/dst_ip(string) - ip addresses in human readable format 0x0009 timestamp(string) - time and date in human readable format 0x000B proto_type(string) - see for possible values <function>hep_set</function> usage ... /* get a generic chunk */ hep_get("proto_type", "$var(vid)", "$var(data)"); /* get custom chunk - you must know what kind of data is there */ hep_set("uint32", "31"/*chk id*/, "$var(vid)", "$var(data)") ...
<function moreinfo="none">hep_del(chunk_id)</function> Removes a hep chunk. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_ROUTE. Meaning of the parameters is as follows: chunk_id - same meaning as the chunk_id in . <function>hep_set</function> usage ... /* get a generic chunk */ hep_del("25"); /* removes chunk with chunk id 25 */ ...
<function moreinfo="none">hep_relay()</function> Relay a message statefully to destination indicated in current URI. (If the original URI was rewritten by UsrLoc, RR, strip/prefix, etc., the new URI will be taken). The message has to have been a HEP message, version 1, 2 or 3. For version 1 and 2 you can relay only using UDP, for version 3 TCP and UDP can be used. This function can be used from REQUEST_ROUTE,FAILURE_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE,LOCAL_ROUTE. <function>hep_relay</function> usage ... $du="sip:192.168.153.157"; if (!hep_relay()) { xlog("Hep proxying failed!\n"); exit; } ...
<function moreinfo="none">hep_resume_sip()</function> Break hep route execution and resume into the main request route. WARNING: USE THIS FUNCTION ONLY FROM A ROUTE DEFINED USING hep_route PARAMETER. <function>hep_resume_sip</function> usage ... modparam("sipcapture", "hep_route", "my_hep_route") route[my_hep_route] { ... /* resume execution in the main request route */ hep_resume_sip(); } ...
Exported Async Functions
<function moreinfo="none">sip_capture()</function> Save the message inside the database. The query is being done asnychronously only if the database supports async operations. The query might not be executed exactly at this moment, it depends on the max_async_queries parameter. <function>sip_capture</function> usage ... { async(sip_capture(), capture_resume); } route[capture_resume] { xlog("insert executed\n"); /*continuing logic here */ } ...
Exported PseudoVariables
<function moreinfo="none">hep_net</function> Holds layer 3 and 4 information(IP addresses and ports) about the node from where the hep message was received. The variable is read-only and can be used only if it's referenced by it's name. Possible values for it's name are the following: proto_family - can be AF_INET/AF_INET6 proto_id - it's PROTO_HEP since you receive the message as hep. src_ip - IPv4/IPv6 address, depending on the proto_family, of the sending node. dst_ip - IPv4/IPv6 address, depending on the proto_family, of the receiving node(&osips; hep interface ip on which the message was received). src_port - Sending node port. dst_port - Receiving port(&osips; hep interace port on which the message was received). <function>hep_net</function> usage ... /* received this hep packet on interface 192.168.2.5*/ if ($hep_net(dst_ip) == "192.168.2.5") { /* received this on 192.168.2.5:6060 interface */ if ($hep_net(dst_port) == 6060) { ... /* received this on 192.168.2.5:6061 interface */ } else if ($hep_net(dst_port) == 6061) { ... } } ...
<function moreinfo="none">HEPVERSION - string,int</function> Holds the version of the hep packet received on the interface. <function>HEPVERSION</function> usage ... if ($HEPVERSION == 3) { /* It's a HEPv3 packet*/ ... } else if ($HEPVERSION == 2) { /* It's a HEPv2 packet */ ... } else if ($HEPVERSION == 1) { /* It's a HEPv1 packet */ ... } ...
MI Commands
<function moreinfo="none">sip_capture</function> Name: sip_capture Parameters: capture_mode : turns on/off SIP message capturing. Possible values are: on off The parameter is optional - if missing, the command will return the status of the SIP message capturing (as string on or off ) without changing anything. MI FIFO Command Format: :sip_capture:_reply_fifo_file_ capture_mode _empty_line_
Database setup Before running &osips; with sipcapture, you have to setup the database tables where the module will store the data. For that, if the table were not created by the installation script or you choose to install everything by yourself you can use the sipcapture-create.sql and reportcapture-create.sql or the sipcapture-st-create.sql SQL script in the database directories in the opensips/scripts folder as template. You can also find the complete database documentation on the project webpage, &osipsdbdocslink;.
Limitation 1. Only one capturing mode on RAW socket is supported: IPIP or monitoring/mirroring port. Don't activate both at the same time. 2. By default MySQL doesn't support INSERT DELAYED for partitioning table. You can patch MySQL (http://bugs.mysql.com/bug.php?id=50393) or use separate tables (pseudo partitioning) 3. Mirroring port capturing works only on Linux.
opensips-2.2.2/modules/sipcapture/examples/000077500000000000000000000000001300170765700210305ustar00rootroot00000000000000opensips-2.2.2/modules/sipcapture/examples/opensips.cfg000066400000000000000000000040461300170765700233550ustar00rootroot00000000000000 ####### Global Parameters ######### log_level=3 log_stderror=no log_facility=LOG_LOCAL0 children=5 /* uncomment the following lines to enable debugging */ #debug_mode=yes /* uncomment the next line to disable TCP (default on) */ disable_tcp=yes # default db_url to be used by modules requiring DB connection db_default_url="mysql://opensips:opensipsrw@localhost/opensips" /* uncomment and configure the following line if you want opensips to bind on a specific interface/port/proto (default bind on all available) */ listen=udp:10.0.130.41:5060 ####### Modules Section ######## #set module path mpath="/usr/local/lib64/opensips/modules/" loadmodule "db_mysql.so" loadmodule "sipcapture.so" ####### Routing Logic ######## modparam("sipcapture", "db_url", "mysql://homer_user:homer_password@localhost/homer_data") modparam("sipcapture", "capture_on", 1) /* activate HEP capturing */ modparam("sipcapture", "hep_capture_on", 1) /* IP to listen*/ #modparam("sipcapture", "raw_socket_listen", "10.0.130.41:5060-6000") modparam("sipcapture", "raw_interface", "eth0") /* activate IPIP capturing */ modparam("sipcapture", "raw_ipip_capture_on", 0) /* activate monitoring port capturing */ modparam("sipcapture", "raw_moni_capture_on", 0) /* My table name*/ modparam("sipcapture", "table_name", "sip_capture") modparam("sipcapture", "raw_sock_children", 4) /* Promiscious mode */ modparam("sipcapture", "promiscious_on", 1) ####### Routing Logic ######## # main request routing logic # Main SIP request routing logic # - processing of any incoming SIP request starts with this route route { #For example, you can capture only needed methods... #if (method =~ "^(INVITE|UPDATE|NOTIFY|SUBSCRIBE|OPTIONS|REGISTER)") { sip_capture(); #} exit; } onreply_route { #And only needed reply or needed requests method #if(status =~ "^(1[0-9][0-9]|[3[0-9][0-9]|4[0-9]|[56][0-9][0-9])") { #if($rm =~ "^(INVITE|UPDATE|NOTIFY|SUBSCRIBE|OPTIONS|REGISTER)") { sip_capture(); #} exit; } opensips-2.2.2/modules/sipcapture/examples/partrotate_unixtimestamp.pl000066400000000000000000000142061300170765700265440ustar00rootroot00000000000000#!/usr/bin/perl # # partrotate_unixtimestamp - perl script for mySQL partition rotation # # Copyright (C) 2011 Alexandr Dubovikov (QSC AG) (alexandr.dubovikov@gmail.com) # # This file is part of webhomer, a free capture server. # # partrotate_unixtimestamp is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # partrotate_unixtimestamp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA use DBI; $version = "0.2.1"; $mysql_table = "sip_capture"; $mysql_dbname = "homer_db"; $mysql_user = "mysql_login"; $mysql_password = "mysql_password"; $mysql_host = "localhost"; $maxparts = 6; #6 days $newparts = 2; #new partitions for 2 days. Anyway, start this script daily! @stepsvalues = (86400, 3600, 1800, 900); $partstep = 0; # 0 - Day, 1 - Hour, 2 - 30 Minutes, 3 - 15 Minutes #Check it $partstep=0 if(!defined $stepsvalues[$partstep]); #Mystep $mystep = $stepsvalues[$partstep]; #Coof $coof=int(86400/$mystep); #How much partitions $maxparts*=$coof; $newparts*=$coof; my $db = DBI->connect("DBI:mysql:$mysql_dbname:$mysql_host:3306", $mysql_user, $mysql_password); #$db->{PrintError} = 0; my $sth = $db->do(" CREATE TABLE IF NOT EXISTS `".$mysql_table."` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `micro_ts` bigint(18) NOT NULL DEFAULT '0', `method` varchar(50) NOT NULL DEFAULT '', `reply_reason` varchar(100) NOT NULL, `ruri` varchar(200) NOT NULL DEFAULT '', `ruri_user` varchar(100) NOT NULL DEFAULT '', `from_user` varchar(100) NOT NULL DEFAULT '', `from_tag` varchar(64) NOT NULL DEFAULT '', `to_user` varchar(100) NOT NULL DEFAULT '', `to_tag` varchar(64) NOT NULL, `pid_user` varchar(100) NOT NULL DEFAULT '', `contact_user` varchar(120) NOT NULL, `auth_user` varchar(120) NOT NULL, `callid` varchar(100) NOT NULL DEFAULT '', `callid_aleg` varchar(100) NOT NULL DEFAULT '', `via_1` varchar(256) NOT NULL, `via_1_branch` varchar(80) NOT NULL, `cseq` varchar(25) NOT NULL, `diversion` varchar(256) NOT NULL, `reason` varchar(200) NOT NULL, `content_type` varchar(256) NOT NULL, `authorization` varchar(256) NOT NULL, `user_agent` varchar(256) NOT NULL, `source_ip` varchar(50) NOT NULL DEFAULT '', `source_port` int(10) NOT NULL, `destination_ip` varchar(50) NOT NULL DEFAULT '', `destination_port` int(10) NOT NULL, `contact_ip` varchar(60) NOT NULL, `contact_port` int(10) NOT NULL, `originator_ip` varchar(60) NOT NULL DEFAULT '', `originator_port` int(10) NOT NULL, `proto` int(5) NOT NULL, `family` int(1) DEFAULT NULL, `rtp_stat` varchar(256) NOT NULL, `type` int(2) NOT NULL, `node` varchar(125) NOT NULL, `msg` text NOT NULL, PRIMARY KEY (`id`,`date`), KEY `ruri_user` (`ruri_user`), KEY `from_user` (`from_user`), KEY `to_user` (`to_user`), KEY `pid_user` (`pid_user`), KEY `auth_user` (`auth_user`), KEY `callid_aleg` (`callid_aleg`), KEY `date` (`date`), KEY `callid` (`callid`), KEY `method` (`method`), KEY `source_ip` (`source_ip`), KEY `destination_ip` (`destination_ip`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE ( UNIX_TIMESTAMP(`date`)) (PARTITION pmax VALUES LESS THAN MAXVALUE ENGINE = MyISAM); "); my $query = "SELECT UNIX_TIMESTAMP(CURDATE() - INTERVAL 1 DAY)"; $sth = $db->prepare($query); $sth->execute(); my ($curtstamp) = $sth->fetchrow_array(); $curtstamp+=0; my $query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.PARTITIONS" ."\n WHERE TABLE_NAME='".$mysql_table."' AND TABLE_SCHEMA='".$mysql_dbname."'"; $sth = $db->prepare($query); $sth->execute(); my ($partcount) = $sth->fetchrow_array(); while($partcount > $maxparts ) { $query = "SELECT PARTITION_NAME, MIN(PARTITION_DESCRIPTION)" ."\n FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='".$mysql_table."'" ."\n AND TABLE_SCHEMA='".$mysql_dbname."';"; $sth = $db->prepare($query); $sth->execute(); my ($minpart,$todaytstamp) = $sth->fetchrow_array(); $todaytstamp+=0; #Dont' delete the partition for the current day or for future. Bad idea! if($curtstamp <= $todaytstamp) { $partcount = 0; next; } #Delete $query = "ALTER TABLE ".$mysql_table." DROP PARTITION ".$minpart; $db->do($query); if (!$db->{Executed}) { print "Couldn't drop partition: $minpart\n"; break; } #decrease partcount $partcount--; } # < condition $curtstamp+=(86400); #Create new partitions for(my $i=0; $i<$newparts; $i++) { $oldstamp = $curtstamp; $curtstamp+=$mystep; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($oldstamp); my $newpartname = sprintf("p%04d%02d%02d%02d",($year+=1900),(++$mon),$mday,$hour); $query = "SELECT COUNT(*) " ."\n FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='".$mysql_table."'" ."\n AND TABLE_SCHEMA='".$mysql_dbname."' AND PARTITION_NAME='".$newpartname."'" ."\n AND PARTITION_DESCRIPTION = '".$curtstamp."'"; $sth = $db->prepare($query); $sth->execute(); my ($exist) = $sth->fetchrow_array(); $exist+=0; if(!$exist) { # Fix MAXVALUE. Thanks Dorn B. for report and fix. $query = "ALTER TABLE ".$mysql_table." REORGANIZE PARTITION pmax INTO (PARTITION ".$newpartname ."\n VALUES LESS THAN (".$curtstamp.") ENGINE = MyISAM, PARTITION pmax VALUES LESS THAN MAXVALUE ENGINE = MyISAM)"; $db->do($query); if (!$db->{Executed}) { print "Couldn't add partition: $newpartname\n"; } } } opensips-2.2.2/modules/sipcapture/sipcapture.c000066400000000000000000004067671300170765700215610ustar00rootroot00000000000000/* * sipcapture module - helper module to capture sip messages * * Copyright (C) 2011 Alexandr Dubovikov (QSC AG) (alexandr.dubovikov@gmail.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! \file * sipcapture module - helper module to capture sip messages * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "../proto_hep/hep.h" #include "../proto_hep/hep_cb.h" #include "../../context.h" #include "../../mod_fix.h" #include "../../msg_translator.h" #include "../../action.h" #include "../../socket_info.h" /* BPF structure */ #ifdef __OS_linux #include #endif #ifndef __USE_BSD #define __USE_BSD /* on linux use bsd version of iphdr (more portable) */ #endif /* __USE_BSD */ #include #define __FAVOR_BSD /* on linux use bsd version of udphdr (more portable) */ #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../net/proto_udp/proto_udp.h" #include "../../ut.h" #include "../../ip_addr.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" #include "../../parser/digest/digest.h" #include "../../parser/parse_pai.h" #include "../../parser/parse_ppi.h" #include "../../pvar.h" #include "../../str.h" #include "../../resolve.h" #include "../../receive.h" #include "../../forward.h" #include "../../msg_translator.h" #ifdef STATISTICS #include "../../statistics.h" #endif /* this value shall be put in proto_reserved2 field of * the receive_info structure and help us identify a * hep message */ #define HEPBUF_LEN (1<<14) #define LOWER_DWORD(_c1, _c2, _c3, _c4) (((_c1<<24)|(_c2<<16)|(_c3<<8)|_c4)|0x20202020) #define LOWER_WORD(_c1, _c2) (((_c1|0x20)<<8) | (_c2|0x20)) #define LOWER_BYTE(_c1) (_c1|0x20) #define HEP_GET_CONTEXT(_api) \ (struct hep_context*)context_get_ptr(CONTEXT_GLOBAL, current_processing_ctx, _api.get_hep_ctx_id()) #define HAVE_SHARED_QUERIES (max_async_queries > 1) #define HAVE_MULTIPLE_ASYNC_INSERT (DB_CAPABILITY(db_funcs, DB_CAP_ASYNC_RAW_QUERY) && HAVE_SHARED_QUERIES) #define IS_ASYNC_F (resume_f && resume_param) #define MAX_QUERY 65535 struct _async_query { str last_query_suffix; int curr_async_queries; int query_len; char query_buf[MAX_QUERY]; gen_lock_t query_lock; } *global_async_query; #define QUERY_BUF(_as_query) _as_query->query_buf #define QUERY_LEN(_as_query) _as_query->query_len #define INIT_QUERY_LOCK(_as_query) lock_init(&_as_query->query_lock) #define GET_QUERY_LOCK(_as_query) lock_get(&_as_query->query_lock) #define RELEASE_QUERY_LOCK(_as_query) lock_release(&_as_query->query_lock) #define DESTROY_QUERY_LOCK(_as_query) lock_destroy(&_as_query->query_lock) #define CURR_QUERIES(_as_query) _as_query->curr_async_queries #define LAST_SUFFIX(_as_query) _as_query->last_query_suffix typedef struct _tz_table { str prefix; /* table name */ str suffix; /* time format - strftime */ } tz_table_t; struct tz_table_list { tz_table_t* table; struct _async_query* as_qry; struct tz_table_list* next; }; /* HOMER5 compliant table name */ #define CAPTURE_TABLE_MAX_LEN 256 char table_buf[CAPTURE_TABLE_MAX_LEN]; str current_table; /* modparam defined table */ tz_table_t tz_table; tz_table_t rc_table; /* list of script used tables - we use this list to hold async queries; * when opensips is closed we need to run all queries for all the tables * in case max_async_queries is used */ struct tz_table_list* tz_list=NULL; struct tz_table_list* rc_list=NULL; /* modparam defined table */ struct tz_table_list tz_global; /* modparam defined report_capture table */ struct tz_table_list rc_global; struct _sipcapture_object { str method; str reply_reason; str ruri; str ruri_user; str ruri_domain; str from_user; str from_domain; str from_tag; str to_user; str to_domain; str to_tag; str pid_user; str contact_user; str auth_user; str callid; str callid_aleg; str via_1; str via_1_branch; str cseq; str diversion; str reason; str content_type; str authorization; str user_agent; str source_ip; int source_port; str destination_ip; int destination_port; str contact_ip; int contact_port; str originator_ip; int originator_port; int proto; int family; int proto_type; str rtp_stat; str correlation_id; int type; long long tmstamp; str node; str msg; #ifdef STATISTICS stat_var *stat; #endif }; #define ETHHDR 14 /* sizeof of ethhdr structure */ #define EMPTY_STR(val) val.s=""; val.len=0; #define TABLE_LEN 256 /* * WARNING: if you add/remove keys take care to update * VALUES_STR */ #define NR_KEYS 41 #define RTCP_NR_KEYS 12 typedef void* sc_async_param_t; db_key_t db_keys[NR_KEYS]; db_key_t rtcp_db_keys[RTCP_NR_KEYS]; /* module function prototypes */ static int mod_init(void); static int child_init(int rank); static void raw_socket_process(int rank); static void destroy(void); static int sip_capture(struct sip_msg *msg, char *s1, char *s2); static int async_sip_capture(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char* s1, char* s2); static int sip_capture_fixup(void** param, int param_no); static int sip_capture_async_fixup(void** param, int param_no); static int w_sip_capture(struct sip_msg *msg, char *table_name, async_resume_module **resume_f, void **resume_param); static void set_rtcp_keys(void); static int rc_fixup_1(void** param, int param_no); static int rc_async_fixup_1(void** param, int param_no); static int rc_fixup(void** param, int param_no); static int rc_async_fixup(void** param, int param_no); static int w_report_capture_1(struct sip_msg* msg, char* cor_id_p); static int w_report_capture_2(struct sip_msg* msg, char* table_p, char* cor_id_p); static int w_report_capture_3(struct sip_msg* msg, char* table_p, char* cor_id_p, char* proto_t_p); static int w_report_capture_async_1(struct sip_msg* msg, async_resume_module** resume_f, void** resume_param, char* cor_id_p); static int w_report_capture_async_2(struct sip_msg* msg, async_resume_module** resume_f, void** resume_param, char* table_p, char* cor_id_p); static int w_report_capture_async_3(struct sip_msg* msg, async_resume_module** resume_f, void** resume_param, char* table_p, char* cor_id_p, char* proto_t_p); static int w_report_capture(struct sip_msg* msg, char* table_p, char* cor_id_p, char* proto_t_p, async_resume_module **resume_f, void** resume_param); int hep_msg_received(void); int extract_host_port(void); int raw_capture_socket(struct ip_addr* ip, str* iface, int port_start, int port_end, int proto); int raw_capture_rcv_loop(int rsock, int port1, int port2, int ipip); int sipcapture_db_init(const str* db_url); void sipcapture_db_close(void); static struct mi_root* sip_capture_mi(struct mi_root* cmd, void* param ); static int db_sync_store(db_val_t* vals, db_key_t* keys, int num_keys); typedef int (*append_db_vals_f)(char *buf, int max_len, db_val_t* db_vals); static inline int append_sc_values(char* buf, int max_len, db_val_t* db_vals); static inline int append_rc_values(char* buf, int max_len, db_val_t* db_vals); static int db_async_store(db_val_t* vals, db_key_t* keys, int num_keys, append_db_vals_f append_db_vals, async_resume_module **resume_f, void **resume_param, struct tz_table_list* t_el); int resume_async_dbquery(int fd, struct sip_msg *msg, void *_param); /* setter functions */ static int set_hep_generic_fixup(void** param, int param_no); static int set_hep_fixup(void** param, int param_no); static int w_set_hep_generic(struct sip_msg* msg, char* id, char* data); static int w_set_hep(struct sip_msg* msg, char* id, char* vid, char* data, char* type); /* getter functions */ static int get_hep_fixup(void** param, int param_no); static int get_hep_generic_fixup(void** param, int param_no); static int w_get_hep(struct sip_msg* msg, char* type, char* id, char* vid, char* data); static int w_get_hep_generic(struct sip_msg* msg, char* id, char* vid, char* data); static int parse_hep_route(char *val); /* remove chunk functions */ static int del_hep_fixup(void** param, int param_no); static int w_del_hep(struct sip_msg* msg, char *id); static int pv_get_hep_net(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int pv_get_hep_version(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int set_generic_hep_chunk(struct hepv3* h3, unsigned chunk_id, str *data); /* hep relay function */ static int w_hep_relay(struct sip_msg *msg); static int w_hep_resume_sip(struct sip_msg *msg); static int pv_parse_hep_net_name(pv_spec_p sp, str *in); static int parse_hep_index(str *s_index); static str db_url = {NULL, 0}; static str table_name = str_init("sip_capture"); static str rtcp_table_name = str_init("rtcp_capture"); static str id_column = str_init("id"); static str date_column = str_init("date"); static str micro_ts_column = str_init("micro_ts"); static str method_column = str_init("method"); static str reply_reason_column = str_init("reply_reason"); static str ruri_column = str_init("ruri"); static str ruri_user_column = str_init("ruri_user"); static str from_user_column = str_init("from_user"); static str from_tag_column = str_init("from_tag"); static str to_user_column = str_init("to_user"); static str to_tag_column = str_init("to_tag"); static str pid_user_column = str_init("pid_user"); /* FAMILY TYPE */ static str contact_user_column = str_init("contact_user"); static str auth_user_column = str_init("auth_user"); static str callid_column = str_init("callid"); static str callid_aleg_column = str_init("callid_aleg"); static str via_1_column = str_init("via_1"); static str via_1_branch_column = str_init("via_1_branch"); static str cseq_column = str_init("cseq"); static str diversion_column = str_init("diversion_user"); static str reason_column = str_init("reason"); static str content_type_column = str_init("content_type"); static str authorization_column = str_init("auth"); static str user_agent_column = str_init("user_agent"); static str source_ip_column = str_init("source_ip"); static str source_port_column = str_init("source_port"); static str dest_ip_column = str_init("destination_ip"); static str dest_port_column = str_init("destination_port"); static str contact_ip_column = str_init("contact_ip"); static str contact_port_column = str_init("contact_port"); static str orig_ip_column = str_init("originator_ip"); static str orig_port_column = str_init("originator_port"); static str proto_column = str_init("proto"); static str family_column = str_init("family"); static str rtp_stat_column = str_init("rtp_stat"); static str type_column = str_init("type"); static str correlation_column = str_init("correlation_id"); static str node_column = str_init("node"); static str from_domain_column = str_init("from_domain"); static str to_domain_column = str_init("to_domain"); static str ruri_domain_column = str_init("ruri_domain"); static str msg_column = str_init("msg"); static str capture_node = str_init("homer01"); /* hep pvar related */ static str afinet_str = str_init("AF_INET"); static str afinet6_str = str_init("AF_INET6"); /* hep capture proto types */ static str hep_net_protos[]={ /* want same index as in opensips enum */ {NULL, 0}, str_init("UDP"), str_init("TCP"), str_init("TLS"), str_init("SCTP"), str_init("WS"), str_init("WSS"), str_init("BIN"), str_init("HEP_UDP"), str_init("HEP_TCP"), {NULL, 0} }; static str hep_app_protos[]= { str_init("reserved"), str_init("SIP"), str_init("XMPP"), str_init("SDP"), str_init("RTP"), str_init("RTCP"), str_init("MGCP"), str_init("MEGACO(H.248)"), str_init("M2UA(SS7/SIGTRAN)"), str_init("M3UA(SS7/SIGTRAN)"), str_init("IAX"), str_init("H322"), str_init("H321"), {NULL, 0} }; #define MAX_PAYLOAD 32767 static char payload_buf[MAX_PAYLOAD]; /* dummy request for the hep route */ struct sip_msg dummy_req; /* values to be set from script for hep pvar */ #define VALUES_STR "(%d,%ld,%lld,'%.*s','%.*s','%.*s','%.*s','%.*s','%.*s'," \ "'%.*s','%.*s','%.*s','%.*s','%.*s','%.*s','%.*s','%.*s','%.*s'," \ "'%.*s','%.*s','%.*s','%.*s','%.*s','%.*s',%d,'%.*s',%d," \ "'%.*s',%d,'%.*s',%d,%d,%d,'%.*s',%d,'%.*s','%.*s','%.*s'," \ "'%.*s', '%.*s', '%.*s')" #define RTCP_VALUES_STR "(%ld, %lld, '%.*s', '%.*s', %d, '%.*s', %d," \ "%d, %d, %d, '%.*s', '%.*s')" int max_async_queries=5; int raw_sock_desc = -1; /* raw socket used for ip packets */ unsigned int raw_sock_children = 1; int capture_on = 0; int hep_capture_on = 0; int ipip_capture_on = 0; int moni_capture_on = 0; int moni_port_start = 0; int moni_port_end = 0; int *capture_on_flag = NULL; int promisc_on = 0; int bpf_on = 0; char* hep_route=0; str hep_route_s; #define HEP_NO_ROUTE -1 #define HEP_SIP_ROUTE 0 static char* hep_route_name=NULL; static int hep_route_id=HEP_SIP_ROUTE; str raw_socket_listen = { 0, 0 }; str raw_interface = { 0, 0 }; struct ifreq ifr; /* interface structure */ #ifdef __OS_linux /* Linux socket filter */ /* tcpdump -s 0 udp and portrange 5060-5090 -dd */ static struct sock_filter BPF_code[] = { { 0x28, 0, 0, 0x0000000c }, { 0x15, 0, 7, 0x000086dd }, { 0x30, 0, 0, 0x00000014 }, { 0x15, 0, 18, 0x00000011 }, { 0x28, 0, 0, 0x00000036 }, { 0x35, 0, 1, 0x000013c4 }, { 0x25, 0, 14, 0x000013e2 }, { 0x28, 0, 0, 0x00000038 }, { 0x35, 11, 13, 0x000013c4 }, { 0x15, 0, 12, 0x00000800 }, { 0x30, 0, 0, 0x00000017 }, { 0x15, 0, 10, 0x00000011 }, { 0x28, 0, 0, 0x00000014 }, { 0x45, 8, 0, 0x00001fff }, { 0xb1, 0, 0, 0x0000000e }, { 0x48, 0, 0, 0x0000000e }, { 0x35, 0, 1, 0x000013c4 }, { 0x25, 0, 3, 0x000013e2 }, { 0x48, 0, 0, 0x00000010 }, { 0x35, 0, 2, 0x000013c4 }, { 0x25, 1, 0, 0x000013e2 }, { 0x6, 0, 0, 0x0000ffff }, { 0x6, 0, 0, 0x00000000 }, }; #endif db_func_t db_funcs; /*!< Database functions */ db_con_t* db_con = 0; /*!< database connection */ static db_ps_t sc_ps = NULL; static query_list_t *sc_ins_list = NULL; static db_ps_t rc_ps = NULL; static query_list_t *rc_ins_list = NULL; proto_hep_api_t hep_api; load_hep_f load_hep; static char hepbuf[HEPBUF_LEN]; static str hep_str={hepbuf, 0}; /*! \brief * Exported functions */ static cmd_export_t cmds[] = { {"sip_capture", (cmd_function)sip_capture, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"sip_capture", (cmd_function)sip_capture, 1, sip_capture_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"hep_set", (cmd_function)w_set_hep_generic, 2, set_hep_generic_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"hep_set", (cmd_function)w_set_hep, 4, set_hep_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"hep_get", (cmd_function)w_get_hep_generic, 3, get_hep_generic_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"hep_get", (cmd_function)w_get_hep, 4, get_hep_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"hep_get", (cmd_function)w_get_hep, 4, get_hep_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"hep_del", (cmd_function)w_del_hep, 1, del_hep_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"hep_relay", (cmd_function)w_hep_relay, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"hep_resume_sip", (cmd_function)w_hep_resume_sip, 0, 0, 0, REQUEST_ROUTE}, {"report_capture", (cmd_function)w_report_capture_1, 1, rc_fixup_1, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"report_capture", (cmd_function)w_report_capture_2, 2, rc_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"report_capture", (cmd_function)w_report_capture_3, 3, rc_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; static acmd_export_t acmds[] = { {"sip_capture", (acmd_function)async_sip_capture, 0, 0}, {"sip_capture", (acmd_function)async_sip_capture, 1, sip_capture_async_fixup}, {"report_capture", (acmd_function)w_report_capture_async_1, 1, rc_async_fixup_1}, {"report_capture", (acmd_function)w_report_capture_async_2, 2, rc_async_fixup}, {"report_capture", (acmd_function)w_report_capture_async_3, 3, rc_async_fixup}, {0, 0, 0, 0} }; static proc_export_t procs[] = { {"RAW receiver", 0, 0, raw_socket_process, 1, 0}, {0,0,0,0,0,0} }; /*! \brief * Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &db_url.s }, {"table_name", STR_PARAM, &table_name.s }, {"rtcp_table_name", STR_PARAM, &rtcp_table_name.s }, {"id_column", STR_PARAM, &id_column.s }, {"date_column", STR_PARAM, &date_column.s }, {"micro_ts_column", STR_PARAM, µ_ts_column.s }, {"method_column", STR_PARAM, &method_column.s }, {"reply_reason_column", STR_PARAM, &reply_reason_column.s }, {"ruri_column", STR_PARAM, &ruri_column.s }, {"ruri_user_column", STR_PARAM, &ruri_user_column.s }, {"from_user_column", STR_PARAM, &from_user_column.s }, {"from_tag_column", STR_PARAM, &from_tag_column.s }, {"to_user_column", STR_PARAM, &to_user_column.s }, {"to_tag_column", STR_PARAM, &to_tag_column.s }, {"pid_user_column", STR_PARAM, &pid_user_column.s }, {"contact_user_column", STR_PARAM, &contact_user_column.s }, {"auth_user_column", STR_PARAM, &auth_user_column.s }, {"callid_column", STR_PARAM, &callid_column.s}, {"callid_aleg_column", STR_PARAM, &callid_aleg_column.s}, {"via_1_column", STR_PARAM, &via_1_column.s }, {"via_1_branch_column", STR_PARAM, &via_1_branch_column.s }, {"cseq_column", STR_PARAM, &cseq_column.s }, {"diversion_column", STR_PARAM, &diversion_column.s }, {"reason_column", STR_PARAM, &reason_column.s }, {"content_type_column", STR_PARAM, &content_type_column.s }, {"authorization_column", STR_PARAM, &authorization_column.s }, {"user_agent_column", STR_PARAM, &user_agent_column.s }, {"source_ip_column", STR_PARAM, &source_ip_column.s }, {"source_port_column", STR_PARAM, &source_port_column.s}, {"destination_ip_column", STR_PARAM, &dest_ip_column.s }, {"destination_port_column", STR_PARAM, &dest_port_column.s }, {"contact_ip_column", STR_PARAM, &contact_ip_column.s }, {"contact_port_column", STR_PARAM, &contact_port_column.s }, {"originator_ip_column", STR_PARAM, &orig_ip_column.s }, {"originator_port_column", STR_PARAM, &orig_port_column.s }, {"proto_column", STR_PARAM, &proto_column.s }, {"family_column", STR_PARAM, &family_column.s }, {"rtp_stat_column", STR_PARAM, &rtp_stat_column.s }, {"type_column", STR_PARAM, &type_column.s }, {"node_column", STR_PARAM, &node_column.s }, {"msg_column", STR_PARAM, &msg_column.s }, {"capture_on", INT_PARAM, &capture_on }, {"capture_node", STR_PARAM, &capture_node.s }, {"raw_sock_children", INT_PARAM, &raw_sock_children }, {"hep_capture_on", INT_PARAM, &hep_capture_on }, {"max_async_queries", INT_PARAM, &max_async_queries }, {"raw_socket_listen", STR_PARAM, &raw_socket_listen.s }, {"raw_ipip_capture_on", INT_PARAM, &ipip_capture_on }, {"raw_moni_capture_on", INT_PARAM, &moni_capture_on }, {"raw_interface", STR_PARAM, &raw_interface.s }, {"promiscious_on", INT_PARAM, &promisc_on }, {"raw_moni_bpf_on", INT_PARAM, &bpf_on }, {"hep_route", STR_PARAM, &hep_route_name}, {0, 0, 0} }; /*! \brief * MI commands */ static mi_export_t mi_cmds[] = { { "sip_capture", 0, sip_capture_mi, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; #ifdef STATISTICS stat_var* sipcapture_req; stat_var* sipcapture_rpl; stat_export_t sipcapture_stats[] = { {"captured_requests" , 0, &sipcapture_req }, {"captured_replies" , 0, &sipcapture_rpl }, {0,0,0} }; #endif static module_dependency_t *get_deps_hep(param_export_t *param) { int hep_on = *(int *)param->param_pointer; if (hep_on == 0) return NULL; return alloc_module_dep(MOD_TYPE_DEFAULT, "proto_hep", DEP_ABORT); } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ {"hep_capture_on", get_deps_hep}, { NULL, NULL }, }, }; /** * pseudo-variables */ static pv_export_t mod_items[] = { {{"hep_net", sizeof("hep_net")-1}, 1201, pv_get_hep_net, 0, pv_parse_hep_net_name, 0, 0, 0}, {{"HEPVERSION", sizeof("HEPVERSION")-1}, 1202, pv_get_hep_version, 0, 0, 0, 0, 0}, {{0, 0}, 0, 0, 0, 0, 0, 0, 0} }; /*! \brief module exports */ struct module_exports exports = { "sipcapture", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /*!< dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /*!< Exported functions */ acmds, /*!< Exported async functions */ params, /*!< Exported parameters */ #ifdef STATISTICS sipcapture_stats, /*!< exported statistics */ #else 0, /*!< exported statistics */ #endif mi_cmds, /*!< exported MI functions */ mod_items, /*!< exported pseudo-variables */ procs, /*!< extra processes */ mod_init, /*!< module initialization function */ 0, /*!< response function */ destroy, /*!< destroy function */ child_init /*!< child initialization function */ }; static int parse_hep_route(char *val) { static const str hep_sip_route = str_init("sip"); static const str hep_no_route = str_init("none"); str route_name = {val, strlen(val)}; if ( route_name.len == hep_no_route.len && strncasecmp(route_name.s, hep_no_route.s, hep_no_route.len ) == 0) { hep_route_id = HEP_NO_ROUTE; } else if ( route_name.len == hep_sip_route.len && strncasecmp(route_name.s, hep_sip_route.s, hep_sip_route.len ) == 0) { hep_route_id = HEP_SIP_ROUTE; } else { hep_route_id=get_script_route_ID_by_name( route_name.s, rlist, RT_NO); if ( hep_route_id == -1 ) { LM_ERR("route <%s> not defined!\n", route_name.s); return -1; } } return 0; } void build_dummy_msg(void) { memset(&dummy_req, 0, sizeof(struct sip_msg)); dummy_req.first_line.type = SIP_REQUEST; dummy_req.first_line.u.request.method.s= "DUMMY"; dummy_req.first_line.u.request.method.len= 5; dummy_req.first_line.u.request.uri.s= "sip:user@domain.com"; dummy_req.first_line.u.request.uri.len= 19; dummy_req.rcv.src_ip.af = AF_INET; dummy_req.rcv.dst_ip.af = AF_INET; } void parse_table_str(str* table_s, tz_table_t* tz_table) { if ((tz_table->suffix.s=q_memchr(table_s->s, '%', table_s->len)) == NULL) { tz_table->prefix = *table_s; tz_table->suffix.len = 0; } else { tz_table->prefix.s = table_s->s; tz_table->prefix.len = tz_table->suffix.s - tz_table->prefix.s; tz_table->suffix.len = strlen(tz_table->suffix.s); if (tz_table->prefix.len == 0) tz_table->prefix.s = NULL; } } /*! \brief Initialize sipcapture module */ static int mod_init(void) { int i; struct ip_addr *ip = NULL; /* init db keys */ db_keys[0] = &id_column; db_keys[1] = &date_column; db_keys[2] = µ_ts_column; db_keys[3] = &method_column; db_keys[4] = &reply_reason_column; db_keys[5] = &ruri_column; db_keys[6] = &ruri_user_column; db_keys[7] = &from_user_column; db_keys[8] = &from_tag_column; db_keys[9] = &to_user_column; db_keys[10] = &to_tag_column; db_keys[11] = &pid_user_column; db_keys[12] = &contact_user_column; db_keys[13] = &auth_user_column; db_keys[14] = &callid_column; db_keys[15] = &callid_aleg_column; db_keys[16] = &via_1_column; db_keys[17] = &via_1_branch_column; db_keys[18] = &cseq_column; db_keys[19] = &reason_column; db_keys[20] = &content_type_column; db_keys[21] = &authorization_column; db_keys[22] = &user_agent_column; db_keys[23] = &source_ip_column; db_keys[24] = &source_port_column; db_keys[25] = &dest_ip_column; db_keys[26] = &dest_port_column; db_keys[27] = &contact_ip_column; db_keys[28] = &contact_port_column; db_keys[29] = &orig_ip_column; db_keys[30] = &orig_port_column; db_keys[31] = &proto_column; db_keys[32] = &family_column; db_keys[33] = &rtp_stat_column; db_keys[34] = &type_column; db_keys[35] = &node_column; db_keys[36] = &correlation_column; db_keys[37] = &from_domain_column; db_keys[38] = &to_domain_column; db_keys[39] = &ruri_domain_column; db_keys[40] = &msg_column; set_rtcp_keys(); #ifdef STATISTICS /* register statistics */ if (register_module_stats(exports.name, sipcapture_stats)!=0) { LM_ERR("failed to register core statistics\n"); return -1; } #endif /* check if we need to start extra process */ procs[0].no = (ipip_capture_on || moni_capture_on) ? raw_sock_children:0; table_name.len = strlen(table_name.s); rtcp_table_name.len = strlen(rtcp_table_name.s); date_column.len = strlen(date_column.s); id_column.len = strlen(id_column.s); micro_ts_column.len = strlen(micro_ts_column.s); method_column.len = strlen(method_column.s); reply_reason_column.len = strlen(reply_reason_column.s); ruri_column.len = strlen(ruri_column.s); ruri_user_column.len = strlen(ruri_user_column.s); from_user_column.len = strlen(from_user_column.s); from_tag_column.len = strlen(from_tag_column.s); to_user_column.len = strlen(to_user_column.s); pid_user_column.len = strlen(pid_user_column.s); contact_user_column.len = strlen(contact_user_column.s); auth_user_column.len = strlen(auth_user_column.s); callid_column.len = strlen(callid_column.s); via_1_column.len = strlen(via_1_column.s); via_1_branch_column.len = strlen(via_1_branch_column.s); cseq_column.len = strlen(cseq_column.s); diversion_column.len = strlen(diversion_column.s); reason_column.len = strlen(reason_column.s); content_type_column.len = strlen(content_type_column.s); authorization_column.len = strlen(authorization_column.s); user_agent_column.len = strlen(user_agent_column.s); source_ip_column.len = strlen(source_ip_column.s); source_port_column.len = strlen(source_port_column.s); dest_ip_column.len = strlen(dest_ip_column.s); dest_port_column.len = strlen(dest_port_column.s); contact_ip_column.len = strlen(contact_ip_column.s); contact_port_column.len = strlen(contact_port_column.s); orig_ip_column.len = strlen(orig_ip_column.s); orig_port_column.len = strlen(orig_port_column.s); proto_column.len = strlen(proto_column.s); family_column.len = strlen(family_column.s); type_column.len = strlen(type_column.s); rtp_stat_column.len = strlen(rtp_stat_column.s); node_column.len = strlen(node_column.s); msg_column.len = strlen(msg_column.s); capture_node.len = strlen(capture_node.s); /* extract prefix and suffix from table name */ parse_table_str(&table_name, &tz_table); parse_table_str(&rtcp_table_name, &rc_table); if(raw_socket_listen.s) raw_socket_listen.len = strlen(raw_socket_listen.s); if(raw_interface.s) raw_interface.len = strlen(raw_interface.s); if (hep_capture_on) { load_hep = (load_hep_f)find_export("load_hep", 1, 0); if (!load_hep) { LM_ERR("Can't bind proto hep!\n"); return -1; } if (load_hep(&hep_api)) { LM_ERR("can't bind proto hep\n"); return -1; } if (hep_api.register_hep_cb(hep_msg_received)) { LM_ERR("failed to register hep callback\n"); return -1; } if (hep_route_name != NULL) { if ( parse_hep_route(hep_route_name) < 0 ) { LM_ERR("bad hep route name %s\n", hep_route_name); return -1; } if (hep_route_id > HEP_SIP_ROUTE) { /* builds a dummy message for being able to use the hep route */ build_dummy_msg(); } } /* db_url is mandatory if sip_capture is used */ if (((is_script_func_used("sip_capture", -1) || is_script_async_func_used("sip_capture", -1)) || hep_route_id == HEP_NO_ROUTE) || (is_script_func_used("report_capture", -1) || is_script_async_func_used("report_capture", -1))) { init_db_url(db_url, 0); } else { init_db_url(db_url, 1); } } else { if ((is_script_func_used("sip_capture", -1) || is_script_async_func_used("sip_capture", -1))) { init_db_url(db_url, 0); } else { init_db_url(db_url, 1); } } if (db_url.s && db_url.len) { /* Find a database module */ if (db_bind_mod(&db_url, &db_funcs)) { LM_ERR("unable to bind database module\n"); return -1; } if (!DB_CAPABILITY(db_funcs, DB_CAP_INSERT)) { LM_ERR("database modules does not provide all functions needed" " by module\n"); return -1; } if (DB_CAPABILITY(db_funcs, DB_CAP_ASYNC_RAW_QUERY)) { if (!HAVE_SHARED_QUERIES) { for (i=0; i < 2; i++) { global_async_query = pkg_malloc(sizeof(struct _async_query)); if (global_async_query == NULL) { LM_ERR("no more pkg\n"); return -1; } memset(global_async_query, 0, sizeof(struct _async_query)); if (i==0) { tz_global.as_qry = global_async_query; } else { rc_global.as_qry = global_async_query; } } } else { for (i=0; i < 2; i++) { global_async_query = shm_malloc(sizeof(struct _async_query)); if (global_async_query == NULL) { LM_ERR("no more shm\n"); return -1; } memset(global_async_query, 0, sizeof(struct _async_query)); LAST_SUFFIX(global_async_query).s = shm_malloc(CAPTURE_TABLE_MAX_LEN); if (global_async_query == NULL) { LM_ERR("no more shm\n"); return -1; } LAST_SUFFIX(global_async_query).len = 0; INIT_QUERY_LOCK(global_async_query); if( i == 0) { tz_global.as_qry = global_async_query; } else { rc_global.as_qry = global_async_query; } } } tz_global.table = &tz_table; rc_global.table = &rc_table; } /*Check the table name*/ if(!table_name.len) { LM_ERR("table_name is not defined or empty\n"); return -1; } } capture_on_flag = (int*)shm_malloc(sizeof(int)); if(capture_on_flag==NULL) { LM_ERR("no more shm memory left\n"); return -1; } *capture_on_flag = capture_on; if(ipip_capture_on && moni_capture_on) { LM_ERR("only one RAW mode is supported. Please disable ipip_capture_on or moni_capture_on\n"); return -1; } /* raw processes for IPIP encapsulation */ if (ipip_capture_on || moni_capture_on) { if(extract_host_port() && (((ip=str2ip(&raw_socket_listen)) == NULL) && ((ip=str2ip6(&raw_socket_listen)) == NULL) )) { LM_ERR("bad RAW IP: %.*s\n", raw_socket_listen.len, raw_socket_listen.s); return -1; } if(moni_capture_on && !moni_port_start) { LM_ERR("Please define port/portrange in 'raw_socket_listen', before \ activate monitoring capture\n"); return -1; } raw_sock_desc = raw_capture_socket(raw_socket_listen.len ? ip : 0, raw_interface.len ? &raw_interface : 0, moni_port_start, moni_port_end , ipip_capture_on ? IPPROTO_IPIP : htons(0x0800)); if(raw_sock_desc < 0) { LM_ERR("could not initialize raw udp socket:" " %s (%d)\n", strerror(errno), errno); if (errno == EPERM) LM_ERR("could not initialize raw socket on startup" " due to inadequate permissions, please" " restart as root or with CAP_NET_RAW\n"); return -1; } if(promisc_on && raw_interface.s && raw_interface.len) { memset(&ifr, 0, sizeof(ifr)); memcpy(ifr.ifr_name, raw_interface.s, raw_interface.len); #ifdef __OS_linux if(ioctl(raw_sock_desc, SIOCGIFFLAGS, &ifr) < 0) { LM_ERR("could not get flags from interface [%.*s]:" " %s (%d)\n", raw_interface.len, raw_interface.s, strerror(errno), errno); goto error; } ifr.ifr_flags |= IFF_PROMISC; if (ioctl(raw_sock_desc, SIOCSIFFLAGS, &ifr) < 0) { LM_ERR("could not set PROMISC flag to interface [%.*s]:" " %s (%d)\n", raw_interface.len, raw_interface.s, strerror(errno), errno); goto error; } #endif } } return 0; #ifdef __OS_linux error: if(raw_sock_desc) close(raw_sock_desc); return -1; #endif } /* * returns hep index as an integer from a string */ static int parse_hep_index(str *s_index) { int p; int index=0; int hex_mode=0; unsigned int dec_num; if (s_index == NULL || s_index->s == NULL || s_index->len == 0) { LM_ERR("null index!\n"); return -1; } if (!isdigit(s_index->s[0])) return 0; /* cut the '0x' in the beginning if exists */ if (s_index->len > 2 && s_index->s[0] == '0' && s_index->s[1] == 'x') { s_index->s += 2; s_index->len -= 2; hex_mode=1; } /**/ while (s_index->s[0] == '0') (s_index->s++, s_index->len--); /* decimal */ if (!hex_mode) { if (str2int(s_index, &dec_num) < 0) { LM_ERR("Chunk identifier begins with a digit " "but it's not a valid number!\n"); return -1; } return dec_num; } for (p=0; p < s_index->len; p++) { switch (s_index->s[p]) { case 'a': case 'A': index = (index<<4) + 0xA; break; case 'b': case 'B': index = (index<<4) + 0xB; break; case 'c': case 'C': index = (index<<4) + 0xC; break; case 'd': case 'D': index = (index<<4) + 0xD; break; case 'e': case 'E': index = (index<<4) + 0xE; break; case 'f': case 'F': index = (index<<4) + 0xF; break; default: if (s_index->s[p] >= '0' && s_index->s[p] <= '9') { index = (index<<4) + (s_index->s[p] -'0'); break; } return -1; } } return index==0?-1:index; /* index can't be 0 */ } enum hep_state { STATE_NONE=0, SOURCE=1, DEST, PROTO, TIME, AG_ID, PLOAD}; static int parse_hep_name(str *s_name, unsigned *chunk) { #define CHECK_IS_VALID(_str, _pattern) \ do { \ if (_str.len < (sizeof(_pattern)-1)) \ goto error; \ } while(0); int ret=0; int p=0; enum hep_state state=STATE_NONE; str s; if (s_name == NULL || s_name->s == NULL || s_name->len == 0 || chunk == NULL) { LM_ERR("bad input!\n"); return -1; } str_trim_spaces_lr(*s_name); ret=parse_hep_index(s_name); if (ret<0) { goto error; } else if (ret > 0) { *chunk=ret; return 0; } /* else it's a name; continue */ s = *s_name; if (s.len < 4) { LM_ERR("bad chunk name <%.*s>!\n", s_name->len, s_name->s); return -1; } switch (LOWER_DWORD(s.s[0], s.s[1], s.s[2], s.s[3])) { case LOWER_DWORD('p','r','o','t'): state=PROTO; break; case LOWER_DWORD('s','r','c','_'): state=SOURCE; break; case LOWER_DWORD('d','s','t','_'): state=DEST; break; case LOWER_DWORD('t','i','m','e'): state=TIME; break; case LOWER_DWORD('c','a','p','t'): state=AG_ID; break; case LOWER_DWORD('p','a','y','l'): state=PLOAD; break; default: goto error; } p+=4; switch (state) { case PROTO: /* we need at least 8 bytes protXXXX */ CHECK_IS_VALID(s, "protXXXX"); switch LOWER_DWORD(s.s[p], s.s[p+1], s.s[p+2], s.s[p+3]){ /*proto_family*/ case LOWER_DWORD('o','_','f','a'): p+=4; CHECK_IS_VALID(s, "proto_faXXXX"); if (LOWER_DWORD(s.s[p],s.s[p+1], s.s[p+2], s.s[p+3]) != LOWER_DWORD('m','i','l','y')) goto error; *chunk=HEP_PROTO_FAMILY; break; /* protocol id */ case LOWER_DWORD('o','_','i','d'): *chunk=HEP_PROTO_ID; break; case LOWER_DWORD('o','_','t','y'): p+=4; CHECK_IS_VALID(s, "proto_tyXX"); if (LOWER_WORD(s.s[p],s.s[p+1]) != LOWER_WORD('p','e')) goto error; *chunk=HEP_PROTO_TYPE; break; default: goto error; } break; case SOURCE: CHECK_IS_VALID(s, "src_XX"); switch LOWER_WORD(s.s[p], s.s[p+1]) { case LOWER_WORD('i', 'p'): *chunk=HEP_IPV4_SRC; break; case LOWER_WORD('p', 'o'): CHECK_IS_VALID(s, "src_poXX"); p+=2; if (LOWER_WORD(s.s[p], s.s[p+1]) != LOWER_WORD('r','t')) goto error; *chunk=HEP_SRC_PORT; break; default: goto error; } break; case DEST: CHECK_IS_VALID(s, "dst_XX"); switch LOWER_WORD(s.s[p], s.s[p+1]) { case LOWER_WORD('i', 'p'): *chunk=HEP_IPV4_DST; break; case LOWER_WORD('p', 'o'): CHECK_IS_VALID(s, "dst_poXX"); p+=2; if (LOWER_WORD(s.s[p], s.s[p+1]) != LOWER_WORD('r','t')) goto error; *chunk=HEP_DST_PORT; break; default: goto error; } break; case TIME: CHECK_IS_VALID(s, "timestamp"); if (s.len == sizeof("timestamp")-1 && !strncasecmp(s.s, "timestamp", s.len)) { *chunk = HEP_TIMESTAMP; break; } CHECK_IS_VALID(s, "timestampXXX"); if (s.len == sizeof("timestamp_us")-1 && !strncasecmp(s.s, "timestamp_us", s.len)) { *chunk=HEP_TIMESTAMP_US; break; } goto error; case AG_ID: CHECK_IS_VALID(s, "captXXXXXXXX"); if ((LOWER_DWORD(s.s[p], s.s[p+1], s.s[p+2], s.s[p+3]) != LOWER_DWORD('a','g','e','n')) || (p+=4,0) || LOWER_DWORD(s.s[p], s.s[p+1], s.s[p+2], s.s[p+3]) != LOWER_DWORD('t','_','i','d')) goto error; *chunk = HEP_AGENT_ID; break; case PLOAD: CHECK_IS_VALID(s, "paylXXX"); if ((LOWER_WORD(s.s[p], s.s[p+1]) != LOWER_WORD('o', 'a') || s.s[p+2] != 'd')) goto error; *chunk = HEP_PAYLOAD; break; default: goto error; } return 0; error: LM_ERR("invalid hepvar name <%.*s>! parsed until <%.*s>!\n", s_name->len, s_name->s, s_name->len-p, s_name->s+p); return -1; #undef CHECK_IS_VALID } static int pv_parse_hep_net_name(pv_spec_p sp, str* in) { pv_spec_p e; unsigned id; if (in==NULL || in->s == NULL || in->len == 0) { LM_ERR("bad name!\n"); return -1; } str_trim_spaces_lr(*in); if (in->s[0] != PV_MARKER) { if (parse_hep_name(in, &id) < 0) { LM_ERR("Invalid hep net name <%.*s>!\n", in->len, in->s); return -1; } sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.name.n = id; sp->pvp.pvn.u.isname.type = 0; } else { e = pkg_malloc(sizeof(pv_spec_p)); if (e==NULL) { LM_ERR("no more pkg mem!\n"); return -1; } if (pv_parse_spec(in, e)==NULL) { LM_ERR("invalid pvar!\n"); return -1; } sp->pvp.pvn.u.dname = (void *)e; sp->pvp.pvn.type = PV_NAME_PVAR; } return 0; } static int get_hepvar_name(struct sip_msg *msg, pv_param_t *param, unsigned int *chunk) { pv_spec_p sp; pv_value_t value; if (param->pvn.type == PV_NAME_PVAR) { sp = param->pvn.u.dname; if (pv_get_spec_value(msg, sp, &value) < 0) { LM_ERR("failed to get name pv value!\n"); return -1; } if (!(value.flags&PV_VAL_STR)) { LM_ERR("invalid name!\n"); return -1; } if (parse_hep_name(&value.rs, chunk) < 0) { LM_ERR("invalid name!\n"); return -1; } } else { *chunk = param->pvn.u.isname.name.n; } return 0; } static int get_hep_chunk(struct hepv3* h3, unsigned int chunk_id, pv_value_t *res) { #define SET_PVAL_INT(__pval__, __ival__) \ do { \ __pval__->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT;\ __pval__->ri = __ival__; \ __pval__->rs.len += \ snprintf(__pval__->rs.s + __pval__->rs.len, \ HEPBUF_LEN, "%d", __ival__); \ } while(0); #define SET_PVAL_STR(__pval__, __sval__) \ do { \ __pval__->flags = PV_VAL_STR; \ __pval__->rs.len += \ snprintf(__pval__->rs.s + __pval__->rs.len, \ HEPBUF_LEN, "%.*s", __sval__.len, __sval__.s); \ } while(0); char addr[INET6_ADDRSTRLEN]; time_t time; str addr_str; str time_str; str payload_str; hep_str.len = 0; res->rs = hep_str; res->flags = PV_VAL_STR; switch (chunk_id) { /* ip family */ case HEP_PROTO_FAMILY: if (h3->hg.ip_family.chunk.length == 0) goto chunk_not_set; if (h3->hg.ip_family.data == AF_INET) { SET_PVAL_STR(res, afinet_str); } else { SET_PVAL_STR(res, afinet6_str); } break; /* ip protocol id */ case HEP_PROTO_ID: if (h3->hg.ip_proto.chunk.length == 0) goto chunk_not_set; if (h3->hg.ip_proto.datahg.ip_proto.data>PROTO_WS) { LM_ALERT("Invalid proto!Probably a new one was added %d\n", h3->hg.ip_proto.data); return -1; } SET_PVAL_STR(res, hep_net_protos[h3->hg.ip_proto.data]); break; /* ipv4/6 source * no difference between ipv4/ipv6 from script level; it only returns * the address it the format that it is */ case HEP_IPV4_SRC: case HEP_IPV6_SRC: if (h3->hg.ip_family.data == AF_INET) { if (h3->addr.ip4_addr.src_ip4.chunk.length == 0) goto chunk_not_set; if (inet_ntop(AF_INET, &h3->addr.ip4_addr.src_ip4.data, addr, INET_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } else { if (h3->addr.ip6_addr.src_ip6.chunk.length == 0) goto chunk_not_set; if (inet_ntop(AF_INET6, &h3->addr.ip6_addr.src_ip6.data, addr, INET6_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } addr_str.s = addr; addr_str.len = strlen(addr); SET_PVAL_STR(res, addr_str); break; /* ipv4/6 dest */ case HEP_IPV4_DST: case HEP_IPV6_DST: if (h3->hg.ip_family.data == AF_INET) { if (h3->addr.ip4_addr.dst_ip4.chunk.length == 0) goto chunk_not_set; if (inet_ntop(AF_INET, &h3->addr.ip4_addr.dst_ip4.data, addr, INET_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } else { if (h3->addr.ip6_addr.dst_ip6.chunk.length == 0) goto chunk_not_set; if (inet_ntop(AF_INET6, &h3->addr.ip6_addr.dst_ip6.data, addr, INET6_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } addr_str.s = addr; addr_str.len = strlen(addr); SET_PVAL_STR(res, addr_str); break; /* ipv6 source */ /* source port */ case HEP_SRC_PORT: if (h3->hg.src_port.chunk.length == 0) goto chunk_not_set; SET_PVAL_INT(res, h3->hg.src_port.data); break; /* destination port */ case HEP_DST_PORT: if (h3->hg.dst_port.chunk.length == 0) goto chunk_not_set; SET_PVAL_INT(res, h3->hg.dst_port.data); break; /* timestamp */ case HEP_TIMESTAMP: if (h3->hg.time_sec.chunk.length == 0) goto chunk_not_set; time = h3->hg.time_sec.data; time_str.s = ctime(&time); time_str.len = strlen(time_str.s)-1; SET_PVAL_STR(res, time_str); break; /* timestamp us offset */ case HEP_TIMESTAMP_US: if (h3->hg.time_usec.chunk.length == 0) goto chunk_not_set; SET_PVAL_INT(res, h3->hg.time_usec.data); break; /* proto type (SIP, ...) */ case HEP_PROTO_TYPE: if (h3->hg.proto_t.chunk.length == 0) goto chunk_not_set; if (h3->hg.proto_t.data < 0 || h3->hg.proto_t.data > (sizeof(hep_app_protos)/sizeof(str))-1) { LM_DBG("Not a HEP default defined proto %d\n", h3->hg.ip_proto.data); SET_PVAL_INT(res, h3->hg.proto_t.data); } else { SET_PVAL_STR(res, hep_app_protos[h3->hg.proto_t.data]); } break; /* capture agent id */ case HEP_AGENT_ID: if (h3->hg.capt_id.chunk.length == 0) goto chunk_not_set; SET_PVAL_INT(res, h3->hg.capt_id.data); break; /* payload */ case HEP_PAYLOAD: case HEP_COMPRESSED_PAYLOAD/* gzipped payload */: if (h3->payload_chunk.chunk.length == 0) goto chunk_not_set; payload_str.s = h3->payload_chunk.data; payload_str.len = h3->payload_chunk.chunk.length - sizeof(hep_chunk_t); SET_PVAL_STR(res, payload_str); break; } return 0; chunk_not_set: LM_DBG("generic chunk <%d> not set!\n", chunk_id); return -1; #undef SET_PVAL_STR #undef SET_PVAL_INT } static int del_hep_chunk(struct hepv3* h3, unsigned int chunk_id) { switch (chunk_id) { /* ip family */ case HEP_PROTO_FAMILY: h3->hg.ip_family.chunk.length = 0; break; /* ip protocol id */ case HEP_PROTO_ID: h3->hg.ip_proto.chunk.length = 0; break; case HEP_IPV4_SRC: case HEP_IPV6_SRC: if (h3->hg.ip_family.data == AF_INET) h3->addr.ip4_addr.src_ip4.chunk.length = 0; else h3->addr.ip6_addr.src_ip6.chunk.length = 0; break; /* ipv4/6 dest */ case HEP_IPV4_DST: case HEP_IPV6_DST: if (h3->hg.ip_family.data == AF_INET) h3->addr.ip4_addr.dst_ip4.chunk.length = 0; else h3->addr.ip6_addr.dst_ip6.chunk.length = 0; break; /* ipv6 source */ /* source port */ case HEP_SRC_PORT: h3->hg.src_port.chunk.length = 0; break; /* destination port */ case HEP_DST_PORT: h3->hg.dst_port.chunk.length = 0; break; /* timestamp */ case HEP_TIMESTAMP: h3->hg.time_sec.chunk.length = 0; break; /* timestamp us offset */ case HEP_TIMESTAMP_US: h3->hg.time_usec.chunk.length = 0; break; /* proto type (SIP, ...) */ case HEP_PROTO_TYPE: h3->hg.proto_t.chunk.length = 0; break; /* capture agent id */ case HEP_AGENT_ID: h3->hg.capt_id.chunk.length = 0; break; /* payload */ case HEP_PAYLOAD: case HEP_COMPRESSED_PAYLOAD/* gzipped payload */: h3->payload_chunk.chunk.length = 0; break; } return 1; } static int pv_get_hep_net(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { #define SET_PVAL_INT(__pval__, __ival__) \ do { \ __pval__->flags = PV_VAL_STR|PV_VAL_INT; \ __pval__->ri = __ival__; \ __pval__->rs.len += \ snprintf(__pval__->rs.s + __pval__->rs.len, \ HEPBUF_LEN, "%d", __ival__); \ } while(0); #define SET_PVAL_STR(__pval__, __sval__) \ do { \ __pval__->flags = PV_VAL_STR; \ __pval__->rs.len += \ snprintf(__pval__->rs.s + __pval__->rs.len, \ HEPBUF_LEN, "%.*s", __sval__.len, __sval__.s); \ } while(0); char addr[INET6_ADDRSTRLEN]; unsigned net_info_type; struct hep_context *ctx; struct receive_info *ri; str addr_str; if (msg == NULL) { LM_ERR("invalid message!\n"); return -1; } ctx = HEP_GET_CONTEXT(hep_api); if (ctx == NULL) { LM_ERR("Hep context not there!"); return -1; } ri = &ctx->ri; if (get_hepvar_name(msg, param, &net_info_type) < 0) { LM_ERR("failed to get variable index/name!\n"); return -1; } if (net_info_type < HEP_PROTO_FAMILY || net_info_type > HEP_DST_PORT) { LM_ERR("Invalid hep net var name!\n"); return -1; } hep_str.len = 0; memset(hep_str.s, 0, HEPBUF_LEN); res->rs = hep_str; res->flags = PV_VAL_STR; switch (net_info_type) { /* ip family */ case HEP_PROTO_FAMILY: if (ri->src_ip.af == AF_INET) { SET_PVAL_STR(res, afinet_str); } else { SET_PVAL_STR(res, afinet6_str); } break; /* ip protocol id */ case HEP_PROTO_ID: if (ri->proto < PROTO_UDP || ri->proto >= PROTO_OTHER) { LM_ALERT("Invalid proto!Maybe a new one was added %d\n", ri->proto); return -1; } SET_PVAL_STR(res, hep_net_protos[ri->proto]); break; /* ipv4 source */ case HEP_IPV4_SRC: case HEP_IPV6_SRC: if (ri->src_ip.af == AF_INET) { if (inet_ntop(AF_INET, &ri->src_ip.u.addr, addr, INET_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } else { if (inet_ntop(AF_INET6, &ri->src_ip.u.addr, addr, INET6_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } addr_str.s = addr; addr_str.len = strlen(addr); SET_PVAL_STR(res, addr_str); break; /* ipv4 dest */ case HEP_IPV4_DST: case HEP_IPV6_DST: if (ri->dst_ip.af == AF_INET) { if (inet_ntop(AF_INET, &ri->dst_ip.u.addr, addr, INET_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } else { if (inet_ntop(AF_INET6, &ri->dst_ip.u.addr, addr, INET6_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } addr_str.s = addr; addr_str.len = strlen(addr); SET_PVAL_STR(res, addr_str); break; /* source port */ case HEP_SRC_PORT: SET_PVAL_INT(res, ri->src_port); break; /* destination port */ case HEP_DST_PORT: SET_PVAL_INT(res, ri->dst_port); break; default: break; } return 0; #undef SET_PVAL_STR #undef SET_PVAL_INT } static int pv_get_hep_version(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct hep_context *ctx; ctx = HEP_GET_CONTEXT(hep_api); if (ctx == NULL) { LM_ERR("Hep context not there!"); return -1; } res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; res->ri = ctx->h.version; /* can't have bogus version number here since it's been already * checked in proto_hep */ res->rs = hep_str; res->rs.s = int2str(ctx->h.version, &res->rs.len); return 0; } static int set_generic_hep_chunk(struct hepv3* h3, unsigned chunk_id, str *data) { #define CHECK_LEN(_str, _len, _fail_fmt, ...) \ do { \ if (_str->len < _len) { \ LM_ERR("invalid "#_fail_fmt, __VA_ARGS__); \ return -1; \ } \ } while(0); #define CHECK_PROTO_LEN(_str, _len) CHECK_LEN(_str, _len, \ "invalid protocol <%.*s>!\n", _str->len, _str->s); #define CHECK_PROTOT_LEN(_str, _len) CHECK_LEN(_str, _len, \ "invalid prot_t <%.*s>!\n", _str->len, _str->s); #define RETURN_ERROR(_format, ...) \ do { \ LM_ERR(_format, __VA_ARGS__); \ return -1; \ } while (0); unsigned int port; unsigned int capture_id; switch (chunk_id) { /* ip family - this can't be set; it will be automatically set * if you change ip addresses */ case HEP_PROTO_FAMILY: h3->hg.ip_family.chunk.length = sizeof(hep_chunk_uint8_t); LM_DBG("Proto family can't be set!" " It shall be automatically updated when you change addresses!\n"); return 0; /* ip protocol id */ case HEP_PROTO_ID: /** possible values(string) * UDP * TCP * TLS * SCTP * WS */ h3->hg.ip_proto.chunk.length = sizeof(hep_chunk_uint8_t); CHECK_PROTO_LEN(data, 2); switch (LOWER_WORD(data->s[0], data->s[1])) { case LOWER_WORD('u','d'): CHECK_PROTO_LEN(data, 3); if (LOWER_BYTE(data->s[2]) != 'p') RETURN_ERROR("invalid proto %.*s\n", data->len, data->s); h3->hg.ip_proto.data = PROTO_UDP; break; case LOWER_WORD('t','c'): CHECK_PROTO_LEN(data, 3); if (LOWER_BYTE(data->s[2]) != 'p') RETURN_ERROR("invalid proto %.*s\n", data->len, data->s); h3->hg.ip_proto.data = PROTO_TCP; break; case LOWER_WORD('t','l'): if (LOWER_BYTE(data->s[2]) != 's') RETURN_ERROR("invalid proto %.*s\n", data->len, data->s); h3->hg.ip_proto.data = PROTO_TLS; break; case LOWER_WORD('s','c'): if (LOWER_WORD(data->s[2], data->s[3]) != LOWER_WORD('t', 'p')) RETURN_ERROR("invalid proto %.*s\n", data->len, data->s); h3->hg.ip_proto.data = PROTO_SCTP; break; case LOWER_WORD('w','s'): h3->hg.ip_proto.data = PROTO_WS; break; default: LM_ERR("invalid protocol <%.*s>!\n", data->len, data->s); return -1; } break; /* ipv4 source */ case HEP_IPV4_SRC: case HEP_IPV6_SRC: /** possible values(string) * ip address in human readable format */ if (inet_pton(AF_INET, data->s, &h3->addr.ip4_addr.src_ip4.data) == 0) { /* check if it's ipV6*/ if (inet_pton(AF_INET6, data->s, &h3->addr.ip6_addr.src_ip6.data) == 0) { RETURN_ERROR("address <<%.*s>> it's neither IPv4 nor IPv6!\n", data->len, data->s); } else { /* it's IPv6 change ip family*/ if (h3->hg.ip_family.data == AF_INET) { LM_DBG("You changed source address in hep header to IPv6!" " You also have to change destination IP in order to work!\n"); h3->hg.ip_family.data = AF_INET6; } h3->addr.ip6_addr.src_ip6.chunk.length = sizeof(hep_chunk_ip6_t); } } else { if (h3->hg.ip_family.data == AF_INET6) { LM_DBG("You changed source address in hep header to IPv6!" " You also have to change destination IP in order to work!\n"); h3->hg.ip_family.data = AF_INET; } h3->addr.ip4_addr.src_ip4.chunk.length = sizeof(hep_chunk_ip4_t); } break; /* ipv4 dest */ case HEP_IPV4_DST: case HEP_IPV6_DST: /** possible values(string) * ip address in human readable format */ if (inet_pton(AF_INET, data->s, &h3->addr.ip4_addr.dst_ip4.data) == 0) { /* check if it's ipV6*/ if (inet_pton(AF_INET6, data->s, &h3->addr.ip6_addr.dst_ip6.data) == 0) { RETURN_ERROR("address <<%.*s>> it's neither IPv4 nor IPv6!\n", data->len, data->s); } else { /* it's IPv6 change ip family*/ if (h3->hg.ip_family.data == AF_INET) { LM_DBG("You changed source address in hep header to IPv6!" " You also have to change destination IP in order to work!\n"); h3->hg.ip_family.data = AF_INET6; } h3->addr.ip6_addr.dst_ip6.chunk.length = sizeof(hep_chunk_ip6_t); } } else { if (h3->hg.ip_family.data == AF_INET6) { LM_DBG("You changed source address in hep header to IPv6!" " You also have to change destination IP in order to work!\n"); h3->hg.ip_family.data = AF_INET; } h3->addr.ip4_addr.dst_ip4.chunk.length = sizeof(hep_chunk_ip4_t); } break; /* source port */ case HEP_SRC_PORT: /** possible values(string/int) * valid port */ if (str2int(data, &port) < 0) RETURN_ERROR("invalid port <%.*s>!\n", data->len, data->s); if (port > 65535) RETURN_ERROR("port not in range <%d>!\n", port); h3->hg.src_port.data = port; h3->hg.src_port.chunk.length = sizeof(hep_chunk_uint16_t); break; /* destination port */ case HEP_DST_PORT: /** possible values(string/int) * valid port */ if (str2int(data, &port) < 0) RETURN_ERROR("invalid port <%.*s>!\n", data->len, data->s); if (port > 65535) RETURN_ERROR("port not in range <%d>!\n", port); h3->hg.dst_port.data = port; h3->hg.dst_port.chunk.length = sizeof(hep_chunk_uint16_t); break; case HEP_TIMESTAMP: case HEP_TIMESTAMP_US: LM_WARN("Timestamp can't be set!\n"); return 0; case HEP_PROTO_TYPE: /** possible values(string) * SIP | XMPP | SDP | RTP | RTCP | MGCP | MEGACO * | M2UA | M3UA | IAX | H322 | H321 */ h3->hg.proto_t.chunk.length = sizeof(hep_chunk_uint8_t); CHECK_PROTOT_LEN(data, 3); switch (LOWER_DWORD(data->s[0], data->s[1], data->s[2], ((data->len > 3) ? data->s[3] : 0))) { case LOWER_DWORD('s','i','p',0): h3->hg.proto_t.data = 0x01; break; case LOWER_DWORD('x','m','p','p'): h3->hg.proto_t.data = 0x02; break; case LOWER_DWORD('s','d','p',0): h3->hg.proto_t.data = 0x03; break; case LOWER_DWORD('r','t','p',0): h3->hg.proto_t.data = 0x04; break; case LOWER_DWORD('r','t','c','p'): h3->hg.proto_t.data = 0x05; break; case LOWER_DWORD('m','g','c','p'): h3->hg.proto_t.data = 0x06; break; case LOWER_DWORD('m','e','g','a'): CHECK_PROTOT_LEN(data, 6) if ((LOWER_BYTE(data->s[4]) != 'c' && LOWER_BYTE(data->s[5]) != 'o')) RETURN_ERROR("invalid prot_t type <%.*s>!\n", data->len, data->s); h3->hg.proto_t.data = 0x07; break; case LOWER_DWORD('m','2','u','a'): h3->hg.proto_t.data = 0x08; break; case LOWER_DWORD('m','3','u','a'): h3->hg.proto_t.data = 0x09; break; case LOWER_DWORD('i','a','x',0): h3->hg.proto_t.data = 0x0A; break; case LOWER_DWORD('h','3','2','2'): h3->hg.proto_t.data = 0x0B; break; case LOWER_DWORD('h','3','2','1'): h3->hg.proto_t.data = 0x0C; break; default: RETURN_ERROR("invalid prot_t type <%.*s>!\n", data->len, data->s); } break; /* capture agent id */ case HEP_AGENT_ID: /* DATA here */ if (str2int(data, &capture_id) < 0) RETURN_ERROR("invalid capture id <%.*s>!\n", data->len, data->s); h3->hg.capt_id.data = capture_id; h3->hg.capt_id.chunk.length = sizeof(hep_chunk_uint32_t); break; /* payload */ case HEP_PAYLOAD: case HEP_COMPRESSED_PAYLOAD: if (data->len>MAX_PAYLOAD) { LM_ERR("payload too big! Might be a message from an attacker!\n"); return -1; } memcpy(payload_buf, data->s, data->len); h3->payload_chunk.data = payload_buf; h3->payload_chunk.chunk.length = data->len + sizeof(hep_chunk_t); break; /* internal correlation id */ case HEP_CORRELATION_ID: LM_WARN("not implemented yet!won't set\n"); break; /* vlan ID */ } return 1; #undef CHECK_LEN #undef PROTO_LEN #undef PROTOT_LEN #undef RETURN_ERROR } int extract_host_port(void) { if(raw_socket_listen.len) { char *p1,*p2; p1 = raw_socket_listen.s; if( (p1 = strrchr(p1, ':')) != 0 ) { *p1 = '\0'; p1++; p2=p1; if((p2 = strrchr(p2, '-')) != 0 ) { p2++; moni_port_end = atoi(p2); p1[strlen(p1)-strlen(p2)-1]='\0'; } moni_port_start = atoi(p1); raw_socket_listen.len = strlen(raw_socket_listen.s); } return 1; } return 0; } static int child_init(int rank) { if (rank==PROC_MAIN || rank==PROC_TCP_MAIN) return 0; /* do nothing for the main process */ if (db_url.s) return sipcapture_db_init(&db_url); LM_DBG("db_url is empty\n"); return 0; } int sipcapture_db_init(const str* db_url) { if(db_funcs.init == 0) { LM_CRIT("null dbf\n"); goto error; } db_con = db_funcs.init(db_url); if (!db_con) { LM_ERR("unable to connect database\n"); return -1; } if (db_funcs.use_table(db_con, &table_name) < 0) { LM_ERR("use_table failed\n"); return -1; } return 0; error: return -1; } void sipcapture_db_close(void) { if (db_con && db_funcs.close){ db_funcs.close(db_con); db_con=0; } } static void raw_socket_process(int rank) { if (sipcapture_db_init(&db_url) < 0 ){ LM_ERR("unable to open database connection\n"); return; } raw_capture_rcv_loop(raw_sock_desc, moni_port_start, moni_port_end, moni_capture_on ? 0 : 1); /* Destroy DB socket */ sipcapture_db_close(); } static int do_remaining_queries(str* query_str) { if (!db_con) { db_con = db_funcs.init(&db_url); if (!db_con) { LM_ERR("unable to connect database\n"); return -1; } if (db_funcs.use_table(db_con, &table_name) < 0) { LM_ERR("use_table failed\n"); return -1; } } if (db_funcs.raw_query(db_con, query_str, NULL)) { LM_ERR("failed to insert remaining queries\n"); return -1; } return 0; } static void destroy(void) { str query_str; struct tz_table_list* it=tz_list, *tz_free; /* execute the uninserted queries - async only */ if (DB_CAPABILITY(db_funcs, DB_CAP_ASYNC_RAW_QUERY)) { while (it) { if (it->as_qry && HAVE_SHARED_QUERIES) { if (CURR_QUERIES(it->as_qry)) { query_str.s = QUERY_BUF(it->as_qry); query_str.len = QUERY_LEN(it->as_qry); do_remaining_queries(&query_str); } shm_free(LAST_SUFFIX(it->as_qry).s); DESTROY_QUERY_LOCK(it->as_qry); shm_free(it->as_qry); } tz_free=it; it=it->next; pkg_free(tz_free); } it=rc_list; while (it) { if (it->as_qry && HAVE_SHARED_QUERIES) { if (CURR_QUERIES(it->as_qry)) { query_str.s = QUERY_BUF(it->as_qry); query_str.len = QUERY_LEN(it->as_qry); do_remaining_queries(&query_str); } shm_free(LAST_SUFFIX(it->as_qry).s); DESTROY_QUERY_LOCK(it->as_qry); shm_free(it->as_qry); } tz_free=it; it=it->next; pkg_free(tz_free); } if (!HAVE_SHARED_QUERIES) { if (tz_global.as_qry) pkg_free(tz_global.as_qry); if (rc_global.as_qry) pkg_free(rc_global.as_qry); } else { /* execute remaining queries for both sip_capture and report_capture */ if (tz_global.as_qry) { if (CURR_QUERIES(tz_global.as_qry)) { query_str.s = QUERY_BUF(tz_global.as_qry); query_str.len = QUERY_LEN(tz_global.as_qry); do_remaining_queries(&query_str); } shm_free(LAST_SUFFIX(tz_global.as_qry).s); DESTROY_QUERY_LOCK(tz_global.as_qry); shm_free(tz_global.as_qry); } if (rc_global.as_qry) { if (CURR_QUERIES(rc_global.as_qry)) { query_str.s = QUERY_BUF(rc_global.as_qry); query_str.len = QUERY_LEN(rc_global.as_qry); do_remaining_queries(&query_str); } shm_free(LAST_SUFFIX(rc_global.as_qry).s); DESTROY_QUERY_LOCK(rc_global.as_qry); shm_free(rc_global.as_qry); } } } /* Destroy DB socket */ sipcapture_db_close(); if (capture_on_flag) shm_free(capture_on_flag); if(raw_sock_desc > 0) { if(promisc_on && raw_interface.len) { #ifdef __OS_linux ifr.ifr_flags &= ~(IFF_PROMISC); if (ioctl(raw_sock_desc, SIOCSIFFLAGS, &ifr) < 0) { LM_ERR("could not remove PROMISC flag from interface [%.*s]:" " %s (%d)\n", raw_interface.len, raw_interface.s, strerror(errno), errno); } #endif } close(raw_sock_desc); } } /** * HEP message */ int hep_msg_received(void) { struct sip_msg msg; struct hep_desc *h; struct hep_context* ctx; if ((ctx=HEP_GET_CONTEXT(hep_api))==NULL) { LM_WARN("not a hep message!\n"); return -1; } h = &ctx->h; if(!hep_capture_on) { LM_ERR("HEP is not enabled\n"); return 0; } if ( hep_route_id == HEP_NO_ROUTE ) { memset(&msg, 0, sizeof(struct sip_msg)); switch (h->version) { case 1: case 2: msg.buf = h->u.hepv12.payload; msg.len = strlen(msg.buf); break; case 3: msg.buf = h->u.hepv3.payload_chunk.data; msg.len = h->u.hepv3.payload_chunk.chunk.length - sizeof(struct hep_chunk); break; default: LM_ERR("unknown hep proto [%d]\n", h->version); return -1; } if (parse_msg(msg.buf,msg.len,&msg)!=0) { LM_ERR("Unable to parse message in hep payload!" "Hep version %d!\n", h->version); return -1; } #if 0 /* if message not parsed ok this helps with debugging */ LM_DBG("********************************* SIP MESSAGE ******************\n" "%.*s\n" "***************************************************************\n", (int)msg.len, msg.buf); #endif /* we basically move the sip_capture() call from the scripts here */ if (w_sip_capture(&msg, NULL, NULL, NULL) < 0) { LM_ERR("failed to store the message!\n"); return -1; } /* don't go through the main route */ return HEP_SCRIPT_SKIP; } else if (hep_route_id > HEP_SIP_ROUTE) { /* set request route type */ set_route_type( REQUEST_ROUTE ); /* run given hep route */ run_top_route(rlist[hep_route_id].a, &dummy_req); /* free possible loaded avps */ reset_avps(); /* requested to go through the main sip route */ if (ctx->resume_with_sip) { return 0; } else { return HEP_SCRIPT_SKIP; } } return 0; } static int fixup_tz_table(void** param, struct tz_table_list** list) { str table_s; tz_table_t* tz_fxup_param; struct tz_table_list* list_el,* it; tz_fxup_param = pkg_malloc(sizeof(tz_table_t)); if (tz_fxup_param == NULL) { LM_ERR("no more pkg mem!\n"); return -1; } table_s.s = (char *) *param; table_s.len = strlen(table_s.s); parse_table_str(&table_s, tz_fxup_param); *param = tz_fxup_param; /* if not there add this table to the list */ for ( it=*list; it; it=it->next) { if (it->table->prefix.len == tz_fxup_param->prefix.len && it->table->suffix.len == tz_fxup_param->suffix.len && !memcmp(it->table->prefix.s, tz_fxup_param->prefix.s, tz_fxup_param->prefix.len) && !memcmp(it->table->suffix.s, tz_fxup_param->suffix.s, tz_fxup_param->suffix.len)) /* table already there */ return 0; } list_el = pkg_malloc(sizeof(struct tz_table_list)); if (list_el == NULL) { LM_ERR("no more pkg mem!\n"); return -1; } memset(list_el, 0, sizeof(struct tz_table_list)); list_el->table = tz_fxup_param; if (*list == NULL) { *list = list_el; } else { list_el->next = *list; *list = list_el; } return 0; } static int fixup_async_tz_table(void** param, struct tz_table_list** list) { struct tz_table_list* list_el; if (fixup_tz_table(param, list) < 0) return -1; list_el = *list; /* we store this in shm; need the queries in the end */ if (HAVE_MULTIPLE_ASYNC_INSERT) { list_el->as_qry=shm_malloc(sizeof(struct _async_query)); if (list_el->as_qry == NULL) goto shm_err; memset(list_el->as_qry, 0, sizeof(struct _async_query)); LAST_SUFFIX(list_el->as_qry).s = shm_malloc(CAPTURE_TABLE_MAX_LEN); if (LAST_SUFFIX(list_el->as_qry).s == NULL) goto shm_err; LAST_SUFFIX(list_el->as_qry).len = 0; INIT_QUERY_LOCK(list_el->as_qry); } return 0; shm_err: LM_ERR("no more shared memory!\n"); return -1; } static int sip_capture_fixup(void** param, int param_no) { if (param_no != 1) { LM_ERR("Invalid param number!\n"); return -1; } return fixup_tz_table(param, &tz_list); } static int sip_capture_async_fixup(void** param, int param_no) { if (param_no != 1) { LM_ERR("Invalid param number!\n"); return -1; } return fixup_async_tz_table(param, &tz_list); } static int sip_capture_prepare(struct sip_msg* msg) { /* We need parse all headers */ if (parse_headers(msg, HDR_CALLID_F|HDR_EOH_F, 0) != 0) { LM_ERR("cannot parse headers\n"); return -1; } return 0; } static int sip_capture_store(struct _sipcapture_object *sco, async_resume_module **resume_f, void **resume_param, struct tz_table_list* t_el) { db_val_t db_vals[NR_KEYS]; int i = 0, ret; if(sco==NULL) { LM_DBG("invalid parameter\n"); return -1; } db_vals[0].type = DB_INT; db_vals[0].val.int_val = 0; db_vals[1].type = DB_DATETIME; db_vals[1].val.time_val = (sco->tmstamp/1000000); db_vals[2].type = DB_BIGINT; db_vals[2].val.bigint_val = sco->tmstamp; db_vals[3].type = DB_STR; db_vals[3].val.str_val = sco->method; db_vals[4].type = DB_STR; db_vals[4].val.str_val = sco->reply_reason; db_vals[5].type = DB_STR; db_vals[5].val.str_val = sco->ruri; db_vals[6].type = DB_STR; db_vals[6].val.str_val = sco->ruri_user; db_vals[7].type = DB_STR; db_vals[7].val.str_val = sco->from_user; db_vals[8].type = DB_STR; db_vals[8].val.str_val = sco->from_tag; db_vals[9].type = DB_STR; db_vals[9].val.str_val = sco->to_user; db_vals[10].type = DB_STR; db_vals[10].val.str_val = sco->to_tag; db_vals[11].type = DB_STR; db_vals[11].val.str_val = sco->pid_user; db_vals[12].type = DB_STR; db_vals[12].val.str_val = sco->contact_user; db_vals[13].type = DB_STR; db_vals[13].val.str_val = sco->auth_user; db_vals[14].type = DB_STR; db_vals[14].val.str_val = sco->callid; db_vals[15].type = DB_STR; db_vals[15].val.str_val = sco->callid_aleg; db_vals[16].type = DB_STR; db_vals[16].val.str_val = sco->via_1; db_vals[17].type = DB_STR; db_vals[17].val.str_val = sco->via_1_branch; db_vals[18].type = DB_STR; db_vals[18].val.str_val = sco->cseq; db_vals[19].type = DB_STR; db_vals[19].val.str_val = sco->reason; db_vals[20].type = DB_STR; db_vals[20].val.str_val = sco->content_type; db_vals[21].type = DB_STR; db_vals[21].val.str_val = sco->authorization; db_vals[22].type = DB_STR; db_vals[22].val.str_val = sco->user_agent; db_vals[23].type = DB_STR; db_vals[23].val.str_val = sco->source_ip; db_vals[24].type = DB_INT; db_vals[24].val.int_val = sco->source_port; db_vals[25].type = DB_STR; db_vals[25].val.str_val = sco->destination_ip; db_vals[26].type = DB_INT; db_vals[26].val.int_val = sco->destination_port; db_vals[27].type = DB_STR; db_vals[27].val.str_val = sco->contact_ip; db_vals[28].type = DB_INT; db_vals[28].val.int_val = sco->contact_port; db_vals[29].type = DB_STR; db_vals[29].val.str_val = sco->originator_ip; db_vals[30].type = DB_INT; db_vals[30].val.int_val = sco->originator_port; db_vals[31].type = DB_INT; db_vals[31].val.int_val = sco->proto; db_vals[32].type = DB_INT; db_vals[32].val.int_val = sco->family; db_vals[33].type = DB_STR; db_vals[33].val.str_val = sco->rtp_stat; /* proto type is defined in proto but not stored in homer db :-? */ //db_vals[34].type = DB_INT; //db_vals[34].val.int_val = sco->proto_type; db_vals[34].type = DB_INT; db_vals[34].val.int_val = sco->type; db_vals[35].type = DB_STR; db_vals[35].val.str_val = sco->node; db_vals[36].type = DB_STR; db_vals[36].val.str_val = sco->correlation_id; db_vals[37].type = DB_STR; db_vals[37].val.str_val = sco->from_domain; db_vals[38].type = DB_STR; db_vals[38].val.str_val = sco->to_domain; db_vals[39].type = DB_STR; db_vals[39].val.str_val = sco->ruri_domain; db_vals[40].type = DB_BLOB; db_vals[40].val.blob_val = sco->msg; /* no field can be null */ for (i=0;istat, 1); #endif return ret; } static int db_sync_store(db_val_t* vals, db_key_t* keys, int num_keys) { LM_DBG("storing info...\n"); if (current_table.s && current_table.len) { if (db_funcs.use_table(db_con, ¤t_table) < 0) { LM_ERR("use table failed!\n"); return -1; } } if (db_funcs.insert(db_con, keys, vals, num_keys) < 0) { LM_ERR("failed to insert into database\n"); goto error; } return 1; error: return -1; } static inline int append_sc_values(char* buf, int max_len, db_val_t* db_vals) { int len; len = snprintf(buf, max_len, VALUES_STR, VAL_INT(db_vals+0), VAL_TIME(db_vals+1), VAL_BIGINT(db_vals+2), VAL_STR(db_vals+3).len, VAL_STR(db_vals+3).s, VAL_STR(db_vals+4).len, VAL_STR(db_vals+4).s, VAL_STR(db_vals+5).len, VAL_STR(db_vals+5).s, VAL_STR(db_vals+6).len, VAL_STR(db_vals+6).s, VAL_STR(db_vals+7).len, VAL_STR(db_vals+7).s, VAL_STR(db_vals+8).len, VAL_STR(db_vals+8).s, VAL_STR(db_vals+9).len, VAL_STR(db_vals+9).s, VAL_STR(db_vals+10).len, VAL_STR(db_vals+10).s, VAL_STR(db_vals+11).len, VAL_STR(db_vals+11).s, VAL_STR(db_vals+12).len, VAL_STR(db_vals+12).s, VAL_STR(db_vals+13).len, VAL_STR(db_vals+13).s, VAL_STR(db_vals+14).len, VAL_STR(db_vals+14).s, VAL_STR(db_vals+15).len, VAL_STR(db_vals+15).s, VAL_STR(db_vals+16).len, VAL_STR(db_vals+16).s, VAL_STR(db_vals+17).len, VAL_STR(db_vals+17).s, VAL_STR(db_vals+18).len, VAL_STR(db_vals+18).s, VAL_STR(db_vals+19).len, VAL_STR(db_vals+19).s, VAL_STR(db_vals+20).len, VAL_STR(db_vals+20).s, VAL_STR(db_vals+21).len, VAL_STR(db_vals+21).s, VAL_STR(db_vals+22).len, VAL_STR(db_vals+22).s, VAL_STR(db_vals+23).len, VAL_STR(db_vals+23).s, VAL_INT(db_vals+24), VAL_STR(db_vals+25).len, VAL_STR(db_vals+25).s, VAL_INT(db_vals+26), VAL_STR(db_vals+27).len, VAL_STR(db_vals+27).s, VAL_INT(db_vals+28), VAL_STR(db_vals+29).len, VAL_STR(db_vals+29).s, VAL_INT(db_vals+30), VAL_INT(db_vals+31), VAL_INT(db_vals+32), VAL_STR(db_vals+33).len, VAL_STR(db_vals+33).s, VAL_INT(db_vals+34), VAL_STR(db_vals+35).len, VAL_STR(db_vals+35).s, VAL_STR(db_vals+36).len, VAL_STR(db_vals+36).s, VAL_STR(db_vals+37).len, VAL_STR(db_vals+37).s, VAL_STR(db_vals+38).len, VAL_STR(db_vals+38).s, VAL_STR(db_vals+39).len, VAL_STR(db_vals+39).s, VAL_BLOB(db_vals+40).len, VAL_BLOB(db_vals+40).s ); return len; } static inline int init_raw_query(char* buf, int max_len, str* table_name, db_key_t* keys, int num_keys) { int len, i, ret; len = snprintf(buf, max_len, "INSERT INTO %.*s(", table_name->len, table_name->s); for (i=0; ilen, keys[i]->s); if (ret<0) return ret; len += ret; } ret=snprintf(buf+len, max_len-len, "%.*s) VALUES", keys[num_keys-1]->len, keys[num_keys-1]->s); if (ret<0) return ret; len += ret; return len; } static int db_async_store(db_val_t* vals, db_key_t* keys, int num_keys, append_db_vals_f append_db_vals, async_resume_module **resume_f, void **resume_param, struct tz_table_list* t_el) { int ret; int read_fd; str query_str; struct _async_query *crt_as_query; sc_async_param_t as_param; if (!DB_CAPABILITY(db_funcs, DB_CAP_ASYNC_RAW_QUERY)) { LM_WARN("This database module does not have async queries!" "Using sync insert!\n"); *resume_f = NULL; *resume_param = NULL; async_status = ASYNC_NO_IO; return db_sync_store(vals, keys, num_keys); } if (HAVE_MULTIPLE_ASYNC_INSERT && t_el == NULL) { LM_ERR("can't do multiple insert!\n"); *resume_param = NULL; *resume_f = NULL; return -1; } crt_as_query = t_el->as_qry; if (HAVE_SHARED_QUERIES) GET_QUERY_LOCK(crt_as_query); /* use the global async query; we do this only once */ if (CURR_QUERIES(crt_as_query) == 0) { QUERY_LEN(crt_as_query)=init_raw_query(QUERY_BUF(crt_as_query), MAX_QUERY, ¤t_table, keys, num_keys); } else { QUERY_BUF(crt_as_query)[QUERY_LEN(crt_as_query)++] = ','; } ret=append_db_vals(QUERY_BUF(crt_as_query)+QUERY_LEN(crt_as_query), MAX_QUERY-QUERY_LEN(crt_as_query), vals); if (ret < 0) goto no_buffer; QUERY_LEN(crt_as_query) += ret; if ((++CURR_QUERIES(crt_as_query)) == max_async_queries) { CURR_QUERIES(crt_as_query) = 0; query_str.s = QUERY_BUF(crt_as_query); query_str.len = QUERY_LEN(crt_as_query); read_fd = db_funcs.async_raw_query(db_con, &query_str, &as_param); if (HAVE_SHARED_QUERIES) RELEASE_QUERY_LOCK(crt_as_query); if (read_fd < 0) { *resume_param = NULL; *resume_f = NULL; return -1; } *resume_param = as_param; *resume_f = resume_async_dbquery; async_status = read_fd; return 1; } if (HAVE_SHARED_QUERIES) RELEASE_QUERY_LOCK(crt_as_query); LM_DBG("no query executed!\n"); async_status = ASYNC_NO_IO; return 1; no_buffer: LM_ERR("buffer size exceeded\n"); return -1; } int resume_async_dbquery(int fd, struct sip_msg *msg, void *_param) { int rc; rc = db_funcs.async_resume(db_con, fd, NULL, (sc_async_param_t)_param); if (async_status == ASYNC_CONTINUE || async_status == ASYNC_CHANGE_FD) return rc; if (rc != 0) { LM_ERR("async query returned error (%d)\n", rc); db_funcs.async_free_result(db_con, NULL, (sc_async_param_t)_param); return -1; } LM_DBG("async query executed successfully!\n"); async_status = ASYNC_DONE; db_funcs.async_free_result(db_con, NULL, (sc_async_param_t)_param); return 1; } static inline int change_table_unsafe(struct tz_table_list* t_el, str* new_table_name) { str query_str; /* execute remaining queries for the old table */ if (CURR_QUERIES(t_el->as_qry)) { query_str.s = QUERY_BUF(t_el->as_qry); query_str.len = QUERY_LEN(t_el->as_qry); if (do_remaining_queries(&query_str) < 0){ LM_ERR("failed to execute remaining queries " "when switching to new table!\n"); RELEASE_QUERY_LOCK(t_el->as_qry); return -1; } CURR_QUERIES(t_el->as_qry) = 0; /* update the suffix */ LAST_SUFFIX(t_el->as_qry).len = new_table_name->len - t_el->table->prefix.len; memcpy(LAST_SUFFIX(t_el->as_qry).s, new_table_name->s+t_el->table->prefix.len, LAST_SUFFIX(t_el->as_qry).len); } return 0; } static inline int try_change_suffix(struct tz_table_list* t_el, str* new_table) { int ret=0; struct _async_query* as_qry=t_el->as_qry; GET_QUERY_LOCK(as_qry); if (LAST_SUFFIX(as_qry).len) { if (memcmp(LAST_SUFFIX(as_qry).s, new_table->s+t_el->table->prefix.len, LAST_SUFFIX(as_qry).len)) { /* try changing table */ if (change_table_unsafe(t_el, new_table) < 0) { LM_ERR("failed changing tables!\n"); ret=-1; goto out_safe; } } } out_safe: RELEASE_QUERY_LOCK(t_el->as_qry); return ret; } /* * no need to allocate output string buffer * */ static inline void build_table_name(tz_table_t* table_format, str* table_s) { time_t rawtime; struct tm* gmtm; table_s->s = table_buf; memcpy(current_table.s, table_format->prefix.s, table_format->prefix.len); table_s->len = table_format->prefix.len; if (table_format->suffix.len && table_format->suffix.s) { time(&rawtime); gmtm = gmtime(&rawtime); table_s->len += strftime(table_s->s+table_s->len, CAPTURE_TABLE_MAX_LEN-table_s->len, table_format->suffix.s, gmtm); } } static inline struct tz_table_list* search_table(tz_table_t* el, struct tz_table_list* list) { struct tz_table_list* it = NULL; for (it=list; it; it=it->next) if (el->prefix.len && el->prefix.len == it->table->prefix.len && !memcmp(el->prefix.s, it->table->prefix.s, el->prefix.len) && el->suffix.len == it->table->suffix.len && !memcmp(el->suffix.s, it->table->suffix.s, el->suffix.len)) return it; return it; } static int sip_capture(struct sip_msg *msg, char* s1, char* s2) { return w_sip_capture(msg, s1, NULL, NULL); } static int async_sip_capture(struct sip_msg* msg, async_resume_module **resume_f, void **resume_param, char* s1, char* s2) { return w_sip_capture(msg, s1, resume_f, resume_param); } static int w_sip_capture(struct sip_msg *msg, char *table_name, async_resume_module **resume_f, void **resume_param) { struct _sipcapture_object sco; struct sip_uri from, to, pai, contact; struct hdr_field *hook1 = NULL; struct hdr_field *tmphdr[4]; contact_body_t* cb=0; char src_buf_ip[IP_ADDR_MAX_STR_SIZE+12]; char dst_buf_ip[IP_ADDR_MAX_STR_SIZE+12]; char *port_str = NULL, *tmp = NULL; struct timeval tvb; struct timezone tz; char tmp_node[100]; struct hep_desc *h=NULL; struct hep_context* ctx; tz_table_t* tzt = (tz_table_t*)table_name; struct tz_table_list* t_it=&tz_global; generic_chunk_t* it; if (tzt == NULL ) { tzt = &tz_table; } /* need list element only if for async */ if (IS_ASYNC_F && HAVE_MULTIPLE_ASYNC_INSERT) { if (table_name != NULL) { /* find the table in the list */ if ((t_it=search_table(tzt, tz_list)) == NULL) { LM_ERR("Invalid table given!\n"); return -1; } } } build_table_name(tzt, ¤t_table); if (tzt->suffix.s && tzt->suffix.len && IS_ASYNC_F && HAVE_MULTIPLE_ASYNC_INSERT) { if (try_change_suffix(t_it, ¤t_table) < 0) return -1; } gettimeofday( &tvb, &tz ); if(msg==NULL) { LM_DBG("nothing to capture\n"); return -1; } memset(&sco, 0, sizeof(struct _sipcapture_object)); if (hep_capture_on) { if ((ctx=HEP_GET_CONTEXT(hep_api))==NULL) { LM_WARN("not a hep message!\n"); return -1; } h = &ctx->h; } if(capture_on_flag==NULL || *capture_on_flag==0) { LM_DBG("capture off...\n"); return -1; } if(sip_capture_prepare(msg)<0) return -1; if (h && h->version==3) { /*hepv3; struct might have been modified in script */ sco.tmstamp = (unsigned long long)h->u.hepv3.hg.time_sec.data*1000000 + h->u.hepv3.hg.time_usec.data; snprintf(tmp_node, 100, "%.*s:%i", capture_node.len, capture_node.s, h->u.hepv3.hg.capt_id.data); sco.node.s = tmp_node; sco.node.len = strlen(tmp_node); } else if(h && h->version==2) { sco.tmstamp = (unsigned long long)h->u.hepv12.hep_time.tv_sec*1000000+ h->u.hepv12.hep_time.tv_usec; /* micro ts */ snprintf(tmp_node, 100, "%.*s:%i", capture_node.len, capture_node.s, h->u.hepv12.hep_time.captid); sco.node.s = tmp_node; sco.node.len = strlen(tmp_node); } else { sco.tmstamp = (unsigned long long)tvb.tv_sec*1000000+tvb.tv_usec; /* micro ts */ sco.node = capture_node; } if(msg->first_line.type == SIP_REQUEST) { if (parse_sip_msg_uri(msg)<0) return -1; sco.method = msg->first_line.u.request.method; EMPTY_STR(sco.reply_reason); sco.ruri = msg->first_line.u.request.uri; sco.ruri_user = msg->parsed_uri.user; sco.ruri_user = msg->parsed_uri.host; } else if(msg->first_line.type == SIP_REPLY) { sco.method = msg->first_line.u.reply.status; sco.reply_reason = msg->first_line.u.reply.reason; EMPTY_STR(sco.ruri); EMPTY_STR(sco.ruri_user); } else { LM_ERR("unknow type [%i]\n", msg->first_line.type); EMPTY_STR(sco.method); EMPTY_STR(sco.reply_reason); EMPTY_STR(sco.ruri); EMPTY_STR(sco.ruri_user); } /* Parse FROM */ if(msg->from) { if (parse_from_header(msg)!=0){ LM_ERR("bad or missing" " From: header\n"); return -1; } if (parse_uri(get_from(msg)->uri.s, get_from(msg)->uri.len, &from)<0){ LM_ERR("bad from dropping"" packet\n"); return -1; } sco.from_user = from.user; sco.from_tag = get_from(msg)->tag_value; sco.from_domain = from.host; } else { EMPTY_STR(sco.from_user); EMPTY_STR(sco.from_tag); } /* Parse TO */ if(msg->to) { if (parse_uri(get_to(msg)->uri.s, get_to(msg)->uri.len, &to)<0){ LM_ERR("bad to dropping"" packet\n"); return -1; } sco.to_user = to.user; if(get_to(msg)->tag_value.len) sco.to_tag = get_to(msg)->tag_value; else { EMPTY_STR(sco.to_tag); } sco.to_domain = to.host; } else { EMPTY_STR(sco.to_user); EMPTY_STR(sco.to_tag); } /* Call-id */ if(msg->callid) sco.callid = msg->callid->body; else { EMPTY_STR(sco.callid); } /* P-Asserted-Id */ if(msg->pai && (parse_pai_header(msg) == 0)) { if (parse_uri(get_pai(msg)->uri.s, get_pai(msg)->uri.len, &pai)<0){ LM_DBG("bad pai: method:[%.*s] CID: [%.*s]\n", sco.method.len, sco.method.s, sco.callid.len, sco.callid.s); } else { LM_DBG("PARSE PAI: (%.*s)\n",get_pai(msg)->uri.len, get_pai(msg)->uri.s); sco.pid_user = pai.user; } } else if(msg->ppi && (parse_ppi_header(msg) == 0)) { if (parse_uri(get_ppi(msg)->uri.s, get_ppi(msg)->uri.len, &pai)<0){ LM_DBG("bad ppi: method:[%.*s] CID: [%.*s]\n", sco.method.len, sco.method.s, sco.callid.len, sco.callid.s); } else { sco.pid_user = pai.user; } } else { EMPTY_STR(sco.pid_user); } /* Auth headers */ if(msg->proxy_auth != NULL) hook1 = msg->proxy_auth; else if(msg->authorization != NULL) hook1 = msg->authorization; if(hook1) { if(parse_credentials(hook1) == 0) sco.auth_user = ((auth_body_t*)(hook1->parsed))->digest.username.user; else { EMPTY_STR(sco.auth_user); } } else { EMPTY_STR(sco.auth_user);} if(msg->contact) { if (msg->contact->parsed == 0 && parse_contact(msg->contact) == -1) { LM_ERR("while parsing header\n"); return -1; } cb = (contact_body_t*)msg->contact->parsed; memset(&contact, 0, sizeof(struct sip_uri)); if(cb && cb->contacts) { if(parse_uri( cb->contacts->uri.s, cb->contacts->uri.len, &contact)<0){ LM_ERR("bad contact dropping packet\n"); return -1; } } } /* get header x-cid: */ /* callid_aleg X-CID */ if((tmphdr[0] = get_header_by_static_name(msg,"X-CID")) != NULL) { sco.callid_aleg = tmphdr[0]->body; } else { EMPTY_STR(sco.callid_aleg);} /* VIA 1 */ sco.via_1 = msg->h_via1->body; /* Via branch */ if(msg->via1->branch) sco.via_1_branch = msg->via1->branch->value; else { EMPTY_STR(sco.via_1_branch); } /* CSEQ */ if(msg->cseq) sco.cseq = msg->cseq->body; else { EMPTY_STR(sco.cseq); } /* Reason */ if((tmphdr[1] = get_header_by_static_name(msg,"Reason")) != NULL) { sco.reason = tmphdr[1]->body; } else { EMPTY_STR(sco.reason); } /* Diversion */ if(msg->diversion) sco.diversion = msg->diversion->body; else { EMPTY_STR(sco.diversion);} /* Content-type */ if(msg->content_type) sco.content_type = msg->content_type->body; else { EMPTY_STR(sco.content_type);} /* User-Agent */ if(msg->user_agent) sco.user_agent = msg->user_agent->body; else { EMPTY_STR(sco.user_agent);} /* Contact */ if(msg->contact && cb) { sco.contact_ip = contact.host; str2int(&contact.port, (unsigned int*)&sco.contact_port); } else { EMPTY_STR(sco.contact_ip); sco.contact_port = 0; } /* X-OIP */ if((tmphdr[2] = get_header_by_static_name(msg,"X-OIP")) != NULL) { sco.originator_ip = tmphdr[2]->body; /* Originator port. Should be parsed from XOIP header as ":" param */ tmp = strchr(tmphdr[2]->body.s, ':'); if (tmp) { *tmp = '\0'; port_str = tmp + 1; sco.originator_port = strtol(port_str, NULL, 10); } else sco.originator_port = 0; } else { EMPTY_STR(sco.originator_ip); sco.originator_port = 0; } /* X-RTP-Stat */ if((tmphdr[3] = get_header_by_static_name(msg,"X-RTP-Stat")) != NULL) { sco.rtp_stat = tmphdr[3]->body; } /* P-RTP-Stat */ else if((tmphdr[3] = get_header_by_static_name(msg,"P-RTP-Stat")) != NULL) { sco.rtp_stat = tmphdr[3]->body; } else { EMPTY_STR(sco.rtp_stat); } /* PROTO TYPE * FAMILY TYPE */ if (h && h->version==3) { sco.proto = h->u.hepv3.hg.ip_proto.data; sco.family = h->u.hepv3.hg.ip_family.data; /*SIP, XMPP... */ sco.proto_type = h->u.hepv3.hg.proto_t.data; if (h->u.hepv3.hg.ip_family.data == AF_INET) { if (inet_ntop(AF_INET, &h->u.hepv3.addr.ip4_addr.src_ip4.data, src_buf_ip, INET_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } if (inet_ntop(AF_INET, &h->u.hepv3.addr.ip4_addr.dst_ip4.data, dst_buf_ip, INET_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } else { if (inet_ntop(AF_INET6, &h->u.hepv3.addr.ip6_addr.src_ip6.data, src_buf_ip, INET6_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } if (inet_ntop(AF_INET6, &h->u.hepv3.addr.ip6_addr.dst_ip6.data, dst_buf_ip, INET6_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } sco.source_ip.s = src_buf_ip; sco.source_ip.len = strlen(src_buf_ip); sco.source_port = h->u.hepv3.hg.src_port.data; sco.destination_ip.s = dst_buf_ip; sco.destination_ip.len = strlen(dst_buf_ip); sco.destination_port = h->u.hepv3.hg.dst_port.data; } else if (h && (h->version == 1 || h->version == 2)) { sco.proto = h->u.hepv12.hdr.hp_p; sco.family = h->u.hepv12.hdr.hp_f; /* default SIP; hepv12 doesn't have proto type */ sco.proto_type = 0x01; if (sco.family == AF_INET) { if (inet_ntop(AF_INET, &h->u.hepv12.addr.hep_ipheader.hp_src, src_buf_ip, INET_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } if (inet_ntop(AF_INET, &h->u.hepv12.addr.hep_ipheader.hp_dst, dst_buf_ip, INET_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } else { if (inet_ntop(AF_INET6, &h->u.hepv12.addr.hep_ip6header.hp6_src, src_buf_ip, INET6_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } if (inet_ntop(AF_INET6, &h->u.hepv12.addr.hep_ip6header.hp6_dst, dst_buf_ip, INET6_ADDRSTRLEN) == NULL) { LM_ERR("failed to convert ipv4 address!\n"); return -1; } } sco.source_ip.s = src_buf_ip; sco.source_ip.len = strlen(src_buf_ip); sco.source_port = h->u.hepv12.hdr.hp_sport; sco.destination_ip.s = dst_buf_ip; sco.destination_ip.len = strlen(dst_buf_ip); sco.destination_port = h->u.hepv12.hdr.hp_dport; } else { sco.proto = msg->rcv.proto; sco.family = msg->rcv.src_ip.af; /* IP source and destination */ /*source ip*/ memcpy(src_buf_ip, ip_addr2a(&msg->rcv.src_ip), sizeof(&msg->rcv.src_ip)); ip_addr2a((struct ip_addr*)src_buf_ip); sco.source_ip.s = src_buf_ip; sco.source_ip.len = strlen(src_buf_ip); sco.source_port = msg->rcv.src_port; /*destination ip*/ memcpy(dst_buf_ip, ip_addr2a(&msg->rcv.dst_ip), sizeof(&msg->rcv.dst_ip)); ip_addr2a((struct ip_addr*)dst_buf_ip); sco.destination_ip.s = dst_buf_ip; sco.destination_ip.len = strlen(sco.destination_ip.s); sco.destination_port = msg->rcv.dst_port; } /* we change to internal proto id only for version 3; for version * 1/2 we don't change the buffer inside opensips so we don't need * internal protocol id */ if (h && h->version == 3) { if(sco.proto == PROTO_UDP) sco.proto=IPPROTO_UDP; else if(sco.proto == PROTO_TCP) sco.proto=IPPROTO_TCP; else if(sco.proto == PROTO_TLS) sco.proto=IPPROTO_IDP; /* fake protocol */ else if(sco.proto == PROTO_SCTP) sco.proto=IPPROTO_SCTP; else if(sco.proto == PROTO_WS) sco.proto=IPPROTO_ESP; /* fake protocol */ else { LM_ERR("unknown protocol [%d]\n",sco.proto); sco.proto = PROTO_NONE; } } LM_DBG("src_ip: [%.*s]\n", sco.source_ip.len, sco.source_ip.s); LM_DBG("dst_ip: [%.*s]\n", sco.destination_ip.len, sco.destination_ip.s); LM_DBG("dst_port: [%d]\n", sco.destination_port); LM_DBG("src_port: [%d]\n", sco.source_port); /* PROTO */ /* MESSAGE TYPE */ sco.type = msg->first_line.type; sco.correlation_id.s = ""; sco.correlation_id.len = 0; /* MSG */ if (h && h->version == 3) { for (it=h->u.hepv3.chunk_list; it; it=it->next) { if (it->chunk.type_id == HEP_CORRELATION_ID) { sco.correlation_id.s = it->data; sco.correlation_id.len = it->chunk.length - sizeof(hep_chunk_t); break; } } sco.msg.s = h->u.hepv3.payload_chunk.data; sco.msg.len = h->u.hepv3.payload_chunk.chunk.length - sizeof(hep_chunk_t); } else { sco.msg.s = h->u.hepv12.payload; sco.msg.len = strlen(h->u.hepv12.payload); } //EMPTY_STR(sco.msg); #ifdef STATISTICS if(msg->first_line.type==SIP_REPLY) { sco.stat = sipcapture_rpl; } else { sco.stat = sipcapture_req; } #endif LM_DBG("DONE\n"); return sip_capture_store(&sco, resume_f, resume_param, t_it); } /* * resolve data type in chunk */ enum hep_chunk_value_type {TYPE_ERROR=0,TYPE_UINT8=1, TYPE_UINT16=2, TYPE_UINT32=4, TYPE_INET_ADDR, TYPE_INET6_ADDR=16, TYPE_UTF8, TYPE_BLOB}; static int fix_hep_value_type(str *s) { static const str type_uint_s={"uint", sizeof("uint")-1}; static const str type_utf_string_s=str_init("utf8-string"); static const str type_octet_string_s=str_init("octet-string"); static const str type_inet_addr_s=str_init("inet4-addr"); static const str type_inet6_addr_s=str_init("inet6-addr"); int diff; diff = s->len - type_uint_s.len; /* also applies to 'str' - same len as 'int' */ /* uintX or uintXX */ if (diff > 0 && diff <=2 && !strncasecmp(s->s, type_uint_s.s, type_uint_s.len)) { if (diff == 1) { /* should be int8 */ if (s->s[s->len-1] == '8') return TYPE_UINT8; else goto error; } else { if (s->s[s->len-2] == '1' && s->s[s->len-1] =='6') return TYPE_UINT16; else if (s->s[s->len-2] == '3' && s->s[s->len-1] =='2') return TYPE_UINT32; else goto error; } } else if (s->len==type_utf_string_s.len && !strncasecmp(s->s, type_utf_string_s.s, type_utf_string_s.len)) { return TYPE_UTF8; } else if (s->len == type_octet_string_s.len && !strncasecmp(s->s, type_octet_string_s.s, type_octet_string_s.len)) { return TYPE_BLOB; } else if (s->len == type_inet_addr_s.len && !strncasecmp(s->s, type_inet_addr_s.s, type_inet_addr_s.len)) { return TYPE_INET_ADDR; } else if (s->len == type_inet6_addr_s.len && !strncasecmp(s->s, type_inet6_addr_s.s, type_inet6_addr_s.len)) { return TYPE_INET6_ADDR; } else { goto error; } error: return TYPE_ERROR; } /* * get int value from int or hex value in string format */ static int fix_hex_int(str *s) { unsigned int retval=0; if (!s->len || !s->s) goto error; if (s->len > 2) if ((s->s[0] == '0') && ((s->s[1]|0x20) == 'x')) { if (hexstr2int(s->s+2, s->len-2, &retval)!=0) goto error; else return retval; } if (str2int(s, (unsigned int*)&retval)<0) goto error; return retval; error: LM_ERR("Invalid value for vendor_id: <%*s>!\n", s->len, s->s); return -1; } static int set_hep_generic_fixup(void** param, int param_no) { int type; gparam_p gp; switch (param_no) { case 1: /* chunk id */ if (fixup_sgp(param) < 0) { LM_ERR("fixup for chunk type failed!\n"); return -1; } gp = *param; if (gp->type == GPARAM_TYPE_STR) { if ((type=fix_hex_int(&gp->v.sval)) < 0) { LM_ERR("Invalid chunk value type <%.*s>!\n", gp->v.sval.len, gp->v.sval.s); return -1; } gp->v.ival = type; gp->type = GPARAM_TYPE_INT; } return 0; /* data */ case 2: return fixup_sgp(param); } return 0; } static int set_hep_fixup(void** param, int param_no) { int type; gparam_p gp; switch (param_no) { /* type */ case 1: if (fixup_sgp(param) < 0) { LM_ERR("fixup for chunk type failed!\n"); return -1; } gp = *param; if (gp->type == GPARAM_TYPE_STR) { if ((type=fix_hep_value_type(&gp->v.sval)) == TYPE_ERROR) { LM_ERR("Invalid chunk value type <%.*s>!\n", gp->v.sval.len, gp->v.sval.s); return -1; } gp->v.ival = type; gp->type = GPARAM_TYPE_INT; } return 0; /* chunk id */ case 2: /* vendor*/ case 3: if (fixup_sgp(param) < 0) { LM_ERR("fixup for chunk type failed!\n"); return -1; } gp = *param; if (gp->type == GPARAM_TYPE_STR) { if ((type=fix_hex_int(&gp->v.sval)) < 0) { LM_ERR("Invalid chunk value type <%.*s>!\n", gp->v.sval.len, gp->v.sval.s); return -1; } gp->v.ival = type; gp->type = GPARAM_TYPE_INT; } return 0; /* data */ case 4: return fixup_sgp(param); } return 0; } static int get_hep_generic_fixup(void** param, int param_no) { int type; gparam_p gp; switch (param_no) { case 1: if (fixup_sgp(param) < 0) { LM_ERR("fixup for chunk type failed!\n"); return -1; } gp = *param; if (gp->type == GPARAM_TYPE_STR) { if ((type=fix_hex_int(&gp->v.sval)) < 0) { LM_ERR("Invalid chunk value type <%.*s>!\n", gp->v.sval.len, gp->v.sval.s); return -1; } gp->v.ival = type; gp->type = GPARAM_TYPE_INT; } return 0; /* vendor pvar */ case 2: /* data pvar */ case 3: return fixup_pvar(param); default: LM_ERR("Invalid param number <%d>\n", param_no); return -1; } return 0; } static int get_hep_fixup(void** param, int param_no) { int type; gparam_p gp; switch (param_no) { /* type */ case 1: if (fixup_sgp(param) < 0) { LM_ERR("fixup for chunk type failed!\n"); return -1; } gp = *param; if (gp->type == GPARAM_TYPE_STR) { if ((type=fix_hep_value_type(&gp->v.sval)) == TYPE_ERROR) { LM_ERR("Invalid chunk value type <%.*s>!\n", gp->v.sval.len, gp->v.sval.s); return -1; } gp->v.ival = type; gp->type = GPARAM_TYPE_INT; } return 0; /* chunk id */ case 2: if (fixup_sgp(param) < 0) { LM_ERR("fixup for chunk type failed!\n"); return -1; } gp = *param; if (gp->type == GPARAM_TYPE_STR) { if ((type=fix_hex_int(&gp->v.sval)) < 0) { LM_ERR("Invalid chunk value type <%.*s>!\n", gp->v.sval.len, gp->v.sval.s); return -1; } gp->v.ival = type; gp->type = GPARAM_TYPE_INT; } return 0; /* vendor pvar */ case 3: /* data pvar */ case 4: return fixup_pvar(param); default: LM_ERR("Invalid param number <%d>\n", param_no); return -1; } return 0; } static int del_hep_fixup(void** param, int param_no) { int type; gparam_p gp; if (param_no == 1) { if (fixup_sgp(param) < 0) { LM_ERR("fixup for chunk type failed!\n"); return -1; } gp = *param; if (gp->type == GPARAM_TYPE_STR) { if ((type=fix_hex_int(&gp->v.sval)) < 0) { LM_ERR("Invalid chunk value type <%.*s>!\n", gp->v.sval.len, gp->v.sval.s); return -1; } gp->v.ival = type; gp->type = GPARAM_TYPE_INT; } return 0; } LM_ERR("Invalid param number <%d>\n", param_no); return -1; } static int w_set_hep_generic(struct sip_msg* msg, char* id, char* data) { return w_set_hep(msg, NULL, id, NULL, data); } static int w_set_hep(struct sip_msg* msg, char* type, char* id, char* vid, char* data) { int data_len; int data_type=TYPE_UTF8; int vendor_id=HEP_OPENSIPS_VENDOR_ID; unsigned int chunk_id; unsigned int idata; struct in_addr addr4; struct in6_addr addr6; str s; str data_s; gparam_p gp; struct hep_desc *h; struct hep_context *ctx; generic_chunk_t* ch; generic_chunk_t* it; if (id==NULL || data==NULL) { LM_ERR("Chunk id and chunk data can't be NULL!\n"); return -1; } if ((ctx=HEP_GET_CONTEXT(hep_api)) == NULL) { LM_WARN("not a hep message!\n"); return -1; } h = &ctx->h; if (h->version < 3) { LM_ERR("set chunk only available in HEPv3(EEP)!\n"); return -1; } if (type != NULL) { gp = (gparam_p)type; if (gp->type == GPARAM_TYPE_INT) { data_type = gp->v.ival; } else { if (fixup_get_svalue(msg, gp, &s) < 0) { LM_ERR("Getting vendor id value from pvar failed!\n"); return -1; } if ((data_type=fix_hep_value_type(&s))==TYPE_ERROR) { LM_ERR("Invalid data_type vlaue <%.*s>!\n", s.len, s.s); return -1; } } } gp = (gparam_p)id; if (gp->type == GPARAM_TYPE_INT) { chunk_id = gp->v.ival; } else { if (fixup_get_svalue(msg, gp, &s) < 0) { LM_ERR("Getting vendor id value from pvar failed!\n"); return -1; } if (parse_hep_name(&s, &chunk_id) < 0) { LM_ERR("Invalid chunk id/name!\n"); } } if (vid) { gp = (gparam_p)vid; if (gp->type == GPARAM_TYPE_INT) { vendor_id = gp->v.ival; } else { if (fixup_get_svalue(msg, gp, &s) < 0) { LM_ERR("Getting vendor id value from pvar failed!\n"); return -1; } if ((vendor_id=fix_hex_int(&s)) < 0) { LM_ERR("Invalid vendor id value <%.*s>!\n", s.len, s.s); return -1; } } } if (fixup_get_svalue(msg, (gparam_p)data, &data_s) < 0) { LM_ERR("failed to get chunk data value!\n"); return -1; } if (CHUNK_IS_IN_HEPSTRUCT(chunk_id)) { return set_generic_hep_chunk(&h->u.hepv3, chunk_id, &data_s); } it = NULL; for (it=h->u.hepv3.chunk_list; it; it = it->next) { if (it->chunk.type_id == chunk_id) break; } if (it == NULL){ ch = shm_malloc(sizeof(generic_chunk_t)); if (ch == NULL) goto shm_err; memset(ch, 0, sizeof(generic_chunk_t)); } else { ch = it; } if (data_type == TYPE_UTF8 || data_type == TYPE_BLOB) { data_len = data_s.len; } else if (data_type == TYPE_INET_ADDR) { data_len = sizeof(struct in_addr); if (inet_pton(AF_INET, data_s.s, &addr4)==0) { LM_ERR("not an IPv4 address <<%.*s>>!\n", data_s.len, data_s.s); return -1; } } else if (data_type == TYPE_INET6_ADDR) { data_len = sizeof(struct in6_addr); if (inet_pton(AF_INET6, data_s.s, &addr6)==0) { LM_ERR("not an IPv6 address <<%.*s>>!\n", data_s.len, data_s.s); return -1; } } else { data_len = data_type; if (str2int(&data_s, &idata) < 0) { LM_ERR("Invalid int value for chunk <%*.s>!\n", data_s.len, data_s.s); } /* keep values in big endian */ if (data_type == TYPE_UINT32) idata = htonl(idata); else if (data_type == TYPE_UINT16) idata = htons(idata); } /* if new chunk data is same length as the old one no problem there; * else we need to alloc new memory or delete some of the old one :( */ if (it && (it->chunk.length - sizeof(hep_chunk_t)) != data_len) { ch->data=shm_realloc(ch->data, data_len); if (ch->data == NULL) goto shm_err; } else if (it == NULL) { ch->data = shm_malloc(data_len); if (ch->data == NULL) goto shm_err; } if (data_type == TYPE_UTF8 || data_type == TYPE_BLOB) { memcpy(ch->data, data_s.s, data_len); } else if (data_type == TYPE_INET_ADDR) { memcpy(ch->data, &addr4, sizeof(struct in_addr)); } else if (data_type == TYPE_INET6_ADDR) { memcpy(ch->data, &addr6, sizeof(struct in6_addr)); } else { memcpy(ch->data, &idata, data_len); } ch->chunk.vendor_id = vendor_id; ch->chunk.type_id = chunk_id; ch->chunk.length = sizeof(hep_chunk_t) + data_len; /* if it's not a new chunk don't put it in the list */ if (it == NULL) { if (h->u.hepv3.chunk_list == NULL) { h->u.hepv3.chunk_list = ch; } else { for (it=h->u.hepv3.chunk_list; it->next; it=it->next); it->next=ch; } } return 1; shm_err: LM_ERR("no more shm!\n"); return -1; } static int w_get_hep_generic(struct sip_msg* msg, char* id, char* vid, char* data) { return w_get_hep(msg, NULL, id, vid, data); } static int w_get_hep(struct sip_msg* msg, char* type, char* id, char* vid, char* data) { int data_type; unsigned int net_data; unsigned int chunk_id; struct hep_desc *h; struct hep_context *ctx; str s; pv_spec_p data_pv, vendor_pv; pv_value_t data_val, vendor_val; gparam_p gp; generic_chunk_t* it; data_pv = (pv_spec_p)data; vendor_pv = (pv_spec_p)vid; if (id == NULL) { LM_ERR("No chunk id given!\n"); return -1; } if (vid == NULL && data == NULL) { LM_ERR("No output vars provided!\n"); return -1; } if ((ctx=HEP_GET_CONTEXT(hep_api)) == NULL) { LM_WARN("not a hep message!\n"); return -1; } h = &ctx->h; if (h->version < 3) { LM_ERR("get chunk only available in HEPv3(EEP)!\n"); return -1; } gp = (gparam_p)id; if (gp->type == GPARAM_TYPE_INT) { chunk_id = gp->v.ival; } else { if (fixup_get_svalue(msg, gp, &s) < 0) { LM_ERR("Getting vendor id value from pvar failed!\n"); return -1; } if (parse_hep_name(&s, &chunk_id) < 0) { LM_ERR("Invalid chunk id/name!\n"); } } if (CHUNK_IS_IN_HEPSTRUCT(chunk_id)) { /* don't need type for these; we already know it */ if (data) { if (get_hep_chunk(&h->u.hepv3, chunk_id, &data_val) < 0) goto set_pv_null; } if (vid) { vendor_val.ri = 0; vendor_val.flags = PV_TYPE_INT; } goto set_pv_values; } if (type == NULL) { LM_ERR("no type given! Don't know what to return!\n"); return -1; } gp = (gparam_p)type; if (gp->type == GPARAM_TYPE_INT) { data_type = gp->v.ival; } else { if (fixup_get_svalue(msg, gp, &s) < 0) { LM_ERR("Getting vendor id value from pvar failed!\n"); return -1; } if ((data_type=fix_hep_value_type(&s))==TYPE_ERROR) { LM_ERR("Invalid data_type vlaue <%.*s>!\n", s.len, s.s); return -1; } } for (it=h->u.hepv3.chunk_list; it; it=it->next) { if (it->chunk.type_id == chunk_id) { vendor_val.ri = it->chunk.vendor_id; vendor_val.flags = PV_TYPE_INT; switch (data_type) { /* all int types are in big endian */ case TYPE_UINT8: data_val.ri = ((char*)it->data)[0]; data_val.flags = PV_TYPE_INT; break; case TYPE_UINT16: net_data = ((unsigned short*)it->data)[0]; data_val.ri = htons(net_data); data_val.flags = PV_TYPE_INT; break; case TYPE_UINT32: net_data = ((unsigned int*)it->data)[0]; data_val.ri = htonl(net_data); data_val.flags = PV_TYPE_INT; break; case TYPE_INET_ADDR: hep_str.len = 0; memset(hep_str.s, 0, HEPBUF_LEN); if (inet_ntop(AF_INET, it->data, hep_str.s, INET_ADDRSTRLEN) == NULL) { LM_ERR("Not an IPv4 address!\n"); return -1; } hep_str.len = strlen(hep_str.s); data_val.rs = hep_str; data_val.flags = PV_VAL_STR; break; case TYPE_INET6_ADDR: hep_str.len = 0; memset(hep_str.s, 0, HEPBUF_LEN); if (inet_ntop(AF_INET6, it->data, hep_str.s, INET6_ADDRSTRLEN) == NULL) { LM_ERR("Not an IPv4 address!\n"); return -1; } hep_str.len = strlen(hep_str.s); data_val.rs = hep_str; data_val.flags = PV_VAL_STR; break; case TYPE_UTF8: case TYPE_BLOB: data_val.rs.s = (char*)it->data; data_val.rs.len = it->chunk.length - sizeof(hep_chunk_t); data_val.flags = PV_VAL_STR; break; } break; } } if (it == NULL) goto set_pv_null; set_pv_values: if (data) { if (pv_set_value(msg, data_pv, 0, &data_val) < 0) { LM_ERR("Failed setting data pvar value!\n"); return -1; } } if (vid) { if (pv_set_value(msg, vendor_pv, 0, &vendor_val) < 0) { LM_ERR("Failed setting data pvar value!\n"); return -1; } } return 1; set_pv_null: if (data) { if (pv_set_value(msg, data_pv, 0, NULL) < 0) { LM_ERR("Failed setting data pvar value!\n"); return -1; } } if (vid) { if (pv_set_value(msg, vendor_pv, 0, NULL) < 0) { LM_ERR("Failed setting data pvar value!\n"); return -1; } } return -1; } static int w_del_hep(struct sip_msg* msg, char *id) { unsigned int chunk_id; struct hep_desc *h; struct hep_context *ctx; str s; gparam_p gp; generic_chunk_t* it; generic_chunk_t* foo; if (id==NULL) { LM_ERR("No chunk id provided!\n"); return -1; } if ((ctx=HEP_GET_CONTEXT(hep_api)) == NULL) { LM_WARN("not a hep message!\n"); return -1; } h = &ctx->h; if (h->version < 3) { LM_ERR("del chunk only available in HEPv3(EEP)!\n"); return -1; } gp = (gparam_p)id; if (gp->type == GPARAM_TYPE_INT) { chunk_id = gp->v.ival; } else { if (fixup_get_svalue(msg, gp, &s) < 0) { LM_ERR("Getting vendor id value from pvar failed!\n"); return -1; } if (parse_hep_name(&s, &chunk_id) < 0) { LM_ERR("Invalid chunk id/name!\n"); } } if (CHUNK_IS_IN_HEPSTRUCT(chunk_id)) return del_hep_chunk(&h->u.hepv3, chunk_id); it=h->u.hepv3.chunk_list; if (it->chunk.type_id == chunk_id) { h->u.hepv3.chunk_list = it->next; foo = it; goto free_chunk; } while (it->next) { if (it->next->chunk.type_id == chunk_id) { foo = it->next; it->next = it->next->next; goto free_chunk; } it = it->next; } return -1; free_chunk: shm_free(foo->data); shm_free(foo); return 1; } static inline void osip_to_net_proto(unsigned char* proto) { if(*proto == PROTO_UDP) *proto=IPPROTO_UDP; else if(*proto == PROTO_TCP) *proto=IPPROTO_TCP; else if(*proto == PROTO_TLS) *proto=IPPROTO_IDP; /* fake protocol */ else if(*proto == PROTO_SCTP) *proto=IPPROTO_SCTP; else if(*proto == PROTO_WS) *proto=IPPROTO_ESP; /* fake protocol */ else { LM_ERR("unknown protocol [%d]\n", *proto); *proto = PROTO_NONE; } } static void hepv2_to_buf(struct hepv12* h2, char* buf, int *len) { int buflen; int payload_len = *len; /* instead of copying element by element we just convert * to network order and after convert it back */ h2->hdr.hp_sport = htons(h2->hdr.hp_sport); h2->hdr.hp_dport = htons(h2->hdr.hp_dport); memcpy(buf, &h2->hdr, sizeof(struct hep_hdr)); buflen = sizeof(struct hep_hdr); h2->hdr.hp_sport = ntohs(h2->hdr.hp_sport); h2->hdr.hp_dport = ntohs(h2->hdr.hp_dport); if (h2->hdr.hp_f==AF_INET) { memcpy(buf+buflen, &h2->addr.hep_ipheader, sizeof(struct hep_iphdr)); buflen += sizeof(struct hep_iphdr); } else { memcpy(buf+buflen, &h2->addr.hep_ip6header, sizeof(struct hep_ip6hdr)); buflen += sizeof(struct hep_ip6hdr); } if (h2->hdr.hp_v == 2) { memcpy(buf + buflen, &h2->hep_time, sizeof(struct hep_timehdr)); buflen += sizeof(struct hep_timehdr); } memcpy(buf + buflen, h2->payload, payload_len); *len = buflen + payload_len; } static void hepv3_to_buf(struct hepv3* h3, char* buf, int *len) { #define CONVERT_HEP_CHUNK(_src_chunk, _dst_chunk) \ do { \ _dst_chunk.vendor_id = htons(_src_chunk.vendor_id); \ _dst_chunk.length = htons(_src_chunk.length); \ _dst_chunk.type_id = htons(_src_chunk.type_id); \ } while(0); #define CHUNK_COPY_AND_UPDATE(buf, len, chunk) \ do { \ memcpy(buf+len, &chunk, sizeof(chunk)); \ len += sizeof(chunk); \ } while(0); int af; int buflen=sizeof(hep_ctrl_t); unsigned char osip_proto; generic_chunk_t* it; hep_chunk_t chunk_copy; u_int16_t data16; u_int32_t data32; if (h3->hg.ip_family.chunk.length == 0) { LM_WARN("ip family chunk removed! considering default IPv4!\n"); af = AF_INET; } else { if (h3->hg.ip_family.data != AF_INET && h3->hg.ip_family.data != AF_INET6) { LM_ERR("Unknown family <%d>! Will use IPv4\n", h3->hg.ip_family.data); af = AF_INET; } af = h3->hg.ip_family.data; } if (h3->hg.ip_family.chunk.length) { CONVERT_HEP_CHUNK(h3->hg.ip_family.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); memcpy(buf+buflen, &h3->hg.ip_family.data, sizeof(u_int8_t)); buflen += sizeof(u_int8_t); } if (h3->hg.ip_proto.chunk.length) { CONVERT_HEP_CHUNK(h3->hg.ip_proto.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); osip_proto = h3->hg.ip_proto.data; osip_to_net_proto(&h3->hg.ip_proto.data); memcpy(buf+buflen, &h3->hg.ip_proto.data, sizeof(u_int8_t)); buflen += sizeof(u_int8_t); h3->hg.ip_proto.data = osip_proto; } if (h3->hg.src_port.chunk.length) { CONVERT_HEP_CHUNK(h3->hg.src_port.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); data16 = htons(h3->hg.src_port.data); memcpy(buf+buflen, &data16, sizeof(u_int16_t)); buflen += sizeof(u_int16_t); } if (h3->hg.dst_port.chunk.length) { CONVERT_HEP_CHUNK(h3->hg.dst_port.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); data16 = htons(h3->hg.dst_port.data); memcpy(buf+buflen, &data16, sizeof(u_int16_t)); buflen += sizeof(u_int16_t); } if (h3->hg.time_sec.chunk.length) { CONVERT_HEP_CHUNK(h3->hg.time_sec.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); data32 = htonl(h3->hg.time_sec.data); memcpy(buf+buflen, &data32, sizeof(u_int32_t)); buflen += sizeof(u_int32_t); } if (h3->hg.time_usec.chunk.length) { CONVERT_HEP_CHUNK(h3->hg.time_usec.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); data32 = htonl(h3->hg.time_usec.data); memcpy(buf+buflen, &data32, sizeof(u_int32_t)); buflen += sizeof(u_int32_t); } if (h3->hg.proto_t.chunk.length) { CONVERT_HEP_CHUNK(h3->hg.proto_t.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); memcpy(buf+buflen, &h3->hg.proto_t.data, sizeof(u_int8_t)); buflen += sizeof(u_int8_t); } if (h3->hg.capt_id.chunk.length) { CONVERT_HEP_CHUNK(h3->hg.capt_id.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); data32 = htonl(h3->hg.capt_id.data); memcpy(buf+buflen, &data32, sizeof(u_int32_t)); buflen += sizeof(u_int32_t); } if (af == AF_INET) { if (h3->addr.ip4_addr.src_ip4.chunk.length) { CONVERT_HEP_CHUNK(h3->addr.ip4_addr.src_ip4.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); memcpy(buf+buflen, &h3->addr.ip4_addr.src_ip4.data, sizeof(struct in_addr)); buflen += sizeof(struct in_addr); } if (h3->addr.ip4_addr.dst_ip4.chunk.length) { CONVERT_HEP_CHUNK(h3->addr.ip4_addr.dst_ip4.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); memcpy(buf+buflen, &h3->addr.ip4_addr.dst_ip4.data, sizeof(struct in_addr)); buflen += sizeof(struct in_addr); } } else { if (h3->addr.ip6_addr.src_ip6.chunk.length) { CONVERT_HEP_CHUNK(h3->addr.ip6_addr.src_ip6.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); memcpy(buf+buflen, &h3->addr.ip6_addr.src_ip6.data, sizeof(struct in6_addr)); buflen += sizeof(struct in6_addr); } if (h3->addr.ip6_addr.dst_ip6.chunk.length) { CONVERT_HEP_CHUNK(h3->addr.ip6_addr.dst_ip6.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); memcpy(buf+buflen, &h3->addr.ip6_addr.dst_ip6.data, sizeof(struct in6_addr)); buflen += sizeof(struct in6_addr); } } for (it=h3->chunk_list; it; it=it->next) { CONVERT_HEP_CHUNK(it->chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); memcpy(buf+buflen, it->data, it->chunk.length - sizeof(hep_chunk_t)); buflen += it->chunk.length - sizeof(hep_chunk_t); } if (h3->payload_chunk.chunk.length) { CONVERT_HEP_CHUNK(h3->payload_chunk.chunk, chunk_copy); CHUNK_COPY_AND_UPDATE(buf, buflen, chunk_copy); memcpy(buf+buflen, h3->payload_chunk.data, h3->payload_chunk.chunk.length - sizeof(hep_chunk_t)); buflen += (h3->payload_chunk.chunk.length - sizeof(hep_chunk_t)); } memcpy(((hep_ctrl_t*)buf)->id, HEP_HEADER_ID, HEP_HEADER_ID_LEN); ((hep_ctrl_t*)buf)->length = htons(buflen); *len = buflen; } static int build_hep_buf(str* hep_buf, int* proto) { struct hep_context *ctx; ctx = HEP_GET_CONTEXT(hep_api); if (ctx == NULL) { LM_ERR("Hep context not there!"); return -1; } if (ctx->h.version == 3) { *proto = ctx->h.u.hepv3.hg.ip_proto.data; hepv3_to_buf(&ctx->h.u.hepv3, hep_buf->s, &hep_buf->len); } else { *proto = ctx->h.u.hepv12.hdr.hp_p; hepv2_to_buf(&ctx->h.u.hepv12, hep_buf->s, &hep_buf->len); } return ctx->h.version; } static int w_hep_relay(struct sip_msg *msg) { struct proxy_l* proxy; struct sip_uri uri; struct socket_info* send_sock; union sockaddr_union to; str* uri_s; str buf_s; int hep_version; int proto; int hep_proto; char proto_buf[PROTO_NAME_MAX_SIZE]; if (msg==NULL) { LM_ERR("Invalid sip message!\n"); return -1; } uri_s=GET_NEXT_HOP(msg); if (parse_uri(uri_s->s, uri_s->len, &uri) < 0) { LM_ERR("bad uri <%.*s>!\n", uri_s->len, uri_s->s); return -1; } /* build everything but the sip message because we don't have it yet*/ buf_s.s = payload_buf; /* this way we will know what's the size of the hep payload * in version 1/2 */ buf_s.len = msg->len; if ((hep_version=build_hep_buf(&buf_s, &proto)) < 0) { LM_ERR("failed to append hep header!\n"); return -1; } if (uri.proto == 0 || uri.proto == PROTO_UDP) { hep_proto = PROTO_HEP_UDP; } else if (uri.proto == PROTO_TCP) { if (hep_version == 1 || hep_version == 2) { LM_ERR("TCP not supported for HEPv%d\n", hep_version); return -1; } hep_proto = PROTO_HEP_TCP; } else { LM_ERR("cannot send hep with proto %s\n", proto2str(uri.proto, proto_buf)); return -1; } /* get net info */ proxy = mk_proxy( &uri.host, uri.port_no?uri.port_no:SIP_PORT, proto, 0 ); if (proxy == 0) { LM_ERR("bad host name in URI <%.*s>\n", uri_s->len, ZSW(uri_s->s)); return 0; } hostent2su( &to, &proxy->host, proxy->addr_idx, (proxy->port)?proxy->port:SIP_PORT); /* FIXME */ send_sock=get_send_socket(0, &to, hep_proto); if (send_sock==0){ LM_ERR("cannot forward to af %d, proto %d no corresponding" "listening socket\n", to.s.sa_family, proxy->proto); return -1; } do { if (msg_send(NULL, hep_proto, &to, 0, buf_s.s, buf_s.len, msg)<0){ LM_ERR("failed to send message!\n"); continue; } break; } while( get_next_su( proxy, &to, 0)==0 ); free_proxy(proxy); pkg_free(proxy); return 1; } static int w_hep_resume_sip(struct sip_msg *msg) { struct hep_context* ctx; if (current_processing_ctx == NULL || msg == NULL) { return -1; } if ((ctx=HEP_GET_CONTEXT(hep_api))==NULL) { LM_WARN("not a hep message!\n"); return -1; } if (ctx == NULL) { LM_ERR(" no hep context!\n"); return -1; } if (ctx->resume_with_sip != 0) { LM_ERR("Called this function twice! You should call it" "only from the hep route!\n"); return -1; } ctx->resume_with_sip = 1; /* break hep route execution */ return 0; } #define capture_is_off(_msg) \ (capture_on_flag==NULL || *capture_on_flag==0) /* * Report Capture logic */ /* fixup */ static void set_rtcp_keys(void) { rtcp_db_keys[0] = &date_column; rtcp_db_keys[1] = µ_ts_column; rtcp_db_keys[2] = &correlation_column; rtcp_db_keys[3] = &source_ip_column; rtcp_db_keys[4] = &source_port_column; rtcp_db_keys[5] = &dest_ip_column; rtcp_db_keys[6] = &dest_port_column; rtcp_db_keys[7] = &proto_column; rtcp_db_keys[8] = &family_column; rtcp_db_keys[9] = &type_column; rtcp_db_keys[10] = &node_column; rtcp_db_keys[11] = &msg_column; } #define FIXUP_RC_PARAMS(param, fix_func, param_no) \ do { \ switch (param_no) { \ case 1: \ return fix_func(param, &rc_list); \ case 2: \ case 3: \ return fixup_sgp(param); \ default: \ LM_ERR("Invalid param number!\n"); \ return -1; \ } \ } while(0); static int rc_fixup_1(void** param, int param_no) { if (param_no != 1) { LM_ERR("Invalid param number!\n"); return -1; } return fixup_sgp(param); } static int rc_fixup(void** param, int param_no) { if (param_no < 1 || param_no > 3) { LM_ERR("Invalid param number!\n"); return -1; } FIXUP_RC_PARAMS(param, fixup_tz_table, param_no); return 0; } static int rc_async_fixup_1(void** param, int param_no) { if (param_no != 1) { LM_ERR("Invalid param number!\n"); return -1; } return fixup_sgp(param); } static int rc_async_fixup(void** param, int param_no) { if (param_no < 1 || param_no > 3) { LM_ERR("Invalid param number!\n"); return -1; } FIXUP_RC_PARAMS(param, fixup_async_tz_table, param_no); return 0; } static inline void build_hepv3_obj(struct hepv3* h3, struct _sipcapture_object* sco) { sco->proto = h3->hg.ip_proto.data; sco->family = h3->hg.ip_family.data; if (h3->hg.ip_family.data == AF_INET) { inet_ntop(AF_INET, &(h3->addr.ip4_addr.dst_ip4.data), sco->destination_ip.s, INET_ADDRSTRLEN); inet_ntop(AF_INET, &(h3->addr.ip4_addr.src_ip4.data), sco->source_ip.s, INET_ADDRSTRLEN); } else { inet_ntop(AF_INET, &(h3->addr.ip6_addr.dst_ip6.data), sco->destination_ip.s, INET6_ADDRSTRLEN); inet_ntop(AF_INET, &(h3->addr.ip6_addr.src_ip6.data), sco->source_ip.s, INET6_ADDRSTRLEN); } sco->source_ip.len = strlen(sco->source_ip.s); sco->source_port = h3->hg.src_port.data; sco->destination_ip.len = strlen(sco->destination_ip.s); sco->destination_port = h3->hg.dst_port.data; sco->proto_type = h3->hg.proto_t.data; sco->tmstamp = (unsigned long long)h3->hg.time_sec.data*1000000 + h3->hg.time_usec.data; /* WARN node must be allocated */ sco->node.len = snprintf(sco->node.s, 100, "%.*s:%i", capture_node.len, capture_node.s, h3->hg.capt_id.data); } static inline void build_hepv2_obj(struct hepv12* h2, struct _sipcapture_object* sco) { struct timeval tvb; sco->proto = h2->hdr.hp_p; sco->family = h2->hdr.hp_f; if (h2->hdr.hp_f == AF_INET) { inet_ntop(AF_INET, &(h2->addr.hep_ipheader.hp_dst), sco->destination_ip.s, INET_ADDRSTRLEN); inet_ntop(AF_INET, &(h2->addr.hep_ipheader.hp_src), sco->source_ip.s, INET_ADDRSTRLEN); } else { inet_ntop(AF_INET, &(h2->addr.hep_ip6header.hp6_dst), sco->destination_ip.s, INET6_ADDRSTRLEN); inet_ntop(AF_INET, &(h2->addr.hep_ip6header.hp6_src), sco->source_ip.s, INET6_ADDRSTRLEN); } sco->source_ip.len = strlen(sco->source_ip.s); sco->source_port = h2->hdr.hp_sport; sco->destination_ip.len = strlen(sco->destination_ip.s); sco->destination_port = h2->hdr.hp_dport; /* only sip in hepv1/2 */ sco->proto_type = 1; if (h2->hdr.hp_v == 2) { sco->tmstamp = (unsigned long long)h2->hep_time.tv_sec*1000000 + h2->hep_time.tv_usec; /* WARN node must be allocated */ sco->node.len = snprintf(sco->node.s, 100, "%.*s:%i", capture_node.len, capture_node.s, h2->hep_time.captid); } else { gettimeofday(&tvb, NULL); sco->tmstamp = (unsigned long long)tvb.tv_sec * 1000000 + tvb.tv_usec; sco->node = capture_node; } } static inline int append_rc_values(char* buf, int max_len, db_val_t* db_vals) { int len; len = snprintf(buf, max_len, RTCP_VALUES_STR, VAL_TIME(db_vals+0), VAL_BIGINT(db_vals+1), VAL_STR(db_vals+2).len, VAL_STR(db_vals+2).s, VAL_STR(db_vals+3).len, VAL_STR(db_vals+3).s, VAL_INT(db_vals+4), VAL_STR(db_vals+5).len, VAL_STR(db_vals+5).s, VAL_INT(db_vals+6), VAL_INT(db_vals+7), VAL_INT(db_vals+8), VAL_INT(db_vals+9), VAL_STR(db_vals+10).len, VAL_STR(db_vals+10).s, VAL_STR(db_vals+11).len, VAL_STR(db_vals+11).s ); return len; } static int report_capture(struct sip_msg* msg, str* table, str* cor_id, unsigned int* proto_t, struct tz_table_list* t_el, async_resume_module **resume_f, void** resume_param) { char node[100]; char src_ip[INET6_ADDRSTRLEN], dst_ip[INET6_ADDRSTRLEN]; struct _sipcapture_object sco; struct hep_desc *h; struct hep_context *ctx; db_val_t db_vals[RTCP_NR_KEYS]; if ((ctx=HEP_GET_CONTEXT(hep_api)) == NULL) { LM_WARN("not a hep message!\n"); return -1; } h= &ctx->h; memset(&sco, 0, sizeof(struct _sipcapture_object)); sco.node.s = node; sco.source_ip.s = src_ip; sco.destination_ip.s = dst_ip; if (h->version == 3) { build_hepv3_obj(&h->u.hepv3, &sco); } else { build_hepv2_obj(&h->u.hepv12, &sco); } memset(db_vals, 0, sizeof(db_val_t) * RTCP_NR_KEYS); db_vals[0].type = DB_DATETIME; db_vals[0].val.time_val = (sco.tmstamp/1000000); db_vals[1].type = DB_BIGINT; db_vals[1].val.bigint_val = sco.tmstamp; db_vals[2].type = DB_STR; db_vals[2].val.str_val = *cor_id; db_vals[3].type = DB_STR; db_vals[3].val.str_val = sco.source_ip; db_vals[4].type = DB_INT; db_vals[4].val.int_val = sco.source_port; db_vals[5].type = DB_STR; db_vals[5].val.str_val = sco.destination_ip; db_vals[6].type = DB_INT; db_vals[6].val.int_val = sco.destination_port; db_vals[7].type = DB_INT; db_vals[7].val.int_val = sco.proto; db_vals[8].type = DB_INT; db_vals[8].val.int_val = sco.family; db_vals[9].type = DB_INT; db_vals[9].val.int_val = proto_t?(*proto_t):sco.proto_type; db_vals[10].type = DB_STR; db_vals[10].val.str_val = sco.node; db_vals[11].type = DB_BLOB; /* we can have other pyload than sip only for hepv3 */ if (h->version == 3) { db_vals[11].val.str_val.s = h->u.hepv3.payload_chunk.data; db_vals[11].val.str_val.len = h->u.hepv3.payload_chunk.chunk.length - sizeof(h->u.hepv3.payload_chunk.chunk); } else { db_vals[11].val.str_val.s = msg->buf; db_vals[11].val.str_val.len = msg->len; } /* each query has it's own parameters for the prepared statements */ if (con_set_inslist(&db_funcs,db_con,&rc_ins_list,db_keys,NR_KEYS) < 0 ) CON_RESET_INSLIST(db_con); CON_PS_REFERENCE(db_con) = &rc_ps; if (!resume_f && db_sync_store(db_vals, rtcp_db_keys, RTCP_NR_KEYS) != 1) { LM_ERR("failed to insert into database\n"); return -1; } else if (resume_f) { return db_async_store(db_vals, rtcp_db_keys, RTCP_NR_KEYS, append_rc_values, resume_f, resume_param, t_el); } return 1; } static int w_report_capture_1(struct sip_msg* msg, char* cor_id_p) { return w_report_capture(msg, NULL, cor_id_p, NULL, NULL, NULL); } static int w_report_capture_2(struct sip_msg* msg, char* table_p, char* cor_id_p) { return w_report_capture(msg, table_p, cor_id_p, NULL, NULL, NULL); } static int w_report_capture_3(struct sip_msg* msg, char* table_p, char* cor_id_p, char* proto_t_p) { return w_report_capture(msg, table_p, cor_id_p, proto_t_p, NULL, NULL); } static int w_report_capture_async_1(struct sip_msg* msg, async_resume_module** resume_f, void** resume_param, char* cor_id_p) { return w_report_capture(msg, NULL, cor_id_p, NULL, resume_f, resume_param); } static int w_report_capture_async_2(struct sip_msg* msg, async_resume_module** resume_f, void** resume_param, char* table_p, char* cor_id_p) { return w_report_capture(msg, table_p, cor_id_p, NULL, resume_f, resume_param); } static int w_report_capture_async_3(struct sip_msg* msg, async_resume_module** resume_f, void** resume_param, char* table_p, char* cor_id_p, char* proto_t_p) { return w_report_capture(msg, table_p, cor_id_p, proto_t_p, resume_f, resume_param); } static int w_report_capture(struct sip_msg* msg, char* table_p, char* cor_id_p, char* proto_t_p, async_resume_module **resume_f, void** resume_param) { unsigned int proto_t; str cor_id_s; str proto_t_s; tz_table_t* rct; struct tz_table_list* t_el=&rc_global; if (cor_id_p == NULL) { LM_ERR("correaltion id param is mandatory!\n"); return -1; } if (table_p) { rct = (tz_table_t *)table_p; } else { rct = &rc_table; } if (fixup_get_svalue(msg, (gparam_p)cor_id_p, &cor_id_s) < 0 ) { LM_ERR("failed to fetch correlation id!\n"); return -1; } if (cor_id_s.s == NULL || cor_id_s.len == 0) { LM_ERR("empty correlation id!\n"); return -1; } if (proto_t_p) { if (fixup_get_svalue(msg, (gparam_p)proto_t_p, &proto_t_s) < 0 ) { LM_ERR("failed to fetch correlation id!\n"); return -1; } if (str2int(&proto_t_s, &proto_t) < 0) { LM_ERR("Invalid proto type value!\n"); return -1; } } if (IS_ASYNC_F && HAVE_MULTIPLE_ASYNC_INSERT) { if (table_p) { if ((t_el=search_table(rct, rc_list)) == NULL) { LM_ERR("Invalid table given!\n"); return -1; } } } build_table_name(rct, ¤t_table); if (rct->suffix.s && rct->suffix.len && IS_ASYNC_F && HAVE_MULTIPLE_ASYNC_INSERT) { if (try_change_suffix(t_el, ¤t_table) < 0) return -1; } return report_capture(msg, ¤t_table, &cor_id_s, proto_t_p?&proto_t:NULL, t_el, resume_f, resume_param); } /* * */ /*! \brief * MI Sip_capture command * * MI command format: * name: sip_capture * attribute: name=none, value=[on|off] */ static struct mi_root* sip_capture_mi(struct mi_root* cmd_tree, void* param ) { struct mi_node* node; struct mi_node *rpl; struct mi_root *rpl_tree ; node = cmd_tree->node.kids; if(node == NULL) { rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree == 0) return 0; rpl = &rpl_tree->node; if (*capture_on_flag == 0 ) { node = add_mi_node_child(rpl,0,0,0,MI_SSTR("off")); } else if (*capture_on_flag == 1) { node = add_mi_node_child(rpl,0,0,0,MI_SSTR("on")); } return rpl_tree ; } if(capture_on_flag==NULL) return init_mi_tree( 500, MI_SSTR(MI_INTERNAL_ERR)); if ( node->value.len==2 && (node->value.s[0]=='o' || node->value.s[0]=='O') && (node->value.s[1]=='n'|| node->value.s[1]=='N')) { *capture_on_flag = 1; return init_mi_tree( 200, MI_SSTR(MI_OK)); } else if ( node->value.len==3 && (node->value.s[0]=='o' || node->value.s[0]=='O') && (node->value.s[1]=='f'|| node->value.s[1]=='F') && (node->value.s[2]=='f'|| node->value.s[2]=='F')) { *capture_on_flag = 0; return init_mi_tree( 200, MI_SSTR(MI_OK)); } else { return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM)); } } /* Local raw socket */ int raw_capture_socket(struct ip_addr* ip, str* iface, int port_start, int port_end, int proto) { int sock = -1; union sockaddr_union su; #ifdef __OS_linux struct sock_fprog pf; char short_ifname[sizeof(int)]; int ifname_len; char* ifname; #endif //0x0003 - all packets if(proto == IPPROTO_IPIP) { sock = socket(PF_INET, SOCK_RAW, proto); } #ifdef __OS_linux else if(proto == htons(0x800)) { sock = socket(PF_PACKET, SOCK_RAW, proto); } #endif else { LM_ERR("LSF currently supported only on linux\n"); goto error; } if (sock==-1) goto error; #ifdef __OS_linux /* set socket options */ if (iface && iface->s){ /* workaround for linux bug: arg to setsockopt must have at least * sizeof(int) size or EINVAL would be returned */ if (iface->lens, iface->len); short_ifname[iface->len]=0; /* make sure it's zero term */ ifname_len=sizeof(short_ifname); ifname=short_ifname; }else{ ifname_len=iface->len; ifname=iface->s; } if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, ifname_len) <0){ LM_ERR("could not bind to %.*s: %s [%d]\n", iface->len, ZSW(iface->s), strerror(errno), errno); goto error; } } if(bpf_on) { memset(&pf, 0, sizeof(pf)); pf.len = sizeof(BPF_code) / sizeof(BPF_code[0]); pf.filter = (struct sock_filter *) BPF_code; if(!port_end) port_end = port_start; /* Start PORT */ BPF_code[5] = (struct sock_filter)BPF_JUMP(0x35, port_start, 0, 1); BPF_code[8] = (struct sock_filter)BPF_JUMP(0x35, port_start, 11, 13); BPF_code[16] = (struct sock_filter)BPF_JUMP(0x35, port_start, 0, 1); BPF_code[19] = (struct sock_filter)BPF_JUMP(0x35, port_start, 0, 2); /* Stop PORT */ BPF_code[6] = (struct sock_filter)BPF_JUMP(0x25, port_end, 0, 14); BPF_code[17] = (struct sock_filter)BPF_JUMP(0x25, port_end, 0, 3); BPF_code[20] = (struct sock_filter)BPF_JUMP(0x25, port_end, 1, 0); /* Attach the filter to the socket */ if(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) < 0 ) { LM_ERR("setsockopt filter: [%s] [%d]\n", strerror(errno), errno); } } #endif if (ip && proto == IPPROTO_IPIP){ init_su(&su, ip, 0); if (bind(sock, &su.s, sockaddru_len(su))==-1){ LM_ERR("bind(%s) failed: %s [%d]\n", ip_addr2a(ip), strerror(errno), errno); goto error; } } return sock; error: if (sock!=-1) close(sock); return -1; } /* Local raw receive loop */ int raw_capture_rcv_loop(int rsock, int port1, int port2, int ipip) { static char buf [BUF_SIZE+1]; union sockaddr_union from; union sockaddr_union to; struct receive_info ri; int len; struct ip *iph; struct udphdr *udph; char* udph_start; unsigned short udp_len; int offset = 0; char* end; unsigned short dst_port; unsigned short src_port; struct ip_addr dst_ip, src_ip; for(;;) { len = recvfrom(rsock, buf, BUF_SIZE, 0, 0, 0); if (len<0){ if (len==-1){ LM_ERR("recvfrom: %s [%d]\n", strerror(errno), errno); if ((errno==EINTR)||(errno==EWOULDBLOCK)) continue; else goto error; }else{ LM_DBG("recvfrom error: %d\n", len); continue; } } end=buf+len; offset = ipip ? sizeof(struct ip) : ETHHDR; if (len < (sizeof(struct ip)+sizeof(struct udphdr) + offset)) { LM_DBG("received small packet: %d. Ignore it\n",len); continue; } iph = (struct ip*) (buf + offset); offset+=iph->ip_hl*4; udph_start = buf+offset; udph = (struct udphdr*) udph_start; offset +=sizeof(struct udphdr); if ((buf+offset)>end){ continue; } udp_len=ntohs(udph->uh_ulen); if ((udph_start+udp_len)!=end){ if ((udph_start+udp_len)>end){ continue; }else{ LM_DBG("udp length too small: %d/%d\n", (int)udp_len, (int)(end-udph_start)); continue; } } /*FIL IPs*/ dst_ip.af=AF_INET; dst_ip.len=4; dst_ip.u.addr32[0]=iph->ip_dst.s_addr; /* fill dst_port */ dst_port=ntohs(udph->uh_dport); ip_addr2su(&to, &dst_ip, dst_port); /* fill src_port */ src_port=ntohs(udph->uh_sport); src_ip.af=AF_INET; src_ip.len=4; src_ip.u.addr32[0]=iph->ip_src.s_addr; ip_addr2su(&from, &src_ip, src_port); su_setport(&from, src_port); ri.src_su=from; su2ip_addr(&ri.src_ip, &from); ri.src_port=src_port; su2ip_addr(&ri.dst_ip, &to); ri.dst_port=dst_port; ri.proto=PROTO_UDP; /* cut off the offset */ len -= offset; if (len= port1 && src_port <= port2) || (dst_port >= port1 && dst_port <= port2) || (!port2 && (src_port == port1 || dst_port == port1))) receive_msg(buf+offset, len, &ri, NULL); } return 0; error: return -1; } #undef QUERY_BUF #undef QUERY_LEN #undef LAST_SUFFIX #undef HAVE_MULTIPLE_ASYNC_INSERT opensips-2.2.2/modules/sipcapture/sipcapture.h000066400000000000000000000036371300170765700215530ustar00rootroot00000000000000/* * hep related structure * * Copyright (C) 2011 Alexandr Dubovikov (QSC AG) (alexandr.dubovikov@gmail.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _SIPCAPTURE_H_ #define _SIPCAPTURE_H_ typedef char T8; #define BINDING_REQUEST 0x0001; #if 0 #ifdef __OS_solaris typedef uint8_t u_int8_t; typedef uint16_t u_int16_t; typedef uint32_t u_int32_t; #define IPPROTO_IPIP IPPROTO_ENCAP /* Solaris IPIP protocol has name ENCAP */ #endif struct hep_hdr{ u_int8_t hp_v; /* version */ u_int8_t hp_l; /* length */ u_int8_t hp_f; /* family */ u_int8_t hp_p; /* protocol */ u_int16_t hp_sport; /* source port */ u_int16_t hp_dport; /* destination port */ }; struct hep_timehdr{ u_int32_t tv_sec; /* seconds */ u_int32_t tv_usec; /* useconds */ u_int16_t captid; /* Capture ID node */ }; struct hep_iphdr{ struct in_addr hp_src; struct in_addr hp_dst; /* source and dest address */ }; struct hep_ip6hdr { struct in6_addr hp6_src; /* source address */ struct in6_addr hp6_dst; /* destination address */ }; #endif #endif /* _SIPCAPTURE_H_ */ opensips-2.2.2/modules/sipcapture/sql/000077500000000000000000000000001300170765700200115ustar00rootroot00000000000000opensips-2.2.2/modules/sipcapture/sql/rtcpcapture.sql000066400000000000000000000014511300170765700230670ustar00rootroot00000000000000CREATE TABLE IF NOT EXISTS `rtcp_capture` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `micro_ts` bigint(18) NOT NULL DEFAULT '0', `correlation_id` varchar(256) NOT NULL DEFAULT '', `source_ip` varchar(60) NOT NULL DEFAULT '', `source_port` int(10) NOT NULL DEFAULT 0, `destination_ip` varchar(60) NOT NULL DEFAULT '', `destination_port` int(10) NOT NULL DEFAULT 0, `proto` int(5) NOT NULL DEFAULT 0, `family` int(1) DEFAULT NULL, `type` int(2) NOT NULL DEFAULT 0, `node` varchar(125) NOT NULL DEFAULT '', `msg` varchar(1500) NOT NULL DEFAULT '', PRIMARY KEY (`id`,`date`), KEY `date` (`date`), KEY `correlationid` (`correlation_id`(255)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8; opensips-2.2.2/modules/sipcapture/sql/sipcapture.sql000066400000000000000000000043311300170765700227120ustar00rootroot00000000000000/* * only for MYSQL >= 5.1.43 */ /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `sip_capture` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `micro_ts` bigint(18) NOT NULL DEFAULT '0', `method` varchar(50) NOT NULL DEFAULT '', `reply_reason` varchar(100) NOT NULL, `ruri` varchar(200) NOT NULL DEFAULT '', `ruri_user` varchar(100) NOT NULL DEFAULT '', `from_user` varchar(100) NOT NULL DEFAULT '', `from_domain` varchar(150) NOT NULL DEFAULT '', `from_tag` varchar(64) NOT NULL DEFAULT '', `to_user` varchar(100) NOT NULL DEFAULT '', `to_domain` varchar(150) NOT NULL DEFAULT '', `to_tag` varchar(64) NOT NULL, `pid_user` varchar(100) NOT NULL DEFAULT '', `contact_user` varchar(120) NOT NULL, `auth_user` varchar(120) NOT NULL, `callid` varchar(100) NOT NULL DEFAULT '', `callid_aleg` varchar(100) NOT NULL DEFAULT '', `via_1` varchar(256) NOT NULL, `via_1_branch` varchar(80) NOT NULL, `cseq` varchar(25) NOT NULL, `diversion` varchar(256) NOT NULL, `reason` varchar(200) NOT NULL, `content_type` varchar(256) NOT NULL, `auth` varchar(256) NOT NULL, `user_agent` varchar(256) NOT NULL, `source_ip` varchar(50) NOT NULL DEFAULT '', `source_port` int(10) NOT NULL, `destination_ip` varchar(50) NOT NULL DEFAULT '', `destination_port` int(10) NOT NULL, `contact_ip` varchar(60) NOT NULL, `contact_port` int(10) NOT NULL, `originator_ip` varchar(60) NOT NULL DEFAULT '', `originator_port` int(10) NOT NULL, `correlation_id` varchar(256) NOT NULL DEFAULT '', `proto` int(5) NOT NULL, `family` int(1) DEFAULT NULL, `rtp_stat` varchar(256) NOT NULL, `type` int(2) NOT NULL, `node` varchar(125) NOT NULL, `msg` text NOT NULL, PRIMARY KEY (`id`,`date`), KEY `ruri_user` (`ruri_user`), KEY `from_user` (`from_user`), KEY `to_user` (`to_user`), KEY `pid_user` (`pid_user`), KEY `auth_user` (`auth_user`), KEY `callid_aleg` (`callid_aleg`), KEY `date` (`date`), KEY `callid` (`callid`) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 PARTITION BY RANGE ( UNIX_TIMESTAMP(`date`) ) ( PARTITION pmax VALUES LESS THAN (MAXVALUE) ); opensips-2.2.2/modules/sipmsgops/000077500000000000000000000000001300170765700170575ustar00rootroot00000000000000opensips-2.2.2/modules/sipmsgops/Makefile000066400000000000000000000004151300170765700205170ustar00rootroot00000000000000# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=sipmsgops.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/sipmsgops/README000066400000000000000000000605431300170765700177470ustar00rootroot00000000000000sipmsgops Module Andrei Pelinescu-Onciul FhG FOKUS Edited by Andrei Pelinescu-Onciul Edited by Daniel-Constantin Mierla Edited by Ovidiu Sas Edited by Razvan Crainea Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 7480 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Functions 1.3.1. filter_body(content_type) 1.3.2. append_to_reply(txt) 1.3.3. append_hf(txt) 1.3.4. append_hf(txt, hdr) 1.3.5. insert_hf(txt) 1.3.6. insert_hf(txt, hdr) 1.3.7. append_urihf(prefix, suffix) 1.3.8. is_present_hf(hf_name) 1.3.9. append_time() 1.3.10. is_method(name) 1.3.11. remove_hf(hname [,flags]) 1.3.12. has_body(), has_body(mime) 1.3.13. is_audio_on_hold() 1.3.14. is_privacy(privacy_type) 1.3.15. strip_body(), strip_body(mime) 1.3.16. add_body(body_text, new_content_type) 1.3.17. sipmsg_validate([flags[,result_pvar]]) 1.3.18. codec_exists (name [,clock] ) 1.3.19. codec_delete (name [,clock] ) 1.3.20. codec_move_up (name [,clock] ) 1.3.21. codec_move_down (name [,clock] ) 1.3.22. codec_exists_re ( regexp ) 1.3.23. codec_delete_re ( regexp ) 1.3.24. codec_delete_except_re ( regexp ) 1.3.25. codec_move_up_re ( regexp ) 1.3.26. codec_move_down_re ( regexp ) 1.3.27. change_reply_status(code, reason) 1.3.28. stream_exists(regexp) 1.3.29. stream_delete(regexp) 1.4. Known Limitations List of Examples 1.1. filter_body usage 1.2. append_to_reply usage 1.3. append_hf usage 1.4. append_hf usage 1.5. insert_hf usage 1.6. insert_hf usage 1.7. append_urihf usage 1.8. is_present_hf usage 1.9. append_time usage 1.10. is_method usage 1.11. remove_hf usage 1.12. has_body usage 1.13. is_audio_on_hold usage 1.14. is_privacy usage 1.15. strip_body usage 1.16. add_body usage 1.17. sipmsg_validate usage 1.18. codec_exists usage 1.19. codec_delete usage 1.20. codec_move_up usage 1.21. codec_move_down usage 1.22. codec_move_down usage 1.23. codec_exists_re usage 1.24. codec_delete_re usage 1.25. codec_delete_except_re usage 1.26. codec_move_up_re usage 1.27. codec_move_down_re usage 1.28. codec_move_down usage 1.29. change_reply_status usage 1.30. stream_exists usage 1.31. stream_delete usage Chapter 1. Admin Guide 1.1. Overview The module implements SIP based operations over the messages processed by OpenSIPS. SIP is a text based protocol and the module provides a large set of very useful functions to manipulate the message at SIP level, e.g., inserting new headers or deleting them, check for method type, etc. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Functions 1.3.1. filter_body(content_type) Filters multipart body by leaving out all other body parts except the first body part of given type. Meaning of the parameters is as follows: * content_type - Content type to be left in the body. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.1. filter_body usage ... if (has_body("multipart/mixed")) { if (filter_body("application/sdp") { remove_hf("Content-Type"); append_hf("Content-Type: application/sdp\r\n"); } else { xlog("Body part application/sdp not found\n"); } } ... 1.3.2. append_to_reply(txt) Append txt as header to all replies that will be generated by OpenSIPS for this request. Meaning of the parameters is as follows: * txt - String which may contains pseudo-variables. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ERROR_ROUTE. Example 1.2. append_to_reply usage ... append_to_reply("Foo: bar\r\n"); append_to_reply("Foo: $rm at $Ts\r\n"); ... 1.3.3. append_hf(txt) Appends 'txt' as header after the last header field. Meaning of the parameters is as follows: * txt - Header field to be appended. The value can contain pseudo-variables which will be replaced at run time. Note: Headers which are added in main route cannot be removed in further routes (e.g. failure routes). So, the idea is not to add there any headers that you might want to remove later. To add headers temporarely use the branch route because the changes you do there are per-branch. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.3. append_hf usage ... append_hf("P-hint: VOICEMAIL\r\n"); append_hf("From-username: $fU\r\n"); ... 1.3.4. append_hf(txt, hdr) Appends 'txt' as header after first 'hdr' header field. Meaning of the parameters is as follows: * txt - Header field to be appended. The value can contain pseudo-variables which will be replaced at run time. * hdr - Header name after which the 'txt' is appended. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.4. append_hf usage ... append_hf("P-hint: VOICEMAIL\r\n", "Call-ID"); append_hf("From-username: $fU\r\n", "Call-ID"); ... 1.3.5. insert_hf(txt) Inserts 'txt' as header before the first header field. Meaning of the parameters is as follows: * txt - Header field to be inserted. The value can contain pseudo-variables which will be replaced at run time. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.5. insert_hf usage ... insert_hf("P-hint: VOICEMAIL\r\n"); insert_hf("To-username: $tU\r\n"); ... 1.3.6. insert_hf(txt, hdr) Inserts 'txt' as header before first 'hdr' header field. Meaning of the parameters is as follows: * txt - Header field to be inserted. The value can contain pseudo-variables which will be replaced at run time. * hdr - Header name before which the 'txt' is inserted. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.6. insert_hf usage ... insert_hf("P-hint: VOICEMAIL\r\n", "Call-ID"); insert_hf("To-username: $tU\r\n", "Call-ID"); ... 1.3.7. append_urihf(prefix, suffix) Append header field name with original Request-URI in middle. Meaning of the parameters is as follows: * prefix - string (usually at least header field name). * suffix - string (usually at least line terminator). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.7. append_urihf usage ... append_urihf("CC-Diversion: ", "\r\n"); ... 1.3.8. is_present_hf(hf_name) Return true if a header field is present in message. Note The function is also able to distinguish the compact names. For exmaple “From†will match with “f†Meaning of the parameters is as follows: * hf_name - Header field name.(long or compact form). The hf_name parameter can have the following types: + string - Static header field name + pvar - Header field name is given as a pseudo-variable (as string value) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.8. is_present_hf usage ... if (is_present_hf("From")) log(1, "From HF Present"); ... 1.3.9. append_time() Adds a time header to the reply of the request. You must use it before functions that are likely to send a reply, e.g., save() from 'registrar' module. Header format is: “Date: %a, %d %b %Y %H:%M:%S GMTâ€, with the legend: * %a abbreviated week of day name (locale) * %d day of month as decimal number * %b abbreviated month name (locale) * %Y year with century * %H hour * %M minutes * %S seconds Return true if a header was successfully appended. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.9. append_time usage ... append_time(); ... 1.3.10. is_method(name) Check if the method of the message matches the name. If name is a known method (invite, cancel, ack, bye, options, info, update, register, message, subscribe, notify, refer, prack), the function performs method ID testing (integer comparison) instead of ignore case string comparison. The 'name' can be a list of methods in the form of 'method1|method2|...'. In this case, the function returns true if the SIP message's method is one from the list. IMPORTANT NOTE: in the list must be only methods defined in OpenSIPS with ID (invite, cancel, ack, bye, options, info, update, register, message, subscribe, notify, refer, prack, publish; for more see: http://www.iana.org/assignments/sip-parameters). If used for replies, the function tests the value of method field from CSeq header. Meaning of the parameters is as follows: * name - SIP method name This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, and BRANCH_ROUTE. Example 1.10. is_method usage ... if(is_method("INVITE")) { # process INVITEs here } if(is_method("OPTION|UPDATE")) { # process OPTIONs and UPDATEs here } ... 1.3.11. remove_hf(hname [,flags]) Remove from message all headers with name “hname†Returns true if at least one header is found and removed. Meaning of the parameters is as follows: * hname - header name to be removed. The hname parameter can have the following types: + string - Header name to be removed + pvar - Header name to be removed is the value of an existing pseudo-variable (as string value) + pattern - pattern to match the names of the headers to be removed; can be a simple wildcard or a regexp (see the second param too) * flags - how to interpret the pattern from the first parameted. + r - pattern is a regexp + g - pattern is glob (shell wildcard pattern) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Example 1.11. remove_hf usage ... if(remove_hf("User-Agent")) { # User Agent header removed } ... # removes X-Billing-Account, X-Billing-Price, X-Billing-rateplan, etc remove_hf("X-Billing*", "g"); ... #removes headers by regex remove_hf("^X-g.+[0-9]", "r"); ... 1.3.12. has_body(), has_body(mime) The function returns true if the SIP message has a body attached. The checked includes also the “Content-Lenght†header presence and value. If a parameter is given, the mime described will be also checked against the “Content-Type†header. If the SIP message has a multipart body and mime is not a multipart type , it will search through all the parts for the given type. It will not handle multi-layer multiparts Meaning of the parameters is as follows: * mime - mime to be checked against the “Content-Type†header. If not present or 0, this check will be disabled. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Example 1.12. has_body usage ... if(has_body("application/sdp")) { # do interesting stuff here } ... 1.3.13. is_audio_on_hold() The function returns true if the SIP message has an SDP body attached and at least one audio stream in on hold. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Example 1.13. is_audio_on_hold usage ... if(is_audio_on_hold()) { # do interesting stuff here } ... 1.3.14. is_privacy(privacy_type) The function returns true if the SIP message has a Privacy header field that includes the given privacy_type among its privacy values. See http://www.iana.org/assignments/sip-priv-values for possible privacy type values. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Example 1.14. is_privacy usage ... if(is_privacy("id")) { # do interesting stuff here } ... 1.3.15. strip_body(), strip_body(mime) If the message has a body, this function deletes it, correcting Content-Length. This function also deletes the Content-Type header. If a MIME type is specified it will delete the body if it has the same type or, if the message has a multipart body it will go through the parts and delete all those with the specified type. It will not handle multi-layer multiparts Meaning of the parameters is as follows: * mime - mime to be checked against the “Content-Type†header. If not present or 0, this check will be disabled. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.15. strip_body usage ... strip_body(); #delete any body strip_body("multipart/related") # delete a multipart/related body strip_body("application/sdp") # delete all sdp bodies, even ones enclose d in multipart ... 1.3.16. add_body(body_text, new_content_type) This function can be used to add a body to a message. If a body already exists, it will be replaced with the new one. The second parameter is the content type of the new body. It is optional: in case the function is used to replace an existing body with a body of the same type, there is no need to set this parameter. If the message has no body or if the types are different, it is compulsory to set this parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.16. add_body usage ... add_body("Hello World!", "text/plain"); ... 1.3.17. sipmsg_validate([flags[,result_pvar]]) The function returns true if the SIP message is properly built according to SIP RFC3261. It verifies if the mandatory headers for each request/reply and can also check the format of the headers body. The flags parameter received is optional and can be composed with the following values: * 's' - checks the integrity of the SDP body, if it exists * 'h' - checks the format and integrity of each header body. * 'm' - don't check the Max-Forwards header. * 'r' - checks the R-URI and whether the domain contains valid characters. * 'f' - checks the URI of the 'From' field and whether the domain contains valid characters. * 't' - checks the URI of the 'To' field and whether the domain contains valid characters. * 'c' - checks the URI of the 'Contact' field. The result_pvar parameter sets resulting pvar with text error reason in case of negative result ( easy for logging or propagating the rejection reason back to the bogus UA ) This function can return the following codes: * 1 - the message is RFC3261 compliant and has been successfully validated. * -1 - No SIP message * -2 - Header Parsing error * -3 - No Call-ID header * -4 - No Content-Length header for transports that require it ( eg. TCP ) * -5 - Invalid Content-Length, other from the size of the actual body * -6 - SDP body parsing error. * -7 - No Cseq header. * -8 - No From header. * -9 - No To header. * -10 - No Via header. * -11 - Request URI parse error. * -12 - Bad hostname in R-URI. * -13 - No Max-Forward header. * -14 - No Contact header. * -15 - Path user for non-Register request. * -16 - No allow header in 405 reply. * -17 - No Min-Expire header in 423 reply. * -18 - No Proxy-Authorize header in 407 reply. * -19 - No Unsupported header in 420 reply. * -20 - No WWW-Authorize header in 401 reply. * -21 - No Content-Type header * -22 - To header parse error * -23 - From header parse error * -24 - Bad hostname in To header * -25 - Bad hostname in From header * -26 - Contact header parse error * -255 - undefined errors. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Example 1.17. sipmsg_validate usage ... if(!sipmsg_validate()) { send_reply("400", "Bad Request"); exit; } ... ... # checks also the SDP and headers body if(!sipmsg_validate("sh")) { send_reply("400", "Bad Request/Body"); exit; } ... 1.3.18. codec_exists (name [,clock] ) This function can be used to verify if a codec exists inside an sdp payload. It will search for the codec inside all streams from all sdp sessions. If it is found anywhere it will return TRUE otherwise it will return FALSE. The second parameter is optional, if it is not supplied any clockrate will match. Parameters are CASE INSENSITIVE. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.18. codec_exists usage ... codec_exists("speex"); or codec_exists("GSM","8000"); ... 1.3.19. codec_delete (name [,clock] ) This function can be used to delete a codec from inside an sdp payload. It will search for the codec inside all streams from all sdp sessions. If it is found anywhere it will be deleted from the mapping ("a=...") and from the list of indexes ("m=..."). Returns TRUE if any deletion occurred otherwise it will return FALSE. The second parameter is optional, if it is not supplied any clockrate will match and all will be deleted. Parameters are CASE INSENSITIVE. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.19. codec_delete usage ... codec_delete("speex"); or codec_delete("GSM","8000"); ... 1.3.20. codec_move_up (name [,clock] ) This function can be used to move a codec up in the list of indexes ("m=..."). It will search for the codec inside all streams from all sdp sessions. If it is found anywhere it will be moved to the top of the index list. Returns TRUE if any moves occurred otherwise it will return FALSE. The second parameter is optional, if it is not supplied any clockrate will match and all codecs will be moved to the front while preserving their original ordering. Parameters are CASE INSENSITIVE. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.20. codec_move_up usage ... codec_move_up("speex"); or codec_move_up("GSM","8000"); ... 1.3.21. codec_move_down (name [,clock] ) This function can be used to move a codec down in the list of indexes ("m=..."). It will search for the codec inside all streams from all sdp sessions. If it is found anywhere it will be moved to the back of the index list. Returns TRUE if any moves occurred otherwise it will return FALSE. The second parameter is optional, if it is not supplied any clockrate will match and all codecs will be moved to the back while preserving their original ordering. Parameters are CASE INSENSITIVE. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.21. codec_move_down usage ... codec_move_down("speex"); or codec_move_down("GSM","8000"); ... Example 1.22. codec_move_down usage ... /* This example will move speex with 8000 codec to the back of the list, then it will erase GSM with 8000 clock, and then it will bring all speex codecs to the front of the list. Speex/8000 will be behind any other speex. */ codec_move_down("speex","8000"); codec_delete("GSM","8000"); codec_move_up("speex"); ... 1.3.22. codec_exists_re ( regexp ) This function has the same effect as codec_exists ( without the clock parameter ) the only difference is that it takes a POSIX regular expression as a parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.23. codec_exists_re usage ... codec_exists_re("sp[a-z]*"); ... 1.3.23. codec_delete_re ( regexp ) This function has the same effect as codec_delete ( without the clock parameter ) the only difference is that it takes a POSIX regular expression as a parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.24. codec_delete_re usage ... codec_delete_re("PCMA|PCMU"); ... 1.3.24. codec_delete_except_re ( regexp ) This function deletes all the codecs except those specified by the regular expression. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.25. codec_delete_except_re usage ... codec_delete_except_re("PCMA|PCMU");#will delete all codecs except PCMA and PCMU ... 1.3.25. codec_move_up_re ( regexp ) This function has the same effect as codec_move_up ( without the clock parameter ) the only difference is that it takes a POSIX regular expression as a parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.26. codec_move_up_re usage ... codec_move_up_re("sp[a-z]*"); ... 1.3.26. codec_move_down_re ( regexp ) This function has the same effect as codec_move_down ( without the clock parameter ) the only difference is that it takes a POSIX regular expression as a parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.27. codec_move_down_re usage ... codec_move_down_re("sp[a-z]*"); ... Example 1.28. codec_move_down usage ... /* This example will move speex with 8000 codec to the back of the list, then it will erase GSM with 8000 clock, and then it will bring all speex codecs to the front of the list. Speex/8000 will be behind any other speex. */ codec_move_down("speex","8000"); codec_delete("GSM","8000"); codec_move_up("speex"); ... 1.3.27. change_reply_status(code, reason) Intercept a SIP reply (in any onreply_route) and change its status code and reason phrase prior to propogating it. Meaning of the parameters is as follows: * code - Status code. * reason - Reason phrase. This function can be used from ONREPLY_ROUTE. Example 1.29. change_reply_status usage ... onreply_route { if ($rs == "603") { change_reply_status("404", "Not Found"); exit; } } ... 1.3.28. stream_exists(regexp) This function can be used to verify if a stream exists inside an sdp payload. It will search for the stream inside all sdp sessions. If it is found anywhere it will return TRUE otherwise it will return FALSE. Meaning of the parameters is as follows: * regexp - a POSIX regular expression to match the stream media name. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.30. stream_exists usage ... # check for FAX stream_exists("image"); ... 1.3.29. stream_delete(regexp) This function can be used to delete a whole stream from inside an sdp payload. It will search for the stream inside all sdp sessions. If it is found anywhere it will be deleted along with all attributes Returns TRUE if any deletion occurred otherwise it will return FALSE. Meaning of the parameters is as follows: * regexp - a POSIX regular expression to match the stream media name. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. Example 1.31. stream_delete usage ... # prevent usage of video stream_delete("video"); ... 1.4. Known Limitations Search functions are applied to the current message so modifications made to the sdp will be visible to the codec_exists functions( e.g. after calling codec_delete("speex") , codec_exists("speex") will return false ). opensips-2.2.2/modules/sipmsgops/codecs.c000066400000000000000000000543711300170765700204750ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Andrei Dragus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2009-07-23 first version (andreidragus) */ #include "../../sr_module.h" #include "../../parser/msg_parser.h" #include "../../mem/mem.h" #include "../../data_lump.h" #include "../../parser/sdp/sdp.h" #include "codecs.h" #include "../../route.h" #include "../../mod_fix.h" #define MAX_STREAMS 64 static struct lump *lumps[MAX_STREAMS]; static int lumps_len; enum{ FIND, DELETE, ADD_TO_FRONT, ADD_TO_BACK }; enum{ DESC_NAME, DESC_NAME_AND_CLOCK, DESC_REGEXP, DESC_REGEXP_COMPLEMENT }; static int do_for_all_streams(struct sip_msg* msg, str* str1,str * str2, regex_t* re, int op,int desc); static int stream_process(struct sip_msg * msg, struct sdp_stream_cell *cell, str * s, str* ss, regex_t* re, int op,int description); int fixup_codec(void** param, int param_no) { return fixup_sgp_sgp(param,param_no); } int fixup_codec_regexp(void** param, int param_no) { return fixup_regexp_dynamic_null(param, param_no); } /* * Create the necessary lumps from the message */ static int create_codec_lumps(struct sip_msg * msg) { struct sdp_session_cell * cur_session; struct lump * tmp; int count; /* get the number of streams */ lumps_len = 0; cur_session = msg->sdp->sessions; while(cur_session) { lumps_len += cur_session->streams_num; cur_session = cur_session->next; } if (lumps_len>MAX_STREAMS) { LM_ERR("Overflow - too many streams (%d), limit is %d\n", lumps_len, MAX_STREAMS); return -1; } memset(lumps, 0, MAX_STREAMS * sizeof(struct lump*)); /* for each stream create a specific lump for deletion, skip * and insertion */ LM_DBG("creating %d streams\n",lumps_len); count = 0; cur_session = msg->sdp->sessions; while(cur_session) { struct sdp_stream_cell * cur_cell = cur_session->streams; struct lump* l; str text; while(cur_cell) { l = del_lump(msg, cur_cell->payloads.s - msg->buf, cur_cell->payloads.len,0); lumps[count] = l; if( l == NULL) { LM_ERR("Error adding delete lump for m=\n"); return -1; } l->flags |= LUMPFLAG_CODEC; tmp = insert_skip_lump_after( l ); if(tmp == NULL) { LM_ERR("Error adding skip lump for m=\n"); return -1; } text.len = cur_cell->payloads.len; text.s = (char*)pkg_malloc(cur_cell->payloads.len); if( text.s == NULL ) { LM_ERR("Error alocating lump buffer\n"); return -1; } memcpy(text.s,cur_cell->payloads.s,cur_cell->payloads.len); tmp = insert_new_lump_after( tmp, text.s, text.len, 0); if(tmp == NULL) { LM_ERR("Error adding insert lump for m=\n"); return -1; } count ++; cur_cell = cur_cell->next; } cur_session = cur_session->next; } return 0; }; /* * Find the flagged lumps and save them in the global lump array * Returns : 0 - codec lumps found * 1 - no lump found * -1 - error */ static int find_codec_lumps(struct sip_msg * msg) { struct lump *cur = msg->body_lumps; int count = 0; while( cur) { if( cur->flags & LUMPFLAG_CODEC && cur->after && cur->after->after) count++; cur = cur->next; } if (count>MAX_STREAMS) { LM_CRIT("BUG: too many codec lumps found (%d)\n",count); return -1; } if( count==0 ) { lumps_len = -1; return 1; } lumps_len=0; cur = msg->body_lumps; while( cur) { if( cur->flags & LUMPFLAG_CODEC && cur->after && cur->after->after) { lumps[lumps_len] = cur; lumps_len++; } cur = cur->next; } LM_DBG("found %d streams\n",lumps_len); return 0; }; static int clone_codec_lumps(void) { struct lump *l; int i; char *s; LM_DBG("cloning %d streams\n",lumps_len); for( i=0 ; iafter ; l=l->after ); s = pkg_malloc( l->len+1 ); if (s==NULL) { LM_ERR("failed to alloc new lump pkg buffer\n"); return -1; } memcpy( s, l->u.value, l->len); if (insert_new_lump_after( l, s, l->len, 0)==NULL) { LM_ERR("failed to create new lump\n"); return -1; } } return 0; } static int get_codec_lumps( struct sip_msg *msg ) { int rc; rc = find_codec_lumps(msg); if (rc<0) { LM_ERR("error while searching for codec flags\n"); return -1; } /* codec lumps not yet created -> create them now */ if (rc==1) { if( create_codec_lumps(msg)<0 ) { LM_ERR("failed to create codec lumps\n"); return -1; } /* success - we gave the lumps */ return 0; } /* seams the lumps are already created */ if( route_type & (REQUEST_ROUTE | ONREPLY_ROUTE | LOCAL_ROUTE) ) { /* save to use them directly */ return 0; } if( route_type & (FAILURE_ROUTE | BRANCH_ROUTE) ) { /* clone the inserted lumps */ if ( clone_codec_lumps()<0 ) { LM_ERR("failed to clone codec lumps\n"); return -1; } return 0; } /* shoudn't get here */ return -1; }; /* * Associate a lump with a given cell */ static struct lump * get_associated_lump(struct sip_msg * msg, struct sdp_stream_cell * cell) { struct lump *lmp; int i; LM_DBG("Have %d lumps\n",lumps_len); for( i =0 ; i< lumps_len; i++) { int have = lumps[i]->u.offset; int want = cell->payloads.s - msg->buf; LM_DBG("have lump at %d want at %d\n", have, want ); if( have == want ) { /* got root lump, return the last data one */ for( lmp=lumps[i] ; lmp->after ; lmp=lmp->after); return lmp; } } return NULL; }; static int do_for_all_streams(struct sip_msg* msg, str* str1,str * str2, regex_t* re, int op,int desc) { struct sdp_session_cell * cur_session; int rez; if (msg==NULL || msg==FAKED_REPLY) return -1; if(parse_sdp(msg)) { LM_DBG("Message has no SDP\n"); return -1; } if (get_codec_lumps(msg)<0) { LM_ERR("failed to prepare changes for codecs\n"); return -1; } cur_session = msg->sdp->sessions; rez = -1; while(cur_session) { struct sdp_stream_cell * cur_cell = cur_session->streams; while(cur_cell) { if(stream_process(msg,cur_cell,str1,str2,re,op,desc)==1) rez = 1; cur_cell = cur_cell->next; } cur_session = cur_session->next; } return rez; } int delete_sdp_line( struct sip_msg * msg, char * s) { char * start,*end; if( !s ) return 1; start = s; end = s; while(*start != '\n') start--; start++; while(*end != '\n') end++; end++; /* delete the entry */ if( del_lump(msg, start - msg->buf, end - start,0) == NULL ) { return -1; } return 0; } /* method that processes a stream and keeps the original order * of codecs with the same name */ static int stream_process(struct sip_msg * msg, struct sdp_stream_cell *cell, str * s, str* ss, regex_t* re, int op,int description) { static sdp_payload_attr_t static_payloads[] = { /* as per http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml */ { NULL,0,{ "0",1},{"PCMU",4},{ "8000",4},{NULL,0},{NULL,0} }, /* 0 - PCMU/8000 */ { NULL,0,{ "3",1},{ "GSM",3},{ "8000",4},{NULL,0},{NULL,0} }, /* 3 - GSM/8000 */ { NULL,0,{ "4",1},{"G723",4},{ "8000",4},{NULL,0},{NULL,0} }, /* 4 - G723/8000 */ { NULL,0,{ "5",1},{"DVI4",4},{ "8000",4},{NULL,0},{NULL,0} }, /* 5 - DVI4/8000 */ { NULL,0,{ "6",1},{"DVI4",4},{"16000",5},{NULL,0},{NULL,0} }, /* 6 - DVI4/16000 */ { NULL,0,{ "7",1},{ "LPC",3},{ "8000",4},{NULL,0},{NULL,0} }, /* 7 - LPC/8000 */ { NULL,0,{ "8",1},{"PCMA",4},{ "8000",4},{NULL,0},{NULL,0} }, /* 8 - PCMA/8000 */ { NULL,0,{ "9",1},{"G722",4},{ "8000",4},{NULL,0},{NULL,0} }, /* 9 - G722/8000 */ { NULL,0,{"10",2},{ "L16",3},{"44100",5},{NULL,0},{NULL,0} }, /*10 - L16/44100 */ { NULL,0,{"11",2},{ "L16",3},{"44100",5},{NULL,0},{NULL,0} }, /*11 - L16/44100 */ { NULL,0,{"12",2},{"QCELP",5},{"8000",4},{NULL,0},{NULL,0} }, /*12 -QCELP/8000 */ { NULL,0,{"13",2},{ "CN",2},{ "8000",4},{NULL,0},{NULL,0} }, /*13 - CN/8000 */ { NULL,0,{"14",2},{ "MPA",3},{"90000",5},{NULL,0},{NULL,0} }, /*14 - MPA/90000 */ { NULL,0,{"15",2},{"G728",4},{ "8000",4},{NULL,0},{NULL,0} }, /*15 - G728/8000 */ { NULL,0,{"16",2},{"DVI4",4},{"11025",5},{NULL,0},{NULL,0} }, /*16 - DVI4/11025 */ { NULL,0,{"17",2},{"DVI4",4},{"22050",5},{NULL,0},{NULL,0} }, /*17 - DVI4/22050 */ { NULL,0,{"18",2},{"G729",4},{ "8000",4},{NULL,0},{NULL,0} }, /*18 - G729/8000 */ { NULL,0,{"25",2},{"CelB",4},{ "8000",4},{NULL,0},{NULL,0} }, /*25 - CelB/8000 */ { NULL,0,{"26",2},{"JPEG",4},{"90000",5},{NULL,0},{NULL,0} }, /*26 - JPEG/90000 */ { NULL,0,{"28",2},{ "nv",2},{"90000",5},{NULL,0},{NULL,0} }, /*28 - nv/90000 */ { NULL,0,{"31",2},{"H261",4},{"90000",5},{NULL,0},{NULL,0} }, /*31 - H261/90000 */ { NULL,0,{"32",2},{ "MPV",3},{"90000",5},{NULL,0},{NULL,0} }, /*32 - MPV/90000 */ { NULL,0,{"33",2},{"MP2T",4},{"90000",5},{NULL,0},{NULL,0} }, /*33 - MP2T/90000 */ { NULL,0,{"34",2},{"H263",4},{"90000",5},{NULL,0},{NULL,0} }, /*34 - H263/90000 */ { NULL,0,{"t38",3},{"t38",3},{ "",0},{NULL,0},{NULL,0} }, /*T38- fax */ { NULL,0,{NULL,0},{ NULL,0},{ NULL,0},{NULL,0},{NULL,0} } }; sdp_payload_attr_t *payload; char *cur, *tmp, *buff, temp; struct lump * lmp; str found; int ret, i, depl, single, match, buff_len, is_static; regmatch_t pmatch; lmp = get_associated_lump(msg, cell); if( lmp == NULL) { LM_ERR("There is no lump for this sdp cell\n"); return -1; } /* is stream deleted ?? */ if (lmp->len == 0) return -1; buff_len = 0; ret = 0; buff = pkg_malloc(lmp->len+1); if( buff == NULL) { LM_ERR("Out of memory\n"); return -1; } /* search through each payload */ is_static = 0; payload = cell->payload_attr; while(payload) { if( payload->rtp_enc.s == NULL || (payload->rtp_clock.s == NULL && ss != NULL) || payload->rtp_payload.s == NULL) { goto next_payload; } match = 0; if( description == DESC_REGEXP ||description == DESC_REGEXP_COMPLEMENT ) { /* try to match a regexp */ if (is_static) { match = regexec( re, payload->rtp_enc.s, 1, &pmatch, 0) == 0; } else { temp = payload->rtp_enc.s[payload->rtp_enc.len]; payload->rtp_enc.s[payload->rtp_enc.len] = 0; match = regexec( re, payload->rtp_enc.s, 1, &pmatch, 0) == 0; payload->rtp_enc.s[payload->rtp_enc.len] = temp; } } if( description == DESC_REGEXP_COMPLEMENT) match = !match; if( description == DESC_NAME ) { match = s->len == payload->rtp_enc.len && strncasecmp( s->s, payload->rtp_enc.s , payload->rtp_enc.len) == 0; } if( description == DESC_NAME_AND_CLOCK) { /* try to match name and clock if there is one */ match = s->len == payload->rtp_enc.len && strncasecmp( s->s, payload->rtp_enc.s , payload->rtp_enc.len) == 0 && (ss == NULL || ( ss->len == payload->rtp_clock.len && strncasecmp( ss->s, payload->rtp_clock.s , payload->rtp_clock.len) == 0 ) ); } /* if found, search its index in the m= line */ if (match) { match = 0; cur = lmp->u.value; while( !match && cur < lmp->u.value + lmp->len) { /* find the end of the number */ found.s = cur; while( cur < lmp->u.value + lmp->len && *cur != ' ' ) cur++; found.len = cur - found.s; /* does it matches payload number */ if ( found.len == payload->rtp_payload.len && strncmp( found.s,payload->rtp_payload.s,found.len) == 0) { match = 1; } else { /* continue on searching => skip spaces if there still are any */ while( cur < lmp->u.value + lmp->len && * cur == ' ' ) cur++; } } /* have we found both payload and index */ if (match) { if(op == FIND) { ret = 1; goto end; } if( op == DELETE && !is_static ) { /* find the full 'a=...' entry */ if( delete_sdp_line( msg, payload->rtp_enc.s) < 0 ) { LM_ERR("Unable to add delete lump for a=\n"); ret = -1; goto end; } if( delete_sdp_line( msg, payload->fmtp_string.s) < 0 ) { LM_ERR("Unable to add delete lump for a=\n"); ret = -1; goto end; } } { /* take the following whitespaces as well */ while( cur < lmp->u.value + lmp->len && *cur == ' ' ) { cur++; found.len++; } /* when trimming the very last payload, avoid trailing ws */ if (cur == lmp->u.value + lmp->len) { tmp = found.s; while (*(--tmp) == ' ') { found.s--; found.len++; } } /* delete the string and update iterators */ for(tmp=found.s ; tmp< lmp->u.value + lmp->len ; tmp++ ) *tmp = *(tmp+found.len); cur -= found.len; lmp->len -= found.len; } /* add the deleted number into a buffer to be addded later */ if( op == ADD_TO_FRONT || op == ADD_TO_BACK) { if( buff_len > 0) { memcpy(&buff[buff_len]," ",1); buff_len++; } memcpy(&buff[buff_len],payload->rtp_payload.s, payload->rtp_payload.len); buff_len += payload->rtp_payload.len; } ret = 1; } } /* next payload */ next_payload: if (!is_static) { payload = payload->next; if (payload==NULL) { payload = static_payloads; is_static = 1; } } else { payload ++; if (payload->rtp_payload.s==NULL) payload=NULL; } } if( op == ADD_TO_FRONT && buff_len >0 ) { depl = buff_len; single = 1; if( lmp->len > 0) { depl++; single = 0; } lmp->u.value = (char*)pkg_realloc(lmp->u.value, lmp->len+depl); if(!lmp->u.value) { LM_ERR("No more pkg memory\n"); ret = -1; goto end; } for( i = lmp->len -1 ; i>=0;i--) lmp->u.value[i+depl] = lmp->u.value[i]; memcpy(lmp->u.value,buff,buff_len); if(!single) lmp->u.value[buff_len] = ' '; lmp->len += depl; } if( op == ADD_TO_BACK && buff_len >0 ) { lmp->u.value = (char*)pkg_realloc(lmp->u.value, lmp->len+buff_len+1); if(!lmp->u.value) { LM_ERR("No more pkg memory\n"); ret = -1; goto end; } if( lmp->len > 0) { memcpy(&lmp->u.value[lmp->len]," ",1); lmp->len++; } memcpy(&lmp->u.value[lmp->len],buff,buff_len); lmp->len += buff_len; } end: pkg_free(buff); return ret; } int codec_find (struct sip_msg* msg, char* str1 ) { str res = {0,0}; if(fixup_get_svalue(msg, (gparam_p)str1, &res)!=0) { LM_ERR("no mode value\n"); return -1; } LM_DBG("searching for codec <%.*s> \n",res.len,res.s); return do_for_all_streams( msg, &res, NULL, NULL, FIND, DESC_NAME); } int codec_find_re (struct sip_msg* msg, char* str1 ) { regex_t *re; int do_free; int ret; re = fixup_get_regex(msg,(gparam_p)str1,&do_free); if (!re) { LM_ERR("Failed to get regular expression \n"); return -1; } ret = do_for_all_streams(msg, NULL, NULL, re, FIND, DESC_REGEXP); if (do_free) fixup_free_regexp((void **)&re); return ret; } int codec_find_clock (struct sip_msg* msg, char* str1,char * str2 ) { str codec = {0,0},clock = {0,0}; if(fixup_get_svalue(msg, (gparam_p)str1, &codec)!=0) { LM_ERR("no mode value\n"); return -1; } if(fixup_get_svalue(msg, (gparam_p)str2, &clock)!=0) { LM_ERR("no mode value\n"); return -1; } LM_DBG("searching for codec <%.*s> with clock <%.*s> \n", codec.len,codec.s,clock.len,clock.s); return do_for_all_streams( msg, &codec, &clock, NULL, FIND, DESC_NAME_AND_CLOCK); } int codec_delete (struct sip_msg* msg, char* str1 ) { str res = {0,0}; if(fixup_get_svalue(msg, (gparam_p)str1, &res)!=0) { LM_ERR("no mode value\n"); return -1; } LM_DBG("deleting codec <%.*s> \n",res.len,res.s); return do_for_all_streams( msg, &res, NULL, NULL, DELETE, DESC_NAME); } int codec_delete_re (struct sip_msg* msg, char* str1 ) { regex_t *re; int do_free; int ret; re = fixup_get_regex(msg,(gparam_p)str1,&do_free); if (!re) { LM_ERR("Failed to get regular expression \n"); return -1; } ret = do_for_all_streams( msg, NULL, NULL, re, DELETE, DESC_REGEXP); if (do_free) fixup_free_regexp((void **)&re); return ret; } int codec_delete_except_re (struct sip_msg* msg, char* str1 ) { regex_t *re; int do_free; int ret; re = fixup_get_regex(msg,(gparam_p)str1,&do_free); if (!re) { LM_ERR("Failed to get regular expression \n"); return -1; } ret = do_for_all_streams( msg, NULL, NULL, re, DELETE, DESC_REGEXP_COMPLEMENT); if (do_free) fixup_free_regexp((void **)&re); return ret; } int codec_delete_clock (struct sip_msg* msg, char* str1 ,char * str2) { str codec = {0,0},clock = {0,0}; if(fixup_get_svalue(msg, (gparam_p)str1, &codec)!=0) { LM_ERR("no mode value\n"); return -1; } if(fixup_get_svalue(msg, (gparam_p)str2, &clock)!=0) { LM_ERR("no mode value\n"); return -1; } LM_DBG("deleting codec <%.*s> with clock <%.*s> \n", codec.len,codec.s,clock.len,clock.s); return do_for_all_streams( msg, &codec, &clock, NULL, DELETE, DESC_NAME_AND_CLOCK); } int codec_move_up (struct sip_msg* msg, char* str1) { str res = {0,0}; if(fixup_get_svalue(msg, (gparam_p)str1, &res)!=0) { LM_ERR("no mode value\n"); return -1; } LM_DBG("moving up codec <%.*s> \n",res.len,res.s); return do_for_all_streams( msg, &res, NULL, NULL, ADD_TO_FRONT, DESC_NAME); } int codec_move_up_re (struct sip_msg* msg, char* str1) { regex_t *re; int do_free; int ret; re = fixup_get_regex(msg,(gparam_p)str1,&do_free); if (!re) { LM_ERR("Failed to get regular expression \n"); return -1; } ret = do_for_all_streams( msg, NULL, NULL, re, ADD_TO_FRONT, DESC_REGEXP); if (do_free) fixup_free_regexp((void **)&re); return ret; } int codec_move_up_clock (struct sip_msg* msg, char* str1 ,char * str2) { str codec = {0,0},clock = {0,0}; if(fixup_get_svalue(msg, (gparam_p)str1, &codec)!=0) { LM_ERR("no mode value\n"); return -1; } if(fixup_get_svalue(msg, (gparam_p)str2, &clock)!=0) { LM_ERR("no mode value\n"); return -1; } LM_DBG("moving up codec <%.*s> with clock <%.*s> \n", codec.len,codec.s,clock.len,clock.s); return do_for_all_streams( msg, &codec, &clock, NULL, ADD_TO_FRONT, DESC_NAME_AND_CLOCK); } int codec_move_down (struct sip_msg* msg, char* str1) { str res = {0,0}; if(fixup_get_svalue(msg, (gparam_p)str1, &res)!=0) { LM_ERR("no mode value\n"); return -1; } LM_DBG("moving down codec <%.*s> \n",res.len,res.s); return do_for_all_streams( msg, &res, NULL, NULL, ADD_TO_BACK, DESC_NAME); } int codec_move_down_re (struct sip_msg* msg, char* str1) { regex_t *re; int do_free; int ret; re = fixup_get_regex(msg,(gparam_p)str1,&do_free); if (!re) { LM_ERR("Failed to get regular expression \n"); return -1; } ret = do_for_all_streams( msg, NULL, NULL, re, ADD_TO_BACK, DESC_REGEXP); if (do_free) fixup_free_regexp((void **)&re); return ret; } int codec_move_down_clock (struct sip_msg* msg, char* str1 ,char * str2) { str codec = {0,0},clock = {0,0}; if(fixup_get_svalue(msg, (gparam_p)str1, &codec)!=0) { LM_ERR("no mode value\n"); return -1; } if(fixup_get_svalue(msg, (gparam_p)str2, &clock)!=0) { LM_ERR("no mode value\n"); return -1; } LM_DBG("moving down codec <%.*s> with clock <%.*s> \n", codec.len,codec.s,clock.len,clock.s); return do_for_all_streams( msg, &codec, &clock, NULL, ADD_TO_BACK, DESC_NAME_AND_CLOCK); } static int handle_streams(struct sip_msg* msg, regex_t* re, int delete) { struct sdp_session_cell *session; struct sdp_stream_cell *stream; struct sdp_stream_cell *prev_stream; regmatch_t pmatch; struct lump *lmp, *l; char *begin, *end; char temp; str body; int match; if (msg==NULL || msg==FAKED_REPLY) return -1; if(parse_sdp(msg)) { LM_DBG("Message has no SDP\n"); return -1; } /* search for the stream */ match = 0; for(session=msg->sdp->sessions; session && !match ;session=session->next){ prev_stream = NULL; for( stream=session->streams ; stream ; prev_stream=stream,stream=stream->next){ /* check the media in stream, re based */ temp = stream->media.s[stream->media.len]; stream->media.s[stream->media.len] = 0; match = regexec( re, stream->media.s, 1, &pmatch, 0) == 0; stream->media.s[stream->media.len] = temp; if (match) break; } } if (!match) return -1; LM_DBG(" found stream [%.*s]\n",stream->media.len,stream->media.s); /* stream found */ if (!delete) return 1; /* have to delete the stream*/ if (get_codec_lumps(msg)<0) { LM_ERR("failed to get lumps for streams\n"); return -1; } lmp = get_associated_lump(msg, stream); if( lmp == NULL) { LM_ERR("There is no lump for this sdp cell\n"); return -1; } /* is stream deleted ?? */ if (lmp->len == 0) return -1; /* search the boundries of the stream */ /* look for the beginning of the "m" line */ begin = stream->media.s ; while( *(begin-1)!='\n' && *(begin-1)!='\r') begin--; /* the end is where the next stream starts */ if (prev_stream) { /* there is a stream after */ end = prev_stream->media.s ; while( *(end-1)!='\n' && *(end-1)!='\r') end--; } else { /* last stream */ body.s = NULL; body.len = 0; get_body(msg, &body); end = body.s + body.len; } //LM_DBG(" full stream is [%.*s]\n",end-begin, begin); l = del_lump( msg, (unsigned int)(begin-msg->buf), (unsigned int)(end-begin), 0); if (l==NULL) { LM_ERR("failed to create delete lump\n"); return -1; } /* mark stream as deleted */ lmp->len = 0; return 1; } int stream_find (struct sip_msg* msg, char* str1 ) { regex_t *re; int do_free,ret; re = fixup_get_regex(msg,(gparam_p)str1,&do_free); if (!re) { LM_ERR("Failed to get regular expression \n"); return -1; } ret = handle_streams(msg, re, 0); if (do_free) fixup_free_regexp((void **)&re); return ret; } int stream_delete (struct sip_msg* msg, char* str1 ) { regex_t *re; int do_free,ret; re = fixup_get_regex(msg,(gparam_p)str1,&do_free); if (!re) { LM_ERR("Failed to get regular expression \n"); return -1; } ret = handle_streams(msg, re, 1); if (do_free) fixup_free_regexp((void **)&re); return ret; } opensips-2.2.2/modules/sipmsgops/codecs.h000066400000000000000000000037221300170765700204740ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Andrei Dragus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2009-07-23 first version (andreidragus) */ #ifndef _CODECS_H #define _CODECS_H int codec_init(); int fixup_codec(void** param, int param_no); int fixup_codec_regexp(void** param, int param_no); int codec_find (struct sip_msg* msg, char* str1 ); int codec_delete (struct sip_msg* msg, char* str1 ); int codec_move_up (struct sip_msg* msg, char* str1 ); int codec_move_down (struct sip_msg* msg, char* str1 ); int codec_find_re (struct sip_msg* msg, char* str1); int codec_delete_re (struct sip_msg* msg, char* str1); int codec_delete_except_re (struct sip_msg* msg, char* str1); int codec_move_up_re (struct sip_msg* msg, char* str1 ); int codec_move_down_re (struct sip_msg* msg, char* str1 ); int codec_find_clock (struct sip_msg* msg, char* str1,char * str2 ); int codec_delete_clock (struct sip_msg* msg, char* str1,char * str2 ); int codec_move_up_clock (struct sip_msg* msg, char* str1,char * str2 ); int codec_move_down_clock (struct sip_msg* msg, char* str1,char * str2 ); int stream_find (struct sip_msg* msg, char* re ); int stream_delete (struct sip_msg* msg, char* re ); #endif /* _CODECS_H */ opensips-2.2.2/modules/sipmsgops/doc/000077500000000000000000000000001300170765700176245ustar00rootroot00000000000000opensips-2.2.2/modules/sipmsgops/doc/sipmsgops.xml000066400000000000000000000027341300170765700224000ustar00rootroot00000000000000 %docentities; ]> sipmsgops Module &osipsname; Andrei Pelinescu-Onciul &fhg; pelinescu-onciul@fokus.fraunhofer.de Andrei Pelinescu-Onciul pelinescu-onciul@fokus.fraunhofer.de Daniel-Constantin Mierla miconda@gmail.com Ovidiu Sas osas@voipembedded.com Razvan Crainea razvancrainea@opensips.org 2003 &fhg; $Revision: 7480 $ $Date$ &admin; &faq; opensips-2.2.2/modules/sipmsgops/doc/sipmsgops_admin.xml000066400000000000000000001035701300170765700235500ustar00rootroot00000000000000 &adminguide;
Overview The module implements SIP based operations over the messages processed by OpenSIPS. SIP is a text based protocol and the module provides a large set of very useful functions to manipulate the message at SIP level, e.g., inserting new headers or deleting them, check for method type, etc.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Functions
<function moreinfo="none">filter_body(content_type)</function> Filters multipart body by leaving out all other body parts except the first body part of given type. Meaning of the parameters is as follows: content_type - Content type to be left in the body. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>filter_body</function> usage ... if (has_body("multipart/mixed")) { if (filter_body("application/sdp") { remove_hf("Content-Type"); append_hf("Content-Type: application/sdp\r\n"); } else { xlog("Body part application/sdp not found\n"); } } ...
<function moreinfo="none">append_to_reply(txt)</function> Append txt as header to all replies that will be generated by OpenSIPS for this request. Meaning of the parameters is as follows: txt - String which may contains pseudo-variables. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ERROR_ROUTE. <function>append_to_reply</function> usage ... append_to_reply("Foo: bar\r\n"); append_to_reply("Foo: $rm at $Ts\r\n"); ...
<function moreinfo="none">append_hf(txt)</function> Appends 'txt' as header after the last header field. Meaning of the parameters is as follows: txt - Header field to be appended. The value can contain pseudo-variables which will be replaced at run time. Note: Headers which are added in main route cannot be removed in further routes (e.g. failure routes). So, the idea is not to add there any headers that you might want to remove later. To add headers temporarely use the branch route because the changes you do there are per-branch. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>append_hf</function> usage ... append_hf("P-hint: VOICEMAIL\r\n"); append_hf("From-username: $fU\r\n"); ...
<function moreinfo="none">append_hf(txt, hdr)</function> Appends 'txt' as header after first 'hdr' header field. Meaning of the parameters is as follows: txt - Header field to be appended. The value can contain pseudo-variables which will be replaced at run time. hdr - Header name after which the 'txt' is appended. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>append_hf</function> usage ... append_hf("P-hint: VOICEMAIL\r\n", "Call-ID"); append_hf("From-username: $fU\r\n", "Call-ID"); ...
<function moreinfo="none">insert_hf(txt)</function> Inserts 'txt' as header before the first header field. Meaning of the parameters is as follows: txt - Header field to be inserted. The value can contain pseudo-variables which will be replaced at run time. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>insert_hf</function> usage ... insert_hf("P-hint: VOICEMAIL\r\n"); insert_hf("To-username: $tU\r\n"); ...
<function moreinfo="none">insert_hf(txt, hdr)</function> Inserts 'txt' as header before first 'hdr' header field. Meaning of the parameters is as follows: txt - Header field to be inserted. The value can contain pseudo-variables which will be replaced at run time. hdr - Header name before which the 'txt' is inserted. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>insert_hf</function> usage ... insert_hf("P-hint: VOICEMAIL\r\n", "Call-ID"); insert_hf("To-username: $tU\r\n", "Call-ID"); ...
<function moreinfo="none">append_urihf(prefix, suffix)</function> Append header field name with original Request-URI in middle. Meaning of the parameters is as follows: prefix - string (usually at least header field name). suffix - string (usually at least line terminator). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>append_urihf</function> usage ... append_urihf("CC-Diversion: ", "\r\n"); ...
<function moreinfo="none">is_present_hf(hf_name)</function> Return true if a header field is present in message. The function is also able to distinguish the compact names. For exmaple From will match with f Meaning of the parameters is as follows: hf_name - Header field name.(long or compact form). The hf_name parameter can have the following types: string - Static header field name pvar - Header field name is given as a pseudo-variable (as string value) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>is_present_hf</function> usage ... if (is_present_hf("From")) log(1, "From HF Present"); ...
<function moreinfo="none">append_time()</function> Adds a time header to the reply of the request. You must use it before functions that are likely to send a reply, e.g., save() from 'registrar' module. Header format is: Date: %a, %d %b %Y %H:%M:%S GMT, with the legend: %a abbreviated week of day name (locale) %d day of month as decimal number %b abbreviated month name (locale) %Y year with century %H hour %M minutes %S seconds Return true if a header was successfully appended. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>append_time</function> usage ... append_time(); ...
<function moreinfo="none">is_method(name)</function> Check if the method of the message matches the name. If name is a known method (invite, cancel, ack, bye, options, info, update, register, message, subscribe, notify, refer, prack), the function performs method ID testing (integer comparison) instead of ignore case string comparison. The 'name' can be a list of methods in the form of 'method1|method2|...'. In this case, the function returns true if the SIP message's method is one from the list. IMPORTANT NOTE: in the list must be only methods defined in &osips; with ID (invite, cancel, ack, bye, options, info, update, register, message, subscribe, notify, refer, prack, publish; for more see: http://www.iana.org/assignments/sip-parameters). If used for replies, the function tests the value of method field from CSeq header. Meaning of the parameters is as follows: name - SIP method name This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, and BRANCH_ROUTE. <function>is_method</function> usage ... if(is_method("INVITE")) { # process INVITEs here } if(is_method("OPTION|UPDATE")) { # process OPTIONs and UPDATEs here } ...
<function moreinfo="none">remove_hf(hname [,flags])</function> Remove from message all headers with name hname Returns true if at least one header is found and removed. Meaning of the parameters is as follows: hname - header name to be removed. The hname parameter can have the following types: string - Header name to be removed pvar - Header name to be removed is the value of an existing pseudo-variable (as string value) pattern - pattern to match the names of the headers to be removed; can be a simple wildcard or a regexp (see the second param too) flags - how to interpret the pattern from the first parameted. r - pattern is a regexp g - pattern is glob (shell wildcard pattern) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. <function>remove_hf</function> usage ... if(remove_hf("User-Agent")) { # User Agent header removed } ... # removes X-Billing-Account, X-Billing-Price, X-Billing-rateplan, etc remove_hf("X-Billing*", "g"); ... #removes headers by regex remove_hf("^X-g.+[0-9]", "r"); ...
<function moreinfo="none">has_body()</function>, <function moreinfo="none">has_body(mime)</function> The function returns true if the SIP message has a body attached. The checked includes also the Content-Lenght header presence and value. If a parameter is given, the mime described will be also checked against the Content-Type header. If the SIP message has a multipart body and mime is not a multipart type , it will search through all the parts for the given type. It will not handle multi-layer multiparts Meaning of the parameters is as follows: mime - mime to be checked against the Content-Type header. If not present or 0, this check will be disabled. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. <function>has_body</function> usage ... if(has_body("application/sdp")) { # do interesting stuff here } ...
<function moreinfo="none">is_audio_on_hold()</function> The function returns true if the SIP message has an SDP body attached and at least one audio stream in on hold. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. <function>is_audio_on_hold</function> usage ... if(is_audio_on_hold()) { # do interesting stuff here } ...
<function moreinfo="none">is_privacy(privacy_type)</function> The function returns true if the SIP message has a Privacy header field that includes the given privacy_type among its privacy values. See http://www.iana.org/assignments/sip-priv-values for possible privacy type values. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. <function>is_privacy</function> usage ... if(is_privacy("id")) { # do interesting stuff here } ...
<function moreinfo="none">strip_body(),</function> <function moreinfo="none">strip_body(mime)</function> If the message has a body, this function deletes it, correcting Content-Length. This function also deletes the Content-Type header. If a MIME type is specified it will delete the body if it has the same type or, if the message has a multipart body it will go through the parts and delete all those with the specified type. It will not handle multi-layer multiparts Meaning of the parameters is as follows: mime - mime to be checked against the Content-Type header. If not present or 0, this check will be disabled. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>strip_body</function> usage ... strip_body(); #delete any body strip_body("multipart/related") # delete a multipart/related body strip_body("application/sdp") # delete all sdp bodies, even ones enclosed in multipart ...
<function moreinfo="none">add_body(body_text, new_content_type)</function> This function can be used to add a body to a message. If a body already exists, it will be replaced with the new one. The second parameter is the content type of the new body. It is optional: in case the function is used to replace an existing body with a body of the same type, there is no need to set this parameter. If the message has no body or if the types are different, it is compulsory to set this parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>add_body</function> usage ... add_body("Hello World!", "text/plain"); ...
<function moreinfo="none">sipmsg_validate([flags[,result_pvar]])</function> The function returns true if the SIP message is properly built according to SIP RFC3261. It verifies if the mandatory headers for each request/reply and can also check the format of the headers body. The flags parameter received is optional and can be composed with the following values: 's' - checks the integrity of the SDP body, if it exists 'h' - checks the format and integrity of each header body. 'm' - don't check the Max-Forwards header. 'r' - checks the R-URI and whether the domain contains valid characters. 'f' - checks the URI of the 'From' field and whether the domain contains valid characters. 't' - checks the URI of the 'To' field and whether the domain contains valid characters. 'c' - checks the URI of the 'Contact' field. The result_pvar parameter sets resulting pvar with text error reason in case of negative result ( easy for logging or propagating the rejection reason back to the bogus UA ) This function can return the following codes: 1 - the message is RFC3261 compliant and has been successfully validated. -1 - No SIP message -2 - Header Parsing error -3 - No Call-ID header -4 - No Content-Length header for transports that require it ( eg. TCP ) -5 - Invalid Content-Length, other from the size of the actual body -6 - SDP body parsing error. -7 - No Cseq header. -8 - No From header. -9 - No To header. -10 - No Via header. -11 - Request URI parse error. -12 - Bad hostname in R-URI. -13 - No Max-Forward header. -14 - No Contact header. -15 - Path user for non-Register request. -16 - No allow header in 405 reply. -17 - No Min-Expire header in 423 reply. -18 - No Proxy-Authorize header in 407 reply. -19 - No Unsupported header in 420 reply. -20 - No WWW-Authorize header in 401 reply. -21 - No Content-Type header -22 - To header parse error -23 - From header parse error -24 - Bad hostname in To header -25 - Bad hostname in From header -26 - Contact header parse error -255 - undefined errors. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. <function>sipmsg_validate</function> usage ... if(!sipmsg_validate()) { send_reply("400", "Bad Request"); exit; } ... ... # checks also the SDP and headers body if(!sipmsg_validate("sh")) { send_reply("400", "Bad Request/Body"); exit; } ...
<function moreinfo="none">codec_exists (name [,clock] )</function> This function can be used to verify if a codec exists inside an sdp payload. It will search for the codec inside all streams from all sdp sessions. If it is found anywhere it will return TRUE otherwise it will return FALSE. The second parameter is optional, if it is not supplied any clockrate will match. Parameters are CASE INSENSITIVE. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>codec_exists</function> usage ... codec_exists("speex"); or codec_exists("GSM","8000"); ...
<function moreinfo="none">codec_delete (name [,clock] )</function> This function can be used to delete a codec from inside an sdp payload. It will search for the codec inside all streams from all sdp sessions. If it is found anywhere it will be deleted from the mapping ("a=...") and from the list of indexes ("m=..."). Returns TRUE if any deletion occurred otherwise it will return FALSE. The second parameter is optional, if it is not supplied any clockrate will match and all will be deleted. Parameters are CASE INSENSITIVE. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>codec_delete</function> usage ... codec_delete("speex"); or codec_delete("GSM","8000"); ...
<function moreinfo="none">codec_move_up (name [,clock] )</function> This function can be used to move a codec up in the list of indexes ("m=..."). It will search for the codec inside all streams from all sdp sessions. If it is found anywhere it will be moved to the top of the index list. Returns TRUE if any moves occurred otherwise it will return FALSE. The second parameter is optional, if it is not supplied any clockrate will match and all codecs will be moved to the front while preserving their original ordering. Parameters are CASE INSENSITIVE. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>codec_move_up</function> usage ... codec_move_up("speex"); or codec_move_up("GSM","8000"); ...
<function moreinfo="none">codec_move_down (name [,clock] )</function> This function can be used to move a codec down in the list of indexes ("m=..."). It will search for the codec inside all streams from all sdp sessions. If it is found anywhere it will be moved to the back of the index list. Returns TRUE if any moves occurred otherwise it will return FALSE. The second parameter is optional, if it is not supplied any clockrate will match and all codecs will be moved to the back while preserving their original ordering. Parameters are CASE INSENSITIVE. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>codec_move_down</function> usage ... codec_move_down("speex"); or codec_move_down("GSM","8000"); ... <function>codec_move_down</function> usage ... /* This example will move speex with 8000 codec to the back of the list, then it will erase GSM with 8000 clock, and then it will bring all speex codecs to the front of the list. Speex/8000 will be behind any other speex. */ codec_move_down("speex","8000"); codec_delete("GSM","8000"); codec_move_up("speex"); ...
<function moreinfo="none">codec_exists_re ( regexp )</function> This function has the same effect as codec_exists ( without the clock parameter ) the only difference is that it takes a POSIX regular expression as a parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>codec_exists_re</function> usage ... codec_exists_re("sp[a-z]*"); ...
<function moreinfo="none">codec_delete_re ( regexp )</function> This function has the same effect as codec_delete ( without the clock parameter ) the only difference is that it takes a POSIX regular expression as a parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>codec_delete_re</function> usage ... codec_delete_re("PCMA|PCMU"); ...
<function moreinfo="none">codec_delete_except_re ( regexp )</function> This function deletes all the codecs except those specified by the regular expression. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>codec_delete_except_re</function> usage ... codec_delete_except_re("PCMA|PCMU");#will delete all codecs except PCMA and PCMU ...
<function moreinfo="none">codec_move_up_re ( regexp )</function> This function has the same effect as codec_move_up ( without the clock parameter ) the only difference is that it takes a POSIX regular expression as a parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>codec_move_up_re</function> usage ... codec_move_up_re("sp[a-z]*"); ...
<function moreinfo="none">codec_move_down_re ( regexp )</function> This function has the same effect as codec_move_down ( without the clock parameter ) the only difference is that it takes a POSIX regular expression as a parameter. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>codec_move_down_re</function> usage ... codec_move_down_re("sp[a-z]*"); ... <function>codec_move_down</function> usage ... /* This example will move speex with 8000 codec to the back of the list, then it will erase GSM with 8000 clock, and then it will bring all speex codecs to the front of the list. Speex/8000 will be behind any other speex. */ codec_move_down("speex","8000"); codec_delete("GSM","8000"); codec_move_up("speex"); ...
<function>change_reply_status(code, reason)</function> Intercept a SIP reply (in any onreply_route) and change its status code and reason phrase prior to propogating it. Meaning of the parameters is as follows: code - Status code. reason - Reason phrase. This function can be used from ONREPLY_ROUTE. <function>change_reply_status</function> usage ... onreply_route { if ($rs == "603") { change_reply_status("404", "Not Found"); exit; } } ...
<function moreinfo="none">stream_exists(regexp)</function> This function can be used to verify if a stream exists inside an sdp payload. It will search for the stream inside all sdp sessions. If it is found anywhere it will return TRUE otherwise it will return FALSE. Meaning of the parameters is as follows: regexp - a POSIX regular expression to match the stream media name. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>stream_exists</function> usage ... # check for FAX stream_exists("image"); ...
<function moreinfo="none">stream_delete(regexp)</function> This function can be used to delete a whole stream from inside an sdp payload. It will search for the stream inside all sdp sessions. If it is found anywhere it will be deleted along with all attributes Returns TRUE if any deletion occurred otherwise it will return FALSE. Meaning of the parameters is as follows: regexp - a POSIX regular expression to match the stream media name. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. <function>stream_delete</function> usage ... # prevent usage of video stream_delete("video"); ...
Known Limitations Search functions are applied to the current message so modifications made to the sdp will be visible to the codec_exists functions( e.g. after calling codec_delete("speex") , codec_exists("speex") will return false ).
opensips-2.2.2/modules/sipmsgops/sipmsgops.c000066400000000000000000001560531300170765700212610ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-29: - rewriting actions (replace, search_append) now begin * at the second line -- previously, they could affect * first line too, which resulted in wrong calculation of * forwarded requests and an error consequently * - replace_all introduced * 2003-01-28 scratchpad removed (jiri) * 2003-01-18 append_urihf introduced (jiri) * 2003-03-10 module export interface updated to the new format (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-97 actions permitted to be used from failure/reply routes (jiri) * 2003-04-21 remove_hf and is_present_hf introduced (jiri) * 2003-08-19 subst added (support for sed like res:s/re/repl/flags) (andrei) * 2003-08-20 subst_uri added (like above for uris) (andrei) * 2003-09-11 updated to new build_lump_rpl() interface (bogdan) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) * 2004-05-09: append_time introduced (jiri) * 2004-07-06 subst_user added (like subst_uri but only for user) (sobomax) * 2004-11-12 subst_user changes (old serdev mails) (andrei) * 2005-07-05 is_method("name") to check method using id (ramona) * 2006-03-17 applied patch from Marc Haisenko * for adding has_body() function (bogdan) * 2009-07-23 added methods for sdp codec manipulation(andreidragus) * 2012-02-21 add change_reply_status (idea from kamailio/textopsx) (rpedraza) * */ #include "../../sr_module.h" #include "../../action.h" #include "../../dprint.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../str.h" #include "../../re.h" #include "../../ut.h" #include "../../mod_fix.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_allow.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_event.h" #include "../../parser/parse_hname2.h" #include "../../parser/parse_methods.h" #include "../../parser/parse_content.h" #include "../../parser/parse_privacy.h" #include "../../parser/parse_authenticate.h" #include "../../parser/parse_supported.h" #include "../../parser/parse_disposition.h" #include "../../parser/parse_call_info.h" #include "../../parser/parse_sst.h" #include "../../parser/parse_from.h" #include "../../parser/parse_rr.h" #include "../../parser/sdp/sdp.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/digest/digest.h" #include "../../mod_fix.h" #include "../../trim.h" #include"codecs.h" #include #include #include #include /* for regex */ #include #include #include #include static str header_body = {0, 0}; /* RFC822-conforming dates format: %a -- abbreviated week of day name (locale), %d day of month as decimal number, %b abbreviated month name (locale), %Y year with century, %T time in 24h notation */ #define TIME_FORMAT "Date: %a, %d %b %Y %H:%M:%S GMT" #define MAX_TIME 64 #define AUDIO_STR "audio" #define AUDIO_STR_LEN 5 static int filter_body_f(struct sip_msg*, char*, char*); static int remove_hf_f(struct sip_msg* msg, char* str_hf, char* foo); static int remove_hf_match_f(struct sip_msg* msg, char* pattern, char* foo); static int is_present_hf_f(struct sip_msg* msg, char* str_hf, char* foo); static int append_to_reply_f(struct sip_msg* msg, char* key, char* str); static int append_hf_1(struct sip_msg* msg, char* str1, char* str2); static int append_hf_2(struct sip_msg* msg, char* str1, char* str2); static int insert_hf_1(struct sip_msg* msg, char* str1, char* str2); static int insert_hf_2(struct sip_msg* msg, char* str1, char* str2); static int append_urihf(struct sip_msg* msg, char* str1, char* str2); static int append_time_f(struct sip_msg* msg, char* , char *); static int is_method_f(struct sip_msg* msg, char* , char *); static int has_body_f(struct sip_msg *msg, char *type, char *str2 ); static int is_privacy_f(struct sip_msg *msg, char *privacy, char *str2 ); static int strip_body_f(struct sip_msg *msg, char *str1, char *str2 ); static int strip_body_f2(struct sip_msg *msg, char *str1, char *str2 ); static int add_body_f_1(struct sip_msg *msg, char *str1, char *str2 ); static int add_body_f_2(struct sip_msg *msg, char *str1, char *str2 ); static int is_audio_on_hold_f(struct sip_msg *msg, char *str1, char *str2 ); static int w_sip_validate(struct sip_msg *msg, char *flags_s, char* pv_result); static int hname_fixup(void** param, int param_no); static int free_hname_fixup(void** param, int param_no); static int hname_match_fixup(void** param, int param_no); static int free_hname_match_fixup(void** param, int param_no); static int fixup_method(void** param, int param_no); static int add_header_fixup(void** param, int param_no); static int fixup_body_type(void** param, int param_no); static int fixup_privacy(void** param, int param_no); static int fixup_sip_validate(void** param, int param_no); static int change_reply_status_f(struct sip_msg*, char*, char *); static int change_reply_status_fixup(void** param, int param_no); static int mod_init(void); static cmd_export_t cmds[]={ {"append_to_reply", (cmd_function)append_to_reply_f, 1, fixup_spve_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ERROR_ROUTE}, {"append_hf", (cmd_function)append_hf_1, 1, add_header_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"append_hf", (cmd_function)append_hf_2, 2, add_header_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"insert_hf", (cmd_function)insert_hf_1, 1, add_header_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"insert_hf", (cmd_function)insert_hf_2, 2, add_header_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"append_urihf", (cmd_function)append_urihf, 2, fixup_str_str, fixup_free_str_str, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"remove_hf", (cmd_function)remove_hf_f, 1, hname_fixup, free_hname_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"remove_hf", (cmd_function)remove_hf_match_f, 2, hname_match_fixup, free_hname_match_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"is_present_hf", (cmd_function)is_present_hf_f, 1, hname_fixup, free_hname_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"filter_body", (cmd_function)filter_body_f, 1, fixup_str_null, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"append_time", (cmd_function)append_time_f, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE }, {"is_method", (cmd_function)is_method_f, 1, fixup_method, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"has_body", (cmd_function)has_body_f, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"has_body", (cmd_function)has_body_f, 1, fixup_body_type, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"is_privacy", (cmd_function)is_privacy_f, 1, fixup_privacy, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"strip_body", (cmd_function)strip_body_f, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE }, {"strip_body", (cmd_function)strip_body_f2, 1, fixup_body_type, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE }, {"add_body", (cmd_function)add_body_f_1, 1, fixup_spve_null, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"add_body", (cmd_function)add_body_f_2, 2, add_header_fixup, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_exists", (cmd_function)codec_find, 1, fixup_codec,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_exists_re", (cmd_function)codec_find_re, 1, fixup_codec_regexp, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_exists", (cmd_function)codec_find_clock, 2, fixup_codec,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_delete", (cmd_function)codec_delete, 1, fixup_codec,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_delete_re", (cmd_function)codec_delete_re, 1, fixup_codec_regexp, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_delete_except_re", (cmd_function)codec_delete_except_re, 1, fixup_codec_regexp, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_delete", (cmd_function)codec_delete_clock, 2, fixup_codec,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_move_up", (cmd_function)codec_move_up, 1, fixup_codec,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_move_up_re", (cmd_function)codec_move_up_re, 1, fixup_codec_regexp,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_move_up", (cmd_function)codec_move_up_clock, 2, fixup_codec,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_move_down", (cmd_function)codec_move_down, 1, fixup_codec,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_move_down_re", (cmd_function)codec_move_down_re, 1, fixup_codec_regexp,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"codec_move_down", (cmd_function)codec_move_down_clock, 2, fixup_codec,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"is_audio_on_hold", (cmd_function)is_audio_on_hold_f, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"sipmsg_validate", (cmd_function)w_sip_validate, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"sipmsg_validate", (cmd_function)w_sip_validate, 1, fixup_sip_validate, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"sipmsg_validate", (cmd_function)w_sip_validate, 2, fixup_sip_validate, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"change_reply_status", (cmd_function)change_reply_status_f, 2, change_reply_status_fixup, 0, ONREPLY_ROUTE }, {"stream_exists", (cmd_function)stream_find, 1, fixup_regexp_dynamic_null,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"stream_delete", (cmd_function)stream_delete, 1, fixup_regexp_dynamic_null,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {0,0,0,0,0,0} }; struct module_exports exports= { "sipmsgops", /* module name*/ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ 0, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* per-child init function */ }; static int mod_init(void) { LM_INFO("initializing...\n"); return 0; } static inline int find_line_start(char *text, unsigned int text_len, char **buf, unsigned int *buf_len) { char *ch, *start; unsigned int len; start = *buf; len = *buf_len; while (text_len <= len) { if (strncmp(text, start, text_len) == 0) { *buf = start; *buf_len = len; return 1; } if ((ch = memchr(start, 13, len - 1))) { if (*(ch + 1) != 10) { LM_ERR("No LF after CR\n"); return 0; } len = len - (ch - start + 2); start = ch + 2; } else { LM_ERR("No CRLF found\n"); return 0; } } return 0; } /* Filters multipart body by leaving out everything else except * first body part of given content type. */ static int filter_body_f(struct sip_msg* msg, char* _content_type, char* ignored) { char *start; unsigned int len; str *content_type, body; if ( get_body(msg,&body)!=0 || body.len==0) { LM_DBG("message body has zero length\n"); return -1; } content_type = (str *)_content_type; start = body.s; len = body.len; while (find_line_start("Content-Type: ", 14, &start, &len)) { start = start + 14; len = len - 14; if (len > content_type->len + 2) { if (strncasecmp(start, content_type->s, content_type->len) == 0) { start = start + content_type->len; len = len - content_type->len; if ((*start != 13) || (*(start + 1) != 10)) { LM_ERR("No CRLF found after content type\n"); return -1; } while ((len > 3) && !(*start == 13 && *(start+1) == 10 && *(start+2) == 13 && *(start+3) == 10)) { len = len - 1; start = start + 1; } while ((len > 0) && ((*start == 13) || (*start == 10))) { len = len - 1; start = start + 1; } if (del_lump(msg, body.s - msg->buf, start - body.s, 0) == 0) { LM_ERR("Deleting lump <%.*s> failed\n", (int)(start - body.s), body.s); return -1; } if (find_line_start("--Boundary", 10, &start, &len)) { if (del_lump(msg, start - msg->buf, len, 0) == 0) { LM_ERR("Deleting lump <%.*s> failed\n", len, start); return -1; } else { return 1; } } else { LM_ERR("Boundary not found after content\n"); return -1; } } } else { return -1; } } return -1; } int get_pvs_header_value(struct sip_msg *msg, gparam_p gp, pv_value_p ret) { struct hdr_field hdr; int hdr_len; if (fixup_get_svalue(msg, gp, &ret->rs) != 0) { LM_ERR("failed to get the string value\n"); return -1; } hdr_len = ret->rs.len + 1; if (header_body.len < hdr_len) { header_body.s = pkg_realloc(header_body.s, hdr_len); if (!header_body.s) { LM_ERR("PKG MEMORY depleted!\n"); return E_OUT_OF_MEM; } header_body.len = hdr_len; } memcpy(header_body.s, ret->rs.s, ret->rs.len); header_body.s[ret->rs.len] = ':'; LM_DBG("Parsing %.*s\n", hdr_len, header_body.s); if (parse_hname2(header_body.s, header_body.s + hdr_len, &hdr) == 0) { LM_ERR("error parsing header name '%.*s'\n", ret->rs.len, ret->rs.s); return E_UNSPEC; } if (hdr.type != HDR_OTHER_T && hdr.type != HDR_ERROR_T) { LM_DBG("using hdr type (%d) instead of <%.*s>\n", hdr.type, ret->rs.len, ret->rs.s); ret->flags = PV_VAL_INT; ret->ri = hdr.type; } return 0; } static int hf_already_removed(struct sip_msg* msg, unsigned int offset, unsigned int len, enum _hdr_types_t type) { struct lump *it; /* parse only the msg headers, not the body */ for (it = msg->add_rm; it; it = it->next) { if (it->op == LUMP_DEL && it->type == type && it->u.offset == offset && it->len == len) return 1; } return 0; } static int remove_hf_f(struct sip_msg* msg, char* str_hf, char* foo) { struct hdr_field *hf; struct lump* l; int cnt; pv_value_t pval; cnt=0; pval.flags = PV_VAL_NONE; if (((gparam_p)str_hf)->type == GPARAM_TYPE_INT) { pval.flags = PV_VAL_INT; pval.ri = ((gparam_p)str_hf)->v.ival; } else if (get_pvs_header_value(msg, (gparam_p)str_hf, &pval) != 0) { LM_ERR("failed to get header value\n"); return -1; } /* we need to be sure we have seen all HFs */ parse_headers(msg, HDR_EOH_F, 0); for (hf=msg->headers; hf; hf=hf->next) { /* for well known header names str_hf->s will be set to NULL during parsing of opensips.cfg and str_hf->len contains the header type */ if (pval.flags & PV_VAL_INT) { if (pval.ri != hf->type) continue; } else { if (hf->type != HDR_OTHER_T) continue; if (hf->name.len != pval.rs.len) continue; if (strncasecmp(hf->name.s, pval.rs.s, hf->name.len) != 0) continue; } /* check to see if the header was already removed */ if (hf_already_removed(msg, hf->name.s-msg->buf, hf->len, hf->type)) continue; l=del_lump(msg, hf->name.s-msg->buf, hf->len, hf->type); if (l==0) { LM_ERR("no memory\n"); return -1; } cnt++; } return cnt==0 ? -1 : 1; } static int remove_hf_match_f(struct sip_msg* msg, char* pattern, char* regex_or_glob) { struct hdr_field *hf; struct lump* l; int cnt; str* pat = (str*)pattern; regex_t* re = (regex_t*)pattern; char matchtype = *regex_or_glob; regmatch_t pmatch; char tmp; cnt=0; /* we need to be sure we have seen all HFs */ if (parse_headers(msg, HDR_EOH_F, 0)!=0) { LM_ERR("failed to parse SIP message\n"); return -1; } for (hf=msg->headers; hf; hf=hf->next) { tmp = *(hf->name.s+hf->name.len); *(hf->name.s+hf->name.len) = 0; if( matchtype == 'g' ) { /* GLOB */ if(fnmatch(pat->s, hf->name.s, 0) !=0 ){ *(hf->name.s+hf->name.len) = tmp; continue; } } else if( matchtype == 'r' ){ /* REGEX */ if(regexec(re, hf->name.s, 1, &pmatch, 0)!=0){ *(hf->name.s+hf->name.len) = tmp; continue; } } else { LM_ERR("Unknown match type. Supported types are r (regex) and g (glob)"); return -1; } *(hf->name.s+hf->name.len) = tmp; /* check to see if the header was already removed */ if (hf_already_removed(msg, hf->name.s-msg->buf, hf->len, hf->type)) continue; l=del_lump(msg, hf->name.s-msg->buf, hf->len, hf->type); if (l==0) { LM_ERR("no memory\n"); return -1; } cnt++; } return cnt==0 ? -1 : 1; } static int is_present_hf_f(struct sip_msg* msg, char* str_hf, char* foo) { struct hdr_field *hf; pv_value_t pval; memset(&pval, '\0', sizeof pval); if (((gparam_p)str_hf)->type == GPARAM_TYPE_INT) { pval.flags = PV_VAL_INT; pval.ri = ((gparam_p)str_hf)->v.ival; } else if (get_pvs_header_value(msg, (gparam_p)str_hf, &pval) != 0) { LM_ERR("failed to get header value\n"); return -1; } /* we need to be sure we have seen all HFs */ parse_headers(msg, HDR_EOH_F, 0); if (pval.flags & PV_VAL_INT) { for (hf=msg->headers; hf; hf=hf->next) if (pval.ri == hf->type) return 1; } else { for (hf=msg->headers; hf; hf=hf->next) if (hf->type == HDR_OTHER_T && hf->name.len == pval.rs.len && strncasecmp(hf->name.s, pval.rs.s, hf->name.len) == 0) return 1; } LM_DBG("header '%.*s'(%d) not found\n", pval.rs.len, pval.rs.s, pval.ri); return -1; } static int append_time_f(struct sip_msg* msg, char* p1, char *p2) { size_t len; char time_str[MAX_TIME]; time_t now; struct tm *bd_time; now=time(0); bd_time=gmtime(&now); if (bd_time==NULL) { LM_ERR("gmtime failed\n"); return -1; } len=strftime(time_str, MAX_TIME, TIME_FORMAT, bd_time); if (len>MAX_TIME-2 || len==0) { LM_ERR("unexpected time length\n"); return -1; } time_str[len]='\r'; time_str[len+1]='\n'; if (add_lump_rpl(msg, time_str, len+2, LUMP_RPL_HDR)==0) { LM_ERR("unable to add lump\n"); return -1; } return 1; } static int append_to_reply_f(struct sip_msg* msg, char* key, char* str0) { str s0; if(key==NULL) { LM_ERR("bad parameters\n"); return -1; } if(fixup_get_svalue(msg, (gparam_p)key, &s0)!=0) { LM_ERR("cannot print the format\n"); return -1; } if ( add_lump_rpl( msg, s0.s, s0.len, LUMP_RPL_HDR)==0 ) { LM_ERR("unable to add lump_rl\n"); return -1; } return 1; } /* add str1 to end of header or str1.r-uri.str2 */ static int add_hf_helper(struct sip_msg* msg, str *str1, str *str2, gparam_p hfval, int mode, gparam_p hfanc) { struct lump* anchor; struct hdr_field *hf; char *s; int len; str s0; if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LM_ERR("error while parsing message\n"); return -1; } hf = 0; if(hfanc!=NULL) { for (hf=msg->headers; hf; hf=hf->next) { if(hfanc->type==GPARAM_TYPE_INT) { if (hfanc->v.ival!=hf->type) continue; } else { if (hf->type!=HDR_OTHER_T) continue; if (hf->name.len!=hfanc->v.sval.len) continue; if (strncasecmp(hf->name.s,hfanc->v.sval.s,hf->name.len)!=0) continue; } break; } } if(mode == 0) { /* append */ if(hf==0) { /* after last header */ anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0); } else { /* after hf */ anchor = anchor_lump(msg, hf->name.s + hf->len - msg->buf, 0); } } else { /* insert */ if(hf==0) { /* before first header */ anchor = anchor_lump(msg, msg->headers->name.s - msg->buf, 0); } else { /* before hf */ anchor = anchor_lump(msg, hf->name.s - msg->buf, 0); } } if(anchor == 0) { LM_ERR("can't get anchor\n"); return -1; } if(str1) { s0 = *str1; } else { if(hfval) { if(fixup_get_svalue(msg, hfval, &s0)!=0) { LM_ERR("cannot print the format\n"); return -1; } } else { s0.len = 0; s0.s = 0; } } len=s0.len; if (str2) len+= str2->len + REQ_LINE(msg).uri.len; s = (char*)pkg_malloc(len); if (!s) { LM_ERR("no pkg memory left\n"); return -1; } memcpy(s, s0.s, s0.len); if (str2) { memcpy(s+str1->len, REQ_LINE(msg).uri.s, REQ_LINE(msg).uri.len); memcpy(s+str1->len+REQ_LINE(msg).uri.len, str2->s, str2->len ); } if (insert_new_lump_before(anchor, s, len, 0) == 0) { LM_ERR("can't insert lump\n"); pkg_free(s); return -1; } return 1; } static int append_hf_1(struct sip_msg *msg, char *str1, char *str2 ) { return add_hf_helper(msg, 0, 0, (gparam_p)str1, 0, 0); } static int append_hf_2(struct sip_msg *msg, char *str1, char *str2 ) { return add_hf_helper(msg, 0, 0, (gparam_p)str1, 0, (gparam_p)str2); } static int insert_hf_1(struct sip_msg *msg, char *str1, char *str2 ) { return add_hf_helper(msg, 0, 0, (gparam_p)str1, 1, 0); } static int insert_hf_2(struct sip_msg *msg, char *str1, char *str2 ) { return add_hf_helper(msg, 0, 0, (gparam_p)str1, 1, (gparam_p)str2); } static int append_urihf(struct sip_msg *msg, char *str1, char *str2) { return add_hf_helper(msg, (str*)str1, (str*)str2, 0, 0, 0); } static int is_method_f(struct sip_msg *msg, char *meth, char *str2 ) { str *m; m = (str*)meth; if(msg->first_line.type==SIP_REQUEST) { if(m->s==0) return (msg->first_line.u.request.method_value&m->len)?1:-1; else return (msg->first_line.u.request.method_value==METHOD_OTHER && msg->first_line.u.request.method.len==m->len && (strncasecmp(msg->first_line.u.request.method.s, m->s, m->len)==0))?1:-1; } if(parse_headers(msg, HDR_CSEQ_F, 0)!=0 || msg->cseq==NULL) { LM_ERR("cannot parse cseq header\n"); return -1; /* should it be 0 ?!?! */ } if(m->s==0) return (get_cseq(msg)->method_id&m->len)?1:-1; else return (get_cseq(msg)->method_id==METHOD_OTHER && get_cseq(msg)->method.len==m->len && (strncasecmp(get_cseq(msg)->method.s, m->s, m->len)==0))?1:-1; } /* * Convert char* header_name to str* parameter */ static int hname_fixup(void** param, int param_no) { char *c; int len; struct hdr_field hdr; gparam_p gp = NULL; if (fixup_sgp(param) != 0) { LM_ERR("Fixup failed!\n"); return E_UNSPEC; } gp = (gparam_p)*param; if (gp->type == GPARAM_TYPE_STR) { /* parse_hname2() accepts a minimum 4 bytes len buffer * for parsing, so whatever is the len of the header name, * fill it up to 4 */ len = (gp->v.sval.len<3) ? (4) : (gp->v.sval.len+1) ; c = pkg_malloc( len ); if (!c) return E_OUT_OF_MEM; memcpy(c, gp->v.sval.s, gp->v.sval.len); c[gp->v.sval.len] = ':'; if (parse_hname2(c, c + len, &hdr) == 0) { LM_ERR("error parsing header name\n"); return E_UNSPEC; } pkg_free(c); if (hdr.type != HDR_OTHER_T && hdr.type != HDR_ERROR_T) { LM_DBG("using hdr type (%d) instead of <%.*s>\n", hdr.type, gp->v.sval.len, gp->v.sval.s); gp->type = GPARAM_TYPE_INT; gp->v.ival = hdr.type; } } return 0; } static int free_hname_fixup(void** param, int param_no) { if(*param) { if(((gparam_p)(*param))->type==GPARAM_TYPE_STR) pkg_free(((gparam_p)(*param))->v.sval.s); pkg_free(*param); *param = 0; } return 0; } static int hname_match_fixup(void** param, int param_no) { char * type_param = NULL; char * type_str = NULL; char type = 0; char * matchstr = *(char**)param; if(param_no == 1){ if(strlen(matchstr)==0){ LM_ERR("Empty match string parameter.\n"); return E_UNSPEC; } type_param = ((char*)param)+sizeof(action_elem_t); if(!type_param){ LM_ERR("Unable to fetch the 2nd parameter\n"); return E_UNSPEC; } type_str = *((char**)type_param); if(!type_str){ LM_ERR("Unable to access 2nd parameter value\n"); return E_UNSPEC; } type = *type_str; if(type == 'r'){ /* regex fixup code here */ LM_DBG("processing param1: %s as regex\n", *(char**)param); fixup_regexp_null(param, param_no); }else if(type == 'g'){ /* glob fixup code here */ LM_DBG("processing param1: %s as glob\n", *(char**)param); fixup_str(param); }else{ LM_ERR("unknown match type '%c'\n", type); return E_UNSPEC; } } return 0; } static int free_hname_match_fixup(void** param, int param_no) { char * type_param = NULL; char * type_str = NULL; char type = 0; if(param_no == 1){ type_param = ((char*)param)+sizeof(action_elem_t); if(!type_param){ LM_ERR("Unable to fetch the 2nd parameter\n"); return E_UNSPEC; } type_str = *((char**)type_param); if(!type_str){ LM_ERR("Unable to access 2nd parameter value\n"); return E_UNSPEC; } type = *type_str; if(type == 'r'){ /* regex fixup code here */ LM_DBG("Freeing regexp\n"); fixup_free_regexp_null(param, param_no); }else if(type == 'g'){ /* glob fixup code here */ LM_DBG("Freeing glob\n"); fixup_free_str_str(param, param_no); }else{ LM_ERR("unknown match type in free_hname_match_fixup. Please notify a developer.\n"); } } return 0; } /* * Convert char* method to str* parameter */ static int fixup_method(void** param, int param_no) { str* s; char *p; int m; unsigned int method; s = (str*)pkg_malloc(sizeof(str)); if (!s) { LM_ERR("no pkg memory left\n"); return E_UNSPEC; } s->s = (char*)*param; s->len = strlen(s->s); if(s->len==0) { LM_ERR("empty method name\n"); pkg_free(s); return E_UNSPEC; } m=0; p=s->s; while(*p) { if(*p=='|') { *p = ','; m=1; } p++; } if(parse_methods(s, &method)!=0) { LM_ERR("bad method names\n"); pkg_free(s); return E_UNSPEC; } if(m==1) { if(method==METHOD_UNDEF || method&METHOD_OTHER) { LM_ERR("unknown method in list [%.*s/%d] - must be only defined methods\n", s->len, s->s, method); return E_UNSPEC; } LM_DBG("using id for methods [%.*s/%d]\n", s->len, s->s, method); s->s = 0; s->len = method; } else { if(method!=METHOD_UNDEF && method!=METHOD_OTHER) { LM_DBG("using id for method [%.*s/%d]\n", s->len, s->s, method); s->s = 0; s->len = method; } else LM_DBG("name for method [%.*s/%d]\n", s->len, s->s, method); } *param = (void*)s; return 0; } /* * Convert char* privacy value to corresponding bit value */ static int fixup_privacy(void** param, int param_no) { str p; unsigned int val; p.s = (char*)*param; p.len = strlen(p.s); if (p.len == 0) { LM_ERR("empty privacy value\n"); return E_UNSPEC; } if (parse_priv_value(p.s, p.len, &val) != p.len) { LM_ERR("invalid privacy value\n"); return E_UNSPEC; } *param = (void *)(long)val; return 0; } static int add_header_fixup(void** param, int param_no) { if(param_no==1) { return fixup_spve_null(param, param_no); } else if(param_no==2) { return hname_fixup(param, param_no); } else { LM_ERR("wrong number of parameters\n"); return E_UNSPEC; } } static int fixup_body_type(void** param, int param_no) { char *p; char *r; unsigned int type; if(param_no==1) { p = (char*)*param; if (p==0 || p[0]==0) { type = 0; } else { r = decode_mime_type( p, p+strlen(p) , &type , NULL); if (r==0) { LM_ERR("unsupported mime <%s>\n",p); return E_CFG; } if ( r!=p+strlen(p) ) { LM_ERR("multiple mimes not supported!\n"); return E_CFG; } } pkg_free(*param); *param = (void*)(long)type; } return 0; } static int has_body_f(struct sip_msg *msg, char *type, char *str2 ) { struct multi_body * m; struct part * p; /* parse content len hdr */ if ( msg->content_length==NULL && (parse_headers(msg,HDR_CONTENTLENGTH_F, 0)==-1||msg->content_length==NULL)) return -1; if (get_content_length (msg)==0) { LM_DBG("content length is zero\n"); /* Nothing to see here, please move on. */ return -1; } if( ( ((int)(long)type )>>16) == TYPE_MULTIPART ) { int mime = parse_content_type_hdr(msg); if( mime == ((int)(long)type ) ) return 1; return -1; } /* check type also? */ if (type==0) return 1; m = get_all_bodies(msg); if (m == NULL) { LM_ERR("Failed to get bodies\n"); return -1; } /* if there is no multipart and the type is unspecified default to application/sdp */ if (m->from_multi_part == 0 && m->part_count == 1 && m->first->content_type == 0) { m->first->content_type = ((TYPE_APPLICATION << 16) + SUBTYPE_SDP); } p = m->first; while (p) { if( p->content_type == ((int)(long)type ) ) return 1; p = p->next; } return -1; } static int is_privacy_f(struct sip_msg *msg, char *_privacy, char *str2 ) { if (parse_privacy(msg) == -1) return -1; return get_privacy_values(msg) & ((unsigned int)(long)_privacy) ? 1 : -1; } /* * Function to remove the body of a message * */ static int strip_body_f(struct sip_msg *msg, char *str1, char *str2 ) { str body; /* get body pointer */ if ( get_body(msg,&body)!=0 || body.len==0) { LM_DBG("message body has zero length\n"); return -1; } /* delete all body lumps from the list */ /* NOTE: do not delete the SHM lumps (which are primarily stored in TM Such lumps need to skipped and only detached - bogdan */ del_notflaged_lumps( &msg->body_lumps, LUMPFLAG_SHMEM ); msg->body_lumps = NULL; /* add delete body lump */ if( del_lump(msg, body.s-msg->buf, body.len, HDR_EOH_T) == 0) { LM_ERR("failed to add lump to delete body\n"); return -1; } if (msg->content_type == NULL || msg->content_type->name.s == NULL || msg->content_type->name.len == 0) { LM_WARN("You have a body but you don't have Content-Type! This is" "not a valid SIP message! The body WILL BE stripped!\n"); goto out; } /* add delete content-type header lump */ if(del_lump(msg, msg->content_type->name.s- msg->buf, msg->content_type->len, HDR_CONTENTTYPE_T) == 0) { LM_ERR("failed to add lump to delete content type header\n"); return -1; } out: return 1; } static int strip_body_f2(struct sip_msg *msg, char *type, char *str2 ) { struct multi_body * m; struct part * p; int deleted = 0,mime; /* parse content len hdr */ if ( msg->content_length==NULL && (parse_headers(msg,HDR_CONTENTLENGTH_F, 0)==-1||msg->content_length==NULL)) return -1; if (get_content_length (msg)==0) { LM_DBG("content length is zero\n"); /* Nothing to see here, please move on. */ return -1; } mime = parse_content_type_hdr(msg); if( ( ((int)(long)type )>>16) == TYPE_MULTIPART || (mime >>16) != TYPE_MULTIPART) { if( mime == ((int)(long)type ) ) { strip_body_f(msg,NULL,NULL); } return -1; } m = get_all_bodies(msg); if (m == NULL) { LM_ERR("Failed to get bodies\n"); return -1; } /* if there is no multipart and the type is unspecified default to application/sdp */ if (m->from_multi_part == 0 && m->part_count == 1 && m->first->content_type == 0) { m->first->content_type = ((TYPE_APPLICATION << 16) + SUBTYPE_SDP); } p = m->first; deleted = -1; while (p) { if( p->content_type == ((int)(long)type ) ) { if( del_lump( msg, p->all_data.s - msg->buf - 4 - m->boundary.len, p->all_data.len + 6 + m->boundary.len, 0 ) == 0 ) { LM_ERR("Failed to add body lump\n"); return -1; } deleted = 1; } p = p->next; } return deleted; } /* * Function to add a new body * */ static int add_body_f(struct sip_msg *msg, gparam_p nbody, gparam_p ctype ) { str body; struct lump* anchor; unsigned int offset; str new_body; char* value= NULL; str content_type, ctype_hf; if(fixup_get_svalue(msg, nbody, &new_body)!=0) { LM_ERR("cannot print the format\n"); return -1; } if(new_body.s== NULL || new_body.len == 0) { LM_ERR("null body parameter\n"); return -1; } /* get body pointer */ if ( get_body(msg,&body)!=0 ) { LM_ERR("failed to get gody\n"); return -1; } /* must delete all body lumps from the list */ free_lump_list(msg->body_lumps); msg->body_lumps = NULL; if (body.len!=0) { /* delete old body */ offset = body.s - msg->buf; if(del_lump(msg, offset, body.len, HDR_EOH_T) == 0) { LM_ERR("failed to add lump to delete body\n"); return -1; } } else { LM_DBG("content length is zero\n"); offset = msg->len; if(ctype== NULL) { LM_ERR("No body found and no content-type name given" " as parameter\n"); return -1; } } /* add an add body lump */ anchor = anchor_lump(msg, offset, 0); if(anchor == 0) { LM_ERR("failed to insert an add new body anchor"); return -1; } value = (char*)pkg_malloc(new_body.len); if(value== NULL) { LM_ERR("no more memory\n"); return -1; } memcpy(value, new_body.s, new_body.len); if (insert_new_lump_before(anchor, value, new_body.len, 0) == 0) { LM_ERR("failed to insert lump\n"); pkg_free(value); return -1; } if(ctype) { if(fixup_get_svalue(msg, ctype, &content_type)!=0) { LM_ERR("cannot print the format\n"); return -1; } if(msg->content_type) { /* verify if the parameter has the same value */ if(content_type.len == msg->content_type->body.len && strncmp(msg->content_type->body.s, content_type.s, content_type.len)== 0) { return 1; } if(del_lump(msg, msg->content_type->name.s- msg->buf, msg->content_type->len, HDR_CONTENTTYPE_T) == 0) { LM_ERR("failed to add lump to delete content type header\n"); return -1; } } /* add new Content-Type header */ /* construct header */ ctype_hf.len = strlen("Content-Type: ") + content_type.len+ CRLF_LEN; ctype_hf.s = (char*)pkg_malloc(ctype_hf.len); if(ctype_hf.s == NULL) { LM_ERR("no more memory\n"); return -1; } sprintf(ctype_hf.s, "Content-Type: %.*s%s", content_type.len, content_type.s, CRLF); if( add_hf_helper(msg, &ctype_hf, 0, 0, 0, 0)< 0) { LM_ERR("failed to add content type header\n"); pkg_free(ctype_hf.s); return -1; } pkg_free(ctype_hf.s); } return 1; } static int add_body_f_1(struct sip_msg *msg, char *nbody, char *ctype ) { return add_body_f(msg, (gparam_p)nbody, NULL); } static int add_body_f_2(struct sip_msg *msg, char *nbody, char *ctype ) { return add_body_f(msg, (gparam_p)nbody, (gparam_p)ctype); } static int is_audio_on_hold_f(struct sip_msg *msg, char *str1, char *str2 ) { int sdp_session_num = 0, sdp_stream_num; sdp_session_cell_t* sdp_session; sdp_stream_cell_t* sdp_stream; if (0 == parse_sdp(msg)) { for(;;) { sdp_session = get_sdp_session(msg, sdp_session_num); if(!sdp_session) break; sdp_stream_num = 0; for(;;) { sdp_stream = get_sdp_stream(msg, sdp_session_num, sdp_stream_num); if(!sdp_stream) break; if(sdp_stream->media.len==AUDIO_STR_LEN && strncmp(sdp_stream->media.s,AUDIO_STR,AUDIO_STR_LEN)==0 && sdp_stream->is_on_hold) return 1; sdp_stream_num++; } sdp_session_num++; } } return -1; } #define SIP_PARSE_SDP 0x1 #define SIP_PARSE_HDR 0x2 #define SIP_PARSE_NOMF 0x4 #define SIP_PARSE_RURI 0x8 #define SIP_PARSE_TO 0x10 #define SIP_PARSE_FROM 0x20 #define SIP_PARSE_CONTACT 0x40 static int fixup_sip_validate(void** param, int param_no) { char *flags_s, *end; unsigned long flags = 0; pv_elem_t *pvar; str s; if (param_no==1) { if (!param) { goto end; } flags_s = (char*)*param; end = flags_s + strlen(flags_s); for ( ; flags_s < end; flags_s++) { switch (*flags_s) { case 's': case 'S': flags |= SIP_PARSE_SDP; break; case 'h': case 'H': flags |= SIP_PARSE_HDR; break; case 'm': case 'M': flags |= SIP_PARSE_NOMF; break; case 'r': case 'R': flags |= SIP_PARSE_RURI; break; case 't': case 'T': flags |= SIP_PARSE_TO; break; case 'f': case 'F': flags |= SIP_PARSE_FROM; break; case 'c': case 'C': flags |= SIP_PARSE_CONTACT; break; default: LM_DBG("unknown option \'%c\'\n", *flags_s); break; } } end: *param = (void *)(unsigned long)flags; return 0; } else if (param_no==2) { s.s = (char*)(*param); s.len = strlen(s.s); if (pv_parse_format(&s, &pvar)<0) { LM_ERR( "wrong format[%s]\n",(char*)(*param)); return E_UNSPEC; } *param = (void*)pvar; return 0; } else { LM_ERR("invalid parameter number %d\n", param_no); return E_UNSPEC; } } static int sip_validate_hdrs(struct sip_msg *msg) { struct disposition *disp; struct hdr_field* hf; struct to_body *to; content_t * cont; struct via_body *via_b; str str_aux; char *s_aux, *e_aux; unsigned u_aux; int i_aux; #define CHECK_HDR_EMPTY() \ do { \ str_aux = hf->body; \ trim_len( str_aux.len , str_aux.s , hf->body ); \ if (str_aux.len <= 0) { \ LM_DBG("header '%.*s' has invalid value %d\n", \ hf->name.len, hf->name.s, str_aux.len); \ goto failed; \ } \ } while (0) #define CHECK_HDR_FUNC(_f, _o) \ do { \ if (_f(_o) < 0) { \ LM_DBG("cannot parse '%.*s' header\n", \ hf->name.len, hf->name.s); \ goto failed; \ } \ } while (0) /* skip via, cseq, to and content length * = we can be sure that they have been properly parsed = * = because otherwise the code wouldn't reach here = * = but in 'parse_msg'*/ for (hf = msg->headers; hf; hf = hf->next) { /* try to eliminate errors fast */ if (hf->type == HDR_ERROR_T) { LM_DBG("header %.*s could not be properly parsed\n", hf->name.len, hf->name.s); goto failed; } /* was successfully parsed previously */ if (hf->parsed) continue; /* try to manually parse them */ switch (hf->type) { case HDR_CONTENTTYPE_T: if (!(cont = pkg_malloc(sizeof (content_t)))) { LM_ERR("Unable to allocate memory\n"); goto failed; } memset(cont, 0, sizeof (content_t)); /* it seams we have to parse it! :-( */ e_aux = hf->body.s + hf->body.len; if ((s_aux = decode_mime_type(hf->body.s, e_aux , &u_aux, cont)) == 0) { pkg_free(cont); goto failed; } if (e_aux != s_aux) { LM_DBG("the header CONTENT_TYPE contains " "more than one mime type :-(!\n"); pkg_free(cont); goto failed; } if ((u_aux&0x00ff)==SUBTYPE_ALL || (u_aux>>16)==TYPE_ALL) { LM_DBG("invalid mime with wildcard '*'" " in Content-Type hdr!\n"); pkg_free(cont); goto failed; } cont->type = u_aux; hf->parsed = cont; break; case HDR_VIA2_T: via_b=pkg_malloc(sizeof(struct via_body)); if (!via_b) { LM_ERR("out of pkg memory\n"); goto failed; } memset(via_b, 0, sizeof(struct via_body)); e_aux = parse_via(hf->body.s, hf->body.s + hf->body.len, via_b); if (via_b->error==PARSE_ERROR){ LM_DBG("bad via header\n"); free_via_list(via_b); goto failed; } via_b->hdr.s = hf->name.s; via_b->hdr.len = hf->name.len; hf->parsed = via_b; break; case HDR_CONTENTDISPOSITION_T: if (!(disp = (struct disposition*) pkg_malloc(sizeof(struct disposition)))) { LM_ERR("no more pkg memory\n"); goto failed; } memset(disp, 0, sizeof(struct disposition)); if (parse_disposition(&(hf->body), disp)<0) { free_disposition(&disp); LM_DBG("cannot parse disposition\n"); goto failed; } /* even if success, we need to free the parsed disposition hdr as it is not linked anywhere */ free_disposition(&disp); break; /* to-style headers */ case HDR_FROM_T: case HDR_PPI_T: case HDR_PAI_T: case HDR_RPID_T: case HDR_REFER_TO_T: case HDR_DIVERSION_T: /* these are similar */ if (!(to = pkg_malloc(sizeof(struct to_body)))) { LM_ERR("out of pkg_memory\n"); goto failed; } parse_to(hf->body.s, hf->body.s + hf->body.len + 1, to); if (to->error == PARSE_ERROR) { LM_DBG("bad '%.*s' header\n", hf->name.len, hf->name.s); pkg_free(to); goto failed; } hf->parsed = to; break; case HDR_MAXFORWARDS_T: CHECK_HDR_EMPTY(); /* should be a number */ u_aux = str2s(str_aux.s, str_aux.len, &i_aux); if (i_aux) { LM_DBG("invalid body number in '%.*s'\n", hf->name.len, hf->name.s); goto failed; } hf->parsed = (void*)(unsigned long)u_aux; break; case HDR_SUPPORTED_T: CHECK_HDR_FUNC(parse_supported, msg); break; case HDR_ACCEPT_T: CHECK_HDR_FUNC(parse_accept_hdr, msg); break; case HDR_PRIVACY_T: CHECK_HDR_FUNC(parse_privacy, msg); break; case HDR_CONTACT_T: CHECK_HDR_FUNC(parse_contact, hf); break; case HDR_PATH_T: case HDR_ROUTE_T: case HDR_RECORDROUTE_T: CHECK_HDR_FUNC(parse_rr, hf); break; case HDR_AUTHORIZATION_T: case HDR_PROXYAUTH_T: CHECK_HDR_FUNC(parse_credentials, hf); break; case HDR_EXPIRES_T: CHECK_HDR_FUNC(parse_expires, hf); break; case HDR_ALLOW_T: CHECK_HDR_FUNC(parse_allow, msg); break; case HDR_EVENT_T: CHECK_HDR_FUNC(parse_event, hf); break; case HDR_SESSION_EXPIRES_T: CHECK_HDR_FUNC(parse_session_expires_body, hf); break; case HDR_CALL_INFO_T: CHECK_HDR_FUNC(parse_call_info_header, msg); break; case HDR_MIN_SE_T: case HDR_MIN_EXPIRES_T: CHECK_HDR_FUNC(parse_min_se_body, hf); break; case HDR_PROXY_AUTHENTICATE_T: case HDR_WWW_AUTHENTICATE_T: CHECK_HDR_FUNC(parse_authenticate_header, hf); break; case HDR_CALLID_T: case HDR_PROXYREQUIRE_T: case HDR_UNSUPPORTED_T: case HDR_ACCEPTLANGUAGE_T: case HDR_ORGANIZATION_T: case HDR_PRIORITY_T: case HDR_SUBJECT_T: case HDR_USERAGENT_T: case HDR_ACCEPTDISPOSITION_T: case HDR_RETRY_AFTER_T: /* headers that must have body */ CHECK_HDR_EMPTY(); break; case HDR_ERROR_T: LM_DBG("[BUG] this can't be possible\n"); goto failed; case HDR_VIA1_T: case HDR_TO_T: case HDR_CSEQ_T: case HDR_CONTENTLENGTH_T: case HDR_EOH_T: LM_DBG("duplicate header \'%.*s\'\n", hf->name.len, hf->name.s); case HDR_OTHER_T: default: /* unknown or already parsed */ break; } } return 0; failed: return -1; } #define IS_ALPHANUM(_c) ( \ ((_c) >= 'a' && (_c) <= 'z') || \ ((_c) >= 'A' && (_c) <= 'Z') || \ ((_c) >= '0' && (_c) <= '9') ) static int check_hostname(str *domain) { char *p, *end; if (!domain || domain->len < 0) { LM_DBG("inexistent domain\n"); return -1; } /* always starts with a ALPHANUM */ if (!IS_ALPHANUM(domain->s[0])) { LM_DBG("invalid starting character in domain: %c[%d]\n", domain->s[0], domain->s[0]); return -1; } /* check the last character separately, as it cannot contain '-' */ end = domain->s + domain->len - 1; for (p = domain->s + 1; p < end; p++) { if (!IS_ALPHANUM(*p) && (*p != '-')) { if (*p != '.') { LM_DBG("invalid character in hostname: %c[%d]\n", *p, *p); return -1; } else if (*(p - 1) == '.') { LM_DBG("two consecutive '.' are not allowed in hostname\n"); return -1; } } } /* check if the last character is a '-' */ if (!IS_ALPHANUM(*end) && (*end != '.')) { LM_DBG("invalid character at the end of the domain: %c[%d]\n", *end, *end); return -1; } return 0; } #undef IS_ALPHANUM #define CHECK_HEADER(_m, _h) \ do { \ if (!msg->_h) { \ LM_DBG( _m " doesn't have " #_h " header\n"); \ goto failed; \ } \ } while (0) #define MAX_REASON 256 enum sip_validation_failures { SV_NO_MSG=-1, SV_HDR_PARSE_ERROR=-2, SV_NO_CALLID=-3, SV_NO_CONTENT_LENGTH=-4, SV_INVALID_CONTENT_LENGTH=-5, SV_PARSE_SDP=-6, SV_NO_CSEQ=-7, SV_NO_FROM=-8, SV_NO_TO=-9, SV_NO_VIA1=-10, SV_RURI_PARSE_ERROR=-11, SV_BAD_HOSTNAME=-12, SV_NO_MF=-13, SV_NO_CONTACT=-14, SV_PATH_NONREGISTER=-15, SV_NOALLOW_405=-16, SV_NOMINEXP_423=-17, SV_NO_PROXY_AUTH=-18, SV_NO_UNSUPPORTED=-19, SV_NO_WWW_AUTH=-20, SV_NO_CONTENT_TYPE=-21, SV_TO_PARSE_ERROR=-22, SV_TO_DOMAIN_ERROR=-23, SV_FROM_PARSE_ERROR=-24, SV_FROM_DOMAIN_ERROR=-25, SV_CONTACT_PARSE_ERROR=-26, SV_GENERIC_FAILURE=-255 }; static int w_sip_validate(struct sip_msg *msg, char *flags_s, char* pv_result) { unsigned int hdrs_len; int method; str body; struct hdr_field * ptr; contact_t * contacts; struct sip_uri test_contacts; struct cseq_body * cbody; struct to_body *from, *to; unsigned long flags; pv_elem_t* pv_res = (pv_elem_t*)pv_result; pv_value_t pv_val; char reason[MAX_REASON]; int ret = -SV_GENERIC_FAILURE; if (!msg) { strcpy(reason, "no message object"); ret = SV_NO_MSG; goto failed; } /* try to check the whole SIP msg */ if (parse_headers(msg, HDR_EOH_F, 0) < 0) { strcpy(reason, "message parsing failed"); ret = SV_HDR_PARSE_ERROR; goto failed; } /* any message has to have a call-id */ if (!msg->callid) { strcpy(reason, "message doesn't have callid"); ret = SV_NO_CALLID; goto failed; } /* content length should be present if protocol is not UDP */ if (msg->rcv.proto != PROTO_UDP && !msg->content_length) { snprintf(reason, MAX_REASON-1, "message doesn't have Content Length header for proto %d", msg->rcv.proto); ret = SV_NO_CONTENT_LENGTH; goto failed; } body.s = NULL; body.len = 0; flags = flags_s ? (unsigned long)(void*)flags_s : 0; /* if not CANCEL, check if it has body */ if (msg->first_line.type!=SIP_REQUEST || msg->REQ_METHOD!=METHOD_CANCEL) { if (!msg->unparsed) { strcpy(reason, "invalid parsing"); ret = SV_HDR_PARSE_ERROR; goto failed; } hdrs_len=(unsigned int)(msg->unparsed-msg->buf); if ((hdrs_len+2<=msg->len) && (strncmp(CRLF,msg->unparsed,CRLF_LEN)==0) ) body.s = msg->unparsed + CRLF_LEN; else if ( (hdrs_len+1<=msg->len) && (*(msg->unparsed)=='\n' || *(msg->unparsed)=='\r' ) ) body.s = msg->unparsed + 1; else { /* no body */ body.s = NULL; body.len = 0; } /* determine the length of the body */ if (body.s) body.len = msg->buf + msg->len - body.s; if (get_content_length(msg) != body.len) { snprintf(reason, MAX_REASON-1, "invalid body - content length %ld different than actual body %d", get_content_length(msg), body.len); ret = SV_INVALID_CONTENT_LENGTH; goto failed; } /* if has body, check for SDP */ if (body.s && body.len && (flags & SIP_PARSE_SDP) && parse_content_type_hdr(msg)==(TYPE_APPLICATION<<16 | SUBTYPE_SDP) ) { if (parse_sdp(msg) < 0) { strcpy(reason, "failed to parse SDP message"); ret = SV_PARSE_SDP; goto failed; } } } /* set reason to empty (covers cases where we * exit via CHECK_HEADER) */ reason[0] = 0; /* Cseq */ ret = SV_NO_CSEQ; CHECK_HEADER("", cseq); /* From */ ret = SV_NO_FROM; CHECK_HEADER("", from); /* To */ ret = SV_NO_TO; CHECK_HEADER("", to); /* check only if Via1 is present */ ret = SV_NO_VIA1; CHECK_HEADER("", via1); /* test to header uri */ if(flags & SIP_PARSE_TO) { if(!msg->to->parsed) { if(parse_to_header(msg) < 0) { strcpy(reason, "failed to parse 'To' header"); ret = SV_TO_PARSE_ERROR; goto failed; } } to = (struct to_body*)msg->to->parsed; if(parse_uri(to->uri.s, to->uri.len, &to->parsed_uri) < 0) { strcpy(reason, "failed to parse 'To' header"); ret = SV_TO_PARSE_ERROR; goto failed; } /* check for valid domain format */ if(check_hostname(&to->parsed_uri.host) < 0) { strcpy(reason, "invalid domain for 'To' header"); ret = SV_TO_DOMAIN_ERROR; goto failed; } } /* test from header uri */ if(flags & SIP_PARSE_FROM) { if(!msg->from->parsed) { if(parse_from_header(msg) < 0) { strcpy(reason, "failed to parse 'From' header"); ret = SV_FROM_PARSE_ERROR; goto failed; } } from = (struct to_body*)msg->from->parsed; if(parse_uri(from->uri.s, from->uri.len, &from->parsed_uri) < 0) { strcpy(reason, "failed to parse 'From' header"); ret = SV_FROM_PARSE_ERROR; goto failed; } /* check for valid domain format */ if(check_hostname(&from->parsed_uri.host) < 0) { strcpy(reason, "invalid domain for 'From' header"); ret = SV_FROM_DOMAIN_ERROR; goto failed; } } /* request or reply */ switch (msg->first_line.type) { case SIP_REQUEST: /* check R-URI */ if (flags & SIP_PARSE_RURI) { if(msg->parsed_uri_ok==0 && parse_sip_msg_uri(msg) < 0) { strcpy(reason, "failed to parse R-URI"); ret = SV_RURI_PARSE_ERROR; goto failed; } if (check_hostname(&msg->parsed_uri.host) < 0) { strcpy(reason, "invalid domain for R-URI"); ret = SV_BAD_HOSTNAME; goto failed; } } /* Max-Forwards */ if (!(flags & SIP_PARSE_NOMF)) { ret = SV_NO_MF; CHECK_HEADER("", maxforwards); } if (msg->REQ_METHOD == METHOD_INVITE) { ret = SV_NO_CONTACT; CHECK_HEADER("INVITE", contact); if(flags & SIP_PARSE_CONTACT) { /* iterate through Contact headers */ for(ptr = msg->contact; ptr; ptr = ptr->sibling) { /* parse Contact header */ if(!ptr->parsed && (parse_contact(ptr) < 0 || !ptr->parsed)) { strcpy(reason, "failed to parse 'Contact' header"); ret = SV_CONTACT_PARSE_ERROR; goto failed; } contacts = ((contact_body_t*)ptr->parsed)->contacts; /* empty contacts header - something must be wrong */ if(contacts == NULL) { strcpy(reason, "empty body for 'Contact' header"); ret = SV_CONTACT_PARSE_ERROR; goto failed; } /* iterate through URIs and check validty */ for(; contacts; contacts = contacts->next) { if(parse_uri(contacts->uri.s, contacts->uri.len, &test_contacts) < 0 || test_contacts.host.len < 0) { strcpy(reason, "failed to parse 'Contact' header"); ret = SV_CONTACT_PARSE_ERROR; goto failed; } } } } } if (msg->REQ_METHOD != METHOD_REGISTER && msg->path) { strcpy(reason, "PATH header supported only for REGISTERs"); ret = SV_PATH_NONREGISTER; goto failed; } method = msg->REQ_METHOD; break; case SIP_REPLY: /* checking the reply's message type */ cbody = (struct cseq_body *)msg->cseq->parsed; if (!cbody) { strcpy(reason, "cseq not parsed properly"); ret = SV_NO_CSEQ; goto failed; } method = cbody->method_id; if (method != METHOD_CANCEL) { switch (msg->first_line.u.reply.statuscode) { case 405: ret = SV_NOALLOW_405; CHECK_HEADER("", allow); break; case 423: if (method == METHOD_REGISTER) { ret = SV_NOMINEXP_423; CHECK_HEADER("REGISTER", min_expires); } break; case 407: ret = SV_NO_PROXY_AUTH; CHECK_HEADER("", proxy_authenticate); break; case 420: ret = SV_NO_UNSUPPORTED; CHECK_HEADER("", unsupported); break; case 401: ret = SV_NO_WWW_AUTH; CHECK_HEADER("", www_authenticate); break; } } break; default: strcpy(reason, "invalid message type"); ret = SV_GENERIC_FAILURE; goto failed; } /* check for body */ if (method != METHOD_CANCEL) { if (!msg->unparsed) { strcpy(reason, "invalid parsing"); ret = SV_HDR_PARSE_ERROR; goto failed; } hdrs_len=(unsigned int)(msg->unparsed-msg->buf); if ((hdrs_len+2<=msg->len) && (strncmp(CRLF,msg->unparsed,CRLF_LEN)==0) ) body.s = msg->unparsed + CRLF_LEN; else if ( (hdrs_len+1<=msg->len) && (*(msg->unparsed)=='\n' || *(msg->unparsed)=='\r' ) ) body.s = msg->unparsed + 1; else { /* no body */ body.s = NULL; body.len = 0; } /* determine the length of the body */ body.len = msg->buf + msg->len - body.s; if (get_content_length(msg) != body.len) { snprintf(reason, MAX_REASON-1, "invalid body - content length %ld different than " "actual body %d\n", get_content_length(msg), body.len); ret = SV_INVALID_CONTENT_LENGTH; goto failed; } if (body.len && body.s) { /* if it really has body, check for content type */ ret = SV_NO_CONTENT_TYPE; CHECK_HEADER("", content_type); } } if ((flags & SIP_PARSE_HDR) && sip_validate_hdrs(msg) < 0) { strcpy(reason, "failed to parse headers"); ret = SV_HDR_PARSE_ERROR; goto failed; } return 1; failed: LM_DBG("message does not comply with SIP RFC3261 : (%s)\n", reason); if (pv_result != NULL) { pv_val.rs.len = strlen(reason); pv_val.rs.s = reason; pv_val.flags = PV_VAL_STR; if (pv_set_value(msg, &pv_res->spec, 0, &pv_val) != 0) { LM_ERR("cannot populate parameter\n"); return SV_GENERIC_FAILURE; } } return ret; } #undef CHECK_HEADER /* Change_reply_status config parsing function (supports AVPs) */ static int change_reply_status_fixup(void** param, int param_no) { if(param_no == 1) return fixup_igp(param); if(param_no == 2) return fixup_spve(param); return 0; } /* Function to change the reply status in reply route */ static int change_reply_status_f(struct sip_msg* msg, char* str1, char* str2) { int code_i; str code_s; struct lump *l; char *ch; if(fixup_get_ivalue(msg, (gparam_p)str1, &code_i) < 0) { LM_ERR("Wrong param 1, expected integer\n"); return -1; } if ( fixup_get_svalue(msg, (gparam_p) str2, &code_s) < 0) { LM_ERR("Wrong param 2, expected string\n"); return -1; } if ((code_i < 100) || (code_i > 699)) { LM_ERR("wrong status code: %d\n", code_i); return -1; } if (((code_i < 300) || (msg->REPLY_STATUS < 300)) && (code_i/100 != msg->REPLY_STATUS/100)) { LM_ERR("the class of provisional or positive final replies" " cannot be changed\n"); return -1; } /* rewrite the status code directly in the message buffer */ msg->first_line.u.reply.statuscode = code_i; msg->first_line.u.reply.status.s[2] = code_i % 10 + '0'; code_i /= 10; msg->first_line.u.reply.status.s[1] = code_i % 10 + '0'; code_i /= 10; msg->first_line.u.reply.status.s[0] = code_i + '0'; l = del_lump(msg, msg->first_line.u.reply.reason.s - msg->buf, msg->first_line.u.reply.reason.len, 0); if (!l) { LM_ERR("Failed to add del lump\n"); return -1; } /* clone the reason phrase, the lumps need to be pkg allocated */ ch = (char *)pkg_malloc(code_s.len); if (!ch) { LM_ERR("Not enough memory\n"); return -1; } memcpy(ch, code_s.s, code_s.len); if (insert_new_lump_after(l, ch, code_s.len, 0)==0){ LM_ERR("failed to add new lump: %.*s\n", code_s.len, ch); pkg_free(ch); return -1; } return 1; } opensips-2.2.2/modules/siptrace/000077500000000000000000000000001300170765700166455ustar00rootroot00000000000000opensips-2.2.2/modules/siptrace/Makefile000066400000000000000000000003231300170765700203030ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=siptrace.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/siptrace/README000066400000000000000000000254211300170765700175310ustar00rootroot00000000000000SipTrace Module Daniel-Constantin Mierla Edited by Daniel-Constantin Mierla Copyright © 2006 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. trace_on (integer) 1.3.2. trace_local_ip (str) 1.3.3. trace_id (str) 1.4. Exported Functions 1.4.1. sip_trace(trace_id, [type, [trace_attrs]]) 1.5. Exported MI Functions 1.5.1. sip_trace 1.6. Database setup 1.7. Known issues List of Examples 1.1. Set trace_on parameter 1.2. Set trace_local_ip parameter 1.3. Set trace_id parameter 1.4. sip_trace() usage Chapter 1. Admin Guide 1.1. Overview Offer a possibility to store incoming/outgoing SIP messages in database. Since version 2.2, proto_hep module needs to be loaded in order to duplicate with hep. All hep parameters moved inside proto_hep. The 2.2 version of OpenSIPS came with a major improvement in siptrace module. Now all you have to do is call sip_trace() function with the proper parameters and it will do the job for you. Now you can trace messages, transactions and dialogs with the same function. Also, you can trace to multiple databases, multiple hep destinations and sip destinations using only one parameter. All you need now is defining trace_id parameters in modparam section and switch between them in siptrace function. Also you cand turn tracing on and off using trace_on either globally(for all trace_ids) or for a certain trace_id. IMPORTANT: In 2.2 version support for stateless trace has been removed. The tracing tracing can be turned on/off using fifo command. opensipsctl fifo sip_trace on opensipsctl fifo sip_trace [some_trace_id] on opensipsctl fifo sip_trace off opensipsctl fifo sip_trace [some_trace_id] off 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * database module - mysql, postrgress, dbtext, unixodbc... only if you are using a database type trace id * dialog - only if you want to trace dialogs. * tm - only if you want to trace dialogs/transactions. * proto_hep - only if you want to replicate messages over hep. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. trace_on (integer) Parameter to enable/disable trace (on(1)/off(0)) Default value is "1"(enabled). Example 1.1. Set trace_on parameter ... modparam("siptrace", "trace_on", 1) ... 1.3.2. trace_local_ip (str) The address to be used in the fields that specify the source address (protocol, ip and port) for locally generated messages. If not set, the module sets it to the address of the socket that will be used to send the message. Protocol and/or port are optional and if omitted will take the default values: udp and 5060. Default value is "NULL". Example 1.2. Set trace_local_ip parameter ... #Resulting address: udp:10.1.1.1:5064 modparam("siptrace", "trace_local_ip", "10.1.1.1:5064") ... ... #Resulting address: tcp:10.1.1.1:5060 modparam("siptrace, "trace_local_ip", "tcp:10.1.1.1") ... ... #Resulting address: tcp:10.1.1.1:5064 modparam("siptrace", "trace_local_ip", "tcp:10.1.1.1:5064") ... ... #Resulting address: udp:10.1.1.1:5060 modparam("siptrace", "trace_local_ip", "10.1.1.1") ... 1.3.3. trace_id (str) Specify a destination for the trace. This can be a hep uri, sip uri or a database url and a table. All parameters inside trace_id must be separated by ;. The parameters are given in key-value format, the possible keys being uri for HEP and SIP IDs and uri and table for databases. The format is [id_name]key1=value1;key2=value2;. HEP uris must be specified as hep:host:port. One can declare multiple types of tracing under the same trace id, being identified by their name. So if you define two database url, one hep uri and one sip uri with the same name, when calling sip_trace() with this name tracing shall be done to all the destinations. All the old parameter such as db_url, table and duplicate_uri will form the trace id with the name "default". No default value. If not set the module will be useless. For HEP ids, if transport parameter is not set default will be TCP for version 3 and UDP for 1 and 2. Also if version not set, version 3 will be used by default. Example 1.3. Set trace_id parameter ... /*DB trace id*/ modparam("siptrace", "trace_id", "[tid] uri=mysql://xxxx:xxxx@10.10.10.10/opensips; table=new_sip_trace;") /*hep trace id*/ modparam("siptrace", "trace_id", "[tid]uri=hep:10.10.10.10:6161;") /*sip trace id*/ modparam("siptrace", "trace_id", "[tid]uri=sip:10.10.10.11:5060;") /* notice that they all have the same name * meaning that calling sip_trace("tid",...) * will do sql, sip and hep tracing */ /* hep version 3 with tcp - ALSO DEFAULT ONE */ modparam("siptrace", "trace_id", "[tid]uri=hep:10.10.10.12:5061;transport=tcp;version=3") /* hep version 3 with udp (version not set-default will be 3)*/ modparam("siptrace", "trace_id", "[tid]uri=hep:10.10.10.12:5061;transport=udp") /* hep version 1 */ modparam("siptrace", "trace_id", "[tid]uri=hep:10.10.10.12:5061;version=1") ... 1.4. Exported Functions 1.4.1. sip_trace(trace_id, [type, [trace_attrs]]) Store or replocate current processed SIP message,transaction or dialogin database. It is stored in the form prior applying chages made to it. The traced_user_avp parameter is now an argument to sip_trace() function. Since version 2.2, sip_trace() also catches internally generated replies in stateless mode(sl_send_reply(...)). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE. Meaning of the parameters is as follows: * trace_id (string, pvar) the name of the trace_id specifying where to do the tracing. * type (string) what do you want to trace: dialog, transaction or only the message. If not specified, will try the topmost trace that can be done: if dialog module loaded will trace dialogs, else if tm module loaded will trace transaction and if none of these loaded will trace messages. Types can be the following: + 'm'/'M' trace messages. Is the only one you should use in stateless mode. + 't'/'T' trace transactions. If tm module not loaded, it will be in stateless transaction aware mode meaning that will catch selected requests both in and out and internally generated replies. + 'd'/'D' trace dialog * trace_attrs (string,pvar) this parameter replaces the traced_user_avp from the old version. To avoid duplicating an entry only for this parameter, whatever you put here(string/pvar) shall be stored in the trace_attrs column in the sip_trace table. Example 1.4. sip_trace() usage ... /* see declaration of tid in trace_id section */ $var(trace_id) = "tid"; $var(user) = "osip_user@opensips.org"; ... /* Example 1: how to trace a dialog */ if (has_totag()) { match_dialog(); } else { if (is_method("INVITE") { sip_trace("$var(trace_id)", "d", "$var(user)"); } } ... /* Example 2: how to trace initial INVITE and BYE */ if (has_totag()) { if (is_method("BYE")) { sip_trace("$var(trace_id)", "m", "$var(user)") } } else { if (is_method("INVITE")) { sip_trace("$var(trace_id)", "m", "$var(user)") } } ... /* Example 3: trace initial INVITE transaction */ if (!has_totag()) { if (is_method("INVITE")) { sip_trace("$var(trace_id)", "t", "$var(user)"); } } ... /* Example 4: stateless transaction aware mode!*/ /* tm module must not be loaded */ if (is_method("REGISTER")) { sip_trace("$var(trace_id)", "t", "$var(user)"); if (!www_authorize("", "subscriber")) { /* siptrace will also catch the 401 generated by www_challenge() */ www_challenge("", "1"); } } 1.5. Exported MI Functions 1.5.1. sip_trace Name: sip_trace Parameters: * trace_id/trace_mode : if it is a trace_id name it dumps info about that trace id if the second parameter is not set to on/off or it turns tracing on/off for a certain trace id if it is set, else if it's on/off it turns on/off tracing for all the trace ids. If you turn global trace on but some of the trace ids had trace to off, then they shall not do tracing. In order to do that you have to set the trace_on parameter for each trace_id. Possible values are: + on + off + trace_id name The parameter is optional - if missing, the command will return the status of the SIP message tracing (as string “on†or “offâ€) marked with global and the status for each trace id without changing anything. * trace_mode : this parameter has the same meaning as the trace_mode in the first parameter, but this time it enables/disables tracing for a certain trace id given in the first parameter. MI FIFO Command Format: :sip_trace:_reply_fifo_file_ trace_id/trace_mode trace_mode _empty_line_ 1.6. Database setup Before running OpenSIPS with siptrace, you have to setup the database tables where the module will store the data. For that, if the table were not created by the installation script or you choose to install everything by yourself you can use the siptrace-create.sql SQL script in the database directories in the opensips/scripts folder as template. You can also find the complete database documentation on the project webpage, http://www.opensips.org/html/docs/db/db-schema-devel.html. 1.7. Known issues ACKs related to a transaction that are leaving OpenSIPS are not traced since they are handled statelessly using forward_request function. Fixing it would mean to register a fwdcb callback that would be called for all the messages but would be used only by ACKs, which would be highly ineffective. opensips-2.2.2/modules/siptrace/doc/000077500000000000000000000000001300170765700174125ustar00rootroot00000000000000opensips-2.2.2/modules/siptrace/doc/siptrace.xml000066400000000000000000000021441300170765700217470ustar00rootroot00000000000000 %docentities; ]> SipTrace Module &osipsname; Daniel-Constantin Mierla miconda@gmail.com Daniel-Constantin Mierla miconda@gmail.com 2006 &vsname; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/modules/siptrace/doc/siptrace_admin.xml000066400000000000000000000302001300170765700231110ustar00rootroot00000000000000 &adminguide;
Overview Offer a possibility to store incoming/outgoing SIP messages in database. Since version 2.2, proto_hep module needs to be loaded in order to duplicate with hep. All hep parameters moved inside proto_hep. The 2.2 version of &osips; came with a major improvement in siptrace module. Now all you have to do is call sip_trace() function with the proper parameters and it will do the job for you. Now you can trace messages, transactions and dialogs with the same function. Also, you can trace to multiple databases, multiple hep destinations and sip destinations using only one parameter. All you need now is defining trace_id parameters in modparam section and switch between them in siptrace function. Also you cand turn tracing on and off using trace_on either globally(for all trace_ids) or for a certain trace_id. IMPORTANT: In 2.2 version support for stateless trace has been removed. The tracing tracing can be turned on/off using fifo command. opensipsctl fifo sip_trace on opensipsctl fifo sip_trace [some_trace_id] on opensipsctl fifo sip_trace off opensipsctl fifo sip_trace [some_trace_id] off
Dependencies
&osips; Modules The following modules must be loaded before this module: database module - mysql, postrgress, dbtext, unixodbc... only if you are using a database type trace id dialog - only if you want to trace dialogs. tm - only if you want to trace dialogs/transactions. proto_hep - only if you want to replicate messages over hep.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>trace_on</varname> (integer) Parameter to enable/disable trace (on(1)/off(0)) Default value is "1"(enabled). Set <varname>trace_on</varname> parameter ... modparam("siptrace", "trace_on", 1) ...
<varname>trace_local_ip</varname> (str) The address to be used in the fields that specify the source address (protocol, ip and port) for locally generated messages. If not set, the module sets it to the address of the socket that will be used to send the message. Protocol and/or port are optional and if omitted will take the default values: udp and 5060. Default value is "NULL". Set <varname>trace_local_ip</varname> parameter ... #Resulting address: udp:10.1.1.1:5064 modparam("siptrace", "trace_local_ip", "10.1.1.1:5064") ... ... #Resulting address: tcp:10.1.1.1:5060 modparam("siptrace, "trace_local_ip", "tcp:10.1.1.1") ... ... #Resulting address: tcp:10.1.1.1:5064 modparam("siptrace", "trace_local_ip", "tcp:10.1.1.1:5064") ... ... #Resulting address: udp:10.1.1.1:5060 modparam("siptrace", "trace_local_ip", "10.1.1.1") ...
<varname>trace_id</varname> (str) Specify a destination for the trace. This can be a hep uri, sip uri or a database url and a table. All parameters inside trace_id must be separated by ;. The parameters are given in key-value format, the possible keys being uri for HEP and SIP IDs and uri and table for databases. The format is [id_name]key1=value1;key2=value2;. HEP uris must be specified as hep:host:port. One can declare multiple types of tracing under the same trace id, being identified by their name. So if you define two database url, one hep uri and one sip uri with the same name, when calling sip_trace() with this name tracing shall be done to all the destinations. All the old parameter such as db_url, table and duplicate_uri will form the trace id with the name "default". No default value. If not set the module will be useless. For HEP ids, if transport parameter is not set default will be TCP for version 3 and UDP for 1 and 2. Also if version not set, version 3 will be used by default. Set <varname>trace_id</varname> parameter ... /*DB trace id*/ modparam("siptrace", "trace_id", "[tid] uri=mysql://xxxx:xxxx@10.10.10.10/opensips; table=new_sip_trace;") /*hep trace id*/ modparam("siptrace", "trace_id", "[tid]uri=hep:10.10.10.10:6161;") /*sip trace id*/ modparam("siptrace", "trace_id", "[tid]uri=sip:10.10.10.11:5060;") /* notice that they all have the same name * meaning that calling sip_trace("tid",...) * will do sql, sip and hep tracing */ /* hep version 3 with tcp - ALSO DEFAULT ONE */ modparam("siptrace", "trace_id", "[tid]uri=hep:10.10.10.12:5061;transport=tcp;version=3") /* hep version 3 with udp (version not set-default will be 3)*/ modparam("siptrace", "trace_id", "[tid]uri=hep:10.10.10.12:5061;transport=udp") /* hep version 1 */ modparam("siptrace", "trace_id", "[tid]uri=hep:10.10.10.12:5061;version=1") ...
Exported Functions
<function moreinfo="none">sip_trace(trace_id, [type, [trace_attrs]])</function> Store or replocate current processed SIP message,transaction or dialogin database. It is stored in the form prior applying chages made to it. The traced_user_avp parameter is now an argument to sip_trace() function. Since version 2.2, sip_trace() also catches internally generated replies in stateless mode(sl_send_reply(...)). This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE. Meaning of the parameters is as follows: trace_id (string, pvar) the name of the trace_id specifying where to do the tracing. type (string) what do you want to trace: dialog, transaction or only the message. If not specified, will try the topmost trace that can be done: if dialog module loaded will trace dialogs, else if tm module loaded will trace transaction and if none of these loaded will trace messages. Types can be the following: 'm'/'M' trace messages. Is the only one you should use in stateless mode. 't'/'T' trace transactions. If tm module not loaded, it will be in stateless transaction aware mode meaning that will catch selected requests both in and out and internally generated replies. 'd'/'D' trace dialog trace_attrs (string,pvar) this parameter replaces the traced_user_avp from the old version. To avoid duplicating an entry only for this parameter, whatever you put here(string/pvar) shall be stored in the trace_attrs column in the sip_trace table. <function>sip_trace()</function> usage ... /* see declaration of tid in trace_id section */ $var(trace_id) = "tid"; $var(user) = "osip_user@opensips.org"; ... /* Example 1: how to trace a dialog */ if (has_totag()) { match_dialog(); } else { if (is_method("INVITE") { sip_trace("$var(trace_id)", "d", "$var(user)"); } } ... /* Example 2: how to trace initial INVITE and BYE */ if (has_totag()) { if (is_method("BYE")) { sip_trace("$var(trace_id)", "m", "$var(user)") } } else { if (is_method("INVITE")) { sip_trace("$var(trace_id)", "m", "$var(user)") } } ... /* Example 3: trace initial INVITE transaction */ if (!has_totag()) { if (is_method("INVITE")) { sip_trace("$var(trace_id)", "t", "$var(user)"); } } ... /* Example 4: stateless transaction aware mode!*/ /* tm module must not be loaded */ if (is_method("REGISTER")) { sip_trace("$var(trace_id)", "t", "$var(user)"); if (!www_authorize("", "subscriber")) { /* siptrace will also catch the 401 generated by www_challenge() */ www_challenge("", "1"); } }
Exported MI Functions
<function moreinfo="none">sip_trace</function> Name: sip_trace Parameters: trace_id/trace_mode : if it is a trace_id name it dumps info about that trace id if the second parameter is not set to on/off or it turns tracing on/off for a certain trace id if it is set, else if it's on/off it turns on/off tracing for all the trace ids. If you turn global trace on but some of the trace ids had trace to off, then they shall not do tracing. In order to do that you have to set the trace_on parameter for each trace_id. Possible values are: on off trace_id name The parameter is optional - if missing, the command will return the status of the SIP message tracing (as string on or off) marked with global and the status for each trace id without changing anything. trace_mode : this parameter has the same meaning as the trace_mode in the first parameter, but this time it enables/disables tracing for a certain trace id given in the first parameter. MI FIFO Command Format: :sip_trace:_reply_fifo_file_ trace_id/trace_mode trace_mode _empty_line_
Database setup Before running &osips; with siptrace, you have to setup the database tables where the module will store the data. For that, if the table were not created by the installation script or you choose to install everything by yourself you can use the siptrace-create.sql SQL script in the database directories in the opensips/scripts folder as template. You can also find the complete database documentation on the project webpage, &osipsdbdocslink;.
Known issues ACKs related to a transaction that are leaving &osips; are not traced since they are handled statelessly using forward_request function. Fixing it would mean to register a fwdcb callback that would be called for all the messages but would be used only by ACKs, which would be highly ineffective.
opensips-2.2.2/modules/siptrace/siptrace.c000066400000000000000000001752521300170765700206370ustar00rootroot00000000000000/* * siptrace module - helper module to trace sip messages * * Copyright (C) 2006-2009 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../ut.h" #include "../../ip_addr.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../mi/mi.h" #include "../../db/db.h" #include "../../db/db_insertq.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../../pvar.h" #include "../../sl_cb.h" #include "../../str.h" #include "../../script_cb.h" #include "../tm/tm_load.h" #include "../dialog/dlg_load.h" #include "../proto_hep/hep.h" #include "../proto_hep/hep_cb.h" #include "../../mod_fix.h" #include "siptrace.h" /* DB structures used for all queries */ db_key_t db_keys[NR_KEYS]; db_val_t db_vals[NR_KEYS]; static db_ps_t siptrace_ps = NULL; //static query_list_t *ins_list = NULL; struct tm_binds tmb; struct dlg_binds dlgb; proto_hep_api_t hep_api; load_hep_f load_hep; /* module function prototypes */ static int mod_init(void); static int child_init(int rank); static void destroy(void); static str siptrace_table = str_init("sip_trace"); static str date_column = str_init("time_stamp"); /* 00 */ static str callid_column = str_init("callid"); /* 01 */ static str trace_attrs_column = str_init("trace_attrs"); /* 02 */ static str msg_column = str_init("msg"); /* 03 */ static str method_column = str_init("method"); /* 04 */ static str status_column = str_init("status"); /* 05 */ static str fromproto_column = str_init("from_proto"); /* 06 */ static str fromip_column = str_init("from_ip"); /* 07 */ static str fromport_column = str_init("from_port"); /* 08 */ static str toproto_column = str_init("to_proto"); /* 09 */ static str toip_column = str_init("to_ip"); /* 10 */ static str toport_column = str_init("to_port"); /* 11 */ static str fromtag_column = str_init("fromtag"); /* 12 */ static str direction_column = str_init("direction"); /* 13 */ int trace_on = 1; static int callbacks_registered=0; int *trace_on_flag = NULL; static str trace_local_proto = {NULL, 0}; static str trace_local_ip = {NULL, 0}; static unsigned short trace_local_port = 0; static int sl_ctx_idx=-1; static int default_hep_version=3; static int default_hep_proto=PROTO_HEP_TCP; tlist_elem_p trace_list=NULL; /* * Exported functions */ static cmd_export_t cmds[] = { {"sip_trace", (cmd_function)sip_trace_w, 1, sip_trace_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"sip_trace", (cmd_function)sip_trace_w, 2, sip_trace_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"sip_trace", (cmd_function)sip_trace_w, 3, sip_trace_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"trace_id", STR_PARAM|USE_FUNC_PARAM, parse_trace_id}, {"date_column", STR_PARAM, &date_column.s }, {"callid_column", STR_PARAM, &callid_column.s }, {"trace_attrs_column", STR_PARAM,&trace_attrs_column.s}, {"msg_column", STR_PARAM, &msg_column.s }, {"method_column", STR_PARAM, &method_column.s }, {"status_column", STR_PARAM, &status_column.s }, {"fromproto_column", STR_PARAM, &fromproto_column.s }, {"fromip_column", STR_PARAM, &fromip_column.s }, {"fromport_column", STR_PARAM, &fromport_column.s }, {"toproto_column", STR_PARAM, &toproto_column.s }, {"toip_column", STR_PARAM, &toip_column.s }, {"toport_column", STR_PARAM, &toport_column.s }, {"fromtag_column", STR_PARAM, &fromtag_column.s }, {"direction_column", STR_PARAM, &direction_column.s }, {"trace_on", INT_PARAM, &trace_on }, {"trace_local_ip", STR_PARAM, &trace_local_ip.s }, {0, 0, 0} }; static mi_export_t mi_cmds[] = { { "sip_trace", 0, sip_trace_mi, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; #ifdef STATISTICS #include "../../statistics.h" stat_var* siptrace_req; stat_var* siptrace_rpl; static stat_export_t siptrace_stats[] = { {"traced_requests" , 0, &siptrace_req }, {"traced_replies" , 0, &siptrace_rpl }, {0,0,0} }; #endif static module_dependency_t *get_deps_hep(param_export_t *param) { tlist_elem_p it; for (it=trace_list;it;it=it->next) if (it->type==TYPE_HEP) return alloc_module_dep(MOD_TYPE_DEFAULT, "proto_hep", DEP_ABORT); else if (it->type==TYPE_DB) return alloc_module_dep(MOD_TYPE_SQLDB, NULL, DEP_ABORT); return NULL; } static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ {"trace_id", get_deps_hep}, { NULL, NULL }, }, }; /* module exports */ struct module_exports exports = { "siptrace", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ 0, /* Exported async functions */ params, /* Exported parameters */ #ifdef STATISTICS siptrace_stats, #else 0, /* exported statistics */ #endif mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; static int get_db_struct(str *url, str *tb_name, st_db_struct_t **st_db) { st_db_struct_t *dbs; dbs = pkg_malloc(sizeof(st_db_struct_t)); if (st_db == NULL) { LM_ERR("invalid output parameter!\n"); return -1; } if (url == NULL || url->s == NULL || url->len == 0) { LM_ERR("invalid URL!\n"); return -1; } /* if not set populated with 'siptrace_table' in parse_siptrace_id() */ dbs->table = *tb_name; if (db_bind_mod(url, &dbs->funcs)) { LM_ERR("unable to bind database module\n"); return -1; } if (!DB_CAPABILITY(dbs->funcs, DB_CAP_INSERT)) { LM_ERR("database modules does not provide all functions needed by module\n"); return -1; } if ((dbs->con=dbs->funcs.init(url)) == 0) { LM_CRIT("Cannot connect to DB\n"); return -1; } if (db_check_table_version(&dbs->funcs, dbs->con, &dbs->table, SIPTRACE_TABLE_VERSION) < 0) { LM_ERR("error during table version check.\n"); return -1; } dbs->url = *url; dbs->funcs.close(dbs->con); dbs->con = 0; *st_db = dbs; return 0; } static int parse_siptrace_uri(const str *token, str *uri, str *param1, str* param2) { enum states {ST_TOK_NAME, ST_TOK_VALUE, ST_TOK_END}; enum states state = ST_TOK_NAME; unsigned int p; unsigned int last_equal=0; int _word_start=-1, _word_end=-1; #define HAVE_URI (1<<0) #define HAVE_PARAM1 (1<<1) #define HAVE_PARAM2 (1<<2) unsigned char found_bitmask=0; unsigned char uri_type=TYPE_END; unsigned char no_parsing=0; static str uri_str={"uri", sizeof("uri")-1}; static str tb_name_str={"table", sizeof("table")-1}; static str version_name_str={"version", sizeof("version")-1}; static str transport_name_str={"transport", sizeof("transport")-1}; str name={NULL, 0}, value={NULL, 0}; if (!token) { LM_ERR("bad input parameter!\n"); return -1; } if (!uri || !param1) { LM_ERR("bad output parameter!\n"); return -1; } for (p=0; plen; p++) { switch (token->s[p]){ case '=': if (no_parsing) break; _word_end = _word_end == -1 ? p : _word_end; if (state==ST_TOK_VALUE) { LM_ERR("bad value declaration!parsed until <%.*s>!\n", token->len-p, token->s+p); return -1; } name.s = token->s + _word_start; name.len = _word_end - _word_start; last_equal = p; state=ST_TOK_VALUE; _word_start=_word_end=-1; /* just for databases we need to know what it is before parsing */ if (NULL != q_memchr(&token->s[p+1], '@', token->len - (p + 1))) no_parsing=1; break; case ';': if (no_parsing) break; if (state==ST_TOK_NAME || last_equal == 0) { LM_ERR("bad name declaration!parsed until <%.*s>!\n", token->len-p, token->s+p); return -1; } _word_end = _word_end == -1 ? p : _word_end; value.s = token->s + _word_start;; value.len = _word_end - _word_start; str_trim_spaces_lr(value); if (value.len > 3 && uri_type == TYPE_END) { if (!memcmp(value.s, "hep", 3)) { uri_type = TYPE_HEP; } else if (!memcmp(value.s, "sip", 3)) { uri_type = TYPE_SIP; } else { uri_type = TYPE_DB; } } /* most probably will be found first; will do strcmp only once */ if (!(found_bitmask&HAVE_URI) && !str_strcasecmp(&uri_str, &name)) { found_bitmask |= HAVE_URI; *uri = value; } if (!(found_bitmask&HAVE_PARAM1) && ((uri_type == TYPE_DB && !str_strcasecmp(&tb_name_str, &name)) || (uri_type == TYPE_HEP && !str_strcasecmp(&version_name_str, &name) ) ) ) { found_bitmask |= HAVE_PARAM1; *param1 = value; } if (!(found_bitmask&HAVE_PARAM2) && !str_strcasecmp(&transport_name_str, &name)) { found_bitmask |= HAVE_PARAM2; *param2 = value; } state=ST_TOK_END; _word_start=_word_end=-1; break; case '@': /* continue parsing; passed over database password */ if (no_parsing) { no_parsing=0; } break; case '\n': case '\r': case '\t': case ' ': if (_word_start > 0) { LM_ERR("invalid definition! parsed until <%.*s>\n", token->len-p, &token->s[p]); return -1; } case '(': case ')': case '/': case ':': case '.': case '_': break; default: if (_word_start==-1 && (isalnum(token->s[p])||token->s[p]=='$')) { _word_start = p; } if (no_parsing) break; if (_word_end == -1 && !isalnum(token->s[p])) _word_end = p; if (state==ST_TOK_END) state = ST_TOK_NAME; break; } } if (state != ST_TOK_END) return -1; if (!(found_bitmask&HAVE_URI)) { LM_ERR("URL is MANDATORY!\n"); return -1; } return 0; } static int parse_siptrace_id(str *suri) { #define LIST_SEARCH(__start, __type, __hash) \ do { \ tlist_elem_p __el; \ for (__el=__start; __el; __el=__el->next) { \ if (__el->type==__type &&__el->hash==__hash) \ return 0; \ } \ } while (0); #define ALLOC_EL(__list_el, __lel_size) \ do { \ __list_el = pkg_malloc(__lel_size); \ if (__list_el == NULL) { \ LM_ERR("no more pkg memmory!\n"); \ return -1; \ } \ memset(__list_el, 0, __lel_size); \ } while (0); #define ADD2LIST(__list__, __list_type__, __el__) \ do { \ if (__list__ == NULL) { \ __list__ = __el__; \ break; \ } \ __list_type__ __it = __list__; \ while (__it->next) __it = __it->next; \ __it->next = __el__; \ } while(0); #define PARSE_NAME(__uri, __name) \ do { \ while (__uri->s[0]==' ') \ (__uri->s++, __uri->len--); \ __name.s = __uri->s; \ while (__uri->len \ && (__uri->s[0] != ']' && __uri->s[0] != ' ')) \ (__uri->s++, __uri->len--, __name.len++); \ \ if (*(__uri->s-1) != ']') \ while (__uri->len && __uri->s[0] != ']') \ (__uri->s++, __uri->len--); \ \ if (!__uri->len || __uri->s[0] != ']') { \ LM_ERR("bad name [%.*s]!\n", __uri->len, __uri->s); \ return -1; \ } \ (__uri->s++, __uri->len--); \ } while(0); #define IS_HEP_URI(__url__) ((__url__.len > 3/*O_o*/ \ && (__url__.s[0]|0x20) == 'h' && (__url__.s[1]|0x20) == 'e' \ && (__url__.s[2]|0x20) == 'p')) #define IS_SIP_URI(__url__) ((__url__.len > 3/*O_o*/ \ && (__url__.s[0]|0x20) == 's' && (__url__.s[1]|0x20) == 'i' \ && (__url__.s[2]|0x20) == 'p')) #define IS_UDP(__url__) ((__url__.len == 3/*O_o*/ \ && (__url__.s[0]|0x20) == 'u' && (__url__.s[1]|0x20) == 'd' \ && (__url__.s[2]|0x20) == 'p')) #define IS_TCP(__url__) ((__url__.len == 3/*O_o*/ \ && (__url__.s[0]|0x20) == 't' && (__url__.s[1]|0x20) == 'c' \ && (__url__.s[2]|0x20) == 'p')) unsigned int hash, param_hash; char *new_url; str name={NULL, 0}; str trace_uri; str param1={NULL, 0}, param2={NULL,0}; tlist_elem_p elem; enum types uri_type; unsigned int hep_version; if (suri == NULL) { LM_ERR("bad input parameters!\n"); return -1; } /*format: []uri; it should never have * less than 5 */ if (suri->len < 5) { LM_ERR("suri too short!\n"); return -1; } /* we consider the str trimmed before the function */ if (suri->s[0] != '[') { LM_ERR("bad format for uri {%.*s}\n", suri->len, suri->s); return -1; } else { (suri->s++, suri->len--); \ } PARSE_NAME(suri, name); /*parse '[]'*/ if (parse_siptrace_uri(suri, &trace_uri, ¶m1, ¶m2) < 0) { LM_ERR("invalid uri <%.*s>\n", suri->len, suri->s); return -1; } hash = core_hash(&name, &trace_uri, 0); if (IS_HEP_URI(trace_uri)) { uri_type = TYPE_HEP; if (param1.s && param1.len) { if (param2.s && param2.len) { param_hash = core_hash(¶m1, ¶m2, 0); } else { param_hash = core_hash(¶m1, NULL, 0); } hash^= param_hash; } } else if (IS_SIP_URI(trace_uri)) { uri_type = TYPE_SIP; } else { /* need to take the table into account */ if (param1.s == NULL || param1.len == 0) param1 = siptrace_table; param_hash = core_hash(&trace_uri, ¶m1, 0); hash ^= (param_hash>>3); uri_type = TYPE_DB; } LIST_SEARCH(trace_list, uri_type, hash); if (uri_type == TYPE_HEP) { new_url = pkg_malloc(trace_uri.len); if (new_url == NULL) { LM_ERR("no more pkg mem!\n"); return -1; } memcpy(new_url, "sip", 3); memcpy(new_url+3, trace_uri.s+3, trace_uri.len-3); trace_uri.s = new_url; } ALLOC_EL(elem, sizeof(tlist_elem_t)); elem->type = uri_type; elem->hash = hash; elem->name = name; /* did memset in ALLOC_EL but just to be sure */ elem->next = NULL; if (uri_type == TYPE_DB) { if (get_db_struct(&trace_uri, ¶m1, &elem->el.db) < 0) { LM_ERR("Invalid parameters extracted!url <%.*s>! table name <%.*s>!\n", trace_uri.len, trace_uri.s, param1.len, param1.s); return -1; } } else { if (uri_type == TYPE_HEP) { elem->el.hep = pkg_malloc(sizeof(st_hep_struct_t)); if (elem->el.hep == NULL) { LM_ERR("no more pkg mem!\n"); return -1; } memset(elem->el.hep, 0, sizeof(st_hep_struct_t)); } if (parse_uri(trace_uri.s, trace_uri.len, uri_type == TYPE_SIP ? &elem->el.uri: &elem->el.hep->uri) < 0) { LM_ERR("failed to parse the URI!\n"); return -1; } if (uri_type == TYPE_HEP) { /* version */ if (param1.s && param1.len) { if (str2int(¶m1, &hep_version) < 0) { LM_ERR("invalid hep version parameter!\n"); return -1; } if (hep_version < 1 || hep_version > 3) { LM_ERR("invalid hep protocol version %d\n", hep_version); return -1; } elem->el.hep->version = hep_version; } else { elem->el.hep->version = default_hep_version; } if (param2.s && param2.len) { if (IS_UDP(param2)) { elem->el.hep->transport = PROTO_HEP_UDP; } else if (IS_TCP(param2)) { elem->el.hep->transport = PROTO_HEP_TCP; } else { LM_ERR("Invalid transport protocol [%.*s]\n", param2.len, param2.s); return -1; } } else { if (elem->el.hep->version == 3) elem->el.hep->transport = default_hep_proto; else elem->el.hep->transport = PROTO_HEP_UDP; } /* don't allow TCP for version 1 and 2 */ if ((elem->el.hep->version == 1 || elem->el.hep->version == 2) && elem->el.hep->transport == PROTO_HEP_TCP) { LM_ERR("TCP not allowed for HEPv%d\n", elem->el.hep->version); return -1; } } } ADD2LIST(trace_list, tlist_elem_p, elem); return 0; #undef LIST_SEARCH #undef ALLOC_EL #undef ADD2LIST #undef PARSE_NAME #undef IS_HEP_URI #undef IS_SIP_URI #undef IS_TCP #undef IS_UDP } int parse_trace_id(unsigned int type, void *val) { str suri; suri.s = (char*)val; suri.len = strlen(suri.s); str_trim_spaces_lr(suri); if (parse_siptrace_id(&suri) < 0) { LM_ERR("failed to parse siptrace uri [%.*s]\n", suri.len, suri.s); return -1; } return 0; } static int parse_trace_local_ip(void){ /* We tokenize the trace_local_ip from proto:ip:port to three fields */ trace_local_ip.len = strlen(trace_local_ip.s); unsigned int port_no; char *c = strchr(trace_local_ip.s, ':'); if (c == NULL) { /* Only ip is specified */ trace_local_port = SIP_PORT; trace_local_proto.s = "udp"; trace_local_proto.len = sizeof("udp") -1; } else { str first_token = {c + 1, trace_local_ip.len - (c - trace_local_ip.s) - 1}; if (str2int(&first_token, &port_no) == 0){ /* The first token is the port, so no proto */ if (port_no > 65535 || port_no == 0){ LM_WARN("trace local_ip: port is out of range (%d). " "Will consider it to be %d\n", port_no, SIP_PORT); trace_local_port = SIP_PORT; } else trace_local_port = (unsigned short) port_no; trace_local_proto.s = "udp"; trace_local_proto.len = sizeof("udp") - 1; trace_local_ip.len = c - trace_local_ip.s; } else { /* The first token is the protocol */ trace_local_proto.s = trace_local_ip.s; trace_local_proto.len = c - trace_local_ip.s; if (trace_local_proto.len > 4){ /* Too many letters for the protocol. Avoiding overflow */ LM_ERR("trace_local_ip : wrong protocol\n"); return -1; } else if (trace_local_proto.len == 0){ trace_local_proto.s = "udp"; trace_local_proto.len = sizeof("udp") - 1; } char *c2 = strchr(c + 1, ':'); if (c2 != NULL){ /* We have a second token */ str second_token; second_token.s = c2 + 1; second_token.len = trace_local_ip.len - (c2 - trace_local_ip.s) - 1; if (str2int(&second_token, &port_no) != 0) { trace_local_port = SIP_PORT; LM_WARN("trace_local_ip: port is wrongly defined. " "Will consider it as %hd\n", trace_local_port); } else if (port_no > 65535 || port_no == 0){ LM_WARN("trace local_ip: port is out of range (%d). " "Will consider it to be %d\n", port_no, SIP_PORT); trace_local_port = SIP_PORT; } else trace_local_port = (unsigned short) port_no; trace_local_ip.s = c + 1; trace_local_ip.len = c2 - c - 1; } else { trace_local_port = SIP_PORT; trace_local_ip.len -= c - trace_local_ip.s + 1; trace_local_ip.s = c + 1; } } } return 0; } /* * no fancy stuff just bubble sort; the list will be quite small */ static void do_sort(tlist_elem_p *list_p) { int done=1; tlist_elem_p it, prev, tmp; /* 0 or 1 elems already sorted */ if (*list_p==NULL || (*list_p)->next==NULL) return; do { done=1; prev=NULL; it=*list_p; do { if (it->hash > it->next->hash) { /* need to modify start of the list */ if (!prev) { tmp=it->next; it->next=tmp->next; tmp->next=it; *list_p=tmp; } else { tmp=it->next; prev->next=tmp; it->next=tmp->next; tmp->next=it; } done=0; } prev=it; it=it->next; } while (it && it->next); } while (!done); } static void init_db_cols(void) { #define COL_INIT(_col, _index, _type) \ do { \ db_keys[_index] = &_col##_column; \ db_vals[_index].type = DB_##_type; \ db_vals[_index].nul = 0; \ } while(0); COL_INIT(msg, 0, BLOB); COL_INIT(callid, 1, STR); COL_INIT(method, 2, STR); COL_INIT(status, 3, STR); COL_INIT(fromproto, 4, STR); COL_INIT(fromip, 5, STR); COL_INIT(fromport, 6, INT); COL_INIT(toproto, 7, STR); COL_INIT(toip, 8, STR); COL_INIT(toport, 9, INT); COL_INIT(date, 10, DATETIME); COL_INIT(direction, 11, STRING); COL_INIT(fromtag, 12, STR); COL_INIT(trace_attrs, 13, STR); } static int mod_init(void) { tlist_elem_p it; date_column.len = strlen(date_column.s); callid_column.len = strlen(callid_column.s); trace_attrs_column.len = strlen(trace_attrs_column.s); msg_column.len = strlen(msg_column.s); method_column.len = strlen(method_column.s); status_column.len = strlen(status_column.s); fromproto_column.len = strlen(fromproto_column.s); fromip_column.len = strlen(fromip_column.s); fromport_column.len = strlen(fromport_column.s); toproto_column.len = strlen(toproto_column.s); toip_column.len = strlen(toip_column.s); toport_column.len = strlen(toport_column.s); fromtag_column.len = strlen(fromtag_column.s); direction_column.len = strlen(direction_column.s); if (trace_local_ip.s) parse_trace_local_ip(); LM_INFO("initializing...\n"); trace_on_flag = (int*)shm_malloc(sizeof(int)); if(trace_on_flag==NULL) { LM_ERR("no more shm memory left\n"); return -1; } *trace_on_flag = trace_on; /* initialize hep api */ for (it=trace_list;it;it=it->next) { if (it->type!=TYPE_HEP) continue; LM_DBG("Loading hep api!\n"); load_hep = (load_hep_f)find_export("load_hep", 1, 0); if (!load_hep) { LM_ERR("Can't bind proto hep!\n"); return -1; } if (load_hep(&hep_api)) { LM_ERR("can't bind proto hep\n"); return -1; } break; } /* set db_keys/vals info */ init_db_cols(); /* this will allow using HEP, SIP and DB that * are declared under the same name in the same * sip_trace() call */ for (it=trace_list; it; it=it->next) { it->hash = core_hash(&it->name, NULL, 0); it->traceable=shm_malloc(sizeof(unsigned char)); if (it->traceable==NULL) { LM_ERR("no mre shmem!\n"); return -1; } *it->traceable = trace_on; } /* sort the list */ do_sort(&trace_list); if (trace_list==NULL) { LM_WARN("No trace id defined! The module is useless!\n"); } return 0; } static int child_init(int rank) { tlist_elem_p it; for (it=trace_list; it; it=it->next) { if (it->type == TYPE_DB) { LM_DBG("Initializing trace id [%.*s]\n", it->name.len, it->name.s); it->el.db->con = it->el.db->funcs.init(&it->el.db->url); if (!it->el.db->con) { LM_ERR("Unable to connect to database with url [%.*s]\n", it->el.db->url.len, it->el.db->url.s); return -1; } } } return 0; } static void destroy(void) { tlist_elem_p el, last=NULL; el=trace_list; while (el) { if (last) { shm_free(last->traceable); pkg_free(last); } if (el->type == TYPE_DB) { if (el->el.db->con) { el->el.db->funcs.close(el->el.db->con); } } last=el; el=el->next; } if (last) pkg_free(last); if (trace_on_flag) shm_free(trace_on_flag); } static inline int insert_siptrace(st_db_struct_t *st_db, db_key_t *keys,db_val_t *vals, str *trace_attrs) { if (trace_attrs) { db_vals[13].val.str_val = *trace_attrs; LM_DBG("storing info 14...\n"); } else { db_vals[13].val.str_val.s = ""; db_vals[13].val.str_val.len = 0; } CON_PS_REFERENCE(st_db->con) = &siptrace_ps; if (con_set_inslist(&st_db->funcs,st_db->con, &st_db->ins_list,keys,NR_KEYS) < 0 ) CON_RESET_INSLIST(st_db->con); if(st_db->funcs.insert(st_db->con, keys, vals, NR_KEYS) < 0) { LM_ERR("error storing trace\n"); return -1; } return 0; } static int save_siptrace(struct sip_msg *msg, db_key_t *keys, db_val_t *vals, trace_info_p info) { unsigned int hash; tlist_elem_p it; if (!info || !info->trace_list) { LM_ERR("invalid trace info!\n"); return -1; } if (!(*trace_on_flag)) { LM_DBG("trace is off!\n"); return 0; } hash = info->trace_list->hash; /* check where the hash matches and take the proper action */ for (it=info->trace_list; it && (it->hash == hash); it=it->next) { if (!(*it->traceable)) continue; switch (it->type) { case TYPE_HEP: if (trace_send_hep_duplicate(&db_vals[0].val.blob_val, &db_vals[4].val.str_val, &db_vals[5].val.str_val, db_vals[6].val.int_val, &db_vals[7].val.str_val, &db_vals[8].val.str_val, db_vals[9].val.int_val, it->el.hep) < 0) { LM_ERR("Failed to duplicate with hep to <%.*s:%.*s>\n", it->el.uri.host.len, it->el.uri.host.s, it->el.uri.port.len, it->el.uri.port.s); continue; } break; case TYPE_SIP: if (trace_send_duplicate(db_vals[0].val.blob_val.s, db_vals[0].val.blob_val.len, &it->el.uri) < 0) { LM_ERR("Faield to duplicate with sip to <%.*s:%.*s>\n", it->el.uri.host.len, it->el.uri.host.s, it->el.uri.port.len, it->el.uri.port.s); continue; } break; case TYPE_DB: it->el.db->funcs.use_table(it->el.db->con, &it->el.db->table); if (insert_siptrace(it->el.db, keys, vals, info->trace_attrs) < 0) { LM_ERR("failed to insert in DB!\n"); return -1; } break; default: LM_ERR("invalid type!\n"); return -1; } } return 0; } static void trace_transaction_dlgcb(struct dlg_cell* dlg, int type, struct dlg_cb_params * params) { trace_info_p info; info=*params->param; /* for sl callbacks */ context_put_ptr(CONTEXT_GLOBAL, current_processing_ctx, sl_ctx_idx, info); if (trace_transaction(params->msg, info, 1)<0) { LM_ERR("trace transaction failed!\n"); return; } sip_trace(params->msg, info); } void free_trace_info_pkg(void *param) { pkg_free((trace_info_p)param); } void free_trace_info_shm(void *param) { shm_free((trace_info_p)param); } static int trace_transaction(struct sip_msg* msg, trace_info_p info, char dlg_tran) { if (msg==NULL) return 0; if(tmb.register_tmcb( msg, 0, TMCB_REQUEST_BUILT, trace_onreq_out, info, 0) <=0) { LM_ERR("can't register trace_onreq_out\n"); return -1; } /* allows catching statelessly forwarded ACK in stateful transactions * and stateless replies */ msg->msg_flags |= FL_USE_SIPTRACE; /* doesn't make sense to register the reply callbacks for ACK or PRACK */ if (msg->REQ_METHOD & (METHOD_ACK | METHOD_PRACK)) return 0; if(tmb.register_tmcb( msg, 0, TMCB_RESPONSE_IN, trace_onreply_in, info, 0) <=0) { LM_ERR("can't register trace_onreply_in\n"); return -1; } if(tmb.register_tmcb( msg, 0, TMCB_RESPONSE_OUT, trace_onreply_out, info, dlg_tran?0:free_trace_info_shm) <=0) { LM_ERR("can't register trace_onreply_out\n"); return -1; } return 0; } static int trace_dialog(struct sip_msg *msg, trace_info_p info) { struct dlg_cell* dlg; if (!dlgb.create_dlg || ! dlgb.get_dlg) { LM_ERR("Can't trace dialog!Api not loaded!\n"); return -1; } if (dlgb.create_dlg(msg, 0)<1) { LM_ERR("faield to create dialog!\n"); return -1; } dlg=dlgb.get_dlg(); if (dlg==NULL) { LM_CRIT("BUG: no dialog found after create dialog\n"); return -1; } /* dialog callbacks */ if(dlgb.register_dlgcb(dlg, DLGCB_REQ_WITHIN, trace_transaction_dlgcb,info,0)!=0) { LM_ERR("failed to register dialog callback\n"); return -1; } /* here also free trace info param because we are sure that * this callback is ran only once - when dialog gets for * the first time in DELETED state */ if(dlgb.register_dlgcb(dlg,DLGCB_TERMINATED, trace_transaction_dlgcb,info,free_trace_info_shm)!=0) { LM_ERR("failed to register dialog callback\n"); return -1; } /* also trace this transaction */ if (trace_transaction(msg, info, 1) < 0) { LM_ERR("failed to trace initial INVITE transaction!\n"); return -1; } if ( tmb.register_tmcb( msg, NULL,TMCB_TRANS_CANCELLED, siptrace_dlg_cancel, info, NULL)<0 ) { LM_ERR("failed to register trans cancelled TMCB\n"); return -1; } return 0; } static void siptrace_dlg_cancel(struct cell* t, int type, struct tmcb_params *param) { struct sip_msg *req; req = param->req; LM_DBG("Tracing incoming cancel due to trace_dialog() \n"); /* trace current request */ sip_trace(req, (trace_info_p)(*param->param)); } /* * the topmost flag shall be kept: if both dialog and transaction * flags are set, dialog capture shall be done */ static int st_parse_flags(str *sflags) { int p; int flags=0; for (p=0; plen; p++) { switch(sflags->s[p]) { case 'm': case 'M': if (flags) continue; flags = TRACE_MESSAGE; break; case 't': case 'T': if (flags == TRACE_DIALOG) continue; flags = TRACE_TRANSACTION; break; case 'd': case 'D': flags = TRACE_DIALOG; break; case ' ': continue; default: LM_ERR("invalid character <%c> in" " sip_trace() flags definition", sflags->s[p]); return -1; } } return flags; } static tlist_elem_p get_list_start(str *name) { unsigned int hash; tlist_elem_p it; if (name==NULL) return NULL; hash = core_hash(name, NULL, 0); for (it=trace_list; it; it=it->next) if (hash==it->hash) return it; return NULL; } /* * build a list with only those elements that belong to this * id */ static int sip_trace_fixup(void **param, int param_no) { int _flags; str _sflags; str _trace_attrs; gparam_p gp; pv_elem_p el; tid_param_p tparam; if (param_no < 1 || param_no > 3) { LM_ERR("bad param number!\n"); return -1; } switch (param_no) { case 1: if (fixup_spve(param) < 0) { LM_ERR("trace id fixup failed!\n"); return -1; } tparam=pkg_malloc(sizeof(tid_param_t)); if (!tparam) { LM_ERR("no more pkg mem!\n"); return -1; } gp=*param; if (gp->type==GPARAM_TYPE_STR) { tparam->type = TYPE_LIST; if ((tparam->u.lst=get_list_start(&gp->v.sval))==NULL) { LM_ERR("Trace id <%.*s> not defined!\n", gp->v.sval.len, gp->v.sval.s); return -1; } } else { tparam->type = TYPE_PVAR; tparam->u.el = gp->v.pve; } pkg_free(gp); *param = tparam; break; case 2: _sflags.s = (char *)*param; _sflags.len = strlen(_sflags.s); if ((_flags=st_parse_flags(&_sflags)) < 0) { LM_ERR("flag parsing failed!\n"); return -1; } if (_flags==TRACE_DIALOG) { if (load_dlg_api(&dlgb)!=0) { LM_ERR("Requested dialog trace but dialog module not loaded!\n"); return -1; } } if (_flags==TRACE_TRANSACTION||_flags==TRACE_DIALOG) { if (load_tm_api(&tmb)!=0) { if (_flags==TRACE_DIALOG) { LM_ERR("Requested dialog trace " "but dialog module not loaded!\n"); return -1; } else { LM_INFO("Will do stateless transaction aware tracing!\n"); LM_INFO("Siptrace will catch internally generated replies" " and forwarded requests!\n"); _flags = TRACE_SL_TRANSACTION; } } } /* register callbacks for forwarded messages and internally generated replies */ if (!callbacks_registered && _flags != TRACE_MESSAGE) { /* statelessly forwarded request callback and its context index */ if (register_slcb(SLCB_REQUEST_OUT, FL_USE_SIPTRACE, trace_slreq_out) != 0) { LM_ERR("can't register callback for statelessly forwarded request\n"); return -1; } if (register_slcb(SLCB_REPLY_OUT, FL_USE_SIPTRACE, trace_slreply_out) != 0) { LM_ERR("can't register callback for statelessly forwarded request\n"); return -1; } /* FIXME find a way to pass the flags and the trace_info_p parameter * if there's any*/ #if 0 if (register_slcb(SLCB_ACK_IN, 0, trace_slack_in) != 0) { LM_ERR("can't register callback for statelessly forwarded request\n"); return -1; } #endif /* register the free function only in stateless mode * else tm/dialog will free the structure */ sl_ctx_idx=context_register_ptr(CONTEXT_GLOBAL, _flags==TRACE_SL_TRANSACTION?free_trace_info_pkg:0); /* avoid registering the callbacks mutliple times */ callbacks_registered=1; } *param = (void *)((unsigned long)_flags); break; case 3: _trace_attrs.s = (char *)*param; _trace_attrs.len = strlen(_trace_attrs.s); if (pv_parse_format(&_trace_attrs, &el) < 0) { LM_ERR("Parsing trace attrs param failed!\n"); return -1; } *param = el; break; } return 0; } int trace_has_totag(struct sip_msg* _m) { str tag; if (!_m->to && parse_headers(_m, HDR_TO_F,0)==-1) { LM_ERR("To parsing failed\n"); return 0; } if (!_m->to) { LM_ERR("no To\n"); return 0; } tag=get_to(_m)->tag_value; if (tag.s==0 || tag.len==0) { LM_DBG("no totag\n"); return 0; } LM_DBG("totag found\n"); return 1; } /* siptrace wrapper that verifies if the trace is on */ static int sip_trace_w(struct sip_msg *msg, char *param1, char *param2, char *param3) { int extra_len=0; int trace_flags; str tid_name; str trace_attrs={NULL, 0}; tlist_elem_p list; tid_param_p tparam; trace_info_p info=NULL; trace_info_t stack_info; if(msg==NULL) { LM_DBG("no uas request, local transaction\n"); return -1; } /* NULL trace id; not allowed */ if (param1==NULL) { LM_ERR("Null trace id! This is a mandatory parameter!\n"); return -1; } if ((tparam=(tid_param_p)param1)->type == TYPE_LIST) { list=tparam->u.lst; } else { if (pv_printf_s(msg, tparam->u.el, &tid_name) < 0) { LM_ERR("cannot print trace id PV-formatted string\n"); return -1; } if ((list=get_list_start(&tid_name))==NULL) { LM_ERR("Trace id <%.*s> not defined!\n", tid_name.len, tid_name.s); return -1; } } if (param2 != NULL) { trace_flags = (int)((unsigned long)param2); } else { /* we use the topmost flag; if dialogs available trace dialog etc. */ /* for dialogs check for dialog api and whether it is an initial * INVITE; else we degrade the flag */ if (dlgb.get_dlg && msg->first_line.type == SIP_REQUEST && msg->REQ_METHOD == METHOD_INVITE ) { trace_flags=TRACE_DIALOG; } else if (tmb.t_gett) { trace_flags=TRACE_TRANSACTION; } else { trace_flags=TRACE_SL_TRANSACTION; } } if (trace_flags == TRACE_DIALOG && dlgb.get_dlg && msg->first_line.type == SIP_REQUEST && msg->REQ_METHOD == METHOD_INVITE && !trace_has_totag(msg)) { LM_DBG("tracing dialog!\n"); } else if (trace_flags == TRACE_DIALOG) { LM_DBG("can't trace dialog! Will try to trace transaction\n"); trace_flags = TRACE_TRANSACTION; } if (trace_flags == TRACE_TRANSACTION && tmb.t_gett && msg->first_line.type == SIP_REQUEST && (msg->REQ_METHOD != METHOD_ACK)) { LM_DBG("tracing transaction!\n"); } else if (trace_flags == TRACE_TRANSACTION) { LM_DBG("can't trace transaction! Will trace only this message!\n"); trace_flags = TRACE_MESSAGE; } if (trace_flags == TRACE_SL_TRANSACTION && msg->first_line.type == SIP_REQUEST && (msg->REQ_METHOD != METHOD_ACK)) { LM_DBG("tracing stateless transaction!\n"); } else if (trace_flags == TRACE_SL_TRANSACTION) { LM_DBG("can't trace stateless transaction! " "Will trace only this message!\n"); trace_flags = TRACE_MESSAGE; } if (param3 != NULL) { if (pv_printf_s(msg, (pv_elem_p)param3, &trace_attrs) < 0) { LM_ERR("failed to get trace_attrs param!\n"); return -1; } extra_len = sizeof(str) + trace_attrs.len; } if (trace_flags == TRACE_MESSAGE) { /* we don't need to allocate this structure since it will only be * used in this function's context */ info = &stack_info; memset(info, 0, sizeof(trace_info_t)); if (extra_len) { info->trace_attrs = &trace_attrs; } /* for stateful transactions or dialogs * we need the structure in the shared memory */ } else if(trace_flags == TRACE_DIALOG || trace_flags == TRACE_TRANSACTION) { info=shm_malloc(sizeof(trace_info_t) + extra_len); if (info==NULL) { LM_ERR("no more shm!\n"); return -1; } memset(info, 0, sizeof(trace_info_t) + extra_len); if (extra_len) { info->trace_attrs = (str*)(info+1); info->trace_attrs->s = (char*)(info->trace_attrs+1); memcpy(info->trace_attrs->s, trace_attrs.s, trace_attrs.len); info->trace_attrs->len = trace_attrs.len; } } else if (trace_flags == TRACE_SL_TRANSACTION) { /* we need this structure in pkg for stateless replies * and request out callback */ info=pkg_malloc(sizeof(trace_info_t)); if (info==NULL) { LM_ERR("no more pkg!\n"); return -1; } memset(info, 0, sizeof(trace_info_t)); if (extra_len) info->trace_attrs = &trace_attrs; } info->trace_list=list; if (trace_flags != TRACE_MESSAGE) { context_put_ptr(CONTEXT_GLOBAL, current_processing_ctx, sl_ctx_idx, info); /* this flag here will help catching * stateless replies(sl_send_reply(...))*/ msg->msg_flags |= FL_USE_SIPTRACE; } if (trace_flags==TRACE_DIALOG) { if (trace_dialog(msg, info) < 0) { LM_ERR("trace dialog failed!\n"); return -1; } } else if (trace_flags==TRACE_TRANSACTION) { if (trace_transaction(msg, info, 0) < 0) { LM_ERR("trace transaction failed!\n"); return -1; } } if (sip_trace(msg, info) < 0) { LM_ERR("sip trace failed!\n"); return -1; } return 1; } #define set_sock_columns( _col_proto, _col_ip, _col_port, _buff, _ip, _port, _proto) \ do { \ char *nbuff = proto2str( _proto, _buff); \ _col_proto.val.str_val.s = _buff; \ _col_proto.val.str_val.len = nbuff - _buff; \ strcpy(nbuff, ip_addr2a(_ip)); \ _col_ip.val.str_val.s = nbuff; \ _col_ip.val.str_val.len = strlen(nbuff); \ _col_port.val.int_val = _port; \ } while (0) #define set_columns_to_any( _col_proto, _col_ip, _col_port) \ do { \ _col_proto.val.str_val.s = "any"; \ _col_proto.val.str_val.len = sizeof("any") - 1; \ _col_ip.val.str_val.s = "255.255.255.255"; \ _col_ip.val.str_val.len = sizeof("255.255.255.255") - 1; \ _col_port.val.int_val = 9; \ } while (0) #define set_columns_to_trace_local_ip( _col_proto, _col_ip, _col_port) \ do { \ _col_proto.val.str_val = trace_local_proto; \ _col_ip.val.str_val = trace_local_ip; \ _col_port.val.int_val = trace_local_port; \ } while (0) static int sip_trace(struct sip_msg *msg, trace_info_p info) { static char toip_buff[IP_ADDR_MAX_STR_SIZE+6]; static char fromip_buff[IP_ADDR_MAX_STR_SIZE+6]; if(parse_from_header(msg)==-1 || msg->from==NULL || get_from(msg)==NULL) { LM_ERR("cannot parse FROM header\n"); goto error; } if(parse_headers(msg, HDR_CALLID_F, 0)!=0) { LM_ERR("cannot parse call-id\n"); goto error; } LM_DBG("sip_trace called \n"); db_vals[0].val.blob_val.s = msg->buf; db_vals[0].val.blob_val.len = msg->len; db_vals[1].val.str_val.s = msg->callid->body.s; db_vals[1].val.str_val.len = msg->callid->body.len; if(msg->first_line.type==SIP_REQUEST) { db_vals[2].val.str_val.s = msg->first_line.u.request.method.s; db_vals[2].val.str_val.len = msg->first_line.u.request.method.len; } else { db_vals[2].val.str_val.s = ""; db_vals[2].val.str_val.len = 0; } if(msg->first_line.type==SIP_REPLY) { db_vals[3].val.str_val.s = msg->first_line.u.reply.status.s; db_vals[3].val.str_val.len = msg->first_line.u.reply.status.len; } else { db_vals[3].val.str_val.s = ""; db_vals[3].val.str_val.len = 0; } set_sock_columns( db_vals[4], db_vals[5], db_vals[6], fromip_buff, &msg->rcv.src_ip, msg->rcv.src_port, msg->rcv.proto); set_sock_columns( db_vals[7], db_vals[8], db_vals[9], toip_buff, &msg->rcv.dst_ip, msg->rcv.dst_port, msg->rcv.proto); db_vals[10].val.time_val = time(NULL); db_vals[11].val.string_val = "in"; db_vals[12].val.str_val.s = get_from(msg)->tag_value.s; db_vals[12].val.str_val.len = get_from(msg)->tag_value.len; if (save_siptrace(msg, db_keys,db_vals, info) < 0) { LM_ERR("failed to save siptrace\n"); goto error; } #ifdef STATISTICS if(msg->first_line.type==SIP_REPLY) { update_stat(siptrace_rpl, 1); } else { update_stat(siptrace_req, 1); } #endif return 1; error: return -1; } static void trace_onreq_out(struct cell* t, int type, struct tmcb_params *ps) { if(t==NULL || ps==NULL) { LM_DBG("no uas request, local transaction\n"); return; } if(ps->req==NULL) { LM_DBG("no uas msg, local transaction\n"); return; } LM_DBG("trace on req out \n"); if (ps->extra2) trace_msg_out( ps->req, (str*)ps->extra1, ((struct dest_info*)ps->extra2)->send_sock, ((struct dest_info*)ps->extra2)->proto, &((struct dest_info*)ps->extra2)->to, (trace_info_p)(*ps->param)); else trace_msg_out( ps->req, (str*)ps->extra1, NULL, PROTO_NONE, NULL, (trace_info_p)(*ps->param)); } static void trace_slreq_out(struct sip_msg* req, str *buffer,int rpl_code, union sockaddr_union *to, struct socket_info *sock, int proto) { trace_info_p info; info = context_get_ptr(CONTEXT_GLOBAL, current_processing_ctx, sl_ctx_idx); trace_msg_out(req, buffer, sock, proto, to, info); } static void trace_slreply_out(struct sip_msg* req, str *buffer,int rpl_code, union sockaddr_union *dst, struct socket_info *sock, int proto) { static char fromip_buff[IP_ADDR_MAX_STR_SIZE+12]; static char toip_buff[IP_ADDR_MAX_STR_SIZE+12]; struct ip_addr to_ip; int len; char statusbuf[INT2STR_MAX_LEN]; trace_info_p info; info = context_get_ptr(CONTEXT_GLOBAL, current_processing_ctx, sl_ctx_idx); if (info == NULL) { LM_BUG("null trace info!something is wrong here \n"); return; } if(parse_from_header(req)==-1 || req->from==NULL || get_from(req)==NULL) { LM_ERR("cannot parse FROM header\n"); goto error; } if(parse_headers(req, HDR_CALLID_F, 0)!=0) { LM_ERR("cannot parse call-id\n"); return; } db_vals[0].val.blob_val.s = (buffer)?buffer->s:""; db_vals[0].val.blob_val.len = (buffer)?buffer->len:0; /* check Call-ID header */ if(req->callid==NULL || req->callid->body.s==NULL) { LM_ERR("cannot find Call-ID header!\n"); goto error; } db_vals[1].val.str_val.s = req->callid->body.s; db_vals[1].val.str_val.len = req->callid->body.len; db_vals[2].val.str_val.s = req->first_line.u.request.method.s; db_vals[2].val.str_val.len = req->first_line.u.request.method.len; if(trace_local_ip.s && trace_local_ip.len > 0){ set_columns_to_trace_local_ip( db_vals[4], db_vals[5], db_vals[6]); } else { set_sock_columns( db_vals[4], db_vals[5], db_vals[6], fromip_buff, &req->rcv.dst_ip, req->rcv.dst_port, req->rcv.proto); } char * str_code = int2str(rpl_code, &len); statusbuf[INT2STR_MAX_LEN-1]=0; strncpy(statusbuf, str_code, len >= INT2STR_MAX_LEN ? INT2STR_MAX_LEN-1 : len); db_vals[3].val.str_val.s = statusbuf; db_vals[3].val.str_val.len = len; memset(&to_ip, 0, sizeof(struct ip_addr)); if(dst==0) { set_columns_to_any(db_vals[7], db_vals[8], db_vals[9]); } else { su2ip_addr(&to_ip, dst); set_sock_columns( db_vals[7], db_vals[8],db_vals[9], toip_buff, &to_ip, (unsigned short)su_getport(dst), req->rcv.proto); } db_vals[10].val.time_val = time(NULL); db_vals[11].val.string_val = "out"; db_vals[12].val.str_val.s = get_from(req)->tag_value.s; db_vals[12].val.str_val.len = get_from(req)->tag_value.len; if (save_siptrace(req,db_keys,db_vals, info) < 0) { LM_ERR("failed to save siptrace\n"); goto error; } #ifdef STATISTICS update_stat(siptrace_rpl, 1); #endif return; error: return; } /* FIXME can't get the trace info here */ #if 0 static void trace_slack_in(struct sip_msg* req, str *buffer,int rpl_code, union sockaddr_union *dst, struct socket_info *sock, int proto) { /* FIXME How can we pass the trace info structure here ???? */ // sip_trace(req, NULL); } #endif static void trace_msg_out(struct sip_msg* msg, str *sbuf, struct socket_info* send_sock, int proto, union sockaddr_union *to, trace_info_p info) { static char fromip_buff[IP_ADDR_MAX_STR_SIZE+12]; static char toip_buff[IP_ADDR_MAX_STR_SIZE+12]; struct ip_addr to_ip; if(parse_from_header(msg)==-1 || msg->from==NULL || get_from(msg)==NULL) { LM_ERR("cannot parse FROM header\n"); goto error; } if(parse_headers(msg, HDR_CALLID_F, 0)!=0) { LM_ERR("cannot parse call-id\n"); return; } LM_DBG("trace msg out \n"); if(sbuf!=NULL && sbuf->len>0) { db_vals[0].val.blob_val.s = sbuf->s; db_vals[0].val.blob_val.len = sbuf->len; } else { db_vals[0].val.blob_val.s = "No request buffer"; db_vals[0].val.blob_val.len = sizeof("No request buffer")-1; } /* check Call-ID header */ if(msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot find Call-ID header!\n"); goto error; } db_vals[1].val.str_val.s = msg->callid->body.s; db_vals[1].val.str_val.len = msg->callid->body.len; if(sbuf!=NULL && sbuf->len > 7 && !strncasecmp(sbuf->s, "CANCEL ", 7)) { db_vals[2].val.str_val.s = "CANCEL"; db_vals[2].val.str_val.len = 6; } else { db_vals[2].val.str_val= REQ_LINE(msg).method; } db_vals[3].val.str_val.s = ""; db_vals[3].val.str_val.len = 0; memset(&to_ip, 0, sizeof(struct ip_addr)); if (trace_local_ip.s && trace_local_ip.len > 0){ set_columns_to_trace_local_ip( db_vals[4], db_vals[5], db_vals[6]); } else { if(send_sock==0 || send_sock->sock_str.s==0) { set_sock_columns( db_vals[4], db_vals[5], db_vals[6], fromip_buff, &msg->rcv.dst_ip, msg->rcv.dst_port, msg->rcv.proto); } else { char *nbuff = proto2str(send_sock->proto,fromip_buff); db_vals[4].val.str_val.s = fromip_buff; db_vals[4].val.str_val.len = nbuff - fromip_buff; db_vals[5].val.str_val = send_sock->address_str; db_vals[6].val.int_val = send_sock->port_no; } } if(to==0) { set_columns_to_any(db_vals[7], db_vals[8], db_vals[9]); } else { su2ip_addr(&to_ip, to); set_sock_columns( db_vals[7], db_vals[8], db_vals[9], toip_buff, &to_ip, (unsigned short)su_getport(to), proto); } db_vals[10].val.time_val = time(NULL); db_vals[11].val.string_val = "out"; db_vals[12].val.str_val.s = get_from(msg)->tag_value.s; db_vals[12].val.str_val.len = get_from(msg)->tag_value.len; if (save_siptrace(msg, db_keys,db_vals, info) < 0) { LM_ERR("failed to save siptrace\n"); goto error; } #ifdef STATISTICS update_stat(siptrace_req, 1); #endif return; error: return; } static void trace_onreply_in(struct cell* t, int type, struct tmcb_params *ps) { static char fromip_buff[IP_ADDR_MAX_STR_SIZE+12]; static char toip_buff[IP_ADDR_MAX_STR_SIZE+12]; struct sip_msg* msg; struct sip_msg* req; char statusbuf[INT2STR_MAX_LEN]; int len; if(t==NULL || t->uas.request==0 || ps==NULL) { LM_DBG("no uas request, local transaction\n"); return; } req = ps->req; msg = ps->rpl; if(msg==NULL || req==NULL) { LM_DBG("no reply\n"); return; } LM_DBG("trace onreply in \n"); if(parse_from_header(msg)==-1 || msg->from==NULL || get_from(msg)==NULL) { LM_ERR("cannot parse FROM header\n"); goto error; } if(parse_headers(msg, HDR_CALLID_F, 0)!=0) { LM_ERR("cannot parse call-id\n"); return; } if(msg->len>0) { db_vals[0].val.blob_val.s = msg->buf; db_vals[0].val.blob_val.len = msg->len; } else { db_vals[0].val.blob_val.s = "No reply buffer"; db_vals[0].val.blob_val.len = sizeof("No reply buffer")-1; } /* check Call-ID header */ if(msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot find Call-ID header!\n"); goto error; } db_vals[1].val.str_val.s = msg->callid->body.s; db_vals[1].val.str_val.len = msg->callid->body.len; db_vals[2].val.str_val.s = t->method.s; db_vals[2].val.str_val.len = t->method.len; char * str_code = int2str(ps->code, &len); statusbuf[INT2STR_MAX_LEN-1]=0; strncpy(statusbuf, str_code, len >= INT2STR_MAX_LEN ? INT2STR_MAX_LEN-1 : len); db_vals[3].val.str_val.s = statusbuf; db_vals[3].val.str_val.len = len; set_sock_columns( db_vals[4], db_vals[5], db_vals[6], fromip_buff, &msg->rcv.src_ip, msg->rcv.src_port, msg->rcv.proto); if(trace_local_ip.s && trace_local_ip.len > 0){ set_columns_to_trace_local_ip(db_vals[7], db_vals[8], db_vals[9]); } else { set_sock_columns( db_vals[7], db_vals[8], db_vals[9], toip_buff, &msg->rcv.dst_ip, msg->rcv.dst_port, msg->rcv.proto); } db_vals[10].val.time_val = time(NULL); db_vals[11].val.string_val = "in"; db_vals[12].val.str_val.s = get_from(msg)->tag_value.s; db_vals[12].val.str_val.len = get_from(msg)->tag_value.len; if (save_siptrace(msg, db_keys,db_vals, (trace_info_p)(*ps->param)) < 0) { LM_ERR("failed to save siptrace\n"); goto error; } #ifdef STATISTICS update_stat(siptrace_rpl, 1); #endif return; error: return; } static void trace_onreply_out(struct cell* t, int type, struct tmcb_params *ps) { int faked = 0; static char fromip_buff[IP_ADDR_MAX_STR_SIZE+12]; static char toip_buff[IP_ADDR_MAX_STR_SIZE+12]; struct sip_msg* msg; struct ip_addr to_ip; int len; char statusbuf[8]; str *sbuf; struct dest_info *dst; if (t==NULL || t->uas.request==0 || ps==NULL) { LM_DBG("no uas request, local transaction\n"); return; } LM_DBG("trace onreply out \n"); msg = ps->rpl; if(msg==NULL || msg==FAKED_REPLY) { msg = t->uas.request; faked = 1; } if(parse_from_header(msg)==-1 || msg->from==NULL || get_from(msg)==NULL) { LM_ERR("cannot parse FROM header\n"); goto error; } if(parse_headers(msg, HDR_CALLID_F, 0)!=0) { LM_ERR("cannot parse call-id\n"); return; } sbuf = (str*)ps->extra1; if(faked==0) { if(sbuf!=0 && sbuf->len>0) { db_vals[0].val.blob_val.s = sbuf->s; db_vals[0].val.blob_val.len = sbuf->len; } else if(t->uas.response.buffer.s!=NULL) { db_vals[0].val.blob_val.s = t->uas.response.buffer.s; db_vals[0].val.blob_val.len = t->uas.response.buffer.len; } else if(msg->len>0) { db_vals[0].val.blob_val.s = msg->buf; db_vals[0].val.blob_val.len = msg->len; } else { db_vals[0].val.blob_val.s = "No reply buffer"; db_vals[0].val.blob_val.len = sizeof("No reply buffer")-1; } } else { if(sbuf!=0 && sbuf->len>0) { db_vals[0].val.blob_val.s = sbuf->s; db_vals[0].val.blob_val.len = sbuf->len; } else if(t->uas.response.buffer.s==NULL) { db_vals[0].val.blob_val.s = "No reply buffer"; db_vals[0].val.blob_val.len = sizeof("No reply buffer")-1; } else { db_vals[0].val.blob_val.s = t->uas.response.buffer.s; db_vals[0].val.blob_val.len = t->uas.response.buffer.len; } } /* check Call-ID header */ if(msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot find Call-ID header!\n"); goto error; } db_vals[1].val.str_val.s = msg->callid->body.s; db_vals[1].val.str_val.len = msg->callid->body.len; db_vals[2].val.str_val.s = t->method.s; db_vals[2].val.str_val.len = t->method.len; if(trace_local_ip.s && trace_local_ip.len > 0){ set_columns_to_trace_local_ip(db_vals[4], db_vals[5], db_vals[6]); } else { set_sock_columns( db_vals[4], db_vals[5], db_vals[6], fromip_buff, &msg->rcv.dst_ip, msg->rcv.dst_port, msg->rcv.proto); } strcpy(statusbuf, int2str(ps->code, &len)); db_vals[3].val.str_val.s = statusbuf; db_vals[3].val.str_val.len = len; memset(&to_ip, 0, sizeof(struct ip_addr)); dst = (struct dest_info*)ps->extra2; if(dst==0) { set_columns_to_any( db_vals[7], db_vals[8], db_vals[9]); } else { su2ip_addr(&to_ip, &dst->to); set_sock_columns( db_vals[7], db_vals[8], db_vals[9], toip_buff, &to_ip, (unsigned long)su_getport(&dst->to), dst->proto); } db_vals[10].val.time_val = time(NULL); db_vals[11].val.string_val = "out"; db_vals[12].val.str_val.s = get_from(msg)->tag_value.s; db_vals[12].val.str_val.len = get_from(msg)->tag_value.len; if (save_siptrace(msg, db_keys,db_vals, (trace_info_p)(*ps->param)) < 0) { LM_ERR("failed to save siptrace\n"); goto error; } #ifdef STATISTICS update_stat(siptrace_rpl, 1); #endif return; error: return; } /** * MI command format: * name: sip_trace * attribute: name=none, value=[on|off] */ static struct mi_root* sip_trace_mi(struct mi_root* cmd_tree, void* param ) { #define TID_INFO(_tid_el, _node, _rpl) \ do { \ char uri[256]; \ _node=add_mi_node_child(_rpl, 0, _tid_el->name.s, \ _tid_el->name.len, 0, 0); \ if (_tid_el->type==TYPE_HEP) \ add_mi_attr(_node, 0, MI_SSTR("type"), MI_SSTR("HEP")); \ else if (_tid_el->type==TYPE_SIP) \ add_mi_attr(_node, 0, MI_SSTR("type"), MI_SSTR("SIP")); \ else if (_tid_el->type==TYPE_DB) \ add_mi_attr(_node, 0, MI_SSTR("type"), MI_SSTR("Database")); \ \ if (_tid_el->type==TYPE_HEP) { \ memcpy(uri, _tid_el->el.hep->uri.host.s, _tid_el->el.hep->uri.host.len); \ uri[_tid_el->el.hep->uri.host.len] = ':'; \ memcpy(uri+_tid_el->el.hep->uri.host.len+1, \ _tid_el->el.hep->uri.port.s, _tid_el->el.hep->uri.port.len); \ \ add_mi_attr(_node, 0, MI_SSTR("uri"), uri, \ _tid_el->el.hep->uri.host.len + 1 + _tid_el->el.hep->uri.port.len); \ } else if (_tid_el->type==TYPE_SIP) { \ memcpy(uri, _tid_el->el.uri.host.s, _tid_el->el.uri.host.len); \ uri[_tid_el->el.uri.host.len] = ':'; \ memcpy(uri+_tid_el->el.uri.host.len+1, \ _tid_el->el.uri.port.s, _tid_el->el.uri.port.len); \ \ add_mi_attr(_node, 0, MI_SSTR("uri"), uri, \ _tid_el->el.uri.host.len + 1 + _tid_el->el.uri.port.len); \ } else { \ /* TYPE_DB */ \ add_mi_attr(_node, 0, MI_SSTR("uri"), _tid_el->el.db->url.s, \ _tid_el->el.db->url.len); \ } \ \ if (*_tid_el->traceable) \ add_mi_attr(_node, 0, MI_SSTR("state"), MI_SSTR("on")); \ else \ add_mi_attr(_node, 0, MI_SSTR("state"), MI_SSTR("off")); \ } while(0); struct mi_node* node; struct mi_node *rpl; struct mi_root *rpl_tree ; tlist_elem_p it; unsigned int hash; unsigned int tid_trace_flag=1; node = cmd_tree->node.kids; if(node == NULL) { /* display name, type and state for all ids */ rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree == 0) return 0; rpl = &rpl_tree->node; if (*trace_on_flag == 0 ) { node = add_mi_node_child(rpl,0,MI_SSTR("global"),MI_SSTR("off")); } else if (*trace_on_flag == 1) { node = add_mi_node_child(rpl,0,MI_SSTR("global"),MI_SSTR("on")); } for (it=trace_list;it;it=it->next) TID_INFO(it, node, rpl); return rpl_tree ; } else { if(trace_on_flag==NULL) return init_mi_tree( 500, MI_SSTR(MI_INTERNAL_ERR)); /* global or trace_id name */ if (node && !node->next) { /* if on/off set global accordingly * else display trace_id info */ if ( node->value.len==2 && (node->value.s[0]=='o'|| node->value.s[0]=='O') && (node->value.s[1]=='n'|| node->value.s[1]=='N')) { *trace_on_flag = 1; return init_mi_tree( 200, MI_SSTR(MI_OK)); } else if ( node->value.len==3 && (node->value.s[0]=='o'|| node->value.s[0]=='O') && (node->value.s[1]=='f'|| node->value.s[1]=='F') && (node->value.s[2]=='f'|| node->value.s[2]=='F')) { *trace_on_flag = 0; return init_mi_tree( 200, MI_SSTR(MI_OK)); } /* display trace id content here */ rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree == 0) return 0; rpl = &rpl_tree->node; it=get_list_start(&node->value); hash=it->hash; for (;it&&it->hash==hash;it=it->next) TID_INFO(it, node, rpl); return rpl_tree; } else if (node && node->next && !node->next->next) { /* trace on off for an id */ if ( node->next->value.len==2 && (node->next->value.s[0]=='o'|| node->next->value.s[0]=='O') && (node->next->value.s[1]=='n'|| node->next->value.s[1]=='N')) { tid_trace_flag=1; } else if ( node->next->value.len==3 && (node->next->value.s[0]=='o'|| node->next->value.s[0]=='O') && (node->next->value.s[1]=='f'|| node->next->value.s[1]=='F') && (node->next->value.s[2]=='f'|| node->next->value.s[2]=='F')) { tid_trace_flag=0; } else { return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM)); } it=get_list_start(&node->value); hash=it->hash; for (;it&&it->hash==hash;it=it->next) *it->traceable=tid_trace_flag; return init_mi_tree(200, MI_SSTR(MI_OK)); } /* error here */ return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM)); } return NULL; } static int trace_send_duplicate(char *buf, int len, struct sip_uri *uri) { union sockaddr_union* to; struct socket_info* send_sock; struct proxy_l * p; int proto; int ret; if(buf==NULL || len <= 0) return -1; if(uri==NULL) return 0; to=(union sockaddr_union*)pkg_malloc(sizeof(union sockaddr_union)); if (to==0){ LM_ERR("out of pkg memory\n"); return -1; } /* create a temporary proxy*/ proto = PROTO_UDP; p=mk_proxy(&uri->host, (uri->port_no)?uri->port_no:SIP_PORT, proto, 0); if (p==0){ LM_ERR("bad host name in uri\n"); pkg_free(to); return -1; } hostent2su(to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT); ret = -1; do { send_sock=get_send_socket(0, to, proto); if (send_sock==0){ LM_ERR("can't forward to af %d, proto %d no corresponding listening socket\n", to->s.sa_family,proto); continue; } if (msg_send(send_sock, proto, to, 0, buf, len, NULL)<0){ LM_ERR("cannot send duplicate message\n"); continue; } ret = 0; break; }while( get_next_su( p, to, 0)==0 ); free_proxy(p); /* frees only p content, not p itself */ pkg_free(p); pkg_free(to); return ret; } static int trace_send_hep_duplicate(str *body, str *fromproto, str *fromip, unsigned short fromport, str *toproto, str *toip, unsigned short toport, st_hep_struct_t* hep) { struct proxy_l * p=NULL /* make gcc happy */; int ret; union sockaddr_union from_su; union sockaddr_union to_su; unsigned int proto; union sockaddr_union* to = NULL; int heplen; char *hepbuf; if(body->s==NULL || body->len <= 0) return -1; /* Convert proto:ip:port to sockaddress union SRC IP */ /* proto is going to be converted to netinet proto */ if (pipport2su(fromproto, fromip, fromport, &from_su, &proto)==-1 || (pipport2su(toproto, toip, toport, &to_su, &proto)==-1)) goto error; /* check if from and to are in the same family*/ if(from_su.s.sa_family != to_su.s.sa_family) { LM_ERR("ERROR: trace_send_hep_duplicate: interworking detected ?\n"); goto error; } /* create a temporary proxy*/ p=mk_proxy(&hep->uri.host, (hep->uri.port_no)?hep->uri.port_no:SIP_PORT,proto, 0); if (p==0){ LM_ERR("bad host name in uri\n"); return -1; } to=(union sockaddr_union*)pkg_malloc(sizeof(union sockaddr_union)); if (to==0){ LM_ERR("out of pkg memory\n"); return -1; } hostent2su(to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT); if (hep_api.pack_hep(&from_su, &to_su, proto, body->s, body->len, hep->version, &hepbuf, &heplen)) { LM_ERR("failed to do hep packing\n"); return -1; } ret = -1; do { /* send sock is being found in msg_send() function*/ if (msg_send(NULL, hep->transport, to, 0, hepbuf, heplen, NULL)<0){ LM_ERR("cannot send duplicate message\n"); continue; } ret = 0; break; }while( get_next_su( p, to, 0)==0 ); free_proxy(p); /* frees only p content, not p itself */ pkg_free(p); pkg_free(to); pkg_free(hepbuf); return ret; error: if(p) { free_proxy(p); /* frees only p content, not p itself */ pkg_free(p); } if(to) pkg_free(to); return -1; } /*! * \brief Convert a STR [proto:]ip[:port] into socket address. * [proto:]ip[:port] * \param pipport (udp:127.0.0.1:5060 or tcp:2001:0DB8:AC10:FE01:5060) * \param tmp_su target structure * \param proto uint protocol type * \return success / unsuccess */ static int pipport2su (str *sproto, str *ip, unsigned short port, union sockaddr_union *tmp_su, unsigned int *proto) { struct ip_addr *ip_a; str host_uri; /*parse protocol */ if(strncmp(sproto->s, "udp",3) == 0) *proto = IPPROTO_UDP; else if(strncmp(sproto->s, "tcp",3) == 0) *proto = IPPROTO_TCP; else if(strncmp(sproto->s, "tls",3) == 0) *proto = IPPROTO_IDP; /* fake proto type */ else if(strncmp(sproto->s, "sctp",4) == 0) *proto = IPPROTO_SCTP; else if(strncmp(sproto->s, "any",3) == 0) *proto = IPPROTO_UDP; else if(strncmp(sproto->s, "ws",2) == 0) *proto = IPPROTO_ESP; /* fake proto type */ else { LM_ERR("bad protocol %.*s\n", sproto->len, sproto->s); return -1; } /*check if ip is not null*/ if (ip->len == 0) { LM_ERR("malformed ip address\n"); return -1; } if (port == 0) { port = SIP_PORT; } else{ /*the address contains a port number*/ if (port<1024 || port>65535) { LM_ERR("invalid port number; must be in [1024,65536]\n"); return -1; } } LM_DBG("proto %d, host %.*s , port %d \n",*proto, ip->len, ip->s, port); /* now IPv6 address has no brakets. It should be fixed! */ host_uri = *ip; if (host_uri.s[0] == '[') { if(host_uri.s[host_uri.len-1] != ']') { LM_ERR("bracket not closed\n"); return -1; } host_uri.s++; host_uri.len -= 2; } /* check if it's an ip address */ if (((ip_a = str2ip(&host_uri)) != 0) || ((ip_a = str2ip6 (&host_uri)) != 0) ) { ip_addr2su(tmp_su, ip_a, ntohs(port)); return 0; } LM_ERR("host <%.*s> is not an IP\n",host_uri.len,host_uri.s); return -1; } opensips-2.2.2/modules/siptrace/siptrace.h000066400000000000000000000077071300170765700206430ustar00rootroot00000000000000/* * siptrace module - helper module to trace sip messages * * Copyright (C) 2006-2009 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _SIPTRACE_H #define _SIPTRACE_H #define NR_KEYS 14 #define SIPTRACE_TABLE_VERSION 5 enum trace_flags {TRACE_MESSAGE=(1<<0), TRACE_TRANSACTION=(1<<1), TRACE_SL_TRANSACTION=(1<<2), /* transaction aware in stateless mode */ TRACE_DIALOG=(1<<3)}; typedef struct st_db_struct { str url; db_con_t *con; db_func_t funcs; query_list_t *ins_list; str table; } st_db_struct_t; typedef struct st_hep_struct { struct sip_uri uri; char version; int transport; } st_hep_struct_t; enum types { TYPE_HEP=0, TYPE_SIP, TYPE_DB, TYPE_END }; typedef struct tlist_elem { str name; /* name of the partition */ enum types type; /* SIP-DB-HEP */ unsigned int hash; /* hash over the uri*/ unsigned char *traceable; /* whether or not this idd is traceable */ union { st_db_struct_t *db; st_hep_struct_t *hep; struct sip_uri uri; } el; struct tlist_elem *next; } tlist_elem_t, *tlist_elem_p; enum tid_types {TYPE_LIST=0, TYPE_PVAR}; typedef struct tid_param { enum tid_types type; union { tlist_elem_p lst; pv_elem_p el; } u; } tid_param_t, *tid_param_p; typedef struct trace_info { str *trace_attrs; tlist_elem_p trace_list; } trace_info_t, *trace_info_p; static int sip_trace_fixup(void **param, int param_no); static int sip_trace_w(struct sip_msg*, char*, char*, char*); static int sip_trace(struct sip_msg*, trace_info_p); static int trace_dialog(struct sip_msg*, trace_info_p); static int trace_transaction(struct sip_msg* msg, trace_info_p info, char dlg_tran); static void trace_onreq_out(struct cell* t, int type, struct tmcb_params *ps); static void trace_onreply_in(struct cell* t, int type, struct tmcb_params *ps); static void trace_onreply_out(struct cell* t, int type, struct tmcb_params *ps); static void trace_msg_out(struct sip_msg* req, str *buffer, struct socket_info* send_sock, int proto, union sockaddr_union *to, trace_info_p info); static void siptrace_dlg_cancel(struct cell* t, int type, struct tmcb_params *param); /* * callback used for statelessly forwarded requests; also catches the ACK in * stateful transaction */ static void trace_slreq_out(struct sip_msg* req, str *buffer,int rpl_code, union sockaddr_union *to, struct socket_info *sock, int proto); static void trace_slreply_out(struct sip_msg* req, str *buffer,int rpl_code, union sockaddr_union *dst, struct socket_info *sock, int proto); #if 0 static void trace_slack_in(struct sip_msg* req, str *buffer,int rpl_code, union sockaddr_union *dst, struct socket_info *sock, int proto); #endif static struct mi_root* sip_trace_mi(struct mi_root* cmd, void* param ); static int trace_send_duplicate(char *buf, int len, struct sip_uri *uri); static int trace_send_hep_duplicate(str *body, str *fromproto, str *fromip, unsigned short fromport, str *toproto, str *toip, unsigned short toport, st_hep_struct_t* hep); static int pipport2su (str *sproto, str *ip, unsigned short port, union sockaddr_union *tmp_su, unsigned int *proto); static int parse_trace_id(unsigned int type, void *val); void free_trace_info_pkg(void *param); void free_trace_info_shm(void *param); #endif opensips-2.2.2/modules/sl/000077500000000000000000000000001300170765700154515ustar00rootroot00000000000000opensips-2.2.2/modules/sl/Makefile000066400000000000000000000003161300170765700171110ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=sl.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/sl/README000066400000000000000000000111321300170765700163270ustar00rootroot00000000000000sl Module Bogdan Iancu Edited by Bogdan Iancu Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. enable_stats (integer) 1.4. Exported Functions 1.4.1. sl_send_reply(code, reason) 1.4.2. sl_reply_error() 1.5. Exported Statistics 1.5.1. 1xx_replies 1.5.2. 2xx_replies 1.5.3. 3xx_replies 1.5.4. 4xx_replies 1.5.5. 5xx_replies 1.5.6. 6xx_replies 1.5.7. sent_replies 1.5.8. sent_err_replies 1.5.9. received_ACKs List of Examples 1.1. enable_stats example 1.2. sl_send_reply usage 1.3. sl_reply_error usage Chapter 1. Admin Guide 1.1. Overview The SL module allows OpenSIPS to act as a stateless UA server and generate replies to SIP requests without keeping state. That is beneficial in many scenarios, in which you wish not to burden server's memory and scale well. The SL module needs to filter ACKs sent after a local stateless reply to an INVITE was generated. To recognize such ACKs, OpenSIPS adds a special "signature" in to-tags. This signature is sought for in incoming ACKs, and if included, the ACKs are absorbed. To speed up the filtering process, the module uses a timeout mechanism. When a reply is sent, a timer is set. As time as the timeout didn't hit, the incoming ACK requests will be checked using TO tag value. Once the timer expires, all the ACK are let through - a long time passed till it sent a reply, so it does not expect any ACK that have to be blocked. The ACK filtering may fail in some rare cases. If you think these matter to you, better use stateful processing (tm module) for INVITE processing. Particularly, the problem happens when a UA sends an INVITE which already has a to-tag in it (e.g., a re-INVITE) and OpenSIPS want to reply to it. Than, it will keep the current to-tag, which will be mirrored in ACK. OpenSIPS will not see its signature and forward the ACK downstream. Caused harm is not bad--just a useless ACK is forwarded. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. enable_stats (integer) If the module should generate and export statistics to the core manager. A zero value means disabled. SL module provides statistics about how many replies were sent ( splitted per code classes) and how many local ACKs were filtered out. Default value is 1 (enabled). Example 1.1. enable_stats example modparam("sl", "enable_stats", 0) 1.4. Exported Functions 1.4.1. sl_send_reply(code, reason) For the current request, a reply is sent back having the given code and text reason. The reply is sent stateless, totally independent of the Transaction module and with no retransmission for the INVITE's replies. 'code' and 'reason' can contain pseudo-variables that are replaced at runtime. Meaning of the parameters is as follows: * code - Return code. * reason - Reason phrase. This function can be used from REQUEST_ROUTE, ERROR_ROUTE. Example 1.2. sl_send_reply usage ... sl_send_reply("404", "Not found"); ... sl_send_reply("$err.rcode", "$err.rreason"); ... 1.4.2. sl_reply_error() Sends back an error reply describing the nature of the last internal error. Usually this function should be used after a script function that returned an error code. This function can be used from REQUEST_ROUTE. Example 1.3. sl_reply_error usage ... sl_reply_error(); ... 1.5. Exported Statistics 1.5.1. 1xx_replies The number of 1xx_replies. 1.5.2. 2xx_replies The number of 2xx_replies. 1.5.3. 3xx_replies The number of 3xx_replies. 1.5.4. 4xx_replies The number of 4xx_replies. 1.5.5. 5xx_replies The number of 5xx_replies. 1.5.6. 6xx_replies The number of 6xx_replies. 1.5.7. sent_replies The number of sent_replies. 1.5.8. sent_err_replies The number of sent_err_replies. 1.5.9. received_ACKs The number of received_ACKs. opensips-2.2.2/modules/sl/doc/000077500000000000000000000000001300170765700162165ustar00rootroot00000000000000opensips-2.2.2/modules/sl/doc/sl.xml000066400000000000000000000021121300170765700173520ustar00rootroot00000000000000 %docentities; ]> sl Module &osipsname; Bogdan Iancu
bogdan@opensips.org
Bogdan Iancu
bogdan@opensips.org
2003 &fhg; $Revision: 8740 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/sl/doc/sl_admin.xml000066400000000000000000000130571300170765700205340ustar00rootroot00000000000000 &adminguide;
Overview The SL module allows &osips; to act as a stateless &ua; server and generate replies to &sip; requests without keeping state. That is beneficial in many scenarios, in which you wish not to burden server's memory and scale well. The SL module needs to filter ACKs sent after a local stateless reply to an INVITE was generated. To recognize such ACKs, &osips; adds a special "signature" in to-tags. This signature is sought for in incoming ACKs, and if included, the ACKs are absorbed. To speed up the filtering process, the module uses a timeout mechanism. When a reply is sent, a timer is set. As time as the timeout didn't hit, the incoming ACK requests will be checked using TO tag value. Once the timer expires, all the ACK are let through - a long time passed till it sent a reply, so it does not expect any ACK that have to be blocked. The ACK filtering may fail in some rare cases. If you think these matter to you, better use stateful processing (tm module) for INVITE processing. Particularly, the problem happens when a UA sends an INVITE which already has a to-tag in it (e.g., a re-INVITE) and &osips; want to reply to it. Than, it will keep the current to-tag, which will be mirrored in ACK. &osips; will not see its signature and forward the ACK downstream. Caused harm is not bad--just a useless ACK is forwarded.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>enable_stats</varname> (integer) If the module should generate and export statistics to the core manager. A zero value means disabled. SL module provides statistics about how many replies were sent ( splitted per code classes) and how many local ACKs were filtered out. Default value is 1 (enabled). enable_stats example modparam("sl", "enable_stats", 0)
Exported Functions
<function moreinfo="none">sl_send_reply(code, reason)</function> For the current request, a reply is sent back having the given code and text reason. The reply is sent stateless, totally independent of the Transaction module and with no retransmission for the INVITE's replies. 'code' and 'reason' can contain pseudo-variables that are replaced at runtime. Meaning of the parameters is as follows: code - Return code. reason - Reason phrase. This function can be used from REQUEST_ROUTE, ERROR_ROUTE. <function>sl_send_reply</function> usage ... sl_send_reply("404", "Not found"); ... sl_send_reply("$err.rcode", "$err.rreason"); ...
<function moreinfo="none">sl_reply_error()</function> Sends back an error reply describing the nature of the last internal error. Usually this function should be used after a script function that returned an error code. This function can be used from REQUEST_ROUTE. <function>sl_reply_error</function> usage ... sl_reply_error(); ...
Exported Statistics
<varname>1xx_replies</varname> The number of 1xx_replies.
<varname>2xx_replies</varname> The number of 2xx_replies.
<varname>3xx_replies</varname> The number of 3xx_replies.
<varname>4xx_replies</varname> The number of 4xx_replies.
<varname>5xx_replies</varname> The number of 5xx_replies.
<varname>6xx_replies</varname> The number of 6xx_replies.
<varname>sent_replies</varname> The number of sent_replies.
<varname>sent_err_replies</varname> The number of sent_err_replies.
<varname>received_ACKs</varname> The number of received_ACKs.
opensips-2.2.2/modules/sl/sl.c000066400000000000000000000136331300170765700162410ustar00rootroot00000000000000/* * sl module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free * 2005-03-01 force for stateless replies the incoming interface of * the request (bogdan) * 2006-03-29 callbacks for sending replies added (bogdan) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../script_cb.h" #include "../../mem/mem.h" #include "../../pvar.h" #include "sl_funcs.h" #include "sl_api.h" static int w_sl_send_reply(struct sip_msg* msg, char* str1, char* str2); static int w_sl_reply_error(struct sip_msg* msg, char* str1, char* str2); static int fixup_sl_send_reply(void** param, int param_no); static int mod_init(void); static void mod_destroy(void); /* module parameter */ int sl_enable_stats = 1; /* statistic variables */ stat_var *tx_1xx_rpls; stat_var *tx_2xx_rpls; stat_var *tx_3xx_rpls; stat_var *tx_4xx_rpls; stat_var *tx_5xx_rpls; stat_var *tx_6xx_rpls; stat_var *sent_rpls; stat_var *sent_err_rpls; stat_var *rcv_acks; static cmd_export_t cmds[]={ {"sl_send_reply", (cmd_function)w_sl_send_reply, 2, fixup_sl_send_reply, 0, REQUEST_ROUTE | ERROR_ROUTE }, {"sl_reply_error",(cmd_function)w_sl_reply_error,0, 0, 0, REQUEST_ROUTE}, {"load_sl", (cmd_function)load_sl, 0, 0, 0, 0}, {0,0,0,0,0,0} }; static param_export_t mod_params[]={ { "enable_stats", INT_PARAM, &sl_enable_stats }, { 0,0,0 } }; static stat_export_t mod_stats[] = { {"1xx_replies" , 0, &tx_1xx_rpls }, {"2xx_replies" , 0, &tx_2xx_rpls }, {"3xx_replies" , 0, &tx_3xx_rpls }, {"4xx_replies" , 0, &tx_4xx_rpls }, {"5xx_replies" , 0, &tx_5xx_rpls }, {"6xx_replies" , 0, &tx_6xx_rpls }, {"sent_replies" , 0, &sent_rpls }, {"sent_err_replies" , 0, &sent_err_rpls }, {"received_ACKs" , 0, &rcv_acks }, {0,0,0} }; #ifdef STATIC_SL struct module_exports sl_exports = { #else struct module_exports exports= { #endif "sl", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ NULL, /* exported async functions */ mod_params, /* param exports */ mod_stats, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, 0 /* per-child init function */ }; static int mod_init(void) { LM_INFO("Initializing StateLess engine\n"); /* if statistics are disabled, prevent their registration to core */ if (sl_enable_stats==0) #ifdef STATIC_SL sl_exports.stats = 0; #else exports.stats = 0; #endif /* filter all ACKs before script */ if (register_script_cb(sl_filter_ACK, PRE_SCRIPT_CB|REQ_TYPE_CB, 0 )!=0) { LM_ERR("register_script_cb failed\n"); return -1; } /* init internal SL stuff */ if (sl_startup()!=0) { LM_ERR("sl_startup failed\n"); return -1; } return 0; } static void mod_destroy(void) { sl_shutdown(); } static int fixup_sl_send_reply(void** param, int param_no) { pv_elem_t *model=NULL; str s; /* convert to str */ s.s = (char*)*param; s.len = strlen(s.s); model=NULL; if (param_no==1 || param_no==2) { if(s.len==0) { LM_ERR("no param %d!\n", param_no); return E_UNSPEC; } if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for param no %d!\n", s.s, param_no); return E_UNSPEC; } if(model->spec.getf==NULL) { if(param_no==1) { if(str2int(&s, (unsigned int*)&model->spec.pvp.pvn.u.isname.name.n)!=0 || model->spec.pvp.pvn.u.isname.name.n<100 || model->spec.pvp.pvn.u.isname.name.n>699) { LM_ERR("wrong value [%s] for param no %d!\n", s.s, param_no); LM_ERR("allowed values: 1xx - 6xx only!\n"); return E_UNSPEC; } } } *param = (void*)model; } return 0; } static int w_sl_reply_error( struct sip_msg* msg, char* str1, char* str2) { return sl_reply_error( msg ); } static int w_sl_send_reply(struct sip_msg* msg, char* str1, char* str2) { str code_s; unsigned int code_i; if(((pv_elem_p)str1)->spec.getf!=NULL) { if(pv_printf_s(msg, (pv_elem_p)str1, &code_s)!=0) return -1; if(str2int(&code_s, &code_i)!=0 || code_i<100 || code_i>699) return -1; } else { code_i = ((pv_elem_p)str1)->spec.pvp.pvn.u.isname.name.n; } if(((pv_elem_p)str2)->spec.getf!=NULL) { if(pv_printf_s(msg, (pv_elem_p)str2, &code_s)!=0 || code_s.len <=0) return -1; } else { code_s = ((pv_elem_p)str2)->text; } return sl_send_reply(msg, code_i, &code_s); } int load_sl( struct sl_binds *slb) { if(slb==NULL) return -1; slb->reply = sl_send_reply; slb->get_totag = sl_get_totag; return 1; } opensips-2.2.2/modules/sl/sl.h000066400000000000000000000024371300170765700162460ustar00rootroot00000000000000/* * sl module * * Copyright (C) 2006 Voice Sistem * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-02-06 original version (bogdan) */ #ifndef _SL_H_ #define _SL_H_ #include "../../statistics.h" /* module parameter */ extern int sl_enable_stats; /* statistic variables */ extern stat_var *tx_1xx_rpls; extern stat_var *tx_2xx_rpls; extern stat_var *tx_3xx_rpls; extern stat_var *tx_4xx_rpls; extern stat_var *tx_5xx_rpls; extern stat_var *tx_6xx_rpls; extern stat_var *sent_rpls; extern stat_var *sent_err_rpls; extern stat_var *rcv_acks; #endif opensips-2.2.2/modules/sl/sl_api.h000066400000000000000000000032611300170765700170730ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SL_API_H_ #define _SL_API_H_ #include "../../sr_module.h" #include "../../dprint.h" #include "../../str.h" typedef int (*sl_send_reply_f)(struct sip_msg *msg, int code, str *reason); typedef int (*sl_send_reply_dlg_f)(struct sip_msg *msg, int code, str *reason, str *tag); typedef int (*sl_get_totag_f)(struct sip_msg *msg, str *totag); struct sl_binds { sl_send_reply_f reply; sl_send_reply_dlg_f reply_dlg; sl_get_totag_f get_totag; }; typedef int(*load_sl_f)(struct sl_binds *slb); int load_sl(struct sl_binds *slb); static inline int load_sl_api( struct sl_binds *slb ) { load_sl_f load_sl; /* import the SL auto-loading function */ if ( !(load_sl=(load_sl_f)find_export("load_sl", 0, 0))) { LM_ERR("can't import load_sl\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_sl( slb )==-1) return -1; return 0; } #endif opensips-2.2.2/modules/sl/sl_funcs.c000066400000000000000000000164641300170765700174440ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-02-11 modified sl_send_reply to use the transport independent * msg_send (andrei) * 2003-02-18 replaced TOTAG_LEN w/ TOTAG_VALUE_LEN (it was defined twice * w/ different values!) (andrei) * 2003-03-06 aligned to request2response use of tag bookmarks (jiri) * 2003-04-04 modified sl_send_reply to use src_port if rport is present * in the topmost via (andrei) * 2003-09-11: updated to new build_lump_rpl() interface (bogdan) * 2003-09-11: sl_tag converted to str to fit to the new * build_res_buf_from_sip_req() interface (bogdan) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) * 2004-10-10: use of mhomed disabled for replies (jiri) * 2006-03-29: callbacks for sending replies added (bogdan) * 2006-11-28: Moved numerical code tracking out of sl_send_reply() and into * update_sl_reply_stat(). Also added more detail stat collection. * (Jeffrey Magder - SOMA Networks) */ #include "../../globals.h" #include "../../forward.h" #include "../../dprint.h" #include "../../md5utils.h" #include "../../msg_translator.h" #include "../../timer.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../crc.h" #include "../../dset.h" #include "../../data_lump_rpl.h" #include "../../action.h" #include "../../config.h" #include "../../tags.h" #include "../../script_cb.h" #include "../../sl_cb.h" #include "sl.h" #include "sl_funcs.h" #include "../../usr_avp.h" #include /* to-tag including pre-calculated and fixed part */ static char sl_tag_buf[TOTAG_VALUE_LEN]; static str sl_tag = {sl_tag_buf,TOTAG_VALUE_LEN}; /* from here, the variable prefix begins */ static char *tag_suffix; /* if we for this time did not send any stateless reply, we do not filter */ static unsigned int *sl_timeout = 0; int sl_startup(void) { init_tags( sl_tag.s, &tag_suffix, "OpenSIPS-stateless", SL_TOTAG_SEPARATOR ); /*timeout*/ sl_timeout = (unsigned int*)shm_malloc(sizeof(unsigned int)); if (!sl_timeout) { LM_ERR("no more shm memory!\n"); return -1; } *(sl_timeout)=get_ticks(); return 0; } int sl_shutdown(void) { if (sl_timeout) shm_free(sl_timeout); return 1; } int sl_get_totag(struct sip_msg *msg, str *totag) { *totag = sl_tag; return 0; } /* Take care of the statistics associated with numerical codes and replies */ static inline void update_sl_reply_stat(int code) { stat_var *numerical_stat; /* If stats aren't enabled, just skip over this. */ if (!sl_enable_stats) return; /* OpenSIPS already kept track of the total number of 1xx, 2xx, replies. * There may be setups that still expect these variables to exist, so we * don't touch them */ if (code < 200 ) { update_stat( tx_1xx_rpls , 1); } else if (code<300) { update_stat( tx_2xx_rpls , 1); } else if (code<400) { update_stat( tx_3xx_rpls , 1); } else if (code<500) { update_stat( tx_4xx_rpls , 1); } else if (code<600) { update_stat( tx_5xx_rpls , 1); } else { update_stat( tx_6xx_rpls , 1); } update_stat( sent_rpls , 1); numerical_stat = get_stat_var_from_num_code(code, 1); if (numerical_stat != NULL) update_stat(numerical_stat, 1); } int sl_send_reply_helper(struct sip_msg *msg ,int code, str *text) { str buf; union sockaddr_union to; char *dset; int dset_len; struct bookmark dummy_bm; int backup_mhomed; int ret; if ( msg->REQ_METHOD==METHOD_ACK) return 0; update_sock_struct_from_ip( &to, msg ); /* if that is a redirection message, dump current message set to it */ if (code>=300 && code<400) { dset=print_dset(msg, &dset_len); if (dset) { add_lump_rpl(msg, dset, dset_len, LUMP_RPL_HDR); } } /* add a to-tag if there is a To header field without it */ if ( code>=180 && (msg->to || (parse_headers(msg,HDR_TO_F, 0)!=-1 && msg->to)) && (get_to(msg)->tag_value.s==0 || get_to(msg)->tag_value.len==0) ) { calc_crc_suffix( msg, tag_suffix ); buf.s = build_res_buf_from_sip_req( code, text, &sl_tag, msg, (unsigned int*)&buf.len, &dummy_bm); } else { buf.s = build_res_buf_from_sip_req( code, text, 0, msg, (unsigned int*)&buf.len, &dummy_bm); } if (!buf.s) { LM_ERR("response building failed\n"); goto error; } slcb_run_reply_out( msg, &buf, &to, code); /* supress multhoming support when sending a reply back -- that makes sure that replies will come from where requests came in; good for NATs (there is no known use for mhomed for locally generated replies; note: forwarded cross-interface replies do benefit of mhomed! */ backup_mhomed=mhomed; mhomed=0; /* use for sending the received interface -bogdan*/ ret = msg_send( msg->rcv.bind_address, msg->rcv.proto, &to, msg->rcv.proto_reserved1, buf.s, buf.len, NULL); mhomed=backup_mhomed; pkg_free(buf.s); if (ret<0) goto error; *(sl_timeout) = get_ticks() + SL_RPL_WAIT_TIME; update_sl_reply_stat(code); return 1; error: return -1; } int sl_send_reply(struct sip_msg *msg ,int code, str *text) { return sl_send_reply_helper(msg, code, text); } int sl_reply_error(struct sip_msg *msg ) { char err_buf[MAX_REASON_LEN]; int sip_error; str text; int ret; ret = err2reason_phrase( prev_ser_error, &sip_error, err_buf, sizeof(err_buf), "SL"); if (ret<=0) { LM_ERR("err2reason failed\n"); return -1; } text.len = ret; text.s = err_buf; LM_DBG("error text is %.*s\n",text.len,text.s); ret = sl_send_reply_helper( msg, sip_error, &text); if (ret==-1) return -1; if_update_stat( sl_enable_stats, sent_err_rpls , 1); return ret; } /* Returns: 0 : ACK to a local reply -1 : error 1 : is not an ACK or a non-local ACK */ int sl_filter_ACK(struct sip_msg *msg, void *bar ) { str *tag_str; if (msg->first_line.u.request.method_value!=METHOD_ACK) goto pass_it; /*check the timeout value*/ if ( *(sl_timeout)<= get_ticks() ) { LM_DBG("too late to be a local ACK!\n"); goto pass_it; } /*force to parse to header -> we need it for tag param*/ if (parse_headers( msg, HDR_TO_F, 0 )==-1) { LM_ERR("unable to parse To header\n"); return SCB_RUN_ALL; } if (msg->to) { tag_str = &(get_to(msg)->tag_value); if ( tag_str->len==TOTAG_VALUE_LEN ) { /* calculate the variable part of to-tag */ calc_crc_suffix(msg, tag_suffix); /* test whether to-tag equal now */ if (memcmp(tag_str->s,sl_tag.s,sl_tag.len)==0) { LM_DBG("local ACK found -> dropping it!\n"); if_update_stat( sl_enable_stats, rcv_acks, 1); slcb_run_ack_in( msg ); return SCB_DROP_MSG; } } } pass_it: return SCB_RUN_ALL; } opensips-2.2.2/modules/sl/sl_funcs.h000066400000000000000000000023431300170765700174400ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SL_FUNCS_H #define _SL_FUNCS_H #include "../../str.h" #include "../../parser/msg_parser.h" #define SL_RPL_WAIT_TIME 2 /* in sec */ #define SL_TOTAG_SEPARATOR '.' int sl_startup(); int sl_shutdown(); int sl_send_reply( struct sip_msg *msg, int code, str *reason); int sl_filter_ACK( struct sip_msg *msg, void *foo ); int sl_reply_error( struct sip_msg *msg ); int sl_get_totag( struct sip_msg *msg, str *totag ); #endif opensips-2.2.2/modules/sms/000077500000000000000000000000001300170765700156355ustar00rootroot00000000000000opensips-2.2.2/modules/sms/Makefile000066400000000000000000000003161300170765700172750ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=sms.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/sms/README000066400000000000000000000253611300170765700165240ustar00rootroot00000000000000SMS Module Bogdan-Andrei Iancu Edited by Bogdan-Andrei Iancu Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Hardware Requirements 1.1.2. Numbering Plan 1.1.3. Address Mapping 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. modems (string) 1.3.2. networks (string) 1.3.3. links (string) 1.3.4. default_net (string) 1.3.5. max_sms_parts (integer) 1.3.6. domain (string) 1.3.7. use_contact (integer) 1.3.8. sms_report_type (integer) 1.4. Exported Functions 1.4.1. sms_send_msg_to_net(network_name) 1.4.2. sms_send_msg() 2. Developer Guide List of Examples 1.1. Set modems parameter 1.2. Set networks parameter 1.3. Set links parameter 1.4. Set default_net parameter 1.5. Set max_sms_parts parameter 1.6. Set domain_str parameter 1.7. Set use_contact parameter 1.8. Set sms_report_type parameter 1.9. sms_send_msg_to_net usage 1.10. sms_send_msg usage Chapter 1. Admin Guide 1.1. Overview This module provides a way of communication between SIP network (via SIP MESSAGE) and GSM networks (via ShortMessageService). Communication is possible from SIP to SMS and vice versa. The module provides facilities like SMS confirmation--the gateway can confirm to the SIP user if his message really reached its destination as a SMS--or multi-part messages--if a SIP messages is too long it will be split and sent as multiple SMS. Errors occurred because of an invalid number or a too long message or because of an internal modem malfunction are reported back to the SIP user via a SIP message containing explanations regarding the error. 1.1.1. Hardware Requirements The SMS module needs a GSM modem to be able to send/receive the SMS messages. Usually, this kind of modems are externals, linked to the machine via serial cable. The modem can be a dedicated one (as the ones provided by FALCOM) or can be a GSM telephone that has an internal modem (as the latest mobile phones from NOKIA and ERICSSON). 1.1.2. Numbering Plan The gateway accepts and advertises phone numbers in international format, more specific like: +(international code)(area code)(number). Ex: Germany, D1 = +49 170 5678181 Romania, Connex = +40 722 123456. A number in this format is expected to be placed as username into RURI or in the To header. If RURI misses the username, the To header will be consider. Also, the gateway will advertise in this format the username in Contact headers (in SIP replies and requests) and in From headers (in SIP requests). 1.1.3. Address Mapping To identify the destination number of the SMS, the gateway expects to have a mobile number in username of the SIP destination address (for example sip:+401704678811@sidomain.net). For the reverse direction, because the gateway has only one GSM number, the destination SIP address has to be encapsulated into the SMS body. The gateway expects to find a SIP address at the beginning of the SMS body in “sip:user.host†format. Everything before the SIP address will be discarded, the useful text begins exactly after the address (for example SMS=“For sip:user@host hello world!!†-> SIP= “hello worldâ€) In order to facilitate replying, the gateway sends all the SMS messages with a header containing the source SIP address in the following format: “From sip:user@host (if you reply DONOT remove it)â€. When an SMS-reply is received having this header (all of it or truncated at the end), the header will be left out (it will not be in the SIP message). 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * tm - Transaction Manager. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. modems (string) Define and configure one or more GSM modems. modems_value = modem_definition *( ";" modem_definition ) modem_definition = modem_name "[" list_of_params "]" list_of_params = modem_param *( ";" modem_param ) modem_param = name "=" value The following parameters can be used: * d=device (mandatory) - Device associated with modem (/dev/ttyS0, /dev/modem, etc.). * p=pin (optional) - SIM PIN - default is NULL. * m=mode (optional) - Modem working mode (“ASCIIâ€,“OLDâ€,“DIGICOMâ€, “NEWâ€). Default value is “NEWâ€. * c=SMS_Center (optional) - SMS center number for that modem. Default is the SMS center set on the SIM card. * b=baudrate (optional) - Default is 19600. * r=retry (optional) - How many times to try to re-send a SMS that reported error. Default is twice. * l=looping (optional) - Time for modem to wait before performing a new check for incomimg/outgoing SMS/SIP_MSG. Default is 20. No default value, the parameter is mandatory. Example 1.1. Set modems parameter ... modparam("sms", "modems", "Nokia [d=/dev/ttyS1;b=9600;m=new;l=30] ") modparam("sms", "modems", "Nokia[d=/dev/ttyS1];Siemens[d=/dev/ttyS2]") ... 1.3.2. networks (string) Define and configure used GSM networks. networks_value = net_definition *( ";" net_definition ) net_definition = net_name "[" list_of_params "]" list_of_params = set_param *( ";" set_param ) set_param = name "=" value The following parameters can be used: * m=msx_sms_per_call (optional) - Maximum number of SMS send / received from that net in one modem loop. Default is 10. This parameter was introduced to avoid starvation. Example of the starvation--a modem can send SMS for more than 1 networks. If you have a huge number of SMS for the first network and the number of incoming SIP messages is equal to the sent SMS per same unit of time, the modem will never get to send SMS for the next networks. No default value, the parameter is mandatory. Example 1.2. Set networks parameter ... modparam("sms", "networks", "D1 [m=10] ;d2[ m=20]") ... 1.3.3. links (string) Define from which network each modem should send SMS. links_value = modem_assoc *( ";" modem_assoc ) modem_assoc = modem_name "[" list_of_networks "]" list_of_networks = network *( ";" network ) No default value, the parameter is mandatory. Example 1.3. Set links parameter ... modparam("sms", "links", "NOKIA[D1;d2]") ... The modem NOKIA will send SMS from D1 and D2 net (in this order !). if in a net queue are more then max_sms_per_call SMS the modem will not sleep before starting the next loop ! Shortly, if messages are waiting to be sent, the modem will not go in sleep. 1.3.4. default_net (string) The default network to use. If no one specified, the first defined network is used. This parameter is useful only if the “sms_send_msg†exported function is used (see Section 1.4, “Exported Functionsâ€). Example 1.4. Set default_net parameter ... modparam("sms", "default_net", "D1") ... 1.3.5. max_sms_parts (integer) Shows in how many parts (SMS messages) a SIP message can be split. If exceeded, the SIP message will be sent truncated and the SIP user will get back another message containing the unsent part. Default value is 4. Example 1.5. Set max_sms_parts parameter ... modparam("sms", "max_sms_parts", 10) ... 1.3.6. domain (string) Specify a fake domain name to be used by the gateway. The Contact headers and the From header from request will be construct based on this fake domain name. It's useful when the gateway is transparently hidden behind a proxy/register (located on different machines). Default is the name of the machine the gateway is running on. Example 1.6. Set domain_str parameter ... modparam("sms", "domain_str", "foo.bar") ... 1.3.7. use_contact (integer) If a contact header should be added to the outgoing SIP messages. Even if the SIP draft forbids this, some UAS require it. Default is 0 (no). Example 1.7. Set use_contact parameter ... modparam("sms", "use_contact", 1) ... 1.3.8. sms_report_type (integer) If the modem should ask for SMS confirmation from the SMS Center. If the SMSC reply with an error code, the gateway will send back to SIP user a SIP message containing the text (or part of it) that couldn't be send. Two report mechanisms are implemented: * 1 - the reports are delivered by the GSM device as SMS reports (so far supported only by Nokia modems); * 2 - the reports are delivered as async. CDS responses (supported by almost all modems, except Ericsson). Default is 0 (no report). Example 1.8. Set sms_report_type parameter ... modparam("sms", "sms_report_type", 1) ... 1.4. Exported Functions 1.4.1. sms_send_msg_to_net(network_name) Put the SIP msg in the specified network queue. The function return error if the number encapsulated into SIP message is malformed, if the content_type is incorrect or because of some internal failures. Meaning of the parameters is as follows: * network_name - Name of network. This function can be used from REQUEST_ROUTE. Example 1.9. sms_send_msg_to_net usage ... if (sms_send_msg_to_net("D1")) { if (!t_reply("202", "yes sir, SMS sent over")) { # if replying failed, retry statelessly sl_reply_error(); }; } else { if (!t_reply("502", "Bad gateway - SMS error")) { # if replying failed, retry statelessly sl_reply_error(); }; exit; }; ... 1.4.2. sms_send_msg() The same as the previous one, but use the default network queue. This function can be used from REQUEST_ROUTE. Example 1.10. sms_send_msg usage ... if (sms_send_msg_to_net()) { if (!t_reply("202", "yes sir, SMS sent over")) { # if replying failed, retry statelessly sl_reply_error(); }; } else { if (!t_reply("502", "Bad gateway - SMS error")) { # if replying failed, retry statelessly sl_reply_error(); }; exit; }; ... Chapter 2. Developer Guide Each modem forks its own process for sending /fetching SMS. Communication and queuing between OpenSIPS working processes and modem processes is done with pipes. opensips-2.2.2/modules/sms/doc/000077500000000000000000000000001300170765700164025ustar00rootroot00000000000000opensips-2.2.2/modules/sms/doc/sms.xml000066400000000000000000000022401300170765700177240ustar00rootroot00000000000000 %docentities; ]> <acronym>SMS</acronym> Module &osipsname; Bogdan-Andrei Iancu
bogdan@opensips.org
Bogdan-Andrei Iancu
bogdan@opensips.org
2003 &fhg; $Revision: 8740 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/sms/doc/sms_admin.xml000066400000000000000000000330201300170765700210740ustar00rootroot00000000000000 &adminguide;
Overview This module provides a way of communication between &sip; network (via &sip; MESSAGE) and GSM networks (via ShortMessageService). Communication is possible from &sip; to SMS and vice versa. The module provides facilities like SMS confirmation--the gateway can confirm to the &sip; user if his message really reached its destination as a SMS--or multi-part messages--if a &sip; messages is too long it will be split and sent as multiple SMS. Errors occurred because of an invalid number or a too long message or because of an internal modem malfunction are reported back to the &sip; user via a &sip; message containing explanations regarding the error.
Hardware Requirements The SMS module needs a GSM modem to be able to send/receive the SMS messages. Usually, this kind of modems are externals, linked to the machine via serial cable. The modem can be a dedicated one (as the ones provided by FALCOM) or can be a GSM telephone that has an internal modem (as the latest mobile phones from NOKIA and ERICSSON).
Numbering Plan The gateway accepts and advertises phone numbers in international format, more specific like: +(international code)(area code)(number). Ex: Germany, D1 = +49 170 5678181 Romania, Connex = +40 722 123456. A number in this format is expected to be placed as username into RURI or in the To header. If RURI misses the username, the To header will be consider. Also, the gateway will advertise in this format the username in Contact headers (in &sip; replies and requests) and in From headers (in &sip; requests).
Address Mapping To identify the destination number of the SMS, the gateway expects to have a mobile number in username of the &sip; destination address (for example sip:+401704678811@sidomain.net). For the reverse direction, because the gateway has only one GSM number, the destination &sip; address has to be encapsulated into the SMS body. The gateway expects to find a &sip; address at the beginning of the SMS body in sip:user.host format. Everything before the &sip; address will be discarded, the useful text begins exactly after the address (for example SMS=For sip:user@host hello world!! -> SIP= hello world) In order to facilitate replying, the gateway sends all the SMS messages with a header containing the source &sip; address in the following format: From sip:user@host (if you reply DONOT remove it)<new_line>. When an SMS-reply is received having this header (all of it or truncated at the end), the header will be left out (it will not be in the &sip; message).
Dependencies
&osips; Modules The following modules must be loaded before this module: tm - Transaction Manager.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>modems</varname> (string) Define and configure one or more GSM modems. modems_value = modem_definition *( ";" modem_definition ) modem_definition = modem_name "[" list_of_params "]" list_of_params = modem_param *( ";" modem_param ) modem_param = name "=" value The following parameters can be used: d=device (mandatory) - Device associated with modem (/dev/ttyS0, /dev/modem, etc.). p=pin (optional) - SIM PIN - default is NULL. m=mode (optional) - Modem working mode (ASCII,OLD,DIGICOM, NEW). Default value is NEW. c=SMS_Center (optional) - SMS center number for that modem. Default is the SMS center set on the SIM card. b=baudrate (optional) - Default is 19600. r=retry (optional) - How many times to try to re-send a SMS that reported error. Default is twice. l=looping (optional) - Time for modem to wait before performing a new check for incomimg/outgoing SMS/SIP_MSG. Default is 20. No default value, the parameter is mandatory. Set <varname>modems</varname> parameter ... modparam("sms", "modems", "Nokia [d=/dev/ttyS1;b=9600;m=new;l=30] ") modparam("sms", "modems", "Nokia[d=/dev/ttyS1];Siemens[d=/dev/ttyS2]") ...
<varname>networks</varname> (string) Define and configure used GSM networks. networks_value = net_definition *( ";" net_definition ) net_definition = net_name "[" list_of_params "]" list_of_params = set_param *( ";" set_param ) set_param = name "=" value The following parameters can be used: m=msx_sms_per_call (optional) - Maximum number of SMS send / received from that net in one modem loop. Default is 10. This parameter was introduced to avoid starvation. Example of the starvation--a modem can send SMS for more than 1 networks. If you have a huge number of SMS for the first network and the number of incoming &sip; messages is equal to the sent SMS per same unit of time, the modem will never get to send SMS for the next networks. No default value, the parameter is mandatory. Set <varname>networks</varname> parameter ... modparam("sms", "networks", "D1 [m=10] ;d2[ m=20]") ...
<varname>links</varname> (string) Define from which network each modem should send SMS. links_value = modem_assoc *( ";" modem_assoc ) modem_assoc = modem_name "[" list_of_networks "]" list_of_networks = network *( ";" network ) No default value, the parameter is mandatory. Set <varname>links</varname> parameter ... modparam("sms", "links", "NOKIA[D1;d2]") ... The modem NOKIA will send SMS from D1 and D2 net (in this order !). if in a net queue are more then max_sms_per_call SMS the modem will not sleep before starting the next loop ! Shortly, if messages are waiting to be sent, the modem will not go in sleep.
<varname>default_net</varname> (string) The default network to use. If no one specified, the first defined network is used. This parameter is useful only if the sms_send_msg exported function is used (see ). Set <varname>default_net</varname> parameter ... modparam("sms", "default_net", "D1") ...
<varname>max_sms_parts</varname> (integer) Shows in how many parts (SMS messages) a &sip; message can be split. If exceeded, the &sip; message will be sent truncated and the &sip; user will get back another message containing the unsent part. Default value is 4. Set <varname>max_sms_parts</varname> parameter ... modparam("sms", "max_sms_parts", 10) ...
<varname>domain</varname> (string) Specify a fake domain name to be used by the gateway. The Contact headers and the From header from request will be construct based on this fake domain name. It's useful when the gateway is transparently hidden behind a proxy/register (located on different machines). Default is the name of the machine the gateway is running on. Set <varname>domain_str</varname> parameter ... modparam("sms", "domain_str", "foo.bar") ...
<varname>use_contact</varname> (integer) If a contact header should be added to the outgoing &sip; messages. Even if the &sip; draft forbids this, some &uas; require it. Default is 0 (no). Set <varname>use_contact</varname> parameter ... modparam("sms", "use_contact", 1) ...
<varname>sms_report_type</varname> (integer) If the modem should ask for SMS confirmation from the SMS Center. If the SMSC reply with an error code, the gateway will send back to &sip; user a &sip; message containing the text (or part of it) that couldn't be send. Two report mechanisms are implemented: 1 - the reports are delivered by the GSM device as SMS reports (so far supported only by Nokia modems); 2 - the reports are delivered as async. CDS responses (supported by almost all modems, except Ericsson). Default is 0 (no report). Set <varname>sms_report_type</varname> parameter ... modparam("sms", "sms_report_type", 1) ...
Exported Functions
<function moreinfo="none">sms_send_msg_to_net(network_name)</function> Put the &sip; msg in the specified network queue. The function return error if the number encapsulated into &sip; message is malformed, if the content_type is incorrect or because of some internal failures. Meaning of the parameters is as follows: network_name - Name of network. This function can be used from REQUEST_ROUTE. <function>sms_send_msg_to_net</function> usage ... if (sms_send_msg_to_net("D1")) { if (!t_reply("202", "yes sir, SMS sent over")) { # if replying failed, retry statelessly sl_reply_error(); }; } else { if (!t_reply("502", "Bad gateway - SMS error")) { # if replying failed, retry statelessly sl_reply_error(); }; exit; }; ...
<function moreinfo="none">sms_send_msg()</function> The same as the previous one, but use the default network queue. This function can be used from REQUEST_ROUTE. <function>sms_send_msg</function> usage ... if (sms_send_msg_to_net()) { if (!t_reply("202", "yes sir, SMS sent over")) { # if replying failed, retry statelessly sl_reply_error(); }; } else { if (!t_reply("502", "Bad gateway - SMS error")) { # if replying failed, retry statelessly sl_reply_error(); }; exit; }; ...
opensips-2.2.2/modules/sms/doc/sms_devel.xml000066400000000000000000000004521300170765700211060ustar00rootroot00000000000000 &develguide; Each modem forks its own process for sending /fetching SMS. Communication and queuing between &osips; working processes and modem processes is done with pipes. opensips-2.2.2/modules/sms/libsms_charset.c000066400000000000000000000032241300170765700210040ustar00rootroot00000000000000/* SMS Server Tools Copyright (C) 2000 Stefan Frings This program is free software unless you got it under another license directly from the author. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. Either version 2 of the License, or (at your option) any later version. http://www.isis.de/members/~s.frings mailto:s.frings@mail.isis.de */ #include "libsms_charset.h" #define noc 183 // non existent character // iso 8859-1 unsigned char charset[128] ={ '@' , 163 , '$' , 165 , 232 , 233 , 249 , 236 , 242 , 199 , 10 , 216 , 248 , 13 , 197 , 229 , noc , '_' , noc , noc , noc , noc , noc , noc , noc , noc , noc , noc , 198 , 230 , 223 , 201 , ' ' , '!' , 34 , '#' , '$' , '%' , '&' , 39 , '(' , ')' , '*' , '+' , ',' , '-' , '.' , '/' , '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , ':' , ';' , '<' , '=' , '>' , '?' , 161 , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' , 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z' , 196 , 214 , 209 , 220 , 167 , 191 , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' , 228 , 246 , 241 , 252 , 224 }; char ascii2sms(const char c) { char found='*'; // replacement for nonexistent characters int i; for (i=0; i<128 ; i++) if (c==charset[i]) { found=i; break; } return found; } char sms2ascii(const char c) { return charset[(int)c]; } opensips-2.2.2/modules/sms/libsms_charset.h000066400000000000000000000010321300170765700210040ustar00rootroot00000000000000/* SMS Server Tools Copyright (C) 2000 Stefan Frings This program is free software unless you got it under another license directly from the author. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. Either version 2 of the License, or (at your option) any later version. http://www.isis.de/members/~s.frings mailto:s.frings@mail.isis.de */ #ifndef CHARSET_H #define CHARSET_H char ascii2sms(const char c); char sms2ascii(const char c); #endif opensips-2.2.2/modules/sms/libsms_getsms.c000066400000000000000000000275331300170765700206660ustar00rootroot00000000000000/* SMS Server Tools Copyright (C) 2000-2002 Stefan Frings This program is free software unless you got it under another license directly from the author. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. Either version 2 of the License, or (at your option) any later version. http://www.isis.de/members/~s.frings mailto:s.frings@mail.isis.de */ #include #include #include #include #include #include #include #include #include "../../ut.h" #include "libsms_charset.h" #include "libsms_modem.h" #include "libsms_sms.h" #include "sms_funcs.h" #define set_date(_date,_Pointer) {\ (_date)[0] = (_Pointer)[3];\ (_date)[1] = (_Pointer)[2];\ (_date)[2] = '-';\ (_date)[3] = (_Pointer)[5];\ (_date)[4] = (_Pointer)[4];\ (_date)[5] = '-';\ (_date)[6] = (_Pointer)[1];\ (_date)[7] = (_Pointer)[0];} #define set_time( _time , _Pointer) {\ (_time)[0] = (_Pointer)[1];\ (_time)[1] = (_Pointer)[0];\ (_time)[2] = ':';\ (_time)[3] = (_Pointer)[3];\ (_time)[4] = (_Pointer)[2];\ (_time)[5] = ':';\ (_time)[6] = (_Pointer)[5];\ (_time)[7] = (_Pointer)[4];} /* converts an octet to a 8-Bit value */ static inline int octet2bin(char* octet) { int result=0; if (octet[0]>57) result=octet[0]-55; else result=octet[0]-48; result=result<<4; if (octet[1]>57) result+=octet[1]-55; else result+=octet[1]-48; return result; } /* converts a PDU-String to Ascii; the first octet is the length return the length of ascii */ static int pdu2ascii(char* pdu, char* ascii) { int bitposition=0; int byteposition; int byteoffset; int charcounter; int bitcounter; int count; int octetcounter; char c; char binary[500]; /* First convert all octets to bytes */ count=octet2bin(pdu); for (octetcounter=0; octetcounter>1)&127; /* The shift fills with 1, but I want 0 */ } if (/*cs_convert*/1) ascii[charcounter]=sms2ascii(c); else if (c==0) ascii[charcounter]=183; else ascii[charcounter]=c; } ascii[count]=0; return count; } static int pdu2binary(char* pdu, char* binary) { int count; int octetcounter; count=octet2bin(pdu); for (octetcounter=0; octetcountermode==MODE_DIGICOM) { put_command(mdm,"AT+CMGL=\"ALL\"\r",14,answer, sizeof(answer),200,0); /* search for beginning of the answer */ position=strstr(answer,"+CMGL: "); if (position) { end=position+7; while (*end<'9' && *end>'0') end++; if (end==position+7) { foo = str2s(position+7,end-position-7,&err); if (!err) { LM_DBG("found a message at memory %i\n",foo); sim=foo; } position = 0; } position = 0; } } else { LM_DBG("trying to get stored message %i\n",sim); clen=sprintf(command,"AT+CMGR=%i\r",sim); put_command(mdm,command,clen,answer,sizeof(answer),50,0); /* search for beginning of the answer */ position=strstr(answer,"+CMGR:"); } /* keine SMS empfangen, weil Modem nicht mit +CMGR oder +CMGL geantwortet hat */ if (position==0) return 0; beginning=position+7; /* keine SMS, weil Modem mit +CMGR: 0,,0 geantwortet hat */ if (strstr(answer,",,0\r")) return 0; /* After that we have the PDU or ASCII string */ for( end=beginning ; *end && *end!='\r' ; end++ ); if ( !*end || end-beginning<4) return 0; for( end=end+1 ; *end && *end!='\r' ; end++ ); if ( !*end || end-beginning<4) return 0; /* Now we have the end of the PDU or ASCII string */ *end=0; strcpy(pdu,beginning); return sim; } /* deletes the selected sms from the sim card */ static void deletesms(struct modem *mdm, int sim) { char command[32]; char answer[128]; int clen; LM_DBG("deleting message %i !\n",sim); clen = sprintf(command,"AT+CMGD=%i\r",sim); put_command(mdm, command, clen, answer, sizeof(answer), 50, 0); } // checks the size of the SIM memory int check_memory(struct modem *mdm, int flag) { char answer[500]; char* posi; int laenge; int err,foo; int j, out; for(out=0,j=0;!out && j<10; j++) { if (put_command(mdm,"AT+CPMS?\r",9,answer,sizeof(answer),50,0) && (posi=strstr(answer,"+CPMS:"))!=0 ) { // Modem supports CPMS command. Read memory size if ( (posi=strchr(posi,','))!=0 ) { posi++; if ( (laenge=strcspn(posi,",\r"))!=0 ) { if (flag==USED_MEM ) { foo = str2s(posi,laenge,&err); if (err) { LM_ERR("failed to convert into integer used_memory" " from CPMS response\n"); } else { return foo; } } posi+=laenge+1; if ( (laenge=strcspn(posi,",\r"))!=0 ) { foo = str2s(posi,laenge,&err); if (err) { LM_ERR("failed to convert into integer max_memory" " from CPMS response\n"); } else { return foo; } } } } /* if(strstr) */ } /* if(put_command) */ /* if we are here -> some error happened */ if (checkmodem(mdm)!=0) { LM_WARN("something happened with the modem -> was re-init -> let's retry\n"); } else { LM_ERR("modem seems to be ok, but we had an error? I give up!\n"); out = 1; } } /* for */ if (out==0) LM_ERR("modem does not respond after 10 retries, give up!\n"); return -1; } /* splits an ASCII string into the parts */ /* returns length of ascii */ static int splitascii(struct modem *mdm, char *source, struct incame_sms *sms) { char* start; char* end; /* the text is after the \r */ for( start=source ; *start && *start!='\r' ; start++ ); if (!*start) return 1; start++; strcpy(sms->ascii,start); /* get the senders MSISDN */ start=strstr(source,"\",\""); if (start==0) { sms->userdatalength=strlen(sms->ascii); return 1; } start+=3; end=strstr(start,"\","); if (end==0) { sms->userdatalength=strlen(sms->ascii); return 1; } *end=0; strcpy(sms->sender,start); /* Siemens M20 inserts the senders name between MSISDN and date */ start=end+3; // Workaround for Thomas Stoeckel // if (start[0]=='\"') start++; if (start[2]!='/') { // if next is not a date is must be the name end=strstr(start,"\","); if (end==0) { sms->userdatalength=strlen(sms->ascii); return 1; } *end=0; strcpy(sms->name,start); } /* Get the date */ start=end+3; sprintf(sms->date,"%c%c-%c%c-%c%c",start[3],start[4],start[0],start[1], start[6],start[7]); /* Get the time */ start+=9; sprintf(sms->time,"%c%c:%c%c:%c%c",start[0],start[1],start[3],start[4], start[7],start[7]); sms->userdatalength=strlen(sms->ascii); return 1; } /* Subroutine for splitpdu() for messages type 0 (SMS-Deliver) Returns the length of the ascii string In binary mode ascii contains the binary SMS */ static int split_type_0( char* Pointer,struct incame_sms *sms) { int Length; int padding; int is_binary; Length=octet2bin(Pointer); padding=Length%2; Pointer+=4; memcpy(sms->sender,Pointer,Length+padding); swapchars(sms->sender,Length+padding); /* remove Padding characters after swapping */ sms->sender[Length]=0; Pointer=Pointer+Length+padding+3; is_binary = ((Pointer[0] & 4)==4); Pointer++; set_date(sms->date,Pointer); Pointer=Pointer+6; set_time(sms->time,Pointer); Pointer=Pointer+8; if (is_binary) sms->userdatalength = pdu2binary(Pointer,sms->ascii); else sms->userdatalength = pdu2ascii(Pointer,sms->ascii); return 1; } /* Subroutine for splitpdu() for messages type 2 (Staus Report) Returns the length of the ascii string. In binary mode ascii contains the binary SMS */ static int split_type_2( char* position, struct incame_sms *sms) { int length; int padding; char *p; /* get from report the sms id */ sms->sms_id = octet2bin(position); position+=2; /* get recipient address */ length=octet2bin(position); padding=length%2; position+=4; memcpy(sms->sender,position,length+padding); sms->sender[length]=0; swapchars(sms->sender,length); // get SMSC timestamp position+=length+padding; set_date(sms->date,position); set_time(sms->time,position+6); // get Discharge timestamp position+=14; p = sms->ascii + 2; set_date(p,position); *(p+DATE_LEN) = ' '; set_time(p+DATE_LEN+1,position+6); // get Status position+=14; sms->ascii[0] = (unsigned char)octet2bin(position); sms->ascii[1] = ' '; sms->ascii[2+DATE_LEN+1+TIME_LEN] = 0; sms->userdatalength=2+DATE_LEN+1+TIME_LEN; return 1; } /* Splits a PDU string into the parts */ /* Returns the length of the ascii string. In binary mode ascii contains the binary SMS */ static int splitpdu(struct modem *mdm, char* pdu, struct incame_sms *sms) { int Length; int Type; char* Pointer; char* start; char* end; /* Get the senders Name if given. Depends on the modem. */ start=strstr(pdu,"\",\""); if (start!=0) { start+=3; end=strstr(start,"\","); if (end!=0) { memcpy(sms->name,start,end-start); sms->name[end-start]=0; } } else end=pdu; /* the pdu is after the first \r */ for( start=end+1 ; *start && *start!='\r' ; start++ ); if (!*start) return 0; pdu=++start; /* removes unwanted ctrl chars at the beginning */ while ( *pdu && (*pdu<=' ')) pdu++; Pointer=pdu; if (mdm->mode!=MODE_OLD) { /* get senders smsc */ Length=octet2bin(pdu)*2-2; if (Length>0) { Pointer=pdu+4; memcpy(sms->smsc,Pointer,Length); swapchars(sms->smsc,Length); /* remove Padding characters after swapping */ if (sms->smsc[Length-1]=='F') sms->smsc[Length-1]=0; else sms->smsc[Length] = 0; } Pointer=pdu+Length+4; } /* is UDH bit set? udh=(octet2bin(Pointer)&4) */ Type=octet2bin(Pointer) & 3; Pointer+=2; if (Type==0) { sms->is_statusreport = 0; /*SMS Deliver*/ return split_type_0( Pointer, sms); } else if (Type==2) { sms->is_statusreport = 1; /*Status Report*/ return split_type_2( Pointer, sms); } /*Unsupported type*/ return -1; } static inline int decode_pdu( struct modem *mdm, char *pdu, struct incame_sms *sms) { int ret; memset( sms, 0, sizeof(struct incame_sms) ); /* Ok, now we split the PDU string into parts and show it */ if (mdm->mode==MODE_ASCII || mdm->mode==MODE_DIGICOM) ret = splitascii(mdm, pdu, sms); else ret = splitpdu(mdm, pdu, sms); if (ret==-1) { LM_ERR("failed to split pdu/ascii!\n"); return -1; } return 1; } int getsms( struct incame_sms *sms, struct modem *mdm, int sim) { char pdu[512]; int found; int ret; found = fetchsms(mdm,sim,pdu); if ( !found ) { LM_ERR("failed to fetch sms %d!\n",sim); return -1; } /* decode the pdu */ ret = decode_pdu(mdm,pdu,sms); /* delete the sms*/ deletesms(mdm,found); return ret; } int cds2sms(struct incame_sms *sms, struct modem *mdm, char *s, int s_len) { char *data; char *ptr; char tmp; int n; /* pdu starts after 2 "\r\n" */ ptr = s; for ( n=0 ; n<2 && (ptr=strstr(ptr,"\r\n")) ; n++,ptr+=2 ); if (n<2) { LM_ERR("failed to find pdu beginning in CDS!\n"); goto error; } data = ptr; /* pdu end with "\r\n" */ if (!(ptr=strstr(data,"\r\n"))) { LM_ERR("failed to find pdu end in CDS!\n"); goto error; } tmp = ptr[0]; ptr[0] = 0; /* decode the pdu */ n = decode_pdu(mdm,data-3,sms); ptr[0] = tmp; if (n==-1) goto error; return 1; error: return -1; } opensips-2.2.2/modules/sms/libsms_modem.c000066400000000000000000000225641300170765700204640ustar00rootroot00000000000000/* SMS Server Tools Copyright (C) 2000 Stefan Frings This program is free software unless you got it under another license directly from the author. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. Either version 2 of the License, or (at your option) any later version. http://www.isis.de/members/~s.frings mailto:s.frings@mail.isis.de */ #include #include #include #include #include #include #include #include #include #include #include #ifdef __sun #include #endif #include "libsms_modem.h" #include "../../dprint.h" #define MAX_BUF 2048 #define CDS_HDR "\r\n+CDS:" #define CDS_HDR_LEN (strlen(CDS_HDR)) #define optz(_n,_l) (buf+buf_len-(((_n)+(_l)>buf_len)?buf_len:(_n)+(_l))) /* global variables */ int sms_report_type; cds_report cds_report_func; int put_command( struct modem *mdm, char* cmd, int cmd_len, char* answer, int max, int timeout,char* exp_end) { static char buf[MAX_BUF]; static int buf_len = 0; char* pos; char* foo; char* ptr; char* to_move; char* answer_s; char* answer_e; int timeoutcounter; int available; int status; int exp_end_len; int n; /* check if fd is "clean" for reading */ timeoutcounter = 0; ioctl(mdm->fd,TIOCMGET,&status); while (!(status & TIOCM_CTS)) { usleep( READ_SLEEP ); timeoutcounter++; ioctl(mdm->fd,TIOCMGET,&status); if (timeoutcounter>=timeout) { LM_INFO("Modem is not clear to send\n"); return 0; } } #ifdef SHOW_SMS_MODEM_COMMAND LM_DBG("<--<%d>-->[%.*s] \n",cmd_len,cmd_len,cmd); #endif /* send the command to the modem */ n=write(mdm->fd,cmd,cmd_len); tcdrain(mdm->fd); /* read from the modem */ exp_end_len = exp_end?strlen(exp_end):0; answer_s = buf; answer_e = 0; to_move = 0; do { /* try to read some bytes */ ioctl(mdm->fd,FIONREAD,&available); /* how many bytes are available to read? */ if (available<1) /* if 0 then wait a little bit and retry */ { usleep( READ_SLEEP ); timeoutcounter++; ioctl(mdm->fd,FIONREAD,&available); } if (available>0) { /* How many bytes do I want to read maximum? Not more than buffer size. And how many bytes are available? */ n = (available>MAX_BUF-buf_len-1)?MAX_BUF-buf_len-1:available; /* read data */ n = read( mdm->fd, buf+buf_len, n); if (n<0) { LM_ERR("error reading from modem: %s\n", strerror(errno)); goto error; } if (n) { buf_len += n; buf[buf_len] = 0; //LM_DBG("read = [%s]\n",buf+buf_len-n); foo = pos = 0; if ( (!exp_end && ((pos=strstr(optz(n,4),"OK\r\n")) || (foo=strstr(optz(n,5),"ERROR")))) || (exp_end && (pos=strstr(optz(n,exp_end_len),exp_end)) )) { /* we found the end */ //LM_DBG("end found = %s\n", // (foo?"ERROR":(exp_end?exp_end:"OK"))); /* for ERROR we still have to read EOL */ if (!foo || (foo=strstr(foo+5,"\r\n"))) { answer_e = foo?foo+2:(pos+(exp_end?exp_end_len:4)); timeoutcounter = timeout; } } } } /* repeat until timout */ }while (timeoutcounterbaudrate | CRTSCTS | CS8 | CLOCAL | CREAD | O_NDELAY; //uncomment next line to disable hardware handshake //newtio.c_cflag &= ~CRTSCTS; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; newtio.c_lflag = 0; newtio.c_cc[VTIME] = 1; newtio.c_cc[VMIN] = 0; tcflush(mdm->fd, TCIOFLUSH); tcsetattr(mdm->fd,TCSANOW,&newtio); return 0; } int initmodem(struct modem *mdm, cds_report cds_report_f) { char command[100]; char answer[100]; int retries=0; int success=0; int clen=0; int n; LM_INFO("init modem %s on %s.\n",mdm->name,mdm->device); if (mdm->pin[0]) { /* Checking if modem needs PIN */ put_command(mdm,"AT+CPIN?\r",9,answer,sizeof(answer),50,0); if (strstr(answer,"+CPIN: SIM PIN")) { LM_INFO("Modem needs PIN, entering PIN...\n"); clen=sprintf(command,"AT+CPIN=\"%s\"\r",mdm->pin); put_command(mdm,command,clen,answer,sizeof(answer),100,0); put_command(mdm,"AT+CPIN?\r",9,answer,sizeof(answer),50,0); if (!strstr(answer,"+CPIN: READY")) { if (strstr(answer,"+CPIN: SIM PIN")) { LM_ERR("Modem did not accept this PIN\n"); goto error; } else if (strstr(answer,"+CPIN: SIM PUK")) { LM_ERR("YourPIN is locked! Unlock it manually!\n"); goto error; } else { goto error; } } LM_INFO("INFO:initmodem: PIN Ready!\n"); sleep(5); } } if (mdm->mode==MODE_DIGICOM) success=1; else { LM_INFO("INFO:initmodem: Checking if Modem is registered to" " the network\n"); success=0; retries=0; do { retries++; put_command(mdm,"AT+CREG?\r",9,answer,sizeof(answer),100,0); if (strchr(answer,'1') ) { LM_INFO("INFO:initmodem: Modem is registered to the" " network\n"); success=1; } else if (strchr(answer,'2')) { // added by bogdan LM_WARN("Modems seems to try to reach the network!" " Let's wait a little bit\n"); retries--; sleep(2); } else if (strchr(answer,'5')) { // added by Thomas Stoeckel LM_INFO("Modem is registered to a roaming partner network\n"); success=1; } else if (strstr(answer,"ERROR")) { LM_WARN("Ignoring that modem does not support +CREG command\n"); success=1; } else { LM_NOTICE("NOTICE:initmodem: Waiting 2 sec. before retrying\n"); sleep(2); } }while ((success==0)&&(retries<20)); } if (success==0) { LM_ERR("Modem is not registered to the network\n"); goto error; } for( n=0 ; n<2+2*(sms_report_type==CDS_REPORT) ; n++) { /* build the command */ switch (n) { case 0: strcpy(command,"AT+CMGF=0\r"); command[8]+=(mdm->mode==MODE_ASCII || mdm->mode==MODE_DIGICOM); clen = 10; break; case 1: strcpy(command,"AT S7=45 S0=0 L1 V1 X4 &c1 E1 Q0\r"); clen = 33; break; case 2: strcpy(command,"AT+CSMP=49,167,0,241\r"); clen = 21; break; case 3: strcpy(command,"AT+CNMI=1,1,0,1,0\r"); clen = 18; break; } /* send it to modem */ retries=0; success=0; do { retries++; /*querying the modem*/ put_command(mdm,command,clen,answer,sizeof(answer),100,0); /*dealing with the answer*/ if (strstr(answer,"ERROR")) { LM_NOTICE("Waiting 1 sec. before to retrying\n"); sleep(1); } else success=1; }while ((success==0)&&(retries<3)); /* have we succeeded? */ if (success==0) { LM_ERR("cmd [%.*s] returned ERROR\n", clen-1,command); goto error; } } /* end for */ if ( sms_report_type==CDS_REPORT && !cds_report_f) { LM_ERR("no CDS_REPORT function given\n"); goto error; } cds_report_func = cds_report_f; if (mdm->smsc[0]) { LM_INFO("Changing SMSC to \"%s\"\n",mdm->smsc); setsmsc(mdm,mdm->smsc); } return 0; error: return -1; } int checkmodem(struct modem *mdm) { char answer[500]; /* Checking if modem needs PIN */ put_command(mdm,"AT+CPIN?\r",9,answer,sizeof(answer),50,0); if (!strstr(answer,"+CPIN: READY")) { LM_WARN("modem wants the PIN again!\n"); goto reinit; } if (mdm->mode!=MODE_DIGICOM) { put_command(mdm,"AT+CREG?\r",9,answer,sizeof(answer),100,0); if (!strchr(answer,'1') ) { LM_WARN("Modem is not registered to the" " network\n"); goto reinit; } } return 1; reinit: LM_WARN("re -init the modem!!\n"); initmodem(mdm,cds_report_func); return -1; } int setsmsc(struct modem *mdm, char *smsc) { char command[100]; char answer[50]; int clen; if (smsc && smsc[0]) { clen=sprintf(command,"AT+CSCA=\"+%s\"\r",smsc); put_command(mdm,command,clen,answer,sizeof(answer),50,0); } return 0; } int openmodem( struct modem *mdm) { mdm->fd = open(mdm->device, O_RDWR | O_NOCTTY ); if (mdm->fd <0) return -1; tcgetattr(mdm->fd,&(mdm->oldtio)); return 0; } int closemodem(struct modem *mdm) { tcsetattr(mdm->fd,TCSANOW,&(mdm->oldtio)); close(mdm->fd); return 0; } opensips-2.2.2/modules/sms/libsms_modem.h000066400000000000000000000025041300170765700204610ustar00rootroot00000000000000/* SMS Server Tools Copyright (C) 2000 Stefan Frings This program is free software unless you got it under another license directly from the author. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. Either version 2 of the License, or (at your option) any later version. http://www.isis.de/members/~s.frings mailto:s.frings@mail.isis.de */ #ifndef _LIBSMS_MODEM_H #define _LIBSMS_MODEM_H #include #include "sms_funcs.h" #define MODE_OLD 1 #define MODE_DIGICOM 2 #define MODE_ASCII 3 #define MODE_NEW 4 #define READ_SLEEP 10000 #define READ_TIMEOUT 10 typedef int(*cds_report)( struct modem* , char* , int ); /* put_command Sends a command to the modem and waits max timout*0.1 seconds for an answer. The function returns the length of the answer. The answer can be Ok, ERROR or expect. The command may be empty or NULL */ int put_command( struct modem *mdm, char* command, int clen, char* answer, int max, int timeout,char* expect); int setmodemparams( struct modem *mdm); int checkmodem(struct modem *mdm); int initmodem(struct modem *mdm, cds_report cds_report_f); int setsmsc(struct modem *mdm, char *smsc); int openmodem(struct modem *mdm); int closemodem(struct modem *mdm); #endif opensips-2.2.2/modules/sms/libsms_putsms.c000066400000000000000000000133161300170765700207110ustar00rootroot00000000000000/* SMS Server Tools Copyright (C) 2000-2002 Stefan Frings This program is free software unless you got it under another license directly from the author. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. Either version 2 of the License, or (at your option) any later version. http://www.isis.de/members/~s.frings mailto:s.frings@mail.isis.de */ #include #include #include #include #include #include #include #include #include "sms_funcs.h" #include "libsms_charset.h" #include "libsms_modem.h" int sms_report_type; static char hexa[16] = { '0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F' }; void swapchars(char* string, int len) /* Swaps every second character */ { int position; char c; for (position=0; position>4]; pdu[2*character+1] = hexa[foo&0x0f]; } pdu[2*(pdubyteposition+1)]=0; return 2*(pdubyteposition+1); } /* Create a HEX Dump */ int binary2pdu(char* binary, int length, char* pdu) { int character; unsigned char foo; for (character=0;character>4]; pdu[2*character+1] = hexa[foo&0x0f]; } pdu[2*length]=0; return 2*length; } /* make the PDU string. The destination variable pdu has to be big enough. */ int make_pdu(struct sms_msg *msg, struct modem *mdm, char* pdu) { int coding; int flags; char tmp[500]; int pdu_len=0; int foo; memcpy(tmp,msg->to.s,msg->to.len); foo = msg->to.len; tmp[foo] = 0; // terminate the number with F if the length is odd if ( foo%2 ) { tmp[foo]='F'; tmp[++foo] = 0; } // Swap every second character swapchars(tmp,foo); flags = 0x01; /* SMS-Submit MS to SMSC */ if (sms_report_type!=NO_REPORT) flags |= 0x20 ; /* status report request */ coding=240+1; // Dummy + Class 1 if (mdm->mode!=MODE_OLD) flags+=16; // Validity field /* concatenate the first part of the PDU string */ if (mdm->mode==MODE_OLD) pdu_len += sprintf(pdu,"%02X00%02X91%s00%02X%02X",flags, msg->to.len,tmp,coding,msg->text.len); else pdu_len += sprintf(pdu,"00%02X00%02X91%s00%02XA7%02X",flags, msg->to.len,tmp,coding,msg->text.len); /* Create the PDU string of the message */ /* pdu_len += binary2pdu(msg->text.s,msg->text.len,pdu+pdu_len); */ pdu_len += ascii2pdu(msg->text.s,msg->text.len,pdu+pdu_len,1/*convert*/); /* concatenate the text to the PDU string */ return pdu_len; } /* search into modem reply for the sms id */ inline int fetch_sms_id(char *answer) { char *p; int id; p = strstr(answer,"+CMGS:"); if (!p) goto error; p += 6; /* parse to the first digit */ while(p && *p && (*p==' ' || *p=='\r' || *p=='\n')) p++; if (*p<'0' || *p>'9') goto error; /* convert the number*/ id = 0; while (p && *p>='0' && *p<='9') id = id*10 + *(p++)-'0'; return id; error: return -1; } /* send sms */ int putsms( struct sms_msg *sms_messg, struct modem *mdm) { char command[500]; char command2[500]; char answer[500]; char pdu[500]; int clen,clen2; int retries; int err_code; int pdu_len; int sms_id; pdu_len = make_pdu(sms_messg, mdm, pdu); if (mdm->mode==MODE_OLD) clen = sprintf(command,"AT+CMGS=%i\r",pdu_len/2); else if (mdm->mode==MODE_ASCII) clen = sprintf(command,"AT+CMGS=\"+%.*s\"\r",sms_messg->to.len, sms_messg->to.s); else clen = sprintf(command,"AT+CMGS=%i\r",pdu_len/2-1); if (mdm->mode==MODE_ASCII) clen2=sprintf(command2,"%.*s\x1A",sms_messg->text.len, sms_messg->text.s); else clen2=sprintf(command2,"%.*s\x1A",pdu_len,pdu); sms_id = 0; for(err_code=0,retries=0;err_code<2 && retriesretry; retries++) { if (put_command(mdm,command,clen,answer,sizeof(answer),50,"\r\n> ") && put_command(mdm,command2,clen2,answer,sizeof(answer),1000,0) && strstr(answer,"OK") ) { /* no error during sending and the modem said OK */ err_code = 2; /* if reports were request, we have to fetch the sms id from the modem reply to keep trace of the status reports */ if (sms_report_type!=NO_REPORT) { sms_id = fetch_sms_id(answer); if (sms_id==-1) err_code = 1; } } else { /* we have an error */ if (checkmodem(mdm)==-1) { err_code = 0; LM_WARN("resending last sms! \n"); } else if (err_code==0) { LM_WARN("possible corrupted sms. Let's try again!\n"); err_code = 1; }else { LM_ERR("We have a FUBAR sms!! drop it!\n"); err_code = 3; } } } if (err_code==0) LM_WARN("something spooky is going on with the modem!" " Re-inited and re-tried for %d times without success!\n", mdm->retry); return (err_code==0?-2:(err_code==2?sms_id:-1)); } opensips-2.2.2/modules/sms/libsms_sms.h000066400000000000000000000023061300170765700201620ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _LIBSMS_SMS_H #define _LIBSMS_SMS_H #include "sms_funcs.h" #define MAX_MEM 0 #define USED_MEM 1 int putsms( struct sms_msg *sms_messg, struct modem *mdm); int getsms( struct incame_sms *sms, struct modem *mdm, int sim); int check_memory( struct modem *mdm, int flag); void swapchars(char* string, int len); int cds2sms(struct incame_sms *sms, struct modem *mdm, char *s, int s_len); #endif opensips-2.2.2/modules/sms/sms.c000066400000000000000000000347321300170765700166140ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) * 2003-04-02 port_no_str does not contain a leading ':' anymore (andrei) * 2003-04-06 Only child 1 will execute child init (janakj) * 2003-10-24 updated to the new socket_info lists (andrei) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../dprint.h" #include "../../ut.h" #include "../../globals.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../socket_info.h" #include "../tm/tm_load.h" #include "sms_funcs.h" #include "sms_report.h" #include "libsms_modem.h" static int sms_init(void); static int sms_exit(void); static int w_sms_send_msg(struct sip_msg*, char*, char* ); static int w_sms_send_msg_to_net(struct sip_msg*, char*, char*); static int fixup_sms_send_msg_to_net(void** param, int param_no); static void sms_process(int); /* parameters */ char *networks_config = 0; char *modems_config = 0; char *links_config = 0; char *default_net_str = 0; char *domain_str = 0; /*global variables*/ int default_net = 0; int max_sms_parts = MAX_SMS_PARTS; str domain; int *queued_msgs = 0; int use_contact = 0; int sms_report_type = NO_REPORT; struct tm_binds tmb; static proc_export_t sms_procs[] = { {"SMS receiver", 0, 0, sms_process, 0 , 0}, {0,0,0,0,0,0} }; static cmd_export_t cmds[]={ {"sms_send_msg_to_net", (cmd_function)w_sms_send_msg_to_net, 1, fixup_sms_send_msg_to_net, 0, REQUEST_ROUTE}, {"sms_send_msg", (cmd_function)w_sms_send_msg, 0, 0, 0, REQUEST_ROUTE}, {0,0,0,0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; static param_export_t params[]={ {"networks", STR_PARAM, &networks_config }, {"modems", STR_PARAM, &modems_config }, {"links", STR_PARAM, &links_config }, {"default_net", STR_PARAM, &default_net_str }, {"max_sms_parts", INT_PARAM, &max_sms_parts }, {"domain", STR_PARAM, &domain_str }, {"use_contact", INT_PARAM, &use_contact }, {"sms_report_type", INT_PARAM, &sms_report_type }, {0,0,0} }; struct module_exports exports= { "sms", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, 0, params, 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ sms_procs, /* extra processes */ sms_init, /* module initialization function */ (response_function) 0, (destroy_function) sms_exit, /* module exit function */ 0 /* per-child init function */ }; static int fixup_sms_send_msg_to_net(void** param, int param_no) { long net_nr,i; if (param_no==1) { for(net_nr=-1,i=0;idevice,arg+2,arg_end-arg-2); mdm->device[arg_end-arg-2] = 0; break; case 'p': /* pin */ memcpy(mdm->pin,arg+2,arg_end-arg-2); mdm->pin[arg_end-arg-2] = 0; break; case 'm': /* mode */ if (!strncasecmp(arg+2,"OLD",3) && arg_end-arg-2==3) { mdm->mode = MODE_OLD; } else if (!strncasecmp(arg+2,"DIGICOM",7) && arg_end-arg-2==7) { mdm->mode = MODE_DIGICOM; } else if (!strncasecmp(arg+2,"ASCII",5) && arg_end-arg-2==5) { mdm->mode = MODE_ASCII; } else if (!strncasecmp(arg+2,"NEW",3) && arg_end-arg-2==3) { mdm->mode = MODE_NEW; } else { LM_ERR("invalid value \"%.*s\" for param [m]\n", (int)(arg_end-arg-2),arg+2); goto error; } break; case 'c': /* sms center number */ memcpy(mdm->smsc,arg+2,arg_end-arg-2); mdm->smsc[arg_end-arg-2] = 0; break; case 'r': /* retry time */ foo=str2s(arg+2,arg_end-arg-2,&err); if (err) { LM_ERR("failed to convert [r] arg to integer!\n"); goto error; } mdm->retry = foo; break; case 'l': /* looping interval */ foo=str2s(arg+2,arg_end-arg-2,&err); if (err) { LM_ERR("failed to convert [l] arg to integer!\n"); goto error; } mdm->looping_interval = foo; break; case 'b': /* baudrate */ foo=str2s(arg+2,arg_end-arg-2,&err); if (err) { LM_ERR("failed to convert [b] arg to integer!\n"); goto error; } switch (foo) { case 300: foo=B300; break; case 1200: foo=B1200; break; case 2400: foo=B2400; break; case 9600: foo=B9600; break; case 19200: foo=B19200; break; case 38400: foo=B38400; break; case 57600: foo=B57600; break; default: LM_ERR("unsupported value %d for [b] arg!\n",foo); goto error; } mdm->baudrate = foo; break; default: LM_ERR("unknown param name [%c]\n",*arg); goto error; } return 1; error: return -1; } int set_network_arg(struct network *net, char *arg, char *arg_end) { int err,foo; if (*(arg+1)!='=') { LM_ERR("invalid parameter syntax near [=]\n"); goto error; } switch (*arg) { case 'm': /* maximum sms per one call */ foo=str2s(arg+2,arg_end-arg-2,&err); if (err) { LM_ERR("cannot convert [m] arg to integer!\n"); goto error; } net->max_sms_per_call = foo; break; default: LM_ERR("unknown param name [%c]\n",*arg); goto error; } return 1; error: return -1; } int parse_config_lines(void) { char *p,*start; int i, k, step; int mdm_nr, net_nr; nr_of_networks = 0; nr_of_modems = 0; step = 1; /* parsing modems configuration string */ if ( (p = modems_config)==0) { LM_ERR("param \"modems\" not found\n"); goto error; } while (*p) { eat_spaces(p); /*get modem's name*/ start = p; while (*p!=' ' && *p!='\t' && *p!='[' && *p!=0) p++; if ( p==start || *p==0 ) goto parse_error; memcpy(modems[nr_of_modems].name, start, p-start); modems[nr_of_modems].name[p-start] = 0; modems[nr_of_modems].smsc[0] = 0; modems[nr_of_modems].device[0] = 0; modems[nr_of_modems].pin[0] = 0; modems[nr_of_modems].mode = MODE_NEW; modems[nr_of_modems].retry = 4; modems[nr_of_modems].looping_interval = 20; modems[nr_of_modems].baudrate = B9600; memset(modems[nr_of_modems].net_list,0XFF, sizeof(modems[nr_of_modems].net_list) ); /*get modem parameters*/ eat_spaces(p); if (*p!='[') goto parse_error; p++; while (*p!=']') { eat_spaces(p); start = p; while(*p!=' ' && *p!='\t' && *p!=']' && *p!=';' && *p!=0) p++; if ( p==start || *p==0 ) goto parse_error; if (set_modem_arg( &(modems[nr_of_modems]), start, p)==-1) goto error; eat_spaces(p); if (*p==';') { p++; eat_spaces(p); } } if (*p!=']') goto parse_error; p++; /* end of element */ if (modems[nr_of_modems].device[0]==0) { LM_ERR("modem %s has no device associated\n", modems[nr_of_modems].name); goto error; } if (modems[nr_of_modems].smsc[0]==0) { LM_WARN("modem %s has no sms center associated -> using" " the default one from modem\n",modems[nr_of_modems].name); } nr_of_modems++; eat_spaces(p); if (*p==';') { p++; eat_spaces(p); } } if (nr_of_modems==0) { LM_ERR("failed to parse config modems - no modem found!\n"); goto error; } step++; /* parsing networks configuration string */ if ( (p = networks_config)==0) { LM_ERR("param \"networks\" not found\n"); goto error; } while (*p) { eat_spaces(p); /*get network name*/ start = p; while (*p!=' ' && *p!='\t' && *p!='[' && *p!=0) p++; if ( p==start || *p==0 ) goto parse_error; memcpy(networks[nr_of_networks].name, start, p-start); networks[nr_of_networks].name[p-start] = 0; networks[nr_of_networks].max_sms_per_call = 10; /*get network parameters*/ eat_spaces(p); if (*p!='[') goto parse_error; p++; while (*p!=']') { eat_spaces(p); start = p; while(*p!=' ' && *p!='\t' && *p!=']' && *p!=';' && *p!=0) p++; if ( p==start || *p==0 ) goto parse_error; if (set_network_arg( &(networks[nr_of_networks]), start, p)==-1) goto error; eat_spaces(p); if (*p==';') { p++; eat_spaces(p); } } if (*p!=']') goto parse_error; p++; /* end of element */ nr_of_networks++; eat_spaces(p); if (*p==';') p++; eat_spaces(p); } if (nr_of_networks==0) { LM_ERR("no network found!\n"); goto error; } step++; /* parsing links configuration string */ if ( (p = links_config)==0) { LM_ERR("param \"links\" not found\n"); goto error; } while (*p) { eat_spaces(p); /*get modem's device*/ start = p; while (*p!=' ' && *p!='\t' && *p!='[' && *p!=0) p++; if ( p==start || *p==0 ) goto parse_error; /*looks for modem index*/ for(mdm_nr=-1,i=0;i get its index */ for(net_nr=-1,i=0;i not found in net list\n", (int)(p-start), start); goto error; } LM_DBG("linking net \"%s\" to modem \"%s\" on pos %d.\n", networks[net_nr].name,modems[mdm_nr].name,k); modems[mdm_nr].net_list[k++]=net_nr; eat_spaces(p); if (*p==';') { p++; eat_spaces(p); } } if (*p!=']') goto parse_error; p++; /* end of element */ eat_spaces(p); if (*p==';') { p++; eat_spaces(p); } } /* resolving default network name - if any*/ if (default_net_str) { for(net_nr=-1,i=0;iport_no_str.len && si->port_no!=5060); domain.len = si->name.len + i*(si->port_no_str.len+1); domain.s = (char*)pkg_malloc(domain.len); if (!domain.s) { LM_ERR("no more pkg memory!\n"); goto error; } p = domain.s; memcpy(p,si->name.s,si->name.len); p += si->name.len; if (i) { *p=':'; p++; memcpy(p,si->port_no_str.s, si->port_no_str.len); p += si->port_no_str.len; } } /* creates pipes for networks */ for(i=0;i #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../config.h" #include "../../globals.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../../data_lump_rpl.h" #include "../tm/t_hooks.h" #include "../tm/uac.h" #include "sms_funcs.h" #include "sms_report.h" #include "libsms_modem.h" #include "libsms_sms.h" struct modem modems[MAX_MODEMS]; struct network networks[MAX_NETWORKS]; int net_pipes_in[MAX_NETWORKS]; int nr_of_networks; int nr_of_modems; int *queued_msgs; int use_contact; int sms_report_type; struct tm_binds tmb; #define ERR_NUMBER_TEXT " is an invalid number! Please resend your SMS "\ "using a number in +(country code)(area code)(local number) format. Thanks"\ " for using our service!" #define ERR_NUMBER_TEXT_LEN (sizeof(ERR_NUMBER_TEXT)-1) #define ERR_TRUNCATE_TEXT "We are sorry, but your message exceeded our "\ "maximum allowed length. The following part of the message wasn't sent"\ " : " #define ERR_TRUNCATE_TEXT_LEN (sizeof(ERR_TRUNCATE_TEXT)-1) #define ERR_MODEM_TEXT "Due to our modem temporary indisponibility, "\ "the following message couldn't be sent : " #define ERR_MODEM_TEXT_LEN (sizeof(ERR_MODEM_TEXT)-1) #define STORED_NOTE "NOTE: Your SMS received provisional confirmation"\ " 48 \"Delivery is not yet possible\". The SMS was store on the "\ "SMSCenter for further delivery. Our gateway cannot guarantee "\ "further information regarding your SMS delivery! Your message was: " #define STORED_NOTE_LEN (sizeof(STORED_NOTE)-1) #define OK_MSG "Your SMS was finally successfully delivered!"\ " Your message was: " #define OK_MSG_LEN (sizeof(OK_MSG)-1) #define CONTENT_TYPE_HDR "Content-Type: text/plain" #define CONTENT_TYPE_HDR_LEN (sizeof(CONTENT_TYPE_HDR)-1) #define append_str(_p,_s,_l) \ {memcpy((_p),(_s),(_l));\ (_p) += (_l);} #define is_in_sip_addr(_p) \ ((_p)!=' ' && (_p)!='\t' && (_p)!='(' && (_p)!='[' && (_p)!='<' \ && (_p)!='>' && (_p)!=']' && (_p)!=')' && (_p)!='?' && (_p)!='!' \ && (_p)!=';' && (_p)!=',' && (_p)!='\n' && (_p)!='\r' && (_p)!='=') #define no_sip_addr_begin(_p) \ ( (_p)!=' ' && (_p)!='\t' && (_p)!='-' && (_p)!='=' && (_p)!='\r'\ && (_p)!='\n' && (_p)!=';' && (_p)!=',' && (_p)!='.' && (_p)!=':') int push_on_network(struct sip_msg *msg, int net) { str body; struct sip_uri uri; struct sms_msg *sms_messg; struct to_body *from; char *p; int len; int mime; /* get the message's body * anyhow we have to call this function, so let's do it at the beginning * to force the parsing of all the headers - like this we avoid separate * calls of parse_headers function for FROM, CONTENT_LENGTH, TO hdrs */ if (get_body( msg, &body)!=0 || body.len==0) { LM_ERR("failed to extract body from msg!\n"); goto error; } /* parse the content-type header */ if ( (mime=parse_content_type_hdr(msg))<1 ) { LM_ERR("failed to parse Content-Type header\n"); goto error; } /* check the content-type value */ if ( mime!=(TYPE_TEXT<<16)+SUBTYPE_PLAIN && mime!=(TYPE_MESSAGE<<16)+SUBTYPE_CPIM ) { LM_ERR("invalid content-type for a message request! type found=%d\n", mime); goto error; } /* we try to get the user name (phone number) first from the RURI (in our case means from new_uri or from first_line.u.request.uri); if it's missing there (like in requests generated by MSN MESSENGER), we go for "to" header */ LM_DBG("string to get user from new_uri\n"); if ( !msg->new_uri.s||parse_uri( msg->new_uri.s,msg->new_uri.len,&uri) || !uri.user.len ) { LM_DBG("string to get user from R_uri\n"); if ( parse_uri( msg->first_line.u.request.uri.s, msg->first_line.u.request.uri.len ,&uri)||!uri.user.len ) { LM_DBG("string to get user from To\n"); if ((!msg->to&&((parse_headers(msg,HDR_TO_F,0)==-1)||!msg->to)) || parse_uri( get_to(msg)->uri.s, get_to(msg)->uri.len, &uri)<0 || !uri.user.len) { LM_ERR("failed to extract user name from RURI" " and To header!\n"); goto error; } } } /* check the uri.user format = '+(inter code)(number)' */ if (uri.user.len<2 || uri.user.s[0]!='+' || uri.user.s[1]<'1' || uri.user.s[1]>'9') { LM_ERR("user tel number [%.*s] does not respect international format\n" ,uri.user.len,uri.user.s); goto error; } /* parsing from header */ if ( parse_from_header( msg )<0 ) { LM_ERR("failed get FROM header\n"); goto error; } from = (struct to_body*)msg->from->parsed; /*-------------BUILD AND FILL THE SMS_MSG STRUCTURE --------------------*/ /* computes the amount of memory needed */ len = SMS_HDR_BF_ADDR_LEN + from->uri.len + SMS_HDR_AF_ADDR_LEN + body.len + SMS_FOOTER_LEN /*text to send*/ + from->uri.len /* from */ + uri.user.len-1 /* to user (without '+') */ + sizeof(struct sms_msg) ; /* the sms_msg structure */ /* allocs a new sms_msg structure in shared memory */ sms_messg = (struct sms_msg*)shm_malloc(len); if (!sms_messg) { LM_ERR("failed get shm memory!\n"); goto error; } p = (char*)sms_messg + sizeof(struct sms_msg); /* copy "from" into sms struct */ sms_messg->from.len = from->uri.len; sms_messg->from.s = p; append_str(p,from->uri.s,from->uri.len); /* copy "to.user" - we have to strip out the '+' */ sms_messg->to.len = uri.user.len-1; sms_messg->to.s = p; append_str(p,uri.user.s+1,sms_messg->to.len); /* copy (and composing) sms body */ sms_messg->text.len = SMS_HDR_BF_ADDR_LEN + sms_messg->from.len + SMS_HDR_AF_ADDR_LEN + body.len+SMS_FOOTER_LEN; sms_messg->text.s = p; append_str(p, SMS_HDR_BF_ADDR, SMS_HDR_BF_ADDR_LEN); append_str(p, sms_messg->from.s, sms_messg->from.len); append_str(p, SMS_HDR_AF_ADDR, SMS_HDR_AF_ADDR_LEN); append_str(p, body.s, body.len); append_str(p, SMS_FOOTER, SMS_FOOTER_LEN); if (*queued_msgs>MAX_QUEUED_MESSAGES) goto error; (*queued_msgs)++; if (write(net_pipes_in[net], &sms_messg, sizeof(sms_messg))!= sizeof(sms_messg) ) { LM_ERR("failed to write for net %d to pipe [%d] : %s\n", net,net_pipes_in[net],strerror(errno) ); shm_free(sms_messg); (*queued_msgs)--; goto error; } return 1; error: return -1; } int send_sip_msg_request(str *to, str *from_user, str *body) { str msg_type = { "MESSAGE", 7}; str from; str hdrs; int foo; char *p; from.s = hdrs.s = 0; from.len = hdrs.len = 0; /* From header */ from.len = 6 /*"len/*user*/ + 1/*"@"*/ + domain.len /*host*/ + 1 /*">"*/ ; from.s = (char*)pkg_malloc(from.len); if (!from.s) goto error; p=from.s; append_str(p,"s,from_user->len); *(p++)='@'; append_str(p,domain.s,domain.len); *(p++)='>'; /* hdrs = Contact header + Content-type */ /* length */ hdrs.len = CONTENT_TYPE_HDR_LEN + CRLF_LEN; if (use_contact) hdrs.len += 15 /*"Contact: len/*user*/ + 1/*"@"*/ + domain.len/*host*/ + 1 /*">"*/ + CRLF_LEN; hdrs.s = (char*)pkg_malloc(hdrs.len); if (!hdrs.s) goto error; p=hdrs.s; append_str(p,CONTENT_TYPE_HDR,CONTENT_TYPE_HDR_LEN); append_str(p,CRLF,CRLF_LEN); if (use_contact) { append_str(p,"Contact: s,from_user->len); *(p++)='@'; append_str(p,domain.s,domain.len); append_str(p,">"CRLF,1+CRLF_LEN); } /* sending the request */ foo = tmb.t_request( &msg_type, /* request type */ 0, /* Request-URI */ to, /* To */ &from, /* From */ &hdrs, /* Additional headers including CRLF */ body, /* Message body */ 0, /* outbound uri */ 0, /* Callback function */ 0, /* Callback parameter */ 0 ); if (from.s) pkg_free(from.s); if (hdrs.s) pkg_free(hdrs.s); return foo; error: LM_ERR("no more pkg memory!\n"); if (from.s) pkg_free(from.s); if (hdrs.s) pkg_free(hdrs.s); return -1; } static inline int send_error(struct sms_msg *sms_messg, char *msg1_s, int msg1_len, char *msg2_s, int msg2_len) { str body; char *p; int foo; /* body */ body.len = msg1_len + msg2_len; body.s = (char*)pkg_malloc(body.len); if (!body.s) goto error; p=body.s; append_str(p, msg1_s, msg1_len ); append_str(p, msg2_s, msg2_len); /* sending */ foo = send_sip_msg_request( &(sms_messg->from), &(sms_messg->to), &body); pkg_free( body.s ); return foo; error: LM_ERR("no more pkg memory!\n"); return -1; } inline unsigned int split_text(str *text, unsigned char *lens,int nice) { int nr_chunks; int k,k1,len; char c; nr_chunks = 0; len = 0; do{ k = MAX_SMS_LENGTH-(nice&&nr_chunks?SMS_EDGE_PART_LEN:0); if ( len+klen ) { /* is not the last piece :-( */ if (nice && !nr_chunks) k -= SMS_EDGE_PART_LEN; if (text->len-len-k<=SMS_FOOTER_LEN+4) k = (text->len-len)/2; /* ->looks for a point to split */ k1 = k; while( k>0 && (c=text->s[len+k-1])!='.' && c!=' ' && c!=';' && c!='\r' && c!='\n' && c!='-' && c!='!' && c!='?' && c!='+' && c!='=' && c!='\t' && c!='\'') k--; if (klen-len; len = text->len; } nr_chunks++; }while (lenlen); return nr_chunks; } int send_as_sms(struct sms_msg *sms_messg, struct modem *mdm) { static char buf[MAX_SMS_LENGTH]; unsigned int buf_len; unsigned char len_array_1[256], len_array_2[256], *len_array; unsigned int nr_chunks_1, nr_chunks_2, nr_chunks; unsigned int use_nice; str text; char *p, *q; int ret_code; int i; text.s = sms_messg->text.s; text.len = sms_messg->text.len; nr_chunks_1 = split_text( &text, len_array_1, 0); nr_chunks_2 = split_text( &text, len_array_2, 1); if (nr_chunks_1==nr_chunks_2) { len_array = len_array_2; nr_chunks = nr_chunks_2; use_nice = 1; } else { len_array = len_array_1; nr_chunks = nr_chunks_1; use_nice = 0; } sms_messg->ref = 1; for(i=0,p=text.s ; i1 && i) { append_str(q,SMS_EDGE_PART,SMS_EDGE_PART_LEN); *(q-2)=nr_chunks+'0'; *(q-4)=i+1+'0'; } append_str(q,p,len_array[i]); if (nr_chunks>1 && !i) { append_str(q,SMS_EDGE_PART,SMS_EDGE_PART_LEN); *(q-2)=nr_chunks+'0'; *(q-4)=i+1+'0'; } buf_len = q-buf; } else { q = buf; append_str(q,p,len_array[i]); buf_len = len_array[i]; } if (i+1==max_sms_parts && i+1MAX_SMS_LENGTH) buf_len = MAX_SMS_LENGTH; q = buf + (buf_len-SMS_TRUNCATED_LEN-SMS_FOOTER_LEN); append_str(q,SMS_TRUNCATED,SMS_TRUNCATED_LEN); append_str(q,SMS_FOOTER,SMS_FOOTER_LEN); p += buf_len-SMS_TRUNCATED_LEN-SMS_FOOTER_LEN-SMS_EDGE_PART_LEN; send_error(sms_messg, ERR_TRUNCATE_TEXT, ERR_TRUNCATE_TEXT_LEN, p, text.len-(p-text.s)-SMS_FOOTER_LEN); } LM_DBG("---%d--<%d><%d>--\n|%.*s|\n", i, len_array[i], buf_len, (int)buf_len, buf); sms_messg->text.s = buf; sms_messg->text.len = buf_len; if ( (ret_code=putsms(sms_messg,mdm))<0) goto error; if (sms_report_type!=NO_REPORT) add_sms_into_report_queue(ret_code,sms_messg, p-use_nice*(nr_chunks>1)*SMS_EDGE_PART_LEN,len_array[i]); } sms_messg->ref--; /* put back the pointer to the beginning of the message*/ sms_messg->text.s = text.s; sms_messg->text.len = text.len; /* remove the sms if nobody points to it */ if (!sms_messg->ref){ shm_free(sms_messg); } return 1; error: if (ret_code==-1) /* bad number */ send_error(sms_messg, sms_messg->to.s, sms_messg->to.len, ERR_NUMBER_TEXT, ERR_NUMBER_TEXT_LEN); else if (ret_code==-2) /* bad modem */ send_error(sms_messg, ERR_MODEM_TEXT, ERR_MODEM_TEXT_LEN, text.s+SMS_HDR_BF_ADDR_LEN+sms_messg->from.len+SMS_HDR_AF_ADDR_LEN, text.len-SMS_FOOTER_LEN-SMS_HDR_BF_ADDR_LEN-sms_messg->from.len- SMS_HDR_AF_ADDR_LEN ); if (!(--(sms_messg->ref))) shm_free(sms_messg); return -1; } int send_sms_as_sip( struct incame_sms *sms ) { str sip_addr; str sip_body; str sip_from; int is_pattern; int k; char *p; /* first we have to parse the body to try to get out the sip destination address; The sms body can to be in the following two formats: 1. The entire or part of the sent header still exists - we will pars it and consider the start of the sip message the first character that doesn't match the header! 2. The sms body is totally different of the send sms -> search for a sip address inside; everything before it is ignored, only the part following the address being send as sip */ sip_addr.len = 0; sip_body.len = 0; p = sms->ascii; /* is our logo (or a part of it) still there? */ if (*p==SMS_HDR_BF_ADDR[0]) { is_pattern = 1; /* try to match SMS_HDR_BF_ADDR */ k=0; while( is_pattern && pascii+sms->userdatalength && k let's give it a chance and parse for the first word delimiter */ while(pascii+sms->userdatalength && no_sip_addr_begin(*p)) p++; p++; if (p+9>=sms->ascii+sms->userdatalength) { LM_ERR("failed to find sip_address start in sms body [%s]!\n", sms->ascii); goto error; } } /* lets get the address */ if (p[0]!='s' || p[1]!='i' || p[2]!='p' || p[3]!=':') { LM_ERR("wrong sip address format in sms body [%s]!\n",sms->ascii); goto error; } sip_addr.s = p; /* goes to the end of the address */ while(pascii+sms->userdatalength && is_in_sip_addr(*p) ) p++; if (p>=sms->ascii+sms->userdatalength) { LM_ERR("failed to find sip address end in sms body [%s]!\n", sms->ascii); } sip_addr.len = p-sip_addr.s; LM_DBG("sip address found [%.*s]\n", sip_addr.len,sip_addr.s); /* try to match SMS_HDR_AF_ADDR */ k=0; while( is_pattern && pascii+sms->userdatalength && kascii+sms->userdatalength && is_in_sip_addr(*p) ) p++; if (p==sms->ascii+sms->userdatalength) { LM_ERR("failed to find sip address end in sms body [%s]!\n", sms->ascii); goto error; } sip_addr.len = p-sip_addr.s; } else { /* parse to the next word */ /*LM_DBG("*** Skipping word len=%d\n",sms->userdatalength);*/ while(pascii+sms->userdatalength&&no_sip_addr_begin(*p)){ p++; } p++; if (p+9>=sms->ascii+sms->userdatalength) { LM_ERR("failed to find sip address start in sms body [%s]!\n", sms->ascii); goto error; } /*LM_DBG("*** Done\n");*/ } }while (!sip_addr.len); } /* the rest of the sms (if any ;-)) is the body! */ sip_body.s = p; sip_body.len = sms->ascii + sms->userdatalength - p; /* let's trim out all \n an \r from beginning */ while ( sip_body.len && sip_body.s && (sip_body.s[0]=='\n' || sip_body.s[0]=='\r') ) { sip_body.s++; sip_body.len--; } if (sip_body.len==0) { LM_WARN("empty body for sms [%s]", sms->ascii); goto error; } LM_DBG("extracted body is: [%.*s]\n",sip_body.len, sip_body.s); /* finally, let's send it as sip message */ sip_from.s = sms->sender; sip_from.len = strlen(sms->sender); /* patch the body with date and time */ if (sms->userdatalength + CRLF_LEN + 1 /*'('*/ + DATE_LEN + 1 /*','*/ + TIME_LEN + 1 /*')'*/< sizeof(sms->ascii)) { p = sip_body.s + sip_body.len; append_str( p, CRLF, CRLF_LEN); *(p++) = '('; append_str( p, sms->date, DATE_LEN); *(p++) = ','; append_str( p, sms->time, TIME_LEN); *(p++) = ')'; sip_body.len += CRLF_LEN + DATE_LEN + TIME_LEN + 3; } send_sip_msg_request( &sip_addr, &sip_from, &sip_body); return 1; error: return -1; } int check_sms_report( struct incame_sms *sms ) { struct sms_msg *sms_messg; str *s1, *s2; int old; int res; LM_DBG("Report for sms number %d.\n",sms->sms_id); res=relay_report_to_queue( sms->sms_id, sms->sender, sms->ascii[0], &old); if (res==3) { /* error */ /* the sms was confirmed with an error code -> we have to send a message to the SIP user */ s1 = get_error_str(sms->ascii[0]); s2 = get_text_from_report_queue(sms->sms_id); sms_messg = get_sms_from_report_queue(sms->sms_id); send_error( sms_messg, s1->s, s1->len, s2->s, s2->len); } else if (res==1 && sms->ascii[0]==48 && old!=48) { /* provisional 48 */ /* the sms was provisional confirmed with a 48 code -> was stored by SMSC -> no further real-time tracing possible */ s2 = get_text_from_report_queue(sms->sms_id); sms_messg = get_sms_from_report_queue(sms->sms_id); send_error( sms_messg, STORED_NOTE, STORED_NOTE_LEN, s2->s, s2->len); } else if (res==2 && old==48) { /* we received OK for a SMS that had received prev. an 48 code. The note that we send for 48 has to be now clarify */ s2 = get_text_from_report_queue(sms->sms_id); sms_messg = get_sms_from_report_queue(sms->sms_id); send_error( sms_messg, OK_MSG, OK_MSG_LEN, s2->s, s2->len); } if (res>1) /* final response */ remove_sms_from_report_queue(sms->sms_id); return 1; } int check_cds_report( struct modem *mdm, char *cds, int cds_len) { struct incame_sms sms; if (cds2sms( &sms, mdm, cds, cds_len)==-1) return -1; check_sms_report( &sms ); return 1; } void modem_process(struct modem *mdm) { struct sms_msg *sms_messg; struct incame_sms sms; struct network *net; int i,k,len; int counter; int dont_wait; int empty_pipe; int cpms_unsupported; int max_mem=0, used_mem=0; sms_messg = 0; cpms_unsupported = 0; /* let's open/init the modem */ LM_DBG("opening modem\n"); if (openmodem(mdm)==-1) { LM_ERR("failed to open modem %s!" " %s \n",mdm->name,strerror(errno)); return; } setmodemparams(mdm); initmodem(mdm,check_cds_report); if ( (max_mem=check_memory(mdm,MAX_MEM))==-1 ) { LM_WARN("CPMS command unsupported! using default values (10,10)\n"); used_mem = max_mem = 10; cpms_unsupported = 1; } LM_DBG("modem maximum memory is %d\n",max_mem); set_gettime_function(); while(1) { dont_wait = 0; for (i=0;inet_list[i]!=-1;i++) { counter = 0; empty_pipe = 0; net = &(networks[mdm->net_list[i]]); /*getting msgs from pipe*/ while( countermax_sms_per_call && !empty_pipe ) { /* let's read a sms from pipe */ len = read(net->pipe_out, &sms_messg, sizeof(sms_messg)); if (len!=sizeof(sms_messg)) { if (len>=0) LM_ERR("truncated message read from pipe! " "-> discarded\n"); else if (errno==EAGAIN) empty_pipe = 1; else LM_ERR("pipe reading failed: %s\n",strerror(errno)); sleep(1); counter++; continue; } (*queued_msgs)--; /* compute and send the sms */ LM_DBG("%s processing sms for net %s:" " \n\tTo:[%.*s]\n\tBody=<%d>[%.*s]\n", mdm->device, net->name, sms_messg->to.len,sms_messg->to.s, sms_messg->text.len,sms_messg->text.len,sms_messg->text.s); send_as_sms( sms_messg , mdm); counter++; /* if I reached the limit -> set not to wait */ if (counter==net->max_sms_per_call) dont_wait = 1; }/*while*/ }/*for*/ /* let's see if we have incoming sms */ if ( !cpms_unsupported ) if ((used_mem = check_memory(mdm,USED_MEM))==-1) { LM_ERR("CPMS command failed! cannot get used mem->using 10\n"); used_mem = 10; } /* if any, let's get them */ if (used_mem) LM_DBG("%d new SMS on modem\n",used_mem); for(i=1,k=1;k<=used_mem && i<=max_mem;i++) { if (getsms(&sms,mdm,i)!=-1) { k++; LM_DBG("SMS Get from location %d\n",i); /*for test ;-) -> to be remove*/ LM_DBG("SMS RECEIVED:\n\rFrom: %s %s\n\r%.*s %.*s" "\n\r\"%.*s\"\n\r",sms.sender,sms.name, DATE_LEN,sms.date,TIME_LEN,sms.time, sms.userdatalength,sms.ascii); if (!sms.is_statusreport) send_sms_as_sip(&sms); else check_sms_report(&sms); } } /* if reports are used, checks for expired records in report queue */ if (sms_report_type!=NO_REPORT) check_timeout_in_report_queue(); /* sleep -> if it's needed */ if (!dont_wait) { sleep(mdm->looping_interval); } }/*while*/ } opensips-2.2.2/modules/sms/sms_funcs.h000066400000000000000000000060521300170765700200110ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SMS_FUNCS_H #define _SMS_FUNCS_H #include "../../parser/msg_parser.h" #include "../../str.h" #include #include "../tm/tm_load.h" #define MAX_MODEMS 5 /* max number of modems */ #define MAX_NETWORKS 5 /* max number of networks */ #define MAX_CHAR_BUF 128 /* max length of character buffer */ #define MAX_CONFIG_PARAM 1024 /* max length of a config parameter */ #define MAX_SMS_LENGTH 160 #define MAX_SMS_PARTS 4 /* maximum number of parts for a sms */ #define MAX_QUEUED_MESSAGES 100 /* maximum nr of messages waiting to send */ #define SMS_HDR_BF_ADDR "From " #define SMS_HDR_BF_ADDR_LEN (sizeof(SMS_HDR_BF_ADDR)-1) #define SMS_HDR_AF_ADDR " (if you reply DO NOT remove it)\r\n\r\n" #define SMS_HDR_AF_ADDR_LEN (sizeof(SMS_HDR_AF_ADDR)-1) #define SMS_FOOTER "\r\n\r\n[OpenSIPS.ORG]" #define SMS_FOOTER_LEN (sizeof(SMS_FOOTER)-1) #define SMS_EDGE_PART "( / )" #define SMS_EDGE_PART_LEN (sizeof(SMS_EDGE_PART)-1) #define SMS_TRUNCATED "(truncated)" #define SMS_TRUNCATED_LEN (sizeof(SMS_TRUNCATED)-1) #define TIME_LEN 8 /* xx-xx-xx */ #define DATE_LEN TIME_LEN #define NO_REPORT 0 #define SMS_REPORT 1 #define CDS_REPORT 2 struct network { char name[MAX_CHAR_BUF+1]; int max_sms_per_call; int pipe_out; }; struct modem { char name[MAX_CHAR_BUF+1]; char device[MAX_CHAR_BUF+1]; char pin[MAX_CHAR_BUF+1]; char smsc[MAX_CHAR_BUF+1]; int net_list[MAX_NETWORKS]; struct termios oldtio; int mode; int retry; int looping_interval; int fd; int baudrate; }; struct sms_msg { str text; str to; str from; int ref; }; struct incame_sms { char sender[31]; char name[64]; char date[DATE_LEN]; char time[TIME_LEN]; char ascii[500]; char smsc[31]; int userdatalength; int is_statusreport; int sms_id; }; extern struct modem modems[MAX_MODEMS]; extern struct network networks[MAX_NETWORKS]; extern int net_pipes_in[MAX_NETWORKS]; extern int nr_of_networks; extern int nr_of_modems; extern int max_sms_parts; extern str domain; extern int *queued_msgs; extern int use_contact; extern int sms_report_type; extern struct tm_binds tmb; void modem_process(struct modem*); int push_on_network(struct sip_msg*, int); #endif opensips-2.2.2/modules/sms/sms_report.c000066400000000000000000000211041300170765700201740ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../../mem/shm_mem.h" #include "../../timer.h" #include "sms_report.h" #include "sms_funcs.h" #define REPORT_TIMEOUT 1*60*60 // one hour #define START_ERR_MSG "Your message (or part of it) couldn't be "\ "delivered. The SMS Center said: " #define START_ERR_MSG_LEN (strlen(START_ERR_MSG)) #define END_ERR_MSG ". The message was: " #define END_ERR_MSG_LEN (strlen( END_ERR_MSG)) struct report_cell { int status; time_t timeout; char *text; unsigned int text_len; struct sms_msg *sms; }; struct report_cell *report_queue=0; typedef time_t (get_time_func)(void); get_time_func *get_time; /*-------------- Function to set time - from ser or system ------------------*/ /* gets the time from ser */ static time_t get_time_ser(void) { return get_ticks(); } /* gets the time from system */ static time_t get_time_sys(void) { return time(0); } /* detects if the ser time function get_ticks works, and depending of that sets the correct time function to be used */ void set_gettime_function(void) { unsigned int t1,t2; t1 = get_ticks(); sleep(2); t2 = get_ticks(); if (!t1 && !t2) { get_time = get_time_sys; LM_INFO("using system time func.\n"); } else { get_time = get_time_ser; LM_INFO("using ser time func.\n"); } } static inline void free_report_cell(struct report_cell *cell) { if (!cell) return; if (cell->sms && !(--(cell->sms->ref))) shm_free(cell->sms); cell->sms = 0; cell->status = 0; cell->timeout = 0; cell->text = 0; cell->text_len = 0; } int init_report_queue(void) { report_queue = (struct report_cell*) shm_malloc(NR_CELLS*sizeof(struct report_cell)); if (!report_queue) { LM_ERR("no more pkg memory!\n"); return -1; } memset( report_queue , 0 , NR_CELLS*sizeof(struct report_cell) ); return 1; } void destroy_report_queue(void) { int i; if (report_queue){ for(i=0;i discarding\n",id); free_report_cell(&(report_queue[id])); } sms->ref++; report_queue[id].status = -1; report_queue[id].sms = sms; report_queue[id].text = p; report_queue[id].text_len = l; report_queue[id].timeout = get_time() + REPORT_TIMEOUT; } int relay_report_to_queue(int id, char *phone, int status, int *old_status) { struct report_cell *cell; int ret_code; cell = &(report_queue[id]); ret_code = 0; /* first, do we have a match into the sms queue? */ if (!cell->sms) { LM_INFO("report received for cell %d," " but the sms was already trashed from queue!\n",id); goto done; } if (strlen(phone)!=cell->sms->to.len || strncmp(phone,cell->sms->to.s,cell->sms->to.len)) { LM_INFO("report received for cell %d, but the phone nr is different" "->old report->ignored\n",id); goto done; } if (old_status) *old_status = cell->status; cell->status = status; if (status>=0 && status<32) { LM_DBG("sms %d confirmed with code %d\n", id, status); ret_code = 2; /* success */ } else if (status<64) { /* provisional report */ LM_DBG("sms %d received prov. report with" " code %d\n",id, status); ret_code = 1; /* provisional */ } else { LM_DBG("sms %d received error report with code %d\n",id, status); ret_code = 3; /* error */ } done: return ret_code; } void check_timeout_in_report_queue(void) { int i; time_t current_time; current_time = get_time(); for(i=0;iascii,"Ok,short message received by the SME"); break; case 1: strcat(sms->ascii,"Ok,short message forwarded by the SC to" " the SME but the SC is unable to confirm delivery"); break; case 2: strcat(sms->ascii,"Ok,short message replaced by the SC"); break; case 32: strcat(sms->ascii,"Still trying,congestion"); break; case 33: strcat(sms->ascii,"Still trying,SME busy"); break; case 34: strcat(sms->ascii,"Still trying,no response from SME"); break; case 35: strcat(sms->ascii,"Still trying,service rejected"); break; case 36: strcat(sms->ascii,"Still trying,quality of service not" " available"); break; case 37: strcat(sms->ascii,"Still trying,error in SME"); break; case 48: err_str.s = START_ERR_MSG"Delivery is not possible"END_ERR_MSG; err_str.len = 24 + START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; */ case 64: err_str.s = START_ERR_MSG"Error, remote procedure error"END_ERR_MSG; err_str.len = 29 + START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 65: err_str.s = START_ERR_MSG"Error,incompatible destination"END_ERR_MSG; err_str.len = 30 + START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 66: err_str.s = START_ERR_MSG"Error,connection rejected by SME"END_ERR_MSG; err_str.len = 32 + START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 67: err_str.s = START_ERR_MSG"Error,not obtainable"END_ERR_MSG; err_str.len = 20 + START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 68: err_str.s = START_ERR_MSG"Error,quality of service not available"END_ERR_MSG; err_str.len = 38 + START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 69: err_str.s = START_ERR_MSG"Error,no interworking available"END_ERR_MSG; err_str.len = 31 + START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 70: err_str.s = START_ERR_MSG"Error,SM validity period expired"END_ERR_MSG; err_str.len = 32 + START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 71: err_str.s = START_ERR_MSG"Error,SM deleted by originating SME"END_ERR_MSG; err_str.len = 35 + START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 72: err_str.s = START_ERR_MSG"Error,SM deleted by SC administration"END_ERR_MSG; err_str.len = 37+ START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 73: err_str.s = START_ERR_MSG"Error,SM does not exist"END_ERR_MSG; err_str.len = 29+ START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 96: err_str.s = START_ERR_MSG"Error,congestion"END_ERR_MSG; err_str.len = 23+ START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 97: err_str.s = START_ERR_MSG"Error,SME busy"END_ERR_MSG; err_str.len = 14+ START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 98: err_str.s = START_ERR_MSG"Error,no response from SME"END_ERR_MSG; err_str.len = 26+ START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 99: err_str.s = START_ERR_MSG"Error,service rejected"END_ERR_MSG; err_str.len = 22+ START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 100: err_str.s = START_ERR_MSG"Error,quality of service not available"END_ERR_MSG; err_str.len = 38+ START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; case 101: err_str.s = START_ERR_MSG"Error,error in SME"END_ERR_MSG; err_str.len = 18+ START_ERR_MSG_LEN + END_ERR_MSG_LEN; break; default: err_str.s = START_ERR_MSG"Unknown error code"END_ERR_MSG; err_str.len = 18+ START_ERR_MSG_LEN + END_ERR_MSG_LEN; } return &err_str; } opensips-2.2.2/modules/sms/sms_report.h000066400000000000000000000025751300170765700202140ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _H_SMS_REPORT_DEF #define _H_SMS_REPORT_DEF #include "../../str.h" #include "sms_funcs.h" #define NR_CELLS 256 int init_report_queue(); void destroy_report_queue(); void add_sms_into_report_queue(int id, struct sms_msg *sms, char *, int ); int relay_report_to_queue(int id, char *phone, int status, int *old_status); void check_timeout_in_report_queue(); str* get_error_str(int status); void remove_sms_from_report_queue(int id); str* get_text_from_report_queue(int id); struct sms_msg* get_sms_from_report_queue(int id); void set_gettime_function(); #endif opensips-2.2.2/modules/sngtc/000077500000000000000000000000001300170765700161515ustar00rootroot00000000000000opensips-2.2.2/modules/sngtc/Makefile000066400000000000000000000003011300170765700176030ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen = NAME = sngtc.so LIBS = -lsngtc_node include ../../Makefile.modules opensips-2.2.2/modules/sngtc/README000066400000000000000000000122121300170765700170270ustar00rootroot00000000000000sngtc Module Liviu Chircu OpenSIPS Solutions Edited by Liviu Chircu Copyright © 2013 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How it works 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Functions 1.4.1. sngtc_offer() 1.4.2. sngtc_callee_answer([listen_if_A[, listen_if_B]]) 1.4.3. sngtc_caller_answer() List of Examples 1.1. sngtc_offer usage 1.2. sngtc_callee_answer usage 1.3. sngtc_caller_answer usage Chapter 1. Admin Guide 1.1. Overview The Sangoma transcoding module offers the possibility of performing voice transcoding with the D-series transcoding cards manufactured by Sangoma. The module makes use of the Sangoma Transcoding API in order to manage transcoding sessions on the dedicated equipment. For the cards in the network to be detected, the Sangoma SOAP server must be up and running (sngtc_server daemon). 1.2. How it works The module performs several modifications in the SDP body of SIP INVITE, 200 OK and ACK messages. In all transcoding scenarios, the UAC performs early SDP negotiation, while the UAS does late negotiation. This way, OpenSIPS becomes responsible for intersecting the codec offer and answer, together with the management of transcoding sessions on the Sangoma cards. This scenario brings about a couple of restrictions: * UACs MUST only perform early SDP negotiation * UASs MUST support late SDP negotiation (rfc 3261 requirement) Since the sngtc_node library performs several memory allocations with each newly created transcoding session, the module uses a dedicated process, responsible for the management of the above-mentioned sessions. The sangoma_worker process communicates with the OpenSIPS UDP receivers through a series of pipes. 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * dialog. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * sngtc_node library - download from Sangoma, compile and install . * sngtc_server up and running. 1.4. Exported Functions 1.4.1. sngtc_offer() The function strips off the SDP offer from a SIP INVITE, thus asking for another SDP offer from the opposite endpoint (late negotiation). The following error codes may be returned: * -1 - SDP parsing error * -3 - internal error / no more memory The function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. Example 1.1. sngtc_offer usage ... if (is_method("INVITE")) { t_newtran(); create_dialog(); sngtc_offer(); } ... 1.4.2. sngtc_callee_answer([listen_if_A[, listen_if_B]]) Handles the SDP offer from 200 OK responses, intersects both offers with the capabilities of the transcoding card and creates a new transcoding session on the card only if necessary. It then rewrites the 200 OK SDP so that it contains the information resulted from the codec intersection. Parameters explained: Since the D-series transcoding cards are connected through either a PCI slot or simply an Ethernet connector, they cannot be assigned global IPs. Consequently, the module will write the local, private IP of the card in the SDP answers sent to each of the endpoints. Since this will not work with non-local UAs, the optional parameters force the RTP listen interface for each UA. This way, the script writer can enforce a global IP for the incoming RTP (which can be port forwarded to a transcoding card). * listen_if_A - the interface where the UAC (the caller) will send RTP after the call is established (IP from the 'c=' SDP line(s)) * listen_if_B - the interface where the UAS (the callee) will send RTP after the call is established (IP from the 'c=' SDP line(s)) The following error codes may be returned: * -1 - SDP parsing error * -2 - failed to create transcoding session * -3 - internal error / no more memory This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. Example 1.2. sngtc_callee_answer usage ... onreply_route[1] { if ($rs == 200) sngtc_callee_answer("11.12.13.14", "11.12.13.14"); } ... 1.4.3. sngtc_caller_answer() Attaches an SDP body to the caller's ACK request, so that it matches the late SDP negotiation done by the UAS. The following error codes may be returned: * -3 - internal error / no more memory This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. Example 1.3. sngtc_caller_answer usage ... if (has_totag()) { if (loose_route()) { ... if (is_method("ACK")) sngtc_caller_answer(); } ... } ... opensips-2.2.2/modules/sngtc/doc/000077500000000000000000000000001300170765700167165ustar00rootroot00000000000000opensips-2.2.2/modules/sngtc/doc/sngtc.xml000066400000000000000000000017271300170765700205650ustar00rootroot00000000000000 %docentities; ]> sngtc Module &osipsname; Liviu Chircu OpenSIPS Solutions
liviu@opensips.org
Liviu Chircu
liviu@opensips.org
2013 &osipssol;
&admin;
opensips-2.2.2/modules/sngtc/doc/sngtc_admin.xml000066400000000000000000000151671300170765700217400ustar00rootroot00000000000000 &adminguide;
Overview The Sangoma transcoding module offers the possibility of performing voice transcoding with the D-series transcoding cards manufactured by Sangoma. The module makes use of the Sangoma Transcoding API in order to manage transcoding sessions on the dedicated equipment. For the cards in the network to be detected, the Sangoma SOAP server must be up and running (sngtc_server daemon).
How it works The module performs several modifications in the SDP body of SIP INVITE, 200 OK and ACK messages. In all transcoding scenarios, the UAC performs early SDP negotiation, while the UAS does late negotiation. This way, OpenSIPS becomes responsible for intersecting the codec offer and answer, together with the management of transcoding sessions on the Sangoma cards. This scenario brings about a couple of restrictions: UACs MUST only perform early SDP negotiation UASs MUST support late SDP negotiation (rfc 3261 requirement) Since the sngtc_node library performs several memory allocations with each newly created transcoding session, the module uses a dedicated process, responsible for the management of the above-mentioned sessions. The sangoma_worker process communicates with the OpenSIPS UDP receivers through a series of pipes.
Dependencies
&osips; Modules The following modules must be loaded before this module: dialog.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: sngtc_node library - download from Sangoma, compile and install . sngtc_server up and running.
Exported Functions
<function moreinfo="none">sngtc_offer()</function> The function strips off the SDP offer from a SIP INVITE, thus asking for another SDP offer from the opposite endpoint (late negotiation). The following error codes may be returned: -1 - SDP parsing error -3 - internal error / no more memory The function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. <function moreinfo="none">sngtc_offer</function> usage ... if (is_method("INVITE")) { t_newtran(); create_dialog(); sngtc_offer(); } ...
<function moreinfo="none">sngtc_callee_answer([listen_if_A[, listen_if_B]]) </function> Handles the SDP offer from 200 OK responses, intersects both offers with the capabilities of the transcoding card and creates a new transcoding session on the card only if necessary. It then rewrites the 200 OK SDP so that it contains the information resulted from the codec intersection. Parameters explained: Since the D-series transcoding cards are connected through either a PCI slot or simply an Ethernet connector, they cannot be assigned global IPs. Consequently, the module will write the local, private IP of the card in the SDP answers sent to each of the endpoints. Since this will not work with non-local UAs, the optional parameters force the RTP listen interface for each UA. This way, the script writer can enforce a global IP for the incoming RTP (which can be port forwarded to a transcoding card). listen_if_A - the interface where the UAC (the caller) will send RTP after the call is established (IP from the 'c=' SDP line(s)) listen_if_B - the interface where the UAS (the callee) will send RTP after the call is established (IP from the 'c=' SDP line(s)) The following error codes may be returned: -1 - SDP parsing error -2 - failed to create transcoding session -3 - internal error / no more memory This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. <function moreinfo="none">sngtc_callee_answer</function> usage ... onreply_route[1] { if ($rs == 200) sngtc_callee_answer("11.12.13.14", "11.12.13.14"); } ...
<function moreinfo="none">sngtc_caller_answer()</function> Attaches an SDP body to the caller's ACK request, so that it matches the late SDP negotiation done by the UAS. The following error codes may be returned: -3 - internal error / no more memory This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE. <function moreinfo="none">sngtc_caller_answer</function> usage ... if (has_totag()) { if (loose_route()) { ... if (is_method("ACK")) sngtc_caller_answer(); } ... } ...
opensips-2.2.2/modules/sngtc/sngtc.c000066400000000000000000001130601300170765700174340ustar00rootroot00000000000000/** * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2013-06-05 created (liviu) * */ #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/sdp/sdp.h" #include "../../data_lump.h" #include "../../ut.h" #include "../../pt.h" #include "../../net/net_udp.h" #include "../../net/net_tcp.h" #include "../../mod_fix.h" #include "../dialog/dlg_load.h" #include #include "sngtc.h" #include "sngtc_proc.h" static struct codec_mapping codec_str_mappings[] = { { {"AMR", 3}, 8000, 0, SNGTC_CODEC_AMR_475 }, { {"AMR", 3}, 8000, 1, SNGTC_CODEC_AMR_515 }, { {"AMR", 3}, 8000, 2, SNGTC_CODEC_AMR_590 }, { {"AMR", 3}, 8000, 3, SNGTC_CODEC_AMR_670 }, { {"AMR", 3}, 8000, 4, SNGTC_CODEC_AMR_740 }, { {"AMR", 3}, 8000, 5, SNGTC_CODEC_AMR_795 }, { {"AMR", 3}, 8000, 6, SNGTC_CODEC_AMR_1020 }, { {"AMR", 3}, 8000, 7, SNGTC_CODEC_AMR_1220 }, { {"G722", 4}, 8000, -1, SNGTC_CODEC_G722 }, { {"G723", 4}, 8000, -1, SNGTC_CODEC_G723_1 }, { {"G723", 4}, 8000, -1, SNGTC_CODEC_G723_1 }, { {"G726-16", 7}, 8000, -1, SNGTC_CODEC_G726_16 }, { {"G726-24", 7}, 8000, -1, SNGTC_CODEC_G726_24 }, { {"G726-32", 7}, 8000, -1, SNGTC_CODEC_G726_32 }, { {"G726-40", 7}, 8000, -1, SNGTC_CODEC_G726_40 }, { {"G729", 4}, 8000, -1, SNGTC_CODEC_G729AB }, { { "GSM", 3}, 8000, -1, SNGTC_CODEC_GSM_FR }, { {"iLBC", 4}, 8000, -1, SNGTC_CODEC_ILBC }, { { "L16", 3}, 44100, -1, SNGTC_CODEC_L16_2 }, { { "L16", 3}, 44100, -1, SNGTC_CODEC_L16_1 }, { {"PCMA", 4}, 8000, -1, SNGTC_CODEC_PCMA }, { {"PCMA", 4}, 8000, -1, SNGTC_CODEC_PCMA }, { {"PCMU", 4}, 8000, -1, SNGTC_CODEC_PCMU }, { {"SIREN", 5}, 16000, -1, SNGTC_CODEC_SIREN7_24 }, { { 0, 0 }, -1, -1, -1 } }; /* Mappings of standard payload types and Sangoma codecs */ static struct codec_mapping codec_int_mappings[] = { { { "0", 1}, 8000, -1, SNGTC_CODEC_PCMU }, { { "3", 1}, 8000, -1, SNGTC_CODEC_GSM_FR }, { { "4", 1}, 8000, -1, SNGTC_CODEC_G723_1 }, { { "8", 1}, 8000, -1, SNGTC_CODEC_PCMA }, { { "9", 1}, 8000, -1, SNGTC_CODEC_G722 }, { { "10",2}, 44100, -1, SNGTC_CODEC_L16_2 }, { { "11",2}, 44100, -1, SNGTC_CODEC_L16_1 }, { { "18",2}, 8000, -1, SNGTC_CODEC_G729AB }, { { "0", 0}, -1, -1, -1 } }; /* internal module variables */ static str dlg_key_sngtc_info = str_init("SngTc"); static str sdp_buffer = { NULL, 0 }; /* results of matchings on all streams of two endpoints */ static struct codec_pair codec_matches[MAX_STREAMS]; /* force a certain IP for the transcoding card (most often a public IP) */ static str card_ip_a, card_ip_b; /* index of the current SIP UDP receiver's pipe */ static int pipe_index; /* one R+W pipe for each SIP UDP receiver process */ int *sip_workers_pipes; /* pipe for the sangoma worker */ int sangoma_pipe[2]; static int *proc_counter; gen_lock_t *index_lock; /* generic module functions */ static int mod_init(void); static int child_init(int rank); static void mod_destroy(void); static sngtc_init_cfg_t sngtc_init_cfg; static struct dlg_binds dlg_binds; /* module specific functions */ static int sngtc_offer(struct sip_msg *msg); static int w_sngtc_callee_answer(struct sip_msg *msg, char *gp_ip_a, char *gp_ip_b); static int sngtc_callee_answer(struct sip_msg *msg); static int sngtc_caller_answer(struct sip_msg *msg); static int sng_logger(int level, char *fmt, ...); static proc_export_t procs[] = { { "sangoma_worker", NULL, NULL, sangoma_worker_loop, 1, 0 }, { 0, 0, 0, 0, 0, 0 }, }; static param_export_t params[] = { { 0, 0, 0 } }; static cmd_export_t cmds[] = { { "sngtc_offer", (cmd_function)sngtc_offer, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, { "sngtc_callee_answer", (cmd_function)w_sngtc_callee_answer, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, { "sngtc_callee_answer", (cmd_function)w_sngtc_callee_answer, 1, fixup_sgp_null, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, { "sngtc_callee_answer", (cmd_function)w_sngtc_callee_answer, 2, fixup_sgp_sgp, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, { "sngtc_caller_answer", (cmd_function)sngtc_caller_answer, 0, 0, 0, REQUEST_ROUTE|ONREPLY_ROUTE }, { 0, 0, 0, 0, 0, 0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "dialog", DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "sngtc", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, &deps, /* OpenSIPS module dependencies */ cmds, 0, params, 0, 0, 0, procs, mod_init, (response_function) 0, (destroy_function)mod_destroy, child_init }; int sng_create_rtp(void * usr_priv, sngtc_codec_request_leg_t *codec_reg_leg, sngtc_codec_reply_leg_t* codec_reply_leg, void **rtp_fd) { LM_DBG("create_rtp callback\n"); return 0; } int sng_create_rtp_port(void * usr_priv, uint32_t host_ip, uint32_t *rtp_port, void **rtp_fd) { LM_DBG("create_rtp_port callback\n"); return 0; } int sng_destroy_rtp(void * usr_priv, void *fd) { LM_DBG("destroy_rtp callback\n"); return 0; } int sng_release_rtp_port(void * usr_priv, uint32_t host_ip, uint32_t rtp_port, void *rtp_fd) { LM_DBG("release_rtp_port callback\n"); return 0; } void free_transcoding_sessions(struct sngtc_session_list *first) { struct sngtc_session_list *session, *aux; struct sngtc_request req; int rc; req.type = REQ_FREE_SESSION; req.response_fd = sip_workers_pipes[pipe_index + WRITE_END]; for (session = first; session; ) { LM_DBG("freeing transcoding session %p\n", session->reply); sngtc_print_reply(L_DBG, session->reply); req.sng_reply = session->reply; if (write(sangoma_pipe[WRITE_END], &req, sizeof(req)) < 0) { LM_ERR("failed to write on sangoma pipe fd %d (%d: %s)\n", sangoma_pipe[WRITE_END], errno, strerror(errno)); goto free_mem; } if (read(sip_workers_pipes[pipe_index + READ_END], &rc, sizeof(rc)) < 0) { LM_ERR("failed to read sangoma worker reply on pipe fd %d (%d: %s)\n", sip_workers_pipes[pipe_index + READ_END], errno, strerror(errno)); goto free_mem; } if (rc != 0) { LM_ERR("failed to free transcoding session\n"); sngtc_print_reply(L_ERR, session->reply); } LM_DBG("successfully freed transcoding session\n"); free_mem: aux = session; session = session->next; shm_free(aux); } } /** * sngtc_dlg_terminated (callback) - completely free the struct sngtc_info * * attached to the dialog. * * Also releases the ongoing transcoding session(s) at card level */ void sngtc_dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *params) { str info_ptr; struct sngtc_info *info; LM_DBG("freeing the sdp buffer\n"); if (dlg_binds.fetch_dlg_value(dlg, &dlg_key_sngtc_info, &info_ptr, 0) != 0) { LM_ERR("failed to fetch caller sdp\n"); return; } info = *(struct sngtc_info **)info_ptr.s; LM_DBG("Info ptr: %p\n", info); free_transcoding_sessions(info->sessions); if (info->caller_sdp.s) shm_free(info->caller_sdp.s); if (info->modified_caller_sdp.s) shm_free(info->modified_caller_sdp.s); shm_free(info); } static int mod_init(void) { int i, sip_workers_no; LM_INFO("initializing module\n"); memset(&dlg_binds, 0, sizeof(dlg_binds)); if (load_dlg_api(&dlg_binds) != 0) { LM_ERR("failed to load dlg api\n"); return -1; } sdp_buffer.s = pkg_malloc(SDP_BUFFER_SIZE); if (!sdp_buffer.s) { LM_ERR("insufficient pkg memory\n"); return -1; } sip_workers_no = udp_count_processes() + tcp_count_processes(); LM_DBG("Children: %d\n", sip_workers_no); sip_workers_pipes = pkg_malloc(2 * sip_workers_no * sizeof(*sip_workers_pipes)); if (!sip_workers_pipes) { LM_ERR("Not enough pkg mem\n"); return -1; } index_lock = shm_malloc(sizeof(*index_lock)); if (!index_lock) { LM_ERR("No more shm mem\n"); return -1; } if (!lock_init(index_lock)) { LM_ERR("Failed to init lock\n"); return -1; } proc_counter = shm_malloc(sizeof(*proc_counter)); if (!proc_counter) { LM_ERR("Not enough shm mem\n"); return -1; } *proc_counter = 0; if (pipe(sangoma_pipe) != 0) { LM_ERR("Failed to create sangoma worker pipe\n"); return -1; } LM_DBG("Sangoma pipe: [%d %d]\n", sangoma_pipe[0], sangoma_pipe[1]); for (i = 0; i < sip_workers_no; i++) { if (pipe(sip_workers_pipes + 2 * i) != 0) { LM_ERR("Failed to create pipe for UDP receiver %d\n", i); return -1; } LM_DBG("SIP pipe: [%d %d]\n", sip_workers_pipes[2 * i], sip_workers_pipes[2 * i + 1]); } sngtc_init_cfg.operation_mode = SNGTC_MODE_SOAP_CLIENT; sngtc_init_cfg.log = sng_logger; sngtc_init_cfg.create_rtp = sng_create_rtp; sngtc_init_cfg.create_rtp_port = sng_create_rtp_port; sngtc_init_cfg.destroy_rtp = sng_destroy_rtp; sngtc_init_cfg.release_rtp_port = sng_release_rtp_port; if (sngtc_detect_init_modules(&sngtc_init_cfg, &i) != 0) { LM_ERR("failed to detect vocallo modules\n"); return -1; } LM_DBG("Detected %d vocallo modules\n", i); if (sngtc_activate_modules(&sngtc_init_cfg, &i) != 0) { LM_ERR("failed to activate vocallo modules\n"); return -1; } LM_DBG("Activated %d vocallo modules\n", i); return 0; } static int child_init(int rank) { LM_DBG("init child\n"); if (rank <= PROC_MAIN) return 0; lock_get(index_lock); pipe_index = 2 * (*proc_counter)++; close(sip_workers_pipes[pipe_index + WRITE_END]); lock_release(index_lock); LM_DBG("proc index: %d\n", pipe_index / 2); return 0; } static void mod_destroy(void) { LM_INFO("destroying module\n"); } static int sng_logger(int level, char *fmt, ...) { va_list args; static char buffer[256]; va_start(args, fmt); vsnprintf(buffer, 256, fmt, args); switch (level) { case SNGTC_LOGLEVEL_DEBUG: LM_GEN1(L_DBG, fmt, args); LM_DBG("%s\n", buffer); break; case SNGTC_LOGLEVEL_WARN: LM_GEN1(L_WARN, fmt, args); LM_WARN("%s\n", buffer); break; case SNGTC_LOGLEVEL_INFO: LM_GEN1(L_INFO, fmt, args); LM_INFO("%s\n", buffer); break; case SNGTC_LOGLEVEL_STATS: LM_GEN1(L_INFO, fmt, args); LM_INFO("%s\n", buffer); break; case SNGTC_LOGLEVEL_ERROR: LM_GEN1(L_ERR, fmt, args); LM_ERR("%s\n", buffer); break; case SNGTC_LOGLEVEL_CRIT: LM_GEN1(L_CRIT, fmt, args); LM_CRIT("%s\n", buffer); break; default: LM_GEN1(L_WARN, fmt, args); } va_end(args); return 0; } /** * store_sngtc_info - stores the caller's SDP body in the current dialog * in a struct sngtc_info * */ int store_sngtc_info(struct dlg_cell *dlg, str *body) { struct sngtc_info *info; str st; /* duplicate the body in shm and store the pointer in the dialog */ info = shm_malloc(sizeof(*info)); if (!info) { LM_ERR("no more shm\n"); return -1; } memset(info, 0, sizeof(*info)); lock_init(&info->lock); info->caller_sdp.s = shm_malloc(body->len + 2); if (!info->caller_sdp.s) { LM_ERR("no more shm\n"); goto exit; } info->caller_sdp.len = body->len + 2; /* SDP parser needs starting CRLF */ memcpy(info->caller_sdp.s, body->s - 2, info->caller_sdp.len); st.s = (void *)&info; st.len = sizeof(void *); LM_DBG("storing info ptr: %p\n", (void *) st.s); if (dlg_binds.store_dlg_value(dlg, &dlg_key_sngtc_info, &st) != 0) { LM_ERR("failed to store msg body in dialog\n"); goto exit; } LM_DBG("CALLER SDP: '%.*s' [%d]\n", info->caller_sdp.len, info->caller_sdp.s, info->caller_sdp.len); return 0; exit: if (info) { if (info->caller_sdp.s) shm_free(info->caller_sdp.s); shm_free(info); } return -1; } /** * sngtc_offer - will remove the SDP body of an early negotiation INVITE and * store it in the newly created dialog as a dlg_val. * * @return: 1 on success, negative on failure */ static int sngtc_offer(struct sip_msg *msg) { struct hdr_field *hf; struct lump *lump; struct dlg_cell *dlg; struct sngtc_info *info = NULL; str body, totag, st; if (dlg_binds.create_dlg(msg, 0) < 0) { LM_ERR("failed to create dialog\n"); return SNGTC_ERR; } dlg = dlg_binds.get_dlg(); if (!dlg) { LM_ERR("failed to fetch current dialog\n"); return SNGTC_ERR; } if (get_body(msg, &body) != 0 || body.len <= 0) { LM_ERR("can only do transcoding for early negotiation INVITES\n"); return SNGTC_SDP_ERR; } totag = get_to(msg)->tag_value; /* INVITE retransmissions will skip this part */ if (dlg_binds.fetch_dlg_value(dlg, &dlg_key_sngtc_info, &st, 0) != 0) { if (store_sngtc_info(dlg, &body) != 0) { LM_ERR("failed to create sngtc info struct\n"); return SNGTC_ERR; } /* register a callback to free the above */ if (dlg_binds.register_dlgcb(dlg, DLGCB_EXPIRED|DLGCB_FAILED|DLGCB_TERMINATED, sngtc_dlg_terminated, NULL, NULL) != 0) { LM_ERR("failed to register dialog callback\n"); return SNGTC_ERR; } /* for re-INVITES, just recreate the struct sngtc_info */ } else if (totag.s && totag.len != 0) { info = *(struct sngtc_info **)(st.s); free_transcoding_sessions(info->sessions); if (info->caller_sdp.s) shm_free(info->caller_sdp.s); if (info->modified_caller_sdp.s) shm_free(info->modified_caller_sdp.s); if (store_sngtc_info(dlg, &body) != 0) { LM_ERR("failed to create sngtc info struct\n"); return SNGTC_ERR; } shm_free(info); } LM_DBG("SDP body:\n"); LM_DBG("%.*s\n", body.len, body.s); hf = msg->content_type; /* delete the Content-Type header, we're setting up late negotiation */ lump = del_lump(msg, hf->name.s - msg->buf, hf->len, HDR_OTHER_T); if (!lump) { LM_ERR("no more pkg mem\n"); return SNGTC_ERR; } /* trim the entire SDP body */ lump = del_lump(msg, body.s - msg->buf, body.len, HDR_OTHER_T); if (!lump) { LM_ERR("no more pkg mem\n"); return SNGTC_ERR; } return 1; } /** * sngtc_get_codec_str - obtains the sngtc mapping for the given encoding name * * @return: on success: enum sngtc_codec_definition * on failure: -1 * * TODO: optimize: binary search (5 iterations instead of 25?) */ static int sngtc_get_codec_str(str *encode) { int i; for (i = 0; codec_str_mappings[i].bitrate != -1; i++) if (codec_str_mappings[i].name.len == encode->len && str_strcasecmp(&codec_str_mappings[i].name, encode) == 0) return codec_str_mappings[i].sng_codec; return -1; } /** * sngtc_get_codec_int - obtains the sngtc mapping for the given payload type * * @return: on success: enum sngtc_codec_definition * on failure: -1 * * TODO: optimize: binary search (3 iterations instead of 8?) */ static int sngtc_get_codec_int(str *payload) { int i; for (i = 0; codec_int_mappings[i].bitrate != -1; i++) if (codec_int_mappings[i].name.len == payload->len && str_strcasecmp(&codec_int_mappings[i].name, payload) == 0) return codec_int_mappings[i].sng_codec; return -1; } /** * remove_sdp_stream_attrs - removes all attributes from the specified stream * * @return: struct lump * with the removed information */ static struct lump *remove_sdp_stream_attrs(struct sip_msg *msg, struct sdp_stream_cell *stream) { struct lump *lump; char *attrs_end = NULL; LM_DBG("Removing all %d codecs from SDP stream: |%.*s|\n", stream->payloads_num, stream->payloads.len, stream->payloads.s); /* find the last parsed structure of the last attribute */ if (stream->payload_attr->fmtp_string.len > 0) { attrs_end = stream->payload_attr->fmtp_string.s + stream->payload_attr->fmtp_string.len; } else if (stream->payload_attr->rtp_params.len > 0) { attrs_end = stream->payload_attr->rtp_params.s + stream->payload_attr->rtp_params.len; } else if (stream->payload_attr->rtp_clock.len > 0) { attrs_end = stream->payload_attr->rtp_clock.s + stream->payload_attr->rtp_clock.len; } if (!attrs_end) { LM_ERR("invalid SDP stream received\n"); print_sdp_stream(stream, L_ERR); return NULL; } lump = del_lump(msg, stream->payloads.s - msg->buf, attrs_end - stream->payloads.s, HDR_OTHER_T); if (!lump) { LM_ERR("failed to add del lump\n"); return NULL; } return lump; } int replace_sdp_stream_port(struct sip_msg *msg, struct sdp_stream_cell *stream, unsigned int transcoder_port) { struct lump *lump; char *p; lump = del_lump(msg, stream->port.s - msg->buf, stream->port.len, HDR_OTHER_T); if (!lump) { LM_ERR("failed to add del lump\n"); return -1; } p = pkg_malloc(6); if (!p) { LM_ERR("no more pkg mem\n"); return -1; } sprintf(p, "%d", transcoder_port); if (!insert_new_lump_after(lump, p, strlen(p), HDR_OTHER_T)) { LM_ERR("no more pkg mem\n"); return -1; } return 0; } /** * write_sdp_stream_attr - writes a single attribute in the SDP stream, after * all the attributes had been previously wiped off with a del_lump operation * * If @reply is non-NULL, a 'c=IN IP4 X.X.X.X' line is added to the stream, * containing the transcoder card IP * * @return: 0 on success, negative on failure */ static int write_sdp_stream_attr(struct sip_msg *msg, struct lump *del_lump, struct sdp_payload_attr *attr, struct sngtc_codec_reply *reply) { char *buf; int len = 0; struct in_addr addr; len += 2 * attr->rtp_payload.len + A_LINE_PREFIX_LEN + INET_ADDRSTRLEN + attr->rtp_enc.len + 1 + 1 + attr->rtp_clock.len + 2 + C_LINE_LEN + 2 * CRLF_LEN; buf = pkg_malloc(len); if (!buf) { LM_ERR("no more pkg memory\n"); return SNGTC_ERR; } if (reply) { len = sprintf(buf, "%.*s%s" "c=IN IP4 ", attr->rtp_payload.len, attr->rtp_payload.s, CRLF); if (card_ip_b.s) len += sprintf(buf + len, "%.*s%s" "a=rtpmap:%.*s %.*s/%.*s", card_ip_b.len, card_ip_b.s, CRLF, attr->rtp_payload.len, attr->rtp_payload.s, attr->rtp_enc.len, attr->rtp_enc.s, attr->rtp_clock.len, attr->rtp_clock.s); else { addr.s_addr = ntohl(reply->a.codec_ip); if (!inet_ntop(AF_INET, &addr, buf + len, INET_ADDRSTRLEN)) { LM_ERR("Failed to convert IP from binary to string\n"); return SNGTC_ERR; } while (buf[len]) len++; len += sprintf(buf + len, "%s" "a=rtpmap:%.*s %.*s/%.*s", CRLF, attr->rtp_payload.len, attr->rtp_payload.s, attr->rtp_enc.len, attr->rtp_enc.s, attr->rtp_clock.len, attr->rtp_clock.s); } } else len = sprintf(buf, "%.*s%s" "a=rtpmap:%.*s %.*s/%.*s", attr->rtp_payload.len, attr->rtp_payload.s, CRLF, attr->rtp_payload.len, attr->rtp_payload.s, attr->rtp_enc.len, attr->rtp_enc.s, attr->rtp_clock.len, attr->rtp_clock.s); if (!insert_new_lump_after(del_lump, buf, len, HDR_OTHER_T)) { LM_ERR("failed to insert lump with codec result\n"); return SNGTC_ERR; } return 0; } /** * create_transcoding_session - creates a new Sangoma transcoding session and * adds it to the current dialog's list of ongoing transcoding sessions * * @info : output parameter, holds a list with all tc sessions on the card */ static struct sngtc_codec_reply *create_transcoding_session( struct sngtc_codec_request *request, struct sngtc_info *info) { struct sngtc_codec_reply *reply; struct sngtc_session_list *session; struct sngtc_request req; int rc; session = shm_malloc(sizeof(*session) + sizeof(*reply)); if (!session) { LM_ERR("no more shm mem\n"); return NULL; } reply = (struct sngtc_codec_reply *)(session + 1); session->next = NULL; session->reply = reply; LM_DBG("creating sng transcoding session\n"); req.type = REQ_CREATE_SESSION; req.response_fd = sip_workers_pipes[pipe_index + WRITE_END]; req.sng_req = *request; req.sng_reply = reply; if (write(sangoma_pipe[WRITE_END], &req, sizeof(req)) < 0) { LM_ERR("failed to write on sangoma pipe fd %d (%d: %s)\n", sangoma_pipe[WRITE_END], errno, strerror(errno)); goto out_free; } if (read(sip_workers_pipes[pipe_index + READ_END], &rc, sizeof(rc)) < 0) { LM_ERR("failed to read sangoma worker reply on pipe fd %d (%d: %s)\n", sip_workers_pipes[pipe_index + READ_END], errno, strerror(errno)); goto out_free; } if (rc != 0) { LM_ERR("failed to create sangoma transcoding session\n"); goto out_free; } LM_DBG("created new transcoding session\n"); sngtc_print_reply(L_DBG, reply); if (!info->sessions) info->sessions = info->last_session = session; else { info->last_session->next = session; info->last_session = session; } return reply; out_free: free_transcoding_sessions(info->sessions); shm_free(session); return NULL; } /** * match_codecs - intersects the attributes of SDP streams @s1 and @s2 and * stores the results in the @pair parameter * * @return: * SNGTC_ON - streams are incompatible, transcoding will be performed * SNGTC_OFF - streams have at least 1 common codec * SNGTC_UNSUP_CODECS - card cannot transcode from/into any codecs * SNGTC_BAD_SDP - one stream has no attributes at all */ static int match_codecs(struct sdp_stream_cell *s1, struct sdp_stream_cell *s2, struct codec_pair *pair) { struct sdp_payload_attr *att1, *att2, *tatt1, *tatt2; int common_codec, tc1, tc2; int c1, c2; int i, j; common_codec = tc1 = tc2 = 0; att1 = att2 = tatt1 = tatt2 = NULL; LM_DBG("stream 1: %d codecs\n", s1->payloads_num); for (i = 0; i < s1->payloads_num && !common_codec; i++) { att1 = s1->p_payload_attr[i]; LM_DBG("Codec: '%.*s'\n", att1->rtp_enc.len, att1->rtp_enc.s); if (att1->rtp_enc.len != 0) c1 = sngtc_get_codec_str(&att1->rtp_enc); else c1 = sngtc_get_codec_int(&att1->rtp_payload); if (!tc1 && c1 > 0) { tatt1 = att1; tc1 = c1; } LM_DBG("stream 2: %d codecs\n", s2->payloads_num); for (j = 0; j < s2->payloads_num; j++) { att2 = s2->p_payload_attr[j]; LM_DBG("Codec: '%.*s'\n", att2->rtp_enc.len, att2->rtp_enc.s); /* if the attribute has a 'a=rtpmap' line, get that enc, * otherwise use the default one for the payload type */ if (att2->rtp_enc.len != 0) c2 = sngtc_get_codec_str(&att2->rtp_enc); else c2 = sngtc_get_codec_int(&att2->rtp_payload); if (!tc2 && c2 > 0) { tatt2 = att2; tc2 = c2; } if (c1 == c2 && c1 != -1) { common_codec = 1; break; } } } if (!att1 || !att2) { LM_ERR("received bogus sdp with no attributes\n"); LM_ERR("caller:\n"); print_sdp_stream(s1, L_ERR); LM_ERR("callee:\n"); print_sdp_stream(s2, L_ERR); pair->status = SNGTC_BAD_SDP; return SNGTC_BAD_SDP; } if (!tc1 || !tc2) { LM_ERR("endpoints have no common codecs and at least one side " "contains only unsupported Sangoma codecs\n"); LM_ERR("caller:\n"); print_sdp_stream(s1, L_ERR); LM_ERR("callee:\n"); print_sdp_stream(s2, L_ERR); pair->status = SNGTC_UNSUP_CODECS; return SNGTC_UNSUP_CODECS; } if (!common_codec) { pair->att1 = tatt1; pair->att2 = tatt2; pair->tc1 = tc1; pair->tc2 = tc2; pair->status = SNGTC_ON; return SNGTC_ON; } pair->att1 = att1; pair->att2 = att2; pair->status = SNGTC_OFF; return SNGTC_OFF; } static int process_stream(struct sdp_stream_cell *s1, struct sdp_stream_cell *s2, str *src, str *dst, unsigned char idx) { int rc = 0, len; struct sdp_payload_attr *att = codec_matches[idx].att2; struct in_addr addr; if (s1->next && s2->next) rc = process_stream(s1->next, s2->next, src, dst, idx - 1); else if (s1->next || s2->next) LM_ERR("found different number of SDP streams - choosing min\n"); /* check if attribute port must be rewritten */ if (codec_matches[idx].status == SNGTC_ON) { LM_DBG("codec tc status: TC ON\n"); len = s1->port.s - src->s; memcpy(dst->s + dst->len, src->s, len); dst->len += len; dst->len += sprintf(dst->s + dst->len, "%d", codec_matches[idx].reply->b.codec_udp_port); src->s += len + s1->port.len; } else LM_DBG("codec tc status: TC OFF\n"); /* copy everything up to the rtp payload list (0 3 101 ...) */ len = s1->p_payload_attr[0]->rtp_payload.s - src->s; memcpy(dst->s + dst->len, src->s, len); dst->len += len; src->s += len; if (codec_matches[idx].status == SNGTC_ON) { dst->len += sprintf(dst->s + dst->len, "%.*s%s" "c=IN IP4 ", att->rtp_payload.len, att->rtp_payload.s, CRLF); if (card_ip_a.s) dst->len += sprintf(dst->s + dst->len, "%.*s%s" "a=rtpmap:%.*s %.*s/%.*s", card_ip_a.len, card_ip_a.s, CRLF, att->rtp_payload.len, att->rtp_payload.s, att->rtp_enc.len, att->rtp_enc.s, att->rtp_clock.len, att->rtp_clock.s); else { addr.s_addr = ntohl(codec_matches[idx].reply->b.codec_ip); if (!inet_ntop(AF_INET, &addr, dst->s + dst->len, INET_ADDRSTRLEN)) { LM_ERR("Failed to convert IP from binary to string\n"); return SNGTC_ERR; } while (dst->s[dst->len]) dst->len++; dst->len += sprintf(dst->s + dst->len, "%s" "a=rtpmap:%.*s %.*s/%.*s", CRLF, att->rtp_payload.len, att->rtp_payload.s, att->rtp_enc.len, att->rtp_enc.s, att->rtp_clock.len, att->rtp_clock.s); } } else dst->len += sprintf(dst->s + dst->len, "%.*s%s" "a=rtpmap:%.*s %.*s/%.*s", att->rtp_payload.len, att->rtp_payload.s, CRLF, att->rtp_payload.len, att->rtp_payload.s, att->rtp_enc.len, att->rtp_enc.s, att->rtp_clock.len, att->rtp_clock.s); return rc; } /** * performs the following operations at 200 OK time (early neg <-> late neg): * * - alters the callee's 200 OK message (adds the final decided codec) * - alters the caller's SDP (in memory), so it can be attached @ ACK * - opens transcoding sessions on the card if necessary * * Note: assumes all streams are on corresponding positions in both SDPs */ static int process_session(struct sip_msg *msg, struct sngtc_info *info, str *src, str *dst, struct sdp_session_cell *s1, struct sdp_session_cell *s2) { struct sdp_stream_cell *sm1, *sm2; struct sngtc_session_list *tc_session; struct sngtc_codec_request request; struct sngtc_codec_reply *reply = NULL; struct codec_pair pair; struct lump *lump, *nl; struct in_addr addr; int rc = 0, ret, tc_on = 0; int idx; char buf[INET_ADDRSTRLEN]; str repl; if (s1->next && s2->next) rc = process_session(msg, info, src, dst, s1->next, s2->next); else if (s1->next || s2->next) LM_ERR("endpoints have a different number of SDP sessions" " - choosing min number\n"); if (rc != 0) goto out; tc_session = info->sessions; for (idx = MAX_STREAMS - 1, sm1 = s1->streams, sm2 = s2->streams; sm1 && sm2; sm1 = sm1->next, sm2 = sm2->next, idx--) { ret = match_codecs(sm1, sm2, &pair); codec_matches[idx] = pair; switch (ret) { case SNGTC_OFF: LM_DBG("NO NEED FOR TRANSCODING\n"); /* delete codecs from 200 OK; write endpoint A codec */ /* ip and port stay the same */ lump = remove_sdp_stream_attrs(msg, sm2); if (!lump) { LM_ERR("failed to clear sdp codecs\n"); return SNGTC_SDP_ERR; } LM_DBG("sdp stream:\n"); print_sdp_stream(sm2, L_DBG); ret = write_sdp_stream_attr(msg, lump, pair.att2, NULL); if (ret != 0) { LM_ERR("failed to write sdp stream codec\n"); return ret; } break; case SNGTC_ON: tc_on = 1; if (is_processed(info)) goto use_existing_sessions; LM_DBG("TRANSCODING ([%d] %.*s:%.*s <--> [%d] %.*s:%.*s)\n", pair.tc1, s1->ip_addr.len, s1->ip_addr.s, sm1->port.len, sm1->port.s, pair.tc2, s2->ip_addr.len, s2->ip_addr.s, sm2->port.len, sm2->port.s); memset(&request, 0, sizeof(request)); request.usr_priv = NULL; /* Codec, ms, IP and port for side A */ request.a.codec_id = pair.tc1; request.a.ms = 0; sprintf(buf, "%.*s", s1->ip_addr.len, s1->ip_addr.s); ret = inet_pton(AF_INET, buf, &addr); if (ret != 1) { LM_ERR("failed to convert ip %s to binary form (%d)\n", s1->ip_addr.s, ret); return SNGTC_ERR; } request.a.host_ip = htonl(addr.s_addr); request.a.host_netmask = (unsigned int)-1; if (str2int(&sm1->port, &request.a.host_udp_port) != 0) LM_ERR("Failed to parse integer stored in port str '%.*s'\n", sm1->port.len, sm1->port.s); /* Codec, ms, IP and port for side B */ request.b.codec_id = pair.tc2; request.b.ms = 0; sprintf(buf, "%.*s", s2->ip_addr.len, s2->ip_addr.s); ret = inet_pton(AF_INET, buf, &addr); if (ret != 1) { LM_ERR("failed to convert ip %.*s to binary form (%d)\n", s2->ip_addr.len, s2->ip_addr.s, ret); return SNGTC_ERR; } request.b.host_ip = htonl(addr.s_addr); request.b.host_netmask = (unsigned int)-1; if (str2int(&sm2->port, &request.b.host_udp_port) != 0) LM_ERR("Failed to parse integer stored in port str '%.*s'\n", sm2->port.len, sm2->port.s); LM_DBG("Transcoding request: %d:%d <--> %d:%d\n", request.a.host_ip, request.a.host_udp_port, request.b.host_ip, request.b.host_udp_port); reply = create_transcoding_session(&request, info); if (!reply) { LM_ERR("Failed to create a transcoding session on the card\n"); return SNGTC_TC_ERR; } use_existing_sessions: LM_DBG("NEW TC SESSION!\n"); if (is_processed(info)) { reply = tc_session->reply; tc_session = tc_session->next; } codec_matches[idx].reply = reply; /** * delete codecs from 200 OK * write the common codec * replace IP with ip of Sangoma card * replace port with endpoint A newly opened port on card */ lump = remove_sdp_stream_attrs(msg, sm2); if (!lump) { LM_ERR("failed to clear sdp codecs\n"); return SNGTC_SDP_ERR; } nl = del_lump(msg, s2->ip_addr.s - msg->buf, s2->ip_addr.len, 0); if (!nl) { LM_ERR("failed to add del lump\n"); return SNGTC_ERR; } if (pkg_str_dup(&repl, &card_ip_b) != 0) { LM_ERR("failed to dup in pkg mem\n"); return SNGTC_ERR; } if (!insert_new_lump_after(nl, repl.s, repl.len, HDR_OTHER_T)) { LM_ERR("failed to insert lump with codec result\n"); return SNGTC_ERR; } if (replace_sdp_stream_port(msg, sm2, reply->a.codec_udp_port) != 0) { LM_ERR("failed to rewrite sdp stream port\n"); return SNGTC_ERR; } if (write_sdp_stream_attr(msg, lump, pair.att1, reply) != 0) { LM_ERR("failed to write sdp stream codecs\n"); return SNGTC_ERR; } break; case SNGTC_UNSUP_CODECS: LM_ERR("endpoints have no common codecs and at least one side " "contains only unsupported Sangoma codecs\n"); LM_ERR("caller:\n"); print_sdp_stream(sm1, L_ERR); LM_ERR("callee:\n"); print_sdp_stream(sm2, L_ERR); return SNGTC_SDP_ERR; case SNGTC_BAD_SDP: LM_ERR("received bogus sdp with no attributes\n"); LM_ERR("caller:\n"); print_sdp_stream(sm1, L_ERR); LM_ERR("callee:\n"); print_sdp_stream(sm2, L_ERR); return SNGTC_SDP_ERR; } } if (tc_on) { LM_DBG("transcoding: ON\n"); memcpy(dst->s + dst->len, src->s, s1->ip_addr.s - src->s); dst->len += s1->ip_addr.s - src->s; dst->len += sprintf(dst->s + dst->len, "%.*s", card_ip_a.len, card_ip_a.s); src->s += s1->ip_addr.s - src->s + s1->ip_addr.len; } else LM_DBG("transcoding: OFF\n"); rc |= process_stream(s1->streams, s2->streams, src, dst, MAX_STREAMS - 1); out: return rc; } static int w_sngtc_callee_answer(struct sip_msg *msg, char *gp_ip_a, char *gp_ip_b) { if (!gp_ip_a) { card_ip_a.s = card_ip_b.s = NULL; goto out; } if (fixup_get_svalue(msg, (gparam_p)gp_ip_a, &card_ip_a) != 0) { LM_ERR("failed to get fixup value for caller: bad pvar\n"); return SNGTC_ERR; } if (!gp_ip_b) { card_ip_b.s = NULL; goto out; } if (fixup_get_svalue(msg, (gparam_p)gp_ip_b, &card_ip_b) != 0) { LM_ERR("failed to get fixup value for callee: bad pvar\n"); return SNGTC_ERR; } out: return sngtc_callee_answer(msg); } /** * sngtc_callee_answer - handles the SDP offer of the callee * * At this point, we have both offers of the endpoints, and can decide whether * transcoding is needed or not. */ static int sngtc_callee_answer(struct sip_msg *msg) { struct dlg_cell *dlg; struct sngtc_info *info; str caller_sdp, dst; sdp_info_t sdp; str *sdp_ptr; int rc; LM_DBG("sngtc_callee_answer\n"); dlg = dlg_binds.get_dlg(); if (!dlg) { LM_ERR("failed to fetch current dialog\n"); return SNGTC_ERR; } /* get the pointer to the SDP body of the caller */ if (dlg_binds.fetch_dlg_value(dlg, &dlg_key_sngtc_info, &dst, 0) != 0) { LM_ERR("failed to fetch caller sdp\n"); return SNGTC_ERR; } info = *(struct sngtc_info **)(dst.s); sdp_ptr = &info->caller_sdp; LM_DBG("ptrs: %p %p\n", sdp_ptr, info->caller_sdp.s); caller_sdp.len = sdp_ptr->len; caller_sdp.s = sdp_ptr->s; lock_get(&info->lock); LM_DBG("FETCHED CALLER SDP: '%.*s' [%d]\n", caller_sdp.len, caller_sdp.s, caller_sdp.len); memset(&sdp, 0, sizeof(sdp)); if (parse_sdp_session(&caller_sdp, 0, NULL, &sdp) != 0) { LM_ERR("failed to parse caller sdp body\n"); rc = SNGTC_SDP_ERR; goto out_free; } if (parse_sdp(msg) != 0) { LM_ERR("failed to parse callee sdp body\n"); rc = SNGTC_SDP_ERR; goto out_free; } dst.s = sdp_buffer.s; dst.len = 0; /* perform all 200 OK SDP changes and pre-compute the ACK SDP body */ rc = process_session(msg, info, &caller_sdp, &dst, sdp.sessions, msg->sdp->sessions); if (rc != 0) { LM_ERR("failed to rewrite SDP bodies of the endpoints\n"); goto out_free; } if (!is_processed(info)) { dst.s = sdp_buffer.s; LM_DBG("caller ACK SDP: '%.*s'\n", dst.len, dst.s); info->modified_caller_sdp.s = shm_malloc(dst.len); if (!info->modified_caller_sdp.s) { LM_ERR("no more shm memory\n"); rc = SNGTC_ERR; goto out_free; } memcpy(info->modified_caller_sdp.s, dst.s, dst.len); info->modified_caller_sdp.len = dst.len; } info->flags |= PROCESSED_FLAG; lock_release(&info->lock); if (sdp.sessions) __free_sdp(&sdp); return 1; out_free: free_transcoding_sessions(info->sessions); lock_release(&info->lock); if (sdp.sessions) __free_sdp(&sdp); return rc; } /** * sngtc_caller_answer - attaches an SDP body to ACK requests */ static int sngtc_caller_answer(struct sip_msg *msg) { char *p; str body; struct dlg_cell *dlg; struct lump *lump; struct sngtc_info *info; int len; LM_DBG("processing ACK\n"); if (get_body(msg, &body) != 0 || body.len > 0) { LM_ERR("ACK should not contain a SDP body\n"); return SNGTC_ERR; } dlg = dlg_binds.get_dlg(); if (!dlg) { LM_ERR("failed to fetch current dialog\n"); return SNGTC_ERR; } /* get the SDP body from the INVITE which was mangled at 200 OK */ if (dlg_binds.fetch_dlg_value(dlg, &dlg_key_sngtc_info, &body, 0) != 0) { LM_ERR("failed to fetch caller sdp\n"); return SNGTC_ERR; } info = *(struct sngtc_info **)(body.s); /* duplicate the SDP in pkg mem for the lumps mechanism */ if (pkg_str_dup(&body, &info->modified_caller_sdp) != 0) { LM_ERR("failed to dup in pkg mem\n"); return SNGTC_ERR; } LM_DBG("Duplicated SDP: '%.*s'\n", body.len, body.s); lump = anchor_lump(msg, msg->content_length->name.s - msg->buf, 0); if (!lump) { LM_ERR("failed to insert anchor lump\n"); return SNGTC_ERR; } p = pkg_malloc(SDP_CONTENT_TYPE_LEN); if (!p) { LM_ERR("no more pkg memory\n"); return SNGTC_ERR; } /* add the Content-Type header */ memcpy(p, "Content-Type: application/sdp\r\n", SDP_CONTENT_TYPE_LEN); if (!insert_new_lump_before(lump, p, SDP_CONTENT_TYPE_LEN, 0)) { LM_ERR("failed to insert Content-Type lump\n"); return SNGTC_ERR; } LM_DBG("blen: %d\n", msg->content_length->body.len); lump = del_lump(msg, msg->content_length->body.s - msg->buf, msg->content_length->body.len, HDR_OTHER_T); if (!lump) { LM_ERR("failed to insert del lump for the content length\n"); return SNGTC_ERR; } p = pkg_malloc(CONTENT_LEN_DIGITS); if (!p) { LM_ERR("no more pkg memory\n"); return SNGTC_ERR; } LM_DBG("len: %d\n", body.len); len = sprintf(p, "%d", body.len); if (!insert_new_lump_after(lump, p, len, HDR_OTHER_T)) { LM_ERR("failed to insert Content-Length lump\n"); return SNGTC_ERR; } lump = anchor_lump(msg, msg->len - CRLF_LEN, 0); if (!lump) { LM_ERR("failed to insert anchor lump\n"); return SNGTC_ERR; } if (!insert_new_lump_before(lump, body.s, body.len, 0)) { LM_ERR("failed to insert SDP body lump\n"); return SNGTC_ERR; } return 1; } opensips-2.2.2/modules/sngtc/sngtc.h000066400000000000000000000075001300170765700174420ustar00rootroot00000000000000/** * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2013-06-05 created (liviu) * */ #ifndef __SNGTC_H__ #define __SNGTC_H__ #include #include "../../locking.h" #define SDP_CONTENT_TYPE_LEN (sizeof("Content-Type: application/sdp\r\n") - 1) #define A_LINE_PREFIX_LEN (sizeof("a=rtpmap:" - 1)) #define C_LINE_LEN (sizeof("c=IN IP4 xxx.xxx.xxx.xxx\r\n") - 1) #define SDP_BUFFER_SIZE 4096 #define MAX_STREAMS 30 #define CONTENT_LEN_DIGITS 5 #define PROCESSED_FLAG (1 << 0) #define READ_END 0 #define WRITE_END 1 #define is_processed(_info) (_info->flags & PROCESSED_FLAG) /** * @_level: OpenSIPS debug level * @_l: struct sngtc_codec_request_leg * @_start: string identifier of the leg */ #define sngtc_print_request_leg(_level, _l, _start) \ do { \ LM_GEN1(_level, "%s: [Codec: %d][ms: %d][ip: %d][nm: %d][port: %d]\n", \ _start, _l.codec_id, _l.ms, _l.host_ip, _l.host_netmask, \ _l.host_udp_port); \ } while (0) /** * @_level: OpenSIPS debug level * @_l: struct sngtc_codec_reply_leg * @_start: string identifier of the leg */ #define sngtc_print_reply_leg(_level, _l, _start) \ do { \ LM_GEN1(_level, "%s: [IP: %d][nm: %d][port: %d]\n", \ _start, _l.codec_ip, _l.codec_netmask, _l.codec_udp_port); \ LM_GEN1(_level, "%s: [Host IP: %d][nm: %d][port: %d][iana: %d]\n", \ _start, _l.host_ip, _l.host_netmask, _l.host_udp_port, \ _l.iana_code); \ } while (0) /** * @_level: OpenSIPS debug level * @_r: struct sngtc_codec_request */ #define sngtc_print_request(_level, _r) \ do { \ LM_GEN1(_level, "sngtc_codec_request with rtcp: %d\n", _r.rtcp_enable); \ sngtc_print_request_leg(_level, (_r).a, "A"); \ sngtc_print_request_leg(_level, (_r).b, "B"); \ } while (0) /** * @_level: OpenSIPS debug level * @_r: struct sngtc_codec_reply * */ #define sngtc_print_reply(_level, _r) \ do { \ LM_GEN1(_level, "sngtc_codec_reply with [mod_session: %d]" \ "[rtp_session: %d]\n", (_r)->codec_module_session_idx, \ (_r)->codec_rtp_session_idx); \ sngtc_print_reply_leg(_level, (_r)->a, "A"); \ sngtc_print_reply_leg(_level, (_r)->b, "B"); \ } while (0) #define SNGTC_SDP_ERR -1 #define SNGTC_TC_ERR -2 #define SNGTC_ERR -3 enum sng_module_status { SNGTC_UNSUP_CODECS = -2, SNGTC_BAD_SDP, SNGTC_OFF, SNGTC_ON, }; struct sngtc_session_list { struct sngtc_codec_reply *reply; struct sngtc_session_list *next; }; struct sngtc_info { str caller_sdp; str modified_caller_sdp; gen_lock_t lock; /* various session-related flags */ int flags; /* optional, used if transcoding is needed */ struct sngtc_session_list *sessions; struct sngtc_session_list *last_session; }; struct codec_pair { struct sdp_payload_attr *att1; struct sdp_payload_attr *att2; enum sngtc_codec_definition tc1; enum sngtc_codec_definition tc2; enum sng_module_status status; struct sngtc_codec_reply *reply; }; struct codec_mapping { str name; int bitrate; int mode; int sng_codec; }; #endif /* __SNGTC_H__ */ opensips-2.2.2/modules/sngtc/sngtc_proc.c000066400000000000000000000042221300170765700204560ustar00rootroot00000000000000/** * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2013-06-05 created (liviu) * */ #include #include "../../str.h" #include "../../pt.h" #include "../../dprint.h" #include "sngtc.h" #include "sngtc_proc.h" void sangoma_worker_loop(int proc_no) { struct sngtc_request req; int rc; close(sangoma_pipe[WRITE_END]); for (;;) { rc = 0; LM_DBG("reading from pipe\n"); if (read(sangoma_pipe[READ_END], &req, sizeof(req)) < 0) { LM_ERR("failed to read from pipe (%d - %s)\n", errno, strerror(errno)); continue; } switch (req.type) { case REQ_CREATE_SESSION: LM_DBG("CREATE request\n"); if (sngtc_create_transcoding_session(&req.sng_req, req.sng_reply, 0) != 0) { LM_ERR("failed to create sng transcoding session\n"); sngtc_print_request(L_ERR, req.sng_req); rc = 1; } break; case REQ_FREE_SESSION: LM_DBG("FREE request\n"); sngtc_print_reply(L_DBG, req.sng_reply); if (sngtc_free_transcoding_session(req.sng_reply) != 0) { LM_ERR("failed to free sng transcoding session\n"); sngtc_print_reply(L_ERR, req.sng_reply); rc = 1; } break; default: LM_ERR("dropping invalid sangoma request: %d\n", req.type); rc = 1; } if (write(req.response_fd, &rc, sizeof(rc)) < 0) LM_ERR("failed to write in response pipe fd %d (%d: %s)\n", req.response_fd, errno, strerror(errno)); } } opensips-2.2.2/modules/sngtc/sngtc_proc.h000066400000000000000000000024251300170765700204660ustar00rootroot00000000000000/** * Copyright (C) 2013 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2013-06-05 created (liviu) * */ #include enum request_type { REQ_CREATE_SESSION, REQ_FREE_SESSION }; /* information needed to make a request to the sangoma worker */ struct sngtc_request { enum request_type type; /* pipe descriptor used to send the response */ int response_fd; struct sngtc_codec_request sng_req; struct sngtc_codec_reply *sng_reply; }; extern int sangoma_pipe[2]; void sangoma_worker_loop(int proc_no); opensips-2.2.2/modules/snmpstats/000077500000000000000000000000001300170765700170675ustar00rootroot00000000000000opensips-2.2.2/modules/snmpstats/Makefile000066400000000000000000000015261300170765700205330ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs HAS_BUILDER = $(shell if which net-snmp-config >/dev/null 2>/dev/null;then echo YES; fi) ifeq ($(HAS_BUILDER),YES) # use autodetection DEFS += $(shell net-snmp-config --cflags) LIBS = $(shell net-snmp-config --netsnmp-agent-libs --external-agent-libs) INSTALLMIBDIR = $(cfg_prefix)$(shell net-snmp-config --prefix)/share/snmp/mibs else # use standard know paths DEFS +=-I$(LOCALBASE)/include LIBS =-L$(LOCALBASE)/lib -lnetsnmpmibs -lnetsnmpagent \ -lnetsnmphelpers -lnetsnmp INSTALLMIBDIR = $(cfg_prefix)/share/snmp/mibs endif CFLAGS+= auto_gen= NAME=snmpstats.so include ../../Makefile.modules install_module_custom: echo "installing mibs ..." mkdir -p $(INSTALLMIBDIR) $(INSTALL_CFG) mibs/OPENSER* $(INSTALLMIBDIR) opensips-2.2.2/modules/snmpstats/README000066400000000000000000000622641300170765700177610ustar00rootroot00000000000000SNMPStats Module (Simple Network Management Protocal Statistic Module) Jeffrey Magder SOMA Networks, Inc. Edited by Jeffrey Magder Copyright © 2006 SOMA Networks, Inc. Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. General Scalar Statistics 1.1.2. SNMP Tables 1.1.3. Alarm Monitoring 1.2. How it works 1.2.1. How the SNMPStats module gets its data 1.2.2. How data is moved from the SNMPStats module to a NOC 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. sipEntityType (String) 1.4.2. MsgQueueMinorThreshold (Integer) 1.4.3. MsgQueueMajorThreshold (Integer) 1.4.4. dlg_minor_threshold (Integer) 1.4.5. dlg_major_threshold (Integer) 1.4.6. snmpgetPath (String) 1.4.7. snmpCommunity (String) 1.5. Exported Functions 1.6. Installation and Running 1.6.1. Compiling the SNMPStats Module 1.6.2. Configuring SNMP daemon to allow connections from the SNMPStats module. 1.6.3. Configuring the SNMPStats module for communication with a Master Agent 1.6.4. Testing for a proper Configuration 2. Frequently Asked Questions List of Examples 1.1. Setting the sipEntityType parameter 1.2. Setting the MsgQueueMinorThreshold parameter 1.3. Setting the MsgQueueMajorThreshold parameter 1.4. Setting the dlg_minor_threshold parameter 1.5. Setting the dlg_major_threshold parameter 1.6. Setting the snmpgetPath parameter 1.7. Setting the snmpCommunity parameter Chapter 1. Admin Guide 1.1. Overview The SNMPStats module provides an SNMP management interface to OpenSIPS. Specifically, it provides general SNMP queryable scalar statistics, table representations of more complicated data such as user and contact information, and alarm monitoring capabilities. 1.1.1. General Scalar Statistics The SNMPStats module provides a number of general scalar statistics. Details are available in OPENSER-MIB, OPENSER-REG-MIB, OPENSER-SIP-COMMON-MIB, and OPENSER-SIP-SERVER-MIB. But briefly, these scalars are: openserSIPProtocolVersion, openserSIPServiceStartTime, openserSIPEntityType, openserSIPSummaryInRequests, openserSIPSummaryOutRequest, openserSIPSummaryInResponses, openserSIPSummaryOutResponses, openserSIPSummaryTotalTransactions, openserSIPCurrentTransactions, openserSIPNumUnsupportedUris, openserSIPNumUnsupportedMethods, openserSIPOtherwiseDiscardedMsgs, openserSIPProxyStatefulness openserSIPProxyRecordRoute, openserSIPProxyAuthMethod, openserSIPNumProxyRequireFailures, openserSIPRegMaxContactExpiryDuration, openserSIPRegMaxUsers, openserSIPRegCurrentUsers, openserSIPRegDfltRegActiveInterval, openserSIPRegAcceptedRegistrations, openserSIPRegRejectedRegistrations, openserMsgQueueDepth. openserCurNumDialogs, openserCurNumDialogsInProgress, openserCurNumDialogsInSetup, openserTotalNumFailedDialogSetups There are also scalars associated with alarms. They are as follows: openserMsgQueueMinorThreshold, openserMsgQueueMajorThreshold, openserMsgQueueDepthAlarmStatus, openserMsgQueueDepthMinorAlarm, openserMsgQueueDepthMajorAlarm, openserDialogLimitMinorThreshold, openserDialogLimitMajorThreshold, openserDialogUsageState, openserDialogLimitAlarmStatus, openserDialogLimitMinorAlarm, openserDialogLimitMajorAlarm 1.1.2. SNMP Tables The SNMPStats module provides several tables, containing more complicated data. The current available tables are: openserSIPPortTable, openserSIPMethodSupportedTable, openserSIPStatusCodesTable, openserSIPRegUserTable, openserSIPContactTable, openserSIPRegUserLookupTable 1.1.3. Alarm Monitoring If enabled, the SNMPStats module will monitor for alarm conditions. Currently, there are two alarm types defined. 1. The number of active dialogs has passed a minor or major threshold. The idea is that a network operation centre can be made aware that their SIP servers may be overloaded, without having to explicitly check for this condition. If a minor or major condition has occurred, then a openserDialogLimitMinorEvent trap or a openserDialogLimitMajorEvent trap will be generated, respectively. The minor and major thresholds are described in the parameters section below. 2. The number of bytes waiting to be consumed across all of OpenSIPS's listening ports has passed a minor or major threshold. The idea is that a network operation centre can be made aware that a machine hosting a SIP server may be entering a degraded state, and to investigate why this is so. If the number of bytes to be consumed passes a minor or major threshold, then a openserMsgQueueDepthMinorEvent or openserMsgQueueDepthMajorEvent trap will be sent out, respectively. Full details of these traps can be found in the distributions OPENSER-MIB file. 1.2. How it works 1.2.1. How the SNMPStats module gets its data The SNMPStats module uses OpenSIPSs internal statistic framework to collect most of its data. However, there are two exceptions. 1. The openserSIPRegUserTable and openserSIPContactTable rely on the usrloc modules callback system. Specifically, the SNMPStats module will receive callbacks whenever a user/contact is added to the system. 2. The SNMPStats modules openserSIPMsgQueueDepthMinorEvent and openserSIPMsgQueueDepthMajorEvent alarms rely on the OpenSIPS core to find out what interfaces, ports, and transports OpenSIPS is listening on. However,the module will actually query the proc file system to find out the number of bytes waiting to be consumed. (Currently, this will only work on systems providing the proc file system). 1.2.2. How data is moved from the SNMPStats module to a NOC We have now explained how the SNMPStats module gathers its data. We still have not explained how it exports this data to a NOC (Network Operations Centre) or administrator. The SNMPStats module expects to connect to a Master Agent. This would be a SNMP daemon running either on the same system as the OpenSIPS instance, or on another system. (Communication can take place over TCP, so there is no restriction that this daemon need be on the same system as OpenSIPS). If the master agent is unavailable when OpenSIPS first starts up, the SNMPStats module will continue to run. However, you will not be able to query it. Thankfully, the SNMPStats module continually looks for its master agent. So even if the master agent is started late, or if the link to the SNMPStats module is severed due to a temporary hardware failure or crashed and restarted master agent, the link will eventually be re-established. No data should be lost, and querying can begin again. To request for this data, you will need to query the master agent. The master agent will then redirect the request to the SNMPStats module, which will respond to the master agent, which will in turn respond to your request. 1.3. Dependencies 1.3.1. OpenSIPS Modules The SNMPStats module provides a plethora of statistics, some of which are collected by other modules. If the dependent modules are not loaded then those specific statistics will still be returned, but with zeroed values. All other statistics will continue to function normally. This means that the SNMPStats module has no hard/mandatory dependencies on other modules. There are however, soft dependencies, as follows: * usrloc - all scalars and tables relating to users and contacts are dependent on the usrloc module. If the module is not loaded, the respective tables will be empty. * dialog - all scalars relating to the number of dialogs are dependent on the presence of the dialog module. Furthermore, if the module is not loaded, then the openserDialogLimitMinorEvent, and openserDialogLimitMajorEvent alarm will be disabled. The contents of the openserSIPMethodSupportedTable change depending on which modules are loaded. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * Net SNMP DEV (libsnmp-dev on debian) - SNMP library (development files) must be installed at the time of compilation. Furthermore, there are several shared objects that must be loadable at the time SNMPStats is loaded. This means that SNMP lib must be installed (but not necessarily running) on the system that has loaded the SNMPStats module. (Details can be found in the compilation section below). * SNMP tools(snmp on debian) - SNMP tools package to provide the snmpget command (internally used by the SNMPStats module. 1.4. Exported Parameters 1.4.1. sipEntityType (String) This parameter describes the entity type for this OpenSIPS instance, and will be used in determining what is returned for the openserSIPEntityType scalar. Valid parameters are: registrarServer, redirectServer, proxyServer, userAgent, other Example 1.1. Setting the sipEntityType parameter ... modparam("snmpstats", "sipEntityType", "registrarServer") modparam("snmpstats", "sipEntityType", "proxyServer") ... Note that as the above example shows, you can define this parameter more than once. This is of course because a given OpenSIPS instance can take on more than one role. 1.4.2. MsgQueueMinorThreshold (Integer) The SNMPStats module monitors the number of bytes waiting to be consumed by OpenSIPS. If the number of bytes waiting to be consumed exceeds a minor threshold, the SNMPStats module will send out an openserMsgQueueDepthMinorEvent trap to signal that an alarm condition has occurred. The minor threshold is set with the MsgQueueMinorThreshold parameter. Example 1.2. Setting the MsgQueueMinorThreshold parameter ... modparam("snmpstats", "MsgQueueMinorThreshold", 2000) ... If this parameter is not set, then there will be no minor alarm monitoring. 1.4.3. MsgQueueMajorThreshold (Integer) The SNMPStats module monitors the number of bytes waiting to be consumed by OpenSIPS. If the number of bytes waiting to be consumed exceeds a major threshold, the SNMPStats module will send out an openserMsgQueueDepthMajorEvent trap to signal that an alarm condition has occurred. The major threshold is set with the MsgQueueMajorThreshold parameter. Example 1.3. Setting the MsgQueueMajorThreshold parameter ... modparam("snmpstats", "MsgQueueMajorThreshold", 5000) ... If this parameter is not set, then there will be no major alarm monitoring. 1.4.4. dlg_minor_threshold (Integer) The SNMPStats module monitors the number of active dialogs. If the number of active dialogs exceeds a minor threshold, the SNMPStats module will send out an openserDialogLimitMinorEvent trap to signal that an alarm condition has occurred. The minor threshold is set with the dlg_minor_threshold parameter. Example 1.4. Setting the dlg_minor_threshold parameter ... modparam("snmpstats", "dlg_minor_threshold", 500) ... If this parameter is not set, then there will be no minor alarm monitoring. 1.4.5. dlg_major_threshold (Integer) The SNMPStats module monitors the number of active dialogs. If the number of active dialogs exceeds a major threshold, the SNMPStats module will send out an openserDialogLimitMajorEvent trap to signal that an alarm condition has occurred. The major threshold is set with the dlg_major_threshold parameter. Example 1.5. Setting the dlg_major_threshold parameter ... modparam("snmpstats", "dlg_major_threshold", 750) ... If this parameter is not set, then there will be no major alarm monitoring. 1.4.6. snmpgetPath (String) The SNMPStats module provides the openserSIPServiceStartTime scalar. This scalar requires the SNMPStats module to perform a snmpget query to the master agent. You can use this parameter to set the path to your instance of SNMP's snmpget program. Default value is “/usr/local/bin/â€. Example 1.6. Setting the snmpgetPath parameter ... modparam("snmpstats", "snmpgetPath", "/my/custom/path/") ... 1.4.7. snmpCommunity (String) The SNMPStats module provides the openserSIPServiceStartTime scalar. This scalar requires the SNMPStats module to perform a snmpget query to the master agent. If you have defined a custom community string for the snmp daemon, you need to specify it with this parameter. Default value is “publicâ€. Example 1.7. Setting the snmpCommunity parameter ... modparam("snmpstats", "snmpCommunity", "customCommunityString") ... 1.5. Exported Functions Currently, there are no exported functions. 1.6. Installation and Running There are several things that need to be done to get the SNMPStats module compiled and up and running. 1.6.1. Compiling the SNMPStats Module In order for the SNMPStats module to compile, you will need to have installed the packages providing SNMP (Simple Network Management Protocol) libray and development files. The SNMPStats modules makefile requires that the SNMP script "net-snmp-config" can run. IMPORTANT: By default, SNMP loads mibs from /var/lib/mibs/ietf/.Keep in mind that you have to copy OpenSIPS mibs wherevere your mibs folder is. 1.6.2. Configuring SNMP daemon to allow connections from the SNMPStats module. The SNMPStats module will communicate with the SNMP Master Agent. This communication happens over a protocol known as AgentX. This means you need to have an SMP daemon (acting as Master Agent) running - it can be on the same machine or on a different one. First you need to turn on AgentX support. The exact location of the configuration file (snmpd.conf) may vary depending on your system. By default, via a package installation, it is located in: /etc/snmp/snmpd.conf. At the very end of the file add the following line: master agentx The line tells SNMP daemon to act as an AgentX master agent, so that it can accept connections from sub-agents such as the SNMPStats module. There is still one last step. Even though we have configured SNMP to have AgentX support, we still need to tell the daemon which interface and port to listen to for AgentX connections. This is done also via the configuration file (snmpd.conf) : agentXSocket tcp:localhost:705 This tells SNMP daemon to act as a master agent, listening on the localhost UDP interface at port 705. 1.6.3. Configuring the SNMPStats module for communication with a Master Agent The previous section explained how to set up a SNMP master agent to accept AgentX connections. We now need to tell the SNMPStats module how to communicate with this master agent. This is done by giving the SNMPStats module its own SNMP configuration file. The file must be named "snmpstats.conf", and must be in the same folder as the "snmpd.conf" file that was configured above. By default this would be: /etc/snmp/snmpstats.conf The default configuration file included with the distribution can be used, and contains the following: agentXSocket tcp:localhost:705 The above line tells the SNMPStats module to register with the master agent on the localhost, port 705. The parameters should match up with the snmpd process. Note that the master agent (snmpd) does not need to be present on the same machine as OpenSIPS. The localhost could be replaced with any other machine. 1.6.4. Testing for a proper Configuration As a quick test to make sure that the SNMPStats module sub-agent can successfully connect to the SNMP Master agent, be sure the snmpd service is stopped (/etc/init.d/snmpd stop) and manually start snmpd with the following: snmpd -f -Dagentx -x tcp:localhost:705 2>&1 | less You should see something similar to the following: No log handling enabled - turning on stderr logging registered debug token agentx, 1 ... Turning on AgentX master support. agentx/master: initializing... agentx/master: initializing... DONE NET-SNMP version 5.3.1 Now, start up OpenSIPS in another window. In the snmpd window, you should see a bunch of: agentx/master: handle pdu (req=0x2c58ebd4,trans=0x0,sess=0x0) agentx/master: open 0x81137c0 agentx/master: opened 0x814bbe0 = 6 with flags = a0 agentx/master: send response, stat 0 (req=0x2c58ebd4,trans=0x0,sess= 0x0) agentx_build: packet built okay The messages beginning with "agentx" are debug messages stating that something is happening with an AgentX sub-agent, appearing because of the -Dagentx snmpd switch. The large number of debug messages appear at startup as the SNMPStats module registers all of its scalars and tables with the Master Agent. If you receive these messages, then SNMPStats module and SNMP daemon have both been configured correctly. Chapter 2. Frequently Asked Questions 2.1. Where can I find more about SNMP? There are many websites that explain SNMP at all levels of detail. A great general introduction can be found at http://en.wikipedia.org/wiki/SNMP If you are interested in the nitty gritty details of the protocol, then please look at RFC 3410. RFC 3410 maps out the many other RFCs that define SNMP, and can be found at http://www.rfc-archive.org/getrfc.php?rfc=3410 INFO: Also if you want a nice tutorial for setting up snmpstats with OpenSIPS try this one. 2.2. Where can I find more about NetSNMP? NetSNMP source code, documentation, FAQs, and tutorials can all be found at http://net-snmp.sourceforge.net/. 2.3. Where can I find out more about AgentX? The full details of the AgentX protocol are explained in RFC 2741, available at: http://www.rfc-archive.org/getrfc.php?rfc=2741 2.4. Why am I not receiving any SNMP Traps? Assuming you've configured the trap thresholds in opensips.cfg with something similar to: modparam("snmpstats", "MsgQueueMinorThreshold", 1234) modparam("snmpstats", "MsgQueueMajorThreshold", 5678) modparam("snmpstats", "dlg_minor_threshold", 500) modparam("snmpstats", "dlg_minor_threshold", 600) Then either OpenSIPS is not reaching these thresholds (which is a good thing), or you haven't set up the trap monitor correctly. To prove this to yourself, you can start NetSNMP with: snmpd -f -Dtrap -x localhost:705 The -f tells the NetSNMP process to not daemonize, and the -Dtrap enables trap debug logs. You should see something similar to the following: registered debug token trap, 1 trap: adding callback trap sink ----- You should see both trapsess: adding to trap table ----- of these lines. Turning on AgentX master support. trap: send_trap 0 0 NET-SNMP-TC::linux trap: sending trap type=167, version=1 NET-SNMP version 5.3.1 If the two lines above did not appear, then you probably have not included the following in your snmpd.conf file. trap2sink machineToSendTrapsTo:machinesPortNumber. When a trap has been received by snmpd, the following will appear in the above output: sent_trap -1 -1 NET-SNMP-TC::linus sending trap type=167, version=1 You'll also need a program to collect the traps and do something with them (such as sending them to syslog). NetSNMP provides snmptrapd for this. Other solutions exist as well. Google is your friend. 2.5. OpenSIPS refuses to load the SNMPStats module. Why is it displaying "load_module: could not open module snmpstats.so"? On some systems, you may receive the following error at stdout or the log files depending on the configuration. ERROR: load_module: could not open module : libnetsnmpmibs.so.10: cannot open shared object file: No such file or directory. This means one of two things: 1. You did not install NetSNMP. ("make install" if building from source) 2. The dynamic linker cannot find the necessary libraries. In the second case, the fix is as follows: 1. find / -name "libnetsnmpmibs*" + You will find a copy unless you haven't installed NetSNMP. Make note of the path. 2. less /etc/ld.so.conf + If the file is missing the path from step 1, then add the path to ld.so.conf 3. ldconfig 4. Try starting OpenSIPS again. Alternatively, you may prefix your startup command with: LD_LIBRARY_PATH=/path/noted/in/step/one/above For example, on my system I ran: LD_LIBRARY_PATH=/usr/local/lib opensipsctl start 2.6. How can I learn what all the scalars and tables are? All scalars and tables are named in the SNMPStats module overview. The files OPENSER-MIB, OPENSER-REG-MIB, OPENSER-SIP-COMMON-MIB and OPENSER-SIP-SERVER-MIB contain the full definitions and descriptions. Note however, that the MIBs may actually contain scalars and tables which are currently not provided by the SNMPStats module. Therefore, it is better to use NetSNMP's snmptranslate as an alternative. Take the openserSIPEntityType scalar as an example. You can invoke snmptranslate as follows: snmptranslate -TBd openserSIPEntityType Which would result in something similar to the following: -- FROM OPENSER-SIP-COMMON-MIB -- TEXTUAL CONVENTION OpenSIPSSIPEntityRole SYNTAX BITS {other(0), userAgent(1), proxyServer(2), redirect Server(3), registrarServer(4)} MAX-ACCESS read-only STATUS current DESCRIPTION " This object identifies the list of SIP entities this row is related to. It is defined as a bit map. Each bit represents a type of SIP entity. If a bit has value 1, the SIP entity represented by this row plays the role of this entity type. If a bit has value 0, the SIP entity represented by this row does not act as this entity type Combinations of bits can be set when the SIP entity plays multiple SIP roles." 2.7. Why do snmpget, snmpwalk, and snmptable always time out? If your snmp operations are always returning with: "Timeout: No Response from localhost", then chances are that you are making the query with the wrong community string. Default installs will most likely use "public" as their default community strings. Grep your snmpd.conf file for the string "rocommunity", and use the result as your community string in your queries. 2.8. How do I use snmpget? NetSNMP's snmpget is used as follows: snmpget -v 2c -c theCommunityString machineToSendTheMachineTo scalar Element.0 For example, consider an snmpget on the openserSIPEntityType scalar, run on the same machine running the OpenSIPS instance, with the default "public" community string. The command would be: snmpget -v2c -c public localhost openserSIPEntityType.0 Which would result in something similar to: OPENSER-SIP-COMMON-MIB::openserSIPEntityType.0 = BITS: F8 \ other(0) userAgent(1) proxyServer(2) \ redirectServer(3) registrarServer(4) 2.9. How do I use snmptable? NetSNMP's snmptable is used as follows: snmptable -Ci -v 2c -c theCommunityString machineToSendTheMachineTo theTableName For example, consider the openserSIPRegUserTable. If we run the snmptable command on the same machine as the running OpenSIPS instance, configured with the default "public" community string. The command would be: snmptable -Ci -v 2c -c public localhost openserSIPRegUserTable Which would result in something similar to: index openserSIPUserUri openserSIPUserAuthenticationFailures 1 DefaultUser 0 2 bogdan 0 3 jeffrey.magder 0 2.10. Where can I find more about OpenSIPS? Take a look at http://www.opensips.org/. 2.11. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * User Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/users * Developer Mailing List - http://lists.opensips.org/cgi-bin/mailman/listinfo/devel E-mails regarding any stable OpenSIPS release should be sent to and e-mails regarding development versions should be sent to . If you want to keep the mail private, send it to . 2.12. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/snmpstats/alarm_checks.c000066400000000000000000000100151300170765700216440ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * This file groups together alarm checking and handling */ #include #include "../../socket_info.h" #include "alarm_checks.h" #include "sub_agent.h" #include "utilities.h" #include "openserObjects.h" #include "openserMIBNotifications.h" /* Returns the number of bytes currently waiting in the msg queue if they exceed * the threshold, and zero otherwise. If threshold_to_compare_to is < 0, then * no check will be performed and zero always returned. */ int check_msg_queue_alarm(int threshold_to_compare_to) { int bytesWaiting = 0; if (threshold_to_compare_to < 0) { return 0; } bytesWaiting = get_total_bytes_waiting(PROTO_NONE); if (bytesWaiting > threshold_to_compare_to) { return bytesWaiting; } return 0; } /* Returns the number of active dialogs if they exceed the threshold, and zero * otherwise. */ int check_dialog_alarm(int threshold_to_compare_to) { int num_dialogs; if (threshold_to_compare_to < 0) { return 0; } num_dialogs = get_statistic("active_dialogs"); if (num_dialogs > threshold_to_compare_to) { return num_dialogs; } return 0; } /* This function will be called periodically from an OpenSIPS timer. The first * time it is called, it will query OPENSER-MIB for configured thresholds. */ void run_alarm_check(unsigned int ticks, void * attr) { static int msg_queue_minor_threshold; static int msg_queue_major_threshold; static int dialog_minor_threshold; static int dialog_major_threshold; static char firstRun = 1; int bytesInMsgQueue; int numActiveDialogs; /* We only need to retrieve our thresholds the first time around */ if (firstRun) { register_with_master_agent(ALARM_AGENT_NAME); msg_queue_minor_threshold = get_msg_queue_minor_threshold(); msg_queue_major_threshold = get_msg_queue_major_threshold(); dialog_minor_threshold = get_dialog_minor_threshold(); dialog_major_threshold = get_dialog_major_threshold(); firstRun = 0; } /* We need to have this here in case the master agent fails and is * restarted. Without it, we won't be able to re-establish or AgentX * connection */ agent_check_and_process(0); /* Check for MsgQueue alarm conditions */ /* The retrieved number of bytes will be zero unless * there is an alarm condition. In this case the number * of bytes will be returned. */ bytesInMsgQueue = check_msg_queue_alarm(msg_queue_minor_threshold); if (bytesInMsgQueue != 0) { send_openserMsgQueueDepthMinorEvent_trap(bytesInMsgQueue, msg_queue_minor_threshold); } bytesInMsgQueue = check_msg_queue_alarm(msg_queue_major_threshold); if (bytesInMsgQueue != 0) { send_openserMsgQueueDepthMajorEvent_trap(bytesInMsgQueue, msg_queue_major_threshold); } /* Check for Dialog alarm conditions: */ numActiveDialogs = check_dialog_alarm(dialog_minor_threshold); if (numActiveDialogs != 0) { send_openserDialogLimitMinorEvent_trap(numActiveDialogs, dialog_minor_threshold); } numActiveDialogs = check_dialog_alarm(dialog_major_threshold); if (numActiveDialogs != 0) { send_openserDialogLimitMajorEvent_trap(numActiveDialogs, dialog_major_threshold); } } opensips-2.2.2/modules/snmpstats/alarm_checks.h000066400000000000000000000034651300170765700216640ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * This file groups together alarm checking and handling */ #ifndef _SNMPSTATS_ALARM_AGENT_ #define _SNMPSTATS_ALARM_AGENT_ #define ALARM_AGENT_FREQUENCY_IN_SECONDS 5 #define ALARM_AGENT_NAME "snmpstats_alarm_agent" /* Returns the number of bytes currently waiting in the msg queue if they exceed * the threshold, and zero otherwise. If threshold_to_compare_to is < 0, then * no check will be performed and zero always returned. */ int check_msg_queue_alarm(int threshold_to_compare_to); /* Returns the number of active dialogs if they exceed the threshold, and zero * otherwise. */ int check_dialog_alarm(int threshold_to_compare_to); /* This function will be called periodically from an OpenSIPS timer. The first * time it is called, it will query OPENSER-MIB for configured thresholds. */ void run_alarm_check(unsigned int ticks, void * attr); #endif opensips-2.2.2/modules/snmpstats/doc/000077500000000000000000000000001300170765700176345ustar00rootroot00000000000000opensips-2.2.2/modules/snmpstats/doc/snmpstats.xml000066400000000000000000000022711300170765700224140ustar00rootroot00000000000000 %docentities; ]> SNMPStats Module (Simple Network Management Protocal Statistic Module) &osipsname; Jeffrey Magder SOMA Networks, Inc.
jmagder@somanetworks.com
Jeffrey Magder
jmagder@somanetworks.com
2006 SOMA Networks, Inc. $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/snmpstats/doc/snmpstats_admin.xml000066400000000000000000000454531300170765700235750ustar00rootroot00000000000000 &adminguide;
Overview The SNMPStats module provides an SNMP management interface to OpenSIPS. Specifically, it provides general SNMP queryable scalar statistics, table representations of more complicated data such as user and contact information, and alarm monitoring capabilities.
General Scalar Statistics The SNMPStats module provides a number of general scalar statistics. Details are available in OPENSER-MIB, OPENSER-REG-MIB, OPENSER-SIP-COMMON-MIB, and OPENSER-SIP-SERVER-MIB. But briefly, these scalars are: openserSIPProtocolVersion, openserSIPServiceStartTime, openserSIPEntityType, openserSIPSummaryInRequests, openserSIPSummaryOutRequest, openserSIPSummaryInResponses, openserSIPSummaryOutResponses, openserSIPSummaryTotalTransactions, openserSIPCurrentTransactions, openserSIPNumUnsupportedUris, openserSIPNumUnsupportedMethods, openserSIPOtherwiseDiscardedMsgs, openserSIPProxyStatefulness openserSIPProxyRecordRoute, openserSIPProxyAuthMethod, openserSIPNumProxyRequireFailures, openserSIPRegMaxContactExpiryDuration, openserSIPRegMaxUsers, openserSIPRegCurrentUsers, openserSIPRegDfltRegActiveInterval, openserSIPRegAcceptedRegistrations, openserSIPRegRejectedRegistrations, openserMsgQueueDepth. openserCurNumDialogs, openserCurNumDialogsInProgress, openserCurNumDialogsInSetup, openserTotalNumFailedDialogSetups There are also scalars associated with alarms. They are as follows: openserMsgQueueMinorThreshold, openserMsgQueueMajorThreshold, openserMsgQueueDepthAlarmStatus, openserMsgQueueDepthMinorAlarm, openserMsgQueueDepthMajorAlarm, openserDialogLimitMinorThreshold, openserDialogLimitMajorThreshold, openserDialogUsageState, openserDialogLimitAlarmStatus, openserDialogLimitMinorAlarm, openserDialogLimitMajorAlarm
SNMP Tables The SNMPStats module provides several tables, containing more complicated data. The current available tables are: openserSIPPortTable, openserSIPMethodSupportedTable, openserSIPStatusCodesTable, openserSIPRegUserTable, openserSIPContactTable, openserSIPRegUserLookupTable
Alarm Monitoring If enabled, the SNMPStats module will monitor for alarm conditions. Currently, there are two alarm types defined. The number of active dialogs has passed a minor or major threshold. The idea is that a network operation centre can be made aware that their SIP servers may be overloaded, without having to explicitly check for this condition. If a minor or major condition has occurred, then a openserDialogLimitMinorEvent trap or a openserDialogLimitMajorEvent trap will be generated, respectively. The minor and major thresholds are described in the parameters section below. The number of bytes waiting to be consumed across all of OpenSIPS's listening ports has passed a minor or major threshold. The idea is that a network operation centre can be made aware that a machine hosting a SIP server may be entering a degraded state, and to investigate why this is so. If the number of bytes to be consumed passes a minor or major threshold, then a openserMsgQueueDepthMinorEvent or openserMsgQueueDepthMajorEvent trap will be sent out, respectively. Full details of these traps can be found in the distributions OPENSER-MIB file.
How it works
How the SNMPStats module gets its data The SNMPStats module uses OpenSIPSs internal statistic framework to collect most of its data. However, there are two exceptions. The openserSIPRegUserTable and openserSIPContactTable rely on the usrloc modules callback system. Specifically, the SNMPStats module will receive callbacks whenever a user/contact is added to the system. The SNMPStats modules openserSIPMsgQueueDepthMinorEvent and openserSIPMsgQueueDepthMajorEvent alarms rely on the OpenSIPS core to find out what interfaces, ports, and transports OpenSIPS is listening on. However,the module will actually query the proc file system to find out the number of bytes waiting to be consumed. (Currently, this will only work on systems providing the proc file system).
How data is moved from the SNMPStats module to a NOC We have now explained how the SNMPStats module gathers its data. We still have not explained how it exports this data to a NOC (Network Operations Centre) or administrator. The SNMPStats module expects to connect to a Master Agent. This would be a SNMP daemon running either on the same system as the OpenSIPS instance, or on another system. (Communication can take place over TCP, so there is no restriction that this daemon need be on the same system as OpenSIPS). If the master agent is unavailable when OpenSIPS first starts up, the SNMPStats module will continue to run. However, you will not be able to query it. Thankfully, the SNMPStats module continually looks for its master agent. So even if the master agent is started late, or if the link to the SNMPStats module is severed due to a temporary hardware failure or crashed and restarted master agent, the link will eventually be re-established. No data should be lost, and querying can begin again. To request for this data, you will need to query the master agent. The master agent will then redirect the request to the SNMPStats module, which will respond to the master agent, which will in turn respond to your request.
Dependencies
&osips; Modules The SNMPStats module provides a plethora of statistics, some of which are collected by other modules. If the dependent modules are not loaded then those specific statistics will still be returned, but with zeroed values. All other statistics will continue to function normally. This means that the SNMPStats module has no hard/mandatory dependencies on other modules. There are however, soft dependencies, as follows: usrloc - all scalars and tables relating to users and contacts are dependent on the usrloc module. If the module is not loaded, the respective tables will be empty. dialog - all scalars relating to the number of dialogs are dependent on the presence of the dialog module. Furthermore, if the module is not loaded, then the openserDialogLimitMinorEvent, and openserDialogLimitMajorEvent alarm will be disabled. The contents of the openserSIPMethodSupportedTable change depending on which modules are loaded.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: Net SNMP DEV (libsnmp-dev on debian) - SNMP library (development files) must be installed at the time of compilation. Furthermore, there are several shared objects that must be loadable at the time SNMPStats is loaded. This means that SNMP lib must be installed (but not necessarily running) on the system that has loaded the SNMPStats module. (Details can be found in the compilation section below). SNMP tools(snmp on debian) - SNMP tools package to provide the snmpget command (internally used by the SNMPStats module.
Exported Parameters
<varname>sipEntityType</varname> (String) This parameter describes the entity type for this OpenSIPS instance, and will be used in determining what is returned for the openserSIPEntityType scalar. Valid parameters are: registrarServer, redirectServer, proxyServer, userAgent, other Setting the <varname>sipEntityType</varname> parameter ... modparam("snmpstats", "sipEntityType", "registrarServer") modparam("snmpstats", "sipEntityType", "proxyServer") ... Note that as the above example shows, you can define this parameter more than once. This is of course because a given OpenSIPS instance can take on more than one role.
<varname>MsgQueueMinorThreshold</varname> (Integer) The SNMPStats module monitors the number of bytes waiting to be consumed by OpenSIPS. If the number of bytes waiting to be consumed exceeds a minor threshold, the SNMPStats module will send out an openserMsgQueueDepthMinorEvent trap to signal that an alarm condition has occurred. The minor threshold is set with the MsgQueueMinorThreshold parameter. Setting the <varname>MsgQueueMinorThreshold</varname> parameter ... modparam("snmpstats", "MsgQueueMinorThreshold", 2000) ... If this parameter is not set, then there will be no minor alarm monitoring.
<varname>MsgQueueMajorThreshold</varname> (Integer) The SNMPStats module monitors the number of bytes waiting to be consumed by OpenSIPS. If the number of bytes waiting to be consumed exceeds a major threshold, the SNMPStats module will send out an openserMsgQueueDepthMajorEvent trap to signal that an alarm condition has occurred. The major threshold is set with the MsgQueueMajorThreshold parameter. Setting the <varname>MsgQueueMajorThreshold</varname> parameter ... modparam("snmpstats", "MsgQueueMajorThreshold", 5000) ... If this parameter is not set, then there will be no major alarm monitoring.
<varname>dlg_minor_threshold</varname> (Integer) The SNMPStats module monitors the number of active dialogs. If the number of active dialogs exceeds a minor threshold, the SNMPStats module will send out an openserDialogLimitMinorEvent trap to signal that an alarm condition has occurred. The minor threshold is set with the dlg_minor_threshold parameter. Setting the <varname>dlg_minor_threshold</varname> parameter ... modparam("snmpstats", "dlg_minor_threshold", 500) ... If this parameter is not set, then there will be no minor alarm monitoring.
<varname>dlg_major_threshold</varname> (Integer) The SNMPStats module monitors the number of active dialogs. If the number of active dialogs exceeds a major threshold, the SNMPStats module will send out an openserDialogLimitMajorEvent trap to signal that an alarm condition has occurred. The major threshold is set with the dlg_major_threshold parameter. Setting the <varname>dlg_major_threshold</varname> parameter ... modparam("snmpstats", "dlg_major_threshold", 750) ... If this parameter is not set, then there will be no major alarm monitoring.
<varname>snmpgetPath</varname> (String) The SNMPStats module provides the openserSIPServiceStartTime scalar. This scalar requires the SNMPStats module to perform a snmpget query to the master agent. You can use this parameter to set the path to your instance of SNMP's snmpget program. Default value is /usr/local/bin/. Setting the <varname>snmpgetPath</varname> parameter ... modparam("snmpstats", "snmpgetPath", "/my/custom/path/") ...
<varname>snmpCommunity</varname> (String) The SNMPStats module provides the openserSIPServiceStartTime scalar. This scalar requires the SNMPStats module to perform a snmpget query to the master agent. If you have defined a custom community string for the snmp daemon, you need to specify it with this parameter. Default value is public. Setting the <varname>snmpCommunity</varname> parameter ... modparam("snmpstats", "snmpCommunity", "customCommunityString") ...
Exported Functions Currently, there are no exported functions.
Installation and Running There are several things that need to be done to get the SNMPStats module compiled and up and running.
Compiling the SNMPStats Module In order for the SNMPStats module to compile, you will need to have installed the packages providing SNMP (Simple Network Management Protocol) libray and development files. The SNMPStats modules makefile requires that the SNMP script "net-snmp-config" can run. IMPORTANT: By default, SNMP loads mibs from /var/lib/mibs/ietf/.Keep in mind that you have to copy &osips; mibs wherevere your mibs folder is.
Configuring SNMP daemon to allow connections from the SNMPStats module. The SNMPStats module will communicate with the SNMP Master Agent. This communication happens over a protocol known as AgentX. This means you need to have an SMP daemon (acting as Master Agent) running - it can be on the same machine or on a different one. First you need to turn on AgentX support. The exact location of the configuration file (snmpd.conf) may vary depending on your system. By default, via a package installation, it is located in: /etc/snmp/snmpd.conf. At the very end of the file add the following line: master agentx The line tells SNMP daemon to act as an AgentX master agent, so that it can accept connections from sub-agents such as the SNMPStats module. There is still one last step. Even though we have configured SNMP to have AgentX support, we still need to tell the daemon which interface and port to listen to for AgentX connections. This is done also via the configuration file (snmpd.conf) : agentXSocket tcp:localhost:705 This tells SNMP daemon to act as a master agent, listening on the localhost UDP interface at port 705.
Configuring the SNMPStats module for communication with a Master Agent The previous section explained how to set up a SNMP master agent to accept AgentX connections. We now need to tell the SNMPStats module how to communicate with this master agent. This is done by giving the SNMPStats module its own SNMP configuration file. The file must be named "snmpstats.conf", and must be in the same folder as the "snmpd.conf" file that was configured above. By default this would be: /etc/snmp/snmpstats.conf The default configuration file included with the distribution can be used, and contains the following: agentXSocket tcp:localhost:705 The above line tells the SNMPStats module to register with the master agent on the localhost, port 705. The parameters should match up with the snmpd process. Note that the master agent (snmpd) does not need to be present on the same machine as OpenSIPS. The localhost could be replaced with any other machine.
Testing for a proper Configuration As a quick test to make sure that the SNMPStats module sub-agent can successfully connect to the SNMP Master agent, be sure the snmpd service is stopped (/etc/init.d/snmpd stop) and manually start snmpd with the following: snmpd -f -Dagentx -x tcp:localhost:705 2>&1 | less You should see something similar to the following: No log handling enabled - turning on stderr logging registered debug token agentx, 1 ... Turning on AgentX master support. agentx/master: initializing... agentx/master: initializing... DONE NET-SNMP version 5.3.1 Now, start up OpenSIPS in another window. In the snmpd window, you should see a bunch of: agentx/master: handle pdu (req=0x2c58ebd4,trans=0x0,sess=0x0) agentx/master: open 0x81137c0 agentx/master: opened 0x814bbe0 = 6 with flags = a0 agentx/master: send response, stat 0 (req=0x2c58ebd4,trans=0x0,sess=0x0) agentx_build: packet built okay The messages beginning with "agentx" are debug messages stating that something is happening with an AgentX sub-agent, appearing because of the -Dagentx snmpd switch. The large number of debug messages appear at startup as the SNMPStats module registers all of its scalars and tables with the Master Agent. If you receive these messages, then SNMPStats module and SNMP daemon have both been configured correctly.
opensips-2.2.2/modules/snmpstats/doc/snmpstats_faq.xml000066400000000000000000000256561300170765700232570ustar00rootroot00000000000000 &faqguide; Where can I find more about SNMP? There are many websites that explain SNMP at all levels of detail. A great general introduction can be found at http://en.wikipedia.org/wiki/SNMP If you are interested in the nitty gritty details of the protocol, then please look at RFC 3410. RFC 3410 maps out the many other RFCs that define SNMP, and can be found at http://www.rfc-archive.org/getrfc.php?rfc=3410 INFO: Also if you want a nice tutorial for setting up snmpstats with &osips; try this one. Where can I find more about NetSNMP? NetSNMP source code, documentation, FAQs, and tutorials can all be found at http://net-snmp.sourceforge.net/. Where can I find out more about AgentX? The full details of the AgentX protocol are explained in RFC 2741, available at: http://www.rfc-archive.org/getrfc.php?rfc=2741 Why am I not receiving any SNMP Traps? Assuming you've configured the trap thresholds in opensips.cfg with something similar to: modparam("snmpstats", "MsgQueueMinorThreshold", 1234) modparam("snmpstats", "MsgQueueMajorThreshold", 5678) modparam("snmpstats", "dlg_minor_threshold", 500) modparam("snmpstats", "dlg_minor_threshold", 600) Then either OpenSIPS is not reaching these thresholds (which is a good thing), or you haven't set up the trap monitor correctly. To prove this to yourself, you can start NetSNMP with: snmpd -f -Dtrap -x localhost:705 The -f tells the NetSNMP process to not daemonize, and the -Dtrap enables trap debug logs. You should see something similar to the following: registered debug token trap, 1 trap: adding callback trap sink ----- You should see both trapsess: adding to trap table ----- of these lines. Turning on AgentX master support. trap: send_trap 0 0 NET-SNMP-TC::linux trap: sending trap type=167, version=1 NET-SNMP version 5.3.1 If the two lines above did not appear, then you probably have not included the following in your snmpd.conf file. trap2sink machineToSendTrapsTo:machinesPortNumber. When a trap has been received by snmpd, the following will appear in the above output: sent_trap -1 -1 NET-SNMP-TC::linus sending trap type=167, version=1 You'll also need a program to collect the traps and do something with them (such as sending them to syslog). NetSNMP provides snmptrapd for this. Other solutions exist as well. Google is your friend. OpenSIPS refuses to load the SNMPStats module. Why is it displaying "load_module: could not open module snmpstats.so"? On some systems, you may receive the following error at stdout or the log files depending on the configuration. ERROR: load_module: could not open module </usr/local/lib/opensips/modules/snmpstats.so>: libnetsnmpmibs.so.10: cannot open shared object file: No such file or directory. This means one of two things: You did not install NetSNMP. ("make install" if building from source) The dynamic linker cannot find the necessary libraries. In the second case, the fix is as follows: find / -name "libnetsnmpmibs*" You will find a copy unless you haven't installed NetSNMP. Make note of the path. less /etc/ld.so.conf If the file is missing the path from step 1, then add the path to ld.so.conf ldconfig Try starting OpenSIPS again. Alternatively, you may prefix your startup command with: LD_LIBRARY_PATH=/path/noted/in/step/one/above For example, on my system I ran: LD_LIBRARY_PATH=/usr/local/lib opensipsctl start How can I learn what all the scalars and tables are? All scalars and tables are named in the SNMPStats module overview. The files OPENSER-MIB, OPENSER-REG-MIB, OPENSER-SIP-COMMON-MIB and OPENSER-SIP-SERVER-MIB contain the full definitions and descriptions. Note however, that the MIBs may actually contain scalars and tables which are currently not provided by the SNMPStats module. Therefore, it is better to use NetSNMP's snmptranslate as an alternative. Take the openserSIPEntityType scalar as an example. You can invoke snmptranslate as follows: snmptranslate -TBd openserSIPEntityType Which would result in something similar to the following: -- FROM OPENSER-SIP-COMMON-MIB -- TEXTUAL CONVENTION OpenSIPSSIPEntityRole SYNTAX BITS {other(0), userAgent(1), proxyServer(2), redirectServer(3), registrarServer(4)} MAX-ACCESS read-only STATUS current DESCRIPTION " This object identifies the list of SIP entities this row is related to. It is defined as a bit map. Each bit represents a type of SIP entity. If a bit has value 1, the SIP entity represented by this row plays the role of this entity type. If a bit has value 0, the SIP entity represented by this row does not act as this entity type Combinations of bits can be set when the SIP entity plays multiple SIP roles." Why do snmpget, snmpwalk, and snmptable always time out? If your snmp operations are always returning with: "Timeout: No Response from localhost", then chances are that you are making the query with the wrong community string. Default installs will most likely use "public" as their default community strings. Grep your snmpd.conf file for the string "rocommunity", and use the result as your community string in your queries. How do I use snmpget? NetSNMP's snmpget is used as follows: snmpget -v 2c -c theCommunityString machineToSendTheMachineTo scalarElement.0 For example, consider an snmpget on the openserSIPEntityType scalar, run on the same machine running the OpenSIPS instance, with the default "public" community string. The command would be: snmpget -v2c -c public localhost openserSIPEntityType.0 Which would result in something similar to: OPENSER-SIP-COMMON-MIB::openserSIPEntityType.0 = BITS: F8 \ other(0) userAgent(1) proxyServer(2) \ redirectServer(3) registrarServer(4) How do I use snmptable? NetSNMP's snmptable is used as follows: snmptable -Ci -v 2c -c theCommunityString machineToSendTheMachineTo theTableName For example, consider the openserSIPRegUserTable. If we run the snmptable command on the same machine as the running OpenSIPS instance, configured with the default "public" community string. The command would be: snmptable -Ci -v 2c -c public localhost openserSIPRegUserTable Which would result in something similar to: index openserSIPUserUri openserSIPUserAuthenticationFailures 1 DefaultUser 0 2 bogdan 0 3 jeffrey.magder 0 Where can I find more about OpenSIPS? Take a look at &osipshomelink;. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: User Mailing List - &osipsuserslink; Developer Mailing List - &osipsdevlink; E-mails regarding any stable &osips; release should be sent to &osipsusersmail; and e-mails regarding development versions should be sent to &osipsdevmail;. If you want to keep the mail private, send it to &osipshelpmail;. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/snmpstats/hashTable.c000066400000000000000000000177051300170765700211400ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Hash Stuff; * * For an overview of its structures, please see hashTable.h * * Potential Performance Improvements: Pass the length of the aor strings around * everywhere, so we don't have to calculate it ourselves. * */ #include #include #include "hashTable.h" #include "../../dprint.h" #include "../../mem/mem.h" #include #include #include #include "openserSIPRegUserTable.h" /* Calculates and returns a hash index to a hash table. The index is calculated * by summing up all the characters specified with theString, and using the * hashTableSize as the modulus. */ int calculateHashSlot(char *theString, int hashTableSize) { char *currentCharacter = theString; int runningTotal = 0; while (*currentCharacter != '\0') { runningTotal += *currentCharacter; currentCharacter++; } return runningTotal % hashTableSize; } /* Searches the hash table specified as theTable, of size 'size', for a record * indexed with 'aor'. If a match is found, then an aorToIndextStruct_t * structure is returned. * * This function is called to discover the map between OpenSER's "aor" * (Address of Records) indexing scheme, and the SNMPStats modules integer * indexing scheme for its contact/user data. * * Returns: the aorToIndexStruct_t mapping structure if a match was found, * or NULL otherwise. */ aorToIndexStruct_t *findHashRecord(hashSlot_t *theTable, char *aor, int size) { int hashIndex = calculateHashSlot(aor, size); int aorStringLength = strlen(aor); aorToIndexStruct_t *currentRecord = theTable[hashIndex].first; while (currentRecord != NULL) { /* If the strings are the same length and the same in every * other way, then return the given record. */ if (currentRecord->aorLength == aorStringLength && memcmp(currentRecord->aor, aor, aorStringLength)==0) { return currentRecord; } currentRecord = currentRecord->next; } return NULL; } /* Returns a chunk of memory large enough to store 'size' hashSlot's. The * table will contain mappings between OpenSER's "aor" user/contact indexing * scheme, and SNMPStats integer indexing scheme */ hashSlot_t *createHashTable(int size) { hashSlot_t *hashTable = NULL; int numberOfBytes = sizeof(hashSlot_t)*size; hashTable = pkg_malloc(numberOfBytes); if (!hashTable) { LM_ERR("no more pkg memory"); return NULL; } memset(hashTable, 0, numberOfBytes); return hashTable; } /* Inserts the record specified with 'theRecord' into our hash table. */ void insertHashRecord(hashSlot_t *theTable, aorToIndexStruct_t *theRecord, int size) { int hashIndex = calculateHashSlot(theRecord->aor, size); /* Link up this record backward so that it points to whatever the last * 'last element' was. */ theRecord->prev = theTable[hashIndex].last; /* This is the first record in the hash table, so assign the first and * last pointers to this record. */ if (theTable[hashIndex].last == NULL) { theTable[hashIndex].last = theRecord; theTable[hashIndex].first = theRecord; } else { /* Make the element that was previously the last element point * to this new record, as its next element. */ theTable[hashIndex].last->next = theRecord; /* Reassign the 'final element' pointer to this new record. */ theTable[hashIndex].last = theRecord; } } /* * This function will search the provided hash table for an entry indexed by * 'aor'. If an entry is found then: * * - Its numContacts counter will be decremented. * - If its numContacts counter reaches zero, then the entry will be removed * from the hash table. * */ void deleteUser(hashSlot_t *theTable, char *aor, int hashTableSize) { int hashIndex = calculateHashSlot(aor, hashTableSize); int searchStringLength = strlen(aor); aorToIndexStruct_t *currentRecord = theTable[hashIndex].first; while (currentRecord != NULL) { /* First make sure both strings are the same length. If so, * then compare all bytes. If this succeeds, then we need to * link up the previous and next element together. */ if (currentRecord->aorLength == searchStringLength && memcmp(currentRecord->aor, aor, searchStringLength) == 0) { currentRecord->numContacts--; /* There are still contacts relying on this user, so * don't delete anything. */ if (currentRecord->numContacts > 0) { return; } /* There are no more contacts relying on this user, so * delete the row from the table. */ deleteRegUserRow(currentRecord->userIndex); /* Maintenance of the hash table */ if (currentRecord->prev == NULL) { /* Edge Case: First element in list was just deleted, so set * up the first element to point to the one after the one * just deleted */ theTable[hashIndex].first = currentRecord->next; } else { /* Not the first element, so hook up the previous node to * the node after the one just deleted. */ currentRecord->prev->next = currentRecord->next; } if (currentRecord->next == NULL) { /* Edge Case: The last element has been targetted for * deletion. So move the pointer to the node just before * this one. */ theTable[hashIndex].last = currentRecord->prev; } else { /* Not the last element, so hook up next nodes previous * element to this nodes previous. */ currentRecord->next->prev = currentRecord->prev; } pkg_free(currentRecord); /* We are done, so just return. */ return; } /* Advance to the next records. */ currentRecord = currentRecord->next; } } /* Returns a aorToIndexStruct_t, holding the given 'userIndex' and 'aor'. The * structure is used to map between the "aor" (OpenSER's way of indexing * users/contacts), and the SNMPStats user and contact integer indexes. * * NOTE: that this record does not make a copy of aor, but instead points * directly to the parameter. Therefore make sure that aor is not on the stack, * and is not going to disappear before this record is deleted. */ aorToIndexStruct_t *createHashRecord(int userIndex, char *aor) { int aorLength =strlen(aor); aorToIndexStruct_t *theRecord = pkg_malloc(sizeof(aorToIndexStruct_t)+ (aorLength+1)* sizeof(char)); if (theRecord == NULL) { LM_ERR("failed to create a mapping record for %s", aor); return NULL; } memset(theRecord, 0, sizeof(aorToIndexStruct_t)); theRecord->aor = (char*)theRecord + sizeof(aorToIndexStruct_t); memcpy(theRecord->aor, aor, aorLength ); theRecord->aor[aorLength] = '\0'; theRecord->aorLength = aorLength; theRecord->userIndex = userIndex; theRecord->numContacts = 1; return theRecord; } /* Debugging function. Prints off an entire hash slot. */ void printHashSlot(hashSlot_t *theTable, int index) { aorToIndexStruct_t *currentRecord = theTable[index].first; LM_ERR("dumping Hash Slot #%d\n", index); while (currentRecord != NULL) { LM_ERR( "\tString: %s - Index: %d\n", currentRecord->aor, currentRecord->userIndex); currentRecord = currentRecord->next; } } opensips-2.2.2/modules/snmpstats/hashTable.h000066400000000000000000000147441300170765700211450ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Hash Stuff; * * This file describes several structure. In general, it was necessary to map * between OpenSER's "aor" (Address of Record) and string indexing mechanisms, * and the SNMPStats modules integer indexing scheme for users and contacts. * While it would have been a more natural fit to use string indexes in the * SNMPStats module, SNMP limitations precluded this. * * aorToIndexStruct: maps an aor to: * - a userIndex, to uniquely identify each RegUserTable SNMP row * - a contactList, containing all contacts for the user specified by * userIndex. * * The aorToIndexStruct also contains a numContacts counter. Each time a new * contact is associated with the aor (user), the counter is incremented. Each * time a contact is dissasociated (due to an expiration), the counter is * decremented. When the counter reaches zero the structure will be deleted. * * contactToIndexStruct: maps a contact name to: * - a contactIndex, used to uniquely identify each ContactTable SNMP row. * */ #ifndef HASHSLOT_H #define HASHSLOT_H /* * Used to map between a 'contact' name (OpenSER's index) and a contact index. * (SNMPStats Index) */ typedef struct contactToIndexStruct { char *contactName; int contactIndex; struct contactToIndexStruct *next; } contactToIndexStruct_t; /* * Used to map between an 'aor' (OpenSIPS index) and a user index. (SNMPStats * index). Since each user can have multiple contacts, the structure also has a * 'contactIndex', and a reference to the contactToIndexStruct list. */ typedef struct aorToIndexStruct { /* Pointer to the actual address record in the given SNMP row. */ char *aor; int aorLength; /* Points to the user index, which is used to uniquely identify each * SNMP row in a table. */ int userIndex; /* Each contact needs a unique index, for each user. This value should * be incremented each time a contact is added. This way, we can know * what index to use for the next addition to the contactList. */ int contactIndex; /* Pointer to the contact list. */ contactToIndexStruct_t *contactList; struct aorToIndexStruct *prev; /* The structure is part of a hash table, so this element is needed so * that we can point to the next element in the colission slot. */ struct aorToIndexStruct *next; /* This counter will be incremented when a new contact is associated * with this user record, and will be decremented each time an * associated contact is removed. When the count reaches 0, it is safe * to remove this record. */ int numContacts; } aorToIndexStruct_t; typedef struct hashSlot { /* Number of elements in this list. */ int numberOfElements; /* First element in the list. */ struct aorToIndexStruct* first; /* Last element in the list. This is here for optimization purposes. * It stands to reason that things added later will need to be deleted * later. So they should be added to the end of the list. This way, * things that are to be deleted sooner will be at the front of the * list. */ struct aorToIndexStruct* last; } hashSlot_t; /******************************************************************* * More detailed function definitions can be found in hashTable.c */ /* Returns a aorToIndexStruct_t, holding the given 'userIndex' and 'aor'. The * structure is used to map between the "aor" (OpenSER's way of indexing * users/contacts), and the SNMPStats user and contact integer indexes. * * NOTE: that this record does not make a copy of aor, but instead points * directly to the parameter. Therefore make sure that aor is not on the stack, * and is not going to disappear before this record is deleted. */ aorToIndexStruct_t *createHashRecord(int userIndex, char *aor); /* Returns a chunk of memory large enough to store 'size' hashSlot's. The * table will contain mappings between OpenSER's "aor" user/contact indexing * scheme, and SNMPStats integer indexing scheme */ hashSlot_t *createHashTable(int size); /* Calculates and returns a hash index to a hash table. The index is calculated * by summing up all the characters specified with theString, and using the * hashTableSize as the modulus. */ int calculateHashSlot(char *theString, int hashTableSize); /* Searches the hash table specified as theTable, of size 'size', for a record * indexed with 'aor'. If a match is found, then an aorToIndextStruct_t * structure is returned. * * This function is called to discover the map between OpenSER's "aor" * (Address of Records) indexing scheme, and the SNMPStats modules integer * indexing scheme for its contact/user data. * * Returns: the aorToIndexStruct_t mapping structure if a match was found, * or NULL otherwise. */ aorToIndexStruct_t *findHashRecord(hashSlot_t *theTable, char *aor, int size); /* Inserts theRecord into an appropriate place in theTable, when size is given. */ void insertHashRecord(hashSlot_t *theTable, aorToIndexStruct_t *theRecord, int size); /* Debugging function. Prints off an entire hash slot. */ void printHashSlot(hashSlot_t *theTable, int index); /* If a record is found with string aor in theTable, it is deleted and its * SNMPStats user integer index is returned. */ int deleteHashRecord(hashSlot_t *theTable, char *aor, int hashTableSize); /* * This function will search the provided hash table for an entry indexed by * 'aor'. If an entry is found then: * * - Its numContacts counter will be decremented. * - If its numContacts counter reaches zero, then the entry will be removed * from the hash table. * */ void deleteUser(hashSlot_t *theTable, char *aor, int hashTableSize); #endif opensips-2.2.2/modules/snmpstats/interprocess_buffer.c000066400000000000000000000304551300170765700233130ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * This file implements the interprocess buffer, used for marshalling data * exchange from the usrloc module to the openserSIPRegUserTable, * openserSIPContactTable, and indirectly the openserSIPRegUserLookupTable. * Details on why the interprocess buffer is needed can be found in the comments * at the top of interprocess_buffer.h */ #include #include #include #include "interprocess_buffer.h" #include "openserSIPContactTable.h" #include "openserSIPRegUserTable.h" #include "hashTable.h" #include "utilities.h" #include "../usrloc/ul_callback.h" /* * The hash table: * * 1) maps all aor's to snmp's UserIndex for help in deleting SNMP Rows. * * 2) maps a given aor to a contact list. */ hashSlot_t *hashTable; /* All interprocess communication is stored between these two declarations. */ interprocessBuffer_t *frontRegUserTableBuffer = NULL; interprocessBuffer_t *endRegUserTableBuffer = NULL; /* This is to protect the potential racecondition in which a command is added to * the buffer while it is being consumed */ gen_lock_t *interprocessCBLock; /* * This function takes an element of the interprocess buffer passed to it, and * handles populating the respective user and contact tables with its contained * data. */ static void executeInterprocessBufferCmd(interprocessBuffer_t *currentBuffer); /* * Initialize shared memory used to buffer communication between the usrloc * module and the SNMPStats module. (Specifically, the user and contact tables) */ int initInterprocessBuffers(void) { /* Initialize the shared memory that will be used to buffer messages * over the usrloc module to RegUserTable callback. */ frontRegUserTableBuffer = shm_malloc(sizeof(interprocessBuffer_t)); endRegUserTableBuffer = shm_malloc(sizeof(interprocessBuffer_t)); if(frontRegUserTableBuffer == NULL || endRegUserTableBuffer == NULL) { LM_ERR("no more shared memory\n"); return -1; } memset(frontRegUserTableBuffer, 0x00, sizeof(interprocessBuffer_t)); memset(endRegUserTableBuffer, 0x00, sizeof(interprocessBuffer_t)); /* Initialize a lock to the interprocess buffer. The lock will be used * to control race-conditions that would otherwise occur if an snmp * command was received while the interprocess buffer was being consumed. */ interprocessCBLock = lock_alloc(); lock_init(interprocessCBLock); hashTable = createHashTable(HASH_SIZE); if(hashTable == NULL) { LM_ERR("no more shared memory\n"); shm_free(frontRegUserTableBuffer); frontRegUserTableBuffer = NULL; shm_free(endRegUserTableBuffer); endRegUserTableBuffer = NULL; return -1; } return 1; } void IBAlarmHandler(unsigned int reg, void *clientarg) { consumeInterprocessBuffer(); } int setInterprocessBuffersAlarm(void) { if ( snmp_alarm_register(5/*seconds*/,SA_REPEAT,IBAlarmHandler,NULL)==0){ LM_ERR("failed to set consumer snmp alarm\n"); return -1; } return 0; } /* USRLOC Callback Handler: * * This function should be registered to receive callbacks from the usrloc * module. It can be called for any of the callbacks listed in ul_callback.h. * The callback type will be passed in 'type', and the contact the callback * applies to will be supplied in 'contactInfo. This information will be copied * into the interprocess buffer. The interprocess buffer will be consumed at a * later time, when consumeInterprocessBuffer() is called. * * This callback is thread safe with respect to the consumeInterprocessBuffer() * function. Specifically, the interprocess buffer should not be corrupted by * any race conditions between this function and the consumeInterprocessBuffer() * function. */ void handleContactCallbacks(ucontact_t *contactInfo, int type, void *param) { char *p; interprocessBuffer_t *currentBufferElement; if (frontRegUserTableBuffer==NULL) return; currentBufferElement = shm_malloc( sizeof(interprocessBuffer_t) + contactInfo->aor->len+1 + contactInfo->c.len+1 ); if (currentBufferElement == NULL) { goto error; } p = (char*)(currentBufferElement + 1 ); /* We need to maintain our own copies of the AOR and contact address to * prevent the corruption of our internal data structures. * * If we do not maintain our own copies, then the AOR and contact address * pointed to could be removed and reallocated to another thread before * we get a chance to consume our interprocess buffer. */ currentBufferElement->stringName = p; memcpy( p , contactInfo->aor->s, contactInfo->aor->len ); p[contactInfo->aor->len] = 0; /* make it NULL terminated */ p += contactInfo->aor->len + 1; currentBufferElement->stringContact = p; memcpy( p , contactInfo->c.s, contactInfo->c.len ); p[contactInfo->c.len] = 0; /* make it NULL terminated */ currentBufferElement->contactInfo = contactInfo; currentBufferElement->callbackType = type; currentBufferElement->next = NULL; /* A lock is necessary to prevent a race condition. Specifically, it * could happen that we find the front of the buffer to be non-null, * are scheduled out, the entire buffer (or part of it) is consumed and * freed, and then we assign our list to deallocated memory. */ lock_get(interprocessCBLock); /* This is the first element to be added. */ if (frontRegUserTableBuffer->next == NULL) { frontRegUserTableBuffer->next = currentBufferElement; } else { endRegUserTableBuffer->next->next = currentBufferElement; } endRegUserTableBuffer->next = currentBufferElement; lock_release(interprocessCBLock); return; error: LM_ERR("Not enough shared memory for openserSIPRegUserTable insert." " (%s)\n", contactInfo->c.s); } /* Interprocess Buffer consumption Function. This function will iterate over * every element of the interprocess buffer, and add or remove the specified * contacts and users. Whether the contacts are added or removed is dependent * on if the original element was added as a result of a UL_CONTACT_INSERT, * UL_CONTACT_EXPIRE or UL_CONTACT_DELETE callback. * * The function will free any memory occupied by the interprocess buffer. * * Note: This function is believed to be thread safe. Specifically, it protects * corruption of the interprocess buffer through the interprocessCBLock. * This ensures no corruption of the buffer by race conditions. The lock * has been designed to be occupied for as short a period as possible, so * as to prevent long waits. Specifically, once we start consumption of * the list, other processes are free to continue even before we are done. * This is made possible by simply changing the head of the interprocess * buffer, and then releasing the lock. */ void consumeInterprocessBuffer(void) { interprocessBuffer_t *previousBuffer; interprocessBuffer_t *currentBuffer; /* There is nothing to consume, so just exit. */ if (frontRegUserTableBuffer->next == NULL) { return; } /* We are going to consume the entire buffer, but we don't want the * buffer to change midway through. So assign the front of the buffer * to NULL so that any other callbacks from the usrloc module will be * appended to a new list. We need to be careful to get a lock first * though, to avoid race conditions. */ lock_get(interprocessCBLock); currentBuffer = frontRegUserTableBuffer->next; frontRegUserTableBuffer->next = NULL; endRegUserTableBuffer->next = NULL; lock_release(interprocessCBLock); while (currentBuffer != NULL) { executeInterprocessBufferCmd(currentBuffer); /* We need to assign the current buffer to a temporary place * before we move onto the next buffer. Otherwise the memory * could be modified between freeing it and moving onto the next * buffer element. */ previousBuffer = currentBuffer; currentBuffer = currentBuffer->next; shm_free(previousBuffer); } } /* * This function takes an element of the interprocess buffer passed to it, and * handles populating the respective user and contact tables with its contained * data. */ static void executeInterprocessBufferCmd(interprocessBuffer_t *currentBuffer) { int delContactIndex; aorToIndexStruct_t *currentUser; if (currentBuffer->callbackType == UL_CONTACT_INSERT) { /* Add the user if the user doesn't exist, or increment its * contact index otherwise. */ updateUser(currentBuffer->stringName); } else if (currentBuffer->callbackType != UL_CONTACT_EXPIRE && currentBuffer->callbackType != UL_CONTACT_DELETE) { /* Currently we only support UL_CONTACT_INSERT, UL_CONTACT_DELETE and * UL_CONTACT_EXPIRE. If we receive another callback type, this * is a bug. */ LM_ERR("found a command on the interprocess buffer that" " was not an INSERT or EXPIRE"); return; } currentUser = findHashRecord(hashTable, currentBuffer->stringName, HASH_SIZE); /* This should never happen. This is more of a sanity check. */ if (currentUser == NULL) { LM_NOTICE("Received a request for contact: %s for user: %s who doesn't " "exists\n", currentBuffer->stringName, currentBuffer->stringContact); return; } /* This buffer element specified that we need to add a contact. So lets * add them */ if (currentBuffer->callbackType == UL_CONTACT_INSERT) { /* Increment the contact index, which will be used to generate * our new row. */ currentUser->contactIndex++; /* We should do this after we create the row in the snmptable. * Its easier to delete the SNMP Row than the contact record. */ if(!insertContactRecord(&(currentUser->contactList), currentUser->contactIndex, currentBuffer->stringContact)) { LM_ERR("openserSIPRegUserTable was unable to allocate memory for " "adding contact: %s to user %s.\n", currentBuffer->stringName, currentBuffer->stringContact); /* We didn't use the index, so decrement it so we can * use it next time around. */ currentUser->contactIndex--; return; } if (!createContactRow(currentUser->userIndex, currentUser->contactIndex, currentBuffer->stringContact, currentBuffer->contactInfo)) { deleteContactRecord(&(currentUser->contactList), currentBuffer->stringContact); } } else { delContactIndex = deleteContactRecord(&(currentUser->contactList), currentBuffer->stringContact); /* This should never happen. But its probably wise to check and * to print out debug messages in case there is a hidden bug. */ if(delContactIndex == 0) { LM_ERR("Received a request to delete contact: %s for user: %s" " who doesn't exist\n", currentBuffer->stringName, currentBuffer->stringContact); return; } deleteContactRow(currentUser->userIndex, delContactIndex); deleteUser(hashTable, currentBuffer->stringName, HASH_SIZE); } } void freeInterprocessBuffer(void) { interprocessBuffer_t *currentBuffer, *previousBuffer; if(endRegUserTableBuffer) { endRegUserTableBuffer->next = NULL; shm_free(endRegUserTableBuffer); endRegUserTableBuffer = NULL; } if(frontRegUserTableBuffer) { if (frontRegUserTableBuffer->next != NULL) { currentBuffer = frontRegUserTableBuffer->next; frontRegUserTableBuffer->next = NULL; while (currentBuffer != NULL) { previousBuffer = currentBuffer; currentBuffer = currentBuffer->next; shm_free(previousBuffer); } } else { LM_DBG("Nothing to clean\n"); } shm_free(frontRegUserTableBuffer); frontRegUserTableBuffer = NULL; } } opensips-2.2.2/modules/snmpstats/interprocess_buffer.h000066400000000000000000000117331300170765700233160ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * The SNMPStats module exposes user information through openserSIPRegUserTable, * openserSIPContactTable, and openserSIPRegUserLookupTable. These tables are * populated through callback mechanisms from the usrloc module. Unfortunately * the NetSNMP table population code is very slow when dealing with large * amounts of data. Because we don't want to experience a performance hit when * registering users, we make use of the interprocess buffer. Specifically, * instead of adding/removing users/contacts from the SNMP tables directly, the * callbacks add an add/delete command to the interprocessBuffer. * When an snmp request is recieved by the SNMPStats sub-process, it will * consume this interprocess buffer, adding and deleting users. When it is * finished, it can service the SNMP request. * * This doesn't remove the NetSNMP inefficiency of course, but it does move it * to a non-critical path. Such an approach allows SNMP support with almost no * overhead to the rest of OpenSIPS. */ #ifndef _SNMPSTATS_USER_UTILITIES_ #define _SNMPSTATS_USER_UTILITIES_ #include "../../str.h" #include "../../locking.h" #include "snmpstats_globals.h" #include "hashTable.h" #include "../usrloc/ucontact.h" /* Represents an element of the interprocess buffer. */ typedef struct interprocessBuffer { char *stringName; char *stringContact; int callbackType; struct interprocessBuffer *next; ucontact_t *contactInfo; } interprocessBuffer_t; /* Both of these will be used to reference in the interprocess buffer */ extern interprocessBuffer_t *frontRegUserTableBuffer; extern interprocessBuffer_t *endRegUserTableBuffer; /* A request to consume the interprocess buffer could occur at the same time * there is a request to add to the interprocess buffer. (Or vice-versa). This * lock is used to prevent these race conditions. */ extern gen_lock_t *interprocessCBLock; extern hashSlot_t *hashTable; /* * Initialize shared memory used to buffer communication between the usrloc * module and the SNMPStats module. (Specifically, the user and contact tables) */ int initInterprocessBuffers(void); int setInterprocessBuffersAlarm(void); /* USRLOC Callback Handler: * * This function should be registered to receive callbacks from the usrloc * module. It can be called for any of the callbacks listed in ul_Callback.h. * The callback type will be passed in 'type', and the contact the callback * applies to will be supplied in 'contactInfo. This information will be copied * into the interprocess buffer. The interprocess buffer will beconsumed at a * later time, when consumeInterprocessBuffer() is called. * * This callback is thread safe with respect to the consumeInterprocessBuffer() * function. Specifically, the interprocess buffer should not be corrupted by * any race conditions between this function and the consumeInterprocessBuffer() * function. */ void handleContactCallbacks(ucontact_t *contactInfo, int type, void *param); /* Interprocess Buffer consumption Function. This function will iterate over * every element of the interprocess buffer, and add or remove the specified * contacts and users. Whether the contacts are added or removed is dependent * on if the original element was added as a result of a UL_CONTACT_INSERT or * UL_CONTACT_EXPIRE callback. * * The function will free any memory occupied by the interprocess buffer. * * Note: This function is believed to be thread safe. Specifically, it protects * corruption of the interprocess buffer through the interprocessCBLock. * This ensures no corruption of the buffer by race conditions. The lock * has been designed to be occupied for as short a period as possible, so * as to prevent long waits. Specifically, once we start consumption of * the list, other processes are free to continue even before we are done. * This is made possible by simply changing the head of the interprocess * buffer, and then releasing the lock. */ void consumeInterprocessBuffer(); void freeInterprocessBuffer(void); #endif opensips-2.2.2/modules/snmpstats/mibs/000077500000000000000000000000001300170765700200215ustar00rootroot00000000000000opensips-2.2.2/modules/snmpstats/mibs/OPENSER-MIB000066400000000000000000000523331300170765700214720ustar00rootroot00000000000000-- *********************************************************************** -- OPENSER-MIB: OPENSER MIB -- -- Date of Creation: Januay 2006 -- -- This MIB provides information related to the OpenSER SIP Router. -- -- Copyright (c) The Internet Society (2006) -- Ammendments (c) Soma Networks, Inc. (2006) -- -- All rights reserved. -- ***************************************************************** OPENSER-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, OBJECT-IDENTITY, NOTIFICATION-TYPE, Integer32, Unsigned32, Counter32, Counter64, Gauge32 FROM SNMPv2-SMI DateAndTime FROM SNMPv2-TC SnmpAdminString FROM SNMP-FRAMEWORK-MIB X731UsageState, X731AlarmStatus, X731AlarmState FROM OPENSER-TC MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP FROM SNMPv2-CONF openserModules, openserMIB FROM OPENSER-REG-MIB; openserModule MODULE-IDENTITY LAST-UPDATED "200603231200Z" ORGANIZATION "OpenSER" CONTACT-INFO "http://www.openser.org" DESCRIPTION "OpenSER MIB module. This module defines objects which are neceesary to monitor an instance of OpenSER. The objects in this MIB are intended to be used in conjunction with those rovided in OPENSER-SIP-COMMON-MIB and OPENSER-SIP-SERVER-MIB. In particular, this MIB defines the X.731 attributes required to monitor the OpenSER subsystems and the OpenSER core itself." REVISION "200605021200Z" DESCRIPTION "clarified some of the statisitcs collected, added the alarm states and notifications." REVISION "200603231200Z" DESCRIPTION "changed ownership to OpenSER group, and made it SMIv2 compliant." REVISION "200601061200Z" DESCRIPTION "Initial version of the OPENSER-MIB module." ::= { openserModules 5 } -- -- Top-Level Components of this MIB. -- openserObjects OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for accessible objects in the MIB." ::= { openserMIB 1 } openserMIBEvents OBJECT-IDENTITY STATUS current DESCRIPTION "A sub-tree for all the OPENSER-MIB related events and traps." ::= {openserMIB 2 } openserConform OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for Conformance specifications." ::= { openserMIB 3 } -- -- openserObjects sub-components -- openserServer OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for Server state, alarms, and management." ::= { openserObjects 1 } openserMsgs OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for tracking of individual SIP messages processed by OpenSER." ::= { openserObjects 2 } openserDialog OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for tracking of SIP Dialogs being processed by OpenSER." ::= { openserObjects 3 } -- -- OpenSER Server Objects -- -- -- OpenSER Message Objects -- -- -- Message Statistics -- openserMsgStats OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for Message Statistics." ::= { openserMsgs 3 } openserMsgQueueDepth OBJECT-TYPE SYNTAX Gauge32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of packets (udp or tcp) waiting in the OS network queue, waiting to be processed." ::= { openserMsgStats 1 } openserMsgQueueMinorThreshold OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "When the openserMsgQueueDepth reaches this point, a minor alarm will be generated. This value reflects the configured value; if it is set to -1, then the alarm is never sent." ::= { openserMsgStats 2 } openserMsgQueueMajorThreshold OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "When the openserMsgQueueDepth reaches this point, a major alarm will be generated. This value reflects the configured value; if it is set to -1, then the alarm is never sent." ::= { openserMsgStats 3 } -- -- Message Alarms -- openserMsgAlarms OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for Message Alarms." ::= { openserMsgs 4 } openserMsgQueueDepthAlarmStatus OBJECT-TYPE SYNTAX X731AlarmStatus MAX-ACCESS read-only STATUS current DESCRIPTION "This status variable reports the alarm levels that have been generated for the message queue depth. The thresholds are defined in the OpenSER configuration; if they are not configured, this alarm status will always be clear. Specifically, in this case no bits will be toggled, and therefore this scalar will return 00. This scalar follows the X731AlarmStatus specs, and can take on the following values: underRepair(0), critical(1), major(2), minor(3), alarmOutstanding(4), unknown(5)" ::= { openserMsgAlarms 1 } openserMsgQueueDepthMinorAlarm OBJECT-TYPE SYNTAX X731AlarmState MAX-ACCESS read-only STATUS current DESCRIPTION "This variable indicates whether the minor message queue depth threshold has been surpassed. clear(0) the queue depth is below the configured threshold. minor(1) the queue depth has exceeded the configured threshold." ::= { openserMsgAlarms 2 } openserMsgQueueDepthMajorAlarm OBJECT-TYPE SYNTAX X731AlarmState MAX-ACCESS read-only STATUS current DESCRIPTION "This variable indicates whether the major message queue depth threshold has been surpassed. clear(0) the queue depth is below the configured threshold. major(2) the queue depth has exceeded the configured threshold." ::= { openserMsgAlarms 3 } -- -- OpenSER Dialog Objects -- -- -- The Dialog Table: -- contains information about currently active calls. -- openserDialogTable OBJECT-TYPE SYNTAX SEQUENCE OF OpenSERDialogTableEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains information on all active calls in the system." ::= { openserDialog 1 } openserDialogTableEntry OBJECT-TYPE SYNTAX OpenSERDialogTableEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This entry contains information for a single active call in the system." INDEX { openserDialogIndex } ::= { openserDialogTable 1 } OpenSERDialogTableEntry ::= SEQUENCE { openserDialogIndex Unsigned32, openserDialogType INTEGER, openserDialogToUri SnmpAdminString, openserDialogFromUri SnmpAdminString, openserDialogState INTEGER, openserDialogStartTime DateAndTime, openserDialogLastUpdateTime DateAndTime, openserDialogExpiryTime DateAndTime } openserDialogIndex OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS not-accessible STATUS current DESCRIPTION "The index of this dialog." ::= { openserDialogTableEntry 1 } openserDialogType OBJECT-TYPE SYNTAX INTEGER { invite(1), subscribe(2) } MAX-ACCESS read-only STATUS current DESCRIPTION "The type of Dialog: invite(1) - the dialog was created with a SIP INVITE. subscribe(2) - the dialog was created with a SIP SUBSCRIBE." ::= { openserDialogTableEntry 2 } openserDialogToUri OBJECT-TYPE SYNTAX SnmpAdminString MAX-ACCESS read-only STATUS current DESCRIPTION "The URI of the To header." ::= { openserDialogTableEntry 3 } openserDialogFromUri OBJECT-TYPE SYNTAX SnmpAdminString MAX-ACCESS read-only STATUS current DESCRIPTION "The URI of the From header." ::= { openserDialogTableEntry 4 } openserDialogState OBJECT-TYPE SYNTAX INTEGER { unconfirmed(0), early(1), confirmed(2), terminalted(3) } MAX-ACCESS read-only STATUS current DESCRIPTION "The Dialogue state: unconfirmed (0) - we do not yet have a dialog, but a request has been made and we may create one in the future. All calls begin in this state. early (1) - dialog migrates to this state when we receive a 101-199 response. confirmed (2) - dialog migrates from unconfirmed OR early to this state when we received a 200-299 response. terminated (3) - dialog migrates from unconfirmed OR early to this state when a final 3xx-4xx-5xx-6xx is message is received. terminated dialogs stay present in the system for only a short period of time, and then are cleared by OpenSER." ::= { openserDialogTableEntry 5 } openserDialogStartTime OBJECT-TYPE SYNTAX DateAndTime MAX-ACCESS read-only STATUS current DESCRIPTION "The time this dialog was started." ::= { openserDialogTableEntry 6 } openserDialogLastUpdateTime OBJECT-TYPE SYNTAX DateAndTime MAX-ACCESS read-only STATUS current DESCRIPTION "The last time this dialog was updated; ie, when the last SIP message for this dialog was seen." ::= { openserDialogTableEntry 7 } openserDialogExpiryTime OBJECT-TYPE SYNTAX DateAndTime MAX-ACCESS read-only STATUS current DESCRIPTION "The time at which this dialog will automatically expire." ::= { openserDialogTableEntry 8 } openserDialogStats OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-section for Dialog counters and statistics." ::= { openserDialog 2 } openserCurNumDialogs OBJECT-TYPE SYNTAX Gauge32 MAX-ACCESS read-only STATUS current DESCRIPTION "The current number of dialogs either in EARLY or CONFIRMED state." ::= { openserDialogStats 1 } openserCurNumDialogsInProgress OBJECT-TYPE SYNTAX Gauge32 MAX-ACCESS read-only STATUS current DESCRIPTION "The current number of CONFIRMED dialogs." ::= { openserDialogStats 2 } openserCurNumDialogsInSetup OBJECT-TYPE SYNTAX Gauge32 MAX-ACCESS read-only STATUS current DESCRIPTION "The current number of EARLY dialogs." ::= { openserDialogStats 3 } openserTotalNumFailedDialogSetups OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "The total number of calls that failed with an error. The following codes define a failed call:" ::= { openserDialogStats 4 } openserDialogLimitMinorThreshold OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The configured minor threshold indicating the openser system has reached a high level of dialogs. -1 indicates the value is not configured, and the alarm will never be set." ::= { openserDialogStats 5 } openserDialogLimitMajorThreshold OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The configured threshold indicating the openser system has reached a maximum number of dialogs, and perhaps performace will suffer. -1 indicates the value is not configured, and alarms will never be set, and the usage state will never be reported as 'busy'." ::= { openserDialogStats 6 } -- -- Dialog State -- openserDialogStates OBJECT-IDENTITY STATUS current DESCRIPTION "a sub-tree for OpenSER's dialog tracking states." ::= { openserDialog 3 } openserDialogUsageState OBJECT-TYPE SYNTAX X731UsageState MAX-ACCESS read-only STATUS current DESCRIPTION "The current usage state of OpenSER's dialog tracking: idle(0) - no dialogs are currently being tracked. active(1) - at least 1 dialog is being tracked. busy(2) - the maximum number of dialogs are being tracked. The 'busy' point is based on a configuration variable (the same as openserDialogMajorLimitThreshold): if this value is -1, then the server is never considered 'busy'." ::= { openserDialogStates 1 } -- -- Dialog Alarms -- openserDialogAlarms OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for call alarms." ::= { openserDialog 4 } openserDialogLimitAlarmStatus OBJECT-TYPE SYNTAX X731AlarmStatus MAX-ACCESS read-only STATUS current DESCRIPTION "This alarm status reports the overall alarm status of the dialog sub-system. If the thresholds are not configured, then this status will always be 'clear'. This scalar follows the X731AlarmStatus specs, and can take on the following values: underRepair(0), critical(1), major(2), minor(3), alarmOutstanding(4), unknown(5)" ::= { openserDialogAlarms 1 } openserDialogLimitMinorAlarm OBJECT-TYPE SYNTAX X731AlarmState MAX-ACCESS read-only STATUS current DESCRIPTION "The OpenSER system is approaching or has reached its configured minor dialog limit threshold. clear(0) - the current number of dialogs is below the configured threshold. minor(1) - the current number of dialogs has reached or exceeded the configured threshold. The threshold is defined in the OpenSER configuration; if it is not configured, this alarm will not be sent." ::= { openserDialogAlarms 2 } openserDialogLimitMajorAlarm OBJECT-TYPE SYNTAX X731AlarmState MAX-ACCESS read-only STATUS current DESCRIPTION "The OpenSER system is approaching or has reached its configured dialog limit. clear(0) - the current number of dialogs is below the configured threshold. major(2) - the current number of dialogs has reached or exceeded the configured threshold. The threshold is defined in the OpenSER configuration; if it is not configured, this alarm will not be sent." ::= { openserDialogAlarms 3 } -- -- Notifications -- -- -- This section defines the SNMP traps, or notifications, that the OPENSER-MIB -- supports. -- openserMIBNotifications OBJECT-IDENTITY STATUS current DESCRIPTION "A sub-section for reverse-mapping between SNMPv1 and SNMPv2 events." ::= { openserMIBEvents 0 } openserMsgQueueDepthMinorEvent NOTIFICATION-TYPE OBJECTS { openserMsgQueueDepth, openserMsgQueueMinorThreshold } STATUS current DESCRIPTION "The message queue depth (as reported by openserMsgQueueDepth) has exceeded the minor configured threshold openserMsgQueueMinorThreshold." ::= { openserMIBNotifications 1 } openserMsgQueueDepthMajorEvent NOTIFICATION-TYPE OBJECTS { openserMsgQueueDepth, openserMsgQueueMajorThreshold } STATUS current DESCRIPTION "The message queue depth (as reported by openserMsgQueueDepth) has exceeded the minor configured threshold openserMsgQueueMinorThreshold." ::= { openserMIBNotifications 2 } openserDialogLimitMinorEvent NOTIFICATION-TYPE OBJECTS { openserCurNumDialogs, openserDialogLimitMinorThreshold } STATUS current DESCRIPTION "The number of dialogs being tracked in the system (as reported by openserCurNumDialogs) has exceeded the minor configured threshold openserDialogLimitMinorThreshold." ::= { openserMIBNotifications 3 } openserDialogLimitMajorEvent NOTIFICATION-TYPE OBJECTS { openserCurNumDialogs, openserDialogLimitMajorThreshold } STATUS current DESCRIPTION "The number of dialogs being tracked in the system (as reported by openserCurNumDialogs) has exceeded the major configured threshold openserDialogLimitMajorThreshold." ::= { openserMIBNotifications 4 } -- -- Conformance -- -- -- Compliance Statements -- openserCompliance MODULE-COMPLIANCE STATUS current DESCRIPTION "The compliance statement for OpenSER in the SOMAvoice product." MODULE -- this module MANDATORY-GROUPS { openserX731Group, openserStatsGroup, openserAlarmsGroup, openserMsgQueueEvents, openserDialogLimitEvents } ::= { openserConform 1 } openserGroups OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for defining the conformance groups." ::= { openserConform 2 } openserNotifications OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for defining the conformance notifications." ::= { openserConform 3 } -- -- Units of Conformance -- openserX731Group OBJECT-GROUP OBJECTS { openserDialogUsageState } STATUS current DESCRIPTION "A collection of objects providing X.731 state and alarm status information in OpenSER." ::= { openserGroups 1 } openserStatsGroup OBJECT-GROUP OBJECTS { openserMsgQueueDepth, openserMsgQueueMinorThreshold, openserMsgQueueMajorThreshold, openserDialogType, openserDialogToUri, openserDialogFromUri, openserDialogState, openserDialogStartTime, openserDialogLastUpdateTime, openserDialogExpiryTime, openserCurNumDialogs, openserCurNumDialogsInProgress, openserCurNumDialogsInSetup, openserTotalNumFailedDialogSetups, openserDialogLimitMinorThreshold, openserDialogLimitMajorThreshold } STATUS current DESCRIPTION "A collection of objects providing statistics in OpenSER." ::= { openserGroups 2 } openserAlarmsGroup OBJECT-GROUP OBJECTS { openserMsgQueueDepthAlarmStatus, openserMsgQueueDepthMinorAlarm, openserMsgQueueDepthMajorAlarm, openserDialogLimitAlarmStatus, openserDialogLimitMinorAlarm, openserDialogLimitMajorAlarm } STATUS current DESCRIPTION "A collection of objects providing stateful alarm information in OpenSER." ::= { openserGroups 3 } openserMsgQueueEvents NOTIFICATION-GROUP NOTIFICATIONS { openserMsgQueueDepthMinorEvent, openserMsgQueueDepthMajorEvent } STATUS current DESCRIPTION "A collection of events for the message queue depth." ::= { openserNotifications 1 } openserDialogLimitEvents NOTIFICATION-GROUP NOTIFICATIONS { openserDialogLimitMinorEvent, openserDialogLimitMajorEvent } STATUS current DESCRIPTION "A collection of events for dialog tracking." ::= { openserNotifications 2 } END -- vim:ts=4:et:sw=4:si:ai:tw=78 opensips-2.2.2/modules/snmpstats/mibs/OPENSER-REG-MIB000066400000000000000000000130671300170765700221060ustar00rootroot00000000000000-- ******************************************************************* -- OPENSER-REG-MIB: OpenSER Global Registration MIB file -- -- Date of Creation: March 2006 -- Mike Varley -- -- Copyright (c) The Internet Society (2006) -- Ammendments (c) Soma Networks, Inc. (2006) -- -- Modeled after recommedations in 'Understanding SNMP MIBs' -- (Perkins, McGinnis) Prentice Hall 1997, chapter 8. -- -- using SMIv2 syntax. -- -- ******************************************************************* OPENSER-REG-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, enterprises, OBJECT-IDENTITY FROM SNMPv2-SMI; openserRegMibModule MODULE-IDENTITY LAST-UPDATED "200702150430Z" ORGANIZATION "OpenSER" CONTACT-INFO "http://www.openser.org open source project." DESCRIPTION "OpenSER Global Registration MIB" REVISION "200702150430Z" DESCRIPTION "Moved the OpenSER MIBs from the experimental branch to the IANA assigned enterprise branch 27483" REVISION "200603211200Z" DESCRIPTION "Initial OpenSER Mib Release" ::= { openserModules 1 } -- top level enterprise identification openser OBJECT-IDENTITY STATUS current DESCRIPTION "The IANA has assigned enterprise branch 27483 to OpenSER." ::= { enterprises 27483 } -- namespace management sub-trees openserReg OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for identification of Modules and logical components." ::= { openser 1 } openserGeneric OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for objects and events common to OpenSER products." ::= { openser 2 } openserProducts OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for objects and events for specific OpenSER products and modules." ::= { openser 3 } -- Agent implementation profiles: Agents which implement the product MIBs -- can publish under this branch which portions they support or exclude. openserCaps OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for agent implementation profiles." ::= { openser 4 } openserReqs OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for Management Application module requirements." ::= { openser 5 } -- Experimental branch, for development openserExpr OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for experimental definitions and new development." ::= { openser 6 } -- -- The Registration sub-tree -- openserModules OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree to register the values assigned to modules with the MODULE-IDENTITY construct. Core MIBs are registered under this branch." ::= { openserReg 1 } openserPlugins OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree to register plugin modules. Plugins with their own specific MIBs are registered under this branch." ::= {openserReg 2 } -- -- Generic Sub-tree: currently there are no generic object or event definitions -- for the openSER group. -- -- -- Products Sub-tree: this is the sub-tree where all product specific object -- and events are defined. -- openserCoreMIB OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for all objects and events related to the core OpenSER MIBs" ::= { openserProducts 1 } openserPluginMIB OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for all objects and events related to plugins for OpenSER." ::= { openserProducts 2 } -- -- Agent Capabilities Sub-tree: currently there are no agent capability MIBs -- defined. -- -- -- Requirements Sub-tree: currently there are no Management Application -- requirement MIBs defined. -- -- -- Experimental Sub-tree: currently there are no experimental MIBs defined. -- -- -- Module Registrations: each MIB needs a registration entry here, referenced -- by the MODULE-IDENTITY construct. -- -- The following are just Module Identifier definitions; the actual OID -- registration takes place in the MIBs themselves. This is for reference, -- if someone is looking to add a new module. -- -- openserRegMibModule ::= { openserModules 1 } is this files MODULE-IDENTITY -- The Text Conventions used by the OpenSER product tree. openserTcModule OBJECT IDENTIFIER ::= { openserModules 2 } -- The OpenSER version of the SIP-COMMON-MIB. openserSIPCommonModule OBJECT IDENTIFIER ::= { openserModules 3 } -- The OpenSER version of the SIP-SERVER-MIB. openserSIPServerModule OBJECT IDENTIFIER ::= { openserModules 4 } -- The OpenSER MIB Module, containing internal statistics and state -- information. openserModule OBJECT IDENTIFIER ::= { openserModules 5 } -- -- Plugin Module Registrations: add any OpenSER plugin MIB Module registrations -- here, under openserPlugins. -- -- -- Product Core MIBs: this section has the actual MIB objects registered. -- The MIB files will define objects that use the following as their parent. -- openserSIPCommonMIB OBJECT-IDENTITY STATUS current DESCRIPTION "The OpenSER version of the SIP-COMMON-MIB. All objects and events are defined under this branch." ::= { openserCoreMIB 1 } openserSIPServerMIB OBJECT-IDENTITY STATUS current DESCRIPTION "The OpenSER version of the SIP-SERVER-MIB. All objects and events are defined under this branch." ::= { openserCoreMIB 2 } openserMIB OBJECT-IDENTITY STATUS current DESCRIPTION "The OpenSER MIB object and event definitons." ::= { openserCoreMIB 3 } -- -- Product Plugin MIBs: add any specific OpenSER plugin MIBs here, under -- openserPluginMIB -- END opensips-2.2.2/modules/snmpstats/mibs/OPENSER-SIP-COMMON-MIB000066400000000000000000000735641300170765700231220ustar00rootroot00000000000000-- *********************************************************************** -- -- This MIB provides information related to the OpenSER SIP Router. -- -- Copyright (c) The Internet Society (2006) -- Ammendments (c) Soma Networks, Inc. (2006) -- -- All rights reserved. -- ***************************************************************** OPENSER-SIP-COMMON-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, OBJECT-IDENTITY, Counter32, Gauge32, TimeTicks, Unsigned32 FROM SNMPv2-SMI RowStatus FROM SNMPv2-TC MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF SnmpAdminString FROM SNMP-FRAMEWORK-MIB OpenSERSIPTransportProtocol, OpenSERSIPMethodIdentifier, OpenSERSIPEntityRole FROM OPENSER-TC openserModules, openserSIPCommonMIB FROM OPENSER-REG-MIB InetPortNumber, InetAddressType, InetAddress FROM INET-ADDRESS-MIB; openserSIPCommonModule MODULE-IDENTITY LAST-UPDATED "200603221200Z" ORGANIZATION "OpenSER" CONTACT-INFO "http://www.openser.org" DESCRIPTION "Originally taken from the IETF Session Initiation Protocol Working Group (sip@ietf.org). The description is pulled from there: Session Initiation Protocol (SIP) Common MIB module. This module defines objects which MAY be common to all SIP entities. SIP is an application-layer signalling protocol for creating, modifying and terminating multimedia sessions with one or more participants. These sessions include Internet multimedia conferences and Internet telephone calls. SIP is defined in RFC 3261 (June 2002). This MIB is defined for managing objects which are common to SIP User Agents (UAs), Proxy, Redirect and Registrar servers. Objects specific to each of these entities MAY be managed using entity specific MIBs defined in other modules. User Agent (UA): A logical entity that can act as both a user agent client and user agent server. User Agent Client (UAC): A logical entity that creates a new request, and then uses the client transaction state machinery to send it. The role of UAC lasts only for the duration of that transaction. In other words, if a piece of software initiates a request, it acts as a UAC for the duration of that transaction. If it receives a request later, it assumes the role of a user agent server for the processing of that transaction. User Agent Server (UAS): a logical entity that generates a response to a SIP request. The response accepts, rejects, or redirects the request. This role lasts only for the duration of that transaction. In other words, if a piece of software responds to a request, it acts as a UAS for the duration of that transaction. If it generates a request later, it assumes the role of a user agent client for the processing of that transaction. Proxy, Proxy Server: An intermediary entity that acts as both a server and a client for the purpose of making requests on behalf of other clients. A proxy server primarily plays the role of routing, which means its job is to ensure that a request is sent to another entity 'closer' to the targeted user. Proxies are also useful for enforcing policy. A proxy interprets, and, if necessary, rewrites specific parts of a request message before forwarding it. Redirect Server: A redirect server is a user agent server that generates 3xx responses to requests it receives, directing the client to contact an alternate set of URIs. Registrar: A registrar is a server that accepts REGISTER requests and places the information it receives in those requests into the location service for the domain it handles. Copyright (C) The Internet Society (2005). This version of this MIB module is part of RFC XXXX; see the RFC itself for full legal notices." REVISION "200501281700Z" DESCRIPTION "Initial version of the IETF SOMA-SIP-COMMON-MIB module. This version published as part of RFC XXXX." ::= { openserModules 3 } -- -- Top-Level Components of this MIB. -- openserSIPCommonObjects OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for accessible objects in the MIB." ::= { openserSIPCommonMIB 1 } openserSIPCommonConform OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for conformance specifications." ::= { openserSIPCommonMIB 2 } -- -- This MIB contains objects that are common to all SIP entities. -- -- -- Common basic configuration -- openserSIPCommonCfgBase OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for common basic configurations." ::= { openserSIPCommonObjects 1 } openserSIPCommonCfgTimer OBJECT-IDENTITY STATUS current DESCRIPTION "Protocol timer configuration group." ::= { openserSIPCommonObjects 2 } openserSIPCommonStatsSummary OBJECT-IDENTITY STATUS current DESCRIPTION "SIP message summary statistics group." ::= { openserSIPCommonObjects 3 } openserSIPCommonStatsMethod OBJECT-IDENTITY STATUS current DESCRIPTION "Per method statistics group." ::= { openserSIPCommonObjects 4 } openserSIPCommonStatusCode OBJECT-IDENTITY STATUS current DESCRIPTION "Per Status code or status code class statistics group." ::= { openserSIPCommonObjects 5 } openserSIPCommonStatsTrans OBJECT-IDENTITY STATUS current DESCRIPTION "Transaction statistics group." ::= { openserSIPCommonObjects 6 } openserSIPCommonStatsRetry OBJECT-IDENTITY STATUS current DESCRIPTION "Method retry statistics group." ::= { openserSIPCommonObjects 7 } openserSIPCommonStatsOther OBJECT-IDENTITY STATUS current DESCRIPTION "Other statistics group." ::= { openserSIPCommonObjects 8 } openserSIPCommonNotifObjects OBJECT-IDENTITY STATUS current DESCRIPTION "Accessible-for-notify objects group." ::= { openserSIPCommonObjects 9 } -- -- Common Configuration Objects -- openserSIPProtocolVersion OBJECT-TYPE SYNTAX SnmpAdminString MAX-ACCESS read-only STATUS current DESCRIPTION "This object will reflect the version of SIP supported by this SIP entity. It will follow the same format as SIP version information contained in the SIP messages generated by this SIP entity. For example, entities supporting SIP version 2 will return 'SIP/2.0' as dictated by the standard." REFERENCE "RFC 3261, Section 7.1" ::= { openserSIPCommonCfgBase 1 } openserSIPServiceStartTime OBJECT-TYPE SYNTAX TimeTicks MAX-ACCESS read-only STATUS current DESCRIPTION "The value of sysUpTime at the time the SIP entity was last started. If started prior to the last re-initialization of the local network management subsystem, then this object contains a zero value." ::= { openserSIPCommonCfgBase 2 } openserSIPEntityType OBJECT-TYPE SYNTAX OpenSERSIPEntityRole MAX-ACCESS read-only STATUS current DESCRIPTION " This object identifies the list of SIP entities this row is related to. It is defined as a bit map. Each bit represents a type of SIP entity. If a bit has value 1, the SIP entity represented by this row plays the role of this entity type. If a bit has value 0, the SIP entity represented by this row does not act as this entity type Combinations of bits can be set when the SIP entity plays multiple SIP roles. The bits can be: other(0), userAgent(1), proxyServer(2), redirectServer(3), registrarServer(4)" ::= { openserSIPCommonCfgBase 4 } -- -- Support for multiple ports -- openserSIPPortTable OBJECT-TYPE SYNTAX SEQUENCE OF OpenSERSIPPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains the list of ports that each SIP entity in this system is allowed to use. These ports can be advertised using the Contact header in a REGISTER request or response." ::= { openserSIPCommonCfgBase 5 } openserSIPPortEntry OBJECT-TYPE SYNTAX OpenSERSIPPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Specification of a particular port. Each row represents those objects for a particular SIP entity present in this system." INDEX { openserSIPIPType, openserSIPIPAddress, openserSIPPort } ::= { openserSIPPortTable 1 } OpenSERSIPPortEntry ::= SEQUENCE { openserSIPIPType InetAddressType, openserSIPIPAddress InetAddress, openserSIPPort InetPortNumber, openserSIPTransportRcv OpenSERSIPTransportProtocol } openserSIPIPType OBJECT-TYPE SYNTAX InetAddressType MAX-ACCESS not-accessible STATUS current DESCRIPTION "This object tells us what the address format is for the next column" ::= { openserSIPPortEntry 1 } openserSIPIPAddress OBJECT-TYPE SYNTAX InetAddress MAX-ACCESS not-accessible STATUS current DESCRIPTION "This object shows which IP Address the row pertains to" ::= { openserSIPPortEntry 2 } openserSIPPort OBJECT-TYPE SYNTAX InetPortNumber MAX-ACCESS not-accessible STATUS current DESCRIPTION "This object reflects a particular port that can be used by the SIP application." ::= { openserSIPPortEntry 3 } openserSIPTransportRcv OBJECT-TYPE SYNTAX OpenSERSIPTransportProtocol MAX-ACCESS read-only STATUS current DESCRIPTION "This object will specify the transport protocol the SIP entity will use to receive SIP messages. This object is a bit map. Each bit represents a transport protocol. If a bit has value 1, then that transport protocol is currently being used. If a bit has value 0, then that transport protocol is currently not being used. The bits are assigned as follows: other(0), udp(1), tcp(2), sctp(3), tls(4)" ::= { openserSIPPortEntry 4 } -- -- Supported SIP Methods -- openserSIPMethodSupportedTable OBJECT-TYPE SYNTAX SEQUENCE OF OpenSERSIPMethodSupportedEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains a list of methods supported by each SIP entity in this system. This MAY include, but is not limited to, the standard set of SIP methods discussed in Section 7.1 of RFC 3261. Any additional methods that MAY be incorporated into the SIP protocol can be represented by this table without any requirement to update this MIB. The table is informational in nature; conveying to the NMS capabilities of the managed system. From a protocol point of view, the list of methods advertised by the SIP entity in the Allow header (Section 20.5 of RFC 3261) MUST be consistent with the methods reflected in this table." ::= { openserSIPCommonCfgBase 7 } openserSIPMethodSupportedEntry OBJECT-TYPE SYNTAX OpenSERSIPMethodSupportedEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A particular method supported by the SIP entity. Each row represents those objects for a particular SIP entity present in this system. The objects in this table entry SHOULD be non-volatile and their value SHOULD be kept at reboot." INDEX { openserSIPMethodSupportedIndex } ::= { openserSIPMethodSupportedTable 1 } OpenSERSIPMethodSupportedEntry ::= SEQUENCE { openserSIPMethodSupportedIndex OpenSERSIPMethodIdentifier, openserSIPMethodName SnmpAdminString } openserSIPMethodSupportedIndex OBJECT-TYPE SYNTAX OpenSERSIPMethodIdentifier MAX-ACCESS not-accessible STATUS current DESCRIPTION "This object uniquely identifies a conceptual row in the table and reflects an assigned number used to identifier a specific SIP method. This identifier is suitable for referenceing the associated method throughout this and other MIBs supported by this managed system." ::= { openserSIPMethodSupportedEntry 1 } openserSIPMethodName OBJECT-TYPE SYNTAX SnmpAdminString MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the supported method's name. The method name MUST be all upper case (eg, 'FOO')." ::= { openserSIPMethodSupportedEntry 2 } -- -- Summary Statistics -- openserSIPSummaryInRequests OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object indicates the total number of SIP request messages received by the SIP entity including retransmissions." ::= { openserSIPCommonStatsSummary 1 } openserSIPSummaryOutRequests OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains the total number of SIP request messages sent out (originated and relayed) by the SIP entity. Where a particular message is sent more than once, for example as a retransmission, or as a result of forking, each transmission is counted separately." ::= { openserSIPCommonStatsSummary 2 } openserSIPSummaryInResponses OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains the total number of SIP response messages received by the SIP entity including retransmissions." ::= { openserSIPCommonStatsSummary 3 } openserSIPSummaryOutResponses OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains the total number of SIP response messages sent (originated and relayed) by the SIP entity including retransmissions." ::= { openserSIPCommonStatsSummary 4 } openserSIPSummaryTotalTransactions OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains a count of the number of transactions that are in progress and transactions that have reached the terminated state. It is not applicable to stateless SIP Proxy Servers. A SIP transaction occurs between a client and a server and comprises all messages from the first request sent from the client to the server up to a final (non-1xx) response sent from the server to the client. If the request is INVITE and the final response is a non-2xx, the transaction also include an ACK to the response. The ACK for a 2xx response to an INVITE request is a separate transaction. The branch ID parameter in the Via header field values serves as a transaction identifier. A transaction is identified by the CSeq sequence number within a single call leg. The ACK request has the same CSeq number as the corresponding INVITE request, but comprises a transaction of its own. In the case of a forked request, each branch counts as a single transaction. For a transaction stateless Proxy Server, this counter is always 0." ::= { openserSIPCommonStatsSummary 5 } -- -- SIP Method Statistics -- Total counts for each SIP method. -- openserSIPMethodStatsTable OBJECT-TYPE SYNTAX SEQUENCE OF OpenSERSIPMethodStatsEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains the method statistics objects for SIP entities. Each row represents those objects for a particular SIP entity present in this system." ::= { openserSIPCommonStatsMethod 1 } openserSIPMethodStatsEntry OBJECT-TYPE SYNTAX OpenSERSIPMethodStatsEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A row of per entity method statistics. Each row represents those objects for a particular SIP entity present in this system." INDEX { openserSIPStatsMethod } ::= { openserSIPMethodStatsTable 1 } OpenSERSIPMethodStatsEntry ::= SEQUENCE { openserSIPStatsMethod OpenSERSIPMethodIdentifier, openserSIPStatsOutbounds Counter32, openserSIPStatsInbounds Counter32 } openserSIPStatsMethod OBJECT-TYPE SYNTAX OpenSERSIPMethodIdentifier MAX-ACCESS not-accessible STATUS current DESCRIPTION "This object uniquely identifies the SIP method related to the objects in a particular row." ::= { openserSIPMethodStatsEntry 1 } openserSIPStatsOutbounds OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the total number of requests sent by the SIP entity, excluding retransmissions. Retransmissions are counted separately and are not reflected in this counter." REFERENCE "RFC 3261, Section 7.1" ::= { openserSIPMethodStatsEntry 2 } openserSIPStatsInbounds OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the total number of requests received by the SIP entity. Retransmissions are counted separately and are not reflected in this counter." REFERENCE "RFC 3261, Section 7.1" ::= { openserSIPMethodStatsEntry 3 } -- -- Support for specific status codes -- openserSIPStatusCodesTable OBJECT-TYPE SYNTAX SEQUENCE OF OpenSERSIPStatusCodesEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains the list of SIP status codes which each SIP entity in this system has been requested to monitor. It is the mechanism by which specific status codes are monitored." ::= { openserSIPCommonStatusCode 1 } openserSIPStatusCodesEntry OBJECT-TYPE SYNTAX OpenSERSIPStatusCodesEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This row contains information on a particular SIP status code that the SIP entity has been requested to monitor. Each row represents those objects for a particular SIP entity present in this system." INDEX { openserSIPStatusCodeMethod, openserSIPStatusCodeValue } ::= { openserSIPStatusCodesTable 1 } OpenSERSIPStatusCodesEntry ::= SEQUENCE { openserSIPStatusCodeMethod OpenSERSIPMethodIdentifier, openserSIPStatusCodeValue Unsigned32, openserSIPStatusCodeIns Counter32, openserSIPStatusCodeOuts Counter32, openserSIPStatusCodeRowStatus RowStatus } openserSIPStatusCodeMethod OBJECT-TYPE SYNTAX OpenSERSIPMethodIdentifier MAX-ACCESS not-accessible STATUS current DESCRIPTION "This object uniquely identifies a conceptual row in the table and reflects an assigned number used to identifier a specific SIP method." ::= { openserSIPStatusCodesEntry 1 } openserSIPStatusCodeValue OBJECT-TYPE SYNTAX Unsigned32 (100..999) MAX-ACCESS not-accessible STATUS current DESCRIPTION "This object contains a SIP status code value that the SIP entity has been requested to monitor. All of the other information in the row is related to this value." ::= { openserSIPStatusCodesEntry 2 } openserSIPStatusCodeIns OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the total number of response messages received by the SIP entity with the status code value contained in the sipStatusCodeValue column." ::= { openserSIPStatusCodesEntry 3 } openserSIPStatusCodeOuts OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the total number of response messages sent by the SIP entity with the status code value contained in the sipStatusCodeValue column." ::= { openserSIPStatusCodesEntry 4 } openserSIPStatusCodeRowStatus OBJECT-TYPE SYNTAX RowStatus MAX-ACCESS read-create STATUS current DESCRIPTION "The row augmentation in sipStatusCodeNotifTable will be governed by the value of this RowStatus. This object is REQUIRED to create or delete rows by a manager. The values 'createAndGo' and 'destroy' are the only valid values allowed for this object. If a row exists, it will reflect a status of 'active' when queried." ::= { openserSIPStatusCodesEntry 5 } -- -- Transaction Statistics -- openserSIPCurrentTransactions OBJECT-TYPE SYNTAX Gauge32 (0..4294967295) MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains the number of transactions awaiting definitive (non-1xx) response. In the case of a forked request, each branch counts as a single transaction corresponding to the entity identified by applIndex." ::= { openserSIPCommonStatsTrans 1 } -- -- SIP Retry Statistics -- -- This group contains various statistic objects about -- retransmission counts. -- openserSIPCommonStatsRetryTable OBJECT-TYPE SYNTAX SEQUENCE OF OpenSERSIPCommonStatsRetryEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains retry statistics objects applicable to each SIP entity in this system." ::= { openserSIPCommonStatsRetry 1 } openserSIPCommonStatsRetryEntry OBJECT-TYPE SYNTAX OpenSERSIPCommonStatsRetryEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A row of retry statistics. Each row represents those objects for a particular SIP entity present in this system." INDEX { openserSIPStatsRetryMethod } ::= { openserSIPCommonStatsRetryTable 1 } OpenSERSIPCommonStatsRetryEntry ::= SEQUENCE { openserSIPStatsRetryMethod OpenSERSIPMethodIdentifier, openserSIPStatsRetries Counter32, openserSIPStatsRetryFinalResponses Counter32, openserSIPStatsRetryNonFinalResponses Counter32 } openserSIPStatsRetryMethod OBJECT-TYPE SYNTAX OpenSERSIPMethodIdentifier MAX-ACCESS not-accessible STATUS current DESCRIPTION "This object uniquely identifies the SIP method related to the objects in a row." ::= { openserSIPCommonStatsRetryEntry 1 } openserSIPStatsRetries OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the total number of request retransmissions that have been sent by the SIP entity. Note that there could be multiple retransmissions per request." ::= { openserSIPCommonStatsRetryEntry 2 } openserSIPStatsRetryFinalResponses OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the total number of Final Response retries that have been sent by the SIP entity. Note that there could be multiple retransmissions per request." ::= { openserSIPCommonStatsRetryEntry 3 } openserSIPStatsRetryNonFinalResponses OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the total number of non-Final Response retries that have been sent by the SIP entity." ::= { openserSIPCommonStatsRetryEntry 4 } -- -- Other Common Statistics -- openserSIPNumUnsupportedUris OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of RequestURIs received with unsupported scheme. A server normally responds to such requests with a 400 Bad Request status code." ::= { openserSIPCommonStatsOther 1 } openserSIPNumUnsupportedMethods OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of SIP requests received with unsupported methods. A server normally responds to such requests with a 501 (Not Implemented) or 405 (Method Not Allowed)." ::= { openserSIPCommonStatsOther 2 } openserSIPOtherwiseDiscardedMsgs OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of SIP messages received that for any number of reasons was discarded without a response." ::= { openserSIPCommonStatsOther 3 } -- -- Conformance -- -- -- Compliance Statements -- openserSIPCommonCompliance MODULE-COMPLIANCE STATUS current DESCRIPTION "The compliance statement for SIP entities." MODULE -- this module MANDATORY-GROUPS { openserSIPCommonConfigGroup, openserSIPCommonStatsGroup } GROUP openserSIPCommonStatsRetryGroup DESCRIPTION "This group is OPTIONAL. A SIP entity can elect to not provide any retry statistics." ::= { openserSIPCommonConform 1 } openserSIPCommonGroups OBJECT IDENTIFIER ::= { openserSIPCommonConform 2 } -- -- Units of Conformance -- openserSIPCommonConfigGroup OBJECT-GROUP OBJECTS { openserSIPProtocolVersion, openserSIPServiceStartTime, openserSIPPort, openserSIPTransportRcv, openserSIPIPType, openserSIPIPAddress, openserSIPEntityType, openserSIPMethodName } STATUS current DESCRIPTION "A collection of objects providing configuration common to all SIP enities." ::= { openserSIPCommonGroups 1 } openserSIPCommonStatsGroup OBJECT-GROUP OBJECTS { openserSIPSummaryInRequests, openserSIPSummaryOutRequests, openserSIPSummaryInResponses, openserSIPSummaryOutResponses, openserSIPSummaryTotalTransactions, openserSIPStatsOutbounds, openserSIPStatsInbounds, openserSIPStatusCodeIns, openserSIPStatusCodeOuts, openserSIPStatusCodeRowStatus, openserSIPCurrentTransactions, openserSIPNumUnsupportedUris, openserSIPNumUnsupportedMethods, openserSIPOtherwiseDiscardedMsgs } STATUS current DESCRIPTION "A collection of objects providing statistics common to all SIP entities." ::= { openserSIPCommonGroups 3 } openserSIPCommonStatsRetryGroup OBJECT-GROUP OBJECTS { openserSIPStatsRetries, openserSIPStatsRetryFinalResponses, openserSIPStatsRetryNonFinalResponses } STATUS current DESCRIPTION "A collection of objects providing retry statistics." ::= { openserSIPCommonGroups 4 } END opensips-2.2.2/modules/snmpstats/mibs/OPENSER-SIP-SERVER-MIB000066400000000000000000000730231300170765700231260ustar00rootroot00000000000000-- *********************************************************************** -- -- This MIB provides information related to the OpenSER SIP Router. -- -- Copyright (c) The Internet Society (2006) -- Ammendments (c) Soma Networks, Inc. (2006) -- -- All rights reserved. -- ***************************************************************** OPENSER-SIP-SERVER-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, OBJECT-IDENTITY, Counter32, Unsigned32, Gauge32 FROM SNMPv2-SMI TruthValue, DateAndTime, RowStatus FROM SNMPv2-TC MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF SnmpAdminString FROM SNMP-FRAMEWORK-MIB openserModules, openserSIPServerMIB FROM OPENSER-REG-MIB; openserSIPServerModule MODULE-IDENTITY LAST-UPDATED "200603281700Z" ORGANIZATION "OpenSER" CONTACT-INFO "http://www.openser.org" DESCRIPTION " Adapted from the draft-ietf-sip-mib-10.txt: SIP WG email: sip@ietf.org Co-editor Kevin Lingle Cisco Systems, Inc. postal: 7025 Kit Creek Road P.O. Box 14987 Research Triangle Park, NC 27709 USA email: klingle@cisco.com phone: +1 919 392 2029 Co-editor Joon Maeng email: jmaeng@austin.rr.com Co-editor Jean-Francois Mule CableLabs postal: 858 Coal Creek Circle Louisville, CO 80027 USA email: jf.mule@cablelabs.com phone: +1 303 661 3708 Co-editor Dave Walker email: drwalker@rogers.com Session Initiation Protocol (SIP) Server MIB module. SIP is an application-layer signaling protocol for creating, modifying and terminating multimedia sessions with one or more participants. These sessions include Internet multimedia conferences and Internet telephone calls. SIP is defined in RFC 3261 (June 2002). This MIB is defined for the management of SIP Proxy, Redirect and Registrar Servers. A Proxy Server acts as both a client and a server. It accepts requests from other clients, either responding to them or passing them on to other servers, possibly after modification. A Redirect Server accepts requests from clients and returns zero or more addresses to that client. Unlike a User Agent Server it does not accept calls. A Registrar is a server that accepts REGISTER requests. A Registrar is typically co-located with a Proxy or Redirect Server. Copyright (C) The Internet Society (2006). This version of this MIB module is part of RFC XXXX; see the RFC itself for full legal notices." REVISION "200603281700Z" DESCRIPTION "Added openserSIPRegUserLookupTable." REVISION "200603231200Z" DESCRIPTION "made OpenSER specific and changed sipRegUserTable." REVISION "200603021700Z" DESCRIPTION "Initial version of the IETF SIP-SERVER-MIB module." ::= { openserModules 4 } -- -- Top-Level Components of this MIB. -- openserSIPServerObjects OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for accessible objects in the MIB." ::= { openserSIPServerMIB 1 } openserSIPServerConform OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for conformance specifications." ::= { openserSIPServerMIB 2 } -- -- These groups contain objects common to all SIP servers. -- openserSIPServerCfg OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for server configuration objects." ::= { openserSIPServerObjects 1 } -- -- Common Server Configuration Objects -- -- -- This group contains MIB objects related to SIP Proxy Servers. -- openserSIPProxyCfg OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for proxy server configuration objects." ::= { openserSIPServerObjects 3 } openserSIPProxyStats OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for proxy server statistics objects." ::= { openserSIPServerObjects 4 } -- -- Proxy Server Configuration -- openserSIPProxyStatefulness OBJECT-TYPE SYNTAX INTEGER { stateless(1), transactionStateful(2), callStateful(3) } MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the default mode of operation for the Proxy Server entity. A stateless proxy is a logical entity that does not maintain the client or server transaction state machines when it processes requests. A stateless proxy forwards every request it receives downstream and every response it receives upstream. If the value of this object is stateless(1), the proxy defaults to stateless operations. A transaction stateful proxy, or simply a 'stateful proxy', is a logical entity that maintains the client and server transaction state machines during the processing of a request. A (transaction) stateful proxy is not the same as a call stateful proxy. If the value if this object is transactionStateful(2), the proxy is stateful on a transaction basis. A call stateful proxy is a logical entity if it retains state for a dialog from the initiating INVITE to the terminating BYE request. A call stateful proxy is always transaction stateful, but the converse is not necessarily true. If the value of this object is callStateful(3), the proxy is call stateful." REFERENCE "RFC 3261, Section 16" ::= { openserSIPProxyCfg 1 } openserSIPProxyRecordRoute OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects whether or not the proxy adds itself to the Record-Route header as a default action. This header is used to list the proxies that insist on being in the signaling path for subsequent requests related to the call-leg. If the value of this object is 'true', the proxy adds itself to the end of the Record-Route header, creating the header if required. If the value is 'false', the proxy does not add itself to the Record-Route header." REFERENCE "RFC 3261, Section 20.30" ::= { openserSIPProxyCfg 3 } -- -- Security -- openserSIPProxyAuthMethod OBJECT-TYPE SYNTAX BITS { none(0), tls(1), digest(2) } MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the authentication methods that MAY be used to authenticate request originators. bit 0 no authentication is performed bit 1 TLS is used bit 2 HTTP Digest is used." REFERENCE "RFC 3261 Sections 22, 23, 26, 26.2.3" ::= { openserSIPProxyCfg 4 } -- -- Proxy Server Statistics -- openserSIPNumProxyRequireFailures OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains the number of occurrences of unsupported options being specified in received Proxy- Require headers. Such occurrences result in a 420 Bad Extension status code being returned." ::= { openserSIPProxyStats 1 } -- -- This group contains MIB objects related to SIP Registrars. -- openserSIPRegCfg OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for SIP Registrar Configuration." ::= { openserSIPServerObjects 5 } openserSIPRegStats OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for SIP Registrar Statistics." ::= { openserSIPServerObjects 6 } -- -- Registrar Configuration -- openserSIPRegMaxContactExpiryDuration OBJECT-TYPE SYNTAX Unsigned32 (0..4294967295) UNITS "seconds" MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the maximum expiry that may be requested by a User Agent for a particular Contact. User Agents can specify expiry using either an Expiry header in a REGISTER request, or using an Expires parameter in a Contact header in a REGISTER request. If the value requested by the User Agent is greater than the value of this object, then the contact information is given the duration specified by this object, and that duration is indicated to the User Agent in the response." ::= { openserSIPRegCfg 2 } openserSIPRegMaxUsers OBJECT-TYPE SYNTAX Unsigned32 (1..4294967295) MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the maximum number of users that the Registrar supports. The current number of users is reflected by sipRegCurrentUsers." ::= { openserSIPRegCfg 3 } openserSIPRegCurrentUsers OBJECT-TYPE SYNTAX Gauge32 (0..4294967295) MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the number of users currently registered with the Registrar." ::= { openserSIPRegCfg 4 } openserSIPRegDfltRegActiveInterval OBJECT-TYPE SYNTAX Unsigned32 (1..4294967295) UNITS "seconds" MAX-ACCESS read-only STATUS current DESCRIPTION "This object reflects the default time interval the Registrar considers registrations to be active. The value is used to compute the Expires header in the REGISTER response. If a user agent requests a time interval shorter than specified by this object, the Registrar SHOULD honor that request. If a Contact entry does not have an 'expires' parameter, the value of the Expires header field is used instead. If a Contact entry has no 'expires' parameter and no the Expires header field is present, the value of this object is used as the default value. This object SHOULD be non-volatile and its value SHOULD be kept at reboot." REFERENCE "RFC 3261, Section 10.2" ::= { openserSIPRegCfg 5 } -- -- Per User Information -- openserSIPRegUserTable OBJECT-TYPE SYNTAX SEQUENCE OF OpenSERSIPRegUserEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains information on all users registered to each Registrar in this system." ::= { openserSIPRegCfg 6 } openserSIPRegUserEntry OBJECT-TYPE SYNTAX OpenSERSIPRegUserEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This entry contains information for a single user registered to this Registrar. Each row represents those objects for a particular SIP server present in this system." INDEX { openserSIPUserIndex } ::= { openserSIPRegUserTable 1 } OpenSERSIPRegUserEntry ::= SEQUENCE { openserSIPUserIndex Unsigned32, openserSIPUserUri SnmpAdminString, openserSIPUserAuthenticationFailures Counter32 } openserSIPUserIndex OBJECT-TYPE SYNTAX Unsigned32 (1..4294967295) MAX-ACCESS not-accessible STATUS current DESCRIPTION "This object uniquely identifies a conceptual row in the table." ::= { openserSIPRegUserEntry 1 } openserSIPUserUri OBJECT-TYPE SYNTAX SnmpAdminString MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains the user's address-of-record. It is the main form by which the Registrar knows the user. The format is typically 'user@domain'. It is contained in the To header for all REGISTER requests." ::= { openserSIPRegUserEntry 2 } openserSIPUserAuthenticationFailures OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains a count of the number of times the user has failed authentication." ::= { openserSIPRegUserEntry 3 } -- -- Per Contact Information -- openserSIPContactTable OBJECT-TYPE SYNTAX SEQUENCE OF OpenSERSipContactEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains information on every location where a registered user (specified by sipUserIndex) wishes to be found (i.e. the user has provided Contact information to each SIP Registrar in this system)." ::= { openserSIPRegCfg 7 } openserSIPContactEntry OBJECT-TYPE SYNTAX OpenSERSipContactEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This entry contains information for a single Contact. Multiple contacts may exist for a single user." INDEX { openserSIPUserIndex, openserSIPContactIndex } ::= { openserSIPContactTable 1 } OpenSERSipContactEntry ::= SEQUENCE { openserSIPContactIndex Unsigned32, openserSIPContactDisplayName SnmpAdminString, openserSIPContactURI SnmpAdminString, openserSIPContactLastUpdated DateAndTime, openserSIPContactExpiry DateAndTime, openserSIPContactPreference SnmpAdminString } openserSIPContactIndex OBJECT-TYPE SYNTAX Unsigned32 (1..4294967295) MAX-ACCESS not-accessible STATUS current DESCRIPTION "Along with the sipUserIndex, this object uniquely identifies a conceptual row in the table." ::= { openserSIPContactEntry 1 } openserSIPContactDisplayName OBJECT-TYPE SYNTAX SnmpAdminString MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains the display name for the Contact. For example, 'Santa at Home', or 'Santa on his Sled', corresponding to contact URIs of sip:BigGuy@sip.northpole.ca or sip:sclaus817@sip.mobile.com, respectively." ::= { openserSIPContactEntry 2 } openserSIPContactURI OBJECT-TYPE SYNTAX SnmpAdminString MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains a SIP URI where the user can be contacted. This URI is normally returned to a client from a Redirect Server, or is used as the RequestURI in a SIP request line for requests forwarded by a proxy." ::= { openserSIPContactEntry 3 } openserSIPContactLastUpdated OBJECT-TYPE SYNTAX DateAndTime MAX-ACCESS read-only STATUS current DESCRIPTION "This object indicates the time when this contact information was accepted. If the contact information is updated via a subsequent REGISTER of the same information, this object is also updated." ::= { openserSIPContactEntry 4 } openserSIPContactExpiry OBJECT-TYPE SYNTAX DateAndTime MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains the date and time when the contact information will no longer be valid. Such times may be specified by the user at registration (Expires header or expiry parameter in the Contact information), or a system default can be applied." ::= { openserSIPContactEntry 5 } openserSIPContactPreference OBJECT-TYPE SYNTAX SnmpAdminString MAX-ACCESS read-only STATUS current DESCRIPTION "This object indicates a relative preference for the particular Contact header field value compared to other bindings for this address-of-record. A registering user may provide this preference as a 'qvalue'parameter in the Contact header. The format of this item is a decimal number between 0 and 1 (for example 0.9). Higher values indicate locations preferred by the user." REFERENCE "RFC 3261, Section 10.2.1.2, 16.6 and 20.10" ::= { openserSIPContactEntry 6 } -- -- The regUser lookup table. Clients can use this structure to do a search on -- the SIPRegUserTable and hence the SIPContactTable by URI, to avoid having to -- walk the entire table. -- -- Clients first get an index by doing a 'get' on the LookupCounter: they can -- do a 'set' on the table, providing their index and the URI they wish to find. -- They set the row-status to 'createAndGo'. -- -- The Agent will then search internally through the RegUserTable and populate -- the RegUserIndex column with the RegUserIndex associated with the provided -- URI. Until the index is set, the row-status will be 'notReady', when the -- agent has finished, the row-status will be set to 'active. -- -- When the client has completed the transaction, it should set the row to -- status to 'destroy', so the agent can remove the row and keep this table -- size small. -- -- It is not the intention of this table to keep mappings very long, or update -- them if the RegUserTable changes. Clients should always verify the URI they -- get from the RegUserTable is consitent with what they expect; this is SNMP -- and there are no guarantees between requests. -- openserSIPRegUserLookupCounter OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This is a monotonically increasing value that increases by 1 each time and SNMP-GET is performed on this object. Clients should read this object, then use the result as an index into the openserSIPRegUserLookupTable. This ensures that every row-set in the openserSIPRegUserTable is unique, and each client request will have a unique index to use." ::= { openserSIPRegCfg 8 } openserSIPRegUserLookupTable OBJECT-TYPE SYNTAX SEQUENCE OF OpenSERSIPRegUserLookupEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains the index mapping between a user's URI and the index into the openserSIPRegUserTable. To use this table, a client must first get an index by doing a SNMP GET on openserSIPRegUserLookupCounter. Then it must do an SNMP SET on this table and create a new row in the table: it needs to set the index, the URI the client is looking for, and set the row-status to createAndGo. Then the client can do an SNMP GET on this table with the aquired index. If the row-status is 'active', then the openserSIPRegUserIndex is the index into the openserSIPRegUserTable that has the specified URI (0 zero indicates no such URI). The client should then set the row-status to 'destroy' in order to clean up this entry. Note that after some time the entry will be automatically removed regardless." ::= { openserSIPRegCfg 9 } openserSIPRegUserLookupEntry OBJECT-TYPE SYNTAX OpenSERSIPRegUserLookupEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This entry contains a mapping from a given URI to the index in the openserSIPRegUserTable." INDEX { openserSIPRegUserLookupIndex } ::= { openserSIPRegUserLookupTable 1 } OpenSERSIPRegUserLookupEntry ::= SEQUENCE { openserSIPRegUserLookupIndex Unsigned32, openserSIPRegUserLookupURI SnmpAdminString, openserSIPRegUserIndex Unsigned32, openserSIPRegUserLookupRowStatus RowStatus } openserSIPRegUserLookupIndex OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS not-accessible STATUS current DESCRIPTION "The specific lookup index for a client's request. Rows are automatically reaped, so this table remains small." ::= { openserSIPRegUserLookupEntry 1 } openserSIPRegUserLookupURI OBJECT-TYPE SYNTAX SnmpAdminString MAX-ACCESS read-create STATUS current DESCRIPTION "This object is the URI to locate in the openserSIPRegUserTable. The client must provide this data." ::= { openserSIPRegUserLookupEntry 2 } openserSIPRegUserIndex OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object is the index of the specified URI in the openserSIPRegUserTable. 0 (zero) indicates the URI is not in the openserSIPRegUserTable. This value is provided by the agent when the row-status is set to 'active'." ::= { openserSIPRegUserLookupEntry 3 } openserSIPRegUserLookupRowStatus OBJECT-TYPE SYNTAX RowStatus MAX-ACCESS read-create STATUS current DESCRIPTION "This is the row status for the row: createAndGo: clients set the value to this when they create the row, to indicate they wish the agent to proceed. notReady: the agent has not yet finished processing the request. active: the agent has completed its search and populated the openserSIPRegUserIndex column. destroy: the client sets this value to indicate it has finished with the row. The agent will remove it from the table." ::= { openserSIPRegUserLookupEntry 4 } -- -- Registrar Statistics -- openserSIPRegAcceptedRegistrations OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains a count of the number of REGISTER requests that have been accepted (status code 200) by the Registrar. This includes additions of new contact information, refreshing contact information, as well as requests for deletion of contact information." ::= { openserSIPRegStats 1 } openserSIPRegRejectedRegistrations OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "This object contains a count of the number REGISTER requests that have been rejected by the Registrar." ::= { openserSIPRegStats 2 } -- -- Conformance -- openserSIPServerCompliances OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for server compliences." ::= { openserSIPServerConform 1 } openserSIPServerGroups OBJECT-IDENTITY STATUS current DESCRIPTION "Sub-tree for server group compliences." ::= { openserSIPServerConform 2 } -- -- Compliance Statements -- openserSIPProxyServerCompliance MODULE-COMPLIANCE STATUS current DESCRIPTION "The compliance statement for SIP entities acting as Proxy Servers." MODULE -- this module MANDATORY-GROUPS { openserSIPProxyConfigGroup, openserSIPProxyStatsGroup } ::= { openserSIPServerCompliances 1 } openserSIPRegistrarServerCompliance MODULE-COMPLIANCE STATUS current DESCRIPTION "The compliance statement for SIP entities acting as Registrars." MODULE -- this module MANDATORY-GROUPS { openserSIPRegistrarConfigGroup, openserSIPRegistrarStatsGroup } GROUP openserSIPRegistrarUsersGroup DESCRIPTION "This is an optional group." ::= { openserSIPServerCompliances 3 } -- -- Units of Conformance -- openserSIPProxyConfigGroup OBJECT-GROUP OBJECTS { openserSIPProxyStatefulness, openserSIPProxyRecordRoute, openserSIPProxyAuthMethod } STATUS current DESCRIPTION "A collection of objects providing configuration for SIP Proxy servers." ::= { openserSIPServerGroups 2 } openserSIPProxyStatsGroup OBJECT-GROUP OBJECTS { openserSIPNumProxyRequireFailures } STATUS current DESCRIPTION "A collection of objects providing statistics for SIP Proxy servers." ::= { openserSIPServerGroups 3 } openserSIPRegistrarConfigGroup OBJECT-GROUP OBJECTS { openserSIPRegMaxContactExpiryDuration, openserSIPRegMaxUsers, openserSIPRegCurrentUsers, openserSIPRegDfltRegActiveInterval } STATUS current DESCRIPTION "A collection of objects providing configuration for SIP Registrars." ::= { openserSIPServerGroups 4 } openserSIPRegistrarStatsGroup OBJECT-GROUP OBJECTS { openserSIPRegAcceptedRegistrations, openserSIPRegRejectedRegistrations } STATUS current DESCRIPTION "A collection of objects providing statistics for SIP Registrars." ::= { openserSIPServerGroups 5 } openserSIPRegistrarUsersGroup OBJECT-GROUP OBJECTS { openserSIPUserUri, openserSIPUserAuthenticationFailures, openserSIPContactDisplayName, openserSIPContactURI, openserSIPContactLastUpdated, openserSIPContactExpiry, openserSIPContactPreference } STATUS current DESCRIPTION "A collection of objects related to registered users." ::= {openserSIPServerGroups 6 } openserSIPRegistrarLookupGroup OBJECT-GROUP OBJECTS { openserSIPRegUserLookupCounter, openserSIPRegUserLookupURI, openserSIPRegUserIndex, openserSIPRegUserLookupRowStatus } STATUS current DESCRIPTION "A collection of objects related to registered user lookups." ::= {openserSIPServerGroups 7 } END opensips-2.2.2/modules/snmpstats/mibs/OPENSER-TC000066400000000000000000000264421300170765700213730ustar00rootroot00000000000000-- ******************************************************************* -- OPENSER-TC: OpenSER MIB Textual Conventions -- -- Date of Creation: March 2006 -- Mike Varley -- -- Copyright (c) The Internet Society (2006) -- Ammendments (c) Soma Networks, Inc. (2006) -- -- ******************************************************************* OPENSER-TC DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, Unsigned32 FROM SNMPv2-SMI TEXTUAL-CONVENTION FROM SNMPv2-TC openserModules FROM OPENSER-REG-MIB; openserTcModule MODULE-IDENTITY LAST-UPDATED "200603211200Z" ORGANIZATION "OpenSER" CONTACT-INFO "http://www.openser.org" DESCRIPTION "OpenSER Global Textual Conventions." ::={ openserModules 2 } -- -- SIP type descriptions, taken from SIP-TC in drafts-ietf-sip-mib-10.txt -- OpenSERSIPTransportProtocol ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "This convention is a bit map. Each bit represents a transport protocol. If a bit has value 1, then that selected transport protocol is in some way dependent on the context of the object using this convention. If a bit has value 0, then that transport protocol is not selected. Combinations of bits can be set when multiple transport protocols are selected. bit 0 : a protocol other than those defined here. bit 1 : User Datagram Protocol. bit 2 : Transmission Control Protocol. bit 3 : Stream Control Transmission Protocol. bit 4 : Transport Layer Security Protocol." SYNTAX BITS { other(0), -- none of the following udp(1), tcp(2), sctp(3), tls(4) } -- REFERENCE "RFC 3261, Section 18" OpenSERSIPEntityRole ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "This convention defines the role of a SIP entity. Examples of SIP entities are proxies, user agents, redirect servers, registrars or combinations of the above." SYNTAX BITS { other(0), userAgent(1), proxyServer(2), redirectServer(3), registrarServer(4) } OpenSERSIPMethodIdentifier ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "This is an IETF Assigned Numbers Authority (IANA) assigned number that uniquely identifies a SIP method. The scope of uniqueness is the context of all defined SIP methods. Experimental support of extension methods is acceptable and expected. Extention methods are those defined in Internet-Draft documents but not yet allocated an official number by IANA. To support experimental extension methods, any object using this textual convention as syntax MAY return/accept a method identifier value other than those defined by IANA. That system MUST ensure no collisions with officially assigned method identifier values and MUST provide a identifier to method name mapping via a mechanism like sipMethodSupportedTable." SYNTAX Unsigned32 (1..4294967295) -- -- ITU X.731 state definitions. -- X731AdminState ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "Represents the various possible administrative states. A value of 'locked' means the resource is administratively prohibited from use. A value of 'shuttingDown' means that usage is administratively limited to current instances of use. A value of 'unlocked' means the resource is not administratively prohibited from use. A value of 'unknown' means that this resource is unable to report administrative state." SYNTAX INTEGER { locked (0), shuttingDown (1), unlocked (2), unknown (3) } X731OperState ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "Represents the possible values of operational states. A value of 'disabled' means the resource is totally inoperable. A value of 'enabled' means the resource is partially or fully operable. A value of 'testing' means the resource is currently being tested and cannot therefore report whether it is operational or not. A value of 'unknown' means that this resource is unable to report operational state. " SYNTAX INTEGER { disabled (0), enabled (1), unknown (2) } X731UsageState ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "Represents the possible values of usage states. A value of 'idle' means the resource is servicing no users. A value of 'active' means the resource is currently in use and it has sufficient spare capacity to provide for additional users. A value of 'busy' means the resource is currently in use, but it currently has no spare capacity to provide for additional users. A value of 'unknown' means that this resource is unable to report usage state." SYNTAX INTEGER { idle (0), active (1), busy (2), unknown (3) } X731AlarmState ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "Represents whether a particular sub-system is in an alarm state, and its severity. This value is either reported as 'clear', or one of the alarm severities. An object of this type represents one alarm in the sub-system; the alarm status (below) can be looked at as a collection of AlarmStates. A value of unknown means the object cannot report its alarm state." SYNTAX INTEGER { clear(0), critical(1), major(2), minor(3), unknown(4) } X731AlarmStatus ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "Represents the possible values of alarm status. An Alarm is a persistent indication of an error condition. When no bits of this attribute are set, then no active alarms are known against this entity and it is not under repair. When the 'value of underRepair' is set, the resource is currently being repaired, which, depending on the implementation, may make the other values in this bit string not meaningful. When the value of 'critical' is set, one or more critical alarms are active against the resource. When the value of 'major' is set, one or more major alarms are active against the resource. When the value of 'minor' is set, one or more minor alarms are active against the resource. A value of 'unknown' means that this resource is unable to report alarm state." SYNTAX BITS { underRepair (0), critical(1), major(2), minor(3), alarmOutstanding(4), unknown(5) } X731ProceduralStatus ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "Represents the possible values fo the procedural status. A value of initialization required means the resource requires initialization to be invoked by the manager. The operational state should be disabled. A value of not initialized means the resource requires initialization and this procedure has not been initiatated. The resource initializes itself autonomously. The operational state may be enabled or disabled. A value of initializing means the resource requires initialization and initialization procedure has started but is not yet complete. A value of reporting means the resource has completed some processing operation and is notifying the results of the operation, e.g. a test process is sending its results. The operational state should be enabled. A value of terminating means the resource is in a termination phase." SYNTAX BITS { initializationRequired(0), notInitialized(1), initializing(2), reporting(3), terminating(4), unknown(5) } X731AvailabilityStatus ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "Represents the possible values of availability status. A value of in test means the resource is undergoing a test procedure. A value of failed means the resource has an internal fault that prevents if from operating. The operational state should be disabled. A value of power off means the resource requires power to be applied and is not powered on. The operational state should be disabled. A value of off line means the resource requires a routine operation to be performed to place it online and make it available for use. The operation may be manual or automatic or both. The operational state should be disabled. A value of off duty means the resource has been made inactive by an internal control process that is expect to reactivate the resource at some scheduled time. A value of dependency mens the resource cannot operate because some other resource on which it depends is unavailable. The operational state should be disabled. A value of degraded means the resource is providing a service that is degraded in some respect. The operational state should be enabled. A value of not installed means the resource is not present or is incomplete. The operational state should be disabled. A value of log full indicates that a a log full condition exists." SYNTAX BITS { inTest(0), failed(1), powerOff(2), offLine(3), offDuty(4), dependency(5), degraded(6), notInstalled(7), logFull(8), unknown(9) } X731ControlStatus ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "Represents the possible values of control status. A value of subject to test means the resource is available to normal users but tests may be conducted on it simultaneously. A value of part of services locked means the resource is in a state where a manager has administratively restricted a part of the service from the user(s) of a resource. The administrative state should be unlocked. A value of reserved for test means the resource has been made administratively unavailable since it is undergoing a test procedure. The administrative state should be locked. A value of suspended means the resource has had its service administratively suspended. The administrative state should be unlocked." SYNTAX BITS { subjectToTest(0), partOfServicesLocked(1), reservedForTest(2), suspended(3), unknown(4) } X731StandbyStatus ::= TEXTUAL-CONVENTION STATUS current DESCRIPTION "Represents the possible values of standby status. A value of 'hotStandby' means the resource is not providing service, but it will be immediately able to take over the role of the resource to be backed-up, without the need for initialization activity, and will contain the same information as the resource to be backed up. A value of 'coldStandy' means that the resource is to back-up another resource, but will not be immediately able to take over the role of a resource to be backed up, and will require some initialization activity. A value of 'providingService' means the resource is providing service. A value of 'unknown' means that this resource is unable to report standby state." SYNTAX INTEGER { hotStandby(0), coldStandby(1), providingService(2), unknown(3) } END opensips-2.2.2/modules/snmpstats/openserMIBNotifications.c000066400000000000000000000147431300170765700240010ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Note: this file originally auto-generated by mib2c using * : mib2c.notify.conf,v 5.3 2004/04/15 12:29:19 dts12 Exp $ * * This file contains functions for sending all traps supported by the SNMPStats * module. */ #include #include #include #include "openserMIBNotifications.h" #include "../../dprint.h" #include "../../config.h" static oid snmptrap_oid[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; /* * Sends off an openserMsgQueueDepthMinorEvent trap to the master agent, * assigning the following variable bindings: * * - openserMsgQueueDepth = msgQueueDepth * - openserMsgQueueMinorThreshold = minorThreshold * */ int send_openserMsgQueueDepthMinorEvent_trap(int msgQueueDepth, int minorThreshold) { netsnmp_variable_list *var_list = NULL; oid openserMsgQueueDepthMinorEvent_oid[] = { OPENSER_OID,3,1,3,2,0,1 }; oid openserMsgQueueDepth_oid[] = { OPENSER_OID,3,1,3,1,2,3,1,0 }; oid openserMsgQueueMinorThreshold_oid[] = { OPENSER_OID,3,1,3,1,2,3,2,0 }; snmp_varlist_add_variable(&var_list, snmptrap_oid, OID_LENGTH(snmptrap_oid), ASN_OBJECT_ID, (u_char *)openserMsgQueueDepthMinorEvent_oid, (int)sizeof(openserMsgQueueDepthMinorEvent_oid)); snmp_varlist_add_variable(&var_list, openserMsgQueueDepth_oid, OID_LENGTH(openserMsgQueueDepth_oid), ASN_GAUGE, (u_char *)&msgQueueDepth, sizeof(msgQueueDepth)); snmp_varlist_add_variable(&var_list, openserMsgQueueMinorThreshold_oid, OID_LENGTH(openserMsgQueueMinorThreshold_oid), ASN_INTEGER, (u_char *)&minorThreshold, sizeof(minorThreshold)); send_v2trap( var_list ); snmp_free_varbind( var_list ); return SNMP_ERR_NOERROR; } /* * Sends off an openserMsgQueueDepthMajorEvent trap to the master agent, * assigning the following variable bindings: * * - openserMsgQueueDepth = msgQueueDepth * - openserMsgQueueMajorThreshold = majorThreshold * */ int send_openserMsgQueueDepthMajorEvent_trap(int msgQueueDepth, int majorThreshold) { netsnmp_variable_list *var_list = NULL; oid openserMsgQueueDepthMajorEvent_oid[] = { OPENSER_OID,3,1,3,2,0,2 }; oid openserMsgQueueDepth_oid[] = { OPENSER_OID,3,1,3,1,2,3,1,0 }; oid openserMsgQueueMajorThreshold_oid[] = { OPENSER_OID,3,1,3,1,2,3,3,0 }; snmp_varlist_add_variable(&var_list, snmptrap_oid, OID_LENGTH(snmptrap_oid), ASN_OBJECT_ID, (u_char *)openserMsgQueueDepthMajorEvent_oid, (int)sizeof(openserMsgQueueDepthMajorEvent_oid)); snmp_varlist_add_variable(&var_list, openserMsgQueueDepth_oid, OID_LENGTH(openserMsgQueueDepth_oid), ASN_GAUGE, (u_char *)&msgQueueDepth, sizeof(msgQueueDepth)); snmp_varlist_add_variable(&var_list, openserMsgQueueMajorThreshold_oid, OID_LENGTH(openserMsgQueueMajorThreshold_oid), ASN_INTEGER, (u_char *)&majorThreshold, sizeof(majorThreshold)); send_v2trap( var_list ); snmp_free_varbind( var_list ); return SNMP_ERR_NOERROR; } /* * Sends off an openserDialogLimitMinorEvent trap to the master agent, * assigning the following variable bindings: * * - openserCurNumDialogs = numDialogs * - openserDialogLimitMinorThreshold = threshold * */ int send_openserDialogLimitMinorEvent_trap(int numDialogs, int threshold) { netsnmp_variable_list *var_list = NULL; oid openserDialogLimitMinorEvent_oid[] = { OPENSER_OID,3,1,3,2,0,3 }; oid openserCurNumDialogs_oid[] = { OPENSER_OID,3,1,3,1,3,2,1, 0 }; oid openserDialogLimitMinorThreshold_oid[] = { OPENSER_OID,3,1,3,1,3,2,5, 0 }; snmp_varlist_add_variable(&var_list, snmptrap_oid, OID_LENGTH(snmptrap_oid), ASN_OBJECT_ID, (u_char *)openserDialogLimitMinorEvent_oid, (int)sizeof(openserDialogLimitMinorEvent_oid)); snmp_varlist_add_variable(&var_list, openserCurNumDialogs_oid, OID_LENGTH(openserCurNumDialogs_oid), ASN_GAUGE, (u_char *)&numDialogs, sizeof(numDialogs)); snmp_varlist_add_variable(&var_list, openserDialogLimitMinorThreshold_oid, OID_LENGTH(openserDialogLimitMinorThreshold_oid), ASN_INTEGER, (u_char *)&threshold, sizeof(threshold)); send_v2trap( var_list ); snmp_free_varbind( var_list ); return SNMP_ERR_NOERROR; } /* * Sends off an openserDialogLimitMinorEvent trap to the master agent, * assigning the following variable bindings: * * - openserCurNumDialogs = numDialogs * - openserDialogLimitMinorThreshold = threshold * */ int send_openserDialogLimitMajorEvent_trap(int numDialogs, int threshold) { netsnmp_variable_list *var_list = NULL; oid openserDialogLimitMajorEvent_oid[] = { OPENSER_OID,3,1,3,2,0,4 }; oid openserCurNumDialogs_oid[] = { OPENSER_OID,3,1,3,1,3,2,1, 0 }; oid openserDialogLimitMajorThreshold_oid[] = { OPENSER_OID,3,1,3,1,3,2,6, 0 }; snmp_varlist_add_variable(&var_list, snmptrap_oid, OID_LENGTH(snmptrap_oid), ASN_OBJECT_ID, (u_char *)openserDialogLimitMajorEvent_oid, (int)sizeof(openserDialogLimitMajorEvent_oid)); snmp_varlist_add_variable(&var_list, openserCurNumDialogs_oid, OID_LENGTH(openserCurNumDialogs_oid), ASN_GAUGE, (u_char *)&numDialogs, sizeof(numDialogs)); snmp_varlist_add_variable(&var_list, openserDialogLimitMajorThreshold_oid, OID_LENGTH(openserDialogLimitMajorThreshold_oid), ASN_INTEGER, (u_char *)&threshold, sizeof(threshold)); send_v2trap( var_list ); snmp_free_varbind( var_list ); return SNMP_ERR_NOERROR; } opensips-2.2.2/modules/snmpstats/openserMIBNotifications.h000066400000000000000000000050231300170765700237750ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Note: this file originally auto-generated by mib2c using * : mib2c.notify.conf,v 5.3 2004/04/15 12:29:19 dts12 Exp $ * * This file contains function prototypes for sending all traps supported by the * SNMPStats module. * */ #ifndef OPENSERMIBNOTIFICATIONS_H #define OPENSERMIBNOTIFICATIONS_H /* * Sends off an openserMsgQueueDepthMinorEvent trap to the master agent, * assigning the following variable bindings: * * - openserMsgQueueDepth = msgQueueDepth * - openserMsgQueueMinorThreshold = minorThreshold * */ int send_openserMsgQueueDepthMinorEvent_trap(int msgQueueDepth, int minorThreshold); /* * Sends off an openserMsgQueueDepthMajorEvent trap to the master agent, * assigning the following variable bindings: * * - openserMsgQueueDepth = msgQueueDepth * - openserMsgQueueMajorThreshold = majorThreshold * */ int send_openserMsgQueueDepthMajorEvent_trap(int msgQueueDepth, int majorThreshold); /* * Sends off an openserDialogLimitMinorEvent trap to the master agent, * assigning the following variable bindings: * * - openserCurNumDialogs = numDialogs * - openserDialogLimitMinorThreshold = threshold * */ int send_openserDialogLimitMinorEvent_trap(int numDialogs, int threshold); /* * Sends off an openserDialogLimitMinorEvent trap to the master agent, * assigning the following variable bindings: * * - openserCurNumDialogs = numDialogs * - openserDialogLimitMinorThreshold = threshold * */ int send_openserDialogLimitMajorEvent_trap(int numDialogs, int threshold); #endif /* OPENSERMIBNOTIFICATIONS_H */ opensips-2.2.2/modules/snmpstats/openserObjects.c000066400000000000000000000444421300170765700222300ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Note: this file originally auto-generated by mib2c using * : mib2c.scalar.conf,v 1.9 2005/01/07 09:37:18 dts12 Exp $ * * This file defines all registration and handling of all scalars defined in the * OPENSER-MIB. Please see OPENSER-MIB for the complete descriptions of the * individual scalars. */ #include #include "openserObjects.h" #include "../../dprint.h" #include "../../statistics.h" #include "../../config.h" #include "../../socket_info.h" #include "snmpstats_globals.h" #include "utilities.h" #include "alarm_checks.h" /* * Initializes the openserObjects module. This involves: * * - Registering all OID's * - Setting up handlers for all OID's * * This function is mostly auto-generated. */ void init_openserObjects(void) { static oid openserMsgQueueDepth_oid[] = { OPENSER_OID,3,1,3,1,2,3,1 }; static oid openserMsgQueueMinorThreshold_oid[] = { OPENSER_OID,3,1,3,1,2,3,2 }; static oid openserMsgQueueMajorThreshold_oid[] = { OPENSER_OID,3,1,3,1,2,3,3 }; static oid openserMsgQueueDepthAlarmStatus_oid[] = { OPENSER_OID,3,1,3,1,2,4,1 }; static oid openserMsgQueueDepthMinorAlarm_oid[] = { OPENSER_OID,3,1,3,1,2,4,2 }; static oid openserMsgQueueDepthMajorAlarm_oid[] = { OPENSER_OID,3,1,3,1,2,4,3 }; static oid openserCurNumDialogs_oid[] = { OPENSER_OID,3,1,3,1,3,2,1 }; static oid openserCurNumDialogsInProgress_oid[] = { OPENSER_OID,3,1,3,1,3,2,2 }; static oid openserCurNumDialogsInSetup_oid[] = { OPENSER_OID,3,1,3,1,3,2,3 }; static oid openserTotalNumFailedDialogSetups_oid[] = { OPENSER_OID,3,1,3,1,3,2,4 }; static oid openserDialogLimitMinorThreshold_oid[] = { OPENSER_OID,3,1,3,1,3,2,5 }; static oid openserDialogLimitMajorThreshold_oid[] = { OPENSER_OID,3,1,3,1,3,2,6 }; static oid openserDialogUsageState_oid[] = { OPENSER_OID,3,1,3,1,3,3,1 }; static oid openserDialogLimitAlarmStatus_oid[] = { OPENSER_OID,3,1,3,1,3,4,1 }; static oid openserDialogLimitMinorAlarm_oid[] = { OPENSER_OID,3,1,3,1,3,4,2 }; static oid openserDialogLimitMajorAlarm_oid[] = { OPENSER_OID,3,1,3,1,3,4,3 }; DEBUGMSGTL(("openserObjects", "Initializing\n")); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserMsgQueueDepth", handle_openserMsgQueueDepth, openserMsgQueueDepth_oid, OID_LENGTH(openserMsgQueueDepth_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserMsgQueueMinorThreshold", handle_openserMsgQueueMinorThreshold, openserMsgQueueMinorThreshold_oid, OID_LENGTH(openserMsgQueueMinorThreshold_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserMsgQueueMajorThreshold", handle_openserMsgQueueMajorThreshold, openserMsgQueueMajorThreshold_oid, OID_LENGTH(openserMsgQueueMajorThreshold_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserMsgQueueDepthAlarmStatus", handle_openserMsgQueueDepthAlarmStatus, openserMsgQueueDepthAlarmStatus_oid, OID_LENGTH(openserMsgQueueDepthAlarmStatus_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserMsgQueueDepthMinorAlarm", handle_openserMsgQueueDepthMinorAlarm, openserMsgQueueDepthMinorAlarm_oid, OID_LENGTH(openserMsgQueueDepthMinorAlarm_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserMsgQueueDepthMajorAlarm", handle_openserMsgQueueDepthMajorAlarm, openserMsgQueueDepthMajorAlarm_oid, OID_LENGTH(openserMsgQueueDepthMajorAlarm_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserCurNumDialogs", handle_openserCurNumDialogs, openserCurNumDialogs_oid, OID_LENGTH(openserCurNumDialogs_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserCurNumDialogsInProgress", handle_openserCurNumDialogsInProgress, openserCurNumDialogsInProgress_oid, OID_LENGTH(openserCurNumDialogsInProgress_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserCurNumDialogsInSetup", handle_openserCurNumDialogsInSetup, openserCurNumDialogsInSetup_oid, OID_LENGTH(openserCurNumDialogsInSetup_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserTotalNumFailedDialogSetups", handle_openserTotalNumFailedDialogSetups, openserTotalNumFailedDialogSetups_oid, OID_LENGTH(openserTotalNumFailedDialogSetups_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserDialogLimitMinorThreshold", handle_openserDialogLimitMinorThreshold, openserDialogLimitMinorThreshold_oid, OID_LENGTH(openserDialogLimitMinorThreshold_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserDialogLimitMajorThreshold", handle_openserDialogLimitMajorThreshold, openserDialogLimitMajorThreshold_oid, OID_LENGTH(openserDialogLimitMajorThreshold_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserDialogUsageState", handle_openserDialogUsageState, openserDialogUsageState_oid, OID_LENGTH(openserDialogUsageState_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserDialogLimitAlarmStatus", handle_openserDialogLimitAlarmStatus, openserDialogLimitAlarmStatus_oid, OID_LENGTH(openserDialogLimitAlarmStatus_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserDialogLimitMinorAlarm", handle_openserDialogLimitMinorAlarm, openserDialogLimitMinorAlarm_oid, OID_LENGTH(openserDialogLimitMinorAlarm_oid), HANDLER_CAN_RONLY) ); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserDialogLimitMajorAlarm", handle_openserDialogLimitMajorAlarm, openserDialogLimitMajorAlarm_oid, OID_LENGTH(openserDialogLimitMajorAlarm_oid), HANDLER_CAN_RONLY) ); } /* * The following are thresholds used by: * * - The alarm monitoring process, to decide when to send out traps. * - All scalars involving alarm status's and thresholds. * * By default they are initialized to -1, which disables alarm checks. * These are set through the opensips.cfg file with the following modparams to * the snmpstats module: * * - dlg_minor_threshold * - dlg_major_threshold * * - MsgQueueMinorThreshold * - MsgQueueMajorThreshold * */ static int dialog_minor_threshold = -1; static int dialog_major_threshold = -1; static int msgQueueMinorThreshold = -1; static int msgQueueMajorThreshold = -1; int handle_openserMsgQueueDepth(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int bytesWaiting; bytesWaiting = get_total_bytes_waiting(PROTO_NONE); if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE, (u_char *) &bytesWaiting, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserMsgQueueMinorThreshold(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &msgQueueMinorThreshold, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserMsgQueueMajorThreshold(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &msgQueueMajorThreshold, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserMsgQueueDepthAlarmStatus(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* The MIB specifications say the scalar should be set to 'clear' if * everything is ok. According the X731AlarmStatus specification, * this means that no bits are toggled. So we set the state to zero by * default */ unsigned int state = 0; if (check_msg_queue_alarm(msgQueueMinorThreshold)) { state |= TC_ALARM_STATUS_MINOR; } if (check_msg_queue_alarm(msgQueueMajorThreshold)) { state |= TC_ALARM_STATUS_MAJOR; } if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, (u_char *)&state, 1); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserMsgQueueDepthMinorAlarm(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int x731AlarmState = TC_ALARM_STATE_CLEAR; if (check_msg_queue_alarm(msgQueueMinorThreshold)) { x731AlarmState = TC_ALARM_STATE_MINOR; } if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &x731AlarmState, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserMsgQueueDepthMajorAlarm(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int x731AlarmState = TC_ALARM_STATE_CLEAR; if (check_msg_queue_alarm(msgQueueMajorThreshold)) { x731AlarmState = TC_ALARM_STATE_MAJOR; } if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &x731AlarmState, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserCurNumDialogs(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* This scalar is defined as the number of dialogs in both the EARLY and * the CONFIRMED state. */ int result = get_statistic("active_dialogs"); if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserCurNumDialogsInProgress(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* This scalar is defined as the number of dialogs in the CONFIRMED * state only. active_dialogs includes both confirmed and early, so * we subtract out early_dialogs from active_dialogs. */ int result = get_statistic("active_dialogs") - get_statistic("early_dialogs"); if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserCurNumDialogsInSetup(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* This scalar is defined as the number of dialogs in the EARLY state. * */ int result = get_statistic("early_dialogs"); if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserTotalNumFailedDialogSetups(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("failed_dialogs"); if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserDialogLimitMinorThreshold(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &dialog_minor_threshold, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserDialogLimitMajorThreshold(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &dialog_major_threshold, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserDialogUsageState(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* Return value follows the X731UsageState Textual Convention * * We default to 'unknown' */ int usage_state = TC_USAGE_STATE_UNKNOWN; int num_dialogs = get_statistic("active_dialogs"); if (num_dialogs==0) { usage_state = TC_USAGE_STATE_IDLE; } else { usage_state = TC_USAGE_STATE_ACTIVE; } if ((dialog_major_threshold > -1) && (num_dialogs > dialog_major_threshold)) { usage_state = TC_USAGE_STATE_BUSY; } if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &usage_state, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserDialogLimitAlarmStatus(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* The MIB specifications say the scalar should be set to 'clear' if * everything is ok. According the X731AlarmStatus specification, * this means that no bits are toggled. So we set the state to zero by * default */ unsigned int state = 0; if (check_dialog_alarm(dialog_minor_threshold)) { state |= TC_ALARM_STATUS_MINOR; } if (check_dialog_alarm(dialog_major_threshold)) { state |= TC_ALARM_STATUS_MAJOR; } if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, (u_char *) &state, 1); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserDialogLimitMinorAlarm(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int x731AlarmState = TC_ALARM_STATE_CLEAR; if (check_dialog_alarm(dialog_minor_threshold)) { x731AlarmState = TC_ALARM_STATE_MINOR; } if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &x731AlarmState, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserDialogLimitMajorAlarm(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int x731AlarmState = TC_ALARM_STATE_CLEAR; if (check_dialog_alarm(dialog_major_threshold)) { x731AlarmState = TC_ALARM_STATE_MAJOR; } if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &x731AlarmState, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } /* If a proper integer is passed that is >= -1, then newValue will be set to * val, and 0 returned. Otherwise -1 is returned. */ static int set_if_valid_threshold(modparam_t type, void *val, char *varStr, int *newVal) { if (val==0) { LM_ERR("%s called with a null value!\n", varStr); return -1; } if (type != INT_PARAM) { LM_ERR("%s called with type %d instead of %d!\n", varStr, type, INT_PARAM); return -1; } int new_threshold = (int)(long)(int *)val; if (new_threshold < -1) { LM_ERR("%s called with an invalid threshold=%d!\n", varStr, new_threshold); return -1; } *newVal = new_threshold; return 0; } /* * Paramater Configuration Functions */ /* Handles setting of the message queue minor alarm threshold */ int set_queue_minor_threshold(modparam_t type, void *val) { return set_if_valid_threshold(type, val, "MsgQueueMinorThreshold", &msgQueueMinorThreshold); } /* Handles setting of the message queue major alarm threshold */ int set_queue_major_threshold(modparam_t type, void *val) { return set_if_valid_threshold(type, val, "MsgQueueMajorThreshold", &msgQueueMajorThreshold); } /* Handles setting of the dialog minor threshold */ int set_dlg_minor_threshold(modparam_t type, void *val) { return set_if_valid_threshold(type, val, "set_dlg_minor_threshold", &dialog_minor_threshold); } /* Handles setting of the dialog major threshold */ int set_dlg_major_threshold(modparam_t type, void *val) { return set_if_valid_threshold(type, val, "set_dlg_major_threshold", &dialog_major_threshold); } int get_msg_queue_minor_threshold(void) { return msgQueueMinorThreshold; } int get_msg_queue_major_threshold(void) { return msgQueueMajorThreshold; } int get_dialog_minor_threshold(void) { return dialog_minor_threshold; } int get_dialog_major_threshold(void) { return dialog_major_threshold; } opensips-2.2.2/modules/snmpstats/openserObjects.h000066400000000000000000000052701300170765700222310ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Note: this file originally auto-generated by mib2c using * : mib2c.scalar.conf,v 1.9 2005/01/07 09:37:18 dts12 Exp $ * * This file defines all registration and handling of all scalars defined in the * OPENSER-MIB. Please see OPENSER-MIB for the complete descriptions of the * individual scalars. * */ #ifndef OPENSEROBJECTS_H #define OPENSEROBJECTS_H #include #include #include /* function declarations */ void init_openserObjects(void); Netsnmp_Node_Handler handle_openserMsgQueueDepth; Netsnmp_Node_Handler handle_openserMsgQueueMinorThreshold; Netsnmp_Node_Handler handle_openserMsgQueueMajorThreshold; Netsnmp_Node_Handler handle_openserMsgQueueDepthAlarmStatus; Netsnmp_Node_Handler handle_openserMsgQueueDepthMinorAlarm; Netsnmp_Node_Handler handle_openserMsgQueueDepthMajorAlarm; Netsnmp_Node_Handler handle_openserCurNumDialogs; Netsnmp_Node_Handler handle_openserCurNumDialogsInProgress; Netsnmp_Node_Handler handle_openserCurNumDialogsInSetup; Netsnmp_Node_Handler handle_openserTotalNumFailedDialogSetups; Netsnmp_Node_Handler handle_openserDialogLimitMinorThreshold; Netsnmp_Node_Handler handle_openserDialogLimitMajorThreshold; Netsnmp_Node_Handler handle_openserDialogUsageState; Netsnmp_Node_Handler handle_openserDialogLimitAlarmStatus; Netsnmp_Node_Handler handle_openserDialogLimitMinorAlarm; Netsnmp_Node_Handler handle_openserDialogLimitMajorAlarm; /* The following four functions are used to retrieve thresholds set via the * opensips.cfg configuration file. */ int get_msg_queue_minor_threshold(); int get_msg_queue_major_threshold(); int get_dialog_minor_threshold(); int get_dialog_major_threshold(); #endif /* OPENSEROBJECTS_H */ opensips-2.2.2/modules/snmpstats/openserSIPCommonObjects.c000066400000000000000000000355451300170765700237610ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Note: this file originally auto-generated by mib2c using * : mib2c.scalar.conf,v 1.9 2005/01/07 09:37:18 dts12 Exp $ * * This file defines all registration and handling of all scalars defined in the * OPENSER-SIP-COMMON-MIB. Please see OPENSER-SIP-COMMON-MIB for the complete * descriptions of the individual scalars. */ #include #include #include #include "../../statistics.h" #include "../../config.h" #include #include #include #include "openserSIPCommonObjects.h" #include "snmpstats_globals.h" #include "utilities.h" static char *openserVersion = "SIP/2.0"; static unsigned int openserEntityType = TC_SIP_ENTITY_ROLE_OTHER; /* * Initializes the openserSIPCommonObjects MIB elements. This involves: * * - Registering all OID's * - Setting up handlers for all OID's * * This function is mostly auto-generated. */ void init_openserSIPCommonObjects(void) { static oid openserSIPProtocolVersion_oid[] = { OPENSER_OID,3,1,1,1,1,1 }; static oid openserSIPServiceStartTime_oid[] = { OPENSER_OID,3,1,1,1,1,2 }; static oid openserSIPEntityType_oid[] = { OPENSER_OID,3,1,1,1,1,4 }; static oid openserSIPSummaryInRequests_oid[] = { OPENSER_OID,3,1,1,1,3,1 }; static oid openserSIPSummaryOutRequests_oid[] = { OPENSER_OID,3,1,1,1,3,2 }; static oid openserSIPSummaryInResponses_oid[] = { OPENSER_OID,3,1,1,1,3,3 }; static oid openserSIPSummaryOutResponses_oid[] = { OPENSER_OID,3,1,1,1,3,4 }; static oid openserSIPSummaryTotalTransactions_oid[] = { OPENSER_OID,3,1,1,1,3,5 }; static oid openserSIPCurrentTransactions_oid[] = { OPENSER_OID,3,1,1,1,6,1 }; static oid openserSIPNumUnsupportedUris_oid[] = { OPENSER_OID,3,1,1,1,8,1 }; static oid openserSIPNumUnsupportedMethods_oid[] = { OPENSER_OID,3,1,1,1,8,2 }; static oid openserSIPOtherwiseDiscardedMsgs_oid[] = { OPENSER_OID,3,1,1,1,8,3 }; DEBUGMSGTL(("openserSIPCommonObjects", "Initializing\n")); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPProtocolVersion", handle_openserSIPProtocolVersion, openserSIPProtocolVersion_oid, OID_LENGTH(openserSIPProtocolVersion_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPServiceStartTime", handle_openserSIPServiceStartTime, openserSIPServiceStartTime_oid, OID_LENGTH(openserSIPServiceStartTime_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPEntityType", handle_openserSIPEntityType, openserSIPEntityType_oid, OID_LENGTH(openserSIPEntityType_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPSummaryInRequests", handle_openserSIPSummaryInRequests, openserSIPSummaryInRequests_oid, OID_LENGTH(openserSIPSummaryInRequests_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPSummaryOutRequests", handle_openserSIPSummaryOutRequests, openserSIPSummaryOutRequests_oid, OID_LENGTH(openserSIPSummaryOutRequests_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPSummaryInResponses", handle_openserSIPSummaryInResponses, openserSIPSummaryInResponses_oid, OID_LENGTH(openserSIPSummaryInResponses_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPSummaryOutResponses", handle_openserSIPSummaryOutResponses, openserSIPSummaryOutResponses_oid, OID_LENGTH(openserSIPSummaryOutResponses_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPSummaryTotalTransactions", handle_openserSIPSummaryTotalTransactions, openserSIPSummaryTotalTransactions_oid, OID_LENGTH(openserSIPSummaryTotalTransactions_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPCurrentTransactions", handle_openserSIPCurrentTransactions, openserSIPCurrentTransactions_oid, OID_LENGTH(openserSIPCurrentTransactions_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPNumUnsupportedUris", handle_openserSIPNumUnsupportedUris, openserSIPNumUnsupportedUris_oid, OID_LENGTH(openserSIPNumUnsupportedUris_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPNumUnsupportedMethods", handle_openserSIPNumUnsupportedMethods, openserSIPNumUnsupportedMethods_oid, OID_LENGTH(openserSIPNumUnsupportedMethods_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPOtherwiseDiscardedMsgs", handle_openserSIPOtherwiseDiscardedMsgs, openserSIPOtherwiseDiscardedMsgs_oid, OID_LENGTH(openserSIPOtherwiseDiscardedMsgs_oid), HANDLER_CAN_RONLY)); } int handle_openserSIPProtocolVersion(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, (u_char *) openserVersion, 7); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } /* * The scalar represents what sysUpTime was when OpenSIPS first started. This * data was stored in a file when SNMPStats first started up, as a result of a * call to spawn_sysUpTime_child() */ int handle_openserSIPServiceStartTime(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int elapsedTime = 0; char buffer[SNMPGET_MAX_BUFFER]; FILE *theFile = fopen(SNMPGET_TEMP_FILE, "r"); /* Open the file created by spawn_sysUpTime_child(), and parse our the * required data. */ if (theFile == NULL) { LM_ERR("failed to read sysUpTime file at %s\n", SNMPGET_TEMP_FILE); } else { if (fgets(buffer, SNMPGET_MAX_BUFFER, theFile)==NULL) return SNMP_ERR_GENERR; /* Find the positions of '(' and ')' so we can extract out the * timeticks value. */ char *openBracePosition = strchr(buffer, '('); char *closedBracePosition = strchr(buffer, ')'); /* Make sure that both the '(' and ')' exist in the file, and * that '(' occurs earlier than the ')'. If all these * conditions are true, then attempt to convert the string into * an integer. */ if (openBracePosition != NULL && closedBracePosition != NULL && openBracePosition < closedBracePosition) { elapsedTime = (int) strtol(++openBracePosition, NULL, 10); } fclose(theFile); } if (reqinfo->mode == MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_TIMETICKS, (u_char *) &elapsedTime, sizeof(elapsedTime)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPEntityType(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, (u_char *) &openserEntityType, 1); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPSummaryInRequests(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int numRequests = get_statistic("rcv_requests"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &numRequests, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPSummaryOutRequests(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int out_requests = get_statistic("fwd_requests"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &out_requests, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPSummaryInResponses(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("rcv_replies"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPSummaryOutResponses(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* We can find the number of outbound responses sent by adding three * sources * * 1) fwd_replies from core_stats * 2) local_replies and relayed_replies from the tm module * 3) sent_replies from the sl module. */ int fwd_replies = get_statistic("fwd_replies"); int local_replies = get_statistic("local_replies"); int relayed_replies = get_statistic("relayed_replies"); int sent_replies = get_statistic("sent_replies"); int result = fwd_replies + local_replies + relayed_replies + sent_replies; if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPSummaryTotalTransactions(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* We can find the total number of transactions by summing * UAC_transactions and UAS_transactions. We don't need to add * inuse_transactions because this will already be accounted for in the * two other statistics. */ int result = get_statistic("UAS_transactions") + get_statistic("UAC_transactions"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPCurrentTransactions(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("inuse_transactions"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPNumUnsupportedUris(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("bad_URIs_rcvd"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPNumUnsupportedMethods(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("unsupported_methods"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPOtherwiseDiscardedMsgs(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* We should be able to get this number from existing stats layed out in * the following equation: */ int result = get_statistic("err_requests") + get_statistic("err_replies") + get_statistic("drop_requests") + get_statistic("drop_replies"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } /* * Parameter Setting Functions */ /* If type==STR_PARAM and stringParam is valid, this function will overwrite * openserEntityType with a bit value corresponding to the IETF's RFC for * the SIP MIB. (Textual Convention SipEntityRole). Anything else is * considered an error. * * Returns 0 on success, -1 on failure. */ int handleSipEntityType( modparam_t type, void* val) { /* By default we start off as "other". */ static char firstTime = 1; if (!stringHandlerSanityCheck(type, val, "sipEntityType")) { return -1; } char *strEntityType = (char *)val; /* This is our first time through this function, so we need to change * openserEntityType from its default to 0, allowing our bitmasks below * to work as expected. */ if (firstTime) { firstTime = 0; openserEntityType = 0; } /* Begin our string comparison. This isn't the most efficient approach, * but we don't expect this function to be called anywhere other than at * startup. So our inefficiency is outweiged by simplicity */ if (strcasecmp(strEntityType, "other") == 0) { openserEntityType |= TC_SIP_ENTITY_ROLE_OTHER; } else if (strcasecmp(strEntityType, "userAgent") == 0) { openserEntityType |= TC_SIP_ENTITY_ROLE_USER_AGENT; } else if (strcasecmp(strEntityType, "proxyServer") == 0) { openserEntityType |= TC_SIP_ENTITY_ROLE_PROXY_SERVER; } else if (strcasecmp(strEntityType, "redirectServer") == 0) { openserEntityType |= TC_SIP_ENTITY_ROLE_REDIRECT_SERVER; } else if (strcasecmp(strEntityType, "registrarServer") == 0) { openserEntityType |= TC_SIP_ENTITY_ROLE_REGISTRAR_SERVER; } else { LM_ERR("The configuration file specified sipEntityType=%s," " an unknown type\n", strEntityType); return -1; } return 0; } opensips-2.2.2/modules/snmpstats/openserSIPCommonObjects.h000066400000000000000000000041431300170765700237540ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Note: this file originally auto-generated by mib2c using * : mib2c.scalar.conf,v 1.9 2005/01/07 09:37:18 dts12 Exp $ * * This file defines all registration and handling of all scalars defined in the * OPENSER-SIP-COMMON-MIB. Please see OPENSER-SIP-COMMON-MIB for the complete * descriptions of the individual scalars. */ #ifndef OPENSERSIPCOMMONOBJECTS_H #define OPENSERSIPCOMMONOBJECTS_H void init_openserSIPCommonObjects(void); Netsnmp_Node_Handler handle_openserSIPProtocolVersion; Netsnmp_Node_Handler handle_openserSIPServiceStartTime; Netsnmp_Node_Handler handle_openserSIPEntityType; Netsnmp_Node_Handler handle_openserSIPSummaryInRequests; Netsnmp_Node_Handler handle_openserSIPSummaryOutRequests; Netsnmp_Node_Handler handle_openserSIPSummaryInResponses; Netsnmp_Node_Handler handle_openserSIPSummaryOutResponses; Netsnmp_Node_Handler handle_openserSIPSummaryTotalTransactions; Netsnmp_Node_Handler handle_openserSIPCurrentTransactions; Netsnmp_Node_Handler handle_openserSIPNumUnsupportedUris; Netsnmp_Node_Handler handle_openserSIPNumUnsupportedMethods; Netsnmp_Node_Handler handle_openserSIPOtherwiseDiscardedMsgs; #endif /* OPENSERSIPCOMMONOBJECTS_H */ opensips-2.2.2/modules/snmpstats/openserSIPContactTable.c000066400000000000000000000326061300170765700235550ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Note: this file originally auto-generated by mib2c using * mib2c.array-user.conf * * This file contains the implementation of the openserSIPContact Table. For a * full description of this structure, please see the OPENSER-SIP-SERVER-MIB. * * Some important notes on implementation follow: * * We require OpenSERs usrloc module to inform us when a contact is * added/removed. The general callback process works as follows: * * 1) On startup, we register handleContactCallbacks() for USRLOC callbacks, so * we can be informed whenever a contact is added/removed from the system. * This registration happens with a call to registerForUSRLOCCallbacks(). * (This is actually called when the SNMPStats module is initialized) * * 2) Whenever we receive a contact callback, handleContactCallbacks() will * quickly add the contact information and operation type to the * interprocess buffer. * * 3) When we receive an SNMP request for user/contact information, we consume * the interprocess buffer with consumeInterprocessBuffer(). The function * will add/delete rows to the tables, and then service the SNMP request. * * Notes: * * - The interprocess buffer was necessary, because NetSNMP's containers can be * very inefficient at adding large amounts of data at a time, such as when * OpenSIPS first starts up. It was decided its better to make an SNMP manager * wait for data, instead of slowing down the rest of OpenSIPS while the * sub-agent processes the data. * * - It is important to send periodic SNMP requests to this table (or the user * table), to make sure the process buffer doesn't get too large. * */ #include #include #include #include #include "hashTable.h" #include "interprocess_buffer.h" #include "utilities.h" #include "openserSIPContactTable.h" #include "snmpstats_globals.h" #include "../../mem/mem.h" #include "../../str.h" #include "../../sr_module.h" #include "../../locking.h" #include "../usrloc/usrloc.h" #include "../usrloc/ucontact.h" static netsnmp_handler_registration *my_handler = NULL; static netsnmp_table_array_callbacks cb; oid openserSIPContactTable_oid[] = { openserSIPContactTable_TABLE_OID }; size_t openserSIPContactTable_oid_len = OID_LENGTH(openserSIPContactTable_oid); /* * This function adds a new contactToIndexStruct_t record to the front of * 'contactRecord'. * * The structure is used to map a contact name to the SNMPStats modules integer * indexing scheme. It will be used later when a delete command comes in, and * we need to find out which SNMP row the information is stored under. */ int insertContactRecord( contactToIndexStruct_t **contactRecord, int index, char *name) { int nameLength =strlen(name); contactToIndexStruct_t *newContactRecord = (contactToIndexStruct_t *) pkg_malloc(sizeof(contactToIndexStruct_t) +(nameLength+1)* sizeof(char)); if (newContactRecord == NULL) { LM_ERR("no more pkg memory\n"); return 0; } newContactRecord->next = *contactRecord; newContactRecord->contactName = (char*)newContactRecord + sizeof(contactToIndexStruct_t); memcpy(newContactRecord->contactName, name, nameLength); newContactRecord->contactName[nameLength]= '\0'; newContactRecord->contactIndex = index; *contactRecord = newContactRecord; return 1; } /* * This function will remove the contactToIndexStruct_T record matching * 'contactName' from the users contactToIndexStruct_t linked-list, and return * the records index. In the event that the record could not be found, 0 will * be returned. */ int deleteContactRecord(contactToIndexStruct_t **contactRecord,char *contactName) { int contactIndexToReturn; contactToIndexStruct_t *currentContact = *contactRecord; contactToIndexStruct_t *previousContact = *contactRecord; while (currentContact != NULL) { if (strcmp(currentContact->contactName, contactName) == 0) { /* This means that this is the first element. Link up * the pointer to the next element */ if (currentContact == previousContact) { *contactRecord = currentContact->next; } else { previousContact->next = currentContact->next; } contactIndexToReturn = currentContact->contactIndex; pkg_free(currentContact); return contactIndexToReturn; } previousContact = currentContact; currentContact = currentContact->next; } return 0; } /* * Creates an SNMP row and inserts it into the contact table. This function * should only be called when the interprocess buffer is being consumed. * * Returns: 1 on success, and 0 otherwise. */ int createContactRow(int userIndex, int contactIndex, char *contactName, ucontact_t *contactInfo) { openserSIPContactTable_context *theRow; oid *OIDIndex; int stringLength; theRow = SNMP_MALLOC_TYPEDEF(openserSIPContactTable_context); if (theRow == NULL) { LM_ERR("failed to create a row for openserSIPContactTable\n"); return 0; } /* We need enough memory for both the user index and contact index. */ OIDIndex = pkg_malloc(sizeof(oid)*2); if (OIDIndex == NULL) { free(theRow); LM_ERR("failed to create a row for openserSIPContactTable\n"); return 0; } stringLength = strlen(contactName); /* Generate the Rows Index */ OIDIndex[0] = userIndex; OIDIndex[1] = contactIndex; theRow->index.len = 2; theRow->index.oids = OIDIndex; theRow->openserSIPContactIndex = contactIndex; /* Fill in the rest of the rows columns */ theRow->openserSIPContactURI = (unsigned char*) pkg_malloc((stringLength+ 1)* sizeof(char)); if(theRow->openserSIPContactURI == NULL) { pkg_free(OIDIndex); free(theRow); LM_ERR("failed to allocate memory for contact name\n"); return 0; } memcpy(theRow->openserSIPContactURI, contactName, stringLength); theRow->openserSIPContactURI[stringLength] = '\0'; theRow->openserSIPContactURI_len = stringLength; theRow->contactInfo = contactInfo; CONTAINER_INSERT(cb.container, theRow); return 1; } /* * Removes the row indexed by userIndex and contactIndex, and free's up the * memory allocated to it. If the row could not be found, then nothing is done. */ void deleteContactRow(int userIndex, int contactIndex) { openserSIPContactTable_context *theRow; netsnmp_index indexToRemove; oid indexToRemoveOID[2]; /* Form the OID Index of the row so we can search for it */ indexToRemoveOID[0] = userIndex; indexToRemoveOID[1] = contactIndex; indexToRemove.oids = indexToRemoveOID; indexToRemove.len = 2; theRow = CONTAINER_FIND(cb.container, &indexToRemove); /* The ContactURI is shared memory, the index.oids was allocated from * pkg_malloc(), and theRow was made with the NetSNMP API which uses * malloc() */ if (theRow != NULL) { CONTAINER_REMOVE(cb.container, &indexToRemove); pkg_free(theRow->openserSIPContactURI); pkg_free(theRow->index.oids); free(theRow); } } /* * Initializes the openserSIPContactTable module. This involves: * * 1) Registering the tables OID with the master agent * * 2) Creating a default row, so that there is a row to query to trigger the * consumption of the interprocess buffer. */ void init_openserSIPContactTable(void) { initialize_table_openserSIPContactTable(); static char *defaultUser = "DefaultUser"; createContactRow(1, 1, defaultUser, NULL); } /* * Initialize the openserSIPContactTable table by defining its contents and how * it's structured. * * This function is mostly auto-generated. */ void initialize_table_openserSIPContactTable(void) { netsnmp_table_registration_info *table_info; if(my_handler) { snmp_log(LOG_ERR, "initialize_table_openserSIPContactTable_" "handler called again\n"); return; } memset(&cb, 0x00, sizeof(cb)); /** create the table structure itself */ table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); my_handler = netsnmp_create_handler_registration( "openserSIPContactTable", netsnmp_table_array_helper_handler, openserSIPContactTable_oid, openserSIPContactTable_oid_len, HANDLER_CAN_RONLY); if (!my_handler || !table_info) { snmp_log(LOG_ERR, "malloc failed in initialize_table_openser" "SIPContactTable_handler\n"); return; /** mallocs failed */ } /** index: openserSIPUserIndex */ netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED); /** index: openserSIPContactIndex */ netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED); table_info->min_column = openserSIPContactTable_COL_MIN; table_info->max_column = openserSIPContactTable_COL_MAX; /*************************************************** * registering the table with the master agent */ cb.get_value = openserSIPContactTable_get_value; cb.container = netsnmp_container_find("openserSIPContactTable_primary:" "openserSIPContactTable:" "table_container"); DEBUGMSGTL(("initialize_table_openserSIPContactTable", "Registering table openserSIPContactTable " "as a table array\n")); netsnmp_table_container_register(my_handler, table_info, &cb, cb.container, 1); } /* * This routine is called to process get requests for elements of the table. * * The function differs from its original auto-generated form in that the row * itself doesn't store all the data that is needed. Some of the values * (openserSIPContactURI, openserSIPContactExpiry, openserSIPContactPreference) * may have changed since the row was first created. Therefore, this data is * retrieved when it is requested for. */ int openserSIPContactTable_get_value( netsnmp_request_info *request, netsnmp_index *item, netsnmp_table_request_info *table_info ) { static char defaultExpiry[8] = {0, 0, 0, 0, 0, 0, 0, 0}; /* Needs to be large enough to hold the null terminated string "-0.01". */ char contactPreference[6]; float preferenceAsFloat = -1; char *retrievedExpiry; struct tm *timeValue; /* First things first, we need to consume the interprocess buffer, in * case something has changed. We want to return the freshest data. */ consumeInterprocessBuffer(); netsnmp_variable_list *var = request->requestvb; openserSIPContactTable_context *context = (openserSIPContactTable_context *)item; switch(table_info->colnum) { case COLUMN_OPENSERSIPCONTACTDISPLAYNAME: /* FIXME: WHERE DO WE FIND THIS?? Setting to the same * thing as contact uri for now. */ snmp_set_var_typed_value(var, ASN_OCTET_STR, (unsigned char*) context->openserSIPContactURI, context->openserSIPContactURI_len); break; case COLUMN_OPENSERSIPCONTACTURI: snmp_set_var_typed_value(var, ASN_OCTET_STR, (unsigned char*) context->openserSIPContactURI, context->openserSIPContactURI_len); break; case COLUMN_OPENSERSIPCONTACTLASTUPDATED: if (context->contactInfo != NULL) { timeValue = localtime(&(context->contactInfo->last_modified)); retrievedExpiry = convertTMToSNMPDateAndTime(timeValue); } else { retrievedExpiry = defaultExpiry; } snmp_set_var_typed_value(var, ASN_OCTET_STR, (unsigned char*) retrievedExpiry, 8); break; case COLUMN_OPENSERSIPCONTACTEXPIRY: if (context->contactInfo != NULL) { timeValue = localtime(&(context->contactInfo->expires)); retrievedExpiry = convertTMToSNMPDateAndTime(timeValue); } else { retrievedExpiry = defaultExpiry; } snmp_set_var_typed_value(var, ASN_OCTET_STR, (unsigned char*) retrievedExpiry, 8); break; case COLUMN_OPENSERSIPCONTACTPREFERENCE: if (context->contactInfo != NULL) { preferenceAsFloat = context->contactInfo->q; } /* OpenSER stores the q-value as an integer for speed * purposes. We need to convert this value to a float * to conform to the MIB and RFC specifications of the q * value. */ preferenceAsFloat /= 100.0; /* Convert the float into a string, as specified by the * MIB. */ sprintf(contactPreference, "%5.2f", preferenceAsFloat); snmp_set_var_typed_value(var, ASN_OCTET_STR, (unsigned char*) contactPreference, 5); break; default: /** We shouldn't get here */ snmp_log(LOG_ERR, "unknown column in " "openserSIPContactTable_get_value\n"); return SNMP_ERR_GENERR; } return SNMP_ERR_NOERROR; } /* * openserSIPContactTable_get_by_idx is an auto-generated function. */ const openserSIPContactTable_context * openserSIPContactTable_get_by_idx(netsnmp_index * hdr) { return (const openserSIPContactTable_context *) CONTAINER_FIND(cb.container, hdr ); } opensips-2.2.2/modules/snmpstats/openserSIPContactTable.h000066400000000000000000000157101300170765700235570ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Note: this file was originally auto-generated by mib2c using * mib2c.array-user.conf * * This file contains the implementation of the openserSIPContact Table. For a * full description of this structure, please see the OPENSER-SIP-SERVER-MIB. * * Some important notes on implementation follow: * * We require OpenSERs usrloc module to inform us when a contact is * added/removed. The general callback process works as follows: * * 1) On startup, we register handleContactCallbacks() for USRLOC callbacks, so * we can be informed whenever a contact is added/removed from the system. * This registration happens with a call to registerForUSRLOCCallbacks(). * (This is actually called when the SNMPStats module is initialized) * * 2) Whenever we receive a contact callback, handleContactCallbacks() will * quickly add the contact information and operation type to the * interprocess buffer. * * 3) When we receive an SNMP request for user/contact information, we consume * the interprocess buffer with consumeInterprocessBuffer(). The function * will add/delete rows to the tables, and then service the SNMP request. * * Notes: * * - The interprocess buffer was necessary, because NetSNMP's containers can be * very inefficient at adding large amounts of data at a time, such as when * OpenSIPS first starts up. It was decided its better to make an SNMP manager * wait for data, instead of slowing down the rest of OpenSIPS while the * sub-agent processes the data. * * - It is important to send periodic SNMP requests to this table (or the user * table), to make sure the process buffer doesn't get too large. */ #ifndef OPENSERSIPCONTACTTABLE_H #define OPENSERSIPCONTACTTABLE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "interprocess_buffer.h" #include "../usrloc/ucontact.h" #include "../../config.h" /* This strucutre is actually quite different from what was generated by mib2c. * Specifically, the scalars openserSIPContactURI, openserSIPContactExpiry, and * openserSIPContactPreference have been removed from the rows definition. This * was done because the values change over time. Therefore we retrieve the data * only when it is requested for, instead of storing stale data. */ typedef struct openserSIPContactTable_context_s { netsnmp_index index; /** THIS MUST BE FIRST!!! */ unsigned long openserSIPContactIndex; unsigned char *openserSIPContactURI; long openserSIPContactURI_len; /* A pointer to the rest of the contact structure, giving us access to * openserSIPContactURI, openserSIPContactExpirty, and * openserSIPContactPreference. */ ucontact_t *contactInfo; void * data; } openserSIPContactTable_context; /******************************/ /* Customized SNMP Prototypes */ /******************************/ /* * Creates an SNMP row and inserts it into the contact table. This function * should only be called when the interprocess buffer is being consumed. * * Returns: 1 on success, and 0 otherwise. */ int createContactRow(int userIndex, int contactIndex, char *contactName, ucontact_t *contactInfo); /* * Removes the row indexed by userIndex and contactIndex, and free's up the * memory allocated to it. If the row could not be found, then nothing is done. */ void deleteContactRow(int userIndex, int contactIndex); /* * This function adds a new contactToIndexStruct_t record to the front of * 'contactRecord'. * * The structure is used to map a contact name to the SNMPStats modules integer * indexing scheme. It will be used later when a delete command comes in, and * we need to find out which SNMP row the information is stored under. */ int insertContactRecord(contactToIndexStruct_t **contactRecord, int index, char *name); /* * This function will remove the contactToIndexStruct_T record matching * 'contactName' from the users contactToIndexStruct_t linked-list, and return * the records index. In the event that the record could not be found, 0 will * be returned. */ int deleteContactRecord(contactToIndexStruct_t **contactRecord, char *contactName); /********************************/ /* Normal SNMP Table Prototypes */ /********************************/ /* * Initializes the openserSIPContactTable module. This involves: * * 1) Registering the tables OID with the master agent * * 2) Creating a default row, so that there is a row to query to trigger the * consumption of the interprocess buffer. */ void init_openserSIPContactTable(void); /* * Initialize the openserSIPContactTable table by defining its contents and how * it's structured. * * This function is mostly auto-generated. */ void initialize_table_openserSIPContactTable(void); const openserSIPContactTable_context * openserSIPContactTable_get_by_idx( netsnmp_index *); const openserSIPContactTable_context * openserSIPContactTable_get_by_idx_rs( netsnmp_index *, int row_status); /* This routine is called to process get requests for elements of the table. */ int openserSIPContactTable_get_value(netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *); /* oid declarations */ extern oid openserSIPContactTable_oid[]; extern size_t openserSIPContactTable_oid_len; #define openserSIPContactTable_TABLE_OID OPENSER_OID,3,1,2,1,5,7 /************************************************************* * column number definitions for table openserSIPContactTable */ #define COLUMN_OPENSERSIPCONTACTINDEX 1 #define COLUMN_OPENSERSIPCONTACTDISPLAYNAME 2 #define COLUMN_OPENSERSIPCONTACTURI 3 #define COLUMN_OPENSERSIPCONTACTLASTUPDATED 4 #define COLUMN_OPENSERSIPCONTACTEXPIRY 5 #define COLUMN_OPENSERSIPCONTACTPREFERENCE 6 #define openserSIPContactTable_COL_MIN 2 #define openserSIPContactTable_COL_MAX 6 #ifdef __cplusplus } #endif #endif /** OPENSERSIPCONTACTTABLE_H */ opensips-2.2.2/modules/snmpstats/openserSIPMethodSupportedTable.c000066400000000000000000000170151300170765700253050ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Originally Generated with mib2c using mib2c.array-user.conf * * The file implements the openserSIPMethodSupportedTable. The table is * populated by looking to see which modules are loaded, and guessing what SIP * Methods they provide. It is quite possible that this initial implementation * is not very good at guessing. This should be fixed in future releases as * more information becomes available. * * For full details, please see the OPENSER-SIP-COMMON-MIB. * */ #include "../../sr_module.h" #include "../../mem/mem.h" #include #include #include #include #include "openserSIPMethodSupportedTable.h" static netsnmp_handler_registration *my_handler = NULL; static netsnmp_table_array_callbacks cb; oid openserSIPMethodSupportedTable_oid[] = { openserSIPMethodSupportedTable_TABLE_OID }; size_t openserSIPMethodSupportedTable_oid_len = OID_LENGTH(openserSIPMethodSupportedTable_oid); /* Create a row at the given index, containing stringToRegister, and insert it * into the table. Note that stringToRegister will be copied, so it is not * necessary to pre-allocate this string anywhere. */ void createRow(int index, char *stringToRegister) { openserSIPMethodSupportedTable_context *theRow; oid *OIDIndex; char *copiedString; int stringLength; theRow = SNMP_MALLOC_TYPEDEF(openserSIPMethodSupportedTable_context); if (theRow == NULL) { LM_ERR("failed to create a row for openserSIPMethodSupportedTable\n"); return; } OIDIndex = pkg_malloc(sizeof(oid)); if (OIDIndex == NULL) { free(theRow); LM_ERR("failed to create a row for openserSIPMethodSupportedTable\n"); return; } stringLength = strlen(stringToRegister); copiedString = pkg_malloc((stringLength + 1) * sizeof(char)); if (copiedString == NULL) { LM_ERR("failed to create a row for openserSIPMethodSupportedTable\n"); return; } strcpy(copiedString, stringToRegister); OIDIndex[0] = index; theRow->index.len = 1; theRow->index.oids = OIDIndex; theRow->openserSIPMethodSupportedIndex = index; theRow->openserSIPMethodName = (unsigned char*) copiedString; theRow->openserSIPMethodName_len = stringLength; CONTAINER_INSERT(cb.container, theRow); } /* Initializes the openserSIPMethodSupportedTable, and populates the tables * contents */ void init_openserSIPMethodSupportedTable(void) { initialize_table_openserSIPMethodSupportedTable(); /* Tables is defined as follows: * * 1) METHOD_INVITE * 2) METHOD_CANCEL * 3) METHOD_ACK * 4) METHOD_BYE * 5) METHOD_INFO * 6) METHOD_OPTIONS * 7) METHOD_UPDATE * 8) METHOD_REGISTER * 9) METHOD_MESSAGE * 10) METHOD_SUBSCRIBE * 11) METHOD_NOTIFY * 12) METHOD_PRACK * 13) METHOD_REFER * 14) METHOD_PUBLISH * * We should keep these indices fixed. For example if we don't support * METHOD_REGISTER but we do support METHOD_MESSAGE, then METHOD_MESSAGE * should still be at index 9. * * NOTE: My way of checking what METHODS we support is probably wrong. * Please feel free to correct it! */ if (module_loaded("sl")) { createRow(1, "METHOD_INVITE"); createRow(2, "METHOD_CANCEL"); createRow(3, "METHOD_ACK"); } if (module_loaded("tm")) { createRow(4, "METHOD_BYE"); } if (module_loaded("options")) { createRow(6, "METHOD_OPTIONS"); } if (module_loaded("dialog")) { createRow(7, "METHOD_UPDATE"); } if (module_loaded("registrar")) { createRow(8, "METHOD_REGISTER"); createRow(10, "METHOD_SUBSCRIBE"); createRow(11, "METHOD_NOTIFY"); } createRow(5, "METHOD_INFO"); createRow(9, "METHOD_MESSAGE"); /* I'm not sure what these guys are, so saying we support them by * default. */ createRow(12, "METHOD_PRACK"); createRow(13, "METHOD_REFER"); createRow(14, "METHOD_PUBLISH"); } /* Initialize the openserSIPMethodSupportedTable by defining its structure and * callback mechanisms */ void initialize_table_openserSIPMethodSupportedTable(void) { netsnmp_table_registration_info *table_info; if(my_handler) { snmp_log(LOG_ERR, "initialize_table_openserSIPMethodSupported" "Table_handler called again\n"); return; } memset(&cb, 0x00, sizeof(cb)); /** create the table structure itself */ table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); my_handler = netsnmp_create_handler_registration( "openserSIPMethodSupportedTable", netsnmp_table_array_helper_handler, openserSIPMethodSupportedTable_oid, openserSIPMethodSupportedTable_oid_len, HANDLER_CAN_RONLY); if (!my_handler || !table_info) { snmp_log(LOG_ERR, "malloc failed in initialize_table_openser" "SIPMethodSupportedTable_handler\n"); return; } netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED); table_info->min_column = openserSIPMethodSupportedTable_COL_MIN; table_info->max_column = openserSIPMethodSupportedTable_COL_MAX; /*************************************************** * registering the table with the master agent */ cb.get_value = openserSIPMethodSupportedTable_get_value; cb.container = netsnmp_container_find("openserSIPMethodSupportedTable_primary:" "openserSIPMethodSupportedTable:" "table_container"); DEBUGMSGTL(("initialize_table_openserSIPMethodSupportedTable", "Registering table openserSIPMethodSupportedTable" "as a table array\n")); netsnmp_table_container_register(my_handler, table_info, &cb, cb.container, 1); } /* * This routine is called to process get requests for elements of the table. * * The function is pretty much left as is from the auto-generated code. */ int openserSIPMethodSupportedTable_get_value( netsnmp_request_info *request, netsnmp_index *item, netsnmp_table_request_info *table_info ) { netsnmp_variable_list *var = request->requestvb; openserSIPMethodSupportedTable_context *context = (openserSIPMethodSupportedTable_context *)item; switch(table_info->colnum) { case COLUMN_OPENSERSIPMETHODNAME: /** SnmpAdminString = ASN_OCTET_STR */ snmp_set_var_typed_value(var, ASN_OCTET_STR, (unsigned char*) context->openserSIPMethodName, context->openserSIPMethodName_len ); break; default: /** We shouldn't get here */ snmp_log(LOG_ERR, "unknown column in openserSIPMethod" "SupportedTable_get_value\n"); return SNMP_ERR_GENERR; } return SNMP_ERR_NOERROR; } /* * openserSIPMethodSupportedTable_get_by_idx is an auto-generated function. */ const openserSIPMethodSupportedTable_context * openserSIPMethodSupportedTable_get_by_idx(netsnmp_index * hdr) { return (const openserSIPMethodSupportedTable_context *) CONTAINER_FIND(cb.container, hdr ); } opensips-2.2.2/modules/snmpstats/openserSIPMethodSupportedTable.h000066400000000000000000000065211300170765700253120ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Originally Generated with Mib2c using mib2c.array-user.conf. * * This file defines the prototypes used to define the * openserSIPMethodSupportedTable. For full details, please see the * OPENSER-SIP-COMMON-MIB. */ #ifndef OPENSERSIPMETHODSUPPORTEDTABLE_H #define OPENSERSIPMETHODSUPPORTEDTABLE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "../../config.h" /* * This strucutre represents a single row in the SNMP table, and is mostly * auto-generated. */ typedef struct openserSIPMethodSupportedTable_context_s { netsnmp_index index; /** OpenSERSIPMethodIdentifier = ASN_UNSIGNED */ unsigned long openserSIPMethodSupportedIndex; /** SnmpAdminString = ASN_OCTET_STR */ unsigned char *openserSIPMethodName; long openserSIPMethodName_len; void * data; } openserSIPMethodSupportedTable_context; /* Initializes the openserSIPMethodSupportedTable, and populates the tables * contents */ void init_openserSIPMethodSupportedTable(void); /* Defines openserSIPMethodSupportedTable's structure and callback mechanisms */ void initialize_table_openserSIPMethodSupportedTable(void); /* * This routine is called to process get requests for elements of the table. * * The function is pretty much left as is from the auto-generated code. */ int openserSIPMethodSupportedTable_get_value(netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *); const openserSIPMethodSupportedTable_context * openserSIPMethodSupportedTable_get_by_idx(netsnmp_index *); const openserSIPMethodSupportedTable_context * openserSIPMethodSupportedTable_get_by_idx_rs(netsnmp_index *, int row_status); /* * oid declarations */ extern oid openserSIPMethodSupportedTable_oid[]; extern size_t openserSIPMethodSupportedTable_oid_len; #define openserSIPMethodSupportedTable_TABLE_OID OPENSER_OID,3,1,1,1,1,7 /* * column number definitions for table openserSIPMethodSupportedTable */ #define COLUMN_OPENSERSIPMETHODSUPPORTEDINDEX 1 #define COLUMN_OPENSERSIPMETHODNAME 2 #define openserSIPMethodSupportedTable_COL_MIN 2 #define openserSIPMethodSupportedTable_COL_MAX 2 #ifdef __cplusplus } #endif #endif /** OPENSERSIPMETHODSUPPORTEDTABLE_H */ opensips-2.2.2/modules/snmpstats/openserSIPPortTable.c000066400000000000000000000226351300170765700231070ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Originally Generated with mib2c using mib2c.array-user.conf * * This file implements the openserSIPPortTable. For a full description of the table, * please see the OPENSER-SIP-COMMON-MIB. * */ #include "snmpstats_globals.h" #include "../../socket_info.h" #include #include #include #include #include "openserSIPPortTable.h" #include "../../statistics.h" #include "../../mem/mem.h" static netsnmp_handler_registration *my_handler = NULL; static netsnmp_table_array_callbacks cb; oid openserSIPPortTable_oid[] = { openserSIPPortTable_TABLE_OID }; size_t openserSIPPortTable_oid_len = OID_LENGTH(openserSIPPortTable_oid); /* Returns a new OID with the following structure: * * ipType.NUM_IP_OCTETS.ipAddress[0].ipAddress[1]...ipAddress[NUM_IP_OCTETS].portNumber * * sizeOfOID will be assigned the length of the oid. * * Note: This function returns a newly allocated block of memory. Make sure to * deallocate the memory when you no longer need it. */ oid *createIndex(int ipType, int *ipAddress, int *sizeOfOID) { oid *currentOIDIndex; int i; /* The size needs to be large enough such that it can store the ipType * (one octet), the prefixed length (one octet), the number of * octets to the IP Address (NUM_IP_OCTETS), and the port. */ *sizeOfOID = NUM_IP_OCTETS + 3; /* Allocate space for the OID Index. */ currentOIDIndex = pkg_malloc((*sizeOfOID) * sizeof(oid)); if (currentOIDIndex == NULL) { LM_ERR("failed to create a row for openserSIPPortTable\n"); *sizeOfOID = 0; return NULL; } /* Assign the OID Index */ currentOIDIndex[0] = ipType; currentOIDIndex[1] = NUM_IP_OCTETS; for (i = 0; i < NUM_IP_OCTETS; i++) { currentOIDIndex[i+2] = ipAddress[i]; } /* Extract out the port number */ currentOIDIndex[NUM_IP_OCTETS+2] = ipAddress[NUM_IP_OCTETS]; return currentOIDIndex; } /* Will return an existing row indexed by the parameter list if one exists, and * return a new one otherwise. If the row is new, then the provided index will be * assigned to the new row. * * Note: NULL will be returned on an error */ openserSIPPortTable_context *getRow(int ipType, int *ipAddress) { int lengthOfOID; oid *currentOIDIndex = createIndex(ipType, ipAddress, &lengthOfOID); if (currentOIDIndex == NULL) { return NULL; } netsnmp_index theIndex; theIndex.oids = currentOIDIndex; theIndex.len = lengthOfOID; openserSIPPortTable_context *rowToReturn; /* Lets check to see if there is an existing row. */ rowToReturn = CONTAINER_FIND(cb.container, &theIndex); /* We found an existing row, so there is no need to create a new one. * Let's return it to the caller. */ if (rowToReturn != NULL) { /* We don't need the index we allocated anymore, because the * existing row already has its own copy, so free the memory */ pkg_free(currentOIDIndex); return rowToReturn; } /* If we are here then the row doesn't exist yet. So lets create it. */ rowToReturn = SNMP_MALLOC_TYPEDEF(openserSIPPortTable_context); /* Not enough memory to create the new row. */ if (rowToReturn == NULL) { pkg_free(currentOIDIndex); return NULL; } /* Assign the Container Index. */ rowToReturn->index.len = lengthOfOID; rowToReturn->index.oids = currentOIDIndex; memcpy(rowToReturn->openserSIPStringIndex, currentOIDIndex, NUM_IP_OCTETS + 3); rowToReturn->openserSIPStringIndex_len = NUM_IP_OCTETS + 3; /* Insert the new row into the table */ CONTAINER_INSERT(cb.container, rowToReturn); return rowToReturn; } /* * Will create rows for this table from theList. The final parameter snmpIndex * can point to any integer >= zero. All rows created by this function will be * indexed starting at snmpIndex++. The parameter is implemented as a pointer * to an integer so that if the function is called again with another * 'protocol', we can continue from the last index. */ void createRowsFromIPList(int *theList, int listSize, int protocol, int *snmpIndex) { openserSIPPortTable_context *currentRow; int curIndexOfIP; int curSocketIdx; int valueToAssign; if (protocol == PROTO_UDP) { valueToAssign = TC_TRANSPORT_PROTOCOL_UDP; } else if (protocol == PROTO_TCP) { valueToAssign = TC_TRANSPORT_PROTOCOL_TCP; } else if (protocol == PROTO_TLS) { valueToAssign = TC_TRANSPORT_PROTOCOL_TLS; } else { valueToAssign = TC_TRANSPORT_PROTOCOL_OTHER; } /* Create all rows with respect to the given protocol */ for (curSocketIdx=0; curSocketIdx < listSize; curSocketIdx++) { curIndexOfIP = (NUM_IP_OCTETS + 1) * curSocketIdx; /* Retrieve an existing row, or a new row if one doesn't * allready exist. */ currentRow = getRow(1, &theList[curIndexOfIP]); if (currentRow == NULL) { LM_ERR("failed to create all the " "rows for the openserSIPPortTable\n"); return; } currentRow->openserSIPTransportRcv[0] |= valueToAssign; currentRow->openserSIPTransportRcv_len = 1; } } /* * Initializes the openserSIPPortTable module. * * Specifically, this function will define the tables structure, and then * populate it with the ports and transports that OpenSIPS is listening on. * */ void init_openserSIPPortTable(void) { int curSNMPIndex = 0; initialize_table_openserSIPPortTable(); int *UDPList = NULL; int *TCPList = NULL; int *TLSList = NULL; int numUDPSockets; int numTCPSockets; int numTLSSockets; /* Retrieve the list of the number of UDP and TCP sockets. */ numUDPSockets = get_socket_list_from_proto(&UDPList, PROTO_UDP); numTCPSockets = get_socket_list_from_proto(&TCPList, PROTO_TCP); numTLSSockets = get_socket_list_from_proto(&TLSList, PROTO_TLS); /* Generate all rows, using all retrieved interfaces. */ createRowsFromIPList(UDPList, numUDPSockets, PROTO_UDP, &curSNMPIndex); curSNMPIndex = 0; createRowsFromIPList(TCPList, numTCPSockets, PROTO_TCP, &curSNMPIndex); curSNMPIndex = 0; createRowsFromIPList(TLSList, numTLSSockets, PROTO_TLS, &curSNMPIndex); } /* Initialize the openserSIPPortTable table by defining how it is structured */ void initialize_table_openserSIPPortTable(void) { netsnmp_table_registration_info *table_info; if(my_handler) { snmp_log(LOG_ERR, "initialize_table_openserSIPPortTable_handler" "called again\n"); return; } memset(&cb, 0x00, sizeof(cb)); /* create the table structure itself */ table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); my_handler = netsnmp_create_handler_registration("openserSIPPortTable", netsnmp_table_array_helper_handler, openserSIPPortTable_oid, openserSIPPortTable_oid_len, HANDLER_CAN_RONLY); if (!my_handler || !table_info) { snmp_log(LOG_ERR, "malloc failed in " "initialize_table_openserSIPPortTable_handler\n"); return; /** mallocs failed */ } /* Set up the table's structural definition */ /* index: openserSIPPortIndex */ netsnmp_table_helper_add_index(table_info, ASN_OCTET_STR); table_info->min_column = openserSIPPortTable_COL_MIN; table_info->max_column = openserSIPPortTable_COL_MAX; /* register the table with the master agent */ cb.get_value = openserSIPPortTable_get_value; cb.container = netsnmp_container_find("openserSIPPortTable_primary:" "openserSIPPortTable:" "table_container"); DEBUGMSGTL(("initialize_table_openserSIPPortTable", "Registering table openserSIPPortTable " "as a table array\n")); netsnmp_table_container_register(my_handler, table_info, &cb, cb.container, 1); } /* * This routine is called to process get requests for elements of the table. * * The function is mostly left in its auto-generated form */ int openserSIPPortTable_get_value(netsnmp_request_info *request, netsnmp_index *item, netsnmp_table_request_info *table_info ) { netsnmp_variable_list *var = request->requestvb; openserSIPPortTable_context *context = (openserSIPPortTable_context *)item; switch(table_info->colnum) { case COLUMN_OPENSERSIPTRANSPORTRCV: /** OpenSERSIPTransportProtocol = ASN_OCTET_STR */ snmp_set_var_typed_value(var, ASN_OCTET_STR, (unsigned char *) &context->openserSIPTransportRcv, context->openserSIPTransportRcv_len ); break; default: /** We shouldn't get here */ snmp_log(LOG_ERR, "unknown column in " "openserSIPPortTable_get_value\n"); return SNMP_ERR_GENERR; } return SNMP_ERR_NOERROR; } /* Auto-generated function */ const openserSIPPortTable_context * openserSIPPortTable_get_by_idx(netsnmp_index * hdr) { return (const openserSIPPortTable_context *) CONTAINER_FIND(cb.container, hdr ); } opensips-2.2.2/modules/snmpstats/openserSIPPortTable.h000066400000000000000000000062301300170765700231050ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Originally Generated with Mib2c using mib2c.array-user.conf. * * This file defines the openserSIPPortTable prototypes. For a full description * of the table, please see the OPENSER-SIP-COMMON-MIB. */ #ifndef OPENSERSIPPORTTABLE_H #define OPENSERSIPPORTTABLE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "../../config.h" #define SIP_PORT_TABLE_STR_INDEX_SIZE 10 /* This strucutre represents a single row in the table. */ typedef struct openserSIPPortTable_context_s { netsnmp_index index; unsigned char openserSIPStringIndex[SIP_PORT_TABLE_STR_INDEX_SIZE]; unsigned long openserSIPStringIndex_len; unsigned char openserSIPTransportRcv[2]; long openserSIPTransportRcv_len; void * data; } openserSIPPortTable_context; /* * Initializes the openserSIPPortTable module. * * Specifically, this function will define the tables structure, and then * populate it with the ports and transports that OpenSIPS is listening on. * */ void init_openserSIPPortTable(void); /* Initialize the openserSIPPortTable table by defining how it is structured */ void initialize_table_openserSIPPortTable(void); /* * This routine is called to process get requests for elements of the table. * The function is mostly left in its auto-generated form */ int openserSIPPortTable_get_value(netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *); const openserSIPPortTable_context * openserSIPPortTable_get_by_idx( netsnmp_index *); const openserSIPPortTable_context * openserSIPPortTable_get_by_idx_rs( netsnmp_index *, int row_status); /* * oid declarations */ extern oid openserSIPPortTable_oid[]; extern size_t openserSIPPortTable_oid_len; #define openserSIPPortTable_TABLE_OID OPENSER_OID,3,1,1,1,1,5 /* * column number definitions for table openserSIPPortTable */ #define COLUMN_OPENSERSIPTRANSPORTRCV 4 #define openserSIPPortTable_COL_MIN 4 #define openserSIPPortTable_COL_MAX 4 #ifdef __cplusplus } #endif #endif /** OPENSERSIPPORTTABLE_H */ opensips-2.2.2/modules/snmpstats/openserSIPRegUserLookupTable.c000066400000000000000000000560331300170765700247300ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Note: this file originally auto-generated by mib2c using * mib2c.array-user.conf * * This file implements the openserSIPRegUserLookupTable. For a full * description of the table, please see the OPENSER-SIP-SERVER-MIB. * * This file is much larger and more complicated than the files for other * tables. This is because the table is settable, bringing a lot of SNMP * overhead. Most of the file consists of the original auto-generated * code (aside from white space and comment changes). * * The functions that have been modified to implement this table are the * following: * * 1) openserSIPRegUserLookupTable_extract_index() * * - Modified to fail if the index is invalid. The index is invalid if it * does not match up with the global userLookupCounter. * * 2) openserSIPRegUserLookupTable_can_[activate|deactivate|delete]() * * - Simplified to always allow activation/deactivation/deletion. * * 3) openserSIPRegUserLookupTable_set_reserve1() * * - The reserve1 phase passes if the row is new, and the rowStatus column * is being set to 'createAndGo' * - The reserve1 phase passes if the row is not new, and the rowStatus * column is being set to 'destroy' * * 4) openserSIPRegUserLookupTable_set_action() * * - The function was modified to populate the row with the userIndex of the * supplied URI if that URI was found on the system, and set the rowStatus * to 'active'. If the URI was not found, the rowStatus is set to * 'notInService' * * You can safely ignore the other functions. */ #include #include #include #include #include "openserSIPRegUserLookupTable.h" #include "snmpstats_globals.h" #include "hashTable.h" #include "interprocess_buffer.h" static netsnmp_handler_registration *my_handler = NULL; static netsnmp_table_array_callbacks cb; oid openserSIPRegUserLookupTable_oid[] = { openserSIPRegUserLookupTable_TABLE_OID }; size_t openserSIPRegUserLookupTable_oid_len = OID_LENGTH(openserSIPRegUserLookupTable_oid); /* * Initializes the openserSIPRegUserLookupTable table. This step is easier * than in the other tables because there is no table population. All table * population takes place during run time. */ void init_openserSIPRegUserLookupTable(void) { initialize_table_openserSIPRegUserLookupTable(); } /* the *_row_copy routine */ static int openserSIPRegUserLookupTable_row_copy( openserSIPRegUserLookupTable_context * dst, openserSIPRegUserLookupTable_context * src) { if(!dst||!src) { return 1; } /* copy index, if provided */ if(dst->index.oids) { free(dst->index.oids); } if(snmp_clone_mem( (void*)&dst->index.oids, src->index.oids, src->index.len * sizeof(oid) )) { dst->index.oids = NULL; return 1; } dst->index.len = src->index.len; /* Copy out almost all components of the structure. We don't copy out * the openserSIPRegUSerLookupURI (or its length). */ dst->openserSIPRegUserLookupIndex = src->openserSIPRegUserLookupIndex; dst->openserSIPRegUserIndex = src->openserSIPRegUserIndex; dst->openserSIPRegUserLookupRowStatus = src->openserSIPRegUserLookupRowStatus; return 0; } /* * the *_extract_index routine. (Mostly auto-generated) * * This routine is called when a set request is received for an index * that was not found in the table container. Here, we parse the oid * in the individual index components and copy those indexes to the * context. Then we make sure the indexes for the new row are valid. * * It has been modified from its original form in that if the indexes are * invalid, then they aren't returned. An index is invalid if: * * 1) It is < 1 * 2) It doesn't match the global userLookupIndex. (As per MIB specs) * */ int openserSIPRegUserLookupTable_extract_index( openserSIPRegUserLookupTable_context * ctx, netsnmp_index * hdr) { /* * temporary local storage for extracting oid index * * extract index uses varbinds (netsnmp_variable_list) to parse * the index OID into the individual components for each index part. */ netsnmp_variable_list var_openserSIPRegUserLookupIndex; int err; /* copy index, if provided */ if(hdr) { netsnmp_assert(ctx->index.oids == NULL); if((hdr->len > MAX_OID_LEN) || snmp_clone_mem( (void*)&ctx->index.oids, hdr->oids, hdr->len * sizeof(oid) )) { return -1; } ctx->index.len = hdr->len; } /* Set up the index */ memset(&var_openserSIPRegUserLookupIndex, 0x00, sizeof(var_openserSIPRegUserLookupIndex)); var_openserSIPRegUserLookupIndex.type = ASN_UNSIGNED; var_openserSIPRegUserLookupIndex.next_variable = NULL; /* parse the oid into the individual index components */ err = parse_oid_indexes( hdr->oids, hdr->len, &var_openserSIPRegUserLookupIndex ); if (err == SNMP_ERR_NOERROR) { /* copy index components into the context structure */ ctx->openserSIPRegUserLookupIndex = *var_openserSIPRegUserLookupIndex.val.integer; /* * Check to make sure that the index corresponds to the * global_userLookupCounter, as per the MIB specifications. */ if (*var_openserSIPRegUserLookupIndex.val.integer != global_UserLookupCounter || *var_openserSIPRegUserLookupIndex.val.integer < 1) { err = -1; } } /* parsing may have allocated memory. free it. */ snmp_reset_var_buffers( &var_openserSIPRegUserLookupIndex ); return err; } /* * This is an auto-generated function. In general the *_can_activate routine * is called when a row is changed to determine if all the values set are * consistent with the row's rules for a row status of ACTIVE. If not, then 0 * can be returned to prevent the row status from becomming final. * * For our purposes, we have no need for this check, so we always return 1. */ int openserSIPRegUserLookupTable_can_activate( openserSIPRegUserLookupTable_context *undo_ctx, openserSIPRegUserLookupTable_context *row_ctx, netsnmp_request_group * rg) { return 1; } /* * This is an auto-generated function. In general the *_can_deactivate routine * is called when a row that is currently ACTIVE is set to a state other than * ACTIVE. If there are conditions in which a row should not be allowed to * transition out of the ACTIVE state (such as the row being referred to by * another row or table), check for them here. * * Since this table has no reason why this shouldn't be allowed, we always * return 1; */ int openserSIPRegUserLookupTable_can_deactivate( openserSIPRegUserLookupTable_context *undo_ctx, openserSIPRegUserLookupTable_context *row_ctx, netsnmp_request_group * rg) { return 1; } /* * This is an auto-generated function. In general the *_can_delete routine is * called to determine if a row can be deleted. This usually involved checking * if it can be deactivated, and if it can be, then checking for other * conditions. * * Since this table ha no reason why row deletion shouldn't be allowed, we * always return 1, unless we can't deactivate. */ int openserSIPRegUserLookupTable_can_delete( openserSIPRegUserLookupTable_context *undo_ctx, openserSIPRegUserLookupTable_context *row_ctx, netsnmp_request_group * rg) { if(openserSIPRegUserLookupTable_can_deactivate(undo_ctx,row_ctx,rg) != 1) return 0; return 1; } /* * This is an auto-generated function. * * The *_create_row routine is called by the table handler to create a new row * for a given index. This is the first stage of the row creation process. The * *_set_reserve_* functions can be used to prevent the row from being inserted * into the table even if the row passes any preliminary checks set here. * * Returns a newly allocated openserSIPRegUserLookupTable_context structure (a * row in the table) if the indexes are legal. NULL will be returned otherwise. */ openserSIPRegUserLookupTable_context * openserSIPRegUserLookupTable_create_row( netsnmp_index* hdr) { openserSIPRegUserLookupTable_context * ctx = SNMP_MALLOC_TYPEDEF(openserSIPRegUserLookupTable_context); if (!ctx) { return NULL; } /* * Extract the index. The function has been modified from its original * auto-generated version in that the function will fail if index is * somehow invalid. */ if(openserSIPRegUserLookupTable_extract_index( ctx, hdr )) { if (NULL != ctx->index.oids) { free(ctx->index.oids); } free(ctx); return NULL; } ctx->openserSIPRegUserLookupURI = NULL; ctx->openserSIPRegUserLookupURI_len = 0; ctx->openserSIPRegUserIndex = 0; ctx->openserSIPRegUserLookupRowStatus = 0; return ctx; } /* * Auto-generated function. The *_duplicate row routine */ openserSIPRegUserLookupTable_context * openserSIPRegUserLookupTable_duplicate_row( openserSIPRegUserLookupTable_context * row_ctx) { openserSIPRegUserLookupTable_context * dup; if(!row_ctx) return NULL; dup = SNMP_MALLOC_TYPEDEF(openserSIPRegUserLookupTable_context); if(!dup) return NULL; if(openserSIPRegUserLookupTable_row_copy(dup,row_ctx)) { free(dup); dup = NULL; } return dup; } /* * The *_delete_row method is auto-generated, and is called to delete a row. * * This will not be called if earlier checks said that this row can't be * deleted. However, in our implementation there is never a reason why this * function can't be called. */ netsnmp_index * openserSIPRegUserLookupTable_delete_row( openserSIPRegUserLookupTable_context * ctx ) { if(ctx->index.oids) { free(ctx->index.oids); } if (ctx->openserSIPRegUserLookupURI != NULL) { pkg_free(ctx->openserSIPRegUserLookupURI); } free( ctx ); return NULL; } /* * Large parts of this function have been auto-generated. The functions purpose * is to check to make sure all SNMP set values for the given row, have been * valid. If not, then the process is supposed to be aborted. Otherwise, we * pass on to the *_reserve2 function. * * For our purposes, our only check is to make sure that either of the following * conditions are true: * * 1) If this row already exists, then the SET request is setting the rowStatus * column to 'destroy'. * * 2) If this row does not already exist, then the SET request is setting the * rowStatus to 'createAndGo'. * * Since the MIB specified there are to be no other modifications to the row, * any other condition is considered illegal, and will result in an SNMP error * being returned. */ void openserSIPRegUserLookupTable_set_reserve1( netsnmp_request_group *rg ) { openserSIPRegUserLookupTable_context *row_ctx = (openserSIPRegUserLookupTable_context *)rg->existing_row; netsnmp_variable_list *var; netsnmp_request_group_item *current; int rc; for( current = rg->list; current; current = current->next ) { var = current->ri->requestvb; rc = SNMP_ERR_NOERROR; switch(current->tri->colnum) { case COLUMN_OPENSERSIPREGUSERLOOKUPURI: if (row_ctx->openserSIPRegUserLookupRowStatus == 0 || row_ctx->openserSIPRegUserLookupRowStatus == TC_ROWSTATUS_NOTREADY) { } else { rc = SNMP_ERR_BADVALUE; } break; case COLUMN_OPENSERSIPREGUSERLOOKUPROWSTATUS: /** RowStatus = ASN_INTEGER */ rc = netsnmp_check_vb_type_and_size(var, ASN_INTEGER, sizeof( row_ctx->openserSIPRegUserLookupRowStatus)); /* Want to make sure that if it already exists that it * is setting it to 'destroy', or if it doesn't exist, * that it is setting it to 'createAndGo' */ if (row_ctx->openserSIPRegUserLookupRowStatus == 0 && *var->val.integer != TC_ROWSTATUS_CREATEANDGO) { rc = SNMP_ERR_BADVALUE; } else if (row_ctx->openserSIPRegUserLookupRowStatus == TC_ROWSTATUS_ACTIVE && *var->val.integer != TC_ROWSTATUS_DESTROY) { rc = SNMP_ERR_BADVALUE; } break; default: /** We shouldn't get here */ rc = SNMP_ERR_GENERR; snmp_log(LOG_ERR, "unknown column in openserSIPReg" "UserLookupTable_set_reserve1\n"); } if (rc) { netsnmp_set_mode_request_error( MODE_SET_BEGIN, current->ri, rc ); } rg->status = SNMP_MAX( rg->status, current->ri->status ); } } /* * Auto-generated function. The function is supposed to check for any * last-minute conditions not being met. However, we don't have any such * conditions, so we leave the default function as is. */ void openserSIPRegUserLookupTable_set_reserve2( netsnmp_request_group *rg ) { openserSIPRegUserLookupTable_context *undo_ctx = (openserSIPRegUserLookupTable_context *)rg->undo_info; netsnmp_request_group_item *current; int rc; rg->rg_void = rg->list->ri; for( current = rg->list; current; current = current->next ) { rc = SNMP_ERR_NOERROR; switch(current->tri->colnum) { case COLUMN_OPENSERSIPREGUSERLOOKUPURI: break; case COLUMN_OPENSERSIPREGUSERLOOKUPROWSTATUS: /** RowStatus = ASN_INTEGER */ rc = netsnmp_check_vb_rowstatus(current->ri->requestvb, undo_ctx ? undo_ctx->openserSIPRegUserLookupRowStatus:0); rg->rg_void = current->ri; break; default: /** We shouldn't get here */ netsnmp_assert(0); /** why wasn't this caught in reserve1? */ } if (rc) { netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc); } } } /* * This function is called only when all the *_reserve[1|2] functions were * succeful. Its purpose is to make any changes to the row before it is * inserted into the table. * * In the case of this table, this involves looking up the index of the * requested user in the URI to userIndex mapping hash table. If the result is * found, the index will be copied to the row, and the rowStatus set to * 'active'. Otherwise, the row status will be set to 'notInService' * * All other handling is auto-generated. */ void openserSIPRegUserLookupTable_set_action( netsnmp_request_group *rg ) { /* First things first, we need to consume the interprocess buffer, in * case something has changed. We want to return the freshest data. */ consumeInterprocessBuffer(); aorToIndexStruct_t *hashRecord; netsnmp_variable_list *var; openserSIPRegUserLookupTable_context *row_ctx = (openserSIPRegUserLookupTable_context *)rg->existing_row; openserSIPRegUserLookupTable_context *undo_ctx = (openserSIPRegUserLookupTable_context *)rg->undo_info; netsnmp_request_group_item *current; int row_err = 0; /* Copy the actual data to the row. */ for( current = rg->list; current; current = current->next ) { var = current->ri->requestvb; switch(current->tri->colnum) { case COLUMN_OPENSERSIPREGUSERLOOKUPURI: row_ctx->openserSIPRegUserLookupURI = pkg_malloc(sizeof(char)*(var->val_len + 1)); memcpy(row_ctx->openserSIPRegUserLookupURI, var->val.string, var->val_len); /* Usually NetSNMP won't terminate strings with '\0'. * The hash function expect them to be terminated * though, so we have to add this on to the end. The +1 * in the malloc makes sure of the extra space for us. */ row_ctx->openserSIPRegUserLookupURI[var->val_len] = '\0'; row_ctx->openserSIPRegUserLookupURI_len = var->val_len; /* Do the lookup. If we could find the record, then set * the index and the row status to active. Otherwise, * set the row to notInService */ hashRecord = findHashRecord(hashTable, (char *) row_ctx->openserSIPRegUserLookupURI, HASH_SIZE); if (hashRecord == NULL) { row_ctx->openserSIPRegUserIndex = 0; row_ctx->openserSIPRegUserLookupRowStatus = TC_ROWSTATUS_NOTINSERVICE; } else { row_ctx->openserSIPRegUserIndex = hashRecord->userIndex; row_ctx->openserSIPRegUserLookupRowStatus = TC_ROWSTATUS_ACTIVE; } break; case COLUMN_OPENSERSIPREGUSERLOOKUPROWSTATUS: row_ctx->openserSIPRegUserLookupRowStatus = *var->val.integer; if (*var->val.integer == TC_ROWSTATUS_CREATEANDGO) { rg->row_created = 1; /* Set to NOT READY until the lookup URI has * been supplied. */ row_ctx->openserSIPRegUserLookupRowStatus = TC_ROWSTATUS_NOTREADY; } else if (*var->val.integer == TC_ROWSTATUS_DESTROY) { rg->row_deleted = 1; } else { /* We should never be here, because the RESERVE * functions should have taken care of all other * values. */ LM_ERR("invalid RowStatus in openserSIPStatusCodesTable\n"); } break; default: /** We shouldn't get here */ netsnmp_assert(0); /** why wasn't this caught in reserve1? */ } } /* * done with all the columns. Could check row related * requirements here. */ #ifndef openserSIPRegUserLookupTable_CAN_MODIFY_ACTIVE_ROW if( undo_ctx && RS_IS_ACTIVE(undo_ctx->openserSIPRegUserLookupRowStatus) && row_ctx && RS_IS_ACTIVE(row_ctx->openserSIPRegUserLookupRowStatus) ) { row_err = 1; } #endif /* * check activation/deactivation */ row_err = netsnmp_table_array_check_row_status(&cb, rg, row_ctx ? &row_ctx->openserSIPRegUserLookupRowStatus : NULL, undo_ctx ? &undo_ctx->openserSIPRegUserLookupRowStatus : NULL); if(row_err) { netsnmp_set_mode_request_error(MODE_SET_BEGIN, (netsnmp_request_info*)rg->rg_void, row_err); return; } } /* * The COMMIT phase is used to do any extra processing after the ACTION phase. * In our table, there is nothing to do, so the function body is empty. */ void openserSIPRegUserLookupTable_set_commit( netsnmp_request_group *rg ) { } /* * This function is called if the *_reserve[1|2] calls failed. Its supposed to * free up any resources allocated earlier. However, we already take care of * all these resources in earlier functions. So for our purposes, the function * body is empty. */ void openserSIPRegUserLookupTable_set_free( netsnmp_request_group *rg ) { } /* * This function is called if an ACTION phase fails, to do extra clean-up work. * We don't have anything complicated enough to warrant putting anything in this * function. Therefore, its just left with an empty function body. */ void openserSIPRegUserLookupTable_set_undo( netsnmp_request_group *rg ) { } /* * Initialize the openserSIPRegUserLookupTable table by defining how it is * structured. * * This function is mostly auto-generated. */ void initialize_table_openserSIPRegUserLookupTable(void) { netsnmp_table_registration_info *table_info; if(my_handler) { snmp_log(LOG_ERR, "initialize_table_openserSIPRegUserLookup" "Table_handler called again\n"); return; } memset(&cb, 0x00, sizeof(cb)); /** create the table structure itself */ table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); my_handler = netsnmp_create_handler_registration( "openserSIPRegUserLookupTable", netsnmp_table_array_helper_handler, openserSIPRegUserLookupTable_oid, openserSIPRegUserLookupTable_oid_len, HANDLER_CAN_RWRITE); if (!my_handler || !table_info) { snmp_log(LOG_ERR, "malloc failed in " "initialize_table_openserSIPRegUserLookup" "Table_handler\n"); return; /** mallocs failed */ } /* * Setting up the table's definition */ netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED); table_info->min_column = openserSIPRegUserLookupTable_COL_MIN; table_info->max_column = openserSIPRegUserLookupTable_COL_MAX; /* * registering the table with the master agent */ cb.get_value = openserSIPRegUserLookupTable_get_value; cb.container = netsnmp_container_find( "openserSIPRegUserLookupTable_primary:" "openserSIPRegUserLookupTable:" "table_container"); cb.can_set = 1; cb.create_row = (UserRowMethod*)openserSIPRegUserLookupTable_create_row; cb.duplicate_row = (UserRowMethod*)openserSIPRegUserLookupTable_duplicate_row; cb.delete_row = (UserRowMethod*)openserSIPRegUserLookupTable_delete_row; cb.row_copy = (Netsnmp_User_Row_Operation *) openserSIPRegUserLookupTable_row_copy; cb.can_activate = (Netsnmp_User_Row_Action *) openserSIPRegUserLookupTable_can_activate; cb.can_deactivate = (Netsnmp_User_Row_Action *) openserSIPRegUserLookupTable_can_deactivate; cb.can_delete = (Netsnmp_User_Row_Action *) openserSIPRegUserLookupTable_can_delete; cb.set_reserve1 = openserSIPRegUserLookupTable_set_reserve1; cb.set_reserve2 = openserSIPRegUserLookupTable_set_reserve2; cb.set_action = openserSIPRegUserLookupTable_set_action; cb.set_commit = openserSIPRegUserLookupTable_set_commit; cb.set_free = openserSIPRegUserLookupTable_set_free; cb.set_undo = openserSIPRegUserLookupTable_set_undo; DEBUGMSGTL(("initialize_table_openserSIPRegUserLookupTable", "Registering table openserSIPRegUserLookupTable " "as a table array\n")); netsnmp_table_container_register(my_handler, table_info, &cb, cb.container, 1); } /* * This function was auto-generated and didn't need modifications from its * auto-generation. It is called to handle an SNMP GET request. */ int openserSIPRegUserLookupTable_get_value( netsnmp_request_info *request, netsnmp_index *item, netsnmp_table_request_info *table_info ) { netsnmp_variable_list *var = request->requestvb; openserSIPRegUserLookupTable_context *context = (openserSIPRegUserLookupTable_context *)item; switch(table_info->colnum) { case COLUMN_OPENSERSIPREGUSERLOOKUPURI: /** SnmpAdminString = ASN_OCTET_STR */ snmp_set_var_typed_value(var, ASN_OCTET_STR, (unsigned char*) context->openserSIPRegUserLookupURI, context->openserSIPRegUserLookupURI_len); break; case COLUMN_OPENSERSIPREGUSERINDEX: /** UNSIGNED32 = ASN_UNSIGNED */ snmp_set_var_typed_value(var, ASN_UNSIGNED, (unsigned char*) &context->openserSIPRegUserIndex, sizeof(context->openserSIPRegUserIndex)); break; case COLUMN_OPENSERSIPREGUSERLOOKUPROWSTATUS: /** RowStatus = ASN_INTEGER */ snmp_set_var_typed_value(var, ASN_INTEGER, (unsigned char*) &context->openserSIPRegUserLookupRowStatus, sizeof( context->openserSIPRegUserLookupRowStatus)); break; default: /** We shouldn't get here */ snmp_log(LOG_ERR, "unknown column in " "openserSIPRegUserLookupTable_get_value\n"); return SNMP_ERR_GENERR; } return SNMP_ERR_NOERROR; } /* * openserSIPRegUserLookupTable_get_by_idx */ const openserSIPRegUserLookupTable_context * openserSIPRegUserLookupTable_get_by_idx(netsnmp_index * hdr) { return (const openserSIPRegUserLookupTable_context *) CONTAINER_FIND(cb.container, hdr ); } opensips-2.2.2/modules/snmpstats/openserSIPRegUserLookupTable.h000066400000000000000000000144711300170765700247350ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Note: this file originally auto-generated by mib2c using * mib2c.array-user.conf, * * This file implements the openserSIPRegUserLookupTable. For a full * description of the table, please see the OPENSER-SIP-SERVER-MIB. * * This file consists of many more functions than the other header files. * This is because this table is writable, bringing a lot of SNMP overhead. * * Most of the contents are auto-generated (aside from white space and comment * changes), and can be ignored. The functions that have been modified are: * * 1) openserSIPRegUserLookupTable_extract_index() * * 2) openserSIPRegUserLookupTable_can_[activate|deactivate|delete]() * * 3) openserSIPRegUserLookupTable_set_reserve1() * * 4) openserSIPRegUserLookupTable_set_action() * * Full details can be found in openserSIPRegUserLookupTable.c. You can safely * ignore the other functions. */ #ifndef OPENSERSIPREGUSERLOOKUPTABLE_H #define OPENSERSIPREGUSERLOOKUPTABLE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "../../config.h" /* This structure represnts a row in the table */ typedef struct openserSIPRegUserLookupTable_context_s { netsnmp_index index; /** UNSIGNED32 = ASN_UNSIGNED */ unsigned long openserSIPRegUserLookupIndex; /** SnmpAdminString = ASN_OCTET_STR */ unsigned char *openserSIPRegUserLookupURI; long openserSIPRegUserLookupURI_len; /** UNSIGNED32 = ASN_UNSIGNED */ unsigned long openserSIPRegUserIndex; /** RowStatus = ASN_INTEGER */ long openserSIPRegUserLookupRowStatus; void * data; } openserSIPRegUserLookupTable_context; /* * Initializes the openserSIPRegUserLookupTable table. This step is easier * than in the other tables because there is no table population. All table * population takes place during run time. */ void init_openserSIPRegUserLookupTable(void); /* * Initialize the openserSIPRegUserLookupTable table by defining how it is * structured. * * This function is mostly auto-generated. */ void initialize_table_openserSIPRegUserLookupTable(void); /* * This function was auto-generated and didn't need modifications from its * auto-generation. It is called to handle an SNMP GET request. */ int openserSIPRegUserLookupTable_get_value(netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *); const openserSIPRegUserLookupTable_context * openserSIPRegUserLookupTable_get_by_idx(netsnmp_index *); const openserSIPRegUserLookupTable_context * openserSIPRegUserLookupTable_get_by_idx_rs( netsnmp_index *, int row_status); /* oid declarations */ extern oid openserSIPRegUserLookupTable_oid[]; extern size_t openserSIPRegUserLookupTable_oid_len; #define openserSIPRegUserLookupTable_TABLE_OID OPENSER_OID,3,1,2,1,5,9 /* column number definitions for table openserSIPRegUserLookupTable */ #define COLUMN_OPENSERSIPREGUSERLOOKUPINDEX 1 #define COLUMN_OPENSERSIPREGUSERLOOKUPURI 2 #define COLUMN_OPENSERSIPREGUSERINDEX 3 #define COLUMN_OPENSERSIPREGUSERLOOKUPROWSTATUS 4 #define openserSIPRegUserLookupTable_COL_MIN 2 #define openserSIPRegUserLookupTable_COL_MAX 4 /* Handles index extraction for row creation */ int openserSIPRegUserLookupTable_extract_index( openserSIPRegUserLookupTable_context *ctx, netsnmp_index *hdr); /* Handle RESERVE1 and RESERVE2 phases of an SNMP SET */ void openserSIPRegUserLookupTable_set_reserve1(netsnmp_request_group *); void openserSIPRegUserLookupTable_set_reserve2(netsnmp_request_group *); /* Handle the SET and ACTION phases of an SNMP SET */ void openserSIPRegUserLookupTable_set_action(netsnmp_request_group *); void openserSIPRegUserLookupTable_set_commit(netsnmp_request_group *); /* Handle Resource cleanup if the ACTION or RESERVE1/RESERVE2 phases of an * SNMPSET fail */ void openserSIPRegUserLookupTable_set_free(netsnmp_request_group *); void openserSIPRegUserLookupTable_set_undo(netsnmp_request_group *); openserSIPRegUserLookupTable_context * openserSIPRegUserLookupTable_duplicate_row( openserSIPRegUserLookupTable_context*); netsnmp_index * openserSIPRegUserLookupTable_delete_row( openserSIPRegUserLookupTable_context*); /* Used to check if there is a reason why a row can't be activated * (There is no reason in our implementation) */ int openserSIPRegUserLookupTable_can_activate( openserSIPRegUserLookupTable_context *undo_ctx, openserSIPRegUserLookupTable_context *row_ctx, netsnmp_request_group * rg); /* Used to check if there is a reason why a row can't be deactivated * (There is no reason in our implementation) */ int openserSIPRegUserLookupTable_can_deactivate( openserSIPRegUserLookupTable_context *undo_ctx, openserSIPRegUserLookupTable_context *row_ctx, netsnmp_request_group * rg); /* Used to check if there is a reason why a row can't be deleted * (There is no reason in our implementation) */ int openserSIPRegUserLookupTable_can_delete( openserSIPRegUserLookupTable_context *undo_ctx, openserSIPRegUserLookupTable_context *row_ctx, netsnmp_request_group * rg); /* Basic structural setups of the new row */ openserSIPRegUserLookupTable_context * openserSIPRegUserLookupTable_create_row( netsnmp_index*); #ifdef __cplusplus } #endif #endif /** OPENSERSIPREGUSERLOOKUPTABLE_H */ opensips-2.2.2/modules/snmpstats/openserSIPRegUserTable.c000066400000000000000000000264351300170765700235410ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Note: this file originally auto-generated by mib2c using * mib2c.array-user.conf * * This file implements the openserSIPRegUserTable. For a full description of * the table, please see the OPENSER-SIP-SERVER-MIB. * * Understanding this code will be much simpler with the following information: * * 1) All rows are indexed by an integer user index. This is different from the * usrloc module, which indexes by strings. This less natural indexing * scheme was required due to SNMP String index limitations. (for example, * SNMP has maximum index lengths.) * * 2) We need a quick way of mapping usrloc indices to our integer indices. For * this reason a string indexed Hash Table was created, with each entry mapping * to an integer user index. * * This hash table is used by the openserSIPContactTable (the hash table also * maps a user to its contacts), as well as the openserSIPRegUserLookupTable. * The hash table is also used for quick lookups when a user expires. (i.e, it * gives us a more direct reference, instead of having to search the whole * table). * * 3) We are informed about new/expired users via a callback mechanism from the * usrloc module. Because of NetSNMP inefficiencies, we had to abstract this * process. Specifically: * * - It can take a long time for the NetSNMP code base to populate a table with * a large number of records. * * - We rely on callbacks for updated user information. * * Clearly, using the SNMPStats module in this situation could lead to some * big performance loses if we don't find another way to deal with this. The * solution was to use an interprocess communications buffer. * * Instead of adding the record directly to the table, the callback functions * now adds either an add/delete command to the interprocessBuffer. When an * snmp request is recieved by the SNMPStats sub-process, it will consume * this interprocess buffer, adding and deleting users. When it is finished, * it can service the SNMP request. * * This doesn't remove the NetSNMP inefficiency, but instead moves it to a * non-critical path. Such an approach allows SNMP support with almost no * overhead to the rest of OpenSIPS. */ #include #include #include #include #include "hashTable.h" #include "interprocess_buffer.h" #include "utilities.h" #include "openserSIPRegUserTable.h" #include "snmpstats_globals.h" #include "../../sr_module.h" #include "../../locking.h" #include "../usrloc/usrloc.h" static netsnmp_handler_registration *my_handler = NULL; static netsnmp_table_array_callbacks cb; oid openserSIPRegUserTable_oid[] = { openserSIPRegUserTable_TABLE_OID }; size_t openserSIPRegUserTable_oid_len = OID_LENGTH(openserSIPRegUserTable_oid); /* If the usrloc module is loaded, this function will grab hooks into its * callback registration function, and add handleContactCallbacks() as the * callback for UL_CONTACT_INSERT and UL_CONTACT_EXPIRE. * * Returns 1 on success, and zero otherwise. */ int registerForUSRLOCCallbacks(void) { bind_usrloc_t bind_usrloc; usrloc_api_t ul; bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (!bind_usrloc) { LM_INFO("Can't find ul_bind_usrloc\n"); goto error; } if (bind_usrloc(&ul) < 0 || ul.register_ulcb == NULL) { LM_INFO("Can't bind usrloc\n"); goto error; } ul.register_ulcb(UL_CONTACT_INSERT, handleContactCallbacks, NULL); ul.register_ulcb(UL_CONTACT_EXPIRE, handleContactCallbacks, NULL); ul.register_ulcb(UL_CONTACT_DELETE, handleContactCallbacks, NULL); return 1; error: LM_INFO("failed to register for callbacks with the USRLOC module.\n"); LM_INFO("openserSIPContactTable and openserSIPUserTable will be" " unavailable\n"); return 0; } /* Removes an SNMP row indexed by userIndex, and frees the string and index it * pointed to. */ void deleteRegUserRow(int userIndex) { openserSIPRegUserTable_context *theRow; netsnmp_index indexToRemove; oid indexToRemoveOID; indexToRemoveOID = userIndex; indexToRemove.oids = &indexToRemoveOID; indexToRemove.len = 1; theRow = CONTAINER_FIND(cb.container, &indexToRemove); /* The userURI is shared memory, the index.oids was allocated from * pkg_malloc(), and theRow was made with the NetSNMP API which uses * malloc() */ if (theRow != NULL) { CONTAINER_REMOVE(cb.container, &indexToRemove); pkg_free(theRow->openserSIPUserUri); pkg_free(theRow->index.oids); free(theRow); } } /* * Adds or updates a user: * * - If a user with the name userName exists, its 'number of contacts' count * will be incremented. * - If the user doesn't exist, the user will be added to the table, and its * number of contacts' count set to 1. */ void updateUser(char *userName) { int userIndex; aorToIndexStruct_t *newRecord; aorToIndexStruct_t *existingRecord = findHashRecord(hashTable, userName, HASH_SIZE); /* We found an existing record, so we need to update its 'number of * contacts' count. */ if (existingRecord != NULL) { existingRecord->numContacts++; return; } /* Make a new row, and insert a record of it into our mapping data * structures */ userIndex = createRegUserRow(userName); if (userIndex == 0) { LM_ERR("openserSIPRegUserTable ran out of memory." " Not able to add user: %s", userName); return; } newRecord = createHashRecord(userIndex, userName); /* If we couldn't create a record in the hash table, then we won't be * able to access this row properly later. So remove the row from the * table and fail. */ if (newRecord == NULL) { deleteRegUserRow(userIndex); LM_ERR("openserSIPRegUserTable was not able to push %s into the hash." " User not added to this table\n", userName); return; } /* Insert the new record of the mapping data structure into the hash * table */ /*insertHashRecord(hashTable, createHashRecord(userIndex, userName), HASH_SIZE);*/ insertHashRecord(hashTable, newRecord, HASH_SIZE); } /* Creates a row and inserts it. * * Returns: The rows userIndex on success, and 0 otherwise. */ int createRegUserRow(char *stringToRegister) { int static index = 0; index++; openserSIPRegUserTable_context *theRow; oid *OIDIndex; int stringLength; theRow = SNMP_MALLOC_TYPEDEF(openserSIPRegUserTable_context); if (theRow == NULL) { LM_ERR("failed to create a row for openserSIPRegUserTable\n"); return 0; } OIDIndex = pkg_malloc(sizeof(oid)); if (OIDIndex == NULL) { free(theRow); LM_ERR("failed to create a row for openserSIPRegUserTable\n"); return 0; } stringLength = strlen(stringToRegister); OIDIndex[0] = index; theRow->index.len = 1; theRow->index.oids = OIDIndex; theRow->openserSIPUserIndex = index; theRow->openserSIPUserUri = (unsigned char*)pkg_malloc(stringLength* sizeof(char)); if(theRow->openserSIPUserUri== NULL) { pkg_free(OIDIndex); free(theRow); LM_ERR("failed to create a row for openserSIPRegUserTable\n"); return 0; } memcpy(theRow->openserSIPUserUri, stringToRegister, stringLength); theRow->openserSIPUserUri_len = stringLength; theRow->openserSIPUserAuthenticationFailures = 0; CONTAINER_INSERT(cb.container, theRow); return index; } /* Initializes the openserSIPRegUserTable module. */ void init_openserSIPRegUserTable(void) { /* Register this table with the master agent */ initialize_table_openserSIPRegUserTable(); /* We need to create a default row, so create DefaultUser */ static char *defaultUser = "DefaultUser"; createRegUserRow(defaultUser); } /* * Initialize the openserSIPRegUserTable table by defining its contents and how * it's structured */ void initialize_table_openserSIPRegUserTable(void) { netsnmp_table_registration_info *table_info; if(my_handler) { snmp_log(LOG_ERR, "initialize_table_openserSIPRegUserTable_hand" "ler called again\n"); return; } memset(&cb, 0x00, sizeof(cb)); /* create the table structure itself */ table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); my_handler = netsnmp_create_handler_registration( "openserSIPRegUserTable", netsnmp_table_array_helper_handler, openserSIPRegUserTable_oid, openserSIPRegUserTable_oid_len, HANDLER_CAN_RONLY); if (!my_handler || !table_info) { snmp_log(LOG_ERR, "malloc failed in initialize_table_openser" "SIPRegUserTable_handler\n"); return; /** mallocs failed */ } netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED); table_info->min_column = openserSIPRegUserTable_COL_MIN; table_info->max_column = openserSIPRegUserTable_COL_MAX; cb.get_value = openserSIPRegUserTable_get_value; cb.container = netsnmp_container_find("openserSIPRegUserTable_primary:" "openserSIPRegUserTable:" "table_container"); DEBUGMSGTL(("initialize_table_openserSIPRegUserTable", "Registering table openserSIPRegUserTable " "as a table array\n")); netsnmp_table_container_register(my_handler, table_info, &cb, cb.container, 1); } /* Handles SNMP GET requests. */ int openserSIPRegUserTable_get_value( netsnmp_request_info *request, netsnmp_index *item, netsnmp_table_request_info *table_info ) { /* First things first, we need to consume the interprocess buffer, in * case something has changed. We want to return the freshest data. */ consumeInterprocessBuffer(); netsnmp_variable_list *var = request->requestvb; openserSIPRegUserTable_context *context = (openserSIPRegUserTable_context *)item; switch(table_info->colnum) { case COLUMN_OPENSERSIPUSERURI: /** SnmpAdminString = ASN_OCTET_STR */ snmp_set_var_typed_value(var, ASN_OCTET_STR, (unsigned char*)context->openserSIPUserUri, context->openserSIPUserUri_len ); break; case COLUMN_OPENSERSIPUSERAUTHENTICATIONFAILURES: /** COUNTER = ASN_COUNTER */ snmp_set_var_typed_value(var, ASN_COUNTER, (unsigned char*) &context->openserSIPUserAuthenticationFailures, sizeof( context->openserSIPUserAuthenticationFailures)); break; default: /** We shouldn't get here */ snmp_log(LOG_ERR, "unknown column in " "openserSIPRegUserTable_get_value\n"); return SNMP_ERR_GENERR; } return SNMP_ERR_NOERROR; } const openserSIPRegUserTable_context * openserSIPRegUserTable_get_by_idx(netsnmp_index * hdr) { return (const openserSIPRegUserTable_context *) CONTAINER_FIND(cb.container, hdr ); } opensips-2.2.2/modules/snmpstats/openserSIPRegUserTable.h000066400000000000000000000146111300170765700235370ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Note: this file originally auto-generated by mib2c using * mib2c.array-user.conf * * This file defines the prototypes that implement the openserSIPRegUserTable. * For a full description of the table, please see the OPENSER-SIP-SERVER-MIB. * * Understanding this code will be much simpler with the following information: * * 1) All rows are indexed by an integer user index. This is different from the * usrloc module, which indexes by strings. This less natural indexing * scheme was required due to SNMP String index limitations. (for example, * SNMP has maximum index lengths.) * * 2) We need a quick way of mapping usrloc indices to our integer indices. For * this reason a string indexed Hash Table was created, with each entry mapping * to an integer user index. * * This hash table is used by the openserSIPContactTable (the hash table also * maps a user to its contacts), as well as the openserSIPRegUserLookupTable. * The hash table is also used for quick lookups when a user expires. (i.e, it * gives us a more direct reference, instead of having to search the whole * table). * * 3) We are informed about new/expired users via a callback mechanism from the * usrloc module. Because of NetSNMP inefficiencies, we had to abstract this * process. Specifically: * * - It can take a long time for the NetSNMP code base to populate a table with * a large number of records. * * - We rely on callbacks for updated user information. * * Clearly, using the SNMPStats module in this situation could lead to some * big performance loses if we don't find another way to deal with this. The * solution was to use an interprocess communications buffer. * * Instead of adding the record directly to the table, the callback functions * now adds either an add/delete command to the interprocessBuffer. When an * snmp request is recieved by the SNMPStats sub-process, it will consume * this interprocess buffer, adding and deleting users. When it is finished, * it can service the SNMP request. * * This doesn't remove the NetSNMP inefficiency, but instead moves it to a * non-critical path. Such an approach allows SNMP support with almost no * overhead to the rest of OpenSIPS. */ #ifndef OPENSERSIPREGUSERTABLE_H #define OPENSERSIPREGUSERTABLE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "../../config.h" /* Defines what each SNMP Row is made of. */ typedef struct openserSIPRegUserTable_context_s { netsnmp_index index; unsigned long openserSIPUserIndex; /* There are potentially a lot of these of varying sizes, so lets * allocate only the amount of memory we need when the row is * created. */ unsigned char *openserSIPUserUri; long openserSIPUserUri_len; unsigned long openserSIPUserAuthenticationFailures; void * data; } openserSIPRegUserTable_context; /*******************************/ /* Customized Prototypes */ /*******************************/ /* If the usrloc module is loaded, this function will grab hooks into its * callback registration function, and add handleContactCallbacks() as the * callback for UL_CONTACT_INSERT and UL_CONTACT_EXPIRE. * * Returns 1 on success, and zero otherwise */ int registerForUSRLOCCallbacks(); /* * Creates a row and inserts it. * * Returns: The rows userIndex on success, and 0 otherwise. */ int createRegUserRow(char *stringToRegister); /* Removes an SNMP row indexed by userIndex, and frees the string and index it * pointed to. */ void deleteRegUserRow(int userIndex); /* Creates an 'aor to userindex' record from stringName and userIndex, and pushes * them onto the hash table. */ void pushUserIntoHashTable(int userIndex, char *stringName); /* * Adds or updates a user: * * - If a user with the name userName exists, its 'number of contacts' count * will be incremented. * - If the user doesn't exist, the user will be added to the table, and its * number of contacts' count set to 1. */ void updateUser(char *userName); /*******************************/ /* Normal Function Prototypes */ /*******************************/ /* Initializes the openserSIPRegUserTable module. */ void init_openserSIPRegUserTable(void); /* * Initialize the openserSIPRegUserTable table by defining its contents and how * it's structured */ void initialize_table_openserSIPRegUserTable(void); const openserSIPRegUserTable_context * openserSIPRegUserTable_get_by_idx( netsnmp_index *); const openserSIPRegUserTable_context * openserSIPRegUserTable_get_by_idx_rs( netsnmp_index *, int row_status); /* Handles SNMP GET requests. */ int openserSIPRegUserTable_get_value( netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *); /* OID Declarations. */ extern oid openserSIPRegUserTable_oid[]; extern size_t openserSIPRegUserTable_oid_len; #define openserSIPRegUserTable_TABLE_OID OPENSER_OID,3,1,2,1,5,6 /* Column Definitions */ #define COLUMN_OPENSERSIPUSERINDEX 1 #define COLUMN_OPENSERSIPUSERURI 2 #define COLUMN_OPENSERSIPUSERAUTHENTICATIONFAILURES 3 #define openserSIPRegUserTable_COL_MIN 2 #define openserSIPRegUserTable_COL_MAX 3 #ifdef __cplusplus } #endif #endif /** OPENSERSIPREGUSERTABLE_H */ opensips-2.2.2/modules/snmpstats/openserSIPServerObjects.c000066400000000000000000000275661300170765700240030ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Note: this file originally auto-generated by mib2c using * : mib2c.scalar.conf,v 1.9 2005/01/07 09:37:18 dts12 Exp $ * * This file implements all scalares defines in the OPENSER-SIP-SERVER-MIB. For * a full description of the scalars, please see OPENSER-SIP-SERVER-MIB. * */ #include #include "../../statistics.h" #include "../../sr_module.h" #include "../../config.h" #include "../usrloc/usrloc.h" #include #include #include #include "openserSIPServerObjects.h" #include "snmpstats_globals.h" #include "utilities.h" /* Used for openserSIPRegUserLookupCounter. (See the MIB file for details) */ unsigned int global_UserLookupCounter; /* Initializes the openserSIPServerObjects module. This involves: * * - Registering all OID's * - Setting up handlers for all OID's * * This function is mostly auto-generated. */ void init_openserSIPServerObjects(void) { static oid openserSIPProxyStatefulness_oid[] = { OPENSER_OID,3,1,2,1,3,1 }; static oid openserSIPProxyRecordRoute_oid[] = { OPENSER_OID,3,1,2,1,3,3 }; static oid openserSIPProxyAuthMethod_oid[] = { OPENSER_OID,3,1,2,1,3,4 }; static oid openserSIPNumProxyRequireFailures_oid[] = { OPENSER_OID,3,1,2,1,4,1 }; static oid openserSIPRegMaxContactExpiryDuration_oid[] = { OPENSER_OID,3,1,2,1,5,2 }; static oid openserSIPRegMaxUsers_oid[] = { OPENSER_OID,3,1,2,1,5,3 }; static oid openserSIPRegCurrentUsers_oid[] = { OPENSER_OID,3,1,2,1,5,4 }; static oid openserSIPRegDfltRegActiveInterval_oid[] = { OPENSER_OID,3,1,2,1,5,5 }; static oid openserSIPRegUserLookupCounter_oid[] = { OPENSER_OID,3,1,2,1,5,8 }; static oid openserSIPRegAcceptedRegistrations_oid[] = { OPENSER_OID,3,1,2,1,6,1 }; static oid openserSIPRegRejectedRegistrations_oid[] = { OPENSER_OID,3,1,2,1,6,2 }; DEBUGMSGTL(("openserSIPServerObjects", "Initializing\n")); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPProxyStatefulness", handle_openserSIPProxyStatefulness, openserSIPProxyStatefulness_oid, OID_LENGTH(openserSIPProxyStatefulness_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPProxyRecordRoute", handle_openserSIPProxyRecordRoute, openserSIPProxyRecordRoute_oid, OID_LENGTH(openserSIPProxyRecordRoute_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPProxyAuthMethod", handle_openserSIPProxyAuthMethod, openserSIPProxyAuthMethod_oid, OID_LENGTH(openserSIPProxyAuthMethod_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPNumProxyRequireFailures", handle_openserSIPNumProxyRequireFailures, openserSIPNumProxyRequireFailures_oid, OID_LENGTH(openserSIPNumProxyRequireFailures_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPRegMaxContactExpiryDuration", handle_openserSIPRegMaxContactExpiryDuration, openserSIPRegMaxContactExpiryDuration_oid, OID_LENGTH(openserSIPRegMaxContactExpiryDuration_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPRegMaxUsers", handle_openserSIPRegMaxUsers, openserSIPRegMaxUsers_oid, OID_LENGTH(openserSIPRegMaxUsers_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPRegCurrentUsers", handle_openserSIPRegCurrentUsers, openserSIPRegCurrentUsers_oid, OID_LENGTH(openserSIPRegCurrentUsers_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPRegDfltRegActiveInterval", handle_openserSIPRegDfltRegActiveInterval, openserSIPRegDfltRegActiveInterval_oid, OID_LENGTH(openserSIPRegDfltRegActiveInterval_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPRegUserLookupCounter", handle_openserSIPRegUserLookupCounter, openserSIPRegUserLookupCounter_oid, OID_LENGTH(openserSIPRegUserLookupCounter_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPRegAcceptedRegistrations", handle_openserSIPRegAcceptedRegistrations, openserSIPRegAcceptedRegistrations_oid, OID_LENGTH(openserSIPRegAcceptedRegistrations_oid), HANDLER_CAN_RONLY)); netsnmp_register_scalar( netsnmp_create_handler_registration( "openserSIPRegRejectedRegistrations", handle_openserSIPRegRejectedRegistrations, openserSIPRegRejectedRegistrations_oid, OID_LENGTH(openserSIPRegRejectedRegistrations_oid), HANDLER_CAN_RONLY)); } /* The following are handlers for the scalars in the OPENSER-SIP-SERVER-MIB. */ int handle_openserSIPProxyStatefulness(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int statefullness; if (module_loaded("dialog")) { statefullness = PROXY_STATEFULNESS_CALL_STATEFUL; } else if (module_loaded("tm")) { statefullness = PROXY_STATEFULNESS_TRANSACTION_STATEFUL; } else { statefullness = PROXY_STATEFULNESS_STATELESS; } if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &statefullness, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPProxyRecordRoute(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* If the rr module is loaded, then we support record route. otherwise, * we want to return 2, which is false for SNMP TruthValue. */ int supportRecordRoute = TC_FALSE; if (module_loaded("rr")) { supportRecordRoute = TC_TRUE; } if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &supportRecordRoute, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPProxyAuthMethod(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* The result needs to be returned as an SNMP bit field. */ unsigned int auth_bitfield = SIP_AUTH_METHOD_NONE; if (module_loaded("tls")) { auth_bitfield |= SIP_AUTH_METHOD_TLS; auth_bitfield &= ~SIP_AUTH_METHOD_NONE; } /* We can have both tls and auth loaded simultaneously. Therefore we * use an if instead of a else/else-if. */ if (module_loaded("auth")) { auth_bitfield |= SIP_AUTH_METHOD_DIGEST; auth_bitfield &= ~SIP_AUTH_METHOD_NONE; } if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, (u_char *) &auth_bitfield, 1); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPNumProxyRequireFailures(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("bad_msg_hdr"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPRegMaxContactExpiryDuration(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("max_expires"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_UNSIGNED, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPRegMaxUsers(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /* OpenSER doesn't currently have a parameterized maximum number of * users. So we return the maximum value an unsigned32 SNMP datatype * can hold. */ unsigned int result = 0xffffffff; if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_UNSIGNED, (u_char *) &result, sizeof(unsigned int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPRegCurrentUsers(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int max_users = 0; max_users = get_statistic("registered_users"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE, (u_char *) &max_users, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPRegDfltRegActiveInterval(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("default_expire"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_UNSIGNED, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPRegUserLookupCounter(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = ++global_UserLookupCounter; /* If we have had so many requests that we've hit our maximum index, * then we reset our counter back to 1. For this not to cause problems, * it will be required that old rows belonging to the table * openserSIPRegUserLookupTable are eventually deleted. */ if (global_UserLookupCounter > MAX_USER_LOOKUP_COUNTER) { global_UserLookupCounter = 1; } if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPRegAcceptedRegistrations(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("accepted_regs"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } int handle_openserSIPRegRejectedRegistrations(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int result = get_statistic("rejected_regs"); if (reqinfo->mode==MODE_GET) { snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER, (u_char *) &result, sizeof(int)); return SNMP_ERR_NOERROR; } return SNMP_ERR_GENERR; } opensips-2.2.2/modules/snmpstats/openserSIPServerObjects.h000066400000000000000000000044731300170765700240000ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Note: this file originally auto-generated by mib2c using * : mib2c.scalar.conf,v 1.9 2005/01/07 09:37:18 dts12 Exp $ * * This file implements all scalars defined in the OPENSER-SIP-SERVER-MIB. * For a full description of the scalars, please see OPENSER-SIP-SERVER-MIB. * */ #ifndef OPENSERSIPSERVEROBJECTS_H #define OPENSERSIPSERVEROBJECTS_H /* function declarations */ void init_openserSIPServerObjects(void); Netsnmp_Node_Handler handle_openserSIPProxyStatefulness; Netsnmp_Node_Handler handle_openserSIPProxyRecordRoute; Netsnmp_Node_Handler handle_openserSIPProxyAuthMethod; Netsnmp_Node_Handler handle_openserSIPNumProxyRequireFailures; Netsnmp_Node_Handler handle_openserSIPRegMaxContactExpiryDuration; Netsnmp_Node_Handler handle_openserSIPRegMaxUsers; Netsnmp_Node_Handler handle_openserSIPRegCurrentUsers; Netsnmp_Node_Handler handle_openserSIPRegDfltRegActiveInterval; Netsnmp_Node_Handler handle_openserSIPRegUserLookupCounter; Netsnmp_Node_Handler handle_openserSIPRegAcceptedRegistrations; Netsnmp_Node_Handler handle_openserSIPRegRejectedRegistrations; #define PROXY_STATEFULNESS_STATELESS 1 #define PROXY_STATEFULNESS_TRANSACTION_STATEFUL 2 #define PROXY_STATEFULNESS_CALL_STATEFUL 3 #define SIP_AUTH_METHOD_NONE (128 >> 0) #define SIP_AUTH_METHOD_TLS (128 >> 1) #define SIP_AUTH_METHOD_DIGEST (128 >> 2) #endif /* OPENSERSIPSERVEROBJECTS_H */ opensips-2.2.2/modules/snmpstats/openserSIPStatusCodesTable.c000066400000000000000000000577001300170765700244250ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Note: this file originally auto-generated by mib2c using * mib2c.array-user.conf * * The file implements the openserSIPStatusCodesTable. For a full description * of the table, please see the OPENSER-SIP-COMMON-MIB. * * This file is much larger and more complicated than the files for other * tables. This is because the table is settable, bringing a lot of SNMP * overhead. Most of the file consists of the original auto-generated * code (aside from white space and comment changes). * * The functions that have been modified to implement this table are the * following: * * 1) openserSIPStatusCodesTable_create_row() * * - The row structure has been modified from its default to store the * number of messages that have been recieved and sent with a certain * status code, at the time this row was created. This function * populates that data. * * 2) openserSIPStatusCodesTable_extract_index() * * - Modified to fail if the index is invalid. The index is invalid if it * does not match up with the global userLookupCounter. * * 3) openserSIPStatusCodesTable_can_[activate|deactivate|delete]() * * - Simplified to always allow activation/deactivation/deletion. * * 4) openserSIPStatusCodesTable_set_reserve1() * * - The reserve1 phase passes if the row is new, and the rowStatus column * is being set to 'createAndGo' * * - The reserve1 phase passes if the row is not new, and the rowStatus * column is being set to 'destroy' * * 5) openserSIPStatusCodesTable_get_value() * * - Instead of returning a variable binding to either * openserSIPStatusCodeIns or openserSIPStatusCodeOuts, the function * returns a variable binding equal to the current value as per the * statistics framework, minus either openserSIPStatusCodeIns or * openserSIPStatusCodeOuts * * You can safely ignore the other functions. * */ #include #include #include #include #include "openserSIPStatusCodesTable.h" #include "snmpstats_globals.h" static netsnmp_handler_registration *my_handler = NULL; static netsnmp_table_array_callbacks cb; oid openserSIPStatusCodesTable_oid[] = { openserSIPStatusCodesTable_TABLE_OID }; size_t openserSIPStatusCodesTable_oid_len = OID_LENGTH(openserSIPStatusCodesTable_oid); /* * Initializes the openserSIPStatusCodesTable module. This step is easier * than in the other tables because there is no table population. All table * population takes place during run time. */ void init_openserSIPStatusCodesTable(void) { initialize_table_openserSIPStatusCodesTable(); } /* the *_row_copy routine */ static int openserSIPStatusCodesTable_row_copy( openserSIPStatusCodesTable_context * dst, openserSIPStatusCodesTable_context * src) { if(!dst||!src) return 1; /* copy index, if provided */ if(dst->index.oids) { free(dst->index.oids); } if(snmp_clone_mem( (void*)&dst->index.oids, src->index.oids, src->index.len * sizeof(oid) )) { dst->index.oids = NULL; return 1; } dst->index.len = src->index.len; /* copy components into the context structure */ dst->openserSIPStatusCodeMethod = src->openserSIPStatusCodeMethod; dst->openserSIPStatusCodeValue = src->openserSIPStatusCodeValue; dst->openserSIPStatusCodeIns = src->openserSIPStatusCodeIns; dst->openserSIPStatusCodeOuts = src->openserSIPStatusCodeOuts; dst->openserSIPStatusCodeRowStatus = src->openserSIPStatusCodeRowStatus; return 0; } /* * the *_extract_index routine (Mostly auto-generated) * * This routine is called when a set request is received for an index that * was not found in the table container. Here, we parse the oid in the * individual index components and copy those indexes to the context. Then * we make sure the indexes for the new row are valid. * * It has been modified from its original form in that the indexes aren't * returned if they are invalid. An index is invalid if it is not between * 100 and 699 (Inclusive). */ int openserSIPStatusCodesTable_extract_index( openserSIPStatusCodesTable_context * ctx, netsnmp_index * hdr) { /* * temporary local storage for extracting oid index * * extract index uses varbinds (netsnmp_variable_list) to parse * the index OID into the individual components for each index part. */ netsnmp_variable_list var_openserSIPStatusCodeMethod; netsnmp_variable_list var_openserSIPStatusCodeValue; int err; /* * copy index, if provided */ if(hdr) { netsnmp_assert(ctx->index.oids == NULL); if((hdr->len > MAX_OID_LEN) || snmp_clone_mem( (void*)&ctx->index.oids, hdr->oids, hdr->len * sizeof(oid))) { return -1; } ctx->index.len = hdr->len; } /* Initialize the two variables responsible for holding our two indices. */ memset(&var_openserSIPStatusCodeMethod, 0x00, sizeof(var_openserSIPStatusCodeMethod)); memset( &var_openserSIPStatusCodeValue, 0x00, sizeof(var_openserSIPStatusCodeValue) ); var_openserSIPStatusCodeMethod.type = ASN_UNSIGNED; var_openserSIPStatusCodeValue.type = ASN_UNSIGNED; var_openserSIPStatusCodeMethod.next_variable = &var_openserSIPStatusCodeValue; var_openserSIPStatusCodeValue.next_variable = NULL; /* parse the oid into the individual index components */ err = parse_oid_indexes( hdr->oids, hdr->len, &var_openserSIPStatusCodeMethod ); if (err == SNMP_ERR_NOERROR) { /* copy index components into the context structure */ ctx->openserSIPStatusCodeMethod = *var_openserSIPStatusCodeMethod.val.integer; ctx->openserSIPStatusCodeValue = *var_openserSIPStatusCodeValue.val.integer; if (*var_openserSIPStatusCodeMethod.val.integer < 1) { err = -1; } if (*var_openserSIPStatusCodeValue.val.integer < 100 || *var_openserSIPStatusCodeValue.val.integer > 699) { err = -1; } } /* parsing may have allocated memory. free it. */ snmp_reset_var_buffers( &var_openserSIPStatusCodeMethod ); return err; } /* * This is an auto-generated function. In general the *_can_activate routine * is called when a row is changed to determine if all the values set are * consistent with the row's rules for a row status of ACTIVE. If not, then 0 * can be returned to prevent the row status from becomming final. * * For our purposes, we have no need for this check, so we always return 1. */ int openserSIPStatusCodesTable_can_activate( openserSIPStatusCodesTable_context *undo_ctx, openserSIPStatusCodesTable_context *row_ctx, netsnmp_request_group * rg) { return 1; } /* * This is an auto-generated function. In general the *_can_deactivate routine * is called when a row that is currently ACTIVE is set to a state other than * ACTIVE. If there are conditions in which a row should not be allowed to * transition out of the ACTIVE state (such as the row being referred to by * another row or table), check for them here. * * Since this table has no reason why this shouldn't be allowed, we always * return 1; */ int openserSIPStatusCodesTable_can_deactivate( openserSIPStatusCodesTable_context *undo_ctx, openserSIPStatusCodesTable_context *row_ctx, netsnmp_request_group * rg) { return 1; } /* * This is an auto-generated function. In general the *_can_delete routine is * called to determine if a row can be deleted. This usually involved checking * if it can be deactivated, and if it can be, then checking for other * conditions. * * Since this table ha no reason why row deletion shouldn't be allowed, we * always return 1, unless we can't deactivate. */ int openserSIPStatusCodesTable_can_delete( openserSIPStatusCodesTable_context *undo_ctx, openserSIPStatusCodesTable_context *row_ctx, netsnmp_request_group * rg) { if(openserSIPStatusCodesTable_can_deactivate(undo_ctx,row_ctx,rg) != 1) return 0; return 1; } /* * (Mostly auto-generated function) * * The *_create_row routine is called by the table handler to create a new row * for a given index. This is the first stage of the row creation process. The * *_set_reserve_* functions can be used to prevent the row from being inserted * into the table even if the row passes any preliminary checks set here. * * Returns a newly allocated openserSIPRegUserLookupTable_context structure (a * row in the table) if the indexes are legal. NULL will be returned otherwise. * * The function has been modified from its original form, in that it will store * the number of messages 'in' to the system, and the number of messages 'out' * of the system, that had a status code matching this rows status code, at the * time this row was created. * * This value will be used in the future to calculate the delta between now and * the time this row has been read. * */ openserSIPStatusCodesTable_context * openserSIPStatusCodesTable_create_row( netsnmp_index* hdr) { stat_var *in_status_code; stat_var *out_status_code; openserSIPStatusCodesTable_context * ctx = SNMP_MALLOC_TYPEDEF(openserSIPStatusCodesTable_context); if(!ctx) return NULL; /* The *_extract_index funtion already validates the indices, so we * don't need to do any further evaluations here. */ if(openserSIPStatusCodesTable_extract_index( ctx, hdr )) { if (NULL != ctx->index.oids) free(ctx->index.oids); free(ctx); return NULL; } /* The indices were already set up in the extract_index function * above. */ ctx->openserSIPStatusCodeIns = 0; ctx->openserSIPStatusCodeOuts = 0; ctx->openserSIPStatusCodeRowStatus = 0; /* Retrieve the index for the status code, and then assign the starting * values. The starting values will be used to calculate deltas during * the next snmpget/snmpwalk/snmptable/etc. */ int codeIndex = ctx->openserSIPStatusCodeValue; ctx->startingInStatusCodeValue = 0; ctx->startingOutStatusCodeValue = 0; in_status_code = get_stat_var_from_num_code(codeIndex, 0); out_status_code = get_stat_var_from_num_code(codeIndex, 1); if (in_status_code != NULL) { ctx->startingInStatusCodeValue = *(long *)in_status_code->u.val; } if (out_status_code != NULL) { ctx->startingOutStatusCodeValue = *(long *)out_status_code->u.val; } return ctx; } /* * Auto-generated function. The *_duplicate row routine */ openserSIPStatusCodesTable_context * openserSIPStatusCodesTable_duplicate_row( openserSIPStatusCodesTable_context * row_ctx) { openserSIPStatusCodesTable_context * dup; if(!row_ctx) return NULL; dup = SNMP_MALLOC_TYPEDEF(openserSIPStatusCodesTable_context); if(!dup) return NULL; if(openserSIPStatusCodesTable_row_copy(dup,row_ctx)) { free(dup); dup = NULL; } return dup; } /* * The *_delete_row method is auto-generated, and is called to delete a row. * * This will not be called if earlier checks said that this row can't be * deleted. However, in our implementation there is never a reason why this * function can't be called. */ netsnmp_index * openserSIPStatusCodesTable_delete_row( openserSIPStatusCodesTable_context * ctx ) { if(ctx->index.oids) free(ctx->index.oids); free( ctx ); return NULL; } /* * Large parts of this function have been auto-generated. The functions purpose * is to check to make sure all SNMP set values for the given row, have been * valid. If not, then the process is supposed to be aborted. Otherwise, we * pass on to the *_reserve2 function. * * For our purposes, our only check is to make sure that either of the following * conditions are true: * * 1) If this row already exists, then the SET request is setting the rowStatus * column to 'destroy'. * * 2) If this row does not already exist, then the SET request is setting the * rowStatus to 'createAndGo'. * * Since the MIB specified there are to be no other modifications to the row, * any other condition is considered illegal, and will result in an SNMP error * being returned. */ void openserSIPStatusCodesTable_set_reserve1( netsnmp_request_group *rg ) { openserSIPStatusCodesTable_context *row_ctx = (openserSIPStatusCodesTable_context *)rg->existing_row; netsnmp_variable_list *var; netsnmp_request_group_item *current; int rc; /* Loop through the specified columns, and make sure that all values are * valid. */ for( current = rg->list; current; current = current->next ) { var = current->ri->requestvb; rc = SNMP_ERR_NOERROR; switch(current->tri->colnum) { case COLUMN_OPENSERSIPSTATUSCODEROWSTATUS: /** RowStatus = ASN_INTEGER */ rc = netsnmp_check_vb_type_and_size(var, ASN_INTEGER, sizeof( row_ctx->openserSIPStatusCodeRowStatus)); /* Want to make sure that if it already exists that it * is setting it to 'destroy', or if it doesn't exist, * that it is setting it to 'createAndGo' */ if (row_ctx->openserSIPStatusCodeRowStatus == 0 && *var->val.integer != TC_ROWSTATUS_CREATEANDGO) { rc = SNMP_ERR_BADVALUE; } else if (row_ctx->openserSIPStatusCodeRowStatus == TC_ROWSTATUS_ACTIVE && *var->val.integer != TC_ROWSTATUS_DESTROY) { rc = SNMP_ERR_BADVALUE; } break; default: /** We shouldn't get here */ rc = SNMP_ERR_GENERR; snmp_log(LOG_ERR, "unknown column in openserSIP" "StatusCodesTable_set_reserve1\n"); } if (rc) { netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc ); } rg->status = SNMP_MAX(rg->status, current->ri->status); } } /* * Auto-generated function. The function is supposed to check for any * last-minute conditions not being met. However, we don't have any such * conditions, so we leave the default function as is. */ void openserSIPStatusCodesTable_set_reserve2( netsnmp_request_group *rg ) { openserSIPStatusCodesTable_context *undo_ctx = (openserSIPStatusCodesTable_context *)rg->undo_info; netsnmp_request_group_item *current; int rc; rg->rg_void = rg->list->ri; for( current = rg->list; current; current = current->next ) { rc = SNMP_ERR_NOERROR; switch(current->tri->colnum) { case COLUMN_OPENSERSIPSTATUSCODEROWSTATUS: /** RowStatus = ASN_INTEGER */ rc = netsnmp_check_vb_rowstatus(current->ri->requestvb, undo_ctx ? undo_ctx->openserSIPStatusCodeRowStatus:0); rg->rg_void = current->ri; break; default: /** We shouldn't get here */ netsnmp_assert(0); /** why wasn't this caught in reserve1? */ } if (rc) { netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc); } } } /* * This function is called only when all the *_reserve[1|2] functions were * succeful. Its purpose is to make any changes to the row before it is * inserted into the table. * * In our case, we don't require any changes. So we leave the original * auto-generated code as is. */ void openserSIPStatusCodesTable_set_action( netsnmp_request_group *rg ) { netsnmp_variable_list *var; openserSIPStatusCodesTable_context *row_ctx = (openserSIPStatusCodesTable_context *)rg->existing_row; openserSIPStatusCodesTable_context *undo_ctx = (openserSIPStatusCodesTable_context *)rg->undo_info; netsnmp_request_group_item *current; int row_err = 0; /* Depending on what the snmpset was, set the row to be created or * deleted. */ for( current = rg->list; current; current = current->next ) { var = current->ri->requestvb; switch(current->tri->colnum) { case COLUMN_OPENSERSIPSTATUSCODEROWSTATUS: /** RowStatus = ASN_INTEGER */ row_ctx->openserSIPStatusCodeRowStatus = *var->val.integer; if (*var->val.integer == TC_ROWSTATUS_CREATEANDGO) { rg->row_created = 1; } else if (*var->val.integer == TC_ROWSTATUS_DESTROY) { rg->row_deleted = 1; } else { /* We should never be here, because the RESERVE * functions should have taken care of all other * values. */ LM_ERR("Invalid RowStatus in openserSIPStatusCodesTable\n"); } break; default: /** We shouldn't get here */ netsnmp_assert(0); /** why wasn't this caught in reserve1? */ } } /* * done with all the columns. Could check row related * requirements here. */ #ifndef openserSIPStatusCodesTable_CAN_MODIFY_ACTIVE_ROW if( undo_ctx && RS_IS_ACTIVE(undo_ctx->openserSIPStatusCodeRowStatus) && row_ctx && RS_IS_ACTIVE(row_ctx->openserSIPStatusCodeRowStatus)) { row_err = 1; } #endif /* * check activation/deactivation */ row_err = netsnmp_table_array_check_row_status(&cb, rg, row_ctx ? &row_ctx->openserSIPStatusCodeRowStatus : NULL, undo_ctx ? &undo_ctx->openserSIPStatusCodeRowStatus : NULL); if(row_err) { netsnmp_set_mode_request_error(MODE_SET_BEGIN, (netsnmp_request_info*)rg->rg_void, row_err); return; } } /* * The COMMIT phase is used to do any extra processing after the ACTION phase. * In our table, there is nothing to do, so the function body is empty. */ void openserSIPStatusCodesTable_set_commit( netsnmp_request_group *rg ) { } /* * This function is called if the *_reserve[1|2] calls failed. Its supposed to * free up any resources allocated earlier. However, we already take care of * all these resources in earlier functions. So for our purposes, the function * body is empty. */ void openserSIPStatusCodesTable_set_free( netsnmp_request_group *rg ) { } /* * This function is called if an ACTION phase fails, to do extra clean-up work. * We don't have anything complicated enough to warrant putting anything in this * function. Therefore, its just left with an empty function body. */ void openserSIPStatusCodesTable_set_undo( netsnmp_request_group *rg ) { } /* * Initialize the openserSIPStatusCodesTable table by defining how it is * structured. * * This function is mostly auto-generated. */ void initialize_table_openserSIPStatusCodesTable(void) { netsnmp_table_registration_info *table_info; if(my_handler) { snmp_log(LOG_ERR, "initialize_table_openserSIPStatusCodes" "Table_handler called again\n"); return; } memset(&cb, 0x00, sizeof(cb)); /** create the table structure itself */ table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); my_handler = netsnmp_create_handler_registration( "openserSIPStatusCodesTable", netsnmp_table_array_helper_handler, openserSIPStatusCodesTable_oid, openserSIPStatusCodesTable_oid_len, HANDLER_CAN_RWRITE); if (!my_handler || !table_info) { snmp_log(LOG_ERR, "malloc failed in initialize_table_openserSIP" "StatusCodesTable_handler\n"); return; /** mallocs failed */ } /** index: openserSIPStatusCodeMethod */ netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED); /** index: openserSIPStatusCodeValue */ netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED); table_info->min_column = openserSIPStatusCodesTable_COL_MIN; table_info->max_column = openserSIPStatusCodesTable_COL_MAX; /*************************************************** * registering the table with the master agent */ cb.get_value = openserSIPStatusCodesTable_get_value; cb.container = netsnmp_container_find("openserSIPStatusCodesTable_primary:" "openserSIPStatusCodesTable:" "table_container"); #ifdef openserSIPStatusCodesTable_CUSTOM_SORT netsnmp_container_add_index(cb.container, netsnmp_container_find( "openserSIPStatusCodesTable_custom:" "openserSIPStatusCodesTable:" "table_container")); cb.container->next->compare = openserSIPStatusCodesTable_cmp; #endif cb.can_set = 1; cb.create_row = (UserRowMethod*)openserSIPStatusCodesTable_create_row; cb.duplicate_row = (UserRowMethod*)openserSIPStatusCodesTable_duplicate_row; cb.delete_row = (UserRowMethod*)openserSIPStatusCodesTable_delete_row; cb.row_copy = (Netsnmp_User_Row_Operation *) openserSIPStatusCodesTable_row_copy; cb.can_activate = (Netsnmp_User_Row_Action *) openserSIPStatusCodesTable_can_activate; cb.can_deactivate = (Netsnmp_User_Row_Action *) openserSIPStatusCodesTable_can_deactivate; cb.can_delete = (Netsnmp_User_Row_Action *)openserSIPStatusCodesTable_can_delete; cb.set_reserve1 = openserSIPStatusCodesTable_set_reserve1; cb.set_reserve2 = openserSIPStatusCodesTable_set_reserve2; cb.set_action = openserSIPStatusCodesTable_set_action; cb.set_commit = openserSIPStatusCodesTable_set_commit; cb.set_free = openserSIPStatusCodesTable_set_free; cb.set_undo = openserSIPStatusCodesTable_set_undo; DEBUGMSGTL(("initialize_table_openserSIPStatusCodesTable", "Registering table openserSIPStatusCodesTable " "as a table array\n")); netsnmp_table_container_register(my_handler, table_info, &cb, cb.container, 1); } /* * This function is called to handle SNMP GET requests. * * The row which this function is called with, will store a message code. The * function will retrieve the 'number of messages in' and 'number of messages * out' statistic for this particular message code from the statistics * framework. * * The function will then subtract from this value the value it was initialized * with when the row was first created. In this sense, the row shows how many * ins and how many outs have been received (With respect to the message code) * since this row was created. */ int openserSIPStatusCodesTable_get_value( netsnmp_request_info *request, netsnmp_index *item, netsnmp_table_request_info *table_info ) { stat_var *the_stat; netsnmp_variable_list *var = request->requestvb; openserSIPStatusCodesTable_context *context = (openserSIPStatusCodesTable_context *)item; /* Retrieve the statusCodeIdx so we can calculate deltas between current * values and previous values. */ int statusCodeIdx = context->openserSIPStatusCodeValue; switch(table_info->colnum) { case COLUMN_OPENSERSIPSTATUSCODEINS: context->openserSIPStatusCodeIns = 0; the_stat = get_stat_var_from_num_code(statusCodeIdx, 0); if (the_stat != NULL) { /* Calculate the Delta */ context->openserSIPStatusCodeIns = *(long *)the_stat->u.val - context->startingInStatusCodeValue; } snmp_set_var_typed_value(var, ASN_COUNTER, (unsigned char*) &context->openserSIPStatusCodeIns, sizeof(context->openserSIPStatusCodeIns)); break; case COLUMN_OPENSERSIPSTATUSCODEOUTS: context->openserSIPStatusCodeOuts = 0; the_stat = get_stat_var_from_num_code(statusCodeIdx, 1); if (the_stat != NULL) { /* Calculate the Delta */ context->openserSIPStatusCodeOuts = *(long *)the_stat->u.val - context->startingOutStatusCodeValue; } snmp_set_var_typed_value(var, ASN_COUNTER, (unsigned char*) &context->openserSIPStatusCodeOuts, sizeof(context->openserSIPStatusCodeOuts) ); break; case COLUMN_OPENSERSIPSTATUSCODEROWSTATUS: /** RowStatus = ASN_INTEGER */ snmp_set_var_typed_value(var, ASN_INTEGER, (unsigned char*) &context->openserSIPStatusCodeRowStatus, sizeof(context->openserSIPStatusCodeRowStatus) ); break; default: /** We shouldn't get here */ snmp_log(LOG_ERR, "unknown column in " "openserSIPStatusCodesTable_get_value\n"); return SNMP_ERR_GENERR; } return SNMP_ERR_NOERROR; } /* * openserSIPRegUserLookupTable_get_by_idx */ const openserSIPStatusCodesTable_context * openserSIPStatusCodesTable_get_by_idx(netsnmp_index * hdr) { return (const openserSIPStatusCodesTable_context *) CONTAINER_FIND(cb.container, hdr ); } opensips-2.2.2/modules/snmpstats/openserSIPStatusCodesTable.h000066400000000000000000000176471300170765700244400ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * 2007-02-16 Moved all OID registrations from the experimental branch to * OpenSER's IANA assigned enterprise branch. (jmagder) * * Note: this file originally auto-generated by mib2c using * mib2c.array-user.conf * * The file implements the openserSIPStatusCodesTable. For a full description * of the table, please see the OPENSER-SIP-COMMON-MIB. * * This file consists of many more functions than the other header files. * This is because this table is writable, bringing a lot of SNMP overhead. * * Most of the contents are auto-generated (aside from white space and comment * changes), and can be ignored. The functions that have been modified are: * * 1) openserSIPStatusCodesTable_create_row() * * 2) openserSIPStatusCodesTable_extract_index() * * 3) openserSIPStatusCodesTable_can_[activate|deactivate|delete]() * * 4) openserSIPStatusCodesTable_set_reserve1() * * 5) openserSIPStatusCodesTable_get_value() * * Full details can be found in openserSIPStatusCodesTable.c. You can safely * ignore the other functions. * */ #ifndef OPENSERSIPSTATUSCODESTABLE_H #define OPENSERSIPSTATUSCODESTABLE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "../../config.h" /* * The structure has been mostly auto-generated, but its semantics have been * changed. * * Specifically, the openserSIPStatusCodeIns and openserSIPStatusCodeOuts * variables don't store the total number of messages sent or received from the * system at the time of a SNMP GET request. Instead, they store the number of * messages in or out (with respect to the message code specified by * openserSIPStatusCodeValue) at the time of the rows *creation*. * * When the get request is received, the statistics framework will be queried, * and these values subtracted from that query. This effectively gives us how * many of the given message codes have occurred since the creation of the row, * insead of since OpenSIPS first loaded up. */ typedef struct openserSIPStatusCodesTable_context_s { /* The container interface requires that this be first. */ netsnmp_index index; /* The first index. */ unsigned long openserSIPStatusCodeMethod; /* The second index, specifying which status code to monitor */ unsigned long openserSIPStatusCodeValue; /* Stores the current status code value - startingInStatusCodeValue * (at the time this row was created) */ unsigned long openserSIPStatusCodeIns; /* Stores the current status code value - startingOutStatusCodeValue * (at the time this row was created) */ unsigned long openserSIPStatusCodeOuts; /* Initialized to zero at startup to signify uninitialized. This can * only be assigned createAndGo(4) at this point. It can also be * assigned destroy(6), but only if the value is in the active(1) state. */ long openserSIPStatusCodeRowStatus; /* Added automatically, but not really used by us. */ void * data; long startingInStatusCodeValue; long startingOutStatusCodeValue; } openserSIPStatusCodesTable_context; /* * Initializes the openserSIPStatusCodesTable module. This step is easier * than in the other tables because there is no table population. All table * population takes place during run time. */ void init_openserSIPStatusCodesTable(void); /* * Initialize the openserSIPStatusCodesTable table by defining how it is * structured. * * This function is mostly auto-generated. */ void initialize_table_openserSIPStatusCodesTable(void); /* * This function is called to handle SNMP GET requests. * * The row which this function is called with, will store a message code. The * function will retrieve the 'number of messages in' and 'number of messages * out' statistic for this particular message code from the statistics * framework. * * The function will then subtract from this value the value it was initialized * with when the row was first created. In this sense, the row shows how many * ins and how many outs have been received (With respect to the message code) * since this row was created. */ int openserSIPStatusCodesTable_get_value(netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *); const openserSIPStatusCodesTable_context * openserSIPStatusCodesTable_get_by_idx(netsnmp_index *); const openserSIPStatusCodesTable_context * openserSIPStatusCodesTable_get_by_idx_rs(netsnmp_index *, int row_status); /* oid declarations */ extern oid openserSIPStatusCodesTable_oid[]; extern size_t openserSIPStatusCodesTable_oid_len; #define openserSIPStatusCodesTable_TABLE_OID OPENSER_OID,3,1,1,1,5,1 /* column number definitions for table openserSIPStatusCodesTable */ #define COLUMN_OPENSERSIPSTATUSCODEMETHOD 1 #define COLUMN_OPENSERSIPSTATUSCODEVALUE 2 #define COLUMN_OPENSERSIPSTATUSCODEINS 3 #define COLUMN_OPENSERSIPSTATUSCODEOUTS 4 #define COLUMN_OPENSERSIPSTATUSCODEROWSTATUS 5 #define openserSIPStatusCodesTable_COL_MIN 3 #define openserSIPStatusCodesTable_COL_MAX 5 /* Handles index extraction for row creation */ int openserSIPStatusCodesTable_extract_index( openserSIPStatusCodesTable_context * ctx, netsnmp_index * hdr ); /* Handle RESERVE1 and RESERVE2 phases of an SNMP SET */ void openserSIPStatusCodesTable_set_reserve1( netsnmp_request_group * ); void openserSIPStatusCodesTable_set_reserve2( netsnmp_request_group * ); /* Handle the SET and ACTION phases of an SNMP SET */ void openserSIPStatusCodesTable_set_action( netsnmp_request_group * ); void openserSIPStatusCodesTable_set_commit( netsnmp_request_group * ); /* Handle Resource cleanup if the ACTION or RESERVE1/RESERVE2 phases of an * SNMPSET fail */ void openserSIPStatusCodesTable_set_free( netsnmp_request_group * ); void openserSIPStatusCodesTable_set_undo( netsnmp_request_group * ); openserSIPStatusCodesTable_context * openserSIPStatusCodesTable_duplicate_row( openserSIPStatusCodesTable_context* ); netsnmp_index * openserSIPStatusCodesTable_delete_row( openserSIPStatusCodesTable_context* ); /* Used to check if there is a reason why a row can't be activated * (There is no reason in our implementation) */ int openserSIPStatusCodesTable_can_activate( openserSIPStatusCodesTable_context *undo_ctx, openserSIPStatusCodesTable_context *row_ctx, netsnmp_request_group * rg); /* Used to check if there is a reason why a row can't be deactivated * (There is no reason in our implementation) */ int openserSIPStatusCodesTable_can_deactivate( openserSIPStatusCodesTable_context *undo_ctx, openserSIPStatusCodesTable_context *row_ctx, netsnmp_request_group * rg); /* Used to check if there is a reason why a row can't be deleted * (There is no reason in our implementation) */ int openserSIPStatusCodesTable_can_delete( openserSIPStatusCodesTable_context *undo_ctx, openserSIPStatusCodesTable_context *row_ctx, netsnmp_request_group * rg); openserSIPStatusCodesTable_context * openserSIPStatusCodesTable_create_row( netsnmp_index* ); #ifdef __cplusplus } #endif #endif /** OPENSERSIPSTATUSCODESTABLE_H */ opensips-2.2.2/modules/snmpstats/snmpstats.c000066400000000000000000000370741300170765700213020ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * There are some important points to understanding the SNMPStat modules * architecture. * * 1) The SNMPStats module will fork off a new process in mod_child_init when * the rank is equal to PROC_MAIN_PROCESS. The sub-process will be * responsible for registering with a master agent (the source of snmp * requests), and handling all received requests. * * 2) The Module will register a periodic alarm checking function with a sip * timer using register_timer(). This function checks for alarm conditions, * and will send out traps to the master agent when it detects their * presence. * * 3) The SNMPStats module is required to run an external application upon * startup, to collect sysUpTime data from the master agent. This involves * spawning a short-lived process. For this reason, the module temporarily * installs a new SIGCHLD handler to deal specifically with this process. It * does not change the normal SIGCHLD behaviour for any process except for * this short lived sysUpTime process. * * 4) mod_init() will initialize some interprocess communication buffers, as * well as callback mechanisms for the usrloc module. To understand what the * interprocess buffer and callbacks are and are for, please see the comments * at the beginning of openserSIPRegUserTable.c * */ #include #include #include #include #include #include #include #include "snmpstats.h" #include "snmpstats_globals.h" #include "../../timer.h" #include #include #include #include "openserSIPRegUserTable.h" #include "openserSIPContactTable.h" #include "interprocess_buffer.h" #include "hashTable.h" #include "alarm_checks.h" #include "utilities.h" #include "sub_agent.h" /* Required in every OpenSIPS Module. */ /* * The module will fork off a child process to run an snmp command via execve(). * We need a customized handler to ignore the SIGCHLD when the execve() * finishes. We keep around the child process's pid for the customized * handler. * * Specifically, If the process that generated the SIGCHLD doesn't match this * pid, we call OpenSER's default handlers. Otherwise, we just ignore SIGCHLD. */ volatile pid_t sysUpTime_pid; /* The functions spawns a sysUpTime child. See the function definition below * for a full description. */ static int spawn_sysUpTime_child(); /* Storage for the "snmpgetPath" and "snmpCommunity" opensips.cfg parameters. * The parameters are used to define what happens with the sysUpTime child. */ char *snmpget_path = NULL; char *snmp_community = NULL; /* * This module replaces the default SIGCHLD handler with our own, as explained * in the documentation for sysUpTime_pid above. This structure holds the old * handler so we can call and restore OpenSER's usual handler when appropriate */ static struct sigaction old_sigchld_handler; /* The following message codes are from Wikipedia at: * * http://en.wikipedia.org/wiki/SIP_Responses * * If there are more message codes added at a later time, they should be added * here, and to out_message_code_names below. * * The array is used to register the statistics keeping track of the number of * messages received with the response code X. */ char *in_message_code_names[] = { "100_in", "180_in", "181_in", "182_in", "183_in", "200_in", "202_in", "300_in", "301_in", "302_in", "305_in", "380_in", "400_in", "401_in", "402_in", "403_in", "404_in", "405_in", "406_in", "407_in", "408_in", "410_in", "413_in", "414_in", "415_in", "416_in", "420_in", "421_in", "423_in", "480_in", "481_in", "482_in", "483_in", "484_in", "485_in", "486_in", "487_in", "488_in", "491_in", "492_in", "494_in", "500_in", "501_in", "502_in", "503_in", "504_in", "505_in", "513_in", "600_in", "603_in", "604_in", "606_in" }; /* The following message codes are from Wikipedia at: * * http://en.wikipedia.org/wiki/SIP_Responses * * If there are more message codes added at a later time, they should be added * here, and to in_message_code_names above. * * The array is used to register the statistics keeping track of the number of * messages send out with the response code X. */ char *out_message_code_names[] = { "100_out", "180_out", "181_out", "182_out", "183_out", "200_out", "202_out", "300_out", "301_out", "302_out", "305_out", "380_out", "400_out", "401_out", "402_out", "403_out", "404_out", "405_out", "406_out", "407_out", "408_out", "410_out", "413_out", "414_out", "415_out", "416_out", "420_out", "421_out", "423_out", "480_out", "481_out", "482_out", "483_out", "484_out", "485_out", "486_out", "487_out", "488_out", "491_out", "492_out", "494_out", "500_out", "501_out", "502_out", "503_out", "504_out", "505_out", "513_out", "600_out", "603_out", "604_out", "606_out" }; /* message_code_stat_array[0] will be the data source for message_code_array[0] * message_code_stat_array[3] will be the data source for message_code_array[3] * and so on. */ stat_var **in_message_code_stats = NULL; stat_var **out_message_code_stats = NULL; /* Adds the message code statistics to the statistics framework */ static int register_message_code_statistics(void) { int i; int number_of_message_codes = sizeof(in_message_code_names) / sizeof(char *); in_message_code_stats = shm_malloc(sizeof(stat_var) * number_of_message_codes); out_message_code_stats = shm_malloc(sizeof(stat_var) * number_of_message_codes); /* We can only proceed if we had enough memory to allocate the * statistics. Note that we don't free the memory, but we don't care * because the system is going to shut down */ if (in_message_code_stats == NULL || out_message_code_stats == NULL) { return -1; } /* Make sure everything is zeroed out */ memset(in_message_code_stats, 0, number_of_message_codes); memset(out_message_code_stats, 0, number_of_message_codes); for (i = 0; i < number_of_message_codes; i++) { register_stat(SNMPSTATS_MODULE_NAME, in_message_code_names[i], &in_message_code_stats[i], 0); register_stat(SNMPSTATS_MODULE_NAME, out_message_code_names[i], &out_message_code_stats[i], 0); } return 0; } /* This is the first function to be called by OpenSIPS, to initialize the module. * This call must always return a value as soon as possible. If it were not to * return, then OpenSIPS would not be able to initialize any of the other * modules. */ static int mod_init(void) { LM_INFO("Starting up the SNMPStats Module\n"); if (register_message_code_statistics() < 0) { return -1; } /* Initialize shared memory used to buffer communication between the * usrloc module and the snmpstats module. */ initInterprocessBuffers(); /* We need to register for callbacks with usrloc module, for whenever a * contact is added or removed from the system. We need to do it now * before OpenSER's functions get a chance to load up old user data from * the database. That load will happen if a lookup() function is come * across in openser.cfg. */ if (!registerForUSRLOCCallbacks()) { /* Originally there were descriptive error messages here to help * the operator debug problems. Turns out this may instead * alarm them about problems they don't need to worry about. So * the messages are commented out for now */ /* LM_ERR("snmpstats module was unable to register callbacks" " with the usrloc module\n"); LM_ERR("Are you sure that the usrloc module was loaded" " before the snmpstats module in "); LM_ERR("openser.cfg? openserSIPRegUserTable will not be " "updated."); */ } /* Register the alarm checking function to run periodically */ register_timer( "snmp-alarm", run_alarm_check, 0, ALARM_AGENT_FREQUENCY_IN_SECONDS, TIMER_FLAG_DELAY_ON_DELAY); return 0; } /* This function is called when OpenSIPS has finished creating all instances of * itself. It is at this point that we want to create our AgentX sub-agent * process, and register a handler for any state changes of our child. */ static int mod_child_init(int rank) { /* We only want to setup a single process, under the first SIP worker, which will exist all the time */ if (rank != 1) { return 0; } /* Spawn a child that will check the system up time. */ spawn_sysUpTime_child(); return 0; } /* This function is called when OpenSIPS is shutting down. When this happens, we * log a useful message and kill the AgentX Sub-Agent child process */ static void mod_destroy(void) { LM_INFO("The SNMPStats module got the kill signal\n"); freeInterprocessBuffer(); LM_INFO("Shutting down the AgentX Sub-Agent!\n"); } /* The SNMPStats module forks off a child process to run an snmp command via * execve(). We need a customized handler to catch and ignore its SIGCHLD when * it terminates. We also need to make sure to forward other processes * SIGCHLD's to OpenSER's usual SIGCHLD handler. We do this by resetting back * OpenSER's own signal handlers after we caught our appropriate SIGCHLD. */ static void sigchld_handler(int signal) { int pid_of_signalled_process_status; int pid_of_signalled_process; /* We need to lookout for the expected SIGCHLD from our * sysUpTime child process, and ignore it. If the SIGCHLD is * from another process, we need to call OpenSER's usual * handlers */ pid_of_signalled_process = waitpid(-1, &pid_of_signalled_process_status, WNOHANG); if (pid_of_signalled_process == sysUpTime_pid) { /* It was the sysUpTime process which died, which was expected. * At this point we will never see any SIGCHLDs from any other * SNMPStats process. This means that we can restore OpenSER's * original handlers. */ sigaction(SIGCHLD, &old_sigchld_handler, NULL); } else { /* We need this 'else-block' in case another OpenSER process dies * unexpectantly before the sysUpTime process dies. If this * doesn't happen, then this code will never be called, because * the block above re-assigns OpenSER's original SIGCHLD * handler. If it does happen, then we make sure to call the * default signal handlers. */ if (old_sigchld_handler.sa_handler != SIG_IGN && old_sigchld_handler.sa_handler != SIG_DFL) { (*(old_sigchld_handler.sa_handler))(signal); } } } /* * This function will spawn a child that retrieves the sysUpTime and stores the * result in a file. This file will be read by the AgentX Sub-agent process to * supply the openserSIPServiceStartTime time. This function never returns, * but it will generated a SIGCHLD when it terminates. There must a SIGCHLD * handler to ignore the SIGCHLD for only this process. (See sigchld_handler * above). * * NOTE: sysUpTime is a scalar provided by netsnmp. It is not the same thing as * a normal system uptime. Support for this has been provided to try to * match the IETF Draft SIP MIBs as closely as possible. */ static int spawn_sysUpTime_child(void) { struct sigaction new_sigchld_handler; char *local_path_to_snmpget = "/usr/bin/"; char *snmpget_binary_name = "/snmpget"; char *full_path_to_snmpget = NULL; char *snmp_community_string = "public"; /* Set up a new SIGCHLD handler. The handler will be responsible for * ignoring SIGCHLDs generated by our sysUpTime child process. Every * other SIGCHLD will be redirected to the old SIGCHLD handler. */ sigfillset(&new_sigchld_handler.sa_mask); new_sigchld_handler.sa_flags = SA_RESTART; new_sigchld_handler.sa_handler = sigchld_handler; sigaction(SIGCHLD, &new_sigchld_handler, &old_sigchld_handler); pid_t result_pid = fork(); if (result_pid < 0) { LM_ERR("failed to not spawn an agent to check sysUpTime\n"); return -1; } else if (result_pid != 0) { /* Keep around the PID of the sysUpTime process so that the * customized SIGCHLD handler knows to ignore the SIGCHLD we * generate when we terminate. */ sysUpTime_pid = result_pid; return 0; } /* If we are here, then we are the child process. Lets set up the file * descriptors so we can capture the output of snmpget. */ int snmpget_fd = open(SNMPGET_TEMP_FILE, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (snmpget_fd == -1) { LM_ERR("failed to open a temporary file " "for snmpget to write to\n"); return -1; } /* Redirect Standard Output to our temporary file. */ dup2(snmpget_fd, 1); if (snmp_community != NULL) { snmp_community_string = snmp_community; } else { LM_INFO("An snmpCommunity parameter was not provided." " Defaulting to %s\n", snmp_community_string); } char *args[] = {"-Ov", "-c", snmp_community_string, "localhost", SYSUPTIME_OID, (char *) 0}; /* Make sure we have a path to snmpget, so we can retrieve the * sysUpTime. */ if (snmpget_path == NULL) { LM_DBG("An snmpgetPath parameter was not specified." " Defaulting to %s\n", local_path_to_snmpget); } else { local_path_to_snmpget = snmpget_path; } int local_path_to_snmpget_length = strlen(local_path_to_snmpget); int snmpget_binary_name_length = strlen(snmpget_binary_name); /* Allocate enough memory to hold the path, the binary name, and the * null character. We don't use pkg_memory here. */ full_path_to_snmpget = malloc(sizeof(char) * (local_path_to_snmpget_length + snmpget_binary_name_length + 1)); if (full_path_to_snmpget == NULL) { LM_ERR("Ran out of memory while trying to retrieve sysUpTime. "); LM_ERR( " openserSIPServiceStartTime is " "defaulting to zero\n"); return -1; } else { /* Make a new string containing the full path to the binary. */ strcpy(full_path_to_snmpget, local_path_to_snmpget); strcpy(&full_path_to_snmpget[local_path_to_snmpget_length], snmpget_binary_name); } /* snmpget -Ov -c public localhost .1.3.6.1.2.1.1.3.0 */ if (execve(full_path_to_snmpget, args, NULL) == -1) { LM_ERR( "snmpget failed to run. Did you supply the snmpstats module" " with a proper snmpgetPath parameter? The " "openserSIPServiceStartTime is defaulting to zero\n"); close(snmpget_fd); free(full_path_to_snmpget); exit(-1); } /* We should never be able to get here, because execve() is never * supposed to return. */ free(full_path_to_snmpget); exit(-1); } /* This function is called whenever the opensips.cfg file specifies the * snmpgetPath parameter. The function will set the snmpget_path parameter. */ int set_snmpget_path( modparam_t type, void *val) { if (!stringHandlerSanityCheck(type, val, "snmpgetPath" )) { return -1; } snmpget_path = (char *)val; return 0; } /* Handles setting of the snmp community string. */ int set_snmp_community( modparam_t type, void *val) { if (!stringHandlerSanityCheck(type, val, "snmpCommunity")) { return -1; } snmp_community = (char *)val; return 0; } opensips-2.2.2/modules/snmpstats/snmpstats.h000066400000000000000000000130051300170765700212730ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * Structure and prototype definitions for the SNMPStats module. * * There are some important points to understanding the SNMPStat modules * architecture. * * 1) The SNMPStats module will fork off a new process in mod_child_init when * the rank is equal to PROC_MAIN_PROCESS. The sub-process will be * responsible for registering with a master agent (the source of snmp * requests), and handling all received requests. * * 2) The Module will register a periodic alarm checking function with a sip * timer using register_timer(). This function checks for alarm conditions, * and will send out traps to the master agent when it detects their * presence. * * 3) The SNMPStats module is required to run an external application upon * startup, to collect sysUpTime data from the master agent. This involves * spawning a short-lived process. For this reason, the module temporarily * installs a new SIGCHLD handler to deal specifically with this process. It * does not change the normal SIGCHLD behaviour for any process except for * this short lived sysUpTime process. * * 4) mod_init() will initialize some interprocess communication buffers, as * well as callback mechanisms for the usrloc module. To understand what the * interprocess buffer and callbacks are and are for, please see the * respective comments in interprocess_buffer.h, openserSIPRegUserTable.h, * and openserSIPContactTable.h. */ #ifndef _SNMP_STATS_ #define _SNMP_STATS_ #include "../../sr_module.h" #include "../../statistics.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../script_cb.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "snmpstats_globals.h" #include "sub_agent.h" #define SNMPSTATS_MODULE_NAME "snmpstats" #define SYSUPTIME_OID ".1.3.6.1.2.1.1.3.0" /* This is the first function to be called by OpenSIPS, to initialize the module. * This call must always return a value as soon as possible. If it were not to * return, then OpenSIPS would not be able to initialize any of the other * modules. */ static int mod_init(void); /* This function is called when OpenSIPS has finished creating all instances of * itself. It is at this point that we want to create our AgentX sub-agent * process, and register a handler for any state changes of our child. */ static int mod_child_init(int rank); /* This function is called when OpenSIPS is shutting down. When this happens, we * log a useful message and kill the AgentX Sub-Agent child process */ static void mod_destroy(void); static proc_export_t mod_procs[] = { {"SNMP AgentX", 0, 0, agentx_child, 1 , 0}, {0,0,0,0,0,0} }; /* * This structure defines the SNMPStats parameters that can be configured * through the opensips.cfg configuration file. */ static param_export_t mod_params[] = { { "sipEntityType", STR_PARAM|USE_FUNC_PARAM, (void *)handleSipEntityType }, { "MsgQueueMinorThreshold", INT_PARAM|USE_FUNC_PARAM, (void *)set_queue_minor_threshold }, { "MsgQueueMajorThreshold", INT_PARAM|USE_FUNC_PARAM, (void *)set_queue_major_threshold }, { "dlg_minor_threshold", INT_PARAM|USE_FUNC_PARAM, (void *)set_dlg_minor_threshold }, { "dlg_major_threshold", INT_PARAM|USE_FUNC_PARAM, (void *)set_dlg_major_threshold }, { "snmpgetPath", STR_PARAM|USE_FUNC_PARAM, (void *)set_snmpget_path }, { "snmpCommunity", STR_PARAM|USE_FUNC_PARAM, (void *)set_snmp_community }, { 0,0,0 } }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "usrloc", DEP_SILENT }, { MOD_TYPE_DEFAULT, "dialog", DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { SNMPSTATS_MODULE_NAME, /* module's name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, /* module's version */ DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ mod_params, /* param exports */ 0, /* exported statistics */ 0, /* MI Functions */ 0, /* pseudo-variables */ mod_procs, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, /* Destroy function */ mod_child_init /* per-child init function */ }; #endif opensips-2.2.2/modules/snmpstats/snmpstats_globals.h000066400000000000000000000112651300170765700230040ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * */ #ifndef _SNMP_STATS_GLOBALS_ #define _SNMP_STATS_GLOBALS_ #include "../../sr_module.h" /*************************************************************** * Textual Conventions for BITS types - begins * * To set a bit : |= with the define * To clear a bit: &= ~(the_define) * * Example: * * 1) Setting a minor alarm status: * * currentAlarmStatus |= TC_ALARM_STATUS_MINOR * * 2) Clearing a minor alarm status: * * currentAlarmStatus &= ~TC_ALARM_STATUS_MINOR */ #define TC_SIP_TRANSPORT_PROTOCOL_OTHER (128>>0) #define TC_SIP_TRANSPORT_PROTOCOL_UDP (128>>1) #define TC_SIP_TRANSPORT_PROTOCOL_TCP (128>>2) #define TC_SIP_TRANSPORT_PROTOCOL_SCTP (128>>3) #define TC_SIP_TRANSPORT_PROTOCOL_TLS (128>>4) #define TC_SIP_ENTITY_ROLE_OTHER (128 >> 0) #define TC_SIP_ENTITY_ROLE_USER_AGENT (128 >> 1) #define TC_SIP_ENTITY_ROLE_PROXY_SERVER (128 >> 2) #define TC_SIP_ENTITY_ROLE_REDIRECT_SERVER (128 >> 3) #define TC_SIP_ENTITY_ROLE_REGISTRAR_SERVER (128 >> 4) #define TC_SIP_OPTION_TAG_REQUIRE (128 >> 0) #define TC_SIP_OPTION_TAG_PROXY_REQUIRE (128 >> 1) #define TC_SIP_OPTION_TAG_SUPPORTED (128 >> 2) #define TC_SIP_OPTION_TAG_UNSUPPORTED (128 >> 3) #define TC_ALARM_STATUS_UNDER_REPAIR (128 >> 0) #define TC_ALARM_STATUS_CRITICAL (128 >> 1) #define TC_ALARM_STATUS_MAJOR (128 >> 2) #define TC_ALARM_STATUS_MINOR (128 >> 3) #define TC_ALARM_STATUS_ALARM_OUTSTANDING (128 >> 4) #define TC_ALARM_STATUS_UNKNOWN (128 >> 5) #define TC_TRANSPORT_PROTOCOL_OTHER (128 >> 0) #define TC_TRANSPORT_PROTOCOL_UDP (128 >> 1) #define TC_TRANSPORT_PROTOCOL_TCP (128 >> 2) #define TC_TRANSPORT_PROTOCOL_SCTP (128 >> 3) #define TC_TRANSPORT_PROTOCOL_TLS (128 >> 4) /* * Textual Conventions for BITS types - ends *************************************************************/ /*************************************************************** * Textual Conventions for INTEGER types - begins */ #define TC_ALARM_STATE_CLEAR 0 #define TC_ALARM_STATE_CRITICAL 1 #define TC_ALARM_STATE_MAJOR 2 #define TC_ALARM_STATE_MINOR 3 #define TC_ALARM_STATE_UNKNOWN 4 #define TC_USAGE_STATE_IDLE 0 #define TC_USAGE_STATE_ACTIVE 1 #define TC_USAGE_STATE_BUSY 2 #define TC_USAGE_STATE_UNKNOWN 3 #define TC_ROWSTATUS_ACTIVE 1 #define TC_ROWSTATUS_NOTINSERVICE 2 #define TC_ROWSTATUS_NOTREADY 3 #define TC_ROWSTATUS_CREATEANDGO 4 #define TC_ROWSTATUS_CREATEANDWAIT 5 #define TC_ROWSTATUS_DESTROY 6 /* * Textual Conventions for INTEGER types - ends *************************************************************/ #define TC_TRUE 1 #define TC_FALSE 2 #define SNMPGET_TEMP_FILE "/tmp/openSER_SNMPAgent.txt" #define SNMPGET_MAX_BUFFER 80 #define MAX_PROC_BUFFER 256 #define MAX_USER_LOOKUP_COUNTER 255 #define HASH_SIZE 32 extern unsigned int global_UserLookupCounter; /******************************************************************************* * Configuration File Handler Prototypes */ /* Handles setting of the sip entity type parameter. */ int handleSipEntityType( modparam_t type, void* val); /* Handles setting of the Msg Queue Depth Minor Threshold */ int set_queue_minor_threshold(modparam_t type, void *val); /* Handles setting of the Msg Queue Depth Major Threshold */ int set_queue_major_threshold(modparam_t type, void *val); /* Handles setting of the dialog minor threshold */ int set_dlg_minor_threshold(modparam_t type, void *val); /* Handles setting of the dialog major threshold */ int set_dlg_major_threshold(modparam_t type, void *val); /* Handles setting of the path to the snmpget binary. */ int set_snmpget_path( modparam_t type, void *val); /* Handles setting of the snmp community string. */ int set_snmp_community( modparam_t type, void *val); #endif opensips-2.2.2/modules/snmpstats/sub_agent.c000066400000000000000000000117121300170765700212040ustar00rootroot00000000000000/* * History: * -------- * 2006-11-23 initial version (jmagder) * * This file defines all functions required to establish a relationship with a * master agent. */ #include #include #include #include #include "sub_agent.h" /* Bring in the NetSNMP headers */ #include #include #include /* Bring in the initialization functions for all scalars */ #include "openserSIPCommonObjects.h" #include "openserSIPServerObjects.h" #include "openserObjects.h" /* Bring in the initialization functions for all tables */ #include "openserSIPPortTable.h" #include "openserSIPMethodSupportedTable.h" #include "openserSIPStatusCodesTable.h" #include "openserSIPRegUserTable.h" #include "openserSIPContactTable.h" #include "openserSIPRegUserLookupTable.h" #include "openserMIBNotifications.h" #include "interprocess_buffer.h" #include "../../dprint.h" static int keep_running; /* The function handles Handles shutting down of the sub_agent process. */ static void sigterm_handler(int signal) { /* Just exit. The master agent will clean everything up for us */ exit(0); } /* This function: * * 1) Registers itself with the Master Agent * * 2) Initializes all of the SNMPStats modules scalars and tables, while * simultaneously registering their respective SNMP OID's and handlers * with the master agent. * * 3) Repeatedly checks for new SNMP messages to process * * Note: This function never returns, so it should always be called from a * sub-process. * */ static int initialize_agentx(void) { /* We register with a master agent */ register_with_master_agent(AGENT_PROCESS_NAME); setInterprocessBuffersAlarm(); /* Initialize all scalars, and let the master agent know we want to * handle all OID's pertaining to these scalars. */ init_openserSIPCommonObjects(); init_openserSIPServerObjects(); init_openserObjects(); /* Initialiaze all the tables, and let the master agent know we want to * handle all the OID's pertaining to these tables */ init_openserSIPPortTable(); init_openserSIPMethodSupportedTable(); init_openserSIPStatusCodesTable(); init_openserSIPRegUserTable(); init_openserSIPContactTable(); init_openserSIPRegUserLookupTable(); /* In case we recevie a request to stop (kill -TERM or kill -INT) */ keep_running = 1; while(keep_running) { agent_check_and_process(1); /* 0 == don't block */ } snmp_shutdown(AGENT_PROCESS_NAME); SOCK_CLEANUP; exit (0); return 0; } /* Creates a child that will become the AgentX sub-agent. The child will * insulate itself from the rest of OpenSIPS by overriding most of signal * handlers. */ void agentx_child(int rank) { struct sigaction new_sigterm_handler; struct sigaction default_handlers; struct sigaction sigpipe_handler; /* Setup a SIGTERM handler */ sigfillset(&new_sigterm_handler.sa_mask); new_sigterm_handler.sa_flags = 0; new_sigterm_handler.sa_handler = sigterm_handler; sigaction(SIGTERM, &new_sigterm_handler, NULL); /* We don't want OpenSIPS's normal handlers doing anything when * we die. As far as OpenSIPS knows this process never existed. * So override all signal handlers to the OS default. */ sigemptyset(&default_handlers.sa_mask); default_handlers.sa_flags = 0; default_handlers.sa_handler = SIG_DFL; sigaction(SIGCHLD, &default_handlers, NULL); sigaction(SIGINT, &default_handlers, NULL); sigaction(SIGHUP, &default_handlers, NULL); sigaction(SIGUSR1, &default_handlers, NULL); /* SIGUSR2 must be handled by OpenSIPS as it is used for collecting info on pkg memory */ /*sigaction(SIGUSR2, &default_handlers, NULL);*/ /* It is possible that the master agent will unregister us if we * take too long to respond to an SNMP request. This would * happen if a large number of users/contacts have been * registered between snmp requests to the user/contact tables. * In this situation we may try to write to a closed socket when * we are done processing, resulting in a SIGPIPE. This doesn't * need to be fatal however, because we can re-establish our * connection. Therefore we set ourselves up to ignore the * SIGPIPE. */ sigpipe_handler.sa_flags = SA_RESTART; sigpipe_handler.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sigpipe_handler, NULL); initialize_agentx(); } /* This function opens up a connection with the master agent specified in * the snmpstats modules configuration file */ void register_with_master_agent(char *name_to_register_under) { /* Set ourselves up as an AgentX Client. */ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); /* Initialize TCP if necessary. (Its here for WIN32) compatibility. */ SOCK_STARTUP; /* Read in our configuration file to determine master agent ping times * what port communication is to take place over, etc. */ init_agent("snmpstats"); /* Use a name we can register our agent under. */ init_snmp(name_to_register_under); } opensips-2.2.2/modules/snmpstats/sub_agent.h000066400000000000000000000030221300170765700212040ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * This file defines all functions required to establish a relationship with a * master agent. */ #ifndef _SNMPSTATS_SUB_AGENT_ #define _SNMPSTATS_SUB_AGENT_ #define AGENT_PROCESS_NAME "snmpstats_sub_agent" /* Run the AgentX sub-agent as a separate process. The child will * insulate itself from the rest of OpenSIPS by overriding most of signal * handlers. */ void agentx_child(int rank); /* This function opens up a connection with the master agent specified in * the snmpstats modules configuration file */ void register_with_master_agent(char *name_to_register_under); #endif opensips-2.2.2/modules/snmpstats/utilities.c000066400000000000000000000064311300170765700212520ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * This file was created to group together utility functions that were useful * throughout the SNMPStats module, without belonging to any file in particular. */ #include #include #include "utilities.h" #include "../../str.h" #include "../../locking.h" #include "../../mem/mem.h" /* Silently returns 1 if the supplied parameters are sane. Otherwise, an error * message is logged for parameterName, and 0 returned. */ int stringHandlerSanityCheck( modparam_t type, void *val, char *parameterName) { char *theString = (char *)val; /* Make sure the function was called correctly. */ if (type != STR_PARAM) { LM_ERR("the %s parameter was assigned a type %d instead of %d\n", parameterName, type, STR_PARAM); return 0; } /* An empty string was supplied. We consider this illegal */ if (theString==0 || (theString[0])==0) { LM_ERR("the %s parameter was specified with an empty string\n", parameterName); return 0; } return 1; } /* * This function is a wrapper around the standard statistic framework. It will * return the value of the statistic denoted with statName, or zero if the * statistic was not found. */ int get_statistic(char *statName) { int result = 0; str theStr; theStr.s = statName; theStr.len = strlen(statName); stat_var *theVar = get_stat(&theStr); if (theVar==0) { LM_INFO("failed to retrieve statistics for %s\n", statName); } else { result = get_stat_val(theVar); } return result; } /* Returns a pointer to an SNMP DateAndTime OCTET STRING representation of the * time structure. Note that the pointer is to static data, so it shouldn't be * counted on to be around if this function is called again. */ char * convertTMToSNMPDateAndTime(struct tm *timeStructure) { static char dateAndTime[8]; /* The tm structure stores the number of years since 1900. We need to * change the offset. */ int currentYear = timeStructure->tm_year + 1900; /* See SNMPv2-TC for the conversion details */ dateAndTime[0] = (char) ((currentYear & 0xFF00) >> 8); dateAndTime[1] = (char) currentYear & 0xFF; dateAndTime[2] = (char) timeStructure->tm_mon + 1; dateAndTime[3] = (char) timeStructure->tm_mday; dateAndTime[4] = (char) timeStructure->tm_hour; dateAndTime[5] = (char) timeStructure->tm_min; dateAndTime[6] = (char) timeStructure->tm_sec; dateAndTime[7] = 0; return dateAndTime; } opensips-2.2.2/modules/snmpstats/utilities.h000066400000000000000000000036421300170765700212600ustar00rootroot00000000000000/* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-11-23 initial version (jmagder) * * This file was created to group together utility functions that were useful * throughout the SNMPStats module, without belonging to any file in particular. */ #ifndef _SNMP_UTILITIES_ #define _SNMP_UTILITIES_ #include #include "../../str.h" #include "../../sr_module.h" /* Performs sanity checks on the parameters passed to a string configuration * file parameter handler. */ int stringHandlerSanityCheck( modparam_t type, void *val, char *parameterName); /* * This function is a wrapper around the standard statistic framework. It will * return the value of the statistic denoted with statName, or zero if the * statistic was not found. */ int get_statistic(char *statName); /* Returns a pointer to an SNMP DateAndTime OCTET STRING representation of the * time structure. Note that the pointer is to static data, so it shouldn't be * counted on to be around if this function is called again. */ char * convertTMToSNMPDateAndTime(struct tm *timeStructure); #endif opensips-2.2.2/modules/speeddial/000077500000000000000000000000001300170765700167655ustar00rootroot00000000000000opensips-2.2.2/modules/speeddial/Makefile000066400000000000000000000003251300170765700204250ustar00rootroot00000000000000# $Id$ # # SPEEDDIAL Module # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=speeddial.so LIBS= DEFS+= include ../../Makefile.modules opensips-2.2.2/modules/speeddial/README000066400000000000000000000170241300170765700176510ustar00rootroot00000000000000SpeedDial Module Elena-Ramona Modroiu Edited by Elena-Ramona Modroiu Copyright © 2004 Voice Sistem SRL Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. user_column (string) 1.3.3. domain_column (string) 1.3.4. sd_user_column (string) 1.3.5. sd_domain_column (string) 1.3.6. new_uri_column (string) 1.3.7. domain_prefix (string) 1.3.8. use_domain (int) 1.4. Exported Functions 1.4.1. sd_lookup(table [, owner]) 1.5. Installation and Running 1.5.1. OpenSIPS config file List of Examples 1.1. Set db_url parameter 1.2. Set user_column parameter 1.3. Set domain_column parameter 1.4. Set sd_user_column parameter 1.5. Set sd_domain_column parameter 1.6. Set new_uri_column parameter 1.7. Set domain_prefix parameter 1.8. Set use_domain parameter 1.9. sd_lookup usage 1.10. OpenSIPS config script - sample speeddial usage Chapter 1. Admin Guide 1.1. Overview This module provides on-server speed dial facilities. An user can store records consisting of pairs short numbers (2 digits) and SIP addresses into a table of OpenSIPS. Then it can dial the two digits whenever it wants to call the SIP address associated with them. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * database module (mysql, dbtext, ...). 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. db_url (string) The URL of database where the table containing speed dial records. Default value is mysql://opensipsro:opensipsro@localhost/opensips. Example 1.1. Set db_url parameter ... modparam("speeddial", "db_url", "mysql://user:xxx@localhost/db_name") ... 1.3.2. user_column (string) The name of column storing the user name of the owner of the speed dial record. Default value is “usernameâ€. Example 1.2. Set user_column parameter ... modparam("speeddial", "user_column", "userid") ... 1.3.3. domain_column (string) The name of column storing the domain of the owner of the speed dial record. Default value is “domainâ€. Example 1.3. Set domain_column parameter ... modparam("speeddial", "domain_column", "userdomain") ... 1.3.4. sd_user_column (string) The name of the column storing the user part of the short dial address. Default value is “sd_usernameâ€. Example 1.4. Set sd_user_column parameter ... modparam("speeddial", "sd_user_column", "short_user") ... 1.3.5. sd_domain_column (string) The name of the column storing the domain of the short dial address. Default value is “sd_domainâ€. Example 1.5. Set sd_domain_column parameter ... modparam("speeddial", "sd_domain_column", "short_domain") ... 1.3.6. new_uri_column (string) The name of the column containing the URI that will be use to replace the short dial URI. Default value is “new_uriâ€. Example 1.6. Set new_uri_column parameter ... modparam("speeddial", "new_uri_column", "real_uri") ... 1.3.7. domain_prefix (string) If the domain of the owner (From URI) starts with the value of this parameter, then it is stripped before performing the lookup of the short number. Default value is NULL. Example 1.7. Set domain_prefix parameter ... modparam("speeddial", "domain_prefix", "tel.") ... 1.3.8. use_domain (int) The parameter specifies wheter or not to use the domain when searching a speed dial record (0 - no domain, 1 - use domain from From URI, 2 - use both domains, from From URI and from request URI). Default value is 0. Example 1.8. Set use_domain parameter ... modparam("speeddial", "use_domain", 1) ... 1.4. Exported Functions 1.4.1. sd_lookup(table [, owner]) The function lookups the short dial number from R-URI in 'table' and replaces the R-URI with associated address. Meaning of the parameters is as follows: * table - The name of the table storing the speed dial records. This parameter may include pseudovariables. * owner - The SIP URI of the owner of short dialing codes. If not pressent, URI of From header is used. This function can be used from REQUEST_ROUTE. Example 1.9. sd_lookup usage ... # 'speed_dial' is the default table name created by opensips db script if(uri=~"sip:[0-9]{2}@.*") sd_lookup("speed_dial"); # use auth username if(uri=~"sip:[0-9]{2}@.*") sd_lookup("speed_dial", "sip:$au@$fd"); ... 1.5. Installation and Running 1.5.1. OpenSIPS config file Next picture displays a sample usage of speeddial. Example 1.10. OpenSIPS config script - sample speeddial usage ... # # $Id$ # # sample config script to use speeddial module # # ----------- global configuration parameters ------------------------ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) # ------------------ module loading ---------------------------------- mpath="/usr/local/lib/opensips/modules" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "mysql.so" loadmodule "speeddial.so" loadmodule "mi_fifo.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- usrloc params -- modparam("usrloc", "db_mode", 0) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); exit; }; if (!method=="REGISTER") record_route(); if (loose_route()) { if (!t_relay()) { sl_reply_error(); }; exit; }; if (!uri==myself) { if (!t_relay()) { sl_reply_error(); }; exit; }; if (uri==myself) { if (method=="REGISTER") { save("location"); exit; }; if(uri=~"sip:[0-9]{2}@.*") sd_lookup("speeddial"); lookup("aliases"); if (!uri==myself) { if (!t_relay()) { sl_reply_error(); }; exit; }; if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; }; if (!t_relay()) { sl_reply_error(); }; } ... opensips-2.2.2/modules/speeddial/doc/000077500000000000000000000000001300170765700175325ustar00rootroot00000000000000opensips-2.2.2/modules/speeddial/doc/speeddial.cfg000066400000000000000000000032311300170765700221440ustar00rootroot00000000000000# # $Id$ # # sample config script to use speeddial module # # ----------- global configuration parameters ------------------------ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) # ------------------ module loading ---------------------------------- mpath="/usr/local/lib/opensips/modules" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "mysql.so" loadmodule "speeddial.so" loadmodule "mi_fifo.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- usrloc params -- modparam("usrloc", "db_mode", 0) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); exit; }; if (!method=="REGISTER") record_route(); if (loose_route()) { if (!t_relay()) { sl_reply_error(); }; exit; }; if (!uri==myself) { if (!t_relay()) { sl_reply_error(); }; exit; }; if (uri==myself) { if (method=="REGISTER") { save("location"); exit; }; if(uri=~"sip:[0-9]{2}@.*") sd_lookup("speeddial"); lookup("aliases"); if (!uri==myself) { if (!t_relay()) { sl_reply_error(); }; exit; }; if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; }; if (!t_relay()) { sl_reply_error(); }; } opensips-2.2.2/modules/speeddial/doc/speeddial.xml000066400000000000000000000024101300170765700222030ustar00rootroot00000000000000 %docentities; ]> SpeedDial Module &osipsname; Elena-Ramona Modroiu ramona@rosdev.ro
http://www.rosdev.ro
Elena-Ramona Modroiu ramona@rosdev.ro
2004 &voicesystem; $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/speeddial/doc/speeddial_admin.xml000066400000000000000000000147661300170765700233740ustar00rootroot00000000000000 &adminguide;
Overview This module provides on-server speed dial facilities. An user can store records consisting of pairs short numbers (2 digits) and SIP addresses into a table of OpenSIPS. Then it can dial the two digits whenever it wants to call the SIP address associated with them.
Dependencies
&osips; Modules The following modules must be loaded before this module: database module (mysql, dbtext, ...).
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>db_url</varname> (string) The URL of database where the table containing speed dial records. Default value is &defaultrodb;. Set <varname>db_url</varname> parameter ... modparam("speeddial", "db_url", "mysql://user:xxx@localhost/db_name") ...
<varname>user_column</varname> (string) The name of column storing the user name of the owner of the speed dial record. Default value is username. Set <varname>user_column</varname> parameter ... modparam("speeddial", "user_column", "userid") ...
<varname>domain_column</varname> (string) The name of column storing the domain of the owner of the speed dial record. Default value is domain. Set <varname>domain_column</varname> parameter ... modparam("speeddial", "domain_column", "userdomain") ...
<varname>sd_user_column</varname> (string) The name of the column storing the user part of the short dial address. Default value is sd_username. Set <varname>sd_user_column</varname> parameter ... modparam("speeddial", "sd_user_column", "short_user") ...
<varname>sd_domain_column</varname> (string) The name of the column storing the domain of the short dial address. Default value is sd_domain. Set <varname>sd_domain_column</varname> parameter ... modparam("speeddial", "sd_domain_column", "short_domain") ...
<varname>new_uri_column</varname> (string) The name of the column containing the URI that will be use to replace the short dial URI. Default value is new_uri. Set <varname>new_uri_column</varname> parameter ... modparam("speeddial", "new_uri_column", "real_uri") ...
<varname>domain_prefix</varname> (string) If the domain of the owner (From URI) starts with the value of this parameter, then it is stripped before performing the lookup of the short number. Default value is NULL. Set <varname>domain_prefix</varname> parameter ... modparam("speeddial", "domain_prefix", "tel.") ...
<varname>use_domain</varname> (int) The parameter specifies wheter or not to use the domain when searching a speed dial record (0 - no domain, 1 - use domain from From URI, 2 - use both domains, from From URI and from request URI). Default value is 0. Set <varname>use_domain</varname> parameter ... modparam("speeddial", "use_domain", 1) ...
Exported Functions
<function moreinfo="none">sd_lookup(table [, owner])</function> The function lookups the short dial number from R-URI in 'table' and replaces the R-URI with associated address. Meaning of the parameters is as follows: table - The name of the table storing the speed dial records. This parameter may include pseudovariables. owner - The SIP URI of the owner of short dialing codes. If not pressent, URI of From header is used. This function can be used from REQUEST_ROUTE. <function>sd_lookup</function> usage ... # 'speed_dial' is the default table name created by opensips db script if(uri=~"sip:[0-9]{2}@.*") sd_lookup("speed_dial"); # use auth username if(uri=~"sip:[0-9]{2}@.*") sd_lookup("speed_dial", "sip:$au@$fd"); ...
Installation and Running
&osips; config file Next picture displays a sample usage of speeddial. &osips; config script - sample speeddial usage ... &speeddialcfg; ...
opensips-2.2.2/modules/speeddial/sdlookup.c000066400000000000000000000132611300170765700207740ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * */ #include #include "../../dprint.h" #include "../../action.h" #include "../../config.h" #include "../../ut.h" #include "../../mod_fix.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../db/db.h" #include "speeddial.h" #include "sdlookup.h" #define MAX_USERURI_SIZE 256 static char useruri_buf[MAX_USERURI_SIZE]; /** * */ int sd_lookup(struct sip_msg* _msg, char* _table, char* _owner) { static db_ps_t my_ps = NULL; str user_s, table_s, uri_s; int nr_keys; struct sip_uri *puri; struct sip_uri turi; db_key_t db_keys[4]; db_val_t db_vals[4]; db_key_t db_cols[1]; db_res_t* db_res = NULL; if(_table==NULL || fixup_get_svalue(_msg, (gparam_p)_table, &table_s)!=0) { LM_ERR("invalid table parameter"); return -1; } /* init */ nr_keys = 0; db_cols[0]=&new_uri_column; if(_owner) { memset(&turi, 0, sizeof(struct sip_uri)); if(fixup_get_svalue(_msg, (gparam_p)_owner, &uri_s)!=0) { LM_ERR("invalid owner uri parameter"); return -1; } if(parse_uri(uri_s.s, uri_s.len, &turi)!=0) { LM_ERR("bad owner SIP address!\n"); goto err_server; } LM_DBG("using user id [%.*s]\n", uri_s.len, uri_s.s); puri = &turi; } else { /* take username@domain from From header */ if ( (puri = parse_from_uri(_msg ))==NULL ) { LM_ERR("failed to parse FROM header\n"); goto err_server; } } db_keys[nr_keys]=&user_column; db_vals[nr_keys].type = DB_STR; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.str_val.s = puri->user.s; db_vals[nr_keys].val.str_val.len = puri->user.len; nr_keys++; if(use_domain>=1) { db_keys[nr_keys]=&domain_column; db_vals[nr_keys].type = DB_STR; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.str_val.s = puri->host.s; db_vals[nr_keys].val.str_val.len = puri->host.len; nr_keys++; if (dstrip_s.s!=NULL && dstrip_s.len>0 && dstrip_s.lenhost.len && strncasecmp(puri->host.s,dstrip_s.s,dstrip_s.len)==0) { db_vals[nr_keys].val.str_val.s += dstrip_s.len; db_vals[nr_keys].val.str_val.len -= dstrip_s.len; } } /* take sd from r-uri */ if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("failed to parsing Request-URI\n"); goto err_server; } db_keys[nr_keys]=&sd_user_column; db_vals[nr_keys].type = DB_STR; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.str_val.s = _msg->parsed_uri.user.s; db_vals[nr_keys].val.str_val.len = _msg->parsed_uri.user.len; nr_keys++; if(use_domain>=2) { db_keys[nr_keys]=&sd_domain_column; db_vals[nr_keys].type = DB_STR; db_vals[nr_keys].nul = 0; db_vals[nr_keys].val.str_val.s = _msg->parsed_uri.host.s; db_vals[nr_keys].val.str_val.len = _msg->parsed_uri.host.len; nr_keys++; if (dstrip_s.s!=NULL && dstrip_s.len>0 && dstrip_s.len<_msg->parsed_uri.host.len && strncasecmp(_msg->parsed_uri.host.s,dstrip_s.s,dstrip_s.len)==0) { db_vals[nr_keys].val.str_val.s += dstrip_s.len; db_vals[nr_keys].val.str_val.len -= dstrip_s.len; } } db_funcs.use_table(db_handle, &table_s); CON_PS_REFERENCE(db_handle) = &my_ps; if(db_funcs.query(db_handle, db_keys, NULL, db_vals, db_cols, nr_keys /*no keys*/, 1 /*no cols*/, NULL, &db_res)!=0) { LM_ERR("failed to query database\n"); goto err_server; } if (RES_ROW_N(db_res)<=0 || RES_ROWS(db_res)[0].values[0].nul != 0) { LM_DBG("no sip address found for R-URI\n"); if (db_res!=NULL && db_funcs.free_result(db_handle, db_res) < 0) LM_DBG("failed to free result of query\n"); return -1; } user_s.s = useruri_buf+4; switch(RES_ROWS(db_res)[0].values[0].type) { case DB_STRING: strcpy(user_s.s, (char*)RES_ROWS(db_res)[0].values[0].val.string_val); user_s.len = strlen(user_s.s); break; case DB_STR: strncpy(user_s.s, (char*)RES_ROWS(db_res)[0].values[0].val.str_val.s, RES_ROWS(db_res)[0].values[0].val.str_val.len); user_s.len = RES_ROWS(db_res)[0].values[0].val.str_val.len; user_s.s[user_s.len] = '\0'; break; case DB_BLOB: strncpy(user_s.s, (char*)RES_ROWS(db_res)[0].values[0].val.blob_val.s, RES_ROWS(db_res)[0].values[0].val.blob_val.len); user_s.len = RES_ROWS(db_res)[0].values[0].val.blob_val.len; user_s.s[user_s.len] = '\0'; default: LM_ERR("unknown type of DB new_uri column\n"); if (db_res != NULL && db_funcs.free_result(db_handle, db_res) < 0) { LM_DBG("failed to free result of query\n"); } goto err_server; } /* check 'sip:' */ if(user_s.len<4 || strncasecmp(user_s.s, "sip:", 4)) { memcpy(useruri_buf, "sip:", 4); user_s.s -= 4; user_s.len += 4; } /** * Free the result because we don't need it anymore */ if (db_res!=NULL && db_funcs.free_result(db_handle, db_res) < 0) LM_DBG("failed to free result of query\n"); /* set the URI */ LM_DBG("URI of sd from R-URI [%.*s]\n", user_s.len,user_s.s); if(set_ruri(_msg, &user_s)<0) { LM_ERR("failed to replace the R-URI\n"); goto err_server; } return 1; err_server: return -1; } opensips-2.2.2/modules/speeddial/sdlookup.h000066400000000000000000000020001300170765700207660ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * */ #ifndef _SDLOOKUP_H_ #define _SDLOOKUP_H_ #include "../../parser/msg_parser.h" int sd_lookup(struct sip_msg* _msg, char* _table, char* _str2); #endif /* _SDLOOKUP_H_ */ opensips-2.2.2/modules/speeddial/speeddial.c000066400000000000000000000107351300170765700210710ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * */ #include #include #include "../../sr_module.h" #include "../../db/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../mod_fix.h" #include "sdlookup.h" /* Module destroy function prototype */ static void destroy(void); /* Module child-init function prototype */ static int child_init(int rank); /* Module initialization function prototype */ static int mod_init(void); /* Module parameter variables */ static str db_url = {NULL,0}; str user_column = str_init("username"); str domain_column = str_init("domain"); str sd_user_column = str_init("sd_username"); str sd_domain_column = str_init("sd_domain"); str new_uri_column = str_init("new_uri"); int use_domain = 0; static str domain_prefix = {NULL, 0}; str dstrip_s = {NULL, 0}; db_func_t db_funcs; /* Database functions */ db_con_t* db_handle=0; /* Database connection handle */ /* Exported functions */ static cmd_export_t cmds[] = { {"sd_lookup", (cmd_function)sd_lookup, 1, fixup_spve_null, 0, REQUEST_ROUTE}, {"sd_lookup", (cmd_function)sd_lookup, 2, fixup_spve_spve, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0, 0} }; /* Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &db_url.s }, {"user_column", STR_PARAM, &user_column.s }, {"domain_column", STR_PARAM, &domain_column.s }, {"sd_user_column", STR_PARAM, &sd_user_column.s }, {"sd_domain_column", STR_PARAM, &sd_domain_column.s }, {"new_uri_column", STR_PARAM, &new_uri_column.s }, {"use_domain", INT_PARAM, &use_domain }, {"domain_prefix", STR_PARAM, &domain_prefix.s }, {0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /* Module interface */ struct module_exports exports = { "speeddial", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* Exported functions */ NULL, /* Exported async functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ child_init /* child initialization function */ }; /** * */ static int child_init(int rank) { db_handle = db_funcs.init(&db_url); if (!db_handle) { LM_ERR("failed to connect database\n"); return -1; } return 0; } /** * */ static int mod_init(void) { LM_DBG("initializing\n"); init_db_url( db_url , 0 /*cannot be null*/); user_column.len = strlen(user_column.s); domain_column.len = strlen(domain_column.s); sd_user_column.len = strlen(sd_user_column.s); sd_domain_column.len = strlen(sd_domain_column.s); new_uri_column.len = strlen(new_uri_column.s); if (domain_prefix.s) domain_prefix.len = strlen(domain_prefix.s); /* Find a database module */ if (db_bind_mod(&db_url, &db_funcs)) { LM_ERR("failed to bind database module\n"); return -1; } if (!DB_CAPABILITY(db_funcs, DB_CAP_QUERY)) { LM_ERR("Database modules does not " "provide all functions needed by SPEEDDIAL module\n"); return -1; } if (domain_prefix.s && domain_prefix.len > 0) { dstrip_s.s = domain_prefix.s; dstrip_s.len = domain_prefix.len; } return 0; } /** * */ static void destroy(void) { if (db_handle) db_funcs.close(db_handle); } opensips-2.2.2/modules/speeddial/speeddial.h000066400000000000000000000027731300170765700211010ustar00rootroot00000000000000/* * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * */ #ifndef _SPEEDDIAL_H_ #define _SPEEDDIAL_H_ #include "../../db/db.h" #include "../../parser/msg_parser.h" /* Module parameters variables */ extern str user_column; /* 'username' column name */ extern str domain_column; /* 'domain' column name */ extern str sd_user_column; /* 'sd_username' column name */ extern str sd_domain_column; /* 'sd_domain' column name */ extern str new_uri_column; /* 'new_uri' column name */ extern int use_domain; /* use or not the domain for sd lookup */ extern str dstrip_s; extern db_func_t db_funcs; /* Database functions */ extern db_con_t* db_handle; /* Database connection handle */ #endif /* _SPEEDDIAL_H_ */ opensips-2.2.2/modules/sql_cacher/000077500000000000000000000000001300170765700171375ustar00rootroot00000000000000opensips-2.2.2/modules/sql_cacher/Makefile000066400000000000000000000002551300170765700206010ustar00rootroot00000000000000# WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=sql_cacher.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/sql_cacher/README000066400000000000000000000232551300170765700200260ustar00rootroot00000000000000SQL Cacher Module Robert-Vladut Patrascu OpenSIPS Solutions Copyright © 2015 www.opensips-solutions.com __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.3. Exported Parameters 1.3.1. cache_table (string) 1.3.2. spec_delimiter (string) 1.3.3. pvar_delimiter (string) 1.3.4. columns_delimiter (string) 1.3.5. sql_fetch_nr_rows (integer) 1.3.6. full_caching_expire (integer) 1.3.7. reload_interval (integer) 1.4. Exported Functions 1.5. MI Commands 1.5.1. sql_cacher_reload 1.6. Exported pseudo-variables 1.6.1. $sql_cached_value(id{sep}col{sep}key) 1.7. Usage Example List of Examples 1.1. cache_table parameter usage 1.2. spec_delimiter parameter usage 1.3. pvar_delimiter parameter usage 1.4. columns_delimiter parameter usage 1.5. sql_fetch_nr_rows parameter usage 1.6. full_caching_expire parameter usage 1.7. reload_interval parameter usage 1.8. sql_cacher_reload usage 1.9. sql_cached_value(id{sep}col{sep}key) pseudo-variable usage 1.10. Example database content - carrierfailureroute table 1.11. Setting the cache_table parameter 1.12. Accessing cached values Chapter 1. Admin Guide 1.1. Overview The sql_cacher module introduces the possibility to cache data from a SQL-based database (using different OpenSIPS modules which implement the DB API) into a cache system implemented in OpenSIPS through the CacheDB Interface. This is done by specifying the databases URLs, SQL table to be used, desired columns to be cached and other details in the OpenSIPS configuration script. The cached data is available in the script through the read-only pseudovariable “$sql_cached_value†similar to a Key-Value system. A specified column from the SQL table has the role of “key†therefore the value of this column along with the name of a required column are provided as "parameters" to the pseudovariable returning the appropriate value of the column. There are two types of caching available: * full caching - the entire SQL table (all the rows) is loaded into the cache at OpenSIPS startup; * on demand - the rows of the SQL table are loaded at runtime when appropriate keys are requested. For on demand caching, the stored values have a configurable expire period after which they are permanently removed unless an MI reload function is called for a specific key. In the case of full caching the data is automatically reloaded at a configurable interval. Consequently if the data in the SQL database changes and a MI reload function is called, the old data remains in cache only until it expires. 1.2. Dependencies The following modules must be loaded before this module: * The OpenSIPS modules that offer actual database back-end connection 1.3. Exported Parameters 1.3.1. cache_table (string) This parameter can be set multiple times in order to cache multiple SQL tables or even the same table but with a different configuration. The module distinguishes those different entries by an “id†string. The caching entry is specified via this parameter that has it's own subparameters. Each of those parameters are separted by a configurable delimiter and have the following format: param_name=param_value The parameters are: * id : cache entry id * db_url : the URL of the SQL database * cachedb_url : the URL of the CacheDB database * table : SQL database table name * key : SQL database column name of the “key†column * columns : names of the columns to be cached from the SQL database, separated by a configurable delimiter If not present, all the columns from the table will be cached * on_demand : specifies the type of caching: + 0 : full caching + 1 : on demand If not present, default value is “0†* expire : expire period for the values stored in the cache for the on demand caching type in seconds If not present, default value is “1 hour†The parameters must be given in the exact order specified above. Overall, the parameter does not have a default value, it must be set at least once in order to cache any table. Example 1.1. cache_table parameter usage modparam("sql_cacher", "cache_table", "id=caching_name db_url=mysql://root:opensips@localhost/opensips_2_2 cachedb_url=mongodb:mycluster://127.0.0.1:27017/db.col table=table_name key=column_name_0 columns=column_name_1 column_name_2 column_name_3 on_demand=0") 1.3.2. spec_delimiter (string) The delimiter to be used in the caching entry specification provided in the cache_table parameter to separate the subparameters. It must be a single character. The default value is newline. Example 1.2. spec_delimiter parameter usage modparam("sql_cacher", "spec_delimiter", "\n") 1.3.3. pvar_delimiter (string) The delimiter to be used in the “$sql_cached_value†pseudovariable to separate the caching id, the desired column name and the value of the key. It must be a single character. The default value is “:â€. Example 1.3. pvar_delimiter parameter usage modparam("sql_cacher", "pvar_delimiter", " ") 1.3.4. columns_delimiter (string) The delimiter to be used in the columns subparameter of the caching entry specification provided in the cache_table parameter to separate the desired columns names. It must be a single character. The default value is “ â€(space). Example 1.4. columns_delimiter parameter usage modparam("sql_cacher", "columns_delimiter", ",") 1.3.5. sql_fetch_nr_rows (integer) The number of rows to be fetched into OpenSIPS private memory in one chunk from the SQL database driver. When querying large tables, adjust this parameter accordingly to avoid the filling of OpenSIPS private memory. The default value is “100â€. Example 1.5. sql_fetch_nr_rows parameter usage modparam("sql_cacher", "sql_fetch_nr_rows", "1000") 1.3.6. full_caching_expire (integer) Expire period for the values stored in cache for the full caching type in seconds. This is the longest time that deleted or modified data remains in cache. The default value is “24 hoursâ€. Example 1.6. full_caching_expire parameter usage modparam("sql_cacher", "full_caching_expire", "3600") 1.3.7. reload_interval (integer) This parameter represents how many seconds before the data expires (for full caching) the automatic reloading is triggerd. The default value is “60 sâ€. Example 1.7. reload_interval parameter usage modparam("sql_cacher", "reload_interval", "5") 1.4. Exported Functions No function exported to be used from configuration file. 1.5. MI Commands 1.5.1. sql_cacher_reload Reloads the entire SQL table in cache for full caching or the specified key for on demand caching. The first parameter is the caching id. The second parameter must be a value of the key column from the SQL table. For full caching this parameter is not needed. Example 1.8. sql_cacher_reload usage ... $ opensipsctl fifo sql_cacher_reload caching_name ... $ opensipsctl fifo sql_cacher_reload caching_name key ... 1.6. Exported pseudo-variables 1.6.1. $sql_cached_value(id{sep}col{sep}key) The cached data is available through this read-only PV.The format is the following: * sep : separator configured by “pvar_delimiter†parameter * id : cache entry id * col : name of the required column * key : value of the “key†column Example 1.9. sql_cached_value(id{sep}col{sep}key) pseudo-variable usage ... $avp(a) = $sql_cached_value(caching_name:column_name_1:key1); ... 1.7. Usage Example This section provides an usage example for the caching of an SQL table. Suppose one in interested in caching the columns: “host_nameâ€, “reply_codeâ€, “flags†and “next_domain†from the “carrierfailureroute†table of the OpenSIPS database. Example 1.10. Example database content - carrierfailureroute table ... +----+---------+-----------+------------+--------+-----+-------------+ | id | domain | host_name | reply_code | flags | mask | next_domain | +----+---------+-----------+------------+-------+------+-------------+ | 1 | 99 | | 408 | 16 | 16 | | | 2 | 99 | gw1 | 404 | 0 | 0 | 100 | | 3 | 99 | gw2 | 50. | 0 | 0 | 100 | | 4 | 99 | | 404 | 2048 | 2112 | asterisk-1 | +----+---------+-----------+------------+-------+------+-------------+ ... In the first place, the details of the caching must be provided by setting the module parameter “cache_table†in the OpenSIPS configuration script. Example 1.11. Setting the cache_table parameter modparam("sql_cacher", "cache_table", "id=carrier_fr_caching db_url=mysql://root:opensips@localhost/opensips cachedb_url=mongodb:mycluster://127.0.0.1:27017/my_db.col table=carrierfailureroute key=id columns=host_name reply_code flags next_domain") Next, the values of the cached columns ca be accessed through the “$sql_cached_value†PV. Example 1.12. Accessing cached values ... $avp(rc1) = $sql_cached_value(carrier_fr_caching:reply_code:1); $avp(rc2) = $sql_cached_value(carrier_fr_caching:reply_code:2); ... var(some_id)=4; $avp(nd) = $sql_cached_value(carrier_fr_caching:next_domain:$var(some_id )); ... xlog("host name is: $sql_cached_value(carrier_fr_caching:host_name:2)"); ... opensips-2.2.2/modules/sql_cacher/doc/000077500000000000000000000000001300170765700177045ustar00rootroot00000000000000opensips-2.2.2/modules/sql_cacher/doc/sql_cacher.xml000066400000000000000000000016331300170765700225350ustar00rootroot00000000000000 %docentities; ]> SQL Cacher Module &osipsname; Robert-Vladut Patrascu OpenSIPS Solutions
rvlad.patrascu@gmail.com http://www.opensips.org
2015 &osipssol;
&admin;
opensips-2.2.2/modules/sql_cacher/doc/sql_cacher_admin.xml000066400000000000000000000303141300170765700237030ustar00rootroot00000000000000 &adminguide;
Overview The sql_cacher module introduces the possibility to cache data from a SQL-based database (using different &osips; modules which implement the DB API) into a cache system implemented in &osips; through the CacheDB Interface. This is done by specifying the databases URLs, SQL table to be used, desired columns to be cached and other details in the &osips; configuration script. The cached data is available in the script through the read-only pseudovariable $sql_cached_value similar to a Key-Value system. A specified column from the SQL table has the role of key therefore the value of this column along with the name of a required column are provided as "parameters" to the pseudovariable returning the appropriate value of the column. There are two types of caching available: full caching - the entire SQL table (all the rows) is loaded into the cache at &osips; startup; on demand - the rows of the SQL table are loaded at runtime when appropriate keys are requested. For on demand caching, the stored values have a configurable expire period after which they are permanently removed unless an MI reload function is called for a specific key. In the case of full caching the data is automatically reloaded at a configurable interval. Consequently if the data in the SQL database changes and a MI reload function is called, the old data remains in cache only until it expires.
Dependencies The following modules must be loaded before this module: The &osips; modules that offer actual database back-end connection
Exported Parameters
<varname>cache_table</varname> (string) This parameter can be set multiple times in order to cache multiple SQL tables or even the same table but with a different configuration. The module distinguishes those different entries by an id string. The caching entry is specified via this parameter that has it's own subparameters. Each of those parameters are separted by a configurable delimiter and have the following format: param_name=param_value The parameters are: id : cache entry id db_url : the URL of the SQL database cachedb_url : the URL of the CacheDB database table : SQL database table name key : SQL database column name of the key column columns : names of the columns to be cached from the SQL database, separated by a configurable delimiter If not present, all the columns from the table will be cached on_demand : specifies the type of caching: 0 : full caching 1 : on demand If not present, default value is 0 expire : expire period for the values stored in the cache for the on demand caching type in seconds If not present, default value is 1 hour The parameters must be given in the exact order specified above. Overall, the parameter does not have a default value, it must be set at least once in order to cache any table. <varname>cache_table</varname> parameter usage modparam("sql_cacher", "cache_table", "id=caching_name db_url=mysql://root:opensips@localhost/opensips_2_2 cachedb_url=mongodb:mycluster://127.0.0.1:27017/db.col table=table_name key=column_name_0 columns=column_name_1 column_name_2 column_name_3 on_demand=0")
<varname>spec_delimiter</varname> (string) The delimiter to be used in the caching entry specification provided in the cache_table parameter to separate the subparameters. It must be a single character. The default value is newline. <varname>spec_delimiter</varname> parameter usage modparam("sql_cacher", "spec_delimiter", "\n")
<varname>pvar_delimiter</varname> (string) The delimiter to be used in the $sql_cached_value pseudovariable to separate the caching id, the desired column name and the value of the key. It must be a single character. The default value is :. <varname>pvar_delimiter</varname> parameter usage modparam("sql_cacher", "pvar_delimiter", " ")
<varname>columns_delimiter</varname> (string) The delimiter to be used in the columns subparameter of the caching entry specification provided in the cache_table parameter to separate the desired columns names. It must be a single character. The default value is (space). <varname>columns_delimiter</varname> parameter usage modparam("sql_cacher", "columns_delimiter", ",")
<varname>sql_fetch_nr_rows</varname> (integer) The number of rows to be fetched into &osips; private memory in one chunk from the SQL database driver. When querying large tables, adjust this parameter accordingly to avoid the filling of &osips; private memory. The default value is 100. <varname>sql_fetch_nr_rows</varname> parameter usage modparam("sql_cacher", "sql_fetch_nr_rows", "1000")
<varname>full_caching_expire</varname> (integer) Expire period for the values stored in cache for the full caching type in seconds. This is the longest time that deleted or modified data remains in cache. The default value is 24 hours. <varname>full_caching_expire</varname> parameter usage modparam("sql_cacher", "full_caching_expire", "3600")
<varname>reload_interval</varname> (integer) This parameter represents how many seconds before the data expires (for full caching) the automatic reloading is triggerd. The default value is 60 s. <varname>reload_interval</varname> parameter usage modparam("sql_cacher", "reload_interval", "5")
Exported Functions No function exported to be used from configuration file.
<acronym>MI</acronym> Commands
<function moreinfo="none">sql_cacher_reload</function> Reloads the entire SQL table in cache for full caching or the specified key for on demand caching. The first parameter is the caching id. The second parameter must be a value of the key column from the SQL table. For full caching this parameter is not needed. <function moreinfo="none">sql_cacher_reload</function> usage ... $ opensipsctl fifo sql_cacher_reload caching_name ... $ opensipsctl fifo sql_cacher_reload caching_name key ...
Exported pseudo-variables
<varname>$sql_cached_value(id{sep}col{sep}key)</varname> The cached data is available through this read-only PV.The format is the following: sep : separator configured by pvar_delimiter parameter id : cache entry id col : name of the required column key : value of the key column <function moreinfo="none">sql_cached_value(id{sep}col{sep}key) pseudo-variable</function> usage ... $avp(a) = $sql_cached_value(caching_name:column_name_1:key1); ...
Usage Example This section provides an usage example for the caching of an SQL table. Suppose one in interested in caching the columns: host_name, reply_code, flags and next_domain from the carrierfailureroute table of the &osips; database. Example database content - carrierfailureroute table ... +----+---------+-----------+------------+--------+-----+-------------+ | id | domain | host_name | reply_code | flags | mask | next_domain | +----+---------+-----------+------------+-------+------+-------------+ | 1 | 99 | | 408 | 16 | 16 | | | 2 | 99 | gw1 | 404 | 0 | 0 | 100 | | 3 | 99 | gw2 | 50. | 0 | 0 | 100 | | 4 | 99 | | 404 | 2048 | 2112 | asterisk-1 | +----+---------+-----------+------------+-------+------+-------------+ ... In the first place, the details of the caching must be provided by setting the module parameter cache_table in the &osips; configuration script. Setting the <varname>cache_table</varname> parameter modparam("sql_cacher", "cache_table", "id=carrier_fr_caching db_url=mysql://root:opensips@localhost/opensips cachedb_url=mongodb:mycluster://127.0.0.1:27017/my_db.col table=carrierfailureroute key=id columns=host_name reply_code flags next_domain") Next, the values of the cached columns ca be accessed through the $sql_cached_value PV. Accessing cached values ... $avp(rc1) = $sql_cached_value(carrier_fr_caching:reply_code:1); $avp(rc2) = $sql_cached_value(carrier_fr_caching:reply_code:2); ... var(some_id)=4; $avp(nd) = $sql_cached_value(carrier_fr_caching:next_domain:$var(some_id)); ... xlog("host name is: $sql_cached_value(carrier_fr_caching:host_name:2)"); ...
opensips-2.2.2/modules/sql_cacher/sql_cacher.c000066400000000000000000001401411300170765700214100ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-09-xx initial version (Vlad Patrascu) */ #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../trim.h" #include "../../pvar.h" #include "../../locking.h" #include "../../rw_locking.h" #include "../../timer.h" #include "sql_cacher.h" static int mod_init(void); static void destroy(void); static int child_init(int rank); int pv_parse_name(pv_spec_p sp, str *in); int pv_init_param(pv_spec_p sp, int param); int pv_get_sql_cached_value(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int parse_cache_entry(unsigned int type, void *val); static struct mi_root* mi_reload(struct mi_root *cmd_tree, void *param); static str spec_delimiter = str_init(DEFAULT_SPEC_DELIM); static str pvar_delimiter = str_init(DEFAULT_PVAR_DELIM); static str columns_delimiter = str_init(DEFAULT_COLUMNS_DELIM); static int fetch_nr_rows = DEFAULT_FETCH_NR_ROWS; static int full_caching_expire = DEFAULT_FULL_CACHING_EXPIRE; static int reload_interval = DEFAULT_RELOAD_INTERVAL; static cache_entry_t **entry_list; static struct queried_key **queries_in_progress; /* per process db handlers corresponding to cache entries in entry_list */ static db_handlers_t *db_hdls_list; gen_lock_t *queries_lock; /* module parameters */ static param_export_t mod_params[] = { {"spec_delimiter", STR_PARAM, &spec_delimiter.s}, {"pvar_delimiter", STR_PARAM, &pvar_delimiter.s}, {"columns_delimiter", STR_PARAM, &columns_delimiter.s}, {"sql_fetch_nr_rows", INT_PARAM, &fetch_nr_rows}, {"full_caching_expire", INT_PARAM, &full_caching_expire}, {"reload_interval", INT_PARAM, &reload_interval}, {"cache_table", STR_PARAM|USE_FUNC_PARAM, (void *)&parse_cache_entry}, {0,0,0} }; static pv_export_t mod_items[] = { {{"sql_cached_value", sizeof("sql_cached_value") - 1}, 1000, pv_get_sql_cached_value, 0, pv_parse_name, 0, 0, 0}, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; static mi_export_t mi_cmds[] = { { "sql_cacher_reload", "reload the SQL database into the cache", mi_reload, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_CACHEDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; /** * module exports */ struct module_exports exports = { "sql_cacher", /* module name */ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ mod_params, /* exported parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response handling function */ destroy, /* destroy function */ child_init /* per-child init function */ }; static int parse_cache_entry(unsigned int type, void *val) { cache_entry_t *new_entry; char *p1, *p2, *tmp, *c_tmp1, *c_tmp2; int col_idx; int rc = -1; int i; str parse_str_copy, parse_str; if(!entry_list){ entry_list = shm_malloc(sizeof(cache_entry_t*)); if (!entry_list) { LM_ERR("No more memory for cache entries list\n"); return -1; } *entry_list = NULL; } parse_str.len = strlen((char *)val); parse_str.s = pkg_malloc(parse_str.len); if(!parse_str.s){ LM_ERR("No more pkg memory\n"); return -1; } memcpy(parse_str.s, (char *)val, parse_str.len); new_entry = shm_malloc(sizeof(cache_entry_t)); if (!new_entry) { LM_ERR("No more memory for cache entry struct\n"); return -1; } new_entry->id.s = NULL; new_entry->columns = NULL; new_entry->nr_columns = 0; new_entry->on_demand = 0; new_entry->expire = DEFAULT_ON_DEMAND_EXPIRE; new_entry->nr_ints = 0; new_entry->nr_strs = 0; new_entry->column_types = 0; new_entry->ref_lock = NULL; #define PARSE_TOKEN(_ptr1, _ptr2, field, field_name_str, field_name_len) \ do { \ (_ptr2) = memchr((_ptr1), '=', parse_str.len - \ ((_ptr1) - parse_str.s)); \ if (!(_ptr2)) { \ LM_ERR("expected: '=' after %.*s\n", (field_name_len), (field_name_str)); \ goto parse_err; \ } \ if (!memcmp((_ptr1), (field_name_str), (field_name_len))) { \ if (*((_ptr1)+(field_name_len)) != '=') { \ LM_ERR("expected: '=' after %.*s\n", (field_name_len), (field_name_str)); \ goto parse_err; \ } \ tmp = memchr((_ptr2) + 1, spec_delimiter.s[0], parse_str.len - \ ((_ptr2) - parse_str.s)); \ if (!tmp) { \ LM_ERR("expected: %c after value of %.*s\n", spec_delimiter.s[0], \ (field_name_len), (field_name_str)); \ goto parse_err; \ } \ new_entry->field.len = tmp - (_ptr2) - 1; \ if (new_entry->field.len <= 0) { \ LM_ERR("expected value of: %.*s\n", (field_name_len), (field_name_str)); \ goto parse_err; \ } \ new_entry->field.s = shm_malloc(new_entry->field.len); \ memcpy(new_entry->field.s, p2 + 1, new_entry->field.len); \ } else { \ LM_ERR("expected: %.*s instead of: %.*s\n", (field_name_len), (field_name_str), \ (field_name_len), (_ptr1)); \ goto parse_err; \ } \ } while (0) parse_str_copy = parse_str; trim(&parse_str); /* parse the id */ p1 = parse_str.s; PARSE_TOKEN(p1, p2, id, ID_STR, ID_STR_LEN); /* parse the db_url */ p1 = tmp + 1; PARSE_TOKEN(p1, p2, db_url, DB_URL_STR, DB_URL_LEN); /* parse the cachedb_url */ p1 = tmp + 1; PARSE_TOKEN(p1, p2, cachedb_url, CACHEDB_URL_STR, CACHEDB_URL_LEN); /* parse the table name */ p1 = tmp + 1; PARSE_TOKEN(p1, p2, table, TABLE_STR, TABLE_STR_LEN); #undef PARSE_TOKEN /* parse the key column name */ p1 = tmp + 1; p2 = memchr(p1, '=', parse_str.len - (p1 - parse_str.s)); if (!p2) { LM_ERR("expected: '=' after %.*s\n", KEY_STR_LEN, KEY_STR); goto parse_err; } if (!memcmp(p1, KEY_STR, KEY_STR_LEN)) { if (*(p1+KEY_STR_LEN) != '=') { \ LM_ERR("expected: '=' after %.*s\n", KEY_STR_LEN, KEY_STR); goto parse_err; } tmp = memchr(p2 + 1, spec_delimiter.s[0], parse_str.len - (p2 - parse_str.s)); if (!tmp) /* delimiter not found, reached the end of the string to parse */ new_entry->key.len = parse_str.len - (p2 - parse_str.s + 1); else new_entry->key.len = tmp - p2 - 1; if (new_entry->key.len <= 0) { LM_ERR("expected value of: %.*s\n", KEY_STR_LEN, KEY_STR); goto parse_err; } new_entry->key.s = shm_malloc(new_entry->key.len); memcpy(new_entry->key.s, p2 + 1, new_entry->key.len); if (!tmp) goto end_parsing; } else { LM_ERR("expected: %.*s instead of: %.*s\n", (KEY_STR_LEN), (KEY_STR), \ KEY_STR_LEN, p1); goto parse_err; } /* parse the required column names if present */ p1 = tmp + 1; p2 = memchr(p1, '=', parse_str.len - (p1 - parse_str.s)); if (!p2) { LM_ERR("expected: '='\n"); goto parse_err; } if (!memcmp(p1, COLUMNS_STR, COLUMNS_STR_LEN)) { if (*(p1+COLUMNS_STR_LEN) != '=') { \ LM_ERR("expected: '=' after: %.*s\n", COLUMNS_STR_LEN, COLUMNS_STR); goto parse_err; } col_idx = 0; tmp = memchr(p2 + 1, spec_delimiter.s[0], parse_str.len - (p2 - parse_str.s)); /* just count how many columns there are */ new_entry->nr_columns = 1; c_tmp1 = memchr(p2 + 1, columns_delimiter.s[0], parse_str.len - (p2 - parse_str.s + 1)); while (c_tmp1) { new_entry->nr_columns++; c_tmp1 = memchr(c_tmp1 + 1, columns_delimiter.s[0], parse_str.len - (c_tmp1 - parse_str.s + 1)); } if (new_entry->nr_columns > sizeof(long long)) { LM_WARN("Too many columns, maximum number is %lu\n", (unsigned long)sizeof(long long)); goto parse_err; } /* allocate array of columns and actually parse */ new_entry->columns = shm_malloc(new_entry->nr_columns * sizeof(str*)); c_tmp1 = p2 + 1; c_tmp2 = memchr(p2 + 1, columns_delimiter.s[0], parse_str.len - (p2 - parse_str.s + 1)); while (c_tmp2) { new_entry->columns[col_idx] = shm_malloc(sizeof(str)); (*new_entry->columns[col_idx]).len = c_tmp2 - c_tmp1; if ((*new_entry->columns[col_idx]).len <= 0) { LM_ERR("expected name of column\n"); goto parse_err; } (*new_entry->columns[col_idx]).s = shm_malloc((*new_entry->columns[col_idx]).len); memcpy((*new_entry->columns[col_idx]).s, c_tmp1, (*new_entry->columns[col_idx]).len); c_tmp1 = c_tmp2 + 1; c_tmp2 = memchr(c_tmp1, columns_delimiter.s[0], parse_str.len - (c_tmp1 - parse_str.s + 1)); col_idx++; } new_entry->columns[col_idx] = shm_malloc(sizeof(str)); if (!tmp) (*new_entry->columns[col_idx]).len = parse_str.len - (p2 - c_tmp1 + 1); else (*new_entry->columns[col_idx]).len = tmp - c_tmp1; if ((*new_entry->columns[col_idx]).len <= 0) { LM_ERR("expected name of column\n"); goto parse_err; } (*new_entry->columns[col_idx]).s = shm_malloc((*new_entry->columns[col_idx]).len); memcpy((*new_entry->columns[col_idx]).s, c_tmp1, (*new_entry->columns[col_idx]).len); if (!tmp) /* delimiter not found, reached the end of the string to parse */ goto end_parsing; else { p1 = tmp + 1; p2 = memchr(p1, '=', parse_str.len - (p1 - parse_str.s)); if (!p2) { LM_ERR("expected: '='\n"); goto parse_err; } } } /* parse on demand parameter */ if (!memcmp(p1, ONDEMAND_STR, ONDEMAND_STR_LEN)) { if (*(p1+ONDEMAND_STR_LEN) != '=') { \ LM_ERR("expected: '=' after: %.*s\n", ONDEMAND_STR_LEN, ONDEMAND_STR); goto parse_err; } tmp = memchr(p2 + 1, spec_delimiter.s[0], parse_str.len - (p2 - parse_str.s)); str str_val; if (!tmp) /* delimiter not found, reached the end of the string to parse */ str_val.len = parse_str.len - (p2 - parse_str.s + 1); else str_val.len = tmp - p2 - 1; if (str_val.len <= 0) { LM_ERR("expected value of: %.*s\n", ONDEMAND_STR_LEN, ONDEMAND_STR); goto parse_err; } str_val.s = p2 + 1; if(str2int(&str_val, &new_entry->on_demand)) { LM_ERR("expected integer value for: %.*s instead of: %.*s\n", ONDEMAND_STR_LEN, ONDEMAND_STR, str_val.len, str_val.s); goto parse_err; } if (!tmp) /* delimiter not found, reached the end of the string to parse */ goto end_parsing; else { p1 = tmp + 1; p2 = memchr(p1, '=', parse_str.len - (p1 - parse_str.s)); if (!p2) { LM_ERR("expected: '='\n"); goto parse_err; } } } /* parse expire parameter */ if (!memcmp(p1, EXPIRE_STR, EXPIRE_STR_LEN)) { str str_val; str_val.len = parse_str.len - (p2 - parse_str.s + 1); if (str_val.len <= 0) { LM_ERR("expected value of: %.*s\n", EXPIRE_STR_LEN, EXPIRE_STR); goto parse_err; } str_val.s = p2 + 1; if(str2int(&str_val, &new_entry->expire)) { LM_ERR("expected integer value for: %.*s instead of: %.*s\n", EXPIRE_STR_LEN, EXPIRE_STR, str_val.len, str_val.s); goto parse_err; } } else { LM_ERR("unknown parameter: %.*s\n", (int)(parse_str.len - (p1 - parse_str.s)), p1); goto parse_err; } end_parsing: new_entry->next = NULL; if (*entry_list) new_entry->next = *entry_list; *entry_list = new_entry; pkg_free(parse_str_copy.s); return 0; parse_err: if (!new_entry->id.s) LM_WARN("invalid cache entry specification: %.*s\n", parse_str.len, parse_str.s); else LM_WARN("invalid cache entry specification for id: %.*s\n", new_entry->id.len, new_entry->id.s); if (new_entry->columns) { for (i=0; i < new_entry->nr_columns; i++) if (new_entry->columns[i]) { if ((*new_entry->columns[i]).s) shm_free((*new_entry->columns[i]).s); shm_free(new_entry->columns[i]); } shm_free(new_entry->columns); } shm_free(new_entry); pkg_free(parse_str_copy.s); // } return rc; } /* get the column types from the sql query result */ static int get_column_types(cache_entry_t *c_entry, db_val_t *values, int nr_columns) { unsigned int i; long long one = 1; db_type_t val_type; c_entry->nr_ints = 0; c_entry->nr_strs = 0; c_entry->column_types = 0; for (i = 0; i < nr_columns; i++) { val_type = VAL_TYPE(values + i); switch (val_type) { case DB_INT: case DB_BIGINT: case DB_DOUBLE: c_entry->nr_ints++; c_entry->column_types &= ~(one << i); break; case DB_STRING: case DB_STR: c_entry->nr_strs++; c_entry->column_types |= (one << i); break; default: return -1; } } return 0; } /* returns the total size of the actual value which will be stored in the cachedb*/ static unsigned int get_cdb_val_size(cache_entry_t *c_entry, db_val_t *values, int nr_columns) { unsigned int i, len = 0; db_type_t val_type; /* reload version + integer values + offsets of the string values */ len = INT_B64_ENC_LEN + c_entry->nr_ints*INT_B64_ENC_LEN + c_entry->nr_strs*INT_B64_ENC_LEN; /* length of the actual string values*/ for (i = 0; i < nr_columns; i++) { val_type = VAL_TYPE(values + i); switch (val_type) { case DB_STRING: len += strlen(VAL_STRING(values + i)); break; case DB_STR: len += VAL_STR(values + i).len; break; default: continue; } } return len; } static int insert_in_cachedb(cache_entry_t *c_entry, db_handlers_t *db_hdls, db_val_t *key, db_val_t *values, int reload_version, int nr_columns) { unsigned int i, offset = 0, strs_offset = 0; int int_val; int int_key_len = 0; char int_buf[4], int_enc_buf[INT_B64_ENC_LEN]; char *int_key_buf = NULL; str str_val; db_type_t val_type; str str_key; str cdb_val; str cdb_key; cdb_val.len = get_cdb_val_size(c_entry, values, nr_columns); cdb_val.s = pkg_malloc(cdb_val.len); if (!cdb_val.s) { LM_ERR("No more pkg memory\n"); return -1; } /* store the reload version (base64 encoded) */ memcpy(int_buf, &reload_version, 4); base64encode((unsigned char *)int_enc_buf, (unsigned char *)int_buf, 4); memcpy(cdb_val.s, int_enc_buf, INT_B64_ENC_LEN); offset += INT_B64_ENC_LEN; /* store the integer values (base64 encoded) */ for (i = 0; i < nr_columns; i++) { int_val = 0; val_type = VAL_TYPE(values + i); switch (val_type) { case DB_INT: int_val = VAL_INT(values + i); break; case DB_BIGINT: int_val = (int)VAL_BIGINT(values + i); break; case DB_DOUBLE: int_val = (int)VAL_DOUBLE(values + i); break; default: continue; } if (VAL_NULL(values + i)) memset(int_enc_buf, 0, INT_B64_ENC_LEN); else { memcpy(int_buf, &int_val, 4); base64encode((unsigned char *)int_enc_buf, (unsigned char *)int_buf, 4); } memcpy(cdb_val.s + offset, int_enc_buf, INT_B64_ENC_LEN); offset += INT_B64_ENC_LEN; } /* store the string values and their offsets as integers (base64 encoded) */ strs_offset = offset + c_entry->nr_strs * INT_B64_ENC_LEN; for (i = 0; i < nr_columns; i++) { val_type = VAL_TYPE(values + i); switch (val_type) { case DB_STRING: str_val.s = (char *)VAL_STRING(values + i); str_val.len = strlen(str_val.s); break; case DB_STR: str_val = VAL_STR(values + i); break; default: continue; } if (VAL_NULL(values + i)) int_val = 0; else int_val = strs_offset; memcpy(int_buf, &int_val, 4); base64encode((unsigned char *)int_enc_buf, (unsigned char *)int_buf, 4); memcpy(cdb_val.s + offset, int_enc_buf, INT_B64_ENC_LEN); offset += INT_B64_ENC_LEN; memcpy(cdb_val.s + strs_offset, str_val.s, str_val.len); strs_offset += str_val.len; } /* make sure the key is string */ val_type = VAL_TYPE(key); switch (val_type) { case DB_STRING: str_key.s = (char *)VAL_STRING(key); str_key.len = strlen(str_key.s); break; case DB_STR: str_key = VAL_STR(key); break; case DB_INT: int_key_buf = sint2str(VAL_INT(key), &int_key_len); break; case DB_BIGINT: int_val = (int)VAL_BIGINT(key); int_key_buf = sint2str(int_val, &int_key_len); break; case DB_DOUBLE: int_val = (int)VAL_DOUBLE(key); int_key_buf = sint2str(int_val, &int_key_len); break; default: LM_ERR("Unsupported type for SQL DB key column\n"); return -1; } if (int_key_len) { str_key.s = int_key_buf; str_key.len = int_key_len; } cdb_key.len = c_entry->id.len + str_key.len; cdb_key.s = pkg_malloc(cdb_key.len); if (!cdb_key.s) { LM_ERR("No more pkg memory\n"); return -1; } memcpy(cdb_key.s, c_entry->id.s, c_entry->id.len); memcpy(cdb_key.s + c_entry->id.len, str_key.s, str_key.len); if (db_hdls->cdbf.set(db_hdls->cdbcon, &cdb_key, &cdb_val, c_entry->expire) < 0) { LM_ERR("Failed to insert the values for key: %.*s in cachedb\n", str_key.len, str_key.s); return -1; } pkg_free(cdb_key.s); pkg_free(cdb_val.s); return 0; } static db_handlers_t *db_init_test_conn(cache_entry_t *c_entry) { db_handlers_t *new_db_hdls; str test_query_key_str = str_init(TEST_QUERY_STR); str cdb_test_key = str_init(CDB_TEST_KEY_STR); str cdb_test_val = str_init(CDB_TEST_VAL_STR); db_key_t query_key_col; db_val_t query_key_val; db_res_t *sql_res; str cachedb_res; unsigned int i; new_db_hdls = pkg_malloc(sizeof(db_handlers_t)); if (!new_db_hdls) { LM_ERR("No more pkg memory for db handlers\n"); return NULL; } new_db_hdls->c_entry = c_entry; new_db_hdls->db_con = 0; new_db_hdls->query_ps = NULL; new_db_hdls->cdbcon = 0; new_db_hdls->next = db_hdls_list; db_hdls_list = new_db_hdls; /* cachedb init and test connection */ if (cachedb_bind_mod(&c_entry->cachedb_url, &new_db_hdls->cdbf) < 0) { LM_ERR("Unable to bind to a cachedb database driver for URL: %.*s\n", c_entry->cachedb_url.len, c_entry->cachedb_url.s); return NULL; } /* open a test connection */ new_db_hdls->cdbcon = new_db_hdls->cdbf.init(&c_entry->cachedb_url); if (!new_db_hdls->cdbcon) { LM_ERR("Cannot init connection to cachedb: %.*s\n", c_entry->cachedb_url.len, c_entry->cachedb_url.s); return NULL; } /* setting and geting a test key in cachedb */ if (new_db_hdls->cdbf.set(new_db_hdls->cdbcon, &cdb_test_key, &cdb_test_val, 0) < 0) { LM_ERR("Failed to set test key in cachedb: %.*s\n", c_entry->cachedb_url.len, c_entry->cachedb_url.s); new_db_hdls->cdbf.destroy(new_db_hdls->cdbcon); new_db_hdls->cdbcon = 0; return NULL; } if (new_db_hdls->cdbf.get(new_db_hdls->cdbcon, &cdb_test_key, &cachedb_res) < 0) { LM_ERR("Failed to get test key from cachedb: %.*s\n", c_entry->cachedb_url.len, c_entry->cachedb_url.s); new_db_hdls->cdbf.destroy(new_db_hdls->cdbcon); new_db_hdls->cdbcon = 0; return NULL; } if (str_strcmp(&cachedb_res, &cdb_test_val) != 0) { LM_ERR("Inconsistent test key for cachedb: %.*s\n", c_entry->cachedb_url.len, c_entry->cachedb_url.s); new_db_hdls->cdbf.destroy(new_db_hdls->cdbcon); new_db_hdls->cdbcon = 0; return NULL; } /* SQL DB init and test connection */ if (db_bind_mod(&c_entry->db_url, &new_db_hdls->db_funcs) < 0) { LM_ERR("Unable to bind to a SQL database driver for URL: %.*s\n", c_entry->db_url.len, c_entry->db_url.s); return NULL; } /* open a test connection */ if ((new_db_hdls->db_con = new_db_hdls->db_funcs.init(&c_entry->db_url)) == 0) { LM_ERR("Cannot init connection to SQL DB: %.*s\n", c_entry->db_url.len, c_entry->db_url.s); return NULL; } /* verify the column names by running a test query with a bogus key */ if (new_db_hdls->db_funcs.use_table(new_db_hdls->db_con, &c_entry->table) < 0) { LM_ERR("Invalid table name: %.*s\n", c_entry->table.len, c_entry->table.s); new_db_hdls->db_funcs.close(new_db_hdls->db_con); new_db_hdls->db_con = 0; return NULL; } VAL_NULL(&query_key_val) = 0; VAL_TYPE(&query_key_val) = DB_STR; VAL_STR(&query_key_val) = test_query_key_str; query_key_col = &c_entry->key; if (new_db_hdls->db_funcs.query(new_db_hdls->db_con, &query_key_col, 0, &query_key_val, c_entry->columns, 1, c_entry->nr_columns, 0, &sql_res) != 0) { LM_ERR("Failure to issuse test query to SQL DB: %.*s\n", c_entry->db_url.len, c_entry->db_url.s); new_db_hdls->db_funcs.close(new_db_hdls->db_con); new_db_hdls->db_con = 0; return NULL; } /* no columns specified in cache entry -> cache entire table and get column names from the sql result */ if (!c_entry->columns) { c_entry->nr_columns = RES_COL_N(sql_res); c_entry->columns = shm_malloc(c_entry->nr_columns * sizeof(str*)); for (i = 0; i < c_entry->nr_columns; i++) { c_entry->columns[i] = shm_malloc(sizeof(str)); (*c_entry->columns[i]).len = RES_NAMES(sql_res)[i]->len; (*c_entry->columns[i]).s = shm_malloc((*c_entry->columns[i]).len); memcpy((*c_entry->columns[i]).s, RES_NAMES(sql_res)[i]->s, (*c_entry->columns[i]).len); } } new_db_hdls->db_funcs.free_result(new_db_hdls->db_con, sql_res); return new_db_hdls; } static int load_entire_table(cache_entry_t *c_entry, db_handlers_t *db_hdls, int reload_version) { db_key_t *query_cols = NULL; db_res_t *sql_res = NULL; db_row_t *row; db_val_t *values; int i; query_cols = pkg_malloc((c_entry->nr_columns + 1) * sizeof(db_key_t)); if (!query_cols) { LM_ERR("No more pkg memory\n"); return -1; } query_cols[0] = &(c_entry->key); for (i=0; i < c_entry->nr_columns; i++) query_cols[i+1] = &((*c_entry->columns[i])); /* query the entire table */ if (db_hdls->db_funcs.use_table(db_hdls->db_con, &c_entry->table) < 0) { LM_ERR("Invalid table name: %.*s\n", c_entry->table.len, c_entry->table.s); db_hdls->db_funcs.close(db_hdls->db_con); db_hdls->db_con = 0; return -1; } if (DB_CAPABILITY(db_hdls->db_funcs, DB_CAP_FETCH)) { if (db_hdls->db_funcs.query(db_hdls->db_con, NULL, 0, NULL, query_cols, 0, c_entry->nr_columns + 1, 0, 0) != 0) { LM_ERR("Failure to issue query to SQL DB: %.*s\n", c_entry->db_url.len, c_entry->db_url.s); goto error; } if (db_hdls->db_funcs.fetch_result(db_hdls->db_con,&sql_res,fetch_nr_rows)<0) { LM_ERR("Error fetching rows from SQL DB: %.*s\n", c_entry->db_url.len, c_entry->db_url.s); goto error; } } else { if (db_hdls->db_funcs.query(db_hdls->db_con, NULL, 0, NULL, query_cols, 0, c_entry->nr_columns + 1, 0, &sql_res) != 0) { LM_ERR("Failure to issue query to SQL DB: %.*s\n", c_entry->db_url.len, c_entry->db_url.s); goto error; } } pkg_free(query_cols); if (RES_ROW_N(sql_res) == 0) { LM_WARN("Table: %.*s is empty!\n", c_entry->table.len, c_entry->table.s); db_hdls->db_funcs.free_result(db_hdls->db_con, sql_res); return 0; } row = RES_ROWS(sql_res); values = ROW_VALUES(row); if (get_column_types(c_entry, values + 1, ROW_N(row) - 1) < 0) { LM_ERR("One ore more SQL columns have an unsupported type\n"); goto error; } /* load the rows into the cahchedb */ do { for (i=0; i < RES_ROW_N(sql_res); i++) { row = RES_ROWS(sql_res) + i; values = ROW_VALUES(row); if (!VAL_NULL(values)) if (insert_in_cachedb(c_entry, db_hdls, values ,values + 1, reload_version, ROW_N(row) - 1) < 0) return -1; } if (DB_CAPABILITY(db_hdls->db_funcs, DB_CAP_FETCH)) { if (db_hdls->db_funcs.fetch_result(db_hdls->db_con,&sql_res,fetch_nr_rows)<0) { LM_ERR("Error fetching rows (1) from SQL DB: %.*s\n", c_entry->db_url.len, c_entry->db_url.s); goto error; } } else { break; } } while (RES_ROW_N(sql_res) > 0); db_hdls->db_funcs.free_result(db_hdls->db_con, sql_res); return 0; error: if (sql_res) db_hdls->db_funcs.free_result(db_hdls->db_con, sql_res); return -1; } /* return: * 0 - succes * -1 - error * -2 - not found in sql db */ static int load_key(cache_entry_t *c_entry, db_handlers_t *db_hdls, str key, db_val_t **values, db_res_t **sql_res) { db_key_t key_col; db_row_t *row; db_val_t key_val; str src_key, null_val; src_key.len = c_entry->id.len + key.len; src_key.s = pkg_malloc(src_key.len); if (!src_key.s) { LM_ERR("No more shm memory\n"); return -1; } memcpy(src_key.s, c_entry->id.s, c_entry->id.len); memcpy(src_key.s + c_entry->id.len, key.s, key.len); key_col = &(c_entry->key); VAL_NULL(&key_val) = 0; VAL_TYPE(&key_val) = DB_STR; VAL_STR(&key_val) = key; if (db_hdls->db_funcs.use_table(db_hdls->db_con, &c_entry->table) < 0) { LM_ERR("Invalid table name: %.*s\n", c_entry->table.len, c_entry->table.s); db_hdls->db_funcs.close(db_hdls->db_con); db_hdls->db_con = 0; return -1; } CON_PS_REFERENCE(db_hdls->db_con) = &db_hdls->query_ps; if (db_hdls->db_funcs.query(db_hdls->db_con, &key_col, 0, &key_val, c_entry->columns, 1, c_entry->nr_columns, 0, sql_res) != 0) { LM_ERR("Failure to issue query to SQL DB: %.*s\n", c_entry->db_url.len, c_entry->db_url.s); goto sql_error; } if (RES_ROW_N(*sql_res) == 0) { LM_WARN("key %.*s not found in SQL db\n", key.len, key.s); null_val.len = 0; null_val.s = NULL; if (db_hdls->cdbf.set(db_hdls->cdbcon, &src_key, &null_val, c_entry->expire) < 0) { LM_ERR("Failed to insert null in cachedb\n"); pkg_free(src_key.s); goto sql_error; } pkg_free(src_key.s); db_hdls->db_funcs.free_result(db_hdls->db_con, *sql_res); return -2; } else if (RES_ROW_N(*sql_res) > 1) { LM_ERR("To many columns returned\n"); goto sql_error; } row = RES_ROWS(*sql_res); *values = ROW_VALUES(row); if (c_entry->nr_ints + c_entry->nr_strs == 0 && get_column_types(c_entry, *values, ROW_N(row)) < 0) { LM_ERR("SQL column has unsupported type\n"); goto sql_error; } if (insert_in_cachedb(c_entry, db_hdls, &key_val, *values, 0, ROW_N(row)) < 0) return -1; return 0; sql_error: if (*sql_res) db_hdls->db_funcs.free_result(db_hdls->db_con, *sql_res); return -1; } void reload_timer(unsigned int ticks, void *param) { cache_entry_t *c_entry; db_handlers_t *db_hdls; str rld_vers_key; int rld_vers = 0; for (c_entry = *entry_list, db_hdls = db_hdls_list; c_entry; c_entry = c_entry->next, db_hdls = db_hdls->next) { if (c_entry->on_demand) continue; rld_vers_key.len = c_entry->id.len + 23; rld_vers_key.s = pkg_malloc(rld_vers_key.len); if (!rld_vers_key.s) { LM_ERR("No more pkg memory\n"); return; } memcpy(rld_vers_key.s, c_entry->id.s, c_entry->id.len); memcpy(rld_vers_key.s + c_entry->id.len, "_sql_cacher_reload_vers", 23); lock_start_write(c_entry->ref_lock); if(db_hdls->cdbf.get_counter(db_hdls->cdbcon, &rld_vers_key, &rld_vers) < 0) { LM_ERR("Failed to get reload version integer from cachedb\n"); pkg_free(rld_vers_key.s); continue; } if (load_entire_table(c_entry, db_hdls, rld_vers) < 0) LM_ERR("Failed to reload table %.*s\n", c_entry->table.len, c_entry->table.s); lock_stop_write(c_entry->ref_lock); pkg_free(rld_vers_key.s); } } static struct mi_root* mi_reload(struct mi_root *root, void *param) { struct mi_node *node; cache_entry_t *c_entry; db_handlers_t *db_hdls; db_val_t *values; db_res_t *sql_res = NULL; struct queried_key *it; str entry_id, key, src_key; str rld_vers_key; int rld_vers = 0, rc; /* cache entry id */ node = root->node.kids; if (!node || !node->value.len || !node->value.s) { LM_ERR("no parameters received\n"); return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM)); } entry_id = node->value; for (c_entry = *entry_list, db_hdls = db_hdls_list; c_entry; c_entry = c_entry->next, db_hdls = db_hdls->next) if (!memcmp(entry_id.s, c_entry->id.s, entry_id.len)) break; if (!c_entry) { LM_ERR("Entry %.*s not found\n", entry_id.len, entry_id.s); return init_mi_tree(500, MI_SSTR("ERROR Cache entry not found\n")); } /* key */ node = node->next; if (c_entry->on_demand) { if (!node || !node->value.len || !node->value.s) { LM_ERR("missing key parameter\n"); return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM)); } key = node->value; } if (c_entry->on_demand) { src_key.len = c_entry->id.len + key.len; src_key.s = pkg_malloc(src_key.len); if (!src_key.s) { LM_ERR("No more shm memory\n"); return NULL; } memcpy(src_key.s, c_entry->id.s, c_entry->id.len); memcpy(src_key.s + c_entry->id.len, key.s, key.len); lock_get(queries_lock); for (it = *queries_in_progress; it; it = it->next) if (!memcmp(it->key.s, src_key.s, src_key.len)) break; pkg_free(src_key.s); if (it) { /* key is in list */ lock_release(queries_lock); lock_get(it->wait_sql_query); } rc = load_key(c_entry, db_hdls, key, &values, &sql_res); if (rc == 0) db_hdls->db_funcs.free_result(db_hdls->db_con, sql_res); if (it) lock_release(it->wait_sql_query); else lock_release(queries_lock); if (rc == -1) return init_mi_tree(500, MI_SSTR("ERROR Reloading key from SQL database\n")); else if (rc == -2) return init_mi_tree(500, MI_SSTR("ERROR Reloading key from SQL database, key not found\n")); } else { rld_vers_key.len = c_entry->id.len + 23; rld_vers_key.s = pkg_malloc(rld_vers_key.len); if (!rld_vers_key.s) { LM_ERR("No more pkg memory\n"); return NULL; } memcpy(rld_vers_key.s, c_entry->id.s, c_entry->id.len); memcpy(rld_vers_key.s + c_entry->id.len, "_sql_cacher_reload_vers", 23); lock_start_write(c_entry->ref_lock); if (db_hdls->cdbf.add(db_hdls->cdbcon, &rld_vers_key, 1, 0, &rld_vers) < 0) { LM_DBG("Failed to increment reload version integer from cachedb\n"); return init_mi_tree(500, MI_SSTR("ERROR Reloading SQL database\n")); } pkg_free(rld_vers_key.s); if (load_entire_table(c_entry, db_hdls, rld_vers) < 0) { LM_DBG("Failed to reload table\n"); return init_mi_tree(500, MI_SSTR("ERROR Reloading SQL database\n")); } lock_stop_write(c_entry->ref_lock); } return init_mi_tree(200, MI_SSTR(MI_OK_S)); } static int mod_init(void) { cache_entry_t *c_entry; db_handlers_t *db_hdls; char use_timer = 0, entry_success = 0; str rld_vers_key; int reload_version = -1; if (full_caching_expire <= 0) { full_caching_expire = DEFAULT_FULL_CACHING_EXPIRE; LM_WARN("Invalid full_caching_expire parameter, " "setting default value: %d sec\n", DEFAULT_FULL_CACHING_EXPIRE); } if (reload_interval <= 0 || reload_interval >= full_caching_expire) { reload_interval = DEFAULT_RELOAD_INTERVAL; LM_WARN("Invalid reload_interval parameter, " "setting default value: %d sec\n", DEFAULT_RELOAD_INTERVAL); } if(!entry_list){ entry_list = shm_malloc(sizeof(cache_entry_t*)); if (!entry_list) { LM_ERR("No more memory for cache entries list\n"); return -1; } *entry_list = NULL; } queries_in_progress = shm_malloc(sizeof(struct queried_key *)); if (!queries_in_progress) { LM_ERR("No more memory for queries_in_progress list\n"); return -1; } *queries_in_progress = NULL; queries_lock = lock_alloc(); if (!queries_lock) { LM_ERR("No more memory for queries_lock\n"); return -1; } if (!lock_init(queries_lock)) { LM_ERR("Failed to init queries_lock\n"); return -1; } for (c_entry = *entry_list; c_entry; c_entry = c_entry->next) { if ((db_hdls = db_init_test_conn(c_entry)) == NULL) continue; /* cache the entire table if on demand is not set*/ if (!c_entry->on_demand) { use_timer = 1; c_entry->expire = full_caching_expire; c_entry->ref_lock = lock_init_rw(); if (!c_entry->ref_lock) { LM_ERR("Failed to init readers-writers lock\n"); continue; } if (load_entire_table(c_entry, db_hdls, 0) < 0) LM_ERR("Failed to cache the entire table: %s\n", c_entry->table.s); else { /* set up reload version counter for this entry in cachedb */ rld_vers_key.len = c_entry->id.len + 23; rld_vers_key.s = pkg_malloc(rld_vers_key.len); if (!rld_vers_key.s) { LM_ERR("No more pkg memory\n"); return -1; } memcpy(rld_vers_key.s, c_entry->id.s, c_entry->id.len); memcpy(rld_vers_key.s + c_entry->id.len, "_sql_cacher_reload_vers", 23); db_hdls->cdbf.add(db_hdls->cdbcon, &rld_vers_key, 1, 0, &reload_version); db_hdls->cdbf.sub(db_hdls->cdbcon, &rld_vers_key, 1, 0, &reload_version); if (reload_version != 0) LM_ERR("Failed to set up reload version counter in cahchedb for " "entry %.*s\n", c_entry->id.len, c_entry->id.s); else entry_success = 1; LM_DBG("Cached the entire table %s\n", c_entry->table.s); } } else entry_success = 1; db_hdls->db_funcs.close(db_hdls->db_con); db_hdls->db_con = 0; db_hdls->cdbf.destroy(db_hdls->cdbcon); db_hdls->cdbcon = 0; } if (!entry_success) { LM_ERR("Failed to set up any cache entry\n"); return -1; } if (use_timer && register_timer("sql_cacher_reload-timer", reload_timer, NULL, full_caching_expire - reload_interval, TIMER_FLAG_DELAY_ON_DELAY) < 0) { LM_ERR("failed to register timer\n"); return -1; } return 0; } static int child_init(int rank) { db_handlers_t *db_hdls; cache_entry_t *c_entry; for (db_hdls = db_hdls_list, c_entry = *entry_list; db_hdls; db_hdls = db_hdls->next, c_entry = c_entry->next) { db_hdls->cdbcon = db_hdls->cdbf.init(&c_entry->cachedb_url); if (!db_hdls->cdbcon) { LM_ERR("Cannot connect to cachedb from child\n"); return -1; } if ((db_hdls->db_con = db_hdls->db_funcs.init(&c_entry->db_url)) == 0) { LM_ERR("Cannot connect to SQL DB from child\n"); return -1; } } return 0; } /* return: * 1 - if found * -2 - if not found * -1 - if error */ static int cdb_fetch(pv_name_fix_t *pv_name, str *cdb_res, int *entry_rld_vers) { str cdb_key; str rld_vers_key; int rc; cdb_key.len = pv_name->id.len + pv_name->key.len; cdb_key.s = pkg_malloc(cdb_key.len); if (!cdb_key.s) { LM_ERR("No more pkg memory\n"); return -1; } memcpy(cdb_key.s, pv_name->id.s, pv_name->id.len); memcpy(cdb_key.s + pv_name->id.len, pv_name->key.s, pv_name->key.len); if (!pv_name->c_entry->on_demand) { rld_vers_key.len = pv_name->id.len + 23; rld_vers_key.s = pkg_malloc(rld_vers_key.len); if (!rld_vers_key.s) { LM_ERR("No more pkg memory\n"); return -1; } memcpy(rld_vers_key.s, pv_name->id.s, pv_name->id.len); memcpy(rld_vers_key.s + pv_name->id.len, "_sql_cacher_reload_vers", 23); if(pv_name->db_hdls->cdbf.get_counter(pv_name->db_hdls->cdbcon, &rld_vers_key, entry_rld_vers) < 0) return -1; pkg_free(rld_vers_key.s); } else *entry_rld_vers = 0; rc = pv_name->db_hdls->cdbf.get(pv_name->db_hdls->cdbcon, &cdb_key, cdb_res); pkg_free(cdb_key.s); return rc; } /* return: * 0 - succes * 1 - succes, null value in db * -1 - error * -2 - does not match reload version (old value) */ static int cdb_val_decode(pv_name_fix_t *pv_name, str *cdb_val, int reload_version, str *str_res, int *int_res) { long long one = 1; int int_val, next_str_off, i, rc; char int_buf[4]; const char zeroes[INT_B64_ENC_LEN] = {0}; if (pv_name->col_offset == -1) { LM_WARN("Unknown column %.*s\n", pv_name->col.len, pv_name->col.s); return -1; } if (!pv_name->c_entry->on_demand) { /* decode the reload version */ if (base64decode((unsigned char *)int_buf, (unsigned char *)(cdb_val->s), INT_B64_ENC_LEN) != 4) goto error; memcpy(&int_val, int_buf, 4); if (reload_version != int_val) return -2; } /* null integer value in db */ if (!memcmp(cdb_val->s + pv_name->col_offset, zeroes, INT_B64_ENC_LEN)) return 1; /* decode the integer value or the offset of the string value */ if (base64decode((unsigned char *)int_buf, (unsigned char *)(cdb_val->s + pv_name->col_offset), INT_B64_ENC_LEN) != 4) goto error; memcpy(&int_val, int_buf, 4); if ((pv_name->c_entry->column_types & (one << pv_name->col_nr)) != 0) { /* null string value in db */ if (int_val == 0) return 1; str_res->s = cdb_val->s + int_val; if (pv_name->last_str) str_res->len = cdb_val->len - int_val; else { /* calculate the length of the current string using the offset of the next not null string */ i = 1; do { rc = base64decode((unsigned char *)int_buf, (unsigned char *)(cdb_val->s + pv_name->col_offset + i * INT_B64_ENC_LEN), INT_B64_ENC_LEN); if (rc != 4) goto error; memcpy(&next_str_off, int_buf, 4); i++; } while (next_str_off == 0 && pv_name->col_offset + i*INT_B64_ENC_LEN < (pv_name->c_entry->nr_columns + 1) * INT_B64_ENC_LEN); if (next_str_off == 0) str_res->len = cdb_val->len - int_val; else str_res->len = next_str_off - int_val; } } else { *int_res = int_val; } return 0; error: LM_ERR("Failed to decode value: %.*s from cachedb\n", cdb_val->len, cdb_val->s); return -1; } static void optimize_cdb_decode(pv_name_fix_t *pv_name) { int i, j, prev_cols; char col_type1, col_type2; long long one = 1; for (i = 0; i < pv_name->c_entry->nr_columns; i++) { if (!memcmp((*pv_name->c_entry->columns[i]).s, pv_name->col.s, pv_name->col.len)) { pv_name->col_nr = i; prev_cols = 0; col_type1 = ((pv_name->c_entry->column_types & (one << i)) != 0); for (j = 0; j < i; j++) { col_type2 = ((pv_name->c_entry->column_types & (one << j)) != 0); if (col_type1 == col_type2) prev_cols++; } if (col_type1) { pv_name->col_offset = INT_B64_ENC_LEN + pv_name->c_entry->nr_ints*INT_B64_ENC_LEN + prev_cols*INT_B64_ENC_LEN; if (prev_cols == pv_name->c_entry->nr_strs - 1) pv_name->last_str = 1; else pv_name->last_str = 0; } else pv_name->col_offset = INT_B64_ENC_LEN + prev_cols*INT_B64_ENC_LEN; break; } } if (i == pv_name->c_entry->nr_columns) pv_name->col_offset = -1; } /* return: * 0 - succes * 1 - succes, null value in db * -1 - error * -2 - not found in sql db */ static int on_demand_load(pv_name_fix_t *pv_name, str *cdb_res, str *str_res, int *int_res) { struct queried_key *it, *prev = NULL, *tmp, *new_key; str src_key; db_res_t *sql_res = NULL; db_val_t *values; db_type_t val_type; int i, rld_vers_dummy, rc; for (i = 0; i < pv_name->c_entry->nr_columns; i++) if (!memcmp((*pv_name->c_entry->columns[i]).s, pv_name->col.s, pv_name->col.len)) { pv_name->col_nr = i; break; } if (i == pv_name->c_entry->nr_columns) { LM_WARN("Unknown column %.*s\n", pv_name->col.len, pv_name->col.s); return -1; } src_key.len = pv_name->id.len + pv_name->key.len; src_key.s = shm_malloc(src_key.len); if (!src_key.s) { LM_ERR("No more shm memory\n"); return -1; } memcpy(src_key.s, pv_name->id.s, pv_name->id.len); memcpy(src_key.s + pv_name->id.len, pv_name->key.s, pv_name->key.len); lock_get(queries_lock); it = *queries_in_progress; while (it) { if (!memcmp(it->key.s, src_key.s, src_key.len)) { /* key is in list */ it->nr_waiting_procs++; lock_release(queries_lock); /* wait for the query to complete */ lock_get(it->wait_sql_query); lock_get(queries_lock); shm_free(src_key.s); if (it->nr_waiting_procs == 1) { lock_release(it->wait_sql_query); lock_destroy(it->wait_sql_query); lock_dealloc(it->wait_sql_query); /* if this is the last process waiting, delete key from list */ if (prev) prev->next = it->next; else *queries_in_progress = it->next; tmp = it; it = it->next; shm_free(tmp); } else if (it->nr_waiting_procs > 1) { it->nr_waiting_procs--; lock_release(it->wait_sql_query); } lock_release(queries_lock); /* reload key from cachedb */ if (cdb_fetch(pv_name, cdb_res, &rld_vers_dummy) < 0) { LM_ERR("Error or missing value on retrying fetch from cachedb\n"); return -1; } if (pv_name->last_str == -1) optimize_cdb_decode(pv_name); return cdb_val_decode(pv_name, cdb_res, 0, str_res, int_res); } else { it = it->next; } prev = it; } if (!it) { /* if key not found in list */ /* insert key in list */ new_key = shm_malloc(sizeof(struct queried_key)); if (!new_key) { LM_ERR("No more shm memory\n"); lock_release(queries_lock); return -1; } new_key->key = src_key; new_key->nr_waiting_procs = 0; new_key->wait_sql_query = lock_alloc(); if (!new_key->wait_sql_query) { LM_ERR("No more memory for wait_sql_query lock\n"); lock_release(queries_lock); return -1; } if (!lock_init(new_key->wait_sql_query)) { LM_ERR("Failed to init wait_sql_query lock\n"); lock_release(queries_lock); return -1; } new_key->next = NULL; if (*queries_in_progress) new_key->next = *queries_in_progress; *queries_in_progress = new_key; lock_get(new_key->wait_sql_query); lock_release(queries_lock); rc = load_key(pv_name->c_entry, pv_name->db_hdls, pv_name->key, &values, &sql_res); if (rc) { lock_release(new_key->wait_sql_query); return rc; } lock_get(queries_lock); lock_release(new_key->wait_sql_query); /* delete key from list */ if (new_key->nr_waiting_procs == 0) { lock_destroy(new_key->wait_sql_query); lock_dealloc(new_key->wait_sql_query); *queries_in_progress = new_key->next; shm_free(new_key->key.s); shm_free(new_key); } lock_release(queries_lock); if (VAL_NULL(values + pv_name->col_nr)) return 1; val_type = VAL_TYPE(values + pv_name->col_nr); switch (val_type) { case DB_STRING: str_res->s = (char *)VAL_STRING(values + pv_name->col_nr); str_res->len = strlen(str_res->s); break; case DB_STR: str_res = &(VAL_STR(values + pv_name->col_nr)); break; case DB_INT: *int_res = VAL_INT(values + pv_name->col_nr); break; case DB_BIGINT: *int_res = (int)VAL_BIGINT(values + pv_name->col_nr); break; case DB_DOUBLE: *int_res = (int)VAL_DOUBLE(values + pv_name->col_nr); break; default: LM_ERR("Unsupported type for SQL column\n"); return -1; } pv_name->db_hdls->db_funcs.free_result(pv_name->db_hdls->db_con, sql_res); return 0; } return -1; } static int parse_pv_name_s(pv_name_fix_t *pv_name, str *name_s) { char *p1 = NULL, *p2 = NULL; char last; #define PARSE_TOKEN(_ptr1, _ptr2, type, delim) \ do { \ (_ptr2) = memchr((_ptr1), (delim), \ name_s->len - ((_ptr1) - name_s->s) + 1); \ if (!(_ptr2)) { \ LM_ERR("Invalid syntax for pvar name\n"); \ return -1; \ } \ int _prev_len = pv_name->type.len; \ pv_name->type.len = (_ptr2) - (_ptr1); \ if (!pv_name->type.s) { \ pv_name->type.s = pkg_malloc(pv_name->type.len); \ if (!pv_name->type.s) { \ LM_ERR("No more pkg memory\n"); \ return -1; \ } \ memcpy(pv_name->type.s, (_ptr1), pv_name->type.len); \ } else if (memcmp(pv_name->type.s, (_ptr1), pv_name->type.len)) { \ if (_prev_len != pv_name->type.len) { \ pv_name->type.s = pkg_realloc(pv_name->type.s, pv_name->type.len); \ if (!pv_name->type.s) { \ LM_ERR("No more pkg memory\n"); \ return -1; \ } \ } \ memcpy(pv_name->type.s, (_ptr1), pv_name->type.len); \ } \ } while (0) last = name_s->s[name_s->len]; p1 = name_s->s; PARSE_TOKEN(p1, p2, id, pvar_delimiter.s[0]); p1 = p2 + 1; PARSE_TOKEN(p1, p2, col, pvar_delimiter.s[0]); p1 = p2 + 1; PARSE_TOKEN(p1, p2, key, last); #undef PARSE_TOKEN return 0; } int pv_parse_name(pv_spec_p sp, str *in) { pv_elem_t *model = NULL, *it; pv_name_fix_t *pv_name; if (!in || !in->s || !sp) return -1; pv_name = pkg_malloc(sizeof(pv_name_fix_t)); if (!pv_name) { LM_ERR("No more pkg memory\n"); return -1; } pv_name->id.s = NULL; pv_name->id.len = 0; pv_name->col.s = NULL; pv_name->col.len = 0; pv_name->key.s = NULL; pv_name->key.len = 0; pv_name->c_entry = NULL; pv_name->pv_elem_list = NULL; pv_name->col_offset = -1; pv_name->last_str = -1; sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void *)pv_name; if (pv_parse_format(in, &model) < 0) { LM_ERR("Wrong format for pvar name\n"); return -1; } for (it = model; it; it = it->next) { if (it->spec.type != PVT_NONE) break; } if (it) { /* if there are variables in the name, parse later */ pv_name->pv_elem_list = model; } else { if (parse_pv_name_s(pv_name, &(model->text)) < 0) return -1; } return 0; } int pv_get_sql_cached_value(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { pv_name_fix_t *pv_name; str name_s; cache_entry_t *it_entries; db_handlers_t *it_db; int rc, rc2, int_res = 0, l = 0; char *ch = NULL; long long one = 1; str str_res = {NULL, 0}, cdb_res; int entry_rld_vers; if (!param || param->pvn.type != PV_NAME_PVAR || !param->pvn.u.dname) { LM_CRIT("Bad pvar get function parameters\n"); return -1; } pv_name = (pv_name_fix_t *)param->pvn.u.dname; if (!pv_name) { LM_ERR("Unable to get name struct from dname\n"); return -1; } if (pv_name->pv_elem_list) { /* there are variables in the name which need to be evaluated, then parse */ if (pv_printf_s(msg, pv_name->pv_elem_list, &name_s) != 0 || name_s.len == 0 || !name_s.s) { LM_ERR("Unable to evaluate variables in pv name"); return pv_get_null(msg, param, res); } if (parse_pv_name_s(pv_name, &name_s) < 0) return pv_get_null(msg, param, res); } if (!pv_name->c_entry) { for (it_entries = *entry_list, it_db = db_hdls_list; it_entries; it_entries = it_entries->next, it_db = it_db->next) if (!memcmp(it_entries->id.s, pv_name->id.s, pv_name->id.len)) { pv_name->c_entry = it_entries; pv_name->db_hdls = it_db; break; } if (!it_entries) { LM_WARN("Unknown caching id %.*s\n", pv_name->id.len, pv_name->id.s); return pv_get_null(msg, param, res); } } if (!pv_name->c_entry->on_demand) lock_start_read(pv_name->c_entry->ref_lock); rc = cdb_fetch(pv_name, &cdb_res, &entry_rld_vers); if (rc == -1) { LM_ERR("Error fetching from cachedb\n"); return pv_get_null(msg, param, res); } if (!pv_name->c_entry->on_demand) { if (rc == -2) { LM_WARN("key %.*s not found in SQL db\n", pv_name->key.len, pv_name->key.s); lock_stop_read(pv_name->c_entry->ref_lock); return pv_get_null(msg, param, res); } else { if (pv_name->last_str == -1) optimize_cdb_decode(pv_name); rc2 = cdb_val_decode(pv_name, &cdb_res, entry_rld_vers, &str_res, &int_res); lock_stop_read(pv_name->c_entry->ref_lock); if (rc2 == -1) return pv_get_null(msg, param, res); if (rc2 == -2) { LM_WARN("key %.*s not found in SQL db\n", pv_name->key.len, pv_name->key.s); return pv_get_null(msg, param, res); } if (rc2 == 1) { LM_WARN("NULL value in SQL db\n"); return pv_get_null(msg, param, res); } } } else { if (rc == -2) { rc2 = on_demand_load(pv_name, &cdb_res, &str_res, &int_res); if (rc2 == -1 || rc2 == -2) return pv_get_null(msg, param, res); if (rc2 == 1) { LM_WARN("NULL value in SQL db\n"); return pv_get_null(msg, param, res); } } else { if (!cdb_res.len || !cdb_res.s) { LM_WARN("key %.*s not found in SQL db\n", pv_name->key.len, pv_name->key.s); return pv_get_null(msg, param, res); } if (pv_name->last_str == -1) optimize_cdb_decode(pv_name); rc2 = cdb_val_decode(pv_name, &cdb_res, 0, &str_res, &int_res); if (rc2 == -1) return pv_get_null(msg, param, res); if (rc2 == 1) { LM_WARN("NULL value in SQL db\n"); return pv_get_null(msg, param, res); } } } if ((pv_name->c_entry->column_types & (one << pv_name->col_nr)) != 0) { res->flags = PV_VAL_STR; res->rs.s = str_res.s; res->rs.len = str_res.len; } else { res->ri = int_res; ch = int2str(int_res, &l); res->rs.s = ch; res->rs.len = l; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; } return 0; } static void destroy(void) { db_handlers_t *db_hdls; struct queried_key *q_it, *q_tmp; cache_entry_t *c_it, *c_tmp; int i; for(db_hdls = db_hdls_list; db_hdls; db_hdls = db_hdls->next) { if (db_hdls->cdbcon) db_hdls->cdbf.destroy(db_hdls->cdbcon); if (db_hdls->db_con) db_hdls->db_funcs.close(db_hdls->db_con); } q_it = *queries_in_progress; while (q_it) { q_tmp = q_it; q_it = q_it->next; lock_destroy(q_tmp->wait_sql_query); lock_dealloc(q_tmp->wait_sql_query); shm_free(q_tmp->key.s); shm_free(q_tmp); } shm_free(queries_in_progress); c_it = *entry_list; while (c_it) { c_tmp = c_it; c_it = c_it->next; shm_free(c_tmp->id.s); shm_free(c_tmp->db_url.s); shm_free(c_tmp->cachedb_url.s); shm_free(c_tmp->table.s); shm_free(c_tmp->key.s); for (i = 0; i < c_tmp->nr_columns; i++) { shm_free((*c_tmp->columns[i]).s); shm_free(c_tmp->columns[i]); } shm_free(c_tmp->columns); lock_destroy_rw(c_tmp->ref_lock); shm_free(c_tmp); } shm_free(entry_list); lock_destroy(queries_lock); lock_dealloc(queries_lock); } opensips-2.2.2/modules/sql_cacher/sql_cacher.h000066400000000000000000000056631300170765700214260ustar00rootroot00000000000000/** * * Copyright (C) 2015 OpenSIPS Foundation * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2015-09-xx initial version (Vlad Patrascu) */ #ifndef _SQL_CACHER_H_ #define _SQL_CACHER_H_ #include "../../db/db.h" #include "../../cachedb/cachedb.h" #define DEFAULT_SPEC_DELIM "\n" #define DEFAULT_COLUMNS_DELIM " " #define DEFAULT_PVAR_DELIM ":" #define ID_STR "id" #define ID_STR_LEN ((int)(sizeof(ID_STR) - 1)) #define DB_URL_STR "db_url" #define DB_URL_LEN ((int)(sizeof(DB_URL_STR) - 1)) #define CACHEDB_URL_STR "cachedb_url" #define CACHEDB_URL_LEN ((int)(sizeof(CACHEDB_URL_STR) - 1)) #define TABLE_STR "table" #define TABLE_STR_LEN ((int)(sizeof(TABLE_STR) - 1)) #define KEY_STR "key" #define KEY_STR_LEN ((int)(sizeof(KEY_STR) - 1)) #define COLUMNS_STR "columns" #define COLUMNS_STR_LEN ((int)(sizeof(COLUMNS_STR) - 1)) #define ONDEMAND_STR "on_demand" #define ONDEMAND_STR_LEN ((int)(sizeof(ONDEMAND_STR) - 1)) #define EXPIRE_STR "expire" #define EXPIRE_STR_LEN ((int)(sizeof(EXPIRE_STR) - 1)) #define DEFAULT_ON_DEMAND_EXPIRE 3600 #define DEFAULT_FULL_CACHING_EXPIRE 86400 /* 24h */ #define DEFAULT_RELOAD_INTERVAL 60 #define DEFAULT_FETCH_NR_ROWS 100 #define TEST_QUERY_STR "sql_cacher_test_query_key" #define CDB_TEST_KEY_STR "sql_cacher_cdb_test_key" #define CDB_TEST_VAL_STR "sql_cacher_cdb_test_val" #define INT_B64_ENC_LEN 8 typedef struct _cache_entry { str id; str db_url; str cachedb_url; str table; str key; str **columns; unsigned int nr_columns; unsigned int on_demand; unsigned int expire; unsigned int nr_ints, nr_strs; long long column_types; rw_lock_t *ref_lock; struct _cache_entry *next; } cache_entry_t; typedef struct _db_handlers { cache_entry_t *c_entry; db_func_t db_funcs; db_con_t *db_con; db_ps_t query_ps; cachedb_funcs cdbf; cachedb_con *cdbcon; struct _db_handlers *next; } db_handlers_t; struct parse_entry { str to_parse_str; struct parse_entry *next; }; struct queried_key { str key; int nr_waiting_procs; gen_lock_t *wait_sql_query; struct queried_key *next; }; typedef struct _pv_name_fix { str id; str col; str key; cache_entry_t *c_entry; db_handlers_t *db_hdls; pv_elem_t *pv_elem_list; int col_offset; int col_nr; char last_str; } pv_name_fix_t; #endifopensips-2.2.2/modules/sst/000077500000000000000000000000001300170765700156445ustar00rootroot00000000000000opensips-2.2.2/modules/sst/Makefile000066400000000000000000000020411300170765700173010ustar00rootroot00000000000000# # $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile # # Copyright (C) 2006 SOMA Networks, INC. # Written by: Ron Winacott # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # History: # -------- # 2006-05-11 initial version (ronw) # include ../../Makefile.defs auto_gen= NAME=sst.so include ../../Makefile.modules opensips-2.2.2/modules/sst/README000066400000000000000000000215021300170765700165240ustar00rootroot00000000000000SST Module (SIP Session Timer) Ron Winacott SOMA Networks, Inc. Edited by Ron Winacott Copyright © 2006 SOMA Networks, Inc. Revision History Revision $Revision: 5901 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. How it works 1.3. Dependencies 1.3.1. OpenSIPS Modules 1.3.2. External Libraries or Applications 1.4. Exported Parameters 1.4.1. enable_stats (integer) 1.4.2. min_se (integer) 1.4.3. sst_interval (integer) 1.4.4. reject_to_small (integer) 1.4.5. sst_flag (string/integer) 1.5. Exported Functions 1.5.1. sstCheckMin(send_reply_flag) 1.6. Exported Statistics 1.6.1. expired_sst List of Examples 1.1. Session timer call flow 1.2. Set enable_stats parameter 1.3. Set min_se parameter 1.4. Set sst_interval parameter 1.5. Set reject_to_small parameter 1.6. Set sst_flag parameter 1.7. sstCheckMin usage Chapter 1. Admin Guide 1.1. Overview The sst module provides a way to update the dialog expire timer based on the SIP INVITE/200 OK Session-Expires header value. You can use the sst module in an OpenSIPS proxy to allow freeing of local resources of dead (expired) calls. You can also use the sst module to validate the MIN_SE header value and reply to any request with a "422 - Session Timer Too Small" if the value is too small for your OpenSIPS configuration. 1.2. How it works The sst module uses the dialog module to be notified of any new or updated dialogs. It will then look for and extract the session-expire: header value (if there is one) and override the dialog expire timer value for the current context dialog. You flag any call setup INVITE that you want to cause a timed session to be established. This will cause OpenSIPS to request the use of session times if the UAC does not request it. All of this happens with a properly configured dialog and sst module and setting the dialog flag and the sst flag at the time any INVITE sip message is seen. There is no opensips.cfg script function call required to set the dialog expire timeout value. See the dialog module users guide for more information. The sstCheckMin() script function can be used to varify the Session-expires / MIN-SE header field values are not too small for a proxy. If the SST min_se parameter value is smaller then the messages Session-Expires / MIN-SE values, the test will return true. You can also configure the function to send the 422 response for you. The following was taken from the RFC as a call flow example: Example 1.1. Session timer call flow +-------+ +-------+ +-------+ | UAC-1 | | PROXY | | UAC-2 | +-------+ +-------+ +-------+ |(1) INVITE | | |SE: 50 | | |----------->| | | |(2)sstCheckMin | | |-----+ | | | | | | |<----+ | |(3) 422 | | |MSE:1800 | | |<-----------| | | | | |(4)ACK | | |----------->| | | | | |(5) INVITE | | |SE: 1800 | | |MSE: 1800 | | |----------->| | | |(6)sstCheckMin | | |-----+ | | | | | | |<----+ | | |(7)setflag | | |Dialog flag | | |Set expire | | |-----+ | | | | | | |<----+ | | | | | |(8)INVITE | | |SE: 1800 | | |MSE: 1800 | | |-------------->| | | | ... 1.3. Dependencies 1.3.1. OpenSIPS Modules The following modules must be loaded before this module: * dialog - dialog module and its decencies. (tm) * sl - stateless module. 1.3.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.4. Exported Parameters 1.4.1. enable_stats (integer) If the statistics support should be enabled or not. Via statistic variables, the module provide information about the dialog processing. Set it to zero to disable or to non-zero to enable it. Default value is “1†(enabled). Example 1.2. Set enable_stats parameter ... modparam("sst", "enable_stats", 0) ... 1.4.2. min_se (integer) The value is used to set the proxies MIN-SE value and is used in the 422 reply as the proxies MIN-SE: header value if the sstCheckMin() flag is set to true and the check fails. If not set and sstCheckMin() is called with the send-reply flag set to true, the default 1800 seconds will be used as the compare and the MIN-SE: header value if the 422 reply is sent. Default value is “1800†seconds. Example 1.3. Set min_se parameter ... modparam("sst", "min_se", 2400) ... 1.4.3. sst_interval (integer) The sst minimum interval in Session-Expires header if OpenSIPS request the use of session times. The used value will be the maximum value between OpenSIPS minSE, UAS minSE and this value. Per default the interval used will be the min_se value Default value is “0†seconds. Example 1.4. Set sst_interval parameter ... modparam("sst", "sst_interval", 2400) ... 1.4.4. reject_to_small (integer) In the initial INVITE if the UAC has requested a Session-Expire: and it's value is smaller then our local policies Min-SE (see min_se above), then the PROXY has the right to reject the call by replying to the message with a 422 Session Timer Too Small and state our local Min-SE: value. The INVITE is NOT forwarded on through the PROXY. This flag if true will tell the SST module to reject the INVITE with a 422 response. If false, the INVITE is forwarded through the PROXY with out any modifications. Default value is “1†(true/on). Example 1.5. Set reject_to_small parameter ... modparam("sst", "reject_to_small", 0) ... 1.4.5. sst_flag (string/integer) Keeping with OpenSIPS, the module will not do anything to any message unless instructed to do so via the opensips.cfg script. You must set the sst_flag value in the setflag() call of the INVITE you want the sst module to process. But before you can do that, you need to tell the sst module which flag value you are assigning to sst. In most cases when ever you create a new dialog via create_dialog() function,you will want to set the sst flag. If create_dialog() is not called and the sst flag is set, it will not have any effect. This parameter must be set of the module will not load. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is “Not set!â€. Example 1.6. Set sst_flag parameter ... modparam("sst", "sst_flag", "SST_FLAG") ... route { ... if (method=="INVITE") { setflag(SST_FLAG); # Set the sst flag create_dialog(); # and then create the dialog } ... } 1.5. Exported Functions 1.5.1. sstCheckMin(send_reply_flag) Check the current Session-Expires / MIN-SE values against the sst_min_se parameter value. If the Session-Expires or MIN_SE header value is less then modules minimum value, this function will return true. If the fuction is called with the send_reply_flag set to true (1) and the requested Session-Expires / MIN-SE values are too small, a 422 reply will be sent for you. The 422 will carry a MIN-SE: header with the sst min_se parameter value set. Meaning of the parameters is as follows: * min_allowed - The value to compare the MIN_SE header value to. Example 1.7. sstCheckMin usage ... modparam("sst", "sst_flag", 6) modparam("sst", "min_se", 2400) # Must be >= 90 ... route { if (method=="INVITE") { if (sstCheckMin("1")) { xlog("L_ERR", "422 Session Timer Too Small reply sent.\n "); exit; } # track the session timers via the dialog module setflag(6); create_dialog(); } } ... or ... route { if (method=="INVITE") { if (sstCheckMin("0")) { xlog("L_ERR", "Session Timer Too Small, dropping request \n"); exit; } # track the session timers via the dialog module setflag(5); setflag(6); } } ... 1.6. Exported Statistics 1.6.1. expired_sst Number of dialogs which got expired session timer. opensips-2.2.2/modules/sst/doc/000077500000000000000000000000001300170765700164115ustar00rootroot00000000000000opensips-2.2.2/modules/sst/doc/sst.xml000066400000000000000000000022131300170765700177420ustar00rootroot00000000000000 %docentities; ]> SST Module (SIP Session Timer) &osipsname; Ron Winacott SOMA Networks, Inc.
ronw@somanetworks.com
Ron Winacott
ronw@somanetworks.com
2006 SOMA Networks, Inc. $Revision: 5901 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/sst/doc/sst_admin.xml000066400000000000000000000236141300170765700211220ustar00rootroot00000000000000 &adminguide;
Overview The sst module provides a way to update the dialog expire timer based on the SIP INVITE/200 OK Session-Expires header value. You can use the sst module in an OpenSIPS proxy to allow freeing of local resources of dead (expired) calls. You can also use the sst module to validate the MIN_SE header value and reply to any request with a "422 - Session Timer Too Small" if the value is too small for your OpenSIPS configuration.
How it works The sst module uses the dialog module to be notified of any new or updated dialogs. It will then look for and extract the session-expire: header value (if there is one) and override the dialog expire timer value for the current context dialog. You flag any call setup INVITE that you want to cause a timed session to be established. This will cause OpenSIPS to request the use of session times if the UAC does not request it. All of this happens with a properly configured dialog and sst module and setting the dialog flag and the sst flag at the time any INVITE sip message is seen. There is no opensips.cfg script function call required to set the dialog expire timeout value. See the dialog module users guide for more information. The sstCheckMin() script function can be used to varify the Session-expires / MIN-SE header field values are not too small for a proxy. If the SST min_se parameter value is smaller then the messages Session-Expires / MIN-SE values, the test will return true. You can also configure the function to send the 422 response for you. The following was taken from the RFC as a call flow example: Session timer call flow +-------+ +-------+ +-------+ | UAC-1 | | PROXY | | UAC-2 | +-------+ +-------+ +-------+ |(1) INVITE | | |SE: 50 | | |----------->| | | |(2)sstCheckMin | | |-----+ | | | | | | |<----+ | |(3) 422 | | |MSE:1800 | | |<-----------| | | | | |(4)ACK | | |----------->| | | | | |(5) INVITE | | |SE: 1800 | | |MSE: 1800 | | |----------->| | | |(6)sstCheckMin | | |-----+ | | | | | | |<----+ | | |(7)setflag | | |Dialog flag | | |Set expire | | |-----+ | | | | | | |<----+ | | | | | |(8)INVITE | | |SE: 1800 | | |MSE: 1800 | | |-------------->| | | | ...
Dependencies
&osips; Modules The following modules must be loaded before this module: dialog - dialog module and its decencies. (tm) sl - stateless module.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>enable_stats</varname> (integer) If the statistics support should be enabled or not. Via statistic variables, the module provide information about the dialog processing. Set it to zero to disable or to non-zero to enable it. Default value is 1 (enabled). Set <varname>enable_stats</varname> parameter ... modparam("sst", "enable_stats", 0) ...
<varname>min_se</varname> (integer) The value is used to set the proxies MIN-SE value and is used in the 422 reply as the proxies MIN-SE: header value if the sstCheckMin() flag is set to true and the check fails. If not set and sstCheckMin() is called with the send-reply flag set to true, the default 1800 seconds will be used as the compare and the MIN-SE: header value if the 422 reply is sent. Default value is 1800 seconds. Set <varname>min_se</varname> parameter ... modparam("sst", "min_se", 2400) ...
<varname>sst_interval</varname> (integer) The sst minimum interval in Session-Expires header if OpenSIPS request the use of session times. The used value will be the maximum value between OpenSIPS minSE, UAS minSE and this value. Per default the interval used will be the min_se value Default value is 0 seconds. Set <varname>sst_interval</varname> parameter ... modparam("sst", "sst_interval", 2400) ...
<varname>reject_to_small</varname> (integer) In the initial INVITE if the UAC has requested a Session-Expire: and it's value is smaller then our local policies Min-SE (see min_se above), then the PROXY has the right to reject the call by replying to the message with a 422 Session Timer Too Small and state our local Min-SE: value. The INVITE is NOT forwarded on through the PROXY. This flag if true will tell the SST module to reject the INVITE with a 422 response. If false, the INVITE is forwarded through the PROXY with out any modifications. Default value is 1 (true/on). Set <varname>reject_to_small</varname> parameter ... modparam("sst", "reject_to_small", 0) ...
<varname>sst_flag</varname> (string/integer) Keeping with OpenSIPS, the module will not do anything to any message unless instructed to do so via the opensips.cfg script. You must set the sst_flag value in the setflag() call of the INVITE you want the sst module to process. But before you can do that, you need to tell the sst module which flag value you are assigning to sst. In most cases when ever you create a new dialog via create_dialog() function,you will want to set the sst flag. If create_dialog() is not called and the sst flag is set, it will not have any effect. This parameter must be set of the module will not load. WARNING: Setting INT flags is deprecated! Use quoted strings instead! Default value is Not set!. Set <varname>sst_flag</varname> parameter ... modparam("sst", "sst_flag", "SST_FLAG") ... route { ... if (method=="INVITE") { setflag(SST_FLAG); # Set the sst flag create_dialog(); # and then create the dialog } ... }
Exported Functions
<function moreinfo="none">sstCheckMin(send_reply_flag)</function> Check the current Session-Expires / MIN-SE values against the sst_min_se parameter value. If the Session-Expires or MIN_SE header value is less then modules minimum value, this function will return true. If the fuction is called with the send_reply_flag set to true (1) and the requested Session-Expires / MIN-SE values are too small, a 422 reply will be sent for you. The 422 will carry a MIN-SE: header with the sst min_se parameter value set. Meaning of the parameters is as follows: min_allowed - The value to compare the MIN_SE header value to. <function>sstCheckMin</function> usage ... modparam("sst", "sst_flag", 6) modparam("sst", "min_se", 2400) # Must be >= 90 ... route { if (method=="INVITE") { if (sstCheckMin("1")) { xlog("L_ERR", "422 Session Timer Too Small reply sent.\n"); exit; } # track the session timers via the dialog module setflag(6); create_dialog(); } } ... or ... route { if (method=="INVITE") { if (sstCheckMin("0")) { xlog("L_ERR", "Session Timer Too Small, dropping request\n"); exit; } # track the session timers via the dialog module setflag(5); setflag(6); } } ...
Exported Statistics
<varname>expired_sst</varname> Number of dialogs which got expired session timer.
opensips-2.2.2/modules/sst/sst.c000066400000000000000000000131301300170765700166170ustar00rootroot00000000000000/* * SIP Session Timer (sst) module - support for tracking dialogs and * SIP Session Timers. * * Copyright (C) 2006 SOMA Networks, INC. * Written by: Ron Winacott (karwin) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-05-11 initial version (karwin) * 2006-10-10 RFC compilent changes. Added the other flags (karwin) */ #include #include #include #include "../../sr_module.h" #include "../signaling/signaling.h" #include "sst_handlers.h" /* also includes sr_module.h needed by handlers */ static int mod_init(void); /** SIGNALING binds */ struct sig_binds sigb; /* * statistic variables */ int sst_enable_stats = 1; stat_var *expired_sst = 0; /* * The default or script parameter for the requested MIN-SE: value for * this proxy. (in seconds) If the passed in value is 0, then this * proxy will except any value from the UAC as its min-SE value. If * the value is NOT set then the default will be asserted. */ unsigned int sst_minSE = 90; /* * Should the PROXY (us) reject (with a 422 reply) and SE < sst_minSE * requests is it can. Default is YES. */ unsigned int sst_reject = 1; /* The sst message flag value */ static int sst_flag = -1; static char *sst_flag_str = 0; /* * The sst minimum interval in Session-Expires header if OpenSIPS * request the use of session times. The used value will be the * maximum value between OpenSIPS minSE, UAS minSE and this value */ unsigned int sst_interval = 0; /* * Binding to the dialog module */ struct dlg_binds dialog_st; struct dlg_binds *dlg_binds = &dialog_st; /* * Script commands we export. */ static cmd_export_t cmds[]={ {"sstCheckMin", (cmd_function)sst_check_min, 1, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {0,0,0,0,0,0} }; /* * Script parameters */ static param_export_t mod_params[]={ { "enable_stats", INT_PARAM, &sst_enable_stats }, { "min_se", INT_PARAM, &sst_minSE }, { "reject_to_small", INT_PARAM, &sst_reject }, { "sst_flag", STR_PARAM, &sst_flag_str }, { "sst_flag", INT_PARAM, &sst_flag }, { "sst_interval", INT_PARAM, &sst_interval }, { 0,0,0 } }; /* * Export the statistics we have */ static stat_export_t mod_stats[] = { {"expired_sst", 0, &expired_sst}, {0,0,0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_DEFAULT, "signaling", DEP_ABORT }, { MOD_TYPE_DEFAULT, "dialog", DEP_ABORT }, /* * FIXME: silent module load ordering, due to Session-Expires updates from sst * proper fix should involve dialog callback ordering */ { MOD_TYPE_DEFAULT, "pua_dialoginfo", DEP_SILENT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports= { "sst", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ mod_params, /* param exports */ mod_stats, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ 0, /* Destroy function */ 0 /* per-child init function */ }; /** * The initialization function, called when the module is loaded by * the script. This function is called only once. * * Bind to the dialog module and setup the callbacks. Also initialize * the shared memory to store our interninal information in. * * @return 0 to continue to load the OpenSIPS, -1 to stop the loading * and abort OpenSIPS. */ static int mod_init(void) { LM_INFO("SIP Session Timer module - initializing\n"); /* * if statistics are disabled, prevent their registration to core. */ if (sst_enable_stats==0) { exports.stats = 0; } fix_flag_name(sst_flag_str, sst_flag); sst_flag = get_flag_id_by_name(FLAG_TYPE_MSG, sst_flag_str); if (sst_flag == -1) { LM_ERR("no sst flag set!!\n"); return -1; } else if (sst_flag > MAX_FLAG) { LM_ERR("invalid sst flag %d!!\n", sst_flag); return -1; } /* load SIGNALING API */ if(load_sig_api(&sigb)< 0) { LM_ERR("can't load signaling functions\n"); return -1; } /* * Init the handlers */ sst_handler_init(sst_minSE, sst_flag, sst_reject,sst_interval); /* * Register the main (static) dialog call back. */ if (load_dlg_api(&dialog_st) != 0) { LM_ERR("failed to load dialog hooks\n"); return(-1); } /* Load dialog hooks */ dialog_st.register_dlgcb(NULL, DLGCB_CREATED, sst_dialog_created_CB, NULL, NULL); if (dialog_st.register_dlgcb(NULL, DLGCB_LOADED, sst_dialog_loaded_CB, NULL, NULL) != 0) { LM_ERR("cannot register dialog_loaded callback\n"); return -1; } /* * We are GOOD-TO-GO. */ return 0; } opensips-2.2.2/modules/sst/sst_handlers.c000066400000000000000000001001041300170765700204750ustar00rootroot00000000000000/* * Copyright (C) 2006 SOMA Networks, Inc. * Written by Ron Winacott (karwin) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ /** *\file *\brief Functions for the SST module */ /** * SST support: * * The Session-Expires header conveys the session interval for a SIP * call. It is placed in an INVITE request and is allowed in any 2xx * class response to an INVITE. Its presence indicates that the UAC * wishes to use the session timer for this call. Unlike the * SIP-Expires header, it can only contain a delta-time, which is the * current time, plus the session interval from the response. * * For example, if a UAS generates a 200 OK response to a re-INVITE * that contained a Session-Expires header with a value of 1800 * seconds (30 minutes), the UAS computes the session expiration as 30 * minutes after the time when the 200 OK response was sent. For each * proxy, the session expiration is 30 minutes after the time when the * 2xx was received or sent. For the UAC, the expiration time is 30 * minutes after the receipt of the final response. * */ #include /* for snprintf() */ #include /* for memset() */ #include /* For atoi() */ #include "../../pvar.h" #include "../../parser/parse_sst.h" #include "../../parser/parse_supported.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "../../dprint.h" #include "../../sr_module.h" /* Needed for find_export() */ #include "../signaling/signaling.h" #include "../dialog/dlg_vals.h" #include "sst_handlers.h" #include "sst_mi.h" /* * My own LM_*() macros to add the correct message prefix and * file/function/line number information to all LOG messages. */ #define DLOGMSG(msg) { \ if (msg->first_line.type == SIP_REQUEST) { \ LM_INFO("REQUEST: %.*s\n", \ msg->first_line.u.request.method.len, \ msg->first_line.u.request.method.s); \ } \ else { \ LM_INFO("RESPONSE: %d %.*s\n", \ msg->first_line.u.reply.statuscode, \ msg->first_line.u.reply.reason.len, \ msg->first_line.u.reply.reason.s); \ } \ } #ifndef MIN #define MIN(a, b) (ab?a:b) #endif #define CHECK_AND_UPDATE_SST_INFO(info, field, value, dirty) \ do {\ if (info-> field != value) { \ info-> field = value; \ dirty = 1;\ }\ } while (0) #define CHECK_AND_UPDATE_SST_INFO_TMP(info, field, value, dirty, tmp) \ do {\ tmp.field = value; \ CHECK_AND_UPDATE_SST_INFO(info, field, tmp.field, dirty); \ } while (0) /** * The binding to the dialog module functions. Most importantly the * register_dlgcb function. */ extern struct dlg_binds *dlg_binds; /** * A collection of information about SST in the current SIP message * being processed. */ typedef struct sst_msg_info_st { int supported; /* supported = timer in message */ unsigned int min_se; /* The Min-SE: value or zero */ unsigned int se; /* The Sesion-Expires: header */ enum sst_refresher refresher;/* The refresher (parse_sst.h) */ } sst_msg_info_t; /** * Local function prototypes See function definition for * documentation. */ #ifdef USE_CONFIRM_CALLBACK static void sst_dialog_confirmed_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params); #endif /* USE_CONFIRM_CALLBACK */ static void sst_free_info(void* param); static void sst_dialog_terminate_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params); static void sst_dialog_request_within_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params); static void sst_dialog_response_fwded_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params); static int send_response(struct sip_msg *request, int code, str *reason, char *header, int header_len); static int append_header(struct sip_msg *msg, const char *header); static int remove_minse_header(struct sip_msg *msg); static int parse_msg_for_sst_info(struct sip_msg *msg, sst_msg_info_t *minfo); static int send_reject(struct sip_msg *msg, unsigned int min_se); static void set_dialog_lifetime(struct dlg_cell *dlg, unsigned int value); static void setup_dialog_callbacks(struct dlg_cell *did, sst_info_t *info); /** * The pointer to the stateless reply function This is used to send a * 422 reply if asked to with a Min-SE: header value to small. */ extern struct sig_binds sigb; /** * Our Min-SE: header field value and test. */ static unsigned int sst_min_se = 0; /** * Should the SE < sst_min_se be rehected with a 422 reply? */ static unsigned int sst_reject = 1; /** * The value of the message flag to flag an INVITE we want to process * through the SST module. */ static int sst_flag = 0; /** * Our Session-Expire minimum interval */ static unsigned int sst_interval = 0; static str sst_422_rpl = str_init("Session Timer Too Small"); static str info_val_name = str_init("sst_info"); /** * This is not a public API. This function is called when the module * is loaded from the mod_init() function in sst.c to initialize the * callback handlers and local variables. * * @param timeout_avp_p - The pointer to the dialog modules timeout * AVP. * @param min_se - The minimum session expire value allowed by this * PROXY. * @param flag - sst flag * @param reject - reject state * @param interval - The minimum session expire value used by this * PROXY */ void sst_handler_init(unsigned int min_se, int flag, unsigned int reject, unsigned int interval) { sst_min_se = min_se; sst_flag = 1 << flag; sst_reject = reject; sst_interval = MAX(interval, sst_min_se); } /** * Every time a new dialog is created (from a new INVITE) the dialog * module will call this callback function. We need to track the * dialogs lifespan from this point forward until it is terminated * with a BYE, CANCEL, etc. In the process, we will see if either or * both ends of the conversation supports SIP Session Timers and setup * the dialog timeout to expire at the session timer expire time. Each * time the new re-INVITE is seen to update the SST, we will reset the * life span of the dialog to match it. * * This function will setup the other types of dialog callbacks * required to track the lifespan of the dialog. It will also start * the state tracking to figure out if and who supports SST. * * As per RFC4028: Request handling: * * - The proxy may insert a SE header if none found. * - The SE value can be anything >= Min-SE (if found) * - The proxy MUST NOT add a refresher parameter to the SE. * * - If SE is already there, the Proxy can reduce its value but no * lower then the Min-SE value if present. * - If the SE value is >= Min-SE the proxy MUST NOT increase it! * - If the SE value is < Min-SE (settable by the proxy) the proxy * MUST increase the SE value to >= the new Min-SE. * - The proxy MUST NOT insert or change the refresher parameter. * * - If the supported=timer is found, the proxy may reject the request * with a 422 if the SE value is smaller then the local policy. The * 422 MUST hold the proxies Min-SE value >= 90. * - If support=timer is NOT indecated, the proxy can't reject with a * 422 but can include/increase the MIN-SE: to be = to local policy. * and increase the SE to match the new Min-SE value. * - the proxy MUST NOT insert/change the Min-SE header if * supported=timer is present. (DoS attacks) * * @param did - The dialog ID * @param type - The trigger event type (CREATED) * @param params - The pointer to nothing. As we did not attach * anything to this callback in the dialog module. */ void sst_dialog_created_CB(struct dlg_cell *did, int type, struct dlg_cb_params * params) { sst_info_t *info = NULL; sst_msg_info_t minfo; struct sip_msg* msg = params->msg; memset(&minfo, 0, sizeof(sst_msg_info_t)); /* * Only deal with messages flaged as SST interested. */ if ((msg->flags & sst_flag) != sst_flag) { LM_DBG("SST flag was not set for this request\n"); return; } /* * look only at INVITE */ if (msg->first_line.type != SIP_REQUEST || msg->first_line.u.request.method_value != METHOD_INVITE) { LM_WARN("dialog create callback called with a non-INVITE request.\n"); return; } /* * Gather all he information about SST for this message */ if (parse_msg_for_sst_info(msg, &minfo)) { LM_ERR("failed to parse sst information\n"); return; } info = (sst_info_t *)shm_malloc(sizeof(sst_info_t)); if (info == NULL) { LM_ERR("No more shared memory!\n"); return; } memset(info, 0, sizeof(sst_info_t)); info->requester = (minfo.se?SST_UAC:SST_UNDF); info->supported = (minfo.supported?SST_UAC:SST_UNDF); info->interval = MAX(sst_interval, 90); /* For now, will set for real * later */ if (minfo.se != 0) { /* * There is a SE already there, this is good, we just need to * check the values out a little before passing it along. */ if (minfo.se < sst_min_se) { /* * Problem, the requested Session-Expires is too small for * our local policy. We need to fix it, or reject it or * ignore it. */ if (!minfo.supported) { /* * Increase the Min-SE: value in the request and * forward it. */ char buf[80]; if (minfo.min_se) { /* We need to update, which means, remove + * insert */ remove_minse_header(msg); } info->interval = MAX(sst_min_se, minfo.min_se); snprintf(buf, 80, "Min-SE: %d\r\n", info->interval); if (append_header(msg, buf)) { LM_ERR("Could not append modified Min-SE: header\n"); } } else if (sst_reject) { /* Make sure that that all are at least 90 */ send_reject(msg, MAX(MAX(sst_min_se, minfo.min_se), 90)); shm_free(info); return; } } /* end of se < sst_min_se */ else { /* Use the INVITE SE: value */ info->interval = minfo.se; } } else { /* * No Session-Expire: stated in request. */ char buf[80]; info->interval = MAX(minfo.min_se, sst_min_se); if (minfo.min_se && minfo.min_se < sst_min_se) { remove_minse_header(msg); snprintf(buf, 80, "Min-SE: %d\r\n", info->interval); if (append_header(msg, buf)) { LM_ERR("failed to append modified Min-SE: header\n"); /* What to do? Let is slide, we can still work */ } } info->interval = MAX(info->interval, sst_interval); info->requester = SST_PXY; snprintf(buf, 80, "Session-Expires: %d\r\n", info->interval); if (append_header(msg, buf)) { LM_ERR("failed to append Session-Expires header to proxy " "requested SST.\n"); shm_free(info); return; /* Nothing we can do! */ } } /* We keep the sst_info in the dialog's vals in case of restarting */ /* No const here because of store_dlg_value's definition */ str raw_info = {(char*)info, sizeof(sst_info_t)}; if (dlg_binds->store_dlg_value(did, &info_val_name, &raw_info) != 0) { LM_ERR("No sst_info can be added to the dialog." "This dialog won't be considered after restart!\n"); } dlg_binds->set_mod_flag(did, SST_DIALOG_FLAG); setup_dialog_callbacks(did, info); /* Early setup of default timeout */ set_dialog_lifetime(did, info->interval); return; } void sst_dialog_loaded_CB(struct dlg_cell *did, int type, struct dlg_cb_params *params){ /* Check if this is previously marked by sst module */ if (!dlg_binds->is_mod_flag_set(did, SST_DIALOG_FLAG)) return; /* We try to get the original sst info back */ sst_info_t *info = (sst_info_t *)shm_malloc(sizeof(sst_info_t)); if (info == NULL) { LM_ERR ("No more shared memory!\n"); return; } memset(info, 0, sizeof(sst_info_t)); str raw_info = {(char*)info, sizeof(sst_info_t)}; if (dlg_binds->fetch_dlg_value(did, &info_val_name, &raw_info, 1) != 0){ LM_ERR ("No sst_info found!\n"); return; } setup_dialog_callbacks(did, info); } #ifdef USE_CONFIRM_CALLBACK /** * Play time. Please ignore this call. */ static void sst_dialog_confirmed_CB(struct dlg_cell *did, int type, struct dlg_cb_params * params) { struct sip_msg* msg = params->msg; LM_DBG("confirmed dialog CB %p\n", did); DLOGMSG(msg); } #endif /* USE_CONFIRM_CALLBACK */ /* * free function for dialog callbacks */ static void sst_free_info(void* param) { sst_info_t* info = (sst_info_t *) param; if (info == NULL) { LM_ERR("null sst info!\n"); return; } /* * FIXME refcnt is 0 that means no dialog termination callback * was called what shall we do here? For the moment we free * the memory but this might crash if the free function is called * multiple times * */ if (info->refcnt == 0 || (--info->refcnt) == 0) shm_free(info); } /** * This callback is called when ever a dialog is terminated. The cause * of the termination can be normal, failed call, or expired. It is * the expired dialog we are really interested in. * * @param did - The Dialog ID / structure pointer. Used as an ID only. * @param type - The termination cause/reason. * @param params - The sst information */ static void sst_dialog_terminate_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { switch (type) { case DLGCB_FAILED: LM_DBG("DID %p failed (canceled). " "Terminating session.\n", did); break; case DLGCB_EXPIRED: /* In the case of expired, the msg is pointing at a * FAKED_REPLY (-1) */ LM_DBG("Terminating session.\n"); break; default: /* Normal termination. */ LM_DBG("Terminating DID %p session\n", did); break; } ((sst_info_t *)(*params->param))->refcnt++; return; } /** * Callback from the dialog module when the dialog is being updated in * its life span. We are only interested in the INVITE or UPDATE if * SST is supported and active for this dialog. In this case, we need * to update the expire time for the dialog based on the * Session-Expires: header in the reINVITE/UPDATE request. * * When this callback returns control to the dialog module it WILL * reset the timeout of the dialog. We need to make sure we set the * AVP here or the dialog timeout will be reset to the DEFAULT value * if this is a different transaction. (so the AVP value is gone) * * @param did - The dialog structure. The pointer is used as an ID. * @param type - The reason for the callback. DLGCB_REQ_WITHIN * @param params - The sst information */ static void sst_dialog_request_within_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { sst_info_t *info = (sst_info_t *)*(params->param); sst_info_t tmp_info; sst_msg_info_t minfo = {0,0,0,0}; struct sip_msg* msg = params->msg; short info_dirty = 0; if (msg->first_line.type == SIP_REQUEST) { if ((msg->first_line.u.request.method_value == METHOD_INVITE || msg->first_line.u.request.method_value == METHOD_UPDATE)) { LM_DBG("Update by a REQUEST. %.*s\n", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s); if (parse_msg_for_sst_info(msg, &minfo)) { // FIXME: need an error message here return; } /* Early resetting of the value here */ if (minfo.se > 0) { if (sst_interval > minfo.min_se) CHECK_AND_UPDATE_SST_INFO(info, interval, sst_interval, info_dirty); else CHECK_AND_UPDATE_SST_INFO_TMP(info, interval, MAX(minfo.se, sst_min_se), info_dirty, tmp_info); } CHECK_AND_UPDATE_SST_INFO_TMP(info, supported, (minfo.supported?SST_UAC:SST_UNDF), info_dirty, tmp_info); set_dialog_lifetime(did, info->interval); } else if (msg->first_line.u.request.method_value == METHOD_PRACK || msg->first_line.u.request.method_value == METHOD_ACK) { /* Special case here. The PRACK will cause the dialog * module to reset the timeout value to the ldg->lifetime * value and look for the new AVP value bound to the * 1XX/PRACK/200OK/ACK transaction and not to the * INVITE/200OK avp value. So we need to set the AVP * again! */ LM_DBG("ACK/PRACK workaround applied!%d\n", info->interval); set_dialog_lifetime(did, info->interval); } } else if (msg->first_line.type == SIP_REPLY) { if ((msg->first_line.u.reply.statuscode > 199 && msg->first_line.u.reply.statuscode < 300)) { /* * To spec (RFC) the internal time out value so not be reset * until here. */ LM_DBG("Update by a REPLY %d %.*s\n", msg->first_line.u.reply.statuscode, msg->first_line.u.reply.reason.len, msg->first_line.u.reply.reason.s); if (parse_msg_for_sst_info(msg, &minfo)) { // FIXME: need an error message here return; } set_dialog_lifetime(did, minfo.se); CHECK_AND_UPDATE_SST_INFO_TMP(info, supported, (minfo.supported?SST_UAC:SST_UNDF), info_dirty, tmp_info); CHECK_AND_UPDATE_SST_INFO(info, interval, minfo.se, info_dirty); } } if (info_dirty){ str raw_info = {(char*)info, sizeof(sst_info_t)}; if (dlg_binds->store_dlg_value(did, &info_val_name, &raw_info) != 0) { LM_ERR("sst_info can't be updated\n"); } } } /** * This callback is called on any response message in the lifespan of * the dialog. The callback is called just before the message is * copied to pkg memory so it is still mutable. * * @param did - The dialog structure. The pointer is used as an ID. * @param type - The reason for the callback. DLGCB_CONFIRMED * @param params - The sst information */ static void sst_dialog_response_fwded_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { struct sip_msg* msg = params->msg; int *param; short info_dirty = 0; /* * This test to see if the message is a response sould ALWAYS be * true. This callback should not get called for requests. But * lets be safe. */ if (msg->first_line.type != SIP_REPLY) return; sst_msg_info_t minfo = {0,0,0,0}; sst_info_t *info = (sst_info_t *)*(params->param); sst_info_t tmp_info; LM_DBG("Dialog seen REPLY %d %.*s\n", msg->first_line.u.reply.statuscode, msg->first_line.u.reply.reason.len, msg->first_line.u.reply.reason.s); /* * Need to check to see if it is a 422 response. If it is, * make sure our Min-SE: for this dialog is set at least as * large as in the Min-SE: in the reply 422 message. If not, * we will create an INVITE, 422 loop. */ if (msg->first_line.u.reply.statuscode == 422) { if (parse_msg_for_sst_info(msg, &minfo)) { LM_ERR("failed to prase sst information for thr 422 reply\n"); return; } /* Make sure we do not try to use anything smaller */ if (info->interval < minfo.min_se) CHECK_AND_UPDATE_SST_INFO(info, interval, minfo.min_se, info_dirty); goto update_info; /* There is nothing else to do with this */ } /* * We need to get the method this reply is for from the CSEQ * body. The RFC states we can only play with 2XX from the * INVITE or reINVTE/UPDATE. */ if (!msg->cseq && ((parse_headers(msg, HDR_CSEQ_F, 0) == -1) || !msg->cseq)) { LM_ERR("failed to parse CSeq\n"); return; } /* 2XX replies to INVITES only !*/ if (msg->first_line.u.reply.statuscode > 199 && msg->first_line.u.reply.statuscode < 300 && (get_cseq(msg)->method_id == METHOD_INVITE || get_cseq(msg)->method_id == METHOD_UPDATE)) { if (parse_msg_for_sst_info(msg, &minfo)) { LM_ERR("failed to parse sst information for the 2XX reply\n"); return; } LM_DBG("parsing 200 OK response %d / %d\n", minfo.supported, minfo.se); if (info->supported != SST_UAC) { CHECK_AND_UPDATE_SST_INFO_TMP(info, supported, (minfo.supported?SST_UAS:SST_UNDF),info_dirty, tmp_info); } if (minfo.se != 0) { if (sst_interval > minfo.min_se) CHECK_AND_UPDATE_SST_INFO(info, interval, sst_interval, info_dirty); else CHECK_AND_UPDATE_SST_INFO_TMP(info, interval, MAX(minfo.se, sst_min_se), info_dirty, tmp_info); LM_DBG("UAS supports timer\n"); set_dialog_lifetime(did, info->interval); } else { /* no se header found, we want to resquest it. */ if (info->supported == SST_UAC) { char se_buf[80]; LM_DBG("UAC supports timer\n"); LM_DBG("appending the Session-Expires: header to the 2XX reply." " UAC will deal with it.\n"); /* * GOOD! we can just insert the Session-Expires: * header and forward back to the UAC and it will * deal with refreshing the session. */ if (sst_interval > minfo.min_se) CHECK_AND_UPDATE_SST_INFO(info, interval, sst_interval, info_dirty); else CHECK_AND_UPDATE_SST_INFO_TMP(info, interval, MAX(minfo.se, sst_min_se), info_dirty, tmp_info); snprintf(se_buf, 80, "Session-Expires: %d;refresher=uac\r\n", info->interval); if (append_header(msg, se_buf)) { LM_ERR("failed to append Session-Expires header\n"); return; } /* Set the dialog timeout HERE */ set_dialog_lifetime(did, info->interval); } else { /* We are sunk, uac did not request it, and it * does not support it */ LM_DBG("UAC and UAS do not support timers!" " No session timers for this session.\n"); param = find_param_export("dialog", "default_timeout", INT_PARAM); CHECK_AND_UPDATE_SST_INFO_TMP(info, interval, param?*param:12*3600, info_dirty, tmp_info); set_dialog_lifetime(did, info->interval); } } } /* End of 2XX for an INVITE */ update_info: if (info_dirty){ str raw_info = {(char*)info, sizeof(sst_info_t)}; if (dlg_binds->store_dlg_value(did, &info_val_name, &raw_info) != 0) { LM_ERR("sst_info can't be updated\n"); } } } /** * The sstCheckMin() script command handler. Return 1 (true) if the * MIN-SE: of the message is too small compared to the sst_min_se * value. This will allow the script to reply to this INVITE with a * "422 Session Timer Too Small" response. if sst_min_se was never set * the recommended value of 1800 seconds will be used. * * If the flag (str1) is set to 1, the 422 reply will be sent with the * sst MIN_SE value in the header. If the flag is not set or is NULL, * no reply is sent. * @param msg - The sip message from the script (INVITE only) * @param flag - Reply mode Flag. 0/NULL do not send reply, 1 send 422 * reply if Session-Expires is to small with the MIN-SE * header in the reply * @param str2 - Not used. * * @return 1 if the MIN-SE is too small, -1 if it is OK, or It could * not be checked. * * NOTE: returning 0 == drop message, 1 == true, -1 == false in the * script. */ int sst_check_min(struct sip_msg *msg, char *flag, char *str2) { enum parse_sst_result result; struct session_expires se = {0,0}; unsigned minse = 0; /* * Only look in INVITES. We can't reply with a 422 to a 2xx reply * now can we. This check can ONLY be done on the INVITE/UPDATE. */ if (msg->first_line.type == SIP_REQUEST && msg->first_line.u.request.method_value == METHOD_INVITE) { /* * First see if there is an Session-Expires: header. If there * is, also look for a MIN-SE: header. If there is, use the * minimum value of the two to compare with srt1. All MUST not * be less then 90 and 1800 is recomended. See RCF section 4. */ if ((result = parse_session_expires(msg, &se)) != parse_sst_success) { if (result != parse_sst_header_not_found) { LM_ERR("failed to parse Session-Expires headers.\n"); return 0; /* Error drop the message */ } /* Session-Expires not supported/stated */ LM_DBG("No Session-Expires header found. retuning false (-1)\n"); /* * NOTE: 0 == drop message, 1 == true, -1 == false */ return -1; } /* * We have a Session_expire header. Now look for the MIN-SE. */ if ((result = parse_min_se(msg, &minse)) != parse_sst_success) { if (result != parse_sst_header_not_found) { /* * This is an error. The header was found but could * not parse it. */ LM_ERR("failed to parse MIN-SE header.\n"); return -1; } /* * If not stated, use the value from the session-expires * header */ LM_DBG("No MIN-SE header found.\n"); minse = 90 /*this is the recommended value*/ /*se.interval*/; } LM_DBG("Session-Expires: %d; MIN-SE: %d\n", se.interval, minse); /* * Now compare our MIN-SE with the messages and see if it is * too small. We will take the smaller of the messages * Session-expires and min-se if stated. */ if (sst_min_se > MIN(minse, se.interval)) { /* * Too small. See if we need to send the 422 and are able * to send it. */ if (flag) { char minse_hdr[3+1+2+1+1+11+CRLF_LEN+2+1]; int hdr_len = 3+1+2+1+1+11+CRLF_LEN+2; memset(minse_hdr, 0, hdr_len+1); hdr_len = snprintf(minse_hdr, hdr_len, "%s%d%s", "MIN-SE: ", sst_min_se,CRLF); LM_DBG("Sending 422: %.*s\n", hdr_len, minse_hdr); if (send_response(msg, 422, &sst_422_rpl, minse_hdr, hdr_len)){ LM_ERR("Error sending 422 reply.\n"); } } LM_DBG("Done returning true (1)\n"); return 1; /* return true */ } } LM_DBG("Done returning false (-1)\n"); /* * All is good. */ return -1; /* return false */ } /** * Send a reply (response) to the passed in SIP request messsage with * the code and reason. If the header is not NULL (and header_len != * 0) the add the header to the reply message. * * @param request The SIP request message to build the reply from. * @param code The response code. i.e 200 * @param reason The response reason. i.e. "OK" * @param header the header block to add to the reply. * @param header_len The length of the header block. (header) * * @return 0 on success, none-zero on an error. */ static int send_response(struct sip_msg *request, int code, str *reason, char *header, int header_len) { if (sigb.reply != 0) { /* Add new headers if not null or zero length */ if ((header) && (header_len)) { if (add_lump_rpl(request, header, header_len, LUMP_RPL_HDR) == 0) { /* An error with adding the lump */ LM_ERR("unable to append header.\n"); return -1; } } /* Now using the sl function, send the reply/response */ if (sigb.reply(request, code, reason, NULL) < 0) { LM_ERR("Unable to sent reply.\n"); return -1; } } else { return -1; } return(0); } /** * Given some header text, append it to the passed in message. * * @param msg The message to append the header text to. * @param header The header text to append. * * @return 0 on success, non-zero on failure. */ static int append_header(struct sip_msg *msg, const char *header) { struct lump* anchor = NULL; char *s = NULL; int len = 0; LM_DBG("Appending header: %s", header); if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse headers in message.\n"); return(1); } if ((anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0)) == 0) { LM_ERR("failed to get anchor to append header\n"); return(1); } len = strlen(header); if ((s = (char *)pkg_malloc(len)) == 0) { LM_ERR("No more pkg memory. (size requested = %d)\n", len); return(1); } memcpy(s, header, len); if (insert_new_lump_before(anchor, s, len, 0) == 0) { LM_ERR("failed to insert lump\n"); pkg_free(s); return(1); } LM_DBG("Done appending header successfully.\n"); return(0); } /** * Remove a header from a message if found. * * @param msg The message to look for the header to remove. * @param header The header name: text. * * @return 0 if the header was not found, >0 is successful, -1 on an * error. */ static int remove_minse_header(struct sip_msg *msg) { struct lump* anchor = NULL; struct hdr_field *hf = NULL; int cnt = 0; /* parse all headers as we want to get all MIN-SE headers*/ if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse headers in message.\n"); return(-1); } for (hf = msg->min_se; hf; hf = hf->sibling) { anchor = del_lump(msg, hf->name.s-msg->buf, hf->len, 0); if (anchor == 0) { LM_ERR("no more pkg memory\n"); return -1; } cnt++; } return cnt; } /** * Set the dialog's AVP value so the dialog module will use this value * and not the default when returning from the dialog callback. * * @param dlg The current dialog * @param value The value you want to set the AVP to. */ static void set_dialog_lifetime(struct dlg_cell *dlg, unsigned int value) { /* Set the dialog timeout HERE */ dlg->lifetime = value; dlg->lifetime_dirty = 1; LM_DBG("set dialog timeout value to %d\n", value); } /** * Gether the message information about SST from the current message * being processed. * * @param msg The current message to parse. * @param minfo The SST information found in the message. * * @return 0 on success, -1 on a parsing error. */ static int parse_msg_for_sst_info(struct sip_msg *msg, sst_msg_info_t *minfo) { int rtn = 0; struct session_expires se = {0,0}; if (!msg || !minfo) { return (-1); } /* * parse the supported infor */ minfo->supported = 0; /*Clear it */ minfo->se = 0; minfo->refresher = sst_refresher_unspecified; minfo->min_se = 0; /* * The parse_supported() will return 0 if found and parsed OK, -1 * if not found or an error parsing the one it did find! So assume * it is not found if unsuccessfull. */ if (msg->supported && parse_supported(msg) == 0 && (get_supported(msg) & F_SUPPORTED_TIMER)) minfo->supported = 1; /* * Parse the Min-SE: header next. */ minfo->min_se = 0; if ((rtn = parse_min_se(msg, &minfo->min_se)) != parse_sst_success) { minfo->min_se = 0; /* Make sure it statys clean */ } minfo->se = 0; if ((rtn = parse_session_expires(msg, &se)) == parse_sst_success) { minfo->se = se.interval; minfo->refresher = se.refresher; } return(0); } /** * Add the Min-SE: header and send a reply 422. * * @param msg The message to opperate on. * @param min_se The Min-SE: value to use in the heaader. * * @return 0 on success, -1 on error. */ static int send_reject(struct sip_msg *msg, unsigned int min_se) { char tmp[2]; /* to find the length */ int hdr_len = 0; char *minse_hdr = NULL; hdr_len = snprintf(tmp, 2, "%s %d%s", "MIN-SE:", min_se, CRLF); if ((minse_hdr = pkg_malloc(hdr_len+1)) != NULL) { memset(minse_hdr, 0, hdr_len+1); snprintf(minse_hdr, hdr_len+1, "%s %d%s", "MIN-SE:", min_se, CRLF); if (send_response(msg, 422, &sst_422_rpl, minse_hdr, hdr_len)) { LM_ERR("Error sending 422 reply.\n"); return(-1); } pkg_free(minse_hdr); LM_DBG("Send reject reply 422 with Min-SE: %d\n", min_se); return(0); } return(-1); } /** * A helper function to setup all the callbacks from the dialog module * after we find intrest in the dialog. * * @param did The Dialog ID. * @param info The sst information. * */ static void setup_dialog_callbacks(struct dlg_cell *did, sst_info_t *info) { /* * Register for the other callbacks from the dialog. */ #ifdef USE_CONFIRM_CALLBACK LM_DBG("Adding callback DLGCB_CONFIRMED\n"); dlg_binds->register_dlgcb(did, DLGCB_CONFIRMED, sst_dialog_confirmed_CB, info, NULL); #endif /* USE_CONFIRM_CALLBACK */ LM_DBG("Adding callback " "DLGCB_FAILED|DLGCB_TERMINATED|DLGCB_EXPIRED\n"); if (dlg_binds->register_dlgcb(did, DLGCB_FAILED|DLGCB_TERMINATED|DLGCB_EXPIRED, sst_dialog_terminate_CB, (void *)info, sst_free_info) != 0) LM_ERR("could not add the DLGCB_TERMINATED callback\n"); LM_DBG("Adding callback DLGCB_REQ_WITHIN\n"); /* This is for the reINVITE/UPDATE requests */ dlg_binds->register_dlgcb(did, DLGCB_REQ_WITHIN, sst_dialog_request_within_CB, info, NULL); /* * This is for the final configuration of who will do SST for * us. In the DLGCB_CONFIRMED callback the message is * immutable! we must do all the real work in the DLGCB_FRD * callback were we can change the message. */ LM_DBG("Adding callback DLGCB_RESPONSE_FWDED|DLGCB_RESPONSE_WITHIN\n"); dlg_binds->register_dlgcb(did, DLGCB_RESPONSE_FWDED|DLGCB_RESPONSE_WITHIN, sst_dialog_response_fwded_CB, info, NULL); LM_DBG("Adding mi handler\n"); dlg_binds->register_dlgcb(did, DLGCB_MI_CONTEXT, sst_dialog_mi_context_CB, info, NULL); } opensips-2.2.2/modules/sst/sst_handlers.h000066400000000000000000000042511300170765700205100ustar00rootroot00000000000000/* * Copyright (C) 2006 SOMA Networks, Inc. * Written By Ron Winacott (karwin) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2006-05-11 initial version (karwin) * 2006-10-10 Code cleanup of this header file. (karwin) */ #ifndef _SST_HANDLERS_H_ #define _SST_HANDLERS_H_ #include "../../pvar.h" #include "../../parser/msg_parser.h" #include "../dialog/dlg_load.h" /** * Fag values used in the sst_info_t See below. */ enum sst_flags { SST_UNDF=0, /* 0 - --- */ SST_UAC=1, /* 1 - 2^0 */ SST_UAS=2, /* 2 - 2^1 */ SST_PXY=4, /* 4 - 2^2 */ SST_NSUP=8 /* 8 - 2^3 */ }; /** * The local state required to figure out if and who supports SST and * if and who will be the refresher. */ typedef struct sst_info_st { enum sst_flags requester; enum sst_flags supported; unsigned int interval; volatile unsigned int refcnt; } sst_info_t; /** * The static (opening) callback function for all dialog creations */ void sst_dialog_created_CB(struct dlg_cell *did, int type, struct dlg_cb_params * params); void sst_dialog_loaded_CB(struct dlg_cell *did, int type, struct dlg_cb_params *params); /** * The script function */ int sst_check_min(struct sip_msg *msg, char *str1, char *str2); /** * The handlers initializer function */ void sst_handler_init(unsigned int minSE, int flag, unsigned int reject, unsigned int interval); #endif /* _SST_HANDLERS_H_ */ opensips-2.2.2/modules/sst/sst_mi.c000066400000000000000000000040071300170765700173070ustar00rootroot00000000000000/* * Copyright (C) 2008 SOMA Networks, Inc. * Written By Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2008-04-11 initial version (osas) */ #include "../../ut.h" #include "../../mi/mi.h" #include "../dialog/dlg_load.h" #include "sst_handlers.h" /** * The dialog mi helper function. */ void sst_dialog_mi_context_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params) { struct mi_node* parent_node = (struct mi_node*)(params->dlg_data); struct mi_node* node; struct mi_attr* attr; sst_info_t* sst_info = (sst_info_t*)*(params->param); char* p; int len; node = add_mi_node_child(parent_node, 0, "sst", 3, NULL, 0); if (node==NULL) { LM_ERR("oom\n"); return; } p = int2str((unsigned long)(sst_info->requester), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "requester_flags", 15, p, len); if(attr == NULL) { LM_ERR("oom requester_flags\n"); return; } p = int2str((unsigned long)(sst_info->supported), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "supported_flags", 15, p, len); if(attr == NULL) { LM_ERR("oom supported_flags\n"); return; } p = int2str((unsigned long)(sst_info->interval), &len); attr = add_mi_attr(node, MI_DUP_VALUE, "interval", 8, p, len); if(attr == NULL) { LM_ERR("oom interval\n"); return; } return; } opensips-2.2.2/modules/sst/sst_mi.h000066400000000000000000000021741300170765700173170ustar00rootroot00000000000000/* * Copyright (C) 2008 SOMA Networks, Inc. * Written By Ovidiu Sas (osas) * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * History: * -------- * 2008-04-11 initial version (osas) */ #ifndef _SST_MI_H_ #define _SST_MI_H_ #include "../dialog/dlg_load.h" /** * The dialog mi helper function. */ void sst_dialog_mi_context_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params); #endif /* _SST_MI_H_ */ opensips-2.2.2/modules/statistics/000077500000000000000000000000001300170765700172255ustar00rootroot00000000000000opensips-2.2.2/modules/statistics/Makefile000066400000000000000000000002671300170765700206720ustar00rootroot00000000000000# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=statistics.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/statistics/README000066400000000000000000000062721300170765700201140ustar00rootroot00000000000000Statistics Module Bogdan Iancu Edited by Bogdan Iancu Copyright © 2006 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. variable (string) 1.4. Exported Functions 1.4.1. update_stat(variable,value) 1.4.2. reset_stat(variable) 1.5. Exported pseudo-variables 1.5.1. $stat List of Examples 1.1. variable example 1.2. update_stat usage 1.3. reset_stat usage 1.4. $stat usage Chapter 1. Admin Guide 1.1. Overview The Statistics module is a wrapper over the internal statistics manager, allowing the script writer to dynamically define and use of statistic variables. By bringing the statistics support into the script, it takes advantage of the script flexibility in defining logics, making possible implementation of any kind of statistic scenario. 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. variable (string) Name of a new statistic variable. The name may be followed by additional flag which describe the variable behavior: * no_reset : variable cannot be reset. Example 1.1. variable example modparam("statistics", "variable", "register_counter") modparam("statistics", "variable", "active_calls/no_reset") 1.4. Exported Functions 1.4.1. update_stat(variable,value) Updates the value of the statistic variable with the new value. Meaning of the parameters is as follows: * variable - variable to be updated (it can be a string or a pseudovariable); * value - value to update with; it may be also negative. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. Example 1.2. update_stat usage ... update_stat("register_counter", "+1"); ... $var(a_calls) = "active_calls"; update_stat("$var(a_calls)", "-1"); ... 1.4.2. reset_stat(variable) Resets to zero the value of the statistic variable. Meaning of the parameters is as follows: * variable - variable to be reset-ed (it can be a string or a pseudovariable). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. Example 1.3. reset_stat usage ... reset_stat("register_counter"); ... $var(reg_counter) = "register_counter"; update_stat("$var(reg_counter)"); ... 1.5. Exported pseudo-variables 1.5.1. $stat Allows to get and reset core and modules exported statistics. The pseudo-variable receives as parameter the name of the statistic that it want to get or reset. Example 1.4. $stat usage ... xlog("SHM used size = $stat(used_size) \n"); ... $stat(err_requests) = 0; ... opensips-2.2.2/modules/statistics/doc/000077500000000000000000000000001300170765700177725ustar00rootroot00000000000000opensips-2.2.2/modules/statistics/doc/statistics.xml000066400000000000000000000020551300170765700227100ustar00rootroot00000000000000 %docentities; ]> Statistics Module &osipsname; Bogdan Iancu
bogdan@opensips.org
Bogdan Iancu
bogdan@opensips.org
2006 &voicesystem; $Revision: 8740 $ $Date$
&admin; &faq;
opensips-2.2.2/modules/statistics/doc/statistics_admin.xml000066400000000000000000000101371300170765700240600ustar00rootroot00000000000000 &adminguide;
Overview The Statistics module is a wrapper over the internal statistics manager, allowing the script writer to dynamically define and use of statistic variables. By bringing the statistics support into the script, it takes advantage of the script flexibility in defining logics, making possible implementation of any kind of statistic scenario.
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>variable</varname> (string) Name of a new statistic variable. The name may be followed by additional flag which describe the variable behavior: no_reset : variable cannot be reset. variable example modparam("statistics", "variable", "register_counter") modparam("statistics", "variable", "active_calls/no_reset")
Exported Functions
<function moreinfo="none">update_stat(variable,value)</function> Updates the value of the statistic variable with the new value. Meaning of the parameters is as follows: variable - variable to be updated (it can be a string or a pseudovariable); value - value to update with; it may be also negative. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. <function>update_stat</function> usage ... update_stat("register_counter", "+1"); ... $var(a_calls) = "active_calls"; update_stat("$var(a_calls)", "-1"); ...
<function moreinfo="none">reset_stat(variable)</function> Resets to zero the value of the statistic variable. Meaning of the parameters is as follows: variable - variable to be reset-ed (it can be a string or a pseudovariable). This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. <function>reset_stat</function> usage ... reset_stat("register_counter"); ... $var(reg_counter) = "register_counter"; update_stat("$var(reg_counter)"); ...
Exported pseudo-variables
<varname>$stat</varname> Allows to get and reset core and modules exported statistics. The pseudo-variable receives as parameter the name of the statistic that it want to get or reset. <varname>$stat</varname> usage ... xlog("SHM used size = $stat(used_size) \n"); ... $stat(err_requests) = 0; ...
opensips-2.2.2/modules/statistics/statistics.c000066400000000000000000000302101300170765700215570ustar00rootroot00000000000000/* * statistics module - script interface to internal statistics manager * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-03-14 initial version (bogdan) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../ut.h" #include "../../mod_fix.h" #include "../../statistics.h" #include "../../mem/mem.h" #include "stats_funcs.h" static int reg_param_stat( modparam_t type, void* val); static int mod_init(void); static int w_update_stat(struct sip_msg* msg, char* stat, char* n); static int w_reset_stat(struct sip_msg* msg, char* stat, char* foo); static int fixup_stat(void** param, int param_no); int pv_parse_name(pv_spec_p sp, str *in); int pv_set_stat(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val); int pv_get_stat(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); #define STAT_PARAM_TYPE_STAT 1 #define STAT_PARAM_TYPE_NAME 2 #define STAT_PARAM_TYPE_PVAR 3 #define STAT_PARAM_TYPE_FMT 4 struct stat_param { unsigned int type; union { stat_var *stat; pv_spec_t *pvar; str *name; pv_elem_t *format; } u; }; static cmd_export_t cmds[]={ {"update_stat", (cmd_function)w_update_stat, 2, fixup_stat, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE| LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"reset_stat", (cmd_function)w_reset_stat, 1, fixup_stat, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE| LOCAL_ROUTE|STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {0,0,0,0,0,0} }; static param_export_t mod_params[]={ { "variable", STR_PARAM|USE_FUNC_PARAM, (void*)reg_param_stat }, { 0,0,0 } }; static pv_export_t mod_items[] = { { {"stat", sizeof("stat")-1}, 1100, pv_get_stat, pv_set_stat, pv_parse_name, 0, 0, 0}, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; struct module_exports exports= { "statistics", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ mod_params, /* param exports */ 0, /* exported statistics */ 0, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ 0, /* module destroy function */ 0 /* per-child init function */ }; static int reg_param_stat( modparam_t type, void* val) { return reg_statistic( (char*)val); } static int mod_init(void) { LM_INFO("initializing\n"); if (register_all_mod_stats()!=0) { LM_ERR("failed to register statistic variables\n"); return E_UNSPEC; } return 0; } static int fixup_stat(void** param, int param_no) { struct stat_param *sp; pv_elem_t *format; str s; long n; int err; s.s = (char*)*param; s.len = strlen(s.s); if (param_no==1) { /* reference to the statistic name */ sp = (struct stat_param *)pkg_malloc(sizeof(struct stat_param)); if (sp==NULL) { LM_ERR("no more pkg mem (%d)\n", (int)sizeof(struct stat_param)); return E_OUT_OF_MEM; } memset( sp, 0 , sizeof(struct stat_param) ); /* parse it */ if (pv_parse_format( &s, &sp->u.format)!=0) { LM_ERR("failed to parse statistic name format <%s> \n",s.s); return E_CFG; } format = sp->u.format; /* is it only one token ? */ if (format->next==NULL && (format->text.len==0 || format->spec.type==PVT_NONE)) { if (format->text.s && format->text.len) { /* text token */ sp->u.stat = get_stat( &format->text ); if (sp->u.stat) { /* statistic found */ sp->type = STAT_PARAM_TYPE_STAT; } else { /* stat not found, keep the name for later */ sp->type = STAT_PARAM_TYPE_NAME; sp->u.name = &format->text; } } else { /* pvar token */ sp->type = STAT_PARAM_TYPE_PVAR; sp->u.pvar = &format->spec; } /* we do not free "format" as we keep links inside of it! */ } else { /* if more tokens, keep the entire format */ sp->type = STAT_PARAM_TYPE_FMT; } /* do not free the original string, the "format" points inside ! */ *param=(void*)sp; return 0; } else if (param_no==2) { /* update value - integer */ if (s.s[0]=='-' || s.s[0]=='+') { n = str2s( s.s+1, s.len-1, &err); if (s.s[0]=='-') n = -n; } else { n = str2s( s.s, s.len, &err); } if (err==0){ if (n==0) { LM_ERR("update with 0 has no sense\n"); return E_CFG; } pkg_free(*param); *param=(void*)n; return 0; }else{ LM_ERR("bad update number <%s>\n",(char*)(*param)); return E_CFG; } } return 0; } static int w_update_stat(struct sip_msg *msg, char *stat_p, char *n) { struct stat_param *sp = (struct stat_param *)stat_p; pv_value_t pv_val; stat_var *stat; if (sp->type==STAT_PARAM_TYPE_STAT) { /* we have the statistic */ update_stat( sp->u.stat, (long)n); return 1; } if (sp->type==STAT_PARAM_TYPE_PVAR) { /* take name from PVAR */ if (pv_get_spec_value(msg, sp->u.pvar, &pv_val)!=0 || (pv_val.flags & PV_VAL_STR)==0 ) { LM_ERR("failed to get pv string value\n"); return -1; } } else if (sp->type==STAT_PARAM_TYPE_FMT) { /* take name from FMT */ if (pv_printf_s( msg, sp->u.format, &(pv_val.rs) )!=0 ) { LM_ERR("failed to get format string value\n"); return -1; } } else if (sp->type==STAT_PARAM_TYPE_NAME) { /* take name from STRING */ pv_val.rs = *sp->u.name; } LM_DBG("needed statistic is <%.*s>\n", pv_val.rs.len, pv_val.rs.s); /* name is in pv_val.rs -> look for it */ stat = get_stat( &(pv_val.rs) ); if ( stat==NULL ) { /* stats not found -> create it */ LM_DBG("creating dynamic statistic <%.*s>\n", pv_val.rs.len, pv_val.rs.s); if (register_dynamic_stat( &(pv_val.rs), &stat )!=0) { LM_ERR("failed to create dynamic statistic <%.*s>\n", pv_val.rs.len, pv_val.rs.s); return -1; } if (sp->type==STAT_PARAM_TYPE_NAME) { sp->u.stat = stat; sp->type=STAT_PARAM_TYPE_STAT; } } /* statistic exists ! */ update_stat( stat, (long)n); return 1; } static int w_reset_stat(struct sip_msg *msg, char* stat_p, char *foo) { struct stat_param *sp = (struct stat_param *)stat_p; pv_value_t pv_val; stat_var *stat; if (sp->type==STAT_PARAM_TYPE_STAT) { /* we have the statistic */ reset_stat( sp->u.stat); return 1; } if (sp->type==STAT_PARAM_TYPE_PVAR) { /* take name from PVAR */ if (pv_get_spec_value(msg, sp->u.pvar, &pv_val)!=0 || (pv_val.flags & PV_VAL_STR)==0 ) { LM_ERR("failed to get pv string value\n"); return -1; } } else if (sp->type==STAT_PARAM_TYPE_FMT) { /* take name from FMT */ if (pv_printf_s( msg, sp->u.format, &(pv_val.rs) )!=0 ) { LM_ERR("failed to get format string value\n"); return -1; } } else if (sp->type==STAT_PARAM_TYPE_NAME) { /* take name from STRING */ pv_val.rs = *sp->u.name; } /* name is in pv_val.rs -> look for it */ stat = get_stat( &(pv_val.rs) ); if ( stat==NULL ) { /* stats not found -> create it */ LM_DBG("creating dynamic statistic <%.*s>\n", pv_val.rs.len, pv_val.rs.s); if (register_dynamic_stat( &(pv_val.rs), &stat )!=0) { LM_ERR("failed to create dynamic statistic <%.*s>\n", pv_val.rs.len, pv_val.rs.s); return -1; } if (sp->type==STAT_PARAM_TYPE_NAME) { sp->u.stat = stat; sp->type=STAT_PARAM_TYPE_STAT; } } /* statistic exists ! */ reset_stat( stat ); return 1; } int pv_parse_name(pv_spec_p sp, str *in) { stat_var *stat; pv_elem_t *format; if(in==NULL || in->s==NULL || sp==NULL) return -1; LM_DBG("name %p with name <%.*s>\n", &sp->pvp.pvn, in->len, in->s); if (pv_parse_format( in, &format)!=0) { LM_ERR("failed to parse statistic name format <%.*s> \n", in->len,in->s); return -1; } /* text only ? */ if (format->next==NULL && format->spec.type==PVT_NONE) { /* search for the statistic */ stat = get_stat( &format->text ); if (stat==NULL) { /* statistic does not exist (yet) -> fill in the string name */ sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = AVP_NAME_STR; if (clone_pv_stat_name( in, &sp->pvp.pvn.u.isname.name.s )!=0) { LM_ERR("failed to clone name of statistic \n"); return -1; } LM_DBG("name %p, name cloned (in=%p, out=%p)\n", &sp->pvp.pvn, in->s, sp->pvp.pvn.u.isname.name.s.s); } else { /* link the stat pointer directly as dynamic name */ sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void*)stat; LM_DBG("name %p, stat found\n", &sp->pvp.pvn); } } else { sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = 0; /* not string */ sp->pvp.pvn.u.isname.name.s.s = (char*)(void*)format; sp->pvp.pvn.u.isname.name.s.len = 0; LM_DBG("name %p, stat name is FMT\n", &sp->pvp.pvn); } return 0; } static inline int get_stat_name(struct sip_msg* msg, pv_name_t *name, int create, stat_var **stat) { pv_value_t pv_val; /* is the statistic found ? */ if (name->type==PV_NAME_INTSTR) { LM_DBG("stat with name %p still not found\n", name); /* not yet :( */ /* do we have at least the name ?? */ if (name->u.isname.type==0) { /* name is FMT */ if (pv_printf_s( msg, (pv_elem_t *)name->u.isname.name.s.s, &(pv_val.rs) )!=0 || (pv_val.flags&PV_VAL_NULL) ) { LM_ERR("failed to get format string value\n"); return -1; } } else { /* name is string */ pv_val.rs = name->u.isname.name.s; } /* lookup for the statistic */ *stat = get_stat( &pv_val.rs ); LM_DBG("stat name %p (%.*s) after lookup is %p\n", name, pv_val.rs.len, pv_val.rs.s, *stat); if (*stat==NULL) { if (!create) return 0; LM_DBG("creating dynamic statistic <%.*s>\n", pv_val.rs.len, pv_val.rs.s); /* stats not found -> create it */ if (register_dynamic_stat( &pv_val.rs, stat )!=0) { LM_ERR("failed to create dynamic statistic <%.*s>\n", pv_val.rs.len, pv_val.rs.s); return -1; } } /* if name is static string, better link the stat directly * and discard name */ if (name->u.isname.type==AVP_NAME_STR) { LM_DBG("name %p freeing %p\n",name,name->u.isname.name.s.s); /* it is totally unsafe to free this shm block here, as it is * referred by the spec from all the processess. Even if we create * here a small leak (one time only), we do not have a better fix * until a final review of the specs in pkg and shm mem - bogdan */ //shm_free(name->u.isname.name.s.s); name->u.isname.name.s.s = NULL; name->u.isname.name.s.len = 0; name->type = PV_NAME_PVAR; name->u.dname = (void*)*stat; } } else { /* stat already found ! */ *stat = (stat_var*)name->u.dname; LM_DBG("stat name %p is founded\n",name); } return 0; } int pv_set_stat(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { stat_var *stat; if (get_stat_name( msg, &(param->pvn), 1, &stat)!=0) { LM_ERR("failed to generate/get statistic name\n"); return -1; } if (val != 0) LM_WARN("non-zero value - setting value to 0\n"); reset_stat( stat ); return 0; } int pv_get_stat(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { stat_var *stat; if(msg==NULL || res==NULL) return -1; if (get_stat_name( msg, &(param->pvn), 0, &stat)!=0) { LM_ERR("failed to generate/get statistic name\n"); return -1; } if (stat==NULL) return pv_get_null(msg, param, res); res->ri = (int)get_stat_val( stat ); res->rs.s = sint2str(res->ri, &res->rs.len); res->flags = PV_VAL_INT|PV_VAL_STR|PV_TYPE_INT; return 0; } opensips-2.2.2/modules/statistics/stats_funcs.c000066400000000000000000000045171300170765700217340ustar00rootroot00000000000000/* * statistics module - script interface to internal statistics manager * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-03-14 initial version (bogdan) */ #include #include "../../dprint.h" #include "../../statistics.h" #include "../../mem/mem.h" #include "stats_funcs.h" #define NORESET_FLAG_STR "no_reset" #define MODULE_STATS "script" typedef struct stat_mod_elem_ { char *name; int flags; struct stat_mod_elem_ *next; } stat_elem; static stat_elem *stat_list = 0; int reg_statistic( char* name) { stat_elem *se; char *flag_str; int flags; if (name==0 || *name==0) { LM_ERR("empty parameter\n"); goto error; } flags = 0; flag_str = strchr( name, '/'); if (flag_str) { *flag_str = 0; flag_str++; if (strcasecmp( flag_str, NORESET_FLAG_STR)==0) { flags |= STAT_NO_RESET; } else { LM_ERR("unsupported flag <%s>\n",flag_str); goto error; } } se = (stat_elem*)pkg_malloc( sizeof(stat_elem) ); if (se==0) { LM_ERR("no more pkg mem\n"); goto error; } se->name = name; se->flags = flags; se->next = stat_list; stat_list = se; return 0; error: return -1; } int register_all_mod_stats(void) { stat_elem *se; stat_elem *se_tmp; #ifdef STATISTICS stat_var *stat = NULL; #endif se = stat_list; while( se ) { se_tmp = se; se = se->next; /* register the new variable */ if (register_stat(MODULE_STATS, se_tmp->name, &stat, se_tmp->flags)!=0){ LM_ERR("failed to register var. <%s> flags %d\n", se_tmp->name,se_tmp->flags); return -1; } pkg_free(se_tmp); } return 0; } opensips-2.2.2/modules/statistics/stats_funcs.h000066400000000000000000000021131300170765700217270ustar00rootroot00000000000000/* * statistics module - script interface to internal statistics manager * * Copyright (C) 2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-03-14 initial version (bogdan) */ #ifndef _STATISTICS_STATS_FUNCS_ #define _STATISTICS_STATS_FUNCS_ int reg_statistic( char* name); int register_all_mod_stats(); #endif opensips-2.2.2/modules/stun/000077500000000000000000000000001300170765700160245ustar00rootroot00000000000000opensips-2.2.2/modules/stun/Makefile000066400000000000000000000001411300170765700174600ustar00rootroot00000000000000include ../../Makefile.defs auto_gen= NAME=stun.so LIBS= DEFS+= include ../../Makefile.modules opensips-2.2.2/modules/stun/README000066400000000000000000000113171300170765700167070ustar00rootroot00000000000000Stun Module Razvan Pistolea Edited by Razvan Pistolea Copyright © 2009 Voice Sistem SRL Revision History Revision $Revision:$ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. The idea 1.1.2. Basic Operation 1.1.3. Supported STUN Attributes 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. primary_ip (str) 1.3.2. primary_port (str) 1.3.3. alternate_ip (str) 1.3.4. alternate_port (str) 1.4. Exported MI Functions List of Examples 1.1. Set primary_ip parameter 1.2. Set primary_port parameter 1.3. Set alternate_ip parameter 1.4. Set alternate_port parameter Chapter 1. Admin Guide 1.1. Overview 1.1.1. The idea A stun server working with the same port as SIP (5060) in order to gain accurate information. The benefit would be an exact external address in the case of NATs translating differently when given different destination ports. The server may also advertise different network addresses than the ones it is actually listening on. 1.1.2. Basic Operation The stun server will use 4 sockets: * socket1 = ip1 : port1 * socket2 = ip1 : port2 * socket3 = ip2 : port1 * socket4 = ip2 : port2 The sockets come from existing SIP sockets or are created. Socket1 must allways be a SIP UDP listener from OpenSIPS. The server will create a separate process. This process will listen for data on created sockets. The server will register a callback function to SIP. This function is called when a specific (stun)header is found. 1.1.3. Supported STUN Attributes This stun implements RFC3489 (and XOR_MAPPED_ADDRESS from RFC5389) * MAPPED_ADDRESS * RESPONSE_ADDRESS * CHANGE_REQUEST * SOURCE_ADDRESS * CHANGED_ADDRESS * ERROR_CODE * UNKNOWN_ATTRIBUTES * REFLECTED_FROM * XOR_MAPPED_ADDRESS Not supported attributes: * USERNAME * PASSWORD * MESSAGE_INTEGRITY and associated ERROR_CODEs 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: None. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. primary_ip (str) The IP of an interface which is configured as an UDP SIP listener in OpenSIPS. This is a mandatory parameter. Syntax: "ip [/ advertised_ip] By default, the primary_ip and the advertised primary_ip will be identical. This may be changed with an optional "/ xxx.xxx.xxx.xxx" string. Example 1.1. Set primary_ip parameter ... modparam("stun", "primary_ip", "192.168.0.100") # Example of a STUN server within OpenSIPS which is behind NAT modparam("stun", "primary_ip", "192.168.0.100 / 64.50.46.78") ... 1.3.2. primary_port (str) The port configured (together with the primary_ip) as an UDP SIP listener in OpenSIPS. The default value is 5060. Syntax: "port [/ advertised_port] By default, the primary_port and the advertised primary_port will be identical. This may be changed with an optional "/ adv_port" string. Example 1.2. Set primary_port parameter ... modparam("stun", "primary_port", "5060") # Listening on a primary port, but advertising a different one modparam("stun", "primary_port", "5060 / 5062") ... 1.3.3. alternate_ip (str) Another IP from another interface. This is a mandatory parameter. Syntax: "ip [/ advertised_ip] By default, the alternate_ip and the advertised alternate_ip will be identical. This may be changed with an optional "/ xxx.xxx.xxx.xxx" string. Example 1.3. Set alternate_ip parameter ... modparam("stun","alternate_ip","11.22.33.44") # Example of a STUN server within OpenSIPS which is behind NAT modparam("stun", "alternate_ip", "192.168.0.100 / 64.78.46.50") ... 1.3.4. alternate_port (str) The port used by the STUN server for the second interface. The default value is 3478 (default STUN port). Syntax: "port [/ advertised_port] By default, the alternate_port and the advertised alternate_port will be identical. This may be changed with an optional "/ adv_port" string. Example 1.4. Set alternate_port parameter ... modparam("stun","alternate_port","3479") # Listening on an alternate port, but advertising a different one modparam("stun", "alternate_port", "5060 / 5062") ... 1.4. Exported MI Functions opensips-2.2.2/modules/stun/doc/000077500000000000000000000000001300170765700165715ustar00rootroot00000000000000opensips-2.2.2/modules/stun/doc/stun.xml000066400000000000000000000020361300170765700203050ustar00rootroot00000000000000 %docentities; ]> Stun Module &osipsname; Razvan Pistolea razvy000@yahoo.com Razvan Pistolea razvy000@yahoo.com 2009 &voicesystem; $Revision:$ $Date$ &admin; &faq; opensips-2.2.2/modules/stun/doc/stun_admin.xml000066400000000000000000000140561300170765700214620ustar00rootroot00000000000000 &adminguide;
Overview
The idea A stun server working with the same port as SIP (5060) in order to gain accurate information. The benefit would be an exact external address in the case of NATs translating differently when given different destination ports. The server may also advertise different network addresses than the ones it is actually listening on.
Basic Operation The stun server will use 4 sockets: socket1 = ip1 : port1 socket2 = ip1 : port2 socket3 = ip2 : port1 socket4 = ip2 : port2 The sockets come from existing SIP sockets or are created. Socket1 must allways be a SIP UDP listener from OpenSIPS. The server will create a separate process. This process will listen for data on created sockets. The server will register a callback function to SIP. This function is called when a specific (stun)header is found.
Supported STUN Attributes This stun implements RFC3489 (and XOR_MAPPED_ADDRESS from RFC5389) MAPPED_ADDRESS RESPONSE_ADDRESS CHANGE_REQUEST SOURCE_ADDRESS CHANGED_ADDRESS ERROR_CODE UNKNOWN_ATTRIBUTES REFLECTED_FROM XOR_MAPPED_ADDRESS Not supported attributes: USERNAME PASSWORD MESSAGE_INTEGRITY and associated ERROR_CODEs
Dependencies
&osips; Modules The following modules must be loaded before this module: None.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>primary_ip</varname> (str) The IP of an interface which is configured as an UDP SIP listener in &osips;. This is a mandatory parameter. Syntax: "ip [/ advertised_ip] By default, the primary_ip and the advertised primary_ip will be identical. This may be changed with an optional "/ xxx.xxx.xxx.xxx" string. Set <varname>primary_ip</varname> parameter ... modparam("stun", "primary_ip", "192.168.0.100") # Example of a STUN server within OpenSIPS which is behind NAT modparam("stun", "primary_ip", "192.168.0.100 / 64.50.46.78") ...
<varname>primary_port</varname> (str) The port configured (together with the primary_ip) as an UDP SIP listener in &osips;. The default value is 5060. Syntax: "port [/ advertised_port] By default, the primary_port and the advertised primary_port will be identical. This may be changed with an optional "/ adv_port" string. Set <varname>primary_port</varname> parameter ... modparam("stun", "primary_port", "5060") # Listening on a primary port, but advertising a different one modparam("stun", "primary_port", "5060 / 5062") ...
<varname>alternate_ip</varname> (str) Another IP from another interface. This is a mandatory parameter. Syntax: "ip [/ advertised_ip] By default, the alternate_ip and the advertised alternate_ip will be identical. This may be changed with an optional "/ xxx.xxx.xxx.xxx" string. Set <varname>alternate_ip</varname> parameter ... modparam("stun","alternate_ip","11.22.33.44") # Example of a STUN server within OpenSIPS which is behind NAT modparam("stun", "alternate_ip", "192.168.0.100 / 64.78.46.50") ...
<varname>alternate_port</varname> (str) The port used by the STUN server for the second interface. The default value is 3478 (default STUN port). Syntax: "port [/ advertised_port] By default, the alternate_port and the advertised alternate_port will be identical. This may be changed with an optional "/ adv_port" string. Set <varname>alternate_port</varname> parameter ... modparam("stun","alternate_port","3479") # Listening on an alternate port, but advertising a different one modparam("stun", "alternate_port", "5060 / 5062") ...
Exported MI Functions
opensips-2.2.2/modules/stun/stun.c000066400000000000000000001112711300170765700171640ustar00rootroot00000000000000/* * Copyright (C) 2009 Voice Sistem SRL * Copyright (C) 2009 Razvan * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2009-09-03 initial version (razvan) * 2014-03-04 added advertised IPs and ports (liviu) */ #include "../../sr_module.h" /* param_export_t, proc_export_t */ #include "../../net/proto_udp/proto_udp.h" /* register_udprecv_cb() */ #include "../../socket_info.h" /* grep_sock_info() */ #include "../../ip_addr.h" /* struct socket_info */ #include "../../str.h" /* str */ #include "../../trim.h" #include "stun.h" /* Globals */ struct socket_info* grep1 = NULL; struct socket_info* grep2 = NULL; struct socket_info* grep3 = NULL; struct socket_info* grep4 = NULL; int assign_once = FALSE; int sockfd1=-1; /* ip1 port1 */ int sockfd2=-1; /* ip1 port2 */ int sockfd3=-1; /* ip2 port1 */ int sockfd4=-1; /* ip2 port2 */ int ip1, ip2; int port1 = 5060, port2 = 3478; /* default SIP and STUN ports */ /* dot representation of the above IPs - for socket matching and printing */ char *primary_ip, *alternate_ip; /* different advertised IPs and ports, in case we're behind NAT */ int adv_ip1 = -1, adv_ip2 = -1; int adv_port1, adv_port2; /* Fixup functions */ int parse_primary_ip(modparam_t type, void *val); int parse_primary_port(modparam_t type, void *val); int parse_alternate_ip(modparam_t type, void *val); int parse_alternate_port(modparam_t type, void *val); /* * Exported parameters ip, port */ static param_export_t params[] = { {"primary_ip", STR_PARAM | USE_FUNC_PARAM, parse_primary_ip }, {"primary_port", STR_PARAM | USE_FUNC_PARAM, parse_primary_port }, {"alternate_ip", STR_PARAM | USE_FUNC_PARAM, parse_alternate_ip }, {"alternate_port", STR_PARAM | USE_FUNC_PARAM, parse_alternate_port }, { 0, 0, 0} }; /* Extra proces for listening loop */ static proc_export_t mod_procs[] = { {"Stun loop", 0, 0, stun_loop, 1 , 0}, {0,0,0,0,0,0} }; struct module_exports exports = { "stun", /* module's name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, /* module version */ DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ mod_procs, /* extra processes */ stun_mod_init, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ child_init /* per-child init function */ }; /* init */ int bind_ip_port(int ip, int port, int* sockfd){ struct sockaddr_in server; int rc; *sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if(*sockfd < 0){ LM_ERR("socket failed : %s\n",strerror(errno)); return -1; } memset(&server, 0, sizeof(server)); /* zero structure */ server.sin_family = AF_INET; /* internet address family */ server.sin_port = htons(port); server.sin_addr.s_addr = htonl(ip); /* bind to the local address */ rc = bind(*sockfd, (struct sockaddr *)&server, sizeof(server)); if(rc < 0){ LM_ERR("bind failed : %s\n",strerror(errno)); return -2; } return 0; } static int stun_mod_init(void) { str s; if (!primary_ip || primary_ip[0] == '\0') { LM_ERR("Primary IP was not configured!\n"); return -1; } if (!alternate_ip || alternate_ip[0] == '\0') { LM_ERR("Alternate IP was not configured!\n"); return -1; } if (adv_ip1 != -1 && adv_port1 == 0) adv_port1 = port1; if (adv_ip2 != -1 && adv_port2 == 0) adv_port1 = port2; s.s = primary_ip; s.len = strlen(primary_ip); grep1 = grep_sock_info(&s, (unsigned short)port1, PROTO_UDP); if(!grep1){ LM_ERR("IP1:port1 [%s:%d] not found in listening sockets\n", primary_ip, port1); return -1; } grep2 = grep_sock_info(&s, (unsigned short)port2, PROTO_UDP); if(!grep2){ LM_DBG("IP1:port2 [%s:%d] not found in listening sockets\n", primary_ip, port2); if (bind_ip_port(ip1, port2, &sockfd2)!=0) { LM_ERR("failed to bind for IP1:port2 [%s:%d]\n", primary_ip, port2); return -1; } } s.s = alternate_ip; s.len = strlen(alternate_ip); grep3 = grep_sock_info(&s, (unsigned short)port1, PROTO_UDP); if(!grep3){ LM_DBG("IP2:port1 [%s:%d] not found in listening sockets\n", alternate_ip, port1); if (bind_ip_port(ip2, port1, &sockfd3)!=0) { LM_ERR("failed to bind for IP2:port1 [%s:%d]\n", alternate_ip, port1); return -1; } } grep4 = grep_sock_info(&s, (unsigned short)port2, PROTO_UDP); if(!grep4){ LM_DBG("IP2:port2 [%s:%d] not found in listening sockets\n", alternate_ip, port2); if (bind_ip_port(ip2, port2, &sockfd4)!=0) { LM_ERR("failed to bind for IP2:port2 [%s:%d]\n", alternate_ip, port2); return -1; } } /* register for BINDING_REQUEST */ if (register_udprecv_cb(&receive, 0, (T8) (BINDING_REQUEST>>8), (T8)BINDING_REQUEST) != 0) { LM_ERR("failed to install UDP recv callback\n"); return -1; } LM_DBG("stun init succeeded\n"); return 0; } void stun_loop(int rank) { fd_set read_set, all_set; int maxfd; int nready; char buffer[65536]; str msg; unsigned int clientAddrLen; struct receive_info ri; FD_ZERO(&all_set); maxfd = MAX ( MAX(sockfd1, sockfd2), MAX(sockfd3, sockfd4)); LM_DBG("created sockets fd = %i %i %i %i (max = %i)\n", sockfd1, sockfd2, sockfd3, sockfd4, maxfd); sockfd1 = grep1->socket; if(grep2) sockfd2 = grep2->socket; else FD_SET(sockfd2, &all_set); if(grep3) sockfd3 = grep3->socket; else FD_SET(sockfd3, &all_set); if(grep4) sockfd4 = grep4->socket; else FD_SET(sockfd4, &all_set); LM_DBG("created and gained sockets fd = %i %i %i %i\n", sockfd1, sockfd2, sockfd3, sockfd4); /* this will never change as buffer is fixed */ msg.s = buffer; memset( &ri, 0, sizeof(ri) ); for(;;){ LM_DBG("READING\n"); read_set = all_set; nready = select(maxfd+1, &read_set, NULL, NULL, NULL); if (nready < 0) { if (errno != EINTR) LM_ERR("error in select %d(%s)\n", errno, strerror(errno)); continue; } if(FD_ISSET(sockfd2, &read_set)){ clientAddrLen = sizeof(struct sockaddr); msg.len = recvfrom(sockfd2, buffer, 65536, 0, (struct sockaddr *) &ri.src_su.sin, &clientAddrLen); receive(sockfd2, &ri, &msg, NULL); } if(FD_ISSET(sockfd3, &read_set)){ clientAddrLen = sizeof(struct sockaddr); msg.len = recvfrom(sockfd3, buffer, 65536, 0, (struct sockaddr *) &ri.src_su.sin, &clientAddrLen); receive(sockfd3, &ri, &msg, NULL); } if(FD_ISSET(sockfd4, &read_set)){ clientAddrLen = sizeof(struct sockaddr); msg.len = recvfrom(sockfd4, buffer, 65536, 0, (struct sockaddr *) &ri.src_su.sin, &clientAddrLen); receive(sockfd4, &ri, &msg, NULL); } } } static int child_init(int rank){ sockfd1 = grep1->socket; if(grep2) sockfd2 = grep2->socket; if(grep3) sockfd3 = grep3->socket; if(grep4) sockfd4 = grep4->socket; /*optimization if(getpid() < -5?){ close(sockfd1); close(sockfd2); close(sockfd3); close(sockfd4); } */ return 0; } /* receive */ int receive(int sockfd, struct receive_info *ri, str *msg, void* param) { struct sockaddr_in * client; Buffer recv_buffer; Buffer* resp_buffer; StunMsg* recv_msg; StunMsg* resp_msg; StunCtl ctl; char s[32]; client = (struct sockaddr_in *) &(ri->src_su.sin); /* info & checks*/ if(sockfd == sockfd1) sprintf(s, "%i %s %d", sockfd1, primary_ip, port1); else if(sockfd == sockfd2) sprintf(s, "%i %s %d", sockfd2, primary_ip, port2); else if(sockfd == sockfd3) sprintf(s, "%i %s %d", sockfd3, alternate_ip, port1); else if(sockfd == sockfd4) sprintf(s, "%i %s %d", sockfd4, alternate_ip, port2); else{ sprintf(s, "%i unknown %s %d", sockfd, alternate_ip, port2); LM_DBG("Received: on [%s] from [%s %i]; drop msg\n", s, inet_ntoa(client->sin_addr), ntohs(client->sin_port)); return -1; } LM_DBG("Received: on [%s] from [%s %i]\n", s, inet_ntoa(client->sin_addr), ntohs(client->sin_port)); LM_DBG("Message: size = %i, body = \n", msg->len); /* print_hex(msg->s, msg->len); */ /* deserialize */ recv_buffer.buffer = msg->s; recv_buffer.size = msg->len; recv_msg = deserialize(&recv_buffer); if(!recv_msg) /* received junk or out of mem */ return -1; LM_DBG("Received Message:\n"); printStunMsg(recv_msg); memset(&ctl,0,sizeof(StunCtl)); /* process */ ctl.srs = client; ctl.srs_size = sizeof(struct sockaddr);; ctl.sock_inbound = sockfd; resp_msg = process(recv_msg, &ctl); if(!resp_msg){ /* process junk or out of mem */ freeStunMsg(&recv_msg); if (ctl.dst && ctl.dst != client) pkg_free(ctl.dst); return -1; } LM_DBG("Send Message:\n"); printStunMsg(resp_msg); /* serialize */ resp_buffer = serialize(resp_msg); if (resp_buffer == NULL) { freeStunMsg(&recv_msg); freeStunMsg(&resp_msg); if (ctl.dst && ctl.dst != client) pkg_free(ctl.dst); LM_ERR("failed to get resp buffer\n"); return -1; } /* send */ if(ctl.sock_outbound == sockfd1) sprintf(s, "%i %s %d", sockfd1, primary_ip, port1); else if(ctl.sock_outbound == sockfd2) sprintf(s, "%i %s %d", sockfd2, primary_ip, port2); else if(ctl.sock_outbound == sockfd3) sprintf(s, "%i %s %d", sockfd3, alternate_ip, port1); else if(ctl.sock_outbound == sockfd4) sprintf(s, "%i %s %d", sockfd4, alternate_ip, port2); else sprintf(s, "%i unknown", ctl.sock_outbound); LM_DBG("Sending: from [%s] to [%s %i]\n", s, inet_ntoa(ctl.dst->sin_addr), ntohs(ctl.dst->sin_port)); sendto(ctl.sock_outbound, resp_buffer->buffer, resp_buffer->size, 0, (struct sockaddr *) ctl.dst, ctl.srs_size); LM_DBG("\n\n\n"); /* free */ if (ctl.dst && ctl.dst != client) pkg_free(ctl.dst); freeStunMsg(&recv_msg); freeStunMsg(&resp_msg); freeStunBuf(&resp_buffer); return 0; } /* deserialize */ int getTlvAttribute(IN_OUT Buffer* buf, IN_OUT StunMsg* msg){ /* * return number of bytes eaten * 0 ; ok * -1 attribute allready exists ; will be ignored * -1 non-mandatory unknown attribute ; will be ignored * -2 responce address familly != 0x01 ; drop msg * -3 attribute length overflows buffer ; drop msg * -4 attribute length does not corespond ; drop msg * -5 hmac attribute is not the last ; drop msg * -6 out of mem ; drop msg - server error */ T16 type; T16 len; int rc; char* b; /* iterator */ T16 * b2; rc = 0; b = (char*) buf->buffer; type = ntohs(*(T16 *) b); b+=2; len = ntohs(*(T16 *) b); b+=2; if(4 + len > buf->size){ LM_DBG("Attribute length overflows; drop msg\n"); return -3; } /* HMAC must be the last attribute */ msg->hmacIsLastAttribute = FALSE; switch(type){ case CHANGE_REQUEST: if(len != 4){ LM_DBG("Attribute length doest not correspond with type " "CHANGE_REQUEST; drop msg\n"); return -4; } if(!msg->hasChangeRequest){ msg->hasChangeRequest = TRUE; msg->changeRequestFlags = ntohl(*(T32*) b); b+=4; }else{ LM_DBG("Attribute CHANGE_REQUEST allready exists; " "ignore attribute\n"); rc = -1; } break; case RESPONSE_ADDRESS: if(len != 8){ LM_DBG("Attribute length doest not correspond with type " "RESPONSE_ADDRESS; drop msg\n"); return -4; } if(!msg->hasResponceAddress){ msg->hasResponceAddress = TRUE; msg->responceAddress = (StunAddr*) pkg_malloc(sizeof(StunAddr)); if(!msg->responceAddress) return -6; memset(msg->responceAddress, 0, sizeof(StunAddr)); msg->responceAddress->unused = *(char*) b; b+=1; msg->responceAddress->family = *(char*) b; b+=1; if(msg->responceAddress->family != 0x01){ LM_DBG("Responce address familly != 0x01\n"); rc = -2; } msg->responceAddress->port = ntohs( *(T16*) b); b+=2; msg->responceAddress->ip4 = ntohl( *(T32*) b); b+=4; }else{ LM_DBG("Attribute RESPONSE_ADDRESS allready exists; " "ignore attribute\n"); rc = -1; } break; case MESSAGE_INTEGRITY: if(len != 20){ LM_DBG("Attribute length doest not correspond with type " "MESSAGE_INTEGRITY; drop msg\n"); return -4; } if(!msg->hasMessageIntegrity){ msg->hasMessageIntegrity = TRUE; msg->hmacIsLastAttribute = TRUE; /* allocate Buffer hmac */ msg->hmac = (Buffer*) pkg_malloc(sizeof(Buffer)); if(!msg->hmac) return -6; memset(msg->hmac, 0, sizeof(Buffer)); /* allocate 20 bytes */ msg->hmac->buffer = (char*)pkg_malloc(20 * sizeof(char)); if(!msg->hmac->buffer) return -6; msg->hmac->size = 20; /* copy the 20 bytes */ memcpy(msg->hmac->buffer, b, 20); b+=20; }else{ LM_DBG("Attribute allready exists, will ignore attribute\n"); rc = -1; } /* check SHA1 and set errror TO DO */ break; default: if(type <= 0x7fff){ LM_DBG("Unknown mandatory attribute type = %i len = %i\n", type, len); /* set the errorCode to 420 if no other error exists */ if(!msg->hasErrorCode){ msg->hasErrorCode = TRUE; msg->errorCode = 420; } /* allocate unknownAttributes structure if first time*/ if(!msg->hasUnknownAttributes){ msg->hasUnknownAttributes = TRUE; /* allocate unknownAttributes Buffer */ msg->unknownAttributes = (Buffer*) pkg_malloc( sizeof(Buffer)); if(!msg->unknownAttributes) return -6; memset(msg->unknownAttributes, 0, sizeof(Buffer)); /* allocate array of unknownAttributes (12 should suffice)*/ msg->unknownAttributes->buffer = (char*) pkg_malloc( MAX_UNKNOWN_ATTRIBUTES * sizeof(T16)); if(!msg->unknownAttributes->buffer) return -6; memset(msg->unknownAttributes->buffer, 0, MAX_UNKNOWN_ATTRIBUTES * sizeof(T16)); /* size reprezents the serializable size of attributes*/ msg->unknownAttributes->size = 0; } /* address the buffer as a T16 array */ b2 = (T16*) msg->unknownAttributes->buffer; if(msg->unknownAttributes->size / sizeof(T16) < MAX_UNKNOWN_ATTRIBUTES){ b2[msg->unknownAttributes->size / sizeof(T16)] = type; msg->unknownAttributes->size += sizeof(T16); } }else{ LM_DBG("Unknown non-mandatory attribute type = %i len = %i; " "will ignore\n", type, len); rc = -1; } break; } /* HMAC must be the last attribute */ if(msg->hasMessageIntegrity && !msg->hmacIsLastAttribute){ LM_DBG("HMAC attribute is not last; drop msg\n"); rc = -5; } /* remaining size and pointer */ buf->size -= (4+len); /* |type + len + value| = 2 + 2 + len */ buf->buffer = b; return rc; } StunMsg* deserialize(IN Buffer* buffer){ /* * return NULL if out of memory or not stun message * return StunMsg* even if mallformed (because it must receive a reply) */ int rc; char* b; Buffer remain; StunMsg* msg; /* b is iterator */ b = (char*) buffer->buffer; /* allocate returned structure */ msg = (StunMsg*) pkg_malloc(sizeof(StunMsg)); if(!msg){ LM_ERR("out of mem\n"); goto error; } memset(msg, 0, sizeof(StunMsg)); /* check if message has at least the 20 bits header */ if(buffer->size < 20){ LM_WARN("Buff size < 20\n"); goto error; } /* message type */ msg->type = ntohs(* (T16*) b); b+=2; /* message length (without header) */ msg->len = ntohs(* (T16*)b); b+=2; /* message unique id */ msg->id = (char*) pkg_malloc(16*sizeof(char)); if(!msg){ LM_DBG("out of mem\n"); goto error; } memcpy(msg->id, b, 16); b+=16; /* remaining buffer to be parsed for the list of attributes */ remain.buffer = b; remain.size = buffer->size - (b - buffer->buffer); /* = buffer->size -20 */ /* each tlv attribute */ while(remain.size){ rc = getTlvAttribute(&remain, msg); if(-5 <= rc && rc<=-2){ msg->hasErrorCode = TRUE; msg->errorCode = 400; /* bad(malformed) message */ break; }else if(rc == -6){ LM_DBG("out of mem\n"); goto error; } } return msg; error: freeStunMsg(&msg); return NULL; } /* process */ int addError(IN unsigned int errorCode, IN char* errorName, OUT Buffer* dest){ int len = strlen(errorName); dest->buffer = (char*) pkg_malloc(4 + len + 1); if(!dest->buffer){ LM_DBG("out of mem\n"); return -1; } dest->size = 4 + len; snprintf(dest->buffer, dest->size + 1,"%c%c%c%c%.*s", 0 ,0, (errorCode / 100) & 0x07, errorCode % 100, len, errorName); return dest->size; } int addTlvAttribute(IN_OUT StunMsg* msg , IN StunMsg* srs_msg, IN int type, IN_OUT StunCtl* ctl){ int i; T16* b2; /* iterator */ T32 cookie; int rc = -1; struct sockaddr_in* alternate_dst; switch(type){ case MAPPED_ADDRESS: msg->mappedAddress = (StunAddr* ) pkg_malloc(sizeof(StunAddr)); if(!msg->mappedAddress){ LM_DBG("out of mem\n"); return -1; } msg->hasMappedAddress = TRUE; msg->mappedAddress->unused = 0; msg->mappedAddress->family = 0x01; msg->mappedAddress->port = ntohs(ctl->srs->sin_port); msg->mappedAddress->ip4 = ntohl(ctl->srs->sin_addr.s_addr); return 2 + 2 + 8; case SOURCE_ADDRESS: msg->sourceAddress = (StunAddr*) pkg_malloc(sizeof(StunAddr)); if(!msg->sourceAddress){ LM_DBG("out of mem\n"); return -1; } msg->hasSourceAddress = TRUE; msg->sourceAddress->unused = 0; msg->sourceAddress->family = 0x01; if(ctl->sock_outbound == sockfd1){ msg->sourceAddress->ip4 = ADV_IP(ip1, adv_ip1); msg->sourceAddress->port = ADV_PORT(port1, adv_port1); }else if(ctl->sock_outbound == sockfd2){ msg->sourceAddress->ip4 = ADV_IP(ip1, adv_ip1); msg->sourceAddress->port = ADV_PORT(port2, adv_port2); }else if(ctl->sock_outbound == sockfd3){ msg->sourceAddress->ip4 = ADV_IP(ip2, adv_ip2); msg->sourceAddress->port = ADV_PORT(port1, adv_port1); }else if(ctl->sock_outbound == sockfd4){ msg->sourceAddress->ip4 = ADV_IP(ip2, adv_ip2); msg->sourceAddress->port = ADV_PORT(port2, adv_port2); } return 2 + 2 + 8; case CHANGED_ADDRESS: msg->changedAddress = (StunAddr*) pkg_malloc(sizeof(StunAddr)); if(!msg->changedAddress){ LM_DBG("out of mem\n"); return -1; } msg->hasChangedAddress = TRUE; msg->changedAddress->unused = 0; msg->changedAddress->family = 0x01; /* ip port inverse t1 = sockfd1; 0 0 1 1 t2 = sockfd2; 0 1 1 0 t3 = sockfd3; 1 0 0 1 t4 = sockfd4; 1 1 0 0 it is the inverse ip and port on whitch it was received 1 >< 4 ; 2 >< 3 */ if(ctl->sock_inbound == sockfd1){ msg->changedAddress->ip4 = ADV_IP(ip2, adv_ip2); msg->changedAddress->port = ADV_PORT(port2, adv_port2); }else if(ctl->sock_inbound == sockfd2){ msg->changedAddress->ip4 = ADV_IP(ip2, adv_ip2); msg->changedAddress->port = ADV_PORT(port1, adv_port1); }else if(ctl->sock_inbound == sockfd3){ msg->changedAddress->ip4 = ADV_IP(ip1, adv_ip1); msg->changedAddress->port = ADV_PORT(port2, adv_port2); }else if(ctl->sock_inbound == sockfd4){ msg->changedAddress->ip4 = ADV_IP(ip1, adv_ip1); msg->changedAddress->port = ADV_PORT(port1, adv_port1); } return 2 + 2 + 8; case REFLECTED_FROM: /* where the response should be sent */ alternate_dst = (struct sockaddr_in*) pkg_malloc( sizeof(struct sockaddr_in)); if(!alternate_dst){ LM_DBG("out of mem\n"); return -1; } memset(alternate_dst, 0, sizeof(struct sockaddr_in)); alternate_dst->sin_family = AF_INET; alternate_dst->sin_port = htons(srs_msg->responceAddress->port); alternate_dst->sin_addr.s_addr = htonl( srs_msg->responceAddress->ip4); ctl->dst = alternate_dst; /* same as mapped address */ msg->reflectedFromAddress = (StunAddr*) pkg_malloc( sizeof(StunAddr)); if(!msg->reflectedFromAddress){ LM_DBG("out of mem\n"); return -1; } msg->hasReflectedFrom = TRUE; msg->reflectedFromAddress->unused = 0; msg->reflectedFromAddress->family = 0x01; msg->reflectedFromAddress->port = ntohs(ctl->srs->sin_port); msg->reflectedFromAddress->ip4 = ntohl(ctl->srs->sin_addr.s_addr); return 2 + 2 + 8; case XOR_MAPPED_ADDRESS: cookie = ntohl(*(T32*) msg->id); msg->xorMappedAddress = (StunAddr* ) pkg_malloc(sizeof(StunAddr)); if(!msg->xorMappedAddress){ LM_DBG("out of mem\n"); return -1; } msg->hasXorMappedAddress = TRUE; msg->xorMappedAddress->unused = 0; msg->xorMappedAddress->family = 0x01; msg->xorMappedAddress->port = ntohs( ctl->srs->sin_port) ^ (T16)(cookie>>16); msg->xorMappedAddress->ip4 = ntohl( ctl->srs->sin_addr.s_addr) ^ cookie; return 2 + 2 + 8; case ERROR_CODE: msg->errorReason = (Buffer*) pkg_malloc(sizeof(Buffer)); if(!msg->errorReason){ LM_DBG("out of mem\n"); return -1; } memset(msg->errorReason, 0, sizeof(Buffer)); switch(msg->errorCode){ /* remember multiple of 4 bytes */ case 400: rc = addError(400, "Bad Request ", msg->errorReason); break; case 420: rc = addError(420, "Unknown Attribute ", msg->errorReason); break; case 500: rc = addError(500, "Server Error", msg->errorReason); break; case 600: rc = addError(600, "Global Failure ", msg->errorReason); break; } if(rc < 0) return -1; else msg->hasErrorCode = TRUE; return 2 + 2 + rc; case UNKNOWN_ATTRIBUTES: /* allocate unknownAttributes buffer */ msg->unknownAttributes = (Buffer*) pkg_malloc(sizeof(Buffer)); if(!msg->unknownAttributes){ LM_DBG("out of mem\n"); return -1; } memset(msg->unknownAttributes, 0, sizeof(Buffer)); /* the number of unknown attributes must be even */ /* urmatorul numar mai mare sau egal cu X multiplu de N este * (X + N -1 ) / N * N */ if((srs_msg->unknownAttributes->size / sizeof(T16)) %2 == 0 ) msg->unknownAttributes->size = srs_msg->unknownAttributes->size; else msg->unknownAttributes->size = srs_msg->unknownAttributes->size + sizeof(T16); /* allocate buffer */ msg->unknownAttributes->buffer = (char*) pkg_malloc(msg->unknownAttributes->size); if(!msg->unknownAttributes->buffer){ LM_DBG("out of mem\n"); return -1; } /* just copy the unknown from the deserialized message */ memcpy(msg->unknownAttributes->buffer, srs_msg->unknownAttributes->buffer, srs_msg->unknownAttributes->size); /* iterator */ b2 = (T16 *) msg->unknownAttributes->buffer; /* if number of unknws is not even; duplicate the last one */ if((srs_msg->unknownAttributes->size / sizeof(T16)) % 2 == 1) b2[msg->unknownAttributes->size/sizeof(T16)-1] = b2[ msg->unknownAttributes->size/sizeof(T16)-1 -1]; /* convert to network order */ for(i=0; i < msg->unknownAttributes->size/sizeof(T16); i++) b2[i] = ntohs(b2[i]); msg->hasUnknownAttributes = TRUE; return 2 + 2 + msg->unknownAttributes->size; } return -1; } void swap(IN_OUT int* a, IN_OUT int* b){ int t = *a; *a = *b; *b = t; } StunMsg* process(IN StunMsg* msg, IN_OUT StunCtl* ctl){ int rc; int t1, t2, t3, t4; /* used for socket swapping */ StunMsg* rmsg; /* returned response message */ if(!msg){ LM_DBG("error NULL msg\n"); return NULL; } /* allocate responce message */ rmsg = (StunMsg* ) pkg_malloc(sizeof(StunMsg)); if(!rmsg){ LM_DBG("out of mem\n"); return NULL; } memset(rmsg, 0, sizeof(StunMsg)); /* this server treats just the BINDING requsts */ if(msg->type == BINDING_REQUEST){ /* type & id */ rmsg->type = BINDING_RESPONCE; rmsg->len = 0; /* allocate id */ rmsg->id = (char*) pkg_malloc(16*sizeof(char)); if(!rmsg->id){ LM_DBG("out of mem\n"); goto error; } /* response must have the same id as request*/ memcpy(rmsg->id, msg->id, 16); /* if has change ip, port request */ if(msg->hasChangeRequest && !msg->hasErrorCode){ /* ip port */ t1 = sockfd1; /* 0 0 */ t2 = sockfd2; /* 0 1 */ t3 = sockfd3; /* 1 0 */ t4 = sockfd4; /* 1 1 */ /* LM_DBG("process()1 t1=%i t2=%i t3=%i t4=%i\n", t1, t2, t3, t4); */ /* outbound depends on INBOUND and on REQUEST_FLAGS */ /* eliminate INBOUND dependency */ if(ctl->sock_inbound == t1){ }else if(ctl->sock_inbound == t2){ /* swap ports - mentain ips */ swap(&t1, &t2); swap(&t3, &t4); }else if(ctl->sock_inbound == t3){ /* swap ips -mentain port */ swap(&t1, &t3); swap(&t2, &t4); }else if(ctl->sock_inbound == t4){ /* swap ips and ports */ swap(&t1, &t2); swap(&t3, &t4); swap(&t1, &t3); swap(&t2, &t4); } /* LM_DBG("process()2 t1=%i t2=%i t3=%i t4=%i\n", t1, t2, t3, t4); */ /* eliminate REQUEST_FLAGS dependency */ if(msg->changeRequestFlags & CHANGE_IP){ /* swap ips -mentain port */ swap(&t1, &t3); swap(&t2, &t4); } if(msg->changeRequestFlags & CHANGE_PORT){ /* swap ports - mentain ips */ swap(&t1, &t2); swap(&t3, &t4); } /* LM_DBG("process()3 t1=%i t2=%i t3=%i t4=%i\n", t1, t2, t3, t4); */ ctl->sock_outbound = t1; }else{ ctl->sock_outbound = ctl->sock_inbound; } /* if it has errors send a BINDING_ERROR responce */ if(msg->hasErrorCode){ /* send back */ ctl->dst = ctl->srs; /* type */ rmsg->type = BINDING_ERROR; /* mandatory ERROR_CODE attribute */ rmsg->errorCode = msg->errorCode; rc=addTlvAttribute(rmsg, msg, ERROR_CODE, ctl); if(rc<0){ LM_DBG("error at ERROR_CODE\n"); goto error; } rmsg->len+=rc; /* conditional list of UNKNOWN_ATTRIBUTES */ if(msg->hasUnknownAttributes){ rc=addTlvAttribute(rmsg, msg, UNKNOWN_ATTRIBUTES, ctl); if(rc<0){ LM_DBG("error at UNKNOWN_ATTRIBUTES\n"); goto error; } rmsg->len+=rc; } /* even if it has CHANGE_REQUEST, * the error response is send to source */ return rmsg; } if(msg->hasResponceAddress){ rc=addTlvAttribute(rmsg, msg, REFLECTED_FROM, ctl); if(rc<0){ LM_DBG("error at REFLECTED_FROM\n"); goto error; } rmsg->len+=rc; }else{ ctl->dst = ctl->srs; } /* add atributes */ rc=addTlvAttribute(rmsg, msg, MAPPED_ADDRESS, ctl); if(rc<0){ LM_DBG("error at MAPPED_ADDRESS\n"); goto error; } rmsg->len+=rc; rc=addTlvAttribute(rmsg, msg, SOURCE_ADDRESS, ctl); if(rc<0){ LM_DBG("error at SOURCE_ADDRESS\n"); goto error; } rmsg->len+=rc; rc=addTlvAttribute(rmsg, msg, CHANGED_ADDRESS, ctl); if(rc<0){ LM_DBG("error at CHANGED_ADDRESS\n"); goto error; } rmsg->len+=rc; rc=addTlvAttribute(rmsg, msg, XOR_MAPPED_ADDRESS, ctl); if(rc<0){ LM_DBG("error at XOR_MAPPED_ADDRESS\n"); goto error; } rmsg->len+=rc; }else{ pkg_free(rmsg); return NULL; } return rmsg; error: freeStunMsg(&rmsg); return NULL; } /* serialize */ int serializeStunBuffer(OUT char* b, IN T16 type, IN Buffer* buf){ T16 netorder = htons(type); T16 netlen = htons(buf->size); /* TYPE */ memcpy(b, &netorder, 2); b+=2; /* LENGTH */ memcpy(b, &netlen, 2); b+=2; /* VALUE */ memcpy(b, buf->buffer, buf->size); b+=buf->size; return 4 + buf->size; } int serializeStunAddr(OUT char* b, IN T16 type, IN_OUT StunAddr* addr){ T16 netorder = htons(type); T16 netlen = htons(8); /* TYPE */ memcpy(b, &netorder, 2); b+=2; /* LENGTH */ memcpy(b, &netlen, 2); b+=2; /* VALUE */ memcpy(b, &addr->unused, 1); b+=1; memcpy(b, &addr->family, 1); b+=1; addr->port = htons(addr->port); memcpy(b, &addr->port, 2); b+=2; addr->ip4 = htonl(addr->ip4); memcpy(b, &addr->ip4, 4); b+=4; return 12; } Buffer* serialize(IN StunMsg* msg){ char* b; Buffer* buffer; /* allocate the returned Buffer */ buffer = (Buffer* ) pkg_malloc(sizeof(Buffer)); if(!buffer){ LM_DBG("out of mem\n"); return NULL; } memset(buffer, 0 , sizeof(Buffer)); /* set size */ buffer->size = msg->len +20; /* allocate contents */ buffer->buffer = (char*) pkg_malloc(buffer->size * sizeof(char)); if(!buffer->buffer){ LM_DBG("out of mem\n"); pkg_free(buffer); return NULL; } memset(buffer->buffer, 0, buffer->size * sizeof(char)); /* iterator b */ b = buffer->buffer; /* type */ msg->type = htons(msg->type); memcpy(b, &msg->type, 2); b+=2; /* len */ msg->len = htons(msg->len); memcpy(b, &msg->len, 2); b+=2; /* id */ memcpy(b, msg->id, 16); b+=16; /* list of attributes */ if(msg->hasMappedAddress) b+=serializeStunAddr(b, MAPPED_ADDRESS, msg->mappedAddress); if(msg->hasSourceAddress) b+=serializeStunAddr(b, SOURCE_ADDRESS, msg->sourceAddress); if(msg->hasChangedAddress) b+=serializeStunAddr(b, CHANGED_ADDRESS, msg->changedAddress); if(msg->hasXorMappedAddress) b+=serializeStunAddr(b, XOR_MAPPED_ADDRESS, msg->xorMappedAddress); if(msg->hasReflectedFrom) b+=serializeStunAddr(b, REFLECTED_FROM, msg->reflectedFromAddress); if(msg->hasErrorCode) b+=serializeStunBuffer(b, ERROR_CODE, msg->errorReason); if(msg->hasUnknownAttributes) b+=serializeStunBuffer(b, UNKNOWN_ATTRIBUTES, msg->unknownAttributes); return buffer; } /* free */ void freeStunMsg(IN_OUT StunMsg** msg){ if(*msg){ LM_DBG("freeing\n"); /* char* */ if((*msg)->id){ pkg_free((*msg)->id); } /* StunAddr */ if((*msg)->mappedAddress){ pkg_free((*msg)->mappedAddress); } if((*msg)->responceAddress){ pkg_free((*msg)->responceAddress); } if((*msg)->sourceAddress){ pkg_free((*msg)->sourceAddress); } if((*msg)->changedAddress){ pkg_free((*msg)->changedAddress); } if((*msg)->reflectedFromAddress){ pkg_free((*msg)->reflectedFromAddress); } if((*msg)->xorMappedAddress){ pkg_free((*msg)->xorMappedAddress); } /* Buffer */ if((*msg)->username){ if((*msg)->username->buffer){ pkg_free((*msg)->username->buffer); } pkg_free((*msg)->username); } if((*msg)->password){ if((*msg)->password->buffer){ pkg_free((*msg)->password->buffer); } pkg_free((*msg)->password); } if((*msg)->hmac){ if((*msg)->hmac->buffer){ pkg_free((*msg)->hmac->buffer); } pkg_free((*msg)->hmac); } if((*msg)->unknownAttributes){ if((*msg)->unknownAttributes->buffer){ pkg_free((*msg)->unknownAttributes->buffer); } pkg_free((*msg)->unknownAttributes); } if((*msg)->errorReason){ if((*msg)->errorReason->buffer){ pkg_free((*msg)->errorReason->buffer); } pkg_free((*msg)->errorReason); } /* StunMsg */ pkg_free(*msg); *msg = NULL; } } void freeStunBuf(IN_OUT Buffer** buffer){ if(*buffer){ if((*buffer)->buffer){ pkg_free((*buffer)->buffer); } pkg_free(*buffer); *buffer = NULL; } } /* print */ void printStunAddr(StunAddr* addr){ struct in_addr ip; UNUSED(ip); ip.s_addr = htonl(addr->ip4); LM_DBG("\t\t\tUnused = %02X\n", addr->unused); if(addr->family == 0x01){ LM_DBG("\t\t\tFamily = %02X (IPv4)\n", addr->family); }else{ LM_DBG("\t\t\tFamily = %02X\n", addr->family); } LM_DBG("\t\t\tPort = %hu\n", addr->port); LM_DBG("\t\t\tIPv4 = %s\n", inet_ntoa(ip)); } void printStunMsg(StunMsg* msg){ int i; char* s; //char s2[16]; T16* val; UNUSED(s); UNUSED(val); switch(msg->type){ case BINDING_REQUEST: s = "BINDING_REQUEST"; break; case BINDING_RESPONCE: s = "BINDING_RESPONCE"; break; case BINDING_ERROR: s = "BINDING_ERROR"; break; default: s = "UNKNOWN_STUN_TYPE"; } LM_DBG("\tType = %s\n", s); LM_DBG("\tLen = %i\n", msg->len); val = (T16*)msg->id; if(0x1234 == ntohs(0x3412)){ LM_DBG("\tID = %04hX%04hX%04hX%04hX%04hX%04hX%04hX%04hX\n\n", ntohs(val[0]),ntohs(val[1]),ntohs(val[2]),ntohs(val[3]), ntohs(val[4]),ntohs(val[5]),ntohs(val[6]),ntohs(val[7])); }else{ LM_DBG("\tID = %04hX%04hX%04hX%04hX%04hX%04hX%04hX%04hX\n\n", val[0],val[1],val[2],val[3], val[4],val[5],val[6],val[7]); } LM_DBG("\tAttributes:\n"); if(msg->hasMappedAddress){ LM_DBG("\t\tMAPPED_ADDRESS\n"); printStunAddr(msg->mappedAddress); } if(msg->hasChangedAddress){ LM_DBG("\t\tCHANGED_ADDRESS\n"); printStunAddr(msg->changedAddress); } if(msg->hasSourceAddress){ LM_DBG("\t\tSOURCE_ADDRESS\n"); printStunAddr(msg->sourceAddress); } if(msg->hasResponceAddress){ LM_DBG("\t\tRESPONCE_ADDRESS\n"); printStunAddr(msg->responceAddress); } if(msg->hasChangeRequest){ LM_DBG("\t\tCHANGE_REQUEST\n"); if(msg->changeRequestFlags & CHANGE_IP) LM_DBG("\t\t\tCHANGE_IP\n"); if(msg->changeRequestFlags & CHANGE_PORT) LM_DBG("\t\t\tCHANGE_PORT\n"); } if(msg->hasXorMappedAddress){ LM_DBG("\t\tXOR_MAPPED_ADDRESS\n"); printStunAddr(msg->xorMappedAddress); } if(msg->hasReflectedFrom){ LM_DBG("\t\tREFLECTED_FROM_ADDRESS\n"); printStunAddr(msg->reflectedFromAddress); } if(msg->hasErrorCode){ LM_DBG("\t\tERROR_CODE\n"); if(msg->errorReason){ LM_DBG("\t\t\tCLASS = %u\n", msg->errorReason->buffer[2]); LM_DBG("\t\t\tNUMBER = %u\n", msg->errorReason->buffer[3]); LM_DBG("\t\t\tSTRING = %.*s\n", msg->errorReason->size - 4, &(msg->errorReason->buffer[4])); } } if(msg->hasUnknownAttributes){ LM_DBG("\t\tUNKNOWN_ATTRIBUTES\n"); val = (T16*) msg->unknownAttributes->buffer; for(i=0; i< msg->unknownAttributes->size / sizeof(T16); i++){ LM_DBG("\t\t\tATTRIBUTE[%i] = %i\n", i, val[i]); } } } void print_hex(IN char* buffer, IN int size){ int i; T16* t16 = (T16*) buffer; UNUSED(t16); for(i=0; i /* printf, fprintf*/ #include /* socket, bind */ #include /* sockaddr_in, inet_ntoa*/ #include /* atoi */ #include /* memset */ #include #include /* close */ #include #include /* int16 */ #include #include /* T8 T16 T32 */ /* defs */ #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define IN #define OUT #define IN_OUT #define MAX_UNKNOWN_ATTRIBUTES 12 #define ADV_IP(ip, adv_ip) (adv_ip != -1 ? adv_ip : ip) #define ADV_PORT(port, adv_port) (adv_port ? adv_port : port) /* types */ typedef char Bool; typedef char T8; typedef uint16_t T16; typedef uint32_t T32; /* enums */ typedef enum { FALSE = 0, TRUE =1 } Boolean; typedef enum { BINDING_REQUEST = 0x0001, BINDING_RESPONCE = 0x0101, BINDING_ERROR = 0x0111, SHARED_REQUEST = 0x0002, SHARED_RESPONCE = 0x0102, SHARED_ERROR = 0x0112 } Methods; typedef enum { MAPPED_ADDRESS = 0x0001, RESPONSE_ADDRESS = 0x0002, CHANGE_REQUEST = 0x0003, SOURCE_ADDRESS = 0x0004, CHANGED_ADDRESS = 0x0005, USERNAME = 0x0006, PASSWORD = 0x0007, MESSAGE_INTEGRITY = 0x0008, ERROR_CODE = 0x0009, UNKNOWN_ATTRIBUTES = 0x000a, REFLECTED_FROM = 0x000b, /* rfc 5389 */ REALM = 0x0014, NONCE = 0x0015, XOR_MAPPED_ADDRESS = 0x8020, SOFTWARE = 0x8022, ALTERNATE_SERVER = 0x8023, FINGERPRINT = 0x8028, /*BOGUS = 0xB0B0 */ BOGUS = 0x00BB } Attributes; typedef enum{ CHANGE_PORT = 0x0002, CHANGE_IP = 0x0004 }CHANGE_REQUEST_FLAGS; /* structs */ typedef struct buffer{ char* buffer; int size; }Buffer; typedef struct family_port_ip4{ T8 unused; /* host order */ T8 family; /* host order */ T16 port; /* host order */ T32 ip4; /* host order */ }StunAddr; typedef struct stun_message{ T16 type; /* host order */ T16 len; /* host order */ char* id; Bool hasMappedAddress; StunAddr* mappedAddress; /* host order */ Bool hasResponceAddress; StunAddr* responceAddress; Bool hasSourceAddress; StunAddr* sourceAddress; Bool hasChangedAddress; StunAddr* changedAddress; Bool hasReflectedFrom; StunAddr* reflectedFromAddress; Bool hasXorMappedAddress; StunAddr* xorMappedAddress; Bool hasChangeRequest; T32 changeRequestFlags; Bool hasUsername; Buffer* username; Bool hasPassword; Buffer* password; Bool hasMessageIntegrity; Bool hmacIsLastAttribute; Buffer* hmac; Bool hasUnknownAttributes; Buffer* unknownAttributes; Bool hasErrorCode; T32 errorCode; Buffer* errorReason; }StunMsg; typedef struct stun_controll{ /* inbound receivedFrom sockaddr_in receivedWhere sock_inbound, size outbound sendTo sockaddr_in sendFrom sock_outbound */ struct sockaddr_in* srs; int srs_size; int sock_inbound; struct sockaddr_in* dst; int sock_outbound; int upd_tcp_tls; }StunCtl; /* init */ int bind_ip_port(int ip, int port, int* sockfd); static int stun_mod_init(void); void stun_loop(int rank); static int child_init(int rank); /* receive */ int receive(int sockfd, struct receive_info *ri, str *msg, void* param); /* deserialize */ int getTlvAttribute(IN_OUT Buffer* buf, IN_OUT StunMsg* msg); StunMsg* deserialize(IN Buffer* buf); /* process */ int addError(IN unsigned int errorCode, IN char* errorName, OUT Buffer* dest); int addTlvAttribute(IN_OUT StunMsg* msg, IN StunMsg* srs_msg, IN int type, IN_OUT StunCtl* ctl); StunMsg* process(IN StunMsg* msg, IN_OUT StunCtl* ctl); /* serialize */ int serializeStunBuffer(OUT char* b, IN T16 type, IN Buffer* buf); int serializeStunAddr(OUT char* b, IN T16 type, IN_OUT StunAddr* addr); Buffer* serialize(IN StunMsg* msg); /* free */ void freeStunMsg(IN_OUT StunMsg** msg); void freeStunBuf(IN_OUT Buffer** buffer); /* print */ void print_hex(char* buffer, int size); void printStunAddr(StunAddr* addr); void printStunMsg(StunMsg* msg); #endif /* _STUFF_H */ opensips-2.2.2/modules/textops/000077500000000000000000000000001300170765700165415ustar00rootroot00000000000000opensips-2.2.2/modules/textops/Makefile000066400000000000000000000003311300170765700201760ustar00rootroot00000000000000# $Id$ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=textops.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/textops/README000066400000000000000000000232241300170765700174240ustar00rootroot00000000000000textops Module Andrei Pelinescu-Onciul FhG FOKUS Edited by Andrei Pelinescu-Onciul Edited by Daniel-Constantin Mierla Edited by Ovidiu Sas Copyright © 2003 FhG FOKUS Revision History Revision $Revision: 7480 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Known Limitations 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Functions 1.3.1. search(re) 1.3.2. search_body(re) 1.3.3. search_append(re, txt) 1.3.4. search_append_body(re, txt) 1.3.5. replace(re, txt) 1.3.6. replace_body(re, txt) 1.3.7. replace_all(re, txt) 1.3.8. replace_body_all(re, txt) 1.3.9. replace_body_atonce(re, txt) 1.3.10. subst('/re/repl/flags') 1.3.11. subst_uri('/re/repl/flags') 1.3.12. subst_user('/re/repl/flags') 1.3.13. subst_body('/re/repl/flags') List of Examples 1.1. search usage 1.2. search_body usage 1.3. search_append usage 1.4. search_append_body usage 1.5. replace usage 1.6. replace_body usage 1.7. replace_all usage 1.8. replace_body_all usage 1.9. replace_body_atonce usage 1.10. subst usage 1.11. subst_uri usage 1.12. subst usage 1.13. subst_body usage Chapter 1. Admin Guide 1.1. Overview The module implements text based operations over the SIP message processed by OpenSIPS. SIP is a text based protocol and the module provides a large set of very useful functions to manipulate the message at text level, e.g., regular expression search and replace, Perl-like substitutions, etc. Note: all SIP-aware functions like insert_hf, append_hf or codec operations have been moved to the sipmsgops module. 1.1.1. Known Limitations search ignores folded lines. For example, search(“(From|f):.*@foo.barâ€) doesn't match the following From header field: From: medabeda ;tag=1234 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Functions 1.3.1. search(re) Searches for the re in the message. Meaning of the parameters is as follows: * re - Regular expression. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.1. search usage ... if ( search("[Ss][Ii][Pp]") ) { /*....*/ }; ... 1.3.2. search_body(re) Searches for the re in the body of the message. Meaning of the parameters is as follows: * re - Regular expression. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.2. search_body usage ... if ( search_body("[Ss][Ii][Pp]") ) { /*....*/ }; ... 1.3.3. search_append(re, txt) Searches for the first match of re and appends txt after it. Meaning of the parameters is as follows: * re - Regular expression. * txt - String to be appended. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.3. search_append usage ... search_append("[Oo]pen[Ss]er", " SIP Proxy"); ... 1.3.4. search_append_body(re, txt) Searches for the first match of re in the body of the message and appends txt after it. Meaning of the parameters is as follows: * re - Regular expression. * txt - String to be appended. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.4. search_append_body usage ... search_append_body("[Oo]pen[Ss]er", " SIP Proxy"); ... 1.3.5. replace(re, txt) Replaces the first occurrence of re with txt. Meaning of the parameters is as follows: * re - Regular expression. * txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.5. replace usage ... replace("opensips", "Open SIP Server"); ... 1.3.6. replace_body(re, txt) Replaces the first occurrence of re in the body of the message with txt. Meaning of the parameters is as follows: * re - Regular expression. * txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.6. replace_body usage ... replace_body("opensips", "Open SIP Server"); ... 1.3.7. replace_all(re, txt) Replaces all occurrence of re with txt. Meaning of the parameters is as follows: * re - Regular expression. * txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.7. replace_all usage ... replace_all("opensips", "Open SIP Server"); ... 1.3.8. replace_body_all(re, txt) Replaces all occurrence of re in the body of the message with txt. Matching is done on a per-line basis. Meaning of the parameters is as follows: * re - Regular expression. * txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.8. replace_body_all usage ... replace_body_all("opensips", "Open SIP Server"); ... 1.3.9. replace_body_atonce(re, txt) Replaces all occurrence of re in the body of the message with txt. Matching is done over the whole body. Meaning of the parameters is as follows: * re - Regular expression. * txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.9. replace_body_atonce usage ... # strip the whole body from the message: if(has_body() && replace_body_atonce("^.+$", "")) remove_hf("Content-Type"); ... 1.3.10. subst('/re/repl/flags') Replaces re with repl (sed or perl like). Meaning of the parameters is as follows: * '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). 're' - is regular expression 'repl' - is replacement string - may contain pseudo-variables 'flags' - substitution flags (i - ignore case, g - global) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.10. subst usage ... # replace the uri in to: with the message uri (just an example) if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1\u\2/ig') ) {}; # replace the uri in to: with the value of avp sip_address (just an exam ple) if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1$avp(sip_address)\ 2/ig') ) {}; ... 1.3.11. subst_uri('/re/repl/flags') Runs the re substitution on the message uri (like subst but works only on the uri) Meaning of the parameters is as follows: * '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). 're' - is regular expression 'repl' - is replacement string - may contain pseudo-variables 'flags' - substitution flags (i - ignore case, g - global) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.11. subst_uri usage ... # adds 3463 prefix to numeric uris, and save the original uri (\0 match) # as a parameter: orig_uri (just an example) if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:3463\1@\2;orig_uri=\0/i')){$ # adds the avp 'uri_prefix' as prefix to numeric uris, and save the orig inal # uri (\0 match) as a parameter: orig_uri (just an example) if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:$avp(uri_prefix)\1@\2;orig_uri=\ 0/i')){$ ... 1.3.12. subst_user('/re/repl/flags') Runs the re substitution on the message uri (like subst_uri but works only on the user portion of the uri) Meaning of the parameters is as follows: * '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). 're' - is regular expression 'repl' - is replacement string - may contain pseudo-variables 'flags' - substitution flags (i - ignore case, g - global) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.12. subst usage ... # adds 3463 prefix to uris ending with 3642 (just an example) if (subst_user('/3642$/36423463/')){$ ... # adds avp 'user_prefix' as prefix to username in r-uri ending with 3642 if (subst_user('/(.*)3642$/$avp(user_prefix)\13642/')){$ ... 1.3.13. subst_body('/re/repl/flags') Replaces re with repl (sed or perl like) in the body of the message. Meaning of the parameters is as follows: * '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). 're' - is regular expression 'repl' - is replacement string - may contain pseudo-variables 'flags' - substitution flags (i - ignore case, g - global) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. Example 1.13. subst_body usage ... if ( subst_body('/^o=(.*) /o=$fU ') ) {}; ... opensips-2.2.2/modules/textops/doc/000077500000000000000000000000001300170765700173065ustar00rootroot00000000000000opensips-2.2.2/modules/textops/doc/textops.xml000066400000000000000000000025271300170765700215440ustar00rootroot00000000000000 %docentities; ]> textops Module &osipsname; Andrei Pelinescu-Onciul &fhg; pelinescu-onciul@fokus.fraunhofer.de Andrei Pelinescu-Onciul pelinescu-onciul@fokus.fraunhofer.de Daniel-Constantin Mierla miconda@gmail.com Ovidiu Sas osas@voipembedded.com 2003 &fhg; $Revision: 7480 $ $Date$ &admin; &faq; opensips-2.2.2/modules/textops/doc/textops_admin.xml000066400000000000000000000325621300170765700227160ustar00rootroot00000000000000 &adminguide;
Overview The module implements text based operations over the SIP message processed by OpenSIPS. SIP is a text based protocol and the module provides a large set of very useful functions to manipulate the message at text level, e.g., regular expression search and replace, Perl-like substitutions, etc. Note: all SIP-aware functions like insert_hf, append_hf or codec operations have been moved to the sipmsgops module.
Known Limitations search ignores folded lines. For example, search((From|f):.*@foo.bar) doesn't match the following From header field: From: medabeda <sip:medameda@foo.bar>;tag=1234
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Functions
<function moreinfo="none">search(re)</function> Searches for the re in the message. Meaning of the parameters is as follows: re - Regular expression. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>search</function> usage ... if ( search("[Ss][Ii][Pp]") ) { /*....*/ }; ...
<function moreinfo="none">search_body(re)</function> Searches for the re in the body of the message. Meaning of the parameters is as follows: re - Regular expression. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>search_body</function> usage ... if ( search_body("[Ss][Ii][Pp]") ) { /*....*/ }; ...
<function moreinfo="none">search_append(re, txt)</function> Searches for the first match of re and appends txt after it. Meaning of the parameters is as follows: re - Regular expression. txt - String to be appended. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>search_append</function> usage ... search_append("[Oo]pen[Ss]er", " SIP Proxy"); ...
<function moreinfo="none">search_append_body(re, txt)</function> Searches for the first match of re in the body of the message and appends txt after it. Meaning of the parameters is as follows: re - Regular expression. txt - String to be appended. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>search_append_body</function> usage ... search_append_body("[Oo]pen[Ss]er", " SIP Proxy"); ...
<function moreinfo="none">replace(re, txt)</function> Replaces the first occurrence of re with txt. Meaning of the parameters is as follows: re - Regular expression. txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>replace</function> usage ... replace("opensips", "Open SIP Server"); ...
<function moreinfo="none">replace_body(re, txt)</function> Replaces the first occurrence of re in the body of the message with txt. Meaning of the parameters is as follows: re - Regular expression. txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>replace_body</function> usage ... replace_body("opensips", "Open SIP Server"); ...
<function moreinfo="none">replace_all(re, txt)</function> Replaces all occurrence of re with txt. Meaning of the parameters is as follows: re - Regular expression. txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>replace_all</function> usage ... replace_all("opensips", "Open SIP Server"); ...
<function moreinfo="none">replace_body_all(re, txt)</function> Replaces all occurrence of re in the body of the message with txt. Matching is done on a per-line basis. Meaning of the parameters is as follows: re - Regular expression. txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>replace_body_all</function> usage ... replace_body_all("opensips", "Open SIP Server"); ...
<function moreinfo="none">replace_body_atonce(re, txt)</function> Replaces all occurrence of re in the body of the message with txt. Matching is done over the whole body. Meaning of the parameters is as follows: re - Regular expression. txt - String. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>replace_body_atonce</function> usage ... # strip the whole body from the message: if(has_body() && replace_body_atonce("^.+$", "")) remove_hf("Content-Type"); ...
<function moreinfo="none">subst('/re/repl/flags')</function> Replaces re with repl (sed or perl like). Meaning of the parameters is as follows: '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). 're' - is regular expression 'repl' - is replacement string - may contain pseudo-variables 'flags' - substitution flags (i - ignore case, g - global) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>subst</function> usage ... # replace the uri in to: with the message uri (just an example) if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1\u\2/ig') ) {}; # replace the uri in to: with the value of avp sip_address (just an example) if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1$avp(sip_address)\2/ig') ) {}; ...
<function moreinfo="none">subst_uri('/re/repl/flags')</function> Runs the re substitution on the message uri (like subst but works only on the uri) Meaning of the parameters is as follows: '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). 're' - is regular expression 'repl' - is replacement string - may contain pseudo-variables 'flags' - substitution flags (i - ignore case, g - global) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>subst_uri</function> usage ... # adds 3463 prefix to numeric uris, and save the original uri (\0 match) # as a parameter: orig_uri (just an example) if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:3463\1@\2;orig_uri=\0/i')){$ # adds the avp 'uri_prefix' as prefix to numeric uris, and save the original # uri (\0 match) as a parameter: orig_uri (just an example) if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:$avp(uri_prefix)\1@\2;orig_uri=\0/i')){$ ...
<function moreinfo="none">subst_user('/re/repl/flags')</function> Runs the re substitution on the message uri (like subst_uri but works only on the user portion of the uri) Meaning of the parameters is as follows: '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). 're' - is regular expression 'repl' - is replacement string - may contain pseudo-variables 'flags' - substitution flags (i - ignore case, g - global) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>subst</function> usage ... # adds 3463 prefix to uris ending with 3642 (just an example) if (subst_user('/3642$/36423463/')){$ ... # adds avp 'user_prefix' as prefix to username in r-uri ending with 3642 if (subst_user('/(.*)3642$/$avp(user_prefix)\13642/')){$ ...
<function moreinfo="none">subst_body('/re/repl/flags')</function> Replaces re with repl (sed or perl like) in the body of the message. Meaning of the parameters is as follows: '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). 're' - is regular expression 'repl' - is replacement string - may contain pseudo-variables 'flags' - substitution flags (i - ignore case, g - global) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. <function>subst_body</function> usage ... if ( subst_body('/^o=(.*) /o=$fU ') ) {}; ...
opensips-2.2.2/modules/textops/textops.c000066400000000000000000000434161300170765700204230ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-29: - rewriting actions (replace, search_append) now begin * at the second line -- previously, they could affect * first line too, which resulted in wrong calculation of * forwarded requests and an error consequently * - replace_all introduced * 2003-01-28 scratchpad removed (jiri) * 2003-03-10 module export interface updated to the new format (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-97 actions permitted to be used from failure/reply routes (jiri) * 2003-04-21 remove_hf introduced (jiri) * 2003-08-19 subst added (support for sed like res:s/re/repl/flags) (andrei) * 2003-08-20 subst_uri added (like above for uris) (andrei) * 2003-09-11 updated to new build_lump_rpl() interface (bogdan) * 2004-07-06 subst_user added (like subst_uri but only for user) (sobomax) * 2004-11-12 subst_user changes (old serdev mails) (andrei) * */ #include "../../sr_module.h" #include "../../action.h" #include "../../dprint.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../str.h" #include "../../re.h" #include "../../mod_fix.h" #include "../../parser/parse_uri.h" #include "../../mod_fix.h" #include #include #include #include /* for regex */ #include #include #include static int search_f(struct sip_msg*, char*, char*); static int search_body_f(struct sip_msg*, char*, char*); static int replace_f(struct sip_msg*, char*, char*); static int replace_body_f(struct sip_msg*, char*, char*); static int replace_all_f(struct sip_msg*, char*, char*); static int replace_body_all_f(struct sip_msg*, char*, char*); static int replace_body_atonce_f(struct sip_msg*, char*, char*); static int subst_f(struct sip_msg*, char*, char*); static int subst_uri_f(struct sip_msg*, char*, char*); static int subst_user_f(struct sip_msg*, char*, char*); static int subst_body_f(struct sip_msg*, char*, char*); static int search_append_f(struct sip_msg*, char*, char*); static int search_append_body_f(struct sip_msg*, char*, char*); static int fixup_substre(void**, int); static int mod_init(void); static cmd_export_t cmds[]={ {"search", (cmd_function)search_f, 1, fixup_regexp_null, fixup_free_regexp_null, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"search_body", (cmd_function)search_body_f, 1, fixup_regexp_null, fixup_free_regexp_null, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"search_append", (cmd_function)search_append_f, 2, fixup_regexp_none,fixup_free_regexp_none, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"search_append_body", (cmd_function)search_append_body_f, 2, fixup_regexp_none, fixup_free_regexp_none, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"replace", (cmd_function)replace_f, 2, fixup_regexp_none, fixup_free_regexp_none, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"replace_body", (cmd_function)replace_body_f, 2, fixup_regexp_none, fixup_free_regexp_none, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"replace_all", (cmd_function)replace_all_f, 2, fixup_regexp_none, fixup_free_regexp_none, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"replace_body_all", (cmd_function)replace_body_all_f,2, fixup_regexp_none, fixup_free_regexp_none, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"replace_body_atonce", (cmd_function)replace_body_atonce_f,2, fixup_regexpNL_none, fixup_free_regexp_none, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"subst", (cmd_function)subst_f, 1, fixup_substre, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"subst_uri", (cmd_function)subst_uri_f, 1, fixup_substre, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"subst_user", (cmd_function)subst_user_f, 1, fixup_substre, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"subst_body", (cmd_function)subst_body_f, 1, fixup_substre, 0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {0,0,0,0,0,0} }; struct module_exports exports= { "textops", /* module name*/ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ NULL, /* exported async functions */ 0, /* module parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* per-child init function */ }; static int mod_init(void) { LM_INFO("initializing...\n"); return 0; } static char *get_header(struct sip_msg *msg) { return msg->buf+msg->first_line.len; } static int search_f(struct sip_msg* msg, char* key, char* str2) { /*we registered only 1 param, so we ignore str2*/ regmatch_t pmatch; if (regexec((regex_t*) key, msg->buf, 1, &pmatch, 0)!=0) return -1; return 1; } static int search_body_f(struct sip_msg* msg, char* key, char* str2) { str body; /*we registered only 1 param, so we ignore str2*/ regmatch_t pmatch; if ( get_body(msg,&body)!=0 || body.len==0) { LM_DBG("message body has zero length\n"); return -1; } if (regexec((regex_t*) key, body.s, 1, &pmatch, 0)!=0) return -1; return 1; } static int search_append_f(struct sip_msg* msg, char* key, char* str2) { struct lump* l; regmatch_t pmatch; char* s; int len; char *begin; int off; begin=get_header(msg); /* msg->orig/buf previously .. uri problems */ off=begin-msg->buf; if (regexec((regex_t*) key, begin, 1, &pmatch, 0)!=0) return -1; if (pmatch.rm_so!=-1){ if ((l=anchor_lump(msg, off+pmatch.rm_eo, 0))==0) return -1; len=strlen(str2); s=pkg_malloc(len); if (s==0){ LM_ERR("memory allocation failure\n"); return -1; } memcpy(s, str2, len); if (insert_new_lump_after(l, s, len, 0)==0){ LM_ERR("could not insert new lump\n"); pkg_free(s); return -1; } return 1; } return -1; } static int search_append_body_f(struct sip_msg* msg, char* key, char* str2) { struct lump* l; regmatch_t pmatch; char* s; int len; int off; str body; if ( get_body(msg,&body)!=0 || body.len==0) { LM_DBG("message body has zero length\n"); return -1; } off=body.s-msg->buf; if (regexec((regex_t*) key, body.s, 1, &pmatch, 0)!=0) return -1; if (pmatch.rm_so!=-1){ if ((l=anchor_lump(msg, off+pmatch.rm_eo, 0))==0) return -1; len=strlen(str2); s=pkg_malloc(len); if (s==0){ LM_ERR("memory allocation failure\n"); return -1; } memcpy(s, str2, len); if (insert_new_lump_after(l, s, len, 0)==0){ LM_ERR("could not insert new lump\n"); pkg_free(s); return -1; } return 1; } return -1; } static int replace_all_f(struct sip_msg* msg, char* key, char* str2) { struct lump* l; regmatch_t pmatch; char* s; int len; char* begin; int off; int ret; int eflags; begin = get_header(msg); ret=-1; /* pessimist: we will not find any */ len=strlen(str2); eflags=0; /* match ^ at the beginning of the string*/ while (beginbuf+msg->len && regexec((regex_t*) key, begin, 1, &pmatch, eflags)==0) { off=begin-msg->buf; if (pmatch.rm_so==-1){ LM_ERR("offset unknown\n"); return -1; } if (pmatch.rm_so==pmatch.rm_eo){ LM_ERR("matched string is empty... invalid regexp?\n"); return -1; } if ((l=del_lump(msg, pmatch.rm_so+off, pmatch.rm_eo-pmatch.rm_so, 0))==0) { LM_ERR("del_lump failed\n"); return -1; } s=pkg_malloc(len); if (s==0){ LM_ERR("memory allocation failure\n"); return -1; } memcpy(s, str2, len); if (insert_new_lump_after(l, s, len, 0)==0){ LM_ERR("could not insert new lump\n"); pkg_free(s); return -1; } /* new cycle */ begin=begin+pmatch.rm_eo; /* is it still a string start */ if (*(begin-1)=='\n' || *(begin-1)=='\r') eflags&=~REG_NOTBOL; else eflags|=REG_NOTBOL; ret=1; } /* while found ... */ return ret; } static int do_replace_body_f(struct sip_msg* msg, char* key, char* str2, int nobol) { struct lump* l; regmatch_t pmatch; char* s; int len; char* begin; int off; int ret; int eflags; str body; if ( get_body(msg,&body)!=0 || body.len==0) { LM_DBG("message body has zero length\n"); return -1; } begin=body.s; ret=-1; /* pessimist: we will not find any */ len=strlen(str2); eflags=0; /* match ^ at the beginning of the string*/ while (beginbuf+msg->len && regexec((regex_t*) key, begin, 1, &pmatch, eflags)==0) { off=begin-msg->buf; if (pmatch.rm_so==-1){ LM_ERR("offset unknown\n"); return -1; } if (pmatch.rm_so==pmatch.rm_eo){ LM_ERR("matched string is empty... invalid regexp?\n"); return -1; } if ((l=del_lump(msg, pmatch.rm_so+off, pmatch.rm_eo-pmatch.rm_so, 0))==0) { LM_ERR("del_lump failed\n"); return -1; } s=pkg_malloc(len); if (s==0){ LM_ERR("memory allocation failure\n"); return -1; } memcpy(s, str2, len); if (insert_new_lump_after(l, s, len, 0)==0){ LM_ERR("could not insert new lump\n"); pkg_free(s); return -1; } /* new cycle */ begin=begin+pmatch.rm_eo; /* is it still a string start */ if (nobol && (*(begin-1)=='\n' || *(begin-1)=='\r')) eflags&=~REG_NOTBOL; else eflags|=REG_NOTBOL; ret=1; } /* while found ... */ return ret; } static int replace_body_all_f(struct sip_msg* msg, char* key, char* str2) { return do_replace_body_f(msg, key, str2, 1); } static int replace_body_atonce_f(struct sip_msg* msg, char* key, char* str2) { return do_replace_body_f(msg, key, str2, 0); } static int replace_f(struct sip_msg* msg, char* key, char* str2) { struct lump* l; regmatch_t pmatch; char* s; int len; char* begin; int off; begin=get_header(msg); /* msg->orig previously .. uri problems */ if (regexec((regex_t*) key, begin, 1, &pmatch, 0)!=0) return -1; off=begin-msg->buf; if (pmatch.rm_so!=-1){ if ((l=del_lump(msg, pmatch.rm_so+off, pmatch.rm_eo-pmatch.rm_so, 0))==0) return -1; len=strlen(str2); s=pkg_malloc(len); if (s==0){ LM_ERR("memory allocation failure\n"); return -1; } memcpy(s, str2, len); if (insert_new_lump_after(l, s, len, 0)==0){ LM_ERR("could not insert new lump\n"); pkg_free(s); return -1; } return 1; } return -1; } static int replace_body_f(struct sip_msg* msg, char* key, char* str2) { struct lump* l; regmatch_t pmatch; char* s; int len; char* begin; int off; str body; if ( get_body(msg,&body)!=0 || body.len==0) { LM_DBG("message body has zero length\n"); return -1; } begin=body.s; /* msg->orig previously .. uri problems */ if (regexec((regex_t*) key, begin, 1, &pmatch, 0)!=0) return -1; off=begin-msg->buf; if (pmatch.rm_so!=-1){ if ((l=del_lump(msg, pmatch.rm_so+off, pmatch.rm_eo-pmatch.rm_so, 0))==0) return -1; len=strlen(str2); s=pkg_malloc(len); if (s==0){ LM_ERR("memory allocation failure\n"); return -1; } memcpy(s, str2, len); if (insert_new_lump_after(l, s, len, 0)==0){ LM_ERR("could not insert new lump\n"); pkg_free(s); return -1; } return 1; } return -1; } /* sed-perl style re: s/regular expression/replacement/flags */ static int subst_f(struct sip_msg* msg, char* subst, char* ignored) { struct lump* l; struct replace_lst* lst; struct replace_lst* rpl; char* begin; struct subst_expr* se; int off; int ret; int nmatches; se=(struct subst_expr*)subst; begin=get_header(msg); /* start after first line to avoid replacing the uri */ off=begin-msg->buf; ret=-1; if ((lst=subst_run(se, begin, msg, &nmatches))==0) goto error; /* not found */ for (rpl=lst; rpl; rpl=rpl->next){ LM_DBG("%s: replacing at offset %d [%.*s] with [%.*s]\n", exports.name, rpl->offset+off, rpl->size, rpl->offset+off+msg->buf, rpl->rpl.len, rpl->rpl.s); if ((l=del_lump(msg, rpl->offset+off, rpl->size, 0))==0) goto error; /* hack to avoid re-copying rpl, possible because both * replace_lst & lumps use pkg_malloc */ if (insert_new_lump_after(l, rpl->rpl.s, rpl->rpl.len, 0)==0){ LM_ERR("ERROR: %s: subst_f: could not insert new lump\n", exports.name); goto error; } /* hack continued: set rpl.s to 0 so that replace_lst_free will * not free it */ rpl->rpl.s=0; rpl->rpl.len=0; } ret=1; error: LM_DBG("lst was %p\n", lst); if (lst) replace_lst_free(lst); if (nmatches<0) LM_ERR("ERROR: %s: subst_run failed\n", exports.name); return ret; } /* sed-perl style re: s/regular expression/replacement/flags, like * subst but works on the message uri */ static int subst_uri_f(struct sip_msg* msg, char* subst, char* ignored) { char* tmp; int len; char c; struct subst_expr* se; str* result; se=(struct subst_expr*)subst; if (msg->new_uri.s){ len=msg->new_uri.len; tmp=msg->new_uri.s; }else{ tmp=msg->first_line.u.request.uri.s; len =msg->first_line.u.request.uri.len; }; /* ugly hack: 0 s[len], and restore it afterward * (our re functions require 0 term strings), we can do this * because we always alloc len+1 (new_uri) and for first_line, the * message will always be > uri.len */ c=tmp[len]; tmp[len]=0; result=subst_str(tmp, msg, se, 0); /* pkg malloc'ed result */ tmp[len]=c; if (result){ LM_DBG("%s match - old uri= [%.*s], new uri= [%.*s]\n", exports.name, len, tmp, (result->len)?result->len:0,(result->s)?result->s:""); if (msg->new_uri.s) pkg_free(msg->new_uri.s); msg->new_uri=*result; msg->parsed_uri_ok=0; /* reset "use cached parsed uri" flag */ pkg_free(result); /* free str* pointer */ return 1; /* success */ } return -1; /* false, no subst. made */ } /* sed-perl style re: s/regular expression/replacement/flags, like * subst but works on the user part of the uri */ static int subst_user_f(struct sip_msg* msg, char* subst, char* ignored) { int rval; str* result; struct subst_expr* se; struct action act; str user; char c; int nmatches; c=0; if (parse_sip_msg_uri(msg)<0){ return -1; /* error, bad uri */ } if (msg->parsed_uri.user.s==0){ /* no user in uri */ user.s=""; user.len=0; }else{ user=msg->parsed_uri.user; c=user.s[user.len]; user.s[user.len]=0; } se=(struct subst_expr*)subst; result=subst_str(user.s, msg, se, &nmatches);/* pkg malloc'ed result */ if (c) user.s[user.len]=c; if (result == NULL) { if (nmatches<0) LM_ERR("subst_user(): subst_str() failed\n"); return -1; } /* result->s[result->len] = '\0'; --subst_str returns 0-term strings */ memset(&act, 0, sizeof(act)); /* be on the safe side */ act.type = SET_USER_T; act.elem[0].type = STR_ST; act.elem[0].u.s = *result; rval = do_action(&act, msg); pkg_free(result->s); pkg_free(result); return rval; } /* sed-perl style re: s/regular expression/replacement/flags */ static int subst_body_f(struct sip_msg* msg, char* subst, char* ignored) { struct lump* l; struct replace_lst* lst; struct replace_lst* rpl; char* begin; struct subst_expr* se; int off; int ret; int nmatches; str body; if ( get_body(msg,&body)!=0 || body.len==0) { LM_DBG("message body has zero length\n"); return -1; } se=(struct subst_expr*)subst; begin=body.s; off=begin-msg->buf; ret=-1; if ((lst=subst_run(se, begin, msg, &nmatches))==0) goto error; /* not found */ for (rpl=lst; rpl; rpl=rpl->next){ LM_DBG("%s replacing at offset %d [%.*s] with [%.*s]\n", exports.name, rpl->offset+off, rpl->size, rpl->offset+off+msg->buf, rpl->rpl.len, rpl->rpl.s); if ((l=del_lump(msg, rpl->offset+off, rpl->size, 0))==0) goto error; /* hack to avoid re-copying rpl, possible because both * replace_lst & lumps use pkg_malloc */ if (insert_new_lump_after(l, rpl->rpl.s, rpl->rpl.len, 0)==0){ LM_ERR("%s could not insert new lump\n", exports.name); goto error; } /* hack continued: set rpl.s to 0 so that replace_lst_free will * not free it */ rpl->rpl.s=0; rpl->rpl.len=0; } ret=1; error: LM_DBG("lst was %p\n", lst); if (lst) replace_lst_free(lst); if (nmatches<0) LM_ERR("%s subst_run failed\n", exports.name); return ret; } static int fixup_substre(void** param, int param_no) { struct subst_expr* se; str subst; LM_DBG("%s module -- fixing %s\n", exports.name, (char*)(*param)); if (param_no!=1) return 0; subst.s=*param; subst.len=strlen(*param); se=subst_parser(&subst); if (se==0){ LM_ERR("%s: bad subst. re %s\n", exports.name, (char*)*param); return E_BAD_RE; } /* don't free string -- needed for specifiers */ /* pkg_free(*param); */ /* replace it with the compiled subst. re */ *param=se; return 0; } opensips-2.2.2/modules/tls_mgm/000077500000000000000000000000001300170765700164755ustar00rootroot00000000000000opensips-2.2.2/modules/tls_mgm/Makefile000066400000000000000000000027271300170765700201450ustar00rootroot00000000000000# # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=tls_mgm.so ETC_DIR?=../../etc/ tls_configs=$(patsubst $(ETC_DIR)/%, %, $(wildcard $(ETC_DIR)/tls/*) \ $(wildcard $(ETC_DIR)/tls/rootCA/*) $(wildcard $(ETC_DIR)/tls/rootCA/certs/*) \ $(wildcard $(ETC_DIR)/tls/rootCA/private/*) $(wildcard $(ETC_DIR)/tls/user/*)) ifeq ($(CROSS_COMPILE),) SSL_BUILDER=$(shell \ if pkg-config --exists libssl; then \ echo 'pkg-config libssl'; \ fi) endif ifneq ($(SSL_BUILDER),) DEFS += $(shell $(SSL_BUILDER) --cflags) LIBS += $(shell $(SSL_BUILDER) --libs) else DEFS += -I$(LOCALBASE)/ssl/include \ -I$(LOCALBASE)/include LIBS += -L$(LOCALBASE)/lib -L$(LOCALBASE)/ssl/lib \ -L$(LOCALBASE)/lib64 -L$(LOCALBASE)/ssl/lib64 \ -lssl -lcrypto endif include ../../Makefile.modules install_module_custom: mkdir -p $(cfg_prefix)/$(cfg_dir)/tls ; \ mkdir -p $(cfg_prefix)/$(cfg_dir)/tls/rootCA ; \ mkdir -p $(cfg_prefix)/$(cfg_dir)/tls/rootCA/certs ; \ mkdir -p $(cfg_prefix)/$(cfg_dir)/tls/rootCA/private ; \ mkdir -p $(cfg_prefix)/$(cfg_dir)/tls/user ; \ for FILE in $(tls_configs) ; do \ if [ -f $(ETC_DIR)/$$FILE ]; then \ if [ "$(tls_overwrite_certs)" != "" -o \ ! -f $(cfg_prefix)/$(cfg_dir)/$$FILE ] ; then \ $(INSTALL_TOUCH) $(ETC_DIR)/$$FILE \ $(cfg_prefix)/$(cfg_dir)/$$FILE ; \ $(INSTALL_CFG) $(ETC_DIR)/$$FILE \ $(cfg_prefix)/$(cfg_dir)/$$FILE ; \ fi; \ fi ;\ done ; \ opensips-2.2.2/modules/tls_mgm/README000066400000000000000000001226701300170765700173650ustar00rootroot00000000000000TLS_MGM module Peter Griffiths unknown Klaus Darilion enum.at Edited by Klaus Darilion Edited by Bogdan-Andrei Iancu Edited by Cesc Santasusana Edited by Klaus Darilion Edited by Christian Lahme Edited by Ionut-Razvan Ionita Edited by Marius Cristian Eseanu Copyright © 2005 Voice Sistem SRL Copyright © 2005 Cesc Santasusana Copyright © 2006 enum.at Copyright © 2013 Secusmart GmbH Copyright © 2015 OpenSIPS Solutions __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. History 1.3. Usage 1.4. Dependencies of external libraries 1.5. Exported Functions 1.5.1. is_peer_verified 1.6. Exported MI Functions 1.6.1. tls_list 1.6.2. tls_reload 1.7. OpenSIPS Exported parameters 1.7.1. listen=interface 1.7.2. tls_method [(string):](string) 1.7.3. certificate [(string):](string) 1.7.4. private_key [(string):](string) 1.7.5. ca_list [(string):]((string) 1.7.6. ca_dir [(string):](string) 1.7.7. ciphers_list [(string):](string) 1.7.8. dh_params [(string):](string) 1.7.9. ec_curve[(string):](string) 1.7.10. verify_cert [(string):](string) and require_cert[(string):](string) 1.7.11. tls_handshake_timeout (integer) and tls_send_timeout (integer) 1.7.12. client_domain_avp (integer) 1.7.13. db_mode (integer) 1.7.14. db_url (string) 1.7.15. db_table (string) 1.7.16. id_col (string) 1.7.17. address_col (string) 1.7.18. address_col (string) 1.7.19. tls_method_col (string) 1.7.20. verify_cert_col (string) 1.7.21. require_cert_col (string) 1.7.22. certificate_col (string) 1.7.23. private_key_col (string) 1.7.24. crl_check_all_col (string) 1.7.25. crl_dir_col (string) 1.7.26. ca_list_col (string) 1.7.27. ca_dir_col (string) 1.7.28. cipher_list_col (string) 1.7.29. dh_params_col (string) 1.7.30. ec_curve_col (string) 1.7.31. server_domain, client_domain (string) 1.8. Pseudo-Variables 1.8.1. $tls_version 1.8.2. $tls_description 1.8.3. $tls_cipher_info 1.8.4. $tls_cipher_bits 1.8.5. $tls_[peer|my]_version 1.8.6. $tls_[peer|my]_serial 1.8.7. $tls_[peer|my]_[subject|issuer] 1.8.8. $tls_[peer|my]_[subject|issuer]_cn 1.8.9. $tls_[peer|my]_[subject|issuer]_locality 1.8.10. $tls_[peer|my]_[subject|issuer]_country 1.8.11. $tls_[peer|my]_[subject|issuer]_state 1.8.12. $tls_[peer|my]_[subject|issuer]_organization 1.8.13. $tls_[peer|my]_[subject|issuer]_unit 1.8.14. $tls_[peer|my]_san_email 1.8.15. $tls_[peer|my]_san_hostname 1.8.16. $tls_[peer|my]_san_uri 1.8.17. $tls_[peer|my]_san_ip 1.8.18. $tls_peer_verified 1.8.19. $tls_peer_revoked 1.8.20. $tls_peer_expired 1.8.21. $tls_peer_selfsigned 1.8.22. $tls_peer_notBefore 1.8.23. $tls_peer_notAfter 1.9. OpenSIPS with TLS - script example 1.10. Debug TLS connections 2. Developer Guide 2.1. API Functions 2.1.1. find_server_domain 2.1.2. find_client_domain 2.1.3. get_handshake_timeout 2.1.4. get_send_timeout 2.2. TLS_CONFIG 2.3. TLS_INIT 2.3.1. ssl context 2.3.2. pre_init_tls 2.3.3. init_tls 2.3.4. destroy_tls 2.3.5. tls_init 2.3.6. os_malloc, os_realloc, os_free 2.4. TLS_DOMAIN 2.4.1. tls_domains 2.4.2. tls_find_server_domain 2.4.3. tls_find_client_domain 2.4.4. tls_find_client_domain_addr 2.4.5. tls_find_client_domain_name 2.4.6. tls_new__domain 2.4.7. tls_new_server_domain 2.4.8. tls_new_client_domain 2.4.9. tls_new_client_domain_name 2.4.10. tls_free_domains List of Examples 1.1. is_peer_verified usage 1.2. Set listen variable 1.3. Set tls_method variable 1.4. Set certificate variable 1.5. Set private_key variable 1.6. Set ca_list variable 1.7. Set ca_dir variable 1.8. Set ciphers_list variable 1.9. Set dh_params variable 1.10. Set verify_cert & require_cert variable 1.11. Set tls_handshake_timeout & tls_send_timeout variable 1.12. Set tls_client_domain_avp variable 1.13. Usage of db_mode block 1.14. Usage of db_url block 1.15. Usage of db_table block 1.16. Usage of id_col block 1.17. Usage of address_col block 1.18. Usage of address_col block 1.19. Usage of tls_method_col block 1.20. Usage of vertify_cert_col block 1.21. Usage of require_cert_col block 1.22. Usage of certificate_col block 1.23. Usage of private_key_col block 1.24. Usage of crl_check_all block 1.25. Usage of crl_dir_col block 1.26. Usage of ca_list_col block 1.27. Usage of ca_dir_col block 1.28. Usage of cipher_list_col block 1.29. Usage of dh_params_col block 1.30. Usage of ec_curve_col block 1.31. Usage of tls_client_domain and tls_server_domain block 1.32. Example of $tls_[peer|my]_[subject|issuer] 1.33. Script with TLS support 1.34. Example of TLS logging Chapter 1. Admin Guide 1.1. Overview This module is a management module for TLS certificates and parameters. It provides an interfaces for all the modules that use the TLS protocol. It also implements TLS related functions to use in the routing script, and exports pseudo variables with certificate and TLS parameters. 1.2. History The TLS support was originally developed by Peter Griffiths and posted as a patch on SER development mailing list. Thanks to Cesc Santasusana, several problems were fixed and some improvements were added. The TLS support was simultaneously added in both projects. In SER, the support was committed in a separate “experimental†CVS tree, as patch to the main CVS tree. In OpenSIPS, the support was integrated directly into the CVS tree, as a built-in component, and is part of stable OpenSIPS since release >=1.0.0. Starting from OpenSIPS 2.2, the certificates managemnet has been decoupled from the TLS communication in two different modules: dh_params which handles the TLS communication and tls_mgm which handles TLS handshake (certificates and parameters). 1.3. Usage This module is used to provision TLS certificates and parameters for all the modules that use TLS transport (currently only proto_tls). The module supports multiple virtual domains that can be assigned to different listeners (servers) or new connections (clients). Each TLS module that uses this management module should assign itself to one or more domains. A script example which details this module's usage can be found in Section 1.9, “OpenSIPS with TLS - script exampleâ€. 1.4. Dependencies of external libraries OpenSIPS TLS v1.0 support requires the following packages: * openssl or libssl >= 0.9.6 * openssl-dev or libssl-dev OpenSIPS TLS v1.1/1.2 support requires the following packages: * openssl or libssl >= 1.0.1e * openssl-dev or libssl-dev 1.5. Exported Functions 1.5.1. is_peer_verified Returns 1 if the message is received via TLS and the peer was verified during TLS connection handshake, otherwise it returns -1 This function can be used from REQUEST_ROUTE. Example 1.1. is_peer_verified usage ... if (is_peer_verified()) { xlog("L_INFO","request from verified TLS peer\n"); } else { xlog("L_INFO","request not verified\n"); } ... 1.6. Exported MI Functions 1.6.1. tls_list List all domains information. 1.6.2. tls_reload Reloads information from the database. 1.7. OpenSIPS Exported parameters All these parameters can be used from the opensips.cfg file, to configure the behavior of OpenSIPS-TLS. 1.7.1. listen=interface Not specific to TLS. Allows to specify the protocol (udp, tcp, tls), the IP address and the port where the listening server will be. Example 1.2. Set listen variable ... listen = tls:1.2.3.4:5061 ... 1.7.2. tls_method [(string):](string) Sets the TLS protocol. The first parameter, if set, represents the id of the domain. TLS method which can be: * TLSv1_2 - means OpenSIPS will accept only TLSv1.2 connections (rfc3261 conformant). * TLSv1 - means OpenSIPS will accept only TLSv1 connections (rfc3261 conformant). * SSLv3 - means OpenSIPS will accept only SSLv3 connections * SSLv2 - means OpenSIPS will accept only SSLv2 connections (almost all old clients support this). * SSLv23 - means OpenSIPS will accept any of the above methods, but the initial SSL hello must be v2 (in the initial hello all the supported protocols are advertised enabling switching to a higher and more secure version). The initial v2 hello means it will not accept connections from SSLv3 or TLSv1 only clients. Default value is SSLv23. Warning Best is to use SSLv23, for extended compatibility. Using any of the other will restrict the version to just that one version. In fact, SSLv2 is disabled in the source code; to use it, you need to edit tls/tls_init.c If you want RFC3261 conformance and all your clients support TLSv1 (or you are planning to use encrypted "tunnels" only between different OpenSIPS proxies) use TLSv1. If you want to support older clients use SSLv23 (in fact most of the applications with SSL support use the SSLv23 method). Example 1.3. Set tls_method variable ... modparam("tls_mgm", "tls_method", "TLSv1") modparam("tls_mgm", "tls_method", "dom:TLSv1") ... 1.7.3. certificate [(string):](string) Public certificate file for OpenSIPS. It will be used as server-side certificate for incoming TLS connections, and as a client-side certificate for outgoing TLS connections. The first parameter, if set, represents the id of the domain. Default value is "CFG_DIR/cert.pem". Example 1.4. Set certificate variable ... modparam("tls_mgm", "certificate", "/mycerts/certs/opensips_server_cert. pem") modparam("tls_mgm", "certificate", "dom:/mycerts/certs/opensips_server_c ert.pem") ... 1.7.4. private_key [(string):](string) Private key of the above certificate. I must be kept in a safe place with tight permissions! The first parameter, if set, represents the id of the domain. Default value is "CFG_DIR/cert.pem". Example 1.5. Set private_key variable ... modparam("tls_mgm", "private_key", "/mycerts/private/prik.pem") modparam("tls_mgm", "private_key", "dom:/mycerts/private/prik.pem") ... 1.7.5. ca_list [(string):]((string) List of trusted CAs. The file contains the certificates accepted, one after the other. It MUST be a file, not a folder. The first parameter, if set, represents the id of the domain. Default value is "". Example 1.6. Set ca_list variable ... modparam("tls_mgm", "ca_list", "/mycerts/certs/ca_list.pem") modparam("tls_mgm", "ca_list", "dom:/mycerts/certs/ca_list.pem") ... 1.7.6. ca_dir [(string):](string) Directory storing trusted CAs. The path contains the certificates accepted, each as hash which is linked to certificate file. The first parameter, if set, represents the id of the domain. Default value is "". Example 1.7. Set ca_dir variable ... modparam("tls_mgm", "ca_dir", "/mycerts/certs") modparam("tls_mgm", "ca_dir", "dom:/mycerts/certs") ... 1.7.7. ciphers_list [(string):](string) You can specify the list of algorithms for authentication and encryption that you allow. The first parameter, if set, represents the id of the domain. To obtain a list of ciphers and then choose, use the openssl application: * openssl ciphers 'ALL:eNULL:!LOW:!EXPORT' Warning Do not use the NULL algorithms (no encryption) ... only for testing!!! It defaults to the OpenSSL default ciphers. Example 1.8. Set ciphers_list variable ... modparam("tls_mgm", "ciphers_list", "NULL") modparam("tls_mgm", "ciphers_list", "dom:NULL") ... 1.7.8. dh_params [(string):](string) You can specify a file which contains Diffie-Hellman parameters as a PEM-file. This is needed if you would like to specify ciphers including Diffie-Hellman mode. The first parameter, if set, represents the id of the domain. It defaults to not set a dh param file. Example 1.9. Set dh_params variable ... modparam("tls_mgm", "dh_params", "/etc/pki/CA/dh1024.pem") modparam("tls_mgm", "dh_params", "dom:/etc/pki/CA/dh1024.pem") ... 1.7.9. ec_curve[(string):](string) You can specify an elliptic curve which should be used for ciphers which demand an elliptic curve. The first parameter, if set, represents the id of the domain. It's usable only if TLS v1.1/1.2 support was compiled. A list of curves which can be used you can get by openssl ecparam -list_curve It defaults to not set a elliptic curve. 1.7.10. verify_cert [(string):](string) and require_cert[(string):](string) Technically, verify_cert activates SSL_VERIFY_PEER in the ssl_context. 'require_cert' does the same with SSL_VERIFY_FAIL_IF_NO_PEER_CERT, which is only possible if SSL_VERIFY_PEER is also turned on. Since version 2.1, these parameters act have been reduced to only one. They act both on client side and server side if no domain specified, elseway they act on a specific domain, depending on the first parameter. These two parameters are used for incoming TLS connections, where OpenSIPS acts as server. It's usable only if TLS support was compiled. Default value for both is 1. Example 1.10. Set verify_cert & require_cert variable ... # turn on the strictest and strongest authentication possible modparam("tls_mgm", "require_cert", "1") modparam("tls_mgm", "require_cert", "1:1") modparam("tls_mgm", "verify_cert", "0") modparam("tls_mgm", "verify_cert", "1:1") ... 1.7.11. tls_handshake_timeout (integer) and tls_send_timeout (integer) Timeouts ... advanced users only Default value for both is 30. Example 1.11. Set tls_handshake_timeout & tls_send_timeout variable ... modparam("tls_mgm", "tls_handshake_timeout", 119) # number of seconds modparam("tls_mgm", "tls_send_timeout", 121) # number of seconds ... 1.7.12. client_domain_avp (integer) This sets the interger AVP used for name based TLS server domains (please see tls_client_domain for more details). Setting the value to 0 disables name based TLS client domains. It's usable only if TLS support was compiled. Default value is 0. Example 1.12. Set tls_client_domain_avp variable ... modparam("tls_mgm", "tls_client_domain_avp", "400") ... 1.7.13. db_mode (integer) When db_mode is enabled (1), this module cannot accept new domains configuration from the script. Default value is 0. (not enabled) Example 1.13. Usage of db_mode block modparam("tls_mgm", "db_mode", 1) 1.7.14. db_url (string) The database url. It cannot be NULL. Example 1.14. Usage of db_url block modparam("tls_mgm", "db_url", "mysql://root:admin@localhost/opensips") 1.7.15. db_table (string) Sets the database table name. Default value is "tls_mgm". Example 1.15. Usage of db_table block modparam("tls_mgm", "db_table", "tls_mgm") 1.7.16. id_col (string) Sets the id column name. Default value is "id". Example 1.16. Usage of id_col block modparam("tls_mgm", "id_col", "id") 1.7.17. address_col (string) Sets the address column name. Default value is "address". Example 1.17. Usage of address_col block modparam("tls_mgm", "address_col", "addr") 1.7.18. address_col (string) Sets the address column name. Default value is "address". Example 1.18. Usage of address_col block modparam("tls_mgm", "address_col", "addr") 1.7.19. tls_method_col (string) Sets the method column name. Default value is "method". Example 1.19. Usage of tls_method_col block modparam("tls_mgm", "tls_method_col", "method") 1.7.20. verify_cert_col (string) Sets the verrify certificate column name. Default value is "verify_cert". Example 1.20. Usage of vertify_cert_col block modparam("tls_mgm", "verify_cert_col", "verify_cert") 1.7.21. require_cert_col (string) Sets the require certificate column name. Default value is "require_cert". Example 1.21. Usage of require_cert_col block modparam("tls_mgm", "require_cert_col", "req") 1.7.22. certificate_col (string) Sets the certificate column name. Default value is "certificate". Example 1.22. Usage of certificate_col block modparam("tls_mgm", "certificate_col", "certificate") 1.7.23. private_key_col (string) Sets the private key column name. Default value is "private_key". Example 1.23. Usage of private_key_col block modparam("tls_mgm", "private_key_col", "pk") 1.7.24. crl_check_all_col (string) Sets the crl_check_all column name. Default value is "crl_check_all". Example 1.24. Usage of crl_check_all block modparam("tls_mgm", "crl_check_all_col", "crl_check") 1.7.25. crl_dir_col (string) Sets the crl directory column name. Default value is "crl_dir". Example 1.25. Usage of crl_dir_col block modparam("tls_mgm", "crl_dir_col", "crl_dir") 1.7.26. ca_list_col (string) Sets the CA list column name. Default value is "ca_list". Example 1.26. Usage of ca_list_col block modparam("tls_mgm", "ca_list_col", "ca_list") 1.7.27. ca_dir_col (string) Sets the CA directory column name. Default value is "ca_dir". Example 1.27. Usage of ca_dir_col block modparam("tls_mgm", "ca_dir_col", "ca_dir") 1.7.28. cipher_list_col (string) Sets the cipher list column name. Default value is "cipher_list". Example 1.28. Usage of cipher_list_col block modparam("tls_mgm", "cipher_list_col", "cipher_list") 1.7.29. dh_params_col (string) Sets the Diffie-Hellmann parameters column name. Default value is "dh_params". Example 1.29. Usage of dh_params_col block modparam("tls_mgm", "dh_params_col", "dh_parms") 1.7.30. ec_curve_col (string) Sets the ec_curve column name. Default value is "ec_curve". Example 1.30. Usage of ec_curve_col block modparam("tls_mgm", "ec_curve_col", "ec_curve") 1.7.31. server_domain, client_domain (string) If you only run one domain, the main one is enough. If you are running several TLS servers (that is, you have more than one listen=tls:ip:port entry in the config file), you can specify some parameters for each of them separately (not all the above). The wording 'TLS domain' means that this TLS connection will have different parameters than another TLS connection (from another TLS domain). Thus, TLS domains must are not directly related to different SIP domains, although they are often used in common. Depending on the direction of the TLS handshake, a TLS domain is called 'client domain' (=outgouing TLS connection) or 'server domain' (= incoming TLS connection). For example, TLS domains can be used in virtual hosting scenarios with TLS. OpenSIPS offers SIP service for multiple domains, e.g. atlanta.com and biloxi.com. Altough both domains will be hosted a single SIP proxy, the SIP proxy needs 2 certificates: One for atlanta.com and one for biloxi.com. For incoming TLS connections, the SIP proxy has to present the respective certificate during the TLS handshake. As the SIP proxy does not have received a SIP message yet (this is done after the TLS handshake), the SIP proxy can not retrieve the target domain (which will be usually retrieved from the domain in the request URI). Thus, distinction for these domains must be done by using multiple sockets. The socket on which the TLS connection is received, identifies the respective domain. Thus the SIP proxy is able to present the proper certificate. For outgoing TLS connections, the SIP proxy usually has to provide a client certificate. In this scenario, socket based distinction is not possible as there is no dedicated outgoing socket. Thus, the certificate selection (selection of the proper TLS client domain) will be name based. For this purpose, TLS client domains can be associated with a name (e.g. the domain can be used as name). If the SIP proxy establishes a new outgoing TLS connection, it checks for the TLS client domain AVP (parameter tls_client_domain_avp). If this AVP is set (e.g. in OpenSIPS.cfg), OpenSIPS searches for a TLS client domain with the same name and uses the certificates defined in the respective tls_client_domain section. TLS client domains can also be socket based. If name based domains are disabled or no name based AVP is found, OpenSIPS searches for socket based TLS client domains. In this case the mapping between to the TLS client domain is done based on the destination socket of the underlying outgoing TCP connection. Note: If there is already an existing TLS connection to the remote target, it will be reused wether the TLS client domain AVP matches or not. NOTE: Make sure to also configure OpenSIPS to listen on the specified IP:port. NOTE: Except tls_handshake_timeout and tls_send_timeout all TLS parameters can be set per TLS domain. If a parameter is not explicit set, the default value will be used. It's usable only if TLS support was compiled. Example 1.31. Usage of tls_client_domain and tls_server_domain block ... listen=tls:IP_2:port2 listen=tls:IP_3:port3 ... # set the TLS client domain AVP modparam("proto_tls", "tls_client_domain_avp", "400") ... # 'atlanta' server domain modparam("tls_mgm", "server_domain", "1=IP_2:port2") modparam("tls_mgm", "certificate", "1:/certs/atlanta.com/cert.pem") modparam("tls_mgm", "private_key", "1:/certs/atlanta.com/privkey.pem") modparam("tls_mgm", "ca_list", "1:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "1:tlsv1") modparam("tls_mgm", "verify_cert", "1:1") modparam("tls_mgm", "require_cert", "1:1") #'biloxy' server domain modparam("tls_mgm", "server_domain", "2=IP_3:port3") modparam("tls_mgm", "certificate", "2:/certs/biloxy.com/cert.pem") modparam("tls_mgm", "private_key", "2:/certs/biloxy.com/privkey.pem") modparam("tls_mgm", "ca_list", "2:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "2:tlsv1") modparam("tls_mgm", "verify_cert", "2:1") modparam("tls_mgm", "require_cert", "2:1") # 'atlanta' client domain modparam("tls_mgm", "client_domain", "3=IP_2:port2") modparam("tls_mgm", "certificate", "3:/certs/atlanta.com/cert.pem") modparam("tls_mgm", "private_key", "3:/certs/atlanta.com/privkey.pem") modparam("tls_mgm", "ca_list", "3:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "3:tlsv1") modparam("tls_mgm", "verify_cert", "3:1") modparam("tls_mgm", "require_cert", "3:1") #'biloxy' client domain modparam("tls_mgm", "client_domain", "4=IP_3:port3") modparam("tls_mgm", "certificate", "4:/certs/biloxy.com/cert.pem") modparam("tls_mgm", "private_key", "4:/certs/biloxy.com/privkey.pem") modparam("tls_mgm", "ca_list", "4:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "4:tlsv1") modparam("tls_mgm", "verify_cert", "4:1") modparam("tls_mgm", "require_cert", "4:1") # socket based TLS server domains (for TLS based downstream from GW prov ider) modparam("tls_mgm", "client_domain", "5=IP_5:port5") modparam("tls_mgm", "certificate", "5:/certs/atlanta.com/cert.pem") modparam("tls_mgm", "private_key", "5:/certs/atlanta.com/privkey.pem") modparam("tls_mgm", "ca_list", "5:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "5:tlsv1") modparam("tls_mgm", "verify_cert", "5:0") # socket based TLS client domains (for TLS based upstream to GW provider ) # GW IP: 1.2.3.4, GW port: 6677 modparam("tls_mgm", "client_domain", "6=1.2.3.4:6677") modparam("tls_mgm", "certificate", "6:/certs/biloxy.com/cert.pem") modparam("tls_mgm", "private_key", "6:/certs/biloxy.com/privkey.pem") modparam("tls_mgm", "ca_list", "6:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "6:tlsv1") modparam("tls_mgm", "verify_cert", "6:0") ... route{ ... # calls to other SIP domains # set the proper SSL context (certificate) for local hosted domains avp_write("$fd","$avp(fd)"); t_relay(); # uses NAPTR and SRV lookups exit; ... # calls to the PSTN GW t_relay("tls:1.2.3.4:6677"); exit; ... 1.8. Pseudo-Variables This module exports the follong pseudo-variables: Some pseudo variables are available for both, the peer'S certificate and the local certificate. Further, some parameters can be read from the “Subject†field or the “Issuer†field. 1.8.1. $tls_version $tls_version - the TLS/SSL version which is used on the TLS connection from which the message was received. String type. 1.8.2. $tls_description $tls_description - the TLS/SSL description of the TLS connection from which the message was received. String type. 1.8.3. $tls_cipher_info $tls_cipher_info - the TLS/SSL cipher which is used on the TLS connection from which the message was received. String type. 1.8.4. $tls_cipher_bits $tls_cipher_bits - the number of cipher bits which are used on the TLS connection from which the message was received. String and Integer type. 1.8.5. $tls_[peer|my]_version $tls_[peer|my]_version - the version of the certificate. String type. 1.8.6. $tls_[peer|my]_serial $tls_[peer|my]_serial - the serial number of the certificate. String and Integer type. 1.8.7. $tls_[peer|my]_[subject|issuer] $tls_[peer|my]_[subject|issuer] - ASCII dump of the fields in the issuer/subject section of the certificate. String type. Example 1.32. Example of $tls_[peer|my]_[subject|issuer] /C=AT/ST=Vienna/L=Vienna/O=enum.at/CN=enum.at 1.8.8. $tls_[peer|my]_[subject|issuer]_cn $tls_[peer|my]_[subject|issuer]_cn - commonName in the issuer/subject section of the certificate. String type. 1.8.9. $tls_[peer|my]_[subject|issuer]_locality $tls_[peer|my]_[subject|issuer]_locality - localityName in the issuer/subject section of the certificate. String type. 1.8.10. $tls_[peer|my]_[subject|issuer]_country $tls_[peer|my]_[subject|issuer]_country - countryName in the issuer/subject section of the certificate. String type. 1.8.11. $tls_[peer|my]_[subject|issuer]_state $tls_[peer|my]_[subject|issuer]_state - stateOrProvinceName in the issuer/subject section of the certificate. String type. 1.8.12. $tls_[peer|my]_[subject|issuer]_organization $tls_[peer|my]_[subject|issuer]_organization - organizationName in the issuer/subject section of the certificate. String type. 1.8.13. $tls_[peer|my]_[subject|issuer]_unit $tls_[peer|my]_[subject|issuer]_unit - organizationalUnitName in the issuer/subject section of the certificate. String type. 1.8.14. $tls_[peer|my]_san_email $tls_[peer|my]_san_email - email address in the “subject alternative name†extension. String type. 1.8.15. $tls_[peer|my]_san_hostname $tls_[peer|my]_san_hostname - hostname (DNS) in the “subject alternative name†extension. String type. 1.8.16. $tls_[peer|my]_san_uri $tls_[peer|my]_san_uri - URI in the “subject alternative name†extension. String type. 1.8.17. $tls_[peer|my]_san_ip $tls_[peer|my]_san_ip - ip address in the “subject alternative name†extension. String type. 1.8.18. $tls_peer_verified $tls_peer_verified - Returns 1 if the peer's certificate was successful verified. Otherwise it returns 0. String and Integer type. 1.8.19. $tls_peer_revoked $tls_peer_revoked - Returns 1 if the peer's certificate was revoked. Otherwise it returns 0. String and Integer type. 1.8.20. $tls_peer_expired $tls_peer_expired - Returns 1 if the peer's certificate is expired. Otherwise it returns 0. String and Integer type. 1.8.21. $tls_peer_selfsigned $tls_peer_selfsigned - Returns 1 if the peer's certificate is selfsigned. Otherwise it returns 0. String and Integer type. 1.8.22. $tls_peer_notBefore $tls_peer_notBefore - Returns the notBefore validity date of the peer's certificate. String type. 1.8.23. $tls_peer_notAfter $tls_peer_notAfter - Returns the notAfter validity date of the peer's certificate. String type. 1.9. OpenSIPS with TLS - script example IMPORTANT: The TLS support is based on TCP, and for allowing OpenSIPS to use TCP, it must be started in multi-process mode. So, there is a must to have the "fork" parameter set to "yes": NOTE: Since the TLS engine is quite memory consuming, increase the used memory by the run time parameter "-m" (see OpenSIPS -h for more details). * fork = yes Example 1.33. Script with TLS support # ----------- global configuration parameters ------------------------ log_level=3 log_stderror=no check_via=no dns=no rev_dns=no listen=_your_serv_IP_ port=5060 children=4 fifo="/tmp/opensips_fifo" # ------------------ module loading ---------------------------------- #TLS specific settings loadmodule "tls_mgm.so" loadmodule "proto_tls.so" modparam("tls_mgm", "certificate", "/path/opensipsX_cert.pem") modparam("tls_mgm", "private_key", "/path/privkey.pem") modparam("tls_mgm", "ca_list", "/path/calist.pem") modparam("tls_mgm", "ca_list", "/path/calist.pem") modparam("tls_mgm", "require_cert", "1") modparam("tls_mgm", "verify_cert", "1") alias=_DNS_ALIAS_ loadmodule "modules/sl/sl.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/mysql/mysql.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/tm/tm.so" loadmodule "modules/auth/auth.so" loadmodule "modules/auth_db/auth_db.so" loadmodule "modules/textops/textops.so" loadmodule "modules/uri_db/uri_db.so" # ----------------- setting module-specific parameters --------------- # -- auth_db params -- modparam("auth_db", "db_url", "sql_url") modparam("auth_db", "password_column", "password") modparam("auth_db", "calculate_ha1", 1) # -- registrar params -- # no multiple registrations modparam("registrar", "append_branches", 0) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; # if somene claims to belong to our domain in From, # challenge him (skip REGISTERs -- we will chalenge them later) if (from_uri==myself) { setflag(1); if ( (method=="INVITE" || method=="SUBSCRIBE" || method=="MESSAGE" ) && !(src_ip==myself) ) { if (!(proxy_authorize( "domA.net", "subscriber" ))) { proxy_challenge("domA.net","0"/*no-qop*/); break; }; if (!db_check_from()) { log("LOG: From Cheating attempt in INVITE\n"); sl_send_reply("403", "That is ugly -- use From=id next time (OB)"); break; }; }; # non-REGISTER from other domain } else if ( method=="INVITE" && uri!=myself ) { sl_send_reply("403", "No relaying"); break; }; /* ******** do record-route and loose-route ******* */ if (!(method=="REGISTER")) record_route(); if (loose_route()) { append_hf("P-hint: rr-enforced\r\n"); route(1); break; }; /* ******* check for requests targeted out of our domain ******* */ if ( uri!=myself ) { append_hf("P-hint: OUTBOUND\r\n"); if (uri=~".*@domB.net") { t_relay_to_tls("domB.net","5061"); } else if (uri=~".*@domC.net") { t_relay_to_tls("domC.net","5061"); } else { route(1); }; break; }; /* ******* divert to other domain according to prefixes ******* */ if (method!="REGISTER") { if ( uri=~"sip:201") { strip(3); sethost("domB.net"); t_relay_to_tls("domB.net","5061"); break; } else if ( uri=~"sip:202" ) { strip(3); sethost("domC.net"); t_relay_to_tls("domC.net","5061"); break; }; }; /* ************ requests for our domain ********** */ if (method=="REGISTER") { if (!www_authorize( "domA.net", "subscriber" )) { # challenge if none or invalid credentials www_challenge( "domA.net" /* realm */, "0" /* no qop -- some phones can't deal with it */); break; }; if (!db_check_to()) { log("LOG: To Cheating attempt\n"); sl_send_reply("403", "That is ugly -- use To=id in REGISTERs") ; break; }; # it is an authenticated request, update Contact database now if (!save("location")) { sl_reply_error(); }; break; }; # native SIP destinations are handled using USRLOC DB if (!lookup("location")) { # handle user which was not found sl_send_reply("404", "Not Found"); break; }; # remove all present Alert-info headers remove_hf("Alert-Info"); if (method=="INVITE" && (proto==tls || isflagset(1))) { append_hf("Alert-info: 1\r\n"); # cisco 7960 append_hf("Alert-info: Bellcore-dr4\r\n"); # cisco ATA append_hf("Alert-info: http://foo.bar/x.wav\r\n"); # snom }; # do forwarding if (!t_relay()) { sl_reply_error(); }; #end of script } 1.10. Debug TLS connections If you want to debug TLS connections, put the following log statements into your OpenSIPS.cfg. This will dump all available TLS pseudo variables. Example 1.34. Example of TLS logging xlog("L_INFO","================= start TLS pseudo variables ============ ===\n"); xlog("L_INFO","$$tls_version = '$tls_version'\n"); xlog("L_INFO","$$tls_description = '$tls_description'\n"); xlog("L_INFO","$$tls_cipher_info = '$tls_cipher_info'\n"); xlog("L_INFO","$$tls_cipher_bits = '$tls_cipher_bits'\n"); xlog("L_INFO","$$tls_peer_subject = '$tls_peer_subject'\n") ; xlog("L_INFO","$$tls_peer_issuer = '$tls_peer_issuer'\n"); xlog("L_INFO","$$tls_my_subject = '$tls_my_subject'\n"); xlog("L_INFO","$$tls_my_issuer = '$tls_my_issuer'\n"); xlog("L_INFO","$$tls_peer_version = '$tls_peer_version'\n") ; xlog("L_INFO","$$tls_my_version = '$tls_my_version'\n"); xlog("L_INFO","$$tls_peer_serial = '$tls_peer_serial'\n"); xlog("L_INFO","$$tls_my_serial = '$tls_my_serial'\n"); xlog("L_INFO","$$tls_peer_subject_cn = '$tls_peer_subject_cn'\ n"); xlog("L_INFO","$$tls_peer_issuer_cn = '$tls_peer_issuer_cn'\n "); xlog("L_INFO","$$tls_my_subject_cn = '$tls_my_subject_cn'\n" ); xlog("L_INFO","$$tls_my_issuer_cn = '$tls_my_issuer_cn'\n") ; xlog("L_INFO","$$tls_peer_subject_locality = '$tls_peer_subject_loca lity'\n"); xlog("L_INFO","$$tls_peer_issuer_locality = '$tls_peer_issuer_local ity'\n"); xlog("L_INFO","$$tls_my_subject_locality = '$tls_my_subject_locali ty'\n"); xlog("L_INFO","$$tls_my_issuer_locality = '$tls_my_issuer_localit y'\n"); xlog("L_INFO","$$tls_peer_subject_country = '$tls_peer_subject_coun try'\n"); xlog("L_INFO","$$tls_peer_issuer_country = '$tls_peer_issuer_count ry'\n"); xlog("L_INFO","$$tls_my_subject_country = '$tls_my_subject_countr y'\n"); xlog("L_INFO","$$tls_my_issuer_country = '$tls_my_issuer_country '\n"); xlog("L_INFO","$$tls_peer_subject_state = '$tls_peer_subject_stat e'\n"); xlog("L_INFO","$$tls_peer_issuer_state = '$tls_peer_issuer_state '\n"); xlog("L_INFO","$$tls_my_subject_state = '$tls_my_subject_state' \n"); xlog("L_INFO","$$tls_my_issuer_state = '$tls_my_issuer_state'\ n"); xlog("L_INFO","$$tls_peer_subject_organization = '$tls_peer_subject_orga nization'\n"); xlog("L_INFO","$$tls_peer_issuer_organization = '$tls_peer_issuer_organ ization'\n"); xlog("L_INFO","$$tls_my_subject_organization = '$tls_my_subject_organi zation'\n"); xlog("L_INFO","$$tls_my_issuer_organization = '$tls_my_issuer_organiz ation'\n"); xlog("L_INFO","$$tls_peer_subject_unit = '$tls_peer_subject_unit '\n"); xlog("L_INFO","$$tls_peer_issuer_unit = '$tls_peer_issuer_unit' \n"); xlog("L_INFO","$$tls_my_subject_unit = '$tls_my_subject_unit'\ n"); xlog("L_INFO","$$tls_my_issuer_unit = '$tls_my_issuer_unit'\n "); xlog("L_INFO","$$tls_peer_san_email = '$tls_peer_san_email'\n "); xlog("L_INFO","$$tls_my_san_email = '$tls_my_san_email'\n") ; xlog("L_INFO","$$tls_peer_san_hostname = '$tls_peer_san_hostname '\n"); xlog("L_INFO","$$tls_my_san_hostname = '$tls_my_san_hostname'\ n"); xlog("L_INFO","$$tls_peer_san_uri = '$tls_peer_san_uri'\n") ; xlog("L_INFO","$$tls_my_san_uri = '$tls_my_san_uri'\n"); xlog("L_INFO","$$tls_peer_san_ip = '$tls_peer_san_ip'\n"); xlog("L_INFO","$$tls_my_san_ip = '$tls_my_san_ip'\n"); xlog("L_INFO","$$tls_peer_verified = '$tls_peer_verified'\n" ); xlog("L_INFO","$$tls_peer_revoked = '$tls_peer_revoked'\n") ; xlog("L_INFO","$$tls_peer_expired = '$tls_peer_expired'\n") ; xlog("L_INFO","$$tls_peer_selfsigned = '$tls_peer_selfsigned'\ n"); xlog("L_INFO","$$tls_peer_notBefore = '$tls_peer_notBefore'\n "); xlog("L_INFO","$$tls_peer_notAfter = '$tls_peer_notAfter'\n" ); xlog("L_INFO","================= end TLS pseudo variables ============== =\n"); Chapter 2. Developer Guide 2.1. API Functions 2.1.1. find_server_domain struct tls_domain *find_server_domain(struct ip_addr *ip, unsigned short port); Find a TLS server domain with given ip and port (local listening socket). 2.1.2. find_client_domain struct tls_domain *find_client_domain(struct ip_addr *ip, unsigned short port); Find TLS client domain. 2.1.3. get_handshake_timeout int get_handshake_timeout(void); Returns the handshanke timeout. 2.1.4. get_send_timeout int get_send_timeout(void); Returns the send timeout. 2.2. TLS_CONFIG It contains configuration variables for OpenSIPS's TLS (timeouts, file paths, etc). 2.3. TLS_INIT Initialization related functions and parameters. 2.3.1. ssl context extern SSL_CTX *default_client_ctx; The ssl context is a member of the TLS domain strcuture. Thus, every TLS domain, default and virtual - servers and clients, have its own SSL context. 2.3.2. pre_init_tls int init_tls(void); Called once to pre_initialize the tls subsystem, from the main(). Called before parsing the configuration file. 2.3.3. init_tls int init_tls(void); Called once to initialize the tls subsystem, from the main(). Called after parsing the configuration file. 2.3.4. destroy_tls void destroy_tls(void); Called once, just before cleanup. 2.3.5. tls_init int tls_init(struct socket_info *c); Called once for each tls socket created, from main.c 2.3.6. os_malloc, os_realloc, os_free Wrapper functions around the shm_* functions. OpenSSL uses non-shared memory to create its objects, thus it would not work in OpenSIPS. By creating these wrappers and configuring OpenSSL to use them instead of its default memory functions, we have all OpenSSL objects in shared memory, ready to use. 2.4. TLS_DOMAIN 2.4.1. tls_domains extern struct tls_domain *tls_default_server_domain; The default TLS server domain. extern struct tls_domain *tls_default_client_domain; The default TLS client domain. extern struct tls_domain *tls_server_domains; List with defined server domains. extern struct tls_domain *tls_client_domains; List with defined client domains. 2.4.2. tls_find_server_domain struct tls_domain *tls_find_server_domain(struct ip_addr *ip, unsigned short port); Find a TLS server domain with given ip and port (local listening socket). 2.4.3. tls_find_client_domain struct tls_domain *tls_find_client_domain(struct ip_addr *ip, unsigned short port); Find TLS client domain. 2.4.4. tls_find_client_domain_addr struct tls_domain *tls_find_client_domain_addr(struct ip_addr *ip, unsigned short port); Find TLS client domain with given ip and port (socket of the remote destination). 2.4.5. tls_find_client_domain_name struct tls_domain *tls_find_client_name(str name); Find TLS client domain with given name. 2.4.6. tls_new__domain struct tls_domain *tls_new_domain(int type); Creates new TLS: allocate memory, set the type and initialize members 2.4.7. tls_new_server_domain int tls_new_server_domain(struct ip_addr *ip, unsigned short port); Creates and adds to the list of TLS server domains a new domain. 2.4.8. tls_new_client_domain int tls_new_client_domain(struct ip_addr *ip, unsigned short port); Creates and adds to the list of TLS client domains a new socket based domain. 2.4.9. tls_new_client_domain_name int tls_new_client_domain_name(char *s, int len); Creates and adds to the list of TLS client domains a new name based domain. 2.4.10. tls_free_domains void tls_free_domains(void); Cleans up the entire domain lists. opensips-2.2.2/modules/tls_mgm/api.h000066400000000000000000000022141300170765700174160ustar00rootroot00000000000000/* * File: api.h * Author: cristi * * Created on September 1, 2015, 5:23 PM */ #ifndef TLS_API_H #define TLS_API_H typedef struct tls_domain * (*tls_find_server_domain_f) (struct ip_addr *, unsigned short); typedef struct tls_domain * (*tls_find_client_domain_f) (struct ip_addr *, unsigned short); typedef int (*get_send_timeout_f) (void); typedef int (*get_handshake_timeout_f) (void); typedef void (*tls_release_domain_f) (struct tls_domain *); struct tls_mgm_binds { get_send_timeout_f get_send_timeout; get_handshake_timeout_f get_handshake_timeout; tls_find_server_domain_f find_server_domain; tls_find_client_domain_f find_client_domain; tls_release_domain_f release_domain; }; typedef int(*load_tls_mgm_f)(struct tls_mgm_binds *binds); static inline int load_tls_mgm_api(struct tls_mgm_binds *binds) { load_tls_mgm_f load_tls; /* import the DLG auto-loading function */ if (!(load_tls = (load_tls_mgm_f) find_export("load_tls_mgm", 0, 0))) return -1; /* let the auto-loading function load all DLG stuff */ if (load_tls(binds) == -1) return -1; return 0; } #endif /* TLS_API_H */ opensips-2.2.2/modules/tls_mgm/doc/000077500000000000000000000000001300170765700172425ustar00rootroot00000000000000opensips-2.2.2/modules/tls_mgm/doc/tls_mgm.xml000066400000000000000000000047051300170765700214340ustar00rootroot00000000000000 %docentities; ]> TLS_MGM module &osipsname; Peter Griffiths unknown
peter_grf@yahoo.com
Klaus Darilion enum.at
klaus.darilion@enum.at
Klaus Darilion
klaus.darilion@enum.at
Bogdan-Andrei Iancu
bogdan@opensips.org
Cesc Santasusana
cesc.santa@gmail.com
Klaus Darilion
klaus.darilion@nic.at
Christian Lahme
christian.lahme@secusmart.com
Ionut-Razvan Ionita
ionutionita@opensips.org
Marius Cristian Eseanu
eseanu.cristian@gmail.com
2005 &voicesystem; 2005 Cesc Santasusana 2006 enum.at 2013 Secusmart GmbH 2015 &osipssolname;
&admin; &devel;
opensips-2.2.2/modules/tls_mgm/doc/tls_mgm_admin.xml000066400000000000000000001355231300170765700226070ustar00rootroot00000000000000 &adminguide;
Overview This module is a management module for TLS certificates and parameters. It provides an interfaces for all the modules that use the TLS protocol. It also implements TLS related functions to use in the routing script, and exports pseudo variables with certificate and TLS parameters.
History The TLS support was originally developed by Peter Griffiths and posted as a patch on SER development mailing list. Thanks to Cesc Santasusana, several problems were fixed and some improvements were added. The TLS support was simultaneously added in both projects. In SER, the support was committed in a separate experimental CVS tree, as patch to the main CVS tree. In OpenSIPS, the support was integrated directly into the CVS tree, as a built-in component, and is part of stable OpenSIPS since release >=1.0.0. Starting from OpenSIPS 2.2, the certificates managemnet has been decoupled from the TLS communication in two different modules: dh_params which handles the TLS communication and tls_mgm which handles TLS handshake (certificates and parameters).
Usage This module is used to provision TLS certificates and parameters for all the modules that use TLS transport (currently only proto_tls). The module supports multiple virtual domains that can be assigned to different listeners (servers) or new connections (clients). Each TLS module that uses this management module should assign itself to one or more domains. A script example which details this module's usage can be found in .
Dependencies of external libraries &osips; TLS v1.0 support requires the following packages: openssl or libssl >= 0.9.6 openssl-dev or libssl-dev &osips; TLS v1.1/1.2 support requires the following packages: openssl or libssl >= 1.0.1e openssl-dev or libssl-dev
Exported Functions
<function moreinfo="none">is_peer_verified</function> Returns 1 if the message is received via TLS and the peer was verified during TLS connection handshake, otherwise it returns -1 This function can be used from REQUEST_ROUTE. <function>is_peer_verified</function> usage ... if (is_peer_verified()) { xlog("L_INFO","request from verified TLS peer\n"); } else { xlog("L_INFO","request not verified\n"); } ...
Exported MI Functions
<function moreinfo="none">tls_list</function> List all domains information.
<function moreinfo="none">tls_reload</function> Reloads information from the database.
&osips; Exported parameters All these parameters can be used from the opensips.cfg file, to configure the behavior of &osips;-TLS.
<varname>listen</varname>=interface Not specific to TLS. Allows to specify the protocol (udp, tcp, tls), the IP address and the port where the listening server will be. Set <varname>listen</varname> variable ... listen = tls:1.2.3.4:5061 ...
<varname>tls_method</varname> [(string):](string) Sets the TLS protocol. The first parameter, if set, represents the id of the domain. TLS method which can be: TLSv1_2 - means &osips; will accept only TLSv1.2 connections (rfc3261 conformant). TLSv1 - means &osips; will accept only TLSv1 connections (rfc3261 conformant). SSLv3 - means &osips; will accept only SSLv3 connections SSLv2 - means &osips; will accept only SSLv2 connections (almost all old clients support this). SSLv23 - means &osips; will accept any of the above methods, but the initial SSL hello must be v2 (in the initial hello all the supported protocols are advertised enabling switching to a higher and more secure version). The initial v2 hello means it will not accept connections from SSLv3 or TLSv1 only clients. Default value is SSLv23. Best is to use SSLv23, for extended compatibility. Using any of the other will restrict the version to just that one version. In fact, SSLv2 is disabled in the source code; to use it, you need to edit tls/tls_init.c If you want RFC3261 conformance and all your clients support TLSv1 (or you are planning to use encrypted "tunnels" only between different &osips; proxies) use TLSv1. If you want to support older clients use SSLv23 (in fact most of the applications with SSL support use the SSLv23 method). Set <varname>tls_method</varname> variable ... modparam("tls_mgm", "tls_method", "TLSv1") modparam("tls_mgm", "tls_method", "dom:TLSv1") ...
<varname>certificate</varname> [(string):](string) Public certificate file for &osips;. It will be used as server-side certificate for incoming TLS connections, and as a client-side certificate for outgoing TLS connections. The first parameter, if set, represents the id of the domain. Default value is "CFG_DIR/cert.pem". Set <varname>certificate</varname> variable ... modparam("tls_mgm", "certificate", "/mycerts/certs/opensips_server_cert.pem") modparam("tls_mgm", "certificate", "dom:/mycerts/certs/opensips_server_cert.pem") ...
<varname>private_key</varname> [(string):](string) Private key of the above certificate. I must be kept in a safe place with tight permissions! The first parameter, if set, represents the id of the domain. Default value is "CFG_DIR/cert.pem". Set <varname>private_key</varname> variable ... modparam("tls_mgm", "private_key", "/mycerts/private/prik.pem") modparam("tls_mgm", "private_key", "dom:/mycerts/private/prik.pem") ...
<varname>ca_list</varname> [(string):]((string) List of trusted CAs. The file contains the certificates accepted, one after the other. It MUST be a file, not a folder. The first parameter, if set, represents the id of the domain. Default value is "". Set <varname>ca_list</varname> variable ... modparam("tls_mgm", "ca_list", "/mycerts/certs/ca_list.pem") modparam("tls_mgm", "ca_list", "dom:/mycerts/certs/ca_list.pem") ...
<varname>ca_dir</varname> [(string):](string) Directory storing trusted CAs. The path contains the certificates accepted, each as hash which is linked to certificate file. The first parameter, if set, represents the id of the domain. Default value is "". Set <varname>ca_dir</varname> variable ... modparam("tls_mgm", "ca_dir", "/mycerts/certs") modparam("tls_mgm", "ca_dir", "dom:/mycerts/certs") ...
<varname>ciphers_list</varname> [(string):](string) You can specify the list of algorithms for authentication and encryption that you allow. The first parameter, if set, represents the id of the domain. To obtain a list of ciphers and then choose, use the openssl application: openssl ciphers 'ALL:eNULL:!LOW:!EXPORT' Do not use the NULL algorithms (no encryption) ... only for testing!!! It defaults to the OpenSSL default ciphers. Set <varname>ciphers_list</varname> variable ... modparam("tls_mgm", "ciphers_list", "NULL") modparam("tls_mgm", "ciphers_list", "dom:NULL") ...
<varname>dh_params</varname> [(string):](string) You can specify a file which contains Diffie-Hellman parameters as a PEM-file. This is needed if you would like to specify ciphers including Diffie-Hellman mode. The first parameter, if set, represents the id of the domain. It defaults to not set a dh param file. Set <varname>dh_params</varname> variable ... modparam("tls_mgm", "dh_params", "/etc/pki/CA/dh1024.pem") modparam("tls_mgm", "dh_params", "dom:/etc/pki/CA/dh1024.pem") ...
<varname>ec_curve</varname>[(string):](string) You can specify an elliptic curve which should be used for ciphers which demand an elliptic curve. The first parameter, if set, represents the id of the domain. It's usable only if TLS v1.1/1.2 support was compiled. A list of curves which can be used you can get by openssl ecparam -list_curve It defaults to not set a elliptic curve.
<varname>verify_cert</varname> [(string):](string) and <varname>require_cert</varname>[(string):](string) Technically, verify_cert activates SSL_VERIFY_PEER in the ssl_context. 'require_cert' does the same with SSL_VERIFY_FAIL_IF_NO_PEER_CERT, which is only possible if SSL_VERIFY_PEER is also turned on. Since version 2.1, these parameters act have been reduced to only one. They act both on client side and server side if no domain specified, elseway they act on a specific domain, depending on the first parameter. These two parameters are used for incoming TLS connections, where &osips; acts as server. It's usable only if TLS support was compiled. Default value for both is 1. Set <varname>verify_cert & require_cert </varname> variable ... # turn on the strictest and strongest authentication possible modparam("tls_mgm", "require_cert", "1") modparam("tls_mgm", "require_cert", "1:1") modparam("tls_mgm", "verify_cert", "0") modparam("tls_mgm", "verify_cert", "1:1") ...
<varname>tls_handshake_timeout</varname> (integer) and <varname>tls_send_timeout</varname> (integer) Timeouts ... advanced users only Default value for both is 30. Set <varname>tls_handshake_timeout & tls_send_timeout </varname> variable ... modparam("tls_mgm", "tls_handshake_timeout", 119) # number of seconds modparam("tls_mgm", "tls_send_timeout", 121) # number of seconds ...
<varname>client_domain_avp</varname> (integer) This sets the interger AVP used for name based TLS server domains (please see tls_client_domain for more details). Setting the value to 0 disables name based TLS client domains. It's usable only if TLS support was compiled. Default value is 0. Set <varname>tls_client_domain_avp</varname> variable ... modparam("tls_mgm", "tls_client_domain_avp", "400") ...
<varname>db_mode</varname> (integer) When db_mode is enabled (1), this module cannot accept new domains configuration from the script. Default value is 0. (not enabled) Usage of <varname>db_mode</varname> block modparam("tls_mgm", "db_mode", 1)
<varname>db_url</varname> (string) The database url. It cannot be NULL. Usage of <varname>db_url</varname> block modparam("tls_mgm", "db_url", "mysql://root:admin@localhost/opensips")
<varname>db_table</varname> (string) Sets the database table name. Default value is "tls_mgm". Usage of <varname>db_table</varname> block modparam("tls_mgm", "db_table", "tls_mgm")
<varname>id_col</varname> (string) Sets the id column name. Default value is "id". Usage of <varname>id_col</varname> block modparam("tls_mgm", "id_col", "id")
<varname>address_col</varname> (string) Sets the address column name. Default value is "address". Usage of <varname>address_col</varname> block modparam("tls_mgm", "address_col", "addr")
<varname>address_col</varname> (string) Sets the address column name. Default value is "address". Usage of <varname>address_col</varname> block modparam("tls_mgm", "address_col", "addr")
<varname>tls_method_col</varname> (string) Sets the method column name. Default value is "method". Usage of <varname>tls_method_col</varname> block modparam("tls_mgm", "tls_method_col", "method")
<varname>verify_cert_col</varname> (string) Sets the verrify certificate column name. Default value is "verify_cert". Usage of <varname>vertify_cert_col</varname> block modparam("tls_mgm", "verify_cert_col", "verify_cert")
<varname>require_cert_col</varname> (string) Sets the require certificate column name. Default value is "require_cert". Usage of <varname>require_cert_col</varname> block modparam("tls_mgm", "require_cert_col", "req")
<varname>certificate_col</varname> (string) Sets the certificate column name. Default value is "certificate". Usage of <varname>certificate_col</varname> block modparam("tls_mgm", "certificate_col", "certificate")
<varname>private_key_col</varname> (string) Sets the private key column name. Default value is "private_key". Usage of <varname>private_key_col</varname> block modparam("tls_mgm", "private_key_col", "pk")
<varname>crl_check_all_col</varname> (string) Sets the crl_check_all column name. Default value is "crl_check_all". Usage of <varname>crl_check_all</varname> block modparam("tls_mgm", "crl_check_all_col", "crl_check")
<varname>crl_dir_col</varname> (string) Sets the crl directory column name. Default value is "crl_dir". Usage of <varname>crl_dir_col</varname> block modparam("tls_mgm", "crl_dir_col", "crl_dir")
<varname>ca_list_col</varname> (string) Sets the CA list column name. Default value is "ca_list". Usage of <varname>ca_list_col</varname> block modparam("tls_mgm", "ca_list_col", "ca_list")
<varname>ca_dir_col</varname> (string) Sets the CA directory column name. Default value is "ca_dir". Usage of <varname>ca_dir_col</varname> block modparam("tls_mgm", "ca_dir_col", "ca_dir")
<varname>cipher_list_col</varname> (string) Sets the cipher list column name. Default value is "cipher_list". Usage of <varname>cipher_list_col</varname> block modparam("tls_mgm", "cipher_list_col", "cipher_list")
<varname>dh_params_col</varname> (string) Sets the Diffie-Hellmann parameters column name. Default value is "dh_params". Usage of <varname>dh_params_col</varname> block modparam("tls_mgm", "dh_params_col", "dh_parms")
<varname>ec_curve_col</varname> (string) Sets the ec_curve column name. Default value is "ec_curve". Usage of <varname>ec_curve_col</varname> block modparam("tls_mgm", "ec_curve_col", "ec_curve")
<varname>server_domain, client_domain</varname> (string) If you only run one domain, the main one is enough. If you are running several TLS servers (that is, you have more than one listen=tls:ip:port entry in the config file), you can specify some parameters for each of them separately (not all the above). The wording 'TLS domain' means that this TLS connection will have different parameters than another TLS connection (from another TLS domain). Thus, TLS domains must are not directly related to different SIP domains, although they are often used in common. Depending on the direction of the TLS handshake, a TLS domain is called 'client domain' (=outgouing TLS connection) or 'server domain' (= incoming TLS connection). For example, TLS domains can be used in virtual hosting scenarios with TLS. &osips; offers SIP service for multiple domains, e.g. atlanta.com and biloxi.com. Altough both domains will be hosted a single SIP proxy, the SIP proxy needs 2 certificates: One for atlanta.com and one for biloxi.com. For incoming TLS connections, the SIP proxy has to present the respective certificate during the TLS handshake. As the SIP proxy does not have received a SIP message yet (this is done after the TLS handshake), the SIP proxy can not retrieve the target domain (which will be usually retrieved from the domain in the request URI). Thus, distinction for these domains must be done by using multiple sockets. The socket on which the TLS connection is received, identifies the respective domain. Thus the SIP proxy is able to present the proper certificate. For outgoing TLS connections, the SIP proxy usually has to provide a client certificate. In this scenario, socket based distinction is not possible as there is no dedicated outgoing socket. Thus, the certificate selection (selection of the proper TLS client domain) will be name based. For this purpose, TLS client domains can be associated with a name (e.g. the domain can be used as name). If the SIP proxy establishes a new outgoing TLS connection, it checks for the TLS client domain AVP (parameter tls_client_domain_avp). If this AVP is set (e.g. in &osips;.cfg), &osips; searches for a TLS client domain with the same name and uses the certificates defined in the respective tls_client_domain section. TLS client domains can also be socket based. If name based domains are disabled or no name based AVP is found, &osips; searches for socket based TLS client domains. In this case the mapping between to the TLS client domain is done based on the destination socket of the underlying outgoing TCP connection. Note: If there is already an existing TLS connection to the remote target, it will be reused wether the TLS client domain AVP matches or not. NOTE: Make sure to also configure &osips; to listen on the specified IP:port. NOTE: Except tls_handshake_timeout and tls_send_timeout all TLS parameters can be set per TLS domain. If a parameter is not explicit set, the default value will be used. It's usable only if TLS support was compiled. Usage of <varname>tls_client_domain</varname> and <varname>tls_server_domain</varname> block ... listen=tls:IP_2:port2 listen=tls:IP_3:port3 ... # set the TLS client domain AVP modparam("proto_tls", "tls_client_domain_avp", "400") ... # 'atlanta' server domain modparam("tls_mgm", "server_domain", "1=IP_2:port2") modparam("tls_mgm", "certificate", "1:/certs/atlanta.com/cert.pem") modparam("tls_mgm", "private_key", "1:/certs/atlanta.com/privkey.pem") modparam("tls_mgm", "ca_list", "1:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "1:tlsv1") modparam("tls_mgm", "verify_cert", "1:1") modparam("tls_mgm", "require_cert", "1:1") #'biloxy' server domain modparam("tls_mgm", "server_domain", "2=IP_3:port3") modparam("tls_mgm", "certificate", "2:/certs/biloxy.com/cert.pem") modparam("tls_mgm", "private_key", "2:/certs/biloxy.com/privkey.pem") modparam("tls_mgm", "ca_list", "2:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "2:tlsv1") modparam("tls_mgm", "verify_cert", "2:1") modparam("tls_mgm", "require_cert", "2:1") # 'atlanta' client domain modparam("tls_mgm", "client_domain", "3=IP_2:port2") modparam("tls_mgm", "certificate", "3:/certs/atlanta.com/cert.pem") modparam("tls_mgm", "private_key", "3:/certs/atlanta.com/privkey.pem") modparam("tls_mgm", "ca_list", "3:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "3:tlsv1") modparam("tls_mgm", "verify_cert", "3:1") modparam("tls_mgm", "require_cert", "3:1") #'biloxy' client domain modparam("tls_mgm", "client_domain", "4=IP_3:port3") modparam("tls_mgm", "certificate", "4:/certs/biloxy.com/cert.pem") modparam("tls_mgm", "private_key", "4:/certs/biloxy.com/privkey.pem") modparam("tls_mgm", "ca_list", "4:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "4:tlsv1") modparam("tls_mgm", "verify_cert", "4:1") modparam("tls_mgm", "require_cert", "4:1") # socket based TLS server domains (for TLS based downstream from GW provider) modparam("tls_mgm", "client_domain", "5=IP_5:port5") modparam("tls_mgm", "certificate", "5:/certs/atlanta.com/cert.pem") modparam("tls_mgm", "private_key", "5:/certs/atlanta.com/privkey.pem") modparam("tls_mgm", "ca_list", "5:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "5:tlsv1") modparam("tls_mgm", "verify_cert", "5:0") # socket based TLS client domains (for TLS based upstream to GW provider) # GW IP: 1.2.3.4, GW port: 6677 modparam("tls_mgm", "client_domain", "6=1.2.3.4:6677") modparam("tls_mgm", "certificate", "6:/certs/biloxy.com/cert.pem") modparam("tls_mgm", "private_key", "6:/certs/biloxy.com/privkey.pem") modparam("tls_mgm", "ca_list", "6:/certs/wellknownCAs") modparam("tls_mgm", "tls_method", "6:tlsv1") modparam("tls_mgm", "verify_cert", "6:0") ... route{ ... # calls to other SIP domains # set the proper SSL context (certificate) for local hosted domains avp_write("$fd","$avp(fd)"); t_relay(); # uses NAPTR and SRV lookups exit; ... # calls to the PSTN GW t_relay("tls:1.2.3.4:6677"); exit; ...
Pseudo-Variables This module exports the follong pseudo-variables: Some pseudo variables are available for both, the peer'S certificate and the local certificate. Further, some parameters can be read from the Subject field or the Issuer field.
$tls_version $tls_version - the TLS/SSL version which is used on the TLS connection from which the message was received. String type.
$tls_description $tls_description - the TLS/SSL description of the TLS connection from which the message was received. String type.
$tls_cipher_info $tls_cipher_info - the TLS/SSL cipher which is used on the TLS connection from which the message was received. String type.
$tls_cipher_bits $tls_cipher_bits - the number of cipher bits which are used on the TLS connection from which the message was received. String and Integer type.
$tls_[peer|my]_version $tls_[peer|my]_version - the version of the certificate. String type.
$tls_[peer|my]_serial $tls_[peer|my]_serial - the serial number of the certificate. String and Integer type.
$tls_[peer|my]_[subject|issuer] $tls_[peer|my]_[subject|issuer] - ASCII dump of the fields in the issuer/subject section of the certificate. String type. Example of <varname>$tls_[peer|my]_[subject|issuer]</varname> /C=AT/ST=Vienna/L=Vienna/O=enum.at/CN=enum.at
$tls_[peer|my]_[subject|issuer]_cn $tls_[peer|my]_[subject|issuer]_cn - commonName in the issuer/subject section of the certificate. String type.
$tls_[peer|my]_[subject|issuer]_locality $tls_[peer|my]_[subject|issuer]_locality - localityName in the issuer/subject section of the certificate. String type.
$tls_[peer|my]_[subject|issuer]_country $tls_[peer|my]_[subject|issuer]_country - countryName in the issuer/subject section of the certificate. String type.
$tls_[peer|my]_[subject|issuer]_state $tls_[peer|my]_[subject|issuer]_state - stateOrProvinceName in the issuer/subject section of the certificate. String type.
$tls_[peer|my]_[subject|issuer]_organization $tls_[peer|my]_[subject|issuer]_organization - organizationName in the issuer/subject section of the certificate. String type.
$tls_[peer|my]_[subject|issuer]_unit $tls_[peer|my]_[subject|issuer]_unit - organizationalUnitName in the issuer/subject section of the certificate. String type.
$tls_[peer|my]_san_email $tls_[peer|my]_san_email - email address in the subject alternative name extension. String type.
$tls_[peer|my]_san_hostname $tls_[peer|my]_san_hostname - hostname (DNS) in the subject alternative name extension. String type.
$tls_[peer|my]_san_uri $tls_[peer|my]_san_uri - URI in the subject alternative name extension. String type.
$tls_[peer|my]_san_ip $tls_[peer|my]_san_ip - ip address in the subject alternative name extension. String type.
$tls_peer_verified $tls_peer_verified - Returns 1 if the peer's certificate was successful verified. Otherwise it returns 0. String and Integer type.
$tls_peer_revoked $tls_peer_revoked - Returns 1 if the peer's certificate was revoked. Otherwise it returns 0. String and Integer type.
$tls_peer_expired $tls_peer_expired - Returns 1 if the peer's certificate is expired. Otherwise it returns 0. String and Integer type.
$tls_peer_selfsigned $tls_peer_selfsigned - Returns 1 if the peer's certificate is selfsigned. Otherwise it returns 0. String and Integer type.
$tls_peer_notBefore $tls_peer_notBefore - Returns the notBefore validity date of the peer's certificate. String type.
$tls_peer_notAfter $tls_peer_notAfter - Returns the notAfter validity date of the peer's certificate. String type.
&osips; with TLS - script example IMPORTANT: The TLS support is based on TCP, and for allowing &osips; to use TCP, it must be started in multi-process mode. So, there is a must to have the "fork" parameter set to "yes": NOTE: Since the TLS engine is quite memory consuming, increase the used memory by the run time parameter "-m" (see &osips; -h for more details). fork = yes Script with TLS support # ----------- global configuration parameters ------------------------ log_level=3 log_stderror=no check_via=no dns=no rev_dns=no listen=_your_serv_IP_ port=5060 children=4 fifo="/tmp/opensips_fifo" # ------------------ module loading ---------------------------------- #TLS specific settings loadmodule "tls_mgm.so" loadmodule "proto_tls.so" modparam("tls_mgm", "certificate", "/path/opensipsX_cert.pem") modparam("tls_mgm", "private_key", "/path/privkey.pem") modparam("tls_mgm", "ca_list", "/path/calist.pem") modparam("tls_mgm", "ca_list", "/path/calist.pem") modparam("tls_mgm", "require_cert", "1") modparam("tls_mgm", "verify_cert", "1") alias=_DNS_ALIAS_ loadmodule "modules/sl/sl.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/mysql/mysql.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/tm/tm.so" loadmodule "modules/auth/auth.so" loadmodule "modules/auth_db/auth_db.so" loadmodule "modules/textops/textops.so" loadmodule "modules/uri_db/uri_db.so" # ----------------- setting module-specific parameters --------------- # -- auth_db params -- modparam("auth_db", "db_url", "sql_url") modparam("auth_db", "password_column", "password") modparam("auth_db", "calculate_ha1", 1) # -- registrar params -- # no multiple registrations modparam("registrar", "append_branches", 0) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; # if somene claims to belong to our domain in From, # challenge him (skip REGISTERs -- we will chalenge them later) if (from_uri==myself) { setflag(1); if ( (method=="INVITE" || method=="SUBSCRIBE" || method=="MESSAGE") && !(src_ip==myself) ) { if (!(proxy_authorize( "domA.net", "subscriber" ))) { proxy_challenge("domA.net","0"/*no-qop*/); break; }; if (!db_check_from()) { log("LOG: From Cheating attempt in INVITE\n"); sl_send_reply("403", "That is ugly -- use From=id next time (OB)"); break; }; }; # non-REGISTER from other domain } else if ( method=="INVITE" && uri!=myself ) { sl_send_reply("403", "No relaying"); break; }; /* ******** do record-route and loose-route ******* */ if (!(method=="REGISTER")) record_route(); if (loose_route()) { append_hf("P-hint: rr-enforced\r\n"); route(1); break; }; /* ******* check for requests targeted out of our domain ******* */ if ( uri!=myself ) { append_hf("P-hint: OUTBOUND\r\n"); if (uri=~".*@domB.net") { t_relay_to_tls("domB.net","5061"); } else if (uri=~".*@domC.net") { t_relay_to_tls("domC.net","5061"); } else { route(1); }; break; }; /* ******* divert to other domain according to prefixes ******* */ if (method!="REGISTER") { if ( uri=~"sip:201") { strip(3); sethost("domB.net"); t_relay_to_tls("domB.net","5061"); break; } else if ( uri=~"sip:202" ) { strip(3); sethost("domC.net"); t_relay_to_tls("domC.net","5061"); break; }; }; /* ************ requests for our domain ********** */ if (method=="REGISTER") { if (!www_authorize( "domA.net", "subscriber" )) { # challenge if none or invalid credentials www_challenge( "domA.net" /* realm */, "0" /* no qop -- some phones can't deal with it */); break; }; if (!db_check_to()) { log("LOG: To Cheating attempt\n"); sl_send_reply("403", "That is ugly -- use To=id in REGISTERs"); break; }; # it is an authenticated request, update Contact database now if (!save("location")) { sl_reply_error(); }; break; }; # native SIP destinations are handled using USRLOC DB if (!lookup("location")) { # handle user which was not found sl_send_reply("404", "Not Found"); break; }; # remove all present Alert-info headers remove_hf("Alert-Info"); if (method=="INVITE" && (proto==tls || isflagset(1))) { append_hf("Alert-info: 1\r\n"); # cisco 7960 append_hf("Alert-info: Bellcore-dr4\r\n"); # cisco ATA append_hf("Alert-info: http://foo.bar/x.wav\r\n"); # snom }; # do forwarding if (!t_relay()) { sl_reply_error(); }; #end of script }
Debug TLS connections If you want to debug TLS connections, put the following log statements into your &osips;.cfg. This will dump all available TLS pseudo variables. Example of TLS logging xlog("L_INFO","================= start TLS pseudo variables ===============\n"); xlog("L_INFO","$$tls_version = '$tls_version'\n"); xlog("L_INFO","$$tls_description = '$tls_description'\n"); xlog("L_INFO","$$tls_cipher_info = '$tls_cipher_info'\n"); xlog("L_INFO","$$tls_cipher_bits = '$tls_cipher_bits'\n"); xlog("L_INFO","$$tls_peer_subject = '$tls_peer_subject'\n"); xlog("L_INFO","$$tls_peer_issuer = '$tls_peer_issuer'\n"); xlog("L_INFO","$$tls_my_subject = '$tls_my_subject'\n"); xlog("L_INFO","$$tls_my_issuer = '$tls_my_issuer'\n"); xlog("L_INFO","$$tls_peer_version = '$tls_peer_version'\n"); xlog("L_INFO","$$tls_my_version = '$tls_my_version'\n"); xlog("L_INFO","$$tls_peer_serial = '$tls_peer_serial'\n"); xlog("L_INFO","$$tls_my_serial = '$tls_my_serial'\n"); xlog("L_INFO","$$tls_peer_subject_cn = '$tls_peer_subject_cn'\n"); xlog("L_INFO","$$tls_peer_issuer_cn = '$tls_peer_issuer_cn'\n"); xlog("L_INFO","$$tls_my_subject_cn = '$tls_my_subject_cn'\n"); xlog("L_INFO","$$tls_my_issuer_cn = '$tls_my_issuer_cn'\n"); xlog("L_INFO","$$tls_peer_subject_locality = '$tls_peer_subject_locality'\n"); xlog("L_INFO","$$tls_peer_issuer_locality = '$tls_peer_issuer_locality'\n"); xlog("L_INFO","$$tls_my_subject_locality = '$tls_my_subject_locality'\n"); xlog("L_INFO","$$tls_my_issuer_locality = '$tls_my_issuer_locality'\n"); xlog("L_INFO","$$tls_peer_subject_country = '$tls_peer_subject_country'\n"); xlog("L_INFO","$$tls_peer_issuer_country = '$tls_peer_issuer_country'\n"); xlog("L_INFO","$$tls_my_subject_country = '$tls_my_subject_country'\n"); xlog("L_INFO","$$tls_my_issuer_country = '$tls_my_issuer_country'\n"); xlog("L_INFO","$$tls_peer_subject_state = '$tls_peer_subject_state'\n"); xlog("L_INFO","$$tls_peer_issuer_state = '$tls_peer_issuer_state'\n"); xlog("L_INFO","$$tls_my_subject_state = '$tls_my_subject_state'\n"); xlog("L_INFO","$$tls_my_issuer_state = '$tls_my_issuer_state'\n"); xlog("L_INFO","$$tls_peer_subject_organization = '$tls_peer_subject_organization'\n"); xlog("L_INFO","$$tls_peer_issuer_organization = '$tls_peer_issuer_organization'\n"); xlog("L_INFO","$$tls_my_subject_organization = '$tls_my_subject_organization'\n"); xlog("L_INFO","$$tls_my_issuer_organization = '$tls_my_issuer_organization'\n"); xlog("L_INFO","$$tls_peer_subject_unit = '$tls_peer_subject_unit'\n"); xlog("L_INFO","$$tls_peer_issuer_unit = '$tls_peer_issuer_unit'\n"); xlog("L_INFO","$$tls_my_subject_unit = '$tls_my_subject_unit'\n"); xlog("L_INFO","$$tls_my_issuer_unit = '$tls_my_issuer_unit'\n"); xlog("L_INFO","$$tls_peer_san_email = '$tls_peer_san_email'\n"); xlog("L_INFO","$$tls_my_san_email = '$tls_my_san_email'\n"); xlog("L_INFO","$$tls_peer_san_hostname = '$tls_peer_san_hostname'\n"); xlog("L_INFO","$$tls_my_san_hostname = '$tls_my_san_hostname'\n"); xlog("L_INFO","$$tls_peer_san_uri = '$tls_peer_san_uri'\n"); xlog("L_INFO","$$tls_my_san_uri = '$tls_my_san_uri'\n"); xlog("L_INFO","$$tls_peer_san_ip = '$tls_peer_san_ip'\n"); xlog("L_INFO","$$tls_my_san_ip = '$tls_my_san_ip'\n"); xlog("L_INFO","$$tls_peer_verified = '$tls_peer_verified'\n"); xlog("L_INFO","$$tls_peer_revoked = '$tls_peer_revoked'\n"); xlog("L_INFO","$$tls_peer_expired = '$tls_peer_expired'\n"); xlog("L_INFO","$$tls_peer_selfsigned = '$tls_peer_selfsigned'\n"); xlog("L_INFO","$$tls_peer_notBefore = '$tls_peer_notBefore'\n"); xlog("L_INFO","$$tls_peer_notAfter = '$tls_peer_notAfter'\n"); xlog("L_INFO","================= end TLS pseudo variables ===============\n");
opensips-2.2.2/modules/tls_mgm/doc/tls_mgm_devel.xml000066400000000000000000000142351300170765700226120ustar00rootroot00000000000000 &develguide;
API Functions
find_server_domain struct tls_domain *find_server_domain(struct ip_addr *ip, unsigned short port); Find a TLS server domain with given ip and port (local listening socket).
find_client_domain struct tls_domain *find_client_domain(struct ip_addr *ip, unsigned short port); Find TLS client domain.
get_handshake_timeout int get_handshake_timeout(void); Returns the handshanke timeout.
get_send_timeout int get_send_timeout(void); Returns the send timeout.
TLS_CONFIG It contains configuration variables for &osips;'s TLS (timeouts, file paths, etc).
TLS_INIT Initialization related functions and parameters.
ssl context extern SSL_CTX *default_client_ctx; The ssl context is a member of the TLS domain strcuture. Thus, every TLS domain, default and virtual - servers and clients, have its own SSL context.
pre_init_tls int init_tls(void); Called once to pre_initialize the tls subsystem, from the main(). Called before parsing the configuration file.
init_tls int init_tls(void); Called once to initialize the tls subsystem, from the main(). Called after parsing the configuration file.
destroy_tls void destroy_tls(void); Called once, just before cleanup.
tls_init int tls_init(struct socket_info *c); Called once for each tls socket created, from main.c
os_malloc, os_realloc, os_free Wrapper functions around the shm_* functions. OpenSSL uses non-shared memory to create its objects, thus it would not work in &osips;. By creating these wrappers and configuring OpenSSL to use them instead of its default memory functions, we have all OpenSSL objects in shared memory, ready to use.
TLS_DOMAIN
tls_domains extern struct tls_domain *tls_default_server_domain; The default TLS server domain. extern struct tls_domain *tls_default_client_domain; The default TLS client domain. extern struct tls_domain *tls_server_domains; List with defined server domains. extern struct tls_domain *tls_client_domains; List with defined client domains.
tls_find_server_domain struct tls_domain *tls_find_server_domain(struct ip_addr *ip, unsigned short port); Find a TLS server domain with given ip and port (local listening socket).
tls_find_client_domain struct tls_domain *tls_find_client_domain(struct ip_addr *ip, unsigned short port); Find TLS client domain.
tls_find_client_domain_addr struct tls_domain *tls_find_client_domain_addr(struct ip_addr *ip, unsigned short port); Find TLS client domain with given ip and port (socket of the remote destination).
tls_find_client_domain_name struct tls_domain *tls_find_client_name(str name); Find TLS client domain with given name.
tls_new__domain struct tls_domain *tls_new_domain(int type); Creates new TLS: allocate memory, set the type and initialize members
tls_new_server_domain int tls_new_server_domain(struct ip_addr *ip, unsigned short port); Creates and adds to the list of TLS server domains a new domain.
tls_new_client_domain int tls_new_client_domain(struct ip_addr *ip, unsigned short port); Creates and adds to the list of TLS client domains a new socket based domain.
tls_new_client_domain_name int tls_new_client_domain_name(char *s, int len); Creates and adds to the list of TLS client domains a new name based domain.
tls_free_domains void tls_free_domains(void); Cleans up the entire domain lists.
opensips-2.2.2/modules/tls_mgm/tls.h000066400000000000000000000101451300170765700174510ustar00rootroot00000000000000/* * Copyright (C) 2015 OpenSIPS Foundation * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-12 first version (bogdan) */ #ifndef _PROTO_TLS_H_ #define _PROTO_TLS_H_ #include #include #include #include #include "tls_helper.h" #include "../../locking.h" #define OS_SSL_SESS_ID ((unsigned char*)NAME "-" VERSION) #define OS_SSL_SESS_ID_LEN (sizeof(OS_SSL_SESS_ID)-1) #if OPENSSL_VERSION_NUMBER < 0x00908000L #error "using an unsupported version of OpenSSL (< 0.9.8)" #endif #if OPENSSL_VERSION_NUMBER < 0x10001000L #warning "" #warning "==============================================================" #warning "Your version of OpenSSL is < 1.0.1." #warning " Upgrade for better compatibility, features and security fixes!" #warning "=============================================================" #warning "" #endif static int tls_static_locks_no=0; static gen_lock_set_t* tls_static_locks=NULL; static SSL_METHOD *ssl_methods[TLS_USE_TLSv1_2 + 1]; #define VERIFY_DEPTH_S 3 struct CRYPTO_dynlock_value { gen_lock_t lock; }; static unsigned long tls_get_id(void) { return my_pid(); } /* * Wrappers around OpenSIPS shared memory functions * (which can be macros) */ static void* os_malloc(size_t size) { return shm_malloc(size); } static void* os_realloc(void *ptr, size_t size) { return shm_realloc(ptr, size); } static void os_free(void *ptr) { if (ptr) shm_free(ptr); } static void tls_static_locks_ops(int mode, int n, const char* file, int line) { if (n<0 || n>tls_static_locks_no) { LM_ERR("BUG - SSL Lib attempting to acquire bogus lock\n"); abort(); } if (mode & CRYPTO_LOCK) { lock_set_get(tls_static_locks,n); } else { lock_set_release(tls_static_locks,n); } } static struct CRYPTO_dynlock_value* tls_dyn_lock_create(const char* file, int line) { struct CRYPTO_dynlock_value* new_lock; new_lock=shm_malloc(sizeof(struct CRYPTO_dynlock_value)); if (new_lock==0){ LM_ERR("Failed to allocated new dynamic lock\n"); return 0; } if (lock_init(&new_lock->lock)==0) { LM_ERR("Failed to init new dynamic lock\n"); shm_free(new_lock); return 0; } return new_lock; } static void tls_dyn_lock_ops(int mode, struct CRYPTO_dynlock_value* dyn_lock, const char* file, int line) { if (mode & CRYPTO_LOCK) { lock_get(&dyn_lock->lock); } else { lock_release(&dyn_lock->lock); } } static void tls_dyn_lock_destroy(struct CRYPTO_dynlock_value *dyn_lock, const char* file, int line) { lock_destroy(&dyn_lock->lock); shm_free(dyn_lock); } #endif /* _PROTO_TLS_H_ */ opensips-2.2.2/modules/tls_mgm/tls_config.c000066400000000000000000000066631300170765700210030ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2004,2005 Free Software Foundation, Inc. * Copyright (C) 2006 enum.at * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "tls_config.h" #include "../../config.h" #include "../../ut.h" int tls_default_method = TLS_USE_SSLv23; /* * These are the default values which will be used * for default domains AND virtual domains */ /* enable certificate validation as default value */ int tls_verify_client_cert = 1; int tls_verify_server_cert = 1; int tls_require_client_cert = 1; /* disable CRL validation for all the certificates from the chain */ int crl_check_all = 0; /* default location of certificates */ char *tls_cert_file = TLS_CERT_FILE; char *tls_pkey_file = TLS_PKEY_FILE; char *tls_ca_file = TLS_CA_FILE; char *tls_ca_dir = TLS_CA_DIRECTORY; char *tls_tmp_dh_file = TLS_DH_PARAMS_FILE; /* defaul cipher=0, this means the DEFAULT ciphers */ char *tls_ciphers_list = 0; /* TLS timeouts (in ms); should be low to detect problems fast */ int tls_handshake_timeout = 100; int tls_send_timeout = 100; /* per default, the TLS domains do not have a name */ int tls_client_domain_avp = -1; str id_col = str_init("id"); str type_col = str_init("type"); str address_col = str_init("address"); str method_col = str_init("method"); str verify_cert_col = str_init("verify_cert"); str require_cert_col = str_init("require_cert"); str certificate_col = str_init("certificate"); str pk_col = str_init("private_key"); str crl_check_col = str_init("crl_check_all"); str crl_dir_col = str_init("crl_dir"); str calist_col = str_init("ca_list"); str cadir_col = str_init("ca_dir"); str cplist_col = str_init("cipher_list"); str dhparams_col = str_init("dh_params"); str eccurve_col = str_init("ec_curve"); str tls_db_table = str_init("tls_mgm"); str tls_db_url = {NULL, 0};opensips-2.2.2/modules/tls_mgm/tls_config.h000066400000000000000000000051641300170765700210030ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2004,2005 Free Software Foundation, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef tls_config_h #define tls_config_h #include "tls_config_helper.h" #include "../../str.h" extern int tls_default_method; extern int tls_verify_client_cert; extern int tls_verify_server_cert; extern int tls_require_client_cert; extern int crl_check_all; extern char *tls_cert_file; extern char *tls_pkey_file; extern char *tls_ca_file; extern char *tls_ca_dir; extern char *tls_tmp_dh_file; extern char *tls_ciphers_list; extern str tls_db_url; extern str tls_db_table; extern str id_col; extern str type_col; extern str address_col; extern str method_col; extern str verify_cert_col; extern str require_cert_col; extern str certificate_col; extern str pk_col; extern str crl_check_col; extern str crl_dir_col; extern str calist_col; extern str cadir_col; extern str cplist_col; extern str dhparams_col; extern str eccurve_col; extern int tls_handshake_timeout; extern int tls_send_timeout; extern int tls_client_domain_avp; #endif opensips-2.2.2/modules/tls_mgm/tls_config_helper.h000066400000000000000000000006371300170765700223420ustar00rootroot00000000000000/* * File: tls_config_helper.h * Author: cristi * * Created on September 3, 2015, 6:54 PM */ #ifndef TLS_CONFIG_HELPER_H #define TLS_CONFIG_HELPER_H enum tls_method { TLS_METHOD_UNSPEC = 0, TLS_USE_TLSv1_cli, TLS_USE_TLSv1_srv, TLS_USE_TLSv1, TLS_USE_SSLv23_cli, TLS_USE_SSLv23_srv, TLS_USE_SSLv23, TLS_USE_TLSv1_2_cli, TLS_USE_TLSv1_2_srv, TLS_USE_TLSv1_2 }; #endif /* TLS_CONFIG_HELPER_H */ opensips-2.2.2/modules/tls_mgm/tls_conn.h000066400000000000000000000015131300170765700204650ustar00rootroot00000000000000/* * File: tls_conn.h * Author: razvan * * Created on November 11, 2015, 5:26 PM */ #ifndef TLS_CONN_HELPER_H #define TLS_CONN_HELPER_H #include #include #include "tls_helper.h" #include "tls_config_helper.h" #include "../../locking.h" /* * dump ssl error stack */ static void tls_print_errstack(void) { int code; while ((code = ERR_get_error())) { LM_ERR("TLS errstack: %s\n", ERR_error_string(code, 0)); } } /* * Update ssl structure with new fd */ static int tls_update_fd(struct tcp_connection *c, int fd) { /* * must be run from within a lock */ SSL *ssl; ssl = (SSL *) c->extra_data; if (!SSL_set_fd(ssl, fd)) { LM_ERR("failed to assign socket to ssl\n"); return -1; } LM_DBG("New fd is %d\n", fd); return 0; } #endif /* TLS_CONN_HELPER_H */ opensips-2.2.2/modules/tls_mgm/tls_conn_ops.h000066400000000000000000000125161300170765700213530ustar00rootroot00000000000000/* * File: tls_conn.h * Author: razvan * * Created on November 11, 2015, 5:26 PM */ #ifndef TLS_CONN_OPS_H #define TLS_CONN_OPS_H #include #include #include "tls_conn.h" #include "tls_config_helper.h" #include "../../locking.h" /* * wrapper around SSL_shutdown, returns -1 on error, 0 on success */ static int tls_conn_shutdown(struct tcp_connection *c) { int ret, err; SSL *ssl; /* If EOF or other error on connection, no point in attempting to * do further writing & reading on the con */ if (c->state == S_CONN_BAD || c->state == S_CONN_ERROR || c->state == S_CONN_EOF) return 0; /* * we do not implement full ssl shutdown */ ssl = (SSL *) c->extra_data; if (ssl == 0) { LM_ERR("no ssl data\n"); return -1; } ret = SSL_shutdown(ssl); if (ret == 1) { LM_DBG("shutdown successful\n"); return 0; } else if (ret == 0) { LM_DBG("first phase of 2-way handshake completed succesfuly\n"); return 0; } else { err = SSL_get_error(ssl, ret); switch (err) { case SSL_ERROR_ZERO_RETURN: c->state = S_CONN_EOF; return 0; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: c->state = S_CONN_EOF; return 0; default: LM_ERR("something wrong in SSL: %d, %d, %s\n",err,errno,strerror(errno)); case SSL_ERROR_SYSCALL: c->state = S_CONN_BAD; tls_print_errstack(); return -1; } } LM_ERR("bug\n"); return -1; } static int tls_conn_init(struct tcp_connection* c, struct tls_mgm_binds *api) { struct tls_domain *dom; /* * new connection within a single process, no lock necessary */ LM_DBG("entered: Creating a whole new ssl connection\n"); if ( c->flags&F_CONN_ACCEPTED ) { /* connection created as a result of an accept -> server */ c->proto_flags = F_TLS_DO_ACCEPT; LM_DBG("looking up socket based TLS server " "domain [%s:%d]\n", ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port); dom = api->find_server_domain(&c->rcv.dst_ip, c->rcv.dst_port); if (dom) { LM_DBG("found socket based TLS server domain " "[%s:%d]\n", ip_addr2a(&dom->addr), dom->port); c->extra_data = SSL_new(dom->ctx); api->release_domain(dom); } else { LM_ERR("no TLS server domain found\n"); return -1; } } else { /* connection created as a result of a connect -> client */ c->proto_flags = F_TLS_DO_CONNECT; dom = api->find_client_domain(&c->rcv.src_ip, c->rcv.src_port); if (dom) { c->extra_data = SSL_new(dom->ctx); api->release_domain(dom); } else { LM_ERR("no TLS client domain found\n"); return -1; } } if (!c->extra_data) { LM_ERR("failed to create SSL structure\n"); return -1; } #ifndef OPENSSL_NO_KRB5 if ( ((SSL *)c->extra_data)->kssl_ctx ) { kssl_ctx_free( ((SSL *)c->extra_data)->kssl_ctx ); ((SSL *)c->extra_data)->kssl_ctx = 0; } #endif if ( c->proto_flags & F_TLS_DO_ACCEPT ) { LM_DBG("Setting in ACCEPT mode (server)\n"); SSL_set_accept_state((SSL *) c->extra_data); } else { LM_DBG("Setting in CONNECT mode (client)\n"); SSL_set_connect_state((SSL *) c->extra_data); } return 0; } static void tls_conn_clean(struct tcp_connection* c) { /* * runs within global tcp lock */ LM_DBG("entered\n"); if (c->extra_data) { tls_update_fd(c,c->s); tls_conn_shutdown(c); SSL_free((SSL *) c->extra_data); c->extra_data = 0; } } /* * Wrapper around SSL_read * * returns number of bytes read, 0 on eof and transits into S_CONN_EOF, -1 * on error */ static int _tls_read(struct tcp_connection *c, void *buf, size_t len) { int ret, err; SSL *ssl; ssl = c->extra_data; ret = SSL_read(ssl, buf, len); if (ret > 0) { LM_DBG("%d bytes read\n", ret); return ret; } else if (ret == 0) { /* unclean shutdown of the other peer */ c->state = S_CONN_EOF; return 0; } else { err = SSL_get_error(ssl, ret); switch (err) { case SSL_ERROR_ZERO_RETURN: LM_DBG("TLS connection to %s:%d closed cleanly\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); /* * mark end of file */ c->state = S_CONN_EOF; return 0; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: return 0; case SSL_ERROR_SYSCALL: LM_ERR("SYSCALL error -> (%d) <%s>\n",errno,strerror(errno)); default: LM_ERR("TLS connection to %s:%d read failed\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); LM_ERR("TLS read error: %d\n",err); c->state = S_CONN_BAD; tls_print_errstack(); return -1; } } LM_BUG("bug\n"); return -1; } /* * called only when a connection is in S_CONN_OK, we do not have to care * about accepting or connecting here, each modification of ssl data * structures has to be protected, another process might ask for the same * connection and attempt write to it which would result in updating the * ssl structures */ static int tls_read(struct tcp_connection * c,struct tcp_req *r) { int bytes_free; int fd, read; fd = c->fd; bytes_free = TCP_BUF_SIZE - (int) (r->pos - r->buf); if (bytes_free == 0) { LM_ERR("TLS buffer overrun, dropping\n"); r->error = TCP_REQ_OVERRUN; return -1; } /* * ssl structures may be accessed from several processes, we need to * protect each access and modification by a lock */ lock_get(&c->write_lock); tls_update_fd(c, fd); read = _tls_read(c, r->pos, bytes_free); lock_release(&c->write_lock); if (read > 0) r->pos += read; return read; } #endif /* TLS_CONN_OPS_H */ opensips-2.2.2/modules/tls_mgm/tls_conn_server.h000066400000000000000000000315531300170765700220620ustar00rootroot00000000000000/* * File: tls_conn.h * Author: razvan * * Created on November 11, 2015, 5:26 PM */ #ifndef TLS_CONN_SERVER_H #define TLS_CONN_SERVER_H #include #include #include #include "api.h" #include "tls_conn.h" #include "tls_config_helper.h" #include "../../locking.h" static void tls_dump_cert_info(char* s, X509* cert) { char* subj; char* issuer; subj = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); LM_INFO("%s subject: %s, issuer: %s\n", s ? s : "", subj, issuer); OPENSSL_free(subj); OPENSSL_free(issuer); } static void tls_dump_verification_failure(long verification_result) { switch(verification_result) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: LM_ERR("unable to get issuer certificate\n"); break; case X509_V_ERR_UNABLE_TO_GET_CRL: LM_ERR("unable to get certificate CRL\n"); break; case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: LM_ERR("unable to decrypt certificate's signature\n"); break; case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: LM_ERR("unable to decrypt CRL's signature\n"); break; case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: LM_ERR("unable to decode issuer public key\n"); break; case X509_V_ERR_CERT_SIGNATURE_FAILURE: LM_ERR("certificate signature failure\n"); break; case X509_V_ERR_CRL_SIGNATURE_FAILURE: LM_ERR("CRL signature failure\n"); break; case X509_V_ERR_CERT_NOT_YET_VALID: LM_ERR("certificate is not yet valid\n"); break; case X509_V_ERR_CERT_HAS_EXPIRED: LM_ERR("certificate has expired\n"); break; case X509_V_ERR_CRL_NOT_YET_VALID: LM_ERR("CRL is not yet valid\n"); break; case X509_V_ERR_CRL_HAS_EXPIRED: LM_ERR("CRL has expired\n"); break; case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: LM_ERR("format error in certificate's notBefore field\n"); break; case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: LM_ERR("format error in certificate's notAfter field\n"); break; case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: LM_ERR("format error in CRL's lastUpdate field\n"); break; case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: LM_ERR("format error in CRL's nextUpdate field\n"); break; case X509_V_ERR_OUT_OF_MEM: LM_ERR("out of memory\n"); break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: LM_ERR("self signed certificate\n"); break; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: LM_ERR("self signed certificate in certificate chain\n"); break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: LM_ERR("unable to get local issuer certificate\n"); break; case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: LM_ERR("unable to verify the first certificate\n"); break; case X509_V_ERR_CERT_CHAIN_TOO_LONG: LM_ERR("certificate chain too long\n"); break; case X509_V_ERR_CERT_REVOKED: LM_ERR("certificate revoked\n"); break; case X509_V_ERR_INVALID_CA: LM_ERR("invalid CA certificate\n"); break; case X509_V_ERR_PATH_LENGTH_EXCEEDED: LM_ERR("path length constraint exceeded\n"); break; case X509_V_ERR_INVALID_PURPOSE: LM_ERR("unsupported certificate purpose\n"); break; case X509_V_ERR_CERT_UNTRUSTED: LM_ERR("certificate not trusted\n"); break; case X509_V_ERR_CERT_REJECTED: LM_ERR("certificate rejected\n"); break; case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: LM_ERR("subject issuer mismatch\n"); break; case X509_V_ERR_AKID_SKID_MISMATCH: LM_ERR("authority and subject key identifier mismatch\n"); break; case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: LM_ERR("authority and issuer serial number mismatch\n"); break; case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: LM_ERR("key usage does not include certificate signing\n"); break; case X509_V_ERR_APPLICATION_VERIFICATION: LM_ERR("application verification failure\n"); break; } } /* * Wrapper around SSL_accept, returns -1 on error, 0 on success */ static int tls_accept(struct tcp_connection *c, short *poll_events) { int ret, err; SSL *ssl; X509* cert; if ( (c->proto_flags&F_TLS_DO_ACCEPT)==0 ) { LM_BUG("invalid connection state (bug in TLS code)\n"); return -1; } ssl = (SSL *) c->extra_data; #ifndef OPENSSL_NO_KRB5 if ( ssl->kssl_ctx==NULL ) ssl->kssl_ctx = kssl_ctx_new( ); #endif ret = SSL_accept(ssl); #ifndef OPENSSL_NO_KRB5 if ( ssl->kssl_ctx ) { kssl_ctx_free( ssl->kssl_ctx ); ssl->kssl_ctx = 0; } #endif if (ret > 0) { LM_INFO("New TLS connection from %s:%d accepted\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); /* TLS accept done, reset the flag */ c->proto_flags &= ~F_TLS_DO_ACCEPT; LM_DBG("new TLS connection from %s:%d using %s %s %d\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port, SSL_get_cipher_version(ssl), SSL_get_cipher_name(ssl), SSL_get_cipher_bits(ssl, 0) ); LM_DBG("local socket: %s:%d\n", ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port ); cert = SSL_get_peer_certificate(ssl); if (cert != 0) { tls_dump_cert_info("tls_accept: client TLS certificate", cert); if (SSL_get_verify_result(ssl) != X509_V_OK) { LM_WARN("TLS client certificate verification failed\n"); tls_dump_verification_failure(SSL_get_verify_result(ssl)); } X509_free(cert); } else { LM_INFO("Client did not present a TLS certificate\n"); } cert = SSL_get_certificate(ssl); if (cert != 0) { tls_dump_cert_info("tls_accept: local TLS server certificate", cert); } else { /* this should not happen, servers always present a cert */ LM_ERR("local TLS server domain has no certificate\n"); } return 0; } else { err = SSL_get_error(ssl, ret); switch (err) { case SSL_ERROR_ZERO_RETURN: LM_INFO("TLS connection from %s:%d accept failed cleanly\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); c->state = S_CONN_BAD; return -1; case SSL_ERROR_WANT_READ: if (poll_events) *poll_events = POLLIN; return 0; case SSL_ERROR_WANT_WRITE: if (poll_events) *poll_events = POLLOUT; return 0; default: c->state = S_CONN_BAD; LM_ERR("New TLS connection from %s:%d failed to accept\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); if (errno != 0) LM_ERR("TLS error: (ret=%d, err=%d, errno=%d/%s):\n", ret, err, errno, strerror(errno)); tls_print_errstack(); return -1; } } LM_BUG("bug\n"); return -1; } /* * wrapper around SSL_connect, returns 0 on success, -1 on error */ static int tls_connect(struct tcp_connection *c, short *poll_events) { int ret, err; SSL *ssl; X509* cert; if ( (c->proto_flags&F_TLS_DO_CONNECT)==0 ) { LM_BUG("invalid connection state (bug in TLS code)\n"); return -1; } ssl = (SSL *) c->extra_data; ret = SSL_connect(ssl); if (ret > 0) { LM_INFO("New TLS connection to %s:%d established\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); c->proto_flags &= ~F_TLS_DO_CONNECT; LM_DBG("new TLS connection to %s:%d using %s %s %d\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port, SSL_get_cipher_version(ssl), SSL_get_cipher_name(ssl), SSL_get_cipher_bits(ssl, 0) ); LM_DBG("sending socket: %s:%d \n", ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port ); cert = SSL_get_peer_certificate(ssl); if (cert != 0) { tls_dump_cert_info("tls_connect: server TLS certificate", cert); if (SSL_get_verify_result(ssl) != X509_V_OK) { LM_WARN("TLS server certificate verification failed\n"); tls_dump_verification_failure(SSL_get_verify_result(ssl)); } X509_free(cert); } else { /* this should not happen, servers always present a cert */ LM_ERR("server did not present a TLS certificate\n"); } cert = SSL_get_certificate(ssl); if (cert != 0) { tls_dump_cert_info("tls_connect: local TLS client certificate", cert); } else { LM_INFO("local TLS client domain does not have a certificate\n"); } return 0; } else { err = SSL_get_error(ssl, ret); switch (err) { case SSL_ERROR_ZERO_RETURN: LM_INFO("New TLS connection to %s:%d failed cleanly\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); c->state = S_CONN_BAD; return -1; case SSL_ERROR_WANT_READ: if (poll_events) *poll_events = POLLIN; return 0; case SSL_ERROR_WANT_WRITE: if (poll_events) *poll_events = POLLOUT; return 0; case SSL_ERROR_SYSCALL: LM_ERR("SSL_ERROR_SYSCALL err=%s(%d)\n", strerror(errno), errno); default: LM_ERR("New TLS connection to %s:%d failed\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); LM_ERR("TLS error: %d (ret=%d) err=%s(%d)\n", err,ret,strerror(errno), errno); c->state = S_CONN_BAD; tls_print_errstack(); return -1; } } LM_BUG("bug\n"); return -1; } /* * called before tls_read, the this function should attempt tls_accept or * tls_connect depending on the state of the connection, if this function * does not transit a connection into S_CONN_OK then tcp layer would not * call tcp_read */ static int tls_fix_read_conn(struct tcp_connection *c) { /* * no lock acquired */ int ret; ret = 0; /* * We have to acquire the lock before testing c->state, otherwise a * writer could modify the structure if it gets preempted and has * something to write */ lock_get(&c->write_lock); if ( c->proto_flags & F_TLS_DO_ACCEPT ) { ret = tls_update_fd(c, c->fd); if (!ret) ret = tls_accept(c, NULL); } else if ( c->proto_flags & F_TLS_DO_CONNECT ) { ret = tls_update_fd(c, c->fd); if (!ret) ret = tls_connect(c, NULL); } lock_release(&c->write_lock); return ret; } /* * Wrapper around SSL_write, returns number of bytes written on success, * * -1 on error, 0 when it would block */ static int tls_write(struct tcp_connection *c, int fd, const void *buf, size_t len, short *poll_events) { int ret, err; /* * runs within write lock, no need to lock here */ SSL *ssl; ssl = (SSL *) c->extra_data; ret = SSL_write(ssl, buf, len); if (ret > 0) { LM_DBG("write was successful (%d bytes)\n", ret); return ret; } else { err = SSL_get_error(ssl, ret); switch (err) { case SSL_ERROR_ZERO_RETURN: LM_DBG("connection closed cleanly\n"); c->state = S_CONN_EOF; return -1; case SSL_ERROR_WANT_READ: if (poll_events) *poll_events = POLLIN; return 0; case SSL_ERROR_WANT_WRITE: if (poll_events) *poll_events = POLLOUT; return 0; default: LM_ERR("TLS connection to %s:%d write failed\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); LM_ERR("TLS write error:\n"); c->state = S_CONN_BAD; tls_print_errstack(); return -1; } } LM_BUG("bug\n"); return -1; } /* * This is shamelessly stolen tsend_stream from tsend.c */ /* * fixme: probably does not work correctly */ static int tls_blocking_write(struct tcp_connection *c, int fd, const char *buf, size_t len, struct tls_mgm_binds *api) { #define MAX_SSL_RETRIES 32 int written, n; int timeout, retries; struct pollfd pf; pf.fd = fd; written = 0; retries = 0; if (c->state!=S_CONN_OK) { LM_ERR("TLS broken connection\n"); goto error; } lock_get(&c->write_lock); if (tls_update_fd(c, fd) < 0) goto error; timeout = api->get_send_timeout(); again: n = 0; pf.events = 0; if ( c->proto_flags & F_TLS_DO_ACCEPT ) { if (tls_accept(c, &(pf.events)) < 0) goto error; timeout = api->get_handshake_timeout(); } else if ( c->proto_flags & F_TLS_DO_CONNECT ) { if (tls_connect(c, &(pf.events)) < 0) goto error; timeout = api->get_handshake_timeout(); } else { n = tls_write(c, fd, buf, len, &(pf.events)); timeout = api->get_send_timeout(); } if (n < 0) { LM_ERR("TLS failed to send data\n"); goto error; } /* nothing happens */ if (n==0) { retries++; /* avoid looping */ if (retries==MAX_SSL_RETRIES) { LM_ERR("too many retries with no operation\n"); goto error; } } else { /* reset the retries if we succeeded in doing something*/ retries = 0; } written += n; if (n < len) { /* * partial write */ buf += n; len -= n; } else { /* * successful full write */ lock_release(&c->write_lock); return written; } if (pf.events == 0) pf.events = POLLOUT; poll_loop: while (1) { n = poll(&pf, 1, timeout); if (n < 0) { if (errno == EINTR) continue; /* signal, ignore */ else if (errno != EAGAIN && errno != EWOULDBLOCK) { LM_ERR("TLS poll failed: %s [%d]\n",strerror(errno), errno); goto error; } else goto poll_loop; } else if (n == 0) { /* * timeout */ LM_ERR("TLS send timeout (%d)\n", timeout); goto error; } if (pf.revents & POLLOUT || pf.revents & POLLIN) { /* * we can read or write again */ goto again; } else if (pf.revents & (POLLERR | POLLHUP | POLLNVAL)) { LM_ERR("TLS bad poll flags %x\n",pf.revents); goto error; } /* * if POLLPRI or other non-harmful events happened, continue ( * although poll should never signal them since we're not * interested in them => we should never reach this point) */ } error: lock_release(&c->write_lock); return -1; } #endif /* TLS_CONN_SERVER_H */ opensips-2.2.2/modules/tls_mgm/tls_domain.c000066400000000000000000000303621300170765700207760ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2004,2005 Free Software Foundation, Inc. * Copyright (C) 2006 enum.at * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../mem/mem.h" #include "tls_domain.h" #include "tls_params.h" #include struct tls_domain *tls_server_domains; struct tls_domain *tls_client_domains; struct tls_domain tls_default_server_domain; struct tls_domain tls_default_client_domain; rw_lock_t *dom_lock; struct tls_domain *tls_find_domain_by_id( str *id) { struct tls_domain *d; if (tls_db_enabled) lock_start_read(dom_lock); for (d=tls_server_domains ; d ; d=d->next ) { if (id->len==d->id.len && memcmp(id->s,d->id.s,id->len)==0) { if (tls_db_enabled) lock_stop_read(dom_lock); return d; } } for (d=tls_client_domains ; d ; d=d->next ) { if (id->len==d->id.len && memcmp(id->s,d->id.s,id->len)==0) { if (tls_db_enabled) lock_stop_read(dom_lock); return d; } } if (tls_db_enabled) lock_stop_read(dom_lock); return NULL; } void tls_release_domain_aux(struct tls_domain *dom) { dom->refs--; if (dom->refs == 0) { if (dom->name.s) shm_free(dom->name.s); SSL_CTX_free(dom->ctx); lock_destroy(dom->lock); lock_dealloc(dom->lock); shm_free(dom); } } void tls_release_all_domains(struct tls_domain *dom) { while (dom) { tls_release_domain_aux(dom); dom = dom->next; } } void tls_release_domain(struct tls_domain* dom) { if (!dom || !tls_db_enabled || dom == &tls_default_server_domain || dom == &tls_default_client_domain) return; lock_start_write(dom_lock); tls_release_domain_aux(dom); lock_stop_write(dom_lock); } int set_all_domain_attr(struct tls_domain **dom, char **str_vals, int *int_vals) { size_t len; char *p; struct tls_domain *d = *dom; size_t cadir_len = strlen(str_vals[STR_VALS_CADIR_COL]); size_t calist_len = strlen(str_vals[STR_VALS_CALIST_COL]); size_t certificate_len = strlen(str_vals[STR_VALS_CERTIFICATE_COL]); size_t cplist_len = strlen(str_vals[STR_VALS_CPLIST_COL]); size_t crl_dir_len = strlen(str_vals[STR_VALS_CRL_DIR_COL]); size_t dhparams_len = strlen(str_vals[STR_VALS_DHPARAMS_COL]); size_t eccurve_len = strlen(str_vals[STR_VALS_ECCURVE_COL]); size_t pk_len = strlen(str_vals[STR_VALS_PK_COL]); len = sizeof(struct tls_domain) +d->id.len; if (cadir_len) len += cadir_len + 1; if (calist_len) len += calist_len + 1; if (certificate_len) len += certificate_len + 1; if (cplist_len) len += cplist_len + 1; if (crl_dir_len) len += crl_dir_len + 1; if (dhparams_len) len += dhparams_len + 1; if (eccurve_len) len += eccurve_len + 1; if (pk_len) len += pk_len + 1; d = shm_realloc(d, len); if (d == NULL) { LM_ERR("insufficient shm memory"); d = *dom; *dom = (*dom)->next; shm_free(d); return -1; } *dom = d; if (strcasecmp(str_vals[STR_VALS_METHOD_COL], "SSLV23") == 0 || strcasecmp(str_vals[STR_VALS_METHOD_COL], "TLSany") == 0) d->method = TLS_USE_SSLv23; else if (strcasecmp(str_vals[STR_VALS_METHOD_COL], "TLSV1") == 0) d->method = TLS_USE_TLSv1; else if (strcasecmp(str_vals[STR_VALS_METHOD_COL], "TLSV1_2") == 0) d->method = TLS_USE_TLSv1_2; if (int_vals[INT_VALS_VERIFY_CERT_COL] != -1) { d->verify_cert = int_vals[INT_VALS_VERIFY_CERT_COL]; } if (int_vals[INT_VALS_CRL_CHECK_COL] != -1) { d->crl_check_all = int_vals[INT_VALS_CRL_CHECK_COL]; } if (int_vals[INT_VALS_REQUIRE_CERT_COL] != -1) { d->require_client_cert = int_vals[INT_VALS_REQUIRE_CERT_COL]; } p = (char *) (d + 1); p = p + d->id.len; memset(p, 0, len - (sizeof(struct tls_domain) +d->id.len)); if (cadir_len) { d->ca_directory = p; memcpy(p, str_vals[STR_VALS_CADIR_COL], cadir_len); p = p + cadir_len + 1; } if (calist_len) { d->ca_file = p; memcpy(p, str_vals[STR_VALS_CALIST_COL], calist_len); p = p + calist_len + 1; } if (certificate_len) { d->cert_file = p; memcpy(p, str_vals[STR_VALS_CERTIFICATE_COL], certificate_len); p = p + certificate_len + 1; } if (cplist_len) { d->ciphers_list = p; memcpy(p, str_vals[STR_VALS_CPLIST_COL], cplist_len); p = p + cplist_len + 1; } if (crl_dir_len) { d->crl_directory = p; memcpy(p, str_vals[STR_VALS_CRL_DIR_COL], crl_dir_len); p = p + crl_dir_len + 1; } if (dhparams_len) { d->tmp_dh_file = p; memcpy(p, str_vals[STR_VALS_DHPARAMS_COL], dhparams_len); p = p + dhparams_len + 1; } if (eccurve_len) { d->tls_ec_curve = p; memcpy(p, str_vals[STR_VALS_ECCURVE_COL], eccurve_len); p = p + eccurve_len + 1; } if (pk_len) { d->pkey_file = p; memcpy(p, str_vals[STR_VALS_PK_COL], pk_len); p = p + pk_len + 1; } return 0; } /* * find server domain with given ip and port * return default domain if virtual domain not found */ struct tls_domain * tls_find_server_domain(struct ip_addr *ip, unsigned short port) { if (tls_db_enabled) lock_start_read(dom_lock); struct tls_domain *p = tls_server_domains; while (p) { if ((p->port == port) && ip_addr_cmp(&p->addr, ip)) { LM_DBG("virtual TLS server domain found\n"); if (tls_db_enabled) { lock_get(p->lock); p->refs++; lock_release(p->lock); lock_stop_read(dom_lock); } return p; } p = p->next; } if (tls_db_enabled) lock_stop_read(dom_lock); LM_DBG("virtual TLS server domain not found, " "Using default TLS server domain settings\n"); return &tls_default_server_domain; } /* * find client domain with given ip and port, * return default domain if virtual domain not found */ struct tls_domain * tls_find_client_domain_addr(struct ip_addr *ip, unsigned short port) { struct tls_domain *p = tls_client_domains; while (p) { if ((p->name.len == 0) && (p->port == port) && ip_addr_cmp(&p->addr, ip)) { LM_DBG("virtual TLS client domain found\n"); return p; } p = p->next; } LM_DBG("virtual TLS client domain not found, " "Using default TLS client domain settings\n"); return &tls_default_client_domain; } /* * find client domain with given name, * return 0 if name based virtual domain not found */ struct tls_domain * tls_find_client_domain_name(str name) { struct tls_domain *p = tls_client_domains; while (p) { if ((p->name.len == name.len) && !strncasecmp(p->name.s, name.s, name.len)) { LM_DBG("virtual TLS client domain found\n"); return p; } p = p->next; } LM_DBG("virtual TLS client domain not found\n"); return 0; } /* * find client domain * return 0 if virtual domain not found */ struct tls_domain *tls_find_client_domain(struct ip_addr *ip, unsigned short port){ struct tls_domain *dom; struct usr_avp *avp; int_str val; avp = NULL; if (tls_client_domain_avp > 0) { avp = search_first_avp(0, tls_client_domain_avp, &val, 0); } else { LM_DBG("name based TLS client domains are disabled\n"); } if (tls_db_enabled) lock_start_read(dom_lock); if (!avp) { LM_DBG("no TLS client doman AVP set, looking " "for socket based TLS client domain\n"); dom = tls_find_client_domain_addr(ip, port); if (dom) { LM_DBG("found socket based TLS client domain " "[%s:%d]\n", ip_addr2a(&dom->addr), dom->port); } } else { LM_DBG("TLS client domain AVP found = '%.*s'\n", val.s.len, ZSW(val.s.s)); dom = tls_find_client_domain_name(val.s); if (dom) { LM_DBG("found name based TLS client domain " "'%.*s'\n", val.s.len, ZSW(val.s.s)); } else { LM_DBG("no name based TLS client domain found, " "trying socket based TLS client domains\n"); dom = tls_find_client_domain_addr(ip, port); if (dom) { LM_DBG("found socket based TLS client domain [%s:%d]\n", ip_addr2a(&dom->addr), dom->port); } } } if (tls_db_enabled) { if (dom && dom != &tls_default_client_domain) { lock_get(dom->lock); dom->refs++; lock_release(dom->lock); } lock_stop_read(dom_lock); } return dom; } /* * create a new server domain (identified by a socket) */ int tls_new_server_domain( str *id, struct ip_addr *ip, unsigned short port, struct tls_domain **dom) { struct tls_domain *d; d = tls_new_domain( id, TLS_DOMAIN_SRV); if (d == NULL) { LM_ERR("shm memory allocation failure\n"); return -1; } /* fill socket data */ memcpy(&d->addr, ip, sizeof(struct ip_addr)); d->port = port; d->refs = 1; /* add this new domain to the linked list */ d->next = *dom; *dom = d; return 0; } /* * create a new client domain (identified by a socket) */ int tls_new_client_domain(str *id, struct ip_addr *ip, unsigned short port, struct tls_domain **dom) { struct tls_domain *d; d = tls_new_domain( id, TLS_DOMAIN_CLI); if (d == NULL) { LM_ERR("pkg memory allocation failure\n"); return -1; } /* fill socket data */ memcpy(&d->addr, ip, sizeof(struct ip_addr)); d->port = port; d->refs = 1; /* add this new domain to the linked list */ d->next = *dom; *dom = d; return 0; } /* * create a new client domain (identified by a string) */ int tls_new_client_domain_name( str *id, str *domain, struct tls_domain **dom) { struct tls_domain *d; d = tls_new_domain( id, TLS_DOMAIN_CLI | TLS_DOMAIN_NAME); if (d == NULL) { LM_ERR("pkg memory allocation failure\n"); return -1; } /* initialize name data */ d->name.s = shm_malloc(domain->len); if (d->name.s == NULL) { LM_ERR("pkg memory allocation failure\n"); shm_free(d); return -1; } memcpy(d->name.s, domain->s, domain->len); d->name.len = domain->len; d->refs = 1; /* add this new domain to the linked list */ d->next = *dom; *dom = d; return 0; } /* * allocate memory and set default values for * TLS domain structure */ struct tls_domain *tls_new_domain( str *id, int type) { struct tls_domain *d; LM_DBG("adding new domain [%.*s] type %d\n", id->len, id->s, type); d = shm_malloc(sizeof(struct tls_domain) + id->len); if (d == NULL) { LM_ERR("pkg memory allocation failure\n"); return 0; } memset( d, 0, sizeof(struct tls_domain)); d->lock = lock_alloc(); if (!d->lock){ LM_ERR("failed to allocate lock \n"); shm_free(d); return 0; } if (lock_init(d->lock) == NULL) { LM_ERR("Failed to init lock \n"); shm_free(d); return 0; } d->id.s = (char*)(d+1); d->id.len = id->len; memcpy( d->id.s, id->s, id->len); d->type = type; d->crl_check_all = crl_check_all; if (type & TLS_DOMAIN_SRV) { d->verify_cert = tls_verify_client_cert; d->require_client_cert = tls_require_client_cert; } else { d->verify_cert = tls_verify_server_cert; d->require_client_cert = 0; } d->method = TLS_METHOD_UNSPEC; return d; } /* * clean up */ void tls_free_domains(void) { struct tls_domain *p; while (tls_server_domains) { p = tls_server_domains; tls_server_domains = tls_server_domains->next; shm_free(p); } while (tls_client_domains) { p = tls_client_domains; tls_client_domains = tls_client_domains->next; /* ToDo: If socket based client domains will be implemented, the name may be empty (must be set to NULL manually). Thus no need to free it */ if (p->name.s) { shm_free(p->name.s); } shm_free(p); } } opensips-2.2.2/modules/tls_mgm/tls_domain.h000066400000000000000000000075271300170765700210120ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2004,2005 Free Software Foundation, Inc. * Copyright (C) 2005,2006 iptelorg GmbH * Copyright (C) 2006 enum.at * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TLS_DOMAIN_H #define TLS_DOMAIN_H #include "../../str.h" #include "../../ip_addr.h" #include #include "tls_config.h" #include "tls_helper.h" #include "../../usr_avp.h" #include "../../ut.h" #include "../../rw_locking.h" /* * TLS configuration domain type */ enum tls_domain_type { TLS_DOMAIN_DEF = (1 << 0), /* Default domain */ TLS_DOMAIN_SRV = (1 << 1), /* Server domain */ TLS_DOMAIN_CLI = (1 << 2), /* Client domain */ TLS_DOMAIN_NAME= (1 << 3) /* Name based TLS domain */ }; /* * separate configuration per ip:port */ extern struct tls_domain *tls_server_domains; extern struct tls_domain *tls_client_domains; extern struct tls_domain tls_default_server_domain; extern struct tls_domain tls_default_client_domain; extern int tls_db_enabled; extern rw_lock_t *dom_lock; /* * find domain with given ID */ struct tls_domain *tls_find_domain_by_id( str *id); /* * find domain with given ip and port */ struct tls_domain *tls_find_server_domain(struct ip_addr *ip, unsigned short port); /* find client domain */ struct tls_domain *tls_find_client_domain(struct ip_addr *ip, unsigned short port); /* * find client with given ip and port */ struct tls_domain *tls_find_client_domain_addr(struct ip_addr *ip, unsigned short port); /* * find domain with given name */ struct tls_domain *tls_find_client_domain_name(str name); /* * create a new server domain (identified by socket) */ int tls_new_server_domain(str *id, struct ip_addr *ip, unsigned short port, struct tls_domain **dom); /* * create a new client domain (identified by socket) */ int tls_new_client_domain(str *id, struct ip_addr *ip, unsigned short port, struct tls_domain **dom); /* * create a new client domain (identified by string) */ int tls_new_client_domain_name(str *id, str *domain, struct tls_domain **dom); /* * allocate memory and set default values for * TLS domain structure */ struct tls_domain *tls_new_domain(str *id, int type); /* * clean up */ void tls_free_domains(void); void tls_release_domain(struct tls_domain* dom); void tls_release_all_domains(struct tls_domain* dom); int set_all_domain_attr(struct tls_domain **dom, char **str_vals, int *int_vals); #endif opensips-2.2.2/modules/tls_mgm/tls_helper.h000066400000000000000000000015771300170765700210210ustar00rootroot00000000000000/* * File: tls_helper.h * Author: cristi * * Created on September 3, 2015, 5:26 PM */ #ifndef TLS_HELPER_H #define TLS_HELPER_H #define F_TLS_DO_ACCEPT (1<<0) #define F_TLS_DO_CONNECT (1<<1) #include #include "tls_config_helper.h" #include "../../locking.h" struct tls_domain { str id; int type; struct ip_addr addr; unsigned short port; SSL_CTX *ctx; int verify_cert; int require_client_cert; int crl_check_all; char *cert_file; char *pkey_file; char *crl_directory; char *ca_file; char *tmp_dh_file; char *tls_ec_curve; char *ca_directory; char *ciphers_list; int refs; gen_lock_t *lock; enum tls_method method; struct tls_domain *next; str name; }; #endif /* TLS_HELPER_H */ opensips-2.2.2/modules/tls_mgm/tls_mgm.c000066400000000000000000001341101300170765700203030ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../sr_module.h" #include "../../net/api_proto.h" #include "../../net/api_proto_net.h" #include "../../net/net_tcp.h" #include "../../socket_info.h" #include "../../tsend.h" #include "../../timer.h" #include "../../receive.h" #include "../../pt.h" #include "../../parser/msg_parser.h" #include "../../pvar.h" #include "../../db/db.h" #include "../../net/proto_tcp/tcp_common_defs.h" #include "tls_config.h" #include "tls_domain.h" #include "tls_params.h" #include "tls_select.h" #include "tls.h" #include "api.h" #define DB_CAP DB_CAP_QUERY | DB_CAP_UPDATE #define len(s) s == NULL?0:strlen(s) #define check_val( _col, _val, _type, _not_null, _is_empty_str) \ do{\ if ((_val)->type!=_type) { \ LM_ERR("column %.*s has a bad type\n", _col.len, _col.s); \ goto error;\ } \ if (_not_null && (_val)->nul) { \ LM_ERR("column %.*s is null\n", _col.len, _col.s); \ goto error;\ } \ if (_is_empty_str && VAL_STRING(_val)==0) { \ LM_ERR("column %.*s (str) is empty\n", _col.len, _col.s); \ goto error;\ } \ if ((_val)->type == DB_INT && (_val)->nul) { \ (_val)->val.int_val = -1;\ } \ }while(0) static char *tls_domain_avp = NULL; static int mod_init(void); static void mod_destroy(void); static int tls_get_handshake_timeout(void); static int tls_get_send_timeout(void); static int load_tls_mgm(struct tls_mgm_binds *binds); static struct mi_root* tls_reload(struct mi_root *cmd, void *param); static struct mi_root * tls_list(struct mi_root *root, void *param); static int list_domain(struct mi_node *root, struct tls_domain *d); /* DB handler */ static db_con_t *db_hdl = 0; /* DB functions */ static db_func_t dr_dbf; int tls_db_enabled = 0; /* definition of exported functions */ static int is_peer_verified(struct sip_msg*, char*, char*); static param_export_t params[] = { { "client_domain_avp", STR_PARAM, &tls_domain_avp }, { "server_domain", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_add_srv_domain }, { "client_domain", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_add_cli_domain }, { "tls_method", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_method }, { "verify_cert", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_verify }, { "require_cert", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_require }, { "certificate", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_certificate}, { "private_key", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_pk }, { "crl_check_all", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_crl_check }, { "crl_dir", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_crldir }, { "ca_list", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_calist }, { "ca_dir", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_cadir }, { "ciphers_list", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_cplist }, { "dh_params", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_dhparams }, { "ec_curve", STR_PARAM|USE_FUNC_PARAM, (void*)tlsp_set_eccurve }, { "db_mode", INT_PARAM, &tls_db_enabled }, { "db_url", STR_PARAM, &tls_db_url.s }, { "db_table", STR_PARAM, &tls_db_table.s }, { "id_col", STR_PARAM, &id_col.s }, { "address_col", STR_PARAM, &address_col.s }, { "tls_method_col", STR_PARAM, &method_col.s }, { "verify_cert_col", STR_PARAM, &verify_cert_col.s }, { "require_cert_col", STR_PARAM, &require_cert_col.s }, { "certificate_col", STR_PARAM, &certificate_col.s }, { "private_key_col", STR_PARAM, &pk_col.s }, { "crl_check_all_col", STR_PARAM, &crl_check_col.s }, { "crl_dir_col", STR_PARAM, &crl_dir_col.s }, { "ca_list_col", STR_PARAM, &calist_col.s }, { "ca_dir_col", STR_PARAM, &cadir_col.s }, { "ciphers_list_col", STR_PARAM, &cplist_col.s }, { "dh_params_col", STR_PARAM, &dhparams_col.s }, { "ec_curve_col", STR_PARAM, &eccurve_col.s }, { "tls_handshake_timeout", INT_PARAM, &tls_handshake_timeout }, { "tls_send_timeout", INT_PARAM, &tls_send_timeout }, {0, 0, 0} }; static cmd_export_t cmds[] = { {"is_peer_verified", (cmd_function)is_peer_verified, 0, 0, 0, REQUEST_ROUTE}, {"load_tls_mgm", (cmd_function)load_tls_mgm, 0, 0, 0, 0}, {0,0,0,0,0,0} }; /* * Exported MI functions */ static mi_export_t mi_cmds[] = { { "tls_reload", "reloads stored data from the database", tls_reload, 0, 0, 0}, { "tls_list", "lists all domains", tls_list, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; /* * pseudo variables */ static pv_export_t mod_items[] = { /* TLS session parameters */ {{"tls_version", sizeof("tls_version")-1}, 850, tlsops_version, 0, 0, 0, 0, 0 }, {{"tls_description", sizeof("tls_description")-1}, 850, tlsops_desc, 0, 0, 0, 0, 0 }, {{"tls_cipher_info", sizeof("tls_cipher_info")-1}, 850, tlsops_cipher, 0, 0, 0, 0, 0 }, {{"tls_cipher_bits", sizeof("tls_cipher_bits")-1}, 850, tlsops_bits, 0, 0, 0, 0, 0 }, /* general certificate parameters for peer and local */ {{"tls_peer_version", sizeof("tls_peer_version")-1}, 850, tlsops_cert_version, 0, 0, 0, pv_init_iname, CERT_PEER }, {{"tls_my_version", sizeof("tls_my_version")-1}, 850, tlsops_cert_version, 0, 0, 0, pv_init_iname, CERT_LOCAL }, {{"tls_peer_serial", sizeof("tls_peer_serial")-1}, 850, tlsops_sn, 0, 0, 0, pv_init_iname, CERT_PEER }, {{"tls_my_serial", sizeof("tls_my_serial")-1}, 850, tlsops_sn,0, 0, 0, pv_init_iname, CERT_LOCAL }, /* certificate parameters for peer and local, for subject and issuer*/ {{"tls_peer_subject", sizeof("tls_peer_subject")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT }, {{"tls_peer_issuer", sizeof("tls_peer_issuer")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_ISSUER }, {{"tls_my_subject", sizeof("tls_my_subject")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_SUBJECT }, {{"tls_my_issuer", sizeof("tls_my_issuer")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_ISSUER }, {{"tls_peer_subject_cn", sizeof("tls_peer_subject_cn")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT | COMP_CN }, {{"tls_peer_issuer_cn", sizeof("tls_peer_issuer_cn")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_ISSUER | COMP_CN }, {{"tls_my_subject_cn", sizeof("tls_my_subject_cn")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_SUBJECT | COMP_CN }, {{"tls_my_issuer_cn", sizeof("tls_my_issuer_cn")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_ISSUER | COMP_CN }, {{"tls_peer_subject_locality", sizeof("tls_peer_subject_locality")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT | COMP_L }, {{"tls_peer_issuer_locality", sizeof("tls_peer_issuer_locality")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_ISSUER | COMP_L }, {{"tls_my_subject_locality", sizeof("tls_my_subject_locality")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_SUBJECT | COMP_L }, {{"tls_my_issuer_locality", sizeof("tls_my_issuer_locality")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_ISSUER | COMP_L }, {{"tls_peer_subject_country", sizeof("tls_peer_subject_country")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT | COMP_C }, {{"tls_peer_issuer_country", sizeof("tls_peer_issuer_country")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_ISSUER | COMP_C }, {{"tls_my_subject_country", sizeof("tls_my_subject_country")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_SUBJECT | COMP_C }, {{"tls_my_issuer_country", sizeof("tls_my_issuer_country")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_ISSUER | COMP_C }, {{"tls_peer_subject_state", sizeof("tls_peer_subject_state")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT | COMP_ST }, {{"tls_peer_issuer_state", sizeof("tls_peer_issuer_state")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_ISSUER | COMP_ST }, {{"tls_my_subject_state", sizeof("tls_my_subject_state")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_SUBJECT | COMP_ST }, {{"tls_my_issuer_state", sizeof("tls_my_issuer_state")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_ISSUER | COMP_ST }, {{"tls_peer_subject_organization", sizeof("tls_peer_subject_organization")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT | COMP_O }, {{"tls_peer_issuer_organization", sizeof("tls_peer_issuer_organization")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_ISSUER | COMP_O }, {{"tls_my_subject_organization", sizeof("tls_my_subject_organization")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_SUBJECT | COMP_O }, {{"tls_my_issuer_organization", sizeof("tls_my_issuer_organization")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_ISSUER | COMP_O }, {{"tls_peer_subject_unit", sizeof("tls_peer_subject_unit")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT | COMP_OU }, {{"tls_peer_issuer_unit", sizeof("tls_peer_issuer_unit")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_ISSUER | COMP_OU }, {{"tls_my_subject_unit", sizeof("tls_my_subject_unit")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_SUBJECT | COMP_OU }, {{"tls_my_subject_serial", sizeof("tls_my_subject_serial")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_SUBJECT | COMP_SUBJECT_SERIAL }, {{"tls_peer_subject_serial", sizeof("tls_peer_subject_serial")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT | COMP_SUBJECT_SERIAL }, {{"tls_my_issuer_unit", sizeof("tls_my_issuer_unit")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_LOCAL | CERT_ISSUER | COMP_OU }, /* subject alternative name parameters for peer and local */ {{"tls_peer_san_email", sizeof("tls_peer_san_email")-1}, 850, tlsops_alt, 0, 0, 0, pv_init_iname, CERT_PEER | COMP_E }, {{"tls_my_san_email", sizeof("tls_my_san_email")-1}, 850, tlsops_alt, 0, 0, 0, pv_init_iname, CERT_LOCAL | COMP_E }, {{"tls_peer_san_hostname", sizeof("tls_peer_san_hostname")-1}, 850, tlsops_alt, 0, 0, 0, pv_init_iname, CERT_PEER | COMP_HOST }, {{"tls_my_san_hostname", sizeof("tls_my_san_hostname")-1}, 850, tlsops_alt, 0, 0, 0, pv_init_iname, CERT_LOCAL | COMP_HOST }, {{"tls_peer_san_uri", sizeof("tls_peer_san_uri")-1}, 850, tlsops_alt, 0, 0, 0, pv_init_iname, CERT_PEER | COMP_URI }, {{"tls_my_san_uri", sizeof("tls_my_san_uri")-1}, 850, tlsops_alt, 0, 0, 0, pv_init_iname, CERT_LOCAL | COMP_URI }, {{"tls_peer_san_ip", sizeof("tls_peer_san_ip")-1}, 850, tlsops_alt, 0, 0, 0, pv_init_iname, CERT_PEER | COMP_IP }, {{"tls_my_san_ip", sizeof("tls_my_san_ip")-1}, 850, tlsops_alt, 0, 0, 0, pv_init_iname, CERT_LOCAL | COMP_IP }, /* peer certificate validation parameters */ {{"tls_peer_verified", sizeof("tls_peer_verified")-1}, 850, tlsops_check_cert, 0, 0, 0, pv_init_iname, CERT_VERIFIED }, {{"tls_peer_revoked", sizeof("tls_peer_revoked")-1}, 850, tlsops_check_cert, 0, 0, 0, pv_init_iname, CERT_REVOKED }, {{"tls_peer_expired", sizeof("tls_peer_expired")-1}, 850, tlsops_check_cert, 0, 0, 0, pv_init_iname, CERT_EXPIRED }, {{"tls_peer_selfsigned", sizeof("tls_peer_selfsigned")-1}, 850, tlsops_check_cert, 0, 0, 0, pv_init_iname, CERT_SELFSIGNED }, {{"tls_peer_notBefore", sizeof("tls_peer_notBefore")-1}, 850, tlsops_validity, 0, 0, 0, pv_init_iname, CERT_NOTBEFORE }, {{"tls_peer_notAfter", sizeof("tls_peer_notAfter")-1}, 850, tlsops_validity, 0, 0, 0, pv_init_iname, CERT_NOTAFTER }, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; struct module_exports exports = { "tls_mgm", /* module name*/ MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ 0, /* exported async functions */ params, /* module parameters */ 0, /* exported statistics */ mi_cmds, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ mod_destroy,/* destroy function */ 0, /* per-child init function */ }; #if (OPENSSL_VERSION_NUMBER > 0x10001000L) /* * Load and set DH params to be used in ephemeral key exchange from a file. */ static int set_dh_params(SSL_CTX * ctx, char *filename) { LM_DBG("Entered\n"); BIO *bio = BIO_new_file(filename, "r"); if (!bio) { LM_ERR("unable to open dh params file '%s'\n", filename); return -1; } DH *dh = PEM_read_bio_DHparams(bio, 0, 0, 0); BIO_free(bio); if (!dh) { LM_ERR("unable to read dh params from '%s'\n", filename); return -1; } if (!SSL_CTX_set_tmp_dh(ctx, dh)) { LM_ERR("unable to set dh params\n"); return -1; } DH_free(dh); LM_DBG("DH params from '%s' successfully set\n", filename); return 0; } /* * Set elliptic curve. */ static int set_ec_params(SSL_CTX * ctx, const char* curve_name) { int curve = 0; if (curve_name) { curve = OBJ_txt2nid(curve_name); } if (curve > 0) { EC_KEY *ecdh = EC_KEY_new_by_curve_name (curve); if (! ecdh) { LM_ERR("unable to create EC curve\n"); return -1; } if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh)) { LM_ERR("unable to set tmp_ecdh\n"); return -1; } EC_KEY_free (ecdh); } else { LM_ERR("unable to find the EC curve\n"); return -1; } return 0; } #endif /* loads data from the db */ int load_info(db_func_t *dr_dbf, db_con_t* db_hdl, str *db_table, struct tls_domain **serv_dom, struct tls_domain **cli_dom) { int int_vals[4]; char *str_vals[11]; int i, n; int no_rows = 5; int db_cols = 15; /* the columns from the db table */ db_key_t columns[15]; /* result from a db query */ db_res_t* res; /* a row from the db table */ db_row_t* row; res = 0; columns[0] = &id_col; columns[1] = &address_col; columns[2] = &type_col; columns[3] = &method_col; columns[4] = &verify_cert_col; columns[5] = &require_cert_col; columns[6] = &certificate_col; columns[7] = &pk_col; columns[8] = &crl_check_col; columns[9] = &crl_dir_col; columns[10] = &calist_col; columns[11] = &cadir_col; columns[12] = &cplist_col; columns[13] = &dhparams_col; columns[14] = &eccurve_col; /* checking if the table version is up to date*/ if (db_check_table_version(dr_dbf, db_hdl, db_table, 1/*version*/) != 0) goto error; /* table to use*/ if (dr_dbf->use_table(db_hdl, db_table) < 0) { LM_ERR("cannot select table \"%.*s\"\n", db_table->len, db_table->s); goto error; } if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if (dr_dbf->query(db_hdl, 0, 0, 0, columns, 0, db_cols, 0, 0) < 0) { LM_ERR("DB query failed - retrieve valid connections \n"); goto error; } no_rows = estimate_available_rows(10 + 45 + 4 + 45 + 4 + 4 + 45 + 45 + 4 + 45 + 45 + 45 + 45 + 45 + 45, db_cols); if (no_rows == 0) no_rows = 5; if (dr_dbf->fetch_result(db_hdl, &res, no_rows) < 0) { LM_ERR("Error fetching rows\n"); goto error; } } else { if (dr_dbf->query(db_hdl, 0, 0, 0, columns, 0, db_cols, 0, &res) < 0) { LM_ERR("DB query failed - retrieve valid connections\n"); goto error; } } LM_DBG("%d rows found in %.*s\n", RES_ROW_N(res), db_table->len, db_table->s); n = 0; do { for (i = 0; i < RES_ROW_N(res); i++) { row = RES_ROWS(res) + i; check_val(id_col, ROW_VALUES(row), DB_STRING, 1, 1); str_vals[STR_VALS_ID_COL] = (char *) VAL_STRING(ROW_VALUES(row)); check_val(address_col, ROW_VALUES(row) + 1, DB_STRING, 1, 1); str_vals[STR_VALS_ADDRESS_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 1); check_val(type_col, ROW_VALUES(row) + 2, DB_INT, 1, 0); int_vals[INT_VALS_TYPE_COL] = VAL_INT(ROW_VALUES(row) + 2); check_val(method_col, ROW_VALUES(row) + 3, DB_STRING, 0, 0); str_vals[STR_VALS_METHOD_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 3); check_val(verify_cert_col, ROW_VALUES(row) + 4, DB_INT, 0, 0); int_vals[INT_VALS_VERIFY_CERT_COL] = VAL_INT(ROW_VALUES(row) + 4); check_val(require_cert_col, ROW_VALUES(row) + 5, DB_INT, 0, 0); int_vals[INT_VALS_REQUIRE_CERT_COL] = VAL_INT(ROW_VALUES(row) + 5); check_val(certificate_col, ROW_VALUES(row) + 6, DB_STRING, 0, 0); str_vals[STR_VALS_CERTIFICATE_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 6); check_val(pk_col, ROW_VALUES(row) + 7, DB_STRING, 0, 0); str_vals[STR_VALS_PK_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 7); check_val(crl_check_col, ROW_VALUES(row) + 8, DB_INT, 0, 0); int_vals[INT_VALS_CRL_CHECK_COL] = VAL_INT(ROW_VALUES(row) + 8); check_val(crl_dir_col, ROW_VALUES(row) + 9, DB_STRING, 0, 0); str_vals[STR_VALS_CRL_DIR_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 9); check_val(calist_col, ROW_VALUES(row) + 10, DB_STRING, 0, 0); str_vals[STR_VALS_CALIST_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 10); check_val(cadir_col, ROW_VALUES(row) + 11, DB_STRING, 0, 0); str_vals[STR_VALS_CADIR_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 11); check_val(cplist_col, ROW_VALUES(row) + 12, DB_STRING, 0, 0); str_vals[STR_VALS_CPLIST_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 12); check_val(dhparams_col, ROW_VALUES(row) + 13, DB_STRING, 0, 0); str_vals[STR_VALS_DHPARAMS_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 13); check_val(eccurve_col, ROW_VALUES(row) + 14, DB_STRING, 0, 0); str_vals[STR_VALS_ECCURVE_COL] = (char *) VAL_STRING(ROW_VALUES(row) + 14); tlsp_db_add_domain(str_vals, int_vals, serv_dom, cli_dom); n++; } if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) { if (dr_dbf->fetch_result(db_hdl, &res, no_rows) < 0) { LM_ERR("fetching rows\n"); goto error; } } else { break; } } while (RES_ROW_N(res) > 0); LM_DBG("%d records found in %.*s\n", n, db_table->len, db_table->s); dr_dbf->free_result(db_hdl, res); res = 0; return 0; error: LM_ERR("database"); return -1; } /* This callback is called during each verification process, at each step during the chain of certificates (this function is not the certificate_verification one!). */ int verify_callback(int pre_verify_ok, X509_STORE_CTX *ctx) { char buf[256]; X509 *err_cert; int err, depth; depth = X509_STORE_CTX_get_error_depth(ctx); LM_NOTICE("depth = %d\n",depth); if ( depth > VERIFY_DEPTH_S ) { LM_NOTICE("cert chain too long ( depth > VERIFY_DEPTH_S)\n"); pre_verify_ok=0; } if( pre_verify_ok ) { LM_NOTICE("preverify is good: verify return: %d\n", pre_verify_ok); return pre_verify_ok; } err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); X509_NAME_oneline(X509_get_subject_name(err_cert),buf,sizeof buf); LM_NOTICE("subject = %s\n", buf); LM_NOTICE("verify error:num=%d:%s\n", err, X509_verify_cert_error_string(err)); LM_NOTICE("error code is %d\n", ctx->error); switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf,sizeof buf); LM_NOTICE("issuer= %s\n",buf); break; case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case X509_V_ERR_CERT_NOT_YET_VALID: LM_NOTICE("notBefore\n"); break; case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: case X509_V_ERR_CERT_HAS_EXPIRED: LM_NOTICE("notAfter\n"); break; case X509_V_ERR_CERT_SIGNATURE_FAILURE: case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: LM_NOTICE("unable to decrypt cert " "signature\n"); break; case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: LM_NOTICE("unable to decode issuer " "public key\n"); break; case X509_V_ERR_OUT_OF_MEM: LM_NOTICE("out of memory \n"); break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: LM_NOTICE("Self signed certificate " "issue\n"); break; case X509_V_ERR_CERT_CHAIN_TOO_LONG: LM_NOTICE("certificate chain too long\n"); break; case X509_V_ERR_INVALID_CA: LM_NOTICE("invalid CA\n"); break; case X509_V_ERR_PATH_LENGTH_EXCEEDED: LM_NOTICE("path length exceeded\n"); break; case X509_V_ERR_INVALID_PURPOSE: LM_NOTICE("invalid purpose\n"); break; case X509_V_ERR_CERT_UNTRUSTED: LM_NOTICE("certificate untrusted\n"); break; case X509_V_ERR_CERT_REJECTED: LM_NOTICE("certificate rejected\n"); break; default: LM_NOTICE("something wrong with the cert" " ... error code is %d (check x509_vfy.h)\n", ctx->error); break; } LM_NOTICE("verify return:%d\n", pre_verify_ok); return(pre_verify_ok); } /* * Setup default SSL_CTX (and SSL * ) behavior: * verification, cipherlist, acceptable versions, ... */ static int init_ssl_ctx_behavior( struct tls_domain *d ) { int verify_mode; #if (OPENSSL_VERSION_NUMBER > 0x10001000L) /* * set dh params */ if (!d->tmp_dh_file) { LM_DBG("no DH params file for tls[%s:%d] defined, " "using default '%s'\n", ip_addr2a(&d->addr), d->port, tls_tmp_dh_file); d->tmp_dh_file = tls_tmp_dh_file; } if (d->tmp_dh_file && set_dh_params(d->ctx, d->tmp_dh_file) < 0) return -1; if (d->tls_ec_curve) { if (set_ec_params(d->ctx, d->tls_ec_curve) < 0) { return -1; } } else { LM_NOTICE("No EC curve defined\n"); } #else if (d->tmp_dh_file || tls_tmp_dh_file) LM_WARN("DH params file discarded as not supported by your openSSL version\n"); if (d->tls_ec_curve) LM_WARN("EC params file discarded as not supported by your openSSL version\n"); #endif if( d->ciphers_list != 0 ) { if( SSL_CTX_set_cipher_list(d->ctx, d->ciphers_list) == 0 ) { LM_ERR("failure to set SSL context " "cipher list '%s'\n", d->ciphers_list); return -1; } else { LM_NOTICE("cipher list set to %s\n", d->ciphers_list); } } else { LM_DBG( "cipher list null ... setting default\n"); } /* Set a bunch of options: * do not accept SSLv2 / SSLv3 * no session resumption * choose cipher according to server's preference's*/ SSL_CTX_set_options(d->ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_CIPHER_SERVER_PREFERENCE); /* Set verification procedure * The verification can be made null with SSL_VERIFY_NONE, or * at least easier with SSL_VERIFY_CLIENT_ONCE instead of * SSL_VERIFY_FAIL_IF_NO_PEER_CERT. * For extra control, instead of 0, we can specify a callback function: * int (*verify_callback)(int, X509_STORE_CTX *) * Also, depth 2 may be not enough in some scenarios ... though no need * to increase it much further */ if (d->type & TLS_DOMAIN_SRV) { /* Server mode: * SSL_VERIFY_NONE * the server will not send a client certificate request to the * client, so the client will not send a certificate. * SSL_VERIFY_PEER * the server sends a client certificate request to the client. * The certificate returned (if any) is checked. If the verification * process fails, the TLS/SSL handshake is immediately terminated * with an alert message containing the reason for the verification * failure. The behaviour can be controlled by the additional * SSL_VERIFY_FAIL_IF_NO_PEER_CERT and SSL_VERIFY_CLIENT_ONCE flags. * SSL_VERIFY_FAIL_IF_NO_PEER_CERT * if the client did not return a certificate, the TLS/SSL handshake * is immediately terminated with a ``handshake failure'' alert. * This flag must be used together with SSL_VERIFY_PEER. * SSL_VERIFY_CLIENT_ONCE * only request a client certificate on the initial TLS/SSL * handshake. Do not ask for a client certificate again in case of * a renegotiation. This flag must be used together with * SSL_VERIFY_PEER. */ if( d->verify_cert ) { verify_mode = SSL_VERIFY_PEER; if( d->require_client_cert ) { LM_WARN("client verification activated. Client " "certificates are mandatory.\n"); verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; } else LM_WARN("client verification activated. Client " "certificates are NOT mandatory.\n"); } else { verify_mode = SSL_VERIFY_NONE; LM_WARN("client verification NOT activated. Weaker security.\n"); } } else { /* Client mode: * SSL_VERIFY_NONE * if not using an anonymous cipher (by default disabled), the * server will send a certificate which will be checked. The result * of the certificate verification process can be checked after the * TLS/SSL handshake using the SSL_get_verify_result(3) function. * The handshake will be continued regardless of the verification * result. * SSL_VERIFY_PEER * the server certificate is verified. If the verification process * fails, the TLS/SSL handshake is immediately terminated with an * alert message containing the reason for the verification failure. * If no server certificate is sent, because an anonymous cipher is * used, SSL_VERIFY_PEER is ignored. * SSL_VERIFY_FAIL_IF_NO_PEER_CERT * ignored * SSL_VERIFY_CLIENT_ONCE * ignored */ if( d->verify_cert ) { verify_mode = SSL_VERIFY_PEER; LM_WARN("server verification activated.\n"); } else { verify_mode = SSL_VERIFY_NONE; LM_WARN("server verification NOT activated. Weaker security.\n"); } } SSL_CTX_set_verify( d->ctx, verify_mode, verify_callback); SSL_CTX_set_verify_depth( d->ctx, VERIFY_DEPTH_S); SSL_CTX_set_session_cache_mode( d->ctx, SSL_SESS_CACHE_SERVER ); SSL_CTX_set_session_id_context( d->ctx, OS_SSL_SESS_ID, OS_SSL_SESS_ID_LEN ); return 0; } /* * load a certificate from a file * (certificate file can be a chain, starting by the user cert, * and ending in the root CA; if not all needed certs are in this * file, they are looked up in the caFile or caPATH (see verify * function). */ static int load_certificate(SSL_CTX * ctx, char *filename) { LM_DBG("entered\n"); if (!SSL_CTX_use_certificate_chain_file(ctx, filename)) { LM_ERR("unable to load certificate file '%s'\n", filename); return -1; } LM_DBG("'%s' successfully loaded\n", filename); return 0; } static int load_crl(SSL_CTX * ctx, char *crl_directory, int crl_check_all) { #if OPENSSL_VERSION_NUMBER >= 0x10000000L DIR *d; struct dirent *dir; int crl_added = 0; LM_DBG("Loading CRL from directory\n"); /*Get X509 store from SSL context*/ X509_STORE *store = SSL_CTX_get_cert_store(ctx); if(!store) { LM_ERR("Unable to get X509 store from ssl context\n"); return -1; } /*Parse directory*/ d = opendir(crl_directory); if(!d) { LM_ERR("Unable to open crl directory '%s'\n", crl_directory); return -1; } while ((dir = readdir(d)) != NULL) { /*Skip if not regular file*/ if (dir->d_type != DT_REG) continue; /*Create filename*/ char* filename = (char*) pkg_malloc(sizeof(char)*(strlen(crl_directory)+strlen(dir->d_name)+2)); if (!filename) { LM_ERR("Unable to allocate crl filename\n"); closedir(d); return -1; } strcpy(filename,crl_directory); if(filename[strlen(filename)-1] != '/') strcat(filename,"/"); strcat(filename,dir->d_name); /*Get CRL content*/ FILE *fp = fopen(filename,"r"); pkg_free(filename); if(!fp) continue; X509_CRL *crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL); fclose(fp); if(!crl) continue; /*Add CRL to X509 store*/ if (X509_STORE_add_crl(store, crl) == 1) crl_added++; else LM_ERR("Unable to add crl to ssl context\n"); X509_CRL_free(crl); } closedir(d); if (!crl_added) { LM_ERR("No suitable CRL files found in directory %s\n", crl_directory); return -1; } /*Enable CRL checking*/ X509_VERIFY_PARAM *param; param = X509_VERIFY_PARAM_new(); int flags = X509_V_FLAG_CRL_CHECK; if(crl_check_all) flags |= X509_V_FLAG_CRL_CHECK_ALL; X509_VERIFY_PARAM_set_flags(param, flags); SSL_CTX_set1_param(ctx, param); X509_VERIFY_PARAM_free(param); return 0; #else static int already_warned = 0; if (!already_warned) { LM_WARN("CRL not supported in %s\n", OPENSSL_VERSION_TEXT); already_warned = 1; } return 0; #endif } /* * Load a caList, to be used to verify the client's certificate. * The list is to be stored in a single file, containing all * the acceptable root certificates. */ static int load_ca(SSL_CTX * ctx, char *filename) { LM_DBG("Entered\n"); if (!SSL_CTX_load_verify_locations(ctx, filename, 0)) { LM_ERR("unable to load ca '%s'\n", filename); return -1; } LM_DBG("CA '%s' successfully loaded\n", filename); return 0; } /* * Load a caList from a directory instead of a single file. */ static int load_ca_dir(SSL_CTX * ctx, char *directory) { LM_DBG("Entered\n"); if (!SSL_CTX_load_verify_locations(ctx, 0 , directory)) { LM_ERR("unable to load ca directory '%s'\n", directory); return -1; } LM_DBG("CA '%s' successfully loaded from directory\n", directory); return 0; } static int passwd_cb(char *buf, int size, int rwflag, void *filename) { UI *ui; const char *prompt; ui = UI_new(); if (ui == NULL) goto err; prompt = UI_construct_prompt(ui, "passphrase", filename); UI_add_input_string(ui, prompt, 0, buf, 0, size - 1); UI_process(ui); UI_free(ui); return strlen(buf); err: LM_ERR("passwd_cb failed\n"); if (ui) UI_free(ui); return 0; } /* * load a private key from a file */ static int load_private_key(SSL_CTX * ctx, char *filename) { #define NUM_RETRIES 3 int idx, ret_pwd; LM_DBG("entered\n"); SSL_CTX_set_default_passwd_cb(ctx, passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ctx, filename); for(idx = 0, ret_pwd = 0; idx < NUM_RETRIES; idx++ ) { ret_pwd = SSL_CTX_use_PrivateKey_file(ctx, filename, SSL_FILETYPE_PEM); if ( ret_pwd ) { break; } else { LM_ERR("unable to load private key file '%s'. \n" "Retry (%d left) (check password case)\n", filename, (NUM_RETRIES - idx -1) ); continue; } } if( ! ret_pwd ) { LM_ERR("unable to load private key file '%s'\n", filename); return -1; } if (!SSL_CTX_check_private_key(ctx)) { LM_ERR("key '%s' does not match the public key of the certificate\n", filename); return -1; } LM_DBG("key '%s' successfully loaded\n", filename); return 0; } /* * initialize tls virtual domains */ static int init_tls_domains(struct tls_domain *d) { struct tls_domain *dom; dom = d; while (d) { if (d->name.len) { LM_INFO("Processing TLS domain '%.*s'\n", d->name.len, ZSW(d->name.s)); } else { LM_INFO("Processing TLS domain [%s:%d]\n", ip_addr2a(&d->addr), d->port); } /* * set method */ if (d->method == TLS_METHOD_UNSPEC) { LM_DBG("no method for tls[%s:%d], using default\n", ip_addr2a(&d->addr), d->port); d->method = tls_default_method; } /* * create context */ d->ctx = SSL_CTX_new(ssl_methods[d->method - 1]); if (d->ctx == NULL) { LM_ERR("cannot create ssl context for " "tls[%s:%d]\n", ip_addr2a(&d->addr), d->port); return -1; } if (init_ssl_ctx_behavior( d ) < 0) return -1; /* * load certificate */ if (!d->cert_file) { LM_NOTICE("no certificate for tls[%s:%d] defined, using default" "'%s'\n", ip_addr2a(&d->addr), d->port, tls_cert_file); d->cert_file = tls_cert_file; } if (load_certificate(d->ctx, d->cert_file) < 0) return -1; /** * load crl from directory */ if (!d->crl_directory) { LM_NOTICE("no crl for tls, using none"); } else { if(load_crl(d->ctx, d->crl_directory, d->crl_check_all) < 0) return -1; } /* * load ca */ if (!d->ca_file) { LM_NOTICE("no CA for tls[%s:%d] defined, " "using default '%s'\n", ip_addr2a(&d->addr), d->port, tls_ca_file); d->ca_file = tls_ca_file; } if (d->ca_file && load_ca(d->ctx, d->ca_file) < 0) return -1; /* * load ca from directory */ if (!d->ca_directory) { LM_NOTICE("no CA for tls[%s:%d] defined, " "using default '%s'\n", ip_addr2a(&d->addr), d->port, tls_ca_dir); d->ca_directory = tls_ca_dir; } if (d->ca_directory && load_ca_dir(d->ctx, d->ca_directory) < 0) return -1; d = d->next; } /* * load all private keys as the last step (may prompt for password) */ d = dom; while (d) { if (!d->pkey_file) { LM_NOTICE("no private key for tls[%s:%d] defined, using default" "'%s'\n", ip_addr2a(&d->addr), d->port, tls_pkey_file); d->pkey_file = tls_pkey_file; } if (load_private_key(d->ctx, d->pkey_file) < 0) return -1; d = d->next; } return 0; } static int check_for_krb(void) { SSL_CTX *xx; int j; xx = SSL_CTX_new(ssl_methods[tls_default_method - 1]); if (xx==NULL) return -1; for( j=0 ; jcipher_list) ; j++) { SSL_CIPHER *yy = sk_SSL_CIPHER_value(xx->cipher_list,j); if ( yy->id>=SSL3_CK_KRB5_DES_64_CBC_SHA && yy->id<=SSL3_CK_KRB5_RC4_40_MD5 ) { LM_INFO("KRB5 cipher %s found\n", yy->name); SSL_CTX_free(xx); return 1; } } SSL_CTX_free(xx); return 0; } static int tls_init_multithread(void) { /* init static locks support */ tls_static_locks_no = CRYPTO_num_locks(); if (tls_static_locks_no>0) { /* init a lock set & pass locking function to SSL */ tls_static_locks = lock_set_alloc(tls_static_locks_no); if (tls_static_locks == NULL) { LM_ERR("Failed to alloc static locks\n"); return -1; } if (lock_set_init(tls_static_locks)==0) { LM_ERR("Failed to init static locks\n"); lock_set_dealloc(tls_static_locks); return -1; } CRYPTO_set_locking_callback(tls_static_locks_ops); } CRYPTO_set_id_callback(tls_get_id); /* dynamic locks support*/ CRYPTO_set_dynlock_create_callback(tls_dyn_lock_create); CRYPTO_set_dynlock_lock_callback(tls_dyn_lock_ops); CRYPTO_set_dynlock_destroy_callback(tls_dyn_lock_destroy); return 0; } /* * initialize ssl methods */ static void init_ssl_methods(void) { LM_DBG("entered\n"); ssl_methods[TLS_USE_TLSv1_cli-1] = (SSL_METHOD*)TLSv1_client_method(); ssl_methods[TLS_USE_TLSv1_srv-1] = (SSL_METHOD*)TLSv1_server_method(); ssl_methods[TLS_USE_TLSv1-1] = (SSL_METHOD*)TLSv1_method(); ssl_methods[TLS_USE_SSLv23_cli-1] = (SSL_METHOD*)SSLv23_client_method(); ssl_methods[TLS_USE_SSLv23_srv-1] = (SSL_METHOD*)SSLv23_server_method(); ssl_methods[TLS_USE_SSLv23-1] = (SSL_METHOD*)SSLv23_method(); #if OPENSSL_VERSION_NUMBER >= 0x10001000L ssl_methods[TLS_USE_TLSv1_2_cli-1] = (SSL_METHOD*)TLSv1_2_client_method(); ssl_methods[TLS_USE_TLSv1_2_srv-1] = (SSL_METHOD*)TLSv1_2_server_method(); ssl_methods[TLS_USE_TLSv1_2-1] = (SSL_METHOD*)TLSv1_2_method(); #endif } /* reloads data from the db */ static int reload_data(void) { int n; struct tls_domain *tls_client_domains_tmp; struct tls_domain *tls_server_domains_tmp; tls_client_domains_tmp = NULL; tls_server_domains_tmp = NULL; load_info(&dr_dbf, db_hdl, &tls_db_table, &tls_server_domains_tmp, &tls_client_domains_tmp); /* * now initialize tls virtual domains */ if ((n = init_tls_domains(tls_server_domains_tmp))) { return n; } if ((n = init_tls_domains(tls_client_domains_tmp))) { return n; } lock_start_write(dom_lock); tls_release_all_domains(tls_client_domains); tls_release_all_domains(tls_server_domains); tls_client_domains = tls_client_domains_tmp; tls_server_domains = tls_server_domains_tmp; lock_stop_write(dom_lock); return 0; } /* reloads data from the db */ static struct mi_root* tls_reload(struct mi_root* root, void *param) { LM_INFO("reload data MI command received!\n"); if (!tls_db_enabled) return init_mi_tree(500,"DB mode not enabled", 19); if (reload_data() < 0) { LM_CRIT("failed to load routing data\n"); return init_mi_tree(500, "Failed to reload", 16); } return init_mi_tree(200, MI_SSTR(MI_OK)); } static int mod_init(void){ str s; int n; LM_INFO("initializing TLS protocol\n"); if (tls_db_enabled != 0 && tls_db_enabled != 1) { tls_db_enabled = 1; } if (tls_db_enabled) { /* create & init lock */ if ((dom_lock = lock_init_rw()) == NULL) { LM_CRIT("failed to init lock\n"); return -1; } init_db_url(tls_db_url, 0 /*cannot be null*/); tls_db_table.len = strlen(tls_db_table.s); if (tls_db_table.len == 0) { LM_ERR("db url not specified\n"); return -1; } id_col.len = strlen(id_col.s); address_col.len = strlen(address_col.s); type_col.len = strlen(type_col.s); method_col.len = strlen(method_col.s); verify_cert_col.len = strlen(verify_cert_col.s); require_cert_col.len = strlen(require_cert_col.s); certificate_col.len = strlen(certificate_col.s); pk_col.len = strlen(pk_col.s); crl_check_col.len = strlen(crl_check_col.s); calist_col.len = strlen(calist_col.s); cadir_col.len = strlen(cadir_col.s); cplist_col.len = strlen(cplist_col.s); dhparams_col.len = strlen(dhparams_col.s); eccurve_col.len = strlen(eccurve_col.s); if (db_bind_mod(&tls_db_url, &dr_dbf)) { LM_CRIT("cannot bind to database module! " "Did you forget to load a database module ?\n"); return -1; } /* init DB connection */ if ((db_hdl = dr_dbf.init(&tls_db_url)) == 0) { LM_CRIT("cannot initialize database connection\n"); return -1; } if (dr_dbf.use_table(db_hdl, &tls_db_table) < 0) { LM_ERR("cannot select table \"%.*s\"\n", tls_db_table.len, tls_db_table.s); return -1; } } if (tls_domain_avp) { s.s = tls_domain_avp; s.len = strlen(s.s); if (parse_avp_spec( &s, &tls_client_domain_avp)) { LM_ERR("cannot parse tls_client_avp"); return -1; } } /* * this has to be called before any function calling CRYPTO_malloc, * CRYPTO_malloc will set allow_customize in openssl to 0 */ if (!CRYPTO_set_mem_functions(os_malloc, os_realloc, os_free)) { LM_ERR("unable to set the memory allocation functions\n"); LM_ERR("NOTE: check if you have openssl 1.0.1e-fips, as this " "version is know to be broken; if so, you need to upgrade or " "downgrade to a differen openssl version !!\n"); return -1; } #if !defined(OPENSSL_NO_COMP) STACK_OF(SSL_COMP)* comp_methods; /* disabling compression */ LM_WARN("disabling compression due ZLIB problems\n"); comp_methods = SSL_COMP_get_compression_methods(); if (comp_methods==0) { LM_INFO("openssl compression already disabled\n"); } else { sk_SSL_COMP_zero(comp_methods); } #endif if (tls_init_multithread() < 0) { LM_ERR("failed to init multi-threading support\n"); return -1; } SSL_library_init(); SSL_load_error_strings(); init_ssl_methods(); n = check_for_krb(); if (n==-1) { LM_ERR("kerberos check failed\n"); return -1; } if ( ( n ^ #ifndef OPENSSL_NO_KRB5 1 #else 0 #endif )!=0 ) { LM_ERR("compiled agaist an openssl with %s" "kerberos, but run with one with %skerberos\n", (n==1)?"":"no ",(n!=1)?"no ":""); return -1; } /* * finish setting up the tls default domains */ tls_default_client_domain.type = TLS_DOMAIN_DEF|TLS_DOMAIN_CLI ; tls_default_client_domain.addr.af = AF_INET; tls_default_server_domain.type = TLS_DOMAIN_DEF|TLS_DOMAIN_SRV; tls_default_server_domain.addr.af = AF_INET; /* * now initialize tls default domains */ if ( (n=init_tls_domains(&tls_default_server_domain)) ) { return n; } if ( (n=init_tls_domains(&tls_default_client_domain)) ) { return n; } /* * now initialize tls virtual domains */ if (tls_db_enabled && load_info(&dr_dbf, db_hdl, &tls_db_table, &tls_server_domains, &tls_client_domains)){ return -1; } if ( (n=init_tls_domains(tls_server_domains)) ) { return n; } if ( (n=init_tls_domains(tls_client_domains)) ) { return n; } /* * we are all set */ return 0; } /* * called from main.c when opensips exits (main process) */ static void mod_destroy(void) { struct tls_domain *d; LM_DBG("entered\n"); if (dom_lock) { lock_destroy_rw(dom_lock); dom_lock = 0; } d = tls_server_domains; while (d) { if (d->ctx) SSL_CTX_free(d->ctx); lock_destroy(d->lock); lock_dealloc(d->lock); d = d->next; } d = tls_client_domains; while (d) { if (d->ctx) SSL_CTX_free(d->ctx); lock_destroy(d->lock); lock_dealloc(d->lock); d = d->next; } if (tls_default_server_domain.ctx) { SSL_CTX_free(tls_default_server_domain.ctx); } if (tls_default_client_domain.ctx) { SSL_CTX_free(tls_default_client_domain.ctx); } tls_free_domains(); /* TODO - destroy static locks */ /* library destroy */ ERR_free_strings(); /*SSL_free_comp_methods(); - this function is not on std. openssl*/ EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); return; } static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2) { struct tcp_connection *c; SSL *ssl; long ssl_verify; X509 *x509_cert; LM_DBG("started...\n"); if (msg->rcv.proto != PROTO_TLS) { LM_ERR("proto != TLS --> peer can't be verified, return -1\n"); return -1; } LM_DBG("trying to find TCP connection of received message...\n"); /* what if we have multiple connections to the same remote socket? e.g. we can have connection 1: localIP1:localPort1 <--> remoteIP:remotePort connection 2: localIP2:localPort2 <--> remoteIP:remotePort but I think the is very unrealistic */ tcp_conn_get(0, &(msg->rcv.src_ip), msg->rcv.src_port, PROTO_TLS, &c, NULL/*fd*/); if (c==NULL) { LM_ERR("no corresponding TLS/TCP connection found." " This should not happen... return -1\n"); return -1; } LM_DBG("corresponding TLS/TCP connection found. s=%d, fd=%d, id=%d\n", c->s, c->fd, c->id); if (!c->extra_data) { LM_ERR("no extra_data specified in TLS/TCP connection found." " This should not happen... return -1\n"); goto error; } ssl = (SSL *) c->extra_data; ssl_verify = SSL_get_verify_result(ssl); if ( ssl_verify != X509_V_OK ) { LM_WARN("verification of presented certificate failed... return -1\n"); goto error; } /* now, we have only valid peer certificates or peers without certificates. * Thus we have to check for the existence of a peer certificate */ x509_cert = SSL_get_peer_certificate(ssl); if ( x509_cert == NULL ) { LM_WARN("tlsops:is_peer_verified: WARNING: peer did not presented " "a certificate. Thus it could not be verified... return -1\n"); goto error; } X509_free(x509_cert); tcp_conn_release(c, 0); LM_DBG("tlsops:is_peer_verified: peer is successfully verified" "...done\n"); return 1; error: tcp_conn_release(c, 0); return -1; } static int tls_get_handshake_timeout(void) { return tls_handshake_timeout; } static int tls_get_send_timeout(void) { return tls_send_timeout; } /* lists client or server domains*/ static int list_domain(struct mi_node *root, struct tls_domain *d) { struct mi_node *node = NULL; struct mi_node *child; char *addr; while (d) { node = add_mi_node_child(root, MI_DUP_VALUE, "ID", 2, d->id.s, d->id.len); ; if (node == NULL) goto error; if (d->type & TLS_DOMAIN_SRV) child = add_mi_node_child(node, 0, "Type", 4, "TLS_DOMAIN_SRV", 14); else child = add_mi_node_child(node, 0, "Type", 4, "TLS_DOMAIN_CLI", 14); if (child == NULL) goto error; if (d->type & TLS_DOMAIN_NAME) { child = add_mi_node_child(node, MI_DUP_VALUE, "Name", 4, d->name.s, d->name.len); if (child == NULL) goto error; } else { addr = ip_addr2a(&d->addr); if (addr == NULL) goto error; child = add_mi_node_child(node, MI_DUP_VALUE, "Address", 7, addr, strlen(addr)); if (child == NULL) goto error; } switch (d->method) { case TLS_USE_TLSv1_cli: case TLS_USE_TLSv1_srv: case TLS_USE_TLSv1: child = add_mi_node_child(node, 0, "METHOD", 4, "TLSv1", 5); break; case TLS_USE_SSLv23_cli: case TLS_USE_SSLv23_srv: case TLS_USE_SSLv23: child = add_mi_node_child(node, 0, "METHOD", 4, "SSLv23", 6); break; case TLS_USE_TLSv1_2_cli: case TLS_USE_TLSv1_2_srv: case TLS_USE_TLSv1_2: child = add_mi_node_child(node, 0, "METHOD", 4, "TLSv1_2", 7); break; default: goto error; } if (child == NULL) goto error; if (d->verify_cert) child = add_mi_node_child(node, 0, "VERIFY_CERT", 11, "yes", 3); else child = add_mi_node_child(node, 0, "VERIFY_CERT", 11, "no", 2); if (child == NULL) goto error; if (d->require_client_cert) child = add_mi_node_child(node, 0, "REQ_CLI_CERT", 12, "yes", 3); else child = add_mi_node_child(node, 0, "REQ_CLI_CERT", 12, "no", 2); if (child == NULL) goto error; if (d->crl_check_all) child = add_mi_node_child(node, 0, "CRL_CHECKALL", 12, "yes", 3); else child = add_mi_node_child(node, 0, "CRL_CHECKALL", 12, "no", 2); if (child == NULL) goto error; child = add_mi_node_child(node, MI_DUP_VALUE, "CERT_FILE", 9, d->cert_file, len(d->cert_file)); if (child == NULL) goto error; child = add_mi_node_child(node, MI_DUP_VALUE, "CRL_DIR", 7, d->crl_directory, len(d->crl_directory)); if (child == NULL) goto error; child = add_mi_node_child(node, MI_DUP_VALUE, "CA_FILE", 7, d->ca_file, len(d->ca_file)); if (child == NULL) goto error; child = add_mi_node_child(node, MI_DUP_VALUE, "CA_DIR", 6, d->ca_directory, len(d->ca_directory)); if (child == NULL) goto error; child = add_mi_node_child(node, MI_DUP_VALUE, "PKEY_FILE", 9, d->pkey_file, len(d->pkey_file)); if (child == NULL) goto error; child = add_mi_node_child(node, MI_DUP_VALUE, "CIPHER_LIST", 11, d->ciphers_list, len(d->ciphers_list)); if (child == NULL) goto error; child = add_mi_node_child(node, MI_DUP_VALUE, "DH_PARAMS", 9, d->tmp_dh_file, len(d->tmp_dh_file)); if (child == NULL) goto error; child = add_mi_node_child(node, MI_DUP_VALUE, "EC_CURVE", 8, d->tls_ec_curve, len(d->tls_ec_curve)); if (child == NULL) goto error; d = d->next; } return 0; error: return -1; } /* lists all domains */ static struct mi_root * tls_list(struct mi_root *cmd_tree, void *param) { struct mi_node *root = NULL; struct mi_root *rpl_tree = NULL; rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (rpl_tree == NULL) { return NULL; } if (dom_lock) lock_start_read(dom_lock); root = &rpl_tree->node; if (list_domain(root, tls_client_domains) < 0) goto error; if (list_domain(root, tls_server_domains) < 0) goto error; if (dom_lock) lock_stop_read(dom_lock); return rpl_tree; error: if (dom_lock) lock_stop_read(dom_lock); if (rpl_tree) free_mi_tree(rpl_tree); return NULL; } static int load_tls_mgm(struct tls_mgm_binds *binds) { binds->find_server_domain = tls_find_server_domain; binds->find_client_domain = tls_find_client_domain; binds->get_handshake_timeout = tls_get_handshake_timeout; binds->get_send_timeout = tls_get_send_timeout; binds->release_domain = tls_release_domain; /* everything ok*/ return 1; } opensips-2.2.2/modules/tls_mgm/tls_params.c000066400000000000000000000231071300170765700210110ustar00rootroot00000000000000/* * Copyright (C) 2015 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-18 first version (bogdan) */ #include #include "../../dprint.h" #include "../../resolve.h" /* for str2ip() */ #include "../../ut.h" #include "tls_params.h" static int parse_domain_address(char *val, struct ip_addr **ip, unsigned int *port, str *domain) { char *p = (char*)val; str s; /* get IP */ s.s = p; if ( (p=strchr( p, ':'))==NULL ) goto has_domain; s.len = p-s.s; p++; if ( (*ip=str2ip( &s ))==NULL ) { LM_ERR("[%.*s] is not an ip\n", s.len, s.s); goto parse_err; } /* what is left should be a port */ s.s = p; s.len = val + strlen(val) - p; if (str2int( &s, port)<0) { LM_ERR("[%.*s] is not a port\n", s.len, s.s); goto parse_err; } return 0; has_domain: /* what is left should be a domain */ domain->s = s.s; domain->len = val + strlen(val) - s.s; *ip = NULL; return 0; parse_err: LM_ERR("invalid TSL domain [%s] (error around pos %d)\n", val, (int)(long)(p-val) ); return -1; } static int parse_domain_def(char *val, str *id, struct ip_addr **ip, unsigned int *port, str *domain) { char *p = (char*)val; /* first get the ID */ id->s = p; if ( (p=strchr( p, '='))==NULL ) goto parse_err; id->len = p-id->s; p++; return parse_domain_address(p, ip, port, domain); parse_err: LM_ERR("invalid TSL domain [%s] (error around pos %d)\n", val, (int)(long)(p-val) ); return -1; } int tlsp_add_srv_domain(modparam_t type, void *val) { struct ip_addr *ip; unsigned int port; str domain; str id; if (tls_db_enabled) return -1; if (parse_domain_def( (char*)val, &id, &ip, &port, &domain)<0 ) return -1; if (ip==NULL) { LM_ERR("server domains do not support 'domain name' in definition\n"); return -1; } /* add domain */ if (tls_new_server_domain( &id, ip, port, &tls_server_domains) < 0) { LM_ERR("failed to add new server domain [%s]\n",(char*)val); return -1; } return 1; } int tlsp_add_cli_domain(modparam_t type, void *val) { struct ip_addr *ip; unsigned int port; str domain; str id; if (tls_db_enabled) return -1; if (parse_domain_def( (char*)val, &id, &ip, &port, &domain)<0 ) return -1; /* add domain */ if (ip==NULL) { if (tls_new_client_domain_name( &id, &domain, &tls_client_domains) < 0) { LM_ERR("failed to add new client domain name [%s]\n",(char*)val); return -1; } } else { if (tls_new_client_domain( &id, ip, port, &tls_client_domains ) < 0) { LM_ERR("failed to add new client domain [%s]\n",(char*)val); return -1; } } return 1; } int tlsp_db_add_domain(char **str_vals, int *int_vals, struct tls_domain **serv_dom, struct tls_domain **cli_dom){ struct ip_addr *ip; unsigned int port; str domain; str id; id.s = str_vals[STR_VALS_ID_COL]; id.len = strlen(id.s); if (parse_domain_address( str_vals[STR_VALS_ADDRESS_COL], &ip, &port, &domain)<0 ) return -1; /* add domain */ if (int_vals[INT_VALS_TYPE_COL] == CLIENT_DOMAIN) { if (ip == NULL) { if (tls_new_client_domain_name(&id, &domain, cli_dom) < 0) { LM_ERR("failed to add new client domain name [%.*s]\n", domain.len, domain.s); return -1; } } else { if (tls_new_client_domain(&id, ip, port, cli_dom) < 0) { LM_ERR("failed to add new client domain [%.*s]\n", domain.len, domain.s); return -1; } } if (set_all_domain_attr(cli_dom, str_vals, int_vals) < 0){ LM_ERR("failed to set domain [%.*s] attr\n", domain.len, domain.s); return -1; } } else { if (ip) { if (tls_new_server_domain(&id, ip, port, serv_dom) < 0) { LM_ERR("failed to add new server domain [%.*s]\n", domain.len, domain.s); return -1; } } else { LM_ERR("server domains do not support 'domain name' in definition\n"); return -1; } if (set_all_domain_attr(serv_dom, str_vals, int_vals) < 0){ LM_ERR("failed to set domain [%.*s] attr\n", domain.len, domain.s ); return -1; } } return 1; } static void split_param_val(char *in, str *id, str *val) { char *p = (char*)in; /* format is '[ID=]value' */ /* first try to get the ID */ id->s = p; if ( (p=strchr( p, ':'))==NULL ) { /* just value */ val->s = id->s; val->len = strlen(id->s); id->s = NULL; id->len = 0; return; } /* ID found */ id->len = p-id->s; p++; /* what is left should be the value */ val->s = p; val->len = in + strlen(in) - p; return; } #define set_domain_attr( _id, _field, _val) \ do { \ struct tls_domain *_d; \ if ((_id).s) { \ /* specific TLS domain */ \ if ( (_d=tls_find_domain_by_id(&(_id)))==NULL ) { \ LM_ERR("TLS domain [%.*s] not defined in [%s]\n", \ (_id).len, (_id).s, (char*)in); \ return -1; \ } \ _d->_field = _val; \ } else { \ /* set default domains */ \ tls_default_server_domain._field = _val; \ tls_default_client_domain._field = _val; \ } \ } while(0) int tlsp_set_method(modparam_t type, void *in) { str id; str val; int method; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; if ( strcasecmp( val.s, "SSLV23")==0 || strcasecmp( val.s, "TLSany")==0 ) method = TLS_USE_SSLv23; else if ( strcasecmp( val.s, "TLSV1")==0 ) method = TLS_USE_TLSv1; else if ( strcasecmp( val.s, "TLSV1_2")==0 ) method = TLS_USE_TLSv1_2; else { LM_ERR("unsupported method [%s]\n",val.s); return -1; } set_domain_attr( id, method, method); return 1; } int tlsp_set_verify(modparam_t type, void *in) { str id; str val; unsigned int verify; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; if (str2int( &val, &verify)!=0) { LM_ERR("option is not a number [%s]\n",val.s); return -1; } set_domain_attr( id, verify_cert, verify); return 1; } int tlsp_set_require(modparam_t type, void *in) { str id; str val; unsigned int req; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; if (str2int( &val, &req)!=0) { LM_ERR("option is not a number [%s]\n",val.s); return -1; } set_domain_attr( id, require_client_cert, req); return 1; } int tlsp_set_crl_check(modparam_t type, void *in) { str id; str val; unsigned int check; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; if (str2int( &val, &check)!=0) { LM_ERR("option is not a number [%s]\n",val.s); return -1; } set_domain_attr( id, crl_check_all, check); return 1; } int tlsp_set_crldir(modparam_t type, void *in) { str id; str val; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; set_domain_attr( id, crl_directory, val.s); return 1; } int tlsp_set_certificate(modparam_t type, void *in) { str id; str val; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; set_domain_attr( id, cert_file, val.s); return 1; } int tlsp_set_pk(modparam_t type, void *in) { str id; str val; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; set_domain_attr( id, pkey_file, val.s); return 1; } int tlsp_set_calist(modparam_t type, void *in) { str id; str val; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; set_domain_attr( id, ca_file, val.s); return 1; } int tlsp_set_cadir(modparam_t type, void *in) { str id; str val; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; set_domain_attr( id, ca_directory, val.s); return 1; } int tlsp_set_cplist(modparam_t type, void *in) { str id; str val; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; set_domain_attr( id, ciphers_list, val.s); return 1; } int tlsp_set_dhparams(modparam_t type, void *in) { str id; str val; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; set_domain_attr( id, tmp_dh_file, val.s); return 1; } int tlsp_set_eccurve(modparam_t type, void *in) { str id; str val; split_param_val( (char*)in, &id, &val); if (tls_db_enabled && id.s) return -1; set_domain_attr( id, tls_ec_curve, val.s); return 1; } opensips-2.2.2/modules/tls_mgm/tls_params.h000066400000000000000000000063261300170765700210220ustar00rootroot00000000000000/* * Copyright (C) 2015 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2015-02-18 first version (bogdan) */ #ifndef _MOD_PROTO_TLS_tls_params_h #define _MOD_PROTO_TLS_tls_params_h #include "../../sr_module.h" #include "tls_domain.h" #define STR_VALS_ID_COL 0 #define STR_VALS_ADDRESS_COL 1 #define STR_VALS_METHOD_COL 2 #define STR_VALS_CERTIFICATE_COL 3 #define STR_VALS_PK_COL 4 #define STR_VALS_CRL_DIR_COL 5 #define STR_VALS_CALIST_COL 6 #define STR_VALS_CADIR_COL 7 #define STR_VALS_CPLIST_COL 8 #define STR_VALS_DHPARAMS_COL 9 #define STR_VALS_ECCURVE_COL 10 #define INT_VALS_TYPE_COL 0 #define INT_VALS_VERIFY_CERT_COL 1 #define INT_VALS_REQUIRE_CERT_COL 2 #define INT_VALS_CRL_CHECK_COL 3 #define CLIENT_DOMAIN 0 #define SERVER_DOMAIN 1 int tlsp_add_srv_domain(modparam_t type, void *val); int tlsp_add_cli_domain(modparam_t type, void *val); int tlsp_set_method(modparam_t type, void *val); int tlsp_set_verify(modparam_t type, void *val); int tlsp_set_require(modparam_t type, void *val); int tlsp_set_crl_check(modparam_t type, void *val); int tlsp_set_certificate(modparam_t type, void *val); int tlsp_set_pk(modparam_t type, void *val); int tlsp_set_crldir(modparam_t type, void *val); int tlsp_set_calist(modparam_t type, void *val); int tlsp_set_cadir(modparam_t type, void *val); int tlsp_set_cplist(modparam_t type, void *val); int tlsp_set_dhparams(modparam_t type, void *val); int tlsp_set_eccurve(modparam_t type, void *val); int tlsp_db_add_domain(char **str_vals, int *int_vals, struct tls_domain **serv_dom, struct tls_domain **cli_dom); #endif opensips-2.2.2/modules/tls_mgm/tls_select.c000066400000000000000000000352171300170765700210120ustar00rootroot00000000000000/* * TLS module - select interface * * Copyright (C) 2001-2003 FhG FOKUS * Copyright (C) 2004,2005 Free Software Foundation, Inc. * Copyright (C) 2005 iptelorg GmbH * Copyright (C) 2006 enum.at * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include "../../globals.h" #include "../../net/net_tcp.h" #include "../../ut.h" #include "tls_select.h" struct tcp_connection* get_cur_connection(struct sip_msg* msg) { struct tcp_connection* c; if (msg->rcv.proto != PROTO_TLS) { LM_ERR("transport protocol is not TLS (bug in config)\n"); return 0; } tcp_conn_get(msg->rcv.proto_reserved1, 0, 0, PROTO_NONE, &c, NULL/*fd*/); if (c && c->type != PROTO_TLS) { LM_ERR("connection found but is not TLS (bug in config)\n"); tcp_conn_release(c, 0); return 0; } return c; } static inline SSL* get_ssl(struct tcp_connection* c) { if (!c || !c->extra_data) { LM_ERR("failed to extract SSL data from TLS connection\n"); return 0; } return c->extra_data; } static inline int get_cert(X509** cert, struct tcp_connection** c, struct sip_msg* msg, int my) { SSL* ssl; *cert = 0; *c = get_cur_connection(msg); if (!(*c)) { LM_INFO("TLS connection not found\n"); return -1; } ssl = get_ssl(*c); if (!ssl) goto err; *cert = my ? SSL_get_certificate(ssl) : SSL_get_peer_certificate(ssl); if (!*cert) { LM_ERR("failed to get certificate from SSL structure\n"); goto err; } return 0; err: tcp_conn_release(*c,0); return -1; } int tlsops_cipher(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str cipher; static char buf[1024]; struct tcp_connection* c; SSL* ssl; c = get_cur_connection(msg); if (!c) { LM_INFO("TLS connection not found in select_cipher\n"); goto err; } ssl = get_ssl(c); if (!ssl) goto err; cipher.s = (char*)SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)); cipher.len = cipher.s ? strlen(cipher.s) : 0; if (cipher.len >= 1024) { LM_ERR("cipher name too long\n"); goto err; } memcpy(buf, cipher.s, cipher.len); res->rs.s = buf; res->rs.len = cipher.len; res->flags = PV_VAL_STR; tcp_conn_release(c,0); return 0; err: if (c) tcp_conn_release(c,0); return pv_get_null(msg, param, res); } int tlsops_bits(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str bits; int b; static char buf[1024]; struct tcp_connection* c; SSL* ssl; c = get_cur_connection(msg); if (!c) { LM_INFO("TLS connection not found in select_bits\n"); goto err; } ssl = get_ssl(c); if (!ssl) goto err; b = SSL_CIPHER_get_bits(SSL_get_current_cipher(ssl), 0); bits.s = int2str(b, &bits.len); if (bits.len >= 1024) { LM_ERR("bits string too long\n"); goto err; } memcpy(buf, bits.s, bits.len); res->rs.s = buf; res->rs.len = bits.len; res->ri = b; res->flags = PV_VAL_STR | PV_VAL_INT; tcp_conn_release(c,0); return 0; err: if (c) tcp_conn_release(c,0); return pv_get_null(msg, param, res); } int tlsops_version(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str version; static char buf[1024]; struct tcp_connection* c; SSL* ssl; c = get_cur_connection(msg); if (!c) { LM_INFO("TLS connection not found in select_version\n"); goto err; } ssl = get_ssl(c); if (!ssl) goto err; version.s = (char*)SSL_get_version(ssl); version.len = version.s ? strlen(version.s) : 0; if (version.len >= 1024) { LM_ERR("version string too long\n"); goto err; } memcpy(buf, version.s, version.len); res->rs.s = buf; res->rs.len = version.len; res->flags = PV_VAL_STR; tcp_conn_release(c,0); return 0; err: if (c) tcp_conn_release(c,0); return pv_get_null(msg, param, res); } int tlsops_desc(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[128]; struct tcp_connection* c; SSL* ssl; c = get_cur_connection(msg); if (!c) { LM_INFO("TLS connection not found in select_desc\n"); goto err; } ssl = get_ssl(c); if (!ssl) goto err; buf[0] = '\0'; SSL_CIPHER_description(SSL_get_current_cipher(ssl), buf, 128); res->rs.s = buf; res->rs.len = strlen(buf); res->flags = PV_VAL_STR; tcp_conn_release(c,0); return 0; err: if (c) tcp_conn_release(c,0); return pv_get_null(msg, param, res); } int tlsops_cert_version(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[INT2STR_MAX_LEN]; X509* cert; struct tcp_connection* c; char* version; int my; if (param->pvn.u.isname.name.n & CERT_PEER) { my = 0; } else if (param->pvn.u.isname.name.n & CERT_LOCAL) { my = 1; } else { LM_CRIT("bug in call to tlsops_cert_version\n"); return pv_get_null(msg, param, res); } if (get_cert(&cert, &c, msg, my) < 0) return -1; version = int2str(X509_get_version(cert), &res->rs.len); memcpy(buf, version, res->rs.len); res->rs.s = buf; res->flags = PV_VAL_STR; if (!my) X509_free(cert); tcp_conn_release(c,0); return 0; } /* * Check whether peer certificate exists and verify the result * of certificate verification */ int tlsops_check_cert(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static str succ = str_init("1"); static str fail = str_init("0"); int err; struct tcp_connection* c; SSL* ssl; X509* cert = 0; switch (param->pvn.u.isname.name.n) { case CERT_VERIFIED: err = X509_V_OK; break; case CERT_REVOKED: err = X509_V_ERR_CERT_REVOKED; break; case CERT_EXPIRED: err = X509_V_ERR_CERT_HAS_EXPIRED; break; case CERT_SELFSIGNED: err = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; break; default: LM_CRIT("unexpected parameter value \"%d\"\n", param->pvn.u.isname.name.n); return pv_get_null(msg, param, res); } c = get_cur_connection(msg); if (!c) return -1; ssl = get_ssl(c); if (!ssl) goto err; if ((cert = SSL_get_peer_certificate(ssl)) && SSL_get_verify_result(ssl) == err) { res->rs.s = succ.s; res->rs.len = succ.len; res->ri = 1; } else { res->rs.s = fail.s; res->rs.len = fail.len; res->ri = 0; } res->flags = PV_VAL_STR | PV_VAL_INT; if (cert) X509_free(cert); tcp_conn_release(c,0); return 0; err: if (cert) X509_free(cert); if (c) tcp_conn_release(c,0); return pv_get_null(msg, param, res); } int tlsops_validity(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[1024]; X509* cert; struct tcp_connection* c; BUF_MEM* p; BIO* mem = 0; ASN1_TIME* date; int my = 0; if (get_cert(&cert, &c, msg, my) < 0) return -1; switch (param->pvn.u.isname.name.n) { case CERT_NOTBEFORE: date = X509_get_notBefore(cert); break; case CERT_NOTAFTER: date = X509_get_notAfter(cert); break; default: LM_CRIT("unexpected parameter value \"%d\"\n", param->pvn.u.isname.name.n); goto err; } mem = BIO_new(BIO_s_mem()); if (!mem) { LM_ERR("failed to create memory BIO\n"); goto err; } if (!ASN1_TIME_print(mem, date)) { LM_ERR("failed to print certificate date/time\n"); goto err; } BIO_get_mem_ptr(mem, &p); if (p->length >= 1024) { LM_ERR("Date/time too long\n"); goto err; } memcpy(buf, p->data, p->length); res->rs.s = buf; res->rs.len = p->length; res->flags = PV_VAL_STR ; BIO_free(mem); if (!my) X509_free(cert); tcp_conn_release(c,0); return 0; err: if (mem) BIO_free(mem); if (!my) X509_free(cert); tcp_conn_release(c,0); return pv_get_null(msg, param, res); } int tlsops_sn(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[INT2STR_MAX_LEN]; X509* cert; struct tcp_connection* c; int my, serial; char* sn; if (param->pvn.u.isname.name.n & CERT_PEER) { my = 0; } else if (param->pvn.u.isname.name.n & CERT_LOCAL) { my = 1; } else { LM_CRIT("could not determine certificate\n"); return pv_get_null(msg, param, res); } if (get_cert(&cert, &c, msg, my) < 0) return pv_get_null(msg, param, res); serial = ASN1_INTEGER_get(X509_get_serialNumber(cert)); sn = int2str( serial, &res->rs.len); memcpy(buf, sn, res->rs.len); res->rs.s = buf; res->ri = serial; res->flags = PV_VAL_STR | PV_VAL_INT; if (!my) X509_free(cert); tcp_conn_release(c,0); return 0; } int tlsops_comp(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[1024]; X509* cert; struct tcp_connection* c; X509_NAME* name; X509_NAME_ENTRY* e; ASN1_STRING* asn1; int nid = NID_commonName, index, my = 0, issuer = 0, ind_local; char* elem; str text; UNUSED(elem); text.s = 0; /* copy callback value as we modify it */ ind_local = param->pvn.u.isname.name.n; LM_DBG("ind_local = %x", ind_local); if (ind_local & CERT_PEER) { my = 0; ind_local = ind_local ^ CERT_PEER; } else if (ind_local & CERT_LOCAL) { my = 1; ind_local = ind_local ^ CERT_LOCAL; } else { LM_CRIT("could not determine certificate\n"); return pv_get_null(msg, param, res); } if (ind_local & CERT_SUBJECT) { issuer = 0; ind_local = ind_local ^ CERT_SUBJECT; } else if (ind_local & CERT_ISSUER) { issuer = 1; ind_local = ind_local ^ CERT_ISSUER; } else { LM_CRIT("could not determine subject or issuer\n"); return pv_get_null(msg, param, res); } switch(ind_local) { case COMP_CN: nid = NID_commonName; break; case COMP_O: nid = NID_organizationName; break; case COMP_OU: nid = NID_organizationalUnitName; break; case COMP_C: nid = NID_countryName; break; case COMP_ST: nid = NID_stateOrProvinceName; break; case COMP_L: nid = NID_localityName; break; case COMP_SUBJECT_SERIAL: nid = NID_serialNumber;break; default: nid = NID_undef; } if (get_cert(&cert, &c, msg, my) < 0) return -1; name = issuer ? X509_get_issuer_name(cert) : X509_get_subject_name(cert); if (!name) { LM_ERR("cannot extract subject or issuer name from peer" " certificate\n"); goto err; } if (nid == NID_undef) { /* dump the whole cert info into buf */ X509_NAME_oneline(name, buf, sizeof(buf)); res->rs.s = buf; res->rs.len = strlen(buf); res->flags = PV_VAL_STR; } else { index = X509_NAME_get_index_by_NID(name, nid, -1); if (index == -1) { switch(ind_local) { case COMP_CN: elem = "CommonName"; break; case COMP_O: elem = "OrganizationName"; break; case COMP_OU: elem = "OrganizationalUnitUname"; break; case COMP_C: elem = "CountryName"; break; case COMP_ST: elem = "StateOrProvinceName"; break; case COMP_L: elem = "LocalityName"; break; case COMP_SUBJECT_SERIAL: elem = "SubjectSerialNumber";break; default: elem = "Unknown"; break; } LM_DBG("element %s not found in " "certificate subject/issuer\n", elem); goto err; } e = X509_NAME_get_entry(name, index); asn1 = X509_NAME_ENTRY_get_data(e); text.len = ASN1_STRING_to_UTF8((unsigned char**)(void*)&text.s, asn1); if (text.len < 0 || text.len >= 1024) { LM_ERR("failed to convert ASN1 string\n"); goto err; } memcpy(buf, text.s, text.len); res->rs.s = buf; res->rs.len = text.len; res->flags = PV_VAL_STR; OPENSSL_free(text.s); } if (!my) X509_free(cert); tcp_conn_release(c,0); return 0; err: if (text.s) OPENSSL_free(text.s); if (!my) X509_free(cert); tcp_conn_release(c,0); return pv_get_null(msg, param, res); } int tlsops_alt(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[1024]; int type = GEN_URI, my = 0, n, found = 0, ind_local; STACK_OF(GENERAL_NAME)* names = 0; GENERAL_NAME* nm; X509* cert; struct tcp_connection* c; str text; struct ip_addr ip; ind_local = param->pvn.u.isname.name.n; if (ind_local & CERT_PEER) { my = 0; ind_local = ind_local ^ CERT_PEER; } else if (ind_local & CERT_LOCAL) { my = 1; ind_local = ind_local ^ CERT_LOCAL; } else { LM_CRIT("could not determine certificate\n"); return pv_get_null(msg, param, res); } switch(ind_local) { case COMP_E: type = GEN_EMAIL; break; case COMP_HOST: type = GEN_DNS; break; case COMP_URI: type = GEN_URI; break; case COMP_IP: type = GEN_IPADD; break; default: LM_CRIT("ind_local=%d\n", ind_local); return pv_get_null(msg, param, res); } if (get_cert(&cert, &c, msg, my) < 0) return -1; names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (!names) { LM_ERR("cannot get certificate alternative subject\n"); goto err; } for (n = 0; n < sk_GENERAL_NAME_num(names); n++) { nm = sk_GENERAL_NAME_value(names, n); if (nm->type != type) continue; switch(type) { case GEN_EMAIL: case GEN_DNS: case GEN_URI: text.s = (char*)nm->d.ia5->data; text.len = nm->d.ia5->length; if (text.len >= 1024) { LM_ERR("alternative subject text too long\n"); goto err; } memcpy(buf, text.s, text.len); res->rs.s = buf; res->rs.len = text.len; res->flags = PV_VAL_STR; found = 1; break; case GEN_IPADD: ip.len = nm->d.iPAddress->length; ip.af = (ip.len == 16) ? AF_INET6 : AF_INET; memcpy(ip.u.addr, nm->d.iPAddress->data, ip.len); text.s = ip_addr2a(&ip); text.len = strlen(text.s); memcpy(buf, text.s, text.len); res->rs.s = buf; res->rs.len = text.len; res->flags = PV_VAL_STR; found = 1; break; } break; } if (!found) goto err; if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); if (!my) X509_free(cert); tcp_conn_release(c,0); return 0; err: if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); if (!my) X509_free(cert); tcp_conn_release(c,0); return pv_get_null(msg, param, res); } opensips-2.2.2/modules/tls_mgm/tls_select.h000066400000000000000000000073721300170765700210200ustar00rootroot00000000000000/* * Copyright (C) 2006 enum.at * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _TLS_SELECT_H_ #define _TLS_SELECT_H_ #include #include "../../parser/msg_parser.h" #include "../../pvar.h" enum { CERT_LOCAL = 1<<0, /* Select local certificate */ CERT_PEER = 1<<1, /* Select peer certificate */ CERT_SUBJECT = 1<<2, /* Select subject part of certificate */ CERT_ISSUER = 1<<3, /* Select issuer part of certificate */ CERT_VERIFIED = 1<<4, /* Test for verified certificate */ CERT_REVOKED = 1<<5, /* Test for revoked certificate */ CERT_EXPIRED = 1<<6, /* Expiration certificate test */ CERT_SELFSIGNED = 1<<7, /* self-signed certificate test */ CERT_NOTBEFORE = 1<<8, /* Select validity end from certificate */ CERT_NOTAFTER = 1<<9, /* Select validity start from certificate */ COMP_CN = 1<<10, /* Common name */ COMP_O = 1<<11, /* Organization name */ COMP_OU = 1<<12, /* Organization unit */ COMP_C = 1<<13, /* Country name */ COMP_ST = 1<<14, /* State */ COMP_L = 1<<15, /* Locality/town */ COMP_HOST = 1<<16, /* hostname from subject/alternative */ COMP_URI = 1<<17, /* URI from subject/alternative */ COMP_E = 1<<18, /* Email address */ COMP_IP = 1<<19, /* IP from subject/alternative */ COMP_SUBJECT_SERIAL = 1<<20 /*Serial name from Subject*/ }; typedef int select_t; int tlsops_cipher(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int tlsops_bits(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int tlsops_version(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int tlsops_desc(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int tlsops_cert_version(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); /* * Check whether peer certificate exists and verify the result * of certificate verification */ int tlsops_check_cert(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int tlsops_validity(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int tlsops_sn(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int tlsops_comp(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int tlsops_alt(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); #endif opensips-2.2.2/modules/tm/000077500000000000000000000000001300170765700154535ustar00rootroot00000000000000opensips-2.2.2/modules/tm/Makefile000066400000000000000000000003151300170765700171120ustar00rootroot00000000000000# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=tm.so LIBS= include ../../Makefile.modules opensips-2.2.2/modules/tm/README000066400000000000000000001211561300170765700163410ustar00rootroot00000000000000tm Module Bogdan-Andrei Iancu Jiri Kuthan FhG FOKUS Edited by Jiri Kuthan Edited by Bogdan-Andrei Iancu Edited by Ovidiu Sas Copyright © 2003 FhG FOKUS Copyright © 2005-2008 Voice Sistem SRL Revision History Revision $Revision: 8740 $ $Date$ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. Per-Branch flags 1.1.2. Timeout-Based Failover 1.1.3. DNS Failover 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. fr_timeout (integer) 1.3.2. fr_inv_timeout (integer) 1.3.3. wt_timer (integer) 1.3.4. delete_timer (integer) 1.3.5. T1_timer (integer) 1.3.6. T2_timer (integer) 1.3.7. ruri_matching (integer) 1.3.8. via1_matching (integer) 1.3.9. unix_tx_timeout (integer) 1.3.10. restart_fr_on_each_reply (integer) 1.3.11. tw_append (string) 1.3.12. pass_provisional_replies (integer) 1.3.13. syn_branch (integer) 1.3.14. onreply_avp_mode (integer) 1.3.15. disable_6xx_block (integer) 1.3.16. enable_stats (integer) 1.3.17. minor_branch_flag (string/integer) 1.3.18. timer_partitions (integer) 1.3.19. auto_100trying (integer) 1.4. Exported Functions 1.4.1. t_relay([flags]) 1.4.2. t_relay(proto:host:port,[flags]) 1.4.3. t_reply(code, reason_phrase) 1.4.4. t_reply_with_body(code, reason_phrase, body) 1.4.5. t_newtran() 1.4.6. t_check_trans() 1.4.7. t_check_status(re) 1.4.8. t_local_replied(reply) 1.4.9. t_was_cancelled() 1.4.10. t_cancel_branch([flags]) 1.4.11. t_new_request(method,RURI,from,to[,body[,ctx] ]) 1.4.12. t_on_failure(failure_route) 1.4.13. t_on_reply(reply_route) 1.4.14. t_on_branch(branch_route) 1.4.15. t_add_hdrs("sip_hdrs") 1.4.16. t_replicate(URI,[flags]) 1.4.17. t_write_req(info,fifo) t_write_unix(info,sock) 1.4.18. t_flush_flags() 1.5. Exported pseudo-variables 1.5.1. $T_branch_idx 1.5.2. $T_reply_code 1.5.3. $T_fr_timeout 1.5.4. $T_fr_inv_timeout 1.5.5. $T_ruri 1.5.6. $bavp(name) 1.6. Exported MI Functions 1.6.1. t_uac_dlg 1.6.2. t_uac_cancel 1.6.3. t_hash 1.6.4. t_reply 1.7. Exported statistics 1.7.1. received_replies 1.7.2. relayed_replies 1.7.3. local_replies 1.7.4. UAS_transactions 1.7.5. UAC_transactions 1.7.6. 2xx_transactions 1.7.7. 3xx_transactions 1.7.8. 4xx_transactions 1.7.9. 5xx_transactions 1.7.10. 6xx_transactions 1.7.11. inuse_transactions 2. Developer Guide 2.1. Functions 2.1.1. load_tm(*import_structure) 3. Frequently Asked Questions List of Examples 1.1. Set fr_timeout parameter 1.2. Set fr_inv_timeout parameter 1.3. Set wt_timer parameter 1.4. Set delete_timer parameter 1.5. Set T1_timer parameter 1.6. Set T2_timer parameter 1.7. Set ruri_matching parameter 1.8. Set via1_matching parameter 1.9. Set unix_tx_timeout parameter 1.10. Set restart_fr_on_each_reply parameter 1.11. Set tw_append parameter 1.12. Set pass_provisional_replies parameter 1.13. Set syn_branch parameter 1.14. Set onreply_avp_mode parameter 1.15. Set disable_6xx_block parameter 1.16. Set enable_stats parameter 1.17. Set minor_branch_flag parameter 1.18. Set timer_partitions parameter 1.19. Set auto_100trying parameter 1.20. t_relay usage 1.21. t_relay usage 1.22. t_reply usage 1.23. t_reply_with_body usage 1.24. t_newtran usage 1.25. t_check_trans usage 1.26. t_check_status usage 1.27. t_local_replied usage 1.28. t_was_cancelled usage 1.29. t_cancel_branch usage 1.30. t_new_request usage 1.31. t_on_failure usage 1.32. t_on_reply usage 1.33. t_on_branch usage 1.34. t_add_hdrs usage 1.35. t_replicate usage 1.36. t_write_req/unix usage 1.37. t_flush_flags usage Chapter 1. Admin Guide 1.1. Overview TM module enables stateful processing of SIP transactions. The main use of stateful logic, which is costly in terms of memory and CPU, is some services inherently need state. For example, transaction-based accounting (module acc) needs to process transaction state as opposed to individual messages, and any kinds of forking must be implemented statefully. Other use of stateful processing is it trading CPU caused by retransmission processing for memory. That makes however only sense if CPU consumption per request is huge. For example, if you want to avoid costly DNS resolution for every retransmission of a request to an unresolvable destination, use stateful mode. Then, only the initial message burdens server by DNS queries, subsequent retransmissions will be dropped and will not result in more processes blocked by DNS resolution. The price is more memory consumption and higher processing latency. From user's perspective, the major function is t_relay(). It setup transaction state, absorb retransmissions from upstream, generate downstream retransmissions and correlate replies to requests. In general, if TM is used, it copies clones of received SIP messages in shared memory. That costs the memory and also CPU time (memcpys, lookups, shmem locks, etc.) Note that non-TM functions operate over the received message in private memory, that means that any core operations will have no effect on statefully processed messages after creating the transactional state. For example, calling record_route after t_relay is pretty useless, as the RR is added to privately held message whereas its TM clone is being forwarded. TM is quite big and uneasy to program--lot of mutexes, shared memory access, malloc and free, timers--you really need to be careful when you do anything. To simplify TM programming, there is the instrument of callbacks. The callback mechanisms allow programmers to register their functions to specific event. See t_hooks.h for a list of possible events. Other things programmers may want to know is UAC--it is a very simplistic code which allows you to generate your own transactions. Particularly useful for things like NOTIFYs or IM gateways. The UAC takes care of all the transaction machinery: retransmissions , FR timeouts, forking, etc. See t_uac prototype in uac.h for more details. Who wants to see the transaction result may register for a callback. 1.1.1. Per-Branch flags First what is the idea with the branch concept: branch route is a route to be execute separately for each branch before being sent out - changes in that route should reflect only on that branch. There are several types of flags in OpenSIPS : * message/transaction flags - they are visible everywhere in the transaction (in all routes and in all sequential replies/request). * branch flags - flags that are visible only from a specific branch - in all replies and routes connected to this branch. * script flags - flags that exist only during script execution. They are not store anywhere and are lost once the top level route was left. For example: I have a call parallel forking to GW and to a user. And I would like to know from which branch I will get the final negative reply (if so). I will set a branch route before relaying the calls (with the 2 branches). The branch route will be separately executed for each branch; in the branch going to GW (I can identified it by looking to RURI), I will set a branch flag. This flag will appear only in the onreply route run for replied from GW. It will be also be visible in failure route if the final elected reply belongs to the GW branch. This flags will not be visible in the other branch (in routes executing replies from the other branch). For how to define branch flags and use via script, see Section 1.4.14, “ t_on_branch(branch_route) †and the setbflag(), resetbflag() and isbflagset() script functions. Also, modules may set branch flags before transaction creation (for the moment this feature is not available in script). The REGISTRAR module was the first to use this type of flags. The NAT flag is pushed in branch flags instead in message flags 1.1.2. Timeout-Based Failover Timeouts can be used to trigger failover behavior. E.g. if we send a call to a gateway and the gateway does not send a provisional response within 3 seconds, we want to cancel this call and send the call to another gateway. Another example is to ring a SIP client only for 30 seconds and then redirect the call to the voicemail. The transaction module exports two types of timeouts: * fr_timeout - used when no response was received yet. If there is no response after fr_timeout seconds, the timer triggers (and failure route will be executed if t_on_failure() was called). For INVITE transactions, if a provisional response was received, the timeout is reset to fr_inv_timeout seconds and RT_T2 for all other transactions. Once a final response is received, the transaction has finished. * fr_inv_timeout - this timeout starts counting down once a provisional response was received for an INVITE transaction. For example: You want to have failover if there is no provisional response after 3 seconds, but you want to ring for 60 seconds. Thus, set the fr_timeout to 3 and fr_inv_timeout to 60. 1.1.3. DNS Failover DNS based failover can be use when relaying stateful requests. According to RFC 3263, DNS failover should be done on transport level or transaction level. TM module supports them both. Failover at transport level may be triggered by a failure of sending out the request message. A failure occurs if the corresponding interface was found for sending the request, if the TCP connection was refused or if a generic internal error happened during send. There is no ICMP error report support. Failover at transaction level may be triggered when the transaction completed either with a 503 reply, either with a timeout without any received reply. In such a case, automatically, a new branch will be forked if any other destination IPs can be used to deliver the requests. The new branch will be a clone of the winning branch. The set of destinations IPs is step-by-step build (on demand) based on the NAPTR, SRV and A records available for the destination domain. DNS-based failover is by default applied excepting when this failover is globally disabled (see the core parameter disable_dns_failover) or when the relay flag (per transaction) is set (see the t_relay() function). 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * No dependencies on other OpenSIPS modules. 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. fr_timeout (integer) Timeout which is triggered if no final reply for a request or ACK for a negative INVITE reply arrives (in seconds). Default value is 30 seconds. Example 1.1. Set fr_timeout parameter ... modparam("tm", "fr_timeout", 10) ... 1.3.2. fr_inv_timeout (integer) Timeout which is triggered if no final reply for an INVITE arrives after a provisional message was received (in seconds). This timeout starts counting down once the first provisional response is received. Thus, fast failover (no 100 trying from gateway) can be achieved by setting fr_timeout to low values. See example below. Default value is 120 seconds. Example 1.2. Set fr_inv_timeout parameter ... modparam("tm", "fr_inv_timeout", 200) ... 1.3.3. wt_timer (integer) Time for which a transaction stays in memory to absorb delayed messages after it completed; also, when this timer hits, retransmission of local cancels is stopped (a puristic but complex behavior would be not to enter wait state until local branches are finished by a final reply or FR timer--we simplified). For non-INVITE transaction this timer relates to timer J of RFC 3261 section 17.2.2. According to the RFC this timer should be 64*T1 (= 32 seconds). But this would increase memory usage as the transactions are kept in memory very long. Default value is 5 seconds. Example 1.3. Set wt_timer parameter ... modparam("tm", "wt_timer", 10) ... 1.3.4. delete_timer (integer) Time after which a to-be-deleted transaction currently ref-ed by a process will be tried to be deleted again. Default value is 2 seconds. Example 1.4. Set delete_timer parameter ... modparam("tm", "delete_timer", 5) ... 1.3.5. T1_timer (integer) Retransmission T1 period, in milliseconds. Default value is 500 milliseconds. Example 1.5. Set T1_timer parameter ... modparam("tm", "T1_timer", 700) ... 1.3.6. T2_timer (integer) Maximum retransmission period, in milliseconds. Default value is 4000 milliseconds. Example 1.6. Set T2_timer parameter ... modparam("tm", "T2_timer", 8000) ... 1.3.7. ruri_matching (integer) Should be request-uri matching used as a part of pre-3261 transaction matching as the standard wants us to do so? Turn only off for better interaction with devices that are broken and send different r-uri in CANCEL/ACK than in original INVITE. Default value is 1 (true). Example 1.7. Set ruri_matching parameter ... modparam("tm", "ruri_matching", 0) ... 1.3.8. via1_matching (integer) Should be top most VIA matching used as a part of pre-3261 transaction matching as the standard wants us to do so? Turn only off for better interaction with devices that are broken and send different top most VIA in CANCEL/ACK than in original INVITE. Default value is 1 (true). Example 1.8. Set via1_matching parameter ... modparam("tm", "via1_matching", 0) ... 1.3.9. unix_tx_timeout (integer) Send timeout to be used by function which use UNIX sockets (as t_write_unix). Default value is 2 seconds. Example 1.9. Set unix_tx_timeout parameter ... modparam("tm", "unix_tx_timeout", 5) ... 1.3.10. restart_fr_on_each_reply (integer) If true (non null value), the final response timer will be re-triggered for each received provisional reply. In this case, final response timeout may occur after a time longer than fr_inv_timeout (if UAS keeps sending provisional replies) Default value is 1 (true). Example 1.10. Set restart_fr_on_each_reply parameter ... modparam("tm", "restart_fr_on_each_reply", 0) ... 1.3.11. tw_append (string) List of additional information to be appended by t_write_req and t_write_unix functions. Default value is null string. Syntax of the parameter is: * tw_append = append_name':' element (';'element)* * element = ( [name '='] pseudo_variable) The full list of supported pseudo-variables in OpenSIPS is availabe at: http://opensips.org/docs/pseudo-variables-1.1.x.html Each element will be appended per line in “name: value†format. Element “$rb (message body)†is the only one which does not accept name; the body it will be printed all the time at the end, disregarding its position in the definition string. Example 1.11. Set tw_append parameter ... modparam("tm", "tw_append", "test: ua=$hdr(User-Agent) ;avp=$avp(avp);$rb;time=$Ts") ... 1.3.12. pass_provisional_replies (integer) Enable/disable passing of provisional replies to FIFO applications. Default value is 0. Example 1.12. Set pass_provisional_replies parameter ... modparam("tm", "pass_provisional_replies", 1) ... 1.3.13. syn_branch (integer) Enable/disable the usage of stateful synonym branch IDs in the generated Via headers. They are faster but not reboot-safe. Default value is 1 (use synonym branches). Example 1.13. Set syn_branch parameter ... modparam("tm", "syn_branch", 0) ... 1.3.14. onreply_avp_mode (integer) Describes how the AVPs should be handled in reply route: * 0 - the AVPs will be per message only; they will not interfere with the AVPS stored in transaction; initially there will be an empty list and at the end of the route, all AVPs that were created will be discarded. * 1 - the AVPs will be the transaction AVPs; initially the transaction AVPs will be visible; at the end of the route, the list will attached back to transaction (with all the changes) In mode 1, you can see the AVPs you set in request route, branch route or failure route. The side efect is performance as more locking is required in order to keep the AVP's list integrity. Default value is 0. Example 1.14. Set onreply_avp_mode parameter ... modparam("tm", "onreply_avp_mode", 1) ... 1.3.15. disable_6xx_block (integer) Tells how the 6xx replies should be internally handled: * 0 - the 6xx replies will block any further serial forking (adding new branches). This is the RFC3261 behaviour. * 1 - the 6xx replies will be handled as any other negative reply - serial forking will be allowed. Logically, you need to break RFC3261 if you want to do redirects to announcement and voicemail services. Default value is 0. Example 1.15. Set disable_6xx_block parameter ... modparam("tm", "disable_6xx_block", 1) ... 1.3.16. enable_stats (integer) Enables statistics support in TM module - If enabled, the TM module will internally keep several statistics and export them via the MI - Management Interface. Default value is 1 (enabled). Example 1.16. Set enable_stats parameter ... modparam("tm", "enable_stats", 0) ... 1.3.17. minor_branch_flag (string/integer) A branch flag index to be used in script to mark the minor branches ( before t_relay() ). A minor branch is a branch OpenSIPS will not wait to complete during parallel forking. So, if the rest of the branches are negativly replied OpenSIPS will not wait for a final answer from the minor branch, but it will simply cancel it. Main applicability of minor branch is to fork a branch to a media server for injecting (via 183 Early Media) some pre-call media - of course, this branch will be transparanent for the rest of the call branches (from branch selection point of view). Default value is none (disabled). Example 1.17. Set minor_branch_flag parameter ... modparam("tm", "minor_branch_flag", "MINOR_BFLAG") ... 1.3.18. timer_partitions (integer) The number of partitions for the internal TM timers (retransmissions, delete, wait, etc). Partitioning the timers increase the throughput under heavly load by handling timer events in parallel, rather than all serial. Recomanded range for timer partitions is max 16 (soft limit). Default value is 1 (disabled). Example 1.18. Set timer_partitions parameter ... # Enable two timer partitions modparam("tm", "timer_partitions", 2) ... 1.3.19. auto_100trying (integer) This parameter controls if the TM module should automatically generate an 100 Trying stateful reply when an INVITE transaction is created. You may want to disable this behavior if you want to control from script level when the 100 Trying is to be sent out. Default value is 1 (enabled). Example 1.19. Set auto_100trying parameter ... # Disable automatic 100 Trying modparam("tm", "auto_100trying", 0) ... 1.4. Exported Functions 1.4.1. t_relay([flags]) Relay a message statefully to destination indicated in current URI. (If the original URI was rewritten by UsrLoc, RR, strip/prefix, etc., the new URI will be taken). Returns a negative value on failure--you may still want to send a negative reply upstream statelessly not to leave upstream UAC in lurch. The coresponding transaction may or may not be already created. If not yet created, the function will automatically create it. The function may take as parameter an optional set of flags for controlling the internal behaviour. The flags may be given in decimal or hexa format; supported flags are: * 0x01 - deprecated, not used any more * 0x02 - do not internally send a negative reply in case of forward failure (due internal error, bad RURI, bad message, etc). When a forward failure occurs, no SIP request is relayed and therefore no negative reply or timeout will show up on the failure_route (if one is set). It applies only when the transaction is created. By default one negative reply is sent. Useful if you want to implement a serial forking in case of failure. * 0x04 - disable the DNS failover for the transaction. Only first IP will be used. It disable the failover both at transaport and transaction level. * 0x08 - If the request is a CANCEL, trust and pass further the Reason header from the received CANCEL - shortly, will propagate the Reason header. In case of error, the function returns the following codes: * -1 - generic internal error * -2 - bad message (parsing errors) * -3 - no destination available (no branches were added or request already cancelled) * -4 - bad destination (unresolvable address) * -5 - destination filtered (black listed) * -6 - generic send failed This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.20. t_relay usage ... if (!t_relay()) { sl_reply_error(); exit; } ... 1.4.2. t_relay(proto:host:port,[flags]) Relay a message statefully to a fixed destination. The destination is specified as “[proto:]host[:port]â€. If a destination URI “$du†for this message was set before the function is called then this value will be used as the destination instead of the function parameter. The function may take as parameter an optional set of flags for controlling the internal behaviour - for details see the above “t_relay([flags])†function. This functions can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.21. t_relay usage ... t_relay("tcp:192.168.1.10:5060"); t_relay("mydomain.com:5070","0x1"); t_relay("udp:mydomain.com"); ... 1.4.3. t_reply(code, reason_phrase) Sends a stateful SIP reply to the currently processed requests. Note that if the transaction was not created yet, it will automatically created by internally using the t_newtran function. Meaning of the parameters is as follows: * code - Reply code number. * reason_phrase - Reason string. Both parameters accept any kind of pseudo-variables. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.22. t_reply usage ... t_reply("404", "Use $rU not found"); ... 1.4.4. t_reply_with_body(code, reason_phrase, body) Sends a stateful SIP reply with a body to the currently processed requests. Note that if the transaction was not created yet, it will automatically created by internally using the t_newtran function. Meaning of the parameters is as follows: * code - Reply code number. * reason_phrase - Reason string. * body - Reply body. All parameters accept any kind of pseudo-variables. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.23. t_reply_with_body usage ... if(is_method("INVITE")) { append_to_reply("Contact: $var(contact)\r\n" "Content-Type: application/sdp\r\n"); t_reply_with_body("200", "Ok", "$var(body)"); exit; } ... 1.4.5. t_newtran() Creates the SIP transaction for the currently processed SIP requests. Basically you switch to a stateful processing from this point furhter, with automatic detection of retransmission and auto sending of 100 Trying reply on INVITEs. This function can be used from REQUEST_ROUTE. Example 1.24. t_newtran usage ... t_newtran(); # 100 Trying is fired here xlog("doing my complicated routing logic"); .... t_relay(); # send the call further ... 1.4.6. t_check_trans() Returns true if the current request is associated to a transaction. The relationship between the request and transaction is defined as follows: * non-CANCEL/non-ACK requests - if the request belongs to a transaction (it's a retransmision), the function will do a standard processing of the retransmission and will break/stop the script. The function returns false if the request is not a retransmission. * CANCEL request - true if the cancelled INVITE transaction exists. * ACK request - true if the ACK is a local end-to-end ACK corresponding to an previous INVITE transaction. Note: To detect retransmissions using this function you have to make sure that the initial request has already created a transaction, e.g. by using t_relay(). If the processing of requests may take long time (e.g. DB lookups) and the retransmission arrives before t_relay() is called, you can use the t_newtran() function to manually create a transaction. This function can be used from REQUEST_ROUTE and BRANCH_ROUTE. Example 1.25. t_check_trans usage ... if ( is_method("CANCEL") ) { if ( t_check_trans() ) t_relay(); exit; } ... 1.4.7. t_check_status(re) Returns true if the regualr expression “re†match the reply code of the response message as follows: * in routing block - the code of the last sent reply. * in on_reply block - the code of the current received reply. * in on_failure block - the code of the selected negative final reply. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE . Example 1.26. t_check_status usage ... if (t_check_status("(487)|(408)")) { log("487 or 408 negative reply\n"); } ... 1.4.8. t_local_replied(reply) Returns true if all or last (depending of the parameter) reply(es) were local generated (and not received). Parameter may be “all†or “lastâ€. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. Example 1.27. t_local_replied usage ... if (t_local_replied("all")) { log ("no reply received\n"); } ... 1.4.9. t_was_cancelled() Retuns true if called for an INVITE transaction that was explicitly cancelled by UAC side via a CANCEL request. This function can be used from ONREPLY_ROUTE, FAILURE_ROUTE. Example 1.28. t_was_cancelled usage ... if (t_was_cancelled()) { log("transaction was cancelled by UAC\n"); } ... 1.4.10. t_cancel_branch([flags]) This function is to be call when a reply is received for cancelling a set of branches (see flags) of the current call. Meaning of the parameters is as follows: * flags - (optional) - set of flags (char based flags) to control what branches to be cancelled: + a - all - cancel all pending branches + o - others - cancel all the other pending branches except the current one + empty - current - cancel only the current branch This function can be used from ONREPLY_ROUTE. Example 1.29. t_cancel_branch usage onreply_route[3] { ... if (t_check_status("183")) { # no support for early media t_cancel_branch(); } ... } 1.4.11. t_new_request(method,RURI,from,to[,body[,ctx]]) This function generates and sends out a new SIP request (in a stateful way). The new request is completly unrelated to the currently processed SIP message. Meaning of the parameters is as follows (all do accept variables): * method - the SIP method * RURI - the SIP Request URI (the request will be sent out to this destination) * from - the SIP From hdr information as "[display ]URI" * from - the SIP To hdr information as "[display ]URI" * body - (optional) the SIP body content starting with the content type string: "conten_type body" * ctx - a context string that will be added to the new transaction as an AVP with name "uac_ctx" (it may be visible in local route) Example 1.30. t_new_request usage ... # send a MESSAGE request t_new_request("MESSAGE","sip:alice@192.168.2.2","BOB sip:userB@m ydomain.net","ALICE sip:userA@mydomain.net","text/plain Hello Alice!")) { ... 1.4.12. t_on_failure(failure_route) Sets reply routing block, to which control is passed after a transaction completed with a negative result but before sending a final reply. In the referred block, you can either start a new branch (good for services such as forward_on_no_reply) or send a final reply on your own (good for example for message silo, which received a negative reply from upstream and wants to tell upstream “202 I will take care of itâ€). As not all functions are available from failure route, please check the documentation for each function to see the permissions. Any other commands may result in unpredictable behavior and possible server failure. Only one failure_route can be armed for a request. If you use many times t_on_failure(), only the last one has effect. Note that whenever failure_route is entered, RURI is set to value of the winning branch. Meaning of the parameters is as follows: * failure_route - Reply route block to be called. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE. Example 1.31. t_on_failure usage ... route { t_on_failure("1"); t_relay(); } failure_route[1] { seturi("sip:user@voicemail"); t_relay(); } ... 1.4.13. t_on_reply(reply_route) Sets reply routing block, to which control is passed each time a reply (provisional or final) for the transaction is received. The route is not called for local generated replies! In the referred block, you can inspect the reply and perform text operations on it. As not all functions are available from this type of route, please check the documentation for each function to see the permissions. Any other commands may result in unpredictable behavior and possible server failure. If called from branch route, the reply route will be set only for the current branch - that's it, it will be called only for relies belonging to that particular branch. Of course, from branch route, you can set different reply routes for each branch. When called from a non-branc route, the reply route will be globally set for tha current transaction - it will be called for all replies belonging to that transaction. NOTE that only one> onreply_route can be armed for a transaction. If you use many times t_on_reply(), only the last one has effect. If the processed reply is provisionla reply (1xx code), by calling the drop() function (exported by core), the execution of the route will end and the reply will not be forwarded further. Meaning of the parameters is as follows: * reply_route - Reply route block to be called. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE. Example 1.32. t_on_reply usage ... route { seturi("sip:bob@opensips.org"); # first branch append_branch("sip:alice@opensips.org"); # second branch t_on_reply("global"); # the "global" reply route # is set the whole transaction t_on_branch("1"); t_relay(); } branch_route[1] { if ($rU=="alice") t_on_reply("alice"); # the "alice" reply route # is set only for second branch } onreply_route[alice] { xlog("received reply from alice\n"); } onreply_route[global] { if (t_check_status("1[0-9][0-9]")) { setflag(LOG_FLAG); log("provisional reply received\n"); if (t_check_status("183")) drop; } } ... 1.4.14. t_on_branch(branch_route) Sets a branch route to be execute separately for each branch of the transaction before being sent out - changes in that route should reflect only on that branch. As not all functions are available from this type of route, please check the documentation for each function to see the permissions. Any other commands may result in unpredictable behavior and possible server failure. Only one branch_route can be armed for a request. If you use many time t_on_branch(), only the last one has effect. By calling the drop() function (exported by core), the execution of the branch route will end and the branch will not be forwarded further. Meaning of the parameters is as follows: * branch_route - Branch route block to be called. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE. Example 1.33. t_on_branch usage ... route { t_on_branch("1"); t_relay(); } branch_route[1] { if (uri=~"bad_uri") { xlog("dropping branch $ru \n"); drop; } if (uri=~"GW_uri") { append_rpid(); } } ... 1.4.15. t_add_hdrs("sip_hdrs") Attach a set of headers to the existing transaction - these headers will be appended to all requests related to the transaction (outgoing branches, local ACKS, CANCELs). Example 1.34. t_add_hdrs usage ... t_add_hdrs("X-origin: 1.1.1.1\r\n"); ... 1.4.16. t_replicate(URI,[flags]) Replicates a request to another destination. No information due the replicated request (like reply code) will be forwarded to the original SIP UAC. The destination is specified by a SIP URI. If multiple destinations are to be used, the additional SIP URIs have to be set as branches. The function may take as parameter an optional set of flags for controlling the internal behaviour - for description see the above “t_relay([flags])†function. Note that only 0x4 is applicable here. This functions can be used from REQUEST_ROUTE. Example 1.35. t_replicate usage ... t_replicate("sip:1.2.3.4:5060"); t_replicate("sip:1.2.3.4:5060;transport=tcp"); t_replicate("sip:1.2.3.4","0x4"); ... 1.4.17. t_write_req(info,fifo) t_write_unix(info,sock) Write via FIFO file or UNIX socket a lot of information regarding the request. Which information should be written may be control via the “tw_append†parameter. This functions can be used from REQUEST_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. Example 1.36. t_write_req/unix usage ... modparam("tm","tw_append","append1:Email=$avp(email);UA=$ua") modparam("tm","tw_append","append2:body=$rb") ... t_write_req("voicemail/append1","/tmp/appx_fifo"); ... t_write_unix("logger/append2","/var/run/logger.sock"); ... 1.4.18. t_flush_flags() Flush the flags from current request into the already created transaction. It make sense only in routing block if the transaction was created via t_newtran() and the flags have been altered since. This function can be used from REQUEST_ROUTE and BRANCH_ROUTE . Example 1.37. t_flush_flags usage ... t_flush_flags(); ... 1.5. Exported pseudo-variables Exported pseudo-variables are listed in the next sections. 1.5.1. $T_branch_idx $T_branch_idx - the index (starting with 0 for the first branch) of the currently proccessed branch. This index makes sense only in BRANCH and REPLY routes (where there is a concept of per branch processing). In all the other types of routes, the value of this index will be NULL. 1.5.2. $T_reply_code $T_reply_code - the code of the reply, as follows: in request_route will be the last stateful sent reply; in reply_route will be the current processed reply; in failure_route will be the negative winning reply. In case of no-reply or error, '0' value is returned. 1.5.3. $T_fr_timeout $T_fr_timeout (R/W) - the timeout for the final reply to the current transaction With each different request received, $T_fr_timeout will initially be equal to the fr_timeout parameter. "$T_fr_timeout = NULL;" will reset it to fr_timeout. 1.5.4. $T_fr_inv_timeout $T_fr_inv_timeout (R/W) - the timeout for the final reply to an INVITE request, after a 1XX reply was received With each different request received, $T_fr_inv_timeout will initially be equal to the fr_inv_timeout parameter. "$T_fr_inv_timeout = NULL;" will reset it to fr_inv_timeout. 1.5.5. $T_ruri $T_ruri - the ruri of the current branch; this information is taken from the transaction structure, so you can access this information for any sip message (request/reply) that has a transaction. 1.5.6. $bavp(name) $bavp(name) - a particular type of avp that can have different values for each branch. They can only be used in BRANCH, REPLY and FAILURE routes. Otherwise NULL value is returned. 1.6. Exported MI Functions 1.6.1. t_uac_dlg Generates and sends a local SIP request. Parameters: * method - request method * RURI - request SIP URI * NEXT HOP - next hop SIP URI (OBP); use “.†if no value. * socket - local socket to be used for sending the request; use “.†if no value. * headers - set of additional headers to be added to the request; at least “From†and “To†headers must be specify) * body - (optional, may not be present) request body (if present, requires the “Content-Type†and “Content-length†headers) 1.6.2. t_uac_cancel Generates and sends a CANCEL for an existing SIP request. Parameters: * callid - callid of the INVITE request to be cancelled. * cseq - cseq of the INVITE request to be cancelled. 1.6.3. t_hash Gets information about the load of TM internal hash table. Parameters: * none 1.6.4. t_reply Generates and sends a reply for an existing inbound SIP transaction. Parameters: * code - reply code * reason - reason phrase. * trans_id - transaction identifier (has the hash_entry:label format) * to_tag - To tag to be added to TO header * new_headers - extra headers to be appended to the reply; use a dot (“.â€) char only if there are no headers; * body - (optional, may not be present) reply body (if present, requires the “Content-Type†and “Content-length†headers) 1.7. Exported statistics Exported statistics are listed in the next sections. All statistics except “inuse_transactions†can be reset. 1.7.1. received_replies Total number of total replies received by TM module. 1.7.2. relayed_replies Total number of replies received and relayed by TM module. 1.7.3. local_replies Total number of replies local generated by TM module. 1.7.4. UAS_transactions Total number of transactions created by received requests. 1.7.5. UAC_transactions Total number of transactions created by local generated requests. 1.7.6. 2xx_transactions Total number of transactions completed with 2xx replies. 1.7.7. 3xx_transactions Total number of transactions completed with 3xx replies. 1.7.8. 4xx_transactions Total number of transactions completed with 4xx replies. 1.7.9. 5xx_transactions Total number of transactions completed with 5xx replies. 1.7.10. 6xx_transactions Total number of transactions completed with 6xx replies. 1.7.11. inuse_transactions Number of transactions existing in memory at current time. Chapter 2. Developer Guide 2.1. Functions 2.1.1. load_tm(*import_structure) For programmatic use only--import the TM API. See the cpl_c, acc or jabber modules to see how it works. Meaning of the parameters is as follows: * import_structure - Pointer to the import structure - see “struct tm_binds†in modules/tm/tm_load.h Chapter 3. Frequently Asked Questions 3.1. What happened with old cancel_call() function The function was replace (as functionality) by cancel_branch("a") - cancel all braches. 3.2. How can I report a bug? Please follow the guidelines provided at: https://github.com/OpenSIPS/opensips/issues. opensips-2.2.2/modules/tm/async.c000066400000000000000000000235251300170765700167430ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2014-10-16 created (bogdan) */ #include "../../dprint.h" #include "../../async.h" #include "../../context.h" #include "../../reactor_defs.h" #include "h_table.h" #include "t_lookup.h" #include "t_msgbuilder.h" typedef struct _async_ctx { /* the resume function to be called when data to read is available */ async_resume_module *resume_f; /* parameter registered to the resume function */ void *resume_param; /* the script route to be used to continue after the resume function */ int resume_route; /* the type of the route where the suspend was done */ int route_type; /* the processing context for the handled message */ context_p msg_ctx; /* the transaction for the handled message */ struct cell *t; enum kill_reason kr; /* the transaction that was cancelled by this message */ struct cell *cancelled_t; /* e2e ACK */ struct cell *e2eack_t; } async_ctx; extern int return_code; /* from action.c, return code */ static inline void run_resume_route( int resume_route, struct sip_msg *msg) { /* run the resume route and if it ends the msg handling (no other aysnc * started), run the post script callbacks. */ if ( (run_top_route(rlist[resume_route].a, msg) & ACT_FL_TBCONT) == 0 ) exec_post_req_cb(msg); } /* function triggered from reactor in order to continue the processing */ int t_resume_async(int *fd, void *param) { static struct sip_msg faked_req; static struct ua_client uac; async_ctx *ctx = (async_ctx *)param; struct cell *backup_t; struct cell *backup_cancelled_t; struct cell *backup_e2eack_t; struct usr_avp **backup_list; struct socket_info* backup_si; struct cell *t= ctx->t; int route; LM_DBG("resuming on fd %d, transaction %p \n",*fd, t); if (current_processing_ctx) { LM_CRIT("BUG - a context already set!\n"); abort(); } /* prepare for resume route */ uac.br_flags = getb0flags( t->uas.request ) ; uac.uri = *GET_RURI( t->uas.request ); if (!fake_req( &faked_req /* the fake msg to be built*/, t->uas.request, /* the template msg saved in transaction */ &t->uas, /*the UAS side of the transaction*/ &uac, /* the fake UAC */ 1 /* copy dst_uri too */) ) { LM_ERR("fake_req failed\n"); return 0; } /* enviroment setting */ current_processing_ctx = ctx->msg_ctx; backup_t = get_t(); backup_e2eack_t = get_e2eack_t(); backup_cancelled_t = get_cancelled_t(); /* fake transaction */ set_t( t ); set_cancelled_t(ctx->cancelled_t); set_e2eack_t(ctx->e2eack_t); reset_kr(); set_kr(ctx->kr); /* make available the avp list from transaction */ backup_list = set_avp_list( &t->user_avps ); /* set default send address to the saved value */ backup_si = bind_address; bind_address = t->uac[0].request.dst.send_sock; async_status = ASYNC_DONE; /* assume default status as done */ /* call the resume function in order to read and handle data */ return_code = ctx->resume_f( *fd, &faked_req, ctx->resume_param ); if (async_status==ASYNC_CONTINUE) { /* do not run the resume route */ goto restore; } else if (async_status==ASYNC_CHANGE_FD) { if (return_code<0) { LM_ERR("ASYNC_CHANGE_FD: given file descriptor shall be positive!\n"); goto restore; } else if (return_code > 0 && return_code == *fd) { /*trying to add the same fd; shall continue*/ LM_CRIT("You are trying to replace the old fd with the same fd!" "Will act as in ASYNC_CONTINUE!\n"); goto restore; } /* remove the old fd from the reactor */ reactor_del_reader( *fd, -1, IO_FD_CLOSING); *fd=return_code; /* insert the new fd inside the reactor */ if (reactor_add_reader( *fd, F_SCRIPT_ASYNC, RCT_PRIO_ASYNC, (void*)ctx)<0 ) { LM_ERR("failed to add async FD to reactor -> act in sync mode\n"); do { return_code = ctx->resume_f( *fd, &faked_req, ctx->resume_param ); if (async_status == ASYNC_CHANGE_FD) *fd=return_code; } while(async_status==ASYNC_CONTINUE||async_status==ASYNC_CHANGE_FD); goto route; } /* changed fd; now restore old state */ goto restore; } /* remove from reactor, we are done */ reactor_del_reader( *fd, -1, IO_FD_CLOSING); if (async_status == ASYNC_DONE_CLOSE_FD) close(*fd); route: /* run the resume_route (some type as the original one) */ swap_route_type(route, ctx->route_type); run_resume_route( ctx->resume_route, &faked_req); set_route_type(route); /* no need for the context anymore */ shm_free(ctx); /* free also the processing ctx if still set * NOTE: it may become null if inside the run_resume_route * another async jump was made (and context attached again * to transaction) */ if (current_processing_ctx) { context_destroy(CONTEXT_GLOBAL, current_processing_ctx); pkg_free(current_processing_ctx); } restore: /* restore original environment */ set_t(backup_t); set_cancelled_t(backup_cancelled_t); set_e2eack_t(backup_e2eack_t); /* restore original avp list */ set_avp_list( backup_list ); bind_address = backup_si; free_faked_req( &faked_req, t); current_processing_ctx = NULL; return 0; } int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route) { async_ctx *ctx = NULL; async_resume_module *ctx_f; void *ctx_p; struct cell *t; int r; int fd; /* create transaction and save everything into transaction */ t=get_t(); if ( t==0 || t==T_UNDEFINED ) { /* create transaction */ r = t_newtran( msg , 1 /*full uas clone*/ ); if (r==0) { /* retransmission -> no follow up; we return a negative * code to indicate do_action that the top route is * is completed (there no resume route to follow) */ return -1; } else if (r<0) { LM_ERR("could not create a new transaction\n"); goto failure; } t=get_t(); } else { /* update the cloned UAS (from transaction) * with data from current msg */ if (t->uas.request) update_cloned_msg_from_msg( t->uas.request, msg); } /* run the function (the action) and get back from it the FD, * resume function and param */ if ( a->type!=AMODULE_T || a->elem[0].type!=ACMD_ST || a->elem[0].u.data==NULL ) { LM_CRIT("BUG - invalid action for async I/O - it must be" " a MODULE_T ACMD_ST \n"); goto failure; } async_status = ASYNC_NO_IO; /*assume defauly status "no IO done" */ return_code = ((acmd_export_t*)(a->elem[0].u.data))->function(msg, &ctx_f, &ctx_p, (char*)a->elem[1].u.data, (char*)a->elem[2].u.data, (char*)a->elem[3].u.data, (char*)a->elem[4].u.data, (char*)a->elem[5].u.data, (char*)a->elem[6].u.data ); /* what to do now ? */ if (async_status>=0) { /* async I/O was successfully launched */ fd = async_status; if (msg->REQ_METHOD==METHOD_ACK || /* ^^^ end2end ACK, there is no actual transaction here */ t->uas.request==NULL /* ^^^ local requests do not support async in local route */ ) { goto sync; } } else if (async_status==ASYNC_NO_IO) { /* no IO, so simply go for resume route */ goto resume; } else if (async_status==ASYNC_SYNC) { /* IO already done in SYNC'ed way */ goto resume; } else if (async_status==ASYNC_CHANGE_FD) { LM_ERR("Incorrect ASYNC_CHANGE_FD status usage!" "You should use this status only from the" "resume function in case something went wrong" "and you have other alternatives!\n"); /*FIXME should we go to resume or exit?it's quite an invalid scenario */ goto resume; } else { /* generic error, go for resume route */ goto resume; } /* do we have a reactor in this process, to handle this asyn I/O ? */ if ( 0/*reactor_exists()*/ ) { /* no reactor, so we directly call the resume function which will block waiting for data */ goto sync; } if ( (ctx=shm_malloc(sizeof(async_ctx)))==NULL) { LM_ERR("failed to allocate new ctx\n"); goto sync; } ctx->resume_f = ctx_f; ctx->resume_param = ctx_p; ctx->resume_route = resume_route; ctx->route_type = route_type; ctx->msg_ctx = current_processing_ctx; ctx->t = t; ctx->kr = get_kr(); ctx->cancelled_t = get_cancelled_t(); ctx->e2eack_t = get_e2eack_t(); current_processing_ctx = NULL; set_t(T_UNDEFINED); reset_cancelled_t(); reset_e2eack_t(); /* place the FD + resume function (as param) into reactor */ if (reactor_add_reader( fd, F_SCRIPT_ASYNC, RCT_PRIO_ASYNC, (void*)ctx)<0 ) { LM_ERR("failed to add async FD to reactor -> act in sync mode\n"); goto sync; } /* done, break the script */ return 0; sync: if (ctx) { /* * the context was moved in reactor, but the reactor could not * fullfil the request - we have to restore the environment -- razvanc */ current_processing_ctx = ctx->msg_ctx; set_t(t); set_cancelled_t(ctx->cancelled_t); set_e2eack_t(ctx->e2eack_t); shm_free(ctx); } /* run the resume function */ do { return_code = ctx_f( fd, msg, ctx_p ); if (async_status == ASYNC_CHANGE_FD) fd = return_code; } while(async_status==ASYNC_CONTINUE||async_status==ASYNC_CHANGE_FD); /* run the resume route in sync mode */ run_resume_route( resume_route, msg); /* break original script */ return 0; failure: /* execute here the resume route with failure indication */ return_code = -1; resume: /* run the resume route */ run_resume_route( resume_route, msg); /* the triggering route is terminated and whole script ended */ return 0; } opensips-2.2.2/modules/tm/async.h000066400000000000000000000021751300170765700167460ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2014-10-16 created (bogdan) */ #ifndef _TM_ASYNC_H_ #define _TM_ASYNC_H_ #include "../../async.h" /* TM function to handle async I/O ops based on trasactions */ int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route); int t_resume_async(int *fd, void *param); #endif opensips-2.2.2/modules/tm/callid.c000066400000000000000000000104341300170765700170510ustar00rootroot00000000000000/* * Fast Call-ID Generator * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ---------- * 2003-04-09 Created by janakj * 2003-10-24 updated to the new socket_info lists (andrei) */ #include #include #include "../../dprint.h" #include "../../pt.h" #include "../../socket_info.h" #include "callid.h" #define CALLID_NR_LEN 20 /* Call-ID has the following form: -@ * callid_nr is initialized as a random number and continually * increases; -@ is kept in callid_suffix */ #define CALLID_SUFFIX_LEN ( 1 /* - */ + \ 5 /* pid */ + \ 42 /* embedded v4inv6 address can be looong '128.' */ + \ 2 /* parenthesis [] */ + \ 1 /* ZT 0 */ + \ 16 /* one never knows ;-) */ \ ) #define CID_SEP '-' /* the character which separates random from constant part */ static unsigned long callid_nr; static char callid_buf[CALLID_NR_LEN + CALLID_SUFFIX_LEN]; str callid_prefix; str callid_suffix; /* * Initialize the Call-ID generator -- generates random prefix */ int init_callid(void) { int rand_bits, i; /* calculate the initial call-id */ /* how many bits and chars do we need to display the * whole ULONG number */ callid_prefix.len = sizeof(unsigned long) * 2; callid_prefix.s = callid_buf; if (callid_prefix.len > CALLID_NR_LEN) { LM_ERR("too small callid buffer\n"); return -1; } for(rand_bits = 1, i = RAND_MAX; i; i >>= 1, rand_bits++); /* how long are the rand()s ? */ i = callid_prefix.len * 4 / rand_bits; /* how many rands() fit in the ULONG ? */ /* now fill in the callid with as many random * numbers as you can + 1 */ callid_nr = rand(); /* this is the + 1 */ while(i--) { callid_nr <<= rand_bits; callid_nr |= rand(); } i = snprintf(callid_prefix.s, callid_prefix.len + 1, "%0*lx", callid_prefix.len, callid_nr); if ((i == -1) || (i > callid_prefix.len)) { LM_CRIT("callid calculation failed\n"); return -2; } LM_DBG("Call-ID initialization: '%.*s'\n", callid_prefix.len, callid_prefix.s); return 0; } /* * Child initialization -- generates suffix */ int child_init_callid(int rank) { struct socket_info *si; /* on tcp/tls bind_address is 0 so try to get the first address we listen * on no matter the protocol */ si=bind_address?bind_address:get_first_socket(); if (si==0){ LM_CRIT("null socket list\n"); return -1; } callid_suffix.s = callid_buf + callid_prefix.len; callid_suffix.len = snprintf(callid_suffix.s, CALLID_SUFFIX_LEN, "%c%d@%.*s", CID_SEP, my_pid(), si->address_str.len, si->address_str.s); if ((callid_suffix.len == -1) || (callid_suffix.len > CALLID_SUFFIX_LEN)) { LM_ERR("buffer too small\n"); return -1; } LM_DBG("callid: '%.*s'\n", callid_prefix.len + callid_suffix.len, callid_prefix.s); return 0; } /* * Increment a character in hex, return * carry flag */ static inline int inc_hexchar(char* _c) { if (*_c == '9') { *_c = 'a'; return 0; } if (*_c == 'f') { *_c = '0'; return 1; } (*_c)++; return 0; } /* * Get a unique Call-ID */ void generate_callid(str* callid) { int i; for(i = callid_prefix.len; i; i--) { if (!inc_hexchar(callid_prefix.s + i - 1)) break; } callid->s = callid_prefix.s; callid->len = callid_prefix.len + callid_suffix.len; } opensips-2.2.2/modules/tm/callid.h000066400000000000000000000023211300170765700170520ustar00rootroot00000000000000/* * Fast Call-ID Generator * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ---------- * 2003-04-09 Created by janakj */ #ifndef CALLID_H #define CALLID_H #include "../../str.h" /* * Initialize the Call-ID generator -- generates random prefix */ int init_callid(void); /* * Child initialization -- generates suffix */ int child_init_callid(int rank); /* * Get a unique Call-ID */ void generate_callid(str* callid); #endif /* CALLID_H */ opensips-2.2.2/modules/tm/config.h000066400000000000000000000047671300170765700171070ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _TM_CONFIG_H #define _TM_CONFIG_H /* hash table engine and hash function */ #include "../../hash_func.h" #include "../../config.h" /* size of TM hash table */ #define TM_TABLE_ENTRIES (1<<16) /* always use a power of 2 for hash table size */ #define tm_hash( s1, s2 ) core_hash( &s1, &s2, TM_TABLE_ENTRIES) /* maximum length of localy generated acknowledgment */ #define MAX_ACK_LEN 1024 /* FINAL_RESPONSE_TIMER ... tells how long should the transaction engine wait if no final response comes back*/ #define FR_TIME_OUT 30 #define INV_FR_TIME_OUT 120 /* WAIT timer ... tells how long state should persist in memory after a transaction was finalized*/ #define WT_TIME_OUT 5 /* DELETE timer ... tells how long should the transaction persist in memory after it was removed from the hash table and before it will be deleted */ #define DEL_TIME_OUT 2 /* retransmission timers */ #define RETR_T1 500 /* in milliseconds */ #define RETR_T2 4000 /* in milliseconds */ /* when first reply is sent, this additional space is allocated so that one does not have to reallocate share memory when the message is replaced by a subsequent, longer message */ #define REPLY_OVERBUFFER_LEN 160 #define TAG_OVERBUFFER_LEN 32 /* dimensions of FIFO server */ #define MAX_METHOD 64 #define MAX_HEADER 1024 #define MAX_BODY 1024 #define MAX_DST 512 #define MAX_FROM 512 /* messages generated by server */ #define CANCELING "canceling" #define CANCEL_DONE "ok -- no more pending branches" #define CANCELED "Request canceled" /* to-tag separator for stateful processing */ #define TM_TAG_SEPARATOR '-' /* FIFO substitution character */ #define SUBST_CHAR '!' #endif opensips-2.2.2/modules/tm/dlg.c000066400000000000000000000554571300170765700164050ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-29 Created by janakj * 2003-07-08 added wrapper to calculate_hooks, needed by b2bua (dcm) * 2008-04-04 added support for local and remote dispaly name in TM dialogs * (by Andrei Pisau ) */ #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_to.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" #include "../../trim.h" #include "../../ut.h" #include "../../config.h" #include "dlg.h" #include "t_reply.h" #include "callid.h" #include "uac.h" #include "../../parser/parser_f.h" #define NORMAL_ORDER 0 /* Create route set in normal order - UAS */ #define REVERSE_ORDER 1 /* Create route set in reverse order - UAC */ #define ROUTE_PREFIX "Route: " #define ROUTE_PREFIX_LEN (sizeof(ROUTE_PREFIX) - 1) #define ROUTE_SEPARATOR "," #define ROUTE_SEPARATOR_LEN (sizeof(ROUTE_SEPARATOR) - 1) /*** Temporary hack ! */ /* * This function skips name part * uri parsed by parse_contact must be used * (the uri must not contain any leading or * trailing part and if angle bracket were * used, right angle bracket must be the * last character in the string) * * _s will be modified so it should be a tmp * copy */ void get_raw_uri(str* _s) { char* aq; if (_s->s[_s->len - 1] == '>') { aq = find_not_quoted(_s, '<'); _s->len -= aq - _s->s + 2; _s->s = aq + 1; } } /* * Calculate dialog hooks */ static inline int calculate_hooks(dlg_t* _d) { str* uri; struct sip_uri puri; if (_d->route_set) { uri = &_d->route_set->nameaddr.uri; if (parse_uri(uri->s, uri->len, &puri) < 0) { LM_ERR("failed parse to URI\n"); return -1; } if (puri.lr.s) { if (_d->rem_target.s) _d->hooks.request_uri = &_d->rem_target; else _d->hooks.request_uri = &_d->rem_uri; _d->hooks.next_hop = &_d->route_set->nameaddr.uri; _d->hooks.first_route = _d->route_set; } else { _d->hooks.request_uri = &_d->route_set->nameaddr.uri; _d->hooks.next_hop = _d->hooks.request_uri; _d->hooks.first_route = _d->route_set->next; _d->hooks.last_route = &_d->rem_target; } } else { if (_d->rem_target.s) _d->hooks.request_uri = &_d->rem_target; else _d->hooks.request_uri = &_d->rem_uri; if(_d->hooks.next_hop==NULL) _d->hooks.next_hop = _d->hooks.request_uri; } if ((_d->hooks.request_uri) && (_d->hooks.request_uri->s) && (_d->hooks.request_uri->len)) { _d->hooks.ru.s = _d->hooks.request_uri->s; _d->hooks.ru.len = _d->hooks.request_uri->len; _d->hooks.request_uri = &_d->hooks.ru; get_raw_uri(_d->hooks.request_uri); } if ((_d->hooks.next_hop) && (_d->hooks.next_hop->s) && (_d->hooks.next_hop->len)) { _d->hooks.nh.s = _d->hooks.next_hop->s; _d->hooks.nh.len = _d->hooks.next_hop->len; _d->hooks.next_hop = &_d->hooks.nh; get_raw_uri(_d->hooks.next_hop); } return 0; } /* * wrapper to calculate_hooks * added by dcm */ int w_calculate_hooks(dlg_t* _d) { return calculate_hooks(_d); } /* * Create a new dialog - internal function */ static int _internal_new_dlg_uac(str* _cid, str* _ltag, unsigned int _lseq, str* _luri, str* _ruri, struct socket_info* sock, dlg_t** _d) { dlg_t* res; if (!_cid || !_ltag || !_luri || !_ruri || !_d) { LM_ERR("Invalid parameter value\n"); return -1; } res = (dlg_t*)shm_malloc(sizeof(dlg_t)); if (res == 0) { LM_ERR("No memory left\n"); return -2; } /* Clear everything */ memset(res, 0, sizeof(dlg_t)); /* Make a copy of Call-ID */ if (shm_str_dup(&res->id.call_id, _cid) < 0) return -3; /* Make a copy of local tag (usually From tag) */ if (shm_str_dup(&res->id.loc_tag, _ltag) < 0) return -4; /* Make a copy of local URI (usually From) */ if (shm_str_dup(&res->loc_uri, _luri) < 0) return -5; /* Make a copy of remote URI (usually To) */ if (shm_str_dup(&res->rem_uri, _ruri) < 0) return -6; /* Make a copy of local sequence (usually CSeq) */ res->loc_seq.value = _lseq; /* And mark it as set */ res->loc_seq.is_set = 1; /* set socket */ res->send_sock = sock; *_d = res; if (calculate_hooks(*_d) < 0) { LM_ERR("failed to calculate hooks\n"); /* FIXME: free everything here */ shm_free(res); return -2; } return 0; } /* * Create a new dialog */ int new_dlg_uac(str* _cid, str* _ltag, unsigned int _lseq, str* _luri, str* _ruri, dlg_t** _d) { return _internal_new_dlg_uac(_cid,_ltag,_lseq,_luri,_ruri,NULL,_d); } /* * Create a new dialog (auto mode) */ int new_auto_dlg_uac( str* _luri, str* _ruri, struct socket_info* _sock, dlg_t** _d) { str callid, fromtag; generate_callid(&callid); generate_fromtag(&fromtag, &callid); return _internal_new_dlg_uac(&callid, &fromtag, 13/*cseq*/,_luri, _ruri,_sock,_d); } /* * Store display names into a dialog */ int dlg_add_extra(dlg_t* _d, str* _ldname, str* _rdname) { if(!_d || !_ldname || !_rdname) { LM_ERR("Invalid parameters\n"); return -1; } /* Make a copy of local Display Name */ if(shm_str_dup(&_d->loc_dname, _ldname) < 0) return -2; /* Make a copy of remote Display Name */ if(shm_str_dup(&_d->rem_dname, _rdname) < 0) return -3; return 0; } /* * Parse Contact header field body and extract URI * Does not parse headers !! */ static inline int get_contact_uri(struct sip_msg* _m, str* _uri) { contact_t* c; _uri->len = 0; if (!_m->contact) return 1; if (parse_contact(_m->contact) < 0) { LM_ERR("failed to parse Contact body\n"); return -2; } c = ((contact_body_t*)_m->contact->parsed)->contacts; if (!c) { LM_ERR("Empty body or * contact\n"); return -3; } _uri->s = c->uri.s; _uri->len = c->uri.len; return 0; } /* * Extract tag from To header field of a response * Doesn't parse message headers !! */ static inline int get_to_tag(struct sip_msg* _m, str* _tag) { if (!_m->to) { LM_ERR("To header field missing\n"); return -1; } if (get_to(_m)->tag_value.len) { _tag->s = get_to(_m)->tag_value.s; _tag->len = get_to(_m)->tag_value.len; } else { _tag->len = 0; } return 0; } /* * Extract tag from From header field of a request */ static inline int get_from_tag(struct sip_msg* _m, str* _tag) { if (parse_from_header(_m)<0) { LM_ERR("failed to parse From header\n"); return -1; } if (get_from(_m)->tag_value.len) { _tag->s = get_from(_m)->tag_value.s; _tag->len = get_from(_m)->tag_value.len; } else { _tag->len = 0; } return 0; } /* * Extract Call-ID value * Doesn't parse headers !! */ static inline int get_callid(struct sip_msg* _m, str* _cid) { if (_m->callid == 0) { LM_ERR("Call-ID not found\n"); return -1; } _cid->s = _m->callid->body.s; _cid->len = _m->callid->body.len; trim(_cid); return 0; } /* * Create a copy of route set either in normal or reverse order */ static inline int get_route_set(struct sip_msg* _m, rr_t** _rs, unsigned char _order) { struct hdr_field* ptr; rr_t* last, *p, *t; last = 0; *_rs = 0; ptr = _m->record_route; while(ptr) { if (ptr->type == HDR_RECORDROUTE_T) { if (parse_rr(ptr) < 0) { LM_ERR("failed to parse Record-Route body\n"); goto error; } p = (rr_t*)ptr->parsed; while(p) { if (shm_duplicate_rr(&t, p, 1/*only first*/) < 0) { LM_ERR("duplicating rr_t\n"); goto error; } if (_order == NORMAL_ORDER) { if (!*_rs) *_rs = t; if (last) last->next = t; last = t; } else { t->next = *_rs; *_rs = t; } p = p->next; } } ptr = ptr->next; } return 0; error: shm_free_rr(_rs); return -1; } /* * Extract all necessary information from a response and put it * in a dialog structure */ static inline int response2dlg(struct sip_msg* _m, dlg_t* _d) { str contact, rtag; /* Parse the whole message, we will need all Record-Route headers */ if (parse_headers(_m, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse headers\n"); return -1; } if (get_contact_uri(_m, &contact) < 0) return -2; if (contact.len && shm_str_dup(&_d->rem_target, &contact) < 0) return -3; if (get_to_tag(_m, &rtag) < 0) goto err1; if (rtag.len && shm_str_dup(&_d->id.rem_tag, &rtag) < 0) goto err1; if (get_route_set(_m, &_d->route_set, REVERSE_ORDER) < 0) goto err2; return 0; err2: if (_d->id.rem_tag.s) shm_free(_d->id.rem_tag.s); _d->id.rem_tag.s = 0; _d->id.rem_tag.len = 0; err1: if (_d->rem_target.s) shm_free(_d->rem_target.s); _d->rem_target.s = 0; _d->rem_target.len = 0; return -4; } /* * Handle dialog in DLG_NEW state, we will be processing the * first response */ static inline int dlg_new_resp_uac(dlg_t* _d, struct sip_msg* _m) { int code; /* * Dialog is in DLG_NEW state, we will copy remote * target URI, remote tag if present, and route-set * if present. And we will transit into DLG_CONFIRMED * if the response was 2xx and to DLG_DESTROYED if the * request was a negative final response. */ code = _m->first_line.u.reply.statuscode; if (code < 200) { /* A provisional response, do nothing, we could * update remote tag and route set but we will do that * for a positive final response anyway and I don't want * bet on presence of these fields in provisional responses */ } else if ((code >= 200) && (code < 299)) { /* A final response, update the structures and transit * into DLG_CONFIRMED */ if (response2dlg(_m, _d) < 0) return -1; _d->state = DLG_CONFIRMED; if (calculate_hooks(_d) < 0) { LM_ERR("failed to calculate hooks\n"); return -2; } } else { /* * A negative final response, mark the dialog as destroyed * Again, I do not update the structures here because it * makes no sense to me, a dialog shouldn't be used after * it is destroyed */ _d->state = DLG_DESTROYED; /* Signalize the termination with positive return value */ return 1; } return 0; } /* * Handle dialog in DLG_EARLY state, we will be processing either * next provisional response or a final response */ static inline int dlg_early_resp_uac(dlg_t* _d, struct sip_msg* _m) { int code; code = _m->first_line.u.reply.statuscode; if (code < 200) { /* We are in early state already, do nothing */ } else if ((code >= 200) && (code <= 299)) { /* Same as in dlg_new_resp_uac */ /* A final response, update the structures and transit * into DLG_CONFIRMED */ if (response2dlg(_m, _d) < 0) return -1; _d->state = DLG_CONFIRMED; if (calculate_hooks(_d) < 0) { LM_ERR("failed to calculate hooks\n"); return -2; } } else { /* Else terminate the dialog */ _d->state = DLG_DESTROYED; /* Signalize the termination with positive return value */ return 1; } return 0; } /* * Extract method from CSeq header field */ static inline int get_cseq_method(struct sip_msg* _m, str* _method) { if (!_m->cseq && ((parse_headers(_m, HDR_CSEQ_F, 0)==-1) || !_m->cseq)) { LM_ERR("failed to parse CSeq\n"); return -1; } _method->s = get_cseq(_m)->method.s; _method->len = get_cseq(_m)->method.len; return 0; } /* * Handle dialog in DLG_CONFIRMED state, we will be processing * a response to a request sent within a dialog */ static inline int dlg_confirmed_resp_uac(dlg_t* _d, struct sip_msg* _m) { int code; str method, contact; code = _m->first_line.u.reply.statuscode; /* Dialog has been already confirmed, that means we received * a response to a request sent within the dialog. We will * update remote target URI if and only if the message sent was * a target refresher. */ /* FIXME: Currently we support only INVITEs as target refreshers, * this should be generalized */ /* IF we receive a 481 response, terminate the dialog because * the remote peer indicated that it didn't have the dialog * state anymore, signal this termination with a positive return * value */ if (code == 481) { _d->state = DLG_DESTROYED; return 1; } /* Do nothing if not 2xx */ if ((code < 200) || (code >= 300)) return 0; if (get_cseq_method(_m, &method) < 0) return -1; if ((method.len == 6) && !memcmp("INVITE", method.s, 6)) { /* Get contact if any and update remote target */ if (parse_headers(_m, HDR_CONTACT_F, 0) == -1) { LM_ERR("failed to parse headers\n"); return -2; } /* Try to extract contact URI */ if (get_contact_uri(_m, &contact) < 0) return -3; /* If there is a contact URI */ if (contact.len) { /* Free old remote target if any */ if (_d->rem_target.s) shm_free(_d->rem_target.s); /* Duplicate new remote target */ if (shm_str_dup(&_d->rem_target, &contact) < 0) return -4; } } return 0; } /* * A response arrived, update dialog */ int dlg_response_uac(dlg_t* _d, struct sip_msg* _m) { if (!_d || !_m) { LM_ERR("invalid parameter value\n"); return -1; } /* The main dispatcher */ switch(_d->state) { case DLG_NEW: return dlg_new_resp_uac(_d, _m); case DLG_EARLY: return dlg_early_resp_uac(_d, _m); case DLG_CONFIRMED: return dlg_confirmed_resp_uac(_d, _m); case DLG_DESTROYED: LM_ERR("failed handle destroyed dialog\n"); return -2; } LM_ERR("unsuccessful switch statement\n"); return -3; } /* * Get CSeq number * Does not parse headers !! */ static inline int get_cseq_value(struct sip_msg* _m, unsigned int* _cs) { str num; if (_m->cseq == 0) { LM_ERR("CSeq header not found\n"); return -1; } num.s = get_cseq(_m)->number.s; num.len = get_cseq(_m)->number.len; trim_leading(&num); if (str2int(&num, _cs) < 0) { LM_ERR("converting cseq number failed\n"); return -2; } return 0; } /* * Copy To or From URI without tag parameter */ static inline int get_dlg_uri(struct hdr_field* _h, str* _s) { struct to_param* ptr, *prev; struct to_body* body; char* tag = 0; /* Makes gcc happy */ int tag_len = 0, len; if (!_h) { LM_ERR("header field not found\n"); return -1; } /* From was already parsed when extracting tag * and To is parsed by default */ body = (struct to_body*)_h->parsed; ptr = body->param_lst; prev = 0; while(ptr) { if (ptr->type == TAG_PARAM) break; prev = ptr; ptr = ptr->next; } if (ptr) { /* Tag param found */ if (prev) { tag = prev->value.s + prev->value.len; } else { tag = body->body.s + body->body.len; } if (ptr->next) { tag_len = ptr->value.s + ptr->value.len - tag; } else { tag_len = _h->body.s + _h->body.len - tag; } } _s->s = shm_malloc(_h->body.len - tag_len); if (!_s->s) { LM_ERR("No share memory left\n"); return -1; } if (tag_len) { len = tag - _h->body.s; memcpy(_s->s, _h->body.s, len); memcpy(_s->s + len, tag + tag_len, _h->body.len - len - tag_len); _s->len = _h->body.len - tag_len; } else { memcpy(_s->s, _h->body.s, _h->body.len); _s->len = _h->body.len; } return 0; } /* * Extract all information from a request * and update a dialog structure */ static inline int request2dlg(struct sip_msg* _m, dlg_t* _d) { str contact, rtag, callid; if (parse_headers(_m, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse headers\n"); return -1; } if (get_contact_uri(_m, &contact) < 0) return -2; if (contact.len && shm_str_dup(&_d->rem_target, &contact) < 0) return -3; if (get_from_tag(_m, &rtag) < 0) goto err1; if (rtag.len && shm_str_dup(&_d->id.rem_tag, &rtag) < 0) goto err1; if (get_callid(_m, &callid) < 0) goto err2; if (callid.len && shm_str_dup(&_d->id.call_id, &callid) < 0) goto err2; if (get_cseq_value(_m, &_d->rem_seq.value) < 0) goto err3; _d->rem_seq.is_set = 1; if (get_dlg_uri(_m->from, &_d->rem_uri) < 0) goto err3; if (get_dlg_uri(_m->to, &_d->loc_uri) < 0) goto err4; if (get_route_set(_m, &_d->route_set, NORMAL_ORDER) < 0) goto err5; return 0; err5: if (_d->loc_uri.s) shm_free(_d->loc_uri.s); _d->loc_uri.s = 0; _d->loc_uri.len = 0; err4: if (_d->rem_uri.s) shm_free(_d->rem_uri.s); _d->rem_uri.s = 0; _d->rem_uri.len = 0; err3: if (_d->id.call_id.s) shm_free(_d->id.call_id.s); _d->id.call_id.s = 0; _d->id.call_id.len = 0; err2: if (_d->id.rem_tag.s) shm_free(_d->id.rem_tag.s); _d->id.rem_tag.s = 0; _d->id.rem_tag.len = 0; err1: if (_d->rem_target.s) shm_free(_d->rem_target.s); _d->rem_target.s = 0; _d->rem_target.len = 0; return -4; } /* * Establishing a new dialog, UAS side */ int new_dlg_uas(struct sip_msg* _req, int _code, /*str* _tag,*/ dlg_t** _d) { dlg_t* res; str tag; if (!_req || /*!_tag ||*/ !_d) { LM_ERR("Invalid parameter value\n"); return -1; } if ((_code < 200) || (_code > 299)) { LM_DBG("not a 2xx, no dialog created\n"); return -2; } res = (dlg_t*)shm_malloc(sizeof(dlg_t)); if (res == 0) { LM_ERR("no more share memory\n"); return -3; } /* Clear everything */ memset(res, 0, sizeof(dlg_t)); if (request2dlg(_req, res) < 0) { LM_ERR("converting request to dialog failed\n"); return -4; } tag.s = tm_tags; tag.len = TOTAG_VALUE_LEN; calc_crc_suffix(_req, tm_tag_suffix); if (shm_str_dup(&res->id.loc_tag, &tag) < 0) { free_dlg(res); return -5; } *_d = res; (*_d)->state = DLG_CONFIRMED; if (calculate_hooks(*_d) < 0) { LM_ERR("calculating hooks failed\n"); shm_free(*_d); return -6; } return 0; } /* * UAS side - update a dialog from a request */ int dlg_request_uas(dlg_t* _d, struct sip_msg* _m) { str contact; unsigned int cseq; if (!_d || !_m) { LM_ERR("Invalid parameter value\n"); return -1; } /* We must check if the request is not out of order or retransmission * first, if so then we will not update anything */ if (parse_headers(_m, HDR_CSEQ_F, 0) == -1) { LM_ERR("parsing headers failed\n"); return -2; } if (get_cseq_value(_m, &cseq) < 0) return -3; if (_d->rem_seq.is_set && (cseq <= _d->rem_seq.value)) return 0; /* Neither out of order nor retransmission -> update */ _d->rem_seq.value = cseq; _d->rem_seq.is_set = 1; /* We will als update remote target URI if the message * is target refresher */ if (_m->first_line.u.request.method_value == METHOD_INVITE) { /* target refresher */ if (parse_headers(_m, HDR_CONTACT_F, 0) == -1) { LM_ERR("parsing headers failed\n"); return -4; } if (get_contact_uri(_m, &contact) < 0) return -5; if (contact.len) { if (_d->rem_target.s) shm_free(_d->rem_target.s); if (shm_str_dup(&_d->rem_target, &contact) < 0) return -6; } } return 0; } /* * Calculate length of the route set */ int calculate_routeset_length(dlg_t* _d) { int len; rr_t* ptr; len = 0; ptr = _d->hooks.first_route; if (ptr || _d->hooks.last_route) { len = ROUTE_PREFIX_LEN; len += CRLF_LEN; } while(ptr) { len += ptr->len; ptr = ptr->next; if (ptr) len += ROUTE_SEPARATOR_LEN; } if (_d->hooks.last_route) { if (_d->hooks.first_route) len += ROUTE_SEPARATOR_LEN; len += _d->hooks.last_route->len + 2; /* < > */ } return len; } /* * * Print the route set */ char* print_routeset(char* buf, dlg_t* _d) { rr_t* ptr; ptr = _d->hooks.first_route; if (ptr || _d->hooks.last_route) { memcpy(buf, ROUTE_PREFIX, ROUTE_PREFIX_LEN); buf += ROUTE_PREFIX_LEN; } while(ptr) { memcpy(buf, ptr->nameaddr.name.s, ptr->len); buf += ptr->len; ptr = ptr->next; if (ptr) { memcpy(buf, ROUTE_SEPARATOR, ROUTE_SEPARATOR_LEN); buf += ROUTE_SEPARATOR_LEN; } } if (_d->hooks.last_route) { if (_d->hooks.first_route) { memcpy(buf, ROUTE_SEPARATOR, ROUTE_SEPARATOR_LEN); buf += ROUTE_SEPARATOR_LEN; } *buf = '<'; buf++; memcpy(buf, _d->hooks.last_route->s, _d->hooks.last_route->len); buf += _d->hooks.last_route->len; *buf = '>'; buf++; } if (_d->hooks.first_route || _d->hooks.last_route) { memcpy(buf, CRLF, CRLF_LEN); buf += CRLF_LEN; } return buf; } /* * Destroy a dialog state */ void free_dlg(dlg_t* _d) { if (!_d) return; if (_d->id.call_id.s) shm_free(_d->id.call_id.s); if (_d->id.rem_tag.s) shm_free(_d->id.rem_tag.s); if (_d->id.loc_tag.s) shm_free(_d->id.loc_tag.s); if (_d->loc_uri.s) shm_free(_d->loc_uri.s); if (_d->rem_uri.s) shm_free(_d->rem_uri.s); if (_d->rem_target.s) shm_free(_d->rem_target.s); if (_d->loc_dname.s) shm_free(_d->loc_dname.s); if (_d->rem_dname.s) shm_free(_d->rem_dname.s); /* Free all routes in the route set */ shm_free_rr(&_d->route_set); shm_free(_d); } /* * Print a dialog structure, just for debugging */ void print_dlg(FILE* out, dlg_t* _d) { fprintf(out, "====dlg_t===\n"); fprintf(out, "id.call_id : '%.*s'\n", _d->id.call_id.len, _d->id.call_id.s); fprintf(out, "id.rem_tag : '%.*s'\n", _d->id.rem_tag.len, _d->id.rem_tag.s); fprintf(out, "id.loc_tag : '%.*s'\n", _d->id.loc_tag.len, _d->id.loc_tag.s); fprintf(out, "loc_seq.value : %d\n", _d->loc_seq.value); fprintf(out, "loc_seq.is_set: %s\n", _d->loc_seq.is_set ? "YES" : "NO"); fprintf(out, "rem_seq.value : %d\n", _d->rem_seq.value); fprintf(out, "rem_seq.is_set: %s\n", _d->rem_seq.is_set ? "YES" : "NO"); fprintf(out, "loc_uri : '%.*s'\n",_d->loc_uri.len, _d->loc_uri.s); fprintf(out, "rem_uri : '%.*s'\n",_d->rem_uri.len, _d->rem_uri.s); fprintf(out, "loc_dname : '%.*s'\n",_d->loc_dname.len,_d->loc_dname.s); fprintf(out, "rem_dname : '%.*s'\n",_d->rem_dname.len,_d->rem_dname.s); fprintf(out, "rem_target : '%.*s'\n", _d->rem_target.len,_d->rem_target.s); fprintf(out, "state : "); switch(_d->state) { case DLG_NEW: fprintf(out, "DLG_NEW\n"); break; case DLG_EARLY: fprintf(out, "DLG_EARLY\n"); break; case DLG_CONFIRMED: fprintf(out, "DLG_CONFIRMED\n"); break; case DLG_DESTROYED: fprintf(out, "DLG_DESTROYED\n"); break; } print_rr(out, _d->route_set); if (_d->hooks.request_uri) fprintf(out, "hooks.request_uri: '%.*s'\n", _d->hooks.request_uri->len, _d->hooks.request_uri->s); if (_d->hooks.next_hop) fprintf(out, "hooks.next_hop : '%.*s'\n", _d->hooks.next_hop->len, _d->hooks.next_hop->s); if (_d->hooks.first_route) fprintf(out, "hooks.first_route: '%.*s'\n", _d->hooks.first_route->len,_d->hooks.first_route->nameaddr.name.s); if (_d->hooks.last_route) fprintf(out, "hooks.last_route : '%.*s'\n", _d->hooks.last_route->len, _d->hooks.last_route->s); fprintf(out, "====dlg_t====\n"); } opensips-2.2.2/modules/tm/dlg.h000066400000000000000000000121571300170765700164000ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-29 Created by janakj * 2008-04-04 added support for local and remote dispaly name in TM dialogs * (by Andrei Pisau ) */ #ifndef DLG_H #define DLG_H #include #include "../../str.h" #include "../../parser/parse_rr.h" #include "../../parser/msg_parser.h" /* * Dialog sequence */ typedef struct dlg_seq { unsigned int value; /* Sequence value */ unsigned char is_set; /* is_set flag */ } dlg_seq_t; /* * Dialog state */ typedef enum dlg_state { DLG_NEW = 0, /* New dialog, no reply received yet */ DLG_EARLY, /* Early dialog, provisional response received */ DLG_CONFIRMED, /* Confirmed dialog, 2xx received */ DLG_DESTROYED /* Destroyed dialog */ } dlg_state_t; /* * Structure describing a dialog identifier */ typedef struct dlg_id { str call_id; /* Call-ID */ str rem_tag; /* Remote tag of the dialog */ str loc_tag; /* Local tag of the dialog */ } dlg_id_t; /* * It is necessary to analyze the dialog data to find out * what URI put into the Record-Route, where the message * should be really sent and how to construct the route * set of the message. This structure stores this information * so we don't have to calculate each time we want to send a * message within dialog */ typedef struct dlg_hooks { str ru; str nh; str* request_uri; /* This should be put into Request-URI */ str* next_hop; /* Where the message should be really sent */ rr_t* first_route; /* First route to be printed into the message */ str* last_route; /* If not zero add this as the last route */ } dlg_hooks_t; /* * Structure representing dialog state */ typedef struct dlg { dlg_id_t id; /* Dialog identifier */ dlg_seq_t loc_seq; /* Local sequence number */ dlg_seq_t rem_seq; /* Remote sequence number */ str loc_uri; /* Local URI */ str rem_uri; /* Remote URI */ str obp; /* Outbound proxy */ union sockaddr_union forced_to_su; /* Forced remote sockaddr */ str rem_target; /* Remote target URI */ str loc_dname; /* Local Display Name */ str rem_dname; /* Remote Display Name */ unsigned int T_flags; /* Flags to be passed to transaction */ dlg_state_t state; /* State of the dialog */ rr_t* route_set; /* Route set */ dlg_hooks_t hooks; /* Various hooks used to store information that * can be reused when building a message (to * prevent repeated analyzing of the dialog data */ struct socket_info* send_sock; void *dialog_ctx; /* backpointer to dialog ctx */ struct usr_avp *avps; } dlg_t; /* * Create a new dialog */ int new_dlg_uac(str* _cid, str* _ltag, unsigned int _lseq, str* _luri, str* _ruri, dlg_t** _d); typedef int (*new_dlg_uac_f)(str* _cid, str* _ltag, unsigned int _lseq, str* _luri, str* _ruri, dlg_t** _d); /* * Create a auto new dialog (callid, from tag and CSEQ are auto generated) */ int new_auto_dlg_uac( str* _luri, str* _ruri, struct socket_info* sock, dlg_t** _d); typedef int (*new_auto_dlg_uac_f)(str* _luri, str* _ruri, struct socket_info *sock, dlg_t** _d); /* * Function which adds Display Names to an existing dialog */ int dlg_add_extra(dlg_t* _d, str* _ldname, str* _rdname); typedef int (*dlg_add_extra_f)(dlg_t* _d, str* _ldname, str* _rdname); /* * A response arrived, update dialog */ int dlg_response_uac(dlg_t* _d, struct sip_msg* _m); typedef int (*dlg_response_uac_f)(dlg_t* _d, struct sip_msg* _m); /* * Establishing a new dialog, UAS side */ int new_dlg_uas(struct sip_msg* _req, int _code, /*str* _tag,*/ dlg_t** _d); typedef int (*new_dlg_uas_f)(struct sip_msg* _req, int _code, dlg_t** _d); /* * UAS side - update a dialog from a request */ int dlg_request_uas(dlg_t* _d, struct sip_msg* _m); typedef int (*dlg_request_uas_f)(dlg_t* _d, struct sip_msg* _m); /* * Destroy a dialog state */ void free_dlg(dlg_t* _d); typedef void (*free_dlg_f)(dlg_t* _d); /* * Print a dialog structure, just for debugging */ void print_dlg(FILE* out, dlg_t* _d); typedef void (*print_dlg_f)(FILE* out, dlg_t* _d); /* * Calculate length of the route set */ int calculate_routeset_length(dlg_t* _d); /* * * Print the route set */ char* print_routeset(char* buf, dlg_t* _d); /* * wrapper to calculate_hooks * added by dcm */ int w_calculate_hooks(dlg_t* _d); #endif /* DLG_H */ opensips-2.2.2/modules/tm/doc/000077500000000000000000000000001300170765700162205ustar00rootroot00000000000000opensips-2.2.2/modules/tm/doc/tm.xml000066400000000000000000000031651300170765700173670ustar00rootroot00000000000000 %docentities; ]> tm Module &osipsname; Bogdan-Andrei Iancu
bogdan@opensips.org
Jiri Kuthan &fhg;
jiri@iptel.org
Jiri Kuthan
jiri@iptel.org
Bogdan-Andrei Iancu
bogdan@opensips.org
Ovidiu Sas
osas@voipembedded.com
2003 &fhg; 2005-2008 &voicesystem; $Revision: 8740 $ $Date$
&admin; &devel; &faq;
opensips-2.2.2/modules/tm/doc/tm_admin.xml000066400000000000000000001460261300170765700205430ustar00rootroot00000000000000 &adminguide;
Overview TM module enables stateful processing of SIP transactions. The main use of stateful logic, which is costly in terms of memory and CPU, is some services inherently need state. For example, transaction-based accounting (module acc) needs to process transaction state as opposed to individual messages, and any kinds of forking must be implemented statefully. Other use of stateful processing is it trading CPU caused by retransmission processing for memory. That makes however only sense if CPU consumption per request is huge. For example, if you want to avoid costly DNS resolution for every retransmission of a request to an unresolvable destination, use stateful mode. Then, only the initial message burdens server by DNS queries, subsequent retransmissions will be dropped and will not result in more processes blocked by DNS resolution. The price is more memory consumption and higher processing latency. From user's perspective, the major function is t_relay(). It setup transaction state, absorb retransmissions from upstream, generate downstream retransmissions and correlate replies to requests. In general, if TM is used, it copies clones of received SIP messages in shared memory. That costs the memory and also CPU time (memcpys, lookups, shmem locks, etc.) Note that non-TM functions operate over the received message in private memory, that means that any core operations will have no effect on statefully processed messages after creating the transactional state. For example, calling record_route after t_relay is pretty useless, as the RR is added to privately held message whereas its TM clone is being forwarded. TM is quite big and uneasy to program--lot of mutexes, shared memory access, malloc and free, timers--you really need to be careful when you do anything. To simplify TM programming, there is the instrument of callbacks. The callback mechanisms allow programmers to register their functions to specific event. See t_hooks.h for a list of possible events. Other things programmers may want to know is UAC--it is a very simplistic code which allows you to generate your own transactions. Particularly useful for things like NOTIFYs or IM gateways. The UAC takes care of all the transaction machinery: retransmissions , FR timeouts, forking, etc. See t_uac prototype in uac.h for more details. Who wants to see the transaction result may register for a callback.
Per-Branch flags First what is the idea with the branch concept: branch route is a route to be execute separately for each branch before being sent out - changes in that route should reflect only on that branch. There are several types of flags in &osips; : message/transaction flags - they are visible everywhere in the transaction (in all routes and in all sequential replies/request). branch flags - flags that are visible only from a specific branch - in all replies and routes connected to this branch. script flags - flags that exist only during script execution. They are not store anywhere and are lost once the top level route was left. For example: I have a call parallel forking to GW and to a user. And I would like to know from which branch I will get the final negative reply (if so). I will set a branch route before relaying the calls (with the 2 branches). The branch route will be separately executed for each branch; in the branch going to GW (I can identified it by looking to RURI), I will set a branch flag. This flag will appear only in the onreply route run for replied from GW. It will be also be visible in failure route if the final elected reply belongs to the GW branch. This flags will not be visible in the other branch (in routes executing replies from the other branch). For how to define branch flags and use via script, see and the setbflag(), resetbflag() and isbflagset() script functions. Also, modules may set branch flags before transaction creation (for the moment this feature is not available in script). The REGISTRAR module was the first to use this type of flags. The NAT flag is pushed in branch flags instead in message flags
Timeout-Based Failover Timeouts can be used to trigger failover behavior. E.g. if we send a call to a gateway and the gateway does not send a provisional response within 3 seconds, we want to cancel this call and send the call to another gateway. Another example is to ring a SIP client only for 30 seconds and then redirect the call to the voicemail. The transaction module exports two types of timeouts: fr_timeout - used when no response was received yet. If there is no response after fr_timeout seconds, the timer triggers (and failure route will be executed if t_on_failure() was called). For INVITE transactions, if a provisional response was received, the timeout is reset to fr_inv_timeout seconds and RT_T2 for all other transactions. Once a final response is received, the transaction has finished. fr_inv_timeout - this timeout starts counting down once a provisional response was received for an INVITE transaction. For example: You want to have failover if there is no provisional response after 3 seconds, but you want to ring for 60 seconds. Thus, set the fr_timeout to 3 and fr_inv_timeout to 60.
DNS Failover DNS based failover can be use when relaying stateful requests. According to RFC 3263, DNS failover should be done on transport level or transaction level. TM module supports them both. Failover at transport level may be triggered by a failure of sending out the request message. A failure occurs if the corresponding interface was found for sending the request, if the TCP connection was refused or if a generic internal error happened during send. There is no ICMP error report support. Failover at transaction level may be triggered when the transaction completed either with a 503 reply, either with a timeout without any received reply. In such a case, automatically, a new branch will be forked if any other destination IPs can be used to deliver the requests. The new branch will be a clone of the winning branch. The set of destinations IPs is step-by-step build (on demand) based on the NAPTR, SRV and A records available for the destination domain. DNS-based failover is by default applied excepting when this failover is globally disabled (see the core parameter disable_dns_failover) or when the relay flag (per transaction) is set (see the t_relay() function).
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>fr_timeout</varname> (integer) Timeout which is triggered if no final reply for a request or ACK for a negative INVITE reply arrives (in seconds). Default value is 30 seconds. Set <varname>fr_timeout</varname> parameter ... modparam("tm", "fr_timeout", 10) ...
<varname>fr_inv_timeout</varname> (integer) Timeout which is triggered if no final reply for an INVITE arrives after a provisional message was received (in seconds). This timeout starts counting down once the first provisional response is received. Thus, fast failover (no 100 trying from gateway) can be achieved by setting fr_timeout to low values. See example below. Default value is 120 seconds. Set <varname>fr_inv_timeout</varname> parameter ... modparam("tm", "fr_inv_timeout", 200) ...
<varname>wt_timer</varname> (integer) Time for which a transaction stays in memory to absorb delayed messages after it completed; also, when this timer hits, retransmission of local cancels is stopped (a puristic but complex behavior would be not to enter wait state until local branches are finished by a final reply or FR timer--we simplified). For non-INVITE transaction this timer relates to timer J of RFC 3261 section 17.2.2. According to the RFC this timer should be 64*T1 (= 32 seconds). But this would increase memory usage as the transactions are kept in memory very long. Default value is 5 seconds. Set <varname>wt_timer</varname> parameter ... modparam("tm", "wt_timer", 10) ...
<varname>delete_timer</varname> (integer) Time after which a to-be-deleted transaction currently ref-ed by a process will be tried to be deleted again. Default value is 2 seconds. Set <varname>delete_timer</varname> parameter ... modparam("tm", "delete_timer", 5) ...
<varname>T1_timer</varname> (integer) Retransmission T1 period, in milliseconds. Default value is 500 milliseconds. Set <varname>T1_timer</varname> parameter ... modparam("tm", "T1_timer", 700) ...
<varname>T2_timer</varname> (integer) Maximum retransmission period, in milliseconds. Default value is 4000 milliseconds. Set <varname>T2_timer</varname> parameter ... modparam("tm", "T2_timer", 8000) ...
<varname>ruri_matching</varname> (integer) Should be request-uri matching used as a part of pre-3261 transaction matching as the standard wants us to do so? Turn only off for better interaction with devices that are broken and send different r-uri in CANCEL/ACK than in original INVITE. Default value is 1 (true). Set <varname>ruri_matching</varname> parameter ... modparam("tm", "ruri_matching", 0) ...
<varname>via1_matching</varname> (integer) Should be top most VIA matching used as a part of pre-3261 transaction matching as the standard wants us to do so? Turn only off for better interaction with devices that are broken and send different top most VIA in CANCEL/ACK than in original INVITE. Default value is 1 (true). Set <varname>via1_matching</varname> parameter ... modparam("tm", "via1_matching", 0) ...
<varname>unix_tx_timeout</varname> (integer) Send timeout to be used by function which use UNIX sockets (as t_write_unix). Default value is 2 seconds. Set <varname>unix_tx_timeout</varname> parameter ... modparam("tm", "unix_tx_timeout", 5) ...
<varname>restart_fr_on_each_reply</varname> (integer) If true (non null value), the final response timer will be re-triggered for each received provisional reply. In this case, final response timeout may occur after a time longer than fr_inv_timeout (if UAS keeps sending provisional replies) Default value is 1 (true). Set <varname>restart_fr_on_each_reply</varname> parameter ... modparam("tm", "restart_fr_on_each_reply", 0) ...
<varname>tw_append</varname> (string) List of additional information to be appended by t_write_req and t_write_unix functions. Default value is null string. Syntax of the parameter is: tw_append = append_name':' element (';'element)* element = ( [name '='] pseudo_variable) The full list of supported pseudo-variables in &osips; is availabe at: http://opensips.org/docs/pseudo-variables-1.1.x.html Each element will be appended per line in name: value format. Element $rb (message body) is the only one which does not accept name; the body it will be printed all the time at the end, disregarding its position in the definition string. Set <varname>tw_append</varname> parameter ... modparam("tm", "tw_append", "test: ua=$hdr(User-Agent) ;avp=$avp(avp);$rb;time=$Ts") ...
<varname>pass_provisional_replies</varname> (integer) Enable/disable passing of provisional replies to FIFO applications. Default value is 0. Set <varname>pass_provisional_replies</varname> parameter ... modparam("tm", "pass_provisional_replies", 1) ...
<varname>syn_branch</varname> (integer) Enable/disable the usage of stateful synonym branch IDs in the generated Via headers. They are faster but not reboot-safe. Default value is 1 (use synonym branches). Set <varname>syn_branch</varname> parameter ... modparam("tm", "syn_branch", 0) ...
<varname>onreply_avp_mode</varname> (integer) Describes how the AVPs should be handled in reply route: 0 - the AVPs will be per message only; they will not interfere with the AVPS stored in transaction; initially there will be an empty list and at the end of the route, all AVPs that were created will be discarded. 1 - the AVPs will be the transaction AVPs; initially the transaction AVPs will be visible; at the end of the route, the list will attached back to transaction (with all the changes) In mode 1, you can see the AVPs you set in request route, branch route or failure route. The side efect is performance as more locking is required in order to keep the AVP's list integrity. Default value is 0. Set <varname>onreply_avp_mode</varname> parameter ... modparam("tm", "onreply_avp_mode", 1) ...
<varname>disable_6xx_block</varname> (integer) Tells how the 6xx replies should be internally handled: 0 - the 6xx replies will block any further serial forking (adding new branches). This is the RFC3261 behaviour. 1 - the 6xx replies will be handled as any other negative reply - serial forking will be allowed. Logically, you need to break RFC3261 if you want to do redirects to announcement and voicemail services. Default value is 0. Set <varname>disable_6xx_block</varname> parameter ... modparam("tm", "disable_6xx_block", 1) ...
<varname>enable_stats</varname> (integer) Enables statistics support in TM module - If enabled, the TM module will internally keep several statistics and export them via the MI - Management Interface. Default value is 1 (enabled). Set <varname>enable_stats</varname> parameter ... modparam("tm", "enable_stats", 0) ...
<varname>minor_branch_flag</varname> (string/integer) A branch flag index to be used in script to mark the minor branches ( before t_relay() ). A minor branch is a branch OpenSIPS will not wait to complete during parallel forking. So, if the rest of the branches are negativly replied OpenSIPS will not wait for a final answer from the minor branch, but it will simply cancel it. Main applicability of minor branch is to fork a branch to a media server for injecting (via 183 Early Media) some pre-call media - of course, this branch will be transparanent for the rest of the call branches (from branch selection point of view). Default value is none (disabled). Set <varname>minor_branch_flag</varname> parameter ... modparam("tm", "minor_branch_flag", "MINOR_BFLAG") ...
<varname>timer_partitions</varname> (integer) The number of partitions for the internal TM timers (retransmissions, delete, wait, etc). Partitioning the timers increase the throughput under heavly load by handling timer events in parallel, rather than all serial. Recomanded range for timer partitions is max 16 (soft limit). Default value is 1 (disabled). Set <varname>timer_partitions</varname> parameter ... # Enable two timer partitions modparam("tm", "timer_partitions", 2) ...
<varname>auto_100trying</varname> (integer) This parameter controls if the TM module should automatically generate an 100 Trying stateful reply when an INVITE transaction is created. You may want to disable this behavior if you want to control from script level when the 100 Trying is to be sent out. Default value is 1 (enabled). Set <varname>auto_100trying</varname> parameter ... # Disable automatic 100 Trying modparam("tm", "auto_100trying", 0) ...
Exported Functions
<function moreinfo="none">t_relay([flags])</function> Relay a message statefully to destination indicated in current URI. (If the original URI was rewritten by UsrLoc, RR, strip/prefix, etc., the new URI will be taken). Returns a negative value on failure--you may still want to send a negative reply upstream statelessly not to leave upstream UAC in lurch. The coresponding transaction may or may not be already created. If not yet created, the function will automatically create it. The function may take as parameter an optional set of flags for controlling the internal behaviour. The flags may be given in decimal or hexa format; supported flags are: 0x01 - deprecated, not used any more 0x02 - do not internally send a negative reply in case of forward failure (due internal error, bad RURI, bad message, etc). When a forward failure occurs, no SIP request is relayed and therefore no negative reply or timeout will show up on the failure_route (if one is set). It applies only when the transaction is created. By default one negative reply is sent. Useful if you want to implement a serial forking in case of failure. 0x04 - disable the DNS failover for the transaction. Only first IP will be used. It disable the failover both at transaport and transaction level. 0x08 - If the request is a CANCEL, trust and pass further the Reason header from the received CANCEL - shortly, will propagate the Reason header. In case of error, the function returns the following codes: -1 - generic internal error -2 - bad message (parsing errors) -3 - no destination available (no branches were added or request already cancelled) -4 - bad destination (unresolvable address) -5 - destination filtered (black listed) -6 - generic send failed This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>t_relay</function> usage ... if (!t_relay()) { sl_reply_error(); exit; } ...
<function moreinfo="none">t_relay(proto:host:port,[flags])</function> Relay a message statefully to a fixed destination. The destination is specified as [proto:]host[:port]. If a destination URI $du for this message was set before the function is called then this value will be used as the destination instead of the function parameter. The function may take as parameter an optional set of flags for controlling the internal behaviour - for details see the above t_relay([flags]) function. This functions can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>t_relay</function> usage ... t_relay("tcp:192.168.1.10:5060"); t_relay("mydomain.com:5070","0x1"); t_relay("udp:mydomain.com"); ...
<function moreinfo="none">t_reply(code, reason_phrase)</function> Sends a stateful SIP reply to the currently processed requests. Note that if the transaction was not created yet, it will automatically created by internally using the t_newtran function. Meaning of the parameters is as follows: code - Reply code number. reason_phrase - Reason string. Both parameters accept any kind of pseudo-variables. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>t_reply</function> usage ... t_reply("404", "Use $rU not found"); ...
<function moreinfo="none">t_reply_with_body(code, reason_phrase, body)</function> Sends a stateful SIP reply with a body to the currently processed requests. Note that if the transaction was not created yet, it will automatically created by internally using the t_newtran function. Meaning of the parameters is as follows: code - Reply code number. reason_phrase - Reason string. body - Reply body. All parameters accept any kind of pseudo-variables. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>t_reply_with_body</function> usage ... if(is_method("INVITE")) { append_to_reply("Contact: $var(contact)\r\n" "Content-Type: application/sdp\r\n"); t_reply_with_body("200", "Ok", "$var(body)"); exit; } ...
<function moreinfo="none">t_newtran()</function> Creates the SIP transaction for the currently processed SIP requests. Basically you switch to a stateful processing from this point furhter, with automatic detection of retransmission and auto sending of 100 Trying reply on INVITEs. This function can be used from REQUEST_ROUTE. <function>t_newtran</function> usage ... t_newtran(); # 100 Trying is fired here xlog("doing my complicated routing logic"); .... t_relay(); # send the call further ...
<function moreinfo="none">t_check_trans()</function> Returns true if the current request is associated to a transaction. The relationship between the request and transaction is defined as follows: non-CANCEL/non-ACK requests - if the request belongs to a transaction (it's a retransmision), the function will do a standard processing of the retransmission and will break/stop the script. The function returns false if the request is not a retransmission. CANCEL request - true if the cancelled INVITE transaction exists. ACK request - true if the ACK is a local end-to-end ACK corresponding to an previous INVITE transaction. Note: To detect retransmissions using this function you have to make sure that the initial request has already created a transaction, e.g. by using t_relay(). If the processing of requests may take long time (e.g. DB lookups) and the retransmission arrives before t_relay() is called, you can use the t_newtran() function to manually create a transaction. This function can be used from REQUEST_ROUTE and BRANCH_ROUTE. <function>t_check_trans</function> usage ... if ( is_method("CANCEL") ) { if ( t_check_trans() ) t_relay(); exit; } ...
<function moreinfo="none">t_check_status(re)</function> Returns true if the regualr expression re match the reply code of the response message as follows: in routing block - the code of the last sent reply. in on_reply block - the code of the current received reply. in on_failure block - the code of the selected negative final reply. This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE . <function>t_check_status</function> usage ... if (t_check_status("(487)|(408)")) { log("487 or 408 negative reply\n"); } ...
<function moreinfo="none">t_local_replied(reply)</function> Returns true if all or last (depending of the parameter) reply(es) were local generated (and not received). Parameter may be all or last. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, FAILURE_ROUTE and ONREPLY_ROUTE. <function>t_local_replied</function> usage ... if (t_local_replied("all")) { log ("no reply received\n"); } ...
<function moreinfo="none">t_was_cancelled()</function> Retuns true if called for an INVITE transaction that was explicitly cancelled by UAC side via a CANCEL request. This function can be used from ONREPLY_ROUTE, FAILURE_ROUTE. <function>t_was_cancelled</function> usage ... if (t_was_cancelled()) { log("transaction was cancelled by UAC\n"); } ...
<function moreinfo="none">t_cancel_branch([flags])</function> This function is to be call when a reply is received for cancelling a set of branches (see flags) of the current call. Meaning of the parameters is as follows: flags - (optional) - set of flags (char based flags) to control what branches to be cancelled: a - all - cancel all pending branches o - others - cancel all the other pending branches except the current one empty - current - cancel only the current branch This function can be used from ONREPLY_ROUTE. <function>t_cancel_branch</function> usage onreply_route[3] { ... if (t_check_status("183")) { # no support for early media t_cancel_branch(); } ... }
<function moreinfo="none">t_new_request(method,RURI,from,to[,body[,ctx]])</function> This function generates and sends out a new SIP request (in a stateful way). The new request is completly unrelated to the currently processed SIP message. Meaning of the parameters is as follows (all do accept variables): method - the SIP method RURI - the SIP Request URI (the request will be sent out to this destination) from - the SIP From hdr information as "[display ]URI" from - the SIP To hdr information as "[display ]URI" body - (optional) the SIP body content starting with the content type string: "conten_type body" ctx - a context string that will be added to the new transaction as an AVP with name "uac_ctx" (it may be visible in local route) <function>t_new_request</function> usage ... # send a MESSAGE request t_new_request("MESSAGE","sip:alice@192.168.2.2","BOB sip:userB@mydomain.net","ALICE sip:userA@mydomain.net","text/plain Hello Alice!")) { ...
<function moreinfo="none">t_on_failure(failure_route)</function> Sets reply routing block, to which control is passed after a transaction completed with a negative result but before sending a final reply. In the referred block, you can either start a new branch (good for services such as forward_on_no_reply) or send a final reply on your own (good for example for message silo, which received a negative reply from upstream and wants to tell upstream 202 I will take care of it). As not all functions are available from failure route, please check the documentation for each function to see the permissions. Any other commands may result in unpredictable behavior and possible server failure. Only one failure_route can be armed for a request. If you use many times t_on_failure(), only the last one has effect. Note that whenever failure_route is entered, RURI is set to value of the winning branch. Meaning of the parameters is as follows: failure_route - Reply route block to be called. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE. <function>t_on_failure</function> usage ... route { t_on_failure("1"); t_relay(); } failure_route[1] { seturi("sip:user@voicemail"); t_relay(); } ...
<function moreinfo="none">t_on_reply(reply_route)</function> Sets reply routing block, to which control is passed each time a reply (provisional or final) for the transaction is received. The route is not called for local generated replies! In the referred block, you can inspect the reply and perform text operations on it. As not all functions are available from this type of route, please check the documentation for each function to see the permissions. Any other commands may result in unpredictable behavior and possible server failure. If called from branch route, the reply route will be set only for the current branch - that's it, it will be called only for relies belonging to that particular branch. Of course, from branch route, you can set different reply routes for each branch. When called from a non-branc route, the reply route will be globally set for tha current transaction - it will be called for all replies belonging to that transaction. NOTE that only one> onreply_route can be armed for a transaction. If you use many times t_on_reply(), only the last one has effect. If the processed reply is provisionla reply (1xx code), by calling the drop() function (exported by core), the execution of the route will end and the reply will not be forwarded further. Meaning of the parameters is as follows: reply_route - Reply route block to be called. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE. <function>t_on_reply</function> usage ... route { seturi("sip:bob@opensips.org"); # first branch append_branch("sip:alice@opensips.org"); # second branch t_on_reply("global"); # the "global" reply route # is set the whole transaction t_on_branch("1"); t_relay(); } branch_route[1] { if ($rU=="alice") t_on_reply("alice"); # the "alice" reply route # is set only for second branch } onreply_route[alice] { xlog("received reply from alice\n"); } onreply_route[global] { if (t_check_status("1[0-9][0-9]")) { setflag(LOG_FLAG); log("provisional reply received\n"); if (t_check_status("183")) drop; } } ...
<function moreinfo="none">t_on_branch(branch_route)</function> Sets a branch route to be execute separately for each branch of the transaction before being sent out - changes in that route should reflect only on that branch. As not all functions are available from this type of route, please check the documentation for each function to see the permissions. Any other commands may result in unpredictable behavior and possible server failure. Only one branch_route can be armed for a request. If you use many time t_on_branch(), only the last one has effect. By calling the drop() function (exported by core), the execution of the branch route will end and the branch will not be forwarded further. Meaning of the parameters is as follows: branch_route - Branch route block to be called. This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE. <function>t_on_branch</function> usage ... route { t_on_branch("1"); t_relay(); } branch_route[1] { if (uri=~"bad_uri") { xlog("dropping branch $ru \n"); drop; } if (uri=~"GW_uri") { append_rpid(); } } ...
<function moreinfo="none">t_add_hdrs("sip_hdrs")</function> Attach a set of headers to the existing transaction - these headers will be appended to all requests related to the transaction (outgoing branches, local ACKS, CANCELs). <function>t_add_hdrs</function> usage ... t_add_hdrs("X-origin: 1.1.1.1\r\n"); ...
<function moreinfo="none">t_replicate(URI,[flags])</function> Replicates a request to another destination. No information due the replicated request (like reply code) will be forwarded to the original SIP UAC. The destination is specified by a SIP URI. If multiple destinations are to be used, the additional SIP URIs have to be set as branches. The function may take as parameter an optional set of flags for controlling the internal behaviour - for description see the above t_relay([flags]) function. Note that only 0x4 is applicable here. This functions can be used from REQUEST_ROUTE. <function>t_replicate</function> usage ... t_replicate("sip:1.2.3.4:5060"); t_replicate("sip:1.2.3.4:5060;transport=tcp"); t_replicate("sip:1.2.3.4","0x4"); ...
<function moreinfo="none">t_write_req(info,fifo)</function> <function moreinfo="none">t_write_unix(info,sock)</function> Write via FIFO file or UNIX socket a lot of information regarding the request. Which information should be written may be control via the tw_append parameter. This functions can be used from REQUEST_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. <function>t_write_req/unix</function> usage ... modparam("tm","tw_append","append1:Email=$avp(email);UA=$ua") modparam("tm","tw_append","append2:body=$rb") ... t_write_req("voicemail/append1","/tmp/appx_fifo"); ... t_write_unix("logger/append2","/var/run/logger.sock"); ...
<function moreinfo="none">t_flush_flags()</function> Flush the flags from current request into the already created transaction. It make sense only in routing block if the transaction was created via t_newtran() and the flags have been altered since. This function can be used from REQUEST_ROUTE and BRANCH_ROUTE . <function>t_flush_flags</function> usage ... t_flush_flags(); ...
Exported pseudo-variables Exported pseudo-variables are listed in the next sections.
$T_branch_idx $T_branch_idx - the index (starting with 0 for the first branch) of the currently proccessed branch. This index makes sense only in BRANCH and REPLY routes (where there is a concept of per branch processing). In all the other types of routes, the value of this index will be NULL.
$T_reply_code $T_reply_code - the code of the reply, as follows: in request_route will be the last stateful sent reply; in reply_route will be the current processed reply; in failure_route will be the negative winning reply. In case of no-reply or error, '0' value is returned.
$T_fr_timeout $T_fr_timeout (R/W) - the timeout for the final reply to the current transaction With each different request received, $T_fr_timeout will initially be equal to the parameter. "$T_fr_timeout = NULL;" will reset it to .
$T_fr_inv_timeout $T_fr_inv_timeout (R/W) - the timeout for the final reply to an INVITE request, after a 1XX reply was received With each different request received, $T_fr_inv_timeout will initially be equal to the parameter. "$T_fr_inv_timeout = NULL;" will reset it to .
$T_ruri $T_ruri - the ruri of the current branch; this information is taken from the transaction structure, so you can access this information for any sip message (request/reply) that has a transaction.
$bavp(name) $bavp(name) - a particular type of avp that can have different values for each branch. They can only be used in BRANCH, REPLY and FAILURE routes. Otherwise NULL value is returned.
Exported MI Functions
<function moreinfo="none">t_uac_dlg</function> Generates and sends a local SIP request. Parameters: method - request method RURI - request SIP URI NEXT HOP - next hop SIP URI (OBP); use . if no value. socket - local socket to be used for sending the request; use . if no value. headers - set of additional headers to be added to the request; at least From and To headers must be specify) body - (optional, may not be present) request body (if present, requires the Content-Type and Content-length headers)
<function moreinfo="none">t_uac_cancel</function> Generates and sends a CANCEL for an existing SIP request. Parameters: callid - callid of the INVITE request to be cancelled. cseq - cseq of the INVITE request to be cancelled.
<function moreinfo="none">t_hash</function> Gets information about the load of TM internal hash table. Parameters: none
<function moreinfo="none">t_reply</function> Generates and sends a reply for an existing inbound SIP transaction. Parameters: code - reply code reason - reason phrase. trans_id - transaction identifier (has the hash_entry:label format) to_tag - To tag to be added to TO header new_headers - extra headers to be appended to the reply; use a dot (.) char only if there are no headers; body - (optional, may not be present) reply body (if present, requires the Content-Type and Content-length headers)
Exported statistics Exported statistics are listed in the next sections. All statistics except inuse_transactions can be reset.
received_replies Total number of total replies received by TM module.
relayed_replies Total number of replies received and relayed by TM module.
local_replies Total number of replies local generated by TM module.
UAS_transactions Total number of transactions created by received requests.
UAC_transactions Total number of transactions created by local generated requests.
2xx_transactions Total number of transactions completed with 2xx replies.
3xx_transactions Total number of transactions completed with 3xx replies.
4xx_transactions Total number of transactions completed with 4xx replies.
5xx_transactions Total number of transactions completed with 5xx replies.
6xx_transactions Total number of transactions completed with 6xx replies.
inuse_transactions Number of transactions existing in memory at current time.
opensips-2.2.2/modules/tm/doc/tm_devel.xml000066400000000000000000000012661300170765700205460ustar00rootroot00000000000000 &develguide;
Functions
<function moreinfo="none">load_tm(*import_structure)</function> For programmatic use only--import the TM API. See the cpl_c, acc or jabber modules to see how it works. Meaning of the parameters is as follows: import_structure - Pointer to the import structure - see struct tm_binds in modules/tm/tm_load.h
opensips-2.2.2/modules/tm/doc/tm_faq.xml000066400000000000000000000011011300170765700202020ustar00rootroot00000000000000 &faqguide; What happened with old cancel_call() function The function was replace (as functionality) by cancel_branch("a") - cancel all braches. How can I report a bug? Please follow the guidelines provided at: &osipsbugslink;. opensips-2.2.2/modules/tm/fix_lumps.h000066400000000000000000000055661300170765700176460ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-11-24 changed free_via_lump to free_via_clen_lump and make it * handle CONTENTLENGTH lumps also (andrei) * 2005-07-04 lumps in SHM or dup'ed lumps are not freed and an warning * message is logged (temporary fix) (andrei) * * * here, we delete message lumps which are generated in * core functions using pkg_malloc and applied to shmem * requests; not doing so would result ugly memory problems */ #ifndef _FIX_LUMPS_H #define _FIX_LUMPS_H /* used to delete attached via lumps from msg; msg can be either an original pkg msg, whose Via lump I want to delete before generating next branch, or a shmem-stored message processed during on_reply -- then I want to delete the Via lump for the same reason the other case when I want to delete them is when a message is stored in shmem for branch picking, forwarded lated and Via removal is applied to the shmem-ed message the same thing for Content-Length lumps (FIXME: this should be done in a nicer way) */ inline static void free_via_clen_lump( struct lump **list ) { struct lump *prev_lump, *lump, *a, *foo, *next; next=0; prev_lump=0; for(lump=*list;lump;lump=next) { next=lump->next; if (lump->type==HDR_VIA_T||lump->type==HDR_CONTENTLENGTH_T) { if (lump->flags & LUMPFLAG_SHMEM){ LM_CRIT("free_via_clen_lmp: lump %p, flags %x\n", lump, lump->flags); /* ty to continue */ } a=lump->before; while(a) { foo=a; a=a->before; if (!(foo->flags&LUMPFLAG_SHMEM)) free_lump(foo); if (!(foo->flags&LUMPFLAG_SHMEM)) pkg_free(foo); } a=lump->after; while(a) { foo=a; a=a->after; if (!(foo->flags&LUMPFLAG_SHMEM)) free_lump(foo); if (!(foo->flags&LUMPFLAG_SHMEM)) pkg_free(foo); } if (prev_lump) prev_lump->next = lump->next; else *list = lump->next; LM_DBG("Deleted lump [%p]\n", lump); if (!(lump->flags&LUMPFLAG_SHMEM)) free_lump(lump); if (!(lump->flags&LUMPFLAG_SHMEM)) pkg_free(lump); } else { /* store previous position */ prev_lump=lump; } } } #endif opensips-2.2.2/modules/tm/h_table.c000066400000000000000000000270161300170765700172230ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------- * 2003-03-06 200/INV to-tag list deallocation added; * setting "kill_reason" moved in here -- it is moved * from transaction state to a static var(jiri) * 2003-03-16 removed _TOTAG (jiri) * 2003-03-30 set_kr for requests only (jiri) * 2003-04-04 bug_fix: REQ_IN callback not called for local * UAC transactions (jiri) * 2003-09-12 timer_link->tg will be set only if EXTRA_DEBUG (andrei) * 2003-12-04 global callbacks replaceed with callbacks per transaction; * completion callback merged into them as LOCAL_COMPETED (bogdan) * 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri) * 2004-02-13 t->is_invite and t->local replaced with flags; * timer_link.payload removed (bogdan) * 2004-08-23 avp support added - move and remove avp list to/from * transactions (bogdan) * 2007-01-25 DNS failover at transaction level added (bogdan) */ #include #include "../../mem/shm_mem.h" #include "../../hash_func.h" #include "../../dprint.h" #include "../../md5utils.h" #include "../../ut.h" #include "../../error.h" #include "t_reply.h" #include "t_cancel.h" #include "t_stats.h" #include "h_table.h" #include "fix_lumps.h" /* free_via_clen_lump */ #include "t_hooks.h" #include "t_fwd.h" #include "t_lookup.h" /* indicates how much we have to shift the transaction pointer in order to * obtain a fair distribution on the tm timers */ int tm_timer_shift = 0; static enum kill_reason kr; /* pointer to the big table where all the transaction data lives */ static struct s_table* tm_table; int syn_branch = 1; void reset_kr(void) { kr = 0; } void set_kr( enum kill_reason _kr ) { kr|=_kr; } enum kill_reason get_kr(void) { return kr; } void lock_hash(int i) { lock(&tm_table->entrys[i].mutex); } void unlock_hash(int i) { unlock(&tm_table->entrys[i].mutex); } struct s_table* get_tm_table(void) { return tm_table; } unsigned int transaction_count( void ) { unsigned int i; unsigned int count; count=0; for (i=0; ientrys[i].cur_entries; return count; } void free_cell( struct cell* dead_cell ) { char *b; int i; struct sip_msg *rpl; struct totag_elem *tt, *foo; struct proxy_l *p; if ( has_tran_tmcbs( dead_cell, TMCB_TRANS_DELETED) ) run_trans_callbacks( TMCB_TRANS_DELETED, dead_cell, 0, 0, 0); empty_tmcb_list(&dead_cell->tmcb_hl); release_cell_lock( dead_cell ); tm_shm_lock(); /* UA Server */ if ( dead_cell->uas.request ) free_cloned_msg_unsafe( dead_cell->uas.request ); if ( dead_cell->uas.response.buffer.s ) tm_shm_free_unsafe( dead_cell->uas.response.buffer.s ); /* UA Clients */ for ( i =0 ; inr_of_outgoings; i++ ) { /* retransmission buffer */ if ( (b=dead_cell->uac[i].request.buffer.s) ) tm_shm_free_unsafe( b ); b=dead_cell->uac[i].local_cancel.buffer.s; if (b!=0 && b!=BUSY_BUFFER) tm_shm_free_unsafe( b ); rpl=dead_cell->uac[i].reply; if (rpl && rpl!=FAKED_REPLY && rpl->msg_flags&FL_SHM_CLONE) { free_cloned_msg_unsafe( rpl ); } if ( (p=dead_cell->uac[i].proxy)!=NULL ) { if ( p->host.h_addr_list ) tm_shm_free_unsafe( p->host.h_addr_list ); if ( p->dn ) { if ( p->dn->kids ) tm_shm_free_unsafe( p->dn->kids ); tm_shm_free_unsafe( p->dn ); } tm_shm_free_unsafe(p); } if (dead_cell->uac[i].path_vec.s) { tm_shm_free_unsafe(dead_cell->uac[i].path_vec.s); } if (dead_cell->uac[i].adv_address.s) { tm_shm_free_unsafe(dead_cell->uac[i].adv_address.s); } if (dead_cell->uac[i].adv_port.s) { tm_shm_free_unsafe(dead_cell->uac[i].adv_port.s); } if (dead_cell->uac[i].duri.s) { tm_shm_free_unsafe(dead_cell->uac[i].duri.s); } if (dead_cell->uac[i].user_avps) { tm_destroy_avp_list_unsafe( &dead_cell->uac[i].user_avps); } } /* collected to tags */ tt=dead_cell->fwded_totags; while(tt) { foo=tt->next; tm_shm_free_unsafe(tt->tag.s); tm_shm_free_unsafe(tt); tt=foo; } /* free the avp list */ if (dead_cell->user_avps) tm_destroy_avp_list_unsafe( &dead_cell->user_avps ); /* extra hdrs */ if ( dead_cell->extra_hdrs.s ) tm_shm_free_unsafe( dead_cell->extra_hdrs.s ); /* the cell's body */ tm_shm_free_unsafe( dead_cell ); tm_shm_unlock(); } static inline void init_synonym_id( struct cell *t ) { struct sip_msg *p_msg; int size; char *c; unsigned int myrand; if (!syn_branch) { p_msg=t->uas.request; if (p_msg) { /* char value of a proxied transaction is calculated out of header-fields forming transaction key */ char_msg_val( p_msg, t->md5 ); } else { /* char value for a UAC transaction is created randomly -- UAC is an originating stateful element which cannot be refreshed, so the value can be anything */ /* HACK : not long enough */ myrand=rand(); c=t->md5; size=MD5_LEN; memset(c, '0', size ); int2reverse_hex( &c, &size, myrand ); } } } static inline void init_branches(struct cell *t, unsigned int set) { unsigned int i; struct ua_client *uac; for(i=0;iuac[i]; uac->request.my_T = t; uac->request.branch = i; #ifdef EXTRA_DEBUG uac->request.fr_timer.tg = TG_FR; uac->request.retr_timer.tg = TG_RT; #endif uac->request.fr_timer.set = set; uac->request.retr_timer.set = set; uac->local_cancel.fr_timer.set = set; uac->local_cancel.retr_timer.set = set; uac->local_cancel=uac->request; } } struct cell* build_cell( struct sip_msg* p_msg, int full_uas) { struct cell* new_cell; int sip_msg_len; struct usr_avp **old; struct tm_callback *cbs, *cbs_tmp; unsigned short set; /* allocs a new cell */ new_cell = (struct cell*)shm_malloc(sizeof(struct cell) + context_size(CONTEXT_TRAN)); if ( !new_cell ) { ser_error=E_OUT_OF_MEM; return NULL; } /* filling with 0 */ memset( new_cell, 0, sizeof( struct cell ) + context_size(CONTEXT_TRAN)); /* get timer set id based on the transaction pointer, but * devide by 64 to avoid issues because pointer are 64 bits * aligned */ set = ( ((unsigned long)new_cell)>>tm_timer_shift ) % tm_table->timer_sets; /* UAS */ #ifdef EXTRA_DEBUG new_cell->uas.response.retr_timer.tg=TG_RT; new_cell->uas.response.fr_timer.tg=TG_FR; #endif new_cell->uas.response.retr_timer.set = set; new_cell->uas.response.fr_timer.set = set; new_cell->uas.response.my_T=new_cell; /* dcm: - local generation transactions should not inherit AVPs * - commpletely new message */ if(p_msg) { /* move the current avp list to transaction -bogdan */ old = set_avp_list( &new_cell->user_avps ); new_cell->user_avps = *old; *old = 0; /* set now the hash index & label, in case begin callbacks need them * we are now under hash lock, so it's safe - vlad */ new_cell->hash_index = p_msg->hash_index; new_cell->label = tm_table->entrys[ new_cell->hash_index].next_label; /* move the pending callbacks to transaction -bogdan */ if (p_msg->id==tmcb_pending_id) { new_cell->tmcb_hl = tmcb_pending_hl; tmcb_pending_hl.first = 0; } set_t(new_cell); /* enter callback, which may potentially want to parse some stuff, * before the request is shmem-ized */ if (has_reqin_tmcbs() ) run_reqin_callbacks( new_cell, p_msg, p_msg->REQ_METHOD); /* clean possible previous added vias/clen header or else they would * get propagated in the failure routes */ free_via_clen_lump(&p_msg->add_rm); new_cell->uas.request = sip_msg_cloner(p_msg,&sip_msg_len,full_uas?1:2); if (!new_cell->uas.request) goto error; new_cell->uas.end_request=((char*)new_cell->uas.request)+sip_msg_len; } /* UAC */ init_branches(new_cell, set); new_cell->fr_timeout = fr_timeout; new_cell->fr_inv_timeout = fr_inv_timeout; new_cell->relaied_reply_branch = -1; /* new_cell->T_canceled = T_UNDEFINED; */ #ifdef EXTRA_DEBUG new_cell->wait_tl.tg=TG_WT; new_cell->dele_tl.tg=TG_DEL; #endif new_cell->wait_tl.set = set; new_cell->dele_tl.set = set; init_synonym_id(new_cell); init_cell_lock( new_cell ); return new_cell; error: if (new_cell->user_avps) destroy_avp_list( &new_cell->user_avps ); if (new_cell->tmcb_hl.first) { for( cbs=new_cell->tmcb_hl.first ; cbs ; ) { cbs_tmp = cbs; cbs = cbs->next; shm_free( cbs_tmp ); } } shm_free(new_cell); set_t(NULL); /* unlink transaction AVP list and link back the global AVP list (bogdan)*/ reset_avps(); return NULL; } /* Release all the data contained by the hash table. All the aux. structures * as sems, lists, etc, are also released */ void free_hash_table(void) { struct cell* p_cell; struct cell* tmp_cell; int i; if (tm_table) { /* remove the data contained by each entry */ for( i = 0 ; ientrys)+i ); /* delete all synonyms at hash-collision-slot i */ p_cell=tm_table->entrys[i].first_cell; for( ; p_cell; p_cell = tmp_cell ) { tmp_cell = p_cell->next_cell; free_cell( p_cell ); } } shm_free(tm_table); } } /* */ struct s_table* init_hash_table( unsigned int timer_sets ) { int i; /*allocs the table*/ tm_table= (struct s_table*)shm_malloc( sizeof( struct s_table ) ); if ( !tm_table) { LM_ERR("no more share memory\n"); goto error; } memset( tm_table, 0, sizeof (struct s_table ) ); tm_table->timer_sets = timer_sets; /* inits the entrys */ for( i=0 ; ientrys)+i ); tm_table->entrys[i].next_label = rand(); } return tm_table; error: return 0; } /* Takes an already created cell and links it into hash table on the * appropriate entry. */ void insert_into_hash_table_unsafe( struct cell * p_cell, unsigned int _hash ) { struct entry* p_entry; p_cell->hash_index=_hash; /* locates the appropriate entry */ p_entry = &tm_table->entrys[ _hash ]; p_cell->label = p_entry->next_label++; if ( p_entry->last_cell ) { p_entry->last_cell->next_cell = p_cell; p_cell->prev_cell = p_entry->last_cell; } else p_entry->first_cell = p_cell; p_entry->last_cell = p_cell; /* update stats */ p_entry->cur_entries++; p_entry->acc_entries++; stats_trans_new( is_local(p_cell) ); } /* Un-link a cell from hash_table, but the cell itself is not released */ void remove_from_hash_table_unsafe( struct cell * p_cell) { struct entry* p_entry = &(tm_table->entrys[p_cell->hash_index]); if ( p_cell->prev_cell ) p_cell->prev_cell->next_cell = p_cell->next_cell; else p_entry->first_cell = p_cell->next_cell; if ( p_cell->next_cell ) p_cell->next_cell->prev_cell = p_cell->prev_cell; else p_entry->last_cell = p_cell->prev_cell; # ifdef EXTRA_DEBUG if (p_entry->cur_entries==0) { LM_CRIT("bad things happened: cur_entries=0\n"); abort(); } # endif /* update stats */ p_entry->cur_entries--; if_update_stat(tm_enable_stats, tm_trans_inuse , -1 ); } opensips-2.2.2/modules/tm/h_table.h000066400000000000000000000276531300170765700172370ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-16 removed _TOTAG (jiri) * 2003-03-06 we keep a list of 200/INV to-tags now (jiri) * 2003-03-01 kr set through a function now (jiri) * 2003-12-04 callbacks per transaction added; completion callback * merge into them as LOCAL_COMPETED (bogdan) * 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri) * 2004-02-13 t->is_invite, t->local, t->noisy_ctimer replaced * with flags (bogdan) * 2004-08-23 avp support added - avp list linked in transaction (bogdan) * 2007-01-25 DNS failover at transaction level added (bogdan) */ #ifndef _H_TABLE_H #define _H_TABLE_H #include #include #include "../../parser/msg_parser.h" #include "../../proxy.h" #include "../../md5utils.h" #include "../../async.h" #include "../../usr_avp.h" #include "config.h" struct s_table; struct entry; struct cell; struct timer; struct retr_buf; #include "../../mem/shm_mem.h" #include "lock.h" #include "sip_msg.h" #include "t_reply.h" #include "t_hooks.h" #include "timer.h" #define LOCK_HASH(_h) lock_hash((_h)) #define UNLOCK_HASH(_h) unlock_hash((_h)) void lock_hash(int i); void unlock_hash(int i); #define NO_CANCEL ( (char*) 0 ) #define EXTERNAL_CANCEL ( (char*) -1) #define TYPE_LOCAL_CANCEL -1 #define TYPE_REQUEST 0 /* to be able to assess whether a script writer forgot to release a transaction and leave it for ever in memory, we mark it with operations done over it; if none of these flags is set and script is being left, it is a sign of script error and we need to release on writer's behalf REQ_FWDED means there is a UAC with final response timer ticking. If it hits, transaction will be completed. REQ_RPLD means that a transaction has been replied -- either it implies going to wait state, or for invite transactions FR timer is ticking until ACK arrives REQ_RLSD means that a transaction was put on wait explicitly from t_release_transaction REQ_EXIST means that this request is a retransmission which does not affect transactional state */ enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8 }; typedef void (*setkr_f)(enum kill_reason mykr); typedef struct retr_buf { int activ_type; /* set to status code if the buffer is a reply, 0 if request or -1 if local CANCEL */ str buffer; struct dest_info dst; /* a message can be linked just to retransmission and FR list */ struct timer_link retr_timer; struct timer_link fr_timer; enum lists retr_list; /*the cell that contains this retrans_buff*/ struct cell* my_T; unsigned int branch; }retr_buf_type; /* User Agent Server content */ typedef struct ua_server { struct sip_msg *request; char *end_request; struct retr_buf response; unsigned int status; /* keep to-tags for local 200 replies for INVITE -- * we need them for dialog-wise matching of ACKs; * the pointer shows to shmem-ed reply */ str local_totag; }ua_server_type; /* User Agent Client content */ typedef struct ua_client { struct retr_buf request; struct proxy_l *proxy; /* we maintain a separate copy of cancel rather than reuse the structure for original request; the original request is no longer needed but its delayed timer may fire and interfere with whoever tries to rewrite it */ struct retr_buf local_cancel; /* pointer to retransmission buffer where uri is printed; good for generating ACK/CANCEL */ str uri; /* destination uri (proxy) - needs to be freed */ str duri; /* the path vector used for this branch */ str path_vec; /* the advertised address used for this branch */ str adv_address; /* the advertised port used for this branch */ str adv_port; /* number of RR headers that were locally added for this branch */ unsigned int added_rr; /* if we store a reply (branch picking), this is where it is */ struct sip_msg *reply; /* if we don't store, we at least want to know the status */ short last_received; /* UAC specific flags */ short flags; /* script flags, specific to this branch */ int br_flags; /* the onreply_route to be processed only for this branch */ unsigned int on_reply; /* head list for avps */ struct usr_avp *user_avps; }ua_client_type; struct totag_elem { str tag; short acked; struct totag_elem *next; }; /* transaction's flags */ /* is the transaction's request an INVITE? */ #define T_IS_INVITE_FLAG (1<<0) /* is this a transaction generated by local request? */ #define T_IS_LOCAL_FLAG (1<<1) /* set to one if you want to disallow silent transaction dropping when C timer hits */ #define T_WAS_CANCELLED_FLAG (1<<3) /* transaction was cancelled hopbyhop */ #define T_HOPBYHOP_CANCEL_FLAG (1<<4) /* ACK must not be auto generated for the local transaction */ #define T_NO_AUTOACK_FLAG (1<<5) /* provisional replies must trigger callbacks for local transaction */ #define T_PASS_PROVISIONAL_FLAG (1<<6) /* do auto DNS failover */ #define T_NO_DNS_FAILOVER_FLAG (1<<7) /* transaction must not create new branches */ #define T_NO_NEW_BRANCHES_FLAG (1<<8) /* transaction must forward the REASON header (for CANCEL) */ #define T_CANCEL_REASON_FLAG (1<<9) /* transaction UAC's flags */ /* is the UAC pending for CANCEL ? */ #define T_UAC_TO_CANCEL_FLAG (1<<0) /* have the UAC received any replies? */ #define T_UAC_HAS_RECV_REPLY (1<<1) /* transaction context */ typedef struct cell { /* linking data */ struct cell* next_cell; struct cell* prev_cell; /* tells in which hash table entry the cell lives */ unsigned int hash_index; /* sequence number within hash collision slot */ unsigned int label; /* different information about the transaction */ unsigned int flags; /* how many processes are currently processing this transaction ; note that only processes working on a request/reply belonging to a transaction increase ref_count -- timers don't, since we rely on transaction state machine to clean-up all but wait timer when entering WAIT state and the wait timer is the only place from which a transaction can be deleted (if ref_count==0); good for protecting from conditions in which wait_timer hits and tries to delete a transaction whereas at the same time a delayed message belonging to the transaction is received */ volatile unsigned int ref_count; /* needed for generating local ACK/CANCEL for local transactions; all but cseq_n include the entire header field value, cseq_n only Cseq number; with local transactions, pointers point to outbound buffer, with proxied transactions to inbound request */ str from, callid, cseq_n, to; /* method shortcut -- for local transactions, pointer to outbound buffer, for proxies transactions pointer to original message; needed for reply matching */ str method; /* head of callback list */ struct tmcb_head_list tmcb_hl; /* bindings to wait and delete timer */ struct timer_link wait_tl; struct timer_link dele_tl; /* first branch - when serial forking is performed, keeps the first * branch for each step ; it allows proper branch selection */ int first_branch; /* number of forks */ int nr_of_outgoings; /* nr of replied branch; 0..MAX_BRANCHES=branch value, * -1 no reply, -2 local reply */ int relaied_reply_branch; /* UA Server */ struct ua_server uas; /* UA Clients */ struct ua_client uac[ MAX_BRANCHES ]; /* protection against concurrent reply processing */ ser_lock_t reply_mutex; /* the route to take if no final positive reply arrived */ unsigned int on_negative; /* the onreply_route to be processed if registered to do so */ unsigned int on_reply; /* the branch_route to be processed separately for each branch */ unsigned int on_branch; int fr_timeout; /* final reply timeout (sec) */ int fr_inv_timeout; /* final reply timeout for an INVITE, after 1XX (sec) */ /* MD5checksum (meaningful only if syn_branch=0) */ char md5[MD5_LEN]; #ifdef EXTRA_DEBUG /* scheduled for deletion ? */ short damocles; #endif /* to-tags of 200/INVITEs which were received from downstream and * forwarded or passed to UAC; note that there can be arbitrarily * many due to downstream forking; */ struct totag_elem *fwded_totags; /* list with user avp */ struct usr_avp *user_avps; /* holders for higher contexts */ void *dialog_ctx; /* extra T headers */ str extra_hdrs; }cell_type; /* double-linked list of cells with hash synonyms */ typedef struct entry { struct cell* first_cell; struct cell* last_cell; /* currently highest sequence number in a synonym list */ unsigned int next_label; /* sync mutex */ ser_lock_t mutex; unsigned long acc_entries; unsigned long cur_entries; }entry_type; /* transaction table */ struct s_table { /* table of hash entries; each of them is a list of synonyms */ struct entry entrys[ TM_TABLE_ENTRIES ]; /* we keep it here just as a shortcut, we need it for assigning * a transaction to a specific timer set */ unsigned short timer_sets; }; #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) #define get_retr_timer_payload(_tl_) \ list_entry( _tl_, struct retr_buf, retr_timer) #define get_fr_timer_payload(_tl_) \ list_entry( _tl_, struct retr_buf, fr_timer) #define get_wait_timer_payload(_tl_) \ list_entry( _tl_, struct cell, wait_tl) #define get_dele_timer_payload(_tl_) \ list_entry( _tl_, struct cell, dele_tl) #define get_T_from_reply_rb(_rb_) \ list_entry( list_entry( _rb_, (struct ua_server), response),\ struct cell, uas) #define get_T_from_request_rb(_rb_, _br_) \ list_entry( list_entry( (rb_, (struct ua_client), request) - \ (_br_)*sizeof(struct retr_buf), struct cell, uas) #define get_T_from_cancel_rb(_rb_, _br_) \ list_entry( list_entry( (rb_, (struct ua_client), local_cancel) - \ (_br_)*sizeof(struct retr_buf), struct cell, uas) #define is_invite(_t_) ((_t_)->flags&T_IS_INVITE_FLAG) #define is_local(_t_) ((_t_)->flags&T_IS_LOCAL_FLAG) #define was_cancelled(_t_) ((_t_)->flags&T_WAS_CANCELLED_FLAG) #define is_hopbyhop_cancel(_t_) ((_t_)->flags&T_HOPBYHOP_CANCEL_FLAG) #define no_autoack(_t_) ((_t_)->flags&T_NO_AUTOACK_FLAG) #define pass_provisional(_t_) ((_t_)->flags&T_PASS_PROVISIONAL_FLAG) #define no_new_branches(_t_) ((_t_)->flags&T_NO_NEW_BRANCHES_FLAG) extern int syn_branch; extern int fr_timeout; extern int fr_inv_timeout; extern int tm_timer_shift; void reset_kr(); void set_kr( enum kill_reason kr ); enum kill_reason get_kr(); struct s_table* get_tm_table( void ); struct s_table* init_hash_table(unsigned int timer_sets); void free_hash_table( void ); void free_cell( struct cell* dead_cell ); struct cell* build_cell( struct sip_msg* p_msg, int full_uas ); void remove_from_hash_table_unsafe( struct cell * p_cell); #ifdef OBSOLETED void insert_into_hash_table( struct cell * p_cell, unsigned int _hash); #endif void insert_into_hash_table_unsafe( struct cell * p_cell, unsigned int _hash ); unsigned int transaction_count( void ); /* Unix socket variant */ int unixsock_hash(str* msg); #endif opensips-2.2.2/modules/tm/lock.c000066400000000000000000000164301300170765700165530ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-17 converted to locking.h (andrei) * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) */ #include #include "lock.h" #include "timer.h" #include "../../dprint.h" #ifndef GEN_LOCK_T_PREFERED /* semaphore probing limits */ #define SEM_MIN 16 #define SEM_MAX 4096 /* we implement mutex here using lock sets; as the number of semaphores may be limited (e.g. sysv) and number of synchronized elements high, we partition the synced SER elements and share semaphores in each of the partitions; we try to use as many semaphores as OS gives us for finest granularity. we allocate the locks according to the following plans: 1) transaction timer lists have each a semaphore in a semaphore set 2) retransmission timer lists have each a semaphore in a semaphore set 3) we allocate a semaphore set for hash_entries and try to use as many semaphores in it as OS allows; we partition the hash_entries by available semaphores which are shared in each partition 4) cells get always the same semaphore as its hash entry in which they live */ /* and the maximum number of semaphores in the entry_semaphore set */ static int sem_nr; gen_lock_set_t* timer_semaphore=0; gen_lock_set_t* entry_semaphore=0; gen_lock_set_t* reply_semaphore=0; #endif /* timer group locks */ static ser_lock_t* timer_group_lock=0; /* pointer to a TG_NR lock array, it's safer if we alloc this in shared mem ( required for fast lock ) */ /* initialize the locks; return 0 on success, -1 otherwise */ int lock_initialize( unsigned int timer_sets ) { int i; #ifndef GEN_LOCK_T_PREFERED int probe_run; #endif /* first try allocating semaphore sets with fixed number of semaphores */ LM_DBG("lock initialization started\n"); timer_group_lock=shm_malloc(timer_sets*TG_NR*sizeof(ser_lock_t)); if (timer_group_lock==0){ LM_CRIT("no more share mem\n"); goto error; } #ifdef GEN_LOCK_T_PREFERED for(i=0;ireply_mutex); #else cell->reply_mutex.semaphore_set=reply_semaphore; cell->reply_mutex.semaphore_index = cell->hash_index % sem_nr; #endif /* GEN_LOCK_T_PREFERED */ return 0; } int init_entry_lock( struct s_table* ht, struct entry *entry ) { #ifdef GEN_LOCK_T_PREFERED lock_init(&entry->mutex); #else /* just advice which of the available semaphores to use; specifically, all entries are partitioned into as many partitions as number of available semaphores allows */ entry->mutex.semaphore_set=entry_semaphore; entry->mutex.semaphore_index = ( ((char *)entry - (char *)(ht->entrys ) ) / sizeof(struct entry) ) % sem_nr; #endif return 0; } int release_cell_lock( struct cell *cell ) { #ifndef GEN_LOCK_T_PREFERED /* don't do anything here -- the init_*_lock procedures just advised on usage of shared semaphores but did not generate them */ #endif return 0; } int release_entry_lock( struct entry *entry ) { /* the same as above */ return 0; } int release_timerlist_lock( struct timer *timerlist ) { /* the same as above */ return 0; } int init_timerlist_lock( unsigned int set, enum lists timerlist_id) { get_timertable()[set].timers[timerlist_id].mutex= &(timer_group_lock[ set*TG_NR + timer_group[timerlist_id] ]); return 0; } opensips-2.2.2/modules/tm/lock.h000066400000000000000000000057401300170765700165620ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-17 converted to locking.h (andrei) * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) */ #ifndef __lock_h #define __lock_h #include "../../dprint.h" #include "../../locking.h" #ifdef GEN_LOCK_T_PREFERED #define ser_lock_t gen_lock_t #else /* typedef to structure we use for mutexing; currently, index to a semaphore set identifier now */ typedef struct { gen_lock_set_t* semaphore_set; int semaphore_index; } ser_lock_t; #endif enum timer_groups { TG_FR, TG_WT, TG_DEL, TG_RT, TG_NR }; /* extern ser_lock_t timer_group_lock[TG_NR]; */ #include "h_table.h" #include "timer.h" /* Uni*x permissions for IPC */ #define IPC_PERMISSIONS 0666 int lock_initialize( unsigned int timer_sets ); void lock_cleanup(); #ifdef DBG_LOCK #define lock(_s) _lock( (_s), __FILE__, __FUNCTION__, __LINE__ ) #define unlock(_s) _unlock( (_s), __FILE__, __FUNCTION__, __LINE__ ) #else #define lock(_s) _lock( (_s) ) #define unlock(_s) _unlock( (_s) ) #endif int init_cell_lock( struct cell *cell ); int init_entry_lock( struct s_table* ht, struct entry *entry ); int release_cell_lock( struct cell *cell ); int release_entry_lock( struct entry *entry ); int release_timerlist_lock( struct timer *timerlist ); /* lock semaphore s */ #ifdef DBG_LOCK static inline void _lock(ser_lock_t* s , const char *file, const char *function, unsigned int line) #else static inline void _lock( ser_lock_t* s ) #endif { #ifdef DBG_LOCK LM_DBG("lock : entered from %s , %s(%d)\n", function, file, line ); #endif #ifdef GEN_LOCK_T_PREFERED lock_get(s); #else lock_set_get(s->semaphore_set, s->semaphore_index); #endif } #ifdef DBG_LOCK static inline void _unlock(ser_lock_t* s, const char *file, const char *function, unsigned int line) #else static inline void _unlock( ser_lock_t* s ) #endif { #ifdef DBG_LOCK LM_DBG("unlock : entered from %s, %s:%d\n", file, function, line ); #endif #ifdef GEN_LOCK_T_PREFERED lock_release(s); #else lock_set_release( s->semaphore_set, s->semaphore_index ); #endif } int init_timerlist_lock( unsigned int set, enum lists timerlist_id); #endif opensips-2.2.2/modules/tm/mi.c000066400000000000000000000421071300170765700162300ustar00rootroot00000000000000/* * Header file for TM MI functions * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-12-04 created (bogdan) */ #include #include "../../parser/parse_from.h" #include "mi.h" #include "h_table.h" #include "t_lookup.h" #include "t_reply.h" #include "t_cancel.h" #include "dlg.h" #include "callid.h" #include "uac.h" struct str_list { str s; struct str_list *next; }; #define skip_hf(_hf) \ (((_hf)->type == HDR_FROM_T) || \ ((_hf)->type == HDR_TO_T) || \ ((_hf)->type == HDR_CALLID_T) || \ ((_hf)->type == HDR_CSEQ_T)) /************** Helper functions (from previous FIFO impl) *****************/ /* * check if the request pushed via MI is correctly formed */ static inline struct mi_root* mi_check_msg(struct sip_msg* msg, str* method, str* body, int* cseq, str* callid) { struct cseq_body *parsed_cseq; if (body && body->len && !msg->content_type) return init_mi_tree( 400, MI_SSTR("Content-Type missing")); if (body && body->len && msg->content_length) return init_mi_tree( 400, MI_SSTR("Content-Length disallowed")); if (!msg->to) return init_mi_tree( 400, MI_SSTR("To missing")); if (!msg->from) return init_mi_tree( 400, MI_SSTR("From missing")); /* we also need to know if there is from-tag and add it otherwise */ if (parse_from_header(msg) < 0) return init_mi_tree( 400, MI_SSTR("Error in From")); if (msg->cseq && (parsed_cseq = get_cseq(msg))) { if (str2int( &parsed_cseq->number, (unsigned int*)cseq)!=0) return init_mi_tree( 400, MI_SSTR("Bad CSeq number")); if (parsed_cseq->method.len != method->len || memcmp(parsed_cseq->method.s, method->s, method->len) !=0 ) return init_mi_tree( 400, MI_SSTR("CSeq method mismatch")); } else { *cseq = -1; } if (msg->callid) { callid->s = msg->callid->body.s; callid->len = msg->callid->body.len; } else { callid->s = 0; callid->len = 0; } return 0; } static inline struct str_list *new_str(char *s, int len, struct str_list **last, int *total) { struct str_list *new; new=pkg_malloc(sizeof(struct str_list)); if (!new) { LM_ERR("no more pkg mem\n"); return 0; } new->s.s=s; new->s.len=len; new->next=0; (*last)->next=new; *last=new; *total+=len; return new; } static inline char *get_hfblock( str *uri, struct hdr_field *hf, int *l, struct socket_info** send_sock) { struct str_list sl, *last, *new, *i, *foo; int hf_avail, frag_len, total_len; char *begin, *needle, *dst, *ret, *d; str *sock_name, *portname; union sockaddr_union to_su; ret=0; /* pessimist: assume failure */ total_len=0; last=&sl; last->next=0; portname=sock_name=0; for (; hf; hf=hf->next) { if (skip_hf(hf)) continue; begin=needle=hf->name.s; hf_avail=hf->len; /* substitution loop */ while(hf_avail) { d=memchr(needle, SUBST_CHAR, hf_avail); if (!d || d+1>=needle+hf_avail) { /* nothing to substitute */ new=new_str(begin, hf_avail, &last, &total_len); if (!new) goto error; break; } else { frag_len=d-begin; d++; /* d not at the second substitution char */ switch(*d) { case SUBST_CHAR: /* double SUBST_CHAR: IP */ /* string before substitute */ new=new_str(begin, frag_len, &last, &total_len); if (!new) goto error; /* substitute */ if (!sock_name) { if (*send_sock==0){ *send_sock=uri2sock(0, uri, &to_su,PROTO_NONE); if (!*send_sock) { LM_ERR("send_sock failed\n"); goto error; } } sock_name=&(*send_sock)->address_str; portname=&(*send_sock)->port_no_str; } new=new_str(sock_name->s, sock_name->len, &last, &total_len ); if (!new) goto error; /* inefficient - FIXME --andrei*/ new=new_str(":", 1, &last, &total_len); if (!new) goto error; new=new_str(portname->s, portname->len, &last, &total_len ); if (!new) goto error; /* keep going ... */ begin=needle=d+1;hf_avail-=frag_len+2; continue; default: /* no valid substitution char -- keep going */ hf_avail-=frag_len+1; needle=d; } } /* possible substitute */ } /* substitution loop */ /* proceed to next header */ /* new=new_str(CRLF, CRLF_LEN, &last, &total_len ); if (!new) goto error; */ LM_DBG("one more hf processed\n"); } /* header loop */ /* construct a single header block now */ ret=pkg_malloc(total_len); if (!ret) { LM_ERR("no pkg mem for hf block\n"); goto error; } i=sl.next; dst=ret; while(i) { foo=i; i=i->next; memcpy(dst, foo->s.s, foo->s.len); dst+=foo->s.len; pkg_free(foo); } *l=total_len; return ret; error: i=sl.next; while(i) { foo=i; i=i->next; pkg_free(foo); } *l=0; return 0; } static inline void mi_print_routes( struct mi_node *node, dlg_t* dlg) { #define MI_ROUTE_PREFIX_S "Route: " #define MI_ROUTE_PREFIX_LEN (sizeof(MI_ROUTE_PREFIX_S)-1) #define MI_ROUTE_SEPARATOR_S ", " #define MI_ROUTE_SEPARATOR_LEN (sizeof(MI_ROUTE_SEPARATOR_S)-1) rr_t* ptr; int len; char *p, *s; ptr = dlg->hooks.first_route; if (ptr==NULL) { add_mi_node_child( node, 0, 0, 0, ".",1); return; } len = MI_ROUTE_PREFIX_LEN; for( ; ptr ; ptr=ptr->next) len += ptr->len + MI_ROUTE_SEPARATOR_LEN*(ptr->next!=NULL); if (dlg->hooks.last_route) len += dlg->hooks.last_route->len + 2; s = pkg_malloc( len ); if (s==0) { LM_ERR("no more pkg mem\n"); return; } p = s; memcpy( p, MI_ROUTE_PREFIX_S, MI_ROUTE_PREFIX_LEN); p += MI_ROUTE_PREFIX_LEN; for( ptr = dlg->hooks.first_route ; ptr ; ptr=ptr->next) { memcpy( p, ptr->nameaddr.name.s, ptr->len); p += ptr->len; if (ptr->next) { memcpy( p, MI_ROUTE_SEPARATOR_S, MI_ROUTE_SEPARATOR_LEN); p += MI_ROUTE_SEPARATOR_LEN; } } if (dlg->hooks.last_route) { *(p++) = '<'; memcpy( p, dlg->hooks.last_route->s, dlg->hooks.last_route->len); p += dlg->hooks.last_route->len; *(p++) = '>'; } add_mi_node_child( node, MI_DUP_VALUE, 0, 0, s, len); pkg_free(s); } static inline int mi_print_uris( struct mi_node *node, struct sip_msg* reply) { dlg_t* dlg; if (reply==0) goto empty; dlg = (dlg_t*)shm_malloc(sizeof(dlg_t)); if (!dlg) { LM_ERR("no shm memory left\n"); return -1; } memset(dlg, 0, sizeof(dlg_t)); if (dlg_response_uac(dlg, reply) < 0) { LM_ERR("failed to create dialog\n"); free_dlg(dlg); return -1; } if (dlg->state != DLG_CONFIRMED) { free_dlg(dlg); goto empty; } if (dlg->hooks.request_uri->s) { add_mi_node_child( node, MI_DUP_VALUE, 0, 0, dlg->hooks.request_uri->s, dlg->hooks.request_uri->len); } else { add_mi_node_child( node, 0, 0, 0, ".",1); } if (dlg->hooks.next_hop->s) { add_mi_node_child( node, MI_DUP_VALUE, 0, 0, dlg->hooks.next_hop->s, dlg->hooks.next_hop->len); } else { add_mi_node_child( node, 0, 0, 0, ".",1); } mi_print_routes( node, dlg); free_dlg(dlg); return 0; empty: add_mi_node_child( node, 0, 0, 0, ".",1); add_mi_node_child( node, 0, 0, 0, ".",1); add_mi_node_child( node, 0, 0, 0, ".",1); return 0; } static void mi_uac_dlg_hdl( struct cell *t, int type, struct tmcb_params *ps ) { struct mi_handler *mi_hdl; struct mi_root *rpl_tree; str text; LM_DBG("MI UAC generated status %d\n", ps->code); if (!*ps->param) return; mi_hdl = (struct mi_handler *)(*ps->param); rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) goto done; if (ps->rpl==FAKED_REPLY) { get_reply_status( &text, ps->rpl, ps->code); if (text.s==0) { LM_ERR("get_reply_status failed\n"); rpl_tree = 0; goto done; } add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, 0, 0, text.s, text.len); pkg_free(text.s); mi_print_uris( &rpl_tree->node, 0 ); add_mi_node_child( &rpl_tree->node, 0, 0, 0, ".",1); } else { addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%d %.*s", ps->rpl->first_line.u.reply.statuscode, ps->rpl->first_line.u.reply.reason.len, ps->rpl->first_line.u.reply.reason.s); mi_print_uris( &rpl_tree->node, ps->rpl); add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, 0, 0, ps->rpl->headers->name.s, ps->rpl->len-(ps->rpl->headers->name.s - ps->rpl->buf)); } LM_DBG("mi_callback successfully completed\n"); done: if (ps->code >= 200) { mi_hdl->handler_f( rpl_tree, mi_hdl, 1 /*done*/ ); *ps->param = 0; } else { mi_hdl->handler_f( rpl_tree, mi_hdl, 0 ); } } /**************************** MI functions ********************************/ /* Syntax of "t_uac_dlg" : method RURI NEXT_HOP socket headers [Body] */ struct mi_root* mi_tm_uac_dlg(struct mi_root* cmd_tree, void* param) { static char err_buf[MAX_REASON_LEN]; static struct sip_msg tmp_msg; static dlg_t dlg; struct mi_root *rpl_tree; struct mi_node *node; struct sip_uri pruri; struct sip_uri pnexthop; struct socket_info* sock; str *method; str *ruri; str *nexthop; str *socket; str *hdrs; str *body; str s; str callid = {0,0}; int sip_error; int proto; int port; int cseq = 0; int n; for( n=0,node = cmd_tree->node.kids; n<6 && node ; n++,node=node->next ); if ( !(n==5 || n==6) || node!=0) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* method name (param 1) */ node = cmd_tree->node.kids; method = &node->value; /* RURI (param 2) */ node = node->next; ruri = &node->value; if (parse_uri( ruri->s, ruri->len, &pruri) < 0 ) return init_mi_tree( 400, MI_SSTR("Invalid RURI")); /* nexthop RURI (param 3) */ node = node->next; nexthop = &node->value; if (nexthop->len==1 && nexthop->s[0]=='.') { nexthop = 0; } else { if (parse_uri( nexthop->s, nexthop->len, &pnexthop) < 0 ) return init_mi_tree( 400, MI_SSTR("Invalid NEXTHOP")); } /* socket (param 4) */ node = node->next; socket = &node->value; if (socket->len==1 && socket->s[0]=='.' ) { sock = 0; } else { if (parse_phostport( socket->s, socket->len, &s.s, &s.len, &port,&proto)!=0) return init_mi_tree( 404, MI_SSTR("Invalid local socket")); set_sip_defaults( port, proto); sock = grep_sock_info( &s, (unsigned short)port, proto); if (sock==0) return init_mi_tree( 404, MI_SSTR("Local socket not found")); } /* new headers (param 5) */ node = node->next; if (node->value.len==1 && node->value.s[0]=='.') hdrs = 0; else { hdrs = &node->value; /* use SIP parser to look at what is in the FIFO request */ memset( &tmp_msg, 0, sizeof(struct sip_msg)); tmp_msg.len = hdrs->len; tmp_msg.buf = tmp_msg.unparsed = hdrs->s; if (parse_headers( &tmp_msg, HDR_EOH_F, 0) == -1 ) return init_mi_tree( 400, MI_SSTR("Bad headers")); } /* body (param 5 - optional) */ node = node->next; if (node) body = &node->value; else body = 0; /* at this moment, we collected all the things we got, let's * verify user has not forgotten something */ rpl_tree = mi_check_msg( &tmp_msg, method, body, &cseq, &callid); if (rpl_tree) { if (tmp_msg.headers) free_hdr_field_lst(tmp_msg.headers); return rpl_tree; } s.s = get_hfblock( nexthop ? nexthop : ruri, tmp_msg.headers, &s.len, &sock); if (s.s==0) { if (tmp_msg.headers) free_hdr_field_lst(tmp_msg.headers); return 0; } memset( &dlg, 0, sizeof(dlg_t)); /* Fill in Call-ID, use given Call-ID if * present and generate it if not present */ if (callid.s && callid.len) dlg.id.call_id = callid; else generate_callid(&dlg.id.call_id); /* We will not fill in dlg->id.rem_tag because * if present it will be printed within To HF */ /* Generate fromtag if not present */ if (!(get_from(&tmp_msg)->tag_value.len&&get_from(&tmp_msg)->tag_value.s)) generate_fromtag(&dlg.id.loc_tag, &dlg.id.call_id); /* Fill in CSeq */ if (cseq!=-1) dlg.loc_seq.value = cseq; else dlg.loc_seq.value = DEFAULT_CSEQ; dlg.loc_seq.is_set = 1; if (get_from(&tmp_msg)->tag_value.len!=0) dlg.id.loc_tag = get_from(&tmp_msg)->tag_value; if (get_to(&tmp_msg)->tag_value.len!=0) dlg.id.rem_tag = get_to(&tmp_msg)->tag_value; dlg.loc_uri = get_from(&tmp_msg)->uri; dlg.rem_uri = get_to(&tmp_msg)->uri; dlg.loc_dname = get_from(&tmp_msg)->display; dlg.rem_dname = get_to(&tmp_msg)->display; dlg.hooks.request_uri = ruri; dlg.hooks.next_hop = (nexthop ? nexthop : ruri); dlg.send_sock = sock; if (cmd_tree->async_hdl==NULL) n = t_uac( method, &s, body, &dlg, 0, 0, 0); else n = t_uac( method, &s, body, &dlg, mi_uac_dlg_hdl, (void*)cmd_tree->async_hdl, 0); pkg_free(s.s); if (tmp_msg.headers) free_hdr_field_lst(tmp_msg.headers); if (n<=0) { /* error */ rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; n = err2reason_phrase( n, &sip_error, err_buf, sizeof(err_buf), "MI/UAC") ; if (n > 0 ) addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%d %.*s", sip_error, n, err_buf); else add_mi_node_child( &rpl_tree->node, 0, 0, 0, "500 MI/UAC failed", 17); return rpl_tree; } else { if (cmd_tree->async_hdl==NULL) return init_mi_tree( 202, MI_SSTR("Accepted")); else return MI_ROOT_ASYNC_RPL; } } /* Syntax of "t_uac_cancel" : callid cseq */ struct mi_root* mi_tm_cancel(struct mi_root* cmd_tree, void* param) { struct mi_node *node; struct cell *trans; node = cmd_tree->node.kids; if ( !node || !node->next || node->next->next) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if( t_lookup_callid( &trans, node->value, node->next->value) < 0 ) return init_mi_tree( 481, MI_SSTR("No such transaction")); /* cancel the call */ LM_DBG("cancelling transaction %p\n",trans); cancel_uacs( trans, ~0/*all branches*/); UNREF(trans); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } /* Syntax of "t_hash" : no nodes */ struct mi_root* mi_tm_hash(struct mi_root* cmd_tree, void* param) { struct mi_root* rpl_tree= NULL; struct mi_node* rpl; struct mi_node* node; struct mi_attr* attr; struct s_table* tm_t; char *p; int i; int len; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; tm_t = get_tm_table(); for (i=0; ientrys[i].cur_entries, &len ); attr = add_mi_attr(node, MI_DUP_VALUE, "Current", 7, p, len ); if(attr == NULL) goto error; p = int2str((unsigned long)tm_t->entrys[i].acc_entries, &len ); attr = add_mi_attr(node, MI_DUP_VALUE, "Total", 5, p, len ); if(attr == NULL) goto error; } return rpl_tree; error: free_mi_tree(rpl_tree); return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN); } /* Syntax of "t_reply" : code reason trans_id to_tag new headers [Body] */ struct mi_root* mi_tm_reply(struct mi_root* cmd_tree, void* param) { struct mi_node* node; unsigned int hash_index; unsigned int hash_label; unsigned int rpl_code; struct cell *trans; str *reason; str *totag; str *new_hdrs; str *body; str tmp; char *p; int n; for( n=0,node = cmd_tree->node.kids; n<6 && node ; n++,node=node->next ); if ( !(n==5 || n==6) || node!=0) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); /* get all info from the command */ /* reply code (param 1) */ node = cmd_tree->node.kids; if (str2int( &node->value, &rpl_code)!=0 || rpl_code>=700) return init_mi_tree( 400, MI_SSTR("Invalid reply code")); /* reason text (param 2) */ node = node->next; reason = &node->value; /* trans_id (param 3) */ node = node->next; tmp = node->value; p = memchr( tmp.s, ':', tmp.len); if ( p==NULL) return init_mi_tree( 400, MI_SSTR("Invalid trans_id")); tmp.len = p-tmp.s; if( str2int( &tmp, &hash_index)!=0 ) return init_mi_tree( 400, MI_SSTR("Invalid index in trans_id")); tmp.s = p+1; tmp.len = (node->value.s+node->value.len) - tmp.s; if( str2int( &tmp, &hash_label)!=0 ) return init_mi_tree( 400, MI_SSTR("Invalid label in trans_id")); if( t_lookup_ident( &trans, hash_index, hash_label)<0 ) return init_mi_tree( 404, MI_SSTR("Transaction not found")); /* to_tag (param 4) */ node = node->next; totag = &node->value; /* new headers (param 5) */ node = node->next; if (node->value.len==1 && node->value.s[0]=='.') new_hdrs = 0; else new_hdrs = &node->value; /* body (param 5 - optional) */ node = node->next; if (node) body = &node->value; else body = 0; n = t_reply_with_body( trans, rpl_code, reason, body, new_hdrs, totag); UNREF(trans); if (n<0) return init_mi_tree( 500, MI_SSTR("Reply failed")); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); } opensips-2.2.2/modules/tm/mi.h000066400000000000000000000025701300170765700162350ustar00rootroot00000000000000/* * Header file for TM MI functions * * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-12-04 created (bogdan) */ #ifndef _TM_MI_H_ #define _TM_MI_H_ #include "../../mi/mi.h" #define MI_TM_UAC "t_uac_dlg" #define MI_TM_CANCEL "t_uac_cancel" #define MI_TM_HASH "t_hash" #define MI_TM_REPLY "t_reply" struct mi_root* mi_tm_uac_dlg(struct mi_root* cmd_tree, void* param); struct mi_root* mi_tm_cancel(struct mi_root* cmd_tree, void* param); struct mi_root* mi_tm_hash(struct mi_root* cmd_tree, void* param); struct mi_root* mi_tm_reply(struct mi_root* cmd_tree, void* param); #endif opensips-2.2.2/modules/tm/sip_msg.c000066400000000000000000001116511300170765700172650ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2008-2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-23 - msg_cloner clones msg->from->parsed too (janakj) * 2003-01-29 - scratchpad removed (jiri) * 2003-02-25 - auth_body cloner added (janakj) * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-03-31 removed msg->repl_add_rm (andrei) * 2003-04-04 parsed uris are recalculated on cloning (jiri) * 2003-05-07 received, rport & i via shortcuts are also translated (andrei) * 2003-11-11 updated cloning of lump_rpl (bogdan) * 2004-03-31 alias shortcuts are also translated (andrei) * * * cloning a message into shared memory (TM keeps a snapshot * of messages in memory); note that many operations, which * allocate pkg memory (such as parsing) cannot be used with * a cloned message -- it would result in linking pkg structures * to shmem msg and eventually in a memory error * * the cloned message is stored in a single memory fragment to * save too many shm_mallocs -- these are expensive as they * not only take lookup in fragment table but also a shmem lock * operation (the same for shm_free) * */ #include #include "sip_msg.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../ut.h" #include "../../context.h" #include "../../parser/digest/digest.h" /* rounds to the first 4 byte multiple on 32 bit archs * and to the first 8 byte multiple on 64 bit archs */ #define ROUND4(s) \ (((s)+(sizeof(char*)-1))&(~(sizeof(char*)-1))) #define lump_len( _lump) \ (ROUND4(sizeof(struct lump)) +\ ROUND4(((_lump)->op==LUMP_ADD)?(_lump)->len:0)) #define lump_clone( _new,_old,_ptr) \ {\ (_new) = (struct lump*)(_ptr);\ memcpy( (_new), (_old), sizeof(struct lump) );\ (_new)->flags|=LUMPFLAG_SHMEM; \ (_ptr)+=ROUND4(sizeof(struct lump));\ if ( (_old)->op==LUMP_ADD) {\ (_new)->u.value = (char*)(_ptr);\ memcpy( (_new)->u.value , (_old)->u.value , (_old)->len);\ (_ptr)+=ROUND4((_old)->len);}\ } inline static struct via_body* via_body_cloner( char* new_buf, char *org_buf, struct via_body *param_org_via, char **p) { struct via_body *new_via; struct via_body *first_via, *last_via; struct via_body *org_via; first_via = last_via = 0; org_via = param_org_via; do { /* clones the via_body structure */ new_via = (struct via_body*)(*p); memcpy( new_via , org_via , sizeof( struct via_body) ); (*p) += ROUND4(sizeof( struct via_body )); /* hdr (str type) */ new_via->hdr.s=translate_pointer(new_buf,org_buf,org_via->hdr.s); /* name (str type) */ new_via->name.s=translate_pointer(new_buf,org_buf,org_via->name.s); /* version (str type) */ new_via->version.s= translate_pointer(new_buf,org_buf,org_via->version.s); /* transport (str type) */ new_via->transport.s= translate_pointer(new_buf,org_buf,org_via->transport.s); /* host (str type) */ new_via->host.s=translate_pointer(new_buf,org_buf,org_via->host.s); /* port_str (str type) */ new_via->port_str.s= translate_pointer(new_buf,org_buf,org_via->port_str.s); /* params (str type) */ new_via->params.s=translate_pointer(new_buf,org_buf,org_via->params.s); /* transaction id */ new_via->tid.s= translate_pointer(new_buf, org_buf, org_via->tid.s); /* comment (str type) */ new_via->comment.s= translate_pointer(new_buf,org_buf,org_via->comment.s); if ( org_via->param_lst ) { struct via_param *vp, *new_vp, *last_new_vp; for( vp=org_via->param_lst, last_new_vp=0 ; vp ; vp=vp->next ) { new_vp = (struct via_param*)(*p); memcpy( new_vp , vp , sizeof(struct via_param)); (*p) += ROUND4(sizeof(struct via_param)); new_vp->name.s=translate_pointer(new_buf,org_buf,vp->name.s); new_vp->value.s=translate_pointer(new_buf,org_buf,vp->value.s); new_vp->start=translate_pointer(new_buf,org_buf,vp->start); /* "translate" the shortcuts */ switch(new_vp->type){ case PARAM_BRANCH: new_via->branch = new_vp; break; case PARAM_RECEIVED: new_via->received = new_vp; break; case PARAM_RPORT: new_via->rport = new_vp; break; case PARAM_I: new_via->i = new_vp; break; case PARAM_ALIAS: new_via->alias = new_vp; break; case PARAM_MADDR: new_via->maddr = new_vp; break; } if (last_new_vp) last_new_vp->next = new_vp; else new_via->param_lst = new_vp; last_new_vp = new_vp; last_new_vp->next = NULL; } new_via->last_param = new_vp; }/*end if via has params */ if (last_via) last_via->next = new_via; else first_via = new_via; last_via = new_via; org_via = org_via->next; }while(org_via); return first_via; } static void uri_trans(char *new_buf, char *org_buf, struct sip_uri *uri) { int i; uri->user.s=translate_pointer(new_buf,org_buf,uri->user.s); uri->passwd.s=translate_pointer(new_buf,org_buf,uri->passwd.s); uri->host.s=translate_pointer(new_buf,org_buf,uri->host.s); uri->port.s=translate_pointer(new_buf,org_buf,uri->port.s); uri->params.s=translate_pointer(new_buf,org_buf,uri->params.s); uri->headers.s=translate_pointer(new_buf,org_buf,uri->headers.s); /* parameters */ uri->transport.s=translate_pointer(new_buf,org_buf,uri->transport.s); uri->ttl.s=translate_pointer(new_buf,org_buf,uri->ttl.s); uri->user_param.s=translate_pointer(new_buf,org_buf,uri->user_param.s); uri->maddr.s=translate_pointer(new_buf,org_buf,uri->maddr.s); uri->method.s=translate_pointer(new_buf,org_buf,uri->method.s); uri->lr.s=translate_pointer(new_buf,org_buf,uri->lr.s); uri->r2.s=translate_pointer(new_buf,org_buf,uri->r2.s); /* values */ uri->transport_val.s =translate_pointer(new_buf,org_buf,uri->transport_val.s); uri->ttl_val.s=translate_pointer(new_buf,org_buf,uri->ttl_val.s); uri->user_param_val.s =translate_pointer(new_buf,org_buf,uri->user_param_val.s); uri->maddr_val.s=translate_pointer(new_buf,org_buf,uri->maddr_val.s); uri->method_val.s=translate_pointer(new_buf,org_buf,uri->method_val.s); uri->lr_val.s=translate_pointer(new_buf,org_buf,uri->lr_val.s); uri->r2_val.s=translate_pointer(new_buf,org_buf,uri->r2_val.s); /* unknown params */ for( i=0; iu_name[i].s ; i++ ) { uri->u_name[i].s = translate_pointer(new_buf,org_buf,uri->u_name[i].s); uri->u_val[i].s = translate_pointer(new_buf,org_buf,uri->u_val[i].s); } } static inline struct auth_body* auth_body_cloner(char* new_buf, char *org_buf, struct auth_body *auth, char **p) { struct auth_body* new_auth; new_auth = (struct auth_body*)(*p); memcpy(new_auth , auth , sizeof(struct auth_body)); (*p) += ROUND4(sizeof(struct auth_body)); /* authorized field must be cloned elsewhere */ new_auth->digest.username.whole.s = translate_pointer(new_buf, org_buf, auth->digest.username.whole.s); new_auth->digest.username.user.s = translate_pointer(new_buf, org_buf, auth->digest.username.user.s); new_auth->digest.username.domain.s = translate_pointer(new_buf, org_buf, auth->digest.username.domain.s); new_auth->digest.realm.s = translate_pointer(new_buf, org_buf, auth->digest.realm.s); new_auth->digest.nonce.s = translate_pointer(new_buf, org_buf, auth->digest.nonce.s); new_auth->digest.uri.s = translate_pointer(new_buf, org_buf, auth->digest.uri.s); new_auth->digest.response.s = translate_pointer(new_buf, org_buf, auth->digest.response.s); new_auth->digest.alg.alg_str.s = translate_pointer(new_buf, org_buf, auth->digest.alg.alg_str.s); new_auth->digest.cnonce.s = translate_pointer(new_buf, org_buf, auth->digest.cnonce.s); new_auth->digest.opaque.s = translate_pointer(new_buf, org_buf, auth->digest.opaque.s); new_auth->digest.qop.qop_str.s = translate_pointer(new_buf, org_buf, auth->digest.qop.qop_str.s); new_auth->digest.nc.s = translate_pointer(new_buf, org_buf, auth->digest.nc.s); return new_auth; } static inline int clone_authorized_hooks(struct sip_msg* new, struct sip_msg* old) { struct hdr_field* ptr, *new_ptr, *hook1, *hook2; char stop = 0; get_authorized_cred(old->authorization, &hook1); if (!hook1) stop = 1; get_authorized_cred(old->proxy_auth, &hook2); if (!hook2) stop |= 2; ptr = old->headers; new_ptr = new->headers; while(ptr) { if (ptr == hook1) { if (!new->authorization || !new->authorization->parsed) { LM_CRIT("message cloner (authorization) failed\n"); return -1; } ((struct auth_body*)new->authorization->parsed)->authorized = new_ptr; stop |= 1; } if (ptr == hook2) { if (!new->proxy_auth || !new->proxy_auth->parsed) { LM_CRIT("message cloner (proxy_auth) failed\n"); return -1; } ((struct auth_body*)new->proxy_auth->parsed)->authorized = new_ptr; stop |= 2; } if (stop == 3) break; ptr = ptr->next; new_ptr = new_ptr->next; } return 0; } #define AUTH_BODY_SIZE sizeof(struct auth_body) #define HOOK_NOT_SET(hook) (new_msg->hook == org_msg->hook) /* next macro should only be called if hook is already set */ #define LINK_SIBLING_HEADER(_hook, _hdr) \ do { \ struct hdr_field *_itr; \ for (_itr=new_msg->_hook; _itr->sibling; _itr=_itr->sibling); \ _itr->sibling = _hdr; \ } while(0) #define LUMP_LIST_LEN(_len, list) \ do { \ struct lump* tmp, *chain; \ chain = (list); \ while (chain) \ { \ (_len) += lump_len(chain); \ tmp = chain->before; \ while ( tmp ) \ { \ (_len) += lump_len( tmp ); \ tmp = tmp->before; \ } \ tmp = chain->after; \ while ( tmp ) \ { \ (_len) += lump_len( tmp ); \ tmp = tmp->after; \ } \ chain = chain->next; \ } \ } while(0); #define RPL_LUMP_LIST_LEN(_len,_list) \ do { \ struct lump_rpl *_rpl_lump; \ for(_rpl_lump=_list ; _rpl_lump ; _rpl_lump=_rpl_lump->next) \ _len+=ROUND4(sizeof(struct lump_rpl))+ROUND4(_rpl_lump->text.len);\ }while(0) #define CLONE_LUMP_LIST(_p, anchor, list) \ do { \ struct lump* lump_tmp, *l; \ struct lump** lump_anchor2, **a; \ a = (anchor); \ l = (list); \ while (l) \ { \ lump_clone( (*a) , l , _p ); \ /*before list*/ \ lump_tmp = l->before; \ lump_anchor2 = &((*a)->before); \ while ( lump_tmp ) \ { \ lump_clone( (*lump_anchor2) , lump_tmp , _p ); \ lump_anchor2 = &((*lump_anchor2)->before); \ lump_tmp = lump_tmp->before; \ } \ /*after list*/ \ lump_tmp = l->after; \ lump_anchor2 = &((*a)->after); \ while ( lump_tmp ) \ { \ lump_clone( (*lump_anchor2) , lump_tmp , _p ); \ lump_anchor2 = &((*lump_anchor2)->after); \ lump_tmp = lump_tmp->after; \ } \ a = &((*a)->next); \ l = l->next; \ } \ } while(0) #define CLONE_RPL_LUMP_LIST( _p, _anchor, _list) \ do { \ struct lump_rpl *_rpl_lump, **_rpl_lump_anchor; \ _rpl_lump_anchor = (_anchor); \ for(_rpl_lump=(_list);_rpl_lump;_rpl_lump=_rpl_lump->next) { \ *(_rpl_lump_anchor)=(struct lump_rpl*)(_p); \ (_p) += ROUND4(sizeof( struct lump_rpl )); \ (*_rpl_lump_anchor)->flags = LUMP_RPL_SHMEM | \ (_rpl_lump->flags&(~(LUMP_RPL_NODUP|LUMP_RPL_NOFREE))); \ (*_rpl_lump_anchor)->text.len = _rpl_lump->text.len; \ (*_rpl_lump_anchor)->text.s = (_p); \ (_p) += ROUND4(_rpl_lump->text.len); \ memcpy((*_rpl_lump_anchor)->text.s,_rpl_lump->text.s,_rpl_lump->text.len);\ (*_rpl_lump_anchor)->next=0; \ _rpl_lump_anchor = &((*_rpl_lump_anchor)->next); \ }\ }while(0); /* Takes a SIP msg and makes of a clone on it in shared memory; the clone * is in a single memory chunks (all headers, lumps, etc). * Param "updatable" can be : * 0 - msg cannot be updated -> everything in the single mem chunk * 1 - msg can be updated -> new/dst URI, PATH, lumps are in separate * mem chunks, so they can be updated later * 2 - msg can be updated, but do not copy updatable part at cloning */ struct sip_msg* sip_msg_cloner( struct sip_msg *org_msg, int *sip_msg_len, int updatable) { unsigned int len, l1_len, l2_len, l3_len; struct hdr_field *hdr,*new_hdr,*last_hdr; struct via_body *via; struct via_param *prm; struct to_param *to_prm,*new_to_prm; struct sip_msg *new_msg; char *p; /*computing the length of entire sip_msg structure*/ len = ROUND4(sizeof( struct sip_msg )); /*we will keep only the original msg +ZT */ len += ROUND4(org_msg->len + 1); /*all the headers*/ for( hdr=org_msg->headers ; hdr ; hdr=hdr->next ) { /*size of header struct*/ len += ROUND4(sizeof( struct hdr_field)); switch (hdr->type) { case HDR_VIA_T: for (via=(struct via_body*)hdr->parsed;via;via=via->next) { len+=ROUND4(sizeof(struct via_body)); /*via param*/ for(prm=via->param_lst;prm;prm=prm->next) len+=ROUND4(sizeof(struct via_param )); } break; case HDR_TO_T: case HDR_FROM_T: /* From header might be unparsed */ if (hdr->parsed) { len+=ROUND4(sizeof(struct to_body)); /*to param*/ to_prm = ((struct to_body*)(hdr->parsed))->param_lst; for(;to_prm;to_prm=to_prm->next) len+=ROUND4(sizeof(struct to_param )); } break; case HDR_CSEQ_T: len+=ROUND4(sizeof(struct cseq_body)); break; case HDR_AUTHORIZATION_T: case HDR_PROXYAUTH_T: if (hdr->parsed) { len += ROUND4(AUTH_BODY_SIZE); } break; case HDR_CALLID_T: case HDR_CONTACT_T: case HDR_MAXFORWARDS_T: case HDR_ROUTE_T: case HDR_RECORDROUTE_T: case HDR_PATH_T: case HDR_CONTENTTYPE_T: case HDR_CONTENTLENGTH_T: case HDR_EXPIRES_T: case HDR_SUPPORTED_T: case HDR_PROXYREQUIRE_T: case HDR_UNSUPPORTED_T: case HDR_ALLOW_T: case HDR_EVENT_T: case HDR_ACCEPT_T: case HDR_ACCEPTLANGUAGE_T: case HDR_ORGANIZATION_T: case HDR_PRIORITY_T: case HDR_SUBJECT_T: case HDR_USERAGENT_T: case HDR_ACCEPTDISPOSITION_T: case HDR_CONTENTDISPOSITION_T: case HDR_DIVERSION_T: case HDR_RPID_T: case HDR_REFER_TO_T: case HDR_SESSION_EXPIRES_T: case HDR_MIN_SE_T: case HDR_PPI_T: case HDR_PAI_T: case HDR_PRIVACY_T: case HDR_RETRY_AFTER_T: case HDR_CALL_INFO_T: case HDR_WWW_AUTHENTICATE_T: case HDR_PROXY_AUTHENTICATE_T: /* we ignore them for now even if they have something parsed*/ break; default: if (hdr->parsed) { LM_WARN("header body ignored: %d\n", hdr->type ); } break; }/*switch*/ }/*for all headers*/ /* calculate the "updatable" part of the msg */ /* length of the data lump structures */ l1_len = l2_len = l3_len = 0; LUMP_LIST_LEN(l1_len, org_msg->add_rm); LUMP_LIST_LEN(l2_len, org_msg->body_lumps); RPL_LUMP_LIST_LEN(l3_len, org_msg->reply_lump); switch (updatable) { case 0: /* no update ever */ /* include all the lumps */ len += l1_len + l2_len + l3_len; /* the new uri (if any)*/ if (org_msg->new_uri.s && org_msg->new_uri.len) len += ROUND4(org_msg->new_uri.len); /* the global address */ if (org_msg->set_global_address.s) len += ROUND4(org_msg->set_global_address.len); /* the global port */ if (org_msg->set_global_port.s) len += ROUND4(org_msg->set_global_port.len); break; case 1: /* updatable and cloning now */ case 2: /* updatable, but no cloning now */ /* no additional len processing in these cases */ break; } /* do all mallocs */ p=(char *)shm_malloc(len); if (!p) { LM_ERR("no more share memory\n" ); return 0; } if (sip_msg_len) *sip_msg_len = len; /* filling up the new structure */ new_msg = (struct sip_msg*)p; /* sip msg structure */ memcpy( new_msg , org_msg , sizeof(struct sip_msg)); /* avoid copying pointer to un-clonned structures */ new_msg->sdp = 0; new_msg->multi = 0; new_msg->msg_cb = 0; new_msg->msg_flags |= FL_SHM_CLONE; p += ROUND4(sizeof(struct sip_msg)); /* message buffers(org and scratch pad) */ memcpy( p , org_msg->buf, org_msg->len); /* ZT to be safer */ *(p+org_msg->len)=0; new_msg->buf = p; p += ROUND4(new_msg->len+1); /* unparsed and eoh pointer */ new_msg->unparsed = translate_pointer(new_msg->buf ,org_msg->buf, org_msg->unparsed ); new_msg->eoh = translate_pointer(new_msg->buf,org_msg->buf,org_msg->eoh); /* first line, updating the pointers*/ if ( org_msg->first_line.type==SIP_REQUEST ) { new_msg->first_line.u.request.method.s = translate_pointer( new_msg->buf , org_msg->buf , org_msg->first_line.u.request.method.s ); new_msg->first_line.u.request.uri.s = translate_pointer( new_msg->buf , org_msg->buf , org_msg->first_line.u.request.uri.s ); new_msg->first_line.u.request.version.s = translate_pointer( new_msg->buf , org_msg->buf , org_msg->first_line.u.request.version.s ); if(new_msg->parsed_orig_ruri_ok) uri_trans(new_msg->buf, org_msg->buf, &new_msg->parsed_orig_ruri); if(new_msg->parsed_uri_ok) { if (new_msg->new_uri.s) { /* uri string in a separate buffer */ uri_trans(new_msg->new_uri.s, org_msg->new_uri.s, &new_msg->parsed_uri); } else { /* uri string still in buf */ uri_trans(new_msg->buf, org_msg->buf, &new_msg->parsed_uri); } } } else if ( org_msg->first_line.type==SIP_REPLY ) { new_msg->first_line.u.reply.version.s = translate_pointer( new_msg->buf , org_msg->buf , org_msg->first_line.u.reply.version.s ); new_msg->first_line.u.reply.status.s = translate_pointer( new_msg->buf , org_msg->buf , org_msg->first_line.u.reply.status.s ); new_msg->first_line.u.reply.reason.s = translate_pointer( new_msg->buf , org_msg->buf , org_msg->first_line.u.reply.reason.s ); } /*headers list*/ new_msg->via1=0; new_msg->via2=0; for( hdr=org_msg->headers,last_hdr=0 ; hdr ; hdr=hdr->next ) { new_hdr = (struct hdr_field*)p; memcpy(new_hdr, hdr, sizeof(struct hdr_field) ); p += ROUND4(sizeof( struct hdr_field)); new_hdr->name.s = translate_pointer(new_msg->buf, org_msg->buf, hdr->name.s); new_hdr->body.s = translate_pointer(new_msg->buf, org_msg->buf, hdr->body.s); /* by default, we assume we don't understand this header in TM and better set it to zero; if we do, we will set a specific value in the following switch statement */ new_hdr->parsed=0; new_hdr->sibling=0; switch (hdr->type) { case HDR_VIA_T: /*fprintf(stderr,"prepare to clone via |%.*s|\n", via_len((struct via_body*)hdr->parsed), via_s((struct via_body*)hdr->parsed,org_msg));*/ if ( !new_msg->via1 ) { new_msg->h_via1 = new_hdr; new_msg->via1 = via_body_cloner(new_msg->buf, org_msg->buf, (struct via_body*)hdr->parsed, &p); new_hdr->parsed = (void*)new_msg->via1; /*fprintf(stderr,"setting via1 |%.*s|\n", via_len(new_msg->via1), via_s(new_msg->via1,new_msg));*/ if ( new_msg->via1->next ) new_msg->via2 = new_msg->via1->next; } else if ( !new_msg->via2 ) { LINK_SIBLING_HEADER(h_via1, new_hdr); new_msg->h_via2 = new_hdr; new_msg->via2 = via_body_cloner( new_msg->buf, org_msg->buf, (struct via_body*)hdr->parsed, &p); new_hdr->parsed = (void*)new_msg->via2; } else { LINK_SIBLING_HEADER(h_via1, new_hdr); new_hdr->parsed = via_body_cloner( new_msg->buf , org_msg->buf , (struct via_body*)hdr->parsed , &p); } break; case HDR_CSEQ_T: new_hdr->parsed = p; p +=ROUND4(sizeof(struct cseq_body)); memcpy(new_hdr->parsed, hdr->parsed, sizeof(struct cseq_body)); ((struct cseq_body*)new_hdr->parsed)->number.s = translate_pointer(new_msg->buf ,org_msg->buf, ((struct cseq_body*)hdr->parsed)->number.s ); ((struct cseq_body*)new_hdr->parsed)->method.s = translate_pointer(new_msg->buf ,org_msg->buf, ((struct cseq_body*)hdr->parsed)->method.s ); if (HOOK_NOT_SET(cseq)) new_msg->cseq = new_hdr; break; case HDR_TO_T: case HDR_FROM_T: if (hdr->type == HDR_TO_T) { if (HOOK_NOT_SET(to)) new_msg->to = new_hdr; } else { if (HOOK_NOT_SET(from)) new_msg->from = new_hdr; } /* From header might be unparsed */ if (!hdr->parsed) break; new_hdr->parsed = p; p +=ROUND4(sizeof(struct to_body)); memcpy(new_hdr->parsed, hdr->parsed, sizeof(struct to_body)); ((struct to_body*)new_hdr->parsed)->body.s = translate_pointer( new_msg->buf , org_msg->buf , ((struct to_body*)hdr->parsed)->body.s ); ((struct to_body*)new_hdr->parsed)->uri.s = translate_pointer( new_msg->buf , org_msg->buf , ((struct to_body*)hdr->parsed)->uri.s ); if ( ((struct to_body*)hdr->parsed)->display.s ) ((struct to_body*)new_hdr->parsed)->display.s = translate_pointer( new_msg->buf , org_msg->buf , ((struct to_body*)hdr->parsed)->display.s ); if ( ((struct to_body*)hdr->parsed)->tag_value.s ) ((struct to_body*)new_hdr->parsed)->tag_value.s = translate_pointer( new_msg->buf , org_msg->buf , ((struct to_body*)hdr->parsed)->tag_value.s ); if ( (((struct to_body*)new_hdr->parsed)->parsed_uri.user.s) || (((struct to_body*)new_hdr->parsed)->parsed_uri.host.s) ) uri_trans(new_msg->buf, org_msg->buf, &((struct to_body*)new_hdr->parsed)->parsed_uri); /*to params*/ to_prm = ((struct to_body*)(hdr->parsed))->param_lst; for(;to_prm;to_prm=to_prm->next) { /*alloc*/ new_to_prm = (struct to_param*)p; p +=ROUND4(sizeof(struct to_param )); /*coping*/ memcpy( new_to_prm, to_prm, sizeof(struct to_param )); ((struct to_body*)new_hdr->parsed)->param_lst = 0; new_to_prm->name.s = translate_pointer( new_msg->buf, org_msg->buf , to_prm->name.s ); new_to_prm->value.s = translate_pointer( new_msg->buf, org_msg->buf , to_prm->value.s ); /*linking*/ if ( !((struct to_body*)new_hdr->parsed)->param_lst ) ((struct to_body*)new_hdr->parsed)->param_lst = new_to_prm; else ((struct to_body*)new_hdr->parsed)->last_param->next = new_to_prm; ((struct to_body*)new_hdr->parsed)->last_param = new_to_prm; } break; case HDR_CALLID_T: if (HOOK_NOT_SET(callid)) { new_msg->callid = new_hdr; } break; case HDR_CONTACT_T: if (HOOK_NOT_SET(contact)) { new_msg->contact = new_hdr; } else { LINK_SIBLING_HEADER(contact, new_hdr); } break; case HDR_MAXFORWARDS_T : if (HOOK_NOT_SET(maxforwards)) { new_msg->maxforwards = new_hdr; } break; case HDR_ROUTE_T : if (HOOK_NOT_SET(route)) { new_msg->route = new_hdr; } else { LINK_SIBLING_HEADER(route, new_hdr); } break; case HDR_RECORDROUTE_T : if (HOOK_NOT_SET(record_route)) { new_msg->record_route = new_hdr; } else { LINK_SIBLING_HEADER(record_route, new_hdr); } break; case HDR_PATH_T : if (HOOK_NOT_SET(path)) { new_msg->path = new_hdr; } else { LINK_SIBLING_HEADER(path, new_hdr); } break; case HDR_CONTENTLENGTH_T : if (HOOK_NOT_SET(content_length)) { new_msg->content_length = new_hdr; new_msg->content_length->parsed = hdr->parsed; } break; case HDR_AUTHORIZATION_T : if (HOOK_NOT_SET(authorization)) { new_msg->authorization = new_hdr; } else { LINK_SIBLING_HEADER(authorization, new_hdr); } if (hdr->parsed) { new_hdr->parsed = auth_body_cloner(new_msg->buf , org_msg->buf , (struct auth_body*)hdr->parsed , &p); } break; case HDR_EXPIRES_T : if (HOOK_NOT_SET(expires)) { new_msg->expires = new_hdr; } break; case HDR_PROXYAUTH_T : if (HOOK_NOT_SET(proxy_auth)) { new_msg->proxy_auth = new_hdr; } else { LINK_SIBLING_HEADER(proxy_auth, new_hdr); } if (hdr->parsed) { new_hdr->parsed = auth_body_cloner(new_msg->buf , org_msg->buf , (struct auth_body*)hdr->parsed , &p); } break; case HDR_SUPPORTED_T : if (HOOK_NOT_SET(supported)) { new_msg->supported = new_hdr; } else { LINK_SIBLING_HEADER(supported, new_hdr); } break; case HDR_PROXYREQUIRE_T : if (HOOK_NOT_SET(proxy_require)) { new_msg->proxy_require = new_hdr; } else { LINK_SIBLING_HEADER(proxy_require, new_hdr); } break; case HDR_UNSUPPORTED_T : if (HOOK_NOT_SET(unsupported)) { new_msg->unsupported = new_hdr; } else { LINK_SIBLING_HEADER(unsupported, new_hdr); } break; case HDR_ALLOW_T : if (HOOK_NOT_SET(allow)) { new_msg->allow = new_hdr; } else { LINK_SIBLING_HEADER(allow, new_hdr); } break; case HDR_EVENT_T: if (HOOK_NOT_SET(event)) { new_msg->event = new_hdr; } else { LINK_SIBLING_HEADER(event, new_hdr); } break; case HDR_CONTENTTYPE_T: if (HOOK_NOT_SET(content_type)) { new_msg->content_type = new_hdr; } else { LINK_SIBLING_HEADER(content_type, new_hdr); } break; case HDR_ACCEPT_T: if (HOOK_NOT_SET(accept)) { new_msg->accept = new_hdr; } else { LINK_SIBLING_HEADER(accept, new_hdr); } break; case HDR_ACCEPTLANGUAGE_T: if (HOOK_NOT_SET(accept_language)) { new_msg->accept_language = new_hdr; } else { LINK_SIBLING_HEADER(accept_language, new_hdr); } break; case HDR_ORGANIZATION_T: if (HOOK_NOT_SET(organization)) { new_msg->organization = new_hdr; } break; case HDR_PRIORITY_T: if (HOOK_NOT_SET(priority)) { new_msg->priority = new_hdr; } break; case HDR_SUBJECT_T: if (HOOK_NOT_SET(subject)) { new_msg->subject = new_hdr; } break; case HDR_USERAGENT_T: if (HOOK_NOT_SET(user_agent)) { new_msg->user_agent = new_hdr; } break; case HDR_ACCEPTDISPOSITION_T: if (HOOK_NOT_SET(accept_disposition)) { new_msg->accept_disposition = new_hdr; } else { LINK_SIBLING_HEADER(accept_disposition, new_hdr); } break; case HDR_CONTENTDISPOSITION_T: if (HOOK_NOT_SET(content_disposition)) { new_msg->content_disposition = new_hdr; } break; case HDR_DIVERSION_T: if (HOOK_NOT_SET(diversion)) { new_msg->diversion = new_hdr; } else { LINK_SIBLING_HEADER(diversion, new_hdr); } break; case HDR_RPID_T: if (HOOK_NOT_SET(rpid)) { new_msg->rpid = new_hdr; } break; case HDR_REFER_TO_T: if (HOOK_NOT_SET(refer_to)) { new_msg->refer_to = new_hdr; } break; case HDR_SESSION_EXPIRES_T: if (HOOK_NOT_SET(session_expires)) { new_msg->session_expires = new_hdr; } break; case HDR_MIN_SE_T: if (HOOK_NOT_SET(min_se)) { new_msg->min_se = new_hdr; } break; case HDR_PPI_T: if (HOOK_NOT_SET(ppi)) { new_msg->ppi = new_hdr; } break; case HDR_PAI_T: if (HOOK_NOT_SET(pai)) { new_msg->pai = new_hdr; } break; case HDR_PRIVACY_T: if (HOOK_NOT_SET(privacy)) { new_msg->privacy = new_hdr; } break; case HDR_CALL_INFO_T: if (HOOK_NOT_SET(call_info)) { new_msg->call_info = new_hdr; } break; case HDR_WWW_AUTHENTICATE_T: if (HOOK_NOT_SET(www_authenticate)) { new_msg->www_authenticate = new_hdr; } break; case HDR_PROXY_AUTHENTICATE_T: if (HOOK_NOT_SET(proxy_authenticate)) { new_msg->proxy_authenticate = new_hdr; } break; default: /* ignore the rest*/ ; }/*switch*/ if ( last_hdr ) { last_hdr->next = new_hdr; last_hdr=last_hdr->next; } else { last_hdr=new_hdr; new_msg->headers =new_hdr; } last_hdr->next = 0; new_msg->last_header = last_hdr; } if (clone_authorized_hooks(new_msg, org_msg) < 0) { free_cloned_msg(new_msg); return 0; } /* clone the "updatable" part of the msg */ switch (updatable) { case 0: /* no update ever -> copy in the same chunk */ /* new_uri */ if (org_msg->new_uri.s && org_msg->new_uri.len) { new_msg->new_uri.s = p; memcpy( p , org_msg->new_uri.s , org_msg->new_uri.len); p += ROUND4(org_msg->new_uri.len); } /* dst_uri to zero */ new_msg->dst_uri.s = 0; new_msg->dst_uri.len = 0; /* path_vec to zero */ new_msg->path_vec.s = 0; new_msg->path_vec.len = 0; /* advertised address and port */ if (org_msg->set_global_address.s) { new_msg->set_global_address.s = p; memcpy(p, org_msg->set_global_address.s, org_msg->set_global_address.len); p += ROUND4(org_msg->set_global_address.len); } if (org_msg->set_global_port.s) { new_msg->set_global_port.s = p; memcpy(p, org_msg->set_global_port.s, org_msg->set_global_port.len); p += ROUND4(org_msg->set_global_port.len); } /* clone data lump in the same chunk as sip_msg (not updatable) */ new_msg->add_rm = 0; CLONE_LUMP_LIST(p, &(new_msg->add_rm), org_msg->add_rm); new_msg->body_lumps = 0; CLONE_LUMP_LIST(p, &(new_msg->body_lumps), org_msg->body_lumps); new_msg->reply_lump = 0; CLONE_RPL_LUMP_LIST( p, &(new_msg->reply_lump), org_msg->reply_lump); case 1: /* updatable and cloning now */ new_msg->msg_flags |= FL_SHM_UPDATABLE; /* msg is updatable -> the fields that can be updated are allocated in * separate memory chunks */ tm_shm_lock(); if (org_msg->new_uri.len) new_msg->new_uri.s = (char*)tm_shm_malloc_unsafe( org_msg->new_uri.len ); if (org_msg->dst_uri.len) new_msg->dst_uri.s = (char*)tm_shm_malloc_unsafe( org_msg->dst_uri.len ); if (org_msg->path_vec.len) new_msg->path_vec.s = (char*)tm_shm_malloc_unsafe( org_msg->path_vec.len ); if (org_msg->set_global_address.len) new_msg->set_global_address.s = (char*)tm_shm_malloc_unsafe( org_msg->set_global_address.len ); if (org_msg->set_global_port.len) new_msg->set_global_port.s = (char*)tm_shm_malloc_unsafe( org_msg->set_global_port.len ); if (l1_len) new_msg->add_rm = (struct lump*)tm_shm_malloc_unsafe(l1_len); if (l2_len) new_msg->body_lumps = (struct lump*)tm_shm_malloc_unsafe(l2_len); if (l3_len) new_msg->reply_lump = (struct lump_rpl*)tm_shm_malloc_unsafe(l3_len); tm_shm_unlock(); /*check the malloc result*/ if ( (org_msg->new_uri.len && new_msg->new_uri.s==NULL) || (org_msg->dst_uri.len && new_msg->dst_uri.s==NULL) || (org_msg->path_vec.len && new_msg->path_vec.s==NULL) || (org_msg->set_global_address.len && new_msg->set_global_address.s==NULL) || (org_msg->set_global_port.len && new_msg->set_global_port.s==NULL) || (l1_len && new_msg->add_rm==NULL) || (l2_len && new_msg->body_lumps==NULL) || (l3_len && new_msg->reply_lump==NULL) ) { LM_ERR("failed to sh allocate the updatable part of the msg\n"); free_cloned_msg(new_msg); return 0; } /* copy data */ if (org_msg->new_uri.len) memcpy( new_msg->new_uri.s, org_msg->new_uri.s, org_msg->new_uri.len); if (org_msg->dst_uri.len) memcpy( new_msg->dst_uri.s, org_msg->dst_uri.s, org_msg->dst_uri.len); if (org_msg->path_vec.len) memcpy( new_msg->path_vec.s, org_msg->path_vec.s, org_msg->path_vec.len); if (org_msg->set_global_address.len) memcpy( new_msg->set_global_address.s, org_msg->set_global_address.s, org_msg->set_global_address.len); if (org_msg->set_global_port.len) memcpy( new_msg->set_global_port.s, org_msg->set_global_port.s, org_msg->set_global_port.len); /* clone lumps */ p = (char*)new_msg->add_rm; CLONE_LUMP_LIST( p, &(new_msg->add_rm), org_msg->add_rm); p = (char*)new_msg->body_lumps; CLONE_LUMP_LIST( p, &(new_msg->body_lumps), org_msg->body_lumps); p = (char*)new_msg->reply_lump; CLONE_RPL_LUMP_LIST( p, &(new_msg->reply_lump), org_msg->reply_lump); break; case 2: /* updatable, but no cloning now */ new_msg->msg_flags |= FL_SHM_UPDATABLE; /* new_uri to zero */ new_msg->new_uri.s = 0; new_msg->new_uri.len = 0; /* dst_uri to zero */ new_msg->dst_uri.s = 0; new_msg->dst_uri.len = 0; /* path_vec to zero */ new_msg->path_vec.s = 0; new_msg->path_vec.len = 0; /* set_global_address to zero */ new_msg->set_global_address.s = 0; new_msg->set_global_address.len = 0; /* set_global_port to zero */ new_msg->set_global_port.s = 0; new_msg->set_global_port.len = 0; /* set lumps to zero */ new_msg->add_rm = 0; new_msg->body_lumps = 0; new_msg->reply_lump = 0; break; } return new_msg; } #define REALLOC_CLONED_FIELD_unsafe( _field, _old, _new, _bit) \ do { \ if ( _new->_field.len==0) { \ if (_old->_field.len!=0) \ tm_shm_free_unsafe( _old->_field.s ); \ } else { \ if ( _old->_field.len==0 ) { \ _old->_field.s = (char*)tm_shm_malloc_unsafe(_new->_field.len);\ } else if (_old->_field.len<_new->_field.len) { \ tm_shm_free_unsafe( _old->_field.s );\ _old->_field.s = (char*)tm_shm_malloc_unsafe(_new->_field.len);\ } \ copy_mask |= (1<<_bit);\ LM_DBG(#_field" must be copied old=%d, new=%d\n",_old->_field.len,_new->_field.len);\ } \ } while(0) #define COPY_CLONED_FIELD( _field, _old, _new, _bit) \ do { \ if (copy_mask&(1<<_bit)) { \ if (_old->_field.s==NULL) { \ LM_ERR("Failed to allocated new shm copy for "#_field"\n");\ _old->_field.len = 0;\ } else { \ memcpy( _old->_field.s, _new->_field.s, _new->_field.len); \ _old->_field.len = _new->_field.len;\ }\ } else { \ _old->_field.s = NULL; \ _old->_field.len = 0; \ } \ }while(0) /** * Parameters: * c_msg - Currently saved SIP request in its initial form (Shared memory) * msg - Duplicate of "c_msg" (private memory + heap space) that has * been altered by the script (it is newer than c_msg) * * Handles all realloc() operations needed to update "c_msg" from "msg" * */ int update_cloned_msg_from_msg(struct sip_msg *c_msg, struct sip_msg *msg) { unsigned char copy_mask = 0; int l1_len, l2_len, l3_len; char *p; struct lump *add_rm_aux=NULL,*body_lumps_aux=NULL; struct lump_rpl *reply_lump_aux=NULL; if ( (c_msg->msg_flags & (FL_SHM_UPDATABLE|FL_SHM_CLONE))==0 ) { LM_CRIT("BUG trying to update a msg not in SHM or not " "UPDATABLE (%d)\n", c_msg->msg_flags); return -1; } /* length of the new data lump structures */ l1_len = l2_len = l3_len = 0; LUMP_LIST_LEN(l1_len, msg->add_rm); LUMP_LIST_LEN(l2_len, msg->body_lumps); RPL_LUMP_LIST_LEN(l3_len, msg->reply_lump); tm_shm_lock(); /* SIP related strings */ REALLOC_CLONED_FIELD_unsafe( new_uri, c_msg, msg, 0); REALLOC_CLONED_FIELD_unsafe( dst_uri, c_msg, msg, 1); REALLOC_CLONED_FIELD_unsafe( path_vec, c_msg, msg, 2); REALLOC_CLONED_FIELD_unsafe( set_global_address, c_msg, msg, 3); REALLOC_CLONED_FIELD_unsafe( set_global_port, c_msg, msg, 4); /* * lump reallocation (guaranteed to be equal or greater size). * * c_msg lumps: * - initial set of SHM lumps * * msg lumps: * - initial set of SHM lumps (same memory as in c_msg above!) * - additional set of PKG lumps (from running various script changes) * * That is why mem is not leaked after the following allocations: */ if (l1_len) { add_rm_aux = c_msg->add_rm; c_msg->add_rm = tm_shm_malloc_unsafe(l1_len); } if (l2_len) { body_lumps_aux = c_msg->body_lumps; c_msg->body_lumps = tm_shm_malloc_unsafe(l2_len); } if (l3_len) { reply_lump_aux = c_msg->reply_lump; c_msg->reply_lump = tm_shm_malloc_unsafe(l3_len); } /* done with mem ops */ tm_shm_unlock(); /* copy data now */ COPY_CLONED_FIELD( new_uri, c_msg, msg, 0); COPY_CLONED_FIELD( dst_uri, c_msg, msg, 1); COPY_CLONED_FIELD( path_vec, c_msg, msg, 2); COPY_CLONED_FIELD( set_global_address, c_msg, msg, 3); COPY_CLONED_FIELD( set_global_port, c_msg, msg, 4); /* re-build lumps */ if (l1_len) { if (c_msg->add_rm==NULL) { LM_ERR("failed to clone lumps, not updating \n"); } else { p = (char*)c_msg->add_rm; CLONE_LUMP_LIST( p, &(c_msg->add_rm), msg->add_rm); } } else { c_msg->add_rm = NULL; } if (l2_len) { if (c_msg->body_lumps==NULL) { LM_ERR("failed to clone body lumps, not updating \n"); } else { p = (char*)c_msg->body_lumps; CLONE_LUMP_LIST( p, &(c_msg->body_lumps), msg->body_lumps); } } else { c_msg->body_lumps = NULL; } if (l3_len) { if (c_msg->reply_lump==NULL) { LM_ERR("failed to clone reply lumps, not updating \n"); } else { p = (char*)c_msg->reply_lump; CLONE_RPL_LUMP_LIST( p, &(c_msg->reply_lump), msg->reply_lump); } } else { c_msg->reply_lump = NULL; } /* flags */ c_msg->flags = msg->flags; c_msg->msg_flags = msg->msg_flags|(FL_SHM_UPDATABLE|FL_SHM_CLONE); c_msg->ruri_q = msg->ruri_q; c_msg->ruri_bflags = msg->ruri_bflags; if (!(msg->msg_flags & FL_TM_FAKE_REQ)) { /* if not a fake request, we should free old values right now, otherwise we leak if it's a fake request, then we can't free old info now - we might still need it ( eg. to build a reply from the faked req ) - let the freeing happen when destryong the fake req */ tm_shm_lock(); if (add_rm_aux) tm_shm_free_unsafe(add_rm_aux); if (body_lumps_aux) tm_shm_free_unsafe(body_lumps_aux); if (reply_lump_aux) tm_shm_free_unsafe(reply_lump_aux); tm_shm_unlock(); } return 0; } opensips-2.2.2/modules/tm/sip_msg.h000066400000000000000000000070031300170765700172650ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SIP_MSG_H #define _SIP_MSG_H #include "../../parser/msg_parser.h" #include "../../mem/shm_mem.h" /* TODO: replace these macros with a more generic approach --liviu */ #ifdef HP_MALLOC #define tm_shm_malloc_unsafe shm_malloc #define tm_shm_free_unsafe shm_free #define tm_destroy_avp_list_unsafe destroy_avp_list #define tm_shm_lock() #define tm_shm_unlock() #else #define tm_shm_malloc_unsafe shm_malloc_unsafe #define tm_shm_free_unsafe shm_free_unsafe #define tm_destroy_avp_list_unsafe destroy_avp_list_unsafe #define tm_shm_lock() shm_lock() #define tm_shm_unlock() shm_unlock() #endif #define free_cloned_msg_unsafe( _msg ) \ do { \ if ((_msg)->msg_flags & FL_SHM_UPDATABLE) { \ if ((_msg)->new_uri.s) \ tm_shm_free_unsafe((_msg)->new_uri.s);\ if ((_msg)->dst_uri.s) \ tm_shm_free_unsafe((_msg)->dst_uri.s);\ if ((_msg)->path_vec.s) \ tm_shm_free_unsafe((_msg)->path_vec.s);\ if ((_msg)->set_global_address.s) \ tm_shm_free_unsafe((_msg)->set_global_address.s);\ if ((_msg)->set_global_port.s) \ tm_shm_free_unsafe((_msg)->set_global_port.s);\ if ((_msg)->add_rm) \ tm_shm_free_unsafe((_msg)->add_rm);\ if ((_msg)->body_lumps) \ tm_shm_free_unsafe((_msg)->body_lumps);\ if ((_msg)->reply_lump) \ tm_shm_free_unsafe((_msg)->reply_lump);\ }\ tm_shm_free_unsafe((_msg));\ }while(0) #define free_cloned_msg( _msg ) \ do { \ if ((_msg)->msg_flags & FL_SHM_UPDATABLE) { \ if ((_msg)->new_uri.s) \ shm_free((_msg)->new_uri.s);\ if ((_msg)->dst_uri.s) \ shm_free((_msg)->dst_uri.s);\ if ((_msg)->path_vec.s) \ shm_free((_msg)->path_vec.s);\ if ((_msg)->set_global_address.s) \ shm_free((_msg)->set_global_address.s);\ if ((_msg)->set_global_port.s) \ shm_free((_msg)->set_global_port.s);\ if ((_msg)->add_rm) \ shm_free((_msg)->add_rm);\ if ((_msg)->body_lumps) \ shm_free((_msg)->body_lumps);\ if ((_msg)->reply_lump) \ shm_free((_msg)->reply_lump);\ }\ shm_free((_msg));\ }while(0) struct sip_msg* sip_msg_cloner( struct sip_msg *org_msg, int *sip_msg_len, int updatable ); static inline void clean_msg_clone(struct sip_msg *msg,void *min, void *max) { struct hdr_field *hdr; /* free header's parsed structures that were added in pkg mem */ for( hdr=msg->headers ; hdr ; hdr=hdr->next ) { if ( hdr->parsed && hdr_allocs_parse(hdr) && (hdr->parsedparsed>=max)) { /* header parsed filed doesn't point inside uas.request memory * chunck -> it was added by failure funcs.-> free it as pkg */ LM_DBG("removing hdr->parsed %d\n", hdr->type); clean_hdr_field(hdr); hdr->parsed = 0; } } } int update_cloned_msg_from_msg(struct sip_msg *c_msg, struct sip_msg *msg); #endif opensips-2.2.2/modules/tm/t_cancel.c000066400000000000000000000075361300170765700174020ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ---------- * 2003-04-14 checking if a reply sent before cancel is initiated * moved here (jiri) * 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri) * 2004-02-13 timer_link.payload removed (bogdan) */ #include "t_funcs.h" #include "../../dprint.h" #include "../../ut.h" #include "t_reply.h" #include "t_fwd.h" #include "t_cancel.h" #include "t_msgbuilder.h" #include "t_lookup.h" /* for t_lookup_callid in fifo_uac_cancel */ #define CANCEL_REASON \ "Reason: SIP ;cause=200 ;text=\"Call completed elsewhere\"" str _extra_cancel_hdrs = {NULL,0}; /* determine which branches should be canceled; do it only from within REPLY_LOCK, otherwise collisions could occur (e.g., two 200 for two branches processed by two processes might concurrently try to generate a CANCEL for the third branch, resulting in race conditions during writing to cancel buffer */ void which_cancel( struct cell *t, branch_bm_t *cancel_bm ) { int i; for( i=t->first_branch ; inr_of_outgoings ; i++ ) { if (should_cancel_branch(t, i)) *cancel_bm |= 1<nr_of_outgoings ; i++ ) if (cancel_bm & (1<uac[branch].local_cancel; irb=&t->uac[branch].request; # ifdef EXTRA_DEBUG if (crb->buffer.s!=0 && crb->buffer.s!=BUSY_BUFFER) { LM_CRIT("attempt to rewrite cancel buffer failed\n"); abort(); } # endif cancel=build_cancel(t, branch, &len); if (!cancel) { LM_ERR("attempt to build a CANCEL failed\n"); return; } /* install cancel now */ crb->buffer.s=cancel; crb->buffer.len=len; crb->dst=irb->dst; crb->branch=branch; /* label it as cancel so that FR timer can better now how * to deal with it */ crb->activ_type=TYPE_LOCAL_CANCEL; if ( has_tran_tmcbs( t, TMCB_REQUEST_BUILT) ) { set_extra_tmcb_params( &crb->buffer, &crb->dst); run_trans_callbacks( TMCB_REQUEST_BUILT, t, t->uas.request,0, -t->uas.request->REQ_METHOD); } LM_DBG("sending cancel...\n"); SEND_BUFFER( crb ); /*sets and starts the FINAL RESPONSE timer */ start_retr( crb ); } char *build_cancel(struct cell *Trans,unsigned int branch, unsigned int *len ) { str method = str_init(CANCEL); str reason = str_init(CANCEL_REASON CRLF); str *extra = NULL; /* add the reason hdr, as per RFC 3326 */ if (is_invite(Trans) && Trans->uas.status==200) { extra = &reason; } else if (_extra_cancel_hdrs.s) { extra = &_extra_cancel_hdrs; } return build_local( Trans, branch, &method, extra, NULL /*reply*/ , len ); /* ^^^^ when CANCELing, there are 0 chances to have a reply stored into * transaction ; set it NULL to avoid using the temporary stored reply * (by t_should_relay_response) which may lead into races ( building the * cancel versus handling a final response in a different process )*/ } opensips-2.2.2/modules/tm/t_cancel.h000066400000000000000000000050461300170765700174010ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri) */ #ifndef _CANCEL_H #define _CANCEL_H /* a buffer is empty but cannot be used by anyone else; particularly, we use this value in the buffer pointer in local_buffer to tell "a process is already scheduled to generate a CANCEL, other processes are not supposed to" (which might happen if for example in a three-branch forking, two 200 would enter separate processes and compete for canceling the third branch); note that to really avoid race conditions, the value must be set in REPLY_LOCK */ #define BUSY_BUFFER ((char *)-1) void which_cancel( struct cell *t, branch_bm_t *cancel_bm ); void cancel_uacs( struct cell *t, branch_bm_t cancel_bm ); void cancel_branch( struct cell *t, int branch ); int unixsock_uac_cancel(str* msg); typedef int (*tcanceltrans_f)(struct cell *t, str *hdrs); char *build_cancel(struct cell *Trans,unsigned int branch, unsigned int *len ); inline static short should_cancel_branch( struct cell *t, int b ) { int last_received; last_received = t->uac[b].last_received; /* cancel only if provisional received and no one else attempted to cancel yet */ if ( t->uac[b].local_cancel.buffer.s==NULL ) { if ( last_received>=100 && last_received<200 ) { /* we'll cancel -- label it so that no one else (e.g. another 200 branch) will try to do the same */ t->uac[b].local_cancel.buffer.s=BUSY_BUFFER; return 1; } else if (last_received==0) { /* set flag to catch the delaied replies */ t->uac[b].flags |= T_UAC_TO_CANCEL_FLAG; } } return 0; } extern str _extra_cancel_hdrs; static inline void set_cancel_extra_hdrs( char *s, int l) { _extra_cancel_hdrs.s = s; _extra_cancel_hdrs.len = l; } #endif opensips-2.2.2/modules/tm/t_ctx.c000066400000000000000000000035401300170765700167420ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-10-30 initial version (liviu) */ #include "t_funcs.h" #include "t_ctx.h" int t_ctx_register_int(context_destroy_f f) { return context_register_int(CONTEXT_TRAN, f); } int t_ctx_register_str(context_destroy_f f) { return context_register_str(CONTEXT_TRAN, f); } int t_ctx_register_ptr(context_destroy_f f) { return context_register_ptr(CONTEXT_TRAN, f); } void t_ctx_put_int(struct cell *t, int pos, int data) { context_put_int(CONTEXT_TRAN, context_of(t), pos, data); } void t_ctx_put_str(struct cell *t, int pos, str *data) { context_put_str(CONTEXT_TRAN, context_of(t), pos, data); } void t_ctx_put_ptr(struct cell *t, int pos, void *data) { context_put_ptr(CONTEXT_TRAN, context_of(t), pos, data); } int t_ctx_get_int(struct cell *t, int pos) { return context_get_int(CONTEXT_TRAN, context_of(t), pos); } str *t_ctx_get_str(struct cell *t, int pos) { return context_get_str(CONTEXT_TRAN, context_of(t), pos); } void *t_ctx_get_ptr(struct cell *t, int pos) { return context_get_ptr(CONTEXT_TRAN, context_of(t), pos); } opensips-2.2.2/modules/tm/t_ctx.h000066400000000000000000000036271300170765700167550ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2014-10-30 initial version (liviu) */ #ifndef __T_CTX_H #define __T_CTX_H typedef int (*t_ctx_register_int_f)(context_destroy_f f); typedef int (*t_ctx_register_str_f)(context_destroy_f f); typedef int (*t_ctx_register_ptr_f)(context_destroy_f f); typedef void (*t_ctx_put_int_f)(struct cell *t, int pos, int data); typedef void (*t_ctx_put_str_f)(struct cell *t, int pos, str *data); typedef void (*t_ctx_put_ptr_f)(struct cell *t, int pos, void *data); typedef int (*t_ctx_get_int_f)(struct cell *t, int pos); typedef str *(*t_ctx_get_str_f)(struct cell *t, int pos); typedef void *(*t_ctx_get_ptr_f)(struct cell *t, int pos); int t_ctx_register_int(context_destroy_f f); int t_ctx_register_str(context_destroy_f f); int t_ctx_register_ptr(context_destroy_f); void t_ctx_put_int(struct cell *t, int pos, int data); void t_ctx_put_str(struct cell *t, int pos, str *data); void t_ctx_put_ptr(struct cell *t, int pos, void *data); int t_ctx_get_int(struct cell *t, int pos); str *t_ctx_get_str(struct cell *t, int pos); void *t_ctx_get_ptr(struct cell *t, int pos); #endif /* __T_CTX_H */ opensips-2.2.2/modules/tm/t_dlg.c000066400000000000000000000017051300170765700167130ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "t_dlg.h" dlg_t dlg=0; int t_newdlg( struct sip_msg *msg ) { /* place-holder */ dlg=0; return 0; } dlg_t t_getdlg(void) { return dlg; } opensips-2.2.2/modules/tm/t_dlg.h000066400000000000000000000017741300170765700167260ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _T_DLG_H #define _T_DLG_H #include "../../parser/msg_parser.h" struct dialog { int place_holder; }; typedef struct dialog *dlg_t; int t_newdlg( struct sip_msg *msg ); dlg_t t_getdlg() ; #endif opensips-2.2.2/modules/tm/t_fifo.c000066400000000000000000000450121300170765700170670ustar00rootroot00000000000000/* * transaction maintenance functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2004-02-23 created by splitting it from t_funcs (bogdan) * 2004-11-15 t_write_xxx can print whatever avp/hdr * 2005-07-14 t_write_xxx specification aligned to use pseudo-variables * (bogdan) */ #include #include #include #include #include #include #include #include #include #include #include #include "../../str.h" #include "../../ut.h" #include "../../dprint.h" #include "../../pvar.h" #include "../../mem/mem.h" #include "../../parser/parser_f.h" #include "../../parser/parse_from.h" #include "../../parser/parse_rr.h" #include "../../parser/contact/parse_contact.h" #include "../../tsend.h" #include "t_lookup.h" #include "t_fwd.h" #include "t_fifo.h" /* AF_LOCAL is not defined on solaris */ #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #if !defined(PF_LOCAL) #define PF_LOCAL PF_UNIX #endif /* solaris doesn't have SUN_LEN */ #ifndef SUN_LEN #define SUN_LEN(sa) ( strlen((sa)->sun_path) + \ (size_t)(((struct sockaddr_un*)0)->sun_path) ) #endif int tm_unix_tx_timeout = 2; /* Default is 2 seconds */ #define TWRITE_PARAMS 20 #define TWRITE_VERSION_S "0.3" #define TWRITE_VERSION_LEN (sizeof(TWRITE_VERSION_S)-1) #define eol_line(_i_) ( lines_eol[2*(_i_)] ) #define IDBUF_LEN 128 #define ROUTE_BUFFER_MAX 512 #define APPEND_BUFFER_MAX 4096 #define CMD_BUFFER_MAX 128 #define append_str(_dest,_src,_len) \ do{ \ memcpy( (_dest) , (_src) , (_len) );\ (_dest) += (_len) ;\ }while(0); #define append_chr(_dest,_c) \ *((_dest)++) = _c; #define copy_route(s,len,rs,rlen) \ do {\ if(rlen+len+3 >= ROUTE_BUFFER_MAX){\ LM_ERR("buffer overflow while copying new route\n");\ goto error;\ }\ if(len){\ append_chr(s,','); len++;\ }\ append_chr(s,'<');len++;\ append_str(s,rs,rlen);\ len += rlen; \ append_chr(s,'>');len++;\ } while(0) static str lines_eol[2*TWRITE_PARAMS]; static str eol={"\n",1}; static int sock; struct append_elem { str name; /* name / title */ pv_spec_t spec; /* value's spec */ struct append_elem *next; }; struct tw_append { str name; int add_body; struct append_elem *elems; struct tw_append *next; }; struct tw_info { str action; struct tw_append *append; }; static struct tw_append *tw_appends; /* tw_append syntax: * tw_append = name:element[;element] * element = (title=pseudo_variable) | msg(body) */ int parse_tw_append( modparam_t type, void* val) { struct append_elem *last; struct append_elem *elem; struct tw_append *app; pv_spec_t lspec; char *s; str foo; str bar; if (val==0 || ((char*)val)[0]==0) return 0; s = (char*)val; /* start parsing - first the name */ while( *s && isspace((int)*s) ) s++; if ( !*s || *s==':') goto parse_error; foo.s = s; while ( *s && *s!=':' && !isspace((int)*s) ) s++; if ( !*s || foo.s==s ) goto parse_error; foo.len = s - foo.s; /* parse separator */ while( *s && isspace((int)*s) ) s++; if ( !*s || *s!=':') goto parse_error; s++; /* check for name duplication */ for(app=tw_appends;app;app=app->next) if (app->name.len==foo.len && !strncasecmp(app->name.s,foo.s,foo.len)){ LM_ERR("duplicated tw_append name <%.*s>\n",foo.len,foo.s); goto error; } /* new tw_append structure */ app = (struct tw_append*)pkg_malloc( sizeof(struct tw_append) ); if (app==0) { LM_ERR("no more pkg memory\n"); goto error; } /* init the new append */ app->name = foo; last = app->elems = 0; /* link the new append */ app->next = tw_appends; tw_appends = app; /* parse the elements */ while (*s) { /* skip white spaces */ while( *s && isspace((int)*s) ) s++; if ( !*s ) goto parse_error; /* parse element name */ foo.s = s; while( *s && *s!='=' && *s!=';' && !isspace((int)*s) ) s++; if (foo.s==s) goto parse_error; foo.len = s - foo.s; /* skip spaces */ while( *s && isspace((int)*s) ) s++; if ( *s && *s!='=' && *s!=';' ) goto parse_error; /* short element (without name) ? */ if (*s=='=' ) { /* skip '=' */ s++; /* new append_elem structure */ elem = (struct append_elem*)pkg_malloc(sizeof(struct append_elem)); if (elem==0) { LM_ERR("no more pkg memory\n"); goto error; } memset( elem, 0, sizeof(struct append_elem)); /* set and link the element */ elem->name = foo; if (last==0) { app->elems = elem; } else { last->next = elem; } last = elem; /* skip spaces */ while (*s && isspace((int)*s)) s++; } else { /* go back to reparse as value */ s = foo.s; elem = 0; } /* get value type */ bar.s = s; bar.len = strlen(bar.s); if ( (foo.s=pv_parse_spec( &bar, &lspec))==0 ) goto parse_error; /* if short element....which one? */ if (elem==0) { if (lspec.type!=PVT_MSG_BODY) { LM_ERR("short spec '%.*s' unknown(aceepted only body)\n", (int)(long)(foo.s-s), s); goto error; } app->add_body = 1; } else { elem->spec = lspec; } /* continue parsing*/ s = foo.s; /* skip spaces */ while (*s && isspace((int)*s)) s++; if (*s && *(s++)!=';') goto parse_error; } /* go throught all elements and make the names null terminated */ for( elem=app->elems ; elem ; elem=elem->next) elem->name.s[elem->name.len] = 0; /* make the append name null terminated also */ app->name.s[app->name.len] = 0; return 0; parse_error: LM_ERR("parse error in <%s> around position %ld(%c)\n", (char*)val, (long)(s-(char*)val),*s); error: return -1; } static struct tw_append *search_tw_append(char *name, int len) { struct tw_append * app; for( app=tw_appends ; app ; app=app->next ) if (app->name.len==len && !strncasecmp(app->name.s,name,len) ) return app; return 0; } int fixup_t_write( void** param, int param_no) { struct tw_info *twi; char *s; if (param_no==1) { twi = (struct tw_info*)pkg_malloc( sizeof(struct tw_info) ); if (twi==0) { LM_ERR("no more pkg memory\n"); return E_OUT_OF_MEM; } memset( twi, 0 , sizeof(struct tw_info)); s = (char*)*param; twi->action.s = s; if ( (s=strchr(s,'/'))!=0) { twi->action.len = s - twi->action.s; if (twi->action.len==0) { LM_ERR("empty action name\n"); return E_CFG; } s++; if (*s==0) { LM_ERR("empty append name\n"); return E_CFG; } twi->append = search_tw_append( s, strlen(s)); if (twi->append==0) { LM_ERR("unknown append name <%s>\n",s); return E_CFG; } } else { twi->action.len = strlen(twi->action.s); } *param=(void*)twi; } return 0; } int init_twrite_sock(void) { int flags; sock = socket(PF_LOCAL, SOCK_DGRAM, 0); if (sock == -1) { LM_ERR("unable to create socket: %s\n", strerror(errno)); return -1; } /* Turn non-blocking mode on */ flags = fcntl(sock, F_GETFL); if (flags == -1){ LM_ERR("init_twrite_sock: fcntl failed: %s\n", strerror(errno)); close(sock); return -1; } if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("init_twrite_sock: fcntl: set non-blocking failed:" " %s\n", strerror(errno)); close(sock); return -1; } return 0; } int init_twrite_lines(void) { int i; /* init the line table */ for(i=0;ilen+value->len+2+1>=end) return 0; memcpy( buf, name->s, name->len); buf += name->len; *(buf++) = ':'; *(buf++) = ' '; memcpy( buf, value->s, value->len); buf += value->len; *(buf++) = '\n'; return buf; } static inline char* append2buf( char *buf, int len, struct sip_msg *req, struct append_elem *elem) { pv_value_t value; char *end; end = buf+len; while (elem) { /* get the value */ if (pv_get_spec_value(req, &elem->spec, &value)!=0) { LM_ERR("failed to get '%.*s'\n", elem->name.len,elem->name.s); } /* empty element? */ if ( !(value.flags&PV_VAL_NULL) ) { /* write the value into the buffer */ buf = add2buf( buf, end, &elem->name, &value.rs); if (!buf) { LM_ERR("overflow -> append exceeded %d len\n",len); return 0; } } elem = elem->next; } return buf; } static int assemble_msg(struct sip_msg* msg, struct tw_info *twi) { static char id_buf[IDBUF_LEN]; static char route_buffer[ROUTE_BUFFER_MAX]; static char append_buf[APPEND_BUFFER_MAX]; static char cmd_buf[CMD_BUFFER_MAX]; static str empty_param = {".",1}; unsigned int hash_index, label; contact_body_t* cb=0; contact_t* c=0; name_addr_t na; rr_t* record_route; struct hdr_field* p_hdr; param_hooks_t hooks; int l; char* s, fproxy_lr; str route, next_hop, append, tmp_s, body, str_uri; if(msg->first_line.type != SIP_REQUEST){ LM_ERR("called for something else then a SIP request\n"); goto error; } /* parse all -- we will need every header field for a UAS */ if ( parse_headers(msg, HDR_EOH_F, 0)==-1) { LM_ERR("parse_headers failed\n"); goto error; } /* find index and hash; (the transaction can be safely used due * to refcounting till script completes) */ if( t_get_trans_ident(msg,&hash_index,&label) == -1 ) { LM_ERR("t_get_trans_ident failed\n"); goto error; } /* parse from header */ if (msg->from->parsed==0 && parse_from_header(msg)<0 ) { LM_ERR("failed to parse header\n"); goto error; } /* parse the RURI (doesn't make any malloc) */ msg->parsed_uri_ok = 0; /* force parsing */ if (parse_sip_msg_uri(msg)<0) { LM_ERR("uri has not been parsed\n"); goto error; } /* parse contact header */ str_uri.s = 0; str_uri.len = 0; if(msg->contact) { if (msg->contact->parsed==0 && parse_contact(msg->contact)<0) { LM_ERR("failed to parse header\n"); goto error; } cb = (contact_body_t*)msg->contact->parsed; if(cb && (c=cb->contacts)) { str_uri = c->uri; if (find_not_quoted(&str_uri,'<')) { parse_nameaddr(&str_uri,&na); str_uri = na.uri; } } } /* str_uri is taken from caller's contact or from header * for backwards compatibility with pre-3261 (from is already parsed)*/ if(!str_uri.len || !str_uri.s) str_uri = get_from(msg)->uri; /* parse Record-Route headers */ route.s = s = route_buffer; route.len = 0; fproxy_lr = 0; next_hop = empty_param; p_hdr = msg->record_route; if(p_hdr) { if (p_hdr->parsed==0 && parse_rr(p_hdr)!=0 ) { LM_ERR("failed to parse 'Record-Route:' header\n"); goto error; } record_route = (rr_t*)p_hdr->parsed; } else { record_route = 0; } if( record_route ) { if ( (tmp_s.s=find_not_quoted(&record_route->nameaddr.uri,';'))!=0 && tmp_s.s+1!=record_route->nameaddr.uri.s+ record_route->nameaddr.uri.len) { /* Parse all parameters */ tmp_s.len = record_route->nameaddr.uri.len - (tmp_s.s- record_route->nameaddr.uri.s); if (parse_params( &tmp_s, CLASS_URI, &hooks, &record_route->params) < 0) { LM_ERR("failed to parse record route uri params\n"); goto error; } fproxy_lr = (hooks.uri.lr != 0); LM_DBG("record_route->nameaddr.uri: %.*s\n", record_route->nameaddr.uri.len,record_route->nameaddr.uri.s); if(fproxy_lr){ LM_DBG("first proxy has loose routing\n"); copy_route(s,route.len,record_route->nameaddr.uri.s, record_route->nameaddr.uri.len); } } for(p_hdr = p_hdr->next;p_hdr;p_hdr = p_hdr->next) { /* filter out non-RR hdr and empty hdrs */ if( (p_hdr->type!=HDR_RECORDROUTE_T) || p_hdr->body.len==0) continue; if(p_hdr->parsed==0 && parse_rr(p_hdr)!=0 ){ LM_ERR("failed to parse header\n"); goto error; } for(record_route=p_hdr->parsed; record_route; record_route=record_route->next){ LM_DBG("record_route->nameaddr.uri: <%.*s>\n", record_route->nameaddr.uri.len, record_route->nameaddr.uri.s); copy_route(s,route.len,record_route->nameaddr.uri.s, record_route->nameaddr.uri.len); } } if(!fproxy_lr){ copy_route(s,route.len,str_uri.s,str_uri.len); str_uri = ((rr_t*)msg->record_route->parsed)->nameaddr.uri; } else { next_hop = ((rr_t*)msg->record_route->parsed)->nameaddr.uri; } } LM_DBG("calculated route: %.*s\n",route.len,route.len ? route.s : ""); LM_DBG("next r-uri: %.*s\n",str_uri.len,str_uri.len ? str_uri.s : ""); if ( REQ_LINE(msg).method_value==METHOD_INVITE || (twi->append && twi->append->add_body) ) { /* get body */ if( get_body(msg,&body)!=0 ){ LM_ERR("get_body failed\n"); goto error; } } else { body = empty_param; } /* flags & additional headers */ append.s = s = append_buf; if (sizeof(flag_t)*2+12+1 >= APPEND_BUFFER_MAX) { LM_ERR("buffer overflow while copying flags\n"); goto error; } append_str(s,"P-MsgFlags: ",12); l = APPEND_BUFFER_MAX - (12+1); /* include trailing `\n'*/ if (int2reverse_hex(&s, &l, (int)msg->msg_flags) == -1) { LM_ERR("buffer overflow while copying optional header\n"); goto error; } append_chr(s,'\n'); if ( twi->append && ((s=append2buf( s, APPEND_BUFFER_MAX-(s-append.s), msg, twi->append->elems))==0) ) goto error; /* body separator */ append_chr(s,'.'); append.len = s-append.s; eol_line(1).s = s = cmd_buf; if(twi->action.len+12 >= CMD_BUFFER_MAX){ LM_ERR("buffer overflow while copying command name\n"); goto error; } append_str(s,"sip_request.",12); append_str(s,twi->action.s,twi->action.len); eol_line(1).len = s-eol_line(1).s; eol_line(2)=REQ_LINE(msg).method; /* method type */ eol_line(3)=msg->parsed_uri.user; /* user from r-uri */ eol_line(4)=msg->parsed_uri.host; /* domain */ eol_line(5)=msg->rcv.bind_address->address_str; /* dst ip */ eol_line(6)=msg->rcv.dst_port==SIP_PORT ? empty_param : msg->rcv.bind_address->port_no_str; /* port */ /* r_uri ('Contact:' for next requests) */ eol_line(7)=*GET_RURI(msg); /* r_uri for subsequent requests */ eol_line(8)=str_uri.len?str_uri:empty_param; eol_line(9)=get_from(msg)->body; /* from */ eol_line(10)=msg->to->body; /* to */ eol_line(11)=msg->callid->body; /* callid */ eol_line(12)=get_from(msg)->tag_value; /* from tag */ eol_line(13)=get_to(msg)->tag_value; /* to tag */ eol_line(14)=get_cseq(msg)->number; /* cseq number */ eol_line(15).s=id_buf; /* hash:label */ s = int2str(hash_index, &l); if (l+1>=IDBUF_LEN) { LM_ERR("too big hash\n"); goto error; } memcpy(id_buf, s, l); id_buf[l]=':'; eol_line(15).len=l+1; s = int2str(label, &l); if (l+1+eol_line(15).len>=IDBUF_LEN) { LM_ERR("too big label\n"); goto error; } memcpy(id_buf+eol_line(15).len, s, l); eol_line(15).len+=l; eol_line(16) = route.len ? route : empty_param; eol_line(17) = next_hop; eol_line(18) = append; eol_line(19) = body; /* success */ return 1; error: /* 0 would lead to immediate script exit -- -1 returns * with 'false' to script processing */ return -1; } static int write_to_unixsock(char* sockname, int cnt) { int len, e; struct sockaddr_un dest; if (!sockname) { LM_ERR("invalid parameter\n"); return E_UNSPEC; } len = strlen(sockname); if (len == 0) { LM_ERR("empty socket name\n"); return -1; } else if (len > 107) { LM_ERR("socket name too long\n"); return -1; } memset(&dest, 0, sizeof(dest)); dest.sun_family = PF_LOCAL; memcpy(dest.sun_path, sockname, len); #ifdef HAVE_SOCKADDR_SA_LEN dest.sun_len = len; #endif e = connect(sock, (struct sockaddr*)&dest, SUN_LEN(&dest)); #ifdef HAVE_CONNECT_ECONNRESET_BUG /* * Workaround for a nasty bug in BSD kernels dated back * to the Berkeley days, so that can be found in many modern * BSD-derived kernels. Workaround should be pretty harmless since * in normal conditions connect(2) can never return ECONNRESET. */ if ((e == -1) && (errno == ECONNRESET)) e = 0; #endif if (e == -1) { LM_ERR("failed to connect: %s\n", strerror(errno)); return -1; } if (tsend_dgram_ev(sock, (struct iovec*)(void*)lines_eol, 2 * cnt, tm_unix_tx_timeout * 1000) < 0) { LM_ERR("writev failed: %s\n", strerror(errno)); return -1; } return 0; } int t_write_req(struct sip_msg* msg, char* info, char* vm_fifo) { if (assemble_msg(msg, (struct tw_info*)info) < 0) { LM_ERR("failed to assemble_msg\n"); return -1; } if (write_to_fifo(vm_fifo, TWRITE_PARAMS) == -1) { LM_ERR("write_to_fifo failed\n"); return -1; } /* make sure that if voicemail does not initiate a reply * timely, a SIP timeout will be sent out */ if (add_blind_uac() == -1) { LM_ERR("add_blind failed\n"); return -1; } return 1; } int t_write_unix(struct sip_msg* msg, char* info, char* socket) { if (assemble_msg(msg, (struct tw_info*)info) < 0) { LM_ERR("failed to assemble_msg\n"); return -1; } if (write_to_unixsock(socket, TWRITE_PARAMS) == -1) { LM_ERR("write_to_unixsock failed\n"); return -1; } /* make sure that if voicemail does not initiate a reply * timely, a SIP timeout will be sent out */ if (add_blind_uac() == -1) { LM_ERR("add_blind failed\n"); return -1; } return 1; } opensips-2.2.2/modules/tm/t_fifo.h000066400000000000000000000025671300170765700171040ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-31 200 for INVITE/UAS resent even for UDP (jiri) * 2004-11-15 t_write_xxx can print whatever avp/hdr */ #ifndef _TM_T_FIFO_H_ #define _TM_T_FIFO_H_ #include "../../parser/msg_parser.h" #include "../../sr_module.h" extern int tm_unix_tx_timeout; int fixup_t_write( void** param, int param_no); int parse_tw_append( modparam_t type, void* val); int init_twrite_lines(); int init_twrite_sock(void); int t_write_req(struct sip_msg* msg, char* info, char* vm_fifo); int t_write_unix(struct sip_msg* msg, char* info, char* sock_name); #endif opensips-2.2.2/modules/tm/t_funcs.c000066400000000000000000000152331300170765700172640ustar00rootroot00000000000000/* * transaction maintenance functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-31 200 for INVITE/UAS resent even for UDP (jiri) * info only if compiling w/ -DEXTRA_DEBUG (andrei) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-03-13 send_pr_buffer is called w/ file/function/line debugging * 2003-03-01 start_retr changed to retransmit only for UDP * 2003-02-13 modified send_pr_buffer to use msg_send & rb->dst (andrei) * 2003-04-14 use protocol from uri (jiri) * 2003-04-25 do it (^) really everywhere (jiri) * 2003-04-26 do it (^) really really really everywhere (jiri) * 2003-07-07 added get_proto calls when proxy!=0 (andrei) * 2004-02-13 t->is_invite and t->local replaced with flags (bogdan) * 2005-02-16 fr_*_timer acceps full AVP specifications; empty AVP * desable variable timer feature (bogdan) * 2007-01-25 DNS failover at transaction level added (bogdan) */ #include #include #include #include #include #include "../../dprint.h" #include "../../hash_func.h" #include "../../dset.h" #include "../../mem/mem.h" #include "t_funcs.h" #include "t_fwd.h" #include "t_msgbuilder.h" #include "t_lookup.h" #include "config.h" #include "../../context.h" /* ----------------------------------------------------- */ int send_pr_buffer( struct retr_buf *rb, void *buf, int len, #ifdef EXTRA_DEBUG char* file, const char *function, int line, #endif void* ctx) { if (buf && len && rb ) return msg_send( rb->dst.send_sock, rb->dst.proto, &rb->dst.to, rb->dst.proto_reserved1, buf, len, ctx); else { #ifdef EXTRA_DEBUG LM_CRIT("sending an empty buffer from %s: %s (%d)\n",file, function, line); #else LM_CRIT("attempt to send an empty buffer\n"); #endif return -1; } } void tm_shutdown(void) { LM_DBG("tm_shutdown : start\n"); unlink_timer_lists(); /* destroy the hash table */ LM_DBG("emptying hash table\n"); free_hash_table( ); LM_DBG("releasing timers\n"); free_timer_table(); LM_DBG("removing semaphores\n"); lock_cleanup(); LM_DBG("destroying callback lists\n"); destroy_tmcb_lists(); LM_DBG("tm_shutdown : done\n"); } /* returns 1 if everything was OK or -1 for error */ int t_release_transaction( struct cell *trans ) { set_kr(REQ_RLSD); reset_timer( & trans->uas.response.fr_timer ); reset_timer( & trans->uas.response.retr_timer ); cleanup_uac_timers( trans ); put_on_wait( trans ); return 1; } /* -----------------------HELPER FUNCTIONS----------------------- */ /* */ void put_on_wait( struct cell *Trans ) { #ifdef EXTRA_DEBUG LM_DBG("put on WAIT \n"); #endif /* we put the transaction on wait timer; we do it only once in transaction's timelife because putting it multiple-times might result in a second instance of a wait timer to be set after the first one fired; on expiration of the second instance, the transaction would be re-deleted PROCESS1 PROCESS2 TIMER PROCESS 0. 200/INVITE rx; put_on_wait 1. 200/INVITE rx; 2. WAIT fires; transaction about to be deleted 3. avoid putting on WAIT again 4. WAIT timer executed, transaction deleted */ set_1timer( &Trans->wait_tl, WT_TIMER_LIST, 0 ); } static int kill_transaction( struct cell *trans ) { char err_buffer[128]; int sip_err; int reply_ret; int ret; str reason; /* we reply statefully and enter WAIT state since error might have occurred in middle of forking and we do not want to put the forking burden on upstream client; however, it may fail too due to lack of memory */ ret=err2reason_phrase( ser_error, &sip_err, err_buffer, sizeof(err_buffer), "TM" ); if (ret>0) { reason.s = err_buffer; reason.len = ret; reply_ret=t_reply( trans, trans->uas.request, sip_err, &reason); /* t_release_transaction( T ); */ return reply_ret; } else { LM_ERR("err2reason failed\n"); return -1; } } int t_relay_to( struct sip_msg *p_msg , struct proxy_l *proxy, int flags) { int ret; int new_tran; int reply_ret; struct cell *t; ret=0; new_tran = t_newtran( p_msg, 1/*full UAS cloning*/ ); /* parsing error, memory alloc, whatever ... */ if (new_tran<0) { ret = new_tran; goto done; } /* if that was a retransmission, break from script */ if (new_tran==0) { goto done; } /* new transaction */ /* ACKs do not establish a transaction and are fwd-ed statelessly */ if ( p_msg->REQ_METHOD==METHOD_ACK) { LM_DBG("forwarding ACK\n"); /* send it out */ if (proxy==0) { proxy=uri2proxy(GET_NEXT_HOP(p_msg), p_msg->force_send_socket ? p_msg->force_send_socket->proto : PROTO_NONE ); if (proxy==0) { ret=E_BAD_ADDRESS; goto done; } ret=forward_request( p_msg , proxy); if (ret>=0) ret=1; free_proxy( proxy ); pkg_free( proxy ); } else { ret=forward_request( p_msg , proxy); if (ret>=0) ret=1; } goto done; } /* if replication flag is set, mark the transaction as local so that replies will not be relaied */ t=get_t(); if (flags&TM_T_REPLY_repl_FLAG) t->flags|=T_IS_LOCAL_FLAG; if (flags&TM_T_REPLY_nodnsfo_FLAG) t->flags|=T_NO_DNS_FAILOVER_FLAG; if (flags&TM_T_REPLY_reason_FLAG) t->flags|=T_CANCEL_REASON_FLAG; /* now go ahead and forward ... */ ret=t_forward_nonack( t, p_msg, proxy); if (ret<=0) { LM_DBG("t_forward_nonack returned error \n"); /* we don't want to pass upstream any reply regarding replicating * a request; replicated branch must stop at us*/ if (!(flags&(TM_T_REPLY_repl_FLAG|TM_T_REPLY_noerr_FLAG))) { reply_ret = kill_transaction( t ); if (reply_ret>0) { /* we have taken care of all -- do nothing in script */ LM_DBG("generation of a stateful reply on error succeeded\n"); ret=0; } else { LM_DBG("generation of a stateful reply on error failed\n"); } } } else { LM_DBG("new transaction fwd'ed\n"); } done: return ret; } opensips-2.2.2/modules/tm/t_funcs.h000066400000000000000000000122341300170765700172670ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-18 updated various function prototypes (andrei) * 2003-03-10 removed ifdef _OBSO & made redefined all the *UNREF* macros * in a non-gcc specific way (andrei) * 2003-03-13 now send_pr_buffer will be called w/ function/line info * only when compiling w/ -DEXTRA_DEBUG (andrei) * 2003-03-31 200 for INVITE/UAS resent even for UDP (jiri) * 2007-01-25 DNS failover at transaction level added (bogdan) */ #ifndef _T_FUNCS_H #define _T_FUNCS_H #include #include #include "../../mem/shm_mem.h" #include "../../parser/msg_parser.h" #include "../../globals.h" #include "../../msg_translator.h" #include "../../timer.h" #include "../../forward.h" #include "../../mem/mem.h" #include "../../md5utils.h" #include "../../ip_addr.h" #include "../../parser/parse_uri.h" #include "../../usr_avp.h" struct s_table; struct timer; struct entry; struct cell; struct retr_buf; #include "t_lookup.h" #include "config.h" #include "lock.h" #include "timer.h" #include "sip_msg.h" #include "h_table.h" #include "ut.h" extern int noisy_ctimer; /* t_reply_to flags */ #define TM_T_REPLY_repl_FLAG (1<<0) #define TM_T_REPLY_not_used (1<<1) #define TM_T_REPLY_noerr_FLAG (1<<2) #define TM_T_REPLY_nodnsfo_FLAG (1<<3) #define TM_T_REPLY_reason_FLAG (1<<4) /* send a private buffer: utilize a retransmission structure but take a separate buffer not referred by it; healthy for reducing time spend in REPLIES locks */ /* send a buffer -- 'PR' means private, i.e., it is assumed noone else can affect the buffer during sending time */ #ifdef EXTRA_DEBUG int send_pr_buffer( struct retr_buf *rb, void *buf, int len, char* file, const char *function, int line, void* ctx); #define SEND_PR_BUFFER(_rb,_bf,_le ) \ send_pr_buffer( (_rb), (_bf), (_le), __FILE__, __FUNCTION__, __LINE__, NULL) #define SEND_PR_CONTEXTS_BUFFER(_rb,_bf,_le, _ctx ) \ send_pr_buffer( (_rb), (_bf), (_le), __FILE__, __FUNCTION, __LINE__ ,_ctx) #else int send_pr_buffer( struct retr_buf *rb, void *buf, int len, void* ctx); #define SEND_PR_BUFFER(_rb,_bf,_le ) \ send_pr_buffer( (_rb), (_bf), (_le), NULL) #define SEND_PR_CONTEXTS_BUFFER(_rb,_bf,_le, _ctx ) \ send_pr_buffer( (_rb), (_bf), (_le), _ctx) #endif #define SEND_BUFFER( _rb ) \ SEND_PR_BUFFER( (_rb) , (_rb)->buffer.s , (_rb)->buffer.len ) #define SEND_CONTEXTS_BUFFER( _rb, ctx) \ SEND_PR_CONTEXTS_BUFFER( (_rb) , (_rb)->buffer.s, (_rb)->buffer.len, ctx) #define UNREF_UNSAFE(_T_cell) do { \ ((_T_cell)->ref_count--);\ LM_DBG("UNREF_UNSAFE: [%p] after is %d\n",_T_cell, (_T_cell)->ref_count);\ }while(0) #define REF(_T_cell) do{ \ LOCK_HASH( (_T_cell)->hash_index ); \ REF_UNSAFE(_T_cell); \ UNLOCK_HASH( (_T_cell)->hash_index ); }while(0) #define UNREF(_T_cell) do{ \ LOCK_HASH( (_T_cell)->hash_index ); \ UNREF_UNSAFE(_T_cell); \ UNLOCK_HASH( (_T_cell)->hash_index ); }while(0) #define REF_UNSAFE(_T_cell) do {\ ((_T_cell)->ref_count++);\ LM_DBG("REF_UNSAFE:[%p] after is %d\n",_T_cell, (_T_cell)->ref_count);\ }while(0) #define INIT_REF_UNSAFE(_T_cell) ((_T_cell)->ref_count=1) #define IS_REFFED_UNSAFE(_T_cell) ((_T_cell)->ref_count!=0) #define unset_timeout(timeout) ((timeout) = 0) #define is_timeout_set(timeout) ((timeout) != 0) static inline void _set_fr_retr( struct retr_buf *rb, int retr ) { utime_t timer; if (retr && !rb->retr_timer.deleted) { rb->retr_list=RT_T1_TO_1; set_timer( &rb->retr_timer, RT_T1_TO_1, NULL ); } if (!rb->my_T || !is_timeout_set(rb->my_T->fr_timeout)) set_1timer(&rb->fr_timer, FR_TIMER_LIST, NULL); else { timer = rb->my_T->fr_timeout; set_1timer(&rb->fr_timer, FR_TIMER_LIST, &timer); } } static inline void start_retr(struct retr_buf *rb) { _set_fr_retr(rb, rb->dst.proto==PROTO_UDP); } static inline void force_retr(struct retr_buf *rb) { _set_fr_retr(rb, 1); } void tm_shutdown(); /* function returns: * 1 - a new transaction was created * -1 - error, including retransmission */ int t_add_transaction( struct sip_msg* p_msg ); /* returns 1 if everything was OK or -1 for error */ int t_release_transaction( struct cell *trans ); int get_ip_and_port_from_uri( str* uri , unsigned int *param_ip, unsigned int *param_port); void put_on_wait( struct cell *Trans ); void cleanup_localcancel_timers( struct cell *t ); int t_relay_to( struct sip_msg *p_msg, struct proxy_l *proxy, int replicate); #endif opensips-2.2.2/modules/tm/t_fwd.c000066400000000000000000000572731300170765700167400ustar00rootroot00000000000000/* * Copyright (C) 2010-2014 OpenSIPS Solutions * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-02-13 proto support added (andrei) * 2003-02-24 s/T_NULL/T_NULL_CELL/ to avoid redefinition conflict w/ * nameser_compat.h (andrei) * 2003-03-01 kr set through a function now (jiri) * 2003-03-06 callbacks renamed; "blind UAC" introduced, which makes * transaction behave as if it was forwarded even if it was * not -- good for local UAS, like VM (jiri) * 2003-03-19 replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-03-30 we now watch downstream delivery and if it fails, send an * error message upstream (jiri) * 2003-04-14 use protocol from uri (jiri) * 2003-12-04 global TM callbacks switched to per transaction callbacks * (bogdan) * 2004-02-13: t->is_invite and t->local replaced with flags (bogdan) * 2007-01-25 DNS failover at transaction level added (bogdan) */ #include "../../dprint.h" #include "../../config.h" #include "../../ut.h" #include "../../dset.h" #include "../../timer.h" #include "../../hash_func.h" #include "../../globals.h" #include "../../action.h" #include "../../data_lump.h" #include "../../blacklists.h" #include "../../usr_avp.h" #include "../../mem/mem.h" #include "../../parser/parser_f.h" #include "t_funcs.h" #include "t_hooks.h" #include "t_msgbuilder.h" #include "ut.h" #include "t_cancel.h" #include "t_lookup.h" #include "t_fwd.h" #include "fix_lumps.h" #include "config.h" #include "../../msg_callbacks.h" /* route to execute for the branches */ static int goto_on_branch; int _tm_branch_index = 0; void t_on_branch( unsigned int go_to ) { struct cell *t = get_t(); /* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction; * in MODE_REQUEST T will be set only if the transaction was already * created; if not -> use the static variable */ if (route_type==BRANCH_ROUTE || !t || t==T_UNDEFINED ) goto_on_branch=go_to; else t->on_branch = go_to; } unsigned int get_on_branch(void) { return goto_on_branch; } static inline int pre_print_uac_request( struct cell *t, int branch, struct sip_msg *request) { int backup_route_type; struct usr_avp **backup_list; char *p; /* ... we calculate branch ... */ if (!t_calc_branch(t, branch, request->add_to_branch_s, &request->add_to_branch_len )) { LM_ERR("branch computation failed\n"); goto error; } /* from now on, flag all new lumps with LUMPFLAG_BRANCH flag in order to * be able to remove them later --bogdan */ set_init_lump_flags(LUMPFLAG_BRANCH); /* copy path vector into branch */ if (request->path_vec.len) { t->uac[branch].path_vec.s = shm_resize(t->uac[branch].path_vec.s, request->path_vec.len+1); if (t->uac[branch].path_vec.s==NULL) { LM_ERR("shm_resize failed\n"); goto error; } t->uac[branch].path_vec.len = request->path_vec.len; memcpy( t->uac[branch].path_vec.s, request->path_vec.s, request->path_vec.len+1); } /* do the same for the advertised port & address */ if (request->set_global_address.len) { t->uac[branch].adv_address.s = shm_resize(t->uac[branch].adv_address.s, request->set_global_address.len+1); if (t->uac[branch].adv_address.s==NULL) { LM_ERR("shm_resize failed for storing the advertised address " "(len=%d)\n",request->set_global_address.len); goto error; } t->uac[branch].adv_address.len = request->set_global_address.len; memcpy( t->uac[branch].adv_address.s, request->set_global_address.s, request->set_global_address.len+1); } if (request->set_global_port.len) { t->uac[branch].adv_port.s = shm_resize(t->uac[branch].adv_port.s, request->set_global_port.len+1); if (t->uac[branch].adv_port.s==NULL) { LM_ERR("shm_resize failed for storing the advertised port " "(len=%d)\n",request->set_global_port.len); goto error; } t->uac[branch].adv_port.len = request->set_global_port.len; memcpy( t->uac[branch].adv_port.s, request->set_global_port.s, request->set_global_port.len+1); } /********** run route & callback ************/ /* run branch route, if any; run it before RURI's DNS lookup * to allow to be changed --bogdan */ if (t->on_branch) { /* need to pkg_malloc the dst_uri */ if ( request->dst_uri.s && request->dst_uri.len>0 ) { if ( (p=pkg_malloc(request->dst_uri.len))==0 ) { LM_ERR("no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->dst_uri.s, request->dst_uri.len); request->dst_uri.s = p; } /* need to pkg_malloc the new_uri */ if ( (p=pkg_malloc(request->new_uri.len))==0 ) { LM_ERR("no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->new_uri.s, request->new_uri.len); request->new_uri.s = p; request->parsed_uri_ok = 0; /* make available the avp list from transaction */ backup_list = set_avp_list( &t->user_avps ); /* run branch route */ swap_route_type( backup_route_type, BRANCH_ROUTE); _tm_branch_index = branch; if (run_top_route(branch_rlist[t->on_branch].a, request)&ACT_FL_DROP) { LM_DBG("dropping branch <%.*s>\n", request->new_uri.len, request->new_uri.s); _tm_branch_index = 0; /* restore the route type */ set_route_type( backup_route_type ); /* restore original avp list */ set_avp_list( backup_list ); goto error; } _tm_branch_index = 0; /* restore the route type */ set_route_type( backup_route_type ); /* restore original avp list */ set_avp_list( backup_list ); } /* run the specific callbacks for this transaction */ run_trans_callbacks( TMCB_REQUEST_FWDED, t, request, 0, -request->REQ_METHOD); /* copy dst_uri into branch (after branch route possible updated it) */ if (request->dst_uri.len) { t->uac[branch].duri.s = shm_resize(t->uac[branch].duri.s, request->dst_uri.len); if (t->uac[branch].duri.s==NULL) { LM_ERR("shm_resize failed\n"); goto error; } t->uac[branch].duri.len = request->dst_uri.len; memcpy( t->uac[branch].duri.s,request->dst_uri.s,request->dst_uri.len); } return 0; error: return -1; } /* be aware and use it *all* the time between pre_* and post_* functions! */ static inline char *print_uac_request(struct sip_msg *i_req, unsigned int *len, struct socket_info *send_sock, enum sip_protos proto ) { char *buf; /* build the shm buffer now */ buf=build_req_buf_from_sip_req( i_req, len, send_sock, proto, MSG_TRANS_SHM_FLAG); if (!buf) { LM_ERR("no more shm_mem\n"); ser_error=E_OUT_OF_MEM; return NULL; } return buf; } static inline void post_print_uac_request(struct sip_msg *request, str *org_uri, str *org_dst) { reset_init_lump_flags(); /* delete inserted branch lumps */ del_flaged_lumps( &request->add_rm, LUMPFLAG_BRANCH); del_flaged_lumps( &request->body_lumps, LUMPFLAG_BRANCH); /* free any potential new uri */ if (request->new_uri.s!=org_uri->s) { pkg_free(request->new_uri.s); /* and just to be sure */ request->new_uri.s = 0; request->new_uri.len = 0; request->parsed_uri_ok = 0; } /* free any potential dst uri */ if (request->dst_uri.s!=org_dst->s) { pkg_free(request->dst_uri.s); /* and just to be sure */ request->dst_uri.s = 0; request->dst_uri.len = 0; } } /* introduce a new uac, which is blind -- it only creates the data structures and starts FR timer, but that's it; it does not print messages and send anything anywhere; that is good for FIFO apps -- the transaction must look operationally and FR must be ticking, whereas the request is "forwarded" using a non-SIP way and will be replied the same way */ int add_blind_uac(void) /*struct cell *t*/ { unsigned short branch; struct cell *t; t=get_t(); if (t==T_UNDEFINED || !t ) { LM_ERR("no transaction context\n"); return -1; } branch=t->nr_of_outgoings; if (branch==MAX_BRANCHES) { LM_ERR("maximum number of branches exceeded\n"); return -1; } t->nr_of_outgoings++; /* start FR timer -- protocol set by default to PROTO_NONE, which means retransmission timer will not be started */ start_retr(&t->uac[branch].request); /* we are on a timer -- don't need to put on wait on script clean-up */ set_kr(REQ_FWDED); return 1; /* success */ } static inline int update_uac_dst( struct sip_msg *request, struct ua_client *uac ) { struct socket_info* send_sock; char *shbuf; unsigned int len; send_sock = get_send_socket( request, &uac->request.dst.to , uac->request.dst.proto ); if (send_sock==0) { LM_ERR("failed to fwd to af %d, proto %d " " (no corresponding listening socket)\n", uac->request.dst.to.s.sa_family, uac->request.dst.proto ); ser_error=E_NO_SOCKET; return -1; } if (send_sock!=uac->request.dst.send_sock) { /* rebuild */ shbuf = print_uac_request( request, &len, send_sock, uac->request.dst.proto); if (!shbuf) { ser_error=E_OUT_OF_MEM; return -1; } if (uac->request.buffer.s) shm_free(uac->request.buffer.s); /* things went well, move ahead and install new buffer! */ uac->request.dst.send_sock = send_sock; uac->request.dst.proto_reserved1 = 0; uac->request.buffer.s = shbuf; uac->request.buffer.len = len; } return 0; } static inline unsigned int count_local_rr(struct sip_msg *req) { unsigned int cnt = 0; struct lump *r; /* we look for the RR anchors only * in the main list (no after or before) */ for( r=req->add_rm ; r ; r=r->next ) if ( r->type==HDR_RECORDROUTE_T && r->op==LUMP_NOP && r->after) { /* only the master RR anchor has "after" */ if (r->after->op==LUMP_ADD_OPT && r->after->u.cond==COND_IF_DIFF_REALMS) { /* conditional second RR hdr (when doing double RR) */ if (r->after->flags&LUMPFLAG_COND_TRUE) { cnt++; } } else { /* mandatory first RR hdr */ cnt++; } } return cnt; } /* introduce a new uac to transaction; returns its branch id (>=0) or error (<0); it doesn't send a message yet -- a reply to it might interfere with the processes of adding multiple branches */ static int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop, unsigned int bflags, str* path, struct proxy_l *proxy) { unsigned short branch; int do_free_proxy; int ret; branch=t->nr_of_outgoings; if (branch==MAX_BRANCHES) { LM_ERR("maximum number of branches exceeded\n"); ret=E_CFG; goto error; } /* check existing buffer -- rewriting should never occur */ if (t->uac[branch].request.buffer.s) { LM_CRIT("buffer rewrite attempt\n"); ret=ser_error=E_BUG; goto error; } /* set proper RURI to request to reflect the branch */ request->new_uri=*uri; request->parsed_uri_ok=0; request->dst_uri=*next_hop; request->path_vec=*path; request->ruri_bflags=bflags; if ( pre_print_uac_request( t, branch, request)!= 0 ) { ret = -1; goto error01; } /* check DNS resolution */ if (proxy){ do_free_proxy = 0; }else { proxy=uri2proxy( request->dst_uri.len ? &request->dst_uri:&request->new_uri, request->force_send_socket ? request->force_send_socket->proto : PROTO_NONE ); if (proxy==0) { ret=E_BAD_ADDRESS; goto error01; } do_free_proxy = 1; } msg_callback_process(request, REQ_PRE_FORWARD, (void *)proxy); if ( !(t->flags&T_NO_DNS_FAILOVER_FLAG) ) { t->uac[branch].proxy = shm_clone_proxy( proxy , do_free_proxy ); if (t->uac[branch].proxy==NULL) { ret = E_OUT_OF_MEM; goto error02; } } /* use the first address */ hostent2su( &t->uac[branch].request.dst.to, &proxy->host, proxy->addr_idx, proxy->port ? proxy->port:SIP_PORT); t->uac[branch].request.dst.proto = proxy->proto; if ( update_uac_dst( request, &t->uac[branch] )!=0) { ret = ser_error; goto error02; } /* things went well, move ahead */ t->uac[branch].uri.s=t->uac[branch].request.buffer.s+ request->first_line.u.request.method.len+1; t->uac[branch].uri.len=request->new_uri.len; t->uac[branch].br_flags = request->ruri_bflags; t->uac[branch].added_rr = count_local_rr( request ); t->nr_of_outgoings++; /* done! */ ret=branch; error02: if(do_free_proxy) { free_proxy( proxy ); pkg_free( proxy ); } error01: post_print_uac_request( request, uri, next_hop); if (ret < 0) { /* destroy all the bavps added, the path vector and the destination, * since this branch will never be properly added to * the UAC list, otherwise we'll have memory leaks - razvanc */ if (t->uac[branch].user_avps) destroy_avp_list(&t->uac[branch].user_avps); if (t->uac[branch].path_vec.s) shm_free(t->uac[branch].path_vec.s); if (t->uac[branch].adv_address.s) shm_free(t->uac[branch].adv_address.s); if (t->uac[branch].adv_port.s) shm_free(t->uac[branch].adv_port.s); if (t->uac[branch].duri.s) shm_free(t->uac[branch].duri.s); memset(&t->uac[branch],0,sizeof(t->uac[branch])); } error: return ret; } int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite, int branch ) { int ret; char *shbuf; unsigned int len; str bk_dst_uri; str bk_path_vec; str bk_adv_address; str bk_adv_port; if (t_cancel->uac[branch].request.buffer.s) { LM_CRIT("buffer rewrite attempt\n"); ret=ser_error=E_BUG; goto error; } cancel_msg->new_uri = t_invite->uac[branch].uri; cancel_msg->parsed_uri_ok=0; bk_dst_uri = cancel_msg->dst_uri; bk_path_vec = cancel_msg->path_vec; bk_adv_address = cancel_msg->set_global_address; bk_adv_port = cancel_msg->set_global_port; /* force same path & advertising as for request */ cancel_msg->path_vec = t_invite->uac[branch].path_vec; cancel_msg->set_global_address = t_invite->uac[branch].adv_address; cancel_msg->set_global_port = t_invite->uac[branch].adv_port; if ( pre_print_uac_request( t_cancel, branch, cancel_msg)!= 0 ) { ret = -1; goto error01; } /* force same uri as in INVITE */ if (cancel_msg->new_uri.s!=t_invite->uac[branch].uri.s) { pkg_free(cancel_msg->new_uri.s); cancel_msg->new_uri = t_invite->uac[branch].uri; /* and just to be sure */ cancel_msg->parsed_uri_ok = 0; } /* print */ shbuf=print_uac_request( cancel_msg, &len, t_invite->uac[branch].request.dst.send_sock, t_invite->uac[branch].request.dst.proto); if (!shbuf) { LM_ERR("printing e2e cancel failed\n"); ret=ser_error=E_OUT_OF_MEM; goto error01; } /* install buffer */ t_cancel->uac[branch].request.dst=t_invite->uac[branch].request.dst; t_cancel->uac[branch].request.buffer.s=shbuf; t_cancel->uac[branch].request.buffer.len=len; t_cancel->uac[branch].uri.s=t_cancel->uac[branch].request.buffer.s+ cancel_msg->first_line.u.request.method.len+1; t_cancel->uac[branch].uri.len=t_invite->uac[branch].uri.len; t_cancel->uac[branch].br_flags = cancel_msg->flags; /* success */ ret=1; error01: post_print_uac_request( cancel_msg, &t_invite->uac[branch].uri, &bk_dst_uri); cancel_msg->dst_uri = bk_dst_uri; cancel_msg->path_vec = bk_path_vec; cancel_msg->set_global_address = bk_adv_address; cancel_msg->set_global_port = bk_adv_port; error: return ret; } void cancel_invite(struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite ) { #define CANCEL_REASON_SIP_487 \ "Reason: SIP;cause=487;text=\"ORIGINATOR_CANCEL\"" CRLF branch_bm_t cancel_bitmap; str reason; struct hdr_field *hdr; cancel_bitmap=0; /* send back 200 OK as per RFC3261 */ reason.s = CANCELING; reason.len = sizeof(CANCELING)-1; t_reply( t_cancel, cancel_msg, 200, &reason ); reason.s = NULL; reason.len = 0; /* propagate the REASON flag ? */ if ( t_cancel->flags&T_CANCEL_REASON_FLAG ) { /* look for the Reason header */ if (parse_headers(cancel_msg, HDR_EOH_F, 0)<0) { LM_ERR("failed to parse all hdrs - ignoring Reason hdr\n"); } else { hdr = get_header_by_static_name(cancel_msg, "Reason"); if (hdr!=NULL) { reason.s = hdr->name.s; reason.len = hdr->len; } } } /* if no reason, use NORMAL CLEARING */ if (reason.s == NULL) { reason.s = CANCEL_REASON_SIP_487; reason.len = sizeof(CANCEL_REASON_SIP_487) - 1; } /* generate local cancels for all branches */ which_cancel(t_invite, &cancel_bitmap ); set_cancel_extra_hdrs( reason.s, reason.len); cancel_uacs(t_invite, cancel_bitmap ); set_cancel_extra_hdrs( NULL, 0); /* Do not do anything about branches with no received reply; * continue the retransmission hoping to get something back; * if still not, we will generate the 408 Timeout based on FR * timer; this helps with better coping with missed/lated provisional * replies in the context of cancelling the transaction */ #if 0 /* internally cancel branches with no received reply */ for (i=t_invite->first_branch; inr_of_outgoings; i++) { if (t_invite->uac[i].last_received==0){ /* reset the "request" timers */ reset_timer(&t_invite->uac[i].request.retr_timer); reset_timer(&t_invite->uac[i].request.fr_timer); LOCK_REPLIES( t_invite ); relay_reply(t_invite,FAKED_REPLY,i,487,&dummy_bm); } } #endif } /* function returns: * 1 - forward successful * -1 - error during forward */ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg , struct proxy_l * proxy) { str reply_reason_487 = str_init("Request Terminated"); str backup_uri; str backup_dst; int branch_ret, lowest_ret; str current_uri; branch_bm_t added_branches; int i, q; struct cell *t_invite; int success_branch; str dst_uri; struct socket_info *bk_sock; unsigned int br_flags, bk_bflags; int idx; str path; str bk_path; /* make -Wall happy */ current_uri.s=0; /* before doing enything, update the t falgs from msg */ t->uas.request->flags = p_msg->flags; if (p_msg->REQ_METHOD==METHOD_CANCEL) { t_invite=t_lookupOriginalT( p_msg ); if (t_invite!=T_NULL_CELL) { t_invite->flags |= T_WAS_CANCELLED_FLAG; cancel_invite( p_msg, t, t_invite ); return 1; } } /* do not forward requests which were already cancelled*/ if (no_new_branches(t)) { LM_INFO("discarding fwd for a 6xx transaction\n"); ser_error = E_NO_DESTINATION; return -1; } if (was_cancelled(t)) { /* is this the first attempt of sending a branch out ? */ if (t->nr_of_outgoings==0) { /* if no other signalling was performed on the transaction * and the transaction was already canceled, better * internally generate the 487 reply here */ t_reply( t, p_msg , 487 , &reply_reason_487); } LM_INFO("discarding fwd for a cancelled transaction\n"); ser_error = E_NO_DESTINATION; return -1; } /* backup current uri, sock and flags... add_uac changes it */ backup_uri = p_msg->new_uri; backup_dst = p_msg->dst_uri; bk_sock = p_msg->force_send_socket; bk_path = p_msg->path_vec; bk_bflags = p_msg->ruri_bflags; /* advertised address/port are not changed */ /* check if the UAS retranmission port needs to be updated */ if ( (p_msg->msg_flags ^ t->uas.request->msg_flags) & FL_FORCE_RPORT ) su_setport( &t->uas.response.dst.to, p_msg->rcv.src_port ); /* if no more specific error code is known, use this */ lowest_ret=E_BUG; /* branches added */ added_branches=0; /* branch to begin with */ t->first_branch=t->nr_of_outgoings; /* as first branch, use current uri */ current_uri = *GET_RURI(p_msg); branch_ret = add_uac( t, p_msg, ¤t_uri, &backup_dst, getb0flags(p_msg), &p_msg->path_vec, proxy); if (branch_ret>=0) added_branches |= 1<force_send_socket))!=0 ; idx++ ) { branch_ret = add_uac( t, p_msg, ¤t_uri, &dst_uri, br_flags, &path, proxy); /* pick some of the errors in case things go wrong; note that picking lowest error is just as good as any other algorithm which picks any other negative branch result */ if (branch_ret>=0) added_branches |= 1<new_uri=backup_uri; p_msg->parsed_uri_ok = 0;/* just to be sure; add_uac may parse other uris*/ p_msg->dst_uri = backup_dst; p_msg->force_send_socket = bk_sock; p_msg->path_vec = bk_path; p_msg->ruri_bflags = bk_bflags; /* update on_branch, if modified */ t->on_branch = get_on_branch(); /* update flags, if changed in branch route */ t->uas.request->flags = p_msg->flags; /* things went wrong ... no new branch has been fwd-ed at all */ if (added_branches==0) { LM_ERR("failure to add branches\n"); ser_error = lowest_ret; return lowest_ret; } /* send them out now */ success_branch=0; for (i=t->first_branch; inr_of_outgoings; i++) { if (added_branches & (1<uac[i].br_flags & tcp_no_new_conn_bflag) tcp_no_new_conn = 1; do { if (check_blacklists( t->uac[i].request.dst.proto, &t->uac[i].request.dst.to, t->uac[i].request.buffer.s, t->uac[i].request.buffer.len)) { LM_DBG("blocked by blacklists\n"); ser_error=E_IP_BLOCKED; } else { run_trans_callbacks(TMCB_PRE_SEND_BUFFER, t, p_msg, 0, i); if (SEND_BUFFER( &t->uac[i].request)==0) { ser_error = 0; break; } LM_ERR("sending request failed\n"); ser_error=E_SEND; } /* get next dns entry */ if ( t->uac[i].proxy==0 || get_next_su( t->uac[i].proxy, &t->uac[i].request.dst.to, (ser_error==E_IP_BLOCKED)?0:1)!=0 ) break; t->uac[i].request.dst.proto = t->uac[i].proxy->proto; /* update branch */ if ( update_uac_dst( p_msg, &t->uac[i] )!=0) break; }while(1); tcp_no_new_conn = 0; if (ser_error) { shm_free(t->uac[i].request.buffer.s); t->uac[i].request.buffer.s = NULL; t->uac[i].request.buffer.len = 0; continue; } success_branch++; start_retr( &t->uac[i].request ); set_kr(REQ_FWDED); /* successfully sent out -> run callbacks */ if ( has_tran_tmcbs( t, TMCB_REQUEST_BUILT) ) { set_extra_tmcb_params( &t->uac[i].request.buffer, &t->uac[i].request.dst); run_trans_callbacks( TMCB_REQUEST_BUILT, t, p_msg,0, -p_msg->REQ_METHOD); } } } return (success_branch>0)?1:-1; } int t_replicate(struct sip_msg *p_msg, str *dst, int flags) { /* this is a quite horrible hack -- we just take the message as is, including Route-s, Record-route-s, and Vias , forward it downstream and prevent replies received from relaying by setting the replication/local_trans bit; nevertheless, it should be good enough for the primary customer of this function, REGISTER replication if we want later to make it thoroughly, we need to introduce delete lumps for all the header fields above */ struct cell *t; if ( set_dst_uri( p_msg, dst)!=0 ) { LM_ERR("failed to set dst uri\n"); return -1; } if ( branch_uri2dset( GET_RURI(p_msg) )!=0 ) { LM_ERR("failed to convert uri to dst\n"); return -1; } t=get_t(); if (!t || t==T_UNDEFINED) { /* no transaction yet */ if (route_type==FAILURE_ROUTE) { LM_CRIT("BUG - undefined transaction in failure route\n"); return -1; } return t_relay_to( p_msg, NULL, flags|TM_T_REPLY_repl_FLAG); } else { /* transaction already created */ if (p_msg->REQ_METHOD==METHOD_ACK) /* local ACK */ return -1; t->flags|=T_IS_LOCAL_FLAG; return t_forward_nonack( t, p_msg, NULL ); } } int get_branch_index(void) { return _tm_branch_index; } opensips-2.2.2/modules/tm/t_fwd.h000066400000000000000000000031161300170765700167300ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-18 added proto to various function prototypes (andrei) */ #ifndef _T_FWD_H #define _T_FWD_H #include "../../proxy.h" #include "../../str.h" typedef int (*taddblind_f)( /*struct cell *t */ ); void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite ); int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite, int branch ); int add_blind_uac( ); int t_replicate(struct sip_msg *p_msg, str *dst, int flags); int t_forward_nonack( struct cell *t, struct sip_msg* p_msg, struct proxy_l * p); int t_forward_ack( struct sip_msg* p_msg ); void t_on_branch( unsigned int go_to ); unsigned int get_on_branch(); typedef int (*tgetbranch_f)(void); int get_branch_index(void); #endif opensips-2.2.2/modules/tm/t_hooks.c000066400000000000000000000152661300170765700172770ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-19 replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-12-04 global callbacks moved into transaction callbacks; * multiple events per callback added; single list per * transaction for all its callbacks (bogdan) * 2004-08-23 user avp(attribute value pair) added -> making avp list * available in callbacks (bogdan) */ #include "stdlib.h" #include "../../dprint.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../usr_avp.h" #include "t_hooks.h" #include "t_lookup.h" #include "t_funcs.h" struct tmcb_head_list* req_in_tmcb_hl = 0; struct tmcb_head_list tmcb_pending_hl = {0,0}; unsigned int tmcb_pending_id = (unsigned int)-1; void empty_tmcb_list(struct tmcb_head_list *head) { struct tm_callback *cbp, *cbp_tmp; for( cbp=head->first; cbp ; ) { cbp_tmp = cbp; cbp = cbp->next; if (cbp_tmp->release) cbp_tmp->release(cbp_tmp->param); shm_free( cbp_tmp ); } head->first = 0 ; head->reg_types = 0; } int init_tmcb_lists(void) { req_in_tmcb_hl = (struct tmcb_head_list*)shm_malloc ( sizeof(struct tmcb_head_list) ); if (req_in_tmcb_hl==0) { LM_CRIT("no more shared memory\n"); return -1; } req_in_tmcb_hl->first = 0; req_in_tmcb_hl->reg_types = 0; return 1; } void destroy_tmcb_lists(void) { if (!req_in_tmcb_hl) return; empty_tmcb_list(req_in_tmcb_hl); shm_free(req_in_tmcb_hl); } int insert_tmcb(struct tmcb_head_list *cb_list, int types, transaction_cb f, void *param, release_tmcb_param release_func ) { struct tm_callback *cbp; /* build a new callback structure */ if (!(cbp=shm_malloc( sizeof( struct tm_callback)))) { LM_ERR("no more shared memory\n"); return E_OUT_OF_MEM; } /* link it into the proper place... */ cbp->next = cb_list->first; cb_list->first = cbp; cb_list->reg_types |= types; /* ... and fill it up */ cbp->callback = f; cbp->param = param; cbp->release = release_func; cbp->types = types; if (cbp->next) cbp->id = cbp->next->id+1; else cbp->id = 0; return 1; } /* register a callback function 'f' for 'types' mask of events; * will be called back whenever one of the events occurs in transaction module * (global or per transaction, depending of event type) */ int register_tmcb( struct sip_msg* p_msg, struct cell *t, int types, transaction_cb f, void *param, release_tmcb_param release_func ) { struct tmcb_head_list *cb_list; /* are the callback types valid?... */ if ( types<0 || types>TMCB_MAX ) { LM_CRIT("invalid callback types: mask=%d\n", types); return E_BUG; } /* we don't register null functions */ if (f==0) { LM_CRIT("null callback function\n"); return E_BUG; } if (types&TMCB_REQUEST_IN) { if (types!=TMCB_REQUEST_IN) { LM_CRIT("callback type TMCB_REQUEST_IN " "can't be register along with types\n"); return E_BUG; } if (req_in_tmcb_hl==0) { LM_ERR("callback type TMCB_REQUEST_IN " "registration attempt before TM module initialization\n"); return E_CFG; } cb_list = req_in_tmcb_hl; } else { if (!t) { if (!p_msg) { LM_CRIT("no sip_msg, nor transaction given\n"); return E_BUG; } /* look for the transaction */ t = get_t(); if ( t!=NULL && t!=T_UNDEFINED ){ cb_list = &(t->tmcb_hl); } else { /* no transaction found -> link it to waitting list */ if (p_msg->id!=tmcb_pending_id) { empty_tmcb_list(&tmcb_pending_hl); tmcb_pending_id = p_msg->id; } cb_list = &(tmcb_pending_hl); } } else { cb_list = &(t->tmcb_hl); } } return insert_tmcb( cb_list, types, f, param, release_func ); } static void *tmcb_extra1 = NULL; static void *tmcb_extra2 = NULL; void set_extra_tmcb_params(void *extra1, void *extra2) { tmcb_extra1 = extra1; tmcb_extra2 = extra2; } void run_trans_callbacks( int type , struct cell *trans, struct sip_msg *req, struct sip_msg *rpl, int code ) { struct tmcb_params params; struct tm_callback *cbp; struct usr_avp **backup; struct cell *trans_backup = get_t(); params.req = req; params.rpl = rpl; params.code = code; params.extra1 = tmcb_extra1; params.extra2 = tmcb_extra2; if (trans->tmcb_hl.first==0 || ((trans->tmcb_hl.reg_types)&type)==0 ) return; backup = set_avp_list( &trans->user_avps ); for (cbp=trans->tmcb_hl.first; cbp; cbp=cbp->next) { if ( (cbp->types)&type ) { LM_DBG("trans=%p, callback type %d, id %d entered\n", trans, type, cbp->id ); params.param = &(cbp->param); cbp->callback( trans, type, ¶ms ); } } /* env cleanup */ set_avp_list( backup ); tmcb_extra1 = tmcb_extra2 = 0; set_t(trans_backup); } void run_reqin_callbacks( struct cell *trans, struct sip_msg *req, int code ) { struct tmcb_params params; struct tm_callback *cbp; struct usr_avp **backup; struct cell *trans_backup = get_t(); params.req = req; params.rpl = 0; params.code = code; params.extra1 = tmcb_extra1; params.extra2 = tmcb_extra2; if (req_in_tmcb_hl->first==0) return; backup = set_avp_list( &trans->user_avps ); for (cbp=req_in_tmcb_hl->first; cbp; cbp=cbp->next) { LM_DBG("trans=%p, callback type %d, id %d entered\n", trans, cbp->types, cbp->id ); params.param = &(cbp->param); cbp->callback( trans, cbp->types, ¶ms ); if (req && req->dst_uri.len==-1) { LM_CRIT("callback REQIN id %d entered\n", cbp->id ); req->dst_uri.len = 0; } } set_avp_list( backup ); tmcb_extra1 = tmcb_extra2 = 0; set_t(trans_backup); } void run_trans_callbacks_locked( int type , struct cell *trans, struct sip_msg *req, struct sip_msg *rpl, int code ) { if (trans->tmcb_hl.first==0 || ((trans->tmcb_hl.reg_types)&type)==0 ) return; LOCK_REPLIES(trans); /* run callbacks */ run_trans_callbacks( type , trans, req, rpl, code ); /* SHM message cleanup */ if (trans->uas.request && trans->uas.request->msg_flags&FL_SHM_CLONE) clean_msg_clone( trans->uas.request, trans->uas.request, trans->uas.end_request); UNLOCK_REPLIES(trans); } opensips-2.2.2/modules/tm/t_hooks.h000066400000000000000000000211761300170765700173010ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-16 : backwards-compatibility callback names introduced (jiri) * 2003-03-06 : old callbacks renamed, new one introduced (jiri) * 2003-12-04 : global callbacks moved into transaction callbacks; * multiple events per callback added; single list per * transaction for all its callbacks (bogdan) * 2006-03-29 : added transaction callbacks: TMCB_REQUEST_BUILT and * TMCB_REQUEST_DELETED (bogdan) */ #ifndef _HOOKS_H #define _HOOKS_H struct sip_msg; struct cell; #define TMCB_REQUEST_IN (1<<0) #define TMCB_RESPONSE_IN (1<<1) #define TMCB_REQUEST_FWDED (1<<2) #define TMCB_RESPONSE_FWDED (1<<3) #define TMCB_ON_FAILURE (1<<5) #define TMCB_RESPONSE_PRE_OUT (1<<6) #define TMCB_RESPONSE_OUT (1<<7) #define TMCB_LOCAL_COMPLETED (1<<8) #define TMCB_LOCAL_RESPONSE_OUT (1<<9) #define TMCB_REQUEST_BUILT (1<<10) #define TMCB_TRANS_CANCELLED (1<<11) #define TMCB_TRANS_DELETED (1<<12) #define TMCB_PRE_SEND_BUFFER (1<<13) #define TMCB_MAX ((1<<14)-1) /* * Caution: most of the callbacks work with shmem-ized messages * which you can no more change (e.g., lumps are fixed). Most * reply-processing callbacks are also called from a mutex, * which may cause deadlock if you are not careful. Also, reply * callbacks may pass the value of FAKED_REPLY messages, which * is a non-dereferencable pointer indicating that no message * was received and a timer hit instead. * * All callbacks excepting the TMCB_REQUEST_IN are associates to a * transaction. It means they will be run only when the event will hint * the transaction the callbacks were register for. * TMCB_REQUEST_IN is a global callback - it means it will be run for * all transactions. * * * Callback description: * --------------------- * * TMCB_REQUEST_IN -- a brand-new request was received and is * about to establish transaction; it is not yet cloned and * lives in pkg mem -- your last chance to mangle it before * it gets shmem-ized (then, it's read-only); it's called from * HASH_LOCK, so be careful. It is guaranteed not to be * a retransmission. The transactional context is mostly * incomplete -- this callback is called in very early stage * before the message is shmem-ized (so that you can work * with it). * * TMCB_RESPONSE_IN -- a brand-new reply was received which matches * an existing transaction. It may or may not be a retransmission. * * TMCB_RESPONSE_PRE_OUT -- a final reply is about to be sent out * (either local or proxied); you cannnot change the reply, but * it is useful to update your state before putting the reply on * the network and to avoid any races (receiving an ACK before * updating with the status of the reply) * * TMCB_RESPONSE_OUT -- a final reply was sent out (either local * or proxied) -- there is nothing more you can change from * the callback, it is good for accounting-like uses. * * Note: the message passed to callback may also have * value FAKED_REPLY (like other reply callbacks) which * indicates a pseudo_reply caused by a timer. Check for * this value before deferring -- you will cause a segfault * otherwise. Check for t->uas.request validity too if you * need it ... locally initiated UAC transactions set it to 0. * * (obsolete) TMCB_ON_FAILURE_RO -- called on receipt of a reply or timer; * it means all branches completed with a failure; the callback * function MUST not change anything in the transaction (READONLY) * that's a chance for doing ACC or stuff like this * * TMCB_ON_FAILURE -- called on receipt of a reply or timer; * it means all branches completed with a failure; that's * a chance for example to add new transaction branches * * TMCB_RESPONSE_FWDED -- called when a reply is about to be * forwarded; it is called after a message is received but before * a message is sent out: it is called when the decision is * made to forward a reply; it is parametrized by pkg message * which caused the transaction to complete (which is not * necessarily the same which will be forwarded). As forwarding * has not been executed and may fail, there is no guarantee * a reply will be successfully sent out at this point of time. * * Note: TMCB_REPLY_ON_FAILURE and TMCB_REPLY_FWDED are * called from reply mutex which is used to deterministically * process multiple replies received in parallel. A failure * to set the mutex again or stay too long in the callback * may result in deadlock. * * Note: the reply callbacks will not be evoked if "silent * C-timer hits". That's a feature to clean transactional * state from a proxy quickly -- transactions will then * complete statelessly. If you wish to disable this * feature, either set the global option "noisy_ctimer" * to 1, or set t->noisy_ctimer for selected transaction. * * TMCB_REQUEST_FWDED -- request is being forwarded out. It is * called before a message is forwarded and it is your last * chance to change its shape. * * TMCB_LOCAL_COMPLETED -- final reply for localy initiated * transaction arrived. Message may be FAKED_REPLY. * * * IMPORTANT NOTES: * * 1) that callbacks MUST be installed before forking * (callback lists do not live in shmem and have no access * protection), i.e., at best from mod_init functions. * * 2) the callback's param MUST be in shared memory and will * NOT be freed by TM; you must do it yourself from the * callback function if necessary. */ /* pack structure with all params passed to callback function */ struct tmcb_params { struct sip_msg* req; struct sip_msg* rpl; int code; void **param; void *extra1; void *extra2; }; /* callback function prototype */ typedef void (transaction_cb) (struct cell* t, int type, struct tmcb_params*); /* function to release the callback param */ typedef void (release_tmcb_param) (void *param); /* register callback function prototype */ typedef int (*register_tmcb_f)(struct sip_msg* p_msg, struct cell *t, int cb_types, transaction_cb f, void *param, release_tmcb_param func); struct tm_callback { int id; /* id of this callback - useless */ int types; /* types of events that trigger the callback*/ transaction_cb* callback; /* callback function */ void *param; /* param to be passed to callback function */ release_tmcb_param *release; /* function to release the callback param when the callback is deleted */ struct tm_callback* next; }; struct tmcb_head_list { struct tm_callback *first; int reg_types; }; extern struct tmcb_head_list* req_in_tmcb_hl; extern struct tmcb_head_list tmcb_pending_hl; extern unsigned int tmcb_pending_id; #define has_tran_tmcbs(_T_, _types_) \ ( ((_T_)->tmcb_hl.reg_types)&(_types_) ) #define has_reqin_tmcbs() \ ( req_in_tmcb_hl->first!=0 ) void empty_tmcb_list(struct tmcb_head_list *head); int init_tmcb_lists(void); void destroy_tmcb_lists(void); /* register a callback for several types of events */ int register_tmcb( struct sip_msg* p_msg, struct cell *t, int types, transaction_cb f, void *param, release_tmcb_param release_func ); /* inserts a callback into the a callback list */ int insert_tmcb(struct tmcb_head_list *cb_list, int types, transaction_cb f, void *param, release_tmcb_param release_func ); /* set extra params for callbacks */ void set_extra_tmcb_params(void *extra1, void *extra2); /* run all transaction callbacks for an event type */ void run_trans_callbacks( int type , struct cell *trans, struct sip_msg *req, struct sip_msg *rpl, int code ); void run_trans_callbacks_locked( int type , struct cell *trans, struct sip_msg *req, struct sip_msg *rpl, int code ); /* run all REQUEST_IN callbacks */ void run_reqin_callbacks( struct cell *trans, struct sip_msg *req, int code ); typedef int (*ctx_load_register_func)(void*); #endif opensips-2.2.2/modules/tm/t_lookup.c000066400000000000000000001107051300170765700174570ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ---------- * 2003-01-23 options for disabling r-uri matching introduced (jiri) * nameser_compat.h (andrei) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2003-01-28 scratchpad removed (jiri) * 2003-02-13 init_rb() is proto indep. & it uses struct dest_info (andrei) * 2003-02-24 s/T_NULL/T_NULL_CELL/ to avoid redefinition conflict w/ * 2003-02-27 3261 ACK/200 consumption bug removed (jiri) * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-03-01 kr set through a function now (jiri) * 2003-03-06 dialog matching introduced for ACKs -- that's important for * INVITE UAS (like INVITE) and 200/ACK proxy matching (jiri) * 2003-03-29 optimization: e2e ACK matching only if callback installed * (jiri) * 2003-03-30 set_kr for requests only (jiri) * 2003-04-04 bug_fix: RESPONSE_IN callback not called for local * UAC transactions (jiri) * 2003-04-07 new transactions inherit on_negative and on_relpy from script * variables on instantiation (jiri) * 2003-04-30 t_newtran clean up (jiri) * 2003-08-21 request lookups fixed to skip UAC transactions, * thanks Ed (jiri) * 2003-12-04 global TM callbacks switched to per transaction callbacks * (bogdan) * 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri) * 2004-02-13: t->is_invite and t->local replaced with flags (bogdan) * 2004-10-10: use of mhomed disabled for replies (jiri) * 2005-02-01: use the incoming request interface for sending the replies * - changes in init_rb() (bogdan) * * * This C-file takes care of matching requests and replies with * existing transactions. Note that we do not do SIP-compliant * request matching as asked by SIP spec. We do bitwise matching of * all header fields in requests which form a transaction key. * It is much faster and it works pretty well -- we haven't * had any interop issue neither in lab nor in bake-offs. The reason * is that retransmissions do look same as original requests * (it would be really silly if they would be mangled). The only * exception is we parse To as To in ACK is compared to To in * reply and both of them are constructed by different software. * * As for reply matching, we match based on branch value -- that is * faster too. There are two versions .. with SYNONYMs #define * enabled, the branch includes ordinal number of a transaction * in a synonym list in hash table and is somewhat faster but * not reboot-resilient. SYNONYMs turned off are little slower * but work across reboots as well. * * The branch parameter is formed as follows: * SYNONYMS on: hash.synonym.branch * SYNONYMS off: hash.md5.branch */ #include "../../dprint.h" #include "../../parser/parser_f.h" #include "../../parser/parse_from.h" #include "../../ut.h" #include "../../timer.h" #include "../../hash_func.h" #include "../../forward.h" #include "t_funcs.h" #include "config.h" #include "sip_msg.h" #include "t_hooks.h" #include "t_lookup.h" #include "dlg.h" /* for t_lookup_callid */ #include "t_msgbuilder.h" /* for t_lookup_callid */ #include "t_fwd.h" /* for get_on_branch */ #define EQ_VIA_LEN(_via)\ ( (p_msg->via1->bsize-(p_msg->_via->name.s-(p_msg->_via->hdr.s+p_msg->_via->hdr.len)))==\ (t_msg->via1->bsize-(t_msg->_via->name.s-(t_msg->_via->hdr.s+t_msg->_via->hdr.len))) ) #define EQ_LEN(_hf) (t_msg->_hf->body.len==p_msg->_hf->body.len) #define EQ_REQ_URI_LEN\ (p_msg->first_line.u.request.uri.len==t_msg->first_line.u.request.uri.len) #define EQ_STR(_hf) (memcmp(t_msg->_hf->body.s,\ p_msg->_hf->body.s, \ p_msg->_hf->body.len)==0) #define EQ_REQ_URI_STR\ ( memcmp( t_msg->first_line.u.request.uri.s,\ p_msg->first_line.u.request.uri.s,\ p_msg->first_line.u.request.uri.len)==0) #define EQ_VIA_STR(_via)\ ( memcmp( t_msg->_via->name.s,\ p_msg->_via->name.s,\ (t_msg->via1->bsize-(t_msg->_via->name.s-(t_msg->_via->hdr.s+t_msg->_via->hdr.len)))\ )==0 ) #define EQ_STRS( _s1, _s2 ) \ ( (_s1).len==(_s2).len && memcmp((_s1).s,(_s2).s,(_s2).len)==0) #define HF_LEN(_hf) ((_hf)->len) /* should be request-uri matching used as a part of pre-3261 * transaction matching, as the standard wants us to do so * (and is reasonable to do so, to be able to distinguish * spirals)? turn only off for better interaction with * devices that are broken and send different r-uri in * CANCEL/ACK than in original INVITE */ int ruri_matching=1; int via1_matching=1; /* by default we automaticaly send 100 Trying on trasacation creation */ int auto_100trying=1; /* this is a global variable which keeps pointer to transaction currently processed by a process; it it set by t_lookup_request or t_reply_matching; don't dare to change it anywhere else as it would break ref_counting */ static struct cell *T; /* simillar to T, but it is used for the cancelled invite * transaction (when processing a CANCEL) */ static struct cell *cancelled_T; /* simillar to T, but it is used for the ack-ed invite * transaction (when processing a end-to-end 200 ACK ) */ static struct cell *e2eack_T; static str relay_reason_100 = str_init("Giving a try"); struct cell *get_t(void) { return T; } void set_t(struct cell *t) { T=t; } void init_t(void) {set_t(T_UNDEFINED);} struct cell *get_cancelled_t(void) { return cancelled_T; } void set_cancelled_t(struct cell* t) { cancelled_T=t; } void reset_cancelled_t(void) { cancelled_T=T_UNDEFINED; } struct cell *get_e2eack_t(void) { return e2eack_T; } void set_e2eack_t(struct cell* t) { e2eack_T=t; } void reset_e2eack_t(void) { e2eack_T=T_UNDEFINED; } static inline int parse_dlg( struct sip_msg *msg ) { if (parse_headers(msg, HDR_FROM_F | HDR_CSEQ_F | HDR_TO_F, 0)==-1) { LM_ERR("From or Cseq or To invalid\n"); return 0; } if ((msg->from==0)||(msg->cseq==0)||(msg->to==0)) { LM_ERR("missing From or Cseq or To\n"); return 0; } if (parse_from_header(msg)<0) { LM_ERR("From broken\n"); return 0; } /* To is automatically parsed through HDR_TO in parse bitmap, * we don't need to worry about it now if (parse_to_header(msg)==-1) { LM_ERR("To broken\n"); return 0; } */ return 1; } /* is the ACK (p_msg) in p_msg dialog-wise equal to the INVITE (t_msg) * except to-tags? */ static inline int partial_dlg_matching(struct sip_msg *t_msg, struct sip_msg *p_msg) { struct to_body *inv_from; if (!EQ_LEN(callid)) return 0; if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len) return 0; inv_from=get_from(t_msg); if (!inv_from) { LM_ERR("INV/From not parsed\n"); return 0; } if (inv_from->tag_value.len!=get_from(p_msg)->tag_value.len) return 0; if (!EQ_STR(callid)) return 0; if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s, get_cseq(p_msg)->number.len)!=0) return 0; if (memcmp(inv_from->tag_value.s, get_from(p_msg)->tag_value.s, get_from(p_msg)->tag_value.len)!=0) return 0; return 1; } /* are to-tags in ACK/200 same as those we sent out? */ static inline int dlg_matching(struct cell *p_cell, struct sip_msg *ack ) { if (get_to(ack)->tag_value.len!=p_cell->uas.local_totag.len) return 0; if (memcmp(get_to(ack)->tag_value.s,p_cell->uas.local_totag.s, p_cell->uas.local_totag.len)!=0) return 0; return 1; } static inline int ack_matching(struct cell *p_cell, struct sip_msg *p_msg) { /* partial dialog matching -- no to-tag, only from-tag, * callid, cseq number ; */ if (!partial_dlg_matching(p_cell->uas.request, p_msg)) return 0; /* if this transaction is proxied (as opposed to UAS) we're * done now -- we ignore to-tags; the ACK simply belongs to * this UAS part of dialog, whatever to-tag it gained */ if (p_cell->relaied_reply_branch!=-2) { return 2; /* e2e proxied ACK */ } /* it's a local dialog -- we wish to verify to-tags too */ if (dlg_matching(p_cell, p_msg)) { return 2; } return 0; } /* branch-based transaction matching */ static inline int via_matching( struct via_body *inv_via, struct via_body *ack_via ) { if (inv_via->tid.len!=ack_via->tid.len) return 0; if (memcmp(inv_via->tid.s, ack_via->tid.s, ack_via->tid.len)!=0) return 0; /* ok, tid matches -- now make sure that the * originator matches too to avoid confusion with * different senders generating the same tid */ if (inv_via->host.len!=ack_via->host.len) return 0;; if (memcmp(inv_via->host.s, ack_via->host.s, ack_via->host.len)!=0) return 0; if (inv_via->port!=ack_via->port) return 0; if (inv_via->transport.len!=ack_via->transport.len) return 0; if (memcmp(inv_via->transport.s, ack_via->transport.s, ack_via->transport.len)!=0) return 0; /* everything matched -- we found it */ return 1; } /* transaction matching a-la RFC-3261 using transaction ID in branch (the function assumes there is magic cookie in branch) It returns: 2 if e2e ACK for a proxied transaction found 1 if found (covers ACK for local UAS) 0 if not found (trans undefined) */ static int matching_3261( struct sip_msg *p_msg, struct cell **trans, enum request_method skip_method) { struct cell *p_cell; struct sip_msg *t_msg; struct via_body *via1; int is_ack; int dlg_parsed; int ret = 0; struct cell *e2e_ack_trans; e2e_ack_trans=0; via1=p_msg->via1; is_ack=p_msg->REQ_METHOD==METHOD_ACK; dlg_parsed=0; /* update parsed tid */ via1->tid.s=via1->branch->value.s+MCOOKIE_LEN; via1->tid.len=via1->branch->value.len-MCOOKIE_LEN; for ( p_cell = get_tm_table()->entrys[p_msg->hash_index].first_cell; p_cell; p_cell = p_cell->next_cell ) { t_msg=p_cell->uas.request; if (!t_msg) continue; /* don't try matching UAC transactions */ if (skip_method & t_msg->REQ_METHOD) continue; /* here we do an exercise which will be removed from future code * versions: we try to match end-2-end ACKs if they appear at our * server. This allows some applications bound to TM via callbacks * to correlate the e2e ACKs with transaction context, e.g., for * purpose of accounting. We think it is a bad place here, among * other things because it is not reliable. If a transaction loops * via OpenSIPS the ACK can't be matched to proper INVITE transaction * (it is a separate transactino with its own branch ID) and it * matches all transaction instances in the loop dialog-wise. * Eventually, regardless to which transaction in the loop the * ACK belongs, only the first one will match. */ /* dialog matching needs to be applied for ACK/200s */ if (is_ack && e2e_ack_trans==0 && p_cell->uas.status>=200 && p_cell->uas.status<300) { /* make sure we have parsed all things we need for dialog * matching */ if (!dlg_parsed) { dlg_parsed=1; if (!parse_dlg(p_msg)) { LM_ERR("dlg parsing failed\n"); return 0; } } ret=ack_matching(p_cell /* t w/invite */, p_msg /* ack */); if (ret>0) { e2e_ack_trans=p_cell; continue; } /* this ACK is neither local "negative" one, nor a proxied * end-2-end one, nor an end-2-end one for a UAS transaction * -- we failed to match */ continue; } /* now real tid matching occurs for negative ACKs and any * other requests */ if (!via_matching(t_msg->via1 /* inv via */, via1 /* ack */ )) continue; /* all matched -- we found the transaction ! */ LM_DBG("RFC3261 transaction matched, tid=%.*s\n", via1->tid.len, via1->tid.s); *trans=p_cell; return 1; } /* :-( ... we didn't find any */ /* just check if it we found an e2e ACK previously */ if (e2e_ack_trans) { *trans=e2e_ack_trans; return ret; } LM_DBG("RFC3261 transaction matching failed\n"); return 0; } /* function returns: * negative - transaction wasn't found * -2 - possibly e2e ACK matched * positive - transaction found */ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked ) { struct cell *p_cell; unsigned int isACK; struct sip_msg *t_msg; struct via_param *branch; int match_status; isACK = p_msg->REQ_METHOD==METHOD_ACK; if (isACK) { if (e2eack_T==NULL) return -1; if (e2eack_T!=T_UNDEFINED) return -2; } /* parse all*/ if (check_transaction_quadruple(p_msg)==0) { LM_ERR("too few headers\n"); set_t(0); /* stop processing */ return 0; } /* start searching into the table */ if (!p_msg->hash_index) p_msg->hash_index=tm_hash( p_msg->callid->body , get_cseq(p_msg)->number ) ; LM_DBG("start searching: hash=%d, isACK=%d\n", p_msg->hash_index,isACK); /* first of all, look if there is RFC3261 magic cookie in branch; if * so, we can do very quick matching and skip the old-RFC bizzar * comparison of many header fields */ if (!p_msg->via1) { LM_ERR("no via\n"); set_t(0); return 0; } branch=p_msg->via1->branch; if (branch && branch->value.s && branch->value.len>MCOOKIE_LEN && memcmp(branch->value.s,MCOOKIE,MCOOKIE_LEN)==0) { /* huhuhu! the cookie is there -- let's proceed fast */ LOCK_HASH(p_msg->hash_index); match_status=matching_3261(p_msg,&p_cell, /* skip transactions with different method; otherwise CANCEL * would match the previous INVITE trans. */ isACK ? ~METHOD_INVITE: ~p_msg->REQ_METHOD); switch(match_status) { case 0: goto notfound; /* no match */ case 1: goto found; /* match */ case 2: goto e2e_ack; /* e2e proxy ACK */ } } /* ok -- it's ugly old-fashioned transaction matching -- it is * a bit simplified to be fast -- we don't do all the comparisons * of parsed uri, which was simply too bloated */ LM_DBG("proceeding to pre-RFC3261 transaction matching\n"); /* lock the whole entry*/ LOCK_HASH(p_msg->hash_index); /* all the transactions from the entry are compared */ for ( p_cell = get_tm_table()->entrys[p_msg->hash_index].first_cell; p_cell; p_cell = p_cell->next_cell ) { t_msg = p_cell->uas.request; if (!t_msg) continue; /* skip UAC transactions */ if (!isACK) { /* compare lengths first */ if (!EQ_LEN(callid)) continue; if (!EQ_LEN(cseq)) continue; if (!EQ_LEN(from)) continue; if (!EQ_LEN(to)) continue; if (ruri_matching && !EQ_REQ_URI_LEN) continue; if (via1_matching && !EQ_VIA_LEN(via1)) continue; /* length ok -- move on */ if (!EQ_STR(callid)) continue; if (!EQ_STR(cseq)) continue; if (!EQ_STR(from)) continue; if (!EQ_STR(to)) continue; if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; /* request matched ! */ LM_DBG("non-ACK matched\n"); goto found; } else { /* it's an ACK request*/ /* ACK's relate only to INVITEs - and we look only for 2 types of * INVITEs : (a) negative INVITEs or (b) pozitive UAS INVITEs */ if ( t_msg->REQ_METHOD!=METHOD_INVITE || !(p_cell->uas.status>=300 || (p_cell->nr_of_outgoings==0 && p_cell->uas.status>=200)) ) continue; /* From|To URI , CallID, CSeq # must be always there */ /* compare lengths now */ if (!EQ_LEN(callid)) continue; /* CSeq only the number without method ! */ if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len) continue; if (! EQ_LEN(from)) continue; /* To only the uri -- to many UACs screw up tags */ if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len) continue; if (!EQ_STR(callid)) continue; if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s, get_cseq(p_msg)->number.len)!=0) continue; if (!EQ_STR(from)) continue; if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s, get_to(t_msg)->uri.len)!=0) continue; if (p_cell->uas.status<300) { /* it's a 2xx local UAS transaction */ if (dlg_matching(p_cell, p_msg)) goto e2e_ack; continue; } /* it is not an e2e ACK/200 -- perhaps it is * local negative case; in which case we will want * more elements to match: r-uri and via; allow * mismatching r-uri as an config option for broken * UACs */ if (ruri_matching && !EQ_REQ_URI_LEN ) continue; if (via1_matching && !EQ_VIA_LEN(via1)) continue; if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; /* wow -- we survived all the check! we matched! */ LM_DBG("non-2xx ACK matched\n"); goto found; } /* ACK */ } /* synonym loop */ notfound: /* no transaction found */ set_t(0); e2eack_T = NULL; if (!leave_new_locked || isACK) { UNLOCK_HASH(p_msg->hash_index); } LM_DBG("no transaction found\n"); return -1; e2e_ack: REF_UNSAFE( p_cell ); UNLOCK_HASH(p_msg->hash_index); e2eack_T = p_cell; set_t(0); LM_DBG("e2e proxy ACK found\n"); return -2; found: set_t(p_cell); REF_UNSAFE( T ); set_kr(REQ_EXIST); UNLOCK_HASH( p_msg->hash_index ); LM_DBG("transaction found (T=%p)\n",T); return 1; } /* function lookups transaction being canceled by CANCEL in p_msg; * it returns: * 0 - transaction wasn't found * T - transaction found */ struct cell* t_lookupOriginalT( struct sip_msg* p_msg ) { struct cell *p_cell; unsigned int hash_index; struct sip_msg *t_msg; struct via_param *branch; int ret; /* already looked for it? */ if (cancelled_T!=T_UNDEFINED) return cancelled_T; /* start searching in the table */ hash_index = p_msg->hash_index; LM_DBG("searching on hash entry %d\n",hash_index ); /* first of all, look if there is RFC3261 magic cookie in branch; if * so, we can do very quick matching and skip the old-RFC bizzar * comparison of many header fields */ if (!p_msg->via1) { LM_ERR("no via\n"); cancelled_T = NULL; return 0; } branch=p_msg->via1->branch; if (branch && branch->value.s && branch->value.len>MCOOKIE_LEN && memcmp(branch->value.s,MCOOKIE,MCOOKIE_LEN)==0) { /* huhuhu! the cookie is there -- let's proceed fast */ LOCK_HASH(hash_index); ret=matching_3261(p_msg, &p_cell, /* we are seeking the original transaction -- * skip CANCEL transactions during search */ METHOD_CANCEL); if (ret==1) goto found; else goto notfound; } /* no cookies --proceed to old-fashioned pre-3261 t-matching */ LOCK_HASH(hash_index); /* all the transactions from the entry are compared */ for (p_cell=get_tm_table()->entrys[hash_index].first_cell; p_cell; p_cell = p_cell->next_cell ) { t_msg = p_cell->uas.request; if (!t_msg) continue; /* skip UAC transactions */ /* we don't cancel CANCELs ;-) */ if (t_msg->REQ_METHOD==METHOD_CANCEL) continue; /* check lengths now */ if (!EQ_LEN(callid)) continue; if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len) continue; if (!EQ_LEN(from)) continue; #ifdef CANCEL_TAG if (!EQ_LEN(to)) continue; #else /* relaxed matching -- we don't care about to-tags anymore, * many broken UACs screw them up and ignoring them does not * actually hurt */ if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len) continue; #endif if (ruri_matching && !EQ_REQ_URI_LEN) continue; if (via1_matching && !EQ_VIA_LEN(via1)) continue; /* check the content now */ if (!EQ_STR(callid)) continue; if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s,get_cseq(p_msg)->number.len)!=0) continue; if (!EQ_STR(from)) continue; #ifdef CANCEL_TAG if (!EQ_STR(to)) continue; #else if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s, get_to(t_msg)->uri.len)!=0) continue; #endif if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; /* found */ goto found; } notfound: /* no transaction found */ LM_DBG("no CANCEL matching found! \n" ); UNLOCK_HASH(hash_index); cancelled_T = NULL; LM_DBG("t_lookupOriginalT completed\n"); return 0; found: LM_DBG("canceled transaction found (%p)! \n",p_cell ); cancelled_T = p_cell; REF_UNSAFE( p_cell ); UNLOCK_HASH(hash_index); /* run callback */ run_trans_callbacks( TMCB_TRANS_CANCELLED, cancelled_T, p_msg, 0,0); LM_DBG("t_lookupOriginalT completed\n"); return p_cell; } /* Returns 0 - nothing found * 1 - T found */ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ) { struct cell* p_cell; int hash_index = 0; int entry_label = 0; int branch_id = 0; char *hashi, *branchi, *p, *n; int hashl, branchl; int scan_space; struct cseq_body *cseq; char *loopi; int loopl; char *syni; int synl; /* make compiler warnings happy */ loopi=0; loopl=0; syni=0; synl=0; /* split the branch into pieces: loop_detection_check(ignored), hash_table_id, synonym_id, branch_id */ if (!(p_msg->via1 && p_msg->via1->branch && p_msg->via1->branch->value.s)) goto nomatch2; /* we do RFC 3261 tid matching and want to see first if there is * magic cookie in branch */ if (p_msg->via1->branch->value.len<=MCOOKIE_LEN) goto nomatch2; if (memcmp(p_msg->via1->branch->value.s, MCOOKIE, MCOOKIE_LEN)!=0) goto nomatch2; p=p_msg->via1->branch->value.s+MCOOKIE_LEN; scan_space=p_msg->via1->branch->value.len-MCOOKIE_LEN; /* hash_id */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR); hashl=n-p; scan_space-=hashl; if (!hashl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; hashi=p; p=n+1;scan_space--; if (!syn_branch) { /* md5 value */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR ); loopl = n-p; scan_space-= loopl; if (n==p || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; loopi=p; p=n+1; scan_space--; } else { /* synonym id */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR); synl=n-p; scan_space-=synl; if (!synl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; syni=p; p=n+1;scan_space--; } /* branch id - should exceed the scan_space */ n=eat_token_end( p, p+scan_space ); branchl=n-p; if (!branchl ) goto nomatch2; branchi=p; /* sanity check */ if ((hash_index=reverse_hex2int(hashi, hashl))<0 ||hash_index>=TM_TABLE_ENTRIES || (branch_id=reverse_hex2int(branchi, branchl))<0 ||branch_id>=MAX_BRANCHES || (syn_branch ? (entry_label=reverse_hex2int(syni, synl))<0 : loopl!=MD5_LEN ) ) { LM_DBG("poor reply labels %d label %d branch %d\n", hash_index, entry_label, branch_id ); goto nomatch2; } LM_DBG("hash %d label %d branch %d\n",hash_index, entry_label, branch_id); cseq = get_cseq(p_msg); /* search the hash table list at entry 'hash_index'; lock the entry first */ LOCK_HASH(hash_index); for (p_cell = get_tm_table()->entrys[hash_index].first_cell; p_cell; p_cell=p_cell->next_cell) { /* first look if branch matches */ if (syn_branch) { if (p_cell->label != entry_label) continue; } else { if ( memcmp(p_cell->md5, loopi,MD5_LEN)!=0) continue; } /* sanity check ... too high branch ? */ if ( branch_id>=p_cell->nr_of_outgoings ) continue; /* does method match ? (remember -- CANCELs have the same branch as canceled transactions) */ if (!( /* it's a local cancel */ (cseq->method_id==METHOD_CANCEL && is_invite(p_cell) && p_cell->uac[branch_id].local_cancel.buffer.len ) /* method match */ || ((cseq->method_id!=METHOD_OTHER && p_cell->uas.request)? (cseq->method_id==REQ_LINE(p_cell->uas.request).method_value) :(EQ_STRS(cseq->method,p_cell->method))) )) continue; /* we passed all disqualifying factors .... the transaction has been matched ! */ set_t(p_cell); *p_branch = branch_id; REF_UNSAFE( T ); UNLOCK_HASH(hash_index); LM_DBG("reply matched (T=%p)!\n",T); /* if this is a 200 for INVITE, we will wish to store to-tags to be * able to distinguish retransmissions later and not to call * TMCB_RESPONSE_OUT uselessly; we do it only if callbacks are * enabled -- except callback customers, nobody cares about * retransmissions of multiple 200/INV or ACK/200s */ if (is_invite(p_cell) && p_msg->REPLY_STATUS>=200 && p_msg->REPLY_STATUS<300 && ( (!is_local(p_cell) && has_tran_tmcbs(p_cell, TMCB_RESPONSE_OUT|TMCB_RESPONSE_PRE_OUT) ) || (is_local(p_cell)&&has_tran_tmcbs(p_cell,TMCB_LOCAL_COMPLETED)) )) { if (parse_headers(p_msg, HDR_TO_F, 0)==-1) { LM_ERR("to parsing failed\n"); } } if (!is_local(p_cell) && !(cseq->method_id==METHOD_CANCEL && is_invite(p_cell)) ) { run_trans_callbacks( TMCB_RESPONSE_IN, T, T->uas.request, p_msg, p_msg->REPLY_STATUS); } return 1; } /* for cycle */ /* nothing found */ UNLOCK_HASH(hash_index); LM_DBG("no matching transaction exists\n"); nomatch2: LM_DBG("failure to match a transaction\n"); *p_branch = -1; set_t(0); return -1; } /* Determine current transaction * * Found Not Found Error (e.g. parsing) * Return Value 1 0 -1 * T ptr 0 T_UNDEFINED */ int t_check( struct sip_msg* p_msg , int *param_branch ) { int local_branch; /* is T still up-to-date ? */ LM_DBG("start=%p\n", T); if ( T==T_UNDEFINED ) { /* transaction lookup */ if ( p_msg->first_line.type==SIP_REQUEST ) { /* force parsing all the needed headers*/ if (parse_headers(p_msg, HDR_EOH_F, 0 )==-1) { LM_ERR("parsing error\n"); return -1; } /* in case, we act as UAS for INVITE and reply with 200, * we will need to run dialog-matching for subsequent * ACK, for which we need From-tag; We also need from-tag * in case people want to have proxied e2e ACKs accounted */ if (p_msg->REQ_METHOD==METHOD_INVITE && parse_from_header(p_msg)<0) { LM_ERR("from parsing failed\n"); return -1; } t_lookup_request( p_msg , 0 /* unlock before returning */ ); } else { /* we need Via for branch and Cseq method to distinguish replies with the same branch/cseqNr (CANCEL) */ if ( parse_headers(p_msg, HDR_VIA1_F|HDR_CSEQ_F, 0 )==-1 || !p_msg->via1 || !p_msg->cseq ) { LM_ERR("reply cannot be parsed\n"); return -1; } /* if that is an INVITE, we will also need to-tag for later ACK matching */ if ( get_cseq(p_msg)->method_id==METHOD_INVITE ) { if (parse_headers(p_msg, HDR_TO_F, 0)==-1 || !p_msg->to) { LM_ERR("INVITE reply cannot be parsed\n"); return -1; } } t_reply_matching( p_msg , param_branch!=0?param_branch:&local_branch ); } #ifdef EXTRA_DEBUG if ( T && T!=T_UNDEFINED && T->damocles) { LM_ERR("transaction %p scheduled for deletion " "and called from t_check\n", T); abort(); } #endif LM_DBG("end=%p\n",T); } else { if (T) LM_DBG("transaction already found!\n"); else LM_DBG("transaction previously sought and not found\n"); } return T ? (T==T_UNDEFINED ? -1 : 1 ) : 0; } int init_rb( struct retr_buf *rb, struct sip_msg *msg) { int proto; update_sock_struct_from_ip( &rb->dst.to, msg ); proto=msg->rcv.proto; rb->dst.proto=proto; rb->dst.proto_reserved1=msg->rcv.proto_reserved1; /* use for sending replies the incoming interface of the request -bogdan */ rb->dst.send_sock=msg->rcv.bind_address; return 1; } static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg) { struct sip_msg *shm_msg; shm_msg=new_cell->uas.request; new_cell->from.s=shm_msg->from->name.s; new_cell->from.len=HF_LEN(shm_msg->from); new_cell->to.s=shm_msg->to->name.s; new_cell->to.len=HF_LEN(shm_msg->to); new_cell->callid.s=shm_msg->callid->name.s; new_cell->callid.len=HF_LEN(shm_msg->callid); new_cell->cseq_n.s=shm_msg->cseq->name.s; new_cell->cseq_n.len=get_cseq(shm_msg)->number.s +get_cseq(shm_msg)->number.len -shm_msg->cseq->name.s; new_cell->method=new_cell->uas.request->first_line.u.request.method; if (p_msg->REQ_METHOD==METHOD_INVITE) new_cell->flags |= T_IS_INVITE_FLAG; new_cell->on_negative=get_on_negative(); new_cell->on_reply=get_on_reply(); new_cell->on_branch=get_on_branch(); } static inline int new_t(struct sip_msg *p_msg, int full_uas) { struct cell *new_cell; /* for ACK-dlw-wise matching, we want From-tags */ if (p_msg->REQ_METHOD==METHOD_INVITE && parse_from_header(p_msg)<0) { LM_ERR("no valid From in INVITE\n"); return E_BAD_REQ; } /* make sure uri will be parsed before cloning */ if (parse_sip_msg_uri(p_msg)<0) { LM_ERR("uri invalid\n"); return E_BAD_REQ; } /* add new transaction */ new_cell = build_cell( p_msg , full_uas) ; if ( !new_cell ){ LM_ERR("out of mem\n"); return E_OUT_OF_MEM; } insert_into_hash_table_unsafe( new_cell, p_msg->hash_index ); INIT_REF_UNSAFE(T); /* init pointers to headers needed to construct local requests such as CANCEL/ACK */ init_new_t(new_cell, p_msg); return 1; } /* atomic "new_tran" construct; it returns: <0 on error +1 if a request did not match a transaction - it that was an ack, the calling function shall forward statelessly - otherwise it means, a new transaction was introduced and the calling function shall reply/relay/whatever_appropriate 0 on retransmission */ int t_newtran( struct sip_msg* p_msg, int full_uas ) { int lret, my_err; context_p ctx_backup; /* is T still up-to-date ? */ LM_DBG("transaction on entrance=%p\n",T); if ( T && T!=T_UNDEFINED ) { LM_DBG("transaction already in process %p\n", T ); return E_SCRIPT; } T = T_UNDEFINED; /* first of all, parse everything -- we will store in shared memory and need to have all headers ready for generating potential replies later; parsing later on demand is not an option since the request will be in shmem and applying parse_headers to it would intermix shmem with pkg_mem */ if (parse_headers(p_msg, HDR_EOH_F, 0 )<0) { LM_ERR("parse_headers failed\n"); return E_BAD_REQ; } if ((p_msg->parsed_flag & HDR_EOH_F)!=HDR_EOH_F) { LM_ERR("EoH not parsed\n"); return E_OUT_OF_MEM; } /* t_lookup_requests attempts to find the transaction; it also calls check_transaction_quadruple -> it is safe to assume we have from/callid/cseq/to */ lret = t_lookup_request( p_msg, 1 /* leave locked if not found */ ); /* on error, pass the error in the stack ... nothing is locked yet if 0 is returned */ if (lret==0) return E_BAD_TUPEL; /* transaction found, it's a retransmission */ if (lret>0) { if (p_msg->REQ_METHOD==METHOD_ACK) { t_release_transaction(T); } else { t_retransmit_reply(T); } /* things are done -- return from script */ return 0; } /* from now on, be careful -- hash table is locked */ if (lret==-2) { /* was it an e2e ACK ? if so, trigger a callback */ if (e2eack_T->relaied_reply_branch==-2) { /* if a ACK for a local replied transaction, release the INVITE transaction and break the script -> simply absorb the ACK request */ t_release_transaction(e2eack_T); return 0; } LM_DBG("building branch for end2end ACK - flags=%X\n",e2eack_T->flags); /* to ensure unigueness acros time and space, compute the ACK * branch in the same maner as for INVITE, but put a t->branch * value that cannot exist for that INVITE - as it is compute as * an INVITE, it will not overlapp with other INVITEs or requests. * But the faked value for t->branch guarantee no overalap with * corresponding INVITE --bogdan */ if (!t_calc_branch(e2eack_T, e2eack_T->nr_of_outgoings+1, p_msg->add_to_branch_s, &p_msg->add_to_branch_len )) { LM_ERR("ACK branch computation failed\n"); } return 1; } /* transaction not found, it's a new request (lret<0, lret!=-2); establish a new transaction ... */ if (p_msg->REQ_METHOD==METHOD_ACK) /* ... unless it is in ACK */ return 1; my_err=new_t(p_msg, full_uas); if (my_err<0) { LM_ERR("new_t failed\n"); goto new_err; } UNLOCK_HASH(p_msg->hash_index); /* now, when the transaction state exists, check if there is a meaningful Via and calculate it; better do it now than later: state is established so that subsequent retransmissions will be absorbed and will not possibly block during Via DNS resolution; doing it later would only burn more CPU as if there is an error, we cannot relay later whatever comes out of the the transaction */ if (!init_rb( &T->uas.response, p_msg)) { LM_ERR("unresolvable via1\n"); put_on_wait( T ); t_unref(p_msg); return E_BAD_VIA; } if (auto_100trying && p_msg->REQ_METHOD==METHOD_INVITE) { ctx_backup = current_processing_ctx; current_processing_ctx = NULL; t_reply( T, p_msg , 100 , &relay_reason_100); current_processing_ctx = ctx_backup; } return 1; new_err: UNLOCK_HASH(p_msg->hash_index); return my_err; } int t_unref( struct sip_msg* p_msg ) { enum kill_reason kr; if (T==T_UNDEFINED) return -1; if (T!=T_NULL_CELL) { if (p_msg->first_line.type==SIP_REQUEST){ kr=get_kr(); if (kr==0 ||(p_msg->REQ_METHOD==METHOD_ACK && !(kr & REQ_RLSD))) t_release_transaction(T); } UNREF( T ); } set_t(T_UNDEFINED); return 1; } void t_ref_cell(struct cell *c) { REF(c); } void t_unref_cell(struct cell *c) { UNREF(c); } int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned int* label) { struct cell* t; if(t_check(p_msg,0) != 1){ LM_ERR("no transaction found\n"); return -1; } t = get_t(); if(!t){ LM_ERR("transaction found is NULL\n"); return -1; } *hash_index = t->hash_index; *label = t->label; return 1; } int t_lookup_ident(struct cell ** trans, unsigned int hash_index, unsigned int label) { struct cell* p_cell; if(hash_index >= TM_TABLE_ENTRIES){ LM_ERR("invalid hash_index=%u\n",hash_index); return -1; } LOCK_HASH(hash_index); /* all the transactions from the entry are compared */ for ( p_cell = get_tm_table()->entrys[hash_index].first_cell; p_cell; p_cell = p_cell->next_cell ) { if(p_cell->label == label){ REF_UNSAFE(p_cell); UNLOCK_HASH(hash_index); set_t(p_cell); *trans=p_cell; LM_DBG("transaction found\n"); return 1; } } UNLOCK_HASH(hash_index); set_t(0); *trans=p_cell; LM_DBG("transaction not found\n"); return -1; } int t_is_local(struct sip_msg* p_msg) { struct cell* t; if(t_check(p_msg,0) != 1){ LM_ERR("no transaction found\n"); return -1; } t = get_t(); if(!t){ LM_ERR("transaction found is NULL\n"); return -1; } return is_local(t); } /* lookup a transaction by callid and cseq, parameters are pure * header field content only, e.g. "123@10.0.0.1" and "11" */ int t_lookup_callid(struct cell ** trans, str callid, str cseq) { struct cell* p_cell; unsigned int hash_index; /* I use MAX_HEADER, not sure if this is a good choice... */ char callid_header[MAX_HEADER]; char cseq_header[MAX_HEADER]; /* save return value of print_* functions here */ char* endpos; UNUSED(endpos); /* need method, which is always INVITE in our case */ /* CANCEL is only useful after INVITE */ str invite_method; char* invite_string = INVITE; invite_method.s = invite_string; invite_method.len = INVITE_LEN; /* lookup the hash index where the transaction is stored */ hash_index=tm_hash(callid, cseq); if(hash_index >= TM_TABLE_ENTRIES){ LM_ERR("invalid hash_index=%u\n",hash_index); return -1; } /* create header fields the same way tm does itself, then compare headers */ endpos = print_callid_mini(callid_header, callid); LM_DBG("created comparable call_id header field: >%.*s<\n", (int)(endpos - callid_header), callid_header); endpos = print_cseq_mini(cseq_header, &cseq, &invite_method); LM_DBG("created comparable cseq header field: >%.*s<\n", (int)(endpos - cseq_header), cseq_header); LOCK_HASH(hash_index); /* all the transactions from the entry are compared */ p_cell = get_tm_table()->entrys[hash_index].first_cell; for ( ; p_cell; p_cell = p_cell->next_cell ) { /* compare complete header fields, casecmp to make sure invite=INVITE */ LM_DBG(" <%.*s> <%.*s>\n", p_cell->callid.len, p_cell->callid.s, p_cell->cseq_n.len,p_cell->cseq_n.s); if ( (strncmp(callid_header, p_cell->callid.s, p_cell->callid.len) == 0) && (strncasecmp(cseq_header, p_cell->cseq_n.s, p_cell->cseq_n.len) == 0) ) { LM_DBG("we have a match: callid=>>%.*s<< cseq=>>%.*s<<\n", p_cell->callid.len,p_cell->callid.s, p_cell->cseq_n.len, p_cell->cseq_n.s); REF_UNSAFE(p_cell); UNLOCK_HASH(hash_index); set_t(p_cell); *trans=p_cell; LM_DBG("transaction found.\n"); return 1; } LM_DBG("NO match: callid=%.*s cseq=%.*s\n", p_cell->callid.len, p_cell->callid.s, p_cell->cseq_n.len, p_cell->cseq_n.s); } UNLOCK_HASH(hash_index); LM_DBG("transaction not found.\n"); return -1; } opensips-2.2.2/modules/tm/t_lookup.h000066400000000000000000000063101300170765700174600ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-24 s/T_NULL/T_NULL_CELL/ to avoid redefinition conflict w/ * nameser_compat.h (andrei) * 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri) */ #ifndef _T_LOOKUP_H #define _T_LOOKUP_H #include "config.h" #include "t_funcs.h" #define T_UNDEFINED ( (struct cell*) -1 ) #define T_NULL_CELL ( (struct cell*) 0 ) extern unsigned int global_msg_id; extern int ruri_matching; extern int via1_matching; extern int auto_100trying; void init_t(); int init_rb( struct retr_buf *rb, struct sip_msg *msg ); struct cell* t_lookupOriginalT( struct sip_msg* p_msg ); int t_reply_matching( struct sip_msg* , int* ); int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked ); int t_newtran( struct sip_msg* p_msg, int full_uas ); int _add_branch_label( struct cell *trans, char *str, int *len, int branch ); int add_branch_label( struct cell *trans, struct sip_msg *p_msg, int branch ); /* references T-context */ void t_ref_cell(struct cell *c); /* releases T-context */ int t_unref( struct sip_msg *p_msg); void t_unref_cell( struct cell *); typedef void (*tunrefcell_f)(struct cell *); /* function returns: * -1 - transaction wasn't found * 1 - transaction found */ int t_check( struct sip_msg* , int *branch ); typedef struct cell * (*tlookuporiginalt_f)(struct sip_msg*); typedef struct cell * (*tgett_f)(void); struct cell *get_t(); /* use carefully or better not at all -- current transaction is * primarily set by lookup functions */ void set_t(struct cell *t); struct cell *get_cancelled_t(); void set_cancelled_t(struct cell* t); void reset_cancelled_t(); struct cell *get_e2eack_t(); void set_e2eack_t(struct cell* t); void reset_e2eack_t(); #define T_GET_TI "t_get_trans_ident" #define T_LOOKUP_IDENT "t_lookup_ident" #define T_IS_LOCAL "t_is_local" typedef int (*tislocal_f)(struct sip_msg*); typedef int (*tnewtran_f)(struct sip_msg*); typedef int (*tget_ti_f)(struct sip_msg*, unsigned int*, unsigned int*); typedef int (*tlookup_ident_f)(struct cell**, unsigned int, unsigned int); int t_is_local(struct sip_msg*); int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned int* label); int t_lookup_ident(struct cell** trans, unsigned int hash_index, unsigned int label); /* lookup a transaction by callid and cseq */ int t_lookup_callid(struct cell** trans, str callid, str cseq); #endif opensips-2.2.2/modules/tm/t_msgbuilder.c000066400000000000000000000533141300170765700203050ustar00rootroot00000000000000/* * message printing * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ---------- * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2003-02-13 build_uac_request uses proto (andrei) * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-04-14 build_local no longer checks reply status as it * is now called before reply status is updated to * avoid late ACK sending (jiri) * 2003-10-02 added via_builder set host/port support (andrei) * 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri) * 2004-02-13 t->is_invite and t->local replaced with flags (bogdan) * 2008-04-04 added support for local and remote dispaly name in TM dialogs * (by Andrei Pisau ) */ #include "../../hash_func.h" #include "../../dprint.h" #include "../../parser/parser_f.h" #include "../../ut.h" #include "../../parser/msg_parser.h" #include "../../parser/contact/parse_contact.h" #include "t_funcs.h" #include "t_msgbuilder.h" #include "uac.h" #define ROUTE_PREFIX "Route: " #define ROUTE_PREFIX_LEN (sizeof(ROUTE_PREFIX) - 1) #define ROUTE_SEPARATOR ", " #define ROUTE_SEPARATOR_LEN (sizeof(ROUTE_SEPARATOR) - 1) #define LOCAL_MAXFWD_HEADER "Max-Forwards: " LOCAL_MAXFWD_VALUE CRLF #define LOCAL_MAXFWD_HEADER_LEN (sizeof(LOCAL_MAXFWD_HEADER) - 1) /* convenience macros */ #define append_string(_d,_s,_len) \ do{\ memcpy((_d),(_s),(_len));\ (_d) += (_len);\ }while(0); static inline struct hdr_field* extract_parsed_hdrs( char *buf, int len) { char *p; static struct sip_msg msg; struct hdr_field *hdr; /* skip the first line - not interesting */ p = eat_line( buf, len); if (p>=buf+len) return 0; memset( &msg, 0, sizeof(struct sip_msg) ); msg.buf = buf; msg.len = len; msg.unparsed = p; /* as we need all Route headers, we need to parse all headers */ if (parse_headers( &msg, HDR_EOH_F, 0)==-1) goto error; hdr = msg.headers; msg.headers = 0; free_sip_msg( &msg ); return hdr; error: free_sip_msg( &msg ); return 0; } /* Build a local request based on a previous request; the only customers of this function are local ACK and local CANCEL */ char *build_local(struct cell *Trans,unsigned int branch, str *method, str *extra, struct sip_msg* rpl, unsigned int *len) { char *cancel_buf, *p, *via; unsigned int via_len; struct hdr_field *buf_hdrs; struct hdr_field *hdr; struct sip_msg *req; char branch_buf[MAX_BRANCH_PARAM_LEN]; str branch_str; struct hostport hp; str from; str to; str cseq_n; req = Trans->uas.request; cseq_n = Trans->cseq_n; buf_hdrs = 0; if (rpl && rpl!=FAKED_REPLY) { /* take from and to hdrs from reply */ to.s = rpl->to->name.s; to.len = rpl->to->len; from.s = rpl->from->name.s; from.len = rpl->from->len; if (req && req->msg_flags&FL_USE_UAC_CSEQ) { if ( extract_ftc_hdrs( Trans->uac[branch].request.buffer.s, Trans->uac[branch].request.buffer.len,0 ,0 , &cseq_n ,0)!=0 ) { LM_ERR("build_local: failed to extract UAC hdrs\n"); goto error; } } } else { to = Trans->to; from = Trans->from; if (req && req->msg_flags&(FL_USE_UAC_FROM|FL_USE_UAC_TO|FL_USE_UAC_CSEQ)) { if ( extract_ftc_hdrs( Trans->uac[branch].request.buffer.s, Trans->uac[branch].request.buffer.len, (req->msg_flags&FL_USE_UAC_FROM)?&from:0 , (req->msg_flags&FL_USE_UAC_TO)?&to:0 , (req->msg_flags&FL_USE_UAC_CSEQ)?&cseq_n:0 ,0)!=0 ) { LM_ERR("build_local: failed to extract UAC hdrs\n"); goto error; } } } LM_DBG("using FROM=<%.*s>, TO=<%.*s>, CSEQ_N=<%.*s>\n", from.len,from.s , to.len,to.s , cseq_n.len,cseq_n.s); /* method, separators, version */ *len=SIP_VERSION_LEN + method->len + 2 /* spaces */ + CRLF_LEN; *len+=Trans->uac[branch].uri.len; /*via*/ branch_str.s=branch_buf; if (!t_calc_branch(Trans, branch, branch_str.s, &branch_str.len )) goto error; set_hostport(&hp, (is_local(Trans))?0:req); if (Trans->uac[branch].adv_address.len) hp.host = &Trans->uac[branch].adv_address; if (Trans->uac[branch].adv_port.len) hp.port = &Trans->uac[branch].adv_port; via=via_builder(&via_len, Trans->uac[branch].request.dst.send_sock, &branch_str, 0, Trans->uac[branch].request.dst.proto, &hp ); if (!via){ LM_ERR("no via header got from builder\n"); goto error; } *len+= via_len; /*headers*/ *len+=from.len+Trans->callid.len+to.len+cseq_n.len+1+method->len+CRLF_LEN; /* copy'n'paste Route headers that were sent out */ if (!is_local(Trans) && ( (req && req->route) || /* at least one route was received*/ (Trans->uac[branch].path_vec.len!=0)) ) /* path was forced */ { buf_hdrs = extract_parsed_hdrs(Trans->uac[branch].request.buffer.s, Trans->uac[branch].request.buffer.len ); if (buf_hdrs==NULL) { LM_ERR("failed to reparse the request buffer\n"); goto error01; } for ( hdr=buf_hdrs ; hdr ; hdr=hdr->next ) if (hdr->type==HDR_ROUTE_T) *len+=hdr->len; } /* User Agent */ if (server_signature) { *len += user_agent_header.len + CRLF_LEN; } /* Content Length, MaxFwd, EoM */ *len+=LOCAL_MAXFWD_HEADER_LEN + CONTENT_LENGTH_LEN+1 + (extra?extra->len:0) + (Trans->extra_hdrs.s?Trans->extra_hdrs.len:0) + CRLF_LEN + CRLF_LEN; cancel_buf=shm_malloc( *len+1 ); if (!cancel_buf) { LM_ERR("no more share memory\n"); goto error02; } p = cancel_buf; append_string( p, method->s, method->len ); *(p++) = ' '; append_string( p, Trans->uac[branch].uri.s, Trans->uac[branch].uri.len); append_string( p, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN ); /* insert our via */ append_string(p,via,via_len); /*other headers*/ append_string( p, from.s, from.len ); append_string( p, Trans->callid.s, Trans->callid.len ); append_string( p, to.s, to.len ); append_string( p, cseq_n.s, cseq_n.len ); *(p++) = ' '; append_string( p, method->s, method->len ); append_string( p, CRLF LOCAL_MAXFWD_HEADER, CRLF_LEN+LOCAL_MAXFWD_HEADER_LEN ); /* add Route hdrs (if any) */ for ( hdr=buf_hdrs ; hdr ; hdr=hdr->next ) if(hdr->type==HDR_ROUTE_T) { append_string(p, hdr->name.s, hdr->len ); } if (extra) append_string(p, extra->s, extra->len ); if (Trans->extra_hdrs.s) append_string(p, Trans->extra_hdrs.s, Trans->extra_hdrs.len ); /* User Agent header, Content Length, EoM */ if (server_signature) { append_string(p, user_agent_header.s, user_agent_header.len); append_string(p, CRLF CONTENT_LENGTH "0" CRLF CRLF , CRLF_LEN+CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN); } else { append_string(p, CONTENT_LENGTH "0" CRLF CRLF , CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN); } *p=0; pkg_free(via); free_hdr_field_lst(buf_hdrs); return cancel_buf; error02: free_hdr_field_lst(buf_hdrs); error01: pkg_free(via); error: return NULL; } struct rte { rr_t* ptr; struct rte* next; }; static inline void free_rte_list(struct rte* list) { struct rte* ptr; while(list) { ptr = list; list = list->next; pkg_free(ptr); } } static inline int process_routeset(struct sip_msg* msg, str* contact, struct rte** list, str* ruri, str* next_hop) { struct hdr_field* ptr; rr_t* p; struct rte* t, *head; struct sip_uri puri; ptr = msg->record_route; head = 0; while(ptr) { if (ptr->type == HDR_RECORDROUTE_T) { if (parse_rr(ptr) < 0) { LM_ERR("failed to parse Record-Route header\n"); return -1; } p = (rr_t*)ptr->parsed; while(p) { t = (struct rte*)pkg_malloc(sizeof(struct rte)); if (!t) { LM_ERR("no more pkg memory\n"); free_rte_list(head); return -1; } t->ptr = p; t->next = head; head = t; p = p->next; } } ptr = ptr->next; } if (head) { if (parse_uri(head->ptr->nameaddr.uri.s, head->ptr->nameaddr.uri.len, &puri) < 0) { LM_ERR("failed to parse URI\n"); free_rte_list(head); return -1; } if (puri.lr.s) { /* Next hop is loose router */ *ruri = *contact; *next_hop = head->ptr->nameaddr.uri; } else { /* Next hop is strict router */ *ruri = head->ptr->nameaddr.uri; *next_hop = *ruri; t = head; head = head->next; pkg_free(t); } } else { /* No routes */ *ruri = *contact; *next_hop = *contact; } *list = head; return 0; } static inline int calc_routeset_len(struct rte* list, str* contact) { struct rte* ptr; int ret; if (list || contact) { ret = ROUTE_PREFIX_LEN + CRLF_LEN; } else { return 0; } ptr = list; while(ptr) { if (ptr != list) { ret += ROUTE_SEPARATOR_LEN; } ret += ptr->ptr->len; ptr = ptr->next; } if (contact) { if (list) ret += ROUTE_SEPARATOR_LEN; ret += 2 + contact->len; } return ret; } /* * Print the route set */ static inline char* print_rs(char* p, struct rte* list, str* contact) { struct rte* ptr; if (list || contact) { append_string(p, ROUTE_PREFIX, ROUTE_PREFIX_LEN); } else { return p; } ptr = list; while(ptr) { if (ptr != list) { append_string(p, ROUTE_SEPARATOR, ROUTE_SEPARATOR_LEN); } append_string(p, ptr->ptr->nameaddr.name.s, ptr->ptr->len); ptr = ptr->next; } if (contact) { if (list) append_string(p, ROUTE_SEPARATOR, ROUTE_SEPARATOR_LEN); *p++ = '<'; append_string(p, contact->s, contact->len); *p++ = '>'; } append_string(p, CRLF, CRLF_LEN); return p; } /* * Parse Contact header field body and extract URI * Does not parse headers ! */ static inline int get_contact_uri(struct sip_msg* msg, str* uri) { contact_t* c; uri->len = 0; if (!msg->contact) return 1; if (parse_contact(msg->contact) < 0) { LM_ERR("failed to parse Contact body\n"); return -1; } c = ((contact_body_t*)msg->contact->parsed)->contacts; if (!c) { LM_ERR("body or * contact\n"); return -2; } *uri = c->uri; return 0; } /* * The function creates an ACK for a local INVITE. If 200 OK, route set * will be created and parsed */ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch, str* to, unsigned int *len) { char *req_buf, *p, *via; unsigned int via_len; char branch_buf[MAX_BRANCH_PARAM_LEN]; int branch_len; str branch_str; struct hostport hp; struct rte* list; str contact, ruri, *cont; struct socket_info* send_sock; str next_hop; if (rpl->first_line.u.reply.statuscode < 300 ) { /* build e2e ack for 2xx reply -> we need the route set */ if (get_contact_uri(rpl, &contact) < 0) { return 0; } if (process_routeset(rpl, &contact, &list, &ruri, &next_hop) < 0) { return 0; } if ((contact.s != ruri.s) || (contact.len != ruri.len)) { /* contact != ruri means that the next * hop is a strict router, cont will be non-zero * and print_routeset will append it at the end * of the route set */ cont = &contact; } else { /* Next hop is a loose router, nothing to append */ cont = 0; } } else { /* build hop-by-hop ack for negative reply -> * ruri is the same as in INVITE; no route set */ ruri = Trans->uac[branch].uri; cont = 0; list = 0; } /* method, separators, version: "ACK sip:user@domain.org SIP/2.0" */ *len = SIP_VERSION_LEN + ACK_LEN + 2 /* spaces */ + CRLF_LEN; *len += ruri.len; /* use same socket as for INVITE -bogdan */ send_sock = Trans->uac[branch].request.dst.send_sock; if (!t_calc_branch(Trans, branch, branch_buf, &branch_len)) goto error; branch_str.s = branch_buf; branch_str.len = branch_len; set_hostport(&hp, 0); /* build via */ via = via_builder(&via_len, send_sock, &branch_str, 0, send_sock->proto, &hp); if (!via) { LM_ERR("no via header got from builder\n"); goto error; } *len+= via_len; /*headers*/ *len += Trans->from.len + Trans->callid.len + to->len + Trans->cseq_n.len + 1 + ACK_LEN + CRLF_LEN + LOCAL_MAXFWD_HEADER_LEN; /* copy'n'paste Route headers */ *len += calc_routeset_len(list, cont); /* User Agent */ if (server_signature) *len += user_agent_header.len + CRLF_LEN; /* Content Length, EoM */ *len += CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN; req_buf = shm_malloc(*len + 1); if (!req_buf) { LM_ERR("no more share memory\n"); goto error01; } p = req_buf; append_string( p, ACK " ", ACK_LEN+1 ); append_string(p, ruri.s, ruri.len ); append_string( p, " " SIP_VERSION CRLF, 1 + SIP_VERSION_LEN + CRLF_LEN); /* insert our via */ append_string(p, via, via_len); /*other headers*/ append_string(p, Trans->from.s, Trans->from.len); append_string(p, Trans->callid.s, Trans->callid.len); append_string(p, to->s, to->len); append_string(p, Trans->cseq_n.s, Trans->cseq_n.len); *(p++) = ' '; append_string( p, ACK CRLF LOCAL_MAXFWD_HEADER, ACK_LEN+CRLF_LEN+LOCAL_MAXFWD_HEADER_LEN ); /* Routeset */ p = print_rs(p, list, cont); /* User Agent header, Content Length, EoM */ if (server_signature) { append_string(p, user_agent_header.s, user_agent_header.len); append_string(p, CRLF CONTENT_LENGTH "0" CRLF CRLF, CRLF_LEN+CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN); } else { append_string(p, CONTENT_LENGTH "0" CRLF CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN); } *p = 0; pkg_free(via); free_rte_list(list); return req_buf; error01: pkg_free(via); error: free_rte_list(list); return 0; } /* * Convert length of body into asciiz */ static inline int print_content_length(str* dest, str* body) { static char content_length[INT2STR_MAX_LEN]; int len; /* Print Content-Length */ if (body && body->len) { dest->s = int2bstr(body->len, content_length, &len); dest->len = len; } else { dest->s = "0"; dest->len = 1; } return 0; } /* * Convert CSeq number into asciiz */ static inline int print_cseq_num(str* _s, dlg_t* _d) { static char cseq[INT2STR_MAX_LEN]; int len; _s->s = int2bstr(_d->loc_seq.value, cseq, &len); _s->len = len; return 0; } /* * Create Via header */ static inline int assemble_via(str* dest, struct cell* t, struct socket_info* sock, int branch) { static char branch_buf[MAX_BRANCH_PARAM_LEN]; char* via; int len; unsigned int via_len; str branch_str; struct hostport hp; if (!t_calc_branch(t, branch, branch_buf, &len)) { LM_ERR("branch calculation failed\n"); return -1; } branch_str.s = branch_buf; branch_str.len = len; #ifdef XL_DEBUG printf("!!!proto: %d\n", sock->proto); #endif set_hostport(&hp, 0); via = via_builder(&via_len, sock, &branch_str, 0, sock->proto, &hp); if (!via) { LM_ERR("via building failed\n"); return -2; } dest->s = via; dest->len = via_len; return 0; } /* * Print Request-URI */ static inline char* print_request_uri(char* w, str* method, dlg_t* dialog, struct cell* t, int branch) { append_string(w, method->s, method->len); *(w++) = ' '; t->uac[branch].uri.s = w; t->uac[branch].uri.len = dialog->hooks.request_uri->len; append_string(w, dialog->hooks.request_uri->s, dialog->hooks.request_uri->len); append_string(w, " " SIP_VERSION CRLF, 1 + SIP_VERSION_LEN + CRLF_LEN); LM_DBG("%.*s\n",dialog->hooks.request_uri->len, dialog->hooks.request_uri->s ); return w; } /* * Print To header field */ static inline char* print_to(char* w, dlg_t* dialog, struct cell* t) { t->to.s = w; t->to.len = TO_LEN + dialog->rem_uri.len + CRLF_LEN; append_string(w, TO, TO_LEN); if(dialog->rem_dname.len) { t->to.len += dialog->rem_dname.len; append_string(w, dialog->rem_dname.s, dialog->rem_dname.len); *(w++) = ' '; } if(dialog->rem_dname.len || dialog->id.rem_tag.len) { t->to.len +=1; *(w++) = '<'; } append_string(w, dialog->rem_uri.s, dialog->rem_uri.len); if(dialog->rem_dname.len || dialog->id.rem_tag.len) { t->to.len +=1; *(w++) = '>'; } if (dialog->id.rem_tag.len) { t->to.len += TOTAG_LEN + dialog->id.rem_tag.len ; append_string(w, TOTAG, TOTAG_LEN); append_string(w, dialog->id.rem_tag.s, dialog->id.rem_tag.len); } append_string(w, CRLF, CRLF_LEN); return w; } /* * Print From header field */ static inline char* print_from(char* w, dlg_t* dialog, struct cell* t) { t->from.s = w; t->from.len = FROM_LEN + dialog->loc_uri.len + CRLF_LEN; append_string(w, FROM, FROM_LEN); if(dialog->loc_dname.len) { t->from.len += dialog->loc_dname.len + 1; append_string(w, dialog->loc_dname.s, dialog->loc_dname.len); *(w++) = ' '; } if(dialog->loc_dname.len || dialog->id.loc_tag.len) { t->from.len +=1; *(w++) = '<'; } append_string(w, dialog->loc_uri.s, dialog->loc_uri.len); if(dialog->loc_dname.len || dialog->id.loc_tag.len) { t->from.len += 1; *(w++) = '>'; } if (dialog->id.loc_tag.len) { t->from.len += FROMTAG_LEN + dialog->id.loc_tag.len; append_string(w, FROMTAG, FROMTAG_LEN); append_string(w, dialog->id.loc_tag.s, dialog->id.loc_tag.len); } append_string(w, CRLF, CRLF_LEN); return w; } /* * Print CSeq header field */ char* print_cseq_mini(char* target, str* cseq, str* method) { append_string(target, CSEQ, CSEQ_LEN); append_string(target, cseq->s, cseq->len); *(target++) = ' '; append_string(target, method->s, method->len); return target; } static inline char* print_cseq(char* w, str* cseq, str* method, struct cell* t) { t->cseq_n.s = w; /* don't include method name and CRLF -- subsequent * local requests ACK/CANCEL will add their own */ t->cseq_n.len = CSEQ_LEN + cseq->len; w = print_cseq_mini(w, cseq, method); return w; } /* * Print Call-ID header field * created an extra function for pure header field creation */ char* print_callid_mini(char* target, str callid) { append_string(target, CALLID, CALLID_LEN); append_string(target, callid.s, callid.len); append_string(target, CRLF, CRLF_LEN); return target; } static inline char* print_callid(char* w, dlg_t* dialog, struct cell* t) { /* begins with CRLF, not included in t->callid, don`t know why...?!? */ append_string(w, CRLF, CRLF_LEN); t->callid.s = w; t->callid.len = CALLID_LEN + dialog->id.call_id.len + CRLF_LEN; w = print_callid_mini(w, dialog->id.call_id); return w; } /* * Create a request */ char* build_uac_req(str* method, str* headers, str* body, dlg_t* dialog, int branch, struct cell *t, int* len) { char* buf, *w; str content_length, cseq, via; if (!method || !dialog) { LM_ERR("inalid parameter value\n"); return 0; } if (print_content_length(&content_length, body) < 0) { LM_ERR("failed to print content-length\n"); return 0; } if (print_cseq_num(&cseq, dialog) < 0) { LM_ERR("failed to print CSeq number\n"); return 0; } *len = method->len + 1 + dialog->hooks.request_uri->len + 1 + SIP_VERSION_LEN + CRLF_LEN; if (assemble_via(&via, t, dialog->send_sock, branch) < 0) { LM_ERR("failed to assemble Via\n"); return 0; } *len += via.len; /* To */ *len += TO_LEN + (dialog->rem_dname.len ? dialog->rem_dname.len+1 : 0) + dialog->rem_uri.len + (dialog->id.rem_tag.len ? TOTAG_LEN + dialog->id.rem_tag.len : 0) + (dialog->rem_dname.len || dialog->id.rem_tag.len ? 2 : 0) + CRLF_LEN; /* From */ *len += FROM_LEN + (dialog->loc_dname.len ? dialog->loc_dname.len+1 : 0) + dialog->loc_uri.len + (dialog->id.loc_tag.len ? FROMTAG_LEN + dialog->id.loc_tag.len : 0) + (dialog->loc_dname.len || dialog->id.loc_tag.len ? 2 : 0) + CRLF_LEN; /* Call-ID */ *len += CALLID_LEN + dialog->id.call_id.len + CRLF_LEN; /* CSeq */ *len += CSEQ_LEN + cseq.len + 1 + method->len + CRLF_LEN; /* Route set */ *len += calculate_routeset_length(dialog); /* Max-Forwards */ *len += LOCAL_MAXFWD_HEADER_LEN; /* Content-Length */ *len += CONTENT_LENGTH_LEN + content_length.len + CRLF_LEN; /* Signature */ *len += (server_signature ? (user_agent_header.len + CRLF_LEN) : 0); /* Additional headers */ *len += (headers ? headers->len : 0); /* Message body */ *len += (body ? body->len : 0); /* End of Header */ *len += CRLF_LEN; buf = shm_malloc(*len + 1); if (!buf) { LM_ERR("no more share memory\n"); goto error; } w = buf; w = print_request_uri(w, method, dialog, t, branch); /* Request-URI */ append_string(w, via.s, via.len); /* Top-most Via */ w = print_to(w, dialog, t); /* To */ w = print_from(w, dialog, t); /* From */ w = print_cseq(w, &cseq, method, t); /* CSeq */ w = print_callid(w, dialog, t); /* Call-ID */ w = print_routeset(w, dialog); /* Route set */ /* Max-Forwards */ append_string(w, LOCAL_MAXFWD_HEADER, LOCAL_MAXFWD_HEADER_LEN); /* Content-Length */ append_string(w, CONTENT_LENGTH, CONTENT_LENGTH_LEN); append_string(w, content_length.s, content_length.len); append_string(w, CRLF, CRLF_LEN); /* Server signature */ if (server_signature) { append_string(w, user_agent_header.s, user_agent_header.len); append_string(w, CRLF, CRLF_LEN); } /* extra headers (containing CRLF) */ if (headers) append_string(w, headers->s, headers->len); /* add CRLF between headers and body */ append_string(w, CRLF, CRLF_LEN); /* add body (if required) */ if (body) append_string(w, body->s, body->len); #ifdef EXTRA_DEBUG if (w-buf != *len ) abort(); #endif /* make buffer NULL terminated */ buf[*len] = 0; pkg_free(via.s); return buf; error: pkg_free(via.s); return 0; } int t_calc_branch(struct cell *t, int b, char *branch, int *branch_len) { return syn_branch ? branch_builder( t->hash_index, t->label, 0, b, branch, branch_len ) : branch_builder( t->hash_index, 0, t->md5, b, branch, branch_len ); } opensips-2.2.2/modules/tm/t_msgbuilder.h000066400000000000000000000202111300170765700203000ustar00rootroot00000000000000/* * Copyright (C) 2010-2014 OpenSIPS Solutions * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri) */ #ifndef _MSGBUILDER_H #define _MSGBUILDER_H #include "../../ip_addr.h" #include "../../receive.h" #include "../../dset.h" #include "../../msg_callbacks.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "dlg.h" #define CSEQ "CSeq: " #define CSEQ_LEN (sizeof(CSEQ)-1) #define TO "To: " #define TO_LEN (sizeof(TO)-1) #define CALLID "Call-ID: " #define CALLID_LEN (sizeof(CALLID)-1) #define FROM "From: " #define FROM_LEN (sizeof(FROM)-1) #define FROMTAG ";tag=" #define FROMTAG_LEN (sizeof(FROMTAG)-1) #define TOTAG ";tag=" #define TOTAG_LEN (sizeof(TOTAG)-1) #define LOCAL_MAXFWD_VALUE "70" char *build_local(struct cell *Trans, unsigned int branch, str *method, str *extra, struct sip_msg *rpl, unsigned int *len); char *build_uac_request( str msg_type, str dst, str from, str fromtag, int cseq, str callid, str headers, str body, int branch, struct cell *t, unsigned int *len); /* * The function creates an ACK to 200 OK. Route set will be created * and parsed. The function is used by tm when it generates * local ACK to 200 OK (on behalf of applications using uac */ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch, str* to, unsigned int *len); /* * Create a request */ char* build_uac_req(str* method, str* headers, str* body, dlg_t* dialog, int branch, struct cell *t, int* len); int t_calc_branch(struct cell *t, int b, char *branch, int *branch_len); /* exported minimum functions for use in t_cancel */ char* print_callid_mini(char* target, str callid); char* print_cseq_mini(char* target, str* cseq, str* method); static inline struct sip_msg* buf_to_sip_msg(char *buf, unsigned int len, dlg_t *dialog) { static struct sip_msg req; memset( &req, 0, sizeof(req) ); req.id = get_next_msg_no(); req.buf = buf; req.len = len; if (parse_msg(buf, len, &req)!=0) { LM_CRIT("BUG - buffer parsing failed!"); return NULL; } /* parse all headers, to be sure they get cloned in shm */ if (parse_headers(&req, HDR_EOH_F, 0 )<0) { LM_ERR("parse_headers failed\n"); free_sip_msg(&req); return NULL; } /* check if we have all necessary headers */ if (check_transaction_quadruple(&req)==0) { LM_ERR("too few headers\n"); free_sip_msg(&req); /* stop processing */ return NULL; } /* populate some special fields in sip_msg */ req.force_send_socket = dialog->send_sock; if (set_dst_uri(&req, dialog->hooks.next_hop)) { LM_ERR("failed to set dst_uri\n"); free_sip_msg(&req); return NULL; } req.rcv.proto = dialog->send_sock->proto; req.rcv.src_ip = req.rcv.dst_ip = dialog->send_sock->address; req.rcv.src_port = req.rcv.dst_port = dialog->send_sock->port_no; req.rcv.bind_address = dialog->send_sock; return &req; } static inline int fake_req(struct sip_msg *faked_req, struct sip_msg *shm_msg, struct ua_server *uas, struct ua_client *uac, int with_dst) { /* on_negative_reply faked msg now copied from shmem msg (as opposed * to zero-ing) -- more "read-only" actions (exec in particular) will * work from reply_route as they will see msg->from, etc.; caution, * rw actions may append some pkg stuff to msg, which will possibly be * never released (shmem is released in a single block) */ memcpy( faked_req, shm_msg, sizeof(struct sip_msg)); /* if we set msg_id to something different from current's message * id, the first t_fork will properly clean new branch URIs */ faked_req->id = get_next_msg_no(); /* msg->parsed_uri_ok must be reset since msg_parsed_uri is * not cloned (and cannot be cloned) */ faked_req->parsed_uri_ok = 0; faked_req->msg_flags |= FL_TM_FAKE_REQ; /* new_uri can change -- make a private copy */ faked_req->new_uri.s=pkg_malloc( uac->uri.len+1 ); if (!faked_req->new_uri.s) { LM_ERR("no uri/pkg mem\n"); return 0; } faked_req->new_uri.len = uac->uri.len; memcpy( faked_req->new_uri.s, uac->uri.s, uac->uri.len); faked_req->new_uri.s[faked_req->new_uri.len]=0; faked_req->parsed_uri_ok = 0; /* duplicate the dst_uri, advertised address and port into private mem * so that they can be changed at script level */ if (with_dst) { if (shm_msg->dst_uri.s) { faked_req->dst_uri.s = pkg_malloc(shm_msg->dst_uri.len); if (!faked_req->dst_uri.s) { LM_ERR("out of pkg mem\n"); goto out; } memcpy(faked_req->dst_uri.s, shm_msg->dst_uri.s, shm_msg->dst_uri.len); } } else { faked_req->dst_uri.s = NULL; faked_req->dst_uri.len = 0; } if (shm_msg->set_global_address.s) { faked_req->set_global_address.s = pkg_malloc(shm_msg->set_global_address.len); if (!faked_req->set_global_address.s) { LM_ERR("out of pkg mem\n"); goto out; } memcpy(faked_req->set_global_address.s, shm_msg->set_global_address.s, shm_msg->set_global_address.len); } if (shm_msg->set_global_port.s) { faked_req->set_global_port.s = pkg_malloc(shm_msg->set_global_port.len); if (!faked_req->set_global_port.s) { LM_ERR("out of pkg mem\n"); goto out1; } memcpy(faked_req->set_global_port.s, shm_msg->set_global_port.s, shm_msg->set_global_port.len); } if (shm_msg->path_vec.s) { faked_req->path_vec.s = pkg_malloc(shm_msg->path_vec.len); if (!faked_req->path_vec.s) { LM_ERR("out of pkg mem\n"); goto out2; } memcpy(faked_req->path_vec.s, shm_msg->path_vec.s, shm_msg->path_vec.len); } /* set as flags the global flags and the branch flags from the * elected branch */ faked_req->flags = uas->request->flags; setb0flags( faked_req, uac->br_flags); return 1; out2: pkg_free(faked_req->set_global_port.s); out1: pkg_free(faked_req->set_global_address.s); out: pkg_free(faked_req->new_uri.s); return 0; } inline static void free_faked_req(struct sip_msg *faked_req, struct cell *t) { if (faked_req->new_uri.s) { pkg_free(faked_req->new_uri.s); faked_req->new_uri.s = NULL; } if (faked_req->dst_uri.s) { pkg_free(faked_req->dst_uri.s); faked_req->dst_uri.s = NULL; } if (faked_req->path_vec.s) { pkg_free(faked_req->path_vec.s); faked_req->path_vec.s = NULL; } if (faked_req->set_global_address.s) { pkg_free(faked_req->set_global_address.s); faked_req->set_global_address.s = NULL; } if (faked_req->set_global_port.s) { pkg_free(faked_req->set_global_port.s); faked_req->set_global_port.s = NULL; } /* SDP in not cloned into SHM, so if we have one, it means the SDP * was parsed in the fake environment, so we have to free it */ if (faked_req->sdp) free_sdp(&(faked_req->sdp)); if (faked_req->multi) { free_multi_body(faked_req->multi); faked_req->multi = NULL; } if (faked_req->msg_cb) { msg_callback_process(faked_req, MSG_DESTROY, NULL); } /* free all types of lump that were added in failure handlers */ del_notflaged_lumps( &(faked_req->add_rm), LUMPFLAG_SHMEM ); del_notflaged_lumps( &(faked_req->body_lumps), LUMPFLAG_SHMEM ); del_nonshm_lump_rpl( &(faked_req->reply_lump) ); if (faked_req->add_rm && faked_req->add_rm != t->uas.request->add_rm) shm_free(faked_req->add_rm); if (faked_req->body_lumps && faked_req->body_lumps != t->uas.request->body_lumps) shm_free(faked_req->body_lumps); if (faked_req->reply_lump && faked_req->reply_lump != t->uas.request->reply_lump) shm_free(faked_req->reply_lump); clean_msg_clone( faked_req, t->uas.request, t->uas.end_request); } #endif opensips-2.2.2/modules/tm/t_reply.c000066400000000000000000001413101300170765700172750ustar00rootroot00000000000000/* * Copyright (C) 2010-2014 OpenSIPS Solutions * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-19 faked lump list created in on_reply handlers * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2003-02-13 updated to use rb->dst (andrei) * 2003-02-18 replaced TOTAG_LEN w/ TOTAG_VALUE_LEN (TOTAG_LEN was defined * twice with different values!) (andrei) * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-03-01 kr set through a function now (jiri) * 2003-03-06 saving of to-tags for ACK/200 matching introduced, * voicemail changes accepted, updated to new callback * names (jiri) * 2003-03-10 fixed new to tag bug/typo (if w/o {}) (andrei) * 2003-03-16 removed _TOTAG (jiri) * 2003-03-31 200 for INVITE/UAS resent even for UDP (jiri) * 2003-03-31 removed msg->repl_add_rm (andrei) * 2003-04-05 s/reply_route/failure_route, onreply_route introduced (jiri) * 2003-04-14 local acks generated before reply processing to avoid * delays in length reply processing (like opening TCP * connection to an unavailable destination) (jiri) * 2003-09-11 updates to new build_res_buf_from_sip_req() interface (bogdan) * 2003-09-11 t_reply_with_body() reshaped to use reply_lumps + * build_res_buf_from_sip_req() instead of * build_res_buf_with_body_from_sip_req() (bogdan) * 2003-11-05 flag context updated from failure/reply handlers back * to transaction context (jiri) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) * 2003-12-04 global TM callbacks switched to per transaction callbacks * (bogdan) * 2004-02-06: support for user pref. added - destroy_avps (bogdan) * 2003-11-05 flag context updated from failure/reply handlers back * to transaction context (jiri) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) * 2004-02-13: t->is_invite and t->local replaced with flags (bogdan) * 2004-02-18 fifo_t_reply imported from vm module (bogdan) * 2004-08-23 avp list is available from failure/on_reply routes (bogdan) * 2004-10-01 added a new param.: restart_fr_on_each_reply (andrei) * 2005-03-01 force for statefull replies the incoming interface of * the request (bogdan) * 2005-03-01 local ACK sent to same address as INVITE -> * all [build|send]_[local]_ack functions merged into * send_ack() (bogdan) * 2007-01-25 DNS failover at transaction level added (bogdan) */ #include "../../hash_func.h" #include "../../dprint.h" #include "../../config.h" #include "../../parser/parser_f.h" #include "../../ut.h" #include "../../timer.h" #include "../../error.h" #include "../../action.h" #include "../../dset.h" #include "../../tags.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../usr_avp.h" #include "../../receive.h" #include "../../msg_callbacks.h" #include "h_table.h" #include "t_hooks.h" #include "t_funcs.h" #include "t_reply.h" #include "t_cancel.h" #include "t_msgbuilder.h" #include "t_lookup.h" #include "t_fwd.h" #include "fix_lumps.h" #include "t_stats.h" #include "uac.h" /* restart fr timer on each provisional reply, default yes */ int restart_fr_on_each_reply=1; int onreply_avp_mode = 0; /* disable the 6xx fork-blocking - default no (as per RFC3261) */ int disable_6xx_block = 0; /* flag for marching minor branches */ int minor_branch_flag = -1; char *minor_branch_flag_str = 0; /* private place where we create to-tags for replies */ char tm_tags[TOTAG_VALUE_LEN]; static str tm_tag = {tm_tags,TOTAG_VALUE_LEN}; char *tm_tag_suffix; static int picked_branch=-1; /* where to go if there is no positive reply */ static int goto_on_negative=0; /* where to go on receipt of reply */ static int goto_on_reply=0; /* currently processed branch */ extern int _tm_branch_index; /* returns the picked branch */ int t_get_picked_branch(void) { return picked_branch; } /* we store the reply_route # in private memory which is then processed during t_relay; we cannot set this value before t_relay creates transaction context or after t_relay when a reply may arrive after we set this value; that's why we do it how we do it, i.e., *inside* t_relay using hints stored in private memory before t_relay is called */ void t_on_negative( unsigned int go_to ) { struct cell *t = get_t(); /* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction; * in MODE_REQUEST T will be set only if the transaction was already * created; if not -> use the static variable */ if (!t || t==T_UNDEFINED ) goto_on_negative=go_to; else t->on_negative = go_to; } void t_on_reply( unsigned int go_to ) { struct cell *t = get_t(); /* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction; * in MODE_REQUEST T will be set only if the transaction was already * created; if not -> use the static variable */ if (!t || t==T_UNDEFINED ) { goto_on_reply=go_to; } else { if (route_type==BRANCH_ROUTE) { t->uac[_tm_branch_index].on_reply = go_to; } else { t->on_reply = go_to; } } } unsigned int get_on_negative(void) { return goto_on_negative; } unsigned int get_on_reply(void) { return goto_on_reply; } void tm_init_tags(void) { init_tags(tm_tags, &tm_tag_suffix, "OpenSIPS-TM/tags", TM_TAG_SEPARATOR ); } /* returns 0 if the message was previously acknowledged * (i.e., no E2EACK callback is needed) and one if the * callback shall be executed */ int unmatched_totag(struct cell *t, struct sip_msg *ack) { struct totag_elem *i; str *tag; if (parse_headers(ack, HDR_TO_F,0)==-1 || !ack->to ) { LM_ERR("To invalid\n"); return 1; } tag=&get_to(ack)->tag_value; for (i=t->fwded_totags; i; i=i->next) { if (i->tag.len==tag->len && memcmp(i->tag.s, tag->s, tag->len)==0) { LM_DBG("totag for e2e ACK found: %d\n", i->acked); /* to-tag recorded, and an ACK has been received for it */ if (i->acked) return 0; /* to-tag recorded, but this ACK came for the first time */ i->acked=1; return 1; } } /* surprising: to-tag never sighted before */ return 1; } static inline void update_local_tags(struct cell *trans, struct bookmark *bm, char *dst_buffer, char *src_buffer /* to which bm refers */) { if (bm->to_tag_val.s) { trans->uas.local_totag.s=bm->to_tag_val.s-src_buffer+dst_buffer; trans->uas.local_totag.len=bm->to_tag_val.len; } } /* append a newly received tag from a 200/INVITE to * transaction's set; (only safe if called from within * a REPLY_LOCK); it returns 1 if such a to tag already * exists */ inline static int update_totag_set(struct cell *t, struct sip_msg *ok) { struct totag_elem *i, *n; str *tag; char *s; if (!ok->to || !ok->to->parsed) { LM_ERR("to not parsed\n"); return 0; } tag=&get_to(ok)->tag_value; if (!tag->s) { LM_DBG("no tag in to\n"); return 0; } for (i=t->fwded_totags; i; i=i->next) { if (i->tag.len==tag->len && memcmp(i->tag.s, tag->s, tag->len) ==0 ){ /* to tag already recorded */ #ifdef XL_DEBUG LM_CRIT("totag retransmission\n"); #else LM_DBG("totag retransmission\n"); #endif return 1; } } /* that's a new to-tag -- record it */ #ifndef HP_MALLOC shm_lock(); n=(struct totag_elem*) shm_malloc_unsafe(sizeof(struct totag_elem)); s=(char *)shm_malloc_unsafe(tag->len); shm_unlock(); #else n=(struct totag_elem*) shm_malloc(sizeof(struct totag_elem)); s=(char *)shm_malloc(tag->len); #endif if (!s || !n) { LM_ERR("no more share memory \n"); if (n) shm_free(n); if (s) shm_free(s); return 0; } memset(n, 0, sizeof(struct totag_elem)); memcpy(s, tag->s, tag->len ); n->tag.s=s;n->tag.len=tag->len; n->next=t->fwded_totags; t->fwded_totags=n; LM_DBG("new totag \n"); return 0; } /* * Build and send an ACK to a negative reply */ static int send_ack(struct sip_msg* rpl, struct cell *trans, int branch) { str method = str_init(ACK); str to; char *ack_p; unsigned int ack_len; if(parse_headers(rpl,is_local(trans)?HDR_EOH_F:(HDR_TO_F|HDR_FROM_F),0)==-1 || !rpl->to || !rpl->from ) { LM_ERR("failed to generate a HBH ACK if key HFs in reply missing\n"); goto error; } to.s=rpl->to->name.s; to.len=rpl->to->len; ack_p = is_local(trans)? build_dlg_ack(rpl, trans, branch, &to, &ack_len): build_local( trans, branch, &method, NULL, rpl, &ack_len ); if (ack_p==0) { LM_ERR("failed to build ACK\n"); goto error; } SEND_PR_BUFFER(&trans->uac[branch].request, ack_p, ack_len); shm_free(ack_p); return 0; error: return -1; } static int _reply_light( struct cell *trans, char* buf, unsigned int len, unsigned int code, char *to_tag, unsigned int to_tag_len, int lock, struct bookmark *bm) { struct retr_buf *rb; unsigned int buf_len; branch_bm_t cancel_bitmap; str cb_s; if (!buf) { LM_DBG("response building failed\n"); /* determine if there are some branches to be canceled */ if ( is_invite(trans) ) { if (lock) LOCK_REPLIES( trans ); which_cancel(trans, &cancel_bitmap ); if (lock) UNLOCK_REPLIES( trans ); } /* and clean-up, including cancellations, if needed */ goto error; } cancel_bitmap=0; if (lock) LOCK_REPLIES( trans ); if ( is_invite(trans) ) which_cancel(trans, &cancel_bitmap ); if (trans->uas.status>=200) { LM_ERR("failed to generate %d reply when a final %d was sent out\n", code, trans->uas.status); goto error2; } rb = & trans->uas.response; rb->activ_type=code; trans->uas.status = code; buf_len = rb->buffer.s ? len : len + REPLY_OVERBUFFER_LEN; rb->buffer.s = (char*)shm_resize( rb->buffer.s, buf_len ); /* puts the reply's buffer to uas.response */ if (! rb->buffer.s ) { LM_ERR("failed to allocate shmem buffer\n"); goto error3; } update_local_tags(trans, bm, rb->buffer.s, buf); rb->buffer.len = len ; memcpy( rb->buffer.s , buf , len ); /* needs to be protected too because what timers are set depends on current transactions status */ /* t_update_timers_after_sending_reply( rb ); */ trans->relaied_reply_branch=-2; if (lock) UNLOCK_REPLIES( trans ); /* do UAC cleanup procedures in case we generated a final answer whereas there are pending UACs */ if (code>=200) { if ( is_local(trans) ) { LM_DBG("local transaction completed\n"); if ( has_tran_tmcbs(trans, TMCB_LOCAL_COMPLETED) ) { run_trans_callbacks( TMCB_LOCAL_COMPLETED, trans, 0, FAKED_REPLY, code); } } else { /* run the PRE send callbacks */ if ( has_tran_tmcbs(trans, TMCB_RESPONSE_PRE_OUT) ) { cb_s.s = buf; cb_s.len = len; set_extra_tmcb_params( &cb_s, &rb->dst); if (lock) run_trans_callbacks_locked( TMCB_RESPONSE_PRE_OUT, trans, trans->uas.request, FAKED_REPLY, code); else run_trans_callbacks( TMCB_RESPONSE_PRE_OUT, trans, trans->uas.request, FAKED_REPLY, code); } } if (!is_hopbyhop_cancel(trans)) { cleanup_uac_timers( trans ); if (is_invite(trans)) cancel_uacs( trans, cancel_bitmap ); /* for auth related replies, we do not do retransmission (via set_final_timer()), but only wait for a final reply (put_on_wait() ) - see RFC 3261 (26.3.2.4 DoS Protection) */ if ((code != 401) && (code != 407)) set_final_timer( trans ); else put_on_wait(trans); } } /* send it out : response.dst.send_sock is valid all the time now, * as it's taken from original request -bogdan */ if (!trans->uas.response.dst.send_sock) { LM_CRIT("send_sock is NULL\n"); } SEND_PR_BUFFER( rb, buf, len ); LM_DBG("reply sent out. buf=%p: %.9s..., " "shmem=%p: %.9s\n", buf, buf, rb->buffer.s, rb->buffer.s ); /* run the POST send callbacks */ if (code>=200&&!is_local(trans)&&has_tran_tmcbs(trans,TMCB_RESPONSE_OUT)) { cb_s.s = buf; cb_s.len = len; set_extra_tmcb_params( &cb_s, &rb->dst); if (lock) run_trans_callbacks_locked( TMCB_RESPONSE_OUT, trans, trans->uas.request, FAKED_REPLY, code); else run_trans_callbacks( TMCB_RESPONSE_OUT, trans, trans->uas.request, FAKED_REPLY, code); } pkg_free( buf ) ; stats_trans_rpl( code, 1 /*local*/ ); LM_DBG("finished\n"); return 1; error3: error2: if (lock) UNLOCK_REPLIES( trans ); pkg_free ( buf ); error: /* do UAC cleanup */ cleanup_uac_timers( trans ); if ( is_invite(trans) ) cancel_uacs( trans, cancel_bitmap ); /* we did not succeed -- put the transaction on wait */ put_on_wait(trans); return -1; } /* send a UAS reply * returns 1 if everything was OK or -1 for error */ static int _reply( struct cell *trans, struct sip_msg* p_msg, unsigned int code, str *text, int lock ) { unsigned int len; char * buf, *dset; struct bookmark bm; int dset_len; if (code>=200) set_kr(REQ_RPLD); /* compute the buffer in private memory prior to entering lock; * create to-tag if needed */ /* if that is a redirection message, dump current message set to it */ if (code>=300 && code<400) { dset=print_dset(p_msg, &dset_len); if (dset) { add_lump_rpl(p_msg, dset, dset_len, LUMP_RPL_HDR); } } /* check if the UAS retranmission port needs to be updated */ if ( (p_msg->msg_flags ^ trans->uas.request->msg_flags) & FL_FORCE_RPORT ) su_setport( &trans->uas.response.dst.to, p_msg->rcv.src_port ); if (code>=180 && p_msg->to && (get_to(p_msg)->tag_value.s==0 || get_to(p_msg)->tag_value.len==0)) { calc_crc_suffix( p_msg, tm_tag_suffix ); buf = build_res_buf_from_sip_req(code,text, &tm_tag, p_msg, &len, &bm); return _reply_light( trans, buf, len, code, tm_tag.s, TOTAG_VALUE_LEN, lock, &bm); } else { buf = build_res_buf_from_sip_req(code,text, 0 /*no to-tag*/, p_msg, &len, &bm); return _reply_light(trans,buf,len,code, 0, 0 /* no to-tag */, lock, &bm); } } /*if msg is set -> it will fake the env. vars conforming with the msg; if NULL * the env. will be restore to original */ static inline void faked_env( struct cell *t,struct sip_msg *msg) { static struct cell *backup_t; static struct usr_avp **backup_list; static struct socket_info* backup_si; static int backup_route_type; if (msg) { swap_route_type( backup_route_type, FAILURE_ROUTE); /* tm actions look in beginning whether transaction is * set -- whether we are called from a reply-processing * or a timer process, we need to set current transaction; * otherwise the actions would attempt to look the transaction * up (unnecessary overhead, refcounting) */ /* backup */ backup_t = get_t(); /* fake transaction */ set_t(t); /* make available the avp list from transaction */ backup_list = set_avp_list( &t->user_avps ); /* set default send address to the saved value */ backup_si = bind_address; bind_address = t->uac[0].request.dst.send_sock; } else { /* restore original environment */ set_t(backup_t); set_route_type( backup_route_type ); /* restore original avp list */ set_avp_list( backup_list ); bind_address = backup_si; } } /* return 1 if a failure_route processes */ static inline int run_failure_handlers(struct cell *t) { static struct sip_msg faked_req; struct sip_msg *shmem_msg; struct ua_client *uac; int on_failure; shmem_msg = t->uas.request; uac = &t->uac[picked_branch]; /* failure_route for a local UAC? */ if (!shmem_msg || REQ_LINE(shmem_msg).method_value==METHOD_CANCEL ) { LM_WARN("no UAC or CANCEL support (%d, %d) \n", t->on_negative, t->tmcb_hl.reg_types); return 0; } /* don't start faking anything if we don't have to */ if ( !has_tran_tmcbs( t, TMCB_ON_FAILURE) && !t->on_negative ) { LM_WARN("no negative handler (%d, %d)\n",t->on_negative, t->tmcb_hl.reg_types); return 1; } if (!fake_req(&faked_req, shmem_msg, &t->uas, uac, 0/*no dst_uri*/)) { LM_ERR("fake_req failed\n"); return 0; } /* fake also the env. conforming to the fake msg */ faked_env( t, &faked_req); /* DONE with faking ;-) -> run the failure handlers */ if ( has_tran_tmcbs( t, TMCB_ON_FAILURE) ) { run_trans_callbacks( TMCB_ON_FAILURE, t, &faked_req, uac->reply, uac->last_received); } if (t->on_negative) { /* update flags in transaction if changed by callbacks */ shmem_msg->flags = faked_req.flags; /* avoid recursion -- if failure_route forwards, and does not * set next failure route, failure_route will not be reentered * on failure */ on_failure = t->on_negative; t->on_negative=0; /* run a reply_route action if some was marked */ run_top_route(failure_rlist[on_failure].a, &faked_req); } /* restore original environment and free the fake msg */ faked_env( t, 0); free_faked_req(&faked_req,t); /* if failure handler changed flag, update transaction context */ shmem_msg->flags = faked_req.flags; /* branch flags do not need to be updated as this branch will be never * used again */ return 1; } static inline int is_3263_failure(struct cell *t) { /* is is a DNS failover scenario? - according to RFC 3263 * and RFC 3261, this means 503 reply with Retr-After hdr * or timeout with no reply */ LM_DBG("dns-failover test: branch=%d, last_recv=%d, flags=%X\n", picked_branch, t->uac[picked_branch].last_received, t->uac[picked_branch].flags); switch (t->uac[picked_branch].last_received) { case 408: return ((t->uac[picked_branch].flags&T_UAC_HAS_RECV_REPLY)==0); case 503: if (t->uac[picked_branch].reply==NULL || t->uac[picked_branch].reply==FAKED_REPLY) return 0; /* we do not care about the Retry-After header in 503 * as following discussion on sip-implementers list : * with or without a RA header, a 503 should fire DNS * based failover - bogdan */ return 1; } return 0; } static inline int do_dns_failover(struct cell *t) { static struct sip_msg faked_req; struct sip_msg *shmem_msg; struct sip_msg *req; struct ua_client *uac; dlg_t dialog; int ret, sip_msg_len; uac = &t->uac[picked_branch]; /* check if the DNS resolver can get at least one new IP */ if ( get_next_su( uac->proxy, &uac->request.dst.to, 1)!=0 ) return -1; LM_DBG("new destination available\n"); if (t->uas.request==NULL) { if (!is_local(t)) { LM_CRIT("BUG: proxy transaction without UAS request :-/\n"); return -1; } /* create the cloned SIP msg -> first create a new SIP msg */ memset( &dialog, 0, sizeof(dialog)); dialog.send_sock = uac->request.dst.send_sock; dialog.hooks.next_hop = &uac->uri; req = buf_to_sip_msg(uac->request.buffer.s, uac->request.buffer.len, &dialog); if (req==NULL) { LM_ERR("failed to generate SIP msg from previous buffer\n"); return -1; } /* now do the actual cloning of the SIP message */ t->uas.request = sip_msg_cloner( req, &sip_msg_len, 1); if (t->uas.request==NULL) { LM_ERR("cloning failed\n"); free_sip_msg(req); return -1; } t->uas.end_request = ((char*)t->uas.request) + sip_msg_len; /* free the actual SIP message, keep the clone only */ free_sip_msg(req); /* the sip_msg structure is static in buf_to_sip_msg, so no need to free it */ } shmem_msg = t->uas.request; if (!fake_req(&faked_req, shmem_msg, &t->uas, uac, 1/*with dst_uri*/)) { LM_ERR("fake_req failed\n"); return -1; } /* fake also the env. conforming to the fake msg */ faked_env( t, &faked_req); ret = -1; /* set info as current destination */ if ( set_ruri( &faked_req, &uac->uri)!= 0) goto done; setb0flags( &faked_req, uac->br_flags ); faked_req.force_send_socket = shmem_msg->force_send_socket; /* send it out */ if (t_forward_nonack( t, &faked_req, uac->proxy)==1) ret = 0; done: /* restore original environment and free the fake msg */ faked_env( t, 0); free_faked_req(&faked_req,t); return ret; } static inline int branch_prio( short ret_code, unsigned int is_cancelled) { int first_digit; first_digit = ret_code / 100; switch(first_digit){ case 1: /* 100 - 199 */ case 2: /* 200 - 299 */ return ret_code; case 6: return ret_code - 300; /* 300 - 399 */ case 3: return ret_code + 100; /* 400 - 499 */ case 5: if (ret_code==503) return 801; /* 801 */ return ret_code + 200; /* 700 - 799 */ case 4: switch(ret_code){ case 401: return 500; /* 500 - 599 */ case 407: return 501; case 415: return 502; case 420: return 503; case 484: return 504; case 408: return 800; /* 800 */ case 487: if(is_cancelled) return 0; default: /* the rest of ret codes in the 4xx class */ return ret_code + 200; /* 600 - 699 */ } default: return ret_code + 200; /* > 900 */ } } /* select a branch for forwarding; returns: * 0..X ... branch number * -1 ... error * -2 ... can't decide yet -- incomplete branches present */ static inline int t_pick_branch( struct cell *t, int *res_code, int *do_cancel) { int lowest_b, lowest_s, b, prio; unsigned int cancelled; lowest_b=-1; lowest_s=999; cancelled = was_cancelled(t); *do_cancel = 0; for ( b=t->first_branch; bnr_of_outgoings ; b++ ) { /* skip 'empty branches' */ if (!t->uac[b].request.buffer.s) continue; /* there is still an unfinished UAC transaction; wait now! */ if ( t->uac[b].last_received<200 ) { if (t->uac[b].br_flags & minor_branch_flag) { *do_cancel = 1; continue; /* if last branch, lowest_b remains -1 */ } return -2; } /* compare against the priority of the current branch */ prio = branch_prio(t->uac[b].last_received,cancelled); if ( (lowest_b==-1) || (priouac[lowest_b].last_received,lowest_s); *res_code=lowest_s; return lowest_b; } /* This is the neurological point of reply processing -- called * from within a REPLY_LOCK, t_should_relay_response decides * how a reply shall be processed and how transaction state is * affected. * * Checks if the new reply (with new_code status) should be sent or not * based on the current * transaction status. * Returns - branch number (0,1,...) which should be relayed * -1 if nothing to be relayed */ static enum rps t_should_relay_response( struct cell *Trans , int new_code, int branch , int *should_store, int *should_relay, branch_bm_t *cancel_bitmap, struct sip_msg *reply ) { int branch_cnt; int picked_code; int inv_through; int do_cancel; /* note: this code never lets replies to CANCEL go through; we generate always a local 200 for CANCEL; 200s are not relayed because it's not an INVITE transaction; >= 300 are not relayed because 200 was already sent out */ LM_DBG("T_code=%d, new_code=%d\n", Trans->uas.status,new_code); inv_through=new_code>=200 && new_code<300 && is_invite(Trans); /* if final response sent out, allow only INVITE 2xx */ if ( Trans->uas.status >= 200 ) { if (inv_through) { LM_DBG("200 OK for INVITE after final sent\n"); *should_store=0; Trans->uac[branch].last_received=new_code; *should_relay=branch; return RPS_PUSHED_AFTER_COMPLETION; } if ( is_hopbyhop_cancel(Trans) && new_code>=200) { *should_store=0; *should_relay=-1; picked_branch=-1; return RPS_COMPLETED; } /* except the exception above, too late messages will be discarded */ goto discard; } /* if final response received at this branch, allow only INVITE 2xx */ if (Trans->uac[branch].last_received>=200 && !(inv_through && Trans->uac[branch].last_received<300)) { #ifdef EXTRA_DEBUG /* don't report on retransmissions */ if (Trans->uac[branch].last_received==new_code) { LM_DBG("final reply retransmission\n"); } else /* if you FR-timed-out, faked a local 408 and 487 came, don't * report on it either */ if (Trans->uac[branch].last_received==408 && new_code==487) { LM_DBG("487 reply came for a timed-out branch\n"); } else { /* this looks however how a very strange status rewrite attempt; * report on it */ LM_DBG("status rewrite by UAS: stored: %d, received: %d\n", Trans->uac[branch].last_received, new_code ); } #endif goto discard; } /* no final response sent yet */ /* negative replies subject to fork picking */ if (new_code >=300 ) { Trans->uac[branch].last_received=new_code; /* also append the current reply to the transaction to * make it available in failure routes - a kind of "fake" * save of the final reply per branch */ Trans->uac[branch].reply = reply; if (new_code>=600 && !disable_6xx_block) { /* this is a winner and close all branches */ which_cancel( Trans, cancel_bitmap ); picked_branch=branch; /* no more new branches should be added to this transaction */ Trans->flags |= T_NO_NEW_BRANCHES_FLAG; } else { /* if all_final return lowest */ picked_branch = t_pick_branch( Trans, &picked_code, &do_cancel); if (picked_branch==-2) { /* branches open yet */ *should_store=1; *should_relay=-1; picked_branch=-1; Trans->uac[branch].reply = 0; return RPS_STORE; } if (picked_branch==-1) { LM_CRIT("pick_branch failed (lowest==-1) for code %d\n",new_code); Trans->uac[branch].reply = 0; goto discard; } if (do_cancel) { branch_bm_t cb = 0; which_cancel( Trans, &cb ); cleanup_uac_timers(Trans); cancel_uacs( Trans, cb); } } /* no more pending branches -- try if that changes after * a callback; save branch count to be able to determine * later if new branches were initiated */ branch_cnt=Trans->nr_of_outgoings; reset_kr(); if ( !(Trans->flags&T_NO_DNS_FAILOVER_FLAG) && Trans->uac[picked_branch].proxy!=NULL ) { /* is is a DNS failover scenario, according to RFC 3263 ? */ if (is_3263_failure(Trans)) { LM_DBG("trying DNS-based failover\n"); /* do DNS failover -> add new branches */ if (do_dns_failover( Trans )!=0) { /* skip the failed added branches */ branch_cnt = Trans->nr_of_outgoings; } } } /* run ON_FAILURE handlers ( route and callbacks) */ if ( branch_cnt==Trans->nr_of_outgoings && (has_tran_tmcbs( Trans, TMCB_ON_FAILURE) || Trans->on_negative) ) { run_failure_handlers( Trans ); } /* now reset it; after the failure logic, the reply may * not be stored any more and we don't want to keep into * transaction some broken reference */ Trans->uac[branch].reply = 0; /* look if the callback perhaps replied transaction; it also covers the case in which a transaction is replied localy on CANCEL -- then it would make no sense to proceed to new branches bellow */ if (Trans->uas.status >= 200) { *should_store=0; *should_relay=-1; /* this might deserve an improvement -- if something was already replied, it was put on wait and then, returning RPS_COMPLETED will make t_on_reply put it on wait again; perhaps splitting put_on_wait from send_reply or a new RPS_ code would be healthy */ picked_branch=-1; return RPS_COMPLETED; } /* look if the callback/failure_route introduced new branches ... */ if (branch_cntnr_of_outgoings && get_kr()==REQ_FWDED) { /* await then result of new branches */ *should_store=1; *should_relay=-1; picked_branch=-1; return RPS_STORE; } /* really no more pending branches -- return selected code */ *should_store=0; *should_relay=picked_branch; picked_branch=-1; return RPS_COMPLETED; } /* not >=300 ... it must be 2xx or provisional 1xx */ if (new_code>=100) { /* 1xx and 2xx except 100 will be relayed */ Trans->uac[branch].last_received=new_code; *should_store=0; *should_relay= new_code==100? -1 : branch; if (new_code>=200 ) { which_cancel( Trans, cancel_bitmap ); return RPS_COMPLETED; } else return RPS_PROVISIONAL; } discard: *should_store=0; *should_relay=-1; return RPS_DISCARDED; } /* Retransmits the last sent inbound reply. * input: p_msg==request for which I want to retransmit an associated reply * Returns -1 - error * 1 - OK */ int t_retransmit_reply( struct cell *t ) { static char b[BUF_SIZE]; int len; /* we need to lock the transaction as messages from upstream may change it continuously */ LOCK_REPLIES( t ); if (!t->uas.response.buffer.s) { LM_DBG("nothing to retransmit\n"); goto error; } /* response.dst.send_sock should be valid all the time now, as it's taken from original request -bogdan */ if (t->uas.response.dst.send_sock==0) { LM_CRIT("something to retransmit, but send_sock is NULL\n"); goto error; } len=t->uas.response.buffer.len; if ( len==0 || len>BUF_SIZE ) { LM_DBG("zero length or too big to retransmit: %d\n", len); goto error; } memcpy( b, t->uas.response.buffer.s, len ); UNLOCK_REPLIES( t ); SEND_PR_BUFFER( & t->uas.response, b, len ); LM_DBG("buf=%p: %.9s..., shmem=%p: %.9s\n",b, b, t->uas.response.buffer.s, t->uas.response.buffer.s ); return 1; error: UNLOCK_REPLIES(t); return -1; } int t_reply( struct cell *t, struct sip_msg* p_msg, unsigned int code, str * text ) { return _reply( t, p_msg, code, text, 1 /* lock replies */ ); } int t_reply_unsafe( struct cell *t, struct sip_msg* p_msg, unsigned int code, str * text ) { return _reply( t, p_msg, code, text, 0 /* don't lock replies */ ); } void set_final_timer( /* struct s_table *h_table, */ struct cell *t ) { if ( !is_local(t) && t->uas.request->REQ_METHOD==METHOD_INVITE ) { /* crank timers for negative replies */ if (t->uas.status>=300) { start_retr(&t->uas.response); return; } /* local UAS retransmits too */ if (t->relaied_reply_branch==-2 && t->uas.status>=200) { /* we retransmit 200/INVs regardless of transport -- even if TCP used, UDP could be used upstream and loose the 200, which is not retransmitted by proxies */ force_retr( &t->uas.response ); return; } } put_on_wait(t); } void cleanup_uac_timers( struct cell *t ) { int i; /* reset FR/retransmission timers */ for (i=t->first_branch; inr_of_outgoings; i++ ) { reset_timer( &t->uac[i].request.retr_timer ); reset_timer( &t->uac[i].request.fr_timer ); } LM_DBG("RETR/FR timers reset\n"); } static int store_reply( struct cell *trans, int branch, struct sip_msg *rpl) { # ifdef EXTRA_DEBUG if (trans->uac[branch].reply) { LM_ERR("replacing stored reply; aborting\n"); abort(); } # endif /* when we later do things such as challenge aggregation, we should parse the message here before we conserve it in shared memory; -jiri */ if (rpl==FAKED_REPLY) trans->uac[branch].reply=FAKED_REPLY; else trans->uac[branch].reply = sip_msg_cloner( rpl, 0, 0 ); if (! trans->uac[branch].reply ) { LM_ERR("failed to alloc' clone memory\n"); return 0; } return 1; } /* this is the code which decides what and when shall be relayed upstream; note well -- it assumes it is entered locked with REPLY_LOCK and it returns unlocked! */ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch, unsigned int msg_status, branch_bm_t *cancel_bitmap ) { int relay; int save_clone; char *buf; /* length of outbound reply */ unsigned int res_len; int relayed_code; struct sip_msg *relayed_msg; struct bookmark bm; int totag_retr; enum rps reply_status; /* retransmission structure of outbound reply and request */ struct retr_buf *uas_rb; str cb_s; str text; /* keep compiler warnings about use of uninit vars silent */ res_len=0; buf=0; relayed_msg=0; relayed_code=0; totag_retr=0; /* remember, what was sent upstream to know whether we are * forwarding a first final reply or not */ /* *** store and relay message as needed *** */ reply_status = t_should_relay_response(t, msg_status, branch, &save_clone, &relay, cancel_bitmap, p_msg ); LM_DBG("branch=%d, save=%d, relay=%d\n", branch, save_clone, relay ); /* store the message if needed */ if (save_clone) /* save for later use, typically branch picking */ { if (!store_reply( t, branch, p_msg )) goto error01; } uas_rb = & t->uas.response; if (relay >= 0 ) { /* initialize sockets for outbound reply */ uas_rb->activ_type=msg_status; t->relaied_reply_branch = relay; /* try building the outbound reply from either the current * or a stored message */ relayed_msg = branch==relay ? p_msg : t->uac[relay].reply; if (relayed_msg==FAKED_REPLY) { relayed_code = branch==relay ? msg_status : t->uac[relay].last_received; text.s = error_text(relayed_code); text.len = strlen(text.s); /* FIXME - bogdan*/ if (relayed_code>=180 && t->uas.request->to && (get_to(t->uas.request)->tag_value.s==0 || get_to(t->uas.request)->tag_value.len==0)) { calc_crc_suffix( t->uas.request, tm_tag_suffix ); buf = build_res_buf_from_sip_req( relayed_code, &text, &tm_tag, t->uas.request, &res_len, &bm ); } else { buf = build_res_buf_from_sip_req( relayed_code, &text, 0/* no to-tag */, t->uas.request, &res_len, &bm ); } } else { /* run callbacks for all types of responses - * even if they are shmem-ed or not */ if (has_tran_tmcbs(t,TMCB_RESPONSE_FWDED) ) { run_trans_callbacks( TMCB_RESPONSE_FWDED, t, t->uas.request, relayed_msg, msg_status ); } relayed_code=relayed_msg->REPLY_STATUS; buf = build_res_buf_from_sip_res( relayed_msg, &res_len, uas_rb->dst.send_sock,0); /* remove all lumps which are not in shm * added either by build_res_buf_from_sip_res, or by * the callbacks that have been called with shmem-ed messages - vlad */ if (branch!=relay) { del_notflaged_lumps( &(relayed_msg->add_rm), LUMPFLAG_SHMEM); del_notflaged_lumps( &(relayed_msg->body_lumps), LUMPFLAG_SHMEM); } } if (!buf) { LM_ERR("no mem for outbound reply buffer\n"); goto error02; } /* attempt to copy the message to UAS's shmem: - copy to-tag for ACK matching as well - allocate little a bit more for provisional as larger messages are likely to follow and we will be able to reuse the memory frag */ uas_rb->buffer.s = (char*)shm_resize( uas_rb->buffer.s, res_len + (msg_status<200 ? REPLY_OVERBUFFER_LEN : 0)); if (!uas_rb->buffer.s) { LM_ERR("no more share memory\n"); goto error03; } uas_rb->buffer.len = res_len; memcpy( uas_rb->buffer.s, buf, res_len ); if (relayed_msg==FAKED_REPLY) { /* to-tags for local replies */ update_local_tags(t, &bm, uas_rb->buffer.s, buf); } stats_trans_rpl( relayed_code, (relayed_msg==FAKED_REPLY)?1:0 ); /* update the status ... */ t->uas.status = relayed_code; if (is_invite(t) && relayed_msg!=FAKED_REPLY && relayed_code>=200 && relayed_code < 300 && has_tran_tmcbs( t, TMCB_RESPONSE_OUT|TMCB_RESPONSE_PRE_OUT)) { totag_retr=update_totag_set(t, relayed_msg); } }; /* if relay ... */ UNLOCK_REPLIES( t ); /* Setup retransmission timer _before_ the reply is sent * to avoid race conditions */ if (reply_status == RPS_COMPLETED) { /* for auth related replies, we do not do retransmission (via set_final_timer()), but only wait for a final reply (put_on_wait() ) - see RFC 3261 (26.3.2.4 DoS Protection) */ if ((relayed_code != 401) && (relayed_code != 407)) set_final_timer(t); else put_on_wait(t); } /* send it now (from the private buffer) */ if (relay >= 0) { /* run the PRE sending out callback */ if (!totag_retr && has_tran_tmcbs(t, TMCB_RESPONSE_PRE_OUT) ) { cb_s.s = buf; cb_s.len = res_len; set_extra_tmcb_params( &cb_s, &uas_rb->dst); run_trans_callbacks_locked(TMCB_RESPONSE_PRE_OUT,t,t->uas.request, relayed_msg, relayed_code); } SEND_PR_BUFFER( uas_rb, buf, res_len ); LM_DBG("sent buf=%p: %.9s..., shmem=%p: %.9s\n", buf, buf, uas_rb->buffer.s, uas_rb->buffer.s ); /* run the POST sending out callback */ if (!totag_retr && has_tran_tmcbs(t, TMCB_RESPONSE_OUT) ) { cb_s.s = buf; cb_s.len = res_len; set_extra_tmcb_params( &cb_s, &uas_rb->dst); run_trans_callbacks_locked( TMCB_RESPONSE_OUT, t, t->uas.request, relayed_msg, relayed_code); } pkg_free( buf ); } /* success */ return reply_status; error03: pkg_free( buf ); error02: if (save_clone) { if (t->uac[branch].reply!=FAKED_REPLY) free_cloned_msg( t->uac[branch].reply ); t->uac[branch].reply = NULL; } error01: text.s = "Reply processing error"; text.len = sizeof("Reply processing error")-1; t_reply_unsafe( t, t->uas.request, 500, &text ); UNLOCK_REPLIES(t); if (is_invite(t)) cancel_uacs( t, *cancel_bitmap ); /* a serious error occurred -- attempt to send an error reply; it will take care of clean-ups */ /* failure */ return RPS_ERROR; } /* this is the "UAC" above transaction layer; if a final reply is received, it triggers a callback; note well -- it assumes it is entered locked with REPLY_LOCK and it returns unlocked! */ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch, unsigned int msg_status, branch_bm_t *cancel_bitmap) { /* how to deal with replies for local transaction */ int local_store, local_winner; enum rps reply_status; struct sip_msg *winning_msg; int winning_code; int totag_retr; /* branch_bm_t cancel_bitmap; */ /* keep warning 'var might be used un-inited' silent */ winning_msg=0; winning_code=0; totag_retr=0; *cancel_bitmap=0; reply_status=t_should_relay_response( t, msg_status, branch, &local_store, &local_winner, cancel_bitmap, p_msg ); LM_DBG("branch=%d, save=%d, winner=%d\n", branch, local_store, local_winner ); if (local_store) { if (!store_reply(t, branch, p_msg)) goto error; } if (local_winner>=0) { winning_msg= branch==local_winner ? p_msg : t->uac[local_winner].reply; if (winning_msg==FAKED_REPLY) { winning_code = branch==local_winner ? msg_status : t->uac[local_winner].last_received; } else { winning_code=winning_msg->REPLY_STATUS; } t->uas.status = winning_code; stats_trans_rpl( winning_code, (winning_msg==FAKED_REPLY)?1:0 ); if (is_invite(t) && winning_msg!=FAKED_REPLY && winning_code>=200 && winning_code <300 && has_tran_tmcbs(t, TMCB_RESPONSE_OUT|TMCB_RESPONSE_PRE_OUT) ) { totag_retr=update_totag_set(t, winning_msg); } } UNLOCK_REPLIES(t); if ( local_winner >= 0 ) { if (winning_code < 200) { if (!totag_retr && has_tran_tmcbs(t,TMCB_LOCAL_RESPONSE_OUT)) { LM_DBG("Passing provisional reply %d to FIFO application\n", winning_code); run_trans_callbacks( TMCB_LOCAL_RESPONSE_OUT, t, 0, winning_msg, winning_code); } } else { LM_DBG("local transaction completed\n"); if (!totag_retr && has_tran_tmcbs(t,TMCB_LOCAL_COMPLETED) ) { run_trans_callbacks( TMCB_LOCAL_COMPLETED, t, 0, winning_msg, winning_code ); } } } return reply_status; error: which_cancel(t, cancel_bitmap); UNLOCK_REPLIES(t); cleanup_uac_timers(t); if ( get_cseq(p_msg)->method_id==METHOD_INVITE ) cancel_uacs( t, *cancel_bitmap ); put_on_wait(t); return RPS_ERROR; } /* This function is called whenever a reply for our module is received; * we need to register this function on module initialization; * Returns : 0 - core router stops * 1 - core router relay statelessly */ int reply_received( struct sip_msg *p_msg ) { int msg_status; int last_uac_status; int branch; int reply_status; utime_t timer; /* has the transaction completed now and we need to clean-up? */ branch_bm_t cancel_bitmap; struct ua_client *uac; struct cell *t; struct usr_avp **backup_list; unsigned int has_reply_route; set_t(T_UNDEFINED); /* make sure we know the associated transaction ... */ if (t_check(p_msg, &branch ) == -1) goto not_found; /*... if there is none, tell the core router to fwd statelessly */ t = get_t(); if ((t == 0) || (t == T_UNDEFINED)) goto not_found; cancel_bitmap=0; msg_status=p_msg->REPLY_STATUS; uac=&t->uac[branch]; LM_DBG("org. status uas=%d, uac[%d]=%d local=%d is_invite=%d)\n", t->uas.status, branch, uac->last_received, is_local(t), is_invite(t)); last_uac_status=uac->last_received; if_update_stat( tm_enable_stats, tm_rcv_rpls , 1); /* it's a cancel which is not e2e ? */ if ( get_cseq(p_msg)->method_id==METHOD_CANCEL && is_invite(t) ) { /* ... then just stop timers */ reset_timer( &uac->local_cancel.retr_timer); if ( msg_status >= 200 ) { reset_timer( &uac->local_cancel.fr_timer); } LM_DBG("reply to local CANCEL processed\n"); goto done; } /* *** stop timers *** */ /* stop retransmission */ reset_timer(&uac->request.retr_timer); /* stop final response timer only if I got a final response */ if ( msg_status >= 200 ) { reset_timer( &uac->request.fr_timer); } /* acknowledge negative INVITE replies (do it before detailed * on_reply processing, which may take very long, like if it * is attempted to establish a TCP connection to a fail-over dst */ if (is_invite(t) && ((msg_status >= 300) || (is_local(t) && !no_autoack(t) && msg_status >= 200) )) { if (send_ack(p_msg, t, branch)!=0) LM_ERR("failed to send ACK (local=%s)\n", is_local(t)?"yes":"no"); } _tm_branch_index = branch; /* processing of on_reply block */ has_reply_route = (t->on_reply) || (t->uac[branch].on_reply); if (has_reply_route) { if (onreply_avp_mode) { /* lock the reply*/ LOCK_REPLIES( t ); /* set the as avp_list the one from transaction */ backup_list = set_avp_list(&t->user_avps); } else { backup_list = 0; } /* transfer transaction flag to branch context */ p_msg->flags = t->uas.request ? t->uas.request->flags : 0; setb0flags( p_msg, t->uac[branch].br_flags); /* run block - first per branch and then global one */ if ( t->uac[branch].on_reply && (run_top_route(onreply_rlist[t->uac[branch].on_reply].a,p_msg) &ACT_FL_DROP) && (msg_status<200) ) { if (onreply_avp_mode) { UNLOCK_REPLIES( t ); set_avp_list( backup_list ); } LM_DBG("dropping provisional reply %d\n", msg_status); goto done; } if ( t->on_reply && (run_top_route(onreply_rlist[t->on_reply].a,p_msg) &ACT_FL_DROP) && (msg_status<200) ) { if (onreply_avp_mode) { UNLOCK_REPLIES( t ); set_avp_list( backup_list ); } LM_DBG("dropping provisional reply %d\n", msg_status); goto done; } /* transfer current message context back to t */ t->uac[branch].br_flags = getb0flags(p_msg); if (t->uas.request) t->uas.request->flags = p_msg->flags; if (onreply_avp_mode) /* restore original avp list */ set_avp_list( backup_list ); } if (!onreply_avp_mode || !has_reply_route) /* lock the reply*/ LOCK_REPLIES( t ); /* mark that the UAC received replies */ uac->flags |= T_UAC_HAS_RECV_REPLY; /* we fire a cancel on spot if (a) branch is marked "to be canceled" or (b) * the whole transaction was canceled (received cancel) and no cancel sent * yet on this branch; and of course, only if a provisional reply :) */ if (t->uac[branch].flags&T_UAC_TO_CANCEL_FLAG || ((t->flags&T_WAS_CANCELLED_FLAG) && !t->uac[branch].local_cancel.buffer.s)) { if ( msg_status < 200 ) /* reply for an UAC with a pending cancel -> do cancel now */ cancel_branch(t, branch); /* reset flag */ t->uac[branch].flags &= ~(T_UAC_TO_CANCEL_FLAG); } if (is_local(t)) { reply_status = local_reply(t,p_msg, branch,msg_status,&cancel_bitmap); if (reply_status == RPS_COMPLETED) { cleanup_uac_timers(t); if (is_invite(t)) cancel_uacs(t, cancel_bitmap); /* There is no need to call set_final_timer because we know * that the transaction is local */ put_on_wait(t); } } else { reply_status = relay_reply(t,p_msg,branch,msg_status,&cancel_bitmap); /* clean-up the transaction when transaction completed */ if (reply_status == RPS_COMPLETED) { /* no more UAC FR/RETR (if I received a 2xx, there may * be still pending branches ... */ cleanup_uac_timers(t); if (is_invite(t)) cancel_uacs(t, cancel_bitmap); /* FR for negative INVITES, WAIT anything else */ /* set_final_timer(t); */ } } if (reply_status!=RPS_PROVISIONAL) goto done; /* update FR/RETR timers on provisional replies */ if (msg_status < 200 && (restart_fr_on_each_reply || ((last_uac_status= 180) || (last_uac_status == 0))) ) ) { /* provisional now */ if (is_invite(t)) { /* invite: change FR to longer FR_INV, do not * attempt to restart retransmission any more */ timer = is_timeout_set(t->fr_inv_timeout) ? t->fr_inv_timeout : timer_id2timeout[FR_INV_TIMER_LIST]; LM_DBG("FR_INV_TIMER = %lld\n", timer); set_timer(&uac->request.fr_timer, FR_INV_TIMER_LIST, &timer); } else { /* non-invite: restart retransmissions (slow now) */ uac->request.retr_list = RT_T2; set_timer(&uac->request.retr_timer, RT_T2, 0); } } /* provisional replies */ done: /* we are done with the transaction, so unref it - the reference * was incremented by t_check() function -bogdan*/ t_unref(p_msg); /* don't try to relay statelessly neither on success * (we forwarded statefully) nor on error; on troubles, * simply do nothing; that will make the other party to * retransmit; hopefuly, we'll then be better off */ _tm_branch_index = 0; return 0; not_found: set_t(T_UNDEFINED); return 1; } int w_t_reply_body(struct sip_msg* msg, str* code, str *text, str *body) { struct cell *t; int r; str code_s; unsigned int code_i; str body_s; if(body==0) { LM_ERR("Wrong argument, body must not be NULL\n"); return -1; } if(((pv_elem_p)code)->spec.getf!=NULL) { if(pv_printf_s(msg, (pv_elem_p)code, &code_s)!=0) return -1; if(str2int(&code_s, &code_i)!=0 || code_i<100 || code_i>699) return -1; } else { code_i = ((pv_elem_p)code)->spec.pvp.pvn.u.isname.name.n; } if(((pv_elem_p)text)->spec.getf!=NULL) { if(pv_printf_s(msg, (pv_elem_p)text, &code_s)!=0 || code_s.len <=0) return -1; } else { code_s = ((pv_elem_p)text)->text; } if(((pv_elem_p)body)->spec.getf!=NULL) { if(pv_printf_s(msg, (pv_elem_p)body, &body_s)!=0 || body_s.len <=0) return -1; } else { body_s = ((pv_elem_p)body)->text; } t=get_t(); if ( t==0 || t==T_UNDEFINED ) { /* t_reply_with_body() is a bit of a weird function as it * receiving as parameter the actual msg, but the transaction * (and uses the saved msg from transaction). * So we need to take care and save everything into transaction, * otherwise we will loose the rpl lumps. --bogdan */ r = t_newtran( msg, 1/*full uas cloning*/ ); if (r==0) { /* retransmission -> break the script */ return 0; } else if (r<0) { LM_ERR("could not create a new transaction\n"); return -1; } t=get_t(); } return t_reply_with_body(t, code_i, &code_s, &body_s, 0, 0); } int t_reply_with_body( struct cell *trans, unsigned int code, str *text, str *body, str *new_header, str *to_tag ) { struct lump_rpl *hdr_lump; struct lump_rpl *body_lump; str rpl; int ret; struct bookmark bm; struct sip_msg* p_msg = trans->uas.request; str to_tag_rpl= {0, 0}; /* add the lumps for new_header and for body (by bogdan) */ if (new_header && new_header->len) { hdr_lump = add_lump_rpl( p_msg, new_header->s, new_header->len, LUMP_RPL_HDR ); if ( !hdr_lump ) { LM_ERR("failed to add hdr lump\n"); goto error; } } else { hdr_lump = 0; } /* body lump */ if(body && body->len) { body_lump = add_lump_rpl( p_msg, body->s, body->len, LUMP_RPL_BODY ); if (body_lump==0) { LM_ERR("failed add body lump\n"); goto error_1; } } else { body_lump = 0; } if(to_tag && to_tag->len) { rpl.s = build_res_buf_from_sip_req(code, text, to_tag, p_msg, (unsigned int*)&rpl.len, &bm); to_tag_rpl = *to_tag; } else if (code>=180 && p_msg->to && (get_to(p_msg)->tag_value.s==0 || get_to(p_msg)->tag_value.len==0)) { calc_crc_suffix( p_msg, tm_tag_suffix ); rpl.s = build_res_buf_from_sip_req(code,text, &tm_tag, p_msg, (unsigned int*)&rpl.len, &bm); to_tag_rpl.s = tm_tag.s; to_tag_rpl.len = TOTAG_VALUE_LEN; } else { rpl.s = build_res_buf_from_sip_req(code,text, 0 /*no to-tag*/, p_msg, (unsigned int*)&rpl.len, &bm); } /* since the msg (trans->uas.request) is a clone into shm memory, to avoid * memory leak or crashing (lumps are create in private memory) I will * remove the lumps by myself here (bogdan) */ if ( hdr_lump ) { unlink_lump_rpl( p_msg, hdr_lump); free_lump_rpl( hdr_lump ); } if( body_lump ) { unlink_lump_rpl( p_msg, body_lump); free_lump_rpl( body_lump ); } if (rpl.s==0) { LM_ERR("failed in doing build_res_buf_from_sip_req()\n"); goto error; } ret=_reply_light( trans, rpl.s, rpl.len, code, to_tag_rpl.s, to_tag_rpl.len, 1 /* lock replies */, &bm ); /* mark the transaction as replied */ if (code>=200) set_kr(REQ_RPLD); return ret; error_1: if ( hdr_lump ) { unlink_lump_rpl( p_msg, hdr_lump); free_lump_rpl( hdr_lump ); } error: return -1; } opensips-2.2.2/modules/tm/t_reply.h000066400000000000000000000100461300170765700173030ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _T_REPLY_H #define _T_REPLY_H #include "../../tags.h" #include "h_table.h" extern int restart_fr_on_each_reply; extern int onreply_avp_mode; /* reply processing status */ enum rps { /* something bad happened */ RPS_ERROR=0, /* transaction completed but we still accept the reply */ RPS_PUSHED_AFTER_COMPLETION, /* reply discarded */ RPS_DISCARDED, /* reply stored for later processing */ RPS_STORE, /* transaction completed */ RPS_COMPLETED, /* provisional reply not affecting transaction state */ RPS_PROVISIONAL }; extern char tm_tags[TOTAG_VALUE_LEN]; extern char *tm_tag_suffix; extern int disable_6xx_block; /* flag for marching minor branches */ extern int minor_branch_flag; extern char *minor_branch_flag_str; /* has this to-tag been never seen in previous 200/INVs? */ int unmatched_totag(struct cell *t, struct sip_msg *ack); /* branch bitmap type */ typedef unsigned int branch_bm_t; /* reply export types */ typedef int (*treply_f)(struct sip_msg * , unsigned int , str * ); typedef int (*treply_wb_f)( struct cell* trans, unsigned int code, str *text, str *body, str *new_header, str *to_tag); #define LOCK_REPLIES(_t) lock(&(_t)->reply_mutex ) #define UNLOCK_REPLIES(_t) unlock(&(_t)->reply_mutex ) /* This function is called whenever a reply for our module is received; * we need to register this function on module initialization; * Returns : 0 - core router stops * 1 - core router relay statelessly */ int reply_received( struct sip_msg *p_msg ) ; /* send a UAS reply * Warning: 'buf' and 'len' should already have been build. * returns 1 if everything was OK or -1 for error */ #ifdef _OBSO int t_reply_light( struct cell *trans, char* buf, unsigned int len, unsigned int code, char * text, char *to_tag, unsigned int to_tag_len); #endif int t_reply_with_body( struct cell *trans, unsigned int code, str *text, str *body, str *new_header, str *to_tag ); /* send a UAS reply * returns 1 if everything was OK or -1 for error */ int t_reply( struct cell *t, struct sip_msg * , unsigned int , str * ); /* the same as t_reply, except it does not claim REPLY_LOCK -- useful to be called within reply processing */ int w_t_reply_body(struct sip_msg* msg,str* code,str *text, str *body); int t_reply_unsafe( struct cell *t, struct sip_msg * , unsigned int , str * ); enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch, unsigned int msg_status, branch_bm_t *cancel_bitmap ); enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch, unsigned int msg_status, branch_bm_t *cancel_bitmap ); void set_final_timer( /* struct s_table *h_table,*/ struct cell *t ); void cleanup_uac_timers( struct cell *t ); void on_negative_reply( struct cell* t, struct sip_msg* msg, int code, void *param ); typedef int (*tget_picked_f)(void); int t_get_picked_branch(); /* set which 'reply' structure to take if only negative replies arrive */ void t_on_negative( unsigned int go_to ); unsigned int get_on_negative(); void t_on_reply( unsigned int go_to ); unsigned int get_on_reply(); /* Retransmits the last sent inbound reply. * Returns -1 - error * 1 - OK */ int t_retransmit_reply( struct cell *t ); void tm_init_tags(); int unixsock_t_reply(str* msg); #endif opensips-2.2.2/modules/tm/t_stats.h000066400000000000000000000046761300170765700173220ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-02-07 initial version (bogdan) * 2006-11-28 modified stats_trans_rpl to track individual message codes * (Jeffrey Magder - SOMA Networks) */ #ifndef _T_STATS_H #define _T_STATS_H #include "../../statistics.h" extern int tm_enable_stats; /* statistic variables */ extern stat_var *tm_rcv_rpls; extern stat_var *tm_rld_rpls; extern stat_var *tm_loc_rpls; extern stat_var *tm_uas_trans; extern stat_var *tm_uac_trans; extern stat_var *tm_trans_2xx; extern stat_var *tm_trans_3xx; extern stat_var *tm_trans_4xx; extern stat_var *tm_trans_5xx; extern stat_var *tm_trans_6xx; extern stat_var *tm_trans_inuse; #ifdef STATISTICS inline static void stats_trans_rpl( int code, int local ) { stat_var *numerical_stat; if (tm_enable_stats) { if (code>=700) { return; } else if (code>=600) { update_stat( tm_trans_6xx, 1); } else if (code>=500) { update_stat( tm_trans_5xx, 1); } else if (code>=400) { update_stat( tm_trans_4xx, 1); } else if (code>=300) { update_stat( tm_trans_3xx, 1); } else if (code>=200) { update_stat( tm_trans_2xx, 1); } if (local) update_stat( tm_loc_rpls, 1); else update_stat( tm_rld_rpls, 1); numerical_stat = get_stat_var_from_num_code(code, 1); /* Increment the status code. */ if (numerical_stat != NULL) update_stat(numerical_stat, 1); } } inline static void stats_trans_new( int local ) { if (tm_enable_stats) { update_stat( tm_trans_inuse, 1 ); if (local) update_stat( tm_uac_trans, 1 ); else update_stat( tm_uas_trans, 1 ); } } #else #define stats_trans_rpl( _code , _local ) #define stats_trans_new( _local ) #endif #endif opensips-2.2.2/modules/tm/timer.c000066400000000000000000000730661300170765700167530ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-06-27 timers are not unlinked if timerlist is 0 (andrei) * 2004-02-13 t->is_invite, t->local, t->noisy_ctimer replaced; * timer_link.payload removed (bogdan) * 2007-02-02 re-transmission timers have milliseconds resolution; * add faster timers (shortcuts based on timeout) (bogdan) */ /* timer.c is where we implement TM timers. It has been designed for high performance using some techniques of which timer users need to be aware. One technique is "fixed-timer-length". We maintain separate timer lists, all of them include elements of the same time to fire. That allows *appending* new events to the list as opposed to inserting them by time, which is costly due to searching time spent in a mutex. The performance benefit is noticeable. The limitation is you need a new timer list for each new timer length. Another technique is the timer process slices off expired elements from the list in a mutex, but executes the timer after the mutex is left. That saves time greatly as whichever process wants to add/remove a timer, it does not have to wait until the current list is processed. However, be aware the timers may hit in a delayed manner; you have no guarantee in your process that after resetting a timer, it will no more hit. It might have been removed by timer process, and is waiting to be executed. The following example shows it: PROCESS1 TIMER PROCESS 0. timer hits, it is removed from queue and about to be executed 1. process1 decides to reset the timer 2. timer is executed now 3. if the process1 naively thinks the timer could not have been executed after resetting the timer, it is WRONG -- it was (step 2.) So be careful when writing the timer handlers. Currently defined timers don't hurt if they hit delayed, I hope at least. Re-transmission timer may results in a useless re-transmission -- not too bad. FR timer not too bad either as timer processing uses a REPLY mutex making it safe to other processing affecting transaction state. Wait timer not bad either -- processes putting a transaction on wait don't do anything with it anymore. Example when it does not hurt: P1 TIMER 0. RETR timer removed from list and scheduled for execution 1. 200/BYE received-> reset RETR, put_on_wait 2. RETR timer executed -- too late but it does not hurt 3. WAIT handler executed The rule of thumb is don't touch data you put under a timer. Create data, put them under a timer, and let them live until they are safely destroyed from wait/delete timer. The only safe place to manipulate the data is from timer process in which delayed timers cannot hit (all timers are processed sequentially). A "bad example" -- rewriting content of re-transmission buffer in an unprotected way is bad because a delayed re-transmission timer might hit. That's why our reply re transmission procedure is enclosed in a REPLY_LOCK. */ #include "config.h" #include "h_table.h" #include "timer.h" #include "../../dprint.h" #include "lock.h" #include "../../hash_func.h" #include "../../dprint.h" #include "../../config.h" #include "../../parser/parser_f.h" #include "../../ut.h" #include "../../context.h" #include "t_funcs.h" #include "t_reply.h" #include "t_cancel.h" static struct timer_table *timertable=0; static unsigned int timer_sets = 0; static struct timer detached_timer; /* just to have a value to compare with*/ #define DETACHED_LIST (&detached_timer) #define is_in_timer_list2(_tl) ( (_tl)->timer_list && \ ((_tl)->timer_list!=DETACHED_LIST) ) int timer_group[NR_OF_TIMER_LISTS] = { TG_FR, TG_FR, TG_WT, TG_DEL, TG_RT, TG_RT, TG_RT, TG_RT }; /* default values of timeouts for all the timer list (see timer.h for enumeration of timer lists) */ unsigned int timer_id2timeout[NR_OF_TIMER_LISTS] = { FR_TIME_OUT, /* FR_TIMER_LIST */ INV_FR_TIME_OUT, /* FR_INV_TIMER_LIST */ WT_TIME_OUT, /* WT_TIMER_LIST */ DEL_TIME_OUT, /* DELETE_LIST */ RETR_T1, /* RT_T1_TO_1 */ 0, /* RT_T1_TO_2 */ 0, /* RT_T1_TO_3 */ RETR_T2 /* RT_T2 */ /* NR_OF_TIMER_LISTS */ }; #define UTIME_TYPE 1 static unsigned int timer_id2type[NR_OF_TIMER_LISTS] = { 0, /* FR_TIMER_LIST */ 0, /* FR_INV_TIMER_LIST */ 0, /* WT_TIMER_LIST */ 0, /* DELETE_LIST */ UTIME_TYPE, /* RT_T1_TO_1 */ UTIME_TYPE, /* RT_T1_TO_2 */ UTIME_TYPE, /* RT_T1_TO_3 */ UTIME_TYPE /* RT_T2 */ /* NR_OF_TIMER_LISTS */ }; /******************** handlers ***************************/ static void unlink_timers( struct cell *t ); static void delete_cell( struct cell *p_cell, int unlock ) { #ifdef EXTRA_DEBUG int i; #endif /* there may still be FR/RETR timers, which have been reset (i.e., time_out==TIMER_DELETED) but are stilled linked to timer lists and must be removed from there before the structures are released */ unlink_timers( p_cell ); #ifdef EXTRA_DEBUG if (is_in_timer_list2(& p_cell->wait_tl )) { LM_ERR("transaction %p scheduled for deletion and still on WAIT," " timeout=%lld\n",p_cell, p_cell->wait_tl.time_out); abort(); } if (is_in_timer_list2(& p_cell->uas.response.retr_timer )) { LM_ERR("transaction %p scheduled for deletion and still on RETR (rep)," "timeout=%lld\n",p_cell, p_cell->uas.response.retr_timer.time_out); abort(); } if (is_in_timer_list2(& p_cell->uas.response.fr_timer )) { LM_ERR("transaction %p scheduled for deletion and still on FR (rep)," " timeout=%lld\n", p_cell,p_cell->uas.response.fr_timer.time_out); abort(); } for (i=0; inr_of_outgoings; i++) { if (is_in_timer_list2(& p_cell->uac[i].request.retr_timer)) { LM_ERR("transaction %p scheduled for deletion and still on RETR " "(req %d), timeout %lld\n", p_cell, i, p_cell->uac[i].request.retr_timer.time_out); abort(); } if (is_in_timer_list2(& p_cell->uac[i].request.fr_timer)) { LM_ERR("transaction %p scheduled for deletion and" " still on FR (req %d), timeout %lld\n", p_cell, i, p_cell->uac[i].request.fr_timer.time_out); abort(); } if (is_in_timer_list2(& p_cell->uac[i].local_cancel.retr_timer)) { LM_ERR("transaction %p scheduled for deletion and" " still on RETR/cancel (req %d), timeout %lld\n", p_cell, i, p_cell->uac[i].request.retr_timer.time_out); abort(); } if (is_in_timer_list2(& p_cell->uac[i].local_cancel.fr_timer)) { LM_ERR("transaction %p scheduled for deletion and" " still on FR/cancel (req %d), timeout %lld\n", p_cell, i, p_cell->uac[i].request.fr_timer.time_out); abort(); } } /* reset_retr_timers( hash__XX_table, p_cell ); */ #endif /* still in use ... don't delete */ if ( IS_REFFED_UNSAFE(p_cell) ) { LM_DBG("delete_cell %p: can't delete -- still reffed (%d)\n", p_cell, p_cell->ref_count); if (unlock) UNLOCK_HASH(p_cell->hash_index); /* set to NULL so that set_timer will work */ p_cell->dele_tl.timer_list= NULL; /* it's added to del list for future del */ set_timer( &(p_cell->dele_tl), DELETE_LIST, 0 ); } else { if (unlock) UNLOCK_HASH(p_cell->hash_index); LM_DBG("delete transaction %p\n", p_cell ); free_cell( p_cell ); } } static void fake_reply(struct cell *t, int branch, int code ) { branch_bm_t cancel_bitmap; short do_cancel_branch; enum rps reply_status; do_cancel_branch = is_invite(t) && should_cancel_branch(t, branch); cancel_bitmap=do_cancel_branch ? 1<my_T->damocles) { LM_ERR("transaction %p scheduled for deletion and" " called from RETR timer\n",r_buf->my_T); abort(); } #endif /* the transaction is already removed from RETRANSMISSION_LIST by timer*/ /* re-transmission */ if ( r_buf->activ_type==TYPE_LOCAL_CANCEL || r_buf->activ_type==TYPE_REQUEST ) { LM_DBG("retransmission_handler : request resending" " (t=%p, %.9s ... )\n", r_buf->my_T, r_buf->buffer.s); set_t(r_buf->my_T); SEND_BUFFER( r_buf ); /*if (SEND_BUFFER( r_buf )==-1) { reset_timer( &r_buf->fr_timer ); fake_reply(r_buf->my_T, r_buf->branch, 503 ); return; }*/ set_t(T_UNDEFINED); } else { LM_DBG("retransmission_handler : reply resending " "(t=%p, %.9s ... )\n", r_buf->my_T, r_buf->buffer.s); set_t(r_buf->my_T); t_retransmit_reply(r_buf->my_T); set_t(T_UNDEFINED); } id = r_buf->retr_list; r_buf->retr_list = id < RT_T2 ? id + 1 : RT_T2; retr_tl->timer_list= NULL; /* set to NULL so that set_timer will work */ set_timer( retr_tl, id < RT_T2 ? id + 1 : RT_T2, 0 ); LM_DBG("retransmission_handler : done\n"); } inline static void final_response_handler( struct timer_link *fr_tl ) { #define CANCEL_REASON_SIP_480 \ "Reason: SIP;cause=480;text=\"NO_ANSWER\"" CRLF static context_p my_ctx = NULL; context_p old_ctx; struct retr_buf* r_buf; struct cell *t; if (fr_tl==0){ /* or BUG?, ignoring it for now */ LM_CRIT("final_response_handler(0) called\n"); return; } r_buf = get_fr_timer_payload(fr_tl); t=r_buf->my_T; # ifdef EXTRA_DEBUG if (t->damocles) { LM_ERR("transaction %p scheduled for deletion and" " called from FR timer\n",r_buf->my_T); abort(); } # endif reset_timer( &(r_buf->retr_timer) ); /* the transaction is already removed from FR_LIST by the timer */ /* FR for local cancels.... */ if (r_buf->activ_type==TYPE_LOCAL_CANCEL) { LM_DBG("stop retr for Local Cancel\n"); return; } /* FR for replies (negative INVITE replies) */ if (r_buf->activ_type>0) { # ifdef EXTRA_DEBUG if (t->uas.request->REQ_METHOD!=METHOD_INVITE || t->uas.status < 200 ) { LM_ERR("unknown type reply buffer\n"); abort(); } # endif put_on_wait( t ); return; }; /* as this processing is outside the scope of other messages (it is trigger from timer), a processing context must be attached to it */ old_ctx = current_processing_ctx; if (my_ctx==NULL) { my_ctx = context_alloc(CONTEXT_GLOBAL); if (my_ctx==NULL) { LM_ERR("failed to alloc new ctx in pkg\n"); } } memset( my_ctx, 0, context_size(CONTEXT_GLOBAL) ); current_processing_ctx = my_ctx; /* set the T context too */ set_t( t ); /* out-of-lock do the cancel I/O */ if (is_invite(t) && should_cancel_branch(t, r_buf->branch) ) { set_cancel_extra_hdrs( CANCEL_REASON_SIP_480, sizeof(CANCEL_REASON_SIP_480)-1); cancel_branch(t, r_buf->branch ); set_cancel_extra_hdrs( NULL, 0); } /* lock reply processing to determine how to proceed reliably */ LOCK_REPLIES( t ); LM_DBG("Cancel sent out, sending 408 (%p)\n", t); fake_reply(t, r_buf->branch, 408 ); /* flush the context */ if (current_processing_ctx==NULL) my_ctx=NULL; else context_destroy(CONTEXT_GLOBAL, my_ctx); /* switch back to the old context */ current_processing_ctx = old_ctx; /* reset the T context */ init_t(); LM_DBG("done\n"); } void cleanup_localcancel_timers( struct cell *t ) { int i; for (i=0; inr_of_outgoings; i++ ) { reset_timer( &t->uac[i].local_cancel.retr_timer ); reset_timer( &t->uac[i].local_cancel.fr_timer ); } } inline static void wait_handler( struct timer_link *wait_tl ) { struct cell *p_cell; p_cell = get_wait_timer_payload( wait_tl ); #ifdef EXTRA_DEBUG if (p_cell->damocles) { LM_ERR("transaction %p scheduled for deletion and" " called from WAIT timer\n",p_cell); abort(); } LM_DBG("WAIT timer hit\n"); #endif /* stop cancel timers if any running */ if ( is_invite(p_cell) ) cleanup_localcancel_timers( p_cell ); /* the transaction is already removed from WT_LIST by the timer */ /* remove the cell from the hash table */ LM_DBG("removing %p from table \n", p_cell ); LOCK_HASH( p_cell->hash_index ); remove_from_hash_table_unsafe( p_cell ); /* jku: no more here -- we do it when we put a transaction on wait */ #ifdef EXTRA_DEBUG p_cell->damocles = 1; #endif /* delete (returns with UNLOCK-ed_HASH) */ delete_cell( p_cell, 1 /* unlock on return */ ); LM_DBG("done\n"); } inline static void delete_handler( struct timer_link *dele_tl ) { struct cell *p_cell; p_cell = get_dele_timer_payload( dele_tl ); LM_DBG("removing %p \n", p_cell ); #ifdef EXTRA_DEBUG if (p_cell->damocles==0) { LM_ERR("transaction %p not scheduled for deletion" " and called from DELETE timer\n",p_cell); abort(); } #endif /* we call delete now without any locking on hash/ref_count; we can do that because delete_handler is only entered after the delete timer was installed from wait_handler, which removed transaction from hash table and did not destroy it because some processes were using it; that means that the processes currently using the transaction can unref and no new processes can ref -- we can wait until ref_count is zero safely without locking */ delete_cell( p_cell, 0 /* don't unlock on return */ ); LM_DBG("done\n"); } /***********************************************************/ struct timer_table *get_timertable(void) { return timertable; } void unlink_timer_lists(void) { struct timer_link *tl, *end, *tmp; enum lists i; unsigned int set; if (timertable==0) return; /* nothing to do */ for ( set=0 ; setnext_tl; free_cell( get_dele_timer_payload(tl) ); tl=tmp; } } } struct timer_table *tm_init_timers( unsigned int sets ) { enum lists i; unsigned int set; LM_DBG("creating %d parallel timer structures\n", timer_sets); timertable = (struct timer_table *)shm_malloc ( sets * sizeof(struct timer_table)); if (!timertable) { LM_ERR("no more share memory\n"); goto error0; } memset(timertable, 0, sets * sizeof(struct timer_table)); timer_sets = sets; /* check the timeout values */ if ( timer_id2timeout[FR_TIMER_LIST]timers[i] ); for ( i=0 ; ifirst_tl.next_tl; while (tl!=& timer_list->last_tl) { LM_DBG("[%d]: %p, next=%p \n", list_id, tl, tl->next_tl); tl = tl->next_tl; } } #ifdef TM_TIMER_DEBUG static void check_timer_list( struct timer* timer_list, char *txt) { struct timer_link *tl ; struct timer_link *tl1 ; if (list_id<0 || list_id>=NR_OF_TIMER_LISTS) { LM_CRIT("TM TIMER list [%d] bug [%s]\n",timer_list->id, txt); abort(); } tl = timer_list->last_tl.prev_tl; while (tl!=&timer_list->first_tl) { if (tl->prev_tl==0) { LM_CRIT("TM TIMER list [%d] prev_tl==0 [%s]\n",timer_list->id, txt); abort(); } tl = tl->prev_tl; } tl = timer_list->first_tl.next_tl; while (tl!=&timer_list->last_tl) { if (tl->next_tl==0) { LM_CRIT("TM TIMER list [%d] next_tl==0 [%s]\n",timer_list->id, txt); abort(); } tl = tl->next_tl; } tl = timer_list->first_tl.next_tl; while (tl!=&timer_list->last_tl) { if (tl->ld_tl==0) { LM_CRIT("TM TIMER list [%d] corrupted - ld=0 [%s]\n", timer_list->id, txt); abort(); } if (tl->ld_tl->ld_tl!=tl) { LM_CRIT("TM TIMER list [%d] corrupted - ld cycle broken [%s]\n", timer_list->id, txt); abort(); } if (tl->ld_tl!=tl) { tl1 = tl->next_tl; while(tl1!=tl->ld_tl) { if (tl1->ld_tl) { LM_CRIT("TM TIMER list [%d] corrupted - ld!=0 inside " "cycle [%s]\n", timer_list->id, txt); abort(); } tl1 = tl1->next_tl; } } tl = tl->ld_tl->next_tl; } } #endif static void remove_timer_unsafe( struct timer_link* tl ) { #ifdef EXTRA_DEBUG if (tl && is_in_timer_list2(tl) && tl->timer_list->last_tl.prev_tl==0) { LM_CRIT("Oh no, zero link in trailing timer element\n"); abort(); }; #endif if (is_in_timer_list2( tl )) { #ifdef EXTRA_DEBUG LM_DBG("unlinking timer: tl=%p, timeout=%lld, group=%d\n", tl, tl->time_out, tl->tg); #endif #ifdef TM_TIMER_DEBUG check_timer_list( tl->timer_list, "before remove" ); #endif if (tl->ld_tl && tl->ld_tl!=tl) { if (tl->time_out==tl->prev_tl->time_out) { tl->prev_tl->ld_tl = tl->ld_tl; tl->ld_tl->ld_tl = tl->prev_tl; } else { tl->next_tl->ld_tl = tl->ld_tl; tl->ld_tl->ld_tl = tl->next_tl; } } tl->prev_tl->next_tl = tl->next_tl; tl->next_tl->prev_tl = tl->prev_tl; #ifdef TM_TIMER_DEBUG check_timer_list( tl->timer_list, "after remove" ); #endif tl->next_tl = 0; tl->prev_tl = 0; tl->ld_tl = 0; tl->timer_list = NULL; } } /* put a new linker into a timer_list */ static void insert_timer_unsafe( struct timer *timer_list, struct timer_link *tl, utime_t time_out ) { struct timer_link* ptr; tl->time_out = time_out; tl->timer_list = timer_list; tl->deleted = 0; #ifdef TM_TIMER_DEBUG check_timer_list( timer_list, "before insert" ); #endif ptr = timer_list->last_tl.prev_tl; for( ; ptr != &timer_list->first_tl ; ptr = ptr->ld_tl->prev_tl) { if ( ptr->time_out<=time_out ) break; } /* insert "tl" after "ptr" */ tl->prev_tl = ptr; tl->next_tl = ptr->next_tl; tl->prev_tl->next_tl = tl; tl->next_tl->prev_tl = tl; if (tl->time_out==ptr->time_out) { tl->ld_tl = ptr->ld_tl; ptr->ld_tl = 0; tl->ld_tl->ld_tl = tl; } else { tl->ld_tl = tl; } #ifdef TM_TIMER_DEBUG check_timer_list( timer_list, "after insert" ); #endif LM_DBG("[%d]: %p (%lld)\n",timer_list->id, tl,tl->time_out); } /* detach items passed by the time from timer list */ static struct timer_link *check_and_split_time_list( struct timer *timer_list, utime_t time ) { struct timer_link *tl , *end, *ret; /* quick check whether it is worth entering the lock */ if (timer_list->first_tl.next_tl==&timer_list->last_tl || ( /* timer_list->first_tl.next_tl && */ timer_list->first_tl.next_tl->time_out > time) ) return NULL; /* the entire timer list is locked now -- no one else can manipulate it */ lock(timer_list->mutex); #ifdef TM_TIMER_DEBUG check_timer_list( timer_list, "before split" ); #endif end = &timer_list->last_tl; tl = timer_list->first_tl.next_tl; while( tl!=end && tl->time_out <= time) tl=tl->ld_tl->next_tl; /* nothing to delete found */ if (tl->prev_tl==&(timer_list->first_tl)) { ret = NULL; } else { /* we did find timers to be fired! */ /* the detached list begins with current beginning */ ret = timer_list->first_tl.next_tl; /* and we mark the end of the split list */ tl->prev_tl->next_tl = NULL; /* the shortened list starts from where we suspended */ timer_list->first_tl.next_tl = tl; tl->prev_tl = & timer_list->first_tl; for( tl=ret ; tl ; tl=tl->next_tl ) tl->timer_list = DETACHED_LIST; } #ifdef TM_TIMER_DEBUG check_timer_list( timer_list, "after split" ); #endif #ifdef EXTRA_DEBUG if (timer_list->last_tl.prev_tl==0) { LM_CRIT("Oh no, zero link in trailing timer element\n"); abort(); }; #endif /* give the list lock away */ unlock(timer_list->mutex); return ret; } /* stop timer * WARNING: a reset'ed timer will be lost forever * (successive set_timer won't work unless you're lucky * an catch the race condition, the idea here is there is no * guarantee you can do anything after a timer_reset)*/ void reset_timer( struct timer_link* tl ) { /* disqualify this timer from execution by setting its time_out to zero; it will stay in timer-list until the timer process starts removing outdated elements; then it will remove it but not execute; there is a race condition, though -- see timer.c for more details */ tl->deleted = 1; #ifdef EXTRA_DEBUG LM_DBG("(group %d, tl=%p)\n", tl->tg, tl ); #endif } /* determine timer length and put on a correct timer list * WARNING: - don't try to use it to "move" a timer from one list * to another, you'll run into races * - reset_timer; set_timer might not work, a reset'ed timer * has no set_timer guarantee, it might be lost; * same for an expired timer: only it's handler can * set it again, an external set_timer has no guarantee */ void set_timer( struct timer_link *new_tl, enum lists list_id, utime_t* ext_timeout ) { utime_t timeout; struct timer* list; if (list_id>=NR_OF_TIMER_LISTS) { LM_CRIT("unknown list: %d\n", list_id); #ifdef EXTRA_DEBUG abort(); #endif return; } if (!ext_timeout) { timeout = timer_id2timeout[ list_id ]; } else { timeout = *ext_timeout; } LM_DBG("relative timeout is %lld\n",timeout); list= &(timertable[new_tl->set].timers[ list_id ]); lock(list->mutex); /* check first if we are on the "detached" timer_routine list, * if so do nothing, the timer is not valid anymore * (side effect: reset_timer ; set_timer is not safe, a reseted timer * might be lost, depending on this race condition ) */ if (new_tl->timer_list==DETACHED_LIST){ LM_CRIT("set_timer for %d list called on a \"detached\" " "timer -- ignoring: %p\n", list_id, new_tl); goto end; } /* make sure I'm not already on a list */ remove_timer_unsafe( new_tl ); insert_timer_unsafe( list, new_tl, timeout + ((timer_id2type[list_id]==UTIME_TYPE)?get_uticks():get_ticks())); end: unlock(list->mutex); } /* similar to set_timer, except it allows only one-time timer setting and all later attempts are ignored */ void set_1timer( struct timer_link *new_tl, enum lists list_id, utime_t* ext_timeout ) { utime_t timeout; struct timer* list; if (list_id>=NR_OF_TIMER_LISTS) { LM_CRIT("unknown list: %d\n", list_id); #ifdef EXTRA_DEBUG abort(); #endif return; } if (!ext_timeout) { timeout = timer_id2timeout[ list_id ]; } else { timeout = *ext_timeout; } list= &(timertable[new_tl->set].timers[ list_id ]); lock(list->mutex); if (!new_tl->time_out) { insert_timer_unsafe( list, new_tl, timeout + ((timer_id2type[list_id]==UTIME_TYPE)?get_uticks():get_ticks())); } unlock(list->mutex); } /* should be called only from timer process context, * else it's unsafe */ static void unlink_timers( struct cell *t ) { int i; int remove_fr, remove_retr; unsigned short set; remove_fr=0; remove_retr=0; /* first look if we need to remove timers and play with costly locks at all note that is_in_timer_list2 is unsafe but it does not hurt -- transaction is already dead (wait state) so that no one else will install a FR/RETR timer and it can only be removed from timer process itself -> it is safe to use it without any protection */ if (is_in_timer_list2(&t->uas.response.fr_timer)) remove_fr=1; else for (i=0; inr_of_outgoings; i++) if (is_in_timer_list2(&t->uac[i].request.fr_timer) || is_in_timer_list2(&t->uac[i].local_cancel.fr_timer)) { remove_fr=1; break; } if (is_in_timer_list2(&t->uas.response.retr_timer)) remove_retr=1; else for (i=0; inr_of_outgoings; i++) if (is_in_timer_list2(&t->uac[i].request.retr_timer) || is_in_timer_list2(&t->uac[i].local_cancel.retr_timer)) { remove_retr=1; break; } set = t->wait_tl.set; /* do what we have to do....*/ if (remove_retr) { /* RT_T1 lock is shared by all other RT timer lists -- we can safely lock just one */ lock(timertable[set].timers[RT_T1_TO_1].mutex); remove_timer_unsafe(&t->uas.response.retr_timer); for (i=0; inr_of_outgoings; i++) { remove_timer_unsafe(&t->uac[i].request.retr_timer); remove_timer_unsafe(&t->uac[i].local_cancel.retr_timer); } unlock(timertable[set].timers[RT_T1_TO_1].mutex); } if (remove_fr) { /* FR lock is shared by all other FR timer lists -- we can safely lock just one */ lock(timertable[set].timers[FR_TIMER_LIST].mutex); remove_timer_unsafe(&t->uas.response.fr_timer); for (i=0; inr_of_outgoings; i++) { remove_timer_unsafe(&t->uac[i].request.fr_timer); remove_timer_unsafe(&t->uac[i].local_cancel.fr_timer); } unlock(timertable[set].timers[FR_TIMER_LIST].mutex); } } #define run_handler_for_each( _tl , _handler ) \ while ((_tl))\ {\ /* reset the timer list linkage */\ tmp_tl = (_tl)->next_tl;\ (_tl)->next_tl = (_tl)->prev_tl = 0;\ LM_DBG("timer routine:%d,tl=%p next=%p, timeout=%lld\n",\ id,(_tl),tmp_tl,(_tl)->time_out);\ if ( !(_tl)->deleted ) \ (_handler)( _tl );\ (_tl) = tmp_tl;\ } void timer_routine(unsigned int ticks , void *set) { struct timer_link *tl, *tmp_tl; int id; lock_start_write( timertable[(long)set].ex_lock ); for( id=0 ; id forwards to uri (bogdan) * 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri) * 2004-02-18 t_reply exported via FIFO - imported from VM (bogdan) * 2004-10-01 added a new param.: restart_fr_on_each_reply (andrei) * 2005-05-30 light version of tm_load - find_export dropped -> module * interface dosen't need to export internal functions (bogdan) * 2006-01-15 merged functions which diff only via proto (like t_relay, * t_replicate and t_forward_nonack) (bogdan) * 2007-01-25 t_forward_nonack removed as it merged into t_relay, * t_replicate also accepts flags for controlling DNS failover * (bogdan) * 2008-04-04 added support for local and remote dispaly name in TM dialogs * (by Andrei Pisau ) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../script_cb.h" #include "../../mi/mi.h" #include "../../usr_avp.h" #include "../../mem/mem.h" #include "../../pvar.h" #include "../../mod_fix.h" #include "sip_msg.h" #include "h_table.h" #include "ut.h" #include "t_reply.h" #include "t_fwd.h" #include "t_lookup.h" #include "callid.h" #include "t_cancel.h" #include "t_fifo.h" #include "mi.h" #include "tm_load.h" #include "t_ctx.h" #include "async.h" /* item functions */ static int pv_get_tm_branch_idx(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int pv_get_tm_reply_code(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int pv_get_tm_ruri(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); /* TODO: remove in future versions (deprecated parameters) */ int __set_fr_timer(modparam_t type, void* val); int __set_fr_inv_timer(modparam_t type, void* val); /* fixup functions */ static int fixup_t_send_reply(void** param, int param_no); static int fixup_local_replied(void** param, int param_no); static int fixup_t_relay1(void** param, int param_no); static int fixup_t_relay2(void** param, int param_no); static int fixup_t_replicate(void** param, int param_no); static int fixup_cancel_branch(void** param, int param_no); static int fixup_froute(void** param, int param_no); static int fixup_rroute(void** param, int param_no); static int fixup_broute(void** param, int param_no); static int fixup_t_new_request(void** param, int param_no); /* init functions */ static int mod_init(void); static int child_init(int rank); /* exported functions */ inline static int w_t_newtran(struct sip_msg* p_msg); inline static int w_t_reply(struct sip_msg *msg, char* code, char* text); inline static int w_pv_t_reply(struct sip_msg *msg, char* code, char* text); inline static int w_t_relay(struct sip_msg *p_msg , char *proxy, char* flags); inline static int w_t_replicate(struct sip_msg *p_msg, char *dst,char* ); inline static int w_t_on_negative(struct sip_msg* msg, char *go_to); inline static int w_t_on_reply(struct sip_msg* msg, char *go_to); inline static int w_t_on_branch(struct sip_msg* msg, char *go_to); inline static int t_check_status(struct sip_msg* msg, char *regexp); inline static int t_flush_flags(struct sip_msg* msg); inline static int t_local_replied(struct sip_msg* msg, char *type); inline static int t_check_trans(struct sip_msg* msg); inline static int t_was_cancelled(struct sip_msg* msg); inline static int w_t_cancel_branch(struct sip_msg* msg, char *sflags ); inline static int w_t_add_hdrs(struct sip_msg* msg, char *val ); int t_cancel_trans(struct cell *t, str *hdrs); inline static int w_t_new_request(struct sip_msg* msg, char*, char*, char*, char*, char*, char*); struct sip_msg* tm_pv_context_request(struct sip_msg* msg); struct sip_msg* tm_pv_context_reply(struct sip_msg* msg); /* these values are used when the transaction has not been defined yet */ int fr_timeout; int fr_inv_timeout; #define TM_CANCEL_BRANCH_ALL (1<<0) #define TM_CANCEL_BRANCH_OTHERS (1<<1) #define PV_FIELD_DELIM ", " #define PV_FIELD_DELIM_LEN (sizeof(PV_FIELD_DELIM) - 1) #define PV_LOCAL_BUF_SIZE 511 static char pv_local_buf[PV_LOCAL_BUF_SIZE+1]; static str uac_ctx_avp = str_init("uac_ctx"); static int uac_ctx_avp_id; int pv_get_tm_branch_avp(struct sip_msg*, pv_param_t*, pv_value_t*); int pv_set_tm_branch_avp(struct sip_msg*, pv_param_t*, int, pv_value_t*); int pv_get_tm_fr_timeout(struct sip_msg*, pv_param_t *, pv_value_t*); int pv_set_tm_fr_timeout(struct sip_msg*, pv_param_t *, int, pv_value_t*); int pv_get_tm_fr_inv_timeout(struct sip_msg*, pv_param_t *, pv_value_t*); int pv_set_tm_fr_inv_timeout(struct sip_msg*, pv_param_t *, int, pv_value_t*); struct usr_avp** get_bavp_list(void); /* module parameteres */ int tm_enable_stats = 1; static int timer_partitions = 1; /* statistic variables */ stat_var *tm_rcv_rpls; stat_var *tm_rld_rpls; stat_var *tm_loc_rpls; stat_var *tm_uas_trans; stat_var *tm_uac_trans; stat_var *tm_trans_2xx; stat_var *tm_trans_3xx; stat_var *tm_trans_4xx; stat_var *tm_trans_5xx; stat_var *tm_trans_6xx; stat_var *tm_trans_inuse; static cmd_export_t cmds[]={ {"t_newtran", (cmd_function)w_t_newtran, 0, 0, 0, REQUEST_ROUTE}, {"t_reply", (cmd_function)w_pv_t_reply, 2, fixup_t_send_reply, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"t_replicate", (cmd_function)w_t_replicate, 1, fixup_t_replicate, 0, REQUEST_ROUTE}, {"t_replicate", (cmd_function)w_t_replicate, 2, fixup_t_replicate, 0, REQUEST_ROUTE}, {"t_relay", (cmd_function)w_t_relay, 0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"t_relay", (cmd_function)w_t_relay, 1, fixup_t_relay1, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"t_relay", (cmd_function)w_t_relay, 2, fixup_t_relay2, 0, REQUEST_ROUTE | FAILURE_ROUTE }, {"t_on_failure", (cmd_function)w_t_on_negative, 1, fixup_froute, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"t_on_reply", (cmd_function)w_t_on_reply, 1, fixup_rroute, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"t_on_branch", (cmd_function)w_t_on_branch, 1, fixup_broute, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, {"t_check_status", (cmd_function)t_check_status, 1, fixup_regexp_null, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, {"t_write_req", (cmd_function)t_write_req, 2, fixup_t_write, 0, REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE }, {"t_write_unix", (cmd_function)t_write_unix, 2, fixup_t_write, 0, REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE }, {"t_flush_flags", (cmd_function)t_flush_flags, 0, 0, 0, REQUEST_ROUTE | BRANCH_ROUTE }, {"t_local_replied", (cmd_function)t_local_replied, 1, fixup_local_replied, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE }, {"t_check_trans", (cmd_function)t_check_trans, 0, 0, 0, REQUEST_ROUTE | BRANCH_ROUTE }, {"t_was_cancelled", (cmd_function)t_was_cancelled, 0, 0, 0, FAILURE_ROUTE | ONREPLY_ROUTE }, {"t_cancel_branch", (cmd_function)w_t_cancel_branch,0, 0, 0, ONREPLY_ROUTE }, {"t_cancel_branch", (cmd_function)w_t_cancel_branch,1, fixup_cancel_branch, 0, ONREPLY_ROUTE }, {"t_add_hdrs", (cmd_function)w_t_add_hdrs, 1, fixup_spve_null, 0, REQUEST_ROUTE }, {"t_reply_with_body",(cmd_function)w_t_reply_body, 3,fixup_t_send_reply, 0, REQUEST_ROUTE }, {"t_new_request", (cmd_function)w_t_new_request, 4, fixup_t_new_request, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"t_new_request", (cmd_function)w_t_new_request, 5, fixup_t_new_request, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"t_new_request", (cmd_function)w_t_new_request, 6, fixup_t_new_request, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"load_tm", (cmd_function)load_tm, 0, 0, 0, 0}, {0,0,0,0,0,0} }; static param_export_t params[]={ {"ruri_matching", INT_PARAM, &ruri_matching}, {"via1_matching", INT_PARAM, &via1_matching}, {"fr_timeout", INT_PARAM, &(timer_id2timeout[FR_TIMER_LIST])}, {"fr_inv_timeout", INT_PARAM, &(timer_id2timeout[FR_INV_TIMER_LIST])}, {"fr_timer", INT_PARAM|USE_FUNC_PARAM, __set_fr_timer}, {"fr_inv_timer", INT_PARAM|USE_FUNC_PARAM, __set_fr_inv_timer}, {"wt_timer", INT_PARAM, &(timer_id2timeout[WT_TIMER_LIST])}, {"delete_timer", INT_PARAM, &(timer_id2timeout[DELETE_LIST])}, {"T1_timer", INT_PARAM, &(timer_id2timeout[RT_T1_TO_1])}, {"T2_timer", INT_PARAM, &(timer_id2timeout[RT_T2])}, {"unix_tx_timeout", INT_PARAM, &tm_unix_tx_timeout}, {"restart_fr_on_each_reply", INT_PARAM, &restart_fr_on_each_reply}, {"tw_append", STR_PARAM|USE_FUNC_PARAM, (void*)parse_tw_append }, { "enable_stats", INT_PARAM, &tm_enable_stats }, { "pass_provisional_replies", INT_PARAM, &pass_provisional_replies }, { "syn_branch", INT_PARAM, &syn_branch }, { "onreply_avp_mode", INT_PARAM, &onreply_avp_mode }, { "disable_6xx_block", INT_PARAM, &disable_6xx_block }, { "minor_branch_flag", STR_PARAM, &minor_branch_flag_str }, { "minor_branch_flag", INT_PARAM, &minor_branch_flag }, { "timer_partitions", INT_PARAM, &timer_partitions }, { "auto_100trying", INT_PARAM, &auto_100trying }, {0,0,0} }; static stat_export_t mod_stats[] = { {"received_replies" , 0, &tm_rcv_rpls }, {"relayed_replies" , 0, &tm_rld_rpls }, {"local_replies" , 0, &tm_loc_rpls }, {"UAS_transactions" , 0, &tm_uas_trans }, {"UAC_transactions" , 0, &tm_uac_trans }, {"2xx_transactions" , 0, &tm_trans_2xx }, {"3xx_transactions" , 0, &tm_trans_3xx }, {"4xx_transactions" , 0, &tm_trans_4xx }, {"5xx_transactions" , 0, &tm_trans_5xx }, {"6xx_transactions" , 0, &tm_trans_6xx }, {"inuse_transactions" , STAT_NO_RESET, &tm_trans_inuse }, {0,0,0} }; /** * pseudo-variables exported by TM module */ static pv_export_t mod_items[] = { { {"T_branch_idx", sizeof("T_branch_idx")-1}, 900, pv_get_tm_branch_idx, 0, 0, 0, 0, 0 }, { {"T_reply_code", sizeof("T_reply_code")-1}, 901, pv_get_tm_reply_code, 0, 0, 0, 0, 0 }, { {"T_ruri", sizeof("T_ruri")-1}, 902, pv_get_tm_ruri, 0, 0, 0, 0, 0 }, { {"bavp", sizeof("bavp")-1}, 903, pv_get_tm_branch_avp, pv_set_tm_branch_avp, pv_parse_avp_name, pv_parse_index, 0, 0 }, { {"T_fr_timeout", sizeof("T_fr_timeout")-1}, 904, pv_get_tm_fr_timeout, pv_set_tm_fr_timeout, 0, 0, 0, 0 }, { {"T_fr_inv_timeout", sizeof("T_fr_inv_timeout")-1}, 905, pv_get_tm_fr_inv_timeout, pv_set_tm_fr_inv_timeout, 0, 0, 0, 0 }, { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } }; static mi_export_t mi_cmds [] = { {MI_TM_UAC, 0, mi_tm_uac_dlg, MI_ASYNC_RPL_FLAG, 0, 0 }, {MI_TM_CANCEL, 0, mi_tm_cancel, 0, 0, 0 }, {MI_TM_HASH, 0, mi_tm_hash, MI_NO_INPUT_FLAG, 0, 0 }, {MI_TM_REPLY, 0, mi_tm_reply, 0, 0, 0 }, {0,0,0,0,0,0} }; #ifdef STATIC_TM struct module_exports tm_exports = { #else struct module_exports exports= { #endif "tm", /* module name*/ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ cmds, /* exported functions */ NULL, /* exported async functions */ params, /* exported variables */ mod_stats, /* exported statistics */ mi_cmds, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ (response_function) reply_received, (destroy_function) tm_shutdown, child_init /* per-child init function */ }; /**************************** fixup functions ******************************/ static int fixup_froute(void** param, int param_no) { int rt; rt = get_script_route_ID_by_name( (char *)*param, failure_rlist, FAILURE_RT_NO); if (rt==-1) { LM_ERR("failure route <%s> does not exist\n",(char *)*param); return -1; } pkg_free(*param); *param = (void*)(unsigned long int)rt; return 0; } static int fixup_rroute(void** param, int param_no) { int rt; rt = get_script_route_ID_by_name( (char *)*param, onreply_rlist, ONREPLY_RT_NO); if (rt==-1) { LM_ERR("onreply route <%s> does not exist\n",(char *)*param); return -1; } pkg_free(*param); *param = (void*)(unsigned long int)rt; return 0; } static int fixup_broute(void** param, int param_no) { int rt; rt = get_script_route_ID_by_name( (char *)*param, branch_rlist, BRANCH_RT_NO); if (rt==-1) { LM_ERR("branch route <%s> does not exist\n",(char *)*param); return -1; } pkg_free(*param); *param = (void*)(unsigned long int)rt; return 0; } static int flag_fixup(void** param, int param_no) { unsigned int flags; str s; if (param_no == 1) { s.s = (char*)*param; s.len = strlen(s.s); flags = 0; if ( strno2int(&s, &flags )<0 ) { return -1; } pkg_free(*param); *param = (void*)(unsigned long int)(flags<<1); } return 0; } static int fixup_t_replicate(void** param, int param_no) { str s; pv_elem_t *model; if (param_no == 1) { s.s = (char*)*param; s.len = strlen(s.s); model = NULL; if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for param no %d!\n", s.s, param_no); return E_CFG; } *param = (void*)model; } else { /* flags */ if (flag_fixup( param, 1)!=0) { LM_ERR("bad flags <%s>\n", (char *)(*param)); return E_CFG; } } return 0; } static int fixup_phostport2proxy(void** param, int param_no) { struct proxy_l *proxy; char *s; int port; int proto; str host; if (param_no!=1) { LM_CRIT("called with more than one parameter\n"); return E_BUG; } s = (char *) (*param); if (s==0 || *s==0) { LM_CRIT("empty parameter\n"); return E_UNSPEC; } if (parse_phostport( s, strlen(s), &host.s, &host.len, &port, &proto)!=0){ LM_CRIT("invalid parameter <%s>\n",s); return E_UNSPEC; } proxy = mk_proxy( &host, port, proto, 0); if (proxy==0) { LM_ERR("failed to resolve <%.*s>\n", host.len, host.s ); return ser_error; } *(param)=proxy; return 0; } static int fixup_t_relay1(void** param, int param_no) { if (flag_fixup( param, 1)==0) { /* param is flag -> move it as second param */ *((void**)(((char*)param)+sizeof(action_elem_t))) = *param; *param = 0; return 0; } else if (fixup_phostport2proxy( param, 1)==0 ) { /* param is OBP -> nothing else to do */ return 0; } else { LM_ERR("param is neither flag, nor OBP <%s>\n",(char *)(*param)); return E_CFG; } } static int fixup_t_relay2(void** param, int param_no) { if (param_no==1) { return fixup_phostport2proxy( param, param_no); } else if (param_no==2) { if (flag_fixup( param, 1)!=0) { LM_ERR("bad flags <%s>\n", (char *)(*param)); return E_CFG; } } return 0; } static int fixup_t_send_reply(void** param, int param_no) { pv_elem_t *model=NULL; str s; /* convert to str */ s.s = (char*)*param; s.len = strlen(s.s); if (s.len==0) { LM_ERR("param no. %d is empty!\n", param_no); return E_CFG; } model=NULL; if (param_no>0 && param_no<4) { if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for param no %d!\n", s.s, param_no); return E_CFG; } if(model->spec.getf==NULL && param_no==1) { if(str2int(&s, (unsigned int*)&model->spec.pvp.pvn.u.isname.name.n)!=0 || model->spec.pvp.pvn.u.isname.name.n<100 || model->spec.pvp.pvn.u.isname.name.n>699) { LM_ERR("wrong value [%s] for param no %d! - Allowed only" " 1xx - 6xx \n", s.s, param_no); return E_CFG; } } *param = (void*)model; } return 0; } static int fixup_local_replied(void** param, int param_no) { char *val; int n = 0; if (param_no==1) { val = (char*)*param; if (strcasecmp(val,"all")==0) { n = 0; } else if (strcasecmp(val,"branch")==0) { n = 1; } else if (strcasecmp(val,"last")==0) { n = 2; } else { LM_ERR("invalid param \"%s\"\n", val); return E_CFG; } /* free string */ pkg_free(*param); /* replace it with the compiled re */ *param=(void*)(long)n; } else { LM_ERR("called with parameter != 1\n"); return E_BUG; } return 0; } static int fixup_cancel_branch(void** param, int param_no) { char *c; unsigned int flags; c = (char*)*param; flags = 0; while (*c) { switch (*c) { case 'a': case 'A': flags |= TM_CANCEL_BRANCH_ALL; break; case 'o': case 'O': flags |= TM_CANCEL_BRANCH_OTHERS; break; default: LM_ERR("unsupported flag '%c'\n",*c); return -1; } c++; } pkg_free(*param); *param = (void*)(unsigned long)flags; return 0; } static int fixup_t_new_request(void** param, int param_no) { /* static string or pv-format for all parameters */ return fixup_spve(param); } /***************************** init functions *****************************/ int load_tm( struct tm_binds *tmb) { tmb->register_tmcb = register_tmcb; /* relay function */ tmb->t_relay = (cmd_function)w_t_relay; /* reply functions */ tmb->t_reply = (treply_f)w_t_reply; tmb->t_reply_with_body = t_reply_with_body; /* transaction location/status functions */ tmb->t_newtran = w_t_newtran; tmb->t_is_local = t_is_local; tmb->t_check_trans = (cmd_function)t_check_trans; tmb->t_get_trans_ident = t_get_trans_ident; tmb->t_lookup_ident = t_lookup_ident; tmb->t_gett = get_t; tmb->t_get_e2eackt = get_e2eack_t; tmb->t_get_picked = t_get_picked_branch; tmb->t_lookup_original_t = t_lookupOriginalT; tmb->unref_cell = t_unref_cell; tmb->ref_cell = t_ref_cell; tmb->t_setkr = set_kr; tmb->t_cancel_trans = t_cancel_trans; /* tm uac functions */ tmb->t_addblind = add_blind_uac; tmb->t_request_within = req_within; tmb->t_request_outside = req_outside; tmb->t_request = request; tmb->new_dlg_uac = new_dlg_uac; tmb->new_auto_dlg_uac = new_auto_dlg_uac; tmb->dlg_add_extra = dlg_add_extra; tmb->dlg_response_uac = dlg_response_uac; tmb->new_dlg_uas = new_dlg_uas; tmb->dlg_request_uas = dlg_request_uas; tmb->free_dlg = free_dlg; tmb->print_dlg = print_dlg; tmb->setlocalTholder = setlocalTholder; tmb->get_branch_index = get_branch_index; /* tm context functions */ tmb->t_ctx_register_int = t_ctx_register_int; tmb->t_ctx_register_str = t_ctx_register_str; tmb->t_ctx_register_ptr = t_ctx_register_ptr; tmb->t_ctx_put_int = t_ctx_put_int; tmb->t_ctx_put_str = t_ctx_put_str; tmb->t_ctx_put_ptr = t_ctx_put_ptr; tmb->t_ctx_get_int = t_ctx_get_int; tmb->t_ctx_get_str = t_ctx_get_str; tmb->t_ctx_get_ptr = t_ctx_get_ptr; return 1; } static int do_t_cleanup( struct sip_msg *foo, void *bar) { struct cell *t; empty_tmcb_list(&tmcb_pending_hl); t = get_cancelled_t(); if (t!=NULL && t!=T_UNDEFINED) t_unref_cell(t); t = get_e2eack_t(); if (t!=NULL && t!=T_UNDEFINED) t_unref_cell(t); reset_e2eack_t(); return t_unref(foo) == 0 ? SCB_DROP_MSG : SCB_RUN_ALL; } static int script_init( struct sip_msg *foo, void *bar) { /* we primarily reset all private memory here to make sure * private values left over from previous message will * not be used again */ set_t(T_UNDEFINED); reset_cancelled_t(); reset_e2eack_t(); fr_timeout = timer_id2timeout[FR_TIMER_LIST]; fr_inv_timeout = timer_id2timeout[FR_INV_TIMER_LIST]; /* reset the kill reason status */ reset_kr(); /* reset the static holders for T routes */ t_on_negative( 0 ); t_on_reply(0); t_on_branch(0); return SCB_RUN_ALL; } static int mod_init(void) { unsigned int timer_sets,set; unsigned int roundto_init; LM_INFO("TM - initializing...\n"); /* checking if we have sufficient bitmap capacity for given maximum number of branches */ if (MAX_BRANCHES+1>31) { LM_CRIT("Too many max UACs for UAC branch_bm_t bitmap: %d\n", MAX_BRANCHES ); return -1; } fix_flag_name(minor_branch_flag_str, minor_branch_flag); minor_branch_flag = get_flag_id_by_name(FLAG_TYPE_BRANCH, minor_branch_flag_str); if (minor_branch_flag!=-1) { if (minor_branch_flag > (8*sizeof(int)-1)) { LM_CRIT("invalid minor branch flag\n"); return -1; } minor_branch_flag = 1<
ABYSS Web Server version "SERVER_VERSION"
© Moez Mahfoudh - 2000

" #define SERVER_PLAIN_INFO CRLF"--------------------------------------------------------------------------------"\ CRLF"ABYSS Web Server version "SERVER_VERSION CRLF"(C) Moez Mahfoudh - 2000" /********************************************************************* ** General purpose definitions *********************************************************************/ #ifdef ABYSS_WIN32 #define strcasecmp(a,b) stricmp((a),(b)) #else #define ioctlsocket(a,b,c) ioctl((a),(b),(c)) #endif /* ABYSS_WIN32 */ #ifndef NULL #define NULL ((void *)0) #endif /* NULL */ #ifndef TRUE #define TRUE 1 #endif /* TRUE */ #ifndef FALSE #define FALSE 0 #endif /* FALSE */ #ifdef ABYSS_WIN32 #define LBR "\n" #else #define LBR "\n" #endif /* ABYSS_WIN32 */ /********************************************************************* ** Typedefs *********************************************************************/ typedef unsigned long uint64; typedef long int64; typedef unsigned int uint32; typedef int int32; typedef unsigned short uint16; typedef short int16; typedef unsigned char byte; typedef unsigned char uint8; typedef char int8; #ifndef __cplusplus typedef int bool; #endif /********************************************************************* ** Buffer *********************************************************************/ typedef struct { void *data; uint32 size; uint32 staticid; } TBuffer; bool BufferAlloc(TBuffer *buf,uint32 memsize); bool BufferRealloc(TBuffer *buf,uint32 memsize); void BufferFree(TBuffer *buf); /********************************************************************* ** String *********************************************************************/ typedef struct { TBuffer buffer; uint32 size; } TString; bool StringAlloc(TString *s); bool StringConcat(TString *s,char *s2); bool StringBlockConcat(TString *s,char *s2,char **ref); void StringFree(TString *s); char *StringData(TString *s); /********************************************************************* ** List *********************************************************************/ typedef struct { void **item; uint16 size,maxsize; bool autofree; } TList; void ListInit(TList *sl); void ListInitAutoFree(TList *sl); void ListFree(TList *sl); bool ListAdd(TList *sl,void *str); bool ListAddFromString(TList *list,char *c); bool ListFindString(TList *sl,char *str,uint16 *index); /********************************************************************* ** Table *********************************************************************/ typedef struct { char *name,*value; uint16 hash; } TTableItem; typedef struct { TTableItem *item; uint16 size,maxsize; } TTable; void TableInit(TTable *t); void TableFree(TTable *t); bool TableAdd(TTable *t,char *name,char *value); bool TableAddReplace(TTable *t,char *name,char *value); bool TableFindIndex(TTable *t,char *name,uint16 *index); char *TableFind(TTable *t,char *name); /********************************************************************* ** Thread *********************************************************************/ #ifdef ABYSS_WIN32 #include #define THREAD_ENTRYTYPE WINAPI #else #define THREAD_ENTRYTYPE #include #endif /* ABYSS_WIN32 */ typedef uint32 (THREAD_ENTRYTYPE *TThreadProc)(void *); #ifdef ABYSS_WIN32 typedef HANDLE TThread; #else typedef pthread_t TThread; typedef void* (*PTHREAD_START_ROUTINE)(void *); #endif /* ABYSS_WIN32 */ bool ThreadCreate(TThread *t,TThreadProc func,void *arg); bool ThreadRun(TThread *t); bool ThreadStop(TThread *t); bool ThreadKill(TThread *t); void ThreadWait(uint32 ms); void ThreadExit( TThread *t, int ret_value ); void ThreadClose( TThread *t ); /********************************************************************* ** Mutex *********************************************************************/ #ifdef ABYSS_WIN32 typedef HANDLE TMutex; #else typedef pthread_mutex_t TMutex; #endif /* ABYSS_WIN32 */ bool MutexCreate(TMutex *m); bool MutexLock(TMutex *m); bool MutexUnlock(TMutex *m); bool MutexTryLock(TMutex *m); void MutexFree(TMutex *m); /********************************************************************* ** Pool *********************************************************************/ typedef struct _TPoolZone { char *pos,*maxpos; struct _TPoolZone *next,*prev; /* char data[0]; */ char data[1]; } TPoolZone; typedef struct { TPoolZone *firstzone,*currentzone; uint32 zonesize; TMutex mutex; } TPool; bool PoolCreate(TPool *p,uint32 zonesize); void PoolFree(TPool *p); void *PoolAlloc(TPool *p,uint32 size); char *PoolStrdup(TPool *p,char *s); /********************************************************************* ** Socket *********************************************************************/ #ifdef ABYSS_WIN32 #include #else #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_FILIO_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #endif /* ABYSS_WIN32 */ #define TIME_INFINITE 0xffffffff #ifdef ABYSS_WIN32 typedef SOCKET TSocket; #else typedef uint32 TSocket; #endif /* ABYSS_WIN32 */ typedef struct in_addr TIPAddr; #define IPB1(x) (((unsigned char *)(&x))[0]) #define IPB2(x) (((unsigned char *)(&x))[1]) #define IPB3(x) (((unsigned char *)(&x))[2]) #define IPB4(x) (((unsigned char *)(&x))[3]) bool SocketInit(); bool SocketCreate(TSocket *s); bool SocketClose(TSocket *s); uint32 SocketWrite(TSocket *s, char *buffer, uint32 len); uint32 SocketRead(TSocket *s, char *buffer, uint32 len); uint32 SocketPeek(TSocket *s, char *buffer, uint32 len); bool SocketConnect(TSocket *s, TIPAddr *addr, uint16 port); bool SocketBind(TSocket *s, TIPAddr *addr, uint16 port); bool SocketListen(TSocket *s, uint32 backlog); bool SocketAccept(TSocket *s, TSocket *ns,TIPAddr *ip); uint32 SocketError(); uint32 SocketWait(TSocket *s,bool rd,bool wr,uint32 timems); bool SocketBlocking(TSocket *s, bool b); uint32 SocketAvailableReadBytes(TSocket *s); /********************************************************************* ** File *********************************************************************/ #include #include #include #include #include #ifndef NAME_MAX #define NAME_MAX 1024 #endif #ifdef ABYSS_WIN32 #ifndef __BORLANDC__ #define O_APPEND _O_APPEND #define O_CREAT _O_CREAT #define O_EXCL _O_EXCL #define O_RDONLY _O_RDONLY #define O_RDWR _O_RDWR #define O_TRUNC _O_TRUNC #define O_WRONLY _O_WRONLY #define O_TEXT _O_TEXT #define O_BINARY _O_BINARY #endif #define A_HIDDEN _A_HIDDEN #define A_NORMAL _A_NORMAL #define A_RDONLY _A_RDONLY #define A_SUBDIR _A_SUBDIR #else #define A_SUBDIR 1 #define O_BINARY 0 #define O_TEXT 0 #endif /* ABYSS_WIN32 */ #ifdef ABYSS_WIN32 #ifndef __BORLANDC__ typedef struct _stati64 TFileStat; typedef struct _finddata_t TFileInfo; typedef long TFileFind; #else typedef struct stat TFileStat; typedef struct finddata_t { char name[NAME_MAX+1]; int attrib; uint64 size; time_t time_write; WIN32_FIND_DATA data; } TFileInfo; typedef HANDLE TFileFind; #endif #else #include #include typedef struct stat TFileStat; typedef struct finddata_t { char name[NAME_MAX+1]; int attrib; uint64 size; time_t time_write; } TFileInfo; typedef struct { char path[NAME_MAX+1]; DIR *handle; } TFileFind; #endif typedef int TFile; bool FileOpen(TFile *f, char *name, uint32 attrib); bool FileOpenCreate(TFile *f, char *name, uint32 attrib); bool FileClose(TFile *f); bool FileWrite(TFile *f, void *buffer, uint32 len); int32 FileRead(TFile *f, void *buffer, uint32 len); bool FileSeek(TFile *f, uint64 pos, uint32 attrib); uint64 FileSize(TFile *f); bool FileStat(char *filename,TFileStat *filestat); bool FileFindFirst(TFileFind *filefind,char *path,TFileInfo *fileinfo); bool FileFindNext(TFileFind *filefind,TFileInfo *fileinfo); void FileFindClose(TFileFind *filefind); /********************************************************************* ** Server (1/2) *********************************************************************/ typedef struct { TSocket listensock; TFile logfile; TMutex logmutex; char *name; char *filespath; uint16 port; uint32 keepalivetimeout,keepalivemaxconn,timeout; TList handlers; TList defaultfilenames; void *defaulthandler; bool advertise; #ifdef _UNIX uid_t uid; gid_t gid; TFile pidfile; #endif } TServer; /********************************************************************* ** Conn *********************************************************************/ #define BUFFER_SIZE 4096 typedef struct _TConn { TServer *server; uint32 buffersize,bufferpos; uint32 inbytes,outbytes; TSocket socket; TIPAddr peerip; TThread thread; bool connected; bool inUse; void (*job)(struct _TConn *); char buffer[BUFFER_SIZE]; } TConn; TConn *ConnAlloc(); void ConnFree(TConn *c); bool ConnCreate(TConn *c, TSocket *s, void (*func)(TConn *)); bool ConnProcess(TConn *c); bool ConnKill(TConn *c); bool ConnWrite(TConn *c,void *buffer,uint32 size); bool ConnRead(TConn *c, uint32 timems); void ConnReadInit(TConn *c); bool ConnReadLine(TConn *c,char **z,uint32 timems); bool ConnWriteFromFile(TConn *c,TFile *file,uint64 start,uint64 end, void *buffer,uint32 buffersize,uint32 rate); /********************************************************************* ** Range *********************************************************************/ bool RangeDecode(char *str,uint64 filesize,uint64 *start,uint64 *end); /********************************************************************* ** Date *********************************************************************/ #include typedef struct tm TDate; bool DateToString(TDate *tm,char *s); bool DateToLogString(TDate *tm,char *s); bool DateDecode(char *s,TDate *tm); int32 DateCompare(TDate *d1,TDate *d2); bool DateFromGMT(TDate *d,time_t t); bool DateFromLocal(TDate *d,time_t t); bool DateInit(); /********************************************************************* ** Base64 *********************************************************************/ void Base64Encode(char *s,char *d); /********************************************************************* ** Session *********************************************************************/ typedef enum { m_unknown,m_get,m_put,m_head,m_post,m_delete,m_trace,m_options } TMethod; typedef struct { TMethod method; uint32 nbfileds; char *uri; char *query; char *host; char *from; char *useragent; char *referer; char *requestline; char *user; uint16 port; TList cookies; TList ranges; uint16 status; TString header; bool keepalive,cankeepalive; bool done; TServer *server; TConn *conn; uint8 versionminor,versionmajor; TTable request_headers,response_headers; TDate date; bool chunkedwrite,chunkedwritemode; } TSession; /********************************************************************* ** Request *********************************************************************/ #define CR '\r' #define LF '\n' #define CRLF "\r\n" bool RequestValidURI(TSession *r); bool RequestValidURIPath(TSession *r); bool RequestUnescapeURI(TSession *r); char *RequestHeaderValue(TSession *r,char *name); bool RequestRead(TSession *r); void RequestInit(TSession *r,TConn *c); void RequestFree(TSession *r); bool RequestAuth(TSession *r,char *credential,char *user,char *pass); /********************************************************************* ** Response *********************************************************************/ bool ResponseAddField(TSession *r,char *name,char *value); void ResponseWrite(TSession *r); bool ResponseChunked(TSession *s); void ResponseStatus(TSession *r,uint16 code); void ResponseStatusErrno(TSession *r); bool ResponseContentType(TSession *r,char *type); bool ResponseContentLength(TSession *r,uint64 len); void ResponseError(TSession *r); /********************************************************************* ** HTTP *********************************************************************/ char *HTTPReasonByStatus(uint16 status); int32 HTTPRead(TSession *s,char *buffer,uint32 len); bool HTTPWrite(TSession *s,char *buffer,uint32 len); bool HTTPWriteEnd(TSession *s); /********************************************************************* ** Server (2/2) *********************************************************************/ typedef bool (*URIHandler) (TSession *); bool ServerCreate(TServer *srv,char *name,uint16 port,char *filespath, char *logfilename); void ServerFree(TServer *srv); void ServerInit(TServer *srv); void ServerRun(TServer *srv); void ServerRunOnce(TServer *srv); bool ServerAddHandler(TServer *srv,URIHandler handler); void ServerDefaultHandler(TServer *srv,URIHandler handler); bool LogOpen(TServer *srv, char *filename); void LogWrite(TServer *srv,char *c); void LogClose(TServer *srv); /********************************************************************* ** MIMEType *********************************************************************/ void MIMETypeInit(); bool MIMETypeAdd(char *type,char *ext); char *MIMETypeFromExt(char *ext); char *MIMETypeFromFileName(char *filename); char *MIMETypeGuessFromFile(char *filename); /********************************************************************* ** Conf *********************************************************************/ bool ConfReadMIMETypes(char *filename); bool ConfReadServerFile(char *filename,TServer *srv); /********************************************************************* ** Trace *********************************************************************/ void TraceMsg(char *fmt,...); void TraceExit(char *fmt,...); /********************************************************************* ** Session *********************************************************************/ bool SessionLog(TSession *s); #ifdef __cplusplus } #endif #endif /* _ABYSS_H_ */ opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_conf.c000066400000000000000000000247551300170765700232300ustar00rootroot00000000000000/****************************************************************************** ** conf.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ******************************************************************************/ #include #include #include #if defined(WIN32) && !defined(__BORLANDC__) #include #endif #ifdef _UNIX #include #endif #include #include "abyss_xmlrpc_int.h" #include #include "abyss_trace.h" #include "abyss_server.h" #include "abyss_http.h" /********************************************************************* ** Configuration Files Parsing Functions *********************************************************************/ static abyss_bool ConfReadLine(TFile *f,char *buffer,uint32_t len) { abyss_bool r=TRUE; char c,*p,*z=buffer; while ((--len)>0) { if (FileRead(f,buffer,1)<1) { if (z==buffer) r=FALSE; break; }; if ((*buffer==CR) || (*buffer==LF) ) break; buffer++; }; if (len==0) while (FileRead(f,&c,1)==1) if ((c==CR) || (c==LF)) break; *buffer='\0'; /* Discard comments */ p=strchr(z,'#'); if (p) *p='\0'; return r; } static abyss_bool ConfNextToken(char **p) { while (1) switch (**p) { case '\t': case ' ': (*p)++; break; case '\0': return FALSE; default: return TRUE; }; } static char * ConfGetToken(char **p) { char *p0=*p; while (1) switch (**p) { case '\t': case ' ': case CR: case LF: case '\0': if (p0==*p) return NULL; if (**p) { **p='\0'; (*p)++; }; return p0; default: (*p)++; }; } static abyss_bool ConfReadInt(const char * const p, int32_t * const n, int32_t const min, int32_t const max) { /*---------------------------------------------------------------------------- Convert string 'p' to integer *n. If it isn't a valid integer or is not with the bounds [min, max], return FALSE. Otherwise, return TRUE. -----------------------------------------------------------------------------*/ char * e; *n = strtol(p, &e, 10); if (min != max) return ((e != p) && (*n >= min) && (*n <= max)); else return (e != p); } static abyss_bool ConfReadBool(char *p, abyss_bool *b) { if (strcasecmp(p,"yes")==0) { *b=TRUE; return TRUE; }; if (strcasecmp(p,"no")==0) { *b=FALSE; return TRUE; }; return FALSE; } /********************************************************************* ** MIME Types File *********************************************************************/ static void readMIMETypesFile(const char * const filename, MIMEType ** const MIMETypePP) { abyss_bool success; MIMEType * MIMETypeP; MIMETypeP = MIMETypeCreate(); if (MIMETypeP) { TFile file; abyss_bool fileOpened; fileOpened = FileOpen(&file, filename, O_RDONLY); if (fileOpened) { char z[512]; while (ConfReadLine(&file, z, 512)) { char * p; p = &z[0]; if (ConfNextToken(&p)) { const char * mimetype = ConfGetToken(&p); if (mimetype) { while (ConfNextToken(&p)) { const char * const ext = ConfGetToken(&p); if (ext) MIMETypeAdd2(MIMETypeP, mimetype, ext); else break; } } } } FileClose(&file); success = TRUE; } else success = FALSE; if (!success) MIMETypeDestroy(MIMETypeP); } else success = FALSE; if (success) *MIMETypePP = MIMETypeP; else *MIMETypePP = NULL; } /********************************************************************* ** Server Configuration File *********************************************************************/ static void chdirx(const char * const newdir, abyss_bool * const successP) { #if defined(WIN32) && !defined(__BORLANDC__) *successP = _chdir(newdir) == 0; #else *successP = chdir(newdir) == 0; #endif } static void parseUser(const char * const p, struct _TServer * const srvP) { #ifdef _UNIX if (p[0] == '#') { int32_t n; if (!ConfReadInt(&p[1], &n, 0, 0)) TraceExit("Bad user number '%s'", p); else srvP->uid = n; } else { struct passwd * pwd; if (!(pwd = getpwnam(p))) TraceExit("Unknown user '%s'", p); srvP->uid = pwd->pw_uid; if ((int)srvP->gid==(-1)) srvP->gid = pwd->pw_gid; }; #else TraceMsg("User option ignored"); #endif /* _UNIX */ } static void parsePidfile(const char * const p, struct _TServer * const srvP) { #ifdef _UNIX if (!FileOpenCreate(&srvP->pidfile, p, O_TRUNC | O_WRONLY)) { srvP->pidfile = -1; TraceMsg("Bad PidFile value '%s'", p); }; #else TraceMsg("PidFile option ignored"); #endif /* _UNIX */ } abyss_bool ConfReadServerFile(const char * const filename, TServer * const serverP) { struct _TServer * const srvP = serverP->srvP; TFile f; char z[512]; char * p; unsigned int lineNum; TFileStat fs; if (!FileOpen(&f, filename, O_RDONLY)) return FALSE; lineNum = 0; while (ConfReadLine(&f, z, 512)) { ++lineNum; p = z; if (ConfNextToken(&p)) { const char * const option = ConfGetToken(&p); if (option) { ConfNextToken(&p); if (strcasecmp(option, "port") == 0) { int32_t n; if (ConfReadInt(p, &n, 1, 65535)) srvP->port = n; else TraceExit("Invalid port '%s'", p); } else if (strcasecmp(option, "serverroot") == 0) { abyss_bool success; chdirx(p, &success); if (!success) TraceExit("Invalid server root '%s'",p); } else if (strcasecmp(option, "path") == 0) { if (FileStat(p, &fs)) if (fs.st_mode & S_IFDIR) { xmlrpc_strfree(srvP->filespath); srvP->filespath = strdup(p); continue; } TraceExit("Invalid path '%s'", p); } else if (strcasecmp(option, "default") == 0) { const char * filename; while ((filename = ConfGetToken(&p))) { ListAdd(&srvP->defaultfilenames, strdup(filename)); if (!ConfNextToken(&p)) break; } } else if (strcasecmp(option, "keepalive") == 0) { int32_t n; if (ConfReadInt(p, &n, 1, 65535)) srvP->keepalivemaxconn = n; else TraceExit("Invalid KeepAlive value '%s'", p); } else if (strcasecmp(option, "timeout") == 0) { int32_t n; if (ConfReadInt(p, &n, 1, 3600)) { srvP->keepalivetimeout = n; /* Must see what to do with that */ srvP->timeout = n; } else TraceExit("Invalid TimeOut value '%s'", p); } else if (strcasecmp(option, "mimetypes") == 0) { readMIMETypesFile(p, &srvP->mimeTypeP); if (!srvP->mimeTypeP) TraceExit("Can't read MIME Types file '%s'", p); } else if (strcasecmp(option,"logfile") == 0) { srvP->logfilename = strdup(p); } else if (strcasecmp(option,"user") == 0) { parseUser(p, srvP); } else if (strcasecmp(option, "pidfile")==0) { parsePidfile(p, srvP); } else if (strcasecmp(option, "advertiseserver") == 0) { if (!ConfReadBool(p, &srvP->advertise)) TraceExit("Invalid boolean value " "for AdvertiseServer option"); } else TraceExit("Invalid option '%s' at line %u", option, lineNum); } } } FileClose(&f); return TRUE; } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_conn.c000066400000000000000000000460601300170765700232310ustar00rootroot00000000000000/* Copyright information is at the end of the file. */ #include #include #include #include #include #include #include "abyss_mallocvar.h" #include "abyss_xmlrpc_int.h" #include #include "abyss_socket.h" #include "abyss_server.h" #include "abyss_thread.h" #include "abyss_conn.h" /********************************************************************* ** Conn *********************************************************************/ static TThreadProc connJob; static void connJob(void * const userHandle) { /*---------------------------------------------------------------------------- This is the root function for a thread that processes a connection (performs HTTP transactions). -----------------------------------------------------------------------------*/ TConn * const connectionP = userHandle; (connectionP->job)(connectionP); connectionP->finished = TRUE; /* Note that if we are running in a forked process, setting connectionP->finished has no effect, because it's just our own copy of *connectionP. In this case, Parent must update his own copy based on a SIGCHLD signal that the OS will generate right after we exit. */ ThreadExit(0); } static void connDone(TConn * const connectionP) { /* In the forked case, this is designed to run in the parent process after the child has terminated. */ connectionP->finished = TRUE; if (connectionP->done) connectionP->done(connectionP); } static TThreadDoneFn threadDone; static void threadDone(void * const userHandle) { TConn * const connectionP = userHandle; connDone(connectionP); } static void makeThread(TConn * const connectionP, enum abyss_foreback const foregroundBackground, abyss_bool const useSigchld, const char ** const errorP) { switch (foregroundBackground) { case ABYSS_FOREGROUND: connectionP->hasOwnThread = FALSE; *errorP = NULL; break; case ABYSS_BACKGROUND: { const char * error; connectionP->hasOwnThread = TRUE; ThreadCreate(&connectionP->threadP, connectionP, &connJob, &threadDone, useSigchld, &error); if (error) { xmlrpc_asprintf(errorP, "Unable to create thread to " "process connection. %s", error); xmlrpc_strfree(error); } else *errorP = NULL; } break; } /* switch */ } void ConnCreate(TConn ** const connectionPP, TServer * const serverP, TSocket * const connectedSocketP, TThreadProc * const job, TThreadDoneFn * const done, enum abyss_foreback const foregroundBackground, abyss_bool const useSigchld, const char ** const errorP) { /*---------------------------------------------------------------------------- Create an HTTP connection. A connection carries one or more HTTP transactions (request/response). 'connectedSocketP' transports the requests and responses. The connection handles those HTTP requests. The connection handles the requests primarily by running the function 'job' once. Some connections can do that autonomously, as soon as the connection is created. Others don't until Caller subsequently calls ConnProcess. Some connections complete the processing before ConnProcess return, while others may run the connection asynchronously to the creator, in the background, via a TThread thread. 'foregroundBackground' determines which. 'job' calls methods of the connection to get requests and send responses. Some time after the HTTP transactions are all done, 'done' gets called in some context. -----------------------------------------------------------------------------*/ TConn * connectionP; MALLOCVAR(connectionP); if (connectionP == NULL) xmlrpc_asprintf(errorP, "Unable to allocate memory for a connection " "descriptor."); else { abyss_bool success; uint16_t peerPortNumber; connectionP->server = serverP; connectionP->socketP = connectedSocketP; connectionP->buffersize = 0; connectionP->bufferpos = 0; connectionP->finished = FALSE; connectionP->job = job; connectionP->done = done; connectionP->inbytes = 0; connectionP->outbytes = 0; connectionP->trace = getenv("ABYSS_TRACE_CONN"); connectionP->start = time(NULL); SocketGetPeerName(connectedSocketP, &connectionP->peerip, &peerPortNumber, &success); if (success) makeThread(connectionP, foregroundBackground, useSigchld, errorP); else xmlrpc_asprintf(errorP, "Failed to get peer name from socket."); } *connectionPP = connectionP; } abyss_bool ConnProcess(TConn * const connectionP) { /*---------------------------------------------------------------------------- Drive the main processing of a connection -- run the connection's "job" function, which should read HTTP requests from the connection and send HTTP responses. If we succeed, we guarantee the connection's "done" function will get called some time after all processing is complete. It might be before we return or some time after. If we fail, we guarantee the "done" function will not be called. -----------------------------------------------------------------------------*/ abyss_bool retval; if (connectionP->hasOwnThread) { /* There's a background thread to handle this connection. Set it running. */ retval = ThreadRun(connectionP->threadP); } else { /* No background thread. We just handle it here while Caller waits. */ (connectionP->job)(connectionP); connDone(connectionP); retval = TRUE; } return retval; } void ConnWaitAndRelease(TConn * const connectionP) { if (connectionP->hasOwnThread) ThreadWaitAndRelease(connectionP->threadP); free(connectionP); } abyss_bool ConnKill(TConn * connectionP) { connectionP->finished = TRUE; return ThreadKill(connectionP->threadP); } void ConnReadInit(TConn * const connectionP) { if (connectionP->buffersize>connectionP->bufferpos) { connectionP->buffersize -= connectionP->bufferpos; memmove(connectionP->buffer, connectionP->buffer+connectionP->bufferpos, connectionP->buffersize); connectionP->bufferpos = 0; } else connectionP->buffersize=connectionP->bufferpos = 0; connectionP->inbytes=connectionP->outbytes = 0; } static void traceBuffer(const char * const label, const char * const buffer, unsigned int const size) { unsigned int nonPrintableCount; unsigned int i; nonPrintableCount = 0; /* Initial value */ for (i = 0; i < size; ++i) { if (!isprint(buffer[i]) && buffer[i] != '\n' && buffer[i] != '\r') ++nonPrintableCount; } if (nonPrintableCount > 0) fprintf(stderr, "%s contains %u nonprintable characters.\n", label, nonPrintableCount); fprintf(stderr, "%s:\n", label); fprintf(stderr, "%.*s\n", (int)size, buffer); } static void traceSocketRead(TConn * const connectionP, unsigned int const size) { if (connectionP->trace) traceBuffer("READ FROM SOCKET:", connectionP->buffer + connectionP->buffersize, size); } static void traceSocketWrite(TConn * const connectionP, const char * const buffer, unsigned int const size, abyss_bool const failed) { if (connectionP->trace) { const char * const label = failed ? "FAILED TO WRITE TO SOCKET:" : "WROTE TO SOCKET"; traceBuffer(label, buffer, size); } } static uint32_t bufferSpace(TConn * const connectionP) { return BUFFER_SIZE - connectionP->buffersize; } abyss_bool ConnRead(TConn * const connectionP, uint32_t const timeout) { /*---------------------------------------------------------------------------- Read some stuff on connection *connectionP from the socket. Don't wait more than 'timeout' seconds for data to arrive. Fail if nothing arrives within that time. -----------------------------------------------------------------------------*/ time_t const deadline = time(NULL) + timeout; abyss_bool cantGetData; abyss_bool gotData; cantGetData = FALSE; gotData = FALSE; while (!gotData && !cantGetData) { int const timeLeft = deadline - time(NULL); if (timeLeft <= 0) cantGetData = TRUE; else { int rc; rc = SocketWait(connectionP->socketP, TRUE, FALSE, timeLeft * 1000); if (rc != 1) cantGetData = TRUE; else { uint32_t bytesAvail; bytesAvail = SocketAvailableReadBytes(connectionP->socketP); if (bytesAvail <= 0) cantGetData = TRUE; else { uint32_t const bytesToRead = MIN(bytesAvail, bufferSpace(connectionP)-1); uint32_t bytesRead; bytesRead = SocketRead( connectionP->socketP, (unsigned char*)connectionP->buffer + connectionP->buffersize, bytesToRead); if (bytesRead > 0) { traceSocketRead(connectionP, bytesRead); connectionP->inbytes += bytesRead; connectionP->buffersize += bytesRead; connectionP->buffer[connectionP->buffersize] = '\0'; gotData = TRUE; } } } } } if (gotData) return TRUE; else return FALSE; } abyss_bool ConnWrite(TConn * const connectionP, const void * const buffer, uint32_t const size) { abyss_bool failed; SocketWrite(connectionP->socketP, buffer, size, &failed); traceSocketWrite(connectionP, buffer, size, failed); if (!failed) connectionP->outbytes += size; return !failed; } abyss_bool ConnWriteFromFile(TConn * const connectionP, TFile * const fileP, uint64_t const start, uint64_t const last, void * const buffer, uint32_t const buffersize, uint32_t const rate) { /*---------------------------------------------------------------------------- Write the contents of the file stream *fileP, from offset 'start' up through 'last', to the HTTP connection *connectionP. Meter the reading so as not to read more than 'rate' bytes per second. Use the 'bufferSize' bytes at 'buffer' as an internal buffer for this. -----------------------------------------------------------------------------*/ abyss_bool retval; uint32_t waittime; abyss_bool success; uint32_t readChunkSize; if (rate > 0) { readChunkSize = MIN(buffersize, rate); /* One second's worth */ waittime = (1000 * buffersize) / rate; } else { readChunkSize = buffersize; waittime = 0; } success = FileSeek(fileP, start, SEEK_SET); if (!success) retval = FALSE; else { uint64_t const totalBytesToRead = last - start + 1; uint64_t bytesread; bytesread = 0; /* initial value */ while (bytesread < totalBytesToRead) { uint64_t const bytesLeft = totalBytesToRead - bytesread; uint64_t const bytesToRead = MIN(readChunkSize, bytesLeft); uint64_t bytesReadThisTime; bytesReadThisTime = FileRead(fileP, buffer, bytesToRead); bytesread += bytesReadThisTime; if (bytesReadThisTime > 0) ConnWrite(connectionP, buffer, bytesReadThisTime); else break; if (waittime > 0) xmlrpc_millisecond_sleep(waittime); } retval = (bytesread >= totalBytesToRead); } return retval; } static void processHeaderLine(char * const start, const char * const headerStart, TConn * const connectionP, time_t const deadline, abyss_bool * const gotHeaderP, char ** const nextP, abyss_bool * const errorP) { /*---------------------------------------------------------------------------- If there's enough data in the buffer at *pP, process a line of HTTP header. It is part of a header that starts at 'headerStart' and has been previously processed up to 'start'. The data in the buffer is terminated by a NUL. WE MODIFY THE DATA. -----------------------------------------------------------------------------*/ abyss_bool gotHeader; char * lfPos; char * p; p = start; gotHeader = FALSE; /* initial assumption */ lfPos = strchr(p, LF); if (lfPos) { if ((*p != LF) && (*p != CR)) { /* We're looking at a non-empty line */ if (*(lfPos+1) == '\0') { /* There's nothing in the buffer after the line, so we don't know if there's a continuation line coming. Must read more. */ int const timeLeft = deadline - time(NULL); *errorP = !ConnRead(connectionP, timeLeft); } if (!*errorP) { p = lfPos; /* Point to LF */ /* If the next line starts with whitespace, it's a continuation line, so blank out the line delimiter (LF or CRLF) so as to join the next line with this one. */ if ((*(p+1) == ' ') || (*(p+1) == '\t')) { if (p > headerStart && *(p-1) == CR) *(p-1) = ' '; *p++ = ' '; } else gotHeader = TRUE; } } else { /* We're looking at an empty line (i.e. what marks the end of the header) */ p = lfPos; /* Point to LF */ gotHeader = TRUE; } } if (gotHeader) { /* 'p' points to the final LF */ /* Replace the LF or the CR in CRLF with NUL, so as to terminate the string at 'headerStart' that is the full header. */ if (p > headerStart && *(p-1) == CR) *(p-1) = '\0'; /* NUL out CR in CRLF */ else *p = '\0'; /* NUL out LF */ ++p; /* Point to next line in buffer */ } *gotHeaderP = gotHeader; *nextP = p; } abyss_bool ConnReadHeader(TConn * const connectionP, char ** const headerP) { /*---------------------------------------------------------------------------- Read an HTTP header on connection *connectionP. An HTTP header is basically a line, except that if a line starts with white space, it's a continuation of the previous line. A line is delimited by either LF or CRLF. In the course of reading, we read at least one character past the line delimiter at the end of the header; we may read much more. We leave everything after the header (and its line delimiter) in the internal buffer, with the buffer pointer pointing to it. We use stuff already in the internal buffer (perhaps left by a previous call to this subroutine) before reading any more from from the socket. Return as *headerP the header value. This is in the connection's internal buffer. This contains no line delimiters. -----------------------------------------------------------------------------*/ uint32_t const deadline = time(NULL) + connectionP->server->srvP->timeout; abyss_bool retval; char * p; char * headerStart; abyss_bool error; abyss_bool gotHeader; p = connectionP->buffer + connectionP->bufferpos; headerStart = p; gotHeader = FALSE; error = FALSE; while (!gotHeader && !error) { int const timeLeft = deadline - time(NULL); if (timeLeft <= 0) error = TRUE; else { if (p >= connectionP->buffer + connectionP->buffersize) /* Need more data from the socket to chew on */ error = !ConnRead(connectionP, timeLeft); if (!error) { assert(connectionP->buffer + connectionP->buffersize > p); processHeaderLine(p, headerStart, connectionP, deadline, &gotHeader, &p, &error); } } } if (gotHeader) { /* We've consumed this part of the buffer (but be careful -- you can't reuse that part of the buffer because the string we're returning is in it! */ connectionP->bufferpos += p - headerStart; *headerP = headerStart; retval = TRUE; } else retval = FALSE; return retval; } TServer * ConnServer(TConn * const connectionP) { return connectionP->server; } /******************************************************************************* ** ** conn.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ******************************************************************************/ opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_conn.h000066400000000000000000000053671300170765700232430ustar00rootroot00000000000000#ifndef CONN_H_INCLUDED #define CONN_H_INCLUDED #include #include "abyss_socket.h" #include "abyss_file.h" #define BUFFER_SIZE 4096 struct _TConn { struct _TConn * nextOutstandingP; /* Link to the next connection in the list of outstanding connections */ TServer * server; uint32_t buffersize; /* Index into the connection buffer (buffer[], below) where the next byte read on the connection will go. */ uint32_t bufferpos; /* Index into the connection buffer (buffer[], below) where the next byte to be delivered to the user is. */ uint32_t inbytes,outbytes; TSocket * socketP; TIPAddr peerip; abyss_bool hasOwnThread; TThread * threadP; abyss_bool finished; /* We have done all the processing there is to do on this connection, other than possibly notifying someone that we're done. One thing this signifies is that any thread or process that the connection spawned is dead or will be dead soon, so one could reasonably wait for it to be dead, e.g. with pthread_join(). Note that one can scan a bunch of processes for 'finished' status, but sometimes can't scan a bunch of threads for liveness. */ const char * trace; TThreadProc * job; TThreadDoneFn * done; char buffer[BUFFER_SIZE]; time_t start; }; typedef struct _TConn TConn; TConn * ConnAlloc(void); void ConnFree(TConn * const connectionP); void ConnCreate(TConn ** const connectionPP, TServer * const serverP, TSocket * const connectedSocketP, TThreadProc * const job, TThreadDoneFn * const done, enum abyss_foreback const foregroundBackground, abyss_bool const useSigchld, const char ** const errorP); abyss_bool ConnProcess(TConn * const connectionP); abyss_bool ConnKill(TConn * const connectionP); void ConnWaitAndRelease(TConn * const connectionP); abyss_bool ConnWrite(TConn * const connectionP, const void * const buffer, uint32_t const size); abyss_bool ConnRead(TConn * const c, uint32_t const timems); void ConnReadInit(TConn * const connectionP); abyss_bool ConnReadHeader(TConn * const connectionP, char ** const headerP); abyss_bool ConnWriteFromFile(TConn * const connectionP, TFile * const file, uint64_t const start, uint64_t const end, void * const buffer, uint32_t const buffersize, uint32_t const rate); TServer * ConnServer(TConn * const connectionP); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_data.c000066400000000000000000000343411300170765700232040ustar00rootroot00000000000000/****************************************************************************** ** list.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** *******************************************************************************/ #include #include #include #include "abyss_mallocvar.h" #include "abyss_xmlrpc_int.h" #include #include "abyss_token.h" #include "abyss_data.h" /********************************************************************* ** List *********************************************************************/ void ListInit(TList *sl) { sl->item=NULL; sl->size=sl->maxsize=0; sl->autofree=FALSE; } void ListInitAutoFree(TList *sl) { sl->item=NULL; sl->size=sl->maxsize=0; sl->autofree=TRUE; } void ListFree(TList * const sl) { if (sl->item) { if (sl->autofree) { unsigned int i; for (i = sl->size; i > 0; --i) free(sl->item[i-1]); } free(sl->item); } sl->item = NULL; sl->size = 0; sl->maxsize = 0; } void ListFreeItems(TList * const sl) { if (sl->item) { unsigned int i; for (i = sl->size; i > 0; --i) free(sl->item[i-1]); } } abyss_bool ListAdd(TList * const sl, void * const str) { /*---------------------------------------------------------------------------- Add an item to the end of the list. -----------------------------------------------------------------------------*/ abyss_bool success; if (sl->size >= sl->maxsize) { uint16_t newSize = sl->maxsize + 16; void **newitem; newitem = realloc(sl->item, newSize * sizeof(void *)); if (newitem) { sl->item = newitem; sl->maxsize = newSize; } } if (sl->size >= sl->maxsize) success = FALSE; else { success = TRUE; sl->item[sl->size++] = str; } return success; } void ListRemove(TList * const sl) { /*---------------------------------------------------------------------------- Remove the last item from the list. -----------------------------------------------------------------------------*/ assert(sl->size > 0); --sl->size; } abyss_bool ListAddFromString(TList * const list, const char * const stringArg) { abyss_bool retval; if (!stringArg) retval = TRUE; else { char * buffer; buffer = strdup(stringArg); if (!buffer) retval = FALSE; else { abyss_bool endOfString; abyss_bool error; char * c; for (c = &buffer[0], endOfString = FALSE, error = FALSE; !endOfString && !error; ) { const char * t; NextToken((const char **)&c); while (*c == ',') ++c; t = GetToken(&c); if (!t) endOfString = TRUE; else { char * p; for (p = c - 2; *p == ','; --p) *p = '\0'; if (t[0] != '\0') { abyss_bool added; added = ListAdd(list, (void*)t); if (!added) error = TRUE; } } } retval = !error; xmlrpc_strfree(buffer); } } return retval; } abyss_bool ListFindString(TList * const sl, const char * const str, uint16_t * const indexP) { uint16_t i; if (sl->item && str) for (i=0;isize;i++) if (strcmp(str,(char *)(sl->item[i]))==0) { *indexP=i; return TRUE; }; return FALSE; } /********************************************************************* ** Buffer *********************************************************************/ abyss_bool BufferAlloc(TBuffer *buf,uint32_t memsize) { /* ************** Implement the static buffers ***/ buf->staticid=0; buf->data=(void *)malloc(memsize); if (buf->data) { buf->size=memsize; return TRUE; } else { buf->size=0; return FALSE; }; } void BufferFree(TBuffer *buf) { if (buf->staticid) { /* ************** Implement the static buffers ***/ } else free(buf->data); buf->size=0; buf->staticid=0; } abyss_bool BufferRealloc(TBuffer *buf,uint32_t memsize) { if (buf->staticid) { TBuffer b; if (memsize<=buf->size) return TRUE; if (BufferAlloc(&b,memsize)) { memcpy(b.data,buf->data,buf->size); BufferFree(buf); *buf=b; return TRUE; } } else { void *d; d=realloc(buf->data,memsize); if (d) { buf->data=d; buf->size=memsize; return TRUE; } } return FALSE; } /********************************************************************* ** String *********************************************************************/ abyss_bool StringAlloc(TString *s) { s->size=0; if (BufferAlloc(&(s->buffer),256)) { *(char *)(s->buffer.data)='\0'; return TRUE; } else return FALSE; } abyss_bool StringConcat(TString *s,char *s2) { uint32_t len=strlen(s2); if (len+s->size+1>s->buffer.size) if (!BufferRealloc(&(s->buffer),((len+s->size+1+256)/256)*256)) return FALSE; strcat((char *)(s->buffer.data),s2); s->size+=len; return TRUE; } abyss_bool StringBlockConcat(TString *s,char *s2,char **ref) { uint32_t len=strlen(s2)+1; if (len+s->size>s->buffer.size) if (!BufferRealloc(&(s->buffer),((len+s->size+1+256)/256)*256)) return FALSE; *ref=(char *)(s->buffer.data)+s->size; memcpy(*ref,s2,len); s->size+=len; return TRUE; } void StringFree(TString *s) { s->size=0; BufferFree(&(s->buffer)); } char *StringData(TString *s) { return (char *)(s->buffer.data); } /********************************************************************* ** Hash *********************************************************************/ static uint16_t Hash16(const char * const start) { const char * s; uint16_t i; s = start; i = 0; while(*s) i = i * 37 + *s++; return i; } /********************************************************************* ** Table *********************************************************************/ void TableInit(TTable *t) { t->item=NULL; t->size=t->maxsize=0; } void TableFree(TTable *t) { uint16_t i; if (t->item) { if (t->size) for (i=t->size;i>0;i--) { free(t->item[i-1].name); free(t->item[i-1].value); }; free(t->item); } TableInit(t); } abyss_bool TableFindIndex(TTable * const t, const char * const name, uint16_t * const index) { uint16_t i,hash=Hash16(name); if ((t->item) && (t->size>0) && (*indexsize)) { for (i=*index;isize;i++) if (hash==t->item[i].hash) if (strcmp(t->item[i].name,name)==0) { *index=i; return TRUE; }; }; return FALSE; } abyss_bool TableAddReplace(TTable * const t, const char * const name, const char * const value) { uint16_t i=0; if (TableFindIndex(t,name,&i)) { free(t->item[i].value); if (value) t->item[i].value=strdup(value); else { free(t->item[i].name); if (--t->size>0) t->item[i]=t->item[t->size]; }; return TRUE; } else return TableAdd(t,name,value); } abyss_bool TableAdd(TTable * const t, const char * const name, const char * const value) { if (t->size>=t->maxsize) { TTableItem *newitem; t->maxsize+=16; newitem=(TTableItem *)realloc(t->item,(t->maxsize)*sizeof(TTableItem)); if (newitem) t->item=newitem; else { t->maxsize-=16; return FALSE; } } t->item[t->size].name=strdup(name); t->item[t->size].value=strdup(value); t->item[t->size].hash=Hash16(name); ++t->size; return TRUE; } char * TableFind(TTable * const t, const char * const name) { uint16_t i=0; if (TableFindIndex(t,name,&i)) return t->item[i].value; else return NULL; } /********************************************************************* ** Pool *********************************************************************/ static TPoolZone * PoolZoneAlloc(uint32_t const zonesize) { TPoolZone * poolZoneP; MALLOCARRAY(poolZoneP, zonesize); if (poolZoneP) { poolZoneP->pos = &poolZoneP->data[0]; poolZoneP->maxpos = poolZoneP->pos + zonesize; poolZoneP->next = NULL; poolZoneP->prev = NULL; } return poolZoneP; } static void PoolZoneFree(TPoolZone * const poolZoneP) { free(poolZoneP); } abyss_bool PoolCreate(TPool * const poolP, uint32_t const zonesize) { abyss_bool success; abyss_bool mutexCreated; poolP->zonesize = zonesize; mutexCreated = MutexCreate(&poolP->mutex); if (mutexCreated) { TPoolZone * const firstZoneP = PoolZoneAlloc(zonesize); if (firstZoneP != NULL) { poolP->firstzone = firstZoneP; poolP->currentzone = firstZoneP; success = TRUE; } else success = FALSE; if (!success) MutexFree(&poolP->mutex); } else success = FALSE; return success; } void * PoolAlloc(TPool * const poolP, uint32_t const size) { /*---------------------------------------------------------------------------- Allocate a block of size 'size' from pool 'poolP'. -----------------------------------------------------------------------------*/ void * retval; if (size == 0) retval = NULL; else { abyss_bool gotMutexLock; gotMutexLock = MutexLock(&poolP->mutex); if (!gotMutexLock) retval = NULL; else { TPoolZone * const curPoolZoneP = poolP->currentzone; if (curPoolZoneP->pos + size < curPoolZoneP->maxpos) { retval = curPoolZoneP->pos; curPoolZoneP->pos += size; } else { uint32_t const zonesize = MAX(size, poolP->zonesize); TPoolZone * const newPoolZoneP = PoolZoneAlloc(zonesize); if (newPoolZoneP) { newPoolZoneP->prev = curPoolZoneP; newPoolZoneP->next = curPoolZoneP->next; curPoolZoneP->next = newPoolZoneP; poolP->currentzone = newPoolZoneP; retval= newPoolZoneP->data; newPoolZoneP->pos = newPoolZoneP->data + size; } else retval = NULL; } MutexUnlock(&poolP->mutex); } } return retval; } void PoolReturn(TPool * const poolP, void * const blockP) { /*---------------------------------------------------------------------------- Return the block at 'blockP' to the pool 'poolP'. WE ASSUME THAT IS THE MOST RECENTLY ALLOCATED AND NOT RETURNED BLOCK IN THE POOL. -----------------------------------------------------------------------------*/ TPoolZone * const curPoolZoneP = poolP->currentzone; assert((char*)curPoolZoneP->data < (char*)blockP && (char*)blockP < (char*)curPoolZoneP->pos); curPoolZoneP->pos = blockP; if (curPoolZoneP->pos == curPoolZoneP->data) { /* That emptied out the current zone. Free it and make the previous zone current. */ assert(curPoolZoneP->prev); /* entry condition */ curPoolZoneP->prev->next = NULL; PoolZoneFree(curPoolZoneP); } } void PoolFree(TPool * const poolP) { TPoolZone * poolZoneP; TPoolZone * nextPoolZoneP; for (poolZoneP = poolP->firstzone; poolZoneP; poolZoneP = nextPoolZoneP) { nextPoolZoneP = poolZoneP->next; free(poolZoneP); } } const char * PoolStrdup(TPool * const poolP, const char * const origString) { char * newString; if (origString == NULL) newString = NULL; else { newString = PoolAlloc(poolP, strlen(origString) + 1); if (newString != NULL) strcpy(newString, origString); } return newString; } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_data.h000066400000000000000000000044761300170765700232170ustar00rootroot00000000000000#ifndef DATA_H_INCLUDED #define DATA_H_INCLUDED #include "abyss_thread.h" /********************************************************************* ** List *********************************************************************/ typedef struct { void **item; uint16_t size; uint16_t maxsize; abyss_bool autofree; } TList; void ListInit(TList * const listP); void ListInitAutoFree(TList * const listP); void ListFree(TList * const listP); void ListFreeItems(TList * const listP); abyss_bool ListAdd(TList * const listP, void * const str); void ListRemove(TList * const listP); abyss_bool ListAddFromString(TList * const listP, const char * const c); abyss_bool ListFindString(TList * const listP, const char * const str, uint16_t * const indexP); typedef struct { char *name,*value; uint16_t hash; } TTableItem; typedef struct { TTableItem *item; uint16_t size,maxsize; } TTable; void TableInit(TTable * const t); void TableFree(TTable * const t); abyss_bool TableAdd(TTable * const t, const char * const name, const char * const value); abyss_bool TableAddReplace(TTable * const t, const char * const name, const char * const value); abyss_bool TableFindIndex(TTable * const t, const char * const name, uint16_t * const index); char * TableFind(TTable * const t, const char * const name); /********************************************************************* ** Pool *********************************************************************/ typedef struct _TPoolZone { char * pos; char * maxpos; struct _TPoolZone * next; struct _TPoolZone * prev; /* char data[0]; Some compilers don't accept this */ char data[1]; } TPoolZone; typedef struct { TPoolZone * firstzone; TPoolZone * currentzone; uint32_t zonesize; TMutex mutex; } TPool; abyss_bool PoolCreate(TPool * const poolP, uint32_t const zonesize); void PoolFree(TPool * const poolP); void * PoolAlloc(TPool * const poolP, uint32_t const size); void PoolReturn(TPool * const poolP, void * const blockP); const char * PoolStrdup(TPool * const poolP, const char * const origString); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_date.c000066400000000000000000000134741300170765700232140ustar00rootroot00000000000000/****************************************************************************** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** *******************************************************************************/ #include #include #include #include #include #include #include "abyss_date.h" /********************************************************************* ** Date *********************************************************************/ static char *_DateDay[7]= { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; static char *_DateMonth[12]= { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; static int32_t _DateTimeBias=0; static char _DateTimeBiasStr[6]=""; abyss_bool DateToString(TDate *tm,char *s) { if (mktime(tm)==(time_t)(-1)) { *s='\0'; return FALSE; }; sprintf(s,"%s, %02d %s %04d %02d:%02d:%02d GMT",_DateDay[tm->tm_wday],tm->tm_mday, _DateMonth[tm->tm_mon],tm->tm_year+1900,tm->tm_hour,tm->tm_min,tm->tm_sec); return TRUE; } abyss_bool DateToLogString(TDate * const tmP, char * const s) { time_t t; t = mktime(tmP); if (t != (time_t)(-1)) { TDate d; abyss_bool success; success = DateFromLocal(&d, t); if (success) { sprintf(s, "%02d/%s/%04d:%02d:%02d:%02d %s", d.tm_mday, _DateMonth[d.tm_mon], d.tm_year+1900, d.tm_hour, d.tm_min, d.tm_sec, _DateTimeBiasStr); return TRUE; } } *s = '\0'; return FALSE; } abyss_bool DateDecode(const char * const dateString, TDate * const tmP) { int rc; const char * s; uint32_t n; s = &dateString[0]; /* Ignore spaces, day name and spaces */ while ((*s==' ') || (*s=='\t')) ++s; while ((*s!=' ') && (*s!='\t')) ++s; while ((*s==' ') || (*s=='\t')) ++s; /* try to recognize the date format */ rc = sscanf(s, "%*s %d %d:%d:%d %d%*s", &tmP->tm_mday, &tmP->tm_hour, &tmP->tm_min, &tmP->tm_sec, &tmP->tm_year); if (rc != 5) { int rc; rc = sscanf(s, "%d %n%*s %d %d:%d:%d GMT%*s", &tmP->tm_mday,&n,&tmP->tm_year, &tmP->tm_hour, &tmP->tm_min, &tmP->tm_sec); if (rc != 5) { int rc; rc = sscanf(s, "%d-%n%*[A-Za-z]-%d %d:%d:%d GMT%*s", &tmP->tm_mday, &n, &tmP->tm_year, &tmP->tm_hour, &tmP->tm_min, &tmP->tm_sec); if (rc != 5) return FALSE; } } /* s points now to the month string */ s += n; for (n = 0; n < 12; ++n) { char * p; p =_DateMonth[n]; if (tolower(*p++) == tolower(*s)) if (*p++ == tolower(s[1])) if (*p == tolower(s[2])) break; } if (n == 12) return FALSE; tmP->tm_mon = n; /* finish the work */ if (tmP->tm_year > 1900) tmP->tm_year -= 1900; else { if (tmP->tm_year < 70) tmP->tm_year += 100; } tmP->tm_isdst = 0; return (mktime(tmP) != (time_t)(-1)); } int32_t DateCompare(TDate *d1,TDate *d2) { int32_t x; if ((x=d1->tm_year-d2->tm_year)==0) if ((x=d1->tm_mon-d2->tm_mon)==0) if ((x=d1->tm_mday-d2->tm_mday)==0) if ((x=d1->tm_hour-d2->tm_hour)==0) if ((x=d1->tm_min-d2->tm_min)==0) x=d1->tm_sec-d2->tm_sec; return x; } abyss_bool DateFromGMT(TDate *d,time_t t) { TDate *dx; dx=gmtime(&t); if (dx) { *d=*dx; return TRUE; }; return FALSE; } abyss_bool DateFromLocal(TDate *d,time_t t) { return DateFromGMT(d,t+_DateTimeBias*2); } abyss_bool DateInit(void) { time_t t; TDate gmt,local,*d; time(&t); if (DateFromGMT(&gmt,t)) { d=localtime(&t); if (d) { local=*d; _DateTimeBias = (local.tm_sec-gmt.tm_sec)+(local.tm_min-gmt.tm_min)*60 +(local.tm_hour-gmt.tm_hour)*3600; sprintf(_DateTimeBiasStr, "%+03d%02d", _DateTimeBias/3600,(abs(_DateTimeBias) % 3600)/60); return TRUE; }; } return FALSE; } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_date.h000066400000000000000000000011211300170765700232030ustar00rootroot00000000000000#ifndef DATE_H_INCLUDED #define DATE_H_INCLUDED #include #include typedef struct tm TDate; abyss_bool DateToString(TDate * const tmP, char * const s); abyss_bool DateToLogString(TDate * const tmP, char * const s); abyss_bool DateDecode(const char * const dateString, TDate * const tmP); int32_t DateCompare(TDate * const d1, TDate * const d2); abyss_bool DateFromGMT(TDate * const d, time_t const t); abyss_bool DateFromLocal(TDate * const d, time_t const t); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_file.c000066400000000000000000000144371300170765700232160ustar00rootroot00000000000000/****************************************************************************** ** file.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ******************************************************************************/ #include #ifdef WIN32 #include #endif #ifndef WIN32 #include #include #endif #include #include "abyss_file.h" /********************************************************************* ** File *********************************************************************/ abyss_bool FileOpen(TFile *f, const char *name,uint32_t attrib) { #if defined( WIN32 ) && !defined( __BORLANDC__ ) return ((*f=_open(name,attrib))!=(-1)); #else return ((*f=open(name,attrib))!=(-1)); #endif } abyss_bool FileOpenCreate(TFile *f, const char *name, uint32_t attrib) { #if defined( WIN32 ) && !defined( __BORLANDC__ ) return ((*f=_open(name,attrib | O_CREAT,_S_IWRITE | _S_IREAD))!=(-1)); #else return ((*f=open(name,attrib | O_CREAT,S_IWRITE | S_IREAD))!=(-1)); #endif } abyss_bool FileWrite(TFile * const f, const void * const buffer, uint32_t const len) { #if defined( WIN32 ) && !defined( __BORLANDC__ ) return (_write(*f,buffer,len)==(int32_t)len); #else return (write(*f,buffer,len)==(int32_t)len); #endif } int32_t FileRead(TFile *f, void *buffer, uint32_t len) { #if defined( WIN32 ) && !defined( __BORLANDC__ ) return (_read(*f,buffer,len)); #else return (read(*f,buffer,len)); #endif } abyss_bool FileSeek(TFile *f, uint64_t pos, uint32_t attrib) { #if defined( WIN32 ) && !defined( __BORLANDC__ ) return (_lseek(*f,pos,attrib)!=(-1)); #else return (lseek(*f,pos,attrib)!=(-1)); #endif } uint64_t FileSize(TFile *f) { #if defined( WIN32 ) && !defined( __BORLANDC__ ) return (_filelength(*f)); #else struct stat fs; fstat(*f,&fs); return (fs.st_size); #endif } abyss_bool FileClose(TFile *f) { #if defined( WIN32 ) && !defined( __BORLANDC__ ) return (_close(*f)!=(-1)); #else return (close(*f)!=(-1)); #endif } abyss_bool FileStat(const char * const filename, TFileStat * const filestat) { #if defined( WIN32 ) && !defined( __BORLANDC__ ) return (_stati64(filename,filestat)!=(-1)); #else return (stat(filename,filestat)!=(-1)); #endif } abyss_bool FileFindFirst(TFileFind * const filefind, const char * const path, TFileInfo * const fileinfo) { #ifdef WIN32 abyss_bool ret; char *p=path+strlen(path); *p='\\'; *(p+1)='*'; *(p+2)='\0'; #ifndef __BORLANDC__ ret=(((*filefind)=_findfirst(path,fileinfo))!=(-1)); #else *filefind = FindFirstFile( path, &fileinfo->data ); ret = *filefind != NULL; if( ret ) { LARGE_INTEGER li; li.LowPart = fileinfo->data.nFileSizeLow; li.HighPart = fileinfo->data.nFileSizeHigh; strcpy( fileinfo->name, fileinfo->data.cFileName ); fileinfo->attrib = fileinfo->data.dwFileAttributes; fileinfo->size = li.QuadPart; fileinfo->time_write = fileinfo->data.ftLastWriteTime.dwLowDateTime; } #endif *p='\0'; return ret; #else /* WIN32 */ strncpy(filefind->path,path,NAME_MAX); filefind->path[NAME_MAX]='\0'; filefind->handle=opendir(path); if (filefind->handle) return FileFindNext(filefind,fileinfo); return FALSE; #endif /* WIN32 */ } abyss_bool FileFindNext(TFileFind *filefind,TFileInfo *fileinfo) { #ifdef WIN32 #ifndef __BORLANDC__ return (_findnext(*filefind,fileinfo)!=(-1)); #else abyss_bool ret = FindNextFile( *filefind, &fileinfo->data ); if( ret ) { LARGE_INTEGER li; li.LowPart = fileinfo->data.nFileSizeLow; li.HighPart = fileinfo->data.nFileSizeHigh; strcpy( fileinfo->name, fileinfo->data.cFileName ); fileinfo->attrib = fileinfo->data.dwFileAttributes; fileinfo->size = li.QuadPart; fileinfo->time_write = fileinfo->data.ftLastWriteTime.dwLowDateTime; } return ret; #endif #else /* WIN32 */ struct dirent *de; /****** Must be changed ***/ char z[NAME_MAX+1]; de=readdir(filefind->handle); if (de) { struct stat fs; strcpy(fileinfo->name,de->d_name); strcpy(z,filefind->path); strncat(z,"/",NAME_MAX); strncat(z,fileinfo->name,NAME_MAX); z[NAME_MAX]='\0'; stat(z,&fs); if (fs.st_mode & S_IFDIR) fileinfo->attrib=A_SUBDIR; else fileinfo->attrib=0; fileinfo->size=fs.st_size; fileinfo->time_write=fs.st_mtime; return TRUE; }; return FALSE; #endif /* WIN32 */ } void FileFindClose(TFileFind *filefind) { #ifdef WIN32 #ifndef __BORLANDC__ _findclose(*filefind); #else FindClose( *filefind ); #endif #else /* WIN32 */ closedir(filefind->handle); #endif /* WIN32 */ } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_file.h000066400000000000000000000046041300170765700232160ustar00rootroot00000000000000#ifndef FILE_H_INCLUDED #define FILE_H_INCLUDED #include #include #include #include #include #ifndef NAME_MAX #define NAME_MAX 1024 #endif #ifdef WIN32 #ifndef __BORLANDC__ #define O_APPEND _O_APPEND #define O_CREAT _O_CREAT #define O_EXCL _O_EXCL #define O_RDONLY _O_RDONLY #define O_RDWR _O_RDWR #define O_TRUNC _O_TRUNC #define O_WRONLY _O_WRONLY #define O_TEXT _O_TEXT #define O_BINARY _O_BINARY #endif #define A_HIDDEN _A_HIDDEN #define A_NORMAL _A_NORMAL #define A_RDONLY _A_RDONLY #define A_SUBDIR _A_SUBDIR #else #define A_SUBDIR 1 #define O_BINARY 0 #define O_TEXT 0 #endif /* WIN32 */ #ifdef WIN32 #ifndef __BORLANDC__ typedef struct _stati64 TFileStat; typedef struct _finddata_t TFileInfo; typedef long TFileFind; #else /* WIN32 */ typedef struct stat TFileStat; typedef struct finddata_t { char name[NAME_MAX+1]; int attrib; uint64_t size; time_t time_write; WIN32_FIND_DATA data; } TFileInfo; typedef HANDLE TFileFind; #endif /* WIN32 */ #else #include #include typedef struct stat TFileStat; typedef struct finddata_t { char name[NAME_MAX+1]; int attrib; uint64_t size; time_t time_write; } TFileInfo; typedef struct { char path[NAME_MAX+1]; DIR *handle; } TFileFind; #endif typedef int TFile; abyss_bool FileOpen(TFile * const f, const char * const name, uint32_t const attrib); abyss_bool FileOpenCreate(TFile * const f, const char * const name, uint32_t const attrib); abyss_bool FileClose(TFile * const f); abyss_bool FileWrite(TFile * const f, const void * const buffer, uint32_t const len); int32_t FileRead(TFile * const f, void * const buffer, uint32_t const len); abyss_bool FileSeek(TFile * const f, uint64_t const pos, uint32_t const attrib); uint64_t FileSize(TFile * const f); abyss_bool FileStat(const char * const filename, TFileStat * const filestat); abyss_bool FileFindFirst(TFileFind * const filefind, const char * const path, TFileInfo * const fileinfo); abyss_bool FileFindNext(TFileFind * const filefind, TFileInfo * const fileinfo); void FileFindClose(TFileFind * const filefind); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_http.c000066400000000000000000000572531300170765700232610ustar00rootroot00000000000000/* Copyright information is at the end of the file */ #include #include #include #include #include #include #include #include #include "abyss_mallocvar.h" #include "abyss_xmlrpc_int.h" #include #include "abyss_server.h" #include "abyss_session.h" #include "abyss_conn.h" #include "abyss_token.h" #include "abyss_date.h" #include "abyss_data.h" #include "abyss_info.h" #include "abyss_http.h" /********************************************************************* ** Request Parser *********************************************************************/ /********************************************************************* ** Request *********************************************************************/ static void initRequestInfo(TRequestInfo * const requestInfoP, httpVersion const httpVersion, const char * const requestLine, TMethod const httpMethod, const char * const host, unsigned int const port, const char * const path, const char * const query) { /*---------------------------------------------------------------------------- Set up the request info structure. For information that is controlled by headers, use the defaults -- I.e. the value that applies if the request contains no applicable header. -----------------------------------------------------------------------------*/ requestInfoP->requestline = requestLine; requestInfoP->method = httpMethod; requestInfoP->host = host; requestInfoP->port = port; requestInfoP->uri = path; requestInfoP->query = query; requestInfoP->from = NULL; requestInfoP->useragent = NULL; requestInfoP->referer = NULL; requestInfoP->user = NULL; if (httpVersion.major > 1 || (httpVersion.major == 1 && httpVersion.minor >= 1)) requestInfoP->keepalive = TRUE; else requestInfoP->keepalive = FALSE; } static void freeRequestInfo(TRequestInfo * const requestInfoP) { if (requestInfoP->requestline) xmlrpc_strfree(requestInfoP->requestline); if (requestInfoP->user) xmlrpc_strfree(requestInfoP->user); } void RequestInit(TSession * const sessionP, TConn * const connectionP) { time_t nowtime; sessionP->validRequest = false; /* Don't have valid request yet */ time(&nowtime); sessionP->date = *gmtime(&nowtime); sessionP->conn = connectionP; sessionP->responseStarted = FALSE; sessionP->chunkedwrite = FALSE; sessionP->chunkedwritemode = FALSE; ListInit(&sessionP->cookies); ListInit(&sessionP->ranges); TableInit(&sessionP->request_headers); TableInit(&sessionP->response_headers); sessionP->status = 0; /* No status from handler yet */ StringAlloc(&(sessionP->header)); } void RequestFree(TSession * const sessionP) { if (sessionP->validRequest) freeRequestInfo(&sessionP->request_info); ListFree(&sessionP->cookies); ListFree(&sessionP->ranges); TableFree(&sessionP->request_headers); TableFree(&sessionP->response_headers); StringFree(&(sessionP->header)); } static void readRequestLine(TSession * const sessionP, char ** const requestLineP, uint16_t * const httpErrorCodeP) { *httpErrorCodeP = 0; /* Ignore CRLFs in the beginning of the request (RFC2068-P30) */ do { abyss_bool success; success = ConnReadHeader(sessionP->conn, requestLineP); if (!success) *httpErrorCodeP = 408; /* Request Timeout */ } while (!*httpErrorCodeP && (*requestLineP)[0] == '\0'); } static void unescapeUri(char * const uri, abyss_bool * const errorP) { char * x; char * y; x = y = uri; *errorP = FALSE; while (*x && !*errorP) { switch (*x) { case '%': { char c; ++x; c = tolower(*x++); if ((c >= '0') && (c <= '9')) c -= '0'; else if ((c >= 'a') && (c <= 'f')) c -= 'a' - 10; else *errorP = TRUE; if (!*errorP) { char d; d = tolower(*x++); if ((d >= '0') && (d <= '9')) d -= '0'; else if ((d >= 'a') && (d <= 'f')) d -= 'a' - 10; else *errorP = TRUE; if (!*errorP) *y++ = ((c << 4) | d); } } break; default: *y++ = *x++; break; } } *y = '\0'; } static void parseHostPort(char * const hostport, const char ** const hostP, unsigned short * const portP, uint16_t * const httpErrorCodeP) { char * colonPos; colonPos = strchr(hostport, ':'); if (colonPos) { const char * p; uint32_t port; *colonPos = '\0'; /* Split hostport at the colon */ *hostP = hostport; for (p = colonPos + 1, port = 0; isdigit(*p) && port < 65535; (port = port * 10 + (*p - '0')), ++p); *portP = port; if (*p || port == 0) *httpErrorCodeP = 400; /* Bad Request */ else *httpErrorCodeP = 0; } else { *hostP = hostport; *portP = 80; *httpErrorCodeP = 0; } } static void parseRequestUri(char * const requestUri, const char ** const hostP, const char ** const pathP, const char ** const queryP, unsigned short * const portP, uint16_t * const httpErrorCodeP) { /*---------------------------------------------------------------------------- Parse the request URI (in the request line "GET http://www.myserver.com/myfile?parm HTTP/1.1", "http://www.myserver.com/myfile?parm" is the request URI). This destroys *requestUri and returns pointers into *requestUri! This is extremely ugly. We need to redo it with dynamically allocated storage. We should return individual malloc'ed strings. -----------------------------------------------------------------------------*/ abyss_bool error; unescapeUri(requestUri, &error); if (error) *httpErrorCodeP = 400; /* Bad Request */ else { char * requestUriNoQuery; /* The request URI with any query (the stuff marked by a question mark at the end of a request URI) chopped off. */ { /* Split requestUri at the question mark */ char * const qmark = strchr(requestUri, '?'); if (qmark) { *qmark = '\0'; *queryP = qmark + 1; } else *queryP = NULL; } requestUriNoQuery = requestUri; if (requestUriNoQuery[0] == '/') { *hostP = NULL; *pathP = requestUriNoQuery; *portP = 80; } else { if (!xmlrpc_strneq(requestUriNoQuery, "http://", 7)) *httpErrorCodeP = 400; /* Bad Request */ else { char * const hostportpath = &requestUriNoQuery[7]; char * const slashPos = strchr(hostportpath, '/'); char * hostport; if (slashPos) { char * p; *pathP = slashPos; /* Nul-terminate the host name. To make space for it, slide the whole name back one character. This moves it into the space now occupied by the end of "http://", which we don't need. */ for (p = hostportpath; *p != '/'; ++p) *(p-1) = *p; *(p-1) = '\0'; hostport = hostportpath - 1; *httpErrorCodeP = 0; } else { *pathP = "*"; hostport = hostportpath; *httpErrorCodeP = 0; } if (!*httpErrorCodeP) parseHostPort(hostport, hostP, portP, httpErrorCodeP); } } } } static void parseRequestLine(char * const requestLine, TMethod * const httpMethodP, httpVersion * const httpVersionP, const char ** const hostP, unsigned short * const portP, const char ** const pathP, const char ** const queryP, abyss_bool * const moreLinesP, uint16_t * const httpErrorCodeP) { /*---------------------------------------------------------------------------- Modifies *header1 and returns pointers to its storage! -----------------------------------------------------------------------------*/ const char * httpMethodName; char * p; p = requestLine; /* Jump over spaces */ NextToken((const char **)&p); httpMethodName = GetToken(&p); if (!httpMethodName) *httpErrorCodeP = 400; /* Bad Request */ else { char * requestUri; if (xmlrpc_streq(httpMethodName, "GET")) *httpMethodP = m_get; else if (xmlrpc_streq(httpMethodName, "PUT")) *httpMethodP = m_put; else if (xmlrpc_streq(httpMethodName, "OPTIONS")) *httpMethodP = m_options; else if (xmlrpc_streq(httpMethodName, "DELETE")) *httpMethodP = m_delete; else if (xmlrpc_streq(httpMethodName, "POST")) *httpMethodP = m_post; else if (xmlrpc_streq(httpMethodName, "TRACE")) *httpMethodP = m_trace; else if (xmlrpc_streq(httpMethodName, "HEAD")) *httpMethodP = m_head; else *httpMethodP = m_unknown; /* URI and Query Decoding */ NextToken((const char **)&p); requestUri = GetToken(&p); if (!requestUri) *httpErrorCodeP = 400; /* Bad Request */ else { parseRequestUri(requestUri, hostP, pathP, queryP, portP, httpErrorCodeP); if (!*httpErrorCodeP) { const char * httpVersion; NextToken((const char **)&p); /* HTTP Version Decoding */ httpVersion = GetToken(&p); if (httpVersion) { uint32_t vmin, vmaj; if (sscanf(httpVersion, "HTTP/%d.%d", &vmaj, &vmin) != 2) *httpErrorCodeP = 400; /* Bad Request */ else { httpVersionP->major = vmaj; httpVersionP->minor = vmin; *httpErrorCodeP = 0; /* no error */ } *moreLinesP = TRUE; } else { /* There is no HTTP version, so this is a single line request. */ *httpErrorCodeP = 0; /* no error */ *moreLinesP = FALSE; } } } } } static void strtolower(char * const s) { char * t; t = &s[0]; while (*t) { *t = tolower(*t); ++t; } } static void getFieldNameToken(char ** const pP, char ** const fieldNameP, uint16_t * const httpErrorCodeP) { char * fieldName; NextToken((const char **)pP); fieldName = GetToken(pP); if (!fieldName) *httpErrorCodeP = 400; /* Bad Request */ else { if (fieldName[strlen(fieldName)-1] != ':') /* Not a valid field name */ *httpErrorCodeP = 400; /* Bad Request */ else { fieldName[strlen(fieldName)-1] = '\0'; /* remove trailing colon */ strtolower(fieldName); *httpErrorCodeP = 0; /* no error */ *fieldNameP = fieldName; } } } static void processHeader(const char * const fieldName, char * const fieldValue, TSession * const sessionP, uint16_t * const httpErrorCodeP) { /*---------------------------------------------------------------------------- We may modify *fieldValue, and we put pointers to *fieldValue and *fieldName into *sessionP. We must fix this some day. *sessionP should point to individual malloc'ed strings. -----------------------------------------------------------------------------*/ *httpErrorCodeP = 0; /* initial assumption */ if (xmlrpc_streq(fieldName, "connection")) { if (xmlrpc_strcaseeq(fieldValue, "keep-alive")) sessionP->request_info.keepalive = TRUE; else sessionP->request_info.keepalive = FALSE; } else if (xmlrpc_streq(fieldName, "host")) parseHostPort(fieldValue, &sessionP->request_info.host, &sessionP->request_info.port, httpErrorCodeP); else if (xmlrpc_streq(fieldName, "from")) sessionP->request_info.from = fieldValue; else if (xmlrpc_streq(fieldName, "user-agent")) sessionP->request_info.useragent = fieldValue; else if (xmlrpc_streq(fieldName, "referer")) sessionP->request_info.referer = fieldValue; else if (xmlrpc_streq(fieldName, "range")) { if (xmlrpc_strneq(fieldValue, "bytes=", 6)) { abyss_bool succeeded; succeeded = ListAddFromString(&sessionP->ranges, &fieldValue[6]); *httpErrorCodeP = succeeded ? 0 : 400; } } else if (xmlrpc_streq(fieldName, "cookies")) { abyss_bool succeeded; succeeded = ListAddFromString(&sessionP->cookies, fieldValue); *httpErrorCodeP = succeeded ? 0 : 400; } } abyss_bool RequestRead(TSession * const sessionP) { uint16_t httpErrorCode; /* zero for no error */ char * requestLine; readRequestLine(sessionP, &requestLine, &httpErrorCode); if (!httpErrorCode) { TMethod httpMethod; const char * host; const char * path; const char * query; unsigned short port; abyss_bool moreHeaders=false; parseRequestLine(requestLine, &httpMethod, &sessionP->version, &host, &port, &path, &query, &moreHeaders, &httpErrorCode); if (!httpErrorCode) initRequestInfo(&sessionP->request_info, sessionP->version, strdup(requestLine), httpMethod, host, port, path, query); while (moreHeaders && !httpErrorCode) { char * p; abyss_bool succeeded; succeeded = ConnReadHeader(sessionP->conn, &p); if (!succeeded) httpErrorCode = 408; /* Request Timeout */ else { if (!*p) /* We have reached the empty line so all the request was read. */ moreHeaders = FALSE; else { char * fieldName; getFieldNameToken(&p, &fieldName, &httpErrorCode); if (!httpErrorCode) { char * fieldValue; NextToken((const char **)&p); fieldValue = p; TableAdd(&sessionP->request_headers, fieldName, fieldValue); processHeader(fieldName, fieldValue, sessionP, &httpErrorCode); } } } } } if (httpErrorCode) ResponseStatus(sessionP, httpErrorCode); else sessionP->validRequest = true; return !httpErrorCode; } char *RequestHeaderValue(TSession *r,char *name) { return (TableFind(&r->request_headers,name)); } abyss_bool RequestValidURI(TSession * const sessionP) { if (!sessionP->request_info.uri) return FALSE; if (xmlrpc_streq(sessionP->request_info.uri, "*")) return (sessionP->request_info.method != m_options); if (strchr(sessionP->request_info.uri, '*')) return FALSE; return TRUE; } abyss_bool RequestValidURIPath(TSession * const sessionP) { uint32_t i; const char * p; p = sessionP->request_info.uri; i = 0; if (*p == '/') { i = 1; while (*p) if (*(p++) == '/') { if (*p == '/') break; else if ((strncmp(p,"./",2) == 0) || (strcmp(p, ".") == 0)) ++p; else if ((strncmp(p, "../", 2) == 0) || (strcmp(p, "..") == 0)) { p += 2; --i; if (i == 0) break; } /* Prevent accessing hidden files (starting with .) */ else if (*p == '.') return FALSE; else if (*p) ++i; } } return (*p == 0 && i > 0); } abyss_bool RequestAuth(TSession *r,char *credential,char *user,char *pass) { char *p,*x; char z[80],t[80]; p=RequestHeaderValue(r,"authorization"); if (p) { NextToken((const char **)&p); x=GetToken(&p); if (x) { if (strcasecmp(x,"basic")==0) { NextToken((const char **)&p); sprintf(z,"%s:%s",user,pass); Base64Encode(z,t); if (strcmp(p,t)==0) { r->request_info.user=strdup(user); return TRUE; }; }; } }; sprintf(z,"Basic realm=\"%s\"",credential); ResponseAddField(r,"WWW-Authenticate",z); ResponseStatus(r,401); return FALSE; } /********************************************************************* ** Range *********************************************************************/ abyss_bool RangeDecode(char *str,uint64_t filesize,uint64_t *start,uint64_t *end) { char *ss; *start=0; *end=filesize-1; if (*str=='-') { *start=filesize-strtol(str+1,&ss,10); return ((ss!=str) && (!*ss)); }; *start=strtol(str,&ss,10); if ((ss==str) || (*ss!='-')) return FALSE; str=ss+1; if (!*str) return TRUE; *end=strtol(str,&ss,10); if ((ss==str) || (*ss) || (*end<*start)) return FALSE; return TRUE; } /********************************************************************* ** HTTP *********************************************************************/ const char * HTTPReasonByStatus(uint16_t const code) { struct _HTTPReasons { uint16_t status; const char * reason; }; static struct _HTTPReasons const reasons[] = { { 100,"Continue" }, { 101,"Switching Protocols" }, { 200,"OK" }, { 201,"Created" }, { 202,"Accepted" }, { 203,"Non-Authoritative Information" }, { 204,"No Content" }, { 205,"Reset Content" }, { 206,"Partial Content" }, { 300,"Multiple Choices" }, { 301,"Moved Permanently" }, { 302,"Moved Temporarily" }, { 303,"See Other" }, { 304,"Not Modified" }, { 305,"Use Proxy" }, { 400,"Bad Request" }, { 401,"Unauthorized" }, { 402,"Payment Required" }, { 403,"Forbidden" }, { 404,"Not Found" }, { 405,"Method Not Allowed" }, { 406,"Not Acceptable" }, { 407,"Proxy Authentication Required" }, { 408,"Request Timeout" }, { 409,"Conflict" }, { 410,"Gone" }, { 411,"Length Required" }, { 412,"Precondition Failed" }, { 413,"Request Entity Too Large" }, { 414,"Request-URI Too Long" }, { 415,"Unsupported Media Type" }, { 500,"Internal Server Error" }, { 501,"Not Implemented" }, { 502,"Bad Gateway" }, { 503,"Service Unavailable" }, { 504,"Gateway Timeout" }, { 505,"HTTP Version Not Supported" }, { 000, NULL } }; const struct _HTTPReasons * reasonP; reasonP = &reasons[0]; while (reasonP->status <= code) if (reasonP->status == code) return reasonP->reason; else ++reasonP; return "No Reason"; } int32_t HTTPRead(TSession * const s ATTR_UNUSED, const char * const buffer ATTR_UNUSED, uint32_t const len ATTR_UNUSED) { return 0; } abyss_bool HTTPWriteBodyChunk(TSession * const sessionP, const char * const buffer, uint32_t const len) { abyss_bool succeeded; if (sessionP->chunkedwrite && sessionP->chunkedwritemode) { char chunkHeader[16]; sprintf(chunkHeader, "%x\r\n", len); succeeded = ConnWrite(sessionP->conn, chunkHeader, strlen(chunkHeader)); if (succeeded) { succeeded = ConnWrite(sessionP->conn, buffer, len); if (succeeded) succeeded = ConnWrite(sessionP->conn, "\r\n", 2); } } else succeeded = ConnWrite(sessionP->conn, buffer, len); return succeeded; } abyss_bool HTTPWriteEndChunk(TSession * const sessionP) { abyss_bool retval; if (sessionP->chunkedwritemode && sessionP->chunkedwrite) { /* May be one day trailer dumping will be added */ sessionP->chunkedwritemode = FALSE; retval = ConnWrite(sessionP->conn, "0\r\n\r\n", 5); } else retval = TRUE; return retval; } abyss_bool HTTPKeepalive(TSession * const sessionP) { /*---------------------------------------------------------------------------- Return value: the connection should be kept alive after the session *sessionP is over. -----------------------------------------------------------------------------*/ return (sessionP->request_info.keepalive && !sessionP->serverDeniesKeepalive && sessionP->status < 400); } /****************************************************************************** ** ** http.c ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ******************************************************************************/ opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_http.h000066400000000000000000000021611300170765700232520ustar00rootroot00000000000000#ifndef HTTP_H_INCLUDED #define HTTP_H_INCLUDED #include "abyss_conn.h" /********************************************************************* ** Request *********************************************************************/ abyss_bool RequestValidURI(TSession *r); abyss_bool RequestValidURIPath(TSession *r); abyss_bool RequestUnescapeURI(TSession *r); abyss_bool RequestRead(TSession *r); void RequestInit(TSession *r,TConn *c); void RequestFree(TSession *r); abyss_bool RequestAuth(TSession *r,char *credential,char *user,char *pass); /********************************************************************* ** HTTP *********************************************************************/ const char * HTTPReasonByStatus(uint16_t const code); int32_t HTTPRead(TSession * const sessionP, const char * const buffer, uint32_t const len); abyss_bool HTTPWriteBodyChunk(TSession * const sessionP, const char * const buffer, uint32_t const len); abyss_bool HTTPWriteEndChunk(TSession * const sessionP); abyss_bool HTTPKeepalive(TSession * const sessionP); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_info.h000066400000000000000000000011271300170765700232270ustar00rootroot00000000000000#ifndef ABYSS_INFO_H_INCLUDED #define ABYSS_INFO_H_INCLUDED #define SERVER_VERSION "1.06" #define SERVER_HVERSION "XMLRPC_ABYSS/1.06" #define SERVER_HTML_INFO \ "


" \ "ABYSS Web Server for XML-RPC For C/C++ " \ "version "SERVER_VERSION"
" \ "

" #define SERVER_PLAIN_INFO \ CRLF "----------------------------------------" \ "----------------------------------------" \ CRLF "ABYSS Web Server for XML-RPC For C/C++ " \ "version "SERVER_VERSION CRLF"See xmlrpc-c.sourceforge.net" #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_mallocvar.h000066400000000000000000000056211300170765700242570ustar00rootroot00000000000000/* These are some dynamic memory allocation facilities. They are essentially an extension to C, as they do allocations with a cognizance of C variables. You can use them to make C read more like a high level language. Before including this, you must define an __inline__ macro if your compiler doesn't recognize it as a keyword. */ #ifndef MALLOCVAR_INCLUDED #define MALLOCVAR_INCLUDED #include #include #include static __inline__ void mallocProduct(void ** const resultP, unsigned int const factor1, unsigned int const factor2) { /*---------------------------------------------------------------------------- malloc a space whose size in bytes is the product of 'factor1' and 'factor2'. But if that size cannot be represented as an unsigned int, return NULL without allocating anything. Also return NULL if the malloc fails. If either factor is zero, malloc a single byte. Note that malloc() actually takes a size_t size argument, so the proper test would be whether the size can be represented by size_t, not unsigned int. But there is no reliable indication available to us, like UINT_MAX, of what the limitations of size_t are. We assume size_t is at least as expressive as unsigned int and that nobody really needs to allocate more than 4GB of memory. -----------------------------------------------------------------------------*/ if (factor1 == 0 || factor2 == 0) *resultP = malloc(1); else { if (UINT_MAX / factor2 < factor1) *resultP = NULL; else *resultP = malloc(factor1 * factor2); } } static __inline__ void reallocProduct(void ** const blockP, unsigned int const factor1, unsigned int const factor2) { if (UINT_MAX / factor2 < factor1) *blockP = NULL; else *blockP = realloc(*blockP, factor1 * factor2); } /* IMPLEMENTATION NOTE: There are huge strict aliasing pitfalls here if you cast pointers, e.g. (void **) */ #define MALLOCARRAY(arrayName, nElements) do { \ void * array; \ mallocProduct(&array, nElements, sizeof(arrayName[0])); \ arrayName = array; \ } while (0) #define REALLOCARRAY(arrayName, nElements) { \ void * array = arrayName; \ reallocProduct(&array, nElements, sizeof(arrayName[0])); \ arrayName = array; \ } while (0) #define MALLOCARRAY_NOFAIL(arrayName, nElements) \ do { \ MALLOCARRAY(arrayName, nElements); \ if ((arrayName) == NULL) \ abort(); \ } while(0) #define REALLOCARRAY_NOFAIL(arrayName, nElements) \ do { \ REALLOCARRAY(arrayName, nElements); \ if ((arrayName) == NULL) \ abort(); \ } while(0) #define MALLOCVAR(varName) \ varName = malloc(sizeof(*varName)) #define MALLOCVAR_NOFAIL(varName) \ do {if ((varName = malloc(sizeof(*varName))) == NULL) abort();} while(0) #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_response.c000066400000000000000000000366661300170765700241450ustar00rootroot00000000000000/*============================================================================= response =============================================================================== This module contains callbacks from and services for a request handler. Copyright information is at the end of the file =============================================================================*/ #include #include #include #include #include #include #include #include #include "abyss_mallocvar.h" #include "abyss_xmlrpc_int.h" #include #include "abyss_server.h" #include "abyss_session.h" #include "abyss_conn.h" #include "abyss_token.h" #include "abyss_date.h" #include "abyss_data.h" #include "abyss_info.h" #include "abyss_http.h" void ResponseError(TSession * const sessionP) { const char * const reason = HTTPReasonByStatus(sessionP->status); const char * errorDocument; ResponseAddField(sessionP, "Content-type", "text/html"); ResponseWriteStart(sessionP); xmlrpc_asprintf(&errorDocument, "Error %d" "

Error %d

%s

" SERVER_HTML_INFO "", sessionP->status, sessionP->status, reason); ConnWrite(sessionP->conn, errorDocument, strlen(errorDocument)); xmlrpc_strfree(errorDocument); } abyss_bool ResponseChunked(TSession * const sessionP) { /* This is only a hope, things will be real only after a call of ResponseWriteStart() */ assert(!sessionP->responseStarted); sessionP->chunkedwrite = (sessionP->version.major > 1) || (sessionP->version.major == 1 && (sessionP->version.minor >= 1)); sessionP->chunkedwritemode = TRUE; return TRUE; } void ResponseStatus(TSession * const sessionP, uint16_t const code) { sessionP->status = code; } uint16_t ResponseStatusFromErrno(int const errnoArg) { uint16_t code; switch (errnoArg) { case EACCES: code=403; break; case ENOENT: code=404; break; default: code=500; } return code; } void ResponseStatusErrno(TSession * const sessionP) { ResponseStatus(sessionP, ResponseStatusFromErrno(errno)); } abyss_bool ResponseAddField(TSession * const sessionP, const char * const name, const char * const value) { return TableAdd(&sessionP->response_headers, name, value); } static void addDateHeader(TSession * const sessionP) { char dateValue[64]; abyss_bool validDate; validDate = DateToString(&sessionP->date, dateValue); if (sessionP->status >= 200 && validDate) ResponseAddField(sessionP, "Date", dateValue); } void ResponseWriteStart(TSession * const sessionP) { struct _TServer * const srvP = ConnServer(sessionP->conn)->srvP; unsigned int i; assert(!sessionP->responseStarted); if (sessionP->status == 0) { // Handler hasn't set status. That's an error sessionP->status = 500; } sessionP->responseStarted = TRUE; { const char * const reason = HTTPReasonByStatus(sessionP->status); const char * line; xmlrpc_asprintf(&line,"HTTP/1.1 %u %s\r\n", sessionP->status, reason); ConnWrite(sessionP->conn, line, strlen(line)); xmlrpc_strfree(line); } if (HTTPKeepalive(sessionP)) { const char * keepaliveValue; ResponseAddField(sessionP, "Connection", "Keep-Alive"); xmlrpc_asprintf(&keepaliveValue, "timeout=%u, max=%u", srvP->keepalivetimeout, srvP->keepalivemaxconn); ResponseAddField(sessionP, "Keep-Alive", keepaliveValue); xmlrpc_strfree(keepaliveValue); } else ResponseAddField(sessionP, "Connection", "close"); if (sessionP->chunkedwrite && sessionP->chunkedwritemode) ResponseAddField(sessionP, "Transfer-Encoding", "chunked"); addDateHeader(sessionP); /* Generation of the server field */ if (srvP->advertise) ResponseAddField(sessionP, "Server", SERVER_HVERSION); /* send all the fields */ for (i = 0; i < sessionP->response_headers.size; ++i) { TTableItem * const ti = &sessionP->response_headers.item[i]; const char * line; xmlrpc_asprintf(&line, "%s: %s\r\n", ti->name, ti->value); ConnWrite(sessionP->conn, line, strlen(line)); xmlrpc_strfree(line); } ConnWrite(sessionP->conn, "\r\n", 2); } abyss_bool ResponseWriteBody(TSession * const sessionP, const char * const data, uint32_t const len) { return HTTPWriteBodyChunk(sessionP, data, len); } abyss_bool ResponseWriteEnd(TSession * const sessionP) { return HTTPWriteEndChunk(sessionP); } abyss_bool ResponseContentType(TSession * const serverP, const char * const type) { return ResponseAddField(serverP, "Content-type", type); } abyss_bool ResponseContentLength(TSession * const sessionP, uint64_t const len) { char contentLengthValue[32]; sprintf(contentLengthValue, "%llu", (long long unsigned int)len); return ResponseAddField(sessionP, "Content-length", contentLengthValue); } /********************************************************************* ** MIMEType *********************************************************************/ struct MIMEType { TList typeList; TList extList; TPool pool; }; static MIMEType * globalMimeTypeP = NULL; MIMEType * MIMETypeCreate(void) { MIMEType * MIMETypeP; MALLOCVAR(MIMETypeP); if (MIMETypeP) { ListInit(&MIMETypeP->typeList); ListInit(&MIMETypeP->extList); PoolCreate(&MIMETypeP->pool, 1024); } return MIMETypeP; } void MIMETypeDestroy(MIMEType * const MIMETypeP) { PoolFree(&MIMETypeP->pool); } void MIMETypeInit(void) { if (globalMimeTypeP != NULL) abort(); globalMimeTypeP = MIMETypeCreate(); } void MIMETypeTerm(void) { if (globalMimeTypeP == NULL) abort(); MIMETypeDestroy(globalMimeTypeP); globalMimeTypeP = NULL; } static void mimeTypeAdd(MIMEType * const MIMETypeP, const char * const type, const char * const ext, abyss_bool * const successP) { uint16_t index; void * mimeTypesItem; abyss_bool typeIsInList; assert(MIMETypeP != NULL); typeIsInList = ListFindString(&MIMETypeP->typeList, type, &index); if (typeIsInList) mimeTypesItem = MIMETypeP->typeList.item[index]; else mimeTypesItem = (void*)PoolStrdup(&MIMETypeP->pool, type); if (mimeTypesItem) { abyss_bool extIsInList; extIsInList = ListFindString(&MIMETypeP->extList, ext, &index); if (extIsInList) { MIMETypeP->typeList.item[index] = mimeTypesItem; *successP = TRUE; } else { void * extItem = (void*)PoolStrdup(&MIMETypeP->pool, ext); if (extItem) { abyss_bool addedToMimeTypes; addedToMimeTypes = ListAdd(&MIMETypeP->typeList, mimeTypesItem); if (addedToMimeTypes) { abyss_bool addedToExt; addedToExt = ListAdd(&MIMETypeP->extList, extItem); *successP = addedToExt; if (!*successP) ListRemove(&MIMETypeP->typeList); } else *successP = FALSE; if (!*successP) PoolReturn(&MIMETypeP->pool, extItem); } else *successP = FALSE; } } else *successP = FALSE; } abyss_bool MIMETypeAdd2(MIMEType * const MIMETypeArg, const char * const type, const char * const ext) { MIMEType * MIMETypeP = MIMETypeArg ? MIMETypeArg : globalMimeTypeP; abyss_bool success; if (MIMETypeP == NULL) success = FALSE; else mimeTypeAdd(MIMETypeP, type, ext, &success); return success; } abyss_bool MIMETypeAdd(const char * const type, const char * const ext) { return MIMETypeAdd2(globalMimeTypeP, type, ext); } static const char * mimeTypeFromExt(MIMEType * const MIMETypeP, const char * const ext) { const char * retval; uint16_t extindex; abyss_bool extIsInList; assert(MIMETypeP != NULL); extIsInList = ListFindString(&MIMETypeP->extList, ext, &extindex); if (!extIsInList) retval = NULL; else retval = MIMETypeP->typeList.item[extindex]; return retval; } const char * MIMETypeFromExt2(MIMEType * const MIMETypeArg, const char * const ext) { const char * retval; MIMEType * MIMETypeP = MIMETypeArg ? MIMETypeArg : globalMimeTypeP; if (MIMETypeP == NULL) retval = NULL; else retval = mimeTypeFromExt(MIMETypeP, ext); return retval; } const char * MIMETypeFromExt(const char * const ext) { return MIMETypeFromExt2(globalMimeTypeP, ext); } static void findExtension(const char * const fileName, const char ** const extP) { unsigned int extPos = 0; /* stifle unset variable warning */ /* Running estimation of where in fileName[] the extension starts */ abyss_bool extFound; unsigned int i; /* We're looking for the last dot after the last slash */ for (i = 0, extFound = FALSE; fileName[i]; ++i) { char const c = fileName[i]; if (c == '.') { extFound = TRUE; extPos = i + 1; } if (c == '/') extFound = FALSE; } if (extFound) *extP = &fileName[extPos]; else *extP = NULL; } static const char * mimeTypeFromFileName(MIMEType * const MIMETypeP, const char * const fileName) { const char * retval; const char * ext; assert(MIMETypeP != NULL); findExtension(fileName, &ext); if (ext) retval = MIMETypeFromExt2(MIMETypeP, ext); else retval = "application/octet-stream"; return retval; } const char * MIMETypeFromFileName2(MIMEType * const MIMETypeArg, const char * const fileName) { const char * retval; MIMEType * MIMETypeP = MIMETypeArg ? MIMETypeArg : globalMimeTypeP; if (MIMETypeP == NULL) retval = NULL; else retval = mimeTypeFromFileName(MIMETypeP, fileName); return retval; } const char * MIMETypeFromFileName(const char * const fileName) { return MIMETypeFromFileName2(globalMimeTypeP, fileName); } static abyss_bool fileContainsText(const char * const fileName) { /*---------------------------------------------------------------------------- Return true iff we can read the contents of the file named 'fileName' and see that it appears to be composed of plain text characters. -----------------------------------------------------------------------------*/ abyss_bool retval; abyss_bool fileOpened; TFile file; fileOpened = FileOpen(&file, fileName, O_BINARY | O_RDONLY); if (fileOpened) { char const ctlZ = 26; unsigned char buffer[80]; int32_t readRc; unsigned int i; readRc = FileRead(&file, buffer, sizeof(buffer)); if (readRc >= 0) { unsigned int bytesRead = readRc; abyss_bool nonTextFound; nonTextFound = FALSE; /* initial value */ for (i = 0; i < bytesRead; ++i) { char const c = buffer[i]; if (c < ' ' && !isspace(c) && c != ctlZ) nonTextFound = TRUE; } retval = !nonTextFound; } else retval = FALSE; FileClose(&file); } else retval = FALSE; return retval; } static const char * mimeTypeGuessFromFile(MIMEType * const MIMETypeP, const char * const fileName) { const char * retval; const char * ext; findExtension(fileName, &ext); retval = NULL; if (ext && MIMETypeP) retval = MIMETypeFromExt2(MIMETypeP, ext); if (!retval) { if (fileContainsText(fileName)) retval = "text/plain"; else retval = "application/octet-stream"; } return retval; } const char * MIMETypeGuessFromFile2(MIMEType * const MIMETypeArg, const char * const fileName) { return mimeTypeGuessFromFile(MIMETypeArg ? MIMETypeArg : globalMimeTypeP, fileName); } const char * MIMETypeGuessFromFile(const char * const fileName) { return mimeTypeGuessFromFile(globalMimeTypeP, fileName); } /********************************************************************* ** Base64 *********************************************************************/ void Base64Encode(char *s,char *d) { /* Conversion table. */ static char tbl[64] = { 'A','B','C','D','E','F','G','H', 'I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X', 'Y','Z','a','b','c','d','e','f', 'g','h','i','j','k','l','m','n', 'o','p','q','r','s','t','u','v', 'w','x','y','z','0','1','2','3', '4','5','6','7','8','9','+','/' }; uint32_t i,length=strlen(s); char *p=d; /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ for (i = 0; i < length; i += 3) { *p++ = tbl[s[0] >> 2]; *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; *p++ = tbl[s[2] & 0x3f]; s += 3; } /* Pad the result if necessary... */ if (i == length + 1) *(p - 1) = '='; else if (i == length + 2) *(p - 1) = *(p - 2) = '='; /* ...and zero-terminate it. */ *p = '\0'; } /****************************************************************************** ** ** http.c ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ******************************************************************************/ opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_server.c000066400000000000000000001347731300170765700236130ustar00rootroot00000000000000/* Copyright information is at end of file */ #include #include #include #include #include #include #ifdef WIN32 #include #else #include #include #endif #include #include #include "abyss_mallocvar.h" #include "abyss_xmlrpc_int.h" #include #include "abyss_trace.h" #include "abyss_session.h" #include "abyss_conn.h" #include "abyss_socket.h" #ifdef WIN32 #include "socket_win.h" #else #include "abyss_socket_unix.h" #endif #include "abyss_http.h" #include "abyss_date.h" #include "abyss_info.h" #include "abyss_server.h" #include "../../dprint.h" void ServerTerminate(TServer * const serverP) { struct _TServer * const srvP = serverP->srvP; srvP->terminationRequested = true; } void ServerResetTerminate(TServer * const serverP) { struct _TServer * const srvP = serverP->srvP; srvP->terminationRequested = false; } typedef int (*TQSortProc)(const void *, const void *); static int cmpfilenames(const TFileInfo **f1,const TFileInfo **f2) { if (((*f1)->attrib & A_SUBDIR) && !((*f2)->attrib & A_SUBDIR)) return (-1); if (!((*f1)->attrib & A_SUBDIR) && ((*f2)->attrib & A_SUBDIR)) return 1; return strcmp((*f1)->name,(*f2)->name); } static int cmpfiledates(const TFileInfo **f1,const TFileInfo **f2) { if (((*f1)->attrib & A_SUBDIR) && !((*f2)->attrib & A_SUBDIR)) return (-1); if (!((*f1)->attrib & A_SUBDIR) && ((*f2)->attrib & A_SUBDIR)) return 1; return ((*f1)->time_write-(*f2)->time_write); } static void determineSortType(const char * const query, abyss_bool * const ascendingP, uint16_t * const sortP, abyss_bool * const textP, const char ** const errorP) { *ascendingP = TRUE; *sortP = 1; *textP = FALSE; *errorP = NULL; if (query) { if (xmlrpc_streq(query, "plain")) *textP = TRUE; else if (xmlrpc_streq(query, "name-up")) { *sortP = 1; *ascendingP = TRUE; } else if (xmlrpc_streq(query, "name-down")) { *sortP = 1; *ascendingP = FALSE; } else if (xmlrpc_streq(query, "date-up")) { *sortP = 2; *ascendingP = TRUE; } else if (xmlrpc_streq(query, "date-down")) { *sortP = 2; *ascendingP = FALSE; } else { xmlrpc_asprintf(errorP, "invalid query value '%s'", query); } } } static void generateListing(TList * const listP, char * const z, const char * const uri, TPool * const poolP, const char ** const errorP, uint16_t * const responseStatusP) { TFileInfo fileinfo; TFileFind findhandle; *errorP = NULL; if (!FileFindFirst(&findhandle, z, &fileinfo)) { *responseStatusP = ResponseStatusFromErrno(errno); xmlrpc_asprintf(errorP, "Can't read first entry in directory"); } else { ListInit(listP); do { TFileInfo * fi; /* Files whose names start with a dot are ignored */ /* This includes implicitly the ./ and ../ */ if (*fileinfo.name == '.') { if (xmlrpc_streq(fileinfo.name, "..")) { if (xmlrpc_streq(uri, "/")) continue; } else continue; } fi = (TFileInfo *)PoolAlloc(poolP, sizeof(fileinfo)); if (fi) { abyss_bool success; memcpy(fi, &fileinfo, sizeof(fileinfo)); success = ListAdd(listP, fi); if (!success) xmlrpc_asprintf(errorP, "ListAdd() failed"); } else xmlrpc_asprintf(errorP, "PoolAlloc() failed."); } while (!*errorP && FileFindNext(&findhandle, &fileinfo)); if (*errorP) { *responseStatusP = 500; ListFree(listP); } FileFindClose(&findhandle); } } static void sendDirectoryDocument(TList * const listP, abyss_bool const ascending, uint16_t const sort, abyss_bool const text, const char * const uri, MIMEType * const mimeTypeP, TSession * const sessionP, char * const z) { char *p,z1[26],z2[20],z3[9],u; const char * z4; int16_t i; uint32_t k; if (text) { sprintf(z, "Index of %s" CRLF, uri); i = strlen(z)-2; p = z + i + 2; while (i > 0) { *(p++) = '-'; --i; } *p = '\0'; strcat(z, CRLF CRLF "Name Size " "Date-Time Type" CRLF "------------------------------------" "--------------------------------------------"CRLF); } else { sprintf(z, "Index of %s" "

Index of %s

",
                uri, uri);
        strcat(z, "Name                      Size      "
               "Date-Time             Type
"CRLF); } HTTPWriteBodyChunk(sessionP, z, strlen(z)); /* Sort the files */ qsort(listP->item, listP->size, sizeof(void *), (TQSortProc)(sort == 1 ? cmpfilenames : cmpfiledates)); /* Write the listing */ if (ascending) i = 0; else i = listP->size - 1; while ((i < listP->size) && (i >= 0)) { TFileInfo * fi; struct tm ftm; fi = listP->item[i]; if (ascending) ++i; else --i; strcpy(z, fi->name); k = strlen(z); if (fi->attrib & A_SUBDIR) { z[k++] = '/'; z[k] = '\0'; } if (k > 24) { z[10] = '\0'; strcpy(z1, z); strcat(z1, "..."); strcat(z1, z + k - 11); k = 24; p = z1 + 24; } else { strcpy(z1, z); ++k; p = z1 + k; while (k < 25) z1[k++] = ' '; z1[25] = '\0'; } ftm = *gmtime(&fi->time_write); sprintf(z2, "%02u/%02u/%04u %02u:%02u:%02u",ftm.tm_mday,ftm.tm_mon+1, ftm.tm_year+1900,ftm.tm_hour,ftm.tm_min,ftm.tm_sec); if (fi->attrib & A_SUBDIR) { strcpy(z3, " -- "); z4 = "Directory"; } else { if (fi->size < 9999) u = 'b'; else { fi->size /= 1024; if (fi->size < 9999) u = 'K'; else { fi->size /= 1024; if (fi->size < 9999) u = 'M'; else u = 'G'; } } sprintf(z3, "%5llu %c", (long long unsigned int)fi->size, u); if (xmlrpc_streq(fi->name, "..")) z4 = ""; else z4 = MIMETypeFromFileName2(mimeTypeP, fi->name); if (!z4) z4 = "Unknown"; } if (text) sprintf(z, "%s%s %s %s %s"CRLF, z1, p, z3, z2, z4); else sprintf(z, "%s%s %s %s %s"CRLF, fi->name, fi->attrib & A_SUBDIR ? "/" : "", z1, p, z3, z2, z4); HTTPWriteBodyChunk(sessionP, z, strlen(z)); } /* Write the tail of the file */ if (text) strcpy(z, SERVER_PLAIN_INFO); else strcpy(z, "
" SERVER_HTML_INFO "" CRLF CRLF); HTTPWriteBodyChunk(sessionP, z, strlen(z)); } static void fileDate(TSession * const sessionP, time_t const statFilemodTime, TDate * const fileDateP) { abyss_bool haveDate; TDate filemodDate; haveDate = DateFromLocal(&filemodDate, statFilemodTime); if (haveDate) { if (DateCompare(&sessionP->date, &filemodDate) < 0) *fileDateP = sessionP->date; else *fileDateP = filemodDate; } else *fileDateP = sessionP->date; } static abyss_bool ServerDirectoryHandler(TSession * const r, char * const z, time_t const fileModTime, MIMEType * const mimeTypeP) { TList list; abyss_bool text; abyss_bool ascending; uint16_t sort; /* 1=by name, 2=by date */ TPool pool; TDate date; const char * error; uint16_t responseStatus=0; TDate dirdate; const char * imsHdr; determineSortType(r->request_info.query, &ascending, &sort, &text, &error); if (error) { ResponseStatus(r,400); xmlrpc_strfree(error); return TRUE; } fileDate(r, fileModTime, &dirdate); imsHdr = RequestHeaderValue(r, "If-Modified-Since"); if (imsHdr) { if (DateDecode(imsHdr, &date)) { if (DateCompare(&dirdate, &date) <= 0) { ResponseStatus(r, 304); ResponseWrite(r); return TRUE; } } } if (!PoolCreate(&pool, 1024)) { ResponseStatus(r, 500); return TRUE; } generateListing(&list, z, r->request_info.uri, &pool, &error, &responseStatus); if (error) { ResponseStatus(r, responseStatus); xmlrpc_strfree(error); PoolFree(&pool); return TRUE; } /* Send something to the user to show that we are still alive */ ResponseStatus(r, 200); ResponseContentType(r, (text ? "text/plain" : "text/html")); if (DateToString(&dirdate, z)) ResponseAddField(r, "Last-Modified", z); ResponseChunked(r); ResponseWrite(r); if (r->request_info.method!=m_head) sendDirectoryDocument(&list, ascending, sort, text, r->request_info.uri, mimeTypeP, r, z); HTTPWriteEndChunk(r); /* Free memory and exit */ ListFree(&list); PoolFree(&pool); return TRUE; } #define BOUNDARY "##123456789###BOUNDARY" static void sendBody(TSession * const sessionP, TFile * const fileP, uint64_t const filesize, const char * const mediatype, uint64_t const start0, uint64_t const end0, char * const z) { if (sessionP->ranges.size == 0) ConnWriteFromFile(sessionP->conn, fileP, 0, filesize - 1, z, 4096, 0); else if (sessionP->ranges.size == 1) ConnWriteFromFile(sessionP->conn, fileP, start0, end0, z, 4096, 0); else { uint64_t i; for (i = 0; i <= sessionP->ranges.size; ++i) { ConnWrite(sessionP->conn,"--", 2); ConnWrite(sessionP->conn, BOUNDARY, strlen(BOUNDARY)); ConnWrite(sessionP->conn, CRLF, 2); if (i < sessionP->ranges.size) { uint64_t start; uint64_t end; abyss_bool decoded; decoded = RangeDecode((char *)(sessionP->ranges.item[i]), filesize, &start, &end); if (decoded) { /* Entity header, not response header */ sprintf(z, "Content-type: %s" CRLF "Content-range: bytes %llu-%llu/%llu" CRLF "Content-length: %llu" CRLF CRLF, mediatype, (long long unsigned int)start, (long long unsigned int)end, (long long unsigned int)filesize, (long long unsigned int)(end-start+1)); ConnWrite(sessionP->conn, z, strlen(z)); ConnWriteFromFile(sessionP->conn, fileP, start, end, z, 4096, 0); } } } } } static abyss_bool ServerFileHandler(TSession * const r, char * const z, time_t const fileModTime, MIMEType * const mimeTypeP) { const char * mediatype; TFile file; uint64_t filesize; uint64_t start; uint64_t end; TDate date; char * p; TDate filedate; mediatype = MIMETypeGuessFromFile2(mimeTypeP, z); if (!FileOpen(&file,z,O_BINARY | O_RDONLY)) { ResponseStatusErrno(r); return TRUE; } fileDate(r, fileModTime, &filedate); p = RequestHeaderValue(r, "if-modified-since"); if (p) { if (DateDecode(p,&date)) { if (DateCompare(&filedate, &date) <= 0) { ResponseStatus(r, 304); ResponseWrite(r); return TRUE; } else r->ranges.size = 0; } } filesize = FileSize(&file); switch (r->ranges.size) { case 0: ResponseStatus(r, 200); break; case 1: { abyss_bool decoded; decoded = RangeDecode((char *)(r->ranges.item[0]), filesize, &start, &end); if (!decoded) { ListFree(&(r->ranges)); ResponseStatus(r, 200); break; } sprintf(z, "bytes %llu-%llu/%llu", (long long unsigned int)start, (long long unsigned int)end, (long long unsigned int)filesize); ResponseAddField(r, "Content-range", z); ResponseContentLength(r, end - start + 1); ResponseStatus(r, 206); } break; default: ResponseContentType(r, "multipart/ranges; boundary=" BOUNDARY); ResponseStatus(r, 206); break; } if (r->ranges.size == 0) { ResponseContentLength(r, filesize); ResponseContentType(r, mediatype); } if (DateToString(&filedate, z)) ResponseAddField(r, "Last-Modified", z); ResponseWrite(r); if (r->request_info.method != m_head) sendBody(r, &file, filesize, mediatype, start, end, z); FileClose(&file); return TRUE; } static abyss_bool ServerDefaultHandlerFunc(TSession * const sessionP) { struct _TServer * const srvP = ConnServer(sessionP->conn)->srvP; char *p; char z[4096]; TFileStat fs; unsigned int i; abyss_bool endingslash=FALSE; if (!RequestValidURIPath(sessionP)) { ResponseStatus(sessionP, 400); return TRUE; } /* Must check for * (asterisk uri) in the future */ if (sessionP->request_info.method == m_options) { ResponseAddField(sessionP, "Allow", "GET, HEAD"); ResponseContentLength(sessionP, 0); ResponseStatus(sessionP, 200); return TRUE; } if ((sessionP->request_info.method != m_get) && (sessionP->request_info.method != m_head)) { ResponseAddField(sessionP, "Allow", "GET, HEAD"); ResponseStatus(sessionP, 405); return TRUE; } strcpy(z, srvP->filespath); strcat(z, sessionP->request_info.uri); p = z + strlen(z) - 1; if (*p == '/') { endingslash = TRUE; *p = '\0'; } #ifdef WIN32 p = z; while (*p) { if ((*p) == '/') *p= '\\'; ++p; } #endif /* WIN32 */ if (!FileStat(z, &fs)) { ResponseStatusErrno(sessionP); return TRUE; } if (fs.st_mode & S_IFDIR) { /* Redirect to the same directory but with the ending slash ** to avoid problems with some browsers (IE for examples) when ** they generate relative urls */ if (!endingslash) { strcpy(z, sessionP->request_info.uri); p = z+strlen(z); *p = '/'; *(p+1) = '\0'; ResponseAddField(sessionP, "Location", z); ResponseStatus(sessionP, 302); ResponseWrite(sessionP); return TRUE; } *p = DIRECTORY_SEPARATOR[0]; ++p; i = srvP->defaultfilenames.size; while (i-- > 0) { *p = '\0'; strcat(z, (srvP->defaultfilenames.item[i])); if (FileStat(z, &fs)) { if (!(fs.st_mode & S_IFDIR)) return ServerFileHandler(sessionP, z, fs.st_mtime, srvP->mimeTypeP); } } *(p-1) = '\0'; if (!FileStat(z, &fs)) { ResponseStatusErrno(sessionP); return TRUE; } return ServerDirectoryHandler(sessionP, z, fs.st_mtime, srvP->mimeTypeP); } else return ServerFileHandler(sessionP, z, fs.st_mtime, srvP->mimeTypeP); } static void initUnixStuff(struct _TServer * const srvP) { #ifndef WIN32 srvP->pidfile = srvP->uid = srvP->gid = -1; #endif } static abyss_bool logOpen(struct _TServer * const srvP) { abyss_bool success; success = FileOpenCreate(&srvP->logfile, srvP->logfilename, O_WRONLY | O_APPEND); if (success) { abyss_bool success; success = MutexCreate(&srvP->logmutex); if (success) srvP->logfileisopen = TRUE; else TraceMsg("Can't create mutex for log file"); if (!success) FileClose(&srvP->logfile); } else TraceMsg("Can't open log file '%s'", srvP->logfilename); return success; } static void logClose(struct _TServer * const srvP) { if (srvP->logfileisopen) { FileClose(&srvP->logfile); MutexFree(&srvP->logmutex); srvP->logfileisopen = FALSE; } } static void initSocketStuff(struct _TServer * const srvP, abyss_bool const noAccept, TSocket * const userSocketP, uint16_t const port, const char ** const errorP) { if (userSocketP) { *errorP = NULL; srvP->serverAcceptsConnections = TRUE; srvP->socketBound = TRUE; srvP->listenSocketP = userSocketP; } else if (noAccept) { *errorP = NULL; srvP->serverAcceptsConnections = FALSE; srvP->socketBound = FALSE; } else { *errorP = NULL; srvP->serverAcceptsConnections = TRUE; srvP->socketBound = FALSE; srvP->port = port; } srvP->weCreatedListenSocket = FALSE; } static void createServer(struct _TServer ** const srvPP, abyss_bool const noAccept, TSocket * const userSocketP, uint16_t const portNumber, const char ** const errorP) { struct _TServer * srvP; MALLOCVAR(srvP); if (srvP == NULL) { xmlrpc_asprintf(errorP, "Unable to allocate space for server descriptor"); } else { srvP->terminationRequested = false; initSocketStuff(srvP, noAccept, userSocketP, portNumber, errorP); if (!*errorP) { srvP->defaulthandler = ServerDefaultHandlerFunc; srvP->name = strdup("unnamed"); srvP->filespath = strdup(DEFAULT_DOCS); srvP->logfilename = NULL; srvP->keepalivetimeout = 15; srvP->keepalivemaxconn = 30; srvP->timeout = 15; srvP->advertise = TRUE; srvP->mimeTypeP = NULL; srvP->useSigchld = FALSE; initUnixStuff(srvP); ListInitAutoFree(&srvP->handlers); ListInitAutoFree(&srvP->defaultfilenames); srvP->logfileisopen = FALSE; *errorP = NULL; } if (*errorP) free(srvP); } *srvPP = srvP; } static void setNamePathLog(TServer * const serverP, const char * const name, const char * const filesPath, const char * const logFileName) { /*---------------------------------------------------------------------------- This odd function exists to help with backward compatibility. Today, we have the expandable model where you create a server with default parameters, then use ServerSet... functions to choose non-default parameters. But before, you specified these three parameters right in the arguments of various create functions. -----------------------------------------------------------------------------*/ if (name) ServerSetName(serverP, name); if (filesPath) ServerSetFilesPath(serverP, filesPath); if (logFileName) ServerSetLogFileName(serverP, logFileName); } abyss_bool ServerCreate(TServer * const serverP, const char * const name, uint16_t const portNumber, const char * const filesPath, const char * const logFileName) { abyss_bool const noAcceptFalse = FALSE; abyss_bool success; const char * error; createServer(&serverP->srvP, noAcceptFalse, NULL, portNumber, &error); if (error) { TraceMsg(error); xmlrpc_strfree(error); success = FALSE; } else { success = TRUE; setNamePathLog(serverP, name, filesPath, logFileName); } return success; } static void createSocketFromOsSocket(TOsSocket const osSocket, TSocket ** const socketPP) { #ifdef WIN32 SocketWinCreateWinsock(osSocket, socketPP); #else SocketUnixCreateFd(osSocket, socketPP); #endif } abyss_bool ServerCreateSocket(TServer * const serverP, const char * const name, TOsSocket const socketFd, const char * const filesPath, const char * const logFileName) { abyss_bool success; TSocket * socketP; createSocketFromOsSocket(socketFd, &socketP); if (socketP) { abyss_bool const noAcceptFalse = FALSE; const char * error; createServer(&serverP->srvP, noAcceptFalse, socketP, 0, &error); if (error) { TraceMsg(error); success = FALSE; xmlrpc_strfree(error); } else { success = TRUE; setNamePathLog(serverP, name, filesPath, logFileName); } } else success = FALSE; return success; } abyss_bool ServerCreateNoAccept(TServer * const serverP, const char * const name, const char * const filesPath, const char * const logFileName) { abyss_bool const noAcceptTrue = TRUE; abyss_bool success; const char * error; createServer(&serverP->srvP, noAcceptTrue, NULL, 0, &error); if (error) { TraceMsg(error); success = FALSE; xmlrpc_strfree(error); } else { success = TRUE; setNamePathLog(serverP, name, filesPath, logFileName); } return success; } void ServerCreateSocket2(TServer * const serverP, TSocket * const socketP, const char ** const errorP) { abyss_bool const noAcceptFalse = FALSE; assert(socketP); createServer(&serverP->srvP, noAcceptFalse, socketP, 0, errorP); } static void terminateHandlers(TList * const handlersP) { /*---------------------------------------------------------------------------- Terminate all handlers in the list '*handlersP'. I.e. call each handler's terminate function. -----------------------------------------------------------------------------*/ if (handlersP->item) { unsigned int i; for (i = handlersP->size; i > 0; --i) { URIHandler2 * const handlerP = handlersP->item[i-1]; if (handlerP->term) handlerP->term(handlerP->userdata); } } } void ServerFree(TServer * const serverP) { struct _TServer * const srvP = serverP->srvP; if (srvP->weCreatedListenSocket) SocketDestroy(srvP->listenSocketP); xmlrpc_strfree(srvP->name); xmlrpc_strfree(srvP->filespath); ListFree(&srvP->defaultfilenames); terminateHandlers(&srvP->handlers); ListFree(&srvP->handlers); logClose(srvP); if (srvP->logfilename) xmlrpc_strfree(srvP->logfilename); free(srvP); } void ServerSetName(TServer * const serverP, const char * const name) { xmlrpc_strfree(serverP->srvP->name); serverP->srvP->name = strdup(name); } void ServerSetFilesPath(TServer * const serverP, const char * const filesPath) { xmlrpc_strfree(serverP->srvP->filespath); serverP->srvP->filespath = strdup(filesPath); } void ServerSetLogFileName(TServer * const serverP, const char * const logFileName) { struct _TServer * const srvP = serverP->srvP; if (srvP->logfilename) xmlrpc_strfree(srvP->logfilename); srvP->logfilename = strdup(logFileName); } void ServerSetKeepaliveTimeout(TServer * const serverP, uint32_t const keepaliveTimeout) { serverP->srvP->keepalivetimeout = keepaliveTimeout; } void ServerSetKeepaliveMaxConn(TServer * const serverP, uint32_t const keepaliveMaxConn) { serverP->srvP->keepalivemaxconn = keepaliveMaxConn; } void ServerSetTimeout(TServer * const serverP, uint32_t const timeout) { serverP->srvP->timeout = timeout; } void ServerSetAdvertise(TServer * const serverP, abyss_bool const advertise) { serverP->srvP->advertise = advertise; } void ServerSetMimeType(TServer * const serverP, MIMEType * const MIMETypeP) { serverP->srvP->mimeTypeP = MIMETypeP; } static void runUserHandler(TSession * const sessionP, struct _TServer * const srvP) { abyss_bool handled; int i; for (i = srvP->handlers.size-1, handled = FALSE; i >= 0 && !handled; --i) { URIHandler2 * const handlerP = srvP->handlers.item[i]; if (handlerP->handleReq2) handlerP->handleReq2(handlerP, sessionP, &handled); else if (handlerP->handleReq1) handled = handlerP->handleReq1(sessionP); } if (!handled) ((URIHandler)(srvP->defaulthandler))(sessionP); } static void processDataFromClient(TConn * const connectionP, abyss_bool const lastReqOnConn, abyss_bool * const keepAliveP) { TSession session; RequestInit(&session, connectionP); session.serverDeniesKeepalive = lastReqOnConn; RequestRead(&session); if (session.status == 0) { if (session.version.major >= 2) ResponseStatus(&session, 505); else if (!RequestValidURI(&session)) ResponseStatus(&session, 400); else runUserHandler(&session, connectionP->server->srvP); } assert(session.status != 0); if (session.responseStarted) HTTPWriteEndChunk(&session); else ResponseError(&session); *keepAliveP = HTTPKeepalive(&session); SessionLog(&session); RequestFree(&session); } static TThreadProc serverFunc; static void serverFunc(void * const userHandle) { /*---------------------------------------------------------------------------- Do server stuff on one connection. At its simplest, this means do one HTTP request. But with keepalive, it can be many requests. -----------------------------------------------------------------------------*/ TConn * const connectionP = userHandle; struct _TServer * const srvP = connectionP->server->srvP; unsigned int requestCount; /* Number of requests we've handled so far on this connection */ abyss_bool connectionDone; /* No more need for this HTTP connection */ requestCount = 0; connectionDone = FALSE; while (!connectionDone) { abyss_bool success; /* Wait to read until timeout */ success = ConnRead(connectionP, srvP->keepalivetimeout); if (!success) connectionDone = TRUE; else { abyss_bool const lastReqOnConn = requestCount + 1 >= srvP->keepalivemaxconn; abyss_bool keepalive; processDataFromClient(connectionP, lastReqOnConn, &keepalive); ++requestCount; if (!keepalive) connectionDone = TRUE; /**************** Must adjust the read buffer *****************/ ConnReadInit(connectionP); } } } static void createAndBindSocket(struct _TServer * const srvP) { abyss_bool success; success = SocketInit(); if (!success) TraceMsg("Can't initialize TCP sockets"); else { TSocket * socketP; SocketUnixCreate(&socketP); if (!socketP) TraceMsg("Can't create a socket"); else { abyss_bool success; success = SocketBind(socketP, NULL, srvP->port); if (!success) TraceMsg("Failed to bind listening socket to port number %u", srvP->port); else { srvP->weCreatedListenSocket = TRUE; srvP->socketBound = TRUE; srvP->listenSocketP = socketP; } if (!success) SocketDestroy(socketP); } } } void ServerInit(TServer * const serverP) { /*---------------------------------------------------------------------------- Initialize a server to accept connections. Do not confuse this with creating the server -- ServerCreate(). Not necessary or valid with a server that doesn't accept connections (i.e. user supplies the TCP connections). -----------------------------------------------------------------------------*/ struct _TServer * const srvP = serverP->srvP; abyss_bool success; if (!srvP->serverAcceptsConnections) { TraceMsg("ServerInit() is not valid on a server that doesn't " "accept connections " "(i.e. created with ServerCreateNoAccept)"); success = FALSE; } else { if (!srvP->socketBound) createAndBindSocket(srvP); if (srvP->socketBound) { success = SocketListen(srvP->listenSocketP, MAX_CONN); if (!success) TraceMsg("Failed to listen on bound socket."); } else success = FALSE; } if (!success) exit(1); } /* We don't do any locking on the outstanding connections list, so we must make sure that only the master thread (the one that listens for connections) ever accesses it. That's why when a thread completes, it places the connection in "finished" status, but doesn't destroy the connection. */ typedef struct { TConn * firstP; unsigned int count; /* Redundant with 'firstP', for quick access */ } outstandingConnList; static void createOutstandingConnList(outstandingConnList ** const listPP) { outstandingConnList * listP; MALLOCVAR_NOFAIL(listP); listP->firstP = NULL; /* empty list */ listP->count = 0; *listPP = listP; } static void destroyOutstandingConnList(outstandingConnList * const listP) { assert(listP->firstP == NULL); assert(listP->count == 0); free(listP); } static void addToOutstandingConnList(outstandingConnList * const listP, TConn * const connectionP) { connectionP->nextOutstandingP = listP->firstP; listP->firstP = connectionP; ++listP->count; } static void freeFinishedConns(outstandingConnList * const listP) { /*---------------------------------------------------------------------------- Garbage-collect the resources associated with connections that are finished with their jobs. Thread resources, connection pool descriptor, etc. -----------------------------------------------------------------------------*/ TConn ** pp; pp = &listP->firstP; while (*pp) { TConn * const connectionP = (*pp); ThreadUpdateStatus(connectionP->threadP); if (connectionP->finished) { /* Take it out of the list */ *pp = connectionP->nextOutstandingP; --listP->count; ConnWaitAndRelease(connectionP); } else { /* Move to next connection in list */ pp = &connectionP->nextOutstandingP; } } } static void waitForConnectionFreed(outstandingConnList * const outstandingConnListP ATTR_UNUSED) { /*---------------------------------------------------------------------------- Wait for a connection descriptor in 'connectionPool' to be probably freed. -----------------------------------------------------------------------------*/ /* TODO: We should do something more sophisticated here. For pthreads, we can have a thread signal us by semaphore when it terminates. For fork, we might be able to use the "done" handler argument to ConnCreate() to get interrupted when the death of a child signal happens. */ xmlrpc_millisecond_sleep(2000); } static void waitForNoConnections(outstandingConnList * const outstandingConnListP) { while (outstandingConnListP->firstP) { freeFinishedConns(outstandingConnListP); if (outstandingConnListP->firstP) waitForConnectionFreed(outstandingConnListP); } } static void waitForConnectionCapacity(outstandingConnList * const outstandingConnListP) { /*---------------------------------------------------------------------------- Wait until there are fewer than the maximum allowed connections in progress. -----------------------------------------------------------------------------*/ while (outstandingConnListP->count >= MAX_CONN) { freeFinishedConns(outstandingConnListP); if (outstandingConnListP->firstP) waitForConnectionFreed(outstandingConnListP); } } #ifndef WIN32 void ServerHandleSigchld(pid_t const pid) { //ThreadHandleSigchld(pid); } #endif void ServerUseSigchld(TServer * const serverP) { struct _TServer * const srvP = serverP->srvP; srvP->useSigchld = TRUE; } TThreadDoneFn destroySocket; static void destroyConnSocket(void * const userHandle) { /*---------------------------------------------------------------------------- This is a "connection done" function for the connection the server serves. It gets called some time after the connection has done its thing. Its job is to clean up stuff the server created for use by the connection, but the server can't clean up because the connection might be processed asynchronously in a background thread. To wit, we destroy the connection's socket. -----------------------------------------------------------------------------*/ TConn * const connectionP = userHandle; SocketDestroy(connectionP->socketP); } static void serverRun2(TServer * const serverP) { struct _TServer * const srvP = serverP->srvP; outstandingConnList * outstandingConnListP; createOutstandingConnList(&outstandingConnListP); while (!srvP->terminationRequested) { TConn * connectionP; abyss_bool connected; abyss_bool failed; TSocket * connectedSocketP; TIPAddr peerIpAddr; SocketAccept(srvP->listenSocketP, &connected, &failed, &connectedSocketP, &peerIpAddr); if (connected) { const char * error; freeFinishedConns(outstandingConnListP); waitForConnectionCapacity(outstandingConnListP); ConnCreate(&connectionP, serverP, connectedSocketP, &serverFunc, &destroyConnSocket, ABYSS_BACKGROUND, srvP->useSigchld, &error); if (!error) { addToOutstandingConnList(outstandingConnListP, connectionP); ConnProcess(connectionP); SocketClose(connectedSocketP); /* When connection is done (which could be later, courtesy of a background thread), destroyConnSocket() will destroy *connectedSocketP. */ } else { xmlrpc_strfree(error); SocketDestroy(connectedSocketP); } } else if (failed) TraceMsg("Socket Error=%d", SocketError(srvP->listenSocketP)); } waitForNoConnections(outstandingConnListP); destroyOutstandingConnList(outstandingConnListP); } void ServerRun(TServer * const serverP) { struct _TServer * const srvP = serverP->srvP; if (!srvP->socketBound) TraceMsg("This server is not set up to accept connections " "on its own, so you can't use ServerRun(). " "Try ServerRunConn() or ServerInit()"); else serverRun2(serverP); } static void serverRunConn(TServer * const serverP, TSocket * const connectedSocketP) { /*---------------------------------------------------------------------------- Do the HTTP transaction on the TCP connection on the socket 'connectedSocketP'. (socket must be in connected state, with nothing having been read or written on the connection yet). -----------------------------------------------------------------------------*/ struct _TServer * const srvP = serverP->srvP; TConn * connectionP; const char * error; srvP->keepalivemaxconn = 1; ConnCreate(&connectionP, serverP, connectedSocketP, &serverFunc, NULL, ABYSS_FOREGROUND, srvP->useSigchld, &error); if (error) { TraceMsg("Couldn't create HTTP connection out of " "connected socket. %s", error); xmlrpc_strfree(error); } else { ConnProcess(connectionP); ConnWaitAndRelease(connectionP); } } void ServerRunConn2(TServer * const serverP, TSocket * const connectedSocketP, const char ** const errorP) { /*---------------------------------------------------------------------------- Do the HTTP transaction on the TCP connection on the socket 'connectedOsSocket'. (socket must be connected state, with nothing having been read or written on the connection yet). -----------------------------------------------------------------------------*/ struct _TServer * const srvP = serverP->srvP; if (srvP->serverAcceptsConnections) xmlrpc_asprintf(errorP, "This server is configured to accept connections on " "its own socket. " "Try ServerRun() or ServerCreateNoAccept()."); else { serverRunConn(serverP, connectedSocketP); *errorP = NULL; } } void ServerRunConn(TServer * const serverP, TOsSocket const connectedOsSocket) { TSocket * socketP; createSocketFromOsSocket(connectedOsSocket, &socketP); if (!socketP) TraceExit("Unable to use supplied socket"); else { const char * error; ServerRunConn2(serverP, socketP, &error); if (error) { TraceExit("Failed to run server on connection on file " "descriptor %d. %s", connectedOsSocket, error); xmlrpc_strfree(error); } SocketDestroy(socketP); } } void ServerRunOnce(TServer * const serverP) { /*---------------------------------------------------------------------------- Accept a connection from the listening socket and do the HTTP transaction that comes over it. If no connection is presently waiting on the listening socket, wait for one. But return immediately if we receive a signal during the wait. -----------------------------------------------------------------------------*/ struct _TServer * const srvP = serverP->srvP; if (!srvP->socketBound) TraceMsg("This server is not set up to accept connections " "on its own, so you can't use ServerRunOnce(). " "Try ServerRunConn() or ServerInit()"); else { abyss_bool connected; abyss_bool failed; TSocket * connectedSocketP; TIPAddr remoteAddr; srvP->keepalivemaxconn = 1; SocketAccept(srvP->listenSocketP, &connected, &failed, &connectedSocketP, &remoteAddr); if (connected) { serverRunConn(serverP, connectedSocketP); SocketDestroy(connectedSocketP); } else if (failed) TraceMsg("Socket Error=%d", SocketError(srvP->listenSocketP)); } } void ServerRunOnce2(TServer * const serverP, enum abyss_foreback const foregroundBackground ATTR_UNUSED) { /*---------------------------------------------------------------------------- This is a backward compatibility interface to ServerRunOnce(). 'foregroundBackground' is meaningless. We always process the connection in the foreground. The parameter exists because we once thought we could do them in the background, but we really can't do that in any clean way. If Caller wants background execution, he can spin his own thread or process to call us. It makes much more sense in Caller's context. -----------------------------------------------------------------------------*/ ServerRunOnce(serverP); } static void setGroups(void) { #ifdef HAVE_SETGROUPS if (setgroups(0, NULL) == (-1)) TraceExit("Failed to setup the group."); #endif } void ServerDaemonize(TServer * const serverP) { /*---------------------------------------------------------------------------- Turn Caller into a daemon (i.e. fork a child, then exit; the child returns to Caller). NOTE: It's ridiculous, but conventional, for us to do this. It's ridiculous because the task of daemonizing is not something particular to Abyss. It ought to be done by a higher level. In fact, it should be done before the Abyss server program is even execed. The user should run a "daemonize" program that creates a daemon which execs the Abyss server program. -----------------------------------------------------------------------------*/ struct _TServer * const srvP = serverP->srvP; #ifndef _WIN32 /* Become a daemon */ switch (fork()) { case 0: break; case -1: TraceExit("Unable to become a daemon"); default: /* We are the parent */ exit(0); } setsid(); /* Change the current user if we are root */ if (getuid()==0) { if (srvP->uid == (uid_t)-1) TraceExit("Can't run under root privileges. " "Please add a User option in your " "Abyss configuration file."); setGroups(); if (srvP->gid != (gid_t)-1) if (setgid(srvP->gid)==(-1)) TraceExit("Failed to change the group."); if (setuid(srvP->uid) == -1) TraceExit("Failed to change the user."); } if (srvP->pidfile != -1) { char z[16]; sprintf(z, "%d", getpid()); FileWrite(&srvP->pidfile, z, strlen(z)); FileClose(&srvP->pidfile); } #endif /* _WIN32 */ } void ServerAddHandler2(TServer * const serverP, URIHandler2 * const handlerArgP, abyss_bool * const successP) { URIHandler2 * handlerP; MALLOCVAR(handlerP); if (handlerP == NULL) *successP = FALSE; else { *handlerP = *handlerArgP; if (handlerP->init == NULL) *successP = TRUE; else handlerP->init(handlerP, successP); if (*successP) *successP = ListAdd(&serverP->srvP->handlers, handlerP); if (!*successP) free(handlerP); } } static URIHandler2 * createHandler(URIHandler const function) { URIHandler2 * handlerP; MALLOCVAR(handlerP); if (handlerP != NULL) { handlerP->init = NULL; handlerP->term = NULL; handlerP->userdata = NULL; handlerP->handleReq2 = NULL; handlerP->handleReq1 = function; } return handlerP; } abyss_bool ServerAddHandler(TServer * const serverP, URIHandler const function) { URIHandler2 * handlerP; abyss_bool success; handlerP = createHandler(function); if (handlerP == NULL) success = FALSE; else { success = ListAdd(&serverP->srvP->handlers, handlerP); if (!success) free(handlerP); } return success; } void ServerDefaultHandler(TServer * const serverP, URIHandler const handler) { serverP->srvP->defaulthandler = handler ? handler : ServerDefaultHandlerFunc; } void LogWrite(TServer * const serverP, const char * const msg) { struct _TServer * const srvP = serverP->srvP; if (!srvP->logfileisopen && srvP->logfilename) logOpen(srvP); if (srvP->logfileisopen) { abyss_bool success; success = MutexLock(&srvP->logmutex); if (success) { const char * const lbr = "\n"; FileWrite(&srvP->logfile, msg, strlen(msg)); FileWrite(&srvP->logfile, lbr, strlen(lbr)); MutexUnlock(&srvP->logmutex); } } } /******************************************************************************* ** ** server.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ******************************************************************************/ opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_server.h000066400000000000000000000047371300170765700236140ustar00rootroot00000000000000#ifndef SERVER_H_INCLUDED #define SERVER_H_INCLUDED #include #include #include "abyss_file.h" #include "abyss_data.h" typedef struct _Tsocket Tsocket; struct _TServer { abyss_bool terminationRequested; /* User wants this server to terminate as soon as possible, in particular before accepting any more connections and without waiting for any. */ abyss_bool socketBound; /* The listening socket exists and is bound to a local address (may already be listening as well) */ TSocket * listenSocketP; /* Meaningful only when 'socketBound' is true: file descriptor of the listening socket ("listening socket" means socket for listening, not a socket that is listening right now). */ abyss_bool weCreatedListenSocket; /* We created the listen socket (whose fd is 'listensock'), as opposed to 1) User supplied it; or 2) there isn't one. */ const char * logfilename; abyss_bool logfileisopen; TFile logfile; TMutex logmutex; const char * name; const char * filespath; abyss_bool serverAcceptsConnections; /* We listen for and accept TCP connections for HTTP transactions. (The alternative is the user supplies a TCP-connected socket for each transaction) */ uint16_t port; /* Meaningful only when 'socketBound' is false: port number to which we should bind the listening socket */ uint32_t keepalivetimeout; uint32_t keepalivemaxconn; uint32_t timeout; /* Maximum time in seconds the server will wait to read a header or a data chunk from the socket. */ TList handlers; TList defaultfilenames; void * defaulthandler; abyss_bool advertise; MIMEType * mimeTypeP; /* NULL means to use the global MIMEType object */ abyss_bool useSigchld; /* Meaningless if not using forking for threads. TRUE means user will call ServerHandleSigchld to indicate that a SIGCHLD signal was received, and server shall use that as its indication that a child has died. FALSE means server will not be aware of SIGCHLD and will instead poll for existence of PIDs to determine if a child has died. */ #ifndef WIN32 uid_t uid; gid_t gid; TFile pidfile; #endif }; void ServerBackgroundProcessComplete(pid_t const pid); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_session.c000066400000000000000000000123741300170765700237600ustar00rootroot00000000000000/****************************************************************************** ** session.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** *******************************************************************************/ #include #include #include #include #include "abyss_xmlrpc_int.h" #include #include "abyss_server.h" #include "abyss_conn.h" #include "abyss_session.h" abyss_bool SessionRefillBuffer(TSession * const sessionP) { /*---------------------------------------------------------------------------- Get the next chunk of data from the connection into the buffer. I.e. read data from the socket. -----------------------------------------------------------------------------*/ struct _TServer * const srvP = sessionP->conn->server->srvP; abyss_bool succeeded; /* Reset our read buffer & flush data from previous reads. */ ConnReadInit(sessionP->conn); /* Read more network data into our buffer. If we encounter a timeout, exit immediately. We're very forgiving about the timeout here. We allow a full timeout per network read, which would allow somebody to keep a connection alive nearly indefinitely. But it's hard to do anything intelligent here without very complicated code. */ succeeded = ConnRead(sessionP->conn, srvP->timeout); return succeeded; } size_t SessionReadDataAvail(TSession * const sessionP) { return sessionP->conn->buffersize - sessionP->conn->bufferpos; } void SessionGetReadData(TSession * const sessionP, size_t const max, const char ** const outStartP, size_t * const outLenP) { /*---------------------------------------------------------------------------- Extract some data which the server has read and buffered for the session. Don't get or wait for any data that has not yet arrived. Do not return more than 'max'. We return a pointer to the first byte as *outStartP, and the length in bytes as *outLenP. The memory pointed to belongs to the session. -----------------------------------------------------------------------------*/ uint32_t const bufferPos = sessionP->conn->bufferpos; *outStartP = &sessionP->conn->buffer[bufferPos]; assert(bufferPos <= sessionP->conn->buffersize); *outLenP = MIN(max, sessionP->conn->buffersize - bufferPos); /* move pointer past the bytes we are returning */ sessionP->conn->bufferpos += *outLenP; assert(sessionP->conn->bufferpos <= sessionP->conn->buffersize); } void SessionGetRequestInfo(TSession * const sessionP, const TRequestInfo ** const requestInfoPP) { *requestInfoPP = &sessionP->request_info; } abyss_bool SessionLog(TSession * const sessionP) { abyss_bool retval; if (!sessionP->validRequest) retval = FALSE; else { const char * const user = sessionP->request_info.user; const char * logline; char date[30]; DateToLogString(&sessionP->date, date); xmlrpc_asprintf(&logline, "%d.%d.%d.%d - %s - [%s] \"%s\" %d %d", IPB1(sessionP->conn->peerip), IPB2(sessionP->conn->peerip), IPB3(sessionP->conn->peerip), IPB4(sessionP->conn->peerip), user ? user : "", date, sessionP->request_info.requestline, sessionP->status, sessionP->conn->outbytes ); if (logline) { LogWrite(sessionP->conn->server, logline); xmlrpc_strfree(logline); } retval = TRUE; } return retval; } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_session.h000066400000000000000000000024651300170765700237650ustar00rootroot00000000000000#ifndef SESSION_H_INCLUDED #define SESSION_H_INCLUDED #include #include "abyss_date.h" #include "abyss_data.h" typedef struct { uint8_t major; uint8_t minor; } httpVersion; struct _TSession { abyss_bool validRequest; /* Client has sent, and server has recognized, a valid HTTP request. This is false when the session is new. If and when the server reads the request from the client and finds it to be valid HTTP, it becomes true. */ TRequestInfo request_info; uint32_t nbfileds; TList cookies; TList ranges; uint16_t status; /* Response status from handler. Zero means handler has not set it. */ TString header; abyss_bool serverDeniesKeepalive; /* Server doesn't want keepalive for this session, regardless of what happens in the session. E.g. because the connection has already been kept alive long enough. */ abyss_bool responseStarted; /* Handler has at least started the response (i.e. called ResponseWrite()) */ struct _TConn * conn; httpVersion version; TTable request_headers; TTable response_headers; TDate date; abyss_bool chunkedwrite; abyss_bool chunkedwritemode; }; #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_socket.c000066400000000000000000000145701300170765700235650ustar00rootroot00000000000000/****************************************************************************** ** socket.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** *******************************************************************************/ /*============================================================================ socket.c ============================================================================== Implementation of TSocket class: A generic socket over which one can transport an HTTP stream or manage HTTP connections ============================================================================*/ #include #include #include #include #include #include #include "abyss_mallocvar.h" #include "abyss_xmlrpc_int.h" #include #ifdef WIN32 #include "socket_win.h" #else #include "abyss_socket_unix.h" #endif #include "abyss_socket.h" #ifdef WIN32 #include "socket_win.h" #else #include "abyss_socket_unix.h" #endif #include "abyss_socket.h" static void socketOsInit(abyss_bool * const succeededP) { #ifdef WIN32 SocketWinInit(succeededP); #else SocketUnixInit(succeededP); #endif } static void socketOsTerm(void) { #ifdef WIN32 SocketWinTerm(); #else SocketUnixTerm(); #endif } abyss_bool SocketTraceIsActive; abyss_bool SocketInit(void) { abyss_bool retval; socketOsInit(&retval); SocketTraceIsActive = (getenv("ABYSS_TRACE_SOCKET") != NULL); if (SocketTraceIsActive) fprintf(stderr, "Abyss socket layer will trace socket traffic " "due to ABYSS_TRACE_SOCKET environment variable\n"); return retval; } void SocketTerm(void) { socketOsTerm(); } /* SocketCreate() is not exported to the Abyss user. It is meant to be used by an implementation-specific TSocket generator which is exported to the Abyss user, e.g. SocketCreateUnix() in socket_unix.c The TSocket generator functions are the _only_ user-accessible functions that are particular to an implementation. */ static uint const socketSignature = 0x060609; void SocketCreate(const struct TSocketVtbl * const vtblP, void * const implP, TSocket ** const socketPP) { TSocket * socketP; MALLOCVAR(socketP); if (socketP) { socketP->implP = implP; socketP->vtbl = *vtblP; socketP->signature = socketSignature; *socketPP = socketP; } } void SocketClose(TSocket * const socketP) { socketP->vtbl.close(socketP); } void SocketDestroy(TSocket * const socketP) { assert(socketP->signature == socketSignature); socketP->vtbl.destroy(socketP); socketP->signature = 0; /* For debuggability */ free(socketP); } void SocketWrite(TSocket * const socketP, const unsigned char * const buffer, uint32_t const len, abyss_bool * const failedP) { (*socketP->vtbl.write)(socketP, buffer, len, failedP ); } uint32_t SocketRead(TSocket * const socketP, unsigned char * const buffer, uint32_t const len) { return (*socketP->vtbl.read)(socketP, (char*)buffer, len); } abyss_bool SocketConnect(TSocket * const socketP, TIPAddr * const addrP, uint16_t const portNumber) { return (*socketP->vtbl.connect)(socketP, addrP, portNumber); } abyss_bool SocketBind(TSocket * const socketP, TIPAddr * const addrP, uint16_t const portNumber) { return (*socketP->vtbl.bind)(socketP, addrP, portNumber); } abyss_bool SocketListen(TSocket * const socketP, uint32_t const backlog) { return (*socketP->vtbl.listen)(socketP, backlog); } void SocketAccept(TSocket * const listenSocketP, abyss_bool * const connectedP, abyss_bool * const failedP, TSocket ** const acceptedSocketPP, TIPAddr * const ipAddrP) { (*listenSocketP->vtbl.accept)(listenSocketP, connectedP, failedP, acceptedSocketPP, ipAddrP); } uint32_t SocketWait(TSocket * const socketP, abyss_bool const rd, abyss_bool const wr, uint32_t const timems) { return (*socketP->vtbl.wait)(socketP, rd, wr, timems); } uint32_t SocketAvailableReadBytes(TSocket * const socketP) { return (*socketP->vtbl.availableReadBytes)(socketP); } void SocketGetPeerName(TSocket * const socketP, TIPAddr * const ipAddrP, uint16_t * const portNumberP, abyss_bool * const successP) { (*socketP->vtbl.getPeerName)(socketP, ipAddrP, portNumberP, successP); } uint32_t SocketError(TSocket * const socketP) { return (*socketP->vtbl.error)(socketP); } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_socket.h000066400000000000000000000115301300170765700235630ustar00rootroot00000000000000#ifndef SOCKET_H_INCLUDED #define SOCKET_H_INCLUDED #include #include #include #define IPB1(x) (((unsigned char *)(&x))[0]) #define IPB2(x) (((unsigned char *)(&x))[1]) #define IPB3(x) (((unsigned char *)(&x))[2]) #define IPB4(x) (((unsigned char *)(&x))[3]) typedef struct in_addr TIPAddr; typedef void SocketCloseImpl(TSocket * const socketP); typedef void SocketDestroyImpl(TSocket * const socketP); typedef void SocketWriteImpl(TSocket * const socketP, const unsigned char * const buffer, uint32_t const len, abyss_bool * const failedP); typedef uint32_t SocketReadImpl(TSocket * const socketP, char * const buffer, uint32_t const len); typedef abyss_bool SocketConnectImpl(TSocket * const socketP, TIPAddr * const addrP, uint16_t const portNumber); typedef abyss_bool SocketBindImpl(TSocket * const socketP, TIPAddr * const addrP, uint16_t const portNumber); typedef abyss_bool SocketListenImpl(TSocket * const socketP, uint32_t const backlog); typedef void SocketAcceptImpl(TSocket * const listenSocketP, abyss_bool * const connectedP, abyss_bool * const failedP, TSocket ** const acceptedSocketPP, TIPAddr * const ipAddrP); typedef uint32_t SocketErrorImpl(TSocket * const socketP); typedef uint32_t SocketWaitImpl(TSocket * const socketP, abyss_bool const rd, abyss_bool const wr, uint32_t const timems); typedef uint32_t SocketAvailableReadBytesImpl(TSocket * const socketP); typedef void SocketGetPeerNameImpl(TSocket * const socketP, TIPAddr * const ipAddrP, uint16_t * const portNumberP, abyss_bool * const successP); struct TSocketVtbl { SocketCloseImpl * close; SocketDestroyImpl * destroy; SocketWriteImpl * write; SocketReadImpl * read; SocketConnectImpl * connect; SocketBindImpl * bind; SocketListenImpl * listen; SocketAcceptImpl * accept; SocketErrorImpl * error; SocketWaitImpl * wait; SocketAvailableReadBytesImpl * availableReadBytes; SocketGetPeerNameImpl * getPeerName; }; struct _TSocket { uint signature; /* With both background and foreground use of sockets, and background being both fork and pthread, it is very easy to screw up socket lifetime and try to destroy twice. We use this signature to help catch such bugs. */ void * implP; struct TSocketVtbl vtbl; }; #define TIME_INFINITE 0xffffffff extern abyss_bool SocketTraceIsActive; abyss_bool SocketInit(void); void SocketTerm(void); void SocketClose(TSocket * const socketP); void SocketCreate(const struct TSocketVtbl * const vtblP, void * const implP, TSocket ** const socketPP); void SocketWrite(TSocket * const socketP, const unsigned char * const buffer, uint32_t const len, abyss_bool * const failedP); uint32_t SocketRead(TSocket * const socketP, unsigned char * const buffer, uint32_t const len); abyss_bool SocketConnect(TSocket * const socketP, TIPAddr * const addrP, uint16_t const portNumber); abyss_bool SocketBind(TSocket * const socketP, TIPAddr * const addrP, uint16_t const portNumber); abyss_bool SocketListen(TSocket * const socketP, uint32_t const backlog); void SocketAccept(TSocket * const listenSocketP, abyss_bool * const connectedP, abyss_bool * const failedP, TSocket ** const acceptedSocketP, TIPAddr * const ipAddrP); uint32_t SocketWait(TSocket * const socketP, abyss_bool const rd, abyss_bool const wr, uint32_t const timems); uint32_t SocketAvailableReadBytes(TSocket * const socketP); void SocketGetPeerName(TSocket * const socketP, TIPAddr * const ipAddrP, uint16_t * const portNumberP, abyss_bool * const successP); uint32_t SocketError(TSocket * const socketP); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_socket_unix.c000066400000000000000000000341141300170765700246240ustar00rootroot00000000000000/****************************************************************************** ** socket_unix.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** *******************************************************************************/ /*============================================================================= socket_unix.c =============================================================================== This is the implementation of TSocket for a standard Unix (POSIX) stream socket -- what you create with a socket() C library call. =============================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_SYS_FILIO_H #include #endif //#if HAVE_SYS_IOCTL_H #include //#endif #include "abyss_xmlrpc_int.h" #include "abyss_mallocvar.h" #include "abyss_trace.h" #include "abyss_socket.h" #include #include "abyss_socket_unix.h" struct socketUnix { /*---------------------------------------------------------------------------- The properties/state of a TSocket unique to a Unix TSocket. -----------------------------------------------------------------------------*/ int fd; /* File descriptor of the POSIX socket (such as is created by socket() in the C library) on which the TSocket is based. */ abyss_bool userSuppliedFd; /* The file descriptor and associated POSIX socket belong to the user; we did not create it. */ }; void SocketUnixInit(abyss_bool * const succeededP) { *succeededP = TRUE; } void SocketUnixTerm(void) { } static SocketCloseImpl socketClose; static SocketDestroyImpl socketDestroy; static SocketWriteImpl socketWrite; static SocketReadImpl socketRead; static SocketConnectImpl socketConnect; static SocketBindImpl socketBind; static SocketListenImpl socketListen; static SocketAcceptImpl socketAccept; static SocketErrorImpl socketError; static SocketWaitImpl socketWait; static SocketAvailableReadBytesImpl socketAvailableReadBytes; static SocketGetPeerNameImpl socketGetPeerName; static struct TSocketVtbl const vtbl = { &socketClose, &socketDestroy, &socketWrite, &socketRead, &socketConnect, &socketBind, &socketListen, &socketAccept, &socketError, &socketWait, &socketAvailableReadBytes, &socketGetPeerName }; void SocketUnixCreate(TSocket ** const socketPP) { struct socketUnix * socketUnixP; MALLOCVAR(socketUnixP); if (socketUnixP) { int rc; rc = socket(AF_INET, SOCK_STREAM, 0); if (rc < 0) *socketPP = NULL; else { socketUnixP->fd = rc; socketUnixP->userSuppliedFd = FALSE; { int32_t n = 1; int rc; rc = setsockopt(socketUnixP->fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof(n)); if (rc < 0) *socketPP = NULL; else SocketCreate(&vtbl, socketUnixP, socketPP); } if (!*socketPP) close(socketUnixP->fd); } if (!*socketPP) free(socketUnixP); } else *socketPP = NULL; } void SocketUnixCreateFd(int const fd, TSocket ** const socketPP) { struct socketUnix * socketUnixP; MALLOCVAR(socketUnixP); if (socketUnixP) { socketUnixP->fd = fd; socketUnixP->userSuppliedFd = TRUE; SocketCreate(&vtbl, socketUnixP, socketPP); if (!*socketPP) free(socketUnixP); } else *socketPP = NULL; } static void socketClose(TSocket * const socketP) { struct socketUnix * const socketUnixP = socketP->implP; if (!socketUnixP->userSuppliedFd && socketUnixP->fd ) { close(socketUnixP->fd); socketUnixP->fd = 0; } } static void socketDestroy(TSocket * const socketP) { struct socketUnix * const socketUnixP = socketP->implP; socketClose(socketP); free(socketUnixP); } static void socketWrite(TSocket * const socketP, const unsigned char * const buffer, uint32_t const len, abyss_bool * const failedP) { struct socketUnix * const socketUnixP = socketP->implP; size_t bytesLeft; abyss_bool error; assert(sizeof(size_t) >= sizeof(len)); for (bytesLeft = len, error = FALSE; bytesLeft > 0 && !error; ) { size_t const maxSend = (size_t)(-1) >> 1; ssize_t rc; rc = send(socketUnixP->fd, &buffer[len-bytesLeft], MIN(maxSend, bytesLeft), 0); if (SocketTraceIsActive) { if (rc < 0) fprintf(stderr, "Abyss socket: send() failed. errno=%d (%s)", errno, strerror(errno)); else if (rc == 0) fprintf(stderr, "Abyss socket: send() failed. " "Socket closed.\n"); else fprintf(stderr, "Abyss socket: sent %u bytes: '%.*s'\n", (unsigned int)-rc, (int)-rc, &buffer[len-bytesLeft]); } if (rc <= 0) /* 0 means connection closed; < 0 means severe error */ error = TRUE; else bytesLeft -= rc; } *failedP = error; } static uint32_t socketRead(TSocket * const socketP, char * const buffer, uint32_t const len) { struct socketUnix * const socketUnixP = socketP->implP; int rc; rc = recv(socketUnixP->fd, buffer, len, 0); if (SocketTraceIsActive) { if (rc < 0) fprintf(stderr, "Abyss socket: recv() failed. errno=%d (%s)", errno, strerror(errno)); else fprintf(stderr, "Abyss socket: read %u bytes: '%.*s'\n", len, (int)len, buffer); } return rc; } abyss_bool socketConnect(TSocket * const socketP, TIPAddr * const addrP, uint16_t const portNumber) { struct socketUnix * const socketUnixP = socketP->implP; struct sockaddr_in name; int rc; name.sin_family = AF_INET; name.sin_port = htons(portNumber); name.sin_addr = *addrP; rc = connect(socketUnixP->fd, (struct sockaddr *)&name, sizeof(name)); return rc != -1; } abyss_bool socketBind(TSocket * const socketP, TIPAddr * const addrP, uint16_t const portNumber) { struct socketUnix * const socketUnixP = socketP->implP; struct sockaddr_in name; int rc; name.sin_family = AF_INET; name.sin_port = htons(portNumber); if (addrP) name.sin_addr = *addrP; else name.sin_addr.s_addr = INADDR_ANY; rc = bind(socketUnixP->fd, (struct sockaddr *)&name, sizeof(name)); return (rc != -1); } abyss_bool socketListen(TSocket * const socketP, uint32_t const backlog) { struct socketUnix * const socketUnixP = socketP->implP; int32_t const minus1 = -1; int rc; /* Disable the Nagle algorithm to make persistant connections faster */ setsockopt(socketUnixP->fd, IPPROTO_TCP,TCP_NODELAY, &minus1, sizeof(minus1)); rc = listen(socketUnixP->fd, backlog); return (rc != -1); } static void socketAccept(TSocket * const listenSocketP, abyss_bool * const connectedP, abyss_bool * const failedP, TSocket ** const acceptedSocketPP, TIPAddr * const ipAddrP) { /*---------------------------------------------------------------------------- Accept a connection on the listening socket 'listenSocketP'. Return as *acceptedSocketPP the socket for the accepted connection. If no connection is waiting on 'listenSocketP', wait until one is. If we receive a signal while waiting, return immediately. Return *connectedP true iff we accepted a connection. Return *failedP true iff we were unable to accept a connection for some reason other than that we were interrupted. Return both false if our wait for a connection was interrupted by a signal. -----------------------------------------------------------------------------*/ struct socketUnix * const listenSocketUnixP = listenSocketP->implP; abyss_bool connected, failed, interrupted; connected = FALSE; failed = FALSE; interrupted = FALSE; while (!connected && !failed && !interrupted) { struct sockaddr_in sa; socklen_t size = sizeof(sa); int rc; rc = accept(listenSocketUnixP->fd, (struct sockaddr *)&sa, &size); if (rc >= 0) { int const acceptedFd = rc; struct socketUnix * acceptedSocketUnixP; MALLOCVAR(acceptedSocketUnixP); if (acceptedSocketUnixP) { acceptedSocketUnixP->fd = acceptedFd; acceptedSocketUnixP->userSuppliedFd = FALSE; SocketCreate(&vtbl, acceptedSocketUnixP, acceptedSocketPP); if (!*acceptedSocketPP) failed = TRUE; else { connected = TRUE; *ipAddrP = sa.sin_addr; } if (failed) free(acceptedSocketUnixP); } else failed = TRUE; if (failed) close(acceptedFd); } else if (errno == EINTR) interrupted = TRUE; else failed = TRUE; } *failedP = failed; *connectedP = connected; } static uint32_t socketWait(TSocket * const socketP, abyss_bool const rd, abyss_bool const wr, uint32_t const timems) { struct socketUnix * const socketUnixP = socketP->implP; fd_set rfds, wfds; struct timeval tv; FD_ZERO(&rfds); FD_ZERO(&wfds); if (rd) FD_SET(socketUnixP->fd, &rfds); if (wr) FD_SET(socketUnixP->fd, &wfds); tv.tv_sec = timems / 1000; tv.tv_usec = timems % 1000; for (;;) { int rc; rc = select(socketUnixP->fd + 1, &rfds, &wfds, NULL, (timems == TIME_INFINITE ? NULL : &tv)); switch(rc) { case 0: /* time out */ return 0; case -1: /* socket error */ if (errno == EINTR) break; return 0; default: if (FD_ISSET(socketUnixP->fd, &rfds)) return 1; if (FD_ISSET(socketUnixP->fd, &wfds)) return 2; return 0; } } } static uint32_t socketAvailableReadBytes(TSocket * const socketP) { struct socketUnix * const socketUnixP = socketP->implP; uint32_t x; int rc; rc = ioctl(socketUnixP->fd, FIONREAD, &x); return rc == 0 ? x : 0; } static void socketGetPeerName(TSocket * const socketP, TIPAddr * const ipAddrP, uint16_t * const portNumberP, abyss_bool * const successP) { struct socketUnix * const socketUnixP = socketP->implP; socklen_t addrlen; int rc; struct sockaddr sockAddr; addrlen = sizeof(sockAddr); rc = getpeername(socketUnixP->fd, &sockAddr, &addrlen); if (rc < 0) { TraceMsg("getpeername() failed. errno=%d (%s)", errno, strerror(errno)); *successP = FALSE; } else { if (addrlen != sizeof(sockAddr)) { TraceMsg("getpeername() returned a socket address of the wrong " "size: %u. Expected %u", addrlen, sizeof(sockAddr)); *successP = FALSE; } else { if (sockAddr.sa_family != AF_INET) { TraceMsg("Socket does not use the Inet (IP) address " "family. Instead it uses family %d", sockAddr.sa_family); *successP = FALSE; } else { struct sockaddr_in * const sockAddrInP = (struct sockaddr_in *) &sockAddr; *ipAddrP = sockAddrInP->sin_addr; *portNumberP = sockAddrInP->sin_port; *successP = TRUE; } } } } static uint32_t socketError(TSocket * const socketP) { if (socketP){} /* defeat compiler warning */ return errno; } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_socket_unix.h000066400000000000000000000002271300170765700246270ustar00rootroot00000000000000#ifndef SOCKET_UNIX_H_INCLUDED #define SOCKET_UNIX_H_INCLUDED void SocketUnixInit(abyss_bool * const succeededP); void SocketUnixTerm(void); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_thread.h000066400000000000000000000027621300170765700235510ustar00rootroot00000000000000#ifndef THREAD_H_INCLUDED #define THREAD_H_INCLUDED /********************************************************************* ** Thread *********************************************************************/ typedef struct abyss_thread TThread; void ThreadPoolInit(void); typedef void TThreadProc(void * const userHandleP); typedef void TThreadDoneFn(void * const userHandleP); void ThreadCreate(TThread ** const threadPP, void * const userHandle, TThreadProc * const func, TThreadDoneFn * const threadDone, abyss_bool const useSigchld, const char ** const errorP); abyss_bool ThreadRun(TThread * const threadP); abyss_bool ThreadStop(TThread * const threadP); abyss_bool ThreadKill(TThread * threadP); void ThreadWaitAndRelease(TThread * const threadP); void ThreadExit(int const retValue); void ThreadRelease(TThread * const threadP); abyss_bool ThreadForks(void); void ThreadUpdateStatus(TThread * const threadP); #ifndef WIN32 void ThreadHandleSigchld(pid_t const pid); #endif /********************************************************************* ** Mutex *********************************************************************/ #ifdef WIN32 typedef HANDLE TMutex; #else #include typedef pthread_mutex_t TMutex; #endif /* WIN32 */ abyss_bool MutexCreate(TMutex *m); abyss_bool MutexLock(TMutex *m); abyss_bool MutexUnlock(TMutex *m); abyss_bool MutexTryLock(TMutex *m); void MutexFree(TMutex *m); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_thread_fork.c000066400000000000000000000206201300170765700245560ustar00rootroot00000000000000/****************************************************************************** ** thread_fork.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** *******************************************************************************/ #include #include #include #include #include #include #include #include #include "abyss_xmlrpc_int.h" #include #include "abyss_mallocvar.h" #include "abyss_thread.h" static void blockSignalClass(int const signalClass, sigset_t * const oldBlockedSetP) { sigset_t newBlockedSet; sigemptyset(&newBlockedSet); sigaddset(&newBlockedSet, signalClass); sigprocmask(SIG_BLOCK, &newBlockedSet, oldBlockedSetP); } struct abyss_thread { struct abyss_thread * nextInPoolP; TThreadDoneFn * threadDone; void * userHandle; pid_t pid; abyss_bool useSigchld; /* This means that user is going to call ThreadHandleSigchld() when it gets a death of a child signal for this process. If false, he's going to leave us in the dark, so we'll have to poll to know if the process is dead or not. */ }; /* Because signals are global, we need this global variable in order for the signal handler to figure out to what thread the signal belongs. */ /* We use a singly linked list. Every time we access it, we have to run the whole chain. To make this scale up, we should replace it with a doubly linked list and some kind of index by PID. But large scale systems probably aren't using fork threads anyway. */ static struct { struct abyss_thread * firstP; } ThreadPool; void ThreadPoolInit(void) { ThreadPool.firstP = NULL; } static struct abyss_thread * findThread(pid_t const pid) { struct abyss_thread * p; for (p = ThreadPool.firstP; p && p->pid != pid; p = p->nextInPoolP); return p; } static void addToPool(struct abyss_thread * const threadP) { if (ThreadPool.firstP == NULL) ThreadPool.firstP = threadP; else { struct abyss_thread * p; for (p = ThreadPool.firstP; p->nextInPoolP; p = p->nextInPoolP); /* p points to the last thread in the list */ p->nextInPoolP = threadP; } } static void removeFromPool(struct abyss_thread * const threadP) { if (threadP == ThreadPool.firstP) ThreadPool.firstP = threadP->nextInPoolP; else { struct abyss_thread * p; for (p = ThreadPool.firstP; p && p->nextInPoolP != threadP; p = p->nextInPoolP); if (p) /* p points to thread right before the one we want to remove */ p->nextInPoolP = threadP->nextInPoolP; } } void ThreadHandleSigchld(pid_t const pid) { /*---------------------------------------------------------------------------- Handle a death of a child signal for process 'pid', which may be one of our threads. -----------------------------------------------------------------------------*/ struct abyss_thread * const threadP = findThread(pid); if (threadP) { if (threadP->threadDone) threadP->threadDone(threadP->userHandle); threadP->pid = 0; } /* Note that threadDone might free *threadP */ } void ThreadUpdateStatus(TThread * const threadP) { if (!threadP->useSigchld) { if (threadP->pid) { if (kill(threadP->pid, 0) != 0) { if (threadP->threadDone) threadP->threadDone(threadP->userHandle); threadP->pid = 0; } } } } void ThreadCreate(TThread ** const threadPP, void * const userHandle, TThreadProc * const func, TThreadDoneFn * const threadDone, abyss_bool const useSigchld, const char ** const errorP) { TThread * threadP; MALLOCVAR(threadP); if (threadP == NULL) xmlrpc_asprintf(errorP, "Can't allocate memory for thread descriptor."); else { sigset_t oldBlockedSet; pid_t rc; threadP->nextInPoolP = NULL; threadP->threadDone = threadDone; threadP->userHandle = userHandle; threadP->useSigchld = useSigchld; threadP->pid = 0; /* We have to be sure we don't get the SIGCHLD for this child's death until the child is properly registered in the thread pool so that the handler will know who he is. */ blockSignalClass(SIGCHLD, &oldBlockedSet); rc = fork(); if (rc < 0) xmlrpc_asprintf(errorP, "fork() failed, errno=%d (%s)", errno, strerror(errno)); else if (rc == 0) { /* This is the child */ (*func)(userHandle); exit(0); } else { /* This is the parent */ threadP->pid = rc; addToPool(threadP); sigprocmask(SIG_SETMASK, &oldBlockedSet, NULL); /* restore */ *errorP = NULL; *threadPP = threadP; } if (*errorP) { removeFromPool(threadP); free(threadP); } } } abyss_bool ThreadRun(TThread * const threadP ATTR_UNUSED) { return TRUE; } abyss_bool ThreadStop(TThread * const threadP ATTR_UNUSED) { return TRUE; } abyss_bool ThreadKill(TThread * const threadP ATTR_UNUSED) { return TRUE; } void ThreadWaitAndRelease(TThread * const threadP) { if (threadP->pid) { int exitStatus; waitpid(threadP->pid, &exitStatus, 0); threadP->threadDone(threadP->userHandle); threadP->pid = 0; } ThreadRelease(threadP); } void ThreadExit(int const retValue) { /* Note that the OS will automatically send a SIGCHLD signal to the parent process after we exit. The handler for that signal will run threadDone in parent's context. Alternatively, if the parent is set up for signals, the parent will eventually poll for the existence of our PID and call threadDone when he sees we've gone. */ exit(retValue); } void ThreadRelease(TThread * const threadP) { removeFromPool(threadP); free(threadP); } abyss_bool ThreadForks(void) { return TRUE; } /********************************************************************* ** Mutex *********************************************************************/ /* As two processes don't share memory, there is nothing to synchronize, so locking is a no-op. */ abyss_bool MutexCreate(TMutex * const mutexP ATTR_UNUSED) { return TRUE; } abyss_bool MutexLock(TMutex * const mutexP ATTR_UNUSED) { return TRUE; } abyss_bool MutexUnlock(TMutex * const mutexP ATTR_UNUSED) { return TRUE; } abyss_bool MutexTryLock(TMutex * const mutexP ATTR_UNUSED) { return TRUE; } void MutexFree(TMutex * const mutexP ATTR_UNUSED) { } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_token.c000066400000000000000000000047401300170765700234130ustar00rootroot00000000000000/****************************************************************************** ** token.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** *******************************************************************************/ #include #include "abyss_token.h" void NextToken(const char ** const pP) { abyss_bool gotToken; gotToken = FALSE; while (!gotToken) { switch (**pP) { case '\t': case ' ': ++(*pP); break; default: gotToken = TRUE; }; } } char * GetToken(char ** const pP) { char * p0; p0 = *pP; while (1) { switch (**pP) { case '\t': case ' ': case CR: case LF: case '\0': if (p0 == *pP) return NULL; if (**pP) { **pP = '\0'; ++(*pP); }; return p0; default: ++(*pP); }; } } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_token.h000066400000000000000000000002241300170765700234110ustar00rootroot00000000000000#ifndef ABYSS_TOKEN_H_INCLUDED #define ABYSS_TOKEN_H_INCLUDED void NextToken(const char ** const pP); char * GetToken(char ** const pP); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_trace.c000066400000000000000000000046151300170765700233720ustar00rootroot00000000000000/****************************************************************************** ** trace.c ** ** This file is part of the ABYSS Web server project. ** ** Copyright (C) 2000 by Moez Mahfoudh . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ******************************************************************************/ #include #include #include #include "abyss_trace.h" /********************************************************************* ** Tracing functions *********************************************************************/ static void TraceVMsg(const char * const fmt, va_list argptr) { vprintf(fmt,argptr); printf("\n"); } void TraceMsg(const char * const fmt, ...) { va_list argptr; va_start(argptr,fmt); TraceVMsg(fmt,argptr); va_end(argptr); } void TraceExit(const char * const fmt, ...) { va_list argptr; va_start(argptr,fmt); TraceVMsg(fmt,argptr); va_end(argptr); exit(1); } opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_trace.h000066400000000000000000000002261300170765700233710ustar00rootroot00000000000000#ifndef TRACE_H_INCLUDED #define TRACE_H_INCLUDED void TraceMsg(const char * const fmt, ...); void TraceExit(const char * const fmt, ...); #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_xmlrpc_int.h000066400000000000000000000023601300170765700244530ustar00rootroot00000000000000#ifndef SLEEP_INT_H_INCLUDED #define SLEEP_INT_H_INCLUDED #include extern const char * const xmlrpc_strsol; void xmlrpc_millisecond_sleep(unsigned int const milliseconds); void xmlrpc_asprintf(const char ** const retvalP, const char * const fmt, ...); void xmlrpc_strfree(const char * const string); static inline unsigned short xmlrpc_streq(const char * const a, const char * const b) { return (strcmp(a, b) == 0); } static inline unsigned short xmlrpc_strcaseeq(const char * const a, const char * const b) { return (strcasecmp(a, b) == 0); } static inline unsigned short xmlrpc_strneq(const char * const a, const char * const b, size_t const len) { return (strncmp(a, b, len) == 0); } #define ATTR_UNUSED __attribute__((__unused__)) #define DIRECTORY_SEPARATOR "/" #endif #ifndef XMLRPC_C_UTIL_INT_H_INCLUDED #define XMLRPC_C_UTIL_INT_H_INCLUDED typedef enum { false = 0, true = 1 } bool; #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) /* When we deallocate a pointer in a struct, we often replace it with ** this and throw in a few assertions here and there. */ #define XMLRPC_BAD_POINTER ((void*) 0xDEADBEEF) #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/abyss_xmlrpc_server.c000066400000000000000000001112441300170765700251640ustar00rootroot00000000000000/* Copyright information is at the end of the file */ #include #include #include #include #include #include #include #include #ifdef _WIN32 # include #else # include # include # include #endif #include "abyss_mallocvar.h" #include #include #include #include "abyss_xmlrpc_int.h" #include /*========================================================================= ** die_if_fault_occurred **========================================================================= ** If certain kinds of out-of-memory errors occur during server setup, ** we want to quit and print an error. */ static void die_if_fault_occurred(xmlrpc_env *env) { if (env->fault_occurred) { fprintf(stderr, "Unexpected XML-RPC fault: %s (%d)\n", env->fault_string, env->fault_code); exit(1); } } static void addAuthCookie(xmlrpc_env * const envP, TSession * const abyssSessionP, const char * const authCookie) { const char * cookieResponse; xmlrpc_asprintf(&cookieResponse, "auth=%s", authCookie); if (cookieResponse == xmlrpc_strsol) xmlrpc_faultf(envP, "Insufficient memory to generate cookie " "response header."); else { ResponseAddField(abyssSessionP, "Set-Cookie", cookieResponse); xmlrpc_strfree(cookieResponse); } } static void sendXmlData(xmlrpc_env * const envP, TSession * const abyssSessionP, const char * const body, size_t const len, bool const chunked) { /*---------------------------------------------------------------------------- Generate an HTTP response containing body 'body' of length 'len' characters. This is meant to run in the context of an Abyss URI handler for Abyss session 'abyssSessionP'. -----------------------------------------------------------------------------*/ const char * http_cookie = NULL; /* This used to set http_cookie to getenv("HTTP_COOKIE"), but that doesn't make any sense -- environment variables are not appropriate for this. So for now, cookie code is disabled. - Bryan 2004.10.03. */ /* Various bugs before Xmlrpc-c 1.05 caused the response to be not chunked in the most basic case, but chunked if the client explicitly requested keepalive. I think it's better not to chunk, because it's simpler, so I removed this in 1.05. I don't know what the purpose of chunking would be, and an original comment suggests the author wasn't sure chunking was a good idea. In 1.06 we added the user option to chunk. */ if (chunked) ResponseChunked(abyssSessionP); ResponseStatus(abyssSessionP, 200); if (http_cookie) /* There's an auth cookie, so pass it back in the response. */ addAuthCookie(envP, abyssSessionP, http_cookie); if ((size_t)(uint32_t)len != len) xmlrpc_faultf(envP, "XML-RPC method generated a response too " "large for Abyss to send"); else { uint32_t const abyssLen = (uint32_t)len; ResponseContentType(abyssSessionP, "text/xml; charset=\"utf-8\""); ResponseContentLength(abyssSessionP, abyssLen); ResponseWriteStart(abyssSessionP); ResponseWriteBody(abyssSessionP, body, abyssLen); ResponseWriteEnd(abyssSessionP); } } static void sendError(TSession * const abyssSessionP, unsigned int const status) { /*---------------------------------------------------------------------------- Send an error response back to the client. -----------------------------------------------------------------------------*/ ResponseStatus(abyssSessionP, (uint16_t) status); ResponseError(abyssSessionP); } static void traceChunkRead(TSession * const abyssSessionP) { fprintf(stderr, "XML-RPC handler got a chunk of %u bytes\n", (unsigned int)SessionReadDataAvail(abyssSessionP)); } static void refillBufferFromConnection(xmlrpc_env * const envP, TSession * const abyssSessionP, const char * const trace) { /*---------------------------------------------------------------------------- Get the next chunk of data from the connection into the buffer. -----------------------------------------------------------------------------*/ abyss_bool succeeded; succeeded = SessionRefillBuffer(abyssSessionP); if (!succeeded) xmlrpc_env_set_fault_formatted( envP, XMLRPC_TIMEOUT_ERROR, "Timed out waiting for " "client to send its POST data"); else { if (trace) traceChunkRead(abyssSessionP); } } static void getBody(xmlrpc_env * const envP, TSession * const abyssSessionP, size_t const contentSize, const char * const trace, xmlrpc_mem_block ** const bodyP) { /*---------------------------------------------------------------------------- Get the entire body, which is of size 'contentSize' bytes, from the Abyss session and return it as the new memblock *bodyP. The first chunk of the body may already be in Abyss's buffer. We retrieve that before reading more. -----------------------------------------------------------------------------*/ xmlrpc_mem_block * body; if (trace) fprintf(stderr, "XML-RPC handler processing body. " "Content Size = %u bytes\n", (unsigned)contentSize); body = xmlrpc_mem_block_new(envP, 0); if (!envP->fault_occurred) { size_t bytesRead; const char * chunkPtr; size_t chunkLen; bytesRead = 0; while (!envP->fault_occurred && bytesRead < contentSize) { SessionGetReadData(abyssSessionP, contentSize - bytesRead, &chunkPtr, &chunkLen); bytesRead += chunkLen; assert(bytesRead <= contentSize); XMLRPC_MEMBLOCK_APPEND(char, envP, body, chunkPtr, chunkLen); if (bytesRead < contentSize) refillBufferFromConnection(envP, abyssSessionP, trace); } if (envP->fault_occurred) xmlrpc_mem_block_free(body); else *bodyP = body; } } static void storeCookies(TSession * const httpRequestP, unsigned int * const httpErrorP) { /*---------------------------------------------------------------------------- Get the cookie settings from the HTTP headers and remember them for use in responses. -----------------------------------------------------------------------------*/ const char * const cookie = RequestHeaderValue(httpRequestP, "cookie"); if (cookie) { /* Setting the value in an environment variable doesn't make any sense. So for now, cookie code is disabled. -Bryan 04.10.03. setenv("HTTP_COOKIE", cookie, 1); */ } /* TODO: parse HTTP_COOKIE to find auth pair, if there is one */ *httpErrorP = 0; } static void validateContentType(TSession * const httpRequestP, unsigned int * const httpErrorP) { /*---------------------------------------------------------------------------- If the client didn't specify a content-type of "text/xml", return "400 Bad Request". We can't allow the client to default this header, because some firewall software may rely on all XML-RPC requests using the POST method and a content-type of "text/xml". -----------------------------------------------------------------------------*/ const char * const content_type = RequestHeaderValue(httpRequestP, "content-type"); if (content_type == NULL) *httpErrorP = 400; else { const char * const sempos = strchr(content_type, ';'); unsigned int baselen; /* Length of the base portion of the content type, e.g. "text/xml" int "text/xml;charset=utf-8" */ if (sempos) baselen = sempos - content_type; else baselen = strlen(content_type); if (!xmlrpc_strneq(content_type, "text/xml", baselen)) *httpErrorP = 400; else *httpErrorP = 0; } } static void processContentLength(TSession * const httpRequestP, size_t * const inputLenP, unsigned int * const httpErrorP) { /*---------------------------------------------------------------------------- Make sure the content length is present and non-zero. This is technically required by XML-RPC, but we only enforce it because we don't want to figure out how to safely handle HTTP < 1.1 requests without it. If the length is missing, return "411 Length Required". -----------------------------------------------------------------------------*/ const char * const content_length = RequestHeaderValue(httpRequestP, "content-length"); if (content_length == NULL) *httpErrorP = 411; else { if (content_length[0] == '\0') *httpErrorP = 400; else { unsigned long contentLengthValue; char * tail; contentLengthValue = strtoul(content_length, &tail, 10); if (*tail != '\0') /* There's non-numeric crap in the length */ *httpErrorP = 400; else if (contentLengthValue < 1) *httpErrorP = 400; else if ((unsigned long)(size_t)contentLengthValue != contentLengthValue) *httpErrorP = 400; else { *httpErrorP = 0; *inputLenP = (size_t)contentLengthValue; } } } } static void traceHandlerCalled(TSession * const abyssSessionP) { const char * methodDesc; const TRequestInfo * requestInfoP; fprintf(stderr, "xmlrpc_server_abyss URI path handler called.\n"); SessionGetRequestInfo(abyssSessionP, &requestInfoP); fprintf(stderr, "URI = '%s'\n", requestInfoP->uri); switch (requestInfoP->method) { case m_unknown: methodDesc = "unknown"; break; case m_get: methodDesc = "get"; break; case m_put: methodDesc = "put"; break; case m_head: methodDesc = "head"; break; case m_post: methodDesc = "post"; break; case m_delete: methodDesc = "delete"; break; case m_trace: methodDesc = "trace"; break; case m_options: methodDesc = "m_options"; break; default: methodDesc = "?"; } fprintf(stderr, "HTTP method = '%s'\n", methodDesc); if (requestInfoP->query) fprintf(stderr, "query (component of URL)='%s'\n", requestInfoP->query); else fprintf(stderr, "URL has no query component\n"); } static void processCall(TSession * const abyssSessionP, size_t const contentSize, xmlrpc_registry * const registryP, bool const wantChunk, const char * const trace) { /*---------------------------------------------------------------------------- Handle an RPC request. This is an HTTP request that has the proper form to be one of our RPCs. Its content length is 'contentSize' bytes. -----------------------------------------------------------------------------*/ xmlrpc_env env; if (trace) fprintf(stderr, "xmlrpc_server_abyss URI path handler processing RPC.\n"); xmlrpc_env_init(&env); if (contentSize > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)) xmlrpc_env_set_fault_formatted( &env, XMLRPC_LIMIT_EXCEEDED_ERROR, "XML-RPC request too large (%ld bytes)", (long)contentSize); else { xmlrpc_mem_block *body=0; /* Read XML data off the wire. */ getBody(&env, abyssSessionP, contentSize, trace, &body); if (!env.fault_occurred) { xmlrpc_mem_block * output; /* Process the RPC. */ output = xmlrpc_registry_process_call( &env, registryP, NULL, XMLRPC_MEMBLOCK_CONTENTS(char, body), XMLRPC_MEMBLOCK_SIZE(char, body)); if (!env.fault_occurred) { /* Send out the result. */ sendXmlData(&env, abyssSessionP, XMLRPC_MEMBLOCK_CONTENTS(char, output), XMLRPC_MEMBLOCK_SIZE(char, output), wantChunk); XMLRPC_MEMBLOCK_FREE(char, output); } XMLRPC_MEMBLOCK_FREE(char, body); } } if (env.fault_occurred) { if (env.fault_code == XMLRPC_TIMEOUT_ERROR) sendError(abyssSessionP, 408); /* 408 Request Timeout */ else sendError(abyssSessionP, 500); /* 500 Internal Server Error */ } xmlrpc_env_clean(&env); } /**************************************************************************** Abyss handlers (to be registered with and called by Abyss) ****************************************************************************/ static const char * trace_abyss; struct uriHandlerXmlrpc { /*---------------------------------------------------------------------------- This is the part of an Abyss HTTP request handler (aka URI handler) that is specific to the Xmlrpc-c handler. -----------------------------------------------------------------------------*/ xmlrpc_registry * registryP; const char * uriPath; /* malloc'ed */ bool chunkResponse; /* The handler should chunk its response whenever possible */ }; static void termUriHandler(void * const arg) { struct uriHandlerXmlrpc * const uriHandlerXmlrpcP = arg; xmlrpc_strfree(uriHandlerXmlrpcP->uriPath); free(uriHandlerXmlrpcP); } static void handleXmlrpcReq(URIHandler2 * const this, TSession * const abyssSessionP, abyss_bool * const handledP) { /*---------------------------------------------------------------------------- Our job is to look at this HTTP request that the Abyss server is trying to process and see if we can handle it. If it's an XML-RPC call for this XML-RPC server, we handle it. If it's not, we refuse it and Abyss can try some other handler. Our return code is TRUE to mean we handled it; FALSE to mean we didn't. Note that failing the request counts as handling it, and not handling it does not mean we failed it. This is an Abyss HTTP Request handler -- type URIHandler2. -----------------------------------------------------------------------------*/ struct uriHandlerXmlrpc * const uriHandlerXmlrpcP = this->userdata; const TRequestInfo * requestInfoP; if (trace_abyss) traceHandlerCalled(abyssSessionP); SessionGetRequestInfo(abyssSessionP, &requestInfoP); /* Note that requestInfoP->uri is not the whole URI. It is just the "file name" part of it. */ if (strcmp(requestInfoP->uri, uriHandlerXmlrpcP->uriPath) != 0) /* It's for the path (e.g. "/RPC2") that we're supposed to handle. */ *handledP = FALSE; else { *handledP = TRUE; /* We understand only the POST HTTP method. For anything else, return "405 Method Not Allowed". */ if (requestInfoP->method != m_post) sendError(abyssSessionP, 405); else { unsigned int httpError; storeCookies(abyssSessionP, &httpError); if (httpError) sendError(abyssSessionP, httpError); else { unsigned int httpError; validateContentType(abyssSessionP, &httpError); if (httpError) sendError(abyssSessionP, httpError); else { unsigned int httpError; size_t contentSize; processContentLength(abyssSessionP, &contentSize, &httpError); if (httpError) sendError(abyssSessionP, httpError); else processCall(abyssSessionP, contentSize, uriHandlerXmlrpcP->registryP, uriHandlerXmlrpcP->chunkResponse, trace_abyss); } } } } if (trace_abyss) fprintf(stderr, "xmlrpc_server_abyss URI path handler returning.\n"); } /*========================================================================= ** xmlrpc_server_abyss_default_handler **========================================================================= ** This handler returns a 404 Not Found for all requests. See the header ** for more documentation. */ static xmlrpc_bool xmlrpc_server_abyss_default_handler(TSession * const sessionP) { if (trace_abyss) fprintf(stderr, "xmlrpc_server_abyss default handler called.\n"); sendError(sessionP, 404); return TRUE; } static void sigchld(int const signalClass ATTR_UNUSED) { /*---------------------------------------------------------------------------- This is a signal handler for a SIGCHLD signal (which informs us that one of our child processes has terminated). The only child processes we have are those that belong to the Abyss server (and then only if the Abyss server was configured to use forking as a threading mechanism), so we respond by passing the signal on to the Abyss server. -----------------------------------------------------------------------------*/ #ifndef WIN32 bool childrenLeft; bool error; assert(signalClass == SIGCHLD); error = false; childrenLeft = true; /* initial assumption */ /* Reap defunct children until there aren't any more. */ while (childrenLeft && !error) { int status; pid_t pid; pid = waitpid((pid_t) -1, &status, WNOHANG); if (pid == 0) childrenLeft = false; else if (pid < 0) { /* because of ptrace */ if (errno != EINTR) error = true; } else ServerHandleSigchld(pid); } #endif /* WIN32 */ } struct signalHandlers { struct sigaction pipe; struct sigaction chld; }; static void setupSignalHandlers(struct signalHandlers * const oldHandlersP) { #ifndef WIN32 struct sigaction mysigaction; sigemptyset(&mysigaction.sa_mask); mysigaction.sa_flags = 0; /* This signal indicates connection closed in the middle */ mysigaction.sa_handler = SIG_IGN; sigaction(SIGPIPE, &mysigaction, &oldHandlersP->pipe); /* This signal indicates a child process (request handler) has died */ mysigaction.sa_handler = sigchld; sigaction(SIGCHLD, &mysigaction, &oldHandlersP->chld); #endif } static void restoreSignalHandlers(struct signalHandlers const oldHandlers) { #ifndef WIN32 sigaction(SIGPIPE, &oldHandlers.pipe, NULL); sigaction(SIGCHLD, &oldHandlers.chld, NULL); #endif } static void runServerDaemon(TServer * const serverP, runfirstFn const runfirst, void * const runfirstArg) { struct signalHandlers oldHandlers; setupSignalHandlers(&oldHandlers); ServerUseSigchld(serverP); ServerDaemonize(serverP); /* We run the user supplied runfirst after forking, but before accepting connections (helpful when running with threads) */ if (runfirst) runfirst(runfirstArg); ServerRun(serverP); restoreSignalHandlers(oldHandlers); } static void setHandler(xmlrpc_env * const envP, TServer * const srvP, const char * const uriPath, xmlrpc_registry * const registryP, bool const chunkResponse) { struct uriHandlerXmlrpc * uriHandlerXmlrpcP; URIHandler2 uriHandler; abyss_bool success; trace_abyss = getenv("XMLRPC_TRACE_ABYSS"); MALLOCVAR_NOFAIL(uriHandlerXmlrpcP); uriHandlerXmlrpcP->registryP = registryP; uriHandlerXmlrpcP->uriPath = strdup(uriPath); uriHandlerXmlrpcP->chunkResponse = chunkResponse; uriHandler.handleReq2 = handleXmlrpcReq; uriHandler.handleReq1 = NULL; uriHandler.userdata = uriHandlerXmlrpcP; uriHandler.init = NULL; uriHandler.term = &termUriHandler; ServerAddHandler2(srvP, &uriHandler, &success); if (!success) xmlrpc_faultf(envP, "Abyss failed to register the Xmlrpc-c request " "handler. ServerAddHandler2() failed."); if (envP->fault_occurred) free(uriHandlerXmlrpcP); } void xmlrpc_server_abyss_set_handler(xmlrpc_env * const envP, TServer * const srvP, const char * const uriPath, xmlrpc_registry * const registryP) { setHandler(envP, srvP, uriPath, registryP, false); } static void setHandlers(TServer * const srvP, const char * const uriPath, xmlrpc_registry * const registryP, bool const chunkResponse) { xmlrpc_env env; xmlrpc_env_init(&env); trace_abyss = getenv("XMLRPC_TRACE_ABYSS"); setHandler(&env, srvP, uriPath, registryP, chunkResponse); if (env.fault_occurred) abort(); ServerDefaultHandler(srvP, xmlrpc_server_abyss_default_handler); xmlrpc_env_clean(&env); } void xmlrpc_server_abyss_set_handlers2(TServer * const srvP, const char * const uriPath, xmlrpc_registry * const registryP) { setHandlers(srvP, uriPath, registryP, false); } void xmlrpc_server_abyss_set_handlers(TServer * const srvP, xmlrpc_registry * const registryP) { setHandlers(srvP, "/RPC2", registryP, false); } static void oldHighLevelAbyssRun(xmlrpc_env * const envP ATTR_UNUSED, const xmlrpc_server_abyss_parms * const parmsP, unsigned int const parmSize) { /*---------------------------------------------------------------------------- This is the old deprecated interface, where the caller of the xmlrpc_server_abyss API supplies an Abyss configuration file and we use it to daemonize (fork into the background, chdir, set uid, etc.) and run the Abyss server. The new preferred interface, implemented by normalLevelAbyssRun(), instead lets Caller set up the process environment himself and pass Abyss parameters in memory. That's a more conventional and flexible API. -----------------------------------------------------------------------------*/ TServer server; runfirstFn runfirst; void * runfirstArg; DateInit(); ServerCreate(&server, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL); ConfReadServerFile(parmsP->config_file_name, &server); setHandlers(&server, "/RPC2", parmsP->registryP, false); ServerInit(&server); if (parmSize >= XMLRPC_APSIZE(runfirst_arg)) { runfirst = parmsP->runfirst; runfirstArg = parmsP->runfirst_arg; } else { runfirst = NULL; runfirstArg = NULL; } runServerDaemon(&server, runfirst, runfirstArg); ServerFree(&server); } static void setAdditionalServerParms(const xmlrpc_server_abyss_parms * const parmsP, unsigned int const parmSize, TServer * const serverP) { /* The following ought to be parameters on ServerCreate(), but it looks like plugging them straight into the TServer structure is the only way to set them. */ if (parmSize >= XMLRPC_APSIZE(keepalive_timeout) && parmsP->keepalive_timeout > 0) ServerSetKeepaliveTimeout(serverP, parmsP->keepalive_timeout); if (parmSize >= XMLRPC_APSIZE(keepalive_max_conn) && parmsP->keepalive_max_conn > 0) ServerSetKeepaliveMaxConn(serverP, parmsP->keepalive_max_conn); if (parmSize >= XMLRPC_APSIZE(timeout) && parmsP->timeout > 0) ServerSetTimeout(serverP, parmsP->timeout); if (parmSize >= XMLRPC_APSIZE(dont_advertise)) ServerSetAdvertise(serverP, !parmsP->dont_advertise); } static void extractServerCreateParms( xmlrpc_env * const envP, const xmlrpc_server_abyss_parms * const parmsP, unsigned int const parmSize, abyss_bool * const socketBoundP, unsigned int * const portNumberP, TOsSocket * const socketFdP, const char ** const logFileNameP) { if (parmSize >= XMLRPC_APSIZE(socket_bound)) *socketBoundP = parmsP->socket_bound; else *socketBoundP = FALSE; if (*socketBoundP) { if (parmSize < XMLRPC_APSIZE(socket_handle)) xmlrpc_faultf(envP, "socket_bound is true, but server parameter " "structure does not contain socket_handle (it's too " "short)"); else *socketFdP = parmsP->socket_handle; } else { if (parmSize >= XMLRPC_APSIZE(port_number)) *portNumberP = parmsP->port_number; else *portNumberP = 8080; if (*portNumberP > 0xffff) xmlrpc_faultf(envP, "TCP port number %u exceeds the maximum possible " "TCP port number (65535)", *portNumberP); } if (!envP->fault_occurred) { if (parmSize >= XMLRPC_APSIZE(log_file_name) && parmsP->log_file_name) *logFileNameP = strdup(parmsP->log_file_name); else *logFileNameP = NULL; } } static void createServerBoundSocket(xmlrpc_env * const envP, TOsSocket const socketFd, const char * const logFileName, TServer * const serverP, TSocket ** const socketPP) { TSocket * socketP; const char * error; SocketUnixCreateFd(socketFd, &socketP); if (!socketP) xmlrpc_faultf(envP, "Unable to create Abyss socket out of " "file descriptor %d.", socketFd); else { ServerCreateSocket2(serverP, socketP, &error); if (error) { xmlrpc_faultf(envP, "Abyss failed to create server. %s", error); xmlrpc_strfree(error); } else { *socketPP = socketP; ServerSetName(serverP, "XmlRpcServer"); if (logFileName) ServerSetLogFileName(serverP, logFileName); } if (envP->fault_occurred) SocketDestroy(socketP); } } static void createServer(xmlrpc_env * const envP, const xmlrpc_server_abyss_parms * const parmsP, unsigned int const parmSize, TServer * const serverP, TSocket ** const socketPP) { /*---------------------------------------------------------------------------- Create a bare server. It will need further setup before it is ready to use. -----------------------------------------------------------------------------*/ abyss_bool socketBound; unsigned int portNumber = 0; TOsSocket socketFd = 0; const char * logFileName=NULL; extractServerCreateParms(envP, parmsP, parmSize, &socketBound, &portNumber, &socketFd, &logFileName); if (!envP->fault_occurred) { if (socketBound) createServerBoundSocket(envP, socketFd, logFileName, serverP, socketPP); else { ServerCreate(serverP, "XmlRpcServer", portNumber, DEFAULT_DOCS, logFileName); *socketPP = NULL; } if (logFileName) xmlrpc_strfree(logFileName); } } static bool chunkResponseParm(const xmlrpc_server_abyss_parms * const parmsP, unsigned int const parmSize) { return parmSize >= XMLRPC_APSIZE(chunk_response) && parmsP->chunk_response; } static const char * uriPathParm(const xmlrpc_server_abyss_parms * const parmsP, unsigned int const parmSize) { const char * uriPath; if (parmSize >= XMLRPC_APSIZE(uri_path) && parmsP->uri_path) uriPath = parmsP->uri_path; else uriPath = "/RPC2"; return uriPath; } static xmlrpc_server_shutdown_fn shutdownAbyss; static void shutdownAbyss(xmlrpc_env * const envP, void * const context, const char * const comment ATTR_UNUSED) { /*---------------------------------------------------------------------------- Tell Abyss to wrap up whatever it's doing and shut down. This is a server shutdown function to be registered in the method registry, for use by the 'system.shutdown' system method. After we return, Abyss will finish up the system.shutdown and any other connections that are in progress, then the call to ServerRun() etc. will return. But Abyss may be stuck waiting for something, such as the next HTTP connection. In that case, until it gets what it's waiting for, it won't even know it's supposed t shut down. In particular, a caller of system.shutdown may have to execute one more RPC in order for the shutdown to happen. -----------------------------------------------------------------------------*/ TServer * const serverP = context; xmlrpc_env_init(envP); ServerTerminate(serverP); } static void normalLevelAbyssRun(xmlrpc_env * const envP, const xmlrpc_server_abyss_parms * const parmsP, unsigned int const parmSize) { TServer server; TSocket * socketP = 0; DateInit(); createServer(envP, parmsP, parmSize, &server, &socketP); if (!envP->fault_occurred) { struct signalHandlers oldHandlers; setAdditionalServerParms(parmsP, parmSize, &server); setHandlers(&server, uriPathParm(parmsP, parmSize), parmsP->registryP, chunkResponseParm(parmsP, parmSize)); ServerInit(&server); setupSignalHandlers(&oldHandlers); ServerUseSigchld(&server); if (0) /* Too much of a security risk. In 1.07, there is a server parameter to enable this. */ xmlrpc_registry_set_shutdown(parmsP->registryP, &shutdownAbyss, &server); ServerRun(&server); restoreSignalHandlers(oldHandlers); ServerFree(&server); if (socketP) SocketDestroy(socketP); } } void xmlrpc_server_abyss(xmlrpc_env * const envP, const xmlrpc_server_abyss_parms * const parmsP, unsigned int const parmSize) { XMLRPC_ASSERT_ENV_OK(envP); if (parmSize < XMLRPC_APSIZE(registryP)) xmlrpc_faultf(envP, "You must specify members at least up through " "'registryP' in the server parameters argument. " "That would mean the parameter size would be >= %lu " "but you specified a size of %u", XMLRPC_APSIZE(registryP), parmSize); else { if (parmsP->config_file_name) oldHighLevelAbyssRun(envP, parmsP, parmSize); else normalLevelAbyssRun(envP, parmsP, parmSize); } } /*========================================================================= XML-RPC Server Method Registry This is an old deprecated form of the server facilities that uses global variables. =========================================================================*/ /* These global variables must be treated as read-only after the server has started. */ static TServer globalSrv; /* When you use the old interface (xmlrpc_server_abyss_init(), etc.), this is the Abyss server to which they refer. Obviously, there can be only one Abyss server per program using this interface. */ static xmlrpc_registry * builtin_registryP; void xmlrpc_server_abyss_init_registry(void) { /* This used to just create the registry and Caller would be responsible for adding the handlers that use it. But that isn't very modular -- the handlers and registry go together; there's no sense in using the built-in registry and not the built-in handlers because if you're custom building something, you can just make your own regular registry. So now we tie them together, and we don't export our handlers. */ xmlrpc_env env; xmlrpc_env_init(&env); builtin_registryP = xmlrpc_registry_new(&env); die_if_fault_occurred(&env); xmlrpc_env_clean(&env); setHandlers(&globalSrv, "/RPC2", builtin_registryP, false); } xmlrpc_registry * xmlrpc_server_abyss_registry(void) { /* This is highly deprecated. If you want to mess with a registry, make your own with xmlrpc_registry_new() -- don't mess with the internal one. */ return builtin_registryP; } /* A quick & easy shorthand for adding a method. */ void xmlrpc_server_abyss_add_method(char * const method_name, xmlrpc_method const method, void * const user_data) { xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_registry_add_method(&env, builtin_registryP, NULL, method_name, method, user_data); die_if_fault_occurred(&env); xmlrpc_env_clean(&env); } void xmlrpc_server_abyss_add_method_w_doc(char * const method_name, xmlrpc_method const method, void * const user_data, char * const signature, char * const help) { xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_registry_add_method_w_doc( &env, builtin_registryP, NULL, method_name, method, user_data, signature, help); die_if_fault_occurred(&env); xmlrpc_env_clean(&env); } void xmlrpc_server_abyss_init(int const flags ATTR_UNUSED, const char * const config_file) { DateInit(); MIMETypeInit(); ServerCreate(&globalSrv, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL); ConfReadServerFile(config_file, &globalSrv); xmlrpc_server_abyss_init_registry(); /* Installs /RPC2 handler and default handler that use the built-in registry. */ ServerInit(&globalSrv); } void xmlrpc_server_abyss_run_first(runfirstFn const runfirst, void * const runfirstArg) { runServerDaemon(&globalSrv, runfirst, runfirstArg); } void xmlrpc_server_abyss_run(void) { runServerDaemon(&globalSrv, NULL, NULL); } /* ** Copyright (C) 2001 by First Peer, Inc. All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** There is more copyright information in the bottom half of this file. ** Please see it for more details. */ opensips-2.2.2/obsolete_modules/mi_xmlrpc/doc/000077500000000000000000000000001300170765700214665ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/mi_xmlrpc/doc/mi_xmlrpc.cfg000066400000000000000000000017051300170765700241440ustar00rootroot00000000000000# # $Id$ # # this example shows use of xmlrpc # # ----------- global configuration parameters ------------------------ debug=9 # debug level (cmd line: -dddddddddd) fork=no log_stderror=yes # (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) listen=udp:127.0.0.1:5060 children=2 # ------------------ module loading ---------------------------------- mpath="../sip-server/modules/" loadmodule "sl/sl.so" loadmodule "xlog/xlog.so" loadmodule "textops/textops.so" loadmodule "mi_xmlrpc/mi_xmlrpc.so" modparam("mi_xmlrpc", "port", 7999) modparam("mi_xmlrpc", "reply_option", 1) modparam("mi_xmlrpc", "buffer_size", 8000) # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all requests if (!is_method("ACK")) { xlog("=== received $rm $ru $fu\n"); sl_send_reply("200", "ok"); return; }; } opensips-2.2.2/obsolete_modules/mi_xmlrpc/doc/mi_xmlrpc.xml000066400000000000000000000021111300170765700241750ustar00rootroot00000000000000 %docentities; ]> mi_xmlrpc Module &osipsname; Lavinia-Andreea Andrei Lavinia-Andreea Andrei Juha Heinanen 2006 &voicesystem; $Revision: 8740 $ $Date$ &admin; &faq; opensips-2.2.2/obsolete_modules/mi_xmlrpc/doc/mi_xmlrpc_admin.xml000066400000000000000000000140761300170765700253620ustar00rootroot00000000000000 &adminguide;
Overview This module implements a xmlrpc server that handles xmlrpc requests and generates xmlrpc responses. When a xmlrpc message is received a default method is executed. At first, it looks up the MI command. If found it parses the called procedure's parameters into a MI tree and the command is executed. A MI reply tree is returned that is formatted back in xmlrpc. The response is built in two ways - like a string that contains the MI tree nodes information (name, values and attributes) or like an array whose elements are consisted of each MI tree node stored information. Implementation of mi_xmlrpc module's xmlrpc server is based on Abyss XML-RPC server. Current version of Abyss server "normalizes" CRLF sequence in received XML-RPC strings to LF character, which makes it impossible to pass CRLF sequence from xmlrpc client application to &osips; modules, such as mi_fifo and pua_mi, that accept requests via MI transport. To overcome this limitation mi_xmlrpc module implements a hack that coverts each LFLF sequence in received XML-RPC strings to CRLF sequence.
To-do Features to be added in the future: possibility to select the listening IP address multiple ports per IP address
Dependencies
&osips; Modules The following modules must be loaded before this module: No dependencies on other &osips; modules.
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: libxml. libxmlrpc-c3 version 0.9.10 - 0.9.42. (using other versions may be dangerous and lead to opensips blocking) libxmlrpc-c3-dev version 0.9.10 - 0.9.42. If libxmlrpc-c3[-dev] package is not available on your system, you may check if packages for your OS are not available on the xmlrpc-cproject ( http://xmlrpc-c.sourceforge.net/). Otherwise you need to install the library and devel headers from the sources. In both cases, keep in mind to use the 0.9.10 version!!.
Exported Parameters
<varname>port</varname>(integer) The port number used by the XMLRPX server to listen for incoming requests. The default value is 8080. Ports lower than 1024 are not accepted. Set <varname>port</varname> parameter ... modparam("mi_xmlrpc", "port", 8000) ...
<varname>log_file</varname>(string) A log file to be used by the internal Abyss html server used by the XMLRPX library. The default values NONE (no logging). Set <varname>log_file</varname> parameter ... modparam("mi_xmlrpc", "log_file", "/var/log/abyss.log") ...
<varname>reply_option</varname> (integer) Given the xmlrpc response specifications that a methodResponse can contain a single params section with a single param section, there is the possibility to choose between a string built response or an array built one. For a 0 value, a single string parameter will be replied (merging the whole response). For non-0 value, each line from the response will be encoded as an element into an array of strings. The default option is a string built response (0). Set <varname>reply_option</varname> parameter ... modparam("mi_xmlrpc", "reply_option", 0) ...
<varname>buffer_size</varname> (integer) It specifies the maximum length of the buffer used to write in the MI tree node information in order to build the xmlrpc response. The default value is 8192. Set <varname>reply_option</varname> parameter ... modparam("mi_xmlrpc", "buffer_size", 8192) ...
Exported Functions No function exported to be used from configuration file.
Example This is an example showing the xmlrpc format for the get_statistics dialog: tm: MI commad: response. XMLRPC request get_statistics dialog: tm: ]]>
opensips-2.2.2/obsolete_modules/mi_xmlrpc/mi_xmlrpc.c000066400000000000000000000133651300170765700230670ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-11-30 first version (lavinia) * 2007-10-05 support for libxmlrpc-c3 version 1.x.x added (dragos) */ #include #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "mi_xmlrpc.h" #include "xr_writer.h" #include "xr_parser.h" #include "xr_server.h" #define XMLRPC_SERVER_WANT_ABYSS_HANDLERS #ifdef XMLRPC_OLD_VERSION #include "abyss.h" #include #include #else #include #include #include #include #endif #include "../../sr_module.h" #include "../../str.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" xmlrpc_env env; xmlrpc_value * xr_response; xmlrpc_registry * registryP; int rpl_opt = 0; /* module functions */ static int mod_init(); static int destroy(void); static void xmlrpc_process(int rank); static int port = 8080; static char *log_file = NULL; static int read_buf_size = MAX_READ; static TServer srv; static proc_export_t mi_procs[] = { {"MI XMLRPC", 0, 0, xmlrpc_process, 1 , PROC_FLAG_INITCHILD }, {0,0,0,0,0,0} }; /* module parameters */ static param_export_t mi_params[] = { {"port", INT_PARAM, &port}, {"log_file", STR_PARAM, &log_file}, {"reply_option", INT_PARAM, &rpl_opt}, {"buffer_size", INT_PARAM, &read_buf_size}, {0,0,0} }; /* module exports */ struct module_exports exports = { "mi_xmlrpc", /* module name */ MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ NULL, /* OpenSIPS module dependencies */ 0, /* exported functions */ 0, /* exported async functions */ mi_params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported PV */ mi_procs, /* extra processes */ mod_init, /* module initialization function */ (response_function) 0, /* response handling function */ (destroy_function) destroy, /* destroy function */ 0 /* per-child init function */ }; static int mod_init(void) { LM_DBG("testing port number...\n"); if ( port <= 1024 ) { LM_WARN("port<1024, using 8080...\n"); port = 8080; } if (init_async_lock()!=0) { LM_ERR("failed to init async lock\n"); return -1; } return 0; } static void xmlrpc_sigchld( int sig ) { pid_t pid; int status; while(1) { pid = waitpid( (pid_t) -1, &status, WNOHANG ); /* none left */ if ( pid == 0 ) break; if (pid<0) { /* because of ptrace */ if ( errno == EINTR ) continue; break; } #ifndef XMLRPC_OLD_VERSION else ServerHandleSigchld(pid); #endif } #ifdef SIGCLD if (signal(SIGCHLD, xmlrpc_sigchld)==SIG_ERR) LM_ERR("failed to re-install signal handler for SIGCHLD\n"); #endif } static void xmlrpc_process(int rank) { /* install handler to catch termination of child processes */ if (signal(SIGCHLD, xmlrpc_sigchld)==SIG_ERR) { LM_ERR("failed to install signal handler for SIGCHLD\n"); goto error; } /* Server Abyss init */ xmlrpc_env_init(&env); #ifdef XMLRPC_OLD_VERSION xmlrpc_server_abyss_init_registry(); registryP= xmlrpc_server_abyss_registry(); #else registryP = xmlrpc_registry_new(&env); #endif DateInit(); MIMETypeInit(); if (!ServerCreate(&srv, "XmlRpcServer", port, "", log_file)) { LM_ERR("failed to create XMLRPC server\n"); goto error; } #ifdef XMLRPC_OLD_VERSION if (!ServerAddHandler(&srv, xmlrpc_server_abyss_rpc2_handler)) { LM_ERR("failed to add handler to server\n"); goto error; } ServerDefaultHandler(&srv, xmlrpc_server_abyss_default_handler); #else xmlrpc_server_abyss_set_handlers2(&srv, "/RPC2", registryP); #endif ServerInit(&srv); if( init_mi_child() != 0 ) { LM_CRIT("failed to init the mi process\n"); goto error; } if ( xr_writer_init(read_buf_size) != 0 ) { LM_ERR("failed to init the reply writer\n"); goto error; } #ifdef XMLRPC_OLD_VERSION xmlrpc_env_init(&env); #endif if ( rpl_opt == 1 ) { xr_response = xmlrpc_build_value(&env, "()"); if ( env.fault_occurred ){ LM_ERR("failed to create an empty array: %s\n", env.fault_string); goto cleanup; } } if ( set_default_method(&env,registryP) != 0 ) { LM_ERR("failed to set up the default method!\n"); goto cleanup; } /* Run server abyss */ LM_INFO("starting xmlrpc server\n"); ServerRun(&srv); LM_CRIT("Server terminated!!!\n"); cleanup: xmlrpc_env_clean(&env); if ( xr_response ) xmlrpc_DECREF(xr_response); error: exit(-1); } int destroy(void) { LM_DBG("destroying module ...\n"); destroy_async_lock(); return 0; } opensips-2.2.2/obsolete_modules/mi_xmlrpc/mi_xmlrpc.h000066400000000000000000000021661300170765700230710ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-11-30 first version (lavinia) */ #ifndef _MI_XMLRPC_H_ #define _MI_XMLRPC_H_ #include #define XMLRPC_WANT_INTERNAL_DECLARATIONS #include extern xmlrpc_env env; extern xmlrpc_value * xr_response; extern int rpl_opt; #define MAX_READ 8192 #endif /* _MI_XMLRPC_H_ */ opensips-2.2.2/obsolete_modules/mi_xmlrpc/xr_parser.c000066400000000000000000000127701300170765700231010ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-11-30 first version (lavinia) * 2007-10-05 support for libxmlrpc-c3 version 1.x.x added (dragos) */ #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "xr_parser.h" #include "xr_parser_lib.h" #include "mi_xmlrpc.h" /* * Convert in argument string each LFLF to CRLF and return length of * the string not including the terminating `\0' character. * This is a hack that is needed as long as Abyss XML-RPC server "normalizes" * CRLF to LF in XML-RPC strings. */ int lflf_to_crlf_hack(char *s) { unsigned int len; len = 0; while (*s) { if (*(s + 1) && (*s == '\n') && *(s + 1) == '\n') { *s = '\r'; s = s + 2; len = len + 2; } else { s++; len++; } } return len; } struct mi_root * xr_parse_tree( xmlrpc_env * env, xmlrpc_value * paramArray ) { struct mi_root * mi_root; int size, i; size_t length; xmlrpc_int32 intValue; xmlrpc_bool boolValue; #ifdef XMLRPC_OLD_VERSION double doubleValue; char * contents; #else xmlrpc_double doubleValue; #endif char * stringValue = 0; char * byteStringValue =0; xmlrpc_value * item; mi_root = init_mi_tree(0, 0, 0); if ( !mi_root ) { LM_ERR("the MI tree cannot be initialized!\n"); goto error; } size = xmlrpc_array_size(env, paramArray); for (i=0 ; i< size ; i++) { item = xmlrpc_array_get_item(env, paramArray, i); if ( env->fault_occurred ) { LM_ERR("failed to get array item: %s\n", env->fault_string); goto error; } switch ( xmlrpc_value_type(item) ) { case (XMLRPC_TYPE_INT): #ifdef XMLRPC_OLD_VERSION intValue = item->_value.i; #else xmlrpc_read_int(env,item,&intValue); #endif if (addf_mi_node_child(&mi_root->node,0,0,0,"%d",intValue)==NULL) { LM_ERR("failed to add node to the MI tree.\n"); goto error; } break; case (XMLRPC_TYPE_BOOL): #ifdef XMLRPC_OLD_VERSION boolValue = item->_value.b; #else xmlrpc_read_bool(env,item,&boolValue); #endif if (addf_mi_node_child(&mi_root->node,0,0,0,"%u",boolValue)==NULL){ LM_ERR("failed to add node to the MI tree.\n"); goto error; } break; case (XMLRPC_TYPE_DOUBLE): #ifdef XMLRPC_OLD_VERSION doubleValue = item->_value.d; #else xmlrpc_read_double(env,item,&doubleValue); #endif if ( addf_mi_node_child(&mi_root->node, 0, 0, 0, "%lf", doubleValue) == NULL ) { LM_ERR("failed to add node to the MI tree.\n"); goto error; } break; case (XMLRPC_TYPE_STRING): #if HAVE_UNICODE_WCHAR #ifdef XMLRPC_OLD_VERSION xmlrpc_read_string_w(env, item, &stringValue); #else xmlrpc_read_string_w(env, item , (const char **)&stringValue); #endif #else #ifdef XMLRPC_OLD_VERSION xmlrpc_read_string(env, item, &stringValue); #else xmlrpc_read_string(env, item, (const char **)&stringValue); #endif #endif if ( env->fault_occurred ) { LM_ERR("failed to read stringValue: %s!\n", env->fault_string); goto error; } if ( add_mi_node_child(&mi_root->node, 0, 0, 0, stringValue, lflf_to_crlf_hack(stringValue)) == NULL ) { LM_ERR("failed to add node to the MI tree.\n"); goto error; } break; case (XMLRPC_TYPE_BASE64): #ifdef XMLRPC_OLD_VERSION length = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &item->_block); contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &item->_block); byteStringValue = pkg_malloc(length); if ( !byteStringValue ){ xmlrpc_env_set_fault_formatted(env, XMLRPC_INTERNAL_ERROR, "Unable to allocate %u bytes for byte string.", length); LM_ERR("pkg_malloc cannot allocate any more memory!\n"); goto error; } else memcpy(byteStringValue, contents, length); if ( add_mi_node_child(&mi_root->node, 0, 0, 0, byteStringValue, length) == NULL ) { LM_ERR("failed to add node to the MI tree.\n"); goto error; } #else xmlrpc_read_base64(env, item, &length, (const unsigned char **)(void*)&byteStringValue); if ( env->fault_occurred ) { LM_ERR("failed to read byteStringValue: %s!\n", env->fault_string); goto error; } if ( add_mi_node_child(&mi_root->node, MI_DUP_VALUE, 0, 0, byteStringValue, length) == NULL ) { LM_ERR("failed to add node to the MI tree.\n"); goto error; } free(byteStringValue); byteStringValue = NULL; #endif break; default : LM_ERR("unsupported node type %d\n", xmlrpc_value_type(item) ); xmlrpc_env_set_fault_formatted( env, XMLRPC_TYPE_ERROR, "Unsupported value of type %d supplied", xmlrpc_value_type(item)); goto error; } } return mi_root; error: if ( mi_root ) free_mi_tree(mi_root); if ( byteStringValue ) pkg_free(byteStringValue); return 0; } opensips-2.2.2/obsolete_modules/mi_xmlrpc/xr_parser.h000066400000000000000000000021721300170765700231010ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-11-30 first version (lavinia) */ #ifndef _XR_PARSER_H_ #define _XR_PARSER_H_ #include #define XMLRPC_WANT_INTERNAL_DECLARATIONS #include #include "../../mi/tree.h" struct mi_root * xr_parse_tree ( xmlrpc_env * env, xmlrpc_value * paramArray ); #endif /* _XR_PARSER_H_ */ opensips-2.2.2/obsolete_modules/mi_xmlrpc/xr_parser_lib.h000066400000000000000000000141211300170765700237240ustar00rootroot00000000000000/* ** Copyright (C) 2001 by First Peer, Inc. All rights reserved. ** Copyright (C) 2001 by Eric Kidd. All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. */ /* History: * --------- * 2006-11-30 imported from xmlrpc project, file /xmlrpc-c/src/xmlrpc_data.c, * version 1.06.06 (lavinia) * 2007-10-05 support for libxmlrpc-c3 version 1.x.x added (dragos) */ #ifdef XMLRPC_OLD_VERSION #define XMLRPC_WANT_INTERNAL_DECLARATIONS #include static char * xmlrpc_typeName ( xmlrpc_type type ) { switch(type) { case XMLRPC_TYPE_INT: return "INT"; case XMLRPC_TYPE_BOOL: return "BOOL"; case XMLRPC_TYPE_DOUBLE: return "DOUBLE"; case XMLRPC_TYPE_DATETIME: return "DATETIME"; case XMLRPC_TYPE_STRING: return "STRING"; case XMLRPC_TYPE_BASE64: return "BASE64"; case XMLRPC_TYPE_ARRAY: return "ARRAY"; case XMLRPC_TYPE_STRUCT: return "STRUCT"; case XMLRPC_TYPE_C_PTR: return "C_PTR"; case XMLRPC_TYPE_DEAD: return "DEAD"; default: return "Unknown"; } } static void validateType ( xmlrpc_env * env, xmlrpc_value * value, xmlrpc_type expectedType ) { if ( value->_type != expectedType ) { xmlrpc_env_set_fault_formatted( env, XMLRPC_TYPE_ERROR, "Value of type %s supplied where type %s was expected.", xmlrpc_typeName(value->_type), xmlrpc_typeName(expectedType)); } } static void verifyNoNulls ( xmlrpc_env * env, char * content, unsigned int len ) { unsigned int i; for ( i = 0 ; i < len && !env->fault_occurred ; i++ ) if ( content[i] == '\0' ) xmlrpc_env_set_fault(env, XMLRPC_INTERNAL_ERROR, "String must not contain NULL characters"); } static void accessStringValue ( xmlrpc_env * env, xmlrpc_value * value, size_t * length, char ** contents ) { validateType(env, value, XMLRPC_TYPE_STRING); if ( !env->fault_occurred ) { unsigned int size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &value->_block); char * content = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &value->_block); unsigned int len = size - 1; /* The memblock has a null character added to the end */ verifyNoNulls(env, content, len); *length = len; *contents = content; } else { *length = 0; *contents = NULL; } } static void xmlrpc_read_string( xmlrpc_env * env, xmlrpc_value * value, char ** stringValue ) { size_t length; char * contents, *str; accessStringValue(env, value, &length, &contents); if ( !env->fault_occurred ) { str = (char*) pkg_malloc (length+1); if ( str == NULL ) { xmlrpc_env_set_fault_formatted(env, XMLRPC_INTERNAL_ERROR, "Unable to allocate space for %u-character string", length); LM_ERR("pkg_malloc cannot allocate any more memory!\n"); } else { memcpy(str, contents, length); str[length] = '\0'; *stringValue = str; } } } #if HAVE_UNICODE_WCHAR static void verifyNoNullsW( xmlrpc_env * env, wchar_t * contents, unsigned int len ) { unsigned int i; for (i = 0; i < len && !env->fault_occurred; i++) if ( contents[i] == '\0' ) xmlrpc_env_set_fault(env, XMLRPC_INTERNAL_ERROR, "String must not contain NULL characters."); } static void accessStringValueW (xmlrpc_env * env, xmlrpc_value * value, size_t * length, wchar_t ** stringValueW ) { validateType(env, value, XMLRPC_TYPE_STRING); if ( !env->fault_occurred ) { if ( !env->fault_occurred ) { wchar_t * wcontents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, value->_wcs_block); size_t len = XMLRPC_TYPED_MEM_BLOCK_SIZE(wchar_t, value->_wcs_block) - 1; verifyNoNullsW(env, wcontents, len); *length = len; *stringValueW = wcontents; } } } static void xmlrpc_read_string_w ( xmlrpc_env * env, xmlrpc_value * value, wchar_t ** stringValue ) { size_t length; wchar_t * wcontents, * str; accessStringValueW(env, value, &length, &wcontents); if ( !env->fault_occurred ) { if ( !value->_wcs_block ) { char * contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &value->_block); size_t len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &value->_block) - 1; value->_wcs_block = xmlrpc_utf8_to_wcs(env, contents, len + 1); } str = (wchar_t*) pkg_malloc (length + 1); if ( str == NULL ){ xmlrpc_env_set_fault_formatted(env, XMLRPC_INTERNAL_ERROR, "Unable to allocate space for %u-byte string", length); LM_ERR("pkg_malloc cannot allocate any more memory!\n"); } else { memcpy(str, wcontents, length * sizeof(wchar_t)); str[length] = '\0'; *stringValue = str; } } } #endif #endif opensips-2.2.2/obsolete_modules/mi_xmlrpc/xr_server.c000066400000000000000000000156531300170765700231160ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-11-30 first version (lavinia) * 2007-02-02 support for asyncronous reply added (bogdan) * 2007-10-05 support for libxmlrpc-c3 version 1.x.x added (dragos) */ #include "../../str.h" #include "../../dprint.h" #include "../../sr_module.h" #include "../../mi/mi.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../locking.h" #include "../../ut.h" #include "xr_writer.h" #include "xr_parser.h" #include "mi_xmlrpc.h" #include "xr_server.h" #ifdef XMLRPC_OLD_VERSION #include #endif gen_lock_t *xr_lock; #define XMLRPC_ASYNC_FAILED ((void*)-2) #define XMLRPC_ASYNC_EXPIRED ((void*)-3) static inline void free_async_handler( struct mi_handler *hdl ) { if (hdl) shm_free(hdl); } static void xmlrpc_close_async( struct mi_root *mi_rpl, struct mi_handler *hdl, int done) { struct mi_root *shm_rpl; int x; if (!done) { /* we do not pass provisional stuff (yet) */ if (mi_rpl) free_mi_tree( mi_rpl ); return; } /* pass the tree via handler back to originating process */ if ( mi_rpl==NULL || (shm_rpl=clone_mi_tree( mi_rpl, 1))==NULL ) shm_rpl = XMLRPC_ASYNC_FAILED; if (mi_rpl) free_mi_tree(mi_rpl); lock_get(xr_lock); if (hdl->param==NULL) { hdl->param = shm_rpl; x = 0; } else { x = 1; } lock_release(xr_lock); if (x) { if (shm_rpl!=XMLRPC_ASYNC_FAILED) free_shm_mi_tree(shm_rpl); free_async_handler(hdl); } } #define MAX_XMLRPC_WAIT 2*60*4 static inline struct mi_root* wait_async_reply(struct mi_handler *hdl) { struct mi_root *mi_rpl; int i; int x; for( i=0 ; iparam) break; sleep_us(1000*500); } if (i==MAX_XMLRPC_WAIT) { /* no more waiting ....*/ lock_get(xr_lock); if (hdl->param==NULL) { hdl->param = XMLRPC_ASYNC_EXPIRED; x = 0; } else { x = 1; } lock_release(xr_lock); if (x==0) { LM_INFO("exiting before receiving reply\n"); return NULL; } } mi_rpl = (struct mi_root *)hdl->param; if (mi_rpl==XMLRPC_ASYNC_FAILED) mi_rpl = NULL; free_async_handler(hdl); return mi_rpl; } static inline struct mi_handler* build_async_handler(void) { struct mi_handler *hdl; hdl = (struct mi_handler*)shm_malloc( sizeof(struct mi_handler) ); if (hdl==0) { LM_ERR("no more shm mem\n"); return 0; } hdl->handler_f = xmlrpc_close_async; hdl->param = 0; return hdl; } #ifdef XMLRPC_OLD_VERSION xmlrpc_value* default_method (xmlrpc_env* env, char* host, char* methodName, xmlrpc_value* paramArray, void* serverInfo) #else xmlrpc_value* default_method (xmlrpc_env* env, const char* host, const char* methodName, xmlrpc_value* paramArray, void* serverInfo) #endif { xmlrpc_value* ret = NULL; struct mi_root* mi_cmd = NULL; struct mi_root* mi_rpl = NULL; struct mi_handler *hdl = NULL; struct mi_cmd* f; char* response = 0; int is_shm = 0; LM_DBG("starting up.....\n"); xr_writer_reset(); f = lookup_mi_cmd((char*)methodName, strlen(methodName)); if ( f == 0 ) { LM_ERR("command %s is not available!\n", methodName); xmlrpc_env_set_fault_formatted(env, XMLRPC_NO_SUCH_METHOD_ERROR, "Requested command (%s) is not available!", methodName); goto error; } LM_DBG("done looking the mi command.\n"); /* if asyncron cmd, build the async handler */ if (f->flags&MI_ASYNC_RPL_FLAG) { hdl = build_async_handler( ); if (hdl==0) { LM_ERR("failed to build async handler\n"); if ( !env->fault_occurred ) xmlrpc_env_set_fault(env, XMLRPC_INTERNAL_ERROR, "Internal server error while processing request"); goto error; } } else { hdl = NULL; } if (f->flags&MI_NO_INPUT_FLAG) { mi_cmd = 0; } else { mi_cmd = xr_parse_tree(env, paramArray); if ( mi_cmd == NULL ){ LM_ERR("failed to parse MI tree\n"); if ( !env->fault_occurred ) xmlrpc_env_set_fault(env, XMLRPC_INTERNAL_ERROR, "The xmlrpc request could not be parsed into a MI tree!"); goto error; } mi_cmd->async_hdl = hdl; } LM_DBG("done parsing the mi tree.\n"); if ((mi_rpl=run_mi_cmd(f,mi_cmd,(mi_flush_f*)xr_flush_response,env))==0){ LM_ERR("command (%s) processing failed.\n", methodName); xmlrpc_env_set_fault_formatted(env, XMLRPC_INTERNAL_ERROR, "Command (%s) processing failed.\n", methodName); goto error; } else if (mi_rpl==MI_ROOT_ASYNC_RPL) { mi_rpl = wait_async_reply(hdl); hdl = 0; if (mi_rpl==0) { xmlrpc_env_set_fault_formatted(env, XMLRPC_INTERNAL_ERROR, "Command (%s) processing failed (async).\n", methodName); goto error; } is_shm = 1; } LM_DBG("done running the mi command.\n"); if ( rpl_opt == 1 ) { if ( xr_build_response_array( env, mi_rpl ) != 0 ){ if ( !env->fault_occurred ) { LM_ERR("failed parsing the xmlrpc response from the mi tree\n"); xmlrpc_env_set_fault(env, XMLRPC_INTERNAL_ERROR, "Failed to parse the xmlrpc response from the mi tree."); } goto error; } LM_DBG("done building response array.\n"); ret = xr_response; } else { if ( (response = xr_build_response( env, mi_rpl )) == 0 ){ if ( !env->fault_occurred ) { LM_ERR("failed parsing the xmlrpc response from the mi tree\n"); xmlrpc_env_set_fault_formatted(env, XMLRPC_INTERNAL_ERROR, "Failed to parse the xmlrpc response from the mi tree."); } goto error; } LM_DBG("done building response.\n"); ret = xmlrpc_build_value(env, "s", response); } error: free_async_handler(hdl); if ( mi_cmd ) free_mi_tree( mi_cmd ); if ( mi_rpl ) { is_shm?free_shm_mi_tree(mi_rpl):free_mi_tree(mi_rpl);} return ret; } int set_default_method ( xmlrpc_env * env , xmlrpc_registry * registry) { xmlrpc_registry_set_default_method(env, registry, &default_method, NULL); if ( env->fault_occurred ) { LM_ERR("failed to add default method: %s\n", env->fault_string); return -1; } return 0; } int init_async_lock(void) { xr_lock = lock_alloc(); if (xr_lock==NULL) { LM_ERR("failed to create lock\n"); return -1; } if (lock_init(xr_lock)==NULL) { LM_ERR("failed to init lock\n"); return -1; } return 0; } void destroy_async_lock(void) { if (xr_lock) { lock_destroy(xr_lock); lock_dealloc(xr_lock); } } opensips-2.2.2/obsolete_modules/mi_xmlrpc/xr_server.h000066400000000000000000000030321300170765700231070ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-11-30 first version (lavinia) * 2007-10-05 support for libxmlrpc-c3 version 1.x.x added (dragos) */ #ifndef _XR_SERVER_H_ #define _XR_SERVER_H_ #include #define XMLRPC_WANT_INTERNAL_DECLARATIONS #include #ifdef XMLRPC_OLD_VERSION xmlrpc_value * default_method ( xmlrpc_env * env, char * host, char * methodName, xmlrpc_value * paramArray, void * serverInfo ); #else xmlrpc_value * default_method ( xmlrpc_env * env, const char * host, const char * methodName, xmlrpc_value * paramArray, void * serverInfo ); #endif int set_default_method ( xmlrpc_env * env , xmlrpc_registry * registry); int init_async_lock(void); void destroy_async_lock(void); #endif /* _XR_SERVER_H_ */ opensips-2.2.2/obsolete_modules/mi_xmlrpc/xr_writer.c000066400000000000000000000232401300170765700231130ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-11-30 first version (lavinia) */ #include #include "../../str.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "xr_writer.h" #include "mi_xmlrpc.h" static char *reply_buffer = 0; static unsigned int reply_buffer_len = 0; static xmlrpc_value* reply_item; int xr_writer_init( unsigned int size ) { reply_buffer_len = size; reply_buffer = pkg_malloc(size); if(!reply_buffer){ LM_ERR("pkg_malloc cannot allocate any more memory!\n"); return -1; } return 0; } void xr_writer_reset(void) { *reply_buffer = '\0'; } #ifndef XMLRPC_HAS_FORCE_CHARS #define XMLRPC_NONXML_CHAR 0x7F /* This version of XMLRPC does not have xmlrpc_force_to_xml_chars() */ void xmlrpc_force_to_xml_chars(char * const buffer) { char *p = buffer; while (*p != '\0') { if (*p < 0x20 && *p != 0x9 && *p != 0xA && *p != 0xD) *p = XMLRPC_NONXML_CHAR; p++; } } #endif static int xr_write_node(str * buf, struct mi_node * node) { char *end; char *p; struct mi_attr* attr; p = buf->s; end = buf->s + buf->len -1; /* name and value */ if ( node->name.s != NULL ) { if ( p+node->name.len+3 > end ) return -1; memcpy(p, node->name.s, node->name.len); p += node->name.len; *(p++) = ':'; *(p++) = ':'; *(p++) = ' '; } if ( node->value.s != NULL ) { if ( p+node->value.len > end ) return -1; memcpy(p, node->value.s, node->value.len); p += node->value.len; } /* attributes */ for( attr=node->attributes ; attr!=NULL ; attr=attr->next ) { if ( attr->name.s != NULL ) { if ( p+attr->name.len+2 > end ) return -1; *(p++) = ' '; memcpy(p,attr->name.s,attr->name.len); p += attr->name.len; *(p++) = '='; } if (attr->value.s!=NULL) { if (p+attr->value.len>end) return -1; memcpy(p,attr->value.s,attr->value.len); p += attr->value.len; } } if ( p+1 > end ) return -1; *(p++) = '\n'; buf->len -= p-buf->s; buf->s = p; return 0; } static int recur_build_response_array( xmlrpc_env * env, struct mi_node * tree, str * buf ) { for ( ; tree ; tree = tree->next ) { if (!(tree->flags & MI_WRITTEN)) { if ( xr_write_node( buf, tree ) != 0 ) { LM_ERR("failed to get MI node data!\n"); return -1; } /* we are sure that this node has been written * => avoid writing it again */ tree->flags |= MI_WRITTEN; } reply_buffer[reply_buffer_len-buf->len] = 0; xmlrpc_force_to_xml_chars(reply_buffer); reply_item = xmlrpc_build_value(env, "s", reply_buffer); xmlrpc_array_append_item(env, xr_response, reply_item); buf->s = reply_buffer; buf->len = reply_buffer_len; if ( tree->kids ) { if ( recur_build_response_array(env, tree->kids, buf) != 0 ) return -1; } } return 0; } int xr_build_response_array( xmlrpc_env * env, struct mi_root * tree ) { str buf; buf.s = reply_buffer; buf.len = reply_buffer_len; /* test if mi root value is 200 OK (if not no point to continue) */ if ( tree->code<200 || tree->code>=300 ){ LM_DBG("command processing failure: %s\n", tree->reason.s); if (tree->reason.s) xmlrpc_env_set_fault(env, tree->code, tree->reason.s); else xmlrpc_env_set_fault(env, tree->code, "Error"); goto error; } if ( recur_build_response_array(env, (&tree->node)->kids, &buf) != 0 ) { LM_ERR("failed to read from the MI tree!\n"); xmlrpc_env_set_fault(env, 500, "Failed to write reply"); goto error; } return 0; error: if ( reply_buffer ) pkg_free(reply_buffer); return -1; } static int recur_flush_response_array(xmlrpc_env * env, struct mi_node *tree, str *buf) { struct mi_node *kid, *tmp; int ret; for(kid = tree->kids ; kid ; ){ /* write the current kid */ if (!(kid->flags & MI_WRITTEN)){ if (xr_write_node( buf, kid)!=0) { LM_ERR("failed to write - line too long!\n"); return -1; } /* we are sure that this node has been written * => avoid writing it again */ kid->flags |= MI_WRITTEN; } reply_buffer[reply_buffer_len-buf->len] = 0; xmlrpc_force_to_xml_chars(reply_buffer); reply_item = xmlrpc_build_value(env, "s", reply_buffer); xmlrpc_array_append_item(env, xr_response, reply_item); buf->s = reply_buffer; buf->len = reply_buffer_len; /* write the current kid's children */ if ((ret = recur_flush_response_array(env, kid, buf))<0) return -1; else if (ret > 0) return ret; if (!(kid->flags & MI_NOT_COMPLETED)){ tmp = kid; kid = kid->next; tree->kids = kid; free_mi_node(tmp); } else{ /* the node will have more kids => to keep the tree shape, do not * flush any other node for now */ return 1; } } return 0; } int xr_flush_response_array(xmlrpc_env * env, struct mi_root *tree) { str buf; buf.s = reply_buffer; buf.len = reply_buffer_len; /* test if mi root value is 200 OK (if not no point to continue) */ if ( tree->code<200 || tree->code>=300 ){ LM_DBG("command processing failure: %s\n", tree->reason.s); if (tree->reason.s) xmlrpc_env_set_fault(env, tree->code, tree->reason.s); else xmlrpc_env_set_fault(env, tree->code, "Error"); goto error; } if ( recur_flush_response_array(env, (&tree->node)->kids, &buf) != 0 ) { LM_ERR("failed to read from the MI tree!\n"); xmlrpc_env_set_fault(env, 500, "Failed to write reply"); goto error; } return 0; error: if ( reply_buffer ) pkg_free(reply_buffer); return -1; } static int recur_build_response( xmlrpc_env * env, struct mi_node * tree, str * buf ) { for ( ; tree ; tree = tree->next ) { if (!(tree->flags & MI_WRITTEN)) { if ( xr_write_node( buf, tree ) != 0 ) { reply_buffer = (char*) pkg_realloc ( reply_buffer, 2*reply_buffer_len); if ( !reply_buffer ){ LM_ERR("pkg_realloc cannot reallocate any more memory!\n"); return -1; } buf->s = reply_buffer +(reply_buffer_len - buf->len); buf->len += reply_buffer_len; reply_buffer_len *=2 ; if ( xr_write_node( buf, tree ) != 0 ) { LM_ERR("failed to get MI node data!\n"); return -1; } } /* we are sure that this node has been written * => avoid writing it again */ tree->flags |= MI_WRITTEN; } if ( tree->kids ) { if ( recur_build_response(env, tree->kids, buf) != 0 ) return -1; } } return 0; } char* xr_build_response( xmlrpc_env * env, struct mi_root * tree ) { str buf; int len; len = strlen(reply_buffer); buf.s = reply_buffer + len; buf.len = reply_buffer_len - len; if ( tree->code<200 || tree->code>=300 ){ LM_DBG("command processing failure: %s\n", tree->reason.s); if (tree->reason.s) xmlrpc_env_set_fault(env, tree->code, tree->reason.s); else xmlrpc_env_set_fault(env, tree->code, "Error"); return 0; } if ( recur_build_response(env, (&tree->node)->kids, &buf) != 0 ) { LM_ERR("failed to read from the MI tree!\n"); xmlrpc_env_set_fault(env, 500, "Failed to build reply"); return 0; } reply_buffer[reply_buffer_len-buf.len] = 0; xmlrpc_force_to_xml_chars(reply_buffer); return reply_buffer; } static int recur_flush_response(xmlrpc_env * env, struct mi_node *tree, str *buf) { struct mi_node *kid, *tmp; int ret; if (!rpl_opt) { if (recur_build_response(env, tree, buf) != 0) { LM_ERR("failed to read from the MI tree!\n"); return -1; } return 0; } for(kid = tree->kids ; kid ; ){ /* write the current kid */ if (!(kid->flags & MI_WRITTEN)){ if (xr_write_node( buf, kid)!=0) { reply_buffer = (char*) pkg_realloc ( reply_buffer, 2*reply_buffer_len); if ( !reply_buffer ){ LM_ERR("pkg_realloc cannot reallocate any more memory!\n"); return -1; } buf->s = reply_buffer +(reply_buffer_len - buf->len); buf->len += reply_buffer_len; reply_buffer_len *=2 ; if ( xr_write_node( buf, tree ) != 0 ) { LM_ERR("failed to get MI node data!\n"); return -1; } } /* we are sure that this node has been written * => avoid writing it again */ kid->flags |= MI_WRITTEN; } if ((ret = recur_flush_response_array(env, kid, buf))<0) return -1; else if (ret > 0) return ret; if (!(kid->flags & MI_NOT_COMPLETED)){ tmp = kid; kid = kid->next; tree->kids = kid; free_mi_node(tmp); } else{ /* the node will have more kids => to keep the tree shape, do not * flush any other node for now */ return 1; } } return 0; } char* xr_flush_response( xmlrpc_env * env, struct mi_root * tree ) { str buf; int len; len = strlen(reply_buffer); buf.s = reply_buffer + len; buf.len = reply_buffer_len - len; if ( tree->code<200 || tree->code>=300 ){ LM_DBG("command processing failure: %s\n", tree->reason.s); if (tree->reason.s) xmlrpc_env_set_fault(env, tree->code, tree->reason.s); else xmlrpc_env_set_fault(env, tree->code, "Error"); return 0; } if ( recur_flush_response(env, (&tree->node)->kids, &buf) != 0 ) { LM_ERR("failed to read from the MI tree!\n"); xmlrpc_env_set_fault(env, 500, "Failed to build reply"); return 0; } reply_buffer[reply_buffer_len-buf.len] = 0; return reply_buffer; } opensips-2.2.2/obsolete_modules/mi_xmlrpc/xr_writer.h000066400000000000000000000026121300170765700231200ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of Open SIP Server (opensips). * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-11-30 first version (lavinia) */ #ifndef _XR_WRITER_H_ #define _XR_WRITER_H_ #include #define XMLRPC_WANT_INTERNAL_DECLARATIONS #include #include "../../mi/tree.h" int xr_writer_init( unsigned int size ); void xr_writer_reset(void); char * xr_build_response( xmlrpc_env * env, struct mi_root * tree ); char * xr_flush_response( xmlrpc_env * env, struct mi_root * tree ); int xr_build_response_array( xmlrpc_env * env, struct mi_root * tree ); int xr_flush_response_array( xmlrpc_env * env, struct mi_root * tree ); #endif /* _XR_WRITER_H_ */ opensips-2.2.2/obsolete_modules/pdt/000077500000000000000000000000001300170765700175165ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/pdt/Makefile000066400000000000000000000003241300170765700211550ustar00rootroot00000000000000# $Id$ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pdt.so LIBS= include ../../Makefile.modules opensips-2.2.2/obsolete_modules/pdt/README000066400000000000000000000242151300170765700204020ustar00rootroot00000000000000pdt Module Elena-Ramona Modroiu Rosdev.ro Edited by Elena-Ramona Modroiu Copyright © 2003 FhG FOKUS Copyright © 2004 Voice Sistem SRL Copyright © 2008 Elena-Ramona Modroiu __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.2. Dependencies 1.2.1. OpenSIPS Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. db_url (string) 1.3.2. db_table (string) 1.3.3. sdomain_column (string) 1.3.4. prefix_column (string) 1.3.5. domain_column (string) 1.3.6. prefix (string) 1.3.7. char_list (string) 1.3.8. check_domain (integer) 1.4. Exported Functions 1.4.1. prefix2domain(rewrite_mode, multidomain_mode) 1.4.2. prefix2domain(rewrite_mode) 1.4.3. prefix2domain() 1.5. Exported MI Functions 1.5.1. pdt_add 1.5.2. pdt_delete 1.5.3. pdt_list 1.5.4. pdt_reload 1.6. Installation and Running List of Examples 1.1. prefix-domain translation 1.2. Set db_url parameter 1.3. Set db_table parameter 1.4. Set sdomain_column parameter 1.5. Set prefix_column parameter 1.6. Set domain_column parameter 1.7. Set prefix parameter 1.8. Set char_list parameter 1.9. Set check_domain parameter 1.10. prefix2domain usage Chapter 1. Admin Guide 1.1. Overview This module translates a numerical prefix into a domain and updates accordingly the request URI. The module looks up at the R-URI part of a message and if the user part begins with an established prefix it will update the URI. Updating the uri consists of: remove the prefix from the user part of the uri and keep the rest as the user part of the new uri. The host and port are changed with the domain matched for the leading prefix and the domain in From URI. <:password>@ ... and the result will be: <:password>@... Example 1.1. prefix-domain translation prefix=123, domain(FROM)=siphub.org entry in database: sdomain=siphub.org domain[123]=alpha.org domain[124]=beta.org domain[125]=gamma.org The RURI will be updated in the following way" sip:12391001@mydomain.com => sip:91001@alpha.org The prefix could be prefixed by other digits. These digits will not be used to look up the domain (the classic example, 00 used for international calls, then follows the country prefix). For more information on this, see 'prefix' parameter. * A sample config file is located in './doc/'. * MySQL script to create the database needed by PDT is located in '../../scripts/mysql/pdt-create.sql' The database is loaded by OpenSIPS only at start up time and only cache is used to lookup domains. Check the MI Functions for adding/deleting prefix-domain pairs or reloading from database at runtime. * Sample shell scripts to manage prefix-domain pairs are also located in './doc/' (pdt_fifo_add.sh, pdt_fifo_delete.sh, pdt_fifo_list.sh). 1.2. Dependencies 1.2.1. OpenSIPS Modules The following modules must be loaded before this module: * A OpenSIPS database module (e.g., mysql, dbtext). 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running OpenSIPS with this module loaded: * None. 1.3. Exported Parameters 1.3.1. db_url (string) URL of the database table to be used. Default value is “mysql://opensips:opensipsrw@localhost/opensipsâ€. Example 1.2. Set db_url parameter ... modparam("pdt", "db_url", "dbdriver://username:password@dbhost/dbname") ... 1.3.2. db_table (string) Table name. Default value is “pdtâ€. Example 1.3. Set db_table parameter ... modparam("pdt", "db_table", "pdt") ... 1.3.3. sdomain_column (string) Name of 'sdomain' column. Default value is “sdomainâ€. Example 1.4. Set sdomain_column parameter ... modparam("pdt", "domain_column", "source_domain") ... 1.3.4. prefix_column (string) Name of 'prefix' column. Default value is “prefixâ€. Example 1.5. Set prefix_column parameter ... modparam("pdt", "prefix_column", "prefix") ... 1.3.5. domain_column (string) Name of 'domain' column. Default value is “domainâ€. Example 1.6. Set domain_column parameter ... modparam("pdt", "domain_column", "hostname") ... 1.3.6. prefix (string) Default leading prefix who denotes what URI needs to be translated - if it is NULL the module will not check the R-URI against it and the PDT prefix is considered starting from the first digit. Otherwise, the module will check first if the R-URI starts with it and will skip it to look up the domain. Default value is NULL. Example 1.7. Set prefix parameter ... modparam("pdt", "prefix", "00") ... 1.3.7. char_list (string) The list with characters allowed in prefix. Default value is “0123456789â€. Example 1.8. Set char_list parameter ... modparam("pdt", "char_list", "0123456789*+") ... 1.3.8. check_domain (integer) Module will check if destination domain is duplicated for same source domain (1 - check; 0 - don't check). Default value is 1. Example 1.9. Set check_domain parameter ... modparam("pdt", "check_domain", 0) ... 1.4. Exported Functions 1.4.1. prefix2domain(rewrite_mode, multidomain_mode) Build a new URI if it is necessary. Returns 1 when the translation was made or there was nothing to translate (user part of the URI is empty, it does not match the prefix parameter or there is no domain associated with a possible prefix from user part). Returns -1 in error cases. The translation is done based on lookup up for a entry in the database where the sdomain equals the domain in FROM uri, and the prefix matches the beginning of the user part of the RURI. If such an entry is found, then the domain in RURI is updated with the domain of this entry (sdomain, prefix, domain). There is also the possibility to have the translation of URI regardless of source domain. This can be achieved inserting in the database entries where sdomain has the value "*". The “rewrite_mode†parameter specifies whether to strip or not the prefix from user part. The possible values are: * 0: the prefix is removed along with the leading prefix. * 1: only the leading prefix is removed. * 2: the user part of the URI is not changed. * $PV : any PV holding one of the above values. The “multidomain_mode†parameter specifies the kind of multidomain support to use. The possible values are: * 0 : Translation of URI regardless of source domain. * 1 : Translation of URI using as source domain the domain in From-URI. * 2 : Translation of URI using as source domain the domain in From-URI. In case there is no entry for the required sdomain, it tries the translation using "*" as sdomain. * $PV : any PV holding one of the above values. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. Example 1.10. prefix2domain usage ... prefix2domain("2", "2"); ... $var(a) = 1; prefix2domain("$var(a)", "2"); ... 1.4.2. prefix2domain(rewrite_mode) The same as prefix2domain(rewrite_mode, "0"), that is without multidomain support, translation of URI being done regardless of the source domain. ... prefix2domain("2"); ... 1.4.3. prefix2domain() The same as prefix2domain("0", "0"). ... prefix2domain(); ... 1.5. Exported MI Functions The database is loaded by OpenSIPS at start up time. The module uses only the cache to look up domains. If you want to add or delete a new prefix-domain pair at runtime you have to use MI FIFO commands. All changes made via these commands are applied to database and the cache is updated correspondingly. 1.5.1. pdt_add Adds a new sdomain-prefix-domain entry. Name: pdt_add Parameters: * _sdomain_ : source domain * _prefix_: prefix * _domain_: domain corresponding to a pair of source domain and prefix MI FIFO Command Format: :pdt_add:_reply_fifo_file_ _sdomain_ _prefix_ _domain_ _empty_line_ 1.5.2. pdt_delete Removes a sdomain-prefix-domain entry. Name: pdt_delete Parameters: * _sdomain_ : a source domain * _domain_: a domain associated via a prefix with the source domain MI FIFO Command Format: :pdt_delete:_reply_fifo_file_ _sdomain_ _domain_ _empty_line_ 1.5.3. pdt_list Produces a listing of the entries prefixes/domains/sdomains. Name: pdt_list Parameters: * _sdomain_ : a source domain value. * _prefix_ : a prefix value * _domain_: a domain value "." (dot) means NULL value The comparison operation is 'START WITH' -- if domain is 'a' then all domains starting with 'a' are listed. MI FIFO Command Format: :pdt_list:_reply_fifo_file_ _sdomain_ _prefix_ _domain_ _empty_line_ Examples: * “pdt_list siph 2 .†: Lists the entries where sdomain is starting with 'siph', prefix is starting with '2' and domain is anything * “pdt_list siph 2†: Lists the entries where sdomain is starting with 'siph', prefix is starting with '2' and domain is anything * “pdt_list . 2 open†: Lists the entries where sdomain is anything, prefix starts with '2' and domain starts with 'open'. 1.5.4. pdt_reload Reload all sdomain-prefix-domain records from database. Name: pdt_reload Parameters: * none MI FIFO Command Format: :pdt_reload:_reply_fifo_file_ _empty_line_ 1.6. Installation and Running Example shell scripts for MI FIFO commands are placed in './doc/' (pdt_fifo_add.sh, pdt_fifo_delete.sh, pdt_fifo_list.sh). opensips-2.2.2/obsolete_modules/pdt/doc/000077500000000000000000000000001300170765700202635ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/pdt/doc/pdt.cfg000066400000000000000000000040511300170765700215330ustar00rootroot00000000000000# # $Id$ # # simple quick-start config script for pdt # # ----------- global configuration parameters ------------------------ debug=9 # debug level (cmd line: -dddddddddd) fork=no log_stderror=yes# (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 listen=127.0.0.1 # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "modules/mysql/mysql.so" loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/pdt/pdt.so" loadmodule "modules/xlog/xlog.so" loadmodule "modules/mi_fifo/mi_fifo.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! #loadmodule "/usr/lib/ser/modules/auth.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # -- usrloc params -- #modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line #modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # #modparam("auth", "secret", "alsdkhglaksdhfkloiwr") #modparam("auth", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # #modparam("auth", "password_column", "password") modparam("pdt", "db_url", "mysql://ramona@localhost/pdt") modparam("pdt", "db_table", "pd_multidomain") modparam("pdt", "prefix", "2") modparam("pdt", "hsize_2pow", 2) # ------------------------- request routing logic ------------------- # main routing logic route{ xlog("I: r-uri: <$ru> from-uri:<$fu>\n"); prefix2domain(); xlog("II: r-uri: <$ru> from-uri:<$fu>\n"); } opensips-2.2.2/obsolete_modules/pdt/doc/pdt.xml000066400000000000000000000022041300170765700215720ustar00rootroot00000000000000 %docentities; ]> pdt Module &osipsname; Elena-Ramona Modroiu Rosdev.ro ramona@rosdev.ro Elena-Ramona Modroiu ramona@rosdev.ro 2003 &fhg; 2004 &voicesystem; 2008 Elena-Ramona Modroiu &admin; &faq; opensips-2.2.2/obsolete_modules/pdt/doc/pdt_admin.xml000066400000000000000000000335371300170765700227570ustar00rootroot00000000000000 &adminguide;
Overview This module translates a numerical prefix into a domain and updates accordingly the request &uri;. The module looks up at the R-&uri; part of a message and if the user part begins with an established prefix it will update the &uri;. Updating the uri consists of: remove the prefix from the user part of the uri and keep the rest as the user part of the new uri. The host and port are changed with the domain matched for the leading prefix and the domain in From URI. <prefix><userid><:password>@<mydomain.com> ... and the result will be: <userid><:password>@<domain[:port]>... prefix-domain translation prefix=123, domain(FROM)=siphub.org entry in database: sdomain=siphub.org domain[123]=alpha.org domain[124]=beta.org domain[125]=gamma.org The RURI will be updated in the following way" sip:12391001@mydomain.com => sip:91001@alpha.org The prefix could be prefixed by other digits. These digits will not be used to look up the domain (the classic example, 00 used for international calls, then follows the country prefix). For more information on this, see 'prefix' parameter. A sample config file is located in './doc/'. MySQL script to create the database needed by PDT is located in '../../scripts/mysql/pdt-create.sql' The database is loaded by &osips; only at start up time and only cache is used to lookup domains. Check the MI Functions for adding/deleting prefix-domain pairs or reloading from database at runtime. Sample shell scripts to manage prefix-domain pairs are also located in './doc/' (pdt_fifo_add.sh, pdt_fifo_delete.sh, pdt_fifo_list.sh).
Dependencies
&osips; Modules The following modules must be loaded before this module: A &osips; database module (e.g., mysql, dbtext).
External Libraries or Applications The following libraries or applications must be installed before running &osips; with this module loaded: None.
Exported Parameters
<varname>db_url</varname> (string) URL of the database table to be used. Default value is &defaultdb;. Set <varname>db_url</varname> parameter ... modparam("pdt", "db_url", "&exampledb;") ...
<varname>db_table</varname> (string) Table name. Default value is pdt. Set <varname>db_table</varname> parameter ... modparam("pdt", "db_table", "pdt") ...
<varname>sdomain_column</varname> (string) Name of 'sdomain' column. Default value is sdomain. Set <varname>sdomain_column</varname> parameter ... modparam("pdt", "domain_column", "source_domain") ...
<varname>prefix_column</varname> (string) Name of 'prefix' column. Default value is prefix. Set <varname>prefix_column</varname> parameter ... modparam("pdt", "prefix_column", "prefix") ...
<varname>domain_column</varname> (string) Name of 'domain' column. Default value is domain. Set <varname>domain_column</varname> parameter ... modparam("pdt", "domain_column", "hostname") ...
<varname>prefix</varname> (string) Default leading prefix who denotes what &uri; needs to be translated - if it is NULL the module will not check the R-&uri; against it and the PDT prefix is considered starting from the first digit. Otherwise, the module will check first if the R-&uri; starts with it and will skip it to look up the domain. Default value is NULL. Set <varname>prefix</varname> parameter ... modparam("pdt", "prefix", "00") ...
<varname>char_list</varname> (string) The list with characters allowed in prefix. Default value is 0123456789. Set <varname>char_list</varname> parameter ... modparam("pdt", "char_list", "0123456789*+") ...
<varname>check_domain</varname> (integer) Module will check if destination domain is duplicated for same source domain (1 - check; 0 - don't check). Default value is 1. Set <varname>check_domain</varname> parameter ... modparam("pdt", "check_domain", 0) ...
Exported Functions
<function moreinfo="none">prefix2domain(rewrite_mode, multidomain_mode)</function> Build a new &uri; if it is necessary. Returns 1 when the translation was made or there was nothing to translate (user part of the &uri; is empty, it does not match the prefix parameter or there is no domain associated with a possible prefix from user part). Returns -1 in error cases. The translation is done based on lookup up for a entry in the database where the sdomain equals the domain in FROM uri, and the prefix matches the beginning of the user part of the RURI. If such an entry is found, then the domain in RURI is updated with the domain of this entry (sdomain, prefix, domain). There is also the possibility to have the translation of &uri; regardless of source domain. This can be achieved inserting in the database entries where sdomain has the value "*". The rewrite_mode parameter specifies whether to strip or not the prefix from user part. The possible values are: 0: the prefix is removed along with the leading prefix. 1: only the leading prefix is removed. 2: the user part of the URI is not changed. $PV : any PV holding one of the above values. The multidomain_mode parameter specifies the kind of multidomain support to use. The possible values are: 0 : Translation of &uri; regardless of source domain. 1 : Translation of &uri; using as source domain the domain in From-URI. 2 : Translation of &uri; using as source domain the domain in From-URI. In case there is no entry for the required sdomain, it tries the translation using "*" as sdomain. $PV : any PV holding one of the above values. This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. <function>prefix2domain</function> usage ... prefix2domain("2", "2"); ... $var(a) = 1; prefix2domain("$var(a)", "2"); ...
<function moreinfo="none">prefix2domain(rewrite_mode)</function> The same as prefix2domain(rewrite_mode, "0"), that is without multidomain support, translation of &uri; being done regardless of the source domain. ... prefix2domain("2"); ...
<function moreinfo="none">prefix2domain()</function> The same as prefix2domain("0", "0"). ... prefix2domain(); ...
Exported MI Functions The database is loaded by &osips; at start up time. The module uses only the cache to look up domains. If you want to add or delete a new prefix-domain pair at runtime you have to use MI FIFO commands. All changes made via these commands are applied to database and the cache is updated correspondingly.
<function moreinfo="none">pdt_add</function> Adds a new sdomain-prefix-domain entry. Name: pdt_add Parameters: _sdomain_ : source domain _prefix_: prefix _domain_: domain corresponding to a pair of source domain and prefix MI FIFO Command Format: :pdt_add:_reply_fifo_file_ _sdomain_ _prefix_ _domain_ _empty_line_
<function moreinfo="none">pdt_delete</function> Removes a sdomain-prefix-domain entry. Name: pdt_delete Parameters: _sdomain_ : a source domain _domain_: a domain associated via a prefix with the source domain MI FIFO Command Format: :pdt_delete:_reply_fifo_file_ _sdomain_ _domain_ _empty_line_
<function moreinfo="none">pdt_list</function> Produces a listing of the entries prefixes/domains/sdomains. Name: pdt_list Parameters: _sdomain_ : a source domain value. _prefix_ : a prefix value _domain_: a domain value "." (dot) means NULL value The comparison operation is 'START WITH' -- if domain is 'a' then all domains starting with 'a' are listed. MI FIFO Command Format: :pdt_list:_reply_fifo_file_ _sdomain_ _prefix_ _domain_ _empty_line_ Examples: pdt_list siph 2 . : Lists the entries where sdomain is starting with 'siph', prefix is starting with '2' and domain is anything pdt_list siph 2 : Lists the entries where sdomain is starting with 'siph', prefix is starting with '2' and domain is anything pdt_list . 2 open : Lists the entries where sdomain is anything, prefix starts with '2' and domain starts with 'open'.
<function moreinfo="none">pdt_reload</function> Reload all sdomain-prefix-domain records from database. Name: pdt_reload Parameters: none MI FIFO Command Format: :pdt_reload:_reply_fifo_file_ _empty_line_
Installation and Running Example shell scripts for MI FIFO commands are placed in './doc/' (pdt_fifo_add.sh, pdt_fifo_delete.sh, pdt_fifo_list.sh).
opensips-2.2.2/obsolete_modules/pdt/doc/pdt_fifo_add.sh000066400000000000000000000001551300170765700232220ustar00rootroot00000000000000cat /tmp/opensips_reply & cat > /tmp/opensips_fifo << EOF :pdt_add:opensips_reply localhost *57 127.com EOF opensips-2.2.2/obsolete_modules/pdt/doc/pdt_fifo_delete.sh000066400000000000000000000001541300170765700237330ustar00rootroot00000000000000cat /tmp/opensips_reply & cat > /tmp/opensips_fifo << EOF :pdt_delete:opensips_reply localhost 127.com EOF opensips-2.2.2/obsolete_modules/pdt/doc/pdt_fifo_list.sh000066400000000000000000000001421300170765700234410ustar00rootroot00000000000000cat /tmp/opensips_reply & cat > /tmp/opensips_fifo << EOF :pdt_list:opensips_reply local . . EOF opensips-2.2.2/obsolete_modules/pdt/pdt.c000066400000000000000000000561711300170765700204630ustar00rootroot00000000000000/** * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-04-07: a structure for both hashes introduced (ramona) * 2003-04-06: db connection closed in mod_init (janakj) * 2004-06-07: updated to the new DB api (andrei) * 2005-01-26: removed terminating code (ramona) * prefix hash replaced with tree (ramona) * FIFO commands to add/list/delete prefix domains (ramona) * pdt cache per process for fast translation (ramona) * 2006-01-30: multi domain support added */ /* * Prefix-Domains Translation - ser module * Ramona Modroiu */ #include #include #include #include "../../sr_module.h" #include "../../db/db_op.h" #include "../../db/db.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../parser/parse_uri.h" #include "../../timer.h" #include "../../ut.h" #include "../../rw_locking.h" #include "../../action.h" #include "../../mod_fix.h" #include "../../parser/parse_from.h" #include "pdtree.h" #define NR_KEYS 3 /** structures containing prefix-domain pairs */ pdt_tree_t **_ptree = NULL; /** database connection */ static db_con_t *db_con = NULL; static db_func_t pdt_dbf; /** parameters */ static str db_url = {NULL, 0}; static str db_table = str_init("pdt"); static str sdomain_column = str_init("sdomain"); static str prefix_column = str_init("prefix"); static str domain_column = str_init("domain"); static int pdt_check_domain = 1; /** pstn prefix */ str prefix = {"", 0}; /* List of allowed chars for a prefix*/ str pdt_char_list = {"0123456789", 10}; /* reader-writers lock */ static rw_lock_t *pdt_lock = NULL; static int w_prefix2domain(struct sip_msg* msg, char* str1, char* str2); static int w_prefix2domain_1(struct sip_msg* msg, char* mode, char* str2); static int w_prefix2domain_2(struct sip_msg* msg, char* mode, char* sd_en); static int mod_init(void); static void mod_destroy(void); static int child_init(void); static int mod_child_init(int r); static int prefix2domain(struct sip_msg*, int mode, int sd_en); static struct mi_root* pdt_mi_reload(struct mi_root*, void* param); static struct mi_root* pdt_mi_add(struct mi_root*, void* param); static struct mi_root* pdt_mi_delete(struct mi_root*, void* param); static struct mi_root* pdt_mi_list(struct mi_root*, void* param); static int update_new_uri(struct sip_msg *msg, int plen, str *d, int mode); static int pdt_load_db(); static cmd_export_t cmds[]={ {"prefix2domain", (cmd_function)w_prefix2domain, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"prefix2domain", (cmd_function)w_prefix2domain_1, 1, fixup_igp_null, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"prefix2domain", (cmd_function)w_prefix2domain_2, 2, fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {0, 0, 0, 0, 0, 0} }; static param_export_t params[]={ {"db_url", STR_PARAM, &db_url.s}, {"db_table", STR_PARAM, &db_table.s}, {"sdomain_column", STR_PARAM, &sdomain_column.s}, {"prefix_column", STR_PARAM, &prefix_column.s}, {"domain_column", STR_PARAM, &domain_column.s}, {"prefix", STR_PARAM, &prefix.s}, {"char_list", STR_PARAM, &pdt_char_list.s}, {"check_domain", INT_PARAM, &pdt_check_domain}, {0, 0, 0} }; static mi_export_t mi_cmds[] = { { "pdt_add", 0, pdt_mi_add, 0, 0, child_init }, { "pdt_reload", 0, pdt_mi_reload, 0, 0, 0 }, { "pdt_delete", 0, pdt_mi_delete, 0, 0, 0 }, { "pdt_list", 0, pdt_mi_list, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0} }; static dep_export_t deps = { { /* OpenSIPS module dependencies */ { MOD_TYPE_SQLDB, NULL, DEP_ABORT }, { MOD_TYPE_NULL, NULL, 0 }, }, { /* modparam dependencies */ { NULL, NULL }, }, }; struct module_exports exports = { "pdt", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ &deps, /* OpenSIPS module dependencies */ cmds, NULL, params, 0, mi_cmds, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* response function */ mod_destroy, /* destroy function */ mod_child_init /* per child init function */ }; /** * init module function */ static int mod_init(void) { LM_INFO("initializing...\n"); init_db_url( db_url , 0 /*cannot be null*/); db_table.len = strlen(db_table.s); sdomain_column.len = strlen(sdomain_column.s); prefix_column.len = strlen(prefix_column.s); domain_column.len = strlen(domain_column.s); prefix.len = strlen(prefix.s); pdt_char_list.len = strlen(pdt_char_list.s); if(pdt_char_list.len<=0) { LM_ERR("invalid pdt char list\n"); return -1; } LM_INFO("pdt_char_list=%s \n",pdt_char_list.s); /* binding to mysql module */ if(db_bind_mod(&db_url, &pdt_dbf)) { LM_ERR("database module not found\n"); return -1; } if (!DB_CAPABILITY(pdt_dbf, DB_CAP_ALL)) { LM_ERR("database module does not " "implement all functions needed by the module\n"); return -1; } /* open a connection with the database */ db_con = pdt_dbf.init(&db_url); if(db_con==NULL) { LM_ERR("failed to connect to the database\n"); return -1; } if (pdt_dbf.use_table(db_con, &db_table) < 0) { LM_ERR("failed to use_table\n"); goto error1; } LM_DBG("database connection opened successfully\n"); /* create & init lock */ if ((pdt_lock = lock_init_rw()) == NULL) { LM_CRIT("failed to init lock\n"); goto error1; } /* tree pointer in shm */ _ptree = (pdt_tree_t**)shm_malloc( sizeof(pdt_tree_t*) ); if (_ptree==0) { LM_ERR("out of shm mem for pdtree\n"); goto error1; } *_ptree=0; /* loading all information from database */ if(pdt_load_db()!=0) { LM_ERR("cannot load info from database\n"); goto error1; } pdt_dbf.close(db_con); db_con = 0; #if 0 pdt_print_tree(*_ptree); #endif /* success code */ return 0; error1: if (pdt_lock) { lock_destroy_rw( pdt_lock ); pdt_lock = 0; } if(_ptree!=0) shm_free(_ptree); if(db_con!=NULL) { pdt_dbf.close(db_con); db_con = 0; } return -1; } static int child_init(void) { db_con = pdt_dbf.init(&db_url); if(db_con==NULL) { LM_ERR("failed to connect to database\n"); return -1; } if (pdt_dbf.use_table(db_con, &db_table) < 0) { LM_ERR("use_table failed\n"); return -1; } return 0; } /* each child get a new connection to the database */ static int mod_child_init(int r) { if ( child_init()!=0 ) return -1; LM_DBG("#%d: database connection opened successfully\n",r); return 0; } static void mod_destroy(void) { LM_DBG("cleaning up\n"); if (_ptree!=NULL) { if (*_ptree!=NULL) pdt_free_tree(*_ptree); shm_free(_ptree); } if (db_con!=NULL && pdt_dbf.close!=NULL) pdt_dbf.close(db_con); /* destroy lock */ if (pdt_lock) { lock_destroy_rw( pdt_lock ); pdt_lock = 0; } } static int w_prefix2domain(struct sip_msg* msg, char* str1, char* str2) { return prefix2domain(msg, 0, 0); } static int w_prefix2domain_1(struct sip_msg* msg, char* mode, char* str2) { int m; if(fixup_get_ivalue(msg, (gparam_p)mode, &m)!=0) { LM_ERR("no mode value\n"); return -1; } if(m!=1 && m!=2) m = 0; return prefix2domain(msg, m, 0); } static int w_prefix2domain_2(struct sip_msg* msg, char* mode, char* sdm) { int m, s; if(fixup_get_ivalue(msg, (gparam_p)mode, &m)!=0) { LM_ERR("no mode value\n"); return -1; } if(m!=1 && m!=2) m = 0; if(fixup_get_ivalue(msg, (gparam_p)sdm, &s)!=0) { LM_ERR("no multi-domain mode value\n"); return -1; } if(s!=1 && s!=2) s = 0; return prefix2domain(msg, m, s); } /* change the r-uri if it is a PSTN format */ static int prefix2domain(struct sip_msg* msg, int mode, int sd_en) { str *d, p, all={"*",1}; int plen; struct sip_uri uri; if(msg==NULL) { LM_ERR("received null msg\n"); return -1; } /* parse the uri, if not yet */ if(msg->parsed_uri_ok==0) if(parse_sip_msg_uri(msg)<0) { LM_ERR("failed to parse the R-URI\n"); return -1; } /* if the user part begin with the prefix for PSTN users, extract the code*/ if (msg->parsed_uri.user.len<=0) { LM_DBG("user part of the message is empty\n"); return -1; } if(prefix.len>0) { if (msg->parsed_uri.user.len<=prefix.len) { LM_DBG("user part is less than prefix\n"); return -1; } if(strncasecmp(prefix.s, msg->parsed_uri.user.s, prefix.len)!=0) { LM_DBG("PSTN prefix did not matched\n"); return -1; } } if(prefix.len>0 && prefix.len < msg->parsed_uri.user.len && strncasecmp(prefix.s, msg->parsed_uri.user.s, prefix.len)!=0) { LM_DBG("PSTN prefix did not matched\n"); return -1; } p.s = msg->parsed_uri.user.s + prefix.len; p.len = msg->parsed_uri.user.len - prefix.len; lock_start_read( pdt_lock ); if(sd_en==2) { /* take the domain from FROM uri as sdomain */ if(parse_from_header(msg)<0 || msg->from == NULL || get_from(msg)==NULL) { LM_ERR("cannot parse FROM header\n"); goto error; } memset(&uri, 0, sizeof(struct sip_uri)); if (parse_uri(get_from(msg)->uri.s, get_from(msg)->uri.len , &uri)<0) { LM_ERR("failed to parse From uri\n"); goto error; } /* find the domain that corresponds to this prefix */ plen = 0; if((d=pdt_get_domain(*_ptree, &uri.host, &p, &plen))==NULL) { plen = 0; if((d=pdt_get_domain(*_ptree, &all, &p, &plen))==NULL) { LM_INFO("no prefix found in [%.*s]\n", p.len, p.s); goto error; } } } else if(sd_en==1) { /* take the domain from FROM uri as sdomain */ if(parse_from_header(msg)<0 || msg->from == NULL || get_from(msg)==NULL) { LM_ERR("ERROR cannot parse FROM header\n"); goto error; } memset(&uri, 0, sizeof(struct sip_uri)); if (parse_uri(get_from(msg)->uri.s, get_from(msg)->uri.len , &uri)<0) { LM_ERR("failed to parse From uri\n"); goto error; } /* find the domain that corresponds to this prefix */ plen = 0; if((d=pdt_get_domain(*_ptree, &uri.host, &p, &plen))==NULL) { LM_INFO("no prefix found in [%.*s]\n", p.len, p.s); goto error; } } else { /* find the domain that corresponds to this prefix */ plen = 0; if((d=pdt_get_domain(*_ptree, &all, &p, &plen))==NULL) { LM_INFO("no prefix found in [%.*s]\n", p.len, p.s); goto error; } } /* update the new uri */ if(update_new_uri(msg, plen, d, mode)<0) { LM_ERR("new_uri cannot be updated\n"); goto error; } lock_stop_read( pdt_lock ); return 1; error: lock_stop_read( pdt_lock ); return -1; } /* change the uri according to translation of the prefix */ static int update_new_uri(struct sip_msg *msg, int plen, str *d, int mode) { struct action act; if(msg==NULL || d==NULL) { LM_ERR("bad parameters\n"); return -1; } if(mode==0 || (mode==1 && prefix.len>0)) { act.type = STRIP_T; act.elem[0].type = NUMBER_ST; if(mode==0) act.elem[0].u.number = plen + prefix.len; else act.elem[0].u.number = prefix.len; act.next = 0; if (do_action(&act, msg) < 0) { LM_ERR("failed to remove prefix\n"); return -1; } } act.type = SET_HOSTPORT_T; act.elem[0].type = STR_ST; act.elem[0].u.s = *d; act.next = 0; if (do_action(&act, msg) < 0) { LM_ERR("failed to change domain\n"); return -1; } LM_DBG("len=%d uri=%.*s\n", msg->new_uri.len, msg->new_uri.len, msg->new_uri.s); return 0; } static int pdt_load_db(void) { db_key_t db_cols[3] = {&sdomain_column, &prefix_column, &domain_column}; str p, d, sdomain; db_res_t* db_res = NULL; int i, ret; pdt_tree_t *_ptree_new = NULL; pdt_tree_t *old_tree = NULL; int no_rows = 10; if(db_con==NULL) { LM_ERR("no db connection\n"); return -1; } if (pdt_dbf.use_table(db_con, &db_table) < 0) { LM_ERR("failed to use_table\n"); return -1; } if (DB_CAPABILITY(pdt_dbf, DB_CAP_FETCH)) { if(pdt_dbf.query(db_con,0,0,0,db_cols,0,3,&sdomain_column,0) < 0) { LM_ERR("Error while querying db\n"); return -1; } no_rows = estimate_available_rows( 64+16+64, 3); if (no_rows==0) no_rows = 10; if(pdt_dbf.fetch_result(db_con, &db_res, no_rows)<0) { LM_ERR("Error while fetching result\n"); if (db_res) pdt_dbf.free_result(db_con, db_res); goto error; } else { if(RES_ROW_N(db_res)==0) { return 0; } } } else { if((ret=pdt_dbf.query(db_con, NULL, NULL, NULL, db_cols, 0, 3, &sdomain_column, &db_res))!=0 || RES_ROW_N(db_res)<=0 ) { pdt_dbf.free_result(db_con, db_res); if( ret==0) { return 0; } else { goto error; } } } do { for(i=0; i " "duplicated\n", sdomain.len, sdomain.s, p.len, p.s, d.len, d.s); continue; } if(pdt_add_to_tree(&_ptree_new, &sdomain, &p, &d)<0) { LM_ERR("Error adding info to tree\n"); goto error; } } if (DB_CAPABILITY(pdt_dbf, DB_CAP_FETCH)) { if(pdt_dbf.fetch_result(db_con, &db_res, no_rows)<0) { LM_ERR("Error while fetching!\n"); if (db_res) pdt_dbf.free_result(db_con, db_res); goto error; } } else { break; } } while(RES_ROW_N(db_res)>0); pdt_dbf.free_result(db_con, db_res); /* block all readers */ lock_start_write( pdt_lock ); old_tree = *_ptree; *_ptree = _ptree_new; lock_stop_write( pdt_lock ); /* free old data */ if (old_tree!=NULL) pdt_free_tree(old_tree); return 0; error: pdt_dbf.free_result(db_con, db_res); if (_ptree_new!=NULL) pdt_free_tree(_ptree_new); return -1; } /**************************** MI ***************************/ /** * "pdt_reload" syntax : * \n */ static struct mi_root* pdt_mi_reload(struct mi_root *cmd_tree, void *param) { /* re-loading all information from database */ if(pdt_load_db()!=0) { LM_ERR("cannot re-load info from database\n"); goto error; } return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: return init_mi_tree( 500, "Failed to reload",16); } /** * "pdt_add" syntax : * sdomain * prefix * domain */ struct mi_root* pdt_mi_add(struct mi_root* cmd_tree, void* param) { db_key_t db_keys[NR_KEYS] = {&sdomain_column, &prefix_column, &domain_column}; db_val_t db_vals[NR_KEYS]; db_op_t db_ops[NR_KEYS] = {OP_EQ, OP_EQ}; int i= 0; str sd, sp, sdomain; struct mi_node* node= NULL; if(_ptree==NULL) { LM_ERR("strange situation\n"); return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN); } /* read sdomain */ node = cmd_tree->node.kids; if(node == NULL) goto error1; sdomain = node->value; if(sdomain.s == NULL || sdomain.len== 0) return init_mi_tree( 404, "domain not found", 16); if(*sdomain.s=='.' ) return init_mi_tree( 400, "empty param",11); /* read prefix */ node = node->next; if(node == NULL) goto error1; sp= node->value; if(sp.s== NULL || sp.len==0) { LM_ERR("could not read prefix\n"); return init_mi_tree( 404, "prefix not found", 16); } if(*sp.s=='.') return init_mi_tree(400, "empty param", 11); while(i< sp.len) { if(strpos(pdt_char_list.s,sp.s[i]) < 0) return init_mi_tree(400, "bad prefix", 10); i++; } /* read domain */ node= node->next; if(node == NULL || node->next!=NULL) goto error1; sd= node->value; if(sd.s== NULL || sd.len==0) { LM_ERR("could not read domain\n"); return init_mi_tree( 400, "domain not found", 16); } if(*sd.s=='.') return init_mi_tree(400, "empty param", 11); if(pdt_check_domain!=0 && *_ptree!=NULL && pdt_check_pd(*_ptree, &sdomain, &sp, &sd)==1) { LM_ERR("(sdomain,prefix,domain) exists\n"); return init_mi_tree(400, "(sdomain,prefix,domain) exists already", 38); } db_vals[0].type = DB_STR; db_vals[0].nul = 0; db_vals[0].val.str_val.s = sdomain.s; db_vals[0].val.str_val.len = sdomain.len; db_vals[1].type = DB_STR; db_vals[1].nul = 0; db_vals[1].val.str_val.s = sp.s; db_vals[1].val.str_val.len = sp.len; db_vals[2].type = DB_STR; db_vals[2].nul = 0; db_vals[2].val.str_val.s = sd.s; db_vals[2].val.str_val.len = sd.len; /* insert a new domain into database */ if(pdt_dbf.insert(db_con, db_keys, db_vals, NR_KEYS)<0) { LM_ERR("failed to store new prefix/domain\n"); return init_mi_tree( 500,"Cannot store prefix/domain", 26); } /* re-loading all information from database */ if(pdt_load_db()!=0) { LM_ERR("cannot re-load info from database\n"); goto error; } LM_DBG("new prefix added %.*s-%.*s => %.*s\n", sdomain.len, sdomain.s, sp.len, sp.s, sd.len, sd.s); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: if(pdt_dbf.delete(db_con, db_keys, db_ops, db_vals, NR_KEYS)<0) LM_ERR("database/cache are inconsistent\n"); return init_mi_tree( 500, "could not add to cache", 23 ); error1: return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); } /** * "pdt_delete" syntax: * sdomain * domain */ struct mi_root* pdt_mi_delete(struct mi_root* cmd_tree, void* param) { str sd, sdomain; struct mi_node* node= NULL; db_key_t db_keys[2] = {&sdomain_column, &domain_column}; db_val_t db_vals[2]; db_op_t db_ops[2] = {OP_EQ, OP_EQ}; /* read sdomain */ node = cmd_tree->node.kids; if(node == NULL) goto error; sdomain = node->value; if(sdomain.s == NULL || sdomain.len== 0) return init_mi_tree( 404, "domain not found", 16); if( *sdomain.s=='.' ) return init_mi_tree( 400, "400 empty param",11); /* read domain */ node= node->next; if(node == NULL || node->next!=NULL) goto error; sd= node->value; if(sd.s== NULL || sd.len==0) { LM_ERR("could not read domain\n"); return init_mi_tree(404, "domain not found", 16); } if(*sd.s=='.') return init_mi_tree( 400, "empty param", 11); db_vals[0].type = DB_STR; db_vals[0].nul = 0; db_vals[0].val.str_val.s = sdomain.s; db_vals[0].val.str_val.len = sdomain.len; db_vals[1].type = DB_STR; db_vals[1].nul = 0; db_vals[1].val.str_val.s = sd.s; db_vals[1].val.str_val.len = sd.len; if(pdt_dbf.delete(db_con, db_keys, db_ops, db_vals, 2)<0) { LM_ERR("database/cache are inconsistent\n"); return init_mi_tree( 500, "database/cache are inconsistent", 31 ); } /* re-loading all information from database */ if(pdt_load_db()!=0) { LM_ERR("cannot re-load info from database\n"); return init_mi_tree( 500, "cannot reload", 13 ); } LM_DBG("prefix for sdomain [%.*s] domain [%.*s] " "removed\n", sdomain.len, sdomain.s, sd.len, sd.s); return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); error: return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); } int pdt_print_mi_node(pdt_node_t *pt, struct mi_node* rpl, char *code, int len, str *sdomain, str *sd, str *sp) { int i; struct mi_node* node = NULL; struct mi_attr* attr= NULL; if(pt==NULL || len>=PDT_MAX_DEPTH) return 0; for(i=0; is==NULL && sd->s==NULL) || (sp->s==NULL && (sd->s!=NULL && pt[i].domain.len==sd->len && strncasecmp(pt[i].domain.s, sd->s, sd->len)==0)) || (sd->s==NULL && (len+1>=sp->len && strncmp(code, sp->s, sp->len)==0)) || ((sp->s!=NULL && len+1>=sp->len && strncmp(code, sp->s, sp->len)==0) && (sd->s!=NULL && pt[i].domain.len>=sd->len && strncasecmp(pt[i].domain.s, sd->s, sd->len)==0))) { node = add_mi_node_child(rpl, 0, "PDT", 3, 0, 0); if(node == NULL) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "SDOMAIN", 7, sdomain->s, sdomain->len); if(attr == NULL) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "PREFIX", 6, code, len+1); if(attr == NULL) goto error; attr = add_mi_attr(node, MI_DUP_VALUE,"DOMAIN", 6, pt[i].domain.s, pt[i].domain.len); if(attr == NULL) goto error; } } if(pdt_print_mi_node(pt[i].child, rpl, code, len+1, sdomain, sd, sp)<0) goto error; } return 0; error: return -1; } /** * "pdt_list" syntax : * sdomain * prefix * domain * * - '.' (dot) means NULL value and will match anything * - the comparison operation is 'START WITH' -- if domain is 'a' then * all domains starting with 'a' are listed * * Examples * pdt_list o 2 . - lists the entries where sdomain is starting with 'o', * prefix is starting with '2' and domain is anything * * pdt_list . 2 open - lists the entries where sdomain is anything, prefix * starts with '2' and domain starts with 'open' */ struct mi_root* pdt_mi_list(struct mi_root* cmd_tree, void* param) { str sd, sp, sdomain; pdt_tree_t *pt; struct mi_node* node = NULL; unsigned int i= 0; struct mi_root* rpl_tree = NULL; struct mi_node* rpl = NULL; static char code_buf[PDT_MAX_DEPTH+1]; int len; if(_ptree==NULL) { LM_ERR("empty domain list\n"); return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN); } /* read sdomain */ sdomain.s = 0; sdomain.len = 0; sp.s = 0; sp.len = 0; sd.s = 0; sd.len = 0; node = cmd_tree->node.kids; if(node != NULL) { sdomain = node->value; if(sdomain.s == NULL || sdomain.len== 0) return init_mi_tree( 404, "domain not found", 16); if(*sdomain.s=='.') sdomain.s = 0; /* read prefix */ node = node->next; if(node != NULL) { sp= node->value; if(sp.s== NULL || sp.len==0 || *sp.s=='.') sp.s = NULL; else { while(sp.s!=NULL && i!=sp.len) { if(strpos(pdt_char_list.s,sp.s[i]) < 0) { LM_ERR("bad prefix [%.*s]\n", sp.len, sp.s); return init_mi_tree( 400, "bad prefix", 10); } i++; } } /* read domain */ node= node->next; if(node != NULL) { sd= node->value; if(sd.s== NULL || sd.len==0 || *sd.s=='.') sd.s = NULL; } } } rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if(rpl_tree == NULL) return 0; rpl = &rpl_tree->node; rpl->flags |= MI_IS_ARRAY; if(*_ptree==0) return rpl_tree; pt = *_ptree; while(pt!=NULL) { if(sdomain.s==NULL || (sdomain.s!=NULL && pt->sdomain.len>=sdomain.len && strncmp(pt->sdomain.s, sdomain.s, sdomain.len)==0)) { len = 0; if(pdt_print_mi_node(pt->head, rpl, code_buf, len, &pt->sdomain, &sd, &sp)<0) goto error; } pt = pt->next; } return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } opensips-2.2.2/obsolete_modules/pdt/pdtree.c000066400000000000000000000215651300170765700211560ustar00rootroot00000000000000/** * Copyright (C) 2005 Voice Sistem SRL (Voice-System.RO) * * This file is part of opensips, a free SIP server. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2005-01-25 first tree version (ramona) * 2006-01-30 multi domain support added (ramona) */ #include #include #include #include #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../ut.h" #include "pdtree.h" //extern str pdt_char_list = {"1234567890*",11}; extern str pdt_char_list; pdt_tree_t* pdt_init_tree(str* sdomain) { pdt_tree_t *pt = NULL; pt = (pdt_tree_t*)shm_malloc(sizeof(pdt_tree_t)); if(pt==NULL) { LM_ERR("no more shm memory\n"); return NULL; } memset(pt, 0, sizeof(pdt_tree_t)); pt->sdomain.s = (char*)shm_malloc((1+sdomain->len)*sizeof(char)); if(pt->sdomain.s==NULL) { shm_free(pt); LM_ERR("no more shm memory\n"); return NULL; } memset(pt->sdomain.s, 0,1+sdomain->len ); memcpy(pt->sdomain.s, sdomain->s, sdomain->len); pt->sdomain.len = sdomain->len; // printf("sdomain:%.*s\n", pt->sdomain.len, pt->sdomain.s); pt->head = (pdt_node_t*)shm_malloc(PDT_NODE_SIZE*sizeof(pdt_node_t)); if(pt->head == NULL) { shm_free(pt->sdomain.s); shm_free(pt); LM_ERR("no more shm memory\n"); return NULL; } memset(pt->head, 0, PDT_NODE_SIZE*sizeof(pdt_node_t)); return pt; } int add_to_tree(pdt_tree_t *pt, str *sp, str *sd) { int l; pdt_node_t *itn, *itn0; if(pt==NULL || sp==NULL || sp->s==NULL || sd==NULL || sd->s==NULL) { LM_ERR("bad parameters\n"); return -1; } if(sp->len>=PDT_MAX_DEPTH) { LM_ERR("max prefix len exceeded\n"); return -1; } l = 0; itn0 = pt->head; itn = itn0[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].child; while(l < sp->len-1) { if(strpos(pdt_char_list.s,sp->s[l]) < 0) { LM_ERR("invalid char %d in prefix [%c (0x%x)]\n", l, sp->s[l], sp->s[l]); return -1; } if(itn == NULL) { itn = (pdt_node_t*)shm_malloc(PDT_NODE_SIZE*sizeof(pdt_node_t)); if(itn == NULL) { LM_ERR("no more shm mem\n"); return -1; } memset(itn, 0, PDT_NODE_SIZE*sizeof(pdt_node_t)); itn0[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].child = itn; } l++; itn0 = itn; itn = itn0[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].child; } if(itn0[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].domain.s!=NULL) { LM_ERR("prefix already allocated [%.*s/[%.*s]\n", sp->len, sp->s, sd->len, sd->s); return -1; } itn0[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].domain.s = (char*)shm_malloc((sd->len+1)*sizeof(char)); if(itn0[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].domain.s==NULL) { LM_ERR("no more shm mem!\n"); return -1; } strncpy(itn0[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].domain.s, sd->s, sd->len); itn0[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].domain.len = sd->len; itn0[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].domain.s[sd->len] = '\0'; return 0; } pdt_tree_t* pdt_get_tree(pdt_tree_t *pl, str *sdomain) { pdt_tree_t *it; if(pl==NULL) return NULL; if( sdomain==NULL || sdomain->s==NULL) { LM_ERR("bad parameters\n"); return NULL; } it = pl; /* search the tree for the asked sdomain */ while(it!=NULL && str_strcmp(&it->sdomain, sdomain)<0) it = it->next; if(it==NULL || str_strcmp(&it->sdomain, sdomain)>0) return NULL; return it; } int pdt_add_to_tree(pdt_tree_t **dpt, str *sdomain, str *code, str *domain) { pdt_tree_t *ndl, *it, *prev; if( sdomain==NULL || sdomain->s==NULL || code==NULL || code->s==NULL || domain==NULL || domain->s==NULL) { LM_ERR("bad parameters\n"); return -1; } ndl = NULL; it = *dpt; prev = NULL; /* search the it position before which to insert new domain */ while(it!=NULL && str_strcmp(&it->sdomain, sdomain)<0) { prev = it; it = it->next; } // printf("sdomain:%.*s\n", sdomain->len, sdomain->s); /* add new sdomain*/ if(it==NULL || str_strcmp(&it->sdomain, sdomain)>0) { ndl = pdt_init_tree(sdomain); if(ndl==NULL) { LM_ERR("no more shm memory\n"); return -1; } if(add_to_tree(ndl, code, domain)<0) { LM_ERR("internal error!\n"); return -1; } ndl->next = it; /* new domain must be added as first element */ if(prev==NULL) *dpt = ndl; else prev->next=ndl; } else /* add (prefix, code) to already present sdomain */ if(add_to_tree(it, code, domain)<0) { LM_ERR("internal error!\n"); return -1; } return 0; } str* get_domain(pdt_tree_t *pt, str *sp, int *plen) { int l, len; pdt_node_t *itn; str *domain; if(pt==NULL || sp==NULL || sp->s==NULL) { LM_ERR("bad parameters\n"); return NULL; } l = len = 0; itn = pt->head; domain = NULL; while(itn!=NULL && l < sp->len && l < PDT_MAX_DEPTH) { /* check validity */ if(strpos(pdt_char_list.s,sp->s[l]) < 0) { LM_ERR("invalid char at %d in [%.*s]\n", l, sp->len, sp->s); return NULL; } if(itn[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].domain.s!=NULL) { domain = &itn[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].domain; len = l+1; } itn = itn[strpos(pdt_char_list.s,sp->s[l])%PDT_NODE_SIZE].child; l++; } if(plen!=NULL) *plen = len; return domain; } str* pdt_get_domain(pdt_tree_t *pl, str* sdomain, str *code, int *plen) { pdt_tree_t *it; int len; str *domain=NULL; if(pl==NULL || sdomain==NULL || sdomain->s==NULL || code == NULL || code->s == NULL) { LM_ERR("bad parameters\n"); return NULL; } it = pl; while(it!=NULL && str_strcmp(&it->sdomain, sdomain)<0) it = it->next; if(it==NULL || str_strcmp(&it->sdomain, sdomain)>0) return NULL; domain = get_domain(it, code, &len); if(plen!=NULL) *plen = len; return domain; } void pdt_free_node(pdt_node_t *pn) { int i; if(pn==NULL) return; for(i=0; ihead!=NULL) pdt_free_node(pt->head); if(pt->next!=NULL) pdt_free_tree(pt->next); if(pt->sdomain.s!=NULL) shm_free(pt->sdomain.s); shm_free(pt); pt = NULL; return; } int pdt_print_node(pdt_node_t *pn, char *code, int len) { int i; if(pn==NULL || code==NULL || len>=PDT_MAX_DEPTH) return 0; for(i=0; isdomain.len, pt->sdomain.s); len = 0; pdt_print_node(pt->head, pdt_code_buf, len); return pdt_print_tree(pt->next); } int pdt_check_pd_node(pdt_node_t *pn, str *sp, str *sd, char *code, int len) { int i; int ret; if(pn==NULL || code==NULL || len>=PDT_MAX_DEPTH) return 0; ret = 0; for(i=0; ilen == len+1 && strncmp(sp->s, code, sp->len)==0) { LM_DBG("duplicated prefix\n"); return 1; } if(sd->len == pn[i].domain.len && strncmp(sd->s, pn[i].domain.s, sd->len)==0) { LM_DBG("duplicated domain\n"); return 1; } } ret = pdt_check_pd_node(pn[i].child, sp, sd, code, len+1); if(ret != 0) break; } return ret; } /* returns * 1 if prefix or domain already exists * 0 if prefix or domain does not exist * -1 if any error */ int pdt_check_pd(pdt_tree_t *pt, str* sdomain, str *sp, str *sd) { int len; int ret; pdt_tree_t *it; if(pt==NULL || sp==NULL || sd==NULL) { LM_ERR("bad parameters\n"); return -1; } it = pt; while(it!=NULL) { if(it->sdomain.len==sdomain->len && strncasecmp(it->sdomain.s, sdomain->s, sdomain->len)==0) break; it = it->next; } if(it == NULL) return 0; len = 0; ret = pdt_check_pd_node(it->head, sp, sd, pdt_code_buf, len); return ret; } opensips-2.2.2/obsolete_modules/pdt/pdtree.h000066400000000000000000000034621300170765700211570ustar00rootroot00000000000000/** * Copyright (C) 2005 Voice Sistem SRL (Voice-System.RO) * * This file is part of opensips, a free SIP server. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2005-01-25 first tree version (ramona) */ #ifndef _PDTREE_H_ #define _PDTREE_H_ #include "../../str.h" typedef struct _pdt_node { str domain; struct _pdt_node *child; } pdt_node_t; #define PDT_MAX_DEPTH 32 #define PDT_NODE_SIZE pdt_char_list.len typedef struct _pdt_tree { str sdomain; pdt_node_t *head; struct _pdt_tree *next; } pdt_tree_t; /* prefix tree operations */ int add_to_tree(pdt_tree_t *pt, str *code, str *domain); int pdt_add_to_tree(pdt_tree_t **dpt, str* sdomain, str *code, str *domain); pdt_tree_t* pdt_get_tree(pdt_tree_t *pl, str *sdomain); str* get_domain(pdt_tree_t *pt, str *code, int *plen); str* pdt_get_domain(pdt_tree_t *pt, str* sdomain, str *code, int *plen); pdt_tree_t* pdt_init_tree(str* sdomain); void pdt_free_tree(pdt_tree_t *pt); int pdt_print_tree(pdt_tree_t *pt); int pdt_check_pd(pdt_tree_t *pt, str* sdomain, str *sp, str *sd); /* used to get the index for the PDT Tree hash*/ #define strpos(s,c) (strchr(s,c)-s) #endif opensips-2.2.2/obsolete_modules/pdt/scripts/000077500000000000000000000000001300170765700212055ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/pdt/scripts/db_berkeley/000077500000000000000000000000001300170765700234545ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/pdt/scripts/db_berkeley/pdt000066400000000000000000000002271300170765700241670ustar00rootroot00000000000000METADATA_COLUMNS id(int) sdomain(str) prefix(str) domain(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|'' opensips-2.2.2/obsolete_modules/pdt/scripts/dbtext/000077500000000000000000000000001300170765700224775ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/pdt/scripts/dbtext/pdt000066400000000000000000000000741300170765700232120ustar00rootroot00000000000000id(int,auto) sdomain(string) prefix(string) domain(string) opensips-2.2.2/obsolete_modules/pdt/scripts/mysql/000077500000000000000000000000001300170765700223525ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/pdt/scripts/mysql/pdt-create.sql000066400000000000000000000005111300170765700251200ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('pdt','2'); CREATE TABLE pdt ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, sdomain CHAR(128) NOT NULL, prefix CHAR(32) NOT NULL, domain CHAR(128) DEFAULT '' NOT NULL, CONSTRAINT sdomain_prefix_idx UNIQUE (sdomain, prefix) ) ENGINE=MyISAM; opensips-2.2.2/obsolete_modules/pdt/scripts/opensips-pdt.xml000066400000000000000000000005711300170765700243570ustar00rootroot00000000000000 %entities; ]> Prefix-Domain Translation opensips-2.2.2/obsolete_modules/pdt/scripts/oracle/000077500000000000000000000000001300170765700224525ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/pdt/scripts/oracle/pdt-create.sql000066400000000000000000000006351300170765700252270ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('pdt','2'); CREATE TABLE pdt ( id NUMBER(10) PRIMARY KEY, sdomain VARCHAR2(128), prefix VARCHAR2(32), domain VARCHAR2(128) DEFAULT '', CONSTRAINT pdt_sdomain_prefix_idx UNIQUE (sdomain, prefix) ); CREATE OR REPLACE TRIGGER pdt_tr before insert on pdt FOR EACH ROW BEGIN auto_id(:NEW.id); END pdt_tr; / BEGIN map2users('pdt'); END; / opensips-2.2.2/obsolete_modules/pdt/scripts/pdt.xml000066400000000000000000000033271300170765700225230ustar00rootroot00000000000000 %entities; ]> pdt2&MYSQL_TABLE_TYPE; Prefix-Domain Translation means to change the host and port in R-URI, based on the prefix found in R-URI and source domain (that is domain in From-URI). More information can be found at: &OPENSIPS_MOD_DOC;pdt.html id unsigned int &table_id_len; int,auto Unique ID sdomain string &uri_len; Source domain prefix string 32 Prefix found in the username part of R-URI. domain string &uri_len; Domain corresponding to (sdomain, prefix) pair where the message must be sent. sdomain_prefix_idx
opensips-2.2.2/obsolete_modules/pdt/scripts/pi_http/000077500000000000000000000000001300170765700226545ustar00rootroot00000000000000opensips-2.2.2/obsolete_modules/pdt/scripts/pi_http/pdt-mod000066400000000000000000000022401300170765700241410ustar00rootroot00000000000000 pdt show pdt DB_QUERY
display table content .TP 16 .I Command 'speeddial' - manage speed dials (short numbers) .TP 12 .B speeddial show show speeddial details .TP .B speeddial list list speeddial for uri .TP .B speeddial add [] add a speedial (*) .TP .B speeddial rm remove a speeddial (*) .TP .B speeddial help help message .TP 16 .I Command 'avp' - manage AVPs .TP 12 .B avp list [-T table] [-u ] [-a attribute] [-v value] [-t type] list AVPs .TP .B avp add [-T table] add AVP (*) .TP .B avp rm [-T table] [-u ] [-a attribute] [-v value] [-t type] remove AVP (*) .TP .B avp help help message .TP 16 .I Command 'alias_db' - manage database aliases .TP 12 .B alias_db show show alias details .TP .B alias_db list list aliases for uri .TP .B alias_db add add an alias (*) .TP .B alias_db rm remove an alias (*) .TP .B alias_db help help message .TP 16 .I Command 'domain' - manage local domains .TP 12 .B domain reload reload domains from disk .TP .B domain show show current domains in memory .TP .B domain showdb show domains in the database .TP .B domain add add the domain to the database .TP .B domain rm delete the domain from the database .TP 16 .I Command 'fifo' .TP 12 .B fifo send raw FIFO command .TP 16 .I Command 'dialplan' - manage dialplans .TP 12 .B dialplan show show dialplan tables .TP .B dialplan reload reload dialplan tables .TP .B dialplan addrule add a rule .TP .B dialplan rm removes the entire dialplan table .TP .B dialplan rmdpid removes all the gived dpid entries .TP .B dialplan rmrule removes all the gived dpid/prio entries .TP 16 .I Command 'unixsock' .TP 12 .B unixsock send raw unixsock command .TP 16 .I Command 'droute' .TP 12 .B dr gateway add [] [] [] [] adds new route .TP .B dr gateway rm removes route .TP .B dr gateway list lists route(s) .TP .B dr gateway list lists route(s) .TP .B dr gateway list lists all routes .TP .B dr gateway h droute help .TP .B dr rules add [][] [][][][] adds new rule(s) .TP .B dr rules rm removes rules .TP .B dr rules list lists rules(s) .TP .B dr rules list lists rules(s) .TP .B dr rules list lists rules(s) .TP .B dr rules h dr rules help .TP .B dr h dr help .TP 16 .I Databases management: .TP 12 .B db create .TP creates a new database .B db presence adds the presence related tables .TP .B db extra adds the extra tables .TP .B db migrate migrates DB from 1.5 to 1.6 .TP .B db drop !entirely deletes tables! .TP .B db reinit !entirely deletes and than re-creates tables! .TP .B db backup dumps current database to file .TP .B db restore restores tables from a file .TP .B db copy creates a new db from an existing one .TP 16 .I Berkeley DB tables management: .TP 12 .B bdb | db_berkeley list lists the underlying db files in DB_PATH .TP .B bdb | db_berkeley cat db_dump the underlying db file to STDOUT .TP .B bdb | db_berkeley swap installs db.new by db -> db.old; db.new -> db .TP .B bdb | db_berkeley append appends data to an existing db;output DB_PATH/db.new .TP .B bdb | db_berkeley newappend appends data to a new instance of db; output DB_PATH/db.new .TP .B bdb | db_berkeley export exports table data to plain-txt files in dump_dir .TP .B bdb | db_berkeley import imports plain-txt table data and creates new db tables in db_path .SH NOTES .PP Commands labeled with (*) will prompt for a MySQL password. If the environment variable PW is set, the password will not be prompted. .PP IP addresses must be entered in dotted quad format e.g. 1.2.3.4 .SH AUTHORS see .B /usr/share/doc/opensips/AUTHORS .PP This manual page was written by Alejandro Rios P. , based on opensipsctl manpage by Bogdan-Andrei Iancu , for the Debian project (and may be used by others). .SH SEE ALSO .BR opensips(8), opensips.cfg(5), opensipsctl(8), osipsconfig(8) .PP Full documentation on opensips is available at .I http://www.opensips.org/. .PP Mailing lists: .nf users@lists.opensips.org - opensips user community .nf devel@lists.opensips.org - opensips development, new features and unstable version opensips-2.2.2/packaging/000077500000000000000000000000001300170765700153075ustar00rootroot00000000000000opensips-2.2.2/packaging/debian/000077500000000000000000000000001300170765700165315ustar00rootroot00000000000000opensips-2.2.2/packaging/debian/changelog000066400000000000000000000002171300170765700204030ustar00rootroot00000000000000opensips (2.2.2-1) stable; urgency=low * Minor Public Release. -- Razvan Crainea Wed, 19 Oct 2016 18:11:45 +0300 opensips-2.2.2/packaging/debian/common/000077500000000000000000000000001300170765700200215ustar00rootroot00000000000000opensips-2.2.2/packaging/debian/common/changelog000066400000000000000000000006501300170765700216740ustar00rootroot00000000000000opensips (2.2.1-1) stable; urgency=low * Major Public Release. -- Razvan Crainea Wed, 20 Jul 2016 13:13:19 +0300 opensips (2.2.0-1) stable; urgency=low * Major Release -- Razvan Crainea Fri, 04 Sep 2015 13:44:06 +0200 opensips (2.1.0) stable; urgency=low * Major Public Release. -- Bogdan-Andrei Iancu Tue, 07 May 2015 19:22:57 +0200 opensips-2.2.2/packaging/debian/common/compat000066400000000000000000000000021300170765700212170ustar00rootroot000000000000009 opensips-2.2.2/packaging/debian/common/control000066400000000000000000000534721300170765700214370ustar00rootroot00000000000000Source: opensips Section: net Priority: optional Maintainer: Razvan Crainea Build-Depends: bison, debhelper (>= 9), dh-systemd (>= 1.5), dh-python, dpkg-dev (>= 1.16.1.1), flex, libconfuse-dev, libcurl4-gnutls-dev, libdb-dev (>= 4.6.19), libexpat1-dev, libfreeradius-client-dev, libgeoip-dev (>= 1.4.4), libhiredis-dev, libjson-c-dev, libldap2-dev, liblua5.1-dev, libmemcached-dev, libmicrohttpd-dev, libmysqlclient-dev, libncurses5-dev, libpcre3-dev, libperl-dev, libpq-dev, librabbitmq-dev, libsctp-dev, libsnmp-dev, libxml2-dev, libxmlrpc-core-c3-dev, pkg-config, python, unixodbc-dev, xsltproc, zlib1g-dev Standards-Version: 3.9.6 Homepage: http://www.opensips.org/ Package: opensips Architecture: any Multi-Arch: foreign Depends: adduser, ${misc:Depends}, ${python:Depends}, ${shlibs:Depends} Suggests: opensips-b2bua-module, opensips-berkeley-module, opensips-carrierroute-module, opensips-compression-module, opensips-console, opensips-cpl-module, opensips-dbhttp-module, opensips-dialplan-module, opensips-emergency-module, opensips-geoip-module, opensips-http-modules, opensips-identity-module, opensips-jabber-module, opensips-json-module, opensips-ldap-modules, opensips-lua-module, opensips-memcached-module, opensips-mysql-module, opensips-perl-modules, opensips-postgres-module, opensips-presence-modules, opensips-rabbitmq-module, opensips-radius-modules, opensips-redis-module, opensips-regex-module, opensips-restclient-module, opensips-sctp-module, opensips-snmpstats-module, opensips-tls-module, opensips-wss-module, opensips-tlsmgm-module, opensips-unixodbc-module, opensips-xmlrpc-module, opensips-xmpp-module Description: very fast and configurable SIP server OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . C Shell-like scripting language provides full control over the server's behaviour. Its modular architecture allows only required functionality to be loaded. . Among others, the following modules are available: Digest Authentication, CPL scripts, Instant Messaging, MySQL support, Presence Agent, Radius Authentication, Record Routing, SMS Gateway, Jabber/XMPP Gateway, Transaction Module, Registrar and User Location, Load Balaning/Dispatching/LCR, XMLRPC Interface. . This package contains the main OpenSIPS binary along with the principal modules and support binaries including opensipsmc configuration tool. Package: opensips-dbg Priority: extra Architecture: any Multi-Arch: same Section: debug Depends: opensips (= ${binary:Version}), ${misc:Depends} Description: very fast and configurable SIP server [debug symbols] OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package contains the debugging symbols for the OpenSIPS binaries and modules. You only need to install it if you need to debug OpenSIPS. Package: opensips-mysql-module Architecture: any Multi-Arch: same Depends: mysql-client, opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: MySQL database connectivity module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the MySQL database driver for OpenSIPS. Package: opensips-postgres-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), postgresql-client, ${misc:Depends}, ${shlibs:Depends} Description: PostgreSQL database connectivity module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the PostgreSQL database driver for OpenSIPS. Package: opensips-jabber-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Jabber gateway module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the SIP to Jabber translator module for OpenSIPS. Package: opensips-cpl-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: CPL module (CPL interpreter engine) for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides a CPL (Call Processing Language) interpreter for OpenSIPS, turning OpenSIPS into a CPL server (storage and interpreter). Package: opensips-radius-modules Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Radius modules for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the RADIUS driver for the AAA API from OpenSIPS. Package: opensips-unixodbc-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: unixODBC database connectivity module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the unixODBC database driver for OpenSIPS. Package: opensips-presence-modules Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: SIMPLE presence modules for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides several OpenSIPS modules for implementing presence server and presence user agent for RICH presence, registrar-based presence, external triggered presence and XCAP support. Package: opensips-xmlrpc-module Architecture: any Depends: opensips (= ${binary:Version}), opensips-http-modules (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: XMLRPC support for OpenSIPS's Management Interface OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the XMLRPC transport implementation for OpenSIPS's Management Interface. Package: opensips-perl-modules Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Perl extensions and database driver for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides an interface for OpenSIPS to write Perl extensions and the db_perlvdb database driver for OpenSIPS. Package: opensips-snmpstats-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), snmpd, ${misc:Depends}, ${shlibs:Depends} Description: SNMP AgentX subagent module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the snmpstats module for OpenSIPS. This module acts as an AgentX subagent which connects to a master agent. Package: opensips-xmpp-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: XMPP gateway module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the SIP to XMPP IM translator module for OpenSIPS. Package: opensips-carrierroute-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Carrierroute module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the carrierroute module for OpenSIPS, an integrated solution for routing, balancing and blacklisting. Package: opensips-berkeley-module Architecture: any Multi-Arch: same Depends: db-util (>= 4.6.19), opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Berkeley Database module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the berkeley database module for OpenSIPS, a high-performance embedded DB kernel. All database tables are stored in files, no additional server is necessary. Package: opensips-berkeley-bin Architecture: any Multi-Arch: foreign Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Berkeley Database module for OpenSIPS - helper program OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the berkeley database module for OpenSIPS, a high-performance embedded DB kernel. You should normally install opensips-berkeley-module and not this package directly. Package: opensips-ldap-modules Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: LDAP modules for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the ldap and h350 modules for OpenSIPS, enabling LDAP queries from the OpenSIPS config and storage of SIP account data in an LDAP directory. Package: opensips-geoip-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: IP address-to-location looku (MaxMind GeoIP API) for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This module is a lightweight wrapper for the MaxMind GeoIP API. It adds IP address-to-location lookup capability to OpenSIPS scripts. Lookups are executed against the freely-available GeoLite City database; and the non-free GeoIP City database is drop-in compatible Lookups are executed against the freely-available GeoLite City database; and the non-free GeoIP City database is drop-in compatible Package: opensips-regex-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: PCRE regexp modules for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides a module for matching operations against regular expressions using the powerful PCRE library. By default, OpenSIPS support sed-like regular expressions; PCRE library brings perl-like regular expressions. Package: opensips-identity-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: SIP Identity module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides support for SIP Identity (see RFC 4474). Package: opensips-b2bua-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: B2B User Agent modules for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides modules for B2BUA support in OpenSIPS. Both the implementation and control (XML based scenario description) are included. Package: opensips-dbhttp-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: HTTP database connectivity module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the HTTP-based database driver for OpenSIPS Package: opensips-dialplan-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Generic string translation module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides dialplan module that implements generic string translations based on matching and replacement rules. It can be used to manipulate R-URI or a PV and to translated to a new format/value. Package: opensips-memcached-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Interface module to interact with a memcached server OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides an implementation of a cache system designed to work with a memcached server. It uses libmemcached client library to connect to several memcached servers that store data. It registers the three functions for storing, fetching and removing a value to the core memcache management interface. Package: opensips-json-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Support for JSON handling in OpenSIPS script OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package introduces a new type of variable that provides both serialization and de-serialization from JSON format. The script variable provides ways to access (from script) objects and arrays to add,replace or delete values from the script. Package: opensips-console Architecture: any Multi-Arch: foreign Depends: libberkeleydb-perl, libfrontier-rpc-perl, libnet-ip-perl, libterm-readline-perl-perl, opensips (= ${binary:Version}), ${misc:Depends} Suggests: opensips-berkeley-module, opensips-mysql-module, opensips-postgres-module, opensips-unixodbc-module, opensips-xmlrpc-module Description: Generic tool for OpenSIPS provisioning OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides an OpenSIPS Console written in Perl for OpenSIPS provisioning. Package: opensips-redis-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Interface module to interact with a Redis server OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides an implementation of a cache system designed to work with a Redis server. It uses hiredis client library to connect to either a single Redis server instance, or to a Redis Server inside a Redis Cluster. It uses the Key-Value interface exported from the core. Package: opensips-rabbitmq-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Interface module to interact with a RabbitMQ server OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the implementation of a RabbitMQ client for the Event Interface. It is used to send AMQP messages to a RabbitMQ server each time the Event Interface triggers an event subscribed for. Package: opensips-lua-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: LUA extensions for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides an interface for OpenSIPS to utilize LUA extensions. Package: opensips-http-modules Architecture: any Multi-Arch: same Depends: libmicrohttpd10|libmicrohttpd12, opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: HTTP transport layer and Management Interface for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides an HTTP transport layer and Management Interface for OpenSIPS. Package: opensips-compression-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), zlib1g, ${misc:Depends}, ${shlibs:Depends} Description: Headers and body compression module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the compression module, that is able to compress SIP headers and body, as well as shrink the size of a SIP package. Package: opensips-emergency-module Architecture: any Multi-Arch: same Depends: opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Emerrgency call module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . The emergency module provides emergency call treatment for OpenSIPS, following the architecture i2 specification of the american entity NENA. (National Emergency Number Association). Package: opensips-tlsmgm-module Architecture: any Multi-Arch: same Depends: libssl-dev, opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: TLS management module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides support for TLS management for OpenSIPS. Package: opensips-tls-module Architecture: any Multi-Arch: same Depends: libssl-dev, opensips (= ${binary:Version}), opensips-tlsmgm-module (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: TLS transport module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the TLS support for OpenSIPS. Package: opensips-wss-module Architecture: any Multi-Arch: same Depends: libssl-dev, opensips (= ${binary:Version}), opensips-tlsmgm-module (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: WebSocket Secure (WSS) transport module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the WebSocket Secure (WSS) support for OpenSIPS. Package: opensips-sctp-module Architecture: any Multi-Arch: same Depends: libsctp-dev, opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: SCTP transport module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the SCTP support for OpenSIPS. Package: opensips-restclient-module Architecture: any Multi-Arch: same Depends: libcurl4-gnutls-dev, opensips (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: REST client module for OpenSIPS OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. . This package provides the REST client support for OpenSIPS. opensips-2.2.2/packaging/debian/common/copyright000066400000000000000000001236141300170765700217630ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: opensips Source: https://github.com/OpenSIPS/opensips Files: * Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: aaa/* Copyright: 2009, Voice System 2009, Irina Stanescu License: GPL-2+ Files: action.c Copyright: 2010-2014, OpenSIPS Solutions 2005, 2006, Voice Sistem S.R.L 2001-2003, FhG Fokus License: GPL-2+ Files: async.c async.h bin_interface.c bin_interface.h context.c context.h reactor.c reactor.h reactor_defs.h sr_module_deps.c sr_module_deps.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: atomic.h Copyright: 2006, kernel.org License: GPL-2+ Files: blacklists.c blacklists.h core_stats.c core_stats.h errinfo.c errinfo.h pt.c statistics.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: cachedb/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: crc.c Copyright: 2001-2003, FhG Fokus 1986, Gary S. Brown. License: GPL-2+ Files: data_lump.c receive.c Copyright: 2008-2015, OpenSIPS Solutions 2001-2004, FhG Fokus License: GPL-2+ Files: db/* Copyright: 2001-2004, FhG Fokus 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: db/db_async.c db/db_async.h db/db_insertq.c db/db_insertq.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: db/db_id.c db/db_id.h db/db_pool.c db/db_pool.h Copyright: 2001-2005, iptel.org 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: db/db_ps.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: db/db_query.c db/db_query.h Copyright: 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: db/example/* Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: debian/* Copyright: 2002 Andrei Pelinescu-Onciul 2006 Julien Blache 2009 Bjoern Boschman 2009 Bogdan Iancu 2014-2015 Julián Moreno Patiño 2011 Daniel Echeverry 2011 Alejandro Rios P. 2015 Răzvan Crainea License: GPL-2+ Files: doc/dbschema/xsl/pi_framework_mod.xsl doc/dbschema/xsl/pi_framework_table.xsl Copyright: 2010-2014, VoIP Embedded, Inc License: GPL-2+ Files: dset.c dset.h qvalue.c qvalue.h Copyright: 2001-2004, FhG FOKUS License: GPL-2+ Files: evi/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: flags.c forward.c name_alias.c name_alias.h Copyright: 2005-2010, Voice Sistem SRL 2001-2005, FhG Fokus License: GPL-2+ Files: futex_lock.h Copyright: 2012, 2013, Ryan Bullock License: GPL-2+ Files: io_wait.c poll_types.h Copyright: 2005, iptelorg GmbH License: GPL-2+ Files: io_wait.h io_wait_loop.h Copyright: 2014, 2015, OpenSIPS Solutions 2005, iptelorg GmbH License: GPL-2+ Files: main.c resolve.c route.c Copyright: 2005-2009, Voice Sistem S.R.L 2001-2003, FhG Fokus License: GPL-2+ Files: map.c map.h Copyright: 2005-2009, Voice System SRL License: GPL-2+ Files: md5utils.c md5utils.h Copyright: 2001-2004, FhG Fokus 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: mem/common.h mem/hp_malloc.c mem/hp_malloc.h mem/hp_malloc_stats.c mem/hp_malloc_stats.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: mem/vq_malloc.c Copyright: 2001-2003, FhG Fokus 1983, Regents of the University of California License: GPL-2+ Files: menuconfig/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: mi/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: mi/mi_core.c Copyright: 2008-2014, OpenSIPS Solutions 2006, Voice Sistem SRL License: GPL-2+ Files: modules/aaa_radius/* Copyright: 2009, Voice System 2009, Irina Stanescu License: GPL-2+ Files: modules/acc/acc.c Copyright: 2006, Voice System SRL 2001-2003, FhG Fokus License: GPL-2+ Files: modules/acc/acc.h modules/acc/acc_logic.c modules/acc/acc_mod.c modules/acc/acc_mod.h Copyright: 2005-2010, Voice Sistem SRL 2001-2005, FhG Fokus License: GPL-2+ Files: modules/acc/acc_extra.c modules/acc/acc_extra.h modules/acc/acc_logic.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/alias_db/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/alias_db/alias_db.h Copyright: 2004, 2006, Voice Sistem License: GPL-2+ Files: modules/auth/index.c modules/auth/index.h Copyright: 2008, Voice System S.R.L License: GPL-2+ Files: modules/auth_aaa/* Copyright: 2009, Voice Systems 2009, Irina Stanescu 2001-2003, FhG Fokus License: GPL-2+ Files: modules/auth_db/aaa_avps.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/avpops/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/b2b_entities/* Copyright: 2009, 2011, Free Software Fundation License: GPL-2+ Files: modules/b2b_logic/* Copyright: 2009, 2011, Free Software Fundation License: GPL-2+ Files: modules/b2b_sca/* Copyright: 2010-2014, VoIP Embedded, Inc License: GPL-2+ Files: modules/benchmark/* Copyright: 2007, Voice Sistem SRL 2007, Collax GmbH License: GPL-2+ Files: modules/cachedb_cassandra/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/cachedb_couchbase/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/cachedb_local/* Copyright: 2009, Anca Vamanu License: GPL-2+ Files: modules/cachedb_memcached/cachedb_memcached.c Copyright: 2009, Voice Sistem SRL 2009, Andrei Dragus License: GPL-2+ Files: modules/cachedb_mongodb/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/cachedb_redis/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/cachedb_sql/cachedb_sql.c Copyright: 2013, Steve Frécinaux 2013, OpenSIPS Solutions License: GPL-2+ Files: modules/call_center/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/call_control/call_control.c Copyright: 2004-2009, Dan Pascu License: GPL-2+ Files: modules/carrierroute/* Copyright: 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: modules/cfgutils/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/cfgutils/cfgutils.c Copyright: 2007, BASIS AudioNet GmbH 2007, 1&1 Internet AG License: GPL-2+ Files: modules/cfgutils/shvar.c modules/cfgutils/shvar.h Copyright: 2007, Elena-Ramona Modroiu License: GPL-2+ Files: modules/compression/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/cpl_c/cpl_loader.c Copyright: 2006, Voice-Sistem SRL 2001-2003, FhG Fokus License: GPL-2+ Files: modules/db_berkeley/* Copyright: 2007, Cisco Systems License: GPL-2+ Files: modules/db_cachedb/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/db_flatstore/flat_mi.c Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/db_flatstore/flat_mi.h Copyright: 2006, Voice Sistem RL License: GPL-2+ Files: modules/db_http/* Copyright: 2009, Voice Sistem SRL 2009, Andrei Dragus License: GPL-2+ Files: modules/db_http/db_http.h Copyright: 2012, Guillaume Bour (Orange-Vallee) License: GPL-2+ Files: modules/db_mysql/* Copyright: 2001-2004, FhG Fokus 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: modules/db_mysql/dbase.c Copyright: 2009, Voice Sistem SRL 2001-2003, FhG Fokus 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: modules/db_mysql/my_con.c Copyright: 2001-2005, iptel.org 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: modules/db_oracle/* Copyright: 2007, 2008, TRUNK MOBILE License: GPL-2+ Files: modules/db_perlvdb/* Copyright: 2006, 2007, Collax GmbH License: GPL-2+ Files: modules/db_postgres/* Copyright: 2003, August.Net Services, LLC 2008, 1&1 Internet AG License: GPL-2+ Files: modules/db_postgres/db_postgres.c Copyright: 2001-2004, FhG Fokus 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: modules/db_postgres/dbase.c modules/db_postgres/res.c Copyright: 2006, Norman Brandinger 2003, August.Net Services, LLC 2008, 1&1 Internet AG License: GPL-2+ Files: modules/db_postgres/pg_con.c Copyright: 2001-2005, iptel.org 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: modules/db_postgres/pg_con.h Copyright: 2003, August.Net Services, LLC License: GPL-2+ Files: modules/db_postgres/res.h modules/db_postgres/val.h Copyright: 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: modules/db_unixodbc/* Copyright: 2005, 2006, Marco Lorrai 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: modules/db_virtual/* Copyright: 2009, Voice Sistem SRL 2009, Razvan License: GPL-2+ Files: modules/dialog/* Copyright: 2005-2009, Voice System SRL License: GPL-2+ Files: modules/dialog/dialog.c Copyright: 2008-2014, OpenSIPS Solutions 2006, Voice Sistem SRL License: GPL-2+ Files: modules/dialog/dlg_cb.c modules/dialog/dlg_load.h modules/dialog/dlg_vals.c modules/dialog/dlg_vals.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/dialog/dlg_cb.h Copyright: 2006, Voice Sistem SRLs License: GPL-2+ Files: modules/dialog/dlg_db_handler.c modules/dialog/dlg_handlers.c modules/dialog/dlg_hash.h modules/dialog/dlg_profile.c modules/dialog/dlg_profile.h Copyright: 2009-2014, OpenSIPS Solutions 2006-2009, Voice System SRL License: GPL-2+ Files: modules/dialog/dlg_replication.c modules/dialog/dlg_replication.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/dialplan/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/dispatcher/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/dispatcher/dispatch.c Copyright: 2010-2015, OpenSIPS Solutions 2005-2010, Voice-System.ro 2004-2006, FhG Fokus License: GPL-2+ Files: modules/dispatcher/dispatch.h Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: modules/dispatcher/dispatcher.c Copyright: 2005-2010, Voice Sistem SRL 2001-2005, FhG Fokus License: GPL-2+ Files: modules/dispatcher/ds_fixups.c modules/dispatcher/ds_fixups.h Copyright: 2014, OpenSIPS Foundation 2006-2010, Voice Sistem SRL 2004, 2005, FhG Fokus License: GPL-2+ Files: modules/dns_cache/dns_cache.c Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/domain/* Copyright: 2001-2008, Juha Heinanen License: GPL-2+ Files: modules/domain/mi.c modules/domain/mi.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/domainpolicy/* Copyright: 2001-2008, Juha Heinanen License: GPL-2+ Files: modules/domainpolicy/domainpolicy.c modules/domainpolicy/domainpolicy_mod.c Copyright: 2006, Otmar Lendl & Klaus Darilion License: GPL-2+ Files: modules/drouting/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/drouting/dr_api.h modules/drouting/dr_api_internal.c modules/drouting/dr_api_internal.h Copyright: 2014, 2015, OpenSIPS Foundation License: GPL-2+ Files: modules/drouting/dr_db_def.c modules/drouting/dr_db_def.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/drouting/dr_partitions.h Copyright: 2014, OpenSIPS Foundation 2006-2010, Voice Sistem SRL 2004, 2005, FhG Fokus License: GPL-2+ Files: modules/emergency/* Copyright: 2014, 2015, Robison Tesini & Evandro Villaron License: GPL-2+ Files: modules/enum/* Copyright: 2001-2008, Juha Heinanen License: GPL-2+ Files: modules/event_datagram/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/event_rabbitmq/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/event_route/* Copyright: 2010-2014, VoIP Embedded, Inc License: GPL-2+ Files: modules/event_route/event_route.c modules/event_route/event_route.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/event_xmlrpc/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/fraud_detection/* Copyright: 2014, 2015, OpenSIPS Foundation License: GPL-2+ Files: modules/gflags/gflags.c Copyright: 2005, 2006, Voice Sistem S.R.L 2004, FhG License: GPL-2+ Files: modules/group/* Copyright: 2009, Voice Systems 2009, Irina Stanescu 2001-2003, FhG Fokus License: GPL-2+ Files: modules/group/re_group.c modules/group/re_group.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/h350/* Copyright: 2007, University of North Carolina License: GPL-2+ Files: modules/httpd/* Copyright: 2011-2013, VoIP Embedded Inc License: GPL-2+ Files: modules/identity/identity.c Copyright: 2009, Voice Sistem SRL 2007, Alexander Christ License: GPL-2+ with OpenSSL exception Files: modules/identity/identity.h Copyright: 2007, Alexander Christ License: GPL-2+ with OpenSSL exception Files: modules/imc/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/jabber/sha.c Copyright: 1999, Scott G. Miller 1999, 2000, Dave Smith & Julian Missig License: GPL-2+ Files: modules/jabber/tree234.c modules/jabber/tree234.h Copyright: 1999-2001, Simon Tatham License: Expat Files: modules/jabber/xode.c modules/jabber/xode.h modules/jabber/xode_from.c modules/jabber/xode_str.c modules/jabber/xpool.c modules/jabber/xstream.c Copyright: 1998, 1999, The Jabber Team http:jabber.org License: GPL-2+ Files: modules/json/* Copyright: 2009, Voice Sistem SRL 2009, Andrei Dragus License: GPL-2+ Files: modules/ldap/* Copyright: 2007, University of North Carolina License: GPL-2+ Files: modules/ldap/iniparser.c modules/ldap/iniparser.h Copyright: 2007, University of North Carolina 2000, Nicolas Devillard (ndevilla AT free DOT fr) License: GPL-2+ Files: modules/load_balancer/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/load_balancer/lb_bl.c modules/load_balancer/lb_bl.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/lua/* Copyright: 2008,2009: Eric Gouyer 2008-2011: Arnaud Chong License: ISC Files: modules/lua/crc32.c modules/lua/crc32.h Copyright: 2003, Markus Friedl. License: BSD-2-clause Files: modules/mathops/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/mediaproxy/mediaproxy.c Copyright: 2004-2009, Dan Pascu License: GPL-2+ Files: modules/mi_datagram/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/mi_fifo/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/mi_fifo/fifo_fnc.c Copyright: 2005-2010, Voice Sistem SRL 2001-2005, FhG Fokus License: GPL-2+ Files: modules/mi_http/* Copyright: 2011-2013, VoIP Embedded Inc License: GPL-2+ Files: modules/mi_http/http_fnc.c Copyright: 2010-2014, VoIP Embedded, Inc License: GPL-2+ Files: modules/mi_json/* Copyright: 2013 shimaore.net License: GPL-2+ Files: modules/mi_xmlrpc_ng/* Copyright: 2011-2013, VoIP Embedded Inc License: GPL-2+ Files: modules/mi_xmlrpc_ng/http_fnc.c Copyright: 2010-2014, VoIP Embedded, Inc License: GPL-2+ Files: modules/mmgeoip/* Copyright: 2008 SightSpeed, Inc License: GPL-2+ Files: modules/nat_traversal/nat_traversal.c Copyright: 2004-2009, Dan Pascu License: GPL-2+ Files: modules/nathelper/nathelper.c Copyright: 2003-2010, Sippy Software, Inc., http:www.sippysoft.com License: GPL-2+ Files: modules/nathelper/sip_pinger.h Copyright: 2005-2009, Voice System SRL License: GPL-2+ Files: modules/osp/* Copyright: 2001-2005, Fhg Fokus License: GPL-2+ Files: modules/path/* Copyright: 2006 Inode GmbH License: GPL-2+ Files: modules/pdt/* Copyright: 2005, Voice Sistem SRL (Voice-System.RO) License: GPL-2+ Files: modules/pdt/pdt.c Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: modules/peering/* Copyright: 2001-2008, Juha Heinanen License: GPL-2+ Files: modules/peering/peering.c Copyright: 2009, Voice System 2009, Irina Stanescu 2003, 2008, Juha Heinanen License: GPL-2+ Files: modules/peering/verify.c Copyright: 2009, Voice System 2009, Irina-Maria Stanescu 2008, Juha Heinanen License: GPL-2+ Files: modules/perl/* Copyright: 2006 Collax GmbH License: GPL-2+ Files: modules/permissions/* Copyright: 2003, Miklós Tirpák (mtirpak@sztaki.hu) License: GPL-2+ Files: modules/permissions/address.c Copyright: 2009, Voice System 2009, Irina Stanescu 2003, 2008, Juha Heinanen License: GPL-2+ Files: modules/permissions/address.h modules/permissions/mi.c modules/permissions/mi.h Copyright: 2001-2008, Juha Heinanen License: GPL-2+ Files: modules/permissions/hash.c modules/permissions/hash.h Copyright: 2009, Voice System 2009, Irina Stanescu License: GPL-2+ Files: modules/permissions/permissions.c Copyright: 2003-2007, Juha Heinanen 2003, iptel.org 2003, Miklós Tirpák (mtirpak@sztaki.hu) License: GPL-2+ Files: modules/permissions/permissions.h Copyright: 2006, Juha Heinanen 2003, Miklós Tirpák (mtirpak@sztaki.hu) License: GPL-2+ Files: modules/pi_http/* Copyright: 2011-2013, VoIP Embedded Inc License: GPL-2+ Files: modules/pi_http/http_db_handler.c modules/pi_http/http_db_handler.h Copyright: 2010-2014, VoIP Embedded, Inc License: GPL-2+ Files: modules/pike/pike_mi.c modules/pike/pike_mi.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/presence/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/presence/presence.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/presence_callinfo/* Copyright: 2013, OpenSIPS Solutions 2010, Ovidiu Sas License: GPL-2+ Files: modules/presence_callinfo/sca_dialog.c modules/presence_callinfo/sca_dialog.h modules/presence_callinfo/sca_hash.c modules/presence_callinfo/sca_hash.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/presence_dialoginfo/* Copyright: 2008, Klaus Darilion, IPCom 2007, Juha Heinanen License: GPL-2+ Files: modules/presence_dialoginfo/notify_body.c modules/presence_dialoginfo/notify_body.h Copyright: 2008, Klaus Darilion, IPCom 2006, Voice Sistem S.R.L License: GPL-2+ Files: modules/presence_dialoginfo/pidf.c modules/presence_dialoginfo/pidf.h Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/presence_dialoginfo/presence_dialoginfo.h Copyright: 2008, Klaus Darilion IPCom 2007, Juha Heinanen License: GPL-2+ Files: modules/presence_mwi/* Copyright: 2001-2008, Juha Heinanen License: GPL-2+ Files: modules/presence_xcapdiff/presence_xcapdiff.c Copyright: 2008, 2012, AG Projects License: GPL-2+ Files: modules/presence_xml/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/proto_sctp/* Copyright: 2008-2015, OpenSIPS Solutions 2001-2004, FhG Fokus License: GPL-2+ Files: modules/proto_sctp/proto_sctp.c Copyright: 2014, 2015, OpenSIPS Foundation License: GPL-2+ Files: modules/proto_tls/* Copyright: 2005 Voice Sistem SRL 2005 Cesc Santasusana 2006 enum.at 2013 Secusmart GmbH 2014, 2015, OpenSIPS Foundation 2001-2003, FhG Fokus 2004, 2005, Free Software Foundation, Inc 2005, 2006, iptelorg GmbH License: GPL-2+ with OpenSSL exception Files: modules/proto_ws/* Copyright: 2015, - OpenSIPS Foundation / 2001-2003, FhG Fokus License: GPL-2+ Files: modules/proto_ws/proto_ws.h Copyright: 2015, OpenSIPS Project License: GPL-2+ Files: modules/pua/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/pua_bla/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/pua_dialoginfo/dialog_publish.c Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/pua_dialoginfo/pua_dialoginfo.c Copyright: 2008, Klaus Darilion IPCom 2007, 2008, Dan Pascu 2006, Voice Sistem S.R.L License: GPL-2+ Files: modules/pua_dialoginfo/pua_dialoginfo.h Copyright: 2008, Klaus Darilion IPCom 2006, Voice Sistem S.R.L License: GPL-2+ Files: modules/pua_mi/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/pua_usrloc/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/pua_xmpp/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/python/* Copyright: 2009 Sippy Software, Inc., http://www.sippysoft.com License: GPL-2+ Files: modules/qos/* Copyright: 2006-2008, SOMA Networks, Inc License: GPL-2+ Files: modules/qos/qos_ctx_helpers.h Copyright: 2006-2008, SOMA Networks, INC License: GPL-2+ Files: modules/ratelimit/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/ratelimit/ratelimit.c Copyright: 2008, Ovidiu Sas 2006, Hendrik Scholz License: GPL-2+ Files: modules/regex/regex_mod.c Copyright: 2008, Iñaki Baz Castillo License: GPL-2+ Files: modules/registrar/path.c modules/registrar/path.h Copyright: 2006, Andreas Granig License: GPL-2+ Files: modules/registrar/save.c Copyright: 2005-2010, Voice Sistem SRL 2001-2005, FhG Fokus License: GPL-2+ Files: modules/rest_client/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/rls/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/rr/api.c modules/rr/api.h modules/rr/rr_cb.c modules/rr/rr_cb.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/rr/loose.c modules/rr/rr_mod.c Copyright: 2008-2015, OpenSIPS Solutions 2001-2004, FhG Fokus License: GPL-2+ Files: modules/rtpengine/* Copyright: 2003-2010 Sippy Software, Inc. 2005 Voice Sistem S.R.L 2009-2014 TuTPro Inc. 2010 VoIPEmbedded Inc. 2013-2014 Sipwise GmbH 2003 Porta Software Ltd 2001-2007, FhG Fokus License: GPL-2+ Files: modules/rtpproxy/* Copyright: 2003-2010 Sippy Software, Inc., http://www.sippysoft.com License: GPL-2+ Files: modules/rtpproxy/nhelpr_funcs.c modules/rtpproxy/nhelpr_funcs.h Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: modules/rtpproxy/timeout_process.c Copyright: 2008, 2010, Voice System License: GPL-2+ Files: modules/script_helper/script_helper.c Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/seas/* Copyright: 2006 VozTelecom Sistemas License: GPL-2+ Files: modules/signaling/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/sipcapture/* Copyright: 2011, Alexandr Dubovikov (QSC AG) (alexandr.dubovikov@gmail.com) License: GPL-2+ Files: modules/sipmsgops/* Copyright: 2009, Voice Sistem SRL 2009, Andrei Dragus License: GPL-2+ Files: modules/sipmsgops/sipmsgops.c Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: modules/siptrace/siptrace.c Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/sl/sl.h Copyright: 2004, 2006, Voice Sistem License: GPL-2+ Files: modules/sl/sl_api.h modules/sl/sl_cb.c modules/sl/sl_cb.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/sms/* Copyright: 2000-2002 Stefan Frings License: GPL-2+ Files: modules/sms/libsms_sms.h modules/sms/sms.c modules/sms/sms_funcs.c modules/sms/sms_funcs.h modules/sms/sms_report.c modules/sms/sms_report.h Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: modules/sngtc/* Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/snmpstats/* Copyright: 2006 SOMA Networks, Inc. License: GPL-2+ Files: modules/speeddial/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/sst/* Copyright: 2006-2008, SOMA Networks, Inc License: GPL-2+ Files: modules/sst/sst.c Copyright: 2006-2008, SOMA Networks, INC License: GPL-2+ Files: modules/statistics/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/stun/* Copyright: 2009, Voice Sistem SRL 2009, Razvan License: GPL-2+ Files: modules/tm/async.c modules/tm/async.h modules/tm/t_ctx.c modules/tm/t_ctx.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/tm/mi.c modules/tm/timer.c Copyright: 2005-2010, Voice Sistem SRL 2001-2005, FhG Fokus License: GPL-2+ Files: modules/tm/mi.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/tm/sip_msg.c modules/tm/t_fwd.c modules/tm/t_msgbuilder.h modules/tm/t_reply.c modules/tm/uac.c Copyright: 2008-2015, OpenSIPS Solutions 2001-2004, FhG Fokus License: GPL-2+ Files: modules/tm/t_stats.h Copyright: 2004, 2006, Voice Sistem License: GPL-2+ Files: modules/topology_hiding/* Copyright: 2014, 2015, OpenSIPS Foundation License: GPL-2+ Files: modules/uac/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/uac/auth.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/uac_auth/* Copyright: 2013, OpenSIPS Solutions 2011, VoIP Embedded Inc License: GPL-2+ Files: modules/uac_auth/uac_auth.h Copyright: 2011-2013, VoIP Embedded Inc License: GPL-2+ Files: modules/uac_redirect/* Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/uac_registrant/* Copyright: 2011-2013, VoIP Embedded Inc License: GPL-2+ Files: modules/uac_registrant/reg_db_handler.c modules/uac_registrant/reg_db_handler.h Copyright: 2010-2014, VoIP Embedded, Inc License: GPL-2+ Files: modules/uri/* Copyright: 2009, Voice Systems 2009, Irina Stanescu 2001-2003, FhG Fokus License: GPL-2+ Files: modules/uri/aaa_checks.h Copyright: 2009, Voice Systems 2009, Irina Stanescu 2002, 2003, Juha Heinanen License: GPL-2+ Files: modules/uri/checks.c modules/uri/checks.h Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: modules/uri/db_checks.c Copyright: 2001-2004, FhG FOKUS License: GPL-2+ Files: modules/uri/db_checks.h Copyright: 2009, Voice System 2009, Irina Stanescu 2001-2004, FhG FOKUS License: GPL-2+ Files: modules/userblacklist/* Copyright: 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: modules/usrloc/ul_mi.c modules/usrloc/ul_mi.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: modules/usrloc/ureplication.c modules/usrloc/ureplication.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: modules/xcap/* Copyright: 2008, 2012, AG Projects License: GPL-2+ Files: modules/xcap_client/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/xmpp/* Copyright: 2006-2009, Voice Sistem S.R.L License: GPL-2+ Files: modules/xmpp/sha.c Copyright: 1999, Scott G. Miller 1999, 2000, Dave Smith & Julian Missig License: GPL-2+ Files: modules/xmpp/xode.c modules/xmpp/xode.h modules/xmpp/xode_from.c modules/xmpp/xode_str.c modules/xmpp/xpool.c modules/xmpp/xstream.c Copyright: 1998, 1999, The Jabber Team http:jabber.org License: GPL-2+ Files: modules/xmpp/xsnprintf.c Copyright: 1995-1998, The Apache Group. License: Apache Files: msg_callbacks.c msg_callbacks.h Copyright: 2003-2010, Sippy Software, Inc., http:www.sippysoft.com License: GPL-2+ Files: msg_translator.c Copyright: 2006, Andreas Granig 2001-2003, FhG Fokus License: GPL-2+ Files: net/* Copyright: 2015, OpenSIPS Project License: GPL-2+ Files: net/net_tcp.c Copyright: 2014, 2015, OpenSIPS Project 2001-2003, FhG Fokus License: GPL-2+ Files: net/net_tcp_proc.c Copyright: 2008-2015, OpenSIPS Solutions 2001-2004, FhG Fokus License: GPL-2+ Files: net/net_udp.c Copyright: 2014, 2015, OpenSIPS Foundation 2001-2003, FhG Fokus License: GPL-2+ Files: net/proto_tcp/* Copyright: 2015, - OpenSIPS Foundation / 2001-2003, FhG Fokus License: GPL-2+ Files: net/proto_tcp/proto_tcp_handler.h Copyright: 2015, OpenSIPS Project License: GPL-2+ Files: net/proto_udp/proto_udp.c Copyright: 2015, - OpenSIPS Foundation / 2001-2003, FhG Fokus License: GPL-2+ Files: net/proto_udp/proto_udp.h Copyright: 2011-2015, OpenSIPS Solutions License: GPL-2+ Files: net/proto_udp/proto_udp_handler.h Copyright: 2015, OpenSIPS Project License: GPL-2+ Files: net/tcp_conn.h net/tcp_conn_defs.h net/tcp_passfd.c net/tcp_passfd.h Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: obsolete_modules/* Copyright: 2000, Moez Mahfoudh License: BSD-3-clause Files: obsolete_modules/auth_diameter/* Copyright: 2001-2007, FhG Fokus License: GPL-2+ Files: obsolete_modules/mi_xmlrpc/mi_xmlrpc.c obsolete_modules/mi_xmlrpc/mi_xmlrpc.h obsolete_modules/mi_xmlrpc/xr_parser.c obsolete_modules/mi_xmlrpc/xr_parser.h obsolete_modules/mi_xmlrpc/xr_server.c obsolete_modules/mi_xmlrpc/xr_server.h obsolete_modules/mi_xmlrpc/xr_writer.c obsolete_modules/mi_xmlrpc/xr_writer.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: obsolete_modules/mi_xmlrpc/xr_parser_lib.h Copyright: 2001, First Peer, Inc. 2001, Eric Kidd. License: BSD-3-clause Files: parser/case_min_.h parser/case_sess.h parser/parse_sst.c parser/parse_sst.h Copyright: 2006, SOMA Networks, Inc. License: GPL-2+ Files: parser/case_p_as.h parser/case_p_pr.h parser/case_priv.h parser/case_refe.h parser/parse_allow.c parser/parse_allow.h parser/parse_methods.c parser/parse_methods.h parser/parse_pai.c parser/parse_pai.h parser/parse_ppi.c parser/parse_ppi.h parser/parse_privacy.c parser/parse_privacy.h parser/parse_refer_to.c parser/parse_refer_to.h parser/parse_rpid.c parser/parse_rpid.h Copyright: 2001-2008, Juha Heinanen License: GPL-2+ Files: parser/case_path.h parser/parse_supported.c parser/parse_supported.h Copyright: 2006, Andreas Granig License: GPL-2+ Files: parser/case_retr.h Copyright: 2005-2009, Voice System SRL License: GPL-2+ Files: parser/parse_authenticate.c parser/parse_authenticate.h parser/parse_call_info.c parser/parse_call_info.h Copyright: 2010, 2011, VoIP Embedded Inc. License: GPL-2+ Files: parser/parse_min_expires.c parser/parse_min_expires.h Copyright: 2011, VoIP Embedded, Inc. License: GPL-2+ Files: parser/parse_multipart.c parser/parse_multipart.h Copyright: 2004-2009, Voice Sistem SRL License: GPL-2+ Files: parser/parse_replaces.c parser/parse_replaces.h Copyright: 2010-2014, VoIP Embedded, Inc License: GPL-2+ Files: parser/parse_sipifmatch.h Copyright: 2001-2004, FhG FOKUS License: GPL-2+ Files: parser/parse_to.c parser/parse_to.h Copyright: 2001-2005, Fhg Fokus License: GPL-2+ Files: parser/sdp/* Copyright: 2006-2008, SOMA Networks, INC License: GPL-2+ Files: prime_hash.c prime_hash.h Copyright: 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: pvar.c timer.c timer.h Copyright: 2010-2014, OpenSIPS Solutions 2005-2009, Voice Sistem SRL 2001-2003, FhG Fokus License: GPL-2+ Files: regexp.c regexp.h serialize.c serialize.h Copyright: 2001-2008, Juha Heinanen License: GPL-2+ Files: script_var.c script_var.h strcommon.c strcommon.h transformations.c transformations.h Copyright: 2006, 2007, voice-system.ro License: GPL-2+ Files: sha1.c sha1.h Copyright: 2006-2014, ARM Limited License: GPL-2+ Files: sliblist.c sliblist.h Copyright: 2013, VoIP Embedded, Inc License: GPL-3+ Files: statistics.c Copyright: 2008-2014, OpenSIPS Solutions 2006, Voice Sistem SRL License: GPL-2+ Files: test/* Copyright: 2007, 2008, 1&1 Internet AG License: GPL-2+ Files: test/27.sh Copyright: 2008, 2010, Voice System License: GPL-2+ Files: utils/* Copyright: 2001-2004, FhG FOKUS License: GPL-2+ Files: utils/db_berkeley/* Copyright: 2007, Cisco Systems License: GPL-2+ Files: utils/fifo_relay/* Copyright: 2001-2008, Juha Heinanen License: GPL-2+ Files: scripts/dbtextdb/* Copyright: 2008 Google Inc License: GPL-2+ Files: packaging/debian/* Copyright: 2002 Andrei Pelinescu-Onciul 2006 Julien Blache 2009 Bjoern Boschman 2009 Bogdan Iancu 2011 Julián Moreno Patiño 2011 Daniel Echeverry 2011 Alejandro Rios P. 2015 Razvan Crainea 2015 Steve Frécinaux License: GPL-2+ Files: packaging/gentoo/opensips-*.ebuild Copyright: 1999-2005 Gentoo Foundation License: GPL-2 License: Apache Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. . 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . 3. All advertising materials mentioning features or use of this software must display the following acknowledgment: "This product includes software developed by the Apache Group for use in the Apache HTTP server project (http://www.apache.org/)." . 4. The names "Apache Server" and "Apache Group" must not be used to endorse or promote products derived from this software without prior written permission. . 5. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by the Apache Group for use in the Apache HTTP server project (http://www.apache.org/)." . THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. . This software consists of voluntary contributions made by many individuals on behalf of the Apache Group and was originally based on public domain software written at the National Center for Supercomputing Applications, University of Illinois, Urbana-Champaign. For more information on the Apache Group and the Apache HTTP server project, please see . . This code is based on, and used with the permission of, the SIO stdio-replacement strx_* functions by Panos Tsirigotis for xinetd. License: BSD-3-clause Copyright (c) The Regents of the University of California. All rights reserved. . Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: BSD-2-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . THIS SOFTWARE IS PROVIDED BY AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: Expat The MIT License . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License: GPL-2+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. License: GPL-2+ with OpenSSL exception This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . In addition, as a special exception, the author of this program gives permission to link the code of its release with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "OpenSSL". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. License: GPL-3+ 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-3'. License: ISC Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. . THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. License: GPL-2 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. . This package 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 . . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". opensips-2.2.2/packaging/debian/common/opensips.README.Debian000066400000000000000000000014611300170765700237230ustar00rootroot00000000000000OpenSIPS for Debian ------------------ * OpenSIPS setup --------------- To setup OpenSIPS, you need to: - configure OpenSIPS properly to suit your needs - edit /etc/default/opensips, adjust the MEMORY parameter and set RUN_OPENSIPS to "yes" If you are building an HA cluster using heartbeat or similar, you'll want to disable the init script by running: update-rc.d opensips remove so that OpenSIPS will not be launched at system startup. You still need to set RUN_OPENSIPS to "yes" if you want to use the /etc/init.d/opensips init script. Set the DUMP_CORE parameter in /etc/default/opensips to "yes" if you want to get a core dump in case OpenSIPS crashes. The debug symbols for OpenSIPS are provided by the opensips-dbg package. -- Julien BLACHE , Fri, 08 Sep 2006 14:43:21 +0200 opensips-2.2.2/packaging/debian/common/opensips.default000066400000000000000000000014621300170765700232320ustar00rootroot00000000000000# # OpenSIPS startup options # # Set to yes to enable opensips, once configured properly. RUN_OPENSIPS=no # User to run as USER=opensips # Group to run as GROUP=opensips # Amount of shared memory to allocate for the running OpenSIPS server (in Mb) S_MEMORY=64 # Amount of pkg memory to allocate for the running OpenSIPS server (in Mb) P_MEMORY=4 # Enable the server to leave a core file when it crashes. # Set this to 'yes' to enable OpenSIPS to leave a core file when it crashes # or 'no' to disable this feature. This option is case sensitive and only # accepts 'yes' and 'no' and only in lowercase letters. # On some systems (e.g. Ubuntu 6.10, Debian 4.0) it is necessary to specify # a directory for the core files to get a dump. Look into the opensips # init file for an example configuration. DUMP_CORE=no opensips-2.2.2/packaging/debian/common/opensips.examples000066400000000000000000000000131300170765700234130ustar00rootroot00000000000000examples/* opensips-2.2.2/packaging/debian/common/opensips.init000066400000000000000000000106111300170765700225450ustar00rootroot00000000000000#! /bin/sh # ### BEGIN INIT INFO # Provides: opensips # Required-Start: $syslog $network $local_fs $time $remote_fs # Required-Stop: $syslog $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the OpenSIPS SIP server # Description: Start the OpenSIPS SIP server ### END INIT INFO # # TODO: # The following fields should be added (and completed): # Should-Start: postgresql mysql radius # Should-Stop: postgresql mysql radius set -e PATH=/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/sbin/opensips NAME=opensips DESC=opensips CFGFILE=/etc/opensips/opensips.cfg M4CFGFILE=/etc/opensips/opensips.m4 M4ARCHIVEDIR=/etc/opensips/archive HOMEDIR=/var/run/opensips PIDFILE=$HOMEDIR/$NAME.pid DEFAULTS=/etc/default/opensips RUN_OPENSIPS=no [ -e "/lib/lsb/init-functions" ] && . /lib/lsb/init-functions test -f $DAEMON || exit 0 # Load startup options if available if [ -f $DEFAULTS ]; then . $DEFAULTS || true fi if [ "$RUN_OPENSIPS" != "yes" ]; then echo "OpenSIPS not yet configured. Edit /etc/default/opensips first." exit 0 fi check_opensips_config () { # Check if opensips configuration is valid before starting the server set +e out=$($DAEMON -c -f $CFGFILE 2>&1 > /dev/null) retcode=$? set -e if [ "$retcode" != '0' ]; then echo "Not starting $DESC: invalid configuration file!" echo -e "\n$out\n" exit 1 fi } create_radius_seqfile () { # Create a radius sequence file to be used by the radius client if # radius accounting is enabled. This is needed to avoid any issue # with the file not being writable if opensips first starts as user # root because DUMP_CORE is enabled and creates this file as user # root and then later it switches back to user opensips and cannot # write to the file. If the file exists before opensips starts, it # won't change it's ownership and will be writable for both root # and opensips, no matter what options are chosen at install time RADIUS_SEQ_FILE=/var/run/opensips/opensips_radius.seq if [ -d /var/run/opensips ]; then chown ${USER}:${GROUP} /var/run/opensips if [ ! -f $RADIUS_SEQ_FILE ]; then touch $RADIUS_SEQ_FILE fi chown ${USER}:${GROUP} $RADIUS_SEQ_FILE chmod 660 $RADIUS_SEQ_FILE fi } S_MEMORY=$((`echo $S_MEMORY | sed -e 's/[^0-9]//g'`)) P_MEMORY=$((`echo $P_MEMORY | sed -e 's/[^0-9]//g'`)) [ -z "$USER" ] && USER=opensips [ -z "$GROUP" ] && GROUP=opensips [ $S_MEMORY -le 0 ] && S_MEMORY=32 [ $P_MEMORY -le 0 ] && P_MEMORY=32 if test "$DUMP_CORE" = "yes" ; then # set proper ulimit ulimit -c unlimited # directory for the core dump files # COREDIR=/home/corefiles # [ -d $COREDIR ] || mkdir $COREDIR # chmod 777 $COREDIR # echo "$COREDIR/core.%e.sig%s.%p" > /proc/sys/kernel/core_pattern fi OPTIONS="-P $PIDFILE -m $S_MEMORY -M $P_MEMORY -u $USER -g $GROUP -f $CFGFILE" case "$1" in start) check_opensips_config create_radius_seqfile # dirs under /var/run will go away on reboot. mkdir -p "$HOMEDIR" chmod 775 "$HOMEDIR" chown "$USER:$GROUP" "$HOMEDIR" >/dev/null 2>&1 || true # Generate config from M4 if [ -f $M4CFGFILE ]; then m4 -Q $M4CFGFILE >$CFGFILE.tmp if [ $? != 0 ]; then echo "Cannot process m4 macro" rm "$CFGFILE.tmp" exit 1 fi [ -e $CFGFILE ] || touch $CFGFILE # compare configs if [ `md5sum $CFGFILE|awk '{print $1}'` != `md5sum $CFGFILE.tmp|awk '{print $1}'` ]; then mkdir -p "$M4ARCHIVEDIR" mv "$CFGFILE" "$M4ARCHIVEDIR/$NAME.cfg-`date +%Y%m%d_%H%M%S`" fi mv "$CFGFILE.tmp" "$CFGFILE" chown $USER:$GROUP $CFGFILE chmod 640 $CFGFILE fi log_daemon_msg "Starting $DESC" "$NAME" start-stop-daemon --start --quiet --pidfile $PIDFILE \ --exec $DAEMON -- $OPTIONS || echo -n " already running" log_end_msg $? ;; stop) log_daemon_msg "Stopping $DESC" "$NAME" start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \ --exec $DAEMON log_end_msg $? ;; restart|force-reload) check_opensips_config create_radius_seqfile log_daemon_msg "Restarting $DESC" "$NAME" start-stop-daemon --oknodo --stop --quiet --pidfile \ $PIDFILE --exec $DAEMON sleep 1 start-stop-daemon --start --quiet --pidfile \ $PIDFILE --exec $DAEMON -- $OPTIONS log_end_msg $? ;; status) status_of_proc -p $PIDFILE "$DAEMON" "$NAME" ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload|status}" >&2 exit 1 ;; esac exit 0 opensips-2.2.2/packaging/debian/common/opensips.lintian-overrides000066400000000000000000000001551300170765700252420ustar00rootroot00000000000000opensips: python-script-but-no-python-dep usr/lib/x86_64-linux-gnu/opensips/opensipsctl/dbtextdb/dbtextdb.py opensips-2.2.2/packaging/debian/common/opensips.manpages000066400000000000000000000001251300170765700233740ustar00rootroot00000000000000bdb_recover.8 opensips.8 opensips.cfg.5 opensipsdbctl.8 osipsconsole.8 osipsconfig.8 opensips-2.2.2/packaging/debian/common/opensips.postinst000066400000000000000000000025371300170765700234750ustar00rootroot00000000000000#! /bin/sh # # $Id$ PKG=opensips DEFAULTS=/etc/default/opensips HOMEDIR=/var/run/opensips set -e # summary of how this script can be called: # * `configure' # * `abort-upgrade' # * `abort-remove' `in-favour' # # * `abort-deconfigure' `in-favour' # `removing' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package # # quoting from the policy: # Any necessary prompting should almost always be confined to the # post-installation script, and should be protected with a conditional # so that unnecessary prompting doesn't happen if a package's # installation fails and the `postinst' is called with `abort-upgrade', # `abort-remove' or `abort-deconfigure'. case "$1" in configure) adduser --quiet --system --group --disabled-password \ --shell /bin/false --gecos "OpenSIPS" \ --home $HOMEDIR opensips || true ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# opensips-2.2.2/packaging/debian/common/opensips.service000066400000000000000000000011071300170765700232420ustar00rootroot00000000000000[Unit] Description=OpenSIPS SIP Server After=network.target mysqld.service postgresql.service rtpproxy.service [Service] Type=forking User=opensips Group=opensips RuntimeDirectory=opensips RuntimeDirectoryMode=775 Environment=P_MEMORY=32 S_MEMORY=32 EnvironmentFile=-/etc/default/opensips PIDFile=%t/opensips/opensips.pid ExecStart=/usr/sbin/opensips -P %t/opensips/opensips.pid -f /etc/opensips/opensips.cfg -m $S_MEMORY -M $P_MEMORY $OPTIONS ExecStartPre=/usr/sbin/opensips -c -f /etc/opensips/opensips.cfg Restart=always TimeoutStopSec=30s [Install] WantedBy=multi-user.target opensips-2.2.2/packaging/debian/common/opensips.tmpfile000066400000000000000000000000511300170765700232370ustar00rootroot00000000000000d /run/opensips 0775 opensips opensips - opensips-2.2.2/packaging/debian/common/rules000077500000000000000000000264031300170765700211060ustar00rootroot00000000000000#!/usr/bin/make -f # 2010/08/24: Allow building of individual modules (kennard) # 2015/08/14: Change URL to github sources # To control which packages get built, set BUILD_MODPKG_LIST to list # of packages to build (see ALL_MODPKG_LIST for candiate names). Set to # 'NONE' to not build any module packages (the primary package is always # built). By default all delcared packages (debian/control) will be built. # Supported DEB_BUILD_OPTIONS: # noopt,nostrip,debug -- standard # upstreamcflags -- don't set cflags, let upstream package Makefile do it DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) DEBVERSION:=$(shell head -n 1 debian/changelog \ | sed -e 's/^[^(]*(\([^)]*\)).*/\1/') UPVERSION:=$(shell echo $(DEBVERSION) | sed -e 's/^.*://' -e 's/-[0-9.]*$$//' -e 's/.dfsg$$//') FILENAME := opensips_$(UPVERSION).orig.tar.gz URL := https://github.com/OpenSIPS/opensips/archive/$(UPVERSION).tar.gz # Uncomment this to turn on verbose mode. # export DH_VERBOSE=1 # List of all debian module packages we will build # Used to index the make variable below, which has details about each package # # LUA has been removed due to libmemcache dependency. # See https://github.com/OpenSIPS/opensips/issues/580 ALL_MODPKG_LIST := \ MYSQL POSTGRES UNIXODBC JABBER CPL RADIUS \ PRESENCE XMLRPC PERL SNMPSTATS XMPP CROUTE BERKELEY LDAP \ GEOIP REGEX IDENTITY B2BUA DBHTTP DIALPLAN MEMCACHED JSON \ REDIS RABBITMQ HTTP COMPRESSION EMERGENCY \ PROTO_SCTP PROTO_TLS PROTO_WSS TLS_MGM REST_CLIENT LUA ifeq ($(BUILD_MODPKG_LIST),NONE) override BUILD_MODPKG_LIST := else ifeq ($(BUILD_MODPKG_LIST),) override BUILD_MODPKG_LIST := $(ALL_MODPKG_LIST) endif endif # FOO_MODULES is the directory name within modules/ that belongs to the package # FOO_MOD_PATH is same as FOO_MODULES, but with 'modules/' prepended MYSQL_PKGNAME = opensips-mysql-module MYSQL_MODULES = db_mysql MYSQL_MOD_PATH=$(addprefix modules/, $(MYSQL_MODULES)) POSTGRES_PKGNAME = opensips-postgres-module POSTGRES_MODULES = db_postgres POSTGRES_MOD_PATH=$(addprefix modules/, $(POSTGRES_MODULES)) UNIXODBC_PKGNAME = opensips-unixodbc-module UNIXODBC_MODULES=db_unixodbc UNIXODBC_MOD_PATH=$(addprefix modules/, $(UNIXODBC_MODULES)) JABBER_PKGNAME = opensips-jabber-module JABBER_MODULES = jabber JABBER_MOD_PATH=$(addprefix modules/, $(JABBER_MODULES)) CPL_PKGNAME = opensips-cpl-module CPL_MODULES = cpl_c CPL_MOD_PATH=$(addprefix modules/, $(CPL_MODULES)) RADIUS_PKGNAME = opensips-radius-modules RADIUS_MODULES = aaa_radius peering RADIUS_MOD_PATH=$(addprefix modules/, $(RADIUS_MODULES)) PRESENCE_PKGNAME = opensips-presence-modules PRESENCE_MODULES = presence presence_callinfo presence_dialoginfo presence_xml presence_mwi presence_xcapdiff pua pua_bla pua_dialoginfo pua_mi pua_usrloc pua_xmpp rls xcap xcap_client PRESENCE_MOD_PATH=$(addprefix modules/, $(PRESENCE_MODULES)) XMLRPC_PKGNAME = opensips-xmlrpc-module XMLRPC_MODULES = mi_xmlrpc_ng XMLRPC_MOD_PATH=$(addprefix modules/, $(XMLRPC_MODULES)) PERL_PKGNAME = opensips-perl-modules PERL_MODULES = perl db_perlvdb PERL_MOD_PATH=$(addprefix modules/, $(PERL_MODULES)) SNMPSTATS_PKGNAME = opensips-snmpstats-module SNMPSTATS_MODULES = snmpstats SNMPSTATS_MOD_PATH=$(addprefix modules/, $(SNMPSTATS_MODULES)) XMPP_PKGNAME = opensips-xmpp-module XMPP_MODULES = xmpp XMPP_MOD_PATH=$(addprefix modules/, $(XMPP_MODULES)) CROUTE_PKGNAME = opensips-carrierroute-module CROUTE_MODULES = carrierroute CROUTE_MOD_PATH=$(addprefix modules/, $(CROUTE_MODULES)) BERKELEY_PKGNAME = opensips-berkeley-module BERKELEY_MODULES = db_berkeley BERKELEY_MOD_PATH=$(addprefix modules/, $(BERKELEY_MODULES)) LDAP_PKGNAME = opensips-ldap-modules LDAP_MODULES = ldap h350 LDAP_MOD_PATH=$(addprefix modules/, $(LDAP_MODULES)) GEOIP_PKGNAME = opensips-geoip-module GEOIP_MODULES = mmgeoip GEOIP_MOD_PATH=$(addprefix modules/, $(GEOIP_MODULES)) REGEX_PKGNAME = opensips-regex-module REGEX_MODULES = regex REGEX_MOD_PATH=$(addprefix modules/, $(REGEX_MODULES)) IDENTITY_PKGNAME = opensips-identity-module IDENTITY_MODULES = identity IDENTITY_MOD_PATH=$(addprefix modules/, $(IDENTITY_MODULES)) B2BUA_PKGNAME = opensips-b2bua-module B2BUA_MODULES = b2b_entities b2b_logic B2BUA_MOD_PATH=$(addprefix modules/, $(B2BUA_MODULES)) DBHTTP_PKGNAME = opensips-dbhttp-module DBHTTP_MODULES = db_http DBHTTP_MOD_PATH=$(addprefix modules/, $(DBHTTP_MODULES)) DIALPLAN_PKGNAME = opensips-dialplan-module DIALPLAN_MODULES = dialplan DIALPLAN_MOD_PATH=$(addprefix modules/, $(DIALPLAN_MODULES)) MEMCACHED_PKGNAME = opensips-memcached-module MEMCACHED_MODULES = cachedb_memcached MEMCACHED_MOD_PATH=$(addprefix modules/, $(MEMCACHED_MODULES)) REDIS_PKGNAME = opensips-redis-module REDIS_MODULES = cachedb_redis REDIS_MOD_PATH=$(addprefix modules/, $(REDIS_MODULES)) CASSANDRA_PKGNAME = opensips-cassandra-module CASSANDRA_MODULES = cachedb_cassandra CASSANDRA_MOD_PATH=$(addprefix modules/, $(CASSANDRA_MODULES)) RABBITMQ_PKGNAME = opensips-rabbitmq-module RABBITMQ_MODULES = event_rabbitmq RABBITMQ_MOD_PATH=$(addprefix modules/, $(RABBITMQ_MODULES)) LUA_PKGNAME = opensips-lua-module LUA_MODULES = lua LUA_MOD_PATH=$(addprefix modules/, $(LUA_MODULES)) HTTP_PKGNAME = opensips-http-modules HTTP_MODULES = httpd mi_http pi_http mi_json HTTP_MOD_PATH=$(addprefix modules/, $(HTTP_MODULES)) JSON_PKGNAME = opensips-json-module JSON_MODULES = json JSON_MOD_PATH=$(addprefix modules/, $(JSON_MODULES)) COMPRESSION_PKGNAME = opensips-compression-module COMPRESSION_MODULES = compression COMPRESSION_MOD_PATH=$(addprefix modules/, $(COMPRESSION_MODULES)) EMERGENCY_PKGNAME = opensips-emergency-module EMERGENCY_MODULES = emergency EMERGENCY_MOD_PATH=$(addprefix modules/, $(EMERGENCY_MODULES)) PROTO_SCTP_PKGNAME = opensips-sctp-module PROTO_SCTP_MODULES = proto_sctp PROTO_SCTP_MOD_PATH=$(addprefix modules/, $(PROTO_SCTP_MODULES)) PROTO_TLS_PKGNAME = opensips-tls-module PROTO_TLS_MODULES = proto_tls PROTO_TLS_MOD_PATH=$(addprefix modules/, $(PROTO_TLS_MODULES)) PROTO_WSS_PKGNAME = opensips-wss-module PROTO_WSS_MODULES = proto_wss PROTO_WSS_MOD_PATH=$(addprefix modules/, $(PROTO_WSS_MODULES)) TLS_MGM_PKGNAME = opensips-tlsmgm-module TLS_MGM_MODULES = tls_mgm TLS_MGM_MOD_PATH=$(addprefix modules/, $(TLS_MGM_MODULES)) REST_CLIENT_PKGNAME = opensips-restclient-module REST_CLIENT_MODULES = rest_client REST_CLIENT_MOD_PATH=$(addprefix modules/, $(REST_CLIENT_MODULES)) # Need this per-package variables available in environment so accessible # to shell. Since have underscore in them, need to explicitly export export $(foreach pkg,$(ALL_MODPKG_LIST),$(pkg)_PKGNAME $(pkg)_MODULES $(pkg)_MOD_PATH) # not needed anymore #ALL_PACKAGES = opensips $(foreach pkg,$(ALL_MODPKG_LIST),$($(pkg)_PKGNAME)) \ # opensips-console ALL_MODULES = $(foreach pkg,$(ALL_MODPKG_LIST),$($(pkg)_MODULES)) BUILD_MODULE_PATHS = $(foreach pkg,$(BUILD_MODPKG_LIST),$($(pkg)_MOD_PATH)) ## modules not in the "main" package or unstable modules that we never want to build ## Everything we don't specifically exclude here will get built into the primary package NONCORE_MODULES = $(ALL_MODULES) osp # https://wiki.debian.org/ReproducibleBuilds/ CC_EXTRA_OPTS += -DVERSION_NODATE ifeq (cc, $(CC)) CC = gcc endif ifneq (,$(findstring upstreamcflags,$(DEB_BUILD_OPTIONS))) CFLAGS= else CFLAGS = -Wall -g ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 else CFLAGS += -O2 endif endif # inherit variables from dpkg-builder BUILD_CPPFLAGS += $(shell dpkg-buildflags --get CPPFLAGS) BUILD_CFLAGS = $(shell dpkg-buildflags --get CFLAGS) CXXFLAGS += $(shell dpkg-buildflags --get CXXFLAGS) LDFLAGS += $(shell dpkg-buildflags --get LDFLAGS) # https://wiki.debian.org/ReproducibleBuilds/ CFLAGS += -DVERSION_NODATE # force multiach lib path and SMP support all the time VARS = LIBDIR=lib/$(DEB_HOST_MULTIARCH) ISSMP=yes # Debian Jessie doesn't package radius-ng, so use freeradius instead VARS += FREERADIUS=yes ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) INSTALL_PROGRAM += -s endif # support parallel compiling NJOBS = FASTER = ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) NJOBS = -j $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) FASTER = 1 endif override_dh_auto_build: # Add here commands to compile the package. CC="$(CC)" CFLAGS="$(CFLAGS)" $(VARS) FASTER=$(FASTER) $(MAKE) $(NJOBS) app \ modules skip_modules="$(NONCORE_MODULES)" cfg_target=/etc/opensips/ CC="$(CC)" CFLAGS="$(CFLAGS)" $(VARS) FASTER=$(FASTER) $(MAKE) $(NJOBS) \ modules modules="$(BUILD_MODULE_PATHS)" cfg_target=/etc/opensips/ # generate the utils db_berkeley CC="$(CC)" CFLAGS="$(CFLAGS)" $(VARS) FASTER=$(FASTER) $(MAKE) $(NJOBS) utils include_modules="db_berkeley" touch build-stamp override_dh_auto_clean: dh_auto_clean -- $(VARS) include_modules="$(ALL_MODULES)" proper rm -f cfg.tab.h rm -f Makefile.conf rm -f utils/opensipsunix/opensipsunix.o utils/opensipsunix/opensipsunix rm -f utils/db_berkeley/bdb_recover.o utils/db_berkeley/bdb_recover show-env: env show-mod-info: set -e;\ for mod in $(ALL_MODPKG_LIST) ; do\ eval mod_pkgname=\$$$${mod}_PKGNAME;\ eval mod_modules=\$$$${mod}_MODULES;\ eval mod_paths=\$$$${mod}_MOD_PATH;\ echo "MODULE $${mod} AS $${mod_pkgname} FROM $${mod_modules} PATHS $${mod_paths}";\ done @echo "ALL_MOD_PATHS: $(ALL_MODULE_PATHS)" override_dh_auto_install: # Add here commands to install the package into debian/opensips # opensips base package CC="$(CC)" CFLAGS="$(CFLAGS)" $(VARS) FASTER=$(FASTER) $(MAKE) $(NJOBS) install \ install-modules-all skip_modules="$(NONCORE_MODULES)" \ basedir=$(CURDIR)/debian/opensips \ prefix=/usr \ cfg_prefix=$(CURDIR)/debian/opensips \ cfg_target=/etc/opensips/ \ doc_dir=share/doc/opensips \ skip-install-doc=yes # skip INSTALL.gz on debian systems find $(CURDIR)/debian/opensips/etc/opensips -type f -exec chmod -x {} \; sed -i -e "s/^PATH.*//" $(CURDIR)/debian/opensips/usr/sbin/opensipsctl set -e;\ for pkg in $(BUILD_MODPKG_LIST) ; do\ eval pkg_pkgname=\$$$${pkg}_PKGNAME;\ eval pkg_modules=\$$$${pkg}_MODULES;\ eval pkg_paths=\$$$${pkg}_MOD_PATH;\ echo "rules: Installing $${pkg} as $${pkg_pkgname} from $${pkg_modules} ...";\ CC="$(CC)" CFLAGS="$(CFLAGS)" $(VARS) FASTER=$(FASTER) $(MAKE) $(NJOBS) install-modules-all \ modules="$${pkg_paths}" \ basedir=$(CURDIR)/debian/$${pkg_pkgname} \ prefix=/usr \ cfg_prefix=$(CURDIR)/debian/$${pkg_pkgname} \ cfg_target=/etc/opensips/ \ doc_dir=share/doc/$${pkg_pkgname} \ ;\ done # move console binary from opensips to opensips-console mkdir -p $(CURDIR)/debian/opensips-console/usr/sbin mv $(CURDIR)/debian/opensips/usr/sbin/osipsconsole \ $(CURDIR)/debian/opensips-console/usr/sbin/ # move binaries of opensips-berkeley-module to opensips-berkeley-bin mkdir -p $(CURDIR)/debian/opensips-berkeley-bin/usr/ mv $(CURDIR)/debian/opensips-berkeley-module/usr/sbin \ $(CURDIR)/debian/opensips-berkeley-bin/usr/ override_dh_strip: dh_strip --dbg-package=opensips-dbg override_dh_auto_test: #Disable tests, too old and they are obsolete print-version: @@echo "Debian version: $(DEBVERSION)" @@echo "Upstream version: $(UPVERSION)" @@echo "Upstream URL: $(URL)" get-orig-source: @@dh_testdir @@echo Downloading $(FILENAME) from $(URL) ... @@wget -N -nv -T10 -t3 -O ../$(FILENAME) $(URL) # generic rule %: dh $@ --with python2 opensips-2.2.2/packaging/debian/common/source/000077500000000000000000000000001300170765700213215ustar00rootroot00000000000000opensips-2.2.2/packaging/debian/common/source/format000066400000000000000000000000141300170765700225270ustar00rootroot000000000000003.0 (quilt) opensips-2.2.2/packaging/debian/common/watch000066400000000000000000000003521300170765700210520ustar00rootroot00000000000000version=3 opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/opensips-$1\.tar\.gz/ \ https://github.com/OpenSIPS/opensips/tags .*/v?(\d\S*)\.tar\.gz https://github.com/OpenSIPS/opensips/releases /OpenSIPS/opensips/archive/(.+)\.tar\.gz opensips-2.2.2/packaging/freebsd/000077500000000000000000000000001300170765700167215ustar00rootroot00000000000000opensips-2.2.2/packaging/freebsd/Makefile000066400000000000000000000141361300170765700203660ustar00rootroot00000000000000# New ports collection makefile for: opensips # Date created: 20 October 2005 # Whom: jesusr # # $FreeBSD: ports/net/opensips/Makefile,v 1.9 2007/08/20 09:12:42 miwi Exp $ # PORTNAME= opensips PORTVERSION= 2.2.2 CATEGORIES= net MASTER_SITES= http://opensips.org/pub/opensips/${PORTVERSION}/src/ DISTNAME= ${PORTNAME}-${PORTVERSION}-tls_src MAINTAINER= jesusr@FreeBSD.org COMMENT= A very fast and configurable SIP server with TLS support LIB_DEPENDS= radiusclient-ng.2:${PORTSDIR}/net/radiusclient WRKSRC= ${WRKDIR}/${PORTNAME}-${PORTVERSION}-tls USE_GMAKE= yes MAKE_ENV= CC="${CC}" \ CC_EXTRA_OPTS="${CFLAGS}" \ PTHREAD_CFLAGS="${PTHREAD_CFLAGS}" \ PTHREAD_LIBS="${PTHREAD_LIBS}" PLIST_FILES= sbin/opensips sbin/opensipsctl sbin/opensipsdbctl sbin/opensipsunix \ lib/opensips/opensipsctl/opensipsctl.base lib/opensips/opensipsctl/opensipsctl.ctlbase \ lib/opensips/opensipsctl/opensipsctl.dbtext lib/opensips/opensipsctl/opensipsctl.fifo \ lib/opensips/opensipsctl/opensipsctl.sqlbase lib/opensips/opensipsctl/opensipsctl.unixsock \ lib/opensips/opensipsctl/opensipsdbctl.base lib/opensips/opensipsctl/opensipsdbctl.dbtext \ etc/opensips/opensipsctlrc PLIST_DIRS= lib/opensips/modules lib/opensips/opensipsctl lib/opensips MODULES= acc alias_db auth auth_db auth_diameter avpops benchmark \ cfgutils dbtext dialog dispatcher diversion domain \ domainpolicy enum exec flatstore gflags group imc \ mangler maxfwd mediaproxy mi_datagram mi_fifo msilo nathelper \ options path permissions pike registrar rr seas \ siptrace sl sms speeddial sst statistics textops tm uac \ uac_redirect uri uri_db usrloc xlog RC_FILES= acc/radiusclient.conf \ acc/servers \ dictionary.opensips \ opensips.cfg RC_DIRS= acc ONLY_FOR_ARCHS= i386 MAN5= opensips.cfg.5 MAN8= opensips.8 \ opensipsctl.8 \ opensipsunix.8 PORTDOCS= * SUB_FILES= pkg-install pkg-deinstall SUB_LIST= RC_DIRS="${RC_DIRS}" RC_FILES="${RC_FILES}" OPTIONS= MYSQL "MySQL support" on \ POSTGRESQL "PostgreSQL support" off \ UNIXODBC "UnixODBC support" off \ CPL "CPL_C support" off \ TLS "TLS support" off \ SNMPSTATS "SNMPStats support" off .include .if defined(WITH_MYSQL) USE_MYSQL= yes MODULES+= mysql PLIST_FILES+= lib/opensips/opensipsctl/opensipsdbctl.mysql lib/opensips/opensipsctl/opensipsctl.mysql .else MAKE_ENV+= MYSQL=mysql .endif .if defined(WITH_POSTGRESQL) USE_PGSQL= yes MODULES+= postgres PLIST_FILES+= lib/opensips/opensipsctl/opensipsdbctl.pgsql lib/opensips/opensipsctl/opensipsctl.pgsql .else MAKE_ENV+= POSTGRESQL=postgres .endif .if defined(WITH_UNIXODBC) LIB_DEPENDS+= odbc.1:${PORTSDIR}/databases/unixODBC MODULES+= unixodbc .else MAKE_ENV+= UNIXODBC=unixodbc .endif .if defined(WITH_CPL) USE_GNOME= libxml2 MODULES+= cpl_c PLIST_FILES+= etc/opensips/cpl-06.dtd .else MAKE_ENV+= CPL=cpl_c .endif .if defined(WITH_TLS) MODULES+= tlsops .include "${PORTSDIR}/Mk/bsd.openssl.mk" TLS_RC_FILES= tls/rootCA/cacert.pem \ tls/rootCA/certs/01.pem \ tls/rootCA/index.txt \ tls/rootCA/private/cakey.pem \ tls/rootCA/serial \ tls/user/user-calist.pem \ tls/user/user-cert.pem \ tls/user/user-cert_req.pem \ tls/user/user-privkey.pem \ tls/README \ tls/ca.conf \ tls/request.conf \ tls/user.conf RC_FILES+= ${TLS_RC_FILES} TLS_RC_DIRS= tls/user \ tls/rootCA/certs \ tls/rootCA/private \ tls/rootCA \ tls RC_DIRS+= ${TLS_RC_DIRS} .else MAKE_ENV+= TLSOPS=tlsops MAKE_ARGS+= TLS="" .endif .if defined(WITH_SNMPSTATS) RUN_DEPENDS+= snmpcheck:${PORTSDIR}/net-mgmt/net-snmp MODULES+= snmpstats .else MAKE_ENV+= SNMPSTATS=snmpstats .endif PLIST_FILES+= ${MODULES:S|^|lib/opensips/modules/|:S|$|.so|} \ ${RC_FILES:S|^|etc/opensips/|:S|$|.default|} post-patch: .if defined(NOPORTDOCS) @${REINPLACE_CMD} -e 's|install-doc install-man|install-man|' \ ${WRKSRC}/Makefile .endif @${REINPLACE_CMD} -e 's|-g -O9 ||' -e 's|-O9 ||' ${WRKSRC}/Makefile.defs @${REINPLACE_CMD} -e 's|/usr/local/etc/opensips|${PREFIX}/etc/opensips|' \ ${WRKSRC}/scripts/opensipsdbctl.dbtext @${REINPLACE_CMD} -e 's|/usr/local/share/opensips|${PREFIX}/share/opensips|' \ ${WRKSRC}/scripts/opensipsdbctl.dbtext @${REINPLACE_CMD} -e 's|/etc/opensips|${PREFIX}/etc/opensips|' \ ${WRKSRC}/modules/mediaproxy/README \ ${WRKSRC}/modules/cpl_c/README \ ${WRKSRC}/INSTALL @${REINPLACE_CMD} -e 's|/usr/local/sbin/opensips|${PREFIX}/sbin/opensips|' \ ${WRKSRC}/INSTALL @${REINPLACE_CMD} -e 's|/usr/local|${PREFIX}|' \ ${WRKSRC}/etc/opensips.cfg \ ${WRKSRC}/scripts/opensipsctl \ ${WRKSRC}/scripts/opensipsctl.8 \ ${WRKSRC}/scripts/opensipsctl.base \ ${WRKSRC}/scripts/opensipsctl.db_berkeley \ ${WRKSRC}/scripts/opensipsctl.dbtext \ ${WRKSRC}/scripts/opensipsctlrc \ ${WRKSRC}/scripts/opensipsdbctl \ ${WRKSRC}/scripts/opensipsdbctl.base \ ${WRKSRC}/scripts/opensipsdbctl.db_berkeley \ ${WRKSRC}/scripts/opensipsdbctl.dbtext \ ${WRKSRC}/scripts/opensipsdbctl.mysql \ ${WRKSRC}/scripts/opensipsdbctl.pgsql \ ${WRKSRC}/modules/acc/README \ ${WRKSRC}/modules/avp_radius/README \ ${WRKSRC}/modules/db_berkeley/README \ ${WRKSRC}/modules/ldap/README \ ${WRKSRC}/modules/osp/README \ ${WRKSRC}/modules/perl/README \ ${WRKSRC}/modules/seas/README \ ${WRKSRC}/modules/snmpstats/README \ ${WRKSRC}/modules/speeddial/README \ ${WRKSRC}/modules/unixodbc/README @${REINPLACE_CMD} -e 's|/usr/local|${LOCALBASE}|' \ ${WRKSRC}/modules/acc/etc/radiusclient.conf @${REINPLACE_CMD} -e 's|/usr/local/etc/radiusclient|${PREFIX}/etc/opensips/acc|' \ ${WRKSRC}/modules/acc/acc_mod.c ${WRKSRC}/modules/acc/README post-install: ${INSTALL_DATA} ${WRKSRC}/etc/opensips.cfg \ ${PREFIX}/etc/opensips/opensips.cfg.default ${MKDIR} ${PREFIX}/etc/opensips/acc ${INSTALL_DATA} ${WRKSRC}/modules/acc/etc/radiusclient.conf \ ${PREFIX}/etc/opensips/acc/radiusclient.conf.default ${INSTALL_DATA} ${WRKSRC}/modules/acc/etc/servers \ ${PREFIX}/etc/opensips/acc/servers.default .for d in ${TLS_RC_DIRS} ${MKDIR} ${PREFIX}/etc/opensips/${d} .endfor .for f in ${TLS_RC_FILES} ${INSTALL_DATA} ${WRKSRC}/etc/${f} ${PREFIX}/etc/opensips/${f}.default .endfor @PKG_PREFIX=${PREFIX} ${SH} ${PKGINSTALL} ${PKGNAME} POST-INSTALL .include opensips-2.2.2/packaging/freebsd/distinfo000066400000000000000000000003401300170765700204600ustar00rootroot00000000000000MD5 (opensips-1.4.0-tls_src.tar.gz) = e380fa73095274162fac129e16d7c7d8 SHA256 (opensips-1.4.0-tls_src.tar.gz) = afd43b70e5887c5f91769bbc22506e8a6e0da90a91bf699da17f73988fc61fcf SIZE (opensips-1.4.0-tls_src.tar.gz) = 3405325 opensips-2.2.2/packaging/freebsd/files/000077500000000000000000000000001300170765700200235ustar00rootroot00000000000000opensips-2.2.2/packaging/freebsd/files/patch-Makefile000066400000000000000000000071371300170765700225700ustar00rootroot00000000000000--- Makefile.orig Thu Dec 13 18:49:12 2007 +++ Makefile Thu Dec 13 21:49:54 2007 @@ -48,11 +48,11 @@ skip_modules?= # if not set on the cmd. line or the env, exclude this modules: -exclude_modules?= jabber cpl_c mysql postgres osp unixodbc \ - avp_radius auth_radius group_radius uri_radius xmpp \ +exclude_modules?= $(MYSQL) $(POSTGRESQL) $(CPL) $(SNMPSTATS) $(TLSOPS) $(UNIXODBC) \ + jabber osp avp_radius auth_radius group_radius uri_radius xmpp \ presence presence_xml presence_mwi pua pua_bla pua_mi \ pua_usrloc pua_xmpp rls mi_xmlrpc perl snmpstats db_perlvdb \ - ldap carrierroute h350 xcap_client db_berkeley seas + ldap carrierroute h350 xcap_client db_berkeley ifeq ($(TLS),) exclude_modules+= tlsops endif @@ -392,24 +392,11 @@ # note: on solaris 8 sed: ? or \(...\)* (a.s.o) do not work install-cfg: $(cfg-prefix)/$(cfg-dir) - sed -e "s#/usr/.*lib/$(NAME)/modules/#$(modules-target)#g" \ - < etc/$(NAME).cfg > $(cfg-prefix)/$(cfg-dir)$(NAME).cfg.sample0 - sed -e "s#/usr/.*etc/$(NAME)/tls/#$(cfg-target)tls/#g" \ - < $(cfg-prefix)/$(cfg-dir)$(NAME).cfg.sample0 \ - > $(cfg-prefix)/$(cfg-dir)$(NAME).cfg.sample - rm -fr $(cfg-prefix)/$(cfg-dir)$(NAME).cfg.sample0 - chmod 644 $(cfg-prefix)/$(cfg-dir)$(NAME).cfg.sample - if [ -z "${skip_cfg_install}" -a \ - ! -f $(cfg-prefix)/$(cfg-dir)$(NAME).cfg ]; then \ - mv -f $(cfg-prefix)/$(cfg-dir)$(NAME).cfg.sample \ - $(cfg-prefix)/$(cfg-dir)$(NAME).cfg; \ - fi # radius dictionary - $(INSTALL_TOUCH) $(cfg-prefix)/$(cfg-dir)/dictionary.opensips.sample - $(INSTALL_CFG) etc/dictionary.opensips \ - $(cfg-prefix)/$(cfg-dir)/dictionary.opensips.sample + $(INSTALL_TOUCH) $(cfg-prefix)/$(cfg-dir)/dictionary.opensips.default + $(INSTALL_CFG) etc/dictionary.opensips $(cfg-prefix)/$(cfg-dir)/dictionary.opensips.default if [ ! -f $(cfg-prefix)/$(cfg-dir)/dictionary.opensips ]; then \ - mv -f $(cfg-prefix)/$(cfg-dir)/dictionary.opensips.sample \ + cp -f $(cfg-prefix)/$(cfg-dir)/dictionary.opensips.default \ $(cfg-prefix)/$(cfg-dir)/dictionary.opensips; \ fi # opensipsctl config @@ -445,7 +432,7 @@ sed -e "s#/usr/local/lib/opensips#$(lib-target)#g" | \ sed -e "s#/usr/local/etc/opensips#$(cfg-target)#g" >/tmp/opensipsctl $(INSTALL_TOUCH) $(bin-prefix)/$(bin-dir)/opensipsctl - $(INSTALL_BIN) /tmp/opensipsctl $(bin-prefix)/$(bin-dir) + $(BSD_INSTALL_SCRIPT) /tmp/opensipsctl $(bin-prefix)/$(bin-dir) rm -fr /tmp/opensipsctl sed -e "s#/usr/local/sbin#$(bin-target)#g" \ < scripts/opensipsctl.base > /tmp/opensipsctl.base @@ -492,10 +479,10 @@ sed -e "s#/usr/local/lib/opensips#$(lib-target)#g" | \ sed -e "s#/usr/local/etc/opensips#$(cfg-target)#g" >/tmp/opensipsdbctl $(INSTALL_TOUCH) $(bin-prefix)/$(bin-dir)/opensipsdbctl - $(INSTALL_BIN) /tmp/opensipsdbctl $(bin-prefix)/$(bin-dir) + $(BSD_INSTALL_SCRIPT) /tmp/opensipsdbctl $(bin-prefix)/$(bin-dir) rm -fr /tmp/opensipsdbctl $(INSTALL_TOUCH) $(bin-prefix)/$(bin-dir)/$(NAME)unix - $(INSTALL_BIN) utils/$(NAME)unix/$(NAME)unix $(bin-prefix)/$(bin-dir) + $(BSD_INSTALL_SCRIPT) utils/$(NAME)unix/$(NAME)unix $(bin-prefix)/$(bin-dir) # install dbtext stuff mkdir -p $(modules-prefix)/$(lib-dir)/opensipsctl ; \ sed -e "s#/usr/local/share/opensips#$(data-target)#g" \ @@ -624,9 +611,7 @@ if [ -f modules/"$$r"/README ]; then \ $(INSTALL_TOUCH) $(doc-prefix)/$(doc-dir)/README ; \ $(INSTALL_DOC) modules/"$$r"/README \ - $(doc-prefix)/$(doc-dir)/README ; \ - mv -f $(doc-prefix)/$(doc-dir)/README \ - $(doc-prefix)/$(doc-dir)/README."$$r" ; \ + $(doc-prefix)/$(doc-dir)/README."$$r" ; \ fi ; \ fi ; \ done opensips-2.2.2/packaging/freebsd/files/patch-Makefile-mysql000066400000000000000000000004611300170765700237240ustar00rootroot00000000000000--- modules/mysql/Makefile.orig Thu Dec 13 20:15:16 2007 +++ modules/mysql/Makefile Thu Dec 13 20:15:20 2007 @@ -8,7 +8,7 @@ # set CROSS_COMPILE to true if you want to skip # the autodetection -# CROSS_COMPILE=true +CROSS_COMPILE=true ifeq ($(CROSS_COMPILE),) MYSQLCFG=$(shell which mysql_config) opensips-2.2.2/packaging/freebsd/files/patch-Makefile.defs000066400000000000000000000023701300170765700235020ustar00rootroot00000000000000--- Makefile.defs.orig Thu Dec 13 18:56:53 2007 +++ Makefile.defs Thu Dec 13 20:31:40 2007 @@ -135,7 +135,7 @@ ifeq ($(ARCH_B),64b) LIBDIR ?= lib64 else - LIBDIR ?= lib + LIBDIR = lib # assume 32b - it is not really used further ARCH_B=32b endif @@ -228,13 +228,13 @@ TAR ?= tar endif -INSTALL_TOUCH = touch # used to create the file first (good to +INSTALL_TOUCH = : # used to create the file first (good to # make solaris install work) -INSTALL_CFG = $(INSTALL) -m 644 -INSTALL_BIN = $(INSTALL) -m 755 -INSTALL_MODULES = $(INSTALL) -m 755 -INSTALL_DOC = $(INSTALL) -m 644 -INSTALL_MAN = $(INSTALL) -m 644 +INSTALL_CFG = $(BSD_INSTALL_DATA) -m 644 +INSTALL_BIN = $(BSD_INSTALL_PROGRAM) -m 755 +INSTALL_MODULES = $(BSD_INSTALL_PROGRAM) -m 755 +INSTALL_DOC = $(BSD_INSTALL_DATA) -m 644 +INSTALL_MAN = $(BSD_INSTALL_MAN) -m 644 #set some vars from the environment (and not make builtins) CC := $(shell echo "$${CC}") @@ -1254,8 +1254,8 @@ #add libssl if needed ifneq ($(TLS),) -DEFS+= -I$(LOCALBASE)/ssl/include -I$(LOCALBASE)/include -LIBS+= -L$(LOCALBASE)/lib -L$(LOCALBASE)/ssl/lib -lssl -lcrypto +DEFS+= -I$(OPENSSLINC) +LIBS+= -L$(OPENSSLLIB) -lssl -lcrypto endif #add libsctp if needed opensips-2.2.2/packaging/freebsd/files/pkg-deinstall.in000066400000000000000000000013041300170765700231070ustar00rootroot00000000000000#!/bin/sh rc_dir=$PKG_PREFIX/etc/opensips if [ "$2" = DEINSTALL ]; then for f in %%RC_FILES%%; do if /usr/bin/cmp -s $rc_dir/$f.default $rc_dir/$f; then /bin/rm -f $rc_dir/$f fi done elif [ "$2" = POST-DEINSTALL ]; then rc_dirs=`for d in %%RC_DIRS%%; do echo $d; done | /usr/bin/sort -r` for d in $rc_dirs ""; do /bin/rmdir $rc_dir/$d 2>/dev/null || /usr/bin/true done if [ -e $rc_dir ]; then echo "===============================================================================" echo "If you are permanently removing this port, you should manually remove the" echo "$rc_dir directory." echo "===============================================================================" fi fi opensips-2.2.2/packaging/freebsd/files/pkg-install.in000066400000000000000000000003351300170765700226010ustar00rootroot00000000000000#!/bin/sh [ "$2" != POST-INSTALL ] && exit rc_dir=$PKG_PREFIX/etc/opensips for f in %%RC_FILES%%; do if ! [ -e $rc_dir/$f ]; then /usr/bin/install -o root -g wheel -m 644 \ $rc_dir/$f.default $rc_dir/$f fi done opensips-2.2.2/packaging/freebsd/pkg-descr000066400000000000000000000013421300170765700205230ustar00rootroot00000000000000OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. A C Shell like scripting language provides full control over the server's behaviour. It's modular architecture allows only required functionality to be loaded. Currently the following modules are available: digest authentication, CPL scripts, instant messaging, MySQL and UNIXODBC support, a presence agent, radius authentication, record routing, an SMS gateway, a jabber gateway, a transaction and dialog module, OSP module, statistics support, registrar and user location, SIMPLE Presence, Perl programming interface, SNMP and Java SIP Servlet. WWW: http://www.opensips.org/ opensips-2.2.2/packaging/gentoo/000077500000000000000000000000001300170765700166025ustar00rootroot00000000000000opensips-2.2.2/packaging/gentoo/opensips-2.1.3.ebuild000066400000000000000000000055311300170765700222730ustar00rootroot00000000000000# Copyright 1999-2005 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header$ inherit eutils DESCRIPTION="OpenSIPS - flexible and robust SIP (RFC3261) server" HOMEPAGE="http://www.opensips.org/" MY_P="${P}_src" SRC_URI="http://opensips.org/pub/opensips/${PV}/src/${MY_P}.tar.gz" LICENSE="GPL-2" SLOT="0" KEYWORDS="~x86" IUSE="debug ipv6 mysql postgres radius jabber ssl cpl unixodbc" RDEPEND=" mysql? ( >=dev-db/mysql-4.1.20 ) radius? ( >=net-dialup/radiusclient-ng-0.5.0 ) postgres? ( >=dev-db/postgresql-8.0.8 ) jabber? ( dev-libs/expat ) ssl? ( dev-libs/openssl ) cpl? ( dev-libs/libxml2 ) b2bua? ( dev-libs/libxml2 ) presence? ( dev-libs/libxml2 ) unixodbc? ( dev-libs/unixodbc-2.2.6 )" inc_mod="" make_options="" pkg_setup() { use mysql && \ inc_mod="${inc_mod} db_mysql" use postgres && \ inc_mod="${inc_mod} db_postgres" use radius && \ inc_mod="${inc_mod} aaa_radius peering" use jabber && \ inc_mod="${inc_mod} jabber" use cpl && \ inc_mod="${inc_mod} cpl_c" use b2bua && \ inc_mod="${inc_mod} b2b_entities b2bua_logic" use presence && \ inc_mod="${inc_mod} presence presence_dialoginfo presence_mwi presence_xcapdiff presence_xml pua pua_bla pua_dialoginfo pua_mi pua_usrloc pua_xmpp rls xcap xcap_client" use unixodbc && \ inc_mod="${inc_mod} db_unixodbc" export inc_mod } src_unpack() { unpack ${MY_P}.tar.gz cd ${S} use ipv6 } src_compile() { local compile_options pkg_setup # optimization can result in strange debuging symbols so omit it in case if use debug; then compile_options="${compile_options} mode=debug" else compile_options="${compile_options} CFLAGS=${CFLAGS}" fi if use ssl; then compile_options="TLS=1 ${compile_options}" fi emake all "${compile_options}" \ prefix=${ROOT}/ \ include_modules="${inc_mod}" \ cfg_prefix=${ROOT}/ \ cfg_target=${ROOT}/etc/opensips/ || die } src_install () { local install_options emake install \ prefix=${D}/ \ include_modules="${inc_mod}" \ bin_prefix=${D}/usr/sbin \ bin_dir="" \ cfg_prefix=${D}/etc \ cfg_dir=opensips/ \ cfg_target=${D}/etc/opensips \ modules_prefix=${D}/usr/lib/opensips \ modules_dir=modules \ modules_target=${D}/usr/lib/opensips/modules/ \ man_prefix=${D}/usr/share/man \ man_dir="" \ doc_prefix=${D}/usr/share/doc \ doc_dir=${PF} || die exeinto /etc/init.d newexe ${FILESDIR}/opensips.init opensips # fix what the Makefile don't do use mysql || \ rm ${D}/usr/sbin/opensips_mysql.sh } pkg_postinst() { einfo "WARNING: If you upgraded from a previous OpenSIPS version" einfo "please read the README, NEWS and INSTALL files in the" einfo "documentation directory because the database and the" einfo "configuration file of old OpenSIPS versions are incompatible" einfo "with the current version." } pkg_prerm () { ${D}/etc/init.d/opensips stop >/dev/null } opensips-2.2.2/packaging/gentoo/opensips-2.2.2.ebuild000066400000000000000000000055311300170765700222730ustar00rootroot00000000000000# Copyright 1999-2005 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header$ inherit eutils DESCRIPTION="OpenSIPS - flexible and robust SIP (RFC3261) server" HOMEPAGE="http://www.opensips.org/" MY_P="${P}_src" SRC_URI="http://opensips.org/pub/opensips/${PV}/src/${MY_P}.tar.gz" LICENSE="GPL-2" SLOT="0" KEYWORDS="~x86" IUSE="debug ipv6 mysql postgres radius jabber ssl cpl unixodbc" RDEPEND=" mysql? ( >=dev-db/mysql-4.1.20 ) radius? ( >=net-dialup/radiusclient-ng-0.5.0 ) postgres? ( >=dev-db/postgresql-8.0.8 ) jabber? ( dev-libs/expat ) ssl? ( dev-libs/openssl ) cpl? ( dev-libs/libxml2 ) b2bua? ( dev-libs/libxml2 ) presence? ( dev-libs/libxml2 ) unixodbc? ( dev-libs/unixodbc-2.2.6 )" inc_mod="" make_options="" pkg_setup() { use mysql && \ inc_mod="${inc_mod} db_mysql" use postgres && \ inc_mod="${inc_mod} db_postgres" use radius && \ inc_mod="${inc_mod} aaa_radius peering" use jabber && \ inc_mod="${inc_mod} jabber" use cpl && \ inc_mod="${inc_mod} cpl_c" use b2bua && \ inc_mod="${inc_mod} b2b_entities b2bua_logic" use presence && \ inc_mod="${inc_mod} presence presence_dialoginfo presence_mwi presence_xcapdiff presence_xml pua pua_bla pua_dialoginfo pua_mi pua_usrloc pua_xmpp rls xcap xcap_client" use unixodbc && \ inc_mod="${inc_mod} db_unixodbc" export inc_mod } src_unpack() { unpack ${MY_P}.tar.gz cd ${S} use ipv6 } src_compile() { local compile_options pkg_setup # optimization can result in strange debuging symbols so omit it in case if use debug; then compile_options="${compile_options} mode=debug" else compile_options="${compile_options} CFLAGS=${CFLAGS}" fi if use ssl; then compile_options="TLS=1 ${compile_options}" fi emake all "${compile_options}" \ prefix=${ROOT}/ \ include_modules="${inc_mod}" \ cfg_prefix=${ROOT}/ \ cfg_target=${ROOT}/etc/opensips/ || die } src_install () { local install_options emake install \ prefix=${D}/ \ include_modules="${inc_mod}" \ bin_prefix=${D}/usr/sbin \ bin_dir="" \ cfg_prefix=${D}/etc \ cfg_dir=opensips/ \ cfg_target=${D}/etc/opensips \ modules_prefix=${D}/usr/lib/opensips \ modules_dir=modules \ modules_target=${D}/usr/lib/opensips/modules/ \ man_prefix=${D}/usr/share/man \ man_dir="" \ doc_prefix=${D}/usr/share/doc \ doc_dir=${PF} || die exeinto /etc/init.d newexe ${FILESDIR}/opensips.init opensips # fix what the Makefile don't do use mysql || \ rm ${D}/usr/sbin/opensips_mysql.sh } pkg_postinst() { einfo "WARNING: If you upgraded from a previous OpenSIPS version" einfo "please read the README, NEWS and INSTALL files in the" einfo "documentation directory because the database and the" einfo "configuration file of old OpenSIPS versions are incompatible" einfo "with the current version." } pkg_prerm () { ${D}/etc/init.d/opensips stop >/dev/null } opensips-2.2.2/packaging/gentoo/opensips.init000066400000000000000000000007371300170765700213360ustar00rootroot00000000000000#!/sbin/runscript # Copyright 2002 Frauenhofer Gesellschaft FOKUS, Germany. # Distributed under the terms of the GNU General Public License, v2 or later # $Header$ depend() { need net } start() { ebegin "Starting OpenSIPS" start-stop-daemon --start --quiet --pidfile /var/run/opensips.pid \ --exec /usr/sbin/opensips -- -P /var/run/opensips.pid eend $? } stop() { ebegin "Stopping OpenSIPS" start-stop-daemon --stop --quiet --pidfile /var/run/opensips.pid eend $? } opensips-2.2.2/packaging/netbsd/000077500000000000000000000000001300170765700165665ustar00rootroot00000000000000opensips-2.2.2/packaging/netbsd/COMMENT000066400000000000000000000000601300170765700176070ustar00rootroot00000000000000OpenSIPS, very fast and configurable SIP server opensips-2.2.2/packaging/netbsd/DESCR000066400000000000000000000013251300170765700173520ustar00rootroot00000000000000OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. C Shell like scripting language provides full control over the server's behaviour. It's modular architecture allows only required functionality to be loaded. Currently the following modules are available: Digest Authentication, CPL scripts, Instant Messaging, MySQL support, UNIXODBC support, OSP support, Radius Authentication, Record Routing, SMS Gateway, Jabber Gateway, Transaction Module, Dialog Module, Registrar and User Location, Perl programming interface, SIMPLE Presence and SNMP. WWW: http://www.opensips.org/ - Bogdan-Andrei Iancu bogdan@opensips.org opensips-2.2.2/packaging/netbsd/Makefile000066400000000000000000000015431300170765700202310ustar00rootroot00000000000000# New ports collection makefile for: opensips # # $NetBSD$ # # $Id$ # COMMENT= "OpenSIPS" PORTNAME= opensips PORTVERSION= 2.2.2 CATEGORIES= net MASTER_SITES= http://opensips.org/pub/opensips/2.2.2/src/ MAINTAINER= bogdan@opensips.org MAN8=opensips.8 MAN5=opensips.cfg.5 # MANCOMPRESSED must not be specified (if defined is assumed to be yes) USE_GMAKE= yes USE_TOOLS+= gmake # we override the DISTFILE NAME DISTNAME= ${PORTNAME}-${PORTVERSION} DISTFILES= ${DISTNAME}_src.tar.gz WRKSRC= ${WRKDIR}/${PORTNAME}-${PORTVERSION} # build by default mysql and jabber MAKE_ENV= include_modules="mysql jabber" # depends on expat and mysqlclient because of the above modules DEPENDS= expat-[0-9]*:../../textproc/expat \ mysql-client>=3.23.35:../../databases/mysql-client #.include "/usr/pkgsrc/mk/bsd.prefs.mk" pre-fetch: .include "../../mk/bsd.pkg.mk" opensips-2.2.2/packaging/netbsd/PLIST000066400000000000000000000107601300170765700174100ustar00rootroot00000000000000etc/opensips/opensips.cfg etc/opensips/opensipsctlrc sbin/opensips sbin/opensipsctl sbin/opensipsdbctl sbin/osipsconsole lib/opensips/modules/acc.so lib/opensips/modules/alias_db.so lib/opensips/modules/auth.so lib/opensips/modules/auth_aaa.so lib/opensips/modules/auth_db.so lib/opensips/modules/avpops.so lib/opensips/modules/benchmark.so lib/opensips/modules/call_control.so lib/opensips/modules/cfgutils.so lib/opensips/modules/closeddial.so lib/opensips/modules/db_flatstore.so lib/opensips/modules/db_mysql.so lib/opensips/modules/db_text.so lib/opensips/modules/db_virtual.so lib/opensips/modules/dialog.so lib/opensips/modules/dialplan.so lib/opensips/modules/dispatcher.so lib/opensips/modules/diversion.so lib/opensips/modules/domain.so lib/opensips/modules/domainpolicy.so lib/opensips/modules/drouting.so lib/opensips/modules/enum.so lib/opensips/modules/exec.so lib/opensips/modules/gflags.so lib/opensips/modules/group.so lib/opensips/modules/imc.so lib/opensips/modules/jabber.so lib/opensips/modules/load_balancer.so lib/opensips/modules/localcache.so lib/opensips/modules/mangler.so lib/opensips/modules/maxfwd.so lib/opensips/modules/mediaproxy.so lib/opensips/modules/mi_datagram.so lib/opensips/modules/mi_fifo.so lib/opensips/modules/msilo.so lib/opensips/modules/nathelper.so lib/opensips/modules/nat_traversal.so lib/opensips/modules/options.so lib/opensips/modules/path.so lib/opensips/modules/permissions.so lib/opensips/modules/pike.so lib/opensips/modules/ratelimit.so lib/opensips/modules/registrar.so lib/opensips/modules/rr.so lib/opensips/modules/seas.so lib/opensips/modules/signaling.so lib/opensips/modules/siptrace.so lib/opensips/modules/sl.so lib/opensips/modules/sms.so lib/opensips/modules/speeddial.so lib/opensips/modules/sst.so lib/opensips/modules/statistics.so lib/opensips/modules/stun.so lib/opensips/modules/textops.so lib/opensips/modules/tm.so lib/opensips/modules/uac.so lib/opensips/modules/uac_redirect.so lib/opensips/modules/uri.so lib/opensips/modules/usrloc.so lib/opensips/modules/xlog.so lib/opensips/modules/xmpp.so lib/opensipsctl/opensipsctl.base lib/opensipsctl/opensipsctl.sqlbase lib/opensipsctl/opensipsctl.ctlbase lib/opensipsctl/opensipsctl.fifo lib/opensipsctl/opensipsctl.unixsock lib/opensipsctl/opensipsctl.mysql share/doc/opensips/README share/doc/opensips/INSTALL share/doc/opensips/README-MODULES share/doc/opensips/AUTHORS share/doc/opensips/NEWS share/doc/opensips/README.acc share/doc/opensips/README.alias_db share/doc/opensips/README.auth share/doc/opensips/README.auth_aaa share/doc/opensips/README.auth_db share/doc/opensips/README.avpops share/doc/opensips/README.benchmark share/doc/opensips/README.call_control share/doc/opensips/README.cfgutils share/doc/opensips/README.closeddial share/doc/opensips/README.db_flatstore share/doc/opensips/README.db_mysql share/doc/opensips/README.db_text share/doc/opensips/README.db_virtual share/doc/opensips/README.dialog share/doc/opensips/README.dialplan share/doc/opensips/README.dispatcher share/doc/opensips/README.diversion share/doc/opensips/README.domain share/doc/opensips/README.domainpolicy share/doc/opensips/README.drouting share/doc/opensips/README.enum share/doc/opensips/README.exec share/doc/opensips/README.gflags share/doc/opensips/README.group share/doc/opensips/README.imc share/doc/opensips/README.jabber share/doc/opensips/README.load_balancer share/doc/opensips/README.localcache share/doc/opensips/README.mangler share/doc/opensips/README.maxfwd share/doc/opensips/README.mediaproxy share/doc/opensips/README.mi_datagram share/doc/opensips/README.mi_fifo share/doc/opensips/README.msilo share/doc/opensips/README.nathelper share/doc/opensips/README.nat_traversal share/doc/opensips/README.options share/doc/opensips/README.path share/doc/opensips/README.permissions share/doc/opensips/README.pike share/doc/opensips/README.ratelimit share/doc/opensips/README.registrar share/doc/opensips/README.rr share/doc/opensips/README.seas share/doc/opensips/README.signaling share/doc/opensips/README.siptrace share/doc/opensips/README.sl share/doc/opensips/README.sms share/doc/opensips/README.speeddial share/doc/opensips/README.sst share/doc/opensips/README.statistics share/doc/opensips/README.stun share/doc/opensips/README.textops share/doc/opensips/README.tm share/doc/opensips/README.uac share/doc/opensips/README.uac_redirect share/doc/opensips/README.uri share/doc/opensips/README.usrloc share/doc/opensips/README.xlog share/doc/opensips/README.xmpp @dirrm share/doc/opensips @dirrm etc/opensips @dirrm lib/opensips/modules @dirrm lib/opensips opensips-2.2.2/packaging/netbsd/distinfo000066400000000000000000000002201300170765700203220ustar00rootroot00000000000000MD5 (opensips-1.6.0-notls_src.tar.gz) = 9ca396cb5d95623206b109bdfb4c6374 MD5 (opensips-1.6.0-tls_src.tar.gz) = 9ca396cb5d95623206b109bdfb4c6374 opensips-2.2.2/packaging/openbsd/000077500000000000000000000000001300170765700167415ustar00rootroot00000000000000opensips-2.2.2/packaging/openbsd/Makefile000066400000000000000000000017721300170765700204100ustar00rootroot00000000000000# New ports collection makefile for: opensips # # $OpenBSD$ # # $Id$ # COMMENT= "OpenSIPS" PORTNAME= opensips PORTVERSION= 2.2.2 CATEGORIES= net MASTER_SITES= http://opensips.org/pub/opensips/2.2.2/src/ MAINTAINER= bogdan@opensips.org MAN8=opensips.8 MAN5=opensips.cfg.5 # MANCOMPRESSED must not be defined in openbsd (defined==yes) # GPL PERMIT_PACKAGE_CDROM= Yes PERMIT_PACKAGE_FTP= Yes PERMIT_DISTFILES_CDROM= Yes PERMIT_DISTFILES_FTP= Yes USE_GMAKE= yes # we override the DISTFILE NAME DISTNAME= ${PORTNAME}-${PORTVERSION} DISTFILES= ${DISTNAME}_src.tar.gz WRKSRC= ${WRKDIR}/${PORTNAME}-${PORTVERSION} # build by default mysql and jabber MAKE_ENV= include_modules="mysql jabber" # depends on mysql and libexpat because of the above modules BUILD_DEPENDS= :expat-1.*:textproc/expat \ :mysql-client-3.23.*:databases/mysql LIB_DEPENDS= expat.2:expat-1.*:textproc/expat \ lib/mysql/mysqlclient.10:mysql-client-3.23.*:databases/mysql pre-fetch: .include opensips-2.2.2/packaging/openbsd/distinfo000066400000000000000000000002201300170765700204750ustar00rootroot00000000000000MD5 (opensips-1.6.0-notls_src.tar.gz) = 9ca396cb5d95623206b109bdfb4c6374 MD5 (opensips-1.6.0-tls_src.tar.gz) = 9ca396cb5d95623206b109bdfb4c6374 opensips-2.2.2/packaging/openbsd/pkg/000077500000000000000000000000001300170765700175225ustar00rootroot00000000000000opensips-2.2.2/packaging/openbsd/pkg/COMMENT000066400000000000000000000000601300170765700205430ustar00rootroot00000000000000OpenSIPS, very fast and configurable SIP server opensips-2.2.2/packaging/openbsd/pkg/DESCR000066400000000000000000000013221300170765700203030ustar00rootroot00000000000000OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. C Shell like scripting language provides full control over the server's behaviour. It's modular architecture allows only required functionality to be loaded. Currently the following modules are available: Digest Authentication, CPL scripts, Instant Messaging, MySQL support, UNIXODBC support, OSP support, Radius Authentication, Record Routing, SMS Gateway, Jabber Gateway, Transaction Module, Dialog Module, Registrar and User Location, SIMPLE Presence, Perl programming interface, SNMP. WWW: http://www.opensips.org/ - Bogdan-Andrei Iancu bogdan@opensips.org opensips-2.2.2/packaging/openbsd/pkg/PLIST000066400000000000000000000107601300170765700203440ustar00rootroot00000000000000etc/opensips/opensips.cfg etc/opensips/opensipsctlrc sbin/opensips sbin/opensipsctl sbin/opensipsdbctl sbin/osipsconsole lib/opensips/modules/acc.so lib/opensips/modules/alias_db.so lib/opensips/modules/auth.so lib/opensips/modules/auth_aaa.so lib/opensips/modules/auth_db.so lib/opensips/modules/avpops.so lib/opensips/modules/benchmark.so lib/opensips/modules/call_control.so lib/opensips/modules/cfgutils.so lib/opensips/modules/closeddial.so lib/opensips/modules/db_flatstore.so lib/opensips/modules/db_mysql.so lib/opensips/modules/db_text.so lib/opensips/modules/db_virtual.so lib/opensips/modules/dialog.so lib/opensips/modules/dialplan.so lib/opensips/modules/dispatcher.so lib/opensips/modules/diversion.so lib/opensips/modules/domain.so lib/opensips/modules/domainpolicy.so lib/opensips/modules/drouting.so lib/opensips/modules/enum.so lib/opensips/modules/exec.so lib/opensips/modules/gflags.so lib/opensips/modules/group.so lib/opensips/modules/imc.so lib/opensips/modules/jabber.so lib/opensips/modules/load_balancer.so lib/opensips/modules/localcache.so lib/opensips/modules/mangler.so lib/opensips/modules/maxfwd.so lib/opensips/modules/mediaproxy.so lib/opensips/modules/mi_datagram.so lib/opensips/modules/mi_fifo.so lib/opensips/modules/msilo.so lib/opensips/modules/nathelper.so lib/opensips/modules/nat_traversal.so lib/opensips/modules/options.so lib/opensips/modules/path.so lib/opensips/modules/permissions.so lib/opensips/modules/pike.so lib/opensips/modules/ratelimit.so lib/opensips/modules/registrar.so lib/opensips/modules/rr.so lib/opensips/modules/seas.so lib/opensips/modules/signaling.so lib/opensips/modules/siptrace.so lib/opensips/modules/sl.so lib/opensips/modules/sms.so lib/opensips/modules/speeddial.so lib/opensips/modules/sst.so lib/opensips/modules/statistics.so lib/opensips/modules/stun.so lib/opensips/modules/textops.so lib/opensips/modules/tm.so lib/opensips/modules/uac.so lib/opensips/modules/uac_redirect.so lib/opensips/modules/uri.so lib/opensips/modules/usrloc.so lib/opensips/modules/xlog.so lib/opensips/modules/xmpp.so lib/opensipsctl/opensipsctl.base lib/opensipsctl/opensipsctl.sqlbase lib/opensipsctl/opensipsctl.ctlbase lib/opensipsctl/opensipsctl.fifo lib/opensipsctl/opensipsctl.unixsock lib/opensipsctl/opensipsctl.mysql share/doc/opensips/README share/doc/opensips/INSTALL share/doc/opensips/README-MODULES share/doc/opensips/AUTHORS share/doc/opensips/NEWS share/doc/opensips/README.acc share/doc/opensips/README.alias_db share/doc/opensips/README.auth share/doc/opensips/README.auth_aaa share/doc/opensips/README.auth_db share/doc/opensips/README.avpops share/doc/opensips/README.benchmark share/doc/opensips/README.call_control share/doc/opensips/README.cfgutils share/doc/opensips/README.closeddial share/doc/opensips/README.db_flatstore share/doc/opensips/README.db_mysql share/doc/opensips/README.db_text share/doc/opensips/README.db_virtual share/doc/opensips/README.dialog share/doc/opensips/README.dialplan share/doc/opensips/README.dispatcher share/doc/opensips/README.diversion share/doc/opensips/README.domain share/doc/opensips/README.domainpolicy share/doc/opensips/README.drouting share/doc/opensips/README.enum share/doc/opensips/README.exec share/doc/opensips/README.gflags share/doc/opensips/README.group share/doc/opensips/README.imc share/doc/opensips/README.jabber share/doc/opensips/README.load_balancer share/doc/opensips/README.localcache share/doc/opensips/README.mangler share/doc/opensips/README.maxfwd share/doc/opensips/README.mediaproxy share/doc/opensips/README.mi_datagram share/doc/opensips/README.mi_fifo share/doc/opensips/README.msilo share/doc/opensips/README.nathelper share/doc/opensips/README.nat_traversal share/doc/opensips/README.options share/doc/opensips/README.path share/doc/opensips/README.permissions share/doc/opensips/README.pike share/doc/opensips/README.ratelimit share/doc/opensips/README.registrar share/doc/opensips/README.rr share/doc/opensips/README.seas share/doc/opensips/README.signaling share/doc/opensips/README.siptrace share/doc/opensips/README.sl share/doc/opensips/README.sms share/doc/opensips/README.speeddial share/doc/opensips/README.sst share/doc/opensips/README.statistics share/doc/opensips/README.stun share/doc/opensips/README.textops share/doc/opensips/README.tm share/doc/opensips/README.uac share/doc/opensips/README.uac_redirect share/doc/opensips/README.uri share/doc/opensips/README.usrloc share/doc/opensips/README.xlog share/doc/opensips/README.xmpp @dirrm share/doc/opensips @dirrm etc/opensips @dirrm lib/opensips/modules @dirrm lib/opensips opensips-2.2.2/packaging/redhat_fedora/000077500000000000000000000000001300170765700200765ustar00rootroot00000000000000opensips-2.2.2/packaging/redhat_fedora/opensips.init000066400000000000000000000047621300170765700226340ustar00rootroot00000000000000#!/bin/bash # # Startup script for OpenSIPS # # chkconfig: - 85 15 # description: OpenSIPS is a fast SIP Server. # # processname: opensips # pidfile: /var/run/opensips.pid # config: /etc/opensips/opensips.cfg # ### BEGIN INIT INFO # Provides: opensips # Required-Start: $local_fs $network $named # Should-Start: mysqld postgresql # Short-Description: start, stop OpenSIPS # Description: OpenSIPS is a very fast and flexible SIP (RFC3261) server. ### END INIT INFO # Source function library. . /etc/rc.d/init.d/functions prog=opensips opensips=/usr/sbin/$prog cfgdir="/etc/$prog" pidfile="/var/run/$prog.pid" lockfile="/var/lock/subsys/$prog" configfile="$cfgdir/$prog.cfg" m4configfile="$cfgdir/$prog.m4" m4archivedir="$cfgdir/archive" OPTIONS="" RETVAL=0 [ -f /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog start() { echo -n $"Starting $prog: " # check whether OpenSIPs was already started if status -p $pidfile $prog > /dev/null 2>&1 ; then echo -n "already running" && warning && echo return 0 fi # Generate config from M4 if [ -f $m4configfile ]; then cd "$cfgdir" m4 -Q $m4configfile >$configfile.tmp if [ $? != 0 ]; then echo -n "cannot process m4 macro" && failure && echo rm "$configfile.tmp" return 1 fi [ -e $configfile ] || touch $configfile # compare configs if [ `md5sum $configfile|awk '{print $1}'` != `md5sum $configfile.tmp|awk '{print $1}'` ]; then mkdir -p "$m4archivedir" mv "$configfile" "$m4archivedir/$prog.cfg-`date +%Y%m%d_%H%M%S`" fi mv "$configfile.tmp" "$configfile" chown $prog:$prog $configfile chmod 640 $configfile fi # there is something at end of this output which is needed to # report proper [ OK ] status in Fedora scripts daemon $opensips -u $prog -g $prog -P $pidfile -f $configfile $OPTIONS 2>/dev/null | tail -1 RETVAL=$? echo [ $RETVAL = 0 ] && touch $lockfile return $RETVAL } stop() { echo -n $"Stopping $prog: " # check whether OpenSIPs is running if ! status -p $pidfile $prog > /dev/null 2>&1 ; then echo -n "not running" && warning && echo return 0 fi killproc $prog 2> /dev/null RETVAL=$? echo [ $RETVAL = 0 ] && rm -f $lockfile $pidfile return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status -p $pidfile $prog RETVAL=$? ;; restart|reload) stop start ;; condrestart|try-restart) if [ -f $pidfile ] ; then stop start fi ;; *) echo $"Usage: $prog {start|stop|reload|restart|condrestart|status|help}" RETVAL=2 esac exit $RETVAL opensips-2.2.2/packaging/redhat_fedora/opensips.m4cfg000066400000000000000000000013241300170765700226600ustar00rootroot00000000000000#!/bin/bash prog=opensips cfgdir="/etc/$prog" configfile="$cfgdir/$prog.cfg" m4configfile="$cfgdir/$prog.m4" m4archivedir="$cfgdir/archive" # Generate config from M4 if [ -f $m4configfile ]; then cd "$cfgdir" m4 -Q $m4configfile >$configfile.tmp if [ $? != 0 ]; then echo "cannot process m4 macro" rm "$configfile.tmp" exit 1 fi [ -e $configfile ] || touch $configfile # compare configs if [ `md5sum ${configfile}|awk '{print $1}'` != `md5sum ${configfile}.tmp|awk '{print $1}'` ]; then mkdir -p "${m4archivedir}" mv "${configfile}" "${m4archivedir}/${prog}.cfg-`date +%Y%m%d_%H%M%S`" fi mv "${configfile}.tmp" "${configfile}" chown ${prog}:${prog} ${configfile} chmod 640 ${configfile} fi exit 0 opensips-2.2.2/packaging/redhat_fedora/opensips.service000066400000000000000000000010011300170765700233100ustar00rootroot00000000000000[Unit] Description=OpenSIPS is a very fast and flexible SIP (RFC3261) server After=network.target mysqld.service postgresql.service rtpproxy.service [Service] Type=forking User=opensips Group=opensips EnvironmentFile=-/etc/sysconfig/opensips PIDFile=/var/run/opensips/opensips.pid ExecStart=/usr/sbin/opensips -P /var/run/opensips/opensips.pid -f /etc/opensips/opensips.cfg $OPTIONS ExecStartPre=/usr/sbin/opensips-m4cfg Restart=always TimeoutStopSec=30s LimitNOFILE=262144 [Install] WantedBy=multi-user.target opensips-2.2.2/packaging/redhat_fedora/opensips.spec000066400000000000000000001676531300170765700226340ustar00rootroot00000000000000%if 0%{?rhel} # copied from lm_sensors exclusive arch %ifnarch alpha i386 i486 i586 i686 pentium3 pentium4 athlon x86_64 %global disable_snmpstats snmpstats %endif %endif %global EXCLUDE_MODULES sngtc osp cachedb_cassandra cachedb_couchbase cachedb_mongodb %{?disable_snmpstats} %{?el5:db_perlvdb} %{?el5:cachedb_redis} %{!?_with_oracle:db_oracle} lua Summary: Open Source SIP Server Name: opensips Version: 2.2.0 Release: 1%{?dist} License: GPLv2+ Group: System Environment/Daemons Source0: http://download.opensips.org/%{name}-%{version}.tar.gz URL: http://opensips.org BuildRequires: expat-devel BuildRequires: libxml2-devel BuildRequires: bison BuildRequires: flex BuildRequires: subversion BuildRequires: which # needed by snmpstats BuildRequires: radiusclient-ng-devel BuildRequires: mysql-devel BuildRequires: postgresql-devel Requires: m4 # required by snmpstats module %if %{undefined disable_snmpstats} BuildRequires: lm_sensors-devel %endif BuildRequires: net-snmp-devel BuildRequires: unixODBC-devel BuildRequires: openssl-devel BuildRequires: expat-devel BuildRequires: xmlrpc-c-devel BuildRequires: libconfuse-devel %if 0%{?rhel} BuildRequires: db4-devel %else BuildRequires: libdb-devel %endif BuildRequires: openldap-devel BuildRequires: curl-devel BuildRequires: GeoIP-devel BuildRequires: pcre-devel BuildRequires: python-devel %if 0%{?fedora} > 16 || 0%{?rhel} > 6 BuildRequires: systemd-units %endif BuildRequires: libxslt BuildRequires: lynx BuildRequires: ncurses-devel BuildRequires: json-c-devel #Initscripts %if 0%{?fedora} > 16 || 0%{?rhel} > 6 # Users and groups Requires(pre): shadow-utils Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %else Requires(post): chkconfig Requires(preun):chkconfig Requires(preun):initscripts %endif BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %description OpenSIPS or Open SIP Server is a very fast and flexible SIP (RFC3261) proxy server. Written entirely in C, opensips can handle thousands calls per second even on low-budget hardware. A C Shell like scripting language provides full control over the server's behaviour. It's modular architecture allows only required functionality to be loaded. Currently the following modules are available: digest authentication, CPL scripts, instant messaging, MySQL and UNIXODBC support, a presence agent, radius authentication, record routing, an SMS gateway, a jabber gateway, a transaction and dialog module, OSP module, statistics support, registrar and user location. %package aaa_radius Summary: RADIUS backend for AAA api Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description aaa_radius This module provides the RADIUS backend for the AAA API - group, auth, uri module use the AAA API for performing RADIUS ops. %package acc Summary: Accounts transactions information to different backends Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description acc ACC module is used to account transactions information to different backends like syslog, SQL, AAA. %package auth_aaa Summary: Performs authentication using an AAA server Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description auth_aaa This module contains functions that are used to perform authentication using an AAA server. Basically the proxy will pass along the credentials to the AAA server which will in turn send a reply containing result of the authentication. So basically the whole authentication is done in the AAA server. %package b2bua Summary: Back-2-Back User Agent Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description b2bua B2BUA is an implementation of the behavior of a B2BUA as defined in RFC 3261 that offers the possibility to build certain services on top of it. %package carrierroute Summary: Routing extension suitable for carriers Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description carrierroute A module which provides routing, balancing and blacklisting capabilities. %package clusterer Summary: Define and configure an OpenSIPS cluster Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description clusterer Clusterer module stores information about the status of a server belonging to a cluster. %package compression Summary: Message compression and compaction Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: zlib-devel %description compression This module implements message compression/decompression and base64 encoding for sip messages using deflate and gzip algorithm/headers. Another feature of this module is reducing headers to compact for as specified in SIP RFC's, sdp body codec unnecessary description removal (for codecs 0-97), whitelist for headers not be removed (excepting necessary headers). %package cpl_c Summary: Call Processing Language interpreter Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description cpl_c This module implements a CPL (Call Processing Language) interpreter. Support for uploading/downloading/removing scripts via SIP REGISTER method is present. %package db_berkeley Summary: Berkeley DB backend support Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description db_berkeley This is a module which integrates the Berkeley DB into OpenSIPS. It implements the DB API defined in OpenSIPS. %package db_http Summary: HTTP DB backend support Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description db_http This module provides access to a database that is implemented as a HTTP server. %package db_mysql Summary: MySQL Storage Support for the OpenSIPS Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: mysql-libs %description db_mysql The %{name}-db_mysql package contains the MySQL plugin for %{name}, which allows a MySQL-Database to be used for persistent storage. %if 0%{?_with_oracle} %package db_oracle Summary: Oracle Storage Support for the OpenSIPS Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: oracle-instantclient-devel %description db_oracle The %{name}-db_oracle package contains the Oracle plugin for %{name}, which allows a Oracle-Database to be used for persistent storage. %endif %if %{undefined el5} %package db_perlvdb Summary: Perl virtual database engine Group: System Environment/Daemons # require perl-devel for >F7 and perl for <=F6 BuildRequires: perl(ExtUtils::MakeMaker) Requires: %{name} = %{version}-%{release} Requires: %{name}-perl Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) %description db_perlvdb The Perl Virtual Database (VDB) provides a virtualization framework for OpenSIPS's database access. It does not handle a particular database engine itself but lets the user relay database requests to arbitrary Perl functions. %endif %package db_postgresql Summary: PostgreSQL Storage Support for the OpenSIPS Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: postgresql-libs %description db_postgresql The %{name}-db_postgresql package contains the PostgreSQL plugin for %{name}, which allows a PostgreSQL-Database to be used for persistent storage. %package db_sqlite Summary: SQLITE3-backend for database API module Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: sqlite-devel %description db_sqlite This is a module which provides SQLite support for OpenSIPS. It implements the DB API defined in OpenSIPS. %package emergency Summary: Emergency module Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description emergency The emergency module provides emergency call treatment for OpenSIPS, following the architecture i2 specification of the american entity NENA. (National Emergency Number Association). The NENA solution routes the emergency call to a closer gateway (ESGW) and this forward the call to a PSAP(call center responsible for answering emergency calls) that serves the area of the caller, so this must consider the handling and transport of caller location information in the SIP protocol. %package event_datagram Summary: Event datagram module Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description event_datagram This is a module which provides a UNIX/UDP SOCKET transport layer implementation for the Event Interface. %package event_flatstore Summary: Event flatstore module Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description event_flatstore Flatstore module provides a logging facility for different events, triggered through the OpenSIPS Event Interface, directly from the OpenSIPS script. The module logs the events along with their parameters in regular text files. %package event_rabbitmq Summary: Event RabbitMQ module Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: librabbitmq-devel %description event_rabbitmq This module provides the implementation of a RabbitMQ client for the Event Interface. It is used to send AMQP messages to a RabbitMQ server each time the Event Interface triggers an event subscribed for. %package event_route Summary: Route triggering based on events Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description event_route This module provides a simple way for handling different events, triggered through the OpenSIPS Event Interface, directly from the OpenSIPS script. For a specific event, a special route (event_route) has to be declared in the script, and should contain the code that handles the event. The route is executed by the module when the corresponding event is raised by the OpenSIPS Event Interface. %package event_virtual Summary: Aggregator of event backends (failover & balancing) Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description event_virtual Virtual module provides the possibility to have multiple external applications, using different transport protocols, subscribed to the OpenSIPS Event Interface as a single virtual subscriber, for a specific event. %package event_xmlrpc Summary: Event XMLRPC client module Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description event_xmlrpc This module is an implementation of an XMLRPC client used to notify XMLRPC servers whenever certain notifications are raised by OpenSIPS. It acts as a transport layer for the Event Notification Interface. %package fraud_detection Summary: Detects fraudulent calls Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description fraud_detection This module provides a way to prevent some basic fraud attacks. Alerts are provided through return codes and events. %package h350 Summary: H350 implementation Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description h350 The OpenSIPS H350 module enables an OpenSIPS SIP proxy server to access SIP account data stored in an LDAP [RFC4510] directory containing H.350 [H.350] commObjects. %package httpd Summary: HTTP transport layer implementation Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: libmicrohttpd-devel %description httpd This module provides an HTTP transport layer for OpenSIPS. %package jabber Summary: Gateway between OpenSIPS and a jabber server Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description jabber Jabber module that integrates XODE XML parser for parsing Jabber messages. %package json Summary: A JSON variables within the script Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description json This module introduces a new type of variable that provides both serialization and de-serialization from JSON format. %package ldap Summary: LDAP connector Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description ldap The LDAP module implements an LDAP search interface for OpenSIPS. %package memcached Summary: Memcached connector Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: libmemcached-devel %description memcached Memcached module is an implementation of a cache system designed to work with a memcached server. %package mmgeoip Summary: Wrapper for the MaxMind GeoIP API Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description mmgeoip Mmgeoip is a lightweight wrapper for the MaxMind GeoIP API. It adds IP address-to-location lookup capability to OpenSIPS scripts. %package peering Summary: Radius peering Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description peering Peering module allows SIP providers (operators or organizations) to verify from a broker if source or destination of a SIP request is a trusted peer. %package perl Summary: Helps implement your own OpenSIPS extensions in Perl Group: System Environment/Daemons # require perl-devel for >F7 and perl for <=F6 BuildRequires: perl(ExtUtils::MakeMaker) %if 0%{?rhel} BuildRequires: perl(ExtUtils::Embed) %else %if 0%{?rhel} == 5 BuildRequires: perl(ExtUtils::Embed), perl-devel %else BuildRequires: perl(ExtUtils::Embed) %endif %endif Requires: %{name} = %{version}-%{release} Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) %description perl The time needed when writing a new OpenSIPS module unfortunately is quite high, while the options provided by the configuration file are limited to the features implemented in the modules. With this Perl module, you can easily implement your own OpenSIPS extensions in Perl. This allows for simple access to the full world of CPAN modules. SIP URI rewriting could be implemented based on regular expressions; accessing arbitrary data backends, e.g. LDAP or Berkeley DB files, is now extremely simple. %package pi_http Summary: Provisioning Interface module Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description pi_http This module provides an HTTP provisioning interface for OpenSIPS. It is using the OpenSIPS's internal database API to provide a simple way of manipulating records inside OpenSIPS's tables. %package presence Summary: Presence server Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description presence This module implements a presence server. It handles PUBLISH and SUBSCRIBE messages and generates NOTIFY messages. It offers support for aggregation of published presence information for the same presentity using more devices. It can also filter the information provided to watchers according to privacy rules. %package presence_callinfo Summary: SIMPLE Presence extension Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-presence %description presence_callinfo The module enables the handling of "call-info" and "line-seize" events inside the presence module. It is used with the general event handling module: presence and it constructs and adds "Call-Info" headers to notification events. To send "call-info" notification to watchers, a third-party application must publish "call-info" events to the presence server. %package presence_dialoginfo Summary: Extension to Presence server for Dialog-Info Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-presence %description presence_dialoginfo The module enables the handling of "Event: dialog" (as defined in RFC 4235) inside of the presence module. This can be used distribute the dialog-info status to the subscribed watchers. %package presence_mwi Summary: Extension to Presence server for Message Waiting Indication Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-presence %description presence_mwi The module does specific handling for notify-subscribe message-summary (message waiting indication) events as specified in RFC 3842. It is used with the general event handling module, presence. It constructs and adds message-summary event to it. %package presence_xcapdiff Summary: Extension to Presence server for XCAP-DIFF event Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-presence Requires: %{name}-pua_mi %description presence_xcapdiff The presence_xcapdiff is an OpenSIPS module that adds support for the "xcap-diff" event to presence and pua. %package presence_xml Summary: SIMPLE Presence extension Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-presence Requires: %{name}-xcap_client %description presence_xml The module does specific handling for notify-subscribe events using xml bodies. It is used with the general event handling module, presence. %package proto_bin Summary: BIN protocol module - implements binary transport for OpenSIPS Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description proto_bin The proto_bin module is a transport module which implements BIN TCP-based communication. It does not handle TCP connections management, but only offers higher-level primitives to read and write BIN messages over TCP. It calls registered callback functions for every complete message received. %package proto_hep Summary: HEP protocol module - implements HEP transport for SIP Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description proto_hep The proto_hep module is a transport module which implements hepV1 and hepV2 UDP-based communication and hepV3 TCP-based communication. It also offers an API with which you can register callbacks which are called after the HEP header is parsed and also can pack sip messages to HEP messages.The unpacking part is done internally. %package proto_sctp Summary: SCTP protocol module - implements SCTP transport for SIP Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: lksctp-tools BuildRequires: lksctp-tools-devel %description proto_sctp The proto_sctp module is an optional transport module (shared library) which exports the required logic in order to handle SCTP-based communication. (socket initialization and send/recv primitives to be used by higher-level network layers) %package proto_tls Summary: TLS protocol module - implements TLS transport for SIP Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: openssl BuildRequires: openssl-devel %description proto_tls TLS, as defined in SIP RFC 3261, is a mandatory feature for proxies and can be used to secure the SIP signalling on a hop-by-hop basis (not end-to-end). TLS works on top of TCP. DTLS, or TLS over UDP is already defined by IETF and may become available in the future. This module also implements TLS related functions to use in the routing script, and exports pseudo variables with certificate and TLS parameters. %package proto_ws Summary: WebSocket protocol module - implements WS transport for SIP Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description proto_ws The WebSocket protocol (RFC 6455) provides an end-to-end full-duplex communication channel between two web-based applications. This allows WebSocket enabled browsers to connect to a WebSocket server and exchange any type of data. RFC 7118 provides the specifications for transporting SIP messages over the WebSocket protocol. %package proto_wss Summary: WebSocket protocol module - implements WSS transport for SIP Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description proto_wss The WebSocket protocol (RFC 6455) provides an end-to-end full-duplex communication channel between two web-based applications. This allows WebSocket enabled browsers to connect to a WebSocket server and exchange any type of data. RFC 7118 provides the specifications for transporting SIP messages over the WebSocket protocol. %package pua Summary: Offer the functionality of a presence user agent client Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description pua This module offer the functionality of a presence user agent client, sending Subscribe and Publish messages. %package pua_bla Summary: BLA extension for PUA Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-pua Requires: %{name}-presence %description pua_bla The pua_bla module enables Bridged Line Appearances support according to the specifications in draft-anil-sipping-bla-03.txt. %package pua_dialoginfo Summary: Dialog-Info extension for PUA Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-pua %description pua_dialoginfo The pua_dialoginfo retrieves dialog state information from the dialog module and PUBLISHes the dialog-information using the pua module. Thus, in combination with the presence_xml module this can be used to derive dialog-info from the dialog module and NOTIFY the subscribed watchers about dialog-info changes. %package pua_mi Summary: Connector between usrloc and MI interface Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-pua %description pua_mi The pua_mi sends offer the possibility to publish presence information via MI transports. Using this module you can create independent applications/scripts to publish not sip-related information (e.g., system resources like CPU-usage, memory, number of active subscribers ...) %package pua_usrloc Summary: Connector between usrloc and pua modules Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-pua %description pua_usrloc This module is the connector between usrloc and pua modules. It creates the environment to send PUBLISH requests for user location records, on specific events (e.g., when new record is added in usrloc, a PUBLISH with status open (online) is issued; when expires, it sends closed (offline)). Using this module, phones which have no support for presence can be seen as online/offline. %package pua_xmpp Summary: SIMPLE-XMPP Presence gateway Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-pua Requires: %{name}-presence Requires: %{name}-xmpp %description pua_xmpp This module is a gateway for presence between SIP and XMPP. It translates one format into another and uses xmpp, pua and presence modules to manage the transmition of presence state information. %package python Summary: Python scripting support Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description python Helps implement your own OpenSIPS extensions in Python %if %{undefined el5} %package redis Summary: Redis connector Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: hiredis-devel %description redis This module is an implementation of a cache system designed to work with a Redis server. %endif %package regex Summary: RegExp via PCRE library Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description regex This module offers matching operations against regular expressions using the powerful PCRE library. %package rest_client Summary: Implementation of an HTTP client Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description rest_client The rest_client module provides a means of interacting with an HTTP server by doing RESTful queries, such as GET and POST. %package rls Summary: Resource List Server Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-pua Requires: %{name}-presence Requires: %{name}-xcap %description rls The modules is a Resource List Server implementation following the specification in RFC 4662 and RFC 4826. %package rtpengine Summary: Connector to RTPengine external RTP relay Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description rtpengine This is a module that enables media streams to be proxied via an RTP proxy. The only RTP proxy currently known to work with this module is the Sipwise rtpengine https://github.com/sipwise/rtpengine. The rtpengine module is a modified version of the original rtpproxy module using a new control protocol. The module is designed to be a drop-in replacement for the old module from a configuration file point of view, however due to the incompatible control protocol, it only works with RTP proxies which specifically support it. %package seas Summary: Transfers the execution logic control to a given external entity Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description seas SEAS module enables OpenSIPS to transfer the execution logic control of a sip message to a given external entity, called the Application Server. When the OpenSIPS script is being executed on an incoming SIP message, invocation of the as_relay_t() function makes this module send the message along with some transaction information to the specified Application Server. The Application Server then executes some call-control logic code, and tells OpenSIPS to take some actions, ie. forward the message downstream, or respond to the message with a SIP repy, etc %package sms Summary: Gateway between SIP and GSM networks via sms Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description sms This module provides a way of communication between SIP network (via SIP MESSAGE) and GSM networks (via ShortMessageService). Communication is possible from SIP to SMS and vice versa. The module provides facilities like SMS confirmation--the gateway can confirm to the SIP user if his message really reached its destination as a SMS--or multi-part messages--if a SIP messages is too long it will be split and sent as multiple SMS. %if %{undefined disable_snmpstats} %package snmpstats Summary: SNMP management interface for the OpenSIPS Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) %description snmpstats The %{name}-snmpstats package provides an SNMP management interface to OpenSIPS. Specifically, it provides general SNMP queryable scalar statistics, table representations of more complicated data such as user and contact information, and alarm monitoring capabilities. %endif %package tls_mgm Summary: TLS management module Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description tls_mgm This module is a management module for TLS certificates and parameters. It provides an interfaces for all the modules that use the TLS protocol. It also implements TLS related functions to use in the routing script, and exports pseudo variables with certificate and TLS parameters. %package sql_cacher Summary: SQL Caching module Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description sql_cacher The sql_cacher module introduces the possibility to cache data from a SQL-based database (using different OpenSIPS modules which implement the DB API) into a cache system implemented in OpenSIPS through the CacheDB Interface. This is done by specifying the databases URLs, SQL table to be used, desired columns to be cached and other details in the OpenSIPS configuration script. %package topology_hiding Summary: Provides Topology Hiding capabilities Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description topology_hiding This is a module which provides topology hiding capabilities. The module can work on top of the dialog module, or as a standalone module ( thus alowing topology hiding for all types of requests ) %package unixodbc Summary: OpenSIPS unixODBC Storage support Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description unixodbc The %{name}-unixodbc package contains the unixODBC plugin for %{name}, which allows a unixODBC to be used for persistent storage %package xcap Summary: XCAP API provider Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description xcap The module contains several parameters and functions common to all modules using XCAP capabilities. %package xcap_client Summary: XCAP client Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: %{name}-xcap %description xcap_client The modules is an XCAP client for OpenSIPS that can be used by other modules. It fetches XCAP elements, either documents or part of them, by sending HTTP GET requests. It also offers support for conditional queries. It uses libcurl library as a client-side HTTP transfer library. %package xmlrpc Summary: A xmlrpc server Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description xmlrpc This module implements a xmlrpc server that handles xmlrpc requests and generates xmlrpc responses. When a xmlrpc message is received a default method is executed. %package xmpp Summary: Gateway between OpenSIPS and a jabber server Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %description xmpp This modules is a gateway between Openser and a jabber server. It enables the exchange of instant messages between SIP clients and XMPP(jabber) clients. %prep %setup -q -n %{name}-%{version} %build LOCALBASE=/usr NICER=0 CFLAGS="%{optflags}" %{?_with_oracle:ORAHOME="$ORACLE_HOME"} %{__make} all %{?_smp_mflags} TLS=1 \ exclude_modules="%EXCLUDE_MODULES" \ cfg_target=%{_sysconfdir}/opensips/ \ modules_prefix=%{buildroot}%{_prefix} \ modules_dir=%{_lib}/%{name}/modules %install rm -rf $RPM_BUILD_ROOT %{__make} install TLS=1 LIBDIR=%{_lib} \ exclude_modules="%EXCLUDE_MODULES" \ basedir=%{buildroot} prefix=%{_prefix} \ cfg_prefix=%{buildroot} \ modules_prefix=%{buildroot}/%{_prefix} \ modules_dir=%{_lib}/%{name}/modules \ DBTEXTON=yes # fixed dbtext documentation installation # clean some things %if 0%{?el5} rm -rf $RPM_BUILD_ROOT/%{_libdir}/opensips/perl/OpenSIPS/VDB* %endif mkdir -p $RPM_BUILD_ROOT/%{perl_vendorlib} if [ -d "$RPM_BUILD_ROOT/%{_prefix}/perl" ]; then # for fedora>=11 mv $RPM_BUILD_ROOT/%{_prefix}/perl/* \ $RPM_BUILD_ROOT/%{perl_vendorlib}/ else # for fedora<=10 mv $RPM_BUILD_ROOT/%{_libdir}/opensips/perl/* \ $RPM_BUILD_ROOT/%{perl_vendorlib}/ fi mv $RPM_BUILD_ROOT/%{_sysconfdir}/opensips/tls/README \ $RPM_BUILD_ROOT/%{_docdir}/opensips/README.tls rm -f $RPM_BUILD_ROOT%{_docdir}/opensips/INSTALL mv $RPM_BUILD_ROOT/%{_docdir}/opensips docdir # recode documentation for i in docdir/*; do mv -f $i $i.old iconv -f iso8859-1 -t UTF-8 $i.old > $i rm -f $i.old done %if 0%{?fedora} > 16 || 0%{?rhel} > 6 # install systemd files install -D -m 0644 -p packaging/redhat_fedora/%{name}.service $RPM_BUILD_ROOT%{_unitdir}/%{name}.service install -D -m 0644 -p packaging/redhat_fedora/%{name}.tmpfiles.conf $RPM_BUILD_ROOT%{_sysconfdir}/tmpfiles.d/%{name}.conf install -D -m 0755 -p packaging/redhat_fedora/%{name}.m4cfg $RPM_BUILD_ROOT%{_sbindir}/%{name}-m4cfg mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/%{name} %else install -p -D -m 755 packaging/redhat_fedora/opensips.init $RPM_BUILD_ROOT%{_initrddir}/opensips %endif echo -e "\nETCDIR=\"%{_sysconfdir}/opensips\"\n" \ >> $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/opensipsctlrc #install sysconfig file install -D -p -m 644 packaging/redhat_fedora/%{name}.sysconfig $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/%{name} %clean rm -rf $RPM_BUILD_ROOT %pre getent group %{name} >/dev/null || groupadd -r %{name} getent passwd %{name} >/dev/null || \ useradd -r -g %{name} -d %{_localstatedir}/run/%{name} -s /sbin/nologin \ -c "OpenSIPS SIP Server" %{name} 2>/dev/null || : %post %if 0%{?fedora} > 16 || 0%{?rhel} > 6 if [ $1 -eq 1 ] ; then # Initial installation /bin/systemctl daemon-reload >/dev/null 2>&1 || : fi %else /sbin/chkconfig --add %{name} %endif %preun %if 0%{?fedora} > 16 || 0%{?rhel} > 6 if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable %{name}.service > /dev/null 2>&1 || : /bin/systemctl stop %{name}.service > /dev/null 2>&1 || : fi %else if [ $1 = 0 ]; then /sbin/service %{name} stop > /dev/null 2>&1 /sbin/chkconfig --del %{name} fi %endif %files %{_sbindir}/opensips %{_sbindir}/opensipsctl %{_sbindir}/opensipsdbctl %{_sbindir}/opensipsunix %{_sbindir}/osipsconfig %{_sbindir}/osipsconsole %attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips %attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls %attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls/rootCA %attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls/rootCA/certs %attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls/rootCA/private %attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls/user %dir %{_libdir}/opensips/ %dir %{_libdir}/opensips/modules/ %dir %{_libdir}/opensips/opensipsctl/ %dir %{_libdir}/opensips/opensipsctl/dbtextdb/ %if 0%{?fedora} > 16 || 0%{?rhel} > 6 %{_unitdir}/%{name}.service %{_sysconfdir}/tmpfiles.d/%{name}.conf %{_sbindir}/%{name}-m4cfg %dir %attr(0755, %{name}, %{name}) %{_localstatedir}/run/%{name} %else %attr(755,root,root) %{_initrddir}/opensips %endif %config(noreplace) %{_sysconfdir}/opensips/dictionary.opensips %config(noreplace) %{_sysconfdir}/sysconfig/%{name} %attr(640,%{name},%{name}) %config(noreplace) %{_sysconfdir}/opensips/opensips.cfg %attr(640,%{name},%{name}) %config(noreplace) %{_sysconfdir}/opensips/opensipsctlrc %attr(640,%{name},%{name}) %config(noreplace) %{_sysconfdir}/opensips/osipsconsolerc # these files are just an examples so no need to restrict access to them %config(noreplace) %{_sysconfdir}/opensips/tls/ca.conf %config(noreplace) %{_sysconfdir}/opensips/tls/request.conf %config(noreplace) %{_sysconfdir}/opensips/tls/rootCA/cacert.pem %config(noreplace) %{_sysconfdir}/opensips/tls/rootCA/certs/01.pem %config(noreplace) %{_sysconfdir}/opensips/tls/rootCA/index.txt %config(noreplace) %{_sysconfdir}/opensips/tls/rootCA/private/cakey.pem %config(noreplace) %{_sysconfdir}/opensips/tls/rootCA/serial %config(noreplace) %{_sysconfdir}/opensips/tls/user.conf %config(noreplace) %{_sysconfdir}/opensips/tls/user/user-calist.pem %config(noreplace) %{_sysconfdir}/opensips/tls/user/user-cert.pem %config(noreplace) %{_sysconfdir}/opensips/tls/user/user-cert_req.pem %config(noreplace) %{_sysconfdir}/opensips/tls/user/user-privkey.pem %{_libdir}/opensips/opensipsctl/opensipsctl.base %{_libdir}/opensips/opensipsctl/opensipsctl.ctlbase %{_libdir}/opensips/opensipsctl/opensipsctl.dbtext %{_libdir}/opensips/opensipsctl/opensipsctl.fifo %{_libdir}/opensips/opensipsctl/opensipsctl.sqlbase %{_libdir}/opensips/opensipsctl/opensipsctl.unixsock %{_libdir}/opensips/opensipsctl/opensipsdbctl.base %{_libdir}/opensips/opensipsctl/opensipsdbctl.dbtext %{_libdir}/opensips/opensipsctl/dbtextdb/dbtextdb.py* %dir %{_datadir}/opensips/ %dir %{_datadir}/opensips/dbtext/ %dir %{_datadir}/opensips/dbtext/opensips/ %dir %{_datadir}/opensips/menuconfig_templates/ %{_datadir}/opensips/dbtext/opensips/* %{_datadir}/opensips/menuconfig_templates/*.m4 %{_mandir}/man5/opensips.cfg.5* %{_mandir}/man8/opensips.8* %{_mandir}/man8/opensipsctl.8* %{_mandir}/man8/opensipsunix.8* %doc docdir/AUTHORS %doc docdir/NEWS %doc docdir/README %doc docdir/README-MODULES %doc COPYING %{_libdir}/opensips/modules/acc.so %{_libdir}/opensips/modules/alias_db.so %{_libdir}/opensips/modules/auth.so %{_libdir}/opensips/modules/auth_db.so %{_libdir}/opensips/modules/avpops.so %{_libdir}/opensips/modules/benchmark.so %{_libdir}/opensips/modules/cachedb_local.so %{_libdir}/opensips/modules/cachedb_sql.so %{_libdir}/opensips/modules/call_control.so %{_libdir}/opensips/modules/cfgutils.so %{_libdir}/opensips/modules/db_cachedb.so %{_libdir}/opensips/modules/db_flatstore.so %{_libdir}/opensips/modules/db_virtual.so %{_libdir}/opensips/modules/db_text.so %{_libdir}/opensips/modules/dns_cache.so %{_libdir}/opensips/modules/dialog.so %{_libdir}/opensips/modules/dialplan.so %{_libdir}/opensips/modules/dispatcher.so %{_libdir}/opensips/modules/diversion.so %{_libdir}/opensips/modules/domain.so %{_libdir}/opensips/modules/domainpolicy.so %{_libdir}/opensips/modules/drouting.so %{_libdir}/opensips/modules/enum.so %{_libdir}/opensips/modules/exec.so %{_libdir}/opensips/modules/gflags.so %{_libdir}/opensips/modules/group.so %{_libdir}/opensips/modules/identity.so %{_libdir}/opensips/modules/imc.so %{_libdir}/opensips/modules/load_balancer.so %{_libdir}/opensips/modules/mangler.so %{_libdir}/opensips/modules/mathops.so %{_libdir}/opensips/modules/maxfwd.so %{_libdir}/opensips/modules/mediaproxy.so %{_libdir}/opensips/modules/mi_fifo.so %{_libdir}/opensips/modules/mi_datagram.so %{_libdir}/opensips/modules/mi_http.so %{_libdir}/opensips/modules/mi_json.so %{_libdir}/opensips/modules/msilo.so %{_libdir}/opensips/modules/nathelper.so %{_libdir}/opensips/modules/nat_traversal.so %{_libdir}/opensips/modules/rtpproxy.so %{_libdir}/opensips/modules/options.so %{_libdir}/opensips/modules/path.so %{_libdir}/opensips/modules/permissions.so %{_libdir}/opensips/modules/pike.so %{_libdir}/opensips/modules/qos.so %{_libdir}/opensips/modules/ratelimit.so %{_libdir}/opensips/modules/registrar.so %{_libdir}/opensips/modules/rr.so %{_libdir}/opensips/modules/script_helper.so %{_libdir}/opensips/modules/signaling.so %{_libdir}/opensips/modules/sipcapture.so %{_libdir}/opensips/modules/sipmsgops.so %{_libdir}/opensips/modules/siptrace.so %{_libdir}/opensips/modules/sl.so %{_libdir}/opensips/modules/speeddial.so %{_libdir}/opensips/modules/sst.so %{_libdir}/opensips/modules/statistics.so %{_libdir}/opensips/modules/stun.so %{_libdir}/opensips/modules/textops.so %{_libdir}/opensips/modules/tm.so %{_libdir}/opensips/modules/uac.so %{_libdir}/opensips/modules/uac_redirect.so %{_libdir}/opensips/modules/userblacklist.so %{_libdir}/opensips/modules/uri.so %{_libdir}/opensips/modules/usrloc.so %{_libdir}/opensips/modules/uac_auth.so %{_libdir}/opensips/modules/uac_registrant.so %doc docdir/README.acc %doc docdir/README.alias_db %doc docdir/README.auth %doc docdir/README.auth_db %doc docdir/README.avpops %doc docdir/README.benchmark %doc docdir/README.cachedb_local %doc docdir/README.cachedb_sql %doc docdir/README.call_control %doc docdir/README.cfgutils %doc docdir/README.db_flatstore %doc docdir/README.db_text %doc docdir/README.db_virtual %doc docdir/README.dialog %doc docdir/README.dialplan %doc docdir/README.dispatcher %doc docdir/README.diversion %doc docdir/README.dns_cache %doc docdir/README.domain %doc docdir/README.domainpolicy %doc docdir/README.drouting %doc docdir/README.enum %doc docdir/README.exec %doc docdir/README.gflags %doc docdir/README.group %doc docdir/README.identity %doc docdir/README.imc %doc docdir/README.load_balancer %doc docdir/README.mangler %doc docdir/README.maxfwd %doc docdir/README.mediaproxy %doc docdir/README.mi_datagram %doc docdir/README.mi_fifo %doc docdir/README.mi_http %doc docdir/README.msilo %doc docdir/README.nat_traversal %doc docdir/README.nathelper %doc docdir/README.options %doc docdir/README.path %doc docdir/README.permissions %doc docdir/README.pike %doc docdir/README.qos %doc docdir/README.ratelimit %doc docdir/README.registrar %doc docdir/README.rr %doc docdir/README.rtpproxy %doc docdir/README.signaling %doc docdir/README.sipcapture %doc docdir/README.sipmsgops %doc docdir/README.siptrace %doc docdir/README.sl %doc docdir/README.speeddial %doc docdir/README.sst %doc docdir/README.statistics %doc docdir/README.stun %doc docdir/README.textops %doc docdir/README.tls %doc docdir/README.tm %doc docdir/README.uac %doc docdir/README.uac_auth %doc docdir/README.uac_redirect %doc docdir/README.uac_registrant %doc docdir/README.uri %doc docdir/README.userblacklist %doc docdir/README.usrloc %files aaa_radius %{_libdir}/opensips/modules/aaa_radius.so %doc docdir/README.aaa_radius %files acc %{_libdir}/opensips/modules/acc.so %doc docdir/README.acc %files auth_aaa %{_libdir}/opensips/modules/auth_aaa.so %doc docdir/README.auth_aaa %files b2bua %{_libdir}/opensips/modules/b2b_entities.so %{_libdir}/opensips/modules/b2b_logic.so %{_libdir}/opensips/modules/b2b_sca.so %{_libdir}/opensips/modules/call_center.so %doc docdir/README.b2b_entities %doc docdir/README.b2b_logic %doc docdir/README.b2b_sca %doc docdir/README.call_center %files carrierroute %{_libdir}/opensips/modules/carrierroute.so %doc docdir/README.carrierroute %files clusterer %{_libdir}/opensips/modules/clusterer.so %doc docdir/README.clusterer %files compression %{_libdir}/opensips/modules/compression.so %doc docdir/README.compression %files cpl_c %{_libdir}/opensips/modules/cpl_c.so %doc docdir/README.cpl_c %files db_berkeley %{_sbindir}/bdb_recover %{_libdir}/opensips/modules/db_berkeley.so %{_libdir}/opensips/opensipsctl/opensipsctl.db_berkeley %{_libdir}/opensips/opensipsctl/opensipsdbctl.db_berkeley %dir %{_datadir}/opensips/db_berkeley %dir %{_datadir}/opensips/db_berkeley/opensips %{_datadir}/opensips/db_berkeley/opensips/* %doc docdir/README.db_berkeley %files db_http %{_libdir}/opensips/modules/db_http.so %doc docdir/README.db_http %files db_mysql %{_libdir}/opensips/modules/db_mysql.so %{_libdir}/opensips/opensipsctl/opensipsctl.mysql %{_libdir}/opensips/opensipsctl/opensipsdbctl.mysql %dir %{_datadir}/opensips/mysql %{_datadir}/opensips/mysql/*.sql %doc docdir/README.db_mysql %if 0%{?_with_oracle} %files db_oracle %{_sbindir}/opensips_orasel %{_libdir}/opensips/modules/db_oracle.so %{_libdir}/opensips/opensipsctl/opensipsctl.oracle %{_libdir}/opensips/opensipsctl/opensipsdbctl.oracle %{_libdir}/opensips/opensipsctl/opensipsdbfunc.oracle %dir %{_datadir}/opensips/oracle %{_datadir}/opensips/oracle/* %doc docdir/README.db_oracle %endif %if %{undefined el5} %files db_perlvdb %dir %{perl_vendorlib}/OpenSIPS/VDB %dir %{perl_vendorlib}/OpenSIPS/VDB/Adapter %{_libdir}/opensips/modules/db_perlvdb.so %{perl_vendorlib}/OpenSIPS/VDB.pm %{perl_vendorlib}/OpenSIPS/VDB/Adapter/AccountingSIPtrace.pm %{perl_vendorlib}/OpenSIPS/VDB/Adapter/Alias.pm %{perl_vendorlib}/OpenSIPS/VDB/Adapter/Auth.pm %{perl_vendorlib}/OpenSIPS/VDB/Adapter/Describe.pm %{perl_vendorlib}/OpenSIPS/VDB/Adapter/Speeddial.pm %{perl_vendorlib}/OpenSIPS/VDB/Adapter/TableVersions.pm %{perl_vendorlib}/OpenSIPS/VDB/Column.pm %{perl_vendorlib}/OpenSIPS/VDB/Pair.pm %{perl_vendorlib}/OpenSIPS/VDB/ReqCond.pm %{perl_vendorlib}/OpenSIPS/VDB/Result.pm %{perl_vendorlib}/OpenSIPS/VDB/VTab.pm %{perl_vendorlib}/OpenSIPS/VDB/Value.pm %doc docdir/README.db_perlvdb %endif %files db_postgresql %{_libdir}/opensips/modules/db_postgres.so %{_libdir}/opensips/opensipsctl/opensipsctl.pgsql %{_libdir}/opensips/opensipsctl/opensipsdbctl.pgsql %dir %{_datadir}/opensips/postgres %{_datadir}/opensips/postgres/*.sql %doc docdir/README.db_postgres %files db_sqlite %{_libdir}/opensips/modules/db_sqlite.so %doc docdir/README.db_sqlite %{_libdir}/opensips/opensipsctl/opensipsctl.sqlite %{_libdir}/opensips/opensipsctl/opensipsdbctl.sqlite %dir %{_datadir}/opensips/sqlite %{_datadir}/opensips/sqlite/*.sql %files emergency %{_libdir}/opensips/modules/emergency.so %doc docdir/README.emergency %files event_datagram %{_libdir}/opensips/modules/event_datagram.so %doc docdir/README.event_datagram %files event_flatstore %{_libdir}/opensips/modules/event_flatstore.so %doc docdir/README.event_flatstore %files event_rabbitmq %{_libdir}/opensips/modules/event_rabbitmq.so %doc docdir/README.event_rabbitmq %files event_route %{_libdir}/opensips/modules/event_route.so %doc docdir/README.event_route %files event_virtual %{_libdir}/opensips/modules/event_virtual.so %doc docdir/README.event_virtual %files event_xmlrpc %{_libdir}/opensips/modules/event_xmlrpc.so %doc docdir/README.event_xmlrpc %files fraud_detection %{_libdir}/opensips/modules/fraud_detection.so %doc docdir/README.fraud_detection %files h350 %{_libdir}/opensips/modules/h350.so %doc docdir/README.h350 %files httpd %{_libdir}/opensips/modules/httpd.so %doc docdir/README.httpd %files jabber %{_libdir}/opensips/modules/jabber.so %doc docdir/README.jabber %files json %{_libdir}/opensips/modules/json.so %doc docdir/README.json %files ldap %{_libdir}/opensips/modules/ldap.so %doc docdir/README.ldap %files memcached %{_libdir}/opensips/modules/cachedb_memcached.so %doc docdir/README.cachedb_memcached %files mmgeoip %{_libdir}/opensips/modules/mmgeoip.so %doc docdir/README.mmgeoip %files peering %{_libdir}/opensips/modules/peering.so %doc docdir/README.peering %files perl %dir %{perl_vendorlib}/OpenSIPS %dir %{perl_vendorlib}/OpenSIPS/LDAPUtils %dir %{perl_vendorlib}/OpenSIPS/Utils %{_libdir}/opensips/modules/perl.so %{perl_vendorlib}/OpenSIPS.pm %{perl_vendorlib}/OpenSIPS/Constants.pm %{perl_vendorlib}/OpenSIPS/LDAPUtils/LDAPConf.pm %{perl_vendorlib}/OpenSIPS/LDAPUtils/LDAPConnection.pm %{perl_vendorlib}/OpenSIPS/Message.pm %{perl_vendorlib}/OpenSIPS/Utils/PhoneNumbers.pm %{perl_vendorlib}/OpenSIPS/Utils/Debug.pm %doc docdir/README.perl %files pi_http %{_libdir}/opensips/modules/pi_http.so %{_datadir}/opensips/pi_http/* %doc docdir/README.pi_http %files presence %{_libdir}/opensips/modules/presence.so %doc docdir/README.presence %files presence_callinfo %{_libdir}/opensips/modules/presence_callinfo.so %doc docdir/README.presence_callinfo %files presence_dialoginfo %{_libdir}/opensips/modules/presence_dialoginfo.so %doc docdir/README.presence_dialoginfo %files presence_mwi %{_libdir}/opensips/modules/presence_mwi.so %doc docdir/README.presence_mwi %files presence_xcapdiff %{_libdir}/opensips/modules/presence_xcapdiff.so %doc docdir/README.presence_xcapdiff %files presence_xml %{_libdir}/opensips/modules/presence_xml.so %doc docdir/README.presence_xml %files proto_bin %{_libdir}/opensips/modules/proto_bin.so %doc docdir/README.proto_bin %files proto_hep %{_libdir}/opensips/modules/proto_hep.so %doc docdir/README.proto_hep %files proto_sctp %{_libdir}/opensips/modules/proto_sctp.so %doc docdir/README.proto_sctp %files proto_tls %{_libdir}/opensips/modules/proto_tls.so %doc docdir/README.proto_tls %files proto_ws %{_libdir}/opensips/modules/proto_ws.so %doc docdir/README.proto_ws %files proto_wss %{_libdir}/opensips/modules/proto_wss.so %doc docdir/README.proto_wss %files pua %{_libdir}/opensips/modules/pua.so %doc docdir/README.pua %files pua_bla %{_libdir}/opensips/modules/pua_bla.so %doc docdir/README.pua_bla %files pua_dialoginfo %{_libdir}/opensips/modules/pua_dialoginfo.so %doc docdir/README.pua_dialoginfo %files pua_mi %{_libdir}/opensips/modules/pua_mi.so %doc docdir/README.pua_mi %files pua_usrloc %{_libdir}/opensips/modules/pua_usrloc.so %doc docdir/README.pua_usrloc %files pua_xmpp %{_libdir}/opensips/modules/pua_xmpp.so %doc docdir/README.pua_xmpp %files python %{_libdir}/opensips/modules/python.so %if %{undefined el5} %files redis %{_libdir}/opensips/modules/cachedb_redis.so %doc docdir/README.cachedb_redis %endif %files regex %{_libdir}/opensips/modules/regex.so %doc docdir/README.regex %files rest_client %{_libdir}/opensips/modules/rest_client.so %doc docdir/README.rest_client %files rls %{_libdir}/opensips/modules/rls.so %doc docdir/README.rls %files rtpengine %{_libdir}/opensips/modules/rtpengine.so %doc docdir/README.rtpengine %files seas %{_libdir}/opensips/modules/seas.so %doc docdir/README.seas %files sms %{_libdir}/opensips/modules/sms.so %doc docdir/README.sms %if %{undefined disable_snmpstats} %files snmpstats %{_libdir}/opensips/modules/snmpstats.so %doc docdir/README.snmpstats %dir %{_datadir}/snmp %dir %{_datadir}/snmp/mibs %{_datadir}/snmp/mibs/OPENSER-MIB %{_datadir}/snmp/mibs/OPENSER-REG-MIB %{_datadir}/snmp/mibs/OPENSER-SIP-COMMON-MIB %{_datadir}/snmp/mibs/OPENSER-SIP-SERVER-MIB %{_datadir}/snmp/mibs/OPENSER-TC %endif %files sql_cacher %{_libdir}/opensips/modules/sql_cacher.so %doc docdir/README.sql_cacher %files tls_mgm %{_libdir}/opensips/modules/tls_mgm.so %doc docdir/README.tls_mgm %files topology_hiding %{_libdir}/opensips/modules/topology_hiding.so %doc docdir/README.topology_hiding %files unixodbc %{_libdir}/opensips/modules/db_unixodbc.so %doc docdir/README.db_unixodbc %files xcap %{_libdir}/opensips/modules/xcap.so %doc docdir/README.xcap %files xcap_client %{_libdir}/opensips/modules/xcap_client.so %doc docdir/README.xcap_client %files xmlrpc %{_libdir}/opensips/modules/mi_xmlrpc_ng.so %doc docdir/README.mi_xmlrpc_ng %files xmpp %{_libdir}/opensips/modules/xmpp.so %doc docdir/README.xmpp %changelog * Wed Jan 20 2016 Nick Altmann - 2.2.0-1 - Specification updated for opensips 2.2 - New packages: db_sqlite, clusterer, event_flatstore, event_virtual, proto_bin, proto_hep, proto_wss, sql_cacher - Renamed packages: mysql -> db_mysql, postgres -> db_postgres, cpl-c -> cpl_c * Sat Mar 14 2015 Nick Altmann - 2.1.0-1 - Specification updated for opensips 2.1 - Removed packages: auth_diameter, tlsops - New packages: compression, emergency, fraud_detection, proto_sctp, proto_tls, proto_ws, rtpengine, topology_hiding * Fri Mar 21 2014 Nick Altmann - 1.11.0-1 - Update to 1.11.0 * Tue Jul 30 2013 Nick Altmann - 1.10.0-1 - Update to 1.10.0 * Wed May 22 2013 Nick Altmann - 1.9.1-1 - Rebuild specification, add new modules and dependencies * Tue Jan 22 2013 Peter Lemenkov - 1.8.2-3 - Revert systemd macros * Thu Jan 10 2013 Peter Lemenkov - 1.8.2-2 - Allow rtpproxy module to accept avps - Few bugfixes * Tue Nov 06 2012 Peter Lemenkov - 1.8.2-1 - Ver. 1.8.2 (Bugfix release) * Sat Sep 22 2012 Remi Collet - 1.8.1-3 - rebuild against libmemcached.so.11 without SASL * Fri Aug 17 2012 Peter Lemenkov - 1.8.1-2 - Enabled json module - Enabled xmlrpc module - Enabled cachedb_memcached module on EL5, EL6 - Enabled cachedb_redis module on EL6 * Wed Aug 15 2012 Peter Lemenkov - 1.8.1-1 - Ver. 1.8.1 - Dropped all upstreamed patches * Fri Jul 20 2012 Fedora Release Engineering - 1.8.0-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild * Mon Jul 09 2012 Petr Pisar - 1.8.0-2 - Perl 5.16 rebuild * Tue Jul 03 2012 Peter Lemenkov - 1.8.0-1 - update to 1.8.0 * Fri Jun 08 2012 Petr Pisar - 1.7.2-8 - Perl 5.16 rebuild * Sat May 12 2012 Peter Lemenkov - 1.7.2-7 - Change %%define to %%global * Sat May 12 2012 Peter Lemenkov - 1.7.2-6 - Added missing docs * Fri May 11 2012 Peter Lemenkov - 1.7.2-5 - Fixed conditional building with Oracle DB * Sat Apr 28 2012 Peter Lemenkov - 1.7.2-4 - Fixes for systemd unit * Sun Apr 22 2012 Remi Collet - 1.7.2-3 - rebuild against libmemcached.so.10 * Thu Apr 19 2012 Peter Lemenkov - 1.7.2-2 - Fix building on EPEL * Thu Apr 19 2012 Peter Lemenkov - 1.7.2-1 - update to 1.7.2 (bugfix release). - enable systemd support where possible * Fri Apr 13 2012 Jindrich Novy - 1.7.1-6 - rebuild against new librpm and libdb * Sat Mar 03 2012 Remi Collet - 1.7.1-5 - rebuild against libmemcached.so.9 * Fri Feb 10 2012 Petr Pisar - 1.7.1-4 - Rebuild against PCRE 8.30 * Fri Jan 13 2012 Fedora Release Engineering - 1.7.1-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild * Thu Dec 01 2011 John Khvatov - 1.7.1-2 - upstream tarball rebuild * Thu Nov 24 2011 John Khvatov - 1.7.1-1 - update to 1.7.1 (bugfix release). * Mon Nov 07 2011 John Khvatov - 1.7.0-1 - update to 1.7.0 - dropped upstreamed patches - added new modules: event_datagram and python - removed lcr module * Sat Sep 17 2011 Remi Collet - 1.6.4-13 - rebuild against libmemcached.so.8 * Mon Aug 22 2011 John Khvatov - 1.6.4-12 - rebuild against new libnetsnmp * Thu Jul 21 2011 Petr Sabata - 1.6.4-11 - Perl mass rebuild * Wed Jul 20 2011 Petr Sabata - 1.6.4-10 - Perl mass rebuild * Mon Jul 11 2011 Peter Lemenkov - 1.6.4-9 - Updated init-script * Mon Jul 11 2011 Peter Lemenkov - 1.6.4-8 - Upstream re-released traball with several new patches (API compatible) * Fri Jun 17 2011 Marcela MaÅ¡láňová - 1.6.4-7 - Perl mass rebuild * Wed Mar 23 2011 Dan Horák - 1.6.4-6 - rebuilt for mysql 5.5.10 (soname bump in libmysqlclient) * Tue Feb 08 2011 Fedora Release Engineering - 1.6.4-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Wed Dec 22 2010 John Khvatov - 1.6.4-1 - dropped upstreamed patch (opensips-build.patch) - update to 1.6.4 - added new module: presence_callinfo * Sat Oct 30 2010 John Khvatov - 1.6.3-4 - rebuild against new libnetsnmp * Wed Oct 06 2010 Remi Collet - 1.6.3-3 - rebuilt against new libmemcached * Wed Sep 08 2010 Dan Horák - 1.6.3-2 - fix a build issue * Thu Aug 12 2010 John Khvatov - 1.6.3-1 - update to 1.6.3 * Wed Aug 11 2010 David Malcolm - 1.6.2-5 - recompiling .py files against Python 2.7 (rhbz#623343) * Tue Jun 01 2010 Marcela Maslanova - 1.6.2-4 - Mass rebuild with perl-5.12.0 * Wed May 05 2010 Remi Collet - 1.6.2-3 - rebuilt against new libmemcached * Thu Apr 15 2010 John Khvatov - 1.6.2-2 - Disabled build of the memcached subpackage for EPEL * Thu Apr 15 2010 John Khvatov - 1.6.2-1 - Updated to 1.6.2 * Sun Feb 07 2010 Remi Collet - 1.6.1-2 - rebuilt against new libmemcached * Tue Dec 22 2009 John Khvatov - 1.6.1-1 - Updated to 1.6.1 - Dropped upstreamed patches * Wed Nov 04 2009 John Khvatov - 1.6.0-4 - Fixed typo: pia_mi to pua_mi in presence_xcapdiff dependencies * Tue Nov 03 2009 John Khvatov - 1.6.0-3 - Added patch for compatibility with new openssl * Thu Oct 29 2009 John Khvatov - 1.6.0-2 - Added patch for init script to fix malformed comment block - Added COPYING file - Fixed not-capitalized summory of memcached subpackage * Mon Oct 19 2009 John Khvatov - 1.6.0-1 - Created new package from openser package - Upgrade to OpenSIPS 1.6 - New modules - Added osipconsole tool * Tue Aug 25 2009 Tomas Mraz - 1.3.4-8 - rebuilt with new openssl * Sat Jul 25 2009 Fedora Release Engineering - 1.3.4-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Mon Mar 02 2009 Jan ONDREJ (SAL) - 1.3.4-6 - allow build of this package on fedora<=10 * Sat Feb 28 2009 Jan ONDREJ (SAL) - 1.3.4-5 - fix module path * Sat Feb 28 2009 Jan ONDREJ (SAL) - 1.3.4-3 - addedd subversion build dependency to avoid svnversion error messages - fixed installation of perl modules in rawhide * Fri Jan 23 2009 Jan ONDREJ (SAL) 1.3.4-2 - Rebuild for new mysql. * Mon Dec 8 2008 Peter Lemenkov 1.3.4-1 - Ver. 1.3.4 - Added sysconfig-file * Thu Aug 28 2008 Michael Schwendt - 1.3.3-3 - Include lots of unowned directories. * Thu Aug 28 2008 Peter Lemenkov 1.3.3-2 - Removed dialplan and drouting modules from upstream * Thu Aug 28 2008 Peter Lemenkov 1.3.3-1 - Ver. 1.3.3 - Dropped upstreamed patch * Mon Aug 11 2008 Peter Lemenkov 1.3.2-5 - Typo fix * Mon Aug 11 2008 Peter Lemenkov 1.3.2-4 - Fix build with --fuzz=0 * Mon Aug 11 2008 Peter Lemenkov 1.3.2-3 - Fixed urls - Restricted access to openser.cfg and openserctl.cfg - Service won't start by default (BZ# 441297) * Fri May 16 2008 Peter Lemenkov 1.3.2-2 - New modules - dialplan and drouting (this one still has no README) * Thu May 15 2008 Peter Lemenkov 1.3.2-1 - Ver. 1.3.2 * Tue Mar 18 2008 Tom "spot" Callaway - 1.3.1-3 - add Requires for versioned perl (libperl.so) - drop silly file Requires * Fri Mar 14 2008 Jan ONDREJ (SAL) - 1.3.1-2 - removed perl patch, which is not necessary * Thu Mar 13 2008 Jan ONDREJ (SAL) - 1.3.1-1 - update to upstream - removed obsolete patches * Fri Mar 7 2008 Tom "spot" Callaway - 1.3.0-12 - patch perl code to use PERL_SYS_INIT3_BODY * Fri Mar 7 2008 Tom "spot" Callaway - 1.3.0-11 - fix perl build requires * Thu Mar 06 2008 Tom "spot" Callaway - 1.3.0-10 - Rebuild for new perl * Sat Feb 23 2008 Jan ONDREJ (SAL) 1.3.0-9 - ia64 build fix * Sat Feb 9 2008 Peter Lemenkov 1.3.0-8.1 - typo fix * Sat Feb 9 2008 Peter Lemenkov 1.3.0-8 - Rebuild for GCC 4.3 * Sat Jan 26 2008 Jan ONDREJ (SAL) 1.3.0-7 - Updated syntax error in default config * Sat Jan 26 2008 Peter Lemenkov 1.3.0-5 - Merge of acc module into main package * Fri Jan 25 2008 Jan ONDREJ (SAL) 1.3.0-4 - modify and apply forgotten patch4 * Thu Jan 17 2008 Jan ONDREJ (SAL) 1.3.0-2 - removed openser.init and replaced by upstream version - fixed configuration path for openserdbctl (#428799) * Sun Jan 13 2008 Peter Lemenkov 1.3.0-1.4 - 4th try to remove lm_sensors-devel from EL-[45] at ppc{64} * Thu Dec 13 2007 Peter Lemenkov 1.3.0-1 - Final ver. 1.3.0 - Removed some leftovers from spec-file * Wed Dec 12 2007 Peter Lemenkov 1.3.0-0.1.pre1 - Latest snapshot - 1.3.0pre1 * Mon Dec 10 2007 Jan ONDREJ (SAL) 1.2.2-11 - added ETCDIR into openserctlrc (need openser-1.3 to work) * Mon Sep 24 2007 Jan ONDREJ (SAL) 1.2.2-10 - perl scripts moved to perl_vendorlib directory - added LDAPUtils and Utils subdirectories - changed perl module BuildRequires * Mon Sep 24 2007 Jan ONDREJ (SAL) 1.2.2-9 - added reload section to init script - init script specified with initrddir macro - documentation converted to UTF-8 - added doc macro for documentation - documentation moved do proper place (/usr/share/doc/NAME-VERSION/) - which removed from BuildRequires, it's in guidelines exceptions - unixodbc subpackage summary update * Thu Sep 6 2007 Peter Lemenkov 1.2.2-8 - Added another one missing BR - which (needs by snmpstats module) - Cosmetic: dropped commented out 'Requires' * Thu Sep 06 2007 Jan ONDREJ (SAL) 1.2.2-7 - added attr macro for init script - added -p to install arguments to preserve timestamp - parallel make used * Sun Aug 26 2007 Jan ONDREJ (SAL) 1.2.2-6 - Fedora Core 6 build updates - changed attributes for openser.init to be rpmlint more silent * Sun Aug 26 2007 Peter Lemenkov 1.2.2-5 - fixed paths for openssl libs and includes * Sun Aug 26 2007 Peter Lemenkov 1.2.2-4 - Introduced acc and acc_radius modules (Jan Ondrej) - Dropped radius_accounting condition * Sat Aug 25 2007 Peter Lemenkov 1.2.2-3 - Changed license according to Fedora's policy - Make rpmlint more silent * Fri Aug 24 2007 Jan ONDREJ (SAL) 1.2.2-2 - added openser.init script - removed Patch0: openser--Makefile.diff and updated build section - spec file is 80 characters wide - added radius_accounting condition * Wed Aug 22 2007 Peter Lemenkov 1.2.2-1 - Ver. 1.2.2 * Tue Jul 24 2007 Peter Lemenkov 1.2.1-1 - Initial spec. opensips-2.2.2/packaging/redhat_fedora/opensips.sysconfig000066400000000000000000000000451300170765700236630ustar00rootroot00000000000000# # OpenSIPS variables # OPTIONS="" opensips-2.2.2/packaging/redhat_fedora/opensips.tmpfiles.conf000066400000000000000000000000531300170765700244250ustar00rootroot00000000000000d /var/run/opensips 0755 opensips opensips opensips-2.2.2/packaging/rpm/000077500000000000000000000000001300170765700161055ustar00rootroot00000000000000opensips-2.2.2/packaging/rpm/opensips.default000066400000000000000000000014631300170765700213170ustar00rootroot00000000000000# # OpenSIPS startup options # # Set to yes to enable opensips, once configured properly. RUN_OPENSIPS=yes # User to run as USER=opensips # Group to run as GROUP=opensips # Amount of shared memory to allocate for the running OpenSIPS server (in Mb) S_MEMORY=64 # Amount of pkg memory to allocate for the running OpenSIPS server (in Mb) P_MEMORY=4 # Enable the server to leave a core file when it crashes. # Set this to 'yes' to enable OpenSIPS to leave a core file when it crashes # or 'no' to disable this feature. This option is case sensitive and only # accepts 'yes' and 'no' and only in lowercase letters. # On some systems (e.g. Ubuntu 6.10, Debian 4.0) it is necessary to specify # a directory for the core files to get a dump. Look into the opensips # init file for an example configuration. DUMP_CORE=no opensips-2.2.2/packaging/rpm/opensips.init000066400000000000000000000042511300170765700206340ustar00rootroot00000000000000#!/bin/bash # # Startup script for OpenSIPS # # chkconfig: 345 85 15 # description: OpenSIPS is a fast SIP Server. # # processname: opensips # pidfile: /var/run/opensips.pid # config: /etc/opensips/opensips.cfg # Source function library. . /etc/rc.d/init.d/functions OSER=/usr/sbin/opensips PROG=opensips PID_FILE=/var/run/opensips.pid LOCK_FILE=/var/lock/subsys/opensips RETVAL=0 DEFAULTS=/etc/default/opensips RUN_OPENSIPS=no check_opensips_config () { # Check if opensips configuration is valid before starting the server out=$($OSER -c 2>&1 > /dev/null) retcode=$? if [ "$retcode" != '0' ]; then echo "Not starting $DESC: invalid configuration file!" echo -e "\n$out\n" exit 1 fi } start() { check_opensips_config echo -n $"Starting $PROG: " daemon $OSER $OPTIONS >/dev/null 2>/dev/null RETVAL=$? echo [ $RETVAL = 0 ] && touch $LOCK_FILE return $RETVAL } stop() { echo -n $"Stopping $PROG: " killproc $OSER RETVAL=$? echo [ $RETVAL = 0 ] && rm -f $LOCK_FILE $PID_FILE } # Load startup options if available if [ -f $DEFAULTS ]; then . $DEFAULTS || true fi if [ "$RUN_OPENSIPS" != "yes" ]; then echo "OpenSIPS not yet configured. Edit /etc/default/opensips first." exit 0 fi S_MEMORY=$((`echo $S_MEMORY | sed -e 's/[^0-9]//g'`)) P_MEMORY=$((`echo $P_MEMORY | sed -e 's/[^0-9]//g'`)) [ -z "$USER" ] && USER=opensips [ -z "$GROUP" ] && GROUP=opensips [ $S_MEMORY -le 0 ] && S_MEMORY=32 [ $P_MEMORY -le 0 ] && P_MEMORY=4 if test "$DUMP_CORE" = "yes" ; then # set proper ulimit ulimit -c unlimited # directory for the core dump files # COREDIR=/home/corefiles # [ -d $COREDIR ] || mkdir $COREDIR # chmod 777 $COREDIR # echo "$COREDIR/core.%e.sig%s.%p" > /proc/sys/kernel/core_pattern fi OPTIONS="-P $PID_FILE -m $S_MEMORY -M $P_MEMORY -u $USER -g $GROUP" # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status $OSER RETVAL=$? ;; restart) stop start ;; condrestart) if [ -f $PID_FILE ] ; then stop start fi ;; *) echo $"Usage: $PROG {start|stop|restart|condrestart|status|help}" exit 1 esac exit $RETVAL opensips-2.2.2/packaging/rpm/opensips.init.SuSE000066400000000000000000000100531300170765700214470ustar00rootroot00000000000000#! /bin/sh # Copyright (c) 2002 FhG FOKUS, Germany. # All rights reserved. # # /etc/init.d/opensips # ### BEGIN INIT INFO # Provides: sip # Required-Start: $network # X-UnitedLinux-Should-Start: $network # Required-Stop: $network # X-UnitedLinux-Should-Stop: $network # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: OpenSIPS # Description: Start OpenSIPS and provide the routing of SIP requests. ### END INIT INFO # Check for missing binaries (stale symlinks should not happen) OSIPS_BIN=/usr/sbin/opensips test -x $OSIPS_BIN || exit 5 # Check for existence of needed config file and read it OSIPS_CONFIG=/etc/opensips/opensips.cfg test -r $OSIPS_CONFIG || exit 6 # Shell functions sourced from /etc/rc.status: # rc_check check and set local and overall rc status # rc_status check and set local and overall rc status # rc_status -v ditto but be verbose in local rc status # rc_status -v -r ditto and clear the local rc status # rc_status -s display "skipped" and exit with status 3 # rc_status -u display "unused" and exit with status 3 # rc_failed set local and overall rc status to failed # rc_failed set local and overall rc status to # rc_reset clear local rc status (overall remains) # rc_exit exit appropriate to overall rc status # rc_active checks whether a service is activated by symlinks # rc_splash arg sets the boot splash screen to arg (if active) . /etc/rc.status # Reset status of this service rc_reset # Return values acc. to LSB for all commands but status: # 0 - success # 1 - generic or unspecified error # 2 - invalid or excess argument(s) # 3 - unimplemented feature (e.g. "reload") # 4 - user had insufficient privileges # 5 - program is not installed # 6 - program is not configured # 7 - program is not running # 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) # # Note that starting an already running service, stopping # or restarting a not-running service as well as the restart # with force-reload (in case signaling is not supported) are # considered a success. case "$1" in start) echo -n "Starting OpenSIPS " ## Start daemon with startproc(8). If this fails ## the return value is set appropriately by startproc. startproc $OSIPS_BIN -f $OSIPS_CONFIG # Remember status and be verbose rc_status -v ;; stop) echo -n "Shutting down OpenSIPS " ## Stop daemon with killproc(8) and if this fails ## killproc sets the return value according to LSB. killproc -TERM $OSIPS_BIN # Remember status and be verbose rc_status -v ;; restart) ## Stop the service and regardless of whether it was ## running or not, start it again. $0 stop $0 start # Remember status and be quiet rc_status ;; force-reload) ## Signal the daemon to reload its config. Most daemons ## do this on signal 1 (SIGHUP). ## If it does not support it, restart. echo -n "Reload service OpenSIPS " ## Otherwise: $0 stop && $0 start rc_status ;; reload) ## Like force-reload, but if daemon does not support ## signaling, do nothing (!) ## Otherwise if it does not support reload: rc_failed 3 rc_status -v ;; status) echo -n "Checking for service OpenSIPS " ## Check status with checkproc(8), if process is running ## checkproc will return with exit status 0. # Return value is slightly different for the status command: # 0 - service up and running # 1 - service dead, but /var/run/ pid file exists # 2 - service dead, but /var/lock/ lock file exists # 3 - service not running (unused) # 4 - service status unknown :-( # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) # NOTE: checkproc returns LSB compliant status values. checkproc $OSIPS_BIN # NOTE: rc_status knows that we called this init script with # "status" option and adapts its messages accordingly. rc_status -v ;; *) echo "Usage: $0 {start|stop|status|restart|force-reload|reload}" exit 1 ;; esac rc_exit opensips-2.2.2/packaging/rpm/opensips.spec.CentOS000077500000000000000000000763011300170765700217650ustar00rootroot00000000000000%define name opensips %define ver 2.2 %define rel 1 %define _sharedir %{_prefix}/share Summary: OpenSIPS, very fast and flexible SIP Server Name: %name Version: %ver Release: %rel Packager: Bogdan-Andrei Iancu License: GPL Group: System Environment/Daemons Source0: http://opensips.org/pub/opensips/%{ver}/src/%{name}-%{ver}_src.tar.gz Source1: opensips.init Source2: opensips.default URL: http://opensips.org/ Vendor: opensips.org BuildRoot: %{_tmppath}/%{name}-%{ver}-buildroot Conflicts: opensips-mysql < %ver, opensips-xmpp < %ver, opensips-radius < %ver, opensips-cpl < %ver, opensips-unixodbc < %ver, opensips-presence < %ver, opensips-postgres < %ver, opensips-snmpstats < %ver BuildRequires: make flex bison pcre-devel lua-devel libmemcache-devel OSPToolkit-devel mongo-c-driver-devel %description OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. A C Shell like scripting language provides full control over the server's behaviour. It's modular architecture allows only required functionality to be loaded. Currently the following modules are available: digest authentication, CPL scripts, instant messaging, MySQL and UNIXODBC support, a presence agent, radius authentication, record routing, an SMS gateway, a jabber gateway, a transaction and dialog module, OSP module, statistics support, registrar and user location, SNMP, SIMPLE Presence and Perl programming interface. %package mysql Summary: MySQL connectivity for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: mysql-devel, zlib-devel %description mysql The opensips-mysql package contains MySQL database connectivity that you need to use digest authentication module or persistent user location entries. %package postgres Summary: MPOSTGRES connectivity for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: postgresql-devel %description postgres The opensips-postgres package contains Postgres database connectivity that you need to use digest authentication module or persistent user location entries. %package unixodbc Summary: UNIXODBC connectivity for OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: unixODBC-devel %description unixodbc The opensips-unixodbc package contains UNIXODBC database connectivity support that is required by other modules with database dependencies. %package xmpp Summary: sip 2 xmpp/jabber message translation support for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: expat-devel %description xmpp The opensips-xmpp package contains a sip to xmpp/jabber message translator. %%package cpl Summary: CPL interpreter engine for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: libxml2-devel %description cpl The opensips-cpl package contains a SIP CPL interpreter engine. %package presence Summary: sip presence user agent support for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: libxml2-devel, curl-devel %description presence The opensips-pua package contains a sip Presence Agent. %package radius Summary: opensips radius support for AAA API. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: radiusclient-ng-devel %description radius The opensips-radius package contains modules for radius authentication, group membership and uri checking. %package b2bua Summary: opensips b2bua implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: libxml2-devel %description b2bua The opensips-b2bua package contains modules implement b2bua scenarios. %package snmpstats Summary: SNMP AgentX subagent module for OpenSIPS Group: System Environment/Daemons Requires: opensips = %ver, net-snmp-utils BuildRequires: lm_sensors-devel net-snmp-devel %description snmpstats OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. This package provides the snmpstats module for OpenSIPS. This module acts as an AgentX subagent which connects to a master agent. %package ldap Summary: opensips ldap implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: openldap-devel %description ldap The LDAP module implements an LDAP search interface for OpenSIPS. It exports script functions to perform an LDAP search operation and to store the search results as OpenSIPS AVPs. This allows for using LDAP directory data in the OpenSIPS SIP message routing script. %package identity Summary: opensips identity implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: openssl-devel %description identity This module adds support for SIP Identity (see RFC 4474). %package regex Summary: opensips identity implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: pcre-devel %description regex This module offers matching operations against regular expressions using the powerful PCRE library. %package mmgeoip Summary: opensips mmgeoip implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: GeoIP-devel %description mmgeoip This module is a lightweight wrapper for the MaxMind GeoIP API. It adds IP address-to-location lookup capability to OpenSIPS scripts. %package json Summary: opensips json implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: json-c-devel %description json This module introduces a new type of variable that provides both serialization and de-serialization from JSON format. %package carrierroute Summary: opensips carrierroute implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: libconfuse-devel %description carrierroute A module which provides routing, balancing and blacklisting capabilities. %package cachedb_memcached Summary: opensips cachedb_memcached implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: libconfuse-devel libmemcached-devel %description cachedb_memcached This module is an implementation of a cache system designed to work with a memcached server. It uses libmemcached client library to connect to several memcached servers that store data. It registers the three functions for storing, fetching and removing a value to the core memcache management interface. %package cachedb_couchbase Summary: opensips cachedb_couchbase implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: libcouchbase-devel %description cachedb_couchbase This module is an implementation of a cache system designed to work with a Couchbase server. It uses the libcouchbase client library to connect to the server instance, It uses the Key-Value interface exported from the core. %package event_rabbitmq Summary: opensips event_rabbitmq implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: librabbitmq-devel %description event_rabbitmq This module provides the implementation of a RabbitMQ client for the Event Interface. It is used to send AMQP messages to a RabbitMQ server each time the Event Interface triggers an event subscribed for. %package cachedb_redis Summary: opensips cachedb_redis implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: hiredis-devel %description cachedb_redis This module is an implementation of a cache system designed to work with a Redis server. It uses hiredis client library to connect to either a single Redis server instance, or to a Redis Server inside a Redis Cluster. It uses the Key-Value interface exported from the core. %package db_berkeley Summary: opensips db_berkeley implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: db4-devel %description db_berkeley This is a module which integrates the Berkeley DB into OpenSIPS. It implements the DB API defined in OpenSIPS. %package db_oracle Summary: opensips db_oracle implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: oracle-instantclient11.2-devel AutoReqProv: no %description db_oracle This is a module which provides Oracle connectivity for OpenSIPS. It implements the DB API defined in OpenSIPS. If you want to use the nathelper module, or any other modules that calls the get_all_ucontacts API export from usrloc, then you need to set the DORACLE_USRLOC define in the Makefile.defs file before compilation. %package mi_xmlrpc Summary: opensips mi_xmlrpc implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: xmlrpc-c-devel %description mi_xmlrpc This module implements a xmlrpc server that handles xmlrpc requests and generates xmlrpc responses. When a xmlrpc message is received a default method is executed. %package mi_xmlrpc_ng Summary: opensips mi_xmlrpc_ng implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: xmlrpc-c-devel %description mi_xmlrpc_ng This module implements a xmlrpc_ng server that handles xmlrpc_ng requests and generates xmlrpc_ng responses. When a xmlrpc_ng message is received a default method is executed. %package db_http Summary: opensips db_http implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: libcurl-devel libmicrohttpd-devel %description db_http This module provides access to a database that is implemented as a HTTP server. It may be used in special cases where traversing firewalls is a problem, or where data encryption is required. %package cachedb_cassandra Summary: Cassandra connector Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: thrift-cpp-devel %description cachedb_cassandra Cassandra module is an implementation of a cache system designed to work with a cassandra server. %package cachedb_mongodb Summary: Mongodb connector Group: System Environment/Daemons Requires: opensips = %ver %description cachedb_mongodb Mongodb module is an implementation of a cache system designed to work with a mongodb server. %package osp Summary: OSP Support for the OpenSIPS Group: System Environment/Daemons Requires: opensips = %ver %description osp The OSP module enables OpenSIPS to support secure, multi-lateral peering using the OSP standard defined by ETSI (TS 101 321 V4.1.1). %package perl Summary: Helps implement your own OpenSIPS extensions in Perl Group: System Environment/Daemons BuildRequires: perl(ExtUtils::MakeMaker),perl(ExtUtils::Embed), perl-devel Requires: opensips = %ver Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) %description perl The time needed when writing a new OpenSIPS module unfortunately is quite high, while the options provided by the configuration file are limited to the features implemented in the modules. With this Perl module, you can easily implement your own OpenSIPS extensions in Perl. This allows for simple access to the full world of CPAN modules. SIP URI rewriting could be implemented based on regular expressions; accessing arbitrary data backends, e.g. LDAP or Berkeley DB files, is now extremely simple. %package perlvdb Summary: Perl virtual database engine Group: System Environment/Daemons BuildRequires: perl(ExtUtils::MakeMaker) Requires: opensips = %ver Requires: opensips-perl Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) %description perlvdb The Perl Virtual Database (VDB) provides a virtualization framework for OpenSIPS's database access. It does not handle a particular database engine itself but lets the user relay database requests to arbitrary Perl functions. %package sngtc Summary: Sangoma media transcoding interface for the OpenSIPS Group: System Environment/Daemons Requires: opensips = %ver %description sngtc The sngtc package implements interface to Sangoma media transcoding. %package tlsops Summary: TLS-relating functions for the OpenSIPS Group: System Environment/Daemons Requires: opensips = %ver %description tlsops The opensips-tlsops package implements TLS related functions to use in the routing script, and exports pseudo variables with certificate and TLS parameters. %package rest_client Summary: opensips rest client implementation. Group: System Environment/Daemons Requires: opensips = %ver BuildRequires: libcurl-devel %description rest_client This module provides REST client capabilities for OpenSIPS to query a REST server. %prep %setup -n %{name}-%{ver}-tls %build ARCH=$(uname -m) if [ "$ARCH" == "x86_64" ]; then ORACLIENT=64 else ORACLIENT= fi make all exclude_modules="" ORAVERSION=11.2/client$ORACLIENT cfg_target=%{_sysconfdir}/opensips/ %install [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT" make install exclude_modules="" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d install -m755 $RPM_SOURCE_DIR/opensips.init \ $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/opensips mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/default install -m755 $RPM_SOURCE_DIR/opensips.default \ $RPM_BUILD_ROOT/%{_sysconfdir}/default/opensips %clean rm -rf "$RPM_BUILD_ROOT" %post /sbin/chkconfig --add opensips %preun if [ $1 = 0 ]; then /sbin/service opensips stop > /dev/null 2>&1 /sbin/chkconfig --del opensips fi %files %defattr(-,root,root) %dir %{_docdir}/opensips %doc %{_docdir}/opensips/AUTHORS %doc %{_docdir}/opensips/INSTALL %doc %{_docdir}/opensips/README %doc %{_docdir}/opensips/NEWS %doc %{_docdir}/opensips/README-MODULES %doc %{_docdir}/opensips/README.acc %doc %{_docdir}/opensips/README.alias_db %doc %{_docdir}/opensips/README.auth %doc %{_docdir}/opensips/README.auth_aaa %doc %{_docdir}/opensips/README.auth_db %doc %{_docdir}/opensips/README.auth_diameter %doc %{_docdir}/opensips/README.avpops %doc %{_docdir}/opensips/README.benchmark %doc %{_docdir}/opensips/README.cachedb_local %doc %{_docdir}/opensips/README.cachedb_sql %doc %{_docdir}/opensips/README.call_control %doc %{_docdir}/opensips/README.cfgutils %doc %{_docdir}/opensips/README.closeddial %doc %{_docdir}/opensips/README.db_flatstore %doc %{_docdir}/opensips/README.db_cachedb %doc %{_docdir}/opensips/README.db_text %doc %{_docdir}/opensips/README.db_virtual %doc %{_docdir}/opensips/README.dialog %doc %{_docdir}/opensips/README.dispatcher %doc %{_docdir}/opensips/README.diversion %doc %{_docdir}/opensips/README.dns_cache %doc %{_docdir}/opensips/README.domain %doc %{_docdir}/opensips/README.domainpolicy %doc %{_docdir}/opensips/README.drouting %doc %{_docdir}/opensips/README.enum %doc %{_docdir}/opensips/README.event_datagram %doc %{_docdir}/opensips/README.event_route %doc %{_docdir}/opensips/README.event_xmlrpc %doc %{_docdir}/opensips/README.exec %doc %{_docdir}/opensips/README.gflags %doc %{_docdir}/opensips/README.group %doc %{_docdir}/opensips/README.imc %doc %{_docdir}/opensips/README.load_balancer %doc %{_docdir}/opensips/README.lua %doc %{_docdir}/opensips/README.mangler %doc %{_docdir}/opensips/README.mathops %doc %{_docdir}/opensips/README.maxfwd %doc %{_docdir}/opensips/README.mediaproxy %doc %{_docdir}/opensips/README.mi_datagram %doc %{_docdir}/opensips/README.mi_fifo %doc %{_docdir}/opensips/README.mi_http %doc %{_docdir}/opensips/README.msilo %doc %{_docdir}/opensips/README.nat_traversal %doc %{_docdir}/opensips/README.nathelper %doc %{_docdir}/opensips/README.options %doc %{_docdir}/opensips/README.path %doc %{_docdir}/opensips/README.permissions %doc %{_docdir}/opensips/README.pike %doc %{_docdir}/opensips/README.python %doc %{_docdir}/opensips/README.qos %doc %{_docdir}/opensips/README.ratelimit %doc %{_docdir}/opensips/README.registrar %doc %{_docdir}/opensips/README.rest_client %doc %{_docdir}/opensips/README.rr %doc %{_docdir}/opensips/README.rtpproxy %doc %{_docdir}/opensips/README.seas %doc %{_docdir}/opensips/README.script_helper %doc %{_docdir}/opensips/README.signaling %doc %{_docdir}/opensips/README.sipcapture %doc %{_docdir}/opensips/README.sipmsgops %doc %{_docdir}/opensips/README.siptrace %doc %{_docdir}/opensips/README.sl %doc %{_docdir}/opensips/README.sms %doc %{_docdir}/opensips/README.speeddial %doc %{_docdir}/opensips/README.sst %doc %{_docdir}/opensips/README.statistics %doc %{_docdir}/opensips/README.stun %doc %{_docdir}/opensips/README.textops %doc %{_docdir}/opensips/README.tm %doc %{_docdir}/opensips/README.uac %doc %{_docdir}/opensips/README.uac_auth %doc %{_docdir}/opensips/README.uac_redirect %doc %{_docdir}/opensips/README.uac_registrant %doc %{_docdir}/opensips/README.uri %doc %{_docdir}/opensips/README.userblacklist %doc %{_docdir}/opensips/README.usrloc %dir %{_sysconfdir}/opensips %config(noreplace) %{_sysconfdir}/opensips/* %config %{_sysconfdir}/rc.d/init.d/* %config %{_sysconfdir}/default/* %dir %{_libdir}/opensips %dir %{_libdir}/opensips/modules %{_libdir}/opensips/modules/acc.so %{_libdir}/opensips/modules/alias_db.so %{_libdir}/opensips/modules/auth.so %{_libdir}/opensips/modules/auth_aaa.so %{_libdir}/opensips/modules/auth_db.so %{_libdir}/opensips/modules/auth_diameter.so %{_libdir}/opensips/modules/avpops.so %{_libdir}/opensips/modules/benchmark.so %{_libdir}/opensips/modules/cachedb_local.so %{_libdir}/opensips/modules/cachedb_sql.so %{_libdir}/opensips/modules/call_control.so %{_libdir}/opensips/modules/cfgutils.so %{_libdir}/opensips/modules/closeddial.so %{_libdir}/opensips/modules/db_cachedb.so %{_libdir}/opensips/modules/db_flatstore.so %{_libdir}/opensips/modules/db_text.so %{_libdir}/opensips/modules/db_virtual.so %{_libdir}/opensips/modules/dialog.so %{_libdir}/opensips/modules/dispatcher.so %{_libdir}/opensips/modules/diversion.so %{_libdir}/opensips/modules/dns_cache.so %{_libdir}/opensips/modules/domain.so %{_libdir}/opensips/modules/domainpolicy.so %{_libdir}/opensips/modules/drouting.so %{_libdir}/opensips/modules/enum.so %{_libdir}/opensips/modules/event_datagram.so %{_libdir}/opensips/modules/event_route.so %{_libdir}/opensips/modules/event_xmlrpc.so %{_libdir}/opensips/modules/exec.so %{_libdir}/opensips/modules/gflags.so %{_libdir}/opensips/modules/group.so %{_libdir}/opensips/modules/imc.so %{_libdir}/opensips/modules/load_balancer.so %{_libdir}/opensips/modules/lua.so %{_libdir}/opensips/modules/mangler.so %{_libdir}/opensips/modules/mathops.so %{_libdir}/opensips/modules/maxfwd.so %{_libdir}/opensips/modules/mediaproxy.so %{_libdir}/opensips/modules/mi_datagram.so %{_libdir}/opensips/modules/mi_fifo.so %{_libdir}/opensips/modules/mi_http.so %{_libdir}/opensips/modules/msilo.so %{_libdir}/opensips/modules/nat_traversal.so %{_libdir}/opensips/modules/nathelper.so %{_libdir}/opensips/modules/options.so %{_libdir}/opensips/modules/path.so %{_libdir}/opensips/modules/permissions.so %{_libdir}/opensips/modules/pike.so %{_libdir}/opensips/modules/python.so %{_libdir}/opensips/modules/qos.so %{_libdir}/opensips/modules/ratelimit.so %{_libdir}/opensips/modules/registrar.so %{_libdir}/opensips/modules/rest_client.so %{_libdir}/opensips/modules/rr.so %{_libdir}/opensips/modules/rtpproxy.so %{_libdir}/opensips/modules/seas.so %{_libdir}/opensips/modules/script_helper.so %{_libdir}/opensips/modules/signaling.so %{_libdir}/opensips/modules/sipcapture.so %{_libdir}/opensips/modules/sipmsgops.so %{_libdir}/opensips/modules/siptrace.so %{_libdir}/opensips/modules/sl.so %{_libdir}/opensips/modules/sms.so %{_libdir}/opensips/modules/speeddial.so %{_libdir}/opensips/modules/sst.so %{_libdir}/opensips/modules/statistics.so %{_libdir}/opensips/modules/stun.so %{_libdir}/opensips/modules/textops.so %{_libdir}/opensips/modules/tm.so %{_libdir}/opensips/modules/uac.so %{_libdir}/opensips/modules/uac_auth.so %{_libdir}/opensips/modules/uac_redirect.so %{_libdir}/opensips/modules/uac_registrant.so %{_libdir}/opensips/modules/uri.so %{_libdir}/opensips/modules/userblacklist.so %{_libdir}/opensips/modules/usrloc.so %{_sbindir}/opensips %{_sbindir}/osipsconsole %{_sbindir}/osipsconfig %{_sbindir}/opensipsctl %{_sbindir}/opensipsdbctl %{_sbindir}/opensipsunix %{_libdir}/opensips/opensipsctl/dbtextdb/dbtextdb.py %{_libdir}/opensips/opensipsctl/dbtextdb/dbtextdb.pyc %{_libdir}/opensips/opensipsctl/dbtextdb/dbtextdb.pyo %{_libdir}/opensips/opensipsctl/opensipsctl.base %{_libdir}/opensips/opensipsctl/opensipsctl.ctlbase %{_libdir}/opensips/opensipsctl/opensipsctl.dbtext %{_libdir}/opensips/opensipsctl/opensipsctl.fifo %{_libdir}/opensips/opensipsctl/opensipsctl.sqlbase %{_libdir}/opensips/opensipsctl/opensipsctl.unixsock %{_libdir}/opensips/opensipsctl/opensipsdbctl.base %{_libdir}/opensips/opensipsctl/opensipsdbctl.dbtext %{_mandir}/man5/* %{_mandir}/man8/* %{_sharedir}/opensips/dbtext/opensips/* %{_sharedir}/opensips/menuconfig_templates/ %files mysql %defattr(-,root,root) %doc %{_docdir}/opensips/README.db_mysql %{_libdir}/opensips/modules/db_mysql.so %{_sbindir}/opensipsdbctl %{_libdir}/opensips/opensipsctl/opensipsdbctl.mysql %{_libdir}/opensips/opensipsctl/opensipsctl.mysql %{_sharedir}/opensips/mysql/* %files postgres %defattr(-,root,root) %doc %{_docdir}/opensips/README.db_postgres %{_libdir}/opensips/modules/db_postgres.so %{_sbindir}/opensipsdbctl %{_libdir}/opensips/opensipsctl/opensipsdbctl.pgsql %{_libdir}/opensips/opensipsctl/opensipsctl.pgsql %{_sharedir}/opensips/postgres/* %files unixodbc %defattr(-,root,root) %doc %{_docdir}/opensips/README.db_unixodbc %{_libdir}/opensips/modules/db_unixodbc.so %files xmpp %defattr(-,root,root) %doc %{_docdir}/opensips/README.jabber %doc %{_docdir}/opensips/README.xmpp %{_libdir}/opensips/modules/jabber.so %{_libdir}/opensips/modules/xmpp.so %files cpl %defattr(-,root,root) %doc %{_docdir}/opensips/README.cpl_c %{_libdir}/opensips/modules/cpl_c.so %files presence %defattr(-,root,root) %doc %{_docdir}/opensips/README.presence %doc %{_docdir}/opensips/README.presence_callinfo %doc %{_docdir}/opensips/README.presence_dialoginfo %doc %{_docdir}/opensips/README.presence_mwi %doc %{_docdir}/opensips/README.presence_xcapdiff %doc %{_docdir}/opensips/README.presence_xml %doc %{_docdir}/opensips/README.pua %doc %{_docdir}/opensips/README.pua_bla %doc %{_docdir}/opensips/README.pua_dialoginfo %doc %{_docdir}/opensips/README.pua_mi %doc %{_docdir}/opensips/README.pua_usrloc %doc %{_docdir}/opensips/README.pua_xmpp %doc %{_docdir}/opensips/README.rls %doc %{_docdir}/opensips/README.xcap %doc %{_docdir}/opensips/README.xcap_client %{_libdir}/opensips/modules/presence.so %{_libdir}/opensips/modules/presence_callinfo.so %{_libdir}/opensips/modules/presence_dialoginfo.so %{_libdir}/opensips/modules/presence_mwi.so %{_libdir}/opensips/modules/presence_xcapdiff.so %{_libdir}/opensips/modules/presence_xml.so %{_libdir}/opensips/modules/pua.so %{_libdir}/opensips/modules/pua_bla.so %{_libdir}/opensips/modules/pua_dialoginfo.so %{_libdir}/opensips/modules/pua_mi.so %{_libdir}/opensips/modules/pua_usrloc.so %{_libdir}/opensips/modules/pua_xmpp.so %{_libdir}/opensips/modules/rls.so %{_libdir}/opensips/modules/xcap.so %{_libdir}/opensips/modules/xcap_client.so %files radius %defattr(-,root,root) %doc %{_docdir}/opensips/README.aaa_radius %doc %{_docdir}/opensips/README.peering %{_libdir}/opensips/modules/aaa_radius.so %{_libdir}/opensips/modules/peering.so %files snmpstats %defattr(-,root,root) %doc %{_docdir}/opensips/README.snmpstats %{_libdir}/opensips/modules/snmpstats.so %{_sharedir}/snmp/mibs/OPENSER-MIB %{_sharedir}/snmp/mibs/OPENSER-REG-MIB %{_sharedir}/snmp/mibs/OPENSER-SIP-COMMON-MIB %{_sharedir}/snmp/mibs/OPENSER-SIP-SERVER-MIB %{_sharedir}/snmp/mibs/OPENSER-TC %files b2bua %defattr(-,root,root) %doc %{_docdir}/opensips/README.b2b_entities %doc %{_docdir}/opensips/README.b2b_logic %doc %{_docdir}/opensips/README.b2b_sca %doc %{_docdir}/opensips/README.call_center %{_libdir}/opensips/modules/b2b_entities.so %{_libdir}/opensips/modules/b2b_logic.so %{_libdir}/opensips/modules/b2b_sca.so %{_libdir}/opensips/modules/call_center.so %files ldap %defattr(-,root,root) %doc %{_docdir}/opensips/README.ldap %doc %{_docdir}/opensips/README.h350 %{_libdir}/opensips/modules/ldap.so %{_libdir}/opensips/modules/h350.so %files identity %defattr(-,root,root) %doc %{_docdir}/opensips/README.identity %{_libdir}/opensips/modules/identity.so %files regex %defattr(-,root,root) %doc %{_docdir}/opensips/README.regex %doc %{_docdir}/opensips/README.dialplan %{_libdir}/opensips/modules/regex.so %{_libdir}/opensips/modules/dialplan.so %files mmgeoip %defattr(-,root,root) %doc %{_docdir}/opensips/README.mmgeoip %{_libdir}/opensips/modules/mmgeoip.so %files json %defattr(-,root,root) %doc %{_docdir}/opensips/README.json %doc %{_docdir}/opensips/README.mi_json %{_libdir}/opensips/modules/json.so %{_libdir}/opensips/modules/mi_json.so %files carrierroute %defattr(-,root,root) %doc %{_docdir}/opensips/README.carrierroute %{_libdir}/opensips/modules/carrierroute.so %files cachedb_memcached %defattr(-,root,root) %doc %{_docdir}/opensips/README.cachedb_memcached %{_libdir}/opensips/modules/cachedb_memcached.so %files event_rabbitmq %defattr(-,root,root) %doc %{_docdir}/opensips/README.event_rabbitmq %{_libdir}/opensips/modules/event_rabbitmq.so %files cachedb_couchbase %defattr(-,root,root) %doc %{_docdir}/opensips/README.cachedb_couchbase %{_libdir}/opensips/modules/cachedb_couchbase.so %files cachedb_redis %defattr(-,root,root) %doc %{_docdir}/opensips/README.cachedb_redis %{_libdir}/opensips/modules/cachedb_redis.so %files db_berkeley %defattr(-,root,root) %doc %{_docdir}/opensips/README.db_berkeley %{_sbindir}/bdb_recover %{_libdir}/opensips/modules/db_berkeley.so %{_libdir}/opensips/opensipsctl/opensipsctl.db_berkeley %{_libdir}/opensips/opensipsctl/opensipsdbctl.db_berkeley %{_sharedir}/opensips/db_berkeley/* %files db_oracle %defattr(-,root,root) %doc %{_docdir}/opensips/README.db_oracle %{_sbindir}/opensips_orasel %{_libdir}/opensips/modules/db_oracle.so %{_libdir}/opensips/opensipsctl/opensipsctl.oracle %{_libdir}/opensips/opensipsctl/opensipsdbctl.oracle %{_libdir}/opensips/opensipsctl/opensipsdbfunc.oracle %{_sharedir}/opensips/oracle/* %files mi_xmlrpc %defattr(-,root,root) %doc %{_docdir}/opensips/README.mi_xmlrpc %{_libdir}/opensips/modules/mi_xmlrpc.so %files mi_xmlrpc_ng %defattr(-,root,root) %doc %{_docdir}/opensips/README.mi_xmlrpc_ng %{_libdir}/opensips/modules/mi_xmlrpc_ng.so %files db_http %defattr(-,root,root) %doc %{_docdir}/opensips/README.db_http %doc %{_docdir}/opensips/README.httpd %doc %{_docdir}/opensips/README.pi_http %{_libdir}/opensips/modules/db_http.so %{_libdir}/opensips/modules/httpd.so %{_libdir}/opensips/modules/pi_http.so %{_sharedir}/opensips/pi_http/* %files cachedb_cassandra %defattr(-,root,root,-) %doc %{_docdir}/opensips/README.cachedb_cassandra %{_libdir}/opensips/modules/cachedb_cassandra.so %files cachedb_mongodb %defattr(-,root,root,-) %doc %{_docdir}/opensips/README.cachedb_mongodb %{_libdir}/opensips/modules/cachedb_mongodb.so %files osp %defattr(-,root,root,-) %doc %{_docdir}/opensips/README.osp %{_libdir}/opensips/modules/osp.so %files perl %defattr(-,root,root,-) %dir %{_libdir}/opensips/perl/OpenSIPS %dir %{_libdir}/opensips/perl/OpenSIPS/LDAPUtils %dir %{_libdir}/opensips/perl/OpenSIPS/Utils %doc %{_docdir}/opensips/README.perl %{_libdir}/opensips/modules/perl.so %{_libdir}/opensips/perl/OpenSIPS.pm %{_libdir}/opensips/perl/OpenSIPS/Constants.pm %{_libdir}/opensips/perl/OpenSIPS/LDAPUtils/LDAPConf.pm %{_libdir}/opensips/perl/OpenSIPS/LDAPUtils/LDAPConnection.pm %{_libdir}/opensips/perl/OpenSIPS/Message.pm %{_libdir}/opensips/perl/OpenSIPS/Utils/PhoneNumbers.pm %{_libdir}/opensips/perl/OpenSIPS/Utils/Debug.pm %files perlvdb %defattr(-,root,root,-) %dir %{_libdir}/opensips/perl/OpenSIPS/VDB %dir %{_libdir}/opensips/perl/OpenSIPS/VDB/Adapter %doc %{_docdir}/opensips/README.db_perlvdb %{_libdir}/opensips/modules/db_perlvdb.so %{_libdir}/opensips/perl/OpenSIPS/VDB.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Adapter/AccountingSIPtrace.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Adapter/Alias.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Adapter/Auth.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Adapter/Describe.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Adapter/Speeddial.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Adapter/TableVersions.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Column.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Pair.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/ReqCond.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Result.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/VTab.pm %{_libdir}/opensips/perl/OpenSIPS/VDB/Value.pm %files sngtc %defattr(-,root,root,-) %doc %{_docdir}/opensips/README.sngtc %{_libdir}/opensips/modules/sngtc.so %files tlsops %defattr(-,root,root,-) %doc %{_docdir}/opensips/README.tlsops %{_libdir}/opensips/modules/tlsops.so %files rest_client %defattr(-,root,root,-) %doc %{_docdir}/opensips/README.rest_client %{_libdir}/opensips/modules/rest_client.so %changelog * Wed Aug 05 2015 Razvan Crainea - Add rest_client specs * Sun Apr 13 2014 Fabrizio Picconi - Fix x64_86 compilation problems * Tue Mar 22.2.24 Fabrizio Picconi - Some new rpm optional modules are added: b2b_sca,call_center,mi_json,script_helper * Tue Aug 06 2013 Fabrizio Picconi - Some new rpm optional modules are added: mi_xmlrpc_ng,sngtc,db_cachedb,mathops,rest_client * Sun Jul 21 2013 Fabrizio Picconi - Some new rpm optional modules are added: cachedb_cassandra,cachedb_mongodb,osp,perl,perlvdb,tlsops * Sun Jun 02.2.23 Fabrizio Picconi - CentOS 6 support Remove rpmbuild warning on BuildPrereq now deprecated for BuildRequires - opensips 1.9.1 support Updated list of excluded modules Updated list of file in rpm of core and modules - Some new rpm optional modules are added: db_http pi_http json cachedb_couchbase cachedb_memcached cachedb_redis event_rabbitmq db_berkeley perl db_perlvdb carrierroute mmgeoip ldap h350 identity regex dialplan Xlog mi_xmlrpc db_oracle * Mon Oct 18 2010 Marc Leurent - Add missing dependencies pcre-devel for compilation - Xlog Module has been merged into core. * Mon Oct 19 2009 Marc Leurent - Add missing config lines for packaging b2b modules - Add missing config lines for packaging peering module * Mon Oct 12 2009 Bogdan Iancu - updated packages for 1.6.0 release - added b2bua package * Fri May 22 2009 Marc Leurent - Add /etc/default/opensips file to allow shared memory configuration - Modify snmpstats Requires to add net-snmp-utils * Tue Mar 24 2009 Marc Leurent - Fix snmpstats packaging - Fix xmpp packaging - Add some missing README and modules .so files in main package * Tue Mar 03 2009 Julian Yap - Update spec to OpenSIPS version 1.5.0. * Thu Feb 26 2009 Julian Yap - Update spec to OpenSIPS version 1.4.4. - Rename database module names. - Rename PUA module. - Fix dependencies. - Minor build fixes. - Additional files and new modules. * Mon Jul 21 2008 Bogdan-Andrei Iancu - First version of the spec file. opensips-2.2.2/packaging/rpm/opensips.spec.SuSE000066400000000000000000000402161300170765700214420ustar00rootroot00000000000000%define name opensips %define ver 2.1 %define rel rc1 %define EXCLUDED_MODULES aaa_radius b2b_entities b2b_logic db_http json memcached jabber cpl_c xmpp rls mi_xmlrpc xcap_client db_mysql db_postgres db_unixodbc db_oracle db_berkeley osp perl snmpstats db_perlvdb peering carrierroute mmgeoip presence presence_xml presence_mwi presence_dialoginfo pua pua_bla pua_mi pua_usrloc pua_xmpp pua_dialoginfo xcap xcap_client ldap h350 identity regex %define MYSQL_MODULES modules/db_mysql %define UNIXODBC_MODULES modules/db_unixodbc %define POSTGRES_MODULES modules/db_postgres %define XMPP_MODULES modules/jabber modules/xmpp %define CPL_MODULES modules/cpl_c %define SNMPSTATS_MODULES modules/snmpstats %define PRESENCE_MODULES modules/presence modules/presence_dialoginfo modules/presence_xml modules/presence_mwi modules/presence_xcapdiff modules/pua modules/pua_bla modules/pua_dialoginfo modules/pua_mi modules/pua_usrloc modules/pua_xmpp modules/rls modules/xcap modules/xcap_client %define RADIUS_MODULES modules/aaa_radius %define B2BUA_MODULES modules/b2b_entities modules/b2b_logic Summary: OpenSIPS, very fast and flexible SIP server Name: %name Version: %ver Release: %rel Packager: Bogdan-Andrei Iancu Copyright: GPL Group: Networking/Daemons Source: http://opensips.org/pub/opensips/stable/%{name}-%{ver}_src.tar.gz Source2: opensips.init.SuSE URL: http://www.opensips.org/ Vendor: opensips.org BuildRoot: /var/tmp/%{name}-%{ver}-root Conflicts: opensips < %ver, opensips-mysql < %ver, opensips-xmpp < %ver, opensips-radius < %ver, opensips-postgres < %ver, opensips-unixodbc < %ver, opensips-presence < %ver BuildPrereq: make flex bison %description OpenSIPS is a very fast and flexible SIP (RFC3261) server. Written entirely in C, OpenSIPS can handle thousands calls per second even on low-budget hardware. A C Shell like scripting language provides full control over the server's behaviour. It's modular architecture allows only required functionality to be loaded. Currently the following modules are available: digest authentication, CPL scripts, instant messaging, MySQL and UNIXODBC support, a presence agent, radius authentication, record routing, an SMS gateway, a jabber gateway, a transaction and dialog module, OSP module, statistics support, registrar and user location. %package mysql Summary: MySQL connectivity for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildPrereq: mysql-devel zlib-devel %description mysql The opensips-mysql package contains MySQL database connectivity that you need to use digest authentication module or persistent user location entries. %package postgres Summary: MPOSTGRES connectivity for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildPrereq: postgresql-devel %description postgres The opensips-postgres package contains Postgres database connectivity that you need to use digest authentication module or persistent user location entries. %package unixodbc Summary: UNIXODBC connectivity for OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildPrereq: unixodbc-dev %description unixodbc The opensips-unixodbc package contains UNIXODBC database connectivity support that is required by other modules with database dependencies. %package xmpp Summary: sip 2 xmpp/jabber message translation support for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildPrereq: expat %description xmpp The opensips-xmpp package contains a sip to jabber message translator. %package cpl Summary: CPL interpreter engine for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildPrereq: libxml2-dev %description cpl The opensips-cpl package contains a CPL interpreter engine. %package presence Summary: sip presence agent support for the OpenSIPS. Group: System Environment/Daemons Requires: opensips = %ver BuildPrereq: libxml2-dev %description presence The opensips-presence package contains a sip Presence Agent. %package radius Summary: opensips radius support for AAA API. Group: System Environment/Daemons Requires: opensips = %ver BuildPrereq: radiusclient %description radius The opensips-radius package contains modules for radius authentication, group membership and uri checking. %prep %setup %build make all skip_modules="%EXCLUDED_MODULES" cfg_target=/%{_sysconfdir}/opensips/ make modules modules="%MYSQL_MODULES" cfg_target=/%{_sysconfdir}/opensips/ make modules modules="%POSTGRES_MODULES" cfg_target=/%{_sysconfdir}/opensips/ make modules modules="%UNIXODBC_MODULES" cfg_target=/%{_sysconfdir}/opensips/ make modules modules="%XMPP_MODULES" cfg_target=/%{_sysconfdir}/opensips/ make modules modules="%CPL_MODULES" cfg_target=/%{_sysconfdir}/opensips/ make modules modules="%PRESENCE_MODULES" cfg_target=/%{_sysconfdir}/opensips/ make modules modules="%RADIUS_MODULES" cfg_target=/%{_sysconfdir}/opensips/ make modules modules="%B2BUA_MODULES" cfg_target=/%{_sysconfdir}/opensips/ %install [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT" make install skip_modules="%EXCLUDED_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ make install-modules modules="%MYSQL_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ make install-modules modules="%POSTGRES_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ make install-modules modules="%UNIXODBC_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ make install-modules modules="%XMPP_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ make install-modules modules="%CPL_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ make install-modules modules="%PRESENCE_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ make install-modules modules="%RADIUS_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ make install-modules modules="%B2BUA_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ cfg_prefix=$RPM_BUILD_ROOT \ cfg_target=/%{_sysconfdir}/opensips/ \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ make install-doc modules="XMPP_MODULES %RADIUS_MODULES %MYSQL_MODULES %POSTGRES_MODULES %UNIXODBC_MODULES %CPL_MODULES %PRESENCE_MODULES %B2BUA_MODULES" \ basedir=$RPM_BUILD_ROOT \ prefix=/usr \ doc_prefix=$RPM_BUILD_ROOT \ doc_dir=/%{_docdir}/opensips/ mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/init.d install -m755 $RPM_SOURCE_DIR/opensips.init.SuSE \ $RPM_BUILD_ROOT/%{_sysconfdir}/init.d/opensips %clean rm -rf "$RPM_BUILD_ROOT" %post sbin/insserv etc/init.d/opensips %preun if [ $1 = 0 ]; then etc/init.d/opensips stop > /dev/null 2>&1 fi %postun sbin/insserv etc/init.d/ %files %defattr(-,root,root) %dir %{_docdir}/opensips %doc %{_docdir}/opensips/AUTHORS %doc %{_docdir}/opensips/INSTALL %doc %{_docdir}/opensips/READE %doc %{_docdir}/opensips/NEWS %doc %{_docdir}/opensips/README-MODULES %doc %{_docdir}/opensips/README.acc %doc %{_docdir}/opensips/README.alias_db %doc %{_docdir}/opensips/README.auth %doc %{_docdir}/opensips/README.auth_aaa %doc %{_docdir}/opensips/README.auth_db %doc %{_docdir}/opensips/README.auth_diameter %doc %{_docdir}/opensips/README.avpops %doc %{_docdir}/opensips/README.benchmark %doc %{_docdir}/opensips/README.call_control %doc %{_docdir}/opensips/README.cfgutils %doc %{_docdir}/opensips/README.closeddial %doc %{_docdir}/opensips/README.db_flatstore %doc %{_docdir}/opensips/README.db_text %doc %{_docdir}/opensips/README.db_virtual %doc %{_docdir}/opensips/README.dialog %doc %{_docdir}/opensips/README.dialplan %doc %{_docdir}/opensips/README.dispatcher %doc %{_docdir}/opensips/README.diversion %doc %{_docdir}/opensips/README.domain %doc %{_docdir}/opensips/README.domainpolicy %doc %{_docdir}/opensips/README.drouting %doc %{_docdir}/opensips/README.enum %doc %{_docdir}/opensips/README.exec %doc %{_docdir}/opensips/README.gflags %doc %{_docdir}/opensips/README.group %doc %{_docdir}/opensips/README.imc %doc %{_docdir}/opensips/README.load_balancer %doc %{_docdir}/opensips/README.localcache %doc %{_docdir}/opensips/README.mangler %doc %{_docdir}/opensips/README.maxfwd %doc %{_docdir}/opensips/README.mediaproxy %doc %{_docdir}/opensips/README.mi_datagram %doc %{_docdir}/opensips/README.mi_fifo %doc %{_docdir}/opensips/README.msilo %doc %{_docdir}/opensips/README.nat_traversal %doc %{_docdir}/opensips/README.nathelper %doc %{_docdir}/opensips/README.options %doc %{_docdir}/opensips/README.path %doc %{_docdir}/opensips/README.permissions %doc %{_docdir}/opensips/README.pike %doc %{_docdir}/opensips/README.qos %doc %{_docdir}/opensips/README.ratelimit %doc %{_docdir}/opensips/README.registrar %doc %{_docdir}/opensips/README.rr %doc %{_docdir}/opensips/README.seas %doc %{_docdir}/opensips/README.signaling %doc %{_docdir}/opensips/README.siptrace %doc %{_docdir}/opensips/README.sl %doc %{_docdir}/opensips/README.sms %doc %{_docdir}/opensips/README.speeddial %doc %{_docdir}/opensips/README.sst %doc %{_docdir}/opensips/README.statistics %doc %{_docdir}/opensips/README.stun %doc %{_docdir}/opensips/README.textops %doc %{_docdir}/opensips/README.tm %doc %{_docdir}/opensips/README.uac %doc %{_docdir}/opensips/README.uac_redirect %doc %{_docdir}/opensips/README.uri %doc %{_docdir}/opensips/README.userblacklist %doc %{_docdir}/opensips/README.usrloc %doc %{_docdir}/opensips/README.xlog %dir %{_sysconfdir}/opensips %config(noreplace) %{_sysconfdir}/opensips/* %config %{_sysconfdir}/init.d/* %dir %{_libdir}/opensips %dir %{_libdir}/opensips/modules %{_libdir}/opensips/modules/acc.so %{_libdir}/opensips/modules/alias_db.so %{_libdir}/opensips/modules/auth.so %{_libdir}/opensips/modules/auth_aaa.so %{_libdir}/opensips/modules/auth_db.so %{_libdir}/opensips/modules/auth_diameter.so %{_libdir}/opensips/modules/avpops.so %{_libdir}/opensips/modules/benchmark.so %{_libdir}/opensips/modules/call_control.so %{_libdir}/opensips/modules/cfgutils.so %{_libdir}/opensips/modules/closeddial.so %{_libdir}/opensips/modules/db_flatstore.so %{_libdir}/opensips/modules/db_text.so %{_libdir}/opensips/modules/db_virtual.so %{_libdir}/opensips/modules/dialog.so %{_libdir}/opensips/modules/dialplan.so %{_libdir}/opensips/modules/dispatcher.so %{_libdir}/opensips/modules/diversion.so %{_libdir}/opensips/modules/domain.so %{_libdir}/opensips/modules/domainpolicy.so %{_libdir}/opensips/modules/drouting.so %{_libdir}/opensips/modules/enum.so %{_libdir}/opensips/modules/exec.so %{_libdir}/opensips/modules/gflags.so %{_libdir}/opensips/modules/group.so %{_libdir}/opensips/modules/imc.so %{_libdir}/opensips/modules/load_balancer.so %{_libdir}/opensips/modules/localcache.so %{_libdir}/opensips/modules/mangler.so %{_libdir}/opensips/modules/maxfwd.so %{_libdir}/opensips/modules/mediaproxy.so %{_libdir}/opensips/modules/mi_datagram.so %{_libdir}/opensips/modules/mi_fifo.so %{_libdir}/opensips/modules/msilo.so %{_libdir}/opensips/modules/nat_traversal.so %{_libdir}/opensips/modules/nathelper.so %{_libdir}/opensips/modules/options.so %{_libdir}/opensips/modules/path.so %{_libdir}/opensips/modules/permissions.so %{_libdir}/opensips/modules/pike.so %{_libdir}/opensips/modules/qos.so %{_libdir}/opensips/modules/ratelimit.so %{_libdir}/opensips/modules/registrar.so %{_libdir}/opensips/modules/rr.so %{_libdir}/opensips/modules/seas.so %{_libdir}/opensips/modules/signaling.so %{_libdir}/opensips/modules/siptrace.so %{_libdir}/opensips/modules/sl.so %{_libdir}/opensips/modules/sms.so %{_libdir}/opensips/modules/speeddial.so %{_libdir}/opensips/modules/sst.so %{_libdir}/opensips/modules/statistics.so %{_libdir}/opensips/modules/stun.so %{_libdir}/opensips/modules/textops.so %{_libdir}/opensips/modules/tm.so %{_libdir}/opensips/modules/uac.so %{_libdir}/opensips/modules/uac_redirect.so %{_libdir}/opensips/modules/uri.so %{_libdir}/opensips/modules/userblacklist.so %{_libdir}/opensips/modules/usrloc.so %{_libdir}/opensips/modules/xlog.so %{_sbindir}/opensips %{_sbindir}/osipsconsole %{_sbindir}/opensipsctl %{_sbindir}/opensipsdbctl %{_libdir}/opensips/opensipsctl/dbtextdb/dbtextdb.py %{_libdir}/opensipsctl/opensipsctl.base %{_libdir}/opensipsctl/opensipsctl.ctlbase %{_libdir}/opensipsctl/opensipsctl.db_berkeley %{_libdir}/opensipsctl/opensipsctl.dbtext %{_libdir}/opensipsctl/opensipsctl.oracle %{_libdir}/opensipsctl/opensipsctl.sqlbase %{_libdir}/opensipsctl/opensipsctl.fifo %{_libdir}/opensipsctl/opensipsctl.unixsock %{_libdir}/opensipsctl/opensipsdbctl.base %{_libdir}/opensipsctl/opensipsdbctl.db_berkeley %{_libdir}/opensipsctl/opensipsdbctl.dbtext %{_libdir}/opensipsctl/opensipsdbctl.oracle %{_mandir}/man5/* %{_mandir}/man8/* %files mysql %defattr(-,root,root) %doc %{_docdir}/opensips/README.mysql %{_libdir}/opensips/modules/db_mysql.so %{_libdir}/opensipsctl/opensipsctl.mysql %{_libdir}/opensipsctl/opensipsdbctl.mysql %files postgres %defattr(-,root,root) %doc %{_docdir}/opensips/README.postgres %{_libdir}/opensips/modules/db_postgres.so %{_libdir}/opensipsctl/opensipsdbctl.pgsql %{_libdir}/opensipsctl/opensipsctl.pgsql %files unixodbc %defattr(-,root,root) %doc %{_docdir}/opensips/README.unixodbc %{_libdir}/opensips/modules/db_unixodbc.so %files xmpp %defattr(-,root,root) %{_libdir}/opensips/modules/jabber.so %{_libdir}/opensips/modules/xmpp.so %doc %{_docdir}/opensips/README.jabber %doc %{_docdir}/opensips/README.xmpp %files cpl %defattr(-,root,root) %{_libdir}/opensips/modules/cpl_c.so %doc %{_docdir}/opensips/README.cpl_c %files presence %defattr(-,root,root) %doc %{_docdir}/opensips/README.presence %doc %{_docdir}/opensips/README.presence_dialoginfo %doc %{_docdir}/opensips/README.presence_mwi %doc %{_docdir}/opensips/README.presence_xcapdiff %doc %{_docdir}/opensips/README.presence_xml %doc %{_docdir}/opensips/README.pua %doc %{_docdir}/opensips/README.pua_bla %doc %{_docdir}/opensips/README.pua_dialoginfo %doc %{_docdir}/opensips/README.pua_mi %doc %{_docdir}/opensips/README.pua_usrloc %doc %{_docdir}/opensips/README.pua_xmpp %doc %{_docdir}/opensips/README.rls %doc %{_docdir}/opensips/README.xcap_client %{_libdir}/opensips/modules/presence.so %{_libdir}/opensips/modules/presence_dialoginfo.so %{_libdir}/opensips/modules/presence_mwi.so %{_libdir}/opensips/modules/presence_xcapdiff.so %{_libdir}/opensips/modules/presence_xml.so %{_libdir}/opensips/modules/pua.so %{_libdir}/opensips/modules/pua_bla.so %{_libdir}/opensips/modules/pua_dialoginfo.so %{_libdir}/opensips/modules/pua_mi.so %{_libdir}/opensips/modules/pua_usrloc.so %{_libdir}/opensips/modules/pua_xmpp.so %{_libdir}/opensips/modules/rls.so %{_libdir}/opensips/modules/xcap_client.so %files radius %defattr(-,root,root) %{_libdir}/opensips/modules/aaa_radius.so %{_libdir}/opensips/modules/peering.so %doc %{_docdir}/opensips/README.aaa_radius %doc %{_docdir}/opensips/README.peering %files b2bua %defattr(-,root,root) %{_libdir}/opensips/modules/b2b_entities.so %{_libdir}/opensips/modules/b2b_logic.so %doc %{_docdir}/opensips/README.b2b_entities %doc %{_docdir}/opensips/README.b2b_logic %changelog * Mon Oct 12.2.29 Bogdan-Andrei Iancu - Update spec to OpenSIPS version 1.6.0. * Mon Mar 23 2009 Bogdan-Andrei Iancu - Update spec to OpenSIPS version 1.5.0. * Mon Jul 21 2008 Bogdan-Andrei Iancu - First version of the spec file. opensips-2.2.2/packaging/solaris/000077500000000000000000000000001300170765700167635ustar00rootroot00000000000000opensips-2.2.2/packaging/solaris/base-Prototype000066400000000000000000000330751300170765700216330ustar00rootroot00000000000000i pkginfo d none /opt 0755 root sys d none /opt/opensips 0755 root root f none /opt/opensips/AUTHORS 0644 root root d none /opt/opensips/dbtext 0755 root root d none /opt/opensips/dbtext/opensips 0755 root root f none /opt/opensips/dbtext/opensips/acc 0644 root root f none /opt/opensips/dbtext/opensips/active_watchers 0644 root root f none /opt/opensips/dbtext/opensips/address 0644 root root f none /opt/opensips/dbtext/opensips/aliases 0644 root root f none /opt/opensips/dbtext/opensips/carrierfailureroute 0644 root root f none /opt/opensips/dbtext/opensips/carrierroute 0644 root root f none /opt/opensips/dbtext/opensips/cpl 0644 root root f none /opt/opensips/dbtext/opensips/dbaliases 0644 root root f none /opt/opensips/dbtext/opensips/dialog 0644 root root f none /opt/opensips/dbtext/opensips/dialplan 0644 root root f none /opt/opensips/dbtext/opensips/dispatcher 0644 root root f none /opt/opensips/dbtext/opensips/domain 0644 root root f none /opt/opensips/dbtext/opensips/domainpolicy 0644 root root f none /opt/opensips/dbtext/opensips/dr_gateways 0644 root root f none /opt/opensips/dbtext/opensips/dr_groups 0644 root root f none /opt/opensips/dbtext/opensips/dr_rules 0644 root root f none /opt/opensips/dbtext/opensips/globalblacklist 0644 root root f none /opt/opensips/dbtext/opensips/grp 0644 root root f none /opt/opensips/dbtext/opensips/imc_members 0644 root root f none /opt/opensips/dbtext/opensips/imc_rooms 0644 root root f none /opt/opensips/dbtext/opensips/load_balancer 0644 root root f none /opt/opensips/dbtext/opensips/location 0644 root root f none /opt/opensips/dbtext/opensips/missed_calls 0644 root root f none /opt/opensips/dbtext/opensips/nh_sockets 0644 root root f none /opt/opensips/dbtext/opensips/presentity 0644 root root f none /opt/opensips/dbtext/opensips/pua 0644 root root f none /opt/opensips/dbtext/opensips/re_grp 0644 root root f none /opt/opensips/dbtext/opensips/rls_presentity 0644 root root f none /opt/opensips/dbtext/opensips/rls_watchers 0644 root root f none /opt/opensips/dbtext/opensips/route_tree 0644 root root f none /opt/opensips/dbtext/opensips/silo 0644 root root f none /opt/opensips/dbtext/opensips/sip_trace 0644 root root f none /opt/opensips/dbtext/opensips/speed_dial 0644 root root f none /opt/opensips/dbtext/opensips/subscriber 0644 root root f none /opt/opensips/dbtext/opensips/trusted 0644 root root f none /opt/opensips/dbtext/opensips/uri 0644 root root f none /opt/opensips/dbtext/opensips/userblacklist 0644 root root f none /opt/opensips/dbtext/opensips/usr_preferences 0644 root root f none /opt/opensips/dbtext/opensips/version 0644 root root f none /opt/opensips/dbtext/opensips/watchers 0644 root root f none /opt/opensips/dbtext/opensips/xcap 0644 root root d none /opt/opensips/etc 0755 root root d none /opt/opensips/etc/opensips 0700 root root f none /opt/opensips/etc/opensips/opensips.cfg 0500 root root f none /opt/opensips/etc/opensips/opensipsctlrc 0644 root root f none /opt/opensips/etc/opensips/osipsconsolerc 0644 root root f none /opt/opensips/INSTALL 0644 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/acc.so 0755 root root f none /opt/opensips/lib64/opensips/modules/alias_db.so 0755 root root f none /opt/opensips/lib64/opensips/modules/auth.so 0755 root root f none /opt/opensips/lib64/opensips/modules/auth_db.so 0755 root root f none /opt/opensips/lib64/opensips/modules/auth_diameter.so 0755 root root f none /opt/opensips/lib64/opensips/modules/avpops.so 0755 root root f none /opt/opensips/lib64/opensips/modules/benchmark.so 0755 root root f none /opt/opensips/lib64/opensips/modules/call_control.so 0755 root root f none /opt/opensips/lib64/opensips/modules/cfgutils.so 0755 root root f none /opt/opensips/lib64/opensips/modules/closeddial.so 0755 root root f none /opt/opensips/lib64/opensips/modules/cpl_c.so 0755 root root f none /opt/opensips/lib64/opensips/modules/db_flatstore.so 0755 root root f none /opt/opensips/lib64/opensips/modules/db_text.so 0755 root root f none /opt/opensips/lib64/opensips/modules/dialog.so 0755 root root f none /opt/opensips/lib64/opensips/modules/dialplan.so 0755 root root f none /opt/opensips/lib64/opensips/modules/dispatcher.so 0755 root root f none /opt/opensips/lib64/opensips/modules/diversion.so 0755 root root f none /opt/opensips/lib64/opensips/modules/domain.so 0755 root root f none /opt/opensips/lib64/opensips/modules/domainpolicy.so 0755 root root f none /opt/opensips/lib64/opensips/modules/drouting.so 0755 root root f none /opt/opensips/lib64/opensips/modules/enum.so 0755 root root f none /opt/opensips/lib64/opensips/modules/exec.so 0755 root root f none /opt/opensips/lib64/opensips/modules/gflags.so 0755 root root f none /opt/opensips/lib64/opensips/modules/group.so 0755 root root f none /opt/opensips/lib64/opensips/modules/h350.so 0755 root root f none /opt/opensips/lib64/opensips/modules/identity.so 0755 root root f none /opt/opensips/lib64/opensips/modules/imc.so 0755 root root f none /opt/opensips/lib64/opensips/modules/load_balancer.so 0755 root root f none /opt/opensips/lib64/opensips/modules/localcache.so 0755 root root f none /opt/opensips/lib64/opensips/modules/mangler.so 0755 root root f none /opt/opensips/lib64/opensips/modules/maxfwd.so 0755 root root f none /opt/opensips/lib64/opensips/modules/mediaproxy.so 0755 root root f none /opt/opensips/lib64/opensips/modules/mi_datagram.so 0755 root root f none /opt/opensips/lib64/opensips/modules/mi_fifo.so 0755 root root f none /opt/opensips/lib64/opensips/modules/msilo.so 0755 root root f none /opt/opensips/lib64/opensips/modules/nathelper.so 0755 root root f none /opt/opensips/lib64/opensips/modules/nat_traversal.so 0755 root root f none /opt/opensips/lib64/opensips/modules/options.so 0755 root root f none /opt/opensips/lib64/opensips/modules/path.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pike.so 0755 root root f none /opt/opensips/lib64/opensips/modules/permissions.so 0755 root root f none /opt/opensips/lib64/opensips/modules/presence.so 0755 root root f none /opt/opensips/lib64/opensips/modules/presence_dialoginfo.so 0755 root root f none /opt/opensips/lib64/opensips/modules/presence_mwi.so 0755 root root f none /opt/opensips/lib64/opensips/modules/presence_xcapdiff.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_bla.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_dialoginfo.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_mi.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_usrloc.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_xmpp.so 0755 root root f none /opt/opensips/lib64/opensips/modules/qos.so 0755 root root f none /opt/opensips/lib64/opensips/modules/ratelimit.so 0755 root root f none /opt/opensips/lib64/opensips/modules/registrar.so 0755 root root f none /opt/opensips/lib64/opensips/modules/rls.so 0755 root root f none /opt/opensips/lib64/opensips/modules/rr.so 0755 root root f none /opt/opensips/lib64/opensips/modules/seas.so 0755 root root f none /opt/opensips/lib64/opensips/modules/signaling.so 0755 root root f none /opt/opensips/lib64/opensips/modules/siptrace.so 0755 root root f none /opt/opensips/lib64/opensips/modules/sl.so 0755 root root f none /opt/opensips/lib64/opensips/modules/sms.so 0755 root root f none /opt/opensips/lib64/opensips/modules/speeddial.so 0755 root root f none /opt/opensips/lib64/opensips/modules/sst.so 0755 root root f none /opt/opensips/lib64/opensips/modules/statistics.so 0755 root root f none /opt/opensips/lib64/opensips/modules/textops.so 0755 root root f none /opt/opensips/lib64/opensips/modules/tm.so 0755 root root f none /opt/opensips/lib64/opensips/modules/uac.so 0755 root root f none /opt/opensips/lib64/opensips/modules/uac_redirect.so 0755 root root f none /opt/opensips/lib64/opensips/modules/uri.so 0755 root root f none /opt/opensips/lib64/opensips/modules/uri_db.so 0755 root root f none /opt/opensips/lib64/opensips/modules/userblacklist.so 0755 root root f none /opt/opensips/lib64/opensips/modules/usrloc.so 0755 root root f none /opt/opensips/lib64/opensips/modules/xcap_client.so 0755 root root f none /opt/opensips/lib64/opensips/modules/xlog.so 0755 root root d none /opt/opensips/lib64/opensips/opensipsctl 0755 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.base 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.ctlbase 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.fifo 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.unixsock 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.sqlbase 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsdbctl.base 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.dbtext 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsdbctl.dbtext 0644 root root d none /opt/opensips/lib64/opensips/opensipsctl/dbtextdb 0755 root root f none /opt/opensips/lib64/opensips/opensipsctl/dbtextdb/dbtextdb.py 0755 root root d none /opt/opensips/man5 0755 root root f none /opt/opensips/man5/opensips.cfg.5 0644 root root d none /opt/opensips/man8 0755 root root f none /opt/opensips/man8/opensips.8 0644 root root f none /opt/opensips/man8/opensipsctl.8 0644 root root f none /opt/opensips/man8/opensipsunix.8 0644 root root f none /opt/opensips/NEWS 0644 root root f none /opt/opensips/README 0644 root root f none /opt/opensips/README-MODULES 0644 root root f none /opt/opensips/README.acc 0644 root root f none /opt/opensips/README.alias_db 0644 root root f none /opt/opensips/README.auth 0644 root root f none /opt/opensips/README.auth_db 0644 root root f none /opt/opensips/README.auth_diameter 0644 root root f none /opt/opensips/README.avpops 0644 root root f none /opt/opensips/README.benchmark 0644 root root f none /opt/opensips/README.call_control 0644 root root f none /opt/opensips/README.cfgutils 0644 root root f none /opt/opensips/README.closeddial 0644 root root f none /opt/opensips/README.cpl_c 0644 root root f none /opt/opensips/README.db_flatstore 0644 root root f none /opt/opensips/README.db_text 0644 root root f none /opt/opensips/README.dialog 0644 root root f none /opt/opensips/README.dialplan 0644 root root f none /opt/opensips/README.dispatcher 0644 root root f none /opt/opensips/README.diversion 0644 root root f none /opt/opensips/README.domain 0644 root root f none /opt/opensips/README.domainpolicy 0644 root root f none /opt/opensips/README.drouting 0644 root root f none /opt/opensips/README.enum 0644 root root f none /opt/opensips/README.exec 0644 root root f none /opt/opensips/README.gflags 0644 root root f none /opt/opensips/README.group 0644 root root f none /opt/opensips/README.h350 0644 root root f none /opt/opensips/README.identity 0644 root root f none /opt/opensips/README.imc 0644 root root f none /opt/opensips/README.load_balancer 0644 root root f none /opt/opensips/README.localcache 0644 root root f none /opt/opensips/README.mangler 0644 root root f none /opt/opensips/README.maxfwd 0644 root root f none /opt/opensips/README.mediaproxy 0644 root root f none /opt/opensips/README.mi_datagram 0644 root root f none /opt/opensips/README.mi_fifo 0644 root root f none /opt/opensips/README.msilo 0644 root root f none /opt/opensips/README.nathelper 0644 root root f none /opt/opensips/README.nat_traversal 0644 root root f none /opt/opensips/README.options 0644 root root f none /opt/opensips/README.path 0644 root root f none /opt/opensips/README.permissions 0644 root root f none /opt/opensips/README.pike 0644 root root f none /opt/opensips/README.presence 0644 root root f none /opt/opensips/README.presence_dialoginfo 0644 root root f none /opt/opensips/README.presence_mwi 0644 root root f none /opt/opensips/README.presence_xcapdiff 0644 root root f none /opt/opensips/README.pua 0644 root root f none /opt/opensips/README.pua_bla 0644 root root f none /opt/opensips/README.pua_dialoginfo 0644 root root f none /opt/opensips/README.pua_mi 0644 root root f none /opt/opensips/README.pua_usrloc 0644 root root f none /opt/opensips/README.pua_xmpp 0644 root root f none /opt/opensips/README.qos 0644 root root f none /opt/opensips/README.ratelimit 0644 root root f none /opt/opensips/README.registrar 0644 root root f none /opt/opensips/README.rls 0644 root root f none /opt/opensips/README.rr 0644 root root f none /opt/opensips/README.seas 0644 root root f none /opt/opensips/README.signaling 0644 root root f none /opt/opensips/README.siptrace 0644 root root f none /opt/opensips/README.sl 0644 root root f none /opt/opensips/README.sms 0644 root root f none /opt/opensips/README.speeddial 0644 root root f none /opt/opensips/README.sst 0644 root root f none /opt/opensips/README.statistics 0644 root root f none /opt/opensips/README.textops 0644 root root f none /opt/opensips/README.tm 0644 root root f none /opt/opensips/README.uac 0644 root root f none /opt/opensips/README.uac_redirect 0644 root root f none /opt/opensips/README.uri 0644 root root f none /opt/opensips/README.uri_db 0644 root root f none /opt/opensips/README.userblacklist 0644 root root f none /opt/opensips/README.usrloc 0644 root root f none /opt/opensips/README.xcap_client 0644 root root f none /opt/opensips/README.xlog 0644 root root d none /opt/opensips/sbin 0755 root root f none /opt/opensips/sbin/opensips 0755 root root f none /opt/opensips/sbin/opensipsctl 0755 root root f none /opt/opensips/sbin/opensipsdbctl 0755 root root f none /opt/opensips/sbin/opensipsunix 0755 root root f none /opt/opensips/sbin/osipsconsole 0755 root root opensips-2.2.2/packaging/solaris/base-pkginfo000066400000000000000000000004261300170765700212550ustar00rootroot00000000000000PKG="OpenSIPS-base-dbg" NAME="Programmable SIP Server Base Install - Debugging Symbols" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="bogdan@opensips.org" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/berkeley-Prototype000066400000000000000000000072761300170765700225270ustar00rootroot00000000000000i pkginfo i preinstall i postinstall d none /opt 0755 root sys d none /opt/opensips 0755 root root d none /opt/opensips/db_berkeley 0755 root root d none /opt/opensips/db_berkeley/opensips 0755 root root f none /opt/opensips/db_berkeley/opensips/acc 0644 root root f none /opt/opensips/db_berkeley/opensips/active_watchers 0644 root root f none /opt/opensips/db_berkeley/opensips/address 0644 root root f none /opt/opensips/db_berkeley/opensips/aliases 0644 root root f none /opt/opensips/db_berkeley/opensips/carrierfailureroute 0644 root root f none /opt/opensips/db_berkeley/opensips/carrierroute 0644 root root f none /opt/opensips/db_berkeley/opensips/cpl 0644 root root f none /opt/opensips/db_berkeley/opensips/dbaliases 0644 root root f none /opt/opensips/db_berkeley/opensips/dialog 0644 root root f none /opt/opensips/db_berkeley/opensips/dialplan 0644 root root f none /opt/opensips/db_berkeley/opensips/dispatcher 0644 root root f none /opt/opensips/db_berkeley/opensips/domain 0644 root root f none /opt/opensips/db_berkeley/opensips/domainpolicy 0644 root root f none /opt/opensips/db_berkeley/opensips/dr_gateways 0644 root root f none /opt/opensips/db_berkeley/opensips/dr_groups 0644 root root f none /opt/opensips/db_berkeley/opensips/dr_rules 0644 root root f none /opt/opensips/db_berkeley/opensips/globalblacklist 0644 root root f none /opt/opensips/db_berkeley/opensips/grp 0644 root root f none /opt/opensips/db_berkeley/opensips/imc_members 0644 root root f none /opt/opensips/db_berkeley/opensips/imc_rooms 0644 root root f none /opt/opensips/db_berkeley/opensips/load_balancer 0644 root root f none /opt/opensips/db_berkeley/opensips/location 0644 root root f none /opt/opensips/db_berkeley/opensips/missed_calls 0644 root root f none /opt/opensips/db_berkeley/opensips/nh_sockets 0644 root root f none /opt/opensips/db_berkeley/opensips/presentity 0644 root root f none /opt/opensips/db_berkeley/opensips/pua 0644 root root f none /opt/opensips/db_berkeley/opensips/re_grp 0644 root root f none /opt/opensips/db_berkeley/opensips/rls_presentity 0644 root root f none /opt/opensips/db_berkeley/opensips/rls_watchers 0644 root root f none /opt/opensips/db_berkeley/opensips/route_tree 0644 root root f none /opt/opensips/db_berkeley/opensips/silo 0644 root root f none /opt/opensips/db_berkeley/opensips/sip_trace 0644 root root f none /opt/opensips/db_berkeley/opensips/speed_dial 0644 root root f none /opt/opensips/db_berkeley/opensips/subscriber 0644 root root f none /opt/opensips/db_berkeley/opensips/trusted 0644 root root f none /opt/opensips/db_berkeley/opensips/uri 0644 root root f none /opt/opensips/db_berkeley/opensips/userblacklist 0644 root root f none /opt/opensips/db_berkeley/opensips/usr_preferences 0644 root root f none /opt/opensips/db_berkeley/opensips/version 0644 root root f none /opt/opensips/db_berkeley/opensips/watchers 0644 root root f none /opt/opensips/db_berkeley/opensips/xcap 0644 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/db_berkeley.so 0755 root root d none /opt/opensips/lib64/opensips/opensipsctl 0755 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsdbctl.base 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.db_berkeley 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsdbctl.db_berkeley 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.dbtext 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsdbctl.dbtext 0644 root root f none /opt/opensips/README.db_berkeley 0644 root root f none /opt/opensips/sbin/bdb_recover 0755 root root opensips-2.2.2/packaging/solaris/berkeley-pkginfo000066400000000000000000000004441300170765700221450ustar00rootroot00000000000000PKG="OpenSIPS-berkeley-dbg" NAME="Programmable SIP Server Berkeley Database Support - Debugging Symbols" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/berkeley-postinstall000066400000000000000000000002511300170765700230600ustar00rootroot00000000000000#!/sbin/sh TMPLIST="/tmp/.opensipsbdb" echo "Set LD_LIBRARY_PATH to one of these directories before starting OpenSIPS:" echo " " cat $TMPLIST rm -f $TMPLIST exit 0; opensips-2.2.2/packaging/solaris/berkeley-preinstall000066400000000000000000000007651300170765700226730ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-berkeley BASE="OpenSIPS-base" LIBBDB="libdb-4.7.so" TMPLIST="/tmp/.opensipsbdb" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-berkeley depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBBDB > $TMPLIST 2>&1 lines=`wc -l $TMPLIST` if [ $lines = "0" ] then echo "OpenSIPS-berkeley depends on DB Berkeley Libraries, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/packaging/solaris/carrierroute-Prototype000066400000000000000000000005761300170765700234270ustar00rootroot00000000000000i pkginfo i preinstall i postinstall d none /opt 0755 root sys d none /opt/opensips 0755 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/carrierroute.so 0755 root root f none /opt/opensips/README.carrierroute 0644 root root opensips-2.2.2/packaging/solaris/carrierroute-pkginfo000066400000000000000000000004131300170765700230450ustar00rootroot00000000000000PKG="OpenSIPS-carrierroute" NAME="Programmable SIP Server carrierroute Support" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/carrierroute-postinstall000066400000000000000000000002551300170765700237700ustar00rootroot00000000000000#!/sbin/sh TMPLIST="/tmp/.opensipsconfuse" echo "Set LD_LIBRARY_PATH to one of these directories before starting OpenSIPS:" echo " " cat $TMPLIST rm -f $TMPLIST exit 0; opensips-2.2.2/packaging/solaris/carrierroute-preinstall000066400000000000000000000010121300170765700235610ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-carrierroute BASE="OpenSIPS-base" LIBCONFUSE="libconfuse.a" TMPLIST="/tmp/.opensipsconfuse" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-carrierroute depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBCONFUSE > $TMPLIST 2>&1 lines=`wc -l $TMPLIST` if [ $lines = "0" ] then echo "OpenSIPS-carrierroute depends on libConfuse library, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/packaging/solaris/identity-Prototype000066400000000000000000000005661300170765700225510ustar00rootroot00000000000000i pkginfo i preinstall i postinstall d none /opt 0755 root sys d none /opt/opensips 0755 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/identity.so 0755 root root f none /opt/opensips/README.identity 0644 root root opensips-2.2.2/packaging/solaris/identity-pkginfo000066400000000000000000000004021300170765700221660ustar00rootroot00000000000000PKG="OpenSIPS-identity" NAME="Programmable SIP Server Identity Module" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/identity-postinstall000066400000000000000000000002511300170765700231070ustar00rootroot00000000000000#!/sbin/sh TMPLIST="/tmp/.opensipsssl" echo "Set LD_LIBRARY_PATH to one of these directories before starting OpenSIPS:" echo " " cat $TMPLIST rm -f $TMPLIST exit 0; opensips-2.2.2/packaging/solaris/identity-preinstall000066400000000000000000000007531300170765700227170ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-identity BASE="OpenSIPS-base" LIBSSL="libssl.a" TMPLIST="/tmp/.opensipsssl" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-identity depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBSSL > $TMPLIST 2>&1 lines=`wc -l $TMPLIST` if [ $lines = "0" ] then echo "OpenSIPS-xmlrpc depends on OpenSSL Libraries, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/packaging/solaris/ldap-Prototype000066400000000000000000000006321300170765700216320ustar00rootroot00000000000000i pkginfo i preinstall i postinstall d none /opt 0755 root sys d none /opt/opensips 0755 root root f none /opt/opensips/INSTALL 0644 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/ldap.so 0755 root root f none /opt/opensips/README.ldap 0644 root root opensips-2.2.2/packaging/solaris/ldap-pkginfo000066400000000000000000000003731300170765700212640ustar00rootroot00000000000000PKG="OpenSIPS-ldap" NAME="Programmable SIP Server LDAP Support" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/ldap-postinstall000066400000000000000000000002511300170765700221760ustar00rootroot00000000000000#!/sbin/sh TMPLIST="/tmp/.opensipsldap" echo "Set LD_LIBRARY_PATH to one of these directories before starting OpenSIPS:" echo " " cat $TMPLIST rm -f $TMPLIST exit 0; opensips-2.2.2/packaging/solaris/ldap-preinstall000066400000000000000000000007671300170765700220130ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-ldap BASE="OpenSIPS-base" LIBLDAP="libldap-2.4.so.2" TMPLIST="/tmp/.opensipsldap" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-ldap depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBLDAP > $TMPLIST 2>&1 lines=`wc -l /tmp/.opensipsldap` if [ $lines = "0" ] then echo "OpenSIPS-ldap depends on OpenLDAP Libraries, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/packaging/solaris/mmgeoip-Prototype000066400000000000000000000005471300170765700223540ustar00rootroot00000000000000i pkginfo i preinstall d none /opt 0755 root root d none /opt/opensips 0755 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/mmgeoip.so 0755 root root f none /opt/opensips/README.mmgeoip 0644 root root opensips-2.2.2/packaging/solaris/mmgeoip-pkginfo000066400000000000000000000004101300170765700217710ustar00rootroot00000000000000PKG="OpenSIPS-geoip" NAME="Programmable SIP Server Address Location Support" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/mmgeoip-preinstall000066400000000000000000000007701300170765700225220ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-geoip BASE="OpenSIPS-base" LIBGEOIP="libGeoIP.a" TMPLIST="/tmp/.opensipgeoip" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-geoip depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBGEOIP > $TMPLIST 2>&1 lines=`wc -l $TMPLIST` if [ $lines = "0" ] then echo "OpenSIPS-carrierroute depends on Max Mind Geo IP library, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/packaging/solaris/mysql-Prototype000066400000000000000000000045561300170765700220700ustar00rootroot00000000000000i pkginfo i preinstall i postinstall d none /opt 0755 root sys d none /opt/opensips 0700 root root f none /opt/opensips/INSTALL 0644 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/db_mysql.so 0755 root root d none /opt/opensips/lib64/opensips/opensipsctl 0755 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.mysql 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsdbctl.mysql 0644 root root d none /opt/opensips/mysql 0755 root root f none /opt/opensips/mysql/acc-create.sql 0644 root root f none /opt/opensips/mysql/alias_db-create.sql 0644 root root f none /opt/opensips/mysql/auth_db-create.sql 0644 root root f none /opt/opensips/mysql/avpops-create.sql 0644 root root f none /opt/opensips/mysql/carrierroute-create.sql 0644 root root f none /opt/opensips/mysql/closeddial-create.sql 0644 root root f none /opt/opensips/mysql/cpl_create.sql 0644 root root f none /opt/opensips/mysql/dialog-create.sql 0644 root root f none /opt/opensips/mysql/dialplan-create.sql 0644 root root f none /opt/opensips/mysql/dispatcher-create.sql 0644 root root f none /opt/opensips/mysql/domain-create.sql 0644 root root f none /opt/opensips/mysql/domainpolicy-create.sql 0644 root root f none /opt/opensips/mysql/drouting-create.sql 0644 root root f none /opt/opensips/mysql/group-create.sql 0644 root root f none /opt/opensips/mysql/imc-create.sql 0644 root root f none /opt/opensips/mysql/load_balancer-create.sql 0644 root root f none /opt/opensips/mysql/msilo-create.sql 0644 root root f none /opt/opensips/mysql/nathelper-create.sql 0644 root root f none /opt/opensips/mysql/permissions-create.sql 0644 root root f none /opt/opensips/mysql/presence-create.sql 0644 root root f none /opt/opensips/mysql/registrar-create.sql 0644 root root f none /opt/opensips/mysql/rls-create.sql 0644 root root f none /opt/opensips/mysql/siptrace-create.sql 0644 root root f none /opt/opensips/mysql/speeddial-create.sql 0644 root root f none /opt/opensips/mysql/standard-create.sql 0644 root root f none /opt/opensips/mysql/uri_db-create.sql 0644 root root f none /opt/opensips/mysql/userblacklist-create.sql 0644 root root f none /opt/opensips/mysql/usrloc-create.sql 0644 root root f none /opt/opensips/README.db_mysql 0644 root root opensips-2.2.2/packaging/solaris/mysql-pkginfo000066400000000000000000000003751300170765700215130ustar00rootroot00000000000000PKG="OpenSIPS-mysql" NAME="Programmable SIP Server MySQL Support" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/mysql-postinstall000066400000000000000000000002531300170765700224250ustar00rootroot00000000000000#!/sbin/sh TMPLIST="/tmp/.opensipsmysql" echo "Set LD_LIBRARY_PATH to one of these directories before starting OpenSIPS:" echo " " cat $TMPLIST rm -f $TMPLIST exit 0; opensips-2.2.2/packaging/solaris/mysql-preinstall000066400000000000000000000007731300170765700222350ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-mysql BASE="OpenSIPS-base" LIBMYSQL="libmysqlclient.so.15" TMPLIST="/tmp/.opensipsmysql" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-mysql depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBMYSQL > $TMPLIST 2>&1 lines=`wc -l $TMPLIST` if [ $lines = "0" ] then echo "OpenSIPS-mysql depends on MySQL Client Libraries, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/packaging/solaris/perl-Prototype000066400000000000000000000007221300170765700216540ustar00rootroot00000000000000i pkginfo d none /opt 0755 root sys d none /opt/opensips 0755 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/perl.so 0755 root root f none /opt/opensips/lib64/opensips/modules/db_perlvdb.so 0755 root root f none /opt/opensips/README.perl 0644 root root f none /opt/opensips/README.db_perlvdb 0644 root root opensips-2.2.2/packaging/solaris/perl-pkginfo000066400000000000000000000003731300170765700213060ustar00rootroot00000000000000PKG="OpenSIPS-perl" NAME="Programmable SIP Server PERL Support" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/pgsql-Prototype000066400000000000000000000044351300170765700220450ustar00rootroot00000000000000i pkginfo i preinstall i postinstall d none /opt 0755 root sys d none /opt/opensips 0755 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/db_postgres.so 0755 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.pgsql 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsdbctl.pgsql 0644 root root d none /opt/opensips/postgres 0755 root root f none /opt/opensips/postgres/acc-create.sql 0644 root root f none /opt/opensips/postgres/alias_db-create.sql 0644 root root f none /opt/opensips/postgres/auth_db-create.sql 0644 root root f none /opt/opensips/postgres/avpops-create.sql 0644 root root f none /opt/opensips/postgres/carrierroute-create.sql 0644 root root f none /opt/opensips/postgres/cpl_create.sql 0644 root root f none /opt/opensips/postgres/dialog-create.sql 0644 root root f none /opt/opensips/postgres/dialplan-create.sql 0644 root root f none /opt/opensips/postgres/dispatcher-create.sql 0644 root root f none /opt/opensips/postgres/domain-create.sql 0644 root root f none /opt/opensips/postgres/domainpolicy-create.sql 0644 root root f none /opt/opensips/postgres/drouting-create.sql 0644 root root f none /opt/opensips/postgres/group-create.sql 0644 root root f none /opt/opensips/postgres/imc-create.sql 0644 root root f none /opt/opensips/postgres/load_balancer-create.sql 0644 root root f none /opt/opensips/postgres/msilo-create.sql 0644 root root f none /opt/opensips/postgres/nathelper-create.sql 0644 root root f none /opt/opensips/postgres/permissions-create.sql 0644 root root f none /opt/opensips/postgres/presence-create.sql 0644 root root f none /opt/opensips/postgres/registrar-create.sql 0644 root root f none /opt/opensips/postgres/rls-create.sql 0644 root root f none /opt/opensips/postgres/siptrace-create.sql 0644 root root f none /opt/opensips/postgres/speeddial-create.sql 0644 root root f none /opt/opensips/postgres/standard-create.sql 0644 root root f none /opt/opensips/postgres/uri_db-create.sql 0644 root root f none /opt/opensips/postgres/userblacklist-create.sql 0644 root root f none /opt/opensips/postgres/usrloc-create.sql 0644 root root f none /opt/opensips/README.db_postgres 0644 root root opensips-2.2.2/packaging/solaris/pgsql-pkginfo000066400000000000000000000004021300170765700214630ustar00rootroot00000000000000PKG="OpenSIPS-pgsql" NAME="Programmable SIP Server PostgreSQL Support" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/pgsql-postinstall000066400000000000000000000002531300170765700224060ustar00rootroot00000000000000#!/sbin/sh TMPLIST="/tmp/.opensipspgsql" echo "Set LD_LIBRARY_PATH to one of these directories before starting OpenSIPS:" echo " " cat $TMPLIST rm -f $TMPLIST exit 0; opensips-2.2.2/packaging/solaris/pgsql-preinstall000066400000000000000000000010311300170765700222020ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-pgsql BASE="OpenSIPS-base" LIBPGSQL="libpq.so.5" TMPLIST="/tmp/.opensipspgsql" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-pgsql depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBPGSQL > $TMPLIST 2>&1 lines=`wc -l $TMPLIST` if [ $lines = "0" ] then echo "OpenSIPS-pgsql depends on PostgreSQL Client Libraries, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/packaging/solaris/pkginfo000066400000000000000000000003101300170765700203350ustar00rootroot00000000000000PKG=OpenSIPS ARCH=sparc VERSION="2.2.0" CATEGORY=application EMAIL=bogdan@opensips.org NAME= OpenSIPS is a very fast and flexible SIP (RFC3261) server SUNW_ISA=sparcv9 BASEDIR=/usr/local CLASSES=none opensips-2.2.2/packaging/solaris/prototype000066400000000000000000000134651300170765700207640ustar00rootroot00000000000000# $Id$ i pkginfo=pkginfo d none etc 0755 bin bin d none etc/opensips 0755 bin bin e none etc/opensips/opensips.cfg 0644 bin bin e none etc/opensips/opensipsctlrc 0644 bin bin d none sbin 0755 bin bin f none sbin/opensips 0755 bin bin f none sbin/opensipsctl 0755 bin bin f none sbin/opensips_mysql.sh 0755 bin bin d none lib 0755 bin bin d none lib/opensips 2755 bin bin d none lib/opensips/modules 2755 bin bin f none lib/opensips/modules/acc.so 0755 bin bin f none lib/opensips/modules/alias_db.so 0755 bin bin f none lib/opensips/modules/auth.so 0755 bin bin f none lib/opensips/modules/auth_db.so 0755 bin bin f none lib/opensips/modules/auth_diameter.so 0755 bin bin f none lib/opensips/modules/avpops.so 0755 bin bin f none lib/opensips/modules/dbtext.so 0755 bin bin f none lib/opensips/modules/dialog.so 0755 bin bin f none lib/opensips/modules/dispatcher.so 0755 bin bin f none lib/opensips/modules/diversion.so 0755 bin bin f none lib/opensips/modules/domain.so 0755 bin bin f none lib/opensips/modules/domainpolicy.so 0755 bin bin f none lib/opensips/modules/enum.so 0755 bin bin f none lib/opensips/modules/exec.so 0755 bin bin f none lib/opensips/modules/flatstore.so 0755 bin bin f none lib/opensips/modules/gflags.so 0755 bin bin f none lib/opensips/modules/group.so 0755 bin bin f none lib/opensips/modules/jabber.so 0755 bin bin f none lib/opensips/modules/mangler.so 0755 bin bin f none lib/opensips/modules/maxfwd.so 0755 bin bin f none lib/opensips/modules/mediaproxy.so 0755 bin bin f none lib/opensips/modules/mi_fifo.so 0755 bin bin f none lib/opensips/modules/msilo.so 0755 bin bin f none lib/opensips/modules/mysql.so 0755 bin bin f none lib/opensips/modules/nathelper.so 0755 bin bin f none lib/opensips/modules/options.so 0755 bin bin f none lib/opensips/modules/path.so 0755 bin bin f none lib/opensips/modules/permissions.so 0755 bin bin f none lib/opensips/modules/pike.so 0755 bin bin f none lib/opensips/modules/registrar.so 0755 bin bin f none lib/opensips/modules/rr.so 0755 bin bin f none lib/opensips/modules/seas.so 0755 bin bin f none lib/opensips/modules/siptrace.so 0755 bin bin f none lib/opensips/modules/sl.so 0755 bin bin f none lib/opensips/modules/sms.so 0755 bin bin f none lib/opensips/modules/speeddial.so 0755 bin bin f none lib/opensips/modules/sst.so 0755 bin bin f none lib/opensips/modules/statistics.so 0755 bin bin f none lib/opensips/modules/textops.so 0755 bin bin f none lib/opensips/modules/tm.so 0755 bin bin f none lib/opensips/modules/uac.so 0755 bin bin f none lib/opensips/modules/uac_redirect.so 0755 bin bin f none lib/opensips/modules/uri.so 0755 bin bin f none lib/opensips/modules/uri_db.so 0755 bin bin f none lib/opensips/modules/usrloc.so 0755 bin bin f none lib/opensips/modules/xlog.so 0755 bin bin f none lib/opensips/modules/xmpp.so 0755 bin bin d none lib/opensipsctl 0644 bin bin f none lib/opensipsctl/opensipsctl.base 0644 bin bin f none lib/opensipsctl/opensipsctl.ctlbase 0644 bin bin f none lib/opensipsctl/opensipsctl.sqlbase 0644 bin bin f none lib/opensipsctl/opensipsctl.fifo 0644 bin bin f none lib/opensipsctl/opensipsctl.unixsock 0644 bin bin f none lib/opensipsctl/opensipsctl.mysql 0644 bin bin d none doc 0755 bin bin d none doc/opensips 0755 bin bin f none doc/opensips/README 0644 bin bin f none doc/opensips/INSTALL 0644 bin bin f none doc/opensips/README-MODULES 0644 bin bin f none doc/opensips/AUTHORS 0644 bin bin f none doc/opensips/NEWS 0644 bin bin f none doc/opensips/README.acc 0644 bin bin f none doc/opensips/README.alias_db 0644 bin bin f none doc/opensips/README.auth 0644 bin bin f none doc/opensips/README.auth_db 0644 bin bin f none doc/opensips/README.auth_diameter 0644 bin bin f none doc/opensips/README.avpops 0644 bin bin f none doc/opensips/README.dbtext 0644 bin bin f none doc/opensips/README.dialog 0644 bin bin f none doc/opensips/README.dispatcher 0644 bin bin f none doc/opensips/README.diversion 0644 bin bin f none doc/opensips/README.domain 0644 bin bin f none doc/opensips/README.domainpolicy 0644 bin bin f none doc/opensips/README.enum 0644 bin bin f none doc/opensips/README.exec 0644 bin bin f none doc/opensips/README.flatstore 0644 bin bin f none doc/opensips/README.gflags 0644 bin bin f none doc/opensips/README.group 0644 bin bin f none doc/opensips/README.jabber 0644 bin bin f none doc/opensips/README.mangler 0644 bin bin f none doc/opensips/README.maxfwd 0644 bin bin f none doc/opensips/README.mediaproxy 0644 bin bin f none doc/opensips/README.mi_fifo 0644 bin bin f none doc/opensips/README.msilo 0644 bin bin f none doc/opensips/README.mysql 0644 bin bin f none doc/opensips/README.nathelper 0644 bin bin f none doc/opensips/README.options 0644 bin bin f none doc/opensips/README.path 0644 bin bin f none doc/opensips/README.permissions 0644 bin bin f none doc/opensips/README.pike 0644 bin bin f none doc/opensips/README.registrar 0644 bin bin f none doc/opensips/README.rr 0644 bin bin f none doc/opensips/README.siptrace 0644 bin bin f none doc/opensips/README.seas 0644 bin bin f none doc/opensips/README.sl 0644 bin bin f none doc/opensips/README.sms 0644 bin bin f none doc/opensips/README.speeddial 0644 bin bin f none doc/opensips/README.statistics 0644 bin bin f none doc/opensips/README.sst 0644 bin bin f none doc/opensips/README.textops 0644 bin bin f none doc/opensips/README.tm 0644 bin bin f none doc/opensips/README.uac 0644 bin bin f none doc/opensips/README.uac_redirect 0644 bin bin f none doc/opensips/README.uri 0644 bin bin f none doc/opensips/README.uri_db 0644 bin bin f none doc/opensips/README.usrloc 0644 bin bin f none doc/opensips/README.xlog 0644 bin bin f none doc/opensips/README.xmpp 0644 bin bin d none man 0755 bin bin d none man/man8 0755 bin bin f none man/man8/opensips.8 0644 bin bin f none man/man8/opensipsctl.8 0644 bin bin f none man/man8/opensipsunix.8 0644 bin bin d none man/man5 0755 bin bin f none man/man5/opensips.cfg.5 0644 bin bin opensips-2.2.2/packaging/solaris/regex-Prototype000066400000000000000000000005601300170765700220240ustar00rootroot00000000000000i pkginfo i preinstall i postinstall d none /opt 0755 root sys d none /opt/opensips 0755 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/regex.so 0755 root root f none /opt/opensips/README.regex 0644 root root opensips-2.2.2/packaging/solaris/regex-pkginfo000066400000000000000000000003741300170765700214570ustar00rootroot00000000000000PKG="OpenSIPS-regex" NAME="Programmable SIP Server Regex Module" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/regex-postinstall000066400000000000000000000002521300170765700223710ustar00rootroot00000000000000#!/sbin/sh TMPLIST="/tmp/.opensipspcre" echo "Set LD_LIBRARY_PATH to one of these directories before starting OpenSIPS:" echo " " cat $TMPLIST rm -f $TMPLIST exit 0; opensips-2.2.2/packaging/solaris/regex-preinstall000066400000000000000000000007451300170765700222010ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-xmlrpc BASE="OpenSIPS-base" LIBPCRE="libpcre.so" TMPLIST="/tmp/.opensipspcre" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-regex depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBPCRE > $TMPLIST 2>&1 lines=`wc -l $TMPLIST` if [ $lines = "0" ] then echo "OpenSIPS-regex depends on PCRE library, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/packaging/solaris/snmp-Prototype000066400000000000000000000015401300170765700216660ustar00rootroot00000000000000i pkginfo i preinstall i postinstall d none /opt 0755 root sys d none /opt/opensips 0755 root root d none /opt/opensips/etc 0755 root root d none /opt/opensips/etc/opensips 0755 root root f none /opt/opensips/etc/opensips/opensips.cfg 0644 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/snmpstats.so 0755 root root f none /opt/opensips/README.snmpstats 0644 root root d none /opt/opensips/share 0755 root root f none /opt/opensips/share/OPENSER-MIB 0644 root root f none /opt/opensips/share/OPENSER-REG-MIB 0644 root root f none /opt/opensips/share/OPENSER-SIP-COMMON-MIB 0644 root root f none /opt/opensips/share/OPENSER-SIP-SERVER-MIB 0644 root root f none /opt/opensips/share/OPENSER-TC 0644 root root opensips-2.2.2/packaging/solaris/snmp-pkginfo000066400000000000000000000003731300170765700213210ustar00rootroot00000000000000PKG="OpenSIPS-snmp" NAME="Programmable SIP Server SNMP Support" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/snmp-postinstall000066400000000000000000000005031300170765700222330ustar00rootroot00000000000000#!/sbin/sh TMPLIST="/tmp/.opensipssnmp" echo "NOTE: Set LD_LIBRARY_PATH to one of these directories before starting OpenSIPS:" echo " " echo " " cat $TMPLIST rm -f $TMPLIST echo " " echo " " echo "NOTE: Copy the files located at /opt/opensips/share to MIB directory of NET-SNMP installation." echo " " echo " " exit 0; opensips-2.2.2/packaging/solaris/snmp-preinstall000066400000000000000000000007671300170765700220500ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-snmp BASE="OpenSIPS-base" LIBSNMP="libnetsnmp.so.15" TMPLIST="/tmp/.opensipssnmp" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-snmp depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBSNMP > $TMPLIST 2>&1 lines=`wc -l /tmp/.opensipssnmp` if [ $lines = "0" ] then echo "OpenSIPS-snmp depends on NET-SNMP Libraries, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/packaging/solaris/tls-Prototype000066400000000000000000000354041300170765700215210ustar00rootroot00000000000000i pkginfo d none /opt 0755 root sys d none /opt/opensips 0755 root root f none /opt/opensips/AUTHORS 0644 root root d none /opt/opensips/dbtext 0755 root root d none /opt/opensips/dbtext/opensips 0755 root root f none /opt/opensips/dbtext/opensips/acc 0644 root root f none /opt/opensips/dbtext/opensips/active_watchers 0644 root root f none /opt/opensips/dbtext/opensips/address 0644 root root f none /opt/opensips/dbtext/opensips/aliases 0644 root root f none /opt/opensips/dbtext/opensips/carrierfailureroute 0644 root root f none /opt/opensips/dbtext/opensips/carrierroute 0644 root root f none /opt/opensips/dbtext/opensips/cpl 0644 root root f none /opt/opensips/dbtext/opensips/dbaliases 0644 root root f none /opt/opensips/dbtext/opensips/dialog 0644 root root f none /opt/opensips/dbtext/opensips/dialplan 0644 root root f none /opt/opensips/dbtext/opensips/dispatcher 0644 root root f none /opt/opensips/dbtext/opensips/domain 0644 root root f none /opt/opensips/dbtext/opensips/domainpolicy 0644 root root f none /opt/opensips/dbtext/opensips/dr_gateways 0644 root root f none /opt/opensips/dbtext/opensips/dr_groups 0644 root root f none /opt/opensips/dbtext/opensips/dr_rules 0644 root root f none /opt/opensips/dbtext/opensips/globalblacklist 0644 root root f none /opt/opensips/dbtext/opensips/grp 0644 root root f none /opt/opensips/dbtext/opensips/imc_members 0644 root root f none /opt/opensips/dbtext/opensips/imc_rooms 0644 root root f none /opt/opensips/dbtext/opensips/load_balancer 0644 root root f none /opt/opensips/dbtext/opensips/location 0644 root root f none /opt/opensips/dbtext/opensips/missed_calls 0644 root root f none /opt/opensips/dbtext/opensips/nh_sockets 0644 root root f none /opt/opensips/dbtext/opensips/presentity 0644 root root f none /opt/opensips/dbtext/opensips/pua 0644 root root f none /opt/opensips/dbtext/opensips/re_grp 0644 root root f none /opt/opensips/dbtext/opensips/rls_presentity 0644 root root f none /opt/opensips/dbtext/opensips/rls_watchers 0644 root root f none /opt/opensips/dbtext/opensips/route_tree 0644 root root f none /opt/opensips/dbtext/opensips/silo 0644 root root f none /opt/opensips/dbtext/opensips/sip_trace 0644 root root f none /opt/opensips/dbtext/opensips/speed_dial 0644 root root f none /opt/opensips/dbtext/opensips/subscriber 0644 root root f none /opt/opensips/dbtext/opensips/trusted 0644 root root f none /opt/opensips/dbtext/opensips/uri 0644 root root f none /opt/opensips/dbtext/opensips/userblacklist 0644 root root f none /opt/opensips/dbtext/opensips/usr_preferences 0644 root root f none /opt/opensips/dbtext/opensips/version 0644 root root f none /opt/opensips/dbtext/opensips/watchers 0644 root root f none /opt/opensips/dbtext/opensips/xcap 0644 root root d none /opt/opensips/etc 0755 root root d none /opt/opensips/etc/opensips 0700 root root f none /opt/opensips/etc/opensips/opensips.cfg 0500 root root d none /opt/opensips/etc/opensips/tls 0755 root root d none /opt/opensips/etc/opensips/tls/rootCA 0755 root root d none /opt/opensips/etc/opensips/tls/rootCA/certs 0755 root root f none /opt/opensips/etc/opensips/tls/rootCA/certs/01.pem 0644 root root d none /opt/opensips/etc/opensips/tls/rootCA/private 0755 root root f none /opt/opensips/etc/opensips/tls/rootCA/private/cakey.pem 0644 root root f none /opt/opensips/etc/opensips/tls/rootCA/cacert.pem 0644 root root f none /opt/opensips/etc/opensips/tls/rootCA/index.txt 0644 root root f none /opt/opensips/etc/opensips/tls/rootCA/serial 0644 root root d none /opt/opensips/etc/opensips/tls/user 0755 root root f none /opt/opensips/etc/opensips/tls/user/user-calist.pem 0644 root root f none /opt/opensips/etc/opensips/tls/user/user-cert.pem 0644 root root f none /opt/opensips/etc/opensips/tls/user/user-cert_req.pem 0644 root root f none /opt/opensips/etc/opensips/tls/user/user-privkey.pem 0644 root root f none /opt/opensips/etc/opensips/tls/ca.conf 0644 root root f none /opt/opensips/etc/opensips/tls/README 0644 root root f none /opt/opensips/etc/opensips/tls/request.conf 0644 root root f none /opt/opensips/etc/opensips/tls/user.conf 0644 root root f none /opt/opensips/etc/opensips/opensipsctlrc 0644 root root f none /opt/opensips/INSTALL 0644 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/acc.so 0755 root root f none /opt/opensips/lib64/opensips/modules/alias_db.so 0755 root root f none /opt/opensips/lib64/opensips/modules/auth.so 0755 root root f none /opt/opensips/lib64/opensips/modules/auth_db.so 0755 root root f none /opt/opensips/lib64/opensips/modules/auth_diameter.so 0755 root root f none /opt/opensips/lib64/opensips/modules/avpops.so 0755 root root f none /opt/opensips/lib64/opensips/modules/benchmark.so 0755 root root f none /opt/opensips/lib64/opensips/modules/call_control.so 0755 root root f none /opt/opensips/lib64/opensips/modules/cfgutils.so 0755 root root f none /opt/opensips/lib64/opensips/modules/closeddial.so 0755 root root f none /opt/opensips/lib64/opensips/modules/cpl_c.so 0755 root root f none /opt/opensips/lib64/opensips/modules/db_flatstore.so 0755 root root f none /opt/opensips/lib64/opensips/modules/db_text.so 0755 root root f none /opt/opensips/lib64/opensips/modules/dialog.so 0755 root root f none /opt/opensips/lib64/opensips/modules/dialplan.so 0755 root root f none /opt/opensips/lib64/opensips/modules/dispatcher.so 0755 root root f none /opt/opensips/lib64/opensips/modules/diversion.so 0755 root root f none /opt/opensips/lib64/opensips/modules/domain.so 0755 root root f none /opt/opensips/lib64/opensips/modules/domainpolicy.so 0755 root root f none /opt/opensips/lib64/opensips/modules/drouting.so 0755 root root f none /opt/opensips/lib64/opensips/modules/enum.so 0755 root root f none /opt/opensips/lib64/opensips/modules/exec.so 0755 root root f none /opt/opensips/lib64/opensips/modules/gflags.so 0755 root root f none /opt/opensips/lib64/opensips/modules/group.so 0755 root root f none /opt/opensips/lib64/opensips/modules/h350.so 0755 root root f none /opt/opensips/lib64/opensips/modules/identity.so 0755 root root f none /opt/opensips/lib64/opensips/modules/imc.so 0755 root root f none /opt/opensips/lib64/opensips/modules/load_balancer.so 0755 root root f none /opt/opensips/lib64/opensips/modules/localcache.so 0755 root root f none /opt/opensips/lib64/opensips/modules/mangler.so 0755 root root f none /opt/opensips/lib64/opensips/modules/maxfwd.so 0755 root root f none /opt/opensips/lib64/opensips/modules/mediaproxy.so 0755 root root f none /opt/opensips/lib64/opensips/modules/mi_datagram.so 0755 root root f none /opt/opensips/lib64/opensips/modules/mi_fifo.so 0755 root root f none /opt/opensips/lib64/opensips/modules/msilo.so 0755 root root f none /opt/opensips/lib64/opensips/modules/nathelper.so 0755 root root f none /opt/opensips/lib64/opensips/modules/nat_traversal.so 0755 root root f none /opt/opensips/lib64/opensips/modules/options.so 0755 root root f none /opt/opensips/lib64/opensips/modules/path.so 0755 root root f none /opt/opensips/lib64/opensips/modules/permissions.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pike.so 0755 root root f none /opt/opensips/lib64/opensips/modules/presence.so 0755 root root f none /opt/opensips/lib64/opensips/modules/presence_dialoginfo.so 0755 root root f none /opt/opensips/lib64/opensips/modules/presence_mwi.so 0755 root root f none /opt/opensips/lib64/opensips/modules/presence_xcapdiff.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_bla.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_dialoginfo.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_mi.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_usrloc.so 0755 root root f none /opt/opensips/lib64/opensips/modules/pua_xmpp.so 0755 root root f none /opt/opensips/lib64/opensips/modules/qos.so 0755 root root f none /opt/opensips/lib64/opensips/modules/ratelimit.so 0755 root root f none /opt/opensips/lib64/opensips/modules/registrar.so 0755 root root f none /opt/opensips/lib64/opensips/modules/rls.so 0755 root root f none /opt/opensips/lib64/opensips/modules/rr.so 0755 root root f none /opt/opensips/lib64/opensips/modules/seas.so 0755 root root f none /opt/opensips/lib64/opensips/modules/signaling.so 0755 root root f none /opt/opensips/lib64/opensips/modules/siptrace.so 0755 root root f none /opt/opensips/lib64/opensips/modules/sl.so 0755 root root f none /opt/opensips/lib64/opensips/modules/sms.so 0755 root root f none /opt/opensips/lib64/opensips/modules/speeddial.so 0755 root root f none /opt/opensips/lib64/opensips/modules/sst.so 0755 root root f none /opt/opensips/lib64/opensips/modules/statistics.so 0755 root root f none /opt/opensips/lib64/opensips/modules/textops.so 0755 root root f none /opt/opensips/lib64/opensips/modules/tm.so 0755 root root f none /opt/opensips/lib64/opensips/modules/uac.so 0755 root root f none /opt/opensips/lib64/opensips/modules/uac_redirect.so 0755 root root f none /opt/opensips/lib64/opensips/modules/uri.so 0755 root root f none /opt/opensips/lib64/opensips/modules/uri_db.so 0755 root root f none /opt/opensips/lib64/opensips/modules/userblacklist.so 0755 root root f none /opt/opensips/lib64/opensips/modules/usrloc.so 0755 root root f none /opt/opensips/lib64/opensips/modules/xcap_client.so 0755 root root f none /opt/opensips/lib64/opensips/modules/xlog.so 0755 root root f none /opt/opensips/lib64/opensips/modules/tlsops.so 0755 root root d none /opt/opensips/lib64/opensips/opensipsctl 0755 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.base 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.ctlbase 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.fifo 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.unixsock 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.sqlbase 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsdbctl.base 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsctl.dbtext 0644 root root f none /opt/opensips/lib64/opensips/opensipsctl/opensipsdbctl.dbtext 0644 root root d none /opt/opensips/lib64/opensips/opensipsctl/dbtextdb 0755 root root f none /opt/opensips/lib64/opensips/opensipsctl/dbtextdb/dbtextdb.py 0755 root root d none /opt/opensips/man5 0755 root root f none /opt/opensips/man5/opensips.cfg.5 0644 root root d none /opt/opensips/man8 0755 root root f none /opt/opensips/man8/opensips.8 0644 root root f none /opt/opensips/man8/opensipsctl.8 0644 root root f none /opt/opensips/man8/opensipsunix.8 0644 root root f none /opt/opensips/NEWS 0644 root root f none /opt/opensips/README-MODULES 0644 root root f none /opt/opensips/README.acc 0644 root root f none /opt/opensips/README.alias_db 0644 root root f none /opt/opensips/README.auth 0644 root root f none /opt/opensips/README.auth_db 0644 root root f none /opt/opensips/README.auth_diameter 0644 root root f none /opt/opensips/README.avpops 0644 root root f none /opt/opensips/README.benchmark 0644 root root f none /opt/opensips/README.call_control 0644 root root f none /opt/opensips/README.cfgutils 0644 root root f none /opt/opensips/README.closeddial 0644 root root f none /opt/opensips/README.cpl_c 0644 root root f none /opt/opensips/README.db_flatstore 0644 root root f none /opt/opensips/README.db_text 0644 root root f none /opt/opensips/README.dialog 0644 root root f none /opt/opensips/README.dialplan 0644 root root f none /opt/opensips/README.dispatcher 0644 root root f none /opt/opensips/README.diversion 0644 root root f none /opt/opensips/README.domain 0644 root root f none /opt/opensips/README.domainpolicy 0644 root root f none /opt/opensips/README.drouting 0644 root root f none /opt/opensips/README.enum 0644 root root f none /opt/opensips/README.exec 0644 root root f none /opt/opensips/README.gflags 0644 root root f none /opt/opensips/README.group 0644 root root f none /opt/opensips/README.h350 0644 root root f none /opt/opensips/README.identity 0644 root root f none /opt/opensips/README.imc 0644 root root f none /opt/opensips/README.load_balancer 0644 root root f none /opt/opensips/README.localcache 0644 root root f none /opt/opensips/README.mangler 0644 root root f none /opt/opensips/README.maxfwd 0644 root root f none /opt/opensips/README.mediaproxy 0644 root root f none /opt/opensips/README.mi_datagram 0644 root root f none /opt/opensips/README.mi_fifo 0644 root root f none /opt/opensips/README.msilo 0644 root root f none /opt/opensips/README.nathelper 0644 root root f none /opt/opensips/README.nat_traversal 0644 root root f none /opt/opensips/README.options 0644 root root f none /opt/opensips/README.path 0644 root root f none /opt/opensips/README.permissions 0644 root root f none /opt/opensips/README.pike 0644 root root f none /opt/opensips/README.presence 0644 root root f none /opt/opensips/README.presence_dialoginfo 0644 root root f none /opt/opensips/README.presence_mwi 0644 root root f none /opt/opensips/README.presence_xcapdiff 0644 root root f none /opt/opensips/README.pua 0644 root root f none /opt/opensips/README.pua_bla 0644 root root f none /opt/opensips/README.pua_dialoginfo 0644 root root f none /opt/opensips/README.pua_mi 0644 root root f none /opt/opensips/README.pua_usrloc 0644 root root f none /opt/opensips/README.pua_xmpp 0644 root root f none /opt/opensips/README.qos 0644 root root f none /opt/opensips/README.ratelimit 0644 root root f none /opt/opensips/README.registrar 0644 root root f none /opt/opensips/README.rls 0644 root root f none /opt/opensips/README.rr 0644 root root f none /opt/opensips/README.seas 0644 root root f none /opt/opensips/README.signaling 0644 root root f none /opt/opensips/README.siptrace 0644 root root f none /opt/opensips/README.sl 0644 root root f none /opt/opensips/README.sms 0644 root root f none /opt/opensips/README.speeddial 0644 root root f none /opt/opensips/README.sst 0644 root root f none /opt/opensips/README.statistics 0644 root root f none /opt/opensips/README.textops 0644 root root f none /opt/opensips/README.tlsops 0644 root root f none /opt/opensips/README.tm 0644 root root f none /opt/opensips/README.uac 0644 root root f none /opt/opensips/README.uac_redirect 0644 root root f none /opt/opensips/README.uri 0644 root root f none /opt/opensips/README.uri_db 0644 root root f none /opt/opensips/README.userblacklist 0644 root root f none /opt/opensips/README.usrloc 0644 root root f none /opt/opensips/README.xcap_client 0644 root root f none /opt/opensips/README.xlog 0644 root root d none /opt/opensips/sbin 0755 root root f none /opt/opensips/sbin/opensips 0755 root root f none /opt/opensips/sbin/opensipsctl 0755 root root f none /opt/opensips/sbin/opensipsdbctl 0755 root root f none /opt/opensips/sbin/opensipsunix 0755 root root f none /opt/opensips/sbin/osipsconsole 0755 root root opensips-2.2.2/packaging/solaris/tls-pkginfo000066400000000000000000000004101300170765700211360ustar00rootroot00000000000000PKG="OpenSIPS-base-TLS" NAME="Programmable SIP Server Base Install with TLS" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/xmlrpc-Prototype000066400000000000000000000005701300170765700222200ustar00rootroot00000000000000i pkginfo i preinstall i postinstall d none /opt 0755 root sys d none /opt/opensips 0755 root root d none /opt/opensips/lib64 0755 root root d none /opt/opensips/lib64/opensips 0755 root root d none /opt/opensips/lib64/opensips/modules 0755 root root f none /opt/opensips/lib64/opensips/modules/mi_xmlrpc.so 0755 root root f none /opt/opensips/README.mi_xmlrpc 0644 root root opensips-2.2.2/packaging/solaris/xmlrpc-pkginfo000066400000000000000000000004021300170765700216420ustar00rootroot00000000000000PKG="OpenSIPS-xmlrpc" NAME="Programmable SIP Server MI XMLRPC Support" VERSION="2.2.2" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="OpenSIPS Solutions" PSTAMP="19thOct16" EMAIL="saguti@gmail.com" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/" opensips-2.2.2/packaging/solaris/xmlrpc-postinstall000066400000000000000000000002541300170765700225660ustar00rootroot00000000000000#!/sbin/sh TMPLIST="/tmp/.opensipsxmlrpc" echo "Set LD_LIBRARY_PATH to one of these directories before starting OpenSIPS:" echo " " cat $TMPLIST rm -f $TMPLIST exit 0; opensips-2.2.2/packaging/solaris/xmlrpc-preinstall000066400000000000000000000007731300170765700223750ustar00rootroot00000000000000#!/sbin/sh # Script for checking prerequisites for OpenSIPS-xmlrpc BASE="OpenSIPS-base" LIBXMLRPC="libxmlrpc.so.3" TMPLIST="/tmp/.opensipsxmlrpc" pkginfo | grep -i $BASE > /dev/null if [ $? -eq 1 ] then echo "OpenSIPS-xmlrpc depends on package OpenSIPS-base which is not installed"; exit 1; fi find / -name $LIBXMLRPC > $TMPLIST 2>&1 lines=`wc -l $TMPLIST` if [ $lines = "0" ] then echo "OpenSIPS-xmlrpc depends on MySQL Client Libraries, which cannot be found on system"; exit 1; else exit 0; fi opensips-2.2.2/parser/000077500000000000000000000000001300170765700146575ustar00rootroot00000000000000opensips-2.2.2/parser/case_acce.h000066400000000000000000000062471300170765700167270ustar00rootroot00000000000000/* * Accept and Accept-Language Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_ACCE_H #define CASE_ACCE_H #define age_CASE \ switch(LOWER_DWORD(val)) { \ case _age1_: \ hdr->type = HDR_ACCEPTLANGUAGE_T; \ hdr->name.len = 15; \ return (p + 4); \ case _age2_: \ hdr->type = HDR_ACCEPTLANGUAGE_T; \ hdr->name.len = 15; \ p += 4; \ goto dc_cont; \ } #define angu_CASE \ switch(LOWER_DWORD(val)) { \ case _angu_: \ p += 4; \ val = READ(p); \ age_CASE; \ goto other; \ } #define on_CASE \ if (LOWER_BYTE(*p) == 'o') { \ p++; \ if (LOWER_BYTE(*p) == 'n') { \ hdr->type = HDR_ACCEPTDISPOSITION_T; \ hdr->name.len = 18; \ p++; \ goto dc_cont; \ } \ } #define siti_CASE \ switch(LOWER_DWORD(val)) { \ case _siti_: \ p += 4; \ val = READ(p); \ on_CASE; \ goto other; \ } #define ispo_CASE \ switch(LOWER_DWORD(val)) { \ case _ispo_: \ p += 4; \ val = READ(p); \ siti_CASE; \ goto other; \ } #define ptld_CASE \ switch(LOWER_DWORD(val)) { \ case _pt_l_: \ p += 4; \ val = READ(p); \ angu_CASE; \ goto other; \ case _pt_d_: \ p += 4; \ val = READ(p); \ ispo_CASE; \ goto other; \ } #define acce_CASE \ p += 4; \ val = READ(p); \ ptld_CASE; \ if (LOWER_BYTE(*p) == 'p') { \ p++; \ if (LOWER_BYTE(*p) == 't') { \ hdr->type = HDR_ACCEPT_T; \ hdr->name.len = 6; \ p++; \ goto dc_cont; \ } \ } \ goto other; #endif /* CASE_ACCE_H */ opensips-2.2.2/parser/case_allo.h000066400000000000000000000023021300170765700167470ustar00rootroot00000000000000/* * Allow Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_ALLO_H #define CASE_ALLO_H #define allo_CASE \ p += 4; \ if (LOWER_BYTE(*p) == 'w') { \ hdr->type = HDR_ALLOW_T; \ hdr->name.len = 5; \ p++; \ goto dc_cont; \ } \ goto other; #endif /* CASE_ALLO_H */ opensips-2.2.2/parser/case_auth.h000066400000000000000000000033341300170765700167670ustar00rootroot00000000000000/* * Authorization Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_AUTH_H #define CASE_AUTH_H #define AUTH_ATIO_CASE \ if (LOWER_DWORD(val) == _atio_) { \ p += 4; \ switch(LOWER_BYTE(*p)) { \ case 'n': \ hdr->type = HDR_AUTHORIZATION_T; \ hdr->name.len = 13; \ p++; \ goto dc_cont; \ default: \ goto other; \ } \ } #define AUTH_ORIZ_CASE \ if (LOWER_DWORD(val) == _oriz_) { \ p += 4; \ val = READ(p); \ AUTH_ATIO_CASE; \ goto other; \ } #define auth_CASE \ p += 4; \ val = READ(p); \ AUTH_ORIZ_CASE; \ goto other; #endif /* CASE_AUTH_H */ opensips-2.2.2/parser/case_call.h000066400000000000000000000036441300170765700167450ustar00rootroot00000000000000/* * Call-ID Header Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ---------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) */ #ifndef CASE_CALL_H #define CASE_CALL_H #define ID_INF_CASE \ switch(LOWER_DWORD(val)) { \ case __id1_: \ hdr->type = HDR_CALLID_T; \ hdr->name.len = 7; \ return (p + 4); \ \ case __id2_: \ hdr->type = HDR_CALLID_T; \ hdr->name.len = 7; \ p += 4; \ goto dc_end; \ \ case __inf_: \ p += 4; \ if ( LOWER_BYTE(*p) == 'o') {\ hdr->type = HDR_CALL_INFO_T; \ hdr->name.len = 9; \ p += 1; \ goto dc_cont; \ } \ goto other; \ } #define call_CASE \ p += 4; \ val = READ(p); \ ID_INF_CASE; \ goto other; #endif /* CASE_CALL_H */ opensips-2.2.2/parser/case_cont.h000066400000000000000000000073761300170765700170030ustar00rootroot00000000000000/* * Contact, Content-Type, Content-Length, Content-Disposition * Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ---------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) */ #ifndef CASE_CONT_H #define CASE_CONT_H #define TH_CASE \ switch(LOWER_DWORD(val)) { \ case _th12_: \ hdr->type = HDR_CONTENTLENGTH_T; \ hdr->name.len = 14; \ return (p + 4); \ } \ if (LOWER_BYTE(*p) == 't') { \ p++; \ if (LOWER_BYTE(*p) == 'h') { \ hdr->type = HDR_CONTENTLENGTH_T; \ hdr->name.len = 14; \ p++; \ goto dc_cont; \ } \ } #define ion_CASE \ switch(LOWER_DWORD(val)) { \ case _ion1_: \ hdr->type = HDR_CONTENTDISPOSITION_T; \ hdr->name.len = 19; \ return (p + 4); \ case _ion2_: \ hdr->type = HDR_CONTENTDISPOSITION_T; \ hdr->name.len = 19; \ p += 4; \ goto dc_end; \ } #define DISPOSITION_CASE \ switch(LOWER_DWORD(val)) { \ case _osit_: \ p += 4; \ val = READ(p); \ ion_CASE; \ goto other; \ } #define CONTENT_CASE \ switch(LOWER_DWORD(val)) { \ case _leng_: \ p += 4; \ val = READ(p); \ TH_CASE; \ goto other; \ case _type_: \ hdr->type = HDR_CONTENTTYPE_T; \ hdr->name.len = 12; \ p += 4; \ goto dc_cont; \ case _disp_: \ p += 4; \ val = READ(p); \ DISPOSITION_CASE; \ goto other; \ } #define ACT_ENT_CASE \ switch(LOWER_DWORD(val)) { \ case _act1_: \ hdr->type = HDR_CONTACT_T; \ hdr->name.len = 7; \ return (p + 4); \ case _act2_: \ hdr->type = HDR_CONTACT_T; \ hdr->name.len = 7; \ p += 4; \ goto dc_end; \ case _ent__: \ p += 4; \ val = READ(p); \ CONTENT_CASE; \ goto other; \ } #define cont_CASE \ p += 4; \ val = READ(p); \ ACT_ENT_CASE; \ goto other; #endif /* CASE_CONT_H */ opensips-2.2.2/parser/case_cseq.h000066400000000000000000000020461300170765700167600ustar00rootroot00000000000000/* * CSeq Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_CSEQ_H #define CASE_CSEQ_H #define cseq_CASE \ hdr->type = HDR_CSEQ_T; \ hdr->name.len = 4; \ p += 4; \ goto dc_cont #endif /* CASE_CSEQ_H */ opensips-2.2.2/parser/case_dive.h000066400000000000000000000026671300170765700167650ustar00rootroot00000000000000/* * Diversion Header Field Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_DIVE_H #define CASE_DIVE_H #define RSIO_CASE \ switch(LOWER_DWORD(val)) { \ case _rsio_: \ p += 4; \ if (LOWER_BYTE(*p) == 'n') { \ hdr->type = HDR_DIVERSION_T; \ hdr->name.len = 9; \ p++; \ goto dc_cont; \ } \ goto other; \ } #define dive_CASE \ p += 4; \ val = READ(p); \ RSIO_CASE; \ goto other; #endif /* CASE_DIVE_H */ opensips-2.2.2/parser/case_even.h000066400000000000000000000023131300170765700167570ustar00rootroot00000000000000/* * Event Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_EVEN_H #define CASE_EVEN_H #define even_CASE \ p += 4; \ if (LOWER_BYTE(*p) == 't') { \ hdr->type = HDR_EVENT_T; \ hdr->name.len = 5; \ p++; \ goto dc_cont; \ } \ goto other; #endif /* CASE_EVEN_H */ opensips-2.2.2/parser/case_expi.h000066400000000000000000000030241300170765700167670ustar00rootroot00000000000000/* * Expires Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) */ #ifndef CASE_EXPI_H #define CASE_EXPI_H #define EXPI_RES_CASE \ switch(LOWER_DWORD(val)) { \ case _res1_: \ hdr->type = HDR_EXPIRES_T; \ hdr->name.len = 7; \ return (p + 4); \ case _res2_: \ hdr->type = HDR_EXPIRES_T; \ hdr->name.len = 7; \ p += 4; \ goto dc_cont; \ } #define expi_CASE \ p += 4; \ val = READ(p); \ EXPI_RES_CASE; \ goto other; #endif /* CASE_EXPI_H */ opensips-2.2.2/parser/case_from.h000066400000000000000000000020651300170765700167710ustar00rootroot00000000000000/* * From Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_FROM_H #define CASE_FROM_H #define from_CASE \ hdr->type = HDR_FROM_T; \ hdr->name.len = 4; \ p += 4; \ goto dc_cont #endif /* CASE_FROM_H */ opensips-2.2.2/parser/case_max.h000066400000000000000000000026611300170765700166150ustar00rootroot00000000000000/* * Max-Forwards Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_MAX_H #define CASE_MAX_H #define ARDS_CASE \ if (LOWER_DWORD(val) == _ards_) { \ hdr->type = HDR_MAXFORWARDS_T; \ hdr->name.len = 12; \ p += 4; \ goto dc_cont; \ } #define FORW_CASE \ switch(LOWER_DWORD(val)) { \ case _forw_: \ p += 4; \ val = READ(p); \ ARDS_CASE; \ goto other; \ } #define max_CASE \ p += 4; \ val = READ(p); \ FORW_CASE; \ goto other; \ #endif /* CASE_MAX_H */ opensips-2.2.2/parser/case_min_.h000066400000000000000000000043451300170765700167530ustar00rootroot00000000000000/* * Session-Expires Header Field Name Parsing Macros * * Copyright (c) 2006 SOMA Networks, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-02-17 Initial revision (dhsueh@somanetworks.com) */ #ifndef CASE_MIN__H #define CASE_MIN__H 1 #define res__CASE \ switch(LOWER_DWORD(val)) { \ case _res1_ : \ hdr->type = HDR_MIN_EXPIRES_T; \ hdr->name.len = 11; \ return (p + 4); \ case _res2_ : \ hdr->type = HDR_MIN_EXPIRES_T; \ hdr->name.len = 11; \ p += 4; \ goto dc_cont; \ } #define SE_EXPI_CASE \ if ( LOWER_BYTE(*p) == 's' ) { \ p++; \ switch ( LOWER_BYTE(*p) ) { \ case 'e': \ hdr->type = HDR_MIN_SE_T; \ hdr->name.len = 6; \ p++; \ goto dc_cont; \ } \ goto other; \ } \ val = READ(p); \ switch(LOWER_DWORD(val)) { \ case _expi_: \ p += 4; \ val = READ(p); \ res__CASE; \ goto other; \ } #define min__CASE \ p += 4; \ SE_EXPI_CASE; \ goto other; #endif /* ! CASE_MIN__H */ opensips-2.2.2/parser/case_orga.h000066400000000000000000000027521300170765700167610ustar00rootroot00000000000000/* * Organization Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_ORGA_H #define CASE_ORGA_H #define tion_CASE \ if (LOWER_DWORD(val) == _tion_) { \ hdr->type = HDR_ORGANIZATION_T; \ hdr->name.len = 12; \ p += 4; \ goto dc_cont; \ } \ #define niza_CASE \ switch(LOWER_DWORD(val)) { \ case _niza_: \ p += 4; \ val = READ(p); \ tion_CASE; \ goto other; \ } #define orga_CASE \ p += 4; \ val = READ(p); \ niza_CASE; \ goto other; #endif /* CASE_ORGA_H */ opensips-2.2.2/parser/case_p_as.h000066400000000000000000000036771300170765700167620ustar00rootroot00000000000000/* * P-Asserted-Identity Header Field Name Parsing Macros * * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_P_AS_H #define CASE_P_AS_H #define ITY_CASE \ switch( LOWER_DWORD(val) ) { \ case _ity1_: \ hdr->type = HDR_PAI_T; \ hdr->name.len = 19; \ return p + 4; \ case _ity2_: \ hdr->type = HDR_PAI_T; \ hdr->name.len = 19; \ p += 4; \ goto dc_cont; \ } #define DENT_CASE \ if (LOWER_DWORD(val) == _dent_) { \ p += 4; \ val = READ(p); \ ITY_CASE; \ goto other; \ } #define ED_I_CASE \ if (LOWER_DWORD(val) == _ed_i_) { \ p += 4; \ val = READ(p); \ DENT_CASE; \ goto other; \ } #define SERT_CASE \ if (LOWER_DWORD(val) == _sert_) { \ p += 4; \ val = READ(p); \ ED_I_CASE; \ goto other; \ } #define p_as_CASE \ p += 4; \ val = READ(p); \ SERT_CASE; \ goto other; #endif /* CASE_P_AS_H */ opensips-2.2.2/parser/case_p_pr.h000066400000000000000000000036401300170765700167660ustar00rootroot00000000000000/* * P-Preferred-Identity Header Field Name Parsing Macros * * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_P_PR_H #define CASE_P_PR_H #define TITY_CASE \ if (LOWER_DWORD(val) == _tity_) { \ hdr->type = HDR_PPI_T; \ hdr->name.len = 20; \ p += 4; \ goto dc_cont; \ } \ #define IDEN_CASE \ if (LOWER_DWORD(val) == _iden_) { \ p += 4; \ val = READ(p); \ TITY_CASE; \ goto other; \ } #define RED__CASE \ if (LOWER_DWORD(val) == _red__) { \ p += 4; \ val = READ(p); \ IDEN_CASE; \ goto other; \ } #define EFER_CASE \ if (LOWER_DWORD(val) == _efer_) { \ p += 4; \ val = READ(p); \ RED__CASE; \ goto other; \ } #define p_pr_CASE \ p += 4; \ val = READ(p); \ EFER_CASE; \ goto other; #endif /* CASE_P_PR_H */ opensips-2.2.2/parser/case_path.h000066400000000000000000000020371300170765700167610ustar00rootroot00000000000000/* * Supported parser. * * Copyright (C) 2006 Andreas Granig * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef CASE_PATH_H #define CASE_PATH_H #define path_CASE \ p += 4; \ hdr->type = HDR_PATH_T; \ hdr->name.len = 4; \ goto dc_cont; #endif /* CASE_PATH_H */ opensips-2.2.2/parser/case_prio.h000066400000000000000000000024121300170765700167730ustar00rootroot00000000000000/* * Priority Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_PRIO_H #define CASE_PRIO_H #define rity_CASE \ if (LOWER_DWORD(val) == _rity_) { \ hdr->type = HDR_PRIORITY_T; \ hdr->name.len = 8; \ p += 4; \ goto dc_cont; \ } \ #define prio_CASE \ p += 4; \ val = READ(p); \ rity_CASE; \ goto other; #endif /* CASE_PRIO_H */ opensips-2.2.2/parser/case_priv.h000066400000000000000000000026071300170765700170100ustar00rootroot00000000000000/* * Privacy Header Field Name Parsing Macros * * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_PRIV_H #define CASE_PRIV_H #define ACY_CASE \ switch( LOWER_DWORD(val) ) { \ case _acy1_: \ hdr->type = HDR_PRIVACY_T; \ hdr->name.len = 7; \ return p + 4; \ case _acy2_: \ hdr->type = HDR_PRIVACY_T; \ hdr->name.len = 7; \ p += 4; \ goto dc_cont; \ } #define priv_CASE \ p += 4; \ val = READ(p); \ ACY_CASE; \ goto other; #endif /* CASE_PRIV_H */ opensips-2.2.2/parser/case_prox.h000066400000000000000000000070371300170765700170220ustar00rootroot00000000000000/* * Proxy-Require, Proxy-Authorization Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) */ #ifndef CASE_PROX_H #define CASE_PROX_H #define ION_CASE \ switch(LOWER_DWORD(val)) { \ case _ion1_: \ hdr->type = HDR_PROXYAUTH_T; \ hdr->name.len = 19; \ return (p + 4); \ case _ion2_: \ hdr->type = HDR_PROXYAUTH_T; \ hdr->name.len = 19; \ p += 4; \ goto dc_cont; \ } #define IZAT_CASE \ switch(LOWER_DWORD(val)) { \ case _izat_: \ p += 4; \ val = READ(p); \ ION_CASE; \ goto other; \ } #define TE_CASE \ if ( LOWER_BYTE(*p) == 't' && LOWER_BYTE(*(p+1)) == 'e' ) { \ hdr->type = HDR_PROXY_AUTHENTICATE_T; \ hdr->name.len = 18; \ p += 2; \ goto dc_cont; \ } #define TICA_CASE \ switch(LOWER_DWORD(val)) { \ case _tica_: \ p += 4; \ val = READ(p); \ TE_CASE; \ goto other; \ } #define TH2_CASE \ switch(LOWER_DWORD(val)) { \ case _thor_: \ p += 4; \ val = READ(p); \ IZAT_CASE; \ goto other; \ case _then_: \ p += 4; \ val = READ(p); \ TICA_CASE; \ goto other; \ } #define QUIR_CASE \ switch(LOWER_DWORD(val)) { \ case _quir_: \ p += 4; \ switch(LOWER_BYTE(*p)) { \ case 'e': \ hdr->type = HDR_PROXYREQUIRE_T; \ hdr->name.len = 13; \ p++; \ goto dc_cont; \ } \ goto other; \ } #define PROX2_CASE \ switch(LOWER_DWORD(val)) { \ case _y_au_: \ p += 4; \ val = READ(p); \ TH2_CASE; \ goto other; \ case _y_re_: \ p += 4; \ val = READ(p); \ QUIR_CASE; \ goto other; \ } #define prox_CASE \ p += 4; \ val = READ(p); \ PROX2_CASE; \ goto other; #endif /* CASE_PROX_H */ opensips-2.2.2/parser/case_reco.h000066400000000000000000000027331300170765700167600ustar00rootroot00000000000000/* * Record-Route Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_RECO_H #define CASE_RECO_H #define OUTE_CASE \ if (LOWER_DWORD(val) == _oute_) { \ hdr->type = HDR_RECORDROUTE_T; \ hdr->name.len = 12; \ p += 4; \ goto dc_cont; \ } \ #define RD_R_CASE \ switch(LOWER_DWORD(val)) { \ case _rd_r_: \ p += 4; \ val = READ(p); \ OUTE_CASE; \ goto other; \ } #define reco_CASE \ p += 4; \ val = READ(p); \ RD_R_CASE; \ goto other; #endif /* CASE_RECO_H */ opensips-2.2.2/parser/case_refe.h000066400000000000000000000023321300170765700167440ustar00rootroot00000000000000/* * Refer-To Header Field Name Parsing Macros * * Copyright (C) 2005 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef CASE_REFE_H #define CASE_REFE_H #define r_to_CASE \ if (LOWER_DWORD(val) == _r_to_) {\ hdr->type = HDR_REFER_TO_T; \ hdr->name.len = 8; \ p += 4; \ goto dc_cont; \ } #define refe_CASE \ p += 4; \ val = READ(p); \ r_to_CASE; \ goto other; #endif /* CASE_REFE_H */ opensips-2.2.2/parser/case_remo.h000066400000000000000000000034141300170765700167670ustar00rootroot00000000000000/* * Remote-Party-ID Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_REMO_H #define CASE_REMO_H #define _ID_CASE \ switch(LOWER_DWORD(val)) { \ case __id1_: \ hdr->type = HDR_RPID_T; \ hdr->name.len = 15; \ return (p + 4); \ case __id2_: \ hdr->type = HDR_RPID_T; \ hdr->name.len = 15; \ p += 4; \ goto dc_cont; \ } #define ARTY_CASE \ if (LOWER_DWORD(val) == _arty_) { \ p += 4; \ val = READ(p); \ _ID_CASE; \ goto other; \ } #define TE_P_CASE \ if (LOWER_DWORD(val) == _te_p_) { \ p += 4; \ val = READ(p); \ ARTY_CASE; \ goto other; \ } #define remo_CASE \ p += 4; \ val = READ(p); \ TE_P_CASE; \ goto other; #endif /* CASE_REMO_H */ opensips-2.2.2/parser/case_requ.h000066400000000000000000000032471300170765700170050ustar00rootroot00000000000000/* * Require Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) */ #ifndef CASE_REQU_H #define CASE_REQU_H #define IRE_CASE \ switch(LOWER_DWORD(val)) { \ case _ire1_: \ hdr->type = HDR_REQUIRE_T; \ hdr->name.len = 7; \ return (p + 4); \ \ case _ire2_: \ hdr->type = HDR_REQUIRE_T; \ p += 4; \ goto dc_end; \ } #define requ_CASE \ p += 4; \ val = READ(p); \ IRE_CASE; \ goto other; #endif /* CASE_REQU_H */ opensips-2.2.2/parser/case_retr.h000066400000000000000000000032431300170765700170010ustar00rootroot00000000000000/* * P-Asserted-Identity Header Field Name Parsing Macros * * Copyright (C) 2007 Voice System SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_RETR_H #define CASE_RETR_H #define ter_CASE \ switch( LOWER_DWORD(val) ) { \ case _ter1_: \ hdr->type = HDR_RETRY_AFTER_T; \ hdr->name.len = 11; \ return p + 4; \ case _ter2_: \ hdr->type = HDR_RETRY_AFTER_T; \ hdr->name.len = 11; \ p += 4; \ goto dc_cont; \ } #define y_af_CASE \ if (LOWER_DWORD(val) == _y_af_) { \ p += 4; \ val = READ(p); \ ter_CASE; \ goto other; \ } #define retr_CASE \ p += 4; \ val = READ(p); \ y_af_CASE; \ goto other; #endif /* CASE_RETR_H */ opensips-2.2.2/parser/case_rout.h000066400000000000000000000023511300170765700170150ustar00rootroot00000000000000/* * Route Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_ROUT_H #define CASE_ROUT_H #define rout_CASE \ p += 4; \ switch(LOWER_BYTE(*p)) { \ case 'e': \ hdr->type = HDR_ROUTE_T; \ hdr->name.len = 5; \ p++; \ goto dc_cont; \ default: \ goto other; \ } #endif /* CASE_ROUT_H */ opensips-2.2.2/parser/case_sess.h000066400000000000000000000040401300170765700167760ustar00rootroot00000000000000/* * Session-Expires Header Field Name Parsing Macros * * Copyright (c) 2006 SOMA Networks, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-02-17 Initial revision (dhsueh@somanetworks.com) */ #ifndef CASE_SESS_H #define CASE_SESS_H #define RES_CASE \ switch( LOWER_DWORD(val) ) { \ case _res1_: \ hdr->type = HDR_SESSION_EXPIRES_T; \ hdr->name.len = 15; \ return p + 4; \ case _res2_: \ hdr->type = HDR_SESSION_EXPIRES_T; \ hdr->name.len = 15; \ p += 4; \ goto dc_cont; \ } #define EXPI_CASE \ if ( LOWER_DWORD(val) == _expi_ ) { \ p += 4; \ val = READ(p); \ RES_CASE; \ goto other; \ } #define ION__CASE \ if ( LOWER_DWORD(val) == _ion__ ) { \ p += 4; \ val = READ(p); \ EXPI_CASE; \ goto other; \ } #define sess_CASE \ p += 4; \ val = READ(p); \ ION__CASE; \ goto other; #endif opensips-2.2.2/parser/case_sip.h000066400000000000000000000032041300170765700166150ustar00rootroot00000000000000/* * Route Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_SIP_H #define CASE_SIP_H #define atch_CASE \ switch(LOWER_DWORD(val)) { \ case _atch_: \ LM_DBG("end of SIP-If-Match\n"); \ hdr->type = HDR_SIPIFMATCH_T; \ p += 4; \ goto dc_end; \ } #define ifm_CASE \ switch(LOWER_DWORD(val)) { \ case _ifm_: \ LM_DBG("middle of SIP-If-Match: yet=0x%04x\n",LOWER_DWORD(val)); \ p += 4; \ val = READ(p); \ atch_CASE; \ goto other; \ } #define sip_CASE \ LM_DBG("beginning of SIP-If-Match: yet=0x%04x\n",LOWER_DWORD(val)); \ p += 4; \ val = READ(p); \ ifm_CASE; \ goto other; #endif /* CASE_SIP_H */ opensips-2.2.2/parser/case_subj.h000066400000000000000000000026041300170765700167700ustar00rootroot00000000000000/* * Subject Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_SUBJ_H #define CASE_SUBJ_H #define ect_CASE \ switch(LOWER_DWORD(val)) { \ case _ect1_: \ hdr->type = HDR_SUBJECT_T; \ hdr->name.len = 7; \ return (p + 4); \ case _ect2_: \ hdr->type = HDR_SUBJECT_T; \ hdr->name.len = 7; \ p += 4; \ goto dc_cont; \ } #define subj_CASE \ p += 4; \ val = READ(p); \ ect_CASE; \ goto other; #endif /* CASE_SUBJ_H */ opensips-2.2.2/parser/case_supp.h000066400000000000000000000026671300170765700170250ustar00rootroot00000000000000/* * Supported Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_SUPP_H #define CASE_SUPP_H #define ORTE_CASE \ switch(LOWER_DWORD(val)) { \ case _orte_: \ p += 4; \ if (LOWER_BYTE(*p) == 'd') { \ hdr->type = HDR_SUPPORTED_T; \ hdr->name.len = 9; \ p++; \ goto dc_cont; \ } \ goto other; \ } #define supp_CASE \ p += 4; \ val = READ(p); \ ORTE_CASE; \ goto other; #endif /* CASE_SUPP_H */ opensips-2.2.2/parser/case_to.h000066400000000000000000000022371300170765700164510ustar00rootroot00000000000000/* * To Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) */ #ifndef CASE_TO_H #define CASE_TO_H #define to12_CASE \ hdr->type = HDR_TO_T; \ hdr->name.len = 2; \ return (p + 4); #endif /* CASE_TO_H */ opensips-2.2.2/parser/case_unsu.h000066400000000000000000000035051300170765700170200ustar00rootroot00000000000000/* * Unsupported Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) */ #ifndef CASE_UNSU_H #define CASE_UNSU_H #define TED_CASE \ switch(LOWER_DWORD(val)) { \ case _ted1_: \ hdr->type = HDR_UNSUPPORTED_T; \ hdr->name.len = 11; \ return (p + 4); \ case _ted2_: \ hdr->type = HDR_UNSUPPORTED_T; \ hdr->name.len = 11; \ p += 4; \ goto dc_cont; \ } #define PPOR_CASE \ switch(LOWER_DWORD(val)) { \ case _ppor_: \ p += 4; \ val = READ(p); \ TED_CASE; \ goto other; \ } #define unsu_CASE \ p += 4; \ val = READ(p); \ PPOR_CASE; \ goto other; \ #endif /* CASE_UNSU_H */ opensips-2.2.2/parser/case_user.h000066400000000000000000000030251300170765700170010ustar00rootroot00000000000000/* * User-Agent Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_USER_H #define CASE_USER_H #define nt_CASE \ if (LOWER_BYTE(*p) == 'n') { \ p++; \ if (LOWER_BYTE(*p) == 't') { \ hdr->type = HDR_USERAGENT_T; \ hdr->name.len = 10; \ p++; \ goto dc_cont; \ } \ } \ goto other; #define user_CASE \ p += 4; \ val = READ(p); \ switch(LOWER_DWORD(val)) { \ case __age_: \ p += 4; \ nt_CASE; \ } \ goto other; #endif /* CASE_USER_H */ opensips-2.2.2/parser/case_via.h000066400000000000000000000023071300170765700166040ustar00rootroot00000000000000/* * Via Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-28 scratchpad compatibility abandoned (jiri) */ #ifndef CASE_VIA_H #define CASE_VIA_H #define via1_CASE \ hdr->type = HDR_VIA_T; \ hdr->name.len = 3; \ return (p + 4) #define via2_CASE \ hdr->type = HDR_VIA_T; \ hdr->name.len = 3; \ p += 4; \ goto dc_end #endif /* CASE_VIA_H */ opensips-2.2.2/parser/case_www.h000066400000000000000000000032061300170765700166500ustar00rootroot00000000000000/* * WWW-Authenticate Header Field Name Parsing Macros * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CASE_WWW_H #define CASE_WWW_H #define CATE_CASE \ switch(LOWER_DWORD(val)) { \ case _cate_: \ hdr->type = HDR_WWW_AUTHENTICATE_T; \ hdr->name.len = 16; \ p += 4; \ goto dc_cont; \ } #define ENTI_CASE \ switch(LOWER_DWORD(val)) { \ case _enti_: \ p += 4; \ val = READ(p); \ CATE_CASE; \ goto other; \ } #define WWW_AUTH_CASE \ switch(LOWER_DWORD(val)) { \ case _auth_: \ p += 4; \ val = READ(p); \ ENTI_CASE; \ goto other; \ } #define www_CASE \ p += 4; \ val = READ(p); \ WWW_AUTH_CASE; \ goto other; #endif /* CASE_WWW_H */ opensips-2.2.2/parser/contact/000077500000000000000000000000001300170765700163125ustar00rootroot00000000000000opensips-2.2.2/parser/contact/contact.c000066400000000000000000000145211300170765700201140ustar00rootroot00000000000000/* * Parses one Contact in Contact HF body * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-25 Adapted to use new parameter parser (janakj) */ #include /* memset */ #include "../../mem/mem.h" /* pkg_malloc, pkg_free */ #include "../../dprint.h" #include "../../trim.h" /* trim_leading, trim_trailing */ #include "contact.h" #define ST1 1 /* Basic state */ #define ST2 2 /* Quoted */ #define ST3 3 /* Angle quoted */ #define ST4 4 /* Angle quoted and quoted */ #define ST5 5 /* Escape in quoted */ #define ST6 6 /* Escape in angle quoted and quoted */ /* * Skip URI, stops when , (next contact) * or ; (parameter) is found */ static inline int skip_uri(str* _s) { register int st = ST1; while(_s->len) { switch(*(_s->s)) { case ',': case ';': if (st == ST1) return 0; break; case '\"': switch(st) { case ST1: st = ST2; break; case ST2: st = ST1; break; case ST3: st = ST4; break; case ST4: st = ST3; break; case ST5: st = ST2; break; case ST6: st = ST4; break; } break; case '<': switch(st) { case ST1: st = ST3; break; case ST3: LM_ERR("second < found\n"); return -1; case ST5: st = ST2; break; case ST6: st = ST4; break; } break; case '>': switch(st) { case ST1: LM_ERR("> is first\n"); return -2; case ST3: st = ST1; break; case ST5: st = ST2; break; case ST6: st = ST4; break; } break; case '\\': switch(st) { case ST2: st = ST5; break; case ST4: st = ST6; break; case ST5: st = ST2; break; case ST6: st = ST4; break; } break; default: break; } _s->s++; _s->len--; } if (st != ST1) { LM_ERR("< or \" not closed\n"); return -3; } return 0; } /* * Skip name part * * _s will be adjusted to point at the beginning * of URI */ static inline int skip_name(str* _s) { char* last_wsp, *p; int i, quoted = 0; if (!_s) { LM_ERR("invalid parameter value\n"); return -1; } p = _s->s; last_wsp = 0; for(i = 0; i < _s->len; i++) { if (!quoted) { if ((*p == ' ') || (*p == '\t')) { last_wsp = p; } else { if (*p == '<') { _s->s = p; _s->len -= i; return 0; } if (*p == ':') { if (last_wsp) { _s->s = last_wsp; _s->len -= last_wsp - _s->s + 1; } return 0; } if (*p == '\"') { quoted = 1; } } } else { if ((*p == '\"') && (*(p-1) != '\\')) quoted = 0; } p++; } if (quoted) { LM_ERR("closing quote missing in name part of Contact\n"); } else { LM_ERR("error in contact, scheme separator not found\n"); } return -1; } /* * Parse contacts in a Contact HF */ int parse_contacts(str* _s, contact_t** _c) { contact_t* c; contact_t* last; param_hooks_t hooks; last = NULL; while(1) { /* Allocate and clear contact structure */ c = (contact_t*)pkg_malloc(sizeof(contact_t)); if (c == 0) { LM_ERR("no pkg memory left\n"); goto error; } memset(c, 0, sizeof(contact_t)); c->name.s = _s->s; if (skip_name(_s) < 0) { LM_ERR("failed to skip name part\n"); goto error; } c->uri.s = _s->s; c->name.len = _s->s - c->name.s; trim_trailing(&c->name); /* Find the end of the URI */ if (skip_uri(_s) < 0) { LM_ERR("failed to skip URI\n"); goto error; } c->uri.len = _s->s - c->uri.s; /* Calculate URI length */ trim_trailing(&(c->uri)); /* Remove any trailing spaces from URI */ /* Remove <> if any */ if ((c->uri.len >= 2) && (c->uri.s[0] == '<') && (c->uri.s[c->uri.len - 1] == '>')) { c->uri.s++; c->uri.len -= 2; } trim(&c->uri); /* RFC3261 grammar enforces the existence of an URI */ if (c->uri.len==0) { LM_ERR("Empty URI found in contact body\n"); goto error; } if (_s->len == 0) goto ok; if (_s->s[0] == ';') { /* Contact parameter found */ _s->s++; _s->len--; trim_leading(_s); if (_s->len == 0) { LM_ERR("failed to parse params\n"); goto error; } if (parse_params(_s, CLASS_CONTACT, &hooks, &c->params) < 0) { LM_ERR("failed to parse contact parameters\n"); goto error; } c->q = hooks.contact.q; c->expires = hooks.contact.expires; c->received = hooks.contact.received; c->methods = hooks.contact.methods; c->instance = hooks.contact.instance; if (_s->len == 0) goto ok; } /* Next character is comma */ c->len = _s->s - c->name.s; _s->s++; _s->len--; trim_leading(_s); if (_s->len == 0) { LM_ERR("text after comma missing\n"); goto error; } if (last) {last->next=c;} else {*_c = c;} last = c; } error: if (c) pkg_free(c); free_contacts(_c); /* Free any contacts created so far */ return -1; ok: c->len = _s->s - c->name.s; if (last) {last->next=c;} else {*_c = c;} last = c; return 0; } /* * Free list of contacts * _c is head of the list */ void free_contacts(contact_t** _c) { contact_t* ptr; while(*_c) { ptr = *_c; *_c = (*_c)->next; if (ptr->params) { free_params(ptr->params); } pkg_free(ptr); } } /* * Print list of contacts, just for debugging */ void print_contacts(FILE* _o, contact_t* _c) { contact_t* ptr; ptr = _c; while(ptr) { fprintf(_o, "---Contact---\n"); fprintf(_o, "name : '%.*s'\n", ptr->name.len, ptr->name.s); fprintf(_o, "URI : '%.*s'\n", ptr->uri.len, ptr->uri.s); fprintf(_o, "q : %p\n", ptr->q); fprintf(_o, "expires : %p\n", ptr->expires); fprintf(_o, "received: %p\n", ptr->received); fprintf(_o, "method : %p\n", ptr->methods); fprintf(_o, "len : %d\n", ptr->len); if (ptr->params) { print_params(_o, ptr->params); } fprintf(_o, "---/Contact---\n"); ptr = ptr->next; } } opensips-2.2.2/parser/contact/contact.h000066400000000000000000000036441300170765700201250ustar00rootroot00000000000000/* * Contact data type * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-030-25 Adapted to use new parameter parser (janakj) */ #ifndef CONTACT_H #define CONTACT_H #include #include "../../str.h" #include "../parse_param.h" /* * Structure representing a Contact HF body */ typedef struct contact { str name; /* Name part */ str uri; /* contact uri */ param_t* instance; /* +sip.instance parameter hook */ param_t* q; /* q parameter hook */ param_t* expires; /* expires parameter hook */ param_t* methods; /* methods parameter hook */ param_t* received; /* received parameter hook */ param_t* params; /* List of all parameters */ int len; /* Total length of the element */ struct contact* next; /* Next contact in the list */ } contact_t; /* * Parse contacts in a Contact HF */ int parse_contacts(str* _s, contact_t** _c); /* * Free list of contacts * _c is head of the list */ void free_contacts(contact_t** _c); /* * Print list of contacts, just for debugging */ void print_contacts(FILE* _o, contact_t* _c); #endif /* CONTACT_H */ opensips-2.2.2/parser/contact/parse_contact.c000066400000000000000000000116241300170765700213070ustar00rootroot00000000000000/* * Contact header field body parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-25 Adapted to use new parameter parser (janakj) */ #include /* memset */ #include "../hf.h" #include "../../mem/mem.h" /* pkg_malloc, pkg_free */ #include "../../dprint.h" #include "../../trim.h" /* trim_leading */ #include "../../errinfo.h" /* trim_leading */ #include "parse_contact.h" static inline int contact_parser(char* _s, int _l, contact_body_t* _c) { str tmp; tmp.s = _s; tmp.len = _l; trim_leading(&tmp); if (tmp.len == 0) { LM_ERR("empty body\n"); return -1; } if (tmp.s[0] == '*') { _c->star = 1; if (tmp.len!=1) { LM_ERR("invalid START Contact header (more than START only)\n"); return -2; } } else { if (parse_contacts(&tmp, &(_c->contacts)) < 0) { LM_ERR("failed to parse contacts\n"); return -3; } } return 0; } /* * Parse contact header field body */ int parse_contact(struct hdr_field* _h) { contact_body_t* b; if (_h->parsed != 0) { return 0; /* Already parsed */ } b = (contact_body_t*)pkg_malloc(sizeof(contact_body_t)); if (b == 0) { LM_ERR("no pkg memory left\n"); return -1; } memset(b, 0, sizeof(contact_body_t)); if (contact_parser(_h->body.s, _h->body.len, b) < 0) { LM_ERR("failed to parse contact\n"); pkg_free(b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing CONTACT headers"); set_err_reply(400, "bad headers"); return -2; } _h->parsed = (void*)b; return 0; } /* * Free all memory */ void free_contact(contact_body_t** _c) { if ((*_c)->contacts) { free_contacts(&((*_c)->contacts)); } pkg_free(*_c); *_c = 0; } /* * Print structure, for debugging only */ void print_contact(FILE* _o, contact_body_t* _c) { fprintf(_o, "===Contact body===\n"); fprintf(_o, "star: %d\n", _c->star); print_contacts(_o, _c->contacts); fprintf(_o, "===/Contact body===\n"); } /* * Contact header field iterator, returns next contact if any, it doesn't * parse message header if not absolutely necessary */ int contact_iterator(contact_t** c, struct sip_msg* msg, contact_t* prev) { static struct hdr_field* hdr = 0; struct hdr_field* last; contact_body_t* cb; if (!msg) { LM_ERR("invalid parameter value\n"); return -1; } if (!prev) { /* No pointer to previous contact given, find topmost * contact and return pointer to the first contact * inside that header field */ hdr = msg->contact; if (!hdr) { if (parse_headers(msg, HDR_CONTACT_F, 0) == -1) { LM_ERR("failed to parse headers\n"); return -1; } hdr = msg->contact; } if (hdr) { if (parse_contact(hdr) < 0) { LM_ERR("failed to parse Contact\n"); return -1; } } else { *c = 0; return 1; } cb = (contact_body_t*)hdr->parsed; *c = cb->contacts; return 0; } else { /* Check if there is another contact in the * same header field and if so then return it */ if (prev->next) { *c = prev->next; return 0; } /* Try to find and parse another Contact * header field */ last = hdr; hdr = hdr->next; /* Search another already parsed Contact * header field */ while(hdr && hdr->type != HDR_CONTACT_T) { hdr = hdr->next; } if (!hdr) { /* Look for another Contact HF in unparsed * part of the message header */ if (parse_headers(msg, HDR_CONTACT_F, 1) == -1) { LM_ERR("failed to parse message header\n"); return -1; } /* Check if last found header field is Contact * and if it is not the same header field as the * previous Contact HF (that indicates that the previous * one was the last header field in the header) */ if ((msg->last_header->type == HDR_CONTACT_T) && (msg->last_header != last)) { hdr = msg->last_header; } else { *c = 0; return 1; } } if (parse_contact(hdr) < 0) { LM_ERR("failed to parse Contact HF body\n"); return -1; } /* And return first contact within that * header field */ cb = (contact_body_t*)hdr->parsed; *c = cb->contacts; return 0; } } opensips-2.2.2/parser/contact/parse_contact.h000066400000000000000000000033001300170765700213040ustar00rootroot00000000000000/* * Contact header field body parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-25 Adapted to use new parameter parser (janakj) */ #ifndef PARSE_CONTACT_H #define PARSE_CONTACT_H #include #include "../hf.h" #include "../../str.h" #include "../msg_parser.h" #include "contact.h" typedef struct contact_body { unsigned char star; /* Star contact */ contact_t* contacts; /* List of contacts */ } contact_body_t; /* * Parse contact header field body */ int parse_contact(struct hdr_field* _h); /* * Free all memory */ void free_contact(contact_body_t** _c); /* * Print structure, for debugging only */ void print_contact(FILE* _o, contact_body_t* _c); /* * Contact header field iterator, returns next contact if any, it doesn't * parse message header if not absolutely necessary */ int contact_iterator(contact_t** c, struct sip_msg* msg, contact_t* prev); #endif /* PARSE_CONTACT_H */ opensips-2.2.2/parser/digest/000077500000000000000000000000001300170765700161365ustar00rootroot00000000000000opensips-2.2.2/parser/digest/digest.c000066400000000000000000000137121300170765700175650ustar00rootroot00000000000000/* * Digest credentials parser interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "digest.h" #include "../../mem/mem.h" /* pkg_malloc */ #include "../../dprint.h" /* Guess what */ #include /* printf */ #include /* strncasecmp */ /* * Create and initialize a new credentials structure */ static inline int new_credentials(struct hdr_field* _h) { auth_body_t* b; b = (auth_body_t*)pkg_malloc(sizeof(auth_body_t)); if (b == 0) { LM_ERR("no pkg memory left\n"); return -1; } init_dig_cred(&(b->digest)); b->stale = 0; b->authorized = 0; _h->parsed = (void*)b; return 0; } /* * Parse digest credentials * Return value -1 means that the function was unable to allocate * memory and therefore the server should return Internal Server Error, * not Bad Request in this case ! * Bad Request should be send when return value != -1 */ int parse_credentials(struct hdr_field* _h) { int res; if (_h->parsed) { return 0; /* Already parsed */ } if (new_credentials(_h) < 0) { LM_ERR("failed to create new credentials\n"); return -1; } /* parse_digest_cred must return < -1 on error otherwise we will be * unable to distinguish if the error was caused by the server or if the * credentials are broken */ res = parse_digest_cred(&(_h->body), &(((auth_body_t*)(_h->parsed))->digest)); if (res != 0) { free_credentials((auth_body_t**)(void*)&(_h->parsed)); } return res; } /* * Free all memory */ void free_credentials(auth_body_t** _b) { pkg_free(*_b); *_b = 0; } /* * Check semantics of a digest credentials structure * Make sure that all attributes needed to verify response * string are set or at least have a default value * * The returned value is logical OR of all errors encountered * during the check, see dig_err_t type for more details */ dig_err_t check_dig_cred(dig_cred_t* _c) { dig_err_t res = E_DIG_OK; /* Username must be present */ if (_c->username.user.s == 0) res |= E_DIG_USERNAME; /* Realm must be present */ if (_c->realm.s == 0) res |= E_DIG_REALM; /* Nonce that was used must be specified */ if (_c->nonce.s == 0) res |= E_DIG_NONCE; /* URI must be specified */ if (_c->uri.s == 0) res |= E_DIG_URI; /* We cannot check credentials without response */ if (_c->response.s == 0) res |= E_DIG_RESPONSE; /* If QOP parameter is present, some additional * requirements must be met */ if ((_c->qop.qop_parsed == QOP_AUTH_D) || (_c->qop.qop_parsed == QOP_AUTHINT_D)) { /* CNONCE must be specified */ if (_c->cnonce.s == 0) res |= E_DIG_CNONCE; /* and also nonce count must be specified */ if (_c->nc.s == 0) res |= E_DIG_NC; } return res; } /* * Print credential structure content to stdout * Just for debugging */ void print_cred(dig_cred_t* _c) { printf("===Digest credentials===\n"); if (_c) { printf("Username\n"); printf("+--whole = \'%.*s\'\n", _c->username.whole.len, _c->username.whole.s); printf("+--user = \'%.*s\'\n", _c->username.user.len, _c->username.user.s); printf("\\--domain = \'%.*s\'\n", _c->username.domain.len, _c->username.domain.s); printf("Realm = \'%.*s\'\n", _c->realm.len, _c->realm.s); printf("Nonce = \'%.*s\'\n", _c->nonce.len, _c->nonce.s); printf("URI = \'%.*s\'\n", _c->uri.len, _c->uri.s); printf("Response = \'%.*s\'\n", _c->response.len, _c->response.s); printf("Algorithm = \'%.*s\'\n", _c->alg.alg_str.len, _c->alg.alg_str.s); printf("\\--parsed = "); switch(_c->alg.alg_parsed) { case ALG_UNSPEC: printf("ALG_UNSPEC\n"); break; case ALG_MD5: printf("ALG_MD5\n"); break; case ALG_MD5SESS: printf("ALG_MD5SESS\n"); break; case ALG_OTHER: printf("ALG_OTHER\n"); break; } printf("Cnonce = \'%.*s\'\n", _c->cnonce.len, _c->cnonce.s); printf("Opaque = \'%.*s\'\n", _c->opaque.len, _c->opaque.s); printf("QOP = \'%.*s\'\n", _c->qop.qop_str.len, _c->qop.qop_str.s); printf("\\--parsed = "); switch(_c->qop.qop_parsed) { case QOP_UNSPEC_D: printf("QOP_UNSPEC_D\n"); break; case QOP_AUTH_D: printf("QOP_AUTH_D\n"); break; case QOP_AUTHINT_D: printf("QOP_AUTHINT_D\n"); break; case QOP_OTHER_D: printf("QOP_OTHER_D\n"); break; } printf("NC = \'%.*s\'\n", _c->nc.len, _c->nc.s); } printf("===/Digest credentials===\n"); } /* * Mark credentials as selected so functions * following authorize know which credentials * to use if the message contained more than * one */ int mark_authorized_cred(struct sip_msg* _m, struct hdr_field* _h) { struct hdr_field* f; switch(_h->type) { case HDR_AUTHORIZATION_T: f = _m->authorization; break; case HDR_PROXYAUTH_T: f = _m->proxy_auth; break; default: LM_ERR("invalid header field type\n"); return -1; } if (!(f->parsed)) { if (new_credentials(f) < 0) { LM_ERR("new_credentials failed\n"); return -1; } } ((auth_body_t*)(f->parsed))->authorized = _h; return 0; } /* * Get pointer to authorized credentials, if there are no * authorized credentials, 0 is returned */ int get_authorized_cred(struct hdr_field* _f, struct hdr_field** _h) { if (_f && _f->parsed) { *_h = ((auth_body_t*)(_f->parsed))->authorized; } else { *_h = 0; } return 0; } opensips-2.2.2/parser/digest/digest.h000066400000000000000000000053421300170765700175720ustar00rootroot00000000000000/* * Digest credentials parser interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DIGEST_H #define DIGEST_H #include "digest_parser.h" #include "../hf.h" /* struct hdr_field */ #include "../msg_parser.h" typedef struct auth_body { /* This is pointer to header field containing * parsed authorized digest credentials. This * pointer is set in sip_msg->{authorization,proxy_auth} * hooks. * * This is necessary for functions called after * {www,proxy}_authorize, these functions need to know * which credentials are authorized and they will simply * look into * sip_msg->{authorization,proxy_auth}->parsed->authorized */ struct hdr_field* authorized; dig_cred_t digest; /* Parsed digest credentials */ unsigned char stale; /* Flag is set if nonce is stale */ } auth_body_t; /* * Errors returned by check_dig_cred */ typedef enum dig_err { E_DIG_OK = 0, /* Everything is OK */ E_DIG_USERNAME = 1, /* Username missing */ E_DIG_REALM = 2, /* Realm missing */ E_DIG_NONCE = 4, /* Nonce value missing */ E_DIG_URI = 8, /* URI missing */ E_DIG_RESPONSE = 16, /* Response missing */ E_DIG_CNONCE = 32, /* CNONCE missing */ E_DIG_NC = 64, /* Nonce-count missing */ E_DIG_DOMAIN = 128 /* Username domain != realm */ } dig_err_t; /* * Parse digest credentials */ int parse_credentials(struct hdr_field* _h); /* * Free all memory associated with parsed * structures */ void free_credentials(auth_body_t** _b); /* * Print dig_cred structure to stdout */ void print_cred(dig_cred_t* _c); /* * Mark credentials as authorized */ int mark_authorized_cred(struct sip_msg* _m, struct hdr_field* _h); /* * Get pointer to authorized credentials */ int get_authorized_cred(struct hdr_field* _f, struct hdr_field** _h); /* * Check if credentials are correct * (check of semantics) */ dig_err_t check_dig_cred(dig_cred_t* _c); #endif /* DIGEST_H */ opensips-2.2.2/parser/digest/digest_keys.h000066400000000000000000000025101300170765700206170ustar00rootroot00000000000000/* * This file is automatically generated, do not edit * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DIGEST_KEYS_H #define DIGEST_KEYS_H #define _user_ 0x72657375 /* "user" */ #define _name_ 0x656d616e /* "name" */ #define _real_ 0x6c616572 /* "real" */ #define _nonc_ 0x636e6f6e /* "nonc" */ #define _resp_ 0x70736572 /* "resp" */ #define _onse_ 0x65736e6f /* "onse" */ #define _cnon_ 0x6e6f6e63 /* "cnon" */ #define _opaq_ 0x7161706f /* "opaq" */ #define _algo_ 0x6f676c61 /* "algo" */ #define _rith_ 0x68746972 /* "rith" */ #endif /* DIGEST_KEYS_H */ opensips-2.2.2/parser/digest/digest_parser.c000066400000000000000000000201461300170765700211400ustar00rootroot00000000000000/* * Digest credentials parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-02: Added parse_domain function (janakj) */ #include "digest_parser.h" #include "../../trim.h" /* trim_leading */ #include /* strncasecmp */ #include "param_parser.h" /* Digest parameter name parser */ #include "../../ut.h" /* q_memchr */ #define DIGEST_SCHEME "digest" #define DIG_LEN 6 #define QOP_AUTH_STR "auth" #define QOP_AUTH_STR_LEN 4 #define QOP_AUTHINT_STR "auth-int" #define QOP_AUTHINT_STR_LEN 8 #define ALG_MD5_STR "MD5" #define ALG_MD5_STR_LEN 3 #define ALG_MD5SESS_STR "MD5-sess" #define ALG_MD5SESS_STR_LEN 8 /* * Parse quoted string in a parameter body * return the string without quotes in _r * parameter and update _s to point behind the * closing quote */ static inline int parse_quoted(str* _s, str* _r) { char* end_quote; /* The string must have at least * surrounding quotes */ if (_s->len < 2) { return -1; } /* Skip opening quote */ _s->s++; _s->len--; /* Find closing quote */ end_quote = q_memchr(_s->s, '\"', _s->len); /* Not found, return error */ if (!end_quote) { return -2; } /* Let _r point to the string without * surrounding quotes */ _r->s = _s->s; _r->len = end_quote - _s->s; /* Update _s parameter to point * behind the closing quote */ _s->len -= (end_quote - _s->s + 1); _s->s = end_quote + 1; /* Everything went OK */ return 0; } /* * Parse unquoted token in a parameter body * let _r point to the token and update _s * to point right behind the token */ static inline int parse_token(str* _s, str* _r) { int i; /* Save the beginning of the * token in _r->s */ _r->s = _s->s; /* Iterate through the * token body */ for(i = 0; i < _s->len; i++) { /* All these characters * mark end of the token */ switch(_s->s[i]) { case ' ': case '\t': case '\r': case '\n': case ',': /* So if you find * any of them * stop iterating */ goto out; } } out: /* Empty token is error */ if (i == 0) { return -2; } /* Save length of the token */ _r->len = i; /* Update _s parameter so it points * right behind the end of the token */ _s->s = _s->s + i; _s->len -= i; /* Everything went OK */ return 0; } /* * Parse a digest parameter */ static inline int parse_digest_param(str* _s, dig_cred_t* _c) { dig_par_t t; str* ptr; str dummy; /* Get type of the parameter */ if (parse_param_name(_s, &t) < 0) { return -1; } _s->s++; /* skip = */ _s->len--; /* Find the beginning of body */ trim_leading(_s); if (_s->len == 0) { return -2; } /* Decide in which attribute the * body content will be stored */ switch(t) { case PAR_USERNAME: ptr = &_c->username.whole; break; case PAR_REALM: ptr = &_c->realm; break; case PAR_NONCE: ptr = &_c->nonce; break; case PAR_URI: ptr = &_c->uri; break; case PAR_RESPONSE: ptr = &_c->response; break; case PAR_CNONCE: ptr = &_c->cnonce; break; case PAR_OPAQUE: ptr = &_c->opaque; break; case PAR_QOP: ptr = &_c->qop.qop_str; break; case PAR_NC: ptr = &_c->nc; break; case PAR_ALGORITHM: ptr = &_c->alg.alg_str; break; case PAR_OTHER: ptr = &dummy; break; default: ptr = &dummy; break; } /* If the first character is quote, it is * a quoted string, otherwise it is a token */ if (_s->s[0] == '\"') { if (parse_quoted(_s, ptr) < 0) { return -3; } } else { if (parse_token(_s, ptr) < 0) { return -4; } } return 0; } /* * Parse qop parameter body */ static inline void parse_qop(struct qp* _q) { str s; s.s = _q->qop_str.s; s.len = _q->qop_str.len; trim(&s); if ((s.len == QOP_AUTH_STR_LEN) && !strncasecmp(s.s, QOP_AUTH_STR, QOP_AUTH_STR_LEN)) { _q->qop_parsed = QOP_AUTH_D; } else if ((s.len == QOP_AUTHINT_STR_LEN) && !strncasecmp(s.s, QOP_AUTHINT_STR, QOP_AUTHINT_STR_LEN)) { _q->qop_parsed = QOP_AUTHINT_D; } else { _q->qop_parsed = QOP_OTHER_D; } } /* * Parse algorithm parameter body */ static inline void parse_algorithm(struct algorithm* _a) { str s; s.s = _a->alg_str.s; s.len = _a->alg_str.len; trim(&s); if ((s.len == ALG_MD5_STR_LEN) && !strncasecmp(s.s, ALG_MD5_STR, ALG_MD5_STR_LEN)) { _a->alg_parsed = ALG_MD5; } else if ((s.len == ALG_MD5SESS_STR_LEN) && !strncasecmp(s.s, ALG_MD5SESS_STR, ALG_MD5SESS_STR_LEN)) { _a->alg_parsed = ALG_MD5SESS; } else { _a->alg_parsed = ALG_OTHER; } } /* * Parse username for user and domain parts */ static inline void parse_username(struct username* _u) { char* d; _u->user = _u->whole; if (_u->whole.len <= 2) return; d = q_memchr(_u->whole.s, '@', _u->whole.len); if (d) { _u->domain.s = d + 1; _u->domain.len = _u->whole.len - (d - _u->whole.s) - 1; _u->user.len = d - _u->user.s; } } /* * Parse Digest credentials parameter, one by one */ static inline int parse_digest_params(str* _s, dig_cred_t* _c) { char* comma; do { /* Parse the first parameter */ if (parse_digest_param(_s, _c) < 0) { return -1; } /* Try to find the next parameter */ comma = q_memchr(_s->s, ',', _s->len); if (comma) { /* Yes, there is another, * remove any leading white-spaces * and let _s point to the next * parameter name */ _s->len -= comma - _s->s + 1; _s->s = comma + 1; trim_leading(_s); } } while(comma); /* Repeat while there are next parameters */ /* Parse QOP body if the parameter was present */ if (_c->qop.qop_str.s != 0) { parse_qop(&_c->qop); } /* Parse algorithm body if the parameter was present */ if (_c->alg.alg_str.s != 0) { parse_algorithm(&_c->alg); } if (_c->username.whole.s != 0) { parse_username(&_c->username); } return 0; } /* * We support Digest authentication only * * Returns: * 0 - if everything is OK * -1 - Error while parsing * 1 - Unknown scheme */ int parse_digest_cred(str* _s, dig_cred_t* _c) { str tmp; /* Make a temporary copy, we are * going to modify it */ tmp.s = _s->s; tmp.len = _s->len; /* Remove any leading spaces, tabs, \r and \n */ trim_leading(&tmp); /* Check the string length */ if (tmp.len < (DIG_LEN + 1)) return 1; /* Too short, unknown scheme */ /* Now test, if it is digest scheme, since it is the only * scheme we are able to parse here */ if (!strncasecmp(tmp.s, DIGEST_SCHEME, DIG_LEN) && ((tmp.s[DIG_LEN] == ' ') || /* Test for one of LWS chars */ (tmp.s[DIG_LEN] == '\r') || (tmp.s[DIG_LEN] == 'n') || (tmp.s[DIG_LEN] == '\t') || (tmp.s[DIG_LEN] == ','))) { /* Scheme is Digest */ tmp.s += DIG_LEN + 1; tmp.len -= DIG_LEN + 1; /* Again, skip all white-spaces */ trim_leading(&tmp); /* And parse digest parameters */ if (parse_digest_params(&tmp, _c) < 0) { return -2; /* We must not return -1 in this function ! */ } else { return 0; } } else { return 1; /* Unknown scheme */ } } /* * Initialize a digest credentials structure */ void init_dig_cred(dig_cred_t* _c) { memset(_c, 0, sizeof(dig_cred_t)); } opensips-2.2.2/parser/digest/digest_parser.h000066400000000000000000000067101300170765700211460ustar00rootroot00000000000000/* * Digest credentials parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-15: Duplicate algorithm in dig_cred_t removed (janakj) */ #ifndef DIGEST_PARSER_H #define DIGEST_PARSER_H #include "../../str.h" /* Type of algorithm used */ typedef enum alg { ALG_UNSPEC = 0, /* Algorithm parameter not specified */ ALG_MD5 = 1, /* MD5 - default value*/ ALG_MD5SESS = 2, /* MD5-Session */ ALG_OTHER = 4 /* Unknown */ } alg_t; /* Quality Of Protection used */ typedef enum qop_type { QOP_UNSPEC_D = 0, /* QOP parameter not present in response */ QOP_AUTH_D = 1, /* Authentication only */ QOP_AUTHINT_D = 2, /* Authentication with integrity checks */ QOP_OTHER_D = 4 /* Unknown */ } qop_type_t; /* Algorithm structure */ struct algorithm { str alg_str; /* The original string representation */ alg_t alg_parsed; /* Parsed value */ }; /* QOP structure */ struct qp { str qop_str; /* The original string representation */ qop_type_t qop_parsed; /* Parsed value */ }; /* Username structure */ struct username { str whole; /* The whole username parameter value */ str user; /* username part only */ str domain; /* Domain part only */ }; /* * Parsed digest credentials */ typedef struct dig_cred { struct username username; /* Username */ str realm; /* Realm */ str nonce; /* Nonce value */ str uri; /* URI */ str response; /* Response string */ struct algorithm alg; /* Type of algorithm used */ str cnonce; /* Cnonce value */ str opaque; /* Opaque data string */ struct qp qop; /* Quality Of Protection */ str nc; /* Nonce count parameter */ } dig_cred_t; /* * Macro to obtain the value of realm. The macro would first * check if there is any @domain part in the username and if * so, it will be returned as the value of realm. This hack is * ofter used to protect realm using the digest (username parameter * is protected by the response hash) and also to allow subscribers * to specify a different domain part than the one in realm parameter */ #define GET_REALM(cred) \ (((cred)->username.domain.len && (cred)->username.domain.s) ? \ &(cred)->username.domain : \ &(cred)->realm) /* * Initialize a digest credentials structure */ void init_dig_cred(dig_cred_t* _c); /* * We support Digest authentication only * * Returns: * 0 - if everything is OK * -1 - Error while parsing * 1 - Unknown scheme */ int parse_digest_cred(str* _s, dig_cred_t* _c); #endif /* DIGEST_PARSER_H */ opensips-2.2.2/parser/digest/param_parser.c000066400000000000000000000147741300170765700207730ustar00rootroot00000000000000/* * 32-bit Digest parameter name parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "param_parser.h" #include "digest_keys.h" #include "../../trim.h" #include "../../ut.h" #define LOWER_BYTE(b) ((b) | 0x20) #define LOWER_DWORD(d) ((d) | 0x20202020) /* * Parse short (less than 4 bytes) parameter names */ #define PARSE_SHORT \ switch(LOWER_BYTE(*p)) { \ case 'u': \ if (LOWER_BYTE(*(p + 1)) == 'r') { \ if (LOWER_BYTE(*(p + 2)) == 'i') { \ *_type = PAR_URI; \ p += 3; \ goto end; \ } \ } \ break; \ \ case 'q': \ if (LOWER_BYTE(*(p + 1)) == 'o') { \ if (LOWER_BYTE(*(p + 2)) == 'p') { \ *_type = PAR_QOP; \ p += 3; \ goto end; \ } \ } \ break; \ \ case 'n': \ if (LOWER_BYTE(*(p + 1)) == 'c') { \ *_type = PAR_NC; \ p += 2; \ goto end; \ } \ break; \ } /* * Read 4-bytes from memory and store them in an integer variable * Reading byte by byte ensures, that the code works also on HW which * does not allow reading 4-bytes at once from unaligned memory position * (Sparc for example) */ #define READ(val) \ (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)) #define name_CASE \ switch(LOWER_DWORD(val)) { \ case _name_: \ *_type = PAR_USERNAME; \ p += 4; \ goto end; \ } #define user_CASE \ p += 4; \ val = READ(p); \ name_CASE; \ goto other; #define real_CASE \ p += 4; \ if (LOWER_BYTE(*p) == 'm') { \ *_type = PAR_REALM; \ p++; \ goto end; \ } #define nonc_CASE \ p += 4; \ if (LOWER_BYTE(*p) == 'e') { \ *_type = PAR_NONCE; \ p++; \ goto end; \ } #define onse_CASE \ switch(LOWER_DWORD(val)) { \ case _onse_: \ *_type = PAR_RESPONSE; \ p += 4; \ goto end; \ } #define resp_CASE \ p += 4; \ val = READ(p); \ onse_CASE; \ goto other; #define cnon_CASE \ p += 4; \ if (LOWER_BYTE(*p) == 'c') { \ p++; \ if (LOWER_BYTE(*p) == 'e') { \ *_type = PAR_CNONCE; \ p++; \ goto end; \ } \ } \ goto other; #define opaq_CASE \ p += 4; \ if (LOWER_BYTE(*p) == 'u') { \ p++; \ if (LOWER_BYTE(*p) == 'e') { \ *_type = PAR_OPAQUE; \ p++; \ goto end; \ } \ } \ goto other; #define rith_CASE \ switch(LOWER_DWORD(val)) { \ case _rith_: \ p += 4; \ if (LOWER_BYTE(*p) == 'm') { \ *_type = PAR_ALGORITHM; \ p++; \ goto end; \ } \ goto other; \ } #define algo_CASE \ p += 4; \ val = READ(p); \ rith_CASE; \ goto other #define FIRST_QUATERNIONS \ case _user_: user_CASE; \ case _real_: real_CASE; \ case _nonc_: nonc_CASE; \ case _resp_: resp_CASE; \ case _cnon_: cnon_CASE; \ case _opaq_: opaq_CASE; \ case _algo_: algo_CASE; int parse_param_name(str* _s, dig_par_t* _type) { register char* p; register int val; char* end; end = _s->s + _s->len; p = _s->s; val = READ(p); if (_s->len < 4) { goto other; } switch(LOWER_DWORD(val)) { FIRST_QUATERNIONS; default: PARSE_SHORT; goto other; } end: _s->len -= p - _s->s; _s->s = p; trim_leading(_s); if (_s->s[0] == '=') { return 0; } other: p = q_memchr(p, '=', end - p); if (!p) { return -1; /* Parse error */ } else { *_type = PAR_OTHER; _s->len -= p - _s->s; _s->s = p; return 0; } } opensips-2.2.2/parser/digest/param_parser.h000066400000000000000000000030141300170765700207610ustar00rootroot00000000000000/* * 32-bit Digest parameter name parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARAM_PARSER_H #define PARAM_PARSER_H #include "../../str.h" /* * Digest parameter types */ typedef enum dig_par { PAR_USERNAME, /* username parameter */ PAR_REALM, /* realm parameter */ PAR_NONCE, /* nonce parameter */ PAR_URI, /* uri parameter */ PAR_RESPONSE, /* response parameter */ PAR_CNONCE, /* cnonce parameter */ PAR_OPAQUE, /* opaque parameter */ PAR_QOP, /* qop parameter */ PAR_NC, /* nonce-count parameter */ PAR_ALGORITHM, /* algorithm parameter */ PAR_OTHER /* unknown parameter */ } dig_par_t; /* * Parse digest parameter name */ int parse_param_name(str* _s, dig_par_t* _type); #endif /* PARAM_PARSER_H */ opensips-2.2.2/parser/hf.c000066400000000000000000000116751300170765700154320ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2006-02-17 Session-Expires, Min-SE (dhsueh@somanetworks.com) */ /** * \file parser/hf.c * \brief Helper functions for SIP headers. * * Memory management and debugging functions for SIP headers. */ #include "hf.h" #include "parse_via.h" #include "parse_to.h" #include "parse_cseq.h" #include "../dprint.h" #include "../mem/mem.h" #include "parse_def.h" #include "digest/digest.h" /* free_credentials */ #include "parse_event.h" #include "parse_expires.h" #include "parse_rr.h" #include "contact/parse_contact.h" #include "parse_disposition.h" #include "../ut.h" #include "parse_supported.h" #include "parse_allow.h" #include "parse_sst.h" #include "parse_content.h" #include "parse_call_info.h" #include "parse_authenticate.h" /* * Frees a hdr_field structure, * WARNING: it frees only parsed (and not name.s, body.s) */ void clean_hdr_field(struct hdr_field* hf) { if (hf->parsed){ switch(hf->type){ case HDR_VIA_T: free_via_list(hf->parsed); hf->parsed = NULL; break; case HDR_TO_T: free_to(hf->parsed); hf->parsed = NULL; break; case HDR_FROM_T: free_to(hf->parsed); hf->parsed = NULL; break; case HDR_CSEQ_T: free_cseq(hf->parsed); hf->parsed = NULL; break; case HDR_CALLID_T: break; case HDR_CONTACT_T: free_contact((contact_body_t**)(void*)(&(hf->parsed))); break; case HDR_MAXFORWARDS_T: break; case HDR_ROUTE_T: free_rr((rr_t**)(void*)(&hf->parsed)); break; case HDR_RECORDROUTE_T: free_rr((rr_t**)(void*)(&hf->parsed)); break; case HDR_PATH_T: free_rr((rr_t**)(void*)(&hf->parsed)); break; case HDR_CONTENTTYPE_T: free_contenttype((content_t**)(void*)(&hf->parsed)); break; case HDR_CONTENTLENGTH_T: break; case HDR_AUTHORIZATION_T: free_credentials((auth_body_t**)(void*)(&(hf->parsed))); break; case HDR_EXPIRES_T: free_expires((exp_body_t**)(void*)(&(hf->parsed))); break; case HDR_PROXYAUTH_T: free_credentials((auth_body_t**)(void*)(&(hf->parsed))); break; case HDR_SUPPORTED_T: free_supported((struct supported_body**)(void*)(&(hf->parsed))); break; case HDR_PROXYREQUIRE_T: break; case HDR_UNSUPPORTED_T: break; case HDR_ALLOW_T: free_allow((struct allow_body**)(void*)(&(hf->parsed))); break; case HDR_EVENT_T: free_event((event_t**)(void*)(&(hf->parsed))); break; case HDR_ACCEPT_T: pkg_free(hf->parsed); hf->parsed = NULL; break; case HDR_ACCEPTLANGUAGE_T: break; case HDR_ORGANIZATION_T: break; case HDR_PRIORITY_T: break; case HDR_SUBJECT_T: break; case HDR_USERAGENT_T: break; case HDR_ACCEPTDISPOSITION_T: break; case HDR_CONTENTDISPOSITION_T: free_disposition( ((struct disposition**)(void*)(&hf->parsed)) ); break; case HDR_DIVERSION_T: free_to(hf->parsed); hf->parsed = NULL; break; case HDR_RPID_T: free_to(hf->parsed); hf->parsed = NULL; break; case HDR_REFER_TO_T: free_to(hf->parsed); hf->parsed = NULL; break; case HDR_SESSION_EXPIRES_T: free_session_expires((struct session_expires*)hf->parsed ); hf->parsed = NULL; break; case HDR_MIN_SE_T: break; case HDR_MIN_EXPIRES_T: break; case HDR_PPI_T: free_to(hf->parsed); hf->parsed = NULL; break; case HDR_PAI_T: free_to(hf->parsed); hf->parsed = NULL; break; case HDR_PRIVACY_T: break; case HDR_RETRY_AFTER_T: break; case HDR_CALL_INFO_T: free_call_info(hf->parsed); hf->parsed = NULL; break; case HDR_WWW_AUTHENTICATE_T: case HDR_PROXY_AUTHENTICATE_T: free_authenticate((struct authenticate_body *)hf->parsed); hf->parsed = NULL; break; default: LM_CRIT("unknown header type %d\n", hf->type); break; } } } /* * Frees a hdr_field list, * WARNING: frees only ->parsed and ->next*/ void free_hdr_field_lst(struct hdr_field* hf) { struct hdr_field* foo; while(hf) { foo=hf; hf=hf->next; clean_hdr_field(foo); pkg_free(foo); } } void dump_hdr_field( struct hdr_field* hf ) { LM_ERR("type=%d, name=%.*s, body=%.*s, parsed=%p, next=%p\n", hf->type, hf->name.len, ZSW(hf->name.s), hf->body.len, ZSW(hf->body.s), hf->parsed, hf->next ); } opensips-2.2.2/parser/hf.h000066400000000000000000000204521300170765700154300ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2006-02-17 Session-Expires, Min-SE (dhsueh@somanetworks.com) * 2006-03-02 header of same type are linked as sibling (bogdan) */ /** * \file parser/hf.h * \brief Defines for SIP header types */ #ifndef HF_H #define HF_H #include "../str.h" /** * SIP Header types. * if you add a new type: * - make sure it's not greater than 63 * - make sure you add the corresponding flag to the hdr_flags_t defs below * - update clean_hdr_field (in hf.c) * - update sip_msg_cloner (modules/tm/sip_msg.c) * - update parse_headers (msg_parser.c) */ enum _hdr_types_t { HDR_ERROR_T = -1 /**< Error while parsing */, HDR_OTHER_T = 0 /**< Some other header field */, HDR_VIA_T = 1 /**< Via header field */, HDR_VIA1_T = 1 /**< First Via header field */, HDR_VIA2_T = 2 /**< only used as flag */, HDR_TO_T /**< To header field */, HDR_FROM_T /**< From header field */, HDR_CSEQ_T /**< CSeq header field */, HDR_CALLID_T /**< Call-Id header field */, HDR_CONTACT_T /**< Contact header field */, HDR_MAXFORWARDS_T /**< MaxForwards header field */, HDR_ROUTE_T /**< Route header field */, HDR_RECORDROUTE_T /**< Record-Route header field */, HDR_PATH_T /**< Path header fiels */, HDR_CONTENTTYPE_T /**< Content-Type header field */, HDR_CONTENTLENGTH_T /**< Content-Length header field */, HDR_AUTHORIZATION_T /**< Authorization header field */, HDR_EXPIRES_T /**< Expires header field */, HDR_PROXYAUTH_T /**< Proxy-Authorization hdr field */, HDR_SUPPORTED_T /**< Supported header field */, HDR_PROXYREQUIRE_T /**< Proxy-Require header field */, HDR_UNSUPPORTED_T /**< Unsupported header field */, HDR_ALLOW_T /**< Allow header field */, HDR_EVENT_T /**< Event header field */, HDR_ACCEPT_T /**< Accept header field */, HDR_ACCEPTLANGUAGE_T /**< Accept-Language header field */, HDR_ORGANIZATION_T /**< Organization header field */, HDR_PRIORITY_T /**< Priority header field */, HDR_SUBJECT_T /**< Subject header field */, HDR_USERAGENT_T /**< User-Agent header field */, HDR_ACCEPTDISPOSITION_T /**< Accept-Disposition hdr field */, HDR_CONTENTDISPOSITION_T /**< Content-Disposition hdr field */, HDR_DIVERSION_T /**< Diversion header field */, HDR_RPID_T /**< Remote-Party-ID header field */, HDR_REFER_TO_T /**< Refer-To header fiels */, HDR_SESSION_EXPIRES_T /**< Session-Expires header field */, HDR_MIN_SE_T /**< Min-SE header field */, HDR_PPI_T /**< P-Preferred-Identity header field */, HDR_PAI_T /**< P-Asserted-Identity header field */, HDR_PRIVACY_T /**< Privacy header field */, HDR_RETRY_AFTER_T /**< Retry-After header field */, HDR_CALL_INFO_T /**< Call-Info header field */, HDR_WWW_AUTHENTICATE_T /**< WWW-Authenticate header field */, HDR_PROXY_AUTHENTICATE_T /**< Proxy-Authenticate header field */, HDR_MIN_EXPIRES_T /**> Min-Expires header field */, HDR_EOH_T /**< Some other header field */ }; typedef unsigned long long hdr_flags_t; /** * Type to flag conversion * WARNING: HDR_ERROR_T has no corresponding FLAG! */ #define HDR_T2F(type) \ (((type)!=HDR_EOH_T)?((hdr_flags_t)1<<(type)):(~(hdr_flags_t)0)) /** helper macro for easy defining and keeping in sync. the flags enum */ #define HDR_F_DEF(name) HDR_T2F(HDR_##name##_T) /** * Flags definitions * (enums won't work with all the compilers (e.g. icc) due to the 64bit size) */ #define HDR_EOH_F HDR_F_DEF(EOH) #define HDR_VIA_F HDR_F_DEF(VIA) #define HDR_VIA1_F HDR_F_DEF(VIA1) #define HDR_VIA2_F HDR_F_DEF(VIA2) #define HDR_TO_F HDR_F_DEF(TO) #define HDR_FROM_F HDR_F_DEF(FROM) #define HDR_CSEQ_F HDR_F_DEF(CSEQ) #define HDR_CALLID_F HDR_F_DEF(CALLID) #define HDR_CONTACT_F HDR_F_DEF(CONTACT) #define HDR_MAXFORWARDS_F HDR_F_DEF(MAXFORWARDS) #define HDR_ROUTE_F HDR_F_DEF(ROUTE) #define HDR_RECORDROUTE_F HDR_F_DEF(RECORDROUTE) #define HDR_PATH_F HDR_F_DEF(PATH) #define HDR_CONTENTTYPE_F HDR_F_DEF(CONTENTTYPE) #define HDR_CONTENTLENGTH_F HDR_F_DEF(CONTENTLENGTH) #define HDR_AUTHORIZATION_F HDR_F_DEF(AUTHORIZATION) #define HDR_EXPIRES_F HDR_F_DEF(EXPIRES) #define HDR_PROXYAUTH_F HDR_F_DEF(PROXYAUTH) #define HDR_SUPPORTED_F HDR_F_DEF(SUPPORTED) #define HDR_PROXYREQUIRE_F HDR_F_DEF(PROXYREQUIRE) #define HDR_UNSUPPORTED_F HDR_F_DEF(UNSUPPORTED) #define HDR_ALLOW_F HDR_F_DEF(ALLOW) #define HDR_EVENT_F HDR_F_DEF(EVENT) #define HDR_ACCEPT_F HDR_F_DEF(ACCEPT) #define HDR_ACCEPTLANGUAGE_F HDR_F_DEF(ACCEPTLANGUAGE) #define HDR_ORGANIZATION_F HDR_F_DEF(ORGANIZATION) #define HDR_PRIORITY_F HDR_F_DEF(PRIORITY) #define HDR_SUBJECT_F HDR_F_DEF(SUBJECT) #define HDR_USERAGENT_F HDR_F_DEF(USERAGENT) #define HDR_ACCEPTDISPOSITION_F HDR_F_DEF(ACCEPTDISPOSITION) #define HDR_CONTENTDISPOSITION_F HDR_F_DEF(CONTENTDISPOSITION) #define HDR_DIVERSION_F HDR_F_DEF(DIVERSION) #define HDR_RPID_F HDR_F_DEF(RPID) #define HDR_REFER_TO_F HDR_F_DEF(REFER_TO) #define HDR_SESSION_EXPIRES_F HDR_F_DEF(SESSION_EXPIRES) #define HDR_MIN_SE_F HDR_F_DEF(MIN_SE) #define HDR_PPI_F HDR_F_DEF(PPI) #define HDR_PAI_F HDR_F_DEF(PAI) #define HDR_PRIVACY_F HDR_F_DEF(PRIVACY) #define HDR_RETRY_AFTER_F HDR_F_DEF(RETRY_AFTER) #define HDR_CALL_INFO_F HDR_F_DEF(CALL_INFO) #define HDR_WWW_AUTHENTICATE_F HDR_F_DEF(WWW_AUTHENTICATE) #define HDR_PROXY_AUTHENTICATE_F HDR_F_DEF(PROXY_AUTHENTICATE) #define HDR_MIN_EXPIRES_F HDR_F_DEF(MIN_EXPIRES) #define HDR_OTHER_F HDR_F_DEF(OTHER) typedef enum _hdr_types_t hdr_types_t; /** * Data structure for a SIP header. * Format: name':' body */ struct hdr_field { hdr_types_t type; /**< Header field type */ str name; /**< Header field name */ str body; /**< Header field body (may not include CRLF) */ int len; /**< length from hdr start until EoHF (incl.CRLF) */ void* parsed; /**< Parsed data structures */ struct hdr_field* next; /**< Next header field in the list */ struct hdr_field* sibling; /**< Next header of same type */ }; /* returns true if the header links allocated memory on parse field */ static inline int hdr_allocs_parse(struct hdr_field* hdr) { switch(hdr->type){ case HDR_VIA_T: case HDR_TO_T: case HDR_FROM_T: case HDR_CONTACT_T: case HDR_ROUTE_T: case HDR_RECORDROUTE_T: case HDR_PATH_T: case HDR_AUTHORIZATION_T: case HDR_EXPIRES_T: case HDR_PROXYAUTH_T: case HDR_EVENT_T: case HDR_ACCEPT_T: case HDR_CONTENTTYPE_T: case HDR_CONTENTDISPOSITION_T: case HDR_DIVERSION_T: case HDR_RPID_T: case HDR_REFER_TO_T: case HDR_SESSION_EXPIRES_T: case HDR_PPI_T: case HDR_PAI_T: case HDR_CALL_INFO_T: case HDR_WWW_AUTHENTICATE_T: case HDR_PROXY_AUTHENTICATE_T: case HDR_ALLOW_T: case HDR_SUPPORTED_T: return 1; default: return 0; } } /** * Frees a hdr_field structure. * WARNING: it frees only parsed (and not name.s, body.s) * * \param hf header that should be freed */ void clean_hdr_field(struct hdr_field* hf); /** * Frees a hdr_field list. * WARNING: frees only ->parsed and ->next * * \param hf header field that should be freed */ void free_hdr_field_lst(struct hdr_field* hf); /** * Output the contents of a header to the opensips logging system * with log level ERROR. * \param hf header that is dumped */ void dump_hdr_field( struct hdr_field* hf ); #endif /* HF_H */ opensips-2.2.2/parser/keys.h000066400000000000000000000142761300170765700160150ustar00rootroot00000000000000/* * Fast 32-bit Header Field Name Parser -- keys * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-05-01 _acce_ macro added (janakj) * 2003-05-01 Macros for Accept-Disposition added (janakj) * 2003-11-02 Macros for Diversion added (jh) * 2006-02-xx mod 100rel; add timer, Session-Expires, * Min-SE (dhsueh@somanetworks.com) */ #ifndef KEYS_H #define KEYS_H /* * a b c d e f g h i j k l m n o p q r s t u v w x y z : ' ' - * 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 3a 20 2d */ #define _gruu_ 0x75757267 /* "gruu" */ #define _even_ 0x6e657665 /* "even" */ #define _tlis_ 0x73696c74 /* "tlis" */ #define _acce_ 0x65636361 /* "acce" */ #define _allo_ 0x6f6c6c61 /* "allo" */ #define _auth_ 0x68747561 /* "auth" */ #define _oriz_ 0x7a69726f /* "oriz" */ #define _atio_ 0x6f697461 /* "atio" */ #define _call_ 0x6c6c6163 /* "call" */ #define __id2_ 0x2064692d /* "-id " */ #define __id1_ 0x3a64692d /* "-id:" */ #define __inf_ 0x666e692d /* "-inf" for Call-Info */ #define _cont_ 0x746e6f63 /* "cont" */ #define _act2_ 0x20746361 /* "act " */ #define _act1_ 0x3a746361 /* "act:" */ #define _ent__ 0x2d746e65 /* "ent-" */ #define _leng_ 0x676e656c /* "leng" */ #define _th12_ 0x203a6874 /* "th: " */ #define _type_ 0x65707974 /* "type" */ #define _cseq_ 0x71657363 /* "cseq" */ #define _expi_ 0x69707865 /* "expi" */ #define _res2_ 0x20736572 /* "res " */ #define _res1_ 0x3a736572 /* "res:" */ #define _from_ 0x6d6f7266 /* "from" */ #define _max__ 0x2d78616d /* "max-" */ #define _forw_ 0x77726f66 /* "forw" */ #define _ards_ 0x73647261 /* "ards" */ #define _prox_ 0x786f7270 /* "prox" */ #define _y_au_ 0x75612d79 /* "y-au" */ #define _then_ 0x6e656874 /* "then" */ #define _tica_ 0x61636974 /* "tica" */ #define _thor_ 0x726f6874 /* "thor" */ #define _izat_ 0x74617a69 /* "izat" */ #define _ion2_ 0x206e6f69 /* "ion " */ #define _ion1_ 0x3a6e6f69 /* "ion:" */ #define _y_re_ 0x65722d79 /* "y-re" */ #define _quir_ 0x72697571 /* "quir" */ #define _reco_ 0x6f636572 /* "reco" */ #define _rd_r_ 0x722d6472 /* "rd-r" */ #define _oute_ 0x6574756f /* "oute" */ #define _requ_ 0x75716572 /* "requ" */ #define _ire2_ 0x20657269 /* "ire " */ #define _ire1_ 0x3a657269 /* "ire:" */ #define _rout_ 0x74756f72 /* "rout" */ #define _path_ 0x68746170 /* "path" */ #define _supp_ 0x70707573 /* "supp" */ #define _orte_ 0x6574726f /* "orte" */ #define _to12_ 0x203a6f74 /* "to: " */ #define _unsu_ 0x75736e75 /* "unsu" */ #define _ppor_ 0x726f7070 /* "ppor" */ #define _ted2_ 0x20646574 /* "ted " */ #define _ted1_ 0x3a646574 /* "ted:" */ #define _via2_ 0x20616976 /* "via " */ #define _via1_ 0x3a616976 /* "via:" */ #define _www__ 0x2d777777 /* "www-" */ #define _enti_ 0x69746e65 /* "enti" */ #define _cate_ 0x65746163 /* "cate" */ #define _even_ 0x6e657665 /* "even" */ #define _pt_l_ 0x6c2d7470 /* "pt-l" */ #define _angu_ 0x75676e61 /* "angu" */ #define _age2_ 0x20656761 /* "age " */ #define _age1_ 0x3a656761 /* "age:" */ #define _orga_ 0x6167726f /* "orga" */ #define _niza_ 0x617a696e /* "niza" */ #define _tion_ 0x6e6f6974 /* "tion" */ #define _prio_ 0x6f697270 /* "prio" */ #define _rity_ 0x79746972 /* "rity" */ #define _subj_ 0x6a627573 /* "subj" */ #define _ect2_ 0x20746365 /* "ect " */ #define _ect1_ 0x3a746365 /* "ect:" */ #define _user_ 0x72657375 /* "user" */ #define __age_ 0x6567612d /* "-age" */ #define _disp_ 0x70736964 /* "disp" */ #define _osit_ 0x7469736f /* "osit" */ #define _ion2_ 0x206e6f69 /* "ion " */ #define _ion1_ 0x3a6e6f69 /* "ion:" */ #define _pt_d_ 0x64617470 /* "pt-d" */ #define _ispo_ 0x6f707369 /* "ispo" */ #define _siti_ 0x69746973 /* "siti" */ #define _dive_ 0x65766964 /* "dive" */ #define _rsio_ 0x6f697372 /* "rsio" */ #define _remo_ 0x6f6d6572 /* "remo" */ #define _te_p_ 0x702d6574 /* "te-p" */ #define _arty_ 0x79747261 /* "arty" */ #define __id2_ 0x2064692d /* "-id " */ #define __id1_ 0x3a64692d /* "-id:" */ #define _refe_ 0x65666572 /* "refe" */ #define _r_to_ 0x6f742d72 /* "r-to" */ #define _100r_ 0x72303031 /* "100r" for "100rel" */ #define _time_ 0x656d6974 /* "time" */ #define _sess_ 0x73736573 /* "sess" */ #define _ion__ 0x2d6e6f69 /* "ion-" */ #define _expi_ 0x69707865 /* "expi" */ #define _res2_ 0x20736572 /* "res " */ #define _res1_ 0x3a736572 /* "res:" */ #define _min__ 0x2d6e696d /* "min-" for "min-se" or "min-expires" */ #define _expi_ 0x69707865 /* "expi" */ #define _res2_ 0x20736572 /* "res " */ #define _res1_ 0x3a736572 /* "res:" */ #define _p_pr_ 0x72702d70 /* "p-pr" for "p-preferred-identity" */ #define _efer_ 0x72656665 /* "efer" */ #define _red__ 0x2d646572 /* "red-" */ #define _iden_ 0x6e656469 /* "iden" */ #define _tity_ 0x79746974 /* "tity" */ #define _p_as_ 0x73612d70 /* "p-as" for "p-asserted-identity" */ #define _sert_ 0x74726573 /* "sert" */ #define _ed_i_ 0x692d6465 /* "ed-i" */ #define _dent_ 0x746e6564 /* "dent" */ #define _ity2_ 0x20797469 /* "ity " */ #define _ity1_ 0x3a797469 /* "ity:" */ #define _priv_ 0x76697270 /* "priv" for "privacy" */ #define _acy2_ 0x20796361 /* "acy " */ #define _acy1_ 0x3a796361 /* "acy:" */ #define _retr_ 0x72746572 /* "retr" for "retry-after" */ #define _y_af_ 0x66612d79 /* "y-af" */ #define _ter2_ 0x20726574 /* "ter " */ #define _ter1_ 0x3a726574 /* "ter:" */ #endif /* KEYS_H */ opensips-2.2.2/parser/msg_parser.c000066400000000000000000000611121300170765700171660ustar00rootroot00000000000000/* * sip msg. header proxy parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-29 scrathcpad removed (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2003-03-31 removed msg->repl_add_rm (andrei) * 2003-04-26 ZSW (jiri) * 2003-05-01 parser extended to support Accept header field (janakj) * 2005-03-02 free_via_list(vb) on via parse error (andrei) * 2006-02-17 Session-Expires, Min-SE (dhsueh@somanetworks.com) * 2006-03-02 header of same type are linked as sibling (bogdan) * 2006-11-28 Added statistic support for bad message headers. * (Jeffrey Magder - SOMA Networks) * 2008-09-09 Added sdp parsing support (osas) */ #include #include #include "msg_parser.h" #include "parser_f.h" #include "../ut.h" #include "../error.h" #include "../dprint.h" #include "../data_lump_rpl.h" #include "../mem/mem.h" #include "../error.h" #include "../globals.h" #include "../core_stats.h" #include "../errinfo.h" #include "../dset.h" #include "parse_hname2.h" #include "parse_uri.h" #include "parse_content.h" #include "../msg_callbacks.h" #ifdef DEBUG_DMALLOC #include #endif #define parse_hname(_b,_e,_h) parse_hname2((_b),(_e),(_h)) /* number of via's encountered */ int via_cnt; /* returns pointer to next header line, and fill hdr_f ; * if at end of header returns pointer to the last crlf (always buf)*/ char* get_hdr_field(char* buf, char* end, struct hdr_field* hdr) { char* tmp; char *match; struct via_body *vb; struct cseq_body* cseq_b; struct to_body* to_b; int integer; if ((*buf)=='\n' || (*buf)=='\r'){ /* double crlf or lflf or crcr */ LM_DBG("found end of header\n"); hdr->type=HDR_EOH_T; return buf; } tmp=parse_hname(buf, end, hdr); if (hdr->type==HDR_ERROR_T){ LM_ERR("bad header\n"); goto error_bad_hdr; } /* eliminate leading whitespace */ tmp=eat_lws_end(tmp, end); if (tmp>=end) { LM_ERR("hf empty\n"); goto error_bad_hdr; } /* if header-field well-known, parse it, find its end otherwise ; * after leaving the hdr->type switch, tmp should be set to the * next header field */ switch(hdr->type){ case HDR_VIA_T: /* keep number of vias parsed -- we want to report it in replies for diagnostic purposes */ via_cnt++; vb=pkg_malloc(sizeof(struct via_body)); if (vb==0){ LM_ERR("out of pkg memory\n"); goto error; } memset(vb,0,sizeof(struct via_body)); hdr->body.s=tmp; tmp=parse_via(tmp, end, vb); if (vb->error==PARSE_ERROR){ LM_ERR("bad via\n"); free_via_list(vb); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing Via"); set_err_reply(400, "bad Via header"); goto error; } hdr->parsed=vb; vb->hdr.s=hdr->name.s; vb->hdr.len=hdr->name.len; hdr->body.len=tmp-hdr->body.s; break; case HDR_CSEQ_T: cseq_b=pkg_malloc(sizeof(struct cseq_body)); if (cseq_b==0){ LM_ERR("out of pkg memory\n"); goto error; } memset(cseq_b, 0, sizeof(struct cseq_body)); hdr->body.s=tmp; tmp=parse_cseq(tmp, end, cseq_b); if (cseq_b->error==PARSE_ERROR){ LM_ERR("bad cseq\n"); pkg_free(cseq_b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing CSeq`"); set_err_reply(400, "bad CSeq header"); goto error; } hdr->parsed=cseq_b; hdr->body.len=tmp-hdr->body.s; LM_DBG("cseq <%.*s>: <%.*s> <%.*s>\n", hdr->name.len, ZSW(hdr->name.s), cseq_b->number.len, ZSW(cseq_b->number.s), cseq_b->method.len, cseq_b->method.s); break; case HDR_TO_T: to_b=pkg_malloc(sizeof(struct to_body)); if (to_b==0){ LM_ERR("out of pkg memory\n"); goto error; } memset(to_b, 0, sizeof(struct to_body)); hdr->body.s=tmp; tmp=parse_to(tmp, end,to_b); if (to_b->error==PARSE_ERROR){ LM_ERR("bad to header\n"); pkg_free(to_b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing To header"); set_err_reply(400, "bad header"); goto error; } hdr->parsed=to_b; hdr->body.len=tmp-hdr->body.s; LM_DBG("<%.*s> [%d]; uri=[%.*s] \n", hdr->name.len, ZSW(hdr->name.s), hdr->body.len, to_b->uri.len,ZSW(to_b->uri.s)); LM_DBG("to body [%.*s]\n",to_b->body.len, ZSW(to_b->body.s)); break; case HDR_CONTENTLENGTH_T: hdr->body.s=tmp; tmp=parse_content_length(tmp,end, &integer); if (tmp==0){ LM_ERR("bad content_length header\n"); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing Content-Length"); set_err_reply(400, "bad Content-Length header"); goto error; } hdr->parsed=(void*)(long)integer; hdr->body.len=tmp-hdr->body.s; LM_DBG("content_length=%d\n", (int)(long)hdr->parsed); break; case HDR_SUPPORTED_T: case HDR_CONTENTTYPE_T: case HDR_FROM_T: case HDR_CALLID_T: case HDR_CONTACT_T: case HDR_ROUTE_T: case HDR_RECORDROUTE_T: case HDR_PATH_T: case HDR_MAXFORWARDS_T: case HDR_AUTHORIZATION_T: case HDR_EXPIRES_T: case HDR_PROXYAUTH_T: case HDR_PROXYREQUIRE_T: case HDR_UNSUPPORTED_T: case HDR_ALLOW_T: case HDR_EVENT_T: case HDR_ACCEPT_T: case HDR_ACCEPTLANGUAGE_T: case HDR_ORGANIZATION_T: case HDR_PRIORITY_T: case HDR_SUBJECT_T: case HDR_USERAGENT_T: case HDR_CONTENTDISPOSITION_T: case HDR_ACCEPTDISPOSITION_T: case HDR_DIVERSION_T: case HDR_RPID_T: case HDR_REFER_TO_T: case HDR_SESSION_EXPIRES_T: case HDR_MIN_SE_T: case HDR_MIN_EXPIRES_T: case HDR_PPI_T: case HDR_PAI_T: case HDR_PRIVACY_T: case HDR_RETRY_AFTER_T: case HDR_CALL_INFO_T: case HDR_WWW_AUTHENTICATE_T: case HDR_PROXY_AUTHENTICATE_T: case HDR_OTHER_T: /* just skip over it */ hdr->body.s=tmp; /* find end of header */ /* find lf */ do{ match=q_memchr(tmp, '\n', end-tmp); if (match){ match++; }else { LM_ERR("bad body for <%s>(%d)\n", hdr->name.s, hdr->type); tmp=end; goto error_bad_hdr; } tmp=match; }while( matchbody.len=match-hdr->body.s; break; default: LM_CRIT("unknown header type %d\n", hdr->type); goto error; } /* jku: if \r covered by current length, shrink it */ trim_r( hdr->body ); hdr->len=tmp-hdr->name.s; return tmp; error_bad_hdr: set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing headers"); set_err_reply(400, "bad headers"); error: LM_DBG("error exit\n"); update_stat( bad_msg_hdr, 1); hdr->type=HDR_ERROR_T; hdr->len=tmp-hdr->name.s; return tmp; } /* parse the headers and adds them to msg->headers and msg->to, from etc. * It stops when all the headers requested in flags were parsed, on error * (bad header) or end of headers */ /* note: it continues where it previously stopped and goes ahead until end is encountered or desired HFs are found; if you call it twice for the same HF which is present only once, it will fail the second time; if you call it twice and the HF is found on second time too, it's not replaced in the well-known HF pointer but just added to header list; if you want to use a dumb convenience function which will give you the first occurrence of a header you are interested in, look at check_transaction_quadruple */ int parse_headers(struct sip_msg* msg, hdr_flags_t flags, int next) { struct hdr_field *hf; struct hdr_field *itr; char* tmp; char* rest; char* end; hdr_flags_t orig_flag; #define link_sibling_hdr(_hook, _hdr) \ do{ \ if (msg->_hook==0) msg->_hook=_hdr;\ else {\ for(itr=msg->_hook;itr->sibling;itr=itr->sibling);\ itr->sibling = _hdr;\ }\ }while(0) end=msg->buf+msg->len; tmp=msg->unparsed; if (next) { orig_flag = msg->parsed_flag; msg->parsed_flag &= ~flags; }else orig_flag=0; LM_DBG("flags=%llx\n", (unsigned long long)flags); while( tmpparsed_flag) != flags){ hf=pkg_malloc(sizeof(struct hdr_field)); if (hf==0){ ser_error=E_OUT_OF_MEM; LM_ERR("pkg memory allocation failed\n"); goto error; } memset(hf,0, sizeof(struct hdr_field)); hf->type=HDR_ERROR_T; rest=get_hdr_field(tmp, msg->buf+msg->len, hf); switch (hf->type){ case HDR_ERROR_T: LM_INFO("bad header field\n"); goto error; case HDR_EOH_T: msg->eoh=tmp; /* or rest?*/ msg->parsed_flag|=HDR_EOH_F; pkg_free(hf); goto skip; case HDR_OTHER_T: /*do nothing*/ break; case HDR_CALLID_T: if (msg->callid==0) msg->callid=hf; msg->parsed_flag|=HDR_CALLID_F; break; case HDR_TO_T: if (msg->to==0) msg->to=hf; msg->parsed_flag|=HDR_TO_F; break; case HDR_CSEQ_T: if (msg->cseq==0) msg->cseq=hf; msg->parsed_flag|=HDR_CSEQ_F; break; case HDR_FROM_T: if (msg->from==0) msg->from=hf; msg->parsed_flag|=HDR_FROM_F; break; case HDR_CONTACT_T: link_sibling_hdr(contact,hf); msg->parsed_flag|=HDR_CONTACT_F; break; case HDR_MAXFORWARDS_T: if(msg->maxforwards==0) msg->maxforwards=hf; msg->parsed_flag|=HDR_MAXFORWARDS_F; break; case HDR_ROUTE_T: link_sibling_hdr(route,hf); msg->parsed_flag|=HDR_ROUTE_F; break; case HDR_RECORDROUTE_T: link_sibling_hdr(record_route,hf); msg->parsed_flag|=HDR_RECORDROUTE_F; break; case HDR_PATH_T: link_sibling_hdr(path,hf); msg->parsed_flag|=HDR_PATH_F; break; case HDR_CONTENTTYPE_T: if (msg->content_type==0) msg->content_type = hf; msg->parsed_flag|=HDR_CONTENTTYPE_F; break; case HDR_CONTENTLENGTH_T: if (msg->content_length==0) msg->content_length = hf; msg->parsed_flag|=HDR_CONTENTLENGTH_F; break; case HDR_AUTHORIZATION_T: link_sibling_hdr(authorization,hf); msg->parsed_flag|=HDR_AUTHORIZATION_F; break; case HDR_EXPIRES_T: if (msg->expires==0) msg->expires = hf; msg->parsed_flag|=HDR_EXPIRES_F; break; case HDR_PROXYAUTH_T: link_sibling_hdr(proxy_auth,hf); msg->parsed_flag|=HDR_PROXYAUTH_F; break; case HDR_PROXYREQUIRE_T: link_sibling_hdr(proxy_require,hf); msg->parsed_flag|=HDR_PROXYREQUIRE_F; break; case HDR_SUPPORTED_T: link_sibling_hdr(supported,hf); msg->parsed_flag|=HDR_SUPPORTED_F; break; case HDR_UNSUPPORTED_T: link_sibling_hdr(unsupported,hf); msg->parsed_flag|=HDR_UNSUPPORTED_F; break; case HDR_ALLOW_T: link_sibling_hdr(allow,hf); msg->parsed_flag|=HDR_ALLOW_F; break; case HDR_EVENT_T: link_sibling_hdr(event,hf); msg->parsed_flag|=HDR_EVENT_F; break; case HDR_ACCEPT_T: link_sibling_hdr(accept,hf); msg->parsed_flag|=HDR_ACCEPT_F; break; case HDR_ACCEPTLANGUAGE_T: link_sibling_hdr(accept_language,hf); msg->parsed_flag|=HDR_ACCEPTLANGUAGE_F; break; case HDR_ORGANIZATION_T: if (msg->organization==0) msg->organization = hf; msg->parsed_flag|=HDR_ORGANIZATION_F; break; case HDR_PRIORITY_T: if (msg->priority==0) msg->priority = hf; msg->parsed_flag|=HDR_PRIORITY_F; break; case HDR_SUBJECT_T: if (msg->subject==0) msg->subject = hf; msg->parsed_flag|=HDR_SUBJECT_F; break; case HDR_USERAGENT_T: if (msg->user_agent==0) msg->user_agent = hf; msg->parsed_flag|=HDR_USERAGENT_F; break; case HDR_CONTENTDISPOSITION_T: if (msg->content_disposition==0) msg->content_disposition = hf; msg->parsed_flag|=HDR_CONTENTDISPOSITION_F; break; case HDR_ACCEPTDISPOSITION_T: link_sibling_hdr(accept_disposition,hf); msg->parsed_flag|=HDR_ACCEPTDISPOSITION_F; break; case HDR_DIVERSION_T: link_sibling_hdr(diversion,hf); msg->parsed_flag|=HDR_DIVERSION_F; break; case HDR_RPID_T: if (msg->rpid==0) msg->rpid = hf; msg->parsed_flag|=HDR_RPID_F; break; case HDR_CALL_INFO_T: link_sibling_hdr(call_info,hf); msg->parsed_flag|=HDR_CALL_INFO_F; break; case HDR_WWW_AUTHENTICATE_T: link_sibling_hdr(www_authenticate,hf); msg->parsed_flag|=HDR_WWW_AUTHENTICATE_F; break; case HDR_PROXY_AUTHENTICATE_T: link_sibling_hdr(proxy_authenticate,hf); msg->parsed_flag|=HDR_PROXY_AUTHENTICATE_F; break; case HDR_REFER_TO_T: if (msg->refer_to==0) msg->refer_to = hf; msg->parsed_flag|=HDR_REFER_TO_F; break; case HDR_SESSION_EXPIRES_T: if ( msg->session_expires == 0 ) msg->session_expires = hf; msg->parsed_flag |= HDR_SESSION_EXPIRES_F; break; case HDR_MIN_SE_T: if ( msg->min_se == 0 ) msg->min_se = hf; msg->parsed_flag |= HDR_MIN_SE_F; break; case HDR_MIN_EXPIRES_T: if ( msg->min_expires == 0 ) msg->min_expires = hf; msg->parsed_flag |= HDR_MIN_EXPIRES_F; break; case HDR_PPI_T: if (msg->ppi==0) msg->ppi = hf; msg->parsed_flag|=HDR_PPI_F; break; case HDR_PAI_T: if (msg->pai==0) msg->pai = hf; msg->parsed_flag|=HDR_PAI_F; break; case HDR_PRIVACY_T: if (msg->privacy==0) msg->privacy = hf; msg->parsed_flag|=HDR_PRIVACY_F; break; case HDR_RETRY_AFTER_T: break; case HDR_VIA_T: link_sibling_hdr(h_via1,hf); msg->parsed_flag|=HDR_VIA_F; LM_DBG("via found, flags=%llx\n", (unsigned long long)flags); if (msg->via1==0) { LM_DBG("this is the first via\n"); msg->h_via1=hf; msg->via1=hf->parsed; if (msg->via1->next){ msg->via2=msg->via1->next; msg->parsed_flag|=HDR_VIA2_F; } }else if (msg->via2==0){ msg->h_via2=hf; msg->via2=hf->parsed; msg->parsed_flag|=HDR_VIA2_F; LM_DBG("parse_headers: this is the second via\n"); } break; default: LM_CRIT("unknown header type %d\n", hf->type); goto error; } /* add the header to the list*/ if (msg->last_header==0){ msg->headers=hf; msg->last_header=hf; }else{ msg->last_header->next=hf; msg->last_header=hf; } #ifdef EXTRA_DEBUG LM_DBG("header field type %d, name=<%.*s>, body=<%.*s>\n", hf->type, hf->name.len, ZSW(hf->name.s), hf->body.len, ZSW(hf->body.s)); #endif tmp=rest; } skip: msg->unparsed=tmp; return 0; error: ser_error=E_BAD_REQ; if (hf) pkg_free(hf); if (next) msg->parsed_flag |= orig_flag; return -1; } /* returns 0 if ok, -1 for errors */ int parse_msg(char* buf, unsigned int len, struct sip_msg* msg) { char *tmp; char* rest; struct msg_start *fl; int offset; hdr_flags_t flags; /* eat crlf from the beginning */ for (tmp=buf; (*tmp=='\n' || *tmp=='\r')&& (unsigned int)(tmp-buf) < len ; tmp++); offset=tmp-buf; fl=&(msg->first_line); rest=parse_first_line(tmp, len-offset, fl); offset+=rest-tmp; tmp=rest; switch(fl->type){ case SIP_INVALID: LM_DBG("invalid message\n"); /* if failed to parse the first line, we simply consider that the whole buffer was parsed, so that nothing is left to be parsed :) - this will do the trick and make "msg" struct acceptable for following parsing attempts */ msg->unparsed = msg->buf + msg->len; goto error; break; case SIP_REQUEST: LM_DBG("SIP Request:\n"); LM_DBG(" method: <%.*s>\n",fl->u.request.method.len, ZSW(fl->u.request.method.s)); LM_DBG(" uri: <%.*s>\n",fl->u.request.uri.len, ZSW(fl->u.request.uri.s)); LM_DBG(" version: <%.*s>\n",fl->u.request.version.len, ZSW(fl->u.request.version.s)); flags=HDR_VIA_F; break; case SIP_REPLY: LM_DBG("SIP Reply (status):\n"); LM_DBG(" version: <%.*s>\n",fl->u.reply.version.len, ZSW(fl->u.reply.version.s)); LM_DBG(" status: <%.*s>\n", fl->u.reply.status.len, ZSW(fl->u.reply.status.s)); LM_DBG(" reason: <%.*s>\n", fl->u.reply.reason.len, ZSW(fl->u.reply.reason.s)); flags=HDR_VIA_F; break; default: LM_DBG("unknown type %d\n",fl->type); goto error; } msg->unparsed=tmp; /*find first Via: */ if (parse_headers(msg, flags, 0)==-1) goto error; #ifdef EXTRA_DEBUG /* dump parsed data */ if (msg->via1){ LM_DBG(" first via: <%.*s/%.*s/%.*s> <%.*s:%.*s(%d)>", msg->via1->name.len, ZSW(msg->via1->name.s), msg->via1->version.len, ZSW(msg->via1->version.s), msg->via1->transport.len, ZSW(msg->via1->transport.s), msg->via1->host.len, ZSW(msg->via1->host.s), msg->via1->port_str.len, ZSW(msg->via1->port_str.s), msg->via1->port); if (msg->via1->params.s) LM_DBG(";<%.*s>", msg->via1->params.len, ZSW(msg->via1->params.s)); if (msg->via1->comment.s) LM_DBG(" <%.*s>", msg->via1->comment.len, ZSW(msg->via1->comment.s)); LM_DBG ("\n"); } if (msg->via2){ LM_DBG(" first via: <%.*s/%.*s/%.*s> <%.*s:%.*s(%d)>", msg->via2->name.len, ZSW(msg->via2->name.s), msg->via2->version.len, ZSW(msg->via2->version.s), msg->via2->transport.len, ZSW(msg->via2->transport.s), msg->via2->host.len, ZSW(msg->via2->host.s), msg->via2->port_str.len, ZSW(msg->via2->port_str.s), msg->via2->port); if (msg->via2->params.s) LM_DBG(";<%.*s>", msg->via2->params.len, ZSW(msg->via2->params.s)); if (msg->via2->comment.s) LM_DBG(" <%.*s>", msg->via2->comment.len, ZSW(msg->via2->comment.s)); LM_DBG ("\n"); } #endif #ifdef EXTRA_DEBUG LM_DBG("exiting\n"); #endif return 0; error: /* more debugging, msg->orig is/should be null terminated*/ LM_ERR("message=<%.*s>\n", (int)len, ZSW(buf)); return -1; } void free_reply_lump( struct lump_rpl *lump) { struct lump_rpl *foo, *bar; for(foo=lump;foo;) { bar=foo->next; free_lump_rpl(foo); foo = bar; } } /*only the content*/ void free_sip_msg(struct sip_msg* msg) { if (msg->msg_cb) { msg_callback_process(msg, MSG_DESTROY, NULL); } if (msg->new_uri.s) { pkg_free(msg->new_uri.s); msg->new_uri.len=0; } if (msg->set_global_address.s) { pkg_free(msg->set_global_address.s); msg->set_global_address.s = NULL; } if (msg->set_global_port.s) { pkg_free(msg->set_global_port.s); msg->set_global_port.s = NULL; } if (msg->dst_uri.s) { pkg_free(msg->dst_uri.s); msg->dst_uri.len=0; } if (msg->path_vec.s) { pkg_free(msg->path_vec.s); msg->path_vec.len=0; } if (msg->headers) free_hdr_field_lst(msg->headers); if (msg->sdp) free_sdp(&(msg->sdp)); if (msg->add_rm) free_lump_list(msg->add_rm); if (msg->body_lumps) free_lump_list(msg->body_lumps); if (msg->reply_lump) free_reply_lump(msg->reply_lump); if (msg->multi ) { free_multi_body(msg->multi);msg->multi = 0;} /* don't free anymore -- now a pointer to a static buffer */ # ifdef DYN_BUF pkg_free(msg->buf); # endif } /* make sure all HFs needed for transaction identification have been parsed; return 0 if those HFs can't be found */ int check_transaction_quadruple( struct sip_msg* msg ) { if ( parse_headers(msg, HDR_FROM_F|HDR_TO_F|HDR_CALLID_F|HDR_CSEQ_F,0)!=-1 && msg->from && msg->to && msg->callid && msg->cseq ) { return 1; } else { ser_error=E_BAD_TUPEL; return 0; } } /* * Make a private copy of the string and assign it to new_uri */ int set_ruri(struct sip_msg *msg, str *uri) { if (!msg || !uri) { LM_ERR("invalid parameter value\n"); return -1; } /* strange/corrupt input: best to assume it's an empty URI */ if (!uri->s || uri->len == 0) { pkg_free(msg->new_uri.s); memset(&msg->new_uri, 0, sizeof msg->new_uri); return 0; } if (msg->new_uri.s && (msg->new_uri.len >= uri->len)) { memcpy(msg->new_uri.s, uri->s, uri->len); msg->new_uri.len = uri->len; } else { msg->new_uri.s = pkg_realloc(msg->new_uri.s, uri->len + 1); if (!msg->new_uri.s) { LM_ERR("not enough pkg memory (%d)\n",uri->len); return -1; } memcpy(msg->new_uri.s, uri->s, uri->len); msg->new_uri.len = uri->len; } set_ruri_q(msg, Q_UNSPECIFIED); msg->parsed_uri_ok = 0; return 0; } /* * Make a private copy of the string and assign it to dst_uri */ int set_dst_uri(struct sip_msg *msg, str *uri) { if (!msg || !uri) { LM_ERR("invalid parameter value\n"); return -1; } /* strange/corrupt input: best to assume it's an empty URI */ if (!uri->s || uri->len == 0) { pkg_free(msg->dst_uri.s); memset(&msg->dst_uri, 0, sizeof msg->dst_uri); return 0; } if (msg->dst_uri.s && (msg->dst_uri.len >= uri->len)) { memcpy(msg->dst_uri.s, uri->s, uri->len); msg->dst_uri.len = uri->len; } else { msg->dst_uri.s = pkg_realloc(msg->dst_uri.s, uri->len); if (!msg->dst_uri.s) { LM_ERR("not enough pkg memory\n"); return -1; } memcpy(msg->dst_uri.s, uri->s, uri->len); msg->dst_uri.len = uri->len; } return 0; } /* * Make a private copy of the string and assign it to path_vec */ int set_path_vector(struct sip_msg *msg, str *path) { if (!msg || !path) { LM_ERR("invalid parameter value\n"); return -1; } /* strange/corrupt input: best to assume it's an empty URI */ if (!path->s || path->len == 0) { pkg_free(msg->path_vec.s); memset(&msg->path_vec, 0, sizeof msg->path_vec); return 0; } if (msg->path_vec.s && (msg->path_vec.len >= path->len)) { memcpy(msg->path_vec.s, path->s, path->len); msg->path_vec.len = path->len; } else { msg->path_vec.s = pkg_realloc(msg->path_vec.s, path->len); if (!msg->path_vec.s) { LM_ERR("not enough pkg memory\n"); return -1; } memcpy(msg->path_vec.s, path->s, path->len); msg->path_vec.len = path->len; } return 0; } /* convenience macros */ #define LC(_cp) ((*(_cp))|0x20) #define SET_FOUND(_new_state) \ do{\ fill->s=b;fill->len=p-b;\ LM_DBG("hdr %d extracted as <%.*s>\n",\ flag,fill->len,fill->s);\ flags&=~(flag);\ if (flags) {state=_new_state;}\ else {goto done;}\ }while(0) #define GET_CSEQ() \ do{\ for(p++;ps=b;plen=p-fill->s;\ if ( (flags&=~(flag))==0) goto done;\ state=1;\ }while(0) int extract_ftc_hdrs( char *buf, int len, str *from, str *to, str *cseq,str *callid) { char *end, *p; char *b; str *fill; int state; int flags; int flag; p = buf; end = buf+len; state = 1; b = 0; flags = ((from!=0)?0x1:0) | ((to!=0)?0x2:0) | ((cseq!=0)?0x4:0) | ((callid!=0)?0x8:0); flag = 0; fill = 0; LM_DBG("flags = %d\n",flags); while(prepl_add_rm (andrei) * 2003-04-01 2 macros added: GET_NEXT_HOP and GET_RURI (janakj) * 2003-04-04 structure for parsed inbound uri added (jiri) * 2003-04-11 updated the sip_uri structure (lots of fields added) (andrei) * 2003-04-12 added msg_flags to sip_msg (andrei) * 2003-11-02 added diversion header field to sip_msg (jh) * 2004-11-08 added force_send_socket (andrei) * 2005-02-25 uri types added (sip, sips & tel) (andrei) * 2006-02-17 Session-Expires, Min-SE (dhsueh@somanetworks.com) * 2007-09-09 added sdp structure (osas) * 2011-04-20 added support for URI unknown parameters (osas) */ #ifndef MSG_PARSER_H #define MSG_PARSER_H #include #include "../str.h" #include "../lump_struct.h" #include "../flags.h" #include "../ip_addr.h" #include "../md5utils.h" #include "../qvalue.h" #include "../config.h" #include "parse_def.h" #include "parse_cseq.h" #include "parse_content.h" #include "parse_via.h" #include "parse_fline.h" #include "parse_multipart.h" #include "hf.h" #include "sdp/sdp.h" /* convenience short-cut macros */ #define REQ_LINE(_msg) ((_msg)->first_line.u.request) #define REQ_METHOD first_line.u.request.method_value #define REPLY_STATUS first_line.u.reply.statuscode #define REPLY_CLASS(_reply) ((_reply)->REPLY_STATUS/100) /* number methods as power of two to allow bitmap matching */ enum request_method { METHOD_UNDEF=0, /* 0 - --- */ METHOD_INVITE=1, /* 1 - 2^0 */ METHOD_CANCEL=2, /* 2 - 2^1 */ METHOD_ACK=4, /* 3 - 2^2 */ METHOD_BYE=8, /* 4 - 2^3 */ METHOD_INFO=16, /* 5 - 2^4 */ METHOD_OPTIONS=32, /* 6 - 2^5 */ METHOD_UPDATE=64, /* 7 - 2^6 */ METHOD_REGISTER=128, /* 8 - 2^7 */ METHOD_MESSAGE=256, /* 9 - 2^8 */ METHOD_SUBSCRIBE=512, /* 10 - 2^9 */ METHOD_NOTIFY=1024, /* 11 - 2^10 */ METHOD_PRACK=2048, /* 12 - 2^11 */ METHOD_REFER=4096, /* 13 - 2^12 */ METHOD_PUBLISH=8192, /* 14 - 2^13 */ METHOD_OTHER=16384 /* 15 - 2^14 */ }; #define FL_FORCE_RPORT (1<<0) /* force rport (top via) */ #define FL_FORCE_ACTIVE (1<<1) /* force active SDP */ #define FL_FORCE_LOCAL_RPORT (1<<2) /* force local rport (local via) */ #define FL_SDP_IP_AFS (1<<3) /* SDP IP rewritten */ #define FL_SDP_PORT_AFS (1<<4) /* SDP port rewritten */ #define FL_SHM_CLONE (1<<5) /* msg cloned in SHM as a single chunk */ #define FL_USE_UAC_FROM (1<<6) /* take FROM hdr from UAC insteas of UAS*/ #define FL_USE_UAC_TO (1<<7) /* take TO hdr from UAC insteas of UAS */ #define FL_USE_UAC_CSEQ (1<<8) /* take CSEQ hdr from UAC insteas of UAS*/ #define FL_REQ_UPSTREAM (1<<9) /* it's an upstream going request */ #define FL_DO_KEEPALIVE (1<<10) /* keepalive request's source after a * positive reply */ #define FL_USE_MEDIA_PROXY (1<<11) /* use mediaproxy on all messages during * a dialog */ #define FL_USE_RTPPROXY (1<<12) /* used by rtpproxy to remember if the msg * callback had already been registered */ #define FL_NAT_TRACK_DIALOG (1<<13) /* trigger dialog tracking from the * nat_traversal module */ #define FL_USE_SIPTRACE (1<<14) /* used by siptrace to check if the tm * callbacks were registered */ #define FL_SHM_UPDATABLE (1<<15) /* a SHM cloned message can be updated * (TM used, requires FL_SHM_CLONE) */ #define FL_TM_CB_REGISTERED (1<<16) /* tm callbacks for this message have been * registered (by setting this flag, you * will know if any tm callbacks for this * message have been registered) */ #define FL_TM_FAKE_REQ (1<<17) /* the SIP request is a fake one, generated based on the transaction, either in failure route or resume route */ /* define the # of unknown URI parameters to parse */ #define URI_MAX_U_PARAMS 10 #define IFISMETHOD(methodname,firstchar) \ if ( (*tmp==(firstchar) || *tmp==((firstchar) | 32)) && \ strncasecmp( tmp+1, #methodname +1, methodname##_LEN-1)==0 && \ *(tmp+methodname##_LEN)==' ') { \ fl->type=SIP_REQUEST; \ fl->u.request.method.len=methodname##_LEN; \ fl->u.request.method_value=METHOD_##methodname; \ tmp=buffer+methodname##_LEN; \ } /* * Return a URI to which the message should be really sent (not what should * be in the Request URI. The following fields are tried in this order: * 1) dst_uri * 2) new_uri * 3) first_line.u.request.uri */ #define GET_NEXT_HOP(m) \ (((m)->dst_uri.s && (m)->dst_uri.len) ? (&(m)->dst_uri) : \ (((m)->new_uri.s && (m)->new_uri.len) ? (&(m)->new_uri) : (&(m)->first_line.u.request.uri))) /* * Return the Reqeust URI of a message. * The following fields are tried in this order: * 1) new_uri * 2) first_line.u.request.uri */ #define GET_RURI(m) \ (((m)->new_uri.s && (m)->new_uri.len) ? (&(m)->new_uri) : (&(m)->first_line.u.request.uri)) enum _uri_type{ERROR_URI_T=0, SIP_URI_T, SIPS_URI_T, TEL_URI_T, TELS_URI_T, URN_SERVICE_URI_T}; typedef enum _uri_type uri_type; struct sip_uri { str user; /* Username */ str passwd; /* Password */ str host; /* Host name */ str port; /* Port number */ str params; /* Parameters */ str headers; unsigned short port_no; unsigned short proto; /* from transport */ uri_type type; /* uri scheme */ /* parameters */ str transport; str ttl; str user_param; str maddr; str method; str lr; str r2; /* ser specific rr parameter */ str gr; /* GRUU */ /* values */ str transport_val; str ttl_val; str user_param_val; str maddr_val; str method_val; str lr_val; /* lr value placeholder for lr=on a.s.o*/ str r2_val; str gr_val; /* unknown params */ str u_name[URI_MAX_U_PARAMS]; /* Unknown param names */ str u_val[URI_MAX_U_PARAMS]; /* Unknown param valss */ unsigned short u_params_no; /* No of unknown params */ }; #include "parse_to.h" /* Forward declaration */ struct msg_callback; struct sip_msg { unsigned int id; /* message id, unique/process*/ struct msg_start first_line; /* Message first line */ struct via_body* via1; /* The first via */ struct via_body* via2; /* The second via */ struct hdr_field* headers; /* All the parsed headers*/ struct hdr_field* last_header; /* Pointer to the last parsed header*/ hdr_flags_t parsed_flag; /* Already parsed header field types */ /* Via, To, CSeq, Call-Id, From, end of header*/ /* pointers to the first occurrences of these headers; * everything is also saved in 'headers' * (WARNING: do not deallocate them twice!)*/ struct hdr_field* h_via1; struct hdr_field* h_via2; struct hdr_field* callid; struct hdr_field* to; struct hdr_field* cseq; struct hdr_field* from; struct hdr_field* contact; struct hdr_field* maxforwards; struct hdr_field* route; struct hdr_field* record_route; struct hdr_field* path; struct hdr_field* content_type; struct hdr_field* content_length; struct hdr_field* authorization; struct hdr_field* expires; struct hdr_field* proxy_auth; struct hdr_field* supported; struct hdr_field* proxy_require; struct hdr_field* unsupported; struct hdr_field* allow; struct hdr_field* event; struct hdr_field* accept; struct hdr_field* accept_language; struct hdr_field* organization; struct hdr_field* priority; struct hdr_field* subject; struct hdr_field* user_agent; struct hdr_field* content_disposition; struct hdr_field* accept_disposition; struct hdr_field* diversion; struct hdr_field* rpid; struct hdr_field* refer_to; struct hdr_field* session_expires; struct hdr_field* min_se; struct hdr_field* ppi; struct hdr_field* pai; struct hdr_field* privacy; struct hdr_field* call_info; struct hdr_field* www_authenticate; struct hdr_field* proxy_authenticate; struct hdr_field* min_expires; struct sdp_info* sdp; struct multi_body * multi; char* eoh; /* pointer to the end of header (if found) or null */ char* unparsed; /* here we stopped parsing*/ struct receive_info rcv; /* source & dest ip, ports, proto a.s.o*/ char* buf; /* scratch pad, holds a unmodified message, * via, etc. point into it */ unsigned int len; /* message len (orig) */ /* attributes of the msg as first/default branch */ str new_uri; /* changed first line uri, when you change this * don't forget to set parsed_uri_ok to 0 */ str dst_uri; /* Destination URI, must be forwarded to this URI if len!=0 */ qvalue_t ruri_q; /* Q value of RURI */ unsigned int ruri_bflags; /* per-branch flags for RURI*/ /* force sending on this socket */ struct socket_info* force_send_socket; /* path vector to generate Route hdrs */ str path_vec; /* end-of-attributes for RURI as first branch*/ /* current uri */ int parsed_uri_ok; /* 1 if parsed_uri is valid, 0 if not, set it to 0 if you modify the uri (e.g change new_uri)*/ struct sip_uri parsed_uri; /* speed-up > keep here the parsed uri*/ /* the same for original uri */ int parsed_orig_ruri_ok; struct sip_uri parsed_orig_ruri; /* modifications */ struct lump* add_rm; /* used for all the forwarded requests/replies */ struct lump* body_lumps; /* Lumps that update Content-Length */ struct lump_rpl *reply_lump; /* only for localy generated replies !!!*/ /* whatever whoever want to append to branch comes here */ char add_to_branch_s[MAX_BRANCH_PARAM_LEN]; int add_to_branch_len; /* index to TM hash table; stored in core to avoid * unnecessary calculations */ unsigned int hash_index; /* flags used from script */ flag_t flags; /* flags used by core - allows to set various flags on the message; may * be used for simple inter-module communication or remembering * processing state reached */ unsigned int msg_flags; str set_global_address; str set_global_port; struct msg_callback *msg_cb; }; /* pointer to a fakes message which was never received ; (when this message is "relayed", it is generated out of the original request) */ #define FAKED_REPLY ((struct sip_msg *) -1) extern int via_cnt; int parse_msg(char* buf, unsigned int len, struct sip_msg* msg); int parse_headers(struct sip_msg* msg, hdr_flags_t flags, int next); char* get_hdr_field(char* buf, char* end, struct hdr_field* hdr); void free_sip_msg(struct sip_msg* msg); /* make sure all HFs needed for transaction identification have been parsed; return 0 if those HFs can't be found */ int check_transaction_quadruple( struct sip_msg* msg ); /* calculate characteristic value of a message -- this value is used to identify a transaction during the process of reply matching */ inline static int char_msg_val( struct sip_msg *msg, char *cv ) { str src[8]; if (!check_transaction_quadruple(msg)) { LM_ERR("can't calculate char_value due to a parsing error\n"); memset( cv, '0', MD5_LEN ); return 0; } src[0]= msg->from->body; src[1]= msg->to->body; src[2]= msg->callid->body; src[3]= msg->first_line.u.request.uri; src[4]= get_cseq( msg )->number; /* topmost Via is part of transaction key as well ! */ src[5]= msg->via1->host; src[6]= msg->via1->port_str; if (msg->via1->branch) { src[7]= msg->via1->branch->value; MD5StringArray ( cv, src, 8 ); } else { MD5StringArray( cv, src, 7 ); } return 1; } /* returns the body of the SIP message (if none, an empty body will be returned) */ inline static int get_body(struct sip_msg *msg, str *body) { unsigned int hdrs_len; int ct_len; if ( parse_headers(msg,HDR_EOH_F, 0)==-1 ) return -1; if (msg->unparsed){ hdrs_len=(unsigned int)(msg->unparsed-msg->buf); } else { return -1; } if ((hdrs_len+2<=msg->len) && (strncmp(CRLF,msg->unparsed,CRLF_LEN)==0) ) body->s = msg->unparsed + CRLF_LEN; else if ( (hdrs_len+1<=msg->len) && (*(msg->unparsed)=='\n' || *(msg->unparsed)=='\r' ) ) body->s = msg->unparsed + 1; else { /* no body */ body->s = NULL; body->len = 0; return 0; } /* determin the length of the body */ body->len = msg->buf + msg->len - body->s; /* double check the len against content-length hdr (if present, it must be already parsed) */ if (msg->content_length) { ct_len = get_content_length( msg ); if (ct_lenlen) body->len = ct_len; } else { /* no ct -> no body */ body->s = NULL; body->len = 0; } return 0; } /* * Search through already parsed headers (no parsing done) a non-standard * header - all known headers are skipped! */ #define get_header_by_static_name(_msg, _name) \ get_header_by_name(_msg, _name, sizeof(_name)-1) inline static struct hdr_field *get_header_by_name( struct sip_msg *msg, char *s, unsigned int len) { struct hdr_field *hdr; for( hdr=msg->headers ; hdr ; hdr=hdr->next ) { if(len==hdr->name.len && strncasecmp(hdr->name.s,s,len)==0) return hdr; } return NULL; } /* * Make a private copy of the string and assign it to new_uri (new RURI) */ int set_ruri(struct sip_msg* msg, str* uri); /* * Make a private copy of the string and assign it to dst_uri */ int set_dst_uri(struct sip_msg* msg, str* uri); /* * Set the q value of the Request-URI */ #define set_ruri_q(_msg,_q) \ (_msg)->ruri_q = _q /* * Get the q value of the Request-URI */ #define get_ruri_q(_msg) \ (_msg)->ruri_q /* * Get the per branch flags for RURI */ #define getb0flags(_msg) \ (_msg)->ruri_bflags /* * Set the per branch flags for RURI */ #define setb0flags( _msg, _flags) \ (_msg)->ruri_bflags = _flags /* * Make a private copy of the string and assign it to path_vec */ int set_path_vector(struct sip_msg* msg, str* path); /* * Parses a buffer containing a well formed SIP message and extracts the bodies * for FROM , TO , CSEQ and CALL-ID headers. */ int extract_ftc_hdrs( char *buf, int len, str *from, str *to, str *cseq,str *callid); #endif opensips-2.2.2/parser/parse_allow.c000066400000000000000000000044451300170765700173420ustar00rootroot00000000000000/* * Copyright (c) 2004 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2006-03-02 parse_allow() parses and cumulates all ALLOW headers (bogdan) */ #include #include #include "../dprint.h" #include "../errinfo.h" #include "../mem/mem.h" #include "parse_allow.h" #include "parse_methods.h" #include "msg_parser.h" /* * This method is used to parse all Allow HF body. * * params: msg : sip msg * returns 0 on success, * -1 on failure. */ int parse_allow(struct sip_msg *msg) { unsigned int allow; struct hdr_field *hdr; struct allow_body *ab = 0; /* maybe the header is already parsed! */ if (msg->allow && msg->allow->parsed) return 0; /* parse to the end in order to get all ALLOW headers */ if (parse_headers(msg,HDR_EOH_F,0)==-1 || !msg->allow) return -1; /* bad luck! :-( - we have to parse them */ allow = 0; for( hdr=msg->allow ; hdr ; hdr=hdr->sibling) { if (hdr->parsed) { allow |= ((struct allow_body*)hdr->parsed)->allow; continue; } ab = (struct allow_body*)pkg_malloc(sizeof(struct allow_body)); if (ab == 0) { LM_ERR("out of pkg_memory\n"); return -1; } if (parse_methods(&(hdr->body), &(ab->allow))!=0) { LM_ERR("bad allow body header\n"); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing ALLOW header"); set_err_reply(400, "bad headers"); goto error; } ab->allow_all = 0; hdr->parsed = (void*)ab; allow |= ab->allow; } ((struct allow_body*)msg->allow->parsed)->allow_all = allow; return 0; error: if(ab!=0) pkg_free(ab); return -1; } opensips-2.2.2/parser/parse_allow.h000066400000000000000000000031531300170765700173420ustar00rootroot00000000000000/* * Copyright (c) 2004 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2006-03-02 parse_allow() parses and cumulates all ALLOW headers (bogdan) */ #ifndef PARSE_ALLOW_H #define PARSE_ALLOW_H #include "../mem/mem.h" #include "msg_parser.h" /* * casting macro for accessing Allow body */ #define get_allow_methods(p_msg) \ (((struct allow_body*)(p_msg)->allow->parsed)->allow_all) struct allow_body { unsigned int allow; /* allow mask for the current hdr */ unsigned int allow_all; /* allow mask for the all allow hdr - it's * set only for the first hdr in sibling * list*/ }; /* * Parse all Allow HFs */ int parse_allow( struct sip_msg *msg); static inline void free_allow(struct allow_body **ab) { if (ab && *ab) { pkg_free(*ab); *ab = 0; } } #endif /* PARSE_ALLOW_H */ opensips-2.2.2/parser/parse_authenticate.c000066400000000000000000000170451300170765700207020ustar00rootroot00000000000000/* * Copyright (C) 2011 VoIP Embedded Inc. * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-01-31 first version (ramona) * 2011-03-07 Initial revision (Ovidiu Sas) */ #include #include #include "../dprint.h" #include "../ut.h" #include "../mem/mem.h" #include "msg_parser.h" #include "parse_authenticate.h" #define AUTHENTICATE_MD5 (1<<0) #define AUTHENTICATE_MD5SESS (1<<1) #define AUTHENTICATE_STALE (1<<2) #define AUTHENTICATE_DIGEST_S "Digest" #define AUTHENTICATE_DIGEST_LEN (sizeof(AUTHENTICATE_DIGEST_S)-1) #define LOWER1B(_n) \ ((_n)|0x20) #define LOWER4B(_n) \ ((_n)|0x20202020) #define GET4B(_p) \ ((*(_p)<<24) + (*(_p+1)<<16) + (*(_p+2)<<8) + *(_p+3)) #define GET3B(_p) \ ((*(_p)<<24) + (*(_p+1)<<16) + (*(_p+2)<<8) + 0xff) #define CASE_5B(_hex4,_c5, _new_state, _quoted) \ case _hex4: \ if (p+5s==0 || *body->s==0 ) { LM_ERR("empty body\n"); goto error; } memset( auth, 0, sizeof(struct authenticate_body)); p = body->s; end = body->s + body->len; /* parse the "digest" */ while (p=end ) goto parse_error; if ( LOWER4B( GET4B(p) ) != 0x64696765 /*dige*/ || LOWER1B(*(p+4))!=0x73 /*s*/ || LOWER1B(*(p+5))!=0x74 /*t*/) goto parse_error; p += AUTHENTICATE_DIGEST_LEN; if (!isspace((int)*p)) goto parse_error; p++; while (p=\"%.*s\" state=%d\n", name.len,name.s,val.len,val.s,state); /* process the AVP */ switch (state) { case QOP_STATE: auth->qop = val; if(val.len>=4 && !strncasecmp(val.s, "auth", 4)) auth->flags |= QOP_AUTH; break; case REALM_STATE: auth->realm = val; break; case NONCE_STATE: auth->nonce = val; break; case DOMAIN_STATE: auth->domain = val; break; case OPAQUE_STATE: auth->opaque = val; break; case ALGORITHM_STATE: if (val.len==3) { if ( LOWER4B(GET3B(val.s))==0x6d6435ff) /*MD5*/ auth->flags |= AUTHENTICATE_MD5; } else { LM_ERR("unsupported algorithm \"%.*s\"\n",val.len,val.s); goto error; } break; case STALE_STATE: if (val.len==4 && LOWER4B(GET4B(val.s))==0x74727565) /*true*/ { auth->flags |= AUTHENTICATE_STALE; } else if ( !(val.len==5 && LOWER1B(val.s[4])=='e' && LOWER4B(GET4B(val.s))==0x66616c73) ) { LM_ERR("unsupported stale value \"%.*s\"\n",val.len,val.s); goto error; } break; default: break; } } /* some checkings */ if (auth->nonce.s==0 || auth->realm.s==0) { LM_ERR("realm or nonce missing\n"); goto error; } return 0; parse_error: LM_ERR("parse error in <%.*s> around %ld\n", body->len, body->s, (long)(p-body->s)); error: return -1; } int parse_authenticate_header(struct hdr_field *authenticate) { void **parsed; struct authenticate_body *auth_body; parsed = &(authenticate->parsed); while(*parsed == NULL) { auth_body = pkg_malloc(sizeof(struct authenticate_body)); if (auth_body == NULL) { LM_ERR("oom\n"); return -1; } if (0 != parse_authenticate_body(&authenticate->body, auth_body)) return -1; *parsed = auth_body; authenticate = authenticate->sibling; if (authenticate) parsed = &(authenticate->parsed); else break; } return 0; } /* * This method is used to parse WWW-Authenticate header. * * params: msg : sip msg * returns 0 on success, * -1 on failure. */ int parse_www_authenticate_header( struct sip_msg *msg ) { if ( !msg->www_authenticate && (parse_headers(msg, HDR_WWW_AUTHENTICATE_F,0)==-1 || !msg->www_authenticate)) { return -1; } return parse_authenticate_header(msg->www_authenticate); } /* * This method is used to parse Proxy-Authenticate header. * * params: msg : sip msg * returns 0 on success, * -1 on failure. */ int parse_proxy_authenticate_header( struct sip_msg *msg ) { if ( !msg->proxy_authenticate && (parse_headers(msg, HDR_PROXY_AUTHENTICATE_F,0)==-1 || !msg->proxy_authenticate)) { return -1; } return parse_authenticate_header(msg->proxy_authenticate); } void free_authenticate(struct authenticate_body *authenticate_b) { if (authenticate_b) { pkg_free(authenticate_b); } return; } opensips-2.2.2/parser/parse_authenticate.h000066400000000000000000000035651300170765700207110ustar00rootroot00000000000000/* * Copyright (c) 2011 VoIP Embedded Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-01-31 first version (ramona) * 2011-03-07 Initial revision (Ovidiu Sas) */ #ifndef PARSE_AUTHENTICATE_H #define PARSE_AUTHENTICATE_H #include "msg_parser.h" #define AUTHENTICATE_MD5 (1<<0) #define AUTHENTICATE_MD5SESS (1<<1) #define AUTHENTICATE_STALE (1<<2) #define QOP_AUTH (1<<3) #define QOP_AUTH_INT (1<<4) struct authenticate_body { int flags; str realm; str domain; str nonce; str opaque; str qop; }; /* casting macro for accessing www/proxy authenticate body */ #define get_www_authenticate(p_msg) ((struct authenticate_body*)(p_msg)->www_authenticate->parsed) #define get_proxy_authenticate(p_msg) ((struct authenticate_body*)(p_msg)->proxy_authenticate->parsed) /* * WWW/Proxy-Authenticate header field parser */ int parse_proxy_authenticate_header( struct sip_msg *msg ); int parse_www_authenticate_header( struct sip_msg *msg ); int parse_authenticate_header(struct hdr_field *authenticate); void free_authenticate(struct authenticate_body *authenticate_b); #endif /* ! PARSE_AUTHENTICATE_H */ opensips-2.2.2/parser/parse_call_info.c000066400000000000000000000074531300170765700201540ustar00rootroot00000000000000/* * Copyright (C) 2010 VoIP Embedded Inc. * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-11-09 Initial revision (Ovidiu Sas) */ #include "parse_from.h" #include "parse_to.h" #include "parse_call_info.h" #include #include #include "../dprint.h" #include "msg_parser.h" #include "../ut.h" #include "../errinfo.h" #include "../mem/mem.h" /* * This method is used to parse Call-Info header. * * params: msg : sip msg * returns 0 on success, * -1 on failure. */ int parse_call_info_header( struct sip_msg *msg ) { struct call_info_body *callinfo_b, *old_callinfo_b=NULL; struct to_body *call_info_b; struct hdr_field *call_info; void **parsed; char *tmp, *end, *start; unsigned int len; if ( !msg->call_info && (parse_headers(msg, HDR_CALL_INFO_F,0)==-1 || !msg->call_info)) { return -1; } call_info=msg->call_info; /* maybe the header is already parsed! */ if (call_info->parsed) return 0; parsed = &(call_info->parsed); while(*parsed == NULL) { len = call_info->body.len+1; start = call_info->body.s; end = start + len; LM_DBG("parsing the whole body [%.*s]\n", len, call_info->body.s); for( tmp=call_info->body.s; tmp<=end; tmp++) { if (*tmp == ',' || tmp==end) { LM_DBG("[%.*s]\n",(int)(tmp-start),start); callinfo_b = pkg_malloc(sizeof(struct call_info_body)); if (callinfo_b == NULL) { LM_ERR("out of pkg_memory\n"); goto error; } memset(callinfo_b, 0, sizeof(struct call_info_body)); /* now parse it!! */ call_info_b = &(callinfo_b->call_info_body); parse_to(start, tmp, call_info_b); if (call_info_b->error == PARSE_ERROR) { LM_ERR("bad Call-Info header\n"); pkg_free(call_info_b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing Call-Info header"); set_err_reply(400, "bad header"); goto error; } /* Save the first parsed body */ if (*parsed == NULL) *parsed = callinfo_b; else old_callinfo_b->next = callinfo_b; old_callinfo_b = callinfo_b; start = tmp + 1; } } call_info = call_info->sibling; LM_DBG("done ... next call_info [%p]\n", call_info); if (call_info == NULL) { break; } parsed = &(call_info->parsed); } return 0; error: return -1; } inline static void free_call_info_param_list(struct to_param *param_lst) { struct to_param *foo; while(param_lst){ foo=param_lst->next; //LM_DBG(".. free [%p]->[%.*s]\n", param_lst, param_lst->name.len, param_lst->name.s); pkg_free(param_lst); param_lst=foo; } return; } void free_call_info(struct call_info_body *callinfo_b) { struct call_info_body *foo; while(callinfo_b){ //LM_DBG("freeing callinfo\n"); foo=callinfo_b; callinfo_b=callinfo_b->next; if (foo->call_info_body.param_lst) free_call_info_param_list(foo->call_info_body.param_lst); //LM_DBG(". free [%p]->[%.*s]\n", foo, foo->call_info_body.body.len, foo->call_info_body.body.s); pkg_free(foo); //LM_DBG("done freeing callinfo\n"); } return; } opensips-2.2.2/parser/parse_call_info.h000066400000000000000000000025731300170765700201570ustar00rootroot00000000000000/* * Copyright (c) 2010 VoIP Embedded Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2010-11-09 Initial revision (Ovidiu Sas) */ #ifndef PARSE_CALL_INFO_H #define PARSE_CALL_INFO_H 1 #include "msg_parser.h" struct call_info_body{ struct to_body call_info_body; struct call_info_body *next; }; /* casting macro for accessing Call-Info body */ #define get_call_info(p_msg) ((struct call_info_body*)(p_msg)->call_info->parsed) /* * Call-Info header field parser */ int parse_call_info_header( struct sip_msg *msg); void free_call_info(struct call_info_body *call_info_b); #endif /* ! PARSE_CALL_INFO_H */ opensips-2.2.2/parser/parse_content.c000066400000000000000000000431621300170765700176750ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * 2003-08-04 parse_content_type_hdr separates type from subtype inside * the mime type (bogdan) * 2003-08-04 CPL subtype added (bogdan) * 2003-08-05 parse_accept_hdr function added (bogdan) * 2011-06-22 ISUP subtype added (roger) */ #include #include #include #include #include "../mem/mem.h" #include "../dprint.h" #include "../str.h" #include "../ut.h" #include "../errinfo.h" #include "parse_content.h" #define is_mime_char(_c_) \ (isalnum((int)_c_) || (_c_)=='-' || (_c_)=='+' || (_c_)=='.') #define is_char_equal(_c_,_cs_) \ ( (isalpha((int)_c_)?(((_c_)|0x20)==(_cs_)):((_c_)==(_cs_)))==1 ) /* * Node of the type's tree; this tree contains all the known types; */ typedef struct type_node_s { char c; /* char contained by this node */ unsigned char final; /* says what mime type/subtype was detected * if string ends at this node */ unsigned char nr_sons; /* the number of sub-nodes */ int next; /* the next sibling node */ }type_node_t; static type_node_t type_tree[] = { {'t',TYPE_UNKNOWN,1,4}, /* 0 */ {'e',TYPE_UNKNOWN,1,-1}, {'x',TYPE_UNKNOWN,1,-1}, {'t',TYPE_TEXT,0,-1}, {'m',TYPE_UNKNOWN,2,19}, /* 4 */ {'e',TYPE_UNKNOWN,1,11}, /* 5 */ {'s',TYPE_UNKNOWN,1,-1}, {'s',TYPE_UNKNOWN,1,-1}, {'a',TYPE_UNKNOWN,1,-1}, {'g',TYPE_UNKNOWN,1,-1}, {'e',TYPE_MESSAGE,0,-1}, {'u',TYPE_UNKNOWN,1,-1}, /* 11 */ {'l',TYPE_UNKNOWN,1,-1}, {'t',TYPE_UNKNOWN,1,-1}, {'i',TYPE_UNKNOWN,1,-1}, {'p',TYPE_UNKNOWN,1,-1}, {'a',TYPE_UNKNOWN,1,-1}, {'r',TYPE_UNKNOWN,1,-1}, {'t',TYPE_MULTIPART,0,-1}, {'a',TYPE_UNKNOWN,1,-1}, /* 19 */ {'p',TYPE_UNKNOWN,1,-1}, {'p',TYPE_UNKNOWN,1,-1}, {'l',TYPE_UNKNOWN,1,-1}, {'i',TYPE_UNKNOWN,1,-1}, {'c',TYPE_UNKNOWN,1,-1}, {'a',TYPE_UNKNOWN,1,-1}, {'t',TYPE_UNKNOWN,1,-1}, {'i',TYPE_UNKNOWN,1,-1}, {'o',TYPE_UNKNOWN,1,-1}, {'n',TYPE_APPLICATION,0,-1}, }; static type_node_t subtype_tree[] = { {'p',SUBTYPE_UNKNOWN,2,12}, /* 0 */ {'l',SUBTYPE_UNKNOWN,1,5}, {'a',SUBTYPE_UNKNOWN,1,-1}, {'i',SUBTYPE_UNKNOWN,1,-1}, {'n',SUBTYPE_PLAIN,0,-1}, {'i',SUBTYPE_UNKNOWN,1,-1}, /* 5 */ {'d',SUBTYPE_UNKNOWN,1,-1}, {'f',SUBTYPE_UNKNOWN,1,-1}, {'+',SUBTYPE_UNKNOWN,1,-1}, {'x',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'l',SUBTYPE_PIDFXML,0,-1}, {'s',SUBTYPE_UNKNOWN,2,36}, /* 12 */ {'d',SUBTYPE_UNKNOWN,1,15}, {'p',SUBTYPE_SDP,0,-1}, {'i',SUBTYPE_UNKNOWN,1,-1}, /* 15 */ {'m',SUBTYPE_UNKNOWN,1,-1}, {'p',SUBTYPE_UNKNOWN,1,-1}, {'l',SUBTYPE_UNKNOWN,1,-1}, {'e',SUBTYPE_UNKNOWN,1,-1}, {'-',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'e',SUBTYPE_UNKNOWN,1,-1}, {'s',SUBTYPE_UNKNOWN,1,-1}, {'s',SUBTYPE_UNKNOWN,1,-1}, {'a',SUBTYPE_UNKNOWN,1,-1}, {'g',SUBTYPE_UNKNOWN,1,-1}, {'e',SUBTYPE_UNKNOWN,1,-1}, {'-',SUBTYPE_UNKNOWN,1,-1}, {'s',SUBTYPE_UNKNOWN,1,-1}, {'u',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'a',SUBTYPE_UNKNOWN,1,-1}, {'r',SUBTYPE_UNKNOWN,1,-1}, {'y',SUBTYPE_SMS,0,-1}, {'c',SUBTYPE_UNKNOWN,1,45}, /* 36 */ {'p',SUBTYPE_UNKNOWN,2,-1}, {'i',SUBTYPE_UNKNOWN,1,40}, {'m',SUBTYPE_CPIM,0,-1}, {'l',SUBTYPE_UNKNOWN,1,-1}, /* 40 */ {'+',SUBTYPE_UNKNOWN,1,-1}, {'x',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'l',SUBTYPE_CPLXML,0,-1}, {'r',SUBTYPE_UNKNOWN,2,59}, /* 45 */ {'l',SUBTYPE_UNKNOWN,1,53}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'i',SUBTYPE_UNKNOWN,1,-1}, {'+',SUBTYPE_UNKNOWN,1,-1}, {'x',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'l',SUBTYPE_RLMIXML,0,-1}, {'e',SUBTYPE_UNKNOWN,1,-1}, /* 53 */ {'l',SUBTYPE_UNKNOWN,1,-1}, {'a',SUBTYPE_UNKNOWN,1,-1}, {'t',SUBTYPE_UNKNOWN,1,-1}, {'e',SUBTYPE_UNKNOWN,1,-1}, {'d',SUBTYPE_RELATED,0,-1}, {'l',SUBTYPE_UNKNOWN,1,68}, /* 59 */ {'p',SUBTYPE_UNKNOWN,1,-1}, {'i',SUBTYPE_UNKNOWN,1,-1}, {'d',SUBTYPE_UNKNOWN,1,-1}, {'f',SUBTYPE_UNKNOWN,1,-1}, {'+',SUBTYPE_UNKNOWN,1,-1}, {'x',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'l',SUBTYPE_LPIDFXML,0,-1}, {'w',SUBTYPE_UNKNOWN,1,83}, /* 68 */ {'a',SUBTYPE_UNKNOWN,1,-1}, {'t',SUBTYPE_UNKNOWN,1,-1}, {'c',SUBTYPE_UNKNOWN,1,-1}, {'h',SUBTYPE_UNKNOWN,1,-1}, {'e',SUBTYPE_UNKNOWN,1,-1}, {'r',SUBTYPE_UNKNOWN,1,-1}, {'i',SUBTYPE_UNKNOWN,1,-1}, {'n',SUBTYPE_UNKNOWN,1,-1}, {'f',SUBTYPE_UNKNOWN,1,-1}, {'o',SUBTYPE_UNKNOWN,1,-1}, {'+',SUBTYPE_UNKNOWN,1,-1}, {'x',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'l',SUBTYPE_WATCHERINFOXML,0,-1}, {'x',SUBTYPE_UNKNOWN,2,105}, /* 83 */ {'p',SUBTYPE_UNKNOWN,1,92}, {'i',SUBTYPE_UNKNOWN,1,-1}, {'d',SUBTYPE_UNKNOWN,1,-1}, {'f',SUBTYPE_UNKNOWN,1,-1}, {'+',SUBTYPE_UNKNOWN,1,-1}, {'x',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'l',SUBTYPE_XPIDFXML,0,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, /* 92 */ {'l',SUBTYPE_UNKNOWN,1,-1}, {'+',SUBTYPE_UNKNOWN,1,-1}, {'m',SUBTYPE_UNKNOWN,1,-1}, {'s',SUBTYPE_UNKNOWN,1,-1}, {'r',SUBTYPE_UNKNOWN,1,-1}, {'t',SUBTYPE_UNKNOWN,1,-1}, {'c',SUBTYPE_UNKNOWN,1,-1}, {'.',SUBTYPE_UNKNOWN,1,-1}, {'p',SUBTYPE_UNKNOWN,1,-1}, {'i',SUBTYPE_UNKNOWN,1,-1}, {'d',SUBTYPE_UNKNOWN,1,-1}, {'f',SUBTYPE_XML_MSRTC_PIDF,0,-1}, {'e',SUBTYPE_UNKNOWN,1,118}, /* 105 */ {'x',SUBTYPE_UNKNOWN,1,-1}, {'t',SUBTYPE_UNKNOWN,1,-1}, {'e',SUBTYPE_UNKNOWN,1,-1}, {'r',SUBTYPE_UNKNOWN,1,-1}, {'n',SUBTYPE_UNKNOWN,1,-1}, {'a',SUBTYPE_UNKNOWN,1,-1}, {'l',SUBTYPE_UNKNOWN,1,-1}, {'-',SUBTYPE_UNKNOWN,1,-1}, {'b',SUBTYPE_UNKNOWN,1,-1}, {'o',SUBTYPE_UNKNOWN,1,-1}, {'d',SUBTYPE_UNKNOWN,1,-1}, {'y',SUBTYPE_EXTERNAL_BODY,0,-1}, {'m',SUBTYPE_UNKNOWN,1,123}, /* 118 */ {'i',SUBTYPE_UNKNOWN,1,-1}, {'x',SUBTYPE_UNKNOWN,1,-1}, {'e',SUBTYPE_UNKNOWN,1,-1}, {'d',SUBTYPE_MIXED,0,-1}, {'i',SUBTYPE_UNKNOWN,1,-1}, /* 123 */ {'s',SUBTYPE_UNKNOWN,1,-1}, {'u',SUBTYPE_UNKNOWN,1,-1}, {'p',SUBTYPE_ISUP,0,-1}, }; char str_contenttype[50]; char* parse_content_length( char* buffer, char* end, int* length) { int number; char *p; int size; p = buffer; /* search the beginning of the number */ while ( p='0' && *p<='9') { number = number*10 + (*p)-'0'; if (number<0) { LM_ERR("number overflow at pos %d in len number [%.*s]\n", (int)(p-buffer),(int)(end-buffer), buffer); return 0; } size ++; p++; } if (p==end || size==0) goto error; /* now we should have only spaces at the end */ while ( p eat everything to * the end or to the first ',' */ if ( pparams) < 0) goto error; p = params_str.s; cur = con->params; while(cur) { if( cur->name.len == 8 && !strncasecmp(cur->name.s,"boundary",cur->name.len ) ) con->boundary = cur->body; if( cur->name.len == 5 && !strncasecmp(cur->name.s,"start",cur->name.len ) ) con->start = cur->body; cur = cur ->next; } } } /* is this the correct end? */ if (p!=end && *p!=',' ) goto error; /* check the format of the decoded mime */ if ((*mime_type)>>16==TYPE_ALL && ((*mime_type)&0x00ff)!=SUBTYPE_ALL) { LM_ERR("invalid mime format found " " <*/submime> in [%.*s]!!\n", (int)(end-start),start); return 0; } return p; error: LM_ERR("parse error near in [%.*s] char" "[%d][%c] offset=%d\n", (int)(end-start),start,*p,*p,(int)(p-start)); return 0; } /* returns: > 0 mime found * = 0 hdr not found * =-1 error */ int parse_content_type_hdr( struct sip_msg *msg ) { char *end; char *ret; unsigned int mime; content_t * rez; /* is the header already found? */ if ( msg->content_type==0 ) { /* if not, found it */ if ( parse_headers(msg, HDR_CONTENTTYPE_F, 0)==-1) goto error; if ( msg->content_type==0 ) { LM_DBG("missing Content-Type header\n"); return 0; } } /* maybe the header is already parsed! */ if ( msg->content_type->parsed!=0) return get_content_type(msg); rez = (content_t*) pkg_malloc(sizeof (content_t)); if (rez == NULL) { LM_ERR("Unable to allocate memory\n"); goto error; } memset(rez, 0, sizeof (content_t)); /* it seams we have to parse it! :-( */ end = msg->content_type->body.s + msg->content_type->body.len; ret = decode_mime_type(msg->content_type->body.s, end , &mime, rez); if (ret==0) goto parse_error; if (ret!=end) { LM_ERR("the header CONTENT_TYPE contains " "more then one mime type :-(!\n"); goto parse_error; } if ((mime&0x00ff)==SUBTYPE_ALL || (mime>>16)==TYPE_ALL) { LM_ERR("invalid mime with wildcard '*' in Content-Type hdr!\n"); goto parse_error; } rez->type = mime; msg->content_type->parsed = rez; return mime; parse_error: pkg_free(rez); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing CT-TYPE header"); set_err_reply(400, "bad headers"); error: return -1; } /* returns: > 0 ok * = 0 hdr not found * = -1 error */ int parse_accept_hdr( struct sip_msg *msg ) { static unsigned int mimes[MAX_MIMES_NR]; int nr_mimes; unsigned int mime; char *end; char *ret; /* is the header already found? */ if ( msg->accept==0 ) { /* if not, found it */ if ( parse_headers(msg, HDR_ACCEPT_F, 0)==-1) goto error; if ( msg->accept==0 ) { LM_DBG("missing Accept header\n"); return 0; } } /* maybe the header is already parsed! */ if ( msg->accept->parsed!=0) return 1; /* it seams we have to parse it! :-( */ ret = msg->accept->body.s; end = ret + msg->accept->body.len; nr_mimes = 0; while (1){ ret = decode_mime_type(ret, end , &mime, NULL); if (ret==0) goto parse_error; /* a new mime was found -> put it into array */ if (nr_mimes==MAX_MIMES_NR) { LM_ERR("accept hdr contains more than" " %d mime type -> buffer overflow!!\n",MAX_MIMES_NR); goto error; } mimes[nr_mimes++] = mime; /* is another mime following? */ if (ret==end ) break; /* parse the mime separator ',' */ if (*ret!=',' || ret+1==end) { LM_ERR("parse error between mimes at " "char <%x> (offset=%d) in <%.*s>!\n", *ret, (int)(ret-msg->accept->body.s), msg->accept->body.len, msg->accept->body.s); goto parse_error; } /* skip the ',' */ ret++; } /* copy and link the mime buffer into the message */ msg->accept->parsed = (void*)pkg_malloc((nr_mimes+1)*sizeof(int)); if (msg->accept->parsed==0) { LM_ERR("no more pkg memory\n"); goto error; } memcpy(msg->accept->parsed,mimes,nr_mimes*sizeof(int)); /* make the buffer null terminated */ ((int*)msg->accept->parsed)[nr_mimes] = 0; return 1; parse_error: set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing ACCEPT header"); set_err_reply(400, "bad headers"); error: return -1; } void free_contenttype(content_t ** con) { if (*con) { if((*con)->params) free_params((*con)->params); pkg_free(*con); } *con = 0; } char* convert_mime2string_CT(int contenttype) { #define SET_TYPE_PTRS(_type_) \ do { \ subtype_start = type_start + sizeof(_type_) - 1; \ memcpy(type_start, _type_, sizeof(_type_) - 1); \ } while(0); #define SET_SUBTYPE_PTR(_subtype_) memcpy(subtype_start, _subtype_, sizeof(_subtype_)) /* last 16 bits */ int type = contenttype >> 16; /* only first 16 bits */ int subtype = contenttype & (0xFF); char* type_start; char* subtype_start; memset(str_contenttype, 0 , sizeof(str_contenttype)); type_start = str_contenttype; switch (type) { case TYPE_TEXT: SET_TYPE_PTRS("text/"); break; case TYPE_MESSAGE: SET_TYPE_PTRS("message/"); break; case TYPE_APPLICATION: SET_TYPE_PTRS("application/"); break; case TYPE_MULTIPART: SET_TYPE_PTRS("multipart/"); break; case TYPE_ALL: SET_TYPE_PTRS("*/"); break; case TYPE_UNKNOWN: SET_TYPE_PTRS("unknown/"); break; default: LM_ERR("invalid type\n"); return 0; } switch (subtype) { case SUBTYPE_PLAIN: SET_SUBTYPE_PTR("plain"); break; case SUBTYPE_CPIM: SET_SUBTYPE_PTR("cpim"); break; case SUBTYPE_SDP: SET_SUBTYPE_PTR("sdp"); break; case SUBTYPE_CPLXML: SET_SUBTYPE_PTR("cplxml"); break; case SUBTYPE_PIDFXML: SET_SUBTYPE_PTR("pidfxml"); break; case SUBTYPE_RLMIXML: SET_SUBTYPE_PTR("rlmixml"); break; case SUBTYPE_RELATED: SET_SUBTYPE_PTR("related"); break; case SUBTYPE_LPIDFXML: SET_SUBTYPE_PTR("lpidfxml"); break; case SUBTYPE_XPIDFXML: SET_SUBTYPE_PTR("xpidfxml"); break; case SUBTYPE_WATCHERINFOXML: SET_SUBTYPE_PTR("watcherinfoxml"); break; case SUBTYPE_EXTERNAL_BODY: SET_SUBTYPE_PTR("external_body"); break; case SUBTYPE_XML_MSRTC_PIDF: SET_SUBTYPE_PTR("xmlmsrtcpidf"); break; case SUBTYPE_SMS: SET_SUBTYPE_PTR("sms"); break; case SUBTYPE_MIXED: SET_SUBTYPE_PTR("mixed"); break; case SUBTYPE_ISUP: SET_SUBTYPE_PTR("isup"); break; case SUBTYPE_ALL: SET_SUBTYPE_PTR("*"); break; case SUBTYPE_UNKNOWN: SET_SUBTYPE_PTR("unknown"); break; default: LM_ERR("invalid subtype\n"); return 0; } return str_contenttype; #undef SET_TYPE_PTRS #undef SET_SUBTYPE_PTR } opensips-2.2.2/parser/parse_content.h000066400000000000000000000067141300170765700177040ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PARSE_CONTENT_H #define _PARSE_CONTENT_H #include "parse_param.h" typedef struct content { int type; str boundary; str start; param_t* params; } content_t; struct mime_type { unsigned short type; unsigned short subtype; }; /* * Mimes types/subtypes that are recognize */ #define TYPE_TEXT 1 #define TYPE_MESSAGE 2 #define TYPE_APPLICATION 3 #define TYPE_MULTIPART 4 #define TYPE_ALL 0xfe #define TYPE_UNKNOWN 0xff #define SUBTYPE_PLAIN 1 #define SUBTYPE_CPIM 2 #define SUBTYPE_SDP 3 #define SUBTYPE_CPLXML 4 #define SUBTYPE_PIDFXML 5 #define SUBTYPE_RLMIXML 6 #define SUBTYPE_RELATED 7 #define SUBTYPE_LPIDFXML 8 #define SUBTYPE_XPIDFXML 9 #define SUBTYPE_WATCHERINFOXML 10 #define SUBTYPE_EXTERNAL_BODY 11 #define SUBTYPE_XML_MSRTC_PIDF 12 #define SUBTYPE_SMS 13 /* simple-message-summary */ #define SUBTYPE_MIXED 14 #define SUBTYPE_ISUP 15 #define SUBTYPE_ALL 0xfe #define SUBTYPE_UNKNOWN 0xff /* * Maximum number of mimes allowed in Accept header */ #define MAX_MIMES_NR 128 /* * returns the content-length value of a sip_msg as an integer */ #define get_content_length(_msg_) ((long)((_msg_)->content_length?(_msg_)->content_length->parsed:0)) /* * returns the content-type value of a sip_msg as an integer */ #define get_content_type(_msg_) ( ( (content_t *)(_msg_)->content_type->parsed)->type ) /* * returns the accept values of a sip_msg as an null-terminated array * of integer */ #define get_accept(_msg_) ((int*)((_msg_)->accept->parsed)) #include "msg_parser.h" /* * parse the body of the Content-Type header. It's value is also converted * as int. * Returns: n (n>0) : the found type * 0 : hdr not found * -1 : error (parse error ) */ int parse_content_type_hdr( struct sip_msg *msg); /* * parse the body of the Accept header. It's values are also converted * as an null-terminated array of ints. * Returns: 1 : OK * 0 : hdr not found * -1 : error (parse error) */ int parse_accept_hdr( struct sip_msg *msg ); /* * parse the body of a Content_-Length header. Also tries to recognize the * type specified by this header (see th above defines). * Returns the first chr after the end of the header. */ char* parse_content_length( char* buffer, char* end, int* len); /* * parse a string containing a mime description */ char* decode_mime_type(char *start, char *end, unsigned int *mime_type, content_t * con); void free_contenttype(content_t ** con); char* convert_mime2string_CT(int contenttype); #endif opensips-2.2.2/parser/parse_cseq.c000066400000000000000000000044011300170765700171470ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-22 zero-termination in CSeq eliminated (jiri) */ #include "../dprint.h" #include "parse_cseq.h" #include "parser_f.h" /* eat_space_end and so on */ #include "parse_def.h" #include "parse_methods.h" #include "../mem/mem.h" /* * Parse CSeq header field */ char* parse_cseq(char *buf, char* end, struct cseq_body* cb) { char *t, *m, *m_end; cb->error=PARSE_ERROR; t=buf; cb->number.s=t; t=eat_token_end(t, end); if (t>=end) goto error; cb->number.len=t-cb->number.s; m=eat_space_end(t, end); m_end=eat_token_end(m, end); if (m_end>=end) { LM_ERR("method terminated unexpectedly\n"); goto error; } if (m_end==m){ /* null method*/ LM_ERR("no method found\n"); goto error; } cb->method.s=m; t=m_end; cb->method.len=t-cb->method.s; /* cache the method id */ if(parse_method(cb->method.s, t, (unsigned int*)&cb->method_id)==0) { LM_ERR("cannot parse the method\n"); goto error; } /* there may be trailing LWS * (it was not my idea to put it in SIP; -jiri ) */ t=eat_lws_end(t, end); /*check if the header ends here*/ if (t>=end) { LM_ERR("strange EoHF\n"); goto error; } if (*t=='\r' && t+1error=PARSE_OK; return t+2; } if (*t=='\n') { cb->error=PARSE_OK; return t+1; } LM_ERR("expecting CSeq EoL\n"); error: LM_ERR("bad cseq\n"); return t; } void free_cseq(struct cseq_body* cb) { pkg_free(cb); } opensips-2.2.2/parser/parse_cseq.h000066400000000000000000000024661300170765700171650ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARSE_CSEQ #define PARSE_CSEQ #include "../str.h" struct cseq_body{ int error; /* Error code */ str number; /* CSeq number */ str method; /* Associated method */ int method_id; /* Associated method ID */ }; /* casting macro for accessing CSEQ body */ #define get_cseq(p_msg) ((struct cseq_body*)(p_msg)->cseq->parsed) /* * Parse CSeq header field */ char* parse_cseq(char *buf, char* end, struct cseq_body* cb); /* * Free all associated memory */ void free_cseq(struct cseq_body* cb); #endif opensips-2.2.2/parser/parse_def.h000066400000000000000000000016321300170765700167620ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARSE_DEF_H #define PARSE_DEF_H #define PARSE_ERROR -1 #define PARSE_OK 1 #endif /* PARSE_DEF_H */ opensips-2.2.2/parser/parse_disposition.c000066400000000000000000000235321300170765700205660ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * 2003-09-09 created (bogdan) */ #include #include #include #include #include "../mem/mem.h" #include "../dprint.h" #include "../ut.h" #include "../errinfo.h" #include "parse_disposition.h" /* parse a string that supposed to be a disposition and fills up the structure * Returns: -2 : parse error -1 : error * 0 : success */ int parse_disposition( str *s, struct disposition *disp) { enum { FIND_TYPE, TYPE, END_TYPE, FIND_PARAM, PARAM, END_PARAM, FIND_VAL, FIND_QUOTED_VAL, QUOTED_VAL, SKIP_QUOTED_VAL, VAL, END_VAL, F_LF, F_CR, F_CRLF}; struct disposition_param *disp_p; struct disposition_param *new_p; int state; int saved_state; char *tmp; char *end; state = saved_state = FIND_TYPE; end = s->s + s->len; disp_p = 0; for( tmp=s->s; tmpbody.s = tmp; state = QUOTED_VAL; break; case SKIP_QUOTED_VAL: state = QUOTED_VAL; break; case TYPE: disp->type.len = tmp - disp->type.s; state = END_TYPE; break; case PARAM: disp_p->name.len = tmp - disp_p->name.s; state = END_PARAM; break; case VAL: disp_p->body.len = tmp - disp_p->body.s; state = END_VAL; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now =' '*/ state=saved_state; break; } break; case '\n': switch (state) { case TYPE: disp->type.len = tmp - disp->type.s; saved_state = END_TYPE; state = F_LF; break; case PARAM: disp_p->name.len = tmp - disp_p->name.s; saved_state = END_PARAM; state = F_LF; break; case VAL: disp_p->body.len = tmp - disp_p->body.s; saved_state = END_VAL; state = F_CR; break; case FIND_TYPE: case FIND_PARAM: saved_state=state; state=F_LF; break; case F_CR: state=F_CRLF; break; default: LM_ERR("unexpected char [%c] in status %d: <<%.*s>>" ".\n", *tmp,state, (int)(tmp-s->s), s->s); goto parse_error; } break; case '\r': switch (state) { case TYPE: disp->type.len = tmp - disp->type.s; saved_state = END_TYPE; state = F_CR; break; case PARAM: disp_p->name.len = tmp - disp_p->name.s; saved_state = END_PARAM; state = F_CR; break; case VAL: disp_p->body.len = tmp - disp_p->body.s; saved_state = END_VAL; state = F_CR; break; case FIND_TYPE: case FIND_PARAM: saved_state=state; state=F_CR; break; default: LM_ERR("unexpected char [%c] in status %d: <<%.*s>>" ".\n", *tmp,state, (int)(tmp-s->s), ZSW(s->s)); goto parse_error; } break; case 0: LM_ERR("unexpected char [%c] in status %d: <<%.*s>> .\n", *tmp,state, (int)(tmp-s->s), ZSW(s->s)); goto parse_error; break; case ';': switch (state) { case FIND_QUOTED_VAL: disp_p->body.s = tmp; state = QUOTED_VAL; break; case SKIP_QUOTED_VAL: state = QUOTED_VAL; case QUOTED_VAL: break; case VAL: disp_p->body.len = tmp - disp_p->body.s; state = FIND_PARAM; break; case PARAM: disp_p->name.len = tmp - disp_p->name.s; state = FIND_PARAM; break; case TYPE: disp->type.len = tmp - disp->type.s; case END_TYPE: case END_VAL: state = FIND_PARAM; break; default: LM_ERR("unexpected char [%c] in status %d: <<%.*s>> " ".\n", *tmp,state, (int)(tmp-s->s), ZSW(s->s)); goto parse_error; } break; case '=': switch (state) { case FIND_QUOTED_VAL: disp_p->body.s = tmp; state = QUOTED_VAL; break; case SKIP_QUOTED_VAL: state = QUOTED_VAL; case QUOTED_VAL: break; case PARAM: disp_p->name.len = tmp - disp_p->name.s; case END_PARAM: state = FIND_VAL; break; default: LM_ERR("unexpected char [%c] in status %d: <<%.*s>>" ".\n", *tmp,state, (int)(tmp-s->s), ZSW(s->s)); goto parse_error; } break; case '\"': switch (state) { case SKIP_QUOTED_VAL: state = QUOTED_VAL; break; case FIND_VAL: state = FIND_QUOTED_VAL; break; case QUOTED_VAL: disp_p->body.len = tmp - disp_p->body.s; disp_p->is_quoted = 1; state = END_VAL; break; default: LM_ERR("unexpected char [%c] in status %d: <<%.*s>>" ".\n", *tmp,state, (int)(tmp-s->s), ZSW(s->s)); goto parse_error; } break; case '\\': switch (state) { case FIND_QUOTED_VAL: disp_p->body.s = tmp; state = SKIP_QUOTED_VAL; break; case SKIP_QUOTED_VAL: state = QUOTED_VAL; break; case QUOTED_VAL: state = SKIP_QUOTED_VAL; break; default: LM_ERR("unexpected char [%c] in status %d: <<%.*s>>" ".\n", *tmp,state, (int)(tmp-s->s), ZSW(s->s)); goto parse_error; } break; case '(': case ')': case '<': case '>': case '@': case ',': case ':': case '/': case '[': case ']': case '?': case '{': case '}': switch (state) { case FIND_QUOTED_VAL: disp_p->body.s = tmp; state = QUOTED_VAL; break; case SKIP_QUOTED_VAL: state = QUOTED_VAL; case QUOTED_VAL: break; default: LM_ERR("unexpected char [%c] in status %d: <<%.*s>>" ".\n", *tmp,state, (int)(tmp-s->s), ZSW(s->s)); goto parse_error; } break; default: switch (state) { case SKIP_QUOTED_VAL: state = QUOTED_VAL; case QUOTED_VAL: break; case FIND_TYPE: disp->type.s = tmp; state = TYPE; break; case FIND_PARAM: new_p=(struct disposition_param*)pkg_malloc (sizeof(struct disposition_param)); if (new_p==0) { LM_ERR("no more pkg mem\n"); goto error; } memset(new_p,0,sizeof(struct disposition_param)); if (disp_p==0) disp->params = new_p; else disp_p->next = new_p; disp_p = new_p; disp_p->name.s = tmp; state = PARAM; break; case FIND_VAL: disp_p->body.s = tmp; state = VAL; break; case FIND_QUOTED_VAL: disp_p->body.s = tmp; state = QUOTED_VAL; break; } }/*switch*/ }/*for*/ /* check which was the last parser state */ switch (state) { case END_PARAM: case END_TYPE: case END_VAL: break; case TYPE: disp->type.len = tmp - disp->type.s; break; case PARAM: disp_p->name.len = tmp - disp_p->name.s; break; case VAL: disp_p->body.len = tmp - disp_p->body.s; break; default: LM_ERR("wrong final state (%d)\n", state); goto parse_error; } return 0; parse_error: return -2; error: return -1; } /* Frees the entire disposition structure (params + itself) */ void free_disposition( struct disposition **disp) { struct disposition_param *param; /* free the params */ while((*disp)->params) { param = (*disp)->params->next; pkg_free( (*disp)->params); (*disp)->params = param; } pkg_free( *disp ); *disp = 0; } /* looks inside the message, gets the Content-Disposition hdr, parse it, builds * and fills a disposition structure for it what will be attached to hdr as * parsed link. * Returns: -1 : error * 0 : success * 1 : hdr not found */ int parse_content_disposition( struct sip_msg *msg ) { struct disposition *disp; /* look for Content-Disposition header */ if (msg->content_disposition==0) { if (parse_headers(msg, HDR_CONTENTDISPOSITION_F, 0)==-1) goto error; if (msg->content_disposition==0) { LM_DBG("hdr not found\n"); return 1; } } /* now, we have the header -> look if it isn't already parsed */ if (msg->content_disposition->parsed!=0) { /* already parsed, nothing more to be done */ return 0; } /* parse the body */ disp = (struct disposition*)pkg_malloc(sizeof(struct disposition)); if (disp==0) { LM_ERR("no more pkg memory\n"); goto error; } memset(disp,0,sizeof(struct disposition)); switch (parse_disposition( &(msg->content_disposition->body), disp)) { case -2: set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing DISPOSITION header"); set_err_reply(400, "bad headers"); case -1: /* error when parsing the body */ free_disposition( &disp ); goto error; } /* attach the parsed form to the header */ msg->content_disposition->parsed = (void*)disp; return 0; error: return -1; } /* Prints recursive a disposition structure */ void print_disposition( struct disposition *disp) { struct disposition_param *param; LM_DBG("disposition type=<%.*s>[%d]\n", disp->type.len,disp->type.s,disp->type.len); for( param=disp->params; param; param=param->next) { LM_DBG("disposition param: <%.*s>[%d]=<%.*s>[%d] is_quoted=%d\n", param->name.len,param->name.s, param->name.len, param->body.len,param->body.s, param->body.len, param->is_quoted); } } opensips-2.2.2/parser/parse_disposition.h000066400000000000000000000036641300170765700205770ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * 2003-09-09 created (bogdan) */ #ifndef _PARSE_DISPOSITION_H_ #define _PARSE_DISPOSITION_H_ #include "../str.h" #include "msg_parser.h" #define get_content_disposition(_msg_) \ ((struct disposition*)((_msg_)->content_disposition->parsed)) struct disposition_param { str name; str body; int is_quoted; struct disposition_param *next; }; struct disposition { str type; struct disposition_param *params; }; /* looks inside the message, gets the Content-Disposition hdr, parse it, builds * and fills a disposition structure for it what will be attached to hdr as * parsed link. * Returns: -1 : error * 0 : success * 1 : hdr not found */ int parse_content_disposition( struct sip_msg *msg ); /* parse a string that supposed to be a disposition and fills up the structure * Returns: -1 : error * o : success */ int parse_disposition( str *s, struct disposition *disp); /* Frees the entire disposition structure (params + itself) */ void free_disposition( struct disposition **disp); /* Prints recursive a disposition structure */ void print_disposition( struct disposition *disp); #endif opensips-2.2.2/parser/parse_diversion.c000066400000000000000000000045611300170765700202250ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../dprint.h" #include "../ut.h" #include "../mem/mem.h" #include "parse_from.h" #include "parse_to.h" #include "msg_parser.h" /* * This method is used to parse DIVERSION header. * * params: msg : sip msg * returns 0 on success, * -1 on failure. */ int parse_diversion_header(struct sip_msg *msg) { struct to_body* diversion_b; if (!msg->diversion && (parse_headers(msg, HDR_DIVERSION_F, 0) == -1 || !msg->diversion)) { goto error; } /* maybe the header is already parsed! */ if (msg->diversion->parsed) return 0; /* bad luck! :-( - we have to parse it */ /* first, get some memory */ diversion_b = pkg_malloc(sizeof(struct to_body)); if (diversion_b == 0) { LM_ERR("out of pkg_memory\n"); goto error; } /* now parse it!! */ parse_to(msg->diversion->body.s, msg->diversion->body.s + msg->diversion->body.len + 1, diversion_b); if (diversion_b->error == PARSE_ERROR) { LM_ERR("bad diversion header\n"); pkg_free(diversion_b); goto error; } msg->diversion->parsed = diversion_b; return 0; error: return -1; } /* * Get value of given diversion parameter */ str *diversion_param(struct sip_msg *msg, str name) { struct to_param *params; if (parse_diversion_header(msg) == -1) { LM_ERR("could not get diversion parameter\n"); return 0; } params = ((struct to_body*)(msg->diversion->parsed))->param_lst; while (params) { if ((params->name.len == name.len) && (strncmp(params->name.s, name.s, name.len) == 0)) { return &(params->value); } params = params->next; } return 0; } opensips-2.2.2/parser/parse_diversion.h000066400000000000000000000023331300170765700202250ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARSE_DIVERSION_H #define PARSE_DIVERSION_H #include "msg_parser.h" /* casting macro for accessing Diversion body */ #define get_diversion(p_msg) ((struct to_body*)(p_msg)->diversion->parsed) /* * Diversion header field parser */ int parse_diversion_header(struct sip_msg *msg); /* * Get value of given diversion parameter */ str *diversion_param(struct sip_msg *msg, str name); #endif /* PARSE_DIVERSION_H */ opensips-2.2.2/parser/parse_event.c000066400000000000000000000120511300170765700173350ustar00rootroot00000000000000/* * Event header field body parser. * The parser was written for Presence Agent module only. * it recognize presence package only, no sub-packages, no parameters * It should be replaced by a more generic parser if sub-packages or * parameters should be parsed too. * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-26 ZSW (jiri) */ #include /* memset */ #include /* printf */ #include "../mem/mem.h" /* pkg_malloc, pkg_free */ #include "../dprint.h" #include "../trim.h" /* trim_leading */ #include "../ut.h" #include "../errinfo.h" #include "parse_event.h" #define PRES_STR "presence" #define PRES_STR_LEN 8 #define PRES_WINFO_STR "presence.winfo" #define PRES_WINFO_STR_LEN 14 #define PRES_XCAP_DIFF_STR "xcap-diff" #define PRES_XCAP_DIFF_STR_LEN 9 #define PRES_SIP_PROFILE_STR "sip-profile" #define PRES_SIP_PROFILE_STR_LEN 11 #define MWI_STR "message-summary" #define MWI_STR_LEN 15 #define DIALOG_STR "dialog" #define DIALOG_STR_LEN 6 #define DIALOG_SLA_STR "dialog;sla" #define DIALOG_SLA_STR_LEN 10 #define CALL_INFO_STR "call-info" #define CALL_INFO_STR_LEN 9 #define LINE_SEIZE_STR "line-seize" #define LINE_SEIZE_STR_LEN 10 static inline char* skip_token(char* _b, int _l) { int i = 0; for(i = 0; i < _l; i++) { switch(_b[i]) { case ' ': case '\r': case '\n': case '\t': case ';': return _b + i; } } return _b + _l; } int event_parser(char* _s, int _l, event_t* _e) { str tmp; char* end; param_hooks_t phooks; tmp.s = _s; tmp.len = _l; trim_leading(&tmp); if (tmp.len == 0) { LM_ERR("empty body\n"); goto parse_error; } _e->text.s = tmp.s; end = skip_token(tmp.s, tmp.len); _e->text.len = end - tmp.s; if ((_e->text.len == PRES_STR_LEN) && !strncasecmp(PRES_STR, tmp.s, _e->text.len)) { _e->parsed = EVENT_PRESENCE; } else if ((_e->text.len == PRES_XCAP_DIFF_STR_LEN) && !strncasecmp(PRES_XCAP_DIFF_STR, tmp.s, _e->text.len)) { _e->parsed = EVENT_XCAP_DIFF; } else if ((_e->text.len == PRES_WINFO_STR_LEN) && !strncasecmp(PRES_WINFO_STR, tmp.s, _e->text.len)) { _e->parsed = EVENT_PRESENCE_WINFO; } else if ((_e->text.len == PRES_SIP_PROFILE_STR_LEN) && !strncasecmp(PRES_SIP_PROFILE_STR, tmp.s, _e->text.len)) { _e->parsed = EVENT_SIP_PROFILE; } else if ((_e->text.len == DIALOG_STR_LEN) && !strncasecmp(DIALOG_STR, tmp.s, _e->text.len)) { _e->parsed = EVENT_DIALOG; } else if ((_e->text.len == MWI_STR_LEN) && !strncasecmp(MWI_STR, tmp.s, _e->text.len)) { _e->parsed = EVENT_MWI; } else if ((_e->text.len == CALL_INFO_STR_LEN) && !strncasecmp(CALL_INFO_STR, tmp.s, _e->text.len)) { _e->parsed = EVENT_CALL_INFO; } else if ((_e->text.len == LINE_SEIZE_STR_LEN) && !strncasecmp(LINE_SEIZE_STR, tmp.s, _e->text.len)) { _e->parsed = EVENT_LINE_SEIZE; } else { _e->parsed = EVENT_OTHER; } if( (_e->text.len < tmp.len) && (*end)== ';') { str params_str; params_str.s = end+1; params_str.len = tmp.len- _e->text.len- 1; if (parse_params(¶ms_str, CLASS_ANY, &phooks, &_e->params)<0) goto parse_error; if(_e->parsed == EVENT_DIALOG && _e->params!= NULL && _e->params->name.len== 3 && strncasecmp(_e->params->name.s, "sla", 3)== 0 ) { _e->parsed = EVENT_DIALOG_SLA; } } else { _e->params= NULL; } return 0; parse_error: return -1; } /* * Parse Event header field body */ int parse_event(struct hdr_field* _h) { event_t* e; if (_h->parsed != 0) { return 0; } e = (event_t*)pkg_malloc(sizeof(event_t)); if (e == 0) { LM_ERR("no pkg memory left\n"); return -1; } memset(e, 0, sizeof(event_t)); if (event_parser(_h->body.s, _h->body.len, e) < 0) { LM_ERR("event_parser failed\n"); pkg_free(e); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing EVENT header"); set_err_reply(400, "bad headers"); return -2; } _h->parsed = (void*)e; return 0; } /* * Free all memory */ void free_event(event_t** _e) { if (*_e) { if((*_e)->params) free_params((*_e)->params); pkg_free(*_e); } *_e = 0; } /* * Print structure, for debugging only */ void print_event(event_t* _e) { printf("===Event===\n"); printf("text : \'%.*s\'\n", _e->text.len, ZSW(_e->text.s)); printf("parsed: %s\n", (_e->parsed == EVENT_PRESENCE) ? ("EVENT_PRESENCE") : ("EVENT_OTHER")); printf("===/Event===\n"); } opensips-2.2.2/parser/parse_event.h000066400000000000000000000036131300170765700173460ustar00rootroot00000000000000/* * Event header field body parser * This parser was written for Presence Agent module only. * it recognizes presence package only, no subpackages, no parameters * It should be replaced by a more generic parser if subpackages or * parameters should be parsed too. * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARSE_EVENT_H #define PARSE_EVENT_H #include "../str.h" #include "hf.h" #include "parse_param.h" #define EVENT_OTHER 0 #define EVENT_PRESENCE 1 #define EVENT_PRESENCE_WINFO 2 #define EVENT_SIP_PROFILE 3 #define EVENT_XCAP_DIFF 4 #define EVENT_DIALOG 5 #define EVENT_MWI 6 #define EVENT_DIALOG_SLA 7 #define EVENT_CALL_INFO 8 #define EVENT_LINE_SEIZE 9 typedef struct event { str text; /* Original string representation */ int parsed; /* Parsed variant */ param_t* params; } event_t; /* * Parse Event HF body */ int parse_event(struct hdr_field* _h); /* * Release memory */ void free_event(event_t** _e); /* * Print structure, for debugging only */ void print_event(event_t* _e); int event_parser(char* _s, int _l, event_t* _e); #endif /* PARSE_EVENT_H */ opensips-2.2.2/parser/parse_expires.c000066400000000000000000000047451300170765700177060ustar00rootroot00000000000000/* * Expires header field body parser * * Copyright (C) 2001-2003 FhG Fokus * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-26 ZSW (jiri) */ #include /* printf */ #include /* memset */ #include "../mem/mem.h" /* pkg_malloc, pkg_free */ #include "../dprint.h" #include "../trim.h" /* trim_leading */ #include "../ut.h" #include "../errinfo.h" #include "parse_expires.h" static inline int expires_parser(char* _s, int _l, exp_body_t* _e) { str tmp; tmp.s = _s; tmp.len = _l; trim(&tmp); if (tmp.len == 0) { LM_ERR("empty body\n"); _e->valid = 0; return -1; } if ( str2int( &tmp, (unsigned int*)&_e->val)!=0 ) { LM_ERR("body is not a number <%.*s>\n",tmp.len,tmp.s); _e->valid = 0; return -2; } _e->text = tmp; _e->valid = 1; return 0; } /* * Parse expires header field body */ int parse_expires(struct hdr_field* _h) { exp_body_t* e; if (_h->parsed) { return 0; /* Already parsed */ } e = (exp_body_t*)pkg_malloc(sizeof(exp_body_t)); if (e == 0) { LM_ERR("no pkg memory left\n"); return -1; } memset(e, 0, sizeof(exp_body_t)); if (expires_parser(_h->body.s, _h->body.len, e) < 0) { LM_ERR("failed to parse\n"); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing EXPIRE header"); set_err_reply(400, "bad headers"); pkg_free(e); return -2; } _h->parsed = (void*)e; return 0; } /* * Free all memory associated with exp_body_t */ void free_expires(exp_body_t** _e) { pkg_free(*_e); *_e = 0; } /* * Print exp_body_t content, for debugging only */ void print_expires(exp_body_t* _e) { printf("===Expires===\n"); printf("text: \'%.*s\'\n", _e->text.len, ZSW(_e->text.s)); printf("val : %d\n", _e->val); printf("===/Expires===\n"); } opensips-2.2.2/parser/parse_expires.h000066400000000000000000000026261300170765700177070ustar00rootroot00000000000000/* * Expires header field body parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARSE_EXPIRES_H #define PARSE_EXPIRES_H #include "../str.h" #include "hf.h" typedef struct exp_body { str text; /* Original text representation */ unsigned char valid; /* Was parsing successful ? */ int val; /* Parsed value */ } exp_body_t; /* * Parse expires header field body */ int parse_expires(struct hdr_field* _h); /* * Free all memory associated with exp_body_t */ void free_expires(exp_body_t** _e); /* * Print exp_body_t content, for debugging only */ void print_expires(exp_body_t* _e); #endif /* PARSE_EXPIRES_H */ opensips-2.2.2/parser/parse_fline.c000066400000000000000000000652711300170765700173250ustar00rootroot00000000000000/* * sip first line parsing automaton * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-28: removed 0-terminators from first line (jiri) * 2003-04-26 ZSW (jiri) */ #include "../dprint.h" #include "msg_parser.h" #include "parser_f.h" #include "parse_methods.h" #include "../mem/mem.h" #include "../ut.h" /* grammar: request = method SP uri SP version CRLF response = version SP status SP reason CRLF (version = "SIP/2.0") */ /*known methods: INVITE, ACK, CANCEL, BYE*/ enum { START, INVITE1, INVITE2, INVITE3, INVITE4, INVITE5, ACK1, ACK2, CANCEL1, CANCEL2, CANCEL3, CANCEL4, CANCEL5, BYE1, BYE2, SIP1, SIP2, SIP3, SIP4, SIP5, SIP6, FIN_INVITE = 100, FIN_ACK, FIN_CANCEL, FIN_BYE, FIN_SIP, P_METHOD = 200, L_URI, P_URI, L_VER, VER1, VER2, VER3, VER4, VER5, VER6, FIN_VER, L_STATUS, P_STATUS, L_REASON, P_REASON, L_LF, F_CR, F_LF }; #ifdef _CURRENTLY_UNUSED char* parse_fline(char* buffer, char* end, struct msg_start* fl) { char* tmp; register int state; unsigned short stat; stat=0; fl->type=SIP_REQUEST; state=START; for(tmp=buffer;tmpu.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_INVITE; state=L_URI; break; case FIN_ACK: *tmp=0; fl->u.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_ACK; state=L_URI; break; case FIN_CANCEL: *tmp=0; fl->u.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_CANCEL; state=L_URI; break; case FIN_BYE: *tmp=0; fl->u.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_BYE; state=L_URI; break; case FIN_SIP: *tmp=0; fl->u.reply.version.len=tmp-fl->u.reply.version.s; state=L_STATUS; fl->type=SIP_REPLY; break; case P_URI: *tmp=0; fl->u.request.uri.len=tmp-fl->u.request.uri.s; state=L_VER; break; case FIN_VER: *tmp=0; fl->u.request.version.len=tmp-fl->u.request.version.s; state=L_LF; break; case P_STATUS: *tmp=0; fl->u.reply.status.len=tmp-fl->u.reply.status.s; state=L_REASON; break; case P_REASON: /* *tmp=0; fl->u.reply.reason.len=tmp-fl->u.reply.reason.s; */ break; case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: LM_ERR("invalid version in request\n"); goto error; case P_METHOD: default: *tmp=0; fl->u.request.method.len=tmp-fl->u.request.method.s; fl->u.request.method_value=METHOD_OTHER; state=L_URI; } break; case 's': case 'S': switch(state){ case START: state=SIP1; fl->u.reply.version.s=tmp; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number character <%c> in request" "status\n", *tmp); goto error; case L_LF: LM_ERR("invalid character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: fl->u.request.version.s=tmp; state=VER1; break; case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'i': case 'I': switch(state){ case START: state=INVITE1; fl->u.request.method.s=tmp; break; case INVITE3: state=INVITE4; break; case SIP1: state=SIP2; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER1: state=VER2; break; case L_VER: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'p': case 'P': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP2: state=SIP3; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER2: state=VER3; break; case L_VER: case VER1: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case '/': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP3: state=SIP4; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER3: state=VER4; break; case L_VER: case VER1: case VER2: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case '2': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP4: state=SIP5; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: stat=stat*10+*tmp-'0'; break; case L_STATUS: stat=*tmp-'0'; fl->u.reply.status.s=tmp; break; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER4: state=VER5; break; case L_VER: case VER1: case VER2: case VER3: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case '.': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP5: state=SIP6; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER5: state=VER6; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case '0': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case SIP6: state=FIN_SIP; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: stat=stat*10; break; case L_STATUS: stat=0; fl->u.reply.status.s=tmp; break; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case VER6: state=FIN_VER; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case FIN_VER: LM_ERR("invalid version " " in request\n"); goto error; default: state=P_METHOD; } break; case 'n': case 'N': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case INVITE1: state=INVITE2; break; case CANCEL2: state=CANCEL3; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'v': case 'V': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case INVITE2: state=INVITE3; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 't': case 'T': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case INVITE4: state=INVITE5; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'e': case 'E': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case INVITE5: state=FIN_INVITE; break; case CANCEL4: state=CANCEL5; break; case BYE2: state=FIN_BYE; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'a': case 'A': switch(state){ case START: state=ACK1; fl->u.request.method.s=tmp; break; case CANCEL1: state=CANCEL2; break; case BYE2: state=FIN_BYE; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'c': case 'C': switch(state){ case START: state=CANCEL1; fl->u.request.method.s=tmp; break; case CANCEL3: state=CANCEL4; break; case ACK1: state=ACK2; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'k': case 'K': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case ACK2: state=FIN_ACK; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'l': case 'L': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case CANCEL5: state=FIN_CANCEL; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'b': case 'B': switch(state){ case START: state=BYE1; fl->u.request.method.s=tmp; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case 'y': case 'Y': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case BYE1: state=BYE2; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid " "character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } break; case '\r': switch(state){ case P_REASON: *tmp=0; fl->u.reply.reason.len=tmp-fl->u.reply.reason.s; state=F_CR; break; case L_LF: state=F_CR; break; case FIN_VER: *tmp=0; fl->u.request.version.len=tmp-fl->u.request.version.s; state=F_CR; break; case L_REASON: state=F_CR; break; default: LM_ERR("invalid message\n"); goto error; } break; case '\n': switch(state){ case P_REASON: *tmp=0; fl->u.reply.reason.len=tmp-fl->u.reply.reason.s; state=F_LF; goto skip; case FIN_VER: *tmp=0; fl->u.request.version.len=tmp-fl->u.request.version.s; state=F_LF; goto skip; case L_REASON: case L_LF: case F_CR: state=F_LF; goto skip; default: LM_ERR("invalid message\n"); goto error; } break; case '1': case '3': case '4': case '5': case '6': case '7': case '8': case '9': switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: stat=stat*10+*tmp-'0'; break; case L_STATUS: stat=*tmp-'0'; state=P_STATUS; fl->u.reply.status.s=tmp; break; case L_LF: LM_ERR("invalid character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } default: switch(state){ case START: state=P_METHOD; fl->u.request.method.s=tmp; break; case P_URI: case P_REASON: case P_METHOD: break; case L_REASON: fl->u.reply.reason.s=tmp; state=P_REASON; break; case P_STATUS: case L_STATUS: LM_ERR("non-number " "character <%c> in request status\n", *tmp); goto error; case L_LF: LM_ERR("invalid character <%c> in request\n", *tmp); goto error; case L_URI: fl->u.request.uri.s=tmp; state=P_URI; break; case L_VER: case VER1: case VER2: case VER3: case VER4: case VER5: case VER6: case FIN_VER: LM_ERR("invalid version in request\n"); goto error; default: state=P_METHOD; } } } skip: fl->len=tmp-buf; if (fl->type==SIP_REPLY){ fl->u.reply.statuscode=stat; /* fl->u.reply.statusclass=stat/100; */ } return tmp; error: LM_ERR("while parsing first line (state=%d)\n", state); fl->type=SIP_INVALID; return tmp; } #endif /* currently unused */ /* parses the first line, returns pointer to next line & fills fl; also modifies buffer (to avoid extra copy ops) */ char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl) { char *tmp; char* second; char* third; char* nl; unsigned int offset; /* int l; */ char* end; char s1,s2,s3; char *prn; unsigned int t; /* grammar: request = method SP uri SP version CRLF response = version SP status SP reason CRLF (version = "SIP/2.0") */ end=buffer+len; /* see if it's a reply (status) */ /* jku -- parse well-known methods */ /* drop messages which are so short they are for sure useless; utilize knowledge of minimum size in parsing the first token */ if (len <=16 ) { LM_INFO("message too short: %d\n", len); goto error1; } tmp=buffer; /* is it perhaps a reply, ie does it start with "SIP...." ? */ if ( (*tmp=='S' || *tmp=='s') && strncasecmp( tmp+1, SIP_VERSION+1, SIP_VERSION_LEN-1)==0 && (*(tmp+SIP_VERSION_LEN)==' ')) { fl->type=SIP_REPLY; fl->u.reply.version.len=SIP_VERSION_LEN; tmp=buffer+SIP_VERSION_LEN; } else IFISMETHOD( INVITE, 'I' ) else IFISMETHOD( CANCEL, 'C') else IFISMETHOD( ACK, 'A' ) else IFISMETHOD( BYE, 'B' ) else IFISMETHOD( INFO, 'I' ) /* if you want to add another method XXX, include METHOD_XXX in H-file (this is the value which you will take later in processing and define XXX_LEN as length of method name; then just call IFISMETHOD( XXX, 'X' ) ... 'X' is the first latter; everything must be capitals */ else { /* neither reply, nor any of known method requests, let's believe it is an unknown method request */ tmp=eat_token_end(buffer,buffer+len); if ((tmp==buffer)||(tmp>=end)){ LM_INFO("empty or bad first line\n"); goto error1; } if (*tmp!=' ') { LM_INFO("method not followed by SP\n"); goto error1; } fl->type=SIP_REQUEST; /* see if it is another known method */ /* fl->u.request.method_value=METHOD_OTHER; */ if(parse_method(buffer, tmp, (unsigned int*)&fl->u.request.method_value)==0) { LM_INFO("failed to parse the method\n"); goto error1; } fl->u.request.method.len=tmp-buffer; } /* identifying type of message over now; tmp points at space after; go ahead */ fl->u.request.method.s=buffer; /* store ptr to first token */ second=tmp+1; /* jump to second token */ offset=second-buffer; /* EoJku */ /* next element */ tmp=eat_token_end(second, second+len-offset); if (tmp>=end){ goto error; } offset+=tmp-second; third=eat_space_end(tmp, tmp+len-offset); offset+=third-tmp; if ((third==tmp)||(tmp>=end)){ goto error; } fl->u.request.uri.s=second; fl->u.request.uri.len=tmp-second; /* jku: parse status code */ if (fl->type==SIP_REPLY) { if (fl->u.request.uri.len!=3) { LM_INFO("len(status code)!=3: %.*s\n", fl->u.request.uri.len, ZSW(second) ); goto error; } s1=*second; s2=*(second+1);s3=*(second+2); if (s1>='0' && s1<='9' && s2>='0' && s2<='9' && s3>='0' && s3<='9' ) { fl->u.reply.statuscode=(s1-'0')*100+10*(s2-'0')+(s3-'0'); } else { LM_INFO("status_code non-numerical: %.*s\n", fl->u.request.uri.len, ZSW(second) ); goto error; } } /* EoJku */ /* last part: for a request it must be the version, for a reply * it can contain almost anything, including spaces, so we don't care * about it*/ if (fl->type==SIP_REQUEST){ tmp=eat_token_end(third,third+len-offset); offset+=tmp-third; if ((tmp==third)||(tmp>=end)){ goto error; } if (! is_empty_end(tmp, tmp+len-offset)){ goto error; } }else{ tmp=eat_token2_end(third,third+len-offset,'\r'); /* find end of line ('\n' or '\r') */ if (tmp>=end){ /* no crlf in packet => invalid */ goto error; } offset+=tmp-third; } nl=eat_line(tmp,len-offset); if (nl>=end){ /* no crlf in packet or only 1 line > invalid */ goto error; } fl->u.request.version.s=third; fl->u.request.version.len=tmp-third; fl->len=nl-buffer; return nl; error: LM_ERR("bad %s first line\n", (fl->type==SIP_REPLY)?"reply(status)":"request"); LM_ERR("at line 0 char %d: \n", offset ); prn=pkg_malloc( offset ); if (prn) { for (t=0; ttype=SIP_INVALID; LM_INFO("bad message\n"); /* skip line */ nl=eat_line(buffer,len); return nl; } opensips-2.2.2/parser/parse_fline.h000066400000000000000000000046741300170765700173320ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARSE_FLINE_H #define PARSE_FLINE_H #include "../str.h" /* Message is request */ #define SIP_REQUEST 1 /* Message is reply */ #define SIP_REPLY 2 /* Invalid message */ #define SIP_INVALID 0 #define SIP_VERSION "SIP/2.0" #define SIP_VERSION_LEN 7 #define SUBSCRIBE "SUBSCRIBE" #define REGISTER "REGISTER" #define MESSAGE "MESSAGE" #define OPTIONS "OPTIONS" #define PUBLISH "PUBLISH" #define INVITE "INVITE" #define CANCEL "CANCEL" #define UPDATE "UPDATE" #define NOTIFY "NOTIFY" #define PRACK "PRACK" #define REFER "REFER" #define INFO "INFO" #define ACK "ACK" #define BYE "BYE" #define SUBSCRIBE_LEN 9 #define REGISTER_LEN 8 #define MESSAGE_LEN 7 #define OPTIONS_LEN 7 #define PUBLISH_LEN 7 #define INVITE_LEN 6 #define CANCEL_LEN 6 #define UPDATE_LEN 6 #define NOTIFY_LEN 6 #define PRACK_LEN 5 #define REFER_LEN 5 #define INFO_LEN 4 #define ACK_LEN 3 #define BYE_LEN 3 struct msg_start { int type; /* Type of the Message - Request/Response */ int len; /* length including delimiter */ union { struct { str method; /* Method string */ str uri; /* Request URI */ str version; /* SIP version */ int method_value; } request; struct { str version; /* SIP version */ str status; /* Reply status */ str reason; /* Reply reason phrase */ unsigned int /* statusclass,*/ statuscode; } reply; }u; }; char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl); char* parse_fline(char* buffer, char* end, struct msg_start* fl); #endif /* PARSE_FLINE_H */ opensips-2.2.2/parser/parse_from.c000066400000000000000000000062211300170765700171610ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-07-12 missing TAG is reported as error (bogdan) */ #include "parse_from.h" #include "parse_to.h" #include "parse_uri.h" #include #include #include "../errinfo.h" #include "../dprint.h" #include "../ut.h" #include "../mem/mem.h" #include "msg_parser.h" /* * This method is used to parse the from header. It was decided not to parse * anything in core that is not *needed* so this method gets called by * rad_acc module and any other modules that needs the FROM header. * * params: msg : sip msg * returns =0 on success, * <0 on failure. */ int parse_from_header( struct sip_msg *msg) { struct to_body* from_b; if ( !msg->from && ( parse_headers(msg,HDR_FROM_F,0)==-1 || !msg->from)) { LM_ERR("bad msg or missing FROM header\n"); goto error; } /* maybe the header is already parsed! */ if (msg->from->parsed) return 0; /* bad luck! :-( - we have to parse it */ /* first, get some memory */ from_b = pkg_malloc(sizeof(struct to_body)); if (from_b == 0) { LM_ERR("out of pkg_memory\n"); goto error; } /* now parse it!! */ memset(from_b, 0, sizeof(struct to_body)); parse_to(msg->from->body.s,msg->from->body.s+msg->from->body.len+1,from_b); if (from_b->error == PARSE_ERROR) { LM_ERR("bad from header\n"); pkg_free(from_b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing From header"); set_err_reply(400, "bad header"); goto error; } /* REGISTER doesn't have a from tag :( -bogdan if (from_b->tag_value.len==0 || from_b->tag_value.s==0) { LM_ERR("missing TAG value\n"); free_to(from_b); goto error; } */ msg->from->parsed = from_b; return 0; error: return -1; } /** * */ struct sip_uri *parse_from_uri(struct sip_msg *msg) { struct to_body *tb = NULL; if(msg==NULL) return NULL; if(parse_from_header(msg)<0) { LM_ERR("cannot parse FROM header\n"); return NULL; } if(msg->from==NULL || get_from(msg)==NULL) return NULL; tb = get_from(msg); if(tb->parsed_uri.user.s!=NULL || tb->parsed_uri.host.s!=NULL) return &tb->parsed_uri; if (parse_uri(tb->uri.s, tb->uri.len , &tb->parsed_uri)<0) { LM_ERR("failed to parse From uri\n"); memset(&tb->parsed_uri, 0, sizeof(struct sip_uri)); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing From uri"); set_err_reply(400, "bad From uri"); return NULL; } return &tb->parsed_uri; } opensips-2.2.2/parser/parse_from.h000066400000000000000000000022441300170765700171670ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PARSE_FROM_H #define _PARSE_FROM_H #include "msg_parser.h" /* casting macro for accessing From body */ #define get_from(p_msg) ((struct to_body*)(p_msg)->from->parsed) #define free_from(_to_body_) free_to(_to_body_) /* * From header field parser */ int parse_from_header( struct sip_msg *msg); struct sip_uri *parse_from_uri(struct sip_msg *msg); #endif opensips-2.2.2/parser/parse_hname2.c000066400000000000000000000156321300170765700173760ustar00rootroot00000000000000/* * Fast 32-bit Header Field Name Parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2003-05-01 added support for Accept HF (janakj) * 2006-02-17 Session-Expires, Min-SE (dhsueh@somanetworks.com) */ #include "parse_hname2.h" #include "keys.h" #include "../ut.h" /* q_memchr */ #define LOWER_BYTE(b) ((b) | 0x20) #define LOWER_DWORD(d) ((d) | 0x20202020) /* * Skip all white-chars and return position of the first * non-white char */ static inline char* skip_ws(char* p, char *end) { for(; p < end; p++) { if ((*p != ' ') && (*p != '\t')) return p; } return p; } /* * Parser macros */ #include "case_via.h" /* Via */ #include "case_from.h" /* From */ #include "case_to.h" /* To */ #include "case_cseq.h" /* CSeq */ #include "case_call.h" /* Call-ID */ #include "case_cont.h" /* Contact, Content-Type, Content-Length, Content-Purpose, Content-Action, Content-Disposition */ #include "case_rout.h" /* Route */ #include "case_max.h" /* Max-Forwards */ #include "case_reco.h" /* Record-Route */ #include "case_path.h" /* Path */ #include "case_auth.h" /* Authorization */ #include "case_expi.h" /* Expires */ #include "case_prox.h" /* Proxy-Authorization, Proxy-Require */ #include "case_allo.h" /* Allow */ #include "case_unsu.h" /* Unsupported */ #include "case_even.h" /* Event */ #include "case_acce.h" /* Accept, Accept-Language */ #include "case_orga.h" /* Organization */ #include "case_prio.h" /* Priority */ #include "case_subj.h" /* Subject */ #include "case_user.h" /* User-Agent */ #include "case_supp.h" /* Supported */ #include "case_dive.h" /* Diversion */ #include "case_remo.h" /* Remote-Party-ID */ #include "case_refe.h" /* Refer-To */ #include "case_sess.h" /* Session-Expires */ #include "case_min_.h" /* Min-SE */ #include "case_p_pr.h" /* P-Preferred-Identity */ #include "case_p_as.h" /* P-Asserted-Identity */ #include "case_priv.h" /* Privacy */ #include "case_retr.h" /* Retry-After */ #include "case_www.h" /* WWW-Authenticate */ #define READ(val) \ (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)) #define FIRST_QUATERNIONS \ case _via1_: via1_CASE; \ case _from_: from_CASE; \ case _to12_: to12_CASE; \ case _cseq_: cseq_CASE; \ case _call_: call_CASE; \ case _cont_: cont_CASE; \ case _rout_: rout_CASE; \ case _max__: max_CASE; \ case _reco_: reco_CASE; \ case _via2_: via2_CASE; \ case _auth_: auth_CASE; \ case _supp_: supp_CASE; \ case _expi_: expi_CASE; \ case _prox_: prox_CASE; \ case _allo_: allo_CASE; \ case _path_: path_CASE; \ case _unsu_: unsu_CASE; \ case _even_: even_CASE; \ case _acce_: acce_CASE; \ case _orga_: orga_CASE; \ case _prio_: prio_CASE; \ case _subj_: subj_CASE; \ case _user_: user_CASE; \ case _dive_: dive_CASE; \ case _remo_: remo_CASE; \ case _refe_: refe_CASE; \ case _sess_: sess_CASE; \ case _min__: min__CASE; \ case _p_pr_: p_pr_CASE; \ case _p_as_: p_as_CASE; \ case _priv_: priv_CASE; \ case _retr_: retr_CASE; \ case _www__: www_CASE; \ #define PARSE_COMPACT(id) \ switch(*(p + 1)) { \ case ' ': \ case '\t': \ hdr->type = id; \ hdr->name.len = 1; \ p += 2; \ goto dc_end; \ case ':': \ hdr->type = id; \ hdr->name.len = 1; \ return (p + 2); \ } char* parse_hname2(char* begin, char* end, struct hdr_field* hdr) { register char* p; register unsigned int val; if ((end - begin) < 4) { hdr->type = HDR_ERROR_T; return begin; } p = begin; val = LOWER_DWORD(READ(p)); hdr->name.s = begin; switch(val) { FIRST_QUATERNIONS; default: switch(LOWER_BYTE(*p)) { case 't': switch(LOWER_BYTE(*(p + 1))) { case 'o': p += 2; hdr->type = HDR_TO_T; hdr->name.len = 2; goto dc_cont; case ' ': case '\t': p += 2; hdr->type = HDR_TO_T; hdr->name.len = 1; goto dc_end; case ':': hdr->type = HDR_TO_T; hdr->name.len = 1; return (p + 2); } break; case 'v': PARSE_COMPACT(HDR_VIA_T); break; case 'f': PARSE_COMPACT(HDR_FROM_T); break; case 'i': PARSE_COMPACT(HDR_CALLID_T); break; case 'm': PARSE_COMPACT(HDR_CONTACT_T); break; case 'l': PARSE_COMPACT(HDR_CONTENTLENGTH_T); break; case 'k': PARSE_COMPACT(HDR_SUPPORTED_T); break; case 'c': PARSE_COMPACT(HDR_CONTENTTYPE_T); break; case 'o': PARSE_COMPACT(HDR_EVENT_T); break; case 'x': PARSE_COMPACT(HDR_SESSION_EXPIRES_T); break; } goto other; } /* the above swtich will never continue here */ dc_end: /* HDR name entirely found, consume WS till colon */ /* overflow during the "switch-case" parsing ? */ if (p>=end) goto error; p = skip_ws(p, end); if (*p != ':') goto error; /* hdr type, name should be already set at this point */ return (p+1); /*done*/ dc_cont: /* HDR name partially found, see what's next */ /* overflow during the "switch-case" parsing ? */ if (p>=end) goto error; /* hdr type, name should be already set at this point (for partial finding) */ switch (*p) { case ':' : return (p+1); case ' ': case '\t': /* consume spaces to the end of name */ p = skip_ws( p+1, end); if (*p != ':') goto error; return (p+1); /* default: it seems the hdr name continues, fall to "other" */ } other: /* Unknown header type */ hdr->type = HDR_OTHER_T; /* if overflow during the "switch-case" parsing, the "while" will * exit and we will fall in the "error" section */ while ( p < end ) { switch (*p) { case ':' : hdr->name.len = p - hdr->name.s; return (p + 1); case ' ' : case '\t': hdr->name.len = p - hdr->name.s; p = skip_ws(p+1, end); if (*p != ':') goto error; return (p+1); } p++; } error: /* No double colon found, error.. */ hdr->type = HDR_ERROR_T; hdr->name.s = 0; hdr->name.len = 0; return 0; } opensips-2.2.2/parser/parse_hname2.h000066400000000000000000000020501300170765700173710ustar00rootroot00000000000000/* * Fast 32-bit Header Field Name Parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARSE_HNAME2_H #define PARSE_HNAME2_H #include "hf.h" /* * Fast 32-bit header field name parser */ char* parse_hname2(char* begin, char* end, struct hdr_field* hdr); #endif /* PARSE_HNAME2_H */ opensips-2.2.2/parser/parse_methods.c000066400000000000000000000211461300170765700176640ustar00rootroot00000000000000/* * Copyright (c) 2004 Juha Heinanen * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-07-05 - moved and merged method types in msg_parser.h (ramona) * - changed and exported parse_method() to use it from other * files (ramona) */ #include #include "../dprint.h" #include "../trim.h" #include "../core_stats.h" #include "parse_methods.h" #include "msg_parser.h" /* * Check if argument is valid RFC3261 token character. */ static inline int method_char(char _c) { return (_c >= 65 && _c <= 90) /* upper alpha */ || (_c >= 97 && _c <= 122) /* lower aplha */ || (_c >= 48 && _c <= 57) /* digits */ || (_c == '-') || (_c == '.') || (_c == '%') || (_c == '*') || (_c == '_') || (_c == '+') || (_c == '~') || (_c == '+'); } /* * Parse a method pointed by start, end is the last character to check (if NULL * assume that start is a zero terminated string) * => assign enum bit to method. * Returns pointer to next char if parse succeeded * and NULL otherwise. */ char* parse_method(char* start, char* end, unsigned int* method) { int len=0; int max=0; if (!start || !method) { LM_ERR("invalid parameter value\n"); return NULL; } if(end) max = end - start; *method = METHOD_UNDEF; switch (start[0]) { case 'A': case 'a': if(end && max<3) goto unknown; if ((start[1]=='c' || start[1]=='C') && (start[2]=='k' || start[2]=='K')) { *method = METHOD_ACK; len = 3; goto done; } goto unknown; case 'B': case 'b': if(end && max<3) goto unknown; if ((start[1]=='y' || start[1]=='Y') && (start[2]=='e' || start[2]=='E')) { *method = METHOD_BYE; len = 3; goto done; } goto unknown; case 'C': case 'c': if(end && max<6) goto unknown; if ((start[1]=='a' || start[1]=='A') && (start[2]=='n' || start[2]=='N') && (start[3]=='c' || start[3]=='C') && (start[4]=='e' || start[4]=='E') && (start[5]=='l' || start[5]=='L')) { *method = METHOD_CANCEL; len = 6; goto done; } goto unknown; case 'I': case 'i': if(end && max<4) goto unknown; if(start[1]=='n' && start[1]=='N') goto unknown; if ((start[2]=='f' || start[2]=='F') && (start[3]=='o' || start[3]=='O')) { *method = METHOD_INFO; len = 4; goto done; } if(end && max<6) goto unknown; if ((start[2]=='v' || start[2]=='V') && (start[3]=='i' || start[3]=='I') && (start[4]=='t' || start[4]=='T') && (start[5]=='e' || start[5]=='E')) { *method = METHOD_INVITE; len = 6; goto done; } goto unknown; case 'M': case 'm': if(end && max<7) goto unknown; if ((start[1]=='e' || start[1]=='E') && (start[2]=='s' || start[2]=='S') && (start[3]=='s' || start[3]=='S') && (start[4]=='a' || start[4]=='A') && (start[5]=='g' || start[5]=='G') && (start[6]=='e' || start[6]=='E')) { *method = METHOD_MESSAGE; len = 7; goto done; } goto unknown; case 'N': case 'n': if(end && max<6) goto unknown; if ((start[1]=='o' || start[1]=='O') && (start[2]=='t' || start[2]=='T') && (start[3]=='i' || start[3]=='I') && (start[4]=='f' || start[4]=='F') && (start[5]=='y' || start[5]=='Y')) { *method = METHOD_NOTIFY; len = 6; goto done; } goto unknown; case 'O': case 'o': if(end && max<7) goto unknown; if((start[1]=='p' || start[1]=='P') && (start[2]=='t' || start[2]=='T') && (start[3]=='i' || start[3]=='I') && (start[4]=='o' || start[4]=='O') && (start[5]=='n' || start[5]=='N') && (start[6]=='s' || start[6]=='S')) { *method = METHOD_OPTIONS; len = 7; goto done; } goto unknown; case 'P': case 'p': if(end && max<5) goto unknown; if((start[1]=='r' || start[1]=='R') && (start[2]=='a' || start[2]=='A') && (start[3]=='c' || start[3]=='C') && (start[4]=='k' || start[4]=='K')) { *method = METHOD_PRACK; len = 5; goto done; } if(end && max<7) goto unknown; if ((start[1]=='u' || start[1]=='U') && (start[2]=='b' || start[2]=='B') && (start[3]=='l' || start[3]=='L') && (start[4]=='i' || start[4]=='I') && (start[5]=='s' || start[5]=='S') && (start[6]=='h' || start[6]=='H')) { *method = METHOD_PUBLISH; len = 7; goto done; } goto unknown; case 'R': case 'r': if(end && max<5) goto unknown; if(start[1]!='e' && start[1]!='E') goto unknown; if((start[2]=='f' || start[2]=='F') && (start[3]=='e' || start[3]=='E') && (start[4]=='r' || start[4]=='R')) { *method = METHOD_REFER; len = 5; goto done; } if(end && max<8) goto unknown; if ((start[2]=='g' || start[2]=='G') && (start[3]=='i' || start[3]=='I') && (start[4]=='s' || start[4]=='S') && (start[5]=='t' || start[5]=='T') && (start[6]=='e' || start[6]=='E') && (start[7]=='r' || start[7]=='R')) { *method = METHOD_REGISTER; len = 8; goto done; } goto unknown; case 'S': case 's': if(end && max<9) goto unknown; if ((start[1]=='u' || start[1]=='U') && (start[2]=='b' || start[2]=='B') && (start[3]=='s' || start[3]=='S') && (start[4]=='c' || start[4]=='C') && (start[5]=='r' || start[5]=='R') && (start[6]=='i' || start[6]=='I') && (start[7]=='b' || start[7]=='B') && (start[8]=='e' || start[8]=='E')) { *method = METHOD_SUBSCRIBE; len = 9; goto done; } goto unknown; case 'U': case 'u': if(end && max<6) goto unknown; if ((start[1]=='p' || start[1]=='P') && (start[2]=='d' || start[2]=='D') && (start[3]=='a' || start[3]=='A') && (start[4]=='t' || start[4]=='T') && (start[5]=='e' || start[5]=='E')) { *method = METHOD_UPDATE; len = 6; goto done; } goto unknown; default: goto unknown; } done: if(!end || (end && len < max)) { if(start[len]!='\0' && start[len]!=',' && start[len]!=' ' && start[len]!='\t' && start[len]!='\r' && start[len]!='\n') goto unknown; } return (start+len); unknown: update_stat(unsupported_methods, 1); *method = METHOD_OTHER; if(end) { while(len < max) { if((start[len]=='\0' || start[len]==',' || start[len]==' ' || start[len]=='\t' || start[len]=='\r' || start[len]=='\n')) return (start+len); if(!method_char(start[len])) { LM_ERR("invalid character %c\n", start[len]); return NULL; } len++; } return end; } while(start[len]!='\0' && start[len]!=',' && start[len]!=' ' && start[len]!='\t' && start[len]!='\r' && start[len]!='\n') { if(!method_char(start[len])) { LM_ERR("invalid character %c!\n", start[len]); return NULL; } len++; } return (start+len); } /* * Parse comma separated list of methods pointed by _body and assign their * enum bits to _methods. Returns 0 on success and -1 on failure. */ int parse_methods(str* _body, unsigned int* _methods) { str next; char *p; char *p0; unsigned int method; if (!_body || !_methods) { LM_ERR("invalid parameter value\n"); return -1; } next.len = _body->len; next.s = _body->s; trim_leading(&next); *_methods = 0; if (next.len == 0) { goto done; } method = 0; p = next.s; while (p=next.s+next.len || *p == '\0') goto done; if (*p == ',') { p++; while(p=next.s+next.len) goto done; } else { LM_ERR("comma expected\n"); return -1; } } done: LM_DBG("methods 0x%X\n", *_methods); return 0; } opensips-2.2.2/parser/parse_methods.h000066400000000000000000000023001300170765700176600ustar00rootroot00000000000000/* * Copyright (c) 2004 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARSE_METHODS_H #define PARSE_METHODS_H #include "../str.h" #define ALL_METHODS (0xFFFFFFFF) /* * Parse comma separated list of methods pointed by _body and assign their * enum bits to _methods. Returns 1 on success and 0 on failure. */ char* parse_method(char* start, char* end, unsigned int* method); int parse_methods(str* _body, unsigned int* _methods); #endif /* PARSE_METHODS_H */ opensips-2.2.2/parser/parse_min_expires.c000066400000000000000000000030071300170765700205370ustar00rootroot00000000000000/* * Copyright (c) 2011 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2011-12-07 Initial revision (osas@voipembedded.com) */ #include "../error.h" #include "../dprint.h" #include "../errinfo.h" #include "parse_sst.h" int parse_min_expires(struct sip_msg *msg) { if (msg->min_expires==NULL && parse_headers(msg,HDR_MIN_EXPIRES_F,0)!=0 ) { LM_ERR("failed to parse Min=Expires\n"); return -1; } if (msg->min_expires) { /* We will re-uise the min-se parser here */ if (msg->min_expires->parsed == 0 && parse_sst_success != parse_min_se_body(msg->min_expires)) { set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing Min-Expires header"); set_err_reply(400, "bad headers"); return -1; } } return 0; } opensips-2.2.2/parser/parse_min_expires.h000066400000000000000000000023311300170765700205430ustar00rootroot00000000000000/* * Copyright (c) 2011 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2011-12-07 Initial revision (osas@voipembedded.com) */ #ifndef PARSE_MIN_EXPIRES_H #define PARSE_MIN_EXPIRES_H 1 #include "msg_parser.h" #include "parse_sst.h" /** * Parses the "Min-Expires" header for the given msg. * * @param msg the msg to be parsed * @return 0 on success */ int parse_min_expires(struct sip_msg *msg); #endif /* ! PARSE_MIN_EXPIRES_H */ opensips-2.2.2/parser/parse_multipart.c000066400000000000000000000127171300170765700202460ustar00rootroot00000000000000/** * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "../mem/mem.h" #include "msg_parser.h" #include "parse_content.h" #include "parse_hname2.h" #include "parser_f.h" #include "../ut.h" #include "sdp/sdp_helpr_funcs.h" #define parse_hname(_b,_e,_h) parse_hname2((_b),(_e),(_h)) struct part * new_part(void) { struct part * temp; temp = pkg_malloc(sizeof (struct part)); if (temp == 0) { LM_ERR("Unable to allocate memory\n"); return 0; } memset(temp, 0, sizeof (struct part)); return temp; }; char* get_hdr_field_unparsed(char* buf, char* end, struct hdr_field* hdr) { char* tmp; char *match; if ((*buf) == '\n' || (*buf) == '\r') { /* double crlf or lflf or crcr */ LM_DBG("found end of header\n"); hdr->type = HDR_EOH_T; return buf; } LM_DBG("Trying to get header:[%.*s]\n", (int)(end - buf), buf); tmp = parse_hname(buf, end, hdr); if (hdr->type == HDR_ERROR_T) { LM_ERR("bad header\n"); goto error_bad_hdr; } /* eliminate leading whitespace */ tmp = eat_lws_end(tmp, end); if (tmp >= end) { LM_ERR("hf empty\n"); goto error_bad_hdr; } /* just skip over it */ hdr->body.s = tmp; /* find end of header */ /* find lf */ do { match = q_memchr(tmp, '\n', end - tmp); if (match) { match++; } else { LM_ERR("bad body for <%s>(%d)\n", hdr->name.s, hdr->type); tmp = end; goto error_bad_hdr; } tmp = match; } while (match < end && ((*match == ' ') || (*match == '\t'))); tmp = match; hdr->body.len = match - hdr->body.s; /* jku: if \r covered by current length, shrink it */ trim_r(hdr->body); hdr->len = tmp - hdr->name.s; return tmp; error_bad_hdr: LM_ERR("Unable to parse headers\n"); hdr->type = HDR_ERROR_T; hdr->len = tmp - hdr->name.s; return tmp; } struct part * parse_single_part(char * start, char * end) { struct part * ret; char * tmp, *body_end, * mime_end; unsigned int mime; if ((ret = new_part()) == 0) return 0; ret->all_data.s = start; ret->all_data.len = end - start; ret->content_type = -1; LM_DBG("parsing part:[%.*s]\n",(int)(end-start),start); tmp = start; while (1) { struct hdr_field hd; memset(&hd, 0, sizeof (struct hdr_field)); tmp = get_hdr_field_unparsed(tmp, end, &hd); if (tmp == NULL || hd.type == HDR_ERROR_T) { LM_ERR("Error parsing header\n"); return 0; } if (hd.type == HDR_CONTENTTYPE_T) { body_end = hd.body.s + hd.body.len; mime_end = decode_mime_type(hd.body.s, body_end, &mime, NULL); if (mime_end == NULL) { LM_ERR("Error parsing MIME\n"); return 0; } ret->content_type = mime; } if (hd.type == HDR_EOH_T) { /* remove the last \n\r from the body */ tmp += 2; break; } } if (ret->content_type < 0) ret->content_type = ((TYPE_TEXT) << 16) + SUBTYPE_PLAIN; ret->body.s = tmp; ret->body.len = end - ret->body.s; return ret; }; inline struct multi_body * get_all_bodies(struct sip_msg * msg) { char *start, *end; int type = 0; struct part ** cur, * temp; str delimiter, body; /* is body already parsed ? */ if (msg->multi) return msg->multi; if ( get_body(msg,&body)!=0 || body.len==0) return 0; type = parse_content_type_hdr(msg); if (type <= 0) return 0; msg->multi = pkg_malloc(sizeof (struct multi_body)); if (msg->multi == 0) { LM_ERR("Unable to allocate memory\n"); return 0; } memset(msg->multi, 0, sizeof (struct multi_body)); msg->multi->boundary = ((content_t *) msg->content_type->parsed)->boundary; cur = &msg->multi->first; if ((type >> 16) == TYPE_MULTIPART) { msg->multi->from_multi_part = 1; delimiter = ((content_t*) msg->content_type->parsed)->boundary; LM_DBG("Starting parsing with boundary = [%.*s]\n", delimiter.len, delimiter.s); start = find_sdp_line_delimiter( body.s, body.s + body.len, delimiter); if (start == NULL) { LM_ERR("Unable to parse multipart type: malformed - missing delimiters\n"); return 0; } while (1) { end = find_sdp_line_delimiter(start + 1, body.s + body.len, delimiter); if (end == NULL) break; /* add 4 to delimiter 2 for "--" and 2 for "\n\r" */ /* subtract 2 from end for last "\n\r" */ temp = parse_single_part(start + delimiter.len + 4, end-2); if (temp == NULL) { LM_ERR("Unable to parse part:[%.*s]\n",(int)(end-start),start); return 0; } *cur = temp; cur = &temp->next; start = end; } } else { msg->multi->from_multi_part = 0; if ((temp = new_part()) == 0) return 0; temp->content_type = type; temp->body = body; temp->all_data = body; *cur = temp; msg->multi->part_count++; } return msg->multi; }; void free_multi_body(struct multi_body * multi) { struct part * p, *tmp; p = multi->first; while(p) { tmp = p; p = p->next; pkg_free(tmp); } pkg_free(multi); } opensips-2.2.2/parser/parse_multipart.h000066400000000000000000000031551300170765700202470ustar00rootroot00000000000000/** * Copyright (C) 2009 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _PARSE_MULITPART #define _PARSE_MULITPART struct part{ /* MIME content type */ int content_type; /* body of the current part */ str body; /* the whole part ( body + headers) */ str all_data; /* whatever information might be received from parsing the part */ void * parsed_data; struct part * next; }; struct multi_body { int from_multi_part; str boundary; int part_count; struct part * first; }; /* * If the body of the message is multipart get all the parts, * otherwise get a multi_body cotaining one element of the initial body. * Should be used if someone thinks that the message could be multipart * and needs to be interpreted. * */ struct multi_body * get_all_bodies(struct sip_msg * msg); void free_multi_body(struct multi_body *); #endif opensips-2.2.2/parser/parse_nameaddr.c000066400000000000000000000040701300170765700177710ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * -------- * 2003-03-24 Created by janakj * 2003-04-26 ZSW (jiri) */ #include #include "../dprint.h" #include "parse_nameaddr.h" #include "parser_f.h" #include "../ut.h" /* * Parse name-addr part, the given string can be longer, * parsing will stop when closing > is found */ int parse_nameaddr(str* _s, name_addr_t* _a) { char* uri_end; if (!_s || !_a) { LM_ERR("invalid parameter value\n"); return -1; } _a->name.s = _s->s; _a->uri.s = find_not_quoted(_s, '<'); if (_a->uri.s) { _a->name.len = _a->uri.s - _a->name.s; _a->uri.s++; /* We will skip < character */ } else { LM_ERR("no < found\n"); return -3; } _a->uri.len = _s->len - _a->name.len - 1; uri_end = find_not_quoted(&_a->uri, '>'); if (!uri_end) { LM_ERR("no > found\n"); return -4; } /* Total length of the field including <> */ _a->len = uri_end - _a->name.s + 1; _a->uri.len = uri_end - _a->uri.s; return 0; } /* * Print a name-addr structure, just for debugging */ void print_nameaddr(FILE* _o, name_addr_t* _a) { fprintf(_o, "---name-addr---\n"); fprintf(_o, "name: '%.*s'\n", _a->name.len, ZSW(_a->name.s)); fprintf(_o, "uri : '%.*s'\n", _a->uri.len, ZSW(_a->uri.s)); fprintf(_o, "len : %d\n", _a->len); fprintf(_o, "---/name-addr---\n"); } opensips-2.2.2/parser/parse_nameaddr.h000066400000000000000000000030251300170765700177750ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * -------- * 2003-03-24 Created by janakj */ #ifndef PARSE_NAMEADDR_H #define PARSE_NAMEADDR_H #include #include "../str.h" /* * Name-addr structure, see RFC3261 for more details */ typedef struct name_addr { str name; /* Display name part */ str uri; /* Uri part without surrounding <> */ int len; /* Total length of the field (including all * whitechars present in the parsed message */ } name_addr_t; /* * Parse name-addr part, the given string can be longer, * parsing will stop when closing > is found */ int parse_nameaddr(str* _s, name_addr_t* _a); /* * Print a name-addr structure, just for debugging */ void print_nameaddr(FILE* _o, name_addr_t* _a); #endif /* PARSE_NAMEADDR_H */ opensips-2.2.2/parser/parse_pai.c000066400000000000000000000041571300170765700167750ustar00rootroot00000000000000/* * Copyright (C) 2006 Juha Heinanen * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "parse_from.h" #include "parse_to.h" #include #include #include "../dprint.h" #include "msg_parser.h" #include "../ut.h" #include "../errinfo.h" #include "../mem/mem.h" /* * This method is used to parse P-Asserted-Identity header (RFC 3325). * * Currently only one name-addr / addr-spec is supported in the header * and it must contain a sip or sips URI. * * params: msg : sip msg * returns 0 on success, * -1 on failure. */ int parse_pai_header( struct sip_msg *msg ) { struct to_body* pai_b; if ( !msg->pai && (parse_headers(msg, HDR_PAI_F,0)==-1 || !msg->pai)) { goto error; } /* maybe the header is already parsed! */ if (msg->pai->parsed) return 0; /* bad luck! :-( - we have to parse it */ /* first, get some memory */ pai_b = pkg_malloc(sizeof(struct to_body)); if (pai_b == 0) { LM_ERR("out of pkg_memory\n"); goto error; } /* now parse it!! */ parse_to(msg->pai->body.s, msg->pai->body.s + msg->pai->body.len+1, pai_b); if (pai_b->error == PARSE_ERROR) { LM_ERR("bad P-Asserted-Identity header\n"); pkg_free(pai_b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing PAI header"); set_err_reply(400, "bad header"); goto error; } msg->pai->parsed = pai_b; return 0; error: return -1; } opensips-2.2.2/parser/parse_pai.h000066400000000000000000000021471300170765700167770ustar00rootroot00000000000000/* * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PARSE_PAI_H #define PARSE_PAI_H #include "msg_parser.h" /* casting macro for accessing P-Asserted-Identity body */ #define get_pai(p_msg) ((struct to_body*)(p_msg)->pai->parsed) /* * P-Asserted-Identity header field parser */ int parse_pai_header( struct sip_msg *msg); #endif /* PARSE_PAI_H */ opensips-2.2.2/parser/parse_param.c000066400000000000000000000257211300170765700173240ustar00rootroot00000000000000/* * Generic Parameter Parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-24 Created by janakj * 2003-04-07 shm duplication added (janakj) * 2003-04-07 URI class added (janakj) */ #include #include "../str.h" #include "../ut.h" #include "../dprint.h" #include "../trim.h" #include "../mem/mem.h" #include "../mem/shm_mem.h" #include "parse_param.h" /* * Try to find out parameter name, recognized parameters * are q, expires and methods */ static inline void parse_contact_class(param_hooks_t* _h, param_t* _p) { if (!_p->name.s) { LM_ERR("empty value\n"); return; } switch(_p->name.s[0]) { case 'q': case 'Q': if (_p->name.len == 1) { _p->type = P_Q; _h->contact.q = _p; } break; case 'e': case 'E': if ((_p->name.len == 7) && (!strncasecmp(_p->name.s + 1, "xpires", 6))) { _p->type = P_EXPIRES; _h->contact.expires = _p; } break; case 'm': case 'M': if ((_p->name.len == 7) && (!strncasecmp(_p->name.s + 1, "ethods", 6))) { _p->type = P_METHODS; _h->contact.methods = _p; } break; case 'r': case 'R': if ((_p->name.len == 8) && (!strncasecmp(_p->name.s + 1, "eceived", 7))) { _p->type = P_RECEIVED; _h->contact.received = _p; } break; case '+': if ((_p->name.len == 13) && (!strncasecmp(_p->name.s + 1, "sip.instance", 12))) { _p->type = P_INSTANCE; _h->contact.instance = _p; } break; } } /* * Try to find out parameter name, recognized parameters * are transport, lr, r2, maddr */ static inline void parse_uri_class(param_hooks_t* _h, param_t* _p) { if (!_p->name.s) { LM_ERR("empty value\n"); return; } switch(_p->name.s[0]) { case 't': case 'T': if ((_p->name.len == 9) && (!strncasecmp(_p->name.s + 1, "ransport", 8))) { _p->type = P_TRANSPORT; _h->uri.transport = _p; } else if (_p->name.len == 2) { if (((_p->name.s[1] == 't') || (_p->name.s[1] == 'T')) && ((_p->name.s[2] == 'l') || (_p->name.s[2] == 'L'))) { _p->type = P_TTL; _h->uri.ttl = _p; } } break; case 'l': case 'L': if ((_p->name.len == 2) && ((_p->name.s[1] == 'r') || (_p->name.s[1] == 'R'))) { _p->type = P_LR; _h->uri.lr = _p; } break; case 'r': case 'R': if ((_p->name.len == 2) && (_p->name.s[1] == '2')) { _p->type = P_R2; _h->uri.r2 = _p; } break; case 'm': case 'M': if ((_p->name.len == 5) && (!strncasecmp(_p->name.s + 1, "addr", 4))) { _p->type = P_MADDR; _h->uri.maddr = _p; } break; case 'd': case 'D': if ((_p->name.len == 5) && (!strncasecmp(_p->name.s + 1, "stip", 4))) { _p->type = P_DSTIP; _h->uri.dstip = _p; } else if ((_p->name.len == 7) && (!strncasecmp(_p->name.s + 1, "stport", 6))) { _p->type = P_DSTPORT; _h->uri.dstport = _p; } break; } } /* * Parse quoted string in a parameter body * return the string without quotes in _r * parameter and update _s to point behind the * closing quote */ static inline int parse_quoted_param(str* _s, str* _r) { char* end_quote; /* The string must have at least * surrounding quotes */ if (_s->len < 2) { return -1; } /* Skip opening quote */ _s->s++; _s->len--; /* Find closing quote */ end_quote = q_memchr(_s->s, '\"', _s->len); /* Not found, return error */ if (!end_quote) { return -2; } /* Let _r point to the string without * surrounding quotes */ _r->s = _s->s; _r->len = end_quote - _s->s; /* Update _s parameter to point * behind the closing quote */ _s->len -= (end_quote - _s->s + 1); _s->s = end_quote + 1; /* Everything went OK */ return 0; } /* * Parse unquoted token in a parameter body * let _r point to the token and update _s * to point right behind the token */ static inline int parse_token_param(str* _s, str* _r) { int i; /* There is nothing to parse, * return error */ if (_s->len == 0) { return -1; } /* Save the beginning of the * token in _r->s */ _r->s = _s->s; /* Iterate through the * token body */ for(i = 0; i < _s->len; i++) { /* All these characters * mark end of the token */ switch(_s->s[i]) { case ' ': case '\t': case '\r': case '\n': case ',': case ';': /* So if you find * any of them * stop iterating */ goto out; } } out: if (i == 0) { return -1; } /* Save length of the token */ _r->len = i; /* Update _s parameter so it points * right behind the end of the token */ _s->s = _s->s + i; _s->len -= i; /* Everything went OK */ return 0; } /* * Parse a parameter name */ static inline void parse_param_name(str* _s, pclass_t _c, param_hooks_t* _h, param_t* _p) { if (!_s->s) { LM_DBG("empty parameter\n"); return; } _p->name.s = _s->s; while(_s->len) { switch(_s->s[0]) { case ' ': case '\t': case '\r': case '\n': case ';': case ',': case '=': goto out; } _s->s++; _s->len--; } out: _p->name.len = _s->s - _p->name.s; switch(_c) { case CLASS_CONTACT: parse_contact_class(_h, _p); break; case CLASS_URI: parse_uri_class(_h, _p); break; default: break; } } /* * Parse body of a parameter. It can be quoted string or * a single token. */ static inline int parse_param_body(str* _s, param_t* _c) { if (_s->s[0] == '\"') { if (parse_quoted_param(_s, &(_c->body)) < 0) { LM_ERR("failed to parse quoted string\n"); return -2; } } else { if (parse_token_param(_s, &(_c->body)) < 0) { LM_ERR("failed to parse token\n"); return -3; } } return 0; } /* * Parse parameters * _s is string containing parameters, it will be updated to point behind the parameters * _c is class of parameters * _h is pointer to structure that will be filled with pointer to well known parameters * linked list of parsed parameters will be stored in * the variable _p is pointing to * The function returns 0 on success and negative number * on an error */ int parse_params(str* _s, pclass_t _c, param_hooks_t* _h, param_t** _p) { param_t* t; param_t* last; if (!_s || !_h || !_p) { LM_ERR("invalid parameter value\n"); return -1; } memset(_h, 0, sizeof(param_hooks_t)); last = NULL; *_p = 0; if (!_s->s) { /* no parameters at all -- we're done */ LM_DBG("empty uri params, skipping\n"); return 0; } LM_DBG("Parsing params for:[%.*s]\n",_s->len,_s->s); while(1) { t = (param_t*)pkg_malloc(sizeof(param_t)); if (t == 0) { LM_ERR("no pkg memory left\n"); goto error; } memset(t, 0, sizeof(param_t)); parse_param_name(_s, _c, _h, t); trim_leading(_s); if (_s->len == 0) { /* The last parameter without body */ t->len = t->name.len; goto ok; } if (_s->s[0] == '=') { _s->s++; _s->len--; trim_leading(_s); if (_s->len == 0) { LM_ERR("body missing\n"); goto error; } if (parse_param_body(_s, t) < 0) { LM_ERR("failed to parse param body\n"); goto error; } t->len = _s->s - t->name.s; trim_leading(_s); if (_s->len == 0) { goto ok; } } else { t->len = t->name.len; } if (_s->s[0]==',') goto ok; /* To be able to parse header parameters */ if (_s->s[0]=='>') goto ok; /* To be able to parse URI parameters */ if (_s->s[0] != ';') { LM_ERR("invalid character, ; expected, found %c \n",_s->s[0]); goto error; } _s->s++; _s->len--; trim_leading(_s); if (_s->len == 0) { LM_ERR("param name missing after ;\n"); goto error; } if (last) {last->next=t;} else {*_p = t;} last = t; } error: if (t) pkg_free(t); free_params(*_p); *_p = 0; return -2; ok: if (last) {last->next=t;} else {*_p = t;} _h->last_param = last = t; return 0; } /* * Free linked list of parameters */ static inline void do_free_params(param_t* _p, int _shm) { param_t* ptr; while(_p) { ptr = _p; _p = _p->next; if (_shm) shm_free(ptr); else pkg_free(ptr); } } /* * Free linked list of parameters */ void free_params(param_t* _p) { do_free_params(_p, 0); } /* * Free linked list of parameters */ void shm_free_params(param_t* _p) { do_free_params(_p, 1); } /* * Print a parameter structure, just for debugging */ static inline void print_param(FILE* _o, param_t* _p) { char* type; fprintf(_o, "---param(%p)---\n", _p); switch(_p->type) { case P_OTHER: type = "P_OTHER"; break; case P_Q: type = "P_Q"; break; case P_EXPIRES: type = "P_EXPIRES"; break; case P_METHODS: type = "P_METHODS"; break; case P_TRANSPORT: type = "P_TRANSPORT"; break; case P_LR: type = "P_LR"; break; case P_R2: type = "P_R2"; break; case P_MADDR: type = "P_MADDR"; break; case P_TTL: type = "P_TTL"; break; case P_RECEIVED: type = "P_RECEIVED"; break; case P_DSTIP: type = "P_DSTIP"; break; case P_DSTPORT: type = "P_DSTPORT"; break; default: type = "UNKNOWN"; break; } fprintf(_o, "type: %s\n", type); fprintf(_o, "name: \'%.*s\'\n", _p->name.len, _p->name.s); fprintf(_o, "body: \'%.*s\'\n", _p->body.len, _p->body.s); fprintf(_o, "len : %d\n", _p->len); fprintf(_o, "---/param---\n"); } /* * Print linked list of parameters, just for debugging */ void print_params(FILE* _o, param_t* _p) { param_t* ptr; ptr = _p; while(ptr) { print_param(_o, ptr); ptr = ptr->next; } } /* * Duplicate linked list of parameters */ static inline int do_duplicate_params(param_t** _n, param_t* _p, int _shm) { param_t* last, *ptr, *t; if (!_n) { LM_ERR("invalid parameter value\n"); return -1; } last = 0; *_n = 0; ptr = _p; while(ptr) { if (_shm) { t = (param_t*)shm_malloc(sizeof(param_t)); } else { t = (param_t*)pkg_malloc(sizeof(param_t)); } if (!t) { LM_ERR("no more pkg memory\n"); goto err; } memcpy(t, ptr, sizeof(param_t)); t->next = 0; if (!*_n) *_n = t; if (last) last->next = t; last = t; ptr = ptr->next; } return 0; err: do_free_params(*_n, _shm); return -2; } /* * Duplicate linked list of parameters */ int duplicate_params(param_t** _n, param_t* _p) { return do_duplicate_params(_n, _p, 0); } /* * Duplicate linked list of parameters */ int shm_duplicate_params(param_t** _n, param_t* _p) { return do_duplicate_params(_n, _p, 1); } opensips-2.2.2/parser/parse_param.h000066400000000000000000000104351300170765700173250ustar00rootroot00000000000000/* * Generic Parameter Parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-03-24 Created by janakj * 2003-04-07 shm duplication support (janakj) * 2003-04-07 URI class added (janakj) */ #ifndef PARSE_PARAM_H #define PARSE_PARAM_H #include #include "../str.h" /* * Supported types of parameters */ typedef enum ptype { P_OTHER = 0, /* Unknown parameter */ P_Q, /* Contact: q parameter */ P_EXPIRES, /* Contact: expires parameter */ P_METHODS, /* Contact: methods parameter (RFC 3840) */ P_RECEIVED, /* Contact: received parameter */ P_INSTANCE, /* Contact: sip.instance parameter */ P_TRANSPORT, /* URI: transport parameter */ P_LR, /* URI: lr parameter */ P_R2, /* URI: r2 parameter (ser specific) */ P_MADDR, /* URI: maddr parameter */ P_TTL, /* URI: ttl parameter */ P_DSTIP, /* URI: dstip parameter */ P_DSTPORT, /* URi: dstport parameter */ } ptype_t; /* * Class of parameters */ typedef enum pclass { CLASS_ANY = 0, /* Any parameters, well-known hooks will be not used */ CLASS_CONTACT, /* Contact parameters */ CLASS_URI /* URI parameters */ } pclass_t; /* * Structure representing a parameter */ typedef struct param { ptype_t type; /* Type of the parameter */ str name; /* Parameter name */ str body; /* Parameter body */ int len; /* Total length of the parameter including = and quotes */ struct param* next; /* Next parameter in the list */ } param_t; /* * Hooks to well known parameters for contact class of parameters */ struct contact_hooks { struct param* last_param;/* last param - MUST be first field */ struct param* expires; /* expires parameter */ struct param* q; /* q parameter */ struct param* methods; /* methods parameter (RFC 3840) */ struct param* received; /* received parameter */ struct param* instance; /* instance parameter */ }; /* * Hooks to well known parameter for URI class of parameters */ struct uri_hooks { struct param* last_param;/* last param - MUST be first field */ struct param* transport; /* transport parameter */ struct param* lr; /* lr parameter */ struct param* r2; /* r2 parameter */ struct param* maddr; /* maddr parameter */ struct param* ttl; /* ttl parameter */ struct param* dstip; /* Destination IP */ struct param* dstport; /* Destination port */ }; /* * Union of hooks structures for all classes */ typedef union param_hooks { struct contact_hooks contact; /* Contact hooks */ struct uri_hooks uri; /* URI hooks */ struct param* last_param; /* last param */ } param_hooks_t; /* * Parse parameters * _s is string containing parameters * _c is class of parameters * _h is pointer to structure that will be filled with pointer to well known parameters * linked list of parsed parameters will be stored in * the variable _p is pointing to * The function returns 0 on success and negative number * on an error */ int parse_params(str* _s, pclass_t _c, param_hooks_t* _h, param_t** _p); /* * Free linked list of parameters */ void free_params(param_t* _p); /* * Free linked list of parameters from shared memory */ void shm_free_params(param_t* _p); /* * Print linked list of parameters, just for debugging */ void print_params(FILE* _o, param_t* _p); /* * Duplicate linked list of parameters */ int duplicate_params(param_t** _n, param_t* _p); /* * Duplicate linked list of parameters */ int shm_duplicate_params(param_t** _n, param_t* _p); #endif /* PARSE_PARAM_H */ opensips-2.2.2/parser/parse_ppi.c000066400000000000000000000055771300170765700170230ustar00rootroot00000000000000/* * Copyright (C) 2006 Juha Heinanen * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "parse_ppi.h" #include "parse_to.h" #include "parse_uri.h" #include #include #include "../dprint.h" #include "msg_parser.h" #include "../ut.h" #include "../errinfo.h" #include "../mem/mem.h" /* * This method is used to parse P-Preferred-Identity header (RFC 3325). * * Currently only one name-addr / addr-spec is supported in the header * and it must contain a sip or sips URI. * * params: msg : sip msg * returns 0 on success, * -1 on failure. */ int parse_ppi_header( struct sip_msg *msg ) { struct to_body* ppi_b; if ( !msg->ppi && (parse_headers(msg, HDR_PPI_F,0)==-1 || !msg->ppi)) { goto error; } /* maybe the header is already parsed! */ if (msg->ppi->parsed) return 0; /* bad luck! :-( - we have to parse it */ /* first, get some memory */ ppi_b = pkg_malloc(sizeof(struct to_body)); if (ppi_b == 0) { LM_ERR("out of pkg_memory\n"); goto error; } /* now parse it!! */ parse_to(msg->ppi->body.s, msg->ppi->body.s + msg->ppi->body.len+1, ppi_b); if (ppi_b->error == PARSE_ERROR) { LM_ERR("bad P-Preferred-Identity header\n"); pkg_free(ppi_b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing PPI header"); set_err_reply(400, "bad header"); goto error; } msg->ppi->parsed = ppi_b; return 0; error: return -1; } /** * Parse P-Preferred-Identity header URI */ struct sip_uri *parse_ppi_uri(struct sip_msg *msg) { struct to_body *tb = NULL; if(msg==NULL) return NULL; if(parse_ppi_header(msg)<0) { LM_ERR("cannot parse P-P-I header\n"); return NULL; } if(msg->ppi==NULL || get_ppi(msg)==NULL) return NULL; tb = get_ppi(msg); if(tb->parsed_uri.user.s!=NULL || tb->parsed_uri.host.s!=NULL) return &tb->parsed_uri; if (parse_uri(tb->uri.s, tb->uri.len , &tb->parsed_uri)<0) { LM_ERR("failed to parse P-P-I URI\n"); memset(&tb->parsed_uri, 0, sizeof(struct sip_uri)); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing P-P-I URI"); set_err_reply(400, "bad P-Preferred-Identity uri"); return NULL; } return &tb->parsed_uri; } opensips-2.2.2/parser/parse_ppi.h000066400000000000000000000022361300170765700170150ustar00rootroot00000000000000/* * Copyright (C) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PARSE_PPI_H #define PARSE_PPI_H #include "msg_parser.h" /* casting macro for accessing P-Preferred-Identity body */ #define get_ppi(p_msg) ((struct to_body*)(p_msg)->ppi->parsed) /* * P-Preferred-Identity header field parser */ int parse_ppi_header( struct sip_msg *msg); struct sip_uri *parse_ppi_uri(struct sip_msg *msg); #endif /* PARSE_PPI_H */ opensips-2.2.2/parser/parse_privacy.c000066400000000000000000000112671300170765700177010ustar00rootroot00000000000000/* * Copyright (c) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2006-12-18 Introduced parsing of Privacy header (RFC 3323) */ #include #include #include "../dprint.h" #include "../trim.h" #include "../errinfo.h" #include "parse_privacy.h" #include "msg_parser.h" /* * Parse a privacy value pointed by start that can be at most max_len long. * Returns length of matched privacy value or NULL otherwise. */ unsigned int parse_priv_value(char* start, unsigned int max_len, unsigned int* value) { unsigned int len; if (!start || !value) { LM_ERR("invalid parameter value\n"); return 0; } switch (start[0]) { case 'c': case 'C': if(max_len < 8) return 0; if (strncasecmp(start, "critical", 8) == 0) { *value = PRIVACY_CRITICAL; len = 8; break; } else { return 0; } case 'h': case 'H': if (max_len < 6) return 0; if (strncasecmp(start, "header", 6) == 0) { *value = PRIVACY_HEADER; len = 6; break; } if (max_len < 7) return 0; if (strncasecmp(start, "history", 7) == 0) { *value = PRIVACY_HISTORY; len = 7; break; } else { return 0; } case 'i': case 'I': if(max_len < 2) return 0; if (start[1] == 'd' || start[1] == 'D') { *value = PRIVACY_ID; len = 2; break; } else { return 0; } case 'n': case 'N': if(max_len < 4) return 0; if (strncasecmp(start, "none", 4) == 0) { *value = PRIVACY_NONE; len = 4; break; } else { return 0; } case 's': case 'S': if(max_len < 7) return 0; if (strncasecmp(start, "session", 7) == 0) { *value = PRIVACY_SESSION; len = 7; break; } else { return 0; } case 'u': case 'U': if(max_len < 4) return 0; if (strncasecmp(start, "user", 4) == 0) { *value = PRIVACY_USER; len = 4; break; } else { return 0; } default: return 0; } if(len < max_len) { if(start[len] != '\0' && start[len] != ';' && start[len] != ' ' && start[len] != '\t' && start[len] != '\r' && start[len] != '\n') return 0; } return len; } /* * This method is used to parse Privacy HF body, which consist of * comma separated list of priv-values. After parsing, msg->privacy->parsed * contains enum bits of privacy values defined in parse_privacy.h. * Returns 0 on success and -1 on failure. */ int parse_privacy(struct sip_msg *msg) { unsigned int val_len, value, values, len; str next; char *p, *beyond; /* maybe the header is already parsed! */ if (msg->privacy && msg->privacy->parsed) return 0; /* parse Privacy HF (there should be only one) */ if (!msg->privacy && (parse_headers(msg, HDR_PRIVACY_F, 0) == -1 || !msg->privacy)) { goto error; } next.len = msg->privacy->body.len; next.s = msg->privacy->body.s; trim_leading(&next); if (next.len == 0) { LM_ERR("no values\n"); goto parse_error; } values = 0; p = next.s; len = next.len; beyond = p + len; while (p < beyond) { if((val_len = parse_priv_value(p, len, &value)) != 0) { values |= value; p = p + val_len; len = len - val_len; } else { LM_ERR("invalid privacy value\n"); goto parse_error; } while(p < beyond && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) p++; if(p >= beyond) break; if (*p == ';') { p++; while(p < beyond && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) p++; if(p >= beyond) { LM_ERR("no privacy value after comma\n"); goto parse_error; } } else { LM_ERR("semicolon expected\n"); goto parse_error; } } if ((values & PRIVACY_NONE) && (values ^ PRIVACY_NONE)) { LM_ERR("no other privacy values allowed with 'none'\n"); goto parse_error; } msg->privacy->parsed = (void *)(long)values; return 0; parse_error: set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing privacy header"); set_err_reply(400, "bad headers"); error: return -1; } opensips-2.2.2/parser/parse_privacy.h000066400000000000000000000032771300170765700177100ustar00rootroot00000000000000/* * Copyright (c) 2006 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PARSE_PRIVACY_H #define PARSE_PRIVACY_H #include "../mem/mem.h" #include "msg_parser.h" /* bitmap of Privacy header privacy values * (http://www.iana.org/assignments/sip-priv-values) */ enum privacy_value { PRIVACY_USER=1, PRIVACY_HEADER=2, PRIVACY_SESSION=4, PRIVACY_NONE=8, PRIVACY_CRITICAL=16, PRIVACY_ID=32, PRIVACY_HISTORY=64 }; /* * casting macro for accessing enumeration of priv-values */ #define get_privacy_values(p_msg) \ ((unsigned int)(long)((p_msg)->privacy->parsed)) /* * Parse Privacy HF. Returns 0 on success and -1 on failure. */ int parse_privacy(struct sip_msg *msg); /* * Parse a privacy value pointed by start that can be at most max_len long. * Returns length of matched privacy value or NULL otherwise. */ unsigned int parse_priv_value(char* start, unsigned int max_len, unsigned int* value); #endif /* PARSE_PRIVACY_H */ opensips-2.2.2/parser/parse_refer_to.c000066400000000000000000000040031300170765700200170ustar00rootroot00000000000000/* * Copyright (C) 2005 Juha Heinanen * * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../dprint.h" #include "../ut.h" #include "../errinfo.h" #include "../mem/mem.h" #include "msg_parser.h" #include "parse_from.h" #include "parse_to.h" /* * This method is used to parse Refer-To header. * * params: msg : sip msg * returns 0 on success, * -1 on failure. */ int parse_refer_to_header( struct sip_msg *msg ) { struct to_body* refer_to_b; if ( !msg->refer_to && (parse_headers(msg, HDR_REFER_TO_F,0)==-1 || !msg->refer_to)) { goto error; } /* maybe the header is already parsed! */ if (msg->refer_to->parsed) return 0; /* bad luck! :-( - we have to parse it */ /* first, get some memory */ refer_to_b = pkg_malloc(sizeof(struct to_body)); if (refer_to_b == 0) { LM_ERR("out of pkg_memory\n"); goto error; } /* now parse it!! */ parse_to(msg->refer_to->body.s, msg->refer_to->body.s + msg->refer_to->body.len+1, refer_to_b); if (refer_to_b->error == PARSE_ERROR) { LM_ERR("bad Refer-To header\n"); pkg_free(refer_to_b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing REFER-TO header"); set_err_reply(400, "bad headers"); goto error; } msg->refer_to->parsed = refer_to_b; return 0; error: return -1; } opensips-2.2.2/parser/parse_refer_to.h000066400000000000000000000021571300170765700200340ustar00rootroot00000000000000/* * Copyright (C) 2005 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PARSE_REFER_TO_H #define PARSE_REFER_TO_H #include "msg_parser.h" /* casting macro for accessing Refer-To body */ #define get_refer_to(p_msg) ((struct to_body*)(p_msg)->refer_to->parsed) /* * Refer-To header field parser */ int parse_refer_to_header( struct sip_msg *msg); #endif /* PARSE_REFER_TO_H */ opensips-2.2.2/parser/parse_replaces.c000066400000000000000000000165571300170765700200310ustar00rootroot00000000000000/* * Copyright (C) 2011 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2011-06-28 initial implementation (Ovidiu Sas) */ #include #include #include "../dprint.h" #include "../ut.h" #include "../errinfo.h" #include "../mem/mem.h" #include "parse_replaces.h" /* * This method is used to parse Replaces header body. * * params: buf : pointer to Replaces body * buf_len : Replaces body length * replaces_b : pointer to parsing structure * returns 0 on success, * -1 on failure. */ int parse_replaces_body(char* buf, int buf_len, struct replaces_body* replaces_b) { enum states {CLID, PARAM, PARAM_P, PARAM_VAL_P, VAL_P, /* param states */ /* to-tag */ /* 5 - 11 */ TT_T, TT_O, TT__, TT_T2, TT_A, TT_G, TT_eq, /* from-tag */ /* 12 - 20 */ FT_F, FT_R, FT_O, FT_M, FT__, FT_T, FT_A, FT_G, FT_eq, /* early-only */ /* 21 - 31 */ EO_E, EO_A, EO_R, EO_L, EO_Y, EO__, EO_O, EO_N, EO_L2, EO_Y2_FIN, EO_eq }; register enum states state; char* b = NULL; /* current param val */ char* v = NULL; /* current param val */ str* param = NULL; str* param_val = NULL; register char* p; char* end; #define param_set(t_start, v_start) \ param->s=(t_start); \ param->len=(p-(t_start)); \ param_val->s=(v_start); \ param_val->len=(p-(v_start)) #define u_param_set(t_start, v_start) \ /* FIXME: save unknown params */ #define semicolon_case \ case';': \ state=PARAM /* new param */ #define param_common_cases \ semicolon_case; \ break #define u_param_common_cases \ semicolon_case; \ u_param_set(b, v); \ break #define param_single_switch(old_state, c1, new_state) \ case old_state: \ switch(*p){ \ case c1: \ state=(new_state); \ break; \ u_param_common_cases; \ default: \ state=PARAM_P; \ } \ break #define param_switch(old_state, c1, c2, new_state) \ case old_state: \ switch(*p){ \ case c1: \ case c2: \ state=(new_state); \ break; \ u_param_common_cases; \ default: \ state=PARAM_P; \ } \ break #define param_switch1(old_state, c1, new_state) \ case old_state: \ switch(*p){ \ case c1: \ state=(new_state); \ break; \ param_common_cases; \ default: \ state=PARAM_P; \ } \ break #define value_common_cases \ semicolon_case; \ param_set(b, v); \ break #define value_switch(old_state, c1, c2, new_state) \ case old_state: \ switch(*p){ \ case c1: \ case c2: \ state=(new_state); \ break; \ value_common_cases; \ default: \ state=VAL_P; \ } \ break /* init */ p = buf; end = buf + buf_len; state = CLID; memset(replaces_b, 0, sizeof(struct replaces_body)); for(;pcallid_val.s=buf; replaces_b->callid_val.len=p-buf; state = PARAM; break; } break; case PARAM: /* beginning of a new param */ switch(*p){ param_common_cases; /* recognized params */ case 't': case 'T': b = p; state=TT_T; break; case 'f': case 'F': b = p; state=FT_F; break; case 'e': case 'E': b = p; state=EO_E; break; default: b = p; state=PARAM; } break; case PARAM_P: /* ignore current param */ /* supported params: to-tag, from-tag, early-only */ switch(*p){ u_param_common_cases; case '=': v=p+1; state=PARAM_VAL_P; break; }; break; case PARAM_VAL_P: /* value of the ignored current param */ switch(*p){ u_param_common_cases; }; break; case VAL_P: switch(*p){ value_common_cases; } break; /* early-only param */ param_switch(EO_E, 'a', 'A', EO_A); param_switch(EO_A, 'r', 'R', EO_R); param_switch(EO_R, 'l', 'L', EO_L); param_switch(EO_L, 'y', 'Y', EO_Y); param_single_switch(EO_Y, '-', EO__); param_switch(EO__, 'o', 'O', EO_O); param_switch(EO_O, 'n', 'N', EO_N); param_switch(EO_N, 'l', 'L', EO_L2); param_switch(EO_L2, 'y', 'Y', EO_Y2_FIN); case EO_Y2_FIN: switch(*p){ case '=': state = EO_eq; break; semicolon_case; replaces_b->early_only.s=b; replaces_b->early_only.len=(p-b); break; default: state=PARAM_P; } break; /* handle early-only=something case */ case EO_eq: param = &replaces_b->early_only; param_val = &replaces_b->early_only_val; switch(*p){ param_common_cases; default: v=p; state = VAL_P; } break; /* from-tag param */ param_switch(FT_F, 'r', 'R', FT_R); param_switch(FT_R, 'o', 'O', FT_O); param_switch(FT_O, 'm', 'M', FT_M); param_single_switch(FT_M, '-', FT__); param_switch(FT__, 't', 'T', FT_T); param_switch(FT_T, 'a', 'A', FT_A); param_switch(FT_A, 'g', 'G', FT_G); param_switch1(FT_G, '=', FT_eq); case FT_eq: param = &replaces_b->from_tag; param_val = &replaces_b->from_tag_val; switch(*p){ param_common_cases; default: v=p; state = VAL_P; } break; /* to-tag param */ param_switch(TT_T, 'o', 'O', TT_O); param_single_switch(TT_O, '-', TT__); param_switch(TT__, 't', 'T', TT_T2); param_switch(TT_T2, 'a', 'A', TT_A); param_switch(TT_A, 'g', 'G', TT_G); param_switch1(TT_G, '=', TT_eq); case TT_eq: param = &replaces_b->to_tag; param_val = &replaces_b->to_tag_val; switch(*p){ param_common_cases; default: v=p; state = VAL_P; } break; default: LM_CRIT("bad state %d parsed: <%.*s> (%d) / <%.*s> (%d)\n", state, (int)(p-buf), ZSW(buf), (int)(p-buf), buf_len, ZSW(buf), buf_len); return -1; } } switch(state){ case CLID: replaces_b->callid_val.s=buf; replaces_b->callid_val.len=p-buf; break; case PARAM: case PARAM_P: case PARAM_VAL_P: u_param_set(b, v); /* intermediate param states */ case EO_E: /* early-only */ case EO_A: case EO_R: case EO_L: case EO_Y: case EO__: case EO_O: case EO_N: case EO_L2: case FT_F: /* from-tag */ case FT_R: case FT_O: case FT_M: case FT__: case FT_T: case FT_A: case FT_G: case FT_eq: /* ignore empty from-tag params */ case TT_T: /* to-tag */ case TT_O: case TT__: case TT_T2: case TT_A: case TT_G: case TT_eq: /* ignore empty to-tag params */ break; /* fin param states */ case EO_Y2_FIN: case EO_eq: replaces_b->early_only.s=b; replaces_b->early_only.len=p-b; break; case VAL_P: param_set(b, v); break; default: LM_CRIT("bad state %d parsed: <%.*s> (%d) / <%.*s> (%d)\n", state, (int)(p-buf), ZSW(buf), (int)(p-buf), buf_len, ZSW(buf), buf_len); return -1; } return 0; } opensips-2.2.2/parser/parse_replaces.h000066400000000000000000000027221300170765700200230ustar00rootroot00000000000000/* * Copyright (C) 2011 VoIP Embedded, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2011-06-28 initial implementation (Ovidiu Sas) */ #ifndef PARSE_REPLACES_H #define PARSE_REPLACES_H #include "msg_parser.h" /* rfc3891: The SIP "Replaces" Header */ struct replaces_body{ str to_tag; str from_tag; str early_only; str callid_val; str to_tag_val; str from_tag_val; str early_only_val; }; int parse_replaces_body(char* buf, int buf_len, struct replaces_body* replaces_b); ///* casting macro for accessing Replace body */ //#define get_replaces(p_msg) ((struct to_body*)(p_msg)->refer_to->parsed) // ///* Replace header field parser */ //int parse_replaces_header( struct sip_msg *msg); #endif /* PARSE_REPLACES_H */ opensips-2.2.2/parser/parse_rpid.c000066400000000000000000000036631300170765700171630ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "parse_from.h" #include "parse_to.h" #include #include #include "../dprint.h" #include "msg_parser.h" #include "../ut.h" #include "../errinfo.h" #include "../mem/mem.h" /* * This method is used to parse RPID header. * * params: msg : sip msg * returns 0 on success, * -1 on failure. */ int parse_rpid_header( struct sip_msg *msg ) { struct to_body* rpid_b; if ( !msg->rpid && (parse_headers(msg, HDR_RPID_F, 0)==-1 || !msg->rpid)) { goto error; } /* maybe the header is already parsed! */ if (msg->rpid->parsed) return 0; /* bad luck! :-( - we have to parse it */ /* first, get some memory */ rpid_b = pkg_malloc(sizeof(struct to_body)); if (rpid_b == 0) { LM_ERR("out of pkg_memory\n"); goto error; } /* now parse it!! */ parse_to(msg->rpid->body.s,msg->rpid->body.s+msg->rpid->body.len+1,rpid_b); if (rpid_b->error == PARSE_ERROR) { LM_ERR("bad rpid header\n"); pkg_free(rpid_b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing RPID header"); set_err_reply(400, "bad header"); goto error; } msg->rpid->parsed = rpid_b; return 0; error: return -1; } opensips-2.2.2/parser/parse_rpid.h000066400000000000000000000021241300170765700171570ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PARSE_RPID_H #define PARSE_RPID_H #include "msg_parser.h" /* casting macro for accessing RPID body */ #define get_rpid(p_msg) ((struct to_body*)(p_msg)->rpid->parsed) /* * RPID header field parser */ int parse_rpid_header( struct sip_msg *msg); #endif /* PARSE_RPID_H */ opensips-2.2.2/parser/parse_rr.c000066400000000000000000000226211300170765700166430ustar00rootroot00000000000000/* * Route & Record-Route header field parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * History: * -------- * 2003-10-07 parse_rr() split and added parse_rr_body() * 2003-10-21 duplicate_rr() duplicate the whole linked list of RR */ #include #include "parse_rr.h" #include "../mem/mem.h" #include "../mem/shm_mem.h" #include "../dprint.h" #include "../trim.h" #include "../ut.h" #include "../errinfo.h" /* * Parse Route or Record-Route body */ static inline int do_parse_rr_body(char *buf, int len, rr_t **head) { rr_t* r, *last; str s; param_hooks_t hooks; /* Make a temporary copy of the string pointer */ if(buf==0 || len<=0) { LM_DBG("no body for record-route\n"); r = NULL; *head = 0; goto parse_error; } s.s = buf; s.len = len; trim_leading(&s); last = 0; while(1) { /* Allocate and clear rr structure */ r = (rr_t*)pkg_malloc(sizeof(rr_t)); if (!r) { LM_ERR("no pkg memory left\n"); goto error; } memset(r, 0, sizeof(rr_t)); /* Parse name-addr part of the header */ if (parse_nameaddr(&s, &r->nameaddr) < 0) { LM_ERR("failed to parse name-addr\n"); goto parse_error; } r->len = r->nameaddr.len; /* Shift just behind the closing > */ s.s = r->nameaddr.name.s + r->nameaddr.len; /* Point just behind > */ s.len -= r->nameaddr.len; trim_leading(&s); /* Skip any white-chars */ if (s.len == 0) goto ok; /* Nothing left, finish */ if (s.s[0] == ';') { /* Route parameter found */ s.s++; s.len--; trim_leading(&s); if (s.len == 0) { LM_ERR("failed to parse params\n"); goto parse_error; } /* Parse all parameters */ if (parse_params(&s, CLASS_ANY, &hooks, &r->params) < 0) { LM_ERR("failed to parse params\n"); goto parse_error; } r->len = hooks.last_param->name.s + hooks.last_param->len - r->nameaddr.name.s; /* Copy hooks */ /*r->r2 = hooks.rr.r2; */ trim_leading(&s); if (s.len == 0) goto ok; } if (s.s[0] != ',') { LM_ERR("invalid character '%c', comma expected\n", s.s[0]); goto parse_error; } /* Next character is comma or end of header*/ s.s++; s.len--; trim_leading(&s); if (s.len == 0) { LM_ERR("text after comma missing\n"); goto parse_error; } /* Append the structure as last parameter of the linked list */ if (!*head) *head = r; if (last) last->next = r; last = r; } parse_error: LM_ERR("failed to parse RR headers\n"); error: if (r) pkg_free(r); free_rr(head); /* Free any contacts created so far */ return -1; ok: if (!*head) *head = r; if (last) last->next = r; return 0; } /* * Wrapper to do_parse_rr_body() for external calls */ int parse_rr_body(char *buf, int len, rr_t **head) { return do_parse_rr_body(buf, len, head); } /* * Parse Route and Record-Route header fields */ int parse_rr(struct hdr_field* _h) { rr_t* r = NULL; if (!_h) { LM_ERR("invalid parameter value\n"); return -1; } if (_h->parsed) { /* Already parsed, return */ return 0; } if(do_parse_rr_body(_h->body.s, _h->body.len, &r) < 0) { set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing RR headers"); set_err_reply(400, "bad headers"); return -1; } _h->parsed = (void*)r; return 0; } /* * Free list of rrs * _r is head of the list */ static inline void do_free_rr(rr_t** _r, int _shm) { rr_t* ptr; while(*_r) { ptr = *_r; *_r = (*_r)->next; if (ptr->params) { if (_shm) shm_free_params(ptr->params); else free_params(ptr->params); } if (_shm) shm_free(ptr); else pkg_free(ptr); } } /* * Free list of rrs * _r is head of the list */ void free_rr(rr_t** _r) { do_free_rr(_r, 0); } /* * Free list of rrs * _r is head of the list */ void shm_free_rr(rr_t** _r) { do_free_rr(_r, 1); } /* * Print list of RRs, just for debugging */ void print_rr(FILE* _o, rr_t* _r) { rr_t* ptr; ptr = _r; while(ptr) { fprintf(_o, "---RR---\n"); print_nameaddr(_o, &ptr->nameaddr); fprintf(_o, "r2 : %p\n", ptr->r2); if (ptr->params) { print_params(_o, ptr->params); } fprintf(_o, "len: %d\n", ptr->len); fprintf(_o, "---/RR---\n"); ptr = ptr->next; } } /* * Translate all pointers in the structure and also * in all parameters in the list */ static inline void xlate_pointers(rr_t* _orig, rr_t* _r) { param_t* ptr; _r->nameaddr.uri.s = translate_pointer(_r->nameaddr.name.s, _orig->nameaddr.name.s, _r->nameaddr.uri.s); ptr = _r->params; while(ptr) { /* if (ptr->type == P_R2) _r->r2 = ptr; */ ptr->name.s = translate_pointer(_r->nameaddr.name.s, _orig->nameaddr.name.s, ptr->name.s); ptr->body.s = translate_pointer(_r->nameaddr.name.s, _orig->nameaddr.name.s, ptr->body.s); ptr = ptr->next; } } /* * Duplicate a single rr_t structure using pkg_malloc or shm_malloc */ static inline int do_duplicate_rr(rr_t** _new, rr_t* _r, int _shm, int _first) { int len, ret; rr_t* res, *prev, *it; if (!_new || !_r) { LM_ERR("invalid parameter value\n"); return -1; } prev = NULL; *_new = NULL; it = _r; while(it) { if (it->params) { len = it->params->name.s + it->params->len - it->nameaddr.name.s; } else { len = it->nameaddr.len; } if (_shm) res = shm_malloc(sizeof(rr_t) + len); else res = pkg_malloc(sizeof(rr_t) + len); if (!res) { LM_ERR("no shm memory left\n"); goto error; } memcpy(res, it, sizeof(rr_t)); res->nameaddr.name.s = (char*)res + sizeof(rr_t); memcpy(res->nameaddr.name.s, it->nameaddr.name.s, len); if (_shm) { ret = shm_duplicate_params(&res->params, it->params); } else { ret = duplicate_params(&res->params, it->params); } if (ret < 0) { LM_ERR("failed to duplicate parameters\n"); if (_shm) shm_free(res); else pkg_free(res); goto error; } xlate_pointers(it, res); res->next=NULL; if(*_new==NULL) *_new = res; if (_first) return 0; if(prev) prev->next = res; prev = res; it = it->next; } return 0; error: if (_shm) shm_free_rr(_new); else free_rr(_new); *_new = NULL; return -1; } /* * Duplicate a single rr_t structure or the whole list (based on * "first" param) using pkg_malloc */ int duplicate_rr(rr_t** _new, rr_t* _r, int first) { return do_duplicate_rr(_new, _r, 0, first); } /* * Duplicate a single rr_t structure or the whole list (based on * "first" param) using shm_malloc */ int shm_duplicate_rr(rr_t** _new, rr_t* _r, int first) { return do_duplicate_rr(_new, _r, 1, first); } /** * get first RR header and print comma separated bodies in oroute * - order = 0 normal; order = 1 reverse * - nb_recs - input=skip number of rr; output=number of printed rrs */ int print_rr_body(struct hdr_field *iroute, str *oroute, int order, unsigned int * nb_recs) { rr_t *p; int n = 0, nr=0; int i = 0; int route_len; #define MAX_RR_HDRS 64 static str route[MAX_RR_HDRS]; char *cp, *start; if(iroute==NULL) return 0; route_len= 0; memset(route, 0, MAX_RR_HDRS*sizeof(str)); while (iroute!=NULL) { if (parse_rr(iroute) < 0) { LM_ERR("failed to parse RR\n"); goto error; } p =(rr_t*)iroute->parsed; while (p) { route[n].s = p->nameaddr.name.s; route[n].len = p->len; LM_DBG("current rr is %.*s\n", route[n].len, route[n].s); n++; if(n==MAX_RR_HDRS) { LM_ERR("too many RR\n"); goto error; } p = p->next; } iroute = iroute->sibling; } for(i=0;i=*nb_recs)) || (order && (i<=(n-*nb_recs)) )) ) ) { route_len+= route[i].len; nr++; } } if(nb_recs) LM_DBG("skipping %i route records\n", *nb_recs); route_len += --nr; /* for commas */ oroute->s=(char*)pkg_malloc(route_len); if(oroute->s==0) { LM_ERR("no more pkg mem\n"); goto error; } cp = start = oroute->s; if(order==0) { i= (nb_recs == NULL) ? 0:*nb_recs; while (i=0) { memcpy(cp, route[i].s, route[i].len); cp += route[i].len; if (i-->0) *(cp++) = ','; } } oroute->len=cp - start; LM_DBG("out rr [%.*s]\n", oroute->len, oroute->s); LM_DBG("we have %i records\n", n); if(nb_recs != NULL) *nb_recs = (unsigned int)n; return 0; error: return -1; } /* * Path must be available. Function returns the first uri * from Path without any duplication. */ int get_path_dst_uri(str *_p, str *_dst) { rr_t *route = 0; LM_DBG("path for branch: '%.*s'\n", _p->len, _p->s); if(parse_rr_body(_p->s, _p->len, &route) < 0) { LM_ERR("failed to parse Path body\n"); return -1; } if(!route) { LM_ERR("failed to parse Path body no head found\n"); return -1; } *_dst = route->nameaddr.uri; free_rr(&route); return 0; } opensips-2.2.2/parser/parse_rr.h000066400000000000000000000046441300170765700166550ustar00rootroot00000000000000/* * Route & Record-Route Parser * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PARSE_RR_H #define PARSE_RR_H #include #include "msg_parser.h" #include "parse_nameaddr.h" #include "parse_param.h" #include "hf.h" /* * Structure representing a Route & Record-Route HF body */ typedef struct rr { name_addr_t nameaddr; /* Name-addr part */ param_t* r2; /* Hook to r2 parameter */ param_t* params; /* Linked list of other parameters */ int len; /* Length of the whole route field */ int deleted; /* is this header to be removed ? */ struct rr* next; /* Next RR in the list */ } rr_t; /* * Parse Route & Record-Route header fields */ int parse_rr(struct hdr_field* _r); /* * Parse the body of Route & Record-Route headers */ int parse_rr_body(char *buf, int len, rr_t **head); /* * Free list of rr * _c is head of the list */ void free_rr(rr_t** _r); /* * Free list of rr * _c is head of the list */ void shm_free_rr(rr_t** _r); /* * Print list of rrs, just for debugging */ void print_rr(FILE* _o, rr_t* _r); /* * Duplicate a single (first) or the whole list of rr_t structure * using pkg_malloc */ int duplicate_rr(rr_t** _new, rr_t* _r, int _first); /* * Duplicate a single (first) or the whole list of rr_t structure * using shm_malloc */ int shm_duplicate_rr(rr_t** _new, rr_t* _r, int _first); /* * print body for all RR headers in comma separated string */ int print_rr_body(struct hdr_field *iroute, str *oroute, int order, unsigned int * nb_recs); /* * Function returns the first uri * from Path without any duplication. */ int get_path_dst_uri(str *_p, str *_dst); #endif /* PARSE_RR_H */ opensips-2.2.2/parser/parse_sipifmatch.c000066400000000000000000000046011300170765700203450ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "parse_sipifmatch.h" #include "../dprint.h" #include "parse_def.h" #include "../mem/mem.h" #include "../trim.h" #include "../errinfo.h" static inline char* skip_token(char* _b, int _l) { int i = 0; for(i = 0; i < _l; i++) { switch(_b[i]) { case ' ': case '\r': case '\n': case '\t': case ';': return _b + i; } } return _b + _l; } int etag_parser(char *_s, int _l, str *_e) { char* end; _e->s = _s; _e->len = _l; trim_leading(_e); if (_e->len == 0) { LM_ERR("empty body\n"); return -1; } end = skip_token(_e->s, _e->len); _e->len = end - _e->s; return 0; } int parse_sipifmatch(struct hdr_field* _h) { str *e; LM_DBG("called\n"); if (_h->parsed != 0) { return 0; } e = (str*)pkg_malloc(sizeof(str)); if (e == 0) { LM_ERR("no pkg memory left\n"); return -1; } memset(e, 0, sizeof(str)); if (etag_parser(_h->body.s, _h->body.len, e) < 0) { LM_ERR("error in etag_parser\n"); pkg_free(e); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing etag headers"); set_err_reply(400, "bad headers"); return -2; } _h->parsed = (void*)e; return 0; } void free_sipifmatch(str** _e) { if (*_e) pkg_free(*_e); *_e = 0; } opensips-2.2.2/parser/parse_sipifmatch.h000066400000000000000000000022141300170765700203500ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG FOKUS * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PARSE_SIPIFMATCH_H #define PARSE_SIPIFMATCH_H #include "../str.h" #include "hf.h" typedef struct etag { str text; /* Original string representation */ } etag_t; /* * Parse Sipifmatch HF body */ int parse_sipifmatch(struct hdr_field* _h); /* * Release memory */ void free_sipifmatch(str** _e); #endif /* PARSE_SIPIFMATCH_H */ opensips-2.2.2/parser/parse_sst.c000066400000000000000000000155061300170765700170350ustar00rootroot00000000000000/* * Copyright (c) 2006 SOMA Networks, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-02-17 Initial revision (dhsueh@somanetworks.com) */ #include "parse_sst.h" #include "../error.h" #include "../dprint.h" #include "../errinfo.h" #include "../mem/mem.h" static inline int/*bool*/ is_space( char c ) { return (c == ' ' || c == '\t'); } static inline int/*bool*/ is_num( char c ) { return (c >= '0' && c <= '9'); } static inline unsigned lower_byte( char b ) { return b | 0x20; } static inline unsigned lower_4bytes( unsigned d ) { return d | 0x20202020; } static inline unsigned lower_3bytes( unsigned d ) { return d | 0x202020; } static inline unsigned read_4bytes( char *val ) { return (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)); } static inline unsigned read_3bytes( char *val ) { return (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16)); } /* compile-time constants if called with constants */ #define MAKE_4BYTES( a, b, c, d ) \ ( ((a)&0xFF) | (((b)&0xFF)<<8) | (((c)&0xFF)<<16) | (((d)&0xFF)<<24) ) #define MAKE_3BYTES( a, b, c ) \ ( ((a)&0xFF) | (((b)&0xFF)<<8) | (((c)&0xFF)<<16) ) struct session_expires * malloc_session_expires( void ) { struct session_expires *se = (struct session_expires *) pkg_malloc( sizeof(struct session_expires) ); if ( se ) memset( se, 0, sizeof(struct session_expires) ); return se; } void free_session_expires( struct session_expires *se ) { if ( se ) pkg_free( se ); } enum parse_sst_result parse_session_expires_body( struct hdr_field *hf ) { register char *p = hf->body.s; int pos = 0; int len = hf->body.len; char *q; struct session_expires se = { 0, sst_refresher_unspecified }; unsigned tok; if ( !p || len <= 0 ) { LM_ERR(" no body for header field\n" ); return parse_sst_header_not_found; } /* skip whitespace */ for ( ; pos < len && is_space(*p); ++pos, ++p ) /*nothing*/; /* collect a number */ for ( q = p; pos < len && is_num(*q); ++pos, ++q ) se.interval = se.interval*10/*radix*/ + (*q - '0'); if ( q == p ) /*nothing parsed */ { LM_ERR(" no expiry interval\n" ); return parse_sst_no_value; } p = q; /* continue on with params */ while ( pos < len ) { if ( *p == ';' ) { ++p; ++pos; if ( pos + 4 < len ) { switch ( lower_4bytes(read_4bytes(p)) ) { case /*refr*/MAKE_4BYTES('r','e','f','r'): if ( pos + 9 <= len && lower_4bytes(read_4bytes(p+4)) == /*eshe*/MAKE_4BYTES('e','s','h','e') && lower_byte(*(p+8)) == 'r' && *(p+9) == '=' ) { tok = lower_3bytes( read_3bytes(p+10) ); if ( tok == MAKE_3BYTES('u','a','c') ) { se.refresher = sst_refresher_uac; p += 13; pos += 13; } else if ( tok == MAKE_3BYTES('u','a','s') ) { se.refresher = sst_refresher_uas; p += 13; pos += 13; } else /* unrecognized refresher-param */ { LM_ERR(" unrecognized refresher\n" ); return parse_sst_parse_error; } } else /* not "esher=" */ { /* there are no other se-params that start with "refr" */ for ( ; pos < len && *p != ';'; ++pos, ++p ) /*skip to ';'*/; } break; default: /* unrecognized se-param */ for ( ; pos < len && *p != ';'; ++pos, ++p ) /*skip to ';'*/; break; } /*switch*/ } /* exist 4 bytes to check */ else /* less than 4 bytes left */ { /* not enough text left for any of the recognized se-params */ /* no other recognized se-param */ for ( ; pos < len && *p != ';'; ++pos, ++p ) /*skip to ';'*/; } } else /* not ';' */ { LM_ERR("no semicolon separating se-params\n"); return parse_sst_parse_error; } /* if ';' */ } /* while */ hf->parsed = malloc_session_expires(); if ( !hf->parsed ) { LM_ERR(" out of pkg memory\n" ); return parse_sst_out_of_mem; } *((struct session_expires *)hf->parsed) = se; return parse_sst_success; } enum parse_sst_result parse_session_expires( struct sip_msg *msg, struct session_expires *se ) { enum parse_sst_result result; if (msg->session_expires==NULL && parse_headers(msg,HDR_SESSION_EXPIRES_F,0)!=0 ) { LM_ERR("failed to parse message when looking for Session-Expires \n"); return parse_sst_header_not_found; } if ( msg->session_expires ) { if ( msg->session_expires->parsed == 0 && (result = parse_session_expires_body(msg->session_expires)) != parse_sst_success ) { if (result!=parse_sst_out_of_mem) { set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing SE header"); set_err_reply(400, "bad headers"); } return result; } if ( se ) { *se = *((struct session_expires *)msg->session_expires->parsed); } return parse_sst_success; } else { return parse_sst_header_not_found; } } enum parse_sst_result parse_min_se_body( struct hdr_field *hf ) { int len = hf->body.len; char *p = hf->body.s; int pos = 0; unsigned int interval = 0; /* skip whitespace */ for ( ; pos < len && is_space(*p); ++pos, ++p ) /*nothing*/; if ( pos == len ) return parse_sst_no_value; /* collect a number */ for ( ; pos < len && is_num(*p); ++pos, ++p ) interval = interval*10/*radix*/ + (*p - '0'); /* skip whitespace */ for ( ; pos < len && is_space(*p); ++pos, ++p ) /*nothing*/; if ( pos != len ) /* shouldn't be any more junk */ return parse_sst_parse_error; hf->parsed=(void*)(long)interval; return parse_sst_success; } enum parse_sst_result parse_min_se( struct sip_msg *msg, unsigned int *min_se ) { enum parse_sst_result result; if (msg->min_se==NULL && parse_headers(msg,HDR_MIN_SE_F,0)!=0 ) { LM_ERR("failed to parse message when looking for MIN-SE \n"); return parse_sst_header_not_found; } if ( msg->min_se ) { if ( msg->min_se->parsed == 0 && (result = parse_min_se_body(msg->min_se)) != parse_sst_success ) { set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing MINSE header"); set_err_reply(400, "bad headers"); return result; } if ( min_se ) { *min_se = (unsigned int)(long)msg->min_se->parsed; } return parse_sst_success; } else { return parse_sst_header_not_found; } } opensips-2.2.2/parser/parse_sst.h000066400000000000000000000077201300170765700170410ustar00rootroot00000000000000/* * Copyright (c) 2006 SOMA Networks, Inc. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2006-02-17 Initial revision (dhsueh@somanetworks.com) */ #ifndef PARSE_SST_H #define PARSE_SST_H 1 #include "msg_parser.h" #include "hf.h" /** * Indicate the "refresher=" value of the Session-Expires header. */ enum sst_refresher { sst_refresher_unspecified, sst_refresher_uac, sst_refresher_uas, }; /** * We will treat the 'void* parsed' field of struct hdr_field as * a pointer to a struct session_expires. */ struct session_expires { unsigned interval; /* in seconds */ enum sst_refresher refresher; }; enum parse_sst_result { parse_sst_success, parse_sst_header_not_found, /* no header */ parse_sst_no_value, /* no interval specified */ #if NOT_IMPLEMENTED_YET parse_sst_duplicate, /* multiple s-e / x / min-se headers found */ #endif parse_sst_out_of_mem, parse_sst_parse_error, /* something puked */ }; /** * Allocate a zeroed-out struct session_expires. */ struct session_expires * malloc_session_expires( void ); /** * Deallocates memory previously allocated via malloc_session_expires(). */ void free_session_expires( struct session_expires * ); /** * Parses the (should be only one instance) single instance of the * "Session-Expires" or "x" header in the msg. Note that the header * is not automatically parsed in parse_headers()[1] so you'll have to * call this function to get the information. * * Because of time constraints, this function is coded assuming there is * NO WHITESPACE in any of the body -- note that the augBNF for the * Session-Expires body allows sep whitespace between tokens: * delta-seconds SWS ";" SWS "refresher" SWS "=" SWS ( "uac" / "uas" ) * * Note[1]: it looks like only the frequently-used headers are * automatically parsed in parse_headers() (indicated by a parse_ * function of the form "char* parse_(char *buf, char *end, foo*) * and a struct foo with a member called "error"). * * @param msg the sip message to examine * @param se the place to store session-expires information into, if * provided; note that result is also available in * *((struct session_expires *)msg->session_expires->parsed) * @return parse_sst_result */ enum parse_sst_result parse_session_expires( struct sip_msg *msg, struct session_expires *se ); /** * helper function for parse_session_expires */ enum parse_sst_result parse_session_expires_body( struct hdr_field *hf ); /** * Parses the (should be only one instance) single instance of the * "Min-SE" header in the msg. Note that this header is not automatically * parsed in parse_headers() so you'll have to call this function to get * the information. * * @param msg the sip message to examine * @param min_se the place to store the Min-SE value, if provided; note that * result is also available in (unsigned)msg->min_se->parsed * @return parse_sst_result */ enum parse_sst_result parse_min_se( struct sip_msg *msg, unsigned *min_se ); /** * Parses the given instance of "Min-SE" header. * * @param hf the header field structure to be parsed * @return parse_sst_result */ enum parse_sst_result parse_min_se_body( struct hdr_field *hf ); #endif /* ! PARSE_SST_H */ opensips-2.2.2/parser/parse_supported.c000066400000000000000000000077111300170765700202500ustar00rootroot00000000000000/* * Supported parser. * * Copyright (C) 2006 Andreas Granig * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "../mem/mem.h" #include "keys.h" #include "parse_supported.h" #define IS_DELIM(c) (*(c) == ' ' || *(c) == '\t' || *(c) == '\r' || *(c) == '\n' || *(c) == ',') /* from parser/parse_hname2.c: */ #define LOWER_BYTE(b) ((b) | 0x20) #define LOWER_DWORD(d) ((d) | 0x20202020) #define READ(val) \ (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)) /* * Parse Supported HF body. */ static inline int parse_supported_body(str *body, unsigned int *sup) { register char* p; register unsigned int val; int len, pos = 0; *sup = 0; p = body->s; len = body->len; while (pos < len) { /* skip spaces and commas */ for (; pos < len && IS_DELIM(p); ++pos, ++p); val = LOWER_DWORD(READ(p)); switch (val) { /* "path" */ case _path_: if(pos + 4 <= len && IS_DELIM(p+4)) { *sup |= F_SUPPORTED_PATH; pos += 5; p += 5; } else goto default_label; break; /* "gruu" */ case _gruu_: if(pos + 4 <= len && IS_DELIM(p+4)) { *sup |= F_SUPPORTED_GRUU; pos += 5; p += 5; } else goto default_label; break; /* "100rel" */ case _100r_: if ( pos+6 <= len && LOWER_BYTE(*(p+4))=='e' && LOWER_BYTE(*(p+5))=='l' && IS_DELIM(p+6)) { *sup |= F_SUPPORTED_100REL; pos += SUPPORTED_100REL_LEN + 1; p += SUPPORTED_100REL_LEN + 1; } else goto default_label; break; /* "timer" */ case _time_: if ( pos+5 <= len && LOWER_BYTE(*(p+4))=='r' && IS_DELIM(p+5) ) { *sup |= F_SUPPORTED_TIMER; pos += SUPPORTED_TIMER_LEN + 1; p += SUPPORTED_TIMER_LEN + 1; } else goto default_label; break; /* "eventlist" */ case _even_: if ( pos+9 <= len && LOWER_DWORD(READ(p+4))==_tlis_ && LOWER_BYTE(*(p+8))=='t' && IS_DELIM(p+9) ) { *sup |= F_SUPPORTED_EVENTLIST; pos += SUPPORTED_EVENTLIST_LEN + 1; p += SUPPORTED_EVENTLIST_LEN + 1; } else goto default_label; break; /* unknown */ default: default_label: /* skip element */ for (; pos < len && !IS_DELIM(p); ++pos, ++p); break; } } return 0; } /* * Parse all Supported headers */ int parse_supported( struct sip_msg *msg) { unsigned int supported; struct hdr_field *hdr; struct supported_body *sb; /* maybe the header is already parsed! */ if (msg->supported && msg->supported->parsed) return 0; /* parse to the end in order to get all SUPPORTED headers */ if (parse_headers(msg,HDR_EOH_F,0)==-1 || !msg->supported) return -1; /* bad luck! :-( - we have to parse them */ supported = 0; for( hdr=msg->supported ; hdr ; hdr=hdr->sibling) { if (hdr->parsed) { supported |= ((struct supported_body*)hdr->parsed)->supported; continue; } sb = (struct supported_body*)pkg_malloc(sizeof(struct supported_body)); if (sb == 0) { LM_ERR("out of pkg_memory\n"); return -1; } LM_DBG("parsing [%.*s] %p\n",hdr->len,hdr->name.s,hdr); parse_supported_body(&(hdr->body), &(sb->supported)); sb->supported_all = 0; hdr->parsed = (void*)sb; supported |= sb->supported; } ((struct supported_body*)msg->supported->parsed)->supported_all = supported; return 0; } opensips-2.2.2/parser/parse_supported.h000066400000000000000000000045371300170765700202600ustar00rootroot00000000000000/* * Supported parser. * * Copyright (C) 2006 Andreas Granig * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2006-03-02 parse_supported() parses and cumulates all SUPPORTED * headers (bogdan) */ #ifndef PARSE_SUPPORTED_H #define PARSE_SUPPORTED_H #include "msg_parser.h" #include "../mem/mem.h" #define F_SUPPORTED_PATH (1 << 0) #define F_SUPPORTED_100REL (1 << 1) #define F_SUPPORTED_TIMER (1 << 2) #define F_SUPPORTED_EVENTLIST (1 << 3) #define F_SUPPORTED_GRUU (1 << 4) #define SUPPORTED_PATH_STR "path" #define SUPPORTED_PATH_LEN (sizeof(SUPPORTED_PATH_STR)-1) /* RFC 3262 */ #define SUPPORTED_100REL_STR "100rel" #define SUPPORTED_100REL_LEN (sizeof(SUPPORTED_100REL_STR)-1) /* RFC 4028 */ #define SUPPORTED_TIMER_STR "timer" #define SUPPORTED_TIMER_LEN (sizeof(SUPPORTED_TIMER_STR)-1) /* RFC 4662 - RLS */ #define SUPPORTED_EVENTLIST_STR "eventlist" #define SUPPORTED_EVENTLIST_LEN (sizeof(SUPPORTED_EVENTLIST_STR)-1) #define get_supported(p_msg) \ ((p_msg)->supported ? ((struct supported_body*)(p_msg)->supported->parsed)->supported_all : 0) struct supported_body { unsigned int supported; /* supported mask for the current hdr */ unsigned int supported_all; /* supported mask for the all "supported" hdr * - it's set only for the first hdr in * sibling list*/ }; /* * Parse all Supported headers. */ int parse_supported( struct sip_msg *msg); static inline void free_supported(struct supported_body **sb) { if (sb && *sb) { pkg_free(*sb); *sb = 0; } } #endif /* PARSE_SUPPORTED_H */ opensips-2.2.2/parser/parse_to.c000066400000000000000000000415231300170765700166440ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-04-26 ZSW (jiri) * 2006-05-29 removed the NO_PINGTEL_TAG_HACK - it's conflicting the RFC 3261; * TAG parameter must have value; other parameters are accepted * without value (bogdan) */ #include "parse_to.h" #include #include #include "../dprint.h" #include "msg_parser.h" #include "parse_uri.h" #include "../ut.h" #include "../mem/mem.h" #include "../errinfo.h" enum { START_TO, DISPLAY_QUOTED, E_DISPLAY_QUOTED, DISPLAY_TOKEN, DISPLAY_TOKEN2, S_URI_ENCLOSED, URI_ENCLOSED, E_URI_ENCLOSED, URI_OR_TOKEN, MAYBE_URI_END, END, F_CR, F_LF, F_CRLF }; enum { S_PARA_NAME=20, PARA_NAME, S_EQUAL, S_PARA_VALUE, TAG1, TAG2, TAG3, PARA_VALUE_TOKEN , PARA_VALUE_QUOTED, E_PARA_VALUE }; #define add_param( _param , _body ) \ do{\ LM_DBG("%.*s=%.*s\n",param->name.len,ZSW(param->name.s),\ param->value.len,ZSW(param->value.s));\ if (!(_body)->param_lst) (_body)->param_lst=(_param);\ else (_body)->last_param->next=(_param);\ (_body)->last_param =(_param);\ if ((_param)->type==TAG_PARAM)\ memcpy(&((_body)->tag_value),&((_param)->value),sizeof(str));\ (_param) = 0;\ }while(0); void free_to_params(struct to_body* tb) { struct to_param *tp=tb->param_lst; struct to_param *foo; while (tp){ foo = tp->next; pkg_free(tp); tp=foo; } tb->param_lst = tb->last_param = NULL; } void free_to(struct to_body* tb) { free_to_params(tb); pkg_free(tb); } static inline char* parse_to_param(char *buffer, char *end, struct to_body *to_b, int *returned_status) { struct to_param *param; int status; int saved_status; char *tmp; param=0; status=E_PARA_VALUE; saved_status=E_PARA_VALUE; for( tmp=buffer; tmptype=TAG_PARAM; case PARA_NAME: case TAG1: case TAG2: param->name.len = tmp-param->name.s; status = S_EQUAL; break; case PARA_VALUE_TOKEN: param->value.len = tmp-param->value.s; status = E_PARA_VALUE; add_param( param , to_b ); break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now =' '*/ status=saved_status; break; } break; case '\n': switch (status) { case S_PARA_NAME: case S_EQUAL: case S_PARA_VALUE: case E_PARA_VALUE: saved_status=status; status=F_LF; break; case TAG3: param->type=TAG_PARAM; case PARA_NAME: case TAG1: case TAG2: param->name.len = tmp-param->name.s; saved_status = S_EQUAL; status = F_LF; break; case PARA_VALUE_TOKEN: param->value.len = tmp-param->value.s; saved_status = E_PARA_VALUE; status = F_LF; add_param( param , to_b ); break; case F_CR: status=F_CRLF; break; case F_CRLF: case F_LF: status=saved_status; goto endofheader; default: goto parse_error; } break; case '\r': switch (status) { case S_PARA_NAME: case S_EQUAL: case S_PARA_VALUE: case E_PARA_VALUE: saved_status=status; status=F_CR; break; case TAG3: param->type=TAG_PARAM; case PARA_NAME: case TAG1: case TAG2: param->name.len = tmp-param->name.s; saved_status = S_EQUAL; status = F_CR; break; case PARA_VALUE_TOKEN: param->value.len = tmp-param->value.s; saved_status = E_PARA_VALUE; status = F_CR; add_param( param , to_b ); break; case F_CRLF: case F_CR: case F_LF: status=saved_status; goto endofheader; default: goto parse_error; } break; case 0: switch (status) { case PARA_NAME: param->name.len = tmp-param->name.s; case S_EQUAL: case S_PARA_VALUE: if (param->type==TAG_PARAM) goto parse_error; param->value.s = tmp; case PARA_VALUE_TOKEN: status = E_PARA_VALUE; param->value.len = tmp-param->value.s; add_param( param , to_b ); case E_PARA_VALUE: saved_status = status; goto endofheader; break; default: goto parse_error; } break; case '\\': switch (status) { case PARA_VALUE_QUOTED: switch (*(tmp+1)) { case '\r': case '\n': break; default: tmp++; break; } break; default: goto parse_error; } break; case '"': switch (status) { case S_PARA_VALUE: param->value.s = tmp+1; status = PARA_VALUE_QUOTED; break; case PARA_VALUE_QUOTED: param->value.len=tmp-param->value.s ; add_param( param , to_b ); status = E_PARA_VALUE; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; case ';' : switch (status) { case PARA_VALUE_QUOTED: break; case PARA_NAME: param->name.len = tmp-param->name.s; case S_EQUAL: case S_PARA_VALUE: if (param->type==TAG_PARAM) goto parse_error; param->value.s = tmp; case PARA_VALUE_TOKEN: param->value.len=tmp-param->value.s; add_param(param,to_b); case E_PARA_VALUE: param = (struct to_param*) pkg_malloc(sizeof(struct to_param)); if (!param){ LM_ERR("out of pkg memory\n" ); goto error; } memset(param,0,sizeof(struct to_param)); param->type=GENERAL_PARAM; status = S_PARA_NAME; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; case 'T': case 't' : switch (status) { case PARA_VALUE_QUOTED: case PARA_VALUE_TOKEN: case PARA_NAME: break; case S_PARA_NAME: param->name.s = tmp; status = TAG1; break; case S_PARA_VALUE: param->value.s = tmp; status = PARA_VALUE_TOKEN; break; case TAG1: case TAG2: case TAG3: status = PARA_NAME; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; case 'A': case 'a' : switch (status) { case PARA_VALUE_QUOTED: case PARA_VALUE_TOKEN: case PARA_NAME: break; case S_PARA_NAME: param->name.s = tmp; status = PARA_NAME; break; case S_PARA_VALUE: param->value.s = tmp; status = PARA_VALUE_TOKEN; break; case TAG1: status = TAG2; break; case TAG2: case TAG3: status = PARA_NAME; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; case 'G': case 'g' : switch (status) { case PARA_VALUE_QUOTED: case PARA_VALUE_TOKEN: case PARA_NAME: break; case S_PARA_NAME: param->name.s = tmp; status = PARA_NAME; break; case S_PARA_VALUE: param->value.s = tmp; status = PARA_VALUE_TOKEN; break; case TAG1: case TAG3: status = PARA_NAME; break; case TAG2: status = TAG3; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; case '=': switch (status) { case PARA_VALUE_QUOTED: break; case TAG3: param->type=TAG_PARAM; case PARA_NAME: case TAG1: case TAG2: param->name.len = tmp-param->name.s; status = S_PARA_VALUE; break; case S_EQUAL: status = S_PARA_VALUE; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; default: switch (status) { case TAG1: case TAG2: case TAG3: status = PARA_NAME; break; case PARA_VALUE_TOKEN: case PARA_NAME: case PARA_VALUE_QUOTED: break; case S_PARA_NAME: param->name.s = tmp; status = PARA_NAME; break; case S_PARA_VALUE: param->value.s = tmp; status = PARA_VALUE_TOKEN; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: LM_ERR("spitting out [%c] in status %d\n",*tmp,status ); goto error; } }/*switch*/ }/*for*/ endofheader: if (param) { if (saved_status==S_EQUAL||saved_status==S_PARA_VALUE) { saved_status = E_PARA_VALUE; param->value.s= 0; param->value.len=0; if (param->type==TAG_PARAM) goto parse_error; add_param(param, to_b); } else { pkg_free(param); } } *returned_status=saved_status; return tmp; parse_error: LM_ERR("unexpected char [%c] in status %d: <<%.*s>> .\n", *tmp,status, (int)(tmp-buffer), ZSW(buffer)); error: if (param) pkg_free(param); free_to_params(to_b); to_b->error=PARSE_ERROR; *returned_status = status; return tmp; } char* parse_to(char* buffer, char *end, struct to_body *to_b) { int status; int saved_status; char *tmp; char *end_mark; status=START_TO; saved_status=START_TO; memset(to_b, 0, sizeof(struct to_body)); to_b->error=PARSE_OK; end_mark=0; for( tmp=buffer; tmpuri.len = tmp - to_b->uri.s; status = E_URI_ENCLOSED; break; case URI_OR_TOKEN: status = MAYBE_URI_END; end_mark = tmp; break; case DISPLAY_TOKEN: end_mark = tmp; status = DISPLAY_TOKEN2; break; } break; case '\n': switch (status) { case URI_OR_TOKEN: end_mark = tmp; status = MAYBE_URI_END; case MAYBE_URI_END: case DISPLAY_TOKEN: case DISPLAY_TOKEN2: case E_DISPLAY_QUOTED: case END: saved_status=status; status=F_LF; break; case F_CR: status=F_CRLF; break; case F_CRLF: case F_LF: status=saved_status; goto endofheader; default: goto parse_error; } break; case '\r': switch (status) { case URI_OR_TOKEN: end_mark = tmp; status = MAYBE_URI_END; case MAYBE_URI_END: case DISPLAY_TOKEN: case DISPLAY_TOKEN2: case E_DISPLAY_QUOTED: case END: saved_status=status; status=F_CR; break; case F_CRLF: case F_CR: case F_LF: status=saved_status; goto endofheader; default: goto parse_error; } break; case 0: switch (status) { case URI_OR_TOKEN: case MAYBE_URI_END: to_b->uri.len = tmp - to_b->uri.s; case END: saved_status = status = END; goto endofheader; default: goto parse_error; } break; case '\\': switch (status) { case DISPLAY_QUOTED: tmp++; /* jump over next char */ break; default: goto parse_error; } break; case '<': switch (status) { case START_TO: to_b->body.s=tmp; status = S_URI_ENCLOSED; break; case DISPLAY_QUOTED: break; case E_DISPLAY_QUOTED: status = S_URI_ENCLOSED; break; case URI_OR_TOKEN: case DISPLAY_TOKEN: end_mark = tmp; case DISPLAY_TOKEN2: case MAYBE_URI_END: to_b->display.len=end_mark-to_b->display.s; status = S_URI_ENCLOSED; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; case '>': switch (status) { case DISPLAY_QUOTED: break; case URI_ENCLOSED: to_b->uri.len = tmp - to_b->uri.s; case E_URI_ENCLOSED: status = END; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; case '"': switch (status) { case START_TO: to_b->body.s = tmp; to_b->display.s = tmp; status = DISPLAY_QUOTED; break; case DISPLAY_QUOTED: status = E_DISPLAY_QUOTED; to_b->display.len = tmp-to_b->display.s+1; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; case ';' : switch (status) { case DISPLAY_QUOTED: case DISPLAY_TOKEN: case URI_ENCLOSED: break; case URI_OR_TOKEN: end_mark = tmp; case MAYBE_URI_END: to_b->uri.len = end_mark - to_b->uri.s; case END: to_b->body.len = tmp-to_b->body.s; tmp = parse_to_param(tmp,end,to_b,&saved_status); goto endofheader; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: goto parse_error; } break; default: switch (status) { case START_TO: to_b->uri.s = to_b->body.s = tmp; status = URI_OR_TOKEN; to_b->display.s=tmp; break; case S_URI_ENCLOSED: to_b->uri.s=tmp; status=URI_ENCLOSED; break; case MAYBE_URI_END: case DISPLAY_TOKEN2: status = DISPLAY_TOKEN; case DISPLAY_QUOTED: case DISPLAY_TOKEN: case URI_ENCLOSED: case URI_OR_TOKEN: break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: LM_DBG("spitting out [%c] in status %d\n", *tmp,status ); goto error; } }/*char switch*/ }/*for*/ endofheader: if (to_b->display.len==0) to_b->display.s=0; status=saved_status; LM_DBG("end of header reached, state=%d\n", status); /* check if error*/ switch(status){ case MAYBE_URI_END: to_b->uri.len = end_mark - to_b->uri.s; case END: to_b->body.len = tmp - to_b->body.s; case E_PARA_VALUE: break; default: LM_ERR("unexpected end of header in state %d\n", status); goto error; } LM_DBG("display={%.*s}, ruri={%.*s}\n", to_b->display.len, ZSW(to_b->display.s), to_b->uri.len, ZSW(to_b->uri.s)); return tmp; parse_error: LM_ERR("unexpected char [%c] in status %d: <<%.*s>> .\n", *tmp,status, (int)(tmp-buffer), buffer); error: to_b->error=PARSE_ERROR; free_to_params(to_b); return tmp; } /** * */ struct sip_uri *parse_to_uri(struct sip_msg *msg) { struct to_body *tb = NULL; if(msg==NULL || msg->to==NULL || msg->to->parsed==NULL) return NULL; tb = get_to(msg); if(tb->parsed_uri.user.s!=NULL || tb->parsed_uri.host.s!=NULL) return &tb->parsed_uri; if (parse_uri(tb->uri.s, tb->uri.len , &tb->parsed_uri)<0) { LM_ERR("failed to parse To uri\n"); memset(&tb->parsed_uri, 0, sizeof(struct sip_uri)); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing To uri"); set_err_reply(400, "bad To uri"); return NULL; } return &tb->parsed_uri; } int parse_to_header( struct sip_msg *msg) { struct to_body* to_b; if ( !msg->to && ( parse_headers(msg,HDR_TO_F,0)==-1 || !msg->to)) { LM_ERR("bad msg or missing To header\n"); goto error; } /* maybe the header is already parsed! */ if (msg->to->parsed) return 0; /* bad luck! :-( - we have to parse it */ /* first, get some memory */ to_b = pkg_malloc(sizeof(struct to_body)); if (to_b == 0) { LM_ERR("out of pkg_memory\n"); goto error; } /* now parse it!! */ memset(to_b, 0, sizeof(struct to_body)); parse_to(msg->to->body.s,msg->to->body.s+msg->to->body.len+1,to_b); if (to_b->error == PARSE_ERROR) { LM_ERR("bad to header\n"); pkg_free(to_b); set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing too header"); set_err_reply(400, "bad header"); goto error; } msg->to->parsed = to_b; return 0; error: return -1; } opensips-2.2.2/parser/parse_to.h000066400000000000000000000037131300170765700166500ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 Fhg Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PARSE_TO #define PARSE_TO #include "../str.h" #include "msg_parser.h" enum { TAG_PARAM = 400, GENERAL_PARAM }; struct to_param{ int type; /* Type of parameter */ str name; /* Name of parameter */ str value; /* Parameter value */ struct to_param* next; /* Next parameter in the list */ }; struct to_body{ int error; /* Error code */ str body; /* The whole header field body */ str uri; /* URI */ str display; /* Display Name */ str tag_value; /* Value of tag */ struct sip_uri parsed_uri; /* Parsed URI */ struct to_param *param_lst; /* Linked list of parameters */ struct to_param *last_param; /* Last parameter in the list */ }; /* casting macro for accessing To body */ #define get_to(p_msg) ((struct to_body*)(p_msg)->to->parsed) /* * To header field parser */ char* parse_to(char* buffer, char *end, struct to_body *to_b); int parse_to_header( struct sip_msg *msg); struct sip_uri *parse_to_uri(struct sip_msg *msg); void free_to(struct to_body* tb); void free_to_params(struct to_body *tb); #endif opensips-2.2.2/parser/parse_uri.c000066400000000000000000001107551300170765700170250ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-04 convenience inbound-uri parser parse_orig_ruri * introduced (jiri) * 2003-04-11 new parse_uri introduced (better, parses also some parameters, * works in one pass) (andrei) * 2003-04-11 ser_error is now set in parse_uri (andrei) * 2003-04-26 ZSW (jiri) * 2003-07-03 sips:, r2, lr=on support added (andrei) * 2005-02-25 preliminary tel uri support (andrei) * 2005-03-03 more tel uri fixes (andrei) * 2006-11-28 Added statistic support for the number of bad URI's * (Jeffrey Magder - SOMA Networks) * 2011-04-20 added support for URI unknown parameters (osas) */ #include "parse_uri.h" #include #include "../dprint.h" #include "../ut.h" /* q_memchr */ #include "../error.h" #include "../errinfo.h" #include "../core_stats.h" #include "../strcommon.h" int parse_uri_headers(str headers, str h_name[], str h_val[], int h_size) { enum states {URI_H_HEADER, URI_H_VALUE}; register enum states state; char* h; /* header start */ char* v; /* header value start */ str* header; /* current header */ str* header_val; /* current header val */ register char* p; char* end; unsigned int i = 0; /* init */ end = headers.s + headers.len; p = h = headers.s; v = NULL; header = &h_name[0]; header_val = &h_val[0]; state = URI_H_HEADER; memset(h_name, 0, h_size * sizeof(str)); memset(h_val, 0, h_size * sizeof(str)); for(;ps = h; header->len = p-h; header_val->s = NULL; header_val->len = 0; /* advance header and header_val */ i++; if(is = v; header_val->len = p-v; state = URI_H_HEADER; /* advance header and header_val */ i++; if(is = h; header->len = p-h; header_val->s = NULL; header_val->len = 0; break; case URI_H_VALUE: header_val->s = v; header_val->len = p-v; break; } #ifdef EXTRA_DEBUG for(i=0; i<%.*s> val=[%p]-><%.*s>\n", h_name[i].s, h_name[i].len, h_name[i].s, h_val[i].s, h_val[i].len, h_val[i].s); #endif return 0; } /* buf= pointer to beginning of uri (sip:x@foo.bar:5060;a=b?h=i) * len= len of uri * returns: fills uri & returns <0 on error or 0 if ok */ int parse_uri(char* buf, int len, struct sip_uri* uri) { enum states { URI_INIT, URI_USER, URI_PASSWORD, URI_PASSWORD_ALPHA, URI_HOST, URI_HOST_P, URI_HOST6_P, URI_HOST6_END, URI_PORT, URI_PARAM, URI_PARAM_P, URI_PARAM_VAL_P, URI_VAL_P, URI_HEADERS, /* param states */ /* transport */ PT_T, PT_R, PT_A, PT_N, PT_S, PT_P, PT_O, PT_R2, PT_T2, PT_eq, /* ttl */ PTTL_T2, PTTL_L, PTTL_eq, /* user */ PU_U, PU_S, PU_E, PU_R, PU_eq, /* method */ PM_M, PM_E, PM_T, PM_H, PM_O, PM_D, PM_eq, /* maddr */ PMA_A, PMA_D, PMA_D2, PMA_R, PMA_eq, /* lr */ PLR_L, PLR_R_FIN, PLR_eq, /* gr */ PG_G, PG_G_FIN, PG_eq, /* r2 */ PR2_R, PR2_2_FIN, PR2_eq, /* transport values */ /* udp */ VU_U, VU_D, VU_P_FIN, /* tcp */ VT_T, VT_C, VT_P_FIN, /* tls */ VTLS_L, VTLS_S_FIN, /* sctp */ VS_S, VS_C, VS_T, VS_P_FIN, /* ws */ VW_W, VW_S, VW_S_FIN, VWS_S_FIN }; register enum states state; char* s; char* b; /* param start */ char *v; /* value start */ str* param; /* current param */ str* param_val; /* current param val */ str user; str password; int port_no; register char* p; char* end; char* pass; int found_user; int error_headers; unsigned int scheme; uri_type backup; #ifdef EXTRA_DEBUG int i; #endif #define SIP_SCH 0x3a706973 #define SIPS_SCH 0x73706973 #define TEL_SCH 0x3a6c6574 #define URN_SERVICE_SCH 0x3a6e7275 #define URN_SERVICE_STR ":service:" #define URN_SERVICE_STR_LEN (sizeof(URN_SERVICE_STR) - 1) #define case_port( ch, var) \ case ch: \ (var)=(var)*10+ch-'0'; \ break #define still_at_user \ if (found_user==0){ \ user.s=uri->host.s; \ if (pass){\ user.len=pass-user.s; \ password.s=pass+1; \ password.len=p-password.s; \ }else{ \ user.len=p-user.s; \ }\ /* save the uri type/scheme */ \ backup=uri->type; \ /* everything else is 0 */ \ memset(uri, 0, sizeof(struct sip_uri)); \ /* restore the scheme, copy user & pass */ \ uri->type=backup; \ uri->user=user; \ if (pass) uri->passwd=password; \ s=p+1; \ found_user=1;\ error_headers=0; \ state=URI_HOST; \ }else goto error_bad_char #define check_host_end \ case ':': \ /* found the host */ \ uri->host.s=s; \ uri->host.len=p-s; \ state=URI_PORT; \ s=p+1; \ break; \ case ';': \ uri->host.s=s; \ uri->host.len=p-s; \ state=URI_PARAM; \ s=p+1; \ break; \ case '?': \ uri->host.s=s; \ uri->host.len=p-s; \ state=URI_HEADERS; \ s=p+1; \ break; \ case '&': \ case '@': \ goto error_bad_char #define param_set(t_start, v_start) \ param->s=(t_start);\ param->len=(p-(t_start));\ param_val->s=(v_start); \ param_val->len=(p-(v_start)) #define u_param_set(t_start, v_start) \ if (uri->u_params_no < URI_MAX_U_PARAMS){ \ if((v_start)>(t_start)){ \ uri->u_name[uri->u_params_no].s=(t_start); \ uri->u_name[uri->u_params_no].len=((v_start)-(t_start)-1); \ if(p>(v_start)) { \ uri->u_val[uri->u_params_no].s=(v_start); \ uri->u_val[uri->u_params_no].len=(p-(v_start)); \ } \ } else { \ uri->u_name[uri->u_params_no].s=(t_start); \ uri->u_name[uri->u_params_no].len=(p-(t_start)); \ } \ uri->u_params_no++; \ } else { \ LM_ERR("unknown URI param list excedeed\n"); \ } #define semicolon_case \ case';': \ if (pass){ \ found_user=1;/* no user, pass cannot contain ';'*/ \ pass=0; \ } \ state=URI_PARAM /* new param */ #define question_case \ case '?': \ uri->params.s=s; \ uri->params.len=p-s; \ state=URI_HEADERS; \ s=p+1; \ if (pass){ \ found_user=1;/* no user, pass cannot contain '?'*/ \ pass=0; \ } #define colon_case \ case ':': \ if (found_user==0){ \ /*might be pass but only if user not found yet*/ \ if (pass){ \ found_user=1; /* no user */ \ pass=0; \ }else{ \ pass=p; \ } \ } \ state=URI_PARAM_P /* generic param */ #define param_common_cases \ case '@': \ /* ughhh, this is still the user */ \ still_at_user; \ break; \ semicolon_case; \ break; \ question_case; \ break; \ colon_case; \ break #define u_param_common_cases \ case '@': \ /* ughhh, this is still the user */ \ still_at_user; \ break; \ semicolon_case; \ u_param_set(b, v); \ break; \ question_case; \ u_param_set(b, v); \ break; \ colon_case; \ break #define value_common_cases \ case '@': \ /* ughhh, this is still the user */ \ still_at_user; \ break; \ semicolon_case; \ param_set(b, v); \ break; \ question_case; \ param_set(b, v); \ break; \ colon_case; \ state=URI_VAL_P; \ break #define param_switch(old_state, c1, c2, new_state) \ case old_state: \ switch(*p){ \ case c1: \ case c2: \ state=(new_state); \ break; \ u_param_common_cases; \ default: \ state=URI_PARAM_P; \ } \ break #define param_switch1(old_state, c1, new_state) \ case old_state: \ switch(*p){ \ case c1: \ state=(new_state); \ break; \ param_common_cases; \ default: \ state=URI_PARAM_P; \ } \ break #define param_switch_big(old_state, c1, c2, d1, d2, new_state_c, new_state_d) \ case old_state : \ switch(*p){ \ case c1: \ case c2: \ state=(new_state_c); \ break; \ case d1: \ case d2: \ state=(new_state_d); \ break; \ u_param_common_cases; \ default: \ state=URI_PARAM_P; \ } \ break #define value_switch(old_state, c1, c2, new_state) \ case old_state: \ switch(*p){ \ case c1: \ case c2: \ state=(new_state); \ break; \ value_common_cases; \ default: \ state=URI_VAL_P; \ } \ break #define value_switch_big(old_state, c1, c2, d1, d2, new_state_c, new_state_d) \ case old_state: \ switch(*p){ \ case c1: \ case c2: \ state=(new_state_c); \ break; \ case d1: \ case d2: \ state=(new_state_d); \ break; \ value_common_cases; \ default: \ state=URI_VAL_P; \ } \ break #define transport_fin(c_state, proto_no) \ case c_state: \ switch(*p){ \ case '@': \ still_at_user; \ break; \ semicolon_case; \ param_set(b, v); \ uri->proto=(proto_no); \ break; \ question_case; \ param_set(b, v); \ uri->proto=(proto_no); \ break; \ colon_case; \ default: \ state=URI_VAL_P; \ break; \ } \ break /* init */ end=buf+len; p=buf+4; found_user=0; error_headers=0; b=v=0; param=param_val=0; pass=0; password.s = 0; password.len = 0; port_no=0; state=URI_INIT; memset(uri, 0, sizeof(struct sip_uri)); /* zero it all, just to be sure*/ /*look for sip:, sips: or tel:*/ if (len<5) goto error_too_short; scheme=buf[0]+(buf[1]<<8)+(buf[2]<<16)+(buf[3]<<24); scheme|=0x20202020; if (scheme==SIP_SCH){ uri->type=SIP_URI_T; }else if(scheme==SIPS_SCH){ if(buf[4]==':'){ p++; uri->type=SIPS_URI_T;} else goto error_bad_uri; }else if (scheme==TEL_SCH){ uri->type=TEL_URI_T; }else if (scheme==URN_SERVICE_SCH){ if (memcmp(buf+3,URN_SERVICE_STR,URN_SERVICE_STR_LEN) == 0) { p+= URN_SERVICE_STR_LEN-1; uri->type=URN_SERVICE_URI_T; } else goto error_bad_uri; }else goto error_bad_uri; s=p; for(;puser.s=s; uri->user.len=p-s; state=URI_HOST; found_user=1; s=p+1; /* skip '@' */ break; case ':': /* found the user, or the host? */ uri->user.s=s; uri->user.len=p-s; state=URI_PASSWORD; s=p+1; /* skip ':' */ break; case ';': /* this could be still the user or * params?*/ uri->host.s=s; uri->host.len=p-s; state=URI_PARAM; s=p+1; break; case '?': /* still user or headers? */ uri->host.s=s; uri->host.len=p-s; state=URI_HEADERS; s=p+1; break; /* almost anything permitted in the user part */ case '[': case ']': /* the user part cannot contain "[]" */ goto error_bad_char; } break; case URI_PASSWORD: /* this can also be the port (missing user)*/ switch(*p){ case '@': /* found the password*/ uri->passwd.s=s; uri->passwd.len=p-s; port_no=0; state=URI_HOST; found_user=1; s=p+1; /* skip '@' */ break; case ';': /* upps this is the port */ uri->port.s=s; uri->port.len=p-s; uri->port_no=port_no; /* user contains in fact the host */ uri->host.s=uri->user.s; uri->host.len=uri->user.len; uri->user.s=0; uri->user.len=0; state=URI_PARAM; found_user=1; /* there is no user part */ s=p+1; break; case '?': /* upps this is the port */ uri->port.s=s; uri->port.len=p-s; uri->port_no=port_no; /* user contains in fact the host */ uri->host.s=uri->user.s; uri->host.len=uri->user.len; uri->user.s=0; uri->user.len=0; state=URI_HEADERS; found_user=1; /* there is no user part */ s=p+1; break; case_port('0', port_no); case_port('1', port_no); case_port('2', port_no); case_port('3', port_no); case_port('4', port_no); case_port('5', port_no); case_port('6', port_no); case_port('7', port_no); case_port('8', port_no); case_port('9', port_no); case '[': case ']': case ':': goto error_bad_char; default: /* it can't be the port, non number found */ port_no=0; state=URI_PASSWORD_ALPHA; } break; case URI_PASSWORD_ALPHA: switch(*p){ case '@': /* found the password*/ uri->passwd.s=s; uri->passwd.len=p-s; state=URI_HOST; found_user=1; s=p+1; /* skip '@' */ break; case ';': /* contains non-numbers => cannot be port no*/ case '?': goto error_bad_port; case '[': case ']': case ':': goto error_bad_char; } break; case URI_HOST: switch(*p){ case '[': state=URI_HOST6_P; break; case ':': case ';': case '?': /* null host name ->invalid */ case '&': case '@': /*chars not allowed in hosts names */ goto error_bad_host; default: state=URI_HOST_P; } break; case URI_HOST_P: switch(*p){ check_host_end; } break; case URI_HOST6_END: switch(*p){ check_host_end; default: /*no chars allowed after [ipv6] */ goto error_bad_host; } break; case URI_HOST6_P: switch(*p){ case ']': state=URI_HOST6_END; break; case '[': case '&': case '@': case ';': case '?': goto error_bad_host; } break; case URI_PORT: switch(*p){ case ';': uri->port.s=s; uri->port.len=p-s; uri->port_no=port_no; state=URI_PARAM; s=p+1; break; case '?': uri->port.s=s; uri->port.len=p-s; uri->port_no=port_no; state=URI_HEADERS; s=p+1; break; case_port('0', port_no); case_port('1', port_no); case_port('2', port_no); case_port('3', port_no); case_port('4', port_no); case_port('5', port_no); case_port('6', port_no); case_port('7', port_no); case_port('8', port_no); case_port('9', port_no); case '&': case '@': case ':': default: goto error_bad_port; } break; case URI_PARAM: /* beginning of a new param */ switch(*p){ param_common_cases; /* recognized params */ case 't': case 'T': b=p; state=PT_T; break; case 'u': case 'U': b=p; state=PU_U; break; case 'm': case 'M': b=p; state=PM_M; break; case 'l': case 'L': b=p; state=PLR_L; break; case 'g': case 'G': b=p; state=PG_G; break; case 'r': case 'R': b=p; state=PR2_R; break; default: b=p; state=URI_PARAM_P; } break; case URI_PARAM_P: /* ignore current param */ /* supported params: * maddr, transport, ttl, lr, user, method, r2 */ switch(*p){ u_param_common_cases; case '=': v=p + 1; state=URI_PARAM_VAL_P; break; }; break; case URI_PARAM_VAL_P: /* value of the ignored current param */ switch(*p){ u_param_common_cases; }; break; /* ugly but fast param names parsing */ /*transport */ param_switch_big(PT_T, 'r', 'R', 't', 'T', PT_R, PTTL_T2); param_switch(PT_R, 'a', 'A', PT_A); param_switch(PT_A, 'n', 'N', PT_N); param_switch(PT_N, 's', 'S', PT_S); param_switch(PT_S, 'p', 'P', PT_P); param_switch(PT_P, 'o', 'O', PT_O); param_switch(PT_O, 'r', 'R', PT_R2); param_switch(PT_R2, 't', 'T', PT_T2); param_switch1(PT_T2, '=', PT_eq); /* value parsing */ case PT_eq: param=&uri->transport; param_val=&uri->transport_val; uri->proto = PROTO_OTHER; switch (*p){ param_common_cases; case 'u': case 'U': v=p; state=VU_U; break; case 't': case 'T': v=p; state=VT_T; break; case 's': case 'S': v=p; state=VS_S; break; case 'w': case 'W': v=p; state=VW_W; break; default: v=p; state=URI_VAL_P; } break; /* generic value */ case URI_VAL_P: switch(*p){ value_common_cases; } break; /* udp */ value_switch(VU_U, 'd', 'D', VU_D); value_switch(VU_D, 'p', 'P', VU_P_FIN); transport_fin(VU_P_FIN, PROTO_UDP); /* tcp */ value_switch_big(VT_T, 'c', 'C', 'l', 'L', VT_C, VTLS_L); value_switch(VT_C, 'p', 'P', VT_P_FIN); transport_fin(VT_P_FIN, PROTO_TCP); /* tls */ value_switch(VTLS_L, 's', 'S', VTLS_S_FIN); transport_fin(VTLS_S_FIN, PROTO_TLS); /* sctp */ value_switch(VS_S, 'c', 'C', VS_C); value_switch(VS_C, 't', 'T', VS_T); value_switch(VS_T, 'p', 'P', VS_P_FIN); transport_fin(VS_P_FIN, PROTO_SCTP); /* ws */ value_switch(VW_W, 's', 'S', VW_S); case VW_S: if (*p == 's' || *p == 'S') { state=(VWS_S_FIN); break; } /* if not a 's' transiting to VWS_S_FIN, fallback * to testing as existing VW_S_FIN (NOTE the missing break) */ state=(VW_S_FIN); transport_fin(VW_S_FIN, PROTO_WS); transport_fin(VWS_S_FIN, PROTO_WSS); /* ttl */ param_switch(PTTL_T2, 'l', 'L', PTTL_L); param_switch1(PTTL_L, '=', PTTL_eq); case PTTL_eq: param=&uri->ttl; param_val=&uri->ttl_val; switch(*p){ param_common_cases; default: v=p; state=URI_VAL_P; } break; /* user param */ param_switch(PU_U, 's', 'S', PU_S); param_switch(PU_S, 'e', 'E', PU_E); param_switch(PU_E, 'r', 'R', PU_R); param_switch1(PU_R, '=', PU_eq); case PU_eq: param=&uri->user_param; param_val=&uri->user_param_val; switch(*p){ param_common_cases; default: v=p; state=URI_VAL_P; } break; /* method*/ param_switch_big(PM_M, 'e', 'E', 'a', 'A', PM_E, PMA_A); param_switch(PM_E, 't', 'T', PM_T); param_switch(PM_T, 'h', 'H', PM_H); param_switch(PM_H, 'o', 'O', PM_O); param_switch(PM_O, 'd', 'D', PM_D); param_switch1(PM_D, '=', PM_eq); case PM_eq: param=&uri->method; param_val=&uri->method_val; switch(*p){ param_common_cases; default: v=p; state=URI_VAL_P; } break; /*maddr*/ param_switch(PMA_A, 'd', 'D', PMA_D); param_switch(PMA_D, 'd', 'D', PMA_D2); param_switch(PMA_D2, 'r', 'R', PMA_R); param_switch1(PMA_R, '=', PMA_eq); case PMA_eq: param=&uri->maddr; param_val=&uri->maddr_val; switch(*p){ param_common_cases; default: v=p; state=URI_VAL_P; } break; /* lr */ param_switch(PLR_L, 'r', 'R', PLR_R_FIN); case PLR_R_FIN: switch(*p){ case '@': still_at_user; break; case '=': state=PLR_eq; break; semicolon_case; uri->lr.s=b; uri->lr.len=(p-b); break; question_case; uri->lr.s=b; uri->lr.len=(p-b); break; colon_case; break; default: state=URI_PARAM_P; } break; /* handle lr=something case */ case PLR_eq: param=&uri->lr; param_val=&uri->lr_val; switch(*p){ param_common_cases; default: v=p; state=URI_VAL_P; } break; /* r2 */ param_switch1(PR2_R, '2', PR2_2_FIN); case PR2_2_FIN: switch(*p){ case '@': still_at_user; break; case '=': state=PR2_eq; break; semicolon_case; uri->r2.s=b; uri->r2.len=(p-b); break; question_case; uri->r2.s=b; uri->r2.len=(p-b); break; colon_case; break; default: state=URI_PARAM_P; } break; /* handle lr=something case */ case PR2_eq: param=&uri->r2; param_val=&uri->r2_val; switch(*p){ param_common_cases; default: v=p; state=URI_VAL_P; } break; /* gr */ param_switch(PG_G, 'r', 'R', PG_G_FIN); case PG_G_FIN: switch(*p){ case '@': still_at_user; break; case '=': state=PG_eq; break; semicolon_case; uri->gr.s=b; uri->gr.len=(p-b); break; question_case; uri->gr.s=b; uri->gr.len=(p-b); break; colon_case; break; default: state=URI_PARAM_P; } break; /* handle gr=something case */ case PG_eq: param=&uri->gr; param_val=&uri->gr_val; switch(*p){ param_common_cases; default: v=p; state=URI_VAL_P; } break; case URI_HEADERS: /* for now nobody needs them so we completely ignore the * headers (they are not allowed in request uri) --andrei */ switch(*p){ case '@': /* yak, we are still at user */ still_at_user; break; case ';': /* we might be still parsing user, try it */ if (found_user) goto error_bad_char; error_headers=1; /* if this is not the user we have an error */ /* if pass is set => it cannot be user:pass * => error (';') is illegal in a header */ if (pass) goto error_headers; break; case ':': if (found_user==0){ /*might be pass but only if user not found yet*/ if (pass){ found_user=1; /* no user */ pass=0; }else{ pass=p; } } break; case '?': if (pass){ found_user=1; /* no user, pass cannot contain '?'*/ pass=0; } break; } break; default: goto error_bug; } } /*end of uri */ switch (state){ case URI_INIT: /* error empty uri */ goto error_too_short; case URI_USER: /* this is the host, it can't be the user */ if (found_user) goto error_bad_uri; uri->host.s=s; uri->host.len=p-s; state=URI_HOST; break; case URI_PASSWORD: /* this is the port, it can't be the passwd */ if (found_user) goto error_bad_port; uri->port.s=s; uri->port.len=p-s; uri->port_no=port_no; uri->host=uri->user; uri->user.s=0; uri->user.len=0; break; case URI_PASSWORD_ALPHA: /* this is the port, it can't be the passwd */ goto error_bad_port; case URI_HOST_P: case URI_HOST6_END: uri->host.s=s; uri->host.len=p-s; break; case URI_HOST: /* error: null host */ case URI_HOST6_P: /* error: unterminated ipv6 reference*/ goto error_bad_host; case URI_PORT: uri->port.s=s; uri->port.len=p-s; uri->port_no=port_no; break; case URI_PARAM: case URI_PARAM_P: case URI_PARAM_VAL_P: u_param_set(b, v); /* intermediate param states */ case PT_T: /* transport */ case PT_R: case PT_A: case PT_N: case PT_S: case PT_P: case PT_O: case PT_R2: case PT_T2: case PT_eq: /* ignore empty transport params */ case PTTL_T2: /* ttl */ case PTTL_L: case PTTL_eq: case PU_U: /* user */ case PU_S: case PU_E: case PU_R: case PU_eq: case PM_M: /* method */ case PM_E: case PM_T: case PM_H: case PM_O: case PM_D: case PM_eq: case PLR_L: /* lr */ case PR2_R: /* r2 */ case PG_G: /* gr */ uri->params.s=s; uri->params.len=p-s; break; /* fin param states */ case PLR_R_FIN: case PLR_eq: uri->params.s=s; uri->params.len=p-s; uri->lr.s=b; uri->lr.len=p-b; break; case PR2_2_FIN: case PR2_eq: uri->params.s=s; uri->params.len=p-s; uri->r2.s=b; uri->r2.len=p-b; break; case PG_G_FIN: case PG_eq: uri->params.s=s; uri->params.len=p-s; uri->gr.s=b; uri->gr.len=p-b; break; case URI_VAL_P: /* intermediate value states */ case VU_U: case VU_D: case VT_T: case VT_C: case VTLS_L: case VS_S: case VS_C: case VW_W: case VS_T: uri->params.s=s; uri->params.len=p-s; param_set(b, v); break; /* fin value states */ case VU_P_FIN: uri->params.s=s; uri->params.len=p-s; param_set(b, v); uri->proto=PROTO_UDP; break; case VT_P_FIN: uri->params.s=s; uri->params.len=p-s; param_set(b, v); uri->proto=PROTO_TCP; break; case VTLS_S_FIN: uri->params.s=s; uri->params.len=p-s; param_set(b, v); uri->proto=PROTO_TLS; break; case VS_P_FIN: uri->params.s=s; uri->params.len=p-s; param_set(b, v); uri->proto=PROTO_SCTP; break; case VW_S: case VW_S_FIN: uri->params.s=s; uri->params.len=p-s; param_set(b, v); uri->proto=PROTO_WS; break; case VWS_S_FIN: uri->params.s=s; uri->params.len=p-s; param_set(b, v); uri->proto=PROTO_WSS; break; /* headers */ case URI_HEADERS: uri->headers.s=s; uri->headers.len=p-s; if (error_headers) goto error_headers; break; default: goto error_bug; } switch(uri->type){ case SIP_URI_T: if ((uri->user_param_val.len == 5) && (strncasecmp(uri->user_param_val.s, "phone", 5) == 0)) { uri->type = TEL_URI_T; /* move params from user into uri->params */ p=q_memchr(uri->user.s, ';', uri->user.len); if (p){ uri->params.s=p+1; uri->params.len=uri->user.s+uri->user.len-uri->params.s; uri->user.len=p-uri->user.s; } } break; case SIPS_URI_T: if ((uri->user_param_val.len == 5) && (strncasecmp(uri->user_param_val.s, "phone", 5) == 0)) { uri->type = TELS_URI_T; p=q_memchr(uri->user.s, ';', uri->user.len); if (p){ uri->params.s=p+1; uri->params.len=uri->user.s+uri->user.len-uri->params.s; uri->user.len=p-uri->user.s; } } break; case TEL_URI_T: case TELS_URI_T: /* fix tel uris, move the number in uri and empty the host */ uri->user=uri->host; uri->host.s=""; uri->host.len=0; break; case URN_SERVICE_URI_T: /* leave the actual service name in the URI domain part */ break; case ERROR_URI_T: LM_ERR("unexpected error (BUG?)\n"); goto error_bad_uri; break; /* do nothing, avoids a compilation warning */ } #ifdef EXTRA_DEBUG /* do stuff */ LM_DBG("parsed uri:\n type=%d user=<%.*s>(%d)\n passwd=<%.*s>(%d)\n" " host=<%.*s>(%d)\n port=<%.*s>(%d): %d\n params=<%.*s>(%d)\n" " headers=<%.*s>(%d)\n", uri->type, uri->user.len, ZSW(uri->user.s), uri->user.len, uri->passwd.len, ZSW(uri->passwd.s), uri->passwd.len, uri->host.len, ZSW(uri->host.s), uri->host.len, uri->port.len, ZSW(uri->port.s), uri->port.len, uri->port_no, uri->params.len, ZSW(uri->params.s), uri->params.len, uri->headers.len, ZSW(uri->headers.s), uri->headers.len ); LM_DBG(" uri params:\n transport=<%.*s>, val=<%.*s>, proto=%d\n", uri->transport.len, ZSW(uri->transport.s), uri->transport_val.len, ZSW(uri->transport_val.s), uri->proto); LM_DBG(" user-param=<%.*s>, val=<%.*s>\n", uri->user_param.len, ZSW(uri->user_param.s), uri->user_param_val.len, ZSW(uri->user_param_val.s)); LM_DBG(" method=<%.*s>, val=<%.*s>\n", uri->method.len, ZSW(uri->method.s), uri->method_val.len, ZSW(uri->method_val.s)); LM_DBG(" ttl=<%.*s>, val=<%.*s>\n", uri->ttl.len, ZSW(uri->ttl.s), uri->ttl_val.len, ZSW(uri->ttl_val.s)); LM_DBG(" maddr=<%.*s>, val=<%.*s>\n", uri->maddr.len, ZSW(uri->maddr.s), uri->maddr_val.len, ZSW(uri->maddr_val.s)); LM_DBG(" lr=<%.*s>, val=<%.*s>\n", uri->lr.len, ZSW(uri->lr.s), uri->lr_val.len, ZSW(uri->lr_val.s)); LM_DBG(" r2=<%.*s>, val=<%.*s>\n", uri->r2.len, ZSW(uri->r2.s), uri->r2_val.len, ZSW(uri->r2_val.s)); for(i=0; iu_name[i].s; i++) LM_DBG("uname=[%p]-><%.*s> uval=[%p]-><%.*s>\n", uri->u_name[i].s, uri->u_name[i].len, uri->u_name[i].s, uri->u_val[i].s, uri->u_val[i].len, uri->u_val[i].s); if (i!=uri->u_params_no) LM_ERR("inconsisten # of u_name:[%d]!=[%d]\n", i, uri->u_params_no); #endif return 0; error_too_short: LM_ERR("uri too short: <%.*s> (%d)\n", len, ZSW(buf), len); goto error_exit; error_bad_char: LM_ERR("bad char '%c' in state %d" " parsed: <%.*s> (%d) / <%.*s> (%d)\n", *p, state, (int)(p-buf), ZSW(buf), (int)(p-buf), len, ZSW(buf), len); goto error_exit; error_bad_host: LM_ERR("bad host in uri (error at char %c in" " state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n", *p, state, (int)(p-buf), ZSW(buf), (int)(p-buf), len, ZSW(buf), len); goto error_exit; error_bad_port: LM_ERR("bad port in uri (error at char %c in" " state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n", *p, state, (int)(p-buf), ZSW(buf), (int)(p-buf), len, ZSW(buf), len); goto error_exit; error_bad_uri: LM_ERR("bad uri, state %d parsed: <%.*s> (%d) / <%.*s> (%d)\n", state, (int)(p-buf), ZSW(buf), (int)(p-buf), len, ZSW(buf), len); goto error_exit; error_headers: LM_ERR("bad uri headers: <%.*s>(%d) / <%.*s>(%d)\n", uri->headers.len, ZSW(uri->headers.s), uri->headers.len, len, ZSW(buf), len); goto error_exit; error_bug: LM_CRIT("bad state %d parsed: <%.*s> (%d) / <%.*s> (%d)\n", state, (int)(p-buf), ZSW(buf), (int)(p-buf), len, ZSW(buf), len); error_exit: ser_error=E_BAD_URI; uri->type=ERROR_URI_T; update_stat(bad_URIs, 1); return E_BAD_URI; } int parse_sip_msg_uri(struct sip_msg* msg) { char* tmp; int tmp_len; if (msg->parsed_uri_ok) return 1; if (msg->new_uri.s){ tmp=msg->new_uri.s; tmp_len=msg->new_uri.len; }else{ tmp=msg->first_line.u.request.uri.s; tmp_len=msg->first_line.u.request.uri.len; } if (parse_uri(tmp, tmp_len, &msg->parsed_uri)<0){ LM_ERR("bad uri <%.*s>\n", tmp_len, tmp); msg->parsed_uri_ok=0; set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing r-uri"); set_err_reply(400, "bad r-uri"); return -1; } msg->parsed_uri_ok=1; return 0; } int parse_orig_ruri(struct sip_msg* msg) { str *uri; if (msg->parsed_orig_ruri_ok) return 1; uri = &REQ_LINE(msg).uri; if (parse_uri(uri->s, uri->len, &msg->parsed_orig_ruri)<0) { LM_ERR("bad uri <%.*s>\n", uri->len, ZSW(uri->s)); msg->parsed_orig_ruri_ok = 0; set_err_info(OSER_EC_PARSER, OSER_EL_MEDIUM, "error parsing incoming uri"); set_err_reply(400, "bad i-uri"); return -1; } msg->parsed_orig_ruri_ok = 1; return 0; } #define compare_uri_val(field,cmpfunc) \ do { \ if (first.field.len != second.field.len) \ { \ LM_DBG("Different URI field - " #field "\n"); \ return 1; \ } \ else \ { \ if (first.field.len != 0) \ if (cmpfunc(first.field.s,second.field.s,first.field.len)) \ { \ LM_DBG("Different URI field - " #field "\n"); \ return 1; \ } \ } \ } while (0) /* Compare 2 SIP URIs according to RFC 3261 * * Return value : 0 if URIs match * 1 if URIs don't match * -1 if errors have occurred */ int compare_uris(str *raw_uri_a,struct sip_uri* parsed_uri_a, str *raw_uri_b,struct sip_uri *parsed_uri_b) { #define UNESCAPED_BUF_LEN 1024 char unescaped_a[UNESCAPED_BUF_LEN], unescaped_b[UNESCAPED_BUF_LEN]; str unescaped_userA={unescaped_a, UNESCAPED_BUF_LEN}; str unescaped_userB={unescaped_b, UNESCAPED_BUF_LEN}; struct sip_uri first; struct sip_uri second; char matched[URI_MAX_U_PARAMS]; int i,j; if ( (!raw_uri_a && !parsed_uri_a) || (!raw_uri_b && !parsed_uri_b) ) { LM_ERR("Provide either a raw or parsed form of a SIP URI\n"); return -1; } if (raw_uri_a && raw_uri_b) { /* maybe we're lucky and straight-forward comparison succeeds */ if (raw_uri_a->len == raw_uri_b->len) if (strncasecmp(raw_uri_a->s,raw_uri_b->s,raw_uri_a->len) == 0) { LM_DBG("straight-forward URI match\n"); return 0; } } /* XXX - maybe if we have two parsed sip_uris, * or only one parsed and one raw, * it should be possible to do a straight-forward * URI match ? */ if (parsed_uri_a) first = *parsed_uri_a; else { if (parse_uri(raw_uri_a->s,raw_uri_a->len,&first) < 0) { LM_ERR("Failed to parse first URI\n"); return -1; } } if (parsed_uri_b) second = *parsed_uri_b; else { if (parse_uri(raw_uri_b->s,raw_uri_b->len,&second) < 0) { LM_ERR("Failed to parse second URI\n"); return -1; } } if (first.type != second.type) { LM_DBG("Different uri types\n"); return 1; } if (unescape_user(&first.user, &unescaped_userA) < 0 || unescape_user(&second.user, &unescaped_userB) < 0) { LM_ERR("Failed to unescape user!\n"); return -1; } first.user = unescaped_userA; second.user = unescaped_userB; compare_uri_val(user,strncmp); compare_uri_val(passwd,strncmp); compare_uri_val(host,strncasecmp); compare_uri_val(port,strncmp); compare_uri_val(transport_val,strncasecmp); compare_uri_val(ttl_val,strncasecmp); compare_uri_val(user_param_val,strncasecmp); compare_uri_val(maddr_val,strncasecmp); compare_uri_val(method_val,strncasecmp); compare_uri_val(lr_val,strncasecmp); compare_uri_val(r2_val,strncasecmp); if (first.u_params_no == 0 || second.u_params_no == 0) /* one URI doesn't have other params, * automatically all unknown params in other URI match */ goto headers_check; memset(matched,0,URI_MAX_U_PARAMS); for (i=0;i first URI matched second URI, and the other way around */ headers_check: /* XXX Do we really care ? */ compare_uri_val(headers,strncasecmp); return 0; } static const str uri_type_names[6] = { {NULL, 0}, /*This is the error type*/ str_init("sip"), str_init("sips"), str_init("tel"), str_init("tels"), str_init("urn:service") }; char* uri_type2str(const uri_type type, char *result) { if (type == ERROR_URI_T) return NULL; memcpy(result, uri_type_names[type].s, uri_type_names[type].len); return result + uri_type_names[type].len; } int uri_typestrlen(const uri_type type) { return uri_type_names[type].len; } uri_type str2uri_type(char * buf) { int scheme = 0; uri_type type = ERROR_URI_T; scheme=buf[0]+(buf[1]<<8)+(buf[2]<<16)+(buf[3]<<24); scheme|=0x20202020; if (scheme==SIP_SCH){ type=SIP_URI_T; }else if(scheme==SIPS_SCH){ if(buf[4]==':') type=SIPS_URI_T; else type = ERROR_URI_T; }else if (scheme==TEL_SCH){ type=TEL_URI_T; }else if (scheme==URN_SERVICE_SCH){ if (memcmp(buf+3,URN_SERVICE_STR,URN_SERVICE_STR_LEN) == 0) { type=URN_SERVICE_URI_T; } } return type; } opensips-2.2.2/parser/parse_uri.h000066400000000000000000000047711300170765700170320ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PARSE_URI_H #define PARSE_URI_H /* * SIP URI parser */ #include "../str.h" #include "../net/trans.h" #include "../parser/msg_parser.h" /* buf= pointer to beginning of uri (sip:x@foo.bar:5060;a=b?h=i) * len= len of uri * returns: fills uri & returns <0 on error or 0 if ok */ int parse_uri(char *buf, int len, struct sip_uri* uri); /* headers : the list of headers to parse (taken from uri structure) * h_name[] : array of header names * h_val[] : array of header values * h_size : size of header array */ int parse_uri_headers(str headers, str h_name[], str h_val[], int h_size); int parse_sip_msg_uri(struct sip_msg* msg); int parse_orig_ruri(struct sip_msg* msg); int compare_uris(str *raw_uri_a,struct sip_uri* parsed_uri_a, str *raw_uri_b,struct sip_uri *parsed_uri_b); char * uri_type2str(const uri_type type, char *result); int uri_typestrlen(const uri_type type); uri_type str2uri_type(char * buf); /* Gets (in a SIP wise manner) the SIP port from a SIP URI ; if the port is not explicitly set in the URI, it returns the default port corresponding to the user transport protocol (if protocol misses, we assume the default protos according to the URI schema) */ static inline unsigned short get_uri_port(struct sip_uri* _uri, unsigned short *_proto) { unsigned short port; unsigned short proto; /* known protocol? */ if ((proto=_uri->proto)==PROTO_NONE) { /* use UDP as default proto, but TLS for secure schemas */ proto = (_uri->type==SIPS_URI_T || _uri->type==TELS_URI_T)? PROTO_TLS : PROTO_UDP ; } /* known port? */ if ((port=_uri->port_no)==0) port = protos[proto].default_rfc_port; if (_proto) *_proto = proto; return port; } #endif /* PARSE_URI_H */ opensips-2.2.2/parser/parse_via.c000066400000000000000000001421641300170765700170040ustar00rootroot00000000000000/* * via parsing automaton * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * 2003-01-21 added rport parsing code, contributed by * Maxim Sobolev * 2003-01-23 added extra via param parsing code (i=...), used * by tcp to identify the sending socket, by andrei * 2003-01-23 fixed rport parsing code to accept rport w/o any value, * by andrei * 2003-01-27 modified parse_via to set new via_param->start member and * via->params.s (andrei) * 2003-01-28 zero-terminations replaced with VIA_ZT (jiri) * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-04-26 ZSW (jiri) * 2003-06-23 fixed parse_via_param [op].* param. parsing bug (andrei) * 2003-07-02 added support for TLS parsing in via (andrei) * 2003-10-27 added support for alias via param parsing [see * draft-ietf-sip-connect-reuse-00.txt.] (andrei) * 2004-03-31 fixed rport set instead of i bug (andrei) * 2005-03-02 if via has multiple bodies, and one of them is bad set * also the first one as bad (andrei) */ #include #include #include "../dprint.h" #include "../ut.h" #include "../ip_addr.h" #include "../mem/mem.h" #include "parse_via.h" #include "parse_def.h" /* main via states (uri:port ...) */ enum { F_HOST, P_HOST, L_PORT, F_PORT, P_PORT, L_PARAM, F_PARAM, P_PARAM, L_VIA, F_VIA, F_COMMENT, P_COMMENT, F_IP6HOST, P_IP6HOST, F_CRLF, F_LF, F_CR, END_OF_HEADER }; /* first via part state */ enum { F_SIP = 100, SIP1, SIP2, FIN_SIP, L_VER, F_VER, VER1, VER2, FIN_VER, UDP1, UDP2, FIN_UDP, TCP_TLS1, TCP2, FIN_TCP, TLS2, FIN_TLS, SCTP1, SCTP2, SCTP3, FIN_SCTP, WS1, WS_WSS, FIN_WS, FIN_WSS, OTHER_PROTO, L_PROTO, F_PROTO }; /* param related states * WARNING: keep in sync with parse_via.h, PARAM_HIDDEN, ... */ enum { L_VALUE = 200, F_VALUE, P_VALUE, P_STRING, HIDDEN1, HIDDEN2, HIDDEN3, HIDDEN4, HIDDEN5, TTL1, TTL2, BRANCH1, BRANCH2, BRANCH3, BRANCH4, BRANCH5, MADDR1, MADDR2, MADDR3, MADDR4, RECEIVED1, RECEIVED2, RECEIVED3, RECEIVED4, RECEIVED5, RECEIVED6, RECEIVED7, RPORT1, RPORT2, RPORT3, ALIAS1, ALIAS2, ALIAS3, ALIAS4, /* fin states (227-...)*/ FIN_HIDDEN = 230, FIN_TTL, FIN_BRANCH, FIN_MADDR, FIN_RECEIVED, FIN_RPORT, FIN_I, FIN_ALIAS /*GEN_PARAM, PARAM_ERROR*/ /* declared in msg_parser.h*/ }; /* entry state must be F_PARAM, or saved_state=F_PARAM and * state=F_{LF,CR,CRLF}! * output state = L_PARAM or F_PARAM or END_OF_HEADER * (and saved_state= last state); everything else => error * WARNING: param->start must be filled before, it's used in param->size * computation. */ static /*inline*/ char* parse_via_param(char* p, char* end, unsigned char* pstate, unsigned char* psaved_state, struct via_param* param) { char* tmp; register unsigned char state; unsigned char saved_state; state=*pstate; saved_state=*psaved_state; param->type=PARAM_ERROR; for (tmp=p;tmptype=state; param->name.len=tmp-param->name.s; state=L_PARAM; goto endofparam; case FIN_BRANCH: case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED: case FIN_RPORT: case FIN_I: param->type=state; param->name.len=tmp-param->name.s; state=L_VALUE; goto find_value; case F_PARAM: break; case F_LF: case F_CR: case F_CRLF: state=saved_state; break; case GEN_PARAM: default: param->type=GEN_PARAM; param->name.len=tmp-param->name.s; state=L_VALUE; goto find_value; } break; /* \n and \r*/ case '\n': switch(state){ case FIN_HIDDEN: case FIN_ALIAS: param->type=state; param->name.len=tmp-param->name.s; param->size=tmp-param->start; saved_state=L_PARAM; state=F_LF; goto endofparam; case FIN_BRANCH: case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED: case FIN_I: case FIN_RPORT: param->type=state; param->name.len=tmp-param->name.s; param->size=tmp-param->start; saved_state=L_VALUE; state=F_LF; goto find_value; case F_PARAM: saved_state=state; state=F_LF; break; case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; case F_CR: state=F_CRLF; break; case GEN_PARAM: default: param->type=GEN_PARAM; saved_state=L_VALUE; param->name.len=tmp-param->name.s; param->size=tmp-param->start; state=F_LF; goto find_value; } break; case '\r': switch(state){ case FIN_HIDDEN: case FIN_ALIAS: param->type=state; param->name.len=tmp-param->name.s; param->size=tmp-param->start; saved_state=L_PARAM; state=F_CR; goto endofparam; case FIN_BRANCH: case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED: case FIN_I: case FIN_RPORT: param->type=state; param->name.len=tmp-param->name.s; param->size=tmp-param->start; saved_state=L_VALUE; state=F_CR; goto find_value; case F_PARAM: saved_state=state; state=F_CR; break; case F_CR: case F_CRLF: state=END_OF_HEADER; goto end_via; case GEN_PARAM: default: param->type=GEN_PARAM; param->name.len=tmp-param->name.s; param->size=tmp-param->start; saved_state=L_VALUE; state=F_CR; goto find_value; } break; case '=': switch(state){ case FIN_BRANCH: case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED: case FIN_RPORT: case FIN_I: param->type=state; param->name.len=tmp-param->name.s; state=F_VALUE; goto find_value; case F_PARAM: case FIN_HIDDEN: case FIN_ALIAS: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; case GEN_PARAM: default: param->type=GEN_PARAM; param->name.len=tmp-param->name.s; state=F_VALUE; goto find_value; } break; case ';': switch(state){ case FIN_HIDDEN: case FIN_RPORT: /* rport can appear w/o a value */ case FIN_ALIAS: param->type=state; param->name.len=tmp-param->name.s; state=F_PARAM; goto endofparam; case FIN_BRANCH: case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED: case FIN_I: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; case GEN_PARAM: default: param->type=GEN_PARAM; param->name.len=tmp-param->name.s; state=F_PARAM; goto endofparam; } break; case ',': switch(state){ case FIN_HIDDEN: case FIN_RPORT: case FIN_ALIAS: param->type=state; param->name.len=tmp-param->name.s; state=F_VIA; goto endofvalue; case FIN_BRANCH: case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED: case FIN_I: LM_ERR("new via found (',') when '=' expected" "(state %d=)\n", state); goto parse_error; /* or we could ignore this bad param*/ case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; case GEN_PARAM: default: param->type=GEN_PARAM; param->name.len=tmp-param->name.s; state=F_VIA; goto endofvalue; } break; /* param names */ case 'h': case 'H': switch(state){ case F_PARAM: state=HIDDEN1; param->name.s=tmp; break; case BRANCH5: state=FIN_BRANCH; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'i': case 'I': switch(state){ case F_PARAM: state=FIN_I; param->name.s=tmp; break; case HIDDEN1: state=HIDDEN2; break; case RECEIVED4: state=RECEIVED5; break; case ALIAS2: state=ALIAS3; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'd': case 'D': switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case HIDDEN2: state=HIDDEN3; break; case HIDDEN3: state=HIDDEN4; break; case MADDR2: state=MADDR3; break; case MADDR3: state=MADDR4; break; case RECEIVED7: state=FIN_RECEIVED; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'e': case 'E': switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case HIDDEN4: state=HIDDEN5; break; case RECEIVED1: state=RECEIVED2; break; case RECEIVED3: state=RECEIVED4; break; case RECEIVED6: state=RECEIVED7; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'n': case 'N': switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case HIDDEN5: state=FIN_HIDDEN; break; case BRANCH3: state=BRANCH4; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 't': case 'T': switch(state){ case F_PARAM: state=TTL1; param->name.s=tmp; break; case TTL1: state=TTL2; break; case RPORT3: state=FIN_RPORT; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'l': case 'L': switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case TTL2: state=FIN_TTL; break; case ALIAS1: state=ALIAS2; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'm': case 'M': switch(state){ case F_PARAM: state=MADDR1; param->name.s=tmp; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'a': case 'A': switch(state){ case F_PARAM: state=ALIAS1; param->name.s=tmp; break; case MADDR1: state=MADDR2; break; case BRANCH2: state=BRANCH3; break; case ALIAS3: state=ALIAS4; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'r': case 'R': switch(state){ case MADDR4: state=FIN_MADDR; break; case F_PARAM: state=RECEIVED1; param->name.s=tmp; break; case BRANCH1: state=BRANCH2; break; case RPORT2: state=RPORT3; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'c': case 'C': switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case RECEIVED2: state=RECEIVED3; break; case BRANCH4: state=BRANCH5; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'v': case 'V': switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case RECEIVED5: state=RECEIVED6; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'b': case 'B': switch(state){ case F_PARAM: state=BRANCH1; param->name.s=tmp; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'p': case 'P': switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case RECEIVED1: state=RPORT1; break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 'o': case 'O': switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case RPORT1: state=RPORT2; break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; case 's': case 'S': switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case ALIAS4: state=FIN_ALIAS; break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } break; default: switch(state){ case F_PARAM: state=GEN_PARAM; param->name.s=tmp; break; case GEN_PARAM: break; case F_CR: case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; default: state=GEN_PARAM; } } }/* for tmp*/ /* end of packet? => error, no cr/lf,',' found!!!*/ saved_state=state; state=END_OF_HEADER; goto parse_error; find_value: tmp++; for(;*tmp;tmp++){ switch(*tmp){ case ' ': case '\t': switch(state){ case L_VALUE: case F_VALUE: /*eat space*/ break; case P_VALUE: state=L_PARAM; param->value.len=tmp-param->value.s; goto endofvalue; case P_STRING: break; case F_CR: case F_LF: case F_CRLF: state=saved_state; break; default: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; } break; case '\n': switch(state){ case L_VALUE: case F_VALUE: /*eat space*/ case P_STRING: saved_state=state; param->size=tmp-param->start; state=F_LF; break; case P_VALUE: saved_state=L_PARAM; state=F_LF; param->value.len=tmp-param->value.s; goto endofvalue; case F_LF: case F_CRLF: state=END_OF_HEADER; goto end_via; case F_CR: state=F_CRLF; break; default: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; } break; case '\r': switch(state){ case L_VALUE: case F_VALUE: /*eat space*/ case P_STRING: saved_state=state; param->size=tmp-param->start; state=F_CR; break; case P_VALUE: param->value.len=tmp-param->value.s; saved_state=L_PARAM; state=F_CR; goto endofvalue; case F_LF: case F_CR: case F_CRLF: state=END_OF_HEADER; goto end_via; default: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; } break; case '=': switch(state){ case L_VALUE: state=F_VALUE; break; case P_STRING: break; case F_LF: case F_CR: case F_CRLF: state=END_OF_HEADER; goto end_via; default: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; } break; case ';': switch(state){ case P_VALUE: param->value.len=tmp-param->value.s; state=F_PARAM; goto endofvalue; case F_VALUE: param->value.len=0; state=F_PARAM; goto endofvalue; case P_STRING: break; /* what to do? */ case F_LF: case F_CR: case F_CRLF: state=END_OF_HEADER; goto end_via; case L_VALUE: if (param->type==FIN_RPORT){ param->value.len=0; param->value.s=0; /* null value */ state=F_PARAM; goto endofvalue; }; /* no break */ default: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; } break; case ',': switch(state){ case P_VALUE: param->value.len=tmp-param->value.s; state=F_VIA; goto endofvalue; case P_STRING: case F_LF: case F_CR: case F_CRLF: state=END_OF_HEADER; goto end_via; case L_VALUE: if (param->type==FIN_RPORT){ param->value.len=0; param->value.s=0; /* null value */ state=F_VIA; goto endofvalue; }; /* no break */ default: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; } break; /* what to do? */ case '"': switch(state){ case F_VALUE: state=P_STRING; param->value.s=tmp+1; break; case P_STRING: state=L_PARAM; param->value.len=tmp-param->value.s; goto endofvalue; case F_LF: case F_CR: case F_CRLF: state=END_OF_HEADER; goto end_via; default: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; } break; default: switch(state){ case F_VALUE: state=P_VALUE; param->value.s=tmp; break; case P_VALUE: case P_STRING: break; case F_LF: case F_CR: case F_CRLF: state=END_OF_HEADER; goto end_via; default: LM_ERR("invalid char <%c> in state %d\n", *tmp, state); goto parse_error; } } } /* for2 tmp*/ /* end of buff and no CR/LF =>error*/ saved_state=state; state=END_OF_HEADER; goto parse_error; endofparam: endofvalue: param->size=tmp-param->start; normal_exit: *pstate=state; *psaved_state=saved_state; LM_DBG("found param type %d, <%.*s> = <%.*s>; state=%d\n", param->type, param->name.len, ZSW(param->name.s), (param->value.len?param->value.len:3), (param->value.len?param->value.s:"n/a"), state); return tmp; end_via: /* if we are here we found an "unexpected" end of via * (cr/lf). This is valid only if the param type is GEN_PARAM or * RPORT (the only ones which can miss the value; HIDDEN is a * special case )*/ if ((param->type==GEN_PARAM)||(param->type==PARAM_RPORT)){ saved_state=L_PARAM; /* change the saved_state, we have an unknown param. w/o a value */ /* param->size should be computed before */ goto normal_exit; } *pstate=state; *psaved_state=saved_state; LM_DBG("error on param type %d, <%.*s>, state=%d, saved_state=%d\n", param->type, param->name.len, ZSW(param->name.s), state, saved_state); parse_error: LM_ERR("parse_via_param\n"); param->type=PARAM_ERROR; *pstate=PARAM_ERROR; *psaved_state=state; return tmp; } /* * call it with a vb initialized to 0 * returns: pointer after the parsed parts and sets vb->error * WARNING: don't forget to cleanup on error with free_via_list(vb)! */ char* parse_via(char* buffer, char* end, struct via_body *vbody) { char* tmp; char* param_start; unsigned char state; unsigned char saved_state; int c_nest; int err; struct via_body* vb; struct via_param* param; vb=vbody; /* keep orignal vbody value, needed to set the error member in case of multiple via bodies in the same header */ parse_again: vb->error=PARSE_ERROR; /* parse start of via ( SIP/2.0/UDP )*/ state=F_SIP; saved_state=0; /*it should generate error if it's used without set*/ param_start=0; for(tmp=buffer;tmptransport.len=tmp-vb->transport.s; vb->proto=PROTO_UDP; state=F_HOST; /* start looking for host*/ goto main_via; case FIN_TCP: /* finished proto parsing */ vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_TCP; state=F_HOST; /* start looking for host*/ goto main_via; case FIN_TLS: /* finished proto parsing */ vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_TLS; state=F_HOST; /* start looking for host*/ goto main_via; case FIN_SCTP: /* finished proto parsing */ vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_SCTP; state=F_HOST; /* start looking for host*/ goto main_via; case FIN_WS: case WS_WSS: /* finished proto parsing */ vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_WS; state=F_HOST; /* start looking for host*/ goto main_via; case FIN_WSS: /* finished proto parsing */ vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_WSS; state=F_HOST; /* start looking for host*/ goto main_via; case OTHER_PROTO: /* finished proto parsing */ vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_OTHER; state=F_HOST; /* start looking for host*/ goto main_via; case FIN_SIP: vb->name.len=tmp-vb->name.s; state=L_VER; break; case FIN_VER: vb->version.len=tmp-vb->version.s; state=L_PROTO; break; case F_LF: case F_CRLF: case F_CR: /* header continues on this line */ state=saved_state; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case '\n': switch(state){ case L_VER: case F_SIP: case F_VER: case F_PROTO: case L_PROTO: saved_state=state; state=F_LF; break; case FIN_UDP: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_UDP; state=F_LF; saved_state=F_HOST; /* start looking for host*/ goto main_via; case FIN_TCP: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_TCP; state=F_LF; saved_state=F_HOST; /* start looking for host*/ goto main_via; case FIN_TLS: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_TLS; state=F_LF; saved_state=F_HOST; /* start looking for host*/ goto main_via; case WS_WSS: case FIN_WS: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_WS; state=F_LF; saved_state=F_HOST; /* start looking for host*/ goto main_via; case FIN_WSS: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_WS; state=F_LF; saved_state=F_HOST; /* start looking for host*/ goto main_via; case OTHER_PROTO: /* finished proto parsing */ vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_OTHER; state=F_LF; saved_state=F_HOST; /* start looking for host*/ goto main_via; case FIN_SIP: vb->name.len=tmp-vb->name.s; state=F_LF; saved_state=L_VER; break; case FIN_VER: vb->version.len=tmp-vb->version.s; state=F_LF; saved_state=L_PROTO; break; case F_CR: state=F_CRLF; break; case F_LF: case F_CRLF: state=saved_state; goto endofheader; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case '\r': switch(state){ case L_VER: case F_SIP: case F_VER: case F_PROTO: case L_PROTO: saved_state=state; state=F_CR; break; case FIN_UDP: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_UDP; state=F_CR; saved_state=F_HOST; goto main_via; case FIN_TCP: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_TCP; state=F_CR; saved_state=F_HOST; goto main_via; case FIN_TLS: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_TLS; state=F_CR; saved_state=F_HOST; goto main_via; case WS_WSS: case FIN_WS: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_WS; state=F_CR; saved_state=F_HOST; goto main_via; case FIN_WSS: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_WSS; state=F_CR; saved_state=F_HOST; goto main_via; case OTHER_PROTO: vb->transport.len=tmp-vb->transport.s; vb->proto=PROTO_OTHER; state=F_CR; saved_state=F_HOST; goto main_via; case FIN_SIP: vb->name.len=tmp-vb->name.s; state=F_CR; saved_state=L_VER; break; case FIN_VER: vb->version.len=tmp-vb->version.s; state=F_CR; saved_state=L_PROTO; break; case F_LF: /*end of line ?next header?*/ case F_CR: case F_CRLF: state=saved_state; goto endofheader; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case '/': switch(state){ case FIN_SIP: vb->name.len=tmp-vb->name.s; state=F_VER; break; case FIN_VER: vb->version.len=tmp-vb->version.s; state=F_PROTO; break; case L_VER: state=F_VER; break; case L_PROTO: state=F_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; /* match SIP*/ case 'S': case 's': switch(state){ case F_SIP: state=SIP1; vb->name.s=tmp; break; case TLS2: state=FIN_TLS; break; case F_PROTO: state=SCTP1; vb->transport.s=tmp; break; case WS1: state=WS_WSS; break; case WS_WSS: state=FIN_WSS; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case 'I': case 'i': switch(state){ case SIP1: state=SIP2; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case 'p': case 'P': switch(state){ case SIP2: state=FIN_SIP; break; /* allow p in PROTO */ case UDP2: state=FIN_UDP; break; case TCP2: state=FIN_TCP; break; case SCTP3: state=FIN_SCTP; break; case OTHER_PROTO: break; case UDP1: case FIN_UDP: case TCP_TLS1: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case 'U': case 'u': switch(state){ case F_PROTO: state=UDP1; vb->transport.s=tmp; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case 'D': case 'd': switch(state){ case UDP1: state=UDP2; break; case OTHER_PROTO: break; case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case 'T': case 't': switch(state){ case F_PROTO: state=TCP_TLS1; vb->transport.s=tmp; break; case SCTP2: state=SCTP3; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case 'C': case 'c': switch(state){ case TCP_TLS1: state=TCP2; break; case SCTP1: state=SCTP2; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case 'L': case 'l': switch(state){ case TCP_TLS1: state=TLS2; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case 'W': case 'w': switch(state){ case F_PROTO: state=WS1; vb->transport.s=tmp; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; /*match 2.0*/ case '2': switch(state){ case F_VER: state=VER1; vb->version.s=tmp; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case '.': switch(state){ case VER1: state=VER2; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; case '0': switch(state){ case VER2: state=FIN_VER; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; default: switch(state){ case F_PROTO: state=OTHER_PROTO; vb->transport.s=tmp; break; case OTHER_PROTO: break; case UDP1: case UDP2: case FIN_UDP: case TCP_TLS1: case TCP2: case FIN_TCP: case TLS2: case FIN_TLS: case SCTP1: case SCTP2: case SCTP3: case FIN_SCTP: case WS1: case WS_WSS: case FIN_WS: case FIN_WSS: state=OTHER_PROTO; break; default: LM_ERR("bad char <%c> on state %d\n", *tmp, state); goto parse_error; } break; } } /* for tmp*/ /* we should not be here! if everything is ok > main_via*/ LM_ERR("bad via: end of packet on state=%d\n", state); goto parse_error; main_via: /* inc tmp to point to the next char*/ tmp++; c_nest=0; /*state should always be F_HOST here*/; for(;*tmp;tmp++){ switch(*tmp){ case ' ': case '\t': switch(state){ case F_HOST:/*eat the spaces*/ break; case P_HOST: /*mark end of host*/ vb->host.len=tmp-vb->host.s; state=L_PORT; break; case L_PORT: /*eat the spaces*/ case F_PORT: break; case P_PORT: /*end of port */ vb->port_str.len=tmp-vb->port_str.s; state=L_PARAM; break; case L_PARAM: /* eat the space */ case F_PARAM: break; case P_PARAM: /* *tmp=0;*/ /*!?end of param*/ state=L_PARAM; break; case L_VIA: case F_VIA: /* eat the space */ break; case F_COMMENT: case P_COMMENT: break; case F_IP6HOST: /*no spaces allowed*/ case P_IP6HOST: LM_ERR("bad ipv6 reference\n"); goto parse_error; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now =' '*/ state=saved_state; break; default: LM_CRIT("on <%c>, state=%d\n",*tmp, state); goto parse_error; } break; case '\n': switch(state){ case F_HOST:/*eat the spaces*/ case L_PORT: /*eat the spaces*/ case F_PORT: case L_PARAM: /* eat the space */ case F_PARAM: case F_VIA: /* eat the space */ case L_VIA: case F_COMMENT: case P_COMMENT: case F_IP6HOST: case P_IP6HOST: saved_state=state; state=F_LF; break; case P_HOST: /*mark end of host*/ vb->host.len=tmp-vb->host.s; saved_state=L_PORT; state=F_LF; break; case P_PORT: /*end of port */ vb->port_str.len=tmp-vb->port_str.s; saved_state=L_PARAM; state=F_LF; break; case P_PARAM: /* *tmp=0;*/ /*!?end of param*/ saved_state=L_PARAM; state=F_LF; break; case F_CR: state=F_CRLF; break; case F_CRLF: case F_LF: state=saved_state; goto endofheader; default: LM_CRIT("BUG on <%c>\n",*tmp); goto parse_error; } break; case '\r': switch(state){ case F_HOST:/*eat the spaces*/ case L_PORT: /*eat the spaces*/ case F_PORT: case L_PARAM: /* eat the space */ case F_PARAM: case F_VIA: /* eat the space */ case L_VIA: case F_COMMENT: case P_COMMENT: case F_IP6HOST: case P_IP6HOST: saved_state=state; state=F_CR; break; case P_HOST: /*mark end of host*/ vb->host.len=tmp-vb->host.s; saved_state=L_PORT; state=F_CR; break; case P_PORT: /*end of port */ vb->port_str.len=tmp-vb->port_str.s; saved_state=L_PARAM; state=F_CR; break; case P_PARAM: /* *tmp=0;*/ /*!?end of param*/ saved_state=L_PARAM; state=F_CR; break; case F_CRLF: case F_CR: case F_LF: state=saved_state; goto endofheader; default: LM_CRIT("on <%c>\n",*tmp); goto parse_error; } break; case ':': switch(state){ case F_HOST: case F_IP6HOST: state=P_IP6HOST; break; case P_IP6HOST: break; case P_HOST: /*mark end of host*/ vb->host.len=tmp-vb->host.s; state=F_PORT; break; case L_PORT: state=F_PORT; break; case P_PORT: LM_ERR("bad port\n"); goto parse_error; case L_PARAM: case F_PARAM: case P_PARAM: LM_ERR("bad char <%c> in state %d\n", *tmp,state); goto parse_error; case L_VIA: case F_VIA: LM_ERR("bad char in compact via\n"); goto parse_error; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; case F_COMMENT:/*everything is allowed in a comment*/ vb->comment.s=tmp; state=P_COMMENT; break; case P_COMMENT: /*everything is allowed in a comment*/ break; default: LM_CRIT("on <%c> state %d\n", *tmp, state); goto parse_error; } break; case ';': switch(state){ case F_HOST: case F_IP6HOST: LM_ERR(" no host found\n"); goto parse_error; case P_IP6HOST: LM_ERR(" bad ipv6 reference\n"); goto parse_error; case P_HOST: vb->host.len=tmp-vb->host.s; state=F_PARAM; param_start=tmp+1; break; case P_PORT: /*mark the end*/ vb->port_str.len=tmp-vb->port_str.s; case L_PORT: case L_PARAM: state=F_PARAM; param_start=tmp+1; break; case F_PORT: LM_ERR(" bad char <%c> in state %d\n", *tmp,state); goto parse_error; case F_PARAM: LM_ERR("null param?\n"); goto parse_error; case P_PARAM: /*hmm next, param?*/ state=F_PARAM; param_start=tmp+1; break; case L_VIA: case F_VIA: LM_ERR("bad char <%c> in next via\n", *tmp); goto parse_error; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; case F_COMMENT:/*everything is allowed in a comment*/ vb->comment.s=tmp; state=P_COMMENT; break; case P_COMMENT: /*everything is allowed in a comment*/ break; default: LM_CRIT("on <%c> state %d\n", *tmp, state); goto parse_error; } break; case ',': switch(state){ case F_HOST: case F_IP6HOST: LM_ERR("no host found\n"); goto parse_error; case P_IP6HOST: LM_ERR(" bad ipv6 reference\n"); goto parse_error; case P_HOST: /*mark the end*/ vb->host.len=tmp-vb->host.s; state=F_VIA; break; case P_PORT: /*mark the end*/ vb->port_str.len=tmp-vb->port_str.s; state=F_VIA; break; case L_PORT: case L_PARAM: case P_PARAM: case L_VIA: state=F_VIA; break; case F_PORT: case F_PARAM: LM_ERR("invalid char <%c> in state %d\n", *tmp,state); goto parse_error; case F_VIA: /* do nothing, eat ","*/ break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; case F_COMMENT:/*everything is allowed in a comment*/ vb->comment.s=tmp; state=P_COMMENT; break; case P_COMMENT: /*everything is allowed in a comment*/ break; default: LM_CRIT("on <%c> state %d\n",*tmp, state); goto parse_error; } break; case '(': switch(state){ case F_HOST: case F_PORT: case F_PARAM: case F_VIA: case F_IP6HOST: case P_IP6HOST: /*must be terminated in ']'*/ LM_ERR(" on <%c> state %d\n", *tmp, state); goto parse_error; case P_HOST: /*mark the end*/ vb->host.len=tmp-vb->host.s; state=F_COMMENT; c_nest++; break; case P_PORT: /*mark the end*/ vb->port_str.len=tmp-vb->port_str.s; state=F_COMMENT; c_nest++; break; case P_PARAM: /*mark the end*/ vb->params.len=tmp-vb->params.s; state=F_COMMENT; c_nest++; break; case L_PORT: case L_PARAM: case L_VIA: state=F_COMMENT; vb->params.len=tmp-vb->params.s; c_nest++; break; case P_COMMENT: case F_COMMENT: c_nest++; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: LM_CRIT("on <%c> state %d\n", *tmp, state); goto parse_error; } break; case ')': switch(state){ case F_COMMENT: case P_COMMENT: if (c_nest){ c_nest--; if(c_nest==0){ state=L_VIA; vb->comment.len=tmp-vb->comment.s; break; } }else{ LM_ERR(" missing '(' - nesting= %d\n", c_nest); goto parse_error; } break; case F_HOST: case F_PORT: case F_PARAM: case F_VIA: case P_HOST: case P_PORT: case P_PARAM: case L_PORT: case L_PARAM: case L_VIA: case F_IP6HOST: case P_IP6HOST: LM_ERR(" on <%c> state %d\n",*tmp, state); goto parse_error; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: LM_CRIT("on <%c> state %d\n", *tmp, state); goto parse_error; } break; case '[': switch(state){ case F_HOST: vb->host.s=tmp; /* mark start here (include [])*/ state=F_IP6HOST; break; case F_COMMENT:/*everything is allowed in a comment*/ vb->comment.s=tmp; state=P_COMMENT; break; case P_COMMENT: break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: LM_ERR("on <%c> state %d\n",*tmp, state); goto parse_error; } break; case ']': switch(state){ case P_IP6HOST: /*mark the end*/ vb->host.len=(tmp-vb->host.s)+1; /* include "]" */ state=L_PORT; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; case F_COMMENT:/*everything is allowed in a comment*/ vb->comment.s=tmp; state=P_COMMENT; break; case P_COMMENT: break; default: LM_ERR("on <%c> state %d\n",*tmp, state); goto parse_error; } break; default: switch(state){ case F_HOST: state=P_HOST; vb->host.s=tmp; //break; case P_HOST: /*check if host allowed char*/ if ( (*tmp<'a' || *tmp>'z') && (*tmp<'A' || *tmp>'Z') && (*tmp<'0' || *tmp>'9') && *tmp!='-' && *tmp!='.') goto parse_error; break; case F_PORT: state=P_PORT; vb->port_str.s=tmp; //break; case P_PORT: /*check if number*/ if ( *tmp<'0' || *tmp>'9' ) goto parse_error; break; case F_PARAM: /*state=P_PARAM*/; if(vb->params.s==0) vb->params.s=param_start; param=pkg_malloc(sizeof(struct via_param)); if (param==0){ LM_ERR("no pkg memory left\n"); goto error; } memset(param,0, sizeof(struct via_param)); param->start=param_start; tmp=parse_via_param(tmp, end, &state, &saved_state, param); switch(state){ case F_PARAM: param_start=tmp+1; case L_PARAM: case F_LF: case F_CR: vb->params.len=tmp - vb->params.s; break; case F_VIA: vb->params.len=param->start+param->size -vb->params.s; break; case END_OF_HEADER: vb->params.len=param->start+param->size -vb->params.s; break; case PARAM_ERROR: pkg_free(param); goto parse_error; default: pkg_free(param); LM_ERR(" after parse_via_param: invalid " "char <%c> on state %d\n",*tmp, state); goto parse_error; } /*add param to the list*/ if (vb->last_param) vb->last_param->next=param; else vb->param_lst=param; vb->last_param=param; /* update param. shortcuts */ switch(param->type){ case PARAM_BRANCH: vb->branch=param; break; case PARAM_RECEIVED: vb->received=param; break; case PARAM_RPORT: vb->rport=param; break; case PARAM_I: vb->i=param; break; case PARAM_ALIAS: vb->alias=param; break; case PARAM_MADDR: vb->maddr=param; break; } if (state==END_OF_HEADER){ state=saved_state; goto endofheader; } break; case P_PARAM: break; case F_VIA: /*vb->next=tmp;*/ /*???*/ goto nextvia; case L_PORT: case L_PARAM: case L_VIA: LM_ERR("on <%c> state %d (default)\n",*tmp, state); goto parse_error; case F_COMMENT: state=P_COMMENT; vb->comment.s=tmp; break; case P_COMMENT: break; case F_IP6HOST: state=P_IP6HOST; //break; case P_IP6HOST: /*check if host allowed char*/ if ( (*tmp<'a' || *tmp>'f') && (*tmp<'A' || *tmp>'F') && (*tmp<'0' || *tmp>'9') && *tmp!=':') goto parse_error; break; case F_CRLF: case F_LF: case F_CR: /*previous=crlf and now !=' '*/ goto endofheader; default: LM_CRIT("invalid char <%c> in state %d\n",*tmp, state); goto parse_error; } } } LM_DBG("end of packet reached, state=%d\n", state); goto endofpacket; /*end of packet, probably should be goto error*/ endofheader: state=saved_state; LM_DBG("end of header reached, state=%d\n", state); endofpacket: /* check if error*/ switch(state){ case P_HOST: case L_PORT: case P_PORT: case L_PARAM: case P_PARAM: case P_VALUE: case GEN_PARAM: case FIN_HIDDEN: case L_VIA: break; default: LM_ERR(" invalid via - end of header in state %d\n", state); goto parse_error; } /* if (proto) printf("\n", proto); if (host) printf("host= <%s>\n", host); if (port_str) printf("port= <%s>\n", port_str); if (param) printf("params= <%s>\n", param); if (comment) printf("comment= <%s>\n", comment); if(next_via) printf("next_via= <%s>\n", next_via); */ /*LM_DBG("rest=<%s>\n", tmp);*/ vb->error=PARSE_OK; vb->bsize=tmp-buffer; if (vb->port_str.s){ vb->port=str2s(vb->port_str.s, vb->port_str.len, &err); if (err){ LM_ERR(" invalid port number <%.*s>\n", vb->port_str.len, ZSW(vb->port_str.s)); goto parse_error; } } return tmp; nextvia: LM_DBG("next_via\n"); vb->error=PARSE_OK; vb->bsize=tmp-buffer; if (vb->port_str.s){ vb->port=str2s(vb->port_str.s, vb->port_str.len, &err); if (err){ LM_ERR(" invalid port number <%.*s>\n", vb->port_str.len, ZSW(vb->port_str.s)); goto parse_error; } } vb->next=pkg_malloc(sizeof(struct via_body)); if (vb->next==0){ LM_ERR(" out of pkg memory\n"); goto error; } vb=vb->next; memset(vb, 0, sizeof(struct via_body)); buffer=tmp; goto parse_again; parse_error: if (end>buffer){ LM_ERR(" <%.*s>\n", (int)(end-buffer), ZSW(buffer)); } if ((tmp>buffer)&&(tmp\n", (int)(tmp-buffer), ZSW(buffer) ); }else{ LM_ERR("via parse failed\n"); } error: vb->error=PARSE_ERROR; vbody->error=PARSE_ERROR; /* make sure the first via body is marked as bad also */ return tmp; } static inline void free_via_param_list(struct via_param* vp) { struct via_param* foo; while(vp){ foo=vp; vp=vp->next; pkg_free(foo); } } void free_via_list(struct via_body* vb) { struct via_body* foo; while(vb){ foo=vb; vb=vb->next; if (foo->param_lst) free_via_param_list(foo->param_lst); pkg_free(foo); } } opensips-2.2.2/parser/parse_via.h000066400000000000000000000060371300170765700170070ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /* * 2003-01-21 added rport parsing code, contributed by * Maxim Sobolev * 2003-01-21 added extra via param parsing code (i=...), used * by tcp to identify the sending socket, by andrei * 2003-01-27 added a new member (start) to via_param, by andrei * 2003-10-27 added alias to via && PARAM_ALIAS (andrei) */ #ifndef PARSE_VIA_H #define PARSE_VIA_H #include "../str.h" /* via param types * WARNING: keep in sync with parse_via.c FIN_HIDDEN... * and with tm/sip_msg.c via_body_cloner */ enum { PARAM_HIDDEN=230, PARAM_TTL, PARAM_BRANCH, PARAM_MADDR, PARAM_RECEIVED, PARAM_RPORT, PARAM_I, PARAM_ALIAS, GEN_PARAM, PARAM_ERROR }; struct via_param { int type; /* Type of the parameter */ str name; /* Name of the parameter */ str value; /* Value of the parameter */ char* start; /* Pointer to param start, just after ';', * (it can be diff. from name.s!) */ int size; /* total size, including preceding and trailing * white space */ struct via_param* next; /* Next parameter in the list */ }; /* Format: name/version/transport host:port;params comment */ /* WARNING: keep in sync with tm/sip_msg.c via_body_cloner */ struct via_body { int error; str hdr; /* Contains "Via" or "v" */ str name; str version; str transport; str host; unsigned short proto; /* transport */ unsigned short port; str port_str; str params; str comment; unsigned int bsize; /* body size, not including hdr */ struct via_param* param_lst; /* list of parameters*/ struct via_param* last_param; /*last via parameter, internal use*/ /* shortcuts to "important" params*/ struct via_param* branch; str tid; /* transaction id, part of branch */ struct via_param* received; struct via_param* rport; struct via_param* i; struct via_param* alias; /* alias see draft-ietf-sip-connect-reuse-00 */ struct via_param* maddr; struct via_body* next; /* pointer to next via body string if compact via or null */ }; /* * Main Via header field parser */ char* parse_via(char* buffer, char* end, struct via_body *vb); /* * Free allocated memory */ void free_via_list(struct via_body *vb); #endif /* PARSE_VIA_H */ opensips-2.2.2/parser/parser_f.c000066400000000000000000000024141300170765700166250ustar00rootroot00000000000000/* * parser helper functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "parser_f.h" #include "../ut.h" /* returns pointer to next line or after the end of buffer */ char* eat_line(char* buffer, unsigned int len) { char* nl; /* jku .. replace for search with a library function; not conforming as I do not care about CR */ nl=(char *)q_memchr( buffer, '\n', len ); if ( nl ) { if ( nl + 1 < buffer+len) nl++; if (( nl+1len; i++) { if (!quoted) { if (_s->s[i] == '\"') quoted = 1; else if (_s->s[i] == _c) return _s->s + i; } else { if ((_s->s[i] == '\"') && (_s->s[i - 1] != '\\')) quoted = 0; } } return 0; } #endif /* parser_f_h */ opensips-2.2.2/parser/sdp/000077500000000000000000000000001300170765700154455ustar00rootroot00000000000000opensips-2.2.2/parser/sdp/sdp.c000066400000000000000000001014371300170765700164050ustar00rootroot00000000000000/* * SDP parser interface * * Copyright (C) 2008 SOMA Networks, INC. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * HISTORY: * -------- * 2007-09-09 osas: ported and enhanced sdp parsing functions from nathelper module * 2008-04-22 osas: integrated RFC4975 attributes - patch provided by Denis Bilenko (denik) * */ #include "../../ut.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../parser_f.h" #include "../parse_content.h" #include "sdp.h" #include "sdp_helpr_funcs.h" #define USE_PKG_MEM 0 #define USE_SHM_MEM 1 #define HOLD_IP_STR "0.0.0.0" #define HOLD_IP_LEN 7 /** * Creates and initialize a new sdp_info structure */ static inline int new_sdp(struct sip_msg* _m) { sdp_info_t* sdp; sdp = (sdp_info_t*)pkg_malloc(sizeof(sdp_info_t)); if (sdp == NULL) { LM_ERR("No memory left\n"); return -1; } memset( sdp, 0, sizeof(sdp_info_t)); _m->sdp = sdp; return 0; } /** * Alocate a new session cell. */ static inline sdp_session_cell_t *add_sdp_session(sdp_info_t* _sdp, int session_num, str* cnt_disp) { sdp_session_cell_t *session; int len; len = sizeof(sdp_session_cell_t); session = (sdp_session_cell_t*)pkg_malloc(len); if (session == NULL) { LM_ERR("No memory left\n"); return NULL; } memset( session, 0, len); session->session_num = session_num; if (cnt_disp != NULL) { session->cnt_disp.s = cnt_disp->s; session->cnt_disp.len = cnt_disp->len; } /* Insert the new session */ session->next = _sdp->sessions; _sdp->sessions = session; _sdp->sessions_num++; return session; } /** * Allocate a new stream cell. */ static inline sdp_stream_cell_t *add_sdp_stream(sdp_session_cell_t* _session, int stream_num, str* media, str* port, str* transport, str* payloads, int is_rtp, int pf, str* sdp_ip) { sdp_stream_cell_t *stream; int len; len = sizeof(sdp_stream_cell_t); stream = (sdp_stream_cell_t*)pkg_malloc(len); if (stream == NULL) { LM_ERR("No memory left\n"); return NULL; } memset( stream, 0, len); stream->stream_num = stream_num; stream->media.s = media->s; stream->media.len = media->len; stream->port.s = port->s; stream->port.len = port->len; stream->transport.s = transport->s; stream->transport.len = transport->len; stream->payloads.s = payloads->s; stream->payloads.len = payloads->len; stream->is_rtp = is_rtp; stream->pf = pf; stream->ip_addr.s = sdp_ip->s; stream->ip_addr.len = sdp_ip->len; /* Insert the new stream */ stream->next = _session->streams; _session->streams = stream; _session->streams_num++; return stream; } /** * Allocate a new payload. */ static inline sdp_payload_attr_t *add_sdp_payload(sdp_stream_cell_t* _stream, int payload_num, str* payload) { sdp_payload_attr_t *payload_attr; int len; len = sizeof(sdp_payload_attr_t); payload_attr = (sdp_payload_attr_t*)pkg_malloc(len); if (payload_attr == NULL) { LM_ERR("No memory left\n"); return NULL; } memset( payload_attr, 0, len); payload_attr->payload_num = payload_num; payload_attr->rtp_payload.s = payload->s; payload_attr->rtp_payload.len = payload->len; /* Insert the new payload */ payload_attr->next = _stream->payload_attr; _stream->payload_attr = payload_attr; _stream->payloads_num++; return payload_attr; } /** * Initialize fast access pointers. */ static inline sdp_payload_attr_t** init_p_payload_attr(sdp_stream_cell_t* _stream, int pkg) { int payloads_num, i; sdp_payload_attr_t *payload; if (_stream == NULL) { LM_ERR("Invalid stream\n"); return NULL; } payloads_num = _stream->payloads_num; if (payloads_num == 0) { LM_ERR("Invalid number of payloads\n"); return NULL; } if (pkg == USE_PKG_MEM) { _stream->p_payload_attr = (sdp_payload_attr_t**)pkg_malloc(payloads_num * sizeof(sdp_payload_attr_t*)); } else if (pkg == USE_SHM_MEM) { _stream->p_payload_attr = (sdp_payload_attr_t**)shm_malloc(payloads_num * sizeof(sdp_payload_attr_t*)); } else { LM_ERR("undefined memory type\n"); return NULL; } if (_stream->p_payload_attr == NULL) { LM_ERR("No memory left\n"); return NULL; } --payloads_num; payload = _stream->payload_attr; for (i=0;i<=payloads_num;i++) { _stream->p_payload_attr[payloads_num-i] = payload; payload = payload->next; } return _stream->p_payload_attr; } /* * Setters ... */ void set_sdp_payload_attr(sdp_payload_attr_t *payload_attr, str *rtp_enc, str *rtp_clock, str *rtp_params) { if (payload_attr == NULL) { LM_ERR("Invalid payload location\n"); return; } payload_attr->rtp_enc.s = rtp_enc->s; payload_attr->rtp_enc.len = rtp_enc->len; payload_attr->rtp_clock.s = rtp_clock->s; payload_attr->rtp_clock.len = rtp_clock->len; payload_attr->rtp_params.s = rtp_params->s; payload_attr->rtp_params.len = rtp_params->len; return; } void set_sdp_payload_fmtp(sdp_payload_attr_t *payload_attr, str *fmtp_string ) { if (payload_attr == NULL) { LM_ERR("Invalid payload location\n"); return; } payload_attr->fmtp_string.s = fmtp_string->s; payload_attr->fmtp_string.len = fmtp_string->len; return; } /* * Getters .... */ int get_sdp_session_num(struct sip_msg* _m) { if (_m->sdp == NULL) return 0; return _m->sdp->sessions_num; } int get_sdp_stream_num(struct sip_msg* _m) { if (_m->sdp == NULL) return 0; return _m->sdp->streams_num; } sdp_session_cell_t* get_sdp_session_sdp(struct sdp_info* sdp, int session_num) { sdp_session_cell_t *session; session = sdp->sessions; if (session_num >= sdp->sessions_num) return NULL; while (session) { if (session->session_num == session_num) return session; session = session->next; } return NULL; } sdp_session_cell_t* get_sdp_session(struct sip_msg* _m, int session_num) { if (_m->sdp == NULL) return NULL; return get_sdp_session_sdp(_m->sdp, session_num); } sdp_stream_cell_t* get_sdp_stream_sdp(struct sdp_info* sdp, int session_num, int stream_num) { sdp_session_cell_t *session; sdp_stream_cell_t *stream; if (sdp==NULL) return NULL; if (session_num >= sdp->sessions_num) return NULL; session = sdp->sessions; while (session) { if (session->session_num == session_num) { if (stream_num >= session->streams_num) return NULL; stream = session->streams; while (stream) { if (stream->stream_num == stream_num) return stream; stream = stream->next; } break; } else { session = session->next; } } return NULL; } sdp_stream_cell_t* get_sdp_stream(struct sip_msg* _m, int session_num, int stream_num) { if (_m->sdp == NULL) return NULL; return get_sdp_stream_sdp(_m->sdp, session_num, stream_num); } sdp_payload_attr_t* get_sdp_payload4payload(sdp_stream_cell_t *stream, str *rtp_payload) { sdp_payload_attr_t *payload; int i; if (stream == NULL) { LM_ERR("Invalid stream location\n"); return NULL; } if (stream->p_payload_attr == NULL) { LM_ERR("Invalid access pointer to payloads\n"); return NULL; } for (i=0;ipayloads_num;i++) { payload = stream->p_payload_attr[i]; if (rtp_payload->len == payload->rtp_payload.len && (strncmp(rtp_payload->s, payload->rtp_payload.s, rtp_payload->len)==0)) { return payload; } } return NULL; } sdp_payload_attr_t* get_sdp_payload4index(sdp_stream_cell_t *stream, int index) { if (stream == NULL) { LM_ERR("Invalid stream location\n"); return NULL; } if (stream->p_payload_attr == NULL) { LM_ERR("Invalid access pointer to payloads\n"); return NULL; } if (index >= stream->payloads_num) { LM_ERR("Out of range index [%d] for payload\n", index); return NULL; } return stream->p_payload_attr[index]; } /** * SDP parser method. */ int parse_sdp_session(str *sdp_body, int session_num, str *cnt_disp, sdp_info_t* _sdp) { str body = *sdp_body; str sdp_ip = {NULL,0}; str sdp_media, sdp_port, sdp_transport, sdp_payload; str payload; str rtp_payload, rtp_enc, rtp_clock, rtp_params; int is_rtp; char *bodylimit; char *v1p, *o1p, *m1p, *m2p, *c1p, *c2p, *a1p, *a2p, *b1p; str tmpstr1; int stream_num, payloadnum, pf; sdp_session_cell_t *session; sdp_stream_cell_t *stream; sdp_payload_attr_t *payload_attr; int parse_payload_attr; str fmtp_string; /* * Parsing of SDP body. * Each session starts with v-line and each session may contain a few * media descriptions (each starts with m-line). * We have to change ports in m-lines, and also change IP addresses in * c-lines which can be placed either in session header (fallback for * all medias) or media description. * Ports should be allocated for any media. IPs all should be changed * to the same value (RTP proxy IP), so we can change all c-lines * unconditionally. */ bodylimit = body.s + body.len; v1p = find_sdp_line(body.s, bodylimit, 'v'); if (v1p == NULL) { LM_ERR("no sessions in SDP\n"); return -1; } /* get session origin */ o1p = find_sdp_line(v1p, bodylimit, 'o'); if (o1p == NULL) { LM_ERR("no o= in session\n"); return -1; } /* Have this session media description? */ m1p = find_sdp_line(o1p, bodylimit, 'm'); if (m1p == NULL) { LM_ERR("no m= in session\n"); return -1; } /* Allocate a session cell */ session = add_sdp_session(_sdp, session_num, cnt_disp); if (session == NULL) return -1; /* Get origin IP */ tmpstr1.s = o1p; tmpstr1.len = bodylimit - tmpstr1.s; /* limit is session limit text */ if (extract_mediaip(&tmpstr1, &session->o_ip_addr, &session->o_pf,"o=") == -1) { LM_ERR("can't extract origin media IP from the message\n"); return -1; } /* Find c1p only between session begin and first media. * c1p will give common c= for all medias. */ c1p = find_sdp_line(o1p, m1p, 'c'); if (c1p) { /* Extract session address */ tmpstr1.s = c1p; tmpstr1.len = bodylimit - tmpstr1.s; /* limit is session limit text */ if (extract_mediaip(&tmpstr1, &session->ip_addr, &session->pf,"c=") == -1) { LM_ERR("can't extract common media IP from the message\n"); return -1; } } /* Find b1p only between session begin and first media. * b1p will give common b= for all medias. */ b1p = find_sdp_line(o1p, m1p, 'b'); if (b1p) { tmpstr1.s = b1p; tmpstr1.len = m1p - b1p; extract_bwidth(&tmpstr1, &session->bw_type, &session->bw_width); } /* Have session. Iterate media descriptions in session */ m2p = m1p; stream_num = 0; for (;;) { m1p = m2p; if (m1p == NULL || m1p >= bodylimit) break; m2p = find_next_sdp_line(m1p, bodylimit, 'm', bodylimit); /* c2p will point to per-media "c=" */ c2p = find_sdp_line(m1p, m2p, 'c'); if (c2p) { /* Extract stream address */ tmpstr1.s = c2p; tmpstr1.len = bodylimit - tmpstr1.s; /* limit is session limit text */ if (extract_mediaip(&tmpstr1, &sdp_ip, &pf,"c=") == -1) { LM_ERR("can't extract media IP from the message\n"); return -1; } } else { if (!c1p) { /* No "c=" */ LM_ERR("can't find media IP in the message\n"); return -1; } } /* Extract the port on sdp_port */ tmpstr1.s = m1p; tmpstr1.len = m2p - m1p; if (extract_media_attr(&tmpstr1, &sdp_media, &sdp_port, &sdp_transport, &sdp_payload, &is_rtp) == -1) { LM_ERR("can't extract media attr from the message\n"); return -1; } /* Allocate a stream cell */ stream = add_sdp_stream(session, stream_num, &sdp_media, &sdp_port, &sdp_transport, &sdp_payload, is_rtp, pf, &sdp_ip); if (stream == 0) return -1; /* increment total number of streams */ _sdp->streams_num++; /* b1p will point to per-media "b=" */ b1p = find_sdp_line(m1p, m2p, 'b'); if (b1p) { tmpstr1.s = b1p; tmpstr1.len = m2p - b1p; extract_bwidth(&tmpstr1, &stream->bw_type, &stream->bw_width); } /* Parsing the payloads */ tmpstr1.s = sdp_payload.s; tmpstr1.len = sdp_payload.len; payloadnum = 0; if (tmpstr1.len != 0) { for (;;) { a1p = eat_token_end(tmpstr1.s, tmpstr1.s + tmpstr1.len); payload.s = tmpstr1.s; payload.len = a1p - tmpstr1.s; payload_attr = add_sdp_payload(stream, payloadnum, &payload); if (payload_attr == NULL) return -1; tmpstr1.len -= payload.len; tmpstr1.s = a1p; a2p = eat_space_end(tmpstr1.s, tmpstr1.s + tmpstr1.len); tmpstr1.len -= a2p - a1p; tmpstr1.s = a2p; if (a1p >= tmpstr1.s) break; payloadnum++; } /* Initialize fast access pointers */ if (NULL == init_p_payload_attr(stream, USE_PKG_MEM)) { return -1; } parse_payload_attr = 1; } else { parse_payload_attr = 0; } payload_attr = 0; /* Let's figure out the atributes */ a1p = find_sdp_line(m1p, m2p, 'a'); a2p = a1p; for (;;) { a1p = a2p; if (a1p == NULL || a1p >= m2p) break; tmpstr1.s = a2p; tmpstr1.len = m2p - a2p; if (parse_payload_attr && extract_ptime(&tmpstr1, &stream->ptime) == 0) { a1p = stream->ptime.s + stream->ptime.len; } else if (parse_payload_attr && extract_sendrecv_mode(&tmpstr1, &stream->sendrecv_mode, &stream->is_on_hold) == 0) { a1p = stream->sendrecv_mode.s + stream->sendrecv_mode.len; } else if (parse_payload_attr && extract_rtpmap(&tmpstr1, &rtp_payload, &rtp_enc, &rtp_clock, &rtp_params) == 0) { if (rtp_params.len != 0 && rtp_params.s != NULL) { a1p = rtp_params.s + rtp_params.len; } else { a1p = rtp_clock.s + rtp_clock.len; } payload_attr = (sdp_payload_attr_t*)get_sdp_payload4payload(stream, &rtp_payload); set_sdp_payload_attr(payload_attr, &rtp_enc, &rtp_clock, &rtp_params); } else if (extract_rtcp(&tmpstr1, &stream->rtcp_port) == 0) { a1p = stream->rtcp_port.s + stream->rtcp_port.len; } else if (parse_payload_attr && extract_fmtp(&tmpstr1,&rtp_payload,&fmtp_string) == 0){ a1p = fmtp_string.s + fmtp_string.len; payload_attr = (sdp_payload_attr_t*)get_sdp_payload4payload(stream, &rtp_payload); set_sdp_payload_fmtp(payload_attr, &fmtp_string); } else if (extract_accept_types(&tmpstr1, &stream->accept_types) == 0) { a1p = stream->accept_types.s + stream->accept_types.len; } else if (extract_accept_wrapped_types(&tmpstr1, &stream->accept_wrapped_types) == 0) { a1p = stream->accept_wrapped_types.s + stream->accept_wrapped_types.len; } else if (extract_max_size(&tmpstr1, &stream->max_size) == 0) { a1p = stream->max_size.s + stream->max_size.len; } else if (extract_path(&tmpstr1, &stream->path) == 0) { a1p = stream->path.s + stream->path.len; /*} else { */ /* LM_DBG("else: `%.*s'\n", tmpstr1.len, tmpstr1.s); */ } a2p = find_next_sdp_line(a1p-1, m2p, 'a', m2p); } /* Let's detect if the media is on hold by checking * the good old "0.0.0.0" connection address */ if (!stream->is_on_hold) { if (stream->ip_addr.s && stream->ip_addr.len) { if (stream->ip_addr.len == HOLD_IP_LEN && strncmp(stream->ip_addr.s, HOLD_IP_STR, HOLD_IP_LEN)==0) stream->is_on_hold = 1; } else if (session->ip_addr.s && session->ip_addr.len) { if (session->ip_addr.len == HOLD_IP_LEN && strncmp(session->ip_addr.s, HOLD_IP_STR, HOLD_IP_LEN)==0) stream->is_on_hold = 1; } } ++stream_num; } /* Iterate medias/streams in session */ return 0; } static int parse_mixed_content(str *mixed_body, str delimiter, sdp_info_t* _sdp) { int res, no_eoh_found, start_parsing; char *bodylimit, *rest; char *d1p, *d2p; char *ret, *end; unsigned int mime; str sdp_body, cnt_disp; int session_num; struct hdr_field hf; bodylimit = mixed_body->s + mixed_body->len; d1p = find_sdp_line_delimiter(mixed_body->s, bodylimit, delimiter); if (d1p == NULL) { LM_ERR("empty multipart content\n"); return -1; } d2p = d1p; session_num = 0; for(;;) { /* Per-application iteration */ d1p = d2p; if (d1p == NULL || d1p >= bodylimit) break; /* No applications left */ d2p = find_next_sdp_line_delimiter(d1p, bodylimit, delimiter, bodylimit); /* d2p is text limit for application parsing */ memset(&hf,0, sizeof(struct hdr_field)); rest = eat_line(d1p + delimiter.len + 2, d2p - d1p - delimiter.len - 2); if ( rest > d2p ) { LM_ERR("Unparsable <%.*s>\n", (int)(d2p-d1p), d1p); return -1; } no_eoh_found = 1; start_parsing = 0; /*LM_DBG("we need to parse this: <%.*s>\n", d2p-rest, rest); */ while( rest>16)==TYPE_ALL) { LM_ERR("invalid mime with wildcard '*' in Content-Type hdr!\n"); return -1; } //LM_DBG("HDR_CONTENTTYPE_T: %d:%d %p-> <%.*s:%.*s>\n",mime&0x00ff,mime>>16, // hf.name.s,hf.name.len,hf.name.s,hf.body.len,hf.body.s); if (((((unsigned int)mime)>>16) == TYPE_APPLICATION) && ((mime&0x00ff) == SUBTYPE_SDP)) { /*LM_DBG("start_parsing: %d:%d\n",mime&0x00ff,mime>>16); */ start_parsing = 1; } break; case HDR_CONTENTDISPOSITION_T: cnt_disp.s = hf.body.s; cnt_disp.len = hf.body.len; break; case HDR_ERROR_T: return -1; break; default: LM_DBG("unknown header: <%.*s:%.*s>\n",hf.name.len,hf.name.s,hf.body.len,hf.body.s); } } /* end of while */ /* and now we need to parse the content */ if (start_parsing) { sdp_body.s = rest; sdp_body.len = d2p-rest; /* LM_DBG("we need to check session %d: <%.*s>\n", session_num, sdp_body.len, sdp_body.s); */ res = parse_sdp_session(&sdp_body, session_num, &cnt_disp, _sdp); if (res != 0) { /* _sdp is freed by the calling function */ return -1; } session_num++; } } return 0; } /** * Parse SDP. * * returns 0 on success. * non zero on error. */ int parse_sdp(struct sip_msg* _m) { int res; str body, mp_delimiter; int mime; if (_m->sdp) { return 0; /* Already parsed */ } if (get_body(_m, &body)!=0 || body.len==0) { LM_DBG("message body has length zero\n"); return 1; } mime = parse_content_type_hdr(_m); if (mime <= 0) { return -1; } switch (((unsigned int)mime)>>16) { case TYPE_APPLICATION: /* LM_DBG("TYPE_APPLICATION: %d\n",((unsigned int)mime)>>16); */ switch (mime&0x00ff) { case SUBTYPE_SDP: /* LM_DBG("SUBTYPE_SDP: %d\n",mime&0x00ff); */ if (new_sdp(_m) < 0) { LM_ERR("Can't create sdp\n"); return -1; } res = parse_sdp_session(&body, 0, NULL, _m->sdp); if (res != 0) { LM_DBG("free_sdp\n"); free_sdp((sdp_info_t**)(void*)&(_m->sdp)); } return res; break; default: LM_DBG("TYPE_APPLICATION: unknown %d\n",mime&0x00ff); return -1; } break; case TYPE_MULTIPART: /* LM_DBG("TYPE_MULTIPART: %d\n",((unsigned int)mime)>>16); */ switch (mime&0x00ff) { case SUBTYPE_MIXED: /* LM_DBG("SUBTYPE_MIXED: %d <%.*s>\n",mime&0x00ff,_m->content_type->body.len,_m->content_type->body.s); */ if(get_mixed_part_delimiter(&(_m->content_type->body),&mp_delimiter) > 0) { /*LM_DBG("got delimiter: <%.*s>\n",mp_delimiter.len,mp_delimiter.s); */ if (new_sdp(_m) < 0) { LM_ERR("Can't create sdp\n"); return -1; } res = parse_mixed_content(&body, mp_delimiter, _m->sdp); if (res != 0) { LM_DBG("free_sdp\n"); free_sdp((sdp_info_t**)(void*)&(_m->sdp)); } return res; } else { return -1; } break; default: LM_DBG("TYPE_MULTIPART: unknown %d\n",mime&0x00ff); return -1; } break; default: LM_DBG("%d\n",((unsigned int)mime)>>16); return -1; } LM_CRIT("We should not see this!\n"); return res; } /** * Free all memory. */ void free_sdp(sdp_info_t** sdp) { __free_sdp(*sdp); pkg_free(*sdp); *sdp = NULL; } void __free_sdp(sdp_info_t* sdp) { sdp_session_cell_t *session, *l_session; sdp_stream_cell_t *stream, *l_stream; sdp_payload_attr_t *payload, *l_payload; LM_DBG("sdp = %p\n", sdp); if (sdp == NULL) return; LM_DBG("sdp = %p\n", sdp); session = sdp->sessions; LM_DBG("session = %p\n", session); while (session) { l_session = session; session = session->next; stream = l_session->streams; while (stream) { l_stream = stream; stream = stream->next; payload = l_stream->payload_attr; while (payload) { l_payload = payload; payload = payload->next; pkg_free(l_payload); } if (l_stream->p_payload_attr) { pkg_free(l_stream->p_payload_attr); } pkg_free(l_stream); } pkg_free(l_session); } } void print_sdp_stream(sdp_stream_cell_t *stream, int level) { sdp_payload_attr_t *payload; LM_GEN1(level, "....stream[%d]:%p=>%p {%p} '%.*s' '%.*s:%.*s:%.*s' '%.*s' [%d] '%.*s' '%.*s:%.*s' (%d)=>%p '%.*s' '%.*s' '%.*s' '%.*s' '%.*s' '%.*s'\n", stream->stream_num, stream, stream->next, stream->p_payload_attr, stream->media.len, stream->media.s, stream->ip_addr.len, stream->ip_addr.s, stream->port.len, stream->port.s, stream->rtcp_port.len, stream->rtcp_port.s, stream->transport.len, stream->transport.s, stream->is_rtp, stream->payloads.len, stream->payloads.s, stream->bw_type.len, stream->bw_type.s, stream->bw_width.len, stream->bw_width.s, stream->payloads_num, stream->payload_attr, stream->sendrecv_mode.len, stream->sendrecv_mode.s, stream->ptime.len, stream->ptime.s, stream->path.len, stream->path.s, stream->max_size.len, stream->max_size.s, stream->accept_types.len, stream->accept_types.s, stream->accept_wrapped_types.len, stream->accept_wrapped_types.s); payload = stream->payload_attr; while (payload) { LM_GEN1(level, "......payload[%d]:%p=>%p p_payload_attr[%d]:%p '%.*s' '%.*s' '%.*s' '%.*s' '%.*s'\n", payload->payload_num, payload, payload->next, payload->payload_num, stream->p_payload_attr[payload->payload_num], payload->rtp_payload.len, payload->rtp_payload.s, payload->rtp_enc.len, payload->rtp_enc.s, payload->rtp_clock.len, payload->rtp_clock.s, payload->rtp_params.len, payload->rtp_params.s, payload->fmtp_string.len, payload->fmtp_string.s); payload=payload->next; } } void print_sdp_session(sdp_session_cell_t *session, int level) { sdp_stream_cell_t *stream = session==NULL ? NULL : session->streams; if (session==NULL) { LM_ERR("NULL session\n"); return; } LM_GEN1(level, "..session[%d]:%p=>%p '%.*s' '%.*s' '%.*s' '%.*s:%.*s' (%d)=>%p\n", session->session_num, session, session->next, session->cnt_disp.len, session->cnt_disp.s, session->ip_addr.len, session->ip_addr.s, session->o_ip_addr.len, session->o_ip_addr.s, session->bw_type.len, session->bw_type.s, session->bw_width.len, session->bw_width.s, session->streams_num, session->streams); while (stream) { print_sdp_stream(stream, level); stream=stream->next; } } void print_sdp(sdp_info_t* sdp, int level) { sdp_session_cell_t *session; LM_GEN1(level, "sdp:%p=>%p (%d:%d)\n", sdp, sdp->sessions, sdp->sessions_num, sdp->streams_num); session = sdp->sessions; while (session) { print_sdp_session(session, level); session = session->next; } } /* * Free cloned stream. */ void free_cloned_sdp_stream(sdp_stream_cell_t *_stream) { sdp_stream_cell_t *stream, *l_stream; sdp_payload_attr_t *payload, *l_payload; stream = _stream; while (stream) { l_stream = stream; stream = stream->next; payload = l_stream->payload_attr; while (payload) { l_payload = payload; payload = payload->next; shm_free(l_payload); } if (l_stream->p_payload_attr) { shm_free(l_stream->p_payload_attr); } shm_free(l_stream); } } /* * Free cloned session. */ void free_cloned_sdp_session(sdp_session_cell_t *_session) { sdp_session_cell_t *session, *l_session; session = _session; while (session) { l_session = session; session = l_session->next; free_cloned_sdp_stream(l_session->streams); shm_free(l_session); } } void free_cloned_sdp(sdp_info_t* sdp) { free_cloned_sdp_session(sdp->sessions); shm_free(sdp); } sdp_payload_attr_t * clone_sdp_payload_attr(sdp_payload_attr_t *attr) { sdp_payload_attr_t * clone_attr; int len; char *p; if (attr == NULL) { LM_ERR("arg:NULL\n"); return NULL; } len = sizeof(sdp_payload_attr_t) + attr->rtp_payload.len + attr->rtp_enc.len + attr->rtp_clock.len + attr->rtp_params.len + attr->fmtp_string.len; clone_attr = (sdp_payload_attr_t*)shm_malloc(len); if (clone_attr == NULL) { LM_ERR("no more shm mem (%d)\n",len); return NULL; } memset( clone_attr, 0, len); p = (char*)(clone_attr+1); clone_attr->payload_num = attr->payload_num; if (attr->rtp_payload.len) { clone_attr->rtp_payload.s = p; clone_attr->rtp_payload.len = attr->rtp_payload.len; memcpy( p, attr->rtp_payload.s, attr->rtp_payload.len); p += attr->rtp_payload.len; } if (attr->rtp_enc.len) { clone_attr->rtp_enc.s = p; clone_attr->rtp_enc.len = attr->rtp_enc.len; memcpy( p, attr->rtp_enc.s, attr->rtp_enc.len); p += attr->rtp_enc.len; } if (attr->rtp_clock.len) { clone_attr->rtp_clock.s = p; clone_attr->rtp_clock.len = attr->rtp_clock.len; memcpy( p, attr->rtp_clock.s, attr->rtp_clock.len); p += attr->rtp_clock.len; } if (attr->rtp_params.len) { clone_attr->rtp_params.s = p; clone_attr->rtp_params.len = attr->rtp_params.len; memcpy( p, attr->rtp_params.s, attr->rtp_params.len); p += attr->rtp_params.len; } if (attr->fmtp_string.len) { clone_attr->fmtp_string.s = p; clone_attr->fmtp_string.len = attr->fmtp_string.len; memcpy( p, attr->fmtp_string.s, attr->fmtp_string.len); p += attr->fmtp_string.len; } return clone_attr; } sdp_stream_cell_t * clone_sdp_stream_cell(sdp_stream_cell_t *stream) { sdp_stream_cell_t *clone_stream; sdp_payload_attr_t *clone_payload_attr, *payload_attr; int len, i; char *p; if (stream == NULL) { LM_ERR("arg:NULL\n"); return NULL; } /* NOTE: we are not cloning RFC4975 attributes */ len = sizeof(sdp_stream_cell_t) + stream->ip_addr.len + stream->media.len + stream->port.len + stream->transport.len + stream->sendrecv_mode.len + stream->ptime.len + stream->payloads.len + stream->bw_type.len + stream->bw_width.len + stream->rtcp_port.len; clone_stream = (sdp_stream_cell_t*)shm_malloc(len); if (clone_stream == NULL) { LM_ERR("no more shm mem (%d)\n",len); return NULL; } memset( clone_stream, 0, len); p = (char*)(clone_stream+1); payload_attr = NULL; for (i=0;ipayloads_num;i++) { clone_payload_attr = clone_sdp_payload_attr(stream->p_payload_attr[i]); if (clone_payload_attr == NULL) { LM_ERR("unable to clone attributes for payload[%d]\n", i); goto error; } clone_payload_attr->next = payload_attr; payload_attr = clone_payload_attr; } clone_stream->payload_attr = payload_attr; clone_stream->payloads_num = stream->payloads_num; if (clone_stream->payloads_num) { if (NULL == init_p_payload_attr(clone_stream, USE_SHM_MEM)) { goto error; } } clone_stream->stream_num = stream->stream_num; clone_stream->pf = stream->pf; if (stream->ip_addr.len) { clone_stream->ip_addr.s = p; clone_stream->ip_addr.len = stream->ip_addr.len; memcpy( p, stream->ip_addr.s, stream->ip_addr.len); p += stream->ip_addr.len; } clone_stream->is_rtp = stream->is_rtp; if (stream->media.len) { clone_stream->media.s = p; clone_stream->media.len = stream->media.len; memcpy( p, stream->media.s, stream->media.len); p += stream->media.len; } if (stream->port.len) { clone_stream->port.s = p; clone_stream->port.len = stream->port.len; memcpy( p, stream->port.s, stream->port.len); p += stream->port.len; } if (stream->transport.len) { clone_stream->transport.s = p; clone_stream->transport.len = stream->transport.len; memcpy( p, stream->transport.s, stream->transport.len); p += stream->transport.len; } if (stream->sendrecv_mode.len) { clone_stream->sendrecv_mode.s = p; clone_stream->sendrecv_mode.len = stream->sendrecv_mode.len; memcpy( p, stream->sendrecv_mode.s, stream->sendrecv_mode.len); p += stream->sendrecv_mode.len; } if (stream->ptime.len) { clone_stream->ptime.s = p; clone_stream->ptime.len = stream->ptime.len; memcpy( p, stream->ptime.s, stream->ptime.len); p += stream->ptime.len; } if (stream->payloads.len) { clone_stream->payloads.s = p; clone_stream->payloads.len = stream->payloads.len; memcpy( p, stream->payloads.s, stream->payloads.len); p += stream->payloads.len; } if (stream->bw_type.len) { clone_stream->bw_type.s = p; clone_stream->bw_type.len = stream->bw_type.len; p += stream->bw_type.len; } if (stream->bw_width.len) { clone_stream->bw_width.s = p; clone_stream->bw_width.len = stream->bw_width.len; p += stream->bw_width.len; } if (stream->rtcp_port.len) { clone_stream->rtcp_port.s = p; clone_stream->rtcp_port.len = stream->rtcp_port.len; memcpy( p, stream->rtcp_port.s, stream->rtcp_port.len); p += stream->rtcp_port.len; } /* NOTE: we are not cloning RFC4975 attributes: * - path * - max_size * - accept_types * - accept_wrapped_types */ return clone_stream; error: free_cloned_sdp_stream(clone_stream); return NULL; } sdp_session_cell_t * clone_sdp_session_cell(sdp_session_cell_t *session) { sdp_session_cell_t *clone_session; sdp_stream_cell_t *clone_stream, *prev_clone_stream, *stream; int len, i; char *p; if (session == NULL) { LM_ERR("arg:NULL\n"); return NULL; } len = sizeof(sdp_session_cell_t) + session->cnt_disp.len + session->ip_addr.len + session->o_ip_addr.len + session->bw_type.len + session->bw_width.len; clone_session = (sdp_session_cell_t*)shm_malloc(len); if (clone_session == NULL) { LM_ERR("no more shm mem (%d)\n",len); return NULL; } memset( clone_session, 0, len); p = (char*)(clone_session+1); if (session->streams_num) { stream=session->streams; clone_stream=clone_sdp_stream_cell(stream); if (clone_stream==NULL) { goto error; } clone_session->streams=clone_stream; prev_clone_stream=clone_stream; stream=stream->next; for (i=1;istreams_num;i++) { clone_stream=clone_sdp_stream_cell(stream); if (clone_stream==NULL) { goto error; } prev_clone_stream->next=clone_stream; prev_clone_stream=clone_stream; stream=stream->next; } } clone_session->session_num = session->session_num; clone_session->pf = session->pf; clone_session->o_pf = session->o_pf; clone_session->streams_num = session->streams_num; if (session->cnt_disp.len) { clone_session->cnt_disp.s = p; clone_session->cnt_disp.len = session->cnt_disp.len; memcpy( p, session->cnt_disp.s, session->cnt_disp.len); p += session->cnt_disp.len; } if (session->ip_addr.len) { clone_session->ip_addr.s = p; clone_session->ip_addr.len = session->ip_addr.len; memcpy( p, session->ip_addr.s, session->ip_addr.len); p += session->ip_addr.len; } if (session->o_ip_addr.len) { clone_session->o_ip_addr.s = p; clone_session->o_ip_addr.len = session->o_ip_addr.len; memcpy( p, session->o_ip_addr.s, session->o_ip_addr.len); p += session->o_ip_addr.len; } if (session->bw_type.len) { clone_session->bw_type.s = p; clone_session->bw_type.len = session->bw_type.len; memcpy( p, session->bw_type.s, session->bw_type.len); p += session->bw_type.len; } if (session->bw_width.len) { clone_session->bw_width.s = p; clone_session->bw_width.len = session->bw_width.len; memcpy( p, session->bw_width.s, session->bw_width.len); p += session->bw_width.len; } return clone_session; error: free_cloned_sdp_session(clone_session); return NULL; } sdp_info_t * clone_sdp_info(struct sip_msg* _m) { sdp_info_t *clone_sdp_info, *sdp_info=_m->sdp; sdp_session_cell_t *clone_session, *prev_clone_session, *session; int i, len; if (sdp_info==NULL) { LM_ERR("no sdp to clone\n"); return NULL; } if (sdp_info->sessions_num == 0) { LM_ERR("no sessions to clone\n"); return NULL; } len = sizeof(sdp_info_t); clone_sdp_info = (sdp_info_t*)shm_malloc(len); if (clone_sdp_info == NULL) { LM_ERR("no more shm mem (%d)\n",len); return NULL; } LM_DBG("clone_sdp_info: %p\n", clone_sdp_info); memset( clone_sdp_info, 0, len); LM_DBG("we have %d sessions\n", sdp_info->sessions_num); clone_sdp_info->sessions_num = sdp_info->sessions_num; clone_sdp_info->streams_num = sdp_info->streams_num; session=sdp_info->sessions; clone_session=clone_sdp_session_cell(session); if (clone_session==NULL) { goto error; } clone_sdp_info->sessions=clone_session; prev_clone_session=clone_session; session=session->next; for (i=1;isessions_num;i++) { clone_session=clone_sdp_session_cell(session); if (clone_session==NULL) { goto error; } prev_clone_session->next=clone_session; prev_clone_session=clone_session; session=session->next; } return clone_sdp_info; error: free_cloned_sdp(clone_sdp_info); return NULL; } opensips-2.2.2/parser/sdp/sdp.h000066400000000000000000000135671300170765700164200ustar00rootroot00000000000000/* * SDP parser interface * * Copyright (C) 2008 SOMA Networks, INC. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * HISTORY: * -------- * 2007-09-09 osas: ported and enhanced sdp parsing functions from nathelper module * 2008-04-22 osas: integrated RFC4975 attributes - patch provided by Denis Bilenko (denik) * */ #ifndef SDP_H #define SDP_H #include "../msg_parser.h" typedef struct sdp_payload_attr { struct sdp_payload_attr *next; /**< payload index inside stream */ int payload_num; str rtp_payload; str rtp_enc; str rtp_clock; str rtp_params; str fmtp_string; } sdp_payload_attr_t; typedef struct sdp_stream_cell { struct sdp_stream_cell *next; /* c=
*/ /**< connection address family: AF_INET/AF_INET6 */ int pf; /**< connection address */ str ip_addr; /**< stream index inside a session */ int stream_num; /**< flag indicating if this is an RTP stream */ int is_rtp; /**< flag indicating if this stream is on hold */ int is_on_hold; /* m= */ str media; str port; str transport; str sendrecv_mode; str ptime; str payloads; /**< number of payloads inside a stream */ int payloads_num; /* b=: */ /**< alphanumeric modifier giving the meaning of the figure: CT - conference total; AS - application specific */ str bw_type; /**< The is interpreted as kilobits per second by default */ str bw_width; /* RFC3605: Real Time Control Protocol (RTCP) attribute in * Session Description Protocol (SDP) */ /* a=rtcp: port [nettype space addrtype space connection-address] CRLF */ /**< RFC3605: rtcp attribute */ str rtcp_port; /**< RFC4975: path attribute */ str path; /**< RFC4975: max-size attribute */ str max_size; /**< RFC4975: accept-types attribute */ str accept_types; /**< RFC4975: accept-wrapped-types attribute */ str accept_wrapped_types; /**< fast access pointers to payloads */ struct sdp_payload_attr **p_payload_attr; struct sdp_payload_attr *payload_attr; } sdp_stream_cell_t; typedef struct sdp_session_cell { struct sdp_session_cell *next; /**< session index inside sdp */ int session_num; /**< the Content-Disposition header (for Content-Type:multipart/mixed) */ str cnt_disp; /* c=
*/ /**< connection address family: AF_INET/AF_INET6 */ int pf; /**< connection address */ str ip_addr; /* o= */ /**< origin address family: AF_INET/AF_INET6 */ int o_pf; /**< origin address */ str o_ip_addr; /* b=: */ /**< alphanumeric modifier giving the meaning of the figure: CT - conference total; AS - application specific */ str bw_type; /**< The is interpreted as kilobits per second by default */ str bw_width; /**< number of streams inside a session */ int streams_num; struct sdp_stream_cell* streams; } sdp_session_cell_t; /** * Here we hold the head of the parsed sdp structure */ typedef struct sdp_info { int sessions_num; /**< number of SDP sessions */ int streams_num; /**< total number of streams for all SDP sessions */ struct sdp_session_cell *sessions; } sdp_info_t; /* * Parse SDP. */ int parse_sdp(struct sip_msg* _m); int parse_sdp_session(str *sdp_body, int session_num, str *cnt_disp, sdp_info_t* _sdp); /** * Get number of sessions in existing SDP. */ int get_sdp_session_num(struct sip_msg* _m); /** * Get number of streams in existing SDP. */ int get_sdp_stream_num(struct sip_msg* _m); /** * Get a session for the current sip msg based on position inside SDP. */ sdp_session_cell_t* get_sdp_session(struct sip_msg* _m, int session_num); /** * Get a session for the given sdp based on position inside SDP. */ sdp_session_cell_t* get_sdp_session_sdp(struct sdp_info* sdp, int session_num); /** * Get a stream for the current sip msg based on positions inside SDP. */ sdp_stream_cell_t* get_sdp_stream(struct sip_msg* _m, int session_num, int stream_num); /** * Get a stream for the given sdp based on positions inside SDP. */ sdp_stream_cell_t* get_sdp_stream_sdp(struct sdp_info* sdp, int session_num, int stream_num); /** * Get a payload from a stream based on payload. */ sdp_payload_attr_t* get_sdp_payload4payload(sdp_stream_cell_t *stream, str *rtp_payload); /** * Get a payload from a stream based on position. */ sdp_payload_attr_t* get_sdp_payload4index(sdp_stream_cell_t *stream, int index); /** * Free all memory associated with parsed structure. * * Note: this will free up the parsed sdp structure (form PKG_MEM). */ void free_sdp(sdp_info_t** sdp); void __free_sdp(sdp_info_t* sdp); /** * Print the content of the given sdp_info structure. * * Note: only for debug purposes. */ void print_sdp(sdp_info_t* sdp, int log_level); /** * Print the content of the given sdp_session structure. * * Note: only for debug purposes. */ void print_sdp_session(sdp_session_cell_t* sdp_session, int log_level); /** * Print the content of the given sdp_stream structure. * * Note: only for debug purposes. */ void print_sdp_stream(sdp_stream_cell_t *stream, int log_level); #endif /* SDP_H */ opensips-2.2.2/parser/sdp/sdp_cloner.h000066400000000000000000000032611300170765700177500ustar00rootroot00000000000000/* * SDP parser interface * * Copyright (C) 2008 SOMA Networks, INC. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * HISTORY: * -------- * 2007-09-09 osas: ported and enhanced sdp parsing functions from nathelper module * */ #ifndef SDP_CLONER_H #define SDP_CLONER_H #include "sdp.h" /** * Clone the given sdp_session_cell structure. */ sdp_session_cell_t * clone_sdp_session_cell(sdp_session_cell_t *session); /** * Free all memory associated with the cloned sdp_session structure. * * Note: this will free up the parsed sdp structure (form SHM_MEM). */ void free_cloned_sdp_session(sdp_session_cell_t *_session); /** * Clone the given sdp_info structure. * * Note: all cloned structer will be in SHM_MEM. */ sdp_info_t* clone_sdp_info(struct sip_msg* _m); /** * Free all memory associated with the cloned sdp_info structure. * * Note: this will free up the parsed sdp structure (form SHM_MEM). */ void free_cloned_sdp(sdp_info_t* sdp); #endif /* SDP_H */ opensips-2.2.2/parser/sdp/sdp_helpr_funcs.c000066400000000000000000000445201300170765700207740ustar00rootroot00000000000000/* * SDP parser helpers * * Copyright (C) 2008 SOMA Networks, INC. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2007-09-09 ported helper functions from nathelper module (osas) * 2008-04-22 integrated RFC4975 attributes - patch provided by Denis Bilenko (denik) * */ #include "../../ut.h" #include "../msg_parser.h" #include "../parser_f.h" #include "../parse_hname2.h" static struct { const char *s; int len; int is_rtp; } sup_ptypes[] = { {.s = "rtp/avp", .len = 7, .is_rtp = 1}, {.s = "udptl", .len = 5, .is_rtp = 0}, {.s = "rtp/avpf", .len = 8, .is_rtp = 1}, {.s = "rtp/savp", .len = 8, .is_rtp = 1}, {.s = "rtp/savpf", .len = 9, .is_rtp = 1}, {.s = "udp", .len = 3, .is_rtp = 0}, {.s = "udp/bfcp", .len = 8, .is_rtp = 0}, {.s = NULL, .len = 0, .is_rtp = 0} }; #define READ(val) \ (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)) #define advance(_ptr,_n,_str,_error) \ do{\ if ((_ptr)+(_n)>(_str).s+(_str).len)\ goto _error;\ (_ptr) = (_ptr) + (_n);\ }while(0); #define one_of_16( _x , _t ) \ (_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\ ||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14]) #define one_of_8( _x , _t ) \ (_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ||_x==_t[5]||_x==_t[6]) /* * ser_memmem() returns the location of the first occurrence of data * pattern b2 of size len2 in memory block b1 of size len1 or * NULL if none is found. Obtained from NetBSD. */ static void * ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2) { /* Initialize search pointer */ char *sp = (char *) b1; /* Initialize pattern pointer */ char *pp = (char *) b2; /* Initialize end of search address space pointer */ char *eos = sp + len1 - len2; /* Sanity check */ if(!(b1 && b2 && len1 && len2)) return NULL; while (sp <= eos) { if (*sp == *pp) if (memcmp(sp, pp, len2) == 0) return sp; sp++; } return NULL; } int get_mixed_part_delimiter(str* body, str *mp_delimiter) { static unsigned int boun[16] = { 0x6e756f62,0x4e756f62,0x6e556f62,0x4e556f62, 0x6e754f62,0x4e754f62,0x6e554f62,0x4e554f62, 0x6e756f42,0x4e756f42,0x6e556f42,0x4e556f42, 0x6e754f42,0x4e754f42,0x6e554f42,0x4e554f42}; static unsigned int dary[16] = { 0x79726164,0x59726164,0x79526164,0x59526164, 0x79724164,0x59724164,0x79524164,0x59524164, 0x79726144,0x59726144,0x79526144,0x59526144, 0x79724144,0x59724144,0x79524144,0x59524144}; str str_type; unsigned int x; char *p; /* LM_DBG("<%.*s>\n",body->len,body->s); */ p = str_type.s = body->s; str_type.len = body->len; while (*p!=';' && p<(body->s+body->len)) advance(p,1,str_type,error); p++; str_type.s = p; str_type.len = body->len - (p - body->s); /* LM_DBG("string to parse: <%.*s>\n",str_type.len,str_type.s); */ /* skip spaces and tabs if any */ while (*p==' ' || *p=='\t') advance(p,1,str_type,error); advance(p,4,str_type,error); x = READ(p-4); if (!one_of_16(x,boun)) goto other; advance(p,4,str_type,error); x = READ(p-4); if (!one_of_16(x,dary)) goto other; /* skip spaces and tabs if any */ while (*p==' ' || *p=='\t') advance(p,1,str_type,error); if (*p!='=') { LM_ERR("parse error: no = found after boundary field\n"); goto error; } advance(p,1,str_type,error); while ((*p==' ' || *p=='\t') && p+1len = str_type.len - (int)(p-str_type.s); mp_delimiter->s = p; return 1; error: return -1; other: LM_DBG("'boundary' parsing error\n"); return -1; } int extract_rtpmap(str *body, str *rtpmap_payload, str *rtpmap_encoding, str *rtpmap_clockrate, str *rtpmap_parmas) { char *cp, *cp1; int len; if (strncasecmp(body->s, "a=rtpmap:", 9) !=0) { /*LM_DBG("We are not pointing to an a=rtpmap: attribute =>`%.*s'\n", body->len, body->s); */ return -1; } cp1 = body->s; rtpmap_payload->s = cp1 + 9; /* skip `a=rtpmap:' */ rtpmap_payload->len = eat_line(rtpmap_payload->s, body->s + body->len - rtpmap_payload->s) - rtpmap_payload->s; trim_len(rtpmap_payload->len, rtpmap_payload->s, *rtpmap_payload); len = rtpmap_payload->len; /* */ cp = eat_token_end(rtpmap_payload->s, rtpmap_payload->s + rtpmap_payload->len); rtpmap_payload->len = cp - rtpmap_payload->s; if (rtpmap_payload->len <= 0 || cp == rtpmap_payload->s) { LM_ERR("no encoding in `a=rtpmap'\n"); return -1; } len -= rtpmap_payload->len; rtpmap_encoding->s = cp; cp = eat_space_end(rtpmap_encoding->s, rtpmap_encoding->s + len); len -= cp - rtpmap_encoding->s; if (len <= 0 || cp == rtpmap_encoding->s) { LM_ERR("no encoding in `a=rtpmap:'\n"); return -1; } rtpmap_encoding->s = cp; cp1 = (char*)ser_memmem(cp, "/", len, 1); len -= cp1 - cp; if (cp1==NULL || len <= 1 || cp == cp1) { LM_ERR("invalid encoding in `a=rtpmap'\n"); return -1; } rtpmap_encoding->len = cp1 - cp; /* skip the '/' char */ cp = cp1+1; len--; cp1 = (char*)ser_memmem(cp, "/", len, 1); if (cp1 == NULL) { rtpmap_clockrate->s = cp; rtpmap_clockrate->len = len; rtpmap_parmas->s = NULL; rtpmap_parmas->len = 0; } else { rtpmap_clockrate->s = cp; rtpmap_clockrate->len = cp1-cp; len -= cp1 - cp; if (len <= 1) { LM_ERR("invalid encoding in `a=rtpmap:'\n"); return -1; } rtpmap_parmas->s = cp1 + 1; rtpmap_parmas->len = len - 1; } return 0; } int extract_fmtp( str *body, str *fmtp_payload, str *fmtp_string ) { char *cp, *cp1; int len; if (strncasecmp(body->s, "a=fmtp:", 7) !=0) { /*LM_DBG("We are not pointing to an a=fmtp: attribute =>`%.*s'\n", body->len, body->s); */ return -1; } cp1 = body->s; fmtp_payload->s = cp1 + 7; /* skip `a=fmtp:' */ fmtp_payload->len = eat_line(fmtp_payload->s, body->s + body->len - fmtp_payload->s) - fmtp_payload->s; trim_len(fmtp_payload->len, fmtp_payload->s, *fmtp_payload); len = fmtp_payload->len; /* */ cp = eat_token_end(fmtp_payload->s, fmtp_payload->s + fmtp_payload->len); fmtp_payload->len = cp - fmtp_payload->s; if (fmtp_payload->len <= 0 || cp == fmtp_payload->s) { LM_ERR("no encoding in `a=fmtp:'\n"); return -1; } len -= fmtp_payload->len; fmtp_string->s = cp; cp = eat_space_end(fmtp_string->s, fmtp_string->s + len); len -= cp - fmtp_string->s; if (len <= 0 || cp == fmtp_string->s) { LM_ERR("no encoding in `a=fmtp:'\n"); return -1; } fmtp_string->s = cp; fmtp_string->len = eat_line(fmtp_string->s, body->s + body->len - fmtp_string->s) - fmtp_string->s; trim_len(fmtp_string->len, fmtp_string->s, *fmtp_string); return 0; } /* generic method for attribute extraction * field must has format "a=attrname:" */ int extract_field(str *body, str *value, str field) { if (strncmp(body->s, field.s, field.len < body->len ? field.len : body->len) !=0) { /*LM_DBG("We are not pointing to an %.* attribute =>`%.*s'\n", field.len, field.s, body->len, body->s); */ return -1; } value->s = body->s + field.len; /* skip `a=attrname:' */ value->len = eat_line(value->s, body->s + body->len - value->s) - value->s; trim_len(value->len, value->s, *value); return 0; } int extract_ptime(str *body, str *ptime) { static const str field = str_init("a=ptime:"); return extract_field(body, ptime, field); } int extract_accept_types(str *body, str *accept_types) { static const str field = str_init("a=accept-types:"); return extract_field(body, accept_types, field); } int extract_accept_wrapped_types(str *body, str *accept_wrapped_types) { static const str field = str_init("a=accept-wrapped-types:"); return extract_field(body, accept_wrapped_types, field); } int extract_max_size(str *body, str *max_size) { static const str field = str_init("a=max-size:"); return extract_field(body, max_size, field); } int extract_path(str *body, str *path) { static const str field = str_init("a=path:"); return extract_field(body, path, field); } int extract_rtcp(str *body, str *rtcp) { static const str field = str_init("a=rtcp:"); return extract_field(body, rtcp, field); } int extract_sendrecv_mode(str *body, str *sendrecv_mode, int *is_on_hold) { char *cp1; cp1 = body->s; if ( !( (strncasecmp(cp1, "a=sendrecv", 10) == 0) || (strncasecmp(cp1, "a=recvonly", 10) == 0))) { if ( !( (strncasecmp(cp1, "a=inactive", 10) == 0) || (strncasecmp(cp1, "a=sendonly", 10) == 0) )) { return -1; } else { *is_on_hold = 1; } return -1; } sendrecv_mode->s = body->s + 2; /* skip `a=' */ sendrecv_mode->len = 8; /* we know the length and therefore we don't need to overkill */ /* sendrecv_mode->len = eat_line(sendrecv_mode->s, body->s + body->len - sendrecv_mode->s) - sendrecv_mode->s; trim_len(sendrecv_mode->len, sendrecv_mode->s, *sendrecv_mode); */ return 0; } int extract_bwidth(str *body, str *bwtype, str *bwwitdth) { char *cp, *cp1; int len; cp1 = NULL; for (cp = body->s; (len = body->s + body->len - cp) > 0;) { cp1 = (char*)ser_memmem(cp, "b=", len, 2); if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r') break; cp = cp1 + 2; } if (cp1 == NULL) return -1; bwtype->s = cp1 + 2; bwtype->len = eat_line(bwtype->s, body->s + body->len - bwtype->s) - bwtype->s; trim_len(bwtype->len, bwtype->s, *bwtype); cp = bwtype->s; len = bwtype->len; cp1 = (char*)ser_memmem(cp, ":", len, 1); len -= cp1 - cp; if (len <= 0) { LM_ERR("invalid encoding in `b=%.*s'\n", bwtype->len, bwtype->s); return -1; } bwtype->len = cp1 - cp; /* skip ':' */ bwwitdth->s = cp1 + 1; bwwitdth->len = len - 1; return 0; } int extract_mediaip(str *body, str *mediaip, int *pf, char *line) { char *cp, *cp1; int len, nextisip; cp1 = NULL; for (cp = body->s; (len = body->s + body->len - cp) > 0;) { cp1 = (char*)ser_memmem(cp, line, len, 2); if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r') break; cp = cp1 + 2; } if (cp1 == NULL) return -1; mediaip->s = cp1 + 2; mediaip->len = eat_line(mediaip->s, body->s + body->len - mediaip->s) - mediaip->s; trim_len(mediaip->len, mediaip->s, *mediaip); nextisip = 0; for (cp = mediaip->s; cp < mediaip->s + mediaip->len;) { len = eat_token_end(cp, mediaip->s + mediaip->len) - cp; if (nextisip == 1) { mediaip->s = cp; mediaip->len = len; nextisip++; break; } if (len == 3 && memcmp(cp, "IP", 2) == 0) { switch (cp[2]) { case '4': nextisip = 1; *pf = AF_INET; break; case '6': nextisip = 1; *pf = AF_INET6; break; default: break; } } /* consume all spaces starting from the second char after the token First char after the token is the char that stoped the token parsing, so it is space or \r / \n, so we simply skip it */ cp = eat_space_end(cp + len + 1, mediaip->s + mediaip->len); } if (nextisip != 2 || mediaip->len == 0) { LM_ERR("no `IP[4|6]' in `%s' field\n",line); return -1; } return 1; } int extract_media_attr(str *body, str *mediamedia, str *mediaport, str *mediatransport, str *mediapayload, int *is_rtp) { char *cp, *cp1; int len, i; cp1 = NULL; for (cp = body->s; (len = body->s + body->len - cp) > 0;) { cp1 = (char*)ser_memmem(cp, "m=", len, 2); if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r') break; cp = cp1 + 2; } if (cp1 == NULL) { LM_ERR("no `m=' in SDP\n"); return -1; } mediaport->s = cp1 + 2; /* skip `m=' */ mediaport->len = eat_line(mediaport->s, body->s + body->len - mediaport->s) - mediaport->s; trim_len(mediaport->len, mediaport->s, *mediaport); mediapayload->len = mediaport->len; mediamedia->s = mediaport->s; /* Skip media supertype and spaces after it */ cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len); mediaport->len -= cp - mediaport->s; mediamedia->len = mediapayload->len - mediaport->len; if (mediaport->len <= 0 || cp == mediaport->s) { LM_ERR("no port in `m='\n"); return -1; } mediaport->s = cp; cp = eat_space_end(mediaport->s, mediaport->s + mediaport->len); mediaport->len -= cp - mediaport->s; if (mediaport->len <= 0 || cp == mediaport->s) { LM_ERR("no port in `m='\n"); return -1; } /* Extract port */ mediaport->s = cp; cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len); mediatransport->len = mediaport->len - (cp - mediaport->s); if (mediatransport->len <= 0 || cp == mediaport->s) { LM_ERR("no port in `m='\n"); return -1; } mediatransport->s = cp; mediaport->len = cp - mediaport->s; /* Skip spaces after port */ cp = eat_space_end(mediatransport->s, mediatransport->s + mediatransport->len); mediatransport->len -= cp - mediatransport->s; if (mediatransport->len <= 0 || cp == mediatransport->s) { LM_ERR("no protocol type in `m='\n"); return -1; } /* Extract protocol type */ mediatransport->s = cp; cp = eat_token_end(mediatransport->s, mediatransport->s + mediatransport->len); if (cp == mediatransport->s) { LM_ERR("no protocol type in `m='\n"); return -1; } mediatransport->len = cp - mediatransport->s; mediapayload->s = mediatransport->s + mediatransport->len; mediapayload->len -= mediapayload->s - mediamedia->s; cp = eat_space_end(mediapayload->s, mediapayload->s + mediapayload->len); mediapayload->len -= cp - mediapayload->s; mediapayload->s = cp; for (i = 0; sup_ptypes[i].s != NULL; i++) if (mediatransport->len == sup_ptypes[i].len && strncasecmp(mediatransport->s, sup_ptypes[i].s, mediatransport->len) == 0) { *is_rtp = sup_ptypes[i].is_rtp; return 0; } /* Unproxyable protocol type. Generally it isn't error. */ return 0; } /* * Auxiliary for some functions. * Returns pointer to first character of found line, or NULL if no such line. */ char *find_sdp_line(char* p, char* plimit, char linechar) { static char linehead[3] = "x="; char *cp, *cp1; linehead[0] = linechar; /* Iterate through body */ cp = p; for (;;) { if (cp >= plimit) return NULL; cp1 = ser_memmem(cp, linehead, plimit-cp, 2); if (cp1 == NULL) return NULL; /* * As it is body, we assume it has previous line and we can * lookup previous character. */ if (cp1[-1] == '\n' || cp1[-1] == '\r') return cp1; /* * Having such data, but not at line beginning. * Skip them and reiterate. ser_memmem() will find next * occurence. */ if (plimit - cp1 < 2) return NULL; cp = cp1 + 2; } } /* * Auxiliary for some functions. * Returns pointer to first character of found line, or NULL if no such line. */ char *find_sdp_line_complex(char* p, char* plimit, char * name) { char *cp, *cp1; int len = strlen(name); /* Iterate through body */ cp = p; for (;;) { if (cp >= plimit) return NULL; cp1 = ser_memmem(cp, name, plimit-cp, len); if (cp1 == NULL) return NULL; /* * As it is body, we assume it has previous line and we can * lookup previous character. */ if (cp1[-1] == '\n' || cp1[-1] == '\r') return cp1; /* * Having such data, but not at line beginning. * Skip them and reiterate. ser_memmem() will find next * occurence. */ if (plimit - cp1 < 2) return NULL; cp = cp1 + 2; } } /* This function assumes p points to a line of requested type. */ char * find_next_sdp_line(char* p, char* plimit, char linechar, char* defptr) { char *t; if (p >= plimit || plimit - p < 3) return defptr; t = find_sdp_line(p + 2, plimit, linechar); return t ? t : defptr; } /* returns pointer to next header line, and fill hdr_f ; * if at end of header returns pointer to the last crlf (always buf)*/ char* get_sdp_hdr_field(char* buf, char* end, struct hdr_field* hdr) { char* tmp; char *match; if ((*buf)=='\n' || (*buf)=='\r'){ /* double crlf or lflf or crcr */ hdr->type=HDR_EOH_T; return buf; } tmp=parse_hname2(buf, end, hdr); if (hdr->type==HDR_ERROR_T){ LM_ERR("bad header\n"); goto error; } /* eliminate leading whitespace */ tmp=eat_lws_end(tmp, end); if (tmp>=end) { LM_ERR("hf empty\n"); goto error; } /* if header-field well-known, parse it, find its end otherwise ; * after leaving the hdr->type switch, tmp should be set to the * next header field */ switch(hdr->type){ case HDR_CONTENTTYPE_T: case HDR_CONTENTDISPOSITION_T: /* just skip over it */ hdr->body.s=tmp; /* find end of header */ /* find lf */ do{ match=q_memchr(tmp, '\n', end-tmp); if (match){ match++; }else { LM_ERR("bad body for <%s>(%d)\n", hdr->name.s, hdr->type); tmp=end; goto error; } tmp=match; }while( matchbody.len=match-hdr->body.s; break; default: LM_CRIT("unknown header type %d\n", hdr->type); goto error; } /* jku: if \r covered by current length, shrink it */ trim_r( hdr->body ); hdr->len=tmp-hdr->name.s; return tmp; error: LM_DBG("error exit\n"); hdr->type=HDR_ERROR_T; hdr->len=tmp-hdr->name.s; return tmp; } char *find_sdp_line_delimiter(char* p, char* plimit, str delimiter) { static char delimiterhead[3] = "--"; char *cp, *cp1; /* Iterate through body */ cp = p; for (;;) { if (cp >= plimit) return NULL; for(;;) { cp1 = ser_memmem(cp, delimiterhead, plimit-cp, 2); if (cp1 == NULL) return NULL; /* We matched '--', * now let's match the boundary delimiter */ if (strncmp(cp1+2, delimiter.s, delimiter.len) == 0) break; else cp = cp1 + 2 + delimiter.len; if (cp >= plimit) return NULL; } if (cp1[-1] == '\n' || cp1[-1] == '\r') return cp1; if (plimit - cp1 < 2 + delimiter.len) return NULL; cp = cp1 + 2 + delimiter.len; } } /* * This function assumes p points to a delimiter type line. */ char *find_next_sdp_line_delimiter(char* p, char* plimit, str delimiter, char* defptr) { char *t; if (p >= plimit || plimit - p < 3) return defptr; t = find_sdp_line_delimiter(p + 2, plimit, delimiter); return t ? t : defptr; } opensips-2.2.2/parser/sdp/sdp_helpr_funcs.h000066400000000000000000000051501300170765700207750ustar00rootroot00000000000000/* * SDP parser helpers * * Copyright (C) 2008 SOMA Networks, INC. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2007-09-09 ported helper functions from nathelper module (osas) * 2008-04-22 integrated RFC4975 attributes - patch provided by Denis Bilenko (denik) * */ #ifndef _SDP_HLPR_FUNCS_H #define _SDP_HLPR_FUNCS_H #include "../../str.h" #include "../msg_parser.h" /** * Detect the mixed part delimiter. * * Example: "boundary1" * Content-Type: multipart/mixed; boundary="boundary1" */ int get_mixed_part_delimiter(str * body, str * mp_delimiter); int extract_field(str *body, str *value, str field); int extract_rtpmap(str *body, str *rtpmap_payload, str *rtpmap_encoding, str *rtpmap_clockrate, str *rtpmap_parmas); int extract_fmtp( str *body, str *fmtp_payload, str *fmtp_string ); int extract_ptime(str *body, str *ptime); int extract_sendrecv_mode(str *body, str *sendrecv_mode, int *is_on_hold); int extract_mediaip(str *body, str *mediaip, int *pf, char *line); int extract_media_attr(str *body, str *mediamedia, str *mediaport, str *mediatransport, str *mediapayload, int *is_rtp); int extract_bwidth(str *body, str *bwtype, str *bwwitdth); /* RFC3605 attributes */ int extract_rtcp(str *body, str *rtcp); /* RFC4975 attributes */ int extract_accept_types(str *body, str *accept_types); int extract_accept_wrapped_types(str *body, str *accept_wrapped_types); int extract_max_size(str *body, str *max_size); int extract_path(str *body, str *path); char *find_sdp_line(char *p, char *plimit, char linechar); char *find_next_sdp_line(char *p, char *plimit, char linechar, char *defptr); char *find_sdp_line_complex(char* p, char* plimit, char * name); char* get_sdp_hdr_field(char* , char* , struct hdr_field* ); char *find_sdp_line_delimiter(char *p, char *plimit, str delimiter); char *find_next_sdp_line_delimiter(char *p, char *plimit, str delimiter, char *defptr); #endif opensips-2.2.2/poll_types.h000066400000000000000000000030321300170765700157240ustar00rootroot00000000000000/* * Copyright (C) 2005 iptelorg GmbH * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief IO wait poll methods (enum, strings, related function) * see \ref io_wait.h for more details */ #ifndef _poll_types_h #define _poll_types_h enum poll_types { POLL_NONE, POLL_POLL, POLL_EPOLL_LT, POLL_EPOLL_ET, POLL_SIGIO_RT, POLL_SELECT, POLL_KQUEUE, POLL_DEVPOLL, POLL_END}; /* all the function and vars are defined in io_wait.c */ extern char* poll_method_str[POLL_END]; extern char* poll_support; enum poll_types choose_poll_method(); /* returns 0 on success, and an error message on error */ char* check_poll_method(enum poll_types poll_method); char* poll_method_name(enum poll_types poll_method); enum poll_types get_poll_type(char* s); void fix_poll_method( enum poll_types *poll_method ); #endif opensips-2.2.2/prime_hash.c000066400000000000000000000133451300170765700156540ustar00rootroot00000000000000/* * Copyright (C) 2007 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief * Functions for determinung a pseudo random number over a message's * header field, based on CRC32 or a prime number algorithm. */ #include "sr_module.h" #include "parser/parse_uri.h" #include "parser/parse_to.h" #include "parser/parse_from.h" #include "crc.h" #include #include "prime_hash.h" static int determine_source(struct sip_msg *msg, enum hash_source source, str *source_string); static int validate_msg(struct sip_msg * msg); static int determine_call_id (struct sip_msg *msg, str *source_string); static int determine_fromto_uri (struct to_body *fromto, str *source_string); static int determine_fromto_user (struct to_body *fromto, str *source_string); static int first_token (str *source_string); int hash_func (struct sip_msg * msg, enum hash_source source, int denominator) { int ret; unsigned int hash; str source_string; if(determine_source (msg, source, &source_string) == -1) { return -1; } crc32_uint(&source_string, &hash); ret = hash % denominator; LM_DBG("hash: %u %% %i = %i\n", hash, denominator, ret); return ret; } int prime_hash_func(struct sip_msg * msg, enum hash_source source, int denominator) { str source_string; if(source != shs_from_user && source != shs_to_user) { LM_ERR("chosen hash source not usable (may contain letters)\n"); return -1; } if (determine_source (msg, source, &source_string) == -1) { return -1; } static const int INT_DIGIT_LIMIT = 18; static const int PRIME_NUMBER = 51797; uint64_t number = 0; uint64_t p10; int i, j, limit = 0; int ret; char source_number_s[INT_DIGIT_LIMIT + 1]; i = INT_DIGIT_LIMIT - 1; j = source_string.len - 1; source_number_s[INT_DIGIT_LIMIT] ='\0'; while(i >= 0 && j >= 0) { if(isdigit(source_string.s[j])) { source_number_s[i] = source_string.s[j]; i--; } j--; } limit = i; for(i=INT_DIGIT_LIMIT - 1, p10=1; i>limit; i--, p10=p10*10) { number += (source_number_s[i] - '0') * p10; } LM_DBG("source_string is %.*s, source_number_s " "is: %s, number is %llu\n", source_string.len, source_string.s, source_number_s + (limit + 1), (long long unsigned int)number); ret = number % PRIME_NUMBER; ret = ret % denominator + 1; LM_DBG("calculated hash is: %i\n", ret); return ret; } static int determine_source (struct sip_msg *msg, enum hash_source source, str *source_string) { source_string->s = NULL; source_string->len = 0; if(validate_msg(msg) < 0) { return -1; } switch (source) { case shs_call_id: return determine_call_id (msg, source_string); case shs_from_uri: return determine_fromto_uri (get_from(msg), source_string); case shs_from_user: return determine_fromto_user (get_from(msg), source_string); case shs_to_uri: return determine_fromto_uri (get_to(msg), source_string); case shs_to_user: return determine_fromto_user (get_to(msg), source_string); default: LM_ERR("unknown hash source %i.\n", (int) source); return -1; } } static int validate_msg(struct sip_msg * msg) { if(!msg->callid && ((parse_headers(msg, HDR_CALLID_F, 0) == -1) || !msg->callid)) { LM_ERR("Message has no Call-ID header\n"); return -1; } if(!msg->to && ((parse_headers(msg, HDR_TO_F, 0) == -1) || !msg->to)) { LM_ERR("Message has no To header\n"); return -1; } if(!msg->from && ((parse_headers(msg, HDR_FROM_F, 0) == -1) || !msg->from)) { LM_ERR("Message has no From header\n"); return -1; } //TODO it would make more sense to do the parsing just if its needed // but parse_from_header is smart enough, so its probably not a huge problem if (parse_from_header(msg) < 0) { LM_ERR("Error while parsing From header field\n"); return -1; } return 0; } static int determine_call_id (struct sip_msg *msg, str *source_string) { source_string->s = msg->callid->body.s; source_string->len = msg->callid->body.len; first_token (source_string); return 0; } static int determine_fromto_uri (struct to_body *fromto, str *source_string) { if (fromto == NULL) { LM_ERR("fromto is NULL!\n"); return -1; } source_string->s = fromto->uri.s; source_string->len = fromto->uri.len; return 0; } static int determine_fromto_user (struct to_body *fromto, str *source_string) { struct sip_uri uri; if (fromto == NULL) { LM_ERR("fromto is NULL!\n"); return -1; } if (parse_uri (fromto->uri.s, fromto->uri.len, &uri) < 0) { LM_ERR("Failed to parse From or To URI.\n"); return -1; } source_string->s = uri.user.s; source_string->len = uri.user.len; return 0; } static int first_token (str *source_string) { size_t len; if (source_string->s == NULL || source_string->len == 0) { return 0; } while (source_string->len > 0 && isspace (*source_string->s)) { ++source_string->s; --source_string->len; } for (len = 0; len < source_string->len; ++len) { if (isspace (source_string->s[len])) { source_string->len = len; break; } } return 0; } opensips-2.2.2/prime_hash.h000066400000000000000000000045041300170765700156560ustar00rootroot00000000000000/* * Copyright (C) 2007 1&1 Internet AG * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief * Functions for determinung a pseudo random number over a message's * header field, based on CRC32 or a prime number algorithm. */ #ifndef PRIME_HASH_H #define PRIME_HASH_H 1 #include "parser/msg_parser.h" /*! * \brief * Determines from which part of a message the hash shall be calculated. * Possible values are: * * - \b shs_call_id the content of the Call-ID header field * - \b shs_from_uri the entire URI in the From header field * - \b shs_from_user the username part of the URI in the From header field * - \b shs_to_uri the entire URI in the To header field * - \b shs_to_user the username part of the URI in the To header field * - \b shs_error no hash specified */ enum hash_source { shs_call_id = 1, shs_from_uri, shs_from_user, shs_to_uri, shs_to_user, shs_error }; /*! generic interface for hash functions */ typedef int (*hash_func_t)(struct sip_msg * msg, enum hash_source source, int denominator); /*! * \brief CRC32 hash function * Returns an integer number between 0 and denominator - 1 based on * the hash source from the msg. The hash algorith is CRC32. */ int hash_func (struct sip_msg * msg, enum hash_source source, int denominator); /*! * \brief prime hash function * Returns an integer number between 0 and denominator - 1 based on * the hash source from the msg. Use the prime number algorithm. */ int prime_hash_func (struct sip_msg * msg, enum hash_source source, int denominator); #endif opensips-2.2.2/proxy.c000066400000000000000000000201151300170765700147070ustar00rootroot00000000000000/* * proxy list & assoc. functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-02-13 all *proxy functions are now proto aware (andrei) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2007-01-25 support for DNS failover added into proxy structure; * new shm functions for copy/free added (bogdan) */ #include "config.h" #include "globals.h" #include "proxy.h" #include "error.h" #include "dprint.h" #include "mem/mem.h" #include "mem/shm_mem.h" #include #include #include #ifdef DNS_IP_HACK #include "ut.h" #endif #include "resolve.h" #include "ip_addr.h" #include "globals.h" struct proxy_l* proxies=0; int disable_dns_failover=0; int hostent_shm_cpy(struct hostent *dst, struct hostent* src) { int i; char *p; for( i=0 ; src->h_addr_list[i] ; i++ ); dst->h_addr_list = (char**)shm_malloc (i * (src->h_length + sizeof(char*)) + sizeof(char*)); if (dst->h_addr_list==NULL) return -1; p = ((char*)dst->h_addr_list) + (i+1)*sizeof(char*); dst->h_addr_list[i] = 0; for( i-- ; i>=0 ; i-- ) { dst->h_addr_list[i] = p; memcpy( dst->h_addr_list[i], src->h_addr_list[i], src->h_length ); p += src->h_length; } dst->h_addr = src->h_addr_list[0]; dst->h_addrtype = src->h_addrtype; dst->h_length = src->h_length; return 0; } void free_shm_hostent(struct hostent *dst) { if (dst->h_addr_list) shm_free(dst->h_addr_list); } /* copies a hostent structure*, returns 0 on success, <0 on error*/ int hostent_cpy(struct hostent *dst, struct hostent* src) { unsigned int len,len2, i, r; int ret; /* start copying the host entry.. */ /* copy h_name */ len=strlen(src->h_name)+1; dst->h_name=(char*)pkg_malloc(sizeof(char) * len); if (dst->h_name) strncpy(dst->h_name,src->h_name, len); else{ ser_error=ret=E_OUT_OF_MEM; goto error; } /* copy h_aliases */ len=0; if (src->h_aliases) for (;src->h_aliases[len];len++); dst->h_aliases=(char**)pkg_malloc(sizeof(char*)*(len+1)); if (dst->h_aliases==0){ ser_error=ret=E_OUT_OF_MEM; pkg_free(dst->h_name); goto error; } memset((void*)dst->h_aliases, 0, sizeof(char*) * (len+1) ); for (i=0;ih_aliases[i])+1; dst->h_aliases[i]=(char*)pkg_malloc(sizeof(char)*len2); if (dst->h_aliases[i]==0){ ser_error=ret=E_OUT_OF_MEM; pkg_free(dst->h_name); for(r=0; rh_aliases[r]); pkg_free(dst->h_aliases); goto error; } strncpy(dst->h_aliases[i], src->h_aliases[i], len2); } /* copy h_addr_list */ len=0; if (src->h_addr_list) for (;src->h_addr_list[len];len++); dst->h_addr_list=(char**)pkg_malloc(sizeof(char*)*(len+1)); if (dst->h_addr_list==0){ ser_error=ret=E_OUT_OF_MEM; pkg_free(dst->h_name); for(r=0; dst->h_aliases[r]; r++) pkg_free(dst->h_aliases[r]); pkg_free(dst->h_aliases); goto error; } memset((void*)dst->h_addr_list, 0, sizeof(char*) * (len+1) ); for (i=0;ih_addr_list[i]=(char*)pkg_malloc(sizeof(char)*src->h_length); if (dst->h_addr_list[i]==0){ ser_error=ret=E_OUT_OF_MEM; pkg_free(dst->h_name); for(r=0; dst->h_aliases[r]; r++) pkg_free(dst->h_aliases[r]); pkg_free(dst->h_aliases); for (r=0; rh_addr_list[r]); pkg_free(dst->h_addr_list); goto error; } memcpy(dst->h_addr_list[i], src->h_addr_list[i], src->h_length); } /* copy h_addr_type & length */ dst->h_addrtype=src->h_addrtype; dst->h_length=src->h_length; /*finished hostent copy */ return 0; error: LM_CRIT("pkg memory allocation failure\n"); return ret; } void free_hostent(struct hostent *dst) { int r; if (dst->h_name) pkg_free(dst->h_name); if (dst->h_aliases){ for(r=0; dst->h_aliases[r]; r++) { pkg_free(dst->h_aliases[r]); } pkg_free(dst->h_aliases); } if (dst->h_addr_list){ for (r=0; dst->h_addr_list[r];r++) { pkg_free(dst->h_addr_list[r]); } pkg_free(dst->h_addr_list); } } /* Creates a proxy structure out of the host, port and proto * uses also SRV if possible & port==0 (quick hack) */ struct proxy_l* mk_proxy(str* name, unsigned short port, unsigned short proto, int is_sips) { struct proxy_l* p; struct hostent* he; p=(struct proxy_l*) pkg_malloc(sizeof(struct proxy_l)); if (p==0){ ser_error=E_OUT_OF_MEM; LM_CRIT("pkg memory allocation failure\n"); goto error; } memset(p,0,sizeof(struct proxy_l)); p->name=*name; p->port=port; p->proto=proto; LM_DBG("doing DNS lookup...\n"); he = sip_resolvehost(name, &(p->port), &p->proto, is_sips, disable_dns_failover?0:&p->dn ); if (he==0){ ser_error=E_BAD_ADDRESS; LM_CRIT("could not resolve hostname: \"%.*s\"\n", name->len, name->s); pkg_free(p); goto error; } if (hostent_cpy(&(p->host), he)!=0){ free_dns_res( p ); pkg_free(p); goto error; } return p; error: return 0; } /* same as mk_proxy, but in shared memory * uses also SRV if possible & port==0 (quick hack) */ struct proxy_l* mk_proxy_from_ip(struct ip_addr* ip, unsigned short port, unsigned short proto) { struct proxy_l* p; p=(struct proxy_l*) pkg_malloc(sizeof(struct proxy_l)); if (p==0){ LM_CRIT("pkg memory allocation failure\n"); goto error; } memset(p,0,sizeof(struct proxy_l)); p->port=port; p->proto=proto; p->host.h_addrtype=ip->af; p->host.h_length=ip->len; p->host.h_addr_list=pkg_malloc(2*sizeof(char*)); if (p->host.h_addr_list==0) goto error; p->host.h_addr_list[1]=0; p->host.h_addr_list[0]=pkg_malloc(ip->len+1); if (p->host.h_addr_list[0]==0){ pkg_free(p->host.h_addr_list); goto error; } memcpy(p->host.h_addr_list[0], ip->u.addr, ip->len); p->host.h_addr_list[0][ip->len]=0; return p; error: return 0; } void free_proxy(struct proxy_l* p) { if (p) { free_hostent(&p->host); free_dns_res( p ); } } void free_shm_proxy(struct proxy_l* p) { if (p) { free_shm_hostent(&p->host); free_dns_res(p); } } /* same as add_proxy, but it doesn't add the proxy to the list * uses also SRV if possible & port==0 (quick hack) works in shared memory */ struct proxy_l* mk_shm_proxy(str* name, unsigned short port, unsigned short proto, int is_sips) { struct proxy_l* p; struct hostent* he; p=(struct proxy_l*) shm_malloc(sizeof(struct proxy_l)); if (p==0){ ser_error=E_OUT_OF_MEM; LM_CRIT("shm memory allocation failure\n"); goto error; } memset(p,0,sizeof(struct proxy_l)); p->name=*name; p->port=port; p->proto=proto; LM_DBG("doing DNS lookup...\n"); he = sip_resolvehost(name, &(p->port), &p->proto, is_sips, disable_dns_failover?0:&p->dn ); if (he==0){ ser_error=E_BAD_ADDRESS; LM_CRIT("could not resolve hostname: \"%.*s\"\n", name->len, name->s); shm_free(p); goto error; } if (hostent_shm_cpy(&(p->host), he)!=0){ free_dns_res( p ); shm_free(p); goto error; } return p; error: return 0; } /* clones a proxy into pkg memory */ struct proxy_l* clone_proxy(struct proxy_l *sp) { struct proxy_l *dp; dp = (struct proxy_l*)pkg_malloc(sizeof(struct proxy_l)); if (dp==NULL) { LM_ERR("no more pkg memory\n"); return 0; } memset( dp , 0 , sizeof(struct proxy_l)); dp->port = sp->port; dp->proto = sp->proto; dp->addr_idx = sp->addr_idx; /* clone the hostent */ if (hostent_cpy( &dp->host, &sp->host)!=0) goto error0; /* clone the dns resolver */ if (sp->dn) { dp->dn = dns_res_copy(sp->dn); if (dp->dn==NULL) goto error1; } return dp; error1: free_hostent(&dp->host); error0: pkg_free(dp); return 0; } opensips-2.2.2/proxy.h000066400000000000000000000042251300170765700147200ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-02-13 added proto to struct proxy_l & to *_proxy functions (andrei) * 2007-01-25 support for DNS failover added into proxy structure; * unused members removed (bogdan) */ #ifndef _core_proxy_h #define _core_proxy_h #include #include "ip_addr.h" #include "str.h" struct dns_node; #define PROXY_SHM_FLAG (1<<0) struct proxy_l{ str name; /* original name */ unsigned short flags; unsigned short port; unsigned short proto; unsigned short addr_idx; /* crt. addr. idx. */ struct hostent host; /* addresses */ /* tree with the DNS resolving status * NOTE: this is all the time in SHM */ struct dns_node *dn; }; extern struct proxy_l* proxies; struct proxy_l* mk_proxy( str* name, unsigned short port, unsigned short proto, int is_sips); struct proxy_l* mk_shm_proxy(str* name, unsigned short port, unsigned short proto, int is_sips); struct proxy_l* mk_proxy_from_ip(struct ip_addr* ip, unsigned short port, unsigned short proto); void free_proxy(struct proxy_l* p); void free_shm_proxy(struct proxy_l* p); void free_hostent(struct hostent *dst); int hostent_cpy(struct hostent *dst, struct hostent* src); int hostent_shm_cpy(struct hostent *dst, struct hostent* src); void free_shm_hostent(struct hostent *dst); struct proxy_l* clone_proxy(struct proxy_l *sp); #include "resolve.h" #endif opensips-2.2.2/pt.c000066400000000000000000000113141300170765700141520ustar00rootroot00000000000000/* * Copyright (C) 2007 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-06-07 - created to contain process handling functions (bogdan) */ #include #include #include #include "mem/shm_mem.h" #include "net/net_tcp.h" #include "net/net_udp.h" #include "socket_info.h" #include "sr_module.h" #include "dprint.h" #include "pt.h" #include "bin_interface.h" /* array with children pids, 0= main proc, * alloc'ed in shared mem if possible */ struct process_table *pt=0; /* variable keeping the number of created processes READONLY!! */ unsigned int counted_processes = 0; int init_multi_proc_support(void) { unsigned short proc_no; unsigned int i; proc_no = 0; /* UDP based listeners */ proc_no += udp_count_processes(); /* TCP based listeners */ proc_no += tcp_count_processes(); /* attendent */ proc_no++; /* info packet UDP receivers */ /* timer processes */ proc_no += 3 /* timer keeper + timer trigger + dedicated */; /* count the processes requested by modules */ proc_no += count_module_procs(); /* allocate the PID table */ pt = shm_malloc(sizeof(struct process_table)*proc_no); if (pt==0){ LM_ERR("out of memory\n"); return -1; } memset(pt, 0, sizeof(struct process_table)*proc_no); for( i=0 ; inext) { if (m->exports->procs==NULL) continue; for (i=0;m->exports->procs[i].name;i++) { if (!m->exports->procs[i].no || !m->exports->procs[i].function) continue; if (!flags || (m->exports->procs[i].flags & flags)) ret+=m->exports->procs[i].no; } } LM_DBG("%d children are going to be inited\n",ret); return ret; } int id_of_pid(pid_t pid) { int i; for (i = 0; i < counted_processes; i++) if (pt[i].pid == pid) return i; return -1; } opensips-2.2.2/pt.h000066400000000000000000000034611300170765700141630ustar00rootroot00000000000000/* * Process Table * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-15 added tcp_disable support (andrei) */ #ifndef _PT_H #define _PT_H #include #include struct stat_var_; #define MAX_PT_DESC 128 struct process_table { int pid; int unix_sock; /* unix socket on which tcp main listens */ int idx; /* tcp child index, -1 for other processes */ char desc[MAX_PT_DESC]; int default_log_level; /* used when resetting the log level */ int log_level; /* logging level of this process */ struct stat_var_ *load; }; typedef void(*forked_proc_func)(int i); extern struct process_table *pt; extern int process_no; extern unsigned int counted_processes; int init_multi_proc_support(); void set_proc_attrs( char *fmt, ...); pid_t internal_fork(char *proc_desc); int count_init_children(int flags); /* @return: -1 or the index of the given process */ int id_of_pid(pid_t pid); /* return processes pid */ inline static int my_pid(void) { return pt ? pt[process_no].pid : getpid(); } #endif opensips-2.2.2/pvar.c000066400000000000000000003335151300170765700145110ustar00rootroot00000000000000/** * Copyright (C) 2010-2016 OpenSIPS Solutions * Copyright (C) 2005-2009 Voice Sistem SRL * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-10-20 - added header name specifier (ramona) * 2005-06-14 - added avp name specifier (ramona) * 2005-06-18 - added color printing support via escape sequesnces * contributed by Ingo Flaschberger (daniel) * 2005-06-22 - created this file from modules/xlog/pv_lib.c (daniel) * 2009-04-28 - $ct and $ct.fields() PVs added (bogdan) * 2009-05-02 - $branch() added, $br, $bR, $bf, $bF removed (bogdan) */ #include #include #include #include #include #include "dprint.h" #include "mem/mem.h" #include "mem/shm_mem.h" #include "ut.h" #include "trim.h" #include "dset.h" #include "action.h" #include "socket_info.h" #include "route_struct.h" #include "usr_avp.h" #include "errinfo.h" #include "transformations.h" #include "script_var.h" #include "pvar.h" #include "xlog.h" #include "parser/parse_from.h" #include "parser/parse_uri.h" #include "parser/parse_hname2.h" #include "parser/parse_content.h" #include "parser/parse_refer_to.h" #include "parser/parse_rpid.h" #include "parser/parse_diversion.h" #include "parser/parse_ppi.h" #include "parser/parse_pai.h" #include "parser/digest/digest.h" #include "parser/contact/parse_contact.h" #define is_in_str(p, in) (ps+in->len && *p) extern int curr_action_line; extern char *curr_action_file; typedef struct _pv_extra { pv_export_t pve; struct _pv_extra *next; } pv_extra_t, *pv_extra_p; pv_extra_p *_pv_extra_list=0; static str str_marker = { PV_MARKER_STR, 1 }; /* IMPORTANT : the "const" strings returned by the var functions must be read-write (as they may be changed by the script interpreter), so we need to allocated as array and not as pointing to RO data segment */ static char _str_null_hlp[7] = {'<','n','u','l','l','>',0}; static str str_null = { _str_null_hlp, 6 }; static char _str_empty_hlp[1] = { 0 }; static str str_empty = { _str_empty_hlp, 0 }; static char _str_request_route_hlp[] = {'r','e','q','u','e','s','t','_','r','o','u','t','e',0}; static str str_request_route = { _str_request_route_hlp, 13 }; static char _str_failure_route_hlp[] = {'f','a','i','l','u','r','e','_','r','o','u','t','e',0}; static str str_failure_route = { _str_failure_route_hlp, 13 }; static char _str_onreply_route_hlp[] = {'o','n','r','e','p','l','y','_','r','o','u','t','e',0}; static str str_onreply_route = { _str_onreply_route_hlp, 13 }; static char _str_branch_route_hlp[] = {'b','r','a','n','c','h','_','r','o','u','t','e',0}; static str str_branch_route = { _str_branch_route_hlp, 12 }; static char _str_error_route_hlp[] = {'e','r','r','o','r','_','r','o','u','t','e',0}; static str str_error_route = { _str_error_route_hlp, 11 }; static char _str_local_route_hlp[] = {'l','o','c','a','l','_','r','o','u','t','e',0}; static str str_local_route = { _str_local_route_hlp, 11 }; static char _str_startup_route_hlp[] = {'s','t','a','r','t','u','p','_','r','o','u','t','e',0}; static str str_startup_route = { _str_startup_route_hlp, 13 }; static char _str_timer_route_hlp[] = {'t','i','m','e','r','_','r','o','u','t','e',0}; static str str_timer_route = { _str_timer_route_hlp, 11 }; static char _str_event_route_hlp[] = {'e','v','e','n','t','_','r','o','u','t','e',0}; static str str_event_route = { _str_event_route_hlp, 11 }; int _pv_pid = 0; #define PV_FIELD_DELIM ", " #define PV_FIELD_DELIM_LEN (sizeof(PV_FIELD_DELIM) - 1) #define PV_LOCAL_BUF_SIZE 511 static char pv_local_buf[PV_LOCAL_BUF_SIZE+1]; /* pv context list */ pv_context_t* pv_context_lst = NULL; pv_context_t* pv_get_context(str* name); pv_context_t* add_pv_context(str* name, pv_contextf_t get_context); static int pvc_before_check = 1; /* route param variable */ static int pv_get_param(struct sip_msg *msg, pv_param_t *ip, pv_value_t *res); static int pv_parse_param_name(pv_spec_p sp, str *in); /********** helper functions ********/ /** * convert unsigned int to pv_value_t */ int pv_get_uintval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, unsigned int uival) { int l = 0; char *ch = NULL; if(res==NULL) return -1; ch = int2str(uival, &l); res->rs.s = ch; res->rs.len = l; res->ri = (int)uival; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } /** * convert signed int to pv_value_t */ int pv_get_sintval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, int sival) { int l = 0; char *ch = NULL; if(res==NULL) return -1; ch = sint2str(sival, &l); res->rs.s = ch; res->rs.len = l; res->ri = sival; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } /** * convert str to pv_value_t */ int pv_get_strval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, str *sval) { if(res==NULL) return -1; res->rs = *sval; res->flags = PV_VAL_STR; return 0; } /** * convert str-int to pv_value_t (type is str) */ int pv_get_strintval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, str *sval, int ival) { if(res==NULL) return -1; res->rs = *sval; res->ri = ival; res->flags = PV_VAL_STR|PV_VAL_INT; return 0; } /** * convert int-str to pv_value_t (type is int) */ int pv_get_intstrval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, int ival, str *sval) { if(res==NULL) return -1; res->rs = *sval; res->ri = ival; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } /************************************************************/ static int pv_get_marker(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { return pv_get_strintval(msg, param, res, &str_marker, (int)str_marker.s[0]); } int pv_get_null(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(res==NULL) return -1; res->rs = str_empty; res->ri = 0; res->flags = PV_VAL_NULL; return 0; } /************************************************************/ static int pv_get_pid(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(_pv_pid == 0) _pv_pid = (int)getpid(); return pv_get_sintval(msg, param, res, _pv_pid); } extern int return_code; static int pv_get_return_code(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { return pv_get_sintval(msg, param, res, return_code); } static int pv_get_times(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; return pv_get_uintval(msg, param, res, (unsigned int)time(NULL)); } static int pv_get_timem(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct timeval TP; if(msg==NULL) return -1; gettimeofday(&TP, NULL); return pv_get_uintval(msg, param, res, (unsigned int)TP.tv_usec); } static int pv_get_start_times(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; return pv_get_uintval(msg, param, res, (unsigned int)startup_time); } static int pv_parse_time_name(pv_spec_p sp, str *in) { sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = AVP_NAME_STR; sp->pvp.pvn.u.isname.name.s.s = pkg_malloc(in->len + 1); if (sp->pvp.pvn.u.isname.name.s.s==NULL) { LM_ERR("failed to allocated private mem\n"); return -1; } memcpy(sp->pvp.pvn.u.isname.name.s.s, in->s, in->len); sp->pvp.pvn.u.isname.name.s.s[in->len] = 0; sp->pvp.pvn.u.isname.name.s.len = in->len; return 0; } static int pv_get_formated_time(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[128]; time_t t; if(msg==NULL) return -1; time( &t ); res->rs.len = strftime( buf, 127, param->pvn.u.isname.name.s.s, localtime( &t ) ); if (res->rs.len<=0) return pv_get_null(msg, param, res); res->rs.s = buf; res->flags = PV_VAL_STR; return 0; } static int pv_get_timef(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { time_t t; str s; if(msg==NULL) return -1; t = time(NULL); s.s = ctime(&t); s.len = strlen(s.s)-1; return pv_get_strintval(msg, param, res, &s, (int)t); } static int pv_get_msgid(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; return pv_get_uintval(msg, param, res, msg->id); } static int pv_get_method(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->first_line.type == SIP_REQUEST) { return pv_get_strintval(msg, param, res, &msg->first_line.u.request.method, (int)msg->first_line.u.request.method_value); } if(msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1) || (msg->cseq==NULL))) { LM_ERR("no CSEQ header\n"); return pv_get_null(msg, param, res); } return pv_get_strintval(msg, param, res, &get_cseq(msg)->method, get_cseq(msg)->method_id); } static int pv_get_status(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->first_line.type != SIP_REPLY) return pv_get_null(msg, param, res); return pv_get_intstrval(msg, param, res, (int)msg->first_line.u.reply.statuscode, &msg->first_line.u.reply.status); } static int pv_get_reason(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->first_line.type != SIP_REPLY) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &msg->first_line.u.reply.reason); } static int pv_get_ruri(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL || res==NULL) return -1; if(msg->first_line.type == SIP_REPLY) /* REPLY doesn't have a ruri */ return pv_get_null(msg, param, res); if(msg->parsed_uri_ok==0 /* R-URI not parsed*/ && parse_sip_msg_uri(msg)<0) { LM_ERR("failed to parse the R-URI\n"); return pv_get_null(msg, param, res); } if (msg->new_uri.s!=NULL) return pv_get_strval(msg, param, res, &msg->new_uri); return pv_get_strval(msg, param, res, &msg->first_line.u.request.uri); } static int pv_get_ru_q(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL || res==NULL) return -1; if(msg->first_line.type == SIP_REPLY) return pv_get_null(msg, param, res); return pv_get_sintval(msg, param, res, get_ruri_q(msg)); } static int pv_get_ouri(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL || res==NULL) return -1; if(msg->first_line.type == SIP_REPLY) /* REPLY doesn't have a ruri */ return pv_get_null(msg, param, res); if(msg->parsed_orig_ruri_ok==0 /* orig R-URI not parsed*/ && parse_orig_ruri(msg)<0) { LM_ERR("failed to parse the R-URI\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &msg->first_line.u.request.uri); } static int pv_get_xuri_attr(struct sip_msg *msg, struct sip_uri *parsed_uri, pv_param_t *param, pv_value_t *res) { unsigned short proto; str proto_s; if(param->pvn.u.isname.name.n==1) /* username */ { if(parsed_uri->user.s==NULL || parsed_uri->user.len<=0) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &parsed_uri->user); } else if(param->pvn.u.isname.name.n==2) /* domain */ { if(parsed_uri->host.s==NULL || parsed_uri->host.len<=0) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &parsed_uri->host); } else if(param->pvn.u.isname.name.n==3) /* port */ { if(parsed_uri->port.s==NULL) return pv_get_uintval(msg, param, res, get_uri_port( parsed_uri, &proto)); return pv_get_strintval(msg, param, res, &parsed_uri->port, (int)parsed_uri->port_no); } else if(param->pvn.u.isname.name.n==4) /* protocol */ { if(parsed_uri->transport_val.s==NULL) { get_uri_port(parsed_uri, &proto); proto_s.s = protos[proto].name; proto_s.len = strlen(proto_s.s); return pv_get_strintval(msg, param, res, &proto_s, (int)proto); } return pv_get_strintval(msg, param, res, &parsed_uri->transport_val, (int)parsed_uri->proto); } LM_ERR("unknown specifier\n"); return pv_get_null(msg, param, res); } static int pv_get_ruri_attr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->first_line.type == SIP_REPLY) /* REPLY doesn't have a ruri */ return pv_get_null(msg, param, res); if(msg->parsed_uri_ok==0 /* R-URI not parsed*/ && parse_sip_msg_uri(msg)<0) { LM_ERR("failed to parse the R-URI\n"); return pv_get_null(msg, param, res); } return pv_get_xuri_attr(msg, &(msg->parsed_uri), param, res); } static int pv_get_ouri_attr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->first_line.type == SIP_REPLY) /* REPLY doesn't have a ruri */ return pv_get_null(msg, param, res); if(msg->parsed_orig_ruri_ok==0 /* orig R-URI not parsed*/ && parse_orig_ruri(msg)<0) { LM_ERR("failed to parse the R-URI\n"); return pv_get_null(msg, param, res); } return pv_get_xuri_attr(msg, &(msg->parsed_orig_ruri), param, res); } static int pv_get_path(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(!msg->path_vec.s) { return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &msg->path_vec); } #define CT_NAME_S "name" #define CT_NAME_LEN (sizeof(CT_NAME_S)-1) #define CT_NAME_ID 1 #define CT_URI_S "uri" #define CT_URI_LEN (sizeof(CT_URI_S)-1) #define CT_URI_ID 2 #define CT_Q_S "q" #define CT_Q_LEN (sizeof(CT_Q_S)-1) #define CT_Q_ID 3 #define CT_EXPIRES_S "expires" #define CT_EXPIRES_LEN (sizeof(CT_EXPIRES_S)-1) #define CT_EXPIRES_ID 4 #define CT_METHODS_S "methods" #define CT_METHODS_LEN (sizeof(CT_METHODS_S)-1) #define CT_METHODS_ID 5 #define CT_RECEIVED_S "received" #define CT_RECEIVED_LEN (sizeof(CT_RECEIVED_S)-1) #define CT_RECEIVED_ID 6 #define CT_PARAMS_S "params" #define CT_PARAMS_LEN (sizeof(CT_PARAMS_S)-1) #define CT_PARAMS_ID 7 int pv_parse_ct_name(pv_spec_p sp, str *in) { if (sp==NULL) return -1; sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = 0; if (in==NULL || in->s==NULL || in->len==0) { sp->pvp.pvn.u.isname.name.n = 0; } else if (in->len==CT_NAME_LEN && strncasecmp(in->s, CT_NAME_S, CT_NAME_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = CT_NAME_ID; } else if (in->len==CT_URI_LEN && strncasecmp(in->s, CT_URI_S, CT_URI_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = CT_URI_ID; } else if (in->len==CT_Q_LEN && strncasecmp(in->s, CT_Q_S, CT_Q_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = CT_Q_ID; } else if (in->len==CT_EXPIRES_LEN && strncasecmp(in->s, CT_EXPIRES_S, CT_EXPIRES_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = CT_EXPIRES_ID; } else if (in->len==CT_METHODS_LEN && strncasecmp(in->s, CT_METHODS_S, CT_METHODS_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = CT_METHODS_ID; } else if (in->len==CT_RECEIVED_LEN && strncasecmp(in->s, CT_RECEIVED_S, CT_RECEIVED_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = CT_RECEIVED_ID; } else if (in->len==CT_PARAMS_LEN && strncasecmp(in->s, CT_PARAMS_S, CT_PARAMS_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = CT_PARAMS_ID; } else { LM_ERR("unsupported CT field <%.*s>\n",in->len,in->s); return -1; } return 0; } static inline int get_contact_body_field(pv_value_t *res,struct hdr_field *cth, contact_t *ct, pv_name_t *pvn) { param_t *p; if (ct==NULL) { /* star contact hdr */ if (pvn->u.isname.name.n==0) { res->rs = cth->body; res->flags = PV_VAL_STR; return 0; } return pv_get_null(NULL, NULL, res); } switch (pvn->u.isname.name.n) { case 0: /* all body */ res->rs.s = ct->name.s?ct->name.s:ct->uri.s; res->rs.len = ct->len; break; case CT_NAME_ID: /* name only */ if (ct->name.s==NULL || ct->name.len==0) return pv_get_null(NULL, NULL, res); res->rs = ct->name; break; case CT_URI_ID: /* uri only */ res->rs = ct->uri; break; case CT_Q_ID: /* Q param only */ if ( !ct->q || !ct->q->body.s || !ct->q->body.len) return pv_get_null(NULL, NULL, res); res->rs = ct->q->body; break; case CT_EXPIRES_ID: /* EXPIRES param only */ if (!ct->expires||!ct->expires->body.s||!ct->expires->body.len) return pv_get_null(NULL, NULL, res); res->rs = ct->expires->body; break; case CT_METHODS_ID: /* METHODS param only */ if (!ct->methods||!ct->methods->body.s||!ct->methods->body.len) return pv_get_null(NULL, NULL, res); res->rs = ct->methods->body; break; case CT_RECEIVED_ID: /* RECEIVED param only */ if(!ct->received||!ct->received->body.s||!ct->received->body.len) return pv_get_null(NULL, NULL, res); res->rs = ct->received->body; break; case CT_PARAMS_ID: /* all param */ if (!ct->params) return pv_get_null(NULL, NULL, res); res->rs.s = ct->params->name.s; for( p=ct->params ; p->next ; p=p->next); res->rs.len = p->name.s + p->len - res->rs.s; break; default: LM_CRIT("BUG - unsupported ID %d\n",pvn->u.isname.type); return pv_get_null(NULL, NULL, res); } res->flags = PV_VAL_STR; return 0; } static int pv_get_contact_body(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct hdr_field *ct_h; contact_body_t *ct_b; contact_t *ct; int idx; int idxf; char *p; if(msg==NULL) return -1; /* get all CONTACT headers */ if(parse_headers(msg, HDR_EOH_F, 0)==-1 || msg->contact==NULL || !msg->contact->body.s || msg->contact->body.len<=0) { LM_DBG("no contact header!\n"); return pv_get_null(msg, param, res); } ct_h = msg->contact; if (parse_contact( ct_h )!=0) { LM_ERR("failed to parse contact hdr\n"); return -1; } ct_b = (contact_body_t*)ct_h->parsed; if (ct_b==NULL) return pv_get_null(msg, param, res); ct = ct_b->contacts; /* get the index */ if(pv_get_spec_index(msg, param, &idx, &idxf)!=0) { LM_ERR("invalid index\n"); return -1; } if( idxf!=PV_IDX_ALL && idx==0) { /* no index specified -> return the first contact body */ return get_contact_body_field( res , ct_h, ct, ¶m->pvn); } if(idxf==PV_IDX_ALL) { /* return all contact bodies */ p = pv_local_buf; do { if(p!=pv_local_buf) { if (p-pv_local_buf+PV_FIELD_DELIM_LEN+1>PV_LOCAL_BUF_SIZE){ LM_ERR("local buffer length exceeded\n"); return pv_get_null(msg, param, res); } memcpy(p, PV_FIELD_DELIM, PV_FIELD_DELIM_LEN); p += PV_FIELD_DELIM_LEN; } get_contact_body_field( res , ct_h, ct, ¶m->pvn); if (p-pv_local_buf+res->rs.len+1>PV_LOCAL_BUF_SIZE) { LM_ERR("local buffer length exceeded!\n"); return pv_get_null(msg, param, res); } memcpy(p, res->rs.s, res->rs.len); p += res->rs.len; ct = ct?ct->next:NULL; while (ct==NULL && ct_h!=NULL) { ct_h = ct_h->sibling; if (ct_h) { if (parse_contact( ct_h )!=0) { LM_ERR("failed to parse contact hdr\n"); return -1; } ct_b = (contact_body_t*)ct_h->parsed; ct = ct_b->contacts; } } } while (ct_h); res->rs.s = pv_local_buf; res->rs.len = p - pv_local_buf; res->flags = PV_VAL_STR; return 0; } /* numerical index */ if (idx<0) { /* index from the end */ idxf=0; while(ct_h) { idxf++; ct = ct?ct->next:NULL; while (ct==NULL && ct_h!=NULL) { ct_h = ct_h->sibling; if (ct_h) { if (parse_contact( ct_h)!=0) { LM_ERR("failed to parse contact hdr\n"); return -1; } ct_b = (contact_body_t*)ct_h->parsed; ct = ct_b->contacts; } } } if (-idx>idxf) return pv_get_null(msg, param, res); idx = idxf +idx; ct_h = msg->contact; ct_b = (contact_body_t*)ct_h->parsed; ct = ct_b->contacts; } while (idx!=0 && ct_h) { /* get to the next contact body */ idx--; ct = ct?ct->next:NULL; while (ct==NULL && ct_h!=NULL) { ct_h = ct_h->sibling; if (ct_h) { if (parse_contact( ct_h )!=0) { LM_ERR("failed to parse contact hdr\n"); return -1; } ct_b = (contact_body_t*)ct_h->parsed; ct = ct_b->contacts; } } } /* nothing found ?*/ if (ct==NULL) return pv_get_null(msg, param, res); /* take the current body */ return get_contact_body_field( res , ct_h, ct, ¶m->pvn); } extern err_info_t _oser_err_info; static int pv_get_errinfo_attr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(param->pvn.u.isname.name.n==0) /* class */ { return pv_get_sintval(msg, param, res, _oser_err_info.eclass); } else if(param->pvn.u.isname.name.n==1) /* level */ { return pv_get_sintval(msg, param, res, _oser_err_info.level); } else if(param->pvn.u.isname.name.n==2) /* info */ { if(_oser_err_info.info.s==NULL) pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &_oser_err_info.info); } else if(param->pvn.u.isname.name.n==3) /* rcode */ { return pv_get_sintval(msg, param, res, _oser_err_info.rcode); } else if(param->pvn.u.isname.name.n==4) /* rreason */ { if(_oser_err_info.rreason.s==NULL) pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &_oser_err_info.rreason); } else { LM_DBG("invalid attribute!\n"); return pv_get_null(msg, param, res); } } static int pv_get_xto_attr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, struct to_body *xto, int type) { struct sip_uri *uri; if(xto==NULL) return -1; if(param->pvn.u.isname.name.n==1) /* uri */ return pv_get_strval(msg, param, res, &xto->uri); if(param->pvn.u.isname.name.n==4) /* tag */ { if (xto->tag_value.s==NULL || xto->tag_value.len<=0) { LM_DBG("no Tag parameter\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &xto->tag_value); } if(param->pvn.u.isname.name.n==5) /* display name */ { if(xto->display.s==NULL || xto->display.len<=0) { LM_DBG("no Display name\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &xto->display); } if(type==0) { if((uri=parse_to_uri(msg))==NULL) { LM_ERR("cannot parse To URI\n"); return pv_get_null(msg, param, res); } } else { if((uri=parse_from_uri(msg))==NULL) { LM_ERR("cannot parse From URI\n"); return pv_get_null(msg, param, res); } } if(param->pvn.u.isname.name.n==2) /* username */ { if(uri->user.s==NULL || uri->user.len<=0) { LM_DBG("no username\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &uri->user); } else if(param->pvn.u.isname.name.n==3) /* domain */ { if(uri->host.s==NULL || uri->host.len<=0) { LM_DBG("no domain\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &uri->host); } LM_ERR("unknown specifier\n"); return pv_get_null(msg, param, res); } static int pv_get_to_attr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->to==NULL && parse_headers(msg, HDR_TO_F, 0)==-1) { LM_ERR("cannot parse To header\n"); return pv_get_null(msg, param, res); } if(msg->to==NULL || get_to(msg)==NULL) { LM_DBG("no To header\n"); return pv_get_null(msg, param, res); } return pv_get_xto_attr(msg, param, res, get_to(msg), 0); } static int pv_get_from_attr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(parse_from_header(msg)<0) { LM_ERR("cannot parse From header\n"); return pv_get_null(msg, param, res); } if(msg->from==NULL || get_from(msg)==NULL) { LM_DBG("no From header\n"); return pv_get_null(msg, param, res); } return pv_get_xto_attr(msg, param, res, get_from(msg), 1); } static int pv_get_cseq(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1) || (msg->cseq==NULL)) ) { LM_ERR("cannot parse CSEQ header\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &(get_cseq(msg)->number)); } static int pv_get_msg_buf(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str s; if(msg==NULL) return -1; s.s = msg->buf; s.len = msg->len; return pv_get_strval(msg, param, res, &s); } static int pv_get_msg_len(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; return pv_get_uintval(msg, param, res, msg->len); } static int pv_get_flags(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str buf; if (!msg) return -1; buf = bitmask_to_flag_list(FLAG_TYPE_MSG, msg->flags); return pv_get_strval(msg, param, res, &buf); } static inline char* int_to_8hex(int val) { unsigned short digit; int i; static char outbuf[9]; outbuf[8] = '\0'; for(i=0; i<8; i++) { if(val!=0) { digit = val & 0x0f; outbuf[7-i] = digit >= 10 ? digit + 'a' - 10 : digit + '0'; val >>= 4; } else outbuf[7-i] = '0'; } return outbuf; } static int pv_get_bflags(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str buf; if (!msg) return -1; buf = bitmask_to_flag_list(FLAG_TYPE_BRANCH, getb0flags(msg)); return pv_get_strval(msg, param, res, &buf); } static int pv_get_callid(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->callid==NULL && ((parse_headers(msg, HDR_CALLID_F, 0)==-1) || (msg->callid==NULL)) ) { LM_ERR("cannot parse Call-Id header\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &msg->callid->body); } static int pv_get_srcip(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str s; if(msg==NULL) return -1; if ( (s.s=ip_addr2a(&msg->rcv.src_ip))==NULL) return pv_get_null(msg, param, res); s.len = strlen(s.s); return pv_get_strval(msg, param, res, &s); } static int pv_get_srcport(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; return pv_get_uintval(msg, param, res, msg->rcv.src_port); } static int pv_get_rcvip(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->rcv.bind_address==NULL || msg->rcv.bind_address->address_str.s==NULL) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &msg->rcv.bind_address->address_str); } static int pv_get_rcvport(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->rcv.bind_address==NULL || msg->rcv.bind_address->port_no_str.s==NULL) return pv_get_null(msg, param, res); return pv_get_intstrval(msg, param, res, (int)msg->rcv.bind_address->port_no, &msg->rcv.bind_address->port_no_str); } static int pv_get_force_sock(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if (msg->force_send_socket==0) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &msg->force_send_socket->sock_str); } static int pv_get_useragent(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->user_agent==NULL && ((parse_headers(msg, HDR_USERAGENT_F, 0)==-1) || (msg->user_agent==NULL))) { LM_DBG("no User-Agent header\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &msg->user_agent->body); } static int pv_get_refer_to(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(parse_refer_to_header(msg)==-1) { LM_DBG("no Refer-To header\n"); return pv_get_null(msg, param, res); } if(msg->refer_to==NULL || get_refer_to(msg)==NULL) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &(get_refer_to(msg)->uri)); } static int pv_get_route_type(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str s; switch(route_type) { case REQUEST_ROUTE: s = str_request_route; break; case FAILURE_ROUTE: s = str_failure_route; break; case ONREPLY_ROUTE: s = str_onreply_route; break; case BRANCH_ROUTE: s = str_branch_route; break; case ERROR_ROUTE: s = str_error_route; break; case LOCAL_ROUTE: s = str_local_route; break; case STARTUP_ROUTE: s = str_startup_route; break; case TIMER_ROUTE: s = str_timer_route; break; case EVENT_ROUTE: s = str_event_route; break; default: s = str_null; } return pv_get_strval(msg, param, res, &s); } static int pv_get_diversion(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str *val; str name; if(msg == NULL) return -1; if(parse_diversion_header(msg) == -1) { LM_DBG("no Diversion header\n"); return pv_get_null(msg, param, res); } if(msg->diversion == NULL || get_diversion(msg) == NULL) { LM_DBG("no Diversion header\n"); return pv_get_null(msg, param, res); } if(param->pvn.u.isname.name.n == 1) { /* uri */ return pv_get_strval(msg, param, res, &(get_diversion(msg)->uri)); } if(param->pvn.u.isname.name.n == 2) { /* reason param */ name.s = "reason"; name.len = 6; val = diversion_param(msg, name); if (val) { return pv_get_strval(msg, param, res, val); } else { return pv_get_null(msg, param, res); } } if(param->pvn.u.isname.name.n == 3) { /* privacy param */ name.s = "privacy"; name.len = 7; val = diversion_param(msg, name); if (val) { return pv_get_strval(msg, param, res, val); } else { return pv_get_null(msg, param, res); } } LM_ERR("unknown diversion specifier\n"); return pv_get_null(msg, param, res); } static int pv_get_rpid(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(parse_rpid_header(msg)==-1) { LM_DBG("no RPID header\n"); return pv_get_null(msg, param, res); } if(msg->rpid==NULL || get_rpid(msg)==NULL) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &(get_rpid(msg)->uri)); } static int pv_get_ppi_attr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct sip_uri *uri; if(msg==NULL) return -1; if(parse_ppi_header(msg) < 0) { LM_DBG("no P-Preferred-Identity header\n"); return pv_get_null(msg, param, res); } if(msg->ppi == NULL || get_ppi(msg) == NULL) { LM_DBG("no P-Preferred-Identity header\n"); return pv_get_null(msg, param, res); } if(param->pvn.u.isname.name.n == 1) { /* uri */ return pv_get_strval(msg, param, res, &(get_ppi(msg)->uri)); } if(param->pvn.u.isname.name.n==4) { /* display name */ if(get_ppi(msg)->display.s == NULL || get_ppi(msg)->display.len <= 0) { LM_DBG("no P-Preferred-Identity display name\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &(get_ppi(msg)->display)); } if((uri=parse_ppi_uri(msg))==NULL) { LM_ERR("cannot parse P-Preferred-Identity URI\n"); return pv_get_null(msg, param, res); } if(param->pvn.u.isname.name.n==2) { /* username */ if(uri->user.s==NULL || uri->user.len<=0) { LM_DBG("no P-Preferred-Identity username\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &uri->user); } else if(param->pvn.u.isname.name.n==3) { /* domain */ if(uri->host.s==NULL || uri->host.len<=0) { LM_DBG("no P-Preferred-Identity domain\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &uri->host); } LM_ERR("unknown specifier\n"); return pv_get_null(msg, param, res); } static int pv_get_pai(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(parse_pai_header(msg)==-1) { LM_DBG("no P-Asserted-Identity header\n"); return pv_get_null(msg, param, res); } if(msg->pai==NULL || get_pai(msg)==NULL) { LM_DBG("no P-Asserted-Identity header\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &(get_pai(msg)->uri)); } /* proto of received message: $pr or $proto*/ static int pv_get_proto(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str s; if(msg==NULL) return -1; if ( msg->rcv.proto>=PROTO_FIRST && msg->rcv.protorcv.proto].id ) { s.s = protos[msg->rcv.proto].name; s.len = strlen(s.s); } else { s = str_null; } return pv_get_strintval(msg, param, res, &s, (int)msg->rcv.proto); } static int pv_get_dset(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str s; if(msg==NULL) return -1; s.s = print_dset(msg, &s.len); if (s.s == NULL) return pv_get_null(msg, param, res); s.len -= CRLF_LEN; return pv_get_strval(msg, param, res, &s); } static int pv_get_dsturi(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if (msg->dst_uri.s == NULL) { LM_DBG("no destination URI\n"); return pv_get_null(msg, param, res); } return pv_get_strval(msg, param, res, &msg->dst_uri); } static int pv_get_dsturi_attr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct sip_uri uri; unsigned short proto; str proto_s; if(msg==NULL) return -1; if (msg->dst_uri.s == NULL) { LM_DBG("no destination URI\n"); return pv_get_null(msg, param, res); } if(parse_uri(msg->dst_uri.s, msg->dst_uri.len, &uri)!=0) { LM_ERR("failed to parse dst uri\n"); return pv_get_null(msg, param, res); } if(param->pvn.u.isname.name.n==1) /* domain */ { if(uri.host.s==NULL || uri.host.len<=0) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &uri.host); } else if(param->pvn.u.isname.name.n==2) /* port */ { if(uri.port.s==NULL) return pv_get_uintval(msg, param, res, get_uri_port(&uri, &proto)); return pv_get_strintval(msg, param, res, &uri.port, (int)uri.port_no); } else if(param->pvn.u.isname.name.n==3) /* proto */ { if(uri.transport_val.s==NULL) { get_uri_port(&uri, &proto); proto_s.s = protos[proto].name; proto_s.len = strlen(proto_s.s); return pv_get_strintval(msg, param, res, &proto_s, (int)proto); } return pv_get_strintval(msg, param, res, &uri.transport_val, (int)uri.proto); } LM_ERR("invalid specifier\n"); return pv_get_null(msg, param, res); } static int pv_get_content_type(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { #define BUFLEN 1024 str s; int idx=-1; int idxf=-1; int distance=0; char buf[BUFLEN]; struct multi_body* multi; struct part* body_part; struct part* neg_index[2]; if(msg==NULL) return -1; if (pv_get_spec_index(msg, param, &idx, &idxf)!=0) { LM_ERR("invalid index\n"); return -1; } /* no index or all contenttypes */ if (param->pvi.type==0 || idxf == PV_IDX_ALL) { if(msg->content_type==NULL && ((parse_headers(msg, HDR_CONTENTTYPE_F, 0)==-1) || (msg->content_type==NULL))) { LM_DBG("no Content-Type header\n"); return pv_get_null(msg, param, res); } /* only the main contenttype requested*/ if (param->pvi.type==0) return pv_get_strval(msg, param, res, &msg->content_type->body); } if ((multi=get_all_bodies(msg)) == 0 || multi->first == 0) { LM_ERR("cannot get multi body\n"); return pv_get_null(msg, param, res); } /* one contenttype request */ if (idxf != PV_IDX_ALL) { if (idx< 0) { neg_index[0] = neg_index[1] = multi->first; /*distance=last_body_postition-searched_body_position*/ distance -= idx+1; while (neg_index[1]->next) { if (distance == 0) { neg_index[0] = neg_index[0]->next; } else { distance--; } neg_index[1] = neg_index[1]->next; } if (distance>0) { LM_ERR("Index too low [%d]\n", idx); return pv_get_null(msg, param, res); } s.s = convert_mime2string_CT(neg_index[0]->content_type); s.len = strlen(s.s); } else { body_part = multi->first; distance = idx; while (distance && body_part->next) { distance--; body_part=body_part->next; } if (distance > 0) { LM_ERR("Index too big [%d]\n", idx); return pv_get_null(msg, param, res); } s.s = convert_mime2string_CT(body_part->content_type); s.len = strlen(s.s); } } else { /* copy main content type */ memcpy(buf, msg->content_type->body.s, msg->content_type->body.len); buf[msg->content_type->body.len] = ','; s.len = msg->content_type->body.len+1; /* copy all the other contenttypes */ body_part = multi->first; while (body_part) { s.s = convert_mime2string_CT(body_part->content_type); if (s.len + strlen(s.s) >= BUFLEN) { LM_CRIT("buffer overflow! Too many contenttypes!\n"); return pv_get_null(msg, param, res); } memcpy( buf+s.len, s.s, strlen(s.s)); s.len += strlen(s.s); /* delimiter only if something follows */ if(body_part->next) buf[s.len++] = ','; body_part = body_part->next; } s.s = buf; } return pv_get_strval(msg, param, res, &s); #undef BUFLEN } static int pv_get_content_length(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg==NULL) return -1; if(msg->content_length==NULL && ((parse_headers(msg, HDR_CONTENTLENGTH_F, 0)==-1) || (msg->content_length==NULL))) { LM_DBG("no Content-Length header\n"); return pv_get_null(msg, param, res); } return pv_get_intstrval(msg, param, res, (int)(long)msg->content_length->parsed, &msg->content_length->body); } static int pv_get_msg_body(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str s; int idx=-1; int idxf=-1; int distance=0; struct multi_body* multi; struct part* body_part; struct part* neg_index[2]; if(msg==NULL) return -1; if (pv_get_spec_index(msg, param, &idx, &idxf)!=0) { LM_ERR("invalid index\n"); return -1; } /* if no index specified or index requests all bodies*/ if (param->pvi.type==0 || idxf==PV_IDX_ALL) { if (get_body( msg, &s)!=0 || s.len==0 ) { LM_DBG("no message body\n"); return pv_get_null(msg, param, res); } goto end; } if ((multi=get_all_bodies(msg)) == 0 || multi->first == 0) { LM_ERR("cannot get multi body\n"); return pv_get_null(msg, param, res); } if (idx<0) { neg_index[0] = neg_index[1] = multi->first; /*distance=last_body_postition-searched_body_position*/ distance -= idx+1; while (neg_index[1]->next) { if (distance == 0) { neg_index[0] = neg_index[0]->next; } else { distance--; } neg_index[1] = neg_index[1]->next; } if (distance>0) { LM_ERR("Index too low [%d]\n", idx); return pv_get_null(msg, param, res); } s.s = neg_index[0]->body.s; s.len = neg_index[0]->body.len; } else { body_part = multi->first; distance = idx; while (distance && body_part->next) { distance--; body_part=body_part->next; } if (distance > 0) { LM_ERR("Index too big [%d]\n", idx); return pv_get_null(msg, param, res); } s.s = body_part->body.s; s.len = body_part->body.len; } end: return pv_get_strval(msg, param, res, &s); } static int pv_get_authattr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct hdr_field *hdr; str *s; if(msg==NULL) return -1; if ((msg->REQ_METHOD==METHOD_ACK) || (msg->REQ_METHOD==METHOD_CANCEL)) { LM_DBG("no [Proxy-]Authorization header\n"); return pv_get_null(msg, param, res); } if ((parse_headers(msg, HDR_PROXYAUTH_F|HDR_AUTHORIZATION_F, 0)==-1) || (msg->proxy_auth==0 && msg->authorization==0)) { LM_DBG("no [Proxy-]Authorization header\n"); return pv_get_null(msg, param, res); } hdr = (msg->proxy_auth==0)?msg->authorization:msg->proxy_auth; if(parse_credentials(hdr)!=0) { LM_ERR("failed to parse credentials\n"); return pv_get_null(msg, param, res); } switch(param->pvn.u.isname.name.n) { case 11: s = &((auth_body_t*)(hdr->parsed))->digest.nc; break; case 10: s = &((auth_body_t*)(hdr->parsed))->digest.qop.qop_str; break; case 9: s = &((auth_body_t*)(hdr->parsed))->digest.alg.alg_str; break; case 8: s = &((auth_body_t*)(hdr->parsed))->digest.opaque; break; case 7: s = &((auth_body_t*)(hdr->parsed))->digest.cnonce; break; case 6: s = &((auth_body_t*)(hdr->parsed))->digest.response; break; case 5: s = &((auth_body_t*)(hdr->parsed))->digest.nonce; break; case 4: s = &((auth_body_t*)(hdr->parsed))->digest.username.domain; break; case 3: s = &((auth_body_t*)(hdr->parsed))->digest.uri; break; case 2: s = &((auth_body_t*)(hdr->parsed))->digest.realm; break; case 1: s = &((auth_body_t*)(hdr->parsed))->digest.username.user; break; default: s = &((auth_body_t*)(hdr->parsed))->digest.username.whole; } if (s->len==0) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, s); } static inline str *cred_user(struct sip_msg *rq) { struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred=(auth_body_t*)(h->parsed); if (!cred || !cred->digest.username.user.len) return 0; return &cred->digest.username.user; } static inline str *cred_realm(struct sip_msg *rq) { str* realm; struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred=(auth_body_t*)(h->parsed); if (!cred) return 0; realm = GET_REALM(&cred->digest); if (!realm->len || !realm->s) { return 0; } return realm; } static int pv_get_acc_username(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[MAX_URI_SIZE]; str* user; str* realm; struct sip_uri puri; struct to_body* from; str s; /* try to take it from credentials */ user = cred_user(msg); if (user) { realm = cred_realm(msg); if (realm) { s.len = user->len+1+realm->len; if (s.len > MAX_URI_SIZE) { LM_ERR("uri too long\n"); return pv_get_null(msg, param, res); } s.s = buf; memcpy(s.s, user->s, user->len); (s.s)[user->len] = '@'; memcpy(s.s+user->len+1, realm->s, realm->len); return pv_get_strval(msg, param, res, &s); } return pv_get_strval(msg, param, res, user); } /* from from uri */ if(parse_from_header(msg)<0) { LM_ERR("cannot parse FROM header\n"); return pv_get_null(msg, param, res); } if (msg->from && (from=get_from(msg)) && from->uri.len) { if (parse_uri(from->uri.s, from->uri.len, &puri) < 0 ) { LM_ERR("bad From URI\n"); return pv_get_null(msg, param, res); } s.len = puri.user.len + 1 + puri.host.len; if (s.len > MAX_URI_SIZE) { LM_ERR("from URI too long\n"); return pv_get_null(msg, param, res); } s.s = buf; memcpy(s.s, puri.user.s, puri.user.len); (s.s)[puri.user.len] = '@'; memcpy(s.s + puri.user.len + 1, puri.host.s, puri.host.len); } else { s.len = 0; s.s = 0; } return pv_get_strval(msg, param, res, &s); } #define BR_URI_S "uri" #define BR_URI_LEN (sizeof(BR_URI_S)-1) #define BR_URI_ID 1 #define BR_DURI_S "duri" #define BR_DURI_LEN (sizeof(BR_DURI_S)-1) #define BR_DURI_ID 2 #define BR_Q_S "q" #define BR_Q_LEN (sizeof(BR_Q_S)-1) #define BR_Q_ID 3 #define BR_PATH_S "path" #define BR_PATH_LEN (sizeof(BR_PATH_S)-1) #define BR_PATH_ID 4 #define BR_FLAGS_S "flags" #define BR_FLAGS_LEN (sizeof(BR_FLAGS_S)-1) #define BR_FLAGS_ID 5 #define BR_SOCKET_S "socket" #define BR_SOCKET_LEN (sizeof(BR_SOCKET_S)-1) #define BR_SOCKET_ID 6 int pv_parse_branch_name(pv_spec_p sp, str *in) { if (sp==NULL || in==NULL || in->s==NULL || in->len==0) return -1; sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = 0; if (in->len==BR_URI_LEN && strncasecmp(in->s, BR_URI_S, BR_URI_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = BR_URI_ID; } else if (in->len==BR_DURI_LEN && strncasecmp(in->s, BR_DURI_S, BR_DURI_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = BR_DURI_ID; } else if (in->len==BR_Q_LEN && strncasecmp(in->s, BR_Q_S, BR_Q_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = BR_Q_ID; } else if (in->len==BR_PATH_LEN && strncasecmp(in->s, BR_PATH_S, BR_PATH_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = BR_PATH_ID; } else if (in->len==BR_FLAGS_LEN && strncasecmp(in->s, BR_FLAGS_S, BR_FLAGS_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = BR_FLAGS_ID; } else if (in->len==BR_SOCKET_LEN && strncasecmp(in->s, BR_SOCKET_S, BR_SOCKET_LEN)==0 ) { sp->pvp.pvn.u.isname.name.n = BR_SOCKET_ID; } else { LM_ERR("unsupported BRANCH field <%.*s>\n",in->len,in->s); return -1; } return 0; } static inline int get_branch_field( int idx, pv_name_t *pvn, pv_value_t *res) { str uri; qvalue_t q; str duri; str path; unsigned int flags; struct socket_info *si; uri.s = get_branch(idx, &uri.len, &q, &duri, &path, &flags, &si); if (!uri.s) return pv_get_null( NULL, NULL, res); /* got a valid branch, return the field */ switch (pvn->u.isname.name.n) { case 0: case BR_URI_ID: /* return URI */ res->rs = uri; res->flags = PV_VAL_STR; break; case BR_Q_ID: /* return Q */ res->rs.s = q2str(q, (unsigned int*)&res->rs.len); res->flags = PV_VAL_STR; break; case BR_DURI_ID: /* return DURI */ if ( !duri.s || !duri.len) return pv_get_null(NULL, NULL, res); res->rs = duri; res->flags = PV_VAL_STR; break; case BR_PATH_ID: /* return PATH */ if ( !path.s || !path.len) return pv_get_null(NULL, NULL, res); res->rs = path; res->flags = PV_VAL_STR; break; case BR_FLAGS_ID: /* return FLAGS */ res->rs.s = int2str( flags, &res->rs.len); res->ri = flags; res->flags = PV_VAL_STR|PV_VAL_INT; break; case BR_SOCKET_ID: /* return SOCKET */ if ( !si ) return pv_get_null(NULL, NULL, res); res->rs = si->sock_str; res->flags = PV_VAL_STR; break; default: LM_CRIT("BUG - unsupported ID %d\n",pvn->u.isname.name.n); return pv_get_null(NULL, NULL, res); } return 0; } static int pv_get_branch_fields(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str uri; qvalue_t q; int idx; int idxf; char *p; if(msg==NULL || res==NULL) return -1; if(msg->first_line.type == SIP_REPLY || get_nr_branches() == 0) return pv_get_null(msg, param, res); /* get the index */ if(pv_get_spec_index(msg, param, &idx, &idxf)!=0) { LM_ERR("invalid index\n"); return -1; } if (idxf!=PV_IDX_ALL && idx==0) { /* no index specified -> return the first branch */ return get_branch_field( 0, ¶m->pvn, res); } if(idxf==PV_IDX_ALL) { /* return all branches */ p = pv_local_buf; idx = 0; while ( (uri.s=get_branch(idx, &uri.len, &q, 0, 0, 0, 0))!=NULL ) { if ( pv_local_buf + PV_LOCAL_BUF_SIZE <= p + uri.len + PV_FIELD_DELIM_LEN ) { LM_ERR("local buffer length exceeded\n"); return pv_get_null(msg, param, res); } if (idx) { memcpy(p, PV_FIELD_DELIM, PV_FIELD_DELIM_LEN); p += PV_FIELD_DELIM_LEN; } memcpy(p, uri.s, uri.len); p += uri.len; idx++; } res->rs.s = pv_local_buf; res->rs.len = p - pv_local_buf; res->flags = PV_VAL_STR; return 0; } /* numerical index */ if (idx<0) { /* index from the end */ if (-idx > get_nr_branches()) return pv_get_null(msg, param, res); idx = get_nr_branches() + idx; } /* return the request branch info */ return get_branch_field( idx, ¶m->pvn, res); } /************************************************************/ /** * */ static int pv_get_avp(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { unsigned short name_type; int avp_name; int_str avp_value; struct usr_avp *avp; int_str avp_value0; struct usr_avp *avp0; int idx; int idxf; char *p; int n=0; if(msg==NULL || res==NULL || param==NULL) return -1; /* get the name */ if(pv_get_avp_name(msg, param, &avp_name, &name_type)!=0) { LM_ERR("invalid name\n"); return -1; } /* get the index */ if(pv_get_spec_index(msg, param, &idx, &idxf)!=0) { LM_ERR("invalid index\n"); return -1; } if ((avp=search_first_avp(name_type, avp_name, &avp_value, 0))==0) return pv_get_null(msg, param, res); res->flags = PV_VAL_STR; if (idxf!=PV_IDX_ALL && idx==0) { if(avp->flags & AVP_VAL_STR) { res->rs = avp_value.s; } else if(avp->flags & AVP_VAL_NULL) { res->flags |= PV_VAL_NULL; } else { res->rs.s = sint2str(avp_value.n, &res->rs.len); res->ri = avp_value.n; res->flags |= PV_VAL_INT|PV_TYPE_INT; } return 0; } /* print the entire AVP array */ if(idxf==PV_IDX_ALL) { p = pv_local_buf; /* separately handle the first AVP */ if(avp->flags & AVP_VAL_STR) { res->rs = avp_value.s; } else if(avp->flags & AVP_VAL_NULL) { res->rs.s = NULL; } else { res->rs.s = sint2str(avp_value.n, &res->rs.len); } if(p-pv_local_buf+res->rs.len+1>PV_LOCAL_BUF_SIZE) { LM_ERR("local buffer length exceeded!\n"); return pv_get_null(msg, param, res); } memcpy(p, res->rs.s, res->rs.len); p += res->rs.len; /* print subsequent AVPs as [DELIM AVP]* */ while ((avp = search_first_avp(name_type, avp_name, &avp_value, avp))) { if(avp->flags & AVP_VAL_STR) { res->rs = avp_value.s; } else if(avp->flags & AVP_VAL_NULL) { res->rs.s = NULL; } else { res->rs.s = sint2str(avp_value.n, &res->rs.len); } if(p-pv_local_buf+PV_FIELD_DELIM_LEN+1>PV_LOCAL_BUF_SIZE) { LM_ERR("local buffer length exceeded\n"); return pv_get_null(msg, param, res); } memcpy(p, PV_FIELD_DELIM, PV_FIELD_DELIM_LEN); p += PV_FIELD_DELIM_LEN; if(p-pv_local_buf+res->rs.len+1>PV_LOCAL_BUF_SIZE) { LM_ERR("local buffer length exceeded!\n"); return pv_get_null(msg, param, res); } memcpy(p, res->rs.s, res->rs.len); p += res->rs.len; } *p = 0; res->rs.s = pv_local_buf; res->rs.len = p - pv_local_buf; return 0; } /* we have a numeric index */ if(idx<0) { n = 1; avp0 = avp; while ((avp0=search_first_avp(name_type, avp_name, &avp_value0, avp0))!=0) n++; idx = -idx; if(idx>n) { LM_DBG("index out of range\n"); return pv_get_null(msg, param, res); } idx = n - idx; if(idx==0) { if(avp->flags & AVP_VAL_STR) { res->rs = avp_value.s; } else if(avp->flags & AVP_VAL_NULL) { res->flags |= PV_VAL_NULL; } else { res->rs.s = sint2str(avp_value.n, &res->rs.len); res->ri = avp_value.n; res->flags |= PV_VAL_INT|PV_TYPE_INT; } return 0; } } n=0; while(nflags & AVP_VAL_STR) { res->rs = avp_value.s; } else if(avp->flags & AVP_VAL_NULL) { res->flags |= PV_VAL_NULL; } else { res->rs.s = sint2str(avp_value.n, &res->rs.len); res->ri = avp_value.n; res->flags |= PV_VAL_INT|PV_TYPE_INT; } return 0; } LM_DBG("index out of range\n"); return pv_get_null(msg, param, res); } static int pv_get_hdr_prolog(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, pv_value_t* tv) { if(msg==NULL || res==NULL || param==NULL) return -1; /* get the name */ if(param->pvn.type == PV_NAME_PVAR) { if(pv_get_spec_name(msg, param, tv)!=0 || (!(tv->flags&PV_VAL_STR))) { LM_ERR("invalid name\n"); return -1; } } else { if(param->pvn.u.isname.type == AVP_NAME_STR) { tv->flags = PV_VAL_STR; tv->rs = param->pvn.u.isname.name.s; } else { tv->flags = 0; tv->ri = param->pvn.u.isname.name.n; } } /* we need to be sure we have parsed all headers */ if(parse_headers(msg, HDR_EOH_F, 0)<0) { LM_ERR("error parsing headers\n"); return pv_get_null(msg, param, res); } return 1; } static int pv_get_hdrcnt(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { pv_value_t tv; struct hdr_field *hf; unsigned int n; int ret; if ( (ret=pv_get_hdr_prolog(msg, param, res, &tv)) <= 0 ) return ret; n = 0; if (tv.flags==0) { /* it is a known header -> use type to find it */ for (hf=msg->headers; hf; hf=hf->next) { if (tv.ri==hf->type) ++n; } } else { /* it is an un-known header -> use name to find it */ for (hf=msg->headers; hf; hf=hf->next) { if (hf->type==HDR_OTHER_T && hf->name.len==tv.rs.len && strncasecmp(hf->name.s, tv.rs.s, hf->name.len)==0) ++n; } } return pv_get_uintval(msg, param, res, n); } static int pv_get_hdr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int idx; int idxf; pv_value_t tv; struct hdr_field *hf; struct hdr_field *hf0; char *p; int n; int ret; if ( (ret=pv_get_hdr_prolog(msg, param, res, &tv)) <= 0 ) return ret; if (tv.flags==0) { /* it is a known header -> use type to find it */ for (hf=msg->headers; hf; hf=hf->next) { if (tv.ri==hf->type) break; } } else { /* it is an un-known header -> use name to find it */ for (hf=msg->headers; hf; hf=hf->next) { if (hf->type==HDR_OTHER_T && hf->name.len==tv.rs.len && strncasecmp(hf->name.s, tv.rs.s, hf->name.len)==0) break; } } if(hf==NULL) return pv_get_null(msg, param, res); /* get the index */ if(pv_get_spec_index(msg, param, &idx, &idxf)!=0) { LM_ERR("invalid index\n"); return -1; } /* get the value */ res->flags = PV_VAL_STR; if(idxf!=PV_IDX_ALL && idx==0) { res->rs = hf->body; return 0; } if(idxf==PV_IDX_ALL) { p = pv_local_buf; do { if(p!=pv_local_buf) { if(p-pv_local_buf+PV_FIELD_DELIM_LEN+1>PV_LOCAL_BUF_SIZE) { LM_ERR("local buffer length exceeded\n"); return pv_get_null(msg, param, res); } memcpy(p, PV_FIELD_DELIM, PV_FIELD_DELIM_LEN); p += PV_FIELD_DELIM_LEN; } if(p-pv_local_buf+hf->body.len+1>PV_LOCAL_BUF_SIZE) { LM_ERR("local buffer length exceeded!\n"); return pv_get_null(msg, param, res); } memcpy(p, hf->body.s, hf->body.len); p += hf->body.len; /* next hf */ if (tv.flags==0) { /* it is a known header -> use type to find it */ for (hf=hf->next ; hf; hf=hf->next) { if (tv.ri==hf->type) break; } } else { /* it is an un-known header -> use name to find it */ for (hf=hf->next ; hf; hf=hf->next) { if (hf->type==HDR_OTHER_T && hf->name.len==tv.rs.len && strncasecmp(hf->name.s, tv.rs.s, hf->name.len)==0) break; } } } while (hf); *p = 0; res->rs.s = pv_local_buf; res->rs.len = p - pv_local_buf; return 0; } /* we have a numeric index */ hf0 = 0; if(idx<0) { n = 1; /* count headers */ if (tv.flags==0 ) { /* it is a known header -> use type to find it */ for (hf0=hf->next; hf0; hf0=hf0->next) { if (tv.ri==hf0->type) n++; } } else { /* it is an un-known header -> use name to find it */ for (hf0=hf->next; hf0; hf0=hf0->next) { if (hf0->type==HDR_OTHER_T && hf0->name.len==tv.rs.len && strncasecmp(hf0->name.s, tv.rs.s, hf0->name.len)==0) n++; } } idx = -idx; if(idx>n) { LM_DBG("index out of range\n"); return pv_get_null(msg, param, res); } idx = n - idx; if(idx==0) { res->rs = hf->body; return 0; } } n=0; while(n use type to find it */ for (hf0=hf->next; hf0; hf0=hf0->next) { if (tv.ri==hf0->type) { n++; if(n==idx) break; } } } else { /* it is an un-known header -> use name to find it */ for (hf0=hf->next; hf0; hf0=hf0->next) { if (hf0->type==HDR_OTHER_T && hf0->name.len==tv.rs.len && strncasecmp(hf0->name.s, tv.rs.s, hf0->name.len)==0) { n++; if(n==idx) break; } } } if(hf0==NULL) break; } if(hf0!=0) { res->rs = hf0->body; return 0; } LM_DBG("index out of range\n"); return pv_get_null(msg, param, res); } static int pv_get_scriptvar(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int ival = 0; char *sval = NULL; script_var_t *sv=NULL; if(msg==NULL || res==NULL) return -1; if(param==NULL || param->pvn.u.dname==0) return pv_get_null(msg, param, res); sv= (script_var_t*)param->pvn.u.dname; if (sv->v.flags&VAR_VAL_NULL) return pv_get_null(msg, param, res); if(sv->v.flags&VAR_VAL_STR) { res->rs = sv->v.value.s; res->flags = PV_VAL_STR; } else { sval = sint2str(sv->v.value.n, &ival); res->rs.s = sval; res->rs.len = ival; res->ri = sv->v.value.n; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; } return 0; } /********* end PV get functions *********/ /********* start PV set functions *********/ int pv_set_avp(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { int avp_name; int_str avp_val; int flags; unsigned short name_type; int idx, idxf; if(param==NULL) { LM_ERR("bad parameters\n"); return -1; } if(pv_get_avp_name(msg, param, &avp_name, &name_type)!=0) { LM_ALERT("BUG in getting dst AVP name\n"); goto error; } /* get the index */ if(pv_get_spec_index(msg, param, &idx, &idxf)!=0) { LM_ERR("invalid index\n"); return -1; } if(val == NULL) { if(op == COLONEQ_T || idxf == PV_IDX_ALL) destroy_avps(name_type, avp_name, 1); else { if(idx < 0) { LM_ERR("Index with negative value\n"); return -1; } destroy_index_avp(name_type, avp_name, idx); } return 0; } if(op == COLONEQ_T || idxf == PV_IDX_ALL) destroy_avps(name_type, avp_name, 1); flags = name_type; if(val->flags&PV_TYPE_INT) { avp_val.n = val->ri; } else { avp_val.s = val->rs; flags |= AVP_VAL_STR; } if(idxf == PV_IDX_INT || idxf == PV_IDX_PVAR) /* if the avp is indexed */ { if(replace_avp(flags, avp_name, avp_val, idx)< 0) { LM_ERR("Failed to replace avp\n"); goto error; } } else { if (add_avp(flags, avp_name, avp_val)<0) { LM_ERR("error - cannot add AVP\n"); goto error; } } return 0; error: return -1; } int pv_set_scriptvar(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { int_str avp_val; int flags; if(param==NULL) { LM_ERR("bad parameters\n"); return -1; } if(param->pvn.u.dname==0) { LM_ERR("error - cannot find svar\n"); goto error; } if(val == NULL) { set_var_value((script_var_t*)param->pvn.u.dname, NULL, VAR_VAL_NULL); return 0; } if(val->flags&PV_TYPE_INT) { avp_val.n = val->ri; flags = 0; } else { avp_val.s = val->rs; flags = VAR_VAL_STR; } if(set_var_value((script_var_t*)param->pvn.u.dname, &avp_val, flags)==NULL) { LM_ERR("error - cannot set svar [%.*s] \n", ((script_var_t*)param->pvn.u.dname)->name.len, ((script_var_t*)param->pvn.u.dname)->name.s); goto error; } return 0; error: return -1; } int pv_set_dsturi(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { struct action act; if(msg==NULL || param==NULL) { LM_ERR("bad parameters\n"); return -1; } if(val == NULL) { memset(&act, 0, sizeof(act)); act.type = RESET_DSTURI_T; if (do_action(&act, msg)<0) { LM_ERR("error - do action failed)\n"); goto error; } return 1; } if(!(val->flags&PV_VAL_STR)) { LM_ERR("error - str value required to set dst uri\n"); goto error; } if(set_dst_uri(msg, &val->rs)!=0) goto error; return 0; error: return -1; } int pv_set_ruri(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { if(msg==NULL || param==NULL || val==NULL) { LM_ERR("bad parameters\n"); return -1; } if(!(val->flags&PV_VAL_STR)) { LM_ERR("str value required to set R-URI\n"); goto error; } if (set_ruri( msg, &val->rs)!=0) { LM_ERR("failed to set RURI\n"); goto error; } return 0; error: return -1; } int pv_set_ru_q(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { if(msg==NULL || param==NULL || val==NULL) { LM_ERR("bad parameters\n"); return -1; } if(!(val->flags&PV_VAL_INT)) { LM_ERR("int value required to set r-uri queue value\n"); return -1; } if (val->ri > 1000) { LM_WARN("queue value too big %d - setting queue to " "maximum value (1000)\n", val->ri); set_ruri_q(msg, 1000); } else set_ruri_q(msg, val->ri); return 0; } int pv_set_ruri_user(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { struct action act; if(msg==NULL || param==NULL) { LM_ERR("bad parameters\n"); return -1; } if(val == NULL) { memset(&act, 0, sizeof(act)); act.type = SET_USER_T; act.elem[0].type = STRING_ST; act.elem[0].u.string = ""; if (do_action(&act, msg)<0) { LM_ERR("do action failed)\n"); goto error; } return 0; } if(!(val->flags&PV_VAL_STR)) { LM_ERR("str value required to set R-URI user\n"); goto error; } memset(&act, 0, sizeof(act)); act.elem[0].type = STR_ST; act.elem[0].u.s = val->rs; act.type = SET_USER_T; if (do_action(&act, msg)<0) { LM_ERR("do action failed\n"); goto error; } return 0; error: return -1; } int pv_set_ruri_host(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { struct action act; if(msg==NULL || param==NULL || val==NULL) { LM_ERR("bad parameters\n"); return -1; } if(!(val->flags&PV_VAL_STR)) { LM_ERR("str value required to set R-URI hostname\n"); goto error; } memset(&act, 0, sizeof(act)); act.elem[0].type = STR_ST; act.elem[0].u.s = val->rs; act.type = SET_HOST_T; if (do_action(&act, msg)<0) { LM_ERR("do action failed\n"); goto error; } return 0; error: return -1; } int pv_set_dsturi_host(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { struct action act; if(msg==NULL || param==NULL || val==NULL) { LM_ERR("bad parameters\n"); return -1; } if(!(val->flags&PV_VAL_STR)) { LM_ERR("str value required to set DST-URI hostname\n"); goto error; } memset(&act, 0, sizeof(act)); act.elem[0].type = STR_ST; act.elem[0].u.s = val->rs; act.type = SET_DSTHOST_T; if (do_action(&act, msg)<0) { LM_ERR("do action failed\n"); goto error; } return 0; error: return -1; } int pv_set_dsturi_port(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { struct action act; if(msg==NULL || param==NULL) { LM_ERR("bad parameters\n"); return -1; } if(val == NULL) { memset(&act, 0, sizeof(act)); act.type = SET_DSTPORT_T; act.elem[0].type = STR_ST; act.elem[0].u.s.s = ""; act.elem[0].u.s.len = 0; if (do_action(&act, msg)<0) { LM_ERR("do action failed)\n"); goto error; } return 0; } if(!(val->flags&PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; } memset(&act, 0, sizeof(act)); act.elem[0].type = STR_ST; act.elem[0].u.s = val->rs; act.type = SET_DSTPORT_T; if (do_action(&act, msg)<0) { LM_ERR("do action failed\n"); goto error; } return 0; error: return -1; } int pv_set_ruri_port(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { struct action act; if(msg==NULL || param==NULL) { LM_ERR("bad parameters\n"); return -1; } if(val == NULL) { memset(&act, 0, sizeof(act)); act.type = SET_PORT_T; act.elem[0].type = STR_ST; act.elem[0].u.s.s = ""; act.elem[0].u.s.len = 0; if (do_action(&act, msg)<0) { LM_ERR("do action failed)\n"); goto error; } return 0; } if(!(val->flags&PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; } memset(&act, 0, sizeof(act)); act.elem[0].type = STR_ST; act.elem[0].u.s = val->rs; act.type = SET_PORT_T; if (do_action(&act, msg)<0) { LM_ERR("do action failed\n"); goto error; } return 0; error: return -1; } int pv_set_branch(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { if (msg==NULL || param==NULL) { LM_ERR("bad parameters\n"); return -1; } if (msg->first_line.type == SIP_REPLY) return -1; if (!val || !(val->flags&PV_VAL_STR) || val->flags&(PV_VAL_NULL) || val->rs.len==0 ) { LM_ERR("str value required to create a new branch\n"); return -1; } if (append_branch( msg, &val->rs, NULL, NULL, Q_UNSPECIFIED, 0, NULL)!=1){ LM_ERR("failed to append new branch\n"); return -1; } return 0; } int pv_set_branch_fields(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { int idx; int idxf; str *s; qvalue_t q; unsigned int flags; struct socket_info *si; if (msg==NULL || param==NULL) { LM_ERR("bad parameters\n"); return -1; } if (msg->first_line.type == SIP_REPLY) return -1; /* get the index */ if (pv_get_spec_index(msg, param, &idx, &idxf)!=0) { LM_ERR("invalid index\n"); return -1; } if(idxf==PV_IDX_ALL) { LM_ERR("SCRIPT BUG - * not allowed in branch assignment\n"); return -1; } if (idx<0) { idx = get_nr_branches() + idx; } if (idx<0 || idx>=get_nr_branches()) { LM_ERR("SCRIPT BUG - inexisting branch assignment [%d/%d]\n", get_nr_branches(), idx); return -1; } switch (param->pvn.u.isname.name.n) { case BR_URI_ID: /* set URI */ if (!val || !(val->flags&PV_VAL_STR) || val->flags&(PV_VAL_NULL) || val->rs.len==0 ) { LM_ERR("str value required to set the branch URI\n"); return -1; } s = &val->rs; return update_branch( idx, &s, NULL, NULL, NULL, NULL, NULL); case BR_Q_ID: /* set Q */ if ( val && !(val->flags&PV_VAL_INT) ) { LM_ERR("INT value required to set the branch Q\n"); return -1; } q = (!val||val->flags&PV_VAL_NULL)? Q_UNSPECIFIED : val->ri; return update_branch( idx, NULL, NULL, NULL, &q, NULL, NULL); case BR_DURI_ID: /* set DURI */ if ( val && !(val->flags&PV_VAL_STR) ) { LM_ERR("STR value required to set the branch DURI\n"); return -1; } s = (!val||val->flags&PV_VAL_NULL)? NULL : &val->rs; return update_branch( idx, NULL, &s, NULL, NULL, NULL, NULL); case BR_PATH_ID: /* set PATH */ if ( val && !(val->flags&PV_VAL_STR) ) { LM_ERR("STR value required to set the branch PATH\n"); return -1; } s = (!val||val->flags&PV_VAL_NULL)? NULL : &val->rs; return update_branch( idx, NULL, NULL, &s, NULL, NULL, NULL); case BR_FLAGS_ID: /* set FLAGS */ if ( val && !(val->flags&PV_VAL_INT) ) { LM_ERR("INT value required to set the branch FLAGS\n"); return -1; } flags = (!val||val->flags&PV_VAL_NULL)? 0 : val->ri; return update_branch( idx, NULL, NULL, NULL, NULL, &flags, NULL); case BR_SOCKET_ID: /* set SOCKET */ if ( val && !(val->flags&PV_VAL_STR) ) { LM_ERR("STR value required to set the branch SOCKET\n"); return -1; } if (!val || val->flags&PV_VAL_NULL) { si = NULL; } else { str host; int port, proto; if (parse_phostport(val->rs.s, val->rs.len, &host.s, &host.len, &port, &proto) < 0) { LM_ERR("invalid socket specification\n"); return -1; } set_sip_defaults( port, proto); si = grep_sock_info(&host, (unsigned short)port, (unsigned short)proto); if (si==NULL) return -1; } return update_branch( idx, NULL, NULL, NULL, NULL, NULL, &si); default: LM_CRIT("BUG - unsupported ID %d\n",param->pvn.u.isname.type); return -1; } } int pv_set_force_sock(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { struct socket_info *si; int port, proto; str host; if(msg==NULL || param==NULL) { LM_ERR("bad parameters\n"); return -1; } if(val==NULL) { msg->force_send_socket = NULL; return 0; } if(!(val->flags&PV_VAL_STR) || val->rs.len<=0) { LM_ERR("str value required to set the force send sock\n"); goto error; } if (parse_phostport(val->rs.s, val->rs.len, &host.s, &host.len, &port, &proto) < 0) { LM_ERR("invalid socket specification\n"); goto error; } set_sip_defaults( port, proto); si = grep_sock_info(&host, (unsigned short)port, (unsigned short)proto); if (si!=NULL) { msg->force_send_socket = si; } else { LM_WARN("no socket found to match [%.*s]\n", val->rs.len, val->rs.s); } return 0; error: return -1; } /********* end PV set functions *********/ int pv_parse_scriptvar_name(pv_spec_p sp, str *in) { if(in==NULL || in->s==NULL || sp==NULL) return -1; sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void*)add_var(in); if(sp->pvp.pvn.u.dname==NULL) { LM_ERR("cannot register var [%.*s]\n", in->len, in->s); return -1; } return 0; } int pv_parse_hdr_name(pv_spec_p sp, str *in) { str s; char *p; pv_spec_p nsp = 0; struct hdr_field hdr; if(in==NULL || in->s==NULL || sp==NULL) return -1; p = in->s; if(*p==PV_MARKER) { nsp = (pv_spec_p)pkg_malloc(sizeof(pv_spec_t)); if(nsp==NULL) { LM_ERR("no more memory\n"); return -1; } p = pv_parse_spec(in, nsp); if(p==NULL) { LM_ERR("invalid name [%.*s]\n", in->len, in->s); pv_spec_free(nsp); return -1; } //LM_ERR("dynamic name [%.*s]\n", in->len, in->s); //pv_print_spec(nsp); sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void*)nsp; return 0; } if(in->len>=PV_LOCAL_BUF_SIZE-1) { LM_ERR("name too long\n"); return -1; } memcpy(pv_local_buf, in->s, in->len); pv_local_buf[in->len] = ':'; s.s = pv_local_buf; s.len = in->len+1; if (parse_hname2(s.s, s.s + ((s.len<4)?4:s.len), &hdr)==0) { LM_ERR("error parsing header name [%.*s]\n", s.len, s.s); goto error; } sp->pvp.pvn.type = PV_NAME_INTSTR; if (hdr.type!=HDR_OTHER_T && hdr.type!=HDR_ERROR_T) { LM_DBG("using hdr type (%d) instead of <%.*s>\n", hdr.type, in->len, in->s); sp->pvp.pvn.u.isname.type = 0; sp->pvp.pvn.u.isname.name.n = hdr.type; } else { sp->pvp.pvn.u.isname.type = AVP_NAME_STR; sp->pvp.pvn.u.isname.name.s = *in; } return 0; error: return -1; } int pv_parse_avp_name(pv_spec_p sp, str *in) { char *p; char *s; pv_spec_p nsp = 0; if(in==NULL || in->s==NULL || sp==NULL) return -1; p = in->s; if(*p==PV_MARKER) { nsp = (pv_spec_p)pkg_malloc(sizeof(pv_spec_t)); if(nsp==NULL) { LM_ERR("no more memory\n"); return -1; } s = pv_parse_spec(in, nsp); if(s==NULL) { LM_ERR("invalid name [%.*s]\n", in->len, in->s); pv_spec_free(nsp); return -1; } //LM_ERR("dynamic name [%.*s]\n", in->len, in->s); //pv_print_spec(nsp); sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void*)nsp; return 0; } /*LM_DBG("static name [%.*s]\n", in->len, in->s);*/ /* always an int type from now */ sp->pvp.pvn.u.isname.type = 0; if(parse_avp_spec(in, &sp->pvp.pvn.u.isname.name.n)!=0) { LM_ERR("bad avp name [%.*s]\n", in->len, in->s); return -1; } sp->pvp.pvn.type = PV_NAME_INTSTR; return 0; } int pv_parse_index(pv_spec_p sp, str *in) { char *p; char *s; int sign; pv_spec_p nsp = 0; if(in==NULL || in->s==NULL || sp==NULL) return -1; p = in->s; if(*p==PV_MARKER) { nsp = (pv_spec_p)pkg_malloc(sizeof(pv_spec_t)); if(nsp==NULL) { LM_ERR("no more memory\n"); return -1; } memset(nsp, 0, sizeof(pv_spec_t)); s = pv_parse_spec(in, nsp); if(s==NULL) { LM_ERR("invalid index [%.*s]\n", in->len, in->s); pv_spec_free(nsp); return -1; } sp->pvp.pvi.type = PV_IDX_PVAR; sp->pvp.pvi.u.dval = (void*)nsp; return 0; } if(*p=='*' && in->len==1) { sp->pvp.pvi.type = PV_IDX_ALL; return 0; } sign = 1; if(*p=='-') { sign = -1; p++; } sp->pvp.pvi.u.ival = 0; while(ps+in->len && *p>='0' && *p<='9') { sp->pvp.pvi.u.ival = sp->pvp.pvi.u.ival * 10 + *p - '0'; p++; } if(p!=in->s+in->len) { LM_ERR("invalid index [%.*s]\n", in->len, in->s); return -1; } sp->pvp.pvi.u.ival *= sign; sp->pvp.pvi.type = PV_IDX_INT; return 0; } int pv_init_iname(pv_spec_p sp, int param) { if(sp==NULL) return -1; sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.name.n = param; return 0; } int pv_get_line_number(struct sip_msg *msg, pv_param_t *param, pv_value_t *res){ int l; char *ch; if (param==NULL) { LM_CRIT("BUG - bad parameters\n"); return -1; } if(res == NULL) { return -1; } res->ri = curr_action_line; ch = int2str( (unsigned long)res->ri, &l); res->rs.s = ch; res->rs.len = l; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } int pv_get_cfg_file_name(struct sip_msg *msg, pv_param_t *param, pv_value_t *res){ if (param==NULL) { LM_CRIT("BUG - bad parameters\n"); return -1; } if(res == NULL) { return -1; } res->rs.s = curr_action_file; res->rs.len = (res->rs.s)?(strlen(res->rs.s)):(0); res->flags = PV_VAL_STR; return 0; } int pv_set_log_level(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { if(param==NULL) { LM_ERR("bad parameters\n"); return -1; } if(val==NULL || (val->flags&(PV_VAL_NULL|PV_VAL_NONE))!=0) { /* reset the value to default */ reset_proc_log_level(); } else { if ((val->flags&PV_TYPE_INT)==0) { LM_ERR("input for $log_level found not to be an interger\n"); return -1; } set_proc_log_level(val->ri); } return 0; } int pv_get_log_level(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { int l; if (param==NULL) { LM_CRIT("BUG - bad parameters\n"); return -1; } if(res == NULL) { return -1; } res->ri = *log_level; res->rs.s = int2str( (unsigned long)res->ri, &l); res->rs.len = l; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } /** * the table with core pseudo-variables */ static pv_export_t _pv_names_table[] = { {{"avp", (sizeof("avp")-1)}, PVT_AVP, pv_get_avp, pv_set_avp, pv_parse_avp_name, pv_parse_index, 0, 0}, {{"hdr", (sizeof("hdr")-1)}, PVT_HDR, pv_get_hdr, 0, pv_parse_hdr_name, pv_parse_index, 0, 0}, {{"hdrcnt", (sizeof("hdrcnt")-1)}, PVT_HDRCNT, pv_get_hdrcnt, 0, pv_parse_hdr_name, 0, 0, 0}, {{"var", (sizeof("var")-1)}, PVT_SCRIPTVAR, pv_get_scriptvar, pv_set_scriptvar, pv_parse_scriptvar_name, 0, 0, 0}, {{"ai", (sizeof("ai")-1)}, /* */ PVT_PAI_URI, pv_get_pai, 0, 0, 0, 0, 0}, {{"au", (sizeof("au")-1)}, /* */ PVT_AUTH_USERNAME, pv_get_authattr, 0, 0, 0, pv_init_iname, 1}, {{"ar", (sizeof("ar")-1)}, /* auth realm */ PVT_AUTH_REALM, pv_get_authattr, 0, 0, 0, pv_init_iname, 2}, {{"adu", (sizeof("adu")-1)}, /* auth digest uri */ PVT_AUTH_DURI, pv_get_authattr, 0, 0, 0, pv_init_iname, 3}, {{"ad", (sizeof("ad")-1)}, /* */ PVT_AUTH_DOMAIN, pv_get_authattr, 0, 0, 0, pv_init_iname, 4}, {{"an", (sizeof("an")-1)}, /* */ PVT_AUTH_NONCE, pv_get_authattr, 0, 0, 0, pv_init_iname, 5}, {{"auth.nonce", (sizeof("auth.nonce")-1)}, /* */ PVT_AUTH_NONCE, pv_get_authattr, 0, 0, 0, pv_init_iname, 5}, {{"auth.resp", (sizeof("auth.resp")-1)}, /* */ PVT_AUTH_RESPONSE, pv_get_authattr, 0, 0, 0, pv_init_iname, 6}, {{"auth.cnonce", (sizeof("auth.cnonce")-1)}, /* */ PVT_AUTH_CNONCE, pv_get_authattr, 0, 0, 0, pv_init_iname, 7}, {{"auth.opaque", (sizeof("auth.opaque")-1)}, /* */ PVT_AUTH_OPAQUE, pv_get_authattr, 0, 0, 0, pv_init_iname, 8}, {{"auth.alg", (sizeof("auth.alg")-1)}, /* */ PVT_AUTH_ALGORITHM, pv_get_authattr, 0, 0, 0, pv_init_iname, 9}, {{"auth.qop", (sizeof("auth.qop")-1)}, /* */ PVT_AUTH_QOP, pv_get_authattr, 0, 0, 0, pv_init_iname, 10}, {{"auth.nc", (sizeof("auth.nc")-1)}, /* */ PVT_AUTH_NONCE_COUNT, pv_get_authattr, 0, 0, 0, pv_init_iname, 11}, {{"aU", (sizeof("aU")-1)}, /* */ PVT_AUTH_USERNAME_WHOLE, pv_get_authattr, 0, 0, 0, pv_init_iname, 99}, {{"Au", (sizeof("Au")-1)}, /* */ PVT_ACC_USERNAME, pv_get_acc_username, 0, 0, 0, pv_init_iname, 1}, {{"bf", (sizeof("bf")-1)}, /* */ PVT_BFLAGS, pv_get_bflags, 0, 0, 0, 0, 0}, {{"branch", (sizeof("branch")-1)}, /* */ PVT_BRANCH, pv_get_branch_fields, pv_set_branch, 0, 0, 0, 0}, {{"branch", (sizeof("branch")-1)}, /* */ PVT_BRANCH, pv_get_branch_fields, pv_set_branch_fields, pv_parse_branch_name, pv_parse_index, 0, 0}, {{"ci", (sizeof("ci")-1)}, /* */ PVT_CALLID, pv_get_callid, 0, 0, 0, 0, 0}, {{"cl", (sizeof("cl")-1)}, /* */ PVT_CONTENT_LENGTH, pv_get_content_length, 0, 0, 0, 0, 0}, {{"cs", (sizeof("cs")-1)}, /* */ PVT_CSEQ, pv_get_cseq, 0, 0, 0, 0, 0}, {{"ct", (sizeof("ct")-1)}, /* */ PVT_CONTACT, pv_get_contact_body, 0, 0, pv_parse_index, 0, 0}, {{"ct.fields", (sizeof("ct.fields")-1)}, /* */ PVT_CONTACT, pv_get_contact_body, 0, pv_parse_ct_name, pv_parse_index, 0, 0}, {{"cT", (sizeof("cT")-1)}, /* */ PVT_CONTENT_TYPE, pv_get_content_type, 0, 0, pv_parse_index, 0, 0}, {{"dd", (sizeof("dd")-1)}, /* */ PVT_DSTURI_DOMAIN, pv_get_dsturi_attr, pv_set_dsturi_host, 0, 0, pv_init_iname, 1}, {{"di", (sizeof("di")-1)}, /* */ PVT_DIVERSION_URI, pv_get_diversion, 0, 0, 0, pv_init_iname, 1}, {{"dir", (sizeof("dir")-1)}, /* */ PVT_DIV_REASON, pv_get_diversion, 0, 0, 0, pv_init_iname, 2}, {{"dip", (sizeof("dis")-1)}, /* */ PVT_DIV_PRIVACY, pv_get_diversion, 0, 0, 0, pv_init_iname, 3}, {{"dp", (sizeof("dp")-1)}, /* */ PVT_DSTURI_PORT, pv_get_dsturi_attr, pv_set_dsturi_port, 0, 0, pv_init_iname, 2}, {{"dP", (sizeof("dP")-1)}, /* */ PVT_DSTURI_PROTOCOL, pv_get_dsturi_attr, 0, 0, 0, pv_init_iname, 3}, {{"ds", (sizeof("ds")-1)}, /* */ PVT_DSET, pv_get_dset, 0, 0, 0, 0, 0}, {{"du", (sizeof("du")-1)}, /* */ PVT_DSTURI, pv_get_dsturi, pv_set_dsturi, 0, 0, 0, 0}, {{"duri", (sizeof("duri")-1)}, /* */ PVT_DSTURI, pv_get_dsturi, pv_set_dsturi, 0, 0, 0, 0}, {{"err.class", (sizeof("err.class")-1)}, /* */ PVT_ERR_CLASS, pv_get_errinfo_attr, 0, 0, 0, 0, 0}, {{"err.level", (sizeof("err.level")-1)}, /* */ PVT_ERR_LEVEL, pv_get_errinfo_attr, 0, 0, 0, pv_init_iname, 1}, {{"err.info", (sizeof("err.info")-1)}, /* */ PVT_ERR_INFO, pv_get_errinfo_attr, 0, 0, 0, pv_init_iname, 2}, {{"err.rcode", (sizeof("err.rcode")-1)}, /* */ PVT_ERR_RCODE, pv_get_errinfo_attr, 0, 0, 0, pv_init_iname, 3}, {{"err.rreason", (sizeof("err.rreason")-1)}, /* */ PVT_ERR_RREASON, pv_get_errinfo_attr, 0, 0, 0, pv_init_iname, 4}, {{"fd", (sizeof("fd")-1)}, /* */ PVT_FROM_DOMAIN, pv_get_from_attr, 0, 0, 0, pv_init_iname, 3}, {{"from.domain", (sizeof("from.domain")-1)}, /* */ PVT_FROM_DOMAIN, pv_get_from_attr, 0, 0, 0, pv_init_iname, 3}, {{"fn", (sizeof("fn")-1)}, /* */ PVT_FROM_DISPLAYNAME, pv_get_from_attr, 0, 0, 0, pv_init_iname, 5}, {{"fs", (sizeof("fs")-1)}, /* */ PVT_FORCE_SOCK, pv_get_force_sock, pv_set_force_sock, 0, 0, 0, 0}, {{"ft", (sizeof("ft")-1)}, /* */ PVT_FROM_TAG, pv_get_from_attr, 0, 0, 0, pv_init_iname, 4}, {{"fu", (sizeof("fu")-1)}, /* */ PVT_FROM, pv_get_from_attr, 0, 0, 0, pv_init_iname, 1}, {{"from", (sizeof("from")-1)}, /* */ PVT_FROM, pv_get_from_attr, 0, 0, 0, pv_init_iname, 1}, {{"fU", (sizeof("fU")-1)}, /* */ PVT_FROM_USERNAME, pv_get_from_attr, 0, 0, 0, pv_init_iname, 2}, {{"from.user", (sizeof("from.user")-1)}, /* */ PVT_FROM_USERNAME, pv_get_from_attr, 0, 0, 0, pv_init_iname, 2}, {{"log_level", (sizeof("log_level")-1)}, /* per process log level*/ PVT_LOG_LEVEL, pv_get_log_level, pv_set_log_level, 0, 0, 0, 0}, {{"mb", (sizeof("mb")-1)}, /* */ PVT_MSG_BUF, pv_get_msg_buf, 0, 0, 0, 0, 0}, {{"mf", (sizeof("mf")-1)}, /* */ PVT_FLAGS, pv_get_flags, 0, 0, 0, 0, 0}, {{"mi", (sizeof("mi")-1)}, /* */ PVT_MSGID, pv_get_msgid, 0, 0, 0, 0, 0}, {{"ml", (sizeof("ml")-1)}, /* */ PVT_MSG_LEN, pv_get_msg_len, 0, 0, 0, 0, 0}, {{"od", (sizeof("od")-1)}, /* */ PVT_OURI_DOMAIN, pv_get_ouri_attr, 0, 0, 0, pv_init_iname, 2}, {{"op", (sizeof("op")-1)}, /* */ PVT_OURI_PORT, pv_get_ouri_attr, 0, 0, 0, pv_init_iname, 3}, {{"oP", (sizeof("oP")-1)}, /* */ PVT_OURI_PROTOCOL, pv_get_ouri_attr, 0, 0, 0, pv_init_iname, 4}, {{"ou", (sizeof("ou")-1)}, /* */ PVT_OURI, pv_get_ouri, 0, 0, 0, 0, 0}, {{"ouri", (sizeof("ouri")-1)}, /* */ PVT_OURI, pv_get_ouri, 0, 0, 0, 0, 0}, {{"oU", (sizeof("oU")-1)}, /* */ PVT_OURI_USERNAME, pv_get_ouri_attr, 0, 0, 0, pv_init_iname, 1}, {{"path", (sizeof("path")-1)}, /* */ PVT_PATH, pv_get_path, 0, 0, 0, 0, 0}, {{"pd", (sizeof("pd")-1)}, /* */ PVT_PPI_DOMAIN, pv_get_ppi_attr, 0, 0, 0, pv_init_iname, 3}, {{"pn", (sizeof("pn")-1)}, /* */ PVT_PPI_DISPLAYNAME, pv_get_ppi_attr, 0, 0, 0, pv_init_iname, 4}, {{"pp", (sizeof("pp")-1)}, /* */ PVT_PID, pv_get_pid, 0, 0, 0, 0, 0}, {{"pr", (sizeof("pr")-1)}, /* */ PVT_PROTO, pv_get_proto, 0, 0, 0, 0, 0}, {{"proto", (sizeof("proto")-1)}, /* */ PVT_PROTO, pv_get_proto, 0, 0, 0, 0, 0}, {{"pu", (sizeof("pu")-1)}, /* */ PVT_PPI, pv_get_ppi_attr, 0, 0, 0, pv_init_iname, 1}, {{"pU", (sizeof("pU")-1)}, /* */ PVT_PPI_USERNAME, pv_get_ppi_attr, 0, 0, 0, pv_init_iname, 2}, {{"rb", (sizeof("rb")-1)}, /* */ PVT_MSG_BODY, pv_get_msg_body, 0, 0, pv_parse_index, 0, 0}, {{"rc", (sizeof("rc")-1)}, /* */ PVT_RETURN_CODE, pv_get_return_code, 0, 0, 0, 0, 0}, {{"retcode", (sizeof("retcode")-1)}, /* */ PVT_RETURN_CODE, pv_get_return_code, 0, 0, 0, 0, 0}, {{"rd", (sizeof("rd")-1)}, /* */ PVT_RURI_DOMAIN, pv_get_ruri_attr, pv_set_ruri_host, 0, 0, pv_init_iname, 2}, {{"ruri.domain", (sizeof("ruri.domain")-1)}, /* */ PVT_RURI_DOMAIN, pv_get_ruri_attr, pv_set_ruri_host, 0, 0, pv_init_iname, 2}, {{"re", (sizeof("re")-1)}, /* */ PVT_RPID_URI, pv_get_rpid, 0, 0, 0, 0, 0}, {{"rm", (sizeof("rm")-1)}, /* */ PVT_METHOD, pv_get_method, 0, 0, 0, 0, 0}, {{"rp", (sizeof("rp")-1)}, /* */ PVT_RURI_PORT, pv_get_ruri_attr, pv_set_ruri_port, 0, 0, pv_init_iname, 3}, {{"rP", (sizeof("rP")-1)}, /* */ PVT_RURI_PROTOCOL, pv_get_ruri_attr, 0, 0, 0, pv_init_iname, 4}, {{"rr", (sizeof("rr")-1)}, /* */ PVT_REASON, pv_get_reason, 0, 0, 0, 0, 0}, {{"rs", (sizeof("rs")-1)}, /* */ PVT_STATUS, pv_get_status, 0, 0, 0, 0, 0}, {{"rt", (sizeof("rt")-1)}, /* */ PVT_REFER_TO, pv_get_refer_to, 0, 0, 0, 0, 0}, {{"rT", (sizeof("rt")-1)}, /* */ PVT_ROUTE_TYPE, pv_get_route_type, 0, 0, 0, 0, 0}, {{"ru", (sizeof("ru")-1)}, /* */ PVT_RURI, pv_get_ruri, pv_set_ruri, 0, 0, 0, 0}, {{"ruri", (sizeof("ruri")-1)}, /* */ PVT_RURI, pv_get_ruri, pv_set_ruri, 0, 0, 0, 0}, {{"ru_q", (sizeof("ru_q")-1)}, /* */ PVT_RU_Q, pv_get_ru_q, pv_set_ru_q, 0, 0, 0, 0}, {{"rU", (sizeof("rU")-1)}, /* */ PVT_RURI_USERNAME, pv_get_ruri_attr, pv_set_ruri_user, 0, 0, pv_init_iname, 1}, {{"ruri.user", (sizeof("ruri.user")-1)}, /* */ PVT_RURI_USERNAME, pv_get_ruri_attr, pv_set_ruri_user, 0, 0, pv_init_iname, 1}, {{"Ri", (sizeof("Ri")-1)}, /* */ PVT_RCVIP, pv_get_rcvip, 0, 0, 0, 0, 0}, {{"Rp", (sizeof("Rp")-1)}, /* */ PVT_RCVPORT, pv_get_rcvport, 0, 0, 0, 0, 0}, {{"src_ip", (sizeof("src_ip")-1)}, /* */ PVT_SRCIP, pv_get_srcip, 0, 0, 0, 0, 0}, {{"si", (sizeof("si")-1)}, /* */ PVT_SRCIP, pv_get_srcip, 0, 0, 0, 0, 0}, {{"sp", (sizeof("sp")-1)}, /* */ PVT_SRCPORT, pv_get_srcport, 0, 0, 0, 0, 0}, {{"td", (sizeof("td")-1)}, /* */ PVT_TO_DOMAIN, pv_get_to_attr, 0, 0, 0, pv_init_iname, 3}, {{"to.domain", (sizeof("to.domain")-1)}, /* */ PVT_TO_DOMAIN, pv_get_to_attr, 0, 0, 0, pv_init_iname, 3}, {{"time", (sizeof("time")-1)}, /* */ PVT_TIME, pv_get_formated_time, 0, pv_parse_time_name, 0, 0, 0}, {{"tn", (sizeof("tn")-1)}, /* */ PVT_TO_DISPLAYNAME, pv_get_to_attr, 0, 0, 0, pv_init_iname, 5}, {{"tt", (sizeof("tt")-1)}, /* */ PVT_TO_TAG, pv_get_to_attr, 0, 0, 0, pv_init_iname, 4}, {{"tu", (sizeof("tu")-1)}, /* */ PVT_TO, pv_get_to_attr, 0, 0, 0, pv_init_iname, 1}, {{"to", (sizeof("to")-1)}, /* */ PVT_TO, pv_get_to_attr, 0, 0, 0, pv_init_iname, 1}, {{"tU", (sizeof("tU")-1)}, /* */ PVT_TO_USERNAME, pv_get_to_attr, 0, 0, 0, pv_init_iname, 2}, {{"to.user", (sizeof("to.user")-1)}, /* */ PVT_TO_USERNAME, pv_get_to_attr, 0, 0, 0, pv_init_iname, 2}, {{"Tf", (sizeof("Tf")-1)}, /* */ PVT_TIMEF, pv_get_timef, 0, 0, 0, 0, 0}, {{"Ts", (sizeof("Ts")-1)}, /* */ PVT_TIMES, pv_get_times, 0, 0, 0, 0, 0}, {{"Tsm", (sizeof("Tsm")-1)}, /* */ PVT_TIMES, pv_get_timem, 0, 0, 0, 0, 0}, {{"TS", (sizeof("TS")-1)}, /* */ PVT_TIMES, pv_get_start_times, 0, 0, 0, 0, 0}, {{"ua", (sizeof("ua")-1)}, /* */ PVT_USERAGENT, pv_get_useragent, 0, 0, 0, 0, 0}, {{"C", sizeof("C")-1}, PVT_COLOR, pv_get_color, 0, pv_parse_color_name, 0, 0, 0 }, {{"argv", sizeof("argv")-1}, PVT_ARGV, pv_get_argv, 0, pv_parse_argv_name, 0, 0, 0 }, {{"param", sizeof("param")-1}, PVT_ROUTE_PARAM, pv_get_param, 0, pv_parse_param_name, 0, 0, 0 }, {{"cfg_line", sizeof("cfg_line")-1}, PVT_LINE_NUMBER, pv_get_line_number, 0, 0, 0, 0, 0 }, {{"cfg_file", sizeof("cfg_file")-1}, PVT_CFG_FILE_NAME, pv_get_cfg_file_name, 0, 0, 0, 0, 0 }, {{0,0}, 0, 0, 0, 0, 0, 0, 0} }; pv_export_t* pv_lookup_spec_name(str *pvname, pv_spec_p e, int has_name) { int i; pv_extra_p pvi; int found; if(pvname==0 || e==0) { LM_ERR("bad parameters\n"); return NULL; } /* search in main table */ for(i=0; _pv_names_table[i].name.s!=0; i++) { if(_pv_names_table[i].name.len==pvname->len && !((has_name?1:0) ^ (_pv_names_table[i].parse_name?1:0)) && memcmp(_pv_names_table[i].name.s, pvname->s, pvname->len)==0) { /*LM_DBG("found [%.*s] [%d]\n", pvname->len, pvname->s, _pv_names_table[i].type);*/ /* copy data from table to spec */ e->type = _pv_names_table[i].type; e->getf = _pv_names_table[i].getf; e->setf = _pv_names_table[i].setf; return &_pv_names_table[i]; } } /* search in extra list */ if(_pv_extra_list==0) { LM_DBG("extra items list is empty\n"); return NULL; } pvi = *_pv_extra_list; while(pvi) { if(pvi->pve.name.len>pvname->len) break; if(pvi->pve.name.len==pvname->len) { found = strncmp(pvi->pve.name.s, pvname->s, pvname->len); if(found>0) break; if(found==0) { LM_DBG("found in extra list [%.*s]\n", pvname->len, pvname->s); /* copy data from export to spec */ e->type = pvi->pve.type; e->getf = pvi->pve.getf; e->setf = pvi->pve.setf; return &(pvi->pve); } } pvi = pvi->next; } return NULL; } static int is_pv_valid_char(char c) { if((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || (c=='_') || (c=='.')) return 1; return 0; } char* pv_parse_spec(str *in, pv_spec_p e) { char *p; str s; str pvname; str pvcontext; int pvstate; int has_inner_name; trans_t *tr = NULL; pv_export_t *pte = NULL; int n=0; if(in==NULL || in->s==NULL || e==NULL || *in->s!=PV_MARKER) { LM_ERR("bad parameters\n"); return NULL; } // LM_DBG("***** input [%.*s] (%d)\n", in->len, in->s, in->len); tr = 0; pvstate = 0; memset(e, 0, sizeof(pv_spec_t)); p = in->s; p++; if(*p==PV_LNBRACKET) { p++; pvstate = 1; } pvname.s = p; if(*p == PV_MARKER) { p++; if(pvstate==1) { if(*p!=PV_RNBRACKET) goto error; p++; } e->getf = pv_get_marker; e->type = PVT_MARKER; pvname.len = 1; goto done_all; } if (*p==PV_LCBRACKET) { /* context definition*/ p++; pvcontext.s = p; while(is_in_str(p,in) && is_pv_valid_char(*p)) p++; if(*p != PV_RCBRACKET) { LM_ERR("Expected to find the end of the context\n"); return 0; } pvcontext.len = p - pvcontext.s; LM_DBG("Context name is %.*s\n", pvcontext.len, pvcontext.s); p++; e->pvc = pv_get_context(&pvcontext); if(e->pvc == NULL) { if(!pvc_before_check) { LM_ERR("Requested a non existing pv context\n"); return 0; } LM_DBG("No context definition found for [%.*s]\n", pvcontext.len, pvcontext.s); /* create a dummy context strcuture to be filled by the register functions */ e->pvc = add_pv_context(&pvcontext, 0); if(e->pvc == NULL ) { LM_ERR("Failed to new context\n"); return 0; } } } pvname.s = p; while(is_in_str(p,in) && is_pv_valid_char(*p)) p++; pvname.len = p - pvname.s; if(pvstate==1) { if(*p==PV_RNBRACKET) { /* full pv name ended here*/ goto done_inm; } else if(*p==PV_LNBRACKET) { p++; pvstate = 2; } else if(*p==PV_LIBRACKET) { p++; pvstate = 3; } else if(*p==TR_LBRACKET) { p++; pvstate = 4; } else { LM_ERR("invalid char '%c' in [%.*s] (%d)\n", *p, in->len, in->s, pvstate); goto error; } } else { if(!is_in_str(p, in)) { p--; goto done_inm; } else if(*p==PV_LNBRACKET) { p++; pvstate = 5; } else { /* still in input str, but end of PV */ /* p is increased at the end, so decrement here */ p--; goto done_inm; } } done_inm: has_inner_name = (pvstate==2||pvstate==5)?1:0; if((pte = pv_lookup_spec_name(&pvname, e, has_inner_name))==NULL) { LM_ERR("unknown script var $%.*s%s, maybe a 'loadmodule' statement " "is missing?\n", pvname.len, pvname.s,has_inner_name ? "()":""); goto error; } if(pvstate==2 || pvstate==5) { s.s = p; n = 0; while(is_in_str(p, in)) { if(*p==PV_RNBRACKET) { if(n==0) break; n--; } if(*p == PV_LNBRACKET) n++; p++; } if(!is_in_str(p, in)) goto error; if(p==s.s) { LM_ERR("pvar \"%.*s\" does not get empty name param\n", pvname.len, pvname.s); goto error; } s.len = p - s.s; if(pte->parse_name == NULL || pte->parse_name(e, &s)!=0) { LM_ERR("pvar \"%.*s\" has an invalid name param [%.*s]\n", pvname.len, pvname.s, s.len, s.s); goto error; } if(pvstate==2) { p++; if(*p==PV_RNBRACKET) { /* full pv name ended here*/ goto done_vnm; } else if(*p==PV_LIBRACKET) { p++; pvstate = 3; } else if(*p==TR_LBRACKET) { p++; pvstate = 4; } else { LM_ERR("invalid char '%c' in [%.*s] (%d)\n", *p, in->len, in->s, pvstate); goto error; } } else { if(*p==PV_RNBRACKET) { /* full pv name ended here*/ p++; goto done_all; } else { LM_ERR("invalid char '%c' in [%.*s] (%d)\n", *p, in->len, in->s, pvstate); goto error; } } } done_vnm: if(pvstate==3) { if(pte->parse_index==NULL) { LM_ERR("pvar \"%.*s\" does not get index param\n", pvname.len, pvname.s); goto error; } s.s = p; n = 0; while(is_in_str(p, in)) { if(*p==PV_RIBRACKET) { if(n==0) break; n--; } if(*p == PV_LIBRACKET) n++; p++; } if(!is_in_str(p, in)) goto error; if(p==s.s) { LM_ERR("pvar \"%.*s\" does not get empty index param\n", pvname.len, pvname.s); goto error; } s.len = p - s.s; if(pte->parse_index(e, &s)!=0) { LM_ERR("pvar \"%.*s\" has an invalid index param [%.*s]\n", pvname.len, pvname.s, s.len, s.s); goto error; } p++; if(*p==PV_RNBRACKET) { /* full pv name ended here*/ goto done_idx; } else if(*p==TR_LBRACKET) { p++; pvstate = 4; } else { LM_ERR("invalid char '%c' in [%.*s] (%d)\n", *p, in->len, in->s, pvstate); goto error; } } done_idx: if(pvstate==4) { s.s = p-1; n = 0; while(is_in_str(p, in)) { if(*p==TR_RBRACKET) { if(n==0) { /* yet another transformation */ p++; while(is_in_str(p, in) && (*p==' ' || *p=='\t')) p++; if(!is_in_str(p, in) || *p != TR_LBRACKET) { p--; break; } } n--; } if(*p == TR_LBRACKET) n++; p++; } if(!is_in_str(p, in)) goto error; if(p==s.s) { LM_ERR("pvar \"%.*s\" does not get empty index param\n", pvname.len, pvname.s); goto error; } s.len = p - s.s + 1; p = parse_transformation(&s, &tr); if(p==NULL) { LM_ERR("ERROR:bad tr in pvar name \"%.*s\"\n", pvname.len, pvname.s); goto error; } if(*p!=PV_RNBRACKET) { LM_ERR("bad pvar name \"%.*s\" (%c)!\n", in->len, in->s, *p); goto error; } e->trans = (void*)tr; } p++; done_all: if(pte!=NULL && pte->init_param) pte->init_param(e, pte->iparam); return p; error: if(p!=NULL) LM_ERR("wrong char [%c/%d] in [%.*s] at [%d (%d)]\n", *p, (int)*p, in->len, in->s, (int)(p-in->s), pvstate); else LM_ERR("invalid parsing in [%.*s] at (%d)\n", in->len, in->s, pvstate); return NULL; } /* end: pv_parse_spec */ /** * */ int pv_parse_format(str *in, pv_elem_p *el) { char *p, *p0; int n = 0; pv_elem_p e, e0; str s; if(in==NULL || in->s==NULL || el==NULL) return -1; /*LM_DBG("parsing [%.*s]\n", in->len, in->s);*/ if(in->len == 0) { *el = pkg_malloc(sizeof(pv_elem_t)); if(*el == NULL) { LM_ERR("not enough pkg memory for PV element (1)\n"); goto error; } memset(*el, 0, sizeof(pv_elem_t)); (*el)->text = *in; return 0; } p = in->s; *el = NULL; e = e0 = NULL; while(is_in_str(p,in)) { e0 = e; e = pkg_malloc(sizeof(pv_elem_t)); if(!e) { LM_ERR("not enough pkg memory for PV element (2)\n"); goto error; } memset(e, 0, sizeof(pv_elem_t)); n++; if(*el == NULL) *el = e; if(e0) e0->next = e; e->text.s = p; while(is_in_str(p,in) && *p!=PV_MARKER) p++; e->text.len = p - e->text.s; if(!is_in_str(p,in)) break; s.s = p; s.len = in->s+in->len-p; p0 = pv_parse_spec(&s, &e->spec); if(p0==NULL) { LM_ERR("parsing PV spec failed\n"); goto error; } if(!is_in_str(p0,in)) break; p = p0; } /*LM_DBG("format parsed OK: [%d] items\n", n);*/ if(*el == NULL) return -1; return 0; error: pv_elem_free_all(*el); *el = NULL; return -1; } int pv_get_spec_name(struct sip_msg* msg, pv_param_p ip, pv_value_t *name) { if(msg==NULL || ip==NULL || name==NULL) return -1; memset(name, 0, sizeof(pv_value_t)); if(ip->pvn.type==PV_NAME_INTSTR) { if(ip->pvn.u.isname.type&AVP_NAME_STR) { name->rs = ip->pvn.u.isname.name.s; name->flags = PV_VAL_STR; } else { name->ri = ip->pvn.u.isname.name.n; name->flags = PV_VAL_INT|PV_TYPE_INT; } return 0; } /* pvar */ if(pv_get_spec_value(msg, (pv_spec_p)(ip->pvn.u.dname), name)!=0) { LM_ERR("cannot get name value\n"); return -1; } if(name->flags&PV_VAL_NULL || name->flags&PV_VAL_EMPTY) { LM_ERR("null or empty name\n"); return -1; } return 0; } int pv_get_avp_name(struct sip_msg* msg, pv_param_p ip, int *avp_name, unsigned short *name_type) { pv_value_t tv; if(ip==NULL || avp_name==NULL || name_type==NULL) return -1; *avp_name = 0; *name_type = 0; if(ip->pvn.type==PV_NAME_INTSTR) { *name_type = ip->pvn.u.isname.type; *avp_name = ip->pvn.u.isname.name.n; *name_type &= AVP_SCRIPT_MASK; return 0; } /* pvar */ if(pv_get_spec_value(msg, (pv_spec_p)(ip->pvn.u.dname), &tv)!=0) { LM_ERR("cannot get avp value\n"); return -1; } if(tv.flags&PV_VAL_NULL || tv.flags&PV_VAL_EMPTY) { LM_ERR("null or empty name\n"); return -1; } if(!(tv.flags&PV_VAL_STR)) tv.rs.s = int2str(tv.ri, &tv.rs.len); /* search the name here */ *avp_name = get_avp_id(&tv.rs); if (*avp_name == 0) { LM_ERR("cannot find avp %.*s\n", tv.rs.len, tv.rs.s); return -1; } return 0; } int pv_get_spec_index(struct sip_msg* msg, pv_param_p ip, int *idx, int *flags) { pv_value_t tv; if(ip==NULL || idx==NULL || flags==NULL) return -1; *idx = 0; *flags = ip->pvi.type; if(ip->pvi.type == 0) return 0; if(ip->pvi.type == PV_IDX_ALL) { return 0; } if(ip->pvi.type == PV_IDX_INT) { *idx = ip->pvi.u.ival; return 0; } /* pvar */ if(pv_get_spec_value(msg, (pv_spec_p)ip->pvi.u.dval, &tv)!=0) { LM_ERR("cannot get index value\n"); return -1; } if(!(tv.flags & PV_VAL_INT)) { LM_ERR("invalid index value\n"); return -1; } *idx = tv.ri; return 0; } /* function to set pv value */ int pv_set_value(struct sip_msg* msg, pv_spec_p sp, int op, pv_value_t *value) { struct sip_msg* pv_msg; if(msg==NULL || sp==NULL || sp->setf==NULL || sp->type==PVT_NONE) { LM_ERR("bad parameters\n"); return -1; } if(sp->pvc && sp->pvc->contextf) { pv_msg = sp->pvc->contextf(msg); if(pv_msg == NULL || pv_msg==FAKED_REPLY) { LM_DBG("Invalid %p pv context message\n",pv_msg); return -1; } } else pv_msg = msg; return (*sp->setf)(pv_msg, &(sp->pvp), op, value); } int pv_get_spec_value(struct sip_msg* msg, pv_spec_p sp, pv_value_t *value) { int ret = 0; struct sip_msg* pv_msg; if(msg==NULL || sp==NULL || sp->getf==NULL || value==NULL || sp->type==PVT_NONE) { LM_ERR("bad parameters\n"); return -1; } memset(value, 0, sizeof(pv_value_t)); if(sp->pvc && sp->pvc->contextf) { LM_DBG("Found context function %p\n", sp->pvc->contextf); pv_msg = sp->pvc->contextf(msg); if(pv_msg == NULL || pv_msg==FAKED_REPLY) { LM_DBG("Invalid %p pv context message\n",pv_msg); return pv_get_null( NULL, NULL, value); } } else { pv_msg = msg; } ret = (*sp->getf)(pv_msg, &(sp->pvp), value); if(ret!=0) return ret; if(sp->trans) return run_transformations(pv_msg, (trans_t*)sp->trans, value); return ret; } int pv_print_spec(struct sip_msg* msg, pv_spec_p sp, char *buf, int *len) { pv_value_t tok; if(msg==NULL || sp==NULL || buf==NULL || len==NULL) return -1; if(*len <= 0) return -1; memset(&tok, 0, sizeof(pv_value_t)); /* put the value of the specifier */ if(pv_get_spec_value(msg, sp, &tok)==0) { if(tok.flags&PV_VAL_NULL) tok.rs = str_null; if(tok.rs.len < *len) memcpy(buf, tok.rs.s, tok.rs.len); else goto overflow; } *len = tok.rs.len; buf[tok.rs.len] = '\0'; return 0; overflow: LM_ERR("buffer overflow -- increase the buffer size...\n"); return -1; } int pv_printf(struct sip_msg* msg, pv_elem_p list, char *buf, int *len) { int n; pv_value_t tok; pv_elem_p it; char *cur; char *p; int l; if(msg==NULL || list==NULL || buf==NULL || len==NULL) return -1; if(*len <= 0) return -1; *buf = '\0'; cur = buf; n = 0; for (it=list; it; it=it->next) { /* put the text */ if(it->text.s && it->text.len>0) { if(n+it->text.len < *len) { memcpy(cur, it->text.s, it->text.len); n += it->text.len; cur += it->text.len; } else { LM_ERR("no more space for text [%d][%d]\n", n, it->text.len); goto overflow; } } /* put the value of the specifier */ if(it->spec.type!=PVT_NONE && pv_get_spec_value(msg, &(it->spec), &tok)==0) { if(tok.flags&PV_VAL_NULL) tok.rs = str_null; if (tok.flags&PV_VAL_STR || tok.flags&PV_VAL_NULL) { if(n+tok.rs.len < *len) { if(tok.rs.len>0) { memcpy(cur, tok.rs.s, tok.rs.len); n += tok.rs.len; cur += tok.rs.len; } } else { LM_ERR("no more space for spec value [%d][%d]\n", n, tok.rs.len); goto overflow; } } else if (tok.flags&(PV_VAL_INT|PV_TYPE_INT)){ p = int2str(tok.ri, &l); if (n+l < *len) { memcpy(cur, p, l); n += l; cur += l; } else { LM_ERR("no more space for spec value [%d][%d]\n", n, tok.rs.len); goto overflow; } } else { LM_ERR("unknown type %x\n", tok.flags); return -1; } } } goto done; overflow: LM_ERR("buffer overflow -- increase the buffer size from [%d]...\n",*len); return -1; done: #ifdef EXTRA_DEBUG LM_DBG("final buffer length %d\n", n); #endif *cur = '\0'; *len = n; return 0; } pvname_list_t* parse_pvname_list(str *in, unsigned int type) { pvname_list_t* head = NULL; pvname_list_t* al = NULL; pvname_list_t* last = NULL; char *p; pv_spec_t spec; str s; if(in==NULL || in->s==NULL) { LM_ERR("bad parameters\n"); return NULL; } p = in->s; while(is_in_str(p, in)) { while(is_in_str(p, in) && (*p==' '||*p=='\t'||*p==','||*p==';')) p++; if(!is_in_str(p, in)) { if(head==NULL) LM_ERR("wrong item name list [%.*s]\n", in->len, in->s); return head; } s.s=p; s.len = in->s+in->len-p; p = pv_parse_spec(&s, &spec); if(p==NULL || (type && spec.type!=type)) { LM_ERR("wrong item name list [%.*s]!\n", in->len, in->s); goto error; } al = (pvname_list_t*)pkg_malloc(sizeof(pvname_list_t)); if(al==NULL) { LM_ERR("no more memory!\n"); goto error; } memset(al, 0, sizeof(pvname_list_t)); memcpy(&al->sname, &spec, sizeof(pv_spec_t)); if(last==NULL) { head = al; last = al; } else { last->next = al; last = al; } } return head; error: while(head) { al = head; head=head->next; pkg_free(al); } return NULL; } int pv_elem_free_all(pv_elem_p log) { pv_elem_p t; while(log) { t = log; log = log->next; pkg_free(t); } return 0; } void pv_value_destroy(pv_value_t *val) { if(val==0) return; if(val->flags&PV_VAL_PKG) pkg_free(val->rs.s); if(val->flags&PV_VAL_SHM) shm_free(val->rs.s); memset(val, 0, sizeof(pv_value_t)); } #define PV_PRINT_BUF_SIZE 1024 #define PV_PRINT_BUF_NO 7 /*IMPORTANT NOTE - even if the function prints and returns a static buffer, it * has built-in support for 3 levels of nesting (or concurrent usage). * If you think it's not enough for you, either use pv_printf() directly, * either increase PV_PRINT_BUF_NO --bogdan */ int pv_printf_s(struct sip_msg* msg, pv_elem_p list, str *s) { static int buf_itr = 0; static char buf[PV_PRINT_BUF_NO][PV_PRINT_BUF_SIZE]; if (list->next==0 && list->spec.getf==0) { *s = list->text; return 0; } else { s->s = buf[buf_itr]; s->len = PV_PRINT_BUF_SIZE; buf_itr = (buf_itr+1)%PV_PRINT_BUF_NO; return pv_printf( msg, list, s->s, &s->len); } } void pv_spec_free(pv_spec_t *spec) { if(spec==0) return; /* TODO: free name if it is PV */ if(spec->trans) free_transformation((trans_t*)spec->trans); pkg_free(spec); } int pv_spec_dbg(pv_spec_p sp) { if(sp==NULL) { LM_DBG("spec: <>\n"); return 0; } LM_DBG("\n"); LM_DBG("type: %d\n", sp->type); LM_DBG("getf: %p\n", sp->getf); LM_DBG("setf: %p\n", sp->setf); LM_DBG("tran: %p\n", sp->trans); LM_DBG("\n"); LM_DBG("\n"); LM_DBG("type: %d\n", sp->pvp.pvn.type); if(sp->pvp.pvn.type==PV_NAME_INTSTR) { LM_DBG("sub-type: %d\n", sp->pvp.pvn.u.isname.type); if (sp->pvp.pvn.u.isname.type&AVP_NAME_STR) { LM_DBG("name str: %.*s\n", sp->pvp.pvn.u.isname.name.s.len, sp->pvp.pvn.u.isname.name.s.s); } else { LM_DBG("name in: %d\n", sp->pvp.pvn.u.isname.name.n); } } else if(sp->pvp.pvn.type==PV_NAME_PVAR) { pv_spec_dbg((pv_spec_p)sp->pvp.pvn.u.dname); } else { LM_DBG("name: unknown\n"); } LM_DBG("\n"); LM_DBG("\n"); LM_DBG("type: %d\n", sp->pvp.pvi.type); if(sp->pvp.pvi.type==PV_IDX_INT) { LM_DBG("index: %d\n", sp->pvp.pvi.u.ival); } else if(sp->pvp.pvi.type==PV_IDX_PVAR) { pv_spec_dbg((pv_spec_p)sp->pvp.pvi.u.dval); } else if(sp->pvp.pvi.type==PV_IDX_ALL){ LM_DBG("index: *\n"); } else { LM_DBG("index: unknown\n"); } LM_DBG("\n"); LM_DBG("\n"); LM_DBG("name.s==NULL || e->getf==NULL || e->type==PVT_NONE) { LM_ERR("invalid parameters\n"); return -1; } if(_pv_extra_list==0) { LM_DBG("extra items list is not initialized\n"); if(pv_init_extra_list()!=0) { LM_ERR("cannot intit extra list\n"); return -1; } } in = &(e->name); p = in->s; while(is_in_str(p,in) && is_pv_valid_char(*p)) p++; if(is_in_str(p,in)) { LM_ERR("invalid char [%c] in [%.*s]\n", *p, in->len, in->s); return -1; } found = 0; pvi = *_pv_extra_list; while(pvi) { if(pvi->pve.name.len > in->len) break; if(pvi->pve.name.len==in->len) { found = strncmp(pvi->pve.name.s, in->s, in->len); if(found>0) break; if(found==0) { LM_ERR("pvar [%.*s] already exists\n", in->len, in->s); return -1; } } pvj = pvi; pvi = pvi->next; } pvn = (pv_extra_t*)pkg_malloc(sizeof(pv_extra_t)); if(pvn==0) { LM_ERR("no more memory\n"); return -1; } memcpy(pvn, e, sizeof(pv_extra_t)); pvn->pve.type += PVT_EXTRA; if(pvj==0) { pvn->next = *_pv_extra_list; *_pv_extra_list = pvn; goto done; } pvn->next = pvj->next; pvj->next = pvn; done: return 0; } int register_pvars_mod(char *mod_name, pv_export_t *items) { int ret; int i; if (items==0) return 0; for ( i=0 ; items[i].name.s ; i++ ) { ret = pv_add_extra(&items[i]); if (ret!=0) { LM_ERR("failed to register pseudo-variable <%.*s> for module %s\n", items[i].name.len, items[i].name.s, mod_name); } } return 0; } /** * */ int pv_free_extra_list(void) { pv_extra_p xe; pv_extra_p xe1; if(_pv_extra_list!=0) { xe = *_pv_extra_list; while(xe!=0) { xe1 = xe; xe = xe->next; pkg_free(xe1); } pkg_free(_pv_extra_list); _pv_extra_list = 0; } return 0; } pv_context_t* new_pv_context(str* name, pv_contextf_t get_context) { pv_context_t* pvc_new = NULL; int size; /* if(get_context == NULL) { LM_ERR("NULL pointer to function\n"); return 0; } */ size = sizeof(pv_context_t) + name->len; pvc_new = (pv_context_t*)pkg_malloc(size); if(pvc_new == NULL) { LM_ERR("No more memory\n"); return 0; } memset(pvc_new, 0, size); pvc_new->name.s = (char*)pvc_new + sizeof(pv_context_t); memcpy(pvc_new->name.s, name->s, name->len); pvc_new->name.len = name->len; pvc_new->contextf = get_context; return pvc_new; } int register_pv_context(char* cname, pv_contextf_t get_context) { pv_context_t* pvc = pv_context_lst; str name; if(cname == NULL) { LM_DBG("NULL parameter\n"); return -1; } name.s = cname; name.len = strlen(cname); LM_DBG("Registered new context: %.*s / %p\n", name.len, name.s, get_context); pvc = pv_get_context(&name); if(pvc == NULL) { LM_DBG("Context not found\n"); if(add_pv_context(&name, get_context) == NULL) { LM_ERR("Failed to add context\n"); return -1; } return 1; } if(pvc->contextf!=NULL) { LM_ERR("Context already registered [%s]\n", cname); return -1; } if(get_context == NULL) { LM_ERR("NULL context getter function\n"); return -1; } pvc->contextf= get_context; return 1; } /* function to register a pv context getter */ pv_context_t* add_pv_context(str* name, pv_contextf_t get_context) { pv_context_t* pvc = pv_context_lst; pv_context_t* pvc_new, *pvc_prev; if(pvc == NULL) { pvc_new = new_pv_context(name, get_context); if(pvc_new == NULL) { LM_ERR("Failed to allocate context\n"); return 0; } pv_context_lst = pvc_new; return pvc_new; } while(pvc) { if(pvc->name.len == name->len && strncmp(pvc->name.s, name->s, name->len)==0) { LM_ERR("PV Context already registered [%.*s]\n", name->len, name->s); return 0; } pvc_prev = pvc; pvc = pvc->next; } pvc_new = new_pv_context(name, get_context); if(pvc_new == NULL) { LM_ERR("Failed to allocate context\n"); return 0; } LM_DBG("Registered new context: %.*s\n", name->len, name->s); pvc_prev->next = pvc_new; return pvc_new; } pv_context_t* pv_get_context(str* name) { pv_context_t* pvc = pv_context_lst; while(pvc) { if(pvc->name.len == name->len && strncmp(pvc->name.s, name->s, name->len) == 0) { return pvc; } pvc = pvc->next; } return 0; } int pv_contextlist_check(void) { pv_context_t* pvc = pv_context_lst; while(pvc) { if(pvc->contextf == NULL) return -1; pvc = pvc->next; } pvc_before_check = 0; return 0; } /* argument options '-o' */ argv_p argv_vars = NULL; argv_p search_argv(str *name) { argv_p it; for (it = argv_vars; it; it = it->next) { if (it->name.len == name->len && !strncmp(it->name.s, name->s, name->len)) return it; } return 0; } int add_arg_var(char *opt) { char *eq; str name; argv_p new = NULL; if (!opt) { LM_ERR("cannot receive null option\n"); return -1; } eq = strchr(opt, '='); if (!eq) { LM_ERR("invalid option format - '=' char cannot be found\n"); return -1; } if (eq <= opt) { LM_ERR("no option name specified\n"); return -1; } name.s = opt; name.len = eq - name.s; /* check for duplicate option name */ if (search_argv(&name)) { LM_ERR("duplicate option name <%.*s>\n", name.len, name.s); return -1; } new = (argv_p)pkg_malloc(sizeof(argv_t)); if (!new) { LM_ERR("no more pkg memory\n"); return -1; } memset(new, 0, sizeof(argv_t)); new->name.s = name.s; new->name.len = name.len; new->value.s = eq+1; new->value.len = strlen(opt) + opt - new->value.s; if (!new->value.len) new->value.s = 0; new->next = argv_vars; argv_vars = new; LM_DBG("added argument name <%.*s> = <%.*s>\n", name.len, name.s, new->value.len, new->value.s); return 0; } int pv_parse_argv_name(pv_spec_p sp, str *in) { argv_p v_arg; if(in==NULL || in->s==NULL || sp==NULL) return -1; v_arg = search_argv(in); if (!v_arg) { LM_DBG("$argv(%.*s) not found\n", in->len, in->s); sp->pvp.pvv.len = 0; sp->pvp.pvv.s = 0; } else { sp->pvp.pvv = v_arg->value; sp->pvp.pvn.u.isname.name.s = v_arg->name; } sp->pvp.pvn.type = PV_NAME_PVAR; return 0; } int pv_get_argv(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if (!param) { LM_ERR("null parameter received\n"); return -1; } if (param->pvv.len == 0 || !param->pvv.s) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, ¶m->pvv); } static int pv_parse_param_name(pv_spec_p sp, str *in) { char *p; char *s; pv_spec_p nsp = 0; if(in==NULL || in->s==NULL || sp==NULL) return -1; p = in->s; if(*p==PV_MARKER) { nsp = (pv_spec_p)pkg_malloc(sizeof(pv_spec_t)); if(nsp==NULL) { LM_ERR("no more memory\n"); return -1; } s = pv_parse_spec(in, nsp); if(s==NULL) { LM_ERR("invalid name [%.*s]\n", in->len, in->s); pv_spec_free(nsp); return -1; } sp->pvp.pvn.type = PV_NAME_PVAR; sp->pvp.pvn.u.dname = (void*)nsp; return 0; } /*LM_DBG("static name [%.*s]\n", in->len, in->s);*/ /* always an int type from now */ sp->pvp.pvn.u.isname.type = 0; sp->pvp.pvn.type = PV_NAME_INTSTR; if (str2int(in, (unsigned int *)&sp->pvp.pvn.u.isname.name.n) < 0) { LM_ERR("bad param index [%.*s]\n", in->len, in->s); return -1; } return 0; } static int pv_get_param(struct sip_msg *msg, pv_param_t *ip, pv_value_t *res) { int index; pv_value_t tv; if (!ip) { LM_ERR("null parameter received\n"); return -1; } if (route_rec_level == -1 || !route_params[route_rec_level] || route_params_number[route_rec_level] == 0) { LM_DBG("no parameter specified for this route\n"); return pv_get_null(msg, ip, res); } if(ip->pvn.type==PV_NAME_INTSTR) { index = ip->pvn.u.isname.name.n; } else { /* pvar -> it might be another $param variable! */ route_rec_level--; if(pv_get_spec_value(msg, (pv_spec_p)(ip->pvn.u.dname), &tv)!=0) { LM_ERR("cannot get spec value\n"); return -1; } route_rec_level++; if(tv.flags&PV_VAL_NULL || tv.flags&PV_VAL_EMPTY) { LM_ERR("null or empty name\n"); return -1; } if (!(tv.flags&PV_VAL_INT) || str2int(&tv.rs,(unsigned int*)&index) < 0) { LM_ERR("invalid index <%.*s>\n", tv.rs.len, tv.rs.s); return -1; } } if (index < 1 || index > route_params_number[route_rec_level]) { LM_DBG("no such parameter index %d\n", index); return pv_get_null(msg, ip, res); } /* the parameters start at 0, whereas the index starts from 1 */ index--; switch (route_params[route_rec_level][index].type) { case NULLV_ST: res->rs.s = NULL; res->rs.len = res->ri = 0; res->flags = PV_VAL_NULL; break; case STRING_ST: res->rs.s = route_params[route_rec_level][index].u.string; res->rs.len = strlen(res->rs.s); res->flags = PV_VAL_STR; break; case NUMBER_ST: res->rs.s = int2str(route_params[route_rec_level][index].u.number, &res->rs.len); res->ri = route_params[route_rec_level][index].u.number; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; break; case SCRIPTVAR_ST: route_rec_level--; if(pv_get_spec_value(msg, (pv_spec_p)route_params[route_rec_level + 1][index].u.data, res)!=0) { LM_ERR("cannot get spec value\n"); return -1; } route_rec_level++; break; default: LM_ALERT("BUG: invalid parameter type %d\n", route_params[route_rec_level][index].type); return -1; } return 0; } void destroy_argv_list(void) { argv_p arg; while (argv_vars) { arg = argv_vars; argv_vars = argv_vars->next; pkg_free(arg); } } opensips-2.2.2/pvar.h000066400000000000000000000222501300170765700145050ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Definitions for Pseudo-variable support */ #ifndef _PVAR_H_ #define _PVAR_H_ #include "str.h" #include "usr_avp.h" #include "parser/msg_parser.h" #define PV_MARKER_STR "$" #define PV_MARKER '$' #define PV_LNBRACKET_STR "(" #define PV_LNBRACKET '(' #define PV_RNBRACKET_STR ")" #define PV_RNBRACKET ')' #define PV_LIBRACKET_STR "[" #define PV_LIBRACKET '[' #define PV_RIBRACKET_STR "]" #define PV_RIBRACKET ']' #define PV_LCBRACKET '<' #define PV_LCBRACKET_STR "<" #define PV_RCBRACKET '>' #define PV_RCBRACKET_STR ">" #define PV_VAL_NONE 0 #define PV_VAL_NULL 1 #define PV_VAL_EMPTY 2 #define PV_VAL_STR 4 #define PV_VAL_INT 8 #define PV_TYPE_INT 16 #define PV_VAL_PKG 32 #define PV_VAL_SHM 64 #define PV_NAME_INTSTR 0 #define PV_NAME_PVAR 1 #define PV_IDX_INT 3 #define PV_IDX_PVAR 1 #define PV_IDX_ALL 2 /*! if PV name is dynamic, integer, or str */ #define pv_has_dname(pv) ((pv)->pvp.pvn.type==PV_NAME_PVAR) #define pv_has_iname(pv) ((pv)->pvp.pvn.type==PV_NAME_INTSTR \ && !((pv)->pvp.pvn.u.isname.type&AVP_NAME_STR)) #define pv_has_sname(pv) ((pv)->pvp.pvn.type==PV_NAME_INTSTR \ && (pv)->pvp.pvn.u.isname.type&AVP_NAME_STR) #define pv_is_w(pv) ((pv)->setf!=NULL) enum _pv_type { PVT_NONE=0, PVT_EMPTY, PVT_NULL, PVT_MARKER, PVT_AVP, PVT_HDR, PVT_PID, PVT_RETURN_CODE, PVT_TIMES, PVT_TIMEF, PVT_MSGID, PVT_METHOD, PVT_STATUS, PVT_REASON, PVT_RURI, PVT_RURI_USERNAME, PVT_RURI_DOMAIN, PVT_RURI_PORT, PVT_FROM, PVT_FROM_USERNAME, PVT_FROM_DOMAIN, PVT_FROM_TAG, PVT_TO, PVT_TO_USERNAME, PVT_TO_DOMAIN, PVT_TO_TAG, PVT_CSEQ, PVT_CONTACT, PVT_CALLID, PVT_USERAGENT, PVT_MSG_BUF, PVT_MSG_LEN, PVT_FLAGS, PVT_HEXFLAGS, PVT_SRCIP, PVT_SRCPORT, PVT_RCVIP, PVT_RCVPORT, PVT_REFER_TO, PVT_DSET, PVT_DSTURI, PVT_COLOR, PVT_BRANCH, PVT_BRANCHES, PVT_CONTENT_TYPE, PVT_CONTENT_LENGTH, PVT_MSG_BODY, PVT_AUTH_USERNAME, PVT_AUTH_REALM, PVT_RURI_PROTOCOL, PVT_DSTURI_DOMAIN, PVT_DSTURI_PORT, PVT_DSTURI_PROTOCOL, PVT_FROM_DISPLAYNAME, PVT_TO_DISPLAYNAME, PVT_OURI, PVT_OURI_USERNAME, PVT_OURI_DOMAIN, PVT_OURI_PORT, PVT_OURI_PROTOCOL, PVT_FORCE_SOCK, PVT_RPID_URI, PVT_DIVERSION_URI, PVT_ACC_USERNAME, PVT_PPI, PVT_PPI_DISPLAYNAME, PVT_PPI_DOMAIN, PVT_PPI_USERNAME, PVT_PAI_URI, PVT_BFLAGS, PVT_HEXBFLAGS, PVT_SFLAGS, PVT_HEXSFLAGS, PVT_ERR_CLASS, PVT_ERR_LEVEL, PVT_ERR_INFO, PVT_ERR_RCODE, PVT_ERR_RREASON, PVT_SCRIPTVAR, PVT_PROTO, PVT_AUTH_USERNAME_WHOLE, PVT_AUTH_DURI, PVT_DIV_REASON, PVT_DIV_PRIVACY, PVT_AUTH_DOMAIN, PVT_AUTH_NONCE, PVT_AUTH_RESPONSE, PVT_TIME, PVT_PATH, PVT_ARGV, PVT_HDRCNT, PVT_AUTH_NONCE_COUNT, PVT_AUTH_QOP, PVT_AUTH_ALGORITHM, PVT_AUTH_OPAQUE, PVT_AUTH_CNONCE, PVT_RU_Q, PVT_ROUTE_PARAM, PVT_ROUTE_TYPE, PVT_LINE_NUMBER, PVT_CFG_FILE_NAME, PVT_LOG_LEVEL, /* registered by json module */ PVT_JSON, PVT_EXTRA /* keep it last */ }; typedef enum _pv_type pv_type_t; typedef int pv_flags_t; typedef struct _pv_value { str rs; /*!< string value */ int ri; /*!< integer value */ int flags; /*!< flags about the type of value */ } pv_value_t, *pv_value_p; typedef struct _pv_name { int type; /*!< type of name */ union { struct { int type; /*!< type of int_str name - compatibility with AVPs */ int_str name; /*!< the value of the name */ } isname; void *dname; /*!< PV value - dynamic name */ } u; } pv_name_t, *pv_name_p; typedef struct _pv_index { int type; /*!< type of PV index */ union { int ival; /*!< integer value */ void *dval; /*!< PV value - dynamic index */ } u; } pv_index_t, *pv_index_p; typedef struct _pv_param { pv_name_t pvn; /*!< PV name */ pv_index_t pvi; /*!< PV index */ str pvv; /*!< PV value buffer */ } pv_param_t, *pv_param_p; typedef int (*pv_getf_t) (struct sip_msg*, pv_param_t*, pv_value_t*); typedef int (*pv_setf_t) (struct sip_msg*, pv_param_t*, int, pv_value_t*); typedef struct sip_msg* (*pv_contextf_t) (struct sip_msg*); typedef struct pv_context { str name; pv_contextf_t contextf; struct pv_context* next; }pv_context_t; typedef struct _pv_spec { pv_type_t type; /*!< type of PV */ pv_getf_t getf; /*!< get PV value function */ pv_setf_t setf; /*!< set PV value function */ pv_param_t pvp; /*!< parameter to be given to get/set functions */ pv_context_t* pvc; /*< get pv context function */ void *trans; /*!< transformations */ } pv_spec_t, *pv_spec_p; typedef int (*pv_parse_name_f)(pv_spec_p sp, str *in); typedef int (*pv_parse_index_f)(pv_spec_p sp, str *in); typedef int (*pv_init_param_f)(pv_spec_p sp, int param); /*! \brief * PV spec format: * - $class_name * - $class_name(inner_name) * - $(class_name[index]) * - $(class_name(inner_name)[index]) * - $(class_name{transformation}) * - $(class_name(inner_name){transformation}) * - $(class_name[index]{transformation}) * - $(class_name(inner_name)[index]{transformation}) */ typedef struct _pv_export { str name; /*!< class name of PV */ pv_type_t type; /*!< type of PV */ pv_getf_t getf; /*!< function to get the value */ pv_setf_t setf; /*!< function to set the value */ pv_parse_name_f parse_name; /*!< function to parse the inner name */ pv_parse_index_f parse_index; /*!< function to parse the index of PV */ pv_init_param_f init_param; /*!< function to init the PV spec */ int iparam; /*!< parameter for the init function */ } pv_export_t; typedef struct _pv_elem { str text; pv_spec_t spec; struct _pv_elem *next; } pv_elem_t, *pv_elem_p; char* pv_parse_spec(str *in, pv_spec_p sp); int pv_get_spec_value(struct sip_msg* msg, pv_spec_p sp, pv_value_t *value); int pv_print_spec(struct sip_msg* msg, pv_spec_p sp, char *buf, int *len); int pv_printf(struct sip_msg* msg, pv_elem_p list, char *buf, int *len); int pv_elem_free_all(pv_elem_p log); void pv_value_destroy(pv_value_t *val); void pv_spec_free(pv_spec_t *spec); int pv_spec_dbg(pv_spec_p sp); int pv_get_spec_index(struct sip_msg* msg, pv_param_p ip, int *idx, int *flags); int pv_get_avp_name(struct sip_msg* msg, pv_param_p ip, int *avp_name, unsigned short *name_type); int pv_get_spec_name(struct sip_msg* msg, pv_param_p ip, pv_value_t *name); int pv_parse_format(str *in, pv_elem_p *el); int pv_init_iname(pv_spec_p sp, int param); int pv_printf_s(struct sip_msg* msg, pv_elem_p list, str *s); int pv_set_value(struct sip_msg* msg, pv_spec_p sp, int op, pv_value_t *val); typedef struct _pvname_list { pv_spec_t sname; struct _pvname_list *next; } pvname_list_t, *pvname_list_p; typedef struct pv_spec_list { pv_spec_p spec; struct pv_spec_list *next; } pv_spec_list_t, *pv_spec_list_p; pvname_list_t* parse_pvname_list(str *in, unsigned int type); int register_pvars_mod(char *mod_name, pv_export_t *items); int pv_free_extra_list(void); /*! \brief PV helper functions */ int pv_parse_index(pv_spec_p sp, str *in); int pv_parse_avp_name(pv_spec_p sp, str *in); int pv_get_null(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); int pv_get_uintval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, unsigned int uival); int pv_get_sintval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, int sival); int pv_get_strval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, str *sval); int pv_get_strintval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, str *sval, int ival); int pv_get_intstrval(struct sip_msg *msg, pv_param_t *param, pv_value_t *res, int ival, str *sval); int register_pv_context(char* name, pv_contextf_t get_context); int pv_contextlist_check(void); /* command line arguments specified with '-o' */ typedef struct argv { str name; str value; struct argv *next; } argv_t, *argv_p; int add_arg_var(char *opt); int pv_parse_argv_name(pv_spec_p sp, str *in); int pv_get_argv(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); void destroy_argv_list(void); #endif opensips-2.2.2/qvalue.c000066400000000000000000000060351300170765700150300ustar00rootroot00000000000000/* * Handling of the q value * * Copyright (C) 2004 FhG FOKUS * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Functions related to the SIP q value */ #include "error.h" #include "qvalue.h" /* * Convert string representation of q parameter in qvalue_t */ int str2q(qvalue_t* q, char* s, int len) { int i, digits, order; /* States and equivalent regular expressions of input */ enum { ST_START, /* (SPC|TAB)* */ ST_0, /* 0+ */ ST_1, /* 1 */ ST_0_PT, /* 0*\. */ ST_1_PT, /* 1\. */ ST_1_PT_0, /* 1\.0+ */ ST_0_PT_N /* 0*\.[0-9]+ */ } state = ST_START; if (!q || !s) { return E_INVALID_PARAMS; } digits = 1; order = 100; for(i = 0; i < len; i++) { switch(state) { case ST_START: switch(s[i]) { case ' ': case '\t': break; case '0': *q = 0; state = ST_0; break; case '1': *q = 1000; state = ST_1; break; case '.': *q = 0; state = ST_0_PT; break; default: return E_Q_INV_CHAR; } break; case ST_0: switch(s[i]) { case '0': break; case '.': state = ST_0_PT; break; case '1': *q = 1000; state = ST_1; break; default: if (s[i] >= '2' && s[i] <= '9') { return E_Q_TOO_BIG; } else { return E_Q_INV_CHAR; } } break; case ST_1: if (s[i] == '.') { state = ST_1_PT; break; } else { if (s[i] >= '0' && s[i] <= '9') { return E_Q_TOO_BIG; } else { return E_Q_INV_CHAR; } } break; case ST_0_PT: if (s[i] >= '0' && s[i] <= '9') { *q = (s[i] - '0') * order; order /= 10; state = ST_0_PT_N; } else { return E_Q_INV_CHAR; } break; case ST_1_PT: if (s[i] == '0') { state = ST_1_PT_0; } else { if (s[i] >= '1' && s[i] <= '9') { return E_Q_TOO_BIG; } else { return E_Q_INV_CHAR; } } break; case ST_1_PT_0: if (s[i] == '0') { break; } else { if (s[i] >= '1' && s[i] <= '9') { return E_Q_TOO_BIG; } else { return E_Q_INV_CHAR; } } break; case ST_0_PT_N: if (s[i] >= '0' && s[i] <= '9') { if (digits <= 3) { *q += (s[i] - '0') * order; order /= 10; digits++; } } else { return E_Q_INV_CHAR; } break; } } if (state == ST_START) return E_Q_EMPTY; return 0; } opensips-2.2.2/qvalue.h000066400000000000000000000100011300170765700150210ustar00rootroot00000000000000/* * Handling of the q value * * Copyright (C) 2004 FhG FOKUS * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Functions related to the SIP q value */ #ifndef _QVALUE_H #define _QVALUE_H 1 #include /*! \page QvalueHandling Q Value Handling * * The q value expresses the priority of a URI within a set of URIs * (Contact header field in the same SIP message or dset array in * ser. The higher is the q value of a URI the higher is the priority * of the URI. * * The q value is usually expressed as a floating point number with * limited number of decimal digits, for example 0.346. RFC3261 allows * 0-3 decimal digits. * * To speed things up we represent the q value as integer number, it * is then easier to handle/print the value. To convert float into * integer we multiply the q value by 1000, i.e. * (float)0.567 == (int)567. In the opposite direction, values * higher or equal to 1000 are converted to 1.0 and values below or * equal to 0 are converted to 0. * * Value Q_UNSPECIFIED (which is in fact -1) has a special meaning, it * means that the q value is not known and the parameter should not be * printed when printing Contacts, implementations will then use * implementation specific pre-defined values. */ typedef int qvalue_t; /*! \brief * Use this if the value of q is not specified */ #define Q_UNSPECIFIED ((qvalue_t)-1) #define MAX_Q ((qvalue_t)1000) #define MIN_Q ((qvalue_t)0) #define MAX_Q_STR "1" #define MAX_Q_STR_LEN (sizeof(MAX_Q_STR) - 1) #define MIN_Q_STR "0" #define MIN_Q_STR_LEN (sizeof(MIN_Q_STR) - 1) #define Q_PREFIX "0." #define Q_PREFIX_LEN (sizeof(Q_PREFIX) - 1) #define qverr2str(rc) \ (rc == E_Q_INV_CHAR ? "bad characters" : \ rc == E_Q_EMPTY ? "empty value" : \ rc == E_Q_TOO_BIG ? "max value is 1.0" : "bad qvalue") /*! \brief * Calculate the length of printed q */ static inline size_t len_q(qvalue_t q) { if (q == Q_UNSPECIFIED) { return 0; } else if (q >= MAX_Q) { return MAX_Q_STR_LEN; } else if (q <= MIN_Q) { return MIN_Q_STR_LEN; } else if (q % 100 == 0) { return Q_PREFIX_LEN + 1; } else if (q % 10 == 0) { return Q_PREFIX_LEN + 2; } else { return Q_PREFIX_LEN + 3; } } /*! \brief * Convert qvalue_t to double */ static inline double q2double(qvalue_t q) { if (q == Q_UNSPECIFIED) { return -1; } else { return (double)((double)q / (double)1000); } } /*! \brief * Convert double to qvalue_t */ static inline qvalue_t double2q(double q) { if (q == -1) { return Q_UNSPECIFIED; } else { return q * 1000; } } /*! \brief * Convert q value to string */ static inline char* q2str(qvalue_t q, unsigned int* len) { static char buf[sizeof("0.123")]; char* p; p = buf; if (q == Q_UNSPECIFIED) { /* Do nothing */ } else if (q >= MAX_Q) { memcpy(p, MAX_Q_STR, MAX_Q_STR_LEN); p += MAX_Q_STR_LEN; } else if (q <= MIN_Q) { memcpy(p, MIN_Q_STR, MIN_Q_STR_LEN); p += MIN_Q_STR_LEN; } else { memcpy(p, Q_PREFIX, Q_PREFIX_LEN); p += Q_PREFIX_LEN; *p++ = q / 100 + '0'; q %= 100; if (!q) goto end; *p++ = q / 10 + '0'; q %= 10; if (!q) goto end; *p++ = q + '0'; } end: *p = '\0'; if (len) { *len = p - buf; } return buf; } /*! \brief * Convert string representation of q parameter in qvalue_t */ int str2q(qvalue_t* q, char* s, int len); #endif /* _QVALUE_H */ opensips-2.2.2/radius.h000066400000000000000000000066331300170765700150330ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * WARNING: Don't forget to update the dictionary if you update this file !!! * * History: * -------- * 2005-06-28 multi leg call support added (bogdan) * */ /*! * \file * \brief Radius support */ #ifndef _RADIUS_CORE_H #define _RADIUS_CORE_H #ifndef USE_FREERADIUS #include #define DEFAULT_RADIUSCLIENT_CONF \ "/usr/local/etc/radiusclient-ng/radiusclient.conf" #else #include #define DEFAULT_RADIUSCLIENT_CONF "" #endif struct attr { const char *n; int v; }; struct val { const char *n; int v; }; #define A_USER_NAME 0 #define A_SERVICE_TYPE 1 #define A_CALLED_STATION_ID 2 #define A_CALLING_STATION_ID 3 #define A_ACCT_STATUS_TYPE 4 #define A_ACCT_SESSION_ID 5 #define A_SIP_METHOD 6 #define A_SIP_RESPONSE_CODE 7 #define A_SIP_CSEQ 8 #define A_SIP_TO_TAG 9 #define A_SIP_FROM_TAG 10 #define A_DIGEST_RESPONSE 11 #define A_DIGEST_ATTRIBUTES 12 #define A_SIP_URI_USER 13 #define A_SIP_URI_HOST 14 #define A_DIGEST_REALM 15 #define A_DIGEST_NONCE 16 #define A_DIGEST_METHOD 17 #define A_DIGEST_URI 18 #define A_DIGEST_QOP 19 #define A_DIGEST_ALGORITHM 20 #define A_DIGEST_BODY_DIGEST 21 #define A_DIGEST_CNONCE 22 #define A_DIGEST_NONCE_COUNT 23 #define A_DIGEST_USER_NAME 24 #define A_SIP_GROUP 25 #define A_CISCO_AVPAIR 26 #define A_SIP_AVP 27 #define A_TIME_STAMP 28 #define A_SIP_CALL_ID 29 #define A_SIP_REQUEST_HASH 30 #define A_MAX 31 #define V_STATUS_START 0 #define V_STATUS_STOP 1 #define V_STATUS_FAILED 2 #define V_CALL_CHECK 3 #define V_SIP_SESSION 4 #define V_GROUP_CHECK 5 #define V_SIP_CALLER_AVPS 6 #define V_SIP_CALLEE_AVPS 7 #define V_SIP_VERIFY_DESTINATION 8 #define V_SIP_VERIFY_SOURCE 9 #define V_MAX 10 #define INIT_AV(rh, at, nr_at, vl, nr_vl, fn, e1, e2) \ { \ int i; \ DICT_ATTR *da; \ DICT_VALUE *dv; \ \ for (i = 0; i < nr_at; i++) { \ if (at[i].n == NULL) \ continue; \ da = rc_dict_findattr(rh, at[i].n); \ if (da == NULL) { \ LM_ERR("%s: can't get code for the " \ "%s attribute\n", fn, at[i].n); \ return e1; \ } \ at[i].v = da->value; \ } \ for (i = 0; i < nr_vl; i++) { \ if (vl[i].n == NULL) \ continue; \ dv = rc_dict_findval(rh, vl[i].n); \ if (dv == NULL) { \ LM_ERR("%s: can't get code for the " \ "%s attribute value\n", fn, vl[i].n);\ return e2; \ } \ vl[i].v = dv->value; \ } \ } #endif opensips-2.2.2/re.c000066400000000000000000000343521300170765700141440ustar00rootroot00000000000000/* * regexp and regexp substitutions implementations * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * -------- * 2003-08-04 created by andrei * 2004-11-12 minor api extension, added *count (andrei) * 2007-07-27 split function for parsing of replacing string (ancuta) */ /*! * \file * \brief Regexp and regexp substitutions implementations */ #include "dprint.h" #include "mem/mem.h" #include "re.h" #include void subst_expr_free(struct subst_expr* se) { if (se->replacement.s) pkg_free(se->replacement.s); if (se->re) { regfree(se->re); pkg_free(se->re); }; pkg_free(se); } /*! \brief frees the entire list, head (l) too */ void replace_lst_free(struct replace_lst* l) { struct replace_lst* t; while (l){ t=l; l=l->next; if (t->rpl.s) pkg_free(t->rpl.s); pkg_free(t); } } #define MAX_REPLACE_WITH 100 int parse_repl(struct replace_with * rw, char ** begin, char * end, int *max_token_nb, int with_sep) { char* p0; char * repl; str s; int token_nb; int escape; int max_pmatch; char *p, c; /* parse replacement */ p = *begin; c = *p; if(with_sep) p++; repl= p; token_nb=0; max_pmatch=0; escape=0; for(;p=MAX_REPLACE_WITH){ LM_ERR("too many escapes in the replace part %s\n", *begin); goto error; } }else if (*p=='\\') { escape=1; }else if (*p==PV_MARKER) { s.s = p; s.len = end - s.s; p0 = pv_parse_spec(&s, &rw[token_nb].u.spec); if(p0==NULL) { LM_ERR("bad specifier in replace part %s\n", *begin); goto error; } rw[token_nb].size=p0-p; rw[token_nb].offset=p-repl; rw[token_nb].type=REPLACE_SPEC; token_nb++; p=p0-1; }else if (*p==c && with_sep){ goto found_repl; } } if(with_sep){ LM_ERR("missing separator: %s\n", *begin); goto error; } found_repl: *max_token_nb = max_pmatch; *begin = p; return token_nb; error: return -1; } /*! \brief Parse a /regular expression/replacement/flags into a subst_expr structure */ struct subst_expr* subst_parser(str* subst) { char c; char* end; char* p; char* re; char* re_end; char* repl; char* repl_end; struct replace_with rw[MAX_REPLACE_WITH]; int rw_no; //int escape; int cflags; /* regcomp flags */ int replace_all; struct subst_expr* se; regex_t* regex; int max_pmatch; int r; /* init */ se=0; regex=0; cflags=REG_EXTENDED | REG_NEWLINE; /* don't match newline */ replace_all=0; if (subst->len<3){ LM_ERR("expression is too short: %.*s\n", subst->len, subst->s); goto error; } p=subst->s; end=subst->s+subst->len; c=*p; if (c=='\\'){ LM_ERR("invalid separator char <%c> in %.*s\n", c, subst->len, subst->s); goto error; } p++; /* find re */ re=p; for (;plen, subst->s); goto error; found_re: re_end=p; if(end< (p+2) ){ LM_ERR("string too short\n"); goto error; } repl=p+1; if((rw_no = parse_repl(rw, &p, end, &max_pmatch, WITH_SEP))< 0) goto error; repl_end=p; p++; /* parse flags */ for(;plen, subst->s); goto error; } } /* compile the re */ if ((regex=pkg_malloc(sizeof(regex_t)))==0){ LM_ERR("out of pkg memory (re)\n"); goto error; } c=*re_end; /* regcomp expects null terminated strings -- save */ *re_end=0; if (regcomp(regex, re, cflags)!=0){ pkg_free(regex); *re_end=c; /* restore */ LM_ERR("bad regular expression %.*s in %.*s\n", (int)(re_end-re), re, subst->len, subst->s); goto error; } *re_end=c; /* restore */ /* construct the subst_expr structure */ se=pkg_malloc(sizeof(struct subst_expr)+ ((rw_no)?(rw_no-1)*sizeof(struct replace_with):0)); /* 1 replace_with structure is already included in subst_expr */ if (se==0){ LM_ERR("out of pkg memory (subst_expr)\n"); goto error; } memset((void*)se, 0, sizeof(struct subst_expr)); se->replacement.len=repl_end-repl; if ((se->replacement.s=pkg_malloc(se->replacement.len))==0){ LM_ERR("out of pkg memory (replacement)\n"); goto error; } /* start copying */ memcpy(se->replacement.s, repl, se->replacement.len); se->re=regex; se->replace_all=replace_all; se->n_escapes=rw_no; se->max_pmatch=max_pmatch; for (r=0; rreplace[r]=rw[r]; LM_DBG("ok, se is %p\n", se); return se; error: if (se) { subst_expr_free(se); regex=0; } if (regex) { regfree (regex); pkg_free(regex); } return 0; } #if 0 static int replace_len(const char* match, int nmatch, regmatch_t* pmatch, struct subst_expr* se, struct sip_msg* msg) { int r; int len; str* uri; len=se->replacement.len; for (r=0; rn_escapes; r++){ switch(se->replace[r].type){ case REPLACE_NMATCH: len-=se->replace[r].size; if ((se->replace[r].u.nmatchreplace[r].u.nmatch].rm_so!=-1)){ /* do the replace */ len+=pmatch[se->replace[r].u.nmatch].rm_eo- pmatch[se->replace[r].u.nmatch].rm_so; }; break; case REPLACE_CHAR: len-=(se->replace[r].size-1); break; case REPLACE_URI: len-=se->replace[r].size; if (msg->first_line.type!=SIP_REQUEST){ LM_CRIT("uri substitution on a reply\n"); break; /* ignore, we can continue */ } uri= (msg->new_uri.s)?(&msg->new_uri): (&msg->first_line.u.request.uri); len+=uri->len; break; default: LM_CRIT("unknown type %d\n", se->replace[r].type); /* ignore it */ } } return len; } #endif /*! \brief Replies will be allocated with the proper size & rpl.len set * \return 0 on success, <0 on error */ static int replace_build(const char* match, int nmatch, regmatch_t* pmatch, struct subst_expr* se, struct sip_msg* msg, str* rpl) { int r; str* uri; pv_value_t sv; char* p; char* dest; char* end; int size; #define REPLACE_BUFFER_SIZE 1024 static char rbuf[REPLACE_BUFFER_SIZE]; #if 0 /* use static bufer now since we cannot easily get the length */ rpl->len=replace_len(match, nmatch, pmatch, se, msg); if (rpl->len==0){ rpl->s=0; /* empty string */ return 0; } rpl->s=pkg_malloc(rpl->len); if (rpl->s==0){ LM_ERR("out of pkg mem (rpl)\n"); goto error; } #endif p=se->replacement.s; end=p+se->replacement.len; dest=rbuf; for (r=0; rn_escapes; r++){ /* copy the unescaped parts */ size=se->replacement.s+se->replace[r].offset-p; if(dest-rbuf+size>=REPLACE_BUFFER_SIZE-1){ LM_ERR("overflow\n"); goto error; } memcpy(dest, p, size); p+=size+se->replace[r].size; dest+=size; switch(se->replace[r].type){ case REPLACE_NMATCH: if ((se->replace[r].u.nmatchreplace[r].u.nmatch].rm_so!=-1)){ /* do the replace */ size=pmatch[se->replace[r].u.nmatch].rm_eo- pmatch[se->replace[r].u.nmatch].rm_so; if(dest-rbuf+size>=REPLACE_BUFFER_SIZE-1){ LM_ERR("overflow\n"); goto error; } memcpy(dest, match+pmatch[se->replace[r].u.nmatch].rm_so, size); dest+=size; }; break; case REPLACE_CHAR: if(dest-rbuf+1>=REPLACE_BUFFER_SIZE-1){ LM_ERR("overflow\n"); goto error; } *dest=se->replace[r].u.c; dest++; break; case REPLACE_URI: if (msg->first_line.type!=SIP_REQUEST){ LM_CRIT("uri substitution on a reply\n"); break; /* ignore, we can continue */ } uri= (msg->new_uri.s)?(&msg->new_uri): (&msg->first_line.u.request.uri); if(dest-rbuf+uri->len>=REPLACE_BUFFER_SIZE-1){ LM_ERR("overflow\n"); goto error; } memcpy(dest, uri->s, uri->len); dest+=uri->len; break; case REPLACE_SPEC: if(pv_get_spec_value(msg, &se->replace[r].u.spec, &sv)!=0) { LM_CRIT("item substitution returned error\n"); break; /* ignore, we can continue */ } if(dest-rbuf+sv.rs.len>=REPLACE_BUFFER_SIZE-1){ LM_ERR("overflow\n"); goto error; } memcpy(dest, sv.rs.s, sv.rs.len); dest+=sv.rs.len; break; default: LM_CRIT("unknown type %d\n", se->replace[r].type); /* ignore it */ } } memcpy(dest, p, end-p); rpl->len = (dest-rbuf)+(end-p); rpl->s=pkg_malloc(rpl->len); if (rpl->s==0){ LM_ERR("out of pkg mem (rpl)\n"); goto error; } memcpy(rpl->s, rbuf, rpl->len); return 0; error: return -1; } /*! \brief run substitutions * \return 0 if no match or error, or subst result; if count!=0 * it will be set to 0 (no match), the number of matches * or -1 (error). * \note WARNING: input must be 0 terminated! */ struct replace_lst* subst_run(struct subst_expr* se, const char* input, struct sip_msg* msg, int* count) { struct replace_lst *head; struct replace_lst **crt; const char *p; int r; regmatch_t* pmatch; int nmatch; int eflags; int cnt; /* init */ head=0; cnt=0; crt=&head; p=input; nmatch=se->max_pmatch+1; /* no of () referenced + 1 for the whole string: pmatch[0] */ pmatch=pkg_malloc(nmatch*sizeof(regmatch_t)); if (pmatch==0){ LM_ERR("out of pkg mem. (pmatch)\n"); goto error; } eflags=0; do{ r=regexec(se->re, p, nmatch, pmatch, eflags); LM_DBG("running. r=%d\n", r); /* subst */ if (r==0){ /* != REG_NOMATCH */ /* some checks */ if (pmatch[0].rm_so==-1){ LM_ERR("unknown offset?\n"); goto error; } if (pmatch[0].rm_so==pmatch[0].rm_eo){ LM_ERR("matched string is empty... invalid regexp?\n"); goto error; } *crt=pkg_malloc(sizeof(struct replace_lst)); if (*crt==0){ LM_ERR("out of pkg mem (crt)\n"); goto error; } memset(*crt, 0, sizeof(struct replace_lst)); (*crt)->offset=pmatch[0].rm_so+(int)(p-input); (*crt)->size=pmatch[0].rm_eo-pmatch[0].rm_so; LM_DBG("matched (%d, %d): [%.*s]\n", (*crt)->offset, (*crt)->size, (*crt)->size, input+(*crt)->offset); /* create subst. string */ /* construct the string from replace[] */ if (replace_build(p, nmatch, pmatch, se, msg, &((*crt)->rpl))<0){ goto error; } crt=&((*crt)->next); p+=pmatch[0].rm_eo; /* is it still a string start? */ if (*(p-1)=='\n' || *(p-1)=='\r') eflags&=~REG_NOTBOL; else eflags|=REG_NOTBOL; cnt++; } }while((r==0) && se->replace_all); pkg_free(pmatch); if (count)*count=cnt; return head; error: if (head) replace_lst_free(head); if (pmatch) pkg_free(pmatch); if (count) *count=-1; return 0; } /*! \return the substitution result in a str, input must be 0 term * 0 on no match or malloc error * if count is non zero it will be set to the number of matches, or -1 * if error */ str* subst_str(const char *input, struct sip_msg* msg, struct subst_expr* se, int* count) { str* res; struct replace_lst *lst; struct replace_lst* l; int len; int size; const char* p; char* dest; const char* end; /* compute the len */ len=strlen(input); end=input+len; lst=subst_run(se, input, msg, count); if (lst==0){ LM_DBG("no match\n"); return 0; } for (l=lst; l; l=l->next) len+=(int)(l->rpl.len)-l->size; res=pkg_malloc(sizeof(str)); if (res==0){ LM_ERR("out of pkg memory\n"); goto error; } res->s=pkg_malloc(len+1); /* space for null termination */ if (res->s==0){ LM_ERR("out of pkg memory (res->s)\n"); goto error; } res->s[len]=0; res->len=len; /* replace */ dest=res->s; p=input; for(l=lst; l; l=l->next){ size=l->offset+input-p; memcpy(dest, p, size); /* copy till offset */ p+=size + l->size; /* skip l->size bytes */ dest+=size; if (l->rpl.len){ memcpy(dest, l->rpl.s, l->rpl.len); dest+=l->rpl.len; } } memcpy(dest, p, end-p); if(lst) replace_lst_free(lst); return res; error: if (lst) replace_lst_free(lst); if (res){ if (res->s) pkg_free(res->s); pkg_free(res); } if (count) *count=-1; return 0; } opensips-2.2.2/re.h000066400000000000000000000044701300170765700141470ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-08-04 created by andrei * 2004-11-12 minor api extension, added *count (andrei) */ /*! * \file * \brief regexp and regexp substitutions implementations */ #ifndef _re_h #define _re_h #include "str.h" #include "pvar.h" #include "parser/msg_parser.h" #include /* for regex */ #include #define WITH_SEP 1 #define WITHOUT_SEP 0 enum replace_special { REPLACE_NMATCH, REPLACE_CHAR, REPLACE_URI, REPLACE_SPEC }; struct replace_with{ int offset; /* offset in string */ int size; /* size of replace "anchor" in string */ enum replace_special type; union{ int nmatch; char c; pv_spec_t spec; }u; }; struct subst_expr{ regex_t* re; str replacement; int replace_all; int n_escapes; /* escapes number (replace[] size) */ int max_pmatch ; /* highest () referenced */ struct replace_with replace[1]; /* 0 does not work on all compilers */ }; struct replace_lst{ int offset; int size; /* at offset, delete size bytes and replace them with rpl */ str rpl; struct replace_lst *next; }; void subst_expr_free(struct subst_expr* se); void replace_lst_free(struct replace_lst* l); int parse_repl(struct replace_with * rw, char ** begin, char * end, int *max_token_nb, int flag); struct subst_expr* subst_parser(str* subst); struct replace_lst* subst_run( struct subst_expr* se, const char* input, struct sip_msg* msg, int *count); str* subst_str(const char* input, struct sip_msg* msg, struct subst_expr* se, int* count); #endif opensips-2.2.2/reactor.c000066400000000000000000000055361300170765700151770ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2014-08-23 created (bogdan) */ #include #include #include "io_wait.h" #include "globals.h" /* one reactor per process variable */ io_wait_h _worker_io; /* max number of fds per reactor */ unsigned int reactor_size = 0; #define FD_MEM_PERCENT 10 int init_reactor_size(void) { struct rlimit lim; int n, pc; n = sizeof(struct fd_map) + sizeof(struct pollfd); if (open_files_limit>0) { /* the fd limit was explicitly set, so just follow but only warn * if too much memory is to consumed by reactor */ pc = 100*n*open_files_limit / pkg_mem_size; if (pc>=80) { LM_ERR("required memory for a %d files reactor is over 80%% of" " the configured pkg mem (%luMb)\n", open_files_limit, pkg_mem_size); LM_ERR("Please consider increasing the pkg memory or reduce the" " limit of open files...Exiting\n"); return -1; } else if (pc>=50) { LM_WARN("required memory for a %d files reactor is over 50%% of" " the configured pkg mem (%luMb)\n", open_files_limit, pkg_mem_size); LM_WARN("PKG memory may not be enough at runtime (consider " "increasing it), still continuing\n"); } /* seems to have enough mem -> size the reactor based on open files */ reactor_size = open_files_limit; } else { /* auto detect the limit of open files */ if (getrlimit(RLIMIT_NOFILE, &lim)<0){ LM_ERR("cannot get the maximum number of file descriptors: %s\n", strerror(errno)); return -1; } /* calculate the size to fit into 10% PKG mem */ reactor_size = pkg_mem_size * FD_MEM_PERCENT / (100*n); if (reactor_size /* must be included after io_wait.h if SIGIO_RT is used */ #ifdef HAVE_SELECT #define reactor_SELECT_CASE(_timeout,_loop_extra) \ case POLL_SELECT: \ while(1){ \ io_wait_loop_select(&_worker_io, _timeout, 0); \ _loop_extra;\ } \ break; #else #define reactor_SELECT_CASE(_timeout,_loop_extra) #endif #ifdef HAVE_SIGIO_RT #define reactor_SIGIORT_CASE(_timeout,_loop_extra) \ case POLL_SIGIO_RT: \ while(1){ \ io_wait_loop_sigio_rt(&_worker_io, _timeout); \ _loop_extra;\ } \ break; #else #define reactor_SIGIORT_CASE(_timeout,_loop_extra) #endif #ifdef HAVE_EPOLL #define reactor_EPOLL_CASE(_timeout,_loop_extra) \ case POLL_EPOLL_LT: \ while(1){ \ io_wait_loop_epoll(&_worker_io, _timeout, 0); \ _loop_extra;\ } \ break; \ case POLL_EPOLL_ET: \ while(1){ \ io_wait_loop_epoll(&_worker_io, _timeout, 1); \ _loop_extra;\ } \ break; #else #define reactor_EPOLL_CASE(_timeout,_loop_extra) #endif #ifdef HAVE_KQUEUE #define reactor_KQUEUE_CASE(_timeout,_loop_extra) \ case POLL_KQUEUE: \ while(1){ \ io_wait_loop_kqueue(&_worker_io, _timeout, 0); \ _loop_extra;\ } \ break; #else #define reactor_KQUEUE_CASE(_timeout,_loop_extra) #endif #ifdef HAVE_DEVPOLL #define reactor_DEVPOLL_CASE(_timeout,_loop_extra) \ case POLL_DEVPOLL: \ while(1){ \ io_wait_loop_devpoll(&_worker_io, _timeout, 0); \ _loop_extra;\ } \ break; #else #define reactor_DEVPOLL_CASE(_timeout,_loop_extra) #endif #define reactor_main_loop( _timeout, _err, _loop_extra) \ switch(_worker_io.poll_method) { \ case POLL_POLL: \ while(1){ \ io_wait_loop_poll(&_worker_io, _timeout, 0); \ _loop_extra;\ } \ break; \ reactor_SELECT_CASE(_timeout,_loop_extra) \ reactor_SIGIORT_CASE(_timeout,_loop_extra) \ reactor_EPOLL_CASE(_timeout,_loop_extra) \ reactor_KQUEUE_CASE(_timeout,_loop_extra) \ reactor_DEVPOLL_CASE(_timeout,_loop_extra) \ default:\ LM_CRIT("no support for poll method %s (%d)\n", \ poll_method_name(_worker_io.poll_method), \ _worker_io.poll_method);\ goto _err; \ } #endif opensips-2.2.2/reactor_defs.h000066400000000000000000000060131300170765700161740ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * history: * --------- * 2014-08-25 created (bogdan) */ #ifndef _REACTOR_DEFS_H_ #define _REACTOR_DEFS_H_ /* This should be included by files where the reactor is used (in terms of * submitting fd's to it). * The header file provides both the reactor definitions, the fd manipulation * functions, but NO looping and triggering functions. * IF you need to implement a reactor core (looping, triggering), use * the reactor.h file directly !!! */ #include "ip_addr.h" #include "io_wait.h" #include "globals.h" struct worker_io_data { /* source info buffer */ union sockaddr_union* from_sa; /* received info */ struct receive_info ri; /* SIP interface */ struct socket_info *si; }; enum reactor_prios { RCT_PRIO_NET=0, RCT_PRIO_ASYNC, RCT_PRIO_PROC, RCT_PRIO_TIMER, RCT_PRIO_MAX }; enum fd_types { F_NONE=0, /* generic fd types, to be handled by all SIP worker processes */ F_TIMER_JOB, F_SCRIPT_ASYNC=16, /* fd type specifc to UDP oriented processes (SIP workers) */ F_UDP_READ, /* fd types specific to TCP oriented processes (SIP workers) */ F_TCPMAIN, F_TCPCONN, /* fd types for TCP management process (TCP main process) */ F_TCP_LISTENER, F_TCP_TCPWORKER, F_TCP_WORKER }; extern io_wait_h _worker_io; extern unsigned int reactor_size; int init_reactor_size(void); #define init_worker_reactor( _name, _prio_max) \ init_io_wait(&_worker_io, _name, reactor_size, io_poll_method, _prio_max) #define reactor_add_reader( _fd, _type, _prio, _data) \ io_watch_add(&_worker_io, _fd, _type, _data, _prio, IO_WATCH_READ) #define reactor_add_writer( _fd, _type, _prio, _data) \ io_watch_add(&_worker_io, _fd, _type, _data, _prio, IO_WATCH_WRITE) #define reactor_del_reader( _fd, _idx, _io_flags) \ io_watch_del(&_worker_io, _fd, _idx, _io_flags, IO_WATCH_READ) #define reactor_del_writer( _fd, _idx, _io_flags) \ io_watch_del(&_worker_io, _fd, _idx, _io_flags, IO_WATCH_WRITE) #define reactor_del_all( _fd, _idx, _io_flags) \ io_watch_del(&_worker_io, _fd, _idx, _io_flags, IO_WATCH_READ|IO_WATCH_WRITE) #define destroy_worker_reactor() \ destroy_io_wait(&_worker_io) #define reactor_has_async() \ (io_poll_method==POLL_POLL || io_poll_method==POLL_EPOLL_LT || io_poll_method==POLL_EPOLL_ET) #endif opensips-2.2.2/receive.c000066400000000000000000000204301300170765700151500ustar00rootroot00000000000000/* * Copyright (C) 2010-2014 OpenSIPS Solutions * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-29 transport-independent message zero-termination in * receive_msg (jiri) * 2003-02-07 undoed jiri's zero term. changes (they break tcp) (andrei) * 2003-02-10 moved zero-term in the calling functions (udp_receive & * tcp_read_req) * 2003-08-13 fixed exec_pre_cb returning 0 (backported from stable) (andrei) * 2004-02-06 added user preferences support - destroy_avps() (bogdan) * 2004-04-30 exec_pre_cb is called after basic sanity checks (at least one * via present & parsed ok) (andrei) * 2004-08-23 avp core changed - destroy_avp-> reset_avps (bogdan) * 2005-07-26 default onreply route added (andrei) * 2006-12-22 functions for script flags added (bogdan) */ /*! * \file * \brief Receive message and process routing for it */ #include #include #include #include "receive.h" #include "globals.h" #include "dprint.h" #include "route.h" #include "parser/msg_parser.h" #include "forward.h" #include "action.h" #include "mem/mem.h" #include "ip_addr.h" #include "script_cb.h" #include "dset.h" #include "usr_avp.h" #include "core_stats.h" #include "ut.h" #include "context.h" #ifdef DEBUG_DMALLOC #include #endif static unsigned int msg_no=0; /* address preset vars */ str default_global_address={0,0}; str default_global_port={0,0}; str default_via_address={0,0}; str default_via_port={0,0}; unsigned int get_next_msg_no(void) { return ++msg_no; } #define prepare_context( _ctx, _err ) \ do { \ if (_ctx==NULL) { \ _ctx = context_alloc(CONTEXT_GLOBAL);\ if (_ctx==NULL) { \ LM_ERR("failed to allocated new context, skipping\n"); \ goto _err; \ } \ } \ memset( _ctx, 0, context_size(CONTEXT_GLOBAL)); \ }while(0) /*! \note WARNING: buf must be 0 terminated (buf[len]=0) or some things might * break (e.g.: modules/textops) */ int receive_msg(char* buf, unsigned int len, struct receive_info* rcv_info, context_p existing_context) { static context_p ctx = NULL; struct sip_msg* msg; struct timeval start; int rc; char *tmp; str in_buff; in_buff.len = len; in_buff.s = buf; if (existing_context) { context_free(ctx); ctx = existing_context; } /* the raw processing callbacks can change the buffer, further use in_buff.s and at the end try to free in_buff.s if changed by callbacks */ if (run_pre_raw_processing_cb(PRE_RAW_PROCESSING,&in_buff,NULL)<0) { LM_ERR("error in running pre raw callbacks, dropping\n"); goto error; } /* update the length for further processing */ len = in_buff.len; msg=pkg_malloc(sizeof(struct sip_msg)); if (msg==0) { LM_ERR("no pkg mem left for sip_msg\n"); goto error; } msg_no++; /* number of vias parsed -- good for diagnostic info in replies */ via_cnt=0; memset(msg,0, sizeof(struct sip_msg)); /* init everything to 0 */ /* fill in msg */ msg->buf=in_buff.s; msg->len=len; msg->rcv=*rcv_info; msg->id=msg_no; msg->ruri_q = Q_UNSPECIFIED; if (parse_msg(in_buff.s,len, msg)!=0){ tmp=ip_addr2a(&(rcv_info->src_ip)); LM_ERR("Unable to parse msg received from [%s:%d]\n", tmp, rcv_info->src_port); /* if a REQUEST msg was detected (first line was successfully parsed) we should trigger the error route */ if ( msg->first_line.type==SIP_REQUEST && error_rlist.a!=NULL ) run_error_route(msg, 1); goto parse_error; } LM_DBG("After parse_msg...\n"); start_expire_timer(start,execmsgthreshold); /* ... clear branches from previous message */ clear_branches(); if (msg->first_line.type==SIP_REQUEST) { update_stat( rcv_reqs, 1); /* sanity checks */ if ((msg->via1==0) || (msg->via1->error!=PARSE_OK)){ /* no via, send back error ? */ LM_ERR("no via found in request\n"); update_stat( err_reqs, 1); goto parse_error; } /* check if necessary to add receive?->moved to forward_req */ /* check for the alias stuff */ if (msg->via1->alias && tcp_accept_aliases && is_tcp_based_proto(rcv_info->proto) ) { if (tcpconn_add_alias(rcv_info->proto_reserved1, msg->via1->port, rcv_info->proto)!=0){ LM_WARN("tcp alias failed\n"); /* continue */ } } LM_DBG("preparing to run routing scripts...\n"); /* set request route type --bogdan*/ set_route_type( REQUEST_ROUTE ); /* prepare and set a new processing context for this request only if * no context was set from the upper layers */ if (existing_context == NULL) prepare_context( ctx, parse_error ); current_processing_ctx = ctx; /* execute pre-script callbacks, if any; * if some of the callbacks said not to continue with * script processing, don't do so; * if we are here basic sanity checks are already done * (like presence of at least one via), so you can count * on via1 being parsed in a pre-script callback --andrei */ rc = exec_pre_req_cb(msg); if (rc == SCB_DROP_MSG) { update_stat( drp_reqs, 1); goto end; /* drop the message */ } /* exec the routing script */ if (rc & SCB_RUN_TOP_ROUTE) /* run the main request route and skip post_script callbacks * if the TOBE_CONTINUE flag is returned */ if ( run_top_route(rlist[DEFAULT_RT].a, msg) & ACT_FL_TBCONT ) goto end; /* execute post request-script callbacks */ if (rc & SCB_RUN_POST_CBS) exec_post_req_cb(msg); } else if (msg->first_line.type==SIP_REPLY) { update_stat( rcv_rpls, 1); /* sanity checks */ if ((msg->via1==0) || (msg->via1->error!=PARSE_OK)){ /* no via, send back error ? */ LM_ERR("no via found in reply\n"); update_stat( err_rpls, 1); goto parse_error; } /* set reply route type --bogdan*/ set_route_type( ONREPLY_ROUTE ); /* prepare and set a new processing context for this reply only if * no context was set from the upper layers */ if (existing_context == NULL) prepare_context( ctx, parse_error ); current_processing_ctx = ctx; /* execute pre-script callbacks, if any ; * if some of the callbacks said not to continue with * script processing, don't do so ; * if we are here, basic sanity checks are already done * (like presence of at least one via), so you can count * on via1 being parsed in a pre-script callback --andrei */ rc = exec_pre_rpl_cb(msg); if (rc == SCB_DROP_MSG) { update_stat( drp_rpls, 1); goto end; /* drop the reply */ } /* exec the onreply routing script */ if (rc & SCB_RUN_TOP_ROUTE && onreply_rlist[DEFAULT_RT].a && (run_top_route(onreply_rlist[DEFAULT_RT].a,msg) & ACT_FL_DROP) && msg->REPLY_STATUS < 200) { LM_DBG("dropping provisional reply %d\n", msg->REPLY_STATUS); update_stat( drp_rpls, 1); goto end; /* drop the message */ } else { /* send the msg */ forward_reply(msg); /* TODO - TX reply stat */ } /* execute post reply-script callbacks */ if (rc & SCB_RUN_POST_CBS) exec_post_rpl_cb(msg); } end: /* if someone else set the context, then we should also "release" the * static ctx. */ if (current_processing_ctx == NULL) ctx = NULL; else context_destroy(CONTEXT_GLOBAL, ctx); current_processing_ctx = NULL; stop_expire_timer( start, execmsgthreshold, "msg processing", msg->buf, msg->len, 0); reset_longest_action_list(execmsgthreshold); /* free possible loaded avps -bogdan */ reset_avps(); LM_DBG("cleaning up\n"); free_sip_msg(msg); pkg_free(msg); if (in_buff.s != buf) pkg_free(in_buff.s); return 0; parse_error: exec_parse_err_cb(msg); free_sip_msg(msg); pkg_free(msg); error: if (in_buff.s != buf) pkg_free(in_buff.s); return -1; } opensips-2.2.2/receive.h000066400000000000000000000021041300170765700151530ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Receive message handling */ #ifndef receive_h #define receive_h #include "ip_addr.h" #include "context.h" int receive_msg(char* buf, unsigned int len, struct receive_info *ri, context_p existing_context); unsigned int get_next_msg_no(void); #endif opensips-2.2.2/regexp.c000066400000000000000000000055101300170765700150220ustar00rootroot00000000000000/* * Regular expression functions * * Copyright (C) 2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief Regular Expression functions */ #include #include #include #include #include "regexp.h" #include "dprint.h" /*! \brief Replace in replacement tokens \\d with substrings of string pointed by * pmatch. */ int replace(regmatch_t* pmatch, char* string, char* replacement, str* result) { int len, i, j, digit, size; len = strlen(replacement); j = 0; for (i = 0; i < len; i++) { if (replacement[i] == '\\') { if (i < len - 1) { if (isdigit((unsigned char)replacement[i+1])) { digit = replacement[i+1] - '0'; if (pmatch[digit].rm_so != -1) { size = pmatch[digit].rm_eo - pmatch[digit].rm_so; if (j + size < result->len) { memcpy(&(result->s[j]), string+pmatch[digit].rm_so, size); j = j + size; } else { return -1; } } else { return -2; } i = i + 1; continue; } else { i = i + 1; } } else { return -3; } } if (j + 1 < result->len) { result->s[j] = replacement[i]; j = j + 1; } else { return -4; } } result->len = j; return 1; } /*! \brief Match pattern against string and store result in pmatch */ int reg_match(char *pattern, char *string, regmatch_t *pmatch) { regex_t preg; if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) { return -1; } if (preg.re_nsub > MAX_MATCH) { regfree(&preg); return -2; } if (regexec(&preg, string, MAX_MATCH, pmatch, 0)) { regfree(&preg); return -3; } regfree(&preg); return 0; } /*! \brief Match pattern against string and, if match succeeds, and replace string * with replacement substituting tokens \\d with matched substrings. */ int reg_replace(char *pattern, char *replacement, char *string, str *result) { regmatch_t pmatch[MAX_MATCH]; LM_DBG("pattern: '%s', replacement: '%s', string: '%s'\n", pattern, replacement, string); if (reg_match(pattern, string, &(pmatch[0]))) { return -1; } return replace(&pmatch[0], string, replacement, result); } opensips-2.2.2/regexp.h000066400000000000000000000020661300170765700150320ustar00rootroot00000000000000/* * Regular expression definitions * * Copyright (C) 2003 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief Regular expression definitions */ #ifndef REGEXP_H #define REGEXP_H #include "str.h" #define MAX_MATCH 6 extern int reg_replace(char *pattern, char *replacement, char *string, str *result); #endif opensips-2.2.2/resolve.c000066400000000000000000001426631300170765700152220ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2005-2009 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2003-02-13 added proto to sip_resolvehost, for SRV lookups (andrei) * 2003-07-03 default port value set according to proto (andrei) * 2007-01-25 support for DNS failover added (bogdan) * 2008-07-25 support for SRV load-balancing added (bogdan) */ /*! * \file * \brief DNS resolver for OpenSIPS */ #include #include #include #include #include #include #include #include "mem/mem.h" #include "mem/shm_mem.h" #include "net/trans.h" #include "resolve.h" #include "dprint.h" #include "ut.h" #include "ip_addr.h" #include "globals.h" #include "blacklists.h" fetch_dns_cache_f *dnscache_fetch_func=NULL; put_dns_cache_f *dnscache_put_func=NULL; /* stuff related to DNS failover */ #define DNS_NODE_SRV 1 #define DNS_NODE_A 2 struct dns_val { unsigned int ival; char *sval; }; /* mallocs for local stuff */ #define local_malloc pkg_malloc #define local_free pkg_free int dns_try_ipv6=0; /*!< default off */ int dns_try_naptr=1; /*!< default on */ /* declared in globals.h */ int dns_retr_time=-1; int dns_retr_no=-1; int dns_servers_no=-1; int dns_search_list=-1; int disable_dns_blacklist=1; static struct bl_head *failover_bl=0; #define DNS_REVOLVER_BL_ID 17 #define DNS_REVOLVER_BL_NAME "dns" #define DNS_BL_EXPIRE 4*60 #define MAX_BUFF_SIZE 8192 #define MAXALIASES 36 #define MAXADDRS 36 #define DNS_MAX_NAME 256 struct hostent global_he; static char hostbuf[MAX_BUFF_SIZE]; static char *h_addr_ptrs[MAXADDRS]; static char *host_aliases[MAXALIASES]; typedef union { int32_t al; char ac; } align; #define BOUNDED_INCR(x) \ do { \ cp += x; \ if (cp > eom) { \ LM_ERR("Bounded incr failed\n"); \ return -1; \ } \ } while (0) #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eom) { \ LM_ERR("Bounded check failed\n"); \ return -1; \ } \ } while (0) # define DNS_GET16(s, cp) \ do { \ const uint16_t *t_cp = (const uint16_t *) (cp); \ (s) = ntohs (*t_cp); \ } while (0) # define DNS_GET32(s, cp) \ do { \ const uint32_t *t_cp = (const uint32_t *) (cp); \ (s) = ntohl (*t_cp); \ } while (0) static inline unsigned int dns_get16(const u_char *src) { unsigned int dst; DNS_GET16(dst, src); return dst; } static inline unsigned int dns_get32(const u_char *src) { unsigned int dst; DNS_GET32(dst, src); return dst; } int get_dns_answer(union dns_query *answer,int anslen,char *qname,int qtype,int *min_ttl) { register const HEADER *hp; register int n; register const unsigned char *cp; const char* tname; const unsigned char *eom,*erdata; int type, class,ttl, buflen, ancount; int haveanswer, had_error; char *bp,**ap,**hap; char tbuf[DNS_MAX_NAME]; int toobig=0; tname = qname; global_he.h_name = NULL; eom = answer->buff + anslen; hp = &answer->hdr; ancount = ntohs(hp->ancount); bp = hostbuf; buflen = sizeof hostbuf; cp = answer->buff; BOUNDED_INCR(DNS_HDR_SIZE); n = dn_expand(answer->buff, eom, cp, bp, buflen); if (n < 0) { LM_ERR("Error expanding name\n"); return -1; } BOUNDED_INCR(n+4); if (qtype == T_A || qtype == T_AAAA) { n = strlen(bp) + 1; if (n >= DNS_MAX_NAME) { LM_ERR("Name too large\n"); return -1; } global_he.h_name = bp; bp += n; buflen -= n; /* The qname can be abbreviated, but h_name is now absolute. */ qname = global_he.h_name; } ap = host_aliases; *ap = NULL; global_he.h_aliases = host_aliases; hap = h_addr_ptrs; *hap = NULL; global_he.h_addr_list = h_addr_ptrs; haveanswer = 0; had_error = 0; while (ancount-- > 0 && cp < eom && !had_error) { n = dn_expand(answer->buff, eom, cp, bp, buflen); if (n < 0) { had_error++; continue; } cp += n; /* name */ BOUNDS_CHECK(cp,3*2+4); type = dns_get16(cp); cp += 2; /* type */ class = dns_get16(cp); cp += 2; /* class*/ ttl = dns_get32(cp); if (ttl<*min_ttl) *min_ttl=ttl; cp +=4; n = dns_get16(cp); cp += 2; /* len */ BOUNDS_CHECK(cp, n); erdata = cp + n; if (class != C_IN) { LM_ERR("Got response class != C_IN"); had_error++; continue; } if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { if (ap >= &host_aliases[MAXALIASES-1]) continue; n = dn_expand(answer->buff, eom, cp, tbuf, sizeof tbuf); if (n < 0) { LM_ERR("failed to expand alias\n"); had_error++; continue; } cp += n; if (cp != erdata) return -1; /* Store alias. */ *ap++ = bp; n = strlen(bp) + 1; if (n >= DNS_MAX_NAME) { LM_ERR("alias too long\n"); had_error++; continue; } bp += n; buflen -= n; /* Get canonical name. */ n = strlen(tbuf) + 1; if (n > buflen || n >= DNS_MAX_NAME) { had_error++; continue; } strcpy(bp, tbuf); global_he.h_name = bp; bp += n; buflen -= n; continue; } if (qtype == T_PTR && type == T_CNAME) { n = dn_expand(answer->buff, eom, cp, tbuf, sizeof tbuf); if (n < 0) { LM_ERR("failed to expand for t_ptr\n"); had_error++; continue; } cp += n; if (cp != erdata) { LM_ERR("failure\n"); return -1; } /* Get canonical name. */ n = strlen(tbuf) + 1; if (n > buflen || n >= DNS_MAX_NAME) { LM_ERR("ptr name too long\n"); had_error++; continue; } strcpy(bp, tbuf); tname = bp; bp += n; buflen -= n; continue; } if (type != qtype) { LM_ERR("asked for %d, got %d\n",qtype,type); cp+=n; continue; } switch (type) { case T_PTR: if (strcasecmp(tname, bp) != 0) { LM_ERR("asked for %s, got %s\n",tname,bp); cp += n; continue; /* XXX - had_error++ ? */ } n = dn_expand(answer->buff, eom, cp, bp, buflen); if (n < 0) { had_error++; break; } cp += n; if (cp != erdata) { LM_ERR("errdata err\n"); return -1; } if (!haveanswer) global_he.h_name = bp; else if (ap < &host_aliases[MAXALIASES-1]) *ap++ = bp; else n = -1; if (n != -1) { n = strlen(bp) + 1; /* for the \0 */ if (n >= MAXHOSTNAMELEN) { had_error++; break; } bp += n; buflen -= n; } break; case T_A: case T_AAAA: if (strcasecmp(global_he.h_name, bp) != 0) { LM_ERR("asked for %s, got %s\n",global_he.h_name,bp); cp += n; continue; /* XXX - had_error++ ? */ } if (n != global_he.h_length) { cp += n; continue; } if (!haveanswer) { register int nn; global_he.h_name = bp; nn = strlen(bp) + 1; bp += nn; buflen -= nn; } buflen -= sizeof(align) - ((u_long)bp % sizeof(align)); bp += sizeof(align) - ((u_long)bp % sizeof(align)); if (bp + n >= &hostbuf[sizeof hostbuf]) { LM_ERR("size (%d) too big\n", n); had_error++; continue; } if (hap >= &h_addr_ptrs[MAXADDRS-1]) { if (!toobig++) LM_ERR("Too many addresses (%d)\n",MAXADDRS); cp += n; continue; } memmove(*hap++ = bp, cp, n); bp += n; buflen -= n; cp += n; if (cp != erdata) { LM_ERR("failure\n"); return -1; } break; default: LM_ERR("should never get here\n"); return -1; } if (!had_error) haveanswer++; } if (haveanswer) { *ap = NULL; *hap = NULL; if (!global_he.h_name) { n = strlen(qname) + 1; if (n > buflen || n >= DNS_MAX_NAME) { LM_ERR("name too long\n"); return -1; } strcpy(bp, qname); global_he.h_name = bp; bp += n; buflen -= n; } } return 0; } struct hostent* own_gethostbyname2(char *name,int af) { int size,type; struct hostent *cached_he; static union dns_query buff; int min_ttl = INT_MAX; switch (af) { case AF_INET: size=4; type=T_A; break; case AF_INET6: size=16; type=T_AAAA; break; default: LM_ERR("Only A and AAAA queries\n"); return NULL; } cached_he = (struct hostent *)dnscache_fetch_func(name,af==AF_INET?T_A:T_AAAA,0); if (cached_he == NULL) { LM_DBG("not found in cache or other internal error\n"); goto query; } else if (cached_he == (void *)-1) { LM_DBG("previously failed query\n"); return NULL; } else { LM_DBG("cache hit for %s - %d\n",name,af); return cached_he; } query: global_he.h_addrtype=af; global_he.h_length=size; size=res_search(name, C_IN, type, buff.buff, sizeof(buff)); if (size < 0) { LM_DBG("Domain name not found\n"); if (dnscache_put_func(name,af==AF_INET?T_A:T_AAAA,NULL,0,1,0) < 0) LM_ERR("Failed to store %s - %d in cache\n",name,af); return NULL; } if (get_dns_answer(&buff,size,name,type,&min_ttl) < 0) { LM_ERR("Failed to get dns answer\n"); return NULL; } if (dnscache_put_func(name,af==AF_INET?T_A:T_AAAA,&global_he,-1,0,min_ttl) < 0) LM_ERR("Failed to store %s - %d in cache\n",name,af); return &global_he; } inline struct hostent* resolvehost(char* name, int no_ip_test) { static struct hostent* he=0; #ifdef HAVE_GETIPNODEBYNAME int err; static struct hostent* he2=0; #endif struct ip_addr* ip; str s; if (!no_ip_test) { s.s = (char*)name; s.len = strlen(name); /* check if it's an ip address */ if ( ((ip=str2ip(&s))!=0) || ((ip=str2ip6(&s))!=0) ){ /* we are lucky, this is an ip address */ return ip_addr2he(&s, ip); } } if(dns_try_ipv6){ /*try ipv6*/ #ifdef HAVE_GETHOSTBYNAME2 if (dnscache_fetch_func != NULL) { he = own_gethostbyname2(name,AF_INET6); } else { he=gethostbyname2(name, AF_INET6); } #elif defined HAVE_GETIPNODEBYNAME /* on solaris 8 getipnodebyname has a memory leak, * after some time calls to it will fail with err=3 * solution: patch your solaris 8 installation */ if (he2) freehostent(he2); he=he2=getipnodebyname(name, AF_INET6, 0, &err); #else #error neither gethostbyname2 or getipnodebyname present #endif if (he != 0) /* return the inet6 result if exists */ return he; } if (dnscache_fetch_func != NULL) { he = own_gethostbyname2(name,AF_INET); } else { he=gethostbyname(name); } return he; } struct hostent * own_gethostbyaddr(void *addr, socklen_t len, int af) { const unsigned char *uaddr = (const u_char *)addr; static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 }; int n; int ret; static union dns_query ptr_buff; socklen_t size; char qbuf[DNS_MAX_NAME+1]; char *qp=NULL; int min_ttl=INT_MAX; struct hostent *cached_he; if (af == AF_INET6 && len == 16 && (!memcmp(uaddr, mapped, sizeof mapped) || !memcmp(uaddr, tunnelled, sizeof tunnelled))) { /* Unmap. */ addr += sizeof mapped; uaddr += sizeof mapped; af = AF_INET; len = 4; } switch (af) { case AF_INET: size = 4; break; case AF_INET6: size = 16; break; default: LM_ERR("unexpected af = %d\n",af); return NULL; } if (size != len) { LM_ERR("size = %d, len = %d\n",size,len); return NULL; } cached_he = (struct hostent *)dnscache_fetch_func(addr,T_PTR,af==AF_INET?4:16); if (cached_he == NULL) { LM_DBG("not found in cache or other internal error\n"); goto query; } else if (cached_he == (void *)-1) { LM_DBG("previously failed query\n"); return NULL; } else { LM_DBG("cache hit for PTR - %d\n",af); return cached_he; } query: switch (af) { case AF_INET: sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff), (uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff)); break; case AF_INET6: qp = qbuf; for (n = 15; n >= 0; n--) { qp += sprintf(qp, "%x.%x.", uaddr[n] & 0xf, (uaddr[n] >> 4) & 0xf); } strcpy(qp, "ip6.arpa"); break; } global_he.h_addrtype=af; global_he.h_length=len; ret=res_search(qbuf,C_IN,T_PTR,ptr_buff.buff,sizeof(ptr_buff.buff)); if (ret < 0) { LM_DBG("ptr not found\n"); if (dnscache_put_func(addr,T_PTR,NULL,len,1,0) < 0) LM_ERR("Failed to store PTR in cache\n"); return NULL; } if (get_dns_answer(&ptr_buff,ret,qbuf,T_PTR,&min_ttl) < 0) { LM_ERR("Failed to get dns answer\n"); return NULL; } if (dnscache_put_func(addr,T_PTR,&global_he,len,0,min_ttl) < 0) LM_ERR("Failed to store PTR in cache\n"); return &global_he; } struct hostent* rev_resolvehost(struct ip_addr *ip) { if (dnscache_fetch_func != NULL) { return own_gethostbyaddr((char*)(ip)->u.addr, (ip)->len, (ip)->af); } else return gethostbyaddr((char*)(ip)->u.addr, (ip)->len, (ip)->af); } /*! \brief checks if ip is in host(name) and ?host(ip)=name? * ip must be in network byte order! * resolver = DO_DNS | DO_REV_DNS; if 0 no dns check is made * \return 0 if equal */ int check_ip_address(struct ip_addr* ip, str *name, unsigned short port, unsigned short proto, int resolver) { struct hostent* he; int i, len; char* s; /* maybe we are lucky and name it's an ip */ s=ip_addr2a(ip); if (s){ LM_DBG("params %s, %.*s, %d\n", s, name->len, name->s, resolver); len=strlen(s); /* check if name->s is an ipv6 address or an ipv6 address ref. */ if ((ip->af==AF_INET6) && ( ((len==name->len)&&(strncasecmp(name->s, s, name->len)==0)) || ((len==(name->len-2))&&(name->s[0]=='[')&& (name->s[name->len-1]==']')&& (strncasecmp(name->s+1, s, len)==0)) ) ) return 0; else if (strncmp(name->s, s, name->len)==0) return 0; }else{ LM_CRIT("could not convert ip address\n"); return -1; } if (port==0) port=SIP_PORT; if (resolver&DO_DNS){ LM_DBG("doing dns lookup\n"); /* try all names ips */ he=sip_resolvehost(name, &port, &proto, 0, 0); if (he && (int)ip->af==he->h_addrtype){ for(i=0;he && he->h_addr_list[i];i++){ if ( memcmp(&he->h_addr_list[i], ip->u.addr, ip->len)==0) return 0; } } } if (resolver&DO_REV_DNS){ LM_DBG("doing rev. dns lookup\n"); /* try reverse dns */ he=rev_resolvehost(ip); if (he && (strncmp(he->h_name, name->s, name->len)==0)) return 0; for (i=0; he && he->h_aliases[i];i++){ if (strncmp(he->h_aliases[i],name->s, name->len)==0) return 0; } } return -1; } /*! \brief Initialize the DNS resolver * retr_time - time before retransmitting (must be >0) * retr_no - retransmissions number * servers_no - how many dns servers will be used * (from the one listed in /etc/resolv.conf) * search - if 0 the search list in /etc/resolv.conf will * be ignored (HINT: even if you don't have a * search list in resolv.conf, it's still better * to set search to 0, because an empty searchlist * means in fact search "" => it takes more time) * If any of the parameters <0, the default (system specific) value * will be used. See also resolv.conf(5). * \return 0 on success, -1 on error */ int resolv_init(void) { res_init(); #ifdef HAVE_RESOLV_RES if (dns_retr_time>0) _res.retrans=dns_retr_time; if (dns_retr_no>0) _res.retry=dns_retr_no; if (dns_servers_no>=0) _res.nscount=dns_servers_no; if (dns_search_list==0) _res.options&=~(RES_DEFNAMES|RES_DNSRCH); #else #warning "no resolv timeout support" LM_WARN("no resolv options support - resolv options will be ignored\n"); #endif return 0; } /*! \brief Initialize blacklist */ int resolv_blacklist_init(void) { str name = str_init(DNS_REVOLVER_BL_NAME); if (!disable_dns_blacklist) { failover_bl = create_bl_head( DNS_REVOLVER_BL_ID, BL_DO_EXPIRE|BL_BY_DEFAULT, 0, 0, &name); if (failover_bl==NULL) { LM_ERR("failed to create blacklist\n"); return -1; } } return 0; } /*! \brief Skips over a domain name in a dns message * (it can be a sequence of labels ending in \\0, a pointer or * a sequence of labels ending in a pointer -- see rfc1035 * returns pointer after the domain name or null on error */ unsigned char* dns_skipname(unsigned char* p, unsigned char* end) { while(p=end)?0:p; } /*! \brief parses the srv record into a srv_rdata structure * \param msg - pointer to the dns message * \param end - pointer to the end of the message * \param rdata - pointer to the rdata part of the srv answer * \return 0 on error, or a dyn. alloc'ed srv_rdata structure * * SRV rdata format: * 111111 * 0123456789012345 * +----------------+ * | priority | * |----------------| * | weight | * |----------------| * | port number | * |----------------| * | | * ~ name ~ * | | * +----------------+ */ struct srv_rdata* dns_srv_parser( unsigned char* msg, unsigned char* end, unsigned char* rdata) { struct srv_rdata* srv; int len; srv=0; if ((rdata+6)>=end) goto error; srv=(struct srv_rdata*)local_malloc(sizeof(struct srv_rdata)); if (srv==0){ LM_ERR("out of pkg memory\n"); goto error; } memcpy((void*)&srv->priority, rdata, 2); memcpy((void*)&srv->weight, rdata+2, 2); memcpy((void*)&srv->port, rdata+4, 2); rdata+=6; srv->priority=ntohs(srv->priority); srv->weight=ntohs(srv->weight); srv->port=ntohs(srv->port); if ((len=dn_expand(msg, end, rdata, srv->name, MAX_DNS_NAME-1))==-1) goto error; /* add terminating 0 ? (warning: len=compressed name len) */ srv->name_len=strlen(srv->name); return srv; error: if (srv) local_free(srv); return 0; } /*! \brief parses the naptr record into a naptr_rdata structure * \param msg - pointer to the dns message * \param end - pointer to the end of the message * \param rdata - pointer to the rdata part of the naptr answer * \return 0 on error, or a dyn. alloc'ed naptr_rdata structure * * NAPTR rdata format: * 111111 * 0123456789012345 * +----------------+ * | order | * |----------------| * | preference | * |----------------| * ~ flags ~ * | (string) | * |----------------| * ~ services ~ * | (string) | * |----------------| * ~ regexp ~ * | (string) | * |----------------| * ~ replacement ~ | (name) | * +----------------+ */ struct naptr_rdata* dns_naptr_parser( unsigned char* msg, unsigned char* end, unsigned char* rdata) { struct naptr_rdata* naptr; naptr = 0; if ((rdata + 7) >= end) goto error; naptr=(struct naptr_rdata*)local_malloc(sizeof(struct naptr_rdata)); if (naptr == 0){ LM_ERR("out of pkg memory\n"); goto error; } memcpy((void*)&naptr->order, rdata, 2); naptr->order=ntohs(naptr->order); memcpy((void*)&naptr->pref, rdata + 2, 2); naptr->pref=ntohs(naptr->pref); naptr->flags_len = (int)rdata[4]; if ((rdata + 7 + naptr->flags_len) >= end) goto error; memcpy((void*)&naptr->flags, rdata + 5, naptr->flags_len); naptr->services_len = (int)rdata[5 + naptr->flags_len]; if ((rdata + 7 + naptr->flags_len + naptr->services_len) >= end) goto error; memcpy((void*)&naptr->services, rdata + 6 + naptr->flags_len, naptr->services_len); naptr->regexp_len = (int)rdata[6 + naptr->flags_len + naptr->services_len]; if ((rdata + 7 + naptr->flags_len + naptr->services_len + naptr->regexp_len) >= end) goto error; memcpy((void*)&naptr->regexp, rdata + 7 + naptr->flags_len + naptr->services_len, naptr->regexp_len); rdata = rdata + 7 + naptr->flags_len + naptr->services_len + naptr->regexp_len; naptr->repl_len=dn_expand(msg, end, rdata, naptr->repl, MAX_DNS_NAME-1); if ( naptr->repl_len==(unsigned int)-1 ) goto error; /* add terminating 0 ? (warning: len=compressed name len) */ return naptr; error: if (naptr) local_free(naptr); return 0; } /*! \brief Parses a CNAME record into a cname_rdata structure */ struct cname_rdata* dns_cname_parser( unsigned char* msg, unsigned char* end, unsigned char* rdata) { struct cname_rdata* cname; int len; cname=0; cname=(struct cname_rdata*)local_malloc(sizeof(struct cname_rdata)); if(cname==0){ LM_ERR("out of pkg memory\n"); goto error; } if ((len=dn_expand(msg, end, rdata, cname->name, MAX_DNS_NAME-1))==-1) goto error; return cname; error: if (cname) local_free(cname); return 0; } /*! \brief Parses an A record rdata into an a_rdata structure * \return 0 on error or a dyn. alloc'ed a_rdata struct */ struct a_rdata* dns_a_parser(unsigned char* rdata, unsigned char* end) { struct a_rdata* a; if (rdata+4>=end) goto error; a=(struct a_rdata*)local_malloc(sizeof(struct a_rdata)); if (a==0){ LM_ERR("out of pkg memory\n"); goto error; } memcpy(a->ip, rdata, 4); return a; error: return 0; } /*! \brief Parses an AAAA (ipv6) record rdata into an aaaa_rdata structure * \return 0 on error or a dyn. alloc'ed aaaa_rdata struct */ struct aaaa_rdata* dns_aaaa_parser(unsigned char* rdata, unsigned char* end) { struct aaaa_rdata* aaaa; if (rdata+16>=end) goto error; aaaa=(struct aaaa_rdata*)local_malloc(sizeof(struct aaaa_rdata)); if (aaaa==0){ LM_ERR("out of pkg memory\n"); goto error; } memcpy(aaaa->ip6, rdata, 16); return aaaa; error: return 0; } /*! \brief Parses a TXT record into a txt_rdata structure * \note RFC1035: * - is a single length octet followed by that number of characters. * - TXT-DATA One or more s. * * We only take the first string here. */ struct txt_rdata* dns_txt_parser( unsigned char* msg, unsigned char* end, unsigned char* rdata) { struct txt_rdata* txt; unsigned int len; txt=0; txt=(struct txt_rdata*)local_malloc(sizeof(struct txt_rdata)); if(txt==0){ LM_ERR("out of pkg memory\n"); goto error; } len = *rdata; if (rdata + 1 + len >= end) goto error; /* something fishy in the record */ if (len >= sizeof(txt->txt)) goto error; /* not enough space? */ memcpy(txt->txt, rdata+1, len); txt->txt[len] = 0; /* 0-terminate string */ return txt; error: if (txt) local_free(txt); return 0; } /*! \brief parses a EBL record into a ebl_rdata structure * * EBL Record * * 0 1 2 3 4 5 6 7 * +--+--+--+--+--+--+--+--+ * | POSITION | * +--+--+--+--+--+--+--+--+ * / SEPARATOR / * +--+--+--+--+--+--+--+--+ * / APEX / * +--+--+--+--+--+--+--+--+ */ struct ebl_rdata* dns_ebl_parser( unsigned char* msg, unsigned char* end, unsigned char* rdata) { struct ebl_rdata* ebl; int len; ebl=0; ebl=(struct ebl_rdata*)local_malloc(sizeof(struct ebl_rdata)); if(ebl==0){ LM_ERR("out of pkg memory\n"); goto error; } len = *rdata; if (rdata + 1 + len >= end) goto error; /* something fishy in the record */ ebl->position = *rdata; if ( ebl->position > 15 ) goto error; /* doesn't make sense: E.164 numbers can't be longer */ rdata++; ebl->separator_len = (int) *rdata; rdata++; if ((rdata + 1 + ebl->separator_len) >= end) goto error; memcpy((void*)&ebl->separator, rdata, ebl->separator_len); rdata += ebl->separator_len; ebl->apex_len=dn_expand(msg, end, rdata, ebl->apex, MAX_DNS_NAME-1); if ( ebl->apex_len==(unsigned int)-1 ) goto error; ebl->apex[ebl->apex_len] = 0; /* 0-terminate string */ return ebl; error: if (ebl) local_free(ebl); return 0; } /*! \brief frees completely a struct rdata list */ void free_rdata_list(struct rdata* head) { struct rdata* l; struct rdata* next_l; for( l=head; l ; l=next_l) { next_l = l->next; /* free the parsed rdata*/ if (l->rdata) local_free(l->rdata); local_free(l); } } /*! \brief gets the DNS records for name:type * \return A dyn. alloc'ed struct rdata linked list with the parsed responses * or 0 on error * \note see rfc1035 for the query/response format */ struct rdata* get_record(char* name, int type) { int size; int qno, answers_no; int r; static union dns_query buff; unsigned char* p; /* unsigned char* t; int ans_len; static unsigned char answer[ANS_SIZE]; */ static int rdata_struct_len=sizeof(struct rdata)-sizeof(void *) - sizeof(struct rdata *); unsigned char* end; unsigned short rtype, class, rdlength; unsigned int ttl; unsigned int min_ttl = UINT_MAX; struct rdata* head; struct rdata** crt; struct rdata** last; struct rdata* rd; struct srv_rdata* srv_rd; struct srv_rdata* crt_srv; struct naptr_rdata* naptr_rd; struct txt_rdata* txt_rd; struct ebl_rdata* ebl_rd; struct timeval start; int rdata_buf_len=0; if (dnscache_fetch_func != NULL) { head = (struct rdata *)dnscache_fetch_func(name,type,0); if (head == NULL) { LM_DBG("not found in cache or other internal error\n"); goto query; } else if (head == (void *)-1) { LM_DBG("previously failed query\n"); goto not_found; } else { LM_DBG("cache hit for %s - %d\n",name,type); return head; } } query: start_expire_timer(start,execdnsthreshold); size=res_search(name, C_IN, type, buff.buff, sizeof(buff)); stop_expire_timer(start,execdnsthreshold,"dns",name,strlen(name),0); if (size<0) { LM_DBG("lookup(%s, %d) failed\n", name, type); if (dnscache_put_func != NULL) { if (dnscache_put_func(name,type,NULL,0,1,0) < 0) LM_ERR("Failed to store %s - %d in cache\n",name,type); } goto not_found; } else if ((unsigned int)size > sizeof(buff)) size=sizeof(buff); head=rd=0; last=crt=&head; p=buff.buff+DNS_HDR_SIZE; end=buff.buff+size; if (p>=end) goto error_boundary; qno=ntohs((unsigned short)buff.hdr.qdcount); for (r=0; r=end) { LM_ERR("p>=end\n"); goto error; } }; answers_no=ntohs((unsigned short)buff.hdr.ancount); /*ans_len=ANS_SIZE; t=answer;*/ for (r=0; (r=end) goto error_boundary; /* get type */ memcpy((void*) &rtype, (void*)p, 2); rtype=ntohs(rtype); p+=2; /* get class */ memcpy((void*) &class, (void*)p, 2); class=ntohs(class); p+=2; /* get ttl*/ memcpy((void*) &ttl, (void*)p, 4); ttl=ntohl(ttl); if (ttl < min_ttl) min_ttl = ttl; p+=4; /* get size */ memcpy((void*)&rdlength, (void*)p, 2); rdlength=ntohs(rdlength); p+=2; /* check for type */ /* if (rtype!=type){ LM_WAR("wrong type in answer (%d!=%d)\n", rtype, type); p+=rdlength; continue; } */ /* expand the "type" record (rdata)*/ rd=(struct rdata*) local_malloc(sizeof(struct rdata)); if (rd==0){ LM_ERR("out of pkg memory\n"); goto error; } if (dnscache_put_func) rdata_buf_len+=rdata_struct_len; rd->type=rtype; rd->class=class; rd->ttl=ttl; rd->next=0; switch(rtype){ case T_SRV: srv_rd= dns_srv_parser(buff.buff, end, p); if (srv_rd==0) goto error_parse; if (dnscache_put_func) rdata_buf_len+=4*sizeof(unsigned short) + sizeof(unsigned int ) + srv_rd->name_len+1; rd->rdata=(void*)srv_rd; /* insert sorted into the list */ for (crt=&head; *crt; crt= &((*crt)->next)){ crt_srv=(struct srv_rdata*)(*crt)->rdata; if ((srv_rd->priority < crt_srv->priority) || ( (srv_rd->priority == crt_srv->priority) && ((srv_rd->weight==0) || (crt_srv->weight!=0)) ) ){ /* insert here */ goto skip; } } last=&(rd->next); /*end of for => this will be the last elem*/ skip: /* insert here */ rd->next=*crt; *crt=rd; break; case T_A: rd->rdata=(void*) dns_a_parser(p,end); if (rd->rdata==0) goto error_parse; if (dnscache_put_func) rdata_buf_len+=sizeof(struct a_rdata); *last=rd; /* last points to the last "next" or the list head*/ last=&(rd->next); break; case T_AAAA: rd->rdata=(void*) dns_aaaa_parser(p,end); if (rd->rdata==0) goto error_parse; if (dnscache_put_func) rdata_buf_len+=sizeof(struct aaaa_rdata); *last=rd; last=&(rd->next); break; case T_CNAME: rd->rdata=(void*) dns_cname_parser(buff.buff, end, p); if(rd->rdata==0) goto error_parse; if (dnscache_put_func) rdata_buf_len+= strlen(((struct cname_rdata *)rd->rdata)->name) + 1 + sizeof(int); *last=rd; last=&(rd->next); break; case T_NAPTR: naptr_rd = dns_naptr_parser(buff.buff,end,p); rd->rdata=(void*) naptr_rd; if(rd->rdata==0) goto error_parse; if (dnscache_put_func) rdata_buf_len+=2*sizeof(unsigned short) + 4*sizeof(unsigned int) + naptr_rd->flags_len+1 + + naptr_rd->services_len+1+naptr_rd->regexp_len + + 1 + naptr_rd->repl_len + 1; *last=rd; last=&(rd->next); break; case T_TXT: txt_rd = dns_txt_parser(buff.buff, end, p); rd->rdata=(void*) txt_rd; if(rd->rdata==0) goto error_parse; if (dnscache_put_func) rdata_buf_len+=sizeof(int)+strlen(txt_rd->txt)+1; *last=rd; last=&(rd->next); break; case T_EBL: ebl_rd = dns_ebl_parser(buff.buff, end, p); rd->rdata=(void*) ebl_rd; if(rd->rdata==0) goto error_parse; if (dnscache_put_func) rdata_buf_len+=sizeof(unsigned char)+ 2*sizeof(unsigned int)+ebl_rd->apex_len + 1 + ebl_rd->separator_len + 1; *last=rd; last=&(rd->next); break; default: LM_ERR("unknown type %d\n", rtype); rd->rdata=0; *last=rd; last=&(rd->next); } p+=rdlength; } if (dnscache_put_func != NULL) { if (dnscache_put_func(name,type,head,rdata_buf_len,0,min_ttl) < 0) LM_ERR("Failed to store %s - %d in cache\n",name,type); } return head; error_boundary: LM_ERR("end of query buff reached\n"); if(head) free_rdata_list(head); return 0; error_parse: LM_ERR("rdata parse error \n"); if (rd) local_free(rd); /* rd->rdata=0 & rd is not linked yet into the list */ error: LM_ERR("get_record \n"); if (head) free_rdata_list(head); not_found: return 0; } static inline int get_naptr_proto(struct naptr_rdata *n) { if (n->services[3]=='s' || n->services[3]=='S' ) return PROTO_TLS; switch (n->services[n->services_len-1]) { case 'U': case 'u': return PROTO_UDP; break; case 'T': case 't': return PROTO_TCP; break; case 'S': case 's': return PROTO_SCTP; break; } LM_CRIT("failed to detect proto\n"); return PROTO_NONE; } static inline int srv2dns_node(struct rdata *head, struct dns_node **dn) { unsigned int mem; unsigned int l; struct rdata *r; struct dns_node *n; char *p; /* calculate how much mem is required */ mem = sizeof(struct dns_node); for( r=head,l=0 ; r ; r=r->next,l++ ) mem +=sizeof(struct dns_val) + get_naptr(r)->repl_len + 1; n = (struct dns_node*)shm_malloc(mem); if (n==NULL) { LM_ERR("no more shm mem (%d)\n", mem); return -1; } n->type = DNS_NODE_SRV; n->size = mem; n->idx = 0; n->no = l; n->kids = *dn; *dn = n; n->vals = (struct dns_val*)(n+1); p = (char*)(n->vals+l); for( r=head,l=0 ; r ; r=r->next,l++ ) { n->vals[l].ival = get_naptr_proto( get_naptr(r) ); n->vals[l].sval = p; memcpy( p, get_naptr(r)->repl, get_naptr(r)->repl_len ); p += get_naptr(r)->repl_len; *(p++) = 0; } return 0; } static inline int a2dns_node(struct rdata *head, struct dns_node **dn) { unsigned int mem; unsigned int l; struct rdata *r; struct dns_node *n; char *p; /* calculate how much mem is required */ mem = sizeof(struct dns_node); for( r=head,l=0 ; r ; r=r->next,l++ ) { get_srv(r)->name_len = strlen(get_srv(r)->name); mem +=sizeof(struct dns_val) + get_srv(r)->name_len + 1; } n = (struct dns_node*)shm_malloc(mem); if (n==NULL) { LM_ERR("no more shm mem (%d)\n", mem); return -1; } n->type = DNS_NODE_A; n->size = mem; n->idx = 0; n->no = l; n->kids = 0; *dn = n; n->vals = (struct dns_val*)(n+1); p = (char*)(n->vals+l); for( r=head,l=0 ; r ; r=r->next,l++ ) { n->vals[l].ival = get_srv(r)->port; n->vals[l].sval = p; memcpy( p, get_srv(r)->name, get_srv(r)->name_len ); LM_DBG("storing %.*s:%d\n", get_srv(r)->name_len,p,n->vals[l].ival); p += get_srv(r)->name_len; *(p++) = 0; } return 0; } static inline void sort_srvs(struct rdata **head) { #define rd2srv(_rd) ((struct srv_rdata*)_rd->rdata) struct rdata *rd = *head; struct rdata *tail = NULL; struct rdata *rd_next; struct rdata *crt; struct rdata *crt2; unsigned int weight_sum; unsigned int rand_no; *head = NULL; while( rd ) { rd_next = rd->next; if (rd->type!=T_SRV) { rd->next = NULL; free_rdata_list(rd); } else { /* only on element with same priority ? */ if (rd_next==NULL || rd2srv(rd)->priority!=rd2srv(rd_next)->priority) { if (tail) {tail->next=rd;tail=rd;} else {*head=tail=rd;} rd->next = NULL; } else { /* multiple nodes with same priority */ /* -> calculate running sums (and detect the end) */ weight_sum = rd2srv(rd)->running_sum = rd2srv(rd)->weight; crt = rd; while( crt && crt->next && (rd2srv(rd)->priority==rd2srv(crt->next)->priority) ) { crt = crt->next; weight_sum += rd2srv(crt)->weight; rd2srv(crt)->running_sum = weight_sum; } /* crt will point to last RR with same priority */ rd_next = crt->next; crt->next = NULL; /* order the elements between rd and crt */ while (rd->next) { rand_no = (unsigned int) (weight_sum*((float)rand()/RAND_MAX)); for( crt=rd,crt2=NULL ; crt ; crt2=crt,crt=crt->next) { if (rd2srv(crt)->running_sum>=rand_no) break; } if (crt == NULL) { LM_CRIT("bug in sorting SRVs - rand>sum\n"); crt = rd; crt2 = NULL; } /* remove the element from the list ... */ if (crt2==NULL) { rd = rd->next;} else {crt2->next = crt->next;} /* .... and update the running sums */ for ( crt2=crt->next ; crt2 ; crt2=crt2->next) rd2srv(crt2)->running_sum -= rd2srv(crt)->weight; weight_sum -= rd2srv(crt)->weight; /* link the crt in the new list */ crt->next = 0; if (tail) {tail->next=crt;tail=crt;} else {*head=tail=crt;} } /* just insert the last remaining element */ tail->next = rd; tail = rd ; } } rd = rd_next; } } static inline struct hostent* do_srv_lookup(char *name, unsigned short* port, struct dns_node **dn) { struct hostent *he; struct srv_rdata *srv; struct rdata *head; struct rdata *rd; /* perform SRV lookup */ head = get_record( name, T_SRV); sort_srvs(&head); for( rd=head; rd ; rd=rd->next ) { if (rd->type!=T_SRV) continue; /*should never happen*/ srv = (struct srv_rdata*) rd->rdata; if (srv==0) { LM_CRIT("null rdata\n"); free_rdata_list(head); return 0; } LM_DBG("resolving [%s]\n",srv->name); he = resolvehost(srv->name, 1); if ( he!=0 ) { LM_DBG("SRV(%s) = %s:%d\n", name, srv->name, srv->port); if (port) *port=srv->port; if (dn && rd->next && a2dns_node( rd->next, dn)==-1) *dn = 0; free_rdata_list(head); return he; } } if (head) free_rdata_list(head); return 0; } #define naptr_prio(_naptr) \ ((unsigned int)((((_naptr)->order) << 16) + ((_naptr)->pref))) static inline void filter_and_sort_naptr( struct rdata** head_p, struct rdata** filtered_p, int is_sips) { struct naptr_rdata *naptr; struct rdata *head; struct rdata *last; struct rdata *out; struct rdata *l, *ln, *it, *itp; unsigned int prio; char p; head = 0; last = 0; out = 0; for( l=*head_p ; l ; l=ln ) { ln = l->next; if (l->type != T_NAPTR) goto skip0; /*should never happen*/ naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LM_CRIT("null rdata\n"); goto skip0; } /* first filter out by flag and service */ if (naptr->flags_len!=1||(naptr->flags[0]!='s'&&naptr->flags[0]!='S')) goto skip; if (naptr->repl_len==0 || naptr->regexp_len!=0 ) goto skip; if ( (is_sips || naptr->services_len!=7 || strncasecmp(naptr->services,"sip+d2",6) ) && ( naptr->services_len!=8 || strncasecmp(naptr->services,"sips+d2",7))) goto skip; p = naptr->services[naptr->services_len-1]; /* by default we do not support SCTP */ if ( p!='U' && p!='u' && ((p!='T' && p!='t')) && ((p!='S' && p!='s')) ) goto skip; /* is it valid? (SIPS+D2U is not!) */ if ( naptr->services_len==8 && (p=='U' || p=='u')) goto skip; LM_DBG("found valid %.*s -> %s\n", (int)naptr->services_len,naptr->services, naptr->repl); /* this is a supported service -> add it according to order to the * new head list */ prio = naptr_prio(get_naptr(l)); if (head==0) { head = last = l; l->next = 0; } else if ( naptr_prio(get_naptr(head)) >= prio ) { l->next = head; head = l; } else if ( prio >= naptr_prio(get_naptr(last)) ) { l->next = 0; last->next = l; last = l; } else { for( itp=head,it=head->next ; it && it->next ; itp=it,it=it->next ){ if ( prio <= naptr_prio(get_naptr(it))) break; } l->next = itp->next; itp->next = l; } continue; skip: LM_DBG("skipping %.*s -> %s\n", (int)naptr->services_len, naptr->services, naptr->repl); skip0: l->next = out; out = l; } *head_p = head; *filtered_p = out; } #if 0 struct hostent* sip_resolvehost(str* name, unsigned short* port, int *proto, int is_sips) { static char tmp[MAX_DNS_NAME]; struct ip_addr *ip; struct rdata *head; struct rdata *rd; struct hostent* he; if ( (is_sips) && (tls_disable) ) { LM_ERR("cannot resolve SIPS as no TLS support is configured\n"); return 0; } /* check if it's an ip address */ if ( ((ip=str2ip(name))!=0) || ((ip=str2ip6(name))!=0) ){ /* we are lucky, this is an ip address */ if (proto && *proto==PROTO_NONE) *proto = (is_sips)?PROTO_TLS:PROTO_UDP; if (port && *port==0) *port = (is_sips||((*proto)==PROTO_TLS))?SIPS_PORT:SIP_PORT; return ip_addr2he(name,ip); } /* do we have a port? */ if ( !port || (*port)!=0 ) { /* have port -> no NAPTR, no SRV lookup, just A record lookup */ LM_DBG("has port -> do A record lookup!\n"); /* set default PROTO if not set */ if (proto && *proto==PROTO_NONE) *proto = (is_sips)?PROTO_TLS:PROTO_UDP; goto do_a; } /* no port... what about proto? */ if ( !proto || (*proto)!=PROTO_NONE ) { /* have proto, but no port -> do SRV lookup */ LM_DBG("no port, has proto -> do SRV lookup!\n"); if (is_sips && (*proto)!=PROTO_TLS) { LM_ERR("forced proto %d not matching sips uri\n", *proto); return 0; } goto do_srv; } LM_DBG("no port, no proto -> do NAPTR lookup!\n"); /* no proto, no port -> do NAPTR lookup */ if (name->len >= MAX_DNS_NAME) { LM_ERR("domain name too long\n"); return 0; } memcpy(tmp, name->s, name->len); tmp[name->len] = '\0'; /* do NAPTR lookup */ head = get_record( tmp, T_NAPTR); if (head) { /* filter and sort the records */ filter_and_sort_naptr( &head, &rd, is_sips); /* free what is useless */ free_rdata_list( rd ); /* process the NAPTR records */ for( rd=head ; rd ; rd=rd->next ) { he = do_srv_lookup( get_naptr(rd)->repl, port ); if ( he ) { *proto = get_naptr_proto( get_naptr(rd) ); LM_DBG("found!\n"); free_rdata_list(head); return he; } } if (head) free_rdata_list(head); } LM_DBG("no valid NAPTR record found for %.*s," " trying direct SRV lookup...\n", name->len, name->s); *proto = (is_sips)?PROTO_TLS:PROTO_UDP; do_srv: if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME) { LM_WARN("domain name too long (%d)," " unable to perform SRV lookup\n", name->len); /* set defaults */ *port = (is_sips)?SIPS_PORT:SIP_PORT; goto do_a; } switch (*proto) { case PROTO_UDP: memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN); memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len); tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0'; break; case PROTO_TCP: memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN); memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len); tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0'; break; case PROTO_TLS: memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN); memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len); tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0'; break; default: goto err_proto; } he = do_srv_lookup( tmp, port ); if (he) return he; LM_DBG("no valid SRV record found for %s," " trying A record lookup...\n", tmp); /* set default port */ *port = (is_sips||((*proto)==PROTO_TLS))?SIPS_PORT:SIP_PORT; do_a: /* do A record lookup */ if (name->len >= MAX_DNS_NAME) { LM_ERR("domain name too long\n"); return 0; } memcpy(tmp, name->s, name->len); tmp[name->len] = '\0'; he = resolvehost(tmp,1); return he; err_proto: LM_ERR("unsupported proto %d\n", *proto); return 0; } #endif struct hostent* sip_resolvehost( str* name, unsigned short* port, unsigned short *proto, int is_sips, struct dns_node **dn) { static char tmp[MAX_DNS_NAME]; struct ip_addr *ip; struct rdata *head; struct rdata *rd; struct hostent* he; if (dn) *dn = 0; /* check if it's an ip address */ if ( ((ip=str2ip(name))!=0) || ((ip=str2ip6(name))!=0) ){ /* we are lucky, this is an ip address */ if (proto && *proto==PROTO_NONE) *proto = (is_sips)?PROTO_TLS:PROTO_UDP; if (port && *port==0) *port = protos[*proto].default_port; return ip_addr2he(name,ip); } /* do we have a port? */ if ( port && (*port)!=0 ) { /* have port -> no NAPTR, no SRV lookup, just A record lookup */ LM_DBG("has port -> do A record lookup!\n"); /* set default PROTO if not set */ if (proto && *proto==PROTO_NONE) *proto = (is_sips)?PROTO_TLS:PROTO_UDP; goto do_a; } /* no port... what about proto? */ if ( proto && (*proto)!=PROTO_NONE ) { /* have proto, but no port -> do SRV lookup */ LM_DBG("no port, has proto -> do SRV lookup!\n"); if (is_sips && (*proto)!=PROTO_TLS) { LM_ERR("forced proto %d not matching sips uri\n", *proto); return 0; } goto do_srv; } if ( dns_try_naptr==0 ) { if (proto) *proto = (is_sips)?PROTO_TLS:PROTO_UDP; goto do_srv; } LM_DBG("no port, no proto -> do NAPTR lookup!\n"); /* no proto, no port -> do NAPTR lookup */ if (name->len >= MAX_DNS_NAME) { LM_ERR("domain name too long\n"); return 0; } memcpy(tmp, name->s, name->len); tmp[name->len] = '\0'; /* do NAPTR lookup */ head = get_record( tmp, T_NAPTR); if (head) { /* filter and sort the records */ filter_and_sort_naptr( &head, &rd, is_sips); /* free what is useless */ free_rdata_list( rd ); /* process the NAPTR records */ for( rd=head ; rd ; rd=rd->next ) { *proto = get_naptr_proto( get_naptr(rd) ); he = do_srv_lookup( get_naptr(rd)->repl, port, dn); if ( he ) { LM_DBG("valid SRV found!\n"); if (dn) { /* save the state of the resolver for failure cases */ if (*dn==NULL) rd = rd->next; if (rd && srv2dns_node( rd, dn)!=0) { shm_free(*dn); *dn = 0; } } free_rdata_list(head); return he; } } if (head) free_rdata_list(head); } LM_DBG("no valid NAPTR record found for %.*s," " trying direct SRV lookup...\n", name->len, name->s); *proto = (is_sips)?PROTO_TLS:PROTO_UDP; do_srv: if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME) { LM_WARN("domain name too long (%d)," " unable to perform SRV lookup\n", name->len); /* set defaults */ if (port) *port = (is_sips)?SIPS_PORT:SIP_PORT; goto do_a; } switch (*proto) { case PROTO_UDP: memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN); memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len); tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0'; break; case PROTO_TCP: memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN); memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len); tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0'; break; case PROTO_TLS: memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN); memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len); tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0'; break; case PROTO_SCTP: memcpy(tmp, SRV_SCTP_PREFIX, SRV_SCTP_PREFIX_LEN); memcpy(tmp+SRV_SCTP_PREFIX_LEN, name->s, name->len); tmp[SRV_SCTP_PREFIX_LEN + name->len] = '\0'; break; case PROTO_WS: memcpy(tmp, SRV_WS_PREFIX, SRV_WS_PREFIX_LEN); memcpy(tmp+SRV_WS_PREFIX_LEN, name->s, name->len); tmp[SRV_WS_PREFIX_LEN + name->len] = '\0'; break; case PROTO_WSS: memcpy(tmp, SRV_WSS_PREFIX, SRV_WSS_PREFIX_LEN); memcpy(tmp+SRV_WSS_PREFIX_LEN, name->s, name->len); tmp[SRV_WSS_PREFIX_LEN + name->len] = '\0'; break; default: goto err_proto; } he = do_srv_lookup( tmp, port, dn); if (he) return he; LM_DBG("no valid SRV record found for %s, trying A record lookup...\n", tmp); /* set default port */ if (port) *port = protos[*proto].default_port; do_a: /* do A record lookup */ if (name->len >= MAX_DNS_NAME) { LM_ERR("domain name too long\n"); return 0; } memcpy(tmp, name->s, name->len); tmp[name->len] = '\0'; he = resolvehost(tmp,1); return he; err_proto: LM_ERR("unsupported proto %d\n", *proto); return 0; } static inline struct hostent* get_next_he(struct dns_node **node, unsigned short *proto, unsigned short *port) { struct hostent *he; struct dns_node *n; struct dns_node *last_srv; struct dns_node *dn; if (node==NULL || *node==NULL) return 0; n = *node; last_srv = NULL; he = 0; do { switch (n->type) { case DNS_NODE_SRV: last_srv = n; if (n->kids==NULL) { /* need to resolve this SRV and get all the AAA records */ do { dn = 0; he = do_srv_lookup( n->vals[n->idx].sval, port, &dn); if (he) { *proto = n->vals[n->idx].ival; n->idx++; break; } n->idx++; } while(n->idxno); if (he==NULL || (he && n->idx==n->no) ) { /* colapse the SRV node */ shm_free(n); *node = dn; return he; } n->kids = dn; return he; } /* go for the AAA records */ n = n->kids; break; case DNS_NODE_A: /* do resolve until success */ do { he = resolvehost(n->vals[n->idx].sval,1); if (he) { *port = n->vals[n->idx].ival; n->idx++; break; } n->idx++; }while(n->idxno); /* found something? */ if (he==NULL || (he && n->idx==n->no)) { shm_free(n); /* any SRV level? */ if (last_srv==NULL) { /* nothing left */ *node = 0; return he; } last_srv->kids = 0; /* increase the index on the SRV level */ if (++last_srv->idxno) return he; /* colapse the SRV node also */ shm_free(last_srv); *node = 0; } return he; break; default: LM_CRIT("unknown %d node type\n", n->type); return 0; } } while(1); } void free_dns_res( struct proxy_l *p ) { if (p==NULL || p->dn==NULL) return; if (p->dn->kids) shm_free(p->dn->kids); shm_free(p->dn); p->dn = 0; } int get_next_su(struct proxy_l *p, union sockaddr_union* su, int add_to_bl) { struct hostent *he; struct bl_rule *list; struct net ip_net; int n; if (failover_bl && add_to_bl) { memset( &ip_net, 0xff , sizeof(struct net)); hostent2ip_addr( &ip_net.ip, &p->host, p->addr_idx); ip_net.mask.af = ip_net.ip.af; ip_net.mask.len = ip_net.ip.len; list = 0; n = add_rule_to_list( &list, &list, &ip_net, 0, p->port, p->proto, 0); if (n!=0) { LM_ERR("failed to build bl rule\n"); } else { add_list_to_head( failover_bl, list, list, 0, DNS_BL_EXPIRE); } } /* any more available IPs in he ? */ if ( p->host.h_addr_list[++p->addr_idx] ) { /* yes -> return the IP*/ hostent2su( su, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT); return 0; } /* get a new he from DNS */ he = get_next_he( &p->dn, &p->proto, &p->port); if (he==NULL) return -1; /* replace the current he */ if (p->flags&PROXY_SHM_FLAG) { free_shm_hostent( &p->host ); n = hostent_shm_cpy(&(p->host), he); } else { free_hostent( &p->host ); n = hostent_cpy(&(p->host), he); } if (n!=0) { free_dns_res( p ); return -1; } hostent2su( su, &p->host, 0, (p->port)?p->port:SIP_PORT); p->addr_idx = 0; return 0; } static inline struct dns_node *dns_node_copy(struct dns_node *s) { struct dns_node *d; unsigned int i; d = (struct dns_node*)shm_malloc(s->size); if (d==NULL) { LM_ERR("no more shm mem\n"); return 0; } memcpy( d, s, s->size); d->vals = (struct dns_val*)((char*)d + ((char*)s->vals-(char*)s)); for( i=0 ; ino ; i++ ) d->vals[i].sval = (char*)d + ((char*)s->vals[i].sval-(char*)s); return d; } struct dns_node *dns_res_copy(struct dns_node *s) { struct dns_node *d; d = dns_node_copy(s); if (d==NULL) return 0; if (s->kids) { d->kids = dns_node_copy(s->kids); if (d->kids==NULL) { shm_free(d); return 0; } } return d; } opensips-2.2.2/resolve.h000066400000000000000000000225041300170765700152160ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-04-12 support for resolving ipv6 address references added (andrei) * 2004-07-28 darwin needs nameser_compat.h (andrei) * 2007-01-25 support for DNS failover added (bogdan) */ /*! * \file * \brief DNS resolver related functions */ #ifndef __resolve_h #define __resolve_h #include #include #include #include #ifdef __OS_darwin #include #endif #include "mem/shm_mem.h" #include "ip_addr.h" #include "proxy.h" #define MAX_QUERY_SIZE 8192 #define ANS_SIZE 8192 #define DNS_HDR_SIZE 12 #define MAX_DNS_NAME 256 #define MAX_DNS_STRING 255 /*! \brief this is not official yet */ #define T_EBL 65300 typedef void* (fetch_dns_cache_f)(char *name,int r_type,int name_len); typedef int (put_dns_cache_f)(char *name,int r_type,void *record,int rdata_len, int failure,int ttl); extern fetch_dns_cache_f *dnscache_fetch_func; extern put_dns_cache_f *dnscache_put_func; /*! \brief query union*/ union dns_query{ HEADER hdr; unsigned char buff[MAX_QUERY_SIZE]; }; /*! \brief rdata struct*/ struct rdata { unsigned short type; unsigned short class; unsigned int ttl; void* rdata; struct rdata* next; }; /*! \brief srv rec. struct*/ struct srv_rdata { unsigned short priority; unsigned short weight; unsigned short running_sum; unsigned short port; unsigned int name_len; char name[MAX_DNS_NAME]; }; /*! \brief naptr rec. struct*/ struct naptr_rdata { unsigned short order; unsigned short pref; unsigned int flags_len; char flags[MAX_DNS_STRING]; unsigned int services_len; char services[MAX_DNS_STRING]; unsigned int regexp_len; char regexp[MAX_DNS_STRING]; unsigned int repl_len; /* not currently used */ char repl[MAX_DNS_NAME]; }; /*! \brief A rec. struct */ struct a_rdata { unsigned char ip[4]; }; struct aaaa_rdata { unsigned char ip6[16]; }; /*! \brief cname rec. struct*/ struct cname_rdata { char name[MAX_DNS_NAME]; }; /*! \brief txt rec. struct \note This is not strictly correct as TXT records *could* contain multiple strings. */ struct txt_rdata { char txt[MAX_DNS_NAME]; }; /*! \brief EBL rec. struct \note This is an experimental RR for infrastructure ENUM */ struct ebl_rdata { unsigned char position; unsigned int separator_len; char separator[MAX_DNS_NAME]; unsigned int apex_len; char apex[MAX_DNS_NAME]; }; /*! \brief DNS failover related structures */ struct dns_node { unsigned short type; unsigned short size; unsigned short idx; unsigned short no; struct dns_val *vals; struct dns_node *kids; }; struct rdata* get_record(char* name, int type); void free_rdata_list(struct rdata* head); extern int dns_try_ipv6; extern int dns_try_naptr; #define HEX2I(c) \ ( (((c)>='0') && ((c)<='9'))? (c)-'0' : \ (((c)>='A') && ((c)<='F'))? ((c)-'A')+10 : \ (((c)>='a') && ((c)<='f'))? ((c)-'a')+10 : -1 ) #define get_naptr(_rdata) \ ( ((struct naptr_rdata*)(_rdata)->rdata) ) #define get_srv(_rdata) \ ( ((struct srv_rdata*)(_rdata)->rdata) ) int check_ip_address(struct ip_addr* ip, str *name, unsigned short port, unsigned short proto, int resolver); struct hostent* sip_resolvehost(str* name, unsigned short* port, unsigned short *proto, int is_sips, struct dns_node **dn); struct hostent* resolvehost(char* name, int no_ip_test); struct hostent* rev_resolvehost(struct ip_addr *ip); /*! \brief free the DNS resolver state machine */ void free_dns_res( struct proxy_l *p ); /*! \brief make a perfect copy of a resolver state machine */ struct dns_node *dns_res_copy(struct dns_node *s); /*! \brief taked the next destination from a resolver state machine */ int get_next_su(struct proxy_l *p, union sockaddr_union* su, int add_to_bl); int resolv_init(); int resolv_blacklist_init(); /*! \brief converts a str to an ipv4 address, returns the address or 0 on error Warning: the result is a pointer to a statically allocated structure */ static inline struct ip_addr* str2ip(str* st) { int i, j; unsigned char *limit; static struct ip_addr ip; unsigned char *s; if (st == NULL || st->s == NULL) goto error_null; s=(unsigned char*)st->s; /*init*/ ip.u.addr32[0]=0; i=j=0; limit=(unsigned char*)(st->s + st->len); /* first char must be different then '0' */ if ((*s > '9' ) || (*s < '1')) goto error_char; ip.u.addr[i]=ip.u.addr[i]*10+*s-'0'; s++; j++; for(;s3) goto error_dots; s++; if (s==limit) break; if ( (*s <= '9' ) && (*s >= '0') ){ j++; ip.u.addr[i]=ip.u.addr[i]*10+*s-'0'; } else { goto error_char; } }else if ( (j==1) && (*s <= '9' ) && (*s >= '0') ){ /* if first char is '0' then fail conversion */ if (ip.u.addr[i]==0) goto error_char; j++; ip.u.addr[i]=ip.u.addr[i]*10+*s-'0'; }else if ( (j==2) && (*s <= '9' ) && (*s >= '0') ){ /* if first two chars are bigger then '25' then fail conversion */ if (ip.u.addr[i]>25) goto error_char; /* if first three chars are bigger then '255' then fail conversion */ if (ip.u.addr[i]==25 && *s > '5') goto error_char; j++; ip.u.addr[i]=ip.u.addr[i]*10+*s-'0'; }else{ //error unknown char goto error_char; } } if (i<3) goto error_dots; ip.af=AF_INET; ip.len=4; return &ip; error_null: LM_DBG("Null pointer detected\n"); return NULL; error_dots: LM_DBG("too %s dots in [%.*s]\n", (i>3)?"many":"few", st->len, st->s); return NULL; error_char: /* LM_ERR("unexpected char [%p]->[%c] in [%p]->[%.*s] while i=[%d] j=[%d]\n", s, *s, st->s, st->len, st->s, i, j); */ return NULL; } /*! \brief returns an ip_addr struct.; on error returns 0 * the ip_addr struct is static, so subsequent calls will destroy its content*/ static inline struct ip_addr* str2ip6(str* st) { int i, idx1, rest; int no_colons; int double_colon; int hex; static struct ip_addr ip; unsigned short* addr_start; unsigned short addr_end[8]; unsigned short* addr; unsigned char* limit; unsigned char* s; /* init */ if ((st->len) && (st->s[0]=='[')){ /* skip over [ ] */ if (st->s[st->len-1]!=']') goto error_char; s=(unsigned char*)(st->s+1); limit=(unsigned char*)(st->s+st->len-1); }else{ s=(unsigned char*)st->s; limit=(unsigned char*)(st->s+st->len); } i=idx1=rest=0; double_colon=0; no_colons=0; ip.af=AF_INET6; ip.len=16; addr_start=ip.u.addr16; addr=addr_start; memset(addr_start, 0 , 8*sizeof(unsigned short)); memset(addr_end, 0 , 8*sizeof(unsigned short)); for (; s7) goto error_too_many_colons; if (double_colon){ idx1=i; i=0; if (addr==addr_end) goto error_colons; addr=addr_end; }else{ double_colon=1; addr[i]=htons(addr[i]); i++; } }else if ((hex=HEX2I(*s))>=0){ addr[i]=addr[i]*16+hex; double_colon=0; }else{ /* error, unknown char */ goto error_char; } } if (!double_colon){ /* not ending in ':' */ addr[i]=htons(addr[i]); i++; } /* if address contained '::' fix it */ if (addr==addr_end){ rest=8-i-idx1; memcpy(addr_start+idx1+rest, addr_end, i*sizeof(unsigned short)); }else{ /* no double colons inside */ if (no_colons<7) goto error_too_few_colons; } /* DBG("str2ip6: idx1=%d, rest=%d, no_colons=%d, hex=%x\n", idx1, rest, no_colons, hex); DBG("str2ip6: address %x:%x:%x:%x:%x:%x:%x:%x\n", addr_start[0], addr_start[1], addr_start[2], addr_start[3], addr_start[4], addr_start[5], addr_start[6], addr_start[7] ); */ return &ip; error_too_many_colons: LM_DBG("too many colons in [%.*s]\n", st->len, st->s); return 0; error_too_few_colons: LM_DBG("too few colons in [%.*s]\n", st->len, st->s); return 0; error_colons: LM_DBG("too many double colons in [%.*s]\n", st->len, st->s); return 0; error_char: /* DBG("str2ip6: WARNING: unexpected char %c in [%.*s]\n", *s, st->len, st->s);*/ return 0; } static inline struct proxy_l* shm_clone_proxy(struct proxy_l *sp, unsigned int move_dn) { struct proxy_l *dp; dp = (struct proxy_l*)shm_malloc(sizeof(struct proxy_l)); if (dp==NULL) { LM_ERR("no more shm memory\n"); return 0; } memset( dp , 0 , sizeof(struct proxy_l)); dp->port = sp->port; dp->proto = sp->proto; dp->addr_idx = sp->addr_idx; dp->flags = PROXY_SHM_FLAG; /* clone the hostent */ if (hostent_shm_cpy( &dp->host, &sp->host)!=0) goto error0; /* clone the dns resolver */ if (sp->dn) { if (move_dn) { dp->dn = sp->dn; sp->dn = 0; } else { dp->dn = dns_res_copy(sp->dn); if (dp->dn==NULL) goto error1; } } return dp; error1: free_shm_hostent(&dp->host); error0: shm_free(dp); return 0; } #endif opensips-2.2.2/route.c000066400000000000000000001604721300170765700146770ustar00rootroot00000000000000/* * SIP routing engine * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2005-2006 Voice Sistem S.R.L. * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-28 scratchpad removed, src_port introduced (jiri) * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-03-10 updated to the new module exports format (andrei) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-01 added dst_port, proto, af; renamed comp_port to comp_no, * inlined all the comp_* functions (andrei) * 2003-04-05 s/reply_route/failure_route, onreply_route introduced (jiri) * 2003-05-23 comp_ip fixed, now it will resolve its operand and compare * the ip with all the addresses (andrei) * 2003-10-10 added more operators support to comp_* (<,>,<=,>=,!=) (andrei) * 2004-10-19 added from_uri & to_uri (andrei) * 2006-03-02 MODULE_T action points to a cmd_export_t struct instead to * a function address - more info is accessible (bogdan) * Fixup failure reports the config line (bogdan) * 2006-12-22 support for script and branch flags added (bogdan) */ /*! * \file * \brief SIP routing engine */ #include #include #include #include #include #include #include #include #include #include "route.h" #include "forward.h" #include "dprint.h" #include "proxy.h" #include "action.h" #include "sr_module.h" #include "ip_addr.h" #include "resolve.h" #include "socket_info.h" #include "blacklists.h" #include "parser/parse_uri.h" #include "parser/parse_from.h" #include "parser/parse_to.h" #include "mem/mem.h" #include "xlog.h" #include "evi/evi_modules.h" /* main routing script table */ struct script_route rlist[RT_NO]; /* reply routing table */ struct script_route onreply_rlist[ONREPLY_RT_NO]; /* failure routes */ struct script_route failure_rlist[FAILURE_RT_NO]; /* branch routes */ struct script_route branch_rlist[BRANCH_RT_NO]; /* local requests route */ struct script_route local_rlist; /* error route */ struct script_route error_rlist; /* startup route */ struct script_route startup_rlist; /* timer route */ struct script_timer_route timer_rlist[TIMER_RT_NO]; /* event route */ struct script_event_route event_rlist[EVENT_RT_NO]; int route_type = REQUEST_ROUTE; static int fix_actions(struct action* a); /*fwd declaration*/ extern int return_code; /*! * \brief Initialize routing lists */ void init_route_lists(void) { memset(rlist, 0, sizeof(rlist)); memset(onreply_rlist, 0, sizeof(onreply_rlist)); memset(failure_rlist, 0, sizeof(failure_rlist)); memset(branch_rlist, 0, sizeof(branch_rlist)); memset(&local_rlist, 0, sizeof(local_rlist)); memset(&error_rlist, 0, sizeof(error_rlist)); memset(&startup_rlist, 0, sizeof(startup_rlist)); memset(timer_rlist, 0, sizeof(timer_rlist)); memset(event_rlist, 0, sizeof(event_rlist)); rlist[DEFAULT_RT].name = "0"; onreply_rlist[DEFAULT_RT].name = "0"; } int get_script_route_idx( char* name,struct script_route *sr, int size,int set) { unsigned int i; for(i=1;i allocate it now */ sr[i].name = name; return i; } if (strcmp(sr[i].name,name)==0 ) { /* name found */ if (sr[i].a && set) { LM_ERR("Script route <%s> is redefined\n",name); return -1; } return i; } } LM_ERR("Too many routes - no socket left for <%s>\n",name); return -1; } int get_script_route_ID_by_name(char *name, struct script_route *sr, int size) { unsigned int i; for(i=1;itype==EXP_T){ switch(exp->op){ case AND_OP: case OR_OP: if ((ret=fix_expr(exp->left.v.expr))!=0) return ret; ret=fix_expr(exp->right.v.expr); break; case NOT_OP: case EVAL_OP: ret=fix_expr(exp->left.v.expr); break; default: LM_CRIT("unknown op %d\n", exp->op); } }else if (exp->type==ELEM_T){ if (exp->op==MATCH_OP || exp->op==NOTMATCH_OP){ if (exp->right.type==STR_ST){ re=(regex_t*)pkg_malloc(sizeof(regex_t)); if (re==0){ LM_CRIT("out of pkg memory\n"); return E_OUT_OF_MEM; } if (regcomp(re, (char*) exp->right.v.data, REG_EXTENDED|REG_NOSUB|REG_ICASE) ){ LM_CRIT("bad re \"%s\"\n", (char*) exp->right.v.data); pkg_free(re); return E_BAD_RE; } /* replace the string with the re */ pkg_free(exp->right.v.data); exp->right.v.data=re; exp->right.type=RE_ST; }else if (exp->right.type!=RE_ST && exp->right.type!=SCRIPTVAR_ST){ LM_CRIT("invalid type for match\n"); return E_BUG; } } if (exp->left.type==ACTION_O){ ret=fix_actions((struct action*)exp->right.v.data); if (ret!=0){ LM_CRIT("fix_actions error\n"); return ret; } } if (exp->left.type==EXPR_O){ ret=fix_expr(exp->left.v.expr); if (ret!=0){ LM_CRIT("fix left exp error\n"); return ret; } } if (exp->right.type==EXPR_ST){ ret=fix_expr(exp->right.v.expr); if (ret!=0){ LM_CRIT("fix right exp error\n"); return ret; } } ret=0; } return ret; } /*! \brief Adds the proxies in the proxy list & resolves the hostnames * \return 0 if ok, <0 on error */ static int fix_actions(struct action* a) { struct action *t; int ret; cmd_export_t* cmd; acmd_export_t* acmd; struct hostent* he; struct ip_addr ip; struct socket_info* si; str host; int proto=PROTO_NONE, port; struct proxy_l *p; struct bl_head *blh; int i = 0; str s; pv_elem_t *model=NULL; pv_elem_t *models[5]; xl_level_p xlp; event_id_t ev_id; if (a==0){ LM_CRIT("null pointer\n"); return E_BUG; } for(t=a; t!=0; t=t->next){ switch(t->type){ case ROUTE_T: if (t->elem[0].type!=NUMBER_ST){ LM_ALERT("BUG in route() type %d\n", t->elem[0].type); ret = E_BUG; goto error; } if ((t->elem[0].u.number>RT_NO)||(t->elem[0].u.number<0)){ LM_ALERT("invalid routing table number in" "route(%lu)\n", t->elem[0].u.number); ret = E_CFG; goto error; } if ( rlist[t->elem[0].u.number].a==NULL ) { LM_ERR("called route [%s] (id=%d) is not defined\n", rlist[t->elem[0].u.number].name, (int)t->elem[0].u.number); ret = E_CFG; goto error; } if (t->elem[1].type != 0) { if (t->elem[1].type != NUMBER_ST || t->elem[2].type != SCRIPTVAR_ELEM_ST) { LM_ALERT("BUG in route() type %d/%d\n", t->elem[1].type, t->elem[2].type); ret=E_BUG; break; } if (t->elem[1].u.number >= MAX_ACTION_ELEMS || t->elem[1].u.number <= 0) { LM_ALERT("BUG in number of route parameters %d\n", (int)t->elem[1].u.number); ret=E_BUG; break; } } break; case FORWARD_T: if (sl_fwd_disabled>0) { LM_ERR("stateless forwarding disabled, but forward() " "is used!!\n"); ret = E_CFG; goto error; } sl_fwd_disabled = 0; if (t->elem[0].type==NOSUBTYPE) break; case SEND_T: if (t->elem[0].type!=STRING_ST) { LM_CRIT("invalid type %d (should be string)\n", t->type); ret = E_BUG; goto error; } ret = parse_phostport( t->elem[0].u.string, strlen(t->elem[0].u.string), &host.s, &host.len, &port, &proto); if (ret!=0) { LM_ERR("ERROR:fix_actions: FORWARD/SEND bad " "argument\n"); ret = E_CFG; goto error; } p = mk_proxy( &host,(unsigned short)port, proto, 0); if (p==0) { LM_ERR("forward/send failed to add proxy"); ret = E_CFG; goto error; } t->elem[0].type = PROXY_ST; t->elem[0].u.data = (void*)p; s.s = (char*)t->elem[1].u.data; if (s.s && t->elem[1].type == STRING_ST) { /* commands have only one parameter */ s.s = (char *)t->elem[1].u.data; s.len = strlen(s.s); if(s.len==0) { LM_ERR("param is empty string!\n"); return E_CFG; } if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for value param!\n", s.s); ret=E_BUG; goto error; } t->elem[1].u.data = (void*)model; t->elem[1].type = SCRIPTVAR_ELEM_ST; } break; case IF_T: if (t->elem[0].type!=EXPR_ST){ LM_CRIT("invalid subtype %d for if (should be expr)\n", t->elem[0].type); ret = E_BUG; goto error; }else if( (t->elem[1].type!=ACTIONS_ST) &&(t->elem[1].type!=NOSUBTYPE) ){ LM_CRIT("invalid subtype %d for if() {...} (should be" "action)\n", t->elem[1].type); ret = E_BUG; goto error; }else if( (t->elem[2].type!=ACTIONS_ST) &&(t->elem[2].type!=NOSUBTYPE) ){ LM_CRIT("invalid subtype %d for if() {} else{...}(should" "be action)\n", t->elem[2].type); ret = E_BUG; goto error; } if (t->elem[0].u.data){ if ((ret=fix_expr((struct expr*)t->elem[0].u.data))<0) return ret; } if ( (t->elem[1].type==ACTIONS_ST)&&(t->elem[1].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[1].u.data))<0) return ret; } if ( (t->elem[2].type==ACTIONS_ST)&&(t->elem[2].u.data) ){ if((ret=fix_actions((struct action*)t->elem[2].u.data))<0) return ret; } break; case WHILE_T: if (t->elem[0].type!=EXPR_ST){ LM_CRIT("invalid subtype %d for while (should be expr)\n", t->elem[0].type); ret = E_BUG; goto error; }else if( (t->elem[1].type!=ACTIONS_ST) &&(t->elem[1].type!=NOSUBTYPE) ){ LM_CRIT("invalid subtype %d for while() {...} (should be" "action)\n", t->elem[1].type); ret = E_BUG; goto error; } if (t->elem[0].u.data){ if ((ret=fix_expr((struct expr*)t->elem[0].u.data))<0) return ret; } if ( (t->elem[1].type==ACTIONS_ST)&&(t->elem[1].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[1].u.data))<0) return ret; } break; case FOR_EACH_T: if (t->elem[2].type != ACTIONS_ST) { LM_CRIT("bad subtype %d in for-each (should be actions)\n", t->elem[2].type); ret = E_BUG; goto error; } if (t->elem[2].u.data) { if ((ret=fix_actions((struct action*)t->elem[2].u.data))<0) return ret; } break; case SWITCH_T: if ( (t->elem[1].type==ACTIONS_ST)&&(t->elem[1].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[1].u.data))<0) return ret; } break; case CASE_T: if ( (t->elem[1].type==ACTIONS_ST)&&(t->elem[1].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[1].u.data))<0) return ret; } break; case DEFAULT_T: if ( (t->elem[0].type==ACTIONS_ST)&&(t->elem[0].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[0].u.data))<0) return ret; } break; case MODULE_T: cmd = (cmd_export_t*)t->elem[0].u.data; LM_DBG("fixing %s, %s:%d\n", cmd->name, t->file, t->line); if (cmd->fixup){ if (cmd->param_no==0){ ret=cmd->fixup( 0, 0); if (ret<0) goto error; } else { for (i=1; i<=cmd->param_no; i++) { /* we only call the fixup for non-null arguments */ if (t->elem[i].type != NULLV_ST) { ret=cmd->fixup(&t->elem[i].u.data, i); t->elem[i].type=MODFIXUP_ST; if (ret<0) goto error; } } } } break; case ASYNC_T: if ( (t->elem[0].type==ACTIONS_ST)&&(t->elem[0].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[0].u.data))<0) return ret; } break; case AMODULE_T: acmd = (acmd_export_t*)t->elem[0].u.data; LM_DBG("fixing async %s, %s:%d\n", acmd->name, t->file, t->line); if (acmd->fixup){ if (acmd->param_no==0){ ret=acmd->fixup( 0, 0); if (ret<0) goto error; } else { for (i=1; i<=acmd->param_no; i++) { /* we only call the fixup for non-null arguments */ if (t->elem[i].type != NULLV_ST) { ret=acmd->fixup(&t->elem[i].u.data, i); t->elem[i].type=MODFIXUP_ST; if (ret<0) goto error; } } } } break; case FORCE_SEND_SOCKET_T: if (t->elem[0].type!=SOCKID_ST){ LM_CRIT("invalid subtype %d for force_send_socket\n", t->elem[0].type); ret = E_BUG; goto error; } he=resolvehost(((struct socket_id*)t->elem[0].u.data)->name,0); if (he==0){ LM_ERR(" could not resolve %s\n", ((struct socket_id*)t->elem[0].u.data)->name); ret = E_BAD_ADDRESS; goto error; } hostent2ip_addr(&ip, he, 0); si=find_si(&ip, ((struct socket_id*)t->elem[0].u.data)->port, ((struct socket_id*)t->elem[0].u.data)->proto); if (si==0){ LM_ERR("bad force_send_socket" " argument: %s:%d (opensips doesn't listen on it)\n", ((struct socket_id*)t->elem[0].u.data)->name, ((struct socket_id*)t->elem[0].u.data)->port); ret = E_BAD_ADDRESS; goto error; } t->elem[0].u.data=si; t->elem[0].type=SOCKETINFO_ST; break; case SETFLAG_T: case RESETFLAG_T: case ISFLAGSET_T: if (t->elem[0].type == NUMBER_ST) { s.s = int2str((unsigned long)t->elem[0].u.number, &s.len); t->elem[0].u.number = fixup_flag(FLAG_TYPE_MSG, &s); } else if (t->elem[0].type == STR_ST) { t->elem[0].u.number = fixup_flag(FLAG_TYPE_MSG, &t->elem[0].u.s); } else { LM_CRIT("bad xxxflag() type %d\n", t->elem[0].type); ret = E_BUG; goto error; } if (t->elem[0].u.number == NAMED_FLAG_ERROR) { LM_CRIT("Fixup flag failed!\n"); ret=E_CFG; goto error; } break; case SETBFLAG_T: case RESETBFLAG_T: case ISBFLAGSET_T: if (t->elem[0].type!=NUMBER_ST) { LM_CRIT("bad xxxbflag() type " "%d,%d\n", t->elem[0].type, t->elem[0].type); ret=E_BUG; goto error; } if (t->elem[1].type == NUMBER_ST) { s.s = int2str((unsigned long)t->elem[1].u.number, &s.len); t->elem[1].u.number = fixup_flag(FLAG_TYPE_BRANCH, &s); } else if (t->elem[1].type == STR_ST) { t->elem[1].u.number = fixup_flag(FLAG_TYPE_BRANCH, &t->elem[1].u.s); } else { LM_CRIT("bad xxxbflag() type " "%d,%d\n", t->elem[1].type, t->elem[1].type); ret=E_BUG; goto error; } if (t->elem[1].u.number == NAMED_FLAG_ERROR) { LM_CRIT("Fixup flag failed!\n"); ret=E_CFG; goto error; } if (t->elem[1].u.data==0) { ret=E_CFG; goto error; } break; case EQ_T: case COLONEQ_T: case PLUSEQ_T: case MINUSEQ_T: case DIVEQ_T: case MULTEQ_T: case MODULOEQ_T: case BANDEQ_T: case BOREQ_T: case BXOREQ_T: if (t->elem[1].u.data){ if ((ret=fix_expr((struct expr*)t->elem[1].u.data))<0) return ret; } break; case USE_BLACKLIST_T: case UNUSE_BLACKLIST_T: if (t->elem[0].type!=STRING_ST) { LM_CRIT("bad [UN]USE_BLACKLIST type %d\n",t->elem[0].type); ret=E_BUG; goto error; } host.s = t->elem[0].u.string; host.len = strlen(host.s); if (!strcasecmp(host.s, "all")) { blh = NULL; } else { blh = get_bl_head_by_name(&host); if (!blh) { LM_ERR("[UN]USE_BLACKLIST - list " "%s not configured\n", t->elem[0].u.string); ret=E_CFG; goto error; } } t->elem[0].type = BLACKLIST_ST; t->elem[0].u.data = blh; break; case CACHE_STORE_T: case CACHE_FETCH_T: case CACHE_COUNTER_FETCH_T: case CACHE_REMOVE_T: case CACHE_ADD_T: case CACHE_SUB_T: case CACHE_RAW_QUERY_T: /* attr name */ s.s = (char*)t->elem[1].u.data; s.len = strlen(s.s); if(s.len==0) { LM_ERR("param 2 is empty string!\n"); return E_CFG; } if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for value param!\n", s.s); ret=E_BUG; goto error; } t->elem[1].u.data = (void*)model; if (t->type==CACHE_REMOVE_T) break; /* value */ if (t->type==CACHE_FETCH_T || t->type==CACHE_COUNTER_FETCH_T) { if(((pv_spec_p)t->elem[2].u.data)->setf == NULL) { LM_ERR("Third argument cannot be a read-only pvar\n"); ret=E_CFG; goto error; } } else if (t->type==CACHE_STORE_T) { s.s = (char*)t->elem[2].u.data; s.len = strlen(s.s); if(s.len==0) { LM_ERR("param 2 is empty string!\n"); return E_CFG; } if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for value param!\n",s.s); ret=E_BUG; goto error; } t->elem[2].u.data = (void*)model; } else if (t->type==CACHE_RAW_QUERY_T) { if(t->elem[2].u.data != NULL) { s.s = (char*)t->elem[2].u.data; s.len = strlen(s.s); t->elem[2].u.data = (void*)parse_pvname_list(&s, PVT_AVP); if (t->elem[2].u.data == NULL) { ret=E_BUG; goto error; } } } else if (t->type==CACHE_ADD_T || t->type==CACHE_SUB_T) { if(t->elem[4].u.data != NULL && ((pv_spec_p)t->elem[4].u.data)->setf == NULL) { LM_ERR("Fourth argument cannot be a read-only pvar\n"); ret=E_CFG; goto error; } } break; case SET_ADV_ADDR_T: s.s = (char *)t->elem[0].u.data; if (s.s == NULL) { LM_ERR("null param in set_advertised_address\n"); ret=E_BUG; goto error; } s.len = strlen(s.s); if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format for [%.*s] advertised param!\n", t->elem[1].u.s.len,t->elem[1].u.s.s); ret=E_BUG; goto error; } t->elem[0].u.data = (void*)model; break; case SET_ADV_PORT_T: if (t->elem[0].type == STR_ST) { s.s = (char *)t->elem[0].u.data; s.len = strlen(s.s); if (pv_parse_format(&s ,&model) != 0 || !model) { LM_ERR("wrong format for [%.*s] advertised port!\n", t->elem[1].u.s.len, t->elem[1].u.s.s); ret = E_BUG; goto error; } t->elem[0].u.data = model; } break; case XDBG_T: case XLOG_T: s.s = (char*)t->elem[1].u.data; if (s.s == NULL) { /* commands have only one parameter */ s.s = (char *)t->elem[0].u.data; s.len = strlen(s.s); if(s.len==0) { LM_ERR("param is empty string!\n"); return E_CFG; } if(pv_parse_format(&s ,&model) || model==NULL) { LM_ERR("wrong format [%s] for value param!\n", s.s); ret=E_BUG; goto error; } t->elem[0].u.data = (void*)model; t->elem[0].type = SCRIPTVAR_ELEM_ST; } else { /* there are two parameters */ s.s = (char *)t->elem[0].u.data; s.len = strlen(s.s); if (s.len == 0) { LM_ERR("param is empty string\n"); return E_CFG; } xlp = (xl_level_p)pkg_malloc(sizeof(xl_level_t)); if(xlp == NULL) { LM_ERR("no more memory\n"); return E_UNSPEC; } memset(xlp, 0, sizeof(xl_level_t)); if(s.s[0]==PV_MARKER) { xlp->type = 1; if(pv_parse_spec(&s, &xlp->v.sp)==NULL) { LM_ERR("invalid level param\n"); return E_UNSPEC; } } else { xlp->type = 0; switch(s.s[2]) { case 'A': xlp->v.level = L_ALERT; break; case 'C': xlp->v.level = L_CRIT; break; case 'E': xlp->v.level = L_ERR; break; case 'W': xlp->v.level = L_WARN; break; case 'N': xlp->v.level = L_NOTICE; break; case 'I': xlp->v.level = L_INFO; break; case 'D': xlp->v.level = L_DBG; break; default: LM_ERR("unknown log level\n"); return E_UNSPEC; } } t->elem[0].u.data = xlp; s.s = t->elem[1].u.data; s.len = strlen(s.s); if (pv_parse_format(&s, &model) || model == NULL) { LM_ERR("wrong format [%s] for value param\n",s.s); ret=E_BUG; goto error; } t->elem[1].u.data = model; t->elem[1].type = SCRIPTVAR_ELEM_ST; } break; case RAISE_EVENT_T: s.s = t->elem[0].u.data; s.len = strlen(s.s); ev_id = evi_get_id(&s); if (ev_id == EVI_ERROR) { ev_id = evi_publish_event(s); if (ev_id == EVI_ERROR) { LM_ERR("cannot subscribe event\n"); ret=E_UNSPEC; goto error; } } t->elem[0].u.number = ev_id; t->elem[0].type = NUMBER_ST; if (t->elem[1].u.data && ((pv_spec_p)t->elem[1].u.data)->type != PVT_AVP) { LM_ERR("second parameter should be an avp\n"); ret=E_UNSPEC; goto error; } /* if was called with 3 parameters */ if (t->elem[2].u.data && ((pv_spec_p)t->elem[2].u.data)->type != PVT_AVP) { LM_ERR("third parameter should be also an avp\n"); ret=E_UNSPEC; goto error; } break; case CONSTRUCT_URI_T: for (i=0;i<5;i++) { s.s = (char*)t->elem[i].u.data; s.len = strlen(s.s); if(s.len==0) continue; if(pv_parse_format(&s ,&(models[i])) || models[i]==NULL) { LM_ERR("wrong format [%s] for value param!\n",s.s); ret=E_BUG; goto error; } t->elem[i].u.data = (void*)models[i]; } if (((pv_spec_p)t->elem[5].u.data)->type != PVT_AVP) { LM_ERR("Wrong type for the third argument - " "must be an AVP\n"); ret=E_BUG; goto error; } break; case GET_TIMESTAMP_T: if (((pv_spec_p)t->elem[0].u.data)->type != PVT_AVP) { LM_ERR("Wrong type for the first argument - " "must be an AVP\n"); ret=E_BUG; goto error; } if (((pv_spec_p)t->elem[1].u.data)->type != PVT_AVP) { LM_ERR("Wrong type for the second argument - " "must be an AVP\n"); ret=E_BUG; goto error; } } } return 0; error: LM_ERR("fixing failed (code=%d) at %s:%d\n", ret, t->file, t->line); return ret; } inline static int comp_no( int port, void *param, int op, int subtype ) { if (subtype!=NUMBER_ST) { LM_CRIT("number expected: %d\n", subtype ); return E_BUG; } switch (op){ case EQUAL_OP: return port==(long)param; case DIFF_OP: return port!=(long)param; case GT_OP: return port>(long)param; case LT_OP: return port<(long)param; case GTE_OP: return port>=(long)param; case LTE_OP: return port<=(long)param; default: LM_CRIT("unknown operator: %d\n", op ); return E_BUG; } } /*! \brief eval_elem helping function * \return str op param */ inline static int comp_strval(struct sip_msg *msg, int op, str* ival, operand_t *opd) { int ret; regex_t* re; char backup; char backup2; str res; pv_value_t value; if(ival==NULL || ival->s==NULL) goto error; res.s = 0; res.len = 0; if(opd->type == SCRIPTVAR_ST) { if(pv_get_spec_value(msg, opd->v.spec, &value)!=0) { LM_CRIT("cannot get var value\n"); goto error; } if(value.flags&PV_VAL_STR) { res = value.rs; } else { res.s = sint2str(value.ri, &res.len); } } else if(opd->type == NUMBER_ST) { res.s = sint2str(opd->v.n, &res.len); }else if(opd->type == STR_ST) { res = opd->v.s; } else { if((op!=MATCH_OP && op!=NOTMATCH_OP) || opd->type != RE_ST) { LM_CRIT("invalid operation %d/%d\n", op, opd->type); goto error; } } ret=-1; switch(op){ case EQUAL_OP: if(ival->len != res.len) return 0; ret=(strncasecmp(ival->s, res.s, ival->len)==0); break; case DIFF_OP: if(ival->len != res.len) return 1; ret=(strncasecmp(ival->s, res.s, ival->len)!=0); break; case MATCH_OP: case NOTMATCH_OP: backup=ival->s[ival->len];ival->s[ival->len]='\0'; if(opd->type == SCRIPTVAR_ST) { re=(regex_t*)pkg_malloc(sizeof(regex_t)); if (re==0){ LM_CRIT("pkg memory allocation failure\n"); ival->s[ival->len]=backup; goto error; } backup2 = res.s[res.len];res.s[res.len] = '\0'; if (regcomp(re, res.s, REG_EXTENDED|REG_NOSUB|REG_ICASE)) { pkg_free(re); res.s[res.len] = backup2; ival->s[ival->len]=backup; goto error; } ret=(regexec(re, ival->s, 0, 0, 0)==0); regfree(re); pkg_free(re); res.s[res.len] = backup2; } else { ret=(regexec((regex_t*)opd->v.data, ival->s, 0, 0, 0)==0); } ival->s[ival->len]=backup; if(op==NOTMATCH_OP) ret = !ret; break; default: LM_CRIT("unknown op %d\n", op); goto error; } return ret; error: return -1; } /*! \brief eval_elem helping function, returns str op param */ inline static int comp_str(char* str, void* param, int op, int subtype) { int ret; ret=-1; switch(op){ case EQUAL_OP: if (subtype!=STR_ST){ LM_CRIT("bad type %d, string expected\n", subtype); goto error; } ret=(strcasecmp(str, (char*)param)==0); break; case DIFF_OP: if (subtype!=STR_ST){ LM_CRIT("bad type %d, string expected\n", subtype); goto error; } ret=(strcasecmp(str, (char*)param)!=0); break; case MATCH_OP: if (subtype!=RE_ST){ LM_CRIT("bad type %d, RE expected\n", subtype); goto error; } ret=(regexec((regex_t*)param, str, 0, 0, 0)==0); break; case NOTMATCH_OP: if (subtype!=RE_ST){ LM_CRIT("bad type %d, RE expected!\n", subtype); goto error; } ret=(regexec((regex_t*)param, str, 0, 0, 0)!=0); break; default: LM_CRIT("unknown op %d\n", op); goto error; } return ret; error: return -1; } /*! \brief check_self wrapper -- it checks also for the op */ inline static int check_self_op(int op, str* s, unsigned short p) { int ret; ret=check_self(s, p, 0); switch(op){ case EQUAL_OP: break; case DIFF_OP: if (ret>=0) ret=!ret; break; default: LM_CRIT("invalid operator %d\n", op); ret=-1; } return ret; } /*! \brief eval_elem helping function, returns an op param */ inline static int comp_ip(struct sip_msg *msg, int op, struct ip_addr* ip, operand_t *opd) { struct hostent* he; char ** h; int ret; str tmp; ret=-1; switch(opd->type){ case NET_ST: switch(op){ case EQUAL_OP: ret=(matchnet(ip, (struct net*)opd->v.data)==1); break; case DIFF_OP: ret=(matchnet(ip, (struct net*)opd->v.data)!=1); break; default: goto error_op; } break; case STR_ST: case RE_ST: switch(op){ case EQUAL_OP: case MATCH_OP: /* 1: compare with ip2str*/ ret=comp_str(ip_addr2a(ip), opd->v.data, op, opd->type); if (ret==1) break; /* 2: resolve (name) & compare w/ all the ips */ if (opd->type==STR_ST){ he=resolvehost((char*)opd->v.data,0); if (he==0){ LM_DBG("could not resolve %s\n",(char*)opd->v.data); }else if (he->h_addrtype==(int)ip->af){ for(h=he->h_addr_list;(ret!=1)&& (*h); h++){ ret=(memcmp(ip->u.addr, *h, ip->len)==0); } if (ret==1) break; } } /* 3: (slow) rev dns the address * and compare with all the aliases * !!??!! review: remove this? */ if(received_dns & DO_REV_DNS) { he=rev_resolvehost(ip); if (he==0){ print_ip("could not rev_resolve ip address: ", ip, "\n"); ret=0; }else{ /* compare with primary host name */ ret=comp_str(he->h_name, opd->v.data, op, opd->type); /* compare with all the aliases */ for(h=he->h_aliases; (ret!=1) && (*h); h++){ ret=comp_str(*h, opd->v.data, op, opd->type); } } } else { return 0; } break; case DIFF_OP: ret=comp_ip(msg, MATCH_OP, ip, opd); if (ret>=0) ret=!ret; break; default: goto error_op; } break; case MYSELF_ST: /* check if it's one of our addresses*/ tmp.s=ip_addr2a(ip); tmp.len=strlen(tmp.s); ret=check_self_op(op, &tmp, 0); break; default: LM_CRIT("invalid type for src_ip or dst_ip (%d)\n", opd->type); ret=-1; } return ret; error_op: LM_CRIT("invalid operator %d\n", op); return -1; } /*! \brief compare str to str */ inline static int comp_s2s(int op, str *s1, str *s2) { char backup; char backup2; int n; int rt; int ret; regex_t* re; ret = -1; /* check the input values : * s1 - must be a non-empty string * s2 - must be a non-empty string or a regexp* for [NOT]MATCH_OP */ if ( s1->s==NULL ) return 0; switch(op) { case EQUAL_OP: if ( s2->s==NULL || s1->len != s2->len) return 0; ret=(strncasecmp(s1->s, s2->s, s2->len)==0); break; case DIFF_OP: if ( s2->s==NULL ) return 0; if(s1->len != s2->len) return 1; ret=(strncasecmp(s1->s, s2->s, s2->len)!=0); break; case GT_OP: if ( s2->s==NULL ) return 0; n = (s1->len>=s2->len)?s1->len:s2->len; rt = strncasecmp(s1->s,s2->s, n); if (rt>0) ret = 1; else if(rt==0 && s1->len>s2->len) ret = 1; else ret = 0; break; case GTE_OP: if ( s2->s==NULL ) return 0; n = (s1->len>=s2->len)?s1->len:s2->len; rt = strncasecmp(s1->s,s2->s, n); if (rt>0) ret = 1; else if(rt==0 && s1->len>=s2->len) ret = 1; else ret = 0; break; case LT_OP: if ( s2->s==NULL ) return 0; n = (s1->len>=s2->len)?s1->len:s2->len; rt = strncasecmp(s1->s,s2->s, n); if (rt<0) ret = 1; else if(rt==0 && s1->lenlen) ret = 1; else ret = 0; break; case LTE_OP: if ( s2->s==NULL ) return 0; n = (s1->len>=s2->len)?s1->len:s2->len; rt = strncasecmp(s1->s,s2->s, n); if (rt<0) ret = 1; else if(rt==0 && s1->len<=s2->len) ret = 1; else ret = 0; break; case MATCH_OP: if ( s2==NULL || s1->len == 0 ) return 0; backup = s1->s[s1->len]; s1->s[s1->len] = '\0'; ret=(regexec((regex_t*)s2, s1->s, 0, 0, 0)==0); s1->s[s1->len] = backup; break; case NOTMATCH_OP: if ( s2==NULL || s1->len == 0 ) return 0; backup = s1->s[s1->len]; s1->s[s1->len] = '\0'; ret=(regexec((regex_t*)s2, s1->s, 0, 0, 0)!=0); s1->s[s1->len] = backup; break; case MATCHD_OP: case NOTMATCHD_OP: if ( s2->s==NULL || s1->len == 0 ) return 0; re=(regex_t*)pkg_malloc(sizeof(regex_t)); if (re==0) { LM_CRIT("pkg memory allocation failure\n"); return -1; } backup = s1->s[s1->len]; s1->s[s1->len] = '\0'; backup2 = s2->s[s2->len]; s2->s[s2->len] = '\0'; if (regcomp(re, s2->s, REG_EXTENDED|REG_NOSUB|REG_ICASE)) { pkg_free(re); s2->s[s2->len] = backup2; s1->s[s1->len] = backup; return -1; } if(op==MATCHD_OP) ret=(regexec(re, s1->s, 0, 0, 0)==0); else ret=(regexec(re, s1->s, 0, 0, 0)!=0); regfree(re); pkg_free(re); s2->s[s2->len] = backup2; s1->s[s1->len] = backup; break; default: LM_CRIT("unknown op %d\n", op); } return ret; } /*! \brief compare nr to nr */ inline static int comp_n2n(int op, int n1, int n2) { switch(op) { case EQUAL_OP: case MATCH_OP: case MATCHD_OP: if(n1 == n2) return 1; return 0; case NOTMATCH_OP: case NOTMATCHD_OP: case DIFF_OP: if(n1 != n2) return 1; return 0; case GT_OP: if(n1 > n2) return 1; return 0; case GTE_OP: if(n1 >= n2) return 1; return 0; case LT_OP: if(n1 < n2) return 1; return 0; case LTE_OP: if(n1 <= n2) return 1; return 0; default: LM_CRIT("unknown op %d\n", op); } return -1; } static inline const char *op_id_2_string(int op_id) { switch (op_id) { case EQUAL_OP: return "EQUAL"; case MATCH_OP: return "REGEXP_MATCH"; case NOTMATCH_OP: return "REGEXP_NO_MATCH"; case MATCHD_OP: return "DYN_REGEXP_MATCH"; case NOTMATCHD_OP: return "DYN_REGEXP_NO_MATCH"; case GT_OP: return "GREATER_THAN"; case LT_OP: return "LESS_THAN"; case GTE_OP: return "GREATER_OR_EQUAL"; case LTE_OP: return "LESS_OR_EQUAL"; case DIFF_OP: return "DIFFERENT_THAN"; case VALUE_OP: return "VALUE"; case NO_OP: default: return "NONE"; } } static inline const char *expr_type_2_string(int expr_type) { switch (expr_type) { case STRING_ST: return "STRING"; case NET_ST: return "NET_MASK"; case NUMBER_ST: return "NUMBER"; case IP_ST: return "IP"; case RE_ST: return "REGEXP"; case PROXY_ST: return "PROXY"; case EXPR_ST: return "EXPRESSION"; case ACTIONS_ST: return "ACTION"; case CMD_ST: return "FUNCTION"; case MODFIXUP_ST: return "MOD_FIXUP"; case MYSELF_ST: return "MYSELF"; case STR_ST: return "STR"; case SOCKID_ST: return "SOCKET"; case SOCKETINFO_ST: return "SOCKET_INFO"; case SCRIPTVAR_ST: return "VARIABLE"; case NULLV_ST: return "NULL"; case BLACKLIST_ST: return "BLACKLIST"; case SCRIPTVAR_ELEM_ST: return "VARIABLE_ELEMENT"; case NOSUBTYPE: default: return"NONE"; } } static inline const char *val_type_2_string(int val_type) { if (val_type&PV_VAL_STR) return "STRING_VAL"; if (val_type&PV_VAL_INT) return "INTEGER_VAL"; if (val_type&PV_VAL_NULL) return "NULL_VAL"; if (val_type&PV_VAL_EMPTY) return "EMPTY_VAL"; return "NO_VAL"; } inline static int comp_scriptvar(struct sip_msg *msg, int op, operand_t *left, operand_t *right) { str lstr; str rstr; int ln; int rn; pv_value_t lvalue; pv_value_t rvalue; int type; lstr.s = 0; lstr.len = 0; rstr.s = 0; rstr.len = 0; ln = 0; rn =0; if(pv_get_spec_value(msg, left->v.spec, &lvalue)!=0) { LM_ERR("cannot get left var value\n"); goto error; } if(right->type==NULLV_ST) { if(op==EQUAL_OP) { if(lvalue.flags&PV_VAL_NULL) return 1; return 0; } else { if(lvalue.flags&PV_VAL_NULL) return 0; return 1; } } lstr = lvalue.rs; ln = lvalue.ri; type = 0; rvalue.flags = 0; /*just for err printing purposes */ if(right->type == SCRIPTVAR_ST) { if(pv_get_spec_value(msg, right->v.spec, &rvalue)!=0) { LM_ERR("cannot get right var value\n"); goto error; } if(rvalue.flags&PV_VAL_NULL || lvalue.flags&PV_VAL_NULL ) { if (rvalue.flags&PV_VAL_NULL && lvalue.flags&PV_VAL_NULL ) return (op==EQUAL_OP)?1:0; return (op==DIFF_OP)?1:0; } if(op==MATCH_OP||op==NOTMATCH_OP) { if(!((rvalue.flags&PV_VAL_STR) && (lvalue.flags&PV_VAL_STR))) goto error_op; if(op==MATCH_OP) return comp_s2s(MATCHD_OP, &lstr, &rvalue.rs); else return comp_s2s(NOTMATCHD_OP, &lstr, &rvalue.rs); } if((rvalue.flags&PV_VAL_INT) && (lvalue.flags&PV_VAL_INT)) { /* comparing int */ rn = rvalue.ri; type =2; } else if((rvalue.flags&PV_VAL_STR) && (lvalue.flags&PV_VAL_STR)) { /* comparing string */ rstr = rvalue.rs; type =1; } else goto error_op; } else { /* null against a not-null constant */ if(lvalue.flags&PV_VAL_NULL) return (op==DIFF_OP || op==NOTMATCH_OP || op==NOTMATCHD_OP)?1:0; if(right->type == NUMBER_ST) { if(!(lvalue.flags&PV_VAL_INT)) goto error_op; /* comparing int */ type =2; rn = right->v.n; } else if(right->type == STR_ST) { if(!(lvalue.flags&PV_VAL_STR)) goto error_op; /* comparing string */ type =1; rstr = right->v.s; } else { if(op==MATCH_OP || op==NOTMATCH_OP) { if(!(lvalue.flags&PV_VAL_STR) || right->type != RE_ST) goto error_op; return comp_s2s(op, &lstr, (str*)right->v.expr); } /* comparing others */ type = 0; } } if(type==1) { /* compare str */ LM_DBG("str %d : %.*s\n", op, lstr.len, ZSW(lstr.s)); return comp_s2s(op, &lstr, &rstr); } else if(type==2) { LM_DBG("int %d : %d / %d\n", op, ln, rn); return comp_n2n(op, ln, rn); } /* default is error */ error_op: LM_WARN("invalid %s operation: left is %s/%s, right is %s/%s\n", op_id_2_string(op), expr_type_2_string(left->type), val_type_2_string(lvalue.flags), expr_type_2_string(right->type), val_type_2_string(rvalue.flags) ); error: return -1; } /*! \brief * \return 0/1 (false/true) or -1 on error, -127 EXPR_DROP */ static int eval_elem(struct expr* e, struct sip_msg* msg, pv_value_t *val) { struct sip_uri uri; int ret; /* int retl; int retr; */ int ival; pv_value_t lval; pv_value_t rval; char *p; int i,n; ret=E_BUG; if (e->type!=ELEM_T){ LM_CRIT("invalid type\n"); goto error; } if(val) memset(val, 0, sizeof(pv_value_t)); switch(e->left.type){ case METHOD_O: ret=comp_strval(msg, e->op, &msg->first_line.u.request.method, &e->right); break; case URI_O: if(msg->new_uri.s){ if (e->right.type==MYSELF_ST){ if (parse_sip_msg_uri(msg)<0) ret=-1; else ret=check_self_op(e->op, &msg->parsed_uri.host, msg->parsed_uri.port_no? msg->parsed_uri.port_no:SIP_PORT); }else{ ret=comp_strval(msg, e->op, &msg->new_uri, &e->right); } }else{ if (e->right.type==MYSELF_ST){ if (parse_sip_msg_uri(msg)<0) ret=-1; else ret=check_self_op(e->op, &msg->parsed_uri.host, msg->parsed_uri.port_no? msg->parsed_uri.port_no:SIP_PORT); }else{ ret=comp_strval(msg, e->op, &msg->first_line.u.request.uri, &e->right); } } break; case FROM_URI_O: if (parse_from_header(msg)<0){ LM_ERR("bad or missing From: header\n"); goto error; } if (e->right.type==MYSELF_ST){ if (parse_uri(get_from(msg)->uri.s, get_from(msg)->uri.len, &uri) < 0){ LM_ERR("bad uri in From:\n"); goto error; } ret=check_self_op(e->op, &uri.host, uri.port_no?uri.port_no:SIP_PORT); }else{ ret=comp_strval(msg, e->op, &get_from(msg)->uri, &e->right); } break; case TO_URI_O: if ((msg->to==0) && ((parse_headers(msg, HDR_TO_F, 0)==-1) || (msg->to==0))){ LM_ERR("bad or missing To: header\n"); goto error; } /* to content is parsed automatically */ if (e->right.type==MYSELF_ST){ if (parse_uri(get_to(msg)->uri.s, get_to(msg)->uri.len, &uri) < 0){ LM_ERR("bad uri in To:\n"); goto error; } ret=check_self_op(e->op, &uri.host, uri.port_no?uri.port_no:SIP_PORT); }else{ ret=comp_strval(msg, e->op, &get_to(msg)->uri, &e->right); } break; case SRCIP_O: ret=comp_ip(msg, e->op, &msg->rcv.src_ip, &e->right); break; case DSTIP_O: ret=comp_ip(msg, e->op, &msg->rcv.dst_ip, &e->right); break; case NUMBER_O: ret=!(!e->right.v.n); /* !! to transform it in {0,1} */ break; case ACTION_O: ret=run_action_list( (struct action*)e->right.v.data, msg); if(val) { val->flags = PV_TYPE_INT|PV_VAL_INT; val->ri = ret; } if (ret<=0) ret=(ret==0)?EXPR_DROP:0; else ret=1; return ret; case EXPR_O: /* retl = retr = 0; */ memset(&lval, 0, sizeof(pv_value_t)); memset(&rval, 0, sizeof(pv_value_t)); if(e->left.v.data) eval_expr((struct expr*)e->left.v.data,msg,&lval); /* XXX why is retl used here ?? */ /* retl=eval_expr((struct expr*)e->left.v.data,msg,&lval); */ if(lval.flags == PV_VAL_NONE) { pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } if(e->op == BNOT_OP) { if(lval.flags&PV_VAL_INT) { if(val!=NULL) { val->flags = PV_TYPE_INT|PV_VAL_INT; val->ri = ~lval.ri; } pv_value_destroy(&lval); pv_value_destroy(&rval); return (val->ri)?1:0; } LM_ERR("binary NOT on non-numeric value\n"); pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } if(e->right.v.data) eval_expr((struct expr*)e->right.v.data,msg,&rval); /* retr=eval_expr((struct expr*)e->right.v.data,msg,&rval); */ if(lval.flags&PV_TYPE_INT) { if( (rval.flags&PV_VAL_NULL) ) { rval.ri = 0; } else if(!(rval.flags&PV_VAL_INT)) { LM_ERR("invalid numeric operands\n"); pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } if(val!=NULL) val->flags = PV_TYPE_INT|PV_VAL_INT; ival = 0; switch(e->op) { case PLUS_OP: ival = lval.ri + rval.ri; break; case MINUS_OP: ival = lval.ri - rval.ri; break; case DIV_OP: if(rval.ri==0) { LM_ERR("divide by 0\n"); pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } else ival = lval.ri / rval.ri; break; case MULT_OP: ival = lval.ri * rval.ri; break; case MODULO_OP: if(rval.ri==0) { LM_ERR("divide by 0\n"); pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } else ival = lval.ri % rval.ri; break; case BAND_OP: ival = lval.ri & rval.ri; break; case BOR_OP: ival = lval.ri | rval.ri; break; case BXOR_OP: ival = lval.ri ^ rval.ri; break; case BLSHIFT_OP: ival = lval.ri << rval.ri; break; case BRSHIFT_OP: ival = lval.ri >> rval.ri; break; default: LM_ERR("invalid int op %d\n", e->op); val->ri = 0; pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } pv_value_destroy(&lval); pv_value_destroy(&rval); if(val!=NULL) val->ri = ival; return (ival)?1:0; } else if (e->op == PLUS_OP) { if (!val) { ret = (lval.rs.len>0 || rval.rs.len>0); pv_value_destroy(&lval); pv_value_destroy(&rval); return ret; } if (rval.flags & PV_VAL_NULL) { pv_value_destroy(&rval); rval.flags = PV_VAL_STR; } if(!(rval.flags&PV_VAL_STR)) { LM_ERR("invalid string operands\n"); pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } val->rs.s=(char*)pkg_malloc((lval.rs.len+rval.rs.len+1) *sizeof(char)); if(val->rs.s==0) { LM_ERR("no more memory\n"); pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } val->flags = PV_VAL_PKG|PV_VAL_STR; memcpy(val->rs.s, lval.rs.s, lval.rs.len); memcpy(val->rs.s+lval.rs.len, rval.rs.s, rval.rs.len); val->rs.len = lval.rs.len + rval.rs.len; val->rs.s[val->rs.len] = '\0'; pv_value_destroy(&lval); pv_value_destroy(&rval); return 1; } else if ((lval.flags & PV_VAL_STR) && (rval.flags & PV_VAL_STR)) { if (lval.rs.len != rval.rs.len) { LM_ERR("Different length string operands\n"); pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } n = lval.rs.len; val->rs.s = pkg_malloc(n+1); if (!val->rs.s) { LM_ERR("no more memory\n"); pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } switch(e->op) { case BAND_OP: for (i=0;irs.s[i] = lval.rs.s[i] & rval.rs.s[i]; break; case BOR_OP: for (i=0;irs.s[i] = lval.rs.s[i] | rval.rs.s[i]; break; case BXOR_OP: for (i=0;irs.s[i] = lval.rs.s[i] ^ rval.rs.s[i]; break; default: LM_ERR("Only bitwise operations can be applied on strings\n"); val->ri = 0; pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } val->flags = PV_VAL_PKG|PV_VAL_STR; val->rs.len = n; val->rs.s[n] = '\0'; pv_value_destroy(&lval); pv_value_destroy(&rval); return 1; } else { LM_ERR("Invalid operator : %d \n",e->op); pv_value_destroy(&lval); pv_value_destroy(&rval); return 0; } break; case SRCPORT_O: ret=comp_no(msg->rcv.src_port, e->right.v.data, /* e.g., 5060 */ e->op, /* e.g. == */ e->right.type /* 5060 is number */); break; case DSTPORT_O: ret=comp_no(msg->rcv.dst_port, e->right.v.data, e->op, e->right.type); break; case PROTO_O: ret=comp_no(msg->rcv.proto, e->right.v.data, e->op, e->right.type); break; case AF_O: ret=comp_no(msg->rcv.src_ip.af, e->right.v.data, e->op, e->right.type); break; case RETCODE_O: ret=comp_no(return_code, e->right.v.data, e->op, e->right.type); break; case MSGLEN_O: ret=comp_no(msg->len, e->right.v.data, e->op, e->right.type); break; case STRINGV_O: if(val) { val->flags = PV_VAL_STR; val->rs = e->left.v.s; } /* optimization for no dup ?!?! */ return (e->left.v.s.len>0)?1:0; case NUMBERV_O: if(val) { val->flags = PV_TYPE_INT|PV_VAL_INT; val->ri = e->left.v.n; } ret=!(!e->left.v.n); /* !! to transform it in {0,1} */ return ret; case SCRIPTVAR_O: if(e->op==NO_OP) { memset(&rval, 0, sizeof(pv_value_t)); if(pv_get_spec_value(msg, e->right.v.spec, &rval)==0) { if(rval.flags==PV_VAL_NONE || (rval.flags&PV_VAL_NULL) || (rval.flags&PV_VAL_EMPTY) || ((rval.flags&PV_TYPE_INT)&&rval.ri==0)) { pv_value_destroy(&rval); return 0; } if(rval.flags&PV_TYPE_INT) { pv_value_destroy(&rval); return 1; } if(rval.rs.len!=0) { pv_value_destroy(&rval); return 1; } pv_value_destroy(&rval); } return 0; } if(e->op==VALUE_OP) { if(pv_get_spec_value(msg, e->left.v.spec, &lval)==0) { if(val!=NULL) memcpy(val, &lval, sizeof(pv_value_t)); if(lval.flags&PV_VAL_STR) { if(!((lval.flags&PV_VAL_PKG) || (lval.flags&PV_VAL_SHM))) { if(val!=NULL) { /* do pkg duplicate */ p = (char*)pkg_malloc((val->rs.len+1) *sizeof(char)); if(p==0) { LM_ERR("no more pkg memory\n"); memset(val, 0, sizeof(pv_value_t)); return 0; } memcpy(p, val->rs.s, val->rs.len); p[val->rs.len] = 0; val->rs.s = p; val->flags|= PV_VAL_PKG; } } return 1; } if(lval.flags==PV_VAL_NONE || (lval.flags & PV_VAL_NULL) || (lval.flags & PV_VAL_EMPTY)) return 0; if(lval.flags&PV_TYPE_INT) return (lval.ri!=0); else return (lval.rs.len>0); } return 0; } ret=comp_scriptvar(msg, e->op, &e->left, &e->right); break; default: LM_CRIT("invalid operand %d\n", e->left.type); } if(val) { val->flags = PV_TYPE_INT|PV_VAL_INT; val->ri = ret; } return ret; error: if(val) { val->flags = PV_TYPE_INT|PV_VAL_INT; val->ri = -1; } return -1; } /*! \return ret= 0/1 (false/true) , -1 on error or EXPR_DROP (-127) */ int eval_expr(struct expr* e, struct sip_msg* msg, pv_value_t *val) { static int rec_lev=0; int ret; rec_lev++; if (rec_lev>MAX_REC_LEV){ LM_CRIT("too many expressions (%d)\n", rec_lev); ret=-1; goto skip; } if (e->type==ELEM_T){ ret=eval_elem(e, msg, val); }else if (e->type==EXP_T){ switch(e->op){ case AND_OP: ret=eval_expr(e->left.v.expr, msg, val); /* if error or false stop evaluating the rest */ if (ret!=1) break; ret=eval_expr(e->right.v.expr, msg, val); /*ret1 is 1*/ break; case OR_OP: ret=eval_expr(e->left.v.expr, msg, val); /* if true or error stop evaluating the rest */ if (ret!=0) break; ret=eval_expr(e->right.v.expr, msg, val); /* ret1 is 0 */ break; case NOT_OP: ret=eval_expr(e->left.v.expr, msg, val); if (ret<0) break; ret= ! ret; break; case EVAL_OP: ret=eval_expr(e->left.v.expr, msg, val); break; default: LM_CRIT("unknown op %d\n", e->op); ret=-1; } }else{ LM_CRIT("unknown type %d\n", e->type); ret=-1; } skip: rec_lev--; return ret; } /*! \brief adds an action list to head; a must be null terminated (last a->next=0)) */ void push(struct action* a, struct action** head) { struct action *t; if (*head==0){ *head=a; return; } for (t=*head; t->next;t=t->next); t->next=a; } int add_actions(struct action* a, struct action** head) { int ret; LM_DBG("fixing actions...\n"); if ((ret=fix_actions(a))!=0) goto error; push(a,head); return 0; error: return ret; } /*! \brief fixes all action tables * \return 0 if ok , <0 on error */ int fix_rls(void) { int i,ret; for(i=0;inext ) { switch (a->type) { case ROUTE_T: /* this route is already on the current path ? */ for( n=0 ; nelem[0].u.number) break; } if (n!=rcheck_stack_p) break; if (++rcheck_stack_p==RT_NO) { LM_CRIT("stack overflow (%d)\n", rcheck_stack_p); goto error; } rcheck_stack[rcheck_stack_p] = a->elem[0].u.number; if (check_actions( rlist[a->elem[0].u.number].a, r_type)!=0) goto error; rcheck_stack_p--; break; case IF_T: if (check_actions((struct action*)a->elem[1].u.data, r_type)!=0) goto error; if (check_actions((struct action*)a->elem[2].u.data, r_type)!=0) goto error; break; case WHILE_T: if (check_actions((struct action*)a->elem[1].u.data, r_type)!=0) goto error; break; case FOR_EACH_T: if (check_actions((struct action*)a->elem[2].u.data, r_type)!=0) goto error; break; case SWITCH_T: aitem = (struct action*)a->elem[1].u.data; for( ; aitem ; aitem=aitem->next ) { n = check_actions((struct action*)aitem->elem[1].u.data, r_type); if (n!=0) goto error; } break; case MODULE_T: /* do check :D */ fct = (cmd_export_t*)(a->elem[0].u.data); if ( (fct->flags&r_type)!=r_type ) { rcheck_status = -1; LM_ERR("script function " "\"%s\" (types=%d) does not support route type " "(%d)\n",fct->name, fct->flags, r_type); for( n=rcheck_stack_p-1; n>=0 ; n-- ) { LM_ERR("route stack[%d]=%d\n",n,rcheck_stack[n]); } } break; default: break; } } return 0; error: return -1; } /*! \brief check all routing tables for compatiblity between * route types and called module functions; * \return 0 if ok , <0 on error */ int check_rls(void) { int i,ret; rcheck_status = 0; if(rlist[0].a){ if ((ret=check_actions(rlist[0].a,REQUEST_ROUTE))!=0){ LM_ERR("check failed for main request route\n"); return ret; } } for(i=0;i #include #include #include "pvar.h" #include "config.h" #include "error.h" #include "route_struct.h" #include "parser/msg_parser.h" /* * Definition of a script route */ struct script_route{ char *name; /* name of the route */ struct action *a; /* the actions tree defining the route logic */ }; struct script_timer_route{ unsigned int interval; struct action* a; }; #define EV_ROUTE_SYNC 0 #define EV_ROUTE_ASYNC 1 struct script_event_route{ char *name; int mode; struct action *a; }; extern struct script_route rlist[RT_NO]; /*!< main "script table" */ extern struct script_route onreply_rlist[ONREPLY_RT_NO]; /*!< main reply route table */ extern struct script_route failure_rlist[FAILURE_RT_NO]; /*!< Failure route table */ extern struct script_route branch_rlist[BRANCH_RT_NO]; /*!< Branch routes table */ extern struct script_route local_rlist; /*!< Local route table */ extern struct script_route error_rlist; /*!< Error route table */ extern struct script_route startup_rlist; /*!< Startup route table */ extern struct script_timer_route timer_rlist[TIMER_RT_NO]; /*!< Timer route table */ extern struct script_event_route event_rlist[EVENT_RT_NO]; /*!< Events route table */ #define REQUEST_ROUTE 1 /*!< Request route block */ #define FAILURE_ROUTE 2 /*!< Negative-reply route block */ #define ONREPLY_ROUTE 4 /*!< Received-reply route block */ #define BRANCH_ROUTE 8 /*!< Sending-branch route block */ #define ERROR_ROUTE 16 /*!< Error-handling route block */ #define LOCAL_ROUTE 32 /*!< Local-requests route block */ #define STARTUP_ROUTE 64 /*!< Startup route block */ #define TIMER_ROUTE 128 /*!< Timer route block */ #define EVENT_ROUTE 256 /*!< Event route block */ extern int route_type; #define set_route_type(_new_type) \ do{\ route_type=_new_type;\ }while(0) #define swap_route_type(_backup, _new_type) \ do{\ _backup=route_type;\ route_type=_new_type;\ }while(0) #define is_route_type(_type) (route_type==_type) void init_route_lists(); int get_script_route_idx( char* name, struct script_route *sr, int size, int set); int get_script_route_ID_by_name(char *name, struct script_route *sr, int size); void push(struct action* a, struct action** head); int add_actions(struct action* a, struct action** head); void print_rl(); int fix_rls(); int check_rls(); int eval_expr(struct expr* e, struct sip_msg* msg, pv_value_t *val); int run_startup_route(void); int is_script_func_used( char *name, int param_no); int is_script_async_func_used( char *name, int param_no); #endif opensips-2.2.2/route_struct.c000066400000000000000000000410661300170765700163000ustar00rootroot00000000000000/* * route structures helping functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-01-29 src_port introduced (jiri) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-12 FORCE_RPORT_T added (andrei) * 2003-10-02 added SET_ADV_ADDRESS & SET_ADV_PORT (andrei) * 2006-03-02 mk_action -> mk_action_2p and mk_action3 -> mk_action_3p; * both functions take as extra param the cfg line (bogdan) * 2006-12-22 support for script and branch flags added (bogdan) */ /*! * \file * \brief SIP routing engine - structure helping functions */ #include "route_struct.h" #include #include #include #include "sr_module.h" #include "dprint.h" #include "ip_addr.h" #include "mem/mem.h" #include "ut.h" /* ZSW() */ struct expr* mk_exp(int op, struct expr* left, struct expr* right) { struct expr * e; e=(struct expr*)pkg_malloc(sizeof (struct expr)); if (e==0) goto error; e->type=EXP_T; e->op=op; e->left.v.expr=left; e->right.v.expr=right; return e; error: LM_CRIT("pkg memory allocation failure\n"); return 0; } struct expr* mk_elem(int op, int leftt, void *leftd, int rightt, void *rightd) { struct expr * e; e=(struct expr*)pkg_malloc(sizeof (struct expr)); if (e==0) goto error; memset(e, 0, sizeof(struct expr)); e->type=ELEM_T; e->op=op; e->left.type = leftt; e->left.v.data = leftd; if((e->left.type==STR_ST || e->left.type==STRINGV_O) && e->left.v.s.s!=NULL) e->left.v.s.len = strlen(e->left.v.s.s); e->right.type = rightt; e->right.v.data = rightd; if((e->right.type==STR_ST || e->right.type==STRINGV_O) && e->right.v.s.s!=0) e->right.v.s.len = strlen(e->right.v.s.s); return e; error: LM_CRIT("pkg memory allocation failure\n"); return 0; } struct action* mk_action(int type, int n, action_elem_t *elem, int line, char *file) { int i; struct action* a; if(n>MAX_ACTION_ELEMS) { LM_ERR("too many action elements at %s:%d for %d", file, line, type); return 0; } a=(struct action*)pkg_malloc(sizeof(struct action)); if (a==0) goto error; memset(a,0,sizeof(struct action)); a->type=type; for(i=0; ielem[i].type = elem[i].type; a->elem[i].u.data = elem[i].u.data; if(a->elem[i].type==STR_ST && a->elem[i].u.s.s!=NULL) a->elem[i].u.s.len = strlen(a->elem[i].u.s.s); } a->line = line; a->file = file; a->next=0; return a; error: LM_CRIT("pkg memory allocation failure\n"); return 0; } struct action* append_action(struct action* a, struct action* b) { struct action *t; if (b==0) return a; if (a==0) return b; for(t=a;t->next;t=t->next); t->next=b; return a; } void print_expr(struct expr* exp) { if (exp==0){ LM_CRIT("null expression!\n"); return; } if (exp->type==ELEM_T){ switch(exp->left.type){ case METHOD_O: LM_GEN1(L_DBG, "method"); break; case URI_O: LM_GEN1(L_DBG, "uri"); break; case FROM_URI_O: LM_GEN1(L_DBG, "from_uri"); break; case TO_URI_O: LM_GEN1(L_DBG, "to_uri"); break; case SRCIP_O: LM_GEN1(L_DBG, "srcip"); break; case SRCPORT_O: LM_GEN1(L_DBG, "srcport"); break; case DSTIP_O: LM_GEN1(L_DBG, "dstip"); break; case DSTPORT_O: LM_GEN1(L_DBG, "dstport"); break; case SCRIPTVAR_O: LM_GEN1(L_DBG, "scriptvar[%d]", (exp->left.v.spec)?exp->left.v.spec->type:0); break; case NUMBER_O: case NUMBERV_O: LM_GEN1(L_DBG, "%d",exp->left.v.n); break; case STRINGV_O: LM_GEN1(L_DBG, "\"%s\"", ZSW((char*)exp->left.v.data)); break; case ACTION_O: break; case EXPR_O: print_expr((struct expr*)exp->left.v.data); break; default: LM_GEN1(L_DBG, "UNKNOWN[%d]", exp->left.type); } switch(exp->op){ case EQUAL_OP: LM_GEN1(L_DBG, "=="); break; case MATCHD_OP: case MATCH_OP: LM_GEN1(L_DBG, "=~"); break; case NOTMATCHD_OP: case NOTMATCH_OP: LM_GEN1(L_DBG, "!~"); break; case GT_OP: LM_GEN1(L_DBG, ">"); break; case GTE_OP: LM_GEN1(L_DBG, ">="); break; case LT_OP: LM_GEN1(L_DBG, "<"); break; case LTE_OP: LM_GEN1(L_DBG, "<="); break; case DIFF_OP: LM_GEN1(L_DBG, "!="); break; case PLUS_OP: LM_GEN1(L_DBG, "+"); break; case MINUS_OP: LM_GEN1(L_DBG, "-"); break; case DIV_OP: LM_GEN1(L_DBG, "/"); break; case MULT_OP: LM_GEN1(L_DBG, "*"); break; case MODULO_OP: LM_GEN1(L_DBG, " mod "); break; case BAND_OP: LM_GEN1(L_DBG, "&"); break; case BOR_OP: LM_GEN1(L_DBG, "|"); break; case BXOR_OP: LM_GEN1(L_DBG, "^"); break; case BLSHIFT_OP: LM_GEN1(L_DBG, "<<"); break; case BRSHIFT_OP: LM_GEN1(L_DBG, ">>"); break; case BNOT_OP: LM_GEN1(L_DBG, "~"); break; case VALUE_OP: case NO_OP: break; default: LM_GEN1(L_DBG, "", exp->op); } switch(exp->right.type){ case NOSUBTYPE: /* LM_GEN1(L_DBG, "N/A"); */ break; case STRING_ST: LM_GEN1(L_DBG, "\"%s\"", ZSW((char*)exp->right.v.data)); break; case NET_ST: print_net((struct net*)exp->right.v.data); break; case IP_ST: print_ip("", (struct ip_addr*)exp->right.v.data, ""); break; case ACTIONS_ST: print_actions((struct action*)exp->right.v.data); break; case NUMBER_ST: LM_GEN1(L_DBG, "%d",exp->right.v.n); break; case MYSELF_ST: LM_GEN1(L_DBG, "_myself_"); break; case SCRIPTVAR_ST: LM_GEN1(L_DBG, "scriptvar[%d]", exp->right.v.spec->type); break; case NULLV_ST: LM_GEN1(L_DBG, "null"); break; case EXPR_ST: print_expr((struct expr*)exp->right.v.data); break; default: LM_GEN1(L_DBG, "type<%d>", exp->right.type); } }else if (exp->type==EXP_T){ switch(exp->op){ case AND_OP: LM_GEN1(L_DBG, "AND( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case OR_OP: LM_GEN1(L_DBG, "OR( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case NOT_OP: LM_GEN1(L_DBG, "NOT( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, " )"); break; case EVAL_OP: LM_GEN1(L_DBG, "EVAL( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, " )"); break; case PLUS_OP: LM_GEN1(L_DBG, "PLUS( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case MINUS_OP: LM_GEN1(L_DBG, "MINUS( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case DIV_OP: LM_GEN1(L_DBG, "DIV( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case MULT_OP: LM_GEN1(L_DBG, "MULT( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case MODULO_OP: LM_GEN1(L_DBG, "MODULO( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case BAND_OP: LM_GEN1(L_DBG, "BAND( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case BOR_OP: LM_GEN1(L_DBG, "BOR( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case BXOR_OP: LM_GEN1(L_DBG, "BXOR( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case BLSHIFT_OP: LM_GEN1(L_DBG, "BLSHIFT( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case BRSHIFT_OP: LM_GEN1(L_DBG, "BRSHIFT( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, ", "); print_expr(exp->right.v.expr); LM_GEN1(L_DBG, " )"); break; case BNOT_OP: LM_GEN1(L_DBG, "BNOT( "); print_expr(exp->left.v.expr); LM_GEN1(L_DBG, " )"); break; default: LM_GEN1(L_DBG, "UNKNOWN_EXP[%d] ", exp->op); } }else{ LM_ERR("unknown type\n"); } } void print_action(struct action* t) { switch(t->type){ case FORWARD_T: LM_GEN1(L_DBG, "forward("); break; case SEND_T: LM_GEN1(L_DBG, "send("); break; case ASSERT_T: LM_GEN1(L_DBG, "assert("); break; case DROP_T: LM_GEN1(L_DBG, "drop("); break; case LOG_T: LM_GEN1(L_DBG, "log("); break; case ERROR_T: LM_GEN1(L_DBG, "error("); break; case ROUTE_T: LM_GEN1(L_DBG, "route("); break; case EXEC_T: LM_GEN1(L_DBG, "exec("); break; case REVERT_URI_T: LM_GEN1(L_DBG, "revert_uri("); break; case STRIP_T: LM_GEN1(L_DBG, "strip("); break; case APPEND_BRANCH_T: LM_GEN1(L_DBG, "append_branch("); break; case PREFIX_T: LM_GEN1(L_DBG, "prefix("); break; case LEN_GT_T: LM_GEN1(L_DBG, "len_gt("); break; case SETFLAG_T: LM_GEN1(L_DBG, "setflag("); break; case RESETFLAG_T: LM_GEN1(L_DBG, "resetflag("); break; case ISFLAGSET_T: LM_GEN1(L_DBG, "isflagset("); break; case SETBFLAG_T: LM_GEN1(L_DBG, "setbflag("); break; case RESETBFLAG_T: LM_GEN1(L_DBG, "resetbflag("); break; case ISBFLAGSET_T: LM_GEN1(L_DBG, "isbflagset("); break; case SET_HOST_T: LM_GEN1(L_DBG, "sethost("); break; case SET_HOSTPORT_T: LM_GEN1(L_DBG, "sethostport("); break; case SET_USER_T: LM_GEN1(L_DBG, "setuser("); break; case SET_USERPASS_T: LM_GEN1(L_DBG, "setuserpass("); break; case SET_PORT_T: LM_GEN1(L_DBG, "setport("); break; case SET_URI_T: LM_GEN1(L_DBG, "seturi("); break; case IF_T: LM_GEN1(L_DBG, "if ("); break; case WHILE_T: LM_GEN1(L_DBG, "while ("); break; case MODULE_T: LM_GEN1(L_DBG, " external_module_call("); break; case FORCE_RPORT_T: LM_GEN1(L_DBG, "force_rport("); break; case SET_ADV_ADDR_T: LM_GEN1(L_DBG, "set_advertised_address("); break; case SET_ADV_PORT_T: LM_GEN1(L_DBG, "set_advertised_port("); break; case FORCE_TCP_ALIAS_T: LM_GEN1(L_DBG, "force_tcp_alias("); break; case FORCE_SEND_SOCKET_T: LM_GEN1(L_DBG, "force_send_socket"); break; case RETURN_T: LM_GEN1(L_DBG, "return("); break; case EXIT_T: LM_GEN1(L_DBG, "exit("); break; case SWITCH_T: LM_GEN1(L_DBG, "switch("); break; case CASE_T: LM_GEN1(L_DBG, "case("); break; case DEFAULT_T: LM_GEN1(L_DBG, "default("); break; case SBREAK_T: LM_GEN1(L_DBG, "sbreak("); break; case EQ_T: LM_GEN1(L_DBG, "assign("); break; default: LM_GEN1(L_DBG, "UNKNOWN("); } switch(t->elem[0].type){ case STRING_ST: LM_GEN1(L_DBG, "\"%s\"", ZSW(t->elem[0].u.string)); break; case NUMBER_ST: LM_GEN1(L_DBG, "%lu",t->elem[0].u.number); break; case SCRIPTVAR_ST: LM_GEN1(L_DBG, "scriptvar[%d]",t->elem[0].u.item->type); break; case IP_ST: print_ip("", (struct ip_addr*)t->elem[0].u.data, ""); break; case EXPR_ST: print_expr((struct expr*)t->elem[0].u.data); break; case ACTIONS_ST: print_actions((struct action*)t->elem[0].u.data); break; case CMD_ST: LM_GEN1(L_DBG, "f<%s>",((cmd_export_t*)t->elem[0].u.data)->name); break; case SOCKID_ST: LM_GEN1(L_DBG, "%d:%s:%d", ((struct socket_id*)t->elem[0].u.data)->proto, ZSW(((struct socket_id*)t->elem[0].u.data)->name), ((struct socket_id*)t->elem[0].u.data)->port ); break; default: LM_GEN1(L_DBG, "type<%d>", t->elem[0].type); } if (t->type==IF_T) LM_GEN1(L_DBG, ") {"); switch(t->elem[1].type){ case NOSUBTYPE: break; case STRING_ST: LM_GEN1(L_DBG, ", \"%s\"", ZSW(t->elem[1].u.string)); break; case NUMBER_ST: LM_GEN1(L_DBG, ", %lu",t->elem[1].u.number); break; case EXPR_ST: print_expr((struct expr*)t->elem[1].u.data); break; case ACTIONS_ST: print_actions((struct action*)t->elem[1].u.data); break; case SOCKID_ST: LM_GEN1(L_DBG, "%d:%s:%d", ((struct socket_id*)t->elem[1].u.data)->proto, ZSW(((struct socket_id*)t->elem[1].u.data)->name), ((struct socket_id*)t->elem[1].u.data)->port ); break; default: LM_GEN1(L_DBG, ", type<%d>", t->elem[1].type); } if (t->type==IF_T && t->elem[2].type!=NOSUBTYPE) LM_GEN1(L_DBG, " } else { "); switch(t->elem[2].type){ case NOSUBTYPE: break; case STRING_ST: LM_GEN1(L_DBG, ", \"%s\"", ZSW(t->elem[2].u.string)); break; case NUMBER_ST: LM_GEN1(L_DBG, ", %lu",t->elem[2].u.number); break; case EXPR_ST: print_expr((struct expr*)t->elem[2].u.data); break; case ACTIONS_ST: print_actions((struct action*)t->elem[2].u.data); break; case SOCKID_ST: LM_GEN1(L_DBG, "%d:%s:%d", ((struct socket_id*)t->elem[2].u.data)->proto, ZSW(((struct socket_id*)t->elem[2].u.data)->name), ((struct socket_id*)t->elem[2].u.data)->port ); break; default: LM_GEN1(L_DBG, ", type<%d>", t->elem[2].type); } if (t->type==IF_T) LM_GEN1(L_DBG, "}; "); else LM_GEN1(L_DBG, "); "); } void print_actions(struct action* a) { while(a) { print_action(a); a = a->next; } } static int is_mod_func_in_expr(struct expr *e, char *name, int param_no) { if (e->type==ELEM_T) { if (e->left.type==ACTION_O) if (is_mod_func_used((struct action*)e->right.v.data,name,param_no)==1) return 1; } else if (e->type==EXP_T) { if (e->left.v.expr && is_mod_func_in_expr(e->left.v.expr,name,param_no)==1) return 1; if (e->right.v.expr && is_mod_func_in_expr(e->right.v.expr,name,param_no)==1) return 1; } return 0; } int is_mod_func_used(struct action *a, char *name, int param_no) { cmd_export_t *cmd; while(a) { if (a->type==MODULE_T) { /* first param is the name of the function */ cmd = (cmd_export_t*)a->elem[0].u.data; if (strcasecmp(cmd->name, name)==0 && (param_no==cmd->param_no || param_no==-1) ) { LM_DBG("function %s found to be used in script\n",name); return 1; } } if (a->type==IF_T || a->type==WHILE_T) if (is_mod_func_in_expr((struct expr*)a->elem[0].u.data, name,param_no)==1) return 1; /* follow all leads from actions than may have * sub-blocks of instructions */ if (a->elem[0].type==ACTIONS_ST) if (is_mod_func_used((struct action*)a->elem[0].u.data, name,param_no)==1) return 1; if (a->elem[1].type==ACTIONS_ST) if (is_mod_func_used((struct action*)a->elem[1].u.data, name,param_no)==1) return 1; if (a->elem[2].type==ACTIONS_ST) if (is_mod_func_used((struct action*)a->elem[2].u.data, name,param_no)==1) return 1; a = a->next; } return 0; } int is_mod_async_func_used(struct action *a, char *name, int param_no) { acmd_export_t *acmd; for (; a; a = a->next) { if (a->type == ASYNC_T) { acmd = ((struct action *)(a->elem[0].u.data))->elem[0].u.data; LM_DBG("checking %s against %s\n", name, acmd->name); if (strcasecmp(acmd->name, name) == 0 && (param_no == acmd->param_no || param_no == -1)) return 1; } /* follow all leads from actions than may have * sub-blocks of instructions */ if (a->elem[0].type==ACTIONS_ST) if (is_mod_async_func_used((struct action*)a->elem[0].u.data, name,param_no)==1) return 1; if (a->elem[1].type==ACTIONS_ST) if (is_mod_async_func_used((struct action*)a->elem[1].u.data, name,param_no)==1) return 1; if (a->elem[2].type==ACTIONS_ST) if (is_mod_async_func_used((struct action*)a->elem[2].u.data, name,param_no)==1) return 1; } return 0; } opensips-2.2.2/route_struct.h000066400000000000000000000116601300170765700163020ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * * 2003-04-12 FORCE_RPORT_T added (andrei) * 2003-04-22 strip_tail added (jiri) * 2003-10-10 >,<,>=,<=, != and MSGLEN_O added (andrei) * 2003-10-28 FORCE_TCP_ALIAS added (andrei) * 2006-03-02 new field "line" in action struct - the cfg line (bogdan) * 2006-12-22 support for script and branch flags added (bogdan) */ /*! * \file * \brief SIP routing engine - structure management */ #ifndef route_struct_h #define route_struct_h #define EXPR_DROP -127 /* used only by the expression and if evaluator */ /* * Other important values (no macros for them yet): * expr true = 1 * expr false = 0 (used only inside the expression and if evaluator) * * action continue or if used in condition true = 1 * action drop/quit/stop script processing = 0 * action error or if used in condition false = -1 (<0 and !=EXPR_DROP) * */ /*! \todo Add documentation for all ENUMs in this file. */ enum { EXP_T=1, ELEM_T }; enum { AND_OP=1, OR_OP, NOT_OP, EVAL_OP, PLUS_OP, MINUS_OP, DIV_OP, MULT_OP, MODULO_OP, BAND_OP, BOR_OP, BXOR_OP, BNOT_OP, BLSHIFT_OP, BRSHIFT_OP }; enum { EQUAL_OP=20, MATCH_OP, NOTMATCH_OP, MATCHD_OP, NOTMATCHD_OP, GT_OP, LT_OP, GTE_OP, LTE_OP, DIFF_OP, VALUE_OP, NO_OP }; enum { METHOD_O=1, URI_O, FROM_URI_O, TO_URI_O, SRCIP_O, SRCPORT_O, DSTIP_O, DSTPORT_O, PROTO_O, AF_O, MSGLEN_O, DEFAULT_O, ACTION_O, EXPR_O, NUMBER_O, NUMBERV_O, STRINGV_O, RETCODE_O, SCRIPTVAR_O}; enum { FORWARD_T=1, SEND_T, ASSERT_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T, SET_HOST_T, SET_HOSTPORT_T, SET_USER_T, SET_USERPASS_T, SET_PORT_T, SET_URI_T, IF_T, MODULE_T, AMODULE_T, SETFLAG_T, RESETFLAG_T, ISFLAGSET_T , SETBFLAG_T, RESETBFLAG_T, ISBFLAGSET_T , LEN_GT_T, PREFIX_T, STRIP_T,STRIP_TAIL_T, APPEND_BRANCH_T, REMOVE_BRANCH_T, REVERT_URI_T, FORCE_RPORT_T, FORCE_LOCAL_RPORT_T, SET_ADV_ADDR_T, SET_ADV_PORT_T, FORCE_TCP_ALIAS_T, FORCE_SEND_SOCKET_T, SERIALIZE_BRANCHES_T, NEXT_BRANCHES_T, RETURN_T, EXIT_T, SWITCH_T, CASE_T, DEFAULT_T, SBREAK_T, WHILE_T, FOR_EACH_T, SET_DSTURI_T, SET_DSTHOST_T, SET_DSTPORT_T, RESET_DSTURI_T, ISDSTURISET_T, EQ_T, COLONEQ_T, PLUSEQ_T, MINUSEQ_T, DIVEQ_T, MULTEQ_T, MODULOEQ_T, BANDEQ_T, BOREQ_T, BXOREQ_T, USE_BLACKLIST_T, UNUSE_BLACKLIST_T, SET_TIME_STAMP_T,RESET_TIME_STAMP_T, DIFF_TIME_STAMP_T, PV_PRINTF_T, CACHE_STORE_T, CACHE_FETCH_T, CACHE_COUNTER_FETCH_T, CACHE_REMOVE_T, CACHE_ADD_T,CACHE_SUB_T,CACHE_RAW_QUERY_T, XDBG_T, XLOG_T, RAISE_EVENT_T, SUBSCRIBE_EVENT_T, CONSTRUCT_URI_T, GET_TIMESTAMP_T, SCRIPT_TRACE_T, ASYNC_T }; enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST, EXPR_ST, ACTIONS_ST, CMD_ST, ACMD_ST, MODFIXUP_ST, MYSELF_ST, STR_ST, SOCKID_ST, SOCKETINFO_ST, SCRIPTVAR_ST, NULLV_ST, BLACKLIST_ST, SCRIPTVAR_ELEM_ST}; struct expr; #include "pvar.h" typedef struct operand { int type; union operand_val { struct expr* expr; str s; int n; pv_spec_t* spec; void* data; } v; } operand_t, *operand_p; struct expr{ int type; /*!< exp, exp_elem */ int op; /*!< and, or, not | ==, =~ */ operand_t left; operand_t right; }; typedef struct action_elem_ { int type; union { long number; char* string; void* data; str s; pv_spec_t* item; } u; } action_elem_t, *action_elem_p; /*! \brief increase MAX_ACTION_ELEMS to support more module function parameters if you change this define, you need also to change the assignment in the action.c file */ #define MAX_ACTION_ELEMS 7 struct action{ int type; /* forward, drop, log, send ...*/ action_elem_t elem[MAX_ACTION_ELEMS]; int line; char *file; struct action* next; }; struct expr* mk_exp(int op, struct expr* left, struct expr* right); struct expr* mk_elem(int op, int leftt, void *leftd, int rightt, void *rightd); struct action* mk_action(int type, int n, action_elem_t *elem, int line, char *file); struct action* append_action(struct action* a, struct action* b); void print_action(struct action* a); void print_expr(struct expr* exp); void print_actions(struct action* a); int is_mod_func_used(struct action *a, char *name, int param_no); int is_mod_async_func_used(struct action *a, char *name, int param_no); #endif opensips-2.2.2/rw_locking.h000066400000000000000000000050301300170765700156700ustar00rootroot00000000000000/* * Copyright (C) 2016 OpenSIPS Project * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef _rw_locking_h #define _rw_locking_h #include #include "locking.h" #define LOCK_WAIT 10 typedef struct rw_lock_t { gen_lock_t *lock; int w_flag; int r_count; } rw_lock_t; inline static rw_lock_t * lock_init_rw(void) { rw_lock_t * new_lock; new_lock = (rw_lock_t*)shm_malloc(sizeof(rw_lock_t)); if (!new_lock) goto error; memset(new_lock, 0, sizeof(rw_lock_t)); new_lock->lock = lock_alloc(); if (!new_lock->lock) goto error; if (!lock_init(new_lock->lock)) goto error; return new_lock; error: if (new_lock!=NULL && new_lock->lock) lock_dealloc(new_lock->lock); if (new_lock) shm_free(new_lock); return NULL; } inline static void lock_destroy_rw(rw_lock_t *_lock) { if (!_lock) return; if (_lock->lock) { lock_destroy(_lock->lock); lock_dealloc(_lock->lock); } shm_free(_lock); } #define lock_start_write(_lock) \ do { \ __label__ again; \ again: \ lock_get((_lock)->lock); \ /* wait for the other writers */ \ if ((_lock)->w_flag) { \ lock_release((_lock)->lock); \ usleep(LOCK_WAIT); \ goto again; \ } \ (_lock)->w_flag = 1; \ lock_release((_lock)->lock); \ /* wait for readers */ \ while ((_lock)->r_count) \ usleep(LOCK_WAIT); \ } while (0) #define lock_stop_write(_lock) \ do { \ (_lock)->w_flag = 0; \ } while(0) #define lock_start_read(_lock) \ do { \ __label__ again; \ again: \ lock_get((_lock)->lock); \ if ((_lock)->w_flag) { \ lock_release((_lock)->lock); \ usleep(LOCK_WAIT); \ goto again; \ } \ (_lock)->r_count++; \ lock_release((_lock)->lock); \ } while (0) #define lock_stop_read(_lock) \ do { \ lock_get((_lock)->lock); \ (_lock)->r_count--; \ lock_release((_lock)->lock); \ } while (0) #endif opensips-2.2.2/script_cb.c000066400000000000000000000163501300170765700155040ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-29 cleaning pkg allocation introduced (jiri) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2005-02-13 script callbacks devided into request and reply types (bogdan) * 2009-05-21 keep the callback lists in the same order as callbacks were registered (bogdan) */ /*! * \file * \brief Script callbacks */ #include #include "script_cb.h" #include "dprint.h" #include "error.h" #include "mem/mem.h" static struct script_cb *pre_req_cb=0; static struct script_cb *post_req_cb=0; static struct script_cb *pre_rpl_cb=0; static struct script_cb *post_rpl_cb=0; static struct script_cb *parse_err_cb=0; static unsigned int cb_id=0; struct raw_processing_cb_list* pre_processing_cb_list = NULL; struct raw_processing_cb_list* post_processing_cb_list = NULL; static inline int add_callback( struct script_cb **list, cb_function f, void *param, int prio) { struct script_cb *last_cb; struct script_cb *new_cb; new_cb=pkg_malloc(sizeof(struct script_cb)); if (new_cb==0) { LM_ERR("out of pkg memory\n"); return -1; } new_cb->cbf = f; new_cb->id = cb_id++; new_cb->param = param; new_cb->next = NULL; new_cb->prio = prio; /* descending priority sorting; equal priorities are inserted at the end it is important to keep the order at register time, as this reflects the order of loading/init the modules --bogdan */ if (*list==NULL) { *list = new_cb; } else if ((*list)->prio < prio) { new_cb->next = *list; *list = new_cb; } else { for (last_cb = *list; last_cb->next && last_cb->next->prio >= prio; last_cb = last_cb->next) ; new_cb->next = last_cb->next; last_cb->next = new_cb; } return 0; } int __register_script_cb( cb_function f, int type, void *param, int prio) { /* type checkings */ if ( (type&(REQ_TYPE_CB|RPL_TYPE_CB|PARSE_ERR_CB))==0 ) { LM_CRIT("request / reply / error type not specified\n"); goto error; } if ( (type&(PRE_SCRIPT_CB|POST_SCRIPT_CB|PARSE_ERR_CB))==0 || (type&PRE_SCRIPT_CB && type&POST_SCRIPT_CB) ) { LM_CRIT("callback POST or PRE type must be exactly one\n"); goto error; } if (type&PARSE_ERR_CB) { if (add_callback( &parse_err_cb, f, param, prio)<0) goto add_error; } if (type&REQ_TYPE_CB) { /* callback for request script */ if (type&PRE_SCRIPT_CB) { if (add_callback( &pre_req_cb, f, param, prio)<0) goto add_error; } else if (type&POST_SCRIPT_CB) { if (add_callback( &post_req_cb, f, param, prio)<0) goto add_error; } } if (type&RPL_TYPE_CB) { /* callback (also) for reply script */ if (type&PRE_SCRIPT_CB) { if (add_callback( &pre_rpl_cb, f, param, prio)<0) goto add_error; } else if (type&POST_SCRIPT_CB) { if (add_callback( &post_rpl_cb, f, param, prio)<0) goto add_error; } } return 0; add_error: LM_ERR("failed to add callback\n"); error: return -1; } static inline void destroy_cb_list(struct script_cb **list) { struct script_cb *foo; while( *list ) { foo = *list; *list = (*list)->next; pkg_free( foo ); } } void destroy_script_cb(void) { destroy_cb_list( &pre_req_cb ); destroy_cb_list( &post_req_cb ); destroy_cb_list( &pre_rpl_cb ); destroy_cb_list( &post_req_cb ); destroy_cb_list( &parse_err_cb ); } static inline int exec_pre_cb( struct sip_msg *msg, struct script_cb *cb) { int bitmask = SCB_RUN_ALL; for ( ; cb ; cb=cb->next ) { bitmask &= cb->cbf(msg, cb->param); if (bitmask == SCB_DROP_MSG) break; } return bitmask; } static inline int exec_post_cb( struct sip_msg *msg, struct script_cb *cb) { for ( ; cb ; cb=cb->next){ cb->cbf( msg, cb->param); } return 1; } int exec_pre_req_cb( struct sip_msg *msg) { return exec_pre_cb( msg, pre_req_cb); } int exec_pre_rpl_cb( struct sip_msg *msg) { return exec_pre_cb( msg, pre_rpl_cb); } int exec_post_req_cb( struct sip_msg *msg) { return exec_post_cb( msg, post_req_cb); } int exec_post_rpl_cb( struct sip_msg *msg) { return exec_post_cb( msg, post_rpl_cb); } int exec_parse_err_cb( struct sip_msg *msg) { return exec_post_cb( msg, parse_err_cb); } static inline int insert_raw_processing_cb(raw_processing_func f, int type, struct raw_processing_cb_list* list, char freeable) { struct raw_processing_cb_list *elem; if (f == NULL) { LM_ERR("null callback\n"); return -1; } elem = pkg_malloc(sizeof(struct raw_processing_cb_list)); if (elem == NULL) { LM_ERR("no more pkg mem\n"); return -1; } elem->f = f; elem->freeable = freeable; elem->next = NULL; if (list == NULL) { list = elem; return !(type==PRE_RAW_PROCESSING ? (pre_processing_cb_list=list) : (post_processing_cb_list=list)); } else { while (list->next != NULL) list = list->next; list->next=elem; } return 0; } int register_pre_raw_processing_cb(raw_processing_func f, int type, char freeable) { return insert_raw_processing_cb(f, type, pre_processing_cb_list, freeable); } int register_post_raw_processing_cb(raw_processing_func f, int type, char freeable) { return insert_raw_processing_cb(f, type, post_processing_cb_list, freeable); } int run_pre_raw_processing_cb(int type, str* data, struct sip_msg* msg) { return run_raw_processing_cb(type, data, msg, pre_processing_cb_list); } int run_post_raw_processing_cb(int type, str* data, struct sip_msg* msg) { return run_raw_processing_cb(type, data, msg, post_processing_cb_list); } int run_raw_processing_cb(int type, str *data, struct sip_msg* msg, struct raw_processing_cb_list* list) { struct raw_processing_cb_list *foo=NULL, *last_good=NULL, *head=NULL; char *initial_data = data->s, *input_data; int rc; if (list == NULL) return 0; while (list) { input_data = data->s; /* a return code bigger than 0 means you want to keep the callback */ if ((rc = list->f(data, msg)) < 0) { LM_ERR("failed to run callback\n"); return -1; } if (input_data != initial_data && input_data != data->s) pkg_free(input_data); foo = list; list = list->next; if (foo != NULL) { if (foo->freeable && rc == 0) { /* foo will be gone so link the last good element * to the next one */ if (last_good) last_good->next=list; pkg_free(foo); } else { /* keep the first element not to be freed */ if (head == NULL) head = foo; /* and keep track of the last viable element to link with the * next viable element */ last_good = foo; } } } return !(type==PRE_RAW_PROCESSING?(pre_processing_cb_list=head) :(post_processing_cb_list=head)); } opensips-2.2.2/script_cb.h000066400000000000000000000064421300170765700155120ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2005-02-13 script callbacks devided into request and reply types (bogdan) */ /*! * \file * \brief Script callbacks */ #ifndef _SCRIPT_CB_H_ #define _SCRIPT_CB_H_ #include "parser/msg_parser.h" /** * @return: * - SCB_DROP_MSG stops the processing (also skips any other callbacks) * - a combination of the SCB_ flags or SCB_RUN_ALL for any other cases * * NB: return values are logically AND'ed * (one module may wish to skip top route, others may skip post callbacks) */ typedef int (cb_function)( struct sip_msg *msg, void *param ); #define SCB_DROP_MSG 0 #define SCB_RUN_TOP_ROUTE (1<<0) #define SCB_RUN_POST_CBS (1<<1) #define SCB_RUN_ALL (SCB_RUN_TOP_ROUTE | SCB_RUN_POST_CBS) #define PRE_SCRIPT_CB (1<<0) #define POST_SCRIPT_CB (1<<1) #define REQ_TYPE_CB (1<<2) #define RPL_TYPE_CB (1<<3) #define PARSE_ERR_CB (1<<4) /* helper is any type of data that can be used in further processing */ typedef int (*raw_processing_func)(str *data, struct sip_msg* msg); #define PRE_RAW_PROCESSING (1<<0) #define POST_RAW_PROCESSING (1<<1) struct raw_processing_cb_list { raw_processing_func f; struct raw_processing_cb_list* next; char freeable; /* set this parameter if you want to register your callback per message( registration at every message) else put it 0, register callback only once and callback will be called for every message that reaches msg_send */ }; struct script_cb { cb_function *cbf; struct script_cb *next; unsigned int id; void *param; int prio; /* allows callback ordering; highest runs first */ }; #define register_script_cb(func, type, param) \ __register_script_cb(func, type, param, 0) /* sorted by priority in descending order (highest prio callback runs first) */ int __register_script_cb( cb_function f, int type, void *param, int prio); void destroy_script_cb(); int exec_pre_req_cb( struct sip_msg *msg); int exec_post_req_cb( struct sip_msg *msg); int exec_pre_rpl_cb( struct sip_msg *msg); int exec_post_rpl_cb( struct sip_msg *msg); int exec_parse_err_cb( struct sip_msg *msg); int register_pre_raw_processing_cb(raw_processing_func f, int type, char freeable); int register_post_raw_processing_cb(raw_processing_func f, int type, char freeable); int run_pre_raw_processing_cb(int type, str* data, struct sip_msg* msg); int run_post_raw_processing_cb(int type, str* data, struct sip_msg* msg); int run_raw_processing_cb(int type,str *data, struct sip_msg* msg, struct raw_processing_cb_list* list); #endif opensips-2.2.2/script_var.c000066400000000000000000000100051300170765700156770ustar00rootroot00000000000000/* * Copyright (C) 2006 voice-system.ro * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief Script variables */ #include #include #include #include #include "dprint.h" #include "mem/mem.h" #include "script_var.h" static script_var_t *script_vars = 0; script_var_t* add_var(str *name) { script_var_t *it; if(name==0 || name->s==0 || name->len<=0) return 0; for(it=script_vars; it; it=it->next) { if(it->name.len==name->len && strncmp(name->s, it->name.s, name->len)==0) return it; } it = (script_var_t*)pkg_malloc(sizeof(script_var_t)); if(it==0) { LM_ERR("out of pkg mem\n"); return 0; } memset(it, 0, sizeof(script_var_t)); it->name.s = (char*)pkg_malloc((name->len+1)*sizeof(char)); it->v.flags = VAR_VAL_NULL; if(it->name.s==0) { LM_ERR("out of pkg mem!\n"); return 0; } it->name.len = name->len; strncpy(it->name.s, name->s, name->len); it->name.s[it->name.len] = '\0'; it->next = script_vars; script_vars = it; return it; } script_var_t* set_var_value(script_var_t* var, int_str *value, int flags) { if(var==0) return 0; if(value==NULL || flags&VAR_VAL_NULL) { if(var->v.flags&VAR_VAL_STR) pkg_free(var->v.value.s.s); memset(&var->v.value, 0, sizeof(int_str)); var->v.flags = VAR_VAL_NULL; return var; } if(flags&VAR_VAL_STR) { if(var->v.flags&VAR_VAL_STR) { /* old and new values are str */ if(value->s.len>var->v.value.s.len) { /* not enough space to copy */ pkg_free(var->v.value.s.s); memset(&var->v.value, 0, sizeof(int_str)); var->v.value.s.s = (char*)pkg_malloc((value->s.len+1)*sizeof(char)); if(var->v.value.s.s==0) { LM_ERR("out of pkg mem\n"); goto error; } } } else { /* old value was INT or NULL */ memset(&var->v.value, 0, sizeof(int_str)); var->v.value.s.s = (char*)pkg_malloc((value->s.len+1)*sizeof(char)); if(var->v.value.s.s==0) { LM_ERR("out of pkg mem!\n"); goto error; } var->v.flags = VAR_VAL_STR; } strncpy(var->v.value.s.s, value->s.s, value->s.len); var->v.value.s.len = value->s.len; var->v.value.s.s[value->s.len] = '\0'; } else { /* new value is INT */ if(var->v.flags&VAR_VAL_STR) { pkg_free(var->v.value.s.s); memset(&var->v.value, 0, sizeof(int_str)); } var->v.flags = 0; /* no STR, no NULL */ var->v.value.n = value->n; } return var; error: /* set the var to init value */ memset(&var->v.value, 0, sizeof(int_str)); var->v.flags = VAR_VAL_NULL; return NULL; } script_var_t* get_var_by_name(str *name) { script_var_t *it; if(name==0 || name->s==0 || name->len<=0) return 0; for(it=script_vars; it; it=it->next) { if(it->name.len==name->len && strncmp(name->s, it->name.s, name->len)==0) return it; } return 0; } void reset_vars(void) { script_var_t *it; for(it=script_vars; it; it=it->next) { if(it->v.flags&VAR_VAL_STR) pkg_free(it->v.value.s.s); it->v.flags = VAR_VAL_NULL ; memset(&it->v.value, 0, sizeof(int_str)); } } void destroy_vars_list(script_var_t *svl) { script_var_t *it; script_var_t *it0; it = svl; while(it) { it0 = it; it = it->next; pkg_free(it0->name.s); if(it0->v.flags&VAR_VAL_STR) pkg_free(it0->v.value.s.s); pkg_free(it0); } svl = 0; } void destroy_vars(void) { destroy_vars_list(script_vars); } opensips-2.2.2/script_var.h000066400000000000000000000026371300170765700157200ustar00rootroot00000000000000/* * Copyright (C) 2006 voice-system.ro * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief Script variables */ #ifndef _SCRIPT_VAR_H_ #define _SCRIPT_VAR_H_ #include "usr_avp.h" #define VAR_VAL_STR (1<<0) #define VAR_VAL_NULL (1<<1) typedef struct script_val { int flags; int_str value; } script_val_t, *script_val_p; typedef struct script_var { str name; script_val_t v; struct script_var *next; } script_var_t, *script_var_p; script_var_t* add_var(str *name); script_var_t* set_var_value(script_var_t *var, int_str *value, int flags); script_var_t* get_var_by_name(str *name); void reset_vars(); void destroy_vars(); void destroy_vars_list(script_var_t *svl); #endif opensips-2.2.2/scripts/000077500000000000000000000000001300170765700150525ustar00rootroot00000000000000opensips-2.2.2/scripts/db_berkeley/000077500000000000000000000000001300170765700173215ustar00rootroot00000000000000opensips-2.2.2/scripts/db_berkeley/opensips/000077500000000000000000000000001300170765700211615ustar00rootroot00000000000000opensips-2.2.2/scripts/db_berkeley/opensips/acc000066400000000000000000000004471300170765700216370ustar00rootroot00000000000000METADATA_COLUMNS id(int) method(str) from_tag(str) to_tag(str) callid(str) sip_code(str) sip_reason(str) time(datetime) duration(int) ms_duration(int) setuptime(int) created(datetime) METADATA_KEY 4 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|''|NIL|0|0|0|NULL opensips-2.2.2/scripts/db_berkeley/opensips/active_watchers000066400000000000000000000007531300170765700242640ustar00rootroot00000000000000METADATA_COLUMNS id(int) presentity_uri(str) watcher_username(str) watcher_domain(str) to_user(str) to_domain(str) event(str) event_id(str) to_tag(str) from_tag(str) callid(str) local_cseq(int) remote_cseq(int) contact(str) record_route(str) expires(int) status(int) reason(str) version(int) socket_info(str) local_contact(str) METADATA_KEY 1 6 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|'presence'|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|2|NIL|0|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/address000066400000000000000000000003231300170765700225270ustar00rootroot00000000000000METADATA_COLUMNS id(int) grp(int) ip(str) mask(int) port(int) proto(str) pattern(str) context_info(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0|NIL|32|0|'any'|NULL|NULL opensips-2.2.2/scripts/db_berkeley/opensips/aliases000066400000000000000000000007041300170765700225260ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) contact(str) received(str) path(str) expires(datetime) q(double) callid(str) cseq(int) last_modified(datetime) flags(int) cflags(str) user_agent(str) socket(str) methods(int) sip_instance(str) attr(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|NULL|NULL|'2020-05-28 21:32:15'|1.0|'Default-Call-ID'|13|'1900-01-01 00:00:01'|0|NULL|''|NULL|NULL|NULL|NULL opensips-2.2.2/scripts/db_berkeley/opensips/b2b_entities000066400000000000000000000010241300170765700234520ustar00rootroot00000000000000METADATA_COLUMNS id(int) type(int) state(int) ruri(str) from_uri(str) to_uri(str) from_dname(str) to_dname(str) tag0(str) tag1(str) callid(str) cseq0(int) cseq1(int) contact0(str) contact1(str) route0(str) route1(str) sockinfo_srv(str) param(str) lm(int) lrc(int) lic(int) leg_cseq(int) leg_route(str) leg_tag(str) leg_contact(str) leg_sockinfo(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/b2b_logic000066400000000000000000000010231300170765700227220ustar00rootroot00000000000000METADATA_COLUMNS id(int) si_key(str) scenario(str) sstate(int) next_sstate(int) sparam0(str) sparam1(str) sparam2(str) sparam3(str) sparam4(str) sdp(str) lifetime(int) e1_type(int) e1_sid(str) e1_from(str) e1_to(str) e1_key(str) e2_type(int) e2_sid(str) e2_from(str) e2_to(str) e2_key(str) e3_type(int) e3_sid(str) e3_from(str) e3_to(str) e3_key(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|0|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/b2b_sca000066400000000000000000000031311300170765700223750ustar00rootroot00000000000000METADATA_COLUMNS id(int) shared_line(str) watchers(str) app1_shared_entity(int) app1_call_state(int) app1_call_info_uri(str) app1_call_info_appearance_uri(str) app1_b2bl_key(str) app2_shared_entity(int) app2_call_state(int) app2_call_info_uri(str) app2_call_info_appearance_uri(str) app2_b2bl_key(str) app3_shared_entity(int) app3_call_state(int) app3_call_info_uri(str) app3_call_info_appearance_uri(str) app3_b2bl_key(str) app4_shared_entity(int) app4_call_state(int) app4_call_info_uri(str) app4_call_info_appearance_uri(str) app4_b2bl_key(str) app5_shared_entity(int) app5_call_state(int) app5_call_info_uri(str) app5_call_info_appearance_uri(str) app5_b2bl_key(str) app6_shared_entity(int) app6_call_state(int) app6_call_info_uri(str) app6_call_info_appearance_uri(str) app6_b2bl_key(str) app7_shared_entity(int) app7_call_state(int) app7_call_info_uri(str) app7_call_info_appearance_uri(str) app7_b2bl_key(str) app8_shared_entity(int) app8_call_state(int) app8_call_info_uri(str) app8_call_info_appearance_uri(str) app8_b2bl_key(str) app9_shared_entity(int) app9_call_state(int) app9_call_info_uri(str) app9_call_info_appearance_uri(str) app9_b2bl_key(str) app10_shared_entity(int) app10_call_state(int) app10_call_info_uri(str) app10_call_info_appearance_uri(str) app10_b2bl_key(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL|NULL opensips-2.2.2/scripts/db_berkeley/opensips/cachedb000066400000000000000000000002271300170765700224560ustar00rootroot00000000000000METADATA_COLUMNS keyname(str) value(str) counter(int) expires(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|0|0 opensips-2.2.2/scripts/db_berkeley/opensips/carrierfailureroute000066400000000000000000000004011300170765700251550ustar00rootroot00000000000000METADATA_COLUMNS id(int) carrier(int) domain(str) scan_prefix(str) host_name(str) reply_code(str) flags(int) mask(int) next_domain(str) description(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0|''|''|''|''|0|0|''|NULL opensips-2.2.2/scripts/db_berkeley/opensips/carrierroute000066400000000000000000000004471300170765700236170ustar00rootroot00000000000000METADATA_COLUMNS id(int) carrier(int) domain(str) scan_prefix(str) flags(int) mask(int) prob(double) strip(int) rewrite_host(str) rewrite_prefix(str) rewrite_suffix(str) description(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0|''|''|0|0|0|0|''|''|''|NULL opensips-2.2.2/scripts/db_berkeley/opensips/cc_agents000066400000000000000000000002751300170765700230360ustar00rootroot00000000000000METADATA_COLUMNS id(int) agentid(str) location(str) logstate(int) skills(str) last_call_end(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|0|NIL|0 opensips-2.2.2/scripts/db_berkeley/opensips/cc_calls000066400000000000000000000004611300170765700226500ustar00rootroot00000000000000METADATA_COLUMNS id(int) state(int) ig_cback(int) no_rej(int) setup_time(int) eta(int) last_start(int) recv_time(int) caller_dn(str) caller_un(str) b2buaid(str) flow(str) agent(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|''|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/cc_cdrs000066400000000000000000000004541300170765700225070ustar00rootroot00000000000000METADATA_COLUMNS id(int) caller(str) received_timestamp(datetime) wait_time(int) pickup_time(int) talk_time(int) flow_id(str) agent_id(str) call_type(int) rejected(int) fstats(int) cid(int) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|0|0|0|NIL|NULL|-1|0|0|0 opensips-2.2.2/scripts/db_berkeley/opensips/cc_flows000066400000000000000000000003331300170765700227020ustar00rootroot00000000000000METADATA_COLUMNS id(int) flowid(str) priority(int) skill(str) prependcid(str) message_welcome(str) message_queue(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|256|NIL|NIL|NULL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/closeddial000066400000000000000000000003201300170765700232020ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) cd_username(str) cd_domain(str) group_id(str) new_uri(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|'' opensips-2.2.2/scripts/db_berkeley/opensips/clusterer000066400000000000000000000004101300170765700231070ustar00rootroot00000000000000METADATA_COLUMNS id(int) cluster_id(int) machine_id(int) url(str) state(int) last_attempt(int) failed_attempts(int) no_tries(int) duration(int) description(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|1|0|3|0|30|NIL opensips-2.2.2/scripts/db_berkeley/opensips/cpl000066400000000000000000000002541300170765700216630ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) cpl_xml(str) cpl_bin(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|''|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/dbaliases000066400000000000000000000002651300170765700230360ustar00rootroot00000000000000METADATA_COLUMNS id(int) alias_username(str) alias_domain(str) username(str) domain(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|'' opensips-2.2.2/scripts/db_berkeley/opensips/dialog000066400000000000000000000011501300170765700223400ustar00rootroot00000000000000METADATA_COLUMNS dlg_id(int) callid(str) from_uri(str) from_tag(str) to_uri(str) to_tag(str) mangled_from_uri(str) mangled_to_uri(str) caller_cseq(str) callee_cseq(str) caller_ping_cseq(int) callee_ping_cseq(int) caller_route_set(str) callee_route_set(str) caller_contact(str) callee_contact(str) caller_sock(str) callee_sock(str) state(int) start_time(int) timeout(int) vars(str) profiles(str) script_flags(int) module_flags(int) flags(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NULL|NULL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NULL|NULL|0|0|0 opensips-2.2.2/scripts/db_berkeley/opensips/dialplan000066400000000000000000000004201300170765700226640ustar00rootroot00000000000000METADATA_COLUMNS id(int) dpid(int) pr(int) match_op(int) match_exp(str) match_flags(int) subst_exp(str) repl_exp(str) timerec(str) disabled(int) attrs(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|0|NIL opensips-2.2.2/scripts/db_berkeley/opensips/dispatcher000066400000000000000000000003541300170765700232340ustar00rootroot00000000000000METADATA_COLUMNS id(int) setid(int) destination(str) socket(str) state(int) weight(int) priority(int) attrs(str) description(str) METADATA_KEY 1 4 5 6 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0|''|NULL|0|1|0|''|'' opensips-2.2.2/scripts/db_berkeley/opensips/domain000066400000000000000000000002641300170765700223550ustar00rootroot00000000000000METADATA_COLUMNS id(int) domain(str) attrs(str) last_modified(datetime) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|NULL|'1900-01-01 00:00:01' opensips-2.2.2/scripts/db_berkeley/opensips/domainpolicy000066400000000000000000000002641300170765700235750ustar00rootroot00000000000000METADATA_COLUMNS id(int) rule(str) type(str) att(str) val(str) description(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/dr_carriers000066400000000000000000000003061300170765700234020ustar00rootroot00000000000000METADATA_COLUMNS id(int) carrierid(str) gwlist(str) flags(int) state(int) attrs(str) description(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|0|0|''|'' opensips-2.2.2/scripts/db_berkeley/opensips/dr_gateways000066400000000000000000000004101300170765700234100ustar00rootroot00000000000000METADATA_COLUMNS id(int) gwid(str) type(int) address(str) strip(int) pri_prefix(str) attrs(str) probe_mode(int) state(int) socket(str) description(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|0|NIL|0|NULL|NULL|0|0|NULL|'' opensips-2.2.2/scripts/db_berkeley/opensips/dr_groups000066400000000000000000000002531300170765700231100ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) groupid(int) description(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|''|0|'' opensips-2.2.2/scripts/db_berkeley/opensips/dr_partitions000066400000000000000000000005651300170765700237730ustar00rootroot00000000000000METADATA_COLUMNS id(int) partition_name(str) db_url(str) drd_table(str) drr_table(str) drg_table(str) drc_table(str) ruri_avp(str) gw_id_avp(str) gw_priprefix_avp(str) gw_sock_avp(str) rule_id_avp(str) rule_prefix_avp(str) carrier_id_avp(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/dr_rules000066400000000000000000000003631300170765700227250ustar00rootroot00000000000000METADATA_COLUMNS ruleid(int) groupid(str) prefix(str) timerec(str) priority(int) routeid(str) gwlist(str) attrs(str) description(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|0|NULL|NIL|NULL|'' opensips-2.2.2/scripts/db_berkeley/opensips/emergency_report000066400000000000000000000004751300170765700244630ustar00rootroot00000000000000METADATA_COLUMNS id(int) callid(str) selectiveRoutingID(str) routingESN(int) npa(int) esgwri(str) lro(str) VPC_organizationName(str) VPC_hostname(str) VPC_timestamp(str) result(str) disposition(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|0|0|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/emergency_routing000066400000000000000000000002601300170765700246270ustar00rootroot00000000000000METADATA_COLUMNS id(int) selectiveRoutingID(str) routingESN(int) npa(int) esgwri(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|0|0|NIL opensips-2.2.2/scripts/db_berkeley/opensips/emergency_service_provider000066400000000000000000000003501300170765700265120ustar00rootroot00000000000000METADATA_COLUMNS id(int) organizationName(str) hostId(str) nenaId(str) contact(str) certUri(str) nodeIP(str) attribution(int) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/fraud_detection000066400000000000000000000007721300170765700242510ustar00rootroot00000000000000METADATA_COLUMNS ruleid(int) profileid(int) prefix(str) start_hour(str) end_hour(str) daysoftheweek(str) cpm_warning(int) cpm_critical(int) call_duration_warning(int) call_duration_critical(int) total_calls_warning(int) total_calls_critical(int) concurrent_calls_warning(int) concurrent_calls_critical(int) sequential_calls_warning(int) sequential_calls_critical(int) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/globalblacklist000066400000000000000000000002351300170765700242350ustar00rootroot00000000000000METADATA_COLUMNS id(int) prefix(str) whitelist(int) description(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|0|NULL opensips-2.2.2/scripts/db_berkeley/opensips/grp000066400000000000000000000003051300170765700216720ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) grp(str) last_modified(datetime) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|'1900-01-01 00:00:01' opensips-2.2.2/scripts/db_berkeley/opensips/imc_members000066400000000000000000000002511300170765700233640ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) room(str) flag(int) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/imc_rooms000066400000000000000000000002251300170765700230720ustar00rootroot00000000000000METADATA_COLUMNS id(int) name(str) domain(str) flag(int) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/load_balancer000066400000000000000000000003011300170765700236440ustar00rootroot00000000000000METADATA_COLUMNS id(int) group_id(int) dst_uri(str) resources(str) probe_mode(int) description(str) METADATA_KEY 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0|NIL|NIL|0|'' opensips-2.2.2/scripts/db_berkeley/opensips/location000066400000000000000000000007161300170765700227200ustar00rootroot00000000000000METADATA_COLUMNS contact_id(int) username(str) domain(str) contact(str) received(str) path(str) expires(datetime) q(double) callid(str) cseq(int) last_modified(datetime) flags(int) cflags(str) user_agent(str) socket(str) methods(int) sip_instance(str) attr(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|NULL|''|NULL|NULL|'2020-05-28 21:32:15'|1.0|'Default-Call-ID'|13|'1900-01-01 00:00:01'|0|NULL|''|NULL|NULL|NULL|NULL opensips-2.2.2/scripts/db_berkeley/opensips/missed_calls000066400000000000000000000004041300170765700235440ustar00rootroot00000000000000METADATA_COLUMNS id(int) method(str) from_tag(str) to_tag(str) callid(str) sip_code(str) sip_reason(str) time(datetime) setuptime(int) created(datetime) METADATA_KEY 4 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|''|NIL|0|NULL opensips-2.2.2/scripts/db_berkeley/opensips/presentity000066400000000000000000000004031300170765700233070ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) event(str) etag(str) expires(int) received_time(int) body(str) extra_hdrs(str) sender(str) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|''|NIL opensips-2.2.2/scripts/db_berkeley/opensips/pua000066400000000000000000000007001300170765700216660ustar00rootroot00000000000000METADATA_COLUMNS id(int) pres_uri(str) pres_id(str) event(int) expires(int) desired_expires(int) flag(int) etag(str) tuple_id(str) watcher_uri(str) to_uri(str) call_id(str) to_tag(str) from_tag(str) cseq(int) record_route(str) contact(str) remote_contact(str) version(int) extra_headers(str) METADATA_KEY 1 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/re_grp000066400000000000000000000002061300170765700223600ustar00rootroot00000000000000METADATA_COLUMNS id(int) reg_exp(str) group_id(int) METADATA_KEY 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|0 opensips-2.2.2/scripts/db_berkeley/opensips/registrant000066400000000000000000000004601300170765700232660ustar00rootroot00000000000000METADATA_COLUMNS id(int) registrar(str) proxy(str) aor(str) third_party_registrant(str) username(str) password(str) binding_URI(str) binding_params(str) expiry(int) forced_socket(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|NULL|''|NULL|NULL|NULL|''|NULL|NULL|NULL opensips-2.2.2/scripts/db_berkeley/opensips/rls_presentity000066400000000000000000000004051300170765700241710ustar00rootroot00000000000000METADATA_COLUMNS id(int) rlsubs_did(str) resource_uri(str) content_type(str) presence_state(str) expires(int) updated(int) auth_state(int) reason(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/rls_watchers000066400000000000000000000007531300170765700236110ustar00rootroot00000000000000METADATA_COLUMNS id(int) presentity_uri(str) to_user(str) to_domain(str) watcher_username(str) watcher_domain(str) event(str) event_id(str) to_tag(str) from_tag(str) callid(str) local_cseq(int) remote_cseq(int) contact(str) record_route(str) expires(int) status(int) reason(str) version(int) socket_info(str) local_contact(str) METADATA_KEY 4 5 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|'presence'|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|2|NIL|0|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/route_tree000066400000000000000000000001711300170765700232600ustar00rootroot00000000000000METADATA_COLUMNS id(int) carrier(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NULL opensips-2.2.2/scripts/db_berkeley/opensips/rtpproxy_sockets000066400000000000000000000002161300170765700245450ustar00rootroot00000000000000METADATA_COLUMNS id(int) rtpproxy_sock(str) set_id(int) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/silo000066400000000000000000000003721300170765700220540ustar00rootroot00000000000000METADATA_COLUMNS id(int) src_addr(str) dst_addr(str) username(str) domain(str) inc_time(int) exp_time(int) snd_time(int) ctype(str) body(str) METADATA_KEY 3 4 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|0|0|0|NULL|NULL opensips-2.2.2/scripts/db_berkeley/opensips/sip_trace000066400000000000000000000005561300170765700230630ustar00rootroot00000000000000METADATA_COLUMNS id(int) time_stamp(datetime) callid(str) trace_attrs(str) msg(str) method(str) status(str) from_proto(str) from_ip(str) from_port(int) to_proto(str) to_ip(str) to_port(int) fromtag(str) direction(str) METADATA_KEY 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|'1900-01-01 00:00:01'|''|NULL|NIL|''|NULL|NIL|''|NIL|NIL|''|NIL|''|'' opensips-2.2.2/scripts/db_berkeley/opensips/speed_dial000066400000000000000000000003571300170765700232020ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) sd_username(str) sd_domain(str) new_uri(str) fname(str) lname(str) description(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|''|''|'' opensips-2.2.2/scripts/db_berkeley/opensips/subscriber000066400000000000000000000003301300170765700232430ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) password(str) email_address(str) ha1(str) ha1b(str) rpid(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|''|NULL opensips-2.2.2/scripts/db_berkeley/opensips/tls_mgm000066400000000000000000000005541300170765700225520ustar00rootroot00000000000000METADATA_COLUMNS id(str) address(str) type(int) method(str) verify_cert(int) require_cert(int) certificate(str) private_key(str) crl_check_all(int) crl_dir(str) ca_list(str) ca_dir(str) cipher_list(str) dh_params(str) ec_curve(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/uri000066400000000000000000000003121300170765700216770ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) uri_user(str) last_modified(datetime) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|'1900-01-01 00:00:01' opensips-2.2.2/scripts/db_berkeley/opensips/userblacklist000066400000000000000000000002471300170765700237560ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) prefix(str) whitelist(int) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|0 opensips-2.2.2/scripts/db_berkeley/opensips/usr_preferences000066400000000000000000000003571300170765700243030ustar00rootroot00000000000000METADATA_COLUMNS id(int) uuid(str) username(str) domain(str) attribute(str) type(int) value(str) last_modified(datetime) METADATA_KEY 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|0|''|''|0|''|'1900-01-01 00:00:01' opensips-2.2.2/scripts/db_berkeley/opensips/version000066400000000000000000000031011300170765700225640ustar00rootroot00000000000000METADATA_COLUMNS table_name(str) table_version(int) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0 acc| acc|7 active_watchers| active_watchers|11 address| address|5 aliases| aliases|1009 b2b_entities| b2b_entities|1 b2b_logic| b2b_logic|3 b2b_sca| b2b_sca|1 cachedb| cachedb|2 carrierfailureroute| carrierfailureroute|2 carrierroute| carrierroute|3 cc_agents| cc_agents|1 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|''|NIL|NIL cc_cdrs| cc_cdrs|1 cc_flows| cc_flows|1 closeddial| closeddial|1 clusterer| clusterer|1 cpl| cpl|2 dbaliases| dbaliases|2 dialog| dialog|10 dialplan| dialplan|5 dispatcher| dispatcher|7 domain| domain|3 domainpolicy| domainpolicy|3 dr_carriers| dr_carriers|2 dr_gateways| dr_gateways|6 dr_groups| dr_groups|2 dr_partitions| dr_partitions|1 dr_rules| dr_rules|3 emergency_report| emergency_report|1 emergency_routing| emergency_routing|1 emergency_service_provider| emergency_service_provider|1 fraud_detection| fraud_detection|1 globalblacklist| globalblacklist|2 grp| grp|3 imc_members| imc_members|2 imc_rooms| imc_rooms|2 load_balancer| load_balancer|2 location| location|1011 missed_calls| missed_calls|5 presentity| presentity|5 pua| pua|8 re_grp| re_grp|2 registrant| registrant|1 rls_presentity| rls_presentity|1 rls_watchers| rls_watchers|2 route_tree| route_tree|2 rtpproxy_sockets| rtpproxy_sockets|0 silo| silo|6 sip_trace| sip_trace|5 speed_dial| speed_dial|3 subscriber| subscriber|7 tls_mgm| tls_mgm|1 uri| uri|2 userblacklist| userblacklist|2 usr_preferences| usr_preferences|3 watchers| watchers|4 xcap| xcap|4 opensips-2.2.2/scripts/db_berkeley/opensips/watchers000066400000000000000000000004001300170765700227160ustar00rootroot00000000000000METADATA_COLUMNS id(int) presentity_uri(str) watcher_username(str) watcher_domain(str) event(str) status(int) reason(str) inserted_time(int) METADATA_KEY 1 4 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|'presence'|NIL|NIL|NIL opensips-2.2.2/scripts/db_berkeley/opensips/xcap000066400000000000000000000003471300170765700220430ustar00rootroot00000000000000METADATA_COLUMNS id(int) username(str) domain(str) doc(str) doc_type(int) etag(str) source(int) doc_uri(str) port(int) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL opensips-2.2.2/scripts/dbtext/000077500000000000000000000000001300170765700163445ustar00rootroot00000000000000opensips-2.2.2/scripts/dbtext/opensips/000077500000000000000000000000001300170765700202045ustar00rootroot00000000000000opensips-2.2.2/scripts/dbtext/opensips/acc000066400000000000000000000002721300170765700206560ustar00rootroot00000000000000id(int,auto) method(string) from_tag(string) to_tag(string) callid(string) sip_code(string) sip_reason(string) time(int) duration(int) ms_duration(int) setuptime(int) created(int,null) opensips-2.2.2/scripts/dbtext/opensips/active_watchers000066400000000000000000000005711300170765700233050ustar00rootroot00000000000000id(int,auto) presentity_uri(string) watcher_username(string) watcher_domain(string) to_user(string) to_domain(string) event(string) event_id(string,null) to_tag(string) from_tag(string) callid(string) local_cseq(int) remote_cseq(int) contact(string) record_route(string,null) expires(int) status(int) reason(string,null) version(int) socket_info(string) local_contact(string) opensips-2.2.2/scripts/dbtext/opensips/address000066400000000000000000000001631300170765700215540ustar00rootroot00000000000000id(int,auto) grp(int) ip(string) mask(int) port(int) proto(string) pattern(string,null) context_info(string,null) opensips-2.2.2/scripts/dbtext/opensips/aliases000066400000000000000000000004551300170765700215540ustar00rootroot00000000000000id(int,auto) username(string) domain(string) contact(string) received(string,null) path(string,null) expires(int) q(double) callid(string) cseq(int) last_modified(int) flags(int) cflags(string,null) user_agent(string) socket(string,null) methods(int,null) sip_instance(string,null) attr(string,null) opensips-2.2.2/scripts/dbtext/opensips/b2b_entities000066400000000000000000000007311300170765700225010ustar00rootroot00000000000000id(int,auto) type(int) state(int) ruri(string,null) from_uri(string) to_uri(string) from_dname(string,null) to_dname(string,null) tag0(string) tag1(string,null) callid(string) cseq0(int) cseq1(int,null) contact0(string) contact1(string,null) route0(string,null) route1(string,null) sockinfo_srv(string,null) param(string) lm(int) lrc(int,null) lic(int,null) leg_cseq(int,null) leg_route(string,null) leg_tag(string,null) leg_contact(string,null) leg_sockinfo(string,null) opensips-2.2.2/scripts/dbtext/opensips/b2b_logic000066400000000000000000000007261300170765700217560ustar00rootroot00000000000000id(int,auto) si_key(string) scenario(string,null) sstate(int) next_sstate(int) sparam0(string,null) sparam1(string,null) sparam2(string,null) sparam3(string,null) sparam4(string,null) sdp(string,null) lifetime(int) e1_type(int) e1_sid(string,null) e1_from(string) e1_to(string) e1_key(string) e2_type(int) e2_sid(string,null) e2_from(string) e2_to(string) e2_key(string) e3_type(int,null) e3_sid(string,null) e3_from(string,null) e3_to(string,null) e3_key(string,null) opensips-2.2.2/scripts/dbtext/opensips/b2b_sca000066400000000000000000000031321300170765700214210ustar00rootroot00000000000000id(int,auto) shared_line(string) watchers(string) app1_shared_entity(int,null) app1_call_state(int,null) app1_call_info_uri(string,null) app1_call_info_appearance_uri(string,null) app1_b2bl_key(string,null) app2_shared_entity(int,null) app2_call_state(int,null) app2_call_info_uri(string,null) app2_call_info_appearance_uri(string,null) app2_b2bl_key(string,null) app3_shared_entity(int,null) app3_call_state(int,null) app3_call_info_uri(string,null) app3_call_info_appearance_uri(string,null) app3_b2bl_key(string,null) app4_shared_entity(int,null) app4_call_state(int,null) app4_call_info_uri(string,null) app4_call_info_appearance_uri(string,null) app4_b2bl_key(string,null) app5_shared_entity(int,null) app5_call_state(int,null) app5_call_info_uri(string,null) app5_call_info_appearance_uri(string,null) app5_b2bl_key(string,null) app6_shared_entity(int,null) app6_call_state(int,null) app6_call_info_uri(string,null) app6_call_info_appearance_uri(string,null) app6_b2bl_key(string,null) app7_shared_entity(int,null) app7_call_state(int,null) app7_call_info_uri(string,null) app7_call_info_appearance_uri(string,null) app7_b2bl_key(string,null) app8_shared_entity(int,null) app8_call_state(int,null) app8_call_info_uri(string,null) app8_call_info_appearance_uri(string,null) app8_b2bl_key(string,null) app9_shared_entity(int,null) app9_call_state(int,null) app9_call_info_uri(string,null) app9_call_info_appearance_uri(string,null) app9_b2bl_key(string,null) app10_shared_entity(int,null) app10_call_state(int,null) app10_call_info_uri(string,null) app10_call_info_appearance_uri(string,null) app10_b2bl_key(string,null) opensips-2.2.2/scripts/dbtext/opensips/cachedb000066400000000000000000000000711300170765700214760ustar00rootroot00000000000000keyname(string) value(string) counter(int) expires(int) opensips-2.2.2/scripts/dbtext/opensips/carrierfailureroute000066400000000000000000000002451300170765700242060ustar00rootroot00000000000000id(int,auto) carrier(int) domain(string) scan_prefix(string) host_name(string) reply_code(string) flags(int) mask(int) next_domain(string) description(string,null) opensips-2.2.2/scripts/dbtext/opensips/carrierroute000066400000000000000000000003071300170765700226350ustar00rootroot00000000000000id(int,auto) carrier(int) domain(string) scan_prefix(string) flags(int) mask(int) prob(double) strip(int) rewrite_host(string) rewrite_prefix(string) rewrite_suffix(string) description(string,null) opensips-2.2.2/scripts/dbtext/opensips/cc_agents000066400000000000000000000001371300170765700220560ustar00rootroot00000000000000id(int,auto) agentid(string) location(string) logstate(int) skills(string) last_call_end(int) opensips-2.2.2/scripts/dbtext/opensips/cc_calls000066400000000000000000000002721300170765700216730ustar00rootroot00000000000000id(int,auto) state(int) ig_cback(int) no_rej(int) setup_time(int) eta(int) last_start(int) recv_time(int) caller_dn(string) caller_un(string) b2buaid(string) flow(string) agent(string) opensips-2.2.2/scripts/dbtext/opensips/cc_cdrs000066400000000000000000000003011300170765700215210ustar00rootroot00000000000000id(int,auto) caller(string) received_timestamp(int) wait_time(int) pickup_time(int) talk_time(int) flow_id(string) agent_id(string,null) call_type(int) rejected(int) fstats(int) cid(int,null) opensips-2.2.2/scripts/dbtext/opensips/cc_flows000066400000000000000000000001771300170765700217330ustar00rootroot00000000000000id(int,auto) flowid(string) priority(int) skill(string) prependcid(string) message_welcome(string,null) message_queue(string) opensips-2.2.2/scripts/dbtext/opensips/closeddial000066400000000000000000000001651300170765700222340ustar00rootroot00000000000000id(int,auto) username(string) domain(string) cd_username(string) cd_domain(string) group_id(string) new_uri(string) opensips-2.2.2/scripts/dbtext/opensips/clusterer000066400000000000000000000002421300170765700221350ustar00rootroot00000000000000id(int,auto) cluster_id(int) machine_id(int) url(string) state(int) last_attempt(long) failed_attempts(int) no_tries(int) duration(int) description(string,null) opensips-2.2.2/scripts/dbtext/opensips/cpl000066400000000000000000000001301300170765700206770ustar00rootroot00000000000000id(int,auto) username(string) domain(string) cpl_xml(string,null) cpl_bin(string,null) opensips-2.2.2/scripts/dbtext/opensips/dbaliases000066400000000000000000000001321300170765700220520ustar00rootroot00000000000000id(int,auto) alias_username(string) alias_domain(string) username(string) domain(string) opensips-2.2.2/scripts/dbtext/opensips/dialog000066400000000000000000000010041300170765700213610ustar00rootroot00000000000000dlg_id(long) callid(string) from_uri(string) from_tag(string) to_uri(string) to_tag(string) mangled_from_uri(string,null) mangled_to_uri(string,null) caller_cseq(string) callee_cseq(string) caller_ping_cseq(int) callee_ping_cseq(int) caller_route_set(string,null) callee_route_set(string,null) caller_contact(string,null) callee_contact(string,null) caller_sock(string) callee_sock(string) state(int) start_time(int) timeout(int) vars(blob,null) profiles(string,null) script_flags(int) module_flags(int) flags(int) opensips-2.2.2/scripts/dbtext/opensips/dialplan000066400000000000000000000002641300170765700217150ustar00rootroot00000000000000id(int,auto) dpid(int) pr(int) match_op(int) match_exp(string) match_flags(int) subst_exp(string,null) repl_exp(string,null) timerec(string,null) disabled(int) attrs(string,null) opensips-2.2.2/scripts/dbtext/opensips/dispatcher000066400000000000000000000002101300170765700222460ustar00rootroot00000000000000id(int,auto) setid(int) destination(string) socket(string,null) state(int) weight(int) priority(int) attrs(string) description(string) opensips-2.2.2/scripts/dbtext/opensips/domain000066400000000000000000000001031300170765700213700ustar00rootroot00000000000000id(int,auto) domain(string) attrs(string,null) last_modified(int) opensips-2.2.2/scripts/dbtext/opensips/domainpolicy000066400000000000000000000001361300170765700226160ustar00rootroot00000000000000id(int,auto) rule(string) type(string) att(string,null) val(string,null) description(string) opensips-2.2.2/scripts/dbtext/opensips/dr_carriers000066400000000000000000000001541300170765700224260ustar00rootroot00000000000000id(int,auto) carrierid(string) gwlist(string) flags(int) state(int) attrs(string,null) description(string) opensips-2.2.2/scripts/dbtext/opensips/dr_gateways000066400000000000000000000002561300170765700224430ustar00rootroot00000000000000id(int,auto) gwid(string) type(int) address(string) strip(int) pri_prefix(string,null) attrs(string,null) probe_mode(int) state(int) socket(string,null) description(string) opensips-2.2.2/scripts/dbtext/opensips/dr_groups000066400000000000000000000001171300170765700221320ustar00rootroot00000000000000id(int,auto) username(string) domain(string) groupid(int) description(string) opensips-2.2.2/scripts/dbtext/opensips/dr_partitions000066400000000000000000000005061300170765700230110ustar00rootroot00000000000000id(int,auto) partition_name(string) db_url(string) drd_table(string,null) drr_table(string,null) drg_table(string,null) drc_table(string,null) ruri_avp(string,null) gw_id_avp(string,null) gw_priprefix_avp(string,null) gw_sock_avp(string,null) rule_id_avp(string,null) rule_prefix_avp(string,null) carrier_id_avp(string,null) opensips-2.2.2/scripts/dbtext/opensips/dr_rules000066400000000000000000000002321300170765700217430ustar00rootroot00000000000000ruleid(int,auto) groupid(string) prefix(string) timerec(string) priority(int) routeid(string,null) gwlist(string) attrs(string,null) description(string) opensips-2.2.2/scripts/dbtext/opensips/emergency_report000066400000000000000000000003271300170765700235020ustar00rootroot00000000000000id(int,auto) callid(string) selectiveRoutingID(string) routingESN(int) npa(int) esgwri(string) lro(string) VPC_organizationName(string) VPC_hostname(string) VPC_timestamp(string) result(string) disposition(string) opensips-2.2.2/scripts/dbtext/opensips/emergency_routing000066400000000000000000000001211300170765700236460ustar00rootroot00000000000000id(int,auto) selectiveRoutingID(string) routingESN(int) npa(int) esgwri(string) opensips-2.2.2/scripts/dbtext/opensips/emergency_service_provider000066400000000000000000000002051300170765700255340ustar00rootroot00000000000000id(int,auto) organizationName(string) hostId(string) nenaId(string) contact(string) certUri(string) nodeIP(string) attribution(int) opensips-2.2.2/scripts/dbtext/opensips/fraud_detection000066400000000000000000000005611300170765700232700ustar00rootroot00000000000000ruleid(int,auto) profileid(int) prefix(string) start_hour(string) end_hour(string) daysoftheweek(string) cpm_warning(int) cpm_critical(int) call_duration_warning(int) call_duration_critical(int) total_calls_warning(int) total_calls_critical(int) concurrent_calls_warning(int) concurrent_calls_critical(int) sequential_calls_warning(int) sequential_calls_critical(int) opensips-2.2.2/scripts/dbtext/opensips/globalblacklist000066400000000000000000000001051300170765700232540ustar00rootroot00000000000000id(int,auto) prefix(string) whitelist(int) description(string,null) opensips-2.2.2/scripts/dbtext/opensips/grp000066400000000000000000000001151300170765700207140ustar00rootroot00000000000000id(int,auto) username(string) domain(string) grp(string) last_modified(int) opensips-2.2.2/scripts/dbtext/opensips/imc_members000066400000000000000000000001051300170765700224050ustar00rootroot00000000000000id(int,auto) username(string) domain(string) room(string) flag(int) opensips-2.2.2/scripts/dbtext/opensips/imc_rooms000066400000000000000000000000641300170765700221160ustar00rootroot00000000000000id(int,auto) name(string) domain(string) flag(int) opensips-2.2.2/scripts/dbtext/opensips/load_balancer000066400000000000000000000001421300170765700226720ustar00rootroot00000000000000id(int,auto) group_id(int) dst_uri(string) resources(string) probe_mode(int) description(string) opensips-2.2.2/scripts/dbtext/opensips/location000066400000000000000000000004731300170765700217430ustar00rootroot00000000000000contact_id(long,auto) username(string) domain(string,null) contact(string) received(string,null) path(string,null) expires(int) q(double) callid(string) cseq(int) last_modified(int) flags(int) cflags(string,null) user_agent(string) socket(string,null) methods(int,null) sip_instance(string,null) attr(string,null) opensips-2.2.2/scripts/dbtext/opensips/missed_calls000066400000000000000000000002331300170765700225670ustar00rootroot00000000000000id(int,auto) method(string) from_tag(string) to_tag(string) callid(string) sip_code(string) sip_reason(string) time(int) setuptime(int) created(int,null) opensips-2.2.2/scripts/dbtext/opensips/presentity000066400000000000000000000002241300170765700223330ustar00rootroot00000000000000id(int,auto) username(string) domain(string) event(string) etag(string) expires(int) received_time(int) body(blob) extra_hdrs(blob) sender(string) opensips-2.2.2/scripts/dbtext/opensips/pua000066400000000000000000000006011300170765700207110ustar00rootroot00000000000000id(int,auto) pres_uri(string) pres_id(string) event(int) expires(int) desired_expires(int) flag(int) etag(string,null) tuple_id(string,null) watcher_uri(string,null) to_uri(string,null) call_id(string,null) to_tag(string,null) from_tag(string,null) cseq(int,null) record_route(string,null) contact(string,null) remote_contact(string,null) version(int,null) extra_headers(string,null) opensips-2.2.2/scripts/dbtext/opensips/re_grp000066400000000000000000000000541300170765700214040ustar00rootroot00000000000000id(int,auto) reg_exp(string) group_id(int) opensips-2.2.2/scripts/dbtext/opensips/registrant000066400000000000000000000003531300170765700223120ustar00rootroot00000000000000id(int,auto) registrar(string) proxy(string,null) aor(string) third_party_registrant(string,null) username(string,null) password(string,null) binding_URI(string) binding_params(string,null) expiry(int,null) forced_socket(string,null) opensips-2.2.2/scripts/dbtext/opensips/rls_presentity000066400000000000000000000002311300170765700232110ustar00rootroot00000000000000id(int,auto) rlsubs_did(string) resource_uri(string) content_type(string) presence_state(blob) expires(int) updated(int) auth_state(int) reason(string) opensips-2.2.2/scripts/dbtext/opensips/rls_watchers000066400000000000000000000005641300170765700226340ustar00rootroot00000000000000id(int,auto) presentity_uri(string) to_user(string) to_domain(string) watcher_username(string) watcher_domain(string) event(string) event_id(string,null) to_tag(string) from_tag(string) callid(string) local_cseq(int) remote_cseq(int) contact(string) record_route(string,null) expires(int) status(int) reason(string) version(int) socket_info(string) local_contact(string) opensips-2.2.2/scripts/dbtext/opensips/route_tree000066400000000000000000000000431300170765700223010ustar00rootroot00000000000000id(int,auto) carrier(string,null) opensips-2.2.2/scripts/dbtext/opensips/rtpproxy_sockets000066400000000000000000000000601300170765700235650ustar00rootroot00000000000000id(int,auto) rtpproxy_sock(string) set_id(int) opensips-2.2.2/scripts/dbtext/opensips/silo000066400000000000000000000002351300170765700210750ustar00rootroot00000000000000id(int,auto) src_addr(string) dst_addr(string) username(string) domain(string) inc_time(int) exp_time(int) snd_time(int) ctype(string,null) body(blob,null) opensips-2.2.2/scripts/dbtext/opensips/sip_trace000066400000000000000000000003651300170765700221040ustar00rootroot00000000000000id(int,auto) time_stamp(int) callid(string) trace_attrs(string,null) msg(string) method(string) status(string,null) from_proto(string) from_ip(string) from_port(int) to_proto(string) to_ip(string) to_port(int) fromtag(string) direction(string) opensips-2.2.2/scripts/dbtext/opensips/speed_dial000066400000000000000000000002241300170765700222160ustar00rootroot00000000000000id(int,auto) username(string) domain(string) sd_username(string) sd_domain(string) new_uri(string) fname(string) lname(string) description(string) opensips-2.2.2/scripts/dbtext/opensips/subscriber000066400000000000000000000002001300170765700222620ustar00rootroot00000000000000id(int,auto) username(string) domain(string) password(string) email_address(string) ha1(string) ha1b(string) rpid(string,null) opensips-2.2.2/scripts/dbtext/opensips/tls_mgm000066400000000000000000000004621300170765700215730ustar00rootroot00000000000000id(string) address(string) type(int) method(string,null) verify_cert(int,null) require_cert(int,null) certificate(string,null) private_key(int,null) crl_check_all(int,null) crl_dir(string,null) ca_list(string,null) ca_dir(string,null) cipher_list(string,null) dh_params(string,null) ec_curve(string,null) opensips-2.2.2/scripts/dbtext/opensips/uri000066400000000000000000000001221300170765700207210ustar00rootroot00000000000000id(int,auto) username(string) domain(string) uri_user(string) last_modified(int) opensips-2.2.2/scripts/dbtext/opensips/userblacklist000066400000000000000000000001141300170765700227720ustar00rootroot00000000000000id(int,auto) username(string) domain(string) prefix(string) whitelist(int) opensips-2.2.2/scripts/dbtext/opensips/usr_preferences000066400000000000000000000001701300170765700233170ustar00rootroot00000000000000id(int,auto) uuid(string) username(string) domain(string) attribute(string) type(int) value(string) last_modified(int) opensips-2.2.2/scripts/dbtext/opensips/version000066400000000000000000000014031300170765700216120ustar00rootroot00000000000000table_name(string) table_version(int) acc:7 active_watchers:11 address:5 aliases:1009 b2b_entities:1 b2b_logic:3 b2b_sca:1 cachedb:2 carrierfailureroute:2 carrierroute:3 cc_agents:1 cc_cdrs:1 cc_flows:1 closeddial:1 clusterer:1 cpl:2 dbaliases:2 dialog:10 dialplan:5 dispatcher:7 domain:3 domainpolicy:3 dr_carriers:2 dr_gateways:6 dr_groups:2 dr_partitions:1 dr_rules:3 emergency_report:1 emergency_routing:1 emergency_service_provider:1 fraud_detection:1 globalblacklist:2 grp:3 imc_members:2 imc_rooms:2 load_balancer:2 location:1011 missed_calls:5 presentity:5 pua:8 re_grp:2 registrant:1 rls_presentity:1 rls_watchers:2 route_tree:2 rtpproxy_sockets:0 silo:6 sip_trace:5 speed_dial:3 subscriber:7 tls_mgm:1 uri:2 userblacklist:2 usr_preferences:3 watchers:4 xcap:4 opensips-2.2.2/scripts/dbtext/opensips/watchers000066400000000000000000000002261300170765700217470ustar00rootroot00000000000000id(int,auto) presentity_uri(string) watcher_username(string) watcher_domain(string) event(string) status(int) reason(string,null) inserted_time(int) opensips-2.2.2/scripts/dbtext/opensips/xcap000066400000000000000000000001711300170765700210610ustar00rootroot00000000000000id(int,auto) username(string) domain(string) doc(blob) doc_type(int) etag(string) source(int) doc_uri(string) port(int) opensips-2.2.2/scripts/dbtextdb/000077500000000000000000000000001300170765700166525ustar00rootroot00000000000000opensips-2.2.2/scripts/dbtextdb/__init__.py000066400000000000000000000003521300170765700207630ustar00rootroot00000000000000#!/usr/bin/python # # Copyright 2008 Google Inc. All Rights Reserved. """Empty __init__ for dbtextdb module. This file is empty, it only exists to enable importing dbtextdb """ __author__ = 'herman@google.com (Herman Sheremetyev)' opensips-2.2.2/scripts/dbtextdb/dbtextdb.py000077500000000000000000001047031300170765700210340ustar00rootroot00000000000000#!/usr/bin/python # # Copyright 2008 Google Inc. All Rights Reserved. """SQL-like access layer for dbtext. This module provides the glue for opensipsctl to interact with dbtext files using basic SQL syntax thus avoiding special case handling of dbtext. """ __author__ = 'herman@google.com (Herman Sheremetyev)' import fcntl import os import shutil import sys import tempfile import time if 'DBTEXTDB_DEBUG' in os.environ: DEBUG = os.environ['DBTEXTDB_DEBUG'] else: DEBUG = 0 def Debug(msg): """Debug print method.""" if DEBUG: print msg class DBText(object): """Provides connection to a dbtext database.""" RESERVED_WORDS = ['SELECT', 'DELETE', 'UPDATE', 'INSERT', 'SET', 'VALUES', 'INTO', 'FROM', 'ORDER', 'BY', 'WHERE', 'COUNT', 'CONCAT', 'AND', 'AS'] ALL_COMMANDS = ['SELECT', 'DELETE', 'UPDATE', 'INSERT'] WHERE_COMMANDS = ['SELECT', 'DELETE', 'UPDATE'] def __init__(self, location): self.location = location # location of dbtext tables self.tokens = [] # query broken up into tokens self.conditions = {} # args to the WHERE clause self.columns = [] # columns requested by SELECT self.table = '' # name of the table being queried self.header = {} # table header self.orig_data = [] # original table data used to diff after updates self.data = [] # table data as a list of dicts self.count = False # where or not using COUNT() self.aliases = {} # column aliases (SELECT AS) self.targets = {} # target columns-value pairs for INSERT/UPDATE self.args = '' # query arguments preceeding the ; self.command = '' # which command are we executing self.strings = [] # list of string literals parsed from the query self.parens = [] # list of parentheses parsed from the query self._str_placeholder = '__DBTEXTDB_PARSED_OUT_STRING__' self._paren_placeholder = '__DBTEXTDB_PARSED_OUT_PARENS__' if not os.path.isdir(location): raise ParseError(location + ' is not a directory') def _ParseOrderBy(self): """Parse out the column name to be used for ordering the dataset. Raises: ParseError: Invalid ORDER BY clause """ self.order_by = '' if 'ORDER' in self.tokens: order_index = self.tokens.index('ORDER') if order_index != len(self.tokens) - 3: raise ParseError('ORDER must be followed with BY and column name') if self.tokens[order_index + 1] != 'BY': raise ParseError('ORDER must be followed with BY') self.order_by = self.tokens[order_index + 2] # strip off the order by stuff self.tokens.pop() # column name self.tokens.pop() # BY self.tokens.pop() # ORDER elif 'BY' in self.tokens: raise ParseError('BY must be preceeded by ORDER') Debug('Order by: ' + self.order_by) def _ParseConditions(self): """Parse out WHERE clause. Take everything after the WHERE keyword and convert it to a dict of name value pairs corresponding to the columns and their values that should be matched. Raises: ParseError: Invalid WHERE clause NotSupportedError: Unsupported syntax """ self.conditions = {} Debug('self.tokens = %s' % self.tokens) if 'WHERE' not in self.tokens: return if self.command not in self.WHERE_COMMANDS: raise ParseError(self.command + ' cannot have a WHERE clause') if 'OR' in self.tokens: raise NotSupportedError('WHERE clause does not support OR operator') where_clause = self.tokens[self.tokens.index('WHERE') + 1:] self.conditions = self._ParsePairs(' '.join(where_clause), 'AND') for cond in self.conditions: self.conditions[cond] = self._EscapeChars(self.conditions[cond]) Debug('Conditions are [%s]' % self.conditions) # pop off where clause a = self.tokens.pop() while a != 'WHERE': a = self.tokens.pop() Debug('self.tokens: %s' % self.tokens) def _ParseColumns(self): """Parse out the columns that need to be selected. Raises: ParseError: Invalid SELECT syntax """ self.columns = [] self.count = False self.aliases = {} col_end = 0 # this is only valid for SELECT if self.command != 'SELECT': return if 'FROM' not in self.tokens: raise ParseError('SELECT must be followed by FROM') col_end = self.tokens.index('FROM') if not col_end: # col_end == 0 raise ParseError('SELECT must be followed by column name[s]') cols_str = ' '.join(self.tokens[0:col_end]) # check if there is a function modifier on the columns if self.tokens[0] == 'COUNT': self.count = True if col_end == 1: raise ParseError('COUNT must be followed by column name[s]') if not self.tokens[1].startswith(self._paren_placeholder): raise ParseError('COUNT must be followed by ()') cols_str = self._ReplaceParens(self.tokens[1]) cols = cols_str.split(',') for col in cols: if not col.strip(): raise ParseError('Extra comma in columns') col_split = col.split() if col_split[0] == 'CONCAT': # found a concat statement, do the same overall steps for those cols self._ParseColumnsConcatHelper(col_split) else: col_split = col.split() if len(col_split) > 2 and col_split[1] != 'AS': raise ParseError('multiple columns must be separated by a comma') elif len(col_split) == 3: if col_split[1] != 'AS': raise ParseError('Invalid column alias, use AS') my_key = self._ReplaceStringLiterals(col_split[2], quotes=True) my_val = self._ReplaceStringLiterals(col_split[0], quotes=True) self.aliases[my_key] = [my_val] self.columns.append(my_key) elif len(col_split) > 3: raise ParseError('multiple columns must be separated by a comma') elif len(col_split) == 2: # alias my_key = self._ReplaceStringLiterals(col_split[1], quotes=True) my_val = self._ReplaceStringLiterals(col_split[0], quotes=True) self.aliases[my_key] = [my_val] self.columns.append(my_key) else: col = self._ReplaceStringLiterals(col, quotes=True).strip() if not col: # col == '' raise ParseError('empty column name not allowed') self.columns.append(col) # pop off all the columns related junk self.tokens = self.tokens[col_end + 1:] Debug('Columns: %s' % self.columns) Debug('Aliases: %s' % self.aliases) Debug('self.tokens: %s' % self.tokens) def _ParseColumnsConcatHelper(self, col_split): """Handles the columns being CONCAT'd together. Args: col_split: ['column', 'column'] Raises: ParseError: invalid CONCAT() """ concat_placeholder = '_' split_len = len(col_split) if split_len == 1: raise ParseError('CONCAT() must be followed by column name[s]') if not col_split[1].startswith(self._paren_placeholder): raise ParseError('CONCAT must be followed by ()') if split_len > 2: if split_len == 4 and col_split[2] != 'AS': raise ParseError('CONCAT() must be followed by an AS clause') if split_len > 5: raise ParseError('CONCAT() AS clause takes exactly 1 arg. ' 'Extra args: [%s]' % (col_split[4:])) else: concat_placeholder = self._ReplaceStringLiterals(col_split[-1], quotes=True) # make sure this place hodler is unique while concat_placeholder in self.aliases: concat_placeholder += '_' concat_cols_str = self._ReplaceParens(col_split[1]) concat_cols = concat_cols_str.split(',') concat_col_list = [] for concat_col in concat_cols: if ' ' in concat_col.strip(): raise ParseError('multiple columns must be separated by a' ' comma inside CONCAT()') concat_col = self._ReplaceStringLiterals(concat_col, quotes=True).strip() if not concat_col: raise ParseError('Attempting to CONCAT empty set') concat_col_list.append(concat_col) self.aliases[concat_placeholder] = concat_col_list self.columns.append(concat_placeholder) def _ParseTable(self): """Parse out the table name (multiple table names not supported). Raises: ParseError: Unable to parse table name """ table_name = '' if (not self.tokens or # len == 0 (self.tokens[0] in self.RESERVED_WORDS and self.tokens[0] not in ['FROM', 'INTO'])): raise ParseError('Missing table name') # SELECT if self.command == 'SELECT': table_name = self.tokens.pop(0) # INSERT elif self.command == 'INSERT': table_name = self.tokens.pop(0) if table_name == 'INTO': table_name = self.tokens.pop(0) # DELETE elif self.command == 'DELETE': if self.tokens[0] != 'FROM': raise ParseError('DELETE command must be followed by FROM') self.tokens.pop(0) # FROM table_name = self.tokens.pop(0) # UPDATE elif self.command == 'UPDATE': table_name = self.tokens.pop(0) if not self.table: self.table = table_name else: # multiple queries detected, make sure they're against same table if self.table != table_name: raise ParseError('Table changed between queries! %s -> %s' % (self.table, table_name)) Debug('Table is [%s]' % self.table) Debug('self.tokens is %s' % self.tokens) def _ParseTargets(self): """Parse out name value pairs of columns and their values. Raises: ParseError: Unable to parse targets """ self.targets = {} # UPDATE if self.command == 'UPDATE': if self.tokens.pop(0) != 'SET': raise ParseError('UPDATE command must be followed by SET') self.targets = self._ParsePairs(' '.join(self.tokens), ',') # INSERT if self.command == 'INSERT': if self.tokens[0] == 'SET': self.targets = self._ParsePairs(' '.join(self.tokens[1:]), ',') elif len(self.tokens) == 3 and self.tokens[1] == 'VALUES': if not self.tokens[0].startswith(self._paren_placeholder): raise ParseError('INSERT column names must be inside parens') if not self.tokens[2].startswith(self._paren_placeholder): raise ParseError('INSERT values must be inside parens') cols = self._ReplaceParens(self.tokens[0]).split(',') vals = self._ReplaceParens(self.tokens[2]).split(',') if len(cols) != len(vals): raise ParseError('INSERT column and value numbers must match') if not cols: # len == 0 raise ParseError('INSERT column number must be greater than 0') i = 0 while i < len(cols): val = vals[i].strip() if not val: # val == '' raise ParseError('INSERT values cannot be empty') if ' ' in val: raise ParseError('INSERT values must be comma separated') self.targets[cols[i].strip()] = self._ReplaceStringLiterals(val) i += 1 else: raise ParseError('Unable to parse INSERT targets') for target in self.targets: self.targets[target] = self._EscapeChars(self.targets[target]) Debug('Targets are [%s]' % self.targets) def _EscapeChars(self, value): """Escape necessary chars before inserting into dbtext. Args: value: 'string' Returns: escaped: 'string' with chars escaped appropriately """ # test that the value is string, if not return it as is try: value.find('a') except: return value escaped = value escaped = escaped.replace('\\', '\\\\').replace('\0', '\\0') escaped = escaped.replace(':', '\\:').replace('\n', '\\n') escaped = escaped.replace('\r', '\\r').replace('\t', '\\t') return escaped def _UnEscapeChars(self, value): """Un-escape necessary chars before returning to user. Args: value: 'string' Returns: escaped: 'string' with chars escaped appropriately """ # test that the value is string, if not return it as is try: value.find('a') except: return value escaped = value escaped = escaped.replace('\\:', ':').replace('\\n', '\n') escaped = escaped.replace('\\r', '\r').replace('\\t', '\t') escaped = escaped.replace('\\0', '\0').replace('\\\\', '\\') return escaped def Execute(self, query, writethru=True): """Parse and execute the query. Args: query: e.g. 'select * from table;' writethru: bool Returns: dataset: [{col: val, col: val}, {col: val}, {col: val}] Raises: ExecuteError: unable to execute query """ # parse the query self.ParseQuery(query) # get lock and execute the query self.OpenTable() Debug('Running ' + self.command) dataset = [] if self.command == 'SELECT': dataset = self._RunSelect() elif self.command == 'UPDATE': dataset = self._RunUpdate() elif self.command == 'INSERT': dataset = self._RunInsert() elif self.command == 'DELETE': dataset = self._RunDelete() if self.command != 'SELECT' and writethru: self.WriteTempTable() self.MoveTableIntoPlace() Debug(dataset) return dataset def CleanUp(self): """Reset the internal variables (for multiple queries).""" self.tokens = [] # query broken up into tokens self.conditions = {} # args to the WHERE clause self.columns = [] # columns requested by SELECT self.table = '' # name of the table being queried self.header = {} # table header self.orig_data = [] # original table data used to diff after updates self.data = [] # table data as a list of dicts self.count = False # where or not using COUNT() self.aliases = {} # column aliases (SELECT AS) self.targets = {} # target columns-value pairs for INSERT/UPDATE self.args = '' # query arguments preceeding the ; self.command = '' # which command are we executing self.strings = [] # list of string literals parsed from the query self.parens = [] # list of parentheses parsed from the query def ParseQuery(self, query): """External wrapper for the query parsing routines. Args: query: string Raises: ParseError: Unable to parse query """ self.args = query.split(';')[0] self._Tokenize() self._ParseCommand() self._ParseOrderBy() self._ParseConditions() self._ParseColumns() self._ParseTable() self._ParseTargets() def _ParseCommand(self): """Determine the command: SELECT, UPDATE, DELETE or INSERT. Raises: ParseError: unable to parse command """ self.command = self.tokens[0] # Check that command is valid if self.command not in self.ALL_COMMANDS: raise ParseError('Unsupported command: ' + self.command) self.tokens.pop(0) Debug('Command is: %s' % self.command) Debug('self.tokens: %s' % self.tokens) def _Tokenize(self): """Turn the string query into a list of tokens. Split on '(', ')', ' ', ';', '=' and ','. In addition capitalize any SQL keywords found. """ # horrible hack to handle now() time_now = '%s' % int(time.time()) time_now = time_now[0:-2] + '00' # round off the seconds for unittesting while 'now()' in self.args.lower(): start = self.args.lower().find('now()') self.args = ('%s%s%s' % (self.args[0:start], time_now, self.args[start + 5:])) # pad token separators with spaces pad = self.args.replace('(', ' ( ').replace(')', ' ) ') pad = pad.replace(',', ' , ').replace(';', ' ; ').replace('=', ' = ') self.args = pad # parse out all the blocks (string literals and parens) self._ParseOutBlocks() # split remaining into tokens self.tokens = self.args.split() # now capitalize i = 0 while i < len(self.tokens): if self.tokens[i].upper() in self.RESERVED_WORDS: self.tokens[i] = self.tokens[i].upper() i += 1 Debug('Tokens: %s' % self.tokens) def _ParseOutBlocks(self): """Parse out string literals and parenthesized values.""" self.strings = [] self.parens = [] # set str placeholder to a value that's not present in the string while self._str_placeholder in self.args: self._str_placeholder = '%s_' % self._str_placeholder # set paren placeholder to a value that's not present in the string while self._paren_placeholder in self.args: self._paren_placeholder = '%s_' % self._paren_placeholder self.strings = self._ParseOutHelper(self._str_placeholder, ["'", '"'], 'quotes') self.parens = self._ParseOutHelper(self._paren_placeholder, ['(', ')'], 'parens') Debug('Strings: %s' % self.strings) Debug('Parens: %s' % self.parens) def _ParseOutHelper(self, placeholder, delims, mode): """Replace all text within delims with placeholders. Args: placeholder: string delims: list of strings mode: string 'parens': if there are 2 delims treat the first as opening and second as closing, such as with ( and ) 'quotes': treat each delim as either opening or closing and require the same one to terminate the block, such as with ' and " Returns: list: [value1, value2, ...] Raises: ParseError: unable to parse out delims ExecuteError: Invalid usage """ if mode not in ['quotes', 'parens']: raise ExecuteError('_ParseOutHelper: invalid mode ' + mode) if mode == 'parens' and len(delims) != 2: raise ExecuteError('_ParseOutHelper: delims must have 2 values ' 'in "parens" mode') values = [] started = 0 new_args = '' string = '' my_id = 0 delim = '' for c in self.args: if c in delims: if not started: if mode == 'parens' and c != delims[0]: raise ParseError('Found closing delimeter %s before ' 'corresponding %s' % (c, delims[0])) started += 1 delim = c else: if ((mode == 'parens' and c == delim) or (mode == 'quotes' and c != delim)): string = '%s%s' % (string, c) continue # wait for matching delim started -= 1 if not started: values.append(string) new_args = '%s %s' % (new_args, '%s%d' % (placeholder, my_id)) my_id += 1 string = '' else: if not started: new_args = '%s%s' % (new_args, c) else: string = '%s%s' % (string, c) if started: if mode == 'parens': waiting_for = delims[1] else: waiting_for = delim raise ParseError('Unterminated block, waiting for ' + waiting_for) self.args = new_args Debug('Values: %s' % values) return values def _ReplaceStringLiterals(self, s, quotes=False): """Replaces string placeholders with real values. If quotes is set to True surround the returned value with single quotes Args: s: string quotes: bool Returns: s: string """ if s.strip().startswith(self._str_placeholder): str_index = int(s.split(self._str_placeholder)[1]) s = self.strings[str_index] if quotes: s = "'" + s + "'" return s def _ReplaceParens(self, s): """Replaces paren placeholders with real values. Args: s: string Returns: s: string """ if s.strip().startswith(self._paren_placeholder): str_index = int(s.split(self._paren_placeholder)[1]) s = self.parens[str_index].strip() return s def _RunDelete(self): """Run the DELETE command. Go through the rows in self.data matching them against the conditions, if they fit delete the row leaving a placeholder value (in order to keep the iteration process sane). Afterward clean up any empty values. Returns: dataset: [number of affected rows] """ i = 0 length = len(self.data) affected = 0 while i < length: if self._MatchRow(self.data[i]): self.data[i] = None affected += 1 i += 1 # clean out the placeholders while None in self.data: self.data.remove(None) return [affected] def _RunUpdate(self): """Run the UPDATE command. Find the matching rows and update based on self.targets Returns: affected: [int] Raises: ExecuteError: failed to run UPDATE """ i = 0 length = len(self.data) affected = 0 while i < length: if self._MatchRow(self.data[i]): for target in self.targets: if target not in self.header: raise ExecuteError(target + ' is an invalid column name') if self.header[target]['auto']: raise ExecuteError(target + ' is type auto and connot be updated') self.data[i][target] = self._TypeCheck(self.targets[target], target) affected += 1 i += 1 return [affected] def _RunInsert(self): """Run the INSERT command. Build up the row based on self.targets and table defaults, then append to self.data Returns: affected: [int] Raises: ExecuteError: failed to run INSERT """ new_row = {} cols = self._SortHeaderColumns() for col in cols: if col in self.targets: if self.header[col]['auto']: raise ExecuteError(col + ' is type auto: cannot be modified') new_row[col] = self.targets[col] elif self.header[col]['null']: new_row[col] = '' elif self.header[col]['auto']: new_row[col] = self._GetNextAuto(col) else: raise ExecuteError(col + ' cannot be empty or null') self.data.append(new_row) return [1] def _GetNextAuto(self, col): """Figure out the next value for col based on existing values. Scan all the current values and return the highest one + 1. Args: col: string Returns: next: int Raises: ExecuteError: Failed to get auto inc """ highest = 0 seen = [] for row in self.data: if row[col] > highest: highest = row[col] if row[col] not in seen: seen.append(row[col]) else: raise ExecuteError('duplicate value %s in %s' % (row[col], col)) return highest + 1 def _RunSelect(self): """Run the SELECT command. Returns: dataset: [] Raises: ExecuteError: failed to run SELECT """ dataset = [] if ['*'] == self.columns: self.columns = self._SortHeaderColumns() for row in self.data: if self._MatchRow(row): match = [] for col in self.columns: if col in self.aliases: concat = '' for concat_col in self.aliases[col]: if concat_col.startswith("'") and concat_col.endswith("'"): concat += concat_col.strip("'") elif concat_col not in self.header.keys(): raise ExecuteError('Table %s does not have a column %s' % (self.table, concat_col)) else: concat = '%s%s' % (concat, row[concat_col]) if not concat.strip(): raise ExecuteError('Empty CONCAT statement') my_match = concat elif col.startswith("'") and col.endswith("'"): my_match = col.strip("'") elif col not in self.header.keys(): raise ExecuteError('Table %s does not have a column %s' % (self.table, col)) else: my_match = row[col] match.append(self._UnEscapeChars(my_match)) dataset.append(match) if self.count: Debug('Dataset: %s' % dataset) dataset = [len(dataset)] if self.order_by: if self.order_by not in self.header.keys(): raise ExecuteError('Unknown column %s in ORDER BY clause' % self.order_by) pos = self._PositionByCol(self.order_by) dataset = self._SortMatrixByCol(dataset, pos) return dataset def _SortMatrixByCol(self, dataset, pos): """Sorts the matrix (array or arrays) based on a given column value. That is, if given matrix that looks like: [[1, 2, 3], [6, 5, 4], [3, 2, 1]] given pos = 0 produce: [[1, 2, 3], [3, 2, 1], [6, 5, 4]] given pos = 1 produce: [[1, 2, 3], [3, 2, 1], [6, 5, 4]] given pos = 2 produce: [[3, 2, 1], [1, 2, 3], [6, 5, 4]] Works for both integer and string values of column. Args: dataset: [[], [], ...] pos: int Returns: sorted: [[], [], ...] """ # prepend value in pos to the beginning of every row i = 0 while i < len(dataset): dataset[i].insert(0, dataset[i][pos]) i += 1 # sort the matrix, which is done on the row we just prepended dataset.sort() # strip away the first value i = 0 while i < len(dataset): dataset[i].pop(0) i += 1 return dataset def _MatchRow(self, row): """Matches the row against self.conditions. Args: row: ['val', 'val'] Returns: Bool """ match = True # when there are no conditions we match everything if not self.conditions: return match for condition in self.conditions: cond_val = self.conditions[condition] if condition not in self.header.keys(): match = False break else: if cond_val != row[condition]: match = False break return match def _ProcessHeader(self): """Parse out the header information. Returns: {col_name: {'type': string, 'null': string, 'auto': string, 'pos': int}} """ header = self.fd.readline().strip() cols = {} pos = 0 for col in header.split(): col_name = col.split('(')[0] col_type = col.split('(')[1].split(')')[0].split(',')[0] col_null = False col_auto = False if ',' in col.split('(')[1].split(')')[0]: if col.split('(')[1].split(')')[0].split(',')[1].lower() == 'null': col_null = True if col.split('(')[1].split(')')[0].split(',')[1].lower() == 'auto': col_auto = True cols[col_name] = {} cols[col_name]['type'] = col_type cols[col_name]['null'] = col_null cols[col_name]['auto'] = col_auto cols[col_name]['pos'] = pos pos += 1 return cols def _GetData(self): """Reads table data into memory as a list of dicts keyed on column names. Returns: data: [{row}, {row}, ...] Raises: ExecuteError: failed to get data """ data = [] row_num = 0 for row in self.fd: row = row.rstrip('\n') row_dict = {} i = 0 field_start = 0 field_num = 0 while i < len(row): if row[i] == ':': # the following block is executed again after the while is done val = row[field_start:i] col = self._ColByPosition(field_num) val = self._TypeCheck(val, col) row_dict[col] = val field_start = i + 1 # skip the colon itself field_num += 1 if row[i] == '\\': i += 2 # skip the next char since it's escaped else: i += 1 # handle the last field since we won't hit a : at the end # sucks to duplicate the code outside the loop but I can't think # of a better way :( val = row[field_start:i] col = self._ColByPosition(field_num) val = self._TypeCheck(val, col) row_dict[col] = val # verify that all columns were created for col in self.header: if col not in row_dict: raise ExecuteError('%s is missing from row %d in %s' % (col, row_num, self.table)) row_num += 1 data.append(row_dict) return data def _TypeCheck(self, val, col): """Verify type of val based on the header. Make sure the value is returned in quotes if it's a string and as '' when it's empty and Null Args: val: string col: string Returns: val: string Raises: ExecuteError: invalid value or column """ if not val and not self.header[col]['null']: raise ExecuteError(col + ' cannot be empty or null') if (self.header[col]['type'].lower() == 'int' or self.header[col]['type'].lower() == 'double'): try: if val: val = eval(val) except NameError, e: raise ExecuteError('Failed to parse %s in %s ' '(unable to convert to type %s): %s' % (col, self.table, self.header[col]['type'], e)) except SyntaxError, e: raise ExecuteError('Failed to parse %s in %s ' '(unable to convert to type %s): %s' % (col, self.table, self.header[col]['type'], e)) return val def _ColByPosition(self, pos): """Returns column name based on position. Args: pos: int Returns: column: string Raises: ExecuteError: invalid column """ for col in self.header: if self.header[col]['pos'] == pos: return col raise ExecuteError('Header does not contain column %d' % pos) def _PositionByCol(self, col): """Returns position of the column based on the name. Args: col: string Returns: pos: int Raises: ExecuteError: invalid column """ if col not in self.header.keys(): raise ExecuteError(col + ' is not a valid column name') return self.header[col]['pos'] def _SortHeaderColumns(self): """Sort column names by position. Returns: sorted: [col1, col2, ...] Raises: ExecuteError: unable to sort header """ cols = self.header.keys() sorted_cols = [''] * len(cols) for col in cols: pos = self.header[col]['pos'] sorted_cols[pos] = col if '' in sorted_cols: raise ExecuteError('Unable to sort header columns: %s' % cols) return sorted_cols def OpenTable(self): """Opens the table file and places its content into memory. Raises: ExecuteError: unable to open table """ # if we already have a header assume multiple queries on same table # (can't use self.data in case the table was empty to begin with) if self.header: return try: self.fd = open(os.path.join(self.location, self.table), 'r') self.header = self._ProcessHeader() if self.command in ['INSERT', 'DELETE', 'UPDATE']: fcntl.flock(self.fd, fcntl.LOCK_EX) self.data = self._GetData() self.orig_data = self.data[:] # save a copy of the data before modifying except IOError, e: raise ExecuteError('Unable to open table %s: %s' % (self.table, e)) Debug('Header is: %s' % self.header) # type check the conditions for cond in self.conditions: if cond not in self.header.keys(): raise ExecuteError('unknown column %s in WHERE clause' % cond) self.conditions[cond] = self._TypeCheck(self.conditions[cond], cond) # type check the targets for target in self.targets: if target not in self.header.keys(): raise ExecuteError('unknown column in targets: %s' % target) self.targets[target] = self._TypeCheck(self.targets[target], target) Debug('Type checked conditions: %s' % self.conditions) Debug('Data is:') for row in self.data: Debug('=======================') Debug(row) Debug('=======================') def WriteTempTable(self): """Write table header and data. First write header and data to a temp file, then move the tmp file to replace the original table file. """ self.temp_file = tempfile.NamedTemporaryFile() Debug('temp_file: ' + self.temp_file.name) # write header columns = self._SortHeaderColumns() header = '' for col in columns: header = '%s %s' % (header, col) header = '%s(%s' % (header, self.header[col]['type']) if self.header[col]['null']: header = '%s,null)' % header elif self.header[col]['auto']: header = '%s,auto)' % header else: header = '%s)' % header self.temp_file.write(header.strip() + '\n') # write data for row in self.data: row_str = '' for col in columns: row_str = '%s:%s' % (row_str, row[col]) self.temp_file.write(row_str[1:] + '\n') self.temp_file.flush() def MoveTableIntoPlace(self): """Replace the real table with the temp one. Diff the new data against the original and replace the table when they are different. """ if self.data != self.orig_data: temp_file = self.temp_file.name table_file = os.path.join(self.location, self.table) Debug('Copying %s to %s' % (temp_file, table_file)) shutil.copy(self.temp_file.name, self.location + '/' + self.table) def _ParsePairs(self, s, delimeter): """Parses out name value pairs from a string. String contains name=value pairs separated by a delimiter (such as "and" or ",") Args: s: string delimeter: string Returns: my_dict: dictionary Raises: ParseError: unable to parse pairs """ my_dict = {} Debug('parse pairs: [%s]' % s) pairs = s.split(delimeter) for pair in pairs: if '=' not in pair: raise ParseError('Invalid condition pair: ' + pair) split = pair.split('=') Debug('split: %s' % split) if len(split) != 2: raise ParseError('Invalid condition pair: ' + pair) col = split[0].strip() if not col or not split[1].strip() or ' ' in col: raise ParseError('Invalid condition pair: ' + pair) val = self._ReplaceStringLiterals(split[1].strip()) my_dict[col] = val return my_dict class Error(Exception): """DBText error.""" class ParseError(Error): """Parse error.""" class NotSupportedError(Error): """Not Supported error.""" class ExecuteError(Error): """Execute error.""" def main(argv): if len(argv) < 2: print 'Usage %s query' % argv[0] sys.exit(1) if 'DBTEXT_PATH' not in os.environ or not os.environ['DBTEXT_PATH']: print 'DBTEXT_PATH must be set' sys.exit(1) else: location = os.environ['DBTEXT_PATH'] try: conn = DBText(location) dataset = conn.Execute(' '.join(argv[1:])) if dataset: for row in dataset: if conn.command != 'SELECT': print 'Updated %s, rows affected: %d' % (conn.table, row) else: print row except Error, e: print e sys.exit(1) if __name__ == '__main__': main(sys.argv) opensips-2.2.2/scripts/dbtextdb/dbtextdb_test.py000066400000000000000000000664731300170765700221030ustar00rootroot00000000000000#!/usr/bin/python # # Copyright 2008 Google Inc. All Rights Reserved. """Test for dbtext_query.""" __author__ = 'herman@google.com (Herman Sheremetyev)' import time import unittest from dbtextdb import * class DBTextTest(unittest.TestCase): def setUp(self): self.time_now = '%s' % int(time.time()) self.time_now = self.time_now[0:-2] + '00' def testParseQuery(self): db_conn = DBText('./tests') # bad command query_bad_command = 'selecta * from table;' self.assertRaises(ParseError, db_conn.ParseQuery, query_bad_command) # normal query query_normal = 'select * from subscriber;' db_conn.ParseQuery(query_normal) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'subscriber') self.assert_(db_conn.columns == ['*']) db_conn.CleanUp() # normal query with condition query_normal_cond = 'select * from subscriber where column="value";' db_conn.ParseQuery(query_normal_cond) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'subscriber') self.assert_(db_conn.columns == ['*']) self.assert_(db_conn.strings == ['value']) self.assert_(not db_conn.count) self.assert_(db_conn.conditions == {'column': 'value'}) db_conn.CleanUp() # normal query with multiple conditions query_normal_cond = ('select * from subscriber where column="value1" and ' 'col2=" another value " and col3= foo and a="";') db_conn.ParseQuery(query_normal_cond) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'subscriber') self.assert_(db_conn.columns == ['*']) self.assert_(db_conn.strings == ['value1', ' another value ', '']) self.assertEqual(db_conn.conditions, {'column': 'value1', 'col2': ' another value ', 'col3': 'foo', 'a': ''}) db_conn.CleanUp() # normal query with count query_normal_count = 'select count(*) from subscriber;' db_conn.ParseQuery(query_normal_count) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'subscriber') self.assert_(db_conn.columns == ['*']) self.assert_(db_conn.count == True) db_conn.CleanUp() # normal query with now() query_normal_count = 'select count(*) from subscriber where time=now();' db_conn.ParseQuery(query_normal_count) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'subscriber') self.assert_(db_conn.columns == ['*']) self.assert_(db_conn.count == True) self.assertEqual(db_conn.conditions, {'time': self.time_now}) db_conn.CleanUp() # normal delete query query_normal_delete = 'delete from subscriber where foo = 2;' db_conn.ParseQuery(query_normal_delete) self.assert_(db_conn.command == 'DELETE') self.assert_(db_conn.table == 'subscriber') self.assertEqual(db_conn.conditions, {'foo': '2'}) db_conn.CleanUp() # normal insert values query with no into query_normal_insert_values = ('insert subscriber (col1, col2, col3) ' 'values (1, "foo", "");') db_conn.ParseQuery(query_normal_insert_values) self.assert_(db_conn.command == 'INSERT') self.assert_(db_conn.table == 'subscriber') self.assertEqual(db_conn.targets, {'col1': '1', 'col2': 'foo', 'col3': ''}) db_conn.CleanUp() # normal insert values query with into query_normal_insert_into_values = ('insert into subscriber (col1, col2) ' 'values (1, "foo");') db_conn.ParseQuery(query_normal_insert_into_values) self.assert_(db_conn.command == 'INSERT') self.assert_(db_conn.table == 'subscriber') self.assertEqual(db_conn.targets, {'col1': '1', 'col2': 'foo'}) db_conn.CleanUp() # normal insert values query with now() query_normal_insert_into_values = ('insert into subscriber (a, b, c) ' 'values (NOW(), "foo", now());') db_conn.ParseQuery(query_normal_insert_into_values) self.assert_(db_conn.command == 'INSERT') self.assert_(db_conn.table == 'subscriber') self.assertEqual(db_conn.targets, {'a': self.time_now, 'b': 'foo', 'c': self.time_now}) db_conn.CleanUp() # bad insert: missing table bad_insert_query_missing_table = ('insert into (col1, col2) ' 'values (1, "foo");') self.assertRaises(ParseError, db_conn.ParseQuery, bad_insert_query_missing_table) db_conn.CleanUp() # bad insert: missing parens bad_insert_query_missing_parens = ('insert into test col1, col2 ' 'values (1, "foo");') self.assertRaises(ParseError, db_conn.ParseQuery, bad_insert_query_missing_parens) db_conn.CleanUp() # bad insert: missing paren bad_insert_query_missing_paren = ('insert into test (col1, col2) ' 'values 1, "foo");') self.assertRaises(ParseError, db_conn.ParseQuery, bad_insert_query_missing_paren) db_conn.CleanUp() # bad insert: missing quote bad_insert_query_missing_quote = ('insert into test (col1, col2) ' '(values 1, "foo);') self.assertRaises(ParseError, db_conn.ParseQuery, bad_insert_query_missing_quote) db_conn.CleanUp() # bad insert: missing values bad_insert_query_missing_values = ('insert into test (col1, col2) ' '( 1, "foo");') self.assertRaises(ParseError, db_conn.ParseQuery, bad_insert_query_missing_values) db_conn.CleanUp() # bad insert: mislplaced values bad_insert_query_misplaced_values = ('insert into test values (col1, col2) ' '( 1, "foo");') self.assertRaises(ParseError, db_conn.ParseQuery, bad_insert_query_misplaced_values) db_conn.CleanUp() # bad insert: extra values bad_insert_query_extra_values = ('insert into test values (col1, col2) ' ' values values ( 1, "foo");') self.assertRaises(ParseError, db_conn.ParseQuery, bad_insert_query_extra_values) db_conn.CleanUp() # bad insert: extra paren set bad_insert_query_extra_paren_set = ('insert into test values (col1, col2) ' ' values ( 1, "foo")();') self.assertRaises(ParseError, db_conn.ParseQuery, bad_insert_query_extra_paren_set) db_conn.CleanUp() # bad insert: mismatched value pairs bad_insert_query_mismatched_vals = ('insert into test values (col1, col2) ' ' values ("foo");') self.assertRaises(ParseError, db_conn.ParseQuery, bad_insert_query_mismatched_vals) db_conn.CleanUp() # normal insert set query with no into query_normal_insert_set = ('insert subscriber set col= 1, col2 ="\'f\'b";') db_conn.ParseQuery(query_normal_insert_set) self.assert_(db_conn.command == 'INSERT') self.assert_(db_conn.table == 'subscriber') self.assertEqual(db_conn.targets, {'col': '1', 'col2': '\'f\'b'}) db_conn.CleanUp() # normal update query_normal_update = ('update subscriber set col1= 1, col2 ="foo";') db_conn.ParseQuery(query_normal_update) self.assert_(db_conn.command == 'UPDATE') self.assert_(db_conn.table == 'subscriber') self.assertEqual(db_conn.targets, {'col1': '1', 'col2': 'foo'}) db_conn.CleanUp() # normal update with condition query_normal_update_cond = ('update subscriber set col1= 1, col2 ="foo" ' 'where foo = "bar" and id=1 and a="";') db_conn.ParseQuery(query_normal_update_cond) self.assert_(db_conn.command == 'UPDATE') self.assert_(db_conn.table == 'subscriber') self.assertEqual(db_conn.targets, {'col1': '1', 'col2': 'foo'}) self.assertEqual(db_conn.conditions, {'foo': 'bar', 'id': '1', 'a': ''}) db_conn.CleanUp() # bad update: extra parens bad_update_query_extra_paren = ('update test set (col1 = "foo");') self.assertRaises(ParseError, db_conn.ParseQuery, bad_update_query_extra_paren) db_conn.CleanUp() # bad update: missing table bad_update_query_missing_table = ('update SET col1 = "foo";') self.assertRaises(ParseError, db_conn.ParseQuery, bad_update_query_missing_table) db_conn.CleanUp() # bad update: missing set bad_update_query_missing_set = ('update test sett col1 = "foo";') self.assertRaises(ParseError, db_conn.ParseQuery, bad_update_query_missing_set) db_conn.CleanUp() # bad update: missing val bad_update_query_missing_val = ('update test set col1 =;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_update_query_missing_val) db_conn.CleanUp() # bad update: missing comma bad_update_query_missing_comma = ('update test set col1 = "foo" crap =5;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_update_query_missing_comma) db_conn.CleanUp() # bad update: missing equal bad_update_query_missing_equal = ('update test set col1 = "foo", and 5;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_update_query_missing_equal) db_conn.CleanUp() # bad update: missing col bad_update_query_missing_col = ('update test set col1 = "foo", = 5;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_update_query_missing_col) db_conn.CleanUp() # bad update: double col bad_update_query_double_col = ('update test set col1 = "foo", and a = 5;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_update_query_double_col) db_conn.CleanUp() # normal query with multiple columns query_normal_count = 'select col1, "col 2",col3 , "col4" from subscriber;' db_conn.ParseQuery(query_normal_count) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'subscriber') self.assert_(db_conn.strings == ['col 2', 'col4']) self.assert_(db_conn.columns == ['col1', "'col 2'", 'col3', "'col4'"]) db_conn.CleanUp() # normal query with ORDER BY query_normal_order_by = ('select col1, col2 from test' ' ORDER by col1;') db_conn.ParseQuery(query_normal_order_by) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'test') self.assert_(db_conn.columns == ['col1', 'col2']) self.assert_(db_conn.order_by == 'col1') db_conn.CleanUp() # normal query with ORDER BY with conditions query_normal_order_by_cond = ('select col1, col2 from test where col="asdf"' ' and col2 = "foo" ORDER by col;') db_conn.ParseQuery(query_normal_order_by_cond) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'test') self.assert_(db_conn.columns == ['col1', 'col2']) self.assert_(db_conn.conditions == {'col': 'asdf', 'col2': 'foo'}) self.assert_(db_conn.order_by == 'col') db_conn.CleanUp() # normal query with CONCAT query_normal_concat = ('select concat(uname,"@", domain) as email_addr ' 'from subscriber where id=3;') db_conn.ParseQuery(query_normal_concat) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'subscriber') self.assert_(db_conn.columns == ['email_addr']) self.assert_(db_conn.conditions == {'id': '3'}) self.assert_(db_conn.aliases == {'email_addr': ['uname', "'@'", 'domain']}) db_conn.CleanUp() # normal query with multiple CONCAT query_normal_mult_concat = ('select concat(uname,"@", domain) as email,' ' foo as "bar" from table where id=3;') db_conn.ParseQuery(query_normal_mult_concat) self.assert_(db_conn.command == 'SELECT') self.assert_(db_conn.table == 'table') self.assert_(db_conn.columns == ['email', "'bar'"]) self.assert_(db_conn.conditions == {'id': '3'}) self.assert_(db_conn.aliases == {"'bar'": ['foo'], 'email': ['uname', "'@'", 'domain']}) db_conn.CleanUp() # bad query with CONCAT missing AS bad_query_concat_no_as = ('select concat(col1,col2) from test' ' ORDER by col1 col2;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_concat_no_as) db_conn.CleanUp() # bad query with CONCAT missing AS arg bad_query_concat_no_as_arg = ('select concat(col1,col2) as from test' ' ORDER by col1 col2;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_concat_no_as_arg) db_conn.CleanUp() # bad query with CONCAT missing paren bad_query_concat_no_paren = ('select concat(col1,col2 as foo from test' ' ORDER by col1 col2;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_concat_no_paren) db_conn.CleanUp() # bad query with ORDER BY multiple columns bad_query_mult_order_by = ('select col1, col2 from test' ' ORDER by col1 col2;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_mult_order_by) db_conn.CleanUp() # bad select query: missing FROM bad_query_missing_from = 'select * subscriber;' self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_missing_from) db_conn.CleanUp() # bad select query: missing comma in columns bad_query_missing_comma = 'select col1 col2 col3 from subscriber;' self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_missing_comma) db_conn.CleanUp() # bad select query: extra comma in columns bad_query_extra_comma = 'select col1,col2, from subscriber;' self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_extra_comma) db_conn.CleanUp() bad_query_extra_comma = 'select col1,,col2 from subscriber;' self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_extra_comma) db_conn.CleanUp() bad_query_extra_comma = 'select ,col1,col2 from subscriber;' self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_extra_comma) db_conn.CleanUp() # bad conditions: missing AND bad_query_missing_and = ('select * from subscriber where column = asdf ' ' something=missing_and;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_missing_and) db_conn.CleanUp() # bad conditions: missing value bad_query_missing_value = ('select * from subscriber where column = asdf' ' and something=;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_missing_value) db_conn.CleanUp() # bad query: unterminated string bad_query_unterm_str = ('select * from test where column ="asdf;') self.assertRaises(ParseError, db_conn.ParseQuery, bad_query_unterm_str) db_conn.CleanUp() # bad select query: missing table bad_select_query_missing_table = ('select * from where column ="asdf";') self.assertRaises(ParseError, db_conn.ParseQuery, bad_select_query_missing_table) db_conn.CleanUp() def testOpenTable(self): # check that header is retrieved and parsed correctly query = ('select * from test;') db_conn = DBText('./tests') db_conn.ParseQuery(query) db_conn.OpenTable() self.assertEqual(db_conn.header, {'col2': {'auto': False, 'null': True, 'type': 'string', 'pos': 2}, 'id': {'auto': True, 'null': False, 'type': 'int', 'pos': 1}, 'col1': {'auto': False, 'null': False, 'type': 'string', 'pos': 0}}) # check that data is retrieved and parsed correctly query = ('select * from test;') db_conn = DBText('./tests') db_conn.ParseQuery(query) db_conn.OpenTable() self.assertEqual(db_conn.data, [{'col1': 'item1\\:', 'id': 1, 'col2': 'item2'}, {'col1': 'it\\:em1\\\\', 'id': 2, 'col2': ''}, {'col1': '\\:item3', 'id': 3, 'col2': 'asdf\\:'}]) # missing table query = ('select * from non_existent_table;') db_conn = DBText('./tests') db_conn.ParseQuery(query) self.assertRaises(ExecuteError, db_conn.OpenTable) # type string value in type int column query = ('select * from bad_table_wrong_type;') db_conn = DBText('./tests') db_conn.ParseQuery(query) self.assertRaises(ExecuteError, db_conn.OpenTable) # row has fewer fields than header query = ('select * from bad_table_short_row;') db_conn = DBText('./tests') db_conn.ParseQuery(query) self.assertRaises(ExecuteError, db_conn.OpenTable) # row has more fields than header query = ('select * from bad_table_long_row;') db_conn = DBText('./tests') db_conn.ParseQuery(query) self.assertRaises(ExecuteError, db_conn.OpenTable) # value mismatch: non-null column is null query = ('select * from bad_table_null;') db_conn = DBText('./tests') db_conn.ParseQuery(query) self.assertRaises(ExecuteError, db_conn.OpenTable) # value mismatch: int column is string query = ('select * from bad_table_int;') db_conn = DBText('./tests') db_conn.ParseQuery(query) self.assertRaises(ExecuteError, db_conn.OpenTable) def testExecute(self): db_conn = DBText('./tests') writethru = False # test count query = ("select count(*) from subscriber where username='monitor' and" " domain='test.com';") result = db_conn.Execute(query, writethru) self.assertEqual(result, [2]) db_conn.CleanUp() query = ('select count(*) from subscriber where ' "username='test2';") result = db_conn.Execute(query, writethru) self.assertEqual(result, [1]) db_conn.CleanUp() query = ('select count(*) from subscriber where ' "username='test1';") result = db_conn.Execute(query, writethru) self.assertEqual(result, [3]) db_conn.CleanUp() # test concat query = ("select concat(username, '@', domain) as email_addr from " 'subscriber where id = 3;') result = db_conn.Execute(query, writethru) self.assertEqual(result, [['test2@test.com']]) db_conn.CleanUp() # test select query = ("select * from subscriber where username='test2' and" " domain='test.com';") expected_result = [[3, 'test2', 'test.com', 'password', '', '', 'test-team@test.com', 1202336327, '9fe9bfa1315b8202838838c3807a0a32', 'fac1f260ebda200719de4aa29880ee05', '', '']] result = db_conn.Execute(query, writethru) self.assertEqual(result, expected_result) db_conn.CleanUp() query = ('select * from subscriber where id = 3;') expected_result = [[3, 'test2', 'test.com', 'password', '', '', 'test-team@test.com', 1202336327, '9fe9bfa1315b8202838838c3807a0a32', 'fac1f260ebda200719de4aa29880ee05', '', '']] result = db_conn.Execute(query, writethru) self.assertEqual(result, expected_result) db_conn.CleanUp() # test order by query = ('select * from test order by non_existent_column;') self.assertRaises(ExecuteError, db_conn.Execute, query, writethru) db_conn.CleanUp() query = ('select * from unsorted_table order by id;') result = db_conn.Execute(query, writethru) self.assertEqual(result, [[1, 'fred', 'test.com', 2125551234], [2, 'james', 'test4.com', 2125551231], [3, 'mike', 'test2.com', 2125551239], [4, 'alex', 'test1.com', 2125551237], [5, 'john', 'test.com', 2125551240]]) db_conn.CleanUp() query = ('select * from unsorted_table order by user;') result = db_conn.Execute(query, writethru) self.assertEqual(result, [[4, 'alex', 'test1.com', 2125551237], [1, 'fred', 'test.com', 2125551234], [2, 'james', 'test4.com', 2125551231], [5, 'john', 'test.com', 2125551240], [3, 'mike', 'test2.com', 2125551239]]) db_conn.CleanUp() query = ('select * from unsorted_table order by domain;') result = db_conn.Execute(query, writethru) self.assertEqual(result, [[1, 'fred', 'test.com', 2125551234], [5, 'john', 'test.com', 2125551240], [4, 'alex', 'test1.com', 2125551237], [3, 'mike', 'test2.com', 2125551239], [2, 'james', 'test4.com', 2125551231]]) db_conn.CleanUp() query = ('select * from unsorted_table order by number;') result = db_conn.Execute(query, writethru) self.assertEqual(result, [[2, 'james', 'test4.com', 2125551231], [1, 'fred', 'test.com', 2125551234], [4, 'alex', 'test1.com', 2125551237], [3, 'mike', 'test2.com', 2125551239], [5, 'john', 'test.com', 2125551240]]) db_conn.CleanUp() # test delete query = ('delete from unsorted_table where id = 3;') result = db_conn.Execute(query, writethru) self.assertEqual(result, [1]) self.assertEqual(db_conn.data, [{'id': 1, 'user': 'fred', 'domain': 'test.com', 'number': 2125551234}, {'id': 4, 'user': 'alex', 'domain': 'test1.com', 'number': 2125551237}, {'id': 2, 'user': 'james', 'domain': 'test4.com', 'number': 2125551231}, {'id': 5, 'user': 'john', 'domain': 'test.com', 'number': 2125551240}]) db_conn.CleanUp() query = ('delete from unsorted_table where id = 5;') result = db_conn.Execute(query, writethru) self.assertEqual(db_conn.data, [{'id': 1, 'user': 'fred', 'domain': 'test.com', 'number': 2125551234}, {'id': 4, 'user': 'alex', 'domain': 'test1.com', 'number': 2125551237}, {'id': 2, 'user': 'james', 'domain': 'test4.com', 'number': 2125551231}, {'id': 3, 'user': 'mike', 'domain': 'test2.com', 'number': 2125551239}]) db_conn.CleanUp() # test insert with auto increment query = ("insert into unsorted_table set user='jake', domain='test.com'," 'number = 2125551456;') result = db_conn.Execute(query, writethru) self.assertEqual(db_conn.data, [{'id': 1, 'user': 'fred', 'domain': 'test.com', 'number': 2125551234}, {'id': 4, 'user': 'alex', 'domain': 'test1.com', 'number': 2125551237}, {'id': 2, 'user': 'james', 'domain': 'test4.com', 'number': 2125551231}, {'id': 3, 'user': 'mike', 'domain': 'test2.com', 'number': 2125551239}, {'id': 5, 'user': 'john', 'domain': 'test.com', 'number': 2125551240}, {'id': 6, 'user': 'jake', 'domain': 'test.com', 'number': 2125551456}]) db_conn.CleanUp() # test insert with null value query = ("insert into test set col1='asdf';") result = db_conn.Execute(query, writethru) self.assertEqual(db_conn.data, [{'col2': 'item2', 'id': 1, 'col1': 'item1\\:'}, {'col2': '', 'id': 2, 'col1': 'it\\:em1\\\\'}, {'col2': 'asdf\\:', 'id': 3, 'col1': '\\:item3'}, {'col2': '', 'id': 4, 'col1': 'asdf'}]) db_conn.CleanUp() # test insert with null value alternate syntax query = ("insert test ( col1) values ('asdf');") result = db_conn.Execute(query, writethru) self.assertEqual(db_conn.data, [{'col2': 'item2', 'id': 1, 'col1': 'item1\\:'}, {'col2': '', 'id': 2, 'col1': 'it\\:em1\\\\'}, {'col2': 'asdf\\:', 'id': 3, 'col1': '\\:item3'}, {'col2': '', 'id': 4, 'col1': 'asdf'}]) db_conn.CleanUp() # test insert with colon inside value query = ("insert into test set col1='as:df';") result = db_conn.Execute(query, writethru) self.assertEqual(db_conn.data, [{'col2': 'item2', 'id': 1, 'col1': 'item1\\:'}, {'col2': '', 'id': 2, 'col1': 'it\\:em1\\\\'}, {'col2': 'asdf\\:', 'id': 3, 'col1': '\\:item3'}, {'col2': '', 'id': 4, 'col1': 'as\:df'}]) db_conn.CleanUp() # test insert with escaped colon inside value query = ("insert into test set col1='as\:df';") result = db_conn.Execute(query, writethru) self.assertEqual(db_conn.data, [{'col2': 'item2', 'id': 1, 'col1': 'item1\\:'}, {'col2': '', 'id': 2, 'col1': 'it\\:em1\\\\'}, {'col2': 'asdf\\:', 'id': 3, 'col1': '\\:item3'}, {'col2': '', 'id': 4, 'col1': 'as\\\\\\:df'}]) db_conn.CleanUp() # bad insert with non-null column not provided query = ("insert test ( col2) values ('asdf');") self.assertRaises(ExecuteError, db_conn.Execute, query, writethru) db_conn.CleanUp() # bad insert with auto column forced query = ("insert test (col1, id) values ('asdf', 4);") self.assertRaises(ExecuteError, db_conn.Execute, query, writethru) db_conn.CleanUp() # test update with null value query = ("update test set col2='' where id = 3;") result = db_conn.Execute(query, writethru) self.assertEqual(db_conn.data, [{'col2': 'item2', 'id': 1, 'col1': 'item1\\:'}, {'col2': '', 'id': 2, 'col1': 'it\\:em1\\\\'}, {'col2': '', 'id': 3, 'col1': '\\:item3'}]) db_conn.CleanUp() if __name__ == '__main__': unittest.main() opensips-2.2.2/scripts/dbtextdb/tests/000077500000000000000000000000001300170765700200145ustar00rootroot00000000000000opensips-2.2.2/scripts/dbtextdb/tests/bad_table_auto_dupe000066400000000000000000000001571300170765700237040ustar00rootroot00000000000000col1(string) id(int) col2(string,null) col3(string) item1\::1:item2:asdf it\:em1:2:\::lk \:item3:2:asdf\::asdf opensips-2.2.2/scripts/dbtextdb/tests/bad_table_int000066400000000000000000000001621300170765700225050ustar00rootroot00000000000000col1(string) id(int) col2(string,null) col3(string) item1\::string:item2:asdf it\:em1:2:\:: \:item3:3:asdf\::asdf opensips-2.2.2/scripts/dbtextdb/tests/bad_table_long_row000066400000000000000000000001601300170765700235370ustar00rootroot00000000000000col1(string) id(int) col2(string,null) col3(string) item1\::1:item2:asdf it\:em1:2:\::lk: \:item3:3:asdf\::asdf opensips-2.2.2/scripts/dbtextdb/tests/bad_table_null000066400000000000000000000001551300170765700226670ustar00rootroot00000000000000col1(string) id(int) col2(string,null) col3(string) item1\::1:item2:asdf it\:em1:2:\:: \:item3:3:asdf\::asdf opensips-2.2.2/scripts/dbtextdb/tests/bad_table_short_row000066400000000000000000000001541300170765700237420ustar00rootroot00000000000000col1(string) id(int) col2(string,null) col3(string) item1\::1:item2:asdf it\:em1:2:\: \:item3:3:asdf\::asdf opensips-2.2.2/scripts/dbtextdb/tests/bad_table_wrong_type000066400000000000000000000001241300170765700241060ustar00rootroot00000000000000col1(string) id(int) col2(string,null) item1\::1:item2 it\:em1:2a: \:item3:3:asdf\: opensips-2.2.2/scripts/dbtextdb/tests/subscriber000066400000000000000000000017251300170765700221070ustar00rootroot00000000000000id(int,auto) username(string) domain(string) password(string) first_name(string,null) last_name(string,null) email_address(string) datetime_created(int,null) ha1(string) ha1b(string) timezone(string,null) rpid(string,null) 1:monitor:test.com:monitor:::test-team@test.com:1202336326:88bb9163d2b15a3c62def3975fdca72d:27b0e1a4da287171989894532c9f783e:: 2:test1:test.com:password:::test-team@test.com:1202336326:fb43223b02f6331f917cc2491235aad8:9b090c888afe748e3bf5b8681e71f29e:: 3:test2:test.com:password:::test-team@test.com:1202336327:9fe9bfa1315b8202838838c3807a0a32:fac1f260ebda200719de4aa29880ee05:: 4:monitor:test.com:monitor:::test-team@test.com:1202336326:88bb9163d2b15a3c62def3975fdca72d:27b0e1a4da287171989894532c9f783e:: 5:test1:test.com:password:::test-team@test.com:1202336375:fb43223b02f6331f917cc2491235aad8:9b090c888afe748e3bf5b8681e71f29e:: 6:test1:test.com:password:::test-team@test.com:1202336375:fb43223b02f6331f917cc2491235aad8:9b090c888afe748e3bf5b8681e71f29e:: opensips-2.2.2/scripts/dbtextdb/tests/test000066400000000000000000000001321300170765700207120ustar00rootroot00000000000000col1(string) id(int,auto) col2(string,null) item1\::1:item2 it\:em1\\:2: \:item3:3:asdf\: opensips-2.2.2/scripts/dbtextdb/tests/unsorted_table000066400000000000000000000003051300170765700227470ustar00rootroot00000000000000id(int,auto) user(string) domain(string,null) number(int) 1:fred:test.com:2125551234 4:alex:test1.com:2125551237 2:james:test4.com:2125551231 3:mike:test2.com:2125551239 5:john:test.com:2125551240 opensips-2.2.2/scripts/dbtextdb/tests/unsorted_table2000066400000000000000000000002511300170765700230310ustar00rootroot00000000000000id(int,auto) user(string) domain(string,null) number(int) 1:fred:test.com:2125551234 4:alex:test1.com:2125551237 2:james:test4.com:2125551231 5:john:test.com:2125551240 opensips-2.2.2/scripts/mysql/000077500000000000000000000000001300170765700162175ustar00rootroot00000000000000opensips-2.2.2/scripts/mysql/acc-create.sql000066400000000000000000000023761300170765700207370ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('acc','7'); CREATE TABLE acc ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, method CHAR(16) DEFAULT '' NOT NULL, from_tag CHAR(64) DEFAULT '' NOT NULL, to_tag CHAR(64) DEFAULT '' NOT NULL, callid CHAR(64) DEFAULT '' NOT NULL, sip_code CHAR(3) DEFAULT '' NOT NULL, sip_reason CHAR(32) DEFAULT '' NOT NULL, time DATETIME NOT NULL, duration INT(11) UNSIGNED DEFAULT 0 NOT NULL, ms_duration INT(11) UNSIGNED DEFAULT 0 NOT NULL, setuptime INT(11) UNSIGNED DEFAULT 0 NOT NULL, created DATETIME DEFAULT NULL ) ENGINE=InnoDB; CREATE INDEX callid_idx ON acc (callid); INSERT INTO version (table_name, table_version) values ('missed_calls','5'); CREATE TABLE missed_calls ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, method CHAR(16) DEFAULT '' NOT NULL, from_tag CHAR(64) DEFAULT '' NOT NULL, to_tag CHAR(64) DEFAULT '' NOT NULL, callid CHAR(64) DEFAULT '' NOT NULL, sip_code CHAR(3) DEFAULT '' NOT NULL, sip_reason CHAR(32) DEFAULT '' NOT NULL, time DATETIME NOT NULL, setuptime INT(11) UNSIGNED DEFAULT 0 NOT NULL, created DATETIME DEFAULT NULL ) ENGINE=InnoDB; CREATE INDEX callid_idx ON missed_calls (callid); opensips-2.2.2/scripts/mysql/alias_db-create.sql000066400000000000000000000007371300170765700217460ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dbaliases','2'); CREATE TABLE dbaliases ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, alias_username CHAR(64) DEFAULT '' NOT NULL, alias_domain CHAR(64) DEFAULT '' NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, CONSTRAINT alias_idx UNIQUE (alias_username, alias_domain) ) ENGINE=InnoDB; CREATE INDEX target_idx ON dbaliases (username, domain); opensips-2.2.2/scripts/mysql/auth_db-create.sql000066400000000000000000000010721300170765700216070ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('subscriber','7'); CREATE TABLE subscriber ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, password CHAR(25) DEFAULT '' NOT NULL, email_address CHAR(64) DEFAULT '' NOT NULL, ha1 CHAR(64) DEFAULT '' NOT NULL, ha1b CHAR(64) DEFAULT '' NOT NULL, rpid CHAR(64) DEFAULT NULL, CONSTRAINT account_idx UNIQUE (username, domain) ) ENGINE=InnoDB; CREATE INDEX username_idx ON subscriber (username); opensips-2.2.2/scripts/mysql/avpops-create.sql000066400000000000000000000012521300170765700215110ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('usr_preferences','3'); CREATE TABLE usr_preferences ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, uuid CHAR(64) DEFAULT '' NOT NULL, username CHAR(128) DEFAULT 0 NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, attribute CHAR(32) DEFAULT '' NOT NULL, type INT(11) DEFAULT 0 NOT NULL, value CHAR(128) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL ) ENGINE=InnoDB; CREATE INDEX ua_idx ON usr_preferences (uuid, attribute); CREATE INDEX uda_idx ON usr_preferences (username, domain, attribute); CREATE INDEX value_idx ON usr_preferences (value); opensips-2.2.2/scripts/mysql/b2b-create.sql000066400000000000000000000034571300170765700206570ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('b2b_entities','1'); CREATE TABLE b2b_entities ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, type INT(2) NOT NULL, state INT(2) NOT NULL, ruri CHAR(128), from_uri CHAR(128) NOT NULL, to_uri CHAR(128) NOT NULL, from_dname CHAR(64), to_dname CHAR(64), tag0 CHAR(64) NOT NULL, tag1 CHAR(64), callid CHAR(64) NOT NULL, cseq0 INT(11) NOT NULL, cseq1 INT(11), contact0 CHAR(128) NOT NULL, contact1 CHAR(128), route0 TEXT, route1 TEXT, sockinfo_srv CHAR(64), param CHAR(128) NOT NULL, lm INT(11) NOT NULL, lrc INT(11), lic INT(11), leg_cseq INT(11), leg_route TEXT, leg_tag CHAR(64), leg_contact CHAR(128), leg_sockinfo CHAR(128), CONSTRAINT b2b_entities_idx UNIQUE (type, tag0, tag1, callid) ) ENGINE=InnoDB; CREATE INDEX b2b_entities_param ON b2b_entities (param); INSERT INTO version (table_name, table_version) values ('b2b_logic','3'); CREATE TABLE b2b_logic ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, si_key CHAR(64) NOT NULL, scenario CHAR(64), sstate INT(2) NOT NULL, next_sstate INT(2) NOT NULL, sparam0 CHAR(64), sparam1 CHAR(64), sparam2 CHAR(64), sparam3 CHAR(64), sparam4 CHAR(64), sdp TEXT(64), lifetime INT(10) DEFAULT 0 NOT NULL, e1_type INT(2) NOT NULL, e1_sid CHAR(64), e1_from CHAR(128) NOT NULL, e1_to CHAR(128) NOT NULL, e1_key CHAR(64) NOT NULL, e2_type INT(2) NOT NULL, e2_sid CHAR(64), e2_from CHAR(128) NOT NULL, e2_to CHAR(128) NOT NULL, e2_key CHAR(64) NOT NULL, e3_type INT(2), e3_sid CHAR(64), e3_from CHAR(128), e3_to CHAR(128), e3_key CHAR(64), CONSTRAINT b2b_logic_idx UNIQUE (si_key) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/b2b_sca-create.sql000066400000000000000000000053351300170765700215020ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('b2b_sca','1'); CREATE TABLE b2b_sca ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, shared_line CHAR(64) NOT NULL, watchers CHAR(255) NOT NULL, app1_shared_entity INT(1) UNSIGNED DEFAULT NULL, app1_call_state INT(1) UNSIGNED DEFAULT NULL, app1_call_info_uri CHAR(128) DEFAULT NULL, app1_call_info_appearance_uri CHAR(128) DEFAULT NULL, app1_b2bl_key CHAR(64) DEFAULT NULL, app2_shared_entity INT(1) UNSIGNED DEFAULT NULL, app2_call_state INT(1) UNSIGNED DEFAULT NULL, app2_call_info_uri CHAR(128) DEFAULT NULL, app2_call_info_appearance_uri CHAR(128) DEFAULT NULL, app2_b2bl_key CHAR(64) DEFAULT NULL, app3_shared_entity INT(1) UNSIGNED DEFAULT NULL, app3_call_state INT(1) UNSIGNED DEFAULT NULL, app3_call_info_uri CHAR(128) DEFAULT NULL, app3_call_info_appearance_uri CHAR(128) DEFAULT NULL, app3_b2bl_key CHAR(64) DEFAULT NULL, app4_shared_entity INT(1) UNSIGNED DEFAULT NULL, app4_call_state INT(1) UNSIGNED DEFAULT NULL, app4_call_info_uri CHAR(128) DEFAULT NULL, app4_call_info_appearance_uri CHAR(128) DEFAULT NULL, app4_b2bl_key CHAR(64) DEFAULT NULL, app5_shared_entity INT(1) UNSIGNED DEFAULT NULL, app5_call_state INT(1) UNSIGNED DEFAULT NULL, app5_call_info_uri CHAR(128) DEFAULT NULL, app5_call_info_appearance_uri CHAR(128) DEFAULT NULL, app5_b2bl_key CHAR(64) DEFAULT NULL, app6_shared_entity INT(1) UNSIGNED DEFAULT NULL, app6_call_state INT(1) UNSIGNED DEFAULT NULL, app6_call_info_uri CHAR(128) DEFAULT NULL, app6_call_info_appearance_uri CHAR(128) DEFAULT NULL, app6_b2bl_key CHAR(64) DEFAULT NULL, app7_shared_entity INT(1) UNSIGNED DEFAULT NULL, app7_call_state INT(1) UNSIGNED DEFAULT NULL, app7_call_info_uri CHAR(128) DEFAULT NULL, app7_call_info_appearance_uri CHAR(128) DEFAULT NULL, app7_b2bl_key CHAR(64) DEFAULT NULL, app8_shared_entity INT(1) UNSIGNED DEFAULT NULL, app8_call_state INT(1) UNSIGNED DEFAULT NULL, app8_call_info_uri CHAR(128) DEFAULT NULL, app8_call_info_appearance_uri CHAR(128) DEFAULT NULL, app8_b2bl_key CHAR(64) DEFAULT NULL, app9_shared_entity INT(1) UNSIGNED DEFAULT NULL, app9_call_state INT(1) UNSIGNED DEFAULT NULL, app9_call_info_uri CHAR(128) DEFAULT NULL, app9_call_info_appearance_uri CHAR(128) DEFAULT NULL, app9_b2bl_key CHAR(64) DEFAULT NULL, app10_shared_entity INT(1) UNSIGNED DEFAULT NULL, app10_call_state INT(1) UNSIGNED DEFAULT NULL, app10_call_info_uri CHAR(128) DEFAULT NULL, app10_call_info_appearance_uri CHAR(128) DEFAULT NULL, app10_b2bl_key CHAR(64) DEFAULT NULL, CONSTRAINT sca_idx UNIQUE (shared_line) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/cachedb_sql-create.sql000066400000000000000000000004231300170765700224300ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cachedb','2'); CREATE TABLE cachedb ( keyname CHAR(255) PRIMARY KEY NOT NULL, value TEXT(512) NOT NULL, counter INT(10) DEFAULT 0 NOT NULL, expires INT(10) UNSIGNED DEFAULT 0 NOT NULL ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/call_center-create.sql000066400000000000000000000040471300170765700224610ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cc_flows','1'); CREATE TABLE cc_flows ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, flowid CHAR(64) NOT NULL, priority INT(11) UNSIGNED DEFAULT 256 NOT NULL, skill CHAR(64) NOT NULL, prependcid CHAR(32) NOT NULL, message_welcome CHAR(128) DEFAULT NULL, message_queue CHAR(128) NOT NULL, CONSTRAINT unique_flowid UNIQUE (flowid) ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('cc_agents','1'); CREATE TABLE cc_agents ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, agentid CHAR(128) NOT NULL, location CHAR(128) NOT NULL, logstate INT(10) UNSIGNED DEFAULT 0 NOT NULL, skills CHAR(255) NOT NULL, last_call_end INT(11) DEFAULT 0 NOT NULL, CONSTRAINT unique_agentid UNIQUE (agentid) ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('cc_cdrs','1'); CREATE TABLE cc_cdrs ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, caller CHAR(64) NOT NULL, received_timestamp DATETIME NOT NULL, wait_time INT(11) UNSIGNED DEFAULT 0 NOT NULL, pickup_time INT(11) UNSIGNED DEFAULT 0 NOT NULL, talk_time INT(11) UNSIGNED DEFAULT 0 NOT NULL, flow_id CHAR(128) NOT NULL, agent_id CHAR(128) DEFAULT NULL, call_type INT(11) DEFAULT -1 NOT NULL, rejected INT(11) UNSIGNED DEFAULT 0 NOT NULL, fstats INT(11) UNSIGNED DEFAULT 0 NOT NULL, cid INT(11) UNSIGNED DEFAULT 0 ) ENGINE=InnoDB; CREATE TABLE cc_calls ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, state INT(11) NOT NULL, ig_cback INT(11) NOT NULL, no_rej INT(11) NOT NULL, setup_time INT(11) NOT NULL, eta INT(11) NOT NULL, last_start INT(11) NOT NULL, recv_time INT(11) NOT NULL, caller_dn CHAR(128) NOT NULL, caller_un CHAR(128) NOT NULL, b2buaid CHAR(128) DEFAULT '' NOT NULL, flow CHAR(128) NOT NULL, agent CHAR(128) NOT NULL, CONSTRAINT unique_id UNIQUE (b2buaid) ) ENGINE=InnoDB; CREATE INDEX b2buaid_idx ON cc_calls (b2buaid); opensips-2.2.2/scripts/mysql/carrierroute-create.sql000066400000000000000000000027271300170765700227170ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('carrierroute','3'); CREATE TABLE carrierroute ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, carrier INT(10) UNSIGNED DEFAULT 0 NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, scan_prefix CHAR(64) DEFAULT '' NOT NULL, flags INT(11) UNSIGNED DEFAULT 0 NOT NULL, mask INT(11) UNSIGNED DEFAULT 0 NOT NULL, prob FLOAT DEFAULT 0 NOT NULL, strip INT(11) UNSIGNED DEFAULT 0 NOT NULL, rewrite_host CHAR(128) DEFAULT '' NOT NULL, rewrite_prefix CHAR(64) DEFAULT '' NOT NULL, rewrite_suffix CHAR(64) DEFAULT '' NOT NULL, description CHAR(255) DEFAULT NULL ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('carrierfailureroute','2'); CREATE TABLE carrierfailureroute ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, carrier INT(10) UNSIGNED DEFAULT 0 NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, scan_prefix CHAR(64) DEFAULT '' NOT NULL, host_name CHAR(128) DEFAULT '' NOT NULL, reply_code CHAR(3) DEFAULT '' NOT NULL, flags INT(11) UNSIGNED DEFAULT 0 NOT NULL, mask INT(11) UNSIGNED DEFAULT 0 NOT NULL, next_domain CHAR(64) DEFAULT '' NOT NULL, description CHAR(255) DEFAULT NULL ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('route_tree','2'); CREATE TABLE route_tree ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, carrier CHAR(64) DEFAULT NULL ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/closeddial-create.sql000066400000000000000000000012341300170765700223040ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('closeddial','1'); CREATE TABLE closeddial ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, cd_username CHAR(64) DEFAULT '' NOT NULL, cd_domain CHAR(64) DEFAULT '' NOT NULL, group_id CHAR(64) DEFAULT '' NOT NULL, new_uri CHAR(128) DEFAULT '' NOT NULL, CONSTRAINT cd_idx1 UNIQUE (username, domain, cd_domain, cd_username, group_id) ) ENGINE=InnoDB; CREATE INDEX cd_idx2 ON closeddial (group_id); CREATE INDEX cd_idx3 ON closeddial (cd_username); CREATE INDEX cd_idx4 ON closeddial (username); opensips-2.2.2/scripts/mysql/clusterer-create.sql000066400000000000000000000010761300170765700222150ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('clusterer','1'); CREATE TABLE clusterer ( id INT(10) AUTO_INCREMENT PRIMARY KEY NOT NULL, cluster_id INT(10) NOT NULL, machine_id INT(10) NOT NULL, url CHAR(64) NOT NULL, state INT(1) DEFAULT 1 NOT NULL, last_attempt BIGINT(64) UNSIGNED DEFAULT 0 NOT NULL, failed_attempts INT(10) DEFAULT 3 NOT NULL, no_tries INT(10) DEFAULT 0 NOT NULL, duration INT(10) DEFAULT 30 NOT NULL, description CHAR(64), CONSTRAINT clusterer_idx UNIQUE (cluster_id, machine_id) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/cpl-create.sql000066400000000000000000000005101300170765700207530ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cpl','2'); CREATE TABLE cpl ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, cpl_xml TEXT, cpl_bin TEXT, CONSTRAINT account_idx UNIQUE (username, domain) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/dialog-create.sql000066400000000000000000000021271300170765700214420ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dialog','10'); CREATE TABLE dialog ( dlg_id BIGINT(10) UNSIGNED PRIMARY KEY NOT NULL, callid CHAR(255) NOT NULL, from_uri CHAR(128) NOT NULL, from_tag CHAR(64) NOT NULL, to_uri CHAR(128) NOT NULL, to_tag CHAR(64) NOT NULL, mangled_from_uri CHAR(64) DEFAULT NULL, mangled_to_uri CHAR(64) DEFAULT NULL, caller_cseq CHAR(11) NOT NULL, callee_cseq CHAR(11) NOT NULL, caller_ping_cseq INT(11) UNSIGNED NOT NULL, callee_ping_cseq INT(11) UNSIGNED NOT NULL, caller_route_set TEXT(512), callee_route_set TEXT(512), caller_contact CHAR(128), callee_contact CHAR(128), caller_sock CHAR(64) NOT NULL, callee_sock CHAR(64) NOT NULL, state INT(10) UNSIGNED NOT NULL, start_time INT(10) UNSIGNED NOT NULL, timeout INT(10) UNSIGNED NOT NULL, vars BLOB(4096) DEFAULT NULL, profiles TEXT(512) DEFAULT NULL, script_flags INT(10) UNSIGNED DEFAULT 0 NOT NULL, module_flags INT(10) UNSIGNED DEFAULT 0 NOT NULL, flags INT(10) UNSIGNED DEFAULT 0 NOT NULL ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/dialplan-create.sql000066400000000000000000000007101300170765700217630ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dialplan','5'); CREATE TABLE dialplan ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, dpid INT(11) NOT NULL, pr INT(11) NOT NULL, match_op INT(11) NOT NULL, match_exp CHAR(64) NOT NULL, match_flags INT(11) NOT NULL, subst_exp CHAR(64), repl_exp CHAR(32), timerec CHAR(255), disabled INT(11) DEFAULT 0 NOT NULL, attrs CHAR(32) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/dispatcher-create.sql000066400000000000000000000007501300170765700223310ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dispatcher','7'); CREATE TABLE dispatcher ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, setid INT DEFAULT 0 NOT NULL, destination CHAR(192) DEFAULT '' NOT NULL, socket CHAR(128) DEFAULT NULL, state INT DEFAULT 0 NOT NULL, weight INT DEFAULT 1 NOT NULL, priority INT DEFAULT 0 NOT NULL, attrs CHAR(128) DEFAULT '' NOT NULL, description CHAR(64) DEFAULT '' NOT NULL ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/domain-create.sql000066400000000000000000000005441300170765700214530ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('domain','3'); CREATE TABLE domain ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, attrs CHAR(255) DEFAULT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT domain_idx UNIQUE (domain) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/domainpolicy-create.sql000066400000000000000000000006321300170765700226710ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('domainpolicy','3'); CREATE TABLE domainpolicy ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, rule CHAR(255) NOT NULL, type CHAR(255) NOT NULL, att CHAR(255), val CHAR(128), description CHAR(255) NOT NULL, CONSTRAINT rav_idx UNIQUE (rule, att, val) ) ENGINE=InnoDB; CREATE INDEX rule_idx ON domainpolicy (rule); opensips-2.2.2/scripts/mysql/drouting-create.sql000066400000000000000000000046271300170765700220450ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dr_gateways','6'); CREATE TABLE dr_gateways ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, gwid CHAR(64) NOT NULL, type INT(11) UNSIGNED DEFAULT 0 NOT NULL, address CHAR(128) NOT NULL, strip INT(11) UNSIGNED DEFAULT 0 NOT NULL, pri_prefix CHAR(16) DEFAULT NULL, attrs CHAR(255) DEFAULT NULL, probe_mode INT(11) UNSIGNED DEFAULT 0 NOT NULL, state INT(11) UNSIGNED DEFAULT 0 NOT NULL, socket CHAR(128) DEFAULT NULL, description CHAR(128) DEFAULT '' NOT NULL, CONSTRAINT dr_gw_idx UNIQUE (gwid) ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('dr_rules','3'); CREATE TABLE dr_rules ( ruleid INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, groupid CHAR(255) NOT NULL, prefix CHAR(64) NOT NULL, timerec CHAR(255) NOT NULL, priority INT(11) DEFAULT 0 NOT NULL, routeid CHAR(255) DEFAULT NULL, gwlist CHAR(255) NOT NULL, attrs CHAR(255) DEFAULT NULL, description CHAR(128) DEFAULT '' NOT NULL ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('dr_carriers','2'); CREATE TABLE dr_carriers ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, carrierid CHAR(64) NOT NULL, gwlist CHAR(255) NOT NULL, flags INT(11) UNSIGNED DEFAULT 0 NOT NULL, state INT(11) UNSIGNED DEFAULT 0 NOT NULL, attrs CHAR(255) DEFAULT '', description CHAR(128) DEFAULT '' NOT NULL, CONSTRAINT dr_carrier_idx UNIQUE (carrierid) ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('dr_groups','2'); CREATE TABLE dr_groups ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) NOT NULL, domain CHAR(128) DEFAULT '' NOT NULL, groupid INT(11) UNSIGNED DEFAULT 0 NOT NULL, description CHAR(128) DEFAULT '' NOT NULL ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('dr_partitions','1'); CREATE TABLE dr_partitions ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, partition_name CHAR(255) NOT NULL, db_url CHAR(255) NOT NULL, drd_table CHAR(255), drr_table CHAR(255), drg_table CHAR(255), drc_table CHAR(255), ruri_avp CHAR(255), gw_id_avp CHAR(255), gw_priprefix_avp CHAR(255), gw_sock_avp CHAR(255), rule_id_avp CHAR(255), rule_prefix_avp CHAR(255), carrier_id_avp CHAR(255) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/emergency-create.sql000066400000000000000000000025711300170765700221640ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('emergency_routing','1'); CREATE TABLE emergency_routing ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, selectiveRoutingID CHAR(11) NOT NULL, routingESN INT(5) UNSIGNED DEFAULT 0 NOT NULL, npa INT(3) UNSIGNED DEFAULT 0 NOT NULL, esgwri CHAR(50) NOT NULL ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('emergency_report','1'); CREATE TABLE emergency_report ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, callid CHAR(25) NOT NULL, selectiveRoutingID CHAR(11) NOT NULL, routingESN INT(5) UNSIGNED DEFAULT 0 NOT NULL, npa INT(3) UNSIGNED DEFAULT 0 NOT NULL, esgwri CHAR(50) NOT NULL, lro CHAR(20) NOT NULL, VPC_organizationName CHAR(50) NOT NULL, VPC_hostname CHAR(50) NOT NULL, VPC_timestamp CHAR(30) NOT NULL, result CHAR(4) NOT NULL, disposition CHAR(10) NOT NULL ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('emergency_service_provider','1'); CREATE TABLE emergency_service_provider ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, organizationName CHAR(50) NOT NULL, hostId CHAR(30) NOT NULL, nenaId CHAR(50) NOT NULL, contact CHAR(20) NOT NULL, certUri CHAR(50) NOT NULL, nodeIP CHAR(20) NOT NULL, attribution INT(2) UNSIGNED NOT NULL ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/fraud_detection-create.sql000066400000000000000000000015521300170765700233430ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('fraud_detection','1'); CREATE TABLE fraud_detection ( ruleid INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, profileid INT UNSIGNED NOT NULL, prefix CHAR(64) NOT NULL, start_hour CHAR(5) NOT NULL, end_hour CHAR(5) NOT NULL, daysoftheweek CHAR(64) NOT NULL, cpm_warning INT(5) UNSIGNED NOT NULL, cpm_critical INT(5) UNSIGNED NOT NULL, call_duration_warning INT(5) UNSIGNED NOT NULL, call_duration_critical INT(5) UNSIGNED NOT NULL, total_calls_warning INT(5) UNSIGNED NOT NULL, total_calls_critical INT(5) UNSIGNED NOT NULL, concurrent_calls_warning INT(5) UNSIGNED NOT NULL, concurrent_calls_critical INT(5) UNSIGNED NOT NULL, sequential_calls_warning INT(5) UNSIGNED NOT NULL, sequential_calls_critical INT(5) UNSIGNED NOT NULL ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/group-create.sql000066400000000000000000000013201300170765700213310ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('grp','3'); CREATE TABLE grp ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, grp CHAR(64) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT account_group_idx UNIQUE (username, domain, grp) ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('re_grp','2'); CREATE TABLE re_grp ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, reg_exp CHAR(128) DEFAULT '' NOT NULL, group_id INT(11) DEFAULT 0 NOT NULL ) ENGINE=InnoDB; CREATE INDEX group_idx ON re_grp (group_id); opensips-2.2.2/scripts/mysql/imc-create.sql000066400000000000000000000012471300170765700207550ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('imc_rooms','2'); CREATE TABLE imc_rooms ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, name CHAR(64) NOT NULL, domain CHAR(64) NOT NULL, flag INT(11) NOT NULL, CONSTRAINT name_domain_idx UNIQUE (name, domain) ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('imc_members','2'); CREATE TABLE imc_members ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) NOT NULL, domain CHAR(64) NOT NULL, room CHAR(64) NOT NULL, flag INT(11) NOT NULL, CONSTRAINT account_room_idx UNIQUE (username, domain, room) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/load_balancer-create.sql000066400000000000000000000007051300170765700227510ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('load_balancer','2'); CREATE TABLE load_balancer ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, group_id INT(11) UNSIGNED DEFAULT 0 NOT NULL, dst_uri CHAR(128) NOT NULL, resources CHAR(255) NOT NULL, probe_mode INT(11) UNSIGNED DEFAULT 0 NOT NULL, description CHAR(128) DEFAULT '' NOT NULL ) ENGINE=InnoDB; CREATE INDEX dsturi_idx ON load_balancer (dst_uri); opensips-2.2.2/scripts/mysql/msilo-create.sql000066400000000000000000000010661300170765700213270ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('silo','6'); CREATE TABLE silo ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, src_addr CHAR(128) DEFAULT '' NOT NULL, dst_addr CHAR(128) DEFAULT '' NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, inc_time INT DEFAULT 0 NOT NULL, exp_time INT DEFAULT 0 NOT NULL, snd_time INT DEFAULT 0 NOT NULL, ctype CHAR(255) DEFAULT NULL, body BLOB DEFAULT NULL ) ENGINE=InnoDB; CREATE INDEX account_idx ON silo (username, domain); opensips-2.2.2/scripts/mysql/permissions-create.sql000066400000000000000000000007051300170765700225560ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('address','5'); CREATE TABLE address ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, grp SMALLINT(5) UNSIGNED DEFAULT 0 NOT NULL, ip CHAR(50) NOT NULL, mask TINYINT DEFAULT 32 NOT NULL, port SMALLINT(5) UNSIGNED DEFAULT 0 NOT NULL, proto CHAR(4) DEFAULT 'any' NOT NULL, pattern CHAR(64) DEFAULT NULL, context_info CHAR(32) DEFAULT NULL ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/presence-create.sql000066400000000000000000000063751300170765700220200ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('presentity','5'); CREATE TABLE presentity ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) NOT NULL, domain CHAR(64) NOT NULL, event CHAR(64) NOT NULL, etag CHAR(64) NOT NULL, expires INT(11) NOT NULL, received_time INT(11) NOT NULL, body BLOB NOT NULL, extra_hdrs BLOB DEFAULT '' NOT NULL, sender CHAR(128) NOT NULL, CONSTRAINT presentity_idx UNIQUE (username, domain, event, etag) ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('active_watchers','11'); CREATE TABLE active_watchers ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, presentity_uri CHAR(128) NOT NULL, watcher_username CHAR(64) NOT NULL, watcher_domain CHAR(64) NOT NULL, to_user CHAR(64) NOT NULL, to_domain CHAR(64) NOT NULL, event CHAR(64) DEFAULT 'presence' NOT NULL, event_id CHAR(64), to_tag CHAR(64) NOT NULL, from_tag CHAR(64) NOT NULL, callid CHAR(64) NOT NULL, local_cseq INT(11) NOT NULL, remote_cseq INT(11) NOT NULL, contact CHAR(128) NOT NULL, record_route TEXT, expires INT(11) NOT NULL, status INT(11) DEFAULT 2 NOT NULL, reason CHAR(64), version INT(11) DEFAULT 0 NOT NULL, socket_info CHAR(64) NOT NULL, local_contact CHAR(128) NOT NULL, CONSTRAINT active_watchers_idx UNIQUE (presentity_uri, callid, to_tag, from_tag) ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('watchers','4'); CREATE TABLE watchers ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, presentity_uri CHAR(128) NOT NULL, watcher_username CHAR(64) NOT NULL, watcher_domain CHAR(64) NOT NULL, event CHAR(64) DEFAULT 'presence' NOT NULL, status INT(11) NOT NULL, reason CHAR(64), inserted_time INT(11) NOT NULL, CONSTRAINT watcher_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) ) ENGINE=InnoDB; INSERT INTO version (table_name, table_version) values ('xcap','4'); CREATE TABLE xcap ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) NOT NULL, domain CHAR(64) NOT NULL, doc LONGBLOB NOT NULL, doc_type INT(11) NOT NULL, etag CHAR(64) NOT NULL, source INT(11) NOT NULL, doc_uri CHAR(128) NOT NULL, port INT(11) NOT NULL, CONSTRAINT account_doc_type_idx UNIQUE (username, domain, doc_type, doc_uri) ) ENGINE=InnoDB; CREATE INDEX source_idx ON xcap (source); INSERT INTO version (table_name, table_version) values ('pua','8'); CREATE TABLE pua ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, pres_uri CHAR(128) NOT NULL, pres_id CHAR(255) NOT NULL, event INT(11) NOT NULL, expires INT(11) NOT NULL, desired_expires INT(11) NOT NULL, flag INT(11) NOT NULL, etag CHAR(64), tuple_id CHAR(64), watcher_uri CHAR(128), to_uri CHAR(128), call_id CHAR(64), to_tag CHAR(64), from_tag CHAR(64), cseq INT(11), record_route TEXT, contact CHAR(128), remote_contact CHAR(128), version INT(11), extra_headers TEXT ) ENGINE=InnoDB; CREATE INDEX del1_idx ON pua (pres_uri, event); CREATE INDEX del2_idx ON pua (expires); CREATE INDEX update_idx ON pua (pres_uri, pres_id, flag, event); opensips-2.2.2/scripts/mysql/registrant-create.sql000066400000000000000000000011641300170765700223650ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('registrant','1'); CREATE TABLE registrant ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, registrar CHAR(128) DEFAULT '' NOT NULL, proxy CHAR(128) DEFAULT NULL, aor CHAR(128) DEFAULT '' NOT NULL, third_party_registrant CHAR(128) DEFAULT NULL, username CHAR(64) DEFAULT NULL, password CHAR(64) DEFAULT NULL, binding_URI CHAR(128) DEFAULT '' NOT NULL, binding_params CHAR(64) DEFAULT NULL, expiry INT(1) UNSIGNED DEFAULT NULL, forced_socket CHAR(64) DEFAULT NULL, CONSTRAINT aor_idx UNIQUE (aor) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/registrar-create.sql000066400000000000000000000017061300170765700222070ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('aliases','1009'); CREATE TABLE aliases ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, contact CHAR(255) DEFAULT '' NOT NULL, received CHAR(128) DEFAULT NULL, path CHAR(255) DEFAULT NULL, expires DATETIME DEFAULT '2020-05-28 21:32:15' NOT NULL, q FLOAT(10,2) DEFAULT 1.0 NOT NULL, callid CHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INT(11) DEFAULT 13 NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INT(11) DEFAULT 0 NOT NULL, cflags CHAR(255) DEFAULT NULL, user_agent CHAR(255) DEFAULT '' NOT NULL, socket CHAR(64) DEFAULT NULL, methods INT(11) DEFAULT NULL, sip_instance CHAR(255) DEFAULT NULL, attr CHAR(255) DEFAULT NULL, CONSTRAINT alias_idx UNIQUE (username, domain, contact, callid) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/rls-create.sql000066400000000000000000000027571300170765700210140ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('rls_presentity','1'); CREATE TABLE rls_presentity ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, rlsubs_did CHAR(255) NOT NULL, resource_uri CHAR(128) NOT NULL, content_type CHAR(255) NOT NULL, presence_state BLOB NOT NULL, expires INT(11) NOT NULL, updated INT(11) NOT NULL, auth_state INT(11) NOT NULL, reason CHAR(64) NOT NULL, CONSTRAINT rls_presentity_idx UNIQUE (rlsubs_did, resource_uri) ) ENGINE=InnoDB; CREATE INDEX updated_idx ON rls_presentity (updated); INSERT INTO version (table_name, table_version) values ('rls_watchers','2'); CREATE TABLE rls_watchers ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, presentity_uri CHAR(128) NOT NULL, to_user CHAR(64) NOT NULL, to_domain CHAR(64) NOT NULL, watcher_username CHAR(64) NOT NULL, watcher_domain CHAR(64) NOT NULL, event CHAR(64) DEFAULT 'presence' NOT NULL, event_id CHAR(64), to_tag CHAR(64) NOT NULL, from_tag CHAR(64) NOT NULL, callid CHAR(64) NOT NULL, local_cseq INT(11) NOT NULL, remote_cseq INT(11) NOT NULL, contact CHAR(64) NOT NULL, record_route TEXT, expires INT(11) NOT NULL, status INT(11) DEFAULT 2 NOT NULL, reason CHAR(64) NOT NULL, version INT(11) DEFAULT 0 NOT NULL, socket_info CHAR(64) NOT NULL, local_contact CHAR(128) NOT NULL, CONSTRAINT rls_watcher_idx UNIQUE (presentity_uri, callid, to_tag, from_tag) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/rtpproxy-create.sql000066400000000000000000000004061300170765700221100ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('rtpproxy_sockets','0'); CREATE TABLE rtpproxy_sockets ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, rtpproxy_sock TEXT NOT NULL, set_id INT(10) UNSIGNED NOT NULL ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/siptrace-create.sql000066400000000000000000000016451300170765700220210ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('sip_trace','5'); CREATE TABLE sip_trace ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, time_stamp DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, callid CHAR(255) DEFAULT '' NOT NULL, trace_attrs CHAR(128) DEFAULT NULL, msg TEXT NOT NULL, method CHAR(32) DEFAULT '' NOT NULL, status CHAR(128) DEFAULT NULL, from_proto CHAR(5) NOT NULL, from_ip CHAR(50) DEFAULT '' NOT NULL, from_port INT(5) UNSIGNED NOT NULL, to_proto CHAR(5) NOT NULL, to_ip CHAR(50) DEFAULT '' NOT NULL, to_port INT(5) UNSIGNED NOT NULL, fromtag CHAR(64) DEFAULT '' NOT NULL, direction CHAR(4) DEFAULT '' NOT NULL ) ENGINE=InnoDB; CREATE INDEX trace_attrs_idx ON sip_trace (trace_attrs); CREATE INDEX date_idx ON sip_trace (time_stamp); CREATE INDEX fromip_idx ON sip_trace (from_ip); CREATE INDEX callid_idx ON sip_trace (callid); opensips-2.2.2/scripts/mysql/speeddial-create.sql000066400000000000000000000011331300170765700221310ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('speed_dial','3'); CREATE TABLE speed_dial ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, sd_username CHAR(64) DEFAULT '' NOT NULL, sd_domain CHAR(64) DEFAULT '' NOT NULL, new_uri CHAR(128) DEFAULT '' NOT NULL, fname CHAR(64) DEFAULT '' NOT NULL, lname CHAR(64) DEFAULT '' NOT NULL, description CHAR(64) DEFAULT '' NOT NULL, CONSTRAINT speed_dial_idx UNIQUE (username, domain, sd_domain, sd_username) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/standard-create.sql000066400000000000000000000002541300170765700220020ustar00rootroot00000000000000CREATE TABLE version ( table_name CHAR(32) NOT NULL, table_version INT UNSIGNED DEFAULT 0 NOT NULL, CONSTRAINT t_name_idx UNIQUE (table_name) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/tls_mgm-create.sql000066400000000000000000000007651300170765700216530ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('tls_mgm','1'); CREATE TABLE tls_mgm ( id CHAR(64) PRIMARY KEY NOT NULL, address CHAR(64) NOT NULL, type INT(1) NOT NULL, method CHAR(16), verify_cert INT(1), require_cert INT(1), certificate CHAR(255), private_key CHAR(255), crl_check_all INT(1), crl_dir CHAR(255), ca_list CHAR(255), ca_dir CHAR(255), cipher_list CHAR(255), dh_params CHAR(255), ec_curve CHAR(255) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/uri_db-create.sql000066400000000000000000000006471300170765700214540ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('uri','2'); CREATE TABLE uri ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, uri_user CHAR(64) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT account_idx UNIQUE (username, domain, uri_user) ) ENGINE=InnoDB; opensips-2.2.2/scripts/mysql/userblacklist-create.sql000066400000000000000000000014521300170765700230520ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('userblacklist','2'); CREATE TABLE userblacklist ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, prefix CHAR(64) DEFAULT '' NOT NULL, whitelist TINYINT(1) DEFAULT 0 NOT NULL ) ENGINE=InnoDB; CREATE INDEX userblacklist_idx ON userblacklist (username, domain, prefix); INSERT INTO version (table_name, table_version) values ('globalblacklist','2'); CREATE TABLE globalblacklist ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, prefix CHAR(64) DEFAULT '' NOT NULL, whitelist TINYINT(1) DEFAULT 0 NOT NULL, description CHAR(255) DEFAULT NULL ) ENGINE=InnoDB; CREATE INDEX globalblacklist_idx ON globalblacklist (prefix); opensips-2.2.2/scripts/mysql/usrloc-create.sql000066400000000000000000000017261300170765700215160ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('location','1011'); CREATE TABLE location ( contact_id BIGINT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT NULL, contact CHAR(255) DEFAULT '' NOT NULL, received CHAR(128) DEFAULT NULL, path CHAR(255) DEFAULT NULL, expires DATETIME DEFAULT '2020-05-28 21:32:15' NOT NULL, q FLOAT(10,2) DEFAULT 1.0 NOT NULL, callid CHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INT(11) DEFAULT 13 NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INT(11) DEFAULT 0 NOT NULL, cflags CHAR(255) DEFAULT NULL, user_agent CHAR(255) DEFAULT '' NOT NULL, socket CHAR(64) DEFAULT NULL, methods INT(11) DEFAULT NULL, sip_instance CHAR(255) DEFAULT NULL, attr CHAR(255) DEFAULT NULL, CONSTRAINT account_contact_idx UNIQUE (username, domain, contact, callid) ) ENGINE=InnoDB; opensips-2.2.2/scripts/opensipsctl000077500000000000000000001634001300170765700173470ustar00rootroot00000000000000#!/bin/sh # # $Id$ # # opensips control tool for maintaining opensips # #=================================================================== PATH=$PATH:/usr/local/sbin/ # for testing only, please don't enable this in production environments # as this introduce security risks TEST="false" ### include config files if [ -f /etc/opensips/opensipsctlrc ]; then . /etc/opensips/opensipsctlrc fi if [ -f /usr/local/etc/opensips/opensipsctlrc ]; then . /usr/local/etc/opensips/opensipsctlrc fi if [ -f ~/.opensipsctlrc ]; then . ~/.opensipsctlrc fi if [ $TEST = "true" ]; then ETCDIR=etc/ if [ -f ./opensipsctlrc ]; then . ./opensipsctlrc elif [ -f scripts/opensipsctlrc ]; then . scripts/opensipsctlrc fi fi if [ -z $SHELL_TESTED ] && [ -z $NOHLPRINT ] ; then if [ -x /bin/bash ]; then # bash is available export SHELL_TESTED=yes exec /bin/bash "$0" "$@" else NOHLPRINT=yes fi fi ### force values for variables in this section # you better set the variables in ~/.opensipsctlrc if [ -z $ETCDIR ] ; then ETCDIR="/usr/local/etc/opensips" else # Fix relative paths [ "${ETCDIR:0:1}" != "/" ] && ETCDIR=$PWD/$ETCDIR fi ### version for this script VERSION='$Revision: 4448 $' if [ -z $MYDIR ] ; then MYDIR=`dirname $0` fi if [ -z $MYLIBDIR ] ; then MYLIBDIR="/usr/local/lib/opensips/opensipsctl" if [ ! -d $MYLIBDIR ]; then MYLIBDIR=$MYDIR fi fi ##### ------------------------------------------------ ##### ### load base functions # if [ -f "$MYLIBDIR/opensipsctl.base" ]; then . "$MYLIBDIR/opensipsctl.base" else echo -e "Cannot load core functions '$MYLIBDIR/opensipsctl.base' - exiting ...\n" exit -1 fi ##### ------------------------------------------------ ##### ### define default parameters that may be overridden by ### different database engines # now="now()" # ##### ------------------------------------------------ ##### ### DBENGINE # DBENGINELOADED=0 case $DBENGINE in MYSQL|mysql|MySQL) if [ -f "$MYLIBDIR/opensipsctl.mysql" ]; then . "$MYLIBDIR/opensipsctl.mysql" DBENGINELOADED=1 fi ;; PGSQL|pgsql|postgres|postgresql|POSTGRESQL) if [ -f "$MYLIBDIR/opensipsctl.pgsql" ]; then . "$MYLIBDIR/opensipsctl.pgsql" DBENGINELOADED=1 fi ;; ORACLE|oracle|Oracle) if [ -f "$MYLIBDIR/opensipsctl.oracle" ]; then . "$MYLIBDIR/opensipsctl.oracle" DBENGINELOADED=1 fi ;; DBTEXT|dbtext|textdb) if [ -f "$MYLIBDIR/opensipsctl.dbtext" ]; then . "$MYLIBDIR/opensipsctl.dbtext" DBENGINELOADED=1 fi ;; DB_BERKELEY|db_berkeley|BERKELEY|berkeley) if [ -f "$MYLIBDIR/opensipsctl.db_berkeley" ]; then . "$MYLIBDIR/opensipsctl.db_berkeley" DBENGINELOADED=1 fi ;; SQLITE|db_sqlite|SQLITE|sqlite) if [ -f "$MYLIBDIR/opensipsctl.sqlite" ]; then . "$MYLIBDIR/opensipsctl.sqlite" DBENGINELOADED=1 now="datetime('now','localtime')" fi ;; esac if [ $DBENGINELOADED -eq 1 ] ; then mdbg "database engine '$DBENGINE' loaded" elif [ -n "$DBENGINE" ] ; then mwarn "database engine not found - tried '$DBENGINE'" fi # ##### ------------------------------------------------ ##### ### CTLENGINE # CTLENGINELOADED=0 if [ -z $CTLENGINE ] ; then CTLENGINE="FIFO" fi case $CTLENGINE in FIFO|fifo) if [ -f "$MYLIBDIR/opensipsctl.fifo" ]; then . "$MYLIBDIR/opensipsctl.fifo" CTLENGINELOADED=1 fi ;; UNIXSOCK|unixsock) if [ -f "$MYLIBDIR/opensipsctl.unixsock" ]; then . "$MYLIBDIR/opensipsctl.unixsock" CTLENGINELOADED=1 fi ;; esac if [ $CTLENGINELOADED -eq 1 ] ; then mdbg "Control engine '$CTLENGINE' loaded" else mwarn "no control engine found - tried '$CTLENGINE'" fi # ##### ------------------------------------------------ ##### ### common functions # usage() { CMD=`basename $0` if [ "0$VERIFY_ACL" -eq 1 ] ; then EXTRA_TEXT="ACL privileges are: $ACL_GROUPS" fi cat < /dev/null if [ $? -ne 0 ] ; then echo "$RES" | $EGREP "^400" > /dev/null if [ $? -eq 0 ] ; then merr "400; check if you use aliases in OpenSIPS" exit 1 fi echo "$RES" | $EGREP "^200" > /dev/null if [ $? -eq 0 ] ; then ALIAS_UL_EXISTS=1 fi # other errors merr "$RES" exit 1 fi } check_db_alias() { require_dbengine ALIAS_DB_EXISTS=0 QUERY="select count(*) from $DA_TABLE where $DA_ALIAS_USER_COLUMN='$1' \ and $DA_ALIAS_DOMAIN_COLUMN='$2';" CNT=`$DBCMD "$QUERY" | $EGREP -v ERROR | $LAST_LINE` mdbg "check_db_alias: alias counter=$CNT" if [ "$CNT" = "0" ] ; then ALIAS_DB_EXISTS=0 else ALIAS_DB_EXISTS=1 fi } # # check for alias duplicates # params: user domain # output: false if exists, true otherwise check_alias() { ALIAS_EXISTS=0 if [ $ENABLE_ALIASES -eq 1 ] ; then check_ul_alias "$1" "$2" if [ $ALIAS_UL_EXISTS -eq 0 ] ; then ALIAS_EXISTS=0 else ALIAS_EXISTS=1 fi elif [ $ENABLE_ALIASES -eq 2 ] ; then check_db_alias "$1" "$2" if [ $ALIAS_DB_EXISTS -eq 0 ] ; then ALIAS_EXISTS=0 else ALIAS_EXISTS=1 fi fi } # db-based aliases alias_db() { if [ $# -lt 2 ] ; then merr "alias_db - too few parameters" echo usage_alias_db exit 1 fi require_dbengine shift case $1 in list) if [ $# -eq 2 ] ; then # print aliases for user check_aor "$2" if [ $? -ne 0 ] ; then merr "alias_db - <$2> is not a valid AoR (user@domain)" exit 1 fi set_user $2 CLAUSE="WHERE $DA_USER_COLUMN='$OSIPSUSER' AND \ $DA_DOMAIN_COLUMN='$OSIPSDOMAIN'" mecho "Dumping aliases for user=<$2>" echo if [ $DBENGINE = "MYSQL" ] ; then QUERY="SELECT CONCAT($DA_ALIAS_USER_COLUMN,\ '@',$DA_ALIAS_DOMAIN_COLUMN) AS ALIAS FROM $DA_TABLE $CLAUSE;" elif [ $DBENGINE = "PGSQL" ] ; then QUERY="SELECT ($DA_ALIAS_USER_COLUMN || \ '@' || $DA_ALIAS_DOMAIN_COLUMN) AS ALIAS FROM $DA_TABLE $CLAUSE;" fi mecho "Dumping aliases for user=<$2>" echo $DBCMD "$QUERY" # | $AWK 'BEGIN {line=0;} # /^\+/ { next } # { if(line==0) print "ALIASES"; # else print line ")\t" $1 "@" $2; # line++; }' elif [ $# -eq 1 ] ; then mecho "Dumping all aliases may take long: do you want to proceed? [Y|N] " read answer if [ $answer = "y" -o $answer = "Y" ] ; then mecho "Dumping all aliases..." echo else exit 1 fi QUERY="SELECT $DA_ALIAS_USER_COLUMN, $DA_ALIAS_DOMAIN_COLUMN,\ $DA_USER_COLUMN, $DA_DOMAIN_COLUMN FROM $DA_TABLE;" $DBCMD "$QUERY" # | $AWK 'BEGIN {line=0;} # /^\+/ { next } # { line++; # if(line==1) print "SIP-ID \tALIAS\n"; # else print $3 "@" $4 "\t" $1 "@" $2 }' else merr "alias_db - wrong number of params for command [list]" echo usage_alias_db exit 1 fi exit $? ;; show) if [ $# -ne 2 ] ; then merr "alias_db - wrong number of params for command [show]" usage_alias_db exit 1 fi check_aor "$2" if [ $? -ne 0 ] ; then merr "alias_db - $2 is not a valid AoR (user@domain)" exit 1 fi set_user $2 CLAUSE="WHERE $DA_ALIAS_USER_COLUMN='$OSIPSUSER' AND \ $DA_ALIAS_DOMAIN_COLUMN='$OSIPSDOMAIN'" if [ $DBENGINE = "MYSQL" ] ; then QUERY="SELECT CONCAT($DA_USER_COLUMN,'@',$DA_DOMAIN_COLUMN) \ AS 'SIP-ID' FROM $DA_TABLE $CLAUSE ; " elif [ $DBENGINE = "PGSQL" ] ; then QUERY="SELECT ($DA_USER_COLUMN || '@' || $DA_DOMAIN_COLUMN) \ AS 'SIP-ID' FROM $DA_TABLE $CLAUSE ; " fi $DBCMD "$QUERY" #TMP_UUID=`sql_ro_query "$QUERY" | $AWK 'BEGIN {line=0;} # /^\+/ { next } # { line++; # if(line==2) print $1 "@" $2;}'` # #if [ "$TMP_UUID" = "" ] ; then # mecho "non-existent alias <$2>" # exit 1 #fi # #echo "Details for alias <$2>" #echo #echo "SIP-ID: $TMP_UUID" #echo #exit $? ;; add) if [ $# -ne 3 ] ; then usage_alias_db exit 1 fi shift check_aor "$1" if [ $? -ne 0 ] ; then err "alias_db - $1 is not a valid AoR (user@domain)" exit 1 fi check_aor "$2" if [ $? -ne 0 ] ; then err "alias_db - $2 is not a valid AoR (user@domain)" exit 1 fi set_user $1 TMP_OSIPSUSER=$OSIPSUSER TMP_OSIPSDOMAIN=$OSIPSDOMAIN set_user $2 if [ $DBENGINE = "MYSQL" ] ; then if is_value_in_db $DA_TABLE "concat($DA_ALIAS_USER_COLUMN,$DA_ALIAS_DOMAIN_COLUMN)" "$TMP_OSIPSUSER$TMP_OSIPSDOMAIN"; then minfo "$TMP_OSIPSUSER alias already in $DA_TABLE table" exit 0 fi elif [ $DBENGINE = "PGSQL" ] ; then if is_value_in_db $DA_TABLE "$DA_ALIAS_USER_COLUMN || $DA_ALIAS_DOMAIN_COLUMN" "$TMP_OSIPSUSER$TMP_OSIPSDOMAIN"; then minfo "$TMP_OSIPSUSER alias already in $DA_TABLE table" exit 0 fi fi QUERY="INSERT INTO $DA_TABLE ($DA_USER_COLUMN,$DA_DOMAIN_COLUMN,\ $DA_ALIAS_USER_COLUMN,$DA_ALIAS_DOMAIN_COLUMN) VALUES (\ '$OSIPSUSER','$OSIPSDOMAIN','$TMP_OSIPSUSER','$TMP_OSIPSDOMAIN' );" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "alias_db - SQL Error" exit 1 fi exit $? ;; rm) if [ $# -ne 2 ] ; then merr "alias_db - wrong numbers of parameters" usage_alias_db exit 1 fi shift check_aor "$1" if [ $? -ne 0 ] ; then merr "alias_db - $1 is not a valid URI" exit 1 fi set_user $1 CLAUSE="WHERE $DA_ALIAS_USER_COLUMN='$OSIPSUSER' AND \ $DA_ALIAS_DOMAIN_COLUMN='$OSIPSDOMAIN'" QUERY="DELETE FROM $DA_TABLE $CLAUSE;" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "alias_db - SQL Error" exit 1 fi exit $? ;; help) usage_alias_db "alone" ;; *) usage_alias_db exit 1 ;; esac } # end db-aliases # ##### ------------------------------------------------ ##### ### AVP management # # avp list [-T table] [-u ] # [-a attribute] [-v value] [-t type] ... list AVPs # avp add [-T table] # ............ add AVP (*) # avp rm [-T table] [-u ] # [-a attribute] [-v value] [-t type] ... remove AVP (*) avpops() { require_dbengine if [ $# -lt 2 ] ; then merr "avp - too few parameters" minfo "see '$0 avp help'" exit 1 fi if [ "$1" = "avp" ] ; then shift else merr "avp - unknown command $1" minfo "see '$0 avp help'" exit 1 fi case $1 in list) shift CLAUSE="" while [ $# -ne 0 ] do TMP_ARG=$1 shift case $TMP_ARG in -T) if [ -z "$1" ] ; then merr "avp list - table name parameter missing" exit 1 fi AVP_TABLE=$1 ;; -u) if [ -z "$1" ] ; then merr "avp list - user id or uuid parameter missing" exit 1 fi is_aor "$1" if [ $? -eq 0 ] ; then set_user $1 if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_USER_COLUMN='$OSIPSUSER' \ AND $AVP_DOMAIN_COLUMN='$OSIPSDOMAIN'" else CLAUSE="$CLAUSE AND \ $AVP_USER_COLUMN='$OSIPSUSER' AND $AVP_DOMAIN_COLUMN='$OSIPSDOMAIN'" fi else if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_UUID_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_UUID_COLUMN='$1'" fi fi ;; -a) if [ -z "$1" ] ; then merr "avp list - attribute name parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_ATTRIBUTE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_ATTRIBUTE_COLUMN='$1'" fi ;; -v) if [ -z "$1" ] ; then merr "avp list - value parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_VALUE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_VALUE_COLUMN='$1'" fi ;; -t) if [ -z "$1" ] ; then merr "avp list - type parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_TYPE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_TYPE_COLUMN='$1'" fi ;; *) merr "avp list - unknown parameter $1" exit 1 ;; esac shift done QUERY="SELECT $AVP_UUID_COLUMN,$AVP_USER_COLUMN,\ $AVP_DOMAIN_COLUMN,$AVP_ATTRIBUTE_COLUMN,$AVP_TYPE_COLUMN,$AVP_VALUE_COLUMN \ FROM $AVP_TABLE $CLAUSE;" mdbg "Query: $QUERY" mecho "Dumping AVPs" echo $DBCMD "$QUERY" # | $AWK 'BEGIN {line=0;} # /^\+/ { next } # { if(line==0) print "## UUID \tUserID \tAttribute \tType \tValue\n"; # else { # ORS_BAK=ORS; # ORS=""; # print line ") " $1 $2 "@" $3 "\t" $4 "\t\"" $5; # for (i=6;i<=NF;++i) print FS $i; # ORS=ORS_BAK; # print "\""; # } # line++; # }' exit $? ;; add) shift if [ $# -ne 4 ] ; then if [ $# -ne 6 ] ; then merr "avp add - bad number of parameters" exit 1 fi fi if [ $# -eq 6 ] ; then if [ "$1" = "-T" ] ; then AVP_TABLE=$2 shift shift else mecho "avp add - unknown parameter '$1'" exit 1 fi fi is_aor "$1" if [ $? -eq 0 ] ; then set_user $1 else AVP_UUID=$1 fi QUERY="INSERT INTO $AVP_TABLE \ ($AVP_UUID_COLUMN,$AVP_USER_COLUMN,$AVP_DOMAIN_COLUMN,$AVP_ATTRIBUTE_COLUMN,\ $AVP_TYPE_COLUMN,$AVP_VALUE_COLUMN,$AVP_MODIFIED_COLUMN) \ VALUES ('$AVP_UUID','$OSIPSUSER','$OSIPSDOMAIN','$2',$3,'$4',NOW());" # echo "Query: $QUERY" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "avp add - SQL Error" exit 1 else echo mecho "avp add - attribute added" fi exit $? ;; rm) shift CLAUSE="" while [ $# -ne 0 ] do TMP_ARG=$1 shift case $TMP_ARG in -T) if [ -z "$1" ] ; then merr "avp rm - table name parameter missing" exit 1 fi AVP_TABLE=$1 ;; -u) if [ -z "$1" ] ; then merr "avp rm - user id or uuid parameter missing" exit 1 fi is_aor "$1" if [ $? -eq 0 ] ; then set_user $1 if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_USER_COLUMN='$OSIPSUSER' \ AND $AVP_DOMAIN_COLUMN='$OSIPSDOMAIN'" else CLAUSE="$CLAUSE AND \ $AVP_USER_COLUMN='$OSIPSUSER' AND $AVP_DOMAIN_COLUMN='$OSIPSDOMAIN'" fi else if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_UUID_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_UUID_COLUMN='$1'" fi fi ;; -a) if [ -z "$1" ] ; then merr "avp rm - attribute name parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_ATTRIBUTE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_ATTRIBUTE_COLUMN='$1'" fi ;; -v) if [ -z "$1" ] ; then merr "avp rm - value parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_VALUE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_VALUE_COLUMN='$1'" fi ;; -t) if [ -z "$1" ] ; then merr "avp rm - type parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_TYPE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_TYPE_COLUMN='$1'" fi ;; *) merr "avp rm - unknown parameter $1" exit 1 ;; esac shift done QUERY="DELETE FROM $AVP_TABLE $CLAUSE;" mdbg "Query: $QUERY" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "avp rm - SQL Error" exit 1 else echo mecho "avp rm - AVP(s) deleted" fi exit $? ;; help) usage_avp ;; *) merr "avp - unknown command" usage exit 1 ;; esac } # end avpops() # ##### ------------------------------------------------ ##### ### cisco restart # cisco_restart() { require_ctlengine myhost=`get_my_host` RET=`$CTLCMD t_uac_dlg NOTIFY "$1" "." \ "From: sip:daemon@$myhost" \ "To: <$1>" "Event: check-sync" \ "Contact: " "." "." | head -1 ` print_status $RET } # ##### ------------------------------------------------ ##### ### DB operations # db_ops() { require_dbengine case $1 in exec|query) shift if [ $# -ne 1 ] ; then merr "missing query parameter" exit 1 fi $DBCMD "$1" ;; roexec|roquery) shift if [ $# -ne 1 ] ; then merr "missing query parameter" exit 1 fi $DBCMD "$1" ;; run) shift if [ $# -ne 1 ] ; then merr "missing query parameter" exit 1 fi eval QUERY=\$$1 if [ -z "$QUERY" ] ; then merr "missing query value" exit 1 fi $DBCMD "$QUERY" ;; rorun) shift if [ $# -ne 1 ] ; then merr "missing query parameter" exit 1 fi eval QUERY=\$$1 if [ -z "$QUERY" ] ; then merr "missing query value" exit 1 fi $DBCMD "$QUERY" ;; show) shift if [ $# -ne 1 ] ; then merr "missing table parameter" exit 1 fi QUERY="select * FROM $1;" $DBCMD "$QUERY" ;; *) usage_db_ops exit 1 esac } # ##### ------------------------------------------------ ##### ### domain management # domain() { case $1 in reload) require_ctlengine $CTLCMD domain_reload ;; show) require_ctlengine $CTLCMD domain_dump ;; showdb) require_dbengine QUERY="select * FROM $DOMAIN_TABLE ; " $DBCMD "$QUERY" ;; add) require_dbengine shift if [ $# -ne 1 ] ; then merr "missing domain parameter" exit 1 fi if is_value_in_db $DOMAIN_TABLE $DO_DOMAIN_COLUMN $1; then minfo "$1 already in $DOMAIN_TABLE table" exit 0 fi QUERY="insert into $DOMAIN_TABLE ($DO_DOMAIN_COLUMN, \ $DO_LAST_MODIFIED_COLUMN) VALUES ('$1',$now);" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "domain - SQL Error" exit 1 fi minfo "execute '$0 domain reload' to synchronize cache and database" ;; rm) require_dbengine shift if [ $# -ne 1 ] ; then merr "missing domain parameter" exit 1 fi QUERY="delete from $DOMAIN_TABLE where domain='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "domain - SQL Error" exit 1 fi minfo "execute '$0 domain reload' to synchronize cache and database" ;; *) usage_domain exit 1 esac } # ##### ------------------------------------------------ ##### ### address management # address() { case $1 in reload) require_ctlengine $CTLCMD address_reload ;; dump) require_ctlengine $CTLCMD address_dump ;; show) require_dbengine QUERY="select * FROM $ADDRESS_TABLE ; " $DBCMD "$QUERY" ;; add) require_dbengine shift if [ $# -lt 5 ] ; then usage_address exit 1 fi if is_value_in_db $ADDRESS_TABLE ip $2; then minfo "$2 already in $ADDRESS_TABLE table" exit 0 fi case $5 in any|udp|tcp|tls|sctp|none) ;; *) merr "unknown protocol" exit 1 esac QUERY="insert into $ADDRESS_TABLE \ ($ADDRESS_GRP_COLUMN, $ADDRESS_IP_COLUMN, $ADDRESS_MASK_COLUMN, \ $ADDRESS_PORT_COLUMN, $ADDRESS_PROTO_COLUMN) \ VALUES ('$1', '$2', '$3', '$4', '$5');" CONTEXT_INFO="" if [ ! -z "$6" ]; then CONTEXT_INFO="$6" QUERY="insert into $ADDRESS_TABLE \ ($ADDRESS_GRP_COLUMN, $ADDRESS_IP_COLUMN, $ADDRESS_MASK_COLUMN, \ $ADDRESS_PORT_COLUMN, $ADDRESS_PROTO_COLUMN, $ADDRESS_CONTEXT_INFO_COLUMN) \ VALUES ('$1', '$2', '$3', '$4', '$5', '$CONTEXT_INFO');" PATTERN="" if [ ! -z "$7" ]; then PATTERN="$7" QUERY="insert into $ADDRESS_TABLE \ ($ADDRESS_GRP_COLUMN, $ADDRESS_IP_COLUMN, $ADDRESS_MASK_COLUMN, \ $ADDRESS_PORT_COLUMN, $ADDRESS_PROTO_COLUMN, $ADDRESS_CONTEXT_INFO_COLUMN, $ADDRESS_PATTERN_COLUMN) \ VALUES ('$1', '$2', '$3', '$4', '$5', '$CONTEXT_INFO', '$PATTERN');" fi fi $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "address - SQL Error" exit 1 fi minfo "execute '$0 address reload' to synchronize cache and database" ;; rm) require_dbengine shift if [ $# -ne 4 ] ; then usage_address exit 1 fi QUERY="delete from $ADDRESS_TABLE where $ADDRESS_GRP_COLUMN='$1' \ AND $ADDRESS_IP_COLUMN='$2' AND $ADDRESS_MASK_COLUMN='$3'\ AND $ADDRESS_PORT_COLUMN='$4';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "address - SQL Error" exit 1 fi minfo "execute '$0 address reload' to synchronize cache and database" ;; *) usage_address exit 1 esac } # ##### ------------------------------------------------ ##### ### CARRIERROUTE management # cr() { require_dbengine require_ctlengine case $1 in show) mecho "cr routing tree" QUERY="select * FROM $ROUTE_TREE_TABLE ORDER BY $CARRIERROUTE_ROUTE_TREE_PREFIX_COLUMN; " $DBCMD "$QUERY" mecho "cr routes" QUERY="select * FROM $CARRIERROUTE_TABLE ORDER BY \ $CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN,\ $CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN,\ $CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN,\ $CARRIERROUTE_CARRIERROUTE_PROB_COLUMN;" $DBCMD "$QUERY" ;; reload) $CTLCMD cr_reload_routes ;; dump) $CTLCMD cr_dump ;; addrt) shift if [ $# -ne 2 ] ; then merr "cr - missing route_tree" exit 1 fi QUERY="insert into $ROUTE_TREE_TABLE ( $CARRIERROUTE_ROUTE_TREE_PREFIX_COLUMN, \ $CARRIERROUTE_ROUTE_TREE_CARRIER_COLUMN) \ VALUES ($1, '$2');" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; rmrt) shift if [ $# -ne 1 ] ; then merr "cr - missing route_tree to be removed" exit 1 fi QUERY="delete from $ROUTE_TREE_TABLE where $CARRIERROUTE_ROUTE_TREE_CARRIER_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; addcarrier) shift if [ $# -lt 4 ] ; then merr "cr - too few parameters" exit 1 fi PROB=1 STRIP=0 REWRITE_PREFIX= REWRITE_SUFFIX= COMMENT= FLAGS=0 MASK=0 if [ $# -gt 4 ] ; then PROB=$5 if [ $# -gt 5 ] ; then STRIP=$6 if [ $# -gt 6 ] ; then REWRITE_PREFIX=$7 if [ $# -gt 7 ] ; then REWRITE_SUFFIX=$8 if [ $# -gt 8 ] ; then COMMENT=$9 if [ $# -gt 9 ] ; then FLAGS=${10} if [ $# -gt 10 ] ; then MASK=${11} fi fi fi fi fi fi fi CARRIER=$1 SCAN_PREFIX=$2 DOMAIN=$3 REWRITE_HOST=$4 echo $FLAGS echo $MASK QUERY="insert into $CARRIERROUTE_TABLE \ ( $CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_PROB_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_STRIP_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_REWRITE_HOST_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_REWRITE_PREFIX_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_REWRITE_SUFFIX_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_COMMENT_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_FLAGS_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_MASK_COLUMN ) \ VALUES ($CARRIER, '$SCAN_PREFIX', '$DOMAIN', $PROB, $STRIP, \ '$REWRITE_HOST', '$REWRITE_PREFIX', '$REWRITE_SUFFIX', '$COMMENT', \ $FLAGS, $MASK);" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; rmcarrier) shift if [ $# -ne 3 ] ; then merr "cr - too few parameters" exit 1 fi CARRIER=$1 SCAN_PREFIX=$2 DOMAIN=$3 QUERY="delete from $CARRIERROUTE_TABLE where $CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN='$CARRIER' AND \ $CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN='$SCAN_PREFIX' AND \ $CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN=$DOMAIN ;" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; *) usage_cr exit 1 esac } # ##### ------------------------------------------------ ##### ### DISPATCHER management # dispatcher() { require_dbengine require_ctlengine case $1 in show) mecho "dispatcher gateways" QUERY="select * FROM $DISPATCHER_TABLE ORDER BY $DISPATCHER_SETID_COLUMN; " $DBCMD "$QUERY" ;; addgw) shift if [ $# -lt 6 ] ; then merr "too few parameters" usage_dispatcher exit 1 fi if [ $# -gt 6 ] ; then DISPATCHER_DESCRIPTION=$7 else DISPATCHER_DESCRIPTION="" fi DISPATCHER_SETID=$1 DISPATCHER_DESTINATION=$2 DISPATCHER_SOCKET=$3 DISPATCHER_STATE=$4 DISPATCHER_WEIGHT=$5 DISPATCHER_ATTRS=$6 QUERY="insert into $DISPATCHER_TABLE ($DISPATCHER_SETID_COLUMN, $DISPATCHER_DESTINATION_COLUMN, $DISPATCHER_SOCKET_COLUMN, $DISPATCHER_STATE_COLUMN, $DISPATCHER_WEIGHT_COLUMN, $DISPATCHER_ATTRS_COLUMN, $DISPATCHER_DESCRIPTION_COLUMN) VALUES ($DISPATCHER_SETID, '$DISPATCHER_DESTINATION', '$DISPATCHER_SOCKET', $DISPATCHER_STATE, $DISPATCHER_WEIGHT, '$DISPATCHER_ATTRS', '$DISPATCHER_DESCRIPTION');" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dispatcher - SQL Error" exit 1 fi $CTLCMD ds_reload ;; rmgw) shift if [ $# -ne 1 ] ; then merr "missing gateway id to be removed" exit 1 fi QUERY="delete from $DISPATCHER_TABLE where $DISPATCHER_ID_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dispatcher - SQL Error" exit 1 fi $CTLCMD ds_reload ;; reload) $CTLCMD ds_reload ;; dump) $CTLCMD ds_list ;; *) usage_dispatcher exit 1 esac } dr() { require_dbengine require_ctlengine case $1 in show) mecho "dr gateways" QUERY="select * FROM $DR_GATEWAYS_TABLE ORDER BY $DR_GATEWAYS_GWID_COLUMN; " $DBCMD "$QUERY" mecho "dr groups" QUERY="select * FROM $DR_GROUPS_TABLE ORDER BY $DR_GROUPS_GROUPID_COLUMN; " $DBCMD "$QUERY" mecho "dr carriers" QUERY="select * FROM $DR_CARRIERS_TABLE ORDER BY $DR_CARRIERS_CARRIERID_COLUMN; " $DBCMD "$QUERY" mecho "dr rules" QUERY="select * FROM $DR_RULES_TABLE ORDER BY $DR_RULES_GROUPID_COLUMN; " $DBCMD "$QUERY" ;; addgw) shift if [ $# -lt 7 ] ; then merr "too few parameters" usage_dr exit 1 fi if [ $# -gt 7 ] ; then DR_GATEWAYS_DESCRIPTION=$8 else DR_GATEWAYS_DESCRIPTION="" fi DR_GATEWAYS_GWID=$1 DR_GATEWAYS_TYPE=$2 DR_GATEWAYS_ADDRESS=$3 DR_GATEWAYS_STRIP=$4 DR_GATEWAYS_PRI_PREFIX=$5 DR_GATEWAYS_ATTRS=$6 DR_GATEWAYS_PROBE_MODE=$7 QUERY="insert into $DR_GATEWAYS_TABLE \ ( $DR_GATEWAYS_GWID_COLUMN, $DR_GATEWAYS_TYPE_COLUMN, $DR_GATEWAYS_ADDRESS_COLUMN, $DR_GATEWAYS_STRIP_COLUMN, $DR_GATEWAYS_PRI_PREFIX_COLUMN, $DR_GATEWAYS_ATTRS_COLUMN, $DR_GATEWAYS_PROBE_MODE_COLUMN, $DR_GATEWAYS_DESCRIPTION_COLUMN ) \ VALUES ('$DR_GATEWAYS_GWID', $DR_GATEWAYS_TYPE, '$DR_GATEWAYS_ADDRESS', $DR_GATEWAYS_STRIP, '$DR_GATEWAYS_PRI_PREFIX', '$DR_GATEWAYS_ATTRS', $DR_GATEWAYS_PROBE_MODE, '$DR_GATEWAYS_DESCRIPTION' );" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dr - SQL Error" exit 1 fi $CTLCMD dr_reload ;; rmgw) shift if [ $# -ne 1 ] ; then merr "missing gateway id to be removed" exit 1 fi QUERY="delete from $DR_GATEWAYS_TABLE where $DR_GATEWAYS_ID_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dr - SQL Error" exit 1 fi $CTLCMD dr_reload ;; addcr) shift if [ $# -lt 4 ] ; then merr "too few parameters" usage_dr exit 1 fi if [ $# -gt 4 ] ; then DR_CARRIERS_DESCRIPTION=$5 else DR_CARRIERS_DESCRIPTION="" fi DR_CARRIERS_CARRIERID=$1 DR_CARRIERS_GWLIST=$2 DR_CARRIERS_FLAGS=$3 DR_CARRIERS_ATTRS=$4 QUERY="insert into $DR_CARRIERS_TABLE \ ( $DR_CARRIERS_CARRIERID_COLUMN, $DR_CARRIERS_GWLIST_COLUMN, $DR_CARRIERS_FLAGS_COLUMN, $DR_CARRIERS_ATTRS_COLUMN, $DR_CARRIERS_DESCRIPTION_COLUMN ) \ VALUES ('$DR_CARRIERS_CARRIERID', '$DR_CARRIERS_GWLIST', $DR_CARRIERS_FLAGS, '$DR_CARRIERS_ATTRS', '$DR_CARRIERS_DESCRIPTION' );" mecho "$QUERY" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dr - SQL Error" exit 1 fi $CTLCMD dr_reload ;; rmcr) shift if [ $# -ne 1 ] ; then merr "missing gateway id to be removed" exit 1 fi QUERY="delete from $DR_CARRIERS_TABLE where $DR_CARRIERS_ID_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dr - SQL Error" exit 1 fi $CTLCMD dr_reload ;; addgrp) shift if [ $# -lt 3 ] ; then merr "too few parameters" usage_dr exit 1 fi if [ $# -gt 3 ] ; then DR_GROUPS_DESCRIPTION=$4 else DR_GROUPS_DESCRIPTION="" fi DR_GROUPS_USERNAME=$1 DR_GROUPS_DOMAIN=$2 DR_GROUPS_GROUPID=$3 QUERY="insert into $DR_GROUPS_TABLE \ ( $DR_GROUPS_USERNAME_COLUMN, $DR_GROUPS_DOMAIN_COLUMN, $DR_GROUPS_GROUPID_COLUMN, $DR_GROUPS_DESCRIPTION_COLUMN ) \ VALUES ('$DR_GROUPS_USERNAME', '$DR_GROUPS_DOMAIN', $DR_GROUPS_GROUPID, '$DR_GROUPS_DESCRIPTION' );" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dr - SQL Error" exit 1 fi $CTLCMD dr_reload ;; rmgrp) shift if [ $# -ne 1 ] ; then merr "missing gateway id to be removed" exit 1 fi QUERY="delete from $DR_GROUPS_TABLE where $DR_GROUPS_ID_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dr - SQL Error" exit 1 fi $CTLCMD dr_reload ;; addrule) shift if [ $# -lt 7 ] ; then merr "too few parameters" usage_dr exit 1 fi if [ $# -gt 7 ] ; then DR_RULES_DESCRIPTION=$8 else DR_RULES_DESCRIPTION="" fi DR_RULES_GROUPID=$1 DR_RULES_PREFIX=$2 DR_RULES_TIMEREC=$3 DR_RULES_PRIORITY=$4 DR_RULES_ROUTEID=$5 DR_RULES_GWLIST=$6 DR_RULES_ATTRS=$7 QUERY="insert into $DR_RULES_TABLE \ ( $DR_RULES_GROUPID_COLUMN, $DR_RULES_PREFIX_COLUMN, $DR_RULES_TIMEREC_COLUMN, $DR_RULES_PRIORITY_COLUMN, $DR_RULES_ROUTEID_COLUMN, $DR_RULES_GWLIST_COLUMN, $DR_RULES_ATTRS_COLUMN, $DR_RULES_DESCRIPTION_COLUMN ) \ VALUES ('$DR_RULES_GROUPID', '$DR_RULES_PREFIX', '$DR_RULES_TIMEREC', $DR_RULES_PRIORITY, '$DR_RULES_ROUTEID', '$DR_RULES_GWLIST', '$DR_RULES_ATTRS', '$DR_RULES_DESCRIPTION' );" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dr - SQL Error" exit 1 fi $CTLCMD dr_reload ;; rmgrule) shift if [ $# -ne 1 ] ; then merr "missing gateway id to be removed" exit 1 fi QUERY="delete from $DR_RULES_TABLE where $DR_RULES_RULEID_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dr - SQL Error" exit 1 fi $CTLCMD dr_reload ;; reload) $CTLCMD dr_reload ;; gw_status) $CTLCMD dr_gw_status ;; carrier_status) $CTLCMD dr_carrier_status ;; *) usage_dr exit 1 esac } # ##### ------------------------------------------------ ##### ### DIALPLAN management # dialplan() { require_dbengine require_ctlengine case $1 in show) shift if [ $# -gt 0 ] ; then mecho "dialplan $1 tables" QUERY="select * FROM $DIALPLAN_TABLE WHERE $DIALPLAN_DPID_COLUMN=$1 ORDER BY $DIALPLAN_PR_COLUMN ; " else mecho "dialplan tables" QUERY="select * FROM $DIALPLAN_TABLE ORDER BY $DIALPLAN_DPID_COLUMN, $DIALPLAN_PR_COLUMN; " fi $DBCMD "$QUERY" ;; addrule) shift if [ $# -lt 8 ] ; then merr "too few parameters" usage_dialplan exit 1 fi DIALPLAN_DPID=$1 DIALPLAN_PR=$2 DIALPLAN_MATCH_OP=$3 case $DIALPLAN_MATCH_OP in equal) DIALPLAN_MATCH_OP=0 ;; regexp) DIALPLAN_MATCH_OP=1 ;; *) merr "dialplan - unexpected $DIALPLAN_MATCH_OP for operating matching. Use 'equal' or 'regexp'!" exit 1 esac DIALPLAN_MATCH_EXP=$4 DIALPLAN_MATCH_FLAGS=$5 DIALPLAN_SUBST_EXP=$6 DIALPLAN_REPL_EXP=$7 DIALPLAN_ATTRS=$8 QUERY="insert into $DIALPLAN_TABLE \ ( $DIALPLAN_DPID_COLUMN, $DIALPLAN_PR_COLUMN, $DIALPLAN_MATCH_OP_COLUMN, \ $DIALPLAN_MATCH_EXP_COLUMN, $DIALPLAN_MATCH_FLAGS_COLUMN, \ $DIALPLAN_SUBST_EXP_COLUMN, $DIALPLAN_REPL_EXP_COLUMN, \ $DIALPLAN_ATTRS_COLUMN ) \ VALUES ( $DIALPLAN_DPID, $DIALPLAN_PR, $DIALPLAN_MATCH_OP, \ '$DIALPLAN_MATCH_EXP', $DIALPLAN_MATCH_FLAGS, '$DIALPLAN_SUBST_EXP', \ '$DIALPLAN_REPL_EXP', '$DIALPLAN_ATTRS')"; mecho "$QUERY" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dialplan - SQL Error" exit 1 fi $CTLCMD dp_reload ;; rm) QUERY="delete from $DIALPLAN_TABLE; " $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dialplan - SQL Error" exit 1 fi $CTLCMD dp_reload ;; rmdpid) shift if [ $# -lt 1 ] ; then merr "too few parameters" usage_dialplan exit 1 fi DIALPLAN_DPID=$1 QUERY="delete from $DIALPLAN_TABLE where $DIALPLAN_DPID_COLUMN=$DIALPLAN_DPID; " $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dialplan - SQL Error" exit 1 fi $CTLCMD dp_reload ;; rmrule) shift if [ $# -lt 2 ] ; then merr "too few parameters" usage_dialplan exit 1 fi DIALPLAN_DPID=$1 DIALPLAN_PR=$2 QUERY="delete from $DIALPLAN_TABLE where $DIALPLAN_DPID_COLUMN=$DIALPLAN_DPID AND $DIALPLAN_PR_COLUMN=$DIALPLAN_PR; " $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dialplan - SQL Error" exit 1 fi $CTLCMD dp_reload ;; reload) $CTLCMD dp_reload ;; *) usage_dialplan exit 1 esac } # ##### ------------------------------------------------ ##### ### REGISTRANT management # registrant() { require_dbengine require_ctlengine case $1 in show) mecho "registrant table" QUERY="select * FROM $REGISTRANT_TABLE ORDER BY $REGISTRANT_AOR_COLUMN; " $DBCMD "$QUERY" ;; dump) $CTLCMD reg_list ;; add) shift if [ $# -lt 9 ] ; then merr "too few parameters" usage_dialplan exit 1 fi REGISTRANT_REGISTRAR=$1 REGISTRANT_PROXY=$2 REGISTRANT_AOR=$3 REGISTRANT_THIRD_PARTY_REGISTRANT=$4 REGISTRANT_USERNAME=$5 REGISTRANT_PASSWORD=$6 REGISTRANT_BINDING_URI=$7 REGISTRANT_BINDING_PARAMS=$8 REGISTRANT_EXPIRY=$9 REGISTRANT_FORCED_SOCKET=${10} QUERY="insert into $REGISTRANT_TABLE\ ( $REGISTRANT_REGISTRAR_COLUMN,\ $REGISTRANT_PROXY_COLUMN, $REGISTRANT_AOR_COLUMN,\ $REGISTRANT_THIRD_PARTY_REGISTRANT_COLUMN,\ $REGISTRANT_USERNAME_COLUMN,\ $REGISTRANT_PASSWORD_COLUMN,\ $REGISTRANT_BINDING_URI_COLUMN,\ $REGISTRANT_BINDING_PARAMS_COLUMN,\ $REGISTRANT_EXPIRY_COLUMN,\ $REGISTRANT_FORCED_SOCKET_COLUMN)\ VALUES ( '$REGISTRANT_REGISTRAR', '$REGISTRANT_PROXY',\ '$REGISTRANT_AOR',\ '$REGISTRANT_THIRD_PARTY_REGISTRANT',\ '$REGISTRANT_USERNAME', '$REGISTRANT_PASSWORD',\ '$REGISTRANT_BINDING_URI',\ '$REGISTRANT_BINDING_PARAMS',\ $REGISTRANT_EXPIRY, '$REGISTRANT_FORCED_SOCKET')"; #mecho "$QUERY" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "registrant - SQL Error" exit 1 fi #$CTLCMD reg_reload ;; rm) QUERY="delete from $REGISTRANT_TABLE; " $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "registrant - SQL Error" exit 1 fi #$CTLCMD reg_reload ;; rmaor) shift if [ $# -lt 1 ] ; then merr "too few parameters" usage_registrant exit 1 fi REGISTRANT_ID=$1 QUERY="delete from $REGISTRANT_TABLE where $REGISTRANT_ID_COLUMN=$REGISTRANT_ID; " $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "registrant - SQL Error" exit 1 fi #$CTLCMD reg_reload ;; *) usage_registrant exit 1 esac } # ##### ------------------------------------------------ ##### ### opensips_start # opensips_start() { echo minfo "Starting OpenSIPS : " if [ -r $PID_FILE ] ; then if [ -d /proc/$(cat $PID_FILE) ] ; then ps -ef | $EGREP opensips ls -l $PID_FILE minfo "PID file exists ($PID_FILE)! OpenSIPS already running?" exit 1 else minfo "Removing stale PID file $PID_FILE."; rm -f $PID_FILE; fi fi if [ ! -x "$OSIPSBIN" ] ; then echo merr "OpenSIPS binaries not found at $OSIPSBIN" merr "set OSIPSBIN to the path of opensips in $0 or ~/.opensipsctlrc" exit 1 fi if [ $SYSLOG = 1 ] ; then $OSIPSBIN -P $PID_FILE $STARTOPTIONS 1>/dev/null 2>/dev/null else $OSIPSBIN -P $PID_FILE -E $STARTOPTIONS fi sleep 3 if [ ! -s $PID_FILE ] ; then echo merr "PID file $PID_FILE does not exist -- OpenSIPS start failed" exit 1 fi minfo "started (pid: `cat $PID_FILE`)" } # ##### ------------------------------------------------ ##### ### opensips_stop # opensips_stop() { # In case stop was called in restart, it should not exit. echo if [ "$1" = "restart" ] then minfo "Restarting OpenSIPS : " else minfo "Stopping OpenSIPS : " fi if [ -r $PID_FILE ] ; then kill `cat $PID_FILE` minfo "stopped" else echo merr "No PID file found ($PID_FILE)! OpenSIPS probably not running" if [ "$1" != "restart" ] ; then minfo "check with 'ps -ef | $EGREP opensips'" exit 1 fi fi } # ##### ------------------------------------------------ ##### ### options_ping # options_ping() { myhost=`get_my_host` require_ctlengine CMD="t_uac_dlg OPTIONS \"$1\" \".\" \".\" \"From:sip:daemon@$myhost"$'\r\n'"To:<$1>"$'\r\n'"Contact:sip:daemon@$myhost"$'\r\n'"\"" RET=`$CTLCMD $CMD | head -1` print_status $RET } # ##### ------------------------------------------------ ##### ### rpid management # rpid() { if [ "$#" -lt 2 ] ; then merr "rpid - too few parameters" exit 1 fi shift; require_dbengine case $1 in show) if [ $# -eq 2 ] ; then set_user $2 is_user $2 if [ $? -ne 0 ] ; then merr "rpid - invalid user '$2'" exit 1; fi CLAUSE=" WHERE $SUBSCRIBER_COLUMN='$OSIPSUSER' AND \ $REALM_COLUMN='$OSIPSDOMAIN' " elif [ $# -ne 1 ] ; then usage_rpid exit 1 fi QUERY="select $SUBSCRIBER_COLUMN, $RPID_COLUMN FROM $SUB_TABLE \ $CLAUSE ; " $DBCMD "$QUERY" ;; add|rm) MODE=$1; if [ "$MODE" = "add" ] ; then ARG_NUM=3; else ARG_NUM=2; fi if [ $# -lt $ARG_NUM ] ; then usage_rpid exit 1 fi set_user $2 is_user $2 if [ $? -ne 0 ] ; then merr "rpid - invalid user '$2'" exit 1 fi shift 2 if [ "$MODE" = "add" ] ; then RPID_VAL="'$1'"; else RPID_VAL=NULL; fi QUERY="UPDATE $SUB_TABLE SET $RPID_COLUMN=$RPID_VAL \ WHERE $SUBSCRIBER_COLUMN='$OSIPSUSER' AND $REALM_COLUMN='$OSIPSDOMAIN';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "rpid - SQL Error" exit 1 fi $0 rpid show "$OSIPSUSER@$OSIPSDOMAIN" ;; *) usage_rpid exit 1 ;; esac } # ##### ------------------------------------------------ ##### ### SPEEDDIAL management # speeddial() { if [ "$#" -lt 2 ] ; then merr "speeddial - too few parameters" echo usage_speeddial exit 1 fi require_dbengine shift case $1 in list) if [ $# -eq 2 ] ; then # print speed-dials for user check_aor "$2" if [ $? -ne 0 ] ; then merr "speeddial - <$2> is not a valid AoR (user@domain)" exit 1 fi set_user $2 CLAUSE="WHERE $SD_USER_COLUMN='$OSIPSUSER' AND \ $SD_DOMAIN_COLUMN='$OSIPSDOMAIN'" mecho "Dumping speed-dials for user=<$2>" echo if [ $DBENGINE = "MYSQL" ] ; then QUERY="SELECT CONCAT($SD_SD_USER_COLUMN,'@',\ $SD_SD_DOMAIN_COLUMN) AS 'Short number', $SD_NEW_URI_COLUMN AS 'New URI',\ $SD_DESC_COLUMN FROM $SD_TABLE $CLAUSE;" elif [ $DBENGINE = "PGSQL" ] ; then QUERY="SELECT ($SD_SD_USER_COLUMN || '@' || \ $SD_SD_DOMAIN_COLUMN) AS 'Short number', $SD_NEW_URI_COLUMN AS 'New URI',\ $SD_DESC_COLUMN FROM $SD_TABLE $CLAUSE;" fi mecho "Dumping speed-dials for user=<$2>" echo $DBCMD "$QUERY" #| $AWK 'BEGIN {line=0;} # /^\+/ { next } #{ if(line==0) print "## SpeedDial \tNew-URI \tDescription\n"; # else { # ORS_BAK=ORS; # ORS=""; # print line ") " $1 "@" $2 "\t" $3 "\t\"" $4; # for (i=5;i<=NF;++i) print FS $i; # ORS=ORS_BAK; # print "\""; # } # line++; #}' elif [ $# -eq 1 ] ; then mecho "Dumping all speed-dials may take long: do you want to proceed? [Y|N] " read answer if [ "$answer" = "y" -o "$answer" = "Y" ] ; then mecho "Dumping all speed-dials..." echo else exit 1 fi if [ $DBENGINE = "MYSQL" ] ; then QUERY="SELECT CONCAT($SD_SD_USER_COLUMN,'@',\ $SD_SD_DOMAIN_COLUMN) AS 'Short number', CONCAT($SD_USER_COLUMN,'@',\ $SD_DOMAIN_COLUMN) AS 'Owner', $SD_NEW_URI_COLUMN AS 'New URI',\ $SD_DESC_COLUMN FROM $SD_TABLE;" elif [ $DBENGINE = "PGSQL" ] ; then QUERY="SELECT ($SD_SD_USER_COLUMN||'@'||\ $SD_SD_DOMAIN_COLUMN) AS 'Short number', ($SD_USER_COLUMN||'@'||\ $SD_DOMAIN_COLUMN) AS 'Owner', $SD_NEW_URI_COLUMN AS 'New URI',\ $SD_DESC_COLUMN FROM $SD_TABLE;" fi $DBCMD "$QUERY" #| $AWK 'BEGIN {line=0;} # /^\+/ { next } # { line++; #if(line==1) print "SIP-ID \tSpeedDial \tNew-URI \tDescritpion\n"; # else { # ORS_BAK=ORS; # ORS=""; # print $3 "@" $4 "\t" $1 "@" $2 "\t" $5 "\t\"" $6; # for (i=7;i<=NF;++i) print FS $i; # ORS=ORS_BAK; # print "\""; # } # }' else merr "speeddial - wrong number of params for command [list]" usage_speeddial exit 1 fi exit $? ;; show) if [ $# -ne 2 ] ; then merr "speeddial - wrong number of params for command [show]" usage_speeddial exit 1 fi check_aor "$2" if [ $? -ne 0 ] ; then merr "speeddial - $2 is not a valid AoR (user@domain)" exit 1 fi set_user $2 CLAUSE="WHERE $SD_SD_USER_COLUMN='$OSIPSUSER' AND \ $SD_SD_DOMAIN_COLUMN='$OSIPSDOMAIN'" if [ $DBENGINE = "MYSQL" ] ; then QUERY="SELECT CONCAT($SD_USER_COLUMN,'@',$SD_DOMAIN_COLUMN) \ AS 'Owner', $SD_NEW_URI_COLUMN AS 'New URI', $SD_DESC_COLUMN FROM \ $SD_TABLE $CLAUSE ; " elif [ $DBENGINE = "PGSQL" ] ; then QUERY="SELECT ($SD_USER_COLUMN || '@' || $SD_DOMAIN_COLUMN) \ AS 'Owner', $SD_NEW_URI_COLUMN AS 'New URI', $SD_DESC_COLUMN FROM \ $SD_TABLE $CLAUSE ; " fi mecho "Details for speeddial <$2>" $DBCMD "$QUERY" # | $AWK 'BEGIN {line=0;} /^\+/ { next } # { # if(line==0) print "## SIP-ID \tNew-URI \tDescritpion\n"; # else { # ORS_BAK=ORS;usage_opensips_monitor() { # ORS=""; # print line ") " $1 "@" $2 "\t" $3 "\t\"" $4; # for (i=5;i<=NF;++i) print FS $i; # ORS=ORS_BAK; # print "\""; # } # line++; # }' exit $? ;; add) if [ $# -ne 4 ] ; then if [ $# -ne 5 ] ; then merr "speeddial - wrong number of parameters" usage_speeddial exit 1 fi fi shift check_aor "$1" if [ $? -ne 0 ] ; then merr "speeddial - $1 is not a valid AoR (user@domain)" exit 1 fi check_aor "$2" if [ $? -ne 0 ] ; then merr "speeddial - $2 is not a valid AoR (user@domain)" exit 1 fi check_sipaor "$3" if [ $? -ne 0 ] ; then merr "speeddial - $3 is not a valid SIP AoR (sip:user@domain)" exit 1 fi set_user $1 TMP_OSIPSUSER=$OSIPSUSER TMP_OSIPSDOMAIN=$OSIPSDOMAIN set_user $2 QUERY="INSERT INTO $SD_TABLE ($SD_USER_COLUMN,$SD_DOMAIN_COLUMN,\ $SD_SD_USER_COLUMN,$SD_SD_DOMAIN_COLUMN,$SD_NEW_URI_COLUMN,$SD_DESC_COLUMN) \ VALUES ('$TMP_OSIPSUSER','$TMP_OSIPSDOMAIN','$OSIPSUSER','$OSIPSDOMAIN','$3','$4');" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "speeddial - SQL Error" exit 1 fi mecho "ok - spedd dial added" echo exit $? ;; rm) if [ $# -ne 3 ] ; then merr "speeddial rm - invalid number of parameters" usage_speeddial exit 1 fi shift check_aor "$1" if [ $? -ne 0 ] ; then merr "speeddial - $1 is not a valid AoR (user@domain)" exit 1 fi check_aor "$2" if [ $? -ne 0 ] ; then merr "speeddial - $2 is not a valid AoR (user@domain)" exit 1 fi set_user $1 TMP_OSIPSUSER=$OSIPSUSER TMP_OSIPSDOMAIN=$OSIPSDOMAIN set_user $2 CLAUSE="WHERE $SD_USER_COLUMN='$TMP_OSIPSUSER' AND \ $SD_DOMAIN_COLUMN='$TMP_OSIPSDOMAIN' AND $SD_SD_USER_COLUMN='$OSIPSUSER' AND \ $SD_SD_DOMAIN_COLUMN='$OSIPSDOMAIN'" QUERY="DELETE FROM $SD_TABLE $CLAUSE;" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "speeddial - SQL Error" exit 1 fi mecho "ok - spedd dial deleted" echo ;; help) usage_speeddial ;; *) merr "speeddial - unknown command" usage_speeddial exit 1 ;; esac } # end speed_dial() # ##### ================================================ ##### ### subscriber management # subscriber() { if [ $# -lt 2 ] ; then merr "too few parameters" usage_subscriber exit 1 fi require_dbengine case $1 in add) if [ $# -ne 3 ] ; then usage_subscriber exit 1 fi shift credentials $1 $2 is_user $1 if [ $? -eq 0 ] ; then minfo "user '$1' already exists" exit 1 fi set_user $1 check_alias $OSIPSUSER $OSIPSDOMAIN if [ $ALIAS_EXISTS -eq 1 ] ; then minfo "user '$1' already exists as alias" exit 1 fi if [ $STORE_PLAINTEXT_PW -eq 1 ] ; then PASS="$2" else PASS="" fi QUERY="insert into $SUB_TABLE ($SUBSCRIBER_COLUMN,\ $REALM_COLUMN,$HA1_COLUMN,$HA1B_COLUMN,$PASSWORD_COLUMN) \ values ('$OSIPSUSER','$OSIPSDOMAIN','$HA1','$HA1B','$PASS');"; $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "introducing the new user '$1' to the database failed" else mecho "new user '$1' added" fi ;; passwd) if [ $# -ne 3 ] ; then usage_subscriber exit 1 fi shift credentials $1 $2 is_user $1 if [ $? -ne 0 ] ; then merr "non-existent user '$1'" exit 1 fi if [ $STORE_PLAINTEXT_PW -eq 1 ] ; then PASS="$2" else PASS="" fi QUERY="update $SUB_TABLE set $HA1_COLUMN='$HA1', \ $HA1B_COLUMN='$HA1B', $PASSWORD_COLUMN='$PASS' \ WHERE $SUBSCRIBER_COLUMN='$OSIPSUSER' and $REALM_COLUMN='$OSIPSDOMAIN';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "password change failed" else minfo "password change succeeded" fi ;; rm) if [ $# -ne 2 ] ; then usage_subscriber exit 1 fi require_ctlengine shift is_user $1 if [ $? -ne 0 ] ; then merr "non-existent user '$1'" exit 1 fi # begin with remove all user's privileges acl revoke $1 > /dev/null 2>&1 # destroy db-aliases QUERY="delete from $DA_TABLE where $DA_USER_COLUMN='$OSIPSUSER' \ and $DA_DOMAIN_COLUMN='$OSIPSDOMAIN';" $DBCMD "$QUERY" # destroy the user now QUERY="delete from $SUB_TABLE where $SUBSCRIBER_COLUMN='$OSIPSUSER' \ and $REALM_COLUMN='$OSIPSDOMAIN';" $DBCMD "$QUERY" # and also all his contacts $0 ul rm $1 > /dev/null 2>&1 ;; esac } # ##### ================================================ ##### ### USRLOC management # usrloc() { if [ $# -lt 2 ] ; then merr "usrloc - too few parameters" usage_usrloc exit 1 fi require_ctlengine if [ "$1" = "alias" ] ; then USRLOC_TABLE="$ALS_TABLE" if [ -z "$USRLOC_TABLE" ] ; then USRLOC_TABLE=aliases fi CHECK_SUB=1 elif [ "$1" = "ul" ] ; then USRLOC_TABLE="$UL_TABLE" if [ -z $USRLOC_TABLE ] ; then USRLOC_TABLE=location fi CHECK_SUB=0 elif [ "$1" = "usrloc" ] ; then USRLOC_TABLE="$UL_TABLE" if [ -z $USRLOC_TABLE ] ; then USRLOC_TABLE=location fi CHECK_SUB=0 else merr "usrloc - unknown subcommand '$1'" usage_usrloc exit 1 fi shift case "$1" in show) if [ $# -eq 2 ] ; then if [ "$2" = "--brief" ] ; then $CTLCMD ul_dump brief else set_user $2 $CTLCMD ul_show_contact \ $USRLOC_TABLE "$OSIPSUSER@$OSIPSDOMAIN" fi elif [ $# -eq 1 ] ; then $CTLCMD ul_dump else merr "wrong number of params" usage_usrloc exit 1 fi exit $? ;; add) if [ $# -eq 3 ] ; then # expires 0 means persistent contact UL_EXPIRES=0 UL_FLAGS=0 BR_FLAGS=0 elif [ $# -eq 4 ] ; then UL_EXPIRES=$4 UL_FLAGS=0 BR_FLAGS=0 else usage_usrloc exit 1 fi shift check_uri "$2" if [ $? -ne 0 ] ; then merr "$2 is not a valid URI" exit 1 fi set_user $1 if [ $CHECK_SUB -ne 0 ] ; then is_user $1 if [ $? -eq 0 ] ; then merr "overlap of alias with an existing subscriber name" exit 1; fi fi check_alias $OSIPSUSER $OSIPSDOMAIN if [ $ALIAS_EXISTS -eq 1 ] ; then if [ $CHECK_SUB -ne 0 ] ; then minfo "alias already defined" else merr "AOR is an alias" fi exit 1 fi $CTLCMD ul_add "$USRLOC_TABLE" "$OSIPSUSER@$OSIPSDOMAIN" "$2" \ "$UL_EXPIRES" "1.00" "0" "$UL_FLAGS" "$BR_FLAGS" "$ALL_METHODS" exit $? ;; rm) if [ $# -eq 2 ] ; then shift set_user $1 $CTLCMD ul_rm $USRLOC_TABLE "$OSIPSUSER@$OSIPSDOMAIN" elif [ $# -eq 3 ] ; then shift set_user $1 check_uri "$2" if [ $? -ne 0 ] ; then merr "$2 is not a valid SIP URI (sip:[user@]domain)" exit 1 fi $CTLCMD ul_rm_contact $USRLOC_TABLE "$OSIPSUSER@$OSIPSDOMAIN" "$2" else merr "wrong number of params" usage_usrloc exit 1 fi ;; *) usage_usrloc exit 1 ;; esac } ##### ================================================ ##### ### TLS CA management # tls_ca() { if [ "$1" = "rootCA" ] ; then if [ -z $2 ] ; then # use default CA_BASE=$ETCDIR/tls else CA_BASE=`(cd $2;pwd)` fi if [ ! -d $CA_BASE ] ; then merr "Config directory ($CA_BASE) does not exist" exit 1 fi CA_CONF='ca.conf' CA_PATH=$CA_BASE/rootCA if [ ! -f $CA_BASE/$CA_CONF ] ; then merr "root CA config file ($CA_BASE/$CA_CONF) does not exist" exit 1 fi if [ -d $CA_PATH ] ; then mwarn "root CA directory ($CA_PATH) exists! Remove it (y/n)?" read X if [ "$X" != "y" -a "$X" != "Y" ] ; then exit 1 fi fi mecho "Creating directory $CA_PATH and its sub-tree" mkdir -p $CA_PATH if [ $? -ne 0 ] ; then merr "Failed to create root directory $CA_PATH" exit 1 fi rm -fr $CA_PATH/* mkdir $CA_PATH/private mkdir $CA_PATH/certs touch $CA_PATH/index.txt echo 01 >$CA_PATH/serial mecho "Creating CA self-signed certificate" ( cd $CA_PATH; openssl req -config $CA_BASE/$CA_CONF -x509 -newkey \ rsa:2048 -days 365 -out ./cacert.pem -outform PEM ) if [ $? -ne 0 ] ; then merr "Failed to create self-signed certificate" exit 1 fi mecho "Protecting CA private key" chmod 600 $CA_PATH/private/cakey.pem mecho "DONE" minfo "Private key can be found in $CA_PATH/private/cakey.pem" minfo "Certificate can be found in $CA_PATH/cacert.pem" elif [ "$1" = "userCERT" ] ; then if [ -z $2 ] ; then merr "Missing user name parameter" exit 1 fi if [ -z $3 ] ; then # use default CA_BASE=$ETCDIR/tls else CA_BASE=`(cd $3;pwd)` fi if [ ! -d $CA_BASE ] ; then merr "Config directory ($CA_BASE) does not exist" exit 1 fi USER_DIR=$CA_BASE/$2 USER_CFG=$CA_BASE/$2.conf USER=$2 REQ_CFG=$CA_BASE/request.conf if [ ! -f $USER_CFG ] ; then merr "User config file $USER_CFG not found" exit 1 fi if [ ! -f $REQ_CFG ] ; then merr "Request config file $REQ_CFG not found" exit 1 fi mecho "Using config file $USER_CFG" if [ -d $USER_DIR ] ; then mwarn "User CERT directory ($USER_DIR) exists! Remove it (y/n)?" read X if [ "$X" != "y" -a "$X" != "Y" ] ; then exit 1 fi fi mecho "Creating directory $USER_DIR" mkdir -p $USER_DIR if [ $? -ne 0 ] ; then merr "Failed to create user directory $USER_DIR " exit 1 fi rm -fr $USER_DIR/* mecho "Creating user certificate request" openssl req -config $USER_CFG -out $USER_DIR/$USER-cert_req.pem \ -keyout $USER_DIR/$USER-privkey.pem -new -nodes if [ $? -ne 0 ] ; then merr "Failed to generate certificate request" exit 1 fi mecho "Signing certificate request" ( cd $CA_BASE ; openssl ca -config $REQ_CFG -in \ $USER_DIR/$USER-cert_req.pem -out $USER_DIR/$USER-cert.pem ) if [ $? -ne 0 ] ; then merr "Failed to generate certificate request" exit 1 fi mecho "Generating CA list" cat $CA_BASE/rootCA/cacert.pem >> $USER_DIR/$USER-calist.pem mecho "DONE" minfo "Private key is locate at $USER_DIR/$USER-privkey.pem " minfo "Certificate is locate at $USER_DIR/$USER-cert.pem " minfo "CA-List is locate at $USER_DIR/$USER-calist.pem " else [ -n "$1" ] && merr "unknown TLS command: \"$1\"" usage_tls exit 1 fi } ##### ================================================ ##### ### trap with gdb opensips processes # opensips_trap() { if [ -z "$GDB" ] ; then merr "'gdb' tool not found: set GDB variable to correct tool path" exit fi DATE=`/bin/date +%Y%m%d_%H%M%S` LOG_FILE=/tmp/gdb_opensips_$DATE minfo "Trap file: $LOG_FILE" $CTLCMD ps > $LOG_FILE echo -n "Trapping OpenSIPS with gdb: " PID_TIMESTAMP_VECTOR=`sed -e 's/.*PID=\([0-9]*\).*/\1/' $LOG_FILE` minfo "$PID_TIMESTAMP_VECTOR" for pid in $PID_TIMESTAMP_VECTOR do echo -n "." PID=`echo $pid | cut -d '-' -f 1` echo "" >> $LOG_FILE echo "---start $PID -----------------------------------------------------" >> $LOG_FILE $GDB opensips $PID -batch --eval-command="bt full" 2>&1 >> $LOG_FILE echo "---end $PID -------------------------------------------------------" >> $LOG_FILE done echo "." } # ##### ================================================ ##### ### main command switch # case $1 in acl) shift acl "$@" ;; add) subscriber "$@" ;; passwd) subscriber "$@" ;; rm) subscriber "$@" ;; alias|ul|usrloc) usrloc "$@" ;; alias_db|aliasdb) alias_db "$@" ;; avp) avpops "$@" ;; cisco_restart) if [ $# -ne 2 ] ; then usage_cisco_restart exit 1 fi cisco_restart $2 ;; db) shift db_ops "$@" ;; showdb|userdb) usage exit 1 ;; domain) shift domain "$@" ;; address) shift address "$@" ;; fifo|unixsock) require_ctlengine shift $CTLCMD "$@" ;; cr) shift cr "$@" ;; dispatcher) shift dispatcher "$@" ;; dr) shift dr "$@" ;; dialplan) shift dialplan "$@" ;; registrant) shift registrant "$@" ;; monitor|console|moni|con) require_ctlengine $OPENSIPS_MONITOR "$@" ;; online) require_ctlengine $CTLCMD ul_dump | $EGREP -i aor | awk '{print $2}' | sort | sort -mu exit $? ;; ping) # error handling is hacked -- filter_fl should not # consume positive status -- that should be done by # calling app if [ $# -ne 2 ] ; then usage_ping exit 1 fi options_ping $2 ;; ps) require_ctlengine $CTLCMD ps ;; restart) opensips_stop restart sleep 2 opensips_start ;; rpid) rpid "$@" ;; speeddial|speed_dial) speeddial "$@" ;; tls) shift tls_ca "$@" ;; trap) require_ctlengine opensips_trap ;; start) opensips_start ;; stop) opensips_stop ;; version) echo "$0 $VERSION" ;; *) merr "$0: Unknown command - \"$1\"" usage exit 1 ;; esac opensips-2.2.2/scripts/opensipsctl.8000066400000000000000000000072761300170765700175220ustar00rootroot00000000000000.\" $Id$ .TH opensipsctl 8 21.06.2006 opensips "OpenSIPS" .\" Process with .\" groff -man -Tascii opensipsctl.8 .\" .SH NAME opensipsctl \- opensips control tool .SH SYNOPSIS .B opensipsctl .BI command [ .BI parameters ] .SH DESCRIPTION .B opensipsctl is a shell script to control .B OpenSIPS SIP server It can be used to manage users, domains, aliases and other server options .SH FILES .PD 0 .I /etc/opensips/.opensipsctlrc .br .I /usr/local/etc/opensips/.opensipsctlrc .br .I ~/.opensipsctlrc .br .SH COMMANDS .TP 12 .B \-h Displays a short usage description, including all available options. .TP .B moni show internal status .TP .B ps show running processes .TP .B fifo send raw FIFO commands .TP .B ping ping a URI (OPTIONS) .TP .B cisco_restart restart a Cisco phone (NOTIFY) .TP 16 .I SUBSCRIBER management: .TP .B add add a new subscriber (*) .TP .B passwd change user's password (*) .TP .B rm delete a user (*) .TP .B alias show [] show aliases .TP .B alias rm remove an alias .TP .B alias add add an aliases .TP .B rpid add add rpid for a user (*) .TP .B rpid rm set rpid to NULL for a user (*) .TP .B rpid show show rpid of a user .TP .B alias_db show show alias details .TP .B alias_db list list aliases for uri .TP .B alias_db add add an alias (*) .TP .B alias_db rm remove an alias (*) .TP .B alias_db help help message .TP .B dispatcher show show dispatcher gateways .TP .B dispatcher reload reload dispatcher gateways .TP .B dispatcher dump show in memory dispatcher gateways .TP .B dispatcher addgw [description] add gateway .TP .B dispatcher rmgw delete gateway .TP .B speeddial show show speeddial details .TP .B speeddial list list speeddial for uri .TP .B speeddial add [] add a speedial (*) .TP .B speeddial rm remove a speeddial (*) .TP .B speeddial help help message .TP 16 .I AVP management: .TP .B avp list [\-T table] [\-u ] \ [\-a attribute] [\-v value] [\-t type] list AVPs .TP .B avp add [\-T table] \ add AVP (*) .TP .B avp rm [\-T table] [\-u ] \ [\-a attribute] [\-v value] [\-t type] remove AVP (*) .TP .B avp help help message .TP 16 .I ACL (Access Control Lists) management .TP .B acl show [] show user membership .TP .B acl grant grant user membership (*) .TP .B acl revoke [] grant user membership(s) (*) .TP 16 .I USRLOC (User Location) management .TP .B ul show [] show in-RAM online users .TP .B ul rm [] delete user's UsrLoc entries .TP .B ul add introduce a permanent UrLoc entry .TP .B ul add introduce a temporary UrLoc entry .TP .B showdb [] show online users flushed in DB .TP 16 .I DOMAIN management .TP .B domain show show list of served domains .TP .B domain add add a new served domain .TP .B domain rm remove a served domain .SH NOTES .PP Commands labeled with (*) will prompt for a MySQL password. If the environment variable PW is set, the password will not be prompted. .PP IP addresses must be entered in dotted quad format e.g. 1.2.3.4 .SH AUTHORS see .B /usr/share/doc/opensips/AUTHORS .SH SEE ALSO .BR opensips(8), opensips.cfg(5) .PP Full documentation on opensips is available at .I http://www.opensips.org/. .PP Mailing lists: .nf users@opensips.org - opensips user community .nf devel@opensips.org - opensips development, new features and unstable version opensips-2.2.2/scripts/opensipsctl.base000066400000000000000000000460571300170765700202650ustar00rootroot00000000000000# # $Id$ # # sc: opensips control; tool for maintaining opensips # #=================================================================== ##### ----------------------------------------------- ##### ### path to useful tools locate_tool() { TOOLPATH="" while [ -n "$1" ] do if [ -x /usr/bin/which ] ; then TOOLPATH=`/usr/bin/which $1` if [ -n "$TOOLPATH" ]; then return fi fi # look in common locations if [ -x "/usr/bin/$1" ] ; then TOOLPATH="/usr/bin/$1" return fi if [ -x "/bin/$1" ] ; then TOOLPATH="/bin/$1" return fi #The md5 tool on FreeBSD 8.3 is located in /sbin/ if [ -x "/sbin/$1" ] ; then TOOLPATH="/sbin/$1" return fi if [ -x "/usr/local/bin/$1" ] ; then TOOLPATH="/usr/local/bin/$1" return fi # Additional locations of GNU tools in Solaris if [ -x "/usr/sfw/bin/$1" ] ; then TOOLPATH="/usr/sfw/bin/$1" return fi if [ -x "/opt/csw/bin/$1" ] ; then TOOLPATH="/opt/csw/bin/$1" return fi shift done return } if [ -z "$EGREP" ] ; then locate_tool egrep if [ -z "$TOOLPATH" ] ; then # now error, but we can look for alternative names if it is the case echo "error: 'egrep' tool not found: set EGREP variable to correct tool path" exit fi EGREP="$TOOLPATH" fi if [ -z "$AWK" ] ; then locate_tool awk if [ -z "$TOOLPATH" ] ; then # now error, but we can look for alternative names if it is the case echo "error: 'awk' tool not found: set AWK variable to correct tool path" exit fi AWK="$TOOLPATH" fi if [ -z "$GDB" ] ; then locate_tool gdb if [ -z "$TOOLPATH" ] ; then # no gdb GDB="" else GDB="$TOOLPATH" fi fi if [ -z "$MD5" ]; then locate_tool md5sum md5 if [ -z "$TOOLPATH" ] ; then # now error, but we can look for alternative names if it is the case echo "error: 'md5sum' or 'md5' tool not found: set MD5 variable to correct tool path" exit fi MD5="$TOOLPATH" fi if [ -z "$LAST_LINE" ] ; then locate_tool tail if [ -z "$TOOLPATH" ] ; then # now error, but we can look for alternative names if it is the case echo "error: 'TAIL' tool not found: set TAIL variable to correct tool path" exit fi LAST_LINE="$TOOLPATH -1" fi if [ -z "$EXPR" ] ; then locate_tool expr if [ -z "$TOOLPATH" ] ; then # now error, but we can look for alternative names if it is the case echo "error: 'expr' tool not found: set EXPR variable to correct tool path" exit fi EXPR="$TOOLPATH" fi ##### ------------------------------------------------ ##### ### configuration for starting/stopping opensips if [ -z "$PID_FILE" ] ; then PID_FILE=/var/run/opensips.pid fi if [ -z "$SYSLOG" ] ; then SYSLOG=1 # 0=output to console, 1=output to syslog fi if [ -z "$STARTOPTIONS" ] ; then STARTOPTIONS= # for example -dddd fi if [ -z "$DIR" ] ; then DIR=`dirname $0` fi if [ -z "$OSIPSBIN" ] ; then OSIPSBIN=$DIR/opensips fi ##### ------------------------------------------------ ##### ### aliases configuration # ENABLE_ALIASES=0 if [ "$ALIASES_TYPE" = "UL" ] ; then ENABLE_ALIASES=1 else if [ "$ALIASES_TYPE" = "DB" ] ; then ENABLE_ALIASES=2 fi fi ##### ------------------------------------------------ ##### ### ACL name verification if [ -z "$VERIFY_ACL" ] ; then VERIFY_ACL=1 fi if [ -z "$ACL_GROUPS" ] ; then ACL_GROUPS="local ld int voicemail free-pstn" fi ##### ----------------------------------------------- ##### #### Defined values ALL_METHODS=4294967295 USERNAME_RE="[-a-zA-Z0-9&=\+\$,;\?/_\.\!~\*'\(\)]+" ##### ----------------------------------------------- ##### #### database tables for SQL databases and DBTEXT # UsrLoc Table if [ -z "$UL_TABLE" ] ; then UL_TABLE=location fi USER_COLUMN=username DOMAIN_COLUMN=domain CALLID_COLUMN=callid # subscriber table if [ -z "$SUB_TABLE" ] ; then SUB_TABLE=subscriber fi REALM_COLUMN=domain HA1_COLUMN=ha1 HA1B_COLUMN=ha1b PASSWORD_COLUMN=password RPID_COLUMN=rpid SUBSCRIBER_COLUMN='username' PHP_LIB_COLUMN=phplib_id if [ -z "$STORE_PLAINTEXT_PW" ] ; then STORE_PLAINTEXT_PW=1 fi # acl table if [ -z "$ACL_TABLE" ] ; then ACL_TABLE=grp fi ACL_USER_COLUMN=username ACL_DOMAIN_COLUMN=domain ACL_GROUP_COLUMN=grp ACL_MODIFIED_COLUMN=last_modified # aliases table if [ -z "$ALS_TABLE" ] ; then ALS_TABLE=aliases fi A_USER_COLUMN=username A_CONTACT_COLUMN=contact A_EXPIRES_COLUMN=expires A_Q_COLUMN=q A_CALLID_COLUMN=callid A_CSEQ_COLUMN=cseq A_LAST_MODIFIED_COLUMN=last_modified # domain table if [ -z "$DOMAIN_TABLE" ] ; then DOMAIN_TABLE=domain fi DO_DOMAIN_COLUMN=domain DO_LAST_MODIFIED_COLUMN=last_modified # route_tree table if [ -z "$ROUTE_TREE_TABLE" ] ; then ROUTE_TREE_TABLE=route_tree fi CARRIERROUTE_ROUTE_TREE_PREFIX_COLUMN=id CARRIERROUTE_ROUTE_TREE_CARRIER_COLUMN=carrier # carrierroute table if [ -z "$CARRIERROUTE_TABLE" ] ; then CARRIERROUTE_TABLE=carrierroute fi CARRIERROUTE_CARRIERROUTE_PREFIX_COLUMN=id CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN=carrier CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN=scan_prefix CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN=domain CARRIERROUTE_CARRIERROUTE_PROB_COLUMN=prob CARRIERROUTE_CARRIERROUTE_STRIP_COLUMN=strip CARRIERROUTE_CARRIERROUTE_REWRITE_HOST_COLUMN=rewrite_host CARRIERROUTE_CARRIERROUTE_REWRITE_PREFIX_COLUMN=rewrite_prefix CARRIERROUTE_CARRIERROUTE_REWRITE_SUFFIX_COLUMN=rewrite_suffix CARRIERROUTE_CARRIERROUTE_COMMENT_COLUMN=description CARRIERROUTE_CARRIERROUTE_FLAGS_COLUMN=flags CARRIERROUTE_CARRIERROUTE_MASK_COLUMN=mask # URI table if [ -z "$URI_TABLE" ] ; then URI_TABLE=uri fi URIUSER_COLUMN=uri_user MODIFIED_COLUMN=last_modified # dbaliases table if [ -z "$DA_TABLE" ] ; then DA_TABLE=dbaliases fi DA_USER_COLUMN=username DA_DOMAIN_COLUMN=domain DA_ALIAS_USER_COLUMN=alias_username DA_ALIAS_DOMAIN_COLUMN=alias_domain # speeddial table if [ -z "$SD_TABLE" ] ; then SD_TABLE=speed_dial fi SD_USER_COLUMN=username SD_DOMAIN_COLUMN=domain SD_SD_USER_COLUMN=sd_username SD_SD_DOMAIN_COLUMN=sd_domain SD_NEW_URI_COLUMN=new_uri SD_DESC_COLUMN=description # avp table if [ -z "$AVP_TABLE" ] ; then AVP_TABLE=usr_preferences fi AVP_UUID_COLUMN=uuid AVP_USER_COLUMN=username AVP_DOMAIN_COLUMN=domain AVP_ATTRIBUTE_COLUMN=attribute AVP_VALUE_COLUMN=value AVP_TYPE_COLUMN=type AVP_MODIFIED_COLUMN=last_modified # address table if [ -z "$ADDRESS_TABLE" ] ; then ADDRESS_TABLE=address fi ADDRESS_GRP_COLUMN=grp ADDRESS_IP_COLUMN=ip ADDRESS_MASK_COLUMN=mask ADDRESS_PORT_COLUMN=port ADDRESS_PROTO_COLUMN=proto ADDRESS_PATTERN_COLUMN=pattern ADDRESS_CONTEXT_INFO_COLUMN=context_info # dispatcher tables if [ -z "$DISPATCHER_TABLE" ] ; then DISPATCHER_TABLE=dispatcher fi DISPATCHER_ID_COLUMN=id DISPATCHER_SETID_COLUMN=setid DISPATCHER_DESTINATION_COLUMN=destination DISPATCHER_SOCKET_COLUMN=socket DISPATCHER_STATE_COLUMN=state DISPATCHER_WEIGHT_COLUMN=weight DISPATCHER_ATTRS_COLUMN=attrs DISPATCHER_DESCRIPTION_COLUMN=description # drouting tables if [ -z "$DR_GATEWAYS_TABLE" ] ; then DR_GATEWAYS_TABLE=dr_gateways fi DR_GATEWAYS_ID_COLUMN=id DR_GATEWAYS_GWID_COLUMN=gwid DR_GATEWAYS_TYPE_COLUMN=type DR_GATEWAYS_ADDRESS_COLUMN=address DR_GATEWAYS_STRIP_COLUMN=strip DR_GATEWAYS_PRI_PREFIX_COLUMN=pri_prefix DR_GATEWAYS_ATTRS_COLUMN=attrs DR_GATEWAYS_PROBE_MODE_COLUMN=probe_mode DR_GATEWAYS_DESCRIPTION_COLUMN=description if [ -z "$DR_GROUPS_TABLE" ] ; then DR_GROUPS_TABLE=dr_groups fi DR_GROUPS_ID_COLUMN=id DR_GROUPS_USERNAME_COLUMN=username DR_GROUPS_DOMAIN_COLUMN=domain DR_GROUPS_GROUPID_COLUMN=groupid DR_GROUPS_DESCRIPTION_COLUMN=description if [ -z "$DR_CARRIERS_TABLE" ] ; then DR_CARRIERS_TABLE=dr_carriers fi DR_CARRIERS_ID_COLUMN=id DR_CARRIERS_CARRIERID_COLUMN=carrierid DR_CARRIERS_GWLIST_COLUMN=gwlist DR_CARRIERS_FLAGS_COLUMN=flags DR_CARRIERS_ATTRS_COLUMN=attrs DR_CARRIERS_DESCRIPTION_COLUMN=description if [ -z "$DR_RULES_TABLE" ] ; then DR_RULES_TABLE=dr_rules fi DR_RULES_RULEID_COLUMN=ruleid DR_RULES_GROUPID_COLUMN=groupid DR_RULES_PREFIX_COLUMN=prefix DR_RULES_TIMEREC_COLUMN=timerec DR_RULES_PRIORITY_COLUMN=priority DR_RULES_ROUTEID_COLUMN=routeid DR_RULES_GWLIST_COLUMN=gwlist DR_RULES_ATTRS_COLUMN=attrs DR_RULES_DESCRIPTION_COLUMN=description # dialplan tables if [ -z "$DIALPLAN_TABLE" ] ; then DIALPLAN_TABLE=dialplan fi DIALPLAN_ID_COLUMN=id DIALPLAN_DPID_COLUMN=dpid DIALPLAN_PR_COLUMN=pr DIALPLAN_MATCH_OP_COLUMN=match_op DIALPLAN_MATCH_EXP_COLUMN=match_exp DIALPLAN_MATCH_FLAGS_COLUMN=match_flags DIALPLAN_SUBST_EXP_COLUMN=subst_exp DIALPLAN_REPL_EXP_COLUMN=repl_exp DIALPLAN_ATTRS_COLUMN=attrs #registrant tables if [ -z "$REGISTRANT_TABLE" ] ; then REGISTRANT_TABLE=registrant fi REGISTRANT_ID_COLUMN=id REGISTRANT_REGISTRAR_COLUMN=registrar REGISTRANT_PROXY_COLUMN=proxy REGISTRANT_AOR_COLUMN=aor REGISTRANT_THIRD_PARTY_REGISTRANT_COLUMN=third_party_registrant REGISTRANT_USERNAME_COLUMN=username REGISTRANT_PASSWORD_COLUMN=password REGISTRANT_BINDING_URI_COLUMN=binding_URI REGISTRANT_BINDING_PARAMS_COLUMN=binding_params REGISTRANT_EXPIRY_COLUMN=expiry REGISTRANT_FORCED_SOCKET_COLUMN=forced_socket # ##### ------------------------------------------------ ##### ### usage functions # usage_base() { echo mecho " -- command 'start|stop|restart|trap'" echo cat <] .......... creates new rootCA tls userCERT [] ... creates user certificate default is $ETCDIR/tls EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_base" usage_acl() { echo mecho " -- command 'acl' - manage access control lists (acl)" echo cat <] .............. show user membership acl grant ....... grant user membership (*) acl revoke [] .... grant user membership(s) (*) EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_acl" usage_cr() { echo mecho " -- command 'cr' - manage carrierroute tables" echo cat < ..................... add a tree cr rmrt ....................................... rm a tree cr addcarrier ................ ............... .........................add a carrier (prob, strip, rewrite_prefix, rewrite_suffix,................... flags, mask and comment are optional arguments) ............... cr rmcarrier ................ rm a carrier EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_cr" usage_rpid() { echo mecho " -- command 'rpid' - manage Remote-Party-ID (RPID)" echo cat < ......... add rpid for a user (*) rpid rm ................. set rpid to NULL for a user (*) rpid show ............... show rpid of a user EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_rpid" usage_subscriber() { echo mecho " -- command 'add|passwd|rm' - manage subscribers" echo cat < .......... add a new subscriber (*) passwd ......... change user's password (*) rm ...................... delete a user (*) EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_subscriber" usage_address() { echo mecho " -- command 'add|dump|reload|rm|show' - manage address" echo cat < [] [] ....................... add a new entry ....................... (from_pattern and tag are optional arguments) address rm ............... remove all entries ....................... for the given grp ip mask port EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_address" usage_dr() { echo mecho " -- command 'dr' - manage dynamic routing" echo cat <
................................. add gateway dr rmgw ....................... delete gateway dr addgrp ................................. add gateway group dr rmgrp ...................... delete gateway group dr addcr ........................... add carrier dr rmcr ....................... delete carrier dr addrule ................................. add rule dr rmrule ................. delete rule dr reload .......................... reload dr tables dr gw_status ....................... show gateway status dr carrier_status .................. show carrier status EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_dr" usage_dispatcher() { echo mecho " -- command 'dispatcher' - manage dispatcher" echo cat < [description] .......................... add gateway dispatcher rmgw ................ delete gateway EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_dispatcher" usage_dialplan() { echo mecho " -- command 'dialplan' - manage dialplans" echo cat < .............. show dialplan tables dialplan reload ................... reload dialplan tables dialplan addrule .................... add a rule dialplan rm ....................... removes the entire dialplan table dialplan rmdpid ............ removes all the gived dpid entries dialplan rmrule ..... removes all the gived dpid/prio entries EOF } usage_registrant() { echo mecho " -- command 'registrant' - manage registrants" echo cat < . add a registrant registrant rm ........................... removes the entire registrant table registrant rmaor ................... removes the gived aor id EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_registrant" ##### ----------------------------------------------- ##### #### Common functions mdbg() { if [ "0$VERBOSE" -ne 0 ] ; then if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e "\033[1m$1\033[0m" else echo "$1" fi fi } mwarn() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;32m'"\033[1mWARNING: $1\033[0m" else echo "** WARNING: $1" fi } minfo() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;33m'"\033[1mINFO: $1\033[0m" else echo "** INFO: $1" fi } mecho() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e "\033[1m$1\033[0m" else echo "$1" fi } merr() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;31m'"\033[1mERROR: $1\033[0m" else echo "** ERROR: $1" fi } # determine host name, typically for use in printing UAC # messages; we use today a simplistic but portable uname -n way -- # no domain name is displayed ; fifo_uac expands !! to host # address only for optional header fields; uname output without # domain is sufficient for informational header fields such as # From # get_my_host() { if [ -z "$SIP_DOMAIN" ]; then uname -n else echo "$SIP_DOMAIN" fi } # calculate name and domain of current user set_user() { OSIPSUSER=`echo $1|$AWK -F@ '{print $1}'` OSIPSDOMAIN=`echo $1|$AWK -F@ '{print $2}'` if [ -z "$OSIPSDOMAIN" ] ; then OSIPSDOMAIN="$SIP_DOMAIN" fi if [ -z "$OSIPSDOMAIN" ] ; then merr "domain unknown: use usernames with domain or set default domain \ in SIP_DOMAIN" exit 1 fi } # check the parameter if it is a valid address of record (user@domain) check_aor() { echo "$1" | $EGREP "^$USERNAME_RE@.*\..*" >/dev/null if [ $? -ne 0 ] ; then echo "error: invalid AoR: $1" > /dev/stderr exit 1 fi } # check the parameter if it is a valid address of record (user@domain) is_aor() { echo "$1" | $EGREP "^$USERNAME_RE@.*\..*" >/dev/null if [ $? -ne 0 ] ; then false else true fi } # check the parameter if it is a valid SIP address of record (sip:user@domain) check_sipaor() { echo "$1" | $EGREP "^sip(s)?:$USERNAME_RE@.*\..*" >/dev/null if [ $? -ne 0 ] ; then echo "error: invalid SIP AoR: $1" > /dev/stderr exit 1 fi } # check the parameter if it is a valid SIP URI # quite simplified now -- it captures just very basic # errors check_uri() { echo "$1" | $EGREP "^sip(s)?:($USERNAME_RE@)?.*\..*" > /dev/null if [ $? -ne 0 ] ; then echo "error: invalid SIP URI: $1" > /dev/stderr exit 1 fi } print_status() { echo $1 | $EGREP "^[1-6][0-9][0-9]" > /dev/null if [ "$?" -eq 0 ] ; then echo $1 else echo "200 OK" fi } # process output from FIFO/Unixsock server; if everything is ok # skip the first "ok" line and proceed to returned # parameters filter_fl() { # tail +2 $AWK 'BEGIN {line=0;IGNORECASE=1;} {line++} NR == 1 && /^200 OK/ { next } /^$/ { next } { print }' } # params: user, realm, password # output: HA1 _gen_ha1() { HA1=`echo -n "$1:$2:$3" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then echo "HA1 calculation failed" exit 1 fi } # params: user, realm, password # output: HA1B _gen_ha1b() { HA1B=`echo -n "$1@$2:$2:$3" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then echo "HA1B calculation failed" exit 1 fi } # params: user, realm, password # output: PHPLIB_ID _gen_phplib_id() { NOW=`date`; PHPLIB_ID=`echo -n "$1$2:$3:$NOW" | $MD5 | $AWK '{ print $1 }'` } # params: user, password # output: HA1, HA1B credentials() { set_user $1 _gen_ha1 "$OSIPSUSER" "$OSIPSDOMAIN" "$2" _gen_ha1b "$OSIPSUSER" "$OSIPSDOMAIN" "$2" } opensips-2.2.2/scripts/opensipsctl.ctlbase000066400000000000000000000035661300170765700207660ustar00rootroot00000000000000# # $Id: opensipsctl.ctlbase 3511 2008-01-08 10:20:46Z henningw $ # # sc: opensips control; tool for maintaining opensips # #=================================================================== ##### ----------------------------------------------- ##### ### common variables and functions for CTL engines # # period in which stats are reprinted if [ -z "$WATCH_PERIOD" ] ; then WATCH_PERIOD=2 fi # ##### ------------------------------------------------ ##### ### usage functions # usage_cisco_restart() { echo mecho " -- command 'cisco_restart' - restart CISCO phone (NOTIFY)" echo cat < ................ restart phone configured for EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_cisco_restart" usage_online() { echo mecho " -- command 'online' - dump online users from memory" echo cat < ......................... ping with SIP OPTIONS EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_ping" usage_usrloc() { echo mecho " -- command 'ul|alias' - manage user location or aliases" echo cat <]................ show in-RAM online users ul show --brief..................... show in-RAM online users in short format ul rm [].... delete user's usrloc entries ul add ............ introduce a permanent usrloc entry ul add .. introduce a temporary usrloc entry EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_usrloc" opensips-2.2.2/scripts/opensipsctl.db_berkeley000066400000000000000000000025451300170765700216140ustar00rootroot00000000000000# $Id: opensipsctl.db_berkeley 4382 2008-06-12 11:04:51Z henningw $ # # opensips control; tool for maintaining opensips's databases # # History: # -------- # 2007-11-05 genesis (wiquan) #=================================================================== # path to the db_berkeley directory if [ -z "$DB_PATH" ] ; then DB_PATH="/usr/local/share/opensips/db_berkeley/opensips" fi #=================================================================== opensips_bdb() { case $1 in reload) shift if [ "$#" -lt 1 ] ; then merr "reload - too few parameters" exit 1 fi $CTLCMD bdb_reload $1 exit $? ;; *) usage exit 1 ;; esac } # domain don't support reload at the moment usage_domain() { echo mecho " -- command 'domain' - manage domains" echo cat < ............ add a new served domain domain rm ............. remove a served domain EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_domain" # showdb is not implemented for SQL databases usage_showdb() { echo mecho " -- command 'showdb|userdb' - dump offline users" echo cat < /dev/null 2>&1 && break done name=osips_rply_$suffix path=$CHROOT_DIR/tmp/$name # construct the command now CMD=":$1:$name\n"; shift while [ -n "$1" ] ; do CMD="${CMD}${1}\n" shift done CMD="${CMD}\n" trap "rm -f $path; kill 0" 2 # start reader now so that it is ready for replies # immediately after a request was sent out cat < $path | filter_fl & # issue FIFO request (printf taken to deal with \n) printf "$CMD" > $OSIPS_FIFO # wait for the reader to complete wait rm $path mdbg "FIFO command was:\n$CMD" } CTLCMD=fifo_cmd fifo_opensips_monitor() { name=opensips_receiver_$$ path=$CHROOT_DIR/tmp/$name if [ ! -w $OSIPS_FIFO ]; then merr "Error opening OpenSIPS's FIFO $OSIPS_FIFO" merr "Make sure you have the line 'modparam(\"mi_fifo\", \"fifo_name\", \"$OSIPS_FIFO\")' in your config" merr "and also have loaded the mi_fifo module." exit 1 fi if ! test -p $path; then mkfifo $path if [ $? -ne 0 ] ; then merr "monitor - error opening read fifo $path" exit 1 fi chmod a+w $path fi trap "rm $path; clear; echo monitor ^C-ed; exit 1" 2 attempt=0 if [ "$2" = "" ]; then loops=-1; else loops=$2; fi clear while [ $loops -ne $attempt ] ; do attempt=`$EXPR $attempt + 1` #clear tput clear # print_stats $name $path $attempt mecho "[cycle #: $attempt; if constant make sure server lives]" cat < $path | filter_fl & cat > $OSIPS_FIFO < $OSIPS_FIFO << EOF :uptime:$name EOF wait echo mecho "Transaction Statistics: " cat < $path | filter_fl & cat > $OSIPS_FIFO < $OSIPS_FIFO < $OSIPS_FIFO < /dev/stderr else printf "$1: " > /dev/stderr fi stty -echo read DBRWPW stty $savetty echo fi } # dbtext don't support db_ops usage_db_ops() { echo mecho " -- command 'db' - database operations" echo cat < ..................... execute SQL query db roexec ................. execute read-only SQL query db run ......................... execute SQL query from \$id variable db rorun ....................... execute read-only SQL query from \$id variable db show
..................... display table content EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_db_ops" # speeddial not implemented for dbtext usage_speeddial() { echo mecho " -- command 'speeddial' - manage speed dials (short numbers)" echo cat < ....... show speeddial details speeddial list ............. list speeddial for uri speeddial add [] ... ........................... add a speedial (*) speeddial rm ....... remove a speeddial (*) speeddial help ...................... help message - , must be an AoR (username@domain) - must be an AoR (username@domain) - must be a SIP AoR (sip:username@domain) - a description for speeddial EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_speeddial" # avp operations not implemented for dbtext usage_avp() { echo mecho " -- command 'avp' - manage AVPs" echo cat <] [-a attribute] [-v value] [-t type] ... list AVPs avp add [-T table] ............ add AVP (*) avp rm [-T table] [-u ] [-a attribute] [-v value] [-t type] ... remove AVP (*) avp help .................................. help message - -T - table name - -u - SIP id or unique id - -a - AVP name - -v - AVP value - -t - AVP name and type (0 (str:str), 1 (str:int), 2 (int:str), 3 (int:int)) - must be an AoR (username@domain) - must be a string but not AoR EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_avp" # alias_db not implemented for dbtext usage_alias_db() { echo mecho " -- command 'alias_db' - manage database aliases" echo cat < .............. show alias details alias_db list ............. list aliases for uri alias_db add ...... add an alias (*) alias_db rm ................ remove an alias (*) alias_db help ...................... help message - must be an AoR (username@domain)" - must be an AoR (username@domain)" EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_alias_db" usage_domain() { echo mecho " -- command 'domain' - manage local domains" echo cat < ................. add the domain to the database domain rm .................. delete the domain from the database EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_domain" opensips-2.2.2/scripts/opensipsctl.sqlite000066400000000000000000000017721300170765700206470ustar00rootroot00000000000000# # $Id$ # # opensips control tool for maintaining opensips # #=================================================================== ##### ----------------------------------------------- ##### ### SQLITE specific variables and functions # ##### ----------------------------------------------- ##### ### load SQL base # if [ -f "$MYLIBDIR/opensipsctl.sqlbase" ]; then . "$MYLIBDIR/opensipsctl.sqlbase" else echo "Cannot load SQL core functions '$MYLIBDIR/opensipsctl.sqlbase' - exiting ..." exit -1 fi ##### ----------------------------------------------- ##### ### binaries if [ -z "$SQLITE" ] ; then locate_tool sqlite3 if [ -z "$TOOLPATH" ] ; then echo "error: 'sqlite' tool not found: set SQLITE variable to correct tool path" exit fi SQLITE="$TOOLPATH" fi # input: sql query, optional pgsql command-line params sqlite_query() { # if password not yet queried, query it now mecho "sqlite_query: $SQLITE $DB_PATH '$1'" $SQLITE $DB_PATH "$1" } DBCMD=sqlite_query DBRAWPARAMS="" opensips-2.2.2/scripts/opensipsctl.unixsock000066400000000000000000000047771300170765700212210ustar00rootroot00000000000000# # $Id: opensipsctl.unixsock 3954 2008-03-27 19:15:18Z osas $ # # sc: opensips control; tool for maintaining opensips # #=================================================================== ##### ----------------------------------------------- ##### ### UNIXSOCK specific variables and functions # ##### ----------------------------------------------- ##### ### load CTL base # if [ -f "$MYLIBDIR/opensipsctl.ctlbase" ]; then . "$MYLIBDIR/opensipsctl.ctlbase" else mwarn "Cannot load CTL core functions '$MYLIBDIR/opensipsctl.ctlbase' ..." # exit -1 fi # ##### ----------------------------------------------- ##### ### parameters # export CHROOT_DIR # needed for opensipsunix if [ -z "$OSIPS_UNIXSOCK" ]; then OSIPS_UNIXSOCK=$CHROOT_DIR/tmp/opensips.sock fi if [ -z "$OSIPSUNIX" ]; then OSIPSUNIX=opensipsunix fi # ##### ----------------------------------------------- ##### ### functions # usage_unixsock() { echo mecho " -- command 'unixsock'" echo cat < { if [ $# -ne 2 ] ; then merr "opensips_dump function takes two param" exit 1 fi if [ "$USED_DBENGINE" = "oracle" ]; then oracle_dump $1 $2 elif [ "$USED_DBENGINE" = "sqlite" ]; then sql_query $1 $DUMP_CMD > $2 elif [ "$PW" = "" ] ; then $DUMP_CMD $1 > $2 else $DUMP_CMD "-p$PW" $1 > $2 fi if [ "$?" -ne 0 ]; then merr "db dump failed" exit 1 fi minfo "db dump successful" } opensips_restore() #pars: { if [ $# -ne 2 ] ; then merr "opensips_restore function takes two params" exit 1 fi if [ "$USED_DBENGINE" = "oracle" ]; then oracle_restore $1 $2 elif [ "$USED_DBENGINE" = "sqlite" ]; then if [ -f $1 ] ; then get_answer ask "Sqlite database exists? Remove current db (y/n): " if [ "$ANSWER" = "y" ]; then rm $1 else minfo "Kept old database" exit 0 fi fi sql_query $1 ".read $2" else sql_query $1 < $2 fi if [ "$?" -ne 0 ]; then merr "db restore failed" exit 1 fi minfo "db restore successful" } opensips_pframework_create() #pars: none { if [ -e $DEFAULT_CFG_DIR/pi_framework_sample ] ; then get_answer ask "Sample already exists. Overwrite? (y/n): " if [ "$ANSWER" != "y" ]; then exit 1 fi fi touch $DEFAULT_CFG_DIR/pi_framework_sample if [ $? -ne 0 ] ; then merr "Unable to create $DEFAULT_CFG_DIR/pi_framework_sample" exit 1 fi if [ -d "$DATA_DIR/pi_http" ] ; then PI_MODULES="$STANDARD_MODULES" else merr "Please install first the pi_http module" exit 1 fi oet_answer $INSTALL_EXTRA_TABLES "Add provisionning framework for extra tables? (y/n): " if [ "$ANSWER" = "y" ]; then PI_MODULES="$PI_MODULES $EXTRA_MODULES" fi get_answer $INSTALL_PRESENCE_TABLES "Add provisionning framework for presence tables? (y/n): " if [ "$ANSWER" = "y" ]; then PI_PRESENCE_MODULES="TRUE" fi cat $DATA_DIR/pi_http/pi_framework-00 > $DEFAULT_CFG_DIR/pi_framework_sample for TABLE in $PI_MODULES; do if [ -e $DATA_DIR/pi_http/$TABLE-table ]; then cat $DATA_DIR/pi_http/$TABLE-table >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: $TABLE - missing table descriptor" fi done if [ "$PI_PRESENCE_MODULES" = "TRUE" ]; then if [ -e $DATA_DIR/pi_http/presence-table ]; then cat $DATA_DIR/pi_http/presence-table >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: presence - missing table descriptor" fi if [ -e $DATA_DIR/pi_http/rls-table ]; then cat $DATA_DIR/pi_http/rls-table >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: rls - missing table descriptor" fi fi cat $DATA_DIR/pi_http/pi_framework-01 >> $DEFAULT_CFG_DIR/pi_framework_sample for TABLE in $PI_MODULES; do if [ -e $DATA_DIR/pi_http/$TABLE-mod ]; then cat $DATA_DIR/pi_http/$TABLE-mod >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: $TABLE - missing mod descriptor" fi done if [ "$PI_PRESENCE_MODULES" = "TRUE" ]; then if [ -e $DATA_DIR/pi_http/presence-mod ]; then cat $DATA_DIR/pi_http/presence-mod >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: presence - missing mod descriptor" fi if [ -e $DATA_DIR/pi_http/rls-mod ]; then cat $DATA_DIR/pi_http/rls-mod >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: rls - missing mod descriptor" fi fi cat $DATA_DIR/pi_http/pi_framework-02 >> $DEFAULT_CFG_DIR/pi_framework_sample minfo "Sample provisionning framework saved as: $DEFAULT_CFG_DIR/pi_framework_sample" } opensips_pframework() #pars: { if [ $# -ne 1 ] ; then merr "opensips_pframework function takes one parameter" exit 1 fi case $1 in create) shift opensips_pframework_create "$@" exit $? ;; *) merr "Unexpected pframework action: $1" usage exit 1 ;; esac } case $1 in migrate) if [ "$USED_DBENGINE" != "mysql" ] ; then merr "The \"$1\" operation is not supported for $USED_DBENGINE (MYSQL only)" exit 1 fi minfo "MySQL DB migration tool for OpenSIPS 2.1.x databases\n\ ----------------------------------------------------------------------" mwarn "\ We recommend using this tool ONLY in order to upgrade an existing\n\ OpenSIPS 2.1.x MySQL database to the 2.2 schema. Behaviour when automatically\n\ migrating earlier DB versions (1.8, 1.9, 1.10, 1.11) to 2.2 is undefined" read -p"Do you wish to continue? (y/N) " ANS [ "$ANS" != "y" -a "$ANS" != "Y" ] && exit 1 if [ $# -ne 3 ] ; then merr "\"migrate\" command requires exactly 2 params: and " exit 1 fi # create new database minfo "Creating new database \"$3\" ..." NO_USER_INIT="yes" opensips_create $3 if [ "$?" -ne 0 ] ; then echo "migrate: creating new database failed" exit 1 fi # migrate data minfo "Migrating data from \"$2\" to \"$3\" ..." migrate_db $2 $3 minfo "Migration successfully completed." exit 0; ;; copy) # copy database to some other name if [ "$USED_DBENGINE" = "berkeley" -o "$USED_DBENGINE" = "dbtext" ] ; then merr "$USED_DBENGINE don't support this operation" exit 1 fi # sqlite has backup function which creats a copy of the db if [ "$USED_DBENGINE" = "sqlite" ] ; then if [ -f $2 ] ; then get_answer ask "File $2 exists? Remove file and install new db instead? (y/n): " if [ "$ANSWER" = "y" ]; then rm $2 else minfo "Kept old $2 file" exit 0 fi fi sql_query $DBNAME ".backup $2" minfo "Copy succesfully completed" exit $? fi shift if [ $# -ne 1 ]; then usage exit 1 fi tmp_file=`mktemp /tmp/opensipsdbctl.XXXXXXXXXX` || exit 1 opensips_dump $DBNAME $tmp_file ret=$? if [ "$ret" -ne 0 ]; then rm $tmp_file exit $ret fi NO_USER_INIT="yes" opensips_create $1 ret=$? if [ "$ret" -ne 0 ]; then rm $tmp_file exit $ret fi opensips_restore $1 $tmp_file ret=$? rm -f $tmp_file exit $ret ;; backup) if [ "$USED_DBENGINE" = "berkeley" -o "$USED_DBENGINE" = "dbtext" ] ; then merr "$USED_DBENGINE don't support this operation" exit 1 fi # backup current database shift if [ $# -ne 1 ]; then usage exit 1 fi opensips_dump $DBNAME $1 exit $? ;; restore) if [ "$USED_DBENGINE" = "berkeley" -o "$USED_DBENGINE" = "dbtext" ] ; then merr "$USED_DBENGINE don't support this operation" exit 1 fi # restore database from a backup shift if [ $# -ne 1 ]; then usage exit 1 fi opensips_restore $DBNAME $1 exit $? ;; create) # create new database structures shift if [ $# -eq 1 ] ; then DBNAME="$1" fi opensips_create $DBNAME exit $? ;; presence) presence_create $DBNAME exit $? ;; extra) extra_create $DBNAME exit $? ;; drop) # delete opensips database # create new database structures shift if [ $# -eq 1 ] ; then DBNAME="$1" fi opensips_drop $DBNAME exit $? ;; reinit) # delete database and create a new one # create new database structures shift if [ $# -eq 1 ] ; then DBNAME="$1" fi opensips_drop $DBNAME ret=$? if [ "$ret" -ne 0 ]; then exit $ret fi opensips_create $DBNAME exit $? ;; bdb|db_berkeley) shift opensips_berkeley "$@" exit $? ;; pframework) shift opensips_pframework "$@" exit $? ;; version) echo "$0 $VERSION" ;; *) merr "$0: Unknown command - \"$1\"" usage exit 1; ;; esac opensips-2.2.2/scripts/opensipsdbctl.base000066400000000000000000000136131300170765700205630ustar00rootroot00000000000000PATH=$PATH:/usr/local/sbin # config vars # name of the database to be used by OpenSIPS DBNAME=${DBNAME:-opensips} # address of database server DBHOST=${DBHOST:-localhost} # port of database server DBPORT=${DBPORT} # user with full privileges over DBNAME database DBRWUSER=${DBRWUSER:-opensips} # password user with full privileges over DBNAME database DBRWPW=${DBRWPW:-opensipsrw} # user name column USERCOL=${USERCOL:-username} # Describe what additional tables to install. Valid values for the variables # below are yes/no/ask. With ask it will interactively ask the user for the # answer, while yes/no allow for automated, unassisted installs. INSTALL_EXTRA_TABLES=${INSTALL_EXTRA_TABLES:-ask} INSTALL_PRESENCE_TABLES=${INSTALL_PRESENCE_TABLES:-ask} # Used by dbtext and db_berkeley to define tables to be created, used by # postgres to do the grants STANDARD_TABLES=${STANDARD_TABLES:-version acc dbaliases domain grp uri speed_dial subscriber location re_grp address missed_calls usr_preferences aliases silo dialog dispatcher dialplan dr_gateways dr_rules dr_groups dr_carriers dr_partitions rtpproxy_sockets load_balancer clusterer tls_mgm } EXTRA_TABLES=${EXTRA_TABLES:-imc_members imc_rooms cpl sip_trace domainpolicy carrierroute route_tree carrierfailureroute userblacklist globalblacklist b2b_entities b2b_logic cachedb registrant cc_flows cc_agents cc_cdrs cc_calls fraud_detection emergency_routing emergency_report} PRESENCE_TABLES=${PRESENCE_TABLES:-presentity active_watchers watchers xcap pua rls_presentity rls_watchers} # SQL definitions # If you change this definitions here, then you must change them # in db/schema/entities.xml too. # FIXME FOREVER=${FOREVER:-2020-05-28 21:32:15} DEFAULT_ALIASES_EXPIRES=${DEFAULT_ALIASES_EXPIRES:-${FOREVER}} DEFAULT_Q=${DEFAULT_Q:-1.0} DEFAULT_CALLID=${DEFAULT_CALLID:-Default-Call-ID} DEFAULT_CSEQ=${DEFAULT_CSEQ:-13} DEFAULT_LOCATION_EXPIRES=${DEFAULT_LOCATION_EXPIRES:-${FOREVER}} # default location for config files DEFAULT_CFG_DIR=/usr/local/etc/opensips # default location for data files DEFAULT_DATA_DIR=/usr/local/share/opensips # Needed programs MD5=${MD5:-md5sum} AWK=${AWK:-awk} GREP=${GREP:-grep} SED=${SED:-sed} # define what modules should be installed STANDARD_MODULES=${STANDARD_MODULES:-standard acc domain group permissions registrar usrloc msilo alias_db uri_db rtpproxy speeddial avpops auth_db dialog dispatcher dialplan drouting load_balancer clusterer tls_mgm } EXTRA_MODULES=${EXTRA_MODULES:-imc cpl siptrace domainpolicy carrierroute userblacklist b2b cachedb_sql registrant call_center fraud_detection emergency} ############################################################ # common functions usage() { COMMAND=`basename $0` cat < ...(creates a new database) $COMMAND drop .....(!entirely deletes tables!) $COMMAND reinit ...(!entirely deletes and then re-creates tables!) $COMMAND backup ...........................(dumps current database to file) $COMMAND restore ..........................(restores tables from a file) $COMMAND copy ...........................(creates a new db from an existing one) $COMMAND migrate ...............(migrates DB from 2.1.x to 2.2.x) $COMMAND presence ................................(adds the presence related tables) $COMMAND extra ...................................(adds the extra tables) if you want to manipulate database as other database user than root, want to change database name from default value "$DBNAME", or want to use other values for users and password, edit the "config vars" section of the command $COMMAND. $COMMAND pframework create .......................(creates a sample provisioning framework file) EOF } #usage # read realm prompt_realm() { printf "Domain (realm) for the default user 'admin': " read SIP_DOMAIN echo } # calculate credentials for admin credentials() { HA1=`echo -n "admin:$SIP_DOMAIN:$DBRWPW" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then merr "HA1 calculation failed" exit 1 fi HA1B=`echo -n "admin@$SIP_DOMAIN:$SIP_DOMAIN:$DBRWPW" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then merr "HA1B calculation failed" exit 1 fi #PHPLIB_ID of users should be difficulty to guess for security reasons NOW=`date`; PHPLIB_ID=`echo -n "$RANDOM:$NOW:$SIP_DOMAIN" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then merr "PHPLIB_ID calculation failed" exit 1 fi } # FIXME use the definition from opensipsctl.base mdbg() { if [ "0$VERBOSE" -ne 0 ] ; then if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e "\033[1m$1\033[0m" else echo "$1" fi fi } mwarn() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;33m'"\033[1mWARNING: $1\033[0m" else echo "** WARNING: $1" fi } minfo() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then if [ ! -z "$2" ]; then echo -e '\E[37;'$2'm'"\033[1mINFO: $1\033[0m" else echo -e '\E[37;32m'"\033[1mINFO: $1\033[0m" fi else echo "** INFO: $1" fi } mecho() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e "\033[1m$1\033[0m" else echo "$1" fi } merr() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;31m'"\033[1mERROR: $1\033[0m" else echo "** ERROR: $1" fi } # Get a y/n value from a variable. If the variable contains the keyword # `ask', then interactively ask the user for an answer. # # Arguments: # $1 - variable holding yes/no/ask # $2 - question to print if value of variable is ask # # Return: # On return $ANSWER will be available with y/n # get_answer () { value=$1 question=$2 if [ "${value}" = "ask" ]; then echo -n "$question" read ANSWER else ANSWER=${value} fi ANSWER="`echo $ANSWER | sed 's/^\(.\).*/\1/g'`" ANSWER="`echo $ANSWER | sed 's/Y/y/g'`" ANSWER="`echo $ANSWER | sed 's/N/n/g'`" } opensips-2.2.2/scripts/opensipsdbctl.db_berkeley000066400000000000000000000350231300170765700221170ustar00rootroot00000000000000# # $Id$ # # Script for maintaining OpenSIPS Berkeley DB tables # Copyright (C) 2007 Cisco Systems # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # History: # -------- # 2007-09-19 genesis (wiquan) # #constants PATH=$PATH:/usr/local/BerkeleyDB.4.6/bin DELIM="|" BACKUP_CMD="tar czvf " RESTORE_CMD="tar xzvf " #berkeley db utility program that writes out db to plain text #small hack to autodetect the db dump command, debian prefix the version.. which db_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db_dump" fi ; which db4.4_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db4.4_dump" fi ; which db4.5_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db4.5_dump" fi ; which db4.6_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db4.6_dump" fi ; which db4.7_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db4.7_dump" fi ; which db4.8_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db4.8_dump" fi ; #berkeley db utility program that imports data from plain text file #small hack to autodetect the db load command, debian prefix the version.. which db_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db_load" fi ; which db4.4_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db4.4_load" fi ; which db4.5_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db4.5_load" fi ; which db4.6_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db4.6_load" fi ; which db4.7_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db4.7_load" fi ; which db4.8_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db4.8_load" fi ; # path to the database schemas DATA_DIR="/usr/local/share/opensips" if [ -d "$DATA_DIR/db_berkeley/opensips" ]; then DB_SCHEMA="$DATA_DIR/db_berkeley/opensips" elif [ -d "scripts/db_berkeley/opensips" ]; then DB_SCHEMA="scripts/db_berkeley/opensips" else DB_SCHEMA="./db_berkeley/opensips" fi # path to the db_berkeley database if [ -z "$DB_PATH" ]; then DB_PATH="/usr/local/etc/opensips/db_berkeley" fi berkeley_usage() { COMMAND=`basename $0` cat < (db_dump the underlying db file to STDOUT) $COMMAND swap (installs db.new by db -> db.old; db.new -> db) $COMMAND append (appends data to an existing db;output DB_PATH/db.new) $COMMAND newappend (appends data to a new instance of db; output DB_PATH/db.new) $COMMAND export (exports table data to plain-txt files in dump_dir) $COMMAND import (imports plain-txt table data and creates new db tables in db_path) EOF } #usage # # # opensips_berkeley() # parms: { case $1 in list|ls) ls -l $DB_PATH exit $? ;; cat) shift opensips_cat $1 $DB_PATH exit $? ;; swap) shift opensips_swap $1 $DB_PATH exit $? ;; append) shift opensips_append $1 $2 $DB_PATH exit $? ;; newappend) shift opensips_newappend $1 $2 $DB_PATH exit $? ;; export) shift opensips_export $1 $DB_PATH exit $? ;; migrate) shift opensips_migrate $1 $DB_PATH exit $? ;; import) shift opensips_import $1 $DB_PATH exit $? ;; *) berkeley_usage exit 1; ;; esac } ## # EXPORT existing data to plain-txt files in DUMP_DIR # eg. DB_PATH/version ---> DUMP_DIR/version.txt # # Export is used as part of a DB migration process to another # major version of berkeley db. opensips_export() # parms: [DB_PATH] { if [ $# -lt 2 ]; then echo "opensips_dump parms: [DB_PATH]" exit 1 fi # Assert: the DB_PATH directory should already exist if [ ! -d $2 ] ; then merr "BerkeleyDB directory does not exist at: [$2]" exit 1 fi # Assert: DB_PATH directory should already contain table 'version' if [ ! -f $2/version ] ; then merr "BerkeleyDB directory does not have VERSION table at: [$2]" exit 1 fi # Create dir at to store the exported data if [ ! -d $1 ] ; then minfo "creating DUMP_DIR at: [$1]" mkdir -p $1 else mdbg "Cleaning out DUMP_DIR to get ready for new data" rm -rf $1/* fi # DUMP_CMD will result in something like this: # # VERSION=3 # format=print # type=hash # h_nelem=2 # db_pagesize=4096 # HEADER=END # METADATA_COLUMNS # callid(str) method(str) from_tag(str) to_tag(str) sip_code(str) sip_reason(str) time(datetime) # METADATA_KEY # 0 # DATA=END # # However, we are only interested in the indented stuff between # 'HEADER=END' and 'DATA=END', # as everything else is DB instance specific. That is, we are interested in this part: # # METADATA_COLUMNS # callid(str) method(str) from_tag(str) to_tag(str) sip_code(str) sip_reason(str) time(datetime) # METADATA_KEY # 0 # # The following PERL filter will do this processing. # # perl -pe 's/^\w.*// ; s/^\s(.*)/$1/' # Dump the STANDARD tables to plain-text files in DUMP_DIR for TABLE in $STANDARD_TABLES; do if [ -f $2/$TABLE ] ; then mdbg "Exporting standard table: $TABLE" $DUMP_CMD -p -h $2 $TABLE | perl -pe 's/^\w.*// ; s/^\s(.*)/$1/' > $1/$TABLE.txt # Check return code to make sure the export worked ok if [ $? -ne 0 ] ; then merr "Export of standard table failed [$TABLE]" # there was a problem, but it is not something # we can handle here; We can deal with this at import # time. fi else mwarn "Table not found: [$TABLE]" fi done # Dump the PRESENCE tables to plain-text files in DUMP_DIR for TABLE in $PRESENCE_TABLES; do if [ -f $2/$TABLE ] ; then mdbg "Exporting presence table: $TABLE" $DUMP_CMD -p -h $2 $TABLE | perl -pe 's/^\w.*// ; s/^\s(.*)/$1/' > $1/$TABLE.txt if [ $? -ne 0 ] ; then merr "Export of presence table failed [$TABLE]" fi else mwarn "Table not found: [$TABLE]" fi done # Dump the EXTRA tables to plain-text files in DUMP_DIR for TABLE in $EXTRA_TABLES; do if [ -f $2/$TABLE ] ; then mdbg "Exporting extra table: $TABLE" $DUMP_CMD -p -h $2 $TABLE | perl -pe 's/^\w.*// ; s/^\s(.*)/$1/' > $1/$TABLE.txt if [ $? -ne 0 ] ; then merr "Export of extra table failed [$TABLE]" fi else mwarn "Table not found: [$TABLE]" fi done mdbg "All tables are now exported to DUMP_DIR: [$1]" return 0 } ## # MIGRATE (schema) # Examine each plain-txt file in DUMP_DIR # (Assumes that opensips_export was already invoked) # # Migrate converts data from schema-old to schema-new in place. # # After this step is complete the IMPORT should be executed. opensips_migrate() # parms: [DB_PATH] { merr "db_berkeley migrate not implemented" exit 1 } ## # IMPORT existing plain-txt files from DUMP_DIR to DB_PATH # eg. DUMP_DIR/version.txt --> DB_PATH/version # # import is used as part of DB migrate to another major version of berkeley db. # this will over-write anything in DB_PATH opensips_import() # parms: [DB_PATH] { if [ $# -lt 2 ]; then echo "opensips_dump parms: [DB_PATH]" exit 1 fi # Assert: DUMP_DIR (source dir) already exists if [ ! -d $1 ] ; then merr "Berkeley DUMP_DIR directory does not exist: [$1]" exit 1; fi # Assert: DUMP_DIR directory should already contain table 'version.txt' if [ ! -f $1/version.txt ] ; then merr "DUMP_DIR directory does not have VERSION.txt data at: [$1]" exit 1 fi # Assert: destination dir exists [DB_PATH] if [ ! -d $2 ] ; then mdbg "Berkeley DB_PATH directory is being created: [$2]" mkdir -p $2 else # Wipe out the destination dir to make room for new data mwarn "Berkeley DB_PATH directory is being purged at: [$2]" rm -rf $2/* fi # Creates STANDARD tables from plain-text files in DUMP_DIR for TABLE in $STANDARD_TABLES; do if [ -s $1/$TABLE.txt ] ; then mdbg "Importing standard table: $TABLE" $LOAD_CMD -T -t hash -f $1/$TABLE.txt -h $2 $TABLE # Check return code to make sure the export worked ok if [ $? -ne 0 ] ; then merr "Import of standard table failed [$TABLE.txt]" merr "Create this missing table with bdb_recover." fi else merr "Import data not found for table: [$TABLE.txt]" merr "Create this missing table with bdb_recover." fi done # Creates PRESENCE tables from plain-text files in DUMP_DIR for TABLE in $PRESENCE_TABLES; do if [ -s $1/$TABLE.txt ] ; then mdbg "Importing presence table: $TABLE" $LOAD_CMD -T -t hash -f $1/$TABLE.txt -h $2 $TABLE # Check return code to make sure the export worked ok if [ $? -ne 0 ] ; then merr "Import of presence table failed [$TABLE.txt]" merr "Create this missing table with bdb_recover." fi else mwarn "Import data not found for table: [$TABLE.txt]" fi done # Creates EXTRA tables from plain-text files in DUMP_DIR for TABLE in $EXTRA_TABLES; do if [ -s $1/$TABLE.txt ] ; then mdbg "Importing extra table: $TABLE" $LOAD_CMD -T -t hash -f $1/$TABLE.txt -h $2 $TABLE # Check return code to make sure the export worked ok if [ $? -ne 0 ] ; then merr "Import of extra table failed [$TABLE.txt]" merr "Create this missing table with bdb_recover." fi else mwarn "Import data not found for table: [$TABLE.txt]" fi done mdbg "All tables are now imported to DB_PATH: [$2]" return 0 } opensips_swap() # parms: [DB_PATH] { if [ $# -lt 2 ]; then echo "opensips_swap parms: [DB_PATH]" exit 1 fi DB=$2/$1 DBNEW=$DB.new DBOLD=$DB.old cp $DB $DBOLD mv $DBNEW $DB } ##### # append process is: # 1. copy DB_PATH/db to DB_PATH/db.new # 2. appends contents of newdata to DB_PATH/db.new # opensips_append() # parms: [DB_PATH] { if [ $# -lt 3 ]; then echo "opensips_append parms: [DB_PATH]" exit 1 fi DB=$3/$1 DBNEW=$DB.new if [ -f $DB.new ] ; then rm $DB.new fi cp $DB $DBNEW # echo "$LOAD_CMD -T -t hash -f $2 -h $3 $1.new" $LOAD_CMD -T -t hash -f $2 -h $3 $1.new # echo "$LOAD_CMD -r fileid -h $3 $1.new" $LOAD_CMD -r fileid -h $3 $1.new } ##### # newappend process is: # 1. create a new temp DBENV in /tmp/sc- # 2. appends contents of newdata to /tmp/sc-/db # 3. move /tmp/sc-/db over to DB_PATH/db.new # 4. delete temp DBENV dir /tmp/sc- # opensips_newappend() # parms: [DB_PATH] { if [ $# -lt 3 ]; then echo "opensips_append parms: [DB_PATH]" exit 1 fi DB=$3/$1 DBNEW=$DB.new if [ -f $DBNEW ] ; then rm $DBNEW fi TMPENV=/tmp/sc-$$ opensips_create $TMPENV cd $OLDPWD $LOAD_CMD -T -t hash -f $2 -h $TMPENV $1 mv $TMPENV/$1 $DBNEW rm -rf $TMPENV } # cat all rows to STDOUT opensips_cat() # pars: { if [ $# -ne 2 ] ; then echo "opensips_cat params [DB_PATH]" exit 1 fi $DUMP_CMD -p -h $2 $1 } opensips_drop() # pars: { if [ $# -ne 1 ] ; then echo "opensips_drop function takes one param" exit 1 fi if [ ! -d $1 ] ; then echo "Directory does not exist: $1" fi minfo "Dropping Berkeley DB database at: $1 ..." # core if [ -f $1/version ] ; then for TABLE in $STANDARD_TABLES; do mdbg "Dropping core table: $TABLE" rm -f $1/$TABLE done fi # presence if [ -f $1/presentity ] ; then for TABLE in $PRESENCE_TABLES; do mdbg "Dropping presence table: $TABLE" rm -f $1/$TABLE done fi # extra tables if [ -f $1/cpl ] ; then for TABLE in $EXTRA_TABLES; do mdbg "Dropping extra table: $TABLE" rm -f $1/$TABLE done fi # delete db files and directory rm -rf $1/__db.001 rm -rf $1/__db.002 rm -rf $1/__db.003 rm -rf $1/__db.004 rmdir $1 } opensips_create() # pars: { if [ $# -ne 1 ] ; then echo "opensips_create param [DB_PATH]" exit 1 fi DB_PATH=$1 if [ ! -d $1 ] ; then minfo "creating Berkeley DB database at: [$1]" mkdir -p $DB_PATH fi for TABLE in $STANDARD_TABLES; do mdbg "Creating standard table: $TABLE" $LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $1 $TABLE if [ $? -ne 0 ] ; then merr "Creating standard tables failed!" exit 1 fi done get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ $ANSWER = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ $ANSWER = "y" ]; then extra_create $1 fi } # opensips_create presence_create() # pars: { if [ $# -ne 1 ] ; then merr "presence_create param [DB_PATH]" exit 1 fi DB_PATH=$1 if [ ! -d $1 ] ; then # Assert: the directory should already exist merr "BerkeleyDB directory does not exist at: [$1]" exit 1 fi if [ ! -f $1/version ] ; then # Assert: directory should already contain table 'version' merr "BerkeleyDB directory does not have VERSION table at: [$1]" exit 1 fi for TABLE in $PRESENCE_TABLES; do mdbg "Creating presence table: $TABLE" $LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $1 $TABLE if [ $? -ne 0 ] ; then merr "Creating presence tables failed!" exit 1 fi done } # end presence_create extra_create() # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param (DB_PATH)" exit 1 fi DB_PATH=$1 if [ ! -d $1 ] ; then # Assert: the directory should already exist merr "BerkeleyDB directory does not exist at: [$1]" exit 1 fi if [ ! -f $1/version ] ; then # Assert: directory should already contain table 'version' merr "BerkeleyDB directory does not have VERSION table at: [$1]" exit 1 fi for TABLE in $EXTRA_TABLES; do mdbg "Creating extra table: $TABLE" $LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $1 $TABLE if [ $? -ne 0 ] ; then merr "Creating extra tables failed!" exit 1 fi done } # end extra_create opensips-2.2.2/scripts/opensipsdbctl.dbtext000066400000000000000000000047271300170765700211510ustar00rootroot00000000000000# $Id$ # # Script for adding and dropping OpenSIPS DBTEXT tables # # History: # 2007-02-14 Branch from mysqldb.sh script and adapt minimal capabilities(Cesc Santasusana) # # 2007-05-31 Move common definitions to opensipsdbctl.base file (henningw) # 2007-06-13 Move database definitions out of this script, use a common # control tool for database tasks, like the opensipsctl (henning) # path to the database schemas DATA_DIR="/usr/local/share/opensips" if [ -d "$DATA_DIR/dbtext/opensips" ]; then DB_SCHEMA="$DATA_DIR/dbtext/opensips" elif [ -d "scripts/dbtext/opensips" ]; then DB_SCHEMA="scripts/dbtext/opensips" else DB_SCHEMA="./dbtext/opensips" fi # path to the dbtext database if [ -z "$DB_PATH" ] ; then DB_PATH="/usr/local/etc/opensips/dbtext" fi opensips_drop() # pars: { if [ $# -ne 1 ] ; then merr "opensips_drop function takes one param" exit 1 fi DB_PATH=$1 minfo "DBTEXT ... erasing all files at: $DB_PATH" rm -rf $DB_PATH } opensips_create () # pars: { if [ $# -ne 1 ] ; then merr "opensips_create function takes one param (DB_PATH)" exit 1 fi DB_PATH=$1 minfo "creating DBTEXT tables at: $DB_PATH ..." mkdir -p $DB_PATH for TABLE in $STANDARD_TABLES; do mdbg "Creating core table: $TABLE" cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE if [ $? -ne 0 ] ; then merr "Creating core tables failed!" exit 1 fi done get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ $ANSWER = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ $ANSWER = "y" ]; then extra_create $1 fi } presence_create () # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param (DB_PATH)" exit 1 fi DB_PATH=$1 minfo "creating DBTEXT presence tables at: $DB_PATH ..." mkdir -p $DB_PATH for TABLE in $PRESENCE_TABLES; do mdbg "Creating presence table: $TABLE" cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE if [ $? -ne 0 ] ; then merr "Creating presence tables failed!" exit 1 fi done } # end presence_create extra_create () # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating DBTEXT extra tables at: $DB_PATH ..." for TABLE in $EXTRA_TABLES; do mdbg "Creating extra table: $TABLE" cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE if [ $? -ne 0 ] ; then merr "Creating extra tables failed!" exit 1 fi done } # end extra_create opensips-2.2.2/scripts/opensipsdbctl.mysql000066400000000000000000000346001300170765700210150ustar00rootroot00000000000000# $Id$ # # Script for adding and dropping OpenSIPS MySQL tables # # History: # 2006-04-07 removed gen_ha1 dependency - use md5sum; # separated the serweb from opensips tables; # fixed the reinstall functionality (bogdan) # 2006-05-16 added ability to specify MD5 from a configuration file # FreeBSD does not have the md5sum function (norm) # 2006-09-02 Added address table (juhe) # 2006-10-27 subscriber table cleanup; some columns are created only if # serweb is installed (bogdan) # 2007-02-28 DB migration added (bogdan) # 2007-05-21 Move SQL database definitions out of this script (henning) # 2007-05-31 Move common definitions to opensipsdbctl.base file (henningw) # 2007-06-11 Use a common control tool for database tasks, like the opensipsctl # path to the database schemas DATA_DIR="/usr/local/share/opensips" if [ -d "$DATA_DIR/mysql" ]; then DB_SCHEMA="$DATA_DIR/mysql" elif [ -d "scripts/mysql" ]; then DB_SCHEMA="scripts/mysql" else DB_SCHEMA="./mysql" fi ################################################################# # config vars ################################################################# # full privileges MySQL user if [ -z "$DBROOTUSER" ]; then DBROOTUSER="root" fi # Uncomment this to set the database root password if you want to run this # script without any user prompt. This is unsafe, but useful e.g. for # automatic testing. #PW="" if ! [ -z "$DBPORT" ]; then PORT_OPT="-P$DBPORT" else PORT_OPT= fi CMD="mysql -h $DBHOST $PORT_OPT -u$DBROOTUSER " DUMP_CMD="mysqldump -h $DBHOST $PORT_OPT -u$DBROOTUSER -c -t " ################################################################# # read password prompt_pw() { savetty=`stty -g` echo -n "MySQL password for $DBROOTUSER: " stty -echo read PW stty $savetty echo export PW } # execute sql command with optional db name # and password parameters given sql_query() { if [ $# -gt 1 ] ; then if [ -n "$1" ]; then DB="$1" # no quoting, mysql client don't like this else DB="" fi shift if [ -n "$PW" ]; then $CMD "-p$PW" $DB -Bse "$@" else $CMD $DB -Bse "$@" fi else if [ -n "$PW" ]; then $CMD "-p$PW" -Bs "$@" else $CMD -Bs "$@" fi fi } opensips_drop() # pars: { if [ $# -ne 1 ] ; then merr "opensips_drop function takes two params" exit 1 fi sql_query "" "drop database $1;" if [ $? -ne 0 ] ; then merr "Dropping database $1 failed!" exit 1 fi minfo "Database $1 deleted" } db_charset_test() { if [ -n "$PW" ]; then CURRCHARSET=`echo "show variables like '%character_set_server%'" | $CMD "-p$PW" | $AWK '{print $2}' | $SED -e 1d` ALLCHARSETS=`echo "show character set" | $CMD "-p$PW" | $AWK '{print $1}' | $SED -e 1d | $GREP -iv "utf8" | $GREP -iv "ucs2"` else CURRCHARSET=`echo "show variables like '%character_set_server%'" | $CMD | $AWK '{print $2}' | $SED -e 1d` ALLCHARSETS=`echo "show character set" | $CMD | $AWK '{print $1}' | $SED -e 1d | $GREP -iv "utf8" | $GREP -iv "ucs2"` fi if [ -z "$ALLCHARSETS" -o -z "$CURRCHARSET" ]; then mwarn "Failed to get the available and used character sets" exit 1; fi tries=0; while [ `echo "$ALLCHARSETS" | $GREP -icw $CURRCHARSET` = "0" ] do mwarn "Your current default mysql characters set cannot be used to create DB. Please choice another one from the following list:" mecho "$ALLCHARSETS" let tries=tries+1; mecho "Enter character set name: " read CURRCHARSET if [ $tries -ge 3 ] then merr "Check your current charset" merr "Exiting..." exit 1; fi done CHARSET=$CURRCHARSET } opensips_create () # pars: { if [ $# -ne 1 ] ; then merr "opensips_create function takes one param" exit 1 fi minfo "test server charset" db_charset_test minfo "creating database $1 ..." # Users: opensips is the regular user, opensipsro only for reading sql_query "" "create database $1 character set $CHARSET;" if [ $? -ne 0 ] ; then merr "Creating database $1 failed!" exit 1 fi if [ "$DBROOTUSER" != "$DBRWUSER" ] ; then sql_query "" " GRANT ALL PRIVILEGES ON $1.* TO '$DBRWUSER' IDENTIFIED BY '$DBRWPW'; GRANT ALL PRIVILEGES ON $1.* TO '${DBRWUSER}'@'$DBHOST' IDENTIFIED BY '$DBRWPW'; " fi if [ $? -ne 0 ] ; then merr "Creating core database and grant privileges failed!" exit 1 fi if [ "$MYSQL_ENGINE" == "" ] ; then MYSQL_ENGINE="InnoDB" fi minfo "Using table engine $MYSQL_ENGINE." for TABLE in $STANDARD_MODULES; do mdbg "Creating core table: $TABLE" $SED "s/ENGINE=[A-Za-z]\+/ENGINE=$MYSQL_ENGINE/g" $DB_SCHEMA/$TABLE-create.sql | sql_query $1 if [ $? -ne 0 ] ; then merr "Creating core tables failed!" exit 1 fi done minfo "Core OpenSIPS tables successfully created." if [ -e $DB_SCHEMA/extensions-create.sql ] then minfo "Creating custom extensions tables" sql_query $1 < $DB_SCHEMA/extensions-create.sql if [ $? -ne 0 ] ; then merr "Creating custom extensions tables failed!" exit 1 fi fi get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ "$ANSWER" = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ "$ANSWER" = "y" ]; then HAS_EXTRA="yes" extra_create $1 fi } # opensips_create presence_create () # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param" exit 1 fi minfo "creating presence tables into $1 ..." sql_query $1 < $DB_SCHEMA/presence-create.sql if [ $? -ne 0 ] ; then merr "Failed to create presence tables!" exit 1 fi sql_query $1 < $DB_SCHEMA/rls-create.sql if [ $? -ne 0 ] ; then merr "Failed to create rls-presence tables!" exit 1 fi minfo "Presence tables successfully created." } # end presence_create extra_create () # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating extra tables into $1 ..." for TABLE in $EXTRA_MODULES; do mdbg "Creating extra table: $TABLE" sql_query $1 < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating extra tables failed!" exit 1 fi done minfo "Extra tables successfully created." } # end extra_create migrate_table () # 4 paremeters (dst_table, dst_cols, src_table, src_cols) { if [ $# -ne 4 ] ; then echo "param1=$1 param2=$2 param3=$3 param4=$4" merr "migrate_table function takes 4 params $@" exit 1 fi src_cols=`echo $4 | sed s/?/$3./g ` X=`sql_query "" "INSERT into $1 ($2) SELECT $src_cols from $3;" 2>&1` if [ $? -ne 0 ] ; then echo $X | $GREP "ERROR 1146" > /dev/null if [ $? -eq 0 ] ; then echo " -- Migrating $3 to $1.....SKIPPED (no source)" return 0 fi echo "ERROR: failed to migrate $3 to $1 ($X)!!!" echo -n "Skip it and continue (y/n)? " read INPUT if [ "$INPUT" = "y" ] || [ "$INPUT" = "Y" ] then return 0 fi exit 1; fi minfo " -- Migrating $3 to $1.....OK" } migrate_db () # 2 parameters (src_db, dst_db) { if [ $# -ne 2 ] ; then merr "migrate_db function takes 2 params" exit 1 fi dst_db=$2 src_db=$1 ## ACC module migrate_table ${dst_db}.acc \ "id,method,from_tag,to_tag,callid,sip_code,sip_reason,time,duration,setuptime,created" \ ${src_db}.acc \ "?id,?method,?from_tag,?to_tag,?callid,?sip_code,?sip_reason,?time,?duration,?setuptime,?created" migrate_table ${dst_db}.missed_calls \ "id,method,from_tag,to_tag,callid,sip_code,sip_reason,time" \ ${src_db}.missed_calls \ "?id,?method,?from_tag,?to_tag,?callid,?sip_code,?sip_reason,?time" ## ALIAS_DB module migrate_table ${dst_db}.dbaliases \ "id,alias_username,alias_domain,username,domain" \ ${src_db}.dbaliases \ "?id,?alias_username,?alias_domain,?username,?domain" ## AUTH_DB module migrate_table ${dst_db}.subscriber \ "id,username,domain,password,email_address,ha1,ha1b,rpid" \ ${src_db}.subscriber \ "?id,?username,?domain,?password,?email_address,?ha1,?ha1b,?rpid" ## AVPOPS module migrate_table ${dst_db}.usr_preferences \ "id,uuid,username,domain,attribute,type,value,last_modified" \ ${src_db}.usr_preferences \ "?id,?uuid,?username,?domain,?attribute,?type,?value,?last_modified" ## B2B modules # runtime data, nothing to migrate ## CACHEDB_SQL module migrate_table ${dst_db}.cachedb \ "keyname,value,counter,expires" \ ${src_db}.cachedb \ "?keyname,?value,?counter,?expires" ## DIALOG modules # runtime data, nothing to migrate ## DIALPLAN module migrate_table ${dst_db}.dialplan \ "id,dpid,pr,match_op,match_exp,match_flags,subst_exp,repl_exp,disabled,attrs" \ ${src_db}.dialplan \ "?id,?dpid,?pr,?match_op,?match_exp,?match_flags,?subst_exp,?repl_exp,?disabled,?attrs" ## DISPATCHER module (changes here, the flags and state columns) migrate_table ${dst_db}.dispatcher \ "id,setid,destination,weight,attrs,description" \ ${src_db}.dispatcher \ "?id,?setid,?destination,?weight,?attrs,?description" ## DOMAIN module migrate_table ${dst_db}.domain \ "id,domain,last_modified" \ ${src_db}.domain \ "?id,?domain,?last_modified" ## DROUTING module migrate_table ${dst_db}.dr_gateways \ "id,gwid,type,address,strip,pri_prefix,attrs,probe_mode,state,socket,description" \ ${src_db}.dr_gateways \ "?id,?gwid,?type,?address,?strip,?pri_prefix,?attrs,?probe_mode,?state,?socket,?description" migrate_table ${dst_db}.dr_rules \ "ruleid,groupid,prefix,timerec,priority,routeid,gwlist,attrs,description" \ ${src_db}.dr_rules \ "?ruleid,?groupid,?prefix,?timerec,?priority,?routeid,?gwlist,?attrs,?description" migrate_table ${dst_db}.dr_groups \ "id,username,domain,groupid,description" \ ${src_db}.dr_groups \ "?id,?username,?domain,?groupid,?description" migrate_table ${dst_db}.dr_carriers \ "id,carrierid,gwlist,flags,attrs,description" \ ${src_db}.dr_carriers \ "?id,?carrierid,?gwlist,?flags,?attrs,?description" ## GROUP module migrate_table ${dst_db}.grp \ "id,username,domain,grp,last_modified" \ ${src_db}.grp \ "?id,?username,?domain,?grp,?last_modified" migrate_table ${dst_db}.re_grp \ "id,reg_exp,group_id" \ ${src_db}.re_grp \ "?id,?reg_exp,?group_id" ## LOAD_BALANCER module migrate_table ${dst_db}.load_balancer \ "id,group_id,dst_uri,resources,probe_mode,description" \ ${src_db}.load_balancer \ "?id,?group_id,?dst_uri,?resources,?probe_mode,?description" \ ## MSILO module migrate_table ${dst_db}.silo \ "id,src_addr,dst_addr,username,domain,inc_time,exp_time,snd_time,ctype,body" \ ${src_db}.silo \ "?id,?src_addr,?dst_addr,?username,?domain,?inc_time,?exp_time,?snd_time,?ctype,?body" ## PERMISSION module migrate_table ${dst_db}.address \ "id,grp,ip,mask,port,proto,pattern,context_info" \ ${src_db}.address \ "?id,?grp,?ip,?mask,?port,?proto,?pattern,?context_info" ## REGISTRAR module migrate_table ${dst_db}.aliases \ "id,username,domain,contact,received,path,expires,q,callid,cseq,last_modified,flags,cflags,user_agent,socket,methods,sip_instance,attr" \ ${src_db}.aliases \ "?id,?username,?domain,?contact,?received,?path,?expires,?q,?callid,?cseq,?last_modified,?flags,?cflags,?user_agent,?socket,?methods,?sip_instance,?attr" ## RTPPROXY module migrate_table ${dst_db}.rtpproxy_sockets \ "id,rtpproxy_sock,set_id" \ ${src_db}.rtpproxy_sockets \ "?id,?rtpproxy_sock,?set_id" ## SPEEDDIAL module migrate_table ${dst_db}.speed_dial \ "id,username,domain,sd_username,sd_domain,new_uri,fname,lname,description" \ ${src_db}.speed_dial \ "?id,?username,?domain,?sd_username,?sd_domain,?new_uri,?fname,?lname,?description" ## URI_DB module migrate_table ${dst_db}.uri \ "id,username,domain,uri_user,last_modified" \ ${src_db}.uri \ "?id,?username,?domain,?uri_user,?last_modified" if [ "$HAS_EXTRA" = "yes" ] ; then ## CARRIERROUTE module migrate_table ${dst_db}.carrierroute \ "id,carrier,domain,scan_prefix,flags,mask,prob,strip,rewrite_host,rewrite_prefix,rewrite_suffix,description" \ ${src_db}.carrierroute \ "?id,?carrier,?domain,?scan_prefix,?flags,?mask,?prob,?strip,?rewrite_host,?rewrite_prefix,?rewrite_suffix,?description" migrate_table ${dst_db}.carrierfailureroute \ "id,carrier,domain,scan_prefix,host_name,reply_code,flags,mask,next_domain,description" \ ${src_db}.carrierfailureroute \ "?id,?carrier,?domain,?scan_prefix,?host_name,?reply_code,?flags,?mask,?next_domain,?description" migrate_table ${dst_db}.route_tree \ "id,carrier" \ ${src_db}.route_tree \ "?id,?carrier" ## CALL_CENTER module migrate_table ${dst_db}.cc_flows \ "flowid,priority,skill,prependcid,message_welcome,message_queue" \ ${src_db}.cc_flows \ "?flowid,?priority,?skill,?prependcid,?message_welcome,?message_queue" migrate_table ${dst_db}.cc_agents \ "agentid,location,logstate,skills,last_call_end" \ ${src_db}.cc_agents \ "?agentid,?location,?logstate,?skills,?last_call_end" migrate_table ${dst_db}.cc_cdrs \ "caller,received_timestamp,wait_time,pickup_time,talk_time,flow_id,agent_id,call_type,rejected,fstats,cid" \ ${src_db}.cc_cdrs \ "?caller,?received_timestamp,?wait_time,?pickup_time,?talk_time,?flow_id,?agent_id,?call_type,?rejected,?fstats,?cid" ## CPL module migrate_table ${dst_db}.cpl \ "id,username,domain,cpl_xml,cpl_bin" \ ${src_db}.cpl \ "?id,?username,?domain,?cpl_xml,?cpl_bin" ## DOMAINPOLICY module migrate_table ${dst_db}.domainpolicy \ "id,rule,type,att,val,description" \ ${src_db}.domainpolicy \ "?id,?rule,?type,?att,?val,?description" ## IMC module migrate_table ${dst_db}.imc_rooms \ "id,name,domain,flag" \ ${src_db}.imc_rooms \ "?id,?name,?domain,?flag" migrate_table ${dst_db}.imc_members \ "id,username,domain,room,flag" \ ${src_db}.imc_members \ "?id,?username,?domain,?room,?flag" ## PRESENCE modules # runtime data only, nothing to migrate ## REGISTRANT module migrate_table ${dst_db}.registrant \ "id,registrar,proxy,aor,third_party_registrant,username,password,binding_URI,binding_params,expiry,forced_socket" \ ${src_db}.registrant \ "?id,?registrar,?proxy,?aor,?third_party_registrant,?username,?password,?binding_URI,?binding_params,?expiry,?forced_socket" ## SIPTRACE module migrate_table ${dst_db}.sip_trace \ "id,time_stamp,callid,trace_attrs,msg,method,status,from_proto,from_ip,from_port,to_proto,to_ip,to_port,fromtag,direction" \ ${src_db}.sip_trace \ "?id,?time_stamp,?callid,?traced_user,?msg,?method,?status,?from_proto,?from_ip,?from_port,?to_proto,?to_ip,?to_port,?fromtag,?direction" ## USERBLACKLIST module migrate_table ${dst_db}.userblacklist \ "id,username,domain,prefix,whitelist" \ ${src_db}.userblacklist \ "?id,?username,?domain,?prefix,?whitelist" migrate_table ${dst_db}.globalblacklist \ "id,prefix,whitelist,description" \ ${src_db}.globalblacklist \ "?id,?prefix,?whitelist,?description" fi } #end migrate_db() export PW if [ "$#" -ne 0 ] && [ "$PW" = "" ]; then prompt_pw fi opensips-2.2.2/scripts/opensipsdbctl.oracle000066400000000000000000000166211300170765700211200ustar00rootroot00000000000000#!/bin/sh # $Id$ # # Script for adding and dropping OpenSIPS Oracle tables # # History: ##In you not have 'AS SYSDBA' access to database connect, comment next string ##and Oracle administrator must create DBROOTUSER ##(see scripts/oracle/admin/_create_as_sys.tmpl) DBSYSUSER="sys" # path to the database schemas DATA_DIR="/usr/local/share/opensips" if [ -d "$DATA_DIR/oracle" ]; then DB_SCHEMA="$DATA_DIR/oracle" elif [ -d "scripts/oracle" ]; then DB_SCHEMA="scripts/oracle" else DB_SCHEMA="./oracle" fi ##### ----------------------------------------------- ##### ### load ORACLE SQL base # if [ -f "$MYLIBDIR/opensipsdbfunc.oracle" ]; then . "$MYLIBDIR/opensipsdbfunc.oracle" else echo "Cannot load ORACLE core functions '$MYLIBDIR/opensipsdbfunc.oracle' - exiting ..." exit -1 fi if [ -z "$SQLPLUS" ] ; then SQLPLUS=`which sqlplus 2>/dev/null` if [ -z "$SQLPLUS" ]; then merr "'sqlplus' tool not found: set SQLPLUS variable to correct tool path" exit 1 fi export SQLPLUS fi if [ -z "$OPENSIPS_ORASEL" ] ; then OPENSIPS_ORASEL=`which opensips_orasel 2>/dev/null` if [ -n "$SQLPLUS" ]; then export OPENSIPS_ORASEL fi fi ################################################################# ORALOG=/tmp/opensrdbctl.log if [ -z "$SED" ]; then SED="sed" fi SED_ROOTUSER="$SED -e s/%DBROOTUSER%/$DBROOTUSER/g" SED_USERS="$SED_ROOTUSER;s/%DBRWUSER%/$DBRWUSER/g" ################################################################# # config vars ################################################################# # 'db'-privileges (scheme) Oracle user if [ -z "$DBROOTUSER" ]; then merr "scheme owner (pivileged user) 'DBROOTUSER' must be defined." exit 1 fi ################################################################# oracle_root_cmd() { prompt_oracle_pw root SUFF="" if [ -n "$1" ]; then SUFF="@$1" fi ORACLE_ROOT_CMD="$SQLPLUS -S -L -R 3 $DBROOTUSER/${DBROOTPW}$SUFF" export ORACLE_ROOT_CMD } opensips_drop() # pars: { if [ $# -ne 1 ] ; then merr "opensips_drop function takes one params" exit 1 fi oracle_root_cmd $1 echo "DROP USER $DBRWUSER CASCADE;" | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then mwarn "Could not drop $DBRWUSER, try to continue.." else minfo "Database user deleted" fi $SED_ROOTUSER $DB_SCHEMA/inc/_dropsch.tmpl | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Dropping scheme for '$DBROOTUSER' in database '$1' failed!" exit 1 fi if [ -n "$DBSYSUSER" ]; then get_answer "ask" "Remove user '$DBROOTUSER' (complete remove scheme)? (y/n): " if [ $ANSWER = "y" ]; then prompt_oracle_pw sys SUFF="" if [ -n "$1" ]; then SUFF="@$1" fi SYSCMD="$SQLPLUS -S -L -R 3 $DBSYSUSER/${DBSYSPW}$SUFF AS SYSDBA" echo "DROP USER $DBROOTUSER CASCADE;" | $SYSCMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Dropping scheme in database '$1' failed!" exit 1 fi fi fi minfo "Scheme '$DBROOTUSER' in database '$1' dropped" } #opensips_drop opensips_create() # pars: { if [ $# -ne 1 ] ; then merr "opensips_create function takes one param" exit 1 fi minfo "creating scheme for '$DBROOTUSER' in database '$1' ..." if [ -n "$DBSYSUSER" ]; then get_answer "ask" "Create user '$DBROOTUSER' (is new scheme)? (y/n): " if [ $ANSWER = "y" ]; then prompt_oracle_pw sys prompt_oracle_pw root SUFF="" if [ -n "$1" ]; then SUFF="@$1" fi SYSCMD="$SQLPLUS -S -L -R 3 $DBSYSUSER/${DBSYSPW}$SUFF AS SYSDBA" echo "create user $DBROOTUSER identified by $DBROOTPW default tablespace DATA temporary tablespace TEMP profile DEFAULT;" | $SYSCMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then mwarn "Create privileged user in database failed, perhaps they allready exist? Try to continue.." fi $SED_ROOTUSER $DB_SCHEMA/inc/_grantroot.tmpl | $SYSCMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating scheme in database '$1' failed!" exit 1 fi fi fi oracle_root_cmd $1 $SED_ROOTUSER $DB_SCHEMA/inc/_createsch.tmpl | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating scheme for '$DBROOTUSER' in database '$1' failed!" exit 1 fi cat $DB_SCHEMA/inc/_create_compat.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating compatibility functions for '$DBROOTUSER' in database '$1' failed!" exit 1 fi prompt_oracle_pw rw echo "create user $DBRWUSER identified by $DBRWPW default tablespace DATA temporary tablespace TEMP profile DEFAULT; grant connect to $DBRWUSER;" | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then mwarn "Create user in database scheme failed, perhaps they allready exist? Try to continue.." fi $SED_USERS $DB_SCHEMA/inc/_grantfunc.tmpl | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Make compatibility functions for users failed!" exit 1 fi for TABLE in $STANDARD_MODULES; do mdbg "Creating core table: $TABLE" cat $DB_SCHEMA/$TABLE-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating core tables failed!" exit 1 fi done if [ -e $DB_SCHEMA/extensions-create.sql ]; then minfo "Creating custom extensions tables" cat $DB_SCHEMA/extensions-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating custom extensions tables failed!" exit 1 fi fi minfo "Core OpenSIPS tables successfully created." get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ $ANSWER = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ $ANSWER = "y" ]; then extra_create $1 fi } # opensips_create presence_create() # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param" exit 1 fi minfo "creating presence tables..." oracle_root_cmd $1 cat $DB_SCHEMA/presence-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Failed to create presence tables!" exit 1 fi cat $DB_SCHEMA/rls-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Failed to create rls-presence tables!" exit 1 fi minfo "Presence tables successfully created." } extra_create() # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating extra tables..." oracle_root_cmd $1 for TABLE in $EXTRA_MODULES; do mdbg "Creating extra table: $TABLE" cat $DB_SCHEMA/$TABLE-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating extra tables failed!" exit 1 fi done minfo "Extra tables successfully created." } oracle_dump() { if [ $# -ne 2 ] ; then merr "oracle_dump function takes two params" exit 1 fi prompt_oracle_pw rw if [ -n "$1" ]; then SUFF="@$1" fi if [ -n "$OPENSIPS_ORASEL" ]; then $OPENSIPS_ORASEL ${DBRWUSER}/${DBRWPW}$SUFF -BLNe \ "select * from table(dump_tables('$DBROOTUSER'));" >$2 else/ echo "set feed 0 lin 8000 pages 0 select * from table(dump_tables('$DBROOTUSER'));" | \ $SQLPLUS -S -L -R 3 ${DBRWUSER}/${DBRWPW}$SUFF >$2 fi return $? } oracle_restore() { if [ $# -ne 2 ] ; then merr "oracle_restore function takes two params" exit 1 fi prompt_oracle_pw rw SUFF="" if [ -n "$1" ]; then SUFF="@$1" fi cat "$2" | $SQLPLUS -S -L -R 3 ${DBRWUSER}/${DBRWPW}$SUFF >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then return 1 fi return 0 } opensips-2.2.2/scripts/opensipsdbctl.pgsql000066400000000000000000000164201300170765700207760ustar00rootroot00000000000000# $Id$ # # Script for adding and dropping OpenSIPS Postgres tables # # History: # 2006-05-16 added ability to specify MD5 from a configuration file # FreeBSD does not have the md5sum function (norm) # 2006-07-14 Corrected syntax from MySQL to Postgres (norm) # moved INDEX creation out of CREATE table statement into # CREATE INDEX (usr_preferences, trusted) # auto_increment isn't valid in Postgres, replaced with # local AUTO_INCREMENT # datetime isn't valid in Postgres, replaced with local DATETIME # split GRANTs for SERWeb tables so that it is only executed # if SERWeb tables are created # added GRANTs for re_grp table # corrected comments to indicate Postgres as opposed to MySQL # made last_modified/created stamps consistent to now() using # local TIMESTAMP # 2006-10-19 Added address table (bogdan) # 2006-10-27 subscriber table cleanup; some columns are created only if # serweb is installed (bogdan) # 2007-01-26 added seperate installation routine for presence related tables # and fix permissions for the SERIAL sequences. # 2007-05-21 Move SQL database definitions out of this script (henning) # 2007-05-31 Move common definitions to opensipsdbctl.base file (henningw) # # 2007-06-11 Use a common control tool for database tasks, like the opensipsctl # path to the database schemas DATA_DIR="/usr/local/share/opensips" if [ -d "$DATA_DIR/postgres" ]; then DB_SCHEMA="$DATA_DIR/postgres" elif [ -d "scripts/postgres" ]; then DB_SCHEMA="scripts/postgres" else DB_SCHEMA="./postgres" fi ################################################################# # config vars ################################################################# # full privileges Postgres user if [ -z "$DBROOTUSER" ]; then DBROOTUSER="postgres" if [ ! -r ~/.pgpass ]; then merr "~/.pgpass does not exist, please create this file and support proper credentials for user postgres." merr "Note: you need at least postgresql>= 7.3" exit 1 fi fi if ! [ -z "$DBPORT" ]; then PORT_OPT="-p$DBPORT" else PORT_OPT= fi CMD="psql -q -h $DBHOST $PORT_OPT -U $DBROOTUSER " DUMP_CMD="pg_dump -h $DBHOST $PORT_OPT -U $DBROOTUSER -c" ################################################################# # read password and export PGPASSWORD prompt_pw() { savetty=`stty -g` echo -n "PGSQL password for $DBROOTUSER: " stty -echo read PGPASSWORD stty $savetty echo export PGPASSWORD } # execute sql command with optional db name sql_query() { if [ $# -gt 1 ] ; then if [ -n "$1" ]; then DB="$1" else DB="" fi shift $CMD -d $DB -c "$@" else $CMD "$@" fi } opensips_drop() # pars: { if [ $# -ne 1 ] ; then merr "opensips_drop function takes two params" exit 1 fi sql_query "template1" "drop database \"$1\";" if [ $? -ne 0 ] ; then merr "Dropping database $1 failed!" exit 1 fi # postgresql users are not dropped automatically sql_query "template1" "drop user \"$DBRWUSER\";" if [ $? -ne 0 ] ; then mwarn "Could not drop $DBRWUSER user, try to continue.." else minfo "Database user deleted" fi minfo "Database $1 dropped" } #opensips_drop opensips_create () # pars: { if [ $# -ne 1 ] ; then merr "opensips_create function takes one param" exit 1 fi minfo "creating database $1 ..." sql_query "template1" "create database \"$1\";" if [ $? -ne 0 ] ; then merr "Creating database failed!" exit 1 fi sql_query "$1" "CREATE FUNCTION "concat" (text,text) RETURNS text AS 'SELECT \$1 || \$2;' LANGUAGE 'sql'; CREATE FUNCTION "rand" () RETURNS double precision AS 'SELECT random();' LANGUAGE 'sql';" if [ $? -ne 0 ] ; then merr "Creating mysql emulation functions failed!" exit 1 fi for TABLE in $STANDARD_MODULES; do mdbg "Creating core table: $TABLE" sql_query "$1" < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating core tables failed!" exit 1 fi done sql_query "$1" "CREATE USER $DBRWUSER WITH PASSWORD '$DBRWPW';" if [ $? -ne 0 ] ; then mwarn "Create user in database failed, perhaps they already exist? Try to continue.." fi for TABLE in $STANDARD_TABLES; do sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE $TABLE TO $DBRWUSER;" if [ $TABLE != "version" ] ; then mdbg "creating table: $TABLE" if [ $TABLE = "dr_rules" ] then mdbg "creating table 2: $TABLE" sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE "$TABLE"_ruleid_seq TO $DBRWUSER;" elif [ $TABLE = "dialog" ] then mdbg "creating table 2: $TABLE" # the dialog table doesn't have an auto increment key #sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE "$TABLE"_dlg_id_seq TO $DBRWUSER;" else mdbg "creating table 3: $TABLE" sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE "$TABLE"_id_seq TO $DBRWUSER;" fi mdbg "created table: $TABLE" fi if [ $? -ne 0 ] ; then merr "Grant privileges to standard tables failed!" exit 1 fi done if [ -e $DB_SCHEMA/extensions-create.sql ] then minfo "Creating custom extensions tables" sql_query $1 < $DB_SCHEMA/extensions-create.sql if [ $? -ne 0 ] ; then merr "Creating custom extensions tables failed!" exit 1 fi fi minfo "Core OpenSIPS tables successfully created." get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ $ANSWER = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ $ANSWER = "y" ]; then extra_create $1 fi } # opensips_create presence_create () # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param" exit 1 fi minfo "creating presence tables into $1 ..." sql_query "$1" < $DB_SCHEMA/presence-create.sql if [ $? -ne 0 ] ; then merr "Failed to create presence tables!" exit 1 fi sql_query "$1" < $DB_SCHEMA/rls-create.sql if [ $? -ne 0 ] ; then merr "Failed to create rls-presence tables!" exit 1 fi for TABLE in $PRESENCE_TABLES; do sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE $TABLE TO $DBRWUSER;" sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE "$TABLE"_id_seq TO $DBRWUSER;" if [ $? -ne 0 ] ; then merr "Grant privileges to presence tables failed!" exit 1 fi done minfo "Presence tables successfully created." } # end presence_create extra_create () # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating extra tables into $1 ..." for TABLE in $EXTRA_MODULES; do mdbg "Creating extra table: $TABLE" sql_query "$1" < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating extra tables failed!" exit 1 fi done for TABLE in $EXTRA_TABLES; do sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE $TABLE TO $DBRWUSER;" if [ $TABLE != "route_tree" ] && [ $TABLE != "cachedb" ] ; then if [ $TABLE == "fraud_detection" ] ; then sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE "$TABLE"_ruleid_seq TO $DBRWUSER;" else sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE "$TABLE"_id_seq TO $DBRWUSER;" fi fi if [ $? -ne 0 ] ; then merr "Grant privileges to extra tables failed!" exit 1 fi done minfo "Extra tables successfully created." } # end extra_create export PGPASSWORD if [ "$#" -ne 0 ] && [ "$PGPASSWORD" = "" ]; then prompt_pw fi opensips-2.2.2/scripts/opensipsdbctl.sqlite000066400000000000000000000120431300170765700211460ustar00rootroot00000000000000# $Id$ # # Script for adding and dropping OpenSIPS SQLITE tables # # History: # 2006-04-07 removed gen_ha1 dependency - use md5sum; # separated the serweb from opensips tables; # fixed the reinstall functionality (bogdan) # 2006-05-16 added ability to specify MD5 from a configuration file # FreeBSD does not have the md5sum function (norm) # 2006-09-02 Added address table (juhe) # 2006-10-27 subscriber table cleanup; some columns are created only if # serweb is installed (bogdan) # 2007-02-28 DB migration added (bogdan) # 2007-05-21 Move SQL database definitions out of this script (henning) # 2007-05-31 Move common definitions to opensipsdbctl.base file (henningw) # 2007-06-11 Use a common control tool for database tasks, like the opensipsctl # path to the database schemas DATA_DIR="/usr/local/share/opensips" if [ -d "$DATA_DIR/sqlite" ]; then DB_SCHEMA="$DATA_DIR/sqlite" elif [ -d "scripts/mysql" ]; then DB_SCHEMA="scripts/sqlite" else DB_SCHEMA="./sqlite" fi ################################################################# # config vars ################################################################# # path to the db_sqlite database if [ -z "$DB_PATH" ]; then DB_PATH="/usr/local/etc/opensips/sqlite" fi CMD="sqlite3" DUMP_CMD=".dump" ################################################################# # read password prompt_pw() { echo "SQLITE does not use password" } # execute sql command with optional db name # and password parameters given sql_query() { if [ $# -gt 1 ] ; then if [ -n "$1" ]; then DB="$1" else DB="" fi shift $CMD $DB "$@" else merr "sqlite3 requires db name" fi } opensips_drop() # pars: { if [ $# -ne 1 ] ; then merr "opensips_drop function takes two params" exit 1 fi #sql_query "" "drop database $1;" rm $1 if [ $? -ne 0 ] ; then merr "Dropping database $1 failed!" exit 1 fi minfo "Database $1 deleted" } db_charset_test() { # if [ -n "$PW" ]; then # CURRCHARSET=`echo "show variables like '%character_set_server%'" | $CMD | $AWK '{print $2}' | $SED -e 1d` # ALLCHARSETS=`echo "show character set" | $CMD "-p$PW" | $AWK '{print $1}' | $SED -e 1d | $GREP -iv "utf8" | $GREP -iv "ucs2"` # else CURRCHARSET=`echo "show variables like '%character_set_server%'" | $CMD | $AWK '{print $2}' | $SED -e 1d` ALLCHARSETS=`echo "show character set" | $CMD | $AWK '{print $1}' | $SED -e 1d | $GREP -iv "utf8" | $GREP -iv "ucs2"` # fi if [ -z "$ALLCHARSETS" -o -z "$CURRCHARSET" ]; then mwarn "Failed to get the available and used character sets" exit 1; fi tries=0; while [ `echo "$ALLCHARSETS" | $GREP -icw $CURRCHARSET` = "0" ] do mwarn "Your current default mysql characters set cannot be used to create DB. Please choice another one from the following list:" mecho "$ALLCHARSETS" let tries=tries+1; mecho "Enter character set name: " read CURRCHARSET if [ $tries -ge 3 ] then merr "Check your current charset" merr "Exiting..." exit 1; fi done CHARSET=$CURRCHARSET } opensips_create () # pars: { if [ $# -ne 1 ] ; then merr "opensips_create function takes one param" exit 1 fi #minfo "test server charset" #db_charset_test minfo "creating database $1 ..." # Users: opensips is the regular user, opensipsro only for reading #fi for TABLE in $STANDARD_MODULES; do mdbg "Creating core table: $TABLE" sql_query $1 ".read $DB_SCHEMA/$TABLE-create.sql" if [ $? -ne 0 ] ; then merr "Creating core tables failed!" exit 1 fi done minfo "Core OpenSIPS tables succesfully created." if [ -e $DB_SCHEMA/extensions-create.sql ] then minfo "Creating custom extensions tables" sql_query $1 ".read $DB_SCHEMA/extensions-create.sql" if [ $? -ne 0 ] ; then merr "Creating custom extensions tables failed!" exit 1 fi fi get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ "$ANSWER" = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ "$ANSWER" = "y" ]; then HAS_EXTRA="yes" extra_create $1 fi } # opensips_create presence_create () # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param" exit 1 fi minfo "creating presence tables into $1 ..." sql_query $1 ".read $DB_SCHEMA/presence-create.sql" if [ $? -ne 0 ] ; then merr "Failed to create presence tables!" exit 1 fi sql_query $1 ".read $DB_SCHEMA/rls-create.sql" if [ $? -ne 0 ] ; then merr "Failed to create rls-presence tables!" exit 1 fi minfo "Presence tables succesfully created." } # end presence_create extra_create () # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating extra tables into $1 ..." for TABLE in $EXTRA_MODULES; do mdbg "Creating extra table: $TABLE" sql_query $1 ".read $DB_SCHEMA/$TABLE-create.sql" if [ $? -ne 0 ] ; then merr "Creating extra tables failed!" exit 1 fi done minfo "Extra tables succesfully created." } # end extra_create #export PW #if [ "$#" -ne 0 ] && [ "$PW" = "" ]; then # prompt_pw #fi opensips-2.2.2/scripts/opensipsdbfunc.oracle000066400000000000000000000020761300170765700212700ustar00rootroot00000000000000#!/bin/sh # $Id$ # # Script for common functions for Oracle engine in opensips # # History: if [ -z "$EGREP" ]; then EGREP="egrep" fi # read any password prompt_oracle_pw() { case $1 in rw) if [ -n "$DBRWPW" ]; then return fi CURDBUSER="$DBRWUSER" ;; root) if [ -n "$DBROOTPW" ]; then return fi CURDBUSER="$DBROOTUSER" ;; sys) if [ -n "$DBSYSPW" ]; then return fi CURDBUSER="$DBSYSUSER" ;; *) merr "prompt_oracle_pw: argument error" exit 1 ;; esac savetty=`stty -g` echo -n "Oracle password for $CURDBUSER: " stty -echo case $1 in rw) read DBRWPW export DBRWPW CURPW=$DBRWPW ;; root) read DBROOTPW export DBROOTPW CURPW=$DBROOTPW ;; sys) read DBSYSPW export DBSYSPW CURPW=$DBSYSPW ;; esac stty $savetty echo if [ -z "$CURPW" ]; then merr "empty password is illegal" exit 1 fi } check_oracle_log() { if [ -f $ORALOG ]; then $EGREP -qi "error" $ORALOG if [ $? -eq 0 ]; then echo "NOTE: last errors stored in $ORALOG" return 0 fi rm $ORALOG fi return 1 } opensips-2.2.2/scripts/oracle/000077500000000000000000000000001300170765700163175ustar00rootroot00000000000000opensips-2.2.2/scripts/oracle/README.TYPES000066400000000000000000000001101300170765700200720ustar00rootroot00000000000000BITMASK => NUMBER(11) INTEGER => NUMBER(<=10) BLOB => VARCHAR2(4000) opensips-2.2.2/scripts/oracle/acc-create.sql000066400000000000000000000025731300170765700210360ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('acc','7'); CREATE TABLE acc ( id NUMBER(10) PRIMARY KEY, method VARCHAR2(16) DEFAULT '', from_tag VARCHAR2(64) DEFAULT '', to_tag VARCHAR2(64) DEFAULT '', callid VARCHAR2(64) DEFAULT '', sip_code VARCHAR2(3) DEFAULT '', sip_reason VARCHAR2(32) DEFAULT '', time DATE, duration NUMBER(10) DEFAULT 0 NOT NULL, ms_duration NUMBER(10) DEFAULT 0 NOT NULL, setuptime NUMBER(10) DEFAULT 0 NOT NULL, created DATE DEFAULT NULL ); CREATE OR REPLACE TRIGGER acc_tr before insert on acc FOR EACH ROW BEGIN auto_id(:NEW.id); END acc_tr; / BEGIN map2users('acc'); END; / CREATE INDEX acc_callid_idx ON acc (callid); INSERT INTO version (table_name, table_version) values ('missed_calls','5'); CREATE TABLE missed_calls ( id NUMBER(10) PRIMARY KEY, method VARCHAR2(16) DEFAULT '', from_tag VARCHAR2(64) DEFAULT '', to_tag VARCHAR2(64) DEFAULT '', callid VARCHAR2(64) DEFAULT '', sip_code VARCHAR2(3) DEFAULT '', sip_reason VARCHAR2(32) DEFAULT '', time DATE, setuptime NUMBER(10) DEFAULT 0 NOT NULL, created DATE DEFAULT NULL ); CREATE OR REPLACE TRIGGER missed_calls_tr before insert on missed_calls FOR EACH ROW BEGIN auto_id(:NEW.id); END missed_calls_tr; / BEGIN map2users('missed_calls'); END; / CREATE INDEX missed_calls_callid_idx ON missed_calls (callid); opensips-2.2.2/scripts/oracle/admin/000077500000000000000000000000001300170765700174075ustar00rootroot00000000000000opensips-2.2.2/scripts/oracle/admin/README000066400000000000000000000002211300170765700202620ustar00rootroot00000000000000replace '%DBROOTUSER%' to user (scheme) name and '%DBROOTPW%' to password in _create_as_sys_tmpl, and tell Oracle administrator to execute it :) opensips-2.2.2/scripts/oracle/admin/_create_as_sys.tmpl000066400000000000000000000010011300170765700232600ustar00rootroot00000000000000create user %DBROOTUSER% identified by %DBROOTPW% default tablespace DATA temporary tablespace TEMP profile DEFAULT; -- Grant/Revoke role privileges grant connect to %DBROOTUSER% with admin option; grant resource to %DBROOTUSER%; -- Grant/Revoke system privileges grant create any synonym to %DBROOTUSER%; grant create role to %DBROOTUSER%; grant create user to %DBROOTUSER%; grant drop user to %DBROOTUSER%; grant unlimited tablespace to %DBROOTUSER%; grant select on sys.dba_role_privs to %DBROOTUSER%; opensips-2.2.2/scripts/oracle/admin/_drop_as_sys.tmpl000066400000000000000000000000401300170765700227630ustar00rootroot00000000000000DROP USER %DBROOTUSER% CASCADE; opensips-2.2.2/scripts/oracle/alias_db-create.sql000066400000000000000000000011271300170765700220400ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dbaliases','2'); CREATE TABLE dbaliases ( id NUMBER(10) PRIMARY KEY, alias_username VARCHAR2(64) DEFAULT '', alias_domain VARCHAR2(64) DEFAULT '', username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', CONSTRAINT dbaliases_alias_idx UNIQUE (alias_username, alias_domain) ); CREATE OR REPLACE TRIGGER dbaliases_tr before insert on dbaliases FOR EACH ROW BEGIN auto_id(:NEW.id); END dbaliases_tr; / BEGIN map2users('dbaliases'); END; / CREATE INDEX dbaliases_target_idx ON dbaliases (username, domain); opensips-2.2.2/scripts/oracle/auth_db-create.sql000066400000000000000000000012621300170765700217100ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('subscriber','7'); CREATE TABLE subscriber ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', password VARCHAR2(25) DEFAULT '', email_address VARCHAR2(64) DEFAULT '', ha1 VARCHAR2(64) DEFAULT '', ha1b VARCHAR2(64) DEFAULT '', rpid VARCHAR2(64) DEFAULT NULL, CONSTRAINT subscriber_account_idx UNIQUE (username, domain) ); CREATE OR REPLACE TRIGGER subscriber_tr before insert on subscriber FOR EACH ROW BEGIN auto_id(:NEW.id); END subscriber_tr; / BEGIN map2users('subscriber'); END; / CREATE INDEX subscriber_username_idx ON subscriber (username); opensips-2.2.2/scripts/oracle/avpops-create.sql000066400000000000000000000015621300170765700216150ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('usr_preferences','3'); CREATE TABLE usr_preferences ( id NUMBER(10) PRIMARY KEY, uuid VARCHAR2(64) DEFAULT '', username VARCHAR2(128) DEFAULT 0 NOT NULL, domain VARCHAR2(64) DEFAULT '', attribute VARCHAR2(32) DEFAULT '', type NUMBER(10) DEFAULT 0 NOT NULL, value VARCHAR2(128) DEFAULT '', last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss') ); CREATE OR REPLACE TRIGGER usr_preferences_tr before insert on usr_preferences FOR EACH ROW BEGIN auto_id(:NEW.id); END usr_preferences_tr; / BEGIN map2users('usr_preferences'); END; / CREATE INDEX usr_preferences_ua_idx ON usr_preferences (uuid, attribute); CREATE INDEX usr_preferences_uda_idx ON usr_preferences (username, domain, attribute); CREATE INDEX usr_preferences_value_idx ON usr_preferences (value); opensips-2.2.2/scripts/oracle/b2b-create.sql000066400000000000000000000041011300170765700207420ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('b2b_entities','1'); CREATE TABLE b2b_entities ( id NUMBER(10) PRIMARY KEY, type NUMBER(10), state NUMBER(10), ruri VARCHAR2(128), from_uri VARCHAR2(128), to_uri VARCHAR2(128), from_dname VARCHAR2(64), to_dname VARCHAR2(64), tag0 VARCHAR2(64), tag1 VARCHAR2(64), callid VARCHAR2(64), cseq0 NUMBER(10), cseq1 NUMBER(10), contact0 VARCHAR2(128), contact1 VARCHAR2(128), route0 CLOB, route1 CLOB, sockinfo_srv VARCHAR2(64), param VARCHAR2(128), lm NUMBER(10), lrc NUMBER(10), lic NUMBER(10), leg_cseq NUMBER(10), leg_route CLOB, leg_tag VARCHAR2(64), leg_contact VARCHAR2(128), leg_sockinfo VARCHAR2(128), CONSTRAINT b2b_entities_b2b_entities_idx UNIQUE (type, tag0, tag1, callid) ); CREATE OR REPLACE TRIGGER b2b_entities_tr before insert on b2b_entities FOR EACH ROW BEGIN auto_id(:NEW.id); END b2b_entities_tr; / BEGIN map2users('b2b_entities'); END; / CREATE INDEX ORA_b2b_entities_param ON b2b_entities (param); INSERT INTO version (table_name, table_version) values ('b2b_logic','3'); CREATE TABLE b2b_logic ( id NUMBER(10) PRIMARY KEY, si_key VARCHAR2(64), scenario VARCHAR2(64), sstate NUMBER(10), next_sstate NUMBER(10), sparam0 VARCHAR2(64), sparam1 VARCHAR2(64), sparam2 VARCHAR2(64), sparam3 VARCHAR2(64), sparam4 VARCHAR2(64), sdp CLOB(64), lifetime NUMBER(10) DEFAULT 0 NOT NULL, e1_type NUMBER(10), e1_sid VARCHAR2(64), e1_from VARCHAR2(128), e1_to VARCHAR2(128), e1_key VARCHAR2(64), e2_type NUMBER(10), e2_sid VARCHAR2(64), e2_from VARCHAR2(128), e2_to VARCHAR2(128), e2_key VARCHAR2(64), e3_type NUMBER(10), e3_sid VARCHAR2(64), e3_from VARCHAR2(128), e3_to VARCHAR2(128), e3_key VARCHAR2(64), CONSTRAINT b2b_logic_b2b_logic_idx UNIQUE (si_key) ); CREATE OR REPLACE TRIGGER b2b_logic_tr before insert on b2b_logic FOR EACH ROW BEGIN auto_id(:NEW.id); END b2b_logic_tr; / BEGIN map2users('b2b_logic'); END; / opensips-2.2.2/scripts/oracle/b2b_sca-create.sql000066400000000000000000000055361300170765700216050ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('b2b_sca','1'); CREATE TABLE b2b_sca ( id NUMBER(10) PRIMARY KEY, shared_line VARCHAR2(64), watchers VARCHAR2(255), app1_shared_entity NUMBER(10) DEFAULT NULL, app1_call_state NUMBER(10) DEFAULT NULL, app1_call_info_uri VARCHAR2(128) DEFAULT NULL, app1_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app1_b2bl_key VARCHAR2(64) DEFAULT NULL, app2_shared_entity NUMBER(10) DEFAULT NULL, app2_call_state NUMBER(10) DEFAULT NULL, app2_call_info_uri VARCHAR2(128) DEFAULT NULL, app2_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app2_b2bl_key VARCHAR2(64) DEFAULT NULL, app3_shared_entity NUMBER(10) DEFAULT NULL, app3_call_state NUMBER(10) DEFAULT NULL, app3_call_info_uri VARCHAR2(128) DEFAULT NULL, app3_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app3_b2bl_key VARCHAR2(64) DEFAULT NULL, app4_shared_entity NUMBER(10) DEFAULT NULL, app4_call_state NUMBER(10) DEFAULT NULL, app4_call_info_uri VARCHAR2(128) DEFAULT NULL, app4_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app4_b2bl_key VARCHAR2(64) DEFAULT NULL, app5_shared_entity NUMBER(10) DEFAULT NULL, app5_call_state NUMBER(10) DEFAULT NULL, app5_call_info_uri VARCHAR2(128) DEFAULT NULL, app5_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app5_b2bl_key VARCHAR2(64) DEFAULT NULL, app6_shared_entity NUMBER(10) DEFAULT NULL, app6_call_state NUMBER(10) DEFAULT NULL, app6_call_info_uri VARCHAR2(128) DEFAULT NULL, app6_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app6_b2bl_key VARCHAR2(64) DEFAULT NULL, app7_shared_entity NUMBER(10) DEFAULT NULL, app7_call_state NUMBER(10) DEFAULT NULL, app7_call_info_uri VARCHAR2(128) DEFAULT NULL, app7_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app7_b2bl_key VARCHAR2(64) DEFAULT NULL, app8_shared_entity NUMBER(10) DEFAULT NULL, app8_call_state NUMBER(10) DEFAULT NULL, app8_call_info_uri VARCHAR2(128) DEFAULT NULL, app8_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app8_b2bl_key VARCHAR2(64) DEFAULT NULL, app9_shared_entity NUMBER(10) DEFAULT NULL, app9_call_state NUMBER(10) DEFAULT NULL, app9_call_info_uri VARCHAR2(128) DEFAULT NULL, app9_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app9_b2bl_key VARCHAR2(64) DEFAULT NULL, app10_shared_entity NUMBER(10) DEFAULT NULL, app10_call_state NUMBER(10) DEFAULT NULL, app10_call_info_uri VARCHAR2(128) DEFAULT NULL, app10_call_info_appearance_uri VARCHAR2(128) DEFAULT NULL, app10_b2bl_key VARCHAR2(64) DEFAULT NULL, CONSTRAINT b2b_sca_sca_idx UNIQUE (shared_line) ); CREATE OR REPLACE TRIGGER b2b_sca_tr before insert on b2b_sca FOR EACH ROW BEGIN auto_id(:NEW.id); END b2b_sca_tr; / BEGIN map2users('b2b_sca'); END; / opensips-2.2.2/scripts/oracle/cachedb_sql-create.sql000066400000000000000000000006161300170765700225340ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cachedb','2'); CREATE TABLE cachedb ( keyname VARCHAR2(255) PRIMARY KEY, value CLOB(512), counter NUMBER(10) DEFAULT 0 NOT NULL, expires NUMBER(10) DEFAULT 0 NOT NULL ); CREATE OR REPLACE TRIGGER cachedb_tr before insert on cachedb FOR EACH ROW BEGIN auto_id(:NEW.id); END cachedb_tr; / BEGIN map2users('cachedb'); END; / opensips-2.2.2/scripts/oracle/call_center-create.sql000066400000000000000000000045711300170765700225630ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cc_flows','1'); CREATE TABLE cc_flows ( id NUMBER(10) PRIMARY KEY, flowid VARCHAR2(64), priority NUMBER(10) DEFAULT 256 NOT NULL, skill VARCHAR2(64), prependcid VARCHAR2(32), message_welcome VARCHAR2(128) DEFAULT NULL, message_queue VARCHAR2(128), CONSTRAINT cc_flows_unique_flowid UNIQUE (flowid) ); CREATE OR REPLACE TRIGGER cc_flows_tr before insert on cc_flows FOR EACH ROW BEGIN auto_id(:NEW.id); END cc_flows_tr; / BEGIN map2users('cc_flows'); END; / INSERT INTO version (table_name, table_version) values ('cc_agents','1'); CREATE TABLE cc_agents ( id NUMBER(10) PRIMARY KEY, agentid VARCHAR2(128), location VARCHAR2(128), logstate NUMBER(10) DEFAULT 0 NOT NULL, skills VARCHAR2(255), last_call_end NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT cc_agents_unique_agentid UNIQUE (agentid) ); CREATE OR REPLACE TRIGGER cc_agents_tr before insert on cc_agents FOR EACH ROW BEGIN auto_id(:NEW.id); END cc_agents_tr; / BEGIN map2users('cc_agents'); END; / INSERT INTO version (table_name, table_version) values ('cc_cdrs','1'); CREATE TABLE cc_cdrs ( id NUMBER(10) PRIMARY KEY, caller VARCHAR2(64), received_timestamp DATE, wait_time NUMBER(10) DEFAULT 0 NOT NULL, pickup_time NUMBER(10) DEFAULT 0 NOT NULL, talk_time NUMBER(10) DEFAULT 0 NOT NULL, flow_id VARCHAR2(128), agent_id VARCHAR2(128) DEFAULT NULL, call_type NUMBER(10) DEFAULT -1 NOT NULL, rejected NUMBER(10) DEFAULT 0 NOT NULL, fstats NUMBER(10) DEFAULT 0 NOT NULL, cid NUMBER(10) DEFAULT 0 ); CREATE OR REPLACE TRIGGER cc_cdrs_tr before insert on cc_cdrs FOR EACH ROW BEGIN auto_id(:NEW.id); END cc_cdrs_tr; / BEGIN map2users('cc_cdrs'); END; / CREATE TABLE cc_calls ( id NUMBER(10) PRIMARY KEY, state NUMBER(10), ig_cback NUMBER(10), no_rej NUMBER(10), setup_time NUMBER(10), eta NUMBER(10), last_start NUMBER(10), recv_time NUMBER(10), caller_dn VARCHAR2(128), caller_un VARCHAR2(128), b2buaid VARCHAR2(128) DEFAULT '', flow VARCHAR2(128), agent VARCHAR2(128), CONSTRAINT cc_calls_unique_id UNIQUE (b2buaid) ); CREATE OR REPLACE TRIGGER cc_calls_tr before insert on cc_calls FOR EACH ROW BEGIN auto_id(:NEW.id); END cc_calls_tr; / BEGIN map2users('cc_calls'); END; / CREATE INDEX cc_calls_b2buaid_idx ON cc_calls (b2buaid); opensips-2.2.2/scripts/oracle/carrierroute-create.sql000066400000000000000000000034421300170765700230120ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('carrierroute','3'); CREATE TABLE carrierroute ( id NUMBER(10) PRIMARY KEY, carrier NUMBER(10) DEFAULT 0 NOT NULL, domain VARCHAR2(64) DEFAULT '', scan_prefix VARCHAR2(64) DEFAULT '', flags NUMBER(10) DEFAULT 0 NOT NULL, mask NUMBER(10) DEFAULT 0 NOT NULL, prob NUMBER DEFAULT 0 NOT NULL, strip NUMBER(10) DEFAULT 0 NOT NULL, rewrite_host VARCHAR2(128) DEFAULT '', rewrite_prefix VARCHAR2(64) DEFAULT '', rewrite_suffix VARCHAR2(64) DEFAULT '', description VARCHAR2(255) DEFAULT NULL ); CREATE OR REPLACE TRIGGER carrierroute_tr before insert on carrierroute FOR EACH ROW BEGIN auto_id(:NEW.id); END carrierroute_tr; / BEGIN map2users('carrierroute'); END; / INSERT INTO version (table_name, table_version) values ('carrierfailureroute','2'); CREATE TABLE carrierfailureroute ( id NUMBER(10) PRIMARY KEY, carrier NUMBER(10) DEFAULT 0 NOT NULL, domain VARCHAR2(64) DEFAULT '', scan_prefix VARCHAR2(64) DEFAULT '', host_name VARCHAR2(128) DEFAULT '', reply_code VARCHAR2(3) DEFAULT '', flags NUMBER(10) DEFAULT 0 NOT NULL, mask NUMBER(10) DEFAULT 0 NOT NULL, next_domain VARCHAR2(64) DEFAULT '', description VARCHAR2(255) DEFAULT NULL ); CREATE OR REPLACE TRIGGER carrierfailureroute_tr before insert on carrierfailureroute FOR EACH ROW BEGIN auto_id(:NEW.id); END carrierfailureroute_tr; / BEGIN map2users('carrierfailureroute'); END; / INSERT INTO version (table_name, table_version) values ('route_tree','2'); CREATE TABLE route_tree ( id NUMBER(10) PRIMARY KEY, carrier VARCHAR2(64) DEFAULT NULL ); CREATE OR REPLACE TRIGGER route_tree_tr before insert on route_tree FOR EACH ROW BEGIN auto_id(:NEW.id); END route_tree_tr; / BEGIN map2users('route_tree'); END; / opensips-2.2.2/scripts/oracle/closeddial-create.sql000066400000000000000000000014501300170765700224040ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('closeddial','1'); CREATE TABLE closeddial ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', cd_username VARCHAR2(64) DEFAULT '', cd_domain VARCHAR2(64) DEFAULT '', group_id VARCHAR2(64) DEFAULT '', new_uri VARCHAR2(128) DEFAULT '', CONSTRAINT closeddial_cd_idx1 UNIQUE (username, domain, cd_domain, cd_username, group_id) ); CREATE OR REPLACE TRIGGER closeddial_tr before insert on closeddial FOR EACH ROW BEGIN auto_id(:NEW.id); END closeddial_tr; / BEGIN map2users('closeddial'); END; / CREATE INDEX closeddial_cd_idx2 ON closeddial (group_id); CREATE INDEX closeddial_cd_idx3 ON closeddial (cd_username); CREATE INDEX closeddial_cd_idx4 ON closeddial (username); opensips-2.2.2/scripts/oracle/clusterer-create.sql000066400000000000000000000012771300170765700223200ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('clusterer','1'); CREATE TABLE clusterer ( id NUMBER(10) PRIMARY KEY, cluster_id NUMBER(10), machine_id NUMBER(10), url VARCHAR2(64), state NUMBER(10) DEFAULT 1 NOT NULL, last_attempt BIGINT(64) DEFAULT 0 NOT NULL, failed_attempts NUMBER(10) DEFAULT 3 NOT NULL, no_tries NUMBER(10) DEFAULT 0 NOT NULL, duration NUMBER(10) DEFAULT 30 NOT NULL, description VARCHAR2(64), CONSTRAINT clusterer_clusterer_idx UNIQUE (cluster_id, machine_id) ); CREATE OR REPLACE TRIGGER clusterer_tr before insert on clusterer FOR EACH ROW BEGIN auto_id(:NEW.id); END clusterer_tr; / BEGIN map2users('clusterer'); END; / opensips-2.2.2/scripts/oracle/cpl-create.sql000066400000000000000000000006411300170765700210600ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cpl','2'); CREATE TABLE cpl ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(64) DEFAULT '', cpl_xml CLOB, cpl_bin CLOB, CONSTRAINT cpl_account_idx UNIQUE (username, domain) ); CREATE OR REPLACE TRIGGER cpl_tr before insert on cpl FOR EACH ROW BEGIN auto_id(:NEW.id); END cpl_tr; / BEGIN map2users('cpl'); END; / opensips-2.2.2/scripts/oracle/dialog-create.sql000066400000000000000000000021231300170765700215360ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dialog','10'); CREATE TABLE dialog ( dlg_id BIGINT(10) PRIMARY KEY, callid VARCHAR2(255), from_uri VARCHAR2(128), from_tag VARCHAR2(64), to_uri VARCHAR2(128), to_tag VARCHAR2(64), mangled_from_uri VARCHAR2(64) DEFAULT NULL, mangled_to_uri VARCHAR2(64) DEFAULT NULL, caller_cseq VARCHAR2(11), callee_cseq VARCHAR2(11), caller_ping_cseq NUMBER(10), callee_ping_cseq NUMBER(10), caller_route_set CLOB(512), callee_route_set CLOB(512), caller_contact VARCHAR2(128), callee_contact VARCHAR2(128), caller_sock VARCHAR2(64), callee_sock VARCHAR2(64), state NUMBER(10), start_time NUMBER(10), timeout NUMBER(10), vars BLOB(4096) DEFAULT NULL, profiles CLOB(512) DEFAULT NULL, script_flags NUMBER(10) DEFAULT 0 NOT NULL, module_flags NUMBER(10) DEFAULT 0 NOT NULL, flags NUMBER(10) DEFAULT 0 NOT NULL ); CREATE OR REPLACE TRIGGER dialog_tr before insert on dialog FOR EACH ROW BEGIN auto_id(:NEW.id); END dialog_tr; / BEGIN map2users('dialog'); END; / opensips-2.2.2/scripts/oracle/dialplan-create.sql000066400000000000000000000010601300170765700220620ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dialplan','5'); CREATE TABLE dialplan ( id NUMBER(10) PRIMARY KEY, dpid NUMBER(10), pr NUMBER(10), match_op NUMBER(10), match_exp VARCHAR2(64), match_flags NUMBER(10), subst_exp VARCHAR2(64), repl_exp VARCHAR2(32), timerec VARCHAR2(255), disabled NUMBER(10) DEFAULT 0 NOT NULL, attrs VARCHAR2(32) ); CREATE OR REPLACE TRIGGER dialplan_tr before insert on dialplan FOR EACH ROW BEGIN auto_id(:NEW.id); END dialplan_tr; / BEGIN map2users('dialplan'); END; / opensips-2.2.2/scripts/oracle/dispatcher-create.sql000066400000000000000000000011631300170765700224300ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dispatcher','7'); CREATE TABLE dispatcher ( id NUMBER(10) PRIMARY KEY, setid NUMBER(10) DEFAULT 0 NOT NULL, destination VARCHAR2(192) DEFAULT '', socket VARCHAR2(128) DEFAULT NULL, state NUMBER(10) DEFAULT 0 NOT NULL, weight NUMBER(10) DEFAULT 1 NOT NULL, priority NUMBER(10) DEFAULT 0 NOT NULL, attrs VARCHAR2(128) DEFAULT '', description VARCHAR2(64) DEFAULT '' ); CREATE OR REPLACE TRIGGER dispatcher_tr before insert on dispatcher FOR EACH ROW BEGIN auto_id(:NEW.id); END dispatcher_tr; / BEGIN map2users('dispatcher'); END; / opensips-2.2.2/scripts/oracle/domain-create.sql000066400000000000000000000007511300170765700215530ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('domain','3'); CREATE TABLE domain ( id NUMBER(10) PRIMARY KEY, domain VARCHAR2(64) DEFAULT '', attrs VARCHAR2(255) DEFAULT NULL, last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), CONSTRAINT domain_domain_idx UNIQUE (domain) ); CREATE OR REPLACE TRIGGER domain_tr before insert on domain FOR EACH ROW BEGIN auto_id(:NEW.id); END domain_tr; / BEGIN map2users('domain'); END; / opensips-2.2.2/scripts/oracle/domainpolicy-create.sql000066400000000000000000000010611300170765700227660ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('domainpolicy','3'); CREATE TABLE domainpolicy ( id NUMBER(10) PRIMARY KEY, rule VARCHAR2(255), type VARCHAR2(255), att VARCHAR2(255), val VARCHAR2(128), description VARCHAR2(255), CONSTRAINT domainpolicy_rav_idx UNIQUE (rule, att, val) ); CREATE OR REPLACE TRIGGER domainpolicy_tr before insert on domainpolicy FOR EACH ROW BEGIN auto_id(:NEW.id); END domainpolicy_tr; / BEGIN map2users('domainpolicy'); END; / CREATE INDEX domainpolicy_rule_idx ON domainpolicy (rule); opensips-2.2.2/scripts/oracle/drouting-create.sql000066400000000000000000000057501300170765700221430ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dr_gateways','6'); CREATE TABLE dr_gateways ( id NUMBER(10) PRIMARY KEY, gwid VARCHAR2(64), type NUMBER(10) DEFAULT 0 NOT NULL, address VARCHAR2(128), strip NUMBER(10) DEFAULT 0 NOT NULL, pri_prefix VARCHAR2(16) DEFAULT NULL, attrs VARCHAR2(255) DEFAULT NULL, probe_mode NUMBER(10) DEFAULT 0 NOT NULL, state NUMBER(10) DEFAULT 0 NOT NULL, socket VARCHAR2(128) DEFAULT NULL, description VARCHAR2(128) DEFAULT '', CONSTRAINT dr_gateways_dr_gw_idx UNIQUE (gwid) ); CREATE OR REPLACE TRIGGER dr_gateways_tr before insert on dr_gateways FOR EACH ROW BEGIN auto_id(:NEW.id); END dr_gateways_tr; / BEGIN map2users('dr_gateways'); END; / INSERT INTO version (table_name, table_version) values ('dr_rules','3'); CREATE TABLE dr_rules ( ruleid NUMBER(10) PRIMARY KEY, groupid VARCHAR2(255), prefix VARCHAR2(64), timerec VARCHAR2(255), priority NUMBER(10) DEFAULT 0 NOT NULL, routeid VARCHAR2(255) DEFAULT NULL, gwlist VARCHAR2(255), attrs VARCHAR2(255) DEFAULT NULL, description VARCHAR2(128) DEFAULT '' ); CREATE OR REPLACE TRIGGER dr_rules_tr before insert on dr_rules FOR EACH ROW BEGIN auto_id(:NEW.id); END dr_rules_tr; / BEGIN map2users('dr_rules'); END; / INSERT INTO version (table_name, table_version) values ('dr_carriers','2'); CREATE TABLE dr_carriers ( id NUMBER(10) PRIMARY KEY, carrierid VARCHAR2(64), gwlist VARCHAR2(255), flags NUMBER(10) DEFAULT 0 NOT NULL, state NUMBER(10) DEFAULT 0 NOT NULL, attrs VARCHAR2(255) DEFAULT '', description VARCHAR2(128) DEFAULT '', CONSTRAINT dr_carriers_dr_carrier_idx UNIQUE (carrierid) ); CREATE OR REPLACE TRIGGER dr_carriers_tr before insert on dr_carriers FOR EACH ROW BEGIN auto_id(:NEW.id); END dr_carriers_tr; / BEGIN map2users('dr_carriers'); END; / INSERT INTO version (table_name, table_version) values ('dr_groups','2'); CREATE TABLE dr_groups ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(128) DEFAULT '', groupid NUMBER(10) DEFAULT 0 NOT NULL, description VARCHAR2(128) DEFAULT '' ); CREATE OR REPLACE TRIGGER dr_groups_tr before insert on dr_groups FOR EACH ROW BEGIN auto_id(:NEW.id); END dr_groups_tr; / BEGIN map2users('dr_groups'); END; / INSERT INTO version (table_name, table_version) values ('dr_partitions','1'); CREATE TABLE dr_partitions ( id NUMBER(10) PRIMARY KEY, partition_name VARCHAR2(255), db_url VARCHAR2(255), drd_table VARCHAR2(255), drr_table VARCHAR2(255), drg_table VARCHAR2(255), drc_table VARCHAR2(255), ruri_avp VARCHAR2(255), gw_id_avp VARCHAR2(255), gw_priprefix_avp VARCHAR2(255), gw_sock_avp VARCHAR2(255), rule_id_avp VARCHAR2(255), rule_prefix_avp VARCHAR2(255), carrier_id_avp VARCHAR2(255) ); CREATE OR REPLACE TRIGGER dr_partitions_tr before insert on dr_partitions FOR EACH ROW BEGIN auto_id(:NEW.id); END dr_partitions_tr; / BEGIN map2users('dr_partitions'); END; / opensips-2.2.2/scripts/oracle/emergency-create.sql000066400000000000000000000033441300170765700222630ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('emergency_routing','1'); CREATE TABLE emergency_routing ( id NUMBER(10) PRIMARY KEY, selectiveRoutingID VARCHAR2(11), routingESN NUMBER(10) DEFAULT 0 NOT NULL, npa NUMBER(10) DEFAULT 0 NOT NULL, esgwri VARCHAR2(50) ); CREATE OR REPLACE TRIGGER emergency_routing_tr before insert on emergency_routing FOR EACH ROW BEGIN auto_id(:NEW.id); END emergency_routing_tr; / BEGIN map2users('emergency_routing'); END; / INSERT INTO version (table_name, table_version) values ('emergency_report','1'); CREATE TABLE emergency_report ( id NUMBER(10) PRIMARY KEY, callid VARCHAR2(25), selectiveRoutingID VARCHAR2(11), routingESN NUMBER(10) DEFAULT 0 NOT NULL, npa NUMBER(10) DEFAULT 0 NOT NULL, esgwri VARCHAR2(50), lro VARCHAR2(20), VPC_organizationName VARCHAR2(50), VPC_hostname VARCHAR2(50), VPC_timestamp VARCHAR2(30), result VARCHAR2(4), disposition VARCHAR2(10) ); CREATE OR REPLACE TRIGGER emergency_report_tr before insert on emergency_report FOR EACH ROW BEGIN auto_id(:NEW.id); END emergency_report_tr; / BEGIN map2users('emergency_report'); END; / INSERT INTO version (table_name, table_version) values ('emergency_service_provider','1'); CREATE TABLE emergency_service_provider ( id NUMBER(10) PRIMARY KEY, organizationName VARCHAR2(50), hostId VARCHAR2(30), nenaId VARCHAR2(50), contact VARCHAR2(20), certUri VARCHAR2(50), nodeIP VARCHAR2(20), attribution NUMBER(10) ); CREATE OR REPLACE TRIGGER emergency_service_provider_tr before insert on emergency_service_provider FOR EACH ROW BEGIN auto_id(:NEW.id); END emergency_service_provider_tr; / BEGIN map2users('emergency_service_provider'); END; / opensips-2.2.2/scripts/oracle/fraud_detection-create.sql000066400000000000000000000015151300170765700234420ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('fraud_detection','1'); CREATE TABLE fraud_detection ( ruleid NUMBER(10) PRIMARY KEY, profileid NUMBER(10), prefix VARCHAR2(64), start_hour VARCHAR2(5), end_hour VARCHAR2(5), daysoftheweek VARCHAR2(64), cpm_warning NUMBER(10), cpm_critical NUMBER(10), call_duration_warning NUMBER(10), call_duration_critical NUMBER(10), total_calls_warning NUMBER(10), total_calls_critical NUMBER(10), concurrent_calls_warning NUMBER(10), concurrent_calls_critical NUMBER(10), sequential_calls_warning NUMBER(10), sequential_calls_critical NUMBER(10) ); CREATE OR REPLACE TRIGGER fraud_detection_tr before insert on fraud_detection FOR EACH ROW BEGIN auto_id(:NEW.id); END fraud_detection_tr; / BEGIN map2users('fraud_detection'); END; / opensips-2.2.2/scripts/oracle/group-create.sql000066400000000000000000000016501300170765700214370ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('grp','3'); CREATE TABLE grp ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', grp VARCHAR2(64) DEFAULT '', last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), CONSTRAINT grp_account_group_idx UNIQUE (username, domain, grp) ); CREATE OR REPLACE TRIGGER grp_tr before insert on grp FOR EACH ROW BEGIN auto_id(:NEW.id); END grp_tr; / BEGIN map2users('grp'); END; / INSERT INTO version (table_name, table_version) values ('re_grp','2'); CREATE TABLE re_grp ( id NUMBER(10) PRIMARY KEY, reg_exp VARCHAR2(128) DEFAULT '', group_id NUMBER(10) DEFAULT 0 NOT NULL ); CREATE OR REPLACE TRIGGER re_grp_tr before insert on re_grp FOR EACH ROW BEGIN auto_id(:NEW.id); END re_grp_tr; / BEGIN map2users('re_grp'); END; / CREATE INDEX re_grp_group_idx ON re_grp (group_id); opensips-2.2.2/scripts/oracle/imc-create.sql000066400000000000000000000016161300170765700210550ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('imc_rooms','2'); CREATE TABLE imc_rooms ( id NUMBER(10) PRIMARY KEY, name VARCHAR2(64), domain VARCHAR2(64), flag NUMBER(10), CONSTRAINT imc_rooms_name_domain_idx UNIQUE (name, domain) ); CREATE OR REPLACE TRIGGER imc_rooms_tr before insert on imc_rooms FOR EACH ROW BEGIN auto_id(:NEW.id); END imc_rooms_tr; / BEGIN map2users('imc_rooms'); END; / INSERT INTO version (table_name, table_version) values ('imc_members','2'); CREATE TABLE imc_members ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(64), room VARCHAR2(64), flag NUMBER(10), CONSTRAINT imc_members_account_room_idx UNIQUE (username, domain, room) ); CREATE OR REPLACE TRIGGER imc_members_tr before insert on imc_members FOR EACH ROW BEGIN auto_id(:NEW.id); END imc_members_tr; / BEGIN map2users('imc_members'); END; / opensips-2.2.2/scripts/oracle/inc/000077500000000000000000000000001300170765700170705ustar00rootroot00000000000000opensips-2.2.2/scripts/oracle/inc/_create_compat.sql000066400000000000000000000060761300170765700225670ustar00rootroot00000000000000create or replace function now(v1 in number := 0) return date is Result date; begin SELECT sysdate INTO Result FROM dual; return Result; end now; / create or replace function rand(v1 in number := 0) return number is Result number; begin SELECT dbms_random.value INTO Result FROM dual; return Result; end rand; / create or replace function concat(v1 in varchar2, v2 in varchar2, v3 in varchar2) return varchar2 IS Result varchar2(4000); begin SELECT v1||v2||v3 INTO Result from dual; return Result; end concat; / create or replace TYPE TABLE_STRING IS TABLE OF VARCHAR2(4000); / create or replace function DUMP_TABLES(P_OWNER in VARCHAR2) RETURN TABLE_STRING PIPELINED IS CURSOR COLUMNS_CUR (P_OWNER in VARCHAR2, P_TABLE in VARCHAR2) IS SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM ALL_TAB_COLUMNS WHERE OWNER = UPPER(P_OWNER) AND TABLE_NAME = UPPER(P_TABLE) ORDER BY COLUMN_ID; COLUMN_REC COLUMNS_CUR%ROWTYPE; TABLE_REC_CUR SYS_REFCURSOR; L_QUERY VARCHAR2(8000); L_QUERY1 VARCHAR2(8000); L_QUERY2 VARCHAR2(8000); L_LINE VARCHAR2(8000); L_COMA CHAR(2) := ' '; FIRST_ROW BOOLEAN := TRUE; BEGIN FOR cur IN (SELECT TABLE_NAME FROM all_tables WHERE owner=UPPER(P_OWNER)) LOOP L_QUERY1 := 'SELECT ''INSERT INTO ' || cur.table_name; L_QUERY2 :='('; OPEN COLUMNS_CUR(P_OWNER, cur.table_name); FIRST_ROW := TRUE; LOOP FETCH COLUMNS_CUR INTO COLUMN_REC; IF FIRST_ROW AND COLUMNS_CUR%NOTFOUND THEN PIPE ROW('Table ''' || P_OWNER || '.' || cur.table_name || ''' not found'); END IF; EXIT WHEN COLUMNS_CUR%NOTFOUND; IF FIRST_ROW THEN L_QUERY2 := L_QUERY2 || COLUMN_REC.COLUMN_NAME; L_QUERY := ' VALUES ('' || '; ELSE L_QUERY2 := L_QUERY2||','||COLUMN_REC.COLUMN_NAME; L_COMA := ', '; L_QUERY := L_QUERY || ' || '', '' || '; END IF; IF COLUMN_REC.DATA_TYPE = 'VARCHAR2' OR COLUMN_REC.DATA_TYPE = 'CHAR' OR COLUMN_REC.DATA_TYPE = 'CLOB' THEN L_QUERY := L_QUERY || 'NVL2(' || COLUMN_REC.COLUMN_NAME || ', '''''''' || REPLACE(' || COLUMN_REC.COLUMN_NAME || ', '''''''', '''''''''''') || '''''''', ''NULL'')'; ELSIF COLUMN_REC.DATA_TYPE = 'DATE' THEN L_QUERY := L_QUERY || 'NVL2(' || COLUMN_REC.COLUMN_NAME || ', ''TO_DATE('''''' || TO_CHAR(' || COLUMN_REC.COLUMN_NAME || ', ''yyyy-mm-dd hh24:mi:ss'') || '''''', ''''yyyy-mm-dd hh24:mi:ss'''')'', ''NULL'')'; ELSIF COLUMN_REC.DATA_TYPE = 'BLOB' THEN L_QUERY := L_QUERY || 'NVL2(' || COLUMN_REC.COLUMN_NAME || ', ''UNSUPPORTED:NON EMPTY BLOB'', ''NULL'')'; ELSE L_QUERY := L_QUERY || 'NVL(TO_CHAR(' || COLUMN_REC.COLUMN_NAME || '), ''NULL'')'; END IF; FIRST_ROW := FALSE; END LOOP; IF NOT FIRST_ROW THEN L_QUERY :=L_QUERY1||L_QUERY2||')'|| L_QUERY || ' || '');'' AS LINE FROM ' || COLUMN_REC.TABLE_NAME; END IF; CLOSE COLUMNS_CUR; /* IF FIRST_ROW THEN RETURN; END IF;*/ OPEN TABLE_REC_CUR FOR L_QUERY; LOOP FETCH TABLE_REC_CUR INTO L_LINE; EXIT WHEN TABLE_REC_CUR%NOTFOUND; PIPE ROW(L_LINE); END LOOP; CLOSE TABLE_REC_CUR; END LOOP; RETURN; END; / opensips-2.2.2/scripts/oracle/inc/_createsch.tmpl000066400000000000000000000003731300170765700220710ustar00rootroot00000000000000create sequence seq_%DBROOTUSER% minvalue 1 maxvalue 9999999999999999999999999999 start with 1 increment by 1 cache 20; create or replace procedure auto_id(fld out number) is begin SELECT seq_%DBROOTUSER%.NEXTVAL INTO fld FROM dual; end auto_id; / opensips-2.2.2/scripts/oracle/inc/_dropsch.tmpl000066400000000000000000000006271300170765700215740ustar00rootroot00000000000000--DROP USER %DBRWUSER% CASCADE; BEGIN FOR cur IN (SELECT 'DROP TABLE '||table_name||' CASCADE CONSTRAINTS PURGE' stmt from all_tables where owner=UPPER('%DBROOTUSER%')) LOOP EXECUTE IMMEDIATE cur.stmt; END LOOP; FOR cur IN (SELECT 'DROP '||object_type||' '||object_name stmt from all_objects where owner=UPPER('%DBROOTUSER%')) LOOP EXECUTE IMMEDIATE cur.stmt; END LOOP; END; / opensips-2.2.2/scripts/oracle/inc/_grantfunc.tmpl000066400000000000000000000011721300170765700221150ustar00rootroot00000000000000BEGIN FOR cur IN (SELECT OBJECT_NAME FROM All_Procedures WHERE owner=UPPER('%DBROOTUSER%') and object_name <> 'MAP2USERS') LOOP EXECUTE IMMEDIATE('GRANT EXECUTE ON %DBROOTUSER%.'||cur.OBJECT_NAME ||' to %DBRWUSER%'); EXECUTE IMMEDIATE('CREATE OR REPLACE SYNONYM %DBRWUSER%.'||cur.OBJECT_NAME ||' for %DBROOTUSER%.'||cur.OBJECT_NAME); END LOOP; END; / CREATE OR REPLACE PROCEDURE map2users(tbl varchar2) is BEGIN EXECUTE IMMEDIATE('GRANT SELECT,INSERT,UPDATE,DELETE ON '||tbl ||' to %DBRWUSER%'); EXECUTE IMMEDIATE('CREATE OR REPLACE SYNONYM %DBRWUSER%.'||tbl ||' for %DBROOTUSER%.'||tbl); END map2users; / opensips-2.2.2/scripts/oracle/inc/_grantroot.tmpl000066400000000000000000000005271300170765700221500ustar00rootroot00000000000000--create user %DBROOTUSER% identified by %DBROOTPW% -- default tablespace DATA temporary tablespace TEMP profile DEFAULT; grant connect to %DBROOTUSER% with admin option; grant resource, create any synonym, create role, create user, drop user, unlimited tablespace to %DBROOTUSER%; grant select on sys.dba_role_privs to %DBROOTUSER%; opensips-2.2.2/scripts/oracle/load_balancer-create.sql000066400000000000000000000010771300170765700230540ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('load_balancer','2'); CREATE TABLE load_balancer ( id NUMBER(10) PRIMARY KEY, group_id NUMBER(10) DEFAULT 0 NOT NULL, dst_uri VARCHAR2(128), resources VARCHAR2(255), probe_mode NUMBER(10) DEFAULT 0 NOT NULL, description VARCHAR2(128) DEFAULT '' ); CREATE OR REPLACE TRIGGER load_balancer_tr before insert on load_balancer FOR EACH ROW BEGIN auto_id(:NEW.id); END load_balancer_tr; / BEGIN map2users('load_balancer'); END; / CREATE INDEX load_balancer_dsturi_idx ON load_balancer (dst_uri); opensips-2.2.2/scripts/oracle/msilo-create.sql000066400000000000000000000012431300170765700214240ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('silo','6'); CREATE TABLE silo ( id NUMBER(10) PRIMARY KEY, src_addr VARCHAR2(128) DEFAULT '', dst_addr VARCHAR2(128) DEFAULT '', username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', inc_time NUMBER(10) DEFAULT 0 NOT NULL, exp_time NUMBER(10) DEFAULT 0 NOT NULL, snd_time NUMBER(10) DEFAULT 0 NOT NULL, ctype VARCHAR2(255) DEFAULT NULL, body BLOB DEFAULT NULL ); CREATE OR REPLACE TRIGGER silo_tr before insert on silo FOR EACH ROW BEGIN auto_id(:NEW.id); END silo_tr; / BEGIN map2users('silo'); END; / CREATE INDEX silo_account_idx ON silo (username, domain); opensips-2.2.2/scripts/oracle/permissions-create.sql000066400000000000000000000010351300170765700226530ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('address','5'); CREATE TABLE address ( id NUMBER(10) PRIMARY KEY, grp NUMBER(5) DEFAULT 0 NOT NULL, ip VARCHAR2(50), mask NUMBER(5) DEFAULT 32 NOT NULL, port NUMBER(5) DEFAULT 0 NOT NULL, proto VARCHAR2(4) DEFAULT 'any', pattern VARCHAR2(64) DEFAULT NULL, context_info VARCHAR2(32) DEFAULT NULL ); CREATE OR REPLACE TRIGGER address_tr before insert on address FOR EACH ROW BEGIN auto_id(:NEW.id); END address_tr; / BEGIN map2users('address'); END; / opensips-2.2.2/scripts/oracle/presence-create.sql000066400000000000000000000072571300170765700221200ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('presentity','5'); CREATE TABLE presentity ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(64), event VARCHAR2(64), etag VARCHAR2(64), expires NUMBER(10), received_time NUMBER(10), body BLOB, extra_hdrs BLOB DEFAULT '', sender VARCHAR2(128), CONSTRAINT presentity_presentity_idx UNIQUE (username, domain, event, etag) ); CREATE OR REPLACE TRIGGER presentity_tr before insert on presentity FOR EACH ROW BEGIN auto_id(:NEW.id); END presentity_tr; / BEGIN map2users('presentity'); END; / INSERT INTO version (table_name, table_version) values ('active_watchers','11'); CREATE TABLE active_watchers ( id NUMBER(10) PRIMARY KEY, presentity_uri VARCHAR2(128), watcher_username VARCHAR2(64), watcher_domain VARCHAR2(64), to_user VARCHAR2(64), to_domain VARCHAR2(64), event VARCHAR2(64) DEFAULT 'presence', event_id VARCHAR2(64), to_tag VARCHAR2(64), from_tag VARCHAR2(64), callid VARCHAR2(64), local_cseq NUMBER(10), remote_cseq NUMBER(10), contact VARCHAR2(128), record_route CLOB, expires NUMBER(10), status NUMBER(10) DEFAULT 2 NOT NULL, reason VARCHAR2(64), version NUMBER(10) DEFAULT 0 NOT NULL, socket_info VARCHAR2(64), local_contact VARCHAR2(128), CONSTRAINT ORA_active_watchers_idx UNIQUE (presentity_uri, callid, to_tag, from_tag) ); CREATE OR REPLACE TRIGGER active_watchers_tr before insert on active_watchers FOR EACH ROW BEGIN auto_id(:NEW.id); END active_watchers_tr; / BEGIN map2users('active_watchers'); END; / INSERT INTO version (table_name, table_version) values ('watchers','4'); CREATE TABLE watchers ( id NUMBER(10) PRIMARY KEY, presentity_uri VARCHAR2(128), watcher_username VARCHAR2(64), watcher_domain VARCHAR2(64), event VARCHAR2(64) DEFAULT 'presence', status NUMBER(10), reason VARCHAR2(64), inserted_time NUMBER(10), CONSTRAINT watchers_watcher_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) ); CREATE OR REPLACE TRIGGER watchers_tr before insert on watchers FOR EACH ROW BEGIN auto_id(:NEW.id); END watchers_tr; / BEGIN map2users('watchers'); END; / INSERT INTO version (table_name, table_version) values ('xcap','4'); CREATE TABLE xcap ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(64), doc BLOB, doc_type NUMBER(10), etag VARCHAR2(64), source NUMBER(10), doc_uri VARCHAR2(128), port NUMBER(10), CONSTRAINT xcap_account_doc_type_idx UNIQUE (username, domain, doc_type, doc_uri) ); CREATE OR REPLACE TRIGGER xcap_tr before insert on xcap FOR EACH ROW BEGIN auto_id(:NEW.id); END xcap_tr; / BEGIN map2users('xcap'); END; / CREATE INDEX xcap_source_idx ON xcap (source); INSERT INTO version (table_name, table_version) values ('pua','8'); CREATE TABLE pua ( id NUMBER(10) PRIMARY KEY, pres_uri VARCHAR2(128), pres_id VARCHAR2(255), event NUMBER(10), expires NUMBER(10), desired_expires NUMBER(10), flag NUMBER(10), etag VARCHAR2(64), tuple_id VARCHAR2(64), watcher_uri VARCHAR2(128), to_uri VARCHAR2(128), call_id VARCHAR2(64), to_tag VARCHAR2(64), from_tag VARCHAR2(64), cseq NUMBER(10), record_route CLOB, contact VARCHAR2(128), remote_contact VARCHAR2(128), version NUMBER(10), extra_headers CLOB ); CREATE OR REPLACE TRIGGER pua_tr before insert on pua FOR EACH ROW BEGIN auto_id(:NEW.id); END pua_tr; / BEGIN map2users('pua'); END; / CREATE INDEX pua_del1_idx ON pua (pres_uri, event); CREATE INDEX pua_del2_idx ON pua (expires); CREATE INDEX pua_update_idx ON pua (pres_uri, pres_id, flag, event); opensips-2.2.2/scripts/oracle/registrant-create.sql000066400000000000000000000013761300170765700224720ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('registrant','1'); CREATE TABLE registrant ( id NUMBER(10) PRIMARY KEY, registrar VARCHAR2(128) DEFAULT '', proxy VARCHAR2(128) DEFAULT NULL, aor VARCHAR2(128) DEFAULT '', third_party_registrant VARCHAR2(128) DEFAULT NULL, username VARCHAR2(64) DEFAULT NULL, password VARCHAR2(64) DEFAULT NULL, binding_URI VARCHAR2(128) DEFAULT '', binding_params VARCHAR2(64) DEFAULT NULL, expiry NUMBER(10) DEFAULT NULL, forced_socket VARCHAR2(64) DEFAULT NULL, CONSTRAINT registrant_aor_idx UNIQUE (aor) ); CREATE OR REPLACE TRIGGER registrant_tr before insert on registrant FOR EACH ROW BEGIN auto_id(:NEW.id); END registrant_tr; / BEGIN map2users('registrant'); END; / opensips-2.2.2/scripts/oracle/registrar-create.sql000066400000000000000000000021561300170765700223070ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('aliases','1009'); CREATE TABLE aliases ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', contact VARCHAR2(255) DEFAULT '', received VARCHAR2(128) DEFAULT NULL, path VARCHAR2(255) DEFAULT NULL, expires DATE DEFAULT to_date('2020-05-28 21:32:15','yyyy-mm-dd hh24:mi:ss'), q NUMBER(10,2) DEFAULT 1.0 NOT NULL, callid VARCHAR2(255) DEFAULT 'Default-Call-ID', cseq NUMBER(10) DEFAULT 13 NOT NULL, last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), flags NUMBER(10) DEFAULT 0 NOT NULL, cflags VARCHAR2(255) DEFAULT NULL, user_agent VARCHAR2(255) DEFAULT '', socket VARCHAR2(64) DEFAULT NULL, methods NUMBER(10) DEFAULT NULL, sip_instance VARCHAR2(255) DEFAULT NULL, attr VARCHAR2(255) DEFAULT NULL, CONSTRAINT aliases_alias_idx UNIQUE (username, domain, contact, callid) ); CREATE OR REPLACE TRIGGER aliases_tr before insert on aliases FOR EACH ROW BEGIN auto_id(:NEW.id); END aliases_tr; / BEGIN map2users('aliases'); END; / opensips-2.2.2/scripts/oracle/rls-create.sql000066400000000000000000000032461300170765700211060ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('rls_presentity','1'); CREATE TABLE rls_presentity ( id NUMBER(10) PRIMARY KEY, rlsubs_did VARCHAR2(255), resource_uri VARCHAR2(128), content_type VARCHAR2(255), presence_state BLOB, expires NUMBER(10), updated NUMBER(10), auth_state NUMBER(10), reason VARCHAR2(64), CONSTRAINT ORA_rls_presentity_idx UNIQUE (rlsubs_did, resource_uri) ); CREATE OR REPLACE TRIGGER rls_presentity_tr before insert on rls_presentity FOR EACH ROW BEGIN auto_id(:NEW.id); END rls_presentity_tr; / BEGIN map2users('rls_presentity'); END; / CREATE INDEX rls_presentity_updated_idx ON rls_presentity (updated); INSERT INTO version (table_name, table_version) values ('rls_watchers','2'); CREATE TABLE rls_watchers ( id NUMBER(10) PRIMARY KEY, presentity_uri VARCHAR2(128), to_user VARCHAR2(64), to_domain VARCHAR2(64), watcher_username VARCHAR2(64), watcher_domain VARCHAR2(64), event VARCHAR2(64) DEFAULT 'presence', event_id VARCHAR2(64), to_tag VARCHAR2(64), from_tag VARCHAR2(64), callid VARCHAR2(64), local_cseq NUMBER(10), remote_cseq NUMBER(10), contact VARCHAR2(64), record_route CLOB, expires NUMBER(10), status NUMBER(10) DEFAULT 2 NOT NULL, reason VARCHAR2(64), version NUMBER(10) DEFAULT 0 NOT NULL, socket_info VARCHAR2(64), local_contact VARCHAR2(128), CONSTRAINT rls_watchers_rls_watcher_idx UNIQUE (presentity_uri, callid, to_tag, from_tag) ); CREATE OR REPLACE TRIGGER rls_watchers_tr before insert on rls_watchers FOR EACH ROW BEGIN auto_id(:NEW.id); END rls_watchers_tr; / BEGIN map2users('rls_watchers'); END; / opensips-2.2.2/scripts/oracle/rtpproxy-create.sql000066400000000000000000000006001300170765700222040ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('rtpproxy_sockets','0'); CREATE TABLE rtpproxy_sockets ( id NUMBER(10) PRIMARY KEY, rtpproxy_sock CLOB, set_id NUMBER(10) ); CREATE OR REPLACE TRIGGER rtpproxy_sockets_tr before insert on rtpproxy_sockets FOR EACH ROW BEGIN auto_id(:NEW.id); END rtpproxy_sockets_tr; / BEGIN map2users('rtpproxy_sockets'); END; / opensips-2.2.2/scripts/oracle/siptrace-create.sql000066400000000000000000000020261300170765700221130ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('sip_trace','5'); CREATE TABLE sip_trace ( id NUMBER(10) PRIMARY KEY, time_stamp DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), callid VARCHAR2(255) DEFAULT '', trace_attrs VARCHAR2(128) DEFAULT NULL, msg CLOB, method VARCHAR2(32) DEFAULT '', status VARCHAR2(128) DEFAULT NULL, from_proto VARCHAR2(5), from_ip VARCHAR2(50) DEFAULT '', from_port NUMBER(10), to_proto VARCHAR2(5), to_ip VARCHAR2(50) DEFAULT '', to_port NUMBER(10), fromtag VARCHAR2(64) DEFAULT '', direction VARCHAR2(4) DEFAULT '' ); CREATE OR REPLACE TRIGGER sip_trace_tr before insert on sip_trace FOR EACH ROW BEGIN auto_id(:NEW.id); END sip_trace_tr; / BEGIN map2users('sip_trace'); END; / CREATE INDEX sip_trace_trace_attrs_idx ON sip_trace (trace_attrs); CREATE INDEX sip_trace_date_idx ON sip_trace (time_stamp); CREATE INDEX sip_trace_fromip_idx ON sip_trace (from_ip); CREATE INDEX sip_trace_callid_idx ON sip_trace (callid); opensips-2.2.2/scripts/oracle/speeddial-create.sql000066400000000000000000000012711300170765700222340ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('speed_dial','3'); CREATE TABLE speed_dial ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', sd_username VARCHAR2(64) DEFAULT '', sd_domain VARCHAR2(64) DEFAULT '', new_uri VARCHAR2(128) DEFAULT '', fname VARCHAR2(64) DEFAULT '', lname VARCHAR2(64) DEFAULT '', description VARCHAR2(64) DEFAULT '', CONSTRAINT speed_dial_speed_dial_idx UNIQUE (username, domain, sd_domain, sd_username) ); CREATE OR REPLACE TRIGGER speed_dial_tr before insert on speed_dial FOR EACH ROW BEGIN auto_id(:NEW.id); END speed_dial_tr; / BEGIN map2users('speed_dial'); END; / opensips-2.2.2/scripts/oracle/standard-create.sql000066400000000000000000000003031300170765700220750ustar00rootroot00000000000000CREATE TABLE version ( table_name VARCHAR2(32), table_version NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT version_t_name_idx UNIQUE (table_name) ); BEGIN map2users('version'); END; / opensips-2.2.2/scripts/oracle/tls_mgm-create.sql000066400000000000000000000012421300170765700217420ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('tls_mgm','1'); CREATE TABLE tls_mgm ( id VARCHAR2(64) PRIMARY KEY, address VARCHAR2(64), type NUMBER(10), method VARCHAR2(16), verify_cert NUMBER(10), require_cert NUMBER(10), certificate VARCHAR2(255), private_key VARCHAR2(255), crl_check_all NUMBER(10), crl_dir VARCHAR2(255), ca_list VARCHAR2(255), ca_dir VARCHAR2(255), cipher_list VARCHAR2(255), dh_params VARCHAR2(255), ec_curve VARCHAR2(255) ); CREATE OR REPLACE TRIGGER tls_mgm_tr before insert on tls_mgm FOR EACH ROW BEGIN auto_id(:NEW.id); END tls_mgm_tr; / BEGIN map2users('tls_mgm'); END; / opensips-2.2.2/scripts/oracle/uri_db-create.sql000066400000000000000000000010171300170765700215440ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('uri','2'); CREATE TABLE uri ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', uri_user VARCHAR2(64) DEFAULT '', last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), CONSTRAINT uri_account_idx UNIQUE (username, domain, uri_user) ); CREATE OR REPLACE TRIGGER uri_tr before insert on uri FOR EACH ROW BEGIN auto_id(:NEW.id); END uri_tr; / BEGIN map2users('uri'); END; / opensips-2.2.2/scripts/oracle/userblacklist-create.sql000066400000000000000000000020661300170765700231540ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('userblacklist','2'); CREATE TABLE userblacklist ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', prefix VARCHAR2(64) DEFAULT '', whitelist NUMBER(5) DEFAULT 0 NOT NULL ); CREATE OR REPLACE TRIGGER userblacklist_tr before insert on userblacklist FOR EACH ROW BEGIN auto_id(:NEW.id); END userblacklist_tr; / BEGIN map2users('userblacklist'); END; / CREATE INDEX ORA_userblacklist_idx ON userblacklist (username, domain, prefix); INSERT INTO version (table_name, table_version) values ('globalblacklist','2'); CREATE TABLE globalblacklist ( id NUMBER(10) PRIMARY KEY, prefix VARCHAR2(64) DEFAULT '', whitelist NUMBER(5) DEFAULT 0 NOT NULL, description VARCHAR2(255) DEFAULT NULL ); CREATE OR REPLACE TRIGGER globalblacklist_tr before insert on globalblacklist FOR EACH ROW BEGIN auto_id(:NEW.id); END globalblacklist_tr; / BEGIN map2users('globalblacklist'); END; / CREATE INDEX ORA_globalblacklist_idx ON globalblacklist (prefix); opensips-2.2.2/scripts/oracle/usrloc-create.sql000066400000000000000000000022111300170765700216040ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('location','1011'); CREATE TABLE location ( contact_id BIGINT(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT NULL, contact VARCHAR2(255) DEFAULT '', received VARCHAR2(128) DEFAULT NULL, path VARCHAR2(255) DEFAULT NULL, expires DATE DEFAULT to_date('2020-05-28 21:32:15','yyyy-mm-dd hh24:mi:ss'), q NUMBER(10,2) DEFAULT 1.0 NOT NULL, callid VARCHAR2(255) DEFAULT 'Default-Call-ID', cseq NUMBER(10) DEFAULT 13 NOT NULL, last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), flags NUMBER(10) DEFAULT 0 NOT NULL, cflags VARCHAR2(255) DEFAULT NULL, user_agent VARCHAR2(255) DEFAULT '', socket VARCHAR2(64) DEFAULT NULL, methods NUMBER(10) DEFAULT NULL, sip_instance VARCHAR2(255) DEFAULT NULL, attr VARCHAR2(255) DEFAULT NULL, CONSTRAINT location_account_contact_idx UNIQUE (username, domain, contact, callid) ); CREATE OR REPLACE TRIGGER location_tr before insert on location FOR EACH ROW BEGIN auto_id(:NEW.id); END location_tr; / BEGIN map2users('location'); END; / opensips-2.2.2/scripts/osipsconsole000077500000000000000000006161761300170765700175410ustar00rootroot00000000000000#!/usr/bin/perl # #$Id$ # # Copyright (C) 2008-2009 Voice Sistem S.R.L # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # History: # ------- # 2008-10-10 first version (iulia_bublea) # #use strict; #use warnings; use Term::ReadLine; use DBI; use POSIX; use Frontier::RPC2; use IO::Socket; use Socket; #use Net::IP; #use BerkeleyDB; #use Config::General; ####variables that need to be changed ##### ------------------------------------------------ ##### # ####path to the directory where opensips binaries are... my $PATH_BIN = "./"; my $PATH_CTLRC = "./scripts/"; my $PATH_ETC = "./etc/"; my $PATH_LIBS = "./scripts/"; my $PATH_SHARE = "./scripts/"; my $OSIPSCONSOLERC = ""; ### include config files if ( -e $PATH_CTLRC . "osipsconsolerc" ) { $OSIPSCONSOLERC = $PATH_CTLRC . "osipsconsolerc"; &include_osipsconsolerc(); } if ( -e "~/.osipsconsolerc" ) { $OSIPSCONSOLERC = "~/.osipsconsolerc"; &include_osipsconsolerc(); } #$conf = new Config::General("$OSIPSCONSOLERC"); #my %config = $conf->getall; my $OSIPSBIN = $PATH_BIN . "opensips"; my $HISTORY_FILE = "/tmp/osipsconsole_history"; my $argnum = $#ARGV + 1; ### aliases configuration ( DB | UL ) my $ALIASES_TYPE = "DB"; my ($socket, @ACL_GROUPS, @arr, $var_name, @OSIPS, $STORE_PLAINTEXT_PW, $DUMP_CMD, $LOAD_CMD, $HA1, $HA1B, $sth, $dbh, @row, $RET, $RES, $DBENGINELOADED, $cmd_fifo, $CHROOT_DIR, $input, $response, $CHECK_SUB, $USRLOC_TABLE, @content, $line, @list, @cmd, @output, $command); my $ETCDIR = " "; my $MI_CONNECTOR = ""; my $OSIPS_FIFO = ""; my $OSIPS_UNIXSOCK = ""; my $OSIPSUNIX = ""; my $OSIPSIP = ""; my $OSIPS_PORT = ""; my $fifo_reply_file = "fifo_reply"; my $fifo_reply_path = "/tmp/" . $fifo_reply_file; my $PID_FILE = ""; my $DBENGINE = ""; my $SIP_DOMAIN = ""; my $VERIFY_ACL = 0; my $DBNAME = ""; my $DBHOST = ""; my $DBPORT = ""; my $DBRWUSER = ""; my $DBROOTUSER = ""; my $DBRWPW = ""; my $CTLENGINE = ""; my $EGREP = ""; my $SYSLOG = ""; my $STARTOPTIONS = ""; my $TOOLPATH = ""; my $AWK = ""; my $MD5 = ""; my $SED = ""; my $LAST_LINE = ""; my $EXPR = ""; my $WATCH_PERIOD = ""; ##### ----------------------------------------------- ##### #### Defined values my $ALL_METHODS=4294967295; my $USERNAME_RE="[-a-zA-Z0-9&=\+\$,;\?/_\.\!~\*'\(\)]+"; my $OSIPSUSER = ""; my $OSIPSDOMAIN = ""; my $CTLENGINELOADED = 0; my $ENABLE_ALIASES = 0; my $ALIAS_EXISTS = 0; ######-----------------------------------------------####### ##database specific variables # my @STANDARD_TABLES = ('version', 'acc', 'dbaliases', 'domain', 'grp', 'uri', 'speed_dial', 'gw', 'subscriber', 'location', 're_grp', 'address', 'missed_calls', 'usr_preferences', 'aliases', 'silo', 'dialog', 'dispatcher', 'dialplan', 'dr_gateways','dr_rules','dr_carriers', 'dr_groups','rtpproxy_sockets','load_balancer'); my @STANDARD_MODULES = ('standard', 'acc', 'domain', 'group', 'permissions', 'registrar', 'usrloc', 'msilo', 'alias_db', 'uri_db', 'speeddial', 'avpops', 'auth_db', 'dialog', 'dispatcher', 'dialplan','drouting','rtpproxy','load_balancer' ); my @EXTRA_TABLES = ('imc_members', 'imc_rooms', 'cpl', 'sip_trace', 'domainpolicy', 'carrierroute', 'route_tree', 'carrierfailureroute', 'userblacklist', 'globalblacklist'); my @EXTRA_MODULES = ('imc', 'cpl', 'siptrace', 'domainpolicy', 'carrierroute', 'userblacklist', 'b2b', 'registrant', 'call_center', 'fraud_detection', 'emergency_routing', 'emergency_report'); my @PRESENCE_TABLES = ('presentity', 'active_watchers', 'watchers', 'xcap', 'pua'); my @PRESENCE_RLS_TABLES = ('rls_presentity', 'rls_watchers'); my $HAS_EXTRA = "NO"; #my $NO_USER_INIT = "NO"; my $MYSQL = ""; my $PGSQL = ""; my $DBTEXTCMD = $PATH_LIBS."/dbtextdb/dbtextdb.py"; # path to the database schemas my $DB_SCHEMA = ""; my $DATA_DIR = $PATH_SHARE; my $DBTEXT_PATH = $PATH_SHARE."/dbtext/opensips"; # path to the db_berkeley database my $DB_PATH = $ENV{'DB_PATH'}; if ( not defined $DB_PATH) { $DB_PATH = $PATH_ETC."/opensips/db_berkeley"; } my $PW = ""; my $ALIAS_DB_EXISTS = ""; my $ALIAS_UL_EXISTS = ""; my $PASS = ""; my $result = 0; ####CLI control variables my @command_list = ('acl'=> ('show','grant','revoke'), 'add'=>'add', 'avp'=>('list','add','rm'), 'passwd'=>'passwd', 'rm'=>'rm', 'alias'=>('show','rm','add'), 'usrloc'=>('show','rm','add'), 'ul'=>('show','rm','add'), 'alias_db'=> ('list','show','add','rm'), 'aliasdb'=> ('list','show','add','rm'), 'domain'=> ('reload','show','showdb','add','rm'), 'address'=> ('show','dump','reload','add','rm'), 'fifo'=>'fifo', 'cr' => ('show','reload','dump','addrt','rmrt','addcarrier','rmcarrier'), 'dispatcher'=>('show','addgw','rmgw','reload','dump'), 'monitor'=>'monitor', 'console'=>'monitor', 'moni'=>'monitor', 'con'=>'monitor', 'online'=>'online', 'ping'=>'ping', 'ps'=>'ps', 'restart' => 'restart', 'rpid'=> ('add','rm','show'), 'speeddial'=> ('list','show','add','rm'), 'speed_dial'=> ('list','show','add','rm'), 'tls'=>('rootCA','userCERT'), 'start' => 'start', 'stop' => 'stop', 'unixsock'=>'unixsock', 'udp'=>'udp', 'version'=>'version', 'xmlrpc'=>'xmlrpc', #'db'=>('exec','roexec','run','rorun','show'), 'dialplan'=>('show','addrule','rm','rmpid','rmrule','reload'), 'db'=>('migrate','copy','backup','restore','create','presence','extra','reinit'), 'bdb'=>('list','ls','cat','swap','append','newappend','export','import','migrate'), 'db_berkeley'=>('list','ls','cat','swap','append','newappend','export','import','migrate'), 'dr'=>('gateway', 'rules'), 'gateway'=>('add','rm','list'), 'rules'=>('add','rm','list'), 'help'=>'help' ); # ##### ------------------------------------------------ ##### ###paths are either initialized in the script or in the file osipsconsolerc sub include_osipsconsolerc() { open (FILE,"< $OSIPSCONSOLERC") || die "Can't Open File: osipsconsolerc\n"; while ($line=){ if ( $line !~ m/^(\s*\w+)/g ) { next; } else { #print $line; unshift(@content,$line); } } close(FILE); } while ( $#content gt -1 ){ my $res = shift(@content); my @arr = split("=",$res); chomp($arr[1]); #initializing global vars if ( $arr[0] eq "PID_FILE" ){ $PID_FILE = $arr[1]; } elsif ( $arr[0] eq "SYSLOG" ) { $SYSLOG = $arr[1]; } elsif ( $arr[0] eq "STARTOPTIONS" ) { $STARTOPTIONS = $arr[1]; } elsif ( $arr[0] eq "ALIASES_TYPE" ) { $ALIASES_TYPE = $arr[1]; if ( $ALIASES_TYPE =~ /^UL/ ) { $ENABLE_ALIASES = 1; } elsif ( $ALIASES_TYPE =~ /DB/ ) { $ENABLE_ALIASES = 2; } } elsif ( $arr[0] eq "MI_CONNECTOR" ) { $MI_CONNECTOR = $arr[1]; @list = split(":",$arr[1]); $CTLENGINE = $list[0]; $CTLENGINELOADED = 1; if ($CTLENGINE =~ /^FIFO$/) { $OSIPS_FIFO = $list[1]; } elsif ($CTLENGINE =~ /^UNIXSOCK$/){ $OSIPS_UNIXSOCK = $list[1]; $OSIPSUNIX = "opensipsunix"; } elsif ($CTLENGINE =~ /^UDP$/){ $OSIPSIP = $list[1]; $OSIPS_PORT = $list[2]; } elsif ($CTLENGINE =~ /^XMLRPC$/){ $OSIPSIP = $list[1]; $OSIPS_PORT = $list[2]; } } ##### ------------------------------------------------ ##### ### ACL name verification elsif ( $arr[0] eq "VERIFY_ACL" ) { $VERIFY_ACL = $arr[1]; } elsif ( $arr[0] eq "ACL_GROUPS" ) { @ACL_GROUPS = split(" ",$arr[1]); } ##### ----------------------------------------------- ##### ### common variables and functions for SQL engines if ( $arr[0] eq "DBENGINE" ) { if ( $arr[1] =~ /^\s*MYSQL/ ) { $DBENGINE = "mysql"; } elsif ( $arr[1] =~ /^\s*PGSQL/ ) { $DBENGINE = "Pg"; } else { $DBENGINE = $arr[1]; } } elsif ( $arr[0] eq "DBNAME" ) { $DBNAME = $arr[1]; } elsif ( $arr[0] eq "DBHOST" ) { $DBHOST = $arr[1]; } elsif ( $arr[0] eq "DBPORT" ) { $DBPORT = $arr[1]; } elsif ( $arr[0] eq "DBRWUSER" ) { $DBRWUSER = $arr[1] } elsif ( $arr[0] eq "DBROOTUSER" ) { $DBROOTUSER = $arr[1]; } elsif ( $arr[0] eq "DBRWPW" ) { $DBRWPW = $arr[1]; } elsif ( $arr[0] eq "SIP_DOMAIN" ) { $SIP_DOMAIN = $arr[1]; } elsif ( $arr[0] eq "STORE_PLAINTEXT_PW" ) { $STORE_PLAINTEXT_PW = $arr[1]; } elsif ( $arr[0] eq "AWK" ) { $AWK = $arr[1]; } elsif ( $arr[0] eq "GREP" ) { $EGREP = $arr[1]; } elsif ( $arr[0] eq "SED" ) { $SED = $arr[1]; } } if ( $PID_FILE eq ""){ $PID_FILE = "/var/run/opensips.pid"; } if ( $SYSLOG eq "" ) { $SYSLOG = 1; # 0=output to console, 1=output to syslog } if ( $STARTOPTIONS eq "" ) { $STARTOPTIONS = ""; # for example -dddd } ##### ------------------------------------------------ ##### ### aliases configuration # if ( $ALIASES_TYPE =~ /^UL/ ) { $ENABLE_ALIASES = 1; } elsif ( $ALIASES_TYPE =~ /DB/ ) { $ENABLE_ALIASES = 2; } # ##### ------------------------------------------------ ##### ### CTLENGINE # if ( $MI_CONNECTOR eq "" ) { $CTLENGINE = "FIFO"; $OSIPS_FIFO = "/tmp/opensips_fifo"; $CTLENGINELOADED = 1; } if ( $OSIPSUNIX eq "" ) { $OSIPSUNIX = "opensipsunix"; } ##### ------------------------------------------------ ##### ### ACL name verification if ( ! $VERIFY_ACL ) { $VERIFY_ACL = 1; } if ( $#ACL_GROUPS lt 0 ) { @ACL_GROUPS=("local", "ld", "int", "voicemail", "free-pstn"); } ##### ----------------------------------------------- ##### ### common variables and functions for SQL engines if ( $DBENGINE eq "" ) { $DBENGINE = "mysql"; } if ( $DBNAME eq "" ) { $DBNAME = "opensips"; } if ( $DBHOST eq "" ) { $DBHOST = "localhost"; } if ( $DBPORT eq "" ) { $DBPORT = "3306"; } if ( $DBRWUSER eq "" ) { $DBRWUSER = "opensips"; } if ( $DBRWPW eq "" ) { $DBRWPW = 'opensipsrw'; } # full privileges SQL user if ( $DBROOTUSER eq "" ) { $DBROOTUSER = "root"; } ### force values for variables in this section # you better set the variables in ~/.osipsconsolerc if ( -z $ETCDIR ) { $ETCDIR=$PATH_ETC."/opensips"; } if ( $EGREP eq "" ) { &locate_tool("egrep"); if ( !-e $TOOLPATH ){ # now error, but we can look for alternative names if it is the case print "error: 'egrep' tool not found: set \$EGREP variable to correct tool path\n"; } else { $EGREP = $TOOLPATH; } } if ($AWK eq "") { &locate_tool('awk'); if ( !-e $TOOLPATH ) { # now error, but we can look for alternative names if it is the case print "error: 'awk' tool not found: set \$AWK variable to correct tool path\n"; } else { $AWK = $TOOLPATH; } } if ( $MD5 eq "") { &locate_tool ('md5sum md5'); if ( !-e $TOOLPATH ) { # now error, but we can look for alternative names if it is the case print "error: 'md5sum' or 'md5' tool not found: set MD5 variable to correct tool path\n"; } else { $MD5 = $TOOLPATH; } } if ( $EXPR eq "" ) { &locate_tool('expr'); if ( !-e $TOOLPATH ) { # now error, but we can look for alternative names if it is the case print "error: 'expr' tool not found: set EXPR variable to correct tool path\n" } else { $EXPR= $TOOLPATH; } } ##### ----------------------------------------------- ##### ### binaries { if ( ($DBENGINE eq "mysql") && ( $MYSQL eq "" ) ) { &locate_tool('mysql'); if ( !-e $TOOLPATH ) { print "Error: 'mysql' tool not found: set MYSQL variable to correct tool path"; } $MYSQL = $TOOLPATH; } } ##### ----------------------------------------------- ##### ### binaries if ( ($DBENGINE eq "Pg") && ($PGSQL eq "" ) ) { &locate_tool('psql'); if ( !-e $TOOLPATH ) { print "Error: 'psql' tool not found: set PGSQL variable to correct tool path\n"; return; } $PGSQL = $TOOLPATH; } #berkeley db utility program that writes out db to plain text #small hack to autodetect the db dump command, debian prefix the version.. system("which db_dump > /dev/null"); if ( $? == 0 ) { $DUMP_CMD = "db_dump"; } system("which db4.4_dump > /dev/null"); if ( $? == 0 ) { $DUMP_CMD = "db4.4_dump"; } system("which db4.5_dump > /dev/null"); if ( $? == 0 ) { $DUMP_CMD = "db4.5_dump"; } system("which db4.6_dump > /dev/null"); if ( $? == 0 ) { $DUMP_CMD = "db4.6_dump"; } #berkeley db utility program that imports data from plain text file #small hack to autodetect the db load command, debian prefix the version.. system("which db_load > /dev/null"); if ( $? == 0 ) { $LOAD_CMD = "db_load"; } system("which db4.4_load > /dev/null"); if ( $? == 0 ) { $LOAD_CMD = "db4.4_load"; } system("which db4.5_load > /dev/null"); if ( $? == 0 ) { $LOAD_CMD = "db4.5_load"; } system("which db4.6_load > /dev/null"); if ( $? == 0 ) { $LOAD_CMD = "db4.6_load"; } # period in which stats are reprinted if ( $WATCH_PERIOD eq "" ) { $WATCH_PERIOD = 2; } ##### ----------------------------------------------- ##### #### database tables for SQL databases # UsrLoc Table my $UL_TABLE = $ENV{'UL_TABLE'}; if ( not defined $UL_TABLE) { $UL_TABLE="location"; } my %ul_table = ('USER_COLUMN' => 'username', 'DOMAIN_COLUMN' => 'domain', 'CALLID_COLUMN'=> 'callid' ); # subscriber table my $SUB_TABLE = $ENV{'SUB_TABLE'}; if ( not defined $SUB_TABLE ) { $SUB_TABLE='subscriber'; } my %sub_table = ('REALM_COLUMN' => 'domain', 'HA1_COLUMN' => 'ha1', 'HA1B_COLUMN'=> 'ha1b', 'PASSWORD_COLUMN' => 'password', 'RPID_COLUMN' => 'rpid', 'SUBSCRIBER_COLUMN' => 'username', 'PHP_LIB_COLUMN' => 'phplib_id', 'EMAIL_ADDRESS' => 'email_address' ); # groups table my $ACL_TABLE = $ENV{'ACL_TABLE'}; if ( not defined $ACL_TABLE ) { $ACL_TABLE = 'grp'; } my %acl_table = ( 'ACL_USER_COLUMN' => 'username', 'ACL_DOMAIN_COLUMN' => 'domain', 'ACL_GROUP_COLUMN' => 'grp', 'ACL_MODIFIED_COLUMN' => 'last_modified', ); # aliases table my $ALS_TABLE = $ENV{'ALS_TABLE'}; if ( not defined $ALS_TABLE ) { $ALS_TABLE = 'aliases'; } my %als_table = ('A_USER_COLUMN' => 'username', 'A_CONTACT_COLUMN' => 'contact', 'A_EXPIRES_COLUMN' => 'expires', 'A_Q_COLUMN' => 'q', 'A_CALLID_COLUMN' => 'callid', 'A_CSEQ_COLUMN' => 'cseq', 'A_LAST_MODIFIED_COLUMN' => 'last_modified' ); # domain table my $DOMAIN_TABLE = $ENV{'DOMAIN_TABLE'}; if ( not defined $DOMAIN_TABLE ) { $DOMAIN_TABLE = 'domain'; } my %domain_table = ('DO_DOMAIN_COLUMN' => 'domain', 'DO_LAST_MODIFIED_COLUMN' => 'last_modified', ); # route_tree table my $ROUTE_TREE_TABLE = $ENV{'ROUTE_TREE_TABLE'}; if ( not defined $ROUTE_TREE_TABLE ) { $ROUTE_TREE_TABLE = 'route_tree'; } my %route_tree_table = ('CARRIERROUTE_ROUTE_TREE_PREFIX_COLUMN' =>'id', 'CARRIERROUTE_ROUTE_TREE_CARRIER_COLUMN' => 'carrier' ); # carrierroute table my $CARRIERROUTE_TABLE = $ENV{'CARRIERROUTE_TABLE'}; if ( not defined $CARRIERROUTE_TABLE ) { $CARRIERROUTE_TABLE = 'carrierroute'; } my %carrierroute_table = ('CARRIERROUTE_CARRIERROUTE_PREFIX_COLUMN' => 'id', 'CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN' => 'carrier', 'CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN' => 'scan_prefix', 'CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN' => 'domain', 'CARRIERROUTE_CARRIERROUTE_PROB_COLUMN' => 'prob', 'CARRIERROUTE_CARRIERROUTE_STRIP_COLUMN' => 'strip', 'CARRIERROUTE_CARRIERROUTE_REWRITE_HOST_COLUMN' => 'rewrite_host', 'CARRIERROUTE_CARRIERROUTE_REWRITE_PREFIX_COLUMN' => 'rewrite_prefix', 'CARRIERROUTE_CARRIERROUTE_REWRITE_SUFFIX_COLUMN' => 'rewrite_suffix', 'CARRIERROUTE_CARRIERROUTE_COMMENT_COLUMN' => 'description', 'CARRIERROUTE_CARRIERROUTE_FLAGS_COLUMN' => 'flags', 'CARRIERROUTE_CARRIERROUTE_MASK_COLUMN' => 'mask' ); # URI table my $URI_TABLE = $ENV{'URI_TABLE'}; if ( not defined $URI_TABLE ) { $URI_TABLE = 'uri' } my %uri_table = ('URIUSER_COLUMN' => 'uri_user', 'MODIFIED_COLUMN' => 'last_modified' ); # dbaliases table my $DA_TABLE = $ENV{'DA_TABLE'}; if ( not defined $DA_TABLE ) { $DA_TABLE = 'dbaliases'; } my %da_table = ('DA_USER_COLUMN' => 'username', 'DA_DOMAIN_COLUMN' => 'domain', 'DA_ALIAS_USER_COLUMN' => 'alias_username', 'DA_ALIAS_DOMAIN_COLUMN' => 'alias_domain' ); # speeddial table my $SD_TABLE = $ENV{'SD_TABLE'}; if ( not defined $SD_TABLE ) { $SD_TABLE = 'speed_dial' } my %sd_table = ('SD_USER_COLUMN' => 'username', 'SD_DOMAIN_COLUMN' => 'domain', 'SD_SD_USER_COLUMN' => 'sd_username', 'SD_SD_DOMAIN_COLUMN' => 'sd_domain', 'SD_NEW_URI_COLUMN' => 'new_uri', 'SD_DESC_COLUMN' => 'description' ); # avp table my $AVP_TABLE = $ENV{'AVP_TABLE'}; if ( not defined $AVP_TABLE ) { $AVP_TABLE = 'usr_preferences'; } my %avp_table = ('AVP_UUID_COLUMN' => 'uuid', 'AVP_USER_COLUMN' => 'username', 'AVP_DOMAIN_COLUMN' => 'domain', 'AVP_ATTRIBUTE_COLUMN' => 'attribute', 'AVP_VALUE_COLUMN' => 'value', 'AVP_TYPE_COLUMN' => 'type', 'AVP_MODIFIED_COLUMN' => 'last_modified' ); # address table my $ADDRESS_TABLE = $ENV{'ADDRESS_TABLE'}; if ( not defined $ADDRESS_TABLE ) { $ADDRESS_TABLE = 'address'; } my %address_table = ('ADDRESS_ID_COLUMN' => 'ip', 'ADDRESS_GRP_COLUMN' => 'grp', 'ADDRESS_IP_COLUMN' => 'ip', 'ADDRESS_MASK_COLUMN' => 'mask', 'ADDRESS_PORT_COLUMN' => 'port', 'ADDRESS_PROTO_COLUMN' => 'proto', 'ADDRESS_PATTERN_COLUMN' => 'pattern', 'ADDRESS_CONTEXT_INFO_COLUMN' => 'context_info' ); # dispatcher tables my $DISPATCHER_TABLE = $ENV{'DISPATCHER_TABLE'}; if ( not defined $DISPATCHER_TABLE ){ $DISPATCHER_TABLE = 'dispatcher'; } my %dispatcher_table = ('DISPATCHER_ID_COLUMN' => 'id', 'DISPATCHER_SETID_COLUMN' => 'setid', 'DISPATCHER_DESTINATION_COLUMN' => 'destination', 'DISPATCHER_SOCKET_COLUMN' => 'socket', 'DISPATCHER_STATE_COLUMN' => 'state', 'DISPATCHER_WEIGHT_COLUMN' => 'weight', 'DISPATCHER_ATTRS_COLUMN' => 'attrs', 'DISPATCHER_DESCRIPTION_COLUMN' => 'description' ); # dialplan tables my $DIALPLAN_TABLE = $ENV{'DIALPLAN_TABLE'}; if ( not defined $DIALPLAN_TABLE ) { $DIALPLAN_TABLE = 'dialplan'; } my %dialplan_table = ('DIALPLAN_ID_COLUMN' => 'id', 'DIALPLAN_DPID_COLUMN' => 'dpid', 'DIALPLAN_PR_COLUMN' => 'pr', 'DIALPLAN_MATCH_OP_COLUMN' => 'match_op', 'DIALPLAN_MATCH_EXP_COLUMN' => 'match_exp', 'DIALPLAN_MATCH_LEN_COLUMN' => 'match_len', 'DIALPLAN_SUBST_EXP_COLUMN' => 'subst_exp', 'DIALPLAN_REPL_EXP_COLUMN' => 'repl_exp', 'DIALPLAN_ATTRS_COLUMN' => 'attrs' ); #drouting tables my $DR_GW_TABLE = $ENV{'DR_GW_TABLE'}; if ( not defined $DR_GW_TABLE ) { $DR_GW_TABLE = 'dr_gateways'; } my %dr_gw_table = ('DR_GW_GWID_COLUMN' => 'gwid', 'DR_GW_ADDRESS_COLUMN' => 'address', 'DR_GW_TYPE_COLUMN' => 'type', 'DR_GW_STRIP_COLUMN' => 'strip', 'DR_GW_PRI_PREFIX_COLUMN' => 'pri_prefix', 'DR_GW_DESCRIPTION_COLUMN' => 'description' ); # dr_rules table my $DR_RULES_TABLE = $ENV{'DR_RULES_TABLE'}; if ( not defined $DR_RULES_TABLE ) { $DR_RULES_TABLE = 'dr_rules'; } my %dr_rules_table = ( 'DR_RULES_RULEID_COLUMN' => 'ruleid', 'DR_RULES_GROUPID_COLUMN' => 'groupid', 'DR_RULES_PREFIX_COLUMN' => 'prefix', 'DR_RULES_TIMEREC_COLUMN' => 'timerec', 'DR_RULES_PRIORITY_COLUMN' => 'priority', 'DR_RULES_ROUTEID_COLUMN' => 'routeid', 'DR_RULES_GWLIST_COLUMN' => 'gwlist', 'DR_RULES_DESCRIPTION_COLUMN' => 'description' ); ##### ----------------------------------------------- ##### ### path to useful tools sub locate_tool() { if ( -x "/usr/bin/which" ) { $TOOLPATH = `which @_`; chomp($TOOLPATH); #if ( $TOOLPATH ) { # return $TOOLPATH; #} return; } # look in common locations if ( -x "/usr/bin/".@_ ){ $TOOLPATH = "/usr/bin/".@_; return; } if ( -x "/bin/".$_[0] ) { $TOOLPATH = "/bin/".$_[0]; return; } if ( -x "/usr/local/bin/".$_[0] ) { $TOOLPATH = "/usr/local/bin/$_[0]"; return; } $TOOLPATH = ""; return; } # ##### ------------------------------------------------ ##### ### usage functions # #online sub usage_online() { print " -- command 'online' - dump online users from memory\n" . "online ............................. display online users\n"; } #monitor sub usage_opensips_monitor() { print " -- command 'monitor' - show internal status\n" . "monitor ............................ show server's internal status\n"; } #ping sub usage_ping() { print " -- command 'ping' - ping a SIP URI (OPTIONS)\n" . "ping ......................... ping with SIP OPTIONS\n"; } #usrloc sub usage_usrloc() { print " -- command 'ul|alias' - manage user location or aliases\n" . "ul show []................ show in-RAM online users\n" . "ul show --brief..................... show in-RAM online users in short format\n" . "ul rm [].... delete user's usrloc entries\n" . "ul add ............ introduce a permanent usrloc entry\n" . "ul add .. introduce a temporary usrloc entry\n" ; } #base - start|stop|restart sub usage_base() { print " -- command 'start|stop|restart'\n" . "restart ............................ restart OpenSIPS\n" . "start .............................. start OpenSIPS\n" . "stop ............................... stop OpenSIPS\n"; } #tls sub usage_tls() { print " -- command 'tls'\n" . "tls rootCA [] .......... creates new rootCA\n" . "tls userCERT [] ... creates user certificate\n" . "\t\t\t\tdefault is $ETCDIR/tls\n"; } #acl sub usage_acl() { print " -- command 'acl' - manage access control lists (acl)\n" . "acl show [] .............. show user membership\n" . "acl grant ....... grant user membership (*)\n" . "acl revoke [] .... revoke user membership(s) (*)\n"; } #cr sub usage_cr() { print " -- command 'cr' - manage carrierroute tables\n" . "cr show ....................................................... show tables\n" . "cr reload ..................................................... reload tables\n" . "cr dump ....................................................... show in memory tables\n" . "cr addrt ..................... add a tree\n" . "cr rmrt ....................................... rm a tree\n" . "cr addcarrier ................\n" . "\t\t[] [] [] [] ...............\n" . "\t\t[] [] [] .........................add a carrier\n" . "\t\t(prob, strip, rewrite_prefix, rewrite_suffix,...................\n" . "\t\tflags, mask and comment are optional arguments) ...............\n" . "cr rmcarrier ................ rm a carrier\n"; } #rpid sub usage_rpid() { print " -- command 'rpid' - manage Remote-Party-ID (RPID)\n" . "rpid add ......... add rpid for a user (*)\n" . "rpid rm ................. set rpid to NULL for a user (*)\n" . "rpid show ............... show rpid of a user\n"; } #subscriber - add|passwd|rm sub usage_subscriber() { print " -- command 'add|passwd|rm' - manage subscribers\n" . "add .......... add a new subscriber (*)\n" . "passwd ......... change user's password (*)\n" . "rm ...................... delete a user (*)\n"; } #address sub usage_address() { print " -- command 'add|dump|reload|rm|show' - manage address\n" . "address show ...................... show db content\n" . "address dump ...................... show cache content\n" . "address reload .................... reload db table into cache\n" . "address add [] []\n" . " ....................... add a new entry\n" . " ....................... (from_pattern and tag are optional arguments)\n" . "address rm ..remove all entries for the given grp ip mask and port\n"; } #dispatcher sub usage_dispatcher() { print " -- command 'dispatcher' - manage dispatcher\n" . "* Examples: dispatcher addgw 1 sip:1.2.3.1:5050 '' 0 50 'og1' 'Outbound Gateway1'\n" . "* dispatcher addgw 2 sip:1.2.3.4:5050 '' 0 50 'og2' 'Outbound Gateway2'\n" . "* dispatcher rmgw 4\n" . "dispatcher show ..................... show dispatcher gateways\n" . "dispatcher reload ................... reload dispatcher gateways\n" . "dispatcher dump ..................... show in memory dispatcher gateways\n" . "dispatcher addgw [description]\n" . " .......................... add gateway\n" . "dispatcher rmgw ................ delete gateway\n"; } # dbtext don't support db_ops sub usage_db_ops() { print " -- command 'db' - database operations\n" . "db exec ..................... execute SQL query\n" . "db roexec ................. execute read-only SQL query\n" . "db run ......................... execute SQL query from \$id variable\n" . "db rorun ....................... execute read-only SQL query from\n" . "\t\t\t\t\$id variable\n" . "db show
..................... display table content\n"; } # speeddial sub usage_speeddial() { print " -- command 'speeddial' - manage speed dials (short numbers)\n" . "speeddial show ....... show speeddial details\n" . "speeddial list ............. list speeddial for uri\n" . "speeddial add [] ... \n" . "\t\t........................... add a speedial (*)\n" . "speeddial rm ....... remove a speeddial (*)\n" . "speeddial help ...................... help message\n" . "\t\t- , must be an AoR (username\@domain)\n" . "\t\t- must be an AoR (username\@domain)\n" . "\t\t- must be a SIP AoR (sip:username\@domain)\n" . "\t\t- a description for speeddial\n"; } # avp sub usage_avp() { print " -- command 'avp' - manage AVPs\n" . "avp list [-T table] [-u ]\n" . "\t[-a attribute] [-v value] [-t type] ... list AVPs\n" . "avp add [-T table] \n" . "\t ............ add AVP (*)\n" . "avp rm [-T table] [-u ]\n" . "\t[-a attribute] [-v value] [-t type] ... remove AVP (*)\n" . "avp help .................................. help message\n" . "\t- -T - table name\n" . "\t- -u - SIP id or unique id\n" . "\t- -a - AVP name\n" . "\t- -v - AVP value\n" . "\t- -t - AVP name and type (0 (str:str), 1 (str:int),\n" . "\t\t\t\t2 (int:str), 3 (int:int))\n" . "\t\t- must be an AoR (username\@domain)\n" . "\t\t- must be a string but not AoR\n"; } # alias_db sub usage_alias_db() { print " -- command 'alias_db' - manage database aliases\n" . "alias_db show .............. show alias details\n" . "alias_db list ............. list aliases for uri\n" . "alias_db add ...... add an alias (*)\n" . "alias_db rm ................ remove an alias (*)\n" . "alias_db help ...................... help message\n" . "\t\t- must be an AoR (username\@domain)\n" . "\t\t- must be an AoR (username\@domain)\n"; } #domain sub usage_domain() { print " -- command 'domain' - manage local domains\n" . "domain reload ....................... reload domains from disk\n" . "domain show ......................... show current domains in memory\n" . "domain showdb ....................... show domains in the database\n" . "domain add ................. add the domain to the database\n" . "domain rm .................. delete the domain from the database\n"; } # fifo sub usage_fifo() { print " -- command 'fifo'\n" . "fifo ............................... send raw FIFO command\n"; } sub usage_dialplan() { print " -- command 'dialplan' - manage dialplans\n" . "dialplan show .............. show dialplan tables\n" . "dialplan reload ................... reload dialplan tables\n" . "dialplan addrule \n" . "\t\t\t \n" . "\t\t\t\t\t.................... add a rule\n" . "dialplan rm ....................... removes the entire dialplan table\n" . "dialplan rmdpid ............ removes all the gived dpid entries\n" . "dialplan rmrule ..... removes all the gived dpid/prio entries\n"; } sub usage_unixsock() { print " -- command 'unixsock'\n" . "unixsock ........................... send raw unixsock command\n"; } # common functions sub usage_db() { my $COMMAND=`basename $0`; print " -- command 'db' - manage OpenSIPS databases\n". "db create ....(creates a new database)\n". "db presence .................................(adds the presence related tables)\n". "db extra ....................................(adds the extra tables)\n". "db migrate ................(migrates DB from 1.8 to 1.9)\n". "db drop ......(!entirely deletes tables!)\n". "db reinit ....(!entirely deletes and than re-creates tables!)\n". "db backup ............................(dumps current database to file)\n". "db restore ...........................(restores tables from a file)\n". "db copy ............................(creates a new db from an existing one)\n\n". "\tif you want to manipulate database as other database user than\n" . "\troot, want to change database name from default value \"$DBNAME\",\n" . "\tor want to use other values for users and password, edit the\n" . "\t\"config vars\" section of the command $COMMAND.\n"; } #usage sub berkeley_usage() { print "Script for maintaining OpenSIPS Berkeley DB tables\n". "bdb | db_berkeley list (lists the underlying db files in DB_PATH)\n". "bdb | db_berkeley cat (db_dump the underlying db file to STDOUT)\n". "bdb | db_berkeley swap (installs db.new by db -> db.old; db.new -> db)\n". "bdb | db_berkeley append (appends data to an existing db;output DB_PATH/db.new)\n". "bdb | db_berkeley newappend (appends data to a new instance of db; output DB_PATH/db.new)\n". "bdb | db_berkeley export (exports table data to plain-txt files in dump_dir)\n". "bdb | db_berkeley import (imports plain-txt table data and creates new db tables in db_path)\n"; } #usage # droute sub usage_dr() { print " -- command 'droute'\n" . "dr gateway add [] [] \n". " [] [].....\n" . "\t\t\t\t\t\t\t..........................adds new route\n" . "dr gateway rm ................................removes route\n" . "dr gateway list ..............................lists route(s)\n" . "dr gateway list ...........................lists route(s)\n" . "dr gateway list ....................................lists all routes\n" . "dr gateway h........................................droute help\n" . "dr rules add [][] \n". "[][][][].....\n" . "\t\t\t\t\t\t\t\t.............adds new rule(s)\n" . "dr rules rm .................................removes rules\n" . "dr rules list...............................lists rules(s)\n" . "dr rules list ...............................lists rules(s)\n" . "dr rules list ...............................lists rules(s)\n" . "dr rules h..................................dr rules help\n" . "dr h........................................dr help\n"; } # determine host name, typically for use in printing UAC # messages; we use today a simplistic but portable uname -n way -- # no domain name is displayed ; fifo_uac expands !! to host # address only for optional header fields; uname output without # domain is sufficient for informational header fields such as # From # sub get_my_host() { if ( $SIP_DOMAIN eq "") { $SIP_DOMAIN = `uname -n`; return $SIP_DOMAIN; } else { return $SIP_DOMAIN; } } # calculate name and domain of current user sub set_user() { @OSIPS = split ("@",$_[0]); $OSIPSUSER = $OSIPS[0]; $OSIPSDOMAIN = $OSIPS[1]; #print "user:".$OSIPSUSER." domain:".$OSIPSDOMAIN."\n"; if ( ! $OSIPSDOMAIN ) { $OSIPSDOMAIN = $SIP_DOMAIN; return; } if ( ! $OSIPSDOMAIN ) { print "domain unknown: use usernames with domain or set default domain in SIP_DOMAIN\n"; return; } return; } # check the parameter if it is a valid address of record (user@domain) sub check_aor() { if ( $_[0] !~ /^$USERNAME_RE\@.*\..*/ ) { print "error: invalid AoR: " . $_[0] . "> /dev/stderr"; $result = 1; } else { $result = 0; } } # check the parameter if it is a valid address of record (user@domain) sub is_aor() { if ( $_[0] !~ /^$USERNAME_RE\@.*\..*/ ) { $result = 1; } else { $result = 0; } } # check the parameter if it is a valid SIP address of record (sip:user@domain) sub check_sipaor() { if ( $_[0] !~ /sips?:$USERNAME_RE\@.*\..*/ ) { print "error: invalid SIP AoR: ". $_[0] . " > /dev/stderr"; $result = 1; } else { $result = 0; } } # check the parameter if it is a valid SIP URI # quite simplified now -- it captures just very basic # errors sub check_uri() { if ( $_[0] !~ /sips?:($USERNAME_RE\@)?.*\..*/) { print "error: invalid SIP URI: " . $_[0] . " > /dev/stderr"; $result = 1; } else { $result = 0; } } #sub print_status() { # if ( $_[0] !~ /^[1-6][0-9][0-9]/ ) { # print $_[0]; # } else { # print "200 OK\n"; # } #} # params: user, realm, password # # output: HA1 sub gen_ha1(){ $HA1=`echo -n \"$_[0]:$_[1]:$_[2]\" | $MD5 | $AWK '{ print \$1 }'`; if ( $? != 0 ) { print "HA1 calculation failed!"; return; } return $HA1; } # params: user, realm, password # output: HA1B sub gen_ha1b() { $HA1B=`echo -n \"$_[0]@$_[1]:$_[1]:$_[2]\" | $MD5 | $AWK '{ print \$1 }'`; if ( $? != 0 ) { print "HA1B calculation failed!"; return; } return $HA1B; } # params: user, realm, password # output: PHPLIB_ID sub gen_phplib_id() { my $NOW=`date`; my $PHPLIB_ID=`echo -n \"$_[0]$_[1]:$_[2]:$NOW\" | $MD5 | $AWK '{ print \$1 }'`; } # params: user, password # output: HA1, HA1B sub credentials() { system(&set_user(@_)); system(&gen_ha1 ( $OSIPSUSER, $OSIPSDOMAIN, $cmd[2] )); system(&gen_ha1b ( $OSIPSUSER, $OSIPSDOMAIN, $cmd[2] )); return; } # params: user # output: false if exists, true otherwise sub is_user() { system(&set_user($_[0])); print $OSIPSUSER ." " .$OSIPSDOMAIN."\n"; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); my $res = &bdb_select_where($SUB_TABLE,$key); $result = $res; } elsif ( $DBENGINE =~ /^DBTEXT$/) { my $res = `\"$DBTEXTCMD\" \"SELECT COUNT(*) FROM $SUB_TABLE WHERE $sub_table{'SUBSCRIBER_COLUMN'} = \"$OSIPSUSER\" AND $sub_table{'REALM_COLUMN'} = \"$OSIPSDOMAIN\"\" 2>&1`; $result = $res; } else { #prepare the query $sth = $dbh->prepare( "SELECT count(*) FROM $SUB_TABLE WHERE $sub_table{'SUBSCRIBER_COLUMN'} = \'$OSIPSUSER\' AND $sub_table{'REALM_COLUMN'} = \'$OSIPSDOMAIN\' " ); #execute the query $sth->execute( ); warn "Entry could not be retrieved from table", $sth->errstr( ), "\n" if $sth->err( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; $result = "@row"; } $sth->finish(); } #print $result; return $result; } # ##### ------------------------------------------------ ##### ### helper functions (require db and/or ctl) # #sub lower() { # lc ($_[0]); # return; #} # params: table, column, value # output: false if exists, true otherwise sub is_value_in_db() { my ($TABLE, $COL, $VALUE); $TABLE=$_[0]; $COL=$_[1]; $VALUE=$_[2]; if ( $DBENGINE =~ /^DB_BERKELEY$/) { &bdb_select_where($TABLE,$VALUE); } elsif ( $DBENGINE =~ /^DBTEXT$/) { my $res = system("$DBTEXTCMD","SELECT count(*) FROM $TABLE WHERE $COL=\'$VALUE\'"); $result =$res; } else { #prepare query $sth = $dbh->prepare( "SELECT count(*) FROM $TABLE WHERE $COL=\'$VALUE\'" ); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); while ( @row = $sth->fetchrow_array( ) ) { $result = "@row"; #print "@row\n"; } $sth->finish(); } if ($result == 0) { $response = 0; } else { $response = 1; } #print $response; } sub prompt_pw() { print "Password for $DBROOTUSER (If no password is needed just hit Enter, else introduce password): "; my $PASS = ; chomp($PASS); if ( $PASS =~ "" ) { $PW = ""; } else { $PW = $PASS; } return $PW; } #params: none # output: DBRWPW #sub prompt_pw() { # if ( -z $DBRWPW ) { # my $savetty=`stty -g` # if ( -z "$1" ] ; then # printf "Password: " > /dev/stderr # else # printf "$1: " > /dev/stderr # fi # stty -echo # read DBRWPW # stty $savetty # echo # } #} sub validate_dbdata() { if( -d $DATA_DIR."/$_[0]" ) { $DB_SCHEMA = $DATA_DIR."/$_[0]"; return $DB_SCHEMA; } else { $DB_SCHEMA = "./$_[0]"; print "Warn fallback to local workdir ./$_[0]\n"; return $DB_SCHEMA; } } sub db_load() { if ( $DBENGINE eq "" ) { print "database engine not specified, please setup one in the config script"; $DBENGINELOADED = 0; return 0; } else { if ($DBENGINE =~ /(^mysql$)|(^MYSQL$)/) { print "Used database is mysql\n"; &validate_dbdata("mysql"); #Connect to the database. $dbh = DBI->connect("DBI:mysql:database=$DBNAME;host=$DBHOST;port=$DBPORT", "$DBRWUSER", "$DBRWPW", {'PrintError' => 0} ); if ($dbh==NULL) { print "Failed to connect the configured Database ". "(database=$DBNAME;host=$DBHOST;port=$DBPORT;user=$DBRWUSER)"; $DBENGINELOADED = 0; return 0; } $DBENGINELOADED = 1; } elsif ($DBENGINE =~ /^oracle$/) { print "Used database is Oracle\n"; &validate_dbdata("oracle"); #Connect to the database. $dbh = DBI->connect("DBI:Oracle:database=$DBNAME;host=$DBHOST", "$DBRWUSER", "$DBRWPW", {'PrintError' => 0} ); if ($dbh==NULL) { print "Failed to connect the configured Database ". "(database=$DBNAME;host=$DBHOST;user=$DBRWUSER)"; $DBENGINELOADED = 0; return 0; } $DBENGINELOADED = 1; } elsif ($DBENGINE =~ /^Pg$/) { print "Used database is PostgreSQL\n"; &validate_dbdata("postgres"); $DBROOTUSER = "postgres"; #Connect to the database. $dbh = DBI->connect("DBI:Pg:database=$DBNAME;host=$DBHOST;". "port=$DBPORT","$DBRWUSER","$DBRWPW", {'PrintError' => 0}); if ($dbh==NULL) { print "Failed to connect the configured Database ". "(database=$DBNAME;host=$DBHOST;user=$DBRWUSER)"; $DBENGINELOADED = 0; return 0; } $DBENGINELOADED = 1; } elsif ($DBENGINE =~ /^DBTEXT$/) { print "Used database is DBTEXT\n"; &validate_dbdata("dbtext/opensips"); $ENV{DBTEXT_PATH} = $DBTEXT_PATH; $DBENGINELOADED = 1; } elsif ($DBENGINE =~ /^DB_BERKELEY$/) { print "Used database is DB_BERKELEY\n"; &validate_dbdata("db_berkeley/opensips"); $ENV{PATH} = $DB_PATH; $DBENGINELOADED = 1; } } return 1; } # ##### ------------------------------------------------ ##### ### CTLENGINE # if ( $CTLENGINELOADED eq 1 ) { print "Control engine " . $CTLENGINE . " loaded\n"; } else { print "no control engine found - tried " . $CTLENGINE . "\n"; } # ##### ------------------------------------------------ ##### ### common functions # sub require_dbengine() { if ( $DBENGINELOADED eq 0 ){ print "This command requires a database engine - none was loaded\n"; } return; } sub require_ctlengine() { if ( $CTLENGINELOADED eq 0 ) { print "This command requires a control engine - none was loaded\n"; } return; } sub not_command(){ print "Not an opensips command!\n"; return; } if ( $argnum == 0 ) { &interactively(); } elsif ($argnum gt 0) { &non_interactively(); } sub interactively(){ my @history_list; my $term = Term::ReadLine->new("OpenSIPS"); my $OUT = $term->OUT() ||\*STDOUT; my $attribs = $term->Attribs; # load history open(HIST,"<$HISTORY_FILE"); @history_list=; while ( @history_list gt 0 ){ $result = shift(@history_list); chomp($result); $term->addhistory($result); } close(HIST); $attribs->{completion_entry_function} = $attribs->{list_completion_function}; $attribs->{completion_word} = [@command_list]; # read commands while (1) { $command = $term->readline('OpenSIPS$:'); if ( $command eq ""){ next; } @cmd = split(" ",$command); my $found=0; foreach my $i (@command_list){ if ($cmd[0] eq $i) { $found=1; } } if ($found == 1 ) { system(&cmd()); } elsif ( ($found == 0) & ($cmd[0] eq "quit" || $cmd[0] eq "exit") ) { #$dbh->disconnect(); open(HIST,"+>>$HISTORY_FILE"); print HIST @history_list; close(HIST); print "Thank you for flying Opensips!!\n"; exit; } elsif ( $found == 0 ) { system(¬_command()); } $term->addhistory($command); unshift(@history_list,"$command\n"); } } sub non_interactively(){ @cmd = @ARGV; &cmd(); } sub cmd() { if ($cmd[0] =~ /^acl$/) { if (&db_load()) { system(&opensips_acl()); } } elsif ($cmd[0] =~ /^add$/) { if (&db_load()) {system(&subscriber()); } } elsif ($cmd[0] =~ /^avp$/) { if (&db_load()) {system(&avpops()); } } elsif ($cmd[0] =~ /(^aliasdb$)|(^alias_db$)/) { if (&db_load()) {system(&alias_db()); } } elsif ($cmd[0] =~ /^cr$/) { if (&db_load()) {system(&opensips_cr()); } } elsif ($cmd[0] =~ /^dialplan$/) { if (&db_load()) {system(&opensips_dialplan()); } } elsif ($cmd[0] =~ /^db$/) { system(&opensips_db()); } elsif ($cmd[0] =~ /^dispatcher$/) { if (&db_load()) {system(&opensips_dispatcher()); } } elsif ($cmd[0] =~ /^domain$/) { if (&db_load()) {system(&domain()); } }elsif ($cmd[0] =~ /(^fifo$)|(^unixsock$)|(^udp$)|(^xmlrpc$)/) { &mi_comm(); } elsif ($cmd[0] =~ /(^moni$)|(^monitor$)|(^con$)|(^console$)/) { &mi_comm_monitor(); } elsif ($cmd[0] =~ /^ping$/) { system(&options_ping()); } elsif ($cmd[0] =~ /^ps$/) { system(&opensips_ps()); } elsif ($cmd[0] =~ /^passwd$/) { if (&db_load()) {system(&subscriber()); } } elsif ($cmd[0] =~ /^online$/) { &opensips_online(); } elsif ($cmd[0] =~ /^rpid$/) { if (&db_load()) {system(&opensips_rpid()); } } elsif ($cmd[0] =~ /^restart$/) { system(&opensips_restart()); } elsif ($cmd[0] =~ /^rm$/) { if (&db_load()) {system(&subscriber()); } } elsif ($cmd[0] =~ /(^speeddial$)|(^speed_dial$)/) { if (&db_load()) {system(&speeddial()); } } elsif ($cmd[0] =~ /^start$/) { system(&opensips_start()); } elsif ($cmd[0] =~ /^stop$/) { system(&opensips_stop()); } elsif ($cmd[0] =~ /^tls$/) { system(&tls_ca()); } elsif ($cmd[0] =~ /^address$/) { if (&db_load()) {system(&address()); } } elsif ($cmd[0] =~ /(^ul$)|(^alias$)|(^usrloc$)/) { system(&opensips_usrloc()); } elsif ($cmd[0] =~ /^version$/) { system(&opensips_version()); } elsif ($cmd[0] =~ /^dr$/) { if (&db_load()) {system(&opensips_dr()); } } elsif ($cmd[0] =~ /^help$/) { system(&opensips_help()); } else { print " unknown command!!!\n"; } return; } #all ##### ------------------------------------------------- ##### #help # sub opensips_help() { &usage_online(); print "\n\n"; &usage_opensips_monitor(); print "\n\n"; &usage_ping(); print "\n\n"; &usage_usrloc(); print "\n\n"; &usage_base(); print "\n\n"; &usage_tls(); print "\n\n"; &usage_acl(); print "\n\n"; &usage_cr(); print "\n\n"; &usage_rpid(); print "\n\n"; &usage_subscriber(); print "\n\n"; &usage_address(); print "\n\n"; &usage_dispatcher(); print "\n\n"; &usage_db_ops(); print "\n\n"; &usage_speeddial(); print "\n\n"; &usage_avp(); print "\n\n"; &usage_alias_db(); print "\n\n"; &usage_domain(); print "\n\n"; &usage_fifo(); print "\n\n"; &usage_dialplan; print "\n\n"; &usage_unixsock(); print "\n\n"; &usage_db(); print "\n\n"; &berkeley_usage(); print "\n\n"; &usage_dr(); print "\n\n"; } # ##### ------------------------------------------------ ##### ### opensips_start # sub opensips_start(){ if ( ($#cmd+1) eq 1 ){ print "\nStarting opensips........\n"; if ( -r $PID_FILE ) { `ps -ef | egrep opensips`; `ls -l $PID_FILE`; print "\nPID file exists ( " . $PID_FILE . " )! OpenSIPS already running?\n"; return; } if ( ! -x $OSIPSBIN ) { print "\nOpenSIPS binaries not found at " . $OSIPSBIN . "\n"; print "\nset OSIPSBIN to the path of opensips in " . $0 . " or ~/.osipsconsolerc\n"; return; } if ( $SYSLOG == 1 ) { `$OSIPSBIN -P $PID_FILE $STARTOPTIONS 1>/dev/null 2>/dev/null`; return; } else { `$OSIPSBIN -P $PID_FILE -f $PATH_ETC/opensips.cfg -E $STARTOPTIONS`; return; } sleep 3; if ( -z $PID_FILE ) { print "\nPID file " . $PID_FILE . " does not exist -- OpenSIPS start failed\n"; return; } print "\nstarted (pid: " . `cat $PID_FILE` . ")\n"; } elsif ($cmd[1] =~ /h/){ &usage_base(); }else { print "No parameters required!!! Syntax is not correct\n"; } return; } # ##### ------------------------------------------------ ##### ### opensips_stop # sub opensips_stop(){ if ( ($#cmd+1) eq 1 ){ print "\nStopping OpenSIPS : \n"; if ( -r $PID_FILE ) { my $sys = `cat $PID_FILE`; `kill $sys`; print "stopped\n"; return; } else { print "\nNo PID file found ( " . $PID_FILE . " )! OpenSIPS probably not running\n"; print "check with 'ps -ef | " . $EGREP . " opensips'\n"; return; } }elsif ($cmd[1] =~ /h/){ &usage_base(); }else { print "No parameters required!!! Syntax is not correct\n"; } return; } # ##### ------------------------------------------------ ##### ### opensips_restart # sub opensips_restart(){ &opensips_stop(); sleep 2; &opensips_start(); } # ##### ------------------------------------------------ ##### ### oppensips_acl # sub opensips_acl() { if (!$#cmd gt 0) { print "Too few parameters\n"; &usage_acl(); return; } else { if ($cmd[1] =~ /^show$/) { if ( $#cmd eq 1 ) { if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_select($ACL_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","SELECT * FROM $ACL_TABLE "); } else { $sth = $dbh->prepare ("SELECT * FROM $ACL_TABLE "); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while (@row = $sth->fetchrow_array( ) ) { print "@row\n"; } warn "Error retireving data from the database! ", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } return; } if ( $#cmd == 2 ) { if ( &is_user($cmd[2]) == 0 ) { print "Non-existent user " . $cmd[2] . "\n"; return; } else { &set_user($cmd[2]); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { print "For DB BERKELEY, this operation needs 2 params: username\@domain and group\n"; return; } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","SELECT * FROM $ACL_TABLE WHERE $acl_table{'ACL_USER_COLUMN'}=\'$OSIPSUSER\' AND $acl_table{'ACL_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\' "); } else { $sth = $dbh->prepare ("SELECT * FROM $ACL_TABLE WHERE $acl_table{'ACL_USER_COLUMN'}=\'$OSIPSUSER\' AND $acl_table{'ACL_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\' "); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while (@row = $sth->fetchrow_array( ) ) { print "@row\n"; } warn "Error retireving data from the database! ", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } } elsif ( $#cmd == 3 ){ if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN,$cmd[3]); &bdb_select_where($ACL_TABLE,$key); } else { print "Too many parameters for $DBENGINE query\n"; return; } } else { &usage_acl(); return; } } elsif ($cmd[1] =~ /^grant$/) { if ( $#cmd lt 3 ) { &usage_acl(); return; } my $acl_inserted = 0; if ( $#cmd == 3 ) { if ( &is_user($cmd[2]) == 0 ) { print "Non-existent user " . $cmd[2] . " Still proceeding? [Y|N]:"; if ( ( $input = ) =~ /[y|Y]/ ) { print "Proceeding with non-local user\n"; } else { return; } } &set_user($cmd[2]); if ( $VERIFY_ACL == 1 ) { my $found = 0; foreach my $i (@ACL_GROUPS) { if ( $cmd[3] =~ /(^$i$)/ ) { print $cmd[3]."...".$i."\n"; $found = 1; } } if ( $found == 0 ) { print "Invalid privilege: acl " . $cmd[3] . " ignored\n"; return; } } my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $date = join("-",$year+1900,$mon+1,$mday+1); my $time = join(":",$hour,$min,$sec); my $last_modified = join(" ",$date,$time); my $unix_time = time(); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN,$cmd[3]); my $value = join(" ",$last_modified); &bdb_insert($ACL_TABLE,$key,$value); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","INSERT INTO $ACL_TABLE ($acl_table{'ACL_USER_COLUMN'},$acl_table{'ACL_GROUP_COLUMN'},$acl_table{'ACL_MODIFIED_COLUMN'},$acl_table{'ACL_DOMAIN_COLUMN'} ) VALUES (\"$OSIPSUSER\",\"$cmd[3]\",$unix_time, \"$OSIPSDOMAIN\" ) "); } else { $sth = $dbh->prepare ("INSERT INTO $ACL_TABLE ($acl_table{'ACL_USER_COLUMN'},$acl_table{'ACL_GROUP_COLUMN'}, $acl_table{'ACL_MODIFIED_COLUMN'},$acl_table{'ACL_DOMAIN_COLUMN'} ) VALUES (\'$OSIPSUSER\',\'$cmd[3]\',\'$last_modified\', \'$OSIPSDOMAIN\' ) " ); ##execute the query $sth->execute( ); print "Data was not inserted in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); if ( &is_user($cmd[2]) == 0 ) { print "acl - SQL Error\n"; return; } $acl_inserted = 1; } } if ( $acl_inserted == 1 ) { if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN,$cmd[3]); &bdb_select_where($ACL_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","SELECT * FROM $ACL_TABLE WHERE $acl_table{'ACL_USER_COLUMN'}=\'$OSIPSUSER\' AND $acl_table{'ACL_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\' "); } else { $sth = $dbh->prepare ("SELECT * FROM $ACL_TABLE WHERE $acl_table{'ACL_USER_COLUMN'}=\'$OSIPSUSER\' AND $acl_table{'ACL_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\' "); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while (@row = $sth->fetchrow_array( ) ) { print "@row\n"; } warn "Could not retrieve data! Data was not inserted in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } } elsif ($cmd[1] =~ /^revoke$/) { &set_user($cmd[2]); if ( $#cmd == 2 ) { if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { print "Too few parameters for this operation!!!\n"; } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","DELETE FROM $ACL_TABLE WHERE $acl_table{'ACL_USER_COLUMN'}=\'$OSIPSUSER\' AND $acl_table{'ACL_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); } else { $sth = $dbh->prepare ("DELETE FROM $ACL_TABLE WHERE $acl_table{'ACL_USER_COLUMN'}=\'$OSIPSUSER\' AND $acl_table{'ACL_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print warn "Could not delete entry! ", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ( $#cmd == 3 ) { if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN,$cmd[3]); &bdb_delete($ACL_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","DELETE FROM $ACL_TABLE WHERE $acl_table{'ACL_USER_COLUMN'}=\'$OSIPSUSER\' AND $acl_table{'ACL_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\' AND $acl_table{'ACL_GROUP_COLUMN'}=\'$cmd[3]\'"); } else { $sth = $dbh->prepare ("DELETE FROM $ACL_TABLE WHERE $acl_table{'ACL_USER_COLUMN'}=\'$OSIPSUSER\' AND $acl_table{'ACL_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\' AND $acl_table{'ACL_GROUP_COLUMN'}=\'$cmd[3]\'"); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print warn "Could not delete entry! ", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } else { print "acl - wrong number of parameters\n"; &usage_acl(); return; } } elsif ($cmd[1] =~ /^h$/) { &usage_acl(); } else { print "acl -unknown command $cmd[1]\n"; } } return; } # ##### ------------------------------------------------ ##### ### opensips_rpid # sub opensips_rpid() { if ( $#cmd lt 1 ) { print "rpid - too few parameters\n"; &usage_rpid(); return; } if ($cmd[1] =~ /^h/) { &usage_rpid(); return; } elsif ($cmd[1] =~ /^add$/) { if ( $#cmd lt 3 ) { print "Too few arguments!"; } else { &set_user($cmd[2]); &is_user($cmd[2]); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_update($SUB_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","UPDATE $SUB_TABLE SET $sub_table{'RPID_COLUMN'} = $cmd[3] WHERE $sub_table{'SUBSCRIBER_COLUMN'}=\"$OSIPSUSER\" AND $sub_table{'REALM_COLUMN'}= \"$OSIPSDOMAIN\" "); } else { $sth = $dbh->prepare("UPDATE $SUB_TABLE SET $sub_table{'RPID_COLUMN'} = $cmd[3] WHERE $sub_table{'SUBSCRIBER_COLUMN'}=\'$OSIPSUSER\' AND $sub_table{'REALM_COLUMN'}= \'$OSIPSDOMAIN\' " ); ##execute the query $sth->execute( ); warn "Entry was not updated in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } } elsif ($cmd[1] =~ /^rm$/) { if ( $#cmd lt 2 ) { print "Too few arguments!"; } else { &set_user($cmd[2]); &is_user($cmd[2]); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_update($SUB_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","UPDATE $SUB_TABLE SET rpid = null WHERE $sub_table{'SUBSCRIBER_COLUMN'} = \'$OSIPSUSER\' AND $sub_table{'REALM_COLUMN'} = \'$OSIPSDOMAIN\'"); } else { $sth = $dbh->prepare("UPDATE $SUB_TABLE SET rpid = null WHERE $sub_table{'SUBSCRIBER_COLUMN'} = \'$OSIPSUSER\' AND $sub_table{'REALM_COLUMN'} = \'$OSIPSDOMAIN\' " ); ##execute the query $sth->execute( ); warn "Entry was not updated in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } } elsif ($cmd[1] =~ /^show$/) { if ($#cmd lt 2){ print "Too few parameters!\n"; return; } else{ if ( &is_user($cmd[2]) == 0 ) { print "rpid - invalid user " . $cmd[2] . "\n"; return; } } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_select_where($SUB_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","select $sub_table{'SUBSCRIBER_COLUMN'}, $sub_table{'RPID_COLUMN'} FROM $SUB_TABLE WHERE $sub_table{'SUBSCRIBER_COLUMN'}=\'$OSIPSUSER\' AND $sub_table{'REALM_COLUMN'}=\'$OSIPSDOMAIN\'"); } else { $sth = $dbh->prepare("select $sub_table{'SUBSCRIBER_COLUMN'}, $sub_table{'RPID_COLUMN'} FROM $SUB_TABLE WHERE $sub_table{'SUBSCRIBER_COLUMN'}=\'$OSIPSUSER\' AND $sub_table{'REALM_COLUMN'}=\'$OSIPSDOMAIN\'" ); ##execute the query $sth->execute( ); warn "Entry was not updated in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } else { print "rpid -unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### subscriber - add|passwd|rm # sub subscriber() { if ( $#cmd lt 1 ) { print "too few parameters\n"; &usage_subscriber(); return; } if ($cmd[0] =~ /^add$/) { if ($#cmd ne 2 ) { &usage_subscriber(); return; } &credentials($cmd[1],$cmd[2]); if ( &is_user($cmd[1]) != 0 ) { print "user " . $cmd[1] . " already exists\n"; return; } &set_user($cmd[1]); &check_alias($OSIPSUSER,$OSIPSDOMAIN); if ( $ALIAS_EXISTS == 1 ) { print "user " . $cmd[1] . " already exists as alias\n"; return; } else { print "Alias $cmd[1] does not exist!\n"; } if ( $STORE_PLAINTEXT_PW eq "1" ) { $PASS = $cmd[2]; $HA1 = ""; $HA1B = ""; } else { $PASS = ""; &credentials($cmd[1],$cmd[2]); } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); my $value = join(" ", $cmd[2], $HA1, $HA1B); &bdb_insert($SUB_TABLE, $key,$value); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","INSERT INTO $SUB_TABLE ($sub_table{'SUBSCRIBER_COLUMN'},$sub_table{'REALM_COLUMN'},$sub_table{'HA1_COLUMN'}, $sub_table{'HA1B_COLUMN'},$sub_table{'PASSWORD_COLUMN'},$sub_table{'EMAIL_ADDRESS'} ) VALUES (\'$OSIPSUSER\',\'$OSIPSDOMAIN\',\'$HA1\',\'$HA1B\',\'$PASS\',\'\') " ); } else { #prepare the query $sth = $dbh->prepare( "INSERT INTO $SUB_TABLE ($sub_table{'SUBSCRIBER_COLUMN'},$sub_table{'REALM_COLUMN'},$sub_table{'HA1_COLUMN'}, $sub_table{'HA1B_COLUMN'},$sub_table{'PASSWORD_COLUMN'},$sub_table{'EMAIL_ADDRESS'} ) VALUES (\'$OSIPSUSER\',\'$OSIPSDOMAIN\',\'$HA1\',\'$HA1B\',\'$PASS\',\'\') " ); #execute the query $sth->execute( ); warn "Introducing the new user " . $cmd[1] . " to subscriber table failed ", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[0] =~ /^passwd$/) { if ( $#cmd lt 2 ) { &usage_subscriber(); return; } elsif ($#cmd eq 2){ if ( &is_user( $cmd[1]) == 0 ) { print "non-existent user: " . $cmd[1] . "\n"; return; } if ( $STORE_PLAINTEXT_PW eq "1" ) { $PASS = $cmd[2]; $HA1 = " "; $HA1B = " "; } else { $PASS = ""; &credentials($cmd[1],$cmd[2]); } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_update($SUB_TABLE, $key); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD","UPDATE $SUB_TABLE SET $sub_table{'HA1_COLUMN'}=\"$HA1\",$sub_table{'HA1B_COLUMN'}=\"$HA1B\", $sub_table{'PASSWORD_COLUMN'}=\"$PASS\" WHERE $sub_table{'SUBSCRIBER_COLUMN'}=\"$OSIPSUSER\" AND $sub_table{'REALM_COLUMN'}=\"$OSIPSDOMAIN\""); } else { $sth = $dbh->prepare( "UPDATE $SUB_TABLE SET $sub_table{'HA1_COLUMN'}=\'$HA1\',$sub_table{'HA1B_COLUMN'}=\'$HA1B\', $sub_table{'PASSWORD_COLUMN'}=\'$PASS\' WHERE $sub_table{'SUBSCRIBER_COLUMN'}=\'$OSIPSUSER\' AND $sub_table{'REALM_COLUMN'}=\'$OSIPSDOMAIN\'" ); #execute the query $sth->execute( ); warn "Changing the password for user: " . $cmd[1] . " in database failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } } elsif ($cmd[0] =~ /^rm$/) { if ( $#cmd ne 1 ) { &usage_subscriber(); return; } &require_ctlengine(); if ( &is_user ( $cmd[1] ) == 0) { print "non-existent user $cmd[1]\n"; return; } # begin with remove all user's privileges # ####################################################acl revoke $1 > /dev/null 2>&1 # destroy db-aliases if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_delete($DA_TABLE, "$OSIPSUSER","$OSIPSDOMAIN"); #&bdb_select($DA_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD", "DELETE FROM $DA_TABLE WHERE $da_table{'DA_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); } else { $sth = $dbh->prepare("DELETE FROM $DA_TABLE WHERE $da_table{'DA_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'" ); #execute the query $sth->execute( ); warn "Destroying db-aliases for: " . $cmd[1] . " in database failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } # destroy the user now if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_delete($SUB_TABLE, "$OSIPSUSER","$OSIPSDOMAIN"); #&bdb_select($SUB_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/) { system("$DBTEXTCMD", "DELETE FROM $SUB_TABLE WHERE $sub_table{'SUBSCRIBER_COLUMN'}=\'$OSIPSUSER\' AND $sub_table{'REALM_COLUMN'}=\'$OSIPSDOMAIN\'"); } else { $sth = $dbh->prepare("DELETE FROM $SUB_TABLE WHERE $sub_table{'SUBSCRIBER_COLUMN'}=\'$OSIPSUSER\' AND $sub_table{'REALM_COLUMN'}=\'$OSIPSDOMAIN\'" ); #execute the query $sth->execute( ); warn "Destroying user " . $cmd[1] . " in subscriber table failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } # and also all his contacts &mi_comm(join(" ","ul_rm",$USRLOC_TABLE, $OSIPSUSER."\@".$OSIPSDOMAIN)); } else { print "unknown command $cmd[0]\n"; } } # ##### ------------------------------------------------ ##### ### opensips_usrloc # sub opensips_usrloc() { if ( $#cmd lt 1 ) { print "usrloc - too few parameters\n"; &usage_usrloc(); return; } &require_ctlengine(); if ( $cmd[0] eq "alias" ) { $USRLOC_TABLE=$ALS_TABLE; if ( -z $USRLOC_TABLE ) { $USRLOC_TABLE = 'aliases'; } $CHECK_SUB = 1; } elsif ( $cmd[0] eq "ul" ) { $USRLOC_TABLE = $UL_TABLE; if ( -z $USRLOC_TABLE ) { $USRLOC_TABLE = 'location'; } $CHECK_SUB = 0; } elsif ( $cmd[0] eq 'usrloc' ) { $USRLOC_TABLE= $UL_TABLE; if ( -z $USRLOC_TABLE ) { $USRLOC_TABLE = 'location'; } $CHECK_SUB = 0; } else { print "usrloc - unknown subcommand " . $cmd[0]; &usage_usrloc(); return; } if ($cmd[1] =~ /^h$/) { &usage_usrloc(); return; } elsif ($cmd[1] =~ /^show$/) { if ( $#cmd == 2){ if ($cmd[2] eq "--brief"){ &mi_comm("ul_dump", "brief"); } else { &set_user($cmd[2]); &mi_comm(join(" ","ul_show_contact",$USRLOC_TABLE,$OSIPSUSER."\@".$OSIPSDOMAIN)); } } elsif ( $#cmd == 1){ &mi_comm("ul_dump"); } else { print "wrong number of params"; &usage_usrloc(); } } elsif ($cmd[1] =~ /^add$/) { my ($UL_EXPIRES, $UL_FLAGS, $BR_FLAGS); if ( $#cmd == 2 ) { # expires 0 means persistent contact $UL_EXPIRES = 0; $UL_FLAGS = 0; $BR_FLAGS = 0 }elsif ( $#cmd == 3 ) { $UL_EXPIRES = $cmd[3]; $UL_FLAGS = 0; $BR_FLAGS = 0; }else { &usage_usrloc(); return; } if ( &check_uri($cmd[3]) != 0 ) { print "$cmd[3] is not a valid URI\n"; return; } &set_user($cmd[2]); if ( $CHECK_SUB != 0 ) { if ( &is_user( $cmd[2] ) ne 0 ) { print "overlap of alias with an existing subscriber name\n"; return; } } &check_alias($OSIPSUSER, $OSIPSDOMAIN); if ( $ALIAS_EXISTS == 1 ) { if ( $CHECK_SUB != 0 ) { print "alias already defined\n"; } else { print "AOR is an alias\n"; } return; } &mi_comm( join("ul_add" ,$USRLOC_TABLE, $OSIPSUSER . "\@" . $OSIPSDOMAIN, $cmd[2],$UL_EXPIRES, "1.00", "0", $UL_FLAGS, $BR_FLAGS,$ALL_METHODS)); } elsif ($cmd[1] =~ /^rm$/) { if ( $#cmd == 2 ) { &set_user($cmd[2]); &mi_comm(join(" ","ul_rm",$USRLOC_TABLE, $OSIPSUSER."\@".$OSIPSDOMAIN)); }elsif ( $#cmd == 3 ) { &set_user( $cmd[2] ); if ( &check_uri($cmd[3]) != 0 ) { print $cmd[3] . "is not a valid SIP URI (sip:[user\@]domain)"; return; } &mi_comm(join(" ","ul_rm_contact", $USRLOC_TABLE, $OSIPSUSER . "\@" . $OSIPSDOMAIN,$cmd[3])); } else { print "wrong number of params\n"; &usage_usrloc(); return; } } else { print "usrloc|ul|alias -unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### options_ping # sub options_ping() { if ( $#cmd lt 1 ) { print "Too few parameters"; &usage_ping(); return; } my $myhost = &get_my_host(); &require_ctlengine(); my $temp = &mi_comm(join(" ","t_uac_dlg", "OPTIONS", "$cmd[1]", ".", ".","\"From:sip:daemon\@$myhost\r\nTo:<$cmd[1]>\r\nContact:sip:daemon\@$myhost\"")); print $temp; return; } # ##### ------------------------------------------------ ##### ### domain # sub domain() { if ( $#cmd lt 1 ) { print "Too few parameters!\n"; &usage_domain(); return; } elsif ( $#cmd gt 2 ) { print "Too many parameters!\n"; &usage_domain(); return; } if ($cmd[1] =~ /^reload$/) { if ( $#cmd gt 1 ) { print "Too many parameters\n!"; &usage_domain(); return; } &require_ctlengine(); &mi_comm('domain_reload'); } elsif ($cmd[1] =~ /^show$/) { if ( $#cmd gt 1 ) { print "Too many parameters\n!"; &usage_domain(); return; } &require_ctlengine(); &mi_comm('domain_dump'); } elsif ($cmd[1] =~ /showdb$/) { if ( $#cmd gt 1 ) { print "Too many parameters\n!"; &usage_domain(); return; } &require_dbengine(); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_select($DOMAIN_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD","select * FROM $DOMAIN_TABLE"); } else { $sth = $dbh->prepare ( "select * FROM $DOMAIN_TABLE"); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while (@row = $sth->fetchrow_array( ) ) { print "@row\n"; } warn "Could not retrieve data! Data was not inserted in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^add$/) { if ( $#cmd lt 2 ) { print "Too few parameters\n!"; &usage_domain(); return; } elsif ( $#cmd gt 2 ) { print "Too many parameters\n"; &usage_domain(); return; } if ( &is_value_in_db($DOMAIN_TABLE, $domain_table{'DO_DOMAIN_COLUMN'},$cmd[2]) == 1 ) { print $cmd[2] . " is already in $DOMAIN_TABLE table\n"; return; } my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $date = join("-",$year+1900,$mon+1,$mday+1); my $time = join(":",$hour,$min,$sec); my $last_modified = join(" ",$date,$time); my $unix_time = time(); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = $cmd[2]; my $value = $last_modified; &bdb_insert($DOMAIN_TABLE,$key,$value); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD","INSERT INTO $DOMAIN_TABLE ($domain_table{'DO_DOMAIN_COLUMN'}, $domain_table{'DO_LAST_MODIFIED_COLUMN'}) VALUES (\'$cmd[2]\',$unix_time) "); } else { $sth = $dbh->prepare( "INSERT INTO $DOMAIN_TABLE ($domain_table{'DO_DOMAIN_COLUMN'}, $domain_table{'DO_LAST_MODIFIED_COLUMN'}) VALUES (\'$cmd[2]\',\'$last_modified\') " ); #execute the query $sth->execute( ); warn "Introducing the new user " . $cmd[1] . " to the database failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^rm$/) { &require_dbengine(); if ( $#cmd lt 2 ) { print "Too few parameters!\n"; &usage_domain(); return; } elsif ( $#cmd gt 2 ) { print "Too ma ny parameters!\n"; &usage_domain(); return; } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = $cmd[2]; &bdb_delete($DOMAIN_TABLE,$cmd[2]); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD","DELETE FROM $DOMAIN_TABLE WHERE domain=\'$cmd[2]\'" ); } else { $sth = $dbh->prepare("DELETE FROM $DOMAIN_TABLE WHERE domain=\'$cmd[2]\'" ); #execute the query $sth->execute( ); warn "Deleting domain " . $cmd[2] . " in table $DOMAIN_TABLE failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } print "execute 'domain reload' to synchronize cache and database\n"; } elsif ($cmd[1] =~ /^h$/) { &usage_domain(); } else { print "domain -unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### address # sub address() { my ($GRP, $IP, $MASK, $PORT, $PROTO, $PATTERN, $CONTEXT_INFO, $columns, $query); if ( $#cmd lt 1 ) { print "Too few parameters!\n"; &usage_address(); return; } if ( $#cmd gt 8 ) { print "Too many parameters!\n"; &usage_address(); return; } if ($cmd[1] =~ /^reload$/) { &require_ctlengine(); &mi_comm('address_reload'); } elsif ($cmd[1] =~ /^dump$/) { &require_ctlengine(); &mi_comm('address_dump'); } elsif ($cmd[1] =~ /^show$/) { require_dbengine(); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_select($ADDRESS_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD","SELECT * FROM $ADDRESS_TABLE"); } else { $sth = $dbh->prepare ( "SELECT * FROM $ADDRESS_TABLE"); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while (@row = $sth->fetchrow_array( ) ) { print "@row\n"; } warn "Could not retrieve data! Data was not inserted in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^add$/) { &require_dbengine(); if ( $#cmd lt 6 ) { print "Too few parameters!\n"; &usage_address(); return; } $GRP = $cmd[2]; $columns = " $address_table{'ADDRESS_GRP_COLUMN'}, "; $query = "$GRP, "; $IP = $cmd[3]; $MASK = $cmd[4]; if ( $cmd[4] !~ /^[0-9]+$/ || $cmd[4] < 0 || $cmd[4] > 32 ) { print "mask '$cmd[4]' is not valid - it must be between 0 and 32 (biti)\n"; return; } $columns .= "$address_table{'ADDRESS_IP_COLUMN'}, $address_table{'ADDRESS_MASK_COLUMN'}, "; $query .= "\'$IP\', \'$MASK\', "; if ( $cmd[6] !~ /^any$|^ANY$|^UDP$|^udp$|^tcp$|^TCP$|^tls$|^TLS$|^sctp$|^SCTP$|^NONE$|^none$/ ) { print "unknown protocol\n"; return; } if ( (&is_value_in_db( $ADDRESS_TABLE, $address_table{'ADDRESS_IP_COLUMN'}, $IP ) != 0) ) { print "Record having $IP is already in $ADDRESS_TABLE table\n"; return; } $PORT = $cmd[5]; $PROTO = $cmd[6]; $columns .= "$address_table{'ADDRESS_PORT_COLUMN'}, $address_table{'ADDRESS_PROTO_COLUMN'} "; $query .= "$PORT, \'$PROTO\' "; my $value = join(" ", $MASK,$PORT,$PROTO); if ( $cmd[6] ) { $CONTEXT_INFO = $cmd[7]; $columns .= ", " . "$address_table{'ADDRESS_CONTEXT_INFO_COLUMN'}"; $query .= ", " . "\'$CONTEXT_INFO\'"; $value = join(" ",$CONTEXT_INFO); if ( $cmd[7] ) { $PATTERN = $cmd[8]; $columns .= ", " . "$address_table{'ADDRESS_PATTERN_COLUMN'}"; $query .= ", " . "\'$PATTERN\'"; $value = join(" ",$PATTERN); } } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ", $GRP, $IP); &bdb_insert($ADDRESS_TABLE, $key, $value); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD", " INSERT INTO $ADDRESS_TABLE ( $columns ) VALUES ( $query ) " ) ; } else { $sth = $dbh->prepare( " INSERT INTO $ADDRESS_TABLE ($columns ) VALUES ( $query )" ); #execute the query $sth->execute( ); warn "Introducing the address rule $cmd[2] $IP $MASK $PORT $PROTO to the database failed \n", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); print "\nExecute 'address reload' to synchronize cache and database\n"; } } elsif ($cmd[1] =~ /^rm$/) { &require_dbengine(); if ( $#cmd ne 5 ) { &usage_address(); return; } $GRP = $cmd[2]; $IP = $cmd[3]; $MASK = $cmd[4]; $PORT = $cmd[5]; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ", $cmd[2], $IP); &bdb_delete($ADDRESS_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD", "DELETE FROM $ADDRESS_TABLE WHERE $address_table{'ADDRESS_GRP_COLUMN'} = $GRP AND $address_table{'ADDRESS_IP_COLUMN'} = \'$IP\' AND $address_table{'ADDRESS_MASK_COLUMN'} = \'$MASK\' AND $address_table{'ADDRESS_PORT_COLUMN'} = $PORT" ); } else { $sth = $dbh->prepare("DELETE FROM $ADDRESS_TABLE WHERE $address_table{'ADDRESS_GRP_COLUMN'} = $GRP AND $address_table{'ADDRESS_IP_COLUMN'} = \'$IP\' AND $address_table{'ADDRESS_MASK_COLUMN'} = \'$MASK\' AND $address_table{'ADDRESS_PORT_COLUMN'} = $PORT" ); #execute the query $sth->execute( ); warn "Deleting address rule: $GRP $IP $MASK $PORT in table $DOMAIN_TABLE failed \n", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } print "\nExecute 'address reload' to synchronize cache and database\n"; } elsif ($cmd[1] =~ /^h$/) { &usage_address(); } else { print "address -unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### DISPATCHER management # sub opensips_dispatcher() { if ( $#cmd lt 6 ) { print "Too few parameters!\n"; &usage_dispatcher(); return; } elsif ( $#cmd gt 8 ) { print "Too many parameters!\n"; &usage_dispatcher(); return; } &require_dbengine(); &require_ctlengine(); if ($cmd[1] =~ /^show$/) { print "dispatcher gateways\n"; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_select($DISPATCHER_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD", "SELECT * FROM $DISPATCHER_TABLE ORDER BY $dispatcher_table{'DISPATCHER_SETID_COLUMN'}" ); } else { $sth = $dbh->prepare ( "SELECT * FROM $DISPATCHER_TABLE ORDER BY $dispatcher_table{'DISPATCHER_SETID_COLUMN'}"); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while (@row = $sth->fetchrow_array( ) ) { print "@row\n"; } warn "Could not retrieve data! Data was not inserted in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^addgw$/) { my ( $DISPATCHER_SETID, $DISPATCHER_DESTINATION, $DISPATCHER_SOCKET, $DISPATCHER_STATE, $DISPATCHER_WEIGHT, $DISPATCHER_ATTRS, $DISPATCHER_DESCRIPTION); if ( $#cmd lt 7 ) { print "Too few parameters!\n"; &usage_dispatcher(); return; } if ( $#cmd gt 8 ) { print "Too many parameters!\n"; &usage_dispatcher(); return; } if ( $#cmd gt 7 ) { $DISPATCHER_DESCRIPTION = $cmd[8]; } else { $DISPATCHER_DESCRIPTION = ""; } $DISPATCHER_SETID = $cmd[2]; $DISPATCHER_DESTINATION = $cmd[3]; $DISPATCHER_SOCKET = $cmd[4]; $DISPATCHER_STATE = $cmd[5]; $DISPATCHER_WEIGHT = $cmd[6]; $DISPATCHER_ATTRS = $cmd[7]; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$DISPATCHER_SETID,$DISPATCHER_STATE); my $value = join(" ",$DISPATCHER_DESTINATION,$DISPATCHER_DESCRIPTION); &bdb_insert($DISPATCHER_TABLE,$key,$value); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD"," INSERT INTO $DISPATCHER_TABLE ( $dispatcher_table{'DISPATCHER_SETID_COLUMN'}, $dispatcher_table{'DISPATCHER_DESTINATION_COLUMN'}, $dispatcher_table{'DISPATCHER_SOCKET_COLUMN'}, $dispatcher_table{'DISPATCHER_STATE_COLUMN'}, $dispatcher_table{'DISPATCHER_WEIGHT_COLUMN'}, $dispatcher_table{'DISPATCHER_ATTRS_COLUMN'}, $dispatcher_table{'DISPATCHER_DESCRIPTION_COLUMN'} ) VALUES ( $DISPATCHER_SETID, \'$DISPATCHER_DESTINATION\', \'$DISPATCHER_SOCKET\', $DISPATCHER_STATE, $DISPATCHER_WEIGHT, \'$DISPATCHER_ATTRS\', \'$DISPATCHER_DESCRIPTION\') "); } else { $sth = $dbh->prepare( " INSERT INTO $DISPATCHER_TABLE ( $dispatcher_table{'DISPATCHER_SETID_COLUMN'}, $dispatcher_table{'DISPATCHER_DESTINATION_COLUMN'}, $dispatcher_table{'DISPATCHER_SOCKET_COLUMN'}, $dispatcher_table{'DISPATCHER_STATE_COLUMN'}, $dispatcher_table{'DISPATCHER_WEIGHT_COLUMN'}, $dispatcher_table{'DISPATCHER_ATTRS_COLUMN'}, $dispatcher_table{'DISPATCHER_DESCRIPTION_COLUMN'} ) VALUES ( $DISPATCHER_SETID, \'$DISPATCHER_DESTINATION\', \'$DISPATCHER_SOCKET\', $DISPATCHER_STATE, $DISPATCHER_WEIGHT, \'$DISPATCHER_ATTRS\', \'$DISPATCHER_DESCRIPTION\') " ); #execute the query $sth->execute( ); warn "Introducing the new user " . $cmd[1] . " to the database failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } &mi_comm('ds_reload'); } elsif ($cmd[1] =~ /^rmgw$/) { if ( $#cmd lt 2 ) { print "missing gateway id to be removed\n"; return; } elsif ( ( $#cmd lt 3 ) && ( $DBENGINE =~ /^DB_BERKELEY$/ ) ) { print "missing flag to be removed\n"; return; } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$cmd[2],$cmd[3]); &bdb_delete($DISPATCHER_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD","DELETE FROM $DISPATCHER_TABLE WHERE $dispatcher_table{'DISPATCHER_SETID_COLUMN'}=\'$cmd[2]\'" ); } else { $sth = $dbh->prepare("DELETE FROM $DISPATCHER_TABLE WHERE $dispatcher_table{'DISPATCHER_SETID_COLUMN'}=\'$cmd[2]\'" ); #execute the query $sth->execute( ); warn "Deleting domain " . $cmd[2] . " in table $DOMAIN_TABLE failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } &mi_comm('ds_reload'); } elsif ($cmd[1] =~ /^reload$/) { &mi_comm('ds_reload'); } elsif ($cmd[1] =~ /^dump$/) { &mi_comm('ds_list'); } elsif ($cmd[1] =~ /^h$/) { &usage_dispatcher(); } else { print "dispatcher - unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### CARRIERROUTE management # sub opensips_cr() { if ( $#cmd lt 1 ) { print "Too few parameters!\n"; &usage_cr(); return; } &require_dbengine(); &require_ctlengine(); if ($cmd[1] =~ /^show$/) { print "cr routing tree\n"; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_select($ROUTE_TREE_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD","SELECT * FROM $ROUTE_TREE_TABLE ORDER BY $route_tree_table{'CARRIERROUTE_ROUTE_TREE_PREFIX_COLUMN'}" ); } else { $sth = $dbh->prepare ( "SELECT * FROM $ROUTE_TREE_TABLE ORDER BY $route_tree_table{'CARRIERROUTE_ROUTE_TREE_PREFIX_COLUMN'}"); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while (@row = $sth->fetchrow_array( ) ) { print "@row\n"; } warn "Could not retrieve data!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } print "cr routes\n"; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_select($CARRIERROUTE_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD"," SELECT * FROM $CARRIERROUTE_TABLE ORDER BY $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_PROB_COLUMN'} "); } else { $sth = $dbh->prepare ( " SELECT * FROM $CARRIERROUTE_TABLE ORDER BY $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_PROB_COLUMN'} "); ##execute the query $sth->execute( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while (@row = $sth->fetchrow_array( ) ) { print "@row\n"; } warn "Could not retrieve data! Data was not inserted in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^reload$/) { &mi_comm('cr_reload_routes'); } elsif ($cmd[1] =~ /^dump$/) { &mi_comm('cr_dump'); } elsif ($cmd[1] =~ /^addrt$/) { if ( $#cmd lt 3 ) { print "cr - missing route_tree\n"; &usage_cr(); return; } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = $cmd[2]; my $value = $cmd[3]; &bdb_insert($ROUTE_TREE_TABLE,$key,$value); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD"," INSERT INTO $ROUTE_TREE_TABLE ( $route_tree_table{'CARRIERROUTE_ROUTE_TREE_PREFIX_COLUMN'}, $route_tree_table{'CARRIERROUTE_ROUTE_TREE_CARRIER_COLUMN'}) VALUES ($cmd[2], \'$cmd[3]\') "); } else { $sth = $dbh->prepare( " INSERT INTO $ROUTE_TREE_TABLE ( $route_tree_table{'CARRIERROUTE_ROUTE_TREE_PREFIX_COLUMN'}, $route_tree_table{'CARRIERROUTE_ROUTE_TREE_CARRIER_COLUMN'}) VALUES ($cmd[2], \'$cmd[3]\') " ); #execute the query $sth->execute( ); warn "Introducing the new route tree prefix and route tree carrier to the database failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } print "execute 'cr reload' to synchronize cache and database\n"; } elsif ($cmd[1] =~ /^rmrt$/) { if ( $#cmd lt 2 ) { print "cr - missing route_tree to be removed\n"; &usage_cr(); return; } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = $cmd[2]; my $value = $cmd[1]; &bdb_delete($ROUTE_TREE_TABLE,$key,$value); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD","DELETE FROM $ROUTE_TREE_TABLE WHERE $route_tree_table{'CARRIERROUTE_ROUTE_TREE_CARRIER_COLUMN'}=\'$cmd[2]\'" ); } else { $sth = $dbh->prepare("DELETE FROM $ROUTE_TREE_TABLE WHERE $route_tree_table{'CARRIERROUTE_ROUTE_TREE_CARRIER_COLUMN'}=\'$cmd[2]\'" ); #execute the query $sth->execute( ); warn "Deleting route tree " . $cmd[2] . " in table $ROUTE_TREE_TABLE failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } print "execute 'cr reload' to synchronize cache and database\n"; } elsif ($cmd[1] =~ /^addcarrier$/) { my ($CARRIER, $SCAN_PREFIX, $DOMAIN, $REWRITE_HOST, $PROB, $STRIP, $REWRITE_PREFIX, $REWRITE_SUFFIX, $COMMENT, $FLAGS, $MASK); if ( $#cmd lt 5 ) { print "cr - too few parameters\n"; &usage_cr(); return; } if ( $#cmd gt 5 ) { $PROB = $cmd[6]; if ( $#cmd gt 6 ) { $STRIP = $cmd[7]; if ( $#cmd gt 7 ) { $REWRITE_PREFIX = $cmd[8]; if ( $#cmd gt 8 ) { $REWRITE_SUFFIX = $cmd[9]; if ( $#cmd gt 9 ) { $COMMENT = $cmd[10]; if ( $#cmd gt 10 ) { $FLAGS = $cmd[11]; if ( $#cmd gt 11 ) { $MASK = $cmd[12]; } else { $MASK = 0; } } else { $FLAGS = 0; $MASK = 0; } } else { $COMMENT = 'NULL'; $FLAGS = 0; $MASK = 0; } } else { $REWRITE_SUFFIX = 'NULL'; $COMMENT = 'NULL'; $FLAGS = 0; $MASK = 0; } } else { $REWRITE_PREFIX = 'NULL'; $REWRITE_SUFFIX = 'NULL'; $COMMENT = 'NULL'; $FLAGS = 0; $MASK = 0; } } else { $STRIP = 0; $REWRITE_PREFIX = 'NULL'; $REWRITE_SUFFIX = 'NULL'; $COMMENT = 'NULL'; $FLAGS = 0; $MASK = 0; } } else { $PROB = 0; $STRIP = 0; $REWRITE_PREFIX = 'NULL'; $REWRITE_SUFFIX = 'NULL'; $COMMENT = 'NULL'; $FLAGS = 0; $MASK = 0; } $CARRIER = $cmd[2]; $SCAN_PREFIX = $cmd[3]; $DOMAIN = $cmd[4]; $REWRITE_HOST = $cmd[5]; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = $CARRIER; my $value = join(" ",$DOMAIN,$SCAN_PREFIX,$FLAGS,$MASK,$PROB,$STRIP,$REWRITE_HOST,$REWRITE_PREFIX,$REWRITE_SUFFIX,$COMMENT); &bdb_insert($CARRIERROUTE_TABLE,$key,$value); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD"," INSERT INTO $CARRIERROUTE_TABLE ( $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_PROB_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_STRIP_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_REWRITE_HOST_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_REWRITE_PREFIX_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_REWRITE_SUFFIX_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_COMMENT_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_FLAGS_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_MASK_COLUMN'}) VALUES ($CARRIER, \'$SCAN_PREFIX\', \'$DOMAIN\', $PROB, $STRIP, \'$REWRITE_HOST\', \'$REWRITE_PREFIX\', \'$REWRITE_SUFFIX\', \'$COMMENT\', $FLAGS, $MASK)"); } else { $sth = $dbh->prepare( " INSERT INTO $CARRIERROUTE_TABLE ( $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_PROB_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_STRIP_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_REWRITE_HOST_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_REWRITE_PREFIX_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_REWRITE_SUFFIX_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_COMMENT_COLUMN'},$carrierroute_table{'CARRIERROUTE_CARRIERROUTE_FLAGS_COLUMN'}, $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_MASK_COLUMN'}) VALUES ($CARRIER, \'$SCAN_PREFIX\', \'$DOMAIN\', $PROB, $STRIP, \'$REWRITE_HOST\', \'$REWRITE_PREFIX\', \'$REWRITE_SUFFIX\', \'$COMMENT\', $FLAGS, $MASK)" ); #execute the query $sth->execute( ); warn "Introducing the new route tree prefix and route tree carrier to the database failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } print "execute 'cr reload' to synchronize cache and database\n"; } elsif ($cmd[1] =~ /^rmcarrier$/) { if ( $#cmd lt 4 ) { print "cr - too few parameters\n"; &usage_cr(); return; } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = $cmd[2]; &bdb_delete($CARRIERROUTE_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system ("$DBTEXTCMD", "DELETE FROM $CARRIERROUTE_TABLE WHERE $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN'}=\'$cmd[2]\' AND $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN'}=\'$cmd[3]\' AND $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN'}=\'$cmd[4]\' "); } else { $sth = $dbh->prepare("DELETE FROM $CARRIERROUTE_TABLE WHERE $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN'}=\'$cmd[2]\' AND $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN'}=\'$cmd[3]\' AND $carrierroute_table{'CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN'}=\'$cmd[4]\' " ); #execute the query $sth->execute( ); warn "Deleting carrier " . $cmd[2] . " in table $CARRIERROUTE_TABLE failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } print "execute 'cr reload' to synchronize cache and database\n"; } elsif ($cmd[1] =~ /^h$/) { &usage_cr(); } else { print "cr - unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### alias management # #check alias sub check_alias(){ $OSIPSUSER = $_[0]; $OSIPSDOMAIN = $_[1]; $ALIAS_EXISTS = 0; &mi_comm( join(" ","ul_show_contact", $ALS_TABLE, $OSIPSUSER."\@".$OSIPSDOMAIN) ); if ( $ENABLE_ALIASES == 1 ) { &check_ul_alias($OSIPSUSER,$OSIPSDOMAIN); if ( $ALIAS_UL_EXISTS == 0 ) { $ALIAS_EXISTS = 0; } else { $ALIAS_EXISTS = 1; } } if ( $ENABLE_ALIASES == 2 ) { &check_db_alias(); if ( $ALIAS_DB_EXISTS == 0 ) { $ALIAS_EXISTS = 0; } else { $ALIAS_EXISTS = 1; } } } sub check_db_alias() { &require_dbengine(); $ALIAS_DB_EXISTS = 0; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { $result = &bdb_select_where($DA_TABLE,"$OSIPSUSER","$OSIPSDOMAIN"); } elsif ( $DBENGINE =~ /^DBTEXT$/) { my $res = system("$DBTEXTCMD","SELECT count(*) FROM $DA_TABLE WHERE $da_table{'DA_ALIAS_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_ALIAS_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); $result = $res; } else { $sth = $dbh->prepare( "SELECT count(*) FROM $DA_TABLE WHERE $da_table{'DA_ALIAS_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_ALIAS_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'" ); #execute the query $sth->execute( ); warn "Retrieving user " . $cmd[1] . " from $DA_TABLE failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; $result = "@row"; } $sth->finish(); } #print $result; if ( $result == 0 ) { $ALIAS_DB_EXISTS = 0; } else { $ALIAS_DB_EXISTS = 1; } } #xxxxxxxxxxxxxxxxxxx sub check_ul_alias() { &require_ctlengine(); $ALIAS_UL_EXISTS = 0; my $temp = &mi_comm(join(" ","ul_show_contact", $ALS_TABLE, $_[0]."\@".$_[1])); if ( $temp !~ /^404/ ) { if ( $temp =~ /^400/ ) { print "400; check if you use aliases in OpenSIPS\n"; return; } if ( $temp =~ /^200/ ) { $ALIAS_UL_EXISTS = 1; } return; } } # ##### ------------------------------------------------ ##### ### db_alias # sub alias_db() { if ( $#cmd lt 1 ) { print "Too few parameters!"; &usage_alias_db(); return; } &require_dbengine(); if ($cmd[1] =~ /(^h$)|(^help$)/) { &usage_alias_db(); return; } elsif ($cmd[1] =~ /^list$/) { if ( $#cmd eq 2 ) { # print aliases for user if ( &check_aor ($cmd[2]) ne 0 ) { print "alias_db - <" .$cmd[2]."> is not a valid AoR (user\@domain)\n"; return; } &set_user($cmd[2]); print "Dumping aliases for user=<".$cmd[2].">\n"; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_select_where($DA_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD","SELECT CONCAT($da_table{'DA_ALIAS_USER_COLUMN'},\'@\',$da_table{'DA_ALIAS_DOMAIN_COLUMN'}) ALIAS FROM $DA_TABLE WHERE $da_table{'DA_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'") } elsif ($DBENGINE =~ /^Pg$/ ) { $sth = $dbh->prepare( "SELECT ($da_table{'DA_ALIAS_USER_COLUMN'} || \'@\' || $da_table{'DA_ALIAS_DOMAIN_COLUMN'}) ALIAS FROM $DA_TABLE WHERE $da_table{'DA_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'" ); #execute the query $sth->execute( ); warn "Concatenting user with domain failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } $sth->finish(); } else { $sth = $dbh->prepare( "SELECT CONCAT($da_table{'DA_ALIAS_USER_COLUMN'},\'@\',$da_table{'DA_ALIAS_DOMAIN_COLUMN'}) ALIAS FROM $DA_TABLE WHERE $da_table{'DA_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'" ); #execute the query $sth->execute( ); warn "Concatenting user with domain failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } $sth->finish(); } } elsif ( $#cmd eq 1 ) { print "Dumping all aliases may take long: do you want to proceed? [Y|N]:"; if ( ( my $input = ) =~ /[y|Y]/ ) { print "Dumping all aliases...\n"; } else { return; } #preparing query if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_select($DA_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD", "SELECT $da_table{'DA_ALIAS_USER_COLUMN'}, $da_table{'DA_ALIAS_DOMAIN_COLUMN'}, $da_table{'DA_USER_COLUMN'}, $da_table{'DA_DOMAIN_COLUMN'} FROM $DA_TABLE" ); } else { $sth = $dbh->prepare( "SELECT $da_table{'DA_ALIAS_USER_COLUMN'}, $da_table{'DA_ALIAS_DOMAIN_COLUMN'}, $da_table{'DA_USER_COLUMN'}, $da_table{'DA_DOMAIN_COLUMN'} FROM $DA_TABLE" ); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } $sth->finish(); } } else { print "alias_db - wrong number of params for command [list]\n"; &usage_alias_db(); return; } } elsif ($cmd[1] =~ /^show$/) { if ( $#cmd ne 2 ) { print "alias_db - wrong number of params for command [show]\n"; &usage_alias_db(); return; } if ( &check_aor ($cmd[2]) ne 0 ) { print "alias_db -". $cmd[2]." is not a valid AoR (user\@domain)\n"; return; } &set_user($cmd[2]); #prepare query if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_select_where($DA_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD", " SELECT CONCAT($da_table{'DA_USER_COLUMN'},\'@\',$da_table{'DA_DOMAIN_COLUMN'} ) AS SIP_ID FROM $DA_TABLE WHERE $da_table{'DA_ALIAS_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_ALIAS_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\' "); } elsif ( $DBENGINE =~ /^Pg$/ ) { $sth = $dbh->prepare( " SELECT ($da_table{'DA_USER_COLUMN'} || \'@\' || $da_table{'DA_DOMAIN_COLUMN'} ) AS SIP_ID FROM $DA_TABLE WHERE $da_table{'DA_ALIAS_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_ALIAS_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\' " ); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } } else { $sth = $dbh->prepare( " SELECT CONCAT($da_table{'DA_USER_COLUMN'},\'@\',$da_table{'DA_DOMAIN_COLUMN'} ) AS SIP_ID FROM $DA_TABLE WHERE $da_table{'DA_ALIAS_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_ALIAS_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\' " ); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } $sth->finish(); } } elsif ($cmd[1] =~ /^add$/) { if ( $#cmd ne 3 ) { &usage_alias_db(); return; } if ( &check_aor($cmd[2]) != 0 ) { print "alias_db - " . $cmd[1] ." is not a valid AoR (user\@domain)\n"; return; } if ( &check_aor($cmd[3]) != 0 ) { print "alias_db - " . $cmd[2] ." is not a valid AoR (user\@domain)\n"; return; } &set_user($cmd[2]); my $TMP_OSIPSUSER = $OSIPSUSER; my $TMP_OSIPSDOMAIN = $OSIPSDOMAIN; &set_user($cmd[3]); if ( &is_value_in_db( $DA_TABLE, $da_table{'DA_ALIAS_USER_COLUMN'}, $TMP_OSIPSUSER ) != 0 ) { print "$TMP_OSIPSUSER alias already in $DA_TABLE table\n"; return; } #prepare query if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$TMP_OSIPSUSER,$TMP_OSIPSDOMAIN); my $value = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_insert($DA_TABLE,$key,$value); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," INSERT INTO $DA_TABLE ($da_table{'DA_USER_COLUMN'},$da_table{'DA_DOMAIN_COLUMN'},$da_table{'DA_ALIAS_USER_COLUMN'}, $da_table{'DA_ALIAS_DOMAIN_COLUMN'}) VALUES (\'$OSIPSUSER\',\'$OSIPSDOMAIN\',\'$TMP_OSIPSUSER\',\'$TMP_OSIPSDOMAIN\' )"); } else { $sth = $dbh->prepare( " INSERT INTO $DA_TABLE ($da_table{'DA_USER_COLUMN'},$da_table{'DA_DOMAIN_COLUMN'},$da_table{'DA_ALIAS_USER_COLUMN'}, $da_table{'DA_ALIAS_DOMAIN_COLUMN'}) VALUES (\'$OSIPSUSER\',\'$OSIPSDOMAIN\',\'$TMP_OSIPSUSER\',\'$TMP_OSIPSDOMAIN\' )" ); #execute the query $sth->execute(); warn "Entry could not be inserted into table", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^rm$/) { if ( $#cmd != 2 ) { print "alias_db - wrong numbers of parameters\n"; &usage_alias_db(); return; } if ( &check_aor($cmd[2]) != 0 ) { print "alias_db - $cmd[1] is not a valid URI\n"; return; } &set_user ( $cmd[2] ); #prepare query if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_delete($DA_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD", " DELETE FROM $DA_TABLE WHERE $da_table{'DA_ALIAS_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_ALIAS_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'" ); } else { $sth = $dbh->prepare( " DELETE FROM $DA_TABLE WHERE $da_table{'DA_ALIAS_USER_COLUMN'}=\'$OSIPSUSER\' AND $da_table{'DA_ALIAS_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'" ); #execute the query $sth->execute( ); warn "Deleting entry failed!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } else { print "alias_db -unknown command $cmd[1]\n"; } } # end db-aliases #####------------------------------------------------##### ### mi_comm # sub mi_comm(){ my $argument = $_[0]; if ( ($#_ + 1) gt 0) { if ($CTLENGINE =~ /^FIFO$/) { &fifo_cmd($argument); } elsif ($CTLENGINE =~ /^UNIXSOCK$/) { &unixsock_cmd($argument); } elsif ($CTLENGINE =~ /^UDP$/) { &udp_cmd($argument); } elsif ($CTLENGINE =~ /^XMLRPC$/) { &xmlrpc_cmd($argument); } } else { if ($cmd[0] =~ /^fifo$/) { &fifo_cmd(); } elsif ($cmd[0] =~ /^unixsock$/) { &unixsock_cmd(); } elsif ($cmd[0] =~ /^udp$/) { &udp_cmd(); } elsif ($cmd[0] =~ /^xmlrpc/) { &xmlrpc_cmd(); } } } sub mi_comm_monitor(){ if ( $CTLENGINE =~ /^FIFO$/) { &fifo_opensips_monitor(); } elsif ( $CTLENGINE =~ /^UNIXSOCK$/) { &unixsock_opensips_monitor(); } elsif ( $CTLENGINE =~ /^UDP$/) { &udp_opensips_monitor(); } elsif ( $CTLENGINE =~ /^XMLRPC$/) { my $content = ""; &xmlrpc_opensips_monitor(); } } ##### ----------------------------------------------- ##### ### FIFO specific variables and functions # sub fifo_cmd() { my @fifo_cmd; if ($cmd[0] =~ /^fifo$/ ) { if ( $#cmd == 0 ) { print "Too few parameters! fifo must take at least the command name as parameter\n"; &usage_fifo(); return; } if ($cmd[1] !~ /arg|debug|kill|list_blacklist|ps|pwd|uptime|version|which|get_statistics|reset_statistics|t_uac_dlg|t_uac_cancel|t_hash|t_reply|ul_rm|ul_rm_contact|ul_dump|ul_flush|ul_add|ul_show_contact/) { print "Fifo command is not valid. Should be in list: [arg|debug|kill|list_blacklist|ps|pwd|uptime|version|which|get_statistics|reset_statistics]\n"; return; } @fifo_cmd = @cmd; shift(@fifo_cmd); } if ($_[0]) { @fifo_cmd = split(" ",$_[0]); } my $arg_list = ""; if ( $#fifo_cmd == 0){ $cmd_fifo = ":" . $fifo_cmd[0] . ":" . $fifo_reply_file . "\n"; } elsif ( $#fifo_cmd gt 0 ){ for (my $i = 1; $i < $#fifo_cmd +1; $i++) { $arg_list = join("",$arg_list,$fifo_cmd[$i],"\n"); } $cmd_fifo = ":" . $fifo_cmd[0] . ":" . $fifo_reply_file . "\n" . $arg_list; } $cmd_fifo = join("",$cmd_fifo,"\n"); &write_read_fifo($OSIPS_FIFO,$fifo_reply_path,$cmd_fifo); } #monitor sub fifo_opensips_monitor() { $cmd_fifo = ":version:$fifo_reply_file\n\n"; print $cmd_fifo."\n"; &write_read_fifo($OSIPS_FIFO,$fifo_reply_path,$cmd_fifo); $cmd_fifo = ":uptime:$fifo_reply_file\n\n"; print $cmd_fifo."\n"; &write_read_fifo($OSIPS_FIFO,$fifo_reply_path,$cmd_fifo); print "Transaction Statistics: \n"; &write_read_fifo($OSIPS_FIFO,$fifo_reply_path,":get_statistics:$fifo_reply_file\nUAS_transactions\nUAC_transactions\ninuse_transactions\n\n"); print "Stateless Server Statistics: \n"; &write_read_fifo($OSIPS_FIFO,$fifo_reply_path,":get_statistics:$fifo_reply_file\nsent_replies\nsent_err_replies\nreceived_ACKs\n\n"); print "UsrLoc Stats: \n"; &write_read_fifo($OSIPS_FIFO,$fifo_reply_path,":get_statistics:$fifo_reply_file\nusrloc:\n\n"); return; } sub write_read_fifo() { $OSIPS_FIFO = $_[0]; $fifo_reply_path = $_[1]; $cmd_fifo = $_[2]; if (!-e $OSIPS_FIFO){ print "File $OSIPS_FIFO does not exist!\n"; return; } if ( !-w $OSIPS_FIFO ) { print "Error opening OpenSIPS's FIFO " . $OSIPS_FIFO . "\n" . "Make sure you have the line 'modparam(\"mi_fifo\", \"fifo_name\", \" " . $OSIPS_FIFO . "\")' in your config\n" . "and also have loaded the mi_fifo module.\n"; return; } unless ( -p $fifo_reply_path ) { unlink $fifo_reply_path; mkfifo ($fifo_reply_path,666) or die "mkfifo $fifo_reply_path failed: $!"; } `chmod a+rw $fifo_reply_path`; open(ANS,">$OSIPS_FIFO") or die "Could not open $OSIPS_FIFO for writing: $!\n"; print ANS $cmd_fifo; close (ANS); open(FIFO, "< $fifo_reply_path") or die "Couldn't open $fifo_reply_path for reading: $!\n"; my @fifo_line = ; shift(@fifo_line); close (FIFO); unlink $fifo_reply_path; if ( ( $cmd[0] =~ /^fifo$/ ) && ($fifo_line[0] =~ /^\d+/) ) { shift(@fifo_line); print "@fifo_line"; } elsif ( $cmd[0] =~ /^fifo$/ ) { print "@fifo_line"; } else { print "@fifo_line"; } } ##### ----------------------------------------------- ##### ### UNIXSOCK specific variables and functions # sub unixsock_cmd() { my ($CMD,@unix_cmd,@var); my $arg_list=""; if ($cmd[0] eq "unixsock") { if ( $#cmd lt 1 ) { print "unixsock must take at least the command name as parameter\n"; return; } shift(@cmd); @unix_cmd = @cmd; } if ($_[0]) { @unix_cmd = split(" ",$_[0]); } # construct the command now if ( $#unix_cmd == 0){ $CMD = ":" . $unix_cmd[0] . ":\n"; } elsif ( $#unix_cmd gt 0 ){ for (my $i = 1; $i < $#unix_cmd +1; $i++) { $arg_list = join("",$arg_list,$unix_cmd[$i],":\n"); } $CMD = ":" . $unix_cmd[0] . ":\n" . $arg_list; } @var = `printf "$CMD" | $OSIPSUNIX $OSIPS_UNIXSOCK`; if ( $var[0] =~ /^200 OK$/) { shift(@var); } print "@var"; print "\nUNIXSOCK command was:\n$CMD"; } sub unixsock_opensips_monitor() { #clear #tput cup 0 0 # print_stats $attempt #print "[cycle #: $attempt; if constant make sure server lives]\n"; &unixsock_cmd("version"); &unixsock_cmd("uptime"); print "\nTransaction Statistics\n"; &unixsock_cmd(join(" ","get_statistics","UAS_transactions")); &unixsock_cmd(join(" ","get_statistics","UAC_transactions")); &unixsock_cmd(join(" ","get_statistics","inuse_transactions")); print "\nStateless Server Statistics\n"; &unixsock_cmd(join(" ","get_statistics","sent_replies")); &unixsock_cmd(join(" ","get_statistics","sent_err_replies")); &unixsock_cmd(join(" ","get_statistics","received_ACKs")); print "\nUsrLoc Stats\n"; &unixsock_cmd(join(" ","get_statistics","registered_users")); &unixsock_cmd(join(" ","get_statistics","location-users")); &unixsock_cmd(join(" ","get_statistics","location-contacts")); &unixsock_cmd(join(" ","get_statistics","location-expires")); return; } ##### ----------------------------------------------- ##### ### UDP specific variables and functions # sub udp_cmd() { my ($CMD, @udp_cmd, @var, $portaddr, $msg, @recv_msg, $str, $MAXLEN); my $arg_list=""; if ($cmd[0] eq "udp") { if ( $#cmd lt 1 ) { print "upd_cmd must take at least the command name as parameter\n"; return; } shift(@cmd); @udp_cmd = @cmd; } if ($_[0]) { @udp_cmd = split(" ",$_[0]); } # construct the command now #$CMD = ":" . $unix_cmd[0] . ":\n"; if ( $#udp_cmd == 0){ $CMD = ":" . $udp_cmd[0] . ":\n"; } elsif ( $#udp_cmd gt 0 ){ for (my $i = 1; $i < $#udp_cmd +1; $i++) { $arg_list = join("",$arg_list,$udp_cmd[$i],":\n"); } $CMD = ":" . $udp_cmd[0] . ":\n" . $arg_list; } $MAXLEN = 2048; socket(SOCKET, PF_INET, SOCK_DGRAM, getprotobyname("udp")) or die "socket: $!"; $portaddr = sockaddr_in($OSIPS_PORT, inet_aton("$OSIPSIP")); send(SOCKET, $CMD, 0, $portaddr) == length($CMD) or die "cannot send msg"; $portaddr = recv(SOCKET, $msg, $MAXLEN, 0); @recv_msg = split("\n",$msg); if ($recv_msg[0] =~ /^\d+/) { shift(@recv_msg); } else { "@recv_msg"; } for (my $i=0; $i<=$#recv_msg; $i++) { print $recv_msg[$i]."\n"; } close (SOCKET); print "\nUDP command was:\n$CMD"; } sub udp_opensips_monitor() { &udp_cmd('version'); &udp_cmd('uptime'); print "\nTransaction Statistics\n"; &udp_cmd(join(" ","get_statistics","UAS_transactions")); &udp_cmd(join(" ","get_statistics","UAC_transactions")); &udp_cmd(join(" ","get_statistics","inuse_transactions")); print "\nStateless Server Statistics\n"; &udp_cmd(join(" ","get_statistics","sent_replies")); &udp_cmd(join(" ","get_statistics","sent_err_replies")); &udp_cmd(join(" ","get_statistics","received_ACKs")); print "\nUsrLoc Stats\n"; &udp_cmd(join(" ","get_statistics","registered_users")); &udp_cmd(join(" ","get_statistics","location-users")); &udp_cmd(join(" ","get_statistics","location-contacts")); &udp_cmd(join(" ","get_statistics","location-expires")); return; } sub xml_do_call() { my $host = $_[0]; my $port = $_[1]; my $query = $_[2]; my ($xml_query, $socket, $answer, $content); #create xmlrpc query $xml_query = "POST /RPC2 HTTP/1.0\nUser_Agent: openser-cp\nHost: ".$host."\nContent-Type: text/xml\nContent-Length: ".length($query)."\n\n".$query."\n"; $socket = new IO::Socket::INET ( PeerAddr => $host, PeerPort => $port, Proto => 'tcp'); die "Could not create socket: $!\n" unless $socket; #print $xml_query to created socket; print $socket $xml_query; # read the answer while ($answer = <$socket>) { $content .= $answer; } close($socket); return $content; } sub write2xmlrpc() { my $remote_host = $_[0]; my $remote_port = $_[1]; my $xml_cmd = $_[2]; my @args = $_[3]; my ($coder, $string, $server_response, $res, $xml_response); #create xmlrpc client $coder = Frontier::RPC2->new( 'encoding' => 'ISO-8859-1' ); #encode command to xmlrpc format $string = $coder->encode_call($xml_cmd,@args); #obtain xmlrpc response $server_response = &xml_do_call($remote_host,$remote_port,$string); #derefferencing the xmlrpc response $xml_response = ${$coder->string($server_response)}; #parsing the xmlrpc response if ( $xml_response =~ />Too few or too many argumentsRequested command (\D+) is not available!")+22,index($xml_response,"")-43-index($xml_response,"")+21); } #print $res; return $res; } sub xmlrpc_cmd() { my (@xmlrpc_cmd,$xml_command); if ($cmd[0] eq "xmlrpc") { if ( $#cmd lt 1 ) { print "upd_cmd must take at least the command name as parameter\n"; return; } shift(@cmd); @xmlrpc_cmd = @cmd; } if ($_[0]) { @xmlrpc_cmd = split(" ",$_[0]); } $xml_command = shift(@xmlrpc_cmd); #print $xml_command." @xmlrpc_cmd\n"; #result of xmlrpc query my $buf = &write2xmlrpc($OSIPSIP,$OSIPS_PORT,$xml_command,@xmlrpc_cmd); print $buf; } sub xmlrpc_opensips_monitor() { # print_stats $attempt &xmlrpc_cmd('version'); &xmlrpc_cmd('uptime'); print "\nTransaction Statistics\n"; &xmlrpc_cmd(join(" ","get_statistics","UAS_transactions")); &xmlrpc_cmd(join(" ","get_statistics","UAC_transactions")); &xmlrpc_cmd(join(" ","get_statistics","inuse_transactions")); print "\nStateless Server Statistics\n"; &xmlrpc_cmd(join(" ","get_statistics","sent_replies")); &xmlrpc_cmd(join(" ","get_statistics","sent_err_replies")); &xmlrpc_cmd(join(" ","get_statistics","received_ACKs")); print "\nUsrLoc Stats\n"; &xmlrpc_cmd(join(" ","get_statistics","registered_users")); &xmlrpc_cmd(join(" ","get_statistics","location-users")); &xmlrpc_cmd(join(" ","get_statistics","location-contacts")); &xmlrpc_cmd(join(" ","get_statistics","location-expires")); return; } # ##### ------------------------------------------------ ##### ### tls_ca # sub tls_ca() { my ( $CA_BASE, $CA_CONF, $CA_PATH ); if ($#cmd lt 1) { print "Too few parameters!\n"; &usage_tls(); return; } if ($cmd[1] =~ /^rootCA$/) { if ( $#cmd == 1 ) { $CA_BASE = $ETCDIR . "/tls"; } else { $CA_BASE=`(cd $cmd[2];pwd)`; } if ( ! -d $CA_BASE ) { print "Config directory ($CA_BASE) does not exist\n"; return; } $CA_CONF='ca.conf'; $CA_PATH=$CA_BASE."/rootCA"; if ( ! -f $CA_BASE."/".$CA_CONF ) { print "root CA config file ($CA_BASE/$CA_CONF) does not exist\n"; return; } if ( -d $CA_PATH ) { print "root CA directory ($CA_PATH) exists! Remove it (y/n)?"; if ( (my $line = ) !~ /y|Y/ ) { return; } } print "Creating directory $CA_PATH and its sub-tree\n"; system("mkdir -p $CA_PATH"); if ( $? != 0 ) { print "Failed to create root directory $CA_PATH\n"; return; } `rm -fr $CA_PATH/*`; `mkdir $CA_PATH/private`; `mkdir $CA_PATH/certs`; `touch $CA_PATH/index.txt`; `echo 01 >$CA_PATH/serial`; print "Creating CA self-signed certificate\n"; system(" cd $CA_PATH; openssl req -config $CA_BASE/$CA_CONF -x509 -newkey rsa:2048 -days 365 -out ./cacert.pem -outform PEM "); if ( $? != 0 ) { print "Failed to create self-signed certificate\n"; return; } print "Protecting CA private key\n"; system("chmod 600 $CA_PATH/private/cakey.pem"); print "DONE\n"; print "Private key can be found in $CA_PATH/private/cakey.pem\n"; print "Certificate can be found in $CA_PATH/cacert.pem\n"; } elsif ($cmd[1] =~ /^userCERT$/) { if ( $#cmd lt 2 ) { print "Missing user name parameter\n"; return; } if ( $#cmd lt 3 ) { # use default $CA_BASE = $ETCDIR."/tls"; } else { $CA_BASE = `(cd $cmd[3];pwd)`; } if ( ! -d $CA_BASE ) { print "Config directory ($CA_BASE) does not exist\n"; return; } my $USER_DIR = $CA_BASE."/".$cmd[2]; my $USER_CFG = $CA_BASE."/".$cmd[2].".conf"; my $USER = $cmd[2]; my $REQ_CFG=$CA_BASE . "/request.conf"; if ( ! -f $USER_CFG ) { print "User config file $USER_CFG not found\n"; return; } if ( ! -f $REQ_CFG ) { print "Request config file $REQ_CFG not found\n"; return; } print "Using config file $USER_CFG\n"; if ( -d $USER_DIR ) { print "User CERT directory ($USER_DIR) exists! Remove it (y/n)?"; if ( ( my $line = ) =~ /y|Y/ ) { return; } } print "Creating directory $USER_DIR\n"; system(mkdir -p $USER_DIR); if ( $? ne 0 ) { print "Failed to create user directory $USER_DIR\n"; return; } `rm -fr $USER_DIR/*`; print "Creating user certificate request\n"; `openssl req -config $USER_CFG -out $USER_DIR/$USER-cert_req.pem -keyout $USER_DIR/$USER-privkey.pem -new -nodes`; if ( $? ne 0 ) { print "Failed to generate certificate request\n"; return; } print "Signing certificate request\n"; system(" cd $CA_BASE ; openssl ca -config $REQ_CFG -in $USER_DIR/$USER-cert_req.pem -out $USER_DIR/$USER-cert.pem "); if ( $? ne 0 ) { print "Failed to generate certificate request\n"; return; } print "Generating CA list\n"; `cat $CA_BASE/rootCA/cacert.pem >> $USER_DIR/$USER-calist.pem`; print "DONE\n"; print "Private key is locate at $USER_DIR/$USER-privkey.pem\n"; print "Certificate is locate at $USER_DIR/$USER-cert.pem\n"; print "CA-List is locate at $USER_DIR/$USER-calist.pem\n"; } elsif ($cmd[1] =~ /^h$/) { &usage_tls(); } else { print "tls -unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### AVP management # # avp list [-T table] [-u ] # [-a attribute] [-v value] [-t type] ... list AVPs # avp add [-T table] # ............ add AVP (*) # avp rm [-T table] [-u ] # [-a attribute] [-v value] [-t type] ... remove AVP (*) sub avpops() { &require_dbengine(); if ( $#cmd lt 1 ) { print "avp - too few parameters\n"; &usage_avp(); return; } my $CLAUSE=""; my $i; if ($cmd[1] =~ /^list$/) { $CLAUSE = ""; if ($#cmd lt 3) { print "avp - too few parameters\n"; &usage_avp(); return; } #shift(@cmd); $i=2; while ( $i lt $#cmd ){ if ($cmd[$i] =~ /^-T$/) { if ($cmd[1] =~ /(\s+)|-[Tuavt]/ ) { print "table name missing\n"; return; } else { $AVP_TABLE = $cmd[$i+1]; } } elsif ($cmd[$i] =~ /^-u$/) { if ( $cmd[$i+1] =~ /(\s+)|-[Tuavt]/ ){ print "avp list - user id or uuid parameter missing or wrong (user\@domain)\n "; return; } if ( &is_aor($cmd[$i+1]) == 0 ) { &set_user($cmd[$i+1]); if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_USER_COLUMN'}=\'$OSIPSUSER\' AND $avp_table{'AVP_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_USER_COLUMN'}=\'$OSIPSUSER\' AND $avp_table{'AVP_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"; } } else { if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_UUID_COLUMN'} = \'$cmd[$i+1]\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_UUID_COLUMN'}=\'$cmd[$i+1]\'"; } } } elsif ($cmd[$i] =~ /^-a$/) { if ( $cmd[1] =~ /(\s+)|-[Tuavt]/ ){ print "avp list - attribute name parameter missing\n"; return; } if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_ATTRIBUTE_COLUMN'}=\'$cmd[$i+1]\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_ATTRIBUTE_COLUMN'}=\'$cmd[$i+1]\'"; } } elsif ($cmd[$i] =~ /^-v$/) { if ( $cmd[$i+1] =~ /(\s+)|-[Tuavt]/ ){ print "avp list - value parameter missing\n"; return; } if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_VALUE_COLUMN'}=\'$cmd[$i+1]\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_VALUE_COLUMN'}=\'$cmd[$i+1]\'"; } } elsif ($cmd[$i] =~ /^-t$/) { if ( $cmd[$i+1] =~ /(\s+)|-[Tuavt]/ ){ print "avp list - type parameter missing\n"; return; } if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_TYPE_COLUMN'}=\'$cmd[$i+1]\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_TYPE_COLUMN'}=\'$cmd[$i+1]\'"; } } else { print "avp list - unknown parameter $cmd[$i]\n"; } $i+=2; } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_select_where($CARRIERROUTE_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD", " SELECT $avp_table{'AVP_UUID_COLUMN'},$avp_table{'AVP_USER_COLUMN'},$avp_table{'AVP_DOMAIN_COLUMN'}, $avp_table{'AVP_ATTRIBUTE_COLUMN'},$avp_table{'AVP_TYPE_COLUMN'},$avp_table{'AVP_VALUE_COLUMN'} FROM $AVP_TABLE $CLAUSE " ); } else { $sth = $dbh->prepare( "SELECT $avp_table{'AVP_UUID_COLUMN'},$avp_table{'AVP_USER_COLUMN'},$avp_table{'AVP_DOMAIN_COLUMN'}, $avp_table{'AVP_ATTRIBUTE_COLUMN'},$avp_table{'AVP_TYPE_COLUMN'},$avp_table{'AVP_VALUE_COLUMN'} FROM $AVP_TABLE $CLAUSE" ); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; $result = $#row+1; } if ( $result == 0 ) { print "No entry found having the corresponding attributes!\n"; } $sth->finish(); } } elsif ($cmd[1] =~ /^add$/) { my $AVP_UUID=""; if ( $#cmd != 7 ) { if ( $#cmd != 9 ) { print "avp add - bad number of parameters\n"; return; } } if ( $#cmd eq 9 ) { if ( $cmd[2] =~ /-T/ ) { $AVP_TABLE=$cmd[3]; } else { print "avp add - unknown parameter $cmd[2]\n"; return; } } if ( &is_aor($cmd[4]) eq 0 ) { &set_user($cmd[4]); } else { $AVP_UUID = $cmd[4]; } my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $date = join("-",$year+1900,$mon+1,$mday+1); my $time = join(":",$hour,$min,$sec); my $last_modified = join(" ",$date,$time); my $unix_time = time(); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); my $value = join(" ",$AVP_UUID,$cmd[5],$cmd[6],$cmd[7],$last_modified); &bdb_insert($CARRIERROUTE_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD", "INSERT INTO $AVP_TABLE ($avp_table{'AVP_UUID_COLUMN'},$avp_table{'AVP_USER_COLUMN'}, $avp_table{'AVP_DOMAIN_COLUMN'},$avp_table{'AVP_ATTRIBUTE_COLUMN'}, $avp_table{'AVP_TYPE_COLUMN'}, $avp_table{'AVP_VALUE_COLUMN'},$avp_table{'AVP_MODIFIED_COLUMN'}) VALUES (\'$AVP_UUID\',\'$OSIPSUSER\',\'$OSIPSDOMAIN\',\'$cmd[5]\',$cmd[6],\'$cmd[7]\',$unix_time)" ); } else { $sth = $dbh->prepare ("INSERT INTO $AVP_TABLE ($avp_table{'AVP_UUID_COLUMN'},$avp_table{'AVP_USER_COLUMN'}, $avp_table{'AVP_DOMAIN_COLUMN'},$avp_table{'AVP_ATTRIBUTE_COLUMN'}, $avp_table{'AVP_TYPE_COLUMN'}, $avp_table{'AVP_VALUE_COLUMN'},$avp_table{'AVP_MODIFIED_COLUMN'}) VALUES (\'$AVP_UUID\',\'$OSIPSUSER\',\'$OSIPSDOMAIN\',\'$cmd[5]\',$cmd[6],\'$cmd[7]\',\'$last_modified\')" ); ##execute the query $sth->execute( ); warn "Data was not inserted in database!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^rm$/) { $CLAUSE = ""; if ($#cmd lt 3) { print "avp - too few parameters\n"; &usage_avp(); return; } $i=2; while ( $i lt $#cmd ){ if ($cmd[$i] =~ /^-T$/ ) { if ( $cmd[$i+1] =~ /(\s+)|-[Tuavt]/ ) { print "avp rm - table name parameter missing\n"; return; } else { $AVP_TABLE = $cmd[$i+1]; } } elsif ($cmd[$i] =~ /^-u$/) { if ( $cmd[$i+1] =~ /(\s+)|-[Tuavt]/ ){ print "avp rm - user id uuid or parameter missing\n"; return; } if ( &is_aor($cmd[$i+1]) == 0 ) { &set_user($cmd[$i+1]); if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_USER_COLUMN'}=\'$OSIPSUSER\' AND $avp_table{'AVP_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_USER_COLUMN'}=\'$OSIPSUSER\' AND $avp_table{'AVP_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"; } } else { if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_UUID_COLUMN'} = \'$cmd[$i+1]\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_UUID_COLUMN'}=\'$cmd[$i+1]\'"; } } } elsif ($cmd[$i] =~ /^-a$/) { if ( $cmd[$i+1] =~ /(\s+)|-[Tuavt]/ ){ print "avp rm - attribute name parameter missing\n"; return; } if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_ATTRIBUTE_COLUMN'}=\'$cmd[$i+1]\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_ATTRIBUTE_COLUMN'}=\'$cmd[$i+1]\'"; } } elsif ($cmd[$i] =~ /^-v$/) { if ( $cmd[$i+1] =~ /(\s+)|-[Tuavt]/ ){ print "avp rm - value parameter missing\n"; return; } if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_VALUE_COLUMN'}=\'$cmd[$i+1]\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_VALUE_COLUMN'}=\'$cmd[$i+1]\'"; } } elsif ($cmd[$i] =~ /^-t$/) { if ( $cmd[$i+1] =~ /(\s+)|-[Tuavt]/ ){ print "avp rm - type parameter missing\n"; return; } if ( $CLAUSE eq "" ) { $CLAUSE = " WHERE $avp_table{'AVP_TYPE_COLUMN'}=\'$cmd[$i+1]\'"; } else { $CLAUSE = "$CLAUSE AND $avp_table{'AVP_TYPE_COLUMN'}=\'$cmd[$i+1]\'"; } } else { print "avp list - unknown parameter $cmd[2]\n"; } $i+=2; } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_delete($CARRIERROUTE_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD", "DELETE FROM $AVP_TABLE $CLAUSE "); } else { #prepare query $sth = $dbh->prepare( "DELETE FROM $AVP_TABLE $CLAUSE " ); #execute the query $sth->execute( ); warn "Deleting data from table failed", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^(help$)|(h$)/) { &usage_avp(); } else { print "avp -unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### DB operations # sub db_ops() { if ($#cmd lt 1) { print "Too few parameters\n"; &usage_db_ops(); return; } for(my $i=3; $i<=$#cmd; $i++){ $cmd[2] = join(" ",$cmd[2],$cmd[$i]); } &require_dbengine(); if ($cmd[1] =~ /(^exec$)|(^query$)/) { if ( $#cmd lt 2 ) { print "Missing query parameter\n"; return; } &db_query($cmd[2]); } elsif ($cmd[1] =~ /(^roexec$)|(^roquery$)/) { if ( $#cmd lt 2 ) { print "Missing query parameter\n"; return; } &db_query($cmd[2]); } elsif ($cmd[1] =~ /^run$/) { my $QUERY; if ( $#cmd lt 2 ) { print "Missing query parameter\n"; return; } $QUERY = system("eval \$$cmd[2]"); #if ( $? != 0 ) { # print "Missing query value\n"; # return; #} &db_query($QUERY); } elsif ($cmd[1] =~ /^rorun$/) { my $QUERY; if ( $#cmd lt 2 ) { print "Missing query parameter\n"; return; } system ("eval $QUERY = \$$cmd[2]"); #if ( $QUERY != 0 ) { # print "Missing query value\n"; # return; #} &db_query($QUERY); } elsif ($cmd[1] =~ /^show$/) { if ( $#cmd != 2 ) { print "Missing table parameter\n"; return; } &db_query("SELECT * FROM $cmd[2]"); } elsif ($cmd[1] =~ /^h$/) { &usage_db_ops(); } else { print "db -unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### speeddial # sub speeddial() { my $TMP_OSIPSUSER; my $TMP_OSIPSDOMAIN; if ($#cmd lt 1) { print "Too few parameters!\n"; &usage_speeddial(); return; } &require_dbengine(); if ($cmd[1] =~ /^list$/) { if ($#cmd == 2) { # print speed-dials for user if ( &check_aor($cmd[2]) != 0 ) { print "speeddial - <$cmd[2]> is not a valid AoR (user\@domain)\n"; return; } &set_user($cmd[2]); print "Dumping speed-dials for user=<$cmd[2]>\n"; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$OSIPSUSER,$OSIPSDOMAIN); &bdb_select_where($SD_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD","SELECT CONCAT($sd_table{'SD_SD_USER_COLUMN'},'\@',$sd_table{'SD_SD_DOMAIN_COLUMN'}) AS Short_number, $sd_table{'SD_NEW_URI_COLUMN'} AS New_URI,$sd_table{'SD_DESC_COLUMN'} FROM $SD_TABLE WHERE $sd_table{'SD_USER_COLUMN'}=\'$OSIPSUSER\' AND $sd_table{'SD_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); } elsif ( $DBENGINE =~ /^Pg$/ ) { $sth = $dbh->prepare("SELECT ($sd_table{'SD_SD_USER_COLUMN'}||'\@'||$sd_table{'SD_SD_DOMAIN_COLUMN'}) AS Short_number, $sd_table{'SD_NEW_URI_COLUMN'} AS New_URI,$sd_table{'SD_DESC_COLUMN'} FROM $SD_TABLE WHERE $sd_table{'SD_USER_COLUMN'}=\'$OSIPSUSER\' AND $sd_table{'SD_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); #execute the query $sth->execute( ); warn "Entry could not be retrieved from table\n", $sth->errstr( ), "\n" if $sth->err( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } } else { $sth = $dbh->prepare("SELECT CONCAT($sd_table{'SD_SD_USER_COLUMN'},'\@',$sd_table{'SD_SD_DOMAIN_COLUMN'}) AS Short_number, $sd_table{'SD_NEW_URI_COLUMN'} AS New_URI,$sd_table{'SD_DESC_COLUMN'} FROM $SD_TABLE WHERE $sd_table{'SD_USER_COLUMN'}=\'$OSIPSUSER\' AND $sd_table{'SD_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); #execute the query $sth->execute( ); warn "Entry could not be retrieved from table\n", $sth->errstr( ), "\n" if $sth->err( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } $sth->finish(); } } elsif ($#cmd == 1) { print "Dumping all speed-dials may take long: do you want to proceed? [Y|N] "; if ( (my $line = ) =~ /(^y$)|(^Y$)/ ) { print "Dumping all speed-dials...\n"; } else { return; } if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_select($SD_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD","SELECT CONCAT($sd_table{'SD_SD_USER_COLUMN'},'\@',$sd_table{'SD_SD_DOMAIN_COLUMN'}) AS Short_number, CONCAT($sd_table{'SD_USER_COLUMN'},'\@',$sd_table{'SD_DOMAIN_COLUMN'}) AS Owner, $sd_table{'SD_NEW_URI_COLUMN'} AS New_URI,$sd_table{'SD_DESC_COLUMN'} FROM $SD_TABLE"); } elsif ( $DBENGINE =~ /^Pg$/ ) { $sth = $dbh->prepare("SELECT ($sd_table{'SD_SD_USER_COLUMN'}||'\@'||$sd_table{'SD_SD_DOMAIN_COLUMN'}) AS Short_number, ($sd_table{'SD_USER_COLUMN'}||'\@'||$sd_table{'SD_DOMAIN_COLUMN'}) AS Owner, $sd_table{'SD_NEW_URI_COLUMN'} AS New_URI,$sd_table{'SD_DESC_COLUMN'} FROM $SD_TABLE"); #execute the query $sth->execute( ); warn "Entry could not be retrieved from table\n", $sth->errstr( ), "\n" if $sth->err( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } } else { $sth = $dbh->prepare("SELECT CONCAT($sd_table{'SD_SD_USER_COLUMN'},'\@',$sd_table{'SD_SD_DOMAIN_COLUMN'}) AS Short_number, CONCAT($sd_table{'SD_USER_COLUMN'},'\@',$sd_table{'SD_DOMAIN_COLUMN'}) AS Owner, $sd_table{'SD_NEW_URI_COLUMN'} AS New_URI,$sd_table{'SD_DESC_COLUMN'} FROM $SD_TABLE"); #execute the query $sth->execute( ); warn "Entry could not be retrieved from table\n", $sth->errstr( ), "\n" if $sth->err( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } } } } elsif ($cmd[1] =~ /^show$/) { if ( $#cmd lt 2 ) { print "speeddial - wrong number of params for command [show]\n"; &usage_speeddial(); return; } if (&check_aor($cmd[2]) != 0 ) { print "speeddial - $cmd[2] is not a valid AoR (user\@domain)\n"; return; } &set_user($cmd[2]); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join("",$OSIPSUSER,$OSIPSDOMAIN); &bdb_select_where($SD_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD","SELECT CONCAT($sd_table{'SD_USER_COLUMN'},'\@',$sd_table{'SD_DOMAIN_COLUMN'}) AS Owner, $sd_table{'SD_NEW_URI_COLUMN'} AS New_URI, $sd_table{'SD_DESC_COLUMN'} FROM $SD_TABLE WHERE $sd_table{'SD_SD_USER_COLUMN'}=\'$OSIPSUSER\' AND $sd_table{'SD_SD_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); } elsif ( $DBENGINE =~ /^Pg$/ ) { $sth = $dbh->prepare("SELECT ($sd_table{'SD_USER_COLUMN'}||'\@'||$sd_table{'SD_DOMAIN_COLUMN'}) AS Owner, $sd_table{'SD_NEW_URI_COLUMN'} AS New_URI, $sd_table{'SD_DESC_COLUMN'} FROM $SD_TABLE WHERE $sd_table{'SD_SD_USER_COLUMN'}=\'$OSIPSUSER\' AND $sd_table{'SD_SD_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); $sth->execute( ); warn "Entry could not be retrieved from table\n", $sth->errstr( ), "\n" if $sth->err( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } } else { $sth = $dbh->prepare("SELECT CONCAT($sd_table{'SD_USER_COLUMN'},'\@',$sd_table{'SD_DOMAIN_COLUMN'}) AS Owner, $sd_table{'SD_NEW_URI_COLUMN'} AS New_URI, $sd_table{'SD_DESC_COLUMN'} FROM $SD_TABLE WHERE $sd_table{'SD_SD_USER_COLUMN'}=\'$OSIPSUSER\' AND $sd_table{'SD_SD_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); $sth->execute( ); warn "Entry could not be retrieved from table\n", $sth->errstr( ), "\n" if $sth->err( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } } } elsif ($cmd[1] =~ /^add$/) { if ( $#cmd != 4 ) { if ( $#cmd != 5 ) { print "speeddial - wrong number of parameters\n"; &usage_speeddial(); return; } } if ( &check_aor($cmd[2]) != 0 ) { print "speeddial - $cmd[2] is not a valid AoR (user\@domain)\n"; return; } if ( &check_aor($cmd[3]) != 0 ) { print "speeddial - $cmd[2] is not a valid AoR (user\@domain)\n"; return; } if ( &check_sipaor($cmd[4]) != 0 ) { print "speeddial - $cmd[4] is not a valid SIP AoR (sip:user\@domain)\n"; return; } &set_user($cmd[2]); $TMP_OSIPSUSER = $OSIPSUSER; $TMP_OSIPSDOMAIN = $OSIPSDOMAIN; &set_user($cmd[3]); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$TMP_OSIPSUSER,$TMP_OSIPSDOMAIN); my $value = join(" ",$OSIPSUSER,$OSIPSDOMAIN,$cmd[4],$cmd[5]); &bdb_insert($SD_TABLE,$key,$value); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD","INSERT INTO $SD_TABLE ($sd_table{'SD_USER_COLUMN'},$sd_table{'SD_DOMAIN_COLUMN'},$sd_table{'SD_SD_USER_COLUMN'},$sd_table{'SD_SD_DOMAIN_COLUMN'},$sd_table{'SD_NEW_URI_COLUMN'},$sd_table{'SD_DESC_COLUMN'}) VALUES (\'$TMP_OSIPSUSER\',\'$TMP_OSIPSDOMAIN\',\'$OSIPSUSER\',\'$OSIPSDOMAIN\',\'$cmd[4]\',\'$cmd[5]\')"); } else { $sth = $dbh->prepare("INSERT INTO $SD_TABLE ($sd_table{'SD_USER_COLUMN'},$sd_table{'SD_DOMAIN_COLUMN'},$sd_table{'SD_SD_USER_COLUMN'},$sd_table{'SD_SD_DOMAIN_COLUMN'},$sd_table{'SD_NEW_URI_COLUMN'},$sd_table{'SD_DESC_COLUMN'}) VALUES (\'$TMP_OSIPSUSER\',\'$TMP_OSIPSDOMAIN\',\'$OSIPSUSER\',\'$OSIPSDOMAIN\',\'$cmd[4]\',\'$cmd[5]\')"); #execute the query $sth->execute(); warn "Entry could not be inserted into table", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^rm$/) { if ($DBENGINE =~ /^DB_BERKELEY$/) { if ( $#cmd != 2 ) { print "Wrong number of parameters!\n"; return; } } else { if ( $#cmd != 3 ) { print "speeddial rm - invalid number of parameters\n"; &usage_speeddial(); return; } } if ( &check_aor($cmd[2]) != 0 ) { print "speeddial - $cmd[2] not a valid AoR (user\@domain)\n"; return; } if ( &check_aor($cmd[2]) != 0 ) { print "speeddial - $cmd[3] is not a valid AoR (user\@domain)\n"; return; } &set_user($cmd[2]); $TMP_OSIPSUSER = $OSIPSUSER; $TMP_OSIPSDOMAIN = $OSIPSDOMAIN; &set_user($cmd[3]); if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = join(" ",$TMP_OSIPSUSER,$TMP_OSIPSDOMAIN); &bdb_delete($SD_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD","DELETE FROM $SD_TABLE WHERE $sd_table{'SD_USER_COLUMN'}=\'$TMP_OSIPSUSER\' AND $sd_table{'SD_DOMAIN_COLUMN'}=\'$TMP_OSIPSDOMAIN\' AND $sd_table{'SD_SD_USER_COLUMN'}=\'$OSIPSUSER\' AND $sd_table{'SD_SD_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); } else { $sth = $dbh->prepare("DELETE FROM $SD_TABLE WHERE $sd_table{'SD_USER_COLUMN'}=\'$TMP_OSIPSUSER\' AND $sd_table{'SD_DOMAIN_COLUMN'}=\'$TMP_OSIPSDOMAIN\' AND $sd_table{'SD_SD_USER_COLUMN'}=\'$OSIPSUSER\' AND $sd_table{'SD_SD_DOMAIN_COLUMN'}=\'$OSIPSDOMAIN\'"); #execute the query $sth->execute( ); warn "Deleting entry failed!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[1] =~ /^(help$)|(h$)/) { &usage_speeddial(); } else { print "speeddial -unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### online # sub opensips_online() { my $var; &require_ctlengine(); $var = system(&mi_comm("ul_dump")); print $var; if ($var =~ /aor/i) { `echo $var | awk '{print $2}' | sort | sort -mu`; } } # ##### ------------------------------------------------ ##### ### dialplan # sub opensips_dialplan() { if ( $#cmd lt 1 ) { print "Too few parameters\n"; &usage_dialplan(); return; } if ($cmd[1] =~ /^show$/) { if ( $#cmd == 2 ) { print "dialplan $cmd[2] tables\n"; if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," SELECT * FROM $DIALPLAN_TABLE WHERE $dialplan_table{'DIALPLAN_DPID_COLUMN'} = $cmd[2] ORDER BY $dialplan_table{'DIALPLAN_PR_COLUMN'} "); } else { $sth = $dbh->prepare( " SELECT * FROM $DIALPLAN_TABLE WHERE $dialplan_table{'DIALPLAN_DPID_COLUMN'} = $cmd[2] ORDER BY $dialplan_table{'DIALPLAN_PR_COLUMN'} " ); #execute query $sth->execute( ); warn "Entry could not be retrieved from table\n", $sth->errstr( ), "\n" if $sth->err( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } $sth->finish( ); } } else { print "dialplan tables\n"; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { &bdb_select($DIALPLAN_TABLE); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD", " SELECT * FROM $DIALPLAN_TABLE ORDER BY $dialplan_table{'DIALPLAN_DPID_COLUMN'}, $dialplan_table{'DIALPLAN_PR_COLUMN'} " ); } else { $sth = $dbh->prepare( " SELECT * FROM $DIALPLAN_TABLE ORDER BY $dialplan_table{'DIALPLAN_DPID_COLUMN'}, $dialplan_table{'DIALPLAN_PR_COLUMN'} " ); #execute query $sth->execute( ); warn "Entry could not be retrieved from table\n", $sth->errstr( ), "\n" if $sth->err( ); ## Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "@row\n"; } $sth->finish(); } } } elsif ($cmd[1] =~ /^reload$/) { &mi_comm('dp_reload'); } elsif ($cmd[1] =~ /^addrule$/) { if ( $#cmd lt 9 ) { print "Too few parameters\n"; &usage_dialplan(); return; } my $DIALPLAN_DPID = $cmd[2]; my $DIALPLAN_PR = $cmd[3]; my $DIALPLAN_MATCH_OP = $cmd[4]; if ($DIALPLAN_MATCH_OP =~ /^equal$/) { $DIALPLAN_MATCH_OP = 0; } elsif ($DIALPLAN_MATCH_OP =~ /^regexp$/) { $DIALPLAN_MATCH_OP = 1; } else { print "dialplan - unexpected $DIALPLAN_MATCH_OP for operating matching. Use 'equal' or 'regexp'!\n"; return; } my $DIALPLAN_MATCH_EXP = $cmd[5]; my $DIALPLAN_MATCH_LEN = $cmd[6]; my $DIALPLAN_SUBST_EXP = $cmd[7]; my $DIALPLAN_REPL_EXP = $cmd[8]; my $DIALPLAN_ATTRS = $cmd[9]; if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," INSERT INTO $DIALPLAN_TABLE ( $dialplan_table{'DIALPLAN_DPID_COLUMN'}, $dialplan_table{'DIALPLAN_PR_COLUMN'}, $dialplan_table{'DIALPLAN_MATCH_OP_COLUMN'}, $dialplan_table{'DIALPLAN_MATCH_EXP_COLUMN'}, $dialplan_table{'DIALPLAN_MATCH_LEN_COLUMN'}, $dialplan_table{'DIALPLAN_SUBST_EXP_COLUMN'}, $dialplan_table{'DIALPLAN_REPL_EXP_COLUMN'}, $dialplan_table{'DIALPLAN_ATTRS_COLUMN'} ) VALUES ( $DIALPLAN_DPID, $DIALPLAN_PR, $DIALPLAN_MATCH_OP, \'$DIALPLAN_MATCH_EXP\', $DIALPLAN_MATCH_LEN, \'$DIALPLAN_SUBST_EXP\', \'$DIALPLAN_REPL_EXP\', \'$DIALPLAN_ATTRS\') " ); } else { $sth=$dbh->prepare( " INSERT INTO $DIALPLAN_TABLE ( $dialplan_table{'DIALPLAN_DPID_COLUMN'}, $dialplan_table{'DIALPLAN_PR_COLUMN'}, $dialplan_table{'DIALPLAN_MATCH_OP_COLUMN'}, $dialplan_table{'DIALPLAN_MATCH_EXP_COLUMN'}, $dialplan_table{'DIALPLAN_MATCH_LEN_COLUMN'}, $dialplan_table{'DIALPLAN_SUBST_EXP_COLUMN'}, $dialplan_table{'DIALPLAN_REPL_EXP_COLUMN'}, $dialplan_table{'DIALPLAN_ATTRS_COLUMN'} ) VALUES ( $DIALPLAN_DPID, $DIALPLAN_PR, $DIALPLAN_MATCH_OP, \'$DIALPLAN_MATCH_EXP\', $DIALPLAN_MATCH_LEN, \'$DIALPLAN_SUBST_EXP\', \'$DIALPLAN_REPL_EXP\', \'$DIALPLAN_ATTRS\') " ); #execute the query $sth->execute(); warn "Entry could not be inserted into table", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } &mi_comm('dp_reload'); } elsif ($cmd[1] =~ /^rm$/) { print "rm"; if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," DELETE FROM $DIALPLAN_TABLE "); } else { $sth = $dbh->prepare(" DELETE FROM $DIALPLAN_TABLE "); #execute the query $sth->execute( ); warn "Deleting entry failed!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } &mi_comm('dp_reload'); } elsif ($cmd[1] =~ /^rmpid$/) { if ( $#cmd lt 2 ) { print "Too few parameters\n"; &usage_dialplan(); return; } if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," DELETE FROM $DIALPLAN_TABLE WHERE $dialplan_table{'DIALPLAN_DPID_COLUMN'}=$cmd[2]" ); } else { $sth = $dbh->prepare( " DELETE FROM $DIALPLAN_TABLE WHERE $dialplan_table{'DIALPLAN_DPID_COLUMN'}=$cmd[2]" ); #execute the query $sth->execute(); warn "Deleting entry failed!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } &mi_comm('dp_reload'); } elsif ($cmd[1] =~ /^rmrule$/) { if ( $#cmd lt 3 ) { print "Too few parameters\n"; &usage_dialplan(); return; } my $DIALPLAN_DPID = $cmd[2]; my $DIALPLAN_PR = $cmd[3]; if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD","DELETE FROM $DIALPLAN_TABLE WHERE $dialplan_table{'DIALPLAN_DPID_COLUMN'}=$DIALPLAN_DPID AND $dialplan_table{'DIALPLAN_PR_COLUMN'}=$DIALPLAN_PR " ); } else { $sth = $dbh->prepare ( "DELETE FROM $DIALPLAN_TABLE WHERE $dialplan_table{'DIALPLAN_DPID_COLUMN'}=$DIALPLAN_DPID AND $dialplan_table{'DIALPLAN_PR_COLUMN'}=$DIALPLAN_PR " ); #execute query $sth->execute(); warn "Deleting entry failed!", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } &mi_comm('dp_reload'); } elsif ($cmd[1] =~ /^h$/) { &usage_dialplan(); } else { print "dialplan -unknown command $cmd[1]\n"; } } # ##### ------------------------------------------------ ##### ### ps shortcut # sub opensips_ps(){ &require_ctlengine(); &mi_comm("ps"); return; } # ##### ------------------------------------------------ ##### ### version shortcut # sub opensips_version() { &require_ctlengine(); &mi_comm("version"); return; } # ##### ------------------------------------------------ ##### ### database operations # sub opensips_db() { if ($cmd[1] =~ /^migrate$/){ if ($cmd[2] =~ /^h$/) { &usage_db(); return; } elsif ( $#cmd != 2 ) { if ( $DBENGINE !~ /^mysql$/ ) { print $DBENGINE ." doesn't support the migrate operation\n"; return; } if ( $#cmd != 3 ) { print "Migrate requires 2 parameters: old and new database\n"; &usage_db(); return; } # create new database print "Creating new database $cmd[3]....\n"; &mysql_opensips_create($cmd[3]); #if ( $? != 0 ) { # print "Migrate: creating new database failed\n"; # return; #} # migrate data print "Migrating data from $cmd[2] to $cmd[3]....\n"; &mysql_migrate_db($cmd[2],$cmd[3]); #if ( $? == 0 ) { print "Migration successfully completed.\n"; #} } } elsif ($cmd[1] =~ /^copy$/) { my ( $tmp_file); # copy database to some other name if ( ( $DBENGINE =~ /^DB_BERKELEY$/ ) || ( $DBENGINE =~ /^DBTEXT$/ ) ) { print "$DBENGINE don't support this operation\n"; return; } if ( $#cmd != 2 ) { &usage_db(); return; } $tmp_file=`mktemp /tmp/osipsconsole_db.XXXXXXXXXX`; &opensips_dump($DBNAME,$tmp_file); if ( $? != 0 ) { `rm $tmp_file`; return; } &opensips_create($cmd[2]); if ( $? != 0 ) { `rm $tmp_file`; return; } &opensips_restore($cmd[2],$tmp_file); `rm -f $tmp_file`; return; } elsif ($cmd[1] =~ /^backup$/) { if ( ($DBENGINE =~ /^DB_BERKELEY$/) || ( $DBENGINE =~ /^DBTEXT$/ ) ) { print "$DBENGINE doesn't support the backup operation\n"; return; } # backup current database if ($cmd[2] =~ /^h$/) { &usage_db(); return; } elsif ( $#cmd != 2 ) { &usage_db(); return; } &opensips_dump($DBNAME, $cmd[2]); } elsif ($cmd[1] =~ /^restore$/) { if ( ($DBENGINE =~ /^DB_BERKELEY$/) || ($DBENGINE =~ /^dbtext$/) ) { print "$DBENGINE doesn't support the restore operation\n"; return; } if ($cmd[2] =~ /^h$/) { &usage_db(); return; } elsif ( $#cmd != 2 ) { &usage_db(); return; } &opensips_restore($cmd[1],); } elsif ($cmd[1] =~ /^create$/) { if ($cmd[2] =~ /^h$/) { &usage_db(); return; } elsif ( $#cmd != 2 ) { &usage_db(); return; } &opensips_create($cmd[2]); } elsif ($cmd[1] =~ /^presence$/) { if ($cmd[2] =~ /^h$/) { &usage_db(); return; } elsif ( $#cmd != 2 ) { &usage_db(); return; } &presence_create($cmd[2]); } elsif ($cmd[1] =~ /^extra$/) { if ($cmd[2] =~ /^h$/) { &usage_db(); return; } elsif ( $#cmd != 2 ) { &usage_db(); return; } &extra_create($cmd[2]); } elsif ($cmd[1] =~ /^drop$/) { if ($cmd[2] =~ /^h$/) { &usage_db(); return; } elsif ( $#cmd != 2 ) { &usage_db(); return; } &opensips_drop($cmd[2]); } elsif ($cmd[1] =~ /^reinit$/) { # delete database and create a new one # create new database structures if ( $#cmd != 2 ) { &usage_db(); return; } if ( $cmd[2] =~ /^h$/ ) { &usage_db(); return; } &opensips_drop($cmd[2]); &opensips_create($cmd[2]); } elsif ($cmd[1] =~ /(^bdb$)|(^db_berkeley$)/){ &opensips_berkeley(); } else { print "Unknown command\n"; } } sub opensips_create() { my $db = $_[0]; if ($DBENGINE =~ /^mysql$/) { $PW = &prompt_pw(); &mysql_opensips_create($db); } elsif ($DBENGINE =~ /^oracle$/) { #&oracle_opensips_create($db); } elsif ($DBENGINE =~ /^Pg$/) { &pgsql_opensips_create($db); } elsif ($DBENGINE =~ /^DBTEXT$/) { &dbtext_opensips_create($db); } elsif ($DBENGINE =~ /^DB_BERKELEY$/) { &bdb_opensips_create($db); } } sub presence_create() { my $db = $_[0]; if ($DBENGINE =~ /^mysql$/) { $PW=&prompt_pw(); &mysql_presence_create($db); } elsif ($DBENGINE =~ /^oracle$/) { #&oracle_presence_create($db); } elsif ($DBENGINE =~ /^Pg$/) { &pgsql_presence_create($db); } elsif ($DBENGINE =~ /^DBTEXT$/) { &dbtext_presence_create($db); } elsif ($DBENGINE =~ /^DB_BERKELEY$/) { &bdb_presence_create($db); } } sub extra_create() { my $db = $_[0]; if ($DBENGINE =~ /^mysql$/) { $PW = &prompt_pw(); &mysql_extra_create($cmd[1]); } elsif ($DBENGINE =~ /^oracle$/) { #&oracle_extra_create($db); } elsif ($DBENGINE =~ /^Pg$/) { &pgsql_extra_create($db); } elsif ($DBENGINE =~ /^DBTEXT$/) { &dbtext_extra_create($db); } elsif ($DBENGINE =~ /^DB_BERKELEY$/) { &bdb_extra_create($db); } } sub opensips_drop() { my $db = $_[0]; if ($DBENGINE =~ /^mysql$/) { $PW = &prompt_pw(); &mysql_opensips_drop($db); } elsif ($DBENGINE =~ /^oracle$/) { #&oracle_opensips_drop($db); } elsif ($DBENGINE =~ /^Pg$/) { &pgsql_opensips_drop($db); } elsif ($DBENGINE =~ /^DBTEXT$/) { &dbtext_opensips_drop($db); } elsif ($DBENGINE =~ /^DB_BERKELEY$/) { &bdb_opensips_drop($db); } } # pars: # ##### ------------------------------------------------ ##### ### database create functions # sub mysql_opensips_create() { my (@content, $temp, @query, $i, $ans, $TABLE, $test); &validate_dbdata("mysql"); if ( $#_ lt 0 ) { print "opensips_create function takes one parameter\n"; return; } my $db = $_[0]; print "Creating database $db ...\n"; # Users: opensips is the regular user, opensipsro only for reading &mysql_query("create database $db;" . "GRANT ALL PRIVILEGES ON $db.\* TO \'$DBRWUSER\' IDENTIFIED BY \'$DBRWPW\'; " . "GRANT ALL PRIVILEGES ON $db.* TO \'${DBRWUSER}\'\@\'$DBHOST\' IDENTIFIED BY \'$DBRWPW\';"); if ($? == 0 ) { print "Database $db....created\n"; } else { print "Creating database $db failed....Exiting!\n"; return; } foreach $TABLE (@STANDARD_MODULES) { print "Creating core table: $TABLE...\n"; open(TABLE, "< $DB_SCHEMA/$TABLE-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &mysql_query($db, $query[$i]); } } print "Core OpenSIPS tables successfully created.\n"; if ( -e $DB_SCHEMA."/extensions-create.sql" ) { print "Creating custom extensions tables\n"; open(TABLE, "< $DB_SCHEMA/extensions-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &mysql_query($db, $query[$i]); } } print "Install presence related tables? (y/n): "; if ( ( $ans = ) =~ /[(^y)(^Y)]$/ ) { &mysql_presence_create($db); } else { return; } print "Install tables for @EXTRA_MODULES? (y/n): "; if ( ( $ans = ) =~ /[(^y)(^Y)]$/ ) { $HAS_EXTRA="yes"; &mysql_extra_create($db); } else { return; } return; } ### pgsql database create functions sub pgsql_opensips_create() { # pars: my ( $db, $TABLE, @query, $temp, @content, $i, $ans ); &validate_dbdata("postgres"); if ( $#_ != 0 ) { print "opensips_create function takes one param\n"; return; } $db = $_[0]; print "Creating database $db...\n"; &pgsql_query("template1", "create database \"$db\";"); if ( $? != 0 ) { print "Creating database failed!"; return; } #&pgsql_query ($db, "CREATE FUNCTION \"concat\" (text,text) RETURNS text AS \"SELECT \$1 || \$2;\" LANGUAGE \'sql\'; # CREATE FUNCTION \"rand\" () RETURNS double precision AS \'SELECT random();\' LANGUAGE \'sql\';"); #if ( $? != 0 ) { # print "Creating pgsql emulation functions failed!"; # return; #} foreach $TABLE (@STANDARD_MODULES) { print "Creating core table: $TABLE...\n"; open(TABLE, "< $DB_SCHEMA/$TABLE-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &pgsql_query($db, $query[$i]); } if ( $? == 0 ) { print "Table $TABLE was created!\n"; } else { print "Table $TABLE could not be created!\n"; } } &pgsql_query ($db, "CREATE USER $DBRWUSER WITH PASSWORD '$DBRWPW';"); if ( $? == 0 ) { print "Creating user $DBRWUSER in database succeeded!\n"; } else { print "Creating user $DBRWUSER in database failed!\n"; } foreach $TABLE ( @STANDARD_TABLES ) { #print $TABLE; &pgsql_query ( $db, "GRANT ALL PRIVILEGES ON TABLE $TABLE TO $DBRWUSER;"); if ( $TABLE !~ /version/ ) { if ( ($TABLE !~ /dr_gateways/) && ($TABLE !~ /rtpproxy_sockets/) && ($TABLE !~ /dr_rules/) ) { &pgsql_query ( $db, "GRANT ALL PRIVILEGES ON TABLE " . $TABLE . "_id_seq TO $DBRWUSER;"); if ( $? != 0 ) { print "Grant privileges to standard tables failed!\n"; } else { print "Privileges to standard tables succeeded!\n"; } } elsif ( $TABLE =~ /dr_gateways/ ) { &pgsql_query ( $db, "GRANT ALL PRIVILEGES ON TABLE " . $TABLE . "_gwid_seq TO $DBRWUSER;"); if ( $? != 0 ) { print "Grant privileges to standard tables failed!\n"; } else { print "Privileges to standard tables succeeded!\n"; } } elsif ( $TABLE =~ /dr_rules/ ) { &pgsql_query ( $db, "GRANT ALL PRIVILEGES ON TABLE " . $TABLE . "_ruleid_seq TO $DBRWUSER;"); if ( $? != 0 ) { print "Grant privileges to standard tables failed!\n"; } else { print "Privileges to standard tables succeeded!\n"; } } } } if ( -e $DB_SCHEMA."/extensions-create.sql" ) { print "Creating custom extensions tables\n"; open(TABLE, "< $DB_SCHEMA/extensions-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &pgsql_query($db, $query[$i]); } if ($? == 0 ) { print "Custom extension tables were created!\n"; } else { print "Custom extension tables could not be created!\n"; } } print "Core OpenSIPS tables successfully created.\n"; print "Install presence related tables? (y/n): "; if ( ( $ans = ) =~ /[(^y)(^Y)]$/ ) { &pgsql_presence_create($db); } else { return; } print "Install tables for @EXTRA_MODULES? (y/n): "; if ( ( $ans = ) =~ /[(^y)(^Y)]$/ ) { $HAS_EXTRA="yes"; &pgsql_extra_create($db); } else { return; } return; } sub dbtext_opensips_create () { # pars: my ( $DB_PATH, $TABLE, $ANSWER ); &validate_dbdata("dbtext/opensips"); if ( $#_ != 0 ) { print "opensips_create function takes one param (DB_PATH)\n"; return; } $DB_PATH = $_[0]; print "creating DBTEXT tables at: $DB_PATH ...\n"; `mkdir -p -m 777 $DB_PATH`; foreach $TABLE ( @STANDARD_TABLES ) { print "Creating core table: $TABLE\n"; `cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE`; if ( $? != 0 ) { print "Creating core tables failed!\n"; return; } } `chmod -R a+w $DB_PATH`; print "Install presence related tables? (y/n): "; if ( ($ANSWER = ) =~ /y|Y/ ) { &dbtext_presence_create($DB_PATH); } print "Install tables for @EXTRA_MODULES? (y/n): "; if ( ($ANSWER = ) =~ /y|Y/ ) { &dbtext_extra_create($DB_PATH); } } sub bdb_opensips_create() { # pars: my ($TABLE, $ans); &validate_dbdata("db_berkeley/opensips"); if ( $#_ != 0 ) { print "opensips_create param [DB_PATH]\n"; return; } $DB_PATH = $_[0]; if ( ! -d $_[0] ) { print "creating Berkeley DB database at: [$_[0]]\n"; `mkdir -p -m 777 $DB_PATH`; } foreach $TABLE (@STANDARD_TABLES) { print "Creating standard table: $TABLE\n"; system("$LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $_[0] $TABLE"); if ( $? != 0 ) { print "Creating standard tables failed!\n"; return; } } print "Install presence related tables? (y/n): "; if ( ($ans = ) =~ /(^y$)|(^Y$)/ ) { &presence_create($DB_PATH); } print "Install presence related tables? (y/n): "; if ( ($ans = ) =~ /(^y$)|(^Y$)/ ) { &extra_create($DB_PATH); } } # opensips_create # ##### ------------------------------------------------ ##### ### mysql presence create functions # sub mysql_presence_create(){ # pars: my (@content, $temp, @query, $i, $TABLE); &validate_dbdata("mysql"); if ( $#_ lt 0 ) { print "presence_create function takes one parameter\n"; return; } my $db = $_[0]; print "Creating presence tables into $db ...\n"; open(TABLE, "< $DB_SCHEMA/presence-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &mysql_query($db, $query[$i]); } open(TABLE, "< $DB_SCHEMA/rls-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &mysql_query($db, $query[$i]); } } ### pgsql presence create functions # sub pgsql_presence_create(){ # pars: my (@content, $temp, @query, $i, $TABLE); &validate_dbdata("mysql"); if ( $#_ != 0 ) { print "presence_create function takes one parameter\n"; return; } my $db = $_[0]; print "Creating presence tables into $db ...\n"; open(TABLE, "< $DB_SCHEMA/presence-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &pgsql_query($db, $query[$i]); if ($? == 0 ) { print "Presence tables were created!\n"; } else { print "Presence tables could not be created!\n"; } } open(TABLE, "< $DB_SCHEMA/rls-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &pgsql_query($db, $query[$i]); if ($? == 0 ) { print "Presence_rls tables were created!\n"; } else { print "Presence_rls tables could not be created!\n"; } } foreach $TABLE (@PRESENCE_TABLES) { &pgsql_query ($db, "GRANT ALL PRIVILEGES ON TABLE $TABLE TO $DBRWUSER;"); &pgsql_query ($db, "GRANT ALL PRIVILEGES ON TABLE " . $TABLE . "_id_seq TO $DBRWUSER;"); if ($? == 0 ) { print "Granting privileges to presence tables succeeded!\n"; } else { print "Granting privileges to presence tables failed!\n"; } } print "Presence tables successfully created.\n"; } sub dbtext_presence_create () { # pars: my ( $DB_PATH, $TABLE ); &validate_dbdata("mysql"); if ( $#_ != 0 ) { print "presence_create function takes one param (DB_PATH)"; return; } $DB_PATH = $_[0]; print "creating DBTEXT presence tables at: $DB_PATH ...\n"; `mkdir -p $DB_PATH`; foreach $TABLE (@PRESENCE_TABLES) { print "Creating presence table: $TABLE\n"; `cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE`; if ( $? != 0 ) { print "Creating presence tables failed!\n"; return; } } foreach $TABLE (@PRESENCE_RLS_TABLES) { print "Creating presence table: $TABLE\n"; `cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE`; if ( $? != 0 ) { print "Creating presence_rls tables failed!\n"; return; } } } # end presence_create sub bdb_presence_create() {# pars: my ($TABLE); &validate_dbdata("mysql"); if ( $#_ != 0 ) { print "presence_create param [DB_PATH]\n"; return; } $DB_PATH=$_[0]; if ( ! -d $_[0] ) { # Assert: the directory should already exist print "BerkeleyDB directory does not exist at: [$_[0]]\n"; print; } if ( ! -f $_[0]."/version" ) { # Assert: directory should already contain table 'version' print "BerkeleyDB directory does not have VERSION table at: [$_[0]]\n"; return; } foreach $TABLE (@PRESENCE_TABLES) { print "Creating presence table: $TABLE\n"; system("$LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $_[0] $TABLE"); if ( $? != 0 ) { print "Creating presence tables failed!\n"; return; } } foreach $TABLE (@PRESENCE_RLS_TABLES) { print "Creating presence table: $TABLE\n"; system("$LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $_[0] $TABLE"); if ( $? != 0 ) { print "Creating presence tables failed!\n"; return; } } } # end presence_create # ##### ------------------------------------------------ ##### ### mysql extra functions # sub mysql_extra_create() { # pars: my (@content, $temp, @query, $i, $TABLE); &validate_dbdata("mysql"); if ( $#_ lt 0 ) { print "extra_create function takes one param\n"; return; } my $db = $_[0]; print "Creating extra tables into $db...\n"; foreach $TABLE (@EXTRA_MODULES) { print "Creating extra table: $TABLE....\n"; open(TABLE, "< $DB_SCHEMA/$TABLE-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &mysql_query($db, $query[$i]); } } } ### pgsql extra functions sub pgsql_extra_create() { # pars: my (@content, $temp, @query, $i, $TABLE); &validate_dbdata("postgres"); if ( $#_ != 0 ) { print "extra_create function takes one param\n"; return; } my $db = $_[0]; print "Creating extra tables into $db...\n"; foreach $TABLE (@EXTRA_MODULES) { print "Creating extra table: $TABLE\n"; open(TABLE, "< $DB_SCHEMA/$TABLE-create.sql"); @content =
; $temp = "@content"; @query = split(";",$temp); for ( $i=0; $i<$#query; $i++) { &pgsql_query($db, $query[$i]); } } print "Extra tables successfully created.\n"; foreach $TABLE ( @EXTRA_TABLES ) { &pgsql_query ($db, "GRANT ALL PRIVILEGES ON TABLE $TABLE TO $DBRWUSER;" ); if ($? == 0 ) { print "Granting privileges on $TABLE to $DBRWUSER succeeded!\n"; } else { print "Granting priviliges on $TABLE to $DBRWUSER failed!\n"; } if ( $TABLE !~ /route_tree/ ) { &pgsql_query ($db, "GRANT ALL PRIVILEGES ON TABLE " . $TABLE . "_id_seq TO $DBRWUSER;"); if ($? == 0 ) { print "Granting privileges on $TABLE to $DBRWUSER succeeded!\n"; } else { print "Granting priviliges on $TABLE to $DBRWUSER failed!\n"; } } } } sub dbtext_extra_create() { # pars: my ( $DB_PATH, $TABLE ); &validate_dbdata("dbtext/opensips"); if ( $#_ != 0 ) { print "extra_create function takes one param\n"; return; } print "creating DBTEXT extra tables at: $DB_PATH ...\n"; foreach $TABLE (@EXTRA_TABLES) { print "Creating extra table: $TABLE\n"; `cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE`; if ( $? != 0 ) { print "Creating extra tables failed!\n"; return; } } } # end extra_create sub bdb_extra_create() { # pars: my ($TABLE); &validate_dbdata("db_berkeley/opensips"); if ( $#_ != 0 ) { print "extra_create function takes one param (DB_PATH)\n"; return; } $DB_PATH = $_[0]; if ( ! -d $_[0] ) { # Assert: the directory should already exist print "BerkeleyDB directory does not exist at: [$_[0]]"; return; } if ( ! -f $_[0]."/version" ) { # Assert: directory should already contain table 'version' print "BerkeleyDB directory does not have VERSION table at: [$_[0]]\n"; return; } foreach $TABLE (@EXTRA_TABLES) { print "Creating extra table: $TABLE\n"; system("$LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $_[0] $TABLE"); if ( $? != 0 ) { print "Creating extra tables failed!\n"; return; } } } # end extra_create # ##### ------------------------------------------------ ##### ### mysql drop functions # sub mysql_opensips_drop() {# pars: if ( $#_ != 0 ) { print "opensips_drop function takes two parameters!\n"; return; } my $db = $_[0]; &mysql_query($db, "drop database $db"); if ($? == 0 ) { print "Database was successfully dropped!\n"; } else { print "Database could not be dropped!\n"; } return; } ### pgsql drop functions sub pgsql_opensips_drop() {# pars: if ( $#_ != 0 ) { print "opensips_drop function takes two params\n"; return; } &pgsql_query("template1", "drop database \"$_[0]\";"); } sub dbtext_opensips_drop() { # pars: my $DB_PATH; if ( $#_ != 0 ) { print "opensips_drop function takes one param\n"; return; } $DB_PATH = $_[0]; print "DBTEXT ... erasing all files at: $DB_PATH\n"; `rm -rf $DB_PATH`; } sub bdb_opensips_drop() { # pars: my $TABLE; if ( $#_ != 0 ) { print "opensips_drop function takes one param\n"; return; } if ( ! -d $_[0] ) { print "Directory does not exist: $_[0]\n"; } print "Dropping Berkeley DB database at: $_[0] ...\n"; # core if ( -f $_[0]."/version" ) { foreach $TABLE ( @STANDARD_TABLES ) { print "Dropping core table: $TABLE\n"; `rm -f $_[0]/$TABLE`; } } # presence if ( -f $_[0]."/presentity" ) { foreach $TABLE (@PRESENCE_TABLES) { print "Dropping presence table: $TABLE\n"; `rm -f $_[0]/$TABLE`; } } if ( -f $_[0]."/presentity" ) { foreach $TABLE (@PRESENCE_RLS_TABLES) { print "Dropping presence table: $TABLE\n"; `rm -f $_[0]/$TABLE`; } } # extra tables if ( -f $_[0]."/cpl" ) { foreach $TABLE (@EXTRA_TABLES) { print "Dropping extra table: $TABLE\n"; `rm -f $_[0]/$TABLE`; } } # delete db files and directory `rm -rf $_[0]/__db.001`; `rm -rf $_[0]/__db.002`; `rm -rf $_[0]/__db.003`; `rm -rf $_[0]/__db.004`; `rmdir $_[0]`; } ## end drop ###migrate functions ---only for mysql sub mysql_migrate_db() { # 2 parameters (src_db, dst_db) if ( $#_ != 1 ) { print "migrate_db function takes 2 params\n"; return; } my $src_db=$_[0]; my $dst_db=$_[1]; ### acc &mysql_migrate_table($dst_db.".acc", "id,method,from_tag,to_tag,callid,sip_code,sip_reason,time,duration,setuptime,created", $src_db . ".acc", "?id,?method,?from_tag,?to_tag,?callid,?sip_code,?sip_reason,?time,?duration,?setuptime,?created" ); ### missed_calls &mysql_migrate_table($dst_db . ".missed_calls", "id,method,from_tag,to_tag,callid,sip_code,sip_reason,time", $src_db . ".missed_calls", "?id,?method,?from_tag,?to_tag,?callid,?sip_code,?sip_reason,?time" ); ### aliases &mysql_migrate_table($dst_db . ".aliases", "id,username,domain,contact,received,path,expires,q,callid,cseq,last_modified,flags,cflags,user_agent,socket,methods", $src_db . ".aliases", "?id,?username,?domain,?contact,?received,?path,?expires,?q,?callid,?cseq,?last_modified,?flags,?cflags,?user_agent,?socket,?methods" ); ### dbaliases &mysql_migrate_table( $dst_db . ".dbaliases", "id,alias_username,alias_domain,username,domain", $src_db . ".dbaliases" ,"?id,?alias_username,?alias_domain,?username,?domain" ); ### grp &mysql_migrate_table( $dst_db . ".grp", "id,username,domain,grp,last_modified", $src_db . ".grp", "?id,?username,?domain,?grp,?last_modified" ); ### re_grp &mysql_migrate_table( $dst_db . ".re_grp", "id,reg_exp,group_id", $src_db . ".re_grp", "?id,?reg_exp,?group_id" ); ### silo &mysql_migrate_table( $dst_db . ".silo", "id,src_addr,dst_addr,username,domain,inc_time,exp_time,snd_time,ctype,body", $src_db . ".silo", "?id,?src_addr,?dst_addr,?username,?domain,?inc_time,?exp_time,?snd_time,?ctype,?body" ); ### domain &mysql_migrate_table( $dst_db . ".domain", "id,domain,last_modified", $src_db . ".domain", "?id,?domain,?last_modified" ); ### uri &mysql_migrate_table( $dst_db . ".uri", "id,username,domain,uri_user,last_modified", $src_db . ".uri", "?id,?username,?domain,?uri_user,?last_modified" ); ### usr_preferences &mysql_migrate_table($dst_db . ".usr_preferences", "id,uuid,username,domain,attribute,type,value,last_modified", $src_db . ".usr_preferences", "?id,?uuid,?username,?domain,?attribute,?type,?value,?last_modified" ); ### address &mysql_migrate_table( $dst_db . ".address", "id,grp,ip,mask,port,proto,pattern,context_info", $src_db . ".trusted", "?id,?grp,?ip,?mask,?port,?proto,?pattern,?context_info" ); ### speed_dial &mysql_migrate_table( $dst_db . ".speed_dial", "id,username,domain,sd_username,sd_domain,new_uri,fname,lname,description", $src_db . ".speed_dial", "?id,?username,?domain,?sd_username,?sd_domain,?new_uri,?fname,?lname,?description" ); ### gw &mysql_migrate_table( $dst_db . ".gw", "id,gw_name,grp_id,ip_addr,port,uri_scheme,transport,strip,tag,flags", $src_db . ".gw", "?id,?gw_name,?grp_id,?ip_addr,?port,?uri_scheme,?transport,?strip,?tag,?flags" ); ### subscriber &mysql_migrate_table( $dst_db . ".subscriber", "id,username,domain,password,email_address,ha1,ha1b,rpid", $src_db . ".subscriber", "?id,?username,?domain,?password,?email_address,?ha1,?ha1b,?rpid" ); ### load_balancer &mysql_migrate_table( $dst_db . ".load_balancer", "id,group_id,dst_uri,resources,probe_mode,description", $src_db . ".load_balancer", "?id,?group_id,?dst_uri,?resources,?probe_mode,?description" ); ### dispatcher &mysql_migrate_table( $dst_db . ".dispatcher", "id,setid,destination,flags,weight,attrs,description", $src_db . ".dispatcher", "?id,?setid,?destination,?flags,?weight,?attrs,?description" ); ### dialplan &mysql_migrate_table( $dst_db . ".dialplan", "id,dpid,pr,match_op,match_exp,subst_exp,repl_exp,attrs", $src_db . ".dialplan", "?id,?dpid,?pr,?match_op,?match_exp,?subst_exp,?repl_exp,?attrs" ); ### rtpproxy &mysql_migrate_table( $dst_db . ".rtpproxy_sockets", "id,rtpproxy_socket,set_id", $src_db . ".nh_sockets", "?id,?rtpproxy_socket,?set_id" ); ### dr_gateways &mysql_migrate_table( $dst_db . ".dr_gateways", "gwid,type,address,strip,pri_prefix,attrs,probe_mode,description", $src_db . ".dr_gateways", "?gwid,?type,?address,?strip,?pri_prefix,?attrs,?probe_mode,?description" ); ### dr_rules &mysql_migrate_table( $dst_db . ".dr_rules", "ruleid,groupid,prefix,timerec,priority,routeid,gwlist,attrs,description", $src_db . ".dr_rules", "?ruleid,?groupid,?prefix,?timerec,?priority,?routeid,?gwlist,?attrs,?description" ); ### dr_groups &mysql_migrate_table( $dst_db . ".dr_groups", "id,username,domain,groupid,description", $src_db . ".dr_groups", "?id,?username,?domain,?groupid,?description" ); ### dr_carriers &mysql_migrate_table( $dst_db . ".dr_carriers", "id,gwlist,description", $src_db . ".dr_carriers", "?id,?gwlist,?description" ); if ( $HAS_EXTRA eq "yes" ) { ### cpl &mysql_migrate_table( $dst_db . ".cpl", "id,username,domain,cpl_xml,cpl_bin", $src_db . ".cpl", "?id,?username,?domain,?cpl_xml,?cpl_bin" ); ### siptrace &mysql_migrate_table( $dst_db . ".sip_trace", "id,time_stamp,callid,traced_user,msg,method,status,fromip,toip,fromtag,direction", $src_db . ".sip_trace", "?id,?time_stamp,?callid,?traced_user,?msg,?method,?status,?fromip,?toip, ?fromtag,?direction" ); ### imc_rooms &mysql_migrate_table( $dst_db . ".imc_rooms", "id,name,domain,flag", $src_db . ".imc_rooms", "?id,?name,?domain,?flag" ); ### imc_members &mysql_migrate_table( $dst_db. ".imc_members", "id,username,domain,room,flag", $src_db. ".imc_members", "?id,?username,?domain,?room,?flag" ); ### carrierroute &mysql_migrate_table( $dst_db . ".carrierroute", "id,carrier,domain,scan_prefix,flags,mask,prob,strip,rewrite_host,rewrite_prefix,rewrite_suffix,description", $src_db . ".carrierroute", "?id,?carrier,?domain,?scan_prefix,?flags,?mask,?prob,?strip,?rewrite_host,?rewrite_prefix,?rewrite_suffix,?description" ); ### carrierfailureroute &mysql_migrate_table( $dst_db . ".carrierfailureroute", "id,carrier,domain,scan_prefix,host_name,reply_code,flags,mask,next_domain,description", $src_db . ".carrierfailureroute", "?id,?carrier,?domain,?scan_prefix,?host_name,?reply_code,?flags,?mask,?next_domain,?description" ); ### route_tree &mysql_migrate_table( $dst_db . ".route_tree", "id,carrier", $src_db . ".route_tree", "?id,?carrier" ); ### domainpolicy &mysql_migrate_table( $dst_db . ".domainpolicy", "id,rule,type,att,val,description", $src_db . ".domainpolicy", "?id,?rule,?type,?att,?val,?description" ); ### userblacklist &mysql_migrate_table( $dst_db . ".userblacklist", "id,username,domain,prefix,whitelist", $src_db . ".userblacklist", "?id,?username,?domain,?prefix,?whitelist" ); ### globalblacklist &mysql_migrate_table( $dst_db . ".globalblacklist", "id,prefix,whitelist,description", $src_db . ".globalblacklist", "?id,?prefix,?whitelist,?description" ); } } sub mysql_migrate_table(){ # 4 paremeters (dst_table, dst_cols, src_table, src_cols) my $X; if ( $#_ != 3 ) { print "migrate_table function takes 4 parameters\n"; return; } my $dst_table = $_[0]; my $dst_cols = $_[1]; my $src_table = $_[2]; my $src_cols = $_[3]; $src_cols=`echo $src_cols | sed s/?/$src_table./g `; if ( $PW eq "" ) { $X = system("mysql -h $DBHOST -P $DBPORT -u$DBROOTUSER -e \"INSERT INTO $dst_table ($dst_cols) SELECT $src_cols FROM $src_table\""); if ( $? != 0 ) { system("echo $X | $EGREP \"ERROR 1146\" > /dev/null"); if ( $? != 0 ) { print " -- Migrating $_[2] to $_[0].....SKIPPED (no source)\n"; return; } print "ERROR: failed to migrate $src_table to $dst_table!!!"; print "Skip it and continue (y/n)? "; if ( ( my $ans = ) =~ /[yY]$/ ) { return; } } } else { $X = system("mysql -h $DBHOST -P $DBPORT -u$DBROOTUSER -p$PW -e \"INSERT INTO $dst_table ($dst_cols) SELECT $src_cols FROM $src_table\""); if ( $? != 0 ) { system("echo $X | $EGREP \"ERROR 1146\" > /dev/null"); if ( $? != 0 ) { print " -- Migrating $_[2] to $_[0].....SKIPPED (no source)\n"; return; } print "ERROR: failed to migrate $src_table to $dst_table!!!"; print "Skip it and continue (y/n)? "; if ( ( my $ans = ) =~ /[yY]$/ ) { return; } } } print " -- Migrating " . $src_table . " to " . $dst_table . ".....OK\n"; } ##migrate functions # ##### ------------------------------------------------ ##### ### dump database # sub opensips_dump() { if ( $#_ != 1 ) { print "opensips_dump function takes two param\n"; return; } $PW = &prompt_pw(); if ( $DBENGINE =~ /^mysql$/ ) { if ( $PW eq "" ) { system("mysqldump -h $DBHOST -P $DBPORT -u$DBROOTUSER -c -t $_[0] > $_[1]"); if ( $? != 0 ) { print "db dump failed\n"; return; } else { print "db dump successful!\n"; return; } } else { system("mysqldump -h $DBHOST -P $DBPORT -u$DBROOTUSER -p$PW -c -t $_[0] > $_[1]"); if ( $? != 0 ) { print "db dump failed\n"; return; } else { print "db dump successful!\n"; return; } } } elsif ( $DBENGINE =~ /^Pg$/ ) { system("pg_dump -h $DBHOST -U $DBROOTUSER -c $_[0] > $_[1]"); if ( $? != 0 ) { print "db dump failed\n"; return; } } elsif ( $DBENGINE =~ /^oracle$/ ) { #&.... return; } else { print "Unknown database engine !!!!"; return; } print "db dump successful\n"; return; } # ##### ------------------------------------------------ ##### ### restore database # sub opensips_restore() { #pars: if ( $#_ != 1 ) { print "opensips_restore function takes two params\n"; return; } if ( $DBENGINE =~ /^oracle$/ ) { #oracle_restore $1 $2 } elsif ( $DBENGINE =~ /^mysql$/ ) { &mysql_query("$_[0] < $_[1]"); return; #sql_query $1 < $2 } elsif ( $DBENGINE =~ /^Pg$/ ) { &pgsql_query("$_[0] < $_[1]"); return; } print "Database was restorered successfully\n"; } # ##### ------------------------------------------------ ##### ### database query functions # sub mysql_query() { my $MYSQL_CMD="mysql -h $DBHOST -P $DBPORT -u$DBROOTUSER "; if ( $#_ > 0 ) { if ( $PW eq "") { system("$MYSQL_CMD -D $_[0] -e \"$_[1]\""); if ( $? != 0 ) { return 1; } else { return 0; } } else { system("$MYSQL_CMD -p$PW -D $_[0] -e \"$_[1]\""); if ( $? != 0 ) { return 1; } else { return 0; } } } elsif ( $#_ == 0 ) { if ( $PW eq "") { system ("$MYSQL_CMD -e \"$_[0]\""); if ( $? != 0 ) { return 1; } else { return 0; } } else { system ("$MYSQL_CMD -p$PW -e \"$_[0]\""); if ( $? != 0 ) { return 1; } else { return 0; } } } } sub pgsql_query() { my $PGSQL_CMD="psql -h $DBHOST -U $DBROOTUSER"; if ( $#_ gt 0 ) { system("$PGSQL_CMD -d $_[0] -c \"$_[1]\""); if ( $? != 0 ) { return 1; } else { return 0; } } else { system("$PGSQL_CMD \"$_[0]\""); if ( $? != 0 ) { return 1; } else { return 0; } } } # ##### ------------------------------------------------ ##### ### db_ops query functions # sub db_query() { my $query = $_[0]; if ($DBENGINE =~ /^mysql$/) { &db_mysql_query($query); } elsif ($DBENGINE =~ /^Pg$/) { &db_pgsql_query($query); } elsif ($DBENGINE =~ /^DBTEXT$/) { &db_dbtext_query($query); } } # input: sql query, optional mysql command-line params sub db_mysql_query() { my $query = $_[0]; # if password not yet queried, query it now #&prompt_pw(); "MySQL password for user '$DBRWUSER@$DBHOST'" system("$MYSQL -h $DBHOST -P $DBPORT -u$DBRWUSER \"-p$DBRWPW\" -D $DBNAME -e \"$query\";"); if ( $? != 0 ) { return 1; } else { return 0; } } # input: sql query, optional pgsql command-line params sub db_pgsql_query() { my ( $query, $PGPASSWORD ); $query = $_[0]; print "pgsql_query: $PGSQL -A -q -t -P fieldsep=\' \' -h $DBHOST -U $DBRWUSER $DBNAME -c \'$query\'\n"; system("$PGSQL -A -q -t -P fieldsep=\" \" -h $DBHOST -U $DBRWUSER \"-W$DBRWPW\" $DBNAME \ -c \"$query\""); } sub db_dbtext_query() { my $query = $_[0]; #print $DBTEXTCMD." ".$query; system("$DBTEXTCMD","$query"); } sub opensips_berkeley() { # parms: if ($#cmd lt 2 ) { &berkeley_usage(); return; } elsif ($cmd[1] =~ /^h$/) { &berkeley_usage(); return; } if ($cmd[1] =~ /(^list$)|(^ls$)/) { system("ls -al $DB_PATH"); } elsif ($cmd[1] =~ /^cat$/) { &opensips_cat($cmd[2],$DB_PATH); } elsif ($cmd[1] =~ /^swap$/) { &opensips_swap($cmd[2], $DB_PATH); } elsif ($cmd[1] =~ /^append$/) { &opensips_append($cmd[2], $cmd[3], $DB_PATH); } elsif ($cmd[1] =~ /^newappend$/) { #shift #opensips_newappend $1 $2 $DB_PATH #exit $? } elsif ($cmd[1] =~ /^export$/) { &opensips_export($cmd[2],$DB_PATH); } elsif ($cmd[1] =~ /^migrate$/) { &opensips_migrate($cmd[2], $DB_PATH); } elsif ($cmd[1] =~ /^import$/) { #shift #opensips_import $1 $DB_PATH #exit $? } else { &berkeley_usage(); } } ## # MIGRATE (schema) # Examine each plain-txt file in DUMP_DIR # (Assumes that opensips_export was already invoked) # # Migrate converts data from schema-old to schema-new in place. # # After this step is complete the IMPORT should be executed. sub opensips_migrate() { # parms: [DB_PATH] print "db_berkeley migrate not implemented\n"; return; } # cat all rows to STDOUT sub opensips_cat() { # pars: if ( $#_ != 1 ) { print "opensips_cat params [DB_PATH]\n"; return; } system("$DUMP_CMD -p -h $_[1] $_[0]"); } ## # EXPORT existing data to plain-txt files in DUMP_DIR # eg. DB_PATH/version ---> DUMP_DIR/version.txt # # Export is used as part of a DB migration process to another # major version of berkeley db. sub opensips_export() { # parms: [DB_PATH] my ($DUMP_DIR, $PATH, $TABLE); if ( $#_ lt 1 ) { print "opensips_dump parms: [DB_PATH]\n"; return; } $DUMP_DIR = $_[0]; $PATH = $_[1]; # Assert: the DB_PATH directory should already exist if ( ! -d $PATH ) { print "BerkeleyDB directory does not exist at: [$PATH]\n"; return; } # Assert: DB_PATH directory should already contain table 'version' if ( ! -f $PATH."/version" ) { print "BerkeleyDB directory does not have VERSION table at: [$PATH]\n"; return; } # Create dir at to store the exported data if ( ! -d $DUMP_DIR) { print "creating DUMP_DIR at: [$DUMP_DIR]\n"; `mkdir -p $DUMP_DIR`; } else { print "Cleaning out DUMP_DIR to get ready for new data\n"; `rm -rf $DUMP_DIR/*`; } foreach $TABLE (@STANDARD_TABLES) { if ( -f $PATH."/".$TABLE ) { print "Exporting standard table: $TABLE\n"; system("$DUMP_CMD -p -h $PATH $TABLE | perl -pe 's/^\w.*// ; s/^\s(.*)/$DUMP_DIR/' > $DUMP_DIR/$TABLE.txt"); # Check return code to make sure the export worked ok if ( $? != 0 ) { print "Export of standard table failed [$TABLE]\n"; # there was a problem, but it is not something # we can handle here; We can deal with this at import # time. } } else { print "Table not found: [$TABLE]\n"; } } # Dump the PRESENCE tables to plain-text files in DUMP_DIR foreach $TABLE (@PRESENCE_TABLES) { if ( -f $PATH."/".$TABLE ) { print "Exporting presence table: $TABLE\n"; system("$DUMP_CMD -p -h $PATH $TABLE | perl -pe 's/^\w.*// ; s/^\s(.*)/$DUMP_DIR/' > $DUMP_DIR/$TABLE.txt"); if ( $? != 0 ) { print "Export of presence table failed [$TABLE]\n"; } } else { print "Table not found: [$TABLE]\n"; } } # Dump the EXTRA tables to plain-text files in DUMP_DIR foreach $TABLE (@EXTRA_TABLES) { if ( -f $PATH."/".$TABLE ) { print "Exporting extra table: $TABLE\n"; system("$DUMP_CMD -p -h $PATH $TABLE | perl -pe 's/^\w.*// ; s/^\s(.*)/$DUMP_DIR/' > $DUMP_DIR/$TABLE.txt"); if ( $? != 0 ) { print "Export of extra table failed [$TABLE]\n"; } } else { print "Table not found: [$TABLE]\n"; } } print "All tables are now exported to DUMP_DIR: [$DUMP_DIR]\n"; return; } sub opensips_swap() { # parms: [DB_PATH] my ( $DB, $DBNEW, $DBOLD ); if ( $#_ lt 1 ) { print "opensips_swap parms: [DB_PATH]\n"; return; } $DB = $_[1]."/".$_[0]; $DBNEW = $DB.".new"; $DBOLD = $DB.".old"; `cp $DB $DBOLD`; `mv $DBNEW $DB`; } ##### # append process is: # 1. copy DB_PATH/db to DB_PATH/db.new # 2. appends contents of newdata to DB_PATH/db.new # sub opensips_append() { # parms: [DB_PATH] my ( $DB, $DBNEW ); if ( $#_ lt 2 ) { print "opensips_append parms: [DB_PATH]\n"; return; } $DB = $_[2]."/".$_[0]; $DBNEW = $DB.".new"; if ( -e $DBNEW ) { `rm $DBNEW`; } `cp $DB $DBNEW`; # echo "$LOAD_CMD -T -t hash -f $2 -h $3 $1.new" system("$LOAD_CMD -T -t hash -f $_[1] -h $_[2] $_[0].new"); # echo "$LOAD_CMD -r fileid -h $3 $1.new" system("$LOAD_CMD -r fileid -h $_[2] $_[0].new"); } sub opensips_newappend() { # parms: [DB_PATH] my ( $DB, $DBNEW, $TMPENV, $OLDPWD); if ( $#_ lt 2 ) { print "opensips_append parms: [DB_PATH]\n"; return; } $DB = $_[2]."/".$_[0]; $DBNEW = $DB.".new"; if ( -e $DBNEW ) { `rm $DBNEW`; } $TMPENV = "/tmp/sc-$$"; &bdb_opensips_create($TMPENV); `cd $OLDPWD`; system("$LOAD_CMD -T -t hash -f $_[1] -h $TMPENV $_[0]"); `mv $TMPENV/$_[0] $DBNEW`; `rm -rf $TMPENV`; } ## # IMPORT existing plain-txt files from DUMP_DIR to DB_PATH # eg. DUMP_DIR/version.txt --> DB_PATH/version # # import is used as part of DB migrate to another major version of berkeley db. # this will over-write anything in DB_PATH sub opensips_import() { # parms: [DB_PATH] my ($TABLE); if ( $#_ lt 1 ) { print "opensips_dump parms: [DB_PATH]\n"; return; } # Assert: DUMP_DIR (source dir) already exists if ( ! -d $_[0] ) { print "Berkeley DUMP_DIR directory does not exist: [$_[0]]\n"; return; } # Assert: DUMP_DIR directory should already contain table 'version.txt' if ( ! -e $_[0]."/version.txt" ) { print "DUMP_DIR directory does not have VERSION.txt data at: [$_[0]]\n"; return; } # Assert: destination dir exists [DB_PATH] if ( ! -d $_[1] ) { print "Berkeley DB_PATH directory is being created: [$_[1]]"; `mkdir -p $_[1]`; } else { # Wipe out the destination dir to make room for new data print "Berkeley DB_PATH directory is being purged at: [$_[1]]\n"; `rm -rf $_[1]./*`; } # Creates STANDARD tables from plain-text files in DUMP_DIR foreach $TABLE (@STANDARD_TABLES) { if ( -f $_[0]."/".$TABLE."txt" ) { print "Importing standard table: $TABLE\n"; system("$LOAD_CMD -T -t hash -f $_[0]/$TABLE.txt -h $_[1] $TABLE"); # Check return code to make sure the export worked ok if ( $? != 0 ) { print "Import of standard table failed [$TABLE.txt]\n"; print "Create this missing table with bdb_recover.\n"; } } else { print "Import data not found for table: [$TABLE.txt]\n"; print "Create this missing table with bdb_recover.\n"; } } # Creates PRESENCE tables from plain-text files in DUMP_DIR foreach $TABLE (@PRESENCE_TABLES) { if ( -f $_[0]."/".$TABLE.".txt" ) { print "Importing presence table: $TABLE\n"; system("$LOAD_CMD -T -t hash -f $_[0]/$TABLE.txt -h $_[1] $TABLE"); # Check return code to make sure the export worked ok if ( $? != 0 ) { print "Import of presence table failed [$TABLE.txt]\n"; print "Create this missing table with bdb_recover.\n"; } } else { print "Import data not found for table: [$TABLE.txt]\n"; } } foreach $TABLE (@PRESENCE_RLS_TABLES) { if ( -f $_[0]."/".$TABLE.".txt" ) { print "Importing presence table: $TABLE\n"; system("$LOAD_CMD -T -t hash -f $_[0]/$TABLE.txt -h $_[1] $TABLE"); # Check return code to make sure the export worked ok if ( $? != 0 ) { print "Import of presence table failed [$TABLE.txt]\n"; print "Create this missing table with bdb_recover.\n"; } } else { print "Import data not found for table: [$TABLE.txt]\n"; } } # Creates EXTRA tables from plain-text files in DUMP_DIR foreach $TABLE (@EXTRA_TABLES) { if ( -s $_[0]."/".$TABLE.".txt" ) { print "Importing extra table: $TABLE\n"; system("$LOAD_CMD -T -t hash -f $_[0]/$TABLE.txt -h $_[1] $TABLE"); # Check return code to make sure the export worked ok if ( $? != 0 ) { print "Import of extra table failed [$TABLE.txt]\n"; print "Create this missing table with bdb_recover.\n"; } } else { print "Import data not found for table: [$TABLE.txt]\n"; } } print "All tables are now imported to DB_PATH: [$_[1]]\n"; return; } # ##### ------------------------------------------------ ##### ### Berkeley DB control functions # sub bdb_select() { my ($TABLE, $db, $cursor); my ($key, $value) = ("", "") ; $TABLE = $_[0]; unlink $DB_PATH; $db = new BerkeleyDB::Hash -Filename => "$DB_PATH/$TABLE" #-Flags => DB_CREATE or die "Cannot open file $DB_PATH: $! $BerkeleyDB::Error\n" ; # print the contents of the file $cursor = $db->db_cursor() ; while ($cursor->c_get($key, $value, DB_NEXT) == 0) { print "$key -> $value\n" } undef $cursor ; undef $db ; } sub bdb_select_where() { my ( $TABLE, $db, $key, $value, $result ); $TABLE = $_[0]; $key = $_[1]; unlink $DB_PATH; $db = new BerkeleyDB::Hash -Filename => "$DB_PATH/$TABLE" #-Flags => DB_CREATE or die "Cannot open file $DB_PATH: $! $BerkeleyDB::Error\n" ; # Check for existence of a key if ( $db->db_get($key, $value) == 0 ) { print "$key -> $value\n\n"; $result = 1; } else { $result = 0; print "Value does not exist in the database\n"; } undef $db ; return $result; } sub bdb_insert() { my ($TABLE, $db, $key, $value); $TABLE = $_[0]; $key = $_[1]; $value =$_[2]; unlink $DB_PATH; $db = new BerkeleyDB::Hash -Filename => "$DB_PATH/$TABLE" #-Flags => DB_CREATE or die "Cannot open file $DB_PATH: $! $BerkeleyDB::Error\n" ; # Add a few key/value pairs to the file $db->db_put($key, $value,DB_NOOVERWRITE) ; undef $db; } sub bdb_delete() { my ($TABLE, $db, $key); $TABLE = $_[0]; $key = $_[1]; unlink $DB_PATH; $db = new BerkeleyDB::Hash -Filename => "$DB_PATH/$TABLE" #-Flags => DB_CREATE or die "Cannot open file $DB_PATH: $! $BerkeleyDB::Error\n" ; # Delete a key/value pair $db->db_del($key) ; undef $db ; } sub bdb_update() { my ( $TABLE, $db, $key, $value, $status, $cursor, @val_array ); $TABLE = $_[0]; shift(@_); $key = "@_"; unlink $DB_PATH; $db = new BerkeleyDB::Hash -Filename => "$DB_PATH/$TABLE" #-Flags => DB_CREATE or die "Cannot open file $DB_PATH: $! $BerkeleyDB::Error\n" ; # print the contents of the file $cursor = $db->db_cursor() ; if ( defined $key ) { $status = $cursor->c_get($key, $value, DB_SET); if ( $status == 0 ) { @val_array = split(" ",$value); if ( $cmd[0] =~ /^rpid$/) { if ( $cmd[1] =~ /^add$/ ) { if ( $#val_array == 4 ) { $val_array[$#val_array] = $cmd[3]; } elsif ( ( $#val_array == 3 ) && ( $val_array[$#val_array-1] =~ /^s*\d+$/) ) { $val_array[$#val_array] = $cmd[3]; } elsif ( ( $#val_array == 3 ) && ( $val_array[1] =~ /^s*\.@\..\.s*$/) && ($val_array[2] =~ /^s*\d+$/) && ($val_array[3] =~ /^s*\d+$/) ) { $val_array[$#val_array+1] = $cmd[3]; } else { $val_array[$#val_array+1] = $cmd[3]; } } elsif ( $cmd[1] =~ /^rm$/ ) { pop(@val_array); } } elsif ( $cmd[0] =~ /^passwd$/ ) { $val_array[0] = $PASS; } } $status = $cursor->c_put($key, "@val_array", DB_CURRENT); } undef $cursor ; undef $db ; } sub opensips_dr() { my ( @var, $address, $gwid, $type, $strip, $pri_prefix, $description, $i ); my ( $groupid, $prefix, $timerec, $priority, $routeid, $gwlist, $ruleid ); if ( $cmd[1] =~ /(^gateway$)/ ) { if ( $#cmd < 2 ) { print "Too few parameters!"; &usage_dr(); return; } if ( $#cmd > 7 ) { print "Too many parameters!"; &usage_dr(); return; } if ($cmd[2] =~ /^add$/) { $type = 0; $strip = 0; $pri_prefix = 'NULL'; $description = ' '; $i = 0; @var = split ("=",$cmd[3]); $address = $var[1]; if ( ($#cmd > 3) & ($#cmd <= 7) ) { $i += 4; while ( $i < $#cmd+1 ) { @var = split ("=",$cmd[$i]); if ($var[0] =~ /type/) { $type = $var[1]; } elsif ($cmd[2] =~ /strip/) { $strip = $var[1]; } elsif ($cmd[2] =~ /pri_prefix/) { $pri_prefix = $var[1]; } elsif ($cmd[2] =~ /description/) { $description = $var[1]; } $i++; } } #prepare query #if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { # my $key = join(" ",$TMP_OSIPSUSER,$TMP_OSIPSDOMAIN); # my $value = join(" ",$OSIPSUSER,$OSIPSDOMAIN); # &bdb_insert($DA_TABLE,$key,$value); #} els if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," INSERT INTO $DR_GW_TABLE ($dr_gw_table{'DR_GW_ADDRESS_COLUMN'},$dr_gw_table{'DR_GW_TYPE_COLUMN'}, $dr_gw_table{'DR_GW_STRIP_COLUMN'},$dr_gw_table{'DR_GW_PRI_PREFIX_COLUMN'}, $dr_gw_table{'DR_GW_DESCRIPTION_COLUMN'}) VALUES (\'$address\',$type,$strip,\'$pri_prefix\',\'$description\' )"); } else { $sth = $dbh->prepare( "INSERT INTO $DR_GW_TABLE ($dr_gw_table{'DR_GW_ADDRESS_COLUMN'},$dr_gw_table{'DR_GW_TYPE_COLUMN'}, $dr_gw_table{'DR_GW_STRIP_COLUMN'},$dr_gw_table{'DR_GW_PRI_PREFIX_COLUMN'}, $dr_gw_table{'DR_GW_DESCRIPTION_COLUMN'}) VALUES (\'$address\',$type,$strip,\'$pri_prefix\',\'$description\' )"); #execute the query $sth->execute(); warn "Entry could not be inserted into table", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[2] =~ /^rm$/) { @var = split("=",$cmd[3]); $gwid = $var[1]; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = $gwid; &bdb_delete($DR_GW_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," DELETE FROM $DR_GW_TABLE WHERE $dr_gw_table{'DR_GW_GWID_COLUMN'}=$gwid "); } else { $sth = $dbh->prepare(" DELETE FROM $DR_GW_TABLE WHERE $dr_gw_table{'DR_GW_GWID_COLUMN'}=$gwid "); #execute the query $sth->execute(); warn "Entry could not be deleted from table", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[2] =~ /^list$/) { if ( $#cmd == 2 ) { if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," SELECT * FROM $DR_GW_TABLE "); } else { $sth = $dbh->prepare( " SELECT * FROM $DR_GW_TABLE " ); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "gwid=$row[0] type=$row[1] address=$row[2] strip=$row[3] pri_prefix=$row[4] description=$row[5]\n"; } $sth->finish(); } return; } elsif ( $#cmd == 3 ) { @var = split("=",$cmd[3]); if ($var[0] =~ /^type$/) { $type = $var[1]; #prepare query #if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { # my $key = ; # &bdb_select_where($DR_GW_TABLE,$key); #} els if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," SELECT * FROM $DR_GW_TABLE WHERE $dr_gw_table{'DR_GW_TYPE_COLUMN'}=$var[1] "); } else { $sth = $dbh->prepare( " SELECT * FROM $DR_GW_TABLE WHERE $dr_gw_table{'DR_GW_TYPE_COLUMN'}=$var[1]"); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "gwid=$row[0] type=$row[1] address=$row[2] strip=$row[3] ". " pri_prefix=$row[4] description=$row[5]\n"; } $sth->finish(); } } elsif ($var[0] =~ /^address$/) { #prepare query #if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { # my $key = join(" ",$TMP_OSIPSUSER,$TMP_OSIPSDOMAIN); # &bdb_select_where($DA_TABLE,$key,$value); #} els if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," SELECT * FROM $DR_GW_TABLE WHERE $dr_gw_table{'DR_GW_ADDRESS_COLUMN'}=\'$var[1]\' "); } else { $sth = $dbh->prepare( " SELECT * FROM $DR_GW_TABLE WHERE $dr_gw_table{'DR_GW_ADDRESS_COLUMN'}=\'$var[1]\' " ); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "gwid=$row[0] type=$row[1] address=$row[2] strip=$row[3] ". " pri_prefix=$row[4] description=$row[5]\n"; } $sth->finish(); } } } } elsif ($cmd[2] =~ /^h$/) { &usage_dr(); } else {print "Not an droute option!!\n"}; } elsif ( $cmd[1] =~ /(^rules$)/ ) { if ( $#cmd lt 2 ) { print "Too few parameters!"; &usage_dr(); return; } elsif ( $#cmd gt 9 ) { print "Too many parameters!"; &usage_dr(); return; } if ($cmd[2] =~ /^add$/) { $groupid = ' '; $prefix = ' '; $timerec = ' '; $priority = 0; $routeid = 0; $gwlist = ' '; $description = ' '; $i = 0; @var = split ("=",$cmd[3]); $gwlist = $var[1]; if ( ($#cmd > 3) & ($#cmd <= 9) ) { $i += 4; while ( $i < $#cmd+1 ) { @var = split ("=",$cmd[$i]); if ($var[0] =~ /^groupid$/) { $groupid = $var[1]; } elsif ($var[0] =~ /^prefix$/) { $prefix = $var[1]; } elsif ($var[0] =~ /^timerec$/) { $timerec = $var[1]; } elsif ($var[0] =~ /^priority$/) { $priority = $var[1]; } elsif ($var[0] =~ /^routeid$/) { $routeid = $var[1]; } elsif ($var[0] =~ /^description$/) { $description = $var[1]; } $i++; } } #prepare query #if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { # my $key = join(" ",$TMP_OSIPSUSER,$TMP_OSIPSDOMAIN); # my $value = join(" ",$OSIPSUSER,$OSIPSDOMAIN); # &bdb_insert($DA_TABLE,$key,$value); #} els if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," INSERT INTO $DR_RULES_TABLE ($dr_rules_table{'DR_RULES_GWLIST_COLUMN'},$dr_rules_table{'DR_RULES_GROUPID_COLUMN'}, $dr_rules_table{'DR_RULES_PREFIX_COLUMN'},$dr_rules_table{'DR_RULES_TIMEREC_COLUMN'}, $dr_rules_table{'DR_RULES_PRIORITY_COLUMN'},$dr_rules_table{'DR_RULES_ROUTEID_COLUMN'}, $dr_rules_table{'DR_RULES_DESCRIPTION_COLUMN'}) VALUES (\'$gwlist\',\'$groupid\',\'$prefix\',\'$timerec\',$priority,$routeid,\'$description\' )"); } else { $sth = $dbh->prepare( " INSERT INTO $DR_RULES_TABLE ($dr_rules_table{'DR_RULES_GWLIST_COLUMN'},$dr_rules_table{'DR_RULES_GROUPID_COLUMN'}, $dr_rules_table{'DR_RULES_PREFIX_COLUMN'},$dr_rules_table{'DR_RULES_TIMEREC_COLUMN'}, $dr_rules_table{'DR_RULES_PRIORITY_COLUMN'},$dr_rules_table{'DR_RULES_ROUTEID_COLUMN'}, $dr_rules_table{'DR_RULES_DESCRIPTION_COLUMN'}) VALUES (\'$gwlist\',\'$groupid\',\'$prefix\',\'$timerec\',$priority,$routeid,\'$description\' )" ); #execute the query $sth->execute(); warn "Entry could not be inserted into table", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[2] =~ /^rm$/) { @var = split("=",$cmd[3]); $ruleid = $var[1]; if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { my $key = $ruleid; &bdb_delete($DR_RULES_TABLE,$key); } elsif ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," DELETE FROM $DR_RULES_TABLE WHERE $dr_rules_table{'DR_RULES_RULEID_COLUMN'}=\'$ruleid\' "); } else { $sth = $dbh->prepare(" DELETE FROM $DR_RULES_TABLE WHERE $dr_rules_table{'DR_RULES_RULEID_COLUMN'}=\'$ruleid\' "); #execute the query $sth->execute(); warn "Entry could not be deleted from table", $sth->errstr( ), "\n" if $sth->err( ); $sth->finish(); } } elsif ($cmd[2] =~ /^list$/) { if ( $#cmd == 2 ) { if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," SELECT * FROM $DR_RULES_TABLE "); } else { $sth = $dbh->prepare( " SELECT * FROM $DR_RULES_TABLE " ); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "ruleid=$row[0] groupid=$row[1] prefix=$row[2] timerec=$row[3] priority=$row[4] routeid=$row[5] gwlist=$row[6] description=$row[7]\n"; } $sth->finish(); } return; } elsif ( $#cmd == 3 ) { @var = split("=",$cmd[3]); if ($var[0] =~ /^gwlist$/) { $gwlist = $var[1]; #prepare query #if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { # my $key = ; # &bdb_select_where($DR_GW_TABLE,$key); #} els if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," SELECT * FROM $DR_RULES_TABLE WHERE $dr_rules_table{'DR_RULES_GWLIST_COLUMN'}=\'$var[1]\' "); } else { $sth = $dbh->prepare( " SELECT * FROM $DR_RULES_TABLE WHERE $dr_rules_table{'DR_RULES_GWLIST_COLUMN'}=\'$var[1]\'"); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "ruleid=$row[0] groupid=$row[1] prefix=$row[2] ". "timerec=$row[3] priority=$row[4] routeid=$row[5] ". "gwlist=$row[6] description=$row[7]\n"; } $sth->finish(); } } elsif ($var[0] =~ /^groupid$/) { $groupid = $var[1]; #prepare query #if ( $DBENGINE =~ /^DB_BERKELEY$/ ) { # my $key = join(" ",$TMP_OSIPSUSER,$TMP_OSIPSDOMAIN); # &bdb_select_where($DA_TABLE,$key,$value); #} els if ( $DBENGINE =~ /^DBTEXT$/ ) { system("$DBTEXTCMD"," SELECT * FROM $DR_RULES_TABLE WHERE $dr_rules_table{'DR_RULES_GROUPID_COLUMN'}=\'$var[1]\' "); } else { $sth = $dbh->prepare( " SELECT * FROM $DR_RULES_TABLE WHERE $dr_rules_table{'DR_RULES_GROUPID_COLUMN'}=\'$var[1]\'"); #execute the query $sth->execute( ); warn "Retrieving data from table failed", $sth->errstr( ), "\n" if $sth->err( ); #Retrieve the results of a row of data and print print "\tQuery results:\n================================================\n"; while ( @row = $sth->fetchrow_array( ) ) { print "ruleid=$row[0] groupid=$row[1] prefix=$row[2] ". "timerec=$row[3] priority=$row[4] routeid=$row[5] ". "gwlist=$row[6] description=$row[7]\n"; } $sth->finish(); } } } } elsif ($cmd[2] =~ /^h$/) { &usage_dr(); } else {print "Not an dr_rules option!!\n"}; } } opensips-2.2.2/scripts/osipsconsolerc000066400000000000000000000057571300170765700200600ustar00rootroot00000000000000# $Id: osicpsconsolerc # # The OpenSIPS configuration file for the control tools. # # Here you can set variables used in the opensipsctl and opensipsdbctl setup # scripts. Per default all variables here are commented out, the control tools # will use their internal default values. ## your SIP domain # SIP_DOMAIN=opensips.org ## database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT, or SQLITE ## by default none is loaded # If you want to setup a database with opensipsdbctl, you must at least specify # this parameter. # DBENGINE=MYSQL ## database host # DBHOST=localhost ## database port (PostgreSQL=5433 mandatory; MYSQL=3306 optional) # DBPORT=3306 ## database name (for ORACLE this is TNS name) # DBNAME=opensips ## database path used by dbtext, db_berkeley, or sqlite # DB_PATH="/usr/local/etc/opensips/dbtext" ## database read/write user # DBRWUSER=opensips ## password for database read/write user # DBRWPW=opensipsrw ## database super user (for ORACLE this is 'scheme-creator' user) # DBROOTUSER=root # Program to calculate a message-digest fingerprint # MD5=md5sum # awk tool # AWK=awk # grep tool # GREP=egrep # sed tool # SED=sed # Describe what additional tables to install. Valid values for the variables # below are yes/no/ask. With ask (default) it will interactively ask the user # for an answer, while yes/no allow for automated, unassisted installs. # # Define what module tables should be installed. # If you use the postgres database and want to change the installed tables, then you # must also adjust the STANDARD_TABLES or EXTRA_TABLES variable accordingly in the # opensipsdbctl.base script. # opensips standard modules # STANDARD_MODULES="standard acc domain group permissions registrar usrloc msilo # alias_db uri_db speeddial avpops auth_db dialog dispatcher # dialplan drouting nathelper load_balancer" # opensips extra modules # EXTRA_MODULES="imc cpl siptrace domainpolicy carrierroute userblacklist # b2b cachedb_sql registrant call_center fraud_detection" ## type of aliases used: DB - database aliases; UL - usrloc aliases ## - default: none # ALIASES_TYPE=DB ## MI_CONNECTOR control engine: FIFO, UNIXSOCK, UDP, XMLRPC # MI_CONNECTOR=FIFO:/tmp/opensips_fifo # MI_CONNECTOR=UNIXSOCK:/tmp/opensips.sock # MI_CONNECTOR=UDP:192.168.2.133:8000 # MI_CONNECTOR=XMLRPC:192.168.2.133:8000 ## check ACL names; default on (1); off (0) # VERIFY_ACL=1 ## ACL names - if VERIFY_ACL is set, only the ACL names from below list ## are accepted # ACL_GROUPS="local ld int voicemail free-pstn" ## do (1) or don't (0) store plaintext passwords ## in the subscriber table - default '1' # STORE_PLAINTEXT_PW=1 ## OPENSIPS START Options ## PID file path - default is: /var/run/opensips.pid # PID_FILE=/var/run/opensips.pid ## OUTPUT control - default output is to SYSLOG ## 0=output to console, 1=output to syslog # SYSLOG=0 ## Extra start options - default is: not set # example: start opensips with 64MB share memory: STARTOPTIONS="-m 64" # STARTOPTIONS= opensips-2.2.2/scripts/pi_http/000077500000000000000000000000001300170765700165215ustar00rootroot00000000000000opensips-2.2.2/scripts/pi_http/acc-mod000066400000000000000000000077231300170765700177600ustar00rootroot00000000000000 acc show acc DB_QUERY idupdatemethodfrom_tagto_tagcallidsip_codesip_reasontimedurationms_durationsetuptimecreatedadd acc DB_INSERT methodfrom_tagto_tagcallidsip_codesip_reasontimedurationms_durationsetuptimecreatedupdate acc DB_UPDATE id= methodfrom_tagto_tagcallidsip_codesip_reasontimedurationms_durationsetuptimecreateddelete acc DB_DELETE id=missed_calls show missed_calls DB_QUERY idupdatemethodfrom_tagto_tagcallidsip_codesip_reasontimesetuptimecreatedadd missed_calls DB_INSERT methodfrom_tagto_tagcallidsip_codesip_reasontimesetuptimecreatedupdate missed_calls DB_UPDATE id= methodfrom_tagto_tagcallidsip_codesip_reasontimesetuptimecreateddelete missed_calls DB_DELETE id= opensips-2.2.2/scripts/pi_http/acc-table000066400000000000000000000031561300170765700202640ustar00rootroot00000000000000 acc mysql idDB_INT methodDB_STR from_tagDB_STR to_tagDB_STR callidDB_STR sip_codeDB_STR sip_reasonDB_STR timeDB_DATETIME durationDB_INT ms_durationDB_INT setuptimeDB_INT createdDB_DATETIME missed_calls mysql idDB_INT methodDB_STR from_tagDB_STR to_tagDB_STR callidDB_STR sip_codeDB_STR sip_reasonDB_STR timeDB_DATETIME setuptimeDB_INT createdDB_DATETIME opensips-2.2.2/scripts/pi_http/alias_db-mod000066400000000000000000000025401300170765700207600ustar00rootroot00000000000000 dbaliases show dbaliases DB_QUERY idupdatealias_usernamealias_domainusernamedomainadd dbaliases DB_INSERT alias_usernamealias_domainusernamedomainupdate dbaliases DB_UPDATE id= alias_usernamealias_domainusernamedomaindelete dbaliases DB_DELETE id= opensips-2.2.2/scripts/pi_http/alias_db-table000066400000000000000000000007141300170765700212710ustar00rootroot00000000000000 dbaliases mysql idDB_INT alias_usernameDB_STR alias_domainDB_STR usernameDB_STR domainDB_STR opensips-2.2.2/scripts/pi_http/auth_db-mod000066400000000000000000000032171300170765700206320ustar00rootroot00000000000000 subscriber show subscriber DB_QUERY idupdateusernamedomainpasswordemail_addressha1ha1brpidadd subscriber DB_INSERT usernamedomainpasswordemail_addressha1ha1brpidupdate subscriber DB_UPDATE id= usernamedomainpasswordemail_addressha1ha1brpiddelete subscriber DB_DELETE id= opensips-2.2.2/scripts/pi_http/auth_db-table000066400000000000000000000011671300170765700211440ustar00rootroot00000000000000 subscriber mysql idDB_INT usernameDB_STR domainDB_STR passwordDB_STR email_addressDB_STR ha1DB_STR ha1bDB_STR rpidDB_STR opensips-2.2.2/scripts/pi_http/avpops-mod000066400000000000000000000032661300170765700205400ustar00rootroot00000000000000 usr_preferences show usr_preferences DB_QUERY idupdateuuidusernamedomainattributetypevaluelast_modifiedadd usr_preferences DB_INSERT uuidusernamedomainattributetypevaluelast_modifiedupdate usr_preferences DB_UPDATE id= uuidusernamedomainattributetypevaluelast_modifieddelete usr_preferences DB_DELETE id= opensips-2.2.2/scripts/pi_http/avpops-table000066400000000000000000000012161300170765700210410ustar00rootroot00000000000000 usr_preferences mysql idDB_INT uuidDB_STR usernameDB_STR domainDB_STR attributeDB_STR typeDB_INT valueDB_STR last_modifiedDB_DATETIME opensips-2.2.2/scripts/pi_http/b2b-mod000066400000000000000000000167271300170765700177030ustar00rootroot00000000000000 b2b_entities show b2b_entities DB_QUERY idupdatetypestaterurifrom_urito_urifrom_dnameto_dnametag0tag1callidcseq0cseq1contact0contact1route0route1sockinfo_srvparamlmlrclicleg_cseqleg_routeleg_tagleg_contactleg_sockinfoadd b2b_entities DB_INSERT typestaterurifrom_urito_urifrom_dnameto_dnametag0tag1callidcseq0cseq1contact0contact1route0route1sockinfo_srvparamlmlrclicleg_cseqleg_routeleg_tagleg_contactleg_sockinfoupdate b2b_entities DB_UPDATE id= typestaterurifrom_urito_urifrom_dnameto_dnametag0tag1callidcseq0cseq1contact0contact1route0route1sockinfo_srvparamlmlrclicleg_cseqleg_routeleg_tagleg_contactleg_sockinfodelete b2b_entities DB_DELETE id=b2b_logic show b2b_logic DB_QUERY idupdatesi_keyscenariosstatenext_sstatesparam0sparam1sparam2sparam3sparam4sdplifetimee1_typee1_side1_frome1_toe1_keye2_typee2_side2_frome2_toe2_keye3_typee3_side3_frome3_toe3_keyadd b2b_logic DB_INSERT si_keyscenariosstatenext_sstatesparam0sparam1sparam2sparam3sparam4sdplifetimee1_typee1_side1_frome1_toe1_keye2_typee2_side2_frome2_toe2_keye3_typee3_side3_frome3_toe3_keyupdate b2b_logic DB_UPDATE id= si_keyscenariosstatenext_sstatesparam0sparam1sparam2sparam3sparam4sdplifetimee1_typee1_side1_frome1_toe1_keye2_typee2_side2_frome2_toe2_keye3_typee3_side3_frome3_toe3_keydelete b2b_logic DB_DELETE id= opensips-2.2.2/scripts/pi_http/b2b-table000066400000000000000000000067601300170765700202070ustar00rootroot00000000000000 b2b_entities mysql idDB_INT typeDB_INT stateDB_INT ruriDB_STR from_uriDB_STR to_uriDB_STR from_dnameDB_STR to_dnameDB_STR tag0DB_STR tag1DB_STR callidDB_STR cseq0DB_INT cseq1DB_INT contact0DB_STR contact1DB_STR route0DB_BLOB route1DB_BLOB sockinfo_srvDB_STR paramDB_STR lmDB_INT lrcDB_INT licDB_INT leg_cseqDB_INT leg_routeDB_BLOB leg_tagDB_STR leg_contactDB_STR leg_sockinfoDB_STR b2b_logic mysql idDB_INT si_keyDB_STR scenarioDB_STR sstateDB_INT next_sstateDB_INT sparam0DB_STR sparam1DB_STR sparam2DB_STR sparam3DB_STR sparam4DB_STR sdpDB_BLOB lifetimeDB_INT e1_typeDB_INT e1_sidDB_STR e1_fromDB_STR e1_toDB_STR e1_keyDB_STR e2_typeDB_INT e2_sidDB_STR e2_fromDB_STR e2_toDB_STR e2_keyDB_STR e3_typeDB_INT e3_sidDB_STR e3_fromDB_STR e3_toDB_STR e3_keyDB_STR opensips-2.2.2/scripts/pi_http/b2b_sca-mod000066400000000000000000000205721300170765700205220ustar00rootroot00000000000000 b2b_sca show b2b_sca DB_QUERY idupdateshared_linewatchersapp1_shared_entityapp1_call_stateapp1_call_info_uriapp1_call_info_appearance_uriapp1_b2bl_keyapp2_shared_entityapp2_call_stateapp2_call_info_uriapp2_call_info_appearance_uriapp2_b2bl_keyapp3_shared_entityapp3_call_stateapp3_call_info_uriapp3_call_info_appearance_uriapp3_b2bl_keyapp4_shared_entityapp4_call_stateapp4_call_info_uriapp4_call_info_appearance_uriapp4_b2bl_keyapp5_shared_entityapp5_call_stateapp5_call_info_uriapp5_call_info_appearance_uriapp5_b2bl_keyapp6_shared_entityapp6_call_stateapp6_call_info_uriapp6_call_info_appearance_uriapp6_b2bl_keyapp7_shared_entityapp7_call_stateapp7_call_info_uriapp7_call_info_appearance_uriapp7_b2bl_keyapp8_shared_entityapp8_call_stateapp8_call_info_uriapp8_call_info_appearance_uriapp8_b2bl_keyapp9_shared_entityapp9_call_stateapp9_call_info_uriapp9_call_info_appearance_uriapp9_b2bl_keyapp10_shared_entityapp10_call_stateapp10_call_info_uriapp10_call_info_appearance_uriapp10_b2bl_keyadd b2b_sca DB_INSERT shared_linewatchersapp1_shared_entityapp1_call_stateapp1_call_info_uriapp1_call_info_appearance_uriapp1_b2bl_keyapp2_shared_entityapp2_call_stateapp2_call_info_uriapp2_call_info_appearance_uriapp2_b2bl_keyapp3_shared_entityapp3_call_stateapp3_call_info_uriapp3_call_info_appearance_uriapp3_b2bl_keyapp4_shared_entityapp4_call_stateapp4_call_info_uriapp4_call_info_appearance_uriapp4_b2bl_keyapp5_shared_entityapp5_call_stateapp5_call_info_uriapp5_call_info_appearance_uriapp5_b2bl_keyapp6_shared_entityapp6_call_stateapp6_call_info_uriapp6_call_info_appearance_uriapp6_b2bl_keyapp7_shared_entityapp7_call_stateapp7_call_info_uriapp7_call_info_appearance_uriapp7_b2bl_keyapp8_shared_entityapp8_call_stateapp8_call_info_uriapp8_call_info_appearance_uriapp8_b2bl_keyapp9_shared_entityapp9_call_stateapp9_call_info_uriapp9_call_info_appearance_uriapp9_b2bl_keyapp10_shared_entityapp10_call_stateapp10_call_info_uriapp10_call_info_appearance_uriapp10_b2bl_keyupdate b2b_sca DB_UPDATE id= shared_linewatchersapp1_shared_entityapp1_call_stateapp1_call_info_uriapp1_call_info_appearance_uriapp1_b2bl_keyapp2_shared_entityapp2_call_stateapp2_call_info_uriapp2_call_info_appearance_uriapp2_b2bl_keyapp3_shared_entityapp3_call_stateapp3_call_info_uriapp3_call_info_appearance_uriapp3_b2bl_keyapp4_shared_entityapp4_call_stateapp4_call_info_uriapp4_call_info_appearance_uriapp4_b2bl_keyapp5_shared_entityapp5_call_stateapp5_call_info_uriapp5_call_info_appearance_uriapp5_b2bl_keyapp6_shared_entityapp6_call_stateapp6_call_info_uriapp6_call_info_appearance_uriapp6_b2bl_keyapp7_shared_entityapp7_call_stateapp7_call_info_uriapp7_call_info_appearance_uriapp7_b2bl_keyapp8_shared_entityapp8_call_stateapp8_call_info_uriapp8_call_info_appearance_uriapp8_b2bl_keyapp9_shared_entityapp9_call_stateapp9_call_info_uriapp9_call_info_appearance_uriapp9_b2bl_keyapp10_shared_entityapp10_call_stateapp10_call_info_uriapp10_call_info_appearance_uriapp10_b2bl_keydelete b2b_sca DB_DELETE id= opensips-2.2.2/scripts/pi_http/b2b_sca-table000066400000000000000000000075701300170765700210350ustar00rootroot00000000000000 b2b_sca mysql idDB_INT shared_lineDB_STR watchersDB_STR app1_shared_entityDB_INT app1_call_stateDB_INT app1_call_info_uriDB_STR app1_call_info_appearance_uriDB_STR app1_b2bl_keyDB_STR app2_shared_entityDB_INT app2_call_stateDB_INT app2_call_info_uriDB_STR app2_call_info_appearance_uriDB_STR app2_b2bl_keyDB_STR app3_shared_entityDB_INT app3_call_stateDB_INT app3_call_info_uriDB_STR app3_call_info_appearance_uriDB_STR app3_b2bl_keyDB_STR app4_shared_entityDB_INT app4_call_stateDB_INT app4_call_info_uriDB_STR app4_call_info_appearance_uriDB_STR app4_b2bl_keyDB_STR app5_shared_entityDB_INT app5_call_stateDB_INT app5_call_info_uriDB_STR app5_call_info_appearance_uriDB_STR app5_b2bl_keyDB_STR app6_shared_entityDB_INT app6_call_stateDB_INT app6_call_info_uriDB_STR app6_call_info_appearance_uriDB_STR app6_b2bl_keyDB_STR app7_shared_entityDB_INT app7_call_stateDB_INT app7_call_info_uriDB_STR app7_call_info_appearance_uriDB_STR app7_b2bl_keyDB_STR app8_shared_entityDB_INT app8_call_stateDB_INT app8_call_info_uriDB_STR app8_call_info_appearance_uriDB_STR app8_b2bl_keyDB_STR app9_shared_entityDB_INT app9_call_stateDB_INT app9_call_info_uriDB_STR app9_call_info_appearance_uriDB_STR app9_b2bl_keyDB_STR app10_shared_entityDB_INT app10_call_stateDB_INT app10_call_info_uriDB_STR app10_call_info_appearance_uriDB_STR app10_b2bl_keyDB_STR opensips-2.2.2/scripts/pi_http/cachedb_sql-mod000066400000000000000000000023551300170765700214560ustar00rootroot00000000000000 cachedb show cachedb DB_QUERY keynameupdatevaluecounterexpiresadd cachedb DB_INSERT keynamevaluecounterexpiresupdate cachedb DB_UPDATE keyname= valuecounterexpiresdelete cachedb DB_DELETE keyname= opensips-2.2.2/scripts/pi_http/cachedb_sql-table000066400000000000000000000006011300170765700217560ustar00rootroot00000000000000 cachedb mysql keynameDB_STR valueDB_BLOB counterDB_INT expiresDB_INT opensips-2.2.2/scripts/pi_http/call_center-mod000066400000000000000000000165241300170765700215040ustar00rootroot00000000000000 cc_flows show cc_flows DB_QUERY idupdateflowidpriorityskillprependcidmessage_welcomemessage_queueadd cc_flows DB_INSERT flowidpriorityskillprependcidmessage_welcomemessage_queueupdate cc_flows DB_UPDATE id= flowidpriorityskillprependcidmessage_welcomemessage_queuedelete cc_flows DB_DELETE id=cc_agents show cc_agents DB_QUERY idupdateagentidlocationlogstateskillslast_call_endadd cc_agents DB_INSERT agentidlocationlogstateskillslast_call_endupdate cc_agents DB_UPDATE id= agentidlocationlogstateskillslast_call_enddelete cc_agents DB_DELETE id=cc_cdrs show cc_cdrs DB_QUERY idupdatecallerreceived_timestampwait_timepickup_timetalk_timeflow_idagent_idcall_typerejectedfstatscidadd cc_cdrs DB_INSERT callerreceived_timestampwait_timepickup_timetalk_timeflow_idagent_idcall_typerejectedfstatscidupdate cc_cdrs DB_UPDATE id= callerreceived_timestampwait_timepickup_timetalk_timeflow_idagent_idcall_typerejectedfstatsciddelete cc_cdrs DB_DELETE id=cc_calls show cc_calls DB_QUERY idupdatestateig_cbackno_rejsetup_timeetalast_startrecv_timecaller_dncaller_unb2buaidflowagentadd cc_calls DB_INSERT stateig_cbackno_rejsetup_timeetalast_startrecv_timecaller_dncaller_unb2buaidflowagentupdate cc_calls DB_UPDATE id= stateig_cbackno_rejsetup_timeetalast_startrecv_timecaller_dncaller_unb2buaidflowagentdelete cc_calls DB_DELETE id= opensips-2.2.2/scripts/pi_http/call_center-table000066400000000000000000000055531300170765700220140ustar00rootroot00000000000000 cc_flows mysql idDB_INT flowidDB_STR priorityDB_INT skillDB_STR prependcidDB_STR message_welcomeDB_STR message_queueDB_STR cc_agents mysql idDB_INT agentidDB_STR locationDB_STR logstateDB_INT skillsDB_STR last_call_endDB_INT cc_cdrs mysql idDB_INT callerDB_STR received_timestampDB_DATETIME wait_timeDB_INT pickup_timeDB_INT talk_timeDB_INT flow_idDB_STR agent_idDB_STR call_typeDB_INT rejectedDB_INT fstatsDB_INT cidDB_INT cc_calls mysql idDB_INT stateDB_INT ig_cbackDB_INT no_rejDB_INT setup_timeDB_INT etaDB_INT last_startDB_INT recv_timeDB_INT caller_dnDB_STR caller_unDB_STR b2buaidDB_STR flowDB_STR agentDB_STR opensips-2.2.2/scripts/pi_http/carrierroute-mod000066400000000000000000000121331300170765700217270ustar00rootroot00000000000000 carrierroute show carrierroute DB_QUERY idupdatecarrierdomainscan_prefixflagsmaskprobstriprewrite_hostrewrite_prefixrewrite_suffixdescriptionadd carrierroute DB_INSERT carrierdomainscan_prefixflagsmaskprobstriprewrite_hostrewrite_prefixrewrite_suffixdescriptionupdate carrierroute DB_UPDATE id= carrierdomainscan_prefixflagsmaskprobstriprewrite_hostrewrite_prefixrewrite_suffixdescriptiondelete carrierroute DB_DELETE id=carrierfailureroute show carrierfailureroute DB_QUERY idupdatecarrierdomainscan_prefixhost_namereply_codeflagsmasknext_domaindescriptionadd carrierfailureroute DB_INSERT carrierdomainscan_prefixhost_namereply_codeflagsmasknext_domaindescriptionupdate carrierfailureroute DB_UPDATE id= carrierdomainscan_prefixhost_namereply_codeflagsmasknext_domaindescriptiondelete carrierfailureroute DB_DELETE id=route_tree show route_tree DB_QUERY idupdatecarrieradd route_tree DB_INSERT carrierupdate route_tree DB_UPDATE id= carrierdelete route_tree DB_DELETE id= opensips-2.2.2/scripts/pi_http/carrierroute-table000066400000000000000000000036551300170765700222500ustar00rootroot00000000000000 carrierroute mysql idDB_INT carrierDB_INT domainDB_STR scan_prefixDB_STR flagsDB_INT maskDB_INT probDB_DOUBLE stripDB_INT rewrite_hostDB_STR rewrite_prefixDB_STR rewrite_suffixDB_STR descriptionDB_STR carrierfailureroute mysql idDB_INT carrierDB_INT domainDB_STR scan_prefixDB_STR host_nameDB_STR reply_codeDB_STR flagsDB_INT maskDB_INT next_domainDB_STR descriptionDB_STR route_tree mysql idDB_INT carrierDB_STR opensips-2.2.2/scripts/pi_http/closeddial-mod000066400000000000000000000030731300170765700213270ustar00rootroot00000000000000 closeddial show closeddial DB_QUERY idupdateusernamedomaincd_usernamecd_domaingroup_idnew_uriadd closeddial DB_INSERT usernamedomaincd_usernamecd_domaingroup_idnew_uriupdate closeddial DB_UPDATE id= usernamedomaincd_usernamecd_domaingroup_idnew_uridelete closeddial DB_DELETE id= opensips-2.2.2/scripts/pi_http/closeddial-table000066400000000000000000000011041300170765700216300ustar00rootroot00000000000000 closeddial mysql idDB_INT usernameDB_STR domainDB_STR cd_usernameDB_STR cd_domainDB_STR group_idDB_STR new_uriDB_STR opensips-2.2.2/scripts/pi_http/clusterer-mod000066400000000000000000000036571300170765700212440ustar00rootroot00000000000000 clusterer show clusterer DB_QUERY idupdatecluster_idmachine_idurlstatelast_attemptfailed_attemptsno_triesdurationdescriptionadd clusterer DB_INSERT cluster_idmachine_idurlstatelast_attemptfailed_attemptsno_triesdurationdescriptionupdate clusterer DB_UPDATE id= cluster_idmachine_idurlstatelast_attemptfailed_attemptsno_triesdurationdescriptiondelete clusterer DB_DELETE id= opensips-2.2.2/scripts/pi_http/clusterer-table000066400000000000000000000014071300170765700215430ustar00rootroot00000000000000 clusterer mysql idDB_INT cluster_idDB_INT machine_idDB_INT urlDB_STR stateDB_INT last_attemptDB_BIGINT failed_attemptsDB_INT no_triesDB_INT durationDB_INT descriptionDB_STR opensips-2.2.2/scripts/pi_http/cpl-mod000066400000000000000000000024301300170765700177760ustar00rootroot00000000000000 cpl show cpl DB_QUERY idupdateusernamedomaincpl_xmlcpl_binadd cpl DB_INSERT usernamedomaincpl_xmlcpl_binupdate cpl DB_UPDATE id= usernamedomaincpl_xmlcpl_bindelete cpl DB_DELETE id= opensips-2.2.2/scripts/pi_http/cpl-table000066400000000000000000000006601300170765700203110ustar00rootroot00000000000000 cpl mysql idDB_INT usernameDB_STR domainDB_STR cpl_xmlDB_BLOB cpl_binDB_BLOB opensips-2.2.2/scripts/pi_http/dialog-mod000066400000000000000000000076751300170765700204770ustar00rootroot00000000000000 dialog show dialog DB_QUERY dlg_idupdatecallidfrom_urifrom_tagto_urito_tagmangled_from_urimangled_to_uricaller_cseqcallee_cseqcaller_ping_cseqcallee_ping_cseqcaller_route_setcallee_route_setcaller_contactcallee_contactcaller_sockcallee_sockstatestart_timetimeoutvarsprofilesscript_flagsmodule_flagsflagsadd dialog DB_INSERT dlg_idcallidfrom_urifrom_tagto_urito_tagmangled_from_urimangled_to_uricaller_cseqcallee_cseqcaller_ping_cseqcallee_ping_cseqcaller_route_setcallee_route_setcaller_contactcallee_contactcaller_sockcallee_sockstatestart_timetimeoutvarsprofilesscript_flagsmodule_flagsflagsupdate dialog DB_UPDATE dlg_id= callidfrom_urifrom_tagto_urito_tagmangled_from_urimangled_to_uricaller_cseqcallee_cseqcaller_ping_cseqcallee_ping_cseqcaller_route_setcallee_route_setcaller_contactcallee_contactcaller_sockcallee_sockstatestart_timetimeoutvarsprofilesscript_flagsmodule_flagsflagsdelete dialog DB_DELETE dlg_id= opensips-2.2.2/scripts/pi_http/dialog-table000066400000000000000000000034331300170765700207730ustar00rootroot00000000000000 dialog mysql dlg_idDB_BIGINT callidDB_STR from_uriDB_STR from_tagDB_STR to_uriDB_STR to_tagDB_STR mangled_from_uriDB_STR mangled_to_uriDB_STR caller_cseqDB_STR callee_cseqDB_STR caller_ping_cseqDB_INT callee_ping_cseqDB_INT caller_route_setDB_BLOB callee_route_setDB_BLOB caller_contactDB_STR callee_contactDB_STR caller_sockDB_STR callee_sockDB_STR stateDB_INT start_timeDB_INT timeoutDB_INT varsDB_BLOB profilesDB_BLOB script_flagsDB_INT module_flagsDB_INT flagsDB_INT opensips-2.2.2/scripts/pi_http/dialplan-mod000066400000000000000000000037451300170765700210160ustar00rootroot00000000000000 dialplan show dialplan DB_QUERY idupdatedpidprmatch_opmatch_expmatch_flagssubst_exprepl_exptimerecdisabledattrsadd dialplan DB_INSERT dpidprmatch_opmatch_expmatch_flagssubst_exprepl_exptimerecdisabledattrsupdate dialplan DB_UPDATE id= dpidprmatch_opmatch_expmatch_flagssubst_exprepl_exptimerecdisabledattrsdelete dialplan DB_DELETE id= opensips-2.2.2/scripts/pi_http/dialplan-table000066400000000000000000000014541300170765700213210ustar00rootroot00000000000000 dialplan mysql idDB_INT dpidDB_INT prDB_INT match_opDB_INT match_expDB_STR match_flagsDB_INT subst_expDB_STR repl_expDB_STR timerecDB_STR disabledDB_INT attrsDB_STR opensips-2.2.2/scripts/pi_http/dispatcher-mod000066400000000000000000000034151300170765700213520ustar00rootroot00000000000000 dispatcher show dispatcher DB_QUERY idupdatesetiddestinationsocketstateweightpriorityattrsdescriptionadd dispatcher DB_INSERT setiddestinationsocketstateweightpriorityattrsdescriptionupdate dispatcher DB_UPDATE id= setiddestinationsocketstateweightpriorityattrsdescriptiondelete dispatcher DB_DELETE id= opensips-2.2.2/scripts/pi_http/dispatcher-table000066400000000000000000000012701300170765700216570ustar00rootroot00000000000000 dispatcher mysql idDB_INT setidDB_INT destinationDB_STR socketDB_STR stateDB_INT weightDB_INT priorityDB_INT attrsDB_STR descriptionDB_STR opensips-2.2.2/scripts/pi_http/domain-mod000066400000000000000000000023011300170765700204640ustar00rootroot00000000000000 domain show domain DB_QUERY idupdatedomainattrslast_modifiedadd domain DB_INSERT domainattrslast_modifiedupdate domain DB_UPDATE id= domainattrslast_modifieddelete domain DB_DELETE id= opensips-2.2.2/scripts/pi_http/domain-table000066400000000000000000000006021300170765700207760ustar00rootroot00000000000000 domain mysql idDB_INT domainDB_STR attrsDB_STR last_modifiedDB_DATETIME opensips-2.2.2/scripts/pi_http/domainpolicy-mod000066400000000000000000000026421300170765700217140ustar00rootroot00000000000000 domainpolicy show domainpolicy DB_QUERY idupdateruletypeattvaldescriptionadd domainpolicy DB_INSERT ruletypeattvaldescriptionupdate domainpolicy DB_UPDATE id= ruletypeattvaldescriptiondelete domainpolicy DB_DELETE id= opensips-2.2.2/scripts/pi_http/domainpolicy-table000066400000000000000000000007741300170765700222300ustar00rootroot00000000000000 domainpolicy mysql idDB_INT ruleDB_STR typeDB_STR attDB_STR valDB_STR descriptionDB_STR opensips-2.2.2/scripts/pi_http/drouting-mod000066400000000000000000000221221300170765700210530ustar00rootroot00000000000000 dr_gateways show dr_gateways DB_QUERY idupdategwidtypeaddressstrippri_prefixattrsprobe_modestatesocketdescriptionadd dr_gateways DB_INSERT gwidtypeaddressstrippri_prefixattrsprobe_modestatesocketdescriptionupdate dr_gateways DB_UPDATE id= gwidtypeaddressstrippri_prefixattrsprobe_modestatesocketdescriptiondelete dr_gateways DB_DELETE id=dr_rules show dr_rules DB_QUERY ruleidupdategroupidprefixtimerecpriorityrouteidgwlistattrsdescriptionadd dr_rules DB_INSERT groupidprefixtimerecpriorityrouteidgwlistattrsdescriptionupdate dr_rules DB_UPDATE ruleid= groupidprefixtimerecpriorityrouteidgwlistattrsdescriptiondelete dr_rules DB_DELETE ruleid=dr_carriers show dr_carriers DB_QUERY idupdatecarrieridgwlistflagsstateattrsdescriptionadd dr_carriers DB_INSERT carrieridgwlistflagsstateattrsdescriptionupdate dr_carriers DB_UPDATE id= carrieridgwlistflagsstateattrsdescriptiondelete dr_carriers DB_DELETE id=dr_groups show dr_groups DB_QUERY idupdateusernamedomaingroupiddescriptionadd dr_groups DB_INSERT usernamedomaingroupiddescriptionupdate dr_groups DB_UPDATE id= usernamedomaingroupiddescriptiondelete dr_groups DB_DELETE id=dr_partitions show dr_partitions DB_QUERY idupdatepartition_namedb_urldrd_tabledrr_tabledrg_tabledrc_tableruri_avpgw_id_avpgw_priprefix_avpgw_sock_avprule_id_avprule_prefix_avpcarrier_id_avpadd dr_partitions DB_INSERT partition_namedb_urldrd_tabledrr_tabledrg_tabledrc_tableruri_avpgw_id_avpgw_priprefix_avpgw_sock_avprule_id_avprule_prefix_avpcarrier_id_avpupdate dr_partitions DB_UPDATE id= partition_namedb_urldrd_tabledrr_tabledrg_tabledrc_tableruri_avpgw_id_avpgw_priprefix_avpgw_sock_avprule_id_avprule_prefix_avpcarrier_id_avpdelete dr_partitions DB_DELETE id= opensips-2.2.2/scripts/pi_http/drouting-table000066400000000000000000000070141300170765700213660ustar00rootroot00000000000000 dr_gateways mysql idDB_INT gwidDB_STR typeDB_INT addressDB_STR stripDB_INT pri_prefixDB_STR attrsDB_STR probe_modeDB_INT stateDB_INT socketDB_STR descriptionDB_STR dr_rules mysql ruleidDB_INT groupidDB_STR prefixDB_STR timerecDB_STR priorityDB_INT routeidDB_STR gwlistDB_STR attrsDB_STR descriptionDB_STR dr_carriers mysql idDB_INT carrieridDB_STR gwlistDB_STR flagsDB_INT stateDB_INT attrsDB_STR descriptionDB_STR dr_groups mysql idDB_INT usernameDB_STR domainDB_STR groupidDB_INT descriptionDB_STR dr_partitions mysql idDB_INT partition_nameDB_STR db_urlDB_STR drd_tableDB_STR drr_tableDB_STR drg_tableDB_STR drc_tableDB_STR ruri_avpDB_STR gw_id_avpDB_STR gw_priprefix_avpDB_STR gw_sock_avpDB_STR rule_id_avpDB_STR rule_prefix_avpDB_STR carrier_id_avpDB_STR opensips-2.2.2/scripts/pi_http/emergency-mod000066400000000000000000000125761300170765700212120ustar00rootroot00000000000000 emergency_routing show emergency_routing DB_QUERY idupdateselectiveRoutingIDroutingESNnpaesgwriadd emergency_routing DB_INSERT selectiveRoutingIDroutingESNnpaesgwriupdate emergency_routing DB_UPDATE id= selectiveRoutingIDroutingESNnpaesgwridelete emergency_routing DB_DELETE id=emergency_report show emergency_report DB_QUERY idupdatecallidselectiveRoutingIDroutingESNnpaesgwrilroVPC_organizationNameVPC_hostnameVPC_timestampresultdispositionadd emergency_report DB_INSERT callidselectiveRoutingIDroutingESNnpaesgwrilroVPC_organizationNameVPC_hostnameVPC_timestampresultdispositionupdate emergency_report DB_UPDATE id= callidselectiveRoutingIDroutingESNnpaesgwrilroVPC_organizationNameVPC_hostnameVPC_timestampresultdispositiondelete emergency_report DB_DELETE id=emergency_service_provider show emergency_service_provider DB_QUERY idupdateorganizationNamehostIdnenaIdcontactcertUrinodeIPattributionadd emergency_service_provider DB_INSERT organizationNamehostIdnenaIdcontactcertUrinodeIPattributionupdate emergency_service_provider DB_UPDATE id= organizationNamehostIdnenaIdcontactcertUrinodeIPattributiondelete emergency_service_provider DB_DELETE id= opensips-2.2.2/scripts/pi_http/emergency-table000066400000000000000000000040641300170765700215130ustar00rootroot00000000000000 emergency_routing mysql idDB_INT selectiveRoutingIDDB_STR routingESNDB_INT npaDB_INT esgwriDB_STR emergency_report mysql idDB_INT callidDB_STR selectiveRoutingIDDB_STR routingESNDB_INT npaDB_INT esgwriDB_STR lroDB_STR VPC_organizationNameDB_STR VPC_hostnameDB_STR VPC_timestampDB_STR resultDB_STR dispositionDB_STR emergency_service_provider mysql idDB_INT organizationNameDB_STR hostIdDB_STR nenaIdDB_STR contactDB_STR certUriDB_STR nodeIPDB_STR attributionDB_INT opensips-2.2.2/scripts/pi_http/fraud_detection-mod000066400000000000000000000060021300170765700223560ustar00rootroot00000000000000 fraud_detection show fraud_detection DB_QUERY ruleidupdateprofileidprefixstart_hourend_hourdaysoftheweekcpm_warningcpm_criticalcall_duration_warningcall_duration_criticaltotal_calls_warningtotal_calls_criticalconcurrent_calls_warningconcurrent_calls_criticalsequential_calls_warningsequential_calls_criticaladd fraud_detection DB_INSERT profileidprefixstart_hourend_hourdaysoftheweekcpm_warningcpm_criticalcall_duration_warningcall_duration_criticaltotal_calls_warningtotal_calls_criticalconcurrent_calls_warningconcurrent_calls_criticalsequential_calls_warningsequential_calls_criticalupdate fraud_detection DB_UPDATE ruleid= profileidprefixstart_hourend_hourdaysoftheweekcpm_warningcpm_criticalcall_duration_warningcall_duration_criticaltotal_calls_warningtotal_calls_criticalconcurrent_calls_warningconcurrent_calls_criticalsequential_calls_warningsequential_calls_criticaldelete fraud_detection DB_DELETE ruleid= opensips-2.2.2/scripts/pi_http/fraud_detection-table000066400000000000000000000024051300170765700226710ustar00rootroot00000000000000 fraud_detection mysql ruleidDB_INT profileidDB_INT prefixDB_STR start_hourDB_STR end_hourDB_STR daysoftheweekDB_STR cpm_warningDB_INT cpm_criticalDB_INT call_duration_warningDB_INT call_duration_criticalDB_INT total_calls_warningDB_INT total_calls_criticalDB_INT concurrent_calls_warningDB_INT concurrent_calls_criticalDB_INT sequential_calls_warningDB_INT sequential_calls_criticalDB_INT opensips-2.2.2/scripts/pi_http/group-mod000066400000000000000000000045471300170765700203670ustar00rootroot00000000000000 grp show grp DB_QUERY idupdateusernamedomaingrplast_modifiedadd grp DB_INSERT usernamedomaingrplast_modifiedupdate grp DB_UPDATE id= usernamedomaingrplast_modifieddelete grp DB_DELETE id=re_grp show re_grp DB_QUERY idupdatereg_expgroup_idadd re_grp DB_INSERT reg_expgroup_idupdate re_grp DB_UPDATE id= reg_expgroup_iddelete re_grp DB_DELETE id= opensips-2.2.2/scripts/pi_http/group-table000066400000000000000000000013631300170765700206700ustar00rootroot00000000000000 grp mysql idDB_INT usernameDB_STR domainDB_STR grpDB_STR last_modifiedDB_DATETIME re_grp mysql idDB_INT reg_expDB_STR group_idDB_INT opensips-2.2.2/scripts/pi_http/imc-mod000066400000000000000000000047531300170765700200020ustar00rootroot00000000000000 imc_rooms show imc_rooms DB_QUERY idupdatenamedomainflagadd imc_rooms DB_INSERT namedomainflagupdate imc_rooms DB_UPDATE id= namedomainflagdelete imc_rooms DB_DELETE id=imc_members show imc_members DB_QUERY idupdateusernamedomainroomflagadd imc_members DB_INSERT usernamedomainroomflagupdate imc_members DB_UPDATE id= usernamedomainroomflagdelete imc_members DB_DELETE id= opensips-2.2.2/scripts/pi_http/imc-table000066400000000000000000000014741300170765700203070ustar00rootroot00000000000000 imc_rooms mysql idDB_INT nameDB_STR domainDB_STR flagDB_INT imc_members mysql idDB_INT usernameDB_STR domainDB_STR roomDB_STR flagDB_INT opensips-2.2.2/scripts/pi_http/load_balancer-mod000066400000000000000000000027441300170765700217760ustar00rootroot00000000000000 load_balancer show load_balancer DB_QUERY idupdategroup_iddst_uriresourcesprobe_modedescriptionadd load_balancer DB_INSERT group_iddst_uriresourcesprobe_modedescriptionupdate load_balancer DB_UPDATE id= group_iddst_uriresourcesprobe_modedescriptiondelete load_balancer DB_DELETE id= opensips-2.2.2/scripts/pi_http/load_balancer-table000066400000000000000000000010231300170765700222730ustar00rootroot00000000000000 load_balancer mysql idDB_INT group_idDB_INT dst_uriDB_STR resourcesDB_STR probe_modeDB_INT descriptionDB_STR opensips-2.2.2/scripts/pi_http/msilo-mod000066400000000000000000000035301300170765700203450ustar00rootroot00000000000000 silo show silo DB_QUERY idupdatesrc_addrdst_addrusernamedomaininc_timeexp_timesnd_timectypebodyadd silo DB_INSERT src_addrdst_addrusernamedomaininc_timeexp_timesnd_timectypebodyupdate silo DB_UPDATE id= src_addrdst_addrusernamedomaininc_timeexp_timesnd_timectypebodydelete silo DB_DELETE id= opensips-2.2.2/scripts/pi_http/msilo-table000066400000000000000000000013431300170765700206550ustar00rootroot00000000000000 silo mysql idDB_INT src_addrDB_STR dst_addrDB_STR usernameDB_STR domainDB_STR inc_timeDB_INT exp_timeDB_INT snd_timeDB_INT ctypeDB_STR bodyDB_BLOB opensips-2.2.2/scripts/pi_http/permissions-mod000066400000000000000000000031421300170765700215740ustar00rootroot00000000000000 address show address DB_QUERY idupdategrpipmaskportprotopatterncontext_infoadd address DB_INSERT grpipmaskportprotopatterncontext_infoupdate address DB_UPDATE id= grpipmaskportprotopatterncontext_infodelete address DB_DELETE id= opensips-2.2.2/scripts/pi_http/permissions-table000066400000000000000000000011451300170765700221050ustar00rootroot00000000000000 address mysql idDB_INT grpDB_INT ipDB_STR maskDB_INT portDB_INT protoDB_STR patternDB_STR context_infoDB_STR opensips-2.2.2/scripts/pi_http/pi_framework-00000066400000000000000000000025601300170765700213510ustar00rootroot00000000000000 mysql://opensips:opensipsrw@localhost/opensips opensips-2.2.2/scripts/pi_http/pi_framework-01000066400000000000000000000026561300170765700213600ustar00rootroot00000000000000 opensips-2.2.2/scripts/pi_http/pi_framework-02000066400000000000000000000000151300170765700213440ustar00rootroot00000000000000 opensips-2.2.2/scripts/pi_http/pi_framework.xml000066400000000000000000005145311300170765700217410ustar00rootroot00000000000000 mysql://opensips:opensipsrw@localhost/opensips acc mysql idDB_INT methodDB_STR from_tagDB_STR to_tagDB_STR callidDB_STR sip_codeDB_STR sip_reasonDB_STR timeDB_DATETIME durationDB_INT ms_durationDB_INT setuptimeDB_INT createdDB_DATETIME missed_calls mysql idDB_INT methodDB_STR from_tagDB_STR to_tagDB_STR callidDB_STR sip_codeDB_STR sip_reasonDB_STR timeDB_DATETIME setuptimeDB_INT createdDB_DATETIME dbaliases mysql idDB_INT alias_usernameDB_STR alias_domainDB_STR usernameDB_STR domainDB_STR subscriber mysql idDB_INT usernameDB_STR domainDB_STR passwordDB_STR email_addressDB_STR ha1DB_STR ha1bDB_STR rpidDB_STR usr_preferences mysql idDB_INT uuidDB_STR usernameDB_STR domainDB_STR attributeDB_STR typeDB_INT valueDB_STR last_modifiedDB_DATETIME b2b_entities mysql idDB_INT typeDB_INT stateDB_INT ruriDB_STR from_uriDB_STR to_uriDB_STR from_dnameDB_STR to_dnameDB_STR tag0DB_STR tag1DB_STR callidDB_STR cseq0DB_INT cseq1DB_INT contact0DB_STR contact1DB_STR route0DB_BLOB route1DB_BLOB sockinfo_srvDB_STR paramDB_STR lmDB_INT lrcDB_INT licDB_INT leg_cseqDB_INT leg_routeDB_BLOB leg_tagDB_STR leg_contactDB_STR leg_sockinfoDB_STR b2b_logic mysql idDB_INT si_keyDB_STR scenarioDB_STR sstateDB_INT next_sstateDB_INT sparam0DB_STR sparam1DB_STR sparam2DB_STR sparam3DB_STR sparam4DB_STR sdpDB_BLOB lifetimeDB_INT e1_typeDB_INT e1_sidDB_STR e1_fromDB_STR e1_toDB_STR e1_keyDB_STR e2_typeDB_INT e2_sidDB_STR e2_fromDB_STR e2_toDB_STR e2_keyDB_STR e3_typeDB_INT e3_sidDB_STR e3_fromDB_STR e3_toDB_STR e3_keyDB_STR b2b_sca mysql idDB_INT shared_lineDB_STR watchersDB_STR app1_shared_entityDB_INT app1_call_stateDB_INT app1_call_info_uriDB_STR app1_call_info_appearance_uriDB_STR app1_b2bl_keyDB_STR app2_shared_entityDB_INT app2_call_stateDB_INT app2_call_info_uriDB_STR app2_call_info_appearance_uriDB_STR app2_b2bl_keyDB_STR app3_shared_entityDB_INT app3_call_stateDB_INT app3_call_info_uriDB_STR app3_call_info_appearance_uriDB_STR app3_b2bl_keyDB_STR app4_shared_entityDB_INT app4_call_stateDB_INT app4_call_info_uriDB_STR app4_call_info_appearance_uriDB_STR app4_b2bl_keyDB_STR app5_shared_entityDB_INT app5_call_stateDB_INT app5_call_info_uriDB_STR app5_call_info_appearance_uriDB_STR app5_b2bl_keyDB_STR app6_shared_entityDB_INT app6_call_stateDB_INT app6_call_info_uriDB_STR app6_call_info_appearance_uriDB_STR app6_b2bl_keyDB_STR app7_shared_entityDB_INT app7_call_stateDB_INT app7_call_info_uriDB_STR app7_call_info_appearance_uriDB_STR app7_b2bl_keyDB_STR app8_shared_entityDB_INT app8_call_stateDB_INT app8_call_info_uriDB_STR app8_call_info_appearance_uriDB_STR app8_b2bl_keyDB_STR app9_shared_entityDB_INT app9_call_stateDB_INT app9_call_info_uriDB_STR app9_call_info_appearance_uriDB_STR app9_b2bl_keyDB_STR app10_shared_entityDB_INT app10_call_stateDB_INT app10_call_info_uriDB_STR app10_call_info_appearance_uriDB_STR app10_b2bl_keyDB_STR cachedb mysql keynameDB_STR valueDB_BLOB counterDB_INT expiresDB_INT cc_flows mysql idDB_INT flowidDB_STR priorityDB_INT skillDB_STR prependcidDB_STR message_welcomeDB_STR message_queueDB_STR cc_agents mysql idDB_INT agentidDB_STR locationDB_STR logstateDB_INT skillsDB_STR last_call_endDB_INT cc_cdrs mysql idDB_INT callerDB_STR received_timestampDB_DATETIME wait_timeDB_INT pickup_timeDB_INT talk_timeDB_INT flow_idDB_STR agent_idDB_STR call_typeDB_INT rejectedDB_INT fstatsDB_INT cidDB_INT cc_calls mysql idDB_INT stateDB_INT ig_cbackDB_INT no_rejDB_INT setup_timeDB_INT etaDB_INT last_startDB_INT recv_timeDB_INT caller_dnDB_STR caller_unDB_STR b2buaidDB_STR flowDB_STR agentDB_STR carrierroute mysql idDB_INT carrierDB_INT domainDB_STR scan_prefixDB_STR flagsDB_INT maskDB_INT probDB_DOUBLE stripDB_INT rewrite_hostDB_STR rewrite_prefixDB_STR rewrite_suffixDB_STR descriptionDB_STR carrierfailureroute mysql idDB_INT carrierDB_INT domainDB_STR scan_prefixDB_STR host_nameDB_STR reply_codeDB_STR flagsDB_INT maskDB_INT next_domainDB_STR descriptionDB_STR route_tree mysql idDB_INT carrierDB_STR closeddial mysql idDB_INT usernameDB_STR domainDB_STR cd_usernameDB_STR cd_domainDB_STR group_idDB_STR new_uriDB_STR clusterer mysql idDB_INT cluster_idDB_INT machine_idDB_INT urlDB_STR stateDB_INT last_attemptDB_BIGINT failed_attemptsDB_INT no_triesDB_INT durationDB_INT descriptionDB_STR cpl mysql idDB_INT usernameDB_STR domainDB_STR cpl_xmlDB_BLOB cpl_binDB_BLOB dialog mysql dlg_idDB_BIGINT callidDB_STR from_uriDB_STR from_tagDB_STR to_uriDB_STR to_tagDB_STR mangled_from_uriDB_STR mangled_to_uriDB_STR caller_cseqDB_STR callee_cseqDB_STR caller_ping_cseqDB_INT callee_ping_cseqDB_INT caller_route_setDB_BLOB callee_route_setDB_BLOB caller_contactDB_STR callee_contactDB_STR caller_sockDB_STR callee_sockDB_STR stateDB_INT start_timeDB_INT timeoutDB_INT varsDB_BLOB profilesDB_BLOB script_flagsDB_INT module_flagsDB_INT flagsDB_INT dialplan mysql idDB_INT dpidDB_INT prDB_INT match_opDB_INT match_expDB_STR match_flagsDB_INT subst_expDB_STR repl_expDB_STR timerecDB_STR disabledDB_INT attrsDB_STR dispatcher mysql idDB_INT setidDB_INT destinationDB_STR socketDB_STR stateDB_INT weightDB_INT priorityDB_INT attrsDB_STR descriptionDB_STR domain mysql idDB_INT domainDB_STR attrsDB_STR last_modifiedDB_DATETIME domainpolicy mysql idDB_INT ruleDB_STR typeDB_STR attDB_STR valDB_STR descriptionDB_STR dr_gateways mysql idDB_INT gwidDB_STR typeDB_INT addressDB_STR stripDB_INT pri_prefixDB_STR attrsDB_STR probe_modeDB_INT stateDB_INT socketDB_STR descriptionDB_STR dr_rules mysql ruleidDB_INT groupidDB_STR prefixDB_STR timerecDB_STR priorityDB_INT routeidDB_STR gwlistDB_STR attrsDB_STR descriptionDB_STR dr_carriers mysql idDB_INT carrieridDB_STR gwlistDB_STR flagsDB_INT stateDB_INT attrsDB_STR descriptionDB_STR dr_groups mysql idDB_INT usernameDB_STR domainDB_STR groupidDB_INT descriptionDB_STR dr_partitions mysql idDB_INT partition_nameDB_STR db_urlDB_STR drd_tableDB_STR drr_tableDB_STR drg_tableDB_STR drc_tableDB_STR ruri_avpDB_STR gw_id_avpDB_STR gw_priprefix_avpDB_STR gw_sock_avpDB_STR rule_id_avpDB_STR rule_prefix_avpDB_STR carrier_id_avpDB_STR emergency_routing mysql idDB_INT selectiveRoutingIDDB_STR routingESNDB_INT npaDB_INT esgwriDB_STR emergency_report mysql idDB_INT callidDB_STR selectiveRoutingIDDB_STR routingESNDB_INT npaDB_INT esgwriDB_STR lroDB_STR VPC_organizationNameDB_STR VPC_hostnameDB_STR VPC_timestampDB_STR resultDB_STR dispositionDB_STR emergency_service_provider mysql idDB_INT organizationNameDB_STR hostIdDB_STR nenaIdDB_STR contactDB_STR certUriDB_STR nodeIPDB_STR attributionDB_INT fraud_detection mysql ruleidDB_INT profileidDB_INT prefixDB_STR start_hourDB_STR end_hourDB_STR daysoftheweekDB_STR cpm_warningDB_INT cpm_criticalDB_INT call_duration_warningDB_INT call_duration_criticalDB_INT total_calls_warningDB_INT total_calls_criticalDB_INT concurrent_calls_warningDB_INT concurrent_calls_criticalDB_INT sequential_calls_warningDB_INT sequential_calls_criticalDB_INT grp mysql idDB_INT usernameDB_STR domainDB_STR grpDB_STR last_modifiedDB_DATETIME re_grp mysql idDB_INT reg_expDB_STR group_idDB_INT imc_rooms mysql idDB_INT nameDB_STR domainDB_STR flagDB_INT imc_members mysql idDB_INT usernameDB_STR domainDB_STR roomDB_STR flagDB_INT load_balancer mysql idDB_INT group_idDB_INT dst_uriDB_STR resourcesDB_STR probe_modeDB_INT descriptionDB_STR silo mysql idDB_INT src_addrDB_STR dst_addrDB_STR usernameDB_STR domainDB_STR inc_timeDB_INT exp_timeDB_INT snd_timeDB_INT ctypeDB_STR bodyDB_BLOB address mysql idDB_INT grpDB_INT ipDB_STR maskDB_INT portDB_INT protoDB_STR patternDB_STR context_infoDB_STR presentity mysql idDB_INT usernameDB_STR domainDB_STR eventDB_STR etagDB_STR expiresDB_INT received_timeDB_INT bodyDB_BLOB extra_hdrsDB_BLOB senderDB_STR active_watchers mysql idDB_INT presentity_uriDB_STR watcher_usernameDB_STR watcher_domainDB_STR to_userDB_STR to_domainDB_STR eventDB_STR event_idDB_STR to_tagDB_STR from_tagDB_STR callidDB_STR local_cseqDB_INT remote_cseqDB_INT contactDB_STR record_routeDB_BLOB expiresDB_INT statusDB_INT reasonDB_STR versionDB_INT socket_infoDB_STR local_contactDB_STR watchers mysql idDB_INT presentity_uriDB_STR watcher_usernameDB_STR watcher_domainDB_STR eventDB_STR statusDB_INT reasonDB_STR inserted_timeDB_INT xcap mysql idDB_INT usernameDB_STR domainDB_STR docLONGBLOB doc_typeDB_INT etagDB_STR sourceDB_INT doc_uriDB_STR portDB_INT pua mysql idDB_INT pres_uriDB_STR pres_idDB_STR eventDB_INT expiresDB_INT desired_expiresDB_INT flagDB_INT etagDB_STR tuple_idDB_STR watcher_uriDB_STR to_uriDB_STR call_idDB_STR to_tagDB_STR from_tagDB_STR cseqDB_INT record_routeDB_BLOB contactDB_STR remote_contactDB_STR versionDB_INT extra_headersDB_BLOB registrant mysql idDB_INT registrarDB_STR proxyDB_STR aorDB_STR third_party_registrantDB_STR usernameDB_STR passwordDB_STR binding_URIDB_STR binding_paramsDB_STR expiryDB_INT forced_socketDB_STR aliases mysql idDB_INT usernameDB_STR domainDB_STR contactDB_STR receivedDB_STR pathDB_STR expiresDB_DATETIME qDB_DOUBLE callidDB_STR cseqDB_INT last_modifiedDB_DATETIME flagsDB_INT cflagsDB_STR user_agentDB_STR socketDB_STR methodsDB_INT sip_instanceDB_STR attrDB_STR rls_presentity mysql idDB_INT rlsubs_didDB_STR resource_uriDB_STR content_typeDB_STR presence_stateDB_BLOB expiresDB_INT updatedDB_INT auth_stateDB_INT reasonDB_STR rls_watchers mysql idDB_INT presentity_uriDB_STR to_userDB_STR to_domainDB_STR watcher_usernameDB_STR watcher_domainDB_STR eventDB_STR event_idDB_STR to_tagDB_STR from_tagDB_STR callidDB_STR local_cseqDB_INT remote_cseqDB_INT contactDB_STR record_routeDB_BLOB expiresDB_INT statusDB_INT reasonDB_STR versionDB_INT socket_infoDB_STR local_contactDB_STR rtpproxy_sockets mysql idDB_INT rtpproxy_sockDB_BLOB set_idDB_INT sip_trace mysql idDB_INT time_stampDB_DATETIME callidDB_STR trace_attrsDB_STR msgDB_BLOB methodDB_STR statusDB_STR from_protoDB_STR from_ipDB_STR from_portDB_INT to_protoDB_STR to_ipDB_STR to_portDB_INT fromtagDB_STR directionDB_STR speed_dial mysql idDB_INT usernameDB_STR domainDB_STR sd_usernameDB_STR sd_domainDB_STR new_uriDB_STR fnameDB_STR lnameDB_STR descriptionDB_STR version mysql table_nameDB_STR table_versionDB_INT tls_mgm mysql idDB_STR addressDB_STR typeDB_INT methodDB_STR verify_certDB_INT require_certDB_INT certificateDB_STR private_keyDB_STR crl_check_allDB_INT crl_dirDB_STR ca_listDB_STR ca_dirDB_STR cipher_listDB_STR dh_paramsDB_STR ec_curveDB_STR uri mysql idDB_INT usernameDB_STR domainDB_STR uri_userDB_STR last_modifiedDB_DATETIME userblacklist mysql idDB_INT usernameDB_STR domainDB_STR prefixDB_STR whitelistDB_INT globalblacklist mysql idDB_INT prefixDB_STR whitelistDB_INT descriptionDB_STR location mysql contact_idDB_BIGINT usernameDB_STR domainDB_STR contactDB_STR receivedDB_STR pathDB_STR expiresDB_DATETIME qDB_DOUBLE callidDB_STR cseqDB_INT last_modifiedDB_DATETIME flagsDB_INT cflagsDB_STR user_agentDB_STR socketDB_STR methodsDB_INT sip_instanceDB_STR attrDB_STR acc show acc DB_QUERY idupdatemethodfrom_tagto_tagcallidsip_codesip_reasontimedurationms_durationsetuptimecreatedadd acc DB_INSERT methodfrom_tagto_tagcallidsip_codesip_reasontimedurationms_durationsetuptimecreatedupdate acc DB_UPDATE id= methodfrom_tagto_tagcallidsip_codesip_reasontimedurationms_durationsetuptimecreateddelete acc DB_DELETE id=missed_calls show missed_calls DB_QUERY idupdatemethodfrom_tagto_tagcallidsip_codesip_reasontimesetuptimecreatedadd missed_calls DB_INSERT methodfrom_tagto_tagcallidsip_codesip_reasontimesetuptimecreatedupdate missed_calls DB_UPDATE id= methodfrom_tagto_tagcallidsip_codesip_reasontimesetuptimecreateddelete missed_calls DB_DELETE id=dbaliases show dbaliases DB_QUERY idupdatealias_usernamealias_domainusernamedomainadd dbaliases DB_INSERT alias_usernamealias_domainusernamedomainupdate dbaliases DB_UPDATE id= alias_usernamealias_domainusernamedomaindelete dbaliases DB_DELETE id=subscriber show subscriber DB_QUERY idupdateusernamedomainpasswordemail_addressha1ha1brpidadd subscriber DB_INSERT usernamedomainpasswordemail_addressha1ha1brpidupdate subscriber DB_UPDATE id= usernamedomainpasswordemail_addressha1ha1brpiddelete subscriber DB_DELETE id=usr_preferences show usr_preferences DB_QUERY idupdateuuidusernamedomainattributetypevaluelast_modifiedadd usr_preferences DB_INSERT uuidusernamedomainattributetypevaluelast_modifiedupdate usr_preferences DB_UPDATE id= uuidusernamedomainattributetypevaluelast_modifieddelete usr_preferences DB_DELETE id=b2b_entities show b2b_entities DB_QUERY idupdatetypestaterurifrom_urito_urifrom_dnameto_dnametag0tag1callidcseq0cseq1contact0contact1route0route1sockinfo_srvparamlmlrclicleg_cseqleg_routeleg_tagleg_contactleg_sockinfoadd b2b_entities DB_INSERT typestaterurifrom_urito_urifrom_dnameto_dnametag0tag1callidcseq0cseq1contact0contact1route0route1sockinfo_srvparamlmlrclicleg_cseqleg_routeleg_tagleg_contactleg_sockinfoupdate b2b_entities DB_UPDATE id= typestaterurifrom_urito_urifrom_dnameto_dnametag0tag1callidcseq0cseq1contact0contact1route0route1sockinfo_srvparamlmlrclicleg_cseqleg_routeleg_tagleg_contactleg_sockinfodelete b2b_entities DB_DELETE id=b2b_logic show b2b_logic DB_QUERY idupdatesi_keyscenariosstatenext_sstatesparam0sparam1sparam2sparam3sparam4sdplifetimee1_typee1_side1_frome1_toe1_keye2_typee2_side2_frome2_toe2_keye3_typee3_side3_frome3_toe3_keyadd b2b_logic DB_INSERT si_keyscenariosstatenext_sstatesparam0sparam1sparam2sparam3sparam4sdplifetimee1_typee1_side1_frome1_toe1_keye2_typee2_side2_frome2_toe2_keye3_typee3_side3_frome3_toe3_keyupdate b2b_logic DB_UPDATE id= si_keyscenariosstatenext_sstatesparam0sparam1sparam2sparam3sparam4sdplifetimee1_typee1_side1_frome1_toe1_keye2_typee2_side2_frome2_toe2_keye3_typee3_side3_frome3_toe3_keydelete b2b_logic DB_DELETE id=b2b_sca show b2b_sca DB_QUERY idupdateshared_linewatchersapp1_shared_entityapp1_call_stateapp1_call_info_uriapp1_call_info_appearance_uriapp1_b2bl_keyapp2_shared_entityapp2_call_stateapp2_call_info_uriapp2_call_info_appearance_uriapp2_b2bl_keyapp3_shared_entityapp3_call_stateapp3_call_info_uriapp3_call_info_appearance_uriapp3_b2bl_keyapp4_shared_entityapp4_call_stateapp4_call_info_uriapp4_call_info_appearance_uriapp4_b2bl_keyapp5_shared_entityapp5_call_stateapp5_call_info_uriapp5_call_info_appearance_uriapp5_b2bl_keyapp6_shared_entityapp6_call_stateapp6_call_info_uriapp6_call_info_appearance_uriapp6_b2bl_keyapp7_shared_entityapp7_call_stateapp7_call_info_uriapp7_call_info_appearance_uriapp7_b2bl_keyapp8_shared_entityapp8_call_stateapp8_call_info_uriapp8_call_info_appearance_uriapp8_b2bl_keyapp9_shared_entityapp9_call_stateapp9_call_info_uriapp9_call_info_appearance_uriapp9_b2bl_keyapp10_shared_entityapp10_call_stateapp10_call_info_uriapp10_call_info_appearance_uriapp10_b2bl_keyadd b2b_sca DB_INSERT shared_linewatchersapp1_shared_entityapp1_call_stateapp1_call_info_uriapp1_call_info_appearance_uriapp1_b2bl_keyapp2_shared_entityapp2_call_stateapp2_call_info_uriapp2_call_info_appearance_uriapp2_b2bl_keyapp3_shared_entityapp3_call_stateapp3_call_info_uriapp3_call_info_appearance_uriapp3_b2bl_keyapp4_shared_entityapp4_call_stateapp4_call_info_uriapp4_call_info_appearance_uriapp4_b2bl_keyapp5_shared_entityapp5_call_stateapp5_call_info_uriapp5_call_info_appearance_uriapp5_b2bl_keyapp6_shared_entityapp6_call_stateapp6_call_info_uriapp6_call_info_appearance_uriapp6_b2bl_keyapp7_shared_entityapp7_call_stateapp7_call_info_uriapp7_call_info_appearance_uriapp7_b2bl_keyapp8_shared_entityapp8_call_stateapp8_call_info_uriapp8_call_info_appearance_uriapp8_b2bl_keyapp9_shared_entityapp9_call_stateapp9_call_info_uriapp9_call_info_appearance_uriapp9_b2bl_keyapp10_shared_entityapp10_call_stateapp10_call_info_uriapp10_call_info_appearance_uriapp10_b2bl_keyupdate b2b_sca DB_UPDATE id= shared_linewatchersapp1_shared_entityapp1_call_stateapp1_call_info_uriapp1_call_info_appearance_uriapp1_b2bl_keyapp2_shared_entityapp2_call_stateapp2_call_info_uriapp2_call_info_appearance_uriapp2_b2bl_keyapp3_shared_entityapp3_call_stateapp3_call_info_uriapp3_call_info_appearance_uriapp3_b2bl_keyapp4_shared_entityapp4_call_stateapp4_call_info_uriapp4_call_info_appearance_uriapp4_b2bl_keyapp5_shared_entityapp5_call_stateapp5_call_info_uriapp5_call_info_appearance_uriapp5_b2bl_keyapp6_shared_entityapp6_call_stateapp6_call_info_uriapp6_call_info_appearance_uriapp6_b2bl_keyapp7_shared_entityapp7_call_stateapp7_call_info_uriapp7_call_info_appearance_uriapp7_b2bl_keyapp8_shared_entityapp8_call_stateapp8_call_info_uriapp8_call_info_appearance_uriapp8_b2bl_keyapp9_shared_entityapp9_call_stateapp9_call_info_uriapp9_call_info_appearance_uriapp9_b2bl_keyapp10_shared_entityapp10_call_stateapp10_call_info_uriapp10_call_info_appearance_uriapp10_b2bl_keydelete b2b_sca DB_DELETE id=cachedb show cachedb DB_QUERY keynameupdatevaluecounterexpiresadd cachedb DB_INSERT keynamevaluecounterexpiresupdate cachedb DB_UPDATE keyname= valuecounterexpiresdelete cachedb DB_DELETE keyname=cc_flows show cc_flows DB_QUERY idupdateflowidpriorityskillprependcidmessage_welcomemessage_queueadd cc_flows DB_INSERT flowidpriorityskillprependcidmessage_welcomemessage_queueupdate cc_flows DB_UPDATE id= flowidpriorityskillprependcidmessage_welcomemessage_queuedelete cc_flows DB_DELETE id=cc_agents show cc_agents DB_QUERY idupdateagentidlocationlogstateskillslast_call_endadd cc_agents DB_INSERT agentidlocationlogstateskillslast_call_endupdate cc_agents DB_UPDATE id= agentidlocationlogstateskillslast_call_enddelete cc_agents DB_DELETE id=cc_cdrs show cc_cdrs DB_QUERY idupdatecallerreceived_timestampwait_timepickup_timetalk_timeflow_idagent_idcall_typerejectedfstatscidadd cc_cdrs DB_INSERT callerreceived_timestampwait_timepickup_timetalk_timeflow_idagent_idcall_typerejectedfstatscidupdate cc_cdrs DB_UPDATE id= callerreceived_timestampwait_timepickup_timetalk_timeflow_idagent_idcall_typerejectedfstatsciddelete cc_cdrs DB_DELETE id=cc_calls show cc_calls DB_QUERY idupdatestateig_cbackno_rejsetup_timeetalast_startrecv_timecaller_dncaller_unb2buaidflowagentadd cc_calls DB_INSERT stateig_cbackno_rejsetup_timeetalast_startrecv_timecaller_dncaller_unb2buaidflowagentupdate cc_calls DB_UPDATE id= stateig_cbackno_rejsetup_timeetalast_startrecv_timecaller_dncaller_unb2buaidflowagentdelete cc_calls DB_DELETE id=carrierroute show carrierroute DB_QUERY idupdatecarrierdomainscan_prefixflagsmaskprobstriprewrite_hostrewrite_prefixrewrite_suffixdescriptionadd carrierroute DB_INSERT carrierdomainscan_prefixflagsmaskprobstriprewrite_hostrewrite_prefixrewrite_suffixdescriptionupdate carrierroute DB_UPDATE id= carrierdomainscan_prefixflagsmaskprobstriprewrite_hostrewrite_prefixrewrite_suffixdescriptiondelete carrierroute DB_DELETE id=carrierfailureroute show carrierfailureroute DB_QUERY idupdatecarrierdomainscan_prefixhost_namereply_codeflagsmasknext_domaindescriptionadd carrierfailureroute DB_INSERT carrierdomainscan_prefixhost_namereply_codeflagsmasknext_domaindescriptionupdate carrierfailureroute DB_UPDATE id= carrierdomainscan_prefixhost_namereply_codeflagsmasknext_domaindescriptiondelete carrierfailureroute DB_DELETE id=route_tree show route_tree DB_QUERY idupdatecarrieradd route_tree DB_INSERT carrierupdate route_tree DB_UPDATE id= carrierdelete route_tree DB_DELETE id=closeddial show closeddial DB_QUERY idupdateusernamedomaincd_usernamecd_domaingroup_idnew_uriadd closeddial DB_INSERT usernamedomaincd_usernamecd_domaingroup_idnew_uriupdate closeddial DB_UPDATE id= usernamedomaincd_usernamecd_domaingroup_idnew_uridelete closeddial DB_DELETE id=clusterer show clusterer DB_QUERY idupdatecluster_idmachine_idurlstatelast_attemptfailed_attemptsno_triesdurationdescriptionadd clusterer DB_INSERT cluster_idmachine_idurlstatelast_attemptfailed_attemptsno_triesdurationdescriptionupdate clusterer DB_UPDATE id= cluster_idmachine_idurlstatelast_attemptfailed_attemptsno_triesdurationdescriptiondelete clusterer DB_DELETE id=cpl show cpl DB_QUERY idupdateusernamedomaincpl_xmlcpl_binadd cpl DB_INSERT usernamedomaincpl_xmlcpl_binupdate cpl DB_UPDATE id= usernamedomaincpl_xmlcpl_bindelete cpl DB_DELETE id=dialog show dialog DB_QUERY dlg_idupdatecallidfrom_urifrom_tagto_urito_tagmangled_from_urimangled_to_uricaller_cseqcallee_cseqcaller_ping_cseqcallee_ping_cseqcaller_route_setcallee_route_setcaller_contactcallee_contactcaller_sockcallee_sockstatestart_timetimeoutvarsprofilesscript_flagsmodule_flagsflagsadd dialog DB_INSERT dlg_idcallidfrom_urifrom_tagto_urito_tagmangled_from_urimangled_to_uricaller_cseqcallee_cseqcaller_ping_cseqcallee_ping_cseqcaller_route_setcallee_route_setcaller_contactcallee_contactcaller_sockcallee_sockstatestart_timetimeoutvarsprofilesscript_flagsmodule_flagsflagsupdate dialog DB_UPDATE dlg_id= callidfrom_urifrom_tagto_urito_tagmangled_from_urimangled_to_uricaller_cseqcallee_cseqcaller_ping_cseqcallee_ping_cseqcaller_route_setcallee_route_setcaller_contactcallee_contactcaller_sockcallee_sockstatestart_timetimeoutvarsprofilesscript_flagsmodule_flagsflagsdelete dialog DB_DELETE dlg_id=dialplan show dialplan DB_QUERY idupdatedpidprmatch_opmatch_expmatch_flagssubst_exprepl_exptimerecdisabledattrsadd dialplan DB_INSERT dpidprmatch_opmatch_expmatch_flagssubst_exprepl_exptimerecdisabledattrsupdate dialplan DB_UPDATE id= dpidprmatch_opmatch_expmatch_flagssubst_exprepl_exptimerecdisabledattrsdelete dialplan DB_DELETE id=dispatcher show dispatcher DB_QUERY idupdatesetiddestinationsocketstateweightpriorityattrsdescriptionadd dispatcher DB_INSERT setiddestinationsocketstateweightpriorityattrsdescriptionupdate dispatcher DB_UPDATE id= setiddestinationsocketstateweightpriorityattrsdescriptiondelete dispatcher DB_DELETE id=domain show domain DB_QUERY idupdatedomainattrslast_modifiedadd domain DB_INSERT domainattrslast_modifiedupdate domain DB_UPDATE id= domainattrslast_modifieddelete domain DB_DELETE id=domainpolicy show domainpolicy DB_QUERY idupdateruletypeattvaldescriptionadd domainpolicy DB_INSERT ruletypeattvaldescriptionupdate domainpolicy DB_UPDATE id= ruletypeattvaldescriptiondelete domainpolicy DB_DELETE id=dr_gateways show dr_gateways DB_QUERY idupdategwidtypeaddressstrippri_prefixattrsprobe_modestatesocketdescriptionadd dr_gateways DB_INSERT gwidtypeaddressstrippri_prefixattrsprobe_modestatesocketdescriptionupdate dr_gateways DB_UPDATE id= gwidtypeaddressstrippri_prefixattrsprobe_modestatesocketdescriptiondelete dr_gateways DB_DELETE id=dr_rules show dr_rules DB_QUERY ruleidupdategroupidprefixtimerecpriorityrouteidgwlistattrsdescriptionadd dr_rules DB_INSERT groupidprefixtimerecpriorityrouteidgwlistattrsdescriptionupdate dr_rules DB_UPDATE ruleid= groupidprefixtimerecpriorityrouteidgwlistattrsdescriptiondelete dr_rules DB_DELETE ruleid=dr_carriers show dr_carriers DB_QUERY idupdatecarrieridgwlistflagsstateattrsdescriptionadd dr_carriers DB_INSERT carrieridgwlistflagsstateattrsdescriptionupdate dr_carriers DB_UPDATE id= carrieridgwlistflagsstateattrsdescriptiondelete dr_carriers DB_DELETE id=dr_groups show dr_groups DB_QUERY idupdateusernamedomaingroupiddescriptionadd dr_groups DB_INSERT usernamedomaingroupiddescriptionupdate dr_groups DB_UPDATE id= usernamedomaingroupiddescriptiondelete dr_groups DB_DELETE id=dr_partitions show dr_partitions DB_QUERY idupdatepartition_namedb_urldrd_tabledrr_tabledrg_tabledrc_tableruri_avpgw_id_avpgw_priprefix_avpgw_sock_avprule_id_avprule_prefix_avpcarrier_id_avpadd dr_partitions DB_INSERT partition_namedb_urldrd_tabledrr_tabledrg_tabledrc_tableruri_avpgw_id_avpgw_priprefix_avpgw_sock_avprule_id_avprule_prefix_avpcarrier_id_avpupdate dr_partitions DB_UPDATE id= partition_namedb_urldrd_tabledrr_tabledrg_tabledrc_tableruri_avpgw_id_avpgw_priprefix_avpgw_sock_avprule_id_avprule_prefix_avpcarrier_id_avpdelete dr_partitions DB_DELETE id=emergency_routing show emergency_routing DB_QUERY idupdateselectiveRoutingIDroutingESNnpaesgwriadd emergency_routing DB_INSERT selectiveRoutingIDroutingESNnpaesgwriupdate emergency_routing DB_UPDATE id= selectiveRoutingIDroutingESNnpaesgwridelete emergency_routing DB_DELETE id=emergency_report show emergency_report DB_QUERY idupdatecallidselectiveRoutingIDroutingESNnpaesgwrilroVPC_organizationNameVPC_hostnameVPC_timestampresultdispositionadd emergency_report DB_INSERT callidselectiveRoutingIDroutingESNnpaesgwrilroVPC_organizationNameVPC_hostnameVPC_timestampresultdispositionupdate emergency_report DB_UPDATE id= callidselectiveRoutingIDroutingESNnpaesgwrilroVPC_organizationNameVPC_hostnameVPC_timestampresultdispositiondelete emergency_report DB_DELETE id=emergency_service_provider show emergency_service_provider DB_QUERY idupdateorganizationNamehostIdnenaIdcontactcertUrinodeIPattributionadd emergency_service_provider DB_INSERT organizationNamehostIdnenaIdcontactcertUrinodeIPattributionupdate emergency_service_provider DB_UPDATE id= organizationNamehostIdnenaIdcontactcertUrinodeIPattributiondelete emergency_service_provider DB_DELETE id=fraud_detection show fraud_detection DB_QUERY ruleidupdateprofileidprefixstart_hourend_hourdaysoftheweekcpm_warningcpm_criticalcall_duration_warningcall_duration_criticaltotal_calls_warningtotal_calls_criticalconcurrent_calls_warningconcurrent_calls_criticalsequential_calls_warningsequential_calls_criticaladd fraud_detection DB_INSERT profileidprefixstart_hourend_hourdaysoftheweekcpm_warningcpm_criticalcall_duration_warningcall_duration_criticaltotal_calls_warningtotal_calls_criticalconcurrent_calls_warningconcurrent_calls_criticalsequential_calls_warningsequential_calls_criticalupdate fraud_detection DB_UPDATE ruleid= profileidprefixstart_hourend_hourdaysoftheweekcpm_warningcpm_criticalcall_duration_warningcall_duration_criticaltotal_calls_warningtotal_calls_criticalconcurrent_calls_warningconcurrent_calls_criticalsequential_calls_warningsequential_calls_criticaldelete fraud_detection DB_DELETE ruleid=grp show grp DB_QUERY idupdateusernamedomaingrplast_modifiedadd grp DB_INSERT usernamedomaingrplast_modifiedupdate grp DB_UPDATE id= usernamedomaingrplast_modifieddelete grp DB_DELETE id=re_grp show re_grp DB_QUERY idupdatereg_expgroup_idadd re_grp DB_INSERT reg_expgroup_idupdate re_grp DB_UPDATE id= reg_expgroup_iddelete re_grp DB_DELETE id=imc_rooms show imc_rooms DB_QUERY idupdatenamedomainflagadd imc_rooms DB_INSERT namedomainflagupdate imc_rooms DB_UPDATE id= namedomainflagdelete imc_rooms DB_DELETE id=imc_members show imc_members DB_QUERY idupdateusernamedomainroomflagadd imc_members DB_INSERT usernamedomainroomflagupdate imc_members DB_UPDATE id= usernamedomainroomflagdelete imc_members DB_DELETE id=load_balancer show load_balancer DB_QUERY idupdategroup_iddst_uriresourcesprobe_modedescriptionadd load_balancer DB_INSERT group_iddst_uriresourcesprobe_modedescriptionupdate load_balancer DB_UPDATE id= group_iddst_uriresourcesprobe_modedescriptiondelete load_balancer DB_DELETE id=silo show silo DB_QUERY idupdatesrc_addrdst_addrusernamedomaininc_timeexp_timesnd_timectypebodyadd silo DB_INSERT src_addrdst_addrusernamedomaininc_timeexp_timesnd_timectypebodyupdate silo DB_UPDATE id= src_addrdst_addrusernamedomaininc_timeexp_timesnd_timectypebodydelete silo DB_DELETE id=address show address DB_QUERY idupdategrpipmaskportprotopatterncontext_infoadd address DB_INSERT grpipmaskportprotopatterncontext_infoupdate address DB_UPDATE id= grpipmaskportprotopatterncontext_infodelete address DB_DELETE id=presentity show presentity DB_QUERY idupdateusernamedomaineventetagexpiresreceived_timebodyextra_hdrssenderadd presentity DB_INSERT usernamedomaineventetagexpiresreceived_timebodyextra_hdrssenderupdate presentity DB_UPDATE id= usernamedomaineventetagexpiresreceived_timebodyextra_hdrssenderdelete presentity DB_DELETE id=active_watchers show active_watchers DB_QUERY idupdatepresentity_uriwatcher_usernamewatcher_domainto_userto_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactadd active_watchers DB_INSERT presentity_uriwatcher_usernamewatcher_domainto_userto_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactupdate active_watchers DB_UPDATE id= presentity_uriwatcher_usernamewatcher_domainto_userto_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactdelete active_watchers DB_DELETE id=watchers show watchers DB_QUERY idupdatepresentity_uriwatcher_usernamewatcher_domaineventstatusreasoninserted_timeadd watchers DB_INSERT presentity_uriwatcher_usernamewatcher_domaineventstatusreasoninserted_timeupdate watchers DB_UPDATE id= presentity_uriwatcher_usernamewatcher_domaineventstatusreasoninserted_timedelete watchers DB_DELETE id=xcap show xcap DB_QUERY idupdateusernamedomaindocdoc_typeetagsourcedoc_uriportadd xcap DB_INSERT usernamedomaindocdoc_typeetagsourcedoc_uriportupdate xcap DB_UPDATE id= usernamedomaindocdoc_typeetagsourcedoc_uriportdelete xcap DB_DELETE id=pua show pua DB_QUERY idupdatepres_uripres_ideventexpiresdesired_expiresflagetagtuple_idwatcher_urito_uricall_idto_tagfrom_tagcseqrecord_routecontactremote_contactversionextra_headersadd pua DB_INSERT pres_uripres_ideventexpiresdesired_expiresflagetagtuple_idwatcher_urito_uricall_idto_tagfrom_tagcseqrecord_routecontactremote_contactversionextra_headersupdate pua DB_UPDATE id= pres_uripres_ideventexpiresdesired_expiresflagetagtuple_idwatcher_urito_uricall_idto_tagfrom_tagcseqrecord_routecontactremote_contactversionextra_headersdelete pua DB_DELETE id=registrant show registrant DB_QUERY idupdateregistrarproxyaorthird_party_registrantusernamepasswordbinding_URIbinding_paramsexpiryforced_socketadd registrant DB_INSERT registrarproxyaorthird_party_registrantusernamepasswordbinding_URIbinding_paramsexpiryforced_socketupdate registrant DB_UPDATE id= registrarproxyaorthird_party_registrantusernamepasswordbinding_URIbinding_paramsexpiryforced_socketdelete registrant DB_DELETE id=aliases show aliases DB_QUERY idupdateusernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattradd aliases DB_INSERT usernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattrupdate aliases DB_UPDATE id= usernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattrdelete aliases DB_DELETE id=rls_presentity show rls_presentity DB_QUERY idupdaterlsubs_didresource_uricontent_typepresence_stateexpiresupdatedauth_statereasonadd rls_presentity DB_INSERT rlsubs_didresource_uricontent_typepresence_stateexpiresupdatedauth_statereasonupdate rls_presentity DB_UPDATE id= rlsubs_didresource_uricontent_typepresence_stateexpiresupdatedauth_statereasondelete rls_presentity DB_DELETE id=rls_watchers show rls_watchers DB_QUERY idupdatepresentity_urito_userto_domainwatcher_usernamewatcher_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactadd rls_watchers DB_INSERT presentity_urito_userto_domainwatcher_usernamewatcher_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactupdate rls_watchers DB_UPDATE id= presentity_urito_userto_domainwatcher_usernamewatcher_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactdelete rls_watchers DB_DELETE id=rtpproxy_sockets show rtpproxy_sockets DB_QUERY idupdatertpproxy_sockset_idadd rtpproxy_sockets DB_INSERT rtpproxy_sockset_idupdate rtpproxy_sockets DB_UPDATE id= rtpproxy_sockset_iddelete rtpproxy_sockets DB_DELETE id=sip_trace show sip_trace DB_QUERY idupdatetime_stampcallidtrace_attrsmsgmethodstatusfrom_protofrom_ipfrom_portto_prototo_ipto_portfromtagdirectionadd sip_trace DB_INSERT time_stampcallidtrace_attrsmsgmethodstatusfrom_protofrom_ipfrom_portto_prototo_ipto_portfromtagdirectionupdate sip_trace DB_UPDATE id= time_stampcallidtrace_attrsmsgmethodstatusfrom_protofrom_ipfrom_portto_prototo_ipto_portfromtagdirectiondelete sip_trace DB_DELETE id=speed_dial show speed_dial DB_QUERY idupdateusernamedomainsd_usernamesd_domainnew_urifnamelnamedescriptionadd speed_dial DB_INSERT usernamedomainsd_usernamesd_domainnew_urifnamelnamedescriptionupdate speed_dial DB_UPDATE id= usernamedomainsd_usernamesd_domainnew_urifnamelnamedescriptiondelete speed_dial DB_DELETE id=version show version DB_QUERY table_nametable_versionadd version DB_INSERT table_nametable_versiontls_mgm show tls_mgm DB_QUERY idupdateaddresstypemethodverify_certrequire_certcertificateprivate_keycrl_check_allcrl_dirca_listca_dircipher_listdh_paramsec_curveadd tls_mgm DB_INSERT idaddresstypemethodverify_certrequire_certcertificateprivate_keycrl_check_allcrl_dirca_listca_dircipher_listdh_paramsec_curveupdate tls_mgm DB_UPDATE id= addresstypemethodverify_certrequire_certcertificateprivate_keycrl_check_allcrl_dirca_listca_dircipher_listdh_paramsec_curvedelete tls_mgm DB_DELETE id=uri show uri DB_QUERY idupdateusernamedomainuri_userlast_modifiedadd uri DB_INSERT usernamedomainuri_userlast_modifiedupdate uri DB_UPDATE id= usernamedomainuri_userlast_modifieddelete uri DB_DELETE id=userblacklist show userblacklist DB_QUERY idupdateusernamedomainprefixwhitelistadd userblacklist DB_INSERT usernamedomainprefixwhitelistupdate userblacklist DB_UPDATE id= usernamedomainprefixwhitelistdelete userblacklist DB_DELETE id=globalblacklist show globalblacklist DB_QUERY idupdateprefixwhitelistdescriptionadd globalblacklist DB_INSERT prefixwhitelistdescriptionupdate globalblacklist DB_UPDATE id= prefixwhitelistdescriptiondelete globalblacklist DB_DELETE id=location show location DB_QUERY contact_idupdateusernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattradd location DB_INSERT usernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattrupdate location DB_UPDATE contact_id= usernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattrdelete location DB_DELETE contact_id= opensips-2.2.2/scripts/pi_http/pi_framework_example.xml000066400000000000000000000313021300170765700234420ustar00rootroot00000000000000 mysql://opensips:opensipsrw@localhost/opensips dispatcher mysql idDB_INT setidDB_INT destinationDB_STR URI_IPV4HOST socketDB_STR P_IPV4_PORT flagsDB_INT weightDB_INT attrsDB_STR descriptionDB_STR dialplan mysql idDB_INT dpidDB_INT prDB_INT match_opDB_INT match_expDB_STR match_flagsDB_INT subst_expDB_STR repl_expDB_STR disabledDB_INT attrsDB_STR dispatcher show_destinations_with_small_setid dispatcher DB_QUERY setid< idsetiddestinationdescriptionshow_all dispatcher DB_QUERY idsetiddestinationsocketflagsweightattrsdescriptionupdate_setid dispatcher DB_UPDATE id= setidupdate_destination dispatcher DB_UPDATE id= destinationupdate_attr dispatcher DB_UPDATE id= attrsupdate_description dispatcher DB_UPDATE id= description update_socket dispatcher DB_UPDATE id= socketadd_gw dispatcher DB_INSERT setiddestinationsocketattrsdescriptionadd_server_with_setid_100 dispatcher DB_INSERT setid100destinationsocketattrsdescriptiondelete_by_id dispatcher DB_DELETE id=dialplan show_all dialplan DB_QUERY iddpidprmatch_opmatch_expmatch_flagssubst_exprepl_expdisabledattrsidshow_dpid dialplan DB_QUERY dpid= iddpidprmatch_opmatch_expmatch_flagssubst_exprepl_expdisabledattrsidshow_exact_matching dialplan DB_QUERY match_op=0 iddpidprmatch_opmatch_expmatch_flagssubst_exprepl_expdisabledattrsshow_regex_matching dialplan DB_QUERY match_op=1 iddpidprmatch_opmatch_expmatch_flagssubst_exprepl_expdisabledattrsadd dialplan DB_INSERT dpidprmatch_op01match_expmatch_flags01disabled01attrsdelete dialplan DB_DELETE id=update_attr dialplan DB_UPDATE id= attrsupdate_repl_exp dialplan DB_UPDATE id= repl_expenable_rule dialplan DB_UPDATE id= disabled0disable_rule dialplan DB_UPDATE id= disabled1replace dialplan DB_REPLACE dpidprmatch_opmatch_expmatch_flagssubst_exprepl_expdisabledattrs opensips-2.2.2/scripts/pi_http/presence-mod000066400000000000000000000270361300170765700210350ustar00rootroot00000000000000 presentity show presentity DB_QUERY idupdateusernamedomaineventetagexpiresreceived_timebodyextra_hdrssenderadd presentity DB_INSERT usernamedomaineventetagexpiresreceived_timebodyextra_hdrssenderupdate presentity DB_UPDATE id= usernamedomaineventetagexpiresreceived_timebodyextra_hdrssenderdelete presentity DB_DELETE id=active_watchers show active_watchers DB_QUERY idupdatepresentity_uriwatcher_usernamewatcher_domainto_userto_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactadd active_watchers DB_INSERT presentity_uriwatcher_usernamewatcher_domainto_userto_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactupdate active_watchers DB_UPDATE id= presentity_uriwatcher_usernamewatcher_domainto_userto_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactdelete active_watchers DB_DELETE id=watchers show watchers DB_QUERY idupdatepresentity_uriwatcher_usernamewatcher_domaineventstatusreasoninserted_timeadd watchers DB_INSERT presentity_uriwatcher_usernamewatcher_domaineventstatusreasoninserted_timeupdate watchers DB_UPDATE id= presentity_uriwatcher_usernamewatcher_domaineventstatusreasoninserted_timedelete watchers DB_DELETE id=xcap show xcap DB_QUERY idupdateusernamedomaindocdoc_typeetagsourcedoc_uriportadd xcap DB_INSERT usernamedomaindocdoc_typeetagsourcedoc_uriportupdate xcap DB_UPDATE id= usernamedomaindocdoc_typeetagsourcedoc_uriportdelete xcap DB_DELETE id=pua show pua DB_QUERY idupdatepres_uripres_ideventexpiresdesired_expiresflagetagtuple_idwatcher_urito_uricall_idto_tagfrom_tagcseqrecord_routecontactremote_contactversionextra_headersadd pua DB_INSERT pres_uripres_ideventexpiresdesired_expiresflagetagtuple_idwatcher_urito_uricall_idto_tagfrom_tagcseqrecord_routecontactremote_contactversionextra_headersupdate pua DB_UPDATE id= pres_uripres_ideventexpiresdesired_expiresflagetagtuple_idwatcher_urito_uricall_idto_tagfrom_tagcseqrecord_routecontactremote_contactversionextra_headersdelete pua DB_DELETE id= opensips-2.2.2/scripts/pi_http/presence-table000066400000000000000000000115051300170765700213370ustar00rootroot00000000000000 presentity mysql idDB_INT usernameDB_STR domainDB_STR eventDB_STR etagDB_STR expiresDB_INT received_timeDB_INT bodyDB_BLOB extra_hdrsDB_BLOB senderDB_STR active_watchers mysql idDB_INT presentity_uriDB_STR watcher_usernameDB_STR watcher_domainDB_STR to_userDB_STR to_domainDB_STR eventDB_STR event_idDB_STR to_tagDB_STR from_tagDB_STR callidDB_STR local_cseqDB_INT remote_cseqDB_INT contactDB_STR record_routeDB_BLOB expiresDB_INT statusDB_INT reasonDB_STR versionDB_INT socket_infoDB_STR local_contactDB_STR watchers mysql idDB_INT presentity_uriDB_STR watcher_usernameDB_STR watcher_domainDB_STR eventDB_STR statusDB_INT reasonDB_STR inserted_timeDB_INT xcap mysql idDB_INT usernameDB_STR domainDB_STR docLONGBLOB doc_typeDB_INT etagDB_STR sourceDB_INT doc_uriDB_STR portDB_INT pua mysql idDB_INT pres_uriDB_STR pres_idDB_STR eventDB_INT expiresDB_INT desired_expiresDB_INT flagDB_INT etagDB_STR tuple_idDB_STR watcher_uriDB_STR to_uriDB_STR call_idDB_STR to_tagDB_STR from_tagDB_STR cseqDB_INT record_routeDB_BLOB contactDB_STR remote_contactDB_STR versionDB_INT extra_headersDB_BLOB opensips-2.2.2/scripts/pi_http/registrant-mod000066400000000000000000000041051300170765700214030ustar00rootroot00000000000000 registrant show registrant DB_QUERY idupdateregistrarproxyaorthird_party_registrantusernamepasswordbinding_URIbinding_paramsexpiryforced_socketadd registrant DB_INSERT registrarproxyaorthird_party_registrantusernamepasswordbinding_URIbinding_paramsexpiryforced_socketupdate registrant DB_UPDATE id= registrarproxyaorthird_party_registrantusernamepasswordbinding_URIbinding_paramsexpiryforced_socketdelete registrant DB_DELETE id= opensips-2.2.2/scripts/pi_http/registrant-table000066400000000000000000000015161300170765700217160ustar00rootroot00000000000000 registrant mysql idDB_INT registrarDB_STR proxyDB_STR aorDB_STR third_party_registrantDB_STR usernameDB_STR passwordDB_STR binding_URIDB_STR binding_paramsDB_STR expiryDB_INT forced_socketDB_STR opensips-2.2.2/scripts/pi_http/registrar-mod000066400000000000000000000053531300170765700212310ustar00rootroot00000000000000 aliases show aliases DB_QUERY idupdateusernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattradd aliases DB_INSERT usernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattrupdate aliases DB_UPDATE id= usernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattrdelete aliases DB_DELETE id= opensips-2.2.2/scripts/pi_http/registrar-table000066400000000000000000000023331300170765700215340ustar00rootroot00000000000000 aliases mysql idDB_INT usernameDB_STR domainDB_STR contactDB_STR receivedDB_STR pathDB_STR expiresDB_DATETIME qDB_DOUBLE callidDB_STR cseqDB_INT last_modifiedDB_DATETIME flagsDB_INT cflagsDB_STR user_agentDB_STR socketDB_STR methodsDB_INT sip_instanceDB_STR attrDB_STR opensips-2.2.2/scripts/pi_http/rls-mod000066400000000000000000000121231300170765700200200ustar00rootroot00000000000000 rls_presentity show rls_presentity DB_QUERY idupdaterlsubs_didresource_uricontent_typepresence_stateexpiresupdatedauth_statereasonadd rls_presentity DB_INSERT rlsubs_didresource_uricontent_typepresence_stateexpiresupdatedauth_statereasonupdate rls_presentity DB_UPDATE id= rlsubs_didresource_uricontent_typepresence_stateexpiresupdatedauth_statereasondelete rls_presentity DB_DELETE id=rls_watchers show rls_watchers DB_QUERY idupdatepresentity_urito_userto_domainwatcher_usernamewatcher_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactadd rls_watchers DB_INSERT presentity_urito_userto_domainwatcher_usernamewatcher_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactupdate rls_watchers DB_UPDATE id= presentity_urito_userto_domainwatcher_usernamewatcher_domaineventevent_idto_tagfrom_tagcallidlocal_cseqremote_cseqcontactrecord_routeexpiresstatusreasonversionsocket_infolocal_contactdelete rls_watchers DB_DELETE id= opensips-2.2.2/scripts/pi_http/rls-table000066400000000000000000000042371300170765700203370ustar00rootroot00000000000000 rls_presentity mysql idDB_INT rlsubs_didDB_STR resource_uriDB_STR content_typeDB_STR presence_stateDB_BLOB expiresDB_INT updatedDB_INT auth_stateDB_INT reasonDB_STR rls_watchers mysql idDB_INT presentity_uriDB_STR to_userDB_STR to_domainDB_STR watcher_usernameDB_STR watcher_domainDB_STR eventDB_STR event_idDB_STR to_tagDB_STR from_tagDB_STR callidDB_STR local_cseqDB_INT remote_cseqDB_INT contactDB_STR record_routeDB_BLOB expiresDB_INT statusDB_INT reasonDB_STR versionDB_INT socket_infoDB_STR local_contactDB_STR opensips-2.2.2/scripts/pi_http/rtpproxy-mod000066400000000000000000000022211300170765700211250ustar00rootroot00000000000000 rtpproxy_sockets show rtpproxy_sockets DB_QUERY idupdatertpproxy_sockset_idadd rtpproxy_sockets DB_INSERT rtpproxy_sockset_idupdate rtpproxy_sockets DB_UPDATE id= rtpproxy_sockset_iddelete rtpproxy_sockets DB_DELETE id= opensips-2.2.2/scripts/pi_http/rtpproxy-table000066400000000000000000000005411300170765700214400ustar00rootroot00000000000000 rtpproxy_sockets mysql idDB_INT rtpproxy_sockDB_BLOB set_idDB_INT opensips-2.2.2/scripts/pi_http/siptrace-mod000066400000000000000000000047021300170765700210360ustar00rootroot00000000000000 sip_trace show sip_trace DB_QUERY idupdatetime_stampcallidtrace_attrsmsgmethodstatusfrom_protofrom_ipfrom_portto_prototo_ipto_portfromtagdirectionadd sip_trace DB_INSERT time_stampcallidtrace_attrsmsgmethodstatusfrom_protofrom_ipfrom_portto_prototo_ipto_portfromtagdirectionupdate sip_trace DB_UPDATE id= time_stampcallidtrace_attrsmsgmethodstatusfrom_protofrom_ipfrom_portto_prototo_ipto_portfromtagdirectiondelete sip_trace DB_DELETE id= opensips-2.2.2/scripts/pi_http/siptrace-table000066400000000000000000000020561300170765700213460ustar00rootroot00000000000000 sip_trace mysql idDB_INT time_stampDB_DATETIME callidDB_STR trace_attrsDB_STR msgDB_BLOB methodDB_STR statusDB_STR from_protoDB_STR from_ipDB_STR from_portDB_INT to_protoDB_STR to_ipDB_STR to_portDB_INT fromtagDB_STR directionDB_STR opensips-2.2.2/scripts/pi_http/speeddial-mod000066400000000000000000000034341300170765700211570ustar00rootroot00000000000000 speed_dial show speed_dial DB_QUERY idupdateusernamedomainsd_usernamesd_domainnew_urifnamelnamedescriptionadd speed_dial DB_INSERT usernamedomainsd_usernamesd_domainnew_urifnamelnamedescriptionupdate speed_dial DB_UPDATE id= usernamedomainsd_usernamesd_domainnew_urifnamelnamedescriptiondelete speed_dial DB_DELETE id= opensips-2.2.2/scripts/pi_http/speeddial-table000066400000000000000000000012751300170765700214700ustar00rootroot00000000000000 speed_dial mysql idDB_INT usernameDB_STR domainDB_STR sd_usernameDB_STR sd_domainDB_STR new_uriDB_STR fnameDB_STR lnameDB_STR descriptionDB_STR opensips-2.2.2/scripts/pi_http/standard-mod000066400000000000000000000010311300170765700210140ustar00rootroot00000000000000 version show version DB_QUERY table_nametable_versionadd version DB_INSERT table_nametable_version opensips-2.2.2/scripts/pi_http/standard-table000066400000000000000000000004211300170765700213260ustar00rootroot00000000000000 version mysql table_nameDB_STR table_versionDB_INT opensips-2.2.2/scripts/pi_http/tls_mgm-mod000066400000000000000000000050201300170765700206600ustar00rootroot00000000000000 tls_mgm show tls_mgm DB_QUERY idupdateaddresstypemethodverify_certrequire_certcertificateprivate_keycrl_check_allcrl_dirca_listca_dircipher_listdh_paramsec_curveadd tls_mgm DB_INSERT idaddresstypemethodverify_certrequire_certcertificateprivate_keycrl_check_allcrl_dirca_listca_dircipher_listdh_paramsec_curveupdate tls_mgm DB_UPDATE id= addresstypemethodverify_certrequire_certcertificateprivate_keycrl_check_allcrl_dirca_listca_dircipher_listdh_paramsec_curvedelete tls_mgm DB_DELETE id= opensips-2.2.2/scripts/pi_http/tls_mgm-table000066400000000000000000000020651300170765700211760ustar00rootroot00000000000000 tls_mgm mysql idDB_STR addressDB_STR typeDB_INT methodDB_STR verify_certDB_INT require_certDB_INT certificateDB_STR private_keyDB_STR crl_check_allDB_INT crl_dirDB_STR ca_listDB_STR ca_dirDB_STR cipher_listDB_STR dh_paramsDB_STR ec_curveDB_STR opensips-2.2.2/scripts/pi_http/uri_db-mod000066400000000000000000000024551300170765700204730ustar00rootroot00000000000000 uri show uri DB_QUERY idupdateusernamedomainuri_userlast_modifiedadd uri DB_INSERT usernamedomainuri_userlast_modifiedupdate uri DB_UPDATE id= usernamedomainuri_userlast_modifieddelete uri DB_DELETE id= opensips-2.2.2/scripts/pi_http/uri_db-table000066400000000000000000000006721300170765700210020ustar00rootroot00000000000000 uri mysql idDB_INT usernameDB_STR domainDB_STR uri_userDB_STR last_modifiedDB_DATETIME opensips-2.2.2/scripts/pi_http/userblacklist-mod000066400000000000000000000051241300170765700220720ustar00rootroot00000000000000 userblacklist show userblacklist DB_QUERY idupdateusernamedomainprefixwhitelistadd userblacklist DB_INSERT usernamedomainprefixwhitelistupdate userblacklist DB_UPDATE id= usernamedomainprefixwhitelistdelete userblacklist DB_DELETE id=globalblacklist show globalblacklist DB_QUERY idupdateprefixwhitelistdescriptionadd globalblacklist DB_INSERT prefixwhitelistdescriptionupdate globalblacklist DB_UPDATE id= prefixwhitelistdescriptiondelete globalblacklist DB_DELETE id= opensips-2.2.2/scripts/pi_http/userblacklist-table000066400000000000000000000015471300170765700224070ustar00rootroot00000000000000 userblacklist mysql idDB_INT usernameDB_STR domainDB_STR prefixDB_STR whitelistDB_INT globalblacklist mysql idDB_INT prefixDB_STR whitelistDB_INT descriptionDB_STR opensips-2.2.2/scripts/pi_http/usrloc-mod000066400000000000000000000054111300170765700205310ustar00rootroot00000000000000 location show location DB_QUERY contact_idupdateusernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattradd location DB_INSERT usernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattrupdate location DB_UPDATE contact_id= usernamedomaincontactreceivedpathexpiresqcallidcseqlast_modifiedflagscflagsuser_agentsocketmethodssip_instanceattrdelete location DB_DELETE contact_id= opensips-2.2.2/scripts/pi_http/usrloc-table000066400000000000000000000023511300170765700210410ustar00rootroot00000000000000 location mysql contact_idDB_BIGINT usernameDB_STR domainDB_STR contactDB_STR receivedDB_STR pathDB_STR expiresDB_DATETIME qDB_DOUBLE callidDB_STR cseqDB_INT last_modifiedDB_DATETIME flagsDB_INT cflagsDB_STR user_agentDB_STR socketDB_STR methodsDB_INT sip_instanceDB_STR attrDB_STR opensips-2.2.2/scripts/postgres/000077500000000000000000000000001300170765700167205ustar00rootroot00000000000000opensips-2.2.2/scripts/postgres/acc-create.sql000066400000000000000000000026001300170765700214260ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('acc','7'); CREATE TABLE acc ( id SERIAL PRIMARY KEY NOT NULL, method VARCHAR(16) DEFAULT '' NOT NULL, from_tag VARCHAR(64) DEFAULT '' NOT NULL, to_tag VARCHAR(64) DEFAULT '' NOT NULL, callid VARCHAR(64) DEFAULT '' NOT NULL, sip_code VARCHAR(3) DEFAULT '' NOT NULL, sip_reason VARCHAR(32) DEFAULT '' NOT NULL, time TIMESTAMP WITHOUT TIME ZONE NOT NULL, duration INTEGER DEFAULT 0 NOT NULL, ms_duration INTEGER DEFAULT 0 NOT NULL, setuptime INTEGER DEFAULT 0 NOT NULL, created TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL ); ALTER SEQUENCE acc_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX acc_callid_idx ON acc (callid); INSERT INTO version (table_name, table_version) values ('missed_calls','5'); CREATE TABLE missed_calls ( id SERIAL PRIMARY KEY NOT NULL, method VARCHAR(16) DEFAULT '' NOT NULL, from_tag VARCHAR(64) DEFAULT '' NOT NULL, to_tag VARCHAR(64) DEFAULT '' NOT NULL, callid VARCHAR(64) DEFAULT '' NOT NULL, sip_code VARCHAR(3) DEFAULT '' NOT NULL, sip_reason VARCHAR(32) DEFAULT '' NOT NULL, time TIMESTAMP WITHOUT TIME ZONE NOT NULL, setuptime INTEGER DEFAULT 0 NOT NULL, created TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL ); ALTER SEQUENCE missed_calls_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX missed_calls_callid_idx ON missed_calls (callid); opensips-2.2.2/scripts/postgres/alias_db-create.sql000066400000000000000000000010231300170765700224340ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dbaliases','2'); CREATE TABLE dbaliases ( id SERIAL PRIMARY KEY NOT NULL, alias_username VARCHAR(64) DEFAULT '' NOT NULL, alias_domain VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, CONSTRAINT dbaliases_alias_idx UNIQUE (alias_username, alias_domain) ); ALTER SEQUENCE dbaliases_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX dbaliases_target_idx ON dbaliases (username, domain); opensips-2.2.2/scripts/postgres/auth_db-create.sql000066400000000000000000000011721300170765700223110ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('subscriber','7'); CREATE TABLE subscriber ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, password VARCHAR(25) DEFAULT '' NOT NULL, email_address VARCHAR(64) DEFAULT '' NOT NULL, ha1 VARCHAR(64) DEFAULT '' NOT NULL, ha1b VARCHAR(64) DEFAULT '' NOT NULL, rpid VARCHAR(64) DEFAULT NULL, CONSTRAINT subscriber_account_idx UNIQUE (username, domain) ); ALTER SEQUENCE subscriber_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX subscriber_username_idx ON subscriber (username); opensips-2.2.2/scripts/postgres/avpops-create.sql000066400000000000000000000014261300170765700222150ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('usr_preferences','3'); CREATE TABLE usr_preferences ( id SERIAL PRIMARY KEY NOT NULL, uuid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(128) DEFAULT 0 NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, attribute VARCHAR(32) DEFAULT '' NOT NULL, type INTEGER DEFAULT 0 NOT NULL, value VARCHAR(128) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL ); ALTER SEQUENCE usr_preferences_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX usr_preferences_ua_idx ON usr_preferences (uuid, attribute); CREATE INDEX usr_preferences_uda_idx ON usr_preferences (username, domain, attribute); CREATE INDEX usr_preferences_value_idx ON usr_preferences (value); opensips-2.2.2/scripts/postgres/b2b-create.sql000066400000000000000000000037471300170765700213620ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('b2b_entities','1'); CREATE TABLE b2b_entities ( id SERIAL PRIMARY KEY NOT NULL, type INTEGER NOT NULL, state INTEGER NOT NULL, ruri VARCHAR(128), from_uri VARCHAR(128) NOT NULL, to_uri VARCHAR(128) NOT NULL, from_dname VARCHAR(64), to_dname VARCHAR(64), tag0 VARCHAR(64) NOT NULL, tag1 VARCHAR(64), callid VARCHAR(64) NOT NULL, cseq0 INTEGER NOT NULL, cseq1 INTEGER, contact0 VARCHAR(128) NOT NULL, contact1 VARCHAR(128), route0 TEXT, route1 TEXT, sockinfo_srv VARCHAR(64), param VARCHAR(128) NOT NULL, lm INTEGER NOT NULL, lrc INTEGER, lic INTEGER, leg_cseq INTEGER, leg_route TEXT, leg_tag VARCHAR(64), leg_contact VARCHAR(128), leg_sockinfo VARCHAR(128), CONSTRAINT b2b_entities_b2b_entities_idx UNIQUE (type, tag0, tag1, callid) ); ALTER SEQUENCE b2b_entities_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX b2b_entities_b2b_entities_param ON b2b_entities (param); INSERT INTO version (table_name, table_version) values ('b2b_logic','3'); CREATE TABLE b2b_logic ( id SERIAL PRIMARY KEY NOT NULL, si_key VARCHAR(64) NOT NULL, scenario VARCHAR(64), sstate INTEGER NOT NULL, next_sstate INTEGER NOT NULL, sparam0 VARCHAR(64), sparam1 VARCHAR(64), sparam2 VARCHAR(64), sparam3 VARCHAR(64), sparam4 VARCHAR(64), sdp TEXT, lifetime INTEGER DEFAULT 0 NOT NULL, e1_type INTEGER NOT NULL, e1_sid VARCHAR(64), e1_from VARCHAR(128) NOT NULL, e1_to VARCHAR(128) NOT NULL, e1_key VARCHAR(64) NOT NULL, e2_type INTEGER NOT NULL, e2_sid VARCHAR(64), e2_from VARCHAR(128) NOT NULL, e2_to VARCHAR(128) NOT NULL, e2_key VARCHAR(64) NOT NULL, e3_type INTEGER, e3_sid VARCHAR(64), e3_from VARCHAR(128), e3_to VARCHAR(128), e3_key VARCHAR(64), CONSTRAINT b2b_logic_b2b_logic_idx UNIQUE (si_key) ); ALTER SEQUENCE b2b_logic_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/b2b_sca-create.sql000066400000000000000000000052671300170765700222070ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('b2b_sca','1'); CREATE TABLE b2b_sca ( id SERIAL PRIMARY KEY NOT NULL, shared_line VARCHAR(64) NOT NULL, watchers VARCHAR(255) NOT NULL, app1_shared_entity INTEGER DEFAULT NULL, app1_call_state INTEGER DEFAULT NULL, app1_call_info_uri VARCHAR(128) DEFAULT NULL, app1_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app1_b2bl_key VARCHAR(64) DEFAULT NULL, app2_shared_entity INTEGER DEFAULT NULL, app2_call_state INTEGER DEFAULT NULL, app2_call_info_uri VARCHAR(128) DEFAULT NULL, app2_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app2_b2bl_key VARCHAR(64) DEFAULT NULL, app3_shared_entity INTEGER DEFAULT NULL, app3_call_state INTEGER DEFAULT NULL, app3_call_info_uri VARCHAR(128) DEFAULT NULL, app3_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app3_b2bl_key VARCHAR(64) DEFAULT NULL, app4_shared_entity INTEGER DEFAULT NULL, app4_call_state INTEGER DEFAULT NULL, app4_call_info_uri VARCHAR(128) DEFAULT NULL, app4_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app4_b2bl_key VARCHAR(64) DEFAULT NULL, app5_shared_entity INTEGER DEFAULT NULL, app5_call_state INTEGER DEFAULT NULL, app5_call_info_uri VARCHAR(128) DEFAULT NULL, app5_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app5_b2bl_key VARCHAR(64) DEFAULT NULL, app6_shared_entity INTEGER DEFAULT NULL, app6_call_state INTEGER DEFAULT NULL, app6_call_info_uri VARCHAR(128) DEFAULT NULL, app6_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app6_b2bl_key VARCHAR(64) DEFAULT NULL, app7_shared_entity INTEGER DEFAULT NULL, app7_call_state INTEGER DEFAULT NULL, app7_call_info_uri VARCHAR(128) DEFAULT NULL, app7_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app7_b2bl_key VARCHAR(64) DEFAULT NULL, app8_shared_entity INTEGER DEFAULT NULL, app8_call_state INTEGER DEFAULT NULL, app8_call_info_uri VARCHAR(128) DEFAULT NULL, app8_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app8_b2bl_key VARCHAR(64) DEFAULT NULL, app9_shared_entity INTEGER DEFAULT NULL, app9_call_state INTEGER DEFAULT NULL, app9_call_info_uri VARCHAR(128) DEFAULT NULL, app9_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app9_b2bl_key VARCHAR(64) DEFAULT NULL, app10_shared_entity INTEGER DEFAULT NULL, app10_call_state INTEGER DEFAULT NULL, app10_call_info_uri VARCHAR(128) DEFAULT NULL, app10_call_info_appearance_uri VARCHAR(128) DEFAULT NULL, app10_b2bl_key VARCHAR(64) DEFAULT NULL, CONSTRAINT b2b_sca_sca_idx UNIQUE (shared_line) ); ALTER SEQUENCE b2b_sca_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/cachedb_sql-create.sql000066400000000000000000000003721300170765700231340ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cachedb','2'); CREATE TABLE cachedb ( keyname VARCHAR(255) PRIMARY KEY NOT NULL, value TEXT NOT NULL, counter INTEGER DEFAULT 0 NOT NULL, expires INTEGER DEFAULT 0 NOT NULL ); opensips-2.2.2/scripts/postgres/call_center-create.sql000066400000000000000000000042231300170765700231560ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cc_flows','1'); CREATE TABLE cc_flows ( id SERIAL PRIMARY KEY NOT NULL, flowid VARCHAR(64) NOT NULL, priority INTEGER DEFAULT 256 NOT NULL, skill VARCHAR(64) NOT NULL, prependcid VARCHAR(32) NOT NULL, message_welcome VARCHAR(128) DEFAULT NULL, message_queue VARCHAR(128) NOT NULL, CONSTRAINT cc_flows_unique_flowid UNIQUE (flowid) ); ALTER SEQUENCE cc_flows_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('cc_agents','1'); CREATE TABLE cc_agents ( id SERIAL PRIMARY KEY NOT NULL, agentid VARCHAR(128) NOT NULL, location VARCHAR(128) NOT NULL, logstate INTEGER DEFAULT 0 NOT NULL, skills VARCHAR(255) NOT NULL, last_call_end INTEGER DEFAULT 0 NOT NULL, CONSTRAINT cc_agents_unique_agentid UNIQUE (agentid) ); ALTER SEQUENCE cc_agents_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('cc_cdrs','1'); CREATE TABLE cc_cdrs ( id SERIAL PRIMARY KEY NOT NULL, caller VARCHAR(64) NOT NULL, received_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL, wait_time INTEGER DEFAULT 0 NOT NULL, pickup_time INTEGER DEFAULT 0 NOT NULL, talk_time INTEGER DEFAULT 0 NOT NULL, flow_id VARCHAR(128) NOT NULL, agent_id VARCHAR(128) DEFAULT NULL, call_type INTEGER DEFAULT -1 NOT NULL, rejected INTEGER DEFAULT 0 NOT NULL, fstats INTEGER DEFAULT 0 NOT NULL, cid INTEGER DEFAULT 0 ); ALTER SEQUENCE cc_cdrs_id_seq MAXVALUE 2147483647 CYCLE; CREATE TABLE cc_calls ( id SERIAL PRIMARY KEY NOT NULL, state INTEGER NOT NULL, ig_cback INTEGER NOT NULL, no_rej INTEGER NOT NULL, setup_time INTEGER NOT NULL, eta INTEGER NOT NULL, last_start INTEGER NOT NULL, recv_time INTEGER NOT NULL, caller_dn VARCHAR(128) NOT NULL, caller_un VARCHAR(128) NOT NULL, b2buaid VARCHAR(128) DEFAULT '' NOT NULL, flow VARCHAR(128) NOT NULL, agent VARCHAR(128) NOT NULL, CONSTRAINT cc_calls_unique_id UNIQUE (b2buaid) ); ALTER SEQUENCE cc_calls_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX cc_calls_b2buaid_idx ON cc_calls (b2buaid); opensips-2.2.2/scripts/postgres/carrierroute-create.sql000066400000000000000000000030101300170765700234020ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('carrierroute','3'); CREATE TABLE carrierroute ( id SERIAL PRIMARY KEY NOT NULL, carrier INTEGER DEFAULT 0 NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, scan_prefix VARCHAR(64) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, mask INTEGER DEFAULT 0 NOT NULL, prob REAL DEFAULT 0 NOT NULL, strip INTEGER DEFAULT 0 NOT NULL, rewrite_host VARCHAR(128) DEFAULT '' NOT NULL, rewrite_prefix VARCHAR(64) DEFAULT '' NOT NULL, rewrite_suffix VARCHAR(64) DEFAULT '' NOT NULL, description VARCHAR(255) DEFAULT NULL ); ALTER SEQUENCE carrierroute_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('carrierfailureroute','2'); CREATE TABLE carrierfailureroute ( id SERIAL PRIMARY KEY NOT NULL, carrier INTEGER DEFAULT 0 NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, scan_prefix VARCHAR(64) DEFAULT '' NOT NULL, host_name VARCHAR(128) DEFAULT '' NOT NULL, reply_code VARCHAR(3) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, mask INTEGER DEFAULT 0 NOT NULL, next_domain VARCHAR(64) DEFAULT '' NOT NULL, description VARCHAR(255) DEFAULT NULL ); ALTER SEQUENCE carrierfailureroute_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('route_tree','2'); CREATE TABLE route_tree ( id SERIAL PRIMARY KEY NOT NULL, carrier VARCHAR(64) DEFAULT NULL ); ALTER SEQUENCE route_tree_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/closeddial-create.sql000066400000000000000000000013571300170765700230130ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('closeddial','1'); CREATE TABLE closeddial ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, cd_username VARCHAR(64) DEFAULT '' NOT NULL, cd_domain VARCHAR(64) DEFAULT '' NOT NULL, group_id VARCHAR(64) DEFAULT '' NOT NULL, new_uri VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT closeddial_cd_idx1 UNIQUE (username, domain, cd_domain, cd_username, group_id) ); ALTER SEQUENCE closeddial_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX closeddial_cd_idx2 ON closeddial (group_id); CREATE INDEX closeddial_cd_idx3 ON closeddial (cd_username); CREATE INDEX closeddial_cd_idx4 ON closeddial (username); opensips-2.2.2/scripts/postgres/clusterer-create.sql000066400000000000000000000011371300170765700227140ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('clusterer','1'); CREATE TABLE clusterer ( id SERIAL PRIMARY KEY NOT NULL, cluster_id INTEGER NOT NULL, machine_id INTEGER NOT NULL, url VARCHAR(64) NOT NULL, state INTEGER DEFAULT 1 NOT NULL, last_attempt BIGINT DEFAULT 0 NOT NULL, failed_attempts INTEGER DEFAULT 3 NOT NULL, no_tries INTEGER DEFAULT 0 NOT NULL, duration INTEGER DEFAULT 30 NOT NULL, description VARCHAR(64), CONSTRAINT clusterer_clusterer_idx UNIQUE (cluster_id, machine_id) ); ALTER SEQUENCE clusterer_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/cpl-create.sql000066400000000000000000000005401300170765700214570ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cpl','2'); CREATE TABLE cpl ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, cpl_xml TEXT, cpl_bin TEXT, CONSTRAINT cpl_account_idx UNIQUE (username, domain) ); ALTER SEQUENCE cpl_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/dialog-create.sql000066400000000000000000000020071300170765700221400ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dialog','10'); CREATE TABLE dialog ( dlg_id BIGINT PRIMARY KEY NOT NULL, callid VARCHAR(255) NOT NULL, from_uri VARCHAR(128) NOT NULL, from_tag VARCHAR(64) NOT NULL, to_uri VARCHAR(128) NOT NULL, to_tag VARCHAR(64) NOT NULL, mangled_from_uri VARCHAR(64) DEFAULT NULL, mangled_to_uri VARCHAR(64) DEFAULT NULL, caller_cseq VARCHAR(11) NOT NULL, callee_cseq VARCHAR(11) NOT NULL, caller_ping_cseq INTEGER NOT NULL, callee_ping_cseq INTEGER NOT NULL, caller_route_set TEXT, callee_route_set TEXT, caller_contact VARCHAR(128), callee_contact VARCHAR(128), caller_sock VARCHAR(64) NOT NULL, callee_sock VARCHAR(64) NOT NULL, state INTEGER NOT NULL, start_time INTEGER NOT NULL, timeout INTEGER NOT NULL, vars BYTEA DEFAULT NULL, profiles TEXT DEFAULT NULL, script_flags INTEGER DEFAULT 0 NOT NULL, module_flags INTEGER DEFAULT 0 NOT NULL, flags INTEGER DEFAULT 0 NOT NULL ); opensips-2.2.2/scripts/postgres/dialplan-create.sql000066400000000000000000000007521300170765700224720ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dialplan','5'); CREATE TABLE dialplan ( id SERIAL PRIMARY KEY NOT NULL, dpid INTEGER NOT NULL, pr INTEGER NOT NULL, match_op INTEGER NOT NULL, match_exp VARCHAR(64) NOT NULL, match_flags INTEGER NOT NULL, subst_exp VARCHAR(64), repl_exp VARCHAR(32), timerec VARCHAR(255), disabled INTEGER DEFAULT 0 NOT NULL, attrs VARCHAR(32) ); ALTER SEQUENCE dialplan_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/dispatcher-create.sql000066400000000000000000000010311300170765700230230ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dispatcher','7'); CREATE TABLE dispatcher ( id SERIAL PRIMARY KEY NOT NULL, setid INTEGER DEFAULT 0 NOT NULL, destination VARCHAR(192) DEFAULT '' NOT NULL, socket VARCHAR(128) DEFAULT NULL, state INTEGER DEFAULT 0 NOT NULL, weight INTEGER DEFAULT 1 NOT NULL, priority INTEGER DEFAULT 0 NOT NULL, attrs VARCHAR(128) DEFAULT '' NOT NULL, description VARCHAR(64) DEFAULT '' NOT NULL ); ALTER SEQUENCE dispatcher_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/domain-create.sql000066400000000000000000000006251300170765700221540ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('domain','3'); CREATE TABLE domain ( id SERIAL PRIMARY KEY NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, attrs VARCHAR(255) DEFAULT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT domain_domain_idx UNIQUE (domain) ); ALTER SEQUENCE domain_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/domainpolicy-create.sql000066400000000000000000000007321300170765700233730ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('domainpolicy','3'); CREATE TABLE domainpolicy ( id SERIAL PRIMARY KEY NOT NULL, rule VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, att VARCHAR(255), val VARCHAR(128), description VARCHAR(255) NOT NULL, CONSTRAINT domainpolicy_rav_idx UNIQUE (rule, att, val) ); ALTER SEQUENCE domainpolicy_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX domainpolicy_rule_idx ON domainpolicy (rule); opensips-2.2.2/scripts/postgres/drouting-create.sql000066400000000000000000000051021300170765700225330ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dr_gateways','6'); CREATE TABLE dr_gateways ( id SERIAL PRIMARY KEY NOT NULL, gwid VARCHAR(64) NOT NULL, type INTEGER DEFAULT 0 NOT NULL, address VARCHAR(128) NOT NULL, strip INTEGER DEFAULT 0 NOT NULL, pri_prefix VARCHAR(16) DEFAULT NULL, attrs VARCHAR(255) DEFAULT NULL, probe_mode INTEGER DEFAULT 0 NOT NULL, state INTEGER DEFAULT 0 NOT NULL, socket VARCHAR(128) DEFAULT NULL, description VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT dr_gateways_dr_gw_idx UNIQUE (gwid) ); ALTER SEQUENCE dr_gateways_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('dr_rules','3'); CREATE TABLE dr_rules ( ruleid SERIAL PRIMARY KEY NOT NULL, groupid VARCHAR(255) NOT NULL, prefix VARCHAR(64) NOT NULL, timerec VARCHAR(255) NOT NULL, priority INTEGER DEFAULT 0 NOT NULL, routeid VARCHAR(255) DEFAULT NULL, gwlist VARCHAR(255) NOT NULL, attrs VARCHAR(255) DEFAULT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); ALTER SEQUENCE dr_rules_ruleid_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('dr_carriers','2'); CREATE TABLE dr_carriers ( id SERIAL PRIMARY KEY NOT NULL, carrierid VARCHAR(64) NOT NULL, gwlist VARCHAR(255) NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, state INTEGER DEFAULT 0 NOT NULL, attrs VARCHAR(255) DEFAULT '', description VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT dr_carriers_dr_carrier_idx UNIQUE (carrierid) ); ALTER SEQUENCE dr_carriers_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('dr_groups','2'); CREATE TABLE dr_groups ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(128) DEFAULT '' NOT NULL, groupid INTEGER DEFAULT 0 NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); ALTER SEQUENCE dr_groups_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('dr_partitions','1'); CREATE TABLE dr_partitions ( id SERIAL PRIMARY KEY NOT NULL, partition_name VARCHAR(255) NOT NULL, db_url VARCHAR(255) NOT NULL, drd_table VARCHAR(255), drr_table VARCHAR(255), drg_table VARCHAR(255), drc_table VARCHAR(255), ruri_avp VARCHAR(255), gw_id_avp VARCHAR(255), gw_priprefix_avp VARCHAR(255), gw_sock_avp VARCHAR(255), rule_id_avp VARCHAR(255), rule_prefix_avp VARCHAR(255), carrier_id_avp VARCHAR(255) ); ALTER SEQUENCE dr_partitions_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/emergency-create.sql000066400000000000000000000027401300170765700226630ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('emergency_routing','1'); CREATE TABLE emergency_routing ( id SERIAL PRIMARY KEY NOT NULL, selectiveRoutingID VARCHAR(11) NOT NULL, routingESN INTEGER DEFAULT 0 NOT NULL, npa INTEGER DEFAULT 0 NOT NULL, esgwri VARCHAR(50) NOT NULL ); ALTER SEQUENCE emergency_routing_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('emergency_report','1'); CREATE TABLE emergency_report ( id SERIAL PRIMARY KEY NOT NULL, callid VARCHAR(25) NOT NULL, selectiveRoutingID VARCHAR(11) NOT NULL, routingESN INTEGER DEFAULT 0 NOT NULL, npa INTEGER DEFAULT 0 NOT NULL, esgwri VARCHAR(50) NOT NULL, lro VARCHAR(20) NOT NULL, VPC_organizationName VARCHAR(50) NOT NULL, VPC_hostname VARCHAR(50) NOT NULL, VPC_timestamp VARCHAR(30) NOT NULL, result VARCHAR(4) NOT NULL, disposition VARCHAR(10) NOT NULL ); ALTER SEQUENCE emergency_report_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('emergency_service_provider','1'); CREATE TABLE emergency_service_provider ( id SERIAL PRIMARY KEY NOT NULL, organizationName VARCHAR(50) NOT NULL, hostId VARCHAR(30) NOT NULL, nenaId VARCHAR(50) NOT NULL, contact VARCHAR(20) NOT NULL, certUri VARCHAR(50) NOT NULL, nodeIP VARCHAR(20) NOT NULL, attribution INTEGER NOT NULL ); ALTER SEQUENCE emergency_service_provider_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/fraud_detection-create.sql000066400000000000000000000014771300170765700240520ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('fraud_detection','1'); CREATE TABLE fraud_detection ( ruleid SERIAL PRIMARY KEY NOT NULL, profileid INTEGER NOT NULL, prefix VARCHAR(64) NOT NULL, start_hour VARCHAR(5) NOT NULL, end_hour VARCHAR(5) NOT NULL, daysoftheweek VARCHAR(64) NOT NULL, cpm_warning INTEGER NOT NULL, cpm_critical INTEGER NOT NULL, call_duration_warning INTEGER NOT NULL, call_duration_critical INTEGER NOT NULL, total_calls_warning INTEGER NOT NULL, total_calls_critical INTEGER NOT NULL, concurrent_calls_warning INTEGER NOT NULL, concurrent_calls_critical INTEGER NOT NULL, sequential_calls_warning INTEGER NOT NULL, sequential_calls_critical INTEGER NOT NULL ); ALTER SEQUENCE fraud_detection_ruleid_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/group-create.sql000066400000000000000000000014311300170765700220350ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('grp','3'); CREATE TABLE grp ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, grp VARCHAR(64) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT grp_account_group_idx UNIQUE (username, domain, grp) ); ALTER SEQUENCE grp_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('re_grp','2'); CREATE TABLE re_grp ( id SERIAL PRIMARY KEY NOT NULL, reg_exp VARCHAR(128) DEFAULT '' NOT NULL, group_id INTEGER DEFAULT 0 NOT NULL ); ALTER SEQUENCE re_grp_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX re_grp_group_idx ON re_grp (group_id); opensips-2.2.2/scripts/postgres/imc-create.sql000066400000000000000000000013661300170765700214600ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('imc_rooms','2'); CREATE TABLE imc_rooms ( id SERIAL PRIMARY KEY NOT NULL, name VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, flag INTEGER NOT NULL, CONSTRAINT imc_rooms_name_domain_idx UNIQUE (name, domain) ); ALTER SEQUENCE imc_rooms_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('imc_members','2'); CREATE TABLE imc_members ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, room VARCHAR(64) NOT NULL, flag INTEGER NOT NULL, CONSTRAINT imc_members_account_room_idx UNIQUE (username, domain, room) ); ALTER SEQUENCE imc_members_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/load_balancer-create.sql000066400000000000000000000007421300170765700234530ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('load_balancer','2'); CREATE TABLE load_balancer ( id SERIAL PRIMARY KEY NOT NULL, group_id INTEGER DEFAULT 0 NOT NULL, dst_uri VARCHAR(128) NOT NULL, resources VARCHAR(255) NOT NULL, probe_mode INTEGER DEFAULT 0 NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); ALTER SEQUENCE load_balancer_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX load_balancer_dsturi_idx ON load_balancer (dst_uri); opensips-2.2.2/scripts/postgres/msilo-create.sql000066400000000000000000000011461300170765700220270ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('silo','6'); CREATE TABLE silo ( id SERIAL PRIMARY KEY NOT NULL, src_addr VARCHAR(128) DEFAULT '' NOT NULL, dst_addr VARCHAR(128) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, inc_time INTEGER DEFAULT 0 NOT NULL, exp_time INTEGER DEFAULT 0 NOT NULL, snd_time INTEGER DEFAULT 0 NOT NULL, ctype VARCHAR(255) DEFAULT NULL, body BYTEA DEFAULT NULL ); ALTER SEQUENCE silo_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX silo_account_idx ON silo (username, domain); opensips-2.2.2/scripts/postgres/permissions-create.sql000066400000000000000000000007141300170765700232570ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('address','5'); CREATE TABLE address ( id SERIAL PRIMARY KEY NOT NULL, grp SMALLINT DEFAULT 0 NOT NULL, ip VARCHAR(50) NOT NULL, mask SMALLINT DEFAULT 32 NOT NULL, port SMALLINT DEFAULT 0 NOT NULL, proto VARCHAR(4) DEFAULT 'any' NOT NULL, pattern VARCHAR(64) DEFAULT NULL, context_info VARCHAR(32) DEFAULT NULL ); ALTER SEQUENCE address_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/presence-create.sql000066400000000000000000000070121300170765700225060ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('presentity','5'); CREATE TABLE presentity ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, event VARCHAR(64) NOT NULL, etag VARCHAR(64) NOT NULL, expires INTEGER NOT NULL, received_time INTEGER NOT NULL, body BYTEA NOT NULL, extra_hdrs BYTEA DEFAULT '' NOT NULL, sender VARCHAR(128) NOT NULL, CONSTRAINT presentity_presentity_idx UNIQUE (username, domain, event, etag) ); ALTER SEQUENCE presentity_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('active_watchers','11'); CREATE TABLE active_watchers ( id SERIAL PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, to_user VARCHAR(64) NOT NULL, to_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, event_id VARCHAR(64), to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, callid VARCHAR(64) NOT NULL, local_cseq INTEGER NOT NULL, remote_cseq INTEGER NOT NULL, contact VARCHAR(128) NOT NULL, record_route TEXT, expires INTEGER NOT NULL, status INTEGER DEFAULT 2 NOT NULL, reason VARCHAR(64), version INTEGER DEFAULT 0 NOT NULL, socket_info VARCHAR(64) NOT NULL, local_contact VARCHAR(128) NOT NULL, CONSTRAINT active_watchers_active_watchers_idx UNIQUE (presentity_uri, callid, to_tag, from_tag) ); ALTER SEQUENCE active_watchers_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('watchers','4'); CREATE TABLE watchers ( id SERIAL PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, status INTEGER NOT NULL, reason VARCHAR(64), inserted_time INTEGER NOT NULL, CONSTRAINT watchers_watcher_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) ); ALTER SEQUENCE watchers_id_seq MAXVALUE 2147483647 CYCLE; INSERT INTO version (table_name, table_version) values ('xcap','4'); CREATE TABLE xcap ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, doc BYTEA NOT NULL, doc_type INTEGER NOT NULL, etag VARCHAR(64) NOT NULL, source INTEGER NOT NULL, doc_uri VARCHAR(128) NOT NULL, port INTEGER NOT NULL, CONSTRAINT xcap_account_doc_type_idx UNIQUE (username, domain, doc_type, doc_uri) ); ALTER SEQUENCE xcap_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX xcap_source_idx ON xcap (source); INSERT INTO version (table_name, table_version) values ('pua','8'); CREATE TABLE pua ( id SERIAL PRIMARY KEY NOT NULL, pres_uri VARCHAR(128) NOT NULL, pres_id VARCHAR(255) NOT NULL, event INTEGER NOT NULL, expires INTEGER NOT NULL, desired_expires INTEGER NOT NULL, flag INTEGER NOT NULL, etag VARCHAR(64), tuple_id VARCHAR(64), watcher_uri VARCHAR(128), to_uri VARCHAR(128), call_id VARCHAR(64), to_tag VARCHAR(64), from_tag VARCHAR(64), cseq INTEGER, record_route TEXT, contact VARCHAR(128), remote_contact VARCHAR(128), version INTEGER, extra_headers TEXT ); ALTER SEQUENCE pua_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX pua_del1_idx ON pua (pres_uri, event); CREATE INDEX pua_del2_idx ON pua (expires); CREATE INDEX pua_update_idx ON pua (pres_uri, pres_id, flag, event); opensips-2.2.2/scripts/postgres/registrant-create.sql000066400000000000000000000012471300170765700230700ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('registrant','1'); CREATE TABLE registrant ( id SERIAL PRIMARY KEY NOT NULL, registrar VARCHAR(128) DEFAULT '' NOT NULL, proxy VARCHAR(128) DEFAULT NULL, aor VARCHAR(128) DEFAULT '' NOT NULL, third_party_registrant VARCHAR(128) DEFAULT NULL, username VARCHAR(64) DEFAULT NULL, password VARCHAR(64) DEFAULT NULL, binding_URI VARCHAR(128) DEFAULT '' NOT NULL, binding_params VARCHAR(64) DEFAULT NULL, expiry INTEGER DEFAULT NULL, forced_socket VARCHAR(64) DEFAULT NULL, CONSTRAINT registrant_aor_idx UNIQUE (aor) ); ALTER SEQUENCE registrant_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/registrar-create.sql000066400000000000000000000020401300170765700227000ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('aliases','1009'); CREATE TABLE aliases ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, contact VARCHAR(255) DEFAULT '' NOT NULL, received VARCHAR(128) DEFAULT NULL, path VARCHAR(255) DEFAULT NULL, expires TIMESTAMP WITHOUT TIME ZONE DEFAULT '2020-05-28 21:32:15' NOT NULL, q REAL DEFAULT 1.0 NOT NULL, callid VARCHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INTEGER DEFAULT 13 NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, cflags VARCHAR(255) DEFAULT NULL, user_agent VARCHAR(255) DEFAULT '' NOT NULL, socket VARCHAR(64) DEFAULT NULL, methods INTEGER DEFAULT NULL, sip_instance VARCHAR(255) DEFAULT NULL, attr VARCHAR(255) DEFAULT NULL, CONSTRAINT aliases_alias_idx UNIQUE (username, domain, contact, callid) ); ALTER SEQUENCE aliases_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/rls-create.sql000066400000000000000000000032011300170765700214760ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('rls_presentity','1'); CREATE TABLE rls_presentity ( id SERIAL PRIMARY KEY NOT NULL, rlsubs_did VARCHAR(255) NOT NULL, resource_uri VARCHAR(128) NOT NULL, content_type VARCHAR(255) NOT NULL, presence_state BYTEA NOT NULL, expires INTEGER NOT NULL, updated INTEGER NOT NULL, auth_state INTEGER NOT NULL, reason VARCHAR(64) NOT NULL, CONSTRAINT rls_presentity_rls_presentity_idx UNIQUE (rlsubs_did, resource_uri) ); ALTER SEQUENCE rls_presentity_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX rls_presentity_updated_idx ON rls_presentity (updated); INSERT INTO version (table_name, table_version) values ('rls_watchers','2'); CREATE TABLE rls_watchers ( id SERIAL PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, to_user VARCHAR(64) NOT NULL, to_domain VARCHAR(64) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, event_id VARCHAR(64), to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, callid VARCHAR(64) NOT NULL, local_cseq INTEGER NOT NULL, remote_cseq INTEGER NOT NULL, contact VARCHAR(64) NOT NULL, record_route TEXT, expires INTEGER NOT NULL, status INTEGER DEFAULT 2 NOT NULL, reason VARCHAR(64) NOT NULL, version INTEGER DEFAULT 0 NOT NULL, socket_info VARCHAR(64) NOT NULL, local_contact VARCHAR(128) NOT NULL, CONSTRAINT rls_watchers_rls_watcher_idx UNIQUE (presentity_uri, callid, to_tag, from_tag) ); ALTER SEQUENCE rls_watchers_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/rtpproxy-create.sql000066400000000000000000000004301300170765700226060ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('rtpproxy_sockets','0'); CREATE TABLE rtpproxy_sockets ( id SERIAL PRIMARY KEY NOT NULL, rtpproxy_sock TEXT NOT NULL, set_id INTEGER NOT NULL ); ALTER SEQUENCE rtpproxy_sockets_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/siptrace-create.sql000066400000000000000000000020021300170765700225060ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('sip_trace','5'); CREATE TABLE sip_trace ( id SERIAL PRIMARY KEY NOT NULL, time_stamp TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, trace_attrs VARCHAR(128) DEFAULT NULL, msg TEXT NOT NULL, method VARCHAR(32) DEFAULT '' NOT NULL, status VARCHAR(128) DEFAULT NULL, from_proto VARCHAR(5) NOT NULL, from_ip VARCHAR(50) DEFAULT '' NOT NULL, from_port INTEGER NOT NULL, to_proto VARCHAR(5) NOT NULL, to_ip VARCHAR(50) DEFAULT '' NOT NULL, to_port INTEGER NOT NULL, fromtag VARCHAR(64) DEFAULT '' NOT NULL, direction VARCHAR(4) DEFAULT '' NOT NULL ); ALTER SEQUENCE sip_trace_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX sip_trace_trace_attrs_idx ON sip_trace (trace_attrs); CREATE INDEX sip_trace_date_idx ON sip_trace (time_stamp); CREATE INDEX sip_trace_fromip_idx ON sip_trace (from_ip); CREATE INDEX sip_trace_callid_idx ON sip_trace (callid); opensips-2.2.2/scripts/postgres/speeddial-create.sql000066400000000000000000000012231300170765700226320ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('speed_dial','3'); CREATE TABLE speed_dial ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, sd_username VARCHAR(64) DEFAULT '' NOT NULL, sd_domain VARCHAR(64) DEFAULT '' NOT NULL, new_uri VARCHAR(128) DEFAULT '' NOT NULL, fname VARCHAR(64) DEFAULT '' NOT NULL, lname VARCHAR(64) DEFAULT '' NOT NULL, description VARCHAR(64) DEFAULT '' NOT NULL, CONSTRAINT speed_dial_speed_dial_idx UNIQUE (username, domain, sd_domain, sd_username) ); ALTER SEQUENCE speed_dial_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/standard-create.sql000066400000000000000000000002441300170765700225020ustar00rootroot00000000000000CREATE TABLE version ( table_name VARCHAR(32) NOT NULL, table_version INTEGER DEFAULT 0 NOT NULL, CONSTRAINT version_t_name_idx UNIQUE (table_name) ); opensips-2.2.2/scripts/postgres/tls_mgm-create.sql000066400000000000000000000010141300170765700223400ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('tls_mgm','1'); CREATE TABLE tls_mgm ( id VARCHAR(64) PRIMARY KEY NOT NULL, address VARCHAR(64) NOT NULL, type INTEGER NOT NULL, method VARCHAR(16), verify_cert INTEGER, require_cert INTEGER, certificate VARCHAR(255), private_key VARCHAR(255), crl_check_all INTEGER, crl_dir VARCHAR(255), ca_list VARCHAR(255), ca_dir VARCHAR(255), cipher_list VARCHAR(255), dh_params VARCHAR(255), ec_curve VARCHAR(255) ); opensips-2.2.2/scripts/postgres/uri_db-create.sql000066400000000000000000000007251300170765700221520ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('uri','2'); CREATE TABLE uri ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, uri_user VARCHAR(64) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT uri_account_idx UNIQUE (username, domain, uri_user) ); ALTER SEQUENCE uri_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/postgres/userblacklist-create.sql000066400000000000000000000016051300170765700235530ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('userblacklist','2'); CREATE TABLE userblacklist ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, prefix VARCHAR(64) DEFAULT '' NOT NULL, whitelist SMALLINT DEFAULT 0 NOT NULL ); ALTER SEQUENCE userblacklist_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX userblacklist_userblacklist_idx ON userblacklist (username, domain, prefix); INSERT INTO version (table_name, table_version) values ('globalblacklist','2'); CREATE TABLE globalblacklist ( id SERIAL PRIMARY KEY NOT NULL, prefix VARCHAR(64) DEFAULT '' NOT NULL, whitelist SMALLINT DEFAULT 0 NOT NULL, description VARCHAR(255) DEFAULT NULL ); ALTER SEQUENCE globalblacklist_id_seq MAXVALUE 2147483647 CYCLE; CREATE INDEX globalblacklist_globalblacklist_idx ON globalblacklist (prefix); opensips-2.2.2/scripts/postgres/usrloc-create.sql000066400000000000000000000020721300170765700222120ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('location','1011'); CREATE TABLE location ( contact_id BIGSERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, contact VARCHAR(255) DEFAULT '' NOT NULL, received VARCHAR(128) DEFAULT NULL, path VARCHAR(255) DEFAULT NULL, expires TIMESTAMP WITHOUT TIME ZONE DEFAULT '2020-05-28 21:32:15' NOT NULL, q REAL DEFAULT 1.0 NOT NULL, callid VARCHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INTEGER DEFAULT 13 NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, cflags VARCHAR(255) DEFAULT NULL, user_agent VARCHAR(255) DEFAULT '' NOT NULL, socket VARCHAR(64) DEFAULT NULL, methods INTEGER DEFAULT NULL, sip_instance VARCHAR(255) DEFAULT NULL, attr VARCHAR(255) DEFAULT NULL, CONSTRAINT location_account_contact_idx UNIQUE (username, domain, contact, callid) ); ALTER SEQUENCE location_contact_id_seq MAXVALUE 2147483647 CYCLE; opensips-2.2.2/scripts/sqlite/000077500000000000000000000000001300170765700163535ustar00rootroot00000000000000opensips-2.2.2/scripts/sqlite/acc-create.sql000066400000000000000000000022751300170765700210710ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('acc','7'); CREATE TABLE acc ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, method CHAR(16) DEFAULT '' NOT NULL, from_tag CHAR(64) DEFAULT '' NOT NULL, to_tag CHAR(64) DEFAULT '' NOT NULL, callid CHAR(64) DEFAULT '' NOT NULL, sip_code CHAR(3) DEFAULT '' NOT NULL, sip_reason CHAR(32) DEFAULT '' NOT NULL, time DATETIME NOT NULL, duration INTEGER DEFAULT 0 NOT NULL, ms_duration INTEGER DEFAULT 0 NOT NULL, setuptime INTEGER DEFAULT 0 NOT NULL, created DATETIME DEFAULT NULL ); CREATE INDEX acc_callid_idx ON acc (callid); INSERT INTO version (table_name, table_version) values ('missed_calls','5'); CREATE TABLE missed_calls ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, method CHAR(16) DEFAULT '' NOT NULL, from_tag CHAR(64) DEFAULT '' NOT NULL, to_tag CHAR(64) DEFAULT '' NOT NULL, callid CHAR(64) DEFAULT '' NOT NULL, sip_code CHAR(3) DEFAULT '' NOT NULL, sip_reason CHAR(32) DEFAULT '' NOT NULL, time DATETIME NOT NULL, setuptime INTEGER DEFAULT 0 NOT NULL, created DATETIME DEFAULT NULL ); CREATE INDEX missed_calls_callid_idx ON missed_calls (callid); opensips-2.2.2/scripts/sqlite/alias_db-create.sql000066400000000000000000000007351300170765700221000ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dbaliases','2'); CREATE TABLE dbaliases ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, alias_username CHAR(64) DEFAULT '' NOT NULL, alias_domain CHAR(64) DEFAULT '' NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, CONSTRAINT dbaliases_alias_idx UNIQUE (alias_username, alias_domain) ); CREATE INDEX dbaliases_target_idx ON dbaliases (username, domain); opensips-2.2.2/scripts/sqlite/auth_db-create.sql000066400000000000000000000010721300170765700217430ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('subscriber','7'); CREATE TABLE subscriber ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, password CHAR(25) DEFAULT '' NOT NULL, email_address CHAR(64) DEFAULT '' NOT NULL, ha1 CHAR(64) DEFAULT '' NOT NULL, ha1b CHAR(64) DEFAULT '' NOT NULL, rpid CHAR(64) DEFAULT NULL, CONSTRAINT subscriber_account_idx UNIQUE (username, domain) ); CREATE INDEX subscriber_username_idx ON subscriber (username); opensips-2.2.2/scripts/sqlite/avpops-create.sql000066400000000000000000000013051300170765700216440ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('usr_preferences','3'); CREATE TABLE usr_preferences ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, uuid CHAR(64) DEFAULT '' NOT NULL, username CHAR(128) DEFAULT 0 NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, attribute CHAR(32) DEFAULT '' NOT NULL, type INTEGER DEFAULT 0 NOT NULL, value CHAR(128) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL ); CREATE INDEX usr_preferences_ua_idx ON usr_preferences (uuid, attribute); CREATE INDEX usr_preferences_uda_idx ON usr_preferences (username, domain, attribute); CREATE INDEX usr_preferences_value_idx ON usr_preferences (value); opensips-2.2.2/scripts/sqlite/b2b-create.sql000066400000000000000000000034441300170765700210070ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('b2b_entities','1'); CREATE TABLE b2b_entities ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, type INTEGER NOT NULL, state INTEGER NOT NULL, ruri CHAR(128), from_uri CHAR(128) NOT NULL, to_uri CHAR(128) NOT NULL, from_dname CHAR(64), to_dname CHAR(64), tag0 CHAR(64) NOT NULL, tag1 CHAR(64), callid CHAR(64) NOT NULL, cseq0 INTEGER NOT NULL, cseq1 INTEGER, contact0 CHAR(128) NOT NULL, contact1 CHAR(128), route0 TEXT, route1 TEXT, sockinfo_srv CHAR(64), param CHAR(128) NOT NULL, lm INTEGER NOT NULL, lrc INTEGER, lic INTEGER, leg_cseq INTEGER, leg_route TEXT, leg_tag CHAR(64), leg_contact CHAR(128), leg_sockinfo CHAR(128), CONSTRAINT b2b_entities_b2b_entities_idx UNIQUE (type, tag0, tag1, callid) ); CREATE INDEX ORA_b2b_entities_param ON b2b_entities (param); INSERT INTO version (table_name, table_version) values ('b2b_logic','3'); CREATE TABLE b2b_logic ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, si_key CHAR(64) NOT NULL, scenario CHAR(64), sstate INTEGER NOT NULL, next_sstate INTEGER NOT NULL, sparam0 CHAR(64), sparam1 CHAR(64), sparam2 CHAR(64), sparam3 CHAR(64), sparam4 CHAR(64), sdp TEXT(64), lifetime INTEGER DEFAULT 0 NOT NULL, e1_type INTEGER NOT NULL, e1_sid CHAR(64), e1_from CHAR(128) NOT NULL, e1_to CHAR(128) NOT NULL, e1_key CHAR(64) NOT NULL, e2_type INTEGER NOT NULL, e2_sid CHAR(64), e2_from CHAR(128) NOT NULL, e2_to CHAR(128) NOT NULL, e2_key CHAR(64) NOT NULL, e3_type INTEGER, e3_sid CHAR(64), e3_from CHAR(128), e3_to CHAR(128), e3_key CHAR(64), CONSTRAINT b2b_logic_b2b_logic_idx UNIQUE (si_key) ); opensips-2.2.2/scripts/sqlite/b2b_sca-create.sql000066400000000000000000000050561300170765700216360ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('b2b_sca','1'); CREATE TABLE b2b_sca ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, shared_line CHAR(64) NOT NULL, watchers CHAR(255) NOT NULL, app1_shared_entity INTEGER DEFAULT NULL, app1_call_state INTEGER DEFAULT NULL, app1_call_info_uri CHAR(128) DEFAULT NULL, app1_call_info_appearance_uri CHAR(128) DEFAULT NULL, app1_b2bl_key CHAR(64) DEFAULT NULL, app2_shared_entity INTEGER DEFAULT NULL, app2_call_state INTEGER DEFAULT NULL, app2_call_info_uri CHAR(128) DEFAULT NULL, app2_call_info_appearance_uri CHAR(128) DEFAULT NULL, app2_b2bl_key CHAR(64) DEFAULT NULL, app3_shared_entity INTEGER DEFAULT NULL, app3_call_state INTEGER DEFAULT NULL, app3_call_info_uri CHAR(128) DEFAULT NULL, app3_call_info_appearance_uri CHAR(128) DEFAULT NULL, app3_b2bl_key CHAR(64) DEFAULT NULL, app4_shared_entity INTEGER DEFAULT NULL, app4_call_state INTEGER DEFAULT NULL, app4_call_info_uri CHAR(128) DEFAULT NULL, app4_call_info_appearance_uri CHAR(128) DEFAULT NULL, app4_b2bl_key CHAR(64) DEFAULT NULL, app5_shared_entity INTEGER DEFAULT NULL, app5_call_state INTEGER DEFAULT NULL, app5_call_info_uri CHAR(128) DEFAULT NULL, app5_call_info_appearance_uri CHAR(128) DEFAULT NULL, app5_b2bl_key CHAR(64) DEFAULT NULL, app6_shared_entity INTEGER DEFAULT NULL, app6_call_state INTEGER DEFAULT NULL, app6_call_info_uri CHAR(128) DEFAULT NULL, app6_call_info_appearance_uri CHAR(128) DEFAULT NULL, app6_b2bl_key CHAR(64) DEFAULT NULL, app7_shared_entity INTEGER DEFAULT NULL, app7_call_state INTEGER DEFAULT NULL, app7_call_info_uri CHAR(128) DEFAULT NULL, app7_call_info_appearance_uri CHAR(128) DEFAULT NULL, app7_b2bl_key CHAR(64) DEFAULT NULL, app8_shared_entity INTEGER DEFAULT NULL, app8_call_state INTEGER DEFAULT NULL, app8_call_info_uri CHAR(128) DEFAULT NULL, app8_call_info_appearance_uri CHAR(128) DEFAULT NULL, app8_b2bl_key CHAR(64) DEFAULT NULL, app9_shared_entity INTEGER DEFAULT NULL, app9_call_state INTEGER DEFAULT NULL, app9_call_info_uri CHAR(128) DEFAULT NULL, app9_call_info_appearance_uri CHAR(128) DEFAULT NULL, app9_b2bl_key CHAR(64) DEFAULT NULL, app10_shared_entity INTEGER DEFAULT NULL, app10_call_state INTEGER DEFAULT NULL, app10_call_info_uri CHAR(128) DEFAULT NULL, app10_call_info_appearance_uri CHAR(128) DEFAULT NULL, app10_b2bl_key CHAR(64) DEFAULT NULL, CONSTRAINT b2b_sca_sca_idx UNIQUE (shared_line) ); opensips-2.2.2/scripts/sqlite/cachedb_sql-create.sql000066400000000000000000000003741300170765700225710ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cachedb','2'); CREATE TABLE cachedb ( keyname CHAR(255) PRIMARY KEY NOT NULL, value TEXT(512) NOT NULL, counter INTEGER DEFAULT 0 NOT NULL, expires INTEGER DEFAULT 0 NOT NULL ); opensips-2.2.2/scripts/sqlite/call_center-create.sql000066400000000000000000000036501300170765700226140ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cc_flows','1'); CREATE TABLE cc_flows ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, flowid CHAR(64) NOT NULL, priority INTEGER DEFAULT 256 NOT NULL, skill CHAR(64) NOT NULL, prependcid CHAR(32) NOT NULL, message_welcome CHAR(128) DEFAULT NULL, message_queue CHAR(128) NOT NULL, CONSTRAINT cc_flows_unique_flowid UNIQUE (flowid) ); INSERT INTO version (table_name, table_version) values ('cc_agents','1'); CREATE TABLE cc_agents ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, agentid CHAR(128) NOT NULL, location CHAR(128) NOT NULL, logstate INTEGER DEFAULT 0 NOT NULL, skills CHAR(255) NOT NULL, last_call_end INTEGER DEFAULT 0 NOT NULL, CONSTRAINT cc_agents_unique_agentid UNIQUE (agentid) ); INSERT INTO version (table_name, table_version) values ('cc_cdrs','1'); CREATE TABLE cc_cdrs ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, caller CHAR(64) NOT NULL, received_timestamp DATETIME NOT NULL, wait_time INTEGER DEFAULT 0 NOT NULL, pickup_time INTEGER DEFAULT 0 NOT NULL, talk_time INTEGER DEFAULT 0 NOT NULL, flow_id CHAR(128) NOT NULL, agent_id CHAR(128) DEFAULT NULL, call_type INTEGER DEFAULT -1 NOT NULL, rejected INTEGER DEFAULT 0 NOT NULL, fstats INTEGER DEFAULT 0 NOT NULL, cid INTEGER DEFAULT 0 ); CREATE TABLE cc_calls ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, state INTEGER NOT NULL, ig_cback INTEGER NOT NULL, no_rej INTEGER NOT NULL, setup_time INTEGER NOT NULL, eta INTEGER NOT NULL, last_start INTEGER NOT NULL, recv_time INTEGER NOT NULL, caller_dn CHAR(128) NOT NULL, caller_un CHAR(128) NOT NULL, b2buaid CHAR(128) DEFAULT '' NOT NULL, flow CHAR(128) NOT NULL, agent CHAR(128) NOT NULL, CONSTRAINT cc_calls_unique_id UNIQUE (b2buaid) ); CREATE INDEX cc_calls_b2buaid_idx ON cc_calls (b2buaid); opensips-2.2.2/scripts/sqlite/carrierroute-create.sql000066400000000000000000000025201300170765700230420ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('carrierroute','3'); CREATE TABLE carrierroute ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, carrier INTEGER DEFAULT 0 NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, scan_prefix CHAR(64) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, mask INTEGER DEFAULT 0 NOT NULL, prob FLOAT DEFAULT 0 NOT NULL, strip INTEGER DEFAULT 0 NOT NULL, rewrite_host CHAR(128) DEFAULT '' NOT NULL, rewrite_prefix CHAR(64) DEFAULT '' NOT NULL, rewrite_suffix CHAR(64) DEFAULT '' NOT NULL, description CHAR(255) DEFAULT NULL ); INSERT INTO version (table_name, table_version) values ('carrierfailureroute','2'); CREATE TABLE carrierfailureroute ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, carrier INTEGER DEFAULT 0 NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, scan_prefix CHAR(64) DEFAULT '' NOT NULL, host_name CHAR(128) DEFAULT '' NOT NULL, reply_code CHAR(3) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, mask INTEGER DEFAULT 0 NOT NULL, next_domain CHAR(64) DEFAULT '' NOT NULL, description CHAR(255) DEFAULT NULL ); INSERT INTO version (table_name, table_version) values ('route_tree','2'); CREATE TABLE route_tree ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, carrier CHAR(64) DEFAULT NULL ); opensips-2.2.2/scripts/sqlite/closeddial-create.sql000066400000000000000000000012641300170765700224430ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('closeddial','1'); CREATE TABLE closeddial ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, cd_username CHAR(64) DEFAULT '' NOT NULL, cd_domain CHAR(64) DEFAULT '' NOT NULL, group_id CHAR(64) DEFAULT '' NOT NULL, new_uri CHAR(128) DEFAULT '' NOT NULL, CONSTRAINT closeddial_cd_idx1 UNIQUE (username, domain, cd_domain, cd_username, group_id) ); CREATE INDEX closeddial_cd_idx2 ON closeddial (group_id); CREATE INDEX closeddial_cd_idx3 ON closeddial (cd_username); CREATE INDEX closeddial_cd_idx4 ON closeddial (username); opensips-2.2.2/scripts/sqlite/clusterer-create.sql000066400000000000000000000010621300170765700223440ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('clusterer','1'); CREATE TABLE clusterer ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, cluster_id INTEGER NOT NULL, machine_id INTEGER NOT NULL, url CHAR(64) NOT NULL, state INTEGER DEFAULT 1 NOT NULL, last_attempt BIGINT(64) DEFAULT 0 NOT NULL, failed_attempts INTEGER DEFAULT 3 NOT NULL, no_tries INTEGER DEFAULT 0 NOT NULL, duration INTEGER DEFAULT 30 NOT NULL, description CHAR(64), CONSTRAINT clusterer_clusterer_idx UNIQUE (cluster_id, machine_id) ); opensips-2.2.2/scripts/sqlite/cpl-create.sql000066400000000000000000000004651300170765700211200ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('cpl','2'); CREATE TABLE cpl ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, cpl_xml TEXT, cpl_bin TEXT, CONSTRAINT cpl_account_idx UNIQUE (username, domain) ); opensips-2.2.2/scripts/sqlite/dialog-create.sql000066400000000000000000000017701300170765700216010ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dialog','10'); CREATE TABLE dialog ( dlg_id BIGINT(10) PRIMARY KEY NOT NULL, callid CHAR(255) NOT NULL, from_uri CHAR(128) NOT NULL, from_tag CHAR(64) NOT NULL, to_uri CHAR(128) NOT NULL, to_tag CHAR(64) NOT NULL, mangled_from_uri CHAR(64) DEFAULT NULL, mangled_to_uri CHAR(64) DEFAULT NULL, caller_cseq CHAR(11) NOT NULL, callee_cseq CHAR(11) NOT NULL, caller_ping_cseq INTEGER NOT NULL, callee_ping_cseq INTEGER NOT NULL, caller_route_set TEXT(512), callee_route_set TEXT(512), caller_contact CHAR(128), callee_contact CHAR(128), caller_sock CHAR(64) NOT NULL, callee_sock CHAR(64) NOT NULL, state INTEGER NOT NULL, start_time INTEGER NOT NULL, timeout INTEGER NOT NULL, vars BLOB(4096) DEFAULT NULL, profiles TEXT(512) DEFAULT NULL, script_flags INTEGER DEFAULT 0 NOT NULL, module_flags INTEGER DEFAULT 0 NOT NULL, flags INTEGER DEFAULT 0 NOT NULL ); opensips-2.2.2/scripts/sqlite/dialplan-create.sql000066400000000000000000000006601300170765700221230ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dialplan','5'); CREATE TABLE dialplan ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, dpid INTEGER NOT NULL, pr INTEGER NOT NULL, match_op INTEGER NOT NULL, match_exp CHAR(64) NOT NULL, match_flags INTEGER NOT NULL, subst_exp CHAR(64), repl_exp CHAR(32), timerec CHAR(255), disabled INTEGER DEFAULT 0 NOT NULL, attrs CHAR(32) ); opensips-2.2.2/scripts/sqlite/dispatcher-create.sql000066400000000000000000000007401300170765700224640ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dispatcher','7'); CREATE TABLE dispatcher ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, setid INTEGER DEFAULT 0 NOT NULL, destination CHAR(192) DEFAULT '' NOT NULL, socket CHAR(128) DEFAULT NULL, state INTEGER DEFAULT 0 NOT NULL, weight INTEGER DEFAULT 1 NOT NULL, priority INTEGER DEFAULT 0 NOT NULL, attrs CHAR(128) DEFAULT '' NOT NULL, description CHAR(64) DEFAULT '' NOT NULL ); opensips-2.2.2/scripts/sqlite/domain-create.sql000066400000000000000000000005241300170765700216050ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('domain','3'); CREATE TABLE domain ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, attrs CHAR(255) DEFAULT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT domain_domain_idx UNIQUE (domain) ); opensips-2.2.2/scripts/sqlite/domainpolicy-create.sql000066400000000000000000000006361300170765700230310ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('domainpolicy','3'); CREATE TABLE domainpolicy ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, rule CHAR(255) NOT NULL, type CHAR(255) NOT NULL, att CHAR(255), val CHAR(128), description CHAR(255) NOT NULL, CONSTRAINT domainpolicy_rav_idx UNIQUE (rule, att, val) ); CREATE INDEX domainpolicy_rule_idx ON domainpolicy (rule); opensips-2.2.2/scripts/sqlite/drouting-create.sql000066400000000000000000000043721300170765700221760ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('dr_gateways','6'); CREATE TABLE dr_gateways ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, gwid CHAR(64) NOT NULL, type INTEGER DEFAULT 0 NOT NULL, address CHAR(128) NOT NULL, strip INTEGER DEFAULT 0 NOT NULL, pri_prefix CHAR(16) DEFAULT NULL, attrs CHAR(255) DEFAULT NULL, probe_mode INTEGER DEFAULT 0 NOT NULL, state INTEGER DEFAULT 0 NOT NULL, socket CHAR(128) DEFAULT NULL, description CHAR(128) DEFAULT '' NOT NULL, CONSTRAINT dr_gateways_dr_gw_idx UNIQUE (gwid) ); INSERT INTO version (table_name, table_version) values ('dr_rules','3'); CREATE TABLE dr_rules ( ruleid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, groupid CHAR(255) NOT NULL, prefix CHAR(64) NOT NULL, timerec CHAR(255) NOT NULL, priority INTEGER DEFAULT 0 NOT NULL, routeid CHAR(255) DEFAULT NULL, gwlist CHAR(255) NOT NULL, attrs CHAR(255) DEFAULT NULL, description CHAR(128) DEFAULT '' NOT NULL ); INSERT INTO version (table_name, table_version) values ('dr_carriers','2'); CREATE TABLE dr_carriers ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, carrierid CHAR(64) NOT NULL, gwlist CHAR(255) NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, state INTEGER DEFAULT 0 NOT NULL, attrs CHAR(255) DEFAULT '', description CHAR(128) DEFAULT '' NOT NULL, CONSTRAINT dr_carriers_dr_carrier_idx UNIQUE (carrierid) ); INSERT INTO version (table_name, table_version) values ('dr_groups','2'); CREATE TABLE dr_groups ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) NOT NULL, domain CHAR(128) DEFAULT '' NOT NULL, groupid INTEGER DEFAULT 0 NOT NULL, description CHAR(128) DEFAULT '' NOT NULL ); INSERT INTO version (table_name, table_version) values ('dr_partitions','1'); CREATE TABLE dr_partitions ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, partition_name CHAR(255) NOT NULL, db_url CHAR(255) NOT NULL, drd_table CHAR(255), drr_table CHAR(255), drg_table CHAR(255), drc_table CHAR(255), ruri_avp CHAR(255), gw_id_avp CHAR(255), gw_priprefix_avp CHAR(255), gw_sock_avp CHAR(255), rule_id_avp CHAR(255), rule_prefix_avp CHAR(255), carrier_id_avp CHAR(255) ); opensips-2.2.2/scripts/sqlite/emergency-create.sql000066400000000000000000000024111300170765700223110ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('emergency_routing','1'); CREATE TABLE emergency_routing ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, selectiveRoutingID CHAR(11) NOT NULL, routingESN INTEGER DEFAULT 0 NOT NULL, npa INTEGER DEFAULT 0 NOT NULL, esgwri CHAR(50) NOT NULL ); INSERT INTO version (table_name, table_version) values ('emergency_report','1'); CREATE TABLE emergency_report ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, callid CHAR(25) NOT NULL, selectiveRoutingID CHAR(11) NOT NULL, routingESN INTEGER DEFAULT 0 NOT NULL, npa INTEGER DEFAULT 0 NOT NULL, esgwri CHAR(50) NOT NULL, lro CHAR(20) NOT NULL, VPC_organizationName CHAR(50) NOT NULL, VPC_hostname CHAR(50) NOT NULL, VPC_timestamp CHAR(30) NOT NULL, result CHAR(4) NOT NULL, disposition CHAR(10) NOT NULL ); INSERT INTO version (table_name, table_version) values ('emergency_service_provider','1'); CREATE TABLE emergency_service_provider ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, organizationName CHAR(50) NOT NULL, hostId CHAR(30) NOT NULL, nenaId CHAR(50) NOT NULL, contact CHAR(20) NOT NULL, certUri CHAR(50) NOT NULL, nodeIP CHAR(20) NOT NULL, attribution INTEGER NOT NULL ); opensips-2.2.2/scripts/sqlite/fraud_detection-create.sql000066400000000000000000000013751300170765700235020ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('fraud_detection','1'); CREATE TABLE fraud_detection ( ruleid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, profileid INTEGER NOT NULL, prefix CHAR(64) NOT NULL, start_hour CHAR(5) NOT NULL, end_hour CHAR(5) NOT NULL, daysoftheweek CHAR(64) NOT NULL, cpm_warning INTEGER NOT NULL, cpm_critical INTEGER NOT NULL, call_duration_warning INTEGER NOT NULL, call_duration_critical INTEGER NOT NULL, total_calls_warning INTEGER NOT NULL, total_calls_critical INTEGER NOT NULL, concurrent_calls_warning INTEGER NOT NULL, concurrent_calls_critical INTEGER NOT NULL, sequential_calls_warning INTEGER NOT NULL, sequential_calls_critical INTEGER NOT NULL ); opensips-2.2.2/scripts/sqlite/group-create.sql000066400000000000000000000012551300170765700214740ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('grp','3'); CREATE TABLE grp ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, grp CHAR(64) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT grp_account_group_idx UNIQUE (username, domain, grp) ); INSERT INTO version (table_name, table_version) values ('re_grp','2'); CREATE TABLE re_grp ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, reg_exp CHAR(128) DEFAULT '' NOT NULL, group_id INTEGER DEFAULT 0 NOT NULL ); CREATE INDEX re_grp_group_idx ON re_grp (group_id); opensips-2.2.2/scripts/sqlite/imc-create.sql000066400000000000000000000012171300170765700211060ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('imc_rooms','2'); CREATE TABLE imc_rooms ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name CHAR(64) NOT NULL, domain CHAR(64) NOT NULL, flag INTEGER NOT NULL, CONSTRAINT imc_rooms_name_domain_idx UNIQUE (name, domain) ); INSERT INTO version (table_name, table_version) values ('imc_members','2'); CREATE TABLE imc_members ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) NOT NULL, domain CHAR(64) NOT NULL, room CHAR(64) NOT NULL, flag INTEGER NOT NULL, CONSTRAINT imc_members_account_room_idx UNIQUE (username, domain, room) ); opensips-2.2.2/scripts/sqlite/load_balancer-create.sql000066400000000000000000000006521300170765700231060ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('load_balancer','2'); CREATE TABLE load_balancer ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT 0 NOT NULL, dst_uri CHAR(128) NOT NULL, resources CHAR(255) NOT NULL, probe_mode INTEGER DEFAULT 0 NOT NULL, description CHAR(128) DEFAULT '' NOT NULL ); CREATE INDEX load_balancer_dsturi_idx ON load_balancer (dst_uri); opensips-2.2.2/scripts/sqlite/msilo-create.sql000066400000000000000000000010601300170765700214550ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('silo','6'); CREATE TABLE silo ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, src_addr CHAR(128) DEFAULT '' NOT NULL, dst_addr CHAR(128) DEFAULT '' NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, inc_time INTEGER DEFAULT 0 NOT NULL, exp_time INTEGER DEFAULT 0 NOT NULL, snd_time INTEGER DEFAULT 0 NOT NULL, ctype CHAR(255) DEFAULT NULL, body BLOB DEFAULT NULL ); CREATE INDEX silo_account_idx ON silo (username, domain); opensips-2.2.2/scripts/sqlite/permissions-create.sql000066400000000000000000000006331300170765700227120ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('address','5'); CREATE TABLE address ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, grp SMALLINT(5) DEFAULT 0 NOT NULL, ip CHAR(50) NOT NULL, mask TINYINT DEFAULT 32 NOT NULL, port SMALLINT(5) DEFAULT 0 NOT NULL, proto CHAR(4) DEFAULT 'any' NOT NULL, pattern CHAR(64) DEFAULT NULL, context_info CHAR(32) DEFAULT NULL ); opensips-2.2.2/scripts/sqlite/presence-create.sql000066400000000000000000000062671300170765700221540ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('presentity','5'); CREATE TABLE presentity ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) NOT NULL, domain CHAR(64) NOT NULL, event CHAR(64) NOT NULL, etag CHAR(64) NOT NULL, expires INTEGER NOT NULL, received_time INTEGER NOT NULL, body BLOB NOT NULL, extra_hdrs BLOB DEFAULT '' NOT NULL, sender CHAR(128) NOT NULL, CONSTRAINT presentity_presentity_idx UNIQUE (username, domain, event, etag) ); INSERT INTO version (table_name, table_version) values ('active_watchers','11'); CREATE TABLE active_watchers ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, presentity_uri CHAR(128) NOT NULL, watcher_username CHAR(64) NOT NULL, watcher_domain CHAR(64) NOT NULL, to_user CHAR(64) NOT NULL, to_domain CHAR(64) NOT NULL, event CHAR(64) DEFAULT 'presence' NOT NULL, event_id CHAR(64), to_tag CHAR(64) NOT NULL, from_tag CHAR(64) NOT NULL, callid CHAR(64) NOT NULL, local_cseq INTEGER NOT NULL, remote_cseq INTEGER NOT NULL, contact CHAR(128) NOT NULL, record_route TEXT, expires INTEGER NOT NULL, status INTEGER DEFAULT 2 NOT NULL, reason CHAR(64), version INTEGER DEFAULT 0 NOT NULL, socket_info CHAR(64) NOT NULL, local_contact CHAR(128) NOT NULL, CONSTRAINT ORA_active_watchers_idx UNIQUE (presentity_uri, callid, to_tag, from_tag) ); INSERT INTO version (table_name, table_version) values ('watchers','4'); CREATE TABLE watchers ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, presentity_uri CHAR(128) NOT NULL, watcher_username CHAR(64) NOT NULL, watcher_domain CHAR(64) NOT NULL, event CHAR(64) DEFAULT 'presence' NOT NULL, status INTEGER NOT NULL, reason CHAR(64), inserted_time INTEGER NOT NULL, CONSTRAINT watchers_watcher_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) ); INSERT INTO version (table_name, table_version) values ('xcap','4'); CREATE TABLE xcap ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) NOT NULL, domain CHAR(64) NOT NULL, doc BLOB NOT NULL, doc_type INTEGER NOT NULL, etag CHAR(64) NOT NULL, source INTEGER NOT NULL, doc_uri CHAR(128) NOT NULL, port INTEGER NOT NULL, CONSTRAINT xcap_account_doc_type_idx UNIQUE (username, domain, doc_type, doc_uri) ); CREATE INDEX xcap_source_idx ON xcap (source); INSERT INTO version (table_name, table_version) values ('pua','8'); CREATE TABLE pua ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, pres_uri CHAR(128) NOT NULL, pres_id CHAR(255) NOT NULL, event INTEGER NOT NULL, expires INTEGER NOT NULL, desired_expires INTEGER NOT NULL, flag INTEGER NOT NULL, etag CHAR(64), tuple_id CHAR(64), watcher_uri CHAR(128), to_uri CHAR(128), call_id CHAR(64), to_tag CHAR(64), from_tag CHAR(64), cseq INTEGER, record_route TEXT, contact CHAR(128), remote_contact CHAR(128), version INTEGER, extra_headers TEXT ); CREATE INDEX pua_del1_idx ON pua (pres_uri, event); CREATE INDEX pua_del2_idx ON pua (expires); CREATE INDEX pua_update_idx ON pua (pres_uri, pres_id, flag, event); opensips-2.2.2/scripts/sqlite/registrant-create.sql000066400000000000000000000011401300170765700225130ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('registrant','1'); CREATE TABLE registrant ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, registrar CHAR(128) DEFAULT '' NOT NULL, proxy CHAR(128) DEFAULT NULL, aor CHAR(128) DEFAULT '' NOT NULL, third_party_registrant CHAR(128) DEFAULT NULL, username CHAR(64) DEFAULT NULL, password CHAR(64) DEFAULT NULL, binding_URI CHAR(128) DEFAULT '' NOT NULL, binding_params CHAR(64) DEFAULT NULL, expiry INTEGER DEFAULT NULL, forced_socket CHAR(64) DEFAULT NULL, CONSTRAINT registrant_aor_idx UNIQUE (aor) ); opensips-2.2.2/scripts/sqlite/registrar-create.sql000066400000000000000000000016671300170765700223510ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('aliases','1009'); CREATE TABLE aliases ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, contact CHAR(255) DEFAULT '' NOT NULL, received CHAR(128) DEFAULT NULL, path CHAR(255) DEFAULT NULL, expires DATETIME DEFAULT '2020-05-28 21:32:15' NOT NULL, q FLOAT(10,2) DEFAULT 1.0 NOT NULL, callid CHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INTEGER DEFAULT 13 NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, cflags CHAR(255) DEFAULT NULL, user_agent CHAR(255) DEFAULT '' NOT NULL, socket CHAR(64) DEFAULT NULL, methods INTEGER DEFAULT NULL, sip_instance CHAR(255) DEFAULT NULL, attr CHAR(255) DEFAULT NULL, CONSTRAINT aliases_alias_idx UNIQUE (username, domain, contact, callid) ); opensips-2.2.2/scripts/sqlite/rls-create.sql000066400000000000000000000027421300170765700211420ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('rls_presentity','1'); CREATE TABLE rls_presentity ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, rlsubs_did CHAR(255) NOT NULL, resource_uri CHAR(128) NOT NULL, content_type CHAR(255) NOT NULL, presence_state BLOB NOT NULL, expires INTEGER NOT NULL, updated INTEGER NOT NULL, auth_state INTEGER NOT NULL, reason CHAR(64) NOT NULL, CONSTRAINT ORA_rls_presentity_idx UNIQUE (rlsubs_did, resource_uri) ); CREATE INDEX rls_presentity_updated_idx ON rls_presentity (updated); INSERT INTO version (table_name, table_version) values ('rls_watchers','2'); CREATE TABLE rls_watchers ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, presentity_uri CHAR(128) NOT NULL, to_user CHAR(64) NOT NULL, to_domain CHAR(64) NOT NULL, watcher_username CHAR(64) NOT NULL, watcher_domain CHAR(64) NOT NULL, event CHAR(64) DEFAULT 'presence' NOT NULL, event_id CHAR(64), to_tag CHAR(64) NOT NULL, from_tag CHAR(64) NOT NULL, callid CHAR(64) NOT NULL, local_cseq INTEGER NOT NULL, remote_cseq INTEGER NOT NULL, contact CHAR(64) NOT NULL, record_route TEXT, expires INTEGER NOT NULL, status INTEGER DEFAULT 2 NOT NULL, reason CHAR(64) NOT NULL, version INTEGER DEFAULT 0 NOT NULL, socket_info CHAR(64) NOT NULL, local_contact CHAR(128) NOT NULL, CONSTRAINT rls_watchers_rls_watcher_idx UNIQUE (presentity_uri, callid, to_tag, from_tag) ); opensips-2.2.2/scripts/sqlite/rtpproxy-create.sql000066400000000000000000000003451300170765700222460ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('rtpproxy_sockets','0'); CREATE TABLE rtpproxy_sockets ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, rtpproxy_sock TEXT NOT NULL, set_id INTEGER NOT NULL ); opensips-2.2.2/scripts/sqlite/siptrace-create.sql000066400000000000000000000016511300170765700221520ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('sip_trace','5'); CREATE TABLE sip_trace ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, time_stamp DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, callid CHAR(255) DEFAULT '' NOT NULL, trace_attrs CHAR(128) DEFAULT NULL, msg TEXT NOT NULL, method CHAR(32) DEFAULT '' NOT NULL, status CHAR(128) DEFAULT NULL, from_proto CHAR(5) NOT NULL, from_ip CHAR(50) DEFAULT '' NOT NULL, from_port INTEGER NOT NULL, to_proto CHAR(5) NOT NULL, to_ip CHAR(50) DEFAULT '' NOT NULL, to_port INTEGER NOT NULL, fromtag CHAR(64) DEFAULT '' NOT NULL, direction CHAR(4) DEFAULT '' NOT NULL ); CREATE INDEX sip_trace_trace_attrs_idx ON sip_trace (trace_attrs); CREATE INDEX sip_trace_date_idx ON sip_trace (time_stamp); CREATE INDEX sip_trace_fromip_idx ON sip_trace (from_ip); CREATE INDEX sip_trace_callid_idx ON sip_trace (callid); opensips-2.2.2/scripts/sqlite/speeddial-create.sql000066400000000000000000000011171300170765700222670ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('speed_dial','3'); CREATE TABLE speed_dial ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, sd_username CHAR(64) DEFAULT '' NOT NULL, sd_domain CHAR(64) DEFAULT '' NOT NULL, new_uri CHAR(128) DEFAULT '' NOT NULL, fname CHAR(64) DEFAULT '' NOT NULL, lname CHAR(64) DEFAULT '' NOT NULL, description CHAR(64) DEFAULT '' NOT NULL, CONSTRAINT speed_dial_speed_dial_idx UNIQUE (username, domain, sd_domain, sd_username) ); opensips-2.2.2/scripts/sqlite/standard-create.sql000066400000000000000000000002421300170765700221330ustar00rootroot00000000000000CREATE TABLE version ( table_name CHAR(32) NOT NULL, table_version INTEGER DEFAULT 0 NOT NULL, CONSTRAINT version_t_name_idx UNIQUE (table_name) ); opensips-2.2.2/scripts/sqlite/tls_mgm-create.sql000066400000000000000000000007531300170765700220040ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('tls_mgm','1'); CREATE TABLE tls_mgm ( id CHAR(64) PRIMARY KEY NOT NULL, address CHAR(64) NOT NULL, type INTEGER NOT NULL, method CHAR(16), verify_cert INTEGER, require_cert INTEGER, certificate CHAR(255), private_key CHAR(255), crl_check_all INTEGER, crl_dir CHAR(255), ca_list CHAR(255), ca_dir CHAR(255), cipher_list CHAR(255), dh_params CHAR(255), ec_curve CHAR(255) ); opensips-2.2.2/scripts/sqlite/uri_db-create.sql000066400000000000000000000006241300170765700216030ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('uri','2'); CREATE TABLE uri ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, uri_user CHAR(64) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT uri_account_idx UNIQUE (username, domain, uri_user) ); opensips-2.2.2/scripts/sqlite/userblacklist-create.sql000066400000000000000000000014041300170765700232030ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('userblacklist','2'); CREATE TABLE userblacklist ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT '' NOT NULL, prefix CHAR(64) DEFAULT '' NOT NULL, whitelist TINYINT(1) DEFAULT 0 NOT NULL ); CREATE INDEX ORA_userblacklist_idx ON userblacklist (username, domain, prefix); INSERT INTO version (table_name, table_version) values ('globalblacklist','2'); CREATE TABLE globalblacklist ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, prefix CHAR(64) DEFAULT '' NOT NULL, whitelist TINYINT(1) DEFAULT 0 NOT NULL, description CHAR(255) DEFAULT NULL ); CREATE INDEX ORA_globalblacklist_idx ON globalblacklist (prefix); opensips-2.2.2/scripts/sqlite/usrloc-create.sql000066400000000000000000000017071300170765700216510ustar00rootroot00000000000000INSERT INTO version (table_name, table_version) values ('location','1011'); CREATE TABLE location ( contact_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username CHAR(64) DEFAULT '' NOT NULL, domain CHAR(64) DEFAULT NULL, contact CHAR(255) DEFAULT '' NOT NULL, received CHAR(128) DEFAULT NULL, path CHAR(255) DEFAULT NULL, expires DATETIME DEFAULT '2020-05-28 21:32:15' NOT NULL, q FLOAT(10,2) DEFAULT 1.0 NOT NULL, callid CHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INTEGER DEFAULT 13 NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, cflags CHAR(255) DEFAULT NULL, user_agent CHAR(255) DEFAULT '' NOT NULL, socket CHAR(64) DEFAULT NULL, methods INTEGER DEFAULT NULL, sip_instance CHAR(255) DEFAULT NULL, attr CHAR(255) DEFAULT NULL, CONSTRAINT location_account_contact_idx UNIQUE (username, domain, contact, callid) ); opensips-2.2.2/serialize.c000066400000000000000000000221101300170765700155120ustar00rootroot00000000000000/* * Copyright (C) 2005 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2005-11-29 splitted from lcr module (bogdan) */ /*! * \file * \brief Sequential forking implementation */ #include "str.h" #include "qvalue.h" #include "usr_avp.h" #include "dset.h" #include "action.h" #include "route.h" #include "parser/msg_parser.h" #include "parser/parse_rr.h" #include "mem/mem.h" struct serial_contact { str enc_info; qvalue_t q; unsigned short q_flag; int next; }; #define Q_FLAG (1<<4) /*!< usr_avp flag for sequential forking */ #define SERIAL_AVP_ALIAS "serial_branch" /*!< avp alias to be used */ #define SERIAL_AVL_ID 0xff3434 /*!< avp ID of serial AVP */ static int serial_avp; int init_serialization(void) { str alias = { SERIAL_AVP_ALIAS, sizeof(SERIAL_AVP_ALIAS)-1 }; if (parse_avp_spec(&alias, &serial_avp)) { LM_ERR("cannot parse avp spec\n"); return -1; } return 0; } /*! \brief * Loads contacts in destination set into "serial_avp" AVP in reverse * priority order and associated each contact with Q_FLAG telling if * contact is the last one in its priority class. Finally, removes * all branches from destination set. */ int serialize_branches(struct sip_msg *msg, int clean_before ) { static struct serial_contact contacts[MAX_BRANCHES]; int n, last, first, i, prev; str branch, *ruri; qvalue_t q, ruri_q; char *p; str dst_uri, path, enc_info; unsigned int flags; struct socket_info *sock_info; int_str val; int idx; /* Check if anything needs to be done */ if (get_nr_branches() == 0) { LM_DBG("nothing to do - no branches!\n"); return 0; } ruri = GET_RURI(msg); ruri_q = get_ruri_q(msg); flags = getb0flags(msg); for (idx = 0; (branch.s = get_branch(idx,&branch.len,&q,0,0,0,0)); idx++) { if (q != ruri_q) break; } if (branch.s == 0) { LM_DBG("nothing to do - all same q!\n"); return 0; } /* reset contact array */ n = 0; /* Insert Request-URI to contact list */ enc_info.len = 3 * sizeof(long) + ruri->len + msg->dst_uri.len + msg->path_vec.len + 3; enc_info.s = (char*) pkg_malloc (enc_info.len); if (!enc_info.s) { LM_ERR("no pkg memory left\n"); goto error; /* nothing to free here */ } memset(enc_info.s, 0, enc_info.len); p = enc_info.s; LM_DBG("Msg information <%.*s,%.*s,%.*s,%d,%u>\n", ruri->len, ruri->s, msg->dst_uri.len, msg->dst_uri.s, msg->path_vec.len, msg->path_vec.s, ruri_q, flags); *((long*) p) = (long)msg->force_send_socket; p += sizeof(long); *((long*) p) = (long)flags; p += sizeof(long); *((long*) p) = (long)ruri_q; p += sizeof(long); memcpy(p , ruri->s, ruri->len); p += ruri->len + 1; memcpy(p, msg->dst_uri.s, msg->dst_uri.len); p += msg->dst_uri.len + 1; memcpy(p, msg->path_vec.s, msg->path_vec.len); contacts[n].enc_info = enc_info; contacts[n].q = ruri_q; contacts[n].next = -1; last = n; first = n; n++; /* Insert branch URIs to contact list in increasing q order */ for (idx = 0;(branch.s = get_branch(idx, &branch.len, &q, &dst_uri, &path, &flags, &sock_info)); idx++){ enc_info.len = 3 * sizeof(long) + branch.len + dst_uri.len + path.len + 3; enc_info.s = (char*) pkg_malloc (enc_info.len); if (!enc_info.s) { LM_ERR("no pkg memory left\n"); goto error_free; } memset(enc_info.s, 0, enc_info.len); p = enc_info.s; LM_DBG("Branch information <%.*s,%.*s,%.*s,%d,%u>\n", branch.len, branch.s, dst_uri.len, dst_uri.s, path.len, path.s, q, flags); *((long*) p) = (long)sock_info; p += sizeof(long); *((long*) p) = (long)flags; p += sizeof(long); *((long*) p) = (long)q; p += sizeof(long); memcpy(p , branch.s, branch.len); p += branch.len + 1; memcpy(p, dst_uri.s, dst_uri.len); p += dst_uri.len + 1; memcpy(p, path.s, path.len); contacts[n].enc_info = enc_info; contacts[n].q = q; /* insert based on q */ for (i = first, prev=-1; i != -1 && contacts[i].q < q; prev=i,i = contacts[i].next); if (i == -1) { /* append */ last = contacts[last].next = n; contacts[n].next = -1; } else { if (i == first) { /* first element */ contacts[n].next = first; first = n; } else { /* before pos i */ contacts[n].next = contacts[prev].next; contacts[prev].next = n; } } n++; } /* Assign values for q_flags */ for (i = first; contacts[i].next != -1; i = contacts[i].next) { if (contacts[i].q < contacts[contacts[i].next].q) contacts[contacts[i].next].q_flag = Q_FLAG; else contacts[contacts[i].next].q_flag = 0; } if (clean_before) destroy_avps( 0/*type*/, serial_avp, 1/*all*/); /* Add contacts to "contacts" AVP */ for (i = first; i != -1; i = contacts[i].next) { val.s = contacts[i].enc_info; if (add_avp( AVP_VAL_STR|contacts[i].q_flag, serial_avp, val)) { LM_ERR("failed to add avp\n"); goto error_free; } pkg_free(contacts[i].enc_info.s); contacts[i].enc_info.s = NULL; } /* Clear all branches */ clear_branches(); return 0; error_free: for( i=0 ; iforce_send_socket = sock_info; set_ruri_q( msg, q ); setb0flags( msg, flags ); LM_DBG("Msg information <%.*s,%.*s,%.*s,%d,%u> (avp flag=%u)\n", uri.len, uri.s, dst_uri.len, dst_uri.s, path.len, path.s, q, flags, avp->flags); if (avp->flags & Q_FLAG) { destroy_avp(avp); goto done; } prev = avp; avp = search_next_avp(prev, &val); destroy_avp(prev); /* Append branches until out of branches or Q_FLAG is set */ while (avp != NULL) { if (!val.s.s) { LM_ERR("invalid avp value\n"); continue; } p = val.s.s; sock_info = (struct socket_info*) *((long*) p); p += sizeof(long); flags = (unsigned int) *((long*) p); p += sizeof(long); q = (unsigned int) *((long*) p); p += sizeof(long); uri.s = p; uri.len = strlen(p); p += strlen(p) + 1; dst_uri.s = p; dst_uri.len = strlen(p); p += strlen(p) + 1; path.s = p; path.len = strlen(p); LM_DBG("Branch information <%.*s,%.*s,%.*s,%d,%u> (avp flag=%u)\n", uri.len, uri.s, dst_uri.len, dst_uri.s, path.len, path.s, q, flags, avp->flags); rval = append_branch(msg, &uri, &dst_uri, &path, q, flags, sock_info); if (rval == -1) { LM_ERR("append_branch failed\n"); goto error1; } if (avp->flags & Q_FLAG) { destroy_avp(avp); goto done; } prev = avp; avp = search_next_avp(prev, &val); destroy_avp(prev); } return 2; done: return (search_next_avp(avp, NULL)==NULL)?2:1; error1: destroy_avp(avp); error: return -1; } opensips-2.2.2/serialize.h000066400000000000000000000025631300170765700155310ustar00rootroot00000000000000/* * sequential forking implementation * * Copyright (C) 2005 Juha Heinanen * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2005-11-29 splitted from lcr module (bogdan) */ /*! * \file serialize.h * \brief Sequential forking implementation */ #ifndef _CORE_SERIALIZE_H_ #define _CORE_SERIALIZE_H_ #include "parser/msg_parser.h" int init_serialization(); /*! \brief converts the destination set (for parallel forking) into AVPS used * for serial forking. */ int serialize_branches(struct sip_msg *msg, int clean_before ); /*! \brief gets the next branches for serial forking */ int next_branches( struct sip_msg *msg ); #endif opensips-2.2.2/sha1.c000066400000000000000000000406161300170765700143720ustar00rootroot00000000000000/* * FIPS-180-1 compliant SHA-1 implementation * * Copyright (C) 2006-2014, ARM Limited, All Rights Reserved * * This file is part of mbed TLS (https://polarssl.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * The SHA-1 standard was published by NIST in 1993. * * http://www.itl.nist.gov/fipspubs/fip180-1.htm */ #include "sha1.h" #define polarssl_printf printf /* Implementation that should never be optimized out by the compiler */ static void polarssl_zeroize( void *v, size_t n ) { volatile unsigned char *p = v; while( n-- ) *p++ = 0; } #if !defined(POLARSSL_SHA1_ALT) /* * 32-bit integer manipulation macros (big endian) */ #ifndef GET_UINT32_BE #define GET_UINT32_BE(n,b,i) \ { \ (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ | ( (uint32_t) (b)[(i) + 1] << 16 ) \ | ( (uint32_t) (b)[(i) + 2] << 8 ) \ | ( (uint32_t) (b)[(i) + 3] ); \ } #endif #ifndef PUT_UINT32_BE #define PUT_UINT32_BE(n,b,i) \ { \ (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ (b)[(i) + 3] = (unsigned char) ( (n) ); \ } #endif void sha1_init( sha1_context *ctx ) { memset( ctx, 0, sizeof( sha1_context ) ); } void sha1_free( sha1_context *ctx ) { if( ctx == NULL ) return; polarssl_zeroize( ctx, sizeof( sha1_context ) ); } /* * SHA-1 context setup */ void sha1_starts( sha1_context *ctx ) { ctx->total[0] = 0; ctx->total[1] = 0; ctx->state[0] = 0x67452301; ctx->state[1] = 0xEFCDAB89; ctx->state[2] = 0x98BADCFE; ctx->state[3] = 0x10325476; ctx->state[4] = 0xC3D2E1F0; } void sha1_process( sha1_context *ctx, const unsigned char data[64] ) { uint32_t temp, W[16], A, B, C, D, E; GET_UINT32_BE( W[ 0], data, 0 ); GET_UINT32_BE( W[ 1], data, 4 ); GET_UINT32_BE( W[ 2], data, 8 ); GET_UINT32_BE( W[ 3], data, 12 ); GET_UINT32_BE( W[ 4], data, 16 ); GET_UINT32_BE( W[ 5], data, 20 ); GET_UINT32_BE( W[ 6], data, 24 ); GET_UINT32_BE( W[ 7], data, 28 ); GET_UINT32_BE( W[ 8], data, 32 ); GET_UINT32_BE( W[ 9], data, 36 ); GET_UINT32_BE( W[10], data, 40 ); GET_UINT32_BE( W[11], data, 44 ); GET_UINT32_BE( W[12], data, 48 ); GET_UINT32_BE( W[13], data, 52 ); GET_UINT32_BE( W[14], data, 56 ); GET_UINT32_BE( W[15], data, 60 ); #define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) #define R(t) \ ( \ temp = W[( t - 3 ) & 0x0F] ^ W[( t - 8 ) & 0x0F] ^ \ W[( t - 14 ) & 0x0F] ^ W[ t & 0x0F], \ ( W[t & 0x0F] = S(temp,1) ) \ ) #define P(a,b,c,d,e,x) \ { \ e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ } A = ctx->state[0]; B = ctx->state[1]; C = ctx->state[2]; D = ctx->state[3]; E = ctx->state[4]; #define F(x,y,z) (z ^ (x & (y ^ z))) #define K 0x5A827999 P( A, B, C, D, E, W[0] ); P( E, A, B, C, D, W[1] ); P( D, E, A, B, C, W[2] ); P( C, D, E, A, B, W[3] ); P( B, C, D, E, A, W[4] ); P( A, B, C, D, E, W[5] ); P( E, A, B, C, D, W[6] ); P( D, E, A, B, C, W[7] ); P( C, D, E, A, B, W[8] ); P( B, C, D, E, A, W[9] ); P( A, B, C, D, E, W[10] ); P( E, A, B, C, D, W[11] ); P( D, E, A, B, C, W[12] ); P( C, D, E, A, B, W[13] ); P( B, C, D, E, A, W[14] ); P( A, B, C, D, E, W[15] ); P( E, A, B, C, D, R(16) ); P( D, E, A, B, C, R(17) ); P( C, D, E, A, B, R(18) ); P( B, C, D, E, A, R(19) ); #undef K #undef F #define F(x,y,z) (x ^ y ^ z) #define K 0x6ED9EBA1 P( A, B, C, D, E, R(20) ); P( E, A, B, C, D, R(21) ); P( D, E, A, B, C, R(22) ); P( C, D, E, A, B, R(23) ); P( B, C, D, E, A, R(24) ); P( A, B, C, D, E, R(25) ); P( E, A, B, C, D, R(26) ); P( D, E, A, B, C, R(27) ); P( C, D, E, A, B, R(28) ); P( B, C, D, E, A, R(29) ); P( A, B, C, D, E, R(30) ); P( E, A, B, C, D, R(31) ); P( D, E, A, B, C, R(32) ); P( C, D, E, A, B, R(33) ); P( B, C, D, E, A, R(34) ); P( A, B, C, D, E, R(35) ); P( E, A, B, C, D, R(36) ); P( D, E, A, B, C, R(37) ); P( C, D, E, A, B, R(38) ); P( B, C, D, E, A, R(39) ); #undef K #undef F #define F(x,y,z) ((x & y) | (z & (x | y))) #define K 0x8F1BBCDC P( A, B, C, D, E, R(40) ); P( E, A, B, C, D, R(41) ); P( D, E, A, B, C, R(42) ); P( C, D, E, A, B, R(43) ); P( B, C, D, E, A, R(44) ); P( A, B, C, D, E, R(45) ); P( E, A, B, C, D, R(46) ); P( D, E, A, B, C, R(47) ); P( C, D, E, A, B, R(48) ); P( B, C, D, E, A, R(49) ); P( A, B, C, D, E, R(50) ); P( E, A, B, C, D, R(51) ); P( D, E, A, B, C, R(52) ); P( C, D, E, A, B, R(53) ); P( B, C, D, E, A, R(54) ); P( A, B, C, D, E, R(55) ); P( E, A, B, C, D, R(56) ); P( D, E, A, B, C, R(57) ); P( C, D, E, A, B, R(58) ); P( B, C, D, E, A, R(59) ); #undef K #undef F #define F(x,y,z) (x ^ y ^ z) #define K 0xCA62C1D6 P( A, B, C, D, E, R(60) ); P( E, A, B, C, D, R(61) ); P( D, E, A, B, C, R(62) ); P( C, D, E, A, B, R(63) ); P( B, C, D, E, A, R(64) ); P( A, B, C, D, E, R(65) ); P( E, A, B, C, D, R(66) ); P( D, E, A, B, C, R(67) ); P( C, D, E, A, B, R(68) ); P( B, C, D, E, A, R(69) ); P( A, B, C, D, E, R(70) ); P( E, A, B, C, D, R(71) ); P( D, E, A, B, C, R(72) ); P( C, D, E, A, B, R(73) ); P( B, C, D, E, A, R(74) ); P( A, B, C, D, E, R(75) ); P( E, A, B, C, D, R(76) ); P( D, E, A, B, C, R(77) ); P( C, D, E, A, B, R(78) ); P( B, C, D, E, A, R(79) ); #undef K #undef F ctx->state[0] += A; ctx->state[1] += B; ctx->state[2] += C; ctx->state[3] += D; ctx->state[4] += E; } /* * SHA-1 process buffer */ void sha1_update( sha1_context *ctx, const unsigned char *input, size_t ilen ) { size_t fill; uint32_t left; if( ilen == 0 ) return; left = ctx->total[0] & 0x3F; fill = 64 - left; ctx->total[0] += (uint32_t) ilen; ctx->total[0] &= 0xFFFFFFFF; if( ctx->total[0] < (uint32_t) ilen ) ctx->total[1]++; if( left && ilen >= fill ) { memcpy( (void *) (ctx->buffer + left), input, fill ); sha1_process( ctx, ctx->buffer ); input += fill; ilen -= fill; left = 0; } while( ilen >= 64 ) { sha1_process( ctx, input ); input += 64; ilen -= 64; } if( ilen > 0 ) memcpy( (void *) (ctx->buffer + left), input, ilen ); } static const unsigned char sha1_padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* * SHA-1 final digest */ void sha1_finish( sha1_context *ctx, unsigned char output[20] ) { uint32_t last, padn; uint32_t high, low; unsigned char msglen[8]; high = ( ctx->total[0] >> 29 ) | ( ctx->total[1] << 3 ); low = ( ctx->total[0] << 3 ); PUT_UINT32_BE( high, msglen, 0 ); PUT_UINT32_BE( low, msglen, 4 ); last = ctx->total[0] & 0x3F; padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); sha1_update( ctx, sha1_padding, padn ); sha1_update( ctx, msglen, 8 ); PUT_UINT32_BE( ctx->state[0], output, 0 ); PUT_UINT32_BE( ctx->state[1], output, 4 ); PUT_UINT32_BE( ctx->state[2], output, 8 ); PUT_UINT32_BE( ctx->state[3], output, 12 ); PUT_UINT32_BE( ctx->state[4], output, 16 ); } #endif /* !POLARSSL_SHA1_ALT */ /* * output = SHA-1( input buffer ) */ void sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ) { sha1_context ctx; sha1_init( &ctx ); sha1_starts( &ctx ); sha1_update( &ctx, input, ilen ); sha1_finish( &ctx, output ); sha1_free( &ctx ); } #if defined(POLARSSL_FS_IO) /* * output = SHA-1( file contents ) */ int sha1_file( const char *path, unsigned char output[20] ) { FILE *f; size_t n; sha1_context ctx; unsigned char buf[1024]; if( ( f = fopen( path, "rb" ) ) == NULL ) return( POLARSSL_ERR_SHA1_FILE_IO_ERROR ); sha1_init( &ctx ); sha1_starts( &ctx ); while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) sha1_update( &ctx, buf, n ); sha1_finish( &ctx, output ); sha1_free( &ctx ); if( ferror( f ) != 0 ) { fclose( f ); return( POLARSSL_ERR_SHA1_FILE_IO_ERROR ); } fclose( f ); return( 0 ); } #endif /* POLARSSL_FS_IO */ /* * SHA-1 HMAC context setup */ void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, size_t keylen ) { size_t i; unsigned char sum[20]; if( keylen > 64 ) { sha1( key, keylen, sum ); keylen = 20; key = sum; } memset( ctx->ipad, 0x36, 64 ); memset( ctx->opad, 0x5C, 64 ); for( i = 0; i < keylen; i++ ) { ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); } sha1_starts( ctx ); sha1_update( ctx, ctx->ipad, 64 ); polarssl_zeroize( sum, sizeof( sum ) ); } /* * SHA-1 HMAC process buffer */ void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, size_t ilen ) { sha1_update( ctx, input, ilen ); } /* * SHA-1 HMAC final digest */ void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ) { unsigned char tmpbuf[20]; sha1_finish( ctx, tmpbuf ); sha1_starts( ctx ); sha1_update( ctx, ctx->opad, 64 ); sha1_update( ctx, tmpbuf, 20 ); sha1_finish( ctx, output ); polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); } /* * SHA1 HMAC context reset */ void sha1_hmac_reset( sha1_context *ctx ) { sha1_starts( ctx ); sha1_update( ctx, ctx->ipad, 64 ); } /* * output = HMAC-SHA-1( hmac key, input buffer ) */ void sha1_hmac( const unsigned char *key, size_t keylen, const unsigned char *input, size_t ilen, unsigned char output[20] ) { sha1_context ctx; sha1_init( &ctx ); sha1_hmac_starts( &ctx, key, keylen ); sha1_hmac_update( &ctx, input, ilen ); sha1_hmac_finish( &ctx, output ); sha1_free( &ctx ); } #if defined(POLARSSL_SELF_TEST) /* * FIPS-180-1 test vectors */ static unsigned char sha1_test_buf[3][57] = { { "abc" }, { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, { "" } }; static const int sha1_test_buflen[3] = { 3, 56, 1000 }; static const unsigned char sha1_test_sum[3][20] = { { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }, { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }, { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F } }; /* * RFC 2202 test vectors */ static unsigned char sha1_hmac_test_key[7][26] = { { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" "\x0B\x0B\x0B\x0B" }, { "Jefe" }, { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" "\xAA\xAA\xAA\xAA" }, { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" "\x11\x12\x13\x14\x15\x16\x17\x18\x19" }, { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" "\x0C\x0C\x0C\x0C" }, { "" }, /* 0xAA 80 times */ { "" } }; static const int sha1_hmac_test_keylen[7] = { 20, 4, 20, 25, 20, 80, 80 }; static unsigned char sha1_hmac_test_buf[7][74] = { { "Hi There" }, { "what do ya want for nothing?" }, { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" }, { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" }, { "Test With Truncation" }, { "Test Using Larger Than Block-Size Key - Hash Key First" }, { "Test Using Larger Than Block-Size Key and Larger" " Than One Block-Size Data" } }; static const int sha1_hmac_test_buflen[7] = { 8, 28, 50, 50, 20, 54, 73 }; static const unsigned char sha1_hmac_test_sum[7][20] = { { 0xB6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xE2, 0x8B, 0xC0, 0xB6, 0xFB, 0x37, 0x8C, 0x8E, 0xF1, 0x46, 0xBE, 0x00 }, { 0xEF, 0xFC, 0xDF, 0x6A, 0xE5, 0xEB, 0x2F, 0xA2, 0xD2, 0x74, 0x16, 0xD5, 0xF1, 0x84, 0xDF, 0x9C, 0x25, 0x9A, 0x7C, 0x79 }, { 0x12, 0x5D, 0x73, 0x42, 0xB9, 0xAC, 0x11, 0xCD, 0x91, 0xA3, 0x9A, 0xF4, 0x8A, 0xA1, 0x7B, 0x4F, 0x63, 0xF1, 0x75, 0xD3 }, { 0x4C, 0x90, 0x07, 0xF4, 0x02, 0x62, 0x50, 0xC6, 0xBC, 0x84, 0x14, 0xF9, 0xBF, 0x50, 0xC8, 0x6C, 0x2D, 0x72, 0x35, 0xDA }, { 0x4C, 0x1A, 0x03, 0x42, 0x4B, 0x55, 0xE0, 0x7F, 0xE7, 0xF2, 0x7B, 0xE1 }, { 0xAA, 0x4A, 0xE5, 0xE1, 0x52, 0x72, 0xD0, 0x0E, 0x95, 0x70, 0x56, 0x37, 0xCE, 0x8A, 0x3B, 0x55, 0xED, 0x40, 0x21, 0x12 }, { 0xE8, 0xE9, 0x9D, 0x0F, 0x45, 0x23, 0x7D, 0x78, 0x6D, 0x6B, 0xBA, 0xA7, 0x96, 0x5C, 0x78, 0x08, 0xBB, 0xFF, 0x1A, 0x91 } }; /* * Checkup routine */ int sha1_self_test( int verbose ) { int i, j, buflen, ret = 0; unsigned char buf[1024]; unsigned char sha1sum[20]; sha1_context ctx; sha1_init( &ctx ); /* * SHA-1 */ for( i = 0; i < 3; i++ ) { if( verbose != 0 ) polarssl_printf( " SHA-1 test #%d: ", i + 1 ); sha1_starts( &ctx ); if( i == 2 ) { memset( buf, 'a', buflen = 1000 ); for( j = 0; j < 1000; j++ ) sha1_update( &ctx, buf, buflen ); } else sha1_update( &ctx, sha1_test_buf[i], sha1_test_buflen[i] ); sha1_finish( &ctx, sha1sum ); if( memcmp( sha1sum, sha1_test_sum[i], 20 ) != 0 ) { if( verbose != 0 ) polarssl_printf( "failed\n" ); ret = 1; goto exit; } if( verbose != 0 ) polarssl_printf( "passed\n" ); } if( verbose != 0 ) polarssl_printf( "\n" ); for( i = 0; i < 7; i++ ) { if( verbose != 0 ) polarssl_printf( " HMAC-SHA-1 test #%d: ", i + 1 ); if( i == 5 || i == 6 ) { memset( buf, '\xAA', buflen = 80 ); sha1_hmac_starts( &ctx, buf, buflen ); } else sha1_hmac_starts( &ctx, sha1_hmac_test_key[i], sha1_hmac_test_keylen[i] ); sha1_hmac_update( &ctx, sha1_hmac_test_buf[i], sha1_hmac_test_buflen[i] ); sha1_hmac_finish( &ctx, sha1sum ); buflen = ( i == 4 ) ? 12 : 20; if( memcmp( sha1sum, sha1_hmac_test_sum[i], buflen ) != 0 ) { if( verbose != 0 ) polarssl_printf( "failed\n" ); ret = 1; goto exit; } if( verbose != 0 ) polarssl_printf( "passed\n" ); } if( verbose != 0 ) polarssl_printf( "\n" ); exit: sha1_free( &ctx ); return( ret ); } #endif /* POLARSSL_SELF_TEST */ opensips-2.2.2/sha1.h000066400000000000000000000114301300170765700143670ustar00rootroot00000000000000/** * \file sha1.h * * \brief SHA-1 cryptographic hash function * * Copyright (C) 2006-2014, ARM Limited, All Rights Reserved * * This file is part of mbed TLS (https://polarssl.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef POLARSSL_SHA1_H #define POLARSSL_SHA1_H #include #if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) #include typedef UINT32 uint32_t; #else #include #endif #define POLARSSL_ERR_SHA1_FILE_IO_ERROR -0x0076 /**< Read/write error in file. */ #ifdef __cplusplus extern "C" { #endif /** * \brief SHA-1 context structure */ typedef struct { uint32_t total[2]; /*!< number of bytes processed */ uint32_t state[5]; /*!< intermediate digest state */ unsigned char buffer[64]; /*!< data block being processed */ unsigned char ipad[64]; /*!< HMAC: inner padding */ unsigned char opad[64]; /*!< HMAC: outer padding */ } sha1_context; /** * \brief Initialize SHA-1 context * * \param ctx SHA-1 context to be initialized */ void sha1_init( sha1_context *ctx ); /** * \brief Clear SHA-1 context * * \param ctx SHA-1 context to be cleared */ void sha1_free( sha1_context *ctx ); /** * \brief SHA-1 context setup * * \param ctx context to be initialized */ void sha1_starts( sha1_context *ctx ); /** * \brief SHA-1 process buffer * * \param ctx SHA-1 context * \param input buffer holding the data * \param ilen length of the input data */ void sha1_update( sha1_context *ctx, const unsigned char *input, size_t ilen ); /** * \brief SHA-1 final digest * * \param ctx SHA-1 context * \param output SHA-1 checksum result */ void sha1_finish( sha1_context *ctx, unsigned char output[20] ); /* Internal use */ void sha1_process( sha1_context *ctx, const unsigned char data[64] ); #ifdef __cplusplus } #endif #else /* POLARSSL_SHA1_ALT */ #include "sha1_alt.h" #endif /* POLARSSL_SHA1_ALT */ #ifdef __cplusplus extern "C" { #endif /** * \brief Output = SHA-1( input buffer ) * * \param input buffer holding the data * \param ilen length of the input data * \param output SHA-1 checksum result */ void sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ); /** * \brief Output = SHA-1( file contents ) * * \param path input file name * \param output SHA-1 checksum result * * \return 0 if successful, or POLARSSL_ERR_SHA1_FILE_IO_ERROR */ int sha1_file( const char *path, unsigned char output[20] ); /** * \brief SHA-1 HMAC context setup * * \param ctx HMAC context to be initialized * \param key HMAC secret key * \param keylen length of the HMAC key */ void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, size_t keylen ); /** * \brief SHA-1 HMAC process buffer * * \param ctx HMAC context * \param input buffer holding the data * \param ilen length of the input data */ void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, size_t ilen ); /** * \brief SHA-1 HMAC final digest * * \param ctx HMAC context * \param output SHA-1 HMAC checksum result */ void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ); /** * \brief SHA-1 HMAC context reset * * \param ctx HMAC context to be reset */ void sha1_hmac_reset( sha1_context *ctx ); /** * \brief Output = HMAC-SHA-1( hmac key, input buffer ) * * \param key HMAC secret key * \param keylen length of the HMAC key * \param input buffer holding the data * \param ilen length of the input data * \param output HMAC-SHA-1 result */ void sha1_hmac( const unsigned char *key, size_t keylen, const unsigned char *input, size_t ilen, unsigned char output[20] ); /** * \brief Checkup routine * * \return 0 if successful, or 1 if the test failed */ int sha1_self_test( int verbose ); #ifdef __cplusplus } #endif /* sha1.h */ opensips-2.2.2/sl_cb.c000066400000000000000000000052441300170765700146160ustar00rootroot00000000000000/* * Copyright (C) 2016 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2016-02-01 first version (bogdan) */ #include "mem/mem.h" #include "sl_cb.h" struct sl_callback { sl_cb_t* callback; /* callback function */ unsigned int fmask; /* bitmask to match msg_flags */ struct sl_callback* next; /* next callback element*/ }; struct sl_callback* slcb_hl[SLCB_LAST]; /* heads of lists (per type) */ void destroy_slcb_lists(void) { struct sl_callback *cbp, *cbp_tmp; unsigned int i; for( i=0 ; inext; pkg_free( cbp_tmp ); } } } int register_slcb(enum sl_cb_type type, unsigned int fmask, sl_cb_t f) { struct sl_callback *cbp; /* build a new callback structure */ if (!(cbp=pkg_malloc( sizeof( struct sl_callback)))) { LM_ERR("out of pkg. mem\n"); return -1; } /* fill it up */ cbp->callback = f; cbp->fmask = fmask; /* link it at the beginning of the list */ cbp->next = slcb_hl[type]; slcb_hl[type] = cbp; return 0; } void slcb_run_reply_out(struct sip_msg *req, str *buffer, union sockaddr_union *dst, int rpl_code) { struct sl_callback *cbp; for ( cbp=slcb_hl[SLCB_REPLY_OUT] ; cbp ; cbp=cbp->next ) { if (cbp->fmask==0 || cbp->fmask&req->msg_flags) { cbp->callback( req, buffer, rpl_code, dst, NULL, 0); } } } void slcb_run_ack_in(struct sip_msg *req) { struct sl_callback *cbp; for ( cbp=slcb_hl[SLCB_ACK_IN] ; cbp ; cbp=cbp->next ) { if (cbp->fmask==0 || cbp->fmask&req->msg_flags) { cbp->callback( req, NULL, 0, NULL, NULL, 0); } } } void slcb_run_req_out(struct sip_msg *req, str *buffer, union sockaddr_union *dst, struct socket_info *sock, int proto) { struct sl_callback *cbp; for ( cbp=slcb_hl[SLCB_REQUEST_OUT] ; cbp ; cbp=cbp->next ) { if (cbp->fmask==0 || cbp->fmask&req->msg_flags) { cbp->callback( req, buffer, 0, dst, sock, proto); } } } opensips-2.2.2/sl_cb.h000066400000000000000000000035071300170765700146230ustar00rootroot00000000000000/* * Copyright (C) 2016 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2016-02-01 first version (bogdan) */ #ifndef SL_CB_H_ #define SL_CB_H_ #include "str.h" #include "ip_addr.h" #include "parser/msg_parser.h" enum sl_cb_type {SLCB_REPLY_OUT=0, SLCB_ACK_IN, SLCB_REQUEST_OUT, SLCB_LAST }; /* callback function prototype */ typedef void (sl_cb_t) (struct sip_msg* req, str *buffer, int rpl_code, union sockaddr_union *dst, struct socket_info *sock, int proto); /* register callback function prototype */ typedef int (*register_slcb_t)(enum sl_cb_type, unsigned int fmask, sl_cb_t f); /* frees all memory used by the callbacks */ void destroy_slcb_lists(); /* register a SL callback */ int register_slcb(enum sl_cb_type, unsigned int fmask, sl_cb_t f); /* run SL callbacks for a given type */ void slcb_run_reply_out(struct sip_msg *req, str *buffer, union sockaddr_union *dst, int rpl_code); void slcb_run_ack_in(struct sip_msg *req); void slcb_run_req_out(struct sip_msg *req, str *buffer, union sockaddr_union *dst, struct socket_info *sock, int proto); #endif opensips-2.2.2/sliblist.c000066400000000000000000000062231300170765700153570ustar00rootroot00000000000000/* * A simple linked list implementation. * * Copyright (C) 2013 VoIP Embedded, Inc. * * sliblist 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 * * sliblist 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 Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------ * 2013-02-25 initial implementation (osas) */ #include #include "sliblist.h" struct slinkedl_list { struct slinkedl_element *head; struct slinkedl_element *tail; slinkedl_alloc_f *alloc; slinkedl_dealloc_f *dealloc; }; struct slinkedl_element { void *data; struct slinkedl_element *next; }; slinkedl_list_t* slinkedl_init(slinkedl_alloc_f *alloc, slinkedl_dealloc_f *dealloc) { slinkedl_list_t *list; if (alloc==NULL || dealloc==NULL) return NULL; list = (*alloc)(sizeof(slinkedl_list_t)); if (list) { memset(list, 0, sizeof(slinkedl_list_t)); list->alloc = alloc; list->dealloc = dealloc; } return list; } void slinkedl_list_init(slinkedl_list_t* list) { if (!list) return; memset(list, 0, sizeof(slinkedl_list_t)); return; } void *slinkedl_prepend(slinkedl_list_t *list, size_t e_size) { slinkedl_element_t *element; if (!list || !e_size) return NULL; element = (slinkedl_element_t*)(list->alloc(sizeof(slinkedl_element_t) + e_size)); if (!element) return NULL; element->next = list->head; element->data = (void*)(element + 1); list->head = element; if (element->next) { } else { /* This is an empty list */ list->tail = element; } return (element->data); } void *slinkedl_append(slinkedl_list_t *list, size_t e_size) { slinkedl_element_t *element; if (!list || !e_size) return NULL; element = (slinkedl_element_t*)(list->alloc(sizeof(slinkedl_element_t) + e_size)); if (!element) return NULL; element->next = NULL; element->data = (void*)(element + 1); if (list->tail) { list->tail->next = element; list->tail = element; } else { /* This is an empty list */ list->head = element; list->tail = element; } return (element->data); } int slinkedl_traverse(slinkedl_list_t *list, slinkedl_run_data_f func, void *data, void *r_data) { int ret; slinkedl_element_t *element; if (!list) return -1; if (!func) return -2; element = list->head; while(element){ ret = (*func)(element->data, data, r_data); if (ret!=0) return ret; element = element->next; } return 0; } void slinkedl_list_destroy(slinkedl_list_t *list) { slinkedl_element_t *element; slinkedl_dealloc_f *dealloc; if (!list) return; element = list->head; while(element){ list->head = element->next; list->dealloc(element); element = list->head; } dealloc = list->dealloc; (*dealloc)(list); return; } opensips-2.2.2/sliblist.h000066400000000000000000000117561300170765700153730ustar00rootroot00000000000000/* * A simple linked list implementation. * * Copyright (C) 2013 VoIP Embedded, Inc. * * sliblist 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 * * sliblist 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 Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * 2013-02-25 initial implementation (osas) */ #ifndef S_LIB_LIST_H #define S_LIB_LIST_H #include #include /** * Structure defining the simple linked list. */ typedef struct slinkedl_list slinkedl_list_t; /** * Structure defining an elemnt of a simple linked list. */ typedef struct slinkedl_element slinkedl_element_t; /** * Memory allocator to be used in list operations. * * @param size Size of the memory block to be allocated. * @return A pointer to the requsted memory block. * NULL on error; * non NULL on success. */ void *(slinkedl_alloc) (size_t size); typedef void *(slinkedl_alloc_f) (size_t size); /** * Memory de-allocator to be used in list operations. * * @param ptr Ponter to the emmory block to be freeed. */ void (slinkedl_dealloc) (void *ptr); typedef void (slinkedl_dealloc_f) (void *ptr); /** * Function to be called by slinkedl_traverse while traversing the list. * * @param e_data pointer to data stored by the current element. * @param data pointer to given data to work with. * @param r_data pointer to data returned by this function. * return <0 on error and exit list traversal; * return 0 on no action on current list elemnt and * continue list traversal; * return >0 on action successfully completed on current list element * and exit list traversal. * @see slinkedl_traverse() */ int (slinkedl_run_data) (void *e_data, void *data, void *r_data); typedef int (slinkedl_run_data_f) (void *e_data, void *data, void *r_data); /** * List initializer. * This function MUST be called in order to initialize a list. * It's role is to allocate memory for the list structure and * initialize it's internal structure. * * @param alloc pointer to the memory allocator function. * @param dealloc pointer to the memory deallocator function. * @return The pointer to the list structure. * - NULL on error (alloc and dealloc must be non NULL); * - non NULL on success. */ slinkedl_list_t* slinkedl_init(slinkedl_alloc_f *alloc, slinkedl_dealloc_f *dealloc); /** * Insert a list elemnt at the beginning of the list. * One block of memory will be allocated for the whole element. * The memory will be allocated using the memory allocator * provided to the list during initialization. * * @param list The list to operate on. * @param e_size size of the element data to be store by the new element. * @return A pointer to a block of memory with size e_size. * - NULL on error; * - non NULL on success. * The application will use the returned pointer to populate * the memory block with it's data. * @see slinkedl_init() */ void *slinkedl_prepend(slinkedl_list_t *list, size_t e_size); /** * Insert a list elemnt at the end of the list. * One block of memory will be allocated for the whole element. * The memory will be allocated using the memory allocator * provided to the list during initialization. * * @param list The list to operate on. * @param e_size size of the element data to be store by the new element. * @return A pointer to a block of memory with size e_size. * - NULL on error; * - non NULL on success. * The application will use the returned pointer to populate * the memory block with it's data. * @see slinkedl_init() */ void *slinkedl_append(slinkedl_list_t *list, size_t e_size); /** * Traverse the list and execute run_data for each element, * until run_data returns a non zero value or the extent of the list * is reached. * * @psram lit The list to traverse. * @param run_data The funtion to operate on each list element. * @param data The data to be used by run_data function. * @parama r_data The data returned by run_data function. * @return The return code from last run_data call. * @see slinkedl_run_data() */ int slinkedl_traverse(slinkedl_list_t *list, slinkedl_run_data_f run_data, void *data, void *r_data); /** * Destroy the list. * Any element in the list will be silently destroyed. * If you want to perform some actions on list elemnts before destroying it, * use slinkedl_traverse(). * * @param list The list to be distroyed. */ void slinkedl_list_destroy(slinkedl_list_t *list); #endif opensips-2.2.2/socket_info.c000066400000000000000000001011131300170765700160270ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * This file contains code that initializes and handles ser listen addresses * lists (struct socket_info). It is used mainly on startup. * * History: * -------- * 2003-10-22 created by andrei * 2004-10-10 added grep_sock_info (andrei) * 2004-11-08 added find_si (andrei) * 2007-01-11 auto_aliases option added (bogdan) */ /*! * \file * \brief Find & manage listen addresses */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKIO_H #include #endif #include "globals.h" #include "socket_info.h" #include "dprint.h" #include "mem/mem.h" #include "ut.h" #include "resolve.h" #include "name_alias.h" #include "net/trans.h" #define MAX_PROC_BUFFER 256 /* list manip. functions (internal use only) */ /* append */ #define sock_listadd(head, el) \ do{\ if (*(head)==0) *(head)=(el); \ else{ \ for((el)->next=*(head); (el)->next->next;\ (el)->next=(el)->next->next); \ (el)->next->next=(el); \ (el)->prev=(el)->next; \ (el)->next=0; \ }\ }while(0) /* insert after "after" */ #define sock_listins(el, after) \ do{ \ if ((after)){\ (el)->next=(after)->next; \ if ((after)->next) (after)->next->prev=(el); \ (after)->next=(el); \ (el)->prev=(after); \ }else{ /* after==0 = list head */ \ (after)=(el); \ (el)->next=(el)->prev=0; \ }\ }while(0) #define sock_listrm(head, el) \ do {\ if (*(head)==(el)) *(head)=(el)->next; \ if ((el)->next) (el)->next->prev=(el)->prev; \ if ((el)->prev) (el)->prev->next=(el)->next; \ }while(0) /* another helper function, it just creates a socket_info struct */ struct socket_info* new_sock_info( char* name, unsigned short port, unsigned short proto, char *adv_name, unsigned short adv_port, unsigned short children,enum si_flags flags) { struct socket_info* si; si=(struct socket_info*) pkg_malloc(sizeof(struct socket_info)); if (si==0) goto error; memset(si, 0, sizeof(struct socket_info)); si->socket=-1; si->name.len=strlen(name); si->name.s=(char*)pkg_malloc(si->name.len+1); /* include \0 */ if (si->name.s==0) goto error; memcpy(si->name.s, name, si->name.len+1); /* set port & proto */ si->port_no=port; si->proto=proto; si->flags=flags; /* advertised socket information */ /* Make sure the adv_sock_string is initialized, because if there is * no adv_sock_name, no other code will initialize it! */ si->adv_sock_str.s=NULL; si->adv_sock_str.len=0; if(adv_name) { si->adv_name_str.len=strlen(adv_name); si->adv_name_str.s=(char *)pkg_malloc(si->adv_name_str.len+1); if (si->adv_name_str.s==0) goto error; memcpy(si->adv_name_str.s, adv_name, si->adv_name_str.len+1); } si->adv_port = 0; /* Here to help grep_sock_info along. */ if(adv_port) { si->adv_port_str.s=pkg_malloc(10); if (si->adv_port_str.s==0) goto error; si->adv_port_str.len=snprintf(si->adv_port_str.s, 10, "%hu", adv_port); si->adv_port = adv_port; } if ( (si->proto==PROTO_TCP || si->proto==PROTO_TLS) && children) { LM_WARN("number of children per TCP/TLS listener not supported -> ignoring...\n"); } else { si->children = children; } return si; error: LM_ERR("pkg memory allocation error\n"); if (si) pkg_free(si); return 0; } /* delete a socket_info struct */ static void free_sock_info(struct socket_info* si) { if(si){ if(si->name.s) pkg_free(si->name.s); if(si->address_str.s) pkg_free(si->address_str.s); if(si->port_no_str.s) pkg_free(si->port_no_str.s); if(si->adv_name_str.s) pkg_free(si->adv_name_str.s); if(si->adv_port_str.s) pkg_free(si->adv_port_str.s); } } /* checks if the proto: host:port is one of the address we listen on * and returns the corresponding socket_info structure. * if port==0, the port number is ignored * if proto==0 (PROTO_NONE) the protocol is ignored * returns 0 if not found * WARNING: uses str2ip6 so it will overwrite any previous * unsaved result of this function (static buffer) */ struct socket_info* grep_sock_info(str* host, unsigned short port, unsigned short proto) { char* hname; int h_len; struct socket_info* si; struct socket_info** list; unsigned short c_proto; struct ip_addr* ip6; h_len=host->len; hname=host->s; if ((h_len>2)&&((*hname)=='[')&&(hname[h_len-1]==']')){ /* ipv6 reference, skip [] */ hname++; h_len-=2; } c_proto=proto?proto:PROTO_UDP; do{ /* "proto" is all the time valid here */ list=get_sock_info_list(c_proto); if (list==0){ LM_WARN("unknown proto %d\n", c_proto); goto not_found; /* false */ } for (si=*list; si; si=si->next){ LM_DBG("checking if host==us: %d==%d && " " [%.*s] == [%.*s]\n", h_len, si->name.len, h_len, hname, si->name.len, si->name.s ); if (port) { LM_DBG("checking if port %d matches port %d\n", si->port_no, port); if (si->port_no!=port && si->adv_port!=port) { continue; } } if ( (h_len==si->name.len) && (strncasecmp(hname, si->name.s, si->name.len)==0) /*slower*/) /* comp. must be case insensitive, host names * can be written in mixed case, it will also match * ipv6 addresses if we are lucky*/ goto found; /* Check if the adv. name of this socket matches */ if ( (h_len==si->adv_name_str.len) && (strncasecmp(hname, si->adv_name_str.s, si->adv_name_str.len)==0) /*slower*/) /* comp. must be case insensitive, host names * can be in mixed case, it will also match * ipv6 addresses if we are lucky*/ goto found; /* check if host == ip address */ /* ipv6 case is uglier, host can be [3ffe::1] */ ip6=str2ip6(host); if (ip6){ if (ip_addr_cmp(ip6, &si->address)) goto found; /* match */ else continue; /* no match, but this is an ipv6 address so no point in trying ipv4 */ } /* ipv4 */ if ( (!(si->flags&SI_IS_IP)) && (h_len==si->address_str.len) && (memcmp(hname, si->address_str.s, si->address_str.len)==0) ) goto found; } }while( (proto==0) && (c_proto=next_proto(c_proto)) ); not_found: return 0; found: return si; } /* checks if the proto: ip:port is one of the address we listen on * and returns the corresponding socket_info structure. * (same as grep_socket_info, but use ip addr instead) * if port==0, the port number is ignored * if proto==0 (PROTO_NONE) the protocol is ignored * returns 0 if not found * WARNING: uses str2ip6 so it will overwrite any previous * unsaved result of this function (static buffer) */ struct socket_info* find_si(struct ip_addr* ip, unsigned short port, unsigned short proto) { struct socket_info* si; struct socket_info** list; unsigned short c_proto; c_proto=proto?proto:PROTO_UDP; do{ /* get the proper sock_list */ list=get_sock_info_list(c_proto); if (list==0){ LM_WARN("unknown proto %d\n", c_proto); goto not_found; /* false */ } for (si=*list; si; si=si->next){ if (port) { if (si->port_no!=port) { continue; } } if (ip_addr_cmp(ip, &si->address) || ip_addr_cmp(ip, &si->adv_address)) goto found; } }while( (proto==0) && (c_proto=next_proto(c_proto)) ); not_found: return 0; found: return si; } /* adds a new sock_info structure to the corresponding list * return 0 on success, -1 on error */ int new_sock2list(char* name, unsigned short port, unsigned short proto, char *adv_name, unsigned short adv_port, unsigned short children, enum si_flags flags, struct socket_info** list) { struct socket_info* si; si=new_sock_info(name, port, proto, adv_name, adv_port, children, flags); if (si==0){ LM_ERR("new_sock_info failed\n"); goto error; } sock_listadd(list, si); return 0; error: return -1; } /* adds a sock_info structure to the corresponding proto list * return 0 on success, -1 on error */ int add_listen_iface(char* name, unsigned short port, unsigned short proto, char *adv_name, unsigned short adv_port, unsigned short children, enum si_flags flags) { struct socket_info** list; unsigned short c_proto; c_proto=(proto)?proto:PROTO_UDP; LM_INFO("XXX - c_proto = %d\n",c_proto); do{ list=get_sock_info_list(c_proto); if (list==0){ LM_ERR("get_sock_info_list failed\n"); goto error; } if (port==0) /* use default port */ port=protos[c_proto].default_port; if (new_sock2list(name, port, c_proto, adv_name, adv_port, children, flags, list)<0){ LM_ERR("new_sock2list failed\n"); goto error; } }while( (proto==0) && (c_proto=next_proto(c_proto))); return 0; error: return -1; } /* add all family type addresses of interface if_name to the socket_info array * if if_name==0, adds all addresses on all interfaces * WARNING: it only works with ipv6 addresses on FreeBSD * return: -1 on error, 0 on success */ int add_interfaces(char* if_name, int family, unsigned short port, unsigned short proto, unsigned short children, struct socket_info** list) { struct ifconf ifc; struct ifreq ifr; struct ifreq ifrcopy; char* last; char* p; int size; int lastlen; int s; char* tmp; struct ip_addr addr; int ret; enum si_flags flags; #ifdef HAVE_SOCKADDR_SA_LEN #ifndef MAX #define MAX(a,b) ( ((a)>(b))?(a):(b)) #endif #endif /* ipv4 or ipv6 only*/ flags=SI_NONE; s=socket(family, SOCK_DGRAM, 0); ret=-1; lastlen=0; ifc.ifc_req=0; for (size=100; ; size*=2){ ifc.ifc_len=size*sizeof(struct ifreq); ifc.ifc_req=(struct ifreq*) pkg_malloc(size*sizeof(struct ifreq)); if (ifc.ifc_req==0){ LM_ERR("memory allocation failure\n"); goto error; } if (ioctl(s, SIOCGIFCONF, &ifc)==-1){ if(errno==EBADF) goto error; /* invalid descriptor => no such ifs*/ LM_ERR("ioctl failed: %s\n", strerror(errno)); goto error; } if ((lastlen) && (ifc.ifc_len==lastlen)) break; /*success, len not changed*/ lastlen=ifc.ifc_len; /* try a bigger array*/ pkg_free(ifc.ifc_req); } last=(char*)ifc.ifc_req+ifc.ifc_len; for(p=(char*)ifc.ifc_req; pifr_addr.sa_family);*/ continue; } /*get flags*/ ifrcopy=ifr; if (ioctl(s, SIOCGIFFLAGS, &ifrcopy)!=-1){ /* ignore errors */ /* ignore down ifs only if listening on all of them*/ if (if_name==0){ /* if if not up, skip it*/ if (!(ifrcopy.ifr_flags & IFF_UP)) continue; } } if ((if_name==0)|| (strncmp(if_name, ifr.ifr_name, sizeof(ifr.ifr_name))==0)){ /*add address*/ sockaddr2ip_addr(&addr, (struct sockaddr*)(p+(long)&((struct ifreq*)0)->ifr_addr)); if ((tmp=ip_addr2a(&addr))==0) goto error; /* check if loopback */ if (ifrcopy.ifr_flags & IFF_LOOPBACK) flags|=SI_IS_LO; /* add it to one of the lists */ if (new_sock2list(tmp, port, proto, 0, 0, children, flags, list)!=0){ LM_ERR("new_sock2list failed\n"); goto error; } ret=0; } /* printf("%s:\n", ifr->ifr_name); printf(" "); print_sockaddr(&(ifr->ifr_addr)); printf(" "); ls_ifflags(ifr->ifr_name, family, options); printf("\n");*/ } pkg_free(ifc.ifc_req); /*clean up*/ close(s); return ret; error: if (ifc.ifc_req) pkg_free(ifc.ifc_req); if (s >= 0) close(s); return -1; } /* fixes a socket list => resolve addresses, * interface names, fills missing members, remove duplicates */ int fix_socket_list(struct socket_info **list) { struct socket_info* si; struct socket_info* l; struct socket_info* next; char* tmp; int len; struct hostent* he; char** h; /* try to change all the interface names into addresses * --ugly hack */ for (si=*list;si;){ next=si->next; if (add_interfaces(si->name.s, AF_INET, si->port_no, si->proto, si->children, list)!=-1){ /* success => remove current entry (shift the entire array)*/ sock_listrm(list, si); free_sock_info(si); } si=next; } /* get ips & fill the port numbers*/ #ifdef EXTRA_DEBUG LM_DBG("listening on \n"); #endif for (si=*list;si;si=si->next){ /* fix the number of processes per interface */ if (!si->children && is_udp_based_proto(si->proto)) si->children = children_no; if (si->port_no==0) si->port_no= protos[si->proto].default_port; tmp=int2str(si->port_no, &len); if (len>=MAX_PORT_LEN){ LM_ERR("bad port number: %d\n", si->port_no); goto error; } si->port_no_str.s=(char*)pkg_malloc(len+1); if (si->port_no_str.s==0){ LM_ERR("out of pkg memory.\n"); goto error; } strncpy(si->port_no_str.s, tmp, len+1); si->port_no_str.len=len; /* get "official hostnames", all the aliases etc. */ he=resolvehost(si->name.s,0); if (he==0){ LM_ERR("could not resolve %s\n", si->name.s); goto error; } /* check if we got the official name */ if (strcasecmp(he->h_name, si->name.s)!=0){ if (auto_aliases && add_alias(si->name.s, si->name.len, si->port_no, si->proto)<0){ LM_ERR("add_alias failed\n"); } /* change the official name */ pkg_free(si->name.s); si->name.s=(char*)pkg_malloc(strlen(he->h_name)+1); if (si->name.s==0){ LM_ERR("out of pkg memory.\n"); goto error; } si->name.len=strlen(he->h_name); strncpy(si->name.s, he->h_name, si->name.len+1); } /* add the aliases*/ if (auto_aliases) { for(h=he->h_aliases; h && *h; h++) if (add_alias(*h, strlen(*h), si->port_no, si->proto)<0){ LM_ERR("add_alias failed\n"); } } hostent2ip_addr(&si->address, he, 0); /*convert to ip_addr format*/ if ((tmp=ip_addr2a(&si->address))==0) goto error; if (si->address.af == AF_INET6) { si->address_str.s=(char*)pkg_malloc(strlen(tmp)+1+2); if (si->address_str.s==0){ LM_ERR("out of pkg memory.\n"); goto error; } si->address_str.s[0] = '['; strncpy( si->address_str.s+1 , tmp, strlen(tmp)); si->address_str.s[1+strlen(tmp)] = ']'; si->address_str.s[2+strlen(tmp)] = '\0'; si->address_str.len=strlen(tmp) + 2; } else { si->address_str.s=(char*)pkg_malloc(strlen(tmp)+1); if (si->address_str.s==0){ LM_ERR("out of pkg memory.\n"); goto error; } strncpy(si->address_str.s, tmp, strlen(tmp)+1); si->address_str.len=strlen(tmp); } /* set is_ip (1 if name is an ip address, 0 otherwise) */ if ( auto_aliases && (si->address_str.len==si->name.len) && (strncasecmp(si->address_str.s, si->name.s, si->address_str.len)==0) ){ si->flags|=SI_IS_IP; /* do rev. DNS on it (for aliases)*/ he=rev_resolvehost(&si->address); if (he==0){ LM_WARN("could not rev. resolve %s\n", si->name.s); }else{ /* add the aliases*/ if (add_alias(he->h_name, strlen(he->h_name), si->port_no, si->proto)<0){ LM_ERR("add_alias failed\n"); } for(h=he->h_aliases; h && *h; h++) if (add_alias(*h,strlen(*h),si->port_no,si->proto)<0){ LM_ERR(" add_alias failed\n"); } } } /* Now build an ip_addr structure for the adv_name, if there is one * so that find_si can find it later easily. Doing this so that * we can force_send_socket() on an advertised name. Generally there * is little interest in dealing with an advertised name as anything * other than an opaque string that we blindly put into the SIP * message. */ if(si->adv_name_str.len) { /* If adv_name_str is already an IP, this is kinda foolish cus it * converts it to ip_addr, then to he, then here we go back to * ip_addr, but it's either that, or we duplicate the logic to * check for an ip address here, and still we might have to call * resolvehost(). */ he=resolvehost(si->adv_name_str.s,0); if (he==0){ LM_ERR("ERROR: fix_socket_list: could not resolve " "advertised name %s\n", si->adv_name_str.s); goto error; } hostent2ip_addr(&si->adv_address, he, 0); /*convert to ip_addr */ /* build and set string encoding for the adv socket info * This is usefful for the usrloc module when it's generating * or updating the socket on a location record, so we'll generate * it up front just like the regular sock_str so we don't have * to worry about it later. */ tmp = socket2str( si, 0, &si->adv_sock_str.len, 1); if (tmp==0) { LM_ERR("ERROR: fix_socket_list: failed to convert " "socket to string (adv)\n"); goto error; } si->adv_sock_str.s=(char*)pkg_malloc(si->adv_sock_str.len); if (si->adv_sock_str.s==0) { LM_ERR("ERROR: fix_socket_list: out of memory.\n"); goto error; } memcpy(si->adv_sock_str.s, tmp, si->adv_sock_str.len); } /* build and set string encoding for the real socket info */ tmp = socket2str( si, 0, &si->sock_str.len, 0); if (tmp==0) { LM_ERR("failed to convert socket to string\n"); goto error; } si->sock_str.s=(char*)pkg_malloc(si->sock_str.len); if (si->sock_str.s==0) { LM_ERR("out of pkg memory.\n"); goto error; } memcpy(si->sock_str.s, tmp, si->sock_str.len); #ifdef USE_MCAST /* Check if it is an multicast address and * set the flag if so */ if (is_mcast(&si->address)) { si->flags |= SI_IS_MCAST; } #endif /* USE_MCAST */ #ifdef EXTRA_DEBUG printf(" %.*s [%s]:%s%s\n", si->name.len, si->name.s, si->address_str.s, si->port_no_str.s, si->flags & SI_IS_MCAST ? " mcast" : ""); #endif } /* removing duplicate addresses*/ for (si=*list;si; si=si->next){ for (l=si->next;l;){ next=l->next; if ((si->port_no==l->port_no) && (si->address.af==l->address.af) && (memcmp(si->address.u.addr, l->address.u.addr, si->address.len) == 0) ){ #ifdef EXTRA_DEBUG printf("removing duplicate %s [%s] == %s [%s]\n", si->name.s, si->address_str.s, l->name.s, l->address_str.s); #endif /* add the name to the alias list*/ if ((!(l->flags& SI_IS_IP)) && ( (l->name.len!=si->name.len)|| (strncmp(l->name.s, si->name.s, si->name.len)!=0)) ) add_alias(l->name.s, l->name.len, l->port_no, l->proto); /* remove l*/ sock_listrm(list, l); free_sock_info(l); } l=next; } } #ifdef USE_MCAST /* Remove invalid multicast entries */ si=*list; while(si){ if ((si->flags & SI_IS_MCAST) && (si->proto != PROTO_UDP) ){ LM_WARN("removing entry %s:%s [%s]:%s\n", get_proto_name(si->proto), si->name.s, si->address_str.s, si->port_no_str.s); l = si; si=si->next; sock_listrm(list, l); free_sock_info(l); } else { si=si->next; } } #endif /* USE_MCAST */ return 0; error: return -1; } /* * This function will retrieve a list of all ip addresses and ports that * OpenSIPS is listening on, with respect to the transport protocol specified * with 'protocol'. * * The first parameter, ipList, is a pointer to a pointer. It will be assigned * a new block of memory holding the IP Addresses and ports being listened to * with respect to 'protocol'. The array maps a 2D array into a 1 dimensional * space, and is layed out as follows: * * The first NUM_IP_OCTETS indices will be the IP address, and the next index * the port. So if NUM_IP_OCTETS is equal to 4 and there are two IP addresses * found, then: * * - ipList[0] will be the first octet of the first ip address * - ipList[3] will be the last octet of the first ip address. * - iplist[4] will be the port of the first ip address * - * - iplist[5] will be the first octet of the first ip address, * - and so on. * * The function will return the number of sockets which were found. This can * be used to index into ipList. * * NOTE: This function assigns a block of memory equal to: * * returnedValue * (NUM_IP_OCTETS + 1) * sizeof(int); * * Therefore it is CRUCIAL that you free ipList when you are done with * its contents, to avoid a nasty memory leak. */ int get_socket_list_from_proto(int **ipList, int protocol) { struct socket_info *si; struct socket_info** list; int num_ip_octets = 4; int numberOfSockets = 0; int currentRow = 0; /* I hate to use #ifdefs, but this is necessary because of the way * get_sock_info_list() is defined. */ if (protocol == PROTO_TCP) { return 0; } if (protocol == PROTO_TLS) { return 0; } /* Retrieve the list of sockets with respect to the given protocol. */ list=get_sock_info_list(protocol); /* Find out how many sockets are in the list. We need to know this so * we can malloc an array to assign to ipList. */ for(si=list?*list:0; si; si=si->next){ /* We only support IPV4 at this point. */ if (si->address.af == AF_INET) { numberOfSockets++; } } /* There are no open sockets with respect to the given protocol. */ if (numberOfSockets == 0) { return 0; } *ipList = pkg_malloc(numberOfSockets * (num_ip_octets + 1) * sizeof(int)); /* We couldn't allocate memory for the IP List. So all we can do is * fail. */ if (*ipList == NULL) { LM_ERR("no more pkg memory"); return 0; } /* We need to search the list again. So find the front of the list. */ list=get_sock_info_list(protocol); /* Extract out the IP Addresses and ports. */ for(si=list?*list:0; si; si=si->next){ /* We currently only support IPV4. */ if (si->address.af != AF_INET) { continue; } (*ipList)[currentRow*(num_ip_octets + 1) ] = si->address.u.addr[0]; (*ipList)[currentRow*(num_ip_octets + 1)+1] = si->address.u.addr[1]; (*ipList)[currentRow*(num_ip_octets + 1)+2] = si->address.u.addr[2]; (*ipList)[currentRow*(num_ip_octets + 1)+3] = si->address.u.addr[3]; (*ipList)[currentRow*(num_ip_octets + 1)+4] = si->port_no; currentRow++; } return numberOfSockets; } /* * Takes a 'line' (from the proc file system), parses out the ipAddress, * address, and stores the number of bytes waiting in 'rx_queue' * * Returns 1 on success, and 0 on a failed parse. * * Note: The format of ipAddress is as defined in the comments of * get_socket_list_from_proto() in this file. * */ static int parse_proc_net_line(char *line, int *ipAddress, int *rx_queue) { int i; int ipOctetExtractionMask = 0xFF; char *currColonLocation; char *nextNonNumericalChar; char *currentLocationInLine = line; int parsedInteger[4]; /* Example line from /proc/net/tcp or /proc/net/udp: * * sl local_address rem_address st tx_queue rx_queue * 21: 5A0A0B0A:CAC7 1C016E0A:0016 01 00000000:00000000 * * Algorithm: * * 1) Find the location of the first ':' * 2) Parse out the IP Address into an integer * 3) Find the location of the second ':' * 4) Parse out the port number. * 5) Find the location of the fourth ':' * 6) Parse out the rx_queue. */ for (i = 0; i < 4; i++) { currColonLocation = strchr(currentLocationInLine, ':'); /* We didn't find all the needed ':', so fail. */ if (currColonLocation == NULL) { return 0; } /* Parse out the integer, keeping the location of the next * non-numerical character. */ parsedInteger[i] = (int) strtol(++currColonLocation, &nextNonNumericalChar, 16); /* strtol()'s specifications specify that the second parameter * is set to the first parameter when a number couldn't be * parsed out. This means the parse was unsuccesful. */ if (nextNonNumericalChar == currColonLocation) { return 0; } /* Reset the currentLocationInLine to the last non-numerical * character, so that next iteration of this loop, we can find * the next colon location. */ currentLocationInLine = nextNonNumericalChar; } /* Extract out the segments of the IP Address. They are stored in * reverse network byte order. */ for (i = 0; i < NUM_IP_OCTETS; i++) { ipAddress[i] = parsedInteger[0] & (ipOctetExtractionMask << i*8); ipAddress[i] >>= i*8; } ipAddress[NUM_IP_OCTETS] = parsedInteger[1]; *rx_queue = parsedInteger[3]; return 1; } /* * Returns 1 if ipOne was found in ipArray, and 0 otherwise. * * The format of ipOne and ipArray are described in the comments of * get_socket_list_from_proto() in this file. * * */ static int match_ip_and_port(int *ipOne, int *ipArray, int sizeOf_ipArray) { int curIPAddrIdx; int curOctetIdx; int ipArrayIndex; /* Loop over every IP Address */ for (curIPAddrIdx = 0; curIPAddrIdx < sizeOf_ipArray; curIPAddrIdx++) { /* Check for octets that don't match. If one is found, skip the * rest. */ for (curOctetIdx = 0; curOctetIdx < NUM_IP_OCTETS + 1; curOctetIdx++) { /* We've encoded a 2D array as a 1D array. So find out * our position in the 1D array. */ ipArrayIndex = curIPAddrIdx * (NUM_IP_OCTETS + 1) + curOctetIdx; if (ipOne[curOctetIdx] != ipArray[ipArrayIndex]) { break; } } /* If the index from the inner loop is equal to NUM_IP_OCTETS * + 1, then that means that every octet (and the port with the * + 1) matched. */ if (curOctetIdx == NUM_IP_OCTETS + 1) { return 1; } } return 0; } /* * Returns the number of bytes waiting to be consumed on the network interfaces * assigned the IP Addresses specified in interfaceList. The check will be * limited to the TCP or UDP transport exclusively. Specifically: * * - If forTCP is non-zero, the check involves only the TCP transport. * - if forTCP is zero, the check involves only the UDP transport. * * Note: This only works on linux systems supporting the /proc/net/[tcp|udp] * interface. On other systems, zero will always be returned. */ static int get_used_waiting_queue( int forTCP, int *interfaceList, int listSize) { FILE *fp; char *fileToOpen; char lineBuffer[MAX_PROC_BUFFER]; int ipAddress[NUM_IP_OCTETS+1]; int rx_queue; int waitingQueueSize = 0; if (listSize==0 || interfaceList==NULL) return 0; /* Set up the file we want to open. */ if (forTCP) { fileToOpen = "/proc/net/tcp"; } else { fileToOpen = "/proc/net/udp"; } fp = fopen(fileToOpen, "r"); if (fp == NULL) { LM_DBG("Could not open %s. openserMsgQueu eDepth and its related" " alarms will not be available.\n", fileToOpen); return 0; } /* Read in every line of the file, parse out the ip address, port, and * rx_queue, and compare to our list of interfaces we are listening on. * Add up rx_queue for those lines which match our known interfaces. */ while (fgets(lineBuffer, MAX_PROC_BUFFER, fp)!=NULL) { /* Parse out the ip address, port, and rx_queue. */ if(parse_proc_net_line(lineBuffer, ipAddress, &rx_queue)) { /* Only add rx_queue if the line just parsed corresponds * to an interface we are listening on. We do this * check because it is possible that this system has * other network interfaces that OpenSER has been told * to ignore. */ if (match_ip_and_port(ipAddress, interfaceList, listSize)) { waitingQueueSize += rx_queue; } } } fclose(fp); return waitingQueueSize; } /* * Returns the sum of the number of bytes waiting to be consumed on all network * interfaces and transports that OpenSIPS is listening on. * * Note: This currently only works on systems supporting the /proc/net/[tcp|udp] * interface. On other systems, zero will always be returned. To change * this in the future, add an equivalent for get_used_waiting_queue(). */ int get_total_bytes_waiting(int only_proto) { static int *UDPList = NULL; static int *TCPList = NULL; static int *TLSList = NULL; static int numUDPSockets = -1; static int numTCPSockets = -1; static int numTLSSockets = -1; int bytesWaiting = 0; /* Extract out the IP address address for UDP, TCP, and TLS, keeping * track of the number of IP addresses from each transport */ if (numUDPSockets==-1) numUDPSockets = get_socket_list_from_proto(&UDPList, PROTO_UDP); if (numTCPSockets==-1) numTCPSockets = get_socket_list_from_proto(&TCPList, PROTO_TCP); if (numTLSSockets==-1) numTLSSockets = get_socket_list_from_proto(&TLSList, PROTO_TLS); /* Find out the number of bytes waiting on our interface list over all * UDP and TCP transports. */ if (only_proto==PROTO_NONE) { bytesWaiting += get_used_waiting_queue(0, UDPList, numUDPSockets); bytesWaiting += get_used_waiting_queue(1, TCPList, numTCPSockets); bytesWaiting += get_used_waiting_queue(1, TLSList, numTLSSockets); } else if (only_proto==PROTO_UDP) { bytesWaiting += get_used_waiting_queue(0, UDPList, numUDPSockets); } else if (only_proto==PROTO_TCP) { bytesWaiting += get_used_waiting_queue(1, TCPList, numTCPSockets); } else if (only_proto==PROTO_TLS) { bytesWaiting += get_used_waiting_queue(1, TLSList, numTLSSockets); } return bytesWaiting; } void print_aliases(void) { struct host_alias* a; for(a=aliases; a; a=a->next) if (a->port) printf(" %s: %.*s:%d\n", get_proto_name(a->proto), a->alias.len, a->alias.s, a->port); else printf(" %s: %.*s:*\n", get_proto_name(a->proto), a->alias.len, a->alias.s); } /* * Arguments : * sock - socket to have buffer increased * buff_choice - 0 for receive buff, 1 for send buff * buff_max - max size of socket buffer we are looking for * buff_increment - increment nr of bytes after reaching limit * * Returns : * 0 in case of success * 1 in case of failure */ int probe_max_sock_buff(int sock,int buff_choice,int buff_max,int buff_increment) { unsigned int optval, ioptval, ioptvallen, foptval, foptvallen, voptval, voptvallen; int phase=0; int buff_opt; char *info; if (buff_choice == 0) { info = "rcv"; buff_opt = SO_RCVBUF; } else if (buff_choice == 1) { info = "snd"; buff_opt = SO_SNDBUF; } else { LM_WARN("Called with unimplemented buff_choice - %d\n",buff_choice); return 1; } /* try to increase buffer size as much as we can */ ioptvallen=sizeof(ioptval); if (getsockopt( sock, SOL_SOCKET, buff_opt, (void*) &ioptval, &ioptvallen) == -1 ) { LM_ERR("getsockopt: %s\n", strerror(errno)); return -1; } if ( ioptval==0 ) { LM_DBG(" getsockopt: %s initially set to 0; resetting to %d\n", info,buff_increment ); ioptval=buff_increment; } else LM_DBG("getsockopt: %s is initially %d\n",info, ioptval ); for (optval=ioptval; ; ) { /* increase size; double in initial phase, add linearly later */ if (phase==0) optval <<= 1; else optval+=buff_increment; if (optval > maxbuffer){ if (phase==1) break; else { phase=1; optval >>=1; continue; } } LM_DBG("trying : %d\n", optval ); if (setsockopt( sock, SOL_SOCKET, buff_opt, (void*)&optval, sizeof(optval)) ==-1){ /* Solaris returns -1 if asked size too big; Linux ignores */ LM_DBG("setsockopt: SOL_SOCKET failed" " for %d, phase %d: %s\n", optval, phase, strerror(errno)); /* if setting buffer size failed and still in the aggressive phase, try less aggressively; otherwise give up */ if (phase==0) { phase=1; optval >>=1 ; continue; } else break; } /* verify if change has taken effect */ /* Linux note -- otherwise I would never know that; funny thing: Linux doubles size for which we asked in setsockopt */ voptvallen=sizeof(voptval); if (getsockopt( sock, SOL_SOCKET, buff_opt, (void*) &voptval, &voptvallen) == -1 ) { LM_ERR("getsockopt: %s\n", strerror(errno)); return -1; } else { LM_DBG("setting %s: set=%d,verify=%d\n",info, optval, voptval); if (voptval>=1 ; continue; } else break; } } } /* for ... */ foptvallen=sizeof(foptval); if (getsockopt( sock, SOL_SOCKET, buff_opt, (void*) &foptval, &foptvallen) == -1 ) { LM_ERR("getsockopt: %s\n", strerror(errno)); return -1; } LM_INFO("using %s buffer of %d kb\n",info, (foptval/1024)); return 0; } opensips-2.2.2/socket_info.h000066400000000000000000000253031300170765700160420ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to" the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*! * \file * \brief Find & manage listen addresses. * Contains code that initializes and handles server listen addresses * lists (struct socket_info). It is used mainly on startup. */ #ifndef socket_info_h #define socket_info_h #include #include "ip_addr.h" #include "dprint.h" #include "globals.h" #include "net/trans.h" #include "ut.h" /* struct socket_info is defined in ip_addr.h */ #define NUM_IP_OCTETS 4 #define PROTO_NAME_MAX_SIZE 8 /* CHANGEME if you define a bigger protocol name * currently hep_tcp - biggest proto */ int add_listen_iface(char* name, unsigned short port, unsigned short proto, char *adv_name, unsigned short adv_port, unsigned short children, enum si_flags flags); struct socket_info * new_sock_info(char* name, unsigned short port, unsigned short proto, char *adv_name, unsigned short adv_port, unsigned short children, enum si_flags flags); int new_sock2list(char* name, unsigned short port, unsigned short proto, char *adv_name, unsigned short adv_port, unsigned short children, enum si_flags flags, struct socket_info** list); int fix_socket_list(struct socket_info **); /* * This function will retrieve a list of all ip addresses and ports that * OpenSIPS is listening on, with respect to the transport protocol specified * with 'protocol'. * * The first parameter, ipList, is a pointer to a pointer. It will be assigned * new block of memory holding the IP Addresses and ports being listened to * with respect to 'protocol'. The array maps a 2D array into a 1 dimensional * space, and is layed out as follows: * * The first NUM_IP_OCTETS indices will be the IP address, and the next index * the port. So if NUM_IP_OCTETS is equal to 4 and there are two IP addresses * found, then: * * - ipList[0] will be the first octet of the first ip address * - ipList[3] will be the last octet of the first ip address. * - iplist[4] will be the port of the first ip address * - * - iplist[5] will be the first octet of the first ip address, * - and so on. * * The function will return the number of sockets which were found. This can * be used to index into ipList. * * NOTE: This function assigns a block of memory equal to: * * returnedValue * (NUM_IP_OCTETS + 1) * sizeof(int); * * Therefore it is CRUCIAL that you free ipList when you are done with * its contents, to avoid a nasty memory leak. */ int get_socket_list_from_proto(int **ipList, int protocol); /* * Returns the sum of the number of bytes waiting to be consumed on all network * interfaces and transports that OpenSIPS is listening on. * * Note: This currently only works on systems supporting the * /proc/net/[tcp|udp] interface. On other systems, zero will always * be returned. Details of why this is so can be found in * network_stats.c */ int get_total_bytes_waiting(int only_proto); void print_aliases(); struct socket_info* grep_sock_info(str* host, unsigned short port, unsigned short proto); struct socket_info* find_si(struct ip_addr* ip, unsigned short port, unsigned short proto); #define set_sip_defaults( _port, _proto) \ do { \ if (_proto==PROTO_NONE) _proto = PROTO_UDP; \ if (_port==0) { \ if (_proto==PROTO_TLS) _port = SIPS_PORT; else\ _port = SIP_PORT; \ } \ } while(0) /*! \brief helper function: * \return next protocol, if the last one is reached return 0 * \note useful for cycling on the supported protocols */ static inline int next_proto(unsigned short proto) { for( proto++ ; proto 4) && len != 7) return -1; i=PROTO2UINT(s[0], s[1], s[2]); switch(i){ case PROTO2UINT('u', 'd', 'p'): if(len==3) { *proto=PROTO_UDP; return 0; } break; case PROTO2UINT('t', 'c', 'p'): if(len==3) { *proto=PROTO_TCP; return 0; } break; case PROTO2UINT('t', 'l', 's'): if(len==3) { *proto=PROTO_TLS; return 0; } break; case PROTO2UINT('s', 'c', 't'): if(len==4 && (s[3]=='p' || s[3]=='P')) { *proto=PROTO_SCTP; return 0; } break; case PROTO2UINT('w', 's', 's'): if(len==3) { *proto=PROTO_WSS; return 0; } break; case PROTO2UINT('b', 'i', 'n'): if(len==3) { *proto=PROTO_BIN; return 0; } break; case PROTO2UINT('h', 'e', 'p'): if (len != 7 || s[3] != '_') return -1; j=PROTO2UINT(s[4], s[5], s[6]); switch (j) { case PROTO2UINT('u','d', 'p'): *proto=PROTO_HEP_UDP; return 0; case PROTO2UINT('t','c', 'p'): *proto=PROTO_HEP_TCP; return 0; default: return -1; } break; default: if(len==2 && (s[0]|0x20)=='w' && (s[1]|0x20)=='s') { *proto=PROTO_WS; return 0; } return -1; } return -1; } /*! \brief * parses [proto:]host[:port] where proto= udp|tcp|tls * \return 0 on success and -1 on failure */ inline static int parse_phostport(char* s, int slen, char** host, int* hlen, int* port, int* proto) { char* first; /* first ':' occurrence */ char* second; /* second ':' occurrence */ char* p; int bracket; str tmp; char* end; first=second=0; bracket=0; end = s + slen; /* find the first 2 ':', ignoring possible ipv6 addresses * (substrings between []) */ for(p=s; p1) goto error_brackets; break; case ']': bracket--; if (bracket<0) goto error_brackets; break; case ':': if (bracket==0){ if (first==0) first=p; else if( second==0) second=p; else goto error_colons; } break; } } if (p==s) return -1; if (*(p-1)==':') goto error_colons; if (first==0){ /* no ':' => only host */ *host=s; *hlen=(int)(p-s); *port=0; *proto=0; return 0; } if (second){ /* 2 ':' found => check if valid */ if (parse_proto((unsigned char*)s, first-s, proto)<0) goto error_proto; tmp.s = second+1; tmp.len = end - tmp.s; if (str2int( &tmp, (unsigned int*)port )==-1) goto error_port; *host=first+1; *hlen=(int)(second-*host); return 0; } /* only 1 ':' found => it's either proto:host or host:port */ tmp.s = first+1; tmp.len = end - tmp.s; if (str2int( &tmp, (unsigned int*)port )==-1) { /* invalid port => it's proto:host */ if (parse_proto((unsigned char*)s, first-s, proto)<0) goto error_proto; *port=0; *host=first+1; *hlen=(int)(p-*host); }else{ /* valid port => its host:port */ *proto=0; *host=s; *hlen=(int)(first-*host); } return 0; error_brackets: LM_ERR("too many brackets in %s\n", s); return -1; error_colons: LM_ERR(" too many colons in %s\n", s); return -1; error_proto: LM_ERR("bad protocol in %s\n", s); return -1; error_port: LM_ERR("bad port number in %s\n", s); return -1; } /* function will write the proto as string, starting from the p pointer. The new resulting proto will be returned (where writing ended) */ static inline char* proto2str(int proto, char *p) { switch (proto) { case PROTO_UDP: *(p++) = 'u'; *(p++) = 'd'; *(p++) = 'p'; break; case PROTO_TCP: *(p++) = 't'; *(p++) = 'c'; *(p++) = 'p'; break; case PROTO_TLS: *(p++) = 't'; *(p++) = 'l'; *(p++) = 's'; break; case PROTO_SCTP: *(p++) = 's'; *(p++) = 'c'; *(p++) = 't'; *(p++) = 'p'; break; case PROTO_WS: *(p++) = 'w'; *(p++) = 's'; break; case PROTO_WSS: *(p++) = 'w'; *(p++) = 's'; *(p++) = 's'; break; case PROTO_BIN: *(p++) = 'b'; *(p++) = 'i'; *(p++) = 'n'; break; case PROTO_HEP_UDP: *(p++) = 'h'; *(p++) = 'e'; *(p++) = 'p'; *(p++) = '_'; *(p++) = 'u'; *(p++) = 'd'; *(p++) = 'p'; break; case PROTO_HEP_TCP: *(p++) = 'h'; *(p++) = 'e'; *(p++) = 'p'; *(p++) = '_'; *(p++) = 't'; *(p++) = 'c'; *(p++) = 'p'; break; default: LM_CRIT("unsupported proto %d\n", proto); return 0; } return p; } #define MAX_SOCKET_STR ( 4 + 1 + IP_ADDR_MAX_STR_SIZE+1+INT2STR_MAX_LEN+1) #define sock_str_len(_sock) ( 3 + 1*((_sock)->proto==PROTO_SCTP) + 1 + \ (_sock)->address_str.len + 1 + (_sock)->port_no_str.len) static inline char* socket2str(struct socket_info *sock, char *s, int* len, int adv) { static char buf[MAX_SOCKET_STR]; char *p,*p1; if (s) { /* buffer provided -> check lenght */ if ( sock_str_len(sock) > *len ) { LM_ERR("buffer too short\n"); return 0; } p = p1 = s; } else { p = p1 = buf; } p = proto2str( sock->proto, p); if (p==NULL) return 0; *(p++) = ':'; if(adv) { memcpy( p, sock->adv_name_str.s, sock->adv_name_str.len); p += sock->adv_name_str.len; *(p++) = ':'; memcpy( p, sock->adv_port_str.s, sock->adv_port_str.len); p += sock->adv_port_str.len; } else { memcpy( p, sock->address_str.s, sock->address_str.len); p += sock->address_str.len; *(p++) = ':'; memcpy( p, sock->port_no_str.s, sock->port_no_str.len); p += sock->port_no_str.len; } *len = (int)(long)(p-p1); LM_DBG("<%.*s>\n",*len,p1); return p1; } #define get_sock_info_list(_proto) \ ((_proto>=PROTO_FIRST && _proto #include #include #include #include #include #include "net/api_proto.h" #include "net/proto_udp/proto_udp_handler.h" #include "net/proto_tcp/proto_tcp_handler.h" struct sr_module* modules=0; #ifdef STATIC_EXEC extern struct module_exports exec_exports; #endif #ifdef STATIC_TM extern struct module_exports tm_exports; #endif #ifdef STATIC_MAXFWD extern struct module_exports maxfwd_exports; #endif #ifdef STATIC_AUTH extern struct module_exports auth_exports; #endif #ifdef STATIC_RR extern struct module_exports rr_exports; #endif #ifdef STATIC_USRLOC extern struct module_exports usrloc_exports; #endif #ifdef STATIC_SL extern struct module_exports sl_exports; #endif char *mpath=NULL; char mpath_buf[256]; int mpath_len = 0; /* initializes statically built (compiled in) modules*/ int register_builtin_modules(void) { int ret; ret=0; #ifdef STATIC_TM ret=register_module(&tm_exports,"built-in", 0); if (ret<0) return ret; #endif #ifdef STATIC_EXEC ret=register_module(&exec_exports,"built-in", 0); if (ret<0) return ret; #endif #ifdef STATIC_MAXFWD ret=register_module(&maxfwd_exports, "built-in", 0); if (ret<0) return ret; #endif #ifdef STATIC_AUTH ret=register_module(&auth_exports, "built-in", 0); if (ret<0) return ret; #endif #ifdef STATIC_RR ret=register_module(&rr_exports, "built-in", 0); if (ret<0) return ret; #endif #ifdef STATIC_USRLOC ret=register_module(&usrloc_exports, "built-in", 0); if (ret<0) return ret; #endif #ifdef STATIC_SL ret=register_module(&sl_exports, "built-in", 0); if (ret<0) return ret; #endif return ret; } /* registers a module, register_f= module register functions * returns <0 on error, 0 on success */ int register_module(struct module_exports* e, char* path, void* handle) { int ret; struct sr_module* mod; ret=-1; /* add module to the list */ if ((mod=pkg_malloc(sizeof(struct sr_module)))==0){ LM_ERR("no more pkg memory\n"); ret=E_OUT_OF_MEM; goto error; } memset(mod,0, sizeof(struct sr_module)); mod->path=path; mod->handle=handle; mod->exports=e; mod->next=modules; modules=mod; /* register module pseudo-variables */ if (e->items) { LM_DBG("register_pv: %s\n", e->name); if (register_pvars_mod(e->name, e->items)!=0) { LM_ERR("failed to register pseudo-variables for module %s\n", e->name); pkg_free(mod); return -1; } } /* register all module dependencies */ if (e->deps) { ret = add_module_dependencies(mod); if (ret != 0) { LM_CRIT("failed to add module dependencies [%d]\n", ret); return ret; } } return 0; error: return ret; } #ifndef DLSYM_PREFIX /* define it to null */ #define DLSYM_PREFIX #endif static inline int version_control(struct module_exports* exp, char *path) { if ( !exp->version ) { LM_CRIT("BUG - version not defined in module <%s>\n", path ); return 0; } if ( !exp->compile_flags ) { LM_CRIT("BUG - compile flags not defined in module <%s>\n", path ); return 0; } if (strcmp(OPENSIPS_FULL_VERSION, exp->version)==0){ if (strcmp(OPENSIPS_COMPILE_FLAGS, exp->compile_flags)==0) return 1; else { LM_ERR("module compile flags mismatch for %s " " \ncore: %s \nmodule: %s\n", exp->name, OPENSIPS_COMPILE_FLAGS, exp->compile_flags); return 0; } } LM_ERR("module version mismatch for %s; core: %s; module: %s\n", exp->name, OPENSIPS_FULL_VERSION, exp->version ); return 0; } /* returns 0 on success , <0 on error */ int sr_load_module(char* path) { void* handle; unsigned int moddlflags; char* error; struct module_exports* exp; struct sr_module* t; /* load module */ handle=dlopen(path, OPENSIPS_DLFLAGS); /* resolve all symbols now */ if (handle==0){ LM_ERR("could not open module <%s>: %s\n", path, dlerror() ); goto error; } /* check for duplicates */ for(t=modules;t; t=t->next){ if (t->handle==handle){ LM_WARN("attempting to load the same module twice (%s)\n", path); goto skip; } } /* import module interface */ exp = (struct module_exports*)dlsym(handle, DLSYM_PREFIX "exports"); if ( (error =(char*)dlerror())!=0 ){ LM_ERR("load_module: %s\n", error); goto error1; } if(exp->dlflags!=DEFAULT_DLFLAGS && exp->dlflags!=OPENSIPS_DLFLAGS) { moddlflags = exp->dlflags; dlclose(handle); LM_DBG("reloading module %s with flags %d\n", path, moddlflags); handle = dlopen(path, moddlflags); if (handle==0){ LM_ERR("could not open module <%s>: %s\n", path, dlerror() ); goto error; } exp = (struct module_exports*)dlsym(handle, DLSYM_PREFIX "exports"); if ( (error =(char*)dlerror())!=0 ){ LM_ERR("failed to load module : %s\n", error); goto error1; } } /* version control */ if (!version_control(exp, path)) { exit(0); } /* launch register */ if (register_module(exp, path, handle)<0) goto error1; return 0; error1: dlclose(handle); error: skip: return -1; } /* built-in modules with static exports */ struct static_modules { str name; struct module_exports *exp; }; struct static_modules static_modules[] = { { str_init(PROTO_PREFIX "udp"), &proto_udp_exports }, { str_init(PROTO_PREFIX "tcp"), &proto_tcp_exports }, }; static int load_static_module(char *path) { int len = strlen(path); char *end = path + len; struct sr_module* t; unsigned int i; /* eliminate the .so, if found */ if (len > 3 && strncmp(end - 3, ".so", 3)==0) { end -= 3; len -= 3; } /* we check whether the protocol is found within the static_modules */ for (i = 0; i < (sizeof(static_modules)/sizeof(static_modules[0])); i++) { if (len >= static_modules[i].name.len && /* the path ends in the module's name */ memcmp(end - static_modules[i].name.len, static_modules[i].name.s, static_modules[i].name.len) == 0 && /* check if the previous char is '/' or nothing */ (len == static_modules[i].name.len || (*(end-len-1) == '/'))) { /* yey, found the module - check if it was loaded twice */ for(t=modules;t; t=t->next){ if (t->handle==static_modules[i].exp){ LM_WARN("attempting to load the same module twice (%s)\n", path); return 0; } } /* version control */ if (!version_control(static_modules[i].exp, path)) { exit(0); } /* launch register */ if (register_module(static_modules[i].exp, path, static_modules[i].exp)<0) return -1; return 0; } } return -1; } /* returns 0 on success , <0 on error */ int load_module(char* name) { int i_tmp; struct stat statf; /* if this is a static module, load it directly */ if (load_static_module(name) == 0) return 0; if(*name!='/' && mpath!=NULL && strlen(name)+mpath_len<255) { strcpy(mpath_buf+mpath_len, name); if (stat(mpath_buf, &statf) == -1 || S_ISDIR(statf.st_mode)) { i_tmp = strlen(mpath_buf); if(strchr(name, '/')==NULL && strncmp(mpath_buf+i_tmp-3, ".so", 3)==0) { if(i_tmp+strlen(name)<255) { strcpy(mpath_buf+i_tmp-3, "/"); strcpy(mpath_buf+i_tmp-2, name); if (stat(mpath_buf, &statf) == -1) { mpath_buf[mpath_len]='\0'; LM_ERR("module '%s' not found in '%s'\n", name, mpath_buf); return -1; } } else { LM_ERR("failed to load module - path too long\n"); return -1; } } else { LM_ERR("failed to load module - not found\n"); return -1; } } LM_DBG("loading module %s\n", mpath_buf); if (sr_load_module(mpath_buf)!=0){ LM_ERR("failed to load module\n"); return -1; } mpath_buf[mpath_len]='\0'; } else { LM_DBG("loading module %s\n", name); if (sr_load_module(name)!=0){ LM_ERR("failed to load module\n"); return -1; } } return 0; } /* searches the module list and returns pointer to the "name" function or * 0 if not found * flags parameter is OR value of all flags that must match */ cmd_function find_export(char* name, int param_no, int flags) { cmd_export_t* cmd; cmd = find_cmd_export_t(name, param_no, flags); if (cmd==0) return 0; return cmd->function; } /* searches the module list and returns pointer to the "name" cmd_export_t * structure or 0 if not found * In order to find the module the name, flags parameter number and type and * the value of all flags in the config must match to the module export */ cmd_export_t* find_cmd_export_t(char* name, int param_no, int flags) { struct sr_module* t; cmd_export_t* cmd; for(t=modules;t;t=t->next){ for(cmd=t->exports->cmds; cmd && cmd->name; cmd++){ if((strcmp(name, cmd->name)==0)&& (cmd->param_no==param_no) && ((cmd->flags & flags) == flags) ){ LM_DBG("found <%s>(%d) in module %s [%s]\n", name, param_no, t->exports->name, t->path); return cmd; } } } LM_DBG("<%s> not found \n", name); return 0; } /* searches the module list and returns pointer to the async "name" cmd_export_t * structure or 0 if not found * In order to find the module the name, flags parameter number in the config * must match to the module export */ acmd_export_t* find_acmd_export_t(char* name, int param_no) { struct sr_module* t; acmd_export_t* cmd; for(t=modules;t;t=t->next){ for(cmd=t->exports->acmds; cmd && cmd->name; cmd++){ if((strcmp(name, cmd->name)==0)&& (cmd->param_no==param_no) ){ LM_DBG("found async <%s>(%d) in module %s [%s]\n", name, param_no, t->exports->name, t->path); return cmd; } } } LM_DBG("async <%s> not found \n", name); return 0; } /* * searches the module list and returns pointer to "name" function in module * "mod" or 0 if not found * flags parameter is OR value of all flags that must match */ cmd_function find_mod_export(char* mod, char* name, int param_no, int flags) { struct sr_module* t; cmd_export_t* cmd; for (t = modules; t; t = t->next) { if (strcmp(t->exports->name, mod) == 0) { for (cmd = t->exports->cmds; cmd && cmd->name; cmd++) { if ((strcmp(name, cmd->name) == 0) && (cmd->param_no == param_no) && ((cmd->flags & flags) == flags) ){ LM_DBG("found <%s> in module %s [%s]\n", name, t->exports->name, t->path); return cmd->function; } } } } LM_DBG("<%s> in module %s not found\n", name, mod); return 0; } void* find_param_export(char* mod, char* name, modparam_t type) { struct sr_module* t; param_export_t* param; for(t = modules; t; t = t->next) { if (strcmp(mod, t->exports->name) == 0) { for(param=t->exports->params;param && param->name ; param++) { if ((strcmp(name, param->name) == 0) && (param->type == type)) { LM_DBG("found <%s> in module %s [%s]\n", name, t->exports->name, t->path); return param->param_pointer; } } } } LM_DBG("parameter <%s> or module <%s> not found\n", name, mod); return 0; } void destroy_modules(void) { struct sr_module* t, *foo; t = modules; while (t) { foo = t->next; if (t->init_done && t->exports && t->exports->destroy_f) t->exports->destroy_f(); pkg_free(t); t = foo; } modules = NULL; } /* recursive module child initialization; (recursion is used to process the module linear list in the same order in which modules are loaded in config file */ static int init_mod_child( struct sr_module* m, int rank, char *type ) { if (m) { /* iterate through the list; if error occurs, propagate it up the stack */ if (init_mod_child(m->next, rank, type)!=0) return -1; if (m->exports && m->exports->init_child_f) { LM_DBG("type=%s, rank=%d, module=%s\n", type, rank, m->exports->name); if (m->exports->init_child_f(rank)<0) { LM_ERR("failed to initializing module %s, rank %d\n", m->exports->name,rank); return -1; } else { /* module correctly initialized */ return 0; } } /* no init function -- proceed with success */ return 0; } else { /* end of list */ return 0; } } /* * per-child initialization */ int init_child(int rank) { char* type; type = 0; switch(rank) { case PROC_MAIN: type = "PROC_MAIN"; break; case PROC_TIMER: type = "PROC_TIMER"; break; case PROC_MODULE: type = "PROC_MODULE"; break; case PROC_TCP_MAIN: type = "PROC_TCP_MAIN"; break; case PROC_BIN: type = "PROC_BIN"; break; } if (!type) { if (rank>0) type = "CHILD"; else type = "UNKNOWN"; } return init_mod_child(modules, rank, type); } /* recursive module initialization; (recursion is used to process the module linear list in the same order in which modules are loaded in config file */ static int init_mod( struct sr_module* m, int skip_others) { struct sr_module_dep *dep; if (m) { /* iterate through the list; if error occurs, propagate it up the stack */ if (!skip_others && init_mod(m->next, 0) != 0) return -1; /* our module might have been already init'ed through dependencies! */ if (m->init_done) return 0; if (!m->exports) return 0; /* make sure certain modules get loaded before this one */ for (dep = m->sr_deps; dep; dep = dep->next) { if (!dep->mod->init_done) if (init_mod(dep->mod, 1) != 0) return -1; } if (m->exports->init_f) { LM_DBG("initializing module %s\n", m->exports->name); if (m->exports->init_f()!=0) { LM_ERR("failed to initialize module %s\n", m->exports->name); return -1; } } m->init_done = 1; /* no init function -- proceed further */ #ifdef STATISTICS if (m->exports->stats) { LM_DBG("registering stats for %s\n", m->exports->name); if (register_module_stats(m->exports->name,m->exports->stats)!=0) { LM_ERR("failed to registering " "statistics for module %s\n", m->exports->name); return -1; } } #endif /* register MI functions */ if (m->exports->mi_cmds) { LM_DBG("register MI for %s\n", m->exports->name); if (register_mi_mod(m->exports->name,m->exports->mi_cmds)!=0) { LM_ERR("failed to register MI functions for module %s\n", m->exports->name); } } /* proceed with success */ return 0; } else { /* end of list */ return 0; } } /* * Initialize all loaded modules, the initialization * is done *AFTER* the configuration file is parsed */ int init_modules(void) { int ret; ret = init_mod(modules, 0); free_module_dependencies(modules); return ret; } /* Returns 1 if the module with name 'name' is loaded, and zero otherwise. */ int module_loaded(char *name) { struct sr_module *currentMod; for (currentMod=modules; currentMod; currentMod=currentMod->next) { if (strcasecmp(name,currentMod->exports->name)==0) { return 1; } } return 0; } /* Counts the additional the number of processes requested by modules */ int count_module_procs(void) { struct sr_module *m; unsigned int cnt; unsigned int n; for( m=modules,cnt=0 ; m ; m=m->next) { if (m->exports->procs) { for( n=0 ; m->exports->procs[n].name ; n++) if (m->exports->procs[n].function) cnt += m->exports->procs[n].no; } } LM_DBG("modules require %d extra processes\n",cnt); return cnt; } int start_module_procs(void) { struct sr_module *m; unsigned int n; unsigned int l; pid_t x; for( m=modules ; m ; m=m->next) { if (m->exports->procs==NULL) continue; for( n=0 ; m->exports->procs[n].name ; n++) { if ( !m->exports->procs[n].no || !m->exports->procs[n].function ) continue; /* run pre-fork function */ if (m->exports->procs[n].pre_fork_function) if (m->exports->procs[n].pre_fork_function()!=0) { LM_ERR("pre-fork function failed for process \"%s\" " "in module %s\n", m->exports->procs[n].name, m->exports->name); return -1; } /* fork the processes */ for ( l=0; lexports->procs[n].no ; l++) { LM_DBG("forking process \"%s\"/%d for module %s\n", m->exports->procs[n].name, l, m->exports->name); x = internal_fork(m->exports->procs[n].name); if (x<0) { LM_ERR("failed to fork process \"%s\"/%d for module %s\n", m->exports->procs[n].name, l, m->exports->name); return -1; } else if (x==0) { /* new process */ /* initialize the process for the rest of the modules */ if ( m->exports->procs[n].flags&PROC_FLAG_INITCHILD ) { if (init_child(PROC_MODULE) < 0) { LM_ERR("error in init_child for PROC_MODULE\n"); report_failure_status(); exit(-1); } report_conditional_status( (!no_daemon_mode), 0); } else clean_write_pipeend(); /* run the function */ m->exports->procs[n].function(l); /* we shouldn't get here */ exit(0); } } /* run post-fork function */ if (m->exports->procs[n].post_fork_function) if (m->exports->procs[n].post_fork_function()!=0) { LM_ERR("post-fork function failed for process \"%s\" " "in module %s\n", m->exports->procs[n].name, m->exports->name); return -1; } } } return 0; } opensips-2.2.2/sr_module.h000066400000000000000000000207021300170765700155260ustar00rootroot00000000000000/* * modules/plug-in structures declarations * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-10 changed module exports interface: added struct cmd_export * and param_export (andrei) * 2003-03-16 Added flags field to cmd_export_ (janakj) * 2003-04-05 s/reply_route/failure_route, onreply_route introduced (jiri) * 2004-03-12 extra flag USE_FUNC_PARAM added to modparam type - * instead of copying the param value, a func is called (bogdan) * 2004-09-19 switched to version.h for the module versions checks (andrei) * 2004-12-03 changed param_func_t to (modparam_t, void*), killed * param_func_param_t (andrei) * 2006-03-02 added find_cmd_export_t(), killed find_exportp() (bogdan) * 2006-11-28 added module_loaded() (Jeffrey Magder - SOMA Networks) */ /*! * \file * \brief modules/plug-in structures declarations */ #ifndef sr_module_h #define sr_module_h #include #include "parser/msg_parser.h" /* for sip_msg */ #include "statistics.h" #include "mi/mi.h" #include "pvar.h" #include "version.h" #include "route.h" #include "async.h" #include "sr_module_deps.h" typedef struct module_exports* (*module_register)(); typedef int (*cmd_function)(struct sip_msg*, char*, char*, char*, char*, char*, char*); typedef int (*acmd_function)(struct sip_msg*, async_resume_module **, void **, char*, char*, char*, char*, char*, char*); typedef int (*fixup_function)(void** param, int param_no); typedef int (*free_fixup_function)(void** param, int param_no); typedef int (*response_function)(struct sip_msg*); typedef void (*destroy_function)(); typedef int (*init_function)(void); typedef int (*child_init_function)(int rank); #define STR_PARAM (1U<<0) /* String parameter type */ #define INT_PARAM (1U<<1) /* Integer parameter type */ #define USE_FUNC_PARAM (1U<<(8*sizeof(int)-1)) #define PARAM_TYPE_MASK(_x) ((_x)&(~USE_FUNC_PARAM)) typedef unsigned int modparam_t; typedef int (*param_func_t)( modparam_t type, void* val); typedef void (*mod_proc)(int no); typedef int (*mod_proc_wrapper)(); /* Macros - used as rank in child_init function */ #define PROC_MAIN 0 /* Main opensips process */ #define PROC_TIMER -1 /* Timer attendant process */ #define PROC_MODULE -2 /* Extra process requested by modules */ #define PROC_TCP_MAIN -4 /* TCP main process */ #define PROC_BIN -8 /* Any binary interface listener */ #define DEFAULT_DLFLAGS 0 /* value that signals to module loader to use default dlopen flags in opensips */ #ifndef RTLD_NOW /* for openbsd */ #define RTLD_NOW DL_LAZY #endif #define OPENSIPS_DLFLAGS RTLD_NOW #define MODULE_VERSION \ OPENSIPS_FULL_VERSION, \ OPENSIPS_COMPILE_FLAGS #define PROC_FLAG_INITCHILD (1<<0) struct cmd_export_ { char* name; /* null terminated command name */ cmd_function function; /* pointer to the corresponding function */ int param_no; /* number of parameters used by the function */ fixup_function fixup; /* pointer to the function called to "fix" the parameters */ free_fixup_function free_fixup; /* pointer to the function called to free the "fixed" parameters */ int flags; /* Function flags */ }; struct acmd_export_ { char* name; /* null terminated command name */ acmd_function function; /* pointer to the corresponding function */ int param_no; /* number of parameters used by the function */ fixup_function fixup; /* pointer to the function called to "fix" the parameters */ }; struct param_export_ { char* name; /*!< null terminated param. name */ modparam_t type; /*!< param. type */ void* param_pointer; /*!< pointer to the param. memory location */ }; struct proc_export_ { char *name; mod_proc_wrapper pre_fork_function; mod_proc_wrapper post_fork_function; mod_proc function; unsigned int no; unsigned int flags; }; typedef struct dep_export_ { module_dependency_t md[MAX_MOD_DEPS]; modparam_dependency_t mpd[]; } dep_export_t; typedef struct cmd_export_ cmd_export_t; typedef struct acmd_export_ acmd_export_t; typedef struct proc_export_ proc_export_t; struct sr_module{ char* path; void* handle; int init_done; struct module_exports* exports; /* a list of module dependencies */ struct sr_module_dep *sr_deps; struct sr_module* next; }; struct module_exports{ char* name; /*!< null terminated module name */ enum module_type type; char *version; /*!< module version */ char *compile_flags; /*!< compile flags used on the module */ unsigned int dlflags; /*!< flags for dlopen */ dep_export_t *deps; /*!< module and modparam dependencies */ cmd_export_t* cmds; /*!< null terminated array of the exported commands */ acmd_export_t* acmds; /*!< null terminated array of the exported async commands */ param_export_t* params; /*!< null terminated array of the exported module parameters */ stat_export_t* stats; /*!< null terminated array of the exported module statistics */ mi_export_t* mi_cmds; /*!< null terminated array of the exported MI functions */ pv_export_t* items; /*!< null terminated array of the exported module items (pseudo-variables) */ proc_export_t* procs; /*!< null terminated array of the additional processes reqired by the module */ init_function init_f; /*!< Initialization function */ response_function response_f; /*!< function used for responses, returns yes or no; can be null */ destroy_function destroy_f; /*!< function called when the module should be "destroyed", e.g: on opensips exit */ child_init_function init_child_f;/*!< function called by all processes after the fork */ }; extern char *mpath; extern char mpath_buf[]; extern int mpath_len; struct sr_module* modules; /*!< global module list*/ int register_builtin_modules(); int register_module(struct module_exports*, char*, void*); int load_module(char* name); cmd_export_t* find_cmd_export_t(char* name, int param_no, int flags); acmd_export_t* find_acmd_export_t(char* name, int param_no); cmd_function find_export(char* name, int param_no, int flags); cmd_function find_mod_export(char* mod, char* name, int param_no, int flags); void destroy_modules(); int init_child(int rank); int init_modules(void); /*! \brief * Find a parameter with given type and return it's * address in memory * If there is no such parameter, NULL is returned */ void* find_param_export(char* mod, char* name, modparam_t type); /* modules function prototypes: * struct module_exports* mod_register(); (type module_register) * int foo_cmd(struct sip_msg* msg, char* param); * - returns >0 if ok , <0 on error, 0 to stop processing (==DROP) * int response_f(struct sip_msg* msg) * - returns >0 if ok, 0 to drop message */ /*! \brief Check if module is loaded * \return Returns 1 if the module with name 'name' is loaded, and zero otherwise. */ int module_loaded(char *name); /*! \brief Gets a specific module * \return Returns the module if the module with name 'name' is loaded, and * NULL otherwise */ /*! \brief Counts the additional the number of processes requested by modules */ int count_module_procs(); /*! \brief Forks and starts the additional processes required by modules */ int start_module_procs(); #endif opensips-2.2.2/sr_module_deps.c000066400000000000000000000200351300170765700165330ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2014-05-12 removed all module ordering requirements at script level (liviu) */ #include #include #include #include "sr_module_deps.h" #include "dprint.h" #include "error.h" #include "mem/mem.h" #include "sr_module.h" #include "pt.h" /* the list head of unsolved module dependencies: struct sr_module ----> "module_name" */ static struct sr_module_dep unsolved_deps; #define mod_type_to_string(type) \ (type == MOD_TYPE_NULL ? NULL : \ type == MOD_TYPE_SQLDB ? "sqldb module" : \ type == MOD_TYPE_CACHEDB ? "cachedb module" : \ type == MOD_TYPE_AAA ? "aaa module" : \ "module") module_dependency_t *alloc_module_dep(enum module_type mod_type, char *mod_name, enum dep_type dep_type) { module_dependency_t *md; /* also allocate a zeroed entry in the end */ md = pkg_malloc(2 * sizeof *md); if (!md) { LM_ERR("out of pkg\n"); return NULL; } memset(md, 0, 2 * sizeof *md); md->mod_type = mod_type; md->mod_name = mod_name; md->type = dep_type; return md; } module_dependency_t *get_deps_sqldb_url(param_export_t *param) { char *db_url = *(char **)param->param_pointer; if (param->type & USE_FUNC_PARAM) return alloc_module_dep(MOD_TYPE_SQLDB, NULL, DEP_WARN); if (!db_url || strlen(db_url) == 0) return NULL; return alloc_module_dep(MOD_TYPE_SQLDB, NULL, DEP_WARN); } static int add_module_dependency(struct sr_module *mod, module_dependency_t *dep, char *script_param) { struct sr_module_dep *md; int len; LM_DBG("adding type %d dependency %s - (%s %s)\n", dep->type, mod->exports->name, mod_type_to_string(dep->mod_type), dep->mod_name); len = dep->mod_name ? strlen(dep->mod_name) : 0; md = pkg_malloc(sizeof *md + len + 1); if (!md) { LM_CRIT("out of pkg mem\n"); return -1; } memset(md, 0, sizeof *md + len + 1); md->mod = mod; md->mod_type = dep->mod_type; md->type = dep->type; if (dep->mod_name) { md->dep.s = (char *)(md + 1); md->dep.len = len; memcpy(md->dep.s, dep->mod_name, len); } if (script_param) md->script_param = script_param; md->next = unsolved_deps.next; unsolved_deps.next = md; return 0; } /* * register all OpenSIPS module dependencies of a single module parameter */ int add_modparam_dependencies(struct sr_module *mod, param_export_t *param) { struct sr_module_dep *it, *tmp; module_dependency_t *md; modparam_dependency_t *mpd; struct module_dependency *(*get_deps_f)(param_export_t *param) = NULL; if (!mod->exports->deps) return 0; /* lookup this parameter's dependency fetching function */ for (mpd = mod->exports->deps->mpd; mpd->script_param; mpd++) { if (strcmp(mpd->script_param, param->name) == 0) get_deps_f = mpd->get_deps_f; } /* 98% of OpenSIPS's modparams will stop here */ if (!get_deps_f) return 0; /* clear previous entries in case this parameter is set multiple times */ for (it = &unsolved_deps; it->next; it = it->next) { if (strcmp(it->next->mod->exports->name, mod->exports->name) == 0 && (it->next->script_param && strcmp(it->next->script_param, param->name) == 0)) { tmp = it->next; it->next = it->next->next; pkg_free(tmp); } } md = get_deps_f(param); if (!md) return 0; LM_DBG("adding modparam dependencies:\n"); for (; md->mod_type != MOD_TYPE_NULL; md++) { LM_DBG("dependency found: %s ---> ( %s %s )\n", mod->exports->name, mod_type_to_string(md->mod_type), md->mod_name); if (add_module_dependency(mod, md, param->name) != 0) { LM_ERR("failed to add dep!\n"); return E_BUG; } } return 0; } /* * register all OpenSIPS module dependencies of a single module */ int add_module_dependencies(struct sr_module *mod) { module_dependency_t *md; for (md = mod->exports->deps->md; md->mod_type != MOD_TYPE_NULL; md++) { if (add_module_dependency(mod, md, NULL) != 0) { LM_ERR("failed to add mod dep\n"); return -1; } } return 0; } int solve_module_dependencies(struct sr_module *modules) { struct sr_module_dep *md, *it; struct sr_module *this, *mod; enum module_type mod_type; enum dep_type dep_type; int dep_solved; /* * now that we've loaded all shared libraries, * we can attempt to solve each dependency */ for (it = unsolved_deps.next; it; ) { md = it; it = it->next; LM_DBG("solving dependency %s -> %s %.*s\n", md->mod->exports->name, mod_type_to_string(md->mod_type), md->dep.len, md->dep.s); dep_type = md->type; /* * for generic dependencies (e.g. dialog depends on MOD_TYPE_SQLDB), * first load all modules of given type */ if (!md->dep.s) { this = md->mod; mod_type = md->mod_type; for (dep_solved = 0, mod = modules; mod; mod = mod->next) { if (mod != this && mod->exports->type == mod_type) { if (!md) { md = pkg_malloc(sizeof *md); if (!md) { LM_ERR("no more pkg\n"); return -1; } memset(md, 0, sizeof *md); } /* * re-purpose this structure by linking it into a module's * list of dependencies (will be used at init time) * * md->mod used to point to (highlighted with []): * [sr_module A] ---> "mod_name" * * now, the dependency is solved. md->mod will point to: * sr_module A ---> [sr_module B] */ md->mod = mod; md->next = this->sr_deps; this->sr_deps = md; md = NULL; dep_solved++; } } } else { for (dep_solved = 0, mod = modules; mod; mod = mod->next) { if (strcmp(mod->exports->name, md->dep.s) == 0) { /* quick sanity check */ if (mod->exports->type != md->mod_type) LM_BUG("[%.*s %d] -> [%s %d]\n", md->dep.len, md->dep.s, md->mod_type, mod->exports->name, mod->exports->type); /* same re-purposing technique as above */ md->next = md->mod->sr_deps; md->mod->sr_deps = md; md->mod = mod; dep_solved++; break; } } } /* treat unmet dependencies using the intended behaviour */ if (!dep_solved) { switch (dep_type) { case DEP_SILENT: LM_DBG("module %s depends on %s%s%s%.*s, but %s loaded!\n", md->mod->exports->name, md->dep.len == 0 ? ((md->mod_type == MOD_TYPE_SQLDB || md->mod_type == MOD_TYPE_AAA) ? "an " : md->mod_type == MOD_TYPE_CACHEDB ? "a " : "") : "", mod_type_to_string(md->mod_type), md->dep.len == 0 ? "" : " ", md->dep.len, md->dep.s, md->dep.len == 0 ? "none was" : "it was not"); break; case DEP_WARN: case DEP_ABORT: LM_WARN("module %s depends on %s%s%s%.*s, but %s loaded!\n", md->mod->exports->name, md->dep.len == 0 ? ((md->mod_type == MOD_TYPE_SQLDB || md->mod_type == MOD_TYPE_AAA) ? "an " : md->mod_type == MOD_TYPE_CACHEDB ? "a " : "") : "", mod_type_to_string(md->mod_type), md->dep.len == 0 ? "" : " ", md->dep.len, md->dep.s, md->dep.len == 0 ? "none was" : "it was not"); break; } pkg_free(md); if (dep_type == DEP_ABORT) return -1; } } return 0; } /* after all modules are properly loaded, free all sr_module_dep structures */ void free_module_dependencies(struct sr_module *modules) { struct sr_module_dep *aux; struct sr_module *mod; for (mod = modules; mod; mod = mod->next) { while (mod->sr_deps) { aux = mod->sr_deps; mod->sr_deps = aux->next; pkg_free(aux); } } } opensips-2.2.2/sr_module_deps.h000066400000000000000000000100451300170765700165400ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * ------- * 2014-05-12 removed all module ordering requirements at script level (liviu) */ #ifndef SR_MODULE_DEPS_H #define SR_MODULE_DEPS_H /* * Description: * * - the core module dependencies code simply helps rearrange the module loading * order so that the dependencies of each OpenSIPS module are satisfied * * - a module may specify dependencies in two ways: * * module -> module (if X -> Y, load Y before X) - most common * * modparam -> module (if a parameter of module X has a certain value, * ensure module Y loads first) * * - a dependency can be of two types: * * straightforward dependency ("acc" depends on "tm") * * generic dependency ("acc" depends on any MOD_TYPE_SQLDB module) * * - both module and modparam dependencies are populated within exports->deps * * - for the latter, a function must be provided for each modparam: * * input: the parameter's populated param_export_t struct * * output: NULL / module dependency resulted from the value of the modparam * * when dependencies are not satisfied (e.g. depending module not present), * OpenSIPS may throw a warning, abort or not do anything at all * * For a complete usage example, please refer to the "acc" module * * Developer Notes: * - circular module dependencies are possible and not detected! * - it is up to the module writers to prevent such side effects */ #include "str.h" #define MAX_MOD_DEPS 10 typedef struct param_export_ param_export_t; /* core + module level structures */ enum module_type { MOD_TYPE_NULL, /* for iteration purposes */ MOD_TYPE_DEFAULT, MOD_TYPE_SQLDB, MOD_TYPE_CACHEDB, MOD_TYPE_AAA, }; /* behaviour at startup if the dependency is not met */ enum dep_type { DEP_SILENT, /* load re-ordering only if possible */ DEP_WARN, /* load re-ordering, and a warning if module not found */ DEP_ABORT, /* load re-ordering, and shut down if module not found */ }; typedef struct module_dependency { enum module_type mod_type; char *mod_name; /* as found in "module_exports" */ enum dep_type type; } module_dependency_t; typedef struct modparam_dependency { char *script_param; /* module parameter at script level */ /* return value must be allocated in pkg memory! */ struct module_dependency *(*get_deps_f)(param_export_t *param); } modparam_dependency_t; /* helps to avoid duplicate code when writing "get_deps_f" functions */ module_dependency_t *alloc_module_dep(enum module_type mod_type, char *mod_name, enum dep_type dep_type); /* commonly used modparam dependency functions */ /** * get_deps_sqldb_url - commonly used by modules which use SQL DB URLs * * Behaviour: * - imposes a generic MOD_TYPE_SQLDB dependency only when the URL is set * (strlen(url) > 0) */ module_dependency_t *get_deps_sqldb_url(param_export_t *param); /* core level structures and functions */ struct sr_module_dep { struct sr_module *mod; char *script_param; enum module_type mod_type; enum dep_type type; str dep; struct sr_module_dep *next; }; int add_modparam_dependencies(struct sr_module *mod, param_export_t *param); int add_module_dependencies(struct sr_module *mod); int solve_module_dependencies(struct sr_module *modules); void free_module_dependencies(struct sr_module *modules); #endif opensips-2.2.2/statistics.c000066400000000000000000000507231300170765700157300ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * Copyright (C) 2010-2012 OpenSIPS Solutions * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * History: * --------- * 2006-01-16 first version (bogdan) * 2006-11-28 added get_stat_var_from_num_code() (Jeffrey Magder - * SOMA Networks) * 2009-04-23 function var accepts a context parameter (bogdan) * 2012-09-21 support for dynamic statistics (created of demand at runtime) * (bogdan) */ /*! * \file * \brief Statistics support */ #include #include "mem/shm_mem.h" #include "mi/mi.h" #include "ut.h" #include "dprint.h" #include "locking.h" #include "core_stats.h" #include "statistics.h" #include "pt.h" #include "atomic.h" #include "globals.h" #include "rw_locking.h" #ifdef STATISTICS static stats_collector *collector = NULL; static int stats_ready; static struct mi_root *mi_get_stats(struct mi_root *cmd, void *param); static struct mi_root *mi_list_stats(struct mi_root *cmd, void *param); static struct mi_root *mi_reset_stats(struct mi_root *cmd, void *param); static mi_export_t mi_stat_cmds[] = { { "get_statistics", "prints the statistics (all, group or one) realtime values.", mi_get_stats, 0 , 0, 0 }, { "list_statistics", "lists all the registered statistics and their types", mi_list_stats, 0 , 0, 0 }, { "reset_statistics", "resets the value of a statistic variable", mi_reset_stats, 0 , 0, 0 }, { 0, 0, 0, 0, 0, 0} }; #ifdef NO_ATOMIC_OPS #warning STATISTICS: Architecture with no support for atomic operations. \ Using Locks!! gen_lock_t *stat_lock = 0; #endif #define stat_hash(_s) core_hash( _s, 0, STATS_HASH_SIZE) /*! \brief * Returns the statistic associated with 'numerical_code' and 'out_codes'. * Specifically: * * - if out_codes is nonzero, then the stat_var for the number of messages * _sent out_ with the 'numerical_code' will be returned if it exists. * - otherwise, the stat_var for the number of messages _received_ with the * 'numerical_code' will be returned, if the stat exists. */ stat_var *get_stat_var_from_num_code(unsigned int numerical_code, int out_codes) { static char msg_code[INT2STR_MAX_LEN+4]; str stat_name; stat_name.s = int2bstr( (unsigned long)numerical_code, msg_code, &stat_name.len); stat_name.s[stat_name.len++] = '_'; if (out_codes) { stat_name.s[stat_name.len++] = 'o'; stat_name.s[stat_name.len++] = 'u'; stat_name.s[stat_name.len++] = 't'; } else { stat_name.s[stat_name.len++] = 'i'; stat_name.s[stat_name.len++] = 'n'; } return get_stat(&stat_name); } char *build_stat_name( str* prefix, char *var_name) { int n; char *s; char *p; n = prefix->len + 1 + strlen(var_name) + 1; s = (char*)shm_malloc( n ); if (s==0) { LM_ERR("no more shm mem\n"); return 0; } memcpy( s, prefix->s, prefix->len); p = s + prefix->len; *(p++) = '-'; memcpy( p , var_name, strlen(var_name)); p += strlen(var_name); *(p++) = 0; return s; } unsigned int calc_udp_load(void *val) { return ( get_stat_val((stat_var*)val) * 100) / *((int*)(((stat_var*)val)+1)); } unsigned int calc_tcp_load(void *val) { return ( get_stat_val((stat_var*)val) * 100)/tcp_children_no; } int register_udp_load_stat(str *name, stat_var **s, int children) { char *stat_name; /* in a single mem chunk, we put both the stat variable and the number of children for this UDP interface */ *s = shm_malloc(sizeof(stat_var) + sizeof(int)); if (!*s) { LM_ERR("no more shm\n"); return -1; } memset(*s,0,sizeof(stat_var)); *((int*)((*s)+1)) = children; (*s)->u.val = shm_malloc(sizeof(stat_val)); if (!(*s)->u.val) { LM_ERR("no more shm\n"); return -1; } memset((*s)->u.val,0,sizeof(stat_val)); if ( (stat_name = build_stat_name(name,"load")) == 0 || register_stat2("load",stat_name,(stat_var**)calc_udp_load, STAT_IS_FUNC, *s, 0) != 0) { LM_ERR("failed to add load stat\n"); return -1; } return 0; } int register_tcp_load_stat(stat_var **s) { *s = shm_malloc(sizeof(stat_var)); if (!*s) { LM_ERR("no more shm\n"); return -1; } memset(*s,0,sizeof(stat_var)); (*s)->u.val = shm_malloc(sizeof(stat_val)); if (!(*s)->u.val) { LM_ERR("no more shm\n"); return -1; } memset((*s)->u.val,0,sizeof(stat_val)); if (register_stat2("load","tcp-load",(stat_var**)calc_tcp_load, STAT_IS_FUNC, *s, 0) != 0) { LM_ERR("failed to add load stat\n"); return -1; } return 0; } /************* Functions for handling MODULEs(groups) of stats ***************/ static inline module_stats* get_stat_module( str *module) { int i; if ( (module==0) || module->s==0 || module->len==0 ) return 0; for( i=0 ; imod_no ; i++ ) { if ( (collector->amodules[i].name.len == module->len) && (strncasecmp(collector->amodules[i].name.s,module->s,module->len)==0) ) return &collector->amodules[i]; } return 0; } #define add_stat_module(module) \ __add_stat_module(module, 0) static inline module_stats* __add_stat_module( char *module, int unsafe) { module_stats *amods; module_stats *mods; int len; if ( (module==0) || ((len = strlen(module))==0 ) ) return 0; amods = unsafe ? (module_stats*)shm_realloc_unsafe( collector->amodules, (collector->mod_no+1)*sizeof(module_stats)) : (module_stats*)shm_realloc( collector->amodules, (collector->mod_no+1)*sizeof(module_stats)); if (amods==0) { LM_ERR("no more shm memory\n"); return 0; } collector->amodules = amods; collector->mod_no++; mods = &amods[collector->mod_no-1]; memset( mods, 0, sizeof(module_stats) ); mods->name.s = module; mods->name.len = len; mods->idx = collector->mod_no-1; return mods; } /***************** Init / Destroy STATS support functions *******************/ int clone_pv_stat_name(str *name, str *clone) { clone->s = (char*)shm_malloc(name->len); if (clone->s==NULL) { LM_ERR("failed to allocated more shm mem (%d)\n",name->len); return -1; } clone->len = name->len; memcpy(clone->s,name->s,name->len); return 0; } int init_stats_collector(void) { module_stats *dy_mod; /* init the collector */ collector = (stats_collector*)shm_malloc_unsafe(sizeof(stats_collector)); if (collector==0) { LM_ERR("no more shm mem\n"); goto error; } memset( collector, 0 , sizeof(stats_collector)); /* * register shm statistics in an unsafe manner, as some allocators * would actually attempt to update these statistics * during their "safe" allocations -- Liviu */ if (__register_module_stats( "shmem", shm_stats, 1) != 0) { LM_ERR("failed to register sh_mem statistics\n"); goto error; } #ifdef NO_ATOMIC_OPS /* init BIG (really BIG) lock */ stat_lock = lock_alloc(); if (stat_lock==0 || lock_init( stat_lock )==0 ) { LM_ERR("failed to init the really BIG lock\n"); goto error; } #endif collector->rwl = (void*)lock_init_rw(); if (collector->rwl==NULL) { LM_ERR("failed to create RW lock dynamic stats\n"); goto error; } /* register MI commands */ if (register_mi_mod( "statistics", mi_stat_cmds)<0) { LM_ERR("unable to register MI cmds\n"); goto error; } /* register core statistics */ if (register_module_stats( "core", core_stats)!=0 ) { LM_ERR("failed to register core statistics\n"); goto error; } /* register sh_mem statistics */ if (register_module_stats( "net", net_stats)!=0 ) { LM_ERR("failed to register network statistics\n"); goto error; } /* create the module for "dynamic" statistics */ dy_mod = add_stat_module( DYNAMIC_MODULE_NAME ); if (dy_mod==NULL) { LM_ERR("failed to create <%s> module\n",DYNAMIC_MODULE_NAME); goto error; } /* mark it as dynamic, so it will require locking */ dy_mod->is_dyn = 1 ; stats_ready = 1; LM_DBG("statistics manager successfully initialized\n"); return 0; error: return -1; } void destroy_stats_collector(void) { stat_var *stat; stat_var *tmp_stat; int i; #ifdef NO_ATOMIC_OPS /* destroy big lock */ if (stat_lock) lock_destroy( stat_lock ); #endif if (collector) { /* destroy hash tables */ for( i=0 ; ihstats[i] ; stat ; ) { tmp_stat = stat; stat = stat->hnext; if ((tmp_stat->flags&STAT_IS_FUNC)==0 && tmp_stat->u.val) shm_free(tmp_stat->u.val); if ( (tmp_stat->flags&STAT_SHM_NAME) && tmp_stat->name.s) shm_free(tmp_stat->name.s); shm_free(tmp_stat); } /* dynamic stats*/ for( stat=collector->dy_hstats[i] ; stat ; ) { tmp_stat = stat; stat = stat->hnext; if ((tmp_stat->flags&STAT_IS_FUNC)==0 && tmp_stat->u.val) shm_free(tmp_stat->u.val); if ( (tmp_stat->flags&STAT_SHM_NAME) && tmp_stat->name.s) shm_free(tmp_stat->name.s); shm_free(tmp_stat); } } /* destroy sts_module array */ if (collector->amodules) shm_free(collector->amodules); /* destroy the RW lock */ if (collector->rwl) lock_destroy_rw( (rw_lock_t *)collector->rwl); /* destroy the collector */ shm_free(collector); } return; } int stats_are_ready(void) { return stats_ready; } /********************* Create/Register STATS functions ***********************/ /** * Note: certain statistics (e.g. shm statistics) require different handling, * hence the parameter */ int register_stat2( char *module, char *name, stat_var **pvar, unsigned short flags, void *ctx, int unsafe) { module_stats* mods; stat_var **shash; stat_var *stat; stat_var *it; str smodule; int hash; int name_len; if (module==0 || name==0 || pvar==0) { LM_ERR("invalid parameters module=%p, name=%p, pvar=%p \n", module, name, pvar); goto error; } name_len = strlen(name); if(flags&STAT_ONLY_REGISTER){ stat = *pvar; goto do_register; } stat = unsafe ? (stat_var*)shm_malloc_unsafe(sizeof(stat_var) + ((flags&STAT_SHM_NAME)==0)*name_len) : (stat_var*)shm_malloc(sizeof(stat_var) + ((flags&STAT_SHM_NAME)==0)*name_len); if (stat==0) { LM_ERR("no more shm memory\n"); goto error; } memset( stat, 0, sizeof(stat_var) ); if ( (flags&STAT_IS_FUNC)==0 ) { stat->u.val = unsafe ? (stat_val*)shm_malloc_unsafe(sizeof(stat_val)) : (stat_val*)shm_malloc(sizeof(stat_val)); if (stat->u.val==0) { LM_ERR("no more shm memory\n"); goto error1; } #ifdef NO_ATOMIC_OPS *(stat->u.val) = 0; #else atomic_set(stat->u.val,0); #endif *pvar = stat; } else { stat->u.f = (stat_function)(pvar); } /* is the module already recorded? */ do_register: smodule.s = module; smodule.len = strlen(module); mods = get_stat_module(&smodule); if (mods==0) { mods = __add_stat_module(module, 1); if (mods==0) { LM_ERR("failed to add new module\n"); goto error2; } } /* fill the stat record */ stat->mod_idx = mods->idx; stat->name.len = name_len; if ( (flags&STAT_SHM_NAME)==0 ) { if(flags&STAT_ONLY_REGISTER) stat->name.s = shm_malloc_unsafe(name_len); else stat->name.s = (char*)(stat+1); memcpy(stat->name.s, name, name_len); } else { stat->name.s = name; } stat->flags = flags; stat->context = ctx; /* compute the hash by name */ hash = stat_hash( &stat->name ); /* link it into appropriate hash table , with or without locking */ if (mods->is_dyn) { lock_start_write((rw_lock_t *)collector->rwl); shash = collector->dy_hstats; /* double check for duplicates (due race conditions) */ for( it=shash[hash] ; it ; it=stat->hnext ) { if ( (it->name.len==stat->name.len) && (strncasecmp( it->name.s, stat->name.s, stat->name.len)==0) ) { /* duplicate found -> drop current stat and return the * found one */ lock_stop_write((rw_lock_t *)collector->rwl); if (unsafe) { if (flags&STAT_SHM_NAME) shm_free_unsafe(stat->name.s); if ((flags&STAT_IS_FUNC)==0) shm_free_unsafe(stat->u.val); shm_free_unsafe(stat); } else { if (flags&STAT_SHM_NAME) shm_free(stat->name.s); if ((flags&STAT_IS_FUNC)==0) shm_free(stat->u.val); shm_free(stat); } *pvar = it; return 0; } } /* new genuin stat-> continue */ } else { shash = collector->hstats; } if (shash[hash]==0) { shash[hash] = stat; } else { it = shash[hash]; while(it->hnext) it = it->hnext; it->hnext = stat; } collector->stats_no++; /* add the statistic also to the module statistic list */ if (mods->tail) { mods->tail->lnext = stat; } else { mods->head = stat; } mods->tail = stat; mods->no++; if (mods->is_dyn) lock_stop_write((rw_lock_t *)collector->rwl); return 0; error2: if ( (flags&STAT_IS_FUNC)==0 ) { if (unsafe) shm_free_unsafe(*pvar); else shm_free(*pvar); *pvar = 0; } error1: if (unsafe) shm_free_unsafe(stat); else shm_free(stat); error: if ( (flags&STAT_IS_FUNC)==0 && pvar!=NULL) *pvar = 0; return -1; } int register_dynamic_stat( str *name, stat_var **pvar) { char *p; int ret; /*FIXME - what we do here is reallt stupid - convert from str to * char and next function does the other way around - from char to * str - this is temporary, before fixing the register_stat2 function * prototype to accept str rather than char */ if ( (p=pkg_malloc( name->len + 1))==NULL ) { LM_ERR("no more pkg mem (%d)\n",name->len + 1); return -1; } memcpy( p, name->s, name->len); p[name->len] = 0; ret = register_stat( DYNAMIC_MODULE_NAME, p, pvar, 0/*flags*/); pkg_free(p); return ret; } int __register_module_stats(char *module, stat_export_t *stats, int unsafe) { int ret; if (module==0 || module[0]==0 || !stats || !stats[0].name) return 0; for( ; stats->name ; stats++) { ret = register_stat2( module, stats->name, stats->stat_pointer, stats->flags, NULL, unsafe); if (ret!=0) { LM_CRIT("failed to add statistic\n"); return -1; } } return 0; } stat_var* get_stat( str *name ) { stat_var *stat; int hash; if (collector==NULL || name==0 || name->s==0 || name->len==0) return 0; /* compute the hash by name */ hash = stat_hash( name ); /* and look for it , first in the hash for static stats */ for( stat=collector->hstats[hash] ; stat ; stat=stat->hnext ) { if ( (stat->name.len==name->len) && (strncasecmp( stat->name.s, name->s, name->len)==0) ) return stat; } /* and then in the hash for dynamic stats */ lock_start_read((rw_lock_t *)collector->rwl); for( stat=collector->dy_hstats[hash] ; stat ; stat=stat->hnext ) { if ( (stat->name.len==name->len) && (strncasecmp( stat->name.s, name->s, name->len)==0) ) { lock_stop_read((rw_lock_t *)collector->rwl); return stat; } } lock_stop_read((rw_lock_t *)collector->rwl); return 0; } int mi_stat_name(str *mod, str *stat, str *out) { static str tmp_buf = {0, 0}; char *tmp; if (mod) { tmp = pkg_realloc(tmp_buf.s, mod->len + stat->len + 1); if (!tmp) { LM_ERR("no more pkg memory\n"); return -1; } tmp_buf.s = tmp; memcpy(tmp_buf.s, mod->s, mod->len); tmp_buf.len = mod->len; tmp_buf.s[tmp_buf.len++] = ':'; memcpy(tmp_buf.s + tmp_buf.len, stat->s, stat->len); tmp_buf.len += stat->len; out->len = tmp_buf.len; out->s = tmp_buf.s; } else { out->len = stat->len; out->s = stat->s; } return 0; } int mi_print_stat(struct mi_node *rpl, str *mod, str *stat, unsigned long val) { str tmp_buf; if (mi_stat_name(mod, stat, &tmp_buf) < 0) { LM_ERR("cannot get stat name\n"); return -1; } if (!addf_mi_node_child(rpl, MI_DUP_NAME, tmp_buf.s, tmp_buf.len, "%lu", val)) { LM_ERR("cannot add stat\n"); return -1; } return 0; } /***************************** MI STUFF ********************************/ inline static int mi_add_stat(struct mi_node *rpl, stat_var *stat) { return mi_print_stat(rpl, &collector->amodules[stat->mod_idx].name, &stat->name, get_stat_val(stat)); } inline static int mi_list_stat(struct mi_node *rpl, str *mod, stat_var *stat) { str tmp_buf; char *buf; if (mi_stat_name(mod, &stat->name, &tmp_buf) < 0) { LM_ERR("cannot get stat name\n"); return -1; } if (stat->flags & (STAT_IS_FUNC|STAT_NO_RESET)) buf = "non-incremental"; else buf = "incremental"; if (!addf_mi_node_child(rpl, MI_DUP_NAME, tmp_buf.s, tmp_buf.len, "%s", buf)) { LM_ERR("cannot add stat\n"); return -1; } return 0; } inline static int mi_add_module_stats(struct mi_node *rpl, module_stats *mods) { stat_var *stat; int ret = 0; if (mods->is_dyn) lock_start_read((rw_lock_t *)collector->rwl); for( stat=mods->head ; stat ; stat=stat->lnext) { ret = mi_print_stat(rpl, &mods->name, &stat->name, get_stat_val(stat)); if (ret < 0) break; } if (mods->is_dyn) lock_stop_read((rw_lock_t *)collector->rwl); return ret; } inline static int mi_list_module_stats(struct mi_node *rpl, module_stats *mods) { stat_var *stat; int ret = 0; if (mods->is_dyn) lock_start_read((rw_lock_t *)collector->rwl); for( stat=mods->head ; stat ; stat=stat->lnext) { ret = mi_list_stat(rpl, &mods->name, stat); if (ret < 0) break; } if (mods->is_dyn) lock_stop_read((rw_lock_t *)collector->rwl); return ret; } static struct mi_root *mi_get_stats(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct mi_node *rpl; struct mi_node *arg; module_stats *mods; stat_var *stat; str val; int i; if (cmd->node.kids==NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; for( arg=cmd->node.kids ; arg ; arg=arg->next) { if (arg->value.len==0) continue; val = arg->value; if ( val.len==3 && memcmp(val.s,"all",3)==0) { /* add all statistic variables */ for( i=0 ; imod_no ;i++ ) { if (mi_add_module_stats( rpl, &collector->amodules[i] )!=0) goto error; } } else if ( val.len>1 && val.s[val.len-1]==':') { /* add module statistics */ val.len--; mods = get_stat_module( &val ); if (mods==0) continue; if (mi_add_module_stats( rpl, mods )!=0) goto error; } else { /* add only one statistic */ stat = get_stat( &val ); if (stat==0) continue; if (mi_add_stat(rpl,stat)!=0) goto error; } } if (rpl->kids==0) { free_mi_tree(rpl_tree); return init_mi_tree( 404, "Statistics Not Found", 20); } return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } static struct mi_root *mi_list_stats(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct mi_node *rpl; struct mi_node *arg; module_stats *mods; stat_var *stat; str val; int i; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; if (cmd->node.kids == NULL) { for( i=0 ; imod_no ;i++ ) { if (mi_list_module_stats( rpl, &collector->amodules[i] )!=0) goto error; } } else { for( arg=cmd->node.kids ; arg ; arg=arg->next) { if (arg->value.len==0) continue; val = arg->value; if ( val.len>1 && val.s[val.len-1]==':') { /* add module statistics */ val.len--; mods = get_stat_module( &val ); if (mods==0) continue; if (mi_list_module_stats( rpl, mods )!=0) goto error; } else { /* add only one statistic */ stat = get_stat( &val ); if (stat==0) continue; if (mi_list_stat(rpl,NULL, stat)!=0) goto error; } } } if (rpl->kids==0) { free_mi_tree(rpl_tree); return init_mi_tree( 404, "Statistics Not Found", 20); } return rpl_tree; error: free_mi_tree(rpl_tree); return 0; } static struct mi_root *mi_reset_stats(struct mi_root *cmd, void *param) { struct mi_root *rpl_tree; struct mi_node *arg; stat_var *stat; int found; if (cmd->node.kids==NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; found = 0; for( arg=cmd->node.kids ; arg ; arg=arg->next) { if (arg->value.len==0) continue; stat = get_stat( &arg->value ); if (stat==0) continue; reset_stat( stat ); found = 1; } if (!found) { free_mi_tree(rpl_tree); return init_mi_tree( 404, "Statistics Not Found", 20); } return rpl_tree; } #endif /*STATISTICS*/ opensips-2.2.2/statistics.h000066400000000000000000000142341300170765700157320ustar00rootroot00000000000000/* * Copyright (C) 2006 Voice Sistem SRL * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * History: * --------- * 2006-01-16 first version (bogdan) * 2006-11-28 added get_stat_var_from_num_code() (Jeffrey Magder - * SOMA Networks) * 2009-04-23 function var accepts a context parameter (bogdan) */ /*! * \file * \brief OpenSIPS statistics handling */ #ifndef _STATISTICS_H_ #define _STATISTICS_H_ #include "hash_func.h" #include "atomic.h" #define STATS_HASH_POWER 8 #define STATS_HASH_SIZE (1<<(STATS_HASH_POWER)) #define DYNAMIC_MODULE_NAME "dynamic" #define STAT_NO_RESET (1<<0) #define STAT_NO_SYNC (1<<1) #define STAT_SHM_NAME (1<<2) #define STAT_IS_FUNC (1<<3) #define STAT_ONLY_REGISTER (1<<4) #ifdef NO_ATOMIC_OPS typedef unsigned int stat_val; #else typedef atomic_t stat_val; #endif typedef unsigned long (*stat_function)(void *); struct module_stats_; typedef struct stat_var_{ unsigned int mod_idx; str name; unsigned short flags; void * context; union{ stat_val *val; stat_function f; }u; struct stat_var_ *hnext; struct stat_var_ *lnext; } stat_var; typedef struct module_stats_ { str name; unsigned int no; unsigned short is_dyn; unsigned short idx; stat_var *head; stat_var *tail; } module_stats; typedef struct stats_collector_ { int stats_no; int mod_no; stat_var* hstats[STATS_HASH_SIZE]; /* hash with static statistics */ stat_var* dy_hstats[STATS_HASH_SIZE]; /* hash with dynamic statistics */ void *rwl; /* lock for protecting dynamic stats/modules */ module_stats *amodules; }stats_collector; typedef struct stat_export_ { char* name; /* null terminated statistic name */ unsigned short flags; /* flags */ stat_var** stat_pointer; /* pointer to the variable's mem location * * NOTE - it's in shm mem */ } stat_export_t; #ifdef STATISTICS char *build_stat_name( str* prefix, char *var_name); int init_stats_collector(); int stats_are_ready(); /* for code which is statistics-dependent */ int register_udp_load_stat(str *name, stat_var **ctx, int children); int register_tcp_load_stat(stat_var **ctx); void destroy_stats_collector(); #define register_stat(_mod,_name,_pvar,_flags) \ register_stat2(_mod,_name,_pvar,_flags, NULL, 0) int register_stat2( char *module, char *name, stat_var **pvar, unsigned short flags, void* context, int unsafe); int register_dynamic_stat( str *name, stat_var **pvar); #define register_module_stats(mod, stats) \ __register_module_stats(mod, stats, 0) int __register_module_stats(char *module, stat_export_t *stats, int unsafe); int clone_pv_stat_name(str *name, str *clone); stat_var* get_stat( str *name ); unsigned int get_stat_val( stat_var *var ); /*! \brief * Returns the statistic associated with 'numerical_code' and 'is_a_reply'. * Specifically: * * - if in_codes is nonzero, then the stat_var for the number of messages * _received_ with the 'numerical_code' will be returned if it exists. * - otherwise, the stat_var for the number of messages _sent_ with the * 'numerical_code' will be returned, if the stat exists. */ stat_var *get_stat_var_from_num_code(unsigned int numerical_code, int in_codes); #ifdef NO_ATOMIC_OPS #include "locking.h" extern gen_lock_t *stat_lock; #endif #else #define init_stats_collector() 0 #define destroy_stats_collector() #define register_module_stats(_mod,_stats) 0 #define __register_module_stats(_mod,_stats, unsafe) 0 #define register_stat( _mod, _name, _pvar, _flags) 0 #define register_dynamic_stat( _name, _pvar) 0 #define get_stat( _name ) 0 #define get_stat_val( _var ) 0 #define get_stat_var_from_num_code( _n_code, _in_code) NULL #define register_udp_load_stat( _a, _b, _c) 0 #define register_tcp_load_stat( _a) 0 #define stats_are_ready() 0 #define clone_pv_stat_name( _name, _clone) 0 #endif #ifdef STATISTICS #ifdef NO_ATOMIC_OPS #define update_stat( _var, _n) \ do { \ if ( !((_var)->flags&STAT_IS_FUNC) ) {\ if ((_var)->flags&STAT_NO_SYNC) {\ *((_var)->u.val) += _n;\ } else {\ lock_get(stat_lock);\ *((_var)->u.val) += _n;\ lock_release(stat_lock);\ }\ }\ }while(0) #define reset_stat( _var) \ do { \ if ( ((_var)->flags&(STAT_NO_RESET|STAT_IS_FUNC))==0 ) {\ if ((_var)->flags&STAT_NO_SYNC) {\ *((_var)->u.val) = 0;\ } else {\ lock_get(stat_lock);\ *((_var)->u.val) = 0;\ lock_release(stat_lock);\ }\ }\ }while(0) #define get_stat_val( _var ) ((unsigned long)\ ((_var)->flags&STAT_IS_FUNC)?(_var)->u.f((_var)->context):*((_var)->u.val)) #else #define update_stat( _var, _n) \ do { \ if ( !((_var)->flags&STAT_IS_FUNC) ) {\ if (_n>=0) \ atomic_add( _n, (_var)->u.val);\ else \ atomic_sub( -(_n), (_var)->u.val);\ }\ }while(0) #define reset_stat( _var) \ do { \ if ( ((_var)->flags&(STAT_NO_RESET|STAT_IS_FUNC))==0 ) {\ atomic_set( (_var)->u.val, 0);\ }\ }while(0) #define get_stat_val( _var ) ((unsigned long)\ ((_var)->flags&STAT_IS_FUNC)?(_var)->u.f((_var)->context):(_var)->u.val->counter) #endif /* NO_ATOMIC_OPS */ #define if_update_stat(_c, _var, _n) \ do { \ if (_c) update_stat( _var, _n); \ }while(0) #define if_reset_stat(_c, _var) \ do { \ if (_c) reset_stat( _var); \ }while(0) #else #define update_stat( _var, _n) #define reset_stat( _var) #define if_update_stat( _c, _var, _n) #define if_reset_stat( _c, _var) #endif /*STATISTICS*/ #endif opensips-2.2.2/str.h000066400000000000000000000042031300170765700143430ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef str_h #define str_h /** * \file * \brief Common data type for text variables. * - \ref DataTypeText */ /*! * \page DataTypeText Common data type for text variables. * * This data type encapsulate a standard C char array. Its recommended to use * this type if you need variables holding text. Its caches the length of the * C string to avoid repetive calls to strlen, thus improving performance. * Its also safer to explicitly give the length to string operations of the core * or C libraries to prevent problems because of buffer overflows and missing * null-termination. * Important: The char array inside this type is not null-terminated. So if you * need to work with external functions that rely on this termination you must * add a zero at the end by yourself. Keep in mind that the length of the char * array is normally not large enough to store this additional null-termination. * So you must copy the char array to a new buffer that is (len + 1) big, * otherwise memory corruption and undefinied behavour will occur. * Most libraries provides also functions that can work with an explicit given * length, thus avoiding the need for this copy operation. */ struct _str{ char* s; /**< string as char array */ int len; /**< string length, not including null-termination */ }; typedef struct _str str; #endif opensips-2.2.2/strcommon.c000066400000000000000000000163251300170765700155570ustar00rootroot00000000000000/* * Copyright (C) 2007 voice-system.ro * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief Generic string handling functions */ #include "ut.h" #include "strcommon.h" /*! \brief * add backslashes to special characters */ int escape_common(char *dst, char *src, int src_len) { int i, j; if(dst==0 || src==0 || src_len<=0) return 0; j = 0; for(i=0; i='0' && src[i+2]<='9' && src[i+3]>='0' && src[i+3]<='9') { dst[j++] = (src[i+2]-'0')*10 + src[i+3] - '0'; i += 5; } else { dst[j++] = src[i++]; } } return j; } /*! \brief Compute MD5 checksum */ void compute_md5(char *dst, char *src, int src_len) { MD5_CTX context; unsigned char digest[16]; MD5Init (&context); MD5Update (&context, src, src_len); MD5Final (digest, &context); string2hex(digest, 16, dst); } /*! \brief Unscape all printable ASCII characters */ int unescape_user(str *sin, str *sout) { char *at, *p, c; if(sin==NULL || sout==NULL || sin->s==NULL || sout->s==NULL || sin->len<0 || sout->len < sin->len+1) return -1; at = sout->s; p = sin->s; while(p < sin->s+sin->len) { if (*p == '%') { p++; switch (*p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': c = (*p - '0') << 4; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': c = (*p - 'a' + 10) << 4; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': c = (*p - 'A' + 10) << 4; break; default: LM_ERR("invalid hex digit <%u>\n", (unsigned int)*p); return -1; } p++; switch (*p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': c = c + (*p - '0'); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': c = c + (*p - 'a' + 10); break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': c = c + (*p - 'A' + 10); break; default: LM_ERR("invalid hex digit <%u>\n", (unsigned int)*p); return -1; } if ((c < 32) || (c > 126)) { LM_ERR("invalid escaped character <%u>\n", (unsigned int)c); return -1; } *at++ = c; } else { *at++ = *p; } p++; } *at = 0; sout->len = at - sout->s; LM_DBG("unescaped string is <%s>\n", sout->s); return 0; } /*! \brief * Escape all printable characters that are not valid in user * part of request uri * no_need_to_escape = unreserved | user-unreserved * unreserved = aplhanum | mark * mark = - | _ | . | ! | ~ | * | ' | ( | ) * user-unreserved = & | = | + | $ | , | ; | ? | / */ int escape_user(str *sin, str *sout) { char *at, *p; unsigned char x; if(sin==NULL || sout==NULL || sin->s==NULL || sout->s==NULL || sin->len<0 || sout->len < 3*sin->len+1) return -1; at = sout->s; p = sin->s; while (p < sin->s+sin->len) { if (*p < 32 || *p > 126) { LM_ERR("invalid escaped character <%u>\n", (unsigned int)*p); return -1; } if (isdigit((int)*p) || ((*p >= 'A') && (*p <= 'Z')) || ((*p >= 'a') && (*p <= 'z'))) { *at = *p; } else { switch (*p) { /* unreserved chars */ case '-': case '_': case '.': case '!': case '~': case '*': case '\'': case '(': case ')': /* user unreserved chars */ case '&': case '=': case '+': case '$': case ',': case ';': case '?': case '/': *at = *p; break; default: *at++ = '%'; x = (*p) >> 4; if (x < 10) { *at++ = x + '0'; } else { *at++ = x - 10 + 'a'; } x = (*p) & 0x0f; if (x < 10) { *at = x + '0'; } else { *at = x - 10 + 'a'; } } } at++; p++; } *at = 0; sout->len = at - sout->s; LM_DBG("escaped string is <%s>\n", sout->s); return 0; } int unescape_param(str *sin, str *sout) { return unescape_user(sin, sout); } /*! \brief * Escape all printable characters that are not valid in * a param part of request uri: * no_need_to_escape = unreserved | param-unreserved * unreserved = aplhanum | mark * mark = - | _ | . | ! | ~ | * | ' | ( | ) * param-unreserved = [ | ] | / | : | & | + | $ */ int escape_param(str *sin, str *sout) { char *at, *p; unsigned char x; if (sin==NULL || sout==NULL || sin->s==NULL || sout->s==NULL || sin->len<0 || sout->len < 3*sin->len+1) return -1; at = sout->s; p = sin->s; while (p < sin->s+sin->len) { if (*p < 32 || *p > 126) { LM_ERR("invalid escaped character <%u>\n", (unsigned int)*p); return -1; } switch (*p) { /* unreserved chars */ case '-': case '_': case '.': case '!': case '~': case '*': case '\'': case '(': case ')': /* param unreserved chars */ case '[': case ']': case '/': case ':': case '&': case '+': case '$': *at = *p; break; default: *at++ = '%'; x = (*p) >> 4; if (x < 10) { *at++ = x + '0'; } else { *at++ = x - 10 + 'a'; } x = (*p) & 0x0f; if (x < 10) { *at = x + '0'; } else { *at = x - 10 + 'a'; } } at++; p++; } *at = 0; sout->len = at - sout->s; LM_DBG("escaped string is <%s>\n", sout->s); return 0; } opensips-2.2.2/strcommon.h000066400000000000000000000026651300170765700155660ustar00rootroot00000000000000/* * Copyright (C) 2007 voice-system.ro * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! * \file * \brief Common string handling functions */ #ifndef _STRCOMMON_H_ #define _STRCOMMON_H_ #include "str.h" #include "md5.h" #include "crc.h" /* * add backslashes to special characters */ int escape_common(char *dst, char *src, int src_len); /* * remove backslashes to special characters */ int unescape_common(char *dst, char *src, int src_len); int unescape_xml(char *dst, char *src, int src_len); void compute_md5(char *dst, char *src, int src_len); int escape_user(str *sin, str *sout); int unescape_user(str *sin, str *sout); int escape_param(str *sin, str *sout); int unescape_param(str *sin, str *sout); #endif opensips-2.2.2/tags.h000066400000000000000000000051751300170765700145020ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * - utility for generating to-tags * in SER, to-tags consist of two parts: a fixed part * which is bound to server instance and variable part * which is bound to request -- that helps to recognize, * who generated the to-tag in loops through the same * server -- in such cases, fixed part is constant, but * the variable part varies because it depends on * the via header * * History: * -------- * 2003-02-18 changed TOTAG_LEN into TOTAG_VALUE_LEN, to solve * redefinition conflict with tm/t_msgbuilder.h (andrei) */ /*! * \file * \brief Tag handling functions */ #ifndef _TAGS_H #define _TAGS_H #include "parser/msg_parser.h" #include "globals.h" #include "crc.h" #include "str.h" #include "socket_info.h" #define TOTAG_VALUE_LEN (MD5_LEN+CRC16_LEN+1) /*! \brief * Generate variable part of to-tag for a request; * it will have length of CRC16_LEN, sufficiently * long buffer must be passed to the function */ static inline void calc_crc_suffix( struct sip_msg *msg, char *tag_suffix) { int ss_nr; str suffix_source[3]; ss_nr=2; if (msg->via1==0) return; /* no via, bad message */ suffix_source[0]=msg->via1->host; suffix_source[1]=msg->via1->port_str; if (msg->via1->branch) suffix_source[ss_nr++]=msg->via1->branch->value; crcitt_string_array( tag_suffix, suffix_source, ss_nr ); } inline static void init_tags( char *tag, char **suffix, char *signature, char separator ) { str src[3]; struct socket_info* si; si=get_first_socket(); src[0].s=signature; src[0].len=strlen(signature); /* if we are not listening on anything we shouldn't be here */ src[1].s=si?si->address_str.s:""; src[1].len=si?si->address_str.len:0; src[2].s=si?si->port_no_str.s:""; src[2].len=si?si->port_no_str.len:0; MD5StringArray( tag, src, 3 ); tag[MD5_LEN]=separator; *suffix=tag+MD5_LEN+1; } #endif opensips-2.2.2/test/000077500000000000000000000000001300170765700143425ustar00rootroot00000000000000opensips-2.2.2/test/1.sh000077500000000000000000000017241300170765700150450ustar00rootroot00000000000000#!/bin/bash # load a minimal config # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. CFG=1.cfg # setup config echo -e "log_level=3" > $CFG ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 killall -9 opensips rm -f $CFG exit $ret opensips-2.2.2/test/10.sh000077500000000000000000000024311300170765700151210ustar00rootroot00000000000000#!/bin/bash # test basic db related opensipsctl functionality for dbtext # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. cd ../scripts # setup config file cp opensipsctlrc opensipsctlrc.bak cp opensipsctl opensipsctl.bak sed -i "s/# DBENGINE=MYSQL/DBENGINE=DBTEXT/g" opensipsctlrc sed -i "s/TEST=\"false\"/TEST=\"true\"/g" opensipsctl ./opensipsctl avp list > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then ./opensipsctl domain showdb > /dev/null ret=$? fi ; # cleanup mv opensipsctlrc.bak opensipsctlrc mv opensipsctl.bak opensipsctl cd ../test exit $ret opensips-2.2.2/test/11.cfg000066400000000000000000000024261300170765700152500ustar00rootroot00000000000000# ----------- global configuration parameters ------------------------ log_level=3 log_stderror=no children=1 disable_tcp=yes # ------------------ module loading ---------------------------------- mpath="../modules/" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "rr/rr.so" loadmodule "maxfwd/maxfwd.so" loadmodule "textops/textops.so" loadmodule "mi_fifo/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") loadmodule "usrloc/usrloc.so" modparam("usrloc", "db_mode", 1) loadmodule "registrar/registrar.so" #------------------------- request routing logic ------------------- route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (msg:len >= 2048 ) { sl_send_reply("513", "Message Too Large"); exit; } if (!method=="REGISTER") record_route(); if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); } if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(1); } if (uri==myself) { if (method=="REGISTER") { save("location"); exit; } if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; } append_hf("P-hint: usrloc applied\r\n"); } route(1); } route[1] { if (!t_relay()) { sl_reply_error(); } exit; } opensips-2.2.2/test/11.sh000077500000000000000000000035601300170765700151260ustar00rootroot00000000000000#!/bin/bash # database access and persistent storage for registrar on mysql # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require CFG=11.cfg if ! (check_netcat && check_opensips && check_module "db_mysql"); then exit 0 fi ; cp $CFG $CFG.bak echo "loadmodule \"db_mysql/db_mysql.so\"" >> $CFG ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 # register a user cat register.sip | nc -q 1 -u localhost 5060 > /dev/null cd ../scripts if [ "$ret" -eq 0 ] ; then ./opensipsctl ul show | grep "AOR:: 1000" > /dev/null ret=$? fi ; TMP=`mysql -B -u opensipsro --password=opensipsro opensips -e "select COUNT(*) from location where username="1000";" | tail -n 1` if [ "$TMP" -eq 0 ] ; then ret=1 fi ; # unregister the user cat ../test/unregister.sip | nc -q 1 -u localhost 5060 > /dev/null if [ "$ret" -eq 0 ] ; then ./opensipsctl ul show | grep "AOR:: 1000" > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then ret=1 else ret=0 fi ; fi ; ret=`mysql -B -u opensipsro --password=opensipsro opensips -e "select COUNT(*) from location where username="1000";" | tail -n 1` cd ../test killall -9 opensips mv $CFG.bak $CFG exit $ret opensips-2.2.2/test/12.cfg000066400000000000000000000045561300170765700152570ustar00rootroot00000000000000log_stderror=yes mpath="../modules/" loadmodule "db_mysql/db_mysql.so" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "rr/rr.so" loadmodule "maxfwd/maxfwd.so" loadmodule "textops/textops.so" loadmodule "usrloc/usrloc.so" loadmodule "registrar/registrar.so" loadmodule "mi_fifo/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") route{ xlog("received by opensips[$pp] at '$Tf' from $si:$sp, method: $rm, transport: $rP:$Ri:$rp, user agent: $ua\n"); if !(route(39)) { xlog("L_ERR", "Error getting identity\n"); exit; } xlog("L_ERR", "My identity: $avp(identity_uri)\n"); if (!method=="REGISTER") record_route(); if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); }; if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(1); }; xlog("From header tag: $(hdr(From){param.value,tag})\n"); if(is_method("REGISTER")) { if(is_present_hf("Expires")) { xlog("Expires header field present\n"); if($(hdr(Expires){s.int}) > 0) { xlog("this is an registration\n"); } if($(hdr(Expires){s.int}) == 0) { xlog("this is an unregistration\n"); } } if(is_present_hf("Contact")) { xlog("Contact header field present\n"); if ($(hdr(Contact){param.value,expires}{s.int}) > 0) { xlog("this is an registration\n"); } if ($(ct{param.value,expires}{s.int}) == 0) { xlog("this is an unregistration\n"); } } } if (uri==myself) { if (method=="REGISTER") { save("location"); exit; } if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; } append_hf("P-hint: usrloc applied\r\n"); } route(1); } route[1] { if (!t_relay()) { sl_reply_error(); } exit; } route[39] { if ($ai) { $avp(identity_user)=$(ai{uri.user}); $avp(identity_domain)=$(ai{uri.domain}); $avp(identity_uri)=$ai; xlog("L_ERR", "Getting identity from P-Asserted-Identity header\n"); } else if ($pu) { $avp(identity_user)=$(pu{uri.user}); $avp(identity_domain)=$(pu{uri.domain}); $avp(identity_uri)=$pu; xlog("L_ERR", "Getting identity from P-Preferred-Identity header\n"); } else if ($fu) { $avp(identity_user)=$(fu{uri.user}); $avp(identity_domain)=$(fu{uri.domain}); $avp(identity_uri)=$fu; xlog("L_ERR", "Getting identity from FROM URI\n"); } else { xlog("L_ERR", "No identity for $rm: $fu -> $ru\n"); return(-1); } return (1); } opensips-2.2.2/test/12.sh000077500000000000000000000043031300170765700151230ustar00rootroot00000000000000#!/bin/bash # configuration with pseudo-variables, transformations and xlog output # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require CFG=12.cfg TMPFILE=`mktemp -t opensips-test.XXXXXXXXXX` if ! (check_netcat && check_opensips); then exit 0 fi ; ../opensips -w . -f $CFG &> $TMPFILE ret=$? sleep 1 # register a user cat register.sip | nc -q 1 -u localhost 5060 > /dev/null cd ../scripts if [ "$ret" -eq 0 ] ; then ./opensipsctl ul show | grep "AOR:: 1000" > /dev/null ret=$? fi ; # unregister the user cat ../test/unregister.sip | nc -q 1 -u localhost 5060 > /dev/null if [ "$ret" -eq 0 ] ; then ./opensipsctl ul show | grep "AOR:: 1000" > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then ret=1 else ret=0 fi ; fi ; if [ "$ret" -eq 0 ] ; then grep "method: REGISTER, transport: UDP:127.0.0.1:5060, user agent: Twinkle/1.0" $TMPFILE > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then grep "Getting identity from FROM URI" $TMPFILE > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then grep "My identity: sip:1000@127.0.0.1" $TMPFILE > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then grep "Contact header field present" $TMPFILE > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then grep "this is an registration" $TMPFILE > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then grep "this is an unregistration" $TMPFILE > /dev/null ret=$? fi ; fi ; fi ; fi ; fi ; fi ; cd ../test killall -9 opensips rm $TMPFILE exit $ret opensips-2.2.2/test/13.cfg000066400000000000000000000027631300170765700152560ustar00rootroot00000000000000mpath="../modules" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "maxfwd/maxfwd.so" loadmodule "carrierroute/carrierroute.so" loadmodule "mi_fifo/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); return; }; if (msg:len > max_len) { sl_send_reply("513", "Message too big"); return; }; if (method == "REGISTER") { if(!cr_route("default", "register", "$rU", "$rU", "call_id", "$avp(extra)")) { sl_send_reply("403", "Not allowed"); } sl_send_reply("200", "OK"); return; } if (method == "INVITE") { if(!cr_route("default", "proxy", "$rU", "$rU", "call_id")) { sl_send_reply("403", "Not allowed"); } sl_send_reply("100", "Trying"); return; } cr_user_carrier("$fU", "$fd", "$avp(carrier)"); $avp(domain)="start"; if (!cr_route("$avp(carrier)", "$avp(domain)", "$rU", "$rU", "call_id", "$avp(host)")) { xlog("L_ERR", "cr_route failed\n"); exit; } t_on_failure("1"); if (!t_relay()) { sl_reply_error(); }; } failure_route[1] { revert_uri(); if (!cr_next_domain("$avp(carrier)", "$avp(domain)", "$rU", "$avp(host)", "$T_reply_code", "$avp(domain)")) { xlog("L_ERR", "cr_next_domain failed\n"); exit; } if (!cr_route("$avp(carrier)", "$avp(domain)", "$rU", "$rU", "call_id", "$avp(host)")) { xlog("L_ERR", "cr_route failed\n"); exit; } t_on_failure("1"); if (!t_relay()) { xlog("L_ERR", "t_relay failed\n"); exit; }; } opensips-2.2.2/test/13.sh000077500000000000000000000126501300170765700151300ustar00rootroot00000000000000#!/bin/bash # loads a carrierroute config for loadbalancing from mysql database # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require CFG=13.cfg if ! (check_opensips && check_module "carrierroute" ); then exit 0 fi ; cp $CFG $CFG.bak # setup config echo "loadmodule \"db_mysql/db_mysql.so\"" >> $CFG echo "modparam(\"carrierroute\", \"config_source\", \"db\")" >> $CFG # setup database MYSQL="mysql opensips -u opensips --password=opensipsrw -e" $MYSQL "insert into route_tree (id, carrier) values ('1', 'carrier1');" $MYSQL "insert into route_tree (id, carrier) values ('2', 'default');" $MYSQL "insert into route_tree (id, carrier) values ('3', 'carrier2');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('1','1','0','49','0.5','0','host1.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('2','1','0','49','0.5','0','host2.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('3','1','0','42','0.3','0','host3.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('4','1','0','42','0.7','0','host4.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('5','1','0','','0.1','0','host5.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('6','1','1','','0.1','0','host5.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('7','1','2','','0.1','0','host5.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('8','2','0','','1','0','host6.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('9','2','1','','1','0','host6.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('10','2','2','','1','0','host6.local');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, prob, strip, rewrite_host) values ('11','3','0','','1','0','host1.local');" $MYSQL "insert into carrierfailureroute(id, carrier, domain, scan_prefix, host_name, reply_code, flags, mask, next_domain) values ('1', '1', '1', '49', 'host1.local', '404', '', '', '2');" $MYSQL "insert into carrierfailureroute(id, carrier, domain, scan_prefix, host_name, reply_code, flags, mask, next_domain) values ('2', '1', '1', '49', 'host1.local', '4..', '', '', '3');" $MYSQL "insert into carrierfailureroute(id, carrier, domain, scan_prefix, host_name, reply_code, flags, mask, next_domain) values ('3', '2', '1', '49', 'host1.local', '503', '', '', '2');" $MYSQL "insert into carrierfailureroute(id, carrier, domain, scan_prefix, host_name, reply_code, flags, mask, next_domain) values ('4', '2', '2', '49', 'host1.local', '5..', '', '', '3');" ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 cd ../scripts TMPFILE=`mktemp -t opensips-test.XXXXXXXXXX` if [ "$ret" -eq 0 ] ; then ./opensipsctl fifo cr_dump_routes > $TMPFILE ret=$? fi ; if [ "$ret" -eq 0 ] ; then tmp=`grep -v "Printing routing information: Printing tree for carrier carrier1 (1) Printing tree for domain 0 42: 70.140 %, 'host4.local': ON, '0', '', '', '' 42: 30.060 %, 'host3.local': ON, '0', '', '', '' 49: 50.000 %, 'host2.local': ON, '0', '', '', '' 49: 50.000 %, 'host1.local': ON, '0', '', '', '' NULL: 100.000 %, 'host5.local': ON, '0', '', '', '' Printing tree for domain 1 NULL: 100.000 %, 'host5.local': ON, '0', '', '', '' Printing tree for domain 2 NULL: 100.000 %, 'host5.local': ON, '0', '', '', '' Printing tree for carrier default (2) Printing tree for domain 0 NULL: 100.000 %, 'host6.local': ON, '0', '', '', '' Printing tree for domain 1 NULL: 100.000 %, 'host6.local': ON, '0', '', '', '' Printing tree for carrier carrier2 (3) Printing tree for domain 0 NULL: 100.000 %, 'host1.local': ON, '0', '', '', ''" $TMPFILE` if [ "$tmp" = "" ] ; then ret=0 else ret=1 fi ; fi ; killall -9 opensips # cleanup database $MYSQL "delete from route_tree where id = 1;" $MYSQL "delete from route_tree where id = 2;" $MYSQL "delete from route_tree where id = 3;" $MYSQL "delete from carrierroute where carrier=1;" $MYSQL "delete from carrierroute where carrier=2;" $MYSQL "delete from carrierroute where carrier=3;" $MYSQL "delete from carrierfailureroute where carrier=1;" $MYSQL "delete from carrierfailureroute where carrier=2;" $MYSQL "delete from carrierfailureroute where carrier=3;" cd ../test mv $CFG.bak $CFG rm $TMPFILE exit $ret opensips-2.2.2/test/14.cfg000066400000000000000000000014311300170765700152460ustar00rootroot00000000000000mpath="../modules" loadmodule "sl/sl.so" loadmodule "maxfwd/maxfwd.so" loadmodule "carrierroute/carrierroute.so" loadmodule "mi_fifo/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); return; }; if (msg:len > max_len) { sl_send_reply("513", "Message too big"); return; }; if (method == "REGISTER") { if(!cr_route("default", "register", "$rU", "$rU", "call_id", "$avp(extra)")) { sl_send_reply("403", "Not allowed"); } sl_send_reply("200", "OK"); return; } if (method == "INVITE") { if(!cr_route("default", "proxy", "$rU", "$rU", "call_id")) { sl_send_reply("403", "Not allowed"); } sl_send_reply("100", "Trying"); return; } } opensips-2.2.2/test/14.sh000077500000000000000000000050511300170765700151260ustar00rootroot00000000000000#!/bin/bash # loads a carrierroute config for loadbalancing from config file # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require CFG=14.cfg if ! (check_opensips && check_module "carrierroute" ); then exit 0 fi ; cp $CFG $CFG.bak # setup config echo "modparam(\"carrierroute\", \"config_file\", \"`pwd`/../test/carrierroute.cfg\")" >> $CFG ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 cd ../scripts TMPFILE=`mktemp -t opensips-test.XXXXXXXXXX` if [ "$ret" -eq 0 ] ; then ./opensipsctl fifo cr_dump_routes > $TMPFILE ret=$? fi ; if [ "$ret" -eq 0 ] ; then tmp=`grep -v "Printing routing information: Printing tree for carrier default (1) Printing tree for domain register NULL: 0.000 %, 'test1': OFF, '0', '', '', '' NULL: 50.000 %, 'test2.localdomain': ON, '0', '', '', '' NULL: 50.000 %, 'test3.localdomain': ON, '0', '', '', '' Printing tree for domain proxy 2: 87.610 %, 'test7.localdomain': ON, '0', '', '', '' 2: 12.516 %, 'test8.localdomain': ON, '0', '', '', '' 42: 70.070 %, 'test4': ON, '0', '', '', '' 42: 20.020 %, 'test5.localdomain': ON, '0', '', '', '' 42: 10.010 %, 'test6.localdomain': ON, '0', '', '', '' 49: 0.000 %, 'test5.localdomain': OFF, '0', '', '', '' 49: 44.444 %, 'test4': ON, '0', '', '', '' 49: 55.556 %, 'test6.localdomain': ON, '0', '', '', '' Printing tree for domain other NULL: 0.000 %, 'test1': OFF, '0', '', '', '' NULL: 50.000 %, 'test2.localdomain': OFF, '0', '', '', '' Rule is backed up by: test3.localdomain NULL: 50.000 %, 'test3.localdomain': ON, '0', '', '', '' Rule is backup for: test2.localdomain" $TMPFILE` if [ "$tmp" = "" ] ; then ret=0 else ret=1 fi ; fi ; killall -9 opensips cd ../test mv $CFG.bak $CFG rm -f $TMPFILE exit $ret opensips-2.2.2/test/15.sh000077500000000000000000000023231300170765700151260ustar00rootroot00000000000000#!/bin/bash # load all modules without external dependencies with dbtext # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/common source include/require CFG=15.cfg if ! (check_opensips); then exit 0 fi ; echo "loadmodule \"../modules/db_text/db_text.so\"" >> $CFG cat 2.cfg >> $CFG echo "modparam(\"$DB_ALL_MOD\", \"db_url\", \"text://`pwd`/../scripts/dbtext/opensips\")" >> $CFG ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 killall -9 opensips rm $CFG exit $ret opensips-2.2.2/test/16.sh000077500000000000000000000025321300170765700151310ustar00rootroot00000000000000#!/bin/bash # load all modules without external dependencies with postgres # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/common source include/require # Needs a default opensips database setup for postgres if ! (check_opensips && check_module "db_postgres" ); then exit 0 fi ; CFG=2.cfg cp $CFG $CFG.bak echo "loadmodule \"db_postgres/db_postgres.so\"" >> $CFG echo "modparam(\"$DB_ALL_MOD\", \"db_url\", \"postgres://opensipsro:opensipsro@localhost/opensips\")" >> $CFG # start ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 killall -9 opensips mv $CFG.bak $CFG rm -f dispatcher.list exit $ret opensips-2.2.2/test/17.sh000077500000000000000000000036221300170765700151330ustar00rootroot00000000000000#!/bin/bash # load all modules without external dependencies with db_berkeley # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/common source include/require if ! (check_opensips && check_module "db_berkeley" ); then exit 0 fi ; CFG=17.cfg tmp_name=""$RANDOM"_opensipsdb_tmp" echo "loadmodule \"../modules/db_berkeley/db_berkeley.so\"" >> $CFG cat 2.cfg >> $CFG echo "modparam(\"$DB_ALL_MOD\", \"db_url\", \"berkeley://`pwd`/../scripts/$tmp_name\")" >> $CFG cd ../scripts # setup config file cp opensipsctlrc opensipsctlrc.bak sed -i "s/# DBENGINE=MYSQL/DBENGINE=DB_BERKELEY/g" opensipsctlrc sed -i "s/# INSTALL_EXTRA_TABLES=ask/INSTALL_EXTRA_TABLES=yes/g" opensipsctlrc sed -i "s/# INSTALL_PRESENCE_TABLES=ask/INSTALL_PRESENCE_TABLES=yes/g" opensipsctlrc cp opensipsdbctl opensipsdbctl.bak sed -i "s/TEST=\"false\"/TEST=\"true\"/g" opensipsdbctl ./opensipsdbctl create $tmp_name > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then ../opensips -w . -f ../test/$CFG > /dev/null ret=$? fi ; sleep 1 killall -9 opensips # cleanup ./opensipsdbctl drop $tmp_name > /dev/null mv opensipsctlrc.bak opensipsctlrc mv opensipsdbctl.bak opensipsdbctl cd ../test/ rm $CFG exit $ret opensips-2.2.2/test/18.sh000077500000000000000000000024071300170765700151340ustar00rootroot00000000000000#!/bin/bash # runs ../opensips with all command line arguments. # ommited options are -h -v -C -c -D # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_opensips); then exit 0 fi ; # the config file CFG=18.cfg # setup config echo -e "log_level=3" > $CFG # start: ../opensips -f ./$CFG -l 127.0.0.1 -n 0 -rR -v -E -d -T -N 0 -b 23 -m 42 -w ./ -u $(id -u) -g $(id -g) -P ./pid.out -G ./pgid.out > /dev/null 2>&1 ret=$? sleep 1 # clean up: killall -9 opensips rm $CFG rm pgid.out rm pid.out exit $ret opensips-2.2.2/test/19.cfg000066400000000000000000000023131300170765700152530ustar00rootroot00000000000000# OpenSIPS config for lookup / registrar testing #------------------------Global configuration---------------------------------- log_level=3 log_stderror=no listen=udp:127.0.0.1:5060 dns=no rev_dns=no #-----------------------Loading Modules------------------------------------- mpath="../modules/" loadmodule "db_mysql/db_mysql.so" loadmodule "usrloc/usrloc.so" loadmodule "registrar/registrar.so" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "maxfwd/maxfwd.so" #for debugging purposes only loadmodule "mi_fifo/mi_fifo.so" #-----------------------Module parameters------------------------------------- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("usrloc", "db_mode", 3) modparam("usrloc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") #-----------------------Routing configuration---------------------------------# route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit(); } if(!lookup ("location")){ sl_send_reply("404", "Not Found"); } if(method==INVITE){ if (!t_relay()) { sl_reply_error(); } } if (method== ACK) { if (!t_relay()) { sl_reply_error(); } } if (method==BYE){ if (!t_relay()) { sl_reply_error(); } } exit(); } opensips-2.2.2/test/19.sh000077500000000000000000000032771300170765700151430ustar00rootroot00000000000000#!/bin/bash # check user lockup for proxy functionality with usrloc and registrar for mysql # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips); then exit 0 fi ; CFG=19.cfg SRV=5060 UAS=5070 UAC=5080 # add an registrar entry to the db; mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "INSERT INTO location (username,contact,socket,user_agent,cseq,q) VALUES (\"foo\",\"sip:foo@localhost:$UAS\",\"udp:127.0.0.1:$UAS\",\"ser_test\",1,-1);" ../opensips -w . -f $CFG &> /dev/null sipp -sn uas -bg -i localhost -m 10 -f 2 -p $UAS &> /dev/null sipp -sn uac -s foo 127.0.0.1:$SRV -i localhost -m 10 -f 2 -p $UAC &> /dev/null ret=$? # cleanup killall -9 sipp > /dev/null 2>&1 killall -9 opensips > /dev/null 2>&1 mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "DELETE FROM location WHERE ((contact = \"sip:foo@localhost:$UAS\") and (user_agent = \"ser_test\"));" exit $ret; opensips-2.2.2/test/2.cfg000066400000000000000000000040741300170765700151710ustar00rootroot00000000000000mpath="../modules" loadmodule "tm/tm.so" loadmodule "acc/acc.so" loadmodule "alias_db/alias_db.so" loadmodule "auth/auth.so" loadmodule "auth_db/auth_db.so" loadmodule "avpops/avpops.so" loadmodule "benchmark/benchmark.so" loadmodule "cfgutils/cfgutils.so" loadmodule "dialog/dialog.so" loadmodule "dialplan/dialplan.so" loadmodule "dispatcher/dispatcher.so" loadmodule "diversion/diversion.so" loadmodule "domain/domain.so" loadmodule "domainpolicy/domainpolicy.so" loadmodule "enum/enum.so" loadmodule "exec/exec.so" loadmodule "db_flatstore/db_flatstore.so" loadmodule "gflags/gflags.so" loadmodule "group/group.so" loadmodule "imc/imc.so" loadmodule "mangler/mangler.so" loadmodule "maxfwd/maxfwd.so" loadmodule "mi_datagram/mi_datagram.so" loadmodule "mi_fifo/mi_fifo.so" loadmodule "msilo/msilo.so" loadmodule "nathelper/nathelper.so" loadmodule "nat_traversal/nat_traversal.so" loadmodule "options/options.so" loadmodule "path/path.so" loadmodule "pike/pike.so" loadmodule "ratelimit/ratelimit.so" loadmodule "rr/rr.so" loadmodule "seas/seas.so" loadmodule "siptrace/siptrace.so" loadmodule "sl/sl.so" loadmodule "sms/sms.so" loadmodule "speeddial/speeddial.so" loadmodule "sst/sst.so" loadmodule "statistics/statistics.so" loadmodule "textops/textops.so" loadmodule "uac_auth/uac_auth.so" loadmodule "uac/uac.so" loadmodule "uac_redirect/uac_redirect.so" loadmodule "uri/uri.so" loadmodule "uri_db/uri_db.so" loadmodule "usrloc/usrloc.so" loadmodule "userblacklist/userblacklist.so" loadmodule "registrar/registrar.so" loadmodule "permissions/permissions.so" loadmodule "pdt/pdt.so" loadmodule "mediaproxy/mediaproxy.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("mi_datagram", "socket_name", "/tmp/opensips.sock") modparam("dialog", "db_mode", 2) modparam("tm", "fr_inv_timer_avp", "$avp(fr_inv_timer)") modparam("auth", "rpid_avp", "$avp(rpid)") modparam("sms", "modems", "Nokia [d=/dev/ttyS1;b=9600;m=new;l=30] ") modparam("sms", "networks", "D1 [m=10] ;d2[ m=20]") modparam("sms", "links", "NOKIA[D1;d2]") modparam("sst", "sst_flag", 6) modparam("usrloc", "nat_bflag", 15) opensips-2.2.2/test/2.sh000077500000000000000000000024701300170765700150450ustar00rootroot00000000000000#!/bin/bash # load all modules without external dependencies with mysql # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Needs a default opensips database setup for mysql source include/require CFG=2.cfg if ! (check_opensips && check_module "db_mysql" ); then exit 0 fi ; cp $CFG $CFG.bak touch dispatcher.list echo "loadmodule \"db_mysql/db_mysql.so\"" >> $CFG echo "modparam(\"dispatcher\", \"list_file\", \"`pwd`/../test/dispatcher.list\")" >> $CFG # start ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 killall -9 opensips mv $CFG.bak $CFG rm -f dispatcher.list exit $ret opensips-2.2.2/test/20.cfg000066400000000000000000000025061300170765700152470ustar00rootroot00000000000000# OpenSIPS config for lookup / registrar testing #------------------------Global configuration---------------------------------- log_level=3 log_stderror=yes listen=udp:127.0.0.1:5059 dns=no rev_dns=no #-----------------------Loading Modiules------------------------------------- mpath="../modules/" loadmodule "db_mysql/db_mysql.so" loadmodule "usrloc/usrloc.so" loadmodule "registrar/registrar.so" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "maxfwd/maxfwd.so" loadmodule "acc/acc.so" #for debugging purposes only loadmodule "mi_fifo/mi_fifo.so" #-----------------------Module parameters------------------------------------- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("usrloc", "db_mode", 3) modparam("usrloc", "db_url", "mysql://opensips:opensipsrw@127.0.0.1/opensips") modparam("acc", "log_flag", 1) modparam("db_mysql", "timeout_interval", 2) #-----------------------Routing configuration---------------------------------# route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit(); } if(!lookup ("location")){ sl_send_reply("404", "Not Found"); } if(method=="INVITE"){ setflag(1); if (!t_relay()) { sl_reply_error(); } } if (method=="ACK") { if (!t_relay()) { sl_reply_error(); } } if (method=="BYE") { if (!t_relay()) { sl_reply_error(); } } exit(); } opensips-2.2.2/test/20.sh000077500000000000000000000034231300170765700151240ustar00rootroot00000000000000#! /bin/bash # test basic accounting functionality # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips); then exit 0 fi ; CFG="20.cfg" TMPFILE=`mktemp -t opensips-test.XXXXXXXXXX` # add an registrar entry to the db; mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "INSERT INTO location (username,contact,socket,user_agent,cseq,q) VALUES (\"foo\",\"sip:foo@localhost\",\"udp:127.0.0.1:5060\",\"ser_test\",1,-1);" sipp -sn uas -bg -i localhost -m 1 -f 10 -p 5060 &> /dev/null ../opensips -w . -f $CFG &> $TMPFILE sipp -sn uac -s foo 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 10 -p 5061 &> /dev/null egrep '^ACC:[[:space:]]+transaction[[:space:]]+answered:[[:print:]]*code=200;reason=OK$' $TMPFILE > /dev/null ret=$? # cleanup killall -9 sipp &> /dev/null killall -9 opensips &> /dev/null rm $TMPFILE mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "DELETE FROM location WHERE ((contact = \"sip:foo@localhost\") and (user_agent = \"ser_test\"));" exit $ret; opensips-2.2.2/test/21.cfg000066400000000000000000000042361300170765700152520ustar00rootroot00000000000000# # simple quick-start config script # Please refer to the Core CookBook at http://www.opensips.org/dokuwiki/doku.php # for a explanation of possible statements, functions and parameters. # # ----------- global configuration parameters ------------------------ log_level=3 # debug level (cmd line: -dddddddddd) log_stderror=no # (cmd line: -E) children=4 listen=udp:127.0.0.1:5059 dns=no rev_dns=no # ------------------ module loading ---------------------------------- #set module path mpath="../modules/" # Uncomment this if you want to use SQL database loadmodule "db_mysql/db_mysql.so" loadmodule "sl/sl.so" loadmodule "rr/rr.so" loadmodule "maxfwd/maxfwd.so" loadmodule "usrloc/usrloc.so" loadmodule "registrar/registrar.so" loadmodule "textops/textops.so" loadmodule "auth/auth.so" loadmodule "auth_db/auth_db.so" loadmodule "uri_db/uri_db.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line modparam("usrloc", "db_mode", 3) modparam("usrloc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # -- auth params -- # Uncomment if you are using auth module # modparam("auth_db", "calculate_ha1", 1) # If you set "calculate_ha1" parameter to 1 (which true in this config), # uncomment also the following parameter) # modparam("auth_db", "password_column", "password") # ------------------------- request routing logic ------------------- # main routing logic route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483", "Too Many Hops"); exit; } if (is_method("ACK")) { xlog("ACK"); sl_send_reply("200","OK"); exit; } if (uri == myself) { if(method==REGISTER) { if (!www_authorize("localhost", "subscriber")) { www_challenge("localhost", "0"); } else { if(db_check_to() && db_check_from()) { sl_send_reply("200","OK"); } } } else { if (!proxy_authorize("localhost", "subscriber")) { proxy_challenge("localhost", "0"); } else { if(db_check_to() && db_check_from() && db_does_uri_exist()) { sl_send_reply("200","OK"); } } } exit; } } opensips-2.2.2/test/21.sh000077500000000000000000000033331300170765700151250ustar00rootroot00000000000000#!/bin/bash # tests the authentification via auth_db and uri_db # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips); then exit 0 fi ; CFG=21.cfg MYSQL="mysql opensips -u opensips --password=opensipsrw -e" # add an registrar entry to the db; $MYSQL "INSERT INTO subscriber (username, domain, password, email_address) VALUES (\"alice\",\"localhost\",\"alice\",\"alice@localhost\");" ../opensips -w . -f $CFG &> /dev/null; ret=$? sleep 1 if [ "$ret" -eq 0 ] ; then sipp -s alice 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 1 -auth_uri alice@localhost -p 5061 -sf reg_auth.xml -ap alice &> /dev/null; ret=$? fi; if [ "$ret" -eq 0 ] ; then sipp -s alice 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 1 -auth_uri alice@localhost -p 5061 -sf inv_auth.xml -ap alice &> /dev/null; ret=$? fi; #cleanup killall -9 opensips &> /dev/null; killall -9 sipp &> /dev/null; $MYSQL "DELETE FROM subscriber WHERE((username = \"alice\") and (domain = \"localhost\"));" exit $ret; opensips-2.2.2/test/22.sh000077500000000000000000000040161300170765700151250ustar00rootroot00000000000000#!/bin/bash # database access and persistent storage for registrar on postgres # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_netcat && check_opensips && check_module "db_postgres"); then exit 0 fi ; CFG=11.cfg cp $CFG $CFG.tmp echo "loadmodule \"db_postgres/db_postgres.so\"" >> $CFG echo "modparam(\"usrloc\", \"db_url\", \"postgres://opensips:opensipsrw@localhost/opensips\")" >> $CFG ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 # register a user cat register.sip | nc -q 1 -u localhost 5060 > /dev/null cd ../scripts if [ "$ret" -eq 0 ] ; then ./opensipsctl ul show | grep "AOR:: 1000" > /dev/null ret=$? fi ; TMP=`PGPASSWORD='opensipsro' psql -A -t -n -q -h localhost -U opensipsro opensips -c "select COUNT(*) from location where username='1000';" | tail -n 1` if [ "$TMP" -eq 0 ] ; then ret=1 fi ; # unregister the user cat ../test/unregister.sip | nc -q 1 -u localhost 5060 > /dev/null if [ "$ret" -eq 0 ] ; then ./opensipsctl ul show | grep "AOR:: 1000" > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then ret=1 else ret=0 fi ; fi ; ret=`PGPASSWORD='opensipsro' psql -A -t -n -q -h localhost -U opensipsro opensips -c "select COUNT(*) from location where username='1000';" | tail -n 1` killall -9 opensips cd ../test mv $CFG.tmp $CFG exit $ret opensips-2.2.2/test/23.sh000077500000000000000000000103731300170765700151310ustar00rootroot00000000000000#!/bin/bash # loads a carrierroute config for loadbalancing from postgres database # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_opensips && check_module "carrierroute" && check_module "db_postgres"); then exit 0 fi ; CFG=13.cfg cp $CFG $CFG.bak # setup config echo "loadmodule \"db_postgres/db_postgres.so\"" >> $CFG echo "modparam(\"carrierroute\", \"config_source\", \"db\")" >> $CFG echo "modparam(\"carrierroute\", \"db_url\", \"postgres://opensipsro:opensipsro@localhost/opensips\")" >> $CFG # setup database PGPASSWORD='opensipsrw' psql -A -t -n -q -h localhost -U opensips opensips -c "insert into route_tree (id, carrier) values ('1', 'carrier1'); insert into route_tree (id, carrier) values ('2', 'default'); insert into route_tree (id, carrier) values ('3', 'premium'); insert into carrierroute (id, carrier, scan_prefix, domain, prob, strip, rewrite_host) values ('1','1','49','0','0.5','0','host1.local.domain'); insert into carrierroute (id, carrier, scan_prefix, domain, prob, strip, rewrite_host) values ('2','1','49','0','0.5','0','host2.local.domain'); insert into carrierroute (id, carrier, scan_prefix, domain, prob, strip, rewrite_host) values ('3','1','42','0','0.3','0','host3.local'); insert into carrierroute (id, carrier, scan_prefix, domain, prob, strip, rewrite_host) values ('4','1','42','0','0.7','0','host4.local'); insert into carrierroute (id, carrier, scan_prefix, domain, prob, strip, rewrite_host) values ('5','1','1','0','0.5','0','host1-ca.local:5060'); insert into carrierroute (id, carrier, scan_prefix, domain, prob, strip, rewrite_host) values ('6','1','1','0','0.5','0','host2-ca.local.domain:5060'); insert into carrierroute (id, carrier, scan_prefix, domain, prob, strip, rewrite_host) values ('10','1','','0','0.1','0','host5.local'); insert into carrierroute (id, carrier, scan_prefix, domain, prob, strip, rewrite_host) values ('20','2','','0','1','0','host6'); insert into carrierroute (id, carrier, scan_prefix, domain, prob, strip, rewrite_host) values ('21','3','','0','1','0','premium.host.local');" ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 cd ../scripts TMPFILE=`mktemp -t opensips-test.XXXXXXXXXX` if [ "$ret" -eq 0 ] ; then ./opensipsctl fifo cr_dump_routes > $TMPFILE ret=$? fi ; if [ "$ret" -eq 0 ] ; then tmp=`grep -v "Printing routing information: Printing tree for carrier premium (3) Printing tree for domain 0 NULL: 100.000 %, 'premium.host.local': ON, '0', '', '', '' Printing tree for carrier default (2) Printing tree for domain 0 NULL: 100.000 %, 'host6': ON, '0', '', '', '' Printing tree for carrier carrier1 (1) Printing tree for domain 0 1: 50.000 %, 'host2-ca.local.domain:5060': ON, '0', '', '', '' 1: 50.000 %, 'host1-ca.local:5060': ON, '0', '', '', '' 42: 70.140 %, 'host4.local': ON, '0', '', '', '' 42: 30.060 %, 'host3.local': ON, '0', '', '', '' 49: 50.000 %, 'host2.local.domain': ON, '0', '', '', '' 49: 50.000 %, 'host1.local.domain': ON, '0', '', '', '' NULL: 100.000 %, 'host5.local': ON, '0', '', '', ''" $TMPFILE` if [ "$tmp" = "" ] ; then ret=0 else ret=1 fi ; fi ; killall -9 opensips # cleanup database PGPASSWORD='opensipsrw' psql -A -t -n -q -h localhost -U opensips opensips -c "delete from route_tree where id = 1; delete from route_tree where id = 2; delete from route_tree where id = 3; delete from carrierroute where carrier=1; delete from carrierroute where carrier=2; delete from carrierroute where carrier=3;" cd ../test mv $CFG.bak $CFG rm $TMPFILE exit $ret opensips-2.2.2/test/24.sh000077500000000000000000000033121300170765700151250ustar00rootroot00000000000000#!/bin/bash # creates a postgres database with opensipsdbctl and deletes it again # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Needs a mysql database, the root user password must be given # in the file 'dbrootpw' in the test directory if [ ! -f ~/.pgpass ] ; then echo "no .pgpass file, not run" exit 0 fi ; tmp_name=""$RANDOM"_opensipsdb_tmp" cd ../scripts # setup config file cp opensipsctlrc opensipsctlrc.bak sed -i "s/# DBENGINE=MYSQL/DBENGINE=PGSQL/g" opensipsctlrc sed -i "s/# INSTALL_EXTRA_TABLES=ask/INSTALL_EXTRA_TABLES=yes/g" opensipsctlrc sed -i "s/# INSTALL_PRESENCE_TABLES=ask/INSTALL_PRESENCE_TABLES=yes/g" opensipsctlrc cp opensipsdbctl opensipsdbctl.bak sed -i "s/TEST=\"false\"/TEST=\"true\"/g" opensipsdbctl ./opensipsdbctl create $tmp_name &> /dev/null ret=$? if [ "$ret" -eq 0 ] ; then ./opensipsdbctl drop $tmp_name &> /dev/null ret=$? fi ; # cleanup mv opensipsctlrc.bak opensipsctlrc mv opensipsdbctl.bak opensipsdbctl cd ../test exit $ret opensips-2.2.2/test/25.cfg000066400000000000000000000017311300170765700152530ustar00rootroot00000000000000log_level=0 listen=127.0.0.1 port=5059 mpath="../modules/" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "usrloc/usrloc.so" loadmodule "registrar/registrar.so" loadmodule "db_mysql/db_mysql.so" loadmodule "userblacklist/userblacklist.so" loadmodule "maxfwd/maxfwd.so" loadmodule "mi_fifo/mi_fifo.so" modparam("usrloc", "db_mode", 3) modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") route { #xlog("user: $rU\n"); $avp(req-username) = $rU; $avp(req-domain) = $rd; if(!lookup ("location")){ sl_send_reply("404", "Not Found"); exit; } if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (!check_user_blacklist("$avp(req-username)", "$avp(req-domain)")) { xlog("$rU user blacklisted\n"); sl_send_reply("403", "Forbidden"); exit; } if (!check_blacklist("globalblacklist")) { xlog("$rU globally blacklisted\n"); sl_send_reply("403", "Forbidden"); exit; } xlog("$rU not blacklisted\n"); t_relay(); } opensips-2.2.2/test/25.sh000077500000000000000000000123741300170765700151360ustar00rootroot00000000000000#!/bin/bash # loads a userblacklist config from mysql database and test some lists # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips); then exit 0 fi ; CFG=25.cfg MYSQL="mysql opensips -u opensips --password=opensipsrw -e" # add an registrar entry to the db; $MYSQL "insert into location (username,contact,socket,user_agent,cseq,q) values (\"49721123456789\",\"sip:123456789@localhost\",\"udp:127.0.0.1:5060\",\"ser_test\",1,-1);" $MYSQL "insert into location (username,contact,socket,user_agent,cseq,q) values (\"49721123456788\",\"sip:123456788@localhost\",\"udp:127.0.0.1:5060\",\"ser_test\",1,-1);" $MYSQL "insert into location (username,contact,socket,user_agent,cseq,q) values (\"49721123456787\",\"sip:123456787@localhost\",\"udp:127.0.0.1:5060\",\"ser_test\",1,-1);" $MYSQL "insert into location (username,contact,socket,user_agent,cseq,q) values (\"49721123456786\",\"sip:123456786@localhost\",\"udp:127.0.0.1:5060\",\"ser_test\",1,-1);" $MYSQL "insert into location (username,contact,socket,user_agent,cseq,q) values (\"49721123456785\",\"sip:223456789@localhost\",\"udp:127.0.0.1:5060\",\"ser_test\",1,-1);" # setup userblacklist, first some dummy data $MYSQL "insert into userblacklist (username, domain, prefix, whitelist) values ('494675454','','49900','0');" $MYSQL "insert into userblacklist (username, domain, prefix, whitelist) values ('494675453','test.domain','49901','0');" $MYSQL "insert into userblacklist (username, domain, prefix, whitelist) values ('494675231','test','499034132','0');" $MYSQL "insert into userblacklist (username, domain, prefix, whitelist) values ('494675231','','499034133','1');" # some actual data $MYSQL "insert into userblacklist (username, domain, prefix, whitelist) values ('49721123456789','','12345','0');" $MYSQL "insert into userblacklist (username, domain, prefix, whitelist) values ('49721123456788','','123456788','1');" $MYSQL "insert into userblacklist (username, domain, prefix, whitelist) values ('49721123456788','','1234','0');" # and the global ones $MYSQL "insert into globalblacklist (prefix, whitelist, description) values ('123456787','0','_test_');" $MYSQL "insert into globalblacklist (prefix, whitelist, description) values ('123456','0','_test_');" $MYSQL "insert into globalblacklist (prefix, whitelist, description) values ('1','1','_test_');" $MYSQL "insert into globalblacklist (prefix, whitelist, description) values ('','0','_test_');" ../opensips -w . -f $CFG &> /dev/null sleep 1 sipp -sn uas -bg -i localhost -p 5060 &> /dev/null sipp -sn uac -s 49721123456789 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 2 -p 5061 &> /dev/null ret=$? if [ "$ret" -eq 1 ] ; then sipp -sn uac -s 49721123456788 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 2 -p 5061 &> /dev/null ret=$? fi; if [ "$ret" -eq 1 ] ; then sipp -sn uac -s 49721123456787 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 2 -p 5061 &> /dev/null ret=$? fi; if [ "$ret" -eq 1 ] ; then sipp -sn uac -s 49721123456786 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 2 -p 5061 &> /dev/null ret=$? fi; $MYSQL "insert into globalblacklist (prefix, whitelist, description) values ('123456786','1','_test_');" ../scripts/opensipsctl fifo reload_blacklist if [ "$ret" -eq 1 ] ; then sipp -sn uac -s 49721123456786 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 2 -p 5061 &> /dev/null ret=$? fi; $MYSQL "insert into userblacklist (username, domain, prefix, whitelist) values ('49721123456786','','12345','0');" if [ "$ret" -eq 0 ] ; then sipp -sn uac -s 49721123456786 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 2 -p 5061 &> /dev/null ret=$? fi; if [ "$ret" -eq 1 ] ; then sipp -sn uac -s 49721123456785 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 2 -p 5061 &> /dev/null ret=$? fi; $MYSQL "insert into globalblacklist (prefix, whitelist, description) values ('2','1','_test_');" ../scripts/opensipsctl fifo reload_blacklist if [ "$ret" -eq 1 ] ; then sipp -sn uac -s 49721123456785 127.0.0.1:5059 -i 127.0.0.1 -m 1 -f 2 -p 5061 &> /dev/null ret=$? fi; # cleanup: killall -9 sipp > /dev/null 2>&1 killall -9 opensips > /dev/null 2>&1 $MYSQL "delete from location where (user_agent = \"ser_test\");" $MYSQL "delete from userblacklist where username='49721123456786';" $MYSQL "delete from userblacklist where username='49721123456788';" $MYSQL "delete from userblacklist where username='49721123456789';" $MYSQL "delete from userblacklist where username='494675231';" $MYSQL "delete from userblacklist where username='494675453';" $MYSQL "delete from userblacklist where username='494675454';" $MYSQL "delete from globalblacklist where description='_test_';" exit $ret; opensips-2.2.2/test/26.cfg000066400000000000000000000025121300170765700152520ustar00rootroot00000000000000log_level=3 mpath="../modules" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "maxfwd/maxfwd.so" loadmodule "carrierroute/carrierroute.so" loadmodule "mi_fifo/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); return; } if (msg:len > max_len) { sl_send_reply("513", "Message too big"); return; } # set flag for later test setflag(0); # user route if(cr_user_carrier("$rU", "$rd", "$avp(carrier)")) { $avp(domain)="0"; if (!cr_route("$avp(carrier)", "$avp(domain)", "$rU", "$rU", "call_id", "$avp(host)")) { xlog("L_ERR", "cr_route failed\n"); exit; } t_on_failure("1"); } else { # default route if (!cr_route("default", "0", "$rU", "$rU", "call_id")) { xlog("L_ERR", "cr_route failed\n"); exit; } } if (!t_relay()) { sl_reply_error(); } } failure_route[1] { revert_uri(); if (!cr_next_domain("$avp(carrier)", "$avp(domain)", "$rU", "$avp(host)", "$T_reply_code", "$avp(domain)")) { xlog("L_ERR", "cr_next_domain failed\n"); exit; } if (!cr_route("$avp(carrier)", "$avp(domain)", "$rU", "$rU", "call_id", "$avp(host)")) { xlog("L_ERR", "cr_route failed\n"); exit; } t_on_failure("1"); if (!t_relay()) { xlog("L_ERR", "t_relay failed\n"); exit; } } opensips-2.2.2/test/26.sh000077500000000000000000000135301300170765700151320ustar00rootroot00000000000000#!/bin/bash # do some routing with carrierroute route sets from mysql database # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_opensips && check_module "carrierroute" ); then exit 0 fi ; CFG=26.cfg cp $CFG $CFG.bak # setup config echo "loadmodule \"db_mysql/db_mysql.so\"" >> $CFG echo "modparam(\"carrierroute\", \"config_source\", \"db\")" >> $CFG # setup database MYSQL="mysql opensips -u opensips --password=opensipsrw -e" $MYSQL "insert into route_tree (id, carrier) values ('1', 'default');" $MYSQL "insert into route_tree (id, carrier) values ('2', 'carrier1');" $MYSQL "insert into route_tree (id, carrier) values ('3', 'carrier2');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, flags, prob, strip, rewrite_host) values ('1','1','0','49','0','0.5','0','127.0.0.1:7000');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, flags, prob, strip, rewrite_host) values ('2','1','0','49','0','0.5','0','127.0.0.1:8000');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, flags, prob, strip, rewrite_host) values ('3','2','0','49','0','1','0','127.0.0.1:9000');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, flags, mask,prob, strip, rewrite_host) values ('4','3','0','49','0', '3', '1','0','127.0.0.1:10001');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, flags, mask,prob, strip, rewrite_host) values ('5','3','0','49','1', '3', '1','0','127.0.0.1:10000');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, flags, mask, prob, strip, rewrite_host) values ('6','3','0','49','2', '3', '1','0','127.0.0.1:10002');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, flags, prob, strip, rewrite_host) values ('7','3','fallback','49','0','1','0','127.0.0.1:10000');" $MYSQL "insert into carrierroute (id, carrier, domain, scan_prefix, flags, prob, strip, rewrite_host) values ('8','3','2','49','0','1','0','127.0.0.1:10000');" $MYSQL "insert into carrierfailureroute(id, carrier, domain, scan_prefix, host_name, reply_code, flags, mask, next_domain) values ('1', '3', '0', '49', '127.0.0.1:10000', '5..', '', '', 'fallback');" $MYSQL "insert into carrierfailureroute(id, carrier, domain, scan_prefix, host_name, reply_code, flags, mask, next_domain) values ('2', '3', 'fallback', '49', '127.0.0.1:10000', '483', '', '', '2');" $MYSQL "insert into carrierfailureroute(id, carrier, domain, scan_prefix, host_name, reply_code, flags, mask, next_domain) values ('3', '3', 'fallback', '49', '127.0.0.1:9000', '4..', '', '', '2');" $MYSQL "alter table subscriber add cr_preferred_carrier int(10) default NULL;" $MYSQL "insert into subscriber (username, cr_preferred_carrier) values ('49721123456786', 2);" $MYSQL "insert into subscriber (username, cr_preferred_carrier) values ('49721123456785', 3);" ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 if [ "$ret" -eq 0 ] ; then sipp -sn uas -bg -i localhost -m 12 -p 7000 &> /dev/null sipp -sn uas -bg -i localhost -m 12 -p 8000 &> /dev/null sipp -sn uac -s 49721123456787 127.0.0.1:5060 -i 127.0.0.1 -m 20 -p 5061 &> /dev/null ret=$? killall sipp &> /dev/null fi; if [ "$ret" -eq 0 ] ; then sipp -sn uas -bg -i localhost -m 10 -p 9000 &> /dev/null sipp -sn uac -s 49721123456786 127.0.0.1:5060 -i 127.0.0.1 -m 10 -p 5061 &> /dev/null ret=$? fi; if [ "$ret" -eq 0 ] ; then sipp -sn uas -bg -i localhost -m 10 -p 10000 &> /dev/null sipp -sn uac -s 49721123456785 127.0.0.1:5060 -i 127.0.0.1 -m 10 -p 5061 &> /dev/null ret=$? fi; if [ "$ret" -eq 0 ] ; then killall sipp &> /dev/null sipp -sf failure_route.xml -bg -i localhost -m 10 -p 10000 &> /dev/null sipp -sn uac -s 49721123456785 127.0.0.1:5060 -i 127.0.0.1 -m 10 -p 5061 &> /dev/null ret=$? fi; $MYSQL "insert into carrierfailureroute(id, carrier, domain, scan_prefix, host_name, reply_code, flags, mask, next_domain) values ('4', '3', 'fallback', '49', '127.0.0.1:10000', '4..', '', '', '4');" $MYSQL "insert into carrierfailureroute(id, carrier, domain, scan_prefix, host_name, reply_code, flags, mask, next_domain) values ('5', '3', 'fallback', '49', '127.0.0.1:10000', '486', '', '', '2');" if [ ! "$ret" -eq 0 ] ; then ../scripts/opensipsctl fifo cr_reload_routes killall sipp &> /dev/null sipp -sf failure_route.xml -bg -i localhost -m 10 -p 10000 &> /dev/null sipp -sn uac -s 49721123456785 127.0.0.1:5060 -i 127.0.0.1 -m 10 -p 5061 &> /dev/null ret=$? fi; killall -9 opensips killall -9 sipp # cleanup database $MYSQL "delete from route_tree where id = 1;" $MYSQL "delete from route_tree where id = 2;" $MYSQL "delete from route_tree where id = 3;" $MYSQL "delete from carrierroute where carrier=1;" $MYSQL "delete from carrierroute where carrier=2;" $MYSQL "delete from carrierroute where carrier=3;" $MYSQL "delete from carrierfailureroute where carrier=1;" $MYSQL "delete from carrierfailureroute where carrier=2;" $MYSQL "delete from carrierfailureroute where carrier=3;" $MYSQL "delete from subscriber where username='49721123456786';" $MYSQL "delete from subscriber where username='49721123456785';" $MYSQL "alter table subscriber drop cr_preferred_carrier;" cd ../test mv $CFG.bak $CFG exit $ret opensips-2.2.2/test/27.sh000077500000000000000000000027671300170765700151450ustar00rootroot00000000000000#!/bin/bash # test publish and subscribe for presence # Copyright (C) 2008 Voice System # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips && check_module "db_mysql" && check_module "presence" && check_module "presence_xml"); then exit 0 fi ; CFG=presence.cfg ../opensips -w . -f $CFG &> /dev/null; ret=$? sleep 1 if [ "$ret" -eq 0 ] ; then sipp -sf publish_scenario.xml -i 127.0.0.1 -p 5061 -inf publish.csv 127.0.0.1:5059 -recv_timeout 500000 -m 1 &> /dev/null; ret=$? fi; if [ "$ret" -eq 0 ] ; then sipp -sf subscribe_notify_scenario.xml -i 127.0.0.1 -p 5061 -inf subscribe_notify.csv 127.0.0.1:5059 -recv_timeout 500000 -m 1 &> /dev/null; ret=$? fi; #cleanup: killall -9 opensips &> /dev/null; killall -9 sipp &> /dev/null; exit $ret; opensips-2.2.2/test/28.cfg000066400000000000000000000010551300170765700152550ustar00rootroot00000000000000log_level=3 mpath="../modules" loadmodule "sl.so" loadmodule "tm.so" loadmodule "db_mysql.so" loadmodule "cpl_c.so" loadmodule "mi_fifo.so" loadmodule "textops.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("cpl_c","cpl_dtd_file","../modules/cpl_c/cpl-06.dtd") route { if (is_method("INVITE")) { # run incoming script if ( !cpl_run_script("incoming","force_stateful") ) { # script execution failed sl_send_reply("500","CPL script execution failed"); exit; }; }; } opensips-2.2.2/test/28.sh000077500000000000000000000033371300170765700151400ustar00rootroot00000000000000#!/bin/bash # tests simple cpl_c script operations with mysql # Copyright (C) 2008 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips && check_module "db_mysql" && check_module "cpl_c"); then exit 0 fi ; CFG=28.cfg CPL=cpl_ignore.xml TMPFILE=`mktemp -t opensips-test.XXXXXXXXXX` ../opensips -w . -f $CFG &> /dev/null; ret=$? sleep 1 ../scripts/opensipsctl fifo LOAD_CPL sip:alice@127.0.0.1 $CPL if [ "$ret" -eq 0 ] ; then sipp -m 1 -f 1 127.0.0.1:5060 -sf cpl_test.xml &> /dev/null; ret=$? fi; if [ "$ret" -eq 0 ] ; then ../scripts/opensipsctl fifo GET_CPL sip:alice@127.0.0.1 > $TMPFILE diff $TMPFILE $CPL ret=$? fi; if [ "$ret" -eq 0 ] ; then ../scripts/opensipsctl fifo REMOVE_CPL sip:alice@127.0.0.1 ../scripts/opensipsctl fifo GET_CPL sip:alice@127.0.0.1 > $TMPFILE fi; diff $TMPFILE $CPL &> /dev/null; ret=$? if [ ! "$ret" -eq 0 ] ; then ret=0 fi; #cleanup: killall -9 opensips &> /dev/null; killall -9 sipp &> /dev/null; rm $TMPFILE exit $ret; opensips-2.2.2/test/29.sh000077500000000000000000000036471300170765700151450ustar00rootroot00000000000000#!/bin/bash # tests simple cpl_c script operations with postgres # Copyright (C) 2008 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips && check_module "db_postgres" && check_module "cpl_c"); then exit 0 fi ; CFG=28.cfg CPL=cpl_ignore.xml TMPFILE=`mktemp -t opensips-test.XXXXXXXXXX` cp $CFG $CFG.tmp echo "loadmodule \"db_postgres/db_postgres.so\"" >> $CFG echo "modparam(\"cpl_c\", \"db_url\", \"postgres://opensips:opensipsrw@localhost/opensips\")" >> $CFG ../opensips -w . -f $CFG &> /dev/null; ret=$? sleep 1 ../scripts/opensipsctl fifo LOAD_CPL sip:alice@127.0.0.1 $CPL if [ "$ret" -eq 0 ] ; then sipp -m 1 -f 1 127.0.0.1:5060 -sf cpl_test.xml &> /dev/null; ret=$? fi; if [ "$ret" -eq 0 ] ; then ../scripts/opensipsctl fifo GET_CPL sip:alice@127.0.0.1 > $TMPFILE diff $TMPFILE $CPL ret=$? fi; if [ "$ret" -eq 0 ] ; then ../scripts/opensipsctl fifo REMOVE_CPL sip:alice@127.0.0.1 ../scripts/opensipsctl fifo GET_CPL sip:alice@127.0.0.1 > $TMPFILE fi; diff $TMPFILE $CPL &> /dev/null; ret=$? if [ ! "$ret" -eq 0 ] ; then ret=0 fi; #cleanup: killall -9 opensips &> /dev/null; killall -9 sipp &> /dev/null; rm $TMPFILE mv $CFG.tmp $CFG exit $ret; opensips-2.2.2/test/3.sh000077500000000000000000000035701300170765700150500ustar00rootroot00000000000000#!/bin/bash # creates a mysql database with opensipsdbctl and deletes it again # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Needs a mysql database, the root user password must be given # in the file 'dbrootpw' in the test directory if [ ! -f dbrootpw ] ; then echo "no root password, not run" exit 0 fi ; source dbrootpw tmp_name=""$RANDOM"_opensipsdb_tmp" cd ../scripts # setup config file cp opensipsctlrc opensipsctlrc.bak sed -i "s/# DBENGINE=MYSQL/DBENGINE=MYSQL/g" opensipsctlrc sed -i "s/# INSTALL_EXTRA_TABLES=ask/INSTALL_EXTRA_TABLES=yes/g" opensipsctlrc sed -i "s/# INSTALL_PRESENCE_TABLES=ask/INSTALL_PRESENCE_TABLES=yes/g" opensipsctlrc cp opensipsdbctl opensipsdbctl.bak sed -i "s/TEST=\"false\"/TEST=\"true\"/g" opensipsdbctl # set the mysql root password cp opensipsdbctl.mysql opensipsdbctl.mysql.bak sed -i "s/#PW=""/PW="$PW"/g" opensipsdbctl.mysql ./opensipsdbctl create $tmp_name > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then ./opensipsdbctl drop $tmp_name > /dev/null ret=$? fi ; # cleanup mv opensipsctlrc.bak opensipsctlrc mv opensipsdbctl.mysql.bak opensipsdbctl.mysql mv opensipsdbctl.bak opensipsdbctl cd ../test exit $ret opensips-2.2.2/test/30.cfg000066400000000000000000000010231300170765700152410ustar00rootroot00000000000000log_level=3 mpath="../modules" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "maxfwd/maxfwd.so" loadmodule "carrierroute/carrierroute.so" route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); return; } if (msg:len > max_len) { sl_send_reply("513", "Message too big"); return; } # default route if (!cr_route("default", "proxy", "$rU", "$rU", "call_id")) { xlog("L_ERR", "cr_route failed\n"); exit; } if (!t_relay()) { sl_reply_error(); } } opensips-2.2.2/test/30.sh000077500000000000000000000036161300170765700151310ustar00rootroot00000000000000#!/bin/bash # do some routing with carrierroute route sets from a config file # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips && check_module "carrierroute"); then exit 0 fi ; CFG=30.cfg cp $CFG $CFG.bak # setup config echo "modparam(\"carrierroute\", \"config_file\", \"`pwd`/../test/carrierroute-2.cfg\")" >> $CFG ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 if [ "$ret" -eq 0 ] ; then # the distribution is not perfect sipp -sn uas -bg -i localhost -m 12 -p 7000 &> /dev/null sipp -sn uas -bg -i localhost -m 12 -p 8000 &> /dev/null sipp -sn uac -s 49721123456787 127.0.0.1:5060 -i 127.0.0.1 -m 20 -p 5061 &> /dev/null ret=$? killall sipp &> /dev/null fi; if [ "$ret" -eq 0 ] ; then sipp -sn uas -bg -i localhost -m 10 -p 9000 &> /dev/null sipp -sn uac -s 49721123456786 127.0.0.1:5060 -i 127.0.0.1 -m 10 -p 5061 &> /dev/null ret=$? fi; if [ "$ret" -eq 0 ] ; then sipp -sn uas -bg -i localhost -m 10 -p 10000 &> /dev/null sipp -sn uac -s 49721123456785 127.0.0.1:5060 -i 127.0.0.1 -m 10 -p 5061 &> /dev/null ret=$? fi; killall -9 opensips killall -9 sipp cd ../test mv $CFG.bak $CFG exit $ret opensips-2.2.2/test/31.sh000077500000000000000000000030241300170765700151230ustar00rootroot00000000000000#!/bin/bash # database access with fetch_result for usrloc on mysql # Copyright (C) 2008 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_opensips && check_module "db_mysql"); then exit 0 fi ; CFG=11.cfg DOMAIN="local" MYSQL="mysql opensips -u opensips --password=opensipsrw -e" cp $CFG $CFG.bak echo "loadmodule \"db_mysql/db_mysql.so\"" >> $CFG echo "modparam(\"usrloc\", \"fetch_rows\", 13)" >> $CFG COUNTER=0 while [ $COUNTER -lt 139 ]; do COUNTER=$(($COUNTER+1)) $MYSQL "insert into location (username, domain, contact, user_agent) values ('foobar-$COUNTER', '$DOMAIN', 'foobar-$COUNTER@$DOMAIN', '___test___');" done ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 killall -9 opensips $MYSQL "delete from location where user_agent = '___test___'" mv $CFG.bak $CFG exit $ret opensips-2.2.2/test/32.sh000077500000000000000000000033471300170765700151340ustar00rootroot00000000000000#!/bin/bash # database access with fetch_result for usrloc on postgres # Copyright (C) 2008 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips && check_module "db_postgres"); then exit 0 fi ; CFG=11.cfg cp $CFG $CFG.bak echo "loadmodule \"db_postgres/db_postgres.so\"" >> $CFG echo "modparam(\"usrloc\", \"db_url\", \"postgres://opensips:opensipsrw@localhost/opensips\")" >> $CFG echo "modparam(\"usrloc\", \"fetch_rows\", 13)" >> $CFG DOMAIN="local" COUNTER=0 while [ $COUNTER -lt 139 ]; do COUNTER=$(($COUNTER+1)) PGPASSWORD='opensipsrw' psql -A -t -n -q -h localhost -U opensips opensips -c "insert into location (username, domain, contact, user_agent) values ('foobar-$COUNTER', '$DOMAIN', 'foobar-$COUNTER@$DOMAIN', '___test___');" done ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 killall -9 opensips PGPASSWORD='opensipsrw' psql -A -t -n -q -h localhost -U opensips opensips -c "delete from location where user_agent = '___test___'" mv $CFG.bak $CFG exit $ret opensips-2.2.2/test/33.cfg000066400000000000000000000011301300170765700152430ustar00rootroot00000000000000log_level=3 memlog=2 mpath="../modules/" loadmodule "cfgutils.so" loadmodule "mi_fifo/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("cfgutils", "initial_probability", 15) modparam("cfgutils", "hash_file", "33.cfg") modparam("cfgutils", "shvset", "debug=i:1") modparam("cfgutils", "shvset", "pstngw=s:sip:10.10.10.10") modparam("cfgutils", "varset", "init=i:1") modparam("cfgutils", "varset", "gw=s:sip:11.11.11.11;transport=tcp") route{ xlog("PGK status:"); usleep("100"); pkg_status(); sleep("1"); xlog("SHM status:"); shm_status(); abort(); xlog("end"); } opensips-2.2.2/test/33.sh000077500000000000000000000032221300170765700151250ustar00rootroot00000000000000#!/bin/bash # test cfgutils module # Copyright (C) 2008 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_netcat && check_opensips); then exit 0 fi; if [ -e core ] ; then echo "core file found, not run" exit 0 fi; CFG=33.cfg cp $CFG $CFG.bak ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 if [ $ret -eq 0 ] ; then ../scripts/opensipsctl fifo check_config_hash |grep "The actual config file hash is identical to the stored one." > /dev/null ret=$? fi; echo " " >> $CFG if [ $ret -eq 0 ] ; then ../scripts/opensipsctl fifo check_config_hash |grep "The actual config file hash is identical to the stored one." /dev/null ret=$? fi; if [ ! $ret -eq 0 ] ; then # send a message cat register.sip | nc -q 1 -u localhost 5060 > /dev/null fi; sleep 1 killall -9 opensips &> /dev/null ret=$? if [ $ret -eq 0 ] ; then ret=1 else ret=0 fi; if [ ! -e core ] ; then ret=1 fi; rm core mv $CFG.bak $CFG exit $ret opensips-2.2.2/test/34.cfg000066400000000000000000000012041300170765700152460ustar00rootroot00000000000000mpath="../modules" loadmodule "sl.so" loadmodule "tm.so" loadmodule "usrloc.so" loadmodule "presence.so" loadmodule "presence_xml.so" loadmodule "presence_mwi.so" loadmodule "pua.so" loadmodule "pua_bla.so" loadmodule "pua_mi.so" loadmodule "pua_usrloc.so" loadmodule "xmpp.so" loadmodule "pua_xmpp.so" loadmodule "xcap_client.so" loadmodule "rls.so" modparam("rls", "xcap_root", "http://localhost/xcap-root:8000") modparam("presence_xml", "integrated_xcap_server", 1) modparam("pua_bla|pua_usrloc", "default_domain", "localhost") modparam("pua_bla", "header_name", "Sender") modparam("pua_bla|pua_xmpp", "server_address", "sip:bla@127.0.0.1")opensips-2.2.2/test/34.sh000077500000000000000000000027351300170765700151360ustar00rootroot00000000000000#!/bin/bash # load all presence related modules with mysql # Copyright (C) 2008 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Needs a default opensips database setup for mysql source include/require CFG=34.cfg if ! (check_opensips && check_module "db_mysql" && check_module "presence" \ && check_module "presence_xml" && check_module "pua" \ && check_module "xcap_client" && check_module "rls" \ && check_module "presence_mwi" && check_module "pua_bla" \ && check_module "pua_mi" && check_module "pua_usrloc" \ && check_module "pua_xmpp" && check_module "xmpp"); then exit 0 fi ; cp $CFG $CFG.bak echo "loadmodule \"db_mysql/db_mysql.so\"" >> $CFG # start ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 killall -9 opensips mv $CFG.bak $CFG exit $ret opensips-2.2.2/test/35.cfg000066400000000000000000000025641300170765700152610ustar00rootroot00000000000000# OpenSIPS config for lookup / registrar testing #------------------------Global configuration---------------------------------- log_level=3 log_stderror=no listen=udp:127.0.0.1:5060 dns=no rev_dns=no #-----------------------Loading Modules------------------------------------- mpath="../modules/" loadmodule "db_mysql/db_mysql.so" loadmodule "usrloc/usrloc.so" loadmodule "registrar/registrar.so" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "permissions/permissions.so" loadmodule "maxfwd/maxfwd.so" loadmodule "mi_fifo/mi_fifo.so" #-----------------------Module parameters------------------------------------- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("usrloc", "db_mode", 3) modparam("usrloc|permissions", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") #-----------------------Routing configuration---------------------------------# route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit(); } if (!allow_address("0", "$si", "$sp") || !allow_source_address("0")) { #xlog("permissions"); sl_send_reply("403", "Forbidden"); } if(!lookup ("location")){ sl_send_reply("404", "Not Found"); } if(method==INVITE){ if (!t_relay()) { sl_reply_error(); } } if (method== ACK) { if (!t_relay()) { sl_reply_error(); } } if (method==BYE){ if (!t_relay("0x04")) { sl_reply_error(); } } exit(); } opensips-2.2.2/test/35.sh000077500000000000000000000050211300170765700151260ustar00rootroot00000000000000#!/bin/bash # check permissions module functionality # Copyright (C) 2008 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. source include/require if ! (check_sipp && check_opensips); then exit 0 fi ; CFG=35.cfg SRV=5060 UAS=5070 UAC=5080 IP="127.0.0.31" MASK=27 # add an registrar entry to the db; mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "INSERT INTO location (username,contact,socket,user_agent,cseq,q) VALUES (\"foo\",\"sip:foo@localhost:$UAS\",\"udp:127.0.0.1:$UAS\",\"ser_test\",1,-1);" mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "INSERT INTO address (ip_addr, mask) VALUES ('$IP', '$MASK');" ../opensips -w . -f $CFG &> /dev/null sipp -sn uas -bg -i localhost -m 10 -f 2 -p $UAS &> /dev/null sipp -sn uac -s foo 127.0.0.1:$SRV -i localhost -m 10 -f 2 -p $UAC &> /dev/null ret=$? mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "DELETE FROM address WHERE (ip_addr='$IP' AND mask='$MASK');" if [ "$ret" -eq 0 ] ; then killall sipp IP="127.47.6.254" MASK=10 mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "INSERT INTO address (ip_addr, mask) VALUES ('$IP', '$MASK');" ../scripts/opensipsctl fifo address_reload #../scripts/opensipsctl fifo address_dump sipp -sn uas -bg -i localhost -m 10 -f 2 -p $UAS &> /dev/null sipp -sn uac -s foo 127.0.0.1:$SRV -i localhost -m 10 -f 2 -p $UAC &> /dev/null ret=$? mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "DELETE FROM address WHERE (ip_addr='$IP' AND mask='$MASK');" fi; # cleanup killall -9 sipp > /dev/null 2>&1 killall -9 opensips > /dev/null 2>&1 mysql --show-warnings -B -u opensips --password=opensipsrw -D opensips -e "DELETE FROM location WHERE ((contact = \"sip:foo@localhost:$UAS\") and (user_agent = \"ser_test\"));" exit $ret; opensips-2.2.2/test/4.sh000077500000000000000000000022571300170765700150520ustar00rootroot00000000000000#!/bin/bash # test basic fifo functionality # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. CFG=4.cfg # setup config echo -e "loadmodule \"../modules/mi_fifo/mi_fifo.so\"" > $CFG echo -e "modparam(\"mi_fifo\", \"fifo_name\", \"/tmp/opensips_fifo\")" >> $CFG ../opensips -w . -f $CFG > /dev/null ret=$? cd ../scripts if [ "$ret" -eq 0 ] ; then sleep 1 ./opensipsctl ps > /dev/null ret=$? fi ; cd ../test killall -9 opensips rm -f $CFG exit $ret opensips-2.2.2/test/5.cfg000066400000000000000000000237051300170765700151760ustar00rootroot00000000000000# # OpenSIPS basic configuration script # by Anca Vamanu # # Please refer to the Core CookBook at http://www.opensips.org/dokuwiki/doku.php # for a explanation of possible statements, functions and parameters. # ####### Global Parameters ######### log_level=3 log_stderror=no log_facility=LOG_LOCAL0 children=4 /* uncomment the following lines to enable debugging */ #debug_mode=yes /* uncomment the next line to disable TCP (default on) */ #disable_tcp=yes /* uncomment the next line to enable the auto temporary blacklisting of not available destinations (default disabled) */ #disable_dns_blacklist=no /* uncomment the next line to enable IPv6 lookup after IPv4 dns lookup failures (default disabled) */ #dns_try_ipv6=yes /* uncomment the next line to disable the auto discovery of local aliases based on revers DNS on IPs (default on) */ #auto_aliases=no /* uncomment the following lines to enable TLS support (default off) */ #disable_tls = no #listen = tls:your_IP:5061 #tls_verify_server = 1 #tls_verify_client = 1 #tls_require_client_certificate = 0 #tls_method = TLSv1 #tls_certificate = "/usr/local/etc/opensips/tls/user/user-cert.pem" #tls_private_key = "/usr/local/etc/opensips/tls/user/user-privkey.pem" #tls_ca_list = "/usr/local/etc/opensips/tls/user/user-calist.pem" port=5060 /* uncomment and configure the following line if you want opensips to bind on a specific interface/port/proto (default bind on all available) */ #listen=udp:192.168.1.2:5060 ####### Modules Section ######## #set module path mpath="../modules" /* uncomment next line for MySQL DB support */ #loadmodule "db_mysql.so" loadmodule "sl/sl.so" loadmodule "tm/tm.so" loadmodule "rr/rr.so" loadmodule "maxfwd/maxfwd.so" loadmodule "usrloc/usrloc.so" loadmodule "registrar/registrar.so" loadmodule "textops/textops.so" loadmodule "mi_fifo/mi_fifo.so" loadmodule "uri_db/uri_db.so" loadmodule "uri/uri.so" loadmodule "acc/acc.so" /* uncomment next lines for MySQL based authentication support NOTE: a DB (like db_mysql) module must be also loaded */ #loadmodule "auth.so" #loadmodule "auth_db.so" /* uncomment next line for aliases support NOTE: a DB (like db_mysql) module must be also loaded */ #loadmodule "alias_db.so" /* uncomment next line for multi-domain support NOTE: a DB (like db_mysql) module must be also loaded NOTE: be sure and enable multi-domain support in all used modules (see "multi-module params" section ) */ #loadmodule "domain.so" /* uncomment the next two lines for presence server support NOTE: a DB (like db_mysql) module must be also loaded */ #loadmodule "presence.so" #loadmodule "presence_xml.so" # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # ----- rr params ----- # do not append from tag to the RR (no need for this script) modparam("rr", "append_fromtag", 0) # ----- rr params ----- modparam("registrar", "method_filtering", 1) /* uncomment the next line to disable parallel forking via location */ # modparam("registrar", "append_branches", 0) /* uncomment the next line not to allow more than 10 contacts per AOR */ #modparam("registrar", "max_contacts", 10) # ----- uri_db params ----- /* by default we disable the DB support in the module as we do not need it in this configuration */ modparam("uri_db", "use_uri_table", 0) modparam("uri_db", "db_url", "") # ----- acc params ----- /* what sepcial events should be accounted ? */ modparam("acc", "early_media", 1) modparam("acc", "report_cancels", 1) /* by default ww do not adjust the direct of the sequential requests. if you enable this parameter, be sure the enable "append_fromtag" in "rr" module */ modparam("acc", "detect_direction", 0) /* account triggers (flags) */ modparam("acc", "failed_transaction_flag", 3) modparam("acc", "log_flag", 1) modparam("acc", "log_missed_flag", 2) /* uncomment the following lines to enable DB accounting also */ modparam("acc", "db_flag", 1) modparam("acc", "db_missed_flag", 2) # ----- usrloc params ----- modparam("usrloc", "db_mode", 0) /* uncomment the following lines if you want to enable DB persistency for location entries */ #modparam("usrloc", "db_mode", 2) #modparam("usrloc", "db_url", # "mysql://opensips:opensipsrw@192.168.1.3/opensips_1_3") # ----- auth_db params ----- /* uncomment the following lines if you want to enable the DB based authentication */ #modparam("auth_db", "calculate_ha1", yes) #modparam("auth_db", "password_column", "password") #modparam("auth_db", "db_url", # "mysql://opensips:opensipsrw@192.168.1.3/opensips_1_3") #modparam("auth_db", "load_credentials", "") # ----- alias_db params ----- /* uncomment the following lines if you want to enable the DB based aliases */ #modparam("alias_db", "db_url", # "mysql://opensips:opensipsrw@192.168.1.3/opensips_1_3") # ----- domain params ----- /* uncomment the following lines to enable multi-domain detection support */ #modparam("domain", "db_url", # "mysql://opensips:opensipsrw@192.168.1.3/opensips_1_3") #modparam("domain", "db_mode", 1) # Use caching # ----- multi-module params ----- /* uncomment the following line if you want to enable multi-domain support in the modules (dafault off) */ #modparam("alias_db|auth_db|usrloc|uri_db", "use_domain", 1) # ----- presence params ----- /* uncomment the following lines if you want to enable presence */ #modparam("presence|presence_xml", "db_url", # "mysql://opensips:opensipsrw@192.168.1.3/opensips_1_3") #modparam("presence_xml", "force_active", 1) #modparam("presence", "server_address", "sip:192.168.1.2:5060") ####### Routing Logic ######## # main request routing logic route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { if (is_method("BYE")) { setflag(1); # do accouting ... setflag(3); # ... even if the transaction fails } route(1); } else { /* uncomment the following lines if you want to enable presence */ ##if (is_method("SUBSCRIBE") && $rd == "your.server.ip.address") { ## # in-dialog subscribe requests ## route(2); ## exit; ##} if ( is_method("ACK") ) { if ( t_check_trans() ) { # non loose-route, but stateful ACK; must be an ACK after a 487 or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction ... ignore and discard.\n"); exit; } } sl_send_reply("404","Not here"); } exit; } #initial requests # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); # authenticate if from local subscriber (uncomment to enable auth) ##if (!(method=="REGISTER") && from_uri==myself) ##{ ## if (!proxy_authorize("", "subscriber")) { ## proxy_challenge("", "0"); ## exit; ## } ## if (!db_check_from()) { ## sl_send_reply("403","Forbidden auth ID"); ## exit; ## } ## ## consume_credentials(); ## # caller authenticated ##} # record routing if (!is_method("REGISTER|MESSAGE")) record_route(); # account only INVITEs if (is_method("INVITE")) { setflag(1); # do accouting } if (!uri==myself) /* replace with following line if multi-domain support is used */ ##if (!is_uri_host_local()) { append_hf("P-hint: outbound\r\n"); # if you have some interdomain connections via TLS ##if($rd=="tls_domain1.net") { ## t_relay("tls:domain1.net"); ## exit; ##} else if($rd=="tls_domain2.net") { ## t_relay("tls:domain2.net"); ## exit; ##} route(1); } # requests for my domain /* uncomment this if you want to enable presence server and comment the next 'if' block NOTE: uncomment also the definition of route[2] from below */ ##if( is_method("PUBLISH|SUBSCRIBE")) ## route(2); if (is_method("PUBLISH")) { sl_send_reply("503", "Service Unavailable"); exit; } if (is_method("REGISTER")) { # authenticate the REGISTER requests (uncomment to enable auth) ##if (!www_authorize("", "subscriber")) ##{ ## www_challenge("", "0"); ## exit; ##} ## ##if (!db_check_to()) ##{ ## sl_send_reply("403","Forbidden auth ID"); ## exit; ##} if (!save("location")) sl_reply_error(); exit; } if ($rU==NULL) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } # apply DB based aliases (uncomment to enable) ##alias_db_lookup("dbaliases"); if (!lookup("location")) { switch ($retcode) { case -1: case -3: t_newtran(); t_reply("404", "Not Found"); exit; case -2: sl_send_reply("405", "Method Not Allowed"); exit; } } # when routing via usrloc, log the missed calls also setflag(2); route(1); } route[1] { # for INVITEs enable some additional helper routes if (is_method("INVITE")) { t_on_branch("2"); t_on_reply("2"); t_on_failure("1"); } if (!t_relay()) { sl_reply_error(); }; exit; } # Presence route /* uncomment the whole following route for enabling presence NOTE: do not forget to enable the call of this route from the main route */ ##route[2] ##{ ## if (!t_newtran()) ## { ## sl_reply_error(); ## exit; ## }; ## ## if(is_method("PUBLISH")) ## { ## handle_publish(); ## t_release(); ## } ## else ## if( is_method("SUBSCRIBE")) ## { ## handle_subscribe(); ## t_release(); ## } ## ## exit; ##} branch_route[2] { xlog("new branch at $ru\n"); } onreply_route[2] { xlog("incoming reply\n"); } failure_route[1] { if (t_was_cancelled()) { exit; } # uncomment the following lines if you want to block client # redirect based on 3xx replies. ##if (t_check_status("3[0-9][0-9]")) { ##t_reply("404","Not found"); ## exit; ##} # uncomment the following lines if you want to redirect the failed # calls to a different new destination ##if (t_check_status("486|408")) { ## sethostport("192.168.2.100:5060"); ## # do not set the missed call flag again ## t_relay(); ##} } opensips-2.2.2/test/5.sh000077500000000000000000000017451300170765700150540ustar00rootroot00000000000000#!/bin/bash # loads the opensips default config # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Needs a default opensips database setup for mysql CFG=5.cfg # start ../opensips -w . -f $CFG &> /dev/null ret=$? sleep 1 killall -9 opensips exit $ret opensips-2.2.2/test/6.sh000077500000000000000000000020061300170765700150440ustar00rootroot00000000000000#!/bin/bash # checks a configuration with 'opensips -c' and 'opensips -C' # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. CFG=2.cfg # start ../opensips -w . -c -f $CFG > /dev/null 2>&1 ret=$? if [ "$ret" -eq 0 ] ; then ../opensips -C -f $CFG > /dev/null 2>&1 ret=$? fi ; exit $ret opensips-2.2.2/test/7.cfg000066400000000000000000001006201300170765700151700ustar00rootroot00000000000000######################################################################## # This configuration is autogenerated by sip:wizard # (http://www.sipwise.com/wizard) on Fri Nov 09 17:06:52 +0100 2007 # for OpenSIPS 1.2 # # Copyright (C) 2007 Sipwise (support@sipwise.com) ######################################################################## ######################################################################## # By obtaining, using, and/or copying this configuration and/or its # associated documentation, you agree that you have read, understood, # and will comply with the Terms of Usage provided at # http://www.sipwise.com/news/?page_id=6 as well as the following # additions: # # Permission to use, copy, modify, and distribute this configuration and # its associated documentation for any purpose and without fee is hereby # granted, provided that the above copyright notice appears in all # copies, and that both that copyright notice and this permission notice # appear in supporting documentation, and that the name of Sipwise or # the author will not be used in advertising or publicity pertaining to # distribution of the configuration without specific, written prior # permission. ######################################################################## ######################################################################## # Before using this configuration, read the following prerequisites in # order to gain the designated functionallity: # # base: # You have to insert all locally served domains (i.e. # "opensipsctl domain add your.domain.com"). # # nat-mediaproxy: # You have to install mediaproxy # (http://mediaproxy.ag-projects.com/) for relaying RTP traffic. # # usr-preferences: # This feature relies on UUID-based provisioning. Thus, you have # to add the uuid-column to the subscriber table ("alter table # subscriber add column uuid varchar(64);") and populate it with a # UUID (unique user identifier) per user, for example an # auto-incremented id. # # offnet-incoming-sip: # You have to populate the "trusted"-table with rules for # allowed peering hosts (i.e. src_ip="1.2.3.4", proto="udp", # from_pattern="^sip:.*@domain.of.peering.host$", tag="1234"). If the # feature "usr-preferences" is selected, the tag-value is used as # caller-uuid for such calls. # # offnet-pstn: # You have to add a routing entry for lcr (i.e. "opensipsctl lcr # addroute '' '' 1 1"). Additionally, you have to add your gateways # (i.e. "opensipsctl lcr addgw my-test-gw 1.2.3.4 5060 sip udp 1"). # # ring-timeout: # You have to provision the ring-timeout (AVP ringtimeout as # type 1) for each user in the usr_preferences table (i.e. # uuid='1234', username='', domain='', attribute='ringtimeout', # type=1, value='60'). If no timeout is provisioned, the default # timeout will be used. # # cfu: # You have to provision the call-forward-unconditional as full # SIP URI (AVP cfu as type 0) for each user in the usr_preferences # table (i.e. uuid='1234', username='', domain='', attribute='cfu', # type=0, value='sip:foo@otherdomain.com'). Forwards to another user # in the same domain or to other domains are possible. # # cfc: # You have to provision the call-forward-conditional as full SIP # URI (AVP cfc as type 0) for each user in the usr_preferences table # (i.e. uuid='1234', username='', domain='', attribute='cfc', type=0, # value='sip:foo@otherdomain.com'). Forwards to another user in the # same domain or to other domains are possible. # # user-aliases: # You have to add aliases for your users (i.e. "opensipsctl alias # add 01234567 sip:bob@yourdomain.com" for usrloc-based aliases or # make entries into "dbaliases" table for db-based aliases) # # cli: # You have to provision the CLI as full SIP URI (AVP cli as type # 0) for each user in the usr_preferences table (i.e. uuid='1234', # username='', domain='', attribute='cli', type=0, # value='sip:01234567@yourdomain.com'). # # clir: # You have to provision '1' to enable CLIR and '0' to disable it # (AVP clir as type 1) for each user in the usr_preferences table # (i.e. uuid='1234', username='', domain='', attribute='clir', # type=1, value='1'). # # acc-db: # You have to add the columns "src_leg" and "dst_leg" to your # acc-table ("alter table acc add column src_leg varchar(128); alter # table acc add column dst_leg varchar(128);"). # ######################################################################## ######################################################################## # Configuration 'sip:wizard - Fri Nov 09 17:06:52 +0100 2007' ######################################################################## listen = udp:127.0.0.1:5060 mpath = "../modules" children = 8 log_level = 3 disable_tcp = no log_facility = LOG_DAEMON log_stderror = no tcp_children = 4 mhomed = no server_signature = yes sip_warning = no check_via = no dns = no rev_dns = no disable_core_dump = no dns_try_ipv6 = yes dns_use_search_list = yes loadmodule "usrloc/usrloc.so" modparam("usrloc", "user_column", "username") modparam("usrloc", "domain_column", "domain") modparam("usrloc", "contact_column", "contact") modparam("usrloc", "expires_column", "expires") modparam("usrloc", "q_column", "q") modparam("usrloc", "callid_column", "callid") modparam("usrloc", "cseq_column", "cseq") modparam("usrloc", "methods_column", "methods") modparam("usrloc", "flags_column", "flags") modparam("usrloc", "user_agent_column", "user_agent") modparam("usrloc", "received_column", "received") modparam("usrloc", "socket_column", "socket") modparam("usrloc", "use_domain", 0) modparam("usrloc", "desc_time_order", 0) modparam("usrloc", "timer_interval", 60) modparam("usrloc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("usrloc", "db_mode", 1) modparam("usrloc", "matching_mode", 0) modparam("usrloc", "cseq_delay", 20) modparam("usrloc", "nat_bflag", 6) loadmodule "maxfwd/maxfwd.so" modparam("maxfwd", "max_limit", 256) loadmodule "rr/rr.so" modparam("rr", "append_fromtag", 1) modparam("rr", "enable_double_rr", 1) modparam("rr", "add_username", 0) loadmodule "tm/tm.so" modparam("tm", "fr_timer", 30) modparam("tm", "fr_inv_timer", 120) modparam("tm", "wt_timer", 5) modparam("tm", "delete_timer", 2) modparam("tm", "ruri_matching", 1) modparam("tm", "via1_matching", 1) modparam("tm", "unix_tx_timeout", 2) modparam("tm", "restart_fr_on_each_reply", 1) modparam("tm", "pass_provisional_replies", 0) modparam("tm", "fr_inv_timer_avp", "$avp(callee_fr_inv_timer)") loadmodule "mi_fifo/mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("mi_fifo", "reply_dir", "/tmp/") modparam("mi_fifo", "reply_indent", "\t") loadmodule "domain/domain.so" modparam("domain", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("domain", "db_mode", 1) modparam("domain", "domain_table", "domain") modparam("domain", "domain_col", "domain") loadmodule "nathelper/nathelper.so" modparam("nathelper", "natping_interval", 0) modparam("nathelper", "ping_nated_only", 1) modparam("nathelper", "rtpproxy_sock", "unix:/var/run/rtpproxy.sock") modparam("nathelper", "rtpproxy_disable_tout", 60) modparam("nathelper", "rtpproxy_tout", 1) modparam("nathelper", "rtpproxy_retr", 5) modparam("nathelper", "sipping_method", "OPTIONS") modparam("nathelper", "received_avp", "$avp(received)") loadmodule "textops/textops.so" loadmodule "mediaproxy/mediaproxy.so" modparam("mediaproxy", "mediaproxy_socket", "/var/run/proxydispatcher.sock") loadmodule "uri/uri.so" loadmodule "registrar/registrar.so" modparam("registrar", "default_expires", 3600) modparam("registrar", "min_expires", 60) modparam("registrar", "max_expires", 0) modparam("registrar", "default_q", 0) modparam("registrar", "append_branches", 1) modparam("registrar", "case_sensitive", 0) modparam("registrar", "received_param", "received") modparam("registrar", "max_contacts", 0) modparam("registrar", "retry_after", 0) modparam("registrar", "method_filtering", 0) modparam("registrar", "path_mode", 2) modparam("registrar", "path_use_received", 0) modparam("registrar", "received_avp", "$avp(received)") loadmodule "sl/sl.so" modparam("sl", "enable_stats", 1) loadmodule "db_mysql/db_mysql.so" modparam("db_mysql", "ping_interval", 300) modparam("db_mysql", "auto_reconnect", 1) loadmodule "auth/auth.so" modparam("auth", "nonce_expire", 300) modparam("auth", "rpid_suffix", ";party=calling;id-type=subscriber;screen=yes") modparam("auth", "rpid_avp", "$avp(rpid)") loadmodule "auth_db/auth_db.so" modparam("auth_db", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("auth_db", "user_column", "username") modparam("auth_db", "domain_column", "domain") modparam("auth_db", "password_column", "password") modparam("auth_db", "password_column_2", "ha1b") modparam("auth_db", "calculate_ha1", 1) modparam("auth_db", "use_domain", 0) modparam("auth_db", "load_credentials", "$avp(caller_uuid)=uuid") loadmodule "uri_db/uri_db.so" modparam("uri_db", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("uri_db", "db_table", "subscriber") modparam("uri_db", "use_uri_table", 0) modparam("uri_db", "use_domain", 0) loadmodule "avpops/avpops.so" modparam("avpops", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("avpops", "avp_table", "usr_preferences") modparam("avpops", "use_domain", 0) modparam("avpops", "uuid_column", "uuid") modparam("avpops", "username_column", "username") modparam("avpops", "domain_column", "domain") modparam("avpops", "attribute_column", "attribute") modparam("avpops", "value_column", "value") modparam("avpops", "type_column", "type") loadmodule "enum/enum.so" modparam("enum", "domain_suffix", "e164.org.") loadmodule "permissions/permissions.so" modparam("permissions", "default_allow_file", "permissions.allow") modparam("permissions", "default_deny_file", "permissions.deny") modparam("permissions", "check_all_branches", 1) modparam("permissions", "allow_suffix", ".allow") modparam("permissions", "deny_suffix", ".deny") modparam("permissions", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("permissions", "db_mode", 1) modparam("permissions", "trusted_table", "trusted") modparam("permissions", "source_col", "src_ip") modparam("permissions", "proto_col", "proto") modparam("permissions", "from_col", "from_pattern") modparam("permissions", "tag_col", "tag") modparam("permissions", "peer_tag_avp", "$avp(peer_uuid)") loadmodule "lcr/lcr.so" modparam("lcr", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("lcr", "gw_table", "gw") modparam("lcr", "gw_name_column", "gw_name") modparam("lcr", "ip_addr_column", "ip_addr") modparam("lcr", "port_column", "port") modparam("lcr", "uri_scheme_column", "uri_scheme") modparam("lcr", "transport_column", "transport") modparam("lcr", "grp_id_column", "grp_id") modparam("lcr", "lcr_table", "lcr") modparam("lcr", "strip_column", "strip") modparam("lcr", "prefix_column", "prefix") modparam("lcr", "from_uri_column", "from_uri") modparam("lcr", "priority_column", "priority") modparam("lcr", "gw_uri_avp", "$avp(gw_uri)") modparam("lcr", "ruri_user_avp", "$avp(ruri_user)") modparam("lcr", "contact_avp", "$avp(contact)") modparam("lcr", "fr_inv_timer_avp", "$avp(fr_inv_timer_avp)") modparam("lcr", "fr_inv_timer", 90) modparam("lcr", "fr_inv_timer_next", 30) modparam("lcr", "rpid_avp", "$avp(rpid)") modparam("lcr", "flags_avp", "$avp(flags)") loadmodule "uac_redirect/uac_redirect.so" modparam("uac_redirect", "default_filter", "accept") modparam("uac_redirect", "acc_function", "acc_log_request") modparam("uac_redirect", "acc_db_table", "acc") loadmodule "alias_db/alias_db.so" modparam("alias_db", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("alias_db", "user_column", "username") modparam("alias_db", "domain_column", "domain") modparam("alias_db", "alias_user_column", "alias_username") modparam("alias_db", "alias_domain_column", "alias_domain") modparam("alias_db", "use_domain", 0) loadmodule "uac_auth/uac_auth.so" loadmodule "uac/uac.so" modparam("uac", "rr_store_param", "vsf") modparam("uac", "from_restore_mode", "auto") modparam("uac", "from_passwd", "s1p:Wiz4rd!") loadmodule "acc/acc.so" modparam("acc", "early_media", 0) modparam("acc", "failed_transaction_flag", 24) modparam("acc", "report_cancels", 0) modparam("acc", "log_flag", 0) modparam("acc", "log_missed_flag", 0) modparam("acc", "log_level", 2) modparam("acc", "db_flag", 25) modparam("acc", "db_missed_flag", 0) modparam("acc", "db_table_acc", "acc") modparam("acc", "db_table_missed_calls", "missed_calls") modparam("acc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("acc", "acc_method_column", "method") modparam("acc", "acc_callid_column", "callid") modparam("acc", "acc_time_column", "time") modparam("acc", "acc_from_tag_column", "from_tag") modparam("acc", "detect_direction", 1) modparam("acc", "acc_sip_code_column", "sip_code") modparam("acc", "acc_sip_reason_column", "sip_reason") modparam("acc", "multi_leg_info", "src_leg=$avp(src_leg);dst_leg=$avp(dst_leg)") ######################################################################## # Request route 'main' ######################################################################## route[0] { xlog("L_INFO", "New request - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); force_rport(); if(msg:len > max_len) { xlog("L_INFO", "Message too big - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_send_reply("513", "Message Too Big"); exit; } if (!mf_process_maxfwd_header("10")) { xlog("L_INFO", "Too many hops - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_send_reply("483", "Too Many Hops"); exit; } if(!is_method("REGISTER")) { if(nat_uac_test("19")) { record_route(";nat=yes"); } else { record_route(); } } if(is_method("CANCEL") || is_method("BYE")) { end_media_session(); } if(loose_route()) { if(!has_totag()) { xlog("L_INFO", "Initial loose-routing rejected - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_send_reply("403", "Initial Loose-Routing Rejected"); exit; } if(nat_uac_test("19") || search("^Route:.*;nat=yes")) { fix_nated_contact(); setbflag(6); } if(is_method("BYE")) { setflag(24); # account failed transactions setflag(25); # account successful transactions } # mark as loose-routed for acc setflag(26); route(12); } if(is_method("REGISTER")) { route(11); } setflag(24); # account failed transactions setflag(25); # account successful transactions if(is_method("INVITE")) { route(13); } if(is_method("CANCEL") || is_method("ACK")) { route(19); } route(20); } ######################################################################## # Request route 'clear-usr-preferences-caller' ######################################################################## route[1] { xlog("L_INFO", "Clear caller preferences - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); avp_delete("$avp(caller_cli)/g"); avp_delete("$avp(clir)/g"); } ######################################################################## # Request route 'clear-usr-preferences-callee' ######################################################################## route[2] { xlog("L_INFO", "Clear callee preferences - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); avp_delete("$avp(callee_fr_inv_timer)/g"); avp_delete("$avp(cfu)/g"); avp_delete("$avp(cfc)/g"); } ######################################################################## # Request route 'usr-preferences-caller' ######################################################################## route[3] { route(1); xlog("L_INFO", "Load caller preferences for uuid '$avp(caller_uuid)' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); # load caller avps avp_db_load("$avp(caller_uuid)", "*"); avp_copy("$avp(cli)", "$avp(caller_cli)/d"); if(is_avp_set("$avp(clir)/n") && avp_check("$avp(clir)", "eq/i:1")) { # mark for anonymization setflag(28); } } ######################################################################## # Request route 'usr-preferences-callee' ######################################################################## route[4] { xlog("L_INFO", "Load callee preferences for uuid '$avp(callee_uuid)' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); # load callee avps avp_db_load("$avp(callee_uuid)", "*"); if(is_avp_set("$avp(cfu)/s")) { xlog("L_INFO", "Call-forward-unconditional to '$avp(cfu)' found - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(8); avp_delete("$avp(caller_uuid)/g"); avp_copy("$avp(callee_uuid)", "$avp(caller_uuid)/d"); avp_pushto("$ru", "$avp(cfu)"); route(3); route(14); exit; } if(is_avp_set("$avp(ringtimeout)/n")) { xlog("L_INFO", "Setting ring timeout to $avp(ringtimeout) secs - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); avp_copy("$avp(ringtimeout)", "$avp(callee_fr_inv_timer)/d"); } } ######################################################################## # Request route 'acc-caller' ######################################################################## route[5] { xlog("L_INFO", "Setting acc source-leg for uuid '$avp(caller_uuid)' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); avp_printf("$avp(src_leg)", "$avp(caller_uuid)|$avp(acc_caller_user)|$avp(acc_caller_domain)|$avp(acc_state)"); } ######################################################################## # Request route 'acc-callee' ######################################################################## route[6] { xlog("L_INFO", "Setting acc destination-leg for uuid '$avp(callee_uuid)' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); avp_printf("$avp(dst_leg)", "$avp(callee_uuid)|$avp(acc_callee_user)|$avp(acc_callee_domain)"); } ######################################################################## # Request route 'acc-failure' ######################################################################## route[7] { xlog("L_INFO", "Accounting failed request for uuid '$avp(caller_uuid)' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(5); route(6); resetflag(24); acc_db_request("404", "acc"); } ######################################################################## # Request route 'cfu-acc' ######################################################################## route[8] { $avp(acc_callee_user) = $rU; $avp(acc_callee_domain) = $rd; route(5); route(6); avp_delete("$avp(acc_caller_user)"); avp_delete("$avp(acc_caller_domain)"); avp_copy("$avp(acc_callee_user)", "$avp(acc_caller_user)"); avp_copy("$avp(acc_callee_domain)", "$avp(acc_caller_domain)"); avp_delete("$avp(acc_state)/g"); $avp(acc_state) = "cfu"; } ######################################################################## # Request route 'clir' ######################################################################## route[9] { if(isflagset(28) && !isflagset(27)) { setflag(27); xlog("L_INFO", "Anonymize caller - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); uac_replace_from("Anonymous","sip:anonymous@anonymous.invalid"); if(is_present_hf("Privacy")) { remove_hf("Privacy"); } append_hf("Privacy: id\r\n"); } } ######################################################################## # Request route 'stop-media-proxy' ######################################################################## route[10] { if(isflagset(22)) { end_media_session(); } } ######################################################################## # Request route 'base-route-register' ######################################################################## route[11] { sl_send_reply("100", "Trying"); if(!www_authorize("", "subscriber")) { xlog("L_INFO", "Register authentication failed - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); www_challenge("", "0"); exit; } if(!db_check_to()) { xlog("L_INFO", "Spoofed To-URI detected - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_send_reply("403", "Spoofed To-URI Detected"); exit; } consume_credentials(); if(!search("^Contact:[ ]*\*") && nat_uac_test("19")) { fix_nated_register(); setbflag(6); } if(!save("location")) { xlog("L_ERR", "Saving contact failed - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_reply_error(); exit; } xlog("L_INFO", "Registration successful - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); exit; } ######################################################################## # Request route 'base-outbound' ######################################################################## route[12] { if(is_present_hf("P-Asserted-Identity")) { remove_hf("P-Asserted-Identity"); } if(is_present_hf("Remote-Party-ID")) { remove_hf("Remote-Party-ID"); } if(is_avp_set("$avp(caller_cli)/s")) { if(!isflagset(28)) { xlog("L_INFO", "Set caller CLI '$avp(caller_cli)' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); append_hf("P-Asserted-Identity: <$avp(caller_cli)>\r\n"); } } route(9); if(isbflagset(6)) { if(!isflagset(22) && !search("^Content-Length:[ ]*0")) { setflag(22); use_media_proxy(); } t_on_reply("2"); } else { t_on_reply("1"); } if(!isflagset(21)) { t_on_failure("2"); if(!isflagset(26)) { $avp(acc_callee_user) = $rU; $avp(acc_callee_domain) = $rd; route(5); route(6); } } if(is_present_hf("Proxy-Authorization")) { consume_credentials(); } xlog("L_INFO", "Request leaving server, D-URI='$du' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); # no 100 (we already sent it) and no DNS blacklisting if(!t_relay("0x05")) { sl_reply_error(); if(is_method("INVITE") && isbflagset(6)) { end_media_session(); } } exit; } ######################################################################## # Request route 'base-route-invite' ######################################################################## route[13] { sl_send_reply("100", "Trying"); if(from_gw()) { $avp(caller_uuid) = "0"; xlog("L_INFO", "Call from PSTN' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); setflag(23); } else { if(allow_trusted()) { if(is_avp_set("$avp(peer_uuid)/s")) { # use tag-column from trusted-table as uuid for this caller avp_copy("$avp(peer_uuid)", "$avp(caller_uuid)/d"); } else { # if no uuid is set, use "0" as default uuid $avp(caller_uuid) = "0"; } xlog("L_INFO", "Call from trusted peer with uuid '$avp(caller_uuid)' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); if(!is_domain_local("$rd")) { xlog("L_INFO", "Rejecting peering attempt with non-local request domain - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_send_reply("403", "Relaying Denied"); exit; } setflag(23); } else { if(!proxy_authorize("", "subscriber")) { xlog("L_INFO", "Proxy authentication failed - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); proxy_challenge("", "0"); exit; } if(!db_check_from()) { xlog("L_INFO", "Spoofed From-URI detected - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_send_reply("403", "Spoofed From-URI Detected"); exit; } } } $avp(acc_caller_user) = $fU; $avp(acc_caller_domain) = $fd; $avp(acc_state) = "call"; route(3); if(nat_uac_test("19")) { fix_nated_contact(); setbflag(6); } route(14); } ######################################################################## # Request route 'invite-find-callee' ######################################################################## route[14] { if(alias_db_lookup("dbaliases")) { xlog("L_INFO", "Callee was aliased - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); } route(2); if(!is_domain_local("$rd")) { setflag(20); $avp(callee_uuid) = "0"; route(16); } avp_delete("$avp(callee_uuid)"); avp_db_query("select uuid from subscriber where username = '$rU'", "$avp(callee_uuid)"); if(is_avp_set("$avp(callee_uuid)/s")) { xlog("L_INFO", "Callee is local, uuid='$avp(callee_uuid)' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(15); } else { $avp(callee_uuid) = "0"; xlog("L_INFO", "Callee is not local - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(16); } exit; } ######################################################################## # Request route 'invite-to-internal' ######################################################################## route[15] { route(4); if(!lookup("location")) { xlog("L_INFO", "Local user offline - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); $avp(acc_callee_user) = $rU; $avp(acc_callee_domain) = $rd; route(7); sl_send_reply("404", "User Offline"); } else { xlog("L_INFO", "Local user online - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(12); } exit; } ######################################################################## # Request route 'invite-to-external' ######################################################################## route[16] { if(isflagset(20)) { xlog("L_INFO", "Call to foreign domain - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(12); exit; } route(18); if(!isflagset(23)) { # don't allow calls relaying from PSTN to PSTN, if not explicitely forwarded if(uri =~ "^sip:[0-9]+@") { # only route numeric users to PSTN if(!load_gws()) { xlog("L_ERR", "Error loading PSTN gateways - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_send_reply("503", "PSTN Termination Currently Unavailable"); exit; } if(!next_gw()) { xlog("L_ERR", "No PSTN gateways available - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_send_reply("503", "PSTN Termination Currently Unavailable"); exit; } setflag(21); t_on_failure("1"); route(12); } } xlog("L_INFO", "Call to unknown user - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(7); sl_send_reply("404", "User Not Found"); exit; } ######################################################################## # Request route 'normalize-e164' ######################################################################## route[17] { # European numbering plans look like this: # CC = country code (i.e. 43 for Austria) # NDC = national destination code (i.e. 1 for Vienna) # SN = subscriber number (i.e. 4001234) # # CC + NDC + SN, i.e. 4314001234 # # Within the same CC+NDC, it can be omitted, so if # +4314001234 wants to call +4315002345, one can dial # just 5002345. # # Within the same CC, CC can be ommitted and a "0" is prefixed # to NDC, so if +4314001234 wants to call +4326003456, # one can dial 026003456. # # For international calls, either "00" or + is prefixed, like # +49123456780 or 0049123456789. # avp_delete("$avp(orig_callee_user)/g"); $avp(orig_callee_user) = $rU; if(uri =~ "^sip:(\+[1-9])?[0-9]+@") { # looks like a PSTN number if(uri =~ "^sip:0[1-9][0-9]+@") { # we have format 0+NDC+SN strip(1); prefix("+49"); } else if(uri =~ "^sip:00[1-9]+@") { # we have format 00 + CC + NDC + SN strip(2); prefix("+"); } else if(!uri =~ "^sip:\+[1-9][0-9]+@") { # unknown format, maybe NDC wasn't added before? xlog("L_INFO", "Not normalized callee '$avp(orig_callee_user)' to E.164 format - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); return(-1); } # else we have "+" + CC + NDC + SN xlog("L_INFO", "Normalized callee '$avp(orig_callee_user)' to E.164 format '$rU' - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); return(1); } else { xlog("L_INFO", "Not normalized callee '$avp(orig_callee_user)' to E.164 format - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); return(-1); } } ######################################################################## # Request route 'lookup-enum' ######################################################################## route[18] { route(17); if($rc == 1) { if(enum_query("e164.org.")) { # TODO: do GW fallback (load gws, set failure-route)? xlog("L_INFO", "ENUM query succeeded - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); setdsturi("sip:bogus.localhost:5060"); route(12); exit; } else { xlog("L_INFO", "ENUM query failed - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); # ENUM query failed, revert $rU avp_pushto("$ru/username", "$avp(orig_callee_user)"); } } } ######################################################################## # Request route 'base-route-local' ######################################################################## route[19] { t_on_reply("1"); if(t_check_trans()) { xlog("L_INFO", "Request leaving server - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); if(!t_relay()) { sl_reply_error(); } } else { xlog("L_INFO", "Dropping mis-routed request - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); } exit; } ######################################################################## # Request route 'base-route-generic' ######################################################################## route[20] { xlog("L_INFO", "Method not supported - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); sl_send_reply("501", "Method Not Supported Here"); exit; } ######################################################################## # Request route 'base-filter-failover' ######################################################################## route[21] { if(!t_check_status("408|500|503")) { xlog("L_INFO", "No failover routing needed for this response code - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(10); exit; } } ######################################################################## # Reply route 'base-standard-reply' ######################################################################## onreply_route[1] { xlog("L_INFO", "Reply - S=$rs D=$rr F=$fu T=$tu IP=$si ID=$ci\n"); exit; } ######################################################################## # Reply route 'base-nat-reply' ######################################################################## onreply_route[2] { xlog("L_INFO", "NAT-Reply - S=$rs D=$rr F=$fu T=$tu IP=$si ID=$ci\n"); if(nat_uac_test("1")) { fix_nated_contact(); } if(isbflagset(6) && status=~"(180)|(183)|2[0-9][0-9]") { if(!search("^Content-Length:[ ]*0")) { use_media_proxy(); } } exit; } ######################################################################## # Failure route 'pstn-failover' ######################################################################## failure_route[1] { xlog("L_INFO", "Failure route for PSTN entered - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(21); if(!next_gw()) { xlog("L_ERR", "Failed to select next PSTN gateway - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(10); exit; } t_on_failure("1"); route(12); } ######################################################################## # Failure route 'base-standard-failure' ######################################################################## failure_route[2] { if(t_check_status("422|481|487")) { xlog("L_INFO", "Final reply - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(10); exit; } if(t_check_status("301|302")) { avp_delete("$avp(acc_caller_user)/g"); avp_delete("$avp(acc_caller_domain)/g"); avp_delete("$avp(acc_state)/g"); avp_copy("$avp(acc_callee_user)", "$avp(acc_caller_user)"); avp_copy("$avp(acc_callee_domain)", "$avp(acc_caller_domain)"); $avp(acc_state) = "cfc"; setflag(29); if(!get_redirects("1:1")) { xlog("L_ERROR", "Failed to fetch contact '$ct' from 301/302 - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); acc_db_request("480", "acc"); route(10); t_reply("480", "Temporarily Unavailable"); exit; } # get last URI from destination-set and set it as R-URI avp_delete("$avp(tmp)/g"); $avp(tmp) = $ds; avp_subst("$avp(tmp)", "/.*(sip:.+@[^:;>]+).*$/\1/"); avp_pushto("$ru", "$avp(tmp)"); setflag(29); t_on_branch("1"); xlog("L_INFO", "Redirect from UAC intercepted - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(14); exit; } if($avp(cfc) != NULL) { avp_delete("$avp(acc_caller_user)/g"); avp_delete("$avp(acc_caller_domain)/g"); avp_delete("$avp(acc_state)/g"); avp_copy("$avp(acc_callee_user)", "$avp(acc_caller_user)"); avp_copy("$avp(acc_callee_domain)", "$avp(acc_caller_domain)"); $avp(acc_state) = "cfc"; avp_pushto("$ru", "$avp(cfc)"); setflag(29); t_on_branch("1"); xlog("L_INFO", "CFC detected - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); route(14); } route(21); route(10); } ######################################################################## # Branch route 'cfc-drop-local' ######################################################################## branch_route[1] { if(is_domain_local("$rd")) { xlog("L_INFO", "Dropping local branch - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n"); drop(); } } opensips-2.2.2/test/7.sh000077500000000000000000000020621300170765700150470ustar00rootroot00000000000000#!/bin/bash # loads a SIP proxy/registrar config with offnet-termination and accounting # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # config generated by sipwise wizard # Needs a default opensips database setup for mysql CFG=7.cfg # start ../opensips -w . -f $CFG > /dev/null ret=$? sleep 1 killall -9 opensips exit $ret opensips-2.2.2/test/8.sh000077500000000000000000000030011300170765700150420ustar00rootroot00000000000000#!/bin/bash # creates a dbtext database with opensipsdbctl and deletes it again # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. tmp_name=""$RANDOM"_opensipsdb_tmp" cd ../scripts # setup config file cp opensipsctlrc opensipsctlrc.bak sed -i "s/# DBENGINE=MYSQL/DBENGINE=DBTEXT/g" opensipsctlrc sed -i "s/# INSTALL_EXTRA_TABLES=ask/INSTALL_EXTRA_TABLES=yes/g" opensipsctlrc sed -i "s/# INSTALL_PRESENCE_TABLES=ask/INSTALL_PRESENCE_TABLES=yes/g" opensipsctlrc cp opensipsdbctl opensipsdbctl.bak sed -i "s/TEST=\"false\"/TEST=\"true\"/g" opensipsdbctl ./opensipsdbctl create $tmp_name > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then ./opensipsdbctl drop $tmp_name > /dev/null ret=$? fi ; # cleanup mv opensipsctlrc.bak opensipsctlrc mv opensipsdbctl.bak opensipsdbctl cd ../test exit $ret opensips-2.2.2/test/9.sh000077500000000000000000000024271300170765700150560ustar00rootroot00000000000000#!/bin/bash # test basic db related opensipsctl functionality for mysql # Copyright (C) 2007 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. cd ../scripts # setup config file cp opensipsctlrc opensipsctlrc.bak cp opensipsctl opensipsctl.bak sed -i "s/# DBENGINE=MYSQL/DBENGINE=MYSQL/g" opensipsctlrc sed -i "s/TEST=\"false\"/TEST=\"true\"/g" opensipsctl ./opensipsctl avp list > /dev/null ret=$? if [ "$ret" -eq 0 ] ; then ./opensipsctl domain showdb > /dev/null ret=$? fi ; # cleanup mv opensipsctlrc.bak opensipsctlrc mv opensipsctl.bak opensipsctl cd ../test exit $ret opensips-2.2.2/test/Makefile000066400000000000000000000005061300170765700160030ustar00rootroot00000000000000all: -@for FILE in $(wildcard *.sh) ; do \ if [ -f $$FILE ] ; then \ if [ -x $$FILE ] ; then \ echo "run test `basename $$FILE .sh`:" `head "$$FILE" -n 2 | tail -n 1 | cut -c 3-` ; \ ./$$FILE ; \ ret=$$? ; \ if [ ! "$$ret" -eq 0 ] ; then \ echo "failed" ; \ fi ; \ fi ; \ fi ; \ done ; opensips-2.2.2/test/README000066400000000000000000000014031300170765700152200ustar00rootroot00000000000000This directory contains some small "smoke tests", that should assure that basic functionality of the server work as required. This tests should consists of one shell script and a config file if needed. They should not need much time for execution, to allow the run of the complete test suite in a few seconds. All test scripts must be self-contained, should not have external dependencies and must clean up after they are run. The second line in each scripts should contain a small comment that describe the task of the test, it should not output any messages on successful runs. This tests should (in the current state) only run by developers who know what they do. They could delete your database, produce core dumps that fill your harddisk or do other nasty things.. opensips-2.2.2/test/carrierroute-2.cfg000066400000000000000000000006071300170765700176730ustar00rootroot00000000000000domain proxy { prefix 49721123456787 { max_targets = 2 target 127.0.0.1:7000 { prob = 0.5 status = 1 } target 127.0.0.1:8000 { prob = 0.5 status = 1 } } prefix 49721123456786 { max_targets = 1 target 127.0.0.1:9000 { prob = 1.0 status = 1 } } prefix 49721123456785 { max_targets = 1 target 127.0.0.1:10000 { prob = 1.0 status = 1 } } } opensips-2.2.2/test/carrierroute.cfg000066400000000000000000000025631300170765700175370ustar00rootroot00000000000000domain register { prefix NULL { max_targets = 3 target test1 { prob = 0.400000 hash_index = 3 status = 0 } target test3.localdomain { prob = 0.500000 hash_index = 2 status = 1 } target test2.localdomain { prob = 0.500000 hash_index = 1 status = 1 } } } domain proxy { prefix 2 { max_targets = 2 target test8.localdomain { prob = 0.100000 hash_index = 2 status = 1 } target test7.localdomain { prob = 0.700000 hash_index = 1 status = 1 } } prefix 42 { max_targets = 3 target test6.localdomain { prob = 0.100000 hash_index = 2 status = 1 } target test5.localdomain { prob = 0.200000 hash_index = 1 status = 1 } target test4 { prob = 0.700000 hash_index = 3 status = 1 } } prefix 49 { max_targets = 3 target test6.localdomain { prob = 0.500000 hash_index = 3 status = 1 } target test5.localdomain { prob = 0.500000 hash_index = 1 status = 0 } target test4 { prob = 0.400000 hash_index = 2 status = 1 } } } domain other { prefix NULL { max_targets = 3 target test1 { prob = 0.400000 hash_index = 3 status = 0 } target test3.localdomain { prob = 0.500000 hash_index = 2 status = 1 backed_up = {1} } target test2.localdomain { prob = 0.500000 hash_index = 1 status = 0 backup = 2 } } } opensips-2.2.2/test/cpl_ignore.xml000066400000000000000000000001711300170765700172040ustar00rootroot00000000000000 opensips-2.2.2/test/cpl_test.xml000066400000000000000000000011151300170765700166770ustar00rootroot00000000000000 ;tag=[call_number] To: Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:alice@[local_ip]:[local_port] Max-Forwards: 5 Expires: 1800 User-Agent: SIPp/Linux Content-Length: 0 ]]> opensips-2.2.2/test/failure_route.xml000066400000000000000000000056421300170765700177400ustar00rootroot00000000000000 Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> opensips-2.2.2/test/include/000077500000000000000000000000001300170765700157655ustar00rootroot00000000000000opensips-2.2.2/test/include/common000066400000000000000000000002341300170765700171770ustar00rootroot00000000000000DB_ALL_MOD="acc|alias_db|auth_db|dialog|dialplan|dispatcher|domain|domainpolicy|group|imc|msilo|siptrace|speeddial|uri_db|usrloc|permissions|userblacklist" opensips-2.2.2/test/include/require000066400000000000000000000026001300170765700173620ustar00rootroot00000000000000# Copyright (C) 2008 1&1 Internet AG # # This file is part of opensips, a free SIP server. # # opensips is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # opensips is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. function check_opensips() { if ! (test -e ../opensips) ; then echo "opensips not found, not run" return -1 fi; return 0 } function check_module() { if [ $# -ne 1 ]; then echo "wrong number of params in check_module()" return -1 fi if ! (test -e ../modules/$1/$1.so) ; then echo "modules/$1/$1.so not found, not run" return -1 fi; return 0 } function check_netcat() { if ! ( which nc > /dev/null ); then echo "netcat not found, not run" return -1 fi; return 0 } function check_sipp() { if ! ( which sipp > /dev/null ); then echo "sipp not found, not run" return -1 fi; return 0 } opensips-2.2.2/test/inv_auth.xml000066400000000000000000000021751300170765700167060ustar00rootroot00000000000000 ;tag=[call_number] To: Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:alice@[local_ip]:[local_port] Max-Forwards: 5 Expires: 1800 User-Agent: SIPp/Linux Content-Length: 0 ]]> ;tag=[call_number] To: Call-ID: [call_id] CSeq: 2 INVITE Contact: sip:alice@[local_ip]:[local_port] [authentication username=alice password=alice realm=localhost]; Max-Forwards: 5 Expires: 1800 User-Agent: SIPp/Linux Content-Length: 0 ]]> opensips-2.2.2/test/presence.cfg000066400000000000000000000035701300170765700166340ustar00rootroot00000000000000# # simple quick-start config script - Stand-alone presence server # # ----------- global configuration parameters ------------------------ log_level=3 # debug level (cmd line: -dddddddddd) log_stderror=no # (cmd line: -E) children=4 listen=udp:127.0.0.1:5059 dns=no rev_dns=no # ------------------ module loading ---------------------------------- #set module path mpath="../modules/" loadmodule "db_mysql/db_mysql.so" loadmodule "sl/sl.so" loadmodule "maxfwd/maxfwd.so" loadmodule "textops/textops.so" loadmodule "tm/tm.so" loadmodule "rr/rr.so" loadmodule "presence/presence.so" loadmodule "presence_xml/presence_xml.so" loadmodule "avpops/avpops.so" loadmodule "mi_fifo/mi_fifo.so" # ----------------- setting module-specific parameters --------------- # -- presence params -- modparam("presence|presence_xml", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("presence_xml", "force_active", 1) modparam("presence", "server_address", "sip:10.10.10.10:5060") modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo_presence") # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; if (!is_method("SUBSCRIBE|PUBLISH")) { sl_send_reply("488", "Not Acceptable Here"); exit; } # presence handling if (! t_newtran()) { sl_reply_error(); exit; }; if(is_method("PUBLISH")) { handle_publish(); t_release(); } else if( is_method("SUBSCRIBE")) { handle_subscribe(); t_release(); }; exit; } opensips-2.2.2/test/publish.csv000066400000000000000000000000661300170765700165270ustar00rootroot00000000000000SEQUENTIAL user1;127.0.0.1:5059 user2;127.0.0.1:5059 opensips-2.2.2/test/publish_scenario.xml000066400000000000000000000056131300170765700204220ustar00rootroot00000000000000 From: ;tag=[call_number] Call-ID: [call_id] CSeq: 1 PUBLISH Expires: 60 Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO Content-Type: application/pidf+xml User-Agent: sipp Event: presence Content-Length: [len] open ]]> From: "[field0]";tag=[call_number] Call-ID: [$4]///[call_id] CSeq: 1 PUBLISH Expires: 60 Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO Content-Type: application/pidf+xml User-Agent: sipp Event: presence SIP-If-Match:[$3] Content-Length: [len] openBusy ]]> opensips-2.2.2/test/reg_auth.xml000066400000000000000000000021721300170765700166640ustar00rootroot00000000000000 ;tag=[call_number] To: Call-ID: [call_id] CSeq: 1 REGISTER Contact: sip:alice@[local_ip]:[local_port] Max-Forwards: 5 Expires: 1800 User-Agent: SIPp/Linux Content-Length: 0 ]]> ;tag=[call_number] To: Call-ID: [call_id] CSeq: 2 REGISTER Contact: sip:alice@[local_ip]:[local_port] [authentication username=alice password=alice realm=localhost]; Max-Forwards: 5 Expires: 1800 User-Agent: SIPp/Linux Content-Length: 0 ]]> opensips-2.2.2/test/register.sip000066400000000000000000000006161300170765700167060ustar00rootroot00000000000000REGISTER sip:127.0.0.1 SIP/2.0 Via: SIP/2.0/UDP 172.17.13.240:5061;rport;branch=z9hG4bKydcnjlpe Max-Forwards: 70 To: From: ;tag=dyggg Call-ID: ccgdnpeqtepegxu@172.17.13.240 CSeq: 479 REGISTER Contact: ;expires=3600 Allow: INVITE,ACK,BYE,CANCEL,OPTIONS,PRACK,REFER,NOTIFY,SUBSCRIBE,INFO User-Agent: Twinkle/1.0 Content-Length: 0 opensips-2.2.2/test/subscribe_notify.csv000066400000000000000000000001341300170765700204260ustar00rootroot00000000000000SEQUENTIAL user1;127.0.0.1:5059;user2 user2;127.0.0.1:5059;user1 user2;127.0.0.1:5059;user3 opensips-2.2.2/test/subscribe_notify_scenario.xml000066400000000000000000000025561300170765700223300ustar00rootroot00000000000000 From: "[field0]";tag=[call_number] Call-ID: [call_id] CSeq: 1 SUBSCRIBE Expires: 3600 Accept: multipart/related, application/rlmi+xml, application/pidf+xml Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO User-Agent: sipp Event: presence Content-Length: 0 ]]> opensips-2.2.2/test/unregister.sip000066400000000000000000000006131300170765700172460ustar00rootroot00000000000000REGISTER sip:127.0.0.1 SIP/2.0 Via: SIP/2.0/UDP 172.17.13.240:5061;rport;branch=z9hG4bKruthdewh Max-Forwards: 70 To: From: ;tag=jwnqc Call-ID: ccgdnpeqtepegxu@172.17.13.240 CSeq: 481 REGISTER Contact: ;expires=0 Allow: INVITE,ACK,BYE,CANCEL,OPTIONS,PRACK,REFER,NOTIFY,SUBSCRIBE,INFO User-Agent: Twinkle/1.0 Content-Length: 0 opensips-2.2.2/time_rec.c000066400000000000000000000551371300170765700153310ustar00rootroot00000000000000#include #include #include #include "mem/mem.h" #include "mem/shm_mem.h" #include "time_rec.h" #ifndef USE_YWEEK_U #ifndef USE_YWEEK_V #ifndef USE_YWEEK_W #define USE_YWEEK_W /* Monday system */ #endif #endif #endif #ifdef USE_YWEEK_U #define SUN_WEEK(t) (int)(((t)->tm_yday + 7 - \ ((t)->tm_wday)) / 7) #else #define MON_WEEK(t) (int)(((t)->tm_yday + 7 - \ ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7) #endif #define ac_get_wday_yr(t) (int)((t)->tm_yday/7) #define ac_get_wday_mr(t) (int)(((t)->tm_mday-1)/7) #define REC_ERR -1 #define REC_MATCH 0 #define REC_NOMATCH 1 #define _IS_SET(x) (((x)>0)?1:0) #define _D(c) ((c) -'0') static inline int strz2int(char *_bp) { int _v; char *_p; if(!_bp) return 0; _v = 0; _p = _bp; while(*_p && *_p>='0' && *_p<='9') { _v += *_p - '0'; _p++; } return _v; } static inline char* trim(char* _s) { int len; char* end; /* Null pointer, there is nothing to do */ if (!_s) return _s; /* Remove spaces and tabs from the beginning of string */ while ((*_s == ' ') || (*_s == '\t')) _s++; len = strlen(_s); end = _s + len - 1; /* Remove trailing spaces and tabs */ while ((*end == ' ') || (*end == '\t')) end--; if (end != (_s + len - 1)) { *(end+1) = '\0'; } return _s; } int ac_tm_fill(ac_tm_p _atp, struct tm* _tm) { if(!_atp || !_tm) return -1; _atp->t.tm_sec = _tm->tm_sec; /* seconds */ _atp->t.tm_min = _tm->tm_min; /* minutes */ _atp->t.tm_hour = _tm->tm_hour; /* hours */ _atp->t.tm_mday = _tm->tm_mday; /* day of the month */ _atp->t.tm_mon = _tm->tm_mon; /* month */ _atp->t.tm_year = _tm->tm_year; /* year */ _atp->t.tm_wday = _tm->tm_wday; /* day of the week */ _atp->t.tm_yday = _tm->tm_yday; /* day in the year */ _atp->t.tm_isdst = _tm->tm_isdst; /* daylight saving time */ _atp->mweek = ac_get_mweek(_tm); _atp->yweek = ac_get_yweek(_tm); _atp->ywday = ac_get_wday_yr(_tm); _atp->mwday = ac_get_wday_mr(_tm); return 0; } int ac_tm_set_time(ac_tm_p _atp, time_t _t) { if(!_atp) return -1; memset( _atp, 0, sizeof(ac_tm_t)); _atp->time = _t; return ac_tm_fill(_atp, localtime(&_t)); } int ac_get_mweek(struct tm* _tm) { if(!_tm) return -1; #ifdef USE_YWEEK_U return ((_tm->tm_mday-1)/7 + (7-_tm->tm_wday+(_tm->tm_mday-1)%7)/7); #else return ((_tm->tm_mday-1)/7 + (7-(6+_tm->tm_wday)%7+(_tm->tm_mday-1)%7)/7); #endif } int ac_get_yweek(struct tm* _tm) { int week = -1; #ifdef USE_YWEEK_V int days; #endif if(!_tm) return -1; #ifdef USE_YWEEK_U week = SUN_WEEK(_tm); #else week = MON_WEEK(_tm); #endif #ifdef USE_YWEEK_V days = ((_tm->tm_yday + 7 - (_tm->tm_wday ? _tm->tm_wday-1 : 6)) % 7); if(days >= 4) week++; else if(week == 0) week = 53; #endif return week; } int ac_get_wkst(void) { #ifdef USE_YWEEK_U return 0; #else return 1; #endif } int ac_tm_reset(ac_tm_p _atp) { if(!_atp) return -1; memset(_atp, 0, sizeof(ac_tm_t)); return 0; } static ac_maxval_p ac_get_maxval(ac_tm_p _atp) { static ac_maxval_t _amp; struct tm _tm; int _v; /* the number of the days in the year */ _amp.yday = 365 + is_leap_year(_atp->t.tm_year+1900); /* the number of the days in the month */ switch(_atp->t.tm_mon) { case 1: if(_amp.yday == 366) _amp.mday = 29; else _amp.mday = 28; break; case 3: case 5: case 8: case 10: _amp.mday = 30; break; default: _amp.mday = 31; } /* maximum occurrences of a week day in the year */ memset(&_tm, 0, sizeof(struct tm)); _tm.tm_year = _atp->t.tm_year; _tm.tm_mon = 11; _tm.tm_mday = 31; mktime(&_tm); _v = 0; if(_atp->t.tm_wday > _tm.tm_wday) _v = _atp->t.tm_wday - _tm.tm_wday + 1; else _v = _tm.tm_wday - _atp->t.tm_wday; _amp.ywday = (int)((_tm.tm_yday-_v)/7) + 1; /* maximum number of weeks in the year */ _amp.yweek = ac_get_yweek(&_tm) + 1; /* maximum number of the week day in the month */ _amp.mwday=(int)((_amp.mday-1-(_amp.mday-_atp->t.tm_mday)%7)/7)+1; /* maximum number of weeks in the month */ _v = (_atp->t.tm_wday + (_amp.mday - _atp->t.tm_mday)%7)%7; #ifdef USE_YWEEK_U _amp.mweek = (int)((_amp.mday-1)/7+(7-_v+(_amp.mday-1)%7)/7)+1; #else _amp.mweek = (int)((_amp.mday-1)/7+(7-(6+_v)%7+(_amp.mday-1)%7)/7)+1; #endif return &_amp; } int ac_print(ac_tm_p _atp) { static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; if(!_atp) { printf("\n(null)\n"); return -1; } printf("\nSys time: %d\nTime: %02d:%02d:%02d\n", (int)_atp->time, _atp->t.tm_hour, _atp->t.tm_min, _atp->t.tm_sec); printf("Date: %s, %04d-%02d-%02d\n", _wdays[_atp->t.tm_wday], _atp->t.tm_year+1900, _atp->t.tm_mon+1, _atp->t.tm_mday); printf("Year day: %d\nYear week-day: %d\nYear week: %d\n", _atp->t.tm_yday, _atp->ywday, _atp->yweek); printf("Month week: %d\nMonth week-day: %d\n", _atp->mweek, _atp->mwday); return 0; } tr_byxxx_p tr_byxxx_new(char alloc) { tr_byxxx_p _bxp = NULL; if (alloc & PKG_ALLOC) _bxp = (tr_byxxx_p)pkg_malloc(sizeof(tr_byxxx_t)); else _bxp = (tr_byxxx_p)shm_malloc(sizeof(tr_byxxx_t)); if(!_bxp) return NULL; memset(_bxp, 0, sizeof(tr_byxxx_t)); _bxp->flags = alloc; return _bxp; } int tr_byxxx_init(tr_byxxx_p _bxp, int _nr) { if(!_bxp) return -1; _bxp->nr = _nr; if (_bxp->flags & PKG_ALLOC) _bxp->xxx = (int*)pkg_malloc(_nr*sizeof(int)); else _bxp->xxx = (int*)shm_malloc(_nr*sizeof(int)); if(!_bxp->xxx) return -1; if (_bxp->flags & PKG_ALLOC) _bxp->req = (int*)pkg_malloc(_nr*sizeof(int)); else _bxp->req = (int*)shm_malloc(_nr*sizeof(int)); if(!_bxp->req) { if (_bxp->flags & PKG_ALLOC) pkg_free(_bxp->xxx); else shm_free(_bxp->xxx); return -1; } memset(_bxp->xxx, 0, _nr*sizeof(int)); memset(_bxp->req, 0, _nr*sizeof(int)); return 0; } int tr_byxxx_free(tr_byxxx_p _bxp) { char type; if(!_bxp) return -1; type = _bxp->flags & PKG_ALLOC; if(_bxp->xxx) { if (type) pkg_free(_bxp->xxx); else shm_free(_bxp->xxx); } if(_bxp->req) { if (type) pkg_free(_bxp->req); else shm_free(_bxp->req); } if (type) pkg_free(_bxp); else shm_free(_bxp); return 0; } tmrec_p tmrec_new(char alloc) { tmrec_p _trp = NULL; if (alloc & PKG_ALLOC) _trp = (tmrec_p)pkg_malloc(sizeof(tmrec_t)); else _trp = (tmrec_p)shm_malloc(sizeof(tmrec_t)); if(!_trp) return NULL; memset(_trp, 0, sizeof(tmrec_t)); _trp->flags = alloc; /* localtime_r(&_trp->dtstart,&(_trp->ts)); */ return _trp; } int tmrec_free(tmrec_p _trp) { if(!_trp) return -1; tr_byxxx_free(_trp->byday); tr_byxxx_free(_trp->bymday); tr_byxxx_free(_trp->byyday); tr_byxxx_free(_trp->bymonth); tr_byxxx_free(_trp->byweekno); if (_trp->flags & PKG_ALLOC) pkg_free(_trp); else shm_free(_trp); return 0; } int tr_parse_dtstart(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->dtstart = ic_parse_datetime(_in, &(_trp->ts)); return (_trp->dtstart==0)?-1:0; } int tr_parse_dtend(tmrec_p _trp, char *_in) { struct tm _tm; if(!_trp || !_in) return -1; _trp->dtend = ic_parse_datetime(_in,&_tm); return (_trp->dtend==0)?-1:0; } int tr_parse_duration(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->duration = ic_parse_duration(_in); return 0; } int tr_parse_until(tmrec_p _trp, char *_in) { struct tm _tm; if(!_trp || !_in) return -1; _trp->until = ic_parse_datetime(_in, &_tm); return 0; } int tr_parse_freq(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; if(strlen(_in)<5) { _trp->freq = FREQ_NOFREQ; return 0; } if(!strcasecmp(_in, "daily")) { _trp->freq = FREQ_DAILY; return 0; } if(!strcasecmp(_in, "weekly")) { _trp->freq = FREQ_WEEKLY; return 0; } if(!strcasecmp(_in, "monthly")) { _trp->freq = FREQ_MONTHLY; return 0; } if(!strcasecmp(_in, "yearly")) { _trp->freq = FREQ_YEARLY; return 0; } _trp->freq = FREQ_NOFREQ; return 0; } int tr_parse_interval(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->interval = strz2int(_in); return 0; } int tr_parse_byday(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->byday = ic_parse_byday(_in, _trp->flags); return 0; } int tr_parse_bymday(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->bymday = ic_parse_byxxx(_in, _trp->flags); return 0; } int tr_parse_byyday(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->byyday = ic_parse_byxxx(_in, _trp->flags); return 0; } int tr_parse_bymonth(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->bymonth = ic_parse_byxxx(_in, _trp->flags); return 0; } int tr_parse_byweekno(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->byweekno = ic_parse_byxxx(_in, _trp->flags); return 0; } int tr_parse_wkst(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->wkst = ic_parse_wkst(_in); return 0; } int tr_print(tmrec_p _trp) { static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; int i; if(!_trp) { printf("\n(null)\n"); return -1; } printf("Recurrence definition\n-- start time ---\n"); printf("Sys time: %d\n", (int)_trp->dtstart); printf("Time: %02d:%02d:%02d\n", _trp->ts.tm_hour, _trp->ts.tm_min, _trp->ts.tm_sec); printf("Date: %s, %04d-%02d-%02d\n", _wdays[_trp->ts.tm_wday], _trp->ts.tm_year+1900, _trp->ts.tm_mon+1, _trp->ts.tm_mday); printf("---\n"); printf("End time: %d\n", (int)_trp->dtend); printf("Duration: %d\n", (int)_trp->duration); printf("Until: %d\n", (int)_trp->until); printf("Freq: %d\n", (int)_trp->freq); printf("Interval: %d\n", (int)_trp->interval); if(_trp->byday) { printf("Byday: "); for(i=0; i<_trp->byday->nr; i++) printf(" %d%s", _trp->byday->req[i], _wdays[_trp->byday->xxx[i]]); printf("\n"); } if(_trp->bymday) { printf("Bymday: %d:", _trp->bymday->nr); for(i=0; i<_trp->bymday->nr; i++) printf(" %d", _trp->bymday->xxx[i]*_trp->bymday->req[i]); printf("\n"); } if(_trp->byyday) { printf("Byyday:"); for(i=0; i<_trp->byyday->nr; i++) printf(" %d", _trp->byyday->xxx[i]*_trp->byyday->req[i]); printf("\n"); } if(_trp->bymonth) { printf("Bymonth: %d:", _trp->bymonth->nr); for(i=0; i< _trp->bymonth->nr; i++) printf(" %d", _trp->bymonth->xxx[i]*_trp->bymonth->req[i]); printf("\n"); } if(_trp->byweekno) { printf("Byweekno: "); for(i=0; i<_trp->byweekno->nr; i++) printf(" %d", _trp->byweekno->xxx[i]*_trp->byweekno->req[i]); printf("\n"); } printf("Weekstart: %d\n", _trp->wkst); return 0; } time_t ic_parse_datetime(char *_in, struct tm *_tm) { if(!_in || !_tm || strlen(_in)!=15) return 0; memset(_tm, 0, sizeof(struct tm)); _tm->tm_year = _D(_in[0])*1000 + _D(_in[1])*100 + _D(_in[2])*10 + _D(_in[3]) - 1900; _tm->tm_mon = _D(_in[4])*10 + _D(_in[5]) - 1; _tm->tm_mday = _D(_in[6])*10 + _D(_in[7]); _tm->tm_hour = _D(_in[9])*10 + _D(_in[10]); _tm->tm_min = _D(_in[11])*10 + _D(_in[12]); _tm->tm_sec = _D(_in[13])*10 + _D(_in[14]); _tm->tm_isdst = -1 /*daylight*/; return mktime(_tm); } time_t ic_parse_duration(char *_in) { time_t _t, _ft; char *_p; int _fl; if(!_in || strlen(_in)<2) return 0; if(*_in == 'P' || *_in=='p') { _p = _in+1; _fl = 1; } else { _p = _in; _fl = 0; } _t = _ft = 0; while(*_p) { switch(*_p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': _t = _t*10 + *_p - '0'; break; case 'w': case 'W': if(!_fl) { LM_ERR("week duration not allowed" " here (%d) [%s]\n", (int)(_p-_in), _in); return 0; } _ft += _t*7*24*3600; _t = 0; break; case 'd': case 'D': if(!_fl) { LM_ERR("day duration not allowed" " here (%d) [%s]\n", (int)(_p-_in), _in); return 0; } _ft += _t*24*3600; _t = 0; break; case 'h': case 'H': if(_fl) { LM_ERR("hour duration not allowed" " here (%d) [%s]\n", (int)(_p-_in), _in); return 0; } _ft += _t*3600; _t = 0; break; case 'm': case 'M': if(_fl) { LM_ERR("minute duration not allowed" " here (%d) [%s]\n", (int)(_p-_in), _in); return 0; } _ft += _t*60; _t = 0; break; case 's': case 'S': if(_fl) { LM_ERR("second duration not allowed" " here (%d) [%s]\n", (int)(_p-_in), _in); return 0; } _ft += _t; _t = 0; break; case 't': case 'T': if(!_fl) { LM_ERR("'T' not allowed" " here (%d) [%s]\n", (int)(_p-_in), _in); return 0; } _fl = 0; break; default: LM_ERR("bad character here (%d) [%s]\n", (int)(_p-_in), _in); return 0; } _p++; } return _ft; } tr_byxxx_p ic_parse_byday(char *_in, char type) { tr_byxxx_p _bxp = NULL; int _nr, _s, _v; char *_p; if(!_in) return NULL; _bxp = tr_byxxx_new(type); if(!_bxp) return NULL; _p = _in; _nr = 1; while(*_p) { if(*_p == ',') _nr++; _p++; } if(tr_byxxx_init(_bxp, _nr) < 0) { tr_byxxx_free(_bxp); return NULL; } _p = _in; _nr = _v = 0; _s = 1; while(*_p && _nr < _bxp->nr) { switch(*_p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': _v = _v*10 + *_p - '0'; break; case 's': case 'S': _p++; switch(*_p) { case 'a': case 'A': _bxp->xxx[_nr] = WDAY_SA; _bxp->req[_nr] = _s*_v; break; case 'u': case 'U': _bxp->xxx[_nr] = WDAY_SU; _bxp->req[_nr] = _s*_v; break; default: goto error; } _s = 1; _v = 0; break; case 'm': case 'M': _p++; if(*_p!='o' && *_p!='O') goto error; _bxp->xxx[_nr] = WDAY_MO; _bxp->req[_nr] = _s*_v; _s = 1; _v = 0; break; case 't': case 'T': _p++; switch(*_p) { case 'h': case 'H': _bxp->xxx[_nr] = WDAY_TH; _bxp->req[_nr] = _s*_v; break; case 'u': case 'U': _bxp->xxx[_nr] = WDAY_TU; _bxp->req[_nr] = _s*_v; break; default: goto error; } _s = 1; _v = 0; break; case 'w': case 'W': _p++; if(*_p!='e' && *_p!='E') goto error; _bxp->xxx[_nr] = WDAY_WE; _bxp->req[_nr] = _s*_v; _s = 1; _v = 0; break; case 'f': case 'F': _p++; if(*_p!='r' && *_p!='R') goto error; _bxp->xxx[_nr] = WDAY_FR; _bxp->req[_nr] = _s*_v; _s = 1; _v = 0; break; case '-': _s = -1; break; case '+': case ' ': case '\t': break; case ',': _nr++; break; default: goto error; } _p++; } return _bxp; error: tr_byxxx_free(_bxp); return NULL; } tr_byxxx_p ic_parse_byxxx(char *_in, char type) { tr_byxxx_p _bxp = NULL; int _nr, _s, _v; char *_p; if(!_in) return NULL; _bxp = tr_byxxx_new(type); if(!_bxp) return NULL; _p = _in; _nr = 1; while(*_p) { if(*_p == ',') _nr++; _p++; } if(tr_byxxx_init(_bxp, _nr) < 0) { tr_byxxx_free(_bxp); return NULL; } _p = _in; _nr = _v = 0; _s = 1; while(*_p && _nr < _bxp->nr) { switch(*_p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': _v = _v*10 + *_p - '0'; break; case '-': _s = -1; break; case '+': case ' ': case '\t': break; case ',': _bxp->xxx[_nr] = _v; _bxp->req[_nr] = _s; _s = 1; _v = 0; _nr++; break; default: goto error; } _p++; } if(_nr < _bxp->nr) { _bxp->xxx[_nr] = _v; _bxp->req[_nr] = _s; } return _bxp; error: tr_byxxx_free(_bxp); return NULL; } int ic_parse_wkst(char *_in) { if(!_in || strlen(_in)!=2) goto error; switch(_in[0]) { case 's': case 'S': switch(_in[1]) { case 'a': case 'A': return WDAY_SA; case 'u': case 'U': return WDAY_SU; default: goto error; } case 'm': case 'M': if(_in[1]!='o' && _in[1]!='O') goto error; return WDAY_MO; case 't': case 'T': switch(_in[1]) { case 'h': case 'H': return WDAY_TH; case 'u': case 'U': return WDAY_TU; default: goto error; } case 'w': case 'W': if(_in[1]!='e' && _in[1]!='E') goto error; return WDAY_WE; case 'f': case 'F': if(_in[1]!='r' && _in[1]!='R') goto error; return WDAY_FR; break; default: goto error; } error: #ifdef USE_YWEEK_U return WDAY_SU; #else return WDAY_MO; #endif } /*** local headers ***/ int get_min_interval(tmrec_p); int check_min_unit(tmrec_p, ac_tm_p, tr_res_p); int check_freq_interval(tmrec_p _trp, ac_tm_p _atp); int check_byxxx(tmrec_p, ac_tm_p); /** * * return 0/REC_MATCH - the time falls in * -1/REC_ERR - error * 1/REC_NOMATCH - the time falls out */ int check_tmrec(tmrec_p _trp, ac_tm_p _atp, tr_res_p _tsw) { if(!_trp || !_atp) return REC_ERR; /* it is before start date */ if(_atp->time < _trp->dtstart) return REC_NOMATCH; /* no duration or end -> for ever */ if (!_IS_SET(_trp->duration) && !_IS_SET(_trp->dtend)) return REC_MATCH; /* compute the duration of the recurrence interval */ if(!_IS_SET(_trp->duration)) _trp->duration = _trp->dtend - _trp->dtstart; if(_atp->time <= _trp->dtstart+_trp->duration) { if(_tsw) { if(_tsw->flag & TSW_RSET) { if(_tsw->rest>_trp->dtstart+_trp->duration-_atp->time) _tsw->rest = _trp->dtstart+_trp->duration - _atp->time; } else { _tsw->flag |= TSW_RSET; _tsw->rest = _trp->dtstart+_trp->duration - _atp->time; } } return REC_MATCH; } /* after the bound of recurrence */ if(_IS_SET(_trp->until) && _atp->time >= _trp->until + _trp->duration) return REC_NOMATCH; /* check if the instance of recurrence matches the 'interval' */ if(check_freq_interval(_trp, _atp)!=REC_MATCH) return REC_NOMATCH; if(check_min_unit(_trp, _atp, _tsw)!=REC_MATCH) return REC_NOMATCH; if(check_byxxx(_trp, _atp)!=REC_MATCH) return REC_NOMATCH; return REC_MATCH; } int check_freq_interval(tmrec_p _trp, ac_tm_p _atp) { int _t0, _t1; struct tm _tm; if(!_trp || !_atp) return REC_ERR; if(!_IS_SET(_trp->freq)) return REC_NOMATCH; if(!_IS_SET(_trp->interval) || _trp->interval==1) return REC_MATCH; switch(_trp->freq) { case FREQ_DAILY: case FREQ_WEEKLY: memset(&_tm, 0, sizeof(struct tm)); _tm.tm_year = _trp->ts.tm_year; _tm.tm_mon = _trp->ts.tm_mon; _tm.tm_mday = _trp->ts.tm_mday; _t0 = (int)mktime(&_tm); memset(&_tm, 0, sizeof(struct tm)); _tm.tm_year = _atp->t.tm_year; _tm.tm_mon = _atp->t.tm_mon; _tm.tm_mday = _atp->t.tm_mday; _t1 = (int)mktime(&_tm); if(_trp->freq == FREQ_DAILY) return (((_t1-_t0)/(24*3600))%_trp->interval==0)? REC_MATCH:REC_NOMATCH; #ifdef USE_YWEEK_U _t0 -= _trp->ts.tm_wday*24*3600; _t1 -= _atp->t.tm_wday*24*3600; #else _t0 -= ((_trp->ts.tm_wday+6)%7)*24*3600; _t1 -= ((_atp->t.tm_wday+6)%7)*24*3600; #endif return (((_t1-_t0)/(7*24*3600))%_trp->interval==0)? REC_MATCH:REC_NOMATCH; case FREQ_MONTHLY: _t0 = (_atp->t.tm_year-_trp->ts.tm_year)*12 + _atp->t.tm_mon-_trp->ts.tm_mon; return (_t0%_trp->interval==0)?REC_MATCH:REC_NOMATCH; case FREQ_YEARLY: return ((_atp->t.tm_year-_trp->ts.tm_year)%_trp->interval==0)? REC_MATCH:REC_NOMATCH; } return REC_NOMATCH; } int get_min_interval(tmrec_p _trp) { if(!_trp) return FREQ_NOFREQ; if(_trp->freq == FREQ_DAILY || _trp->byday || _trp->bymday || _trp->byyday) return FREQ_DAILY; if(_trp->freq == FREQ_WEEKLY || _trp->byweekno) return FREQ_WEEKLY; if(_trp->freq == FREQ_MONTHLY || _trp->bymonth) return FREQ_MONTHLY; if(_trp->freq == FREQ_YEARLY) return FREQ_YEARLY; return FREQ_NOFREQ; } int check_min_unit(tmrec_p _trp, ac_tm_p _atp, tr_res_p _tsw) { int _v0, _v1; if(!_trp || !_atp) return REC_ERR; switch(get_min_interval(_trp)) { case FREQ_DAILY: break; case FREQ_WEEKLY: if(_trp->ts.tm_wday != _atp->t.tm_wday) return REC_NOMATCH; break; case FREQ_MONTHLY: if(_trp->ts.tm_mday != _atp->t.tm_mday) return REC_NOMATCH; break; case FREQ_YEARLY: if(_trp->ts.tm_mon != _atp->t.tm_mon || _trp->ts.tm_mday != _atp->t.tm_mday) return REC_NOMATCH; break; default: return REC_NOMATCH; } _v0 = _trp->ts.tm_hour*3600 + _trp->ts.tm_min*60 + _trp->ts.tm_sec; _v1 = _atp->t.tm_hour*3600 + _atp->t.tm_min*60 + _atp->t.tm_sec; if(_v1 >= _v0 && _v1 < _v0 + _trp->duration) { if(_tsw) { if(_tsw->flag & TSW_RSET) { if(_tsw->rest>_v0+_trp->duration-_v1) _tsw->rest = _v0 + _trp->duration - _v1; } else { _tsw->flag |= TSW_RSET; _tsw->rest = _v0 + _trp->duration - _v1; } } return REC_MATCH; } return REC_NOMATCH; } int check_byxxx(tmrec_p _trp, ac_tm_p _atp) { int i; ac_maxval_p _amp = NULL; if(!_trp || !_atp) return REC_ERR; if(!_trp->byday && !_trp->bymday && !_trp->byyday && !_trp->bymonth && !_trp->byweekno) return REC_MATCH; _amp = ac_get_maxval(_atp); if(_trp->bymonth) { for(i=0; i<_trp->bymonth->nr; i++) { if(_atp->t.tm_mon == (_trp->bymonth->xxx[i]*_trp->bymonth->req[i]+12)%12) break; } if(i>=_trp->bymonth->nr) return REC_NOMATCH; } if(_trp->freq==FREQ_YEARLY && _trp->byweekno) { for(i=0; i<_trp->byweekno->nr; i++) { if(_atp->yweek == (_trp->byweekno->xxx[i]*_trp->byweekno->req[i]+ _amp->yweek)%_amp->yweek) break; } if(i>=_trp->byweekno->nr) return REC_NOMATCH; } if(_trp->byyday) { for(i=0; i<_trp->byyday->nr; i++) { if(_atp->t.tm_yday == (_trp->byyday->xxx[i]*_trp->byyday->req[i]+ _amp->yday)%_amp->yday) break; } if(i>=_trp->byyday->nr) return REC_NOMATCH; } if(_trp->bymday) { for(i=0; i<_trp->bymday->nr; i++) { #ifdef EXTRA_DEBUG LM_DBG("%d == %d\n", _atp->t.tm_mday, (_trp->bymday->xxx[i]*_trp->bymday->req[i]+ _amp->mday)%_amp->mday + ((_trp->bymday->req[i]<0)?1:0)); #endif if(_atp->t.tm_mday == (_trp->bymday->xxx[i]*_trp->bymday->req[i]+ _amp->mday)%_amp->mday + (_trp->bymday->req[i]<0)?1:0) break; } if(i>=_trp->bymday->nr) return REC_NOMATCH; } if(_trp->byday) { for(i=0; i<_trp->byday->nr; i++) { if(_trp->freq==FREQ_YEARLY) { #ifdef EXTRA_DEBUG LM_DBG("%d==%d && %d==%d\n", _atp->t.tm_wday, _trp->byday->xxx[i], _atp->ywday+1, (_trp->byday->req[i]+_amp->ywday)%_amp->ywday); #endif if(_atp->t.tm_wday == _trp->byday->xxx[i] && _atp->ywday+1 == (_trp->byday->req[i]+_amp->ywday)% _amp->ywday) break; } else { if(_trp->freq==FREQ_MONTHLY) { #ifdef EXTRA_DEBUG LM_DBG("%d==%d && %d==%d\n", _atp->t.tm_wday, _trp->byday->xxx[i], _atp->mwday+1, (_trp->byday->req[i]+_amp->mwday)%_amp->mwday); #endif if(_atp->t.tm_wday == _trp->byday->xxx[i] && _atp->mwday+1==(_trp->byday->req[i]+ _amp->mwday)%_amp->mwday) break; } else { if(_atp->t.tm_wday == _trp->byday->xxx[i]) break; } } } if(i>=_trp->byday->nr) return REC_NOMATCH; } return REC_MATCH; } opensips-2.2.2/time_rec.h000066400000000000000000000076141300170765700153330ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * History: * ------- * 2003-06-24: file imported from tmrec (bogdan) * 2003-xx-xx: file Created (daniel) */ #ifndef _TIME_REC_H_ #define _TIME_REC_H_ /************************ imported from "ac_tm.h" ***************************/ #include /* USE_YWEEK_U -- Sunday system - see strftime %U * USE_YWEEK_V -- ISO 8601 - see strftime %V * USE_YWEEK_W -- Monday system - see strftime %W */ #ifndef USE_YWEEK_U # ifndef USE_YWEEK_V # ifndef USE_YWEEK_W # define USE_YWEEK_W # endif # endif #endif #define FREQ_NOFREQ 0 #define FREQ_YEARLY 1 #define FREQ_MONTHLY 2 #define FREQ_WEEKLY 3 #define FREQ_DAILY 4 #define WDAY_SU 0 #define WDAY_MO 1 #define WDAY_TU 2 #define WDAY_WE 3 #define WDAY_TH 4 #define WDAY_FR 5 #define WDAY_SA 6 #define WDAY_NU 7 #define TSW_TSET 1 #define TSW_RSET 2 #define SHM_ALLOC 0 #define PKG_ALLOC 1 #define is_leap_year(yyyy) ((((yyyy)%400))?(((yyyy)%100)?(((yyyy)%4)?0:1):0):1) #define TR_SEPARATOR '|' #define load_TR_value( _p,_s, _tr, _func, _err, _done) \ do{ \ _s = strchr(_p, (int)TR_SEPARATOR); \ if (_s) \ *_s = 0; \ /* LM_DBG("----parsing tr param <%s>\n",_p); \ */\ if(_s != _p) {\ if( _func( _tr, _p)) {\ LM_DBG("func error\n"); \ if (_s) *_s = TR_SEPARATOR; \ goto _err; \ } \ } \ if (_s) { \ *_s = TR_SEPARATOR; \ _p = _s+1;\ if ( *(_p)==0 ) \ goto _done; \ } else {\ goto _done; \ }\ } while(0) typedef struct _ac_maxval { int yweek; int yday; int ywday; int mweek; int mday; int mwday; } ac_maxval_t, *ac_maxval_p; typedef struct _ac_tm { time_t time; struct tm t; int mweek; int yweek; int ywday; int mwday; char flags; } ac_tm_t, *ac_tm_p; typedef struct _tr_byxxx { int nr; int *xxx; int *req; char flags; } tr_byxxx_t, *tr_byxxx_p; typedef struct _tmrec { time_t dtstart; struct tm ts; time_t dtend; time_t duration; time_t until; int freq; int interval; tr_byxxx_p byday; tr_byxxx_p bymday; tr_byxxx_p byyday; tr_byxxx_p bymonth; tr_byxxx_p byweekno; int wkst; char flags; } tmrec_t, *tmrec_p; typedef struct _tr_res { int flag; time_t rest; } tr_res_t, *tr_res_p; int ac_tm_set_time(ac_tm_p, time_t); int ac_tm_reset(ac_tm_p); int ac_get_mweek(struct tm*); int ac_get_yweek(struct tm*); int ac_get_wkst(); int ac_print(ac_tm_p); tr_byxxx_p tr_byxxx_new(char); int tr_byxxx_init(tr_byxxx_p, int); int tr_byxxx_free(tr_byxxx_p); tmrec_p tmrec_new(char); int tmrec_free(tmrec_p); int tr_parse_dtstart(tmrec_p, char*); int tr_parse_dtend(tmrec_p, char*); int tr_parse_duration(tmrec_p, char*); int tr_parse_until(tmrec_p, char*); int tr_parse_freq(tmrec_p, char*); int tr_parse_interval(tmrec_p, char*); int tr_parse_byday(tmrec_p, char*); int tr_parse_bymday(tmrec_p, char*); int tr_parse_byyday(tmrec_p, char*); int tr_parse_bymonth(tmrec_p, char*); int tr_parse_byweekno(tmrec_p, char*); int tr_parse_wkst(tmrec_p, char*); int tr_print(tmrec_p); time_t ic_parse_datetime(char*,struct tm*); time_t ic_parse_duration(char*); tr_byxxx_p ic_parse_byday(char*, char); tr_byxxx_p ic_parse_byxxx(char*, char); int ic_parse_wkst(char*); int check_tmrec(tmrec_p, ac_tm_p, tr_res_p); #endif opensips-2.2.2/timer.c000066400000000000000000000417301300170765700146540ustar00rootroot00000000000000/* * Copyright (C) 2014 OpenSIPS Solutions * Copyright (C) 2007 Voice Sistem SRL * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2003-03-19 replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-03-29 cleaning pkg_mallocs introduced (jiri) * 2007-02-02 timer with resolution of microseconds added (bogdan) * 2014-09-11 timer tasks distributed via reactors (bogdan) * 2014-10-03 drop all timer processes (aside keeper) (bogdan) */ /*! * \file * \brief Timer handling */ /* keep this first as it needs to include some glib h file with * special defines enabled (mainly sys/types.h) */ #include "reactor.h" #include #include #include #include #include #include #include "action.h" #include "timer.h" #include "dprint.h" #include "error.h" #include "pt.h" #include "config.h" #include "sr_module.h" #include "daemonize.h" #include "mem/mem.h" #include "mem/shm_mem.h" #include /* list with all the registered timers */ static struct os_timer *timer_list = NULL; /* list with all the registered utimers */ static struct os_timer *utimer_list = NULL; static unsigned int *jiffies=0; static utime_t *ujiffies=0; static utime_t *ijiffies=0; static unsigned short timer_id=0; static int timer_pipe[2]; int timer_fd_out = -1 ; /* ret 0 on success, <0 on error*/ int init_timer(void) { int optval; jiffies = shm_malloc(sizeof(unsigned int)); ujiffies = shm_malloc(sizeof(utime_t)); ijiffies = shm_malloc(sizeof(utime_t)); if (jiffies==0 || ujiffies==0 || ijiffies==0 ){ LM_CRIT("could not init jiffies\n"); return E_OUT_OF_MEM; } if (UTIMER_TICK>TIMER_TICK*1000000) { LM_CRIT("UTIMER > TIMER!!\n"); return E_CFG; } if ( ((TIMER_TICK*1000000) % UTIMER_TICK)!=0 ) { LM_CRIT("TIMER must be multiple of UTIMER!!\n"); return E_CFG; } *jiffies=0; *ujiffies=0; *ijiffies=0; /* create the pipe for dispatching the timer jobs */ if ( pipe(timer_pipe)!=0 ) { LM_ERR("failed to create time pipe (%s)!\n",strerror(errno)); return E_UNSPEC; } /* make reading fd non-blocking */ optval=fcntl(timer_pipe[0], F_GETFL); if (optval==-1){ LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno)); return E_UNSPEC; } if (fcntl(timer_pipe[0],F_SETFL,optval|O_NONBLOCK)==-1){ LM_ERR("set non-blocking failed: (%d) %s\n", errno, strerror(errno)); return E_UNSPEC; } /* make visible the "read" part of the pipe */ timer_fd_out = timer_pipe[0]; return 0; } void destroy_timer(void) { if (jiffies){ shm_free(jiffies); jiffies=0; shm_free(ujiffies); ujiffies=0; } } static inline struct os_timer* new_os_timer(char *label, unsigned short flags, timer_function f, void* param, unsigned int interval) { struct os_timer* t; if (label==NULL) label = "n/a"; t=shm_malloc( sizeof(struct os_timer) + strlen(label)+1 ); if (t==0){ LM_ERR("out of pkg memory\n"); return NULL; } t->id=timer_id++; t->flags = flags; t->label = (char*)(t+1); strcpy( t->label, label); t->u.timer_f=f; t->t_param=param; t->interval=interval; t->expires=*jiffies+interval; t->trigger_time = 0; t->time = 0; return t; } /*register a periodic timer; * ret: <0 on error * Hint: if you need it in a module, register it from mod_init or it * won't work otherwise*/ int register_timer(char *label, timer_function f, void* param, unsigned int interval, unsigned short flags) { struct os_timer* t; flags = flags & (~TIMER_FLAG_IS_UTIMER); /* just to be sure */ t = new_os_timer( label, flags, f, param, interval); if (t==NULL) return E_OUT_OF_MEM; /* insert it into the timer list*/ t->next = timer_list; timer_list = t; return t->id; } int register_utimer(char *label, utimer_function f, void* param, unsigned int interval, unsigned short flags) { struct os_timer* t; flags = flags | TIMER_FLAG_IS_UTIMER; /* just to be sure */ t = new_os_timer( label, 1, (timer_function*)f, param, interval); if (t==NULL) return E_OUT_OF_MEM; /* insert it into the utimer list*/ t->next = utimer_list; utimer_list = t; return t->id; } void route_timer_f(unsigned int ticks, void* param) { struct action* a = (struct action*)param; static struct sip_msg* req= NULL; if(req == NULL) { req = (struct sip_msg*)pkg_malloc(sizeof(struct sip_msg)); if(req == NULL) { LM_ERR("No more memory\n"); return; } memset(req, 0, sizeof(struct sip_msg)); req->first_line.type = SIP_REQUEST; req->first_line.u.request.method.s= "DUMMY"; req->first_line.u.request.method.len= 5; req->first_line.u.request.uri.s= "sip:user@domain.com"; req->first_line.u.request.uri.len= 19; req->rcv.src_ip.af = AF_INET; req->rcv.dst_ip.af = AF_INET; } if(a == NULL) { LM_ERR("NULL action\n"); return; } run_top_route(a, req); /* clean whatever extra structures were added by script functions */ free_sip_msg(req); /* remove all added AVP - here we use all the time the default AVP list */ reset_avps( ); } int register_route_timers(void) { struct os_timer* t; int i; if(timer_rlist[0].a == NULL) return 0; /* register the routes */ for(i = 0; i< TIMER_RT_NO; i++) { if(timer_rlist[i].a == NULL) return 0; t = new_os_timer( "timer_route", 0, route_timer_f, timer_rlist[i].a, timer_rlist[i].interval); if (t==NULL) return E_OUT_OF_MEM; /* insert it into the list*/ t->next = timer_list; timer_list = t; } return 1; } unsigned int have_ticks(void) { return jiffies==NULL ? 0 : 1; } unsigned int have_uticks(void) { return ujiffies==NULL ? 0 : 1; } unsigned int get_ticks(void) { return *jiffies; } utime_t get_uticks(void) { return *ujiffies; } static inline void timer_ticker(struct os_timer *timer_list) { struct os_timer* t; unsigned int j; ssize_t l; /* we need to store the original time as while executing the the handlers, the time may pass, affecting the way we calculate the new expire (expire will include the time taken to run handlers) -bogdan */ j = *jiffies; for (t=timer_list;t; t=t->next){ if (j>=t->expires){ if (t->trigger_time) { LM_WARN("timer task <%s> already scheduled for %lld ms" " (now %lld ms), it may overlap..\n", t->label, (utime_t)(t->trigger_time/1000), ((utime_t)*ijiffies/1000) ); if (t->flags&TIMER_FLAG_SKIP_ON_DELAY) { /* skip this execution of the timer handler */ t->expires = j + t->interval; continue; } else if (t->flags&TIMER_FLAG_DELAY_ON_DELAY) { /* delay the execution of the timer handler until the prev one is done */ continue; } else { /* launch the task now, even if overlapping with the already running one */ } } t->expires = j + t->interval; t->trigger_time = *ijiffies; t->time = j; /* push the jobs for execution */ again: l = write( timer_pipe[1], &t, sizeof(t)); if (l==-1) { if (errno==EAGAIN || errno==EINTR || errno==EWOULDBLOCK ) goto again; LM_ERR("writing failed:[%d] %s, skipping job <%s> at %d s\n", errno, strerror(errno),t->label, j); } } } } static inline void utimer_ticker(struct os_timer *utimer_list) { struct os_timer* t; utime_t uj; ssize_t l; /* see comment on timer_ticket */ uj = *ujiffies; for ( t=utimer_list ; t ; t=t->next){ if (uj>=t->expires){ if (t->trigger_time) { LM_WARN("utimer task <%s> already scheduled for %lld ms" " (now %lld ms), it may overlap..\n", t->label, (utime_t)(t->trigger_time/1000), ((utime_t)*ijiffies/1000) ); if (t->flags&TIMER_FLAG_SKIP_ON_DELAY) { /* skip this execution of the timer handler */ t->expires = uj + t->interval; continue; } else if (t->flags&TIMER_FLAG_DELAY_ON_DELAY) { /* delay the execution of the timer handler until the prev one is done */ continue; } else { /* launch the task now, even if overlapping with the already running one */ } } t->expires = uj + t->interval; t->trigger_time = *ijiffies; t->time = uj; /* push the jobs for execution */ again: l = write( timer_pipe[1], &t, sizeof(t)); if (l==-1) { if (errno==EAGAIN || errno==EINTR || errno==EWOULDBLOCK ) goto again; LM_ERR("writing failed:[%d] %s, skipping job <%s> at %lld us\n", errno, strerror(errno),t->label, uj); } } } } static void run_timer_process( void ) { unsigned int multiple; unsigned int cnt; struct timeval o_tv; struct timeval tv, comp_tv; utime_t drift; utime_t uinterval; utime_t wait; utime_t ij; /* timer re-calibration to compensate drifting */ #define compute_wait_with_drift(_tv) \ do { \ if ( drift > ITIMER_TICK ) { \ wait = (drift >= uinterval) ? 0 : uinterval-drift; \ _tv.tv_sec = wait / 1000000; \ _tv.tv_usec = wait % 1000000; \ drift -= uinterval-wait; \ } else { \ _tv = o_tv; \ } \ }while(0) if ( (utimer_list==NULL) || ((TIMER_TICK*1000000) == UTIMER_TICK) ) { o_tv.tv_sec = TIMER_TICK; o_tv.tv_usec = 0; multiple = 1; } else { o_tv.tv_sec = UTIMER_TICK / 1000000; o_tv.tv_usec = UTIMER_TICK % 1000000; multiple = (( TIMER_TICK * 1000000 ) / UTIMER_TICK ) / 1000000; } LM_DBG("tv = %ld, %ld , m=%d\n", (long)o_tv.tv_sec,(long)o_tv.tv_usec,multiple); drift = 0; uinterval = o_tv.tv_sec * 1000000 + o_tv.tv_usec; if (utimer_list==NULL) { /* only TIMERs, ticking at TIMER_TICK */ for( ; ; ) { ij = *ijiffies; compute_wait_with_drift(comp_tv); tv = comp_tv; select( 0, 0, 0, 0, &tv); timer_ticker( timer_list); drift += ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec > (*ijiffies-ij)) ? 0 : *ijiffies-ij - ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec); } } else if (timer_list==NULL) { /* only UTIMERs, ticking at UTIMER_TICK */ for( ; ; ) { ij = *ijiffies; compute_wait_with_drift(comp_tv); tv = comp_tv; select( 0, 0, 0, 0, &tv); utimer_ticker( utimer_list); drift += ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec > (*ijiffies-ij)) ? 0 : *ijiffies-ij - ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec); } } else if (multiple==1) { /* TIMERs and UTIMERs, ticking together TIMER_TICK (synced) */ for( ; ; ) { ij = *ijiffies; compute_wait_with_drift(comp_tv); tv = comp_tv; select( 0, 0, 0, 0, &tv); timer_ticker( timer_list); utimer_ticker( utimer_list); drift += ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec > (*ijiffies-ij)) ? 0 : *ijiffies-ij - ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec); } } else { /* TIMERs and UTIMERs, TIMER_TICK is multiple of UTIMER_TICK */ for( cnt=1 ; ; cnt++ ) { ij = *ijiffies; compute_wait_with_drift(comp_tv); tv = comp_tv; select( 0, 0, 0, 0, &tv); utimer_ticker(utimer_list); if (cnt==multiple) { timer_ticker(timer_list); cnt = 0; } drift += ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec > (*ijiffies-ij)) ? 0 : *ijiffies-ij - ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec); } } } static void run_timer_process_jif(void) { unsigned int multiple; unsigned int umultiple; unsigned int cnt; unsigned int ucnt; struct timeval o_tv; struct timeval tv; struct timeval sync_ts, last_ts; stime_t interval, drift; utime_t last_ticks, last_sync = 0; o_tv.tv_sec = 0; o_tv.tv_usec = ITIMER_TICK; /* internal timer */ multiple = ((TIMER_TICK*1000000)) / (UTIMER_TICK); umultiple = (UTIMER_TICK) / (ITIMER_TICK); LM_DBG("tv = %ld, %ld , m=%d, mu=%d\n", (long)o_tv.tv_sec,(long)o_tv.tv_usec,multiple,umultiple); gettimeofday(&last_ts, 0); last_ticks = *ijiffies; for( cnt=1,ucnt=1 ; ; ucnt++ ) { tv = o_tv; select( 0, 0, 0, 0, &tv); /* update internal timer */ *(ijiffies)+=ITIMER_TICK; /* update public utimer */ if (ucnt==umultiple) { *(ujiffies)+=UTIMER_TICK; /* no overflow test as even if we go for 1 microsecond tick, * this will happen in 14038618 years :P */ ucnt = 0; cnt++; /* update public timer */ if (cnt==multiple) { *(jiffies)+=TIMER_TICK; /* test for overflow (if tick= 1s =>overflow in 136 years)*/ cnt = 0; } } /* synchronize with system time if needed */ if (*ijiffies - last_sync >= TIMER_SYNC_TICKS) { last_sync = *ijiffies; gettimeofday(&sync_ts, 0); interval = (utime_t)sync_ts.tv_sec*1000000 + sync_ts.tv_usec - (utime_t)last_ts.tv_sec*1000000 - last_ts.tv_usec; drift = interval - (*ijiffies - last_ticks); /* protect against sudden time changes */ if (interval < 0 || drift < 0 || drift > TIMER_SYNC_TICKS) { last_ts = sync_ts; last_ticks = *ijiffies; LM_DBG("System time changed, ignoring...\n"); continue; } if (drift > TIMER_MAX_DRIFT_TICKS) { *(ijiffies) += (drift / ITIMER_TICK) * ITIMER_TICK; ucnt += drift / ITIMER_TICK; *(ujiffies) += (ucnt / umultiple) * (UTIMER_TICK); ucnt = ucnt % umultiple; cnt += (unsigned int)(drift / (UTIMER_TICK)); *(jiffies) += (cnt / multiple) * TIMER_TICK; cnt = cnt % multiple; } } } } int start_timer_processes(void) { pid_t pid; /* * A change of the way timers were run. In the pre-1.5 times, * all timer processes had their own jiffies and just the first * one was doing the global ones. Now, there's a separate process * that increases jiffies - run_timer_process_jif(), and the rest * just use that one. * * The main reason for this change was when a function that relied * on jiffies for its timeouts got called from the timer thread and * was unable to detect timeouts. */ if ( (pid=internal_fork("time_keeper"))<0 ) { LM_CRIT("cannot fork time keeper process\n"); goto error; } else if (pid==0) { /* new process */ clean_write_pipeend(); run_timer_process_jif(); exit(-1); } /* fork a timer-trigger process */ if ( (pid=internal_fork("timer"))<0 ) { LM_CRIT("cannot fork timer process\n"); goto error; } else if (pid==0) { /* new process */ clean_write_pipeend(); run_timer_process( ); exit(-1); } return 0; error: return -1; } inline static int handle_io(struct fd_map* fm, int idx,int event_type) { switch(fm->type){ case F_TIMER_JOB: handle_timer_job(); return 0; case F_SCRIPT_ASYNC: async_resume_f( &fm->fd, fm->data); return 0; default: LM_CRIT("unknown fd type %d in Timer Extra\n", fm->type); return -1; } return -1; } int start_timer_extra_processes(int *chd_rank) { pid_t pid; (*chd_rank)++; if ( (pid=internal_fork( "Timer handler"))<0 ) { LM_CRIT("cannot fork Timer handler process\n"); return -1; } else if (pid==0) { /* new Timer process */ /* set a more detailed description */ set_proc_attrs("Timer handler"); if (init_child(*chd_rank) < 0) { report_failure_status(); goto error; } report_conditional_status( 1, 0); /* create the reactor for timer proc */ if ( init_worker_reactor( "Timer_extra", RCT_PRIO_MAX)<0 ) { LM_ERR("failed to init reactor\n"); goto error; } /* init: start watching for the timer jobs */ if (reactor_add_reader( timer_fd_out, F_TIMER_JOB, RCT_PRIO_TIMER,NULL)<0){ LM_CRIT("failed to add timer pipe_out to reactor\n"); goto error; } /* launch the reactor */ reactor_main_loop( 1/*timeout in sec*/, error , ); exit(-1); } /*parent*/ return 0; /* only from child process */ error: exit(-1); } void handle_timer_job(void) { struct os_timer *t; ssize_t l; /* read one "os_timer" pointer from the pipe (non-blocking) */ l = read( timer_fd_out, &t, sizeof(t) ); if (l==-1) { if (errno==EAGAIN || errno==EINTR || errno==EWOULDBLOCK ) return; LM_ERR("read failed:[%d] %s\n", errno, strerror(errno)); return; } /* run the handler */ if (t->flags&TIMER_FLAG_IS_UTIMER) { if (t->trigger_time<(*ijiffies-ITIMER_TICK) ) LM_WARN("utimer job <%s> has a %lld us delay in execution\n", t->label, *ijiffies-t->trigger_time); t->u.utimer_f( t->time , t->t_param); t->trigger_time = 0; } else { if (t->trigger_time<(*ijiffies-ITIMER_TICK) ) LM_WARN("timer job <%s> has a %lld us delay in execution\n", t->label, *ijiffies-t->trigger_time); t->u.timer_f( (unsigned int)t->time , t->t_param); t->trigger_time = 0; } return; } opensips-2.2.2/timer.h000066400000000000000000000063211300170765700146560ustar00rootroot00000000000000/* * timer related functions * * Copyright (C) 2014 OpenSIPS Solutions * Copyright (C) 2007 Voice Sistem SRL * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-02-02 timer with resolution of microseconds added (bogdan) * 2014-09-11 timer tasks are distributed via reactor (bogdan) * 2014-10-03 drop all timer processes (aside keeper and trigger) (bogdan) */ /*! * \file * \brief Timer related functions */ #ifndef timer_h #define timer_h typedef unsigned long long utime_t; typedef long long stime_t; typedef void (timer_function)(unsigned int ticks, void* param); typedef void (utimer_function)(utime_t uticks, void* param); /* define internal timer to 10 milliseconds */ #define ITIMER_TICK 10000 #define TIMER_FLAG_IS_UTIMER (1<<0) #define TIMER_FLAG_SKIP_ON_DELAY (1<<1) #define TIMER_FLAG_DELAY_ON_DELAY (1<<2) /* try to synchronize with system time every 5 seconds */ #define TIMER_SYNC_TICKS 5000000 /* synchronize if drift is greater than internal timer tick */ #define TIMER_MAX_DRIFT_TICKS ITIMER_TICK struct os_timer{ /* unique ID in the list of timer handlers - not really used */ unsigned short id; /* is utimer or timer? */ unsigned short flags; /* string label identifying the handler (what module and what for was registered) */ char *label; /* handler function */ union { timer_function* timer_f; utimer_function* utimer_f; }u; /* parameter to the handler function (does not change during runtime) */ void* t_param; /* triggering interval for the handler (does not change during runtime) */ unsigned int interval; /* internal time for the next triggering */ utime_t expires; /* time of the current triggering (based on ITIMER_TICKs) */ utime_t trigger_time; /* UTICKs or TICKs of the triggering */ utime_t time; /* next element in the timer list */ struct os_timer* next; }; extern int timer_fd_out; int init_timer(void); void destroy_timer(void); /*! \brief register a periodic timer; * ret: <0 on error*/ int register_timer(char *label, timer_function f, void* param, unsigned int interval, unsigned short flags); int register_utimer(char *label, utimer_function f, void* param, unsigned int interval, unsigned short flags); unsigned int have_ticks(void); unsigned int have_uticks(void); unsigned int get_ticks(void); utime_t get_uticks(void); int start_timer_processes(void); int start_timer_extra_processes(int *chd_rank); int register_route_timers(void); void handle_timer_job(void); #endif opensips-2.2.2/transformations.c000066400000000000000000002316551300170765700167740ustar00rootroot00000000000000/* * Copyright (C) 2007 voice-system.ro * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! \file * \brief Support for transformations */ #include #include #include #include #include #include #include "dprint.h" #include "mem/mem.h" #include "ut.h" #include "trim.h" #include "dset.h" #include "usr_avp.h" #include "errinfo.h" #include "resolve.h" #include "ip_addr.h" #include "parser/parse_param.h" #include "parser/parse_uri.h" #include "parser/parse_via.h" #include "parser/parse_to.h" #include "parser/sdp/sdp_helpr_funcs.h" #include "strcommon.h" #include "transformations.h" #include "re.h" #define TR_BUFFER_SIZE 65536 /* structure for CSV transformation */ typedef struct csv { str body; struct csv* next; } csv_t; static char _tr_buffer[TR_BUFFER_SIZE]; int run_transformations(struct sip_msg *msg, trans_t *tr, pv_value_t *val) { trans_t *it; int ret = 0; if(tr==NULL || val==NULL){ LM_DBG("null pointer\n"); return -1; } it = tr; while(it) { ret = (*it->trf)(msg, it->params, it->subtype, val); if(ret!=0) return ret; it = it->next; } return 0; } static void trans_fill_left(pv_value_t *val, str pad, int len) { char *p; int r; /* fill with a single char */ if (pad.len == 1) { memset(_tr_buffer, pad.s[0], len); memcpy(_tr_buffer + len, val->rs.s, val->rs.len); val->flags = PV_VAL_STR; val->rs.s = _tr_buffer; val->rs.len += len; /* fill with a string */ } else { p = _tr_buffer; r = len % pad.len; /* handle the first non-even pad */ if (r != 0) { memcpy(p, pad.s + (pad.len - r), r); p += r; len -= r; val->rs.len += r; } /* save initial string len */ r = val->rs.len; while (len > 0) { memcpy(p, pad.s, pad.len); p += pad.len; val->rs.len += pad.len; len -= pad.len; } memcpy(p + len, val->rs.s, r); val->flags = PV_VAL_STR; val->rs.s = _tr_buffer; } } static void trans_fill_right(pv_value_t *val, str pad, int len) { char *p; int r; memcpy(_tr_buffer, val->rs.s, val->rs.len); /* fill with a single char */ if (pad.len == 1) { memset(_tr_buffer + val->rs.len, pad.s[0], len); val->flags = PV_VAL_STR; val->rs.s = _tr_buffer; val->rs.len += len; /* fill with a string */ } else { p = _tr_buffer + val->rs.len; while (len > 0) { r = len < pad.len ? len : pad.len; memcpy(p, pad.s, r); p += r; val->rs.len += r; len -= pad.len; } val->flags = PV_VAL_STR; val->rs.s = _tr_buffer; } } int tr_eval_string(struct sip_msg *msg, tr_param_t *tp, int subtype, pv_value_t *val) { int i, j; char *p, *s; str st; pv_value_t v; if(val==NULL || val->flags&PV_VAL_NULL) return -1; switch(subtype) { case TR_S_LEN: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->ri = val->rs.len; val->rs.s = int2str(val->ri, &val->rs.len); break; case TR_S_INT: if(!(val->flags&PV_VAL_INT)) { //Default conversion to 0 val->ri = 0; /*Ignore the return value of str2sint. str2sint will convert the string up until it finds a non-number char which is the desired behavior for the script level transformation*/ str2sint(&val->rs, &val->ri); } else { if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); } val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; break; case TR_S_MD5: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); compute_md5(_tr_buffer, val->rs.s, val->rs.len); _tr_buffer[MD5_LEN] = '\0'; val->flags = PV_VAL_STR; val->ri = 0; val->rs.s = _tr_buffer; val->rs.len = MD5_LEN; break; case TR_S_CRC32: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); unsigned int crc_val; int length = 10; crc32_uint(&val->rs,&crc_val); val->rs.len = length; val->rs.s = int2str(crc_val,&length); val->flags = PV_VAL_STR; break; case TR_S_ENCODEHEXA: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(val->rs.len>TR_BUFFER_SIZE/2-1) return -1; j = 0; for(i=0; irs.len; i++) { _tr_buffer[j++] = fourbits2char[(unsigned char)val->rs.s[i] >> 4]; _tr_buffer[j++] = fourbits2char[(unsigned char)val->rs.s[i] & 0xf]; } _tr_buffer[j] = '\0'; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs.s = _tr_buffer; val->rs.len = j; break; case TR_S_DECODEHEXA: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(val->rs.len>TR_BUFFER_SIZE*2-1) return -1; for(i=0; irs.len/2; i++) { if(val->rs.s[2*i]>='0'&&val->rs.s[2*i]<='9') _tr_buffer[i] = (val->rs.s[2*i]-'0') << 4; else if(val->rs.s[2*i]>='a'&&val->rs.s[2*i]<='f') _tr_buffer[i] = (val->rs.s[2*i]-'a'+10) << 4; else if(val->rs.s[2*i]>='A'&&val->rs.s[2*i]<='F') _tr_buffer[i] = (val->rs.s[2*i]-'A'+10) << 4; else return -1; if(val->rs.s[2*i+1]>='0'&&val->rs.s[2*i+1]<='9') _tr_buffer[i] += val->rs.s[2*i+1]-'0'; else if(val->rs.s[2*i+1]>='a'&&val->rs.s[2*i+1]<='f') _tr_buffer[i] += val->rs.s[2*i+1]-'a'+10; else if(val->rs.s[2*i+1]>='A'&&val->rs.s[2*i+1]<='F') _tr_buffer[i] += val->rs.s[2*i+1]-'A'+10; else return -1; } _tr_buffer[i] = '\0'; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs.s = _tr_buffer; val->rs.len = i; break; case TR_S_HEX2DEC: if(val->flags&PV_VAL_INT) break; /* already converted */ s = NULL; if (hexstr2int(val->rs.s, val->rs.len, (unsigned int *)&i) < 0) return -1; val->rs.s = int2str(i, &val->rs.len); val->ri = i; val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; break; case TR_S_DEC2HEX: if(!(val->flags&PV_VAL_INT)) { if(str2sint(&val->rs, &val->ri)!=0) return -1; } val->rs.len = snprintf(_tr_buffer, TR_BUFFER_SIZE, "%x", val->ri); if (val->rs.len < 0 || val->rs.len > TR_BUFFER_SIZE) return -1; val->ri = 0; val->rs.s = _tr_buffer; val->flags = PV_VAL_STR; break; case TR_S_ESCAPECOMMON: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(val->rs.len>TR_BUFFER_SIZE/2-1) return -1; i = escape_common(_tr_buffer, val->rs.s, val->rs.len); _tr_buffer[i] = '\0'; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs.s = _tr_buffer; val->rs.len = i; break; case TR_S_UNESCAPECOMMON: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(val->rs.len>TR_BUFFER_SIZE-1) return -1; i = unescape_common(_tr_buffer, val->rs.s, val->rs.len); _tr_buffer[i] = '\0'; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs.s = _tr_buffer; val->rs.len = i; break; case TR_S_ESCAPEUSER: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(val->rs.len>TR_BUFFER_SIZE/2-1) return -1; st.s = _tr_buffer; st.len = TR_BUFFER_SIZE; if (escape_user(&val->rs, &st)) return -1; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs = st; break; case TR_S_UNESCAPEUSER: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(val->rs.len>TR_BUFFER_SIZE-1) return -1; st.s = _tr_buffer; st.len = TR_BUFFER_SIZE; if (unescape_user(&val->rs, &st)) return -1; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs = st; break; case TR_S_ESCAPEPARAM: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(val->rs.len>TR_BUFFER_SIZE/2-1) return -1; st.s = _tr_buffer; st.len = TR_BUFFER_SIZE; if (escape_param(&val->rs, &st) < 0) return -1; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs = st; break; case TR_S_UNESCAPEPARAM: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(val->rs.len>TR_BUFFER_SIZE-1) return -1; st.s = _tr_buffer; st.len = TR_BUFFER_SIZE; if (unescape_param(&val->rs, &st) < 0) return -1; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs = st; break; case TR_S_SUBSTR: if(tp==NULL || tp->next==NULL) { LM_ERR("substr invalid parameters\n"); return -1; } if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(tp->type==TR_PARAM_NUMBER) { i = tp->v.n; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_INT))) { LM_ERR("substr cannot get p1\n"); return -1; } i = v.ri; } if(tp->next->type==TR_PARAM_NUMBER) { j = tp->next->v.n; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->next->v.data, &v)!=0 || (!(v.flags&PV_VAL_INT))) { LM_ERR("substr cannot get p2\n"); return -1; } j = v.ri; } LM_DBG("i=%d j=%d\n", i, j); if(j<0) { LM_ERR("substr negative offset\n"); return -1; } val->flags = PV_VAL_STR; val->ri = 0; if(i>=0) { if(i>=val->rs.len) { LM_ERR("substr out of range\n"); return -1; } if(i+j>=val->rs.len) j=0; if(j==0) { /* to end */ val->rs.s += i; val->rs.len -= i; break; } val->rs.s += i; val->rs.len = j; break; } i = -i; if(i>val->rs.len) { LM_ERR("substr out of range\n"); return -1; } if(irs.s += val->rs.len-i; val->rs.len = i; break; } val->rs.s += val->rs.len-i; val->rs.len = j; break; case TR_S_SELECT: if(tp==NULL || tp->next==NULL) { LM_ERR("select invalid parameters\n"); return -1; } if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(tp->type==TR_PARAM_NUMBER) { i = tp->v.n; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_INT))) { LM_ERR("select cannot get p1\n"); return -1; } i = v.ri; } val->flags = PV_VAL_STR; val->ri = 0; if(i<0) { s = val->rs.s+val->rs.len-1; p = s; i = -i; i--; while(p>=val->rs.s) { if(*p==tp->next->v.s.s[0]) { if(i==0) break; s = p-1; i--; } p--; } if(i==0) { val->rs.s = p+1; val->rs.len = s-p; } else { val->rs.s = ""; val->rs.len = 0; } } else { s = val->rs.s; p = s; while(prs.s+val->rs.len) { if(*p==tp->next->v.s.s[0]) { if(i==0) break; s = p + 1; i--; } p++; } if(i==0) { val->rs.s = s; val->rs.len = p-s; } else { val->rs.s = ""; val->rs.len = 0; } } break; case TR_S_TOLOWER: if(!(val->flags&PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; break; } if(val->rs.len>TR_BUFFER_SIZE-1) return -1; st.s = _tr_buffer; st.len = val->rs.len; for (i=0; irs.s[i]>='A' && val->rs.s[i]<='Z') ?('a' + val->rs.s[i] -'A'):val->rs.s[i]; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs = st; break; case TR_S_TOUPPER: if(!(val->flags&PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; break; } if(val->rs.len>TR_BUFFER_SIZE-1) return -1; st.s = _tr_buffer; st.len = val->rs.len; for (i=0; irs.s[i]>='a' && val->rs.s[i]<='z') ?('A' + val->rs.s[i] -'a'):val->rs.s[i]; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs = st; break; case TR_S_INDEX: case TR_S_RINDEX: /* Ensure it is in string format */ if(!(val->flags&PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; } /* Needle to look for in haystack */ if(tp->type==TR_PARAM_STRING) { st = tp->v.s; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_STR)) || v.rs.len<=0) { LM_ERR("index/rindex cannot get p1\n"); return -1; } st = v.rs; } /* User supplied starting position */ if (tp->next != NULL) { if(tp->next->type==TR_PARAM_NUMBER) { i = tp->next->v.n; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->next->v.data, &v)!=0 || (!(v.flags&PV_VAL_INT))) { LM_ERR("index/rindex cannot get p2\n"); return -1; } i = v.ri; } } else { /* Default start positions: 0 for index, end of str for rindex */ i = (subtype == TR_S_INDEX ? 0 : (val->rs.len - 1)); } /* If start is negative base it off end of string e.g -2 on 10 char str start of 8. */ if (i < 0 ){ if ( val->rs.len > 0 ) { /* Support wrapping on negative index e.g -2 and -12 index are same on strlen of 10 */ i = ( (i * -1) % val->rs.len ); /* No remainder means we start at 0 otherwise take remainder off the end */ if ( i > 0) { i = (val->rs.len - i); } } else { /* Case of searching through an empty string is caught later */ i = 0; } } /* Index */ if (subtype == TR_S_INDEX) { /* If start index is beyond end of string or Needle is bigger than haystack return -1 */ if ( i >= val->rs.len || st.len > (val->rs.len - i)) { memset(val, 0, sizeof(pv_value_t)); val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->ri = -1; val->rs.s = int2str(val->ri, &val->rs.len); break; } /* Iterate through string starting at index After j there are no longer enough characters left to match the needle */ j = (val->rs.len - st.len); while (i <= j) { if (val->rs.s[i] == st.s[0]) { /* First character matches, do a full comparison shortcut for single character lookups */ if (st.len == 1 || strncmp(val->rs.s + i, st.s, st.len) == 0) { /* Bingo, found it */ memset(val, 0, sizeof(pv_value_t)); val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->ri = i; val->rs.s = int2str(val->ri, &val->rs.len); return 0; } } i++; } /* Rindex */ } else { /* Needle bigger than haystack */ if ( st.len > val->rs.len ) { memset(val, 0, sizeof(pv_value_t)); val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->ri = -1; val->rs.s = int2str(val->ri, &val->rs.len); break; } /* Incase of RINDEX clamp index to end of string */ if (i >= val->rs.len) { i = (val->rs.len - 1); } /* Start position does not leave enough characters to match needle, jump ahead */ if ( st.len > (val->rs.len - i) ) { /* Minimum start position allowing for matches */ i = (val->rs.len - st.len); } /* Iterate through string starting at index and going backwards */ while (i >= 0) { if (val->rs.s[i] == st.s[0]) { /* First character matches, do a full comparison shortcut for single character lookups */ if (st.len == 1 || strncmp(val->rs.s + i, st.s, st.len) == 0) { /* Bingo, found it */ memset(val, 0, sizeof(pv_value_t)); val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->ri = i; val->rs.s = int2str(val->ri, &val->rs.len); return 0; } } i--; } } /* Not found */ memset(val, 0, sizeof(pv_value_t)); val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->ri = -1; val->rs.s = int2str(val->ri, &val->rs.len); break; case TR_S_FILL_LEFT: case TR_S_FILL_RIGHT: /* padding string parameter */ st = tp->v.s; /* padded final length parameter */ i = tp->next->v.n; if (val->flags & PV_VAL_STR) { i -= val->rs.len; } else if (val->flags & PV_VAL_INT) { val->rs.s = int2str(val->ri, &val->rs.len); i -= val->rs.len; } /* no need for padding */ if (i < 0) return 0; if (subtype == TR_S_FILL_LEFT) trans_fill_left(val, st, i); else trans_fill_right(val, st, i); break; case TR_S_WIDTH: if(tp==NULL || tp->next!=NULL) { LM_ERR("width invalid parameters\n"); return -1; } if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if(tp->type==TR_PARAM_NUMBER) { i = tp->v.n; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_INT))) { LM_ERR("substr cannot get p1\n"); return -1; } i = v.ri; } if (i <= 0) { LM_ERR("width invalid (must be >= 1)\n"); return -1; } if (i <= val->rs.len) { /* since the requested width is less than the value length, just update the length */ val->rs.len = i; break; } if(i>TR_BUFFER_SIZE-1) /* width cant be greater than buffer */ return -1; j = i - val->rs.len; /* calc extra length */ p = _tr_buffer; /* copy existing string to buffer and append j spaces */ memcpy(p, val->rs.s, val->rs.len); memset(p+val->rs.len, ' ', j); memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs.s = _tr_buffer; val->rs.len = i; break; case TR_S_B64ENCODE: if(!(val->flags&PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; break; } if(val->rs.len>TR_BUFFER_SIZE-1) { LM_ERR("b64encode value larger than buffer\n"); return -1; } st.s = _tr_buffer; st.len = calc_base64_encode_len(val->rs.len); base64encode((unsigned char *)st.s, (unsigned char *)val->rs.s, val->rs.len); memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs = st; break; case TR_S_B64DECODE: if(!(val->flags&PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; break; } if(val->rs.len>TR_BUFFER_SIZE-1) { LM_ERR("b64decode value larger than buffer\n"); return -1; } st.s = _tr_buffer; st.len = base64decode((unsigned char *)st.s, (unsigned char *)val->rs.s, val->rs.len); memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; val->rs = st; break; case TR_S_XOR: /* ensure string format */ if(!(val->flags&PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; } if(val->rs.len>TR_BUFFER_SIZE-1) { LM_ERR("xor value larger than buffer\n"); return -1; } /* secret to use */ if(tp->type==TR_PARAM_STRING) { st = tp->v.s; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_STR)) || v.rs.len<=0) { LM_ERR("xor cannot get p1\n"); return -1; } st = v.rs; } p = _tr_buffer; for (i=0; irs.len; i++) { *p = val->rs.s[i] ^ st.s[i % st.len]; p++; } /* leave val flags and length in tact and update with result */ val->rs.s = _tr_buffer; break; case TR_S_TRIM: if (!(val->flags & PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; } trim(&val->rs); break; case TR_S_TRIMR: if (!(val->flags & PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; } trim_trailing(&val->rs); break; case TR_S_TRIML: if (!(val->flags & PV_VAL_STR)) { val->rs.s = int2str(val->ri, &val->rs.len); val->flags |= PV_VAL_STR; } trim_leading(&val->rs); break; default: LM_ERR("unknown subtype %d\n", subtype); return -1; } return 0; } static str _tr_empty = { "", 0 }; static str _tr_uri = {0, 0}; static struct sip_uri _tr_parsed_uri; static param_t* _tr_uri_params = NULL; int tr_eval_uri(struct sip_msg *msg, tr_param_t *tp, int subtype, pv_value_t *val) { pv_value_t v; str sv; param_hooks_t phooks; param_t *pit=NULL; if(val==NULL || (!(val->flags&PV_VAL_STR)) || val->rs.len<=0) return -1; if(_tr_uri.len==0 || _tr_uri.len!=val->rs.len || strncmp(_tr_uri.s, val->rs.s, val->rs.len)!=0) { if(val->rs.len>_tr_uri.len) { if(_tr_uri.s) pkg_free(_tr_uri.s); _tr_uri.s = (char*)pkg_malloc((val->rs.len+1)*sizeof(char)); if(_tr_uri.s==NULL) { LM_ERR("no more private memory\n"); if(_tr_uri_params != NULL) { free_params(_tr_uri_params); _tr_uri_params = 0; } memset(&_tr_uri, 0, sizeof(str)); memset(&_tr_parsed_uri, 0, sizeof(struct sip_uri)); return -1; } } _tr_uri.len = val->rs.len; memcpy(_tr_uri.s, val->rs.s, val->rs.len); _tr_uri.s[_tr_uri.len] = '\0'; /* reset old values */ memset(&_tr_parsed_uri, 0, sizeof(struct sip_uri)); if(_tr_uri_params != NULL) { free_params(_tr_uri_params); _tr_uri_params = 0; } /* parse uri -- params only when requested */ if(parse_uri(_tr_uri.s, _tr_uri.len, &_tr_parsed_uri)!=0) { LM_ERR("invalid uri [%.*s]\n", val->rs.len, val->rs.s); if(_tr_uri_params != NULL) { free_params(_tr_uri_params); _tr_uri_params = 0; } pkg_free(_tr_uri.s); memset(&_tr_uri, 0, sizeof(str)); memset(&_tr_parsed_uri, 0, sizeof(struct sip_uri)); return -1; } } memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; switch(subtype) { case TR_URI_USER: val->rs = (_tr_parsed_uri.user.s)?_tr_parsed_uri.user:_tr_empty; break; case TR_URI_HOST: val->rs = (_tr_parsed_uri.host.s)?_tr_parsed_uri.host:_tr_empty; break; case TR_URI_PASSWD: val->rs = (_tr_parsed_uri.passwd.s)?_tr_parsed_uri.passwd:_tr_empty; break; case TR_URI_PORT: val->flags |= PV_TYPE_INT|PV_VAL_INT; val->rs = (_tr_parsed_uri.port.s)?_tr_parsed_uri.port:_tr_empty; val->ri = _tr_parsed_uri.port_no; break; case TR_URI_PARAMS: val->rs = (_tr_parsed_uri.params.s)?_tr_parsed_uri.params:_tr_empty; break; case TR_URI_PARAM: if(tp==NULL) { LM_ERR("param invalid parameters\n"); return -1; } if(_tr_parsed_uri.params.len<=0) { val->rs = _tr_empty; val->flags = PV_VAL_STR; val->ri = 0; break; } if(_tr_uri_params == NULL) { sv = _tr_parsed_uri.params; if (parse_params(&sv, CLASS_ANY, &phooks, &_tr_uri_params)<0) return -1; } if(tp->type==TR_PARAM_STRING) { sv = tp->v.s; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_STR)) || v.rs.len<=0) { LM_ERR("param cannot get p1\n"); return -1; } sv = v.rs; } for (pit = _tr_uri_params; pit; pit=pit->next) { if (pit->name.len==sv.len && strncasecmp(pit->name.s, sv.s, sv.len)==0) { val->rs = pit->body; goto done; } } val->rs = _tr_empty; break; case TR_URI_HEADERS: val->rs = (_tr_parsed_uri.headers.s)?_tr_parsed_uri.headers: _tr_empty; break; case TR_URI_TRANSPORT: val->rs = (_tr_parsed_uri.transport_val.s)? _tr_parsed_uri.transport_val:_tr_empty; break; case TR_URI_TTL: val->rs = (_tr_parsed_uri.ttl_val.s)? _tr_parsed_uri.ttl_val:_tr_empty; break; case TR_URI_UPARAM: val->rs = (_tr_parsed_uri.user_param_val.s)? _tr_parsed_uri.user_param_val:_tr_empty; break; case TR_URI_MADDR: val->rs = (_tr_parsed_uri.maddr_val.s)? _tr_parsed_uri.maddr_val:_tr_empty; break; case TR_URI_METHOD: val->rs = (_tr_parsed_uri.method_val.s)? _tr_parsed_uri.method_val:_tr_empty; break; case TR_URI_LR: val->rs = (_tr_parsed_uri.lr_val.s)? _tr_parsed_uri.lr_val:_tr_empty; break; case TR_URI_R2: val->rs = (_tr_parsed_uri.r2_val.s)? _tr_parsed_uri.r2_val:_tr_empty; break; case TR_URI_SCHEMA: val->rs.s = _tr_uri.s; /* maximum size of schema can be 4 so the ':' shall be found after * five chars */ val->rs.len = q_memchr(val->rs.s, ':', 5) - val->rs.s; break; default: LM_ERR("unknown subtype %d\n", subtype); return -1; } done: return 0; } /* last via string */ static str _tr_via = {0, 0}; /* the actual len of the allocated buffer (to hold the via) */ static int _tr_via_buf_len = 0; /* holder for the parsed via */ static struct via_body *_tr_parsed_via = 0; int tr_eval_via(struct sip_msg *msg, tr_param_t *tp, int subtype, pv_value_t *val) { pv_value_t v; str sv; struct via_param *pit; // WATCHOUT: need at least 2 chars so \r\n check wont segfault if(val==NULL || (!(val->flags&PV_VAL_STR)) || val->rs.len<=2) return -1; if(_tr_via_buf_len==0 || _tr_via.len!=val->rs.len || strncmp(_tr_via.s, val->rs.s, val->rs.len)!=0 || _tr_parsed_via==0) { if (val->rs.len+4 > _tr_via_buf_len) { if(_tr_via.s) pkg_free(_tr_via.s); _tr_via.s = (char*)pkg_malloc((val->rs.len+4)*sizeof(char)); if(_tr_via.s==NULL) { _tr_via_buf_len = 0; LM_ERR("no more private memory\n"); goto error; } _tr_via_buf_len = val->rs.len+4; } _tr_via.len = val->rs.len; memcpy(_tr_via.s, val->rs.s, val->rs.len); // $hdr PV strips off the terminating CRLR // parse_via wants to parse a full message (including // multiple vias), not just a header line. Fake this _tr_via.s[_tr_via.len+0] = '\r'; _tr_via.s[_tr_via.len+1] = '\n'; _tr_via.s[_tr_via.len+2] = 'A'; // anything other than V _tr_via.s[_tr_via.len+3] = '\0'; /* reset old values */ free_via_list(_tr_parsed_via); if ( (_tr_parsed_via=pkg_malloc(sizeof(struct via_body))) == NULL ) { LM_ERR("no more private memory\n"); goto error; } memset(_tr_parsed_via, 0, sizeof(struct via_body)); parse_via(_tr_via.s, _tr_via.s+_tr_via.len+4, _tr_parsed_via); if(_tr_parsed_via->error != PARSE_OK) { LM_ERR("invalid via [%.*s]\n", val->rs.len, val->rs.s); goto error; } } memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; switch(subtype) { case TR_VIA_NAME: val->rs = (_tr_parsed_via->name.s)?_tr_parsed_via->name:_tr_empty; break; case TR_VIA_VERSION: val->rs = (_tr_parsed_via->version.s)?_tr_parsed_via->version:_tr_empty; break; case TR_VIA_TRANSPORT: val->rs = (_tr_parsed_via->transport.s)?_tr_parsed_via->transport:_tr_empty; break; case TR_VIA_HOST: val->rs = (_tr_parsed_via->host.s)?_tr_parsed_via->host:_tr_empty; break; case TR_VIA_PORT: val->flags |= PV_TYPE_INT|PV_VAL_INT; val->rs = (_tr_parsed_via->port_str.s)?_tr_parsed_via->port_str:_tr_empty; val->ri = _tr_parsed_via->port; break; case TR_VIA_PARAMS: val->rs = (_tr_parsed_via->params.s)?_tr_parsed_via->params:_tr_empty; break; case TR_VIA_COMMENT: val->rs = (_tr_parsed_via->comment.s)?_tr_parsed_via->comment:_tr_empty; break; case TR_VIA_PARAM: // param by name if(tp==NULL) { LM_ERR("param invalid parameters\n"); return -1; } if(_tr_parsed_via->params.len<=0) { val->rs = _tr_empty; val->flags = PV_VAL_STR; val->ri = 0; break; } if(tp->type==TR_PARAM_STRING) { sv = tp->v.s; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_STR)) || v.rs.len<=0) { LM_ERR("param cannot get p1\n"); return -1; } sv = v.rs; } for (pit = _tr_parsed_via->param_lst; pit; pit=pit->next) { if (pit->name.len==sv.len && strncasecmp(pit->name.s, sv.s, sv.len)==0) { val->rs = pit->value; goto done; } } val->rs = _tr_empty; break; case TR_VIA_BRANCH: val->rs = (_tr_parsed_via->branch&&_tr_parsed_via->branch->value.s)?_tr_parsed_via->branch->value: _tr_empty; break; case TR_VIA_RECEIVED: val->rs = (_tr_parsed_via->received&&_tr_parsed_via->received->value.s)?_tr_parsed_via->received->value: _tr_empty; break; case TR_VIA_RPORT: val->rs = (_tr_parsed_via->rport&&_tr_parsed_via->rport->value.s)?_tr_parsed_via->rport->value: _tr_empty; break; default: LM_ERR("unknown subtype %d\n", subtype); return -1; } done: return 0; error: if ( _tr_via.s ) { pkg_free(_tr_via.s); } memset(&_tr_via, 0, sizeof(str)); if ( _tr_parsed_via ) { free_via_list(_tr_parsed_via); _tr_parsed_via = 0; } return -1; } static str _tr_csv_str = {0,0}; static csv_t* _tr_csv_list = NULL; static int init_csv(csv_t **t,char *s,int len) { *t = (csv_t *)pkg_malloc(sizeof(csv_t)); if (*t == NULL) { return -1; } memset(*t,0,sizeof(csv_t)); (*t)->body.s = s; (*t)->body.len = len; return 0; } void free_csv_list(csv_t *list) { csv_t *cit; for (cit=list;cit;cit=cit->next) pkg_free(cit); } static int parse_csv(str *s,csv_t **list) { csv_t *t = NULL; csv_t *last = NULL; char *string,*limit,*aux; int len; if (!s || !list) { LM_ERR("Invalid parameter values\n"); return -1; } last = NULL; *list = 0; if (!s->s) { LM_DBG("empty csv params, skipping\n"); return 0; } LM_DBG("Parsing csv for : [%.*s]\n",s->len,s->s); string = s->s; limit = string+s->len; while (*string) { t = NULL; /* quoted token */ if (*string == '\"') { aux = string+1; search: /* find coresponding quote */ while (*aux != '\"') aux++; if ( *(aux+1) != '\"') { /* end of current token, also skip the following comma */ len = aux-string+1; if (init_csv(&t,string,len) < 0) { LM_ERR("no more memory"); goto error; } string +=len+1; if (string > limit) { /* again, end of string */ if (last) { last->next = t;} else {*list = t;} return 0; } } else { /* quoted string inside token */ aux +=2; /* keep searching for final double quote*/ goto search; } } else { /* non quoted csv , find comma */ aux = strchr(string,','); if (aux == NULL) { len = strlen(string); if (init_csv(&t,string,len) < 0) { LM_ERR("no more memory"); goto error; } /* should be end of string ! */ if (last) { last->next = t;} else {*list = t;} return 0; } else { len = aux - string; if (init_csv(&t,string,len) < 0) { LM_ERR("no more memory"); goto error; } string +=len+1; } } if (last) { last->next = t;} else {*list = t;} last = t; } return 0; error: if (t) pkg_free(t); free_csv_list(*list); *list = NULL; return -1; } int tr_eval_csv(struct sip_msg *msg, tr_param_t *tp,int subtype, pv_value_t *val) { str sv; csv_t *cit=NULL; int n,i,list_size=0; pv_value_t v; if(val==NULL || (!(val->flags&PV_VAL_STR)) || val->rs.len<=0) { return -1; } if(_tr_csv_str.len==0 || _tr_csv_str.len!=val->rs.len || strncmp(_tr_csv_str.s, val->rs.s, val->rs.len)!=0) { if(val->rs.len>_tr_csv_str.len) { if(_tr_csv_str.s) pkg_free(_tr_csv_str.s); _tr_csv_str.s = (char*)pkg_malloc((val->rs.len+1)); if(_tr_csv_str.s==NULL) { LM_ERR("no more private memory\n"); memset(&_tr_csv_str, 0, sizeof(str)); if(_tr_csv_list != NULL) { free_csv_list(_tr_csv_list); _tr_csv_list = 0; } return -1; } } _tr_csv_str.len = val->rs.len; memcpy(_tr_csv_str.s, val->rs.s, val->rs.len); _tr_csv_str.s[_tr_csv_str.len] = '\0'; /* reset old values */ if(_tr_csv_list != NULL) { free_csv_list(_tr_csv_list); _tr_csv_list = 0; } /* parse csv */ sv = _tr_csv_str; if (parse_csv(&sv,&_tr_csv_list)<0) return -1; } if (_tr_csv_list == NULL) return -1; switch(subtype) { case TR_CSV_COUNT: val->ri = 0; for (cit=_tr_csv_list;cit;cit=cit->next) val->ri++; val->rs.s = int2str(val->ri, &val->rs.len); val->flags = PV_VAL_INT | PV_VAL_STR | PV_TYPE_INT; break; case TR_CSV_VALUEAT: if(tp==NULL) { LM_ERR("csv invalid parameters\n"); return -1; } if(tp->type==TR_PARAM_NUMBER) { n = tp->v.n; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_INT))) { LM_ERR("cannot get parameter\n"); return -1; } n = v.ri; } if (n<0) { for (cit=_tr_csv_list;cit;cit=cit->next) list_size++; n = list_size + n; if (n<0) { LM_ERR("Too large negative index\n"); return -1; } } cit = _tr_csv_list; for (i=0;inext; if (!cit) { LM_ERR("Index out of bounds\n"); return -1; } } val->rs = cit->body; val->flags = PV_VAL_STR; break; default: LM_ERR("unknown subtype %d\n",subtype); return -1; } return 0; } static str _tr_sdp_str = {0,0}; int tr_eval_sdp(struct sip_msg *msg, tr_param_t *tp,int subtype, pv_value_t *val) { char *bodylimit; char *answer; char *answerEnd; char searchLine; int entryNo,i; pv_value_t v; if(val==NULL || (!(val->flags&PV_VAL_STR)) || val->rs.len<=0) return -1; if (!tp || !tp->next) return -1; if(_tr_sdp_str.len==0 || _tr_sdp_str.len!=val->rs.len || strncmp(_tr_sdp_str.s, val->rs.s, val->rs.len)!=0) { if(val->rs.len>_tr_sdp_str.len) { if(_tr_sdp_str.s) pkg_free(_tr_sdp_str.s); _tr_sdp_str.s = (char*)pkg_malloc((val->rs.len+1)); if(_tr_sdp_str.s==NULL) { LM_ERR("no more private memory\n"); memset(&_tr_sdp_str, 0, sizeof(str)); return -1; } } _tr_sdp_str.len = val->rs.len; memcpy(_tr_sdp_str.s, val->rs.s, val->rs.len); _tr_sdp_str.s[_tr_sdp_str.len] = '\0'; } switch (subtype) { case TR_SDP_LINEAT: bodylimit = _tr_sdp_str.s + _tr_sdp_str.len; searchLine = *(tp->v.s.s); if(tp->next->type==TR_PARAM_NUMBER) entryNo = tp->next->v.n; else { if(pv_get_spec_value(msg, (pv_spec_p)tp->next->v.data, &v)!=0 || (!(v.flags&PV_VAL_INT))) { LM_ERR("cannot get parameter\n"); return -1; } entryNo = v.ri; } if (entryNo < 0) { LM_ERR("negative index provided for sdp.lineat\n"); return -1; } answer = find_sdp_line(_tr_sdp_str.s, bodylimit, searchLine); if (!answer) { LM_DBG("No such line [%c=]\n", searchLine); return pv_get_null(NULL, NULL, val); } for (i=1;i<=entryNo;i++) { answer = find_next_sdp_line(answer,bodylimit,searchLine,bodylimit); if (!answer || answer == bodylimit) { val->flags = PV_VAL_STR; val->rs.s = ""; val->rs.len = 0; LM_DBG("No such line [%c] nr %d in SDP body. Max fields = %d\n", searchLine,entryNo,i); return 0; } } /* find CR */ answerEnd = strchr(answer,13); if (answerEnd == NULL) { LM_ERR("malformed SDP body\n"); return -1; } val->flags = PV_VAL_STR; val->rs.s = answer; val->rs.len = answerEnd - answer; break; default: LM_ERR("unknown subtype %d\n",subtype); return -1; } return 0; } int tr_eval_ip(struct sip_msg *msg, tr_param_t *tp,int subtype, pv_value_t *val) { char *buffer; struct ip_addr *binary_ip; str inet = str_init("INET"); str inet6 = str_init("INET6"); struct hostent *server; struct ip_addr ip; if(val==NULL || (!(val->flags&PV_VAL_STR)) || val->rs.len<=0) return -1; switch (subtype) { case TR_IP_FAMILY: if (val->rs.len == 4) { memcpy(val->rs.s,inet.s,inet.len); val->rs.len = inet.len; } else if (val->rs.len == 16) { memcpy(val->rs.s,inet6.s,inet6.len); val->rs.len = inet6.len; } else { LM_ERR("Invalid ip address provided for ip.family. Binary format expected !\n"); return -1; } val->flags = PV_VAL_STR; break; case TR_IP_NTOP: if (val->rs.len == 4) ip.af = AF_INET; else if (val->rs.len == 16) ip.af = AF_INET6; else { LM_ERR("Invalid ip address provided for ip.ntop. Binary format expected !\n"); return -1; } memcpy(ip.u.addr,val->rs.s,val->rs.len); ip.len = val->rs.len; buffer = ip_addr2a(&ip); val->rs.s = buffer; val->rs.len = strlen(buffer); val->flags = PV_VAL_STR; break; case TR_IP_ISIP: if(!(val->flags&PV_VAL_STR)) val->rs.s = int2str(val->ri, &val->rs.len); if ( str2ip(&(val->rs)) || str2ip6(&(val->rs)) ) val->ri = 1; else val->ri = 0; val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->rs.s = int2str(val->ri, &val->rs.len); break; case TR_IP_PTON: binary_ip = str2ip(&(val->rs)); if (!binary_ip) { binary_ip = str2ip6(&(val->rs)); if (!binary_ip) { LM_ERR("pton transformation applied to invalid IP\n"); return -1; } } val->rs.s = (char *)binary_ip->u.addr; val->rs.len = binary_ip->len; val->flags = PV_VAL_STR; break; case TR_IP_RESOLVE: val->flags = PV_VAL_STR; server = resolvehost(val->rs.s,0); if (!server || !server->h_addr) { val->rs.s = ""; val->rs.len = 0; return 0; } if (server->h_addrtype == AF_INET) { memcpy(ip.u.addr,server->h_addr,4); ip.len = 4; ip.af = AF_INET; } else if (server->h_addrtype == AF_INET6) { memcpy(ip.u.addr,server->h_addr,16); ip.len = 16; ip.af = AF_INET6; } else { LM_ERR("Unexpected IP address type \n"); val->rs.s = ""; val->rs.len = 0; return 0; } buffer = ip_addr2a(&ip); val->rs.s = buffer; val->rs.len = strlen(buffer); break; default: LM_ERR("unknown subtype %d\n",subtype); return -1; } return 0; } #define RE_MAX_SIZE 1024 static char reg_input_buf[RE_MAX_SIZE]; static struct subst_expr *subst_re = NULL; static char reg_buf[RE_MAX_SIZE]; static int reg_buf_len = -1; int tr_eval_re(struct sip_msg *msg, tr_param_t *tp, int subtype, pv_value_t *val) { int match_no=0; pv_value_t v; str *result; str sv; if(val==NULL || (!(val->flags&PV_VAL_STR)) || val->rs.len<=0) return -1; switch (subtype) { case TR_RE_SUBST: if (tp->type == TR_PARAM_STRING) { sv = tp->v.s; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_STR)) || v.rs.len<=0) { LM_ERR("cannot get value from spec\n"); return -1; } sv = v.rs; } LM_DBG("Trying to apply regexp [%.*s] on : [%.*s]\n", sv.len,sv.s,val->rs.len, val->rs.s); if (reg_buf_len != sv.len || memcmp(reg_buf,sv.s,sv.len) != 0) { LM_DBG("we must compile the regexp\n"); if (subst_re != NULL) { LM_DBG("freeing prev regexp\n"); subst_expr_free(subst_re); } subst_re=subst_parser(&sv); if (subst_re==0) { LM_ERR("Can't compile regexp\n"); return -1; } reg_buf_len = sv.len; memcpy(reg_buf,sv.s,sv.len); } else LM_DBG("yay, we can use the pre-compile regexp\n"); memcpy(reg_input_buf,val->rs.s,val->rs.len); reg_input_buf[val->rs.len]=0; result=subst_str(reg_input_buf, msg, subst_re, &match_no); if (result == NULL) { if (match_no == 0) { LM_DBG("no match for subst expression\n"); break; } else if (match_no < 0) { LM_ERR("subst failed\n"); return -1; } } memcpy(reg_input_buf,result->s,result->len); reg_input_buf[result->len]=0; val->flags = PV_VAL_STR; val->rs.s = reg_input_buf; val->rs.len = result->len; pkg_free(result->s); pkg_free(result); return 0; default: LM_ERR("Unexpected subtype for RE : %d\n",subtype); return -1; } return 0; } static str _tr_params_str = {0, 0}; static param_t* _tr_params_list = NULL; int tr_eval_paramlist(struct sip_msg *msg, tr_param_t *tp, int subtype, pv_value_t *val) { pv_value_t v; str sv; int n, i; param_hooks_t phooks; param_t *pit=NULL; if(val==NULL || (!(val->flags&PV_VAL_STR)) || val->rs.len<=0) return -1; if(_tr_params_str.len==0 || _tr_params_str.len!=val->rs.len || strncmp(_tr_params_str.s, val->rs.s, val->rs.len)!=0) { if(val->rs.len>_tr_params_str.len) { if(_tr_params_str.s) pkg_free(_tr_params_str.s); _tr_params_str.s = (char*)pkg_malloc((val->rs.len+1)*sizeof(char)); if(_tr_params_str.s==NULL) { LM_ERR("no more private memory\n"); memset(&_tr_params_str, 0, sizeof(str)); if(_tr_params_list != NULL) { free_params(_tr_params_list); _tr_params_list = 0; } return -1; } } _tr_params_str.len = val->rs.len; memcpy(_tr_params_str.s, val->rs.s, val->rs.len); _tr_params_str.s[_tr_params_str.len] = '\0'; /* reset old values */ if(_tr_params_list != NULL) { free_params(_tr_params_list); _tr_params_list = 0; } /* parse params */ sv = _tr_params_str; if (parse_params(&sv, CLASS_ANY, &phooks, &_tr_params_list)<0) return -1; } if(_tr_params_list==NULL) return -1; memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; switch(subtype) { case TR_PL_VALUE: if(tp==NULL) { LM_ERR("value invalid parameters\n"); return -1; } if(tp->type==TR_PARAM_STRING) { sv = tp->v.s; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_STR)) || v.rs.len<=0) { LM_ERR("value cannot get p1\n"); return -1; } sv = v.rs; } for (pit = _tr_params_list; pit; pit=pit->next) { if (pit->name.len==sv.len && strncasecmp(pit->name.s, sv.s, sv.len)==0) { val->rs = pit->body; goto done; } } val->rs = _tr_empty; break; case TR_PL_VALUEAT: if(tp==NULL) { LM_ERR("name invalid parameters\n"); return -1; } if(tp->type==TR_PARAM_NUMBER) { n = tp->v.n; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_INT))) { LM_ERR("name cannot get p1\n"); return -1; } n = v.ri; } if(n>=0) { for (pit = _tr_params_list; pit; pit=pit->next) { if(n==0) { val->rs = pit->body; goto done; } n--; } } else { /* ugly hack -- params are in reverse order * - first count then find */ n = -n; n--; i = 0; for (pit = _tr_params_list; pit; pit=pit->next) i++; if(nnext) { if(n==0) { val->rs = pit->body; goto done; } n--; } } } val->rs = _tr_empty; break; case TR_PL_NAME: if(tp==NULL) { LM_ERR("name invalid parameters\n"); return -1; } if(tp->type==TR_PARAM_NUMBER) { n = tp->v.n; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_INT))) { LM_ERR("name cannot get p1\n"); return -1; } n = v.ri; } if(n>=0) { for (pit = _tr_params_list; pit; pit=pit->next) { if(n==0) { val->rs = pit->name; goto done; } n--; } } else { /* ugly hack -- params are in sorted order * - first count then find */ n = -n; n--; i = 0; for (pit = _tr_params_list; pit; pit=pit->next) i++; if(nnext) { if(n==0) { val->rs = pit->name; goto done; } n--; } } } val->rs = _tr_empty; break; case TR_PL_COUNT: val->ri = 0; for (pit = _tr_params_list; pit; pit=pit->next) { val->ri++; } val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->rs.s = int2str(val->ri, &val->rs.len); break; case TR_PL_EXIST: if(tp==NULL) { LM_ERR("value invalid parameters\n"); return -1; } if(tp->type==TR_PARAM_STRING) { sv = tp->v.s; } else { if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v)!=0 || (!(v.flags&PV_VAL_STR)) || v.rs.len<=0) { LM_ERR("value cannot get p1\n"); return -1; } sv = v.rs; } val->ri = 0; for (pit = _tr_params_list; pit; pit=pit->next) { if (pit->name.len==sv.len && strncasecmp(pit->name.s, sv.s, sv.len)==0) { val->ri = 1; break; } } val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->rs.s = int2str(val->ri, &val->rs.len); goto done; default: LM_ERR("unknown subtype %d\n", subtype); return -1; } done: return 0; } static str nameaddr_str = {0, 0}; static struct to_body *nameaddr_to_body = NULL; int tr_eval_nameaddr(struct sip_msg *msg, tr_param_t *tp, int subtype, pv_value_t *val) { struct to_param* topar; if(val==NULL || (!(val->flags&PV_VAL_STR)) || val->rs.len<=0) return -1; LM_DBG("String to transform %.*s\n", val->rs.len, val->rs.s); if(nameaddr_str.len==0 || nameaddr_str.len!=val->rs.len || strncmp(nameaddr_str.s, val->rs.s, val->rs.len)!=0) { /* copy the value in the global variable */ if(val->rs.len+CRLF_LEN > nameaddr_str.len) { if(nameaddr_str.s) pkg_free(nameaddr_str.s); nameaddr_str.s = (char*)pkg_malloc((val->rs.len+CRLF_LEN+1)*sizeof(char)); if(nameaddr_str.s==NULL) { LM_ERR("no more private memory\n"); memset(&nameaddr_str, 0, sizeof(str)); return -1; } } nameaddr_str.len = val->rs.len + CRLF_LEN; memcpy(nameaddr_str.s, val->rs.s, val->rs.len); memcpy(nameaddr_str.s + val->rs.len, CRLF, CRLF_LEN); nameaddr_str.s[nameaddr_str.len] = '\0'; /* reset old values */ if (nameaddr_to_body) { free_to(nameaddr_to_body); nameaddr_to_body = NULL; } /* parse TO hdr + params */ nameaddr_to_body = (struct to_body*)pkg_malloc(sizeof(struct to_body)); if(nameaddr_to_body==NULL) { LM_ERR("no more private memory\n"); /* keep the buffer, but flush the content to force the realloc next time */ nameaddr_str.s[0] = 0; return -1; } parse_to(nameaddr_str.s, nameaddr_str.s + nameaddr_str.len, nameaddr_to_body); } if (nameaddr_to_body->error == PARSE_ERROR) { LM_ERR("Wrong syntax. It must have the To header format\n"); return -1; } memset(val, 0, sizeof(pv_value_t)); val->flags = PV_VAL_STR; switch(subtype) { case TR_NA_URI: val->rs =(nameaddr_to_body->uri.s)?nameaddr_to_body->uri:_tr_empty; break; case TR_NA_LEN: val->flags = PV_TYPE_INT|PV_VAL_INT|PV_VAL_STR; val->ri = nameaddr_to_body->body.len; val->rs.s = int2str(val->ri, &val->rs.len); break; case TR_NA_NAME: val->rs = (nameaddr_to_body->display.s)? nameaddr_to_body->display:_tr_empty; break; case TR_NA_PARAM: if(tp->type != TR_PARAM_STRING) { LM_ERR("Wrong type for parameter, it must string\n"); return -1; } topar = nameaddr_to_body->param_lst; /* search the parameter */ while(topar) { if(topar->name.len == tp->v.s.len && strncmp(topar->name.s, tp->v.s.s, topar->name.len)== 0) break; topar = topar->next; } val->rs = (topar)?topar->value:_tr_empty; break; case TR_NA_PARAMS: topar = nameaddr_to_body->param_lst; if (!topar) { LM_DBG("no params\n"); val->rs = _tr_empty; } else { LM_DBG("We have params\n"); val->rs.s = topar->name.s; if (nameaddr_to_body->last_param->value.s==NULL) { val->rs.len = nameaddr_to_body->last_param->name.s + nameaddr_to_body->last_param->name.len - val->rs.s; } else { val->rs.len = nameaddr_to_body->last_param->value.s + nameaddr_to_body->last_param->value.len - val->rs.s; /* compensate the len if the value of the last param is * a quoted value (include the closing quote in the len) */ if ( (val->rs.s+val->rs.lenrs.s[val->rs.len]=='"' || val->rs.s[val->rs.len]=='\'' ) ) val->rs.len++; } } break; default: LM_ERR("unknown subtype %d\n", subtype); return -1; } return 0; } #define is_in_str(p, in) (ps+in->len && *p) char* parse_transformation(str *in, trans_t **tr) { char *p; char *p0; str tclass; trans_t *t = NULL; trans_t *t0 = NULL; str s; if(in==NULL || in->s==NULL || tr==NULL) return NULL; p = in->s; do { while(is_in_str(p, in) && is_ws(*p)) p++; if(*p != TR_LBRACKET) break; p++; t = (trans_t*)pkg_malloc(sizeof(trans_t)); if(t == NULL) { LM_ERR("no more private memory\n"); return NULL; } memset(t, 0, sizeof(trans_t)); if(t0==NULL) *tr = t; else t0->next = t; t0 = t; /* find transformation class */ tclass.s = p; while(is_in_str(p, in) && *p!=TR_CLASS_MARKER) p++; if(*p!=TR_CLASS_MARKER || tclass.s == p) { LM_ERR("invalid transformation: %.*s (%c)!\n", in->len, in->s, *p); goto error; } tclass.len = p - tclass.s; p++; if(tclass.len==1 && (*tclass.s=='s' || *tclass.s=='S')) { t->type = TR_STRING; t->trf = tr_eval_string; s.s = p; s.len = in->s + in->len - p; p0 = tr_parse_string(&s, t); if(p0==NULL) goto error; p = p0; } else if(tclass.len==3 && strncasecmp(tclass.s, "uri", 3)==0) { t->type = TR_URI; t->trf = tr_eval_uri; s.s = p; s.len = in->s + in->len - p; p0 = tr_parse_uri(&s, t); if(p0==NULL) goto error; p = p0; } else if(tclass.len==3 && strncasecmp(tclass.s, "via", 3)==0) { t->type = TR_VIA; t->trf = tr_eval_via; s.s = p; s.len = in->s + in->len - p; p0 = tr_parse_via(&s, t); if(p0==NULL) goto error; p = p0; } else if(tclass.len==5 && strncasecmp(tclass.s, "param", 5)==0) { t->type = TR_PARAMLIST; t->trf = tr_eval_paramlist; s.s = p; s.len = in->s + in->len - p; p0 = tr_parse_paramlist(&s, t); if(p0==NULL) goto error; p = p0; } else if(tclass.len==8 && strncasecmp(tclass.s, "nameaddr", 8)==0) { t->type = TR_NAMEADDR; t->trf = tr_eval_nameaddr; s.s = p; s.len = in->s + in->len - p; p0 = tr_parse_nameaddr(&s, t); if(p0==NULL) goto error; p = p0; } else if (tclass.len==3 && strncasecmp(tclass.s, "csv", 3) == 0) { t->type = TR_CSV; t->trf = tr_eval_csv; s.s = p; s.len = in->s + in->len - p; p0 = tr_parse_csv(&s,t); if (p0==NULL) goto error; p = p0; } else if (tclass.len==3 && strncasecmp(tclass.s,"sdp",3) == 0) { t->type = TR_SDP; t->trf = tr_eval_sdp; s.s = p; s.len = in->s + in->len - p; p0 = tr_parse_sdp(&s,t); if (p0==NULL) goto error; p = p0; } else if (tclass.len==2 && strncasecmp(tclass.s,"ip",2) == 0) { t->type = TR_IP; t->trf = tr_eval_ip; s.s = p; s.len = in->s + in->len - p; p0 = tr_parse_ip(&s,t); if (p0==NULL) goto error; p = p0; } else if (tclass.len==2 && strncasecmp(tclass.s,"re",2) == 0) { t->type = TR_RE; t->trf = tr_eval_re; s.s = p; s.len = in->s + in->len - p; p0 = tr_parse_re(&s,t); if (p0==NULL) goto error; p = p0; } else { LM_ERR("unknown transformation: [%.*s] in [%.*s]\n", tclass.len, tclass.s, in->len, in->s); goto error; } if(*p != TR_RBRACKET) { LM_ERR("invalid transformation: %.*s | %c !!\n", in->len, in->s, *p); goto error; } p++; if(!is_in_str(p, in)) break; } while(1); return p; error: LM_ERR("error parsing [%.*s]\n", in->len, in->s); t = *tr; while(t) { t0 = t; t = t->next; destroy_transformation(t0); pkg_free(t0); } return NULL; } #define _tr_parse_nparam(_p, _p0, _tp, _spec, _n, _sign, _in, _s) \ while(is_in_str(_p, _in) && is_ws(*(_p))) _p++; \ if(*_p==PV_MARKER) \ { /* pseudo-variable */ \ _spec = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); \ if(_spec==NULL) \ { \ LM_ERR("no more private memory!\n"); \ goto error; \ } \ _s.s = _p; _s.len = _in->s + _in->len - _p; \ _p0 = pv_parse_spec(&_s, _spec); \ if(_p0==NULL) \ { \ LM_ERR("invalid spec in substr transformation: %.*s!\n", \ _in->len, _in->s); \ goto error; \ } \ _p = _p0; \ _tp = (tr_param_t*)pkg_malloc(sizeof(tr_param_t)); \ if(_tp==NULL) \ { \ LM_ERR("no more private memory!\n"); \ goto error; \ } \ memset(_tp, 0, sizeof(tr_param_t)); \ _tp->type = TR_PARAM_SPEC; \ _tp->v.data = (void*)_spec; \ } else { \ if(*_p=='+' || *_p=='-' || (*_p>='0' && *_p<='9')) \ { /* number */ \ _sign = 1; \ if(*_p=='-') { \ _p++; \ _sign = -1; \ } else if(*_p=='+') _p++; \ _n = 0; \ while(is_in_str(_p, _in) && is_ws(*(_p))) \ _p++; \ while(is_in_str(_p, _in) && *_p>='0' && *_p<='9') \ { \ _n = _n*10 + *_p - '0'; \ _p++; \ } \ _tp = (tr_param_t*)pkg_malloc(sizeof(tr_param_t)); \ if(_tp==NULL) \ { \ LM_ERR("no more private memory!\n"); \ goto error; \ } \ memset(_tp, 0, sizeof(tr_param_t)); \ _tp->type = TR_PARAM_NUMBER; \ _tp->v.n = sign*n; \ } else { \ LM_ERR("tinvalid param in transformation: %.*s!!\n", \ _in->len, _in->s); \ goto error; \ } \ } #define tr_parse_sparam(_p, _p0, _tp, _spec, _ps, _in, _s) \ __tr_parse_sparam(_p, _p0, _tp, _spec, _ps, _in, _s, 0) \ /* * Not all transformation string parameters have the same meaning * Some of them are SIP headers, thus they cannot contain whitespace, * while others may just be strings with no additional restrictions. * * Set "skip_param_ws" to 1 if your param may contain inside whitespace * -> e.g. ' ', "foo bar", "foob\tar" ... */ #define __tr_parse_sparam(_p, _p0, _tp, _spec, _ps, _in, _s, skip_param_ws) \ while(is_in_str(_p, _in) && is_ws(*(_p))) _p++; \ if(*_p==PV_MARKER) \ { /* pseudo-variable */ \ _spec = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t)); \ if(_spec==NULL) \ { \ LM_ERR("no more private memory!\n"); \ goto error; \ } \ _s.s = _p; _s.len = _in->s + _in->len - _p; \ _p0 = pv_parse_spec(&_s, _spec); \ if(_p0==NULL) \ { \ LM_ERR("invalid spec in substr transformation: %.*s!\n", \ _in->len, _in->s); \ goto error; \ } \ _p = _p0; \ _tp = (tr_param_t*)pkg_malloc(sizeof(tr_param_t)); \ if(_tp==NULL) \ { \ LM_ERR("no more private memory!\n"); \ goto error; \ } \ memset(_tp, 0, sizeof(tr_param_t)); \ _tp->type = TR_PARAM_SPEC; \ _tp->v.data = (void*)_spec; \ } else { /* string */ \ _ps = _p; \ while(is_in_str(_p, _in) && (skip_param_ws || !is_ws(*_p)) \ && *_p!=TR_PARAM_MARKER && *_p!=TR_RBRACKET) \ _p++; \ if(*_p=='\0') \ { \ LM_ERR("invalid param in transformation: %.*s!!\n", \ _in->len, _in->s); \ goto error; \ } \ _tp = (tr_param_t*)pkg_malloc(sizeof(tr_param_t)); \ if(_tp==NULL) \ { \ LM_ERR("no more private memory!\n"); \ goto error; \ } \ memset(_tp, 0, sizeof(tr_param_t)); \ _tp->type = TR_PARAM_STRING; \ _tp->v.s.s = _ps; \ _tp->v.s.len = _p - _ps; \ } char* tr_parse_string(str* in, trans_t *t) { char *p, *cp; char *p0; char *ps; str name; str s; pv_spec_t *spec = NULL; int n; int sign; tr_param_t *tp = NULL; if(in==NULL || t==NULL) return NULL; p = in->s; name.s = in->s; /* find next token */ while(is_in_str(p, in) && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if(*p=='\0') { LM_ERR("invalid transformation: %.*s\n", in->len, in->s); goto error; } name.len = p - name.s; trim(&name); if(name.len==3 && strncasecmp(name.s, "len", 3)==0) { t->subtype = TR_S_LEN; return p; } else if(name.len==3 && strncasecmp(name.s, "int", 3)==0) { t->subtype = TR_S_INT; return p; } else if(name.len==3 && strncasecmp(name.s, "md5", 3)==0) { t->subtype = TR_S_MD5; return p; } else if(name.len==5 && strncasecmp(name.s, "crc32", 5)==0) { t->subtype = TR_S_CRC32; return p; } else if(name.len==7 && strncasecmp(name.s, "tolower", 7)==0) { t->subtype = TR_S_TOLOWER; return p; } else if(name.len==7 && strncasecmp(name.s, "toupper", 7)==0) { t->subtype = TR_S_TOUPPER; return p; } else if(name.len==11 && strncasecmp(name.s, "encode.hexa", 11)==0) { t->subtype = TR_S_ENCODEHEXA; return p; } else if(name.len==11 && strncasecmp(name.s, "decode.hexa", 11)==0) { t->subtype = TR_S_DECODEHEXA; return p; } else if(name.len==7 && strncasecmp(name.s, "hex2dec", 7)==0) { t->subtype = TR_S_HEX2DEC; return p; } else if(name.len==7 && strncasecmp(name.s, "dec2hex", 7)==0) { t->subtype = TR_S_DEC2HEX; return p; } else if(name.len==13 && strncasecmp(name.s, "escape.common", 13)==0) { t->subtype = TR_S_ESCAPECOMMON; return p; } else if(name.len==15 && strncasecmp(name.s, "unescape.common", 15)==0) { t->subtype = TR_S_UNESCAPECOMMON; return p; } else if(name.len==11 && strncasecmp(name.s, "escape.user", 11)==0) { t->subtype = TR_S_ESCAPEUSER; return p; } else if(name.len==13 && strncasecmp(name.s, "unescape.user", 13)==0) { t->subtype = TR_S_UNESCAPEUSER; return p; } else if(name.len==12 && strncasecmp(name.s, "escape.param", 12)==0) { t->subtype = TR_S_ESCAPEPARAM; return p; } else if(name.len==14 && strncasecmp(name.s, "unescape.param", 14)==0) { t->subtype = TR_S_UNESCAPEPARAM; return p; } else if(name.len==5 && strncasecmp(name.s, "index", 5)==0) { t->subtype = TR_S_INDEX; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid index transformation: %.*s!\n", in->len, in->s); goto error; } p++; tr_parse_sparam(p, p0, tp, spec, ps, in, s); t->params = tp; tp = 0; trim_ws(p); if(*p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) { LM_ERR("invalid index transformation: %.*s!\n", in->len, in->s); goto error; } if (*p!=TR_RBRACKET) { p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s); t->params->next = tp; } else { t->params->next = NULL; } tp = 0; while(is_in_str(p, in) && is_ws(*p)) p++; if(*p!=TR_RBRACKET) { LM_ERR("invalid index transformation: %.*s!!\n", in->len, in->s); goto error; } return p; } else if(name.len==6 && strncasecmp(name.s, "rindex", 6)==0) { t->subtype = TR_S_RINDEX; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid rindex transformation: %.*s!\n", in->len, in->s); goto error; } p++; tr_parse_sparam(p, p0, tp, spec, ps, in, s); t->params = tp; tp = 0; trim_ws(p); if(*p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) { LM_ERR("invalid rindex transformation: %.*s!\n", in->len, in->s); goto error; } if (*p!=TR_RBRACKET) { p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s); t->params->next = tp; } else { t->params->next = NULL; } tp = 0; while(is_in_str(p, in) && is_ws(*p)) p++; if(*p!=TR_RBRACKET) { LM_ERR("invalid rindex transformation: %.*s!!\n", in->len, in->s); goto error; } return p; } else if(name.len==6 && strncasecmp(name.s, "substr", 6)==0) { t->subtype = TR_S_SUBSTR; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid substr transformation: %.*s!\n", in->len, in->s); goto error; } p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s); t->params = tp; tp = 0; trim_ws(p); if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid substr transformation: %.*s!\n", in->len, in->s); goto error; } p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s); if(tp->type==TR_PARAM_NUMBER && tp->v.n<0) { LM_ERR("substr negative offset\n"); goto error; } t->params->next = tp; tp = 0; while(is_in_str(p, in) && is_ws(*p)) p++; if(*p!=TR_RBRACKET) { LM_ERR("invalid substr transformation: %.*s!!\n", in->len, in->s); goto error; } return p; } else if(name.len==6 && strncasecmp(name.s, "select", 6)==0) { t->subtype = TR_S_SELECT; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid select transformation: %.*s!\n", in->len, in->s); goto error; } p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s); t->params = tp; tp = 0; trim_ws(p); if(*p!=TR_PARAM_MARKER || *(p+1)=='\0') { LM_ERR("invalid select transformation: %.*s!\n", in->len, in->s); goto error; } p++; tp = (tr_param_t*)pkg_malloc(sizeof(tr_param_t)); if(tp==NULL) { LM_ERR("no more private memory!\n"); goto error; } memset(tp, 0, sizeof(tr_param_t)); tp->type = TR_PARAM_STRING; tp->v.s.s = p; tp->v.s.len = 1; t->params->next = tp; tp = 0; p++; trim_ws(p); if(*p!=TR_RBRACKET) { LM_ERR("invalid select transformation: %.*s!!\n", in->len, in->s); goto error; } return p; } else if ((name.len==9 && strncasecmp(name.s, "fill.left", 9)==0) || (name.len==10 && strncasecmp(name.s, "fill.right", 10)==0)) { t->subtype = (name.len == 9 ? TR_S_FILL_LEFT : TR_S_FILL_RIGHT); if (*p != TR_PARAM_MARKER) { LM_ERR("invalid fill transformation: %.*s!\n", in->len, in->s); goto error; } p++; __tr_parse_sparam(p, p0, tp, spec, ps, in, s, 1); if (tp->type == TR_PARAM_SPEC) { LM_ERR("fill transformation does not allow PVs: %.*s!\n", in->len, in->s); goto error; } if (tp->v.s.len == 0) { LM_ERR("fill transformation is a NOP, maybe use quotes? %.*s\n", in->len, in->s); goto error; } if (tp->v.s.len > 1) { /* we allowed all whitespace, so manually skip trailing ws */ cp = &tp->v.s.s[tp->v.s.len - 1]; trim_trail_ws(cp); tp->v.s.len -= &tp->v.s.s[tp->v.s.len - 1] - cp; /* support for quoted chars/strings */ if (tp->v.s.len > 1 && ((tp->v.s.s[0] == '\'' && tp->v.s.s[tp->v.s.len - 1] == '\'') || (tp->v.s.s[0] == '\"' && tp->v.s.s[tp->v.s.len - 1] == '\"'))) { if (tp->v.s.len == 2) { LM_ERR("fill transformation is a NOP, maybe use quotes? %.*s\n", in->len, in->s); goto error; } tp->v.s.len -= 2; tp->v.s.s++; } } t->params = tp; tp = 0; trim_ws(p); if (*p != TR_PARAM_MARKER || *(p+1) == '\0') { LM_ERR("invalid fill transformation: %.*s!\n", in->len, in->s); goto error; } p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s); if (tp->type == TR_PARAM_SPEC) { LM_ERR("fill transformation does not allow PVs: %.*s!\n", in->len, in->s); goto error; } t->params->next = tp; tp = 0; while(is_in_str(p, in) && is_ws(*p)) p++; if (*p != TR_RBRACKET) { LM_ERR("invalid fill transformation: %.*s!!\n", in->len, in->s); goto error; } return p; } else if(name.len==5 && strncasecmp(name.s, "width", 5)==0) { t->subtype = TR_S_WIDTH; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid substr transformation: %.*s!\n", in->len, in->s); goto error; } p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s); if(tp->type==TR_PARAM_NUMBER && tp->v.n<0) { LM_ERR("width negative\n"); goto error; } t->params = tp; tp = 0; while(is_in_str(p, in) && (*p==' ' || *p=='\t' || *p=='\n')) p++; if(*p!=TR_RBRACKET) { LM_ERR("invalid width transformation: %.*s!!\n", in->len, in->s); goto error; } return p; } else if(name.len==9 && strncasecmp(name.s, "b64encode", 9)==0) { t->subtype = TR_S_B64ENCODE; return p; } else if(name.len==9 && strncasecmp(name.s, "b64decode", 9)==0) { t->subtype = TR_S_B64DECODE; return p; } else if(name.len==3 && strncasecmp(name.s, "xor", 3)==0) { t->subtype = TR_S_XOR; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid xor transformation: %.*s!\n", in->len, in->s); goto error; } p++; tr_parse_sparam(p, p0, tp, spec, ps, in, s); t->params = tp; tp = 0; trim_ws(p); while(is_in_str(p, in) && is_ws(*p)) p++; if(*p!=TR_RBRACKET) { LM_ERR("invalid xor transformation: %.*s!!\n", in->len, in->s); goto error; } return p; } else if (strncasecmp(name.s, "trim", 4) == 0) { if (name.len == 4) t->subtype = TR_S_TRIM; else if (name.len > 4 && strncasecmp(name.s, "trimr", 5) == 0) t->subtype = TR_S_TRIMR; else if (name.len > 4 && strncasecmp(name.s, "triml", 5) == 0) t->subtype = TR_S_TRIML; else { LM_ERR("bad trim transformation!\n"); goto error; } return p; } LM_ERR("unknown transformation: %.*s/%.*s/%d!\n", in->len, in->s, name.len, name.s, name.len); error: if(tp) free_tr_param(tp); if(spec) pv_spec_free(spec); return NULL; } char* tr_parse_uri(str* in, trans_t *t) { char *p; char *p0; char *ps; str name; str s; pv_spec_t *spec = NULL; tr_param_t *tp = NULL; if(in==NULL || in->s==NULL || t==NULL) return NULL; p = in->s; name.s = in->s; /* find next token */ while(*p && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if(*p=='\0') { LM_ERR("invalid transformation: %.*s\n", in->len, in->s); goto error; } name.len = p - name.s; trim(&name); if(name.len==4 && strncasecmp(name.s, "user", 4)==0) { t->subtype = TR_URI_USER; return p; } else if((name.len==4 && strncasecmp(name.s, "host", 4)==0) || (name.len==6 && strncasecmp(name.s, "domain", 6)==0)) { t->subtype = TR_URI_HOST; return p; } else if(name.len==6 && strncasecmp(name.s, "passwd", 6)==0) { t->subtype = TR_URI_PASSWD; return p; } else if(name.len==4 && strncasecmp(name.s, "port", 4)==0) { t->subtype = TR_URI_PORT; return p; } else if(name.len==6 && strncasecmp(name.s, "params", 6)==0) { t->subtype = TR_URI_PARAMS; return p; } else if(name.len==5 && strncasecmp(name.s, "param", 5)==0) { t->subtype = TR_URI_PARAM; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid param transformation: %.*s\n", in->len, in->s); goto error; } p++; tr_parse_sparam(p, p0, tp, spec, ps, in, s); t->params = tp; trim_ws(p); if(*p!=TR_RBRACKET) { LM_ERR("invalid param transformation: %.*s!\n", in->len, in->s); goto error; } return p; } else if(name.len==9 && strncasecmp(name.s, "transport", 9)==0) { t->subtype = TR_URI_TRANSPORT; return p; } else if(name.len==7 && strncasecmp(name.s, "headers", 7)==0) { t->subtype = TR_URI_HEADERS; return p; } else if(name.len==3 && strncasecmp(name.s, "ttl", 3)==0) { t->subtype = TR_URI_TTL; return p; } else if(name.len==6 && strncasecmp(name.s, "uparam", 6)==0) { t->subtype = TR_URI_UPARAM; return p; } else if(name.len==5 && strncasecmp(name.s, "maddr", 5)==0) { t->subtype = TR_URI_MADDR; return p; } else if(name.len==6 && strncasecmp(name.s, "method", 6)==0) { t->subtype = TR_URI_METHOD; return p; } else if(name.len==2 && strncasecmp(name.s, "lr", 2)==0) { t->subtype = TR_URI_LR; return p; } else if(name.len==2 && strncasecmp(name.s, "r2", 2)==0) { t->subtype = TR_URI_R2; return p; } else if (name.len==6 && strncasecmp(name.s, "schema", 6)==0) { t->subtype = TR_URI_SCHEMA; return p; } LM_ERR("unknown transformation: %.*s/%.*s!\n", in->len, in->s, name.len, name.s); error: if(spec) pv_spec_free(spec); return NULL; } char* tr_parse_via(str* in, trans_t *t) { char *p; char *p0; char *ps; str name; str s; pv_spec_t *spec = NULL; tr_param_t *tp = NULL; if(in==NULL || in->s==NULL || t==NULL) return NULL; p = in->s; name.s = in->s; /* find next token */ while(*p && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if(*p=='\0') { LM_ERR("invalid transformation: %.*s\n", in->len, in->s); goto error; } name.len = p - name.s; trim(&name); if(name.len==4 && strncasecmp(name.s, "name", 4)==0) { t->subtype = TR_VIA_NAME; return p; } else if(name.len==7 && strncasecmp(name.s, "version", 7)==0) { t->subtype = TR_VIA_VERSION; return p; } else if(name.len==9 && strncasecmp(name.s, "transport", 9)==0) { t->subtype = TR_VIA_TRANSPORT; return p; } else if((name.len==4 && strncasecmp(name.s, "host", 4)==0) || (name.len==6 && strncasecmp(name.s, "domain", 6)==0)) { t->subtype = TR_VIA_HOST; return p; } else if(name.len==4 && strncasecmp(name.s, "port", 4)==0) { t->subtype = TR_VIA_PORT; return p; } else if(name.len==6 && strncasecmp(name.s, "params", 6)==0) { t->subtype = TR_VIA_PARAMS; return p; } else if(name.len==5 && strncasecmp(name.s, "param", 5)==0) { t->subtype = TR_VIA_PARAM; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid param transformation: %.*s\n", in->len, in->s); goto error; } p++; tr_parse_sparam(p, p0, tp, spec, ps, in, s); t->params = tp; trim_ws(p); if(*p!=TR_RBRACKET) { LM_ERR("invalid param transformation: %.*s!\n", in->len, in->s); goto error; } return p; } else if(name.len==7 && strncasecmp(name.s, "comment", 7)==0) { t->subtype = TR_VIA_COMMENT; return p; } else if(name.len==6 && strncasecmp(name.s, "branch", 6)==0) { t->subtype = TR_VIA_BRANCH; return p; } else if(name.len==8 && strncasecmp(name.s, "received", 8)==0) { t->subtype = TR_VIA_RECEIVED; return p; } else if(name.len==5 && strncasecmp(name.s, "rport", 5)==0) { t->subtype = TR_VIA_RPORT; return p; } LM_ERR("unknown transformation: %.*s/%.*s!\n", in->len, in->s, name.len, name.s); error: if(spec) pv_spec_free(spec); return NULL; } char* tr_parse_paramlist(str* in, trans_t *t) { char *p; char *p0; char *ps; str s; str name; int n; int sign; pv_spec_t *spec = NULL; tr_param_t *tp = NULL; if(in==NULL || in->s==NULL || t==NULL) return NULL; p = in->s; name.s = in->s; /* find next token */ while(is_in_str(p, in) && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if(*p=='\0') { LM_ERR("invalid transformation: %.*s\n", in->len, in->s); goto error; } name.len = p - name.s; trim(&name); if(name.len==5 && strncasecmp(name.s, "value", 5)==0) { t->subtype = TR_PL_VALUE; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid value transformation: %.*s\n", in->len, in->s); goto error; } p++; tr_parse_sparam(p, p0, tp, spec, ps, in, s); t->params = tp; trim_ws(p); if(*p!=TR_RBRACKET) { LM_ERR("invalid value transformation: %.*s!\n", in->len, in->s); goto error; } return p; } else if(name.len==5 && strncasecmp(name.s, "exist", 5)==0) { t->subtype = TR_PL_EXIST; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid value transformation: %.*s\n", in->len, in->s); goto error; } p++; tr_parse_sparam(p, p0, tp, spec, ps, in, s); t->params = tp; trim_ws(p); if(*p!=TR_RBRACKET) { LM_ERR("invalid value transformation: %.*s!\n", in->len, in->s); goto error; } return p; } else if(name.len==7 && strncasecmp(name.s, "valueat", 7)==0) { t->subtype = TR_PL_VALUEAT; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid name transformation: %.*s\n", in->len, in->s); goto error; } p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s) t->params = tp; while(is_in_str(p, in) && is_ws(*p)) p++; if(*p!=TR_RBRACKET) { LM_ERR("invalid name transformation: %.*s!\n", in->len, in->s); goto error; } return p; } else if(name.len==4 && strncasecmp(name.s, "name", 4)==0) { t->subtype = TR_PL_NAME; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid name transformation: %.*s\n", in->len, in->s); goto error; } p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s) t->params = tp; while(is_in_str(p, in) && is_ws(*p)) p++; if(*p!=TR_RBRACKET) { LM_ERR("invalid name transformation: %.*s!\n", in->len, in->s); goto error; } return p; } else if(name.len==5 && strncasecmp(name.s, "count", 5)==0) { t->subtype = TR_PL_COUNT; return p; } LM_ERR("unknown transformation: %.*s/%.*s!\n", in->len, in->s, name.len, name.s); error: if(spec) pv_spec_free(spec); return NULL; } char* tr_parse_nameaddr(str* in, trans_t *t) { char *p; str name; char *p0; char *ps; str s; pv_spec_t *spec = NULL; tr_param_t *tp = NULL; if(in==NULL || t==NULL) return NULL; p = in->s; name.s = in->s; /* find next token */ while(is_in_str(p, in) && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if(*p=='\0') { LM_ERR("invalid transformation: %.*s\n", in->len, in->s); goto error; } name.len = p - name.s; trim(&name); if(name.len==3 && strncasecmp(name.s, "uri", 3)==0) { t->subtype = TR_NA_URI; return p; } else if(name.len==3 && strncasecmp(name.s, "len", 3)==0) { t->subtype = TR_NA_LEN; return p; } else if(name.len==4 && strncasecmp(name.s, "name", 4)==0) { t->subtype = TR_NA_NAME; return p; } else if(name.len==5 && strncasecmp(name.s, "param", 5)==0) { t->subtype = TR_NA_PARAM; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid value transformation: %.*s\n", in->len, in->s); goto error; } p++; tr_parse_sparam(p, p0, tp, spec, ps, in, s); t->params = tp; tp = 0; trim_ws(p); if(*p!=TR_RBRACKET) { LM_ERR("invalid value transformation: %.*s!\n", in->len, in->s); goto error; } return p; } else if(name.len==6 && strncasecmp(name.s, "params", 6)==0) { t->subtype = TR_NA_PARAMS; return p; } LM_ERR("unknown transformation: %.*s/%.*s/%d!\n", in->len, in->s, name.len, name.s, name.len); error: return NULL; } char * tr_parse_csv(str *in, trans_t *t) { char *p; str name; pv_spec_t *spec = NULL; tr_param_t *tp = NULL; char *p0; str s; int n; int sign; if (in == NULL || t == NULL) return NULL; p = in->s; name.s = in->s; /* find next token */ while (is_in_str(p,in) && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if (*p == '\0') { LM_ERR("invalid transformation: %.*s\n",in->len,in->s); return NULL; } name.len = p - name.s; trim(&name); if (name.len==5 && strncasecmp(name.s,"count",5)==0) { t->subtype = TR_CSV_COUNT; return p; } else if (name.len==5 && strncasecmp(name.s,"value",5)==0) { t->subtype = TR_CSV_VALUEAT; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid name transformation: %.*s\n", in->len, in->s); goto error; } p++; _tr_parse_nparam(p, p0, tp, spec, n, sign, in, s) t->params = tp; tp = 0; while(is_in_str(p, in) && is_ws(*p)) p++; if(*p!=TR_RBRACKET) { LM_ERR("invalid name transformation: %.*s!\n", in->len, in->s); goto error; } return p; } LM_ERR("unknown transformation: %.*s/%.*s/%d!\n", in->len, in->s, name.len, name.s, name.len); error: return NULL; } char * tr_parse_sdp(str *in, trans_t *t) { char *p; char *p0; char *ps; str name; pv_spec_t *spec = NULL; tr_param_t *tp = NULL; str s; int n; int sign; if (in == NULL || t == NULL) return NULL; p = in->s; name.s = in->s; /* find next token */ while (is_in_str(p,in) && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if (*p == '\0') { LM_ERR("invalid transformation: %.*s\n",in->len,in->s); return NULL; } name.len = p - name.s; trim(&name); if (name.len==4 && strncasecmp(name.s,"line",4)==0) { t->subtype = TR_SDP_LINEAT; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid lineat transformation: %.*s!\n", in->len, in->s); goto error; } p++; tr_parse_sparam(p, p0, tp, spec,ps, in, s); t->params = tp; tp = 0; trim_ws(p); if(*p!=TR_PARAM_MARKER) { /* lineat has only one parameter */ tp = (tr_param_t*)pkg_malloc(sizeof(tr_param_t)); if (!tp) { LM_ERR("no more pkg memory\n"); goto error; } memset(tp, 0, sizeof(tr_param_t)); tp->type = TR_PARAM_NUMBER; tp->v.n = 0; t->params->next = tp; LM_DBG("sdp.lineat with only one parameter. default = 1\n"); return p; } p++; if (spec) { pkg_free(spec); spec = NULL; } _tr_parse_nparam(p, p0, tp, spec,n,sign, in, s); if(tp->type==TR_PARAM_NUMBER && tp->v.n<0) { LM_ERR("lineat negative argument\n"); goto error; } t->params->next = tp; tp = 0; while(is_in_str(p, in) && is_ws(*p)) p++; if(*p!=TR_RBRACKET) { LM_ERR("invalid lineat transformation: %.*s!!\n", in->len, in->s); goto error; } return p; } LM_ERR("unknown transformation: %.*s/%.*s/%d!\n", in->len, in->s, name.len, name.s, name.len); error: return NULL; } char * tr_parse_ip(str *in, trans_t *t) { char *p; str name; if (in == NULL || t == NULL) return NULL; p = in->s; name.s = in->s; /* find next token */ while (is_in_str(p,in) && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if (*p == '\0') { LM_ERR("invalid transformation: %.*s\n",in->len,in->s); goto error; } name.len = p - name.s; trim(&name); if (name.len==6 && strncasecmp(name.s,"family",6)==0) { t->subtype = TR_IP_FAMILY; return p; } else if (name.len==4 && strncasecmp(name.s,"ntop",4)==0) { t->subtype = TR_IP_NTOP; return p; } else if (name.len == 4 && strncasecmp(name.s,"isip",4) == 0) { t->subtype = TR_IP_ISIP; return p; } else if (name.len == 4 && strncasecmp(name.s,"pton",4) == 0) { t->subtype = TR_IP_PTON; return p; } else if (name.len == 7 && strncasecmp(name.s,"resolve",7) == 0) { t->subtype = TR_IP_RESOLVE; return p; } LM_ERR("unknown transformation: %.*s/%.*s/%d!\n", in->len, in->s, name.len, name.s, name.len); error: return NULL; } char* tr_parse_re(str *in,trans_t *t) { char *p,*p0,*ps; str name,s; pv_spec_t *spec = NULL; tr_param_t *tp = NULL; if (in == NULL || t == NULL) return NULL; p = in->s; name.s = in->s; /* find next token */ while (is_in_str(p,in) && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if (*p == '\0') { LM_ERR("invalid transformation: %.*s\n",in->len,in->s); goto error; } name.len = p - name.s; trim(&name); if (name.len==5 && strncasecmp(name.s,"subst",5)==0) { t->subtype = TR_RE_SUBST; if(*p!=TR_PARAM_MARKER) { LM_ERR("invalid value transformation: %.*s\n", in->len, in->s); goto error; } p++; LM_INFO("preparing to parse param\n"); tr_parse_sparam(p, p0, tp, spec, ps, in, s); t->params = tp; tp = 0; trim_ws(p); if(*p!=TR_RBRACKET) { LM_ERR("invalid value transformation: %.*s!\n", in->len, in->s); goto error; } return p; } LM_ERR("unknown transformation: %.*s/%.*s/%d!\n", in->len, in->s, name.len, name.s, name.len); error: return NULL; } void destroy_transformation(trans_t *t) { tr_param_t *tp; tr_param_t *tp0; if(t==NULL) return; tp = t->params; while(tp) { tp0 = tp; tp = tp->next; free_tr_param(tp0); } memset(t, 0, sizeof(trans_t)); } void free_transformation(trans_t *t) { trans_t *t0; while(t) { t0 = t; t = t->next; destroy_transformation(t0); pkg_free(t0); } } void free_tr_param(tr_param_t *tp) { tr_param_t *tp0; if(tp==NULL) return; while(tp) { tp0 = tp; tp = tp->next; if(tp0->type==TR_PARAM_SPEC) pv_spec_free((pv_spec_t*)tp0->v.data); pkg_free(tp0); } } opensips-2.2.2/transformations.h000066400000000000000000000072321300170765700167710ustar00rootroot00000000000000/* * Copyright (C) 2007 voice-system.ro * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /*! \file * \brief Transformations support */ #ifndef _TRANSFORMATIONS_H_ #define _TRANSFORMATIONS_H_ #include "usr_avp.h" #include "pvar.h" #define TR_LBRACKET_STR "{" #define TR_LBRACKET '{' #define TR_RBRACKET_STR "}" #define TR_RBRACKET '}' #define TR_CLASS_MARKER '.' #define TR_PARAM_MARKER ',' enum _tr_type { TR_NONE=0, TR_STRING, TR_URI, TR_PARAMLIST, TR_NAMEADDR, TR_CSV, TR_SDP,TR_IP, TR_VIA, TR_RE }; enum _tr_s_subtype { TR_S_NONE=0, TR_S_LEN, TR_S_INT, TR_S_MD5, TR_S_SUBSTR, TR_S_SELECT, TR_S_ENCODEHEXA, TR_S_DECODEHEXA, TR_S_HEX2DEC, TR_S_DEC2HEX, TR_S_ESCAPECOMMON, TR_S_UNESCAPECOMMON, TR_S_ESCAPEUSER, TR_S_UNESCAPEUSER, TR_S_ESCAPEPARAM, TR_S_UNESCAPEPARAM, TR_S_TOLOWER, TR_S_TOUPPER, TR_S_CRC32, TR_S_INDEX, TR_S_RINDEX, TR_S_FILL_LEFT, TR_S_FILL_RIGHT, TR_S_WIDTH, TR_S_B64ENCODE, TR_S_B64DECODE, TR_S_XOR, TR_S_TRIM, TR_S_TRIMR, TR_S_TRIML }; enum _tr_uri_subtype { TR_URI_NONE=0, TR_URI_USER, TR_URI_HOST, TR_URI_PASSWD, TR_URI_PORT, TR_URI_PARAMS, TR_URI_PARAM, TR_URI_HEADERS, TR_URI_TRANSPORT, TR_URI_TTL, TR_URI_UPARAM, TR_URI_MADDR, TR_URI_METHOD, TR_URI_LR, TR_URI_R2, TR_URI_SCHEMA }; enum _tr_via_subtype { TR_VIA_NONE=0, TR_VIA_NAME, TR_VIA_VERSION, TR_VIA_TRANSPORT, TR_VIA_HOST, TR_VIA_PORT, TR_VIA_PARAMS, TR_VIA_PARAM, TR_VIA_COMMENT, TR_VIA_BRANCH, TR_VIA_RECEIVED, TR_VIA_RPORT }; enum _tr_param_subtype { TR_PL_NONE=0, TR_PL_VALUE, TR_PL_VALUEAT, TR_PL_NAME, TR_PL_COUNT, TR_PL_EXIST }; enum _tr_nameaddr_subtype { TR_NA_NONE=0, TR_NA_NAME, TR_NA_URI, TR_NA_LEN, TR_NA_PARAM, TR_NA_PARAMS }; enum _tr_param_type { TR_PARAM_NONE=0, TR_PARAM_STRING, TR_PARAM_NUMBER, TR_PARAM_SPEC }; enum _tr_csv_subtype {TR_CSV_NONE=0, TR_CSV_COUNT,TR_CSV_VALUEAT}; enum _tr_sdp_subtype {TR_SDP_NONE=0, TR_SDP_LINEAT}; enum _tr_ip_subtype {TR_IP_NONE=0,TR_IP_FAMILY,TR_IP_NTOP,TR_IP_RESOLVE, TR_IP_ISIP,TR_IP_PTON}; enum _tr_re_subtype {TR_RE_NONE=0,TR_RE_SUBST}; typedef struct tr_param_ { int type; union { int n; str s; void *data; } v; struct tr_param_ *next; } tr_param_t, *tr_param_p; typedef int (*tr_func_t) (struct sip_msg *, tr_param_t*, int, pv_value_t*); typedef struct trans_ { str name; int type; int subtype; tr_func_t trf; tr_param_t *params; struct trans_ *next; } trans_t, *trans_p; int run_transformations(struct sip_msg *msg, trans_t *tr, pv_value_t *val); char* parse_transformation(str *in, trans_t **tr); char* tr_parse_string(str* in, trans_t *t); char* tr_parse_uri(str* in, trans_t *t); char* tr_parse_via(str* in, trans_t *t); char* tr_parse_paramlist(str* in, trans_t *t); char* tr_parse_nameaddr(str* in, trans_t *t); char* tr_parse_csv(str *in,trans_t *t); char* tr_parse_sdp(str *in,trans_t *t); char* tr_parse_ip(str *in,trans_t *t); char* tr_parse_re(str *in,trans_t *t); void destroy_transformation(trans_t *t); void free_transformation(trans_t *t); void free_tr_param(tr_param_t *tp); #endif opensips-2.2.2/trim.h000066400000000000000000000053641300170765700145170ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TRIM_H #define TRIM_H #include "str.h" /* whitespace */ #define is_ws(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\t') /* * trim leading ws * * Input: (char *) */ #define trim_ws(p) while (*(p) && is_ws(*(p))) p++ /* * trim trailing ws * * Input: (char *) */ #define trim_trail_ws(p) while (*(p) && is_ws(*(p))) p-- /* * This switch-case statement is used in * trim_leading and trim_trailing. You can * define characters that should be skipped * here. */ #define TRIM_SWITCH(c) switch(c) { \ case ' ': \ case '\t': \ case '\r': \ case '\n': \ break; \ \ default: \ return; \ } /*! \brief * Remove any leading whitechars, like spaces, * horizontal tabs, carriage returns and line * feeds * * WARNING: String descriptor structure will be * modified ! Make a copy otherwise you * might be unable to free _s->s for * example ! * */ static inline void trim_leading(str* _s) { for(; _s->len > 0; _s->len--, _s->s++) { TRIM_SWITCH(*(_s->s)); } } /*! \brief * Remove any trailing white char, like spaces, * horizontal tabs, carriage returns and line feeds * * WARNING: String descriptor structure will be * modified ! Make a copy otherwise you * might be unable to free _s->s for * example ! */ static inline void trim_trailing(str* _s) { for(; _s->len > 0; _s->len--) { TRIM_SWITCH(_s->s[_s->len - 1]); } } /*! \brief * Do trim_leading and trim_trailing * * WARNING: String structure will be modified ! * Make a copy otherwise you might be * unable to free _s->s for example ! */ static inline void trim(str* _s) { trim_leading(_s); trim_trailing(_s); } #endif /* TRIM_H */ opensips-2.2.2/tsend.c000066400000000000000000000116211300170765700146450ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * send with timeout for stream and datagram sockets * * History: * -------- * 2004-02-26 created by andrei * 2003-03-03 switched to heavy macro use, added tsend_dgram_ev (andrei) */ #include #include #include #include #include #include #include "dprint.h" /* the functions below are very similar => some generic macros */ #define TSEND_INIT \ int n; \ struct pollfd pf; \ pf.fd=fd; \ pf.events=POLLOUT #define TSEND_POLL(f_name) \ poll_loop: \ while(1){ \ n=poll(&pf, 1, timeout); \ if (n<0){ \ if (errno==EINTR) continue; /* signal, ignore */ \ LM_ERR(f_name ": poll failed: %s [%d]\n", \ strerror(errno), errno); \ goto error; \ }else if (n==0){ \ /* timeout */ \ LM_ERR(f_name ": send timeout (%d)\n", timeout); \ goto error; \ } \ if (pf.revents&POLLOUT){ \ /* we can write again */ \ goto again; \ }else if (pf.revents&(POLLERR|POLLHUP|POLLNVAL)){ \ LM_ERR(f_name ": bad poll flags %x\n", \ pf.revents); \ goto error; \ } \ /* if POLLIN or POLLPRI or other non-harmful events happened, \ * continue ( although poll should never signal them since we're \ * not interested in them => we should never reach this point) */ \ } #define TSEND_ERR_CHECK(f_name)\ if (n<0){ \ if (errno==EINTR) goto again; \ else if (errno!=EAGAIN && errno!=EWOULDBLOCK){ \ LM_ERR(f_name ": failed to send: (%d) %s\n", \ errno, strerror(errno)); \ goto error; \ }else goto poll_loop; \ } /*! \brief sends on fd (which must be O_NONBLOCK); if it cannot send any data * in timeout milliseconds it will return ERROR * \return -1 on error, or number of bytes written * (if less than len => couldn't send all) * bugs: signals will reset the timer */ int tsend_stream(int fd, char* buf, unsigned int len, int timeout) { int written; TSEND_INIT; written=0; again: n=send(fd, buf, len, #ifdef HAVE_MSG_NOSIGNAL MSG_NOSIGNAL #else 0 #endif ); TSEND_ERR_CHECK("tsend_stream"); written+=n; if ((unsigned int)n couldn't send all) * bugs: signals will reset the timer */ int tsend_dgram(int fd, char* buf, unsigned int len, const struct sockaddr* to, socklen_t tolen, int timeout) { TSEND_INIT; again: n=sendto(fd, buf, len, 0, to, tolen); TSEND_ERR_CHECK("tsend_dgram"); /* we don't care about partial writes: they shouldn't happen on * a datagram socket */ return n; TSEND_POLL("tsend_datagram"); error: return -1; } /*! \brief sends on connected datagram fd (which must be O_NONBLOCK); * if it cannot send any data in timeout milliseconds it will return ERROR * \return -1 on error, or number of bytes written * (if less than len => couldn't send all) * bugs: signals will reset the timer */ int tsend_dgram_ev(int fd, const struct iovec* v, int count, int timeout) { TSEND_INIT; again: n=writev(fd, v, count); TSEND_ERR_CHECK("tsend_datagram_ev"); return n; TSEND_POLL("tsend_datagram_ev"); error: return -1; } /*! \brief writes a vector on fd (which must be O_NONBLOCK); if it cannot * send any data in timeout milliseconds it will return ERROR * \return -1 on error, or number of bytes written * (if less than len => couldn't send all) * bugs: signals will reset the timer */ int tsend_stream_ev(int fd, struct iovec *iov, int iovcnt, int timeout) { int written; int i, len = 0; TSEND_INIT; for (i = 0; i < iovcnt; i++) len += iov[i].iov_len; written=0; i = 0; again: n=writev(fd, &iov[i], iovcnt); TSEND_ERR_CHECK("tsend_stream"); written+=n; if ((unsigned int)n iov[i].iov_len) { i++; n -= iov[i].iov_len; iovcnt--; } }else{ /* successful full write */ return written; } TSEND_POLL("tsend_stream"); error: return -1; } opensips-2.2.2/tsend.h000066400000000000000000000023551300170765700146560ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2004-02-26 created by andrei */ #ifndef __tsend_h #define __tsend_h int tsend_stream(int fd, char* buf, unsigned int len, int timeout); int tsend_dgram(int fd, char* buf, unsigned int len, const struct sockaddr* to, socklen_t tolen, int timeout); int tsend_stream_ev(int fd, const struct iovec *iov, int iovcnt, int timeout); int tsend_dgram_ev(int fd, const struct iovec* v, int count, int timeout); #endif opensips-2.2.2/usr_avp.c000066400000000000000000000267721300170765700152240ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * --------- * 2004-07-21 created (bogdan) * 2004-10-09 interface more flexible - more function available (bogdan) * 2004-11-07 AVP string values are kept 0 terminated (bogdan) * 2004-11-14 global aliases support added (bogdan) */ #include #include #include #include #include "sr_module.h" #include "dprint.h" #include "str.h" #include "ut.h" #include "mem/shm_mem.h" #include "mem/mem.h" #include "usr_avp.h" #include "locking.h" #include "map.h" static gen_lock_t *extra_lock; static struct usr_avp *global_avps = 0; static struct usr_avp **crt_avps = &global_avps; static map_t avp_map = 0; static map_t avp_map_shm = 0; static int last_avp_index = 0; /* it is also used to indicate that the extra AVPs that are stored in the * shared memory have been initialized */ static int *last_avp_index_shm = 0; #define p2int(_p) (int)(unsigned long)(_p) #define int2p(_i) (void *)(unsigned long)(_i) int init_global_avps(void) { /* initialize map for static avps */ avp_map = map_create(0); if (!avp_map) { LM_ERR("cannot create avp_map\n"); return -1; } return 0; } int init_extra_avps(void) { extra_lock = lock_alloc(); if (!extra_lock) { LM_ERR("cannot allocate lock\n"); return -1; } if (!lock_init(extra_lock)) { LM_ERR("cannot init lock\n"); return -1; } last_avp_index_shm = shm_malloc(sizeof(int)); if (!last_avp_index_shm) { LM_ERR("not enough shm mem\n"); return -1; } *last_avp_index_shm = last_avp_index; /* initialize map for dynamic avps */ avp_map_shm = map_create(AVLMAP_SHARED); if (!avp_map_shm) { LM_ERR("cannot create shared avp_map\n"); return -1; } return 0; } struct usr_avp* new_avp(unsigned short flags, int id, int_str val) { struct usr_avp *avp; str *s; int len; assert( crt_avps!=0 ); if (id < 0) { LM_ERR("invalid AVP name!\n"); goto error; } /* compute the required mem size */ len = sizeof(struct usr_avp); if (flags & AVP_VAL_STR) len += sizeof(str)-sizeof(void*) + (val.s.len+1); avp = (struct usr_avp*)shm_malloc( len ); if (avp==0) { LM_ERR("no more shm mem\n"); goto error; } avp->flags = flags; avp->id = id ; if (flags & AVP_VAL_STR) { /* avp type ID, str value */ s = (str*)(void*)&(avp->data); s->len = val.s.len; s->s = (char*)s + sizeof(str); memcpy( s->s, val.s.s , s->len); s->s[s->len] = 0; } else if (flags & AVP_VAL_NULL) { avp->data = NULL; } else { avp->data = (void *)(long)val.n; } return avp; error: return NULL; } int add_avp(unsigned short flags, int name, int_str val) { struct usr_avp* avp; avp = new_avp(flags, name, val); if(avp == NULL) { LM_ERR("Failed to create new avp structure\n"); return -1; } avp->next = *crt_avps; *crt_avps = avp; return 0; } int add_avp_last(unsigned short flags, int name, int_str val) { struct usr_avp* avp; struct usr_avp* last_avp; avp = new_avp(flags, name, val); if(avp == NULL) { LM_ERR("Failed to create new avp structure\n"); return -1; } /* get end of the list */ for( last_avp=*crt_avps ; last_avp && last_avp->next ; last_avp=last_avp->next); if (last_avp==NULL) { avp->next = *crt_avps; *crt_avps = last_avp = avp; } else { last_avp->next = avp; avp->next = NULL; last_avp = avp; } return 0; } struct usr_avp *search_index_avp(unsigned short flags, int name, int_str *val, unsigned int index) { struct usr_avp *avp = NULL; while ( (avp=search_first_avp( flags, name, 0, avp))!=0 ) { if( index == 0 ){ return avp; } index--; } return 0; } int replace_avp(unsigned short flags, int name, int_str val, int index) { struct usr_avp* avp, *avp_prev; struct usr_avp* avp_new, *avp_del; if(index < 0) { LM_ERR("Index with negative value\n"); return -1; } avp_del = search_index_avp(flags, name, 0, index); if(avp_del == NULL) { LM_DBG("AVP to replace not found\n"); return -1; } avp_new = new_avp(flags, name, val); if(avp_new == NULL) { LM_ERR("Failed to create new avp structure\n"); return -1; } for( avp_prev=0,avp=*crt_avps ; avp ; avp_prev=avp,avp=avp->next ) { if (avp==avp_del) { if (avp_prev) avp_prev->next=avp_new; else *crt_avps = avp_new; avp_new->next = avp_del->next; shm_free(avp_del); return 0; } } return 0; } /* get name functions */ static inline str* __get_avp_name(int id, map_t m) { map_iterator_t it; int **idp; if (map_first(m, &it) < 0) { LM_ERR("map doesn't exist\n"); return NULL; } for (;;) { if (!iterator_is_valid(&it)) return NULL; idp = (int**)iterator_val(&it); if (!idp) { LM_ERR("[BUG] while getting avp name\n"); return NULL; } if (p2int(*idp) == id) return iterator_key(&it); if (iterator_next(&it) < 0) return NULL; } } inline str* get_avp_name_id(int id) { str *name; if (id < 0) return NULL; name = __get_avp_name(id, avp_map); /* search extra galiases */ if (name) return name; lock_get(extra_lock); name = __get_avp_name(id, avp_map_shm); lock_release(extra_lock); return name; } inline str* get_avp_name(struct usr_avp *avp) { return get_avp_name_id(avp->id); } /* get value functions */ inline void get_avp_val(struct usr_avp *avp, int_str *val) { void *data; if (avp==0 || val==0) return; if (avp->flags & AVP_VAL_STR) { /* avp type ID, str value */ data = (void*)&avp->data; val->s = *((str*)data); } else { /* avp type ID, int value */ val->n = (long)(avp->data); } } struct usr_avp** get_avp_list(void) { assert( crt_avps!=0 ); return crt_avps; } /* search functions */ inline static struct usr_avp *internal_search_ID_avp( struct usr_avp *avp, int id, unsigned short flags) { for( ; avp ; avp=avp->next ) { if ( id==avp->id && (flags==0 || (flags&avp->flags))) { return avp; } } return 0; } /** * search first avp beginning with 'start->next' * if start==NULL, beging from head of avp list */ struct usr_avp *search_first_avp( unsigned short flags, int id, int_str *val, struct usr_avp *start) { struct usr_avp *head; struct usr_avp *avp; if (id < 0) { LM_ERR("invalid avp id %d\n", id); return 0; } if(start==0) { assert( crt_avps!=0 ); if (*crt_avps==0) return 0; head = *crt_avps; } else { if(start->next==0) return 0; head = start->next; } /* search for the AVP by ID (&name) */ avp = internal_search_ID_avp(head, id, flags&AVP_SCRIPT_MASK); /* get the value - if required */ if (avp && val) get_avp_val(avp, val); return avp; } struct usr_avp *search_next_avp( struct usr_avp *avp, int_str *val ) { if (avp==0 || avp->next==0) return 0; avp = internal_search_ID_avp( avp->next, avp->id, avp->flags&AVP_SCRIPT_MASK ); if (avp && val) get_avp_val(avp, val); return avp; } /********* free functions ********/ void destroy_avp( struct usr_avp *avp_del) { struct usr_avp *avp; struct usr_avp *avp_prev; for( avp_prev=0,avp=*crt_avps ; avp ; avp_prev=avp,avp=avp->next ) { if (avp==avp_del) { if (avp_prev) avp_prev->next=avp->next; else *crt_avps = avp->next; shm_free(avp); return; } } } int destroy_avps( unsigned short flags, int name, int all) { struct usr_avp *avp; int n; n = 0; while ( (avp=search_first_avp( flags, name, 0, 0))!=0 ) { destroy_avp( avp ); n++; if ( !all ) break; } return n; } void destroy_index_avp( unsigned short flags, int name, int index) { struct usr_avp *avp = NULL; avp = search_index_avp(flags, name, 0, index); if(avp== NULL) { LM_DBG("AVP with the specified index not found\n"); return; } destroy_avp( avp ); } void destroy_avp_list_unsafe( struct usr_avp **list ) { struct usr_avp *avp, *foo; avp = *list; while( avp ) { foo = avp; avp = avp->next; shm_free_unsafe( foo ); } *list = 0; } inline void destroy_avp_list( struct usr_avp **list ) { struct usr_avp *avp, *foo; LM_DBG("destroying list %p\n", *list); avp = *list; while( avp ) { foo = avp; avp = avp->next; shm_free( foo ); } *list = 0; } void reset_avps(void) { assert( crt_avps!=0 ); if ( crt_avps!=&global_avps) { crt_avps = &global_avps; } destroy_avp_list( crt_avps ); } struct usr_avp** set_avp_list( struct usr_avp **list ) { struct usr_avp **foo; assert( crt_avps!=0 ); foo = crt_avps; crt_avps = list; return foo; } static inline int __search_avp_map(str *alias, map_t m) { int **id = (int **)map_find(m, *alias); LM_DBG("looking for [%.*s] avp %s - found %d\n", alias->len, alias->s, m == avp_map_shm ? "in shm": "", id ? p2int(*id) : -1); return id ? p2int(*id) : -1; } static int lookup_avp_alias_str(str *alias, int extra) { int id; if (!alias || !alias->len || !alias->s) return -2; id = __search_avp_map(alias, avp_map); if (id < 0 && extra) { /* search extra alias */ lock_get(extra_lock); id = __search_avp_map(alias, avp_map_shm); lock_release(extra_lock); } return id; } static inline int new_avp_alias(str *alias) { int id = last_avp_index + 1; if (map_put(avp_map, *alias, int2p(id))) { LM_WARN("[BUG] Value should have already be found [%.*s]\n", alias->len, alias->s); return -1; } /* successfully added avp */ last_avp_index++; LM_DBG("added alias %.*s with id %d\n",alias->len,alias->s,id); return id; } static inline int new_avp_extra_alias(str *alias) { int id; if (!last_avp_index_shm) { LM_ERR("extra AVPs are not initialized yet\n"); return -1; } /* check if last avp is valid */ lock_get(extra_lock); id = (*last_avp_index_shm) + 1; if (map_put(avp_map_shm, *alias, int2p(id))) { LM_WARN("[BUG] Value should have already be found [%.*s]\n", alias->len, alias->s); return -1; } (*last_avp_index_shm)++; lock_release(extra_lock); LM_DBG("added extra alias %.*s with id %d\n",alias->len,alias->s,id); return id; } int parse_avp_spec( str *name, int *avp_name) { int id, extra; if (name==0 || name->s==0 || name->len==0) return -1; extra = last_avp_index_shm ? 1 : 0; if (name->len > 2 && name->s[1] == AVP_NAME_DELIM && (name->s[0] == 'i' || name->s[0] == 's')) LM_WARN("Deprecated AVP name format \"%.*s\" - use \"%.*s\" instead\n", name->len, name->s, name->len - 2, name->s + 2); id = lookup_avp_alias_str(name, extra); if (id < 0) { id = extra ? new_avp_extra_alias(name) : new_avp_alias(name); if (id < 0) { LM_ERR("cannot add new avp\n"); return -1; } } if (avp_name) *avp_name = id; return 0; } int get_avp_id(str *name) { int id; if (parse_avp_spec(name, &id)) { LM_ERR("unable to get id\n"); return -1; } return id; } struct usr_avp *clone_avp_list(struct usr_avp *old) { struct usr_avp *a; int_str val; if (!old) return NULL; /* create a copy of the old AVP */ get_avp_val( old, &val ); a = new_avp( old->flags, old->id, val); if (a==NULL) { LM_ERR("cloning failed, trunking the list\n"); return NULL; } a->next = clone_avp_list(old->next); return a; } opensips-2.2.2/usr_avp.h000066400000000000000000000071741300170765700152240ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * TODO (by bogdan) * ---------------- * 1) int_str -> (int,str) * 2) avp is double linked list (faster at delete and insert) * * History: * --------- * 2004-07-21 created (bogdan) * 2004-11-14 global aliases support added (bogdan) * 2005-02-14 list with FLAGS USAGE added (bogdan) */ #ifndef _SER_URS_AVP_H_ #define _SER_URS_AVP_H_ /* * LIST with the allocated flags, their meaning and owner * [0-7] - internal flags; [8-15] - to be used by script * * flag no. owner description * ------------------------------------------------------- * 0 avp_core avp has a string name * 1 avp_core avp has a string value * 2 core contact avp qvalue change * 7 avpops module avp was loaded from DB * */ #include "str.h" typedef union { int n; str s; } int_str; struct usr_avp { int id; unsigned short flags; struct usr_avp *next; void *data; }; #define AVP_NAME_DELIM ':' #define AVP_NAME_VALUE_MASK 0x0003 #define AVP_CORE_MASK 0x00ff #define AVP_SCRIPT_MASK 0xff00 #define avp_core_flags(f) ((f)&0x00ff) #define avp_script_flags(f) (((f)<<8)&0xff00) #define avp_get_script_flags(f) (((f)&0xff00)>>8) #define AVP_NAME_STR (1<<0) #define AVP_VAL_STR (1<<1) #define AVP_VAL_NULL (1<<2) #define is_avp_str_name(a) (a->flags&AVP_NAME_STR) #define is_avp_str_val(a) (a->flags&AVP_VAL_STR) #define GALIAS_CHAR_MARKER '$' /* init functions */ int init_global_avps(); int init_extra_avps(); struct usr_avp* new_avp(unsigned short flags, int name, int_str val); struct usr_avp *clone_avp_list(struct usr_avp *old); /* add functions */ int add_avp( unsigned short flags, int id, int_str val); int add_avp_last( unsigned short flags, int id, int_str val); /* search functions */ struct usr_avp *search_first_avp( unsigned short flags, int id, int_str *val, struct usr_avp *start); struct usr_avp *search_next_avp( struct usr_avp *avp, int_str *val ); struct usr_avp *search_index_avp(unsigned short flags, int name, int_str *val, unsigned int index); /* free functions */ void reset_avps( ); void destroy_avp( struct usr_avp *avp); void destroy_index_avp( unsigned short flags, int name, int index); int destroy_avps( unsigned short flags, int name, int all); void destroy_avp_list( struct usr_avp **list ); void destroy_avp_list_unsafe( struct usr_avp **list ); /* get func */ void get_avp_val(struct usr_avp *avp, int_str *val ); str* get_avp_name(struct usr_avp *avp); str* get_avp_name_id(int id); struct usr_avp** set_avp_list( struct usr_avp **list ); struct usr_avp** get_avp_list( ); /* replace function */ int replace_avp(unsigned short flags, int name, int_str val, int index); /* global alias functions (manipulation and parsing)*/ int get_avp_id(str *alias); int parse_avp_spec( str *name, int *avp_name); #endif opensips-2.2.2/ut.c000066400000000000000000000145271300170765700141700ustar00rootroot00000000000000/* * - various general purpose functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History * ------ * 2006-09-25 created by movind user2uid and group2gid from main.c (bogdan) */ #include #include #include #include "ut.h" char int2str_buf[INT2STR_MAX_LEN]; /* make a null-termianted copy of the given string (in STR format) into * a static local buffer * !!IMPORTANT!! sequential calls do overwrite the previous values. */ char * NTcopy_str( str *s ) { static char *p=NULL; static unsigned int len = 0; if (p!=NULL) { if ( len < s->len+1 ) { p = pkg_realloc( p , s->len+1 ); if (p==NULL) { LM_ERR("no more pkg mem (%d)\n", s->len+1); return NULL; } len = s->len+1; } } else { p = pkg_malloc(s->len+1); if (p==NULL) { LM_ERR("no more pkg mem (%d)\n", s->len+1); return NULL; } len = s->len+1; } memcpy( p , s->s, s->len); p[s->len] = 0; return p; } /* converts a username into uid:gid, * returns -1 on error & 0 on success */ int user2uid(int* uid, int* gid, char* user) { char* tmp; struct passwd *pw_entry; if (user){ *uid=strtol(user, &tmp, 10); if ((tmp==0) ||(*tmp)){ /* maybe it's a string */ pw_entry=getpwnam(user); if (pw_entry==0){ goto error; } *uid=pw_entry->pw_uid; if (gid) *gid=pw_entry->pw_gid; } return 0; } error: return -1; } int group2gid(int* gid, char* group) { char* tmp; struct group *gr_entry; if (group){ *gid=strtol(group, &tmp, 10); if ((tmp==0) ||(*tmp)){ /* maybe it's a string */ gr_entry=getgrnam(group); if (gr_entry==0){ goto error; } *gid=gr_entry->gr_gid; } return 0; } error: return -1; } /* utility function to give each children a unique seed */ void seed_child(unsigned int seed) { srand(seed); } int parse_reply_codes( str *options_reply_codes_str, int **options_reply_codes, int *options_codes_no) { str code_str; unsigned int code; int index= 0; char* sep1, *sep2, *aux; *options_reply_codes = (int*)pkg_malloc( options_reply_codes_str->len/3 * sizeof(int)); if(*options_reply_codes== NULL) { LM_ERR("no more memory\n"); return -1; } sep1 = options_reply_codes_str->s; sep2 = strchr(options_reply_codes_str->s, ','); while(sep2 != NULL) { aux = sep2; while(*sep1 == ' ') sep1++; sep2--; while(*sep2 == ' ') sep2--; code_str.s = sep1; code_str.len = sep2-sep1+1; if(str2int(&code_str, &code)< 0) { LM_ERR("Bad format - not am integer [%.*s]\n", code_str.len, code_str.s); return -1; } if(code<100 ||code > 700) { LM_ERR("Wrong number [%d]- must be a valid SIP reply code\n",code); return -1; } (*options_reply_codes)[index] = code; index++; sep1 = aux +1; sep2 = strchr(sep1, ','); } while(*sep1 == ' ') sep1++; sep2 = options_reply_codes_str->s+options_reply_codes_str->len -1; while(*sep2 == ' ') sep2--; code_str.s = sep1; code_str.len = sep2 -sep1 +1; if(str2int(&code_str, &code)< 0) { LM_ERR("Bad format - not am integer [%.*s]\n", code_str.len, code_str.s); return -1; } if(code<100 ||code > 700) { LM_ERR("Wrong number [%d]- must be a valid SIP reply code\n", code); return -1; } (*options_reply_codes)[index] = code; index++; *options_codes_no = index; return 0; } static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; #define BAD ((unsigned char)-1) static const unsigned char base64val[] = { BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD, 62, BAD,BAD,BAD, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,BAD,BAD, BAD,BAD,BAD,BAD, BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,BAD, BAD,BAD,BAD,BAD, BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,BAD, BAD,BAD,BAD,BAD }; #define DECODE64(c) (isascii(c) ? base64val[c] : BAD) /* function that encodes to base64 * output buffer is assumed to have the right length */ void base64encode(unsigned char *out, unsigned char *in, int inlen) { for (; inlen >= 3; inlen -= 3) { *out++ = base64digits[in[0] >> 2]; *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; *out++ = base64digits[in[2] & 0x3f]; in += 3; } if (inlen > 0) { unsigned char fragment; *out++ = base64digits[in[0] >> 2]; fragment = (in[0] << 4) & 0x30; if (inlen > 1) fragment |= in[1] >> 4; *out++ = base64digits[fragment]; *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c]; *out++ = '='; } } /* function that decodes from base64 * output buffer is assumed to have the right length */ int base64decode(unsigned char *out,unsigned char *in,int len) { int i=0; unsigned char c1,c2,c3,c4; int out_len=0; while (len > i) { do { c1 = base64val[in[i++] & 0xFF]; } while (i> 4); do { c3 = in[i++] & 0xFF; if (c3 == 61) return out_len; c3 = base64val[c3]; } while (i> 2); do { c4 = in[i++] & 0xFF; if (c4 == 61) return out_len; c4 = base64val[c4]; } while (i #include #include #include #include #include #include "config.h" #include "dprint.h" #include "sr_module.h" #include "action.h" #include "str.h" #include "evi/evi_modules.h" #include "evi/evi_core.h" #include "mem/mem.h" #include "mem/shm_mem.h" struct sip_msg; /* zero-string wrapper */ #define ZSW(_c) ((_c)?(_c):"") /* str initialization */ #define str_init(_string) {_string, sizeof(_string) - 1} /* returns string beginning and length without insignificant chars */ #define trim_len( _len, _begin, _mystr ) \ do{ static char _c; \ (_len)=(_mystr).len; \ while ((_len) && ((_c=(_mystr).s[(_len)-1])==0 || _c=='\r' || \ _c=='\n' || _c==' ' || _c=='\t' )) \ (_len)--; \ (_begin)=(_mystr).s; \ while ((_len) && ((_c=*(_begin))==' ' || _c=='\t')) { \ (_len)--;\ (_begin)++; \ } \ }while(0) #define trim_r( _mystr ) \ do{ static char _c; \ while( ((_mystr).len) && ( ((_c=(_mystr).s[(_mystr).len-1]))==0 ||\ _c=='\r' || _c=='\n' ) \ ) \ (_mystr).len--; \ }while(0) /* right and left space trimming */ #define trim_spaces_lr(_s_) \ do{\ for(;(_s_).s[(_s_).len-1]==' ';(_s_).s[--(_s_).len]=0);\ for(;(_s_).s[0]==' ';(_s_).s=(_s_).s+1,(_s_).len--);\ }while(0); /* right and left space trimming without '0' padding */ #define str_trim_spaces_lr(_s_) \ do{\ for(;(_s_).s[(_s_).len-1]==' ';--(_s_).len);\ for(;(_s_).s[0]==' ';(_s_).s=(_s_).s+1,(_s_).len--);\ }while(0); #define translate_pointer( _new_buf , _org_buf , _p) \ ( (_p)?(_new_buf + (_p-_org_buf)):(0) ) #define via_len(_via) \ ((_via)->bsize-((_via)->name.s-\ ((_via)->hdr.s+(_via)->hdr.len))) #define PTR_STRING_SIZE 2+16+1 #define PTR_STR_SIZE 2+16 /* char to hex conversion table */ static char fourbits2char[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /* converts a str to an u. short, returns the u. short and sets *err on * error and if err!=null */ static inline unsigned short str2s(const char* s, unsigned int len, int *err) { unsigned short ret; int i; unsigned char *limit; unsigned char *str; /*init*/ str=(unsigned char*)s; ret=i=0; limit=str+len; for(;str= '0') ){ ret=ret*10+*str-'0'; i++; if (i>5) goto error_digits; }else{ //error unknown char goto error_char; } } if (err) *err=0; return ret; error_digits: LM_DBG("too many letters in [%.*s]\n", (int)len, s); if (err) *err=1; return 0; error_char: LM_DBG("unexpected char %c in %.*s\n", *str, (int)len, s); if (err) *err=1; return 0; } static inline int btostr( char *p, unsigned char val) { unsigned int a,b,i =0; if ( (a=val/100)!=0 ) *(p+(i++)) = a+'0'; /*first digit*/ if ( (b=val%100/10)!=0 || a) *(p+(i++)) = b+'0'; /*second digit*/ *(p+(i++)) = '0'+val%10; /*third digit*/ return i; } /* 2^64~= 16*10^18 => 19+1+1 sign + digits + \0 */ #define INT2STR_MAX_LEN (1+19+1+1) /* INTeger-TO-Buffer-STRing : convers an unsigned long to a string * IMPORTANT: the provided buffer must be at least INT2STR_MAX_LEN size !! */ static inline char* int2bstr(unsigned long l, char *s, int* len) { int i; i=INT2STR_MAX_LEN-2; s[INT2STR_MAX_LEN-1]=0; /* null terminate */ do{ s[i]=l%10+'0'; i--; l/=10; }while(l && (i>=0)); if (l && (i<0)){ LM_CRIT("overflow error\n"); } if (len) *len=(INT2STR_MAX_LEN-2)-i; return &s[i+1]; } /* INTeger-TO-STRing : convers an unsigned long to a string * returns a pointer to a static buffer containing l in asciiz & sets len */ extern char int2str_buf[INT2STR_MAX_LEN]; static inline char* int2str(unsigned long l, int* len) { return int2bstr( l, int2str_buf, len); } /* Signed INTeger-TO-STRing: convers a long to a string * returns a pointer to a static buffer containing l in asciiz & sets len */ static inline char* sint2str(long l, int* len) { int sign; char *p; sign = 0; if(l<0) { sign = 1; l = -l; } p = int2str((unsigned long)l, len); if(sign) { *(--p) = '-'; if (len) (*len)++; } return p; } /* faster memchr version */ static inline char* q_memchr(char* p, int c, unsigned int size) { char* end; end=p+size; for(;p=p;cursor--){ if (*cursor==(unsigned char)c) return cursor; } return NULL; } inline static int reverse_hex2int( char *c, int len ) { char *pc; int r; char mychar; r=0; for (pc=c+len-1; len>0; pc--, len--) { r <<= 4 ; mychar=*pc; if ( mychar >='0' && mychar <='9') r+=mychar -'0'; else if (mychar >='a' && mychar <='f') r+=mychar -'a'+10; else if (mychar >='A' && mychar <='F') r+=mychar -'A'+10; else return -1; } return r; } inline static int int2reverse_hex( char **c, int *size, unsigned int nr ) { unsigned short digit; if (*size && nr==0) { **c = '0'; (*c)++; (*size)--; return 1; } while (*size && nr ) { digit = nr & 0xf ; **c= digit >= 10 ? digit + 'a' - 10 : digit + '0'; nr >>= 4; (*c)++; (*size)--; } return nr ? -1 /* number not processed; too little space */ : 1; } /* same functions, higher representation 64 bit*/ /* if unsafe requested when first non numerical character shall be * met the number shall be returned; avoid giving the * exact len of the number */ inline static int64_t reverse_hex2int64( char *c, int len, int unsafe) { char *pc; int64_t r; char mychar; r=0; for (pc=c+len-1; len>0; pc--, len--) { r <<= 4 ; mychar=*pc; if ( mychar >='0' && mychar <='9') r+=mychar -'0'; else if (mychar >='a' && mychar <='f') r+=mychar -'a'+10; else if (mychar >='A' && mychar <='F') r+=mychar -'A'+10; else if (unsafe) return r; else return -1; } return r; } inline static int64_t int64_2reverse_hex( char **c, int *size, uint64_t nr ) { unsigned short digit; if (*size && nr==0) { **c = '0'; (*c)++; (*size)--; return 1; } while (*size && nr ) { digit = nr & 0xf ; **c= digit >= 10 ? digit + 'a' - 10 : digit + '0'; nr >>= 4; (*c)++; (*size)--; } return nr ? -1 /* number not processed; too little space */ : 1; } inline static int hexstr2int(char *c, int len, unsigned int *val) { char *pc; int r; char mychar; r=0; for (pc=c; pc='0' && mychar <='9') r+=mychar -'0'; else if (mychar >='a' && mychar <='f') r+=mychar -'a'+10; else if (mychar >='A' && mychar <='F') r+=mychar -'A'+10; else return -1; } *val = r; return 0; } /* double output length assumed ; does NOT zero-terminate */ inline static int string2hex( /* input */ unsigned char *str, int len, /* output */ char *hex ) { int orig_len; if (len==0) { *hex='0'; return 1; } orig_len=len; while ( len ) { *hex=fourbits2char[((*str) >> 4) & 0x0f]; hex++; *hex=fourbits2char[(*str) & 0x0f]; hex++; len--; str++; } return orig_len-len; } /* portable sleep in microseconds (no interrupt handling now) */ inline static void sleep_us( unsigned int nusecs ) { struct timeval tval; tval.tv_sec=nusecs/1000000; tval.tv_usec=nusecs%1000000; select(0, NULL, NULL, NULL, &tval ); } /* portable determination of max_path */ inline static int pathmax(void) { #ifdef PATH_MAX static int pathmax=PATH_MAX; #else static int pathmax=0; #endif if (pathmax==0) { /* init */ pathmax=pathconf("/", _PC_PATH_MAX); pathmax=(pathmax<=0)?PATH_MAX_GUESS:pathmax+1; } return pathmax; } inline static int hex2int(char hex_digit) { if (hex_digit>='0' && hex_digit<='9') return hex_digit-'0'; if (hex_digit>='a' && hex_digit<='f') return hex_digit-'a'+10; if (hex_digit>='A' && hex_digit<='F') return hex_digit-'A'+10; /* no valid hex digit ... */ LM_ERR("'%c' is no hex char\n", hex_digit ); return -1; } /* Un-escape URI user -- it takes a pointer to original user str, as well as the new, unescaped one, which MUST have an allocated buffer linked to the 'str' structure ; (the buffer can be allocated with the same length as the original string -- the output string is always shorter (if escaped characters occur) or same-long as the original one). only printable characters are permitted <0 is returned on an unescaping error, length of the unescaped string otherwise */ inline static int un_escape(str *user, str *new_user ) { int i, j, value; int hi, lo; if( new_user==0 || new_user->s==0) { LM_CRIT("called with invalid param\n"); return -1; } new_user->len = 0; j = 0; for (i = 0; i < user->len; i++) { if (user->s[i] == '%') { if (i + 2 >= user->len) { LM_ERR("escape sequence too short in" " '%.*s' @ %d\n", user->len, user->s, i ); goto error; } hi=hex2int(user->s[i + 1]); if (hi<0) { LM_ERR(" non-hex high digit in an escape" " sequence in '%.*s' @ %d\n", user->len, user->s, i+1 ); goto error; } lo=hex2int(user->s[i + 2]); if (lo<0) { LM_ERR("non-hex low digit in an escape sequence in " "'%.*s' @ %d\n", user->len, user->s, i+2 ); goto error; } value=(hi<<4)+lo; if (value < 32 || value > 126) { LM_ERR("non-ASCII escaped character in '%.*s' @ %d\n", user->len, user->s, i ); goto error; } new_user->s[j] = value; i+=2; /* consume the two hex digits, for cycle will move to the next char */ } else { new_user->s[j] = user->s[i]; } j++; /* good -- we translated another character */ } new_user->len = j; return j; error: new_user->len = j; return -1; } /* * Convert a string to lower case */ static inline void strlower(str* _s) { int i; for(i = 0; i < _s->len; i++) { _s->s[i] = tolower(_s->s[i]); } } /* * Convert a str into integer */ static inline int str2int(str* _s, unsigned int* _r) { int i; if (_s==0 || _s->s == 0 || _s->len == 0 || _r == 0) return -1; *_r = 0; for(i = 0; i < _s->len; i++) { if ((_s->s[i] >= '0') && (_s->s[i] <= '9')) { *_r *= 10; *_r += _s->s[i] - '0'; } else { return -1; } } return 0; } /* * Convert a str into signed integer */ static inline int str2sint(str* _s, int* _r) { int i; int s; if (_s==0 || _s->s == 0 || _s->len == 0 || _r == 0) return -1; *_r = 0; s = 1; i=0; if(_s->s[i]=='-') { s=-1; i++; } for(; i < _s->len; i++) { if ((_s->s[i] >= '0') && (_s->s[i] <= '9')) { *_r *= 10; *_r += _s->s[i] - '0'; } else { //Preserve sign for partially converted strings *_r *= s; return -1; } } *_r *= s; return 0; } /* * Convert a str (base 10 or 16) into integer */ static inline int strno2int( str *val, unsigned int *mask ) { /* hexa or decimal*/ if (val->len>2 && val->s[0]=='0' && val->s[1]=='x') { return hexstr2int( val->s+2, val->len-2, mask); } else { return str2int( val, mask); } } /* * Make a copy of a str structure using shm_malloc */ static inline int shm_str_dup(str* dst, const str* src) { dst->s = shm_malloc(src->len); if (!dst->s) { LM_ERR("no shared memory left\n"); return -1; } memcpy(dst->s, src->s, src->len); dst->len = src->len; return 0; } /* * Make a copy of an str structure using shm_malloc * + an additional '\0' byte, so you can make use of dst->s */ static inline int shm_nt_str_dup(str* dst, const str* src) { dst->s = shm_malloc(src->len + 1); if (!dst->s) { LM_ERR("no shared memory left\n"); return -1; } memcpy(dst->s, src->s, src->len); dst->len = src->len; dst->s[dst->len] = '\0'; return 0; } /* * Make a copy of a str structure using pkg_malloc */ static inline int pkg_str_dup(str* dst, const str* src) { dst->s = pkg_malloc(src->len); if (dst->s==NULL) { LM_ERR("no private memory left\n"); return -1; } memcpy(dst->s, src->s, src->len); dst->len = src->len; return 0; } /* * compare two str's */ static inline int str_strcmp(const str *stra, const str *strb) { int i; int alen; int blen; int minlen; if(stra==NULL || strb==NULL || stra->s ==NULL || strb->s==NULL || stra->len<0 || strb->len<0) { LM_ERR("bad parameters\n"); return -2; } alen = stra->len; blen = strb->len; minlen = (alen < blen ? alen : blen); for (i = 0; i < minlen; i++) { const char a = stra->s[i]; const char b = strb->s[i]; if (a < b) return -1; if (a > b) return 1; } if (alen < blen) return -1; else if (alen > blen) return 1; else return 0; } /* * search strb in stra */ static inline char* str_strstr(const str *stra, const str *strb) { int i; int len; if (stra==NULL || strb==NULL || stra->s==NULL || strb->s==NULL || stra->len<=0 || strb->len<=0) { LM_ERR("bad parameters\n"); return NULL; } if (strb->len > stra->len) { LM_ERR("string to find should be smaller than the string" "to search into\n"); return NULL; } len=0; while (stra->len-len >= strb->len){ if (stra->s[len] != strb->s[0]) { len++; continue; } for (i=1; ilen; i++) if (stra->s[len+i]!=strb->s[i]) { len++; break; } if (i != strb->len) continue; return stra->s+len; } return NULL; } /* * case-insensitive compare n chars of two str's */ static inline int str_strncasecmp(const str *stra, const str *strb, int n) { int i; if(stra==NULL || strb==NULL || stra->s ==NULL || strb->s==NULL || stra->len<0 || strb->len<0) { LM_ERR("bad parameters\n"); return -2; } if (stra->lenlens[i]); const char b = tolower(strb->s[i]); if (a < b) return -1; if (a > b) return 1; } return 0; } /* * case-insensitive compare two str's */ static inline int str_strcasecmp(const str *stra, const str *strb) { int i; int alen; int blen; int minlen; if(stra==NULL || strb==NULL || stra->s ==NULL || strb->s==NULL || stra->len<0 || strb->len<0) { LM_ERR("bad parameters\n"); return -2; } alen = stra->len; blen = strb->len; minlen = (alen < blen ? alen : blen); for (i = 0; i < minlen; i++) { const char a = tolower(stra->s[i]); const char b = tolower(strb->s[i]); if (a < b) return -1; if (a > b) return 1; } if (alen < blen) return -1; else if (alen > blen) return 1; else return 0; } #define start_expire_timer(begin,threshold) \ do { \ if ((threshold)) \ gettimeofday(&(begin), NULL); \ } while(0) \ #define stop_expire_timer(begin,threshold,func_info,extra_s,extra_len,tcp) \ do { \ if ((threshold)) \ log_expiry(get_time_diff(&(begin)),(threshold),(func_info),(extra_s),(extra_len),tcp); \ } while(0) int tcp_timeout_con_get; int tcp_timeout_receive_fd; int tcp_timeout_send; #define reset_tcp_vars(threshold) \ do { \ if (threshold) \ { \ tcp_timeout_con_get=0; \ tcp_timeout_receive_fd=0; \ tcp_timeout_send=0; \ } \ } while(0) #define get_time_difference(begin,threshold,tcp_dbg) \ do { \ if ((threshold)) \ tcp_dbg = get_time_diff(&(begin)); \ } while(0) static inline int get_time_diff(struct timeval *begin) { struct timeval end; long seconds,useconds,mtime; gettimeofday(&end,NULL); seconds = end.tv_sec - begin->tv_sec; useconds = end.tv_usec - begin->tv_usec; mtime = ((seconds) * 1000000 + useconds); return mtime; } #define reset_longest_action_list(threshold) \ do { \ if ((threshold)) { \ min_action_time=0; \ memset(longest_action,0,LONGEST_ACTION_SIZE*sizeof(action_time)); \ } \ } while (0) static inline void log_expiry(int time_diff,int expire, const char *func_info,char *extra_dbg,int dbg_len,int tcp) { str param; evi_params_p list; static str func_str = str_init("source"); static str time_str = str_init("time"); static str extra_str = str_init("extra"); int i; if (time_diff > expire) { if (tcp) { LM_WARN("threshold exceeded : tcp took too long : " "con_get=%d, rcv_fd=%d, send=%d. Source : %.*s\n", tcp_timeout_con_get,tcp_timeout_receive_fd, tcp_timeout_send,dbg_len,extra_dbg); time_diff = tcp_timeout_send + tcp_timeout_receive_fd + tcp_timeout_con_get; } else LM_WARN("threshold exceeded : %s took too long - %d us." "Source : %.*s\n",func_info,time_diff,dbg_len,extra_dbg); if (memcmp(func_info,"msg",3) == 0) { for (i=0;itype == MODULE_T) LM_WARN("#%i is a module action : %s - %dus - line %d\n",i+1, ((cmd_export_t*)(longest_action[i].a->elem[0].u.data))->name, longest_action[i].a_time,longest_action[i].a->line); else LM_WARN("#%i is a core action : %d - %dus - line %d\n",i+1, longest_action[i].a->type, longest_action[i].a_time,longest_action[i].a->line); } } } if (evi_probe_event(EVI_THRESHOLD_ID)) { param.s = (char *)func_info; param.len = strlen(func_info); if (!(list = evi_get_params())) return; if (evi_param_add_str(list, &func_str, ¶m)) { LM_ERR("unable to add func parameter\n"); goto error; } if (evi_param_add_int(list, &time_str, &time_diff)) { LM_ERR("unable to add time parameter\n"); goto error; } param.s = extra_dbg; param.len = dbg_len; if (evi_param_add_str(list, &extra_str, ¶m)) { LM_ERR("unable to add extra parameter\n"); goto error; } if (evi_raise_event(EVI_THRESHOLD_ID, list)) { LM_ERR("unable to send event\n"); } } else { LM_DBG("no event raised\n"); } } return; error: evi_free_params(list); } static inline int get_timestamp(int *sec,int *usec) { struct timeval t; if (gettimeofday(&t,NULL) != 0) { LM_ERR("failed to get time of day\n"); return -1; } *sec = t.tv_sec; *usec = t.tv_usec; return 0; } /* * checks if the string is a token as defined in rfc3261 * returns: * -1 - if the string is invalid * 1 - if the string is a token * 0 - not a token */ static inline int str_check_token( str * in) { char *p; if (!in || !in->s || !in->len) return -1; p = in->s + in->len; while (p > in->s) { p--; if (!( /* alphanum */ (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || /* other */ *p == '-' || *p == '.' || *p == '!' || *p == '%' || *p == '*' || *p == '_' || *p == '+' || *p == '`' || *p == '\'' || *p == '~' )) return 0; } return 1; } int user2uid(int* uid, int* gid, char* user); int group2gid(int* gid, char* group); char * NTcopy_str( str *s ); /* utility function to give each children a unique seed */ void seed_child(unsigned int seed); int parse_reply_codes( str *options_reply_codes_str, int **options_reply_codes, int *options_codes_no); void base64encode(unsigned char *out, unsigned char *in, int inlen); int base64decode(unsigned char *out,unsigned char *in,int len); static inline int calc_base64_encode_len(int len) { return (len/3 + (len%3?1:0))*4; } static inline int calc_max_base64_decode_len(int len) { return len*3/4; } #endif opensips-2.2.2/utils/000077500000000000000000000000001300170765700145235ustar00rootroot00000000000000opensips-2.2.2/utils/db_berkeley/000077500000000000000000000000001300170765700167725ustar00rootroot00000000000000opensips-2.2.2/utils/db_berkeley/.gitignore000066400000000000000000000000141300170765700207550ustar00rootroot00000000000000bdb_recover opensips-2.2.2/utils/db_berkeley/Makefile000066400000000000000000000007031300170765700204320ustar00rootroot00000000000000# $Id$ # # db_berkeley Makefile # include ../../Makefile.defs auto_gen= NAME=bdb_recover include ../../Makefile.sources # if you want to tune or reset flags #DEFS:=-DEXTRA_DEBUG -I$(LOCALBASE)/BerkeleyDB.4.6/include DEFS+=-I$(LOCALBASE)/include -I$(LOCALBASE)/BerkeleyDB.4.6/include \ -I$(SYSBASE)/include LIBS=-L$(LOCALBASE)/lib -L$(SYSBASE)/lib -L$(LOCALBASE)/BerkeleyDB.4.6/lib -ldb include ../../Makefile.rules modules: opensips-2.2.2/utils/db_berkeley/bdb_recover.c000066400000000000000000000443571300170765700214270ustar00rootroot00000000000000/* * recovery for berkeley_db module * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #include #include "bdb_recover.h" tbl_cache_p tables; char* schema_dir = NULL; char* db_home = NULL; const char *progname; /** * main -- */ int main(int argc, char* argv[]) { int ret, ch, i; ret = 0; progname = argv[0]; while ((ch = getopt(argc, argv, "s:h:c:C:r:R:")) != EOF) switch (ch) { case 's': schema_dir = optarg; load_schema(optarg); break; case 'c': /*create */ ret = create(optarg); break; case 'C': /*Create all*/ ret = create_all(); break; case 'r': /*recover */ ret = recover(optarg); break; case 'R': /*recover_all */ ret = sscanf(optarg,"%i", &i); if(ret != 1) return -1; ret = recover_all(i); break; case 'h': db_home = optarg; break; case '?': default: return(usage()); } argc -= optind; argv += optind; /*free mem; close open files.*/ cleanup(); return ret; } /** * usage -- * */ int usage(void) { fprintf(stderr, "usage: %s %s\n", progname, "-s schemadir [-h home] [-c tablename]"); fprintf(stderr, "usage: %s %s\n", progname, "-s schemadir [-h home] [-C all]"); fprintf(stderr, "usage: %s %s\n", progname, "-s schemadir [-h home] [-r journal-file]"); fprintf(stderr, "usage: %s %s\n", progname, "-s schemadir [-h home] [-R lastN]"); return (EXIT_FAILURE); } /** * create -- creates a Berkeley DB file with tablename (tn), along with * the needed metadata. * requires the schema data to be already parsed '-L' option. */ int create(char* tn) { DB* db; int rc; tbl_cache_p tbc = NULL; table_p tp = NULL; rc = 0; tbc = get_table(tn); if(!tbc) { fprintf(stderr, "[create] Table %s is not supported.\n",tn); return 1; } tp = tbc->dtp; db = get_db(tp); if(db) { printf("Created table %s\n",tn); rc = 0; } else { fprintf(stderr, "[create] Failed to create table %s\n",tn); rc = 1; } return rc; } /** * create_all -- creates a new Berkeley DB table for only the core tables */ int create_all(void) { tbl_cache_p _tbc = tables; int rc; rc = 0; #ifdef EXTRA_DEBUG time_t tim1 = time(NULL); time_t tim2; int i; #endif while(_tbc) { if(_tbc->dtp) if((rc = create(_tbc->dtp->name)) != 0 ) break; _tbc = _tbc->next; } #ifdef EXTRA_DEBUG tim2 = time(NULL); i = tim2 - tim1; printf("took %i sec\n", i); #endif return rc; } /** * file_list -- * returns a sorted linkedlist of all files in d * * parmameter d is the directory name * parameter tn is optional, * if tablename (tn) is specified returns only jnl files for tablename (tn) * else returns a sorted linkedlist of all files in d * returns lnode_p * the head linknode points to the latests file. */ lnode_p file_list(char* d, char* tn) { DIR *dirp; int i, j, len; char *fn; char *list[MAXFILES]; char dir[MAX_FILENAME_SIZE]; struct dirent *dp; lnode_p h,n; h = n = NULL; i = j = 0; if(!d) { fprintf(stderr, "[file_list]: null path to schema files.\n"); return NULL; } memset(dir, 0, MAX_FILENAME_SIZE); strcpy(dir, d); strcat(dir, "/"); //strcat(dir, "."); dirp = opendir(dir); while ((dp = readdir(dirp)) != NULL) { j=0; if (i> (MAXFILES-1) ) continue; fn = dp->d_name; if (fn[0] == '.') continue; if(tn) { /* only looking for jnl files */ len = strlen(tn); if (!strstr(fn, ".jnl")) continue; if (strncmp(fn, tn, len)) continue; } j = strlen(fn) +1; list[i] = malloc(sizeof(char) * j); memset(list[i], 0 , j); strcat(list[i], fn); i++; } closedir(dirp); qsort(list, i, sizeof(char*), compare); for(j=0;jprev=NULL; n->p = list[j]; if(h) h->prev = n; n->next = h; h = n; } return h; } /** qsort C-string comparison function */ int compare (const void *a, const void *b) { const char **ia = (const char **)a; const char **ib = (const char **)b; return strcmp(*ia, *ib); } /** * recover -- given a journal filename, creates a new db w. metadata, and replays * the events in journalized order. * Results in a new db containing the journaled data. * * fn (filename) must be in the form: * location-20070803175446.jnl */ int recover(char* jfn) { #ifdef EXTRA_DEBUG time_t tim1 = time(NULL); time_t tim2; #endif int len, i, cs, ci, cd, cu; char *v, *s; char line [MAX_ROW_SIZE]; char tn [MAX_TABLENAME_SIZE]; char fn [MAX_FILENAME_SIZE]; char op [7]; //INSERT, DELETE, UPDATE are all 7 char wide (w. null) FILE * fp = NULL; tbl_cache_p tbc = NULL; table_p tp = NULL; i = 0 ; cs = ci = cd = cu = 0; if(!strstr(jfn, ".jnl")) { fprintf(stderr, "[recover]: Does NOT look like a journal file: %s.\n", jfn); return 1; } if(!db_home) { fprintf(stderr, "[recover]: null path to db_home.\n"); return 1; } /*tablename tn*/ s = strchr(jfn, '-'); len = s - jfn; strncpy(tn, jfn, len); tn[len] = 0; /*create abs path to journal file relative to db_home*/ memset(fn, 0 , MAX_FILENAME_SIZE); strcat(fn, db_home); strcat(fn, "/"); strcat(fn, jfn); fp = fopen(fn, "r"); if(!fp) { fprintf(stderr, "[recover]: FAILED to load journal file: %s.\n", jfn); return 2; } tbc = get_table(tn); if(!tbc) { fprintf(stderr, "[recover]: Table %s is not supported.\n",tn); fprintf(stderr, "[recover]: FAILED to load journal file: %s.\n", jfn); return 2; } tp = tbc->dtp; if(!tbc || !tp) { fprintf(stderr, "[recover]: FAILED to get find metadata for : %s.\n", tn); return 3; } while ( fgets(line , MAX_ROW_SIZE, fp) != NULL ) { len = strlen(line); if(line[0] == '#' || line[0] == '\n') continue; if(len > 0) line[len-1] = 0; /*chomp trailing \n */ v = strchr(line, '|'); len = v - line; strncpy(op, line, len); op[len] = 0; switch( get_op(op, len) ) { case INSERT: v++; //now v points to data len = strlen(v); insert(tp, v, len); ci++; break; case UPDATE: v++; len = strlen(v); update(tp, v, len); cu++; break; case DELETE: //v is really the key delete(tp, v, len); cd++; break; case UNKNOWN_OP: fprintf(stderr,"[recover]: UnknownOP - Skipping ROW: %s\n",line); cs++; continue; } i++; } #ifdef EXTRA_DEBUG printf("Processed journal file: %s.\n", jfn); printf("INSERT %i records.\n",ci); printf("UPDATE %i records.\n",cu); printf("DELETE %i records.\n",cd); printf("SKIPed %i records.\n",cs); printf("------------------------\n"); printf("Total %i records.\n",i); tim2 = time(NULL); i = tim2 - tim1; printf("took %i sec\n", i); #endif fclose(fp); return 0; } /** * recover_all -- Iterates over all core tables in enumerated order for recovery from * journal files (.jnl). * The parm 'lastn' is the number of journal files needed to be recovered. * Hardcoded to only find MAXFILES. * * e.g. * 25 journal files are present for the 'acc' table, however you only * want to restore the latest 3; so lastn=3. */ int recover_all(int lastn) { lnode_p n, h; tbl_cache_p _tbc = tables; if(MAXFILES < lastn) return 1; if(!schema_dir) { fprintf(stderr, "[recover_all]: null path to schema files.\n"); return 1; } if(!db_home) { fprintf(stderr, "[recover_all]: null path to db_home.\n"); return 1; } while(_tbc) { int j; if(_tbc->dtp) h = file_list(db_home, _tbc->dtp->name); n = h; /*lastn; move to the oldest of the N*/ for(j=1;jnext != NULL) ) n = n->next; while(n) { printf("[recover_all] recovering file: %s\n",n->p); if(recover(n->p)) fprintf(stderr, "[recover_all]: Error while recovering: [%s]\n. Continuing..\n",n->p); n = n->prev; } while(h) /*free mem*/ { n = h->next; free(h->p); free(h); h = n; } _tbc = _tbc->next; } return 0; } /** * extract_key -- uses the internal schema to extract the key from the data * row that was found in the journal. * caller provides inititialize memory for destination key (k). * data is provided ; key is filled in */ int extract_key(table_p tp, char* k, char* d) { char *s, *p; char buf[MAX_ROW_SIZE]; int n, len; if(!tp || !k || !d) return -1; len=n=0; p = k; /*copy data so we can tokenize w.o trampling */ len = strlen(d); strncpy(buf, d, len); buf[len] = 0; s = strtok(buf, "|"); while(s!=NULL && nncols-1) > n) { if( tp->colp[n]->kflag ) { strncpy(p, s, len); p+=len; *p = '|'; p++; } } s=strtok(NULL, "|"); n++; } *p = 0; return 0; } /** * delete -- deletes a row from the db we are trying to rebuild */ int delete(table_p tp, char* k, int len) { DBT key; DB *db; if(!tp || !k) return 1; if((db = get_db(tp)) == NULL) return 2; memset(&key, 0, sizeof(DBT)); key.data = k; key.ulen = MAX_ROW_SIZE; key.size = len; if ( db->del(db, NULL, &key, 0)) { fprintf(stderr, "[delete] FAILED --> [%.*s] \n", len, k); return 3; } return 0; } /** * _insert -- inserts a new row in to the db we are trying to rebuild * I needed this to directly insert metadata when the db is created. */ int _insert(DB* db, char* k, char* v, int klen, int vlen) { DBT key, data; if(!db || !k || !v) return 1; memset(&key, 0, sizeof(DBT)); key.data = k; key.ulen = MAX_ROW_SIZE; key.size = klen; memset(&data, 0, sizeof(DBT)); data.data = v; data.ulen = MAX_ROW_SIZE; data.size = vlen; if (db->put(db, NULL, &key, &data, 0)) { fprintf(stderr, "[insert] FAILED --> [%.*s] \n", vlen, v); return 1; } return 0; } /** * insert -- given the data row (v) and its length (vlen), we build the corresponding * key, and insert the data in to the db. * This will over-right the value if already present. */ int insert(table_p tp, char* v, int vlen) { char k[MAX_ROW_SIZE]; int rc, klen; DB *db; if(!tp || !v) return 1; if((db = get_db(tp)) == NULL) return 2; memset(k,0,MAX_ROW_SIZE); if( extract_key(tp, k, v) ) { fprintf(stderr, "[insert] failed to extract key for row: %.*s",vlen, v); return 2; } klen = strlen(k); rc = _insert(db, k, v, klen, vlen); return rc; } /** * update -- given the data row (v) and its length (vlen), we build the corresponding * key, and update the data in the db. * This is implemented as DELETE + INSERT. */ int update(table_p tp, char* v, int len) { char k[MAX_ROW_SIZE]; if(!tp || !v) return 1; memset(k,0,MAX_ROW_SIZE); if( extract_key(tp, k, v) ) { fprintf(stderr, "[update] failed to extract key for row: %.*s",len, v); return 2; } /* if( delete(tp, k, strlen(k)) ) return 3; */ if( insert(tp, v, len) ) return 4; return 0; } /** * get_op -- used to convert the string operation name to an enumerated op */ int get_op(char* op, int len) { if((len==6) && strstr("INSERT",op) ) return INSERT; if((len==6) && strstr("UPDATE",op) ) return UPDATE; if((len==6) && strstr("DELETE",op) ) return DELETE; return UNKNOWN_OP; } /** * load_schema -- sets up the internal representation of the schema. */ int load_schema(char* d) { int rc; char *tn; char line1 [MAX_ROW_SIZE]; char line2 [MAX_ROW_SIZE]; char fn [MAX_FILENAME_SIZE]; tbl_cache_p tbc = NULL; table_p tp = NULL; FILE * fp = NULL; lnode_p h,n; rc=0; h = n = NULL; if(!d) { fprintf(stderr, "[load_schema]: null path to schema files.\n"); return 1; } tables = (tbl_cache_p)malloc(sizeof(tbl_cache_t)); if(!tables) return 1; h = file_list(d, NULL); while(h) { n = h->next; /*create abs path to journal file (relative to db_home) */ memset(fn, 0 , MAX_FILENAME_SIZE); strcat(fn, d); strcat(fn, "/"); strcat(fn, h->p); fp = fopen(fn, "r"); if(!fp) { fprintf(stderr, "[load_schema]: FAILED to load schema file: %s.\n", h->p); break; } tn = h->p; tbc = get_table(tn); if(!tbc) { fprintf(stderr, "[load_schema]: Table %s is not supported.\n",tn); fprintf(stderr, "[load_schema]: FAILED to load data for table: %s.\n", tn); goto done; } tp = tbc->dtp; while ( fgets(line1 , MAX_ROW_SIZE, fp) != NULL ) { if ( fgets(line2 , MAX_ROW_SIZE, fp) != NULL ) { if(strstr(line1, METADATA_COLUMNS)) { if(0!=load_metadata_columns(tp, line2)) { fprintf(stderr, "[load_schema]: FAILED to load METADATA COLS in table: %s.\n", tn); goto done; } } if(strstr(line1, METADATA_KEY)) { if(0!=load_metadata_key(tp, line2)) { fprintf(stderr, "[load_schema]: FAILED to load METADATA KEYS in table: %s.\n", tn); goto done; } } } else { fprintf(stderr, "[load_schema]: FAILED to read schema value in table: %s.\n", tn); goto done; } } done: fclose(fp); h = n; } while(h) /*free mem*/ { n = h->next; free(h->p); free(h); h = n; } return rc; } /** * get_table -- return pointer to lazy initialized table struct */ tbl_cache_p get_table(char *_s) { tbl_cache_p _tbc = tables; table_p _tp = NULL; while(_tbc) { if(_tbc->dtp) { if(_tbc->dtp->name && !strcmp(_tbc->dtp->name,_s)) { return _tbc; } } _tbc = _tbc->next; } _tbc = (tbl_cache_p)malloc(sizeof(tbl_cache_t)); if(!_tbc) return NULL; _tp = create_table(_s); if(!_tp) { fprintf(stderr, "[get_table]: failed to create table.\n"); free(_tbc); return NULL; } _tbc->dtp = _tp; if(tables) (tables)->prev = _tbc; _tbc->next = tables; tables = _tbc; return _tbc; } /** * create_table -- returns an initialed table struct */ table_p create_table(char *_s) { int i; table_p tp = NULL; tp = (table_p)malloc(sizeof(table_t)); if(!tp) return NULL; i=strlen(_s)+1; tp->name = (char*)malloc(i*sizeof(char)); strncpy(tp->name, _s, i); tp->ncols=0; tp->nkeys=0; tp->ro=0; tp->logflags=0; tp->db = NULL; for(i=0;icolp[i] = NULL; return tp; } /** * load_metadata_columns -- parses the METADATA_COLUMNS line into the internal * representation. */ int load_metadata_columns(table_p _tp, char* line) { int n,len; char *s = NULL; char cn[64], ct[16]; column_p col; n = len = 0; if(!_tp) return -1; if(_tp->ncols!=0) return 0; /* eg: line = "table_name(str) table_version(int)" */ s = strtok(line, " \t"); while(s!=NULL && nname = (char*)malloc(len * sizeof(char)); strcpy(col->name, cn ); /* set type*/ len = strlen( ct )+1; col->type = (char*)malloc(len * sizeof(char)); strcpy(col->type, ct ); _tp->colp[n] = col; n++; _tp->ncols++; s=strtok(NULL, " \t"); } return 0; } /** * load_metadata_key -- parses the METADATA_KEY line into the internal * representation. */ int load_metadata_key(table_p _tp, char* line) { int ret,n,ci; char *s = NULL; ret = n = ci = 0; if(!_tp)return -1; s = strtok(line, " \t"); while(s!=NULL && n< _tp->ncols) { ret = sscanf(s,"%i", &ci); if(ret != 1) return -1; if( _tp->colp[ci] ) { _tp->colp[ci]->kflag = 1; _tp->nkeys++; } n++; s=strtok(NULL, " "); } return 0; } /** * get_db -- lazy initialized DB access * Its like this so we get new db files only for the tables that have * journal files. * The db file on disk will be named: * .new */ DB* get_db(table_p tp) { int rc; DB* db; char dfn[MAX_FILENAME_SIZE]; if( !tp) return NULL; if( tp->db) return tp->db; memset(dfn, 0, MAX_FILENAME_SIZE); if(db_home) { strcpy(dfn, db_home); strcat(dfn, "/"); } /*creation of DB follows*/ strcat(dfn, tp->name); if ((rc = db_create(&db, NULL, 0)) != 0) { fprintf(stderr, "[create_table]: error db_create for table: %s.\n",dfn); return NULL; } if ((rc = db->open(db, NULL, dfn, NULL, DB_HASH, DB_CREATE, 0664)) != 0) { fprintf(stderr, "[create_table]: error opening %s.\n",dfn); fprintf(stderr, "[create_table]: error msg: %s.\n",db_strerror(rc)); return NULL; } tp->db = db; import_schema(tp); return db; } /** */ int import_schema(table_p tp) { int rc, len1, len2; char line1 [MAX_ROW_SIZE]; char line2 [MAX_ROW_SIZE]; char fn [MAX_FILENAME_SIZE]; FILE * fp = NULL; rc = 0; if(!schema_dir) { fprintf(stderr, "[import_schema]: null schema dir.\n"); return 1; } if(!tp) { fprintf(stderr, "[import_schema]: null table parameter.\n"); return 1; } /*create abs path to journal file (relative to db_home) */ memset(fn, 0 , MAX_FILENAME_SIZE); strcat(fn, schema_dir); strcat(fn, "/"); strcat(fn, tp->name); fp = fopen(fn, "r"); if(!fp) { fprintf(stderr, "[import_schema]: FAILED to open def schema file: %s.\n", fn); return 1; } while ( fgets(line1 , MAX_ROW_SIZE, fp) != NULL ) { if ( fgets(line2 , MAX_ROW_SIZE, fp) != NULL ) { len1 = strlen(line1)-1; len2 = strlen(line2)-1; line1[len1] = 0; line2[len2] = 0; if((rc = _insert(tp->db, line1, line2, len1, len2) )!=0) { fprintf(stderr, "[import_schema]: FAILED to write schema def into table: %s.\n", tp->name); goto done; } } else { fprintf(stderr, "[import_schema]: FAILED to read schema def value in table: %s.\n", tp->name); goto done; } } done: fclose(fp); return rc; } /** * cleanup -- frees memory; closes any files. */ void cleanup(void) { //cleanup while(tables) { int i; tbl_cache_p n = tables->next; table_p tp = tables->dtp; if(tp) { free(tp->name); for(i=0;i< tp->ncols;i++) { free(tp->colp[i]->name); free(tp->colp[i]->type); free(tp->colp[i]); } if(tp->db) tp->db->close(tp->db, 0); free(tp); } free(tables); tables = n; } } opensips-2.2.2/utils/db_berkeley/bdb_recover.h000066400000000000000000000051631300170765700214240ustar00rootroot00000000000000/* * recovery for berkeley_db module * * Copyright (C) 2007 Cisco Systems * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #include #include #include #include #include #include /*max number of journal files that we are reading*/ #define MAXFILES 64 /*max number of columns in a table*/ #define MAX_NUM_COLS 32 /*max char width of a table row*/ #define MAX_ROW_SIZE 2048 /*max char width of a table name*/ #define MAX_TABLENAME_SIZE 64 #define MAX_FILENAME_SIZE 512 #define METADATA_KEY "METADATA_KEY" #define METADATA_COLUMNS "METADATA_COLUMNS" /*operations*/ enum { INSERT, UPDATE, DELETE, UNKNOWN_OP }; typedef struct _lnode { char* p; struct _lnode *prev; struct _lnode *next; } lnode_t, *lnode_p; typedef struct _column { char* name; char* type; int kflag; } column_t, *column_p; typedef struct _table { char* name; column_p colp [MAX_NUM_COLS]; int ncols; int nkeys; int ro; int logflags; DB* db; } table_t, *table_p; typedef struct _tbl_cache { table_p dtp; struct _tbl_cache *prev; struct _tbl_cache *next; } tbl_cache_t, *tbl_cache_p; int usage(void); DB* get_db(table_p tp); int get_op(char* op, int len); int delete(table_p tp, char* v, int len); int insert(table_p tp, char* v, int len); int _insert(DB* db, char* k, char* v, int klen, int vlen); int update(table_p tp, char* v, int len); int create(char* tn); int _version(DB* db); int create_all(void); int recover(char* tn); int recover_all(int lastn); lnode_p file_list(char* d, char* tn); int compare (const void *a, const void *b); int extract_key(table_p tp, char* key, char* data); int load_schema(char* dir); tbl_cache_p get_table(char *s); table_p create_table(char *_s); int load_metadata_columns(table_p _tp, char* line); int load_metadata_key(table_p _tp, char* line); int import_schema(table_p tp); void cleanup(void); opensips-2.2.2/utils/db_oracle/000077500000000000000000000000001300170765700164355ustar00rootroot00000000000000opensips-2.2.2/utils/db_oracle/Makefile000066400000000000000000000041531300170765700201000ustar00rootroot00000000000000# $Id$ # # db_orasel Makefile # include ../../Makefile.defs auto_gen= NAME=opensips_orasel include ../../Makefile.sources ORAPATH= # use for multiple client sdk version install ifneq ($(ORAVERSION),) ORAVERDIR=/$(ORAVERSION) endif # use include/library path's for full client installation ifneq ($(ORAHOME),) DEFS +=-I$(ORAHOME)/include LIBS +=-L$(ORAHOME)/lib ifeq ($(ORAPATH),) ORAPATH=$(ORAHOME)/lib endif else # use standard know paths oci.h locations (linux) DEFS +=-I$(LOCALBASE)/include/oracle$(ORAVERDIR) \ -I$(SYSBASE)/include/oracle$(ORAVERDIR) endif # search 'so' path if it non standard (possible liboclntsh locations on linux) ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(LOCALBASE)/lib64/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(LOCALBASE)/lib64/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib64/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(SYSBASE)/lib64/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(LOCALBASE)/lib64/oracle$(ORAVERDIR)/lib/libocci.so ] && \ echo $(LOCALBASE)/lib64/oracle$(ORAVERDIR)/lib ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib64/oracle$(ORAVERDIR)/lib/libocci.so ] && \ echo $(SYSBASE)/lib64/oracle$(ORAVERDIR)/lib ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(LOCALBASE)/lib/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(LOCALBASE)/lib/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(SYSBASE)/lib/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(LOCALBASE)/lib/oracle$(ORAVERDIR)/lib/libocci.so ] && \ echo $(LOCALBASE)/lib/oracle$(ORAVERDIR)/lib ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib/oracle$(ORAVERDIR)/lib/libocci.so ] && \ echo $(SYSBASE)/lib/oracle$(ORAVERDIR)/lib ) endif ifneq ($(ORAPATH),) LIBS +=-L$(ORAPATH) endif LIBS +=-locci -lclntsh #DEFS +=-DLINUX -D_GNU_SOURCE -D_REENTRANT #LIBS +=-lpthread ifneq ($(ORAPATH),) LIBS +=-Wl,-rpath $(ORAPATH) endif include ../../Makefile.rules modules: opensips-2.2.2/utils/db_oracle/getres.c000066400000000000000000000174211300170765700200770ustar00rootroot00000000000000#include "orasel.h" #include #include /* * Uncomment next string if you will sell 'NULL' on unitialized NON text field */ //#define NULL_ID "NULL" static char st_buf[65536]; enum type_t { DB_STR = 0, DB_DATETIME, /* end of left alignment */ DB_INT, DB_BITMAP, DB_DOUBLE /* MUST belast */ }; //--------------------------------------------------------- struct dmap { OCIDefine** defh; union { dvoid* v; double* f; int* i; char* c; OCIDate* o; }* pv; dvoid** pval; ub2* ilen; sb2* ind; ub2* len; }; typedef struct dmap dmap_t; //----------------------------------------------------------------------------- static void dmap_init(dmap_t* _d, unsigned n) { size_t sz = sizeof(*_d->defh) + sizeof(*_d->pv) + sizeof(*_d->pval) + sizeof(*_d->ilen) + sizeof(*_d->ind) + sizeof(*_d->len); unsigned char *p = safe_malloc(sz * n); _d->defh = (void*)p; p += n*sizeof(*_d->defh); _d->pv = (void*)p; p += n*sizeof(*_d->pv); _d->pval = (void*)p; p += n*sizeof(*_d->pval); _d->ilen = (void*)p; p += n*sizeof(*_d->ilen); _d->ind = (void*)p; p += n*sizeof(*_d->ind); _d->len = (void*)p; // p += n*sizeof(*_d->len); } //----------------------------------------------------------------------------- /* * Get and convert columns from a result. Define handlers and buffers */ static void get_columns(const con_t* con, res_t* _r, dmap_t* _d) { OCIParam *param; size_t tsz; ub4 i, n; sword status; status = OCIAttrGet(con->stmthp, OCI_HTYPE_STMT, &n, NULL, OCI_ATTR_PARAM_COUNT, con->errhp); if (status != OCI_SUCCESS) oraxit(status, con); if (!n) donegood("Empty table"); dmap_init(_d, n); _r->names = (Str**)safe_malloc(n * sizeof(Str*)); _r->types = (unsigned char*)safe_malloc(n * sizeof(unsigned char)); _r->col_n = n; tsz = 0; memset(_d->defh, 0, sizeof(_d->defh[0]) * n); for (i = 0; i < n; i++) { ub4 len; ub2 dtype; unsigned char ctype = DB_DOUBLE; status = OCIParamGet(con->stmthp, OCI_HTYPE_STMT, con->errhp, (dvoid**)(dvoid*)¶m, i+1); if (status != OCI_SUCCESS) goto ora_err; { text *name; status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&name, &len, OCI_ATTR_NAME, con->errhp); if (status != OCI_SUCCESS) goto ora_err; _r->names[i] = str_alloc((char*)name, len); } status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE, con->errhp); if (status != OCI_SUCCESS) goto ora_err; switch (dtype) { case SQLT_UIN: set_bitmap: ctype = DB_BITMAP; len = sizeof(unsigned); break; case SQLT_INT: set_int: ctype = DB_INT; len = sizeof(int); break; case SQLT_VNU: case SQLT_NUM: len = 0; /* PRECISION is ub1 (byte) */ status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&len, NULL, OCI_ATTR_PRECISION, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (len <= 11) { sb1 sc; status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&sc, NULL, OCI_ATTR_SCALE, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (!sc) { dtype = SQLT_INT; if (len != 11) goto set_int; dtype = SQLT_UIN; goto set_bitmap; } if(sc < 0) sc = 0; ctype += sc; } case SQLT_FLT: case SQLT_BFLOAT: case SQLT_BDOUBLE: case SQLT_IBFLOAT: case SQLT_IBDOUBLE: case SQLT_PDN: len = sizeof(double); dtype = SQLT_FLT; break; case SQLT_DATE: case SQLT_DAT: case SQLT_ODT: case SQLT_TIMESTAMP: case SQLT_TIMESTAMP_TZ: case SQLT_TIMESTAMP_LTZ: ctype = DB_DATETIME; len = sizeof(OCIDate); dtype = SQLT_ODT; break; case SQLT_CLOB: case SQLT_BLOB: case SQLT_CHR: case SQLT_STR: case SQLT_VST: case SQLT_VCS: case SQLT_AFC: case SQLT_AVC: ctype = DB_STR; dtype = SQLT_CHR; len = 0; /* DATA_SIZE is ub2 (word) */ status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&len, NULL, OCI_ATTR_DATA_SIZE, con->errhp); if (status != OCI_SUCCESS) goto ora_err; ++len; break; default: errxit("unsupported datatype"); } _r->types[i] = ctype; _d->ilen[i] = (ub2)len; _d->pv[i].v = st_buf + tsz; tsz += len; status = OCIDefineByPos(con->stmthp, &_d->defh[i], con->errhp, i+1, _d->pv[i].v, len, dtype, &_d->ind[i], &_d->len[i], NULL, OCI_DEFAULT); if (status != OCI_SUCCESS) goto ora_err; } if (tsz > sizeof(st_buf)) errxit("too large row"); return; ora_err: oraxit(status, con); } //----------------------------------------------------------------------------- /* * Convert data fron db format to internal format */ static void convert_row(const res_t* _res, Str*** _r, const dmap_t* _d) { unsigned i, n = _res->col_n; Str** v; *_r = v = (Str**)safe_malloc(n * sizeof(Str**)); for (i = 0; i < n; i++, v++) { char buf[64]; unsigned char t = _res->types[i]; if (_d->ind[i] == -1) { static const struct { unsigned len; char s[1]; }_empty = { 0, "" }; #ifdef NULL_ID static const struct { unsigned len; char s[sizeof(NULL_ID)]; }_null = { sizeof(NULL_ID)-1, NULL_ID }; *v = (Str*)&_null; if (t != DB_STR) continue; #endif *v = (Str*)&_empty; continue; } // if (_d->ind[i]) errxit("truncated value in DB"); switch (t) { case DB_STR: *v = str_alloc(_d->pv[i].c, _d->len[i]); break; case DB_INT: *v = str_alloc(buf, snprintf(buf, sizeof(buf), "%i", *_d->pv[i].i)); break; case DB_BITMAP: *v = str_alloc(buf, snprintf(buf, sizeof(buf), "0x%X", *_d->pv[i].i)); break; case DB_DATETIME: { struct tm tm; memset(&tm, 0, sizeof(tm)); OCIDateGetTime(_d->pv[i].o, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); OCIDateGetDate(_d->pv[i].o, &tm.tm_year, &tm.tm_mon, &tm.tm_mday); if (tm.tm_mon) --tm.tm_mon; if (tm.tm_year >= 1900) tm.tm_year -= 1900; *v = str_alloc(buf, strftime(buf, sizeof(buf), "%d-%b-%Y %T", &tm)); } break; case DB_DOUBLE: *v = str_alloc(buf, snprintf(buf, sizeof(buf), "%g", *_d->pv[i].f)); break; default: { double x = fabs(*_d->pv[i].f); const char *fmt = "%.*f"; if (x && (x >= 1.0e6 || x < 1.0e-5)) fmt = "%.*e"; *v = str_alloc(buf, snprintf(buf, sizeof(buf), fmt, (t - DB_DOUBLE), *_d->pv[i].f)); } break; } } } //----------------------------------------------------------------------------- /* * Get rows and convert it from oracle to db API representation */ static void get_rows(const con_t* con, res_t* _r, dmap_t* _d) { ub4 rcnt; sword status; unsigned n = _r->col_n; memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n); status = OCIStmtFetch2(con->stmthp, con->errhp, 1, OCI_FETCH_LAST, 0, OCI_DEFAULT); if (status != OCI_SUCCESS) { if (status == OCI_NO_DATA) donegood("Empty set"); goto ora_err; } status = OCIAttrGet(con->stmthp, OCI_HTYPE_STMT, &rcnt, NULL, OCI_ATTR_CURRENT_POSITION, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (!rcnt) errxit("lastpos==0"); _r->row_n = rcnt; _r->rows = (Str***)safe_malloc(rcnt * sizeof(Str**)); while ( 1 ) { convert_row(_r, &_r->rows[--rcnt], _d); if (!rcnt) return; memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n); status = OCIStmtFetch2(con->stmthp, con->errhp, 1, OCI_FETCH_PRIOR, 0, OCI_DEFAULT); if (status != OCI_SUCCESS) break; } ora_err: oraxit(status, con); } //----------------------------------------------------------------------------- /* * Read database answer and fill the structure */ void get_res(const con_t* con, res_t* _r) { dmap_t dmap; unsigned n; unsigned char *pt; get_columns(con, _r, &dmap); get_rows(con, _r, &dmap); n = _r->col_n; pt = _r->types; do { --n; assert(DB_STR == 0 && DB_DATETIME == 1); pt[n] = (pt[n] <= DB_DATETIME); }while(n); } //----------------------------------------------------------------------------- opensips-2.2.2/utils/db_oracle/orasel.c000066400000000000000000000054241300170765700200730ustar00rootroot00000000000000#include "orasel.h" #include #include outmode_t outmode; //----------------------------------------------------------------------------- static void prepare_uri(con_t* con, const char *uri) { const char *p = strchr(uri, '/'); if(!p || p == uri) goto bad; con->username = str_alloc(uri, p - uri); uri = p+1; p = strchr(uri, '@'); if(!p || p == uri) goto bad; con->password = str_alloc(uri, p - uri); if(strchr(con->password->s, '/')) goto bad; if(!*++p) goto bad; con->uri = str_alloc(p, strlen(p)); return; bad: errxit("invalid db (must be as name/password@dbname)"); } //----------------------------------------------------------------------------- static const Str* prepare_req(const char* req) { Str* ps; char *p; while(*req && isspace((unsigned char)*req)) ++req; if(strncasecmp(req, "select", sizeof("select")-1)) goto bad; p = (char*)req + sizeof("select")-1; if(!*p || !isspace((unsigned char)*p)) goto bad; ps = str_alloc(req, strlen(req)); p = (char*) ps->s + sizeof("select")-1; do if(isspace((unsigned char)*p)) *p = ' '; while(*++p); do --p; while(isspace((unsigned char)*p)); if(*p != ';') goto bad; do { do --p; while(isspace((unsigned char)*p)); }while(*p == ';'); *++p = '\0'; ps->len = p - ps->s; if(ps->len <= sizeof("select")-1) { bad: errxit("support only 'select ...;' request"); } return ps; } //----------------------------------------------------------------------------- static void get_opt(int argc, char* argv[]) { int opt = 0; if(argc <= 1 || (argc == 2 && !strcmp(argv[1], "--help"))) { help: fprintf(stderr, "OpenSIPS for oracle 'select' request utility\n"); opt = -2; /* flag for help print */ } else { while((opt = getopt(argc-1, argv+1, "BLNe:")) != -1) { switch(opt) { case 'B': outmode.raw = 1;; break; case 'L': outmode.hdr = 1; break; case 'N': outmode.emp = 1; break; case 'e': if(optind == argc-1) return; // pass thru default: goto help; } } } fprintf(stderr, "use: %s user/password@db [-BLN] -e \"select ...;\"\n", argv[0]); if(opt == -2) { fprintf(stderr, " where -B - print using tab separator\n"); fprintf(stderr, " -L - skip column headers\n"); fprintf(stderr, " -N - skip notify of empty result\n"); } exit(1); } //----------------------------------------------------------------------------- int main(int argc, char* argv[]) { con_t con; res_t res; const Str* req; get_opt(argc, argv); memset(&con, 0, sizeof(con)); memset(&res, 0, sizeof(res)); prepare_uri(&con, argv[1]); req = prepare_req(optarg); open_sess(&con); send_req(&con, req); get_res(&con, &res); OCITerminate(OCI_DEFAULT); out_res(&res); return 0; } //----------------------------------------------------------------------------- opensips-2.2.2/utils/db_oracle/orasel.h000066400000000000000000000021311300170765700200700ustar00rootroot00000000000000#ifndef __orasel_h__ #define __orasel_h__ #include #include #include #include #include typedef struct { unsigned len; char s[]; }Str; typedef struct { const Str* username; const Str* password; const Str* uri; OCIError* errhp; OCISvcCtx* svchp; OCIEnv* envhp; OCISession* authp; OCIServer* srvhp; OCIStmt* stmthp; }con_t; typedef struct { Str** names; Str*** rows; unsigned char* types; unsigned col_n; unsigned row_n; }res_t; void __attribute__((noreturn)) donegood(const char *msg); void __attribute__((noreturn)) errxit(const char *msg); void __attribute__((noreturn)) oraxit(sword status, const con_t* con); void* safe_malloc(size_t sz); Str* str_alloc(const char *s, size_t len); void open_sess(con_t* con); void send_req(con_t* con, const Str* req); void get_res(const con_t* con, res_t* _r); void out_res(const res_t* _r); typedef struct { unsigned raw : 1, hdr : 1, emp : 1; }outmode_t; extern outmode_t outmode; #endif opensips-2.2.2/utils/db_oracle/outres.c000066400000000000000000000030121300170765700201160ustar00rootroot00000000000000#include "orasel.h" //----------------------------------------------------------------------------- static void out_delim(const unsigned* pl, unsigned nc) { unsigned i; for(i = 0; i < nc; i++) { unsigned j = pl[i] + 2; putchar('+'); do putchar('-'); while(--j); } printf("+\n"); } //----------------------------------------------------------------------------- void out_res(const res_t* _r) { unsigned* pl = NULL; unsigned nc = _r->col_n, nr = _r->row_n, i, j; Str** ps = _r->names; if(!outmode.raw) { pl = safe_malloc(nc * sizeof(unsigned)); for(i = 0; i < nc; i++) pl[i] = ps[i]->len; for(j = 0; j < nr; j++) { ps = _r->rows[j]; for(i = 0; i < nc; i++) if(pl[i] < ps[i]->len) pl[i] = ps[i]->len; } out_delim(pl, nc); } if(!outmode.hdr) { ps = _r->names; for(i = 0; i < nc; i++) { if(!outmode.raw) { printf("| %-*.*s ", pl[i], ps[i]->len, ps[i]->s); } else { if(i) putchar('\t'); printf("%.*s", ps[i]->len, ps[i]->s); } } if(outmode.raw) putchar('\n'); else { printf("|\n"); out_delim(pl, nc); } } for(j = 0; j < nr; j++) { ps = _r->rows[j]; if(!outmode.raw) { for(i = 0; i < nc; i++) printf(_r->types[i] ? "| %-*.*s " : "| %*.*s ", pl[i], ps[i]->len, ps[i]->s); printf("|\n"); } else { for(i = 0; i < nc; i++) { if(i) putchar('\t'); printf("%.*s", ps[i]->len, ps[i]->s); } putchar('\n'); } } if(!outmode.raw) out_delim(pl, nc); } //----------------------------------------------------------------------------- opensips-2.2.2/utils/db_oracle/selcon.c000066400000000000000000000046101300170765700200650ustar00rootroot00000000000000#include "orasel.h" //----------------------------------------------------------------------------- void open_sess(con_t* con) { sword status; if ( OCIEnvCreate(&con->envhp, OCI_DEFAULT | OCI_NEW_LENGTH_SEMANTICS, NULL, NULL, NULL, NULL, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->errhp, OCI_HTYPE_ERROR, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->srvhp, OCI_HTYPE_SERVER, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->svchp, OCI_HTYPE_SVCCTX, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->authp, OCI_HTYPE_SESSION, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->stmthp, OCI_HTYPE_STMT, 0, NULL) != OCI_SUCCESS) { errxit("no oracle memory left"); } status = OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, con->srvhp, 0, OCI_ATTR_SERVER, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIAttrSet(con->authp, OCI_HTYPE_SESSION, (text*)con->username->s, con->username->len, OCI_ATTR_USERNAME, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIAttrSet(con->authp, OCI_HTYPE_SESSION, (text*)con->password->s, con->password->len, OCI_ATTR_PASSWORD, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, con->authp, 0, OCI_ATTR_SESSION, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIServerAttach(con->srvhp, con->errhp, (OraText*)con->uri->s, con->uri->len, 0); if (status != OCI_SUCCESS) goto connect_err; status = OCISessionBegin(con->svchp, con->errhp, con->authp, OCI_CRED_RDBMS, OCI_DEFAULT); if (status != OCI_SUCCESS) { connect_err: oraxit(status, con); } } //----------------------------------------------------------------------------- void send_req(con_t* con, const Str* req) { sword status; status = OCIStmtPrepare(con->stmthp, con->errhp, (text*)req->s, req->len, OCI_NTV_SYNTAX, OCI_DEFAULT); if (status != OCI_SUCCESS) goto request_err; status = OCIStmtExecute(con->svchp, con->stmthp, con->errhp, 0, 0, NULL, NULL, OCI_STMT_SCROLLABLE_READONLY); if (status != OCI_SUCCESS) { request_err: fprintf(stderr, "%.*s\n", req->len, req->s); oraxit(status, con); } } //----------------------------------------------------------------------------- opensips-2.2.2/utils/db_oracle/util.c000066400000000000000000000041541300170765700175620ustar00rootroot00000000000000#include "orasel.h" //----------------------------------------------------------------------------- void __attribute__((noreturn)) donegood(const char *msg) { OCITerminate(OCI_DEFAULT); if (msg && !outmode.emp) printf("%s\n", msg); exit(0); } //----------------------------------------------------------------------------- void __attribute__((noreturn)) errxit(const char *msg) { OCITerminate(OCI_DEFAULT); fprintf(stderr, "ERROR: %s\n", msg); exit(1); } //----------------------------------------------------------------------------- void __attribute__((noreturn)) oraxit(sword status, const con_t* con) { const char *p = NULL; char buf[512]; sword ecd; switch (status) { case OCI_SUCCESS_WITH_INFO: case OCI_ERROR: ecd = 0; if(OCIErrorGet(con->errhp, 1, NULL, &ecd, (OraText*)buf, sizeof(buf), OCI_HTYPE_ERROR) != OCI_SUCCESS) { snprintf(buf, sizeof(buf), "unknown ORAERR %u", ecd); } break; default: snprintf(buf, sizeof(buf), "unknown status %u", status); break; case OCI_SUCCESS: p = "success"; break; case OCI_NEED_DATA: p = "need data"; break; case OCI_NO_DATA: p = "no data"; break; case OCI_INVALID_HANDLE: p = "invalid handle"; break; case OCI_STILL_EXECUTING: /* ORA-3123 */ p = "executing"; break; case OCI_CONTINUE: p = "continue"; break; } if (p) { snprintf(buf, sizeof(buf), "logic error (%s)", p); } errxit(buf); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- static void __attribute__((noreturn)) nomem(void) { errxit("no enough memory"); } //----------------------------------------------------------------------------- void* safe_malloc(size_t sz) { void *p = malloc(sz); if (!p) nomem(); return p; } //----------------------------------------------------------------------------- Str* str_alloc(const char *s, size_t len) { Str* ps = (Str*)safe_malloc(sizeof(Str) + len + 1); ps->len = len; memcpy(ps->s, s, len); ps->s[len] = '\0'; return ps; } //----------------------------------------------------------------------------- opensips-2.2.2/utils/fifo_relay/000077500000000000000000000000001300170765700166425ustar00rootroot00000000000000opensips-2.2.2/utils/fifo_relay/fifo_server.php000066400000000000000000000115441300170765700216710ustar00rootroot00000000000000#!/usr/bin/php4 -q $mtime) { $fd = fopen($fifo_clients, "r"); if ($fd == FALSE) { echo "Cannot open fifo.clients file!\n"; return FALSE; } $clients = array(); while (!feof ($fd)) { $client = ip2long(fgets($fd, 4096)); if ($client != -1) { $clients[] = $client; } } fclose ($fd); $mtime = $cur_mtime; } return in_array($long_addr, $clients, TRUE); } if (($sock = socket_create (AF_INET, SOCK_STREAM, 0)) < 0) { echo "socket_create() failed: " . socket_strerror ($sock) . "\n"; return; } if (($ret = socket_bind($sock, $fifo_server_address, $fifo_server_port)) < 0) { echo "socket_bind() failed: " . socket_strerror ($ret) . "\n"; socket_close($sock); return; } if (($ret = socket_listen ($sock, 5)) < 0) { echo "socket_listen() failed: " . socket_strerror ($ret) . "\n"; socket_close($sock); return; } do { if (($msgsock = socket_accept($sock)) < 0) { echo "socket_accept() failed: ".socket_strerror($msgsock)."\n"; socket_close($msgsock); continue; } socket_getpeername($msgsock, $addr); if (!fifo_allow($fifo_clients, $addr)) { $msg = "403 Forbidden\n"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } if (FALSE === ($fifo_cmd = socket_read ($msgsock, 8192, PHP_BINARY_READ))) { echo "socket_read() failed: ".socket_strerror(socket_last_error($msgsock))."\n"; socket_shutdown($msgsock); socket_close($msgsock); continue; } $fifo_reply_file_name = "ser_fifo_reply_".rand(); $fifo_reply_file = "/tmp/".$fifo_reply_file_name; $fifo_cmd = str_replace("REPLY_FILE_NAME", $fifo_reply_file_name, $fifo_cmd); /* add command separator */ $fifo_cmd=$fifo_cmd."\n"; $fifo_handle=fopen( "/tmp/ser_fifo", "w" ); if (!$fifo_handle) { $msg = "sorry -- cannot open write fifo"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } /* create fifo for replies */ @system("mkfifo -m 666 ".$fifo_reply_file); /* write fifo command */ if (fwrite( $fifo_handle, $fifo_cmd) == -1) { @unlink($fifo_reply_file); @fclose($fifo_handle); $msg = "sorry -- fifo writing error"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } @fclose($fifo_handle); /* read output now */ @$fp = fopen($fifo_reply_file, "r"); if (!$fp) { @unlink($fifo_reply_file); $msg = "sorry -- reply fifo opening error"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } $status = fgetS($fp, 256); if (!$status) { @unlink($fifo_reply_file); $msg = "sorry -- reply fifo reading error"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } socket_write($msgsock, $status, strlen($status)); $rest = fread($fp, 8192); @unlink($fifo_reply_file); socket_write($msgsock, $rest, strlen($rest)); socket_close ($msgsock); } while (true); socket_close ($sock); ?> opensips-2.2.2/utils/opensipsunix/000077500000000000000000000000001300170765700172675ustar00rootroot00000000000000opensips-2.2.2/utils/opensipsunix/Makefile000066400000000000000000000003501300170765700207250ustar00rootroot00000000000000# $Id$ # # serunix Makefile # include ../../Makefile.defs auto_gen= NAME=opensipsunix include ../../Makefile.sources # if you want to tune or reset flags #DEFS:= #LDFLAGS:= #LIBS:= include ../../Makefile.rules modules: opensips-2.2.2/utils/opensipsunix/opensipsunix.8000066400000000000000000000024051300170765700221250ustar00rootroot00000000000000.\" $Id$ .TH opensipsunix 8 21.06.2006 opensips "OpenSIPS" .\" Process with .\" groff -man -Tascii opensipsunix.8 .\" .SH NAME opensipsunix \- opensips UNIX socket wrapper .SH SYNOPSIS .B opensipsunix .BI path_to_socket .SH DESCRIPTION .B opensipsunix is a wrapper for sending external commands .B to OpenSIPS server via UNIX sockets interface. .br This is a binary alternative to the textual FIFO interface. .PP .B opensipsunix reads from standard input one .B OpenSIPS command along with its parameters (if any) and prints the response to standard output. .SH PARAMETERS .TP 3 .B path_to_socket full path of the UNIX socket file used by OpenSIPS to receive the external commands .SH EXAMPLES .PP An OpenSIPS commands consists in one ore more lines: first contains the command name enclosed between ":", the following lines containing the command parameters, one per line. .PP echo ":uptime:" | opensipsunix /tmp/opensips.sock .SH AUTHORS see .B /usr/share/doc/opensips/AUTHORS .SH SEE ALSO .BR opensips(8), opensips.cfg(5), opensipsctl(8) .PP Full documentation on opensips is available at .I http://www.opensips.org/. .PP Mailing lists: .nf users@opensips.org - opensips user community .nf devel@opensips.org - opensips development, new features and unstable version opensips-2.2.2/utils/opensipsunix/opensipsunix.c000066400000000000000000000070071300170765700222030ustar00rootroot00000000000000/* * Copyright (C) 2004 FhG FOKUS * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include /* AF_LOCAL is not defined on solaris */ #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #if !defined(PF_LOCAL) #define PF_LOCAL PF_UNIX #endif /* solaris doesn't have SUN_LEN */ #ifndef SUN_LEN #define SUN_LEN(sa) ( strlen((sa)->sun_path) + \ (size_t)(((struct sockaddr_un*)0)->sun_path) ) #endif #define BUF_SIZE 65536 #define DEFAULT_TIMEOUT 5 int main(int argc, char** argv) { int sock, len; socklen_t from_len; struct sockaddr_un from, to; char name[108]; static char buffer[BUF_SIZE]; char *chroot_dir; if (argc != 2) { printf("Usage: %s \n", argv[0]); return 1; } sock = socket(PF_LOCAL, SOCK_DGRAM, 0); if (sock == -1) { fprintf(stderr, "Error while opening socket: %s\n", strerror(errno)); return -1; } memset(&from, 0, sizeof(from)); from.sun_family = PF_LOCAL; chroot_dir = getenv("CHROOT_DIR"); if (chroot_dir == NULL) chroot_dir = ""; len = snprintf(name, 108, "%s/tmp/OpenSIPS.%d.XXXXXX", chroot_dir, getpid()); if (len == 108 && name[len - 1] != '\0') { fprintf(stderr, "tmpfile too long: %s/tmp/OpenSIPS.%d.XXXXXX\n", chroot_dir, getpid()); return -3; } umask(0); /* set mode to 0666 for when opensips is running as non-root user and opensipsctl is running as root */ if (mkstemp(name) == -1) { fprintf(stderr, "Error in mkstemp with name=%s: %s\n", name, strerror(errno)); return -2; } if (unlink(name) == -1) { fprintf(stderr, "Error in unlink of %s: %s\n", name, strerror(errno)); return -2; } strncpy(from.sun_path, name, strlen(name)); if (bind(sock, (struct sockaddr*)&from, SUN_LEN(&from)) == -1) { fprintf(stderr, "Error in bind: %s\n", strerror(errno)); goto err; } memset(&to, 0, sizeof(to)); to.sun_family = PF_LOCAL; strncpy(to.sun_path, argv[1], sizeof(to.sun_path) - 1); len = fread(buffer, 1, BUF_SIZE, stdin); if (len) { if (sendto(sock, buffer, len, 0, (struct sockaddr*)&to, SUN_LEN(&to)) == -1) { fprintf(stderr, "Error in sendto: %s\n", strerror(errno)); goto err; } from_len = sizeof(from); len = recvfrom(sock, buffer, BUF_SIZE, 0, (struct sockaddr*)&from, &from_len); if (len == -1) { fprintf(stderr, "Error in recvfrom: %s\n", strerror(errno)); goto err; } fprintf(stdout, "%.*s", len, buffer); } else { fprintf(stderr, "Nothing to send\n"); goto err; } close(sock); if (unlink(name) == -1) fprintf(stderr, "Error in unlink of %s: %s\n", name, strerror(errno)); return 0; err: close(sock); if (unlink(name) == -1) fprintf(stderr, "Error in unlink of %s: %s\n", name, strerror(errno)); return -1; } opensips-2.2.2/utils/profile/000077500000000000000000000000001300170765700161635ustar00rootroot00000000000000opensips-2.2.2/utils/profile/launch.sh000066400000000000000000000040111300170765700177650ustar00rootroot00000000000000#!/bin/sh -x # profile filename PREF=$1 # number of cycles NROFCYCLES=4 # functions to go into report MYFN="eat_line|eat_space|eat_token" # set to non-zero if only a report is to be generated #REPONLY=tru CONFIG=profile.cfg SRD=${HOME}/sip_router PRO=${SRD}/profile REP=$PRO/$PREF.txt EXEC=ser function usage() { cat << EOF usage: $0 profile_name currently, this script starts a remote sender at alioth. The sender iterates through all messages available in its directory and send them many times (should be configurable). Sip-router is configured to relay requests to benetnash:sink and responses are built to be routed there too. This repeats NROFCYCLES-times. Results of all cycles are then dumped into -.txt and a report .txt is generated. EOF exit 1 } if [ "$#" -ne 1 ] ; then usage fi cd $SRD function run() { j=0 while [ $j -lt "$NROFCYCLES" ] ; do i=`printf "%.3d" $j` j=`expr $j + 1` echo "*** Entering cycle $j" /usr/bin/time --output="$PRO/${PREF}-${i}-time.txt" ${SRD}/$EXEC -l 192.168.99.100 -D -E -d -f ${PRO}/$CONFIG & #rsh -l jku benetnash.fokus.gmd.de 'nohup bin/sock -s -u 5060 ' rsh -l jku alioth.fokus.gmd.de 'nohup tmp/sipp/start.sh ' killall -INT $EXEC gprof $EXEC > $PRO/${PREF}-${i}.txt done } function report() { cat > $REP << EOF first line ... time spent in tested procedure second line (yyrestart) ... total time third line (receive_msg) ... numer of calls % cumulative self self total time seconds seconds calls ms/call ms/call name EOF j=0 while [ $j -lt "$NROFCYCLES" ] ; do i=`printf "%.3d" $j` j=`expr $j + 1` FN="${PRO}/${PREF}-${i}.txt" echo >> $REP echo >> $REP echo $FN >> $REP egrep "($MYFN|yyrestart)" $FN | grep -v '\[' >> $REP grep 'receive_msg \[.\]' $FN | grep -v '\/' >> $REP echo >> $REP cat $PRO/${PREF}-${i}-time.txt >> $REP done cat >> $REP << EOF Execution environment: EOF cat /proc/cpuinfo /proc/meminfo >> $REP } if [ -z "$REPONLY" ] ; then run fi report echo '*** finished ***' echo opensips-2.2.2/utils/profile/profile.cfg000066400000000000000000000003201300170765700202770ustar00rootroot00000000000000# first sort out iptel.org requests from those destined somewhere else #################################################################################### port=5080 route[0] { forward( 127.0.0.1, 9 ); } opensips-2.2.2/utils/vim/000077500000000000000000000000001300170765700153165ustar00rootroot00000000000000opensips-2.2.2/utils/vim/INSTALL000066400000000000000000000006541300170765700163540ustar00rootroot00000000000000 opensips.vim installation + auto filetype detection ----------------------------------------------------------- * Place "opensips.vim" in ~/.vim/syntax/opensips.vim * Enable auto filetype detection in your ~/.vimrc with the following two lines: set filetype=on au BufNewFile,BufRead *opensips*.cfg,*.{os,osips,opensips} set filetype=opensips For any questions / suggestions, please write us at: users@lists.opensips.org opensips-2.2.2/utils/vim/opensips.vim000066400000000000000000000117471300170765700177050ustar00rootroot00000000000000" Vim syntax file " Language: OpenSIPS 2.x script " Maintainer: Liviu Chircu " Last Change: 2015 May 30 " Quit when a (custom) syntax file was already loaded "if exists("b:current_syntax") " finish "endif "let s:cpo_save = &cpo "set cpo&vim " " Useful scripting keywords syn keyword osStatement return break exit drop syn keyword osLabel case default esac syn keyword osConditional if else switch and or not syn keyword osRepeat while for in syn keyword osAction loadmodule modparam async syn keyword specialOperand myself yes no true false enable disable on off syn keyword specialOperand af uri status from_uri to_uri NULL null syn keyword specialOperand src_ip src_port dst_ip dst_port proto method max_len syn keyword specialOperand UDP TCP TLS SCTP WS INET inet INET6 inet6 syn keyword osGlobalParam log_level memdump memlog log_stderror log_facility log_name syn keyword osGlobalParam debug_mode children auto_aliases listen mpath tcp_children syn keyword osGlobalParam disable_tcp disable_tls check_via dns rev_dns syn keyword osGlobalParam tcp_send_timeout tcp_connect_timeout tcp_no_new_conn_bflag syn keyword osGlobalParam disable_dns_failover disable_dns_blacklist dst_blacklist syn keyword osGlobalParam exec_dns_threshold exec_msg_threshold tcpthreshold syn keyword osGlobalParam xlog_buf_size xlog_force_color enable_asserts syn keyword osGlobalParam user_agent_header db_version_table use_children syn keyword osGlobalParam advertised_address advertised_port disable_core_dump syn keyword osGlobalParam db_max_async_connections include_file avp_aliases syn keyword osGlobalParam bin_listen bin_children alias dns_try_ipv6 dns_try_naptr syn keyword osGlobalParam dns_retr_time dns_retr_no dns_servers_no maxbuffer syn keyword osGlobalParam dns_use_search_list shm_hash_split_percentage syn keyword osGlobalParam shm_secondary_hash_size mem_warming mem_warming_enabled syn keyword osGlobalParam mem_warming_pattern_file mem_warming_percentage syn keyword osGlobalParam mem_log mem_dump execmsgthreshold execdnsthreshold syn keyword osGlobalParam dns_use_search_list shm_hash_split_percentage syn keyword osGlobalParam tcp_threshold tcpthreshold event_shm_threshold syn keyword osGlobalParam event_pkg_threshold query_buffer_size tcp_children syn keyword osGlobalParam query_flush_time sip_warning server_signature syn keyword osGlobalParam user uid group gid chroot workdir wdir mhomed syn keyword osGlobalParam poll_method tcp_accept_aliases tcp_connection_lifetime syn keyword osGlobalParam tcp_listen_backlog tcp_max_connections tcp_keepalive syn keyword osGlobalParam tcp_keepcount tcp_keepidle tcp_keepinterval syn keyword osGlobalParam open_files_limit mcast_loopback mcast_ttl tos syn keyword osGlobalParam max_while_loops disable_stateless_fwd db_default_url syn keyword osGlobalParam disable_503_translation import_file server_header syn keyword osGlobalParam tcp_max_msg_time abort_on_assert " String constants syn match osSpecial contained display "\\\(x\x\+\|\o\{1,3}\|.\|$\)" " OpenSIPS-specific constructs syn match osLogFacility /LOG_\(AUTH\|CRON\|DAEMON\|KERN\|LOCAL[0-7]\|LPR\|MAIL\|NEWS\|USER\|UUCP\|AUTHPRIV\|FTP\|SYSLOG\)/ syn match osRouteStmt /^\s*\(\(onreply\|failure\|branch\|local\|startup\|timer\|event\|error\)_\)\=route\(\s\|\n\)*\(\[\|{\)/he=e-1 syn match osTransfm contained /{[a-zA-Z][a-zA-Z0-9]*\.[a-zA-Z]\+[a-zA-Z0-9]*[^}]*}\+/ syn region osVarCtx contained matchgroup=ctxHi start="<" end="\(request\|reply\)>" syn region osVarIndex contained start="\[" end="]" contains=osNumber,osVarNamed,osVarNamedS,osVarCon,osVarCtx,osTransfm " OpenSIPS variables " TODO: fix me with full list of OS vars (for better validation!) syn region osVarCon contained matchgroup=varHi start="[a-zA-Z_0-9]\+(" end=")" syn region osVar matchgroup=varHi start="\$(" end=")" contains=osVarCtx,osVarCon,osTransfm,osVarIndex syn region osVarNamed matchgroup=varHi start="\$[a-zA-Z_0-9]\+(" end=")" syn match osVarNamedS /\$[a-zA-Z_0-9]\+[^a-zA-Z_0-9(]/me=e-1,he=e-1 syn region osString start=+"+ skip=+\\\\\|\\"+ end=+"+ extend contains=osSpecial,osVar,osVarNamed,osVarNamedS " Comments syn region osCommentL keepend start="#" skip="\\$" end="$" syn region osComment extend start="/\*" end="\*/" syn match osNumber display "\<\d\+\>" " Define the default highlighting. " Only used when an item doesn't have highlighting yet hi def link varHi Type hi def link ctxHi Comment hi def link osVarSimple Type hi def link osVar Type hi def link osVarNamedS Type hi def link osVarIndex Type hi def link osRouteStmt Type hi def link osLogFacility specialOperand hi def link osTransfm Special hi def link osCommentL osComment hi def link osAction osStatement hi def link osStatement Statement hi def link osLabel Label hi def link osConditional Conditional hi def link osRepeat Repeat hi def link osGlobalParam Statement hi def link osComment Comment hi def link osString String hi def link osNumber String hi def link specialOperand String hi def link osSpecial SpecialChar "let &cpo = s:cpo_save "unlet s:cpo_save opensips-2.2.2/version.h000066400000000000000000000072631300170765700152310ustar00rootroot00000000000000/* * version and compile flags macros * * Copyright (C) 2004 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef version_h #define version_h #define OPENSIPS_FULL_VERSION NAME " " VERSION " (" ARCH "/" OS ")" #ifdef STATISTICS #define STATS_STR "STATS: On" #else #define STATS_STR "STATS: Off" #endif #ifdef DISABLE_NAGLE #define DISABLE_NAGLE_STR ", DISABLE_NAGLE" #else #define DISABLE_NAGLE_STR "" #endif #ifdef USE_MCAST #define USE_MCAST_STR ", USE_MCAST" #else #define USE_MCAST_STR "" #endif #ifdef NO_DEBUG #define NO_DEBUG_STR ", NO_DEBUG" #else #define NO_DEBUG_STR "" #endif #ifdef NO_LOG #define NO_LOG_STR ", NO_LOG" #else #define NO_LOG_STR "" #endif #ifdef EXTRA_DEBUG #define EXTRA_DEBUG_STR ", EXTRA_DEBUG" #else #define EXTRA_DEBUG_STR "" #endif #ifdef SHM_MMAP #define SHM_MMAP_STR ", SHM_MMAP" #else #define SHM_MMAP_STR "" #endif #ifdef PKG_MALLOC #define PKG_MALLOC_STR ", PKG_MALLOC" #else #define PKG_MALLOC_STR "" #endif #ifdef VQ_MALLOC #define VQ_MALLOC_STR ", VQ_MALLOC" #else #define VQ_MALLOC_STR "" #endif #ifdef QM_MALLOC #define QM_MALLOC_STR ", QM_MALLOC" #else #define QM_MALLOC_STR "" #endif #ifdef F_MALLOC #define F_MALLOC_STR ", F_MALLOC" #else #define F_MALLOC_STR "" #endif #ifdef HP_MALLOC #define HP_MALLOC_STR ", HP_MALLOC" #else #define HP_MALLOC_STR "" #endif #ifdef USE_SHM_MEM #define USE_SHM_MEM_STR ", USE_SHM_MEM" #else #define USE_SHM_MEM_STR "" #endif #ifdef DBG_MALLOC #define DBG_MALLOC_STR ", DBG_MALLOC" #else #define DBG_MALLOC_STR "" #endif #ifdef DEBUG_DMALLOC #define DEBUG_DMALLOC_STR ", DEBUG_DMALLOC" #else #define DEBUG_DMALLOC_STR "" #endif #ifdef QM_JOIN_FREE #define QM_JOIN_FREE_STR ", QM_JOIN_FREE" #else #define QM_JOIN_FREE_STR "" #endif #ifdef FAST_LOCK #ifdef USE_FUTEX #ifdef ADAPTIVE_WAIT #define FAST_LOCK_STR ", FAST_LOCK-FUTEX-ADAPTIVE_WAIT" #else #define FAST_LOCK_STR ", FAST_LOCK-FUTEX" #endif #elif defined (BUSY_WAIT) #define FAST_LOCK_STR ", FAST_LOCK-BUSY_WAIT" #elif defined (ADAPTIVE_WAIT) #define FAST_LOCK_STR ", FAST_LOCK-ADAPTIVE_WAIT" #else #define FAST_LOCK_STR ", FAST_LOCK" #endif #else #define FAST_LOCK_STR "" #endif #ifdef USE_PTHREAD_MUTEX #define USE_PTHREAD_MUTEX_STR ", USE_PTHREAD_MUTEX" #else #define USE_PTHREAD_MUTEX_STR "" #endif #ifdef USE_POSIX_SEM #define USE_POSIX_SEM_STR ", USE_POSIX_SEM" #else #define USE_POSIX_SEM_STR "" #endif #ifdef USE_SYSV_SEM #define USE_SYSV_SEM_STR ", USE_SYSV_SEM" #else #define USE_SYSV_SEM_STR "" #endif #ifdef DBG_LOCK #define DBG_LOCK_STR ", DBG_LOCK" #else #define DBG_LOCK_STR "" #endif #ifdef NOSMP #define NOSMP_STR "-NOSMP" #else #define NOSMP_STR "" #endif #define OPENSIPS_COMPILE_FLAGS \ STATS_STR EXTRA_DEBUG_STR \ DISABLE_NAGLE_STR USE_MCAST_STR NO_DEBUG_STR NO_LOG_STR \ SHM_MMAP_STR PKG_MALLOC_STR VQ_MALLOC_STR QM_MALLOC_STR F_MALLOC_STR \ HP_MALLOC_STR USE_SHM_MEM_STR DBG_MALLOC_STR \ DEBUG_DMALLOC_STR QM_JOIN_FREE_STR FAST_LOCK_STR NOSMP_STR \ USE_PTHREAD_MUTEX_STR USE_POSIX_SEM_STR USE_SYSV_SEM_STR DBG_LOCK_STR #endif opensips-2.2.2/xlog.c000066400000000000000000000141611300170765700145030ustar00rootroot00000000000000/** * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "sr_module.h" #include "dprint.h" #include "error.h" #include "mem/mem.h" #include "xlog.h" #include "pvar.h" char *log_buf = NULL; int xlog_buf_size = 4096; int xlog_force_color = 0; int xlog_default_level = L_ERR; static int buf_init(void) { LM_DBG("initializing...\n"); log_buf = (char*)pkg_malloc((xlog_buf_size+1)*sizeof(char)); if(log_buf==NULL) { LM_ERR("no pkg memory left\n"); return -1; } return 0; } int xl_print_log(struct sip_msg* msg, pv_elem_p list, int *len) { if (log_buf == NULL) if (buf_init()) { LM_ERR("Cannot print message\n"); return -1; } return pv_printf(msg, list, log_buf, len); } int xlog_2(struct sip_msg* msg, char* lev, char* frm) { int log_len; long level; xl_level_p xlp; pv_value_t value; xlp = (xl_level_p)lev; if(xlp->type==1) { if(pv_get_spec_value(msg, &xlp->v.sp, &value)!=0 || value.flags&PV_VAL_NULL || !(value.flags&PV_VAL_INT)) { LM_ERR("invalid log level value [%d]\n", value.flags); return -1; } level = (long)value.ri; } else { level = xlp->v.level; } if(!is_printable((int)level)) return 1; log_len = xlog_buf_size; if(xl_print_log(msg, (pv_elem_t*)frm, &log_len)<0) return -1; /* log_buf[log_len] = '\0'; */ LM_GEN1((int)level, "%.*s", log_len, log_buf); return 1; } int xlog_1(struct sip_msg* msg, char* frm, char* str2) { int log_len; if(!is_printable(L_ERR)) return 1; log_len = xlog_buf_size; if(xl_print_log(msg, (pv_elem_t*)frm, &log_len)<0) return -1; /* log_buf[log_len] = '\0'; */ LM_GEN1(xlog_default_level, "%.*s", log_len, log_buf); return 1; } /** */ int xdbg(struct sip_msg* msg, char* frm, char* str2) { int log_len; if(!is_printable(L_DBG)) return 1; log_len = xlog_buf_size; if(xl_print_log(msg, (pv_elem_t*)frm, &log_len)<0) return -1; /* log_buf[log_len] = '\0'; */ LM_GEN1(L_DBG, "%.*s", log_len, log_buf); return 1; } int pv_parse_color_name(pv_spec_p sp, str *in) { if(in==NULL || in->s==NULL || sp==NULL) return -1; if(in->len != 2) { LM_ERR("color name must have two chars\n"); return -1; } /* foreground */ switch(in->s[0]) { case 'x': case 's': case 'r': case 'g': case 'y': case 'b': case 'p': case 'c': case 'w': case 'S': case 'R': case 'G': case 'Y': case 'B': case 'P': case 'C': case 'W': break; default: goto error; } /* background */ switch(in->s[1]) { case 'x': case 's': case 'r': case 'g': case 'y': case 'b': case 'p': case 'c': case 'w': break; default: goto error; } sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = AVP_NAME_STR; sp->pvp.pvn.u.isname.name.s = *in; sp->getf = pv_get_color; /* force the color PV type */ sp->type = PVT_COLOR; return 0; error: LM_ERR("invalid color name\n"); return -1; } #define COL_BUF 10 #define append_sstring(p, end, s) \ do{\ if ((p)+(sizeof(s)-1)<=(end)){\ memcpy((p), s, sizeof(s)-1); \ (p)+=sizeof(s)-1; \ }else{ \ /* overflow */ \ LM_ERR("append_sstring overflow\n"); \ goto error;\ } \ } while(0) int pv_get_color(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char color[COL_BUF]; char* p; char* end; str s; if(log_stderr==0 && xlog_force_color==0) { s.s = ""; s.len = 0; return pv_get_strval(msg, param, res, &s); } p = color; end = p + COL_BUF; /* excape sequenz */ append_sstring(p, end, "\033["); if(param->pvn.u.isname.name.s.s[0]!='_') { if (islower((int)param->pvn.u.isname.name.s.s[0])) { /* normal font */ append_sstring(p, end, "0;"); } else { /* bold font */ append_sstring(p, end, "1;"); param->pvn.u.isname.name.s.s[0] += 32; } } /* foreground */ switch(param->pvn.u.isname.name.s.s[0]) { case 'x': append_sstring(p, end, "39;"); break; case 's': append_sstring(p, end, "30;"); break; case 'r': append_sstring(p, end, "31;"); break; case 'g': append_sstring(p, end, "32;"); break; case 'y': append_sstring(p, end, "33;"); break; case 'b': append_sstring(p, end, "34;"); break; case 'p': append_sstring(p, end, "35;"); break; case 'c': append_sstring(p, end, "36;"); break; case 'w': append_sstring(p, end, "37;"); break; default: LM_ERR("invalid foreground\n"); return pv_get_null(msg, param, res); } /* background */ switch(param->pvn.u.isname.name.s.s[1]) { case 'x': append_sstring(p, end, "49"); break; case 's': append_sstring(p, end, "40"); break; case 'r': append_sstring(p, end, "41"); break; case 'g': append_sstring(p, end, "42"); break; case 'y': append_sstring(p, end, "43"); break; case 'b': append_sstring(p, end, "44"); break; case 'p': append_sstring(p, end, "45"); break; case 'c': append_sstring(p, end, "46"); break; case 'w': append_sstring(p, end, "47"); break; default: LM_ERR("invalid background\n"); return pv_get_null(msg, param, res); } /* end */ append_sstring(p, end, "m"); s.s = color; s.len = p-color; return pv_get_strval(msg, param, res, &s); error: return -1; } opensips-2.2.2/xlog.h000066400000000000000000000024561300170765700145140ustar00rootroot00000000000000/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of opensips, a free SIP server. * * opensips is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * opensips is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _XLOG_H_ #define _XLOG_H_ #include "pvar.h" typedef struct _xl_level { int type; union { long level; pv_spec_t sp; } v; } xl_level_t, *xl_level_p; extern int xlog_buf_size; extern int xlog_force_color; extern int xlog_default_level; int xlog_1(struct sip_msg*, char*, char*); int xlog_2(struct sip_msg*, char*, char*); int xdbg(struct sip_msg*, char*, char*); int pv_parse_color_name(pv_spec_p sp, str *in); int pv_get_color(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); #endif